diff --git a/README.md b/README.md index 9b5011093..8c291ec13 100644 --- a/README.md +++ b/README.md @@ -365,6 +365,7 @@ version | git hash 2019.2.1-mbe | ed4090cc21ffb91a45749be62f9a2fa08b69a0fc 2019.4.23f1-mbe | 90cf2678d79ad248593837523bde01561ee6548e 2019.4.24f1-mbe | 90cf2678d79ad248593837523bde01561ee6548e +2019.4.24f1-mbe | 90cf2678d79ad248593837523bde01561ee6548e 2019.4.25f1-mbe | 90cf2678d79ad248593837523bde01561ee6548e 2019.4.26f1-mbe | 90cf2678d79ad248593837523bde01561ee6548e 2019.4.28f1-mbe | 80e6f67978ff74743da62b80ccdce59aff033537 diff --git a/unity-2019.4.24f1-mbe/.clang-format b/unity-2019.4.24f1-mbe/.clang-format new file mode 100644 index 000000000..a4148517c --- /dev/null +++ b/unity-2019.4.24f1-mbe/.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/unity-2019.4.24f1-mbe/.editorconfig b/unity-2019.4.24f1-mbe/.editorconfig new file mode 100644 index 000000000..f06c382dc --- /dev/null +++ b/unity-2019.4.24f1-mbe/.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/unity-2019.4.24f1-mbe/.gitattributes b/unity-2019.4.24f1-mbe/.gitattributes new file mode 100644 index 000000000..25a58f158 --- /dev/null +++ b/unity-2019.4.24f1-mbe/.gitattributes @@ -0,0 +1,69 @@ +# ensure LF endings on all checkouts +configure.ac crlf=input +config.rpath crlf=input +configure.host crlf=input +mkinstalldirs crlf=input +*.sh crlf=input +*.sources crlf=input +.gitattributes crlf=input +*akefile* crlf=input + +# ensure native line endings on checkout +*.c crlf +*.h crlf +*.cs crlf +*.il crlf + +# don't do anything to line-endings. Let CRLFs go into the repo, and CRLF on checkout +*.bat -crlf +*.sln -crlf +*.*proj* -crlf +*.xml -crlf + +# CRLF Handling +# ------------- +# +# The ideal situation would be to do no EOL normalization. Each file +# would have a default EOL, and tools on Windows and Linux would handle +# both EOL formats. +# +# We're not in the ideal world. A popular editor on Windows (possibly +# Visual Studio) silently introduces EOL corruption -- it displays an +# LF-file normally, but any newly added lines have CRLF. On Linux, +# Emacs and versions of VI handle LF-files and CRLF-files properly. +# However, emacs doesn't like files with both LF and CRLF EOLs. Editing +# the file without additional action will increase the EOL corruption +# in the file. +# +# Another vector for mixed EOLs is scripts. We mostly don't have scripts +# that add new lines -- so we rarely see this. However, one major event +# in the tree was the addition of copyright headers using a script. That +# script introduced EOL corruption. +# +# Any automated EOL normalization of files already in the repository will +# cause difficulties in traversing histories, assigning blame, etc. So, we +# don't want to change what's in the repository significantly, even if it +# causes trouble. +# +# What we do now: +# +# a) we ensure that there's no further corruption of LF-files. So, we use +# git's 'crlf' attribute on those files to ensure that things are fine +# when we work on Windows. We could use 'crlf=input', but it doesn't buy +# us much -- we might as well be working with consistent EOLs for files in +# working directories as well as in the repository +# +# b) if the file already of CRLFs, we don't do any normalization. We use '-crlf' +# so that git doesn't do any EOL-conversion of the file. As I said, this +# is mostly harmless on Linux. We can't mark these files as 'crlf' or use +# the new (git 1.7.2) 'eol=crlf' attribute, since it changes the contents +# _inside_ the repository [1], and hence makes history traversal annoying. +# So, we live with occasional EOL corruption. +# +# c) We can handle mixed-EOL files on a case-by-case basis, converting them to +# LF- or CRLF-files based on which causes fewer lines to change +# +# d) We try to ensure no further headaches, by declaring EOL normalization on +# code files, and Unix-flavoured files, like shell-scripts, makefiles, etc. +# +# [1] GIT use LFs as the normalized internal representation. diff --git a/unity-2019.4.24f1-mbe/.gitignore b/unity-2019.4.24f1-mbe/.gitignore new file mode 100644 index 000000000..29553b9d7 --- /dev/null +++ b/unity-2019.4.24f1-mbe/.gitignore @@ -0,0 +1,140 @@ +############################################################################## +# General +############################################################################## + +# OS junk files +[Tt]humbs.db +*.DS_Store + +# Visual Studio / MonoDevelop +*.[Oo]bj +*.exe +*.dll +*.pdb +*.user +*.aps +*.pch +*.vspscc +*.vssscc +*_i.c +*_p.c +*.ncb +*.suo +*.tlb +*.tlh +*.bak +*.ilk +*.log +*.lib +*.sbr +*.sdf +*.opensdf +*.resources +*.res +ipch/ +obj/ +[Bb]in +[Dd]ebug*/ +[Rr]elease*/ +Ankh.NoLoad +*.gpState +.vscode/ +*.exp + +# Tooling +_ReSharper*/ +*.resharper +.idea/* +[Tt]est[Rr]esult* +*.orig +*.rej + +# NuGet packages +!.nuget/* +[Pp]ackages/* +![Pp]ackages/repositories.config + +# Temporary Files +~.* +~$* + +# Autotools-generated files +/Makefile +Makefile.in +aclocal.m4 +autom4te.cache +/build/ +config.cache +config.guess +config.h +config.h.in +config.log +config.status +config.sub +configure +configure.scan +cygconfig.h +depcomp +install-sh +libtool +ltmain.sh +missing +mkinstalldirs +releases +stamp-h +stamp-h1 +stamp-h.in +/test-driver +*~ +*.swp +*.o +.deps + +# Libtool +libtool.m4 +lt~obsolete.m4 +ltoptions.m4 +ltsugar.m4 +ltversion.m4 + +# Dolt (libtool replacement) +doltlibtool +doltcompile + +# pkg-config +*.pc + +# Emacs +semantic.cache + +# gtags +GPATH +GRTAGS +GSYMS +GTAGS + +# Doxygen +docs/doxygen* +docs/perlmod* + +# Bee +artifacts +.vs +external/buildscripts/build.gen* +stevedore + +# Allow +!external/buildscripts/bee.exe + +############################################################################## +# Mono-specific patterns +############################################################################## + +.dirstamp +compile +mono.h +mono-*.tar.* +tmpinst-dir.stamp +msvc/scripts/inputs/ +extensions-config.h + diff --git a/unity-2019.4.24f1-mbe/.gitlab-ci.yml b/unity-2019.4.24f1-mbe/.gitlab-ci.yml new file mode 100644 index 000000000..f02345b22 --- /dev/null +++ b/unity-2019.4.24f1-mbe/.gitlab-ci.yml @@ -0,0 +1,291 @@ +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_win_bare_minimum: + stage: build + tags: + - bokken-job + variables: + BOKKEN_VM: build_win_bare_minimum + BOKKEN_JOB: | + resources: + - name: build_win_bare_minimum + image: platform-foundation/windows-mono-bokken:latest + flavor: b1.xlarge + type: Unity::VM + script: + - git submodule update --init --recursive + - perl external/buildscripts/build_unityscript_bareminimum_win.pl + - powershell mkdir -p incomingbuilds/bareminimum + - powershell cp -r builds/* incomingbuilds/bareminimum/ + artifacts: + paths: + - incomingbuilds/bareminimum + 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/unity-2019.4.24f1-mbe/.gitmodules b/unity-2019.4.24f1-mbe/.gitmodules new file mode 100644 index 000000000..04b5228b6 --- /dev/null +++ b/unity-2019.4.24f1-mbe/.gitmodules @@ -0,0 +1,63 @@ +[submodule "external/aspnetwebstack"] + path = external/aspnetwebstack + url = git://github.com/mono/aspnetwebstack.git +[submodule "external/Newtonsoft.Json"] + path = external/Newtonsoft.Json + url = git://github.com/mono/Newtonsoft.Json.git +[submodule "external/cecil"] + path = external/cecil + url = git://github.com/mono/cecil.git +[submodule "external/rx"] + path = external/rx + url = git://github.com/mono/rx.git + branch = rx-oss-v2.2 +[submodule "external/ikvm"] + path = external/ikvm + url = git://github.com/mono/ikvm-fork.git +[submodule "external/ikdasm"] + path = external/ikdasm + url = git://github.com/mono/ikdasm.git +[submodule "external/reference-assemblies"] + path = external/binary-reference-assemblies + url = git://github.com/mono/reference-assemblies.git +[submodule "external/nunit-lite"] + path = external/nunit-lite + url = git://github.com/mono/NUnitLite.git +[submodule "external/nuget-buildtasks"] + path = external/nuget-buildtasks + url = git://github.com/mono/NuGet.BuildTasks +[submodule "external/cecil-legacy"] + path = external/cecil-legacy + url = git://github.com/mono/cecil.git + branch = mono-legacy-0.9.5 +[submodule "external/boringssl"] + path = external/boringssl + url = git://github.com/mono/boringssl.git + branch = mono +[submodule "external/corefx"] + path = external/corefx + url = git://github.com/mono/corefx.git +[submodule "external/bockbuild"] + path = external/bockbuild + url = git://github.com/mono/bockbuild.git +[submodule "external/linker"] + path = external/linker + url = git://github.com/mono/linker.git +[submodule "external/roslyn-binaries"] + path = external/roslyn-binaries + url = git://github.com/mono/roslyn-binaries.git +[submodule "external/corert"] + path = external/corert + url = git://github.com/mono/corert.git +[submodule "external/xunit-binaries"] + path = external/xunit-binaries + url = git://github.com/mono/xunit-binaries.git +[submodule "external/api-doc-tools"] + path = external/api-doc-tools + url = git://github.com/mono/api-doc-tools.git +[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/unity-2019.4.24f1-mbe/CODEOWNERS b/unity-2019.4.24f1-mbe/CODEOWNERS new file mode 100644 index 000000000..ff81cd72d --- /dev/null +++ b/unity-2019.4.24f1-mbe/CODEOWNERS @@ -0,0 +1,128 @@ +# Mono Code Owners File +# +# GitHub uses this file to determine who to assign for reviews +# on pull requests. Please keep this file alphabetically sorted. +# +# References: +# +# https://github.com/blog/2392-introducing-code-owners +# https://help.github.com/articles/about-codeowners + + +# These owners will be the default owners for everything in the repo. +* @luhenry @marek-safar + +# Order is important. The last matching pattern has the most precedence. + +/acceptance-tests @akoeplinger +/acceptance-tests/profiler-stress* @alexrp + +/data @marek-safar @akoeplinger +/data/gdb @vargaz +/data/lldb @lewurm @vargaz + +/docs @luhenry @marek-safar +/docs/sources/mono-api-profiler.html @alexrp + +/external @marek-safar @luhenry +/external/binary-reference-assemblies @marek-safar @akoeplinger +/external/bockbuild @akoeplinger @alexischr +/external/boringssl @baulig +/external/nuget-buildtasks @radical @akoeplinger + +/libgc @BrzVlad @vargaz + +/llvm @vargaz + +/man @marek-safar @miguel +/man/*prof* @alexrp + +/mcs @marek-safar + +/mcs/build @marek-safar @akoeplinger + +/mcs/class @marek-safar @akoeplinger +/mcs/class/corlib/System.Reflection*/ @vargaz @lambdageek +/mcs/class/Mono.Btls.Interface @baulig +/mcs/class/Mono.Data.Tds @egorbo +/mcs/class/Mono.Debugger.Soft @vargaz +/mcs/class/Mono.Options @jonpryor +/mcs/class/Mono.Profiler.Log @alexrp +/mcs/class/Mono.Security/Mono.Security/Interface @baulig +/mcs/class/System/Mono.AppleTls @baulig +/mcs/class/System/Mono.Btls @baulig +/mcs/class/System/Mono.Net.Security @baulig +/mcs/class/System/Mono.Security.Interface @baulig +/mcs/class/System.Data* @egorbo + +/mcs/errors @marek-safar +/mcs/ilasm @marek-safar +/mcs/jay @marek-safar +/mcs/mcs @marek-safar +/mcs/tests @marek-safar +/mcs/tools @marek-safar @akoeplinger + +/mono @luhenry + +/mono/btls @baulig +/mono/dis @lambdageek @vargaz +/mono/eglib @kumpera @vargaz @luhenry + +/mono/metadata @vargaz @kumpera +/mono/metadata/*verify* @kumpera +/mono/metadata/*profiler* @alexrp +/mono/metadata/monitor* @brzvlad +/mono/metadata/sgen* @brzvlad +/mono/metadata/threads* @luhenry @kumpera +/mono/metadata/threadpool* @luhenry +/mono/metadata/w32* @luhenry +/mono/metadata/*-win* @lateralusX @luhenry + +/mono/mini @vargaz @kumpera +/mono/mini/*cfgdump* @lewurm +/mono/mini/*exceptions* @lewurm +/mono/mini/*ppc* @lewurm +/mono/mini/*type-check* @lewurm +/mono/mini/interp/* @lewurm +/mono/mini/*profiler* @alexrp +/mono/mini/debugger-agent.c @DavidKarlas + +/mono/profiler @alexrp @kumpera + +/mono/sgen @brzvlad @kumpera + +/mono/utils @kumpera @vargaz +/mono/utils/atomic* @alexrp +/mono/utils/mono-hwcap* @alexrp +/mono/utils/mono-mem* @alexrp +/mono/utils/mono-threads* @luhenry @kumpera +/mono/utils/*-win* @lateralusX @kumpera + +/msvc @lateralusX +/msvc/*profiler* @alexrp +/msvc/scripts @akoeplinger + +/packaging/ @alexischr @akoeplinger +/packaging/MacSDK/msbuild.py @radical @akoeplinger + +/runtime @akoeplinger @marek-safar + +/samples @luhenry +/samples/profiler @alexrp +/samples/size @alexrp + +/scripts @akoeplinger +/scripts/ci @akoeplinger +/scripts/ci/run-test-profiler-stress-tests.sh @alexrp + +/support @luhenry @vargaz + +/tools @kumpera +/tools/checker @tritao +/tools/crash-bisector @kumpera +/tools/locale-builder @marek-safar +/tools/monograph @vargaz +/tools/nuget-hash-extractor @marek-safar +/tools/offsets-tool @lewurm @rolfbjarne +/tools/pedump @kumpera @lambdageek +/tools/sgen @brzvlad diff --git a/unity-2019.4.24f1-mbe/LICENSE b/unity-2019.4.24f1-mbe/LICENSE new file mode 100644 index 000000000..39d9ac1c1 --- /dev/null +++ b/unity-2019.4.24f1-mbe/LICENSE @@ -0,0 +1,1442 @@ + +In general, the runtime and its class libraries are licensed under the +terms of the MIT license, and some third party code is licensed under +the 3-clause BSD license. See the file "PATENTS.TXT" for Microsoft's +patent grant on the Mono codebase. + +The Mono distribution does include a handful of pieces of code that +are used during the build system and are covered under different +licenses, those include: + +Build Time Code +=============== + +This is code that is used at build time, or during the maintenance of +Mono itself, and does not end up in the redistributable part of Mono: + +* gettext + + m4 source files used to probe features at build time: GPL + +* Benchmark Source Files + + Logic.cs and zipmark.cs are GPL source files. + +* mono/docs/HtmlAgilityPack + + MS-PL licensed + +* mcs/jay: 4-clause BSD licensed + +* mcs/nunit24: MS-PL + +* mcs/class/I18N/mklist.sh, tools/cvt.sh: GNU GPLv2 + +Runtime Code +============ + +The following code is linked with the final Mono runtime, the libmono +embeddable runtime: + +* support/minizip: BSD license. + +* mono/utils/memcheck.h: BSD license, used on debug builds that use Valgrind. + +* mono/utils/freebsd-dwarf.h, freebsd-elf_common.h, freebsd-elf64.h freebsd-elf32.h: BSD license. + +* mono/utils/bsearch.c: BSD license. + +* mono/metadata/w32file-unix-glob.c, w32file-unix-glob.h: BSD license + +Class Library code +================== + +These are class libraries that can be loaded by your process: + +* mcs/class/RabbitMQ.Client: dual licensed in Apache v2, and Mozilla Public License 1.1 + +* mcs/class/Compat.ICSharpCode.SharpZipLib and + mcs/class/ICSharpCode.SharpZipLib are GPL with class-path exception. + Originates with the SharpDevelop project. + +* mcs/class/System.Core/System/TimeZoneInfo.Android.cs + + This is a port of Apache 2.0-licensed Android code, and thus is + licensed under the Apache 2.0 license + + http://www.apache.org/licenses/LICENSE-2.0 + +API Documentation +================= + +The API documentation is licensed under the terms of the Creative +Commons Attribution 4.0 International Public License + + +The Licenses +============ + + These are the licenses used in Mono, the files are located: + +### MIT X11 License + +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. + + +### Mozilla.MPL + + MOZILLA PUBLIC LICENSE + Version 1.1 + + --------------- + +1. Definitions. + + 1.0.1. "Commercial Use" means distribution or otherwise making the + Covered Code available to a third party. + + 1.1. "Contributor" means each entity that creates or contributes to + the creation of Modifications. + + 1.2. "Contributor Version" means the combination of the Original + Code, prior Modifications used by a Contributor, and the Modifications + made by that particular Contributor. + + 1.3. "Covered Code" means the Original Code or Modifications or the + combination of the Original Code and Modifications, in each case + including portions thereof. + + 1.4. "Electronic Distribution Mechanism" means a mechanism generally + accepted in the software development community for the electronic + transfer of data. + + 1.5. "Executable" means Covered Code in any form other than Source + Code. + + 1.6. "Initial Developer" means the individual or entity identified + as the Initial Developer in the Source Code notice required by Exhibit + A. + + 1.7. "Larger Work" means a work which combines Covered Code or + portions thereof with code not governed by the terms of this License. + + 1.8. "License" means this document. + + 1.8.1. "Licensable" means having the right to grant, to the maximum + extent possible, whether at the time of the initial grant or + subsequently acquired, any and all of the rights conveyed herein. + + 1.9. "Modifications" means any addition to or deletion from the + substance or structure of either the Original Code or any previous + Modifications. When Covered Code is released as a series of files, a + Modification is: + A. Any addition to or deletion from the contents of a file + containing Original Code or previous Modifications. + + B. Any new file that contains any part of the Original Code or + previous Modifications. + + 1.10. "Original Code" means Source Code of computer software code + which is described in the Source Code notice required by Exhibit A as + Original Code, and which, at the time of its release under this + License is not already Covered Code governed by this License. + + 1.10.1. "Patent Claims" means any patent claim(s), now owned or + hereafter acquired, including without limitation, method, process, + and apparatus claims, in any patent Licensable by grantor. + + 1.11. "Source Code" means the preferred form of the Covered Code for + making modifications to it, including all modules it contains, plus + any associated interface definition files, scripts used to control + compilation and installation of an Executable, or source code + differential comparisons against either the Original Code or another + well known, available Covered Code of the Contributor's choice. The + Source Code can be in a compressed or archival form, provided the + appropriate decompression or de-archiving software is widely available + for no charge. + + 1.12. "You" (or "Your") means an individual or a legal entity + exercising rights under, and complying with all of the terms of, this + License or a future version of this License issued under Section 6.1. + For legal entities, "You" includes any entity which controls, is + controlled by, or is under common control with You. For purposes of + this definition, "control" means (a) the power, direct or indirect, + to cause the direction or management of such entity, whether by + contract or otherwise, or (b) ownership of more than fifty percent + (50%) of the outstanding shares or beneficial ownership of such + entity. + +2. Source Code License. + + 2.1. The Initial Developer Grant. + The Initial Developer hereby grants You a world-wide, royalty-free, + non-exclusive license, subject to third party intellectual property + claims: + (a) under intellectual property rights (other than patent or + trademark) Licensable by Initial Developer to use, reproduce, + modify, display, perform, sublicense and distribute the Original + Code (or portions thereof) with or without Modifications, and/or + as part of a Larger Work; and + + (b) under Patents Claims infringed by the making, using or + selling of Original Code, to make, have made, use, practice, + sell, and offer for sale, and/or otherwise dispose of the + Original Code (or portions thereof). + + (c) the licenses granted in this Section 2.1(a) and (b) are + effective on the date Initial Developer first distributes + Original Code under the terms of this License. + + (d) Notwithstanding Section 2.1(b) above, no patent license is + granted: 1) for code that You delete from the Original Code; 2) + separate from the Original Code; or 3) for infringements caused + by: i) the modification of the Original Code or ii) the + combination of the Original Code with other software or devices. + + 2.2. Contributor Grant. + Subject to third party intellectual property claims, each Contributor + hereby grants You a world-wide, royalty-free, non-exclusive license + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Contributor, to use, reproduce, modify, + display, perform, sublicense and distribute the Modifications + created by such Contributor (or portions thereof) either on an + unmodified basis, with other Modifications, as Covered Code + and/or as part of a Larger Work; and + + (b) under Patent Claims infringed by the making, using, or + selling of Modifications made by that Contributor either alone + and/or in combination with its Contributor Version (or portions + of such combination), to make, use, sell, offer for sale, have + made, and/or otherwise dispose of: 1) Modifications made by that + Contributor (or portions thereof); and 2) the combination of + Modifications made by that Contributor with its Contributor + Version (or portions of such combination). + + (c) the licenses granted in Sections 2.2(a) and 2.2(b) are + effective on the date Contributor first makes Commercial Use of + the Covered Code. + + (d) Notwithstanding Section 2.2(b) above, no patent license is + granted: 1) for any code that Contributor has deleted from the + Contributor Version; 2) separate from the Contributor Version; + 3) for infringements caused by: i) third party modifications of + Contributor Version or ii) the combination of Modifications made + by that Contributor with other software (except as part of the + Contributor Version) or other devices; or 4) under Patent Claims + infringed by Covered Code in the absence of Modifications made by + that Contributor. + +3. Distribution Obligations. + + 3.1. Application of License. + The Modifications which You create or to which You contribute are + governed by the terms of this License, including without limitation + Section 2.2. The Source Code version of Covered Code may be + distributed only under the terms of this License or a future version + of this License released under Section 6.1, and You must include a + copy of this License with every copy of the Source Code You + distribute. You may not offer or impose any terms on any Source Code + version that alters or restricts the applicable version of this + License or the recipients' rights hereunder. However, You may include + an additional document offering the additional rights described in + Section 3.5. + + 3.2. Availability of Source Code. + Any Modification which You create or to which You contribute must be + made available in Source Code form under the terms of this License + either on the same media as an Executable version or via an accepted + Electronic Distribution Mechanism to anyone to whom you made an + Executable version available; and if made available via Electronic + Distribution Mechanism, must remain available for at least twelve (12) + months after the date it initially became available, or at least six + (6) months after a subsequent version of that particular Modification + has been made available to such recipients. You are responsible for + ensuring that the Source Code version remains available even if the + Electronic Distribution Mechanism is maintained by a third party. + + 3.3. Description of Modifications. + You must cause all Covered Code to which You contribute to contain a + file documenting the changes You made to create that Covered Code and + the date of any change. You must include a prominent statement that + the Modification is derived, directly or indirectly, from Original + Code provided by the Initial Developer and including the name of the + Initial Developer in (a) the Source Code, and (b) in any notice in an + Executable version or related documentation in which You describe the + origin or ownership of the Covered Code. + + 3.4. Intellectual Property Matters + (a) Third Party Claims. + If Contributor has knowledge that a license under a third party's + intellectual property rights is required to exercise the rights + granted by such Contributor under Sections 2.1 or 2.2, + Contributor must include a text file with the Source Code + distribution titled "LEGAL" which describes the claim and the + party making the claim in sufficient detail that a recipient will + know whom to contact. If Contributor obtains such knowledge after + the Modification is made available as described in Section 3.2, + Contributor shall promptly modify the LEGAL file in all copies + Contributor makes available thereafter and shall take other steps + (such as notifying appropriate mailing lists or newsgroups) + reasonably calculated to inform those who received the Covered + Code that new knowledge has been obtained. + + (b) Contributor APIs. + If Contributor's Modifications include an application programming + interface and Contributor has knowledge of patent licenses which + are reasonably necessary to implement that API, Contributor must + also include this information in the LEGAL file. + + (c) Representations. + Contributor represents that, except as disclosed pursuant to + Section 3.4(a) above, Contributor believes that Contributor's + Modifications are Contributor's original creation(s) and/or + Contributor has sufficient rights to grant the rights conveyed by + this License. + + 3.5. Required Notices. + You must duplicate the notice in Exhibit A in each file of the Source + Code. If it is not possible to put such notice in a particular Source + Code file due to its structure, then You must include such notice in a + location (such as a relevant directory) where a user would be likely + to look for such a notice. If You created one or more Modification(s) + You may add your name as a Contributor to the notice described in + Exhibit A. You must also duplicate this License in any documentation + for the Source Code where You describe recipients' rights or ownership + rights relating to Covered Code. You may choose to offer, and to + charge a fee for, warranty, support, indemnity or liability + obligations to one or more recipients of Covered Code. However, You + may do so only on Your own behalf, and not on behalf of the Initial + Developer or any Contributor. You must make it absolutely clear than + any such warranty, support, indemnity or liability obligation is + offered by You alone, and You hereby agree to indemnify the Initial + Developer and every Contributor for any liability incurred by the + Initial Developer or such Contributor as a result of warranty, + support, indemnity or liability terms You offer. + + 3.6. Distribution of Executable Versions. + You may distribute Covered Code in Executable form only if the + requirements of Section 3.1-3.5 have been met for that Covered Code, + and if You include a notice stating that the Source Code version of + the Covered Code is available under the terms of this License, + including a description of how and where You have fulfilled the + obligations of Section 3.2. The notice must be conspicuously included + in any notice in an Executable version, related documentation or + collateral in which You describe recipients' rights relating to the + Covered Code. You may distribute the Executable version of Covered + Code or ownership rights under a license of Your choice, which may + contain terms different from this License, provided that You are in + compliance with the terms of this License and that the license for the + Executable version does not attempt to limit or alter the recipient's + rights in the Source Code version from the rights set forth in this + License. If You distribute the Executable version under a different + license You must make it absolutely clear that any terms which differ + from this License are offered by You alone, not by the Initial + Developer or any Contributor. You hereby agree to indemnify the + Initial Developer and every Contributor for any liability incurred by + the Initial Developer or such Contributor as a result of any such + terms You offer. + + 3.7. Larger Works. + You may create a Larger Work by combining Covered Code with other code + not governed by the terms of this License and distribute the Larger + Work as a single product. In such a case, You must make sure the + requirements of this License are fulfilled for the Covered Code. + +4. Inability to Comply Due to Statute or Regulation. + + If it is impossible for You to comply with any of the terms of this + License with respect to some or all of the Covered Code due to + statute, judicial order, or regulation then You must: (a) comply with + the terms of this License to the maximum extent possible; and (b) + describe the limitations and the code they affect. Such description + must be included in the LEGAL file described in Section 3.4 and must + be included with all distributions of the Source Code. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Application of this License. + + This License applies to code to which the Initial Developer has + attached the notice in Exhibit A and to related Covered Code. + +6. Versions of the License. + + 6.1. New Versions. + Netscape Communications Corporation ("Netscape") may publish revised + and/or new versions of the License from time to time. Each version + will be given a distinguishing version number. + + 6.2. Effect of New Versions. + Once Covered Code has been published under a particular version of the + License, You may always continue to use it under the terms of that + version. You may also choose to use such Covered Code under the terms + of any subsequent version of the License published by Netscape. No one + other than Netscape has the right to modify the terms applicable to + Covered Code created under this License. + + 6.3. Derivative Works. + If You create or use a modified version of this License (which you may + only do in order to apply it to code which is not already Covered Code + governed by this License), You must (a) rename Your license so that + the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", + "MPL", "NPL" or any confusingly similar phrase do not appear in your + license (except to note that your license differs from this License) + and (b) otherwise make it clear that Your version of the license + contains terms which differ from the Mozilla Public License and + Netscape Public License. (Filling in the name of the Initial + Developer, Original Code or Contributor in the notice described in + Exhibit A shall not of themselves be deemed to be modifications of + this License.) + +7. DISCLAIMER OF WARRANTY. + + COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, + WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF + DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. + THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE + IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, + YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE + COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER + OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF + ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. + +8. TERMINATION. + + 8.1. This License and the rights granted hereunder will terminate + automatically if You fail to comply with terms herein and fail to cure + such breach within 30 days of becoming aware of the breach. All + sublicenses to the Covered Code which are properly granted shall + survive any termination of this License. Provisions which, by their + nature, must remain in effect beyond the termination of this License + shall survive. + + 8.2. If You initiate litigation by asserting a patent infringement + claim (excluding declatory judgment actions) against Initial Developer + or a Contributor (the Initial Developer or Contributor against whom + You file such action is referred to as "Participant") alleging that: + + (a) such Participant's Contributor Version directly or indirectly + infringes any patent, then any and all rights granted by such + Participant to You under Sections 2.1 and/or 2.2 of this License + shall, upon 60 days notice from Participant terminate prospectively, + unless if within 60 days after receipt of notice You either: (i) + agree in writing to pay Participant a mutually agreeable reasonable + royalty for Your past and future use of Modifications made by such + Participant, or (ii) withdraw Your litigation claim with respect to + the Contributor Version against such Participant. If within 60 days + of notice, a reasonable royalty and payment arrangement are not + mutually agreed upon in writing by the parties or the litigation claim + is not withdrawn, the rights granted by Participant to You under + Sections 2.1 and/or 2.2 automatically terminate at the expiration of + the 60 day notice period specified above. + + (b) any software, hardware, or device, other than such Participant's + Contributor Version, directly or indirectly infringes any patent, then + any rights granted to You by such Participant under Sections 2.1(b) + and 2.2(b) are revoked effective as of the date You first made, used, + sold, distributed, or had made, Modifications made by that + Participant. + + 8.3. If You assert a patent infringement claim against Participant + alleging that such Participant's Contributor Version directly or + indirectly infringes any patent where such claim is resolved (such as + by license or settlement) prior to the initiation of patent + infringement litigation, then the reasonable value of the licenses + granted by such Participant under Sections 2.1 or 2.2 shall be taken + into account in determining the amount or value of any payment or + license. + + 8.4. In the event of termination under Sections 8.1 or 8.2 above, + all end user license agreements (excluding distributors and resellers) + which have been validly granted by You or any distributor hereunder + prior to termination shall survive termination. + +9. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL + DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, + OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR + ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY + CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, + WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER + COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN + INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF + LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY + RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW + PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE + EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO + THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. + +10. U.S. GOVERNMENT END USERS. + + The Covered Code is a "commercial item," as that term is defined in + 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer + software" and "commercial computer software documentation," as such + terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 + C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), + all U.S. Government End Users acquire Covered Code with only those + rights set forth herein. + +11. MISCELLANEOUS. + + This License represents the complete agreement concerning subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. This License shall be governed by + California law provisions (except to the extent applicable law, if + any, provides otherwise), excluding its conflict-of-law provisions. + With respect to disputes in which at least one party is a citizen of, + or an entity chartered or registered to do business in the United + States of America, any litigation relating to this License shall be + subject to the jurisdiction of the Federal Courts of the Northern + District of California, with venue lying in Santa Clara County, + California, with the losing party responsible for costs, including + without limitation, court costs and reasonable attorneys' fees and + expenses. The application of the United Nations Convention on + Contracts for the International Sale of Goods is expressly excluded. + Any law or regulation which provides that the language of a contract + shall be construed against the drafter shall not apply to this + License. + +12. RESPONSIBILITY FOR CLAIMS. + + As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or indirectly, + out of its utilization of rights under this License and You agree to + work with Initial Developer and Contributors to distribute such + responsibility on an equitable basis. Nothing herein is intended or + shall be deemed to constitute any admission of liability. + +13. MULTIPLE-LICENSED CODE. + + Initial Developer may designate portions of the Covered Code as + "Multiple-Licensed". "Multiple-Licensed" means that the Initial + Developer permits you to utilize portions of the Covered Code under + Your choice of the NPL or the alternative licenses, if any, specified + by the Initial Developer in the file described in Exhibit A. + +EXHIBIT A -Mozilla Public License. + + ``The contents of this file are subject to the Mozilla Public License + Version 1.1 (the "License"); you may not use this file except in + compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + License for the specific language governing rights and limitations + under the License. + + The Original Code is ______________________________________. + + The Initial Developer of the Original Code is ________________________. + Portions created by ______________________ are Copyright (C) ______ + _______________________. All Rights Reserved. + + Contributor(s): ______________________________________. + + Alternatively, the contents of this file may be used under the terms + of the _____ license (the "[___] License"), in which case the + provisions of [______] License are applicable instead of those + above. If you wish to allow use of your version of this file only + under the terms of the [____] License and not to allow others to use + your version of this file under the MPL, indicate your decision by + deleting the provisions above and replace them with the notice and + other provisions required by the [___] License. If you do not delete + the provisions above, a recipient may use your version of this file + under either the MPL or the [___] License." + + [NOTE: The text of this Exhibit A may differ slightly from the text of + the notices in the Source Code files of the Original Code. You should + use the text of this Exhibit A rather than the text found in the + Original Code Source Code for Your Modifications.] + +### Microsoft Public License + +Microsoft Permissive License (Ms-PL) + + This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software. + +1. Definitions + + The terms “reproduce,” “reproduction,” “derivative works,” and “distribution” have the same meaning here as under U.S. copyright law. + A “contribution” is the original software, or any additions or changes to the software. + A “contributor” is any person that distributes its contribution under this license. + “Licensed patents” are a contributor’s patent claims that read directly on its contribution. + +2. Grant of Rights + + (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. + (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. + +3. Conditions and Limitations + + (A) No Trademark License- This license does not grant you rights to use any contributors’ name, logo, or trademarks. + (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. + (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. + (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. + (E) The software is licensed “as-is.” You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. + (F) If you distribute the software or derivative works with programs you develop, you agree to indemnify, defend, and hold harmless all contributors from any claims, including attorneys’ fees, related to the distribution or use of your programs. For clarity, you have no such obligations to a contributor for any claims based solely on the unmodified contributions of that contributor. + (G) If you make any additions or changes to the original software, you may only distribute them under a new namespace. In addition, you will clearly identify your changes or additions as your own. + +### Infozip BSD + +This is version 2009-Jan-02 of the Info-ZIP license. The definitive +version of this document should be available at +ftp://ftp.info-zip.org/pub/infozip/license.html indefinitely and a +copy at http://www.info-zip.org/pub/infozip/license.html. + +Copyright (c) 1990-2009 Info-ZIP. All rights reserved. + +For the purposes of this copyright and license, "Info-ZIP" is defined +as the following set of individuals: Mark Adler, John Bush, Karl +Davis, Harald Denker, Jean-Michel Dubois, Jean-loup Gailly, Hunter +Goatley, Ed Gordon, Ian Gorman, Chris Herborth, Dirk Haase, Greg +Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz, David +Kirschbaum, Johnny Lee, Onno van der Linden, Igor Mandrichenko, Steve +P. Miller, Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs, +Kai Uwe Rommel, Steve Salisbury, Dave Smith, Steven M. Schweda, +Christian Spieler, Cosmin Truta, Antoine Verheijen, Paul von Behren, +Rich Wales, Mike White. + +This software is provided "as is," without warranty of any kind, +express or implied. In no event shall Info-ZIP or its contributors be +held liable for any direct, indirect, incidental, special or +consequential damages arising out of the use of or inability to use +this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the above disclaimer and the following +restrictions: + +Redistributions of source code (in whole or in part) must retain the +above copyright notice, definition, disclaimer, and this list of +conditions. + +Redistributions in binary form (compiled executables and libraries) +must reproduce the above copyright notice, definition, disclaimer, and +this list of conditions in documentation and/or other materials +provided with the distribution. Additional documentation is not needed +for executables where a command line license option provides these and +a note regarding this option is in the executable's startup +banner. The sole exception to this condition is redistribution of a +standard UnZipSFX binary (including SFXWiz) as part of a +self-extracting archive; that is permitted without inclusion of this +license, as long as the normal SFX banner has not been removed from +the binary or disabled. + +Altered versions--including, but not limited to, ports to new +operating systems, existing ports with new graphical interfaces, +versions with modified or added functionality, and dynamic, shared, or +static library versions not from Info-ZIP--must be plainly marked as +such and must not be misrepresented as being the original source or, +if binaries, compiled from the original source. Such altered versions +also must not be misrepresented as being Info-ZIP releases--including, +but not limited to, labeling of the altered versions with the names +"Info-ZIP" (or any variation thereof, including, but not limited to, +different capitalizations), "Pocket UnZip," "WiZ" or "MacZip" without +the explicit permission of Info-ZIP. Such altered versions are further +prohibited from misrepresentative use of the Zip-Bugs or Info-ZIP +e-mail addresses or the Info-ZIP URL(s), such as to imply Info-ZIP +will provide support for the altered versions. + +Info-ZIP retains the right to use the names "Info-ZIP," "Zip," +"UnZip," "UnZipSFX," "WiZ," "Pocket UnZip," "Pocket Zip," and "MacZip" +for its own source and binary releases. + +### License Creative Commons 2.5 + +// Copyright 2006 James Newton-King +// http://www.newtonsoft.com +// +// This work is licensed under the Creative Commons Attribution 2.5 License +// http://creativecommons.org/licenses/by/2.5/ +// +// You are free: +// * to copy, distribute, display, and perform the work +// * to make derivative works +// * to make commercial use of the work +// +// Under the following conditions: +// * For any reuse or distribution, you must make clear to others the license terms of this work. +// * Any of these conditions can be waived if you get permission from the copyright holder. + +From: james.newtonking@gmail.com [mailto:james.newtonking@gmail.com] On Behalf Of James Newton-King +Sent: Tuesday, June 05, 2007 6:36 AM +To: Konstantin Triger +Subject: Re: Support request by Konstantin Triger for Json.NET + +Hey Kosta + +I think it would be awesome to use Json.NET in Mono for System.Web.Extensions. + +The CC license has the following clause: Any of the above conditions can be waived if you get permission from the copyright holder. + +I can waive that statement for you and Mono. Would that be acceptable? + + +Regards, +James + +### Creative Commons Attribution 4.0 International Public License + +Attribution 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More_considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution 4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution 4.0 International Public License ("Public License"). To the +extent this Public License may be interpreted as a contract, You are +granted the Licensed Rights in consideration of Your acceptance of +these terms and conditions, and the Licensor grants You such rights in +consideration of benefits the Licensor receives from making the +Licensed Material available under these terms and conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + d. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + e. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + f. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + g. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + h. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + i. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + j. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + k. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + 4. If You Share Adapted Material You produce, the Adapter's + License You apply must not prevent recipients of the Adapted + Material from complying with this Public License. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material; and + + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. + +### GPL version 2 + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/unity-2019.4.24f1-mbe/Makefile.am b/unity-2019.4.24f1-mbe/Makefile.am new file mode 100644 index 000000000..024324e3f --- /dev/null +++ b/unity-2019.4.24f1-mbe/Makefile.am @@ -0,0 +1,157 @@ +ACLOCAL_AMFLAGS = -I m4 + +AM_CFLAGS = $(WERROR_CFLAGS) + +MONOTOUCH_SUBDIRS = $(libgc_dir) mono + +# Some tools might not build when cross-compiling +if CROSS_COMPILING +tools_dir = +else +tools_dir = tools +endif + +if BUILD_SUPPORT +support_dir = support +else +support_dir = +endif + +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) $(bdwgc_dir) mono ikvm-native $(support_dir) data runtime scripts man samples tools msvc docs acceptance-tests llvm + +all: update_submodules + +SUBMODULE_ERROR='Could not recursively update all git submodules. You may experience compilation problems if some submodules are out of date' +update_submodules: + @$(srcdir)/scripts/update_submodules.sh + +.PHONY: update_submodules + +EXTRA_DIST= \ + README.md \ + LICENSE \ + autogen.sh \ + mkinstalldirs \ + mono-uninstalled.pc.in \ + winconfig.h \ + code_of_conduct.md \ + external \ + mcs/class/referencesource + +DISTCHECK_CONFIGURE_FLAGS = EXTERNAL_MCS=false EXTERNAL_RUNTIME=false + +# Distribute the 'mcs' tree too +GIT_DIR ?= $(srcdir)/.git +dist-hook: + test -d $(distdir)/mcs || mkdir $(distdir)/mcs + d=`cd $(distdir)/mcs && pwd`; cd $(mcs_topdir) && $(MAKE) distdir=$$d dist-recursive + rm -rf `find $(top_distdir)/external -path '*\.git'` + rm -f `find $(top_distdir)/external -path '*\.exe' -not -path '*/roslyn-binaries/*'` + rm -f `find $(top_distdir)/external -path '*\.dll' -not -path '*/binary-reference-assemblies/*' -not -path '*/roslyn-binaries/*'` + +pkgconfigdir = $(libdir)/pkgconfig +noinst_DATA = mono-uninstalled.pc +DISTCLEANFILES= mono-uninstalled.pc + +# building with monolite +.PHONY: get-monolite-latest +get-monolite-latest: + $(MAKE) -C $(mcs_topdir)/class get-monolite-latest + +if BITCODE +BITCODE_CHECK=yes +endif + +.PHONY: check-ci +check-ci: + MONO_LLVMONLY=$(BITCODE_CHECK) $(srcdir)/scripts/ci/run-test-$(TEST_PROFILE).sh + +.PHONY: validate do-build-mono-mcs mcs-do-clean mcs-do-tests +validate: do-build-mono-mcs + $(MAKE) mcs-do-tests +do-build-mono-mcs: mcs-do-clean + $(MAKE) all +mcs-do-clean: + cd runtime && $(MAKE) clean-local + cd mono/tests && $(MAKE) clean +mcs-do-tests: + cd runtime && $(MAKE) check-local + cd mono/tests && $(MAKE) check + +.PHONY: compiler-tests mcs-do-compiler-tests +compiler-tests: + $(MAKE) test_select='TEST_SUBDIRS="tests errors"' validate +mcs-do-compiler-tests: + $(MAKE) test_select='TEST_SUBDIRS="tests errors"' mcs-do-tests + +.PHONY: bootstrap-world +bootstrap-world: compiler-tests + $(MAKE) install + +if INSTALL_MONOTOUCH +monotouch-do-build: config.h + @list='$(MONOTOUCH_SUBDIRS)'; for subdir in $$list; do \ + case "x$$subdir" in \ + xmono ) target="monotouch-do-build";; \ + * ) target="all";; \ + esac; \ + echo "Making $$target in $$subdir"; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$target); \ + done; + (cd runtime && $(MAKE) $(AM_MAKEFLAGS) monotouch-do-build) + +monotouch-do-clean: + @list='$(MONOTOUCH_SUBDIRS)'; for subdir in $$list; do \ + case "x$$subdir" in \ + xmono ) target="monotouch-do-clean";; \ + * ) target="clean";; \ + esac; \ + echo "Making $$target in $$subdir"; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$target); \ + done; + (cd runtime && $(MAKE) $(AM_MAKEFLAGS) monotouch-do-clean) + +endif + +update-csproj: + -rm msvc/scripts/order + -rm msvc/scripts/order.xml + -rm -rf msvc/scripts/inputs + -mkdir msvc/scripts/inputs + (cd runtime; make V=1 extra_targets=csproj-local) + +package-inputs: + echo '' > msvc/scripts/order.xml + echo '' >> msvc/scripts/order.xml + for i in `cat msvc/scripts/order`; do \ + set `echo $$i | sed -e 's/:/ /' -e 's/.input//'`; \ + cat msvc/scripts/inputs/$$2.input | sed -e 's/\\\\/\\/g' -e 's/\\/\\\\/g' | \ + (echo " "; \ + read boot; echo " $$boot"; \ + read flags; echo " $$flags"; \ + read output; echo " $$output"; \ + read built; echo " `echo $$built | sed 's/\\\/\\\\/g'`"; \ + read libou; echo " $$libou"; \ + read fx_ver; echo " $$fx_ver"; \ + read profile; echo " $$profile"; \ + read resxt; echo " $$resxt"; \ + read resp; echo " $$resp"; \ + echo " ") >> msvc/scripts/order.xml; \ + done + echo "" >> msvc/scripts/order.xml + +# Update llvm version in configure.ac to the output of $LLVM_DIR/bin/llvm-config --version +update-llvm-version: + if test "x$$LLVM_DIR" = "x"; then echo "Set the make variable LLVM_DIR to the directory containing the LLVM installation."; exit 1; fi + REV=`$(LLVM_DIR)/bin/llvm-config --version` && sed -e "s,expected_llvm_version=.*,expected_llvm_version=\"$$REV\"," < configure.ac > tmp && mv tmp configure.ac && echo "Version set to $$REV." + + +update-solution-files: + make update-csproj + make package-inputs + (cd msvc/scripts; make genproj.exe; mono genproj.exe $(GENPROJ_ARGS)) + +update-solution-files-with-tests: + make "GENPROJ_ARGS=2012 true true" update-solution-files diff --git a/unity-2019.4.24f1-mbe/NEWS b/unity-2019.4.24f1-mbe/NEWS new file mode 100644 index 000000000..ec300ca2d --- /dev/null +++ b/unity-2019.4.24f1-mbe/NEWS @@ -0,0 +1,3 @@ +Release notes for Mono are hosted on the web, please see: + + http://www.mono-project.com/docs/about-mono/releases/ diff --git a/unity-2019.4.24f1-mbe/PATENTS.TXT b/unity-2019.4.24f1-mbe/PATENTS.TXT new file mode 100644 index 000000000..a868428bb --- /dev/null +++ b/unity-2019.4.24f1-mbe/PATENTS.TXT @@ -0,0 +1,44 @@ +Microsoft Patent Promise for Mono + +Microsoft Corporation and its affiliates (“Microsoft”) promise not to +assert any Applicable Patents against you for making, using, selling, +offering for sale, importing, or distributing Mono. + +If you file, maintain, or voluntarily participate in any claim in a +lawsuit alleging direct or contributory patent infringement by Mono, +or inducement of patent infringement by Mono, then your rights under +this promise will automatically terminate. + +This promise is not an assurance that (i) any Applicable Patents are +valid or enforceable or (ii) Mono does not infringe patents or other +intellectual property rights of any third party. No rights except +those expressly stated in this promise are granted, waived or received +by Microsoft, whether by implication, exhaustion, estoppel or +otherwise. This is a personal promise directly from Microsoft to you, +and you agree as a condition of benefitting from it that no Microsoft +rights are received from suppliers, distributors, or otherwise in +connection with this promise. + +Definitions: + +“Mono” means those portions of the software development technology, as +originally distributed by Xamarin, Inc. or the .NET Foundation under +the name “Mono,” that implement .NET Framework Functionality, provided +that such portions at a minimum implement all of the required parts of +the mandatory provisions of Standard ECMA-335 – Common Language +Infrastructure (CLI). + +“.NET Framework Functionality” means any functionality in Microsoft’s +.NET Framework as described in Microsoft’s API documentation on +Microsoft’s MSDN website, including the functionality in +Windowsbase.dll, but excluding all other functionality in the Windows +Presentation Foundation component of .NET Framework. + +“Applicable Patents” are those patent claims, currently owned by +Microsoft and acquired in the future, that are necessarily infringed +by Mono. For clarity, Applicable Patents do not include any patent +claims that are infringed (x) by any underlying or enabling technology +that may be used, combined, or distributed in connection with Mono +(such as hardware, operating systems, or applications that run on +Mono), (y) only as a consequence of modification of Mono, or (z) only +by the combination of Mono with third party code. diff --git a/unity-2019.4.24f1-mbe/README.md b/unity-2019.4.24f1-mbe/README.md new file mode 100644 index 000000000..22322e0b4 --- /dev/null +++ b/unity-2019.4.24f1-mbe/README.md @@ -0,0 +1,630 @@ +Unity +============================ +This is Unity Technologies fork of the open source mono project. + +### Versions +Each Unity release 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. + +### 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-trunk (PR to this branch for Mono)](https://github.com/Unity-Technologies/mono/tree/unity-trunk) +* [unity-master (PR to this branch for MonoBleedingEdge)](https://github.com/Unity-Technologies/mono/tree/unity-master) + +#### 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 +standards for C# and the Common Language Runtime. + +The Mono project is part of the [.NET Foundation](http://www.dotnetfoundation.org/) + +[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/mono/mono?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +1. [Compilation and Installation](#compilation-and-installation) +2. [Using Mono](#using-mono) +3. [Directory Roadmap](#directory-roadmap) +4. [Contributing to Mono](#contributing-to-mono) +5. [Reporting bugs](#reporting-bugs) +6. [Configuration Options](#configuration-options) +7. [Working with Submodules](#working-with-submodules) + +### Build Status + +| OS | Architecture | Status | +|--------------|--------------------|------------------------------| +| Ubuntu 14.04 | amd64 | [![ubuntu-1404-amd64][1]][2] | +| Ubuntu 14.04 | i386 | [![ubuntu-1404-i386][3]][4] | +| Debian 8 | armel | [![debian-8-armel][5]][6] | +| Debian 8 | armhf | [![debian-8-armhf][7]][8] | +| Debian 8 | arm64 | [![debian-8-arm64][9]][10] | +| OS X | amd64 | [![osx-amd64][11]][12] | +| OS X | i386 | [![osx-i386][13]][14] | +| Windows | amd64 | [![windows-amd64][15]][16] | +| Windows | i386 | [![windows-amd64][17]][18] | +| CentOS | s390x (cs) | [![centos-s390x][19]][20] | +| Debian 8 | ppc64el (cs) | [![debian-8-ppc64el][21]][22]| + +_(cs) = community supported architecture_ + +[1]: https://jenkins.mono-project.com/job/test-mono-mainline-linux/label=ubuntu-1404-amd64/badge/icon +[2]: https://jenkins.mono-project.com/job/test-mono-mainline-linux/label=ubuntu-1404-amd64 +[3]: https://jenkins.mono-project.com/job/test-mono-mainline-linux/label=ubuntu-1404-i386/badge/icon +[4]: https://jenkins.mono-project.com/job/test-mono-mainline-linux/label=ubuntu-1404-i386/ +[5]: https://jenkins.mono-project.com/job/test-mono-mainline-linux/label=debian-8-armel/badge/icon +[6]: https://jenkins.mono-project.com/job/test-mono-mainline-linux/label=debian-8-armel/ +[7]: https://jenkins.mono-project.com/job/test-mono-mainline-linux/label=debian-8-armhf/badge/icon +[8]: https://jenkins.mono-project.com/job/test-mono-mainline-linux/label=debian-8-armhf/ +[9]: https://jenkins.mono-project.com/job/test-mono-mainline-linux/label=debian-8-arm64/badge/icon +[10]: https://jenkins.mono-project.com/job/test-mono-mainline-linux/label=debian-8-arm64/ +[11]: https://jenkins.mono-project.com/job/test-mono-mainline/label=osx-amd64/badge/icon +[12]: https://jenkins.mono-project.com/job/test-mono-mainline/label=osx-amd64/ +[13]: https://jenkins.mono-project.com/job/test-mono-mainline/label=osx-i386/badge/icon +[14]: https://jenkins.mono-project.com/job/test-mono-mainline/label=osx-i386/ +[15]: https://jenkins.mono-project.com/job/z/label=w64/badge/icon +[16]: https://jenkins.mono-project.com/job/z/label=w64/ +[17]: https://jenkins.mono-project.com/job/z/label=w32/badge/icon +[18]: https://jenkins.mono-project.com/job/z/label=w32/ +[19]: https://jenkins.mono-project.com/job/test-mono-mainline-community/label=centos-s390x/badge/icon +[20]: https://jenkins.mono-project.com/job/test-mono-mainline-community/label=centos-s390x +[21]: https://jenkins.mono-project.com/job/test-mono-mainline-community-chroot/label=debian-8-ppc64el/badge/icon +[22]: https://jenkins.mono-project.com/job/test-mono-mainline-community-chroot/label=debian-8-ppc64el + +Compilation and Installation +============================ + +Building the Software +--------------------- + +Please see our guides for building Mono on +[Mac OS X](http://www.mono-project.com/docs/compiling-mono/mac/), +[Linux](http://www.mono-project.com/docs/compiling-mono/linux/) and +[Windows](http://www.mono-project.com/docs/compiling-mono/windows/). + +Note that building from Git assumes that you already have Mono installed, +so please download and [install the latest Mono release](http://www.mono-project.com/download/) +before trying to build from Git. This is required because the Mono build +relies on a working Mono C# compiler to compile itself +(also known as [bootstrapping](http://en.wikipedia.org/wiki/Bootstrapping_(compilers))). + +If you don't have a working Mono installation +--------------------------------------------- + +If you don't have a working Mono installation, you can try a slightly +more risky approach: getting the latest version of the 'monolite' distribution, +which contains just enough to run the 'mcs' compiler. You do this with: + + # Run the following line after ./autogen.sh + make get-monolite-latest + +This will download and place the files appropriately so that you can then +just run: + + make + +The build will then use the files downloaded by `make get-monolite-latest`. + +Testing and Installation +------------------------ + +You can run the mono and mcs test suites with the command: `make check`. + +Expect to find a few test suite failures. As a sanity check, you +can compare the failures you got with [https://jenkins.mono-project.com/](https://jenkins.mono-project.com/). + +You can now install mono with: `make install` + +You can verify your installation by using the mono-test-install +script, it can diagnose some common problems with Mono's install. +Failure to follow these steps may result in a broken installation. + +Using Mono +========== + +Once you have installed the software, you can run a few programs: + +* `mono program.exe` runtime engine + +* `mcs program.cs` C# compiler + +* `monodis program.exe` CIL Disassembler + +See the man pages for mono(1), mcs(1) and monodis(1) for further details. + +Directory Roadmap +================= + +* `acceptance-tests/` - Optional third party test suites used to validate Mono against a wider range of test cases. + +* `data/` - Configuration files installed as part of the Mono runtime. + +* `docs/` - Technical documents about the Mono runtime. + +* `external/` - Git submodules for external libraries (Newtonsoft.Json, ikvm, etc). + +* `man/` - Manual pages for the various Mono commands and programs. + +* `mcs/` - The class libraries, compiler and tools + + * `class/` - The class libraries (like System.*, Microsoft.Build, etc.) + + * `mcs/` - The Mono C# compiler written in C# + + * `tools/` - Tools like gacutil, ikdasm, mdoc, etc. + +* `mono/` - The core of the Mono Runtime. + + * `arch/` - Architecture specific portions. + + * `cil/` - Common Intermediate Representation, XML +definition of the CIL bytecodes. + + * `dis/` - CIL executable Disassembler + + * `io-layer/` - The I/O layer and system abstraction for +emulating the .NET IO model. + + * `metadata/` - The object system and metadata reader. + + * `mini/` - The Just in Time Compiler. + +* `runtime/` - A directory that contains the Makefiles that link the +mono/ and mcs/ build systems. + +* `samples/` -Some simple sample programs on uses of the Mono +runtime as an embedded library. + +* `scripts/` - Scripts used to invoke Mono and the corresponding program. + +Contributing to Mono +==================== + +Before submitting changes to Mono, please review the [contribution +guidelines](http://www.mono-project.com/community/contributing/). +Please pay particular attention to the [Important +Rules](http://www.mono-project.com/community/contributing/#important-rules) +section. + +Reporting bugs +============== + +To submit bug reports, please [open an issue on the mono GitHub repo](https://github.com/mono/mono/issues/new). + +Please use the search facility to ensure the same bug hasn't already +been submitted and follow our +[guidelines](http://www.mono-project.com/community/bugs/make-a-good-bug-report/) +on how to make a good bug report. + +Configuration Options +===================== + +The following are the configuration options that someone building Mono +might want to use: + +* `--with-sgen=yes,no` - Generational GC support: Used to enable or +disable the compilation of a Mono runtime with the SGen garbage +collector. + + * On platforms that support it, after building Mono, you will have +both a `mono-boehm` binary and a `mono-sgen` binary. `mono-boehm` uses Boehm, +while `mono-sgen` uses the Simple Generational GC. + +* `--with-libgc=[included, none]` - Selects the default Boehm +garbage collector engine to use. + + * *included*: (*slightly modified Boehm GC*) This is the default +value for the Boehm GC, and it's the most feature complete, it will +allow Mono to use typed allocations and support the debugger. + + * *none*: +Disables the inclusion of a Boehm garbage collector. + + * This defaults to `included`. + +* `--with-cooperative-gc` + + * If you pass this flag the Mono runtime is configured to only use + the cooperative mode of the garbage collector. If you do not pass + this flag, then you can control at runtime the use of the + cooperative GC mode by setting the `MONO_ENABLE_COOP` flag. + +* `--with-tls=__thread,pthread` + + * Controls how Mono should access thread local storage, +pthread forces Mono to use the pthread APIs, while +__thread uses compiler-optimized access to it. + + * Although __thread is faster, it requires support from +the compiler, kernel and libc. Old Linux systems do +not support with __thread. + + * This value is typically pre-configured and there is no +need to set it, unless you are trying to debug a problem. + +* `--with-sigaltstack=yes,no` + + * **Experimental**: Use at your own risk, it is known to +cause problems with garbage collection and is hard to +reproduce those bugs. + + * This controls whether Mono will install a special +signal handler to handle stack overflows. If set to +`yes`, it will turn stack overflows into the +StackOverflowException. Otherwise when a stack +overflow happens, your program will receive a +segmentation fault. + + * The configure script will try to detect if your +operating system supports this. Some older Linux +systems do not support this feature, or you might want +to override the auto-detection. + +* `--with-static_mono=yes,no` + + * This controls whether `mono` should link against a +static library (libmono.a) or a shared library +(libmono.so). + + * This defaults to `yes`, and will improve the performance +of the `mono` program. + + * This only affects the `mono' binary, the shared +library libmono.so will always be produced for +developers that want to embed the runtime in their +application. + +* `--with-xen-opt=yes,no` - Optimize code for Xen virtualization. + + * It makes Mono generate code which might be slightly +slower on average systems, but the resulting executable will run +faster under the Xen virtualization system. + + * This defaults to `yes`. + +* `--with-large-heap=yes,no` - Enable support for GC heaps larger than 3GB. + + * This defaults to `no`. + +* `--enable-small-config=yes,no` - Enable some tweaks to reduce memory usage +and disk footprint at the expense of some capabilities. + + * Typically this means that the number of threads that can be created +is limited (256), that the maximum heap size is also reduced (256 MB) +and other such limitations that still make mono useful, but more suitable +to embedded devices (like mobile phones). + + * This defaults to `no`. + +* `--with-ikvm-native=yes,no` - Controls whether the IKVM JNI interface library is +built or not. + + * This is used if you are planning on +using the IKVM Java Virtual machine with Mono. + + * This defaults to `yes`. + +* `--with-profile4=yes,no` - Whether you want to build the 4.x profile libraries +and runtime. + + * This defaults to `yes`. + +* `--with-libgdiplus=installed,sibling,` - Configure where Mono +searches for libgdiplus when running System.Drawing tests. + + * It defaults to `installed`, which means that the +library is available to Mono through the regular +system setup. + + * `sibling` can be used to specify that a libgdiplus +that resides as a sibling of this directory (mono) +should be used. + + * Or you can specify a path to a libgdiplus. + +* `--enable-minimal=LIST` + + * Use this feature to specify optional runtime +components that you might not want to include. This +is only useful for developers embedding Mono that +require a subset of Mono functionality. + * The list is a comma-separated list of components that +should be removed, these are: + + * `aot`: +Disables support for the Ahead of Time compilation. + + * `attach`: +Support for the Mono.Management assembly and the +VMAttach API (allowing code to be injected into +a target VM) + + * `com`: +Disables COM support. + + * `debug`: +Drop debugging support. + + * `decimal`: +Disables support for System.Decimal. + + * `full_messages`: +By default Mono comes with a full table +of messages for error codes. This feature +turns off uncommon error messages and reduces +the runtime size. + + * `generics`: +Generics support. Disabling this will not +allow Mono to run any 2.0 libraries or +code that contains generics. + + * `jit`: +Removes the JIT engine from the build, this reduces +the executable size, and requires that all code +executed by the virtual machine be compiled with +Full AOT before execution. + + * `large_code`: +Disables support for large assemblies. + + * `logging`: +Disables support for debug logging. + + * `pinvoke`: +Support for Platform Invocation services, +disabling this will drop support for any +libraries using DllImport. + + * `portability`: +Removes support for MONO_IOMAP, the environment +variables for simplifying porting applications that +are case-insensitive and that mix the Unix and Windows path separators. + + * `profiler`: +Disables support for the default profiler. + + * `reflection_emit`: +Drop System.Reflection.Emit support + + * `reflection_emit_save`: +Drop support for saving dynamically created +assemblies (AssemblyBuilderAccess.Save) in +System.Reflection.Emit. + + * `shadow_copy`: +Disables support for AppDomain's shadow copies +(you can disable this if you do not plan on +using appdomains). + + * `simd`: +Disables support for the Mono.SIMD intrinsics +library. + + * `ssa`: +Disables compilation for the SSA optimization +framework, and the various SSA-based optimizations. + +* `--enable-llvm` +* `--enable-loadedllvm` + + * This enables the use of LLVM as a code generation engine +for Mono. The LLVM code generator and optimizer will be +used instead of Mono's built-in code generator for both +Just in Time and Ahead of Time compilations. + + * See http://www.mono-project.com/docs/advanced/mono-llvm/ for the +full details and up-to-date information on this feature. + + * You will need to have an LLVM built that Mono can link +against. + + * The `--enable-loadedllvm` variant will make the LLVM backend +into a runtime-loadable module instead of linking it directly +into the main mono binary. + +* `--enable-big-arrays` - Enable use of arrays with indexes larger +than Int32.MaxValue. + + * By default Mono has the same limitation as .NET on +Win32 and Win64 and limits array indexes to 32-bit +values (even on 64-bit systems). + + * In certain scenarios where large arrays are required, +you can pass this flag and Mono will be built to +support 64-bit arrays. + + * This is not the default as it breaks the C embedding +ABI that we have exposed through the Mono development +cycle. + +* `--enable-parallel-mark` + + * Use this option to enable the garbage collector to use +multiple CPUs to do its work. This helps performance +on multi-CPU machines as the work is divided across CPUS. + + * This option is not currently the default on OSX +as it runs into issues there. + + * This option only applies to the Boehm GC. + +* `--enable-dtrace` + + * On Solaris and MacOS X builds a version of the Mono +runtime that contains DTrace probes and can +participate in the system profiling using DTrace. + +* `--disable-dev-random` + + * Mono uses /dev/random to obtain good random data for +any source that requires random numbers. If your +system does not support this, you might want to +disable it. + + * There are a number of runtime options to control this +also, see the man page. + +* `--with-csc=roslyn,mcs,default` + + * Use this option to configure which C# compiler to use. By default + the configure script will pick Roslyn, except on platforms where + Roslyn does not work (Big Endian systems) where it will pick mcs. + + If you specify "mcs", then Mono's C# compiler will be used. This + also allows for a complete bootstrap of Mono's core compiler and + core libraries from source. + +   If you specify "roslyn", then Roslyn's C# compiler will be used. + This currently uses Roslyn binaries. + +* `--enable-nacl` + + * This configures the Mono compiler to generate code +suitable to be used by Google's Native Client: +http://code.google.com/p/nativeclient/ + + * Currently this is used with Mono's AOT engine as +Native Client does not support JIT engines yet. + +* `--enable-wasm` + + * Use this option to configure mono to run on WebAssembly. It will + set both host and target to the WebAssembly triplet. This overrides + the values passed to `--host` or `--target` and ignored what config.sub guesses. + + This is a workaround to enable usage of old automake versions that don't + recognize the wasm triplet. + + +Working With Submodules +======================= + +Mono references several external git submodules, for example +a fork of Microsoft's reference source code that has been altered +to be suitable for use with the Mono runtime. + +This section describes how to use it. + +An initial clone should be done recursively so all submodules will also be +cloned in a single pass: + + $ git clone --recursive git@github.com:mono/mono + +Once cloned, submodules can be updated to pull down the latest changes. +This can also be done after an initial non-recursive clone: + + $ git submodule update --init --recursive + +To pull external changes into a submodule: + + $ cd + $ git pull origin + $ cd + $ git add + $ git commit + +By default, submodules are detached because they point to a specific commit. +Use `git checkout` to move back to a branch before making changes: + + $ cd + $ git checkout + # work as normal; the submodule is a normal repo + $ git commit/push new changes to the repo (submodule) + + $ cd + $ git add # this will record the new commits to the submodule + $ git commit + +To switch the repo of a submodule (this should not be a common or normal thing +to do at all), first edit `.gitmodules` to point to the new location, then: + + $ git submodule sync -- + $ git submodule update --recursive + $ git checkout + +The desired output diff is a change in `.gitmodules` to reflect the +change in the remote URL, and a change in / where you see +the desired change in the commit hash. + +License +======= + +See the LICENSE file for licensing information, and the PATENTS.TXT +file for information about Microsoft's patent grant. + +Mono Trademark Use Policy +========================= + +The use of trademarks and logos for Mono can be found [here] (http://www.dotnetfoundation.org/legal/mono-tm). + +Maintaining the Class Library Solution Files +============================================ + +Mono now ships with a solution file that can be used to build the +assemblies from an IDE. Either by opening the topmost `net_4_x.sln` +file, or to by loading one of the individual `csproj` files located in +each directory. + +These are maintained by extracting the configuration information from +our Makefiles, which as of May 2016 remain the canonical location for +configuration information. + +When changes are made to the Makefiles, a user would need to run the +following command to re-generate the solution files at the top level: + + $ make update-solution-files + diff --git a/unity-2019.4.24f1-mbe/acinclude.m4 b/unity-2019.4.24f1-mbe/acinclude.m4 new file mode 100644 index 000000000..bbc2781ea --- /dev/null +++ b/unity-2019.4.24f1-mbe/acinclude.m4 @@ -0,0 +1,182 @@ +dnl dolt, a replacement for libtool +dnl Copyright © 2007-2008 Josh Triplett +dnl Copying and distribution of this file, with or without modification, +dnl are permitted in any medium without royalty provided the copyright +dnl notice and this notice are preserved. +dnl +dnl To use dolt, invoke the DOLT macro immediately after the libtool macros. +dnl Optionally, copy this file into acinclude.m4, to avoid the need to have it +dnl installed when running autoconf on your project. + +AC_DEFUN([DOLT], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +# dolt, a replacement for libtool +# Josh Triplett +AC_PATH_PROG(DOLT_BASH, bash) +AC_MSG_CHECKING([if dolt supports this host]) +dolt_supported=yes +if test x$DOLT_BASH = x; then + dolt_supported=no +fi +if test x$GCC != xyes; then + dolt_supported=no +fi +case $host in +i?86-*-linux*|i?86-apple-darwin*|x86_64-*-linux*|powerpc-*-linux*|powerpc64-*-linux* \ +|amd64-*-freebsd*|i?86-*-freebsd*|ia64-*-freebsd*|arm*-*-linux*|sparc*-*-linux*|mips*-*-linux*|x86_64-apple-darwin*|aarch64*) + pic_options='-fPIC' + ;; +?86-pc-cygwin*|i?86-pc-cygwin*|i?86-pc-mingw32*) + pic_options='-DDLL_EXPORT' + ;; +i?86-apple-darwin*|arm-apple-darwin*) + pic_options='-fno-common' + ;; +*) + dolt_supported=no + ;; +esac +if test x$dolt_supported = xno ; then + AC_MSG_RESULT([no, falling back to libtool]) + LTCOMPILE='$(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(COMPILE)' + LTCXXCOMPILE='$(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXXCOMPILE)' +else + AC_MSG_RESULT([yes, replacing libtool]) + +dnl Start writing out doltcompile. + cat <<__DOLTCOMPILE__EOF__ >doltcompile +#!$DOLT_BASH +__DOLTCOMPILE__EOF__ + cat <<'__DOLTCOMPILE__EOF__' >>doltcompile +args=("$[]@") +for ((arg=0; arg<${#args@<:@@@:>@}; arg++)) ; do + if test x"${args@<:@$arg@:>@}" = x-o ; then + objarg=$((arg+1)) + break + fi +done +if test x$objarg = x ; then + echo 'Error: no -o on compiler command line' 1>&2 + exit 1 +fi +lo="${args@<:@$objarg@:>@}" +obj="${lo%.lo}" +if test x"$lo" = x"$obj" ; then + echo "Error: libtool object file name \"$lo\" does not end in .lo" 1>&2 + exit 1 +fi +objbase="${obj##*/}" +__DOLTCOMPILE__EOF__ + +dnl Write out shared compilation code. + if test x$enable_shared = xyes; then + cat <<'__DOLTCOMPILE__EOF__' >>doltcompile +libobjdir="${obj%$objbase}.libs" +if test ! -d "$libobjdir" ; then + mkdir_out="$(mkdir "$libobjdir" 2>&1)" + mkdir_ret=$? + if test "$mkdir_ret" -ne 0 && test ! -d "$libobjdir" ; then + echo "$mkdir_out" 1>&2 + exit $mkdir_ret + fi +fi +pic_object="$libobjdir/$objbase.o" +args@<:@$objarg@:>@="$pic_object" +__DOLTCOMPILE__EOF__ + cat <<__DOLTCOMPILE__EOF__ >>doltcompile +"\${args@<:@@@:>@}" $pic_options -DPIC || exit \$? +__DOLTCOMPILE__EOF__ + fi + +dnl Write out static compilation code. +dnl Avoid duplicate compiler output if also building shared objects. + if test x$enable_static = xyes; then + cat <<'__DOLTCOMPILE__EOF__' >>doltcompile +non_pic_object="$obj.o" +args@<:@$objarg@:>@="$non_pic_object" +__DOLTCOMPILE__EOF__ + if test x$enable_shared = xyes; then + cat <<'__DOLTCOMPILE__EOF__' >>doltcompile +"${args@<:@@@:>@}" >/dev/null 2>&1 || exit $? +__DOLTCOMPILE__EOF__ + else + cat <<'__DOLTCOMPILE__EOF__' >>doltcompile +"${args@<:@@@:>@}" || exit $? +__DOLTCOMPILE__EOF__ + fi + fi + +dnl Write out the code to write the .lo file. +dnl The second line of the .lo file must match "^# Generated by .*libtool" + cat <<'__DOLTCOMPILE__EOF__' >>doltcompile +{ +echo "# $lo - a libtool object file" +echo "# Generated by doltcompile, not libtool" +__DOLTCOMPILE__EOF__ + + if test x$enable_shared = xyes; then + cat <<'__DOLTCOMPILE__EOF__' >>doltcompile +echo "pic_object='.libs/${objbase}.o'" +__DOLTCOMPILE__EOF__ + else + cat <<'__DOLTCOMPILE__EOF__' >>doltcompile +echo pic_object=none +__DOLTCOMPILE__EOF__ + fi + + if test x$enable_static = xyes; then + cat <<'__DOLTCOMPILE__EOF__' >>doltcompile +echo "non_pic_object='${objbase}.o'" +__DOLTCOMPILE__EOF__ + else + cat <<'__DOLTCOMPILE__EOF__' >>doltcompile +echo non_pic_object=none +__DOLTCOMPILE__EOF__ + fi + + cat <<'__DOLTCOMPILE__EOF__' >>doltcompile +} > "$lo" +__DOLTCOMPILE__EOF__ + +dnl Done writing out doltcompile; substitute it for libtool compilation. + chmod +x doltcompile + LTCOMPILE='$(top_builddir)/doltcompile $(COMPILE)' + LTCXXCOMPILE='$(top_builddir)/doltcompile $(CXXCOMPILE)' + +dnl automake ignores LTCOMPILE and LTCXXCOMPILE when it has separate CFLAGS for +dnl a target, so write out a libtool wrapper to handle that case. +dnl Note that doltlibtool does not handle inferred tags or option arguments +dnl without '=', because automake does not use them. + cat <<__DOLTLIBTOOL__EOF__ > doltlibtool +#!$DOLT_BASH +__DOLTLIBTOOL__EOF__ + cat <<'__DOLTLIBTOOL__EOF__' >>doltlibtool +top_builddir_slash="${0%%doltlibtool}" +: ${top_builddir_slash:=./} +args=() +modeok=false +tagok=false +for arg in "$[]@"; do + case "$arg" in + --silent) ;; + --mode=compile) modeok=true ;; + --tag=CC|--tag=CXX) tagok=true ;; + --quiet) ;; + *) args@<:@${#args[@]}@:>@="$arg" ;; + esac +done +if $modeok && $tagok ; then + . ${top_builddir_slash}doltcompile "${args@<:@@@:>@}" +else + exec ${top_builddir_slash}libtool "$[]@" +fi +__DOLTLIBTOOL__EOF__ + +dnl Done writing out doltlibtool; substitute it for libtool. + chmod +x doltlibtool + LIBTOOL='$(top_builddir)/doltlibtool' +fi +AC_SUBST(LTCOMPILE) +AC_SUBST(LTCXXCOMPILE) +# end dolt +]) diff --git a/unity-2019.4.24f1-mbe/autogen.sh b/unity-2019.4.24f1-mbe/autogen.sh new file mode 100644 index 000000000..88c2eded6 --- /dev/null +++ b/unity-2019.4.24f1-mbe/autogen.sh @@ -0,0 +1,181 @@ +#!/usr/bin/env bash +# Run this to generate all the initial makefiles, etc. +# Ripped off from GNOME macros version + +DIE=0 + +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. + +if [ -n "$MONO_PATH" ]; then + # from -> /mono/lib:/another/mono/lib + # to -> /mono /another/mono + for i in `echo ${MONO_PATH} | tr ":" " "`; do + i=`dirname ${i}` + if [ -n "{i}" -a -d "${i}/share/aclocal" ]; then + ACLOCAL_FLAGS="-I ${i}/share/aclocal $ACLOCAL_FLAGS" + fi + if [ -n "{i}" -a -d "${i}/bin" ]; then + PATH="${i}/bin:$PATH" + fi + done + export PATH +fi + +(autoconf --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`autoconf' installed to compile Mono." + echo "Download the appropriate package for your distribution," + echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/" + DIE=1 +} + +if [ -z "$LIBTOOLIZE" ]; then + LIBTOOLIZE=`which glibtoolize 2>/dev/null` + if [ ! -x "$LIBTOOLIZE" ]; then + LIBTOOLIZE=`which libtoolize` + fi +fi + +(grep "^AM_PROG_LIBTOOL" $srcdir/configure.ac >/dev/null) && { + ($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`libtoolize' installed to compile Mono." + echo "Get ftp://ftp.gnu.org/gnu/libtool/libtool-1.2.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 + } +} + +grep "^AM_GNU_GETTEXT" $srcdir/configure.ac >/dev/null && { + grep "sed.*POTFILES" $srcdir/configure.ac >/dev/null || \ + (gettext --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`gettext' installed to compile Mono." + echo "Get ftp://alpha.gnu.org/gnu/gettext-0.10.35.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 + } +} + +(automake --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`automake' installed to compile Mono." + echo "Get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 + NO_AUTOMAKE=yes +} + + +# if no automake, don't bother testing for aclocal +test -n "$NO_AUTOMAKE" || (aclocal --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: Missing \`aclocal'. The version of \`automake'" + echo "installed doesn't appear recent enough." + echo "Get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 +} + +if test "$DIE" -eq 1; then + exit 1 +fi + +if test x$NOCONFIGURE = x && test -z "$*"; then + echo "**Warning**: I am going to run \`configure' with no arguments." + echo "If you wish to pass any to it, please specify them on the" + echo \`$0\'" command line." + echo +fi + +case $CC in +xlc ) + am_opt=--include-deps;; +esac + + +if grep "^AM_PROG_LIBTOOL" configure.ac >/dev/null; then + if test -z "$NO_LIBTOOLIZE" ; then + echo "Running libtoolize..." + $LIBTOOLIZE --force --copy + fi +fi + + +# +# Plug in the extension module +# +has_ext_mod=false +ext_mod_args='' +for PARAM; do + if [[ $PARAM =~ "--enable-extension-module" ]] ; then + has_ext_mod=true + if [[ $PARAM =~ "=" ]] ; then + ext_mod_args=`echo $PARAM | cut -d= -f2` + fi + fi +done + +if test x$has_ext_mod = xtrue; then + pushd ../mono-extensions/scripts + sh ./prepare-repo.sh $ext_mod_args || exit 1 + popd +else + cat mono/mini/Makefile.am.in > mono/mini/Makefile.am +fi + + +echo "Running aclocal -I m4 -I . $ACLOCAL_FLAGS ..." +aclocal -Wnone -I m4 -I . $ACLOCAL_FLAGS || { + echo + echo "**Error**: aclocal failed. This may mean that you have not" + echo "installed all of the packages you need, or you may need to" + echo "set ACLOCAL_FLAGS to include \"-I \$prefix/share/aclocal\"" + echo "for the prefix where you installed the packages whose" + echo "macros were not found" + exit 1 +} + +if grep "^AC_CONFIG_HEADERS" configure.ac >/dev/null; then + echo "Running autoheader..." + autoheader || { echo "**Error**: autoheader failed."; exit 1; } +fi + +echo "Running automake --gnu $am_opt ..." +automake --add-missing --gnu -Wno-portability -Wno-obsolete $am_opt || + { echo "**Error**: automake failed."; exit 1; } +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 "$@") + echo Done running libgc/autogen.sh ... +fi + +if test x$MONO_EXTRA_CONFIGURE_FLAGS != x; then + echo "MONO_EXTRA_CONFIGURE_FLAGS is $MONO_EXTRA_CONFIGURE_FLAGS" +fi + +conf_flags="$MONO_EXTRA_CONFIGURE_FLAGS --enable-maintainer-mode --enable-compile-warnings" #--enable-iso-c + +if test x$NOCONFIGURE = x; then + echo Running $srcdir/configure $conf_flags "$@" ... + $srcdir/configure $conf_flags "$@" \ + && echo Now type \`make\' to compile $PKG_NAME || exit 1 +else + echo Skipping configure process. +fi diff --git a/unity-2019.4.24f1-mbe/build-libs.bat b/unity-2019.4.24f1-mbe/build-libs.bat new file mode 100644 index 000000000..342a6b8da --- /dev/null +++ b/unity-2019.4.24f1-mbe/build-libs.bat @@ -0,0 +1,7 @@ +@echo off +cd mcs\jay +vcbuild jay.vcxproj +cd msvc\scripts +csc prepare.cs +prepare.exe ..\..\mcs core +msbuild net_4_x.sln diff --git a/unity-2019.4.24f1-mbe/build-libs.sh b/unity-2019.4.24f1-mbe/build-libs.sh new file mode 100644 index 000000000..f7583abaa --- /dev/null +++ b/unity-2019.4.24f1-mbe/build-libs.sh @@ -0,0 +1,4 @@ +#!/bin/sh +(cd mcs/jay; make) +(cd msvc/scripts/; make prepare.exe; mono prepare.exe ../../mcs core) +msbuild net_4_x.sln diff --git a/unity-2019.4.24f1-mbe/code_of_conduct.md b/unity-2019.4.24f1-mbe/code_of_conduct.md new file mode 100644 index 000000000..84bad191c --- /dev/null +++ b/unity-2019.4.24f1-mbe/code_of_conduct.md @@ -0,0 +1,50 @@ +# Contributor Code of Conduct + +As contributors and maintainers of this project, and in the interest of +fostering an open and welcoming community, we pledge to respect all people who +contribute through reporting issues, posting feature requests, updating +documentation, submitting pull requests or patches, and other activities. + +We are committed to making participation in this project a harassment-free +experience for everyone, regardless of level of experience, gender, gender +identity and expression, sexual orientation, disability, personal appearance, +body size, race, ethnicity, age, religion, or nationality. + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery +* Personal attacks +* Trolling or insulting/derogatory comments +* Public or private harassment +* Publishing other's private information, such as physical or electronic + addresses, without explicit permission +* Other unethical or unprofessional conduct + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +By adopting this Code of Conduct, project maintainers commit themselves to +fairly and consistently applying these principles to every aspect of managing +this project. Project maintainers who do not follow or enforce the Code of +Conduct may be permanently removed from the project team. + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting a project maintainer at miguel@xamarin.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. Maintainers are +obligated to maintain confidentiality with regard to the reporter of an +incident. + + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 1.3.0, available at +[http://contributor-covenant.org/version/1/3/0/][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/3/0/ diff --git a/unity-2019.4.24f1-mbe/config.rpath b/unity-2019.4.24f1-mbe/config.rpath new file mode 100644 index 000000000..17298f234 --- /dev/null +++ b/unity-2019.4.24f1-mbe/config.rpath @@ -0,0 +1,672 @@ +#! /bin/sh +# Output a system dependent set of variables, describing how to set the +# run time search path of shared libraries in an executable. +# +# Copyright 1996-2010 Free Software Foundation, Inc. +# Taken from GNU libtool, 2001 +# Originally by Gordon Matzigkeit , 1996 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. +# +# The first argument passed to this file is the canonical host specification, +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# The environment variables CC, GCC, LDFLAGS, LD, with_gnu_ld +# should be set by the caller. +# +# The set of defined variables is at the end of this script. + +# Known limitations: +# - On IRIX 6.5 with CC="cc", the run time search patch must not be longer +# than 256 bytes, otherwise the compiler driver will dump core. The only +# known workaround is to choose shorter directory names for the build +# directory and/or the installation directory. + +# All known linkers require a `.a' archive for static linking (except MSVC, +# which needs '.lib'). +libext=a +shrext=.so + +host="$1" +host_cpu=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +host_vendor=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +host_os=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` + +# Code taken from libtool.m4's _LT_CC_BASENAME. + +for cc_temp in $CC""; do + case $cc_temp in + compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; + distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; + \-*) ;; + *) break;; + esac +done +cc_basename=`echo "$cc_temp" | sed -e 's%^.*/%%'` + +# Code taken from libtool.m4's _LT_COMPILER_PIC. + +wl= +if test "$GCC" = yes; then + wl='-Wl,' +else + case "$host_os" in + aix*) + wl='-Wl,' + ;; + darwin*) + case $cc_basename in + xlc*) + wl='-Wl,' + ;; + esac + ;; + mingw* | cygwin* | pw32* | os2* | cegcc*) + ;; + hpux9* | hpux10* | hpux11*) + wl='-Wl,' + ;; + irix5* | irix6* | nonstopux*) + wl='-Wl,' + ;; + newsos6) + ;; + linux* | k*bsd*-gnu) + case $cc_basename in + ecc*) + wl='-Wl,' + ;; + icc* | ifort*) + wl='-Wl,' + ;; + lf95*) + wl='-Wl,' + ;; + pgcc | pgf77 | pgf90) + wl='-Wl,' + ;; + ccc*) + wl='-Wl,' + ;; + como) + wl='-lopt=' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + wl='-Wl,' + ;; + esac + ;; + esac + ;; + osf3* | osf4* | osf5*) + wl='-Wl,' + ;; + rdos*) + ;; + solaris*) + wl='-Wl,' + ;; + sunos4*) + wl='-Qoption ld ' + ;; + sysv4 | sysv4.2uw2* | sysv4.3*) + wl='-Wl,' + ;; + sysv4*MP*) + ;; + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + wl='-Wl,' + ;; + unicos*) + wl='-Wl,' + ;; + uts4*) + ;; + esac +fi + +# Code taken from libtool.m4's _LT_LINKER_SHLIBS. + +hardcode_libdir_flag_spec= +hardcode_libdir_separator= +hardcode_direct=no +hardcode_minus_L=no + +case "$host_os" in + cygwin* | mingw* | pw32* | cegcc*) + # FIXME: the MSVC++ port hasn't been tested in a loooong time + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + if test "$GCC" != yes; then + with_gnu_ld=no + fi + ;; + interix*) + # we just hope/assume this is gcc and not c89 (= MSVC++) + with_gnu_ld=yes + ;; + openbsd*) + with_gnu_ld=no + ;; +esac + +ld_shlibs=yes +if test "$with_gnu_ld" = yes; then + # Set some defaults for GNU ld with shared library support. These + # are reset later if shared libraries are not supported. Putting them + # here allows them to be overridden if necessary. + # Unlike libtool, we use -rpath here, not --rpath, since the documented + # option of GNU ld is called -rpath, not --rpath. + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + case "$host_os" in + aix[3-9]*) + # On AIX/PPC, the GNU linker is very broken + if test "$host_cpu" != ia64; then + ld_shlibs=no + fi + ;; + amigaos*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + # Samuel A. Falvo II reports + # that the semantics of dynamic libraries on AmigaOS, at least up + # to version 4, is to share data among multiple programs linked + # with the same dynamic library. Since this doesn't match the + # behavior of shared libraries on other platforms, we cannot use + # them. + ld_shlibs=no + ;; + beos*) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + : + else + ld_shlibs=no + fi + ;; + cygwin* | mingw* | pw32* | cegcc*) + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + hardcode_libdir_flag_spec='-L$libdir' + if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then + : + else + ld_shlibs=no + fi + ;; + interix[3-9]*) + hardcode_direct=no + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + ;; + gnu* | linux* | k*bsd*-gnu) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + : + else + ld_shlibs=no + fi + ;; + netbsd*) + ;; + solaris*) + if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then + ld_shlibs=no + elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + : + else + ld_shlibs=no + fi + ;; + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) + case `$LD -v 2>&1` in + *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) + ld_shlibs=no + ;; + *) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-rpath,$libdir`' + else + ld_shlibs=no + fi + ;; + esac + ;; + sunos4*) + hardcode_direct=yes + ;; + *) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + : + else + ld_shlibs=no + fi + ;; + esac + if test "$ld_shlibs" = no; then + hardcode_libdir_flag_spec= + fi +else + case "$host_os" in + aix3*) + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + hardcode_minus_L=yes + if test "$GCC" = yes; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + hardcode_direct=unsupported + fi + ;; + aix[4-9]*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + else + aix_use_runtimelinking=no + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) + for ld_flag in $LDFLAGS; do + if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then + aix_use_runtimelinking=yes + break + fi + done + ;; + esac + fi + hardcode_direct=yes + hardcode_libdir_separator=':' + if test "$GCC" = yes; then + case $host_os in aix4.[012]|aix4.[012].*) + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && \ + strings "$collect2name" | grep resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + hardcode_direct=unsupported + hardcode_minus_L=yes + hardcode_libdir_flag_spec='-L$libdir' + hardcode_libdir_separator= + fi + ;; + esac + fi + # Begin _LT_AC_SYS_LIBPATH_AIX. + echo 'int main () { return 0; }' > conftest.c + ${CC} ${LDFLAGS} conftest.c -o conftest + aix_libpath=`dump -H conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } +}'` + if test -z "$aix_libpath"; then + aix_libpath=`dump -HX64 conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } +}'` + fi + if test -z "$aix_libpath"; then + aix_libpath="/usr/lib:/lib" + fi + rm -f conftest.c conftest + # End _LT_AC_SYS_LIBPATH_AIX. + if test "$aix_use_runtimelinking" = yes; then + hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" + else + if test "$host_cpu" = ia64; then + hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib' + else + hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" + fi + fi + ;; + amigaos*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + # see comment about different semantics on the GNU ld section + ld_shlibs=no + ;; + bsdi[45]*) + ;; + cygwin* | mingw* | pw32* | cegcc*) + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + hardcode_libdir_flag_spec=' ' + libext=lib + ;; + darwin* | rhapsody*) + hardcode_direct=no + if test "$GCC" = yes ; then + : + else + case $cc_basename in + xlc*) + ;; + *) + ld_shlibs=no + ;; + esac + fi + ;; + dgux*) + hardcode_libdir_flag_spec='-L$libdir' + ;; + freebsd1*) + ld_shlibs=no + ;; + freebsd2.2*) + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + ;; + freebsd2*) + hardcode_direct=yes + hardcode_minus_L=yes + ;; + freebsd* | dragonfly*) + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + ;; + hpux9*) + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + ;; + hpux10*) + if test "$with_gnu_ld" = no; then + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + fi + ;; + hpux11*) + if test "$with_gnu_ld" = no; then + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + case $host_cpu in + hppa*64*|ia64*) + hardcode_direct=no + ;; + *) + hardcode_direct=yes + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + ;; + esac + fi + ;; + irix5* | irix6* | nonstopux*) + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + ;; + netbsd*) + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + ;; + newsos6) + hardcode_direct=yes + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + ;; + openbsd*) + if test -f /usr/libexec/ld.so; then + hardcode_direct=yes + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + else + case "$host_os" in + openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) + hardcode_libdir_flag_spec='-R$libdir' + ;; + *) + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + ;; + esac + fi + else + ld_shlibs=no + fi + ;; + os2*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + ;; + osf3*) + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + ;; + osf4* | osf5*) + if test "$GCC" = yes; then + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + else + # Both cc and cxx compiler support -rpath directly + hardcode_libdir_flag_spec='-rpath $libdir' + fi + hardcode_libdir_separator=: + ;; + solaris*) + hardcode_libdir_flag_spec='-R$libdir' + ;; + sunos4*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_direct=yes + hardcode_minus_L=yes + ;; + sysv4) + case $host_vendor in + sni) + hardcode_direct=yes # is this really true??? + ;; + siemens) + hardcode_direct=no + ;; + motorola) + hardcode_direct=no #Motorola manual says yes, but my tests say they lie + ;; + esac + ;; + sysv4.3*) + ;; + sysv4*MP*) + if test -d /usr/nec; then + ld_shlibs=yes + fi + ;; + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) + ;; + sysv5* | sco3.2v5* | sco5v6*) + hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-R,$libdir`' + hardcode_libdir_separator=':' + ;; + uts4*) + hardcode_libdir_flag_spec='-L$libdir' + ;; + *) + ld_shlibs=no + ;; + esac +fi + +# Check dynamic linker characteristics +# Code taken from libtool.m4's _LT_SYS_DYNAMIC_LINKER. +# Unlike libtool.m4, here we don't care about _all_ names of the library, but +# only about the one the linker finds when passed -lNAME. This is the last +# element of library_names_spec in libtool.m4, or possibly two of them if the +# linker has special search rules. +library_names_spec= # the last element of library_names_spec in libtool.m4 +libname_spec='lib$name' +case "$host_os" in + aix3*) + library_names_spec='$libname.a' + ;; + aix[4-9]*) + library_names_spec='$libname$shrext' + ;; + amigaos*) + library_names_spec='$libname.a' + ;; + beos*) + library_names_spec='$libname$shrext' + ;; + bsdi[45]*) + library_names_spec='$libname$shrext' + ;; + cygwin* | mingw* | pw32* | cegcc*) + shrext=.dll + library_names_spec='$libname.dll.a $libname.lib' + ;; + darwin* | rhapsody*) + shrext=.dylib + library_names_spec='$libname$shrext' + ;; + dgux*) + library_names_spec='$libname$shrext' + ;; + freebsd1*) + ;; + freebsd* | dragonfly*) + case "$host_os" in + freebsd[123]*) + library_names_spec='$libname$shrext$versuffix' ;; + *) + library_names_spec='$libname$shrext' ;; + esac + ;; + gnu*) + library_names_spec='$libname$shrext' + ;; + hpux9* | hpux10* | hpux11*) + case $host_cpu in + ia64*) + shrext=.so + ;; + hppa*64*) + shrext=.sl + ;; + *) + shrext=.sl + ;; + esac + library_names_spec='$libname$shrext' + ;; + interix[3-9]*) + library_names_spec='$libname$shrext' + ;; + irix5* | irix6* | nonstopux*) + library_names_spec='$libname$shrext' + case "$host_os" in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= ;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 ;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 ;; + *) libsuff= shlibsuff= ;; + esac + ;; + esac + ;; + linux*oldld* | linux*aout* | linux*coff*) + ;; + linux* | k*bsd*-gnu) + library_names_spec='$libname$shrext' + ;; + knetbsd*-gnu) + library_names_spec='$libname$shrext' + ;; + netbsd*) + library_names_spec='$libname$shrext' + ;; + newsos6) + library_names_spec='$libname$shrext' + ;; + nto-qnx*) + library_names_spec='$libname$shrext' + ;; + openbsd*) + library_names_spec='$libname$shrext$versuffix' + ;; + os2*) + libname_spec='$name' + shrext=.dll + library_names_spec='$libname.a' + ;; + osf3* | osf4* | osf5*) + library_names_spec='$libname$shrext' + ;; + rdos*) + ;; + solaris*) + library_names_spec='$libname$shrext' + ;; + sunos4*) + library_names_spec='$libname$shrext$versuffix' + ;; + sysv4 | sysv4.3*) + library_names_spec='$libname$shrext' + ;; + sysv4*MP*) + library_names_spec='$libname$shrext' + ;; + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + library_names_spec='$libname$shrext' + ;; + uts4*) + library_names_spec='$libname$shrext' + ;; +esac + +sed_quote_subst='s/\(["`$\\]\)/\\\1/g' +escaped_wl=`echo "X$wl" | sed -e 's/^X//' -e "$sed_quote_subst"` +shlibext=`echo "$shrext" | sed -e 's,^\.,,'` +escaped_libname_spec=`echo "X$libname_spec" | sed -e 's/^X//' -e "$sed_quote_subst"` +escaped_library_names_spec=`echo "X$library_names_spec" | sed -e 's/^X//' -e "$sed_quote_subst"` +escaped_hardcode_libdir_flag_spec=`echo "X$hardcode_libdir_flag_spec" | sed -e 's/^X//' -e "$sed_quote_subst"` + +LC_ALL=C sed -e 's/^\([a-zA-Z0-9_]*\)=/acl_cv_\1=/' <1 +# e.g. Debian puts Mono in /usr/bin and libs in /usr/lib/x86_64-linux-gnu/ which is +# too deep for the old method to work +reloc_libdir=`realpath --relative-to=${prefix} ${libdir} 2> /dev/null || basename ${libdir}` +AC_SUBST(reloc_libdir) + +# Set to yes if Unix sockets cannot be created in an anonymous namespace +need_link_unlink=no + +#Set to extra linker flags to be passed to the runtime binaries (mono /mono-sgen) +extra_runtime_ldflags="" + + +# Hack for WASM +# Current autotools (v1.15) doesn't have a triplet we can use for wasm so the kludge we do is to +# work around it by using a feature flag instead +AC_ARG_ENABLE(wasm,[ --enable-wasm Hack to set the current runtime to target wasm], enable_wasm=$enableval) + +# Thread configuration inspired by sleepycat's db +AC_MSG_CHECKING([host platform characteristics]) + +libgc_threads=no +has_dtrace=no +parallel_mark=yes +ikvm_native=yes + +host_win32=no +target_win32=no +platform_android=no +platform_tizen=no +platform_ios=no +host_darwin=no + + +if test "x$enable_wasm" = "xyes"; then +CFLAGS="$CFLAGS -D_REENTRANT -D_GNU_SOURCE -DNO_UNALIGNED_ACCESS -s WASM=1" +CPPFLAGS="$CPPFLAGS -D_REENTRANT -DUSE_MMAP -D_GNU_SOURCE -DNO_UNALIGNED_ACCESS -s WASM=1" +libdl="-ldl" +libgc_threads=pthreads +platform_wasm=yes + +else + +case "$host" in + *-mingw*|*-*-cygwin*) + AC_DEFINE(DISABLE_PORTABILITY,1,[Disable the io-portability layer]) + AC_DEFINE(HOST_NO_SYMLINKS,1,[This platform does not support symlinks]) + host_win32=yes + mono_cv_clang=no + if test "x$cross_compiling" = "xno"; then + if test "x$host" = "x$build" -a "x$host" = "x$target"; then + target_win32=yes + fi + else + if test "x$host" = "x$target"; then + target_win32=yes + fi + fi + HOST_CC="gcc" + # Windows Vista or later is required + CPPFLAGS="$CPPFLAGS -DWINVER=0x0600 -D_WIN32_WINNT=0x0600 -D_WIN32_IE=0x0501 -D_UNICODE -DUNICODE -DWIN32_THREADS -DFD_SETSIZE=1024" + LDFLAGS="$LDFLAGS -lmswsock -lws2_32 -lole32 -loleaut32 -lpsapi -lversion -ladvapi32 -lwinmm -lkernel32 -liphlpapi" + libmono_cflags="-mms-bitfields -mwindows" + libmono_ldflags="-mms-bitfields -mwindows" + libdl= + libgc_threads=win32 + with_sigaltstack=no + with_tls=pthread + with_sgen_default_concurrent=yes + LN_S=cp + # This forces libgc to use the DllMain based thread registration code on win32 + libgc_configure_args="$libgc_configure_args --enable-win32-dllmain=yes" + ;; + *-*-*netbsd*) + CPPFLAGS="$CPPFLAGS -D_REENTRANT -DGC_NETBSD_THREADS -D_GNU_SOURCE" + libmono_cflags="-D_REENTRANT" + LDFLAGS="$LDFLAGS -pthread" + CPPFLAGS="$CPPFLAGS -DHOST_BSD" + libmono_ldflags="-pthread" + need_link_unlink=yes + libdl="-ldl" + libgc_threads=pthreads + with_sigaltstack=no + use_sigposix=yes + with_sgen_default_concurrent=yes + ;; + *-*-kfreebsd*-gnu) + CPPFLAGS="$CPPFLAGS -DGC_FREEBSD_THREADS -D_GNU_SOURCE -D_REENTRANT -DUSE_MMAP -DUSE_MUNMAP -DTHREAD_LOCAL_ALLOC -pthread" + libmono_cflags="-D_REENTRANT -DTHREAD_LOCAL_ALLOC -pthread" + libmono_ldflags="-lpthread -pthread" + libdl="-ldl" + libgc_threads=pthreads + need_link_unlink=yes + with_sigaltstack=no + use_sigposix=yes + with_sgen_default_concurrent=yes + ;; + *-*-*freebsd*) + if test "x$PTHREAD_CFLAGS" = "x"; then + CPPFLAGS="$CPPFLAGS -DGC_FREEBSD_THREADS" + libmono_cflags= + else + CPPFLAGS="$CPPFLAGS $PTHREAD_CFLAGS -DGC_FREEBSD_THREADS" + libmono_cflags="$PTHREAD_CFLAGS" + fi + if test "x$PTHREAD_LIBS" = "x"; then + LDFLAGS="$LDFLAGS -pthread -L/usr/local/lib" + libmono_ldflags="-pthread" + else + LDFLAGS="$LDFLAGS $PTHREAD_LIBS -L/usr/local/lib" + libmono_ldflags="$PTHREAD_LIBS" + fi + CPPFLAGS="$CPPFLAGS -DHOST_BSD" + need_link_unlink=yes + AC_DEFINE(PTHREAD_POINTER_ID, 1, [pthread is a pointer]) + libdl= + libgc_threads=pthreads + use_sigposix=yes + has_dtrace=yes + with_sgen_default_concurrent=yes + ;; + *-*-*openbsd*) + CPPFLAGS="$CPPFLAGS -D_THREAD_SAFE -DGC_OPENBSD_THREADS -DHOST_BSD -D_REENTRANT -DUSE_MMAP" + if test "x$disable_munmap" != "xyes"; then + CPPFLAGS="$CPPFLAGS -DUSE_MUNMAP" + fi + libmono_cflags="-D_THREAD_SAFE -D_REENTRANT" + LDFLAGS="$LDFLAGS -pthread" + need_link_unlink=yes + AC_DEFINE(PTHREAD_POINTER_ID) + libdl= + libgc_threads=pthreads + with_sigaltstack=no + use_sigposix=yes + with_sgen_default_concurrent=yes + ;; + *-*-linux-android*) + platform_android=yes + AC_DEFINE(HOST_ANDROID,1,[Targeting the Android platform]) + AC_DEFINE(TARGET_ANDROID,1,[Targeting the Android platform]) + + CPPFLAGS="$CPPFLAGS -DGC_LINUX_THREADS -D_GNU_SOURCE -D_REENTRANT -DUSE_MMAP" + if test "x$disable_munmap" != "xyes"; then + CPPFLAGS="$CPPFLAGS -DUSE_MUNMAP" + fi + libmono_cflags="-D_REENTRANT" + libdl="-ldl" + libgc_threads=pthreads + use_sigposix=yes + + with_tls=pthread + with_sigaltstack=no + with_static_mono=no + + # Android doesn't support boehm, as it's missing + support_boehm=no + with_gc=sgen + + # isinf(3) requires -lm; see isinf check below + LDFLAGS="$LDFLAGS -lm" + + # Bionic's sets PTHREAD_STACK_MIN=2*PAGE_SIZE; doesn't define + # PAGE_SIZE; breaks mono/io-layer/collection.c + # Bionic doesn't provide S_IWRITE; breaks io-layer/io.c + CFLAGS="$CFLAGS -DPAGE_SIZE=4096 -DS_IWRITE=S_IWUSR" + CXXFLAGS="$CXXFLAGS -DPAGE_SIZE=4096 -DS_IWRITE=S_IWUSR" + + # to bypass the underscore linker check, can't work when cross-compiling + mono_cv_uscore=yes + mono_cv_clang=no + ;; + *-*-linux*) + CPPFLAGS="$CPPFLAGS -DGC_LINUX_THREADS -D_GNU_SOURCE -D_REENTRANT -DUSE_MMAP" + if test "x$disable_munmap" != "xyes"; then + CPPFLAGS="$CPPFLAGS -DUSE_MUNMAP" + fi + libmono_cflags="-D_REENTRANT" + libdl="-ldl" + libgc_threads=pthreads + use_sigposix=yes + if test "x$cross_compiling" != "xno"; then + # to bypass the underscore linker check, not + # available during cross-compilation + mono_cv_uscore=no + fi + case "$host" in + *-tizen-linux-*) + platform_tizen=yes + ;; + esac + case "$host" in + aarch64-*) + support_boehm=no + with_gc=sgen + ;; + powerpc*-*-linux*) + # https://bugzilla.novell.com/show_bug.cgi?id=504411 + disable_munmap=yes + ;; + esac + with_sgen_default_concurrent=yes + ;; + *-*-nacl*) + echo "nacl no longer supported." + exit 1 + ;; + *-*-hpux*) + CPPFLAGS="$CPPFLAGS -DGC_HPUX_THREADS -D_HPUX_SOURCE -D_XOPEN_SOURCE_EXTENDED -D_REENTRANT" + # +ESdbgasm only valid on bundled cc on RISC + # silently ignored for ia64 + if test $GCC != "yes"; then + CFLAGS="$CFLAGS +ESdbgasm" + # Arrange for run-time dereferencing of null + # pointers to produce a SIGSEGV signal. + LDFLAGS="$LDFLAGS -z" + fi + CFLAGS="$CFLAGS +ESdbgasm" + LDFLAGS="$LDFLAGS -z" + libmono_cflags="-D_REENTRANT" + libmono_ldflags="-lpthread" + libgc_threads=pthreads + need_link_unlink=yes + use_sigposix=yes + ;; + *-*-solaris*) + CPPFLAGS="$CPPFLAGS -DGC_SOLARIS_THREADS -DGC_SOLARIS_PTHREADS -D_REENTRANT -D_POSIX_PTHREAD_SEMANTICS -DUSE_MMAP -DUSE_MUNMAP -DHOST_SOLARIS" + need_link_unlink=yes + libmono_cflags="-D_REENTRANT" + libgc_threads=pthreads + has_dtrace=yes + use_sigposix=yes + enable_solaris_tar_check=yes + ;; + *-*-darwin*) + parallel_mark="Disabled_Currently_Hangs_On_MacOSX" + host_darwin=yes + target_mach=yes + CPPFLAGS="$CPPFLAGS -D_THREAD_SAFE -DGC_MACOSX_THREADS -DUSE_MMAP -DUSE_MUNMAP" + libmono_cflags="-D_THREAD_SAFE" + need_link_unlink=yes + AC_DEFINE(PTHREAD_POINTER_ID) + AC_DEFINE(USE_MACH_SEMA, 1, [...]) + libdl= + libgc_threads=pthreads + has_dtrace=yes + if test "x$cross_compiling" = "xyes"; then + has_broken_apple_cpp=yes + fi + dnl Snow Leopard is horribly broken -- it reports itself as i386-apple-darwin*, but + dnl its gcc defaults to 64-bit mode. They have also deprecated the usage of ucontext + dnl we need to set some flags to build our 32-bit binaries on 10.6 properly + case "$host" in + dnl Snow Leopard and newer config.guess reports as this + i*86-*-darwin*) + BROKEN_DARWIN_FLAGS="-arch i386 -D_XOPEN_SOURCE" + BROKEN_DARWIN_CPPFLAGS="-D_XOPEN_SOURCE" + CPPFLAGS="$CPPFLAGS $BROKEN_DARWIN_CPPFLAGS" + CFLAGS="$CFLAGS $BROKEN_DARWIN_FLAGS" + CXXFLAGS="$CXXFLAGS $BROKEN_DARWIN_FLAGS" + CCASFLAGS="$CCASFLAGS $BROKEN_DARWIN_FLAGS" + CPPFLAGS_FOR_LIBGC="$CPPFLAGS_FOR_LIBGC $BROKEN_DARWIN_CPPFLAGS" + CFLAGS_FOR_LIBGC="$CFLAGS_FOR_LIBGC $BROKEN_DARWIN_FLAGS" + with_sgen_default_concurrent=yes + ;; + x*64-*-darwin*) + with_sgen_default_concurrent=yes + ;; + arm*-darwin*) + platform_ios=yes + has_dtrace=no + ;; + aarch64*-darwin*) + platform_ios=yes + ;; + esac + ;; + *-*-haiku*) + CPPFLAGS="$CPPFLAGS -D_REENTRANT -D_THREAD_SAFE" + libmono_cflags="-D_REENTRANT -D_THREAD_SAFE" + libdl= + LIBS="$LIBS -lnetwork -ltextencoding" + need_link_unlink=yes + AC_DEFINE(PTHREAD_POINTER_ID) + dnl Haiku does not support static TLS with __thread + with_tls=pthread + libgc_threads=pthreads + use_sigposix=yes + ;; + *) + AC_MSG_WARN([*** Please add $host to configure.ac checks!]) + libdl="-ldl" + ;; +esac + +# WASM hack +fi + +AC_MSG_RESULT(ok) + +if test x$need_link_unlink = xyes; then + AC_DEFINE(NEED_LINK_UNLINK, 1, [Define if Unix sockets cannot be created in an anonymous namespace]) +fi + +if test x$host_win32 = xyes; then + AC_DEFINE(HOST_WIN32, 1, [Host Platform is Win32]) +fi + +if test x$target_win32 = xyes; then + AC_DEFINE(TARGET_WIN32, 1, [Target Platform is Win32]) +fi + +if test x$host_darwin = xyes; then + AC_DEFINE(HOST_DARWIN, 1, [Host Platform is Darwin]) +fi + +# Defined for all targets/platforms using classic Windows API support. +AC_DEFINE(HAVE_CLASSIC_WINAPI_SUPPORT, 1, [Use classic Windows API support]) +AC_DEFINE(HAVE_UWP_WINAPI_SUPPORT, 0, [Don't use UWP Windows API support]) + +AC_SUBST(extra_runtime_ldflags) +AM_CONDITIONAL(HOST_WIN32, test x$host_win32 = xyes) +AM_CONDITIONAL(TARGET_WIN32, test x$target_win32 = xyes) +AM_CONDITIONAL(HOST_LINUX, echo x$target_os | grep -q linux) +AM_CONDITIONAL(HOST_DARWIN, test x$host_darwin = xyes) +AM_CONDITIONAL(HOST_SIGPOSIX, test x$use_sigposix = xyes) +AM_CONDITIONAL(HOST_ANDROID, test x$platform_android = xyes) +AM_CONDITIONAL(HOST_TIZEN, test x$platform_tizen = xyes) +AM_CONDITIONAL(HOST_IOS, test x$platform_ios = xyes) +AM_CONDITIONAL(HOST_WASM, test x$platform_wasm = xyes) + +if test -z "$HOST_DARWIN_TRUE"; then : +PLATFORM_AOT_SUFFIX=.dylib +fi + +if test -z "$HOST_LINUX_TRUE"; then : +PLATFORM_AOT_SUFFIX=.so +fi + +if test -z "$HOST_WIN32_TRUE"; then : +PLATFORM_AOT_SUFFIX=.dll +fi + +AC_SUBST(PLATFORM_AOT_SUFFIX) + +## PLATFORM_AOT_SUFFIX not so simple for windows :-) + +AC_CHECK_TOOL(CC, gcc, gcc) +AC_PROG_CC +AC_CHECK_TOOL(CXX, g++, g++) +AC_PROG_CXX +AM_PROG_AS +AC_PROG_INSTALL +AC_PROG_AWK +AM_PROG_CC_C_O +dnl We should use AM_PROG_AS, but it's not available on automake/aclocal 1.4 +: ${CCAS='$(CC)'} +# Set ASFLAGS if not already set. +: ${CCASFLAGS='$(CFLAGS)'} +AC_SUBST(CCAS) +AC_SUBST(CCASFLAGS) + +# AC_PROG_CXX helpfully sets CXX to g++ even if no c++ compiler is found so check +# GXX instead. See http://lists.gnu.org/archive/html/bug-autoconf/2002-04/msg00056.html +if test "x$CXX" = "xg++"; then + if test "x$GXX" != "xyes"; then + # automake/libtool is so broken, it requires g++ even if the c++ sources + # are inside automake conditionals + AC_MSG_ERROR([You need to install g++]) + fi +fi + +dnl may require a specific autoconf version +dnl AC_PROG_CC_FOR_BUILD +dnl CC_FOR_BUILD not automatically detected +CC_FOR_BUILD=$CC +CFLAGS_FOR_BUILD=$CFLAGS +BUILD_EXEEXT= +if test "x$cross_compiling" = "xyes"; then + CC_FOR_BUILD=cc + CFLAGS_FOR_BUILD= + BUILD_EXEEXT="" +fi +AC_SUBST(CC_FOR_BUILD) +AC_SUBST(CFLAGS_FOR_BUILD) +AC_SUBST(HOST_CC) +AC_SUBST(BUILD_EXEEXT) + +AM_CONDITIONAL(CROSS_COMPILING, [test x$cross_compiling = xyes]) +AM_CONDITIONAL(USE_BATCH_FILES, [test x$host_win32 = xyes -a x$cross_compiling = xyes]) + +# Set STDC_HEADERS +AC_HEADER_STDC +AC_LIBTOOL_WIN32_DLL +# This causes monodis to not link correctly +#AC_DISABLE_FAST_INSTALL + +#lookup makedev() header +AC_HEADER_MAJOR + +AM_PROG_LIBTOOL +# Use dolt (http://dolt.freedesktop.org/) instead of libtool for building. +DOLT + +export_ldflags=`(./libtool --config; echo eval echo \\$export_dynamic_flag_spec) | sh` +AC_SUBST(export_ldflags) + +# Test whenever ld supports -version-script +AC_PROG_LD +AC_PROG_LD_GNU + +AM_ICONV() + +AC_CHECK_HEADERS(sys/filio.h sys/sockio.h netdb.h utime.h sys/utime.h semaphore.h sys/un.h linux/rtc.h sys/syscall.h sys/mkdev.h sys/uio.h sys/param.h sys/sysctl.h libproc.h sys/prctl.h) +AC_CHECK_HEADERS(sys/param.h sys/socket.h sys/ipc.h sys/utsname.h alloca.h ucontext.h pwd.h sys/select.h netinet/tcp.h netinet/in.h unistd.h sys/types.h link.h asm/sigcontext.h sys/inotify.h arpa/inet.h complex.h unwind.h) +AC_CHECK_HEADERS([linux/netlink.h linux/rtnetlink.h], + [], [], [#include + #include + #include ]) + +AC_CHECK_HEADERS(sys/user.h, [], [], +[ +#ifdef HAVE_SYS_PARAM_H +# include +#endif +]) + +AC_CHECK_HEADERS(linux/serial.h) + +AC_CHECK_HEADER(zlib.h, [have_zlib=yes], [have_zlib=no]) +if test x$have_zlib = xyes; then + AC_TRY_COMPILE([#include ], [ + #if defined(ZLIB_VERNUM) && (ZLIB_VERNUM >= 0x1230) + return 0; + #else + #error No good zlib found + #endif + ],[ + AC_MSG_RESULT(Using system zlib) + zlib_msg="system zlib" + AC_DEFINE(HAVE_SYS_ZLIB,1,[Have system zlib]) + ],[ + AC_MSG_RESULT(Using embedded zlib) + have_zlib=no + zlib_msg="bundled zlib" + ]) +fi + +AM_CONDITIONAL(HAVE_ZLIB, test x$have_zlib = xyes) +AC_DEFINE(HAVE_ZLIB,1,[Have system zlib]) + +# for mono/metadata/debug-symfile.c +AC_CHECK_HEADERS(elf.h) + +# for support +AC_CHECK_HEADERS(poll.h) +AC_CHECK_HEADERS(sys/poll.h) +AC_CHECK_HEADERS(sys/wait.h) +AC_CHECK_HEADERS(grp.h) +AC_CHECK_HEADERS(syslog.h) + +# for mono/dis +AC_CHECK_HEADERS(wchar.h) +AC_CHECK_HEADERS(ieeefp.h) +AC_MSG_CHECKING(for isinf) +AC_TRY_LINK([#include ], [ + int f = isinf (1.0); +], [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_ISINF, 1, [isinf available]) +], [ + # We'll have to use signals + AC_MSG_RESULT(no) +]) +# mingw +AC_CHECK_FUNCS(_finite, , AC_MSG_CHECKING(for _finite in math.h) + AC_TRY_LINK([#include ], + [ _finite(0.0); ], + AC_DEFINE(HAVE__FINITE, 1, [Have _finite in -lm]) AC_MSG_RESULT(yes), + AC_MSG_RESULT(no))) + +# for Linux statfs support +AC_CHECK_HEADERS(linux/magic.h) + +# For Android NDK unified headers +if test x$platform_android = xyes; then + AC_CHECK_HEADERS(machine/endian.h sys/endian.h) + AC_CHECK_HEADERS(android/legacy_signal_inlines.h, [have_android_signal_inlines=yes], [have_android_signal_inlines=no]) + + # Make sure SIGRT{MIN,MAX} work - they will fail to work with unified headers if building for + # API level < 21 *and* android/legacy_signal_inlines.h doesn't declare (and define) the required + # libc APIs to obtain values for SIGRT{MIN,MAX}. We perform the check only if android/legacy_signal_inlines.h + # is found because in other cases the macros will either work (for NDK < 14) or fail if the legacy header + # doesn't contain the required definitions (NDK 14) + if test x$have_android_signal_inlines = xyes; then + AC_MSG_CHECKING([Whether Android SIGRTMIN/SGRTMAX macros are valid]) + AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM([#include ],[ + int i; + for (i = SIGRTMIN + 1; i < SIGRTMAX; ++i) { + } + ])],[ + AC_MSG_RESULT(yes) + android_sigrtminmax_work=yes + ],[ + AC_MSG_RESULT(no) + android_sigrtminmax_work=no + ] + ) + + if test x$android_sigrtminmax_work = xno; then + AC_MSG_ERROR([Android SIGRTMIN/SIGRTMAX macros don't work in this NDK]) + fi + fi + + # Attempt to detect whether we're using Android NDK unified headers + AC_CHECK_HEADERS(android/api-level.h, [have_android_api_level=yes], [have_android_api_level=no]) + AC_CHECK_HEADERS(android/versioning.h, [have_android_versioning=yes], [have_android_versioning=no]) + + android_unified_headers=no + if test x$have_android_api_level = xyes; then + if test x$have_android_versioning = xyes; then + AC_MSG_CHECKING([whether using Android NDK unified headers]) + + # Both macros are defined only in the NDK unified headers + AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM([ + #include + #include + ],[ + #if __ANDROID_API_O__ == 26 && defined(__INTRODUCED_IN) + return 0 + #else + #error __ANDROID_API_O__ != 26 or the __INTRODUCED_IN macro not defined + #endif + ])],[ + AC_MSG_RESULT(yes) + android_unified_headers=yes + ],[ + AC_MSG_RESULT(no) + android_unified_headers=no + ] + ) + fi + fi + + if test x$android_unified_headers = xyes; then + AC_DEFINE(ANDROID_UNIFIED_HEADERS, 1, [Whether Android NDK unified headers are used]) + fi +fi # Android + +# not 64 bit clean in cross-compile +if test "x$enable_wasm" = "xyes"; then +AC_DEFINE(SIZEOF_VOID_P,4) +AC_DEFINE(SIZEOF_LONG,4) +ac_cv_sizeof_void_p="4" +ac_cv_sizeof_long="4" +else +AC_CHECK_SIZEOF(void *) +AC_CHECK_SIZEOF(long) +fi + +AC_CHECK_SIZEOF(int) +AC_CHECK_SIZEOF(long long) + +AC_CACHE_CHECK([for clang], + mono_cv_clang,[ + AC_TRY_COMPILE([], [ + #ifdef __clang__ + #else + #error "FAILED" + #endif + return 0; + ], + [mono_cv_clang=yes], + [mono_cv_clang=no], + []) +]) + +AC_ARG_ENABLE(visibility-hidden, +[ --disable-visibility-hidden disable usage of -fvisiblity=hidden], + disable_visibility_hidden=yes, disable_visibility_hidden=no) + +WARN='' +if test x"$GCC" = xyes; then + WARN='-Wall -Wunused -Wmissing-prototypes -Wmissing-declarations -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Wpointer-arith -Wno-cast-qual -Wwrite-strings -Wno-switch -Wno-switch-enum -Wno-unused-value -Wno-attributes -Wno-format-zero-length' + + # We require C99 with some GNU extensions, e.g. `linux` macro + CFLAGS="$CFLAGS -std=gnu99" + + # The runtime code does not respect ANSI C strict aliasing rules + CFLAGS="$CFLAGS -fno-strict-aliasing" + + # We rely on signed overflow to behave + CFLAGS="$CFLAGS -fwrapv" + + CFLAGS="$CFLAGS -DMONO_DLL_EXPORT" + if test x"$disable_visibility_hidden" = xno; then + # Don't export any symbols by default + SHARED_CFLAGS="-fvisibility=hidden" + CXXFLAGS="$CXXFLAGS -fvisibility=hidden" + fi + + ORIG_CFLAGS=$CFLAGS + # Check for the normal version, since gcc ignores unknown -Wno options + CFLAGS="$CFLAGS -Wunused-but-set-variable -Werror" + AC_MSG_CHECKING(for -Wno-unused-but-set-variable option to gcc) + AC_TRY_COMPILE([],[ + return 0; + ], [ + AC_MSG_RESULT(yes) + CFLAGS="$ORIG_CFLAGS -Wno-unused-but-set-variable" + ], [ + AC_MSG_RESULT(no) + CFLAGS=$ORIG_CFLAGS + ]) + + if test "x$mono_cv_clang" = "xyes"; then + # https://bugzilla.samba.org/show_bug.cgi?id=8118 + WARN="$WARN -Qunused-arguments" + WARN="$WARN -Wno-unused-function -Wno-tautological-compare -Wno-parentheses-equality -Wno-self-assign -Wno-return-stack-address -Wno-constant-logical-operand" + # We rely on zero length arrays in structs + WARN="$WARN -Wno-zero-length-array" + fi +else + # The Sun Forte compiler complains about inline functions that access static variables + # so disable all inlining. + case "$host" in + *-*-solaris*) + CFLAGS="$CFLAGS -Dinline=" + ;; + esac +fi +CFLAGS="$CFLAGS -g $WARN" +CFLAGS_FOR_LIBGC="$CFLAGS_FOR_LIBGC -g" +CPPFLAGS="$CPPFLAGS -g $WARN" + +# Where's the 'mcs' source tree? +if test -d $srcdir/mcs; then + mcsdir=mcs +else + mcsdir=../mcs +fi + +AC_ARG_WITH(mcs-path, [ --with-mcs-path=/path/to/mcs Specify an alternate mcs source tree], + if test x$with_mcs_path != "x" -a -d $with_mcs_path ; then + mcsdir=$with_mcs_path + fi +) + +AC_ARG_WITH(jumptables, [ --with-jumptables=yes,no enable/disable support for jumptables (ARM-only for now) (defaults to no)],[],[with_jumptables=no]) + +# +# A sanity check to catch cases where the package was unpacked +# with an ancient tar program (Solaris) +# +AC_ARG_ENABLE(solaris-tar-check, +[ --disable-solaris-tar-check disable solaris tar check], + do_solaris_tar_check=no, do_solaris_tar_check=yes) + +if test x"$do_solaris_tar_check" = xyes -a x"$enable_solaris_tar_check" = xyes; then + AC_MSG_CHECKING(integrity of package) + if test -f $mcsdir/class/System.Runtime.Serialization.Formatters.Soap/System.Runtime.Serialization.Formatters.Soap/SoapTypeMapper.cs + then + AC_MSG_RESULT(ok) + else + errorm="Your mono distribution is incomplete; if unpacking from a tar file, make sure you use GNU tar; see http://www.mono-project.com/IncompletePackage for more details" + AC_MSG_ERROR([$errorm]) + fi +fi + +if test "x$with_mcs_path" != "x"; then +mcs_topdir=$(cd "$mcsdir" && pwd) +mcs_topdir_from_srcdir=$mcs_topdir +else +mcs_topdir=$(cd "$srcdir/$mcsdir" && pwd) +mcs_topdir_from_srcdir='$(top_builddir)'/$mcsdir +fi + +# Convert mcs_topdir* paths to Windows syntax. +if test x$cross_compiling$host_win32 = xnoyes; then + mcs_topdir=$(cygpath -m $mcs_topdir) + case $mcs_topdir_from_srcdir in + /cygdrive/*) + mcs_topdir_from_srcdir=$(cygpath -m $mcs_topdir_from_srcdir) + ;; + esac +fi + +AC_SUBST([mcs_topdir]) +AC_SUBST([mcs_topdir_from_srcdir]) + +# gettext: prepare the translation directories. +# we do not configure the full gettext, as we consume it dynamically from C# +AM_PO_SUBDIRS + +if test "x$USE_NLS" = "xyes"; then + AC_CHECK_PROG(HAVE_MSGFMT, msgfmt,yes,no) + + if test "x$HAVE_MSGFMT" = "xno"; then + AC_MSG_ERROR([msgfmt not found. You need to install the 'gettext' package, or pass --enable-nls=no to configure.]) + fi +fi + +AC_PATH_PROG(PKG_CONFIG, pkg-config, no) + +pkg_config_path= +AC_ARG_WITH(crosspkgdir, [ --with-crosspkgdir=/path/to/pkg-config/dir Change pkg-config dir to custom dir], + if test x$with_crosspkgdir = "x"; then + if test -s $PKG_CONFIG_PATH; then + pkg_config_path=$PKG_CONFIG_PATH + fi + else + pkg_config_path=$with_crosspkgdir + PKG_CONFIG_PATH=$pkg_config_path + export PKG_CONFIG_PATH + fi +) + +AC_CHECK_PROG(ninja, ninja, yes, no) +AM_CONDITIONAL(NINJA, test x$ninja != xno) + +AC_ARG_ENABLE(werror, [ --enable-werror Pass -Werror to the C compiler], werror_flag=$enableval, werror_flag=no) +if test x$werror_flag = xyes; then + WERROR_CFLAGS="-Werror" +fi +AC_SUBST([WERROR_CFLAGS]) +AC_SUBST([SHARED_CFLAGS]) + +GLIB_CFLAGS='-I$(top_srcdir)/mono/eglib -I$(top_builddir)/mono/eglib' +GLIB_LIBS='$(top_builddir)/mono/eglib/libeglib.la -lm' + +AC_SUBST(GLIB_CFLAGS) +AC_SUBST(GLIB_LIBS) + +# Enable support for fast thread-local storage +# Some systems have broken support, so we allow to disable it. +AC_ARG_WITH(tls, [ --with-tls=__thread,pthread select Thread Local Storage implementation (defaults to __thread)],[],[with_tls=__thread]) + +# Enable support for using sigaltstack for SIGSEGV and stack overflow handling +# This does not work on some platforms (bug #55253) +AC_ARG_WITH(sigaltstack, [ --with-sigaltstack=yes,no enable/disable support for sigaltstack (defaults to yes)],[],[with_sigaltstack=yes]) + +AC_ARG_WITH(static_mono, [ --with-static_mono=yes,no link mono statically to libmono (faster) (defaults to yes)],[],[with_static_mono=yes]) +AC_ARG_WITH(shared_mono, [ --with-shared_mono=yes,no build a shared libmono library (defaults to yes)],[],[with_shared_mono=yes]) +# Same as --with-shared_mono=no +AC_ARG_ENABLE(libraries, [ --disable-libraries disable the build of libmono], enable_libraries=$enableval, enable_libraries=yes) + +if test "x$enable_static" = "xno"; then + with_static_mono=no +fi + +if test "x$enable_shared" = "xno"; then + with_shared_mono=no +fi + +if test "x$enable_libraries" = "xno"; then + with_shared_mono=no +fi + +AM_CONDITIONAL(DISABLE_LIBRARIES, test x$enable_libraries = xno) + +if test "x$host_win32" = "xyes"; then + # Boehm GC requires the runtime to be in its own dll + with_static_mono=no +fi + +AM_CONDITIONAL(STATIC_MONO, test x$with_static_mono != xno) +AM_CONDITIONAL(SHARED_MONO, test x$with_shared_mono != xno) +AC_ARG_ENABLE(mcs-build, [ --disable-mcs-build disable the build of the mcs directory], try_mcs_build=$enableval, enable_mcs_build=yes) +AC_ARG_ENABLE(support-build, [ --disable-support-build disable the build of the support directory], try_support_build=$enableval, enable_support_build=yes) + +AC_ARG_WITH(xen_opt, [ --with-xen_opt=yes,no Enable Xen-specific behaviour (defaults to yes)],[],[with_xen_opt=yes]) +if test "x$with_xen_opt" = "xyes" -a "x$mono_cv_clang" = "xno"; then + AC_DEFINE(MONO_XEN_OPT, 1, [Xen-specific behaviour]) + ORIG_CFLAGS=$CFLAGS + CFLAGS="$CFLAGS -mno-tls-direct-seg-refs" + AC_MSG_CHECKING(for -mno-tls-direct-seg-refs option to gcc) + AC_TRY_COMPILE([], [ + return 0; + ], [ + AC_MSG_RESULT(yes) + # Pass it to libgc as well + CFLAGS_FOR_LIBGC="$CFLAGS_FOR_LIBGC -mno-tls-direct-seg-refs" + ], [ + AC_MSG_RESULT(no) + CFLAGS=$ORIG_CFLAGS + ]) +fi + +AC_ARG_ENABLE(small-config, [ --enable-small-config Enable tweaks to reduce requirements (and capabilities)], enable_small_config=$enableval, enable_small_config=no) + +if test x$enable_small_config = xyes; then + AC_DEFINE(MONO_SMALL_CONFIG,1,[Reduce runtime requirements (and capabilities)]) + CFLAGS_FOR_LIBGC="$CFLAGS_FOR_LIBGC -DSMALL_CONFIG" +fi + +AC_ARG_ENABLE(system-aot, [ --enable-system-aot Enable the Ahead-Of-Time compilation of system assemblies during the build (on by default on some platforms)], enable_system_aot=$enableval, enable_system_aot=default) + +DISABLED_FEATURES=none +csc_compiler=default +endian=unknown +AC_C_BIGENDIAN([endian=big],[endian=little],[endian=unknown]) +AC_MSG_CHECKING([CSharp compiler to use]) +AC_ARG_WITH(csc, [ --with-csc=mcs,roslyn,default Configures the CSharp compiler to use],[ + if test x$withval = xmcs; then + csc_compiler=mcs + elif test x$withval = xroslyn; then + csc_compiler=roslyn + elif test x$withval = xdefault; then + : + else + AC_MSG_ERROR([You must supply one of "mcs", "roslyn" or "default" to the --with-csc option]) + fi +],[csc_compiler=default]) + +if test $csc_compiler = default; then + if test $endian = big; then + csc_compiler=mcs + elif test $endian = little; then + case "$host" in + powerpc*) csc_compiler=mcs ;; + *) csc_compiler=roslyn ;; + esac + else + csc_compiler=mcs + fi +fi +AC_MSG_RESULT($csc_compiler) + +# +# Set the build profiles and options before things which use them +# + +AC_ARG_WITH(profile4_x, [ --with-profile4_x=yes,no If you want to install the 4.x FX (defaults to yes)], [], [with_profile4_x=default]) +AC_ARG_WITH(monodroid, [ --with-monodroid=yes,no If you want to build the MonoDroid assemblies (defaults to no)], [], [with_monodroid=default]) +AC_ARG_WITH(monotouch, [ --with-monotouch=yes,no If you want to build the Xamarin.iOS assemblies (defaults to no)], [], [with_monotouch=default]) +AC_ARG_WITH(monotouch_watch, [ --with-monotouch_watch=yes,no If you want to build the Xamarin.WatchOS assemblies (defaults to no)], [], [with_monotouch_watch=default]) +AC_ARG_WITH(monotouch_tv, [ --with-monotouch_tv=yes,no If you want to build the Xamarin.TVOS assemblies (defaults to no)], [], [with_monotouch_tv=default]) +AC_ARG_WITH(bitcode, [ --with-bitcode=yes,no If bitcode is enabled (defaults to no)], [], [with_bitcode=default]) +AC_ARG_WITH(xammac, [ --with-xammac=yes,no If you want to build the Xamarin.Mac assemblies (defaults to no)], [], [with_xammac=default]) +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]) + + +AC_ARG_WITH(runtime_preset, [ --with-runtime_preset=net_4_x,all,aot,hybridaot,fullaot,bitcode,unreal Which default profile to build (defaults to net_4_x)], [], [with_runtime_preset=net_4_x]) + +dnl +dnl Profile defaults +dnl +TEST_PROFILE=default +enable_llvm_default=no + +with_profile4_x_default=no +with_monodroid_default=no +with_monotouch_default=no +with_monotouch_watch_default=no +with_monotouch_tv_default=no +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 + +AOT_BUILD_ATTRS=$INVARIANT_AOT_OPTIONS + +if test x$cross_compiling = xyes -o x$enable_mcs_build = xno; then + DISABLE_MCS_DOCS_default=yes +elif test x$with_runtime_preset = xnet_4_x; then + with_profile4_x_default=yes +elif test x$with_runtime_preset = xall; then + with_profile4_x_default=yes + with_monodroid_default=yes + with_monotouch_default=yes + with_monotouch_watch_default=yes + 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 +elif test x$with_runtime_preset = xfullaot; then + DISABLE_MCS_DOCS_default=yes + with_testing_aot_full_default=yes + TEST_PROFILE=testing_aot_full + + 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="--runtime=mobile -O=gsharedvt --aot=full,$INVARIANT_AOT_OPTIONS" + AOT_RUN_FLAGS="--runtime=mobile --full-aot" + AOT_MODE="full" +elif test x$with_runtime_preset = xbitcode; then + DISABLE_MCS_DOCS_default=yes + with_testing_aot_full_default=yes + with_bitcode_default=yes + with_cooperative_gc_default=yes + TEST_PROFILE=testing_aot_full + enable_llvm_default=yes + + 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="--runtime=mobile --aot=llvmonly,$INVARIANT_AOT_OPTIONS" + AOT_RUN_FLAGS="--runtime=mobile --llvmonly" + AOT_MODE="llvmonly" +elif test x$with_runtime_preset = xhybridaot; then + DISABLE_MCS_DOCS_default=yes + with_testing_aot_hybrid_default=yes + TEST_PROFILE=testing_aot_hybrid + + AOT_BUILD_FLAGS="--runtime=mobile --aot=hybrid,$INVARIANT_AOT_OPTIONS" + AOT_RUN_FLAGS="--runtime=mobile --hybrid-aot" +elif test x$with_runtime_preset = xaot; then + with_profile4_x_default=yes + + AOT_BUILD_FLAGS="--aot=$INVARIANT_AOT_OPTIONS" + AOT_RUN_FLAGS="" + + DISABLE_MCS_DOCS_default=yes +elif test x$with_runtime_preset = xwinaot; then + DISABLE_MCS_DOCS_default=yes + with_winaot_default=yes + TEST_PROFILE=winaot + + 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="--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 + TEST_PROFILE=orbis + + 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="--runtime=mobile --aot=full,$INVARIANT_AOT_OPTIONS" + AOT_RUN_FLAGS="--runtime=mobile --full-aot" + AOT_MODE="full" +elif test x$with_runtime_preset = xunreal; then + DISABLE_MCS_DOCS_default=yes + with_unreal_default=yes + TEST_PROFILE=unreal + + mono_feature_disable_com='yes' + mono_feature_disable_remoting='yes' + mono_feature_disable_appdomains='no' + + AOT_BUILD_FLAGS="--runtime=mobile --aot=hybrid,$INVARIANT_AOT_OPTIONS" + AOT_RUN_FLAGS="--runtime=mobile --hybrid-aot" +elif test x$with_runtime_preset = xwasm; then + DISABLE_MCS_DOCS_default=yes + with_wasm_default=yes + TEST_PROFILE=wasm + + 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="--runtime=mobile --aot=full,$INVARIANT_AOT_OPTIONS" + AOT_RUN_FLAGS="--runtime=mobile --full-aot" +else + with_profile4_x_default=yes +fi + +if test "x$AOT_BUILD_FLAGS" != "x"; then : + AC_SUBST(AOT_BUILD_FLAGS) + AC_SUBST(AOT_RUN_FLAGS) + # For llvmonlycheck + fullaotcheck + AC_SUBST(INVARIANT_AOT_OPTIONS) +fi + +AC_SUBST(TEST_PROFILE) + +if test "x$with_profile4_x" = "xdefault"; then + with_profile4_x=$with_profile4_x_default +fi +if test "x$with_monodroid" = "xdefault"; then + with_monodroid=$with_monodroid_default +fi +if test "x$with_monotouch" = "xdefault"; then + with_monotouch=$with_monotouch_default +fi +if test "x$with_monotouch_watch" = "xdefault"; then + with_monotouch_watch=$with_monotouch_watch_default +fi +if test "x$with_monotouch_tv" = "xdefault"; then + with_monotouch_tv=$with_monotouch_tv_default +fi +if test "x$with_bitcode" = "xdefault"; then + with_bitcode=$with_bitcode_default +fi +if test "x$with_xammac" = "xdefault"; then + with_xammac=$with_xammac_default +fi +if test "x$with_testing_aot_hybrid" = "xdefault"; then + with_testing_aot_hybrid=$with_testing_aot_hybrid_default +fi +if test "x$with_testing_aot_full" = "xdefault"; then + with_testing_aot_full=$with_testing_aot_full_default +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 +if test "x$with_unreal" = "xdefault"; then + with_unreal=$with_unreal_default +fi +if test "x$with_wasm" = "xdefault"; then + with_wasm=$with_wasm_default +fi + + +AM_CONDITIONAL(INSTALL_4_x, [test "x$with_profile4_x" = "xyes"]) +AM_CONDITIONAL(INSTALL_MONODROID, [test "x$with_monodroid" != "xno"]) +AM_CONDITIONAL(INSTALL_MONOTOUCH, [test "x$with_monotouch" != "xno"]) +AM_CONDITIONAL(INSTALL_MONOTOUCH_WATCH, [test "x$with_monotouch_watch" != "xno"]) +AM_CONDITIONAL(INSTALL_MONOTOUCH_TV, [test "x$with_monotouch_tv" != "xno"]) +AM_CONDITIONAL(BITCODE, test "x$with_bitcode" = "xyes") +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"]) +AM_CONDITIONAL(FULL_AOT_TESTS, [test "x$TEST_PROFILE" = "xtesting_aot_full"] || [test "x$TEST_PROFILE" = "xwinaot"] || [test "x$TEST_PROFILE" = "xorbis"] || [test "x$TEST_PROFILE" = "xwasm"]) +AM_CONDITIONAL(HYBRID_AOT_TESTS, [test "x$TEST_PROFILE" = "xtesting_aot_hybrid"] || [test "x$TEST_PROFILE" = "xunreal"]) + +default_profile=net_4_x +if test -z "$INSTALL_MONODROID_TRUE"; then : + default_profile=monodroid +fi +if test -z "$INSTALL_MONOTOUCH_TRUE"; then : + default_profile=monotouch +fi +if test -z "$INSTALL_XAMMAC_TRUE"; then : + default_profile=xammac +fi +if test -z "$INSTALL_TESTING_AOT_HYBRID_TRUE"; then : + default_profile=testing_aot_hybrid +fi +if test -z "$INSTALL_TESTING_AOT_FULL_TRUE"; then : + default_profile=testing_aot_full +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 +if test -z "$INSTALL_UNREAL_TRUE"; then : + default_profile=unreal +fi +if test -z "$INSTALL_WASM_TRUE"; then : + default_profile=wasm +fi +if test -z "$INSTALL_4_x_TRUE"; then : + default_profile=net_4_x +fi +DEFAULT_PROFILE=$default_profile +AC_SUBST(DEFAULT_PROFILE) + +# +# End build profile configuration +# + +if test x$USE_NLS = xprofile_default; then + + AC_MSG_CHECKING([NLS used]) + + # We make the default value for USE_NLS + # "no" on OSX because it isn't available on most + # default OSX installs. The most common configurations will + # all disable it, so this saves us typing. + if test x$host_darwin = xyes; then + USE_NLS=no; + else + USE_NLS=yes; + fi + + AC_SUBST([USE_NLS]) + AC_MSG_RESULT([$USE_NLS]) +fi + +AC_ARG_ENABLE(minimal, [ --enable-minimal=LIST drop support for LIST subsystems. + LIST is a comma-separated list from: aot, profiler, decimal, pinvoke, debug, appdomains, verifier, + reflection_emit, reflection_emit_save, large_code, logging, com, ssa, generics, attach, jit, interpreter, simd, soft_debug, perfcounters, normalization, desktop_loader, shared_perfcounters, remoting, + security, lldb, mdb, sgen_remset, sgen_marksweep_par, sgen_marksweep_fixed, sgen_marksweep_fixed_par, sgen_copying.], +[ + for feature in `echo "$enable_minimal" | sed -e "s/,/ /g"`; do + eval "mono_feature_disable_$feature='yes'" + done + DISABLED_FEATURES=$enable_minimal + disabled="Disabled: $enable_minimal" +],[]) + +AC_DEFINE_UNQUOTED(DISABLED_FEATURES, "$DISABLED_FEATURES", [String of disabled features]) + +if test "x$mono_feature_disable_aot" = "xyes"; then + AC_DEFINE(DISABLE_AOT, 1, [Disable AOT Compiler]) + enable_system_aot=no + AC_MSG_NOTICE([Disabled AOT compiler]) +fi + +if test "x$mono_feature_disable_profiler" = "xyes"; then + AC_DEFINE(DISABLE_PROFILER, 1, [Disable default profiler support]) + AC_MSG_NOTICE([Disabled support for the profiler]) +fi +AM_CONDITIONAL(DISABLE_PROFILER, test x$mono_feature_disable_profiler = xyes) + +if test "x$mono_feature_disable_decimal" = "xyes"; then + AC_DEFINE(DISABLE_DECIMAL, 1, [Disable System.Decimal support]) + AC_MSG_NOTICE([Disabled support for decimal]) +fi + +if test "x$mono_feature_disable_pinvoke" = "xyes"; then + AC_DEFINE(DISABLE_PINVOKE, 1, [Disable P/Invoke support]) + AC_MSG_NOTICE([Disabled support for P/Invoke]) +fi + +if test "x$mono_feature_disable_debug" = "xyes"; then + AC_DEFINE(DISABLE_DEBUG, 1, [Disable runtime debugging support]) + AC_MSG_NOTICE([Disabled support for runtime debugging]) +fi + +if test "x$mono_feature_disable_reflection_emit" = "xyes"; then + AC_DEFINE(DISABLE_REFLECTION_EMIT, 1, [Disable reflection emit support]) + mono_feature_disable_reflection_emit_save=yes + AC_MSG_NOTICE([Disabled support for Reflection.Emit]) +fi + +if test "x$mono_feature_disable_reflection_emit_save" = "xyes"; then + AC_DEFINE(DISABLE_REFLECTION_EMIT_SAVE, 1, [Disable assembly saving support in reflection emit]) + AC_MSG_NOTICE([Disabled support for Reflection.Emit.Save]) +fi + +if test "x$mono_feature_disable_large_code" = "xyes"; then + AC_DEFINE(DISABLE_LARGE_CODE, 1, [Disable support for huge assemblies]) + AC_MSG_NOTICE([Disabled support for large assemblies]) +fi + +if test "x$mono_feature_disable_logging" = "xyes"; then + AC_DEFINE(DISABLE_LOGGING, 1, [Disable support debug logging]) + AC_MSG_NOTICE([Disabled support for logging]) +fi + +if test "x$mono_feature_disable_com" = "xyes"; then + AC_DEFINE(DISABLE_COM, 1, [Disable COM support]) + AC_MSG_NOTICE([Disabled COM support]) +fi + +if test "x$mono_feature_disable_ssa" = "xyes"; then + AC_DEFINE(DISABLE_SSA, 1, [Disable advanced SSA JIT optimizations]) + AC_MSG_NOTICE([Disabled SSA JIT optimizations]) +fi + +if test "x$mono_feature_disable_generics" = "xyes"; then + AC_DEFINE(DISABLE_GENERICS, 1, [Disable generics support]) + AC_MSG_NOTICE([Disabled Generics Support]) +fi + +if test "x$mono_feature_disable_shadowcopy" = "xyes"; then + AC_DEFINE(DISABLE_SHADOW_COPY, 1, [Disable Shadow Copy for AppDomains]) + AC_MSG_NOTICE([Disabled Shadow copy for AppDomains]) +fi + +if test "x$mono_feature_disable_portability" = "xyes"; then + AC_DEFINE(DISABLE_PORTABILITY, 1, [Disables the IO portability layer]) + AC_MSG_NOTICE([Disabled IO Portability layer]) +fi + +if test "x$mono_feature_disable_attach" = "xyes"; then + AC_DEFINE(DISABLE_ATTACH, 1, [Disable agent attach support]) + AC_MSG_NOTICE([Disabled agent attach]) +fi + +if test "x$mono_feature_disable_verifier" = "xyes"; then + AC_DEFINE(DISABLE_VERIFIER, 1, [Disables the verifier]) + AC_MSG_NOTICE([Disabled the metadata and IL verifiers]) +fi + +if test "x$mono_feature_disable_jit" = "xyes"; then + AC_DEFINE(DISABLE_JIT, 1, [Disable the JIT, only full-aot mode or interpreter will be supported by the runtime.]) + AC_MSG_NOTICE([Disabled the JIT engine, only full AOT or interpreter will be supported]) +fi + +AM_CONDITIONAL(DISABLE_JIT, test x$mono_feature_disable_jit = xyes) + +if test "x$mono_feature_disable_interpreter" = "xyes"; then + AC_DEFINE(DISABLE_INTERPRETER, 1, [Disable the interpreter.]) + AC_MSG_NOTICE([Disabled the interpreter]) +fi + +AM_CONDITIONAL(DISABLE_INTERPRETER, test x$mono_feature_disable_interpreter = xyes) + +if test "x$mono_feature_disable_simd" = "xyes"; then + AC_DEFINE(DISABLE_SIMD, 1, [Disable SIMD intrinsics related optimizations.]) + AC_MSG_NOTICE([Disabled SIMD support]) +fi + +if test "x$mono_feature_disable_soft_debug" = "xyes"; then + AC_DEFINE(DISABLE_SOFT_DEBUG, 1, [Disable Soft Debugger Agent.]) + AC_MSG_NOTICE([Disabled Soft Debugger.]) +fi + +if test "x$mono_feature_disable_perfcounters" = "xyes"; then + AC_DEFINE(DISABLE_PERFCOUNTERS, 1, [Disable Performance Counters.]) + AC_MSG_NOTICE([Disabled Performance Counters.]) +fi +if test "x$mono_feature_disable_normalization" = "xyes"; then + AC_DEFINE(DISABLE_NORMALIZATION, 1, [Disable String normalization support.]) + AC_MSG_NOTICE([Disabled String normalization support.]) +fi + +#TODO: remove assembly_remapping feature name once everyone is using desktop_loader +if test "x$mono_feature_disable_assembly_remapping" = "xyes" || test "x$mono_feature_disable_desktop_loader" = "xyes"; then + AC_DEFINE(DISABLE_DESKTOP_LOADER, 1, [Disable desktop assembly loader semantics.]) + AC_MSG_NOTICE([Disabled desktop assembly loader semantics.]) +fi + +if test "x$mono_feature_disable_shared_perfcounters" = "xyes"; then + AC_DEFINE(DISABLE_SHARED_PERFCOUNTERS, 1, [Disable shared perfcounters.]) + AC_MSG_NOTICE([Disabled Shared perfcounters.]) +fi + +if test "x$mono_feature_disable_appdomains" = "xyes"; then + AC_DEFINE(DISABLE_APPDOMAINS, 1, [Disable support for multiple appdomains.]) + AC_MSG_NOTICE([Disabled support for multiple appdomains.]) +fi + +if test "x$mono_feature_disable_remoting" = "xyes"; then + AC_DEFINE(DISABLE_REMOTING, 1, [Disable remoting support (This disables type proxies and make com non-functional)]) + AC_MSG_NOTICE([Disabled remoting]) +fi + +if test "x$mono_feature_disable_security" = "xyes"; then + AC_DEFINE(DISABLE_SECURITY, 1, [Disable CAS/CoreCLR security]) + AC_MSG_NOTICE([Disabled CAS/CoreCLR security manager (used e.g. for Moonlight)]) +fi + +if test "x$mono_feature_disable_lldb" = "xyes"; then + AC_DEFINE(DISABLE_LLDB, 1, [Disable support code for the LLDB plugin.]) + AC_MSG_NOTICE([Disabled LLDB plugin support code.]) +fi + +if test "x$mono_feature_disable_mdb" = "xyes"; then + AC_DEFINE(DISABLE_MDB, 1, [Disable support for .mdb symbol files.]) + AC_MSG_NOTICE([Disabled support for .mdb symbol files.]) +fi + +if test "x$mono_feature_disable_sgen_remset" = "xyes"; then + AC_DEFINE(DISABLE_SGEN_REMSET, 1, [Disable wbarrier=remset support in SGEN.]) + AC_MSG_NOTICE([Disabled wbarrier=remset support in SGEN.]) +fi + +if test "x$mono_feature_disable_sgen_marksweep_par" = "xyes"; then + AC_DEFINE(DISABLE_SGEN_MAJOR_MARKSWEEP_PAR, 1, [Disable major=marksweep-par support in SGEN.]) + AC_MSG_NOTICE([Disabled major=marksweep-par support in SGEN.]) +fi + +if test "x$mono_feature_disable_sgen_marksweep_fixed" = "xyes"; then + AC_DEFINE(DISABLE_SGEN_MAJOR_MARKSWEEP_FIXED, 1, [Disable major=marksweep-fixed support in SGEN.]) + AC_MSG_NOTICE([Disabled major=marksweep-fixed support in SGEN.]) +fi + +if test "x$mono_feature_disable_sgen_marksweep_fixed_par" = "xyes"; then + AC_DEFINE(DISABLE_SGEN_MAJOR_MARKSWEEP_FIXED_PAR, 1, [Disable major=marksweep-fixed-par support in SGEN.]) + AC_MSG_NOTICE([Disabled major=marksweep-fixed-par support in SGEN.]) +fi + +if test "x$mono_feature_disable_sgen_copying" = "xyes"; then + AC_DEFINE(DISABLE_SGEN_MAJOR_COPYING, 1, [Disable major=copying support in SGEN.]) + AC_MSG_NOTICE([Disabled major=copying support in SGEN.]) +fi + +AC_ARG_ENABLE(executables, [ --disable-executables disable the build of the runtime executables], enable_executables=$enableval, enable_executables=yes) +AM_CONDITIONAL(DISABLE_EXECUTABLES, test x$enable_executables = xno) + +has_extension_module=no +AC_ARG_ENABLE(extension-module, [ --enable-extension-module=LIST enable the core-extensions from LIST], +[ + for extension in `echo "$enable_extension_module" | sed -e "s/,/ /g"`; do + if test x$extension = xdefault ; then + has_extension_module=yes; + fi + done + if test x$enable_extension_module = xyes; then + has_extension_module=yes; + fi +], []) + +AM_CONDITIONAL([HAS_EXTENSION_MODULE], [test x$has_extension_module != xno]) + +if test x$has_extension_module != xno ; then + AC_DEFINE([ENABLE_EXTENSION_MODULE], 1, [Extension module enabled]) + AC_MSG_NOTICE([Enabling mono extension module.]) +fi + +# Deprecated +AC_ARG_ENABLE(gsharedvt, [ --enable-gsharedvt Enable generic valuetype sharing (Deprecated)], enable_gsharedvt=$enableval, enable_gsharedvt=no) + +AC_MSG_CHECKING(for visibility __attribute__) +AC_COMPILE_IFELSE([ + AC_LANG_SOURCE([[ + void __attribute__ ((visibility ("hidden"))) doit (void) {} + int main () { doit (); return 0; } + ]]) +], [ + have_visibility_hidden=yes + AC_MSG_RESULT(yes) +], [ + have_visibility_hidden=no + 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 + +AC_ARG_WITH(libgc, [ --with-libgc=included,none Controls the Boehm GC config, default=included],[libgc=$with_libgc],[libgc=included]) + +AC_ARG_ENABLE(boehm, [ --disable-boehm Disable the Boehm GC.], support_boehm=$enableval,support_boehm=${support_boehm:-yes}) +AM_CONDITIONAL(SUPPORT_BOEHM, test x$support_boehm = xyes) + +if test "x$support_boehm" = "xyes"; then + + AC_ARG_ENABLE(parallel-mark, [ --enable-parallel-mark Enables Boehm GC Parallel Marking], enable_parallel_mark=$enableval, enable_parallel_mark=$parallel_mark) + if test x$enable_parallel_mark = xyes; then + libgc_configure_args="$libgc_configure_args --enable-parallel-mark" + fi + + gc_msg="" + LIBGC_CPPFLAGS= + LIBGC_LIBS= + LIBGC_STATIC_LIBS= + libgc_dir= + case "x$libgc" in + xincluded) + if test "x$support_boehm" = "xyes"; then + libgc_dir=libgc + fi + + LIBGC_CPPFLAGS='-I$(top_srcdir)/libgc/include' + LIBGC_LIBS='$(top_builddir)/libgc/libmonogc.la' + LIBGC_STATIC_LIBS='$(top_builddir)/libgc/libmonogc-static.la' + + BOEHM_DEFINES="-DHAVE_BOEHM_GC" + + if test x$target_win32 = xyes; then + BOEHM_DEFINES="$BOEHM_DEFINES -DGC_NOT_DLL" + CFLAGS_FOR_LIBGC="$CFLAGS_FOR_LIBGC -DGC_BUILD -DGC_NOT_DLL" + fi + + gc_msg="Included Boehm GC with typed GC" + if test x$enable_parallel_mark = xyes; then + AC_DEFINE_UNQUOTED(DEFAULT_GC_NAME, "Included Boehm (with typed GC and Parallel Mark)", [GC description]) + gc_msg="$gc_msg and parallel mark" + else + AC_DEFINE_UNQUOTED(DEFAULT_GC_NAME, "Included Boehm (with typed GC)", [GC description]) + fi + ;; + + xboehm|xbohem|xyes) + AC_MSG_WARN("External Boehm is no longer supported") + ;; + + xsgen) + AC_MSG_WARN("Use --with-sgen instead, --with-libgc= controls Boehm configuration") + ;; + + xnone) + AC_MSG_WARN("Compiling mono without GC.") + AC_DEFINE_UNQUOTED(DEFAULT_GC_NAME, "none", [GC description]) + AC_DEFINE(HAVE_NULL_GC,1,[No GC support.]) + gc_msg="none" + ;; + *) + AC_MSG_ERROR([Invalid argument $libgc to --with-libgc.]) + ;; + esac + + AC_ARG_WITH(large-heap, [ --with-large-heap=yes,no Enable support for GC heaps larger than 3GB (defaults to no)], [large_heap=$withval], [large_heap=no]) + if test "x$large_heap" = "xyes"; then + CPPFLAGS="$CPPFLAGS -DLARGE_CONFIG" + fi + + AC_SUBST(LIBGC_CPPFLAGS) + AC_SUBST(LIBGC_LIBS) + 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") + +dnl +dnl End of Boehm GC Configuration +dnl + +dnl ************************************* +dnl *** Checks for zero length arrays *** +dnl ************************************* +AC_MSG_CHECKING(whether $CC supports zero length arrays) +AC_TRY_COMPILE([ + struct s { + int length; + char data [0]; + }; +], [], [ + AC_MSG_RESULT(yes) + AC_DEFINE_UNQUOTED(MONO_ZERO_LEN_ARRAY, 0, [Length of zero length arrays]) +], [ + AC_MSG_RESULT(no) + AC_DEFINE_UNQUOTED(MONO_ZERO_LEN_ARRAY, 1, [Length of zero length arrays]) +]) + +dnl *********************************** +dnl *** Checks for signals +dnl *********************************** +AC_CHECK_HEADERS(signal.h) +AC_CHECK_FUNCS(sigaction) +AC_CHECK_FUNCS(kill) +AC_CHECK_FUNCS(signal) + +# signal() is declared inline in Android headers +# so we need to workaround it by overriding the check. +if test x$platform_android = xyes; then + AC_DEFINE(HAVE_SIGNAL,1) +fi + +if test x$host_win32 = xno; then + + dnl hires monotonic clock support + AC_SEARCH_LIBS(clock_gettime, rt) + AC_CHECK_FUNCS(clock_nanosleep) + + dnl dynamic loader support + AC_CHECK_FUNC(dlopen, DL_LIB="", + AC_CHECK_LIB(dl, dlopen, DL_LIB="-ldl", dl_support=no) + ) + if test x$dl_support = xno; then + AC_MSG_WARN([No dynamic loading support available]) + else + 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); + }], + [mono_cv_uscore=yes], + [mono_cv_uscore=no], + []) + ]) + if test "x$mono_cv_uscore" = "xyes"; then + MONO_DL_NEED_USCORE=1 + else + MONO_DL_NEED_USCORE=0 + fi + AC_SUBST(MONO_DL_NEED_USCORE) + AC_CHECK_FUNC(dlerror) + fi + + dnl ****************************************************************** + dnl *** Checks for the IKVM JNI interface library *** + dnl ****************************************************************** + AC_ARG_WITH(ikvm-native, [ --with-ikvm-native=yes,no build the IKVM JNI interface library (defaults to yes)],[with_ikvm_native=$withval],[with_ikvm_native=$ikvm_native]) + + ikvm_native_dir= + if test x$with_ikvm_native = xyes; then + ikvm_native_dir=ikvm-native + jdk_headers_found="IKVM Native" + fi + + AC_SUBST(ikvm_native_dir) + + AC_CHECK_HEADERS(execinfo.h) + + AC_CHECK_HEADERS(sys/auxv.h sys/resource.h) + + AC_CHECK_FUNCS(getgrgid_r) + AC_CHECK_FUNCS(getgrnam_r) + AC_CHECK_FUNCS(getresuid) + AC_CHECK_FUNCS(setresuid) + AC_CHECK_FUNCS(kqueue) + AC_CHECK_FUNCS(backtrace_symbols) + AC_CHECK_FUNCS(mkstemp) + AC_CHECK_FUNCS(mmap) + AC_CHECK_FUNCS(madvise) + AC_CHECK_FUNCS(getrusage) + AC_CHECK_FUNCS(getpriority) + AC_CHECK_FUNCS(setpriority) + AC_CHECK_FUNCS(dl_iterate_phdr) + AC_CHECK_FUNCS(dladdr) + AC_CHECK_FUNCS(sysconf) + AC_CHECK_FUNCS(getrlimit) + AC_CHECK_FUNCS(prctl) + + AC_CHECK_FUNCS(sched_getaffinity) + AC_CHECK_FUNCS(sched_setaffinity) + AC_CHECK_FUNCS(sched_getcpu) + + if test x$platform_android != xyes; then + AC_CHECK_FUNCS(getpwnam_r) + AC_CHECK_FUNCS(getpwuid_r) + fi + + AC_FUNC_STRERROR_R() + + dnl **************************************************************** + dnl *** Check for sched_setaffinity from glibc versions before *** + dnl *** 2.3.4. The older versions of the function only take 2 *** + dnl *** parameters, not 3. *** + dnl *** *** + dnl *** Because the interface change was not made in a minor *** + dnl *** version rev, the __GLIBC__ and __GLIBC_MINOR__ macros *** + dnl *** won't always indicate the interface sched_affinity has. *** + dnl **************************************************************** + AC_MSG_CHECKING(for sched_setaffinity from glibc < 2.3.4) + AC_TRY_COMPILE([#include ], [ + int mask = 1; + sched_setaffinity(0, &mask); + return 0; + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(GLIBC_BEFORE_2_3_4_SCHED_SETAFFINITY, 1, [Have GLIBC_BEFORE_2_3_4_SCHED_SETAFFINITY]) + ], [ + # We have the new, three-parameter version + AC_MSG_RESULT(no) + ]) + AC_TRY_COMPILE([#include ], [ + CPU_COUNT((void *) 0); + ], [ + AC_MSG_RESULT(yes) + AC_DEFINE(GLIBC_HAS_CPU_COUNT, 1, [GLIBC has CPU_COUNT macro in sched.h]) + ], [ + # We have the new, three-parameter version + AC_MSG_RESULT(no) + ]) + + dnl ****************************************************************** + dnl *** Check for large file support *** + dnl *** (If we were using autoconf 2.50 we'd use AC_SYS_LARGEFILE) *** + dnl ****************************************************************** + + # Check that off_t can represent 2**63 - 1 correctly, working around + # potential compiler bugs. Defines LARGE_FILE_SUPPORT, adds $1 to + # CPPFLAGS and sets $large_offt to yes if the test succeeds + large_offt=no + AC_DEFUN([LARGE_FILES], [ + large_CPPFLAGS=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $1" + AC_TRY_COMPILE([ + #include + #include + ], [ + /* Lifted this compile time assert method from: http://www.jaggersoft.com/pubs/CVu11_3.html */ + #define COMPILE_TIME_ASSERT(pred) \ + switch(0){case 0:case pred:;} + COMPILE_TIME_ASSERT(sizeof(off_t) * CHAR_BIT == 64); + ], [ + AC_MSG_RESULT(ok) + AC_DEFINE(HAVE_LARGE_FILE_SUPPORT, 1, [Have large file support]) + large_CPPFLAGS="$large_CPPFLAGS $1" + large_offt=yes + ], [ + AC_MSG_RESULT(no) + ]) + CPPFLAGS=$large_CPPFLAGS + ]) + + AC_MSG_CHECKING(if off_t is 64 bits wide) + LARGE_FILES("") + if test $large_offt = no; then + AC_MSG_CHECKING(if _FILE_OFFSET_BITS=64 gives 64 bit off_t) + LARGE_FILES("-D_FILE_OFFSET_BITS=64") + fi + if test $large_offt = no; then + AC_MSG_WARN([No 64 bit file size support available]) + fi + + dnl ***************************** + dnl *** Checks for libsocket *** + dnl ***************************** + AC_CHECK_LIB(socket, socket, LIBS="$LIBS -lsocket") + + case "$host" in + *-*-*freebsd*) + dnl ***************************** + dnl *** Checks for libinotify *** + dnl ***************************** + AC_CHECK_LIB(inotify, inotify_init, LIBS="$LIBS -linotify") + esac + + dnl ******************************* + dnl *** Checks for MSG_NOSIGNAL *** + dnl ******************************* + AC_MSG_CHECKING(for MSG_NOSIGNAL) + AC_TRY_COMPILE([#include ], [ + int f = MSG_NOSIGNAL; + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_MSG_NOSIGNAL, 1, [Have MSG_NOSIGNAL]) + ], [ + # We'll have to use signals + AC_MSG_RESULT(no) + ]) + + dnl ***************************** + dnl *** Checks for IPPROTO_IP *** + dnl ***************************** + AC_MSG_CHECKING(for IPPROTO_IP) + AC_TRY_COMPILE([#include ], [ + int level = IPPROTO_IP; + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_IPPROTO_IP, 1, [Have IPPROTO_IP]) + ], [ + # We'll have to use getprotobyname + AC_MSG_RESULT(no) + ]) + + dnl ******************************* + dnl *** Checks for IPPROTO_IPV6 *** + dnl ******************************* + AC_MSG_CHECKING(for IPPROTO_IPV6) + AC_TRY_COMPILE([#include ], [ + int level = IPPROTO_IPV6; + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_IPPROTO_IPV6, 1, [Have IPPROTO_IPV6]) + ], [ + # We'll have to use getprotobyname + AC_MSG_RESULT(no) + ]) + + dnl ****************************** + dnl *** Checks for IPPROTO_TCP *** + dnl ****************************** + AC_MSG_CHECKING(for IPPROTO_TCP) + AC_TRY_COMPILE([#include ], [ + int level = IPPROTO_TCP; + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_IPPROTO_TCP, 1, [Have IPPROTO_TCP]) + ], [ + # We'll have to use getprotobyname + AC_MSG_RESULT(no) + ]) + + dnl ***************************** + dnl *** Checks for SOL_IP *** + dnl ***************************** + AC_MSG_CHECKING(for SOL_IP) + AC_TRY_COMPILE([#include ], [ + int level = SOL_IP; + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_SOL_IP, 1, [Have SOL_IP]) + ], [ + # We'll have to use getprotobyname + AC_MSG_RESULT(no) + ]) + + dnl ***************************** + dnl *** Checks for SOL_IPV6 *** + dnl ***************************** + AC_MSG_CHECKING(for SOL_IPV6) + AC_TRY_COMPILE([#include ], [ + int level = SOL_IPV6; + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_SOL_IPV6, 1, [Have SOL_IPV6]) + ], [ + # We'll have to use getprotobyname + AC_MSG_RESULT(no) + ]) + + dnl ***************************** + dnl *** Checks for SOL_TCP *** + dnl ***************************** + AC_MSG_CHECKING(for SOL_TCP) + AC_TRY_COMPILE([#include ], [ + int level = SOL_TCP; + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_SOL_TCP, 1, [Have SOL_TCP]) + ], [ + # We'll have to use getprotobyname + AC_MSG_RESULT(no) + ]) + + dnl ***************************** + dnl *** Checks for IP_PKTINFO *** + dnl ***************************** + AC_MSG_CHECKING(for IP_PKTINFO) + AC_TRY_COMPILE([#include ], [ + int level = IP_PKTINFO; + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_IP_PKTINFO, 1, [Have IP_PKTINFO]) + ], [ + AC_MSG_RESULT(no) + ]) + + dnl ***************************** + dnl *** Checks for IPV6_PKTINFO *** + dnl ***************************** + AC_MSG_CHECKING(for IPV6_PKTINFO) + AC_TRY_COMPILE([#include ], [ + int level = IPV6_PKTINFO; + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_IPV6_PKTINFO, 1, [Have IPV6_PKTINFO]) + ], [ + AC_MSG_RESULT(no) + ]) + + dnl ********************************** + dnl *** Checks for IP_DONTFRAG *** + dnl ********************************** + AC_MSG_CHECKING(for IP_DONTFRAG) + AC_TRY_COMPILE([#include ], [ + int level = IP_DONTFRAG; + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_IP_DONTFRAG, 1, [Have IP_DONTFRAG]) + ], [ + AC_MSG_RESULT(no) + ]) + + dnl ********************************** + dnl *** Checks for IP_DONTFRAGMENT *** + dnl ********************************** + AC_MSG_CHECKING(for IP_DONTFRAGMENT) + AC_TRY_COMPILE([#include ], [ + int level = IP_DONTFRAGMENT; + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_IP_DONTFRAGMENT, 1, [Have IP_DONTFRAGMENT]) + ], [ + AC_MSG_RESULT(no) + ]) + + dnl ********************************** + dnl *** Checks for IP_MTU_DISCOVER *** + dnl ********************************** + AC_MSG_CHECKING(for IP_MTU_DISCOVER) + AC_TRY_COMPILE([#include ], [ + int level = IP_MTU_DISCOVER; + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_IP_MTU_DISCOVER, 1, [Have IP_MTU_DISCOVER]) + ], [ + AC_MSG_RESULT(no) + ]) + + dnl ********************************** + dnl *** Checks for IP_PMTUDISC_DO *** + dnl ********************************** + AC_MSG_CHECKING(for IP_PMTUDISC_DO) + AC_TRY_COMPILE([#include ], [ + int level = IP_PMTUDISC_DO; + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_IP_PMTUDISC_DO, 1, [Have IP_PMTUDISC_DO]) + ], [ + AC_MSG_RESULT(no) + ]) + + dnl ********************************* + dnl *** Check for struct ip_mreqn *** + dnl ********************************* + AC_MSG_CHECKING(for struct ip_mreqn) + AC_TRY_COMPILE([#include ], [ + struct ip_mreqn mreq; + mreq.imr_address.s_addr = 0; + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_STRUCT_IP_MREQN, 1, [Have struct ip_mreqn]) + ], [ + # We'll just have to try and use struct ip_mreq + AC_MSG_RESULT(no) + AC_MSG_CHECKING(for struct ip_mreq) + AC_TRY_COMPILE([#include ], [ + struct ip_mreq mreq; + mreq.imr_interface.s_addr = 0; + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_STRUCT_IP_MREQ, 1, [Have struct ip_mreq]) + ], [ + # No multicast support + AC_MSG_RESULT(no) + ]) + ]) + + dnl ********************************** + dnl *** Check for getaddrinfo *** + dnl ********************************** + AC_MSG_CHECKING(for getaddrinfo) + AC_TRY_LINK([ + #include + #include + ], [ + getaddrinfo(NULL,NULL,NULL,NULL); + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_GETADDRINFO, 1, [Have getaddrinfo]) + ], [ + AC_MSG_RESULT(no) + ]) + + dnl ********************************** + dnl *** Check for gethostbyname2_r *** + dnl ********************************** + AC_MSG_CHECKING(for gethostbyname2_r) + AC_TRY_LINK([ + #include + #include + ], [ + + gethostbyname2_r(NULL,0,NULL,NULL,0,NULL,NULL); + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_GETHOSTBYNAME2_R, 1, [Have gethostbyname2_r]) + ], [ + AC_MSG_RESULT(no) + ]) + + dnl ********************************** + dnl *** Check for gethostbyname2 *** + dnl ********************************** + AC_MSG_CHECKING(for gethostbyname2) + AC_TRY_LINK([ + #include + #include + ], [ + gethostbyname2(NULL,0); + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_GETHOSTBYNAME2, 1, [Have gethostbyname2]) + ], [ + AC_MSG_RESULT(no) + ]) + + dnl ********************************** + dnl *** Check for gethostbyname *** + dnl ********************************** + AC_MSG_CHECKING(for gethostbyname) + AC_TRY_LINK([ + #include + #include + ], [ + gethostbyname(NULL); + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_GETHOSTBYNAME, 1, [Have gethostbyname]) + ], [ + AC_MSG_RESULT(no) + ]) + + dnl ********************************** + dnl *** Check for getprotobyname *** + dnl ********************************** + AC_MSG_CHECKING(for getprotobyname) + AC_TRY_LINK([ + #include + #include + ], [ + getprotobyname(NULL); + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_GETPROTOBYNAME, 1, [Have getprotobyname]) + ], [ + AC_MSG_RESULT(no) + ]) + + dnl ********************************** + dnl *** Check for getnameinfo *** + dnl ********************************** + AC_MSG_CHECKING(for getnameinfo) + AC_TRY_LINK([ + #include + #include + ], [ + getnameinfo (NULL, 0, NULL, 0, NULL, 0, 0); + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_GETNAMEINFO, 1, [Have getnameinfo]) + ], [ + AC_MSG_RESULT(no) + ]) + + + dnl ********************************** + dnl *** Check for inet_ntop *** + dnl ********************************** + AC_MSG_CHECKING(for inet_ntop) + AC_TRY_LINK([ + #include + #include + ], [ + inet_ntop (0, NULL, NULL, 0); + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_INET_NTOP, 1, [Have inet_ntop]) + ], [ + AC_MSG_RESULT(no) + ]) + + dnl ***************************** + dnl *** Checks for libnsl *** + dnl ***************************** + AC_CHECK_FUNC(gethostbyaddr, , AC_CHECK_LIB(nsl, gethostbyaddr, LIBS="$LIBS -lnsl")) + + AC_CHECK_FUNCS(inet_pton inet_aton) + + dnl ***************************** + dnl *** Checks for libxnet *** + dnl ***************************** + case "${host}" in + *solaris* ) + AC_MSG_CHECKING(for Solaris XPG4 support) + if test -f /usr/lib/libxnet.so; then + CPPFLAGS="$CPPFLAGS -D_XOPEN_SOURCE=500" + CPPFLAGS="$CPPFLAGS -D__EXTENSIONS__" + CPPFLAGS="$CPPFLAGS -D_XOPEN_SOURCE_EXTENDED=1" + LIBS="$LIBS -lxnet" + AC_MSG_RESULT(yes) + else + AC_MSG_RESULT(no) + fi + + if test "$GCC" = "yes"; then + CFLAGS="$CFLAGS -Wno-char-subscripts" + fi + ;; + esac + + dnl ***************************** + dnl *** Checks for libpthread *** + dnl ***************************** +# on FreeBSD -STABLE, the pthreads functions all reside in libc_r +# and libpthread does not exist +# + case "${host}" in + *-*-*haiku*) + dnl Haiku has pthread in libroot (libc equiv) + AC_CHECK_LIB(pthread, main, LIBS="$LIBS") + ;; + *-*-*freebsd*) + AC_CHECK_LIB(pthread, main, LIBS="$LIBS -pthread") + ;; + *-*-*openbsd*) + AC_CHECK_LIB(pthread, main, LIBS="$LIBS -pthread") + ;; + *) + AC_CHECK_LIB(pthread, main, LIBS="$LIBS -lpthread") + ;; + esac + AC_CHECK_HEADERS(pthread.h) + AC_CHECK_HEADERS(pthread_np.h) + AC_CHECK_FUNCS(pthread_mutex_timedlock) + AC_CHECK_FUNCS(pthread_getattr_np pthread_attr_get_np pthread_setname_np pthread_cond_timedwait_relative_np) + AC_CHECK_FUNCS(pthread_kill) + AC_MSG_CHECKING(for PTHREAD_MUTEX_RECURSIVE) + AC_TRY_COMPILE([ #include ], [ + pthread_mutexattr_t attr; + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + ], [ + AC_MSG_RESULT(ok) + ], [ + AC_MSG_RESULT(no) + AC_ERROR(Posix system lacks support for recursive mutexes) + ]) + AC_CHECK_FUNCS(pthread_attr_setstacksize) + AC_CHECK_FUNCS(pthread_attr_getstack pthread_attr_getstacksize) + AC_CHECK_FUNCS(pthread_get_stacksize_np pthread_get_stackaddr_np) + + dnl ********************************** + dnl *** Check for mincore *** + dnl ********************************** + AC_MSG_CHECKING(for mincore) + AC_TRY_LINK([ + #include + #include + #include + ], [ + mincore(NULL, 0, NULL); + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_MINCORE, 1, [Have mincore]) + ], [ + AC_MSG_RESULT(no) + ]) + + dnl *********************************** + dnl *** Checks for working __thread *** + dnl *********************************** + AC_MSG_CHECKING(for working __thread) + if test "x$with_tls" != "x__thread"; then + AC_MSG_RESULT(disabled) + elif test "x$cross_compiling" = "xyes"; then + AC_MSG_RESULT(cross compiling, assuming yes) + else + AC_TRY_RUN([ + #if defined(__APPLE__) && defined(__clang__) + #error "__thread does not currently work with clang on Mac OS X" + #endif + + #include + __thread int i; + static int res1, res2; + + void thread_main (void *arg) + { + i = arg; + sleep (1); + if (arg == 1) + res1 = (i == arg); + else + res2 = (i == arg); + } + + int main () { + pthread_t t1, t2; + + i = 5; + + pthread_create (&t1, NULL, thread_main, 1); + pthread_create (&t2, NULL, thread_main, 2); + + pthread_join (t1, NULL); + pthread_join (t2, NULL); + + return !(res1 + res2 == 2); + } + ], [ + AC_MSG_RESULT(yes) + ], [ + AC_MSG_RESULT(no) + with_tls=pthread + ]) + fi + + dnl ************************************** + dnl *** Checks for working sigaltstack *** + dnl ************************************** + AC_MSG_CHECKING(for working sigaltstack) + if test "x$with_sigaltstack" != "xyes"; then + AC_MSG_RESULT(disabled) + elif test "x$cross_compiling" = "xyes"; then + AC_MSG_RESULT(cross compiling, assuming yes) + else + AC_TRY_RUN([ + #include + #include + #include + #include + #include + #include + #if defined(__FreeBSD__) || defined(__NetBSD__) + #define SA_STACK SA_ONSTACK + #endif + static void + sigsegv_signal_handler (int _dummy, siginfo_t *info, void *context) + { + exit (0); + } + + volatile char*__ptr = NULL; + static void * + loop (void *ignored) + { + *__ptr = 0; + return NULL; + } + + static void + child () + { + struct sigaction sa; + #ifdef __APPLE__ + stack_t sas; + #else + struct sigaltstack sas; + #endif + pthread_t id; + pthread_attr_t attr; + + sa.sa_sigaction = sigsegv_signal_handler; + sigemptyset (&sa.sa_mask); + sa.sa_flags = SA_SIGINFO | SA_ONSTACK; + if (sigaction (SIGSEGV, &sa, NULL) == -1) { + perror ("sigaction"); + return; + } + + /* x86 darwin deliver segfaults using SIGBUS */ + if (sigaction (SIGBUS, &sa, NULL) == -1) { + perror ("sigaction"); + return; + } + sas.ss_sp = malloc (SIGSTKSZ); + sas.ss_size = SIGSTKSZ; + sas.ss_flags = 0; + if (sigaltstack (&sas, NULL) == -1) { + perror ("sigaltstack"); + return; + } + + pthread_attr_init (&attr); + if (pthread_create(&id, &attr, loop, &attr) != 0) { + printf ("pthread_create\n"); + return; + } + + sleep (100); + } + + int + main () + { + pid_t son; + int status; + int i; + + son = fork (); + if (son == -1) { + return 1; + } + + if (son == 0) { + child (); + return 0; + } + + for (i = 0; i < 300; ++i) { + waitpid (son, &status, WNOHANG); + if (WIFEXITED (status) && WEXITSTATUS (status) == 0) + return 0; + usleep (10000); + } + + kill (son, SIGKILL); + return 1; + } + + ], [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_WORKING_SIGALTSTACK, 1, [Have a working sigaltstack]) + ], [ + with_sigaltstack=no + AC_MSG_RESULT(no) + ]) + fi + + dnl ******************************** + dnl *** Checks for semaphore lib *** + dnl ******************************** + # 'Real Time' functions on Solaris + # posix4 on Solaris 2.6 + # pthread (first!) on Linux + AC_SEARCH_LIBS(sem_init, pthread rt posix4) + + AC_SEARCH_LIBS(shm_open, pthread rt posix4) + AC_CHECK_FUNCS(shm_open) + + dnl ******************************** + dnl *** Checks for timezone stuff ** + dnl ******************************** + AC_CACHE_CHECK(for tm_gmtoff in struct tm, ac_cv_struct_tm_gmtoff, + AC_TRY_COMPILE([ + #include + ], [ + struct tm tm; + tm.tm_gmtoff = 1; + ], ac_cv_struct_tm_gmtoff=yes, ac_cv_struct_tm_gmtoff=no)) + if test $ac_cv_struct_tm_gmtoff = yes; then + AC_DEFINE(HAVE_TM_GMTOFF, 1, [Have tm_gmtoff]) + else + AC_CACHE_CHECK(for timezone variable, ac_cv_var_timezone, + AC_TRY_COMPILE([ + #include + ], [ + timezone = 1; + ], ac_cv_var_timezone=yes, ac_cv_var_timezone=no)) + if test $ac_cv_var_timezone = yes; then + AC_DEFINE(HAVE_TIMEZONE, 1, [Have timezone variable]) + else + AC_ERROR(unable to find a way to determine timezone) + fi + fi + + dnl ********************************* + dnl *** Checks for math functions *** + dnl ********************************* + AC_SEARCH_LIBS(sqrtf, m) + if test "x$has_broken_apple_cpp" != "xyes"; then + AC_CHECK_FUNCS(finite, , AC_MSG_CHECKING(for finite in math.h) + AC_TRY_LINK([#include ], + [ finite(0.0); ], + AC_DEFINE(HAVE_FINITE, 1, [Have finite in -lm]) AC_MSG_RESULT(yes), + AC_MSG_RESULT(no))) + fi + AC_CHECK_FUNCS(isfinite, , AC_MSG_CHECKING(for isfinite in math.h) + AC_TRY_LINK([#include ], + [ isfinite(0.0); ], + AC_DEFINE(HAVE_ISFINITE, 1, [Have isfinite]) AC_MSG_RESULT(yes), + AC_MSG_RESULT(no))) + + dnl **************************************************************** + dnl *** Checks for working poll() (macosx defines it but doesn't *** + dnl *** have it in the library (duh)) *** + dnl **************************************************************** + AC_CHECK_FUNCS(poll) + + dnl ************************* + dnl *** Check for signbit *** + dnl ************************* + AC_MSG_CHECKING(for signbit) + AC_TRY_LINK([#include ], [ + int s = signbit(1.0); + ], [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_SIGNBIT, 1, [Have signbit]) + ], [ + AC_MSG_RESULT(no) + ]) + + dnl ********************************** + dnl *** epoll *** + dnl ********************************** + AC_CHECK_HEADERS(sys/epoll.h) + haveepoll=no + AC_CHECK_FUNCS(epoll_ctl, [haveepoll=yes], ) + if test "x$haveepoll" = "xyes" -a "x$ac_cv_header_sys_epoll_h" = "xyes"; then + AC_DEFINE(HAVE_EPOLL, 1, [epoll supported]) + fi + + havekqueue=no + + AC_CHECK_HEADERS(sys/event.h) + AC_CHECK_FUNCS(kqueue, [havekqueue=yes], ) + + dnl ************************************** + dnl * Darwin has a race that prevents us from using reliably: + dnl * http://lists.apple.com/archives/darwin-dev/2011/Jun/msg00016.html + dnl * Since kqueue is mostly used for scaling large web servers, + dnl * and very few folks run Mono on large web servers on OSX, falling + dnl * back + dnl ************************************** + if test "x$havekqueue" = "xyes" -a "x$ac_cv_header_sys_event_h" = "xyes"; then + if test "x$host_darwin" = "xno"; then + AC_DEFINE(USE_KQUEUE_FOR_THREADPOOL, 1, [Use kqueue for the threadpool]) + fi + fi + + dnl ****************************** + dnl *** Checks for SIOCGIFCONF *** + dnl ****************************** + AC_CHECK_HEADERS(sys/ioctl.h) + AC_CHECK_HEADERS(net/if.h, [], [], + [ + #ifdef HAVE_SYS_TYPES_H + # include + #endif + #ifdef HAVE_SYS_SOCKET_H + # include + #endif + ]) + AC_MSG_CHECKING(for ifreq) + AC_TRY_COMPILE([ + #include + #include + #include + ], [ + struct ifconf ifc; + struct ifreq *ifr; + void *x; + ifc.ifc_len = 0; + ifc.ifc_buf = NULL; + x = (void *) &ifr->ifr_addr; + ],[ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_SIOCGIFCONF, 1, [Can get interface list]) + ], [ + AC_MSG_RESULT(no) + ]) + dnl ********************************** + dnl *** Checks for sin_len *** + dnl ********************************** + AC_MSG_CHECKING(for sockaddr_in.sin_len) + AC_TRY_COMPILE([ + #include + ], [ + struct sockaddr_in saddr; + saddr.sin_len = sizeof (saddr); + ],[ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_SOCKADDR_IN_SIN_LEN, 1, [sockaddr_in has sin_len]) + ], [ + AC_MSG_RESULT(no) + ]) + dnl ********************************** + dnl *** Checks for sin6_len *** + dnl ********************************** + AC_MSG_CHECKING(for sockaddr_in6.sin6_len) + AC_TRY_COMPILE([ + #include + ], [ + struct sockaddr_in6 saddr6; + saddr6.sin6_len = sizeof (saddr6); + ],[ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_SOCKADDR_IN6_SIN_LEN, 1, [sockaddr_in6 has sin6_len]) + ], [ + AC_MSG_RESULT(no) + ]) + dnl ********************************** + dnl *** Check for getifaddrs *** + dnl ********************************** + AC_MSG_CHECKING(for getifaddrs) + AC_TRY_LINK([ + #include + #include + #include + #include + ], [ + getifaddrs(NULL); + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_GETIFADDRS, 1, [Have getifaddrs]) + ], [ + AC_MSG_RESULT(no) + ]) + dnl ********************************** + dnl *** Check for if_nametoindex *** + dnl ********************************** + AC_MSG_CHECKING(for if_nametoindex) + AC_TRY_LINK([ + #include + #include + #include + #include + ], [ + if_nametoindex(NULL); + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_IF_NAMETOINDEX, 1, [Have if_nametoindex]) + ], [ + AC_MSG_RESULT(no) + ]) + + dnl ********************************** + dnl *** Checks for proclib *** + dnl ********************************** + AC_CHECK_HEADER(sys/errno.h, [AC_DEFINE(HAVE_SYS_ERRNO_H, 1, Define to 1 if you have the header file.)]) + dnl ********************************** + dnl *** Checks for MonoPosixHelper *** + dnl ********************************** + AC_CHECK_HEADERS(checklist.h) + AC_CHECK_HEADERS(pathconf.h) + AC_CHECK_HEADERS(fstab.h) + AC_CHECK_HEADERS(attr/xattr.h) + AC_CHECK_HEADERS(sys/extattr.h) + AC_CHECK_HEADERS(sys/sendfile.h) + AC_CHECK_HEADERS(sys/statvfs.h) + AC_CHECK_HEADERS(sys/statfs.h) + AC_CHECK_HEADERS(sys/vfstab.h) + AC_CHECK_HEADERS(sys/xattr.h) + AC_CHECK_HEADERS(sys/mman.h) + AC_CHECK_HEADERS(sys/param.h) + AC_CHECK_HEADERS(sys/mount.h, [], [], + [ + #ifdef HAVE_SYS_PARAM_H + # include + #endif + ]) + AC_CHECK_HEADERS(sys/mount.h) + AC_CHECK_FUNCS(confstr) + AC_CHECK_FUNCS(seekdir telldir) + AC_CHECK_FUNCS(getdomainname) + AC_CHECK_FUNCS(setdomainname) + AC_CHECK_FUNCS(endgrent getgrent fgetgrent setgrent) + AC_CHECK_FUNCS(setgroups) + AC_CHECK_FUNCS(endpwent getpwent fgetpwent setpwent) + AC_CHECK_FUNCS(getfsstat) + AC_CHECK_FUNCS(lutimes futimes) + AC_CHECK_FUNCS(mremap) + AC_CHECK_FUNCS(remap_file_pages) + AC_CHECK_FUNCS(posix_fadvise) + AC_CHECK_FUNCS(posix_fallocate) + AC_CHECK_FUNCS(posix_madvise) + AC_CHECK_FUNCS(vsnprintf) + AC_CHECK_FUNCS(sendfile) + AC_CHECK_FUNCS(gethostid sethostid) + AC_CHECK_FUNCS(sethostname) + AC_CHECK_FUNCS(statfs) + AC_CHECK_FUNCS(fstatfs) + AC_CHECK_FUNCS(statvfs) + AC_CHECK_FUNCS(fstatvfs) + AC_CHECK_FUNCS(stime) + AC_CHECK_FUNCS(strerror_r) + AC_CHECK_FUNCS(ttyname_r) + AC_CHECK_FUNCS(psignal) + AC_CHECK_FUNCS(getlogin_r) + AC_CHECK_FUNCS(lockf) + AC_CHECK_FUNCS(swab) + AC_CHECK_FUNCS(setusershell endusershell) + AC_CHECK_FUNCS(futimens utimensat) + AC_CHECK_FUNCS(fstatat mknodat readlinkat) + AC_CHECK_FUNCS(readv writev preadv pwritev) + AC_CHECK_FUNCS(setpgid) + AC_CHECK_FUNCS(system) + AC_CHECK_FUNCS(fork execv execve) + AC_CHECK_FUNCS(accept4) + AC_CHECK_FUNCS(localtime_r) + AC_CHECK_FUNCS(mkdtemp) + AC_CHECK_SIZEOF(size_t) + AC_CHECK_TYPES([blksize_t], [AC_DEFINE(HAVE_BLKSIZE_T)], , + [#include + #include + #include ]) + AC_CHECK_TYPES([blkcnt_t], [AC_DEFINE(HAVE_BLKCNT_T)], , + [#include + #include + #include ]) + AC_CHECK_TYPES([suseconds_t], [AC_DEFINE(HAVE_SUSECONDS_T)], , + [#include ]) + AC_CHECK_TYPES([struct cmsghdr], [AC_DEFINE(HAVE_STRUCT_CMSGHDR)], , + [#include ]) + AC_CHECK_TYPES([struct flock], [AC_DEFINE(HAVE_STRUCT_FLOCK)], , + [#include + #include ]) + AC_CHECK_TYPES([struct iovec], [AC_DEFINE(HAVE_STRUCT_IOVEC)], , + [#include ]) + AC_CHECK_TYPES([struct linger], [AC_DEFINE(HAVE_STRUCT_LINGER)], , + [#include ]) + AC_CHECK_TYPES([struct pollfd], [AC_DEFINE(HAVE_STRUCT_POLLFD)], , + [#include ]) + AC_CHECK_TYPES([struct sockaddr], [AC_DEFINE(HAVE_STRUCT_SOCKADDR)], , + [#include ]) + AC_CHECK_TYPES([struct sockaddr_storage], [AC_DEFINE(HAVE_STRUCT_SOCKADDR_STORAGE)], , + [#include ]) + AC_CHECK_TYPES([struct sockaddr_in], [AC_DEFINE(HAVE_STRUCT_SOCKADDR_IN)], , + [#include ]) + AC_CHECK_TYPES([struct sockaddr_in6], [AC_DEFINE(HAVE_STRUCT_SOCKADDR_IN6)], , + [#include ]) + AC_CHECK_TYPES([struct sockaddr_un], [AC_DEFINE(HAVE_STRUCT_SOCKADDR_UN)], , + [#include ]) + AC_CHECK_TYPES([struct stat], [AC_DEFINE(HAVE_STRUCT_STAT)], , + [#include + #include + #include ]) + AC_CHECK_TYPES([struct timespec], [AC_DEFINE(HAVE_STRUCT_TIMESPEC)], , + [#include ]) + AC_CHECK_TYPES([struct timeval], [AC_DEFINE(HAVE_STRUCT_TIMEVAL)], , + [#include + #include + #include ]) + AC_CHECK_TYPES([struct timezone], [AC_DEFINE(HAVE_STRUCT_TIMEZONE)], , + [#include ]) + AC_CHECK_TYPES([struct utimbuf], [AC_DEFINE(HAVE_STRUCT_UTIMBUF)], , + [#include + #include ]) + AC_CHECK_MEMBERS( + [struct dirent.d_off, struct dirent.d_reclen, struct dirent.d_type],,, + [#include + #include ]) + AC_CHECK_MEMBERS( + [struct passwd.pw_gecos],,, + [#include + #include ]) + AC_CHECK_MEMBERS( + [struct statfs.f_flags],,, + [#include + #include ]) + AC_CHECK_MEMBERS( + [struct stat.st_atim, struct stat.st_mtim, struct stat.st_ctim],,, + [#include + #include + #include ]) + + dnl Favour xattr through glibc, but use libattr if we have to + AC_CHECK_FUNC(lsetxattr, , + AC_CHECK_LIB(attr, lsetxattr, XATTR_LIB="-lattr",) + ) + AC_SUBST(XATTR_LIB) + + dnl kinfo_proc.kp_proc works on darwin but fails on other simil-bsds + AC_CHECK_MEMBERS( + [struct kinfo_proc.kp_proc],,, + [#include + #include + #include + #include + ]) + + dnl ********************************* + dnl *** Checks for Windows compilation *** + dnl ********************************* + AC_CHECK_HEADERS(sys/time.h) + AC_CHECK_HEADERS(sys/param.h) + AC_CHECK_HEADERS(dirent.h) + + dnl ****************************************** + dnl *** Checks for OSX and iOS compilation *** + dnl ****************************************** + AC_CHECK_HEADERS(CommonCrypto/CommonDigest.h) + + dnl ********************************* + dnl *** Check for Console 2.0 I/O *** + dnl ********************************* + AC_CHECK_HEADERS([curses.h]) + AC_CHECK_HEADERS([term.h], [], [], + [#if HAVE_CURSES_H + #include + #endif + ]) + AC_CHECK_HEADERS([termios.h]) + + dnl ********************************* + dnl *** Checks for random *** + dnl ********************************* + AC_CHECK_HEADERS(sys/random.h) + AC_CHECK_FUNCS(getrandom getentropy) +else + dnl ********************************* + dnl *** Checks for Windows compilation *** + dnl ********************************* + AC_CHECK_HEADERS(winternl.h) + + jdk_headers_found=no + AC_CHECK_LIB(ws2_32, main, LIBS="$LIBS -lws2_32", AC_ERROR(bad mingw install?)) + AC_CHECK_LIB(psapi, main, LIBS="$LIBS -lpsapi", AC_ERROR(bad mingw install?)) + AC_CHECK_LIB(ole32, main, LIBS="$LIBS -lole32", AC_ERROR(bad mingw install?)) + AC_CHECK_LIB(winmm, main, LIBS="$LIBS -lwinmm", AC_ERROR(bad mingw install?)) + AC_CHECK_LIB(oleaut32, main, LIBS="$LIBS -loleaut32", AC_ERROR(bad mingw install?)) + AC_CHECK_LIB(advapi32, main, LIBS="$LIBS -ladvapi32", AC_ERROR(bad mingw install?)) + AC_CHECK_LIB(version, main, LIBS="$LIBS -lversion", AC_ERROR(bad mingw install?)) + + dnl ********************************* + dnl *** Check for struct ip_mreqn *** + dnl ********************************* + AC_MSG_CHECKING(for struct ip_mreqn) + AC_TRY_COMPILE([#include ], [ + struct ip_mreqn mreq; + mreq.imr_address.s_addr = 0; + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_STRUCT_IP_MREQN) + ], [ + # We'll just have to try and use struct ip_mreq + AC_MSG_RESULT(no) + AC_MSG_CHECKING(for struct ip_mreq) + AC_TRY_COMPILE([#include ], [ + struct ip_mreq mreq; + mreq.imr_interface.s_addr = 0; + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_STRUCT_IP_MREQ) + ], [ + # No multicast support + AC_MSG_RESULT(no) + ]) + ]) + + dnl ********************************** + dnl *** Check for getaddrinfo *** + dnl ********************************** + AC_MSG_CHECKING(for getaddrinfo) + AC_TRY_LINK([ + #include + #include + #include + ], [ + getaddrinfo(NULL,NULL,NULL,NULL); + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_GETADDRINFO, 1, [Have getaddrinfo]) + ], [ + AC_MSG_RESULT(no) + ]) + + dnl ********************************** + dnl *** Check for gethostbyname *** + dnl ********************************** + AC_MSG_CHECKING(for gethostbyname) + AC_TRY_LINK([ + #include + #include + #include + ], [ + gethostbyname(NULL); + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_GETHOSTBYNAME, 1, [Have gethostbyname]) + ], [ + AC_MSG_RESULT(no) + ]) + + dnl ********************************** + dnl *** Check for getprotobyname *** + dnl ********************************** + AC_MSG_CHECKING(for getprotobyname) + AC_TRY_LINK([ + #include + #include + #include + ], [ + getprotobyname(NULL); + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_GETPROTOBYNAME, 1, [Have getprotobyname]) + ], [ + AC_MSG_RESULT(no) + ]) + + dnl ********************************** + dnl *** Check for getnameinfo *** + dnl ********************************** + AC_MSG_CHECKING(for getnameinfo) + AC_TRY_LINK([ + #include + #include + #include + ], [ + getnameinfo (NULL, 0, NULL, 0, NULL, 0, 0); + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_GETNAMEINFO, 1, [Have getnameinfo]) + ], [ + AC_MSG_RESULT(no) + ]) + + dnl ********************************** + dnl *** Check for inet_ntop *** + dnl ********************************** + AC_MSG_CHECKING(for inet_ntop) + AC_TRY_LINK([ + #include + #include + #include + ], [ + inet_ntop (0, NULL, NULL, 0); + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_INET_NTOP, 1, [Have inet_ntop]) + ], [ + AC_MSG_RESULT(no) + ]) + + dnl ********************************** + dnl *** Check for inet_pton *** + dnl ********************************** + AC_MSG_CHECKING(for inet_pton) + AC_TRY_LINK([ + #include + #include + #include + ], [ + #ifndef inet_pton + (void) inet_pton; + #endif + inet_pton (0, NULL, NULL); + ], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_INET_PTON, 1, [Have inet_pton]) + ], [ + AC_MSG_RESULT(no) + ]) + + AC_CHECK_DECLS(InterlockedExchange64, [], [], [[#include ]]) + AC_CHECK_DECLS(InterlockedCompareExchange64, [], [], [[#include ]]) + AC_CHECK_DECLS(InterlockedDecrement64, [], [], [[#include ]]) + AC_CHECK_DECLS(InterlockedIncrement64, [], [], [[#include ]]) + AC_CHECK_DECLS(InterlockedAdd, [], [], [[#include ]]) + AC_CHECK_DECLS(InterlockedAdd64, [], [], [[#include ]]) + AC_CHECK_DECLS(__readfsdword, [], [], [[#include ]]) +fi + +dnl socklen_t check +AC_MSG_CHECKING(for socklen_t) +AC_TRY_COMPILE([ +#include +#include +],[ + socklen_t foo; +],[ +ac_cv_c_socklen_t=yes + AC_DEFINE(HAVE_SOCKLEN_T, 1, [Have socklen_t]) + AC_MSG_RESULT(yes) +],[ + AC_MSG_RESULT(no) +]) + +AC_MSG_CHECKING(for array element initializer support) +AC_TRY_COMPILE([#include ], [ + const int array[] = {[1] = 2,}; +], [ + # Yes, we have it... + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_ARRAY_ELEM_INIT,1,[Supports C99 array initialization]) +], [ + # We'll have to use signals + AC_MSG_RESULT(no) +]) + +AC_CHECK_FUNCS(trunc, , AC_MSG_CHECKING(for trunc in math.h) + # Simply calling trunc (0.0) is no good since gcc will optimize the call away + AC_TRY_LINK([#include ], + [ static void *p = &trunc; ], + [ + AC_DEFINE(HAVE_TRUNC) + AC_MSG_RESULT(yes) + ac_cv_trunc=yes + ], + AC_MSG_RESULT(no))) + +if test "x$ac_cv_truncl" != "xyes"; then + AC_CHECK_LIB(sunmath, aintl, [ AC_DEFINE(HAVE_AINTL, 1, [Has the 'aintl' function]) LIBS="$LIBS -lsunmath"]) +fi + +AC_CHECK_FUNCS(execvp) + +dnl **************************** +dnl *** Look for /dev/random *** +dnl **************************** + +AC_MSG_CHECKING([if usage of random device is requested]) +AC_ARG_ENABLE(dev-random, +[ --disable-dev-random disable the use of the random device (enabled by default)], +try_dev_random=$enableval, try_dev_random=yes) + +AC_MSG_RESULT($try_dev_random) + +case "{$build}" in + *-openbsd*) + NAME_DEV_RANDOM="/dev/srandom" + ;; + +dnl Win32 does not have /dev/random, they have their own method... + + *-mingw*|*-*-cygwin*) + ac_cv_have_dev_random=no + ;; + +dnl Everywhere else, it's /dev/random + + *) + NAME_DEV_RANDOM="/dev/random" + ;; +esac + +AC_DEFINE_UNQUOTED(NAME_DEV_RANDOM, "$NAME_DEV_RANDOM", [Name of /dev/random]) + +dnl Now check if the device actually exists + +if test "x$try_dev_random" = "xyes"; then + AC_CACHE_CHECK(for random device, ac_cv_have_dev_random, + [if test -r "$NAME_DEV_RANDOM" ; then + ac_cv_have_dev_random=yes; else ac_cv_have_dev_random=no; fi]) + if test "x$ac_cv_have_dev_random" = "xyes"; then + AC_DEFINE(HAVE_CRYPT_RNG, 1, [Have /dev/random]) + fi +else + AC_MSG_CHECKING(for random device) + ac_cv_have_dev_random=no + AC_MSG_RESULT(has been disabled) +fi + +if test "x$host_win32" = "xyes"; then + AC_DEFINE(HAVE_CRYPT_RNG) +fi + +if test "x$ac_cv_have_dev_random" = "xno" \ + && test "x$host_win32" = "xno"; then + AC_MSG_WARN([[ +*** +*** A system-provided entropy source was not found on this system. +*** Because of this, the System.Security.Cryptography random number generator +*** will throw a NotImplemented exception. +*** +*** If you are seeing this message, and you know your system DOES have an +*** entropy collection in place, please contact and +*** provide information about the system and how to access the random device. +*** +*** Otherwise you can install either egd or prngd and set the environment +*** variable MONO_EGD_SOCKET to point to the daemon's socket to use that. +***]]) +fi + +AC_ARG_ENABLE(bcl-opt, [ --disable-bcl-opt BCL is compiled with no optimizations (allows accurate BCL debugging)], test_bcl_opt=$enableval, test_bcl_opt=yes) + +AC_MSG_CHECKING([if big-arrays are to be enabled]) +AC_ARG_ENABLE(big-arrays, [ --enable-big-arrays Enable the allocation and indexing of arrays greater than Int32.MaxValue], enable_big_arrays=$enableval, enable_big_arrays=no) +if test "x$enable_big_arrays" = "xyes" ; then + if test "x$ac_cv_sizeof_void_p" = "x8"; then + AC_DEFINE(MONO_BIG_ARRAYS,1,[Enable the allocation and indexing of arrays greater than Int32.MaxValue]) + else + AC_MSG_ERROR([The allocation and indexing of arrays greater than Int32.MaxValue is not supported on this platform.]) + fi +fi +AC_MSG_RESULT($enable_big_arrays) + +dnl ************** +dnl *** DTRACE *** +dnl ************** + +AC_ARG_ENABLE(dtrace,[ --enable-dtrace Enable DTrace probes], enable_dtrace=$enableval, enable_dtrace=$has_dtrace) + +if test "x$enable_dtrace" = "xyes"; then + if test "x$has_dtrace" = "xno"; then + AC_MSG_ERROR([DTrace probes are not supported on this platform.]) + fi + AC_PATH_PROG(DTRACE, [dtrace], [no], [$PATH:/usr/sbin]) + if test "x$DTRACE" = "xno"; then + AC_MSG_RESULT([dtrace utility not found, dtrace support disabled.]) + enable_dtrace=no + elif ! $DTRACE -h -s $srcdir/data/mono.d > /dev/null 2>&1; then + AC_MSG_RESULT([dtrace doesn't support -h option, dtrace support disabled.]) + enable_dtrace=no + fi +fi + +dtrace_g=no +if test "x$enable_dtrace" = "xyes"; then + AC_DEFINE(ENABLE_DTRACE, 1, [Enable DTrace probes]) + DTRACEFLAGS= + if test "x$ac_cv_sizeof_void_p" = "x8"; then + case "$host" in + powerpc-*-darwin*) + DTRACEFLAGS="-arch ppc64" + ;; + i*86-*-darwin*) + DTRACEFLAGS="-arch x86_64" + ;; + *) + DTRACEFLAGS=-64 + ;; + esac + else + case "$host" in + powerpc-*-darwin*) + DTRACEFLAGS="-arch ppc" + ;; + i*86-*-darwin*) + DTRACEFLAGS="-arch i386" + ;; + *) + DTRACEFLAGS=-32 + ;; + esac + fi + AC_SUBST(DTRACEFLAGS) + case "$host" in + *-*-solaris*) + dtrace_g=yes + ;; + esac + AC_CHECK_HEADERS([sys/sdt.h]) +fi +AM_CONDITIONAL(ENABLE_DTRACE, [test x$enable_dtrace = xyes]) +AM_CONDITIONAL(DTRACE_G_REQUIRED, [test x$dtrace_g = xyes]) + +dnl ************************** +dnl *** AOT cross offsets *** +dnl ************************** + +AC_ARG_WITH(cross-offsets, [ --with-cross-offsets= Explicit AOT cross offsets file], + AC_DEFINE_UNQUOTED(MONO_OFFSETS_FILE, "$withval", [AOT cross offsets file])) + +dnl ************** +dnl *** LLVM *** +dnl ************** + +AC_ARG_ENABLE(llvm,[ --enable-llvm Enable the LLVM back-end], enable_llvm=$enableval, enable_llvm=default) +AC_ARG_ENABLE(loadedllvm,[ --enable-loadedllvm Load the LLVM back-end dynamically], enable_llvm=$enableval && enable_loadedllvm=$enableval, enable_loadedllvm=no) +AC_ARG_ENABLE(llvm-version-check,[ --enable-llvm-version-check Check that the LLVM matches the version expected by mono], enable_llvm_version_check=$enableval, enable_llvm_version_check=no) +AC_ARG_ENABLE(llvm-runtime,[ --enable-llvm-runtime Enable runtime support for llvmonly code], enable_llvm_runtime=$enableval, enable_llvm_runtime=no) + +AC_ARG_WITH(llvm, [ --with-llvm= Enable the LLVM back-end], enable_llvm=yes,) + +if test "x$enable_llvm" = "xdefault"; then + enable_llvm=$enable_llvm_default +fi + +if test "x$enable_llvm" = "xyes"; then + if test "x$with_llvm" != "x"; then + LLVM_CONFIG=$with_llvm/bin/llvm-config + if test x$host_win32 = xyes; then + LLVM_CONFIG=$LLVM_CONFIG.exe + fi + if test ! -x $LLVM_CONFIG; then + AC_MSG_ERROR([LLVM executable $LLVM_CONFIG not found.]) + fi + else + AC_PATH_PROG(LLVM_CONFIG, llvm-config, no) + if test "x$LLVM_CONFIG" = "xno"; then + AC_MSG_ERROR([llvm-config not found.]) + fi + fi + + llvm_codegen="x86codegen" + case "$target" in + arm*) + llvm_codegen="armcodegen" + ;; + esac + + if test "x$host_win32" = "xno"; then + + # Should be something like '2.6' or '2.7svn' + llvm_version=`$LLVM_CONFIG --version` + llvm_api_version=`$LLVM_CONFIG --mono-api-version 2>/dev/null` + AC_MSG_CHECKING(LLVM version) + AC_MSG_RESULT($llvm_version $llvm_api_version) + if echo $llvm_version | grep -q 'mono'; then + if test "x$enable_llvm_version_check" = "xyes"; then + if test "$llvm_version" != "$expected_llvm_version"; then + AC_MSG_ERROR([Expected llvm version $expected_llvm_version, but llvm-config --version returned $llvm_version"]) + fi + fi + else + AC_MSG_ERROR([Compiling with stock LLVM is not supported, please use the Mono LLVM repo at https://github.com/mono/llvm, with the GIT branch which matches this version of mono, i.e. 'mono-2-10' for Mono 2.10.]) + fi + + # The output of --cflags seems to include optimizations flags too + if test $llvm_api_version -gt 100; then + # The --cflags argument includes all kinds of warnings -pendantic etc. + LLVM_CFLAGS="-I$with_llvm/include -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS" + LLVM_CXXFLAGS="-I$with_llvm/include -std=c++11 -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS" + else + LLVM_CFLAGS=`$LLVM_CONFIG --cflags | sed -e 's/-O2//g' | sed -e 's/-O0//g' | sed -e 's/-fomit-frame-pointer//g' | sed -e 's/-fPIC//g'` + LLVM_CXXFLAGS=`$LLVM_CONFIG --cxxflags | sed -e 's/-O2//g' | sed -e 's/-O0//g' | sed -e 's/-fomit-frame-pointer//g' | sed -e 's/-fPIC//g'` + fi + # LLVM is compiled with -fno-rtti, so we need this too, since our classes inherit + # from LLVM classes. + LLVM_CXXFLAGS="$LLVM_CXXFLAGS -fno-rtti -fexceptions" + LLVM_LDFLAGS=`$LLVM_CONFIG --ldflags` + # This might include empty lines + LLVM_SYSTEM_LIBS=`$LLVM_CONFIG --system-libs 2>/dev/null | grep -- -` + llvm_jit_supported=yes + llvm_jit_libs="jit mcjit $llvm_codegen" + if test "x$host" != "x$target"; then + # No need for jit libs + llvm_jit_supported=no + llvm_jit_libs="" + elif test $llvm_api_version -gt 100; then + llvm_jit_libs="orcjit $llvm_codegen" + fi + LLVM_LIBS=`$LLVM_CONFIG --libs analysis core bitwriter $llvm_jit_libs` + if test "x$LLVM_LIBS" = "x"; then + echo "$LLVM_CONFIG --libs failed." + exit 1 + fi + LLVM_LIBS="$LLVM_LIBS $LLVM_LDFLAGS $LLVM_SYSTEM_LIBS" + # The c++ standard library used by llvm doesn't show up in $LLVM_SYSTEM_LIBS so add it manually + if echo $LLVM_CXXFLAGS | grep -q -- '-stdlib=libc++'; then + LLVM_LIBS="$LLVM_LIBS -lc++" + else + LLVM_LIBS="$LLVM_LIBS -lstdc++" + fi + + expected_llvm_version="3.4svn-mono-mono/e656cac" + + else + LLVM_CFLAGS="-I$with_llvm/include -DNDEBUG -D__NO_CTYPE_INLINE -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS" + LLVM_CXXFLAGS="$LLVM_CFLAGS -std=gnu++11 -fvisibility-inlines-hidden -fno-rtti -Woverloaded-virtual -Wcast-qual" + LLVM_LDFLAGS="-L$with_llvm/lib" + LLVM_SYSTEM_LIBS="-lshell32 -lpsapi -limagehlp -ldbghelp -lm" + LLVM_LIBS="-lLLVMLTO -lLLVMObjCARCOpts -lLLVMLinker -lLLVMipo -lLLVMVectorize -lLLVMBitWriter \ + -lLLVMARMDisassembler -lLLVMARMCodeGen -lLLVMARMAsmParser -lLLVMARMDesc -lLLVMARMInfo \ + -lLLVMARMAsmPrinter -lLLVMTableGen -lLLVMDebugInfo -lLLVMOption -lLLVMX86Disassembler \ + -lLLVMX86AsmParser -lLLVMX86CodeGen -lLLVMSelectionDAG -lLLVMAsmPrinter -lLLVMX86Desc \ + -lLLVMMCDisassembler -lLLVMX86Info -lLLVMX86AsmPrinter -lLLVMX86Utils -lLLVMJIT \ + -lLLVMAnalysis -lLLVMTarget \ + -lLLVMIRReader -lLLVMAsmParser -lLLVMLineEditor -lLLVMMCAnalysis -lLLVMInstrumentation \ + -lLLVMInterpreter -lLLVMCodeGen -lLLVMScalarOpts -lLLVMInstCombine -lLLVMTransformUtils \ + -lLLVMipa -lLLVMAnalysis -lLLVMProfileData -lLLVMMCJIT -lLLVMTarget -lLLVMRuntimeDyld \ + -lLLVMObject -lLLVMMCParser -lLLVMBitReader -lLLVMExecutionEngine -lLLVMMC -lLLVMCore \ + -lLLVMSupport -lstdc++" + LLVM_LIBS="$LLVM_LIBS $LLVM_SYSTEM_LIBS" + + llvm_config_path=$with_llvm/include/llvm/Config/llvm-config.h + llvm_api_version=`awk '/MONO_API_VERSION/ { print $3 }' $llvm_config_path` + fi + + if test "x$llvm_api_version" = "x"; then + LLVM_CFLAGS="$LLVM_CFLAGS -DLLVM_API_VERSION=0" + LLVM_CXXFLAGS="$LLVM_CXXFLAGS -DLLVM_API_VERSION=0" + else + LLVM_CFLAGS="$LLVM_CFLAGS -DLLVM_API_VERSION=$llvm_api_version" + LLVM_CXXFLAGS="$LLVM_CXXFLAGS -DLLVM_API_VERSION=$llvm_api_version" + fi + + AC_DEFINE_UNQUOTED(LLVM_VERSION, "$llvm_version", [Full version of LLVM libraries]) + + AC_SUBST(LLVM_CFLAGS) + AC_SUBST(LLVM_CXXFLAGS) + AC_SUBST(LLVM_LIBS) + AC_SUBST(LLVM_LDFLAGS) + AC_DEFINE(ENABLE_LLVM, 1, [Enable the LLVM back end]) +fi + +AM_CONDITIONAL(ENABLE_LLVM, [test x$enable_llvm = xyes]) +if test "x$enable_loadedllvm" = "xyes"; then + AC_DEFINE(MONO_LLVM_LOADED, 1, [The LLVM back end is dynamically loaded]) +fi +AM_CONDITIONAL(LOADED_LLVM, [test x$enable_loadedllvm = xyes]) + +if test "x$enable_llvm" = "xyes"; then + enable_llvm_runtime=yes +fi +if test "x$enable_llvm_runtime" = "xyes"; then + AC_DEFINE(ENABLE_LLVM_RUNTIME, 1, [Runtime support code for llvm enabled]) +fi +AM_CONDITIONAL(ENABLE_LLVM_RUNTIME, [test x$enable_llvm_runtime = xyes]) + +TARGET="unknown" +ACCESS_UNALIGNED="yes" + +LIBC="libc.so.6" +INTL="libc.so.6" +SQLITE="libsqlite.so.0" +SQLITE3="libsqlite3.so.0" +X11="libX11.so" +GDKX11="libgdk-x11-2.0.so.0" +GTKX11="libgtk-x11-2.0.so.0" +XINERAMA="libXinerama.so.1" + +sizeof_register="SIZEOF_VOID_P" + +jit_wanted=true +boehm_supported=true +BTLS_SUPPORTED=no +BTLS_PLATFORM= + +if test "x$enable_wasm" = "xyes"; then +TARGET=WASM +HOST=WASM +arch_target=wasm +AC_DEFINE(TARGET_WASM, 1, [Target wasm]) +AC_DEFINE(HOST_WASM, 1, [Host wasm]) +BTLS_SUPPORTED=no +with_tls=pthread +target_mach=no + +else + +case "$host" in + mips*) + TARGET=MIPS; + arch_target=mips; + with_tls=pthread; + ACCESS_UNALIGNED="no" + + AC_MSG_CHECKING(for mips n32) + AC_TRY_COMPILE([],[ + #if _MIPS_SIM != _ABIN32 + #error Not mips n32 + #endif + return 0; + ],[ + AC_MSG_RESULT(yes) + sizeof_register=8 + ],[ + AC_MSG_RESULT(no) + ]) + ;; + i*86-*-*) + TARGET=X86; + arch_target=x86; + case $host_os in + solaris*) + LIBC="libc.so" + INTL="libintl.so" + if test "x$ac_cv_sizeof_void_p" = "x8"; then + TARGET=AMD64 + arch_target=amd64 + fi + + # On solaris 10 x86, gcc prints a warning saying 'visibility attribute not supported on this configuration; ignored', but linking fails. A test case: + # int astruct __attribute__ ((visibility ("hidden"))); + # void foo () + # { + # void *p = &astruct; + # } + # gcc -fPIC --shared -o libfoo.so foo.c + # yields: + # foo.c:6: warning: visibility attribute not supported in this configuration; ignored + # ld: fatal: relocation error: R_386_GOTOFF: file /var/tmp//ccxYR96k.o: symbol astruct: relocation must bind locally + have_visibility_hidden=no + ;; + mingw*|cygwin*) + have_visibility_hidden=no + ;; + haiku*) + LIBC=libroot.so + ;; + linux*) + AOT_SUPPORTED="yes" + BTLS_SUPPORTED=yes + BTLS_PLATFORM=i386 + AC_CHECK_HEADER(stdalign.h,[],[BTLS_SUPPORTED=no]) + ;; + darwin*) + AOT_SUPPORTED="yes" + BTLS_SUPPORTED=yes + BTLS_PLATFORM=i386 + ;; + openbsd*|freebsd*|kfreebsd-gnu*) + ;; + esac + ;; + x86_64-*-* | amd64-*-*) + TARGET=AMD64; + arch_target=amd64; + if test "x$ac_cv_sizeof_void_p" = "x4"; then + AC_DEFINE(__mono_ilp32__, 1, [64 bit mode with 4 byte longs and pointers]) + sizeof_register=8 + fi + case $host_os in + linux*) + AOT_SUPPORTED="yes" + BTLS_SUPPORTED=yes + BTLS_PLATFORM=x86_64 + AC_CHECK_HEADER(stdalign.h,[],[BTLS_SUPPORTED=no]) + ;; + darwin*) + AOT_SUPPORTED="yes" + BTLS_SUPPORTED=yes + BTLS_PLATFORM=x86_64 + boehm_supported=false + ;; + openbsd*|freebsd*|kfreebsd-gnu*) + ;; + mingw*) + ;; + esac + ;; + sparc*-*-*) + if test "x$ac_cv_sizeof_void_p" = "x8"; then + TARGET=SPARC64 + else + TARGET=SPARC + fi + arch_target=sparc; + ACCESS_UNALIGNED="no" + case $host_os in + linux*) ;; + *) + LIBC="libc.so" + INTL="libintl.so" + esac + if test x"$GCC" = xyes; then + # We don't support v8 cpus + CFLAGS="$CFLAGS -Wno-cast-align -mcpu=v9" + fi + if test x"$AR" = xfalse; then + AC_MSG_ERROR([The required utility 'ar' is not found in your PATH. Usually it can be found in /usr/ccs/bin.]) + fi + ;; + *-mingw*|*-*-cygwin*) + # When this is enabled, it leads to very strange crashes at runtime (gcc-3.4.4) + have_visibility_hidden=no + INTL="intl" + ;; + macppc-*-openbsd* | powerpc*-*-linux* | powerpc-*-openbsd* | \ + powerpc-*-sysv* | powerpc-*-darwin* | powerpc-*-netbsd* | powerpc-*-freebsd* ) + if test "x$ac_cv_sizeof_void_p" = "x8"; then + TARGET=POWERPC64; + CPPFLAGS="$CPPFLAGS -D__mono_ppc__ -D__mono_ppc64__" + CFLAGS="$CFLAGS -mminimal-toc" + else + TARGET=POWERPC; + CPPFLAGS="$CPPFLAGS -D__mono_ppc__" + fi + arch_target=ppc; + case $host_os in + linux*|darwin*) + ;; + esac + ;; + armv7k-*-darwin*) + TARGET=ARM; + TARGET_SYS=WATCHOS + arch_target=arm; + arm_fpu=VFP_HARD + ACCESS_UNALIGNED="no" + CPPFLAGS="$CPPFLAGS -D__ARM_EABI__" + ;; + + arm*-darwin*) + TARGET=ARM; + arch_target=arm; + ACCESS_UNALIGNED="no" + CPPFLAGS="$CPPFLAGS -D__ARM_EABI__" + ;; + arm*-linux*) + TARGET=ARM; + arch_target=arm; + ACCESS_UNALIGNED="no" + AOT_SUPPORTED="yes" + CPPFLAGS="$CPPFLAGS -D__ARM_EABI__" + BTLS_SUPPORTED=yes + BTLS_PLATFORM=arm + AC_CHECK_HEADER(stdalign.h,[],[BTLS_SUPPORTED=no]) + case "$target" in + arm*-linux*-gnueabi) + BTLS_PLATFORM=armsoft + ;; + esac + ;; + arm*-netbsd*-eabi*) + TARGET=ARM; + arch_target=arm; + ACCESS_UNALIGNED="no" + CPPFLAGS="$CPPFLAGS -D__ARM_EABI__" + ;; + aarch64-*) + # https://lkml.org/lkml/2012/7/15/133 + 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]) + ;; + s390x-*-linux*) + TARGET=S390X; + arch_target=s390x; + ACCESS_UNALIGNED="yes" + CFLAGS="$CFLAGS -mbackchain -D__USE_STRING_INLINES" + ;; +esac + +HOST=$TARGET + +if test "x$host" != "x$target"; then + AC_DEFINE(MONO_CROSS_COMPILE,1,[The runtime is compiled for cross-compiling mode]) + enable_mcs_build=no + enable_support_build=no + BTLS_SUPPORTED=no + case "$target" in + arm*-darwin*) + TARGET=ARM; + arch_target=arm; + ACCESS_UNALIGNED="no" + CPPFLAGS="$CPPFLAGS -D__ARM_EABI__" + # Can't use tls, since it depends on the runtime detection of tls offsets + # in mono-compiler.h + with_tls=pthread + case "$target" in + armv7k-*) + arm_fpu=VFP_HARD + AC_DEFINE(TARGET_WATCHOS, 1, [...]) + ;; + esac + ;; + powerpc64-ps3-linux-gnu) + TARGET=POWERPC64 + arch_target=powerpc64 + AC_DEFINE(TARGET_PS3, 1, [...]) + # It would be better to just use TARGET_POWERPC64, but lots of code already + # uses this define + AC_DEFINE(__mono_ppc64__, 1, [...]) + AC_DEFINE(__mono_ilp32__, 1, [64 bit mode with 4 byte longs and pointers]) + sizeof_register=8 + target_byte_order=G_BIG_ENDIAN + ;; + powerpc64-xbox360-linux-gnu) + TARGET=POWERPC64 + arch_target=powerpc64 + AC_DEFINE(TARGET_XBOX360, 1, [...]) + # It would be better to just use TARGET_POWERPC64, but lots of code already + # uses this define + sizeof_register=8 + target_byte_order=G_BIG_ENDIAN + ;; + arm*-linux-*) + TARGET=ARM; + arch_target=arm; + AC_DEFINE(TARGET_ARM, 1, [...]) + ACCESS_UNALIGNED="no" + CPPFLAGS="$CPPFLAGS -D__ARM_EABI__" + # Can't use tls, since it depends on the runtime detection of tls offsets + # in mono-compiler.h + with_tls=pthread + target_mach=no + case "$target" in + armv7l-unknown-linux-gnueabi*) + # TEGRA + CPPFLAGS="$CPPFLAGS" + ;; + armv5-*-linux-androideabi*) + AC_DEFINE(TARGET_ANDROID, 1, [...]) + CPPFLAGS="$CPPFLAGS" + ;; + *-linux-androideabi*) + AC_DEFINE(TARGET_ANDROID, 1, [...]) + CPPFLAGS="$CPPFLAGS" + ;; + esac + ;; + arm*-netbsd*-eabi*) + TARGET=ARM; + arch_target=arm; + AC_DEFINE(TARGET_ARM, 1, [...]) + ACCESS_UNALIGNED="no" + CPPFLAGS="$CPPFLAGS -D__ARM_EABI__" + # Can't use tls, since it depends on the runtime detection of tls offsets + # in mono-compiler.h + with_tls=pthread + target_mach=no + ;; + i686*-linux-*) + TARGET=X86; + arch_target=x86; + AC_DEFINE(TARGET_X86, 1, [...]) + CPPFLAGS="$CPPFLAGS" + # Can't use tls, since it depends on the runtime detection of tls offsets + # in mono-compiler.h + with_tls=pthread + target_mach=no + + case "$target" in + *-linux-android*) + AC_DEFINE(TARGET_ANDROID, 1, [...]) + ;; + esac + ;; + x86_64*-linux-*) + TARGET=AMD64; + arch_target=amd64; + AC_DEFINE(TARGET_AMD64, 1, [...]) + CPPFLAGS="$CPPFLAGS" + # Can't use tls, since it depends on the runtime detection of tls offsets + # in mono-compiler.h + with_tls=pthread + target_mach=no + + case "$target" in + *-linux-android*) + AC_DEFINE(TARGET_ANDROID, 1, [...]) + ;; + esac + ;; + x86_64-ps4-freebsd) + TARGET=AMD64; + arch_target=amd64; + AC_DEFINE(TARGET_AMD64, 1, [...]) + AC_DEFINE(TARGET_PS4, 1, [...]) + AC_DEFINE(DISABLE_HW_TRAPS, 1, [...]) + CPPFLAGS="$CPPFLAGS" + # Can't use tls, since it depends on the runtime detection of tls offsets + # in mono-compiler.h + with_tls=pthread + target_mach=no + target_win32=no + ;; + aarch64*-linux-*) + TARGET=ARM64; + arch_target=arm64; + AC_DEFINE(TARGET_ARM64, 1, [...]) + CPPFLAGS="$CPPFLAGS" + # Can't use tls, since it depends on the runtime detection of tls offsets + # in mono-compiler.h + with_tls=pthread + target_mach=no + case "$target" in + *-linux-android*) + AC_DEFINE(TARGET_ANDROID, 1, [...]) + ;; + esac + ;; + aarch64-*) + TARGET=ARM64 + ;; + *) + AC_MSG_ERROR([Cross compiling is not supported for target $target]) + esac +fi + +case "$TARGET" in +X86) + AC_DEFINE(TARGET_X86, 1, [...]) + ;; +AMD64) + AC_DEFINE(TARGET_AMD64, 1, [...]) + ;; +ARM) + AC_DEFINE(TARGET_ARM, 1, [...]) + ;; +ARM64) + AC_DEFINE(TARGET_ARM64, 1, [...]) + ;; +POWERPC) + AC_DEFINE(TARGET_POWERPC, 1, [...]) + ;; +POWERPC64) + AC_DEFINE(TARGET_POWERPC, 1, [...]) + AC_DEFINE(TARGET_POWERPC64, 1, [...]) + ;; +S390X) + AC_DEFINE(TARGET_S390X, 1, [...]) + ;; +MIPS) + AC_DEFINE(TARGET_MIPS, 1, [...]) + ;; +SPARC) + AC_DEFINE(TARGET_SPARC, 1, [...]) + ;; +SPARC64) + AC_DEFINE(TARGET_SPARC64, 1, [...]) + ;; +esac + +case "$HOST" in +X86) + AC_DEFINE(HOST_X86, 1, [...]) + ;; +AMD64) + AC_DEFINE(HOST_AMD64, 1, [...]) + ;; +ARM) + AC_DEFINE(HOST_ARM, 1, [...]) + ;; +ARM64) + AC_DEFINE(HOST_ARM64, 1, [...]) + ;; +POWERPC) + AC_DEFINE(HOST_POWERPC, 1, [...]) + ;; +POWERPC64) + AC_DEFINE(HOST_POWERPC, 1, [...]) + AC_DEFINE(HOST_POWERPC64, 1, [...]) + ;; +S390X) + AC_DEFINE(HOST_S390X, 1, [...]) + ;; +MIPS) + AC_DEFINE(HOST_MIPS, 1, [...]) + ;; +SPARC) + AC_DEFINE(HOST_SPARC, 1, [...]) + ;; +SPARC64) + AC_DEFINE(HOST_SPARC64, 1, [...]) + ;; +esac + +#WASM hack +fi + + +dnl ************* +dnl *** VTUNE *** +dnl ************* + +AC_ARG_ENABLE(vtune,[ --enable-vtune Enable the VTUNE back-end], enable_vtune=$enableval, enable_vtune=no) +AC_ARG_WITH(vtune, [ --with-vtune= Enable jit vtune profiling], enable_vtune=yes,) + +AM_CONDITIONAL(HAVE_VTUNE, test x$enable_vtune = xyes) + +if test "x$enable_vtune" = "xyes"; then + if test "x$with_vtune" = "x"; then + VTUNE_PATH=/opt/intel/vtune_amplifier_xe + else + VTUNE_PATH=$with_vtune + fi + VTUNE_INCLUDE=$VTUNE_PATH/include + case "$TARGET" in + X86) + VTUNE_LIB=$VTUNE_PATH/lib32 + ;; + AMD64) + VTUNE_LIB=$VTUNE_PATH/lib64 + ;; + *) + AC_MSG_ERROR([Unsupported target $TARGET for VTUNE.]) + ;; + esac + if test ! -f $VTUNE_INCLUDE/jitprofiling.h; then + AC_MSG_ERROR([VTUNE $VTUNE_INCLUDE/jitprofiling.h not found.]) + fi + if test ! -f $VTUNE_LIB/libjitprofiling.a; then + AC_MSG_ERROR([VTUNE $VTUNE_LIB/libjitprofiling.a not found.]) + fi + + VTUNE_CFLAGS=-I$VTUNE_INCLUDE + VTUNE_LIBS="-L/$VTUNE_LIB/ -ljitprofiling" + + AC_SUBST(VTUNE_LIBS) + AC_SUBST(VTUNE_CFLAGS) +fi +dnl Use GCC atomic ops if they work on the target. +if test x$GCC = "xyes"; then + case $TARGET in + X86 | AMD64 | ARM | ARM64 | POWERPC | POWERPC64 | MIPS | S390X | SPARC | SPARC64) + AC_DEFINE(USE_GCC_ATOMIC_OPS, 1, [...]) + ;; + esac +fi + +if test "x$target_mach" = "xyes"; then + + if test "x$TARGET_SYS" = "xWATCHOS"; then + AC_DEFINE(TARGET_WATCHOS,1,[The JIT/AOT targets WatchOS]) + 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]) + CPPFLAGS_FOR_LIBGC="$CPPFLAGS_FOR_LIBGC -DTARGET_IOS" + CFLAGS_FOR_LIBGC="$CFLAGS_FOR_LIBGC -DTARGET_IOS" + BTLS_SUPPORTED=no + else + AC_TRY_COMPILE([#include "TargetConditionals.h"],[ + #if TARGET_IPHONE_SIMULATOR == 1 || TARGET_OS_IPHONE == 1 + #error fail this for ios + #endif + return 0; + ], [ + AC_DEFINE(TARGET_OSX,1,[The JIT/AOT targets OSX]) + CPPFLAGS_FOR_LIBGC="$CPPFLAGS_FOR_LIBGC -DTARGET_OSX" + CFLAGS_FOR_LIBGC="$CFLAGS_FOR_LIBGC -DTARGET_OSX" + target_osx=yes + ], [ + 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 + target_ios=yes + ]) + fi + AC_DEFINE(TARGET_MACH,1,[The JIT/AOT targets Apple platforms]) +fi + +AM_CONDITIONAL(TARGET_OSX, test x$target_osx = xyes) + +if test "x$sizeof_register" = "x4"; then + AC_DEFINE(SIZEOF_REGISTER,4,[size of machine integer registers]) +elif test "x$sizeof_register" = "x8"; then + AC_DEFINE(SIZEOF_REGISTER,8,[size of machine integer registers]) +else + AC_DEFINE(SIZEOF_REGISTER,SIZEOF_VOID_P,[size of machine integer registers]) +fi + +AC_SUBST(SIZEOF_VOID_P,[$ac_cv_sizeof_void_p]) + +if test "x$target_byte_order" = "xG_BIG_ENDIAN"; then + AC_DEFINE(TARGET_BYTE_ORDER,G_BIG_ENDIAN,[byte order of target]) +elif test "x$target_byte_order" = "xG_LITTLE_ENDIAN"; then + AC_DEFINE(TARGET_BYTE_ORDER,G_LITTLE_ENDIAN,[byte order of target]) +else + AC_DEFINE(TARGET_BYTE_ORDER,G_BYTE_ORDER,[byte order of target]) +fi + +if test "x$have_visibility_hidden" = "xyes"; then + AC_DEFINE(HAVE_VISIBILITY_HIDDEN, 1, [Support for the visibility ("hidden") attribute]) +fi + +if test "x$have_deprecated" = "xyes"; then + AC_DEFINE(HAVE_DEPRECATED, 1, [Support for the deprecated attribute]) +fi + +dnl +dnl Simple Generational checks (sgen) +dnl +SGEN_DEFINES= +AC_ARG_WITH(sgen, [ --with-sgen=yes,no Extra Generational GC, default=yes],[buildsgen=$with_sgen],[buildsgen=yes]) +AC_ARG_WITH(sgen-default-concurrent, [ --with-sgen-default-concurrent=yes,no Use Concurrent GC, default=no],[],[with_sgen_default_concurrent=no]) +if test x$buildsgen = xyes; then + AC_DEFINE(HAVE_MOVING_COLLECTOR, 1, [Moving collector]) + SGEN_DEFINES="-DHAVE_SGEN_GC" + + conc_gc_msg="" + if test x$with_sgen_default_concurrent != xno; then + AC_DEFINE(HAVE_CONC_GC_AS_DEFAULT, 1, [Defaults to concurrent GC]) + conc_gc_msg=" (concurrent by default)" + fi + + if test "x$gc_msg" = "x"; then + gc_msg="sgen$conc_gc_msg" + else + gc_msg="sgen$conc_gc_msg and $gc_msg" + fi +fi +AC_SUBST(SGEN_DEFINES) +AM_CONDITIONAL(SUPPORT_SGEN, test x$buildsgen = xyes) + +jit_status="Building and using the JIT" + +libsuffix=".so" + +case "$host" in + *-*-darwin*) + libsuffix=".dylib" + LIBC="libc.dylib" + INTL="libintl.dylib" + SQLITE="libsqlite.0.dylib" + SQLITE3="libsqlite3.0.dylib" + X11="libX11.dylib" + GDKX11="libgdk-x11-2.0.dylib" + GTKX11="libgtk-x11-2.0.dylib" + ;; + *-*-*netbsd*) + LIBC="libc.so" + INTL="libintl.so" + SQLITE="libsqlite.so" + SQLITE3="libsqlite3.so" + ;; + *-*-kfreebsd*-gnu) + LIBC="libc.so.0.1" + INTL="libc.so.0.1" + X11="libX11.so.6" + ;; + *-*-*freebsd*) + LIBC="libc.so.7" + INTL="libintl.so" + SQLITE="libsqlite.so" + SQLITE3="libsqlite3.so" + ;; + *-*-*openbsd*) + LIBC="libc.so" + INTL="libintl.so" + SQLITE="libsqlite.so" + SQLITE3="libsqlite3.so" + ;; + *-*-*linux*) + AC_PATH_X + dlsearch_path=`(libtool --config ; echo eval echo \\$sys_lib_dlsearch_path_spec) | sh` + AC_MSG_CHECKING(for the soname of libX11.so) + for i in $x_libraries $dlsearch_path; do + for r in 4 5 6; do + if test -f $i/libX11.so.$r; then + X11=libX11.so.$r + AC_MSG_RESULT($X11) + fi + done + done + + if test "x$X11" = "xlibX11.so"; then + AC_MSG_WARN([Could not find libX11.so. Do you have X.org or XFree86 installed? Assuming libX11.so.6...]); + X11=libX11.so.6 + fi + ;; +esac + +AC_SUBST(libsuffix) + +###################################### +# EGLIB CHECKS +###################################### + +GNUC_PRETTY= +GNUC_UNUSED= +BREAKPOINT="G_STMT_START { raise (SIGTRAP); } G_STMT_END" +if test x$GCC = xyes; then + GNUC_UNUSED='__attribute__((__unused__))' + GNUC_NORETURN='__attribute__((__noreturn__))' + case $host_cpu in + i*86|x86_64) BREAKPOINT="G_STMT_START { __asm__ (\"int \$03\"); } G_STMT_END" ;; + esac +fi +AC_SUBST(GNUC_PRETTY) +AC_SUBST(GNUC_UNUSED) +AC_SUBST(GNUC_NORETURN) +AC_SUBST(BREAKPOINT) + +AC_C_BIGENDIAN([ORDER=G_BIG_ENDIAN],[ORDER=G_LITTLE_ENDIAN]) + +case $host in +*-*-msdos* | *-*-go32* | *-*-mingw32* | *-*-cygwin* | *-*-windows*) + PATHSEP='\\' + SEARCHSEP=';' + OS="WIN32" + PIDTYPE='void *' + ;; +*) + PATHSEP='/' + SEARCHSEP=':' + OS="UNIX" + PIDTYPE='int' + ;; +esac + +case $host in + *-*-solaris*) + CFLAGS="$CFLAGS -D_POSIX_PTHREAD_SEMANTICS" + ;; +esac + +case $target in +arm*-darwin*|aarch64*-*) + CFLAGS="$CFLAGS -U_FORTIFY_SOURCE" + ;; +i*86-*-darwin*) + ORDER=G_LITTLE_ENDIAN + ;; +*-*-haiku*) + LDFLAGS="$LDFLAGS -ltextencoding" + ;; +*-*-openbsd*) + CFLAGS="$CFLAGS -pthread" + LDFLAGS="$LDFLAGS -pthread" + ;; +esac + +AC_SUBST(ORDER) +AC_SUBST(CFLAGS) +AC_SUBST(PATHSEP) +AC_SUBST(SEARCHSEP) +AC_SUBST(OS) +AC_SUBST(PIDTYPE) + +# Defined for all targets/platforms using classic Windows API support. +AC_DEFINE(HAVE_CLASSIC_WINAPI_SUPPORT, 1, [Use classic Windows API support]) +AC_DEFINE(HAVE_UWP_WINAPI_SUPPORT, 0, [Don't use UWP Windows API support]) + +AC_CHECK_FUNCS(strlcpy stpcpy strtok_r rewinddir vasprintf strerror_r) +AC_CHECK_FUNCS(getrlimit) +AC_CHECK_FUNCS(fork execv execve) + +AC_ARG_WITH([overridable-allocators], [ --with-overridable-allocators allow g_*alloc/g_free to call custom allocators set via g_mem_set_vtable]) + +if test x$with_overridable_allocators = xyes; then + AC_DEFINE(ENABLE_OVERRIDABLE_ALLOCATORS,1,[Overridable allocator support enabled]) + AC_MSG_NOTICE([Overridable allocator support enabled]) +else + AC_MSG_NOTICE([Overridable allocator support disabled]) +fi + +# +# Mono currently supports 10.6, but strndup is not available prior to 10.7; avoiding +# the detection of strndup on OS X so Mono built on 10.7+ still runs on 10.6. This can be +# removed once support for 10.6 is dropped. +# +# iOS detection of strndup and getpwuid_r is faulty for some reason so let's simply avoid it +# +if test x$target_osx = xyes; then +AC_CHECK_FUNCS(getpwuid_r) +elif test x$target_ios = xno; then +AC_CHECK_FUNCS(strndup getpwuid_r) +fi + +AM_CONDITIONAL(NEED_VASPRINTF, test x$ac_cv_func_vasprintf = xno || test x$with_overridable_allocators = xyes) +AM_ICONV() +AC_SEARCH_LIBS(sqrtf, m) + +# nanosleep may not be part of libc, also search it in other libraries +AC_SEARCH_LIBS(nanosleep, rt) + +AC_SEARCH_LIBS(dlopen, dl) +old_ldflags="${LDFLAGS}" +LDFLAGS="${LDFLAGS} -Wl,-export-dynamic" +AC_TRY_LINK(, [int i;], found_export_dynamic=yes, found_export_dynamic=no) +if test $found_export_dynamic = no; then + LDFLAGS="${old_ldflags}" +fi + +AC_MSG_CHECKING(for varargs macros) +AC_TRY_COMPILE([],[ +int foo (int i, int j); +#define bar(...) foo (1, __VA_ARGS__) +void main () { + bar (2); +} +],have_iso_varargs=yes,have_iso_varargs=no) +AC_MSG_RESULT($have_iso_varargs) +G_HAVE_ISO_VARARGS=0 +if test "x$have_iso_varargs" = "xyes"; then + G_HAVE_ISO_VARARGS=1 +fi +AC_SUBST(G_HAVE_ISO_VARARGS) + +AC_CHECK_HEADERS(getopt.h sys/select.h sys/time.h sys/wait.h pwd.h iconv.h localcharset.h sys/types.h sys/resource.h) +AC_CHECK_LIB([iconv], [locale_charset],[],[AC_CHECK_LIB([charset], [locale_charset],[LIBS+="-liconv -lcharset"])]) +AC_CHECK_HEADER(alloca.h, [HAVE_ALLOCA_H=1], [HAVE_ALLOCA_H=0]) +AC_SUBST(HAVE_ALLOCA_H) + +if test $ac_cv_sizeof_void_p = $ac_cv_sizeof_int; then + GPOINTER_TO_INT="((gint) (ptr))" + GPOINTER_TO_UINT="((guint) (ptr))" + GINT_TO_POINTER="((gpointer) (v))" + GUINT_TO_POINTER="((gpointer) (v))" + GSIZE="int" + GSIZE_FORMAT='"u"' +elif test $ac_cv_sizeof_void_p = $ac_cv_sizeof_long; then + GPOINTER_TO_INT="((gint)(long) (ptr))" + GPOINTER_TO_UINT="((guint)(long) (ptr))" + GINT_TO_POINTER="((gpointer)(glong) (v))" + GUINT_TO_POINTER="((gpointer)(gulong) (v))" + GSIZE="long" + GSIZE_FORMAT='"lu"' +elif test $ac_cv_sizeof_void_p = $ac_cv_sizeof_long_long; then + GPOINTER_TO_INT="((gint)(long long) (ptr))" + GPOINTER_TO_UINT="((guint)(unsigned long long) (ptr))" + GINT_TO_POINTER="((gpointer)(long long) (v))" + GUINT_TO_POINTER="((gpointer)(unsigned long long) (v))" + GSIZE="long long" + GSIZE_FORMAT='"I64u"' +else + AC_MSG_ERROR([unsupported pointer size]) +fi + +AC_SUBST(GPOINTER_TO_INT) +AC_SUBST(GPOINTER_TO_UINT) +AC_SUBST(GINT_TO_POINTER) +AC_SUBST(GUINT_TO_POINTER) +AC_SUBST(GSIZE) +AC_SUBST(GSIZE_FORMAT) +AC_SUBST(G_GUINT64_FORMAT) +AC_SUBST(G_GINT64_FORMAT) +AC_SUBST(G_GUINT32_FORMAT) +AC_SUBST(G_GINT32_FORMAT) + +# +# END OF EGLIB CHECKS +# + +AC_ARG_WITH([libgdiplus], + [ --with-libgdiplus=installed|sibling| Override the libgdiplus used for System.Drawing tests (defaults to installed)], + [], [with_libgdiplus=installed]) + +# default install location +libgdiplus_install_loc=libgdiplus${libsuffix} +case "$host" in + *-*-*linux*) + libgdiplus_install_loc=libgdiplus${libsuffix}.0 + ;; +esac + +case $with_libgdiplus in + no|installed) + libgdiplus_loc= + ;; + + yes|sibling) + libgdiplus_loc=`cd ../libgdiplus && pwd`/src/libgdiplus.la + ;; + + /*) # absolute path, assume it is an install location + libgdiplus_loc=$with_libgdiplus + libgdiplus_install_loc=$with_libgdiplus + ;; + + *) + libgdiplus_loc=`pwd`/$with_libgdiplus + ;; +esac +AC_SUBST([libgdiplus_loc]) +AC_SUBST([libgdiplus_install_loc]) + +AC_ARG_ENABLE(icall-symbol-map,[ --enable-icall-symbol-map Generate tables which map icall functions to their C symbols], icall_symbol_map=$enableval, icall_symbol_map=no) +if test "x$icall_symbol_map" = "xyes"; then + AC_DEFINE(ENABLE_ICALL_SYMBOL_MAP, 1, [Icall symbol map enabled]) +fi + +AC_ARG_ENABLE(icall-export,[ --enable-icall-export Export icall functions], icall_export=$enableval, icall_export=no) +if test "x$icall_export" = "xyes"; then + AC_DEFINE(ENABLE_ICALL_EXPORT, 1, [Icall export enabled]) +fi + +AC_ARG_ENABLE(icall-tables,[ --disable-icall-tables Disable the runtime lookup of icalls], icall_tables=$enableval, icall_tables=yes) +if test "x$icall_tables" = "xno"; then + AC_DEFINE(DISABLE_ICALL_TABLES, 1, [Icall tables disabled]) +fi + +if test "x$with_tls" = "x__thread"; then + AC_DEFINE(HAVE_KW_THREAD, 1, [Have __thread keyword]) + # Pass the information to libgc + CPPFLAGS="$CPPFLAGS -DUSE_COMPILER_TLS" + AC_MSG_CHECKING(if the tls_model attribute is supported) + AC_TRY_COMPILE([static __thread int foo __attribute__((tls_model("initial-exec")));], [ + ], [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_TLS_MODEL_ATTR, 1, [tls_model available]) + ], [ + AC_MSG_RESULT(no) + ]) +fi + +if test ${TARGET} = ARM; then + dnl ****************************************** + dnl *** Check to see what FPU is available *** + dnl ****************************************** + AC_MSG_CHECKING(which FPU to use) + + # + # This is a bit tricky: + # + # if (__ARM_PCS_VFP) { + # /* mfloat-abi=hard == VFP with hard ABI */ + # } elif (!__SOFTFP__) { + # /* mfloat-abi=softfp == VFP with soft ABI */ + # } else { + # /* mfloat-abi=soft == no VFP */ + # } + # + # The exception is iOS (w/ GCC) where none of the above + # are defined (but iOS always uses the 'softfp' ABI). + # + # No support for FPA. + # + + fpu=NONE + + # iOS GCC always uses the 'softfp' ABI. + if test x"$GCC" = xyes && test x$host_darwin = xyes; then + fpu=VFP + fi + + # Are we using the 'hard' ABI? + if test x$fpu = xNONE; then + AC_TRY_COMPILE([], [ + #ifndef __ARM_PCS_VFP + #error "Float ABI is not 'hard'" + #endif + return 0; + ], [ + fpu=VFP_HARD + ], [ + fpu=NONE + ]) + fi + + # No 'hard' ABI. 'soft' or 'softfp'? + if test x$fpu = xNONE; then + AC_TRY_COMPILE([], [ + #ifdef __SOFTFP__ + #error "Float ABI is not 'softfp'" + #endif + return 0; + ], [ + fpu=VFP + ], [ + fpu=NONE + ]) + fi + + if test x$arm_fpu != x; then + fpu=$arm_fpu + fi + + AC_MSG_RESULT($fpu) + CPPFLAGS="$CPPFLAGS -DARM_FPU_$fpu=1" + unset fpu + + dnl ********************************************* + dnl *** Check which ARM version(s) we can use *** + dnl ********************************************* + AC_MSG_CHECKING(which ARM version to use) + + AC_TRY_COMPILE([], [ + #if !defined(__ARM_ARCH_5T__) && !defined(__ARM_ARCH_5TE__) && !defined(__ARM_ARCH_5TEJ__) + #error Not on ARM v5. + #endif + return 0; + ], [ + arm_v5=yes + + arm_ver=ARMv5 + ], []) + + AC_TRY_COMPILE([], [ + #if !defined(__ARM_ARCH_6J__) && !defined(__ARM_ARCH_6ZK__) && !defined(__ARM_ARCH_6K__) && !defined(__ARM_ARCH_6T2__) && !defined(__ARM_ARCH_6M__) + #error Not on ARM v6. + #endif + return 0; + ], [ + arm_v5=yes + arm_v6=yes + + arm_ver=ARMv6 + ], []) + + AC_TRY_COMPILE([], [ + #if !defined(__ARM_ARCH_7A__) && !defined(__ARM_ARCH_7R__) && !defined(__ARM_ARCH_7EM__) && !defined(__ARM_ARCH_7M__) && !defined(__ARM_ARCH_7S__) + #error Not on ARM v7. + #endif + return 0; + ], [ + arm_v5=yes + arm_v6=yes + arm_v7=yes + + arm_ver=ARMv7 + ], []) + + AC_MSG_RESULT($arm_ver) + + if test x$arm_v5 = xyes; then + AC_DEFINE(HAVE_ARMV5, 1, [ARM v5]) + CPPFLAGS_FOR_LIBGC="$CPPFLAGS_FOR_LIBGC -DHAVE_ARMV5=1" + fi + + if test x$arm_v6 = xyes; then + AC_DEFINE(HAVE_ARMV6, 1, [ARM v6]) + CPPFLAGS_FOR_LIBGC="$CPPFLAGS_FOR_LIBGC -DHAVE_ARMV6=1" + fi + + if test x$arm_v7 = xyes; then + AC_DEFINE(HAVE_ARMV7, 1, [ARM v7]) + CPPFLAGS_FOR_LIBGC="$CPPFLAGS_FOR_LIBGC -DHAVE_ARMV7=1" + fi +fi + +if test ${TARGET} = unknown; then + CPPFLAGS="$CPPFLAGS -DNO_PORT" + AC_MSG_WARN("mono has not been ported to $host: some things may not work.") +fi + +if test "x$platform_android" = "xyes"; then + case "x${TARGET}" in + xARM) + case "x$arm_ver" in + xARMv5) + BTLS_SUPPORTED=yes + BTLS_PLATFORM=android-armv5 + ;; + xARMv6) + BTLS_SUPPORTED=yes + BTLS_PLATFORM=android-armv6 + ;; + xARMv7) + BTLS_SUPPORTED=yes + BTLS_PLATFORM=android-armv7 + ;; + *) + BTLS_SUPPORTED=no + ;; + esac + ;; + xARM64) + BTLS_SUPPORTED=yes + BTLS_PLATFORM=android-v8a + ;; + xX86) + BTLS_SUPPORTED=yes + BTLS_PLATFORM=android-x86 + ;; + xAMD64) + BTLS_SUPPORTED=yes + BTLS_PLATFORM=android-x64 + ;; + *) + BTLS_SUPPORTED=no + ;; + esac +fi + +if test ${ACCESS_UNALIGNED} = no; then + CPPFLAGS="$CPPFLAGS -DNO_UNALIGNED_ACCESS" +fi + +if test x$host_darwin = xyes; then + AC_MSG_CHECKING([for ranlib that supports -no_warning_for_no_symbols option]) + AS_IF( + [$RANLIB -no_warning_for_no_symbols 2>&1 | grep -q "unknown option"], + [AC_MSG_RESULT([no])], + [ + # avoid AR calling ranlib, libtool calls it anyway. suppress no symbols warning. + AR_FLAGS="Scru" + RANLIB="$RANLIB -no_warning_for_no_symbols" + AC_MSG_RESULT([yes]) + ] + ) +fi + +case "x$libgc" in + xincluded) + # Pass CPPFLAGS to libgc configure + # We should use a separate variable for this to avoid passing useless and + # potentially problematic defines to libgc (like -D_FILE_OFFSET_BITS=64) + # This should be executed late so we pick up the final version of CPPFLAGS + # The problem with this approach, is that during a reconfigure, the main + # configure scripts gets invoked with these arguments, so we use separate + # variables understood by libgc's configure to pass CPPFLAGS and CFLAGS. + TMP_CPPFLAGS="$CPPFLAGS $CPPFLAGS_FOR_LIBGC" + if test x$TARGET = xSPARC -o x$TARGET = xSPARC64; then + TMP_CPPFLAGS=`echo $TMP_CPPFLAGS | sed -e 's/-D_FILE_OFFSET_BITS=64//g'` + fi + # Don't pass -finstrument-for-thread-suspension in, + # if these are instrumented it will be very bad news + # (infinite recursion, undefined parking behavior, etc) + TMP_CPPFLAGS=`echo $TMP_CPPFLAGS | sed -e 's/-finstrument-for-thread-suspension//g'` + ac_configure_args="$ac_configure_args --disable-embed-check --with-libgc-threads=$libgc_threads $libgc_configure_args \"CPPFLAGS_FOR_LIBGC=$TMP_CPPFLAGS\" \"CFLAGS_FOR_LIBGC=$CFLAGS_FOR_LIBGC\"" + 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 + +MALLOC_MEMPOOLS=no +AC_ARG_WITH(malloc_mempools,[ --with-malloc-mempools=yes,no Use malloc for each single mempool allocation (only for runtime debugging, defaults to no)],[ + if test x$with_malloc_mempools = xyes; then + MALLOC_MEMPOOLS=yes + AC_DEFINE(USE_MALLOC_FOR_MEMPOOLS,1,[Use malloc for each single mempool allocation]) + fi +]) + + +DISABLE_MCS_DOCS=default +AC_ARG_WITH(mcs_docs,[ --with-mcs-docs=yes,no If you want to build the documentation under mcs (defaults to yes)],[ + if test x$with_mcs_docs != xyes; then + DISABLE_MCS_DOCS=yes + fi +]) +if test -n "$INSTALL_4_x_TRUE"; then : + DISABLE_MCS_DOCS=yes +fi +if test "x$DISABLE_MCS_DOCS" = "xdefault"; then + DISABLE_MCS_DOCS=$DISABLE_MCS_DOCS_default +fi + +AC_ARG_WITH(lazy_gc_thread_creation, [ --with-lazy-gc-thread-creation=yes|no Enable lazy runtime thread creation, embedding host must do it explicitly (defaults to no)],[ + if test x$with_lazy_gc_thread_creation != xno ; then + AC_DEFINE(LAZY_GC_THREAD_CREATION,1,[Enable lazy gc thread creation by the embedding host.]) + fi +], [with_lazy_gc_thread_creation=no]) + +AC_ARG_WITH(cooperative_gc, [ --with-cooperative-gc=yes|no Enable cooperative stop-the-world garbage collection (sgen only) (defaults to no)], [], [with_cooperative_gc=default]) + +if test x$with_cooperative_gc = xdefault; then + with_cooperative_gc=$with_cooperative_gc_default +fi + +if test x$with_cooperative_gc != xno; then + AC_DEFINE(USE_COOP_GC,1,[Enable cooperative stop-the-world garbage collection.]) +fi + +AM_CONDITIONAL([ENABLE_COOP], [test x$with_cooperative_gc != xno]) + +AC_ARG_ENABLE(checked_build, [ --enable-checked-build=LIST To enable checked build (expensive asserts), configure with a comma-separated LIST of checked build modules and then include that same list in the environment variable MONO_CHECK_MODE at runtime. Recognized checked build modules: all, gc, metadata, thread],[ + + if test x$enable_checked_build != x ; then + AC_DEFINE(ENABLE_CHECKED_BUILD,1,[Enable checked build]) + fi + for feature in `echo "$enable_checked_build" | sed -e "s/,/ /g"`; do + eval "mono_checked_build_test_enable_$feature='yes'" + done + + if test "x$mono_checked_build_test_enable_all" = "xyes"; then + eval "mono_checked_build_test_enable_gc='yes'" + eval "mono_checked_build_test_enable_metadata='yes'" + eval "mono_checked_build_test_enable_thread='yes'" + fi + + if test "x$mono_checked_build_test_enable_gc" = "xyes"; then + AC_DEFINE(ENABLE_CHECKED_BUILD_GC, 1, [Enable GC checked build]) + fi + + if test "x$mono_checked_build_test_enable_metadata" = "xyes"; then + AC_DEFINE(ENABLE_CHECKED_BUILD_METADATA, 1, [Enable metadata checked build]) + fi + + if test "x$mono_checked_build_test_enable_thread" = "xyes"; then + AC_DEFINE(ENABLE_CHECKED_BUILD_THREAD, 1, [Enable thread checked build]) + fi + +], []) + +AC_CHECK_HEADER([malloc.h], + [AC_DEFINE([HAVE_USR_INCLUDE_MALLOC_H], [1], + [Define to 1 if you have /usr/include/malloc.h.])],,) + +if test x"$GCC" = xyes; then + # Implicit function declarations are not 64 bit safe + # Do this late, since this causes lots of configure tests to fail + CFLAGS="$CFLAGS -Werror-implicit-function-declaration" + # jay has a lot of implicit declarations + JAY_CFLAGS="-Wno-implicit-function-declaration" +fi + +# When --disable-shared is used, libtool transforms libmono-2.0.la into libmono-2.0.so +# instead of libmono-static.a +if test "x$enable_shared" = "xno" -a "x$enable_executables" = "xyes"; then + LIBMONO_LA=libmini-static.la +else + if test x$buildsgen = xyes; then + LIBMONO_LA=libmonosgen-$API_VER.la + else + LIBMONO_LA=libmonoboehm-$API_VER.la + fi +fi +AC_SUBST(LIBMONO_LA) + +dnl ************** +dnl *** Btls *** +dnl ************** + +AC_ARG_ENABLE(btls, [ --disable-btls Disable the BoringTls provider], enable_btls=$enableval, enable_btls=$BTLS_SUPPORTED) +AC_ARG_WITH(btls_android_ndk, [ --with-btls-android-ndk Android NDK for BoringTls]) + +AM_CONDITIONAL(BTLS, test x$enable_btls = xyes) + +btls_android=no +if test "x$enable_btls" = "xyes"; then + AC_PATH_PROG(CMAKE, [cmake], [no], [$PATH:/Applications/CMake.app/Contents/bin:/usr/local/bin]) + if test "x$CMAKE" = "xno"; then + AC_MSG_ERROR("cmake not found") + fi + + BTLS_ROOT=`cd $srcdir && pwd`/external/boringssl + AC_SUBST(BTLS_ROOT) + + btls_arch= + btls_cflags= + BTLS_CMAKE_ARGS= + + case "$BTLS_PLATFORM" in + i386) + btls_arch=i386 + btls_cflags="-m32" + case $host_os in + darwin*) + btls_cflags="$btls_cflags -arch i386" + esac + ;; + x86_64) + btls_arch=x86_64 + ;; + arm) + btls_arch=arm + ;; + armsoft) + btls_arch=arm + btls_cflags="-DOPENSSL_NO_ASM=1" + ;; + aarch64) + btls_arch=aarch64 + ;; + android-armv5) + BTLS_CMAKE_ARGS="-DANDROID_ABI=\"armeabi\" -DANDROID_NATIVE_API_LEVEL=12" + ;; + android-armv6) + BTLS_CMAKE_ARGS="-DANDROID_ABI=\"armeabi\" -DANDROID_NATIVE_API_LEVEL=12" + ;; + android-armv7) + BTLS_CMAKE_ARGS="-DANDROID_ABI=\"armeabi-v7a\" -DANDROID_NATIVE_API_LEVEL=12" + ;; + android-v8a) + BTLS_CMAKE_ARGS="-DANDROID_ABI=\"arm64-v8a\" -DANDROID_NATIVE_API_LEVEL=12" + ;; + android-x86) + BTLS_CMAKE_ARGS="-DANDROID_ABI=\"x86\" -DANDROID_NATIVE_API_LEVEL=12" + ;; + android-x64) + BTLS_CMAKE_ARGS="-DANDROID_ABI=\"x86_64\" -DANDROID_NATIVE_API_LEVEL=12" + ;; + *) + AC_MSG_ERROR(Invalid BTLS platform) + esac + + if test "x$platform_android" = "xyes"; then + btls_android=yes + BTLS_CMAKE_ARGS="$BTLS_CMAKE_ARGS -DCMAKE_TOOLCHAIN_FILE=$BTLS_ROOT/util/android-cmake/android.toolchain.cmake" + if test "x$with_btls_android_ndk" != "x"; then + BTLS_CMAKE_ARGS="$BTLS_CMAKE_ARGS -DANDROID_NDK=\"$with_btls_android_ndk\"" + else + AC_MSG_ERROR([Need to pass the --with-btls-android-ndk argument when building with BTLS support on Android.]) + fi + fi + + if test "x$btls_arch" != "x"; then + BTLS_CMAKE_ARGS="$BTLS_CMAKE_ARGS -DBTLS_ARCH=\"$btls_arch\"" + fi + + BTLS_CFLAGS="$CPPFLAGS_FOR_BTLS $btls_cflags" + AC_SUBST(BTLS_ARCH) + AC_SUBST(BTLS_CFLAGS) + AC_SUBST(BTLS_PLATFORM) + AC_SUBST(BTLS_CMAKE_ARGS) + + AC_DEFINE(HAVE_BTLS, 1, [BoringTls is supported]) +else + enable_btls=no +fi + +AM_CONDITIONAL(BTLS_ANDROID, test x$btls_android = xyes) + +if test x$DISABLE_MCS_DOCS = xyes; then + docs_dir="" +else + docs_dir=docs +fi +AC_SUBST(docs_dir) + +## Maybe should also disable if mcsdir is invalid. Let's punt the issue for now. +AM_CONDITIONAL(BUILD_MCS, [test x$cross_compiling = xno && test x$enable_mcs_build != xno]) +AM_CONDITIONAL(BUILD_SUPPORT, [test x$enable_support_build != xno]) + +libmono_ldflags="$libmono_ldflags $LIBS" + +AM_CONDITIONAL(MIPS_GCC, test ${TARGET}${ac_cv_prog_gcc} = MIPSyes) +AM_CONDITIONAL(MIPS_SGI, test ${TARGET}${ac_cv_prog_gcc} = MIPSno) +AM_CONDITIONAL(SPARC, test x$TARGET = xSPARC) +AM_CONDITIONAL(SPARC64, test x$TARGET = xSPARC64) +AM_CONDITIONAL(X86, test x$TARGET = xX86) +AM_CONDITIONAL(AMD64, test x$TARGET = xAMD64) +AM_CONDITIONAL(MIPS, test x$TARGET = xMIPS) +AM_CONDITIONAL(POWERPC, test x$TARGET = xPOWERPC) +AM_CONDITIONAL(POWERPC64, test x$TARGET = xPOWERPC64) +AM_CONDITIONAL(ARM, test x$TARGET = xARM) +AM_CONDITIONAL(ARM64, test x$TARGET = xARM64) +AM_CONDITIONAL(S390X, test x$TARGET = xS390X) +AM_CONDITIONAL(WASM, test x$TARGET = xWASM) +AM_CONDITIONAL(HOST_X86, test x$HOST = xX86) +AM_CONDITIONAL(HOST_AMD64, test x$HOST = xAMD64) +AM_CONDITIONAL(HOST_ARM, test x$HOST = xARM) +AM_CONDITIONAL(HOST_ARM64, test x$HOST = xARM64) +AM_CONDITIONAL(HOST_WASM, test x$HOST = xWASM) + +AM_CONDITIONAL(CROSS_COMPILE, test "x$host" != "x$target") + +AC_SUBST(LIBC) +AC_SUBST(INTL) +AC_SUBST(SQLITE) +AC_SUBST(SQLITE3) +AC_SUBST(X11) +AC_SUBST(GDKX11) +AC_SUBST(GTKX11) +AC_SUBST(XINERAMA) +AC_DEFINE_UNQUOTED(MONO_ARCHITECTURE,"$arch_target",[The architecture this is running on]) +AC_SUBST(arch_target) +AC_SUBST(CFLAGS) +AC_SUBST(CPPFLAGS) +AC_SUBST(LDFLAGS) + +#This must always be defined when building the runtime +AC_DEFINE(MONO_INSIDE_RUNTIME,1, [Disable banned functions from being used by the runtime]) + +mono_build_root=`pwd` +AC_SUBST(mono_build_root) + +mono_runtime=mono/mini/mono +AC_SUBST(mono_runtime) + +CSC_LOCATION=`cd $srcdir && pwd`/external/roslyn-binaries/Microsoft.Net.Compilers/Microsoft.Net.Compilers.2.6.0/tools/csc.exe + +if test $csc_compiler = mcs; then + CSC=$mcs_topdir/class/lib/build/mcs.exe +else + CSC=$CSC_LOCATION +fi + +mono_cfg_root=$mono_build_root/runtime +if test x$host_win32 = xyes; then + if test "x$cross_compiling" = "xno"; then + mono_cfg_dir=`cygpath -w -a $mono_cfg_root`\\etc + CSC=`cygpath -m -a $CSC` + CSC_LOCATION=`cygpath -m -a $CSC_LOCATION` + else + mono_cfg_dir=`echo $mono_cfg_root | tr '/' '\\'`\\etc + fi +else + mono_cfg_dir=$mono_cfg_root/etc +fi +AC_SUBST(mono_cfg_dir) + +AC_SUBST(CSC) + +AC_CONFIG_FILES([po/mcs/Makefile.in]) + +AC_CONFIG_FILES([runtime/mono-wrapper],[chmod +x runtime/mono-wrapper]) +AC_CONFIG_FILES([runtime/monodis-wrapper],[chmod +x runtime/monodis-wrapper]) + +AC_CONFIG_COMMANDS([runtime/etc/mono/1.0/machine.config], +[ depth=../../../.. + case $srcdir in + [[\\/$]]* | ?:[[\\/]]* ) reldir=$srcdir ;; + .) reldir=$depth ;; + *) reldir=$depth/$srcdir ;; + esac + $ac_aux_dir/install-sh -d runtime/etc/mono/1.0 + cd runtime/etc/mono/1.0 + rm -f machine.config + $LN_S $reldir/data/net_1_1/machine.config machine.config + cd $depth +],[LN_S='$LN_S']) + +AC_CONFIG_COMMANDS([runtime/etc/mono/2.0/machine.config], +[ depth=../../../.. + case $srcdir in + [[\\/$]]* | ?:[[\\/]]* ) reldir=$srcdir ;; + .) reldir=$depth ;; + *) reldir=$depth/$srcdir ;; + esac + $ac_aux_dir/install-sh -d runtime/etc/mono/2.0 + cd runtime/etc/mono/2.0 + rm -f machine.config + $LN_S $reldir/data/net_2_0/machine.config machine.config + cd $depth +],[LN_S='$LN_S']) + +AC_CONFIG_COMMANDS([runtime/etc/mono/2.0/web.config], +[ depth=../../../.. + case $srcdir in + [[\\/$]]* | ?:[[\\/]]* ) reldir=$srcdir ;; + .) reldir=$depth ;; + *) reldir=$depth/$srcdir ;; + esac + $ac_aux_dir/install-sh -d runtime/etc/mono/2.0 + cd runtime/etc/mono/2.0 + rm -f web.config + $LN_S $reldir/data/net_2_0/web.config web.config + cd $depth +],[LN_S='$LN_S']) + +AC_CONFIG_COMMANDS([runtime/etc/mono/browscap.ini], +[ depth=../../.. + case $srcdir in + [[\\/$]]* | ?:[[\\/]]* ) reldir=$srcdir ;; + .) reldir=$depth ;; + *) reldir=$depth/$srcdir ;; + esac + $ac_aux_dir/install-sh -d runtime/etc/mono/ + cd runtime/etc/mono/ + rm -f browscap.ini + $LN_S $reldir/data/browscap.ini browscap.ini + cd $depth +],[LN_S='$LN_S']) + +AC_CONFIG_COMMANDS([runtime/etc/mono/2.0/Browsers/Compat.browser], +[ depth=../../../../.. + case $srcdir in + [[\\/$]]* | ?:[[\\/]]* ) reldir=$srcdir ;; + .) reldir=$depth ;; + *) reldir=$depth/$srcdir ;; + esac + $ac_aux_dir/install-sh -d runtime/etc/mono/2.0/Browsers/ + cd runtime/etc/mono/2.0/Browsers + rm -f Compat.browser + $LN_S $reldir/data/Browsers/Compat.browser Compat.browser + cd $depth +],[LN_S='$LN_S']) + +AC_CONFIG_COMMANDS([runtime/etc/mono/4.0/Browsers/Compat.browser], +[ depth=../../../../.. + case $srcdir in + [[\\/$]]* | ?:[[\\/]]* ) reldir=$srcdir ;; + .) reldir=$depth ;; + *) reldir=$depth/$srcdir ;; + esac + $ac_aux_dir/install-sh -d runtime/etc/mono/4.0/Browsers/ + cd runtime/etc/mono/4.0/Browsers + rm -f Compat.browser + $LN_S $reldir/data/Browsers/Compat.browser Compat.browser + cd $depth +],[LN_S='$LN_S']) + +AC_CONFIG_COMMANDS([runtime/etc/mono/4.5/Browsers/Compat.browser], +[ depth=../../../../.. + case $srcdir in + [[\\/$]]* | ?:[[\\/]]* ) reldir=$srcdir ;; + .) reldir=$depth ;; + *) reldir=$depth/$srcdir ;; + esac + $ac_aux_dir/install-sh -d runtime/etc/mono/4.5/Browsers/ + cd runtime/etc/mono/4.5/Browsers + rm -f Compat.browser + $LN_S $reldir/data/Browsers/Compat.browser Compat.browser + cd $depth +],[LN_S='$LN_S']) + +AC_CONFIG_COMMANDS([runtime/etc/mono/4.0/machine.config], +[ depth=../../../.. + case $srcdir in + [[\\/$]]* | ?:[[\\/]]* ) reldir=$srcdir ;; + .) reldir=$depth ;; + *) reldir=$depth/$srcdir ;; + esac + $ac_aux_dir/install-sh -d runtime/etc/mono/4.0 + cd runtime/etc/mono/4.0 + rm -f machine.config + $LN_S $reldir/data/net_4_0/machine.config machine.config + cd $depth +],[LN_S='$LN_S']) + +AC_CONFIG_COMMANDS([runtime/etc/mono/4.0/web.config], +[ depth=../../../.. + case $srcdir in + [[\\/$]]* | ?:[[\\/]]* ) reldir=$srcdir ;; + .) reldir=$depth ;; + *) reldir=$depth/$srcdir ;; + esac + $ac_aux_dir/install-sh -d runtime/etc/mono/4.0 + cd runtime/etc/mono/4.0 + rm -f web.config + $LN_S $reldir/data/net_4_0/web.config web.config + cd $depth +],[LN_S='$LN_S']) + +AC_CONFIG_COMMANDS([runtime/etc/mono/4.5/machine.config], +[ depth=../../../.. + case $srcdir in + [[\\/$]]* | ?:[[\\/]]* ) reldir=$srcdir ;; + .) reldir=$depth ;; + *) reldir=$depth/$srcdir ;; + esac + $ac_aux_dir/install-sh -d runtime/etc/mono/4.5 + cd runtime/etc/mono/4.5 + rm -f machine.config + $LN_S $reldir/data/net_4_5/machine.config machine.config + cd $depth +],[LN_S='$LN_S']) + +AC_CONFIG_COMMANDS([runtime/etc/mono/4.5/web.config], +[ depth=../../../.. + case $srcdir in + [[\\/$]]* | ?:[[\\/]]* ) reldir=$srcdir ;; + .) reldir=$depth ;; + *) reldir=$depth/$srcdir ;; + esac + $ac_aux_dir/install-sh -d runtime/etc/mono/4.5 + cd runtime/etc/mono/4.5 + rm -f web.config + $LN_S $reldir/data/net_4_5/web.config web.config + cd $depth +],[LN_S='$LN_S']) + +AC_CONFIG_COMMANDS([quiet-libtool], [sed -e 's/echo "copying selected/# "copying selected/g' < libtool > libtool.tmp && mv libtool.tmp libtool && chmod a+x libtool; sed -e 's/$ECHO "copying selected/# "copying selected/g' < libtool > libtool.tmp && mv libtool.tmp libtool && chmod a+x libtool]) +AC_CONFIG_COMMANDS([nolock-libtool], [sed -e 's/lock_old_archive_extraction=yes/lock_old_archive_extraction=no/g' < libtool > libtool.tmp && mv libtool.tmp libtool && chmod a+x libtool]) + +AC_OUTPUT([ +Makefile +mono-uninstalled.pc +acceptance-tests/Makefile +llvm/Makefile +scripts/mono-find-provides +scripts/mono-find-requires +mono/Makefile +mono/btls/Makefile +mono/utils/Makefile +mono/metadata/Makefile +mono/dis/Makefile +mono/cil/Makefile +mono/arch/Makefile +mono/arch/x86/Makefile +mono/arch/amd64/Makefile +mono/arch/ppc/Makefile +mono/arch/sparc/Makefile +mono/arch/s390x/Makefile +mono/arch/arm/Makefile +mono/arch/arm64/Makefile +mono/arch/mips/Makefile +mono/sgen/Makefile +mono/tests/Makefile +mono/tests/tests-config +mono/tests/gc-descriptors/Makefile +mono/tests/testing_gac/Makefile +mono/unit-tests/Makefile +mono/benchmark/Makefile +mono/mini/Makefile +mono/profiler/Makefile +mono/eglib/Makefile +mono/eglib/eglib-config.h +m4/Makefile +ikvm-native/Makefile +scripts/Makefile +man/Makefile +docs/Makefile +data/Makefile +data/net_2_0/Makefile +data/net_4_0/Makefile +data/net_4_5/Makefile +data/net_2_0/Browsers/Makefile +data/net_4_0/Browsers/Makefile +data/net_4_5/Browsers/Makefile +data/mint.pc +data/mono-2.pc +data/monosgen-2.pc +data/mono.pc +data/mono-cairo.pc +data/mono-nunit.pc +data/mono-options.pc +data/mono-lineeditor.pc +data/monodoc.pc +data/dotnet.pc +data/dotnet35.pc +data/wcf.pc +data/cecil.pc +data/system.web.extensions_1.0.pc +data/system.web.extensions.design_1.0.pc +data/system.web.mvc.pc +data/system.web.mvc2.pc +data/system.web.mvc3.pc +data/aspnetwebstack.pc +data/reactive.pc +samples/Makefile +support/Makefile +data/config +tools/Makefile +tools/locale-builder/Makefile +tools/sgen/Makefile +tools/monograph/Makefile +tools/pedump/Makefile +runtime/Makefile +msvc/Makefile +po/Makefile +]) + +# Update all submodules recursively to ensure everything is checked out +$srcdir/scripts/update_submodules.sh + +if test x$host_win32 = xyes; then + # Get rid of 'cyg' prefixes in library names + sed -e "s/\/cyg\//\/\//" libtool > libtool.new; mv libtool.new libtool; chmod 755 libtool + # libtool seems to inherit -mno-cygwin from our CFLAGS, and uses it to compile its executable + # wrapper scripts which use exec(). gcc has no problem compiling+linking this, but the resulting + # executable doesn't work... + sed -e "s,-mno-cygwin,,g" libtool > libtool.new; mv libtool.new libtool; chmod 755 libtool +fi + +if test x$host_darwin = xyes; then + # This doesn't seem to be required and it slows down parallel builds + sed -e 's,lock_old_archive_extraction=yes,lock_old_archive_extraction=no,g' < libtool > libtool.new && mv libtool.new libtool && chmod +x libtool +fi + +( + case $prefix in + NONE) prefix=$ac_default_prefix ;; + esac + case $exec_prefix in + NONE) exec_prefix='${prefix}' ;; + esac + + # + # If we are cross compiling, we don't build in the mcs/ tree. Let us not clobber + # any existing config.make. This allows people to share the same source tree + # with different build directories, one native and one cross + # + if test x$cross_compiling = xno && test x$enable_mcs_build != xno; then + + test -w $mcs_topdir/build || chmod +w $mcs_topdir/build + + echo "prefix=$prefix" > $mcs_topdir/build/config.make + echo "exec_prefix=$exec_prefix" >> $mcs_topdir/build/config.make + echo "sysconfdir=$sysconfdir" >> $mcs_topdir/build/config.make + echo 'mono_libdir=${exec_prefix}/lib' >> $mcs_topdir/build/config.make + echo "mono_build_root=$mono_build_root" >> $mcs_topdir/build/config.make + echo 'IL_FLAGS = /debug' >> $mcs_topdir/build/config.make + echo "RUNTIME = $mono_build_root/runtime/mono-wrapper" >> $mcs_topdir/build/config.make + echo "ILDISASM = $mono_build_root/runtime/monodis-wrapper" >> $mcs_topdir/build/config.make + echo "JAY_CFLAGS = $JAY_CFLAGS" >> $mcs_topdir/build/config.make + + case $INSTALL in + [[\\/$]]* | ?:[[\\/]]* ) mcs_INSTALL=$INSTALL ;; + *) mcs_INSTALL=$mono_build_root/$INSTALL ;; + esac + + echo "INSTALL = $mcs_INSTALL" >> $mcs_topdir/build/config.make + + export VERSION + [myver=$($AWK 'BEGIN { + split (ENVIRON["VERSION"] ".0.0.0", vsplit, ".") + if(length(vsplit [1]) > 4) { + split (substr(ENVIRON["VERSION"], 0, 4) "." substr(ENVIRON["VERSION"], 5) ".0.0", vsplit, ".") + } + print vsplit [1] "." vsplit [2] "." vsplit [3] "." vsplit [4] + }')] + + echo "MONO_VERSION = $myver" >> $mcs_topdir/build/config.make + echo "MONO_CORLIB_VERSION = $MONO_CORLIB_VERSION" >> $mcs_topdir/build/config.make + + if test x$host_darwin = xyes; then + echo "BUILD_PLATFORM = darwin" >> $mcs_topdir/build/config.make + elif test x$host_win32 = xyes; then + echo "BUILD_PLATFORM = win32" >> $mcs_topdir/build/config.make + else + echo "BUILD_PLATFORM = linux" >> $mcs_topdir/build/config.make + fi + + if test x$host_darwin = xyes; then + echo "HOST_PLATFORM ?= darwin" >> $mcs_topdir/build/config.make + elif test x$host_win32 = xyes; then + echo "HOST_PLATFORM ?= win32" >> $mcs_topdir/build/config.make + else + echo "HOST_PLATFORM ?= linux" >> $mcs_topdir/build/config.make + fi + + if test "x$PLATFORM_AOT_SUFFIX" != "x"; then + echo "PLATFORM_AOT_SUFFIX = $PLATFORM_AOT_SUFFIX" >> $mcs_topdir/build/config.make + fi + + if test x$AOT_SUPPORTED = xyes -a x$enable_system_aot = xdefault; then + enable_system_aot=yes + fi + + if test x$host_win32 = xno -a x$enable_system_aot = xyes; then + echo "ENABLE_AOT = 1" >> $mcs_topdir/build/config.make + fi + + if test x$DISABLE_MCS_DOCS = xyes; then + echo "DISABLE_MCS_DOCS = yes" >> $mcs_topdir/build/config.make + fi + + if test x$has_extension_module != xno; then + echo "EXTENSION_MODULE = 1" >> $srcdir/$mcsdir/build/config.make + fi + + echo "DEFAULT_PROFILE = $default_profile" >> $srcdir/$mcsdir/build/config.make + + if test "x$test_bcl_opt" = "xyes"; then + echo "BCL_OPTIMIZE = 1" >> $srcdir/$mcsdir/build/config.make + fi + + echo "CSC_LOCATION = $CSC_LOCATION" >> $srcdir/$mcsdir/build/config.make + + if test $csc_compiler = mcs; then + echo "MCS_MODE = 1" >> $srcdir/$mcsdir/build/config.make + fi + + if test "x$AOT_BUILD_FLAGS" != "x" ; then + echo "AOT_RUN_FLAGS=$AOT_RUN_FLAGS" >> $srcdir/$mcsdir/build/config.make + echo "AOT_BUILD_FLAGS=$AOT_BUILD_FLAGS" >> $srcdir/$mcsdir/build/config.make + echo "AOT_BUILD_ATTRS=$AOT_BUILD_ATTRS" >> $srcdir/$mcsdir/build/config.make + fi + + if test "x$AOT_MODE" != "x" ; then + echo "AOT_MODE=$AOT_MODE" >> $srcdir/$mcsdir/build/config.make + fi + + if test "x$enable_btls" = "xyes"; then + echo "HAVE_BTLS=1" >> $srcdir/$mcsdir/build/config.make + fi + + fi + +) + +libgdiplus_msg=${libgdiplus_loc:-assumed to be installed} + +btls_platform_string= +if test x$enable_btls = xyes; then + if test x$btls_android = xyes; then + btls_platform_string=" (android:$BTLS_PLATFORM)" + else + btls_platform_string=" ($BTLS_PLATFORM)" + fi +fi + +echo " + mcs source: $mcsdir + C# Compiler: $csc_compiler + + Engine: + Host: $host + Target: $target + GC: $gc_msg + TLS: $with_tls + SIGALTSTACK: $with_sigaltstack + Engine: $jit_status + BigArrays: $enable_big_arrays + DTrace: $enable_dtrace + LLVM Back End: $enable_llvm (dynamically loaded: $enable_loadedllvm) + + Libraries: + .NET 4.x: $with_profile4_x + Xamarin.Android: $with_monodroid + Xamarin.iOS: $with_monotouch + Xamarin.WatchOS: $with_monotouch_watch + 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 + Test profiles: AOT Full ($with_testing_aot_full), AOT Hybrid ($with_testing_aot_hybrid) + JNI support: $jdk_headers_found + libgdiplus: $libgdiplus_msg + zlib: $zlib_msg + BTLS: $enable_btls$btls_platform_string + $disabled +" +if test x$with_static_mono = xno -a "x$host_win32" != "xyes"; then + AC_MSG_WARN(Turning off static Mono is a risk, you might run into unexpected bugs) +fi diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/.gitattributes b/unity-2019.4.24f1-mbe/external/bdwgc/.gitattributes new file mode 100644 index 000000000..4f3ff11bb --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/.gitattributes @@ -0,0 +1,11 @@ +# Git repo attributes. + +# Ensure all text files have normalized (LF) line endings in the repository. +* text=auto + +# These files should use CR/LF line ending: +/BCC_MAKEFILE -text +/digimars.mak -text + +# Note: "core.eol" configuration variable controls which line endings to use +# for the normalized files in the working directory (the default is native). diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/.gitignore b/unity-2019.4.24f1-mbe/external/bdwgc/.gitignore new file mode 100644 index 000000000..b2f182ef2 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/.gitignore @@ -0,0 +1,172 @@ +# Ignored files in bdwgc Git repo. + +# Binary files (in root dir, cord, tests): +*.dll +*.exe +*.gcda +*.gch +*.gcno +*.la +*.lib +*.lo +*.o +*.obj + +*.gc.log +.dirstamp +/*_bench.log +/*_bench.trs +/*test.log +/*test.trs +/.libs/ +/Makefile +/add_gc_prefix +/base_lib +/bdw-gc.pc +/bsd-libgc.a +/bsd-libleak.a +/c++ +/config.cache +/config.log +/config.status +/cord/cordtest +/cord/de +/cord/de_win.rbj +/cord/de_win.res +/cord/tests/de_win.rbj +/cord/tests/de_win.res +/cords +/cordtest +/core +/de +/disclaim_bench +/disclaim_test +/dont_ar_1 +/dont_ar_3 +/dont_ar_4 +/gc-* +/gc.a +/gc.log +/gcname +/gctest +/gctest_dyn_link +/gctest_irix_dyn_link +/hugetest +/if_mach +/if_not_there +/initsecondarythread_test +/leaktest +/libalphagc.so +/libgc.so +/libirixgc.so +/liblinuxgc.so +/libtool +/middletest +/realloc_test +/setjmp_test +/smashtest +/staticrootstest +/subthreadcreate_test +/sunos5gc.so +/test-suite.log +/test_atomic_ops +/test_atomic_ops.log +/test_atomic_ops.trs +/test_cpp +/test_cpp.cpp +/test_cpp.log +/test_cpp.trs +/threadkey_test +/threadleaktest +/threadlibs +/tracetest + +# Config, dependency and stamp files generated by configure: +.deps/ +/include/config.h +config.h.in~ +stamp-h1 + +# External library (without trailing slash to allow symlinks): +/libatomic_ops* +/pthreads-w32* + +# These files are generated by autoreconf: +/Makefile.in +/aclocal.m4 +/autom4te.cache/ +/compile +/config.guess +/config.sub +/configure +/depcomp +/include/config.h.in +/install-sh +/ltmain.sh +/m4/libtool.m4 +/m4/ltoptions.m4 +/m4/ltsugar.m4 +/m4/ltversion.m4 +/m4/lt~obsolete.m4 +/missing +/mkinstalldirs +/test-driver + +# These files are generated by CMake: +*.tlog +/*.vcxproj +/*.vcxproj.filters +/CMakeCache.txt +/CMakeFiles/ +/CTestTestfile.cmake +/DartConfiguration.tcl +/Testing/Temporary/ +/cmake_install.cmake +/gc.sln +/libgc*-dll.so +/libgc*-lib.a +/tests/*.vcxproj +/tests/*.vcxproj.filters +/tests/*test +/tests/CMakeFiles/ +/tests/CTestTestfile.cmake +/tests/Makefile +/tests/cmake_install.cmake +/tests/test_cpp + +# Rarely generated files (mostly by some Win/DOS compilers): +/*.copied.c +/*.csm +/*.err +/*.i +/*.lb1 +/*.lnk +/*.map +/*.out +/*.rbj +/*.res +/*.stackdump +/*.sym +/*.tmp +*.bsc +*.dll.manifest +*.exp +*.idb +*.ilk +*.pdb +*.sbr + +# Stuff from VS build system and IDE +*.vcproj.*.user + +# Code analysis tools: +*.c.gcov +*.cc.gcov +*.h.gcov +*.sancov +/.sv*-dir +/cov-int +/coverage.info +/pvs-project.log +/pvs-project.tasks +/strace_out diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/.gitmodules b/unity-2019.4.24f1-mbe/external/bdwgc/.gitmodules new file mode 100644 index 000000000..f7c7ca9d1 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/.gitmodules @@ -0,0 +1,4 @@ +[submodule "libatomic_ops"] + path = libatomic_ops + url = git://github.com/Unity-Technologies/libatomic_ops.git + branch = unity-release-7_4 diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/.travis.yml b/unity-2019.4.24f1-mbe/external/bdwgc/.travis.yml new file mode 100644 index 000000000..12b2bee1f --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/.travis.yml @@ -0,0 +1,620 @@ +language: c++ + +matrix: + include: + - os: linux + compiler: clang + env: + - CONF_OPTIONS="--enable-cplusplus" + - NO_CLONE_LIBATOMIC_OPS=true + - os: linux + compiler: gcc + env: + - CONF_OPTIONS="--enable-cplusplus" + - os: osx + env: + - CONF_OPTIONS="--enable-cplusplus" + - NO_CLONE_LIBATOMIC_OPS=true + - os: linux + env: + - COVERITY_SCAN_BRANCH=1 + addons: + coverity_scan: + project: + name: ivmai/bdwgc + version: 7.7.0 + notification_email: ivmai@mail.ru + branch_pattern: master + build_command_prepend: "./configure --enable-cplusplus --disable-shared --enable-single-obj-compilation" + build_command: make -j check CFLAGS_EXTRA=-DLINT2 + - os: linux + env: + - MAKEFILE_TARGETS="distcheck" + - AUTOMAKE_VER=1.15 + - M4_VER=1.4.18 + - LIBTOOL_VER=2.4.6 + - PKG_CONFIG_VER=0.29.2 + - NO_CLONE_LIBATOMIC_OPS=true + - os: linux + env: + - MAKEFILE_TARGETS="dist" + - os: linux + compiler: gcc + env: + - CONF_OPTIONS="--enable-gc-assertions --enable-cplusplus --enable-static" + - NO_CLONE_LIBATOMIC_OPS=true + - os: linux + addons: + apt: + packages: + - gcc-multilib + compiler: clang + env: + - CFLAGS_EXTRA="-m32" + - CONF_OPTIONS="--enable-gc-assertions" + - NO_CLONE_LIBATOMIC_OPS=true + - os: linux + addons: + apt: + packages: + - gcc-multilib + compiler: gcc + env: + - CFLAGS_EXTRA="-m32" + - CONF_OPTIONS="--enable-gc-assertions" + - os: osx + env: + - CFLAGS_EXTRA="-m32" + - CONF_OPTIONS="--enable-gc-assertions --enable-cplusplus" + - NO_CLONE_LIBATOMIC_OPS=true + - os: linux + compiler: gcc + env: + - CONF_OPTIONS="--disable-threads --enable-checksums --disable-munmap --enable-cplusplus" + - NO_CLONE_LIBATOMIC_OPS=true + - os: linux + compiler: clang + env: + - CFLAGS_EXTRA="-D DBG_HDRS_ALL -D SHORT_DBG_HDRS" + - CONF_OPTIONS="--enable-gc-assertions --enable-cplusplus" + - NO_CLONE_LIBATOMIC_OPS=true + - os: osx + env: + - CFLAGS_EXTRA="-D DBG_HDRS_ALL -D SHORT_DBG_HDRS -D LINT2" + - CONF_OPTIONS="--enable-gc-assertions --enable-cplusplus --enable-handle-fork=manual" + - NO_CLONE_LIBATOMIC_OPS=true + - os: linux + compiler: gcc + env: + - CFLAGS_EXTRA="-D DEBUG_ADD_DEL_ROOTS -D DEBUG_THREADS -D GC_LOG_TO_FILE_ALWAYS" + - CONF_OPTIONS="--enable-cplusplus" + - os: linux + compiler: gcc + env: + - CFLAGS_EXTRA="-D DEFAULT_STACK_MAYBE_SMALL -D ENABLE_TRACE -D EMPTY_GETENV_RESULTS -D GC_ALWAYS_MULTITHREADED -D GC_NETBSD_THREADS_WORKAROUND -D CPPCHECK" + - CONF_OPTIONS="--enable-cplusplus" + - os: linux + compiler: clang + env: + - CFLAGS_EXTRA="-march=native -D _FORTIFY_SOURCE=2 -D DEFAULT_VDB" + - CONF_OPTIONS="--with-libatomic-ops=no --enable-cplusplus --disable-handle-fork" + - os: linux + addons: + apt: + packages: + - libatomic-ops-dev + compiler: gcc + env: + - CONF_OPTIONS="--with-libatomic-ops=yes --enable-gc-assertions --enable-cplusplus" + - NO_CLONE_LIBATOMIC_OPS=true + - os: osx + env: + - CFLAGS_EXTRA="-march=native -D _FORTIFY_SOURCE=2 -D AO_DISABLE_GCC_ATOMICS" + - CONF_OPTIONS="--with-libatomic-ops=no --enable-munmap --enable-cplusplus --enable-static" + - os: linux + compiler: gcc + env: + - CFLAGS_EXTRA="-D DONT_ADD_BYTE_AT_END" + - CONF_OPTIONS="--enable-gc-assertions --enable-cplusplus" + - os: linux + addons: + apt: + packages: + - gcc-multilib + compiler: gcc + env: + - CFLAGS_EXTRA="-m32 -D MARK_BIT_PER_OBJ" + - os: linux + compiler: gcc + env: + - CFLAGS_EXTRA="-D NO_CLOCK -D POINTER_MASK=~0xf" + - CONF_OPTIONS="--enable-gc-assertions --enable-cplusplus --enable-handle-fork=manual" + - os: linux + compiler: gcc + env: + - CFLAGS_EXTRA="-D PROC_VDB -D GC_NO_SYS_FAULT_H -D NO_INCREMENTAL -D DEBUG_DIRTY_BITS" + - CONF_OPTIONS="--enable-cplusplus --disable-docs" + - os: linux + compiler: clang + env: + - CFLAGS_EXTRA="-D MANUAL_VDB" + - CONF_OPTIONS="--enable-gc-assertions --disable-parallel-mark" + - os: linux + compiler: gcc + env: + - CFLAGS_EXTRA="-D SMALL_CONFIG -D NO_GETENV" + - CONF_OPTIONS="--enable-cplusplus" + - os: linux + compiler: gcc + env: + - CFLAGS_EXTRA="-std=c11 -D GC_NO_SIGSETJMP" + - CONF_OPTIONS="--disable-threads --enable-gc-assertions --enable-cplusplus" + - NO_CLONE_LIBATOMIC_OPS=true + - os: linux + compiler: clang + env: + - CONF_OPTIONS="--disable-thread-local-alloc --enable-cplusplus --enable-static" + - NO_CLONE_LIBATOMIC_OPS=true + - os: linux + compiler: gcc + env: + - CONF_OPTIONS="--disable-parallel-mark --disable-thread-local-alloc --enable-gc-assertions --enable-cplusplus" + - os: linux + addons: + apt: + packages: + - lcov + compiler: gcc + env: + - CONF_OPTIONS="--enable-gcov --enable-single-obj-compilation --enable-cplusplus --disable-shared --enable-gc-assertions" + - CFLAGS_EXTRA="-D USE_CUSTOM_SPECIFIC" + - REPORT_COVERAGE=true + - os: linux + compiler: gcc + env: + - CONF_OPTIONS="--enable-gc-debug --enable-cplusplus" + - os: linux + compiler: gcc + env: + - CONF_OPTIONS="--disable-gc-debug --enable-cplusplus" + - os: linux + compiler: clang + env: + - CONF_OPTIONS="--enable-large-config --enable-cplusplus --disable-shared --enable-static" + - NO_CLONE_LIBATOMIC_OPS=true + - os: osx + env: + - CONF_OPTIONS="--enable-large-config --enable-cplusplus --disable-handle-fork" + - NO_CLONE_LIBATOMIC_OPS=true + - os: linux + addons: + apt: + packages: + - gcc-multilib + compiler: gcc + env: + - CONF_OPTIONS="--enable-large-config --disable-munmap" + - CFLAGS_EXTRA="-m32" + - os: linux + compiler: gcc + env: + - CONF_OPTIONS="--enable-large-config --enable-cplusplus --enable-gc-assertions --enable-static" + - CFLAGS_EXTRA="-D LINT2" + - os: linux + addons: + apt: + packages: + - gcc-multilib + compiler: clang + env: + - CONF_OPTIONS="--enable-redirect-malloc --enable-static --disable-threads" + - CFLAGS_EXTRA="-m32" + - NO_CLONE_LIBATOMIC_OPS=true + - os: osx + env: + - CONF_OPTIONS="--enable-redirect-malloc --enable-cplusplus --enable-static --disable-threads" + - CFLAGS_EXTRA="-m32" + - NO_CLONE_LIBATOMIC_OPS=true + - os: linux + compiler: gcc + env: + - CONF_OPTIONS="--enable-redirect-malloc --enable-gc-debug --enable-cplusplus --enable-gc-assertions" + - os: linux + compiler: clang + env: + - CONF_OPTIONS="--disable-threads --enable-cplusplus" + - CFLAGS_EXTRA="-O3 -march=native" + - NO_CLONE_LIBATOMIC_OPS=true + - os: linux + compiler: gcc + env: + - CONF_OPTIONS="--disable-static --disable-threads --enable-cplusplus" + - CFLAGS_EXTRA="-O3 -march=native" + - NO_CLONE_LIBATOMIC_OPS=true + - os: osx + env: + - CONF_OPTIONS="--disable-threads --enable-cplusplus" + - CFLAGS_EXTRA="-O3 -march=native" + - NO_CLONE_LIBATOMIC_OPS=true + - os: osx + env: + - CSA_CHECK=true + - CFLAGS_EXTRA="-m32" + - os: linux + addons: + apt: + packages: + - clang-4.0 + sources: + - llvm-toolchain-trusty-4.0 + compiler: clang-4.0 + language: c + env: + - CSA_CHECK=true + - CFLAGS_EXTRA="-D ALL_INTERIOR_POINTERS -D CHECKSUMS -D DBG_HDRS_ALL -D DEBUG_THREADS -D ENABLE_TRACE -D GC_ALWAYS_MULTITHREADED -D GC_ASSERTIONS -D GC_ATOMIC_UNCOLLECTABLE -D GC_ENABLE_SUSPEND_THREAD -D GC_GCJ_SUPPORT -D GC_PRINT_BACK_HEIGHT -D GC_THREADS -D HANDLE_FORK -D JAVA_FINALIZATION -D KEEP_BACK_PTRS -D MAKE_BACK_GRAPH -D PARALLEL_MARK -D PRINT_BLACK_LIST -D THREAD_LOCAL_ALLOC -D USE_MMAP -D USE_MUNMAP" + - os: linux + env: + - CPPCHECK_ENABLE="-j16 --enable=information,performance,portability,style,warning extra/AmigaOS.c extra/MacOS.c extra/msvc_dbg.c extra/real_malloc.c extra/symbian.cpp" + sudo: required + - os: linux + env: + - CPPCHECK_ENABLE="--enable=unusedFunction" + sudo: required + - os: linux + compiler: clang + env: + - MAKEFILE_NAME=Makefile.direct + - MAKEFILE_TARGETS="check check-cpp cord/de" + - os: linux + compiler: gcc + env: + - MAKEFILE_NAME=Makefile.direct + - MAKEFILE_TARGETS="check check-cpp cord/de" + - os: osx + env: + - MAKEFILE_NAME=Makefile.direct + - MAKEFILE_TARGETS="check check-cpp cord/de" + - os: linux + addons: + apt: + packages: + - musl-tools + compiler: musl-gcc + language: c + env: + - CONF_OPTIONS="--disable-parallel-mark --enable-gc-assertions" + - os: linux + addons: + apt: + packages: + - clang-4.0 + sources: + - llvm-toolchain-trusty-4.0 + compiler: clang-4.0 + language: c + env: + - CXX=clang++-4.0 + - CFLAGS_EXTRA="-fsanitize=address -fno-common -fno-omit-frame-pointer" + - CONF_OPTIONS="--enable-cplusplus --enable-static" + - NO_CLONE_LIBATOMIC_OPS=true + - TESTS_CUSTOM_RUN=true + sudo: required + - os: linux + addons: + apt: + packages: + - gcc-5 + sources: + - ubuntu-toolchain-r-test + compiler: gcc-5 + language: c + env: + - CFLAGS_EXTRA="-fsanitize=address -O0" + - CONF_OPTIONS="--enable-gc-assertions" + - LDFLAGS="-fuse-ld=gold" + - NO_CLONE_LIBATOMIC_OPS=true + - os: osx + env: + - CFLAGS_EXTRA="-fsanitize=address -m32 -fno-omit-frame-pointer" + - CONF_OPTIONS="--disable-shared --enable-cplusplus" + - NO_CLONE_LIBATOMIC_OPS=true + - os: linux + addons: + apt: + packages: + - clang-4.0 + sources: + - llvm-toolchain-trusty-4.0 + compiler: clang-4.0 + language: c + env: + - CFLAGS_EXTRA="-fsanitize=memory -fno-omit-frame-pointer" + - CONF_OPTIONS="--enable-static" + - TESTS_CUSTOM_RUN=true + - NO_CLONE_LIBATOMIC_OPS=true + sudo: required + - os: linux + compiler: clang + env: + - CFLAGS_EXTRA="-fsanitize=undefined -fno-common -fno-omit-frame-pointer" + - TESTS_CUSTOM_RUN=true + - CONF_OPTIONS="--enable-cplusplus --enable-static" + - NO_CLONE_LIBATOMIC_OPS=true + - os: linux + addons: + apt: + packages: + - clang-4.0 + sources: + - llvm-toolchain-trusty-4.0 + compiler: clang-4.0 + language: c + env: + - CFLAGS_EXTRA="-fsanitize=thread -D NO_CANCEL_SAFE -D NO_INCREMENTAL -D USE_SPIN_LOCK -fno-omit-frame-pointer -D TEST_FORK_WITHOUT_ATFORK" + - CONF_OPTIONS="--enable-gc-assertions --enable-handle-fork=manual" + - NO_CLONE_LIBATOMIC_OPS=true + - os: linux + addons: + apt: + packages: + - clang-4.0 + sources: + - llvm-toolchain-trusty-4.0 + compiler: clang-4.0 + language: c + env: + - CFLAGS_EXTRA="-fsanitize=thread -D NO_CANCEL_SAFE -D NO_INCREMENTAL -D USE_SPIN_LOCK -fno-omit-frame-pointer -D TEST_FORK_WITHOUT_ATFORK" + - CONF_OPTIONS="--enable-gc-assertions --enable-gc-debug --enable-handle-fork=manual --enable-large-config --with-libatomic-ops=no" + - os: linux + addons: + apt: + packages: + - clang-4.0 + sources: + - llvm-toolchain-trusty-4.0 + compiler: clang-4.0 + language: c + env: + - CFLAGS_EXTRA="-fsanitize=thread -D NO_CANCEL_SAFE -D NO_INCREMENTAL -D USE_SPIN_LOCK -fno-omit-frame-pointer -D NTHREADS=15" + - CONF_OPTIONS="--disable-parallel-mark" + - NO_CLONE_LIBATOMIC_OPS=true + - os: linux + addons: + apt: + packages: + - clang-4.0 + sources: + - llvm-toolchain-trusty-4.0 + compiler: clang-4.0 + language: c + env: + - CFLAGS_EXTRA="-fsanitize=thread -D NO_CANCEL_SAFE -D NO_INCREMENTAL -fno-omit-frame-pointer" + - CONF_OPTIONS="--disable-parallel-mark --disable-thread-local-alloc --disable-shared --enable-gc-assertions --with-libatomic-ops=no" + - os: linux + addons: + apt: + packages: + - clang-4.0 + sources: + - llvm-toolchain-trusty-4.0 + compiler: clang-4.0 + language: c + env: + - CXX=clang++-4.0 + - CFLAGS_EXTRA="-O3 -march=native" + - CONF_OPTIONS="--enable-cplusplus --enable-static --enable-single-obj-compilation" + - NO_CLONE_LIBATOMIC_OPS=true + - os: linux + addons: + apt: + packages: + - clang-4.0 + - gcc-multilib + sources: + - llvm-toolchain-trusty-4.0 + compiler: clang-4.0 + language: c + env: + - CFLAGS_EXTRA="-m32 -D _FORTIFY_SOURCE=2 -D GC_DISABLE_INCREMENTAL" + - CONF_OPTIONS="--enable-gc-assertions --enable-gc-debug" + - NO_CLONE_LIBATOMIC_OPS=true + - os: linux + addons: + apt: + packages: + - gcc-5 + - g++-5 + sources: + - ubuntu-toolchain-r-test + compiler: gcc-5 + language: c + env: + - CXX=g++-5 + - CONF_OPTIONS="--enable-cplusplus --enable-gc-assertions --enable-static" + - NO_CLONE_LIBATOMIC_OPS=true + - os: linux + addons: + apt: + packages: + - gcc-5 + - gcc-5-multilib + - gcc-multilib + sources: + - ubuntu-toolchain-r-test + compiler: gcc-5 + language: c + env: + - CFLAGS_EXTRA="-m32 -O3" + - CONF_OPTIONS="--disable-shared --enable-static --enable-single-obj-compilation" + - NO_CLONE_LIBATOMIC_OPS=true + - os: linux + addons: + apt: + packages: + - gcc-5 + - gcc-5-multilib + - gcc-multilib + sources: + - ubuntu-toolchain-r-test + compiler: gcc-5 + language: c + env: + - CFLAGS_EXTRA="-mx32 -march=native -D _FORTIFY_SOURCE=2" + - CONF_OPTIONS="--enable-large-config --enable-gc-assertions" + - NO_CLONE_LIBATOMIC_OPS=true + - os: linux + compiler: clang + env: + - CFLAGS_EXTRA="-x c++" + - CONF_OPTIONS="--enable-gc-assertions --enable-cplusplus --enable-gc-debug --disable-shared" + - MAKEFILE_TARGETS="all" + - NO_CLONE_LIBATOMIC_OPS=true + - os: linux + compiler: gcc + env: + - CC_FOR_CHECK=g++ + - CONF_OPTIONS="--enable-gc-assertions" + - MAKEFILE_TARGETS="all" + - MAKEFILE_TARGETS_CHECK="check" + - NO_CLONE_LIBATOMIC_OPS=true + - os: linux + compiler: clang + env: + - CFLAGS_EXTRA="-O3 -Wall -Wextra -Werror -x c++" + - MAKEFILE_NAME=Makefile.direct + - MAKEFILE_TARGETS="cords" + - os: linux + compiler: gcc + env: + - CC_FOR_CHECK=g++ + - CFLAGS_EXTRA="-O3 -Wall -Wextra -Werror" + - MAKEFILE_NAME=Makefile.direct + - MAKEFILE_TARGETS="cords" + - MAKEFILE_TARGETS_CHECK="cord/de check" + - os: linux + addons: + apt: + packages: + - g++-mingw-w64 + - gcc-mingw-w64 + compiler: x86_64-w64-mingw32-gcc + language: c + env: + - CXX=x86_64-w64-mingw32-g++ + - CONF_OPTIONS="--host=x86_64-w64-mingw32 --enable-cplusplus --enable-static" + - MAKEFILE_TARGETS="all" + - os: linux + addons: + apt: + packages: + - gcc-mingw-w64 + compiler: i686-w64-mingw32-gcc + language: c + env: + - CONF_OPTIONS="--host=i686-w64-mingw32" + - MAKEFILE_TARGETS="all" + - CFLAGS_EXTRA="-fno-omit-frame-pointer" + +before_install: +- if [[ "$CPPCHECK_ENABLE" != "" ]]; then + git clone --depth=3 https://github.com/danmar/cppcheck.git + ~/cppcheck -b master; + make --directory ~/cppcheck -j CXXFLAGS="-O3 -march=native -D NDEBUG"; + fi +- if [[ "$AUTOMAKE_VER" != "" || "$LIBTOOL_VER" != "" + || "$M4_VER" != "" || "$PKG_CONFIG_VER" != "" ]]; then + GNUTOOLS_ROOT=`pwd`/../gnu-tools; + export PATH=$GNUTOOLS_ROOT/bin:$PATH; + fi +- if [[ "$M4_VER" != "" ]]; then + M4_XZ_URL=https://ftp.gnu.org/gnu/m4/m4-$M4_VER.tar.xz; + wget -O - $M4_XZ_URL | tar xf - --xz --directory ~; + (cd ~/m4-$M4_VER && ./configure --prefix=$GNUTOOLS_ROOT && make -j check && make install); + fi +- if [[ "$LIBTOOL_VER" != "" ]]; then + LIBTOOL_XZ_URL=https://ftp.gnu.org/gnu/libtool/libtool-$LIBTOOL_VER.tar.xz; + wget -O - $LIBTOOL_XZ_URL | tar xf - --xz --directory ~; + (cd ~/libtool-$LIBTOOL_VER && ./configure --prefix=$GNUTOOLS_ROOT && make -j && make install); + fi +- if [[ "$AUTOMAKE_VER" != "" ]]; then + AUTOMAKE_XZ_URL=https://ftp.gnu.org/gnu/automake/automake-$AUTOMAKE_VER.tar.xz; + wget -O - $AUTOMAKE_XZ_URL | tar xf - --xz --directory ~; + (cd ~/automake-$AUTOMAKE_VER && ./configure --prefix=$GNUTOOLS_ROOT && make -j && make install); + fi +- if [[ "$PKG_CONFIG_VER" != "" ]]; then + PKG_CONFIG_GZ_URL=https://pkgconfig.freedesktop.org/releases/pkg-config-$PKG_CONFIG_VER.tar.gz; + wget -O - $PKG_CONFIG_GZ_URL | tar xf - --gz --directory ~; + (cd ~/pkg-config-$PKG_CONFIG_VER && ./configure --with-internal-glib --prefix=$GNUTOOLS_ROOT && make -j check && make install); + fi +- if [[ "$MAKEFILE_TARGETS" == *"dist"* ]]; then + autoconf --version; + automake --version; + m4 --version; + libtool --version || true; + pkg-config --version; + fi +- if [[ "$MAKEFILE_NAME" == "" ]]; then MAKEFILE_NAME=Makefile; fi +- if [[ "$MAKEFILE_TARGETS" == "" ]]; then MAKEFILE_TARGETS="check"; fi + +install: +- if [[ "$NO_CLONE_LIBATOMIC_OPS" != true ]]; then + git clone --depth=50 https://github.com/ivmai/libatomic_ops.git; + fi +- "./autogen.sh" +- if [[ "$GNUTOOLS_ROOT" != "" ]]; then mv $GNUTOOLS_ROOT $GNUTOOLS_ROOT-x; fi +- if [[ "$REPORT_COVERAGE" == true ]]; then gem install coveralls-lcov; fi + +script: +- if [[ "$CSA_CHECK" != true && "$CPPCHECK_ENABLE" == "" + && "$MAKEFILE_NAME" != "Makefile.direct" + && "$COVERITY_SCAN_BRANCH" != 1 ]]; then + ./configure $CONF_OPTIONS --enable-werror && cat include/config.h; + fi +- if [[ "$CSA_CHECK" != true && "$CPPCHECK_ENABLE" == "" + && "$COVERITY_SCAN_BRANCH" != 1 ]]; then + make -j -f $MAKEFILE_NAME $MAKEFILE_TARGETS CFLAGS_EXTRA="$CFLAGS_EXTRA" + LDFLAGS="$LDFLAGS"; + fi +- if [[ "$CC_FOR_CHECK" != "" ]]; then + make -f $MAKEFILE_NAME $MAKEFILE_TARGETS_CHECK CC=$CC_FOR_CHECK + CFLAGS_EXTRA="$CFLAGS_EXTRA"; + fi +- if [ -f gctest.log ]; then cat gctest.log; fi +- if [ -f threadkey_test.log ]; then cat threadkey_test.log; fi +- if [[ "$CSA_CHECK" == true ]]; then + ${CC} --analyze -Xanalyzer -analyzer-output=text -Werror + -I include -I libatomic_ops/src $CFLAGS_EXTRA + *.c *.cc cord/*.c cord/tests/cordtest.c cord/tests/de.c extra/gc.c + extra/msvc_dbg.c extra/pcr_interface.c extra/real_malloc.c + tests/*.c tests/*.cc tools/*.c; + fi +- if [[ "$CPPCHECK_ENABLE" != "" ]]; then + ~/cppcheck/cppcheck -q -f --error-exitcode=2 -U GC_API -U long -D CPPCHECK + -I include -I libatomic_ops/src $CPPCHECK_ENABLE + *.cc cord/*.c cord/tests/*.c tests/*.c tests/*.cc tools/*.c + extra/gc.c; + fi +- if [[ "$TESTS_CUSTOM_RUN" == true ]]; then + ASAN_OPTIONS="detect_leaks=1" UBSAN_OPTIONS="halt_on_error=1" + make check-without-test-driver; + fi + +after_success: +- if [[ "$REPORT_COVERAGE" == true ]]; then + lcov --capture --base-directory . --directory . --output-file coverage.info; + lcov --remove coverage.info '/usr/*' 'cord/tests/*' 'libatomic_ops/*' 'tests/*' --output-file coverage.info; + lcov --list coverage.info; + coveralls-lcov --repo-token ${COVERALLS_TOKEN} coverage.info; + fi + +deploy: + provider: releases + api_key: + secure: j1HkSD5hyYFo//XzPemojLk6iBT+T9+PwktxtDOwdasR0lOvPyBS2k/RbBZp+amDna/40efTg2WVI8BEfpRMV8+QoYGVoTWCKCaxFCHMfSZdsPLYsHpeqp7PBh3sFX6iuQuZkRGXHNeG8cLHTTw1TatrEBU/vTZXItYKmOJH8WnlwwkiVXKQ3BvU9EJjFB1OJX9PArBXgoHjDcgi6D1kL6ErsraU9nwnaBNRFx5Tpvz/fXZDwjzMnGcxeu02zhVC37mFDd6VbKom8Pm28u4NjYLLhjdICexc0BaVC0kr3+usJytyMtWMRm6LN4kFievOntHZOEAtuU6/E0Bg8wnB8FX8vXaytb+eUVtcfS/n/x6ykgPtKHrCnWEP8nnruW/3qRExxSBASyAwBceJK+yyzVBtQudK4YZnBXUWkFfsc9pPiauUDXkYlUgvVyb3rXvJTvjdiIle194GtRsCQsGKxCZiI6jMB/wRQAA9AgcSb3FpRTqkVPEUT3Gnn0xT+Y0QLJBBm4eAG8qWoEuMMAW8lAVvfHdZh3AnnIvqONvc2dk7kq5EZyhUPTFFDxZGnXWzIwY7Nm9QkUOdvb9t+RsnoTwBL4hSD4/T1CgW/hULQ7cuiwtK/E+8r5C1VES5/I20qkwfTGwKQchdR6lOLO7YM4VlHfNbEUe/wsE3PBh0ekc= + file: gc-*.tar.gz + file_glob: true + skip_cleanup: true + on: + condition: $MAKEFILE_TARGETS = distcheck + repo: ivmai/bdwgc + tags: true diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/AUTHORS b/unity-2019.4.24f1-mbe/external/bdwgc/AUTHORS new file mode 100644 index 000000000..3953fb26d --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/AUTHORS @@ -0,0 +1,406 @@ +This is an attempt to acknowledge contributions to the garbage collector. +Early contributions also mentioned (duplicated) in ChangeLog file; details of +later ones should be in "git log". + +HISTORY - + + Early versions of this collector were developed as a part of research +projects supported in part by the National Science Foundation +and the Defense Advance Research Projects Agency. + +The garbage collector originated as part of the run-time system for +the Russell programming language implementation. The first version of the +garbage collector was written primarily by Alan Demers. It was then refined +and mostly rewritten, primarily by Hans-J. Boehm, at Cornell U., +the University of Washington, Rice University (where it was first used for +C and assembly code), Xerox PARC, SGI, and HP Labs. However, significant +contributions have also been made by many others. + +Other contributors (my apologies for any omissions): + +Adam Megacz +Adnan Ali +Adrian Bunk +Adrian Pop +Akira Tagoh +Alain Novak +Alan Dosser +Alan J. Demers +Aleksey Demakov +Alessandro Bruni +Alex Ronne Petersen +Alexander Belchenko +Alexander Gavrilov +Alexander Herz +Alexandr Petrosian +Alexandr Shadchin +Alexandre Oliva +Alexis Laferriere +Alistair G. Crooks +Allan Hsu +Andre Leiradella +Andreas Jaeger +Andreas Tobler +Andrei Polushin +Andrej Cedilnik +Andrew Begel +Andrew Buss +Andrew Gray +Andrew Haley +Andrew Horton +Andrew McKinlay +Andrew Pinski +Andrew Stitcher +Andrew Stone +Andy Li +Andy Wingo +Anselm Baird-Smith +Anthony Green +Antoine de Maricourt +Ari Huttunen +Arrigo Triulzi +Ashley Bone +Assar Westerlund +Aurelien Larcher +Barry DeFreese +Baruch Siach +Ben A. Mesander +Ben Cottrell +Ben Hutchings +Ben Maurer +Benjamin Lerman +Bernd Edlinger +Bernd Kuhls +Bernie Solomon +Bill Janssen +Bo Thorsen +Bradley D. LaRonde +Bradley Smith +Brent Benson +Brian Alliet +Brian Beuning +Brian Burton +Brian D. Carlstrom +Brian F. Dennis +Brian Lewis +Bruce Hoult +Bruce Mitchener +Bruno Haible +Bryce McKinlay +Burkhard Linke +Calvin Buckley +Carlos J. Puga Medina +Cesar Eduardo Barros +Charles Fiterman +Charles Mills +Chris Dodd +Chris Lingard +Chris Metcalf +Christian Joensson +Christian Limpach +Christian Thalinger +Christoffe Raffali +Clay Spence +Colin LeMahieu +Craig McDaniel +Dai Sato +Dan Bonachea +Dan Fandrich +Dan Sullivan +Daniel R. Grayson +Danny Smith +Darrell Schiebel +Dave Barrett +Dave Detlefs +Dave Korn +Dave Love +David Ayers +David Brownlee +David Butenhof +David Chase +David Daney +David Grove +David Leonard +David Miller +David Mossberger +David Peroutka +David Pickens +David Stes +David Terei +David Van Horn +Davide Angelocola +Dick Porter +Dietmar Planitzer +Dimitris Vyzovitis +Dimitry Andric +Djamel Magri +Doug Kaufman +Doug Moen +Douglas Steel +Eli Barzilay +Elijah Taylor +Elvenlord Elrond +Emmanual Stumpf +Eric Benson +Eric Holk +Erik M. Bray +Fabian Thylman +Fergus Henderson +Franklin Chen +Fred Gilham +Fred Stearns +Friedrich Dominicus +Gabor Drescher +Gary Leavens +Geoff Norton +George Talbot +Gerard A Allan +Glauco Masotti +Grzegorz Jakacki +Gustavo Giraldez +Gustavo Rodriguez-Rivera +H.J. Lu +Hamayama +Hannes Mehnert +Hanno Boeck +Hans Boehm +Hans-Peter Nilsson +Henning Makholm +Henrik Theiling +Hironori Sakamoto +Hiroshi Kawashima +Hiroshi Yokota +Hubert Garavel +Iain Sandoe +Ian Piumarta +Ian Searle +Igor Khavkine +Ivan Demakov +Ivan Maidanski +Jaap Boender +Jack Andrews +Jacob Navia +Jakub Jelinek +James Clark +James Dominy +Jan Alexander Steffens +Jan Wielemaker +Jani Kajala +Jay Krell +Jean-Baptiste Nivois +Jean-Claude Beaudoin +Jean-Daniel Fekete +Jeff Sturm +Jeffrey Hsu +Jeffrey Mark Siskind +Jeremy Fitzhardinge +Jesper Peterson +Jesse Hull +Jesse Jones +Jesse Rosenstock +Ji-Yong Chung +Jie Liu +Jim Marshall +Jim Meyering +Joao Abecasis +Joerg Sonnenberger +Johannes Schmidt +Johannes Totz +John Bowman +John Clements +John Ellis +John Merryweather Cooper +Jon Moore +Jonathan Bachrach +Jonathan Chambers +Jonathan Clark +Jonathan Pryor +Joshua Richardson +Juan Jose Garcia-Ripoll +Jukka Jylanki +Kai Tietz +Kaz Kojima +Kazu Hirata +Kazuhiro Inaoka +Keith Seitz +Kenjiro Taura +Kenneth Schalk +Kevin Kenny +Kevin Tew +Kevin Warne +Kimura Wataru +Kjetil Matheussen +Klaus Treichel +Klemens Zwischenbrugger +Knut Tvedten +Krister Walfridsson +Kristian Kristensen +Kumar Srikantan +Kurt Miller +Lars Farm +Laurent Morichetti +Linas Vepstas +Loren J. Rittle +Louis Zhuang +Lucas Meijer +Ludovic Courtes +Maarten Thibaut +Manuel A. Fernandez Montecelo +Manuel Serrano +Marc Recht +Marco Maggi +Marcos Dione +Marcus Herbert +Marek Vasut +Margaret Fleck +Mark Boulter +Mark Mitchell +Mark Reichert +Mark Sibly +Mark Weiser +Martin Hirzel +Martin Tauchmann +Matt Austern +Matthew Flatt +Matthias Andree +Matthias Drochner +Maurizio Vairani +Max Mouratov +Melissa O'Neill +Michael Arnoldus +Michael Fox +Michael Smith +Michael Spertus +Michel Schinz +Miguel de Icaza +Mikael Djurfeldt +Mike Frysinger +Mike Gran +Mike McGaughey +Mike Stump +Mitch Harris +Mohan Embar +Naoyuki Sawa +Nathanael Nerode +Neale Ferguson +Neil Sharman +Nicolas Cannasse +Niibe Yutaka +Niklas Therning +Noah Lavine +Nobuyuki Hikichi +Oliver Kurth +Ondrej Bilka +Paolo Molaro +Parag Patel +Patrick Bridges +Patrick C. Beard +Patrick Doyle +Paul Bone +Paul Brook +Paul Graham +Paul Nash +Per Bothner +Peter Bigot +Peter Chubb +Peter Colson +Peter Housel +Peter Monks +Peter Ross +Peter Seebach +Peter Wang +Petr Krajca +Petr Salinger +Petter Urkedal +Philip Brown +Philipp Tomsich +Philippe Queinnec +Phillip Musumeci +Phong Vo +Pierre de Rop +Pontus Rydin +Radek Polak +Rainer Orth +Ranjit Mathew +Rauli Ruohonen +Regis Cridlig +Reimer Behrends +Renaud Blanch +Rene Girard +Rex Dieter +Reza Shahidi +Richard Earnshaw +Richard Henderson +Richard Sandiford +Rob Haack +Robert Brazile +Rodrigo Kumpera +Roger Sayle +Roland McGrath +Roman Hodek +Romain Naour +Romano Paolo Tenca +Rutger Ovidius +Ryan Gonzalez +Ryan Murray +Salvador Eduardo Tropea +Samuel Martin +Samuel Thibault +Scott Ananian +Scott Schwartz +Shawn Wagner +Shea Levy +Shiro Kawai +Simon Gornall +Simon Posnjak +Slava Sysoltsev +Stefan Ring +Stefano Rivera +Sugioka Toshinobu +Suzuki Toshiya +Sven Hartrumpf +Sven Verdoolaege +Takis Psarogiannakopoulos +Tatsuya Bizenn +Tautvydas Zilys +Terrell Russell +Thiemo Seufer +Thomas Funke +Thomas Klausner +Thomas Linder Puls +Thomas Maier +Thomas Schwinge +Thorsten Glaser +Tilman Vogel +Tim Bingham +Timothy N. Newsham +Tom Tromey +Tommaso Tagliapietra +Toralf Foerster +Toshio Endo +Tsugutomo Enami +Tum Nguyen +Tyson Dowd +Uchiyama Yasushi +Ulrich Drepper +Ulrich Weigand +Uros Bizjak +Vernon Lee +Victor Ivrii +Vitaly Magerya +Vladimir Tsichevski +Walter Bright +Walter Underwood +Wilson Ho +Wink Saville +Xi Wang +Xiaokun Zhu +Yann Dirson +Yannis Bres +Yuki Okumura +Yusuke Suzuki +Yvan Roux +Zach Saw +Zhiying Chen +Zhong Shao +Zoltan Varga diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/BCC_MAKEFILE b/unity-2019.4.24f1-mbe/external/bdwgc/BCC_MAKEFILE new file mode 100644 index 000000000..0b3def472 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/BCC_MAKEFILE @@ -0,0 +1,86 @@ +# Makefile for Borland C++ 5.5 on NT +# +bc= c:\Borland\BCC55 +bcbin= $(bc)\bin +bclib= $(bc)\lib +bcinclude= $(bc)\include + +gcinclude1 = $(bc)\gc6.2\include +gcinclude2 = $(bc)\gc6.2\cord + +cc= $(bcbin)\bcc32 +rc= $(bcbin)\brc32 +lib= $(bcbin)\tlib +link= $(bcbin)\ilink32 +cflags= -O2 -R -v- -vi -H -H=gc.csm -I$(bcinclude);$(gcinclude1);$(gcinclude2) -L$(bclib) \ + -w-pro -w-aus -w-par -w-ccc -w-rch -a4 +defines= -DALL_INTERIOR_POINTERS -DNO_GETENV -DJAVA_FINALIZATION -DENABLE_DISCLAIM -DGC_OPERATOR_NEW_ARRAY + +.c.obj: + $(cc) @&&| + $(cdebug) $(cflags) $(cvars) $(defines) -o$* -c $*.c +| + +.cpp.obj: + $(cc) @&&| + $(cdebug) $(cflags) $(cvars) $(defines) -o$* -c $*.cpp +| + +.rc.res: + $(rc) -i$(bcinclude) -r -fo$* $*.rc + +XXXOBJS= XXXalloc.obj XXXreclaim.obj XXXallchblk.obj XXXmisc.obj \ + XXXmach_dep.obj XXXos_dep.obj XXXmark_rts.obj XXXheaders.obj XXXmark.obj \ + XXXobj_map.obj XXXblacklst.obj XXXfinalize.obj XXXnew_hblk.obj \ + XXXdbg_mlc.obj XXXmalloc.obj XXXdyn_load.obj \ + XXXtypd_mlc.obj XXXptr_chck.obj XXXgc_cpp.obj XXXmallocx.obj \ + XXXfnlz_mlc.obj + +OBJS= $(XXXOBJS:XXX=) + +all: gctest.exe cord\de.exe test_cpp.exe + +$(OBJS) test.obj: include\private\gc_priv.h include\private\gc_hdrs.h include\gc.h include\private\gcconfig.h MAKEFILE + +gc.lib: $(OBJS) + del gc.lib + $(lib) $* @&&| + $(XXXOBJS:XXX=+) +| + +gctest.exe: tests\test.obj gc.lib + $(cc) @&&| + $(cflags) -W -e$* tests\test.obj gc.lib +| + +cord\tests\de.obj cord\tests\de_win.obj: include\cord.h \ + include\cord_pos.h cord\tests\de_win.h cord\tests\de_cmds.h + +cord\de.exe: cord\cordbscs.obj cord\cordxtra.obj cord\tests\de.obj \ + cord\tests\de_win.obj cord\tests\de_win.res gc.lib + $(cc) @&&| + $(cflags) -W -e$* cord\cordbscs.obj cord\cordxtra.obj \ + cord\tests\de.obj cord\tests\de_win.obj gc.lib +| + $(rc) cord\tests\de_win.res cord\de.exe + +gc_cpp.obj: include\gc_cpp.h include\gc.h + +gc_cpp.cpp: gc_cpp.cc + copy gc_cpp.cc gc_cpp.cpp + +test_cpp.cpp: tests\test_cpp.cc + copy tests\test_cpp.cc test_cpp.cpp + +test_cpp.exe: test_cpp.obj include\gc_cpp.h include\gc.h gc.lib + $(cc) @&&| + $(cflags) -W -e$* test_cpp.obj gc.lib +| + +scratch: + -del *.obj *.res *.exe *.csm cord\*.obj cord\*.res cord\*.exe cord\*.csm + +clean: + del gc.lib + del *.obj + del tests\test.obj diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/CMakeLists.txt b/unity-2019.4.24f1-mbe/external/bdwgc/CMakeLists.txt new file mode 100644 index 000000000..72b32a3b2 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/CMakeLists.txt @@ -0,0 +1,240 @@ +# +# Copyright (c) 1994 by Xerox Corporation. All rights reserved. +# Copyright (c) 1996 by Silicon Graphics. All rights reserved. +# Copyright (c) 1998 by Fergus Henderson. All rights reserved. +# Copyright (c) 2000-2010 by Hewlett-Packard Company. All rights reserved. +## +# THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED +# OR IMPLIED. ANY USE IS AT YOUR OWN RISK. +## +# Permission is hereby granted to use or copy this program +# for any purpose, provided the above notices are retained on all copies. +# Permission to modify the code and to distribute modified code is granted, +# provided the above notices are retained, and a notice that the code was +# modified is included with the above copyright notice. +## + +# +# get cmake and run: +# cmake -G "Visual Studio 8 2005" +# in the same dir as this file +# this will generate gc.sln +# + +SET(CMAKE_LEGACY_CYGWIN_WIN32 0) # Remove when CMake >= 2.8.4 is required + +PROJECT(gc) + +INCLUDE(CTest) + +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) + +ADD_DEFINITIONS("-D_CRT_SECURE_NO_DEPRECATE + -DALL_INTERIOR_POINTERS -DNO_EXECUTE_PERMISSION") + +IF(APPLE) + IF("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "") + SET(CMAKE_OSX_ARCHITECTURES "ppc;i386;x86_64" CACHE STRING "Build architectures for Mac OS X" FORCE) + ENDIF() +ENDIF(APPLE) + +#LIBATOMIC #TODO +#ADD_LIBRARY(atomic_ops STATIC ) +#SET_TARGET_PROPERTIES(atomic_ops PROPERTIES COMPILE_FLAGS -DNO_DEBUGGING) + + +#LIBGC + +INCLUDE_DIRECTORIES(include) +INCLUDE_DIRECTORIES(libatomic_ops/src) + +SET(SRC alloc.c reclaim.c allchblk.c misc.c mach_dep.c os_dep.c + mark_rts.c headers.c mark.c obj_map.c blacklst.c finalize.c + new_hblk.c dbg_mlc.c malloc.c dyn_load.c typd_mlc.c ptr_chck.c + mallocx.c) +SET(LIBS) +OPTION(enable_threads "TODO" NO) +IF(enable_threads) + FIND_PACKAGE(Threads REQUIRED) + MESSAGE("Thread Model: ${CMAKE_THREAD_LIBS_INIT}" ) + INCLUDE_DIRECTORIES(${Threads_INCLUDE_DIR}) + SET(LIBS ${LIBS} ${Threads_LIBRARIES}) +ENDIF(enable_threads) + +OPTION(enable_handle_fork "Attempt to ensure a usable collector after fork()" ON) + +OPTION(enable_thread_local_alloc "Turn on thread-local allocation optimization" ON) + +OPTION(enable_parallel_mark "Parallelize marking and free list construction" ON) + +#IF(Threads_FOUND) +# ADD_DEFINITIONS("") +#ELSE +# MESSAGE("Parallel mark requires enable_threads ON" ) +#ENDIF(Threads_FOUND) + +#OPTION(enable_cplusplus "install C++ support" ON) +SET(SRC ${SRC} gc_cpp.cc) + +SET(_HOST ${CMAKE_HOST_SYSTEM_PROCESSOR}--${CMAKE_SYSTEM}) #FIXME missing the vendor field. +STRING(TOLOWER ${_HOST} HOST) +MESSAGE("HOST = ${HOST}") + +#Thread Detection. Relying on cmake for lib an includes. +#TODO check cmake detection +IF(CMAKE_USE_PTHREADS_INIT) + SET(SRC ${SRC} pthread_start.c pthread_support.c pthread_stop_world.c) + # Common defines for most POSIX platforms. + IF( HOST MATCHES .*-.*-aix.*|.*-.*-android.*|.*-.*-cygwin.*|.*-.*-darwin.*|.*-.*-.*freebsd.*|.*-.*-haiku.*|.*-.*-gnu.*|.*-.*-hpux11.*|.*-.*-irix.*|.*-.*-.*linux.*|.*-.*-nacl.*|.*-.*-netbsd.*|.*-.*-openbsd.*|.*-.*-osf.*|.*-.*-solaris.*) + ADD_DEFINITIONS("-DGC_THREADS -D_REENTRANT") + IF(enable_parallel_mark) + ADD_DEFINITIONS("-DPARALLEL_MARK") + ENDIF(enable_parallel_mark) + IF(enable_thread_local_alloc) + ADD_DEFINITIONS("-DTHREAD_LOCAL_ALLOC") + SET(SRC ${SRC} thread_local_alloc.c) + ENDIF(enable_thread_local_alloc) + MESSAGE("Explicit GC_INIT() calls may be required.") + ENDIF() + IF ( HOST MATCHES .*-.*-hpux11.*) + MESSAGE("Only HP/UX 11 POSIX threads are supported.") + ADD_DEFINITIONS("-D_POSIX_C_SOURCE=199506L") #TODO test -DVAR=value. Alternative is COMPILE_DEFINITIONS property + ENDIF() + IF ( HOST MATCHES .*-.*-hpux10.*) + MESSAGE("HP/UX 10 POSIX threads are not supported.") + ENDIF() + IF ( HOST MATCHES .*-.*-netbsd.*) + MESSAGE("Only on NetBSD 2.0 or later.") + ADD_DEFINITIONS("-D_PTHREADS") + ENDIF() + IF( HOST MATCHES .*-.*-aix.*|.*-.*-cygwin.*|.*-.*-freebsd.*|.*-.*-haiku.*|.*-.*-hpux11.*|.*-.*-irix.*|.*-.*-kfreebsd.*-gnu|.*-.*-.*linux.*|.*-.*-netbsd.*|.*-.*-openbsd.*|.*-.*-osf.*|.*-.*-solaris.*) + IF(enable_handle_fork) + ADD_DEFINITIONS("-DHANDLE_FORK") + ENDIF(enable_handle_fork) + ENDIF() + IF ( HOST MATCHES .*-.*-cygwin.*) + SET(SRC ${SRC} win32_threads.c) + ENDIF() + IF ( HOST MATCHES .*-.*-darwin.*) + IF(enable_handle_fork) + # The incremental mode conflicts with fork handling. + IF(enable_parallel_mark) + ADD_DEFINITIONS("-DHANDLE_FORK") + ENDIF(enable_parallel_mark) + ENDIF(enable_handle_fork) + SET(SRC ${SRC} darwin_stop_world.c) + #TODO + #darwin_threads=true + ENDIF() +ENDIF(CMAKE_USE_PTHREADS_INIT) + +IF(CMAKE_USE_WIN32_THREADS_INIT) + ADD_DEFINITIONS("-DGC_THREADS") + IF(enable_parallel_mark) + ADD_DEFINITIONS("-DPARALLEL_MARK") + IF(enable_thread_local_alloc) + ADD_DEFINITIONS("-DTHREAD_LOCAL_ALLOC") + SET(SRC ${SRC} thread_local_alloc.c) + ENDIF(enable_thread_local_alloc) + ENDIF() + ADD_DEFINITIONS("-DEMPTY_GETENV_RESULTS") #TODO test + SET(SRC ${SRC} win32_threads.c) +ENDIF(CMAKE_USE_WIN32_THREADS_INIT) + +OPTION(enable_gcj_support "Support for gcj" ON) +IF(enable_gcj_support) + ADD_DEFINITIONS("-DGC_GCJ_SUPPORT") + IF(enable_threads) + ADD_DEFINITIONS("-DGC_ENABLE_SUSPEND_THREAD") + ENDIF(enable_threads) + SET(SRC ${SRC} gcj_mlc.c) +ENDIF(enable_gcj_support) + +OPTION(enable_disclaim "Support alternative finalization interface" ON) +IF(enable_disclaim) + ADD_DEFINITIONS("-DENABLE_DISCLAIM") + SET(SRC ${SRC} fnlz_mlc.c) +ENDIF(enable_disclaim) + +OPTION(enable_java_finalization "Support for java finalization" ON) +IF(enable_java_finalization) + ADD_DEFINITIONS("-DJAVA_FINALIZATION") +ENDIF(enable_java_finalization) + +OPTION(enable_atomic_uncollectable "Support for atomic uncollectible allocation" ON) +IF(enable_atomic_uncollectable) + ADD_DEFINITIONS("-DGC_ATOMIC_UNCOLLECTABLE") +ENDIF(enable_atomic_uncollectable) + +OPTION(enable_gc_debug "Support for pointer back-tracing" NO) +IF(enable_gc_debug) + ADD_DEFINITIONS("-DDBG_HDRS_ALL -DKEEP_BACK_PTRS") + IF (HOST MATCHES ia64-.*-linux.*|i586-.*-linux.*|i686-.*-linux.*|x86-.*-linux.*|x86_64-.*-linux.*) + ADD_DEFINITIONS("-DMAKE_BACK_GRAPH") + ADD_DEFINITIONS("-DSAVE_CALL_COUNT=8") + SET(SRC ${SRC} backgraph.c) + ENDIF() + IF (HOST MATCHES i.86-.*-dgux.*) + ADD_DEFINITIONS("-DMAKE_BACK_GRAPH") + SET(SRC ${SRC} backgraph.c) + ENDIF() +ENDIF(enable_gc_debug) + +OPTION(enable_redirect_malloc "Redirect malloc and friends to GC routines" NO) +IF(enable_redirect_malloc) + IF(enable_gc_debug) + ADD_DEFINITIONS("-DREDIRECT_MALLOC=GC_debug_malloc_replacement") + ADD_DEFINITIONS("-DREDIRECT_REALLOC=GC_debug_realloc_replacement") + ADD_DEFINITIONS("-DREDIRECT_FREE=GC_debug_free") + ELSE(enable_gc_debug) + ADD_DEFINITIONS("-DREDIRECT_MALLOC=GC_malloc") + ENDIF(enable_gc_debug) + ADD_DEFINITIONS("-DGC_USE_DLOPEN_WRAP") +ENDIF(enable_redirect_malloc) + +OPTION(enable_munmap "Return page to the OS if empty for N collections" ON) +IF(enable_munmap) + ADD_DEFINITIONS("-DUSE_MMAP -DUSE_MUNMAP") +ENDIF(enable_munmap) + +OPTION(enable_large_config "Optimize for large heap or root set" NO) +IF(enable_large_config) + ADD_DEFINITIONS("-DLARGE_CONFIG") +ENDIF(enable_large_config) + +OPTION(enable_gc_assertions "Enable collector-internal assertion checking" NO) +IF(enable_gc_assertions) + ADD_DEFINITIONS("-DGC_ASSERTIONS") +ENDIF(enable_gc_assertions) + +OPTION(enable_checksums "Report erroneously cleared dirty bits" NO) +IF(enable_checksums) + IF(enable_munmap OR enable_threads) + MESSAGE("CHECKSUMS not compatible with USE_MUNMAP or threads") + ENDIF(enable_threads) + ADD_DEFINITIONS("-DCHECKSUMS") + SET(SRC ${SRC} checksums.c) +ENDIF(enable_checksums) + +ADD_LIBRARY( gc-lib STATIC ${SRC}) +SET_TARGET_PROPERTIES(gc-lib PROPERTIES + COMPILE_DEFINITIONS GC_NOT_DLL) +#TODO TARGET_LINK_LIBRARIES(... ... ${LIBS}) + +ADD_LIBRARY( gcmt-lib STATIC ${SRC}) +SET_TARGET_PROPERTIES(gcmt-lib PROPERTIES + COMPILE_DEFINITIONS GC_NOT_DLL) + +ADD_LIBRARY( gcmt-dll SHARED ${SRC}) + +IF(WIN32) + ADD_EXECUTABLE(cord cord/cordbscs.c cord/cordxtra.c + cord/tests/de.c cord/tests/de_win.c) + SET_TARGET_PROPERTIES(cord PROPERTIES WIN32_EXECUTABLE TRUE) + SET_TARGET_PROPERTIES(cord PROPERTIES + COMPILE_DEFINITIONS GC_NOT_DLL) + TARGET_LINK_LIBRARIES(cord gc-lib) + TARGET_LINK_LIBRARIES(cord gdi32) +ENDIF(WIN32) + +ADD_SUBDIRECTORY(tests) diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/ChangeLog b/unity-2019.4.24f1-mbe/external/bdwgc/ChangeLog new file mode 100644 index 000000000..579e32a39 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/ChangeLog @@ -0,0 +1,9539 @@ + +== [7.7.0] (development) == + +* Accept Android platform by both CMake and configure +* Access finalize_now atomically to avoid TSan warning without no-sanitize +* Acknowledge thread restart from suspend_handler (NetBSD) +* Add a sanity check that load_acquire and store_release are available +* Add AO primitives implementation to GC based on C11 atomic intrinsic +* Add assertion for suspend_ack_sem in start_world +* Add assertion to allocobj that live unmarked object cannot be reclaimed +* Add assertions about held lock when accessing all_bottom_indices +* Add assertions to ensure ADD_CALL_CHAIN is called holding the lock +* Add basic calculation of the total full-collection time +* Add check that gc_cpp operator delete is called (test_cpp) +* Add debug logging to new_thread about GC_threads hash table collisions +* Add GC prefix to _MSVC_DBG_H macro +* Add initial RISC-V support +* Add Makefile target to run all tests without test-driver +* Add test_atomic_ops to perform minimal testing of used atomic primitives +* Add two-argument alloc_size attribute to calloc_explicitly_typed (GCC) +* Align IRIX/OSF1_THREADS definition in gc_config_macros.h with gcconfig.h +* Allocate non-executable memory by default (CMake) +* Allow compilation of PROC_VDB code on Linux host (GC_NO_SYS_FAULT_H) +* Allow configure --with-libatomic-ops=none to use GCC atomic intrinsics +* Allow custom N_LOCAL_ITERS and ENTRIES_TO_GET values +* Allow gc_assertions enabling in CMake script +* Allow gc_debug, redirect_malloc, large_config options in CMake script +* Allow GC_NETBSD_THREADS_WORKAROUND macro manual definition +* Allow subthreadcreate_test to be compiled with zero NTHREADS +* Allow to turn on spin locking even if thread-local allocations are used +* Always include gc_atomic_ops.h unless threads are disabled +* Avoid 'Unexpected heap growth' in 64-bit multi-threaded gctest if n_tests=1 +* Avoid duplication of code handling pthreads case in configure +* Avoid potential data race during apply_to_each_object(reset_back_edge) +* Avoid potential data race during GC_dump execution +* Avoid potential race between malloc_kind and mark_thread_local_fls_for +* Avoid potential race between realloc and clear_hdr_marks/reclaim_generic +* Avoid potential race in print_static_roots called by dyld_image_add/remove +* Avoid potential race in SET_MARK_BIT_EXIT_IF_SET if parallel marking +* Avoid potential race when accessing size_map table +* Avoid potential race when storing oh_back_ptr during parallel marking +* Avoid SIGSEGV during GC_INIT on some Android devices +* Build only shared libraries by default (configure) +* Change pointer arguments of push_all[_eager]/conditional API to void* type +* Change type of hb_sz field (of hblkhdr) from size_t to word +* Check consistency of descr, adjust, clear arguments of GC_new_kind +* Check that GC_WIN32_PTHREADS is not specified for Cygwin +* Check thread_local is initialized before accessing thread_key +* Collapse multiple BCOPY_EXISTS macro definitions +* Collapse multiple NT_*_MAKEFILE scripts into a single NT_MAKEFILE +* Compile checksums.c only if --enable-checksums is given (configure) +* Convert .html files to Markdown format +* Convert code of .c files to valid C++ code +* Decide between memory unmapping and mprotect-based dirty bits at runtime +* Define ABORT() using _CrtDbgBreak (if available) on Windows host +* Define CLANG/GNUC_PREREQ macros to check gcc/clang minimum version +* Define GC_ASSERT(x) as C assert(x) for external clients of gc_inline.h +* Define GC_PREFETCH_FOR_WRITE to __builtin_prefetch in gc_inline.h (GCC) +* Define GC_THREADS instead of GC_x_THREADS in Makefiles +* Define macro to specify the environment file name extension (Win32/WinCE) +* Define static resend_lost_signals(), restart_all() in pthread_stop_world +* Detect sigsetjmp() availability by configure +* Determine whether to use compiler TLS for kFreeBSD at compile time +* Do not call BCOPY and BZERO if size is zero +* Do not call sem_getvalue in stop_world if one thread exists +* Do not call set_handle_fork(1) in gctest if pthread_atfork not supported +* Do not compile pcr_interface.c and real_malloc.c except by PCR-Makefile +* Do not declare dl_iterate_phdr as weak for kFreeBSD +* Do not print n_rescuing_pages value if incremental collections disabled +* Do not push cpsr and frame pointer on Darwin/arm and Darwin/arm64 +* Do not specify version info for test libraries (Automake) +* Do not use alternate thread library on Solaris +* Do not use asm in GC_pause +* Do not use system clock consistently if NO_CLOCK +* Do not use x86 asm in PUSH_CONTENTS_HDR for NaCl +* Document GC_BUILTIN_ATOMIC macro (and gc_atomic_ops private header file) +* Document STACK_NOT_SCANNED macro in gcconfig.h (Emscripten) +* Enable alternative finalization interface (DISCLAIM) in all makefiles +* Enable handle-fork and memory unmapping by default +* Enable mprotect-based incremental GC for Win64 (GCC) +* Expose API to control rate and max prior attempts of collect_a_little +* Expose API to control the minimum bytes allocated before a GC occurs +* Fix 'comparison of 255 with expr of type bool' error in gc_atomic_ops.h +* Fix 'doc' files installation folder +* Fix build of cord tests as C++ files (Makefile.direct) +* Group common defines for POSIX platforms in configure and CMake scripts +* Guard against USE_PTHREAD_LOCKS and USE_SPIN_LOCK are both defined +* Handle pthread restart signals loss if retry_signals +* Implement FindTopOfStack(0) for ARM and AArch64 (Darwin) +* Implement memory unmapping for Sony PS/3 +* Imply configure --single-obj-compilation if --disable-static +* Include malloc.c in extra/gc.c after include gc_inline.h +* Increase MAX_HEAP_SECTS (10 times) for large-config +* Initial support of Nintendo, Orbis, Sony PSP2, WinRT, Xbox One +* Initial support of TIZEN platform +* Install gc.3 man page instead of copying gc.man to doc folder (configure) +* Make extend_size_map() static (code refactoring) +* Match GC_FAST_MALLOC_GRANS formal and actual arguments where possible +* Move de_win compiled resource files to cord/tests +* Move pcr_interface.c, real_malloc.c to 'extra' folder +* New API function (GC_dump_named) to produce named dumps +* New API function (GC_is_incremental_mode) +* New API function (get_expl_freed_bytes_since_gc) +* New API function (get_size_map_at) to get content of size_map table +* New field (expl_freed_bytes_since_gc) in public prof_stats_s +* New macro ALWAYS_SMALL_CLEAR_STACK to avoid clearing large stack sections +* Pass CFLAGS_FOR_PIC value to CFLAGS in Makefile.direct +* Print time passed since GC initialization in GC_dump +* Public API (GC_deinit) to allow Win32 critical sections deletion +* Reduce probability of collision in threads hashtable for 64-bit targets +* Reduce the default MUNMAP_THRESHOLD value to 2 for Sony PS/3 +* Refactoring of USE_MMAP/USE_MMAP_ANON pairs definition in gcconfig.h +* Remove 'dist' target from Makefile.direct +* Remove a redundant check of __cplusplus in Symbian-specific .cpp files +* Remove Android-specific code in gcconfig.h for M68K +* Remove DGUX_THREADS macro which duplicates GC_DGUX386_THREADS (configure) +* Remove done_init static variable from fnlz_mlc.c +* Remove duplicate definition of ALIGNMENT macro for OpenBSD/arm +* Remove duplicated sample code in leak.md +* Remove EMX_MAKEFILE (add EMX support to Makefile.direct) +* Remove GC code fragment (which already merged) from README.Mac +* Remove GC_GNU_THREADS macro (HURD) +* Remove GENERAL_MALLOC internal macro +* Remove HIGH_BIT macro duplicating SIGNB +* Remove lint-specific code +* Remove Makefile KandRtest target (that supported K&R C compiler) +* Remove name of optional arguments of operator new and new[] in gc_cpp.h +* Remove notes that K&R C compiler is unsupported +* Remove redundant check that clear_fl_marks argument is non-null +* Remove redundant THREADS macro checks in alloc.c and gc_priv.h +* Remove unused USE_GENERIC macro definition and description +* Remove version info in 'de' cord test application +* Replace GC_MALLOC(sizeof T) with GC_NEW(T) in tests +* Replace GC_NO_RETRY_SIGNALS environment variable with GC_RETRY_SIGNALS=0 +* Run command passed to if_not_there directly from Makefile.direct +* Skip grungy_pages update when mark state invalid to speedup read_dirty +* Support configure --disable-thread-local-alloc option (similar for CMake) +* Support enable_checksums option in CMake script +* Support Haiku multi-threaded build by CMake +* Support threads for DragonFly in configure +* Turn on 'atomic uncollectable' functionality by default (CMake) +* Turn on gcj, disclaim and java finalization by default (CMake) +* Turn on incremental mode (if available) in disclaim_test +* Turn on parallel marker by default for all multi-threaded builds +* Update GC compilation and usage notes for Win32 +* Update shared libraries version info to differentiate against v7.6 +* Update top_index entry pointer only when the entry is constructed fully +* Use __builtin_expect in SIZET_SAT_ADD macro +* Use __declspec(allocator) for malloc-like prototypes (MS VS 2015+) +* Use __int64 instead of 'long long' in LONG_MULT if appropriate +* Use __thread keyword for Android NDK r12b+ Clang (arm) +* Use atomic load/store for the concurrently accessed variables in GC_lock +* Use C11 static_assert if available +* Use compiler atomic intrinsics by default if available (configure) +* Use EXPECT FALSE for mark_from code documented as executed rarely +* Use heap-allocated memory for local mark stack of non-marker thread +* Use HOST_ANDROID define instead of PLATFORM_ANDROID +* Use longjmp in fault_handler_openbsd if siglongjmp unavailable (OpenBSD) +* Use MARK_BIT_PER_GRANULE instead of MARK_BIT_PER_OBJ where appropriate +* Use same macro (NTHREADS) across all tests to specify number of threads +* Use sigsetjmp() in setjmp_t tool if available +* Use thread-local allocations for all multi-threaded builds +* Use THREAD_EQUAL consistently to compare pthread_t values +* Workaround Clang optimizer bug crashing clear_stack_inner on OS X 10.8 +* Workaround Thread Sanitizer (TSan) false positive warnings + + +== [7.6.6] 2018-04-20 == + +* Define GC_FREEBSD_THREADS and GC_ADD_CALLER macros for kFreeBSD +* Eliminate 'boolean result used in bitwise operation' cppcheck warning +* Eliminate 'there is pointer arithmetic with NULL' cppcheck warning +* Explicitly unblock GC signals on GC initialization +* Fix 'scope of var can be reduced' cppcheck err in enqueue_all_finalizers +* Fix 'undefined reference to __builtin_unwind_init' linker error (ArmCC) +* Fix arguments delimiter in pcr_interface.c (PCR) +* Fix assertion violation in DllMain of win32_threads +* Fix comment for debug_generic_malloc_inner[_ignore_off_page] +* Fix data race during apply_to_each_object(reset_back_edge) +* Fix dbg_mlc.c/o file name in documentation +* Fix gctest with musl libc on s390x +* Fix include gc_gcj.h in thread_local_alloc.c +* Fix man section number (3) +* Fix missing GC_generic_malloc_words_small implementation in new_gc_alloc.h +* Fix missing new-line in ABORT_ARG definition +* Fix missing SIGBUS handler setup for kFreeBSD +* Fix null dereference in print_callers on backtrace_symbols failure +* Fix null pointer dereference in get_private_path_and_zero_file (Symbian) +* Fix the collector hang when it is configured with --enable-gc-debug +* Fix thread_suspend fail for threads registered from key destructor (OS X) +* Fix type of local variables receiving result of PHT_HASH +* Fix typo in AIX macro name +* Fix typo in comment in specific.h +* Fix unbounded heap growth in case of intensive disappearing links usage +* Remove API symbols renaming in WCC_MAKEFILE +* Support Haiku/amd64 and Haiku/x86 hosts +* Support threads for DragonFly in configure +* Workaround 'address of auto-variable returned' cppcheck error +* Workaround gctest hang on kFreeBSD (if thread-local allocations are on) + + +== [7.6.4] 2018-01-26 == + +* Add note of set_free_space_divisor, set_warn_proc ABI change after gc-7.1 +* Change compiler invocation example in gc.man to use dynamic libgc +* Delete dont_ar_* build intermediate files on make clean (Makefile.direct) +* Do not declare dl_iterate_phdr as weak for DragonFly +* Fix 'cords' parallel build in Makefile.direct +* Fix 'undeclared identifier USRSTACK' compiler error on OpenBSD-6.2 +* Fix error code in abort message if sem_wait failed in start_world (NetBSD) +* Fix GC allocation mutex in child after a fork +* Fix global operator delete definition for C++14 in gc_cpp +* Fix last_reclaimed..gc_no interval comparison to threshold in unmap_old +* Fix libgc version which was changed in linkage breaking way +* Fix missing EOLn output in threadlibs tool +* Fix threadlibs tool to output '-lpthread' for DragonFly +* Prevent DATASTART redefinition for NaCl +* Remove obsolete advice about linking with _DYNAMIC=0 (Linux) + + +== [7.6.2] 2017-12-23 == + +* Add assertion that no hb_n_marks underflow occurs +* Add minimal testing of GC_MALLOC_[ATOMIC_]WORDS and GC_CONS (gctest) +* Add minimal testing of GC_set_bit (gctest) +* Add more cases to huge_test to cover sizes close to word-type maximum +* Add testing of new[]/delete[] (test_cpp) +* Adjust AO_HAVE_x check to match AO_fetch_and_add primitive variant used +* Adjust code indentation of calloc_explicitly_typed +* Align local_mark_stack in help_marker explicitly +* Allow custom TRACE_ENTRIES value +* Allow gctest and thread_leak_test with zero NTHREADS +* Avoid data race in finalized_count (gctest) +* Code refactoring of divide-by-HBLKSIZE occurrences +* Code refactoring of huge_test +* Code refactoring of tests/subthread_create regarding AO add primitive +* Compile thread_local_alloc only if multi-threaded build (Makefile.am) +* Delete preprocessor output on make clean (Makefile.direct) +* Disable implicit multi-threaded mode for Win32 to avoid LOCK crash +* Do not disable parallel mark for WRAP_MARK_SOME +* Do not enable mprotect-based incremental mode if unmapping is on (gctest) +* Do not install documentation if configure --disable-docs (new option) +* Do not use tkill (Android) +* Document base and size of objects allocated by finalized_malloc +* Document configure 'syntax error' issue in README +* Eliminate 'address of local variable returned' static analyzer warning +* Eliminate 'array vs singleton' code defect in typed_test (gctest) +* Eliminate 'assigned value never used' CSA warning in min_bytes_allocd +* Eliminate 'boolean result used in bitwise op' cppcheck false warning +* Eliminate 'C-style pointer casting' cppcheck style warnings in test +* Eliminate 'checking if unsigned variable is <0' cppcheck style warning +* Eliminate 'class member var with name also defined in parent' warning +* Eliminate 'comparison is always false' static analyzer warning in finalize +* Eliminate 'Condition 0==datastart always false' cppcheck warning (dyn_load) +* Eliminate 'condition is always true' cppcheck style warning +* Eliminate 'constructor with 1 argument is not explicit' cppcheck warning +* Eliminate 'CORD_*printf is never used' cppcheck style warnings (cordtest) +* Eliminate 'dereference of null' CSA false warning in array_mark_proc +* Eliminate 'function result not used' code defect in GC_mark_local +* Eliminate 'GC_collecting is set but never used' code defect (Win32) +* Eliminate 'GC_record_fault is never used' cppcheck style warning +* Eliminate 'integer shift by a negative amount' code defect in finalize +* Eliminate 'label not used' cppcheck false warnings in GC_mark_X +* Eliminate 'memory leak' code defect for scratch-allocated memory +* Eliminate 'memory leak' code defect in remove_specific +* Eliminate 'non-null arg compared to null' warning in toggleref_add (GCC) +* Eliminate 'non-reentrant function strtok called' cppcheck warning (POSIX) +* Eliminate 'possible integer underflow' code defect (cord-de) +* Eliminate 'potential overflow' static analyzer warning in test +* Eliminate 'printf format specifies type void*' GCC pedantic warnings +* Eliminate 'scope of variable can be reduced' cppcheck warnings +* Eliminate 'suspicious pointer subtraction' cppcheck warning (gc_cpp) +* Eliminate 'this statement may fall through' GCC warnings +* Eliminate 'unnecessary comparison of static strings' cppcheck warning +* Eliminate 'unsafe vsprintf is deprecated' compiler warning +* Eliminate 'unused formal parameter' compiler warnings in C++ code (MS VC) +* Eliminate 'unused variable' compiler warning in remove_all_threads_but_me +* Eliminate 'use of vulnerable sprintf' code defect in de_win test (cord) +* Eliminate 'value exceeds maximum object size' GCC warning in huge_test +* Eliminate 'value of CLOCK_TYPE unknown' cppcheck info message +* Eliminate 'value of DATASTART2 unknown' cppcheck info messages +* Eliminate 'value of GC_PTHREAD_EXIT_ATTRIBUTE unknown' cppcheck messages +* Eliminate 'value of GC_RETURN_ADDR_PARENT unknown' cppcheck info messages +* Eliminate 'value of NEED_FIXUP_POINTER unknown' cppcheck info messages +* Eliminate 'write to memory that was const-qualified' code analyzer warning +* Eliminate all 'scope of variable can be reduced' cppcheck style warnings +* Eliminate CSA warning about incorrect cast applied to HBLK_OBJS +* Eliminate CSA warning about narrowing cast in CleanUp of test_cpp +* Eliminate CSA warning of non-virtual destructor in test_cpp base class +* Eliminate CSA warning of staticroot that can be a local variable (tests) +* Eliminate CSA warning of unmodified non-const static var (disclaim_test) +* Eliminate redundant local variable in register_finalizer +* Eliminate TSan (Thread Sanitizer) warnings in gctest +* Eliminate UBSan warning of overflow during descr subtraction in mark_from +* Eliminate unreachable PROC/DEFAULT_VDB GC_printf calls in gctest main() +* Eliminate unsigned fl_builder_count underflow in mark_thread +* Enable GC_is_tmp_root for all platforms +* Execute more single-threaded GC tests by CMake +* Expand tabs to spaces in de_win.rc (tests) +* Export GC_dump_finalization/regions() +* Export GC_is_tmp_root() and GC_print_trace[_inner]() +* Export GC_print_free_list() +* Fix '32-bit value shift followed by expansion to 64-bit' code defect +* Fix 'GC_written_pages never read' code defect (GWW_VDB) +* Fix 'label cannot be reached' static analyzer warning in disclaim_test +* Fix 'size of tv is unknown' error in brief_async_signal_safe_sleep (musl) +* Fix 'syntax error' reported by cppcheck for mach_dep +* Fix 'unknown type name GC_INNER' compilation error (FreeBSD) +* Fix 'variable assigned a value that is never used' cppcheck style warnings +* Fix 'void pointers in calculations: behavior undefined' cppcheck warning +* Fix assertion violation about disabled cancel in try_to_collect_inner +* Fix atomic_ops build in Makefile.direct for Solaris +* Fix Clang static analyzer warning about not found gc_priv.h in extra files +* Fix compilation error in get_main_stack_base (Emscripten) +* Fix compilation for winpthreads if HANDLE_FORK +* Fix compilation if configured with --enable-werror on OS X +* Fix cord/de build in Makefile.direct (Linux) +* Fix data race in a list referenced by A.aa (gctest) +* Fix data race in collectable_count (gctest) +* Fix data race in do_local_mark when comparing active_count to helper_count +* Fix data race in GC_suspend/resume_thread +* Fix data race in last_stop_count access (suspend_handler_inner) +* Fix data race in make_descriptor when setting explicit_typing_initialized +* Fix data race in mark_thread when updating mark_no +* Fix data race when getting object size in explicitly-typed allocators +* Fix deadlock in GC_suspend_thread +* Fix gctest failure for Darwin if CPPCHECK is defined +* Fix lack of barriers to synchronize memory for suspend_handler +* Fix marking of disclaim-reachable objects in the incremental mode +* Fix message of VDB implementation used if MPROTECT_VDB+GWW_VDB (gctest) +* Fix missing started_thread_while_stopped call from mark_some if GCC/Clang +* Fix null dereference in GC_stack_range_for if not DARWIN_DONT_PARSE_STACK +* Fix page calculation in checksums +* Fix parallel build in Makefile.direct +* Fix test_cpp and c++ parallel build in Makefile.direct +* Fix typo in comment of GC_mark_some +* Fix typos in cdescr.html and README.sgi +* Make GC_INIT optional for clients even if thread-local allocations enabled +* Match uclinux pattern in configure +* Move conditional GC_need_to_lock setting to gc_locks.h (refactoring) +* Move README.QUICK from DOC_FILES to OTHER_FILES in Makefile.direct +* New API function (GC_is_init_called) to check if BDWGC is initialized +* New target (check-cpp) in Makefile.direct +* Prevent abort in register_data_segments for Symbian and Emscripten +* Prevent multiple 'Caught ACCESS_VIOLATION in marker' per collection +* Print realloc_count value in gctest +* Put invariant name in quotes to make mark_state comments clearer +* Refine configure messages when checking for compiler option support +* Remove extraneous semicolons after AC_MSG_WARN (configure) +* Remove page_was_dirty and remove_protection duplicate definitions +* Remove unnecessary type casts of printf arguments to unsigned long +* Remove unused ALIGN_DOUBLE, USE_GENERIC_PUSH_REGS macros (TILE-Gx/Pro) +* Rename 'test' to 'check' target in Makefile.direct +* Replace deprecated rewind to fseek in cordxtra +* Report gcc/clang pedantic warnings (configure) +* Skip thread suspend/resume API testing for Tru64 (OSF1) +* Support AddressSanitizer (Clang/GCC) and MemorySanitizer (Clang) +* Support GC_init (and get_stack_base) from non-main thread on FreeBSD/NetBSD +* Suppress 'tainted string passed to vulnerable operation' false defects +* Suppress 'taking address of label non-standard' GCC/Clang pedantic warning +* Test GC initialization from non-main thread on FreeBSD and NetBSD +* Test GCJ object creation with length-based descriptor (gctest) +* Update comment in finalized_disclaim to match FINALIZER_CLOSURE_FLAG +* Update README regarding make cords with Makefile.direct +* Update README to use autogen.sh on build from the source repository +* Update shared libraries version info to differentiate against v7.4 +* Use mprotect instead of mmap in GC_unmap() on Cygwin +* Use same style of include gc.h in documentation +* Workaround '!GC_page_size is always false' cppcheck style warning +* Workaround '#error' cppcheck error messages +* Workaround '32-bit value shift by >31 bits is undefined' cppcheck warnings +* Workaround 'array compared to 0', 'untrusted loop bound' false defects +* Workaround 'bad address arithmetic' static analysis tool false positive +* Workaround 'checking if unsigned value is negative' cppcheck warning +* Workaround 'checking unsigned value is negative' code defect in mark_from +* Workaround 'comparison of identical expressions' false code defects +* Workaround 'Condition 0!=GETENV() is always false' cppcheck style warnings +* Workaround 'condition is always false' cppcheck warning in get_next_stack +* Workaround 'condition is always true' cppcheck style warnings in GC_init +* Workaround 'function is never used' cppcheck style warnings +* Workaround 'insecure libc pseudo-random number generator used' code defect +* Workaround 'int shift by negative amount' false code defect in finalize +* Workaround 'local variable size too big' static analyzer warning +* Workaround 'memory leak: result' cppcheck false error (POSIX) +* Workaround 'null pointer dereference' false positive in push_next_marked +* Workaround 'obsolescent bcopy, bzero called' cppcheck warnings (POSIX) +* Workaround 'obsolescent usleep called' cppcheck warning (POSIX) +* Workaround 'obsolete function alloca() called' cppcheck warnings +* Workaround 'passing untyped NULL to variadic function' cppcheck warning +* Workaround 'pointer used before comparison to null' code defect (pthread) +* Workaround 'possible null pointer dereference' cppcheck warnings +* Workaround 'potential multiplication overflow' code defect in de_win (cord) +* Workaround 'redundant assignment of *result to itself' cppcheck warning +* Workaround 'resource leak' false positives in alloc_MS, bl/envfile_init +* Workaround 'same expression on both sides of ==' cppcheck style warning +* Workaround 'same expression on both sides of OR' cppcheck style warning +* Workaround 'struct member is never used' cppcheck style warnings +* Workaround 'tainted int used as loop bound' static analysis tool warning +* Workaround 'Uninitialized variable' cppcheck errors +* Workaround 'unused variable' cppcheck style warnings +* Workaround 'va_list used before va_start' cppcheck error in cord +* Workaround 'value of macro unknown' cppcheck info messages +* Workaround 'value of REDIRECT_MALLOC/FREE unknown' cppcheck info messages +* Workaround 'value of SIGBUS unknown' cppcheck info messages +* Workaround 'value of WINAPI unknown' cppcheck info messages +* Workaround 'variable hides enumerator with same name' cppcheck warnings +* Workaround 'variable reassigned before old value used' cppcheck warnings +* Workaround 'waiting while holding lock' code defect in stop_world (Unix) +* Workaround false 'uninitialized var use' code defect (initsecondarythread) + +Also, includes 7.4.6 changes + + +== [7.6.0] 2016-08-02 == + +* ABORT_ARGn log details at INFO level (Android) +* Add 'pragma message' to gc.h to detect inconsistent WIN64/_WIN64 (MS VC) +* Add API function to calculate total memory in use by all GC blocks +* Add API function to set/modify GC log file descriptor (Unix) +* Add alloc_size attribute to GC_generic_malloc +* Add alt-stack registration support +* Add assertion for GC_new_kind boolean arguments +* Add assertion on lock status to GC_alloc_large and its callers +* Add build scripts for VC 9 (Win32/64) +* Add build system plumbing for building with -Werror +* Add incremental GC support for Darwin/arm64 +* Add profiling callback events to indicate start/end of reclaim phase +* Add support for enumerating the reachable objects in the heap +* Add toggle-ref support (following Mono GC API) +* Added instructions to README.md for building from git +* Adjust code indentation of malloc/calloc/str[n]dup +* Allow fork() automatic handling on Android with API level 21+ +* Allow specific TLS attributes for GC_thread_key +* Allow thread local allocations from within pthread TLS destructors +* Allow to force GC_dump_regularly set on at compilation +* Altera NIOS2 support +* Change 'cord' no-argument functions declaration style to ANSI C +* Check DATASTART is less than DATAEND even assertions off +* Check for execinfo.h by configure +* Code refactoring of GC_push_finalizer/thread/typed_structures +* Code refactoring regarding 'data start' definition for FreeBSD +* Consistently set type of DATASTART/END to ptr_t (code refactoring) +* Consistently use int[] type for '_end' symbol (code refactoring) +* Consistently use outermost parentheses for DATASTART/END, STACKBOTTOM +* Define GC_LINUX_THREADS, NO_EXECUTE_PERMISSION in configure for NaCl +* Define ROUNDUP_PAGESIZE, ROUNDUP_GRANULE_SIZE macros (code refactoring) +* Define public GC_GENERIC_OR_SPECIAL_MALLOC and GC_get_kind_and_size +* Do no declare kernel_id field of GC_Thread_Rep for 64-bit Android +* Do not allow SHORT_DBG_HDRS if KEEP_BACK_PTRS or MAKE_BACK_GRAPH +* Do not warn of missing PT_GNU_RELRO segment when custom DSO filter used +* Document GC_register_my_thread returned value +* Dump the block information in CSV format +* Eliminate redundant *flh check for null in GC_allocobj +* Enable atomic-uncollectable in operator new in gc_cpp.h +* Enable build with musl libc +* Enable gc.h inclusion by client without implicit include windows.h (Win32) +* Enable huge_test for Win64 (and LLP64 target) +* Enable thread-local storage for Android Clang +* Enable thread-local storage usage for GC_malloc/calloc_explicitly_typed +* Export GC_push_all_eager, GC_push_finalizer_structures +* Fix 'arg parameter might be clobbered by setjmp' compiler warning +* Fix assertion in GC_mark_from for non-heap regions +* Fix compilation for Android clang/arm with bfd linker +* Fix integer shift undefined behavior in GC_init_explicit_typing +* Fix missing new-line and redundant trailing dot in WARN messages +* Fix STACKBOTTOM for Solaris 11/x86 +* Fix tag collision between ENABLE_DISCLAIM and KEEP_BACK_PTRS +* Fix unchecked fork() result in gctest (Unix, Cygwin) +* Fix user-defined signals drop by marker threads +* Fix various typos in comments and documentation +* FreeBSD/arm support improvement +* GC_make_descriptor code refactoring (eliminate two local variables) +* GC_malloc[_atomic] global and thread-local generalization with kind +* GC_malloc_[atomic_]uncollectable generalization +* GC_scratch_alloc code refactoring (and WARN message improvement) +* Group all compact fields of GC_arrays to fit in single page +* Handle load_segs overflow in register_dynlib_callback gracefully +* Harmonize OSX/iOS configuration; enable compiling for iPhone simulator +* Implement event callbacks for profiling (following Mono GC API) +* Implement the finalization extension API +* Implement thread suspend/resume API (Linux threads only) +* Improve documentation for disappearing links in gc.h +* Make heap growth more conservative after GC_gcollect_and_unmap call +* Mark fo_head, finalize_now with a single GC_push_all call (refactoring) +* Move MessageBox invocation code from GC_abort to a separate routine (Win32) +* NaCl/arm initial support; NaCl runtime fixes for other CPUs +* New macro (GC_ALWAYS_MULTITHREADED) to set multi-threaded mode implicitly +* New macro (NO_WINMAIN_ENTRY) to prefer main() instead of WinMain in test +* New macro (REDIRECT_MALLOC_IN_HEADER) to enable source-level redirection +* Process all PT_LOAD segments before PT_GNU_RELRO segments (Glibc) +* Re-implement GC_finalized_malloc using GC_malloc_kind +* Refactoring of android_thread_kill/pthread_kill calls +* Refactoring of GC_Xobjfreelist (use single array to keep free lists) +* Refactoring of thread-local *_freelists (use single array of free lists) +* Refine description in README how to build from source repository +* Refine GC_free_space_divisor comment regarding its initial value +* Reformat code of gc_cpp.cc/h +* Remove 'opp' local variable in GC_malloc_X +* Remove 'sig' argument of GC_suspend_handler_inner (code refactoring) +* Remove code commented out by 'ifdef UNDEFINED' +* Remove hb_large_block field (use 1 extra bit of hb_flags instead) +* Remove obsolete BACKING_STORE_ALIGNMENT/DISPLACEMENT macros for Linux/IA64 +* Remove redundant casts in GC_generic_or_special_malloc and similar +* Remove unsupported FreeBSD/ia64 case from gcconfig.h file +* Remove unused GC_gcjdebugobjfreelist +* Rename ATOMIC_UNCOLLECTABLE to GC_ATOMIC_UNCOLLECTABLE +* Replace non-API occurrences of GC_word to word (code refactoring) +* Return GC_UNIMPLEMENTED instead of abort in GC_get_stack_base (OS/2) +* Show WoW64 warning message if running 32-bit on Win64 (enabled by macro) +* Standalone profiling callback for threads suspend/resume +* Support (add machine description for) TILE-Gx and TILEPro targets +* Support build for Android 64-bit (arm64, mips64, x86_64) +* Support FreeBSD/aarch64, FreeBSD/mips +* Support iOS7 64-bit (AArch64) and iOS8+ 32/64-bit (Darwin) +* Support MinGW build in scripts +* Turn off sigsetjmp workaround for Android/x86 starting from NDK r8e +* Use magic header on objects to improve disclaim_test +* Workaround 'sa_sigaction member missing' compiler error (Android/x32) +* Workaround 'unresolved __tls_get_addr' error for Android NDK Clang +* Workaround a bug in winpthreads causing parallel marks deadlock (MinGW) + +Also, includes 7.4.4 changes + + +== [7.4.12] 2018-04-19 == + +* Define GC_FREEBSD_THREADS and GC_ADD_CALLER macros for kFreeBSD +* Fix comment for debug_generic_malloc_inner[_ignore_off_page] +* Fix gctest with musl libc on s390x +* Fix missing new-line in ABORT_ARG definition +* Fix null pointer dereference in get_private_path_and_zero_file (Symbian) +* Fix type of local variables receiving result of PHT_HASH +* Remove API symbols renaming in WCC_MAKEFILE + +Also, includes 7.2k changes + + +== [7.4.10] 2018-01-22 == + +* Fix error code in abort message if sem_wait failed in start_world (NetBSD) +Also, includes 7.2j changes + + +== [7.4.8] 2017-12-22 == + +* Eliminate 'this statement may fall through' GCC warnings +* Eliminate 'value exceeds maximum object size' GCC warning in huge_test +* Fix data race in make_descriptor when setting explicit_typing_initialized +* Fix marking of disclaim-reachable objects in the incremental mode +* Update comment in finalized_disclaim to match FINALIZER_CLOSURE_FLAG +Also, includes 7.2i changes + + +== [7.4.6] 2017-10-26 == + +* Add configure --enable-gcov option (enable code coverage analysis) +* Add configure check whether to define NO_GETCONTEXT +* Adjust GC_memalign comment +* Allow HAVE_DL_ITERATE_PHDR to be defined by client (musl) +* Allow PKG_CHECK_MODULES in configure.ac to be commented out easily +* Avoid busy waiting in mark_thread while GC_parallel is false +* Better document minimum value of size argument for typed allocations +* Change type of THREAD_TABLE_INDEX result to int in win32_threads.c +* Consistently use 'msec' instead of 'ms' in comments in pthread_support +* Do not define amiga_get_mem, MacTemporaryNewPtr unless really used (extra) +* Do not produce .tar.bz2 distribution file (configure) +* Do not require libatomic_ops for single-threaded builds (configure) +* Do not warn of missing PT_GNU_RELRO segment when custom DSO filter used +* Document GWW_VDB in gcdescr.html +* Eliminate 'cast to void* from int' compiler warnings (Darwin/x64) +* Eliminate 'conditional expression is always true' code defect in GC_init +* Eliminate 'FP divide-by-zero' static analyzer warning +* Eliminate 'incompatible function pointer' warning in mark_some (MinGW/x86) +* Eliminate 'ISO C forbids an empty translation unit' GCC pedantic warning +* Eliminate 'ISO C forbids object to function pointer conversion' warning +* Eliminate 'locally defined symbol imported' MS linker warnings (cord) +* Eliminate 'null dereference' code defect warning in register_finalizer +* Eliminate 'possible loss of data' compiler warnings (MS VC) +* Eliminate 'printf format specifier mismatch' compiler warning (tools) +* Eliminate 'type defaults to int in declaration' warning (REDIRECT_MALLOC) +* Eliminate 'value stored is never read' warning of Clang static analyzer +* Eliminate duplicate log messages in GC_mark_from +* Eliminate most of collisions in GC_threads on Linux/x64 +* Ensure GC initialized when atfork_prepare is called by client +* Fix 'arg parameter might be clobbered by setjmp' compiler warning +* Fix 'bogus LR' detection in FindTopOfStack (Darwin) +* Fix 'execvp argument incompatible pointer type' compiler warning (tools) +* Fix 'GetVersion deprecated' compiler warning in os_dep (MS VC) +* Fix 'incompatible pointer' compiler warning in GC_init_dyld (OS X 64-bit) +* Fix 'incompatible ptr-to-int conversion' compiler warning in push_all_stack +* Fix 'ISO C90 does not support %lf, %lg gnu_printf formats' GCC warnings +* Fix 'ISO C90 forbids mixed declarations and code' compiler warning +* Fix 'missing libc-version.h' build error (uClibc/x86[_64]) +* Fix 'replacement operator delete cannot be inline' GCC warning (Cygwin) +* Fix 'variable unused' compiler warning in FirstDLOpenedLinkMap +* Fix 'zero-size array is extension' Clang warning in os_dep (Linux/x86) +* Fix (adjust) GC_scratch_alloc actual argument type +* Fix deadlock in GC_help_marker caused by use of mark_cv of parent process +* Fix finalize.c compilation in 'strict ANSI' mode +* Fix GC shared library tests failure related to dl_iterate_phdr (musl) +* Fix gc.h compliance to strict ANSI (pthreads) +* Fix GC_bytes_allocd incrementation in case of allocation failure +* Fix GC_jmp_buf multiple definition +* Fix GC_noop6 definition to avoid its calls to be optimized away +* Fix gctest failure if PARALLEL_MARK (musl) +* Fix gctest thread stack overflow (musl-gcc) +* Fix initsecondarythread_test runtime failure if GC compiled w/o threads +* Fix lack of 2 trailing zeros in _MSC_VER numbers +* Fix local variable declarations in disclaim_bench +* Fix missing #error pragma +* Fix missing .exe for disclaim test filenames in Makefile (MinGW) +* Fix missing atomic/[un]collectable/realloc_count increments in gctest +* Fix missing new-line and redundant trailing dot in WARN messages +* Fix missing new-line at format strings end in subthread_create test +* Fix mixed include of GC public header and gc_priv.h in disclaim bench/test +* Fix potential overflow in decrement when computing GC_markers_m1 +* Fix printf format specifiers in extra files (cppcheck warnings) +* Fix pthread_start compilation if single-obj-compilation (Linux) +* Fix register_finalizer call in disclaim_bench for GC_DEBUG +* Fix static assertion violation in LONG_MULT for 64-bit targets +* Fix tag collision between ENABLE_DISCLAIM and KEEP_BACK_PTRS +* Fix thread id leaks in subthread_create and threadkey_test +* Fix threaded tests runtime crash if GC_NO_THREAD_REDIRECTS supplied +* Fix tools/setjmp_t to prevent nested_sp inlining +* Fix typo in CHECK_GCLIB_VERSION name (test) +* Fix typos in comments/documentation (ews4800, extend_size_map, push_roots) +* Fix unchecked fork() result in gctest (Unix, Cygwin) +* Improve detection of internal libatomic_ops (configure) +* Move libraries version info to the beginning of Makefile.am +* Prevent abort in register_data_segments for Symbian +* Process all PT_LOAD segments before PT_GNU_RELRO segments (Glibc) +* Refine Makefile.direct comment about multi-threaded GC build +* Refine README about library source downloading +* Refine should_invoke_finalizers documentation +* Remove all generated files by NT_X64_THREADS_MAKEFILE 'clean' target +* Remove non-existent configure option in simple_example.html +* Replace C++ style comments to C ones, remove commented out code (extra) +* Support CFLAGS_EXTRA to pass extra user-defined compiler flags (configure) +* Support CFLAGS_EXTRA when checking for inline and dladdr (configure) +* Suppress 'tainted string passed to vulnerable operation' false defects +* Suppress MS VC warnings about unused param, const condition (NT_MAKEFILE) +* Update bdwgc mailing list online archive link in documentation +* Update shared libraries version info to differentiate against v7.2 +* Use AC_DEFINE for defining NO_GETCONTEXT in configure +* Workaround 'index out of bounds' UBSan false warning in push_marked +* Workaround 'mmap() resource handle leak' static analyzer warning +* Workaround 'redundant assignment of *result to itself' cppcheck warning +* Workaround 'resource leak' error reported by cppcheck (tools, test) +Also, includes 7.2h changes + + +== [7.4.4] 2016-05-25 == + +* Allow GC_FAST_MALLOC_GRANS() multiple use in a function +* Also enable the TSX workaround for i386 (Linux) +* Avoid unstructured procfs on Solaris +* Change cord/de main() declaration style from K-R to ANSI C +* Change no-argument functions declaration style to ANSI C (cord) +* Do not include sigcontext.h and asm/sigcontext.h +* Eliminate 'divide by zero' compiler warning in cordtest +* Eliminate warning about 64-bit pointer-to-int cast (Win64/pthreads-w32) +* Eliminate warnings detected by Cppcheck in cord de[_win] +* Fix 'comparison of non-null parameter is always false' warning (Clang) +* Fix 'CORD_iter5 unused result' code defect in cordxtra +* Fix 'GC_generic_malloc_inner_ignore_off_page not used' compiler warning +* Fix 'implicit declaration of vsnprintf' GCC warning (if strict ANSI mode) +* Fix 'signed-to-bigger-unsigned value assignment' in GC_init_size_map +* Fix 'signed-to-bigger-unsigned value assignment' warning for hb_map +* Fix 'signed-to-bigger-unsigned value assignment' warning in GC_setpagesize +* Fix 'statement unreachable' compiler warning in GC_mark_from +* Fix 'statement unreachable' compiler warning in memalign +* Fix 'unused label' compiler warning in cord/de +* Fix 'value truncated' compiler warning in CORD_cat (MS VC) +* Fix 'variable unused' warning in GC_save_callers +* Fix 'visibility attribute not supported' GCC warning (IBM AIX) +* Fix CMake warning about CMP0054 by unquoting instances of HOST +* Fix Cygwin64 build +* Fix GC_REALLOC to call GC_FREE if new size is zero and pointer is non-NULL +* Fix Makefile.direct for Cygwin +* Fix __alloc_size__ availability detection (Clang) +* Fix abort message in GC_move_long_link +* Fix and code refactoring of lock elision workaround (Linux/x64) +* Fix assertion on mark_lock_holder for non-unique NUMERIC_THREAD_ID +* Fix data race in GC_init_explicit_typing +* Fix gc.mak regarding msvc_dbg and test (MSVC) +* Fix missing error handling of pthread_attr_init/getstacksize +* Fix missing error handling of pthreads_mutex_init and cond_wait +* Fix missing numeric casts in cord +* Fix potential left shift overflows in finalize.c (64-bit targets) +* Fix pthreads-win32 name in comments and documentation +* Fix setup_mark_lock missing prototype +* Fix unchecked fcntl() result +* Fix unchecked pointer dereference in check_ints (gctest) +* Fix unchecked pthread_join() result in threadkey_test +* Fix unchecked sigdelset() result in pthread_support +* Fix undefined PTRFREE/NORMAL in gc_inline.h +* Prefix PREFETCH_FOR_WRITE with GC_ as used in gc_inline.h public header +* Relax mark_mutex attribute needed to disable elision (Linux/x64) +* Remove (deprecate) TODO file +* Remove code duplication in GC_realloc +* Remove duplicate new-line in OUT_OF_MEMORY message (cord) +* Remove references to missing linux_threads.c from documentation +* Revert "Move asm machine-dependent files to 'src' folder" (partly) +* Support Android API level 21 +* Update compiler options in gc.mak (Win32) +* Use mmap instead of sbrk (Hurd) +* Workaround 'comparison is always false' GCC warning in GC_FAST_MALLOC_GRANS +* Workaround 'identical expr on both sides of bitwise op' warning +* Workaround Linux NTPL lock elision bug +* Workaround false warning about unreachable code path +* Workaround invalid '_end' symbol on Android clang 3.5+ +Also, includes 7.2g changes + + +== [7.4.2] 2014-06-03 == + +* Add config option to use STGRTMIN-based signals for thread suspend/resume +* Allow parallel mark to be enabled on powerpc-linux systems +* Check for Fujitsu compiler in builtin_unwind logic (enable FX10/K-Computer) +* Fix 'Array subscript is above array bounds' GCC warning in GC_new_kind/proc +* Fix 'attribute declaration must precede definition' warning (clang-3.1) +* Fix (enable) Cygwin-64 build +* Fix GC_finalized_malloc failure on disclaim_test +* Fix GC_sig_suspend initialization when non-constant SIGRTMIN used +* Fix MS VC redefinition warning for functions declared with GC_ATTR_MALLOC +* Fix TEXT() usage for concatenated strings in GC_CreateLogFile (Win32) +* Fix data roots registration for Android/x86 and NDK ARM 'gold' linker +* Fix find stackbottom on BlueGene P/Q systems +* Fix machdep .lo files path in configure (SPARC, IA-64) +* Fix ok_init assignment (missing cast) in GC_new_kind_inner +* Fix typos in names in AUTHORS and ChangeLog files +* Remove barrett_diagram file duplicated by tree.html +* Remove non-existing DISCARD_WORDS from GC data structure ASCII diagram +* Restore contribution information for ancient releases in ChangeLog +Also, includes 7.2f changes + + +== [7.4.0] 2013-11-17 == + +* Add 'bytes reclaimed' counters to public GC_prof_stats_s +* Add AArch64 (64-bit ARM) target support +* Add GC_LONG_REFS_NOT_NEEDED ifdefs to exclude long link functionality +* Add GC_get_prof_stats[_unsafe]() to GC public API +* Add GC_push_all/conditional() to GC public API +* Add assertion on number_of_objs to GC_extend_size_map +* Add assertion to GC_enable() ensuring no counter underflow +* Add assertion to LOCK definition that lock is not already held +* Add assertion to LONG_MULT and remove useless assert in PUSH_CONTENTS_HDR +* Add double-lock assertion to GC_acquire_mark_lock +* Add manual POSIX fork handling support (Android) +* Add note about 'pkg-config' solving problem with autoconf 2.68 or older +* Add public GC_set/get_abort_func to replace default GC_on_abort +* Add public GC_start_mark_threads() to allow parallel marker in fork child +* Add public setter and getter for GC_push_other_roots +* Add support of Android logger +* Add tests for GC_register/move/unregister_long_link +* Add thread suspend/resume signals public setters (POSIX threads) +* Added long weakref support +* Adjust GC_dont_expand/gc/precollect and GC_print_stats type to match gc.h +* Adjust README.md title and references to doc .html files in it +* Adjust build scripts to enable additional test library in staticrootstest +* Adjust logged messages in start_mark_threads and GC_thr_init +* Adjust printf format specifiers in GC_print_trace +* Allow not to rely on __data_start value (Linux) +* Allow pthread_kill error code logging in GC_suspend/resume (debugging) +* Allow to compile GC_inner_start_routine aside from extra/gc.c +* Allow to omit libc atexit() call +* Avoid LOCK/UNLOCK hard-coding in gc_locks.h for PS3 target +* Better document GC_warn_proc in gc.h +* Call GC_on_abort (with NULL argument) on exit(1) +* Call GC_stats/verbose_log_printf instead of GC_log_printf if print_stats +* Change policy regarding version numbers ("micro" part instead of "alpha") +* Changed C99-style designated init of GC_dl_hashtbl struct to use C89-style +* Check GC_base result in GC_print_all_smashed_proc +* Check that SIG_SUSPEND and SIG_THR_RESTART are different (Pthreads) +* Check traceable_allocator.allocate result before dereference in test_cpp +* Code refactoring of GC_x_printf (move shared code to macro) +* Convert readme to markdown +* Default to use libc_stack_end in single-threaded GC on glibc targets +* Define GC_VSNPRINTF internal macro in misc.c (code refactoring) +* Define functions in darwin_semaphore.h as inline instead of static +* Define old_bus_handler static variable only if used (Unix) +* Detect dladdr() presence by configure +* Disable find-leak GC_gcollect on GC abnormal EXIT +* Do not define _setjmp/_longjmp macros in mach_dep.c +* Do not duplicate android_log_write output to GC log file (Android) +* Do not include sigcontext.h if NO_SIGCONTEXT_H (Linux) +* Do not set GC_lock_holder by call_with_alloc_lock if assertions disabled +* Do not use pthread_getattr_np if NO_PTHREAD_GETATTR_NP specified +* Elaborate comment on dependencies in autogen.sh +* Eliminate 'cast from int to pointer' warning in GC_exclude_static_roots +* Eliminate 'missing exception specification' warning in gc_cpp.cc (Clang) +* Eliminate 'uninitialized variable use' warning in test_printf (cord) +* Eliminate 'unused result' compiler warning in main() of test_cpp +* Eliminate 'unused value' compiler warning in GC_stop_world (Pthreads) +* Eliminate 'unused variable' compiler warning in start_mark_threads (HP/UX) +* Eliminate Clang warning for GC_pthread_exit attribute +* Eliminate GCC warning about uninitialized 'hhdr' in GC_allochblk_nth +* Eliminate GCC warning in GC_get_main_stack_base (OpenBSD) +* Eliminate GCC warnings in setjmp_t.c, test_cpp and cord 'de' app +* Eliminate GC_first_nonempty atomic value reload in GC_mark_local assertion +* Eliminate SIGBUS-related dead code in GC_write_fault_handler (Linux) +* Eliminate warning and simplify expression in GC_init_explicit_typing +* Enable 'force GC at every GC_malloc' debug-related functionality +* Enable on-demand debug logging in GC_FindTopOfStack (Darwin) +* Enable prefetch operations by default (GCC 3.0+) +* Enable staticrootstest for the case of GC shared library build +* Enable thread-local allocation support for Clang on Cygwin +* Explicitly specify that Darwin, Linux and Solaris platforms have dladdr +* Fix ABORT definition for mingw32ce (WinCE) +* Fix AM_CONFIG_HEADER in configure for autoconf-2.69-1 +* Fix GC_CreateThread and GC_beginthreadex definition for Cygwin +* Fix GC_INIT_CONF_ROOTS in gc.h for Android +* Fix GC_INLINE definition to comply with ISO C90 standard (GCC) +* Fix GC_remove_all_threads_but_me for Android (fork support) +* Fix debug_register_displacement calls from GC_debug_generic_malloc_inner +* Fix dyn_load.c compilation for Android 4.3 +* Fix make disclaim_test to link with new GNU ld linking rules +* Improve GC error printing atomicity in GC_debug_X and GC_print_obj +* Improve GC output atomicity in GC_print_obj, GC_print_all_errors +* Improve debug-only messages of add/remove_roots and init_linux_data_start +* Improve fork test logging in gctest +* Improve logged messages about heap size and usage +* Improve logging for Android differentiating messages by log level +* Improve staticrootstest (add global data to library, add lib w/o GC_INIT) +* Improve staticrootstest checks (tests) +* Include "config.h" instead of "private/config.h" on HAVE_CONFIG_H +* Include proper header file in 'tools' for configuration macros +* Include pthread_np.h from pthread_stop_world.c on OpenBSD +* Log error messages to stderr instead of stdout in tests +* Make GC_generic_malloc_ignore_off_page() public +* Make GC_mark_lock_holder variable static +* Make GC_print_trace always thread-safe and remove 'lock' argument +* Mark GC_started_thread_while_stopped() as GC_INNER +* Minimize code duplication in GC_mark_and_push +* Move 'include setjmp.h' from mach_dep.c to gc_priv.h +* Move GC_OPENBSD_UTHREADS definition to private/gcconfig.h (OpenBSD) +* Move GC_get_suspend/thr_restart_signal to misc.c for NaCl and OpenBSD +* Move LOCK/UNLOCK from GC_unregister_disappearing_link_inner outer +* Port BDWGC to Android/x86 +* Postpone the suspend signal in GC_dirty_init only if used to stop world +* Prepend '#' symbol to GC number in logged messages +* Prevent POSIX fork if mprotect_thread is started (Darwin) +* Prevent abort on GC_err/warn_printf write failure +* Prevent misleading AC_MSG_ERROR/AS_IF errors reported in configure.ac +* Put gc_cpp symbols into 'boehmgc' namespace if GC_NAMESPACE defined +* Recognize GC_DONT_GC macro in gc.h (causes GC_INIT to turn off GC) +* Recognize GC_SIG_SUSPEND and GC_SIG_THR_RESTART tuning macros in gc.h +* Redirect WRITE to __android_log_write if GC_ANDROID_LOG (Android) +* Refine comment of GC_is_heap_ptr and GC_thread_is_registered in gc.h +* Register dynamic libraries via dl_iterate_phdr on Android and OpenBSD +* Remove DebugBreak on WriteFile failure (Win32) +* Remove GC_BUILD definition from build scripts +* Remove abort on open log failure from GC_write (Win32) +* Remove configure.ac outdated revision number +* Remove nested EXPECT in GC_core_finalized_malloc +* Remove nested always-false ifdef for HPUX and FREEBSD +* Remove redundant GC_err_printf before abort +* Remove unused UTHREAD_SP_OFFSET macro (OpenBSD) +* Rename subthread_create to subthreadcreate_test (Makefile) +* Replace GC_COND_LOG_PRINTF calls with WARN for allocation failure messages +* Replace GC_log/err_printf() followed by ABORT with ABORT_ARGn() +* Replace GC_stats_log_printf with GC_DBG/INFOLOG_PRINTF +* Replace SIG_SUSPEND/THR_RESTART macros to variables in pthread_stop_world +* Replace Win32 GC_delete_gc_thread with GC_delete_gc_thread_no_free +* Replace conditional GC_log_printf calls with GC_COND/VERBOSE_LOG_PRINTF +* Replace sprintf with defensive snprintf +* Replace var-args GC_noop with GC_noop6 (to eliminate Clang warning) +* Simplify LOCK/UNLOCK macro definition for static code analysis tools +* Specify GC_malloc result is unused in some tests +* Specify GC_pthread_join result is unused in threadkey_test +* Specify LT_INIT in configure.ac +* Start of port to QNX +* Support rthreads introduced in OpenBSD 5.2+ +* Suppress 'GC_dont_gc deprecated' warning in gc.h if GC_DONT_GC +* Tag GC malloc routines with alloc_size attribute for Clang 3.2+ +* Test NO_WRAP_MARK_SOME macro to suppress WRAP_MARK_SOME-specific code +* Turn off GC_LOOP_ON_ABORT functionality if GC compiled with NO_DEBUGGING +* Turn on world-stop delay logging at debug level by default for Android +* Use EXPECT in GC_COND/VERBOSE_LOG_PRINTF +* Use GC_log_printf for logging instead of GC_[err_]printf +* Use compiler TLS for Android NDK gcc/arm +* Use memcpy (BCOPY) instead of strcpy (to suppress GCC warning) +* Use pthread API to operate thread-local data on Linux if no compiler TLS +* Workaround 'ELF_DATA/EM_ALPHA redefined' warning in Android linker.h +* Workaround 'unresolved __tls_get_addr' error for Android NDK Clang +Also, includes 7.2e, 7.2d, 7.2c, 7.2b changes + + +== [7.3alpha2] 2012-05-11 == + +* Add 'const' qualifier to pointer argument of some API functions +* Add GC_UNDERSCORE_STDCALL, UNICODE macro templates to configure (Win32) +* Add GC_get_thr_restart_signal, GC_thread_is_registered to GC API +* Add GC_is_heap_ptr, GC_move_disappearing_link to GC API +* Add SHORT_DBG_HDRS macro template to configure +* Add Symbian port to mainline (porting done by Djamel Magri) +* Add TODO file +* Add assertion ensuring proper alignment of 'pushed' GC symbols +* Add assertion in GC_getspecific on qtid +* Add assertion to GC_incremental_protection_needs, refine documentation +* Add assertion to check GC_large_free_bytes by GC_finish_collection +* Add configure option to compile all library .c files into single gc.o +* Add cordtest to make check +* Add disclaim callbacks for efficient finalization (ENABLE_DISCLAIM) +* Add finalization.html to 'doc' folder +* Add javaxfc.h to the installation set of GC header files (configure) +* Add on-heap-resize event notification to API +* Adjust GC_log_printf format specifiers (regarding signed/unsigned long) +* Adjust GC_requested_heapsize on GC_init if GC_INITIAL_HEAP_SIZE given +* Allow GC_exclude_static_roots() region start to be unaligned +* Allow Win32 DllMain chaining on the client side +* Allow to exclude finalization support by GC_NO_FINALIZATION macro +* Allow to get memory via Win32 VirtualAlloc (USE_WINALLOC) on Cygwin +* Avoid unnecessary GC_find_limit invocation if GC_no_dls +* Avoid use of deprecated GC_dont_gc and GC_stackbottom in gctest +* Cast pointers to word (instead of unsigned long) in specific.h +* Changed the order in autogen.sh so ltmain exists in time for automake +* Declare privately and use handy GC_base_C() for constant object pointers +* Define GC_DLL if DLL_EXPORT at GC build (for Cygwin/MinGW) +* Define GC_READ_ENV_FILE in configure for WinCE unless gc-debug is off +* Do not compile backgraph.c unless configure '--enable-gc-debug' +* Do not compile pthread_stop_world.c for Cygwin/Darwin (configure) +* Do not install ancient new_gc_alloc.h broken for modern STL (configure) +* Enable GC_MIN_MARKERS to set minimal number of pthread-based markers +* Enable PARALLEL_MARK and THREAD_LOCAL_ALLOC for FreeBSD in configure +* Enable parallel mark by default in configure (Darwin/Linux/Solaris/Win32) +* Export GC_is_marked, GC_clear/set_mark_bit (for mark-bit manipulation) +* Extend thread-related debug messages +* Fix 'configure --enable-cplusplus' for Cygwin/MinGW +* Fix DATASTART (and other minor improvements) for NaCl target +* Fix GC_setspecific to prevent garbage collection inside +* Fix compiler warning in cordtest +* Fix minor warnings reported by GCC with '-pedantic' option +* Fix static data roots registration on Android (if GC is shared) +* Implement GC_get_stack_base for Darwin for single-threaded mode +* Improve GC_allochblk algorithm of block splitting when unmapping enabled +* Improve GC_collect_or_expand algorithm for many finalizers registered case +* In tests, print a message in case a test is a no-op +* Instruct configure to hide internal libgc.so symbols if supported by GCC +* Log amount of unmapped memory (if enabled) on marking-for-collection +* Make __data_start a weak symbol to allow loading modules on mips +* Move "cord" library tests to "cord/tests" folder +* Move asm machine-dependent files to "src" folder +* Move build tools sources to "tools" folder +* Move cord_pos.h to public headers folder +* Open log file in APPEND mode on Win32 (similar that on Unix/Cygwin) +* Optimize some functions by moving pthread_self calls out of LOCK section +* Place only major per-release changes description to ChangeLog (this file) +* Prevent compiler warnings in GC_FindTopOfStack and GC_ports (Darwin) +* Recognize GC_LOG_TO_FILE_ALWAYS macro to log to 'gc.log' by default +* Remove all auto-generated files from the repo +* Remove binary icon file for de_win +* Remove cordtest from "cord" library +* Remove duplicate MacOS_Test_config.h file +* Remove gc_amiga_redirects.h (included internally) from public headers +* Remove obsolete Makefile.DLL (superseded by Cygwin/MinGW configure) +* Remove obsolete unused asm files for ALPHA, HPUX, SGI, RS6000, ULTRIX +* Remove unsupported MMAP_STACKS (specific to Solaris threads) +* Remove unused ancient SILENT, __STDC__, NO_SIGNALS macros +* Replace ARGSUSED comment-based annotation with GCC 'unused' attribute +* Replace GC_ms_entry declaration with opaque definition for public API +* Replace long GC_markers global variable with int GC_markers_m1 +* Replace pointer relational comparisons with non-pointer ones +* Replace printf PRIxMAX specifier with '%p' for thread id debug output +* Require autoconf 2.61 instead of v2.64 +* Simplify autogen.sh (use autoreconf) +* Split GC_abort with GC_on_abort and abort() invoked from ABORT +* Support GC_ATTR_MALLOC for MS VisualStudio +* Tag auxiliary malloc-like API functions with 'malloc' attribute +* Tag deprecated variables in GC API +* Tag must-be-non-null arguments of GC API functions +* Turn on "extra" GCC warnings +* Turn on unused-parameter checking for GCC +* Update AUTHORS file +* Use EXPECT for checking various 'initialized' boolean variables +* Use USE_COMPILER_TLS on Cygwin +* Use pthread_key for thread-local storage on FreeBSD +* Use union of AO_t and word to favor strict-aliasing compiler optimization +Also, includes 7.2 changes + + +== [7.2k] 2018-04-19 == + +* Fix arguments delimiter in pcr_interface.c (PCR) +* Fix assertion violation in DllMain of win32_threads +* Fix data race during apply_to_each_object(reset_back_edge) +* Fix dbg_mlc.c/o file name in documentation +* Fix include gc_gcj.h in thread_local_alloc.c +* Fix man section number (3) +* Fix missing GC_generic_malloc_words_small implementation in new_gc_alloc.h +* Fix missing SIGBUS handler setup for kFreeBSD +* Fix null dereference in print_callers on backtrace_symbols failure +* Fix the collector hang when it is configured with --enable-gc-debug +* Fix thread_suspend fail for threads registered from key destructor (OS X) +* Fix typo in AIX macro name +* Fix typo in comment in specific.h + + +== [7.2j] 2018-01-21 == + +* Fix GC allocation mutex in child after a fork +* Fix last_reclaimed..gc_no interval comparison to threshold in unmap_old +* Fix libgc version which was changed in linkage breaking way +* Fix missing EOLn output in threadlibs tool + + +== [7.2i] 2017-12-21 == + +* Avoid data race in finalized_count (gctest) +* Fix assertion violation about disabled cancel in try_to_collect_inner +* Fix data race in a list referenced by A.aa (gctest) +* Fix data race in do_local_mark when comparing active_count to helper_count +* Fix data race in GC_init_explicit_typing +* Fix data race in last_stop_count access (suspend_handler_inner) +* Fix data race in mark_thread when updating mark_no +* Fix data race when getting object size in explicitly-typed allocators +* Fix lack of barriers to synchronize memory for suspend_handler +* Fix typos in cdescr.html, extend_size_map and ews4800 doc, README.sgi +* Prevent 'Unexpected heap growth' in single-threaded gctest (Linux/x64) + + +== [7.2h] 2017-10-12 == + +* Add gctest as a test (CMake) +* Change no-argument functions declaration style to ANSI C (extra files) +* Do not allow SHORT_DBG_HDRS if KEEP_BACK_PTRS or MAKE_BACK_GRAPH +* Ensure oom_fn callback executed on out-of-memory in calloc +* Fix '~' operator application to unsigned values shorter than word +* Fix 'context local variable might be clobbered by setjmp' compiler warning +* Fix 'doc' files installation folder +* Fix 'shift count >= width of type' compiler warning in GC_SQRT_SIZE_MAX +* Fix ALL_INTERIOR_POINTERS name in comments and documentation +* Fix AO_SRC_DIR target name in NT_*_MAKEFILE +* Fix assertion in GC_mark_from for non-heap regions +* Fix assertion in GC_steal_mark_stack for non-heap regions +* Fix assertion violation in GC_repeat_read if --enable-redirect-malloc +* Fix assertion violation in GC_wait_builder called from start_mark_threads +* Fix assertion violation in mark_local checking GC_mark_stack_top +* Fix assertion violation in return_single_freelist in child process +* Fix bm_huge initialization for 64-bit targets (gctest) +* Fix broken external links in documentation +* Fix bytes count passed to add_to_our_memory in backgraph new_back_edges +* Fix calloc_explicitly_typed in case of lb*n overflow +* Fix CMake warning about CMP0054 by unquoting instances of HOST +* Fix conditional expression in pos_fetch, next non-macro definitions (cord) +* Fix configure --disable-munmap handling +* Fix CORD_substr_closure for the case when CORD_from_fn returns C string +* Fix crash in FirstDLOpenedLinkMap if app linked statically (Alpine Linux) +* Fix double lock in pthread_detach (Cygwin, winpthreads) +* Fix double multiplication of lb by n in calloc_explicitly_typed +* Fix enable_parallel_mark condition in CMake script +* Fix external libatomic_ops pkg-config-based detection +* Fix gc_allocator.h file name in new_gc_alloc.h comment +* Fix gc_backptr.h, gc_mark.h, GC_DS_TAGS names in documentation +* Fix gc_cleanup destructor for non-heap objects (gc_cpp) +* Fix GC_collect_or_expand to prevent allocation size value wrap-around +* Fix GC_incremental declaration/definition type mismatch +* Fix GC_mark_stack_top assertion violation properly in mark_local +* Fix GC_remove_specific invocation from remove_all_threads_but_me +* Fix GC_requested_heapsize increment in GC_init +* Fix GC_setspecific to prevent garbage collection inside +* Fix GC_SIZE_MAX definition (Linux/musl-gcc) +* Fix GCJ support in CMake build script +* Fix gctest crash if configure --enable-handle-fork on Darwin +* Fix get_maps on proc maps file asynchronous growth +* Fix hb_n_marks underflow in clear_fl_marks if MARK_BIT_PER_OBJ +* Fix header filename in gcconfig.h comment +* Fix infinite mark_some calls after memory mapping disappeared (Glibc) +* Fix integer shift undefined behavior in GC_init_explicit_typing +* Fix leak_test crash in print_callers if free() is redirected +* Fix Makefile.direct recursive invocation +* Fix malloc routines to prevent size value wrap-around (fix CVE-2016-9427) +* Fix missing win32_threads.c compilation for Cygwin (CMake) +* Fix MS VC warning about compiling unused checksums and thread_local_alloc +* Fix name typos in GC_FAST_MALLOC_GRANS comment +* Fix null dereference in reclaim_block if DONT_ADD_BYTE_AT_END +* Fix OSF1 host pattern in CMakeLists.txt +* Fix PCR-Makefile by removing compilation of a missing file +* Fix potential data race in GC_SysVGetDataStart (SPARC) +* Fix potential integer overflow in GC_find_limit_* functions +* Fix printf arguments type in print_callers +* Fix pthread_detach for threads not yet registered (Cygwin, winpthreads) +* Fix pthread_join to avoid thread removal on failure (Cygwin, winpthreads) +* Fix pthread_join when thread is registered in thread key destructor +* Fix push_complex_descriptor to avoid unlimited global mark stack growth +* Fix removal of dead threads in a child process +* Fix SIGSEGV in GC_is_marked when gc_cleanup is used in leak-finding mode +* Fix SIGSEGV in mark_from called from do_local_mark if WRAP_MARK_SOME +* Fix Solaris/sparc detection in case of strict C compliance is enforced +* Fix STACKBOTTOM for Solaris 11/x86 +* Fix storage class of local variable in register_dynamic_libraries (Irix) +* Fix tools/setjmp_t hang (OS X) +* Fix typed_test to prevent fails in malloc_explicitly_typed (64-bit) +* Fix undefined HEAP_START in register_dynamic_libraries +* Fix USE_CUSTOM_SPECIFIC mode (if manually enabled) for Win32 +* Fix USE_GET_STACKBASE_FOR_MAIN definition in gcconfig.h +* Fix various typos in comments, documentation and printed messages +* Handle load_segs overflow in register_dynlib_callback gracefully +* Prevent misleading AC_MSG_ERROR/AS_IF errors reported in configure.ac +* Replace (fix) 'objs' acronym in comments with 'objects' word +* Revert "Skip GC_DS_PER_OBJECT objs with negative descriptor in GC_mark_from" +* Update documentation about bugs reporting and new releases notification +* Update Download information in GC overview document +* Update shared libraries version info (v7.2) +* Workaround a bug in winpthreads causing parallel marks deadlock (MinGW) +* Workaround missing getcontext() in Docker osrf/ubuntu_32bit + + +== [7.2g] 2016-05-23 == + +* Fix 'illegal option -xassembler-with-cpp' error (Oracle SunCC) +* Fix 'implicit declaration of function' compiler warnings in cord/de +* Fix CFLAGS in configure regarding -O flag passing to SunCC compiler +* Fix FirstDLOpenedLinkMap for case libgc not 1st dynamically linked (NetBSD) +* Fix GC initialization in cord de_win for Cygwin +* Fix GC_get_stack_base if called before GC_init (Win32) +* Fix OSX issue with pthread_attr_setstacksize failure +* Fix Unicode Win32 API calls in cord de_win +* Fix USE_COMPILER_TLS macro duplicate description in README +* Fix cord de_win WndProc prototype parameters for 64-bit (Win64) +* Fix file descriptor resource leak in GC_register_data_segments (OS/2) +* Fix filename printing in cordtest +* Fix missing cord_pos.h, ec.h among installed headers (Automake) +* Fix missing GC_get_stack_base for Amiga +* Fix missing msvc_dbg.h in dist_noinst_HEADERS (Automake) +* Fix mistyped ARM_THREAD_STATE macro (Darwin/arm) +* Fix null-pointer dereferences on out-of-memory in cord and tests +* Fix potential multiplication overflow in check_heap_stats (gctest) +* Fix race (and potential deadlock) at marker threads initialization +* Fix signedness of char values passed to isspace, iscntrl, isxdigit +* Fix typo (items numbering) in GC_finalize_all documentation +* Fix typos in ERROR_FL, GC_malloc_uncollectable comments +* Fix typos in gc_priv.h, in README for ews4800 +* Fix unresolved vsnprintf in misc.c and snprintf in cordtest (DJGPP, VC) +* Fix various spelling errors +* Fix vsprintf_args initialization/cleanup in CORD_vsprintf for EMX +* Regenerate configure files using official libtool release (v2.4.2) +* Remove documentation about obsolete GC_REDIRECT_TO_LOCAL +* Skip GC_DS_PER_OBJECT objects with negative descriptor in GC_mark_from +* windows-untested: Fix paths to msvc_dbg.c/h + + +== [7.2f] 2014-06-03 == + +* Fix 'Bad signal in suspend_handler' abort on FreeBSD-9.2 +* Fix 'source file in a subdirectory' Automake warnings +* Fix ABORT message in GC_restart_handler +* Fix ADD_DEFINITION in CMakeLists.txt for kFreeBSD +* Fix CMakeLists.txt: do not override CMAKE_OSX_ARCHITECTURES +* Fix GC_alloc_large by bumping GC_collect_at_heapsize in GC_add_to_heap +* Fix GC_scratch_last_end_ptr update on GC_scratch_alloc failure +* Fix GET_MEM argument rounding in GC_scratch_alloc and similar +* Fix PARALLEL_MARK for Windows 7+ +* Fix build (broken by fenv.h inclusion) on Linux/x86_64 under uClibc +* Fix crash when using GC_malloc_many() as first allocation call +* Fix mark stack excessive growth during parallel mark +* Fix or remove broken URLs in documentation +* Fix out-of-memory case in new_back_edges, push_in_progress (backgraph) +* Fix typo in GC_collect_or_expand comment +* Fix typos in GC overview file, gc_config_macros.h, gc_cpp.h, README.changes +* Regenerate configure files by automake 1.14.1, libtool 2.4.2.418 +* Update emails/links due to project site and ML transition + + +== [7.2e] 2013-11-10 == + +* Add weak attribute to avoid __data_start undefined messages (s390x) +* Add weak stubs for pthread_cancel API +* Adjust 'pthread_[un]register_cancel undefined ref' workaround (Pthreads) +* Append _test suffix to 'initsecondarythread' binary file names +* Enable PARALLEL_MARK and THREAD_LOCAL_ALLOC for FreeBSD in configure +* Fix 'stack section' pointer passed to push_all_stack_sections (Pthreads) +* Fix GC_CreateThread 'dwStackSize' argument type for Win64 +* Fix GC_PTHREAD_PTRVAL definition for GC_PTHREADS_PARAMARK (Win32) +* Fix GC_clear_stack by declaring 'dummy' local array as volatile +* Fix GC_get_stack_base assembly code (Cygwin/Clang) +* Fix GC_malloc_explicitly_typed_ignore_off_page for large allocations +* Fix GC_marker_Id elements initialization (WinCE) +* Fix GC_print_trace missing unlock +* Fix GC_unix_mmap_get_mem for open of /dev/zero failure +* Fix GC_win32_free_heap compilation error for Cygwin +* Fix GC_win32_free_heap to prevent memory leak if USE_GLOBAL_ALLOC +* Fix Win32 GC_write preventing potential infinite recursion at abort +* Fix assertion violation in GC_mark_from prefetch loop +* Fix collection of objects referenced only from GC_mark_stack_X variables +* Fix dwSize argument of VirtualFree call in detect_GetWriteWatch (Win32) +* Fix heap sections overflow for Win32/Cygwin with enabled parallel marker +* Fix min_bytes_allocd preventing potential infinite loop in GC_allocobj +* Fix missing tabs in SMakefile.amiga file +* Fix null-pointer dereference in CORD_substr_closure +* Fix old_segv/bus_act variables initialization for FreeBSD +* Fix potential double fclose in test_extras (cordtest) +* Fix pthread_attr_t resource leak in pthread_create +* Fix race in GC_print_all_errors regarding GC_leaked +* Fix sizeof in GC_push_thread_structures +* Fix stackbottom/stack_end assignment in GC_call_with_gc_active +* Fix tests makefile to link with new GNU ld linking rules +* Fix typos in comments and documentation +* Fix unportable '==' test operators in configure +* Fix vsprintf_args cleanup in CORD_vsprintf +* Merge FreeBSD New ports collection for boehm-gc v7.2d +* Replace GC_DBG_RA with GC_DBG_EXTRAS macro +* Replace deprecated [CXX]INCLUDES to AM_C[PP]FLAGS in configure.ac file +* Use __builtin_extract_return_addr in GC_RETURN_ADDR_PARENT (gcc/x86) + + +== [7.2d] 2012-08-09 == + +* Fix GC_call_with_stack_base to prevent its tail-call optimization +* Fix all address-of-dummy operations by using GC_approx_sp() instead +* Fix stop_info.stack_ptr assignment in GC_suspend_all for OpenBSD +* Fix test_cpp (ensure the collector recognizes pointers to interiors) +* Fix thread-related tests for pthreads-w32 +* test_cpp: Fix WinMain to prevent SEGV if zero arguments passed (MinGW) + + +== [7.2c] 2012-06-11 == + +* Fix CORD_cat_char_star to prevent SEGV in case of out-of-memory +* Fix GC_FirstDLOpenedLinkMap() for NetBSD 6 release +* Fix GC_scratch_alloc and GC_get_maps invocations to prevent SEGV +* Fix visibility of GC_clear/set_mark_bit (unhide symbols) +* Fix visibility of GC_push_all/conditional, GC_push_other_roots symbols + + +== [7.2b] 2012-05-23 == + +* Fix assertion in GC_malloc_[atomic_]uncollectable (THREADS case only) + + +== [7.2] 2012-05-11 == + +* Abort in GC_thr_init on pthread_atfork failure (POSIX threads) +* Add GC_WIN32_PTHREADS target in configure +* Add GC_is_disabled new function to GC API +* Add info that getcontext() resets FPE mask no longer on Linux/x86_64 +* Add public GC_set_handle_fork to control forked child handling support +* Add realloc_test.c test +* Add support for Hexagon target +* Add thread-safe GC_get_heap_usage_safe to GC API +* Change GC_check_fl_marks prototype and implementation +* Check pthread_create/join result in test +* Define GC_DLL (in configure) if building only dynamic libraries +* Define NO_DEBUGGING (in configure) if "--disable-gc-debug" is set +* Disable incremental mode on Darwin if fork handling requested +* Enable parallel marker in configure for Solaris +* Fix "comparison of signed and unsigned values" compiler warnings +* Fix 'volatile' keyword placement in GC_SysVGetDataStart +* Fix ALIGNMENT, CPP_WORDSZ, GC_GRANULE_BYTES/WORDS for x32 target +* Fix GC_READ_ENV_FILE code for Cygwin +* Fix GC_add_roots_inner for Mac OS X (align segment start) +* Fix GC_check_fl_marks regarding concurrent access +* Fix GC_finalizer_nested size to workaround alignment problem in Watcom +* Fix GC_find_limit_with_bound to always reset fault handler on return +* Fix GC_init static assertion for clang/x64 (Darwin) +* Fix GC_init[_lib_bounds] and GC_get_main_stack_base for malloc redirection +* Fix GC_push_all/selected boundaries check +* Fix GC_register_my_thread marking thread as detached (Cygwin/pthreads-w32) +* Fix GC_remove_all_threads_but_me to cleanup thread-specific data storage +* Fix GC_restart_handler to preserve errno if needed +* Fix GC_root_size update in GC_add_roots_inner (Win32) +* Fix GC_unregister_my_thread to ensure no ongoing incremental GC (Win32) +* Fix GC_with_callee_saves_pushed for clang (disable __builtin_unwind_init) +* Fix calloc, GC_generic_malloc to check for allocation size overflows +* Fix compiler warning in GC_dyld_image_add/remove (Darwin) +* Fix configure --enable-cplusplus make install +* Fix configure to disable GCC aliasing optimization unless forced to +* Fix duplicate definitions in gcconfig.h for NetBSD +* Fix fork() support on Cygwin and Darwin targets +* Fix gc.h compatibility regression regarding GC_PTR, GC_I_HIDE_POINTERS +* Fix gc_cpp.cc for Cygwin (remove duplicate function definition) +* Fix gcconfig.h to define USE_GET_STACKBASE_FOR_MAIN for Android +* Fix gcconfig.h to handle mips64-linux target +* Fix gctest (for Win32) to avoid GC_print_stats internal variable usage +* Fix mach_dep.c to include sys/ucontext.h on Mac OS X 10.6 +* Fix tests to check GC_malloc result for NULL (out-of-memory) +* Fix thread model in configure for MinGW ("win32" instead of "posix") +* Fix various warnings reported by LINT-like tools +* Fix visibility of some GC internal symbols used by GNU GCJ currently +* Port some thread tests to Win32 +* Refine API GC setters and getter comments regarding locking +* Refine GC_stackbottom description in gc.h +* Remove duplicate calls in GC_register_dynamic_libraries +* Remove locking in API GC_get_bytes_since_gc and friends +* Remove newly-added GC_get_heap_size/free_bytes_inner from API +* Remove some local variables that are unused +* Support multi-threading for RTEMS target +* Use global GC_noop_sink variable in GC_noop1 to suppress compiler warning +* Use pkg-config to pick up libatomic_ops, etc +* Workaround some Linux/arm kernels bug to get correct GC_nprocs value + + +== [7.2alpha6] 2011-06-14 == + +* configure_atomic_ops.sh: Remove. +* Makefile.direct (dist gc.tar): Remove configure_atomic_ops.sh. +* Makefile.am (EXTRA_DIST): Add autogen.sh. + +* NT_STATIC_THREADS_MAKEFILE (.cpp.obj): Remove duplicate .cpp +filename passed. +* NT_X64_THREADS_MAKEFILE (.cpp.obj): Use lowercase file +extension. +* NT_X64_STATIC_THREADS_MAKEFILE (.cpp.obj): Likewise. +* NT_MAKEFILE (.cpp.obj): Likewise. + +* alloc.c (GC_add_current_malloc_heap, GC_build_back_graph, +GC_traverse_back_graph): Move prototype to gc_priv.h. +* checksums.c (GC_page_was_ever_dirty): Likewise. +* dbg_mlc.c (GC_default_print_heap_obj_proc): Likewise. +* dyn_load.c (GC_parse_map_entry, GC_get_maps, +GC_segment_is_thread_stack, GC_roots_present, GC_is_heap_base, +GC_get_next_stack): Likewise. +* finalize.c (GC_reset_finalizer_nested, +GC_check_finalizer_nested): Likewise. +* gcj_mlc.c (GC_start_debugging, GC_store_debug_info): Likewise. +* malloc.c (GC_extend_size_map, GC_text_mapping): Likewise. +* mark_rts.c (GC_mark_thread_local_free_lists): Likewise. +* misc.c (GC_register_main_static_data, GC_init_win32, +GC_setpagesize, GC_init_linux_data_start, +GC_set_and_save_fault_handler, GC_init_dyld, GC_init_netbsd_elf, +GC_initialize_offsets, GC_bl_init, GC_do_blocking_inner, +GC_bl_init_no_interiors): Likewise. +* os_dep.c (GC_greatest_stack_base_below, GC_push_all_stacks): +Likewise. +* reclaim.c (GC_check_leaked): Likewise. +* win32_threads.c (GC_gww_dirty_init): Likewise. +* darwin_stop_world.c (GC_is_mach_marker, GC_mprotect_stop, +GC_mprotect_resume): Move prototype to darwin_stop_world.h. +* pthread_support.c (GC_FindTopOfStack): Likewise. +* dyn_load.c (GC_cond_add_roots): Merge adjacent definitions. +* mark.c (GC_page_was_ever_dirty): Remove (as already declared). +* mark_rts.c (GC_roots_present): Change return type to void +pointer (to match the prototype); return NULL instead of FALSE. +* mark_rts.c (GC_add_roots_inner): Cast GC_roots_present() result. +* os_dep.c (NEED_PROC_MAPS): Move definition to gcconfig.h. +* os_dep.c (GC_write_fault_handler): Make STATIC. +* os_dep.c (GC_set_write_fault_handler): New function (only if +GC_WIN32_THREADS). +* pthread_start.c (GC_start_rtn_prepare_thread, +GC_thread_exit_proc): Move prototype to pthread_support.h. +* pthread_support.c (GC_nacl_initialize_gc_thread, +GC_nacl_shutdown_gc_thread, GC_unblock_gc_signals): +Likewise. +* pthread_support.c (GC_stop_init): Move prototype to +pthread_stop_world.h. +* win32_threads.c (GC_write_fault_handler): Remove prototype. +* win32_threads.c (GC_register_my_thread_inner): Call +GC_set_write_fault_handler instead of SetUnhandledExceptionFilter +(only if MPROTECT_VDB). +* doc/README.win32: Add information about DMC. +* include/private/gc_priv.h (GC_set_write_fault_handler): New +prototype (only if GC_WIN32_THREADS and MPROTECT_VDB). + +* misc.c (vsnprintf): Redirect to vsprintf() if NO_VSNPRINTF. + +* win32_threads.c (GC_unregister_my_thread): Use KNOWN_FINISHED() +instead of FINISHED macro. +* tests/test.c (check_heap_stats): Round up max_heap_sz value for +Win32 (same as for USE_MMAP). + +* tests/test.c (check_heap_stats): Adjust printf format specifier +for max_heap_sz; cast max_heap_sz accordingly. + +* doc/README.solaris2: Add note. + +* configure.ac (SOLARIS25_PROC_VDB_BUG_FIXED): Don't define for +Solaris/x86 2.10+. + +* tests/threadkey_test.c (SKIP_THREADKEY_TEST): Skip the test if +defined; explicitly define for some targets. + +* mark.c (GC_dirty): Add prototype (only if MANUAL_VDB). +* stubborn.c (GC_dirty): Likewise. +* include/private/gcconfig.h (GWW_VDB, MPROTECT_VDB, PCR_VDB, +PROC_VDB): Undefine if MANUAL_VDB. +* include/private/gcconfig.h (DEFAULT_VDB): Don't define if +MANUAL_VDB. +* os_dep.c (async_set_pht_entry_from_index): Define for +MANUAL_VDB. +* os_dep.c (GC_read_dirty): Set GC_dirty_maintained only if +success; if ioctl() failed then just print warning instead of +aborting. + +* include/private/gc_priv.h (GC_ASSERT): Use "%d" (instead of %ld) +for line number printing. + +* os_dep.c (GC_read_dirty): Add debug logging if DEBUG_DIRTY_BITS +(for PROC_VDB only); print errors via GC_err_printf; rename "ps" +and "np" local variables to npages and pagesize, respectively; +remove "current_addr" local variable. + +* os_dep.c (GC_get_main_stack_base): Convert to GC_get_stack_base +for BeOS and OS/2; define HAVE_GET_STACK_BASE. +* os_dep.c (GET_MAIN_STACKBASE_SPECIAL): Define when a specific +GC_get_main_stack_base implementation is defined. +* os_dep.c (GC_get_main_stack_base): Define that based on +GC_get_stack_base() in a single place (only if +GET_MAIN_STACKBASE_SPECIAL is unset); check GC_get_stack_base() +result. + +* mark.c (GC_push_selected): Remove "push_fn" argument (use +GC_push_all directly); update the documentation. +* mark.c (GC_push_conditional): Simplify the code (for better +readability). + +* mark.c (alloc_mark_stack): Use FALSE/TRUE (instead of 0/1) for +boolean local variables. +* doc/README.macros (GC_PREFER_MPROTECT_VDB): Update. +* os_dep.c (GC_page_was_dirty, GC_page_was_ever_dirty, +GC_remove_protection): Define for GWW_VDB and PROC_VDB in a single +place. +* os_dep.c (GC_page_was_dirty, GC_page_was_ever_dirty): Compute +PHT_HASH(h) only once (store result to a local variable). + +* doc/README.solaris2: Update. + +* include/private/gcconfig.h (end, InitStackBottom): Declare +extern variable for RTEMS. +* include/private/gcconfig.h (DATASTART, DATAEND, STACKBOTTOM): +Update (for RTEMS). +* include/private/gcconfig.h (DATAEND): Fix a typo in the macro +name (for RTEMS). +* tests/test.c (CONFIGURE_APPLICATION_DOES_NOT_NEED_CLOCK_DRIVER): +Replace with CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER (for RTEMS). + +* include/private/gcconfig.h (MPROTECT_VDB): Enable for Solaris in +single-threaded environment. + +* include/private/gcconfig.h (MPROTECT_VDB): Undefine if PROC_VDB. +* tests/test.c (NUMBER_ROUND_UP): New macro. +* tests/test.c (check_heap_stats): Round up total expected heap +size to the nearest 4 MiB bound. +* tests/test.c (check_heap_stats): Print the current and expected +heap sizes in case of failure. + +* checksums.c (GC_check_blocks, GC_check_dirty): Do log printing +only if GC_print_stats; print errors using GC_err_printf. +* checksums.c (GC_check_blocks): Join adjacent printf() calls into +a single one. + +* pthread_support.c (pthread_join): Add assertion (check thread is +finished). +* pthread_support.c (GC_register_my_thread): Don't detach the +thread if invoked from the thread destructor. +* win32_threads.c (GC_register_my_thread): Likewise. +* win32_threads.c (GC_unregister_my_thread): Don't delete the +thread (just set FINISHED) if the thread is not detached (only if +GC_PTHREADS); add assertion (check the thread is not finished). +* tests/threadkey_test.c (main): Join some created threads. + +* pthread_support.c (GC_delete_gc_thread): Rename "gc_id" local +variable to "t". +* win32_threads.c (GC_delete_gc_thread): Likewise. +* pthread_support.c (pthread_join, pthread_detach, +pthread_cancel): Rename "thread_gc_id" local variable to "t". +* win32_threads.c (GC_pthread_detach): Likewise. +* win32_threads.c (GC_delete_gc_thread): Remove "gc_nvid" local +variable. +* win32_threads.c (GC_pthread_join): Rename "joinee" local +variable to "t". + +* pthread_stop_world.c (pthread_sigmask): Undefine even if not +DEBUG_THREADS. +* pthread_stop_world.c (GC_unblock_gc_signals): New function (only +if GC_EXPLICIT_SIGNALS_UNBLOCK). +* pthread_support.c (GC_unblock_gc_signals): New prototype. +* pthread_support.c (GC_register_my_thread_inner, +GC_register_my_thread): Call GC_unblock_gc_signals (only if +GC_EXPLICIT_SIGNALS_UNBLOCK); add comment. +* include/private/gcconfig.h (GC_EXPLICIT_SIGNALS_UNBLOCK): New +macro. + +* pthread_stop_world.c (GC_suspend_handler_inner): Remove "dummy", +"sig" local variables; rename my_thread local variable to "self". + +* tests/threadkey_test.c (LIMIT): Use smaller value (don't create +more than 30 in parallel by default). + +* tests/threadkey_test.c (key_once, main): Work around for Solaris +PTHREAD_ONCE_INIT. +* tests/threadkey_test.c (LIMIT): Use smaller value for Solaris. + +* dyn_load.c (GC_FirstDLOpenedLinkMap): Remove unused "r" local +variable. +* pthread_support.c (GC_unregister_my_thread_inner): Revert back +GC_remove_specific invocation; add a comment. +* include/private/thread_local_alloc.h (GC_remove_specific): +Revert back. +* specific.c (slow_getspecific): Cast qtid to AO_t. +* include/private/specific.h (key_create, setspecific, +remove_specific): Remove "extern" keyword. +* include/private/specific.h (getspecific): Change type of "qtid" +local variable to unsigned long. + +* pthread_support.c (GC_check_tls): Fix "#endif" comment. +* include/gc.h (GC_REDIRECT_TO_LOCAL): Remove deprecated comment. +* include/private/thread_local_alloc.h (THREAD_LOCAL_ALLOC): +Remove redundant test of the macro. + +* backgraph.c (add_edge): Recognize DEBUG_PRINT_BIG_N_EDGES macro. +* os_dep.c (GC_set_and_save_fault_handler): Recognize +SIGACTION_FLAGS_NODEFER_HACK macro. +* pthread_support.c (mark_mutex): Recognize GLIBC_2_1_MUTEX_HACK +macro. +* pthread_support.c (GC_acquire_mark_lock): Remove commented out +code. +* include/private/gc_priv.h (SUNOS5SIGS): Don't include +sys/siginfo.h on Linux. +* include/private/gcconfig.h (FORCE_WRITE_PREFETCH): New macro +recognized, force PREFETCH_FOR_WRITE to be defined on x86. +* include/private/gcconfig.h (USE_HPUX_FIXED_STACKBOTTOM): New +macro recognized (for HP/UX). + +* os_dep.c (GC_gww_page_was_ever_dirty): Fix comment (for +GWW_VDB). +* os_dep.c (GC_dirty_init): Use memset() for GC_written_pages +resetting (for PROC_VDB). + +* tests/threadkey_test.c: New file. +* tests/tests.am (TESTS, check_PROGRAMS): Add 'threadkey_test'. +* tests/tests.am (threadkey_test_SOURCES, threadkey_test_LDADD): +New variable. + +* pthread_support.c (GC_unregister_my_thread_inner): Don't call +GC_remove_specific. +* include/private/thread_local_alloc.h (GC_remove_specific): +Remove (since it is empty for all targets). +* pthread_support.c (GC_record_stack_base): New inline function. +* win32_threads.c (GC_record_stack_base): Likewise. +* pthread_support.c (GC_register_my_thread_inner): Invoke +GC_record_stack_base. +* win32_threads.c (GC_register_my_thread_inner): Likewise. +* pthread_support.c (GC_register_my_thread): If thread is FINISHED +then call GC_record_stack_base, clear FINISHED, initialize +thread-local list and return success. +* win32_threads.c (GC_register_my_thread): Likewise. +* include/gc.h (GC_register_my_thread): Update documentation. +* include/private/thread_local_alloc.h (GC_thread_key): Likewise. + +* thread_local_alloc.c (GC_malloc, GC_malloc_atomic): Join +adjacent "#ifdef". +* thread_local_alloc.c (GC_malloc_atomic): Call +GC_core_malloc_atomic (instead of GC_core_malloc). + +* pthread_start.c (GC_start_rtn_prepare_thread): Change return +type to GC_thread. +* pthread_start.c (GC_inner_start_routine): Pass the current +thread descriptor to pthread_cleanup_push (same as in +win32_threads.c). +* pthread_stop_world.c (GC_push_all_stacks): Rename "me" local +variable to "self". +* win32_threads.c (GC_push_all_stacks): Likewise. +* pthread_stop_world.c (GC_suspend_all, GC_start_world): Rename +"my_thread" local variable to "self". +* pthread_support.c (GC_unregister_my_thread_inner): New static +function. +* pthread_support.c (GC_unregister_my_thread, +GC_thread_exit_proc): Use GC_unregister_my_thread_inner. +* win32_threads.c (GC_register_my_thread, GC_unregister_my_thread, +GC_do_blocking_inner): Rename "t" local variable to "thread_id". +* win32_threads.c (GC_wait_marker, GC_notify_all_marker): Rename +"id" local variable to "thread_id". + +* pthread_support.c (GC_unregister_my_thread): Call pthread_self +only once. +* win32_threads.c (GC_pthread_start_inner): Likewise. +* pthread_support.c (GC_unregister_my_thread): Add debug output. +* win32_threads.c (GC_unregister_my_thread): Likewise. +* pthread_support.c (GC_register_my_thread, +GC_start_rtn_prepare_thread): Rename "my_pthread" local variable +to "self". + +* include/gc.h (GC_HIDE_POINTER, GC_REVEAL_POINTER): Define +unconditionally (do not test GC_I_HIDE_POINTERS); update the +comment. +* include/gc.h (HIDE_POINTER, REVEAL_POINTER): Define as alias to +GC_HIDE/REVEAL_POINTER, respectively. +* include/private/gc_pmark.h (GC_I_HIDE_POINTERS): Do not define. +* include/private/gc_priv.h (GC_I_HIDE_POINTERS): Likewise. + +* include/gc.h (GC_register_my_thread): Refine the comment. + +* include/gc_inline.h (GC_MALLOC_WORDS, GC_CONS): Add missing +parentheses. +* include/gc_typed.h (GC_get_bit, GC_set_bit, +GC_CALLOC_EXPLICITLY_TYPED): Likewise. + +* include/private/gcconfig.h (NO_GETCONTEXT): Add missing ')'. + +* include/private/gcconfig.h (NO_GETCONTEXT): Do not use +getcontext(2) on m68k because it is not implemented there. + +* alloc.c (GC_clear_a_few_frames): Use BZERO(). +* mark_rts.c (GC_clear_roots, GC_rebuild_root_index): Likewise. +* reclaim.c (GC_start_reclaim): Likewise. +* blacklst.c (total_stack_black_listed): Remove "len" local +variable. +* dbg_mlc.c (GC_generate_random_valid_address): Replace "for" +statement with "do-while" one. +* dyn_load.c (GC_register_dynamic_libraries, +GC_register_dynlib_callback): Remove redundant parentheses. + +* cord/cordxtra.c (CORD_from_file_lazy_inner): Suppress +"unused result" compiler warning for fread(). + +* os_dep.c (GC_write_fault_handler): Break when in_allocd_block +is set to true. + +* dbg_mlc.c (GC_has_other_debug_info): Change return type to int; +return -1 if the object has (or had) debugging info but was +marked deallocated. +* include/private/dbg_mlc.h (GC_has_other_debug_info): Likewise. +* dbg_mlc.c (GC_has_other_debug_info): Update documentation; +remove "ohdr" local variable. +* dbg_mlc.c (GC_debug_free): Don't call GC_free if the object has +probably been deallocated. +* dbg_mlc.c (GC_debug_free): Don't actually free the object even +in the leak-finding mode if GC_findleak_delay_free. +* dbg_mlc.c (GC_check_leaked): New function (only unless +SHORT_DBG_HDRS). +* doc/README.environment (GC_FINDLEAK_DELAY_FREE): Document. +* doc/README.macros (GC_FINDLEAK_DELAY_FREE): Likewise. +* include/private/dbg_mlc.h (START_FLAG, END_FLAG): Use GC_WORD_C +on 64-bit architectures. +* include/private/dbg_mlc.h (NOT_MARKED): Remove redundant +parentheses. +* include/private/dbg_mlc.h (GC_HAS_DEBUG_INFO): Update (due to +GC_has_other_debug_info change). +* include/private/gc_priv.h (GC_findleak_delay_free): New global +variable declaration (unless SHORT_DBG_HDRS). +* misc.c (GC_findleak_delay_free): New global variable; recognize +GC_FINDLEAK_DELAY_FREE. +* misc.c (GC_init): Recognize GC_FINDLEAK_DELAY_FREE environment +variable (unless SHORT_DBG_HDRS). +* reclaim.c (GC_check_leaked): Declare (unless SHORT_DBG_HDRS). +* reclaim.c (GC_add_leaked): Don't add the object to leaked list +if marked as deallocated. + +* dbg_mlc.c (GC_has_other_debug_info): Fix punctuation in the +comment. +* dbg_mlc.c (GC_FREED_MEM_MARKER): New macro. +* dbg_mlc.c (GC_debug_free): Use GC_FREED_MEM_MARKER. +* dbg_mlc.c (GC_smashed): Refine documentation. +* mark.c (GC_push_selected): Change dirty_fn return type to +GC_bool. +* os_dep.c (GC_page_was_ever_dirty): Make GC_INNER. +* reclaim.c (GC_reclaim_small_nonempty_block): Remove "kind" +local variable. +* reclaim.c (GC_reclaim_block): Pass true constant to +GC_reclaim_small_nonempty_block (instead of report_if_found). +* doc/README.autoconf: Update; fix a typo. +* include/private/gcconfig.h (GC_WORD_C): New macro. + +* dbg_mlc.c (GC_store_debug_info_inner): Cast "linenum". +* dbg_mlc.c (GC_check_annotated_obj): Fix punctuation in the +comment. +* dbg_mlc.c (GC_print_smashed_obj): Add (and print) "msg" +argument. +* dbg_mlc.c (GC_debug_free, GC_print_all_smashed_proc): Pass +message to GC_print_smashed_obj. +* dbg_mlc.c (GC_debug_free): Call GC_size once. +* dbg_mlc.c (GC_debug_realloc): Calculate old_sz only if +allocation succeeded; remove unnecessary check for object is +smashed (since this is done in GC_debug_free); remove "clobbered" +local variable. + +* dbg_mlc.c (GC_store_debug_info_inner, GC_store_debug_info): +Rename "integer" argument to "linenum"; change the type of the +argument to int. +* gcj_mlc.c (GC_store_debug_info): Likewise. +* dbg_mlc.c (GET_OH_LINENUM): New macro. +* dbg_mlc.c (GC_print_obj, GC_print_smashed_obj): Use +GET_OH_LINENUM; adjust print format specifier. +* dbg_mlc.c (GC_debug_malloc, GC_debug_malloc_ignore_off_page, +GC_debug_malloc_atomic_ignore_off_page, +GC_debug_generic_malloc_inner, +GC_debug_generic_malloc_inner_ignore_off_page, +GC_debug_malloc_stubborn, GC_debug_malloc_atomic, +GC_debug_malloc_uncollectable, +GC_debug_malloc_atomic_uncollectable): Remove unnecessary cast of +"i". +* gcj_mlc.c (GC_debug_gcj_malloc): Likewise. + +* os_dep.c (GC_linux_stack_base): Rename to +GC_linux_main_stack_base. +* os_dep.c (GC_freebsd_stack_base): Rename to +GC_freebsd_main_stack_base; adjust error message. +* pthread_stop_world.c (GC_stop_init): Use GC_SEM_INIT_PSHARED +as an argument for sem_init(). +* pthread_support.c (pthread_create): Likewise. +* pthread_support.c (pthread_create): Abort in case sem_init() +fails. +* include/private/gc_priv.h (GC_SEM_INIT_PSHARED): Define. +* tests/initsecondarythread.c: Include gcconfig.h; call GC_INIT +from main() if it should be done from the primordial thread only. + +* alloc.c: Don't include sys/types.h for ArmCC. +* dyn_load.c: Likewise. +* os_dep.c: Likewise. +* mach_dep.c (_setjmp, _longjmp): Redirect to setjmp/longjmp for +ArmCC. +* mark.c (GC_noop): Define specially for ArmCC. +* include/private/gc_priv.h (GC_noop): Likewise. +* misc.c (GC_init): Don't test pointers comparison for ArmCC. +* misc.c: Don't include unistd.h for ArmCC. +* os_dep.c (pages_executable): Rename to GC_pages_executable; +make STATIC. +* os_dep.c (GC_unix_mmap_get_mem): Don't define for ArmCC. +* ptr_chck.c (GC_is_visible): Explicitly cast +(GC_DS_PER_OBJECT-GC_INDIR_PER_OBJ_BIAS) to word (to suppress +a compiler warning). +* include/private/gcconfig.h: Recognize __arm. +* include/private/gcconfig.h (HBLKPTR): Define for ArmCC. +* include/private/gcconfig.h (HBLKPTR): Add parentheses for +"bytes" argument. + +* pthread_support.c (GC_get_nprocs): Don't define for Android. +* pthread_support.c (GC_dummy_thread_local): Don't test +GC_LINUX_THREADS. +* include/gc_config_macros.h (GC_ADD_CALLER, GC_RETURN_ADDR): +Define for Android. + +* mach_dep.c (NO_GETCONTEXT): Move to gcconfig.h. +* os_dep.c (GC_write_fault_handler): Don't include ucontext.h if +NO_GETCONTEXT. +* include/private/gcconfig.h (GETPAGESIZE): Define as a sysconf +call for Android. + +* include/private/gc_locks.h (WIN32_LEAN_AND_MEAN, NOSERVICE): +Define before including windows.h. +* include/private/gc_priv.h (WIN32_LEAN_AND_MEAN, NOSERVICE): +Likewise. +* include/private/thread_local_alloc.h (WIN32_LEAN_AND_MEAN, +NOSERVICE): Likewise. +* include/private/gc_priv.h (MS_TIME_DIFF): Avoid floating-point +arithmetics; add a comment. + +* mark.c (GC_clear_hdr_marks): Don't test USE_MARK_BYTES. +* extra/setjmp_t.c (main): Don't test USE_MARK_BITS. +* include/private/gc_pmark.h (SET_MARK_BIT_EXIT_IF_SET): Likewise. +* include/private/gc_pmark.h (SET_MARK_BIT_EXIT_IF_SET): Remove +"mark_byte" local variable. +* include/private/gc_pmark.h (OR_WORD_EXIT_IF_SET): Add a comment +about that AO_or() is not used by GC unless USE_MARK_BITS +explicitly set. +* include/private/gc_priv.h (OR_WORD): Likewise. +* include/private/gc_pmark.h (INCR_MARKS): Remove trailing ';', +add parentheses. +* include/private/gc_priv.h (ONES): Define before use by +MAKE_COOLER. +* include/private/gc_priv.h (MARK_BITS_SZ): Define where used. +* include/private/gc_priv.h (OR_WORD): Don't define if +USE_MARK_BYTES. +* include/private/gcconfig.h (USE_MARK_BYTES); Remove duplicate +definition; simplify expression. + +* os_dep.c (GC_get_maps): Always close the file. +* pthread_support.c (GC_get_nprocs): Likewise. +* os_dep.c (READ): Define similarly across the file (without +parameters). +* pthread_support.c (GC_get_nprocs): Use signed int type for "i" +and "len" local variables (since read() may return -1). +* include/private/gc_pmark.h (LONG_MULT): Add prefix/suffix +double underscore; add "volatile" for asm. +* include/private/gc_pmark.h (LONG_MULT): Add missing +parentheses. +* include/private/gc_priv.h (OR_WORD): Likewise. +* include/private/gc_priv.h (OR_WORD): Remove unnecessary brackets +and ';' symbol. + +* os_dep.c (GC_get_stack_base): Implement for Android (same as +for Linux). +* pthread_support.c (GC_get_nprocs): Return 1 (instead of -1) if +failed to open "stat" file (not to issue a warning twice); update +the comment. +* pthread_support.c (GC_thr_init): Call sysconf() on Android to +get the number of CPUs. + +* include/private/gc_priv.h (_GNU_SOURCE): Revert one of the +recent patches regarding this macro as the macro should be set +(to 1) before including any other system header. + +* doc/README.environment (GC_INITIAL_HEAP_SIZE, +GC_MAXIMUM_HEAP_SIZE): Update. + +* misc.c (GC_parse_mem_size_arg): Allow 'k', 'M', 'G' suffixes in +heap size specifier; return 0 if not a valid one. +* include/gc_cpp.h: Explicitly define inline one-argument delete +operator for Cygwin (as a workaround). +* tests/test_cpp.cc (main): Suppress compiler warnings about +"assigned value is unused". + +* misc.c (GC_parse_mem_size_arg): New function. +* misc.c (GC_init): Use GC_parse_mem_size_arg(). +* pthread_stop_world.c (tkill): Declare for Android. + +* include/private/gc_priv.h (_GNU_SOURCE): Include features.h +first (except for NaCl) and then define the macro to 1 if not yet. + +* tests/tests.am (TESTS, check_PROGRAMS): Add +'initsecondarythread'. +* tests/tests.am (initsecondarythread_SOURCES, +initsecondarythread_LDADD): New variable. + +* dbg_mlc.c (GC_store_debug_info_inner): Always define; add +"const" to its string argument. +* dbg_mlc.c (GC_store_debug_info): Call GC_store_debug_info_inner. +* dbg_mlc.c (GC_debug_free): Set GC_have_errors in case of +smashed or previously deallocated found. +* dbg_mlc.c (GC_check_heap_block): Replace while loop with a for +one. +* reclaim.c (GC_reclaim_check): Likewise. +* dbg_mlc.c (GC_check_heap_proc): Remove redundant cast to word. +* os_dep.c (GC_get_stack_base): Don't initialize +stackbase_main_self/ss_sp on Solaris if thr_main() is zero (thus +calling GC_INIT() from a non-primordial thread is possible now). +* reclaim.c (GC_add_leaked): Turn into an inline one. +* reclaim.c (GC_reclaim_small_nonempty_block): +Change report_if_found type from int/word to boolean. +* include/private/gc_priv.h (GC_start_reclaim): Likewise. +* include/private/gc_priv.h (set_mark_bit_from_hdr, +clear_mark_bit_from_hdr): Place closing parenthesis properly. + +* os_dep.c (GC_get_main_stack_base): Try to use +pthread_attr_getstack first for Linux if THREADS. +* doc/README.macros (USE_GET_STACKBASE_FOR_MAIN): Adjust text +alignment. + +* dbg_mlc.c (GC_generate_random_backtrace_no_gc): Fix a message +typo. +* dbg_mlc.c (GC_debug_malloc): Add a comment (about zero size). +* dbg_mlc.c (GC_strdup): Call GC_err_printf instead of WARN (in +case of NULL argument). +* dbg_mlc.c (GC_free): In case of NULL argument, just return +(without any warning printed); eliminate "uncollectable" local +variable. + +* configure.ac (THREADDLLIBS): Use alternate thread library on +Solaris 8. +* configure.ac (need_atomic_ops_asm): Set to true only for SPARC +Solaris. +* configure.ac: Don't use libdl on mips-sgi-irix6. + +* mach_dep.c (NO_GETCONTEXT); Define for RTEMS. +* mach_dep.c (GC_with_callee_saves_pushed): Don't call +__builtin_unwind_init() for RTEMS; use setjmp() without the +leading underscore (for RTEMS). +* tests/test.c (BIG): Use smaller value for RTEMS. +* tests/test.c (main): Customize for RTEMS. + +* configure.host: Remove doubled words in comments. +* os_dep.c: Likewise. +* doc/README: Likewise. +* extra/setjmp_t.c: Likewise. +* tests/huge_test.c: Likewise. +* extra/setjmp_t.c (getpagesize, nested_sp, main, g): Replace the +K&R-style function definition with the ANSI C one. +* extra/setjmp_t.c (nested_sp): Implement in the same way as +GC_approx_sp. + +* dyn_load.c (GC_dyld_sections): Add more sctions. +* dyn_load.c (GC_dyld_add_sect_fmts): New static varaible. +* dyn_load.c (L2_MAX_OFILE_ALIGNMENT): New macro. +* dyn_load.c (GC_dyld_image_add, GC_dyld_image_remove): Improve +logging; add support for on-demand sections. + +* gcj_mlc.c (GC_gcj_malloc_initialized): Use STATIC unless +GC_ASSERTIONS. +* include/private/gc_priv.h (GC_gcj_malloc_initialized): Don't +declare (as external) unless GC_ASSERTIONS. +* os_dep.c (GC_win32_free_heap): Clear GC_heap_bases[] also for +Cygwin; add FIXME. +* include/private/gcconfig.h: Include for RTEMS. +* include/private/gcconfig.h: Add "#error" for every "-->" mark. +* include/private/gcconfig.h (CLEAR_DOUBLE): Turn the code into +an expression. +* include/private/pthread_support.h (SUSPENDED_EXT): Add new flag +(which existed previously as SUSPENDED and still exists in GCJ). +* include/private/pthread_support.h (DISABLED_GC): Change the +value (as it is already used by SUSPENDED_EXT). + +* tests/test.c (reverse_test): Modify count (BIG) for +ppc64-darwin. + +* reclaim.c (GC_print_all_errors): Recognize new GC_ABORT_ON_LEAK +macro and environment variable; abort if any error has been +printed provided the environment variable (or macro) is set. +* doc/README.environment (GC_ABORT_ON_LEAK): Document. +* doc/README.macros (GC_ABORT_ON_LEAK): Likewise. + +* os_dep.c (GC_unix_sbrk_get_mem, GC_unix_get_mem): Don't define +for RTEMS. +* include/private/gcconfig.h (RTEMS): Add support for. +* include/private/gcconfig.h (GET_MEM): Use calloc() for RTEMS. + +* mallocx.c (GC_malloc_uncollectable): Move to malloc.c (since +it is used internally in some places). + +* dbg_mlc.c (GC_register_finalizer_no_order): Remove redundant +declaration. +* dbg_mlc.c (GC_debug_malloc_replacement, +GC_debug_realloc_replacement): Rename RA to GC_DBG_RA. +* malloc.c (GC_debug_malloc_replacement): Likewise. +* mallocx.c (GC_debug_realloc_replacement): Likewise. +* dbg_mlc.c (GC_store_debug_info): Move proto from dbg_mlc.h. +* malloc.c (GC_strdup, GC_strndup, GC_wcsdup): Move to mallocx.c. +* malloc.c: Include errno.h only REDIRECT_MALLOC; remove redundant +includes of string.h. +* mallocx.c: Include string.h (for GC_strdup). +* include/private/dbg_mlc.h (GC_store_debug_info): Move declaration +to dbg_mlc.c. +* include/private/gc_locks.h (UNCOND_LOCK, UNCOND_UNLOCK): Remove +redundant trailing ';'. +* include/private/gc_priv.h (START_WORLD, COND_DUMP): Likewise. +* include/private/gc_locks.h (LOCK, UNLOCK): Place opening '{' +properly. +* include/private/gc_priv.h (GC_DBG_RA): Move from dbg_mlc.c, +malloc.c, mallocx.c. + +* alloc.c (GC_check_heap, GC_print_all_smashed): Move the +definition from misc.c. +* dbg_mlc.c (GC_debug_malloc_atomic_uncollectable): Define as +public. +* include/gc.h (GC_debug_malloc_atomic_uncollectable): Declare. +* include/gc.h (GC_MALLOC_ATOMIC_UNCOLLECTABLE): Define new public +macro. +* dbg_mlc.c (MAX_SMASHED): Don't define if already set. +* reclaim.c (MAX_LEAKED): Likewise. +* dbg_mlc.c (GC_add_smashed): Add FIXME about the concurrent +access to the global array. +* reclaim.c (GC_add_leaked): Likewise. +* misc.c (GC_print_back_height): Set on if GC_PRINT_BACK_HEIGHT +(new macro) is defined. +* doc/README.macros (GC_PRINT_BACK_HEIGHT): Document. +* misc.c (GC_dump_regularly, GC_init): Replace 0/1 for +GC_dump_regularly and GC_print_back_height variables with +FALSE/TRUE. +* reclaim.c (GC_print_all_errors): Refine the comment. + +* tests/test.c (reverse_test_inner): Undo one of the previous +patches which shifts "c" and "d" pointers only if +ALL_INTERIOR_POINTERS (since interior pointers are always +recognized in stacks). + +* misc.c (GC_stdout, GC_stderr): Move the definition to the place +where GC_log is defined (Unix only). +* misc.c (GC_init): Recognize "GC_ONLY_LOG_TO_FILE" environment +variable and the similar macro; redirect GC_stdout and GC_stderr +to GC_log if "GC_LOG_FILE" environment variable is set unless +prohibited by GC_ONLY_LOG_TO_FILE (Unix only). +* doc/README.environment (GC_ONLY_LOG_TO_FILE): Document. +* doc/README.macros (GC_ONLY_LOG_TO_FILE): Likewise. + +* misc.c (GC_stdout, GC_write): Rename GC_stdout to GC_log (Win32 +only). +* misc.c (GC_write): Add for MacOS (and OS/2); change WRITE() +accordingly. +* misc.c (GC_printf): Check GC_quiet before va_start(). + +* allchblk.c (GC_freehblk): Use GC_log_printf instead of GC_printf +inside "if (GC_print_stats)" branch. +* alloc.c (GC_collect_or_expand): Likewise. +* dyn_load.c (GC_register_dynamic_libraries): Likewise. +* headers.c (GC_scratch_alloc): Likewise. +* os_dep.c (GC_get_maps, GC_remap, PROTECT, +GC_write_fault_handler, GC_dirty_init, GC_mprotect_thread): Likewise. +* alloc.c (min_bytes_allocd): Use GC_log_printf instead of +GC_printf for DEBUG_THREADS output. +* darwin_stop_world.c (GC_stack_range_for, GC_suspend_thread_list, +GC_stop_world, GC_thread_resume, GC_start_world): Likewise. +* pthread_start.c (GC_inner_start_routine): Likewise. +* pthread_stop_world.c (GC_suspend_handler, GC_restart_handler, +GC_push_all_stacks, GC_suspend_all, GC_stop_world, +GC_start_world): Likewise. +* pthread_support.c (GC_mark_thread, GC_get_nprocs, +GC_start_rtn_prepare_thread, pthread_create): Likewise. +* checksums.c (GC_update_check_page): Use GC_printf() instead of +GC_err_printf() for error printing. +* checksums.c (GC_check_blocks, GC_check_dirty): Use GC_log_printf +instead of GC_printf for logging purposes. +* dyn_load.c (sys_errlist, sys_nerr, errno): Move declaration of +external variable outside from GC_register_dynamic_libraries. +* dyn_load.c (GC_register_dynamic_libraries): Don't use +sys_errlist value if errno equals to sys_nerr. +* dyn_load.c (GC_register_dynamic_libraries): Use GC_log_printf +instead of GC_printf for DL_VERBOSE output. +* dyn_load.c (GC_dyld_image_add, GC_dyld_image_remove, +GC_init_dyld): Use GC_log_printf instead of GC_printf for +DARWIN_DEBUG output. +* os_dep.c (catch_exception_raise): Use GC_log_printf +instead of GC_printf for DEBUG_EXCEPTION_HANDLING output. +* reclaim.c (GC_print_free_list): Move "n" increment out of +GC_printf() call. + +* win32_threads.c (DEBUG_CYGWIN_THREADS, DEBUG_WIN32_PTHREADS, +DEBUG_WIN32_THREADS): Remove. +* win32_threads.c (GC_register_my_thread_inner, +GC_win32_start_inner): Use GC_log_printf instead of GC_printf +inside "if (GC_print_stats)" branch. +* win32_threads.c (GC_PTHREAD_PTRVAL): New macro (defined only if +GC_PTHREADS). +* win32_threads.c (GC_delete_gc_thread, NUMERIC_THREAD_ID, +GC_pthread_join, GC_pthread_create): Use GC_PTHREAD_PTRVAL +macro. +* win32_threads.c (GC_push_stack_for, GC_mark_thread, +GC_CreateThread, GC_beginthreadex, GC_pthread_join, +GC_pthread_create, GC_pthread_start_inner, GC_thread_exit_proc, +GC_mark_thread_local_free_lists): Use GC_log_printf instead of +GC_printf for DEBUG_THREADS output. +* win32_threads.c (GC_win32_start_inner, GC_CreateThread, +GC_beginthreadex, GC_pthread_join, GC_pthread_create, +GC_pthread_start_inner, GC_thread_exit_proc): Cast +GetCurrentThreadId result to long; don't cast value of pthread_t +type to int; adjust printf format specifiers. +* doc/README.win32 (DEBUG_WIN32_PTHREADS): Remove obsolete +information. + +* tests/test.c (cons, small_cons, gcj_cons, check_ints, +check_uncollectable_ints, print_int_list, check_marks_int_list, +fork_a_thread, finalizer, mktree, chktree, alloc8bytes, +alloc_small, tree_test, typed_test, check_heap_stats, WinMain, +test, main): Remove unnecessary casts of GC_printf calls to void. + +* allchblk.c (GC_print_hblkfreelist): Adjust (make uniform across +BDWGC) printed message (adjust letters case, terminating dot and +new line symbols). +* alloc.c (GC_check_fl_marks): Likewise. +* backgraph.c (new_back_edges): Likewise. +* checksums.c (GC_check_dirty): Likewise. +* darwin_stop_world.c (GC_push_all_stacks, +GC_suspend_thread_list): Likewise. +* dbg_mlc.c (GC_print_type, GC_debug_free, GC_debug_realloc, +store_old): Likewise. +* dyn_load.c (GC_register_dynamic_libraries): Likewise. +* mark.c (GC_initiate_gc, GC_mark_some, GC_mark_from, GC_push_all, +GC_push_selected, GC_push_next_marked_dirty): Likewise. +* mark_rts.c (GC_exclude_static_roots_inner): Likewise. +* os_dep.c (GC_remap, GC_default_push_other_roots, +GC_push_thread_structures, GC_dirty_init, GC_read_dirty, +catch_exception_raise_state, catch_exception_raise_state_identity, +GC_mprotect_thread_notify, GC_mprotect_thread, +catch_exception_raise): Likewise. +* pthread_stop_world.c (GC_print_sig_mask, GC_push_all_stacks, +GC_stop_world, GC_stop_init): Likewise. +* pthread_support.c (GC_thr_init, GC_register_my_thread_inner, +GC_start_routine): Likewise. +* win32_threads.c (GC_register_my_thread_inner, +GC_push_all_stacks, GC_win32_start_inner, GC_pthread_join, +GC_pthread_start_inner): Likewise. +* alloc.c (GC_expand_hp_inner): Realign the code. +* mark.c (GC_mark_from, GC_mark_local, GC_do_parallel_mark): +Likewise. +* misc.c (GC_init): Likewise. +* os_dep.c (GC_dirty_init, GC_read_dirty): Likewise. +* include/private/gc_pmark.h (PUSH_CONTENTS_HDR): Likewise. +* tests/test.c (run_one_test): Likewise. +* misc.c (GC_err_puts): Document. +* misc.c (GC_err_write): Remove. +* os_dep.c (dump_maps): Likewise. +* include/private/gc_priv.h (GC_err_write): Likewise. +* os_dep.c (GC_print_address_map): Call GC_err_puts() instead of +dump_maps() and GC_err_write(). +* os_dep.c (GC_read_dirty): Remove redundant brackets. + +* tests/test.c (reverse_test_inner): Test interior pointer +recognition only if ALL_INTERIOR_POINTERS. +* tests/test.c (run_one_test): Replace GC_all_interior_pointers +with GC_get_all_interior_pointers(); simplify the expression. +* tests/test.c (check_heap_stats): Replace GC_bytes_allocd and +GC_bytes_allocd_before_gc with GC_get_total_bytes(). +* tests/test.c (main): Replace GC_gc_no with GC_get_gc_no(). + +* dbg_mlc.c (GC_debug_strdup, GC_debug_free): Output a portability +warning if the argument is NULL and GC is in leaks detection mode. +* dbg_mlc.c (GC_debug_strndup, GC_debug_wcsdup): New public +function definition. +* malloc.c (GC_strndup, GC_wcsdup, strndup): Likewise. +* mallocx.c (GC_posix_memalign): Likewise. +* malloc.c (strdup): Fix string size value; rename "len" to "lb". +* mallocx.c: Include errno.h unless WinCE (otherwise include +windows.h for Win32 error constants). +* win32_threads.c: Define WIN32_LEAN_AND_MEAN and NOSERVICE before +windows.h inclusion. +* misc.c (GC_init): Register at-exit callback if GC_find_leak +(even if GC_FIND_LEAK macro is unset). +* pthread_stop_world.c (NACL_STORE_REGS, +__nacl_suspend_thread_if_needed, GC_nacl_initialize_gc_thread): +Use BCOPY() instead of memcpy(). +* pthread_support.c (GC_init_real_syms): Likewise. +* doc/README.macros (GC_DEBUG_REPLACEMENT, GC_REQUIRE_WCSDUP): +Document new macro. +* doc/README.macros (REDIRECT_MALLOC): Update documentation. +* include/gc.h (GC_strndup, GC_posix_memalign, GC_debug_strndup): +New API function prototype. +* include/gc.h (GC_MALLOC, GC_REALLOC): Redirect to +GC_debug_malloc/realloc_replacement() if GC_DEBUG_REPLACEMENT. +* include/gc.h (GC_STRDUP): Remove redundant parentheses. +* include/leak_detector.h (realloc, strdup): Likewise. +* include/gc.h (GC_STRNDUP): New API macro. +* include/gc.h (GC_NEW, GC_NEW_ATOMIC, GC_NEW_STUBBORN, +GC_NEW_UNCOLLECTABLE): Add missing parentheses. +* include/gc.h (GC_wcsdup, GC_debug_wcsdup): New API function +prototype (only if GC_REQUIRE_WCSDUP). +* include/gc.h (GC_WCSDUP): New API macro (only if +GC_REQUIRE_WCSDUP). +* include/leak_detector.h: Include stdlib.h and string.h after gc.h (unless +GC_DONT_INCLUDE_STDLIB). +* include/leak_detector.h (malloc, calloc, free, realloc): +Undefine symbol before its redefinition. +* include/leak_detector.h (strndup, memalign, posix_memalign): +Redefine to the corresponding GC function. +* include/leak_detector.h (wcsdup): Redefine to GC_WCSDUP (only +if GC_REQUIRE_WCSDUP). +* include/leak_detector.h (CHECK_LEAKS): Add comment; don't define +the macro if already defined. + +* misc.c (GC_abort): Use _exit() (instead of DebugBreak) on Win32 +when doing code static analysis (to inform the tool that the +function is a no-return one). +* os_dep.c (GC_linux_stack_base): Remove a duplicate validation +of the length of "stat" file; use signed int type for "i", +"buf_offset" and "len" local variables (since read() may +return -1). + +* blacklst.c (GC_bl_init_no_interiors): New function (the code +moved from GC_bl_init). +* blacklst.c (GC_bl_init): Invoke GC_bl_init_no_interiors unless +GC_all_interior_pointers mode; remove unnecessarily parameter cast +for GC_scratch_alloc call. +* include/private/gc_priv.h (GC_bl_init): Move the function +declaration to misc.c file. +* misc.c (GC_bl_init_no_interiors): Add a prototype. +* misc.c (GC_set_all_interior_pointers): Allow values other than 0 +and 1; allow altering GC_set_all_interior_pointers value even +after GC initialization. +* obj_map.c (GC_initialize_offsets): Clear GC_valid_offsets and +GC_modws_valid_offsets if GC_all_interior_pointers is off. +* misc.c (GC_init): Don't call GC_initialize_offsets() unless +GC_all_interior_pointers mode. + +* alloc.c (GC_finish_collection): Remove redundant brackets; +adjust code indentation. +* blacklst.c (GC_add_to_black_list_normal): Simplify expression +(to improve code readability). +* blacklst.c (GC_is_black_listed): Join nested "if" (into a single +conditional expression); initialize "nblocks" just before the loop +beginning. +* misc.c (GC_init): Don't compute initial_heap_sz if GC is already +initialized. +* include/private/gc_priv.h (GC_initialize_offsets): Move the +function declaration to misc.c file. +* obj_map.c (GC_initialize_offsets): Remove offsets_initialized +static variable since the function is called only once. +* tests/middle.c (main): Use setter for GC_all_interior_pointers; +adjust printf format specifier (and cast the value passed to). + +* doc/README.macros (SMALL_CONFIG, LARGE_CONFIG): Refine the +documentation. +* include/private/gc_hdrs.h (LOG_BOTTOM_SZ): Ignore SMALL_CONFIG +if LARGE_CONFIG is defined. +* include/private/gc_priv.h (CPP_LOG_HBLKSIZE): Likewise. + +* alloc.c (GC_finish_collection): Replace "#else #ifdef" with +"#elif". +* include/private/gc_priv.h (CPP_LOG_HBLKSIZE, LOG_PHT_ENTRIES, +MAX_ROOT_SETS, MAX_HEAP_SECTS): Likewise. +* alloc.c (GC_expand_hp_inner): Check for GC_collect_at_heapsize +overflow even if not LARGE_CONFIG. +* dbg_mlc.c (GC_check_heap_proc): Check "oh" size even if +SMALL_CONFIG. +* finalize.c (GC_print_finalization_stats): Fix "#endif" comment. +* doc/README.environment (GC_LOG_FILE, GC_PRINT_VERBOSE_STATS, +GC_FULL_FREQUENCY): Refine the documentation. + +* extra/msvc_dbg.c: Test _MSC_VER macro; include "gc.h" (for +GC_word). +* extra/msvc_dbg.c (ULONG_PTR): Replace with GC_ULONG_PTR; define +as word. + +* dbg_mlc.c (GC_get_back_ptr_info, GC_print_obj, +GC_print_smashed_obj, GC_debug_free_inner): Add a code for a +LINT-like tool to instruct it that the function is invoked only +with valid parameters (otherwise a SEGV is ok); recognize LINT2 +new macro. +* misc.c (GC_abort): Instruct a LINT-like tool that the function +never returns in fact. +* os_dep.c (GC_linux_stack_base): Check for read buffer overflow; +close the file immediately after read; use STRTOULL() instead of +decoding the address number manually. +* include/private/gc_priv.h (EXPECT): Don't specify outcome for a +LINT-like tool. +* include/private/gc_priv.h (GC_all_interior_pointers): Instruct a +LINT-like tool that the value is restricted to zero and one only +(required since the variable is global and its value is used as a +part of array index expression is some places). + +* dbg_mlc.c (GC_make_closure): Fix SEGV in case GC_malloc returns +NULL. +* dbg_mlc.c (GC_debug_register_finalizer, +GC_debug_register_finalizer_no_order, +GC_debug_register_finalizer_unreachable, +GC_debug_register_finalizer_ignore_self): Handle out of memory +case properly (similar to GC_register_finalizer_inner). +* headers.c (GC_install_header): Handle the case when alloc_hdr() +returns NULL. +* os_dep.c (GC_get_maps_len): Defend against missing "maps" file. +* pthread_support.c (GC_mark_thread): Place a dummy return +statement (which uses "id" argument) before the actual use of "id" +as an array index (to suppress a warning produced by some static +code analysis tools). +* win32_threads.c (GC_mark_thread): Likewise. +* pthread_support.c (GC_thr_init): Abort (with the appropriate +message) if out of memory. + +* finalize.c (GC_register_finalizer_inner): Fix a typo in a +comment. +*include/private/gcconfig.h (STACKBOTTOM): Likewise. +* gcj_mlc.c (GC_core_gcj_malloc): Replace 0/1 with TRUE/FALSE in +EXPECT (the 2nd argument). +* malloc.c (GC_core_malloc_atomic, GC_core_malloc, GC_free): +Likewise. +* mark.c (GC_mark_and_push, GC_mark_and_push_stack): Likewise. +* thread_local_alloc.c (GC_malloc, GC_malloc_atomic): Likewise. +* include/private/gc_hdrs.h (HC_GET_HDR): Likewise. +* include/private/gc_priv.h (SMALL_OBJ): Likewise. +* include/private/specific.h (getspecific): Likewise. +* pthread_support.c (LOCK_STATS): Add a comment. + +* include/gc_pthread_redirects.h (GC_NO_DLOPEN, +GC_NO_PTHREAD_SIGMASK, GC_PTHREAD_CREATE_CONST, +GC_PTHREAD_EXIT_ATTRIBUTE, GC_NO_PTHREAD_CANCEL): Move the +definition to gc_config_macros. + +* pthread_support.c (pthread_cancel, GC_pthread_cancel_t, +GC_pthread_cancel): Test GC_NO_PTHREAD_CANCEL (instead of NACL and +GC_PTHREAD_EXIT_ATTRIBUTE). +* include/gc_pthread_redirects.h (GC_pthread_cancel, +pthread_cancel): Likewise. +* pthread_support.c (GC_pthread_create, GC_pthread_sigmask, +GC_pthread_join, GC_pthread_detach, GC_pthread_cancel): Realign +code. +* include/gc_pthread_redirects.h (GC_PTHREAD_EXIT_ATTRIBUTE): +Define as empty for NaCl. +* include/gc_pthread_redirects.h (GC_NO_PTHREAD_CANCEL): New macro +defined. + +* dyn_load.c (GC_init_dyld): Do not invoke +_dyld_bind_fully_image_containing_address() if GC_no_dls (as it is +not required to register the main data segment in that case). +* include/gc.h (GC_no_dls): Adjust the comment. + +* dyn_load.c (GC_MUST_RESTORE_REDEFINED_DLOPEN): Test +GC_NO_DLOPEN. +* gc_dlopen.c: Likewise. +* include/gc_pthread_redirects.h (GC_dlopen, dlopen): Likewise. +* gc_dlopen.c: Don't include dlfcn.h (as it is included in +gc_pthread_redirects.h). +* pthread_support.c (pthread_sigmask, GC_pthread_sigmask_t, +GC_pthread_sigmask): Test GC_NO_PTHREAD_SIGMASK (instead of +GC_DARWIN_THREADS, GC_OPENBSD_THREADS and NACL). +* include/gc_pthread_redirects.h (GC_pthread_sigmask, +pthread_sigmask): Likewise. +* win32_threads.c (pthread_sigmask, GC_pthread_sigmask): Test +GC_NO_PTHREAD_SIGMASK (instead of GC_WIN32_PTHREADS). +* pthread_support.c (pthread_create, GC_pthread_create_t, +GC_pthread_create): Rename GC_PTHREAD_CONST to +GC_PTHREAD_CREATE_CONST. +* win32_threads.c (GC_pthread_create): Likewise. +* include/gc_pthread_redirects.h: Likewise. +* include/gc_pthread_redirects.h (GC_NO_DLOPEN, +GC_NO_PTHREAD_SIGMASK): New macro defined. +* include/gc_pthread_redirects.h (GC_PTHREAD_CREATE_CONST): Set to +empty for NaCl. +* include/gc_pthread_redirects.h (GC_PTHREAD_EXIT_ATTRIBUTE): Do +not define for Android (as CANCEL_SAFE is not defined). + +* include/gc.h (GC_ADD_CALLER, GC_RETURN_ADDR, +GC_HAVE_BUILTIN_BACKTRACE, GC_CAN_SAVE_CALL_STACKS): Move +definition to gc_config_macros.h file. +* include/gc_config_macros.h: Check the file is included from gc.h +file. +* include/gc_version.h: Likewise. + +* gc_dlopen.c: Empty unit for NaCl. +* os_dep.c: Include fcntl.h for NaCl. +* os_dep.c (GC_get_main_stack_base): Ignore +USE_GET_STACKBASE_FOR_MAIN macro for NaCl. +* os_dep.c (GC_get_stack_base): Return GC_UNIMPLEMENTED for NaCl. +* os_dep.c (GC_remap): Use mmap (instead of mprotect) for NaCl. +* pthread_start.c (GC_inner_start_routine): Don't invoke +pthread_cleanup_push/pop for NaCl. +* pthread_stop_world.c (GC_nacl_num_gc_threads, +GC_nacl_thread_idx, GC_nacl_park_threads_now, +GC_nacl_thread_parker, GC_nacl_gc_thread_self, +GC_nacl_thread_parked, GC_nacl_thread_used, +GC_nacl_thread_parking_inited, GC_nacl_thread_alloc_lock): New +variable (fo NaCl only). +* pthread_stop_world.c (GC_remove_allowed_signals, +suspend_handler_mask, GC_stop_count, GC_world_is_stopped, +GC_retry_signals, SIG_THR_RESTART, GC_suspend_ack_sem, +GC_restart_ack_sem, GC_suspend_handler_inner, GC_suspend_handler, +GC_restart_handler): Don't define for NaCl. +* pthread_support.c (GC_get_nprocs): Likewise. +* include/private/gc_priv.h (SIG_SUSPEND): Likewise. +* include/private/gcconfig.h (LINUX): Likewise. +* pthread_stop_world.c (GC_push_all_stacks): Push register storage +for NaCl. +* pthread_stop_world.c (GC_suspend_all, GC_stop_world, +GC_start_world): Implement for NaCl. +* pthread_stop_world.c (GC_stop_world): Don't define unused "i" +local variable for OpenBSD (and NaCl). +* pthread_stop_world.c (NACL_STORE_REGS): New macro definition for +NaCl. +* pthread_stop_world.c (nacl_pre_syscall_hook, +__nacl_suspend_thread_if_needed, nacl_post_syscall_hook, +GC_nacl_initialize_gc_thread, GC_nacl_shutdown_gc_thread): New +function (for NaCl only). +* pthread_stop_world.c (GC_stop_init): Empty for NaCl. +* pthread_support.c (pthread_cancel, pthread_sigmask): Don't +redirect for NaCl. +* include/gc_pthread_redirects.h (pthread_cancel, +pthread_sigmask): Likewise. +* pthread_support.c (GC_nacl_initialize_gc_thread, +GC_nacl_shutdown_gc_thread): New internal prototype (NaCl only). +* pthread_support.c (GC_new_thread, GC_delete_thread): Initialize +and shutdown thread for NaCl. +* pthread_support.c (GC_thr_init): Call sysconf for NaCl. +* pthread_support.c (GC_pthread_exit): Call GC_thread_exit_proc +for NaCl. +* include/gc.h: Don't include features.h for NaCl. +* include/gc_pthread_redirects.h (GC_PTHREAD_CONST): New macro. +* include/gc_pthread_redirects.h (GC_pthread_create): Use +GC_PTHREAD_CONST instead of const. +* win32_threads.c (GC_pthread_create): Likewise. +* pthread_support.c (GC_pthread_create_t, GC_pthread_create, +pthread_create): Likewise. +* include/private/gcconfig.h (NACL): Recognize NaCl. +* include/private/gcconfig.h (GC_LINUX_THREADS): Valid for NaCl. +* include/private/pthread_stop_world.h (thread_stop_info): Add +reg_storage member; define NACL_GC_REG_STORAGE_SIZE macro (for +NaCl only). +* include/private/pthread_support.h (GC_nacl_gc_thread_self): +Declare internal variable (for NaCl only). + +* mach_dep.c (GC_with_callee_saves_pushed): Fix FE_ALL_EXCEPT +macro. + +* mark.c (GC_mark_some): Prefix and suffix "asm" and "volatile" +keywords with double underscore. +* os_dep.c (catch_exception_raise, catch_exception_raise_state, +catch_exception_raise_state_identity): Add GC_API_OSCALL to +function definition. +* os_dep.c (catch_exception_raise_state, +catch_exception_raise_state_identity): Move definition to be +before GC_ports. +* os_dep.c (catch_exception_raise): Declare to have the symbol +defined before GC_ports. +* os_dep.c (GC_ports): Store references to catch_exception_raise, +catch_exception_raise_state, catch_exception_raise_state_identity +(to prevent stripping these symbols as dead). +* os_dep.c (catch_exception_raise, catch_exception_raise_state, +catch_exception_raise_state_identity): Mark these symbols as +"referenced dynamically" via an assembler directive (unless +NO_DESC_CATCH_EXCEPTION_RAISE). +* include/private/gc_priv.h (GC_API_OSCALL): New macro (defined +similar to GC_API but as if GC_DLL is always defined). + +* os_dep.c: Don't include signal.h for GC_write_fault_handler on +Win32. +* os_dep.c (SIG_OK): Don't return true unless SIGSEGV or SIGBUS on +FreeBSD. +* os_dep.c (CODE_OK): Use SEGV_ACCERR on FreeBSD (define +SEGV_ACCERR for older FreeBSD releases). + +* dyn_load.c (GC_register_map_entries, +GC_register_dynamic_libraries_dl_iterate_phdr): Calculate +DATASTART only once if DATASTART_IS_FUNC. +* dyn_load.c (GC_register_dynamic_libraries_dl_iterate_phdr): +Calculate DATAEND only once if DATAEND_IS_FUNC. +* dyn_load.c: Add comment to some endif; realign some code. +* dyn_load.c (GC_init_dyld): Don't use +_dyld_bind_fully_image_containing_address if +NO_DYLD_BIND_FULLY_IMAGE defined; add FIXME. +* include/private/gcconfig.h (GC_data_start, GC_find_limit): +Declare if used by DATASTART/DATAEND, respectively. +* include/private/gcconfig.h (DATASTART_IS_FUNC, DATAEND_IS_FUNC): +Define if DATASTART/DATAEND is a function, respectively. +* include/private/gcconfig.h (GETPAGESIZE, NO_PTHREAD_TRYLOCK, +NO_DYLD_BIND_FULLY_IMAGE): Define for Darwin/arm as well; include +unistd.h. + +* os_dep.c (GC_setpagesize, GC_task_self, PROTECT, UNPROTECT): +Reorder to remove redundant ifdef for Win32. +* os_dep.c: Add comment to some endif. +* os_dep.c: Include pthread.h (for Linux even if single-threaded) +if USE_GET_STACKBASE_FOR_MAIN; also include it for Darwin. +* os_dep.c (STACKBOTTOM): Redefine for Darwin (unless prohibited +for some reason). +* os_dep.c (GC_get_main_stack_base): Allow +USE_GET_STACKBASE_FOR_MAIN for Linux even if single-threaded; add +assertion for the returned result. +* os_dep.c (GC_get_stack_base): Define for Darwin if +multi-threaded. +* os_dep.c (SIG_OK, CODE_OK): Add comment (for FreeBSD). +* os_dep.c (ID_STOP, ID_RESUME): Define only if threads. +* os_dep.c (catch_exception_raise): Remove redundant parentheses; +refine the documentation. + +* NT_MAKEFILE: Define _CRT_SECURE_NO_DEPRECATE for C++ files as +well. +* NT_STATIC_THREADS_MAKEFILE: Likewise. +* doc/README.macros (USE_GET_STACKBASE_FOR_MAIN): Refine. +* include/gc.h (GC_INIT): Document. +* include/private/gc_priv.h (GC_MACH_HEADER, GC_MACH_SECTION, +GC_GETSECTBYNAME): Define depending only on the word size (i.e., +define these macros also for ARM). +* tests/test.c (check_heap_stats): Print main thread stack bottom +as well (only if verbose mode is on). + +* mach_dep.c (GC_with_callee_saves_pushed): Fix and improve code +introduced by the previous patch (if GETCONTEXT_FPU_EXCMASK_BUG +and X86_64). + +* darwin_stop_world.c (GC_FindTopOfStack): Prefix and suffix +"volatile" keyword with double underscore. +* mach_dep.c (GETCONTEXT_FPU_EXCMASK_BUG): Recognize new macro and +include fenv.h if defined (unless NO_GETCONTEXT or HAVE_PUSH_REGS). +* mach_dep.c (GC_with_callee_saves_pushed): Restore FPU exception +mask corrupted by getcontext if GETCONTEXT_FPU_EXCMASK_BUG. +* include/private/gcconfig.h (GETCONTEXT_FPU_EXCMASK_BUG): Define +for Linux/amd64 (since its GLibc getcontext currently has the bug). + +* allchblk.c (GC_use_entire_heap): Change type to int (as declared +in gc.h); set the default value depending on new GC_USE_ENTIRE_HEAP +macro. +* misc.c (GC_init): Test GC_USE_ENTIRE_HEAP environment variable to +alter the default value of GC_use_entire_heap. +* doc/README.environment (GC_USE_ENTIRE_HEAP): Document. +* doc/README.macros (GC_USE_ENTIRE_HEAP): Likewise. + +* include/private/gcconfig.h (PARALLEL_MARK): Do not make it cause +MPROTECT_VDB undefining. + +* include/private/gcconfig.h (DYNAMIC_LOADING): Fix filename in +the comment. + +* include/private/gc_priv.h (_GC_arrays): Move the conditional +macro definitions (shortcuts for GC_arrays members) into the +structure body. + +* darwin_stop_world.c (GC_mach_handler_thread, +GC_use_mach_handler_thread, +GC_darwin_register_mach_handler_thread): Define only if +MPROTECT_VDB. +* darwin_stop_world.c (GC_suspend_thread_list): Use +GC_mach_handler_thread and GC_use_mach_handler_thread only if +MPROTECT_VDB. +* darwin_stop_world.c (GC_stop_world): Reset GC_mach_threads_count +only if defined (i.e. unless GC_NO_THREADS_DISCOVERY). +* misc.c (GC_init): Fix comment for GWW_VDB. +* os_dep.c (DARWIN_EXC_STATE, DARWIN_EXC_STATE_COUNT, +DARWIN_EXC_STATE_T, DARWIN_EXC_STATE_DAR): New macros. +* os_dep.c (catch_exception_raise): Use DARWIN_EXC_STATE, +DARWIN_EXC_STATE_COUNT, DARWIN_EXC_STATE_T, DARWIN_EXC_STATE_DAR. +* pthread_support.c (GC_thr_init): Define "dummy" local variable +only unless GC_DARWIN_THREADS. +* include/private/gcconfig.h (MPROTECT_VDB): Define for Darwin +even in the single-threaded mode; define for iPhone/iPad. +* include/private/gcconfig.h (IA64): Remove unnecessary "ifdef" +around "undef". +* include/private/gcconfig.h (HEURISTIC1): Remove unused for +Cygwin. +* include/private/gcconfig.h (STACKBOTTOM): Use fixed address for +Darwin/arm (instead of HEURISTIC1). + +* misc.c (GC_write): Replace multiple "ifdef/endif" with "elif" +(for ECOS and NOSYS). +* os_dep.c (GC_get_main_stack_base): Likewise. +* os_dep.c (GC_get_main_stack_base): Check +USE_GET_STACKBASE_FOR_MAIN macro before checking STACKBOTTOM one; +remove "dummy" variable (use result one instead). +* doc/README.macros (SN_TARGET_PS3): Document. +* extra/threadlibs.c (main): Don't output "-lpthread" (and "-ldl") +for Android. +* include/private/pthread_support.h: Fix comment for "endif". + +* misc.c (GC_allocate_ml): Define global variable if SN_TARGET_PS3. +* misc.c (GC_init): Initialize GC_allocate_ml if SN_TARGET_PS3. +* os_dep.c (SIGSEGV): Define to dummy zero if SN_TARGET_PS3. +* os_dep.c (GC_unix_mmap_get_mem): Don't define if SN_TARGET_PS3. +* os_dep.c (GC_default_push_other_roots, +GC_push_thread_structures): Define for SN_TARGET_PS3. +* include/private/gc_locks.h (GC_allocate_ml, LOCK, UNLOCK): Define +for SN_TARGET_PS3. +* include/private/gcconfig.h (SN_TARGET_PS3): Recognize new macro +(Sony PS/3 target). +* include/private/gcconfig.h (THREADS): Define unconditionally if +SN_TARGET_PS3. +* include/private/gcconfig.h (GET_MEM): Define for SN_TARGET_PS3. + +* alloc.c (GC_collect_or_expand): Replace NIL with NULL in message. +* dbg_mlc.c (GC_debug_malloc, GC_debug_malloc_ignore_off_page, +GC_debug_malloc_atomic_ignore_off_page, +GC_debug_generic_malloc_inner, +GC_generic_malloc_inner_ignore_off_page, GC_debug_malloc_stubborn, +GC_debug_malloc_atomic, GC_debug_malloc_uncollectable, +GC_debug_malloc_atomic_uncollectable): Likewise. +* gcj_mlc.c (GC_debug_gcj_malloc): Likewise. +* dbg_mlc.c (GC_check_annotated_obj): Replace NIL with NULL in a +comment. +* dyn_load.c (GC_FirstDLOpenedLinkMap): Likewise. +* mark_rts.c (GC_roots_present): Likewise. +* doc/README: Likewise. +* include/private/gc_hdrs.h (IS_FORWARDING_ADDR_OR_NIL): Likewise. +* include/private/gc_priv.h (_GC_arrays): Likewise. + +* configure.ac: Use AC_CHECK_LIB() to check for pthread instead of +just blindly linking to -lpthread, as Android includes pthread +support within libc and does not provide a separate libpthread. +* dyn_load.c (GC_register_dynamic_libraries): Skip current link map +entry if l_addr is NULL (Android/bionic only). +* pthread_stop_world.c (android_thread_kill): New internal function +(Android only). +* pthread_stop_world.c (GC_suspend_all, GC_start_world): Call +android_thread_kill (based on tkill) instead of pthread_kill on +Android (since pthread_kill cannot be used safely on the platform). +* pthread_support.c (GC_new_thread): Store thread Id (obtained from +gettid) for use by android_thread_kill (Android only). +* include/private/pthread_support.h (GC_Thread_Rep): Add kernel_id +structure member (Android only). +* include/private/gcconfig.h: Recognize __x86_64 macro as a synonym +of __x86_64__ (Darwin); define __environ macro (Android on M68K). + +* allchblk.c (GC_freehblk): Print extended error message (done via +GC_printf() before aborting with a short message) only if +GC_print_stats. +* dyn_load.c (GC_register_dynamic_libraries): Likewise. +* os_dep.c (GC_get_maps, GC_register_data_segments, GC_remap, +PROTECT, GC_write_fault_handler, GC_mprotect_thread): Likewise. +* pthread_stop_world.c (GC_start_world): Likewise. +* win32_threads.c (GC_register_my_thread_inner): Likewise. +* os_dep.c (GC_get_main_stack_base, GC_register_data_segments, +GC_dirty_init): Remove redundant print of an error message before +aborting with the same message. +* os_dep.c (GC_register_data_segments): Remove format specifier +from the string passed to GC_err_puts(); use ABORT instead of EXIT +(if invalid executable type). +* os_dep.c (GC_remap): Adjust printf format specifier (for long +type). +* os_dep.c (GC_dirty_init): Print a message about SIG_IGN detected +(for SIGSEGV/BUS) only if GC_print_stats. +* os_dep.c (catch_exception_raise): Join 2 adjacent GC_err_printf +calls. + +* tests/test.c (main): Print the relevant message if GWW_VDB. +* include/private/gcconfig.h: Don't define MPROTECT_VDB for Win32 +on x64 if compiled by GCC. + +* tests/staticrootstest.c: Include string.h for memset() prototype. +* tests/thread_leak_test.c (main): Fix printf() format specifiers. + +* CMakeLists.txt: Check enable_parallel_mark on Darwin. +* configure.ac: Likewise. +* darwin_stop_world.c (DARWIN_SUSPEND_GC_THREADS, +DARWIN_QUERY_TASK_THREADS): Rename to GC_NO_THREADS_DISCOVERY and +GC_DISCOVER_TASK_THREADS, respectively. +* os_dep.c (DARWIN_SUSPEND_GC_THREADS): Likewise. +* pthread_support.c (DARWIN_SUSPEND_GC_THREADS): Likewise. +* darwin_stop_world.c (DARWIN_QUERY_TASK_THREADS): Don't define +(and remove FIXME). +* darwin_stop_world.c (GC_use_threads_discovery): Add GC_API; +comment; remove FIXME. +* win32_threads.c (GC_NO_DLLMAIN): Rename to +GC_NO_THREADS_DISCOVERY. +* tests/test.c (GC_NO_DLLMAIN): Likewise. +* doc/README.macros (GC_NO_DLLMAIN): Likewise. +* doc/README.win32 (GC_NO_DLLMAIN): Likewise. +* doc/README.macros (GC_NO_THREADS_DISCOVERY): Update the comment. +* win32_threads.c (GC_win32_dll_threads): Define as macro to true +if GC_DISCOVER_TASK_THREADS (and not GC_NO_THREADS_DISCOVERY); +update the comment. +* win32_threads.c (GC_use_DllMain): Rename to +GC_use_threads_discovery; do not set GC_win32_dll_threads if +GC_DISCOVER_TASK_THREADS. +* win32_threads.c (GC_started_thread_while_stopped, +GC_lookup_thread_inner, UNPROTECT_THREAD, GC_lookup_pthread, +GC_thr_init, GC_pthread_create, DllMain): Rewrite some expressions +which use GC_win32_dll_threads to minimize the possibility of +an "unreachable code" compiler warning when GC_win32_dll_threads +is defined as a macro. +* win32_threads.c (GC_unregister_my_thread): Don't call +GC_delete_thread() if GC_win32_dll_threads and THREAD_LOCAL_ALLOC +(since can't happen); use "t" local variable only if not +GC_win32_dll_threads. +* doc/README.macros (GC_DISCOVER_TASK_THREADS): Document. +* include/gc.h (GC_use_DllMain): Rename to +GC_use_threads_discovery but keep old name as a macro definition. +* include/gc.h (GC_use_threads_discovery): Declare also for +Darwin; update the comment. +* tests/test.c (main): Call GC_use_threads_discovery for Darwin +(to test the mode if possible). + +* darwin_stop_world.c (DARWIN_SUSPEND_GC_THREADS, +DARWIN_QUERY_TASK_THREADS): New macro recognized. +* darwin_stop_world.c (GC_query_task_threads): add STATIC; +initialize to false; define as macro if DARWIN_SUSPEND_GC_THREADS +or DARWIN_QUERY_TASK_THREADS; remove FIXME. +* darwin_stop_world.c (GC_use_threads_discovery): New function +(for setting GC_query_task_threads value). +* darwin_stop_world.c (GC_mach_handler_thread, +GC_use_mach_handler_thread, GC_mach_thread, GC_MAX_MACH_THREADS, +GC_mach_threads, GC_mach_threads_count, GC_suspend_thread_list, +GC_darwin_register_mach_handler_thread): Define only if not +DARWIN_SUSPEND_GC_THREADS. +* darwin_stop_world.c (GC_stop_world, GC_start_world): Exclude +the code for GC_query_task_threads case from compilation unless +DARWIN_SUSPEND_GC_THREADS. +* os_dep.c (GC_darwin_register_mach_handler_thread): Declared only +if Darwin threads and not DARWIN_SUSPEND_GC_THREADS. +* os_dep.c (GC_mprotect_thread): Call +GC_darwin_register_mach_handler_thread only if THREADS and not +DARWIN_SUSPEND_GC_THREADS. +* pthread_support.c (marker_mach_threads): Don't define if +DARWIN_SUSPEND_GC_THREADS. +* pthread_support.c (GC_mark_thread): Don't fill in +marker_mach_threads if DARWIN_SUSPEND_GC_THREADS. +* include/private/gc_locks.h (GC_need_to_lock): Always declare for +THREADS case. + +* darwin_stop_world.c (GC_query_task_threads): Don't define to +false for DARWIN_DONT_PARSE_STACK case; unconditionally initialize +the variable to false (for now). +* darwin_stop_world.c (GC_push_all_stacks): Call task_threads() +only if not DARWIN_DONT_PARSE_STACK. +* darwin_stop_world.c (GC_stop_world, GC_start_world): Use the +approach based on task_threads() only if GC_query_task_threads +else use GC_threads table. + +* darwin_stop_world.c (GC_mach_threads): Remove static qualifier. +* darwin_stop_world.c (GC_stop_init): Remove (as we do not need to +really clear GC_mach_threads[]). +* darwin_stop_world.c (GC_stop_world): Reset GC_mach_threads_count +(instead of calling GC_stop_init). +* include/private/pthread_support.h (GC_stop_init): Remove proto. +* pthread_support.c (GC_stop_init): Add proto (unless Darwin). +* pthread_support.c (GC_thr_init): Don't call GC_stop_init() if +GC_DARWIN_THREADS. + +* darwin_stop_world.c (GC_stack_range_for): New static function +(move the code from GC_push_all_stacks). +* darwin_stop_world.c (GC_push_all_stacks): Call +GC_stack_range_for(); rename kern_return local variable to +kern_result. +* darwin_stop_world.c (GC_is_mach_marker): Change argument type +from mach_port_t to thread_act_t. +* pthread_support.c (GC_is_mach_marker): Likewise. + +* darwin_stop_world.c (GC_push_all_stacks): Fix "my_task" local +variable initialization (always call current_task()). +* pthread_support.c (GC_thr_init, GC_register_my_thread_inner): +Don't set thread's stop_info.stack_ptr value for Darwin. +* include/private/darwin_stop_world.h (thread_stop_info): Update +the comment for stack_ptr. + +* darwin_stop_world.c (GC_push_all_stacks): Rename "r", "me" local +variables to "kern_return" and "my_thread" ones, respectively; +call mach_port_deallocate() unconditionally. +* darwin_stop_world.c (GC_stop_world): Don't call mach_thread_self +if DEBUG_THREADS. + +* darwin_stop_world.c (GC_mach_thread): Move from +darwin_stop_world.h. +* include/private/darwin_stop_world.h (GC_mach_thread): Remove. +* win32_threads.c (GC_start_world): Define "thread_id" local +variable only if GC_ASSERTIONS; decide whether to resume a thread +based on its "suspended" field value; assert that suspended thread +stack_base is non-zero and the thread is not our one. + +* darwin_stop_world.c (GC_thread_resume): New inline function +(move code from GC_thread_resume). +* darwin_stop_world.c (GC_start_world): Check result of +task_threads(); call GC_thread_resume(). +* os_dep.c (GC_malloc_heap_l, GC_is_malloc_heap_base): Define +only if not CYGWIN32. +* os_dep.c (GC_is_heap_base): Call GC_is_malloc_heap_base() only +if not CYGWIN32. + +* darwin_stop_world.c (FindTopOfStack): Change return type to +ptr_t (from long); make GC_INNER; add GC_ prefix. +* darwin_stop_world.c (GC_push_all_stacks): Add thread_blocked +local variable (initialized from the corresponding GC_thread +field unless GC_query_task_threads); add assertion that our +thread is not blocked; prefix FindTopOfStack with GC_ and remove +no longer needed cast to ptr_t of the result; handle thread +blocked case (and remove FIXME); use GC_push_all_stack_sections +unless GC_query_task_threads (and remove FIXME). +* pthread_support.c (GC_FindTopOfStack): Declare (if needed). +* pthread_support.c (GC_do_blocking_inner): Call +GC_save_regs_in_stack (if needed) before acquiring the lock. +* win32_threads.c (GC_do_blocking_inner): Likewise. +* pthread_support.c (GC_do_blocking_inner): Set/clear topOfStack +field of GC_thread (Darwin only). +* include/private/pthread_support.h (GC_thread): Add topOfStack +field for Darwin (unless DARWIN_DONT_PARSE_STACK). + +* finalize.c (GC_check_finalizer_nested): Change return type to +char pointer (instead of int pointer); use explicit cast for +GC_finalizer_nested assignment. +* pthread_support.c (GC_check_finalizer_nested): Likewise. +* win32_threads.c (GC_check_finalizer_nested): Likewise. +* finalize.c (GC_finalizer_nested): Change type to unsigned char. +* finalize.c (GC_notify_or_invoke_finalizers): Change type of +"pnested" local variable to char pointer. +* pthread_support.c (GC_do_blocking_inner, +GC_call_with_gc_active): Use explicit cast for "thread_blocked" +field assignment. +* win32_threads.c (GC_lookup_pthread): Use explicit cast for +"suspended" field assignment. +* win32_threads.c (GC_Thread_Rep): Use short type for +finalizer_skipped; use char type for finalizer_nested and flags +fields and reorder some fields (to minimize GC_Thread_Rep +structure size). +* include/private/pthread_support.h (GC_Thread_Rep): Likewise. +* win32_threads.c (GC_Thread_Rep): Use char type for suspended +field (instead of GC_bool). +* include/private/pthread_support.h (GC_Thread_Rep): Use char type +for thread_blocked field (instead of short). + +* darwin_stop_world.c (GC_query_task_threads): New variable (or +macro). +* darwin_stop_world.c (GC_push_all_stacks): Use +GC_query_task_threads (to choose between algorithms based on +kernel task_threads and based on GC_threads table); update FIXME; +remove commented out GC_push_one statements. +* pthread_support.c (GC_thr_init, GC_do_blocking_inner, +GC_call_with_gc_active, GC_register_my_thread_inner): Initialize +stack_ptr field for all platforms. +* pthread_support.c (GC_call_with_gc_active): Initialize +saved_stack_ptr field for all platforms. +* include/private/darwin_stop_world.h (thread_stop_info): Add +stack_ptr field; change type of already_suspended from int to +GC_bool. + +* darwin_stop_world.c (GC_MAX_MACH_THREADS): New macro. +* darwin_stop_world.c (GC_mach_threads, GC_stop_init): Use +GC_MAX_MACH_THREADS instead of THREAD_TABLE_SZ. +* darwin_stop_world.c (GC_mach_threads): Add FIXME. +* darwin_stop_world.c (GC_stop_init, GC_suspend_thread_list, +GC_stop_world): Use FALSE and TRUE for already_suspended field and +"changed", "found" variables. +* darwin_stop_world.c (GC_is_mach_marker): New prototype (only if +PARALLEL_MARK). +* darwin_stop_world.c (GC_suspend_thread_list): Change return type +to GC_bool; change type of "changed", "found" to GC_bool; make +"my_thread" as an argument (instead of acquiring/deallocating it +locally); do not add my_thread, GC_mach_handler_thread and marker +threads to GC_mach_threads table; check for overflow of +GC_mach_threads table; increase GC_mach_threads_count if "found" +is true and info.suspend_count is non-zero. +* darwin_stop_world.c (GC_suspend_thread_list, GC_start_world): +Adjust "thread" format specifiers for GC_printf(); search thread +in "old_list" starting from the previous found one. +* darwin_stop_world.c (GC_stop_world): Rename "changes" to +"changed" local variable; remove "result" variable; adjust +GC_printf debugging message. +* darwin_stop_world.c (GC_start_world): Do not check for +my_thread and GC_use_mach_handler_thread (since they are not added +to GC_mach_threads table); call thread_info() only if +DEBUG_THREADS or GC_ASSERTIONS. +* pthread_support.c (marker_mach_threads): New static variable (if +Darwin). +* pthread_support.c (GC_is_mach_marker): New function (if Darwin). +* pthread_support.c (GC_mark_thread): Fill in marker_mach_threads +table (if Darwin). + +* alloc.c (GC_parallel): Define only if THREADS. +* misc.c (GC_get_parallel): Likewise. +* include/gc.h (GC_parallel, GC_get_parallel, +GC_get_suspend_signal, GC_allow_register_threads, +GC_register_my_thread, GC_unregister_my_thread): Define only if +GC_THREADS. +* include/gc.h (GC_get_heap_size): Fix a typo in a comment. + +* configure.ac: Use `AC_C_INLINE'. +* include/private/gc_priv.h (GC_INLINE): Use "inline" keyword +(determined by configure AC_C_INLINE) if HAVE_CONFIG_H is defined. + +* dyn_load.c (DL_ITERATE_PHDR_STRONG): New macro (define for +FreeBSD). +* dyn_load.c (GC_register_main_static_data): Move the definition +above GC_register_dynamic_libraries_dl_iterate_phdr one (FreeBSD +case); unconditionally return FALSE if DL_ITERATE_PHDR_STRONG. +* dyn_load.c (GC_register_dynamic_libraries_dl_iterate_phdr): Test +GC_register_main_static_data() result (instead of direct testing +of dl_iterate_phdr (to prevent a compiler warning). +* os_dep.c (CODE_OK): Test si_code also for the value of 2 +(FreeBSD case; required for FreeBSD v7+). +* os_dep.c (CODE_OK): Properly use parentheses (HPUX case). +* include/private/gcconfig.h (DATASTART): Cast etext argument in +GC_FreeBSDGetDataStart() call; remove unnecessary "&" (FreeBSD +case). + +* include/private/specific.h (quick_thread_id): Define thru +GC_approx_sp(); define as a macro. +* include/private/specific.h (getspecific): Use GC_INLINE instead +of __inline__ (to work around Sun CC which does not recognize +inline keyword surrounded with underscores). + +* darwin_stop_world.c (FindTopOfStack): Simplify condition +expressions. +* darwin_stop_world.c (GC_push_all_stacks): Merge two variants +of this function (DARWIN_DONT_PARSE_STACK). +* darwin_stop_world.c (GC_push_all_stacks): Add a check for our +thread is found (same as in pthread_stop_world.c). +* darwin_stop_world.c (GC_push_all_stacks): Print the number of +scanned threads if verbose (same as in pthread_stop_world.c). + +* darwin_stop_world.c (GC_push_all_stacks): Reset +thread_state_count value before every thread_get_state call; +refine the comment for thread_state_count. +* darwin_stop_world.c (GC_push_all_stacks): Ignore rsp, rip/eip, +rflags, cs, fs, gs, ss, ds, es, __pc registers; uncomment ebp +register pushing. +* darwin_stop_world.c (GC_push_all_stacks): Set outCount to +GC_MACH_THREAD_STATE_COUNT (instead of THREAD_STATE_MAX). +* darwin_stop_world.c (GC_push_all_stacks): Remove FIXME and WARN +for i386. + +* doc/README.macros (DARWIN_DONT_PARSE_STACK): Fix a typo. +* darwin_stop_world.c (GC_use_mach_handler_thread): Change type +to GC_bool. +* darwin_stop_world.c (GC_suspend_thread_list, GC_start_world): +Simplify the expressions involving GC_use_mach_handler_thread. +* darwin_stop_world.c (GC_darwin_register_mach_handler_thread): +Initialize GC_use_mach_handler_thread to TRUE (instead of 1). + +* include/gc_pthread_redirects.h (GC_pthread_sigmask, GC_dlopen, +pthread_sigmask, dlopen): Don't define for Win32 pthreads (and +don't include signal.h and dlfcn.h). + +* dyn_load.c (GC_register_dynlib_callback): Add FIXME. + +* include/private/gcconfig.h: Add support for FreeBSD on ppc64. + +* os_dep.c (PROTECT, UNPROTECT): Correct VM_PROT_EXEC to +VM_PROT_EXECUTE. + +* os_dep.c (os2_alloc): Don't set PAG_EXECUTE unless +pages_executable is on. +* os_dep.c (os2_alloc): Add FIXME (for recursion). +* os_dep.c (UNPROTECT): Abort with a more informative message if +pages_executable is on ("mprotect" case). +* os_dep.c (PROTECT, UNPROTECT): Set VM_PROT_EXEC if +pages_executable is on (Darwin case). +* pthread_support.c (GC_init_real_syms): Abort with an informative +message if libgc is linked after libpthread. + +* dyn_load.c (GC_register_dynlib_callback): Adjust "start" pointer +for 64-bit targets. +* pthread_support.c (start_mark_threads): Expand PTHREAD_CREATE +macro. +* pthread_support.c (start_mark_threads): Call INIT_REAL_SYMS() +since REAL(pthread_create) is used. +* pthread_support.c (PTHREAD_CREATE): Remove unused. + +* extra/threadlibs.c (main): Remove --wrap for "read" (since not +wrapped anymore). +* doc/README.linux (GC_USE_LD_WRAP): Likewise. +* os_dep.c (__wrap_read): Likewise. + +* include/gc_pthread_redirects.h: Test GC_PTHREADS and GC_H at the +beginning of the file. +* include/gc_pthread_redirects.h (GC_PTHREAD_EXIT_ATTRIBUTE): New +macro (defined only for Linux and Solaris). +* include/gc_pthread_redirects.h (GC_pthread_cancel, +GC_pthread_exit): Declare new API function (only if +GC_PTHREAD_EXIT_ATTRIBUTE). +* include/gc_pthread_redirects.h (pthread_cancel, pthread_exit): +Redirect (if GC_PTHREAD_EXIT_ATTRIBUTE). +* include/private/pthread_support.h (DISABLED_GC): New macro. +* pthread_support.c (pthread_cancel, pthread_exit): Restore +original definition or declare "real" function (if needed and +GC_PTHREAD_EXIT_ATTRIBUTE). +* pthread_support.c (GC_pthread_cancel_t, GC_pthread_exit_t): +Declare new types if needed. +* pthread_support.c (GC_pthread_cancel, GC_pthread_exit): New +function definition (only if GC_PTHREAD_EXIT_ATTRIBUTE). +* pthread_support.c (GC_init_real_syms): Initialize pointers to +the "real" pthread_cancel and pthread_exit (only if +GC_PTHREAD_EXIT_ATTRIBUTE). +* pthread_support.c (GC_unregister_my_thread): Enable collections +if DISABLED_GC was set (only if GC_PTHREAD_EXIT_ATTRIBUTE). +* pthread_support.c (pthread_cancel, pthread_exit): New wrapped +function definition (only if GC_PTHREAD_EXIT_ATTRIBUTE defined). +* pthread_support.c (GC_start_routine): Refine the comment. +* extra/threadlibs.c (main): Adjust --wrap (add "read", +"pthread_exit", "pthread_cancel" but remove "sleep"). +* doc/README.linux (GC_USE_LD_WRAP): Likewise. + +* include/gc.h (GC_MALLOC_STUBBORN): Remove trailing ';' in the +macro definition. +* include/gc.h (GC_reachable_here): Likewise. +* include/gc.h (GC_reachable_here): Prefix and postfix "volatile" +with double '_'. + +* pthread_start.c: New file. +* CMakeLists.txt (SRC): Add pthread_start.c. +* Makefile.am (libgc_la_SOURCES): Likewise. +* Makefile.direct (CSRCS): Likewise. +* Makefile.direct (OBJS): Add pthread_start.obj. +* extra/gc.c: Add a comment; include pthread_start.c. +* pthread_support.c (start_info): Move the struct definition down +closer to its usage. +* pthread_support.c (GC_thread_exit_proc): Replace STATIC with +GC_INNER. +* pthread_support.c (GC_inner_start_routine): Move to the +definition to pthread_start.c; leave only the prototype; remove +STATIC. +* pthread_support.c (GC_start_rtn_prepare_thread): New function +(contains parts of the original GC_inner_start_routine). + +* configure.ac (NO_EXECUTE_PERMISSION): Add comment. +* doc/README.macros (NO_EXECUTE_PERMISSION): Update the +documentation. +* include/gc.h (GC_set_pages_executable, GC_get_pages_executable): +New API function declaration. +* os_dep.c (OPT_PROT_EXEC): Remove (superseded by +pages_executable). +* os_dep.c (pages_executable): New static variable. +* os_dep.c (IGNORE_PAGES_EXECUTABLE): New macro (used by +GC_get_pages_executable only). +* os_dep.c (GC_unix_mmap_get_mem, GC_remap, PROTECT, UNPROTECT): +Replace OPT_PROT_EXEC with pages_executable. +* os_dep.c (GC_unix_mmap_get_mem, GC_remap, GC_win32_get_mem, +GC_wince_get_mem, UNPROTECT): Undefine IGNORE_PAGES_EXECUTABLE. +* os_dep.c (GC_win32_get_mem, GC_wince_get_mem, GC_remap, PROTECT, +UNPROTECT): Use PAGE_EXECUTE_... only if pages_executable is on. +* os_dep.c (GC_set_pages_executable, GC_get_pages_executable): New +API function definition. + +* tests/test.c (check_heap_stats): Increase max_heap_sz by 20% for +64-bit CPUs (to prevent "Unexpected heap growth" failure on Win64, +at least). + +* tests/test.c (check_heap_stats): Increase max_heap_sz by 25% for +32-bit CPUs (to prevent "Unexpected heap growth" failure). + +* gc_dlopen.c (dlopen): Prototype REAL_DLFUNC if GC_USE_LD_WRAP. +* pthread_support.c (pthread_create, pthread_join, pthread_detach, +pthread_sigmask): Likewise. +* gc_dlopen.c (dlopen): Remove cast (redundant since the prototype +is added). +* gc_dlopen.c (GC_dlopen): Fix return type. +* pthread_support.c (GC_init_real_syms): Don't define +LIBPTHREAD_NAME, LIBPTHREAD_NAME_LEN, len, namebuf and +libpthread_name if RTLD_NEXT. + +* gc_dlopen.c (disable_gc_for_dlopen): Update the comment. +* gc_dlopen.c (dlopen): Likewise. +* include/gc.h (GC_enable_incremental): Refine the comment. +* include/gc.h (DECLSPEC_NORETURN): Define macro as empty if +missing (only for Win32). +* include/gc.h (GC_ExitThread): Use DECLSPEC_NORETURN. +* win32_threads.c (GC_ExitThread): Likewise. +* include/gc.h (GC_endthreadex): Add a comment. + +* include/cord.h: Fix typos. + +* Makefile.am (EXTRA_DIST): Add "CMakeLists.txt" and +"tests/CMakeLists.txt". +* doc/doc.am (dist_pkgdata_DATA): Add "doc/README.cmake". + +* mach_dep.c (NO_GETCONTEXT): Also define if AVR32. +* include/private/gcconfig.h (AVR32): New macro (also define the +supplementary macros for the target). +* include/private/thread_local_alloc (USE_COMPILER_TLS): Don't +define for AVR32. + +* tests/leak_test.c (main): Explicitly define as returning int +(to prevent a spurious test failure on some Linux/alpha targets). +* tests/thread_leak_test.c (main): Likewise. +* tests/thread_leak_test.c: Initialize GC_find_leak in the main +thread (before GC_INIT) only. +* tests/leak_test.c (main): Use GC_set_find_leak() instead of +accessing GC_find_leak directly. +* tests/thread_leak_test.c (main): Likewise. + +* include/gc.h (GC_find_leak, GC_finalize_on_demand, +GC_java_finalization, GC_dont_expand, GC_no_dls, +GC_dont_precollect): Simplify the comment (remove the information +about data races since the value is boolean). + +* os_dep.c (GC_get_stack_base, GC_get_main_stack_base): New +Solaris-specific implementation (based on thr_stksegment). +* os_dep.c (stackbase_main_self, stackbase_main_ss_sp): New static +variable used by the Solaris-specific GC_get_stack_base(). + +* pthread_support.c (GC_mark_thread_local_free_lists, +GC_check_tls): Mark (and check) only for live threads (in case of +GC_destroy_thread_local() is called already but GC_delete_thread() +is not yet). +* win32_threads.c (GC_mark_thread_local_free_lists, GC_check_tls): +Likewise. + +* NT_MAKEFILE: Remove the comment about DLL and Win32S. +* NT_STATIC_THREADS_MAKEFILE: Likewise. +* NT_X64_STATIC_THREADS_MAKEFILE: Likewise. +* NT_MAKEFILE: Add ".SUFFIXES" directive (to handle gc_cpp.cc +properly on VS 2005+). +* NT_MAKEFILE: Update GC log file name in comments. +* NT_STATIC_THREADS_MAKEFILE: Likewise. +* NT_X64_STATIC_THREADS_MAKEFILE: Likewise. +* NT_X64_THREADS_MAKEFILE: Likewise. +* doc/README.win32: Likewise. +* NT_MAKEFILE: Remove ":full" for "-debug" option (since no +longer supported by VS). +* NT_STATIC_THREADS_MAKEFILE: Likewise. +* NT_MAKEFILE: Commented out copying of gc_cpp.cc to gc_cpp.cpp. +* NT_STATIC_THREADS_MAKEFILE: Likewise. +* NT_X64_STATIC_THREADS_MAKEFILE: Likewise. +* NT_X64_THREADS_MAKEFILE: Likewise. +* NT_STATIC_THREADS_MAKEFILE: Add -D PARALLEL_MARK option. +* NT_STATIC_THREADS_MAKEFILE: Increase stack size for gctest.exe. +* NT_X64_STATIC_THREADS_MAKEFILE: Remove "-stack" option (use the +default stack size limit). +* NT_X64_THREADS_MAKEFILE: Rename "gc64_dll.dll" to "gc64.dll". +* win32_threads.c (GC_get_next_stack): Always define (since it is +also used for Cygwin now). + +* alloc.c (GC_maybe_gc): Move GC_notify_full_gc() call upper to +be just before GC_clear_marks() call. +* include/gc_mark.h (GC_start_callback_proc): Refine the comment. + +* Makefile.am (check_LTLIBRARIES): Initialize to empty. +* tests/tests.am (TESTS, check_PROGRAMS): Add staticrootstest. +* tests/tests.am (staticrootstest_SOURCES, staticrootstest_LDADD, +libstaticrootslib_la_SOURCES, libstaticrootslib_la_LIBADD, +libstaticrootslib_la_LDFLAGS, libstaticrootslib_la_DEPENDENCIES): +Define. +* tests/tests.am (check_LTLIBRARIES): Add libstaticrootslib.la. + +* tests/staticrootstest.c: New file. +* tests/staticrootslib.c: Likewise. + +* dyn_load.c (GC_get_next_stack, GC_cond_add_roots): Define for +Cygwin as well as other win32 targets. +* dyn_load.c (GC_wnt): Define to constant true. +* dyn_load.c (GC_register_dynamic_libraries): Define for Cygwin as +well as other win32 targets. +* mark_rts.c (rt_hash, GC_roots_present, add_roots_to_index): +Don't define for Cygwin, as on other win32. +* mark_rts.c (GC_add_roots_inner, GC_clear_roots): Handle on +Cygwin as for other win32 targets. +* mark_rts.c (GC_rebuild_root_index): Don't declare on Cygwin, as +other win32. +* mark_rts.c (GC_remove_tmp_roots): Do declare on Cygwin as on +other win32. +* mark_rts.c (GC_remove_roots, GC_remove_roots_inner): Don't +declare on Cygwin as on other win32. +* mark_rts.c (GC_is_tmp_root): Do declare on Cygwin when +!NO_DEBUGGING, as on other win32 targets. +* mark_rts.c (GC_cond_register_dynamic_libraries): Handle on +Cygwin as for other win32 targets. +* os_dep.c (GC_setpagesize): Handle on Cygwin as on other win32. +* os_dep.c (GC_get_main_stack_base): Don't declare on Cygwin, as +other win32. +* os_dep.c (GC_sysinfo): Declare on Cygwin, as other win32. +* os_dep.c (GC_win32_get_mem): Declare on Cygwin, as on other +Win32, but call GC_unix_get_mem instead of GlobalAlloc. +* os_dep.c (GC_win32_free_heap): Declare on Cygwin (as empty). +* ptr_chck.c (GC_is_visible): Register dynamic libraries on Cygwin +as on other win32 platforms. +* win32_threads.c (GC_get_next_stack): Define on Cygwin as well as +for dynamic loading targets. +* include/private/gc_priv.h (GC_INNER): Don't try to use +visibility on Cygwin which does not support it. +* include/private/gc_priv.h (struct roots): Don't declare r_next +member on Cygwin as on other windows hosts. +* include/private/gc_priv.h (LOG_RT_SIZE, RT_SIZE): Don't define +likewise. +* include/private/gc_priv.h (struct _GC_arrays): Do declare +_heap_bases[] member and don't declare _root_index likewise. +* include/private/gc_priv.h (GC_heap_bases): Do define likewise. +* include/private/gc_priv.h (_SYSTEM_INFO): Do forward-declare +likewise. +* include/private/gc_priv.h (GC_sysinfo): Do declare extern +likewise. +* include/private/gcconfig.h (GC_win32_get_mem, GET_MEM): Do +prototype on Cygwin as other win32 platforms. + +* os_dep.c (GC_get_main_stack_base): Use pthread_getattr_np() and +pthread_attr_getstack() instead of GC_get_stack_base() (and check +returned stackaddr for NULL); output a warning on failure. + +* alloc.c (GC_start_call_back): Replace the definition type to +GC_start_callback_proc. +* alloc.c (GC_set_start_callback, GC_get_start_callback): New +setter/getter function. +* alloc.c (GC_try_to_collect_inner): Call GC_notify_full_gc() +unconditionally (because GC_try_to_collect_inner always does full +GC). +* include/gc_mark.h (GC_start_callback_proc): New type. +* include/gc_mark.h (GC_set_start_callback, +GC_get_start_callback): New API function declaration. + +* doc/README.macros (USE_GET_STACKBASE_FOR_MAIN): Document. +* os_dep.c (GC_get_main_stack_base): Recognize +USE_GET_STACKBASE_FOR_MAIN (only if THREADS and LINUX_STACKBOTTOM) +and use GC_get_stack_base() in this case. + +* os_dep.c (GC_get_stack_base): Add LOCK/UNLOCK() (since +GC_find_limit_with_bound() should be called with the lock held). +* backgraph.c (FOR_EACH_PRED): Fix a typo. + +* alloc.c (GC_set_stop_func, GC_get_stop_func): Add +DCL_LOCK_STATE. +* finalize.c (GC_notify_or_invoke_finalizers): Likewise. +* gc_dlopen.c (disable_gc_for_dlopen): Likewise. +* gcj_mlc.c (maybe_finalize, GC_debug_gcj_malloc): Likewise. +* mark.c (GC_print_trace): Likewise. +* misc.c (GC_set_warn_proc, GC_get_warn_proc, GC_enable, +GC_disable, GC_new_free_list, GC_new_kind, GC_new_proc, +GC_set_oom_fn, GC_get_oom_fn, GC_set_finalizer_notifier, +GC_get_finalizer_notifier): Likewise. +* os_dep.c (GC_get_stack_base, GC_print_callers): Likewise. +* pthread_support.c (GC_is_thread_tsd_valid, +GC_wait_for_gc_completion, GC_init_parallel, GC_do_blocking_inner, +GC_call_with_gc_active, GC_unregister_my_thread, pthread_join, +pthread_detach, GC_register_my_thread, GC_inner_start_routine, +pthread_create): Likewise. +* reclaim.c (GC_print_all_errors): Likewise. +* win32_threads.c (GC_is_thread_tsd_valid, GC_register_my_thread, +GC_unregister_my_thread, GC_do_blocking_inner, +GC_call_with_gc_active, GC_lookup_pthread, GC_pthread_join, +GC_pthread_start_inner, GC_thread_exit_proc, GC_pthread_detach, +GC_init_parallel): Likewise. + +* doc/README.darwin: Update. + +* CMakeLists.txt: Adjust INCLUDE_DIRECTORIES and SRC (to make it +usable on Mac OS X). +* doc/README.cmake: Update. + +* CMakeLists.txt: New file (adding CMake support). +* tests/CMakeLists.txt: Likewise. +* doc/README.cmake: Likewise. + +* configure.ac (darwin): Don't define HAS_PPC_THREAD_STATE... +macros. +* include/private/gc_priv.h (THREAD_FLD): Recognize +__DARWIN_UNIX03 instead of HAS_PPC_THREAD_STATE... macros. + +* pthread_support.c: Include and for +OpenBSD. +* pthread_support.c (get_ncpu): Define also for Darwin, NetBSD and +OpenBSD. +* pthread_support.c (GC_thr_init): Use get_ncpu() for Darwin, +NetBSD and OpenBSD. + +* mallocx.c (GC_generic_malloc_many, GC_malloc_many): Define even +if THREADS is undefined. +* include/gc.h (GC_malloc_many): Update the comment. + +* include/gc_cpp.h (GC_PLACEMENT_DELETE): Define for Embarcadero +(formerly known as Borland) C++ compiler v6.21+. +* include/gc_cpp.h (GC_NO_OPERATOR_NEW_ARRAY): Define for ancient +VC++ compilers. + +* win32_threads.c (GC_register_my_thread_inner, +GC_pthread_start_inner): Undo the previous commit changes for +the thread flags and DETACHED state (since the state is only +tested in GC_thread_exit_proc). + +* include/gc.h (GC_unregister_my_thread): Fix a typo; update the +comment. +* pthread_support.c (GC_delete_thread): Allow to delete the main +thread (don't call GC_INTERNAL_FREE for it); update the comment. +* win32_threads.c (GC_delete_thread): Likewise. +* pthread_support.c (GC_unregister_my_thread): Add an assertion +for FINISHED flag is unset. +* tests/test.c (check_heap_stats): Test the main thread +unregistering (only if THREADS). +* win32_threads.c (GC_register_my_thread_inner): Set flags to +DETACHED (only if GC_PTHREADS). +* win32_threads.c (GC_unregister_my_thread): Add FIXME (for +GC_wait_for_gc_completion). +* win32_threads.c (GC_pthread_start_inner): Clear flags detached +state if needed; set pthread_id and flags while holding the lock. + +* include/private/gc_priv.h (SIG_SUSPEND): Don't define for +OpenBSD and Darwin. + +* include/gc.h: Recognize _M_X64 (as an alias for _AMD64_). + +* test.c (main, WinMain): Consistently don't invoke +GC_enable_incremental() if MAKE_BACKGRAPH is defined, but +do invoke it even if parallel marking is enabled. + +* tests/test.c (reverse_test): Comment out a check for MSWIN32 +(when determing BIG value) assuming outdated win32S. +* tests/test.c (reverse_test): Rename to reverse_test_inner; +change the declaration (to be of GC_fn_type); call itself thru +GC_call_with_gc_active() if the argument is zero. +* tests/test.c (reverse_test): New function added calling +reverse_test_inner thru GC_do_blocking (to test GC_do_blocking and +GC_call_with_gc_active). + +* doc/README.macros (IGNORE_DYNAMIC_LOADING, PLATFORM_ANDROID): +Document. +* dyn_load.c: Don't include if PLATFORM_ANDROID. +* dyn_load.c: Include bionic (instead of ) if +PLATFORM_ANDROID. +* include/private/gcconfig.h (LINUX): Define also if +PLATFORM_ANDROID (for the windows-based toolkit). +* include/private/gcconfig.h (SEARCH_FOR_DATA_START): Explicitly +define for Android/x86 platform. +* include/private/gcconfig.h (IGNORE_DYNAMIC_LOADING): Recognize +new macro (undefine DYNAMIC_LOADING in this case). +* include/private/gcconfig.h (CANCEL_SAFE): Don't define if +PLATFORM_ANDROID. +* include/private/gcconfig.h (IF_CANCEL): Fix definition for the +explicitly defined CANCEL_SAFE. + +* allchblk.c (GC_allochblk_nth): Don't call GC_remove_protection() +if GC_DISABLE_INCREMENTAL. +* reclaim.c (GC_reclaim_generic): Likewise. +* checksums.c (GC_page_was_ever_dirty): Add prototype. +* include/private/gc_locks.h (GC_mark_lock_holder): Don't declare +unless PARALLEL_MARK. +* include/private/gc_priv.h (GC_dirty_maintained, +GC_page_was_dirty, GC_remove_protection, GC_dirty_init): Don't +declare if GC_DISABLE_INCREMENTAL. +* include/private/gc_priv.h (GC_print_finalization_stats): Don't +declare if SMALL_CONFIG. +* include/private/gcconfig.h (CHECKSUMS): Explicitly undefine if +GC_DISABLE_INCREMENTAL (since nothing to check). +* include/private/gcconfig.h (DEFAULT_VDB): Don't define if +GC_DISABLE_INCREMENTAL. +* os_dep.c (GC_dirty_maintained): Likewise. +* mark.c (GC_initiate_gc): Don't call GC_read_dirty() if +GC_DISABLE_INCREMENTAL. +* os_dep.c (GC_gww_page_was_ever_dirty, GC_page_was_ever_dirty): +Uncomment; define only if CHECKSUMS. + +* darwin_stop_world.c (GC_push_all_stacks): Fix a bug (call +GC_push_all_stack() instead of GC_push_all_stack_frames()). +* include/private/gc_priv.h (GC_push_all_stack_frames, +GC_push_all_register_frames): Rename to +GC_push_all_stack_sections and GC_push_all_register_sections, +respectively. +* mark_rts.c (GC_push_all_stack_frames, +GC_push_all_register_frames, GC_push_all_stack_part_eager_frames, +GC_push_current_stack): Likewise. +* pthread_stop_world.c (GC_push_all_stacks): Likewise. +* win32_threads.c (GC_push_stack_for): Likewise. +* misc.c (GC_call_with_gc_active): Rename "frame" local variable +to "stacksect". +* pthread_support.c (GC_call_with_gc_active): Likewise. +* win32_threads.c (GC_call_with_gc_active): Likewise. +* pthread_support.c (GC_call_with_gc_active): Update FIXME for +Darwin. +* win32_threads.c (GC_Thread_Rep): Update the comment for +traced_stack_sect. + +* darwin_stop_world.c (GC_push_all_stacks): Rename +activation_frame to traced_stack_sect. +* include/private/gc_priv.h (GC_push_all_stack_frames, +GC_push_all_register_frames): Likewise. +* include/private/pthread_support.h (GC_Thread_Rep): Likewise. +* mark_rts.c (GC_push_all_register_frames, +GC_push_all_stack_frames, GC_push_all_stack_part_eager_frames, +GC_push_current_stack): Likewise. +* pthread_stop_world.c (GC_push_all_stacks): Likewise. +* pthread_support.c (GC_call_with_gc_active): Likewise. +* win32_threads.c (GC_Thread_Rep, GC_call_with_gc_active, +GC_push_stack_for): Likewise. +* include/private/gc_priv.h (GC_activation_frame_s): Rename to +GC_traced_stack_sect_s. +* include/private/gc_priv.h (GC_activation_frame): Rename to +GC_traced_stack_sect. +* misc.c (GC_activation_frame, GC_call_with_gc_active): Likewise. +* doc/README.macros (UNICODE): Document. + +* doc/README.macros (GC_READ_ENV_FILE): Document (new macro). +* include/private/gc_priv.h (GETENV): Recognize GC_READ_ENV_FILE; +declare and use GC_envfile_getenv(). +* misc.c (GC_envfile_content, GC_envfile_length): New static +variable (only if GC_READ_ENV_FILE). +* misc.c (GC_ENVFILE_MAXLEN): New macro (used in GC_envfile_init). +* misc.c (GC_envfile_init, GC_envfile_getenv): New function (only +if GC_READ_ENV_FILE). +* misc.c (GC_init): Call GC_envfile_init() (before using GETENV) +if GC_READ_ENV_FILE. +* misc.c (GC_init): Move GC_setpagesize() and GC_init_win32() +calls to be just before GC_envfile_init() one (since the latter +uses GET_MEM). +* misc.c (GC_abort): use ExitProcess() (instead of DebugBreak) for +WinCE if NO_DEBUGGING; add a comment for DebugBreak() (for WinCE). +* mark_rts.c (GC_add_roots_inner): Remove redundant trailing '\n' +from the ABORT message. +* misc.c (GC_init): Likewise. +* os_dep.c (GC_get_main_stack_base, GC_register_data_segments): +Likewise. +* pthread_stop_world.c (GC_push_all_stacks): Likewise. +* pthread_support.c (GC_init_real_syms, start_mark_threads): +Likewise. + +* win32_threads.c (GC_get_next_stack): Don't define for Cygwin +(since unused for now). + +* dyn_load.c (HAVE_REGISTER_MAIN_STATIC_DATA): Don't define unless +GC_register_main_static_data() is defined. +* dyn_load.c (GC_register_dynamic_libraries): Define only if used +(if DYNAMIC_LOADING or PCR or Win32/CE). +* dyn_load.c (GC_register_main_static_data): Define the default +one only if DYNAMIC_LOADING. +* include/private/gc_priv.h (GC_register_dynamic_libraries): +Declare only if used (to prevent compiler warning). + +* mark_rts.c (GC_approx_sp): Add a comment (for GCC). + + +== [7.2alpha4] 2009-12-01 == + +* configure.ac (AC_CONFIG_COMMANDS): Quote srcdir value. + +* include/gc.h (GC_get_suspend_signal): New function declaration. +* misc.c (GC_get_suspend_signal): New API function (only if +THREADS). + +* alloc.c (min_bytes_allocd): Multiply GC_free_space_divisor by +two if GC_incremental (instead of TRUE_INCREMENTAL). + +* sparc_mach_dep.S (GC_push_regs): Remove the reference. + +* os_dep.c (SIZE_T, PULONG_PTR): Remove. +* os_dep.c (ULONG_PTR): Replace with GC_ULONG_PTR (defined as GC +"word"); add the comment. +* os_dep.c (GetWriteWatch_type, detect_GetWriteWatch, +GC_gww_read_dirty): Prefix ULONG_PTR with "GC_". + +* win32_threads.c (THREAD_TABLE_SZ): Change back to a power-of-two +const value (for speed). +* win32_threads.c (THREAD_TABLE_INDEX): New macro. +* win32_threads.c (GC_new_thread, GC_lookup_thread_inner, +GC_delete_gc_thread, GC_delete_thread, GC_lookup_pthread): Use +THREAD_TABLE_INDEX instead of THREAD_TABLE_SZ. +* win32_threads.c (PTHREAD_MAP_HASH): Rename to PTHREAD_MAP_INDEX. + +* win32_threads.c (THREAD_TABLE_SZ): Make the const value prime. + +* backgraph.c: Remove apostrophe char from "#error". + +* doc/README.macros (GC_DISABLE_INCREMENTAL): Document. +* include/private/gcconfig.h (GC_DISABLE_INCREMENTAL): Recognize +new macro; implicitly define it if SMALL_CONFIG. +* alloc.c (GC_incremental, GC_timeout_stop_func): Check for +GC_DISABLE_INCREMENTAL instead of SMALL_CONFIG. +* include/private/gc_priv.h (GC_incremental, TRUE_INCREMENTAL, +GC_push_conditional): Likewise. +* mark.c (GC_push_next_marked_dirty, GC_push_selected, +GC_push_conditional, GC_block_was_dirty): Likewise. +* misc.c (GC_enable_incremental): Likewise. +* misc.c (GC_init): Likewise. + +* dyn_load.c (WIN32_LEAN_AND_MEAN): Guard with ifndef. +* misc.c (WIN32_LEAN_AND_MEAN): Likewise. +* os_dep.c (WIN32_LEAN_AND_MEAN): Likewise. +* allchblk.c (GC_allochblk_nth): Fix a minor typo (don't/doesn't) +in a comment. +* backgraph.c: Likewise. +* dyn_load.c (GC_register_dynamic_libraries): Likewise. +* extra/threadlibs.c (main): Likewise. +* pthread_support.c (pthread_join): Likewise. +* tests/test.c (main): Likewise. + +* mach_dep.c (GC_push_regs): Remove STATIC (just to catch +a duplicate symbol definition linker error). +* misc.c (GC_clear_stack_inner): Likewise. +* sparc_mach_dep.S (GC_push_regs): Comment out the reference. + +* include/private/gc_priv.h (GC_write_disabled): New variable +declaration (only if GC_ASSERTIONS and Win32 threads). +* misc.c (GC_write): Add assertion for GC_write_disabled value is +not on (only if THREADS). +* win32_threads.c (GC_write_disabled): New variable (only if +GC_ASSERTIONS and not Cygwin). +* win32_threads.c (GC_stop_world): Set and clear GC_write_disabled +(while holding GC_write_cs). + +* win32_threads.c (GC_please_stop): If DllMain-based thread +registration is not compiled in then define GC_please_stop as +a non-volatile variable for assertion only. +* win32_threads.c (GC_stop_world): Set and clear only if defined. +* win32_threads.c (GC_stop_world): Add the comment for GC_printf() +usage (while holding GC_write_cs). +* win32_threads.c (GC_delete_gc_thread): Likewise. +* os_dep.c (GC_remove_protection): Likewise. + +* pthread_support.c (GC_inner_start_routine): Join 3 sequential +GC_printf() calls into a single one (for DEBUG_THREADS). + +* include/private/gc_priv.h (GC_total_stacksize): New variable +declaration (only if THREADS). +* alloc.c (GC_total_stacksize): New variable (only if THREADS). +* alloc.c (min_bytes_allocd): Calculate stack_size using +GC_stackbottom only in the single-threaded case; otherwise use +GC_total_stacksize; print GC_total_stacksize value if +DEBUG_THREADS. +* darwin_stop_world.c (GC_push_all_stacks): Use "%p" printf type +specifier for lo/hi values (instead of "%lx"). +* darwin_stop_world.c (GC_push_all_stacks): Use +GC_push_all_stack_frames() instead of GC_push_all_stack(). +* darwin_stop_world.c (GC_push_all_stacks): Recalculate +GC_total_stacksize value. +* pthread_stop_world.c (GC_push_all_stacks): Likewise. +* win32_threads.c (GC_push_all_stacks): Likewise. +* win32_threads.c (GC_push_stack_for): Pass "me" argument; return +stack size; don't check for non-zero value of thread->stack_base. +* win32_threads.c (GC_push_all_stacks): Don't call +GC_push_stack_for() and don't check for "t->id == me" if +thread->stack_base is zero. + +* dyn_load.c (GC_dump_meminfo): Prefix "%lx" printf type specifier +with "0x". +* os_dep.c (PROTECT): Likewise. +* win32_threads.c (GC_mark_thread_local_free_lists): Cast p->id to +int (to match printf type specifier). + +* tests/test.c (check_heap_stats): Take into account the unmapped +memory size when checking for "Unexpected heap growth"; remove +FIXME. + +* alloc.c: Revert last change. + +* include/private/gcconfig.h (STACKBOTTOM): Add a presence check +for eCos/NOSYS. +* misc.c (GC_write): Comment out _Jv_diag_write() call (since no +longer defined in GCJ). + +* os_dep.c (brk): Rename to ecos_gc_brk. + +* alloc.c (min_bytes_allocd): Use GC_stackbottom value to compute +stack_size even if THREADS. +* doc/README.macros (DEBUG_THREADS): Document. +* pthread_support.c (DEBUG_THREADS): Remove the commented out +definition. +* win32_threads.c (DEBUG_WIN32_THREADS): Remove duplicate +definition. +* win32_threads.c: Include errno.h (except for WinCE). +* win32_threads.c (GC_win32_start_inner): Copy "start" and "param" +to local variables, and free "arg" parameter before "start" +invocation. +* win32_threads.c (GC_beginthreadex): Set errno to EAGAIN on error +(instead of calling SetLastError(ERROR_NOT_ENOUGH_MEMORY)). +* win32_threads.c (GC_beginthreadex): Return 0 on error (instead +of -1). + +* darwin_stop_world.c (GC_darwin_register_mach_handler_thread): +Use GC_INNER for the function definition. +* include/private/darwin_stop_world.h +(GC_darwin_register_mach_handler_thread): Remove the prototype. +* os_dep.c (GC_darwin_register_mach_handler_thread): Use GC_INNER +for the function prototype. +* include/private/gc_priv.h (NDEBUG): Explicitly define if +NO_DEBUGGING and not GC_ASSERTIONS (before the standard headers +inclusion). + +* include/private/gcconfig.h: Move DebugBreak() workaround (for +x86mingw32ce toolchain) to gc_priv.h (after windows.h inclusion). + +* allchblk.c (GC_unmap_old, GC_merge_unmapped, GC_allochblk, +GC_freehblk): Use GC_INNER for the function definition. +* alloc.c (GC_never_stop_func, GC_should_collect, +GC_try_to_collect_inner, GC_collect_a_little_inner, +GC_set_fl_marks, GC_add_to_our_memory, GC_add_to_heap, +GC_expand_hp_inner, GC_collect_or_expand, GC_allocobj): Likewise. +* backgraph.c (GC_build_back_graph, GC_traverse_back_graph): +Likewise. +* blacklst.c (GC_default_print_heap_obj_proc, GC_bl_init, +GC_promote_black_lists, GC_unpromote_black_lists, +GC_add_to_black_list_normal, GC_add_to_black_list_stack, +GC_is_black_listed): Likewise. +* darwin_stop_world.c (GC_push_all_stacks, GC_push_all_stacks, +GC_stop_init, GC_stop_world, GC_start_world): Likewise. +* dbg_mlc.c (GC_has_other_debug_info, GC_store_back_pointer, +GC_marked_for_finalization, GC_generate_random_backtrace_no_gc, +GC_store_debug_info, GC_start_debugging, +GC_debug_generic_malloc_inner, +GC_debug_generic_malloc_inner_ignore_off_page, +GC_debug_malloc_uncollectable, GC_debug_free_inner): Likewise. +* dyn_load.c (GC_register_dynamic_libraries, +GC_register_main_static_data, GC_init_dyld): Likewise. +* finalize.c (GC_push_finalizer_structures, GC_finalize, +GC_notify_or_invoke_finalizers, GC_print_finalization_stats): +Likewise. +* gcj_mlc.c (GC_core_gcj_malloc): Likewise. +* headers.c (GC_find_header, GC_header_cache_miss, +GC_scratch_alloc, GC_init_headers, GC_install_header, +GC_install_counts, GC_remove_header, GC_remove_counts, +GC_next_used_block, GC_prev_block): Likewise. +* mach_dep.c (GC_with_callee_saves_pushed): Likewise. +* malloc.c (GC_collect_or_expand, GC_alloc_large, +GC_generic_malloc_inner, GC_generic_malloc_inner_ignore_off_page, +GC_core_malloc_atomic, GC_core_malloc, GC_free_inner): Likewise. +* mallocx.c (GC_generic_malloc_ignore_off_page): Likewise. +* mark.c (GC_collection_in_progress, GC_clear_hdr_marks, +GC_set_hdr_marks, GC_set_mark_bit, GC_clear_mark_bit, +GC_clear_marks, GC_initiate_gc, GC_mark_some, +GC_mark_stack_empty, GC_invalidate_mark_state, +GC_signal_mark_stack_overflow, GC_mark_from, GC_help_marker, +GC_mark_init, GC_push_all, GC_push_conditional, +GC_mark_and_push_stack, GC_push_all_eager, GC_push_all_stack): +Likewise. +* mark_rts.c (GC_is_static_root, GC_roots_present, GC_approx_sp, +GC_exclude_static_roots_inner, GC_push_all_register_frames, +GC_push_all_stack_frames, GC_cond_register_dynamic_libraries, +GC_push_roots): Likewise. +* misc.c (GC_extend_size_map, GC_clear_stack, GC_err_write): +Likewise. +* new_hblk.c (GC_build_fl, GC_new_hblk): Likewise. +* obj_map.c (GC_register_displacement_inner, GC_add_map_entry, +GC_initialize_offsets): Likewise. +* os_dep.c (GC_get_maps, GC_parse_map_entry, GC_text_mapping, +GC_init_linux_data_start, GC_init_netbsd_elf, GC_setpagesize, +GC_set_and_save_fault_handler, GC_setup_temporary_fault_handler, +GC_reset_fault_handler, GC_get_register_stack_base, GC_init_win32, +GC_add_current_malloc_heap, GC_is_heap_base, GC_unmap, GC_remap, +GC_unmap_gap, GC_push_all_stacks, GC_gww_dirty_init, +GC_dirty_init, GC_read_dirty, GC_page_was_dirty, +GC_page_was_ever_dirty, GC_remove_protection, +GC_write_fault_handler, GC_mprotect_stop, GC_mprotect_resume, +GC_save_callers, GC_print_callers): Likewise. +* pthread_stop_world.c (GC_push_all_stacks, GC_stop_world, +GC_start_world, GC_stop_init): Likewise. +* pthread_support.c (GC_mark_thread_local_free_lists, +GC_lookup_thread, GC_reset_finalizer_nested, +GC_check_finalizer_nested, GC_segment_is_thread_stack, +GC_greatest_stack_base_below, GC_thr_init, GC_init_parallel, +GC_do_blocking_inner, GC_lock, GC_acquire_mark_lock, +GC_release_mark_lock, GC_wait_for_reclaim, GC_notify_all_builder, +GC_wait_marker, GC_notify_all_marker): Likewise. +* reclaim.c (GC_print_all_errors, GC_block_empty, +GC_reclaim_generic, GC_start_reclaim, GC_continue_reclaim, +GC_reclaim_all): Likewise. +* thread_local_alloc.c (GC_init_thread_local, +GC_destroy_thread_local, GC_mark_thread_local_fls_for): Likewise. +* win32_threads.c (GC_reset_finalizer_nested, +GC_check_finalizer_nested, GC_do_blocking_inner, GC_stop_world, +GC_start_world, GC_push_all_stacks, GC_get_next_stack, +GC_acquire_mark_lock, GC_release_mark_lock, GC_wait_for_reclaim, +GC_notify_all_builder, GC_wait_marker, GC_notify_all_marker, +GC_thr_init, GC_init_parallel, GC_lock, +GC_mark_thread_local_free_lists): Likewise. +* alloc.c (GC_add_current_malloc_heap, GC_build_back_graph, +GC_traverse_back_graph): Use GC_INNER for the function prototype. +* darwin_stop_world.c (GC_mprotect_stop, GC_mprotect_resume): +Likewise. +* dbg_mlc.c (GC_default_print_heap_obj_proc): Likewise. +* dyn_load.c (GC_parse_map_entry, GC_get_maps, +GC_segment_is_thread_stack, GC_roots_present, GC_is_heap_base, +GC_get_next_stack): Likewise. +* finalize.c (GC_reset_finalizer_nested, +GC_check_finalizer_nested): Likewise. +* gcj_mlc.c (GC_start_debugging): Likewise. +* include/private/dbg_mlc.h (GC_save_callers, GC_print_callers, +GC_has_other_debug_info, GC_store_debug_info): Likewise. +* include/private/gc_hdrs.h (GC_header_cache_miss): Likewise. +* include/private/gc_locks.h (GC_lock): Likewise. +* include/private/gc_pmark.h (GC_signal_mark_stack_overflow, +GC_mark_from): Likewise. +* include/private/pthread_support.h (GC_lookup_thread, +GC_stop_init): Likewise. +* include/private/thread_local_alloc.h (GC_init_thread_local, +GC_destroy_thread_local, GC_mark_thread_local_fls_for): Likewise. +* malloc.c (GC_extend_size_map, GC_text_mapping): Likewise. +* mark.c (GC_page_was_ever_dirty): Likewise. +* mark_rts.c (GC_mark_thread_local_free_lists): Likewise. +* misc.c (GC_register_main_static_data, GC_init_win32, +GC_setpagesize, GC_init_linux_data_start, +GC_set_and_save_fault_handler, GC_init_dyld, GC_init_netbsd_elf, +GC_do_blocking_inner): Likewise. +* os_dep.c (GC_greatest_stack_base_below): Likewise. +* win32_threads.c (GC_write_fault_handler, GC_gww_dirty_init): +Likewise. +* include/private/gc_priv.h: Likewise. +* include/private/gc_priv.h (GC_INNER): Update the comment. +* doc/README.macros (GC_DLL): Update. + +* alloc.c (GC_collection_in_progress): Move the prototype to +gc_priv.h. +* gc_dlopen.c (GC_collection_in_progress): Likewise. +* pthread_support.c (GC_collection_in_progress): Likewise. +* misc.c (GC_init_parallel): Likewise. +* pthread_support.c (GC_init_parallel): Likewise. +* win32_threads.c (GC_init_parallel): Likewise. +* darwin_stop_world.c (GC_thr_init): Likewise. +* misc.c (GC_thr_init): Likewise. +* pthread_stop_world.c (GC_thr_init): Likewise. +* pthread_support.c (GC_thr_init): Likewise. +* blacklst.c (GC_clear_bl, GC_copy_bl, +GC_number_stack_black_listed): Make STATIC. +* dbg_mlc.c (GC_print_obj, GC_make_closure, +GC_debug_invoke_finalizer): Likewise. +* malloc.c (GC_alloc_large_and_clear): Likewise. +* mark.c (GC_push_selected, GC_push_marked1, GC_push_marked2, +GC_push_marked4, GC_push_marked, GC_push_next_marked, +GC_push_next_marked_dirty, GC_push_next_marked_uncollectable): +Likewise. +* misc.c (GC_clear_stack_inner): Likewise. +* os_dep.c (GC_repeat_read, GC_default_push_other_roots): Likewise. +* darwin_stop_world.c (FindTopOfStack): Make static; define only +if not DARWIN_DONT_PARSE_STACK. +* dbg_mlc.c (GC_debug_free_inner): Define only if DBG_HDRS_ALL. +* dyn_load.c (GC_repeat_read): Remove unused prototype. +* include/private/gc_pmark.h (GC_find_start): Likewise. +* misc.c (GC_read, GC_register_finalizer_no_order): Likewise. +* dyn_load.c (GC_segment_is_thread_stack): Add prototype (only if +THREADS). +* dyn_load.c (GC_register_main_static_data): Define only if +DYNAMIC_LOADING. +* finalize.c (GC_enqueue_all_finalizers): Remove unnecessary tail +"return" statement. +* gc_dlopen.c (GC_SOLARIS_THREADS): Don't recognize (since implies +GC_PTHREADS). +* include/gc.h: Fix a typo. +* include/gc_inline.h (GC_ASSERT): Define (if not defined) since +the header is public. +* include/gc_inline.h (GC_generic_malloc_many): New public +function declaration. +* mallocx.c (GC_generic_malloc_many): Make public. +* include/private/gc_priv.h (GC_INNER): Use visibility attribute +(if available). +* include/private/gc_priv.h (GC_EXTERN): Define using GC_INNER. +* include/private/gc_priv.h: Include atomic_ops.h if THREADS and +MPROTECT_VDB. +* os_dep.c: Don't include atomic_ops.h +* win32_threads.c: Likewise. +* include/private/gc_priv.h (GC_push_selected, GC_push_regs, +GC_push_marked, GC_number_stack_black_listed, +GC_alloc_large_and_clear, GC_reclaim_or_delete_all, +GC_generic_malloc_many, GC_make_closure, +GC_debug_invoke_finalizer, GC_print_obj, GC_page_was_ever_dirty): +Remove the prototype. +* mark.c (GC_page_was_ever_dirty): Add prototype (only if +PROC_VDB). +* include/private/gc_priv.h (GC_push_next_marked_dirty, +GC_push_next_marked, GC_push_next_marked_uncollectable): Move +the prototype to mark.c. +* include/private/gc_priv.h (GC_is_static_root): Declare only if +not THREADS. +* include/private/gc_priv.h (GC_free_inner): Declare only if +THREADS. +* include/private/gc_priv.h (GC_debug_free_inner): Declare only if +THREADS and DBG_HDRS_ALL. +* include/private/gc_priv.h (GC_markers): Declare GC_markers only +if PARALLEL_MARK. +* include/private/gc_priv.h (GC_register_main_static_data): Move +the prototype to misc.c. +* mach_dep.c (GC_push_regs): Make STATIC; define only along with +HAVE_PUSH_REGS definition. +* mach_dep.c (GC_clear_stack_inner): Replace K&R-style function +definition with the ANSI C one. +* mark.c (GC_started_thread_while_stopped): Declared only if not +GNU C. +* win32_threads.c (GC_started_thread_while_stopped): Don't define +if GNU C. +* mark.c (GC_mark_from): Avoid unbalanced brackets in +#if-#else-#endif blocks. +* mark_rts.c (GC_is_static_root): Define only if not THREADS. +* os_dep.c (GC_get_stack_base): Make public (for OpenBSD). +* os_dep.c (GC_page_was_ever_dirty): Comment out the function +except for PROC_VDB. +* tests/test.c (main): Don't reference GC_print_obj, +GC_make_closure, GC_debug_invoke_finalizer, +GC_page_was_ever_dirty, GC_is_fresh (in GC_noop). +* thread_local_alloc.c: Don't include "gc_inline.h". +* win32_threads.c (GC_write_fault_handler): Declare only if +MPROTECT_VDB. + +* allchblk.c (DEBUG): Remove macro (since unused). +* allchblk.c: Include private/gc_priv.h before other includes and +definitions. +* alloc.c: Likewise. +* gc_dlopen.c: Likewise. +* headers.c: Likewise. +* mallocx.c: Likewise. +* mark_rts.c: Likewise. +* new_hblk.c: Likewise. +* reclaim.c: Likewise. +* mark.c: Include private/gc_pmark.h before other includes. +* misc.c: Likewise. +* dyn_load.c (_GNU_SOURCE): Move the definition to gc_priv.h. +* pthread_support.c (_USING_POSIX4A_DRAFT10): Likewise. +* pthread_support.c (_POSIX4A_DRAFT10_SOURCE): Remove (since +already defined in gc_config_macros.h). +* dyn_load.c (GC_init_dyld): Remove parameter cast for +_dyld_register_func_for_add_image() and +_dyld_register_func_for_remove_image(); add the comment about +possible warnings; add FIXME for the deprecated +_dyld_bind_fully_image_containing_address(). +* include/private/gc_priv.h: Include gc.h before the standard +headers inclusion. +* tests/test.c: Likewise. +* include/private/gcconfig.h (DebugBreak): Update the comment. +* typd_mlc.c (ED_INITIAL_SIZE): Remove ';'. + +* configure.ac (openbsd): Define GC_OPENBSD_THREADS. +* configure.ac: Add AM_CONDITIONAL(OPENBSD_THREADS). +* configure.ac: Add sparc-openbsd case. +* doc/README.macros (GC_NETBSD_THREADS, GC_OPENBSD_THREADS): +Document. +* tests/test.c (main): Handle OpenBSD case. +* include/private/pthread_stop_world.h: Likewise. +* extra/threadlibs.c (main): Replace K&R-style function definition +with the ANSI C one. +* extra/threadlibs.c (main): Handle GC_OPENBSD_THREADS case. +* dyn_load.c (OPENBSD): Recognize (similar to NETBSD). +* include/gc_config_macros.h (GC_SOLARIS_THREADS): Recognize; +define it for OpenBSD. +* include/gc_pthread_redirects.h (GC_pthread_sigmask, +pthread_sigmask): Don't declare and redefine for OpenBSD. +* include/private/gcconfig.h: Handle OpenBSD (on arm, sh, i386, +amd64, powerpc). +* mach_dep.c (NO_GETCONTEXT): Likewise. +* include/private/pthread_stop_world.h (thread_stop_info): Don't +define last_stop_count field if OpenBSD. +* misc.c (GC_init_dyld): Add declaration (if NetBSD). +* misc.c (GC_init): Don't call GC_init_netbsd_elf() for OpenBSD. +* os_dep.c (GC_init_netbsd_elf): Don't define for OpenBSD. +* os_dep.c (old_segv_act, GC_jmp_buf_openbsd): New static variable +(only if OpenBSD). +* os_dep.c (GC_fault_handler_openbsd, GC_find_limit_openbsd, +GC_skip_hole_openbsd): New static function (only if OpenBSD). +* os_dep.c (GC_get_stack_base, GC_get_main_stack_base, +GC_register_data_segments): Define specially for OpenBSD case. +* os_dep.c (GC_fault_handler_lock): Initialize to +AO_TS_INITIALIZER (instead of 0). +* pthread_support.c (GC_allocate_lock): Likewise. +* pthread_stop_world.c (NSIG, GC_print_sig_mask, +GC_remove_allowed_signals, suspend_handler_mask, GC_stop_count, +GC_world_is_stopped, GC_retry_signals, SIG_THR_RESTART, +GC_suspend_ack_sem, GC_suspend_handler_inner, GC_suspend_handler, +GC_restart_handler): Don't define and use if OpenBSD. +* pthread_stop_world.c (GC_suspend_all, GC_stop_world, +GC_start_world): Handle OpenBSD case. +* pthread_stop_world.c (GC_stop_init): Define as empty if OpenBSD. +* pthread_support.c (pthread_sigmask): Don't undefine the macro and +don't define the wrapper function if OpenBSD. +* pthread_support.c (GC_thr_init): Handle OpenBSD case. + +* dyn_load.c: Move the inclusion of private/gc_priv.h below +definition of a feature macro (_GNU_SOURCE). + +* include/gc.h (REVEAL_POINTER): Remove redundant parentheses. +* include/gc.h (GC_HIDE_POINTER, GC_REVEAL_POINTER): New macros +(only if GC_I_HIDE_POINTERS). +* backgraph.c (GET_OH_BG_PTR): Prefix REVEAL_POINTER() with "GC_". +* dbg_mlc.c (GC_get_back_ptr_info): Likewise. +* finalize.c (GC_grow_table, GC_dump_finalization, GC_finalize, +GC_enqueue_all_finalizers): Likewise. +* backgraph.c (SET_OH_BG_PTR): Prefix HIDE_POINTER() with "GC_". +* finalize.c (GC_general_register_disappearing_link, +GC_unregister_disappearing_link, GC_register_finalizer_inner, +GC_finalize): Likewise. +* include/private/dbg_mlc.h (HIDE_BACK_PTR): Likewise. +* include/private/dbg_mlc.h (GC_I_HIDE_POINTERS): Define instead +of I_HIDE_POINTERS. +* include/private/gc_priv.h (GC_I_HIDE_POINTERS): Likewise. +* include/gc.h (_GC_H): Strip leading underscore. +* include/gc_backptr.h (_GC_H): Likewise. +* include/gc_gcj.h (_GC_H): Likewise. +* include/gc_mark.h (_GC_H): Likewise. +* include/gc_typed.h (_GC_TYPED_H, _GC_H): Likewise. +* include/javaxfc.h (_GC_H): Likewise. +* include/new_gc_alloc.h (__GC_SPECIALIZE): Likewise. +* include/private/dbg_mlc.h (_GC_H): Likewise. +* include/private/gc_priv.h (_GC_H): Likewise. + +* gc_cpp.cc: Include "gc_cpp.h" instead of . + +* include/private/gc_priv.h (GC_INNER): New macro (for GC-scope +variable definitions). +* include/private/gc_priv.h (GC_EXTERN): Update the comment. +* allchblk.c (GC_unmap_threshold): Define as GC_INNER. +* alloc.c (GC_incremental, GC_world_stopped, GC_n_heap_sects, +GC_n_memory, GC_fail_count): Likewise. +* blacklst.c (GC_black_list_spacing, GC_print_heap_obj): Likewise. +* gcj_mlc.c (GC_gcj_malloc_initialized, GC_gcjobjfreelist): Likewise. +* mach_dep.c (GC_save_regs_ret_val): Likewise. +* mark.c (GC_n_mark_procs, GC_obj_kinds, GC_n_kinds, +GC_mark_stack, GC_mark_stack_limit, GC_mark_stack_size, +GC_mark_stack_top, GC_mark_state, GC_mark_stack_too_small, +GC_mark_no, GC_markers): Likewise. +* mark_rts.c (GC_root_size, GC_push_typed_structures): Likewise. +* misc.c (GC_allocate_ml, GC_debugging_started, GC_check_heap, +GC_print_all_smashed, GC_print_back_height, GC_dump_regularly, +GC_backtraces, GC_force_unmap_on_gcollect, +GC_large_alloc_warn_interval, GC_is_initialized, GC_write_cs, +GC_current_warn_proc, GC_blocked_sp, GC_activation_frame): Likewise. +* os_dep.c (GC_page_size, GC_dont_query_stack_min, +GC_no_win32_dlls, GC_wnt, GC_sysinfo, GC_push_other_roots, +GC_dirty_maintained, GC_fault_handler_lock): Likewise. +* pthread_support.c (GC_allocate_ml, GC_lock_holder, +GC_need_to_lock, GC_thr_initialized, GC_threads, +GC_in_thread_creation, GC_collecting, GC_allocate_lock, +GC_mark_lock_holder): Likewise. +* reclaim.c (GC_bytes_found, GC_fl_builder_count, GC_have_errors): +Likewise. +* win32_threads.c (GC_allocate_ml, GC_lock_holder, +GC_need_to_lock, GC_mark_lock_holder, GC_collecting): Likewise. +* extra/gc.c (GC_INNER, GC_EXTERN): Define as STATIC. +* mach_dep.c (GC_with_callee_saves_pushed): Remove redundant {}. + +* include/private/gc_priv.h (GC_bytes_allocd, GC_objfreelist, +GC_aobjfreelist): Replace GC_EXTERN to extern for SEPARATE_GLOBALS +case (since they are not defined inside GC at present). +* include/private/gc_priv.h (GC_objects_are_marked): Remove the +declaration (since made static). +* mark.c (GC_objects_are_marked): Define as STATIC. +* win32_threads.c (GC_thr_initialized, GC_in_thread_creation): +Likewise. +* mark.c (GC_N_KINDS_INITIAL_VALUE): New macro (defined and used +to initialize GC_n_kinds). +* win32_threads.c (start_mark_threads): Adjust the comment. + +* alloc.c (GC_notify_full_gc): Use GC_INLINE for a tiny static +function. +* backgraph.c (pop_in_progress, GC_apply_to_each_object): Likewise. +* mark_rts.c (add_roots_to_index): Likewise. + +* extra/gc.c: New file. +* Makefile.am (EXTRA_DIST): Add "extra/gc.c". + +* misc.c (GC_log): Remove the declaration; move the definition (to +the place where it is used); make STATIC. +* misc.c (GC_init): Use GC_err_printf() instead of GC_log_printf() +to print open log failure. +* misc.c (GC_write): Don't abort on open log failure if the GC is +compiled with GC_PRINT_VERBOSE_STATS (useful for WinCE). + +* include/private/gcconfig.h (USE_MMAP): Guard with ifndef. + +* allchblk.c (GC_fail_count, GC_large_alloc_warn_interval): Move +the variable declaration to gc_priv.h. +* alloc.c (GC_bytes_found, GC_unmap_threshold, +GC_force_unmap_on_gcollect): Likewise. +* dyn_load.c (GC_no_win32_dlls, GC_wnt): Likewise. +* finalize.c (GC_fail_count): Likewise. +* include/private/gc_locks.h (GC_allocate_ml, GC_lock_holder, +GC_collecting, GC_mark_lock_holder, GC_need_to_lock): Likewise. +* include/private/gc_pmark.h (GC_n_mark_procs, GC_mark_stack_size, +GC_mark_stack_limit, GC_mark_stack_top, GC_mark_stack, +GC_mark_stack_too_small, GC_mark_state): Likewise. +* include/private/pthread_support.h (GC_threads, +GC_thr_initialized, GC_in_thread_creation): Likewise. +* mallocx.c (GC_bytes_found): Likewise. +* mark_rts.c (GC_save_regs_ret_val, GC_world_stopped): Likewise. +* misc.c (GC_unmap_threshold): Likewise. +* os_dep.c (GC_unmap_threshold): Likewise. +* pthread_support.c (GC_markers): Likewise. +* thread_local_alloc.c (GC_gcjobjfreelist, +GC_gcj_malloc_initialized, GC_gcj_kind): Likewise. +* win32_threads.c (GC_fault_handler_lock, GC_write_cs, +GC_dont_query_stack_min, GC_markers, GC_wnt): Likewise. +* include/private/gc_priv.h (GC_EXTERN): New macro (used mostly as +a tag for now); defined after "gcconfig.h" inclusion. +* include/private/gc_priv.h: Use GC_EXTERN instead of "extern" +keyword for most global variables. +* alloc.c (GC_copyright): Add the comment about the symbol +visibility. +* finalize.c (GC_fo_entries): Likewise. +* include/private/gc_priv.h (GC_print_stats): Likewise. +* misc.c (GC_quiet): Likewise. +* mallocx.c (GC_bytes_allocd_tmp): Make the volatile variable +STATIC. +* pthread_support.c (GC_threads): Add explicit zero initializer +(to make the variable definition differ from the declaration). + +* backgraph.c (GC_quiet): Remove the declaration (not needed +anymore since gc_priv.h is always included). +* checksums.c (GC_quiet): Likewise. +* gcj_mlc.c (GC_quiet): Likewise. +* headers.c (GC_hdr_cache_hits, GC_hdr_cache_misses): Add the +comment. +* include/private/gc_hdrs.h (GC_hdr_cache_hits, +GC_hdr_cache_misses): Likewise. +* mark.c (GC_first_nonempty): Make the volatile variable STATIC. +* pthread_stop_world.c (GC_stop_count, GC_world_is_stopped): +Likewise. +* win32_threads.c (GC_please_stop, GC_max_thread_index, +GC_mark_mutex_waitcnt): Likewise. + +* pthread_support.c (GC_USE_LD_WRAP): Fix a typo (swapped 'L' and +'D') in the name. + +* gc_dlopen.c (GC_MUST_RESTORE_REDEFINED_DLOPEN): Define if dlopen +redirection is turned off; turn it on later when dlopen real +symbol is no longer needed (according to the comment and the same +as in dyn_load.c). +* gc_dlopen.c (WRAP_FUNC, REAL_FUNC): Rename to WRAP_DLFUNC and +REAL_DLFUNC, respectively (to have unique names since the +definitions may differ from that of the similar ones in +pthread_support.c). +* mark.c (source): Undefine the macro when no longer needed. +* os_dep.c (handler): Rename the type to GC_fault_handler_t (to +have the unique name across the project). +* os_dep.c (STAT_BUF_SIZE, STAT_READ); Guard with ifndef; add the +comment. +* pthread_support.c (STAT_BUF_SIZE, STAT_READ): Likewise. +* os_dep.c (sbrk): Undo sbrk() redirection (for ECOS) when no +longer needed. + +* pthread_stop_world.c (pthread_sigmask): Undefine before using +in GC_print_sig_mask() (only if DEBUG_THREADS); add the comment. +* win32_threads.c (dlopen, _beginthread): Don't undefine (since +neither redirected nor used here). +* win32_threads.c (GC_Thread_Rep): Rename "table_management" to +"tm" for short; remove "tm_" prefix. +* win32_threads.c (in_use, next): Don't define the macros; use +tm.in_use and tm.next fields, respectively (to ease debugging). +* win32_threads.c (HASH): Rename to PTHREAD_MAP_HASH (to have +unique name across the project). + +* include/private/gc_priv.h (I_HIDE_POINTERS): Define before gc.h +inclusion. +* include/private/gc_pmark.h (I_HIDE_POINTERS): Define if gc.h is +not included yet. +* finalize.c (I_HIDE_POINTERS): Don't define. +* include/private/dbg_mlc.h (I_HIDE_POINTERS): Likewise. +* misc.c (I_HIDE_POINTERS): Likewise. +* include/private/dbg_mlc.h (HIDE_POINTER, REVEAL_POINTER, +GC_hidden_pointer): Don't define if HIDE_POINTER is undefined. +* include/private/gc_pmark.h: Remove the comment about gc_priv.h +inclusion order. + +* dyn_load.c: Include gc_priv.h before using configuration +information (MACOS). +* dyn_load.c (GC_must_restore_redefined_dlopen): Rename to +GC_MUST_RESTORE_REDEFINED_DLOPEN. + +* backgraph.c (SET_OH_BG_PTR): Place outermost parenthesis +properly. +* darwin_stop_world.c: Replace "if DEBUG_THREADS" with +"ifdef DEBUG_THREADS". +* pthread_stop_world.c: Likewise. +* pthread_support.c: Likewise. +* include/gc_inline.h: Guard with GC_INLINE_H. + +* alloc.c (GC_copyright): Define as const. +* alloc.c (GC_collect_at_heapsize): Replace "static" with "STATIC" +(since the name starts with "GC_" prefix). +* dbg_mlc.c (GC_describe_type_fns): Likewise. +* dyn_load.c (GC_FirstDLOpenedLinkMap, +GC_register_dynlib_callback, GC_dyld_sections, +GC_dyld_name_for_hdr, GC_dyld_image_add, GC_dyld_image_remove): +Likewise. +* malloc.c (GC_libpthread_start, GC_libpthread_end, +GC_libld_start, GC_libld_end): Likewise. +* mark_rts.c (GC_remove_root_at_pos, GC_rebuild_root_index): +Likewise. +* os_dep.c (GC_gww_read_dirty, GC_gww_page_was_dirty, +GC_gww_page_was_ever_dirty, GC_mprotect_thread_notify, +GC_mprotect_thread_reply, GC_mprotect_thread, GC_darwin_sigbus, +GC_forward_exception): Likewise. +* pthread_support.c (GC_syms_initialized): Likewise. +* typd_mlc.c (GC_push_typed_structures_proc): Likewise. +* win32_threads.c (GC_win32_dll_threads, +GC_register_my_thread_inner, GC_lookup_pthread, GC_get_stack_min, +GC_waitForSingleObjectInfinite): Likewise. +* darwin_stop_world.c (GC_use_mach_handler_thread, +GC_use_mach_handler_thread, GC_mach_threads_count): Replace +"static" with "STATIC" and add zero initializer. +* os_dep.c (GC_task_self, GC_ports, GC_mprotect_state, +GC_sigbus_count): Likewise. +* headers.c (free_hdr): Replace "static" with GC_INLINE. +* misc.c (GC_tmp): Rename static variable to fwrite_gc_res. +* os_dep.c (memory): Rename static variable to ecos_gc_memory. +* os_dep.c (async_set_pht_entry_from_index): Make static (for +MPROTECT_VDB case). +* pthread_support.c (GC_real_pthread_create, +GC_real_pthread_sigmask, GC_real_pthread_join, +GC_real_pthread_detach, GC_init_real_syms): Use REAL_FUNC() macro +for static GC_real_XXX symbols. +* win32_threads.c (GC_may_be_in_stack): Remove "GC_" prefix. + +* alloc.c (GC_finish_collection): Replace getenv() with GETENV(). +* dyn_load.c (GC_init_dyld): Likewise. +* os_dep.c (GC_print_callers): Likewise. +* dyn_load.c (GC_dyld_name_for_hdr): Cast _dyld_get_image_name() +result (since it's always of "struct mach_header" type). +* dyn_load.c (GC_init_dyld): Cast GC_dyld_image_add and +GC_dyld_image_remove (to always have the first argument of +"struct mach_header" pointer type). + +* configure.ac: Add threads support for OpenBSD case (threads may +not work correctly for it). + +* acinclude.m4: Rename to m4/gc_set_version.m4. +* m4/libtool.m4: Delete the file. +* m4/lt~obsolete.m4: Likewise. +* m4/ltoptions.m4: Likewise. +* m4/ltsugar.m4: Likewise. +* m4/ltversion.m4: Likewise. + +* include/private/gcconfig.h: Define DebugBreak() as _exit(-1) for +x86mingw32ce toolchain to workaround the incorrect DebugBreak() +declaration in winbase.h (the workaround would turn into a no-op +when DebugBreak() will be defined as a macro in the toolchain). + +* include/private/gcconfig.h: Recognize __i386__ if WinCE (for +x86mingw32ce toolchain). +* include/private/gcconfig.h (NO_GETENV): Don't define for CeGCC +toolchain (or if already defined). +* include/private/gcconfig.h (NO_GETENV_WIN32): New macro (always +defined for WinCE or if NO_GETENV is defined). +* misc.c (GC_CreateLogFile): Use NO_GETENV_WIN32 macro instead of +NO_GETENV one. + +* configure.ac: Add AC_CONFIG_MACRO_DIR([m4]). +* Makefile.am: Add "ACLOCAL_AMFLAGS = -I m4". +* libtool.m4: Remove. +* m4/libtool.m4: New file (generated). +* m4/lt~obsolete.m4: Likewise. +* m4/ltoptions.m4: Likewise. +* m4/ltsugar.m4: Likewise. +* m4/ltversion.m4: Likewise. + +* include/gc.h (GC_UNDERSCORE_STDCALL): Recognize new macro; +prefix GC_CreateThread and GC_ExitThread with '_' if defined. +* doc/README.macros (GC_UNDERSCORE_STDCALL): Document. + +* alloc.c (GC_collect_or_expand): Add "retry" argument; add the +comments; don't use "default" stop_func on a retry if +GC_dont_expand. +* alloc.c (GC_allocobj): Pass "retry" argument to +GC_collect_or_expand(). +* malloc.c (GC_alloc_large): Likewise. +* include/private/gc_priv.h (GC_collect_or_expand): Move the +declaration to malloc.c; add "retry" argument. + +* alloc.c (GC_start_call_back): Move the variable definition from +misc.c. +* include/private/gc_priv.h (GC_start_call_back): Remove the +declaration. +* alloc.c (GC_notify_full_gc): Remove unnecessary cast of 0. +* alloc.c (GC_try_to_collect_inner): Also call stop_func at the +beginning of the function. +* include/gc.h (GC_try_to_collect): Refine the comment about +stop_func. + +* alloc.c (GC_default_stop_func, GC_try_to_collect_general, +GC_gcollect): Add the comment. +* alloc.c (GC_try_to_collect_general): Move the assertion on +stop_func != 0 to GC_try_to_collect(). +* alloc.c (GC_try_to_collect_general): If stop_func == 0 then use +GC_default_stop_func instead (holding the lock). +* alloc.c (GC_gcollect): Pass 0 as stop_func instead of +GC_default_stop_func (to prevent data races). + +* Makefile.direct: Move "define arguments" documentation to +doc/README.macros; add reference to doc/README.macros. +* Makefile.dj: Change the documentation reference to +doc/README.macros. +* README.QUICK: Likewise. +* configure.ac: Likewise. +* allchblk.c: Remove unnecessary "-D" from the comment. +* doc/README.macros: Likewise. +* README.environment: Likewise. +* include/gc.h: Likewise. +* include/gc_inline.h: Likewise. +* include/private/gcconfig.h: Likewise. +* README.QUICK: Fix a typo. + +* misc.c (GC_CreateLogFile): Use FILE_ATTRIBUTE_NORMAL for +CreateFile(); don't immediately flush every write if very verbose. + +* doc/README.win32: Replace ".exe.log" to ".gc.log". +* doc/README.win64: Likewise. +* doc/README.win64: Fix a typo. +* misc.c (GC_CreateLogFile): Strip executable file extension for +the log file; use ".gc.log" extension (instead of ".log"). + +* include/gc_config_macros.h: Avoid the redefinition of +GC_xxx_THREADS macros. + +* alloc.c (GC_try_to_collect_general): Change the type of "result" +local variable to GC_bool. + +* include/gc_config_macros.h: Use old behavior for FreeBSD and +NetBSD platform detection code (check that other GC_xxx_THREADS +are undefined); add FIXME. + +* include/gc_config_macros.h: Rearrange the platform detection +code (GC_WIN32_PTHREADS implies GC_WIN32_THREADS; define +GC_THREADS first if GC_XXX_THREADS already set; define proper +GC_XXX_THREADS if GC_THREADS; define GC_PTHREADS in a single +place; define _REENTRANT if posix threads except for Win32). + +* alloc.c (GC_try_to_collect_general): New function (move the code +from GC_try_to_collect, pass force_unmap argument). +* alloc.c (GC_try_to_collect, GC_gcollect): Call +GC_try_to_collect_general(). +* alloc.c (GC_gcollect_and_unmap): New public function. +* include/gc.h (GC_gcollect_and_unmap): New function declaration. +* tests/test.c (window_proc): Call GC_gcollect_and_unmap() on +WM_HIBERNATE event (instead of GC_set_force_unmap_on_gcollect() +and GC_gcollect()). + +* include/gc.h (GC_allow_register_threads, GC_register_my_thread, +GC_unregister_my_thread, GC_malloc_many): Refine the comment. +* include/gc.h (GC_malloc_many, GC_NEXT): Declare unconditionally +(that is, don't depend on GC_THREADS macro). +* include/gc.h: Don't check for __CYGWIN32__ and __CYGWIN__ along +with a check for GC_PTHREADS (since the former implies the +latter). + +* include/gc.h (GC_SOLARIS_THREADS): Don't check for. +* include/gc.h (GC_MIN, GC_MAX): Don't define. +* mallocx.c (GC_malloc_many): Add comment to #endif. + +* configure.ac: Drop the subdir-objects Automake option, since +it's incompatible with picking source files from libatomic_ops. + +* allchblk.c (GC_fail_count, GC_large_alloc_warn_interval): Add +"extern" keyword to a global variable declaration (some compilers +require it). +* alloc.c (GC_bytes_found, GC_unmap_threshold, +GC_force_unmap_on_gcollect): Likewise. +* dyn_load.c (GC_no_win32_dlls, GC_wnt): Likewise. +* finalize.c (GC_fail_count): Likewise. +* include/private/gc_hdrs.h (GC_hdr_cache_hits, +GC_hdr_cache_misses): Likewise. +* mallocx.c (GC_bytes_found): Likewise. +* mark_rts.c (GC_save_regs_ret_val, GC_world_stopped): Likewise. +* misc.c (GC_unmap_threshold): Likewise. +* os_dep.c (GC_unmap_threshold, GC_old_allocator): Likewise. +* pthread_support.c (GC_markers): Likewise. +* thread_local_alloc.c (GC_gcjobjfreelist, +GC_gcj_malloc_initialized, GC_gcj_kind): Likewise. +* win32_threads.c (GC_fault_handler_lock, GC_write_cs, +GC_dont_query_stack_min, GC_markers, GC_wnt): Likewise. + +* tests/huge_test.c: Define GC_IGNORE_WARN (if not defined) to +suppress misleading GC "Out of Memory!" warning printed on every +GC_MALLOC(LONG_MAX) call. +* tests/huge_test.c: Include "gc.h" instead of . +* tests/huge_test.c (main): Replace K&R-style function definition +with the ANSI C one. + +* dyn_load.c (GC_register_dynamic_libraries): Always use +lpMaximumApplicationAddress value for WinCE (even for old +versions). +* os_dep.c (VER_PLATFORM_WIN32_CE): Define if not in winbase.h. +* os_dep.c (GC_dont_query_stack_min): New global variable (only if +WinCE and THREADS). +* os_dep.c (GC_setpagesize): Adjust lpMaximumApplicationAddress +for WinCE (prior to version 6) if not _WIN32_WCE_EMULATION; set +GC_dont_query_stack_min for older WinCE (prior to version 5). +* win32_threads.c (GC_dont_query_stack_min): Declare. +* win32_threads.c (GC_get_stack_min): Rename the macro to +GC_wince_evaluate_stack_min for WinCE; update the comment. +* win32_threads.c (GC_push_stack_for, GC_get_next_stack): Use +GC_wince_evaluate_stack_min() instead of GC_get_stack_min() for +WinCE and don't update thread's last_stack_min value (only if +GC_dont_query_stack_min). +* win32_threads.c (GC_push_stack_for): Skip assertion for WinCE if +GC_dont_query_stack_min (since the evaluated stack_min value may +be incorrect if the stack is bigger than 64 KiB). + +* gc_dlopen.c (GC_dlopen): Add function redirector (only if +GC_USE_LD_WRAP). +* include/gc.h: Include "gc_pthread_redirects.h" even if +GC_USE_LD_WRAP or GC_NO_THREAD_REDIRECTS. +* include/gc_pthread_redirects.h (GC_PTHREAD_REDIRECTS_H): Don't +define and check for (since included only from gc.h). +* include/gc_pthread_redirects.h: Declare "GC_" symbols even if +GC_USE_LD_WRAP or GC_NO_THREAD_REDIRECTS. +* include/gc_pthread_redirects.h: Include signal.h only to get +sigset_t definition. + +* Makefile.direct: Document GC_REGISTER_MEM_PRIVATE. +* mark_rts.c (GC_is_tmp_root): Define also for WinCE unless +NO_DEBUGGING (that is, replace _WIN32_WCE_EMULATION with MSWINCE). +* os_dep.c (GC_sysinfo): Remove explicit global variable +initialization to "{0}" (revert back the previous change) since it +might produce a warning. + +* allchblk.c (GC_large_alloc_warn_interval): Move declaration from +gc_priv.h. +* allchblk.c (GC_large_alloc_warn_suppressed): Move definition +from misc.c; define as STATIC. +* include/private/gc_priv.h (GC_large_alloc_warn_interval, +GC_large_alloc_warn_suppressed): Remove declaration. +* alloc.c (GC_bytes_found): Add "defined in" comment. +* mallocx.c (GC_bytes_found): Likewise. +* misc.c (GC_unmap_threshold): Likewise. +* os_dep.c (GC_old_allocator): Likewise. +* pthread_support.c (GC_markers): Likewise. +* thread_local_alloc.c (GC_gcjobjfreelist, +GC_gcj_malloc_initialized, GC_gcj_kind): Likewise. +* win32_threads.c (GC_markers): Likewise. +* alloc.c (GC_start_time): Explicitly initialize to 0 or NULL (to +be distinctive from a variable declaration). +* backgraph.c (GC_max_height, GC_deepest_obj): Likewise. +* blacklst.c (GC_old_normal_bl, GC_incomplete_normal_bl, +GC_old_stack_bl, GC_incomplete_stack_bl): Likewise. +* checksums.c (GC_faulted, GC_n_dirty_errors, +GC_n_faulted_dirty_errors, GC_n_changed_errors, GC_n_clean, +GC_n_dirty, GC_bytes_in_used_blocks): Likewise. +* dbg_mlc.c (GC_smashed): Likewise. +* finalize.c (GC_old_dl_entries): Likewise. +* gcj_mlc.c (GC_gcj_kind, GC_gcj_debug_kind, GC_gcjobjfreelist, +GC_gcjdebugobjfreelist): Likewise. +* mach_dep.c (GC_save_regs_ret_val): Likewise. +* mark.c (GC_n_rescuing_pages, GC_mark_stack, GC_mark_stack_limit, +GC_mark_stack_top): Likewise. +* misc.c (GC_min_sp, GC_high_water, GC_bytes_allocd_at_reset): +Likewise. +* os_dep.c (GC_data_start, GC_page_size, GC_sysinfo, +GC_old_segv_handler, GC_old_bus_handler, +GC_old_bus_handler_used_si, GC_old_segv_handler_used_si, +GC_proc_buf, GC_proc_fd, GC_vd_base): Likewise. +* pthread_stop_world.c (GC_stop_count, GC_stopping_pid): Likewise. +* reclaim.c (GC_leaked): Likewise. +* typd_mlc.c (GC_explicit_kind, GC_array_kind, GC_ext_descriptors, +GC_typed_mark_proc_index, GC_array_mark_proc_index, +GC_eobjfreelist, GC_arobjfreelist): Likewise. +* win32_threads.c (GC_pthread_map_cache, GC_marker_cv, +GC_marker_Id): Likewise. +* dbg_mlc.c (GC_smashed, GC_n_smashed): Define as STATIC. +* gcj_mlc.c (GC_gcjdebugobjfreelist): Likewise. +* os_dep.c (GC_vd_base): Likewise. +* pthread_support.c (GC_mark_threads): Likewise. +* reclaim.c (GC_leaked): Likewise. +* typd_mlc.c (GC_bm_table): Likewise. +* mark_rts.c (GC_save_regs_ret_val): Change declaration type to +that of definition; add "defined in" comment. +* mark_rts.c (GC_push_current_stack): Remove unnecessary cast for +GC_save_regs_ret_val. +* misc.c (GC_check_heap, GC_print_all_smashed, +GC_start_call_back): Remove unnecessary cast (of 0). +* misc.c (GC_LARGE_ALLOC_WARN_INTERVAL): New tuning macro. +* misc.c (GC_large_alloc_warn_interval): Initialize to +GC_LARGE_ALLOC_WARN_INTERVAL value. +* misc.c (GC_tmp): Change to "static". +* os_dep.c (GC_mprotect_state): Define as static. +* pthread_support.c (dummy_thread_local): Prefix with "GC_". +* win32_threads.c (WinMain): Remove FIXME for WinCE. + +* os_dep.c (PROTECT, UNPROTECT): Use distinct ABORT messages. + +* configure.ac: Rewrite the tests for external or internal +libatomic_ops. +* configure.ac: In particular, drop the symbolic links. Add option +--with-libatomic-ops for forced selection. +* Makefile.am: Adjust the path of source files from libatomic_ops +to not use the links. +* Makefile.am (libgc_la_LIBADD): Add $(ATOMIC_OPS_LIBS). This will +be empty if we use the bundled AO sources. + +* Makefile.am: Strip version suffix for libatomic_ops directory. +* build_atomic_ops.sh: Likewise. +* build_atomic_ops.sh.cygwin: Likewise. +* configure_atomic_ops.sh: Likewise. +* Makefile.direct: Remove AO_VERSION definition; strip version +suffix for libatomic_ops directory. +* NT_STATIC_THREADS_MAKEFILE: Likewise. +* NT_X64_STATIC_THREADS_MAKEFILE: Likewise. +* NT_X64_THREADS_MAKEFILE: Likewise. +* gc.mak: Likewise. + +* libatomic_ops: Rename from "libatomic_ops-1.2". + +* alloc.c (GC_version): Add "const" keyword. +* alloc.c (GC_get_version): New public function. +* include/gc.h (GC_get_version): New function declaration; update +the comment for the GC version. + +* include/private/gc_locks.h (GC_allocate_ml, GC_lock_holder, +GC_collecting, GC_mark_lock_holder, GC_need_to_lock): Use "extern" +(for the global variable declaration) again. +* include/private/gc_pmark.h (GC_n_mark_procs, GC_mark_stack_size, +GC_mark_stack_limit, GC_mark_stack_top, GC_mark_stack, +GC_mark_stack_too_small, GC_mark_state): Likewise. +* include/private/gcconfig.h (GC_register_stackbottom): Likewise. +* include/private/pthread_support.h (GC_threads, +GC_thr_initialized, GC_in_thread_creation): Likewise. +* include/private/gc_priv.h: Likewise. + +* real_malloc.c: Include private/config.h if HAVE_CONFIG_H. + +* allchblk.c (GC_hblkfreelist): Define as STATIC. +* blacklst.c (GC_total_stack_black_listed): Likewise. +* include/private/gc_priv.h (GC_hblkfreelist, GC_stopped_mark, +GC_total_stack_black_listed, GC_push_stubborn_structures): Remove +declaration. +* mark_rts.c (GC_stopped_mark): Add declaration (only if +THREAD_LOCAL_ALLOC). +* allchblk.c (GC_fail_count): Move the declaration out of +GC_allochblk_nth(); remove "extern". +* alloc.c (IF_THREADS): Remove unused macro. +* alloc.c (GC_world_stopped): Define only if THREAD_LOCAL_ALLOC. +* alloc.c (GC_stopped_mark): Set GC_world_stopped value only if +THREAD_LOCAL_ALLOC. +* alloc.c (GC_bytes_found, GC_collection_in_progress, +GC_check_tls, GC_unmap_threshold, GC_force_unmap_on_gcollect): +Remove K&R-style "extern" for the declaration. +* dbg_mlc.c (GC_free_inner): Likewise. +* dyn_load.c (GC_repeat_read, GC_roots_present, GC_is_heap_base, +GC_get_next_stack, GC_no_win32_dlls, GC_wnt): Likewise. +* finalize.c (GC_fail_count): Likewise. +* include/private/gc_hdrs.h (GC_hdr_cache_hits, +GC_hdr_cache_misses): Likewise. +* include/private/gc_locks.h (GC_allocate_ml, GC_lock_holder, +GC_lock, GC_collecting, GC_mark_lock_holder, GC_need_to_lock): +Likewise. +* include/private/gc_pmark.h (GC_mark_procs, GC_n_mark_procs, +GC_mark_stack_size, GC_mark_stack_limit, GC_mark_stack_top, +GC_mark_stack, GC_mark_stack_too_small, GC_mark_state): Likewise. +* include/private/gc_priv.h (GC_current_warn_proc, GC_obj_kinds, +GC_n_kinds, GC_fo_entries, GC_n_heap_sects, GC_n_memory, +GC_page_size, GC_sysinfo, GC_black_list_spacing, +GC_objects_are_marked, GC_incremental, GC_dirty_maintained, +GC_root_size, GC_debugging_started, GC_large_alloc_warn_interval, +GC_large_alloc_warn_suppressed, GC_blocked_sp, +GC_activation_frame, GC_push_other_roots, +GC_push_finalizer_structures, GC_push_thread_structures, +GC_push_typed_structures, GC_start_call_back, GC_is_initialized, +GC_check_heap, GC_print_all_smashed, GC_print_all_errors, +GC_print_heap_obj, GC_have_errors, GC_print_stats, +GC_dump_regularly, GC_backtraces, GC_print_back_height, +GC_debug_generic_malloc_inner, +GC_debug_generic_malloc_inner_ignore_off_page, +GC_fl_builder_count, GC_mark_no, GC_help_marker, +GC_setup_temporary_fault_handler, GC_reset_fault_handler): Likewise. +* include/private/gcconfig.h (GC_SysVGetDataStart, +GC_FreeBSDGetDataStart, GC_register_stackbottom, +GC_MacTemporaryNewPtr, GC_amiga_get_mem): Likewise. +* include/private/pthread_support.h (GC_threads, +GC_thr_initialized, GC_in_thread_creation): Likewise. +* malloc.c (GC_text_mapping): Likewise. +* mallocx.c (GC_bytes_found): Likewise. +* mark.c (GC_check_dirty, GC_started_thread_while_stopped): Likewise. +* mark_rts.c (GC_save_regs_ret_val): Likewise. +* misc.c (GC_clear_stack_inner, GC_init_parallel, GC_init_win32, +GC_setpagesize, GC_init_linux_data_start, +GC_set_and_save_fault_handler, GC_unmap_threshold): Likewise. +* os_dep.c (GC_unmap_threshold, GC_push_all_stacks, +GC_darwin_register_mach_handler_thread): Likewise. +* pthread_support.c (GC_markers, GC_collection_in_progress): +Likewise. +* tests/test.c (GC_amiga_free_all_mem): Likewise. +* thread_local_alloc.c (GC_gcjobjfreelist, +GC_gcj_malloc_initialized, GC_gcj_kind): Likewise. +* win32_threads.c (GC_write_fault_handler, GC_gww_dirty_init, +GC_fault_handler_lock, GC_write_cs, GC_markers): Likewise. +* misc.c (GC_read, GC_register_finalizer_no_order, GC_init_dyld): +Move the declaration out of GC_init(); remove "extern". +* os_dep.c (GC_abort): Add the comment; add workaround to suppress +compiler "unreachable code" warnings for ABORT callers (where +ABORT is followed by a dummy return statement). +* os_dep.c (GC_old_allocator): Move the declaration out of +GC_default_push_other_roots(); remove "extern". +* darwin_stop_world.c (GC_mprotect_stop, GC_mprotect_resume): +Move the declaration out of GC_stop_world() and GC_start_world() +(only if MPROTECT_VDB); remove "extern". + +* win32_threads.c (GC_get_stack_min, GC_push_stack_for, +GC_get_next_stack): Recognize _WIN32_WCE_EMULATION macro (used for +WinCE emulation and for custom WinCE 6 devices); add the comment. +* win32_threads.c (GC_get_stack_min): Cast pointer to word instead +of DWORD. +* win32_threads.c (GC_get_next_stack): Don't use and maintain the +latest known stack_min value for WinCE (if GC_get_stack_min is +defined as a macro); update the comments. +* win32_threads.c (GC_wnt): Don't declare for WinCE. + +* Makefile.direct: Document EMPTY_GETENV_RESULTS. +* gcj_mlc.c (GC_clear_stack): Remove declaration. +* malloc.c (GC_clear_stack): Likewise. +* mallocx.c (GC_clear_stack): Likewise. +* typd_mlc.c (GC_clear_stack): Likewise. +* gcj_mlc.c (GENERAL_MALLOC, GENERAL_MALLOC_IOP): Rename to +GENERAL_MALLOC_INNER and GENERAL_MALLOC_INNER_IOP, respectively; +remove "lb" unnecessary cast to word. +* include/private/gc_priv.h (GC_clear_stack): Add declaration. +* include/private/gc_priv.h (GENERAL_MALLOC, GENERAL_MALLOC_IOP): +Move common declaration from typd_mlc.c and malloc.c; remove +unnecessary result and "lb" parameter casts. +* include/private/thread_local_alloc.h: Guard against duplicate +header file inclusion. +* os_dep.c (USE_MUNMAP): Replace "-->" with an error directive for +the case when USE_MMAP is not defined. +* pthread_support.c (GC_is_thread_tsd_valid): New internal +function (only if GC_ASSERTIONS and THREAD_LOCAL_ALLOC); move the +code from thread-local GC_malloc(); add FIXME for the condition. +* win32_threads.c (GC_is_thread_tsd_valid): Likewise. +* thread_local_alloc.c (GC_gcjobjfreelist): Change the type (to +match that of its definition). +* thread_local_alloc.c (GC_destroy_thread_local): Add a cast for +GC_gcjobjfreelist. +* thread_local_alloc.c (GC_lookup_thread, GC_lookup_thread_inner): +Remove unused declaration; don't include pthread.h. +* thread_local_alloc.c (GC_is_thread_tsd_valid): New declaration +(only if GC_ASSERTIONS). +* thread_local_alloc.c (GC_malloc): Use GC_is_thread_tsd_valid() +instead of GC_lookup_thread(). +* win32_threads.c (GC_lookup_thread_inner): Define as STATIC. +* win32_threads.c (UNPROTECT): Rename to UNPROTECT_THREAD (to have +id different from that in os_dep.c). + +* allchblk.c (GC_enough_large_bytes_left): Replace "inline static" +with GC_INLINE. +* include/private/gc_priv.h (fixed_getenv): Likewise. +* alloc.c (GC_max, GC_min): Replace "static INLINE" with +GC_INLINE. +* mark_rts.c (rt_hash): Likewise. +* win32_threads.c (GC_get_max_thread_index): Likewise. +* include/private/gc_priv.h (INLINE): Prefix with "GC_"; include +"static"; define for Sun CC; define for VC++ (and other +compilers). +* pthread_support.c: Don't define __inline__ for non-GNU compilers +(not needed anymore). + +* NT_THREADS_MAKEFILE: Remove file (since it duplicates gc.mak). +* Makefile.in: Remove reference to NT_THREADS_MAKEFILE. +* Makefile.am: Likewise. +* Makefile.dj: Likewise. +* Makefile.direct: Likewise. +* doc/README.win32: Add reference to gc.mak. +* NT_X64_THREADS_MAKEFILE: Likewise. + +* Makefile.direct: Remove references to acinclude.m4, libtool.m4. + +* autogen.sh: Update. + +* Makefile.am: Don't add libtool.m4 to EXTRA_DIST. +* acinclude.m4: Fix underquoting of GC_SET_VERSION. +* README.QUICK: Update information for Makefile. +* Makefile.am: Do not distribute the substituted bdw-gc.pc. +* configure.ac: Add AM conditional analog to KEEP_BACK_PTRS. +* tests/tests.am: Use it here to conditionally enable tracetest +when possible. + +* dyn_load.c (GC_wnt): Update the comment. +* dyn_load.c (GC_register_dynamic_libraries): Add the comment for +_WIN32_WCE_EMULATION; recognize GC_REGISTER_MEM_PRIVATE (new +macro); call GC_is_heap_base() only if check for Type succeeded. + +* mark_rts.c (GC_is_tmp_root): Don't define unless NO_DEBUGGING; +update the comment. +* include/private/gc_priv.h (GC_is_tmp_root): Remove declaration. + +* include/private/gcconfig.h (CANCEL_SAFE, IF_CANCEL): new macros. +* include/private/gc_priv.h (DISABLE_CANCEL, RESTORE_CANCEL, +ASSERT_CANCEL_DISABLED): New macros. +* alloc.c (GC_maybe_gc): Assert cancellation disabled. +(GC_collect_a_little_inner,GC_try_to_collect, GC_collect_or_expand): +Disable cancellation. +(GC_add_to_our_memory): Check for overflow. +* misc.c (GC_cancel_disable_count): declare. +(GC_init, GC_write): Disable cancellation. +(GC_init): Remove redundant GC_is_initialized test. +* os_dep.c (GC_repeat_read): Assert cancellation disabled. +(GC_get_stack_base): Disable cancellation. +* pthread_stop_world.c (GC_suspend_handler_inner): Disable +cancellation. +* pthread_support.c (GC_mark_thread): Permanently disable +cancellation. +(GC_wait_for_gc_completion, GC_wait_builder, GC_wait_marker): +Assert cancellation disabled. +(fork handling): Disable cancellation, fix comment. +(GC_pthread_create): Disable cancellation. +(GC_unregister_my_thread): Disable cancellation. +* Makefile.direct: Document NO_CANCEL_SAFE. + +* Makefile: Remove outdated file (Makefile.direct should be used +instead). + +* include/gc.h (GC_use_DllMain): Refine the comment. + +* configure.ac: Add documentation to AC_DEFINE for GC_THREADS and +EMPTY_GETENV_RESULTS. +* configure.ac: Fix a typo. +* Makefile.am: Likewise. + +* checksums.c (GC_checksum, GC_update_check_page): Remove +"register" keyword in local variable declarations (for the code +used only for debugging or which is not time-critical). +* dbg_mlc.c (GC_has_other_debug_info, GC_store_debug_info, +GC_store_debug_info_inner, GC_check_annotated_obj, GC_print_obj, +GC_print_smashed_obj, GC_debug_end_stubborn_change, +GC_debug_invoke_finalizer): Likewise. +* dyn_load.c (GC_register_dynamic_libraries): Likewise. +* mallocx.c (GC_realloc): Likewise. +* mark_rts.c (GC_print_static_roots, GC_is_static_root, +GC_clear_roots): Likewise. +* misc.c (GC_write): Likewise. +* os_dep.c (GC_print_callers): Likewise. +* dyn_load.c (GC_register_dynamic_libraries): Rename "i" local +variable to "j" for the nested loop (just not to hide the similar +variable in the outer one). +* mark_rts.c (GC_print_static_roots): Output an error message +using GC_err_printf() (instead of GC_printf()). + +* configure.ac: Move include flag from ${INCLUDE} ... +* Makefile.am: ... to AM_CPPFLAGS and also add the build directory. +* configure.ac: Call AM_CONFIG_HEADER([include/private/config.h]). +* configure.ac: Add documentation to all AC_DEFINE either directly +or using AH_TEMPLATE. + +* win32_threads.c (GC_waitForSingleObjectInfinite): New static +function (only if GC_WINMAIN_REDIRECT). +* win32_threads.c (WinMain): Call GC_waitForSingleObjectInfinite() +thru GC_do_blocking() instead of calling WaitForSingleObject() +directly. + +* pthread_support.c (start_mark_threads): Refine printed message. +* win32_threads.c (GC_thr_init): Likewise. + +* Makefile.direct (GC_WINMAIN_REDIRECT): Add the comment for. +* Makefile.direct (NO_GETENV): Update the comment. +* include/gc.h (GC_WINMAIN_WINCE_LPTSTR): Remove macro. +* include/gc.h (GC_WinMain): Remove declaration. +* include/gc.h (WinMain): Define (as GC_WinMain) if and only if +GC_WINMAIN_REDIRECT. +* tests/test.c (GC_COND_INIT): Define as GC_INIT() also in case of +WinCE target unless GC_WINMAIN_REDIRECT is defined. +* tests/test.c (WINMAIN_LPTSTR): New macro. +* tests/test.c (WinMain): Use WINMAIN_LPTSTR instead of LP[W]STR +and GC_WINMAIN_WINCE_LPTSTR. +* win32_threads.c (start_mark_threads): Add the comment for +MARK_THREAD_STACK_SIZE. +* win32_threads.c: Recognize new GC_WINMAIN_REDIRECT macro. +* win32_threads.c (WINMAIN_LPTSTR, WINMAIN_THREAD_STACK_SIZE): New +macro (only if GC_WINMAIN_REDIRECT). +* win32_threads.c: Undefine WinMain macro if GC_WINMAIN_REDIRECT. +* win32_threads.c (GC_WinMain): Add prototype (only if +GC_WINMAIN_REDIRECT). +* win32_threads.c (main_thread_args, WinMain): Rename +GC_WINMAIN_WINCE_LPTSTR to WINMAIN_LPTSTR. +* win32_threads.c (WinMain): Call GC_INIT() instead of GC_init(); +use WINMAIN_THREAD_STACK_SIZE. +* win32_threads.c (WinMain): Call GC_deinit() and +DeleteCriticalSection() only if WinCE; add FIXME. + +* os_dep.c (GC_get_main_stack_base): add assertion for mem_base +value returned by GC_get_stack_base(). + +* Makefile.direct (MUNMAP_THRESHOLD, GC_FORCE_UNMAP_ON_GCOLLECT): +Add the comment for. +* alloc.c (GC_unmap_threshold, GC_force_unmap_on_gcollect): +Declare external variable (only if USE_MUNMAP). +* alloc.c (GC_try_to_collect): Temporarily set GC_unmap_threshold +value to 1 if GC_force_unmap_on_gcollect and restore it before +unlocking (only if USE_MUNMAP). +* doc/README.environment (GC_FORCE_UNMAP_ON_GCOLLECT): Add +information for. +* include/gc.h (GC_set_force_unmap_on_gcollect, +GC_get_force_unmap_on_gcollect): New public function prototype. +* include/gc.h (GC_FORCE_UNMAP_ON_GCOLLECT): New macro is +recognized. +* misc.c (GC_FORCE_UNMAP_ON_GCOLLECT): Likewise. +* include/gc.h (GC_INIT_CONF_FORCE_UNMAP_ON_GCOLLECT): New +internal macro (used by GC_INIT only). +* misc.c (GC_force_unmap_on_gcollect): New global variable. +* misc.c (GC_init): Recognize new "GC_FORCE_UNMAP_ON_GCOLLECT" +environment variable (and set GC_force_unmap_on_gcollect). +* misc.c (GC_set_force_unmap_on_gcollect, +GC_get_force_unmap_on_gcollect): New public function. +* tests/test.c (window_proc): Call GC_set_force_unmap_on_gcollect +to force the mode on if WM_HIBERNATE; restore the mode after +GC_gcollect(). + +* Makefile.direct (LARGE_CONFIG): Update information. +* include/gc.h (GC_stop_func): Refine the comment. + +* configure.ac: Use EMPTY_GETENV_RESULTS instead of NO_GETENV for +Win32 (workaround for Wine bug). + +* allchblk.c (GC_freehblk): Adjust local variables indentation. +* mallocx.c (GC_generic_malloc_many): Likewise. +* typd_mlc.c (GC_malloc_explicitly_typed_ignore_off_page, +GC_calloc_explicitly_typed): Likewise. +* typd_mlc.c (GC_make_array_descriptor): Remove unnecessary +brackets. + +* configure.ac: Replace GC_WIN32_THREADS with GC_THREADS. +* configure.ac: Process enable_parallel_mark option for Cygwin and +Win32; define THREAD_LOCAL_ALLOC for Win32. + +* include/private/gc_priv.h: Define AO_ASSUME_WINDOWS98 if +PARALLEL_MARK (required for VC++ x86). + +* dbg_mlc.c (GC_generate_random_backtrace): Call +GC_try_to_collect(GC_never_stop_func) instead of GC_gcollect(); +if GC is disabled then print error message and return. +* include/gc.h (GC_try_to_collect): Refine the comment. +* include/private/gc_priv.h (GC_never_stop_func): Fix return type; +refine the comment. + +* add_gc_prefix.c: Move the file to the new "extra" directory. +* AmigaOS.c: Likewise. +* gcname.c: Likewise. +* if_mach.c: Likewise. +* if_not_there.c: Likewise. +* MacOS.c: Likewise. +* msvc_dbg.c: Likewise. +* setjmp_t.c: Likewise. +* threadlibs.c: Likewise. +* EMX_MAKEFILE: Prepend setjmp_t.c with "extra" directory. +* Makefile: Prepend AmigaOS.c, MacOS.c, add_gc_prefix.c, gcname.c, +if_mach.c, if_not_there.c, msvc_dbg.c, setjmp_t.c, threadlibs.c +with "extra" directory. +* Makefile.am: Likewise. +* Makefile.direct: Likewise. +* Makefile.dj: Likewise. +* Makefile.in: Likewise. +* NT_MAKEFILE: Prepend msvc_dbg.obj with "extra" directory. +* NT_STATIC_THREADS_MAKEFILE: Likewise. +* NT_X64_STATIC_THREADS_MAKEFILE: Likewise. +* NT_X64_THREADS_MAKEFILE: Likewise. +* NT_THREADS_MAKEFILE: Prepend msvc_dbg.c with "extra" directory. +* gc.mak: Likewise. +* PCR-Makefile: Prepend if_mach.c, if_not_there.c with "extra" +directory. +* SMakefile.amiga: Prepend AmigaOS.c, setjmp_t.c with "extra" +directory. +* doc/simple_example.html: Update for threadlibs.c. +* os_dep.c: Prepend included AmigaOS.c with "extra" directory. + +* include/gc.h (GC_do_blocking, GC_call_with_gc_active): New +function prototype. +* include/private/gc_priv.h (STOP_WORLD): Replace a no-op (for the +single-threaded case) with an assertion check for the state to be +not a "do-blocking" one. +* include/private/gc_priv.h (blocking_data): Move the structure +definition from pthread_support.c; change "fn" return type to void +pointer. +* include/private/gc_priv.h (GC_activation_frame_s): New structure +type. +* include/private/gc_priv.h (GC_push_all_stack_frames): New +function declaration (only if THREADS). +* include/private/gc_priv.h (GC_world_stopped): Don't declare +unless THREADS. +* include/private/gc_priv.h (GC_blocked_sp, +GC_activation_frame_s): New declaration (only if not THREADS). +* include/private/gc_priv.h (GC_push_all_register_frames): New +function declaration (only for IA-64). +* include/private/gc_priv.h (NURSERY, GC_push_proc): Remove +obsolete (unused) symbols. +* include/private/gc_priv.h (GC_push_all_stack_partially_eager): +Remove declaration (since it is static now). +* mark_rts.c (GC_push_all_stack_partially_eager): Move from mark.c +(for code locality) and make STATIC. +* mark_rts.c (GC_push_all_register_frames): New function (only for +IA-64). +* mark_rts.c (GC_push_all_stack_frames): New function (only if +THREADS). +* mark_rts.c (GC_add_trace_entry): New function prototype (used by +GC_push_all_stack_partially_eager(), only if TRACE_BUF). +* mark_rts.c (GC_push_all_stack_part_eager_frames): New function. +* mar_rts.c (GC_save_regs_ret_val): Move the declaration out of a +function body (only for IA-64). +* mark_rts.c (GC_push_current_stack): Call +GC_push_all_stack_part_eager_frames() instead of +GC_push_all_stack_partially_eager(). +* mark_rts.c (GC_push_current_stack): Call +GC_push_all_register_frames() instead of GC_push_all_eager() for +IA-64 backing store. +* misc.c (GC_do_blocking_inner): Declare function (if THREADS +only). +* misc.c (GC_blocked_sp, GC_blocked_register_sp, +GC_activation_frame): New global variables (only if not THREADS). +* misc.c (GC_call_with_gc_active, GC_do_blocking_inner): New API +function (only if not THREADS). +* misc.c (GC_do_blocking): Move the function from +pthread_support.c. +* include/private/pthread_support.h (GC_Thread_Rep): Add +"activation_frame" field. +* pthread_stop_world.c (GC_push_all_stacks): Call +GC_push_all_stack_frames() and GC_push_all_register_frames instead +of GC_push_all_stack() and/or GC_push_all_eager(); don't check for +STACK_GROWS_UP here. +* pthread_support.c (GC_do_blocking_inner): Remove "static"; store +"fn" result back to "client_data" field. +* pthread_support.c (GC_call_with_gc_active): New API function. +* win32_threads.c (GC_call_with_gc_active): Likewise. +* win32_threads.c (GC_Thread_Rep): Add "thread_blocked_sp" and +"activation_frame" fields. +* win32_threads.c (GC_new_thread): Add assertion checking for +thread_blocked_sp is NULL. +* win32_threads.c (GC_do_blocking_inner): New function. +* win32_threads.c (GC_stop_world): Don't suspend a thread if its +thread_blocked_sp is non-NULL. +* win32_threads.c (GC_push_stack_for): Use thread +"activation_frame" (if non-NULL); use "thread_blocked_sp" if +non-NULL (instead of calling GetThreadContext()); "UNPROTECT" the +thread before modifying its last_stack_min; call +GC_push_all_stack_frames() instead of GC_push_all_stack(); update +the comments. + +* alloc.c (GC_default_stop_func): New static variable (initialized +to GC_never_stop_func). +* alloc.c (GC_set_stop_func, GC_get_stop_func): New function. +* alloc.c (GC_timeout_stop_func): Define as GC_default_stop_func +(instead of GC_never_stop_func) if SMALL_CONFIG (or NO_CLOCK), +else call GC_default_stop_func() before getting "current_time". +* alloc.c (GC_maybe_gc): Expand GC_gcollect_inner() macro (for +FIXME comment). +* alloc.c (GC_maybe_gc, GC_collect_a_little_inner): add FIXME for +replacing GC_never_stop_func with GC_default_stop_func (if +possible). +* alloc.c (GC_gcollect): Use GC_default_stop_func. +* alloc.c (GC_collect_or_expand): Use GC_default_stop_func +(instead of GC_never_stop_func) unless it is trigged due to out of +memory; don't increment GC_fail_count and don't output warning +(before trying to collect again) in case the collection has been +interrupted (by GC_default_stop_func) and the heap expansion has +failed too. +* include/gc.h (GC_set_stop_func, GC_get_stop_func): New function +prototypes. + +* os_dep.c (GC_get_stack_base): Add FIXME; add assertion for +GC_get_writable_length() result. + +* configure.ac: Don't use -lpthread -ldl for Cygwin. + +* NT_THREADS_MAKEFILE: Make it back equal to gc.mak. + +* include/private/gcconfig.h (GWW_VDB): Undefine if +USE_GLOBAL_ALLOC (since incompatible). +* os_dep.c (GetWriteWatch_alloc_flag): Define as 0 unless GWW_VDB +is defined. +* os_dep.c (GC_unmap_threshold): Declare (for use in +GC_init_win32) if USE_MUNMAP. +* os_dep.c (GC_init_win32): Turn off memory unmapping if +GlobalAlloc() is used. +* os_dep.c (GC_win32_get_mem): Define and use new +VIRTUAL_ALLOC_PAD macro; don't waste an extra memory page unless +MPROTECT_VDB is in use. + +* Makefile: Replace "version.h" with "include/gc_version.h". +* include/gc_version.h: Likewise. + +* alloc.c (GC_collect_or_expand): Output heap size in WARN() +(before returning FALSE) for convenience. + +* allchblk.c (GC_allochblk_nth): Use GC_PRIdPTR in WARN() format +string. +* pthread_support.c (start_mark_threads, GC_thr_init): Likewise. +* win32_threads.c (GC_delete_thread): Likewise. +* include/private/gc_priv.h (GC_PRIdPTR): New macro. +* pthread_stop_world.c (GC_suspend_handler_inner): Remove +unnecessary cast for WARN argument. +* pthread_support.c (start_mark_threads): if pthread_create() +failed then don't try to create other marker threads and (after +printing a warning) adjust GC_markers and GC_parallel values; log +GC_markers value (possibly adjusted) after that. + +* win32_threads.c (start_mark_threads): if pthread_create() is +failed then don't try to create other marker threads and (after +printing a warning) adjust GC_markers and GC_parallel values. +* win32_threads.c (mark_mutex_event, builder_cv, mark_cv): Move +the definition upper (to be visible in start_mark_threads()). +* win32_threads.c (start_mark_threads): if CreateThread() or +_beginthreadex() is failed then don't try to create other marker +threads and (after printing a warning) adjust GC_markers, +GC_parallel values, and destroy the event objects (either only +some for the uncreated threads if DONT_USE_SIGNALANDWAIT or all if +not a single thread is created). +* win32_threads.c (GC_thr_init): Log GC_markers value (possibly +adjusted) after start_mark_threads() call. + +* Makefile.am: Back remove "GC_" prefix for PTHREADS, +DARWIN_THREADS, WIN32_THREADS (for configure.ac). + +* include/private/gc_priv.h: Change include of config.h to +private/config.h. +* include/private/gc_pmark.h: Likewise. +* gc_cpp.cc: Likewise. +* tests/test.c: Likewise. +* tests/test_cpp.cc: Include private/config.h (if HAVE_CONFIG_H); +undefine GC_BUILD. + +* finalize.c (GC_general_register_disappearing_link): Return +GC_SUCCESS, GC_DUPLICATE, GC_NO_MEMORY (instead of 0, 1 and 2, +respectively). +* include/gc.h (GC_NO_MEMORY): New macro (defined as 2). +* include/gc.h (GC_register_disappearing_link, +GC_general_register_disappearing_link): Update the comment. +* typd_mlc.c (GC_calloc_explicitly_typed): Use GC_NO_MEMORY macro. +* finalize.c (GC_general_register_disappearing_link, +GC_register_finalizer_inner): Recalculate the hash table index +after GC_oom_fn succeeded (since the table may grow while not +holding the lock) and check again that the entry is still not in +the table (free the unused entry otherwise unless DBG_HDRS_ALL). +* finalize.c (GC_register_finalizer_inner): Initialize "hhdr" +local variable (to prevent a compiler warning). +* finalize.c (GC_register_finalizer_inner): Don't modify the data +pointed by "ocd" and "ofn" in GC_register_finalizer_inner() failed +(due to out of memory). + +* alloc.c (GC_set_fl_marks, GC_clear_fl_marks): Transform loop to +suppress compiler "variable might be uninitialized" warnings. + +* Makefile.direct (DONT_USE_SIGNALANDWAIT): Add the comment for. +* win32_threads.c (DONT_USE_SIGNALANDWAIT): Always define for +WinCE. +* win32_threads.c (THREAD_HANDLE): Cast Id (of DWORD type) to +HANDLE thru word type (to avoid a compiler warning) for WinCE. +* win32_threads.c (GC_marker_cv, GC_marker_Id): New static array +(only if DONT_USE_SIGNALANDWAIT). +* win32_threads.c (start_mark_threads): Initialize GC_marker_Id +and GC_marker_cv for each helper thread (only if +DONT_USE_SIGNALANDWAIT). +* win32_threads.c (GC_mark_mutex_state): New static variable (only +if DONT_USE_SIGNALANDWAIT). +* win32_threads.c (GC_mark_mutex_waitcnt, +signalObjectAndWait_func): Don't define if DONT_USE_SIGNALANDWAIT. +* win32_threads.c (GC_acquire_mark_lock, GC_release_mark_lock): +Use InterlockedExchange() over GC_mark_mutex_state (instead of +AO_fetch_and_add()) if DONT_USE_SIGNALANDWAIT. +* win32_threads.c (GC_wait_marker, GC_notify_all_marker): +Implement wait/broadcast primitives using Win32 multiple events +(one for each marker thread) if DONT_USE_SIGNALANDWAIT (instead of +using Win32 SignalObjectAndWait). +* win32_threads.c (GC_thr_init): Don't declare hK32 local +variable, don't check for GC_wnt, and don't initialize +signalObjectAndWait_func if DONT_USE_SIGNALANDWAIT. + +* alloc.c (GC_finish_collection): Call GC_print_finalization_stats +if GC_print_stats (after getting "done_time"). +* finalize.c (GC_old_dl_entries): New static variable (only if not +SMALL_CONFIG). +* finalize.c (GC_finalize): Save current GC_dl_entries value (only +if not SMALL_CONFIG). +* finalize.c (GC_print_finalization_stats): Define if and only if +not SMALL_CONFIG; use GC_old_dl_entries value; use GC_log_printf() +instead of GC_printf(); use "%lu" (instead of "%u") print format +specifier; use unsigned long type for "ready" counter (for LP64 +targets). +* misc.c (GC_dump): No longer call GC_print_finalization_stats() +here (since it is called from GC_finish_collection()). +* misc.c (STACKBASE): Remove unused macro undef (for NOSYS and +ECOS). + +* alloc.c (GC_expand_hp): Replace GC_init_inner() call with +GC_init() one. +* malloc.c (GC_alloc_large, GC_generic_malloc_inner): Likewise. +* mallocx.c (GC_generic_malloc_many): Likewise. +* misc.c (GC_enable_incremental): Likewise. +* alloc.c (GC_expand_hp): Update the comment. +* mark.c (GC_obj_kinds): Likewise. +* win32_threads.c (GC_allow_register_threads): Likewise. +* private/gc_priv.h (GC_init_inner): Remove function declaration. +* misc.c (GC_init_inner): Replace with public GC_init(). + +* gcj_mlc.c (GC_gcj_fake_mark_proc): New static function. +* gcj_mlc.c (GC_init_gcj_malloc): If mp is 0 then supply +GC_gcj_fake_mark_proc (aborting with the appropriate message) +instead. + +* os_dep.c (GC_wince_get_mem): If VirtualAlloc() returns NULL (due +to out of memory) then don't increment GC_n_heap_bases and don't +call VirtualAlloc() again (with MEM_COMMIT). +* os_dep.c (GC_remap): Abort with a more informatory message if +VirtualAlloc() fails due to out of memory; update FIXME. + +* Makefile: Fix typo for msvc_dbg.c. +* Makefile.direct: Likewise. +* Makefile.am: Prefix PTHREADS, DARWIN_THREADS, WIN32_THREADS with +"GC_". +* Makefile.dj: Don't reference remove files (nursery.c, +gc_nursery.h, gc_copy_descr.h). +* NT_MAKEFILE: Don't define __STDC__ macro (no longer used). +* NT_STATIC_THREADS_MAKEFILE: Likewise. +* NT_THREADS_MAKEFILE: Likewise. +* NT_X64_STATIC_THREADS_MAKEFILE: Likewise. +* NT_X64_THREADS_MAKEFILE: Likewise. +* gc.mak: Likewise. +* NT_MAKEFILE: Remove unnecessary -DGC_BUILD (since it is always +defined in the source files). +* NT_THREADS_MAKEFILE: Likewise. +* NT_X64_THREADS_MAKEFILE: Likewise. +* gc.mak: Likewise. +* NT_X64_THREADS_MAKEFILE: Fix typo for -DGC_NOT_DLL. +* NT_STATIC_THREADS_MAKEFILE: Replace GC_WIN32_THREADS with +GC_THREADS. +* NT_THREADS_MAKEFILE: Likewise. +* NT_X64_STATIC_THREADS_MAKEFILE: Likewise. +* NT_X64_THREADS_MAKEFILE: Likewise. +* gc.mak: Likewise. +* NT_MAKEFILE: Define _CRT_SECURE_NO_DEPRECATE to suppress the +compiler warnings. +* NT_STATIC_THREADS_MAKEFILE: Likewise. +* NT_X64_STATIC_THREADS_MAKEFILE: Place -D_CRT_SECURE_NO_DEPRECATE +before "$*.C" (and "$*.CPP"). +* NT_X64_THREADS_MAKEFILE: Likewise. + +* doc/README.solaris2: Replace GC_SOLARIS_THREADS with GC_THREADS. +* doc/README.win32: Replace GC_WIN32_THREADS with GC_THREADS. +* doc/README.win64: Add info about mingw-w64; add note for VC++ +warnings suppression. + +* os_dep.c (GC_forward_exception): Fix logic in several places. +(OSX-specific) + +* include/private/gc_priv.h (MAX_HEAP_SECTS): Guard with ifndef. + +* Makefile.direct: Copy missing information for -DSHORT_DBG_HDRS +from Makefile. +* Makefile: Remove the information about "define arguments" (which +is incomplete and outdated compared to that in Makefile.direct); +add help reference to Makefile.direct. +* Makefile.dj: Likewise. + +* alloc.c (world_stopped_total_time, world_stopped_total_divisor): +Replace "STATIC" with "static" in the definition (since the +symbols aren't prefixed with "GC_"). +* win32_threads.c (marker_sp, marker_bsp, marker_last_stack_min, +start_mark_threads, mark_mutex, builder_cv, mark_cv, +mark_mutex_event, signalObjectAndWait_func, main_thread_start): +Likewise. +* pthread_support.c (GC_wait_builder): Define as STATIC. +* win32_threads.c (GC_wait_builder): Likewise. + +* misc.c (GC_get_heap_size_inner, GC_get_free_bytes_inner): New +API function. +* include/gc_pmark.h (GC_get_heap_size_inner, +GC_get_free_bytes_inner): New function declaration. + +* include/gc.h: Recognize __CEGCC__ (as a synonym for _WIN32_WCE). +* include/gc_config_macros.h: Likewise. +* include/gc.h (GC_MAXIMUM_HEAP_SIZE): Recognize new macro. +* include/gc.h (GC_INIT_CONF_MAXIMUM_HEAP_SIZE): New macro (for +internal use). +* include/gc_config_macros.h: Always include stddef.h if GCC. +* include/gc_config_macros.h (GC_API): Define for CeGCC in the +same way as for MinGW. +* include/gc_config_macros.h (GC_API): Group the definition for +all cases together (check for GC_DLL only once). +* include/gc_pthread_redirects.h: Group non-Darwin code together. +* tests/test.c: Recognize GC_PRINT_VERBOSE_STATS (only if GC_DLL). + +* Makefile.direct (GC_PTHREADS_PARAMARK, GC_IGNORE_GCJ_INFO, +GC_PRINT_VERBOSE_STATS, GC_DONT_EXPAND, GC_INITIAL_HEAP_SIZE, +GC_FREE_SPACE_DIVISOR, GC_TIME_LIMIT, GC_FULL_FREQ): Add the +comment for. +* misc.c (GC_init_inner): Recognize GC_PRINT_VERBOSE_STATS (new +macro). +* dyn_load.c (GC_wnt): Change definition to TRUE for WinCE; add +FIXME and the comment for WinCE. +* gcj_mlc.c (GC_init_gcj_malloc): Recognize GC_IGNORE_GCJ_INFO +(new macro). +* include/gc.h (GC_HAVE_BUILTIN_BACKTRACE): Don't define for VC++ +WinCE (since backtrace() is unimplemented). +* include/private/gc_priv.h (GC_n_heap_bases): Remove declaration +(since static). +* os_dep.c (GC_n_heap_bases): Define as STATIC; move the +definition to be above GC_is_heap_base(). +* include/private/gcconfig.h: Don't define NOSYS for WinCE on ARM +(both for MinGW and CeGCC toolchains). +* include/private/gcconfig.h: Recognize __CEGCC__ and +__MINGW32CE__ (as synonyms for __WIN32_WCE). +* include/private/gcconfig.h: If SH4 then don't set config +parameters for SH. +* include/private/thread_local_alloc.h (GC_key_create): Don't +abort on failures, just return -1 in these cases (this also +prevents compilation error for targets where ABORT is defined +indirectly as an inline assembler sequence). +* mark.c (WRAP_MARK_SOME): Also define for WinCE; add FIXME for +the GCC-based cross-compiler. +* mark.c (ext_ex_regn, mark_ex_handler): Don't define unless +WRAP_MARK_SOME is defined; define also for WinCE case; don't +check for _WIN64 (since WRAP_MARK_SOME is undefined for it). +* mark.c (GC_mark_some): Use __try/__except also for WinCE; update +the comment. +* misc.c: Include signal.h after gc_pmark.h included; check for +MSWINCE instead of _WIN32_WCE. +* misc.c (GC_init_inner): Remove duplicate GC_setpagesize() call. +* misc.c: Don't include for WinCE targets. +* misc.c (GC_write): Define _MAX_PATH if undefined (workaround for +CeGCC toolchain). +* misc.c (GC_write): Use OutputDebugStringW() instead of +_CrtDbgReport() for WinCE targets. +* os_dep.c (GC_least_described_address): Define as STATIC. +* os_dep.c (GC_register_data_segments): Fix code indentation. +* os_dep.c (GC_wince_get_mem): Initialize "result" local variable +(to prevent a compiler warning). +* os_dep.c (GC_dirty_init): Add comment for WinCE target. +* tests/test.c: Don't include winbase.h directly if GCC for WinCE, +include assert.h instead. +* tests/test.c (tiny_reverse_test): Define and use +TINY_REVERSE_UPPER_VALUE macro (4 if VERY_SMALL_CONFIG else 10); +useful for WinCE. +* win32_threads.c (GC_Thread_Rep): Don't declare "handle" field +for WinCE (since thread Id is used as a "real" thread handle). +* win32_threads.c (THREAD_HANDLE): New macro. +* win32_threads.c (GC_register_my_thread_inner): Don't recognize +DONT_IMPORT_GETCURTHREAD anymore; don't record thread handle on +WinCE. +* Makefile.direct (DONT_IMPORT_GETCURTHREAD): Remove comment for. +* win32_threads.c (UNPROTECT, GC_fault_handler_lock): Don't check +for MSWINCE. +* win32_threads.c (GC_delete_gc_thread, GC_delete_thread): Don't +close thread handle on WinCE (since it's a thread Id). +* win32_threads.c (GC_suspend): Don't check for MSWINCE in the +MPROTECT-related code (for the case if MPROTECT_VDB would be +implemented for WinCE). +* win32_threads.c (GC_suspend, GC_start_world, GC_push_stack_for): +Use THREAD_HANDLE(t) to obtain thread handle. +* win32_threads.c (GC_PTHREADS_PARAMARK): New macro recognized; +implicitly define GC_PTHREADS_PARAMARK if GC_PTHREADS; include +pthread.h; define NUMERIC_THREAD_ID(id) if undefined yet; replace +GC_PTHREADS with GC_PTHREADS_PARAMARK where appropriate (for the +parallel mark support). +* win32_threads.c (start_mark_threads): Use int type for "i" local +variable (instead of "unsigned") to prevent a compiler warning. +* win32_threads.c (start_mark_threads): Don't check CreateThread() +result for -1; call CloseHandle() for the handle created by +CreateThread() (on WinCE); don't use errno (since errno.h is +missing on some targets like WinCE) when printing warning on a +marker thread creation failure. +* win32_threads.c (signalObjectAndWait_func): Define for WinCE. +* win32_threads.c (GC_wait_marker): Remove unnecessary assertion +for non-zero signalObjectAndWait_func (to make the code compilable +for WinCE). +* win32_threads.c (GC_thr_init): Allow PARALLEL_MARK for WinCE; +use GC_sysinfo to get processors count if WinCE; don't check for +SignalObjectAndWait() if WinCE; replace GC_PTHREADS with +GC_PTHREADS_PARAMARK. +* win32_threads.c (GC_thr_init): Recognize GC_MIN_MARKERS new +macro (useful for testing parallel marking on WinCE). +* win32_threads.c (GC_win32_start, main_thread_start): Define as +STATIC. +* win32_threads.c: Don't define main_thread_args, +main_thread_start(), WinMain() for WinCE if GC_DLL. +* win32_threads.c (WINCE_MAIN_STACK_SIZE): Remove useless macro +(since the stack size parameter is ignored on WinCE). +* win32_threads.c (main_thread_start): Remove forward declaration; +place its definition before WinMain() one. +* win32_threads.c (WinMain): Abort if GC_CreateThread() or +WaitForSingleObject() failed (for the main thread). + +* allchblk.c (MUNMAP_THRESHOLD): Move macro definition out of +a function. +* allchblk.c (GC_unmap_threshold): New global variable definition +(initialized to MUNMAP_THRESHOLD). +* allchblk.c (GC_unmap_old): Use GC_unmap_threshold instead of +MUNMAP_THRESHOLD; skip unmapping if GC_unmap_threshold is 0. +* doc/README.environment (GC_UNMAP_THRESHOLD): Add information. +* misc.c (GC_unmap_threshold): New variable declaration. +* misc.c (GC_init_inner): Recognize "GC_UNMAP_THRESHOLD" +environment variable to set GC_unmap_threshold value (only if +USE_MUNMAP). + +* dbg_mlc.c (OFN_UNSET): New macro (to detect +GC_register_finalizer() failures). +* dbg_mlc.c (store_old): Add a check for register_finalizer() +failure caused by an out-of-memory event (leave *ofn and *ocd +unmodified in that case). +* dbg_mlc.c (GC_debug_register_finalizer, +GC_debug_register_finalizer_no_order, +GC_debug_register_finalizer_unreachable, +GC_debug_register_finalizer_ignore_self): Initialize my_old_fn +to OFN_UNSET; clear *ocd and *ofn for non-heap objects (the same +as in GC_register_finalizer_inner()). + +* Makefile.direct (GC_DLL): Add the comment for. +* doc/README.macros: Fix a typo. +* doc/README.macros (_DLL, GC_DLL, GC_NOT_DLL): Update info. +* doc/README.macros (__STDC__): Remove info. +* dbg_mlc.c (GC_get_back_ptr_info, GC_generate_random_heap_address, +GC_generate_random_valid_address, GC_print_backtrace, +GC_generate_random_backtrace, GC_register_describe_type_fn): Add +GC_API and GC_CALL to function definition. +* malloc.c (GC_generic_malloc): Likewise. +* mallocx.c (GC_incr_bytes_allocd, GC_incr_bytes_freed): Likewise. +* mark.c (GC_mark_and_push): Likewise. +* misc.c (GC_new_free_list_inner, GC_new_free_list, +GC_new_kind_inner, GC_new_kind, GC_new_proc_inner, GC_new_proc): +Likewise. +* include/gc_backptr.h (GC_get_back_ptr_info, +GC_generate_random_heap_address, GC_generate_random_valid_address, +GC_generate_random_backtrace, GC_print_backtrace): Add GC_API and +GC_CALL to function prototype. +* include/gc_mark.h (GC_mark_and_push, GC_new_free_list, +GC_new_free_list_inner, GC_new_kind, GC_new_kind_inner, +GC_new_proc, GC_new_proc_inner, GC_generic_malloc, +GC_register_describe_type_fn): Likewise. +* include/new_gc_alloc.h (GC_incr_bytes_allocd, GC_incr_mem_freed, +GC_generic_malloc_words_small): Likewise. +* gc_cpp.cc: Include "config.h" (if HAVE_CONFIG_H defined). +* include/private/gc_pmark.h: Likewise. +* include/private/gc_priv.h: Likewise. +* tests/test.c: Likewise. +* gc_cpp.cc: Define GC_BUILD. +* include/private/gc_pmark.h: Likewise. +* include/private/gc_priv.h: Likewise. +* gc_dlopen.c (WRAP_FUNC, REAL_FUNC): New macro. +* gc_dlopen.c (dlopen): Add GC_API to the wrapper function +definition. +* pthread_support.c (GC_pthread_create, GC_pthread_sigmask, +GC_pthread_join, GC_pthread_detach, pthread_sigmask, pthread_join, +pthread_detach, pthread_create): Likewise. +* win32_threads.c (GC_pthread_join, GC_pthread_create, +GC_pthread_sigmask, GC_pthread_detach): Likewise. +* gc_dlopen.c (dlopen): Use WRAP_FUNC and REAL_FUNC macros. +* include/gc_backptr.h: Include "gc.h". +* include/gc_backptr.h: Use extern "C" for the exported functions. +* include/gc_mark.h: Likewise. +* include/gc_config_macros.h (GC_THREADS): Define the macro if any +GC_XXX_THREADS is defined. +* include/gc_config_macros.h (_PTHREADS, _POSIX4A_DRAFT10_SOURCE): +Move the definitions below the place where GC_NETBSD_THREADS and +GC_DGUX386_THREADS are defined. +* include/gc_config_macros.h (GC_DLL): Don't define (even if _DLL +is defined) for GCC. +* include/gc_config_macros.h (GC_API): Define for Cygwin (in the +same way as for VC++); define for GCC v4+ (other than already +recognized MinGW/Cygwin) as a "default" visibility attribute if +GC_DLL is defined. +* include/gc_config_macros.h (GC_ATTR_MALLOC, GC_ATTR_ALLOC_SIZE): +New macro. +* include/gc.h (GC_malloc, GC_malloc_atomic, GC_strdup, +GC_malloc_uncollectable, GC_malloc_stubborn, GC_memalign, +GC_malloc_atomic_uncollectable, GC_malloc_ignore_off_page, +GC_malloc_atomic_ignore_off_page, GC_debug_malloc, +GC_debug_malloc_atomic, GC_debug_strdup, +GC_debug_malloc_uncollectable, GC_debug_malloc_stubborn, +GC_debug_malloc_ignore_off_page, +GC_debug_malloc_atomic_ignore_off_page, +GC_debug_malloc_replacement): Add GC_ATTR_MALLOC attribute. +* include/gc_gcj.h (GC_gcj_malloc, GC_debug_gcj_malloc, +GC_gcj_malloc_ignore_off_page): Likewise. +* include/gc.h (GC_malloc, GC_malloc_atomic, +GC_malloc_uncollectable, GC_malloc_stubborn, +GC_malloc_atomic_uncollectable, GC_malloc_ignore_off_page, +GC_malloc_atomic_ignore_off_page, GC_debug_malloc, +GC_debug_malloc_atomic, GC_debug_malloc_uncollectable, +GC_debug_malloc_stubborn, GC_debug_malloc_ignore_off_page, +GC_debug_malloc_atomic_ignore_off_page, +GC_debug_malloc_replacement: Add GC_ATTR_ALLOC_SIZE attribute +(for the first argument). +* include/gc_gcj.h (GC_gcj_malloc, GC_debug_gcj_malloc, +GC_gcj_malloc_ignore_off_page): Likewise. +* include/gc.h (GC_memalign, GC_realloc, GC_debug_realloc, +GC_debug_realloc_replacement): Add GC_ATTR_ALLOC_SIZE attribute +(for the second argument). +* include/gc.h (GC_malloc, GC_malloc_atomic, GC_strdup, +GC_malloc_uncollectable, GC_malloc_stubborn, GC_memalign, +GC_malloc_atomic_uncollectable, GC_free, GC_base, GC_size, +GC_realloc, GC_expand_hp, GC_set_max_heap_size, +GC_exclude_static_roots, GC_add_roots, GC_remove_roots, +GC_register_displacement, GC_debug_register_displacement, +GC_try_to_collect, GC_malloc_ignore_off_page, +GC_malloc_atomic_ignore_off_page, GC_debug_malloc, +GC_debug_malloc_atomic, GC_debug_strdup, +GC_debug_malloc_uncollectable, GC_debug_malloc_stubborn, +GC_debug_malloc_ignore_off_page, +GC_debug_malloc_atomic_ignore_off_page, GC_debug_free, +GC_debug_realloc, GC_debug_malloc_replacement, +GC_debug_realloc_replacement, GC_finalization_proc, +GC_register_finalizer, GC_debug_register_finalizer, +GC_register_finalizer_ignore_self, +GC_debug_register_finalizer_ignore_self, +GC_register_finalizer_no_order, +GC_debug_register_finalizer_no_order, +GC_register_finalizer_unreachable, +GC_debug_register_finalizer_unreachable, +GC_register_disappearing_link, +GC_general_register_disappearing_link, +GC_unregister_disappearing_link, GC_noop1, GC_warn_proc, +GC_set_warn_proc, GC_ignore_warn_proc, GC_fn_type, +GC_call_with_alloc_lock, GC_stack_base_func, +GC_call_with_stack_base, GC_same_obj, GC_pre_incr, GC_post_incr, +GC_is_visible, GC_is_valid_displacement, GC_same_obj_print_proc, +GC_is_valid_displacement_print_proc, GC_is_visible_print_proc, +GC_malloc_many, GC_CreateThread, GC_beginthreadex, +GC_endthreadex): Comment out (or remove if single and meaningless) +function argument names (to avoid identifiers out of the name +space). +* include/gc_gcj.h (GC_init_gcj_malloc, GC_gcj_malloc, +GC_debug_gcj_malloc, GC_gcj_malloc_ignore_off_page): Likewise. +* include/gc.h (GC_try_to_collect): Update the comment. +* include/gc.h (GC_size, GC_register_my_thread): Add const +qualifier for the argument referent. +* misc.c (GC_size): Likewise. +* pthread_support.c (GC_register_my_thread_inner, +GC_register_my_thread): Likewise. +* win32_threads.c (GC_register_my_thread_inner, +GC_register_my_thread): Likewise. +* include/gc.h (GC_INIT_CONF_ROOTS): New macro for internal use +(define instead of GC_INIT() for Cygwin and AIX). +* include/gc.h (GC_DONT_EXPAND, GC_MAX_RETRIES, +GC_FREE_SPACE_DIVISOR, GC_FULL_FREQ, GC_TIME_LIMIT, GC_IGNORE_WARN, +GC_INITIAL_HEAP_SIZE): Recognize new macro. +* include/gc.h (GC_INIT_CONF_DONT_EXPAND, GC_INIT_CONF_MAX_RETRIES, +GC_INIT_CONF_FREE_SPACE_DIVISOR, GC_INIT_CONF_FULL_FREQ, +GC_INIT_CONF_TIME_LIMIT, GC_INIT_CONF_IGNORE_WARN, +GC_INIT_CONF_INITIAL_HEAP_SIZE): New macro for internal use. +* include/gc.h (GC_INIT): Use GC_INIT_CONF_XXX macros. +* include/gc_mark.h: Prefix GC_H with '_'. +* include/gc_mark.h (GC_least_plausible_heap_addr, +GC_greatest_plausible_heap_addr, GC_debug_header_size): Use GC_API +for the public variable declaration. +* include/new_gc_alloc.h (GC_objfreelist_ptr, GC_aobjfreelist_ptr, +GC_uobjfreelist_ptr, GC_auobjfreelist_ptr): Likewise. +* include/gc_pthread_redirects.h (GC_pthread_create, +GC_pthread_sigmask, GC_dlopen, GC_pthread_join, GC_pthread_detach): +Use GC_API for the wrapper prototype. +* include/gc_pthread_redirects.h (pthread_create, pthread_join, +pthread_detach, pthread_sigmask, dlopen): Undefine unconditionally +before redirecting. +* include/new_gc_alloc.h: Replace GC_incr_mem_freed() with +GC_incr_bytes_freed(); remove FIXME. +* include/private/gc_priv.h (GC_make_closure, +GC_debug_invoke_finalizer, GC_noop): Remove GC_API for the private +function. +* tests/test.c (GC_print_stats): Handle GC_DLL case regardless of +the target. + +* finalize.c (GC_general_register_disappearing_link, +GC_register_finalizer_inner): Remove unnecessary "ifdef THREADS" +guard for LOCK/UNLOCK(). +* finalize.c (GC_general_register_disappearing_link, +GC_register_finalizer_inner): Get GC_oom_fn value before releasing +the lock (to prevent data races). +* gcj_mlc.c (GC_gcj_malloc, GC_debug_gcj_malloc, +GC_gcj_malloc_ignore_off_page): Likewise. +* mallocx.c (GC_generic_malloc_ignore_off_page): Likewise. +* include/gc_inline.h (GC_FAST_MALLOC_GRANS): Use GC_get_oom_fn() +instead of GC_oom_fn (to prevent data races). +* malloc.c (GC_generic_malloc): Likewise. +* mallocx.c (GC_memalign): Likewise. +* pthread_support.c (pthread_create): Likewise. +* gcj_mlc.c (maybe_finalize): Acquire the lock before setting +last_finalized_no value to prevent data races. +* include/gc.h (GC_gc_no, GC_get_gc_no, GC_oom_fn, GC_set_oom_fn, +GC_set_find_leak, GC_set_finalize_on_demand, +GC_set_java_finalization, GC_set_finalizer_notifier, +GC_set_dont_expand, GC_set_full_freq, GC_set_non_gc_bytes, +GC_set_no_dls, GC_set_free_space_divisor, GC_set_max_retries, +GC_set_dont_precollect, GC_set_time_limit, GC_warn_proc): Refine +the comment. +* misc.c (GC_set_oom_fn): Likewise. +* include/gc.h (GC_general_register_disappearing_link): Refine the +comment (replace "soft" word with "weak"). +* misc.c (GC_oom_fn, GC_get_gc_no, GC_get_parallel, +GC_set_finalizer_notifier, GC_set_find_leak): Add the comment. +* misc.c (GC_set_oom_fn, GC_get_oom_fn, GC_set_finalizer_notifier, +GC_get_finalizer_notifier): Use LOCK/UNLOCK to prevent data races. + +* dbg_mlc.c: Guard include with ifndef MSWINCE; include +"private/dbg_mlc.h" before it. +* malloc.c: Likewise. +* dbg_mlc.c (GC_debug_strdup): Use memcpy() instead of strcpy() +for WinCE (since deprecated); evaluate strlen() only once; don't +set errno for WinCE. +* malloc.c (GC_strdup): Likewise. +* dyn_load.c (GC_wnt): Define as macro (FALSE) for WinCE. +* include/gc.h (GC_unregister_my_thread): Refine the comment. +* include/gc.h (GC_uintptr_t, GC_beginthreadex, GC_endthreadex): +Don't declare for WinCE. +* include/gc.h (GC_WINMAIN_WINCE_LPTSTR): New macro (WinCE only). +* include/gc.h (GC_WinMain): Remove GC_API. +* include/gc.h (GC_WinMain): Use GC_WINMAIN_WINCE_LPTSTR for +lpCmdLine. +* tests/test.c (GC_WinMain): Likewise. +* win32_threads.c (main_thread_args, GC_WinMain): Likewise. +* include/gc_config_macros.h (ptrdiff_t): Guard with +ifndef _PTRDIFF_T_DEFINED; define _PTRDIFF_T_DEFINED macro. +* include/private/gc_locks.h: Guard include "atomic_ops.h" with +ifdef GC_PTHREADS (and not GC_WIN32_THREADS). +* mark.c: Include "atomic_ops.h" if PARALLEL_MARK. +* thread_local_alloc.c: Include "atomic_ops.h" if GC_GCJ_SUPPORT. +* win32_threads.c: Include "atomic_ops.h" if MPROTECT_VDB. +* include/private/gc_locks.h: Use include "atomic_ops.h" instead +of include . +* include/private/gc_priv.h: Likewise. +* include/private/gc_locks.h (GC_allocate_ml, GC_need_to_lock): +Don't export (replace GC_API to "extern"). +* win32_threads.c (GC_allocate_ml): Don't export. +* include/private/gc_priv.h (DebugBreak): Define as macro for +WinCE (if not UNDER_CE and DebugBreak is not defined yet). +* include/private/gc_priv.h (UNALIGNED): Rename to UNALIGNED_PTRS +(since "UNALIGNED" is defined in winnt.h of WinCE). +* mark.c (UNALIGNED): Likewise. +* include/private/gcconfig.h (ARM32): Recognize _M_ARM and _ARM_. +* include/private/gcconfig.h (ALIGNMENT): Check always defined. +* include/private/gcconfig.h: Allow GC_WIN32_THREADS for WinCE. +* include/private/thread_local_alloc.h: Define USE_WIN32_SPECIFIC +for WinCE (since __declspec(thread) is unsupported). +* include/private/thread_local_alloc.h (TLS_OUT_OF_INDEXES): +Define for WinCE (if undefined). +* malloc.c (GC_malloc): Remove outdated comment about disabling +signals. +* misc.c: Don't include (since not used anymore and may +break TEXT() macro defined in winnt.h). +* misc.c (GC_init_inner): Don't use GetModuleHandle() and +InitializeCriticalSectionAndSpinCount() for WinCE. +* misc.c (GC_init_inner): Replace GetModuleHandleA() with +GetModuleHandle() (and use TEXT() macro controlled by UNICODE). +* misc.c (LOG_FILE): Remove unused macro; don't use _T() macro. +* misc.c (GC_CreateLogFile): New static function (Win32/WinCE +only); move the code from GC_write(); replace GETENV() with +GetEnvironmentVariable(); replace CreateFileA() with +CreateFile(); use TEXT() macro (for Unicode support); replace +strcat() with memcpy() (since deprecated in WinCE). +* misc.c (GC_write): Define as STATIC. +* win32_threads.c (GC_attached_thread): Likewise. +* misc.c (GC_write): Use GC_CreateLogFile(). +* misc.c: Define vsnprintf macro as StringCchVPrintfA for WinCE. +* misc.c (GC_abort): Try to invoke MessageBoxA() dynamically +(Win32 only) if DONT_USE_USER32_DLL is defined. +* misc.c (GC_abort): Duplicate msg to GC log file (for Win32 and +WinCE). +* misc.c (GC_abort): Use a more user-friendly abort if +NO_DEBUGGING (Win32 only). +* os_dep.c: Include "atomic_ops.h" only if MPROTECT_VDB (and +THREADS). +* os_dep.c (detect_GetWriteWatch): Use TEXT() for GetModuleHandle +(for Unicode support); check GetModuleHandle() result. +* tests/test.c: Don't define assert for WinCE (since may be +redefined by "assert.h" included from libatomic_ops). +* tests/test.c (FAIL): Define as ABORT for all targets (except +for PCR). +* tests/test.c (n_tests): Don't use AO_t. +* tests/test.c (check_heap_stats): Don't cast n_tests. +* tests/test.c (inc_int_counter): New function (for n_tests atomic +incrementation). +* tests/test.c (run_one_test): Test GC_memalign() for all targets. +* tests/test.c (run_one_test): Avoid unbalanced brackets in +#if-#else-#endif blocks. +* tests/test.c (run_one_test): Replace AO_fetch_and_add1() and +private LOCK/UNLOCK with GC_call_with_alloc_lock(inc_int_counter). +* tests/test.c (check_heap_stats): Replace +"if (sizeof(char *) > 4)" with "#if CPP_WORDSZ == 64" to suppress +"unreachable code" compiler warning. +* tests/test.c (WinMain): Set cmd type to LPWSTR (for WinCE +"UNDER_CE" mode); else use LPSTR type (for Win32 and WinCE). +* tests/test.c (thr_window): Replace "L" string prefix with +TEXT(). +* thread_local_alloc.c: Check THREADS is defined (to prevent other +compiler errors and warnings otherwise). +* tests/test.c (WinMain): Recognize GC_NO_DLLMAIN macro (for +GC_use_DllMain()). +* Makefile.direct (GC_NO_DLLMAIN, DONT_IMPORT_GETCURTHREAD): Add +the comments for. +* win32_threads.c (GC_register_my_thread_inner): Recognize +DONT_IMPORT_GETCURTHREAD macro. +* win32_threads.c: Recognize GC_NO_DLLMAIN macro (to exclude +DllMain support if needed). +* win32_threads.c (GC_NO_DLLMAIN): Define implicitly if DllMain +thread registration is unsupported for a given configuration. +* win32_threads.c (GC_use_DllMain): Update the comment; refine +ABORT message. +* win32_threads.c (GC_use_DllMain, +GC_started_thread_while_stopped, GC_register_my_thread_inner, +GC_lookup_thread_inner, GC_delete_gc_thread, +GC_allow_register_threads, GC_lookup_pthread, +GC_push_thread_structures, GC_stop_world, GC_push_all_stacks): +Check for GC_NO_DLLMAIN. +* win32_threads.c (GC_Thread_Rep.tm_in_use, GC_attached_thread, +DllMain): Don't define if GC_NO_DLLMAIN. +* win32_threads.c (GC_stop_world): Declare "i" and "max" local +variables only if not GC_NO_DLLMAIN (to suppress compiler +warning). +* win32_threads.c (GC_mark_thread, start_mark_threads): Use +CreateThread() instead of _beginthreadex() for WinCE. +* win32_threads.c (MARK_THREAD_STACK_SIZE, WINCE_MAIN_STACK_SIZE): +New macros defined (used by start_mark_threads(), WinMain()). +* win32_threads.c (GC_thr_init): Exclude parallel-specific code on +WinCE for now (since getenv(), GetProcessAffinityMask() and +SignalObjectAndWait() are missing on WinCE). +* win32_threads.c (GC_thr_init): replace GetModuleHandleA() with +GetModuleHandle(); replace CreateEventA() with CreateEvent(); use +TEXT() macro (for Unicode support). + +* include/gc.h (GC_has_static_roots_func): New typedef (user filter +callback). +* include/gc.h (GC_register_has_static_roots_callback): Use +GC_has_static_roots_func type. +* dyn_load.c (GC_has_static_roots, +GC_register_has_static_roots_callback): Likewise. +* dyn_load.c (GC_has_static_roots, +GC_register_has_static_roots_callback): Define on all platforms. +* dyn_load.c (GC_register_dynlib_callback, +GC_register_dynamic_libraries, GC_init_dyld): Replace K&R-style +functions definition with the ANSI C one. +* dyn_load.c (GC_register_dynlib_callback): Use new local variable +"callback" (initialized from GC_has_static_roots) to minimize data +races. +* dyn_load.c (GC_register_dynamic_libraries_dl_iterate_phdr, +GC_cond_add_roots): Define as STATIC. +* mark_rts.c (GC_remove_roots_inner): Likewise. +* dyn_load.c (GC_dyld_image_add): Don't call GC_add_roots() for +sections smaller than pointer size (just to avoid acquiring the +lock unnecessarily). +* dyn_load.c (GC_dyld_name_for_hdr): Define unconditionally (not +only for DARWIN_DEBUG). +* dyn_load.c (GC_dyld_image_add): Replace GC_add_roots() call with +LOCK + GC_add_roots_inner() + UNLOCK. +* dyn_load.c (GC_dyld_image_add): Call GC_has_static_roots() user +callback (if set) holding the lock; if it returns 0 then don't call +GC_add_roots_inner() for that region. +* dyn_load.c (GC_register_has_static_roots_callback): Put +"callback" value to GC_has_static_roots on all platforms. +* dyn_load.c (GC_has_static_roots): Update the comments. +* include/gc.h (GC_exclude_static_roots, GC_add_roots, +GC_remove_roots, GC_register_has_static_roots_callback): Likewise. +* include/private/gc_priv.h (struct roots): Likewise. +* include/private/gc_priv.h (GC_remove_roots_inner): Move prototype +to mark_rts.c and declare it as STATIC. +* include/private/gc_priv.h (GC_exclude_static_roots_inner): New +prototype. +* dyn_load.c (GC_register_dynamic_libraries_dl_iterate_phdr): Use +GC_exclude_static_roots_inner() instead of GC_exclude_static_roots. +* misc.c (GC_init_inner): Likewise. +* mark_rts.c (GC_exclude_static_roots_inner): New function (move +all the code from GC_exclude_static_roots(); add the comment. +* mark_rts.c (GC_add_roots_inner, GC_exclude_static_roots_inner): +add alignment assertion for the lower bound; add assertion for the +lower bound to be less than the upper one. +* mark_rts.c (GC_add_roots_inner, GC_exclude_static_roots): Adjust +the upper bound (round down to be of a pointer-aligned value); +return in case of an empty range. +* mark_rts.c (GC_exclude_static_roots): Acquire the lock and call +GC_exclude_static_roots_inner(). +* mark_rts.c (GC_remove_roots): Quickly check the bounds and return +in case of a do-nothing case (before acquiring the lock). + +* finalize.c (GC_fail_count): New external variable declaration. +* finalize.c (GC_reset_finalizer_nested, +GC_check_finalizer_nested): New function declarations (if THREADS +only). +* finalize.c (GC_finalizer_nested, GC_finalizer_skipped): New +static global variables (used internally by GC_finalize() and +GC_check_finalizer_nested()). +* finalize.c (GC_check_finalizer_nested): New static function +definition (only if not THREADS, used internally by +GC_notify_or_invoke_finalizers() to minimize the probability of +a deep recursion when a client finalizer tries to allocate GC +memory). +* finalize.c (GC_finalize): Reset GC_finalizer_nested value (or +call GC_reset_finalizer_nested()) if last heap expansion failed. +* finalize.c (GC_notify_or_invoke_finalizers): Access GC_gc_no, +GC_finalizer_now, GC_finalize_on_demand, GC_finalizer_notifier, +last_finalizer_notification variables holding the lock (to avoid +data races). +* finalize.c (GC_finalizer_notifier): Add comment. +* finalize.c (GC_notify_or_invoke_finalizers): Add "quick" check +for an empty finalization queue (only if THREADS and not +KEEP_BACK_PTRS/MAKE_BACK_GRAPH). +* finalize.c (GC_notify_or_invoke_finalizers): Call +GC_check_finalizer_nested() and skip GC_invoke_finalizers() call +if appropriate. +* include/private/pthread_support.h (GC_Thread_Rep): Add unsigned +finalizer_nested and finalizer_skipped fields (for internal use +by the multi-threaded GC_check_finalizer_nested()). +* win32_threads.c (GC_Thread_Rep): Likewise. +* pthread_support.c (GC_reset_finalizer_nested, +GC_check_finalizer_nested): New function definitions (the +multi-threaded variants of that in finalize.c). +* win32_threads.c (GC_reset_finalizer_nested, +GC_check_finalizer_nested): Likewise. + +* alloc.c (GC_stopped_mark): Remove GC_log_printf("") (not needed +anymore and GCC produces a warning for it). +* alloc.c (GC_stopped_mark): Adjust printf argument type +specifier. +* backgraph.c: Include dbg_mlc.h before ifdef MAKE_BACK_GRAPH (for +the case when the configuration information comes from aconfig +file). +* checksums.c: Likewise. +* include/gc_allocator.h (GC_ATTR_UNUSED): Use "__unused__" +keyword instead of "unused". +* include/gc_allocator.h: Fix typos in comments. +* thread_local_alloc.c: Likewise. +* include/javaxfc.h (GC_finalize_all): Update comment. +* include/private/gc_priv.h (GC_API_PRIV): New macro (defined as +GC_API and serves only as a marker for the private but exported +symbols used by test.c only). +* include/private/gc_priv.h (GC_abort, GC_arrays, GC_is_marked, +GC_printf, GC_err_printf, GC_log_printf): Replace GC_API decl with +GC_API_PRIV one. +* include/private/gc_priv.h (GC_fo_entries): Don't export it +outside a DLL. +* include/private/gc_priv.h (GC_ATTR_FORMAT_PRINTF): New macro +designated to check the arguments correctness of printf-like +functions (currently works only for GCC v3+). +* include/private/gc_priv.h (GC_printf, GC_err_printf, +GC_log_printf): Use GC_ATTR_FORMAT_PRINTF attribute. + +* dyn_load.c (HAVE_DL_ITERATE_PHDR): Break definition from use. +Define for FreeBSD 7.0+. + +* mach_dep.c: Don't include ucontext.h with NO_GETCONTEXT. + +* include/gc_gcj.h (GC_init_gcj_malloc): Improve descriptive +comment. + +* allchblk.c (GC_merge_unmapped): Don't assume that adjacent +free blocks have different mapping status. Correctly handle gap +between blocks. +(GC_split_block): Remove dead code setting hb_flags. Add comment. +(GC_allochblk): Split blocks also in generational-only mode. +* os_dep.c (GC_unmap_gap): Don't really use munmap. + +* include/private/gc_priv.h (GC_unmapped_bytes): Define as 0 for +not USE_MUNMAP case. + +* Makefile.direct (MARK_BIT_PER_OBJ, PRINT_BLACK_LIST, +USE_PROC_FOR_LIBRARIES): Fix typo in the comments. +* Makefile.direct (USE_MMAP, USE_MUNMAP, THREAD_LOCAL_ALLOC, +PARALLEL_MARK, STATIC): Update the comments. +* include/private/gcconfig.h (GC_PREFER_MPROTECT_VDB): New macro +recognized (only if MPROTECT_VDB). +* Makefile.direct (DONT_USE_USER32_DLL, GC_PREFER_MPROTECT_VDB): +Add the comments for. +* os_dep.c (detect_GetWriteWatch): Recognize "GC_USE_GETWRITEWATCH" +environment variable (only if MPROTECT_VDB, if the variable is +unset when GC_PREFER_MPROTECT_VDB macro controls the strategy). +* doc/README.environment (GC_USE_GETWRITEWATCH): New variable. +* include/private/gcconfig.h (MPROTECT_VDB): Add FIXME for +USE_MUNMAP and PARALLEL_MARK cases (to relax the conditions in +the future). +* misc.c (GC_get_heap_size, GC_get_free_bytes): Ignore the memory +space returned to OS (GC_unmapped_bytes). +* include/gc.h (GC_get_heap_size, GC_get_free_bytes): Update the +comments. +* misc.c (GC_get_unmapped_bytes): New API function. +* include/gc.h (GC_get_unmapped_bytes): New API prototype. +* os_dep.c (GC_dirty_init): Move "ifdef GWW_VDB" block out of +"ifdef MSWIN32" one (for Cygwin). + +* pthread_support.c (GC_allow_register_threads): New API function. +* win32_threads.c (GC_allow_register_threads): Likewise. +* include/gc.h (GC_allow_register_threads): New API prototype. +* include/gc.h (GC_register_my_thread, GC_unregister_my_thread): +Update the comments. +* pthread_support.c (GC_register_my_thread): Check the collector +is in the multi-threaded mode. +* win32_threads.c (GC_register_my_thread): Likewise. + +* finalize.c (GC_finalize_all): Always call GC_invoke_finalizers +instead, following Ivan's original patch. + +* allchblk.c (GC_allochblk_nth): Add assertion. +* checksums.c: Add GC_record_fault, GC_was_faulted, +CC_n_faulted_dirty_errors. +(GC_check_dirty): Remove register declarations, print +dirty bit errors on faulted pages. +* os_dep.c (GC_write_fault_handler): Call GC_record_fault(). +* os_dep.c (GC_remove_protection): Compute index correctly. + + +== [7.2alpha2] 2009-06-12 == + +* dbg_mlc.c (GC_print_smashed_obj): Convert a group of printf() +calls into a single one (for output atomicity). +* typd_mlc.c (GC_calloc_explicitly_typed): Don't declare and use +GC_finalization_failures variable; check the result of +GC_general_register_disappearing_link() (for lack of memory) +instead. +* finalize.c (GC_finalization_failures): Remove unused global +variable. +* finalize.c (GC_general_register_disappearing_link, +GC_general_register_disappearing_link): Don't update the value of +GC_finalization_failures (since unused). +* include/private/gc_pmark.h (PUSH_ONE_CHECKED_STACK, +GC_PUSH_ONE_STACK, GC_PUSH_ONE_HEAP): The first parameter is of +word type now (as FIXUP_POINTER requires numeric argument). +* finalize.c (GC_ignore_self_finalize_mark_proc): GC_PUSH_ONE_HEAP +requires the first parameter of word type. +* mark.c (PUSH_GRANULE): Likewise. +* mark.c (GC_push_one, GC_push_all_eager): Likewise. +* finalize.c (GC_finalize_all): Call GC_invoke_finalizers() or +GC_finalizer_notifier directly, instead +of GC_INVOKE_FINALIZERS() to prevent infinite looping. +* include/javaxfc.h: Clarify GC_finalize_all comment. +* gcj_mlc.c: Include gc_pmark.h before "ifdef GC_GCJ_SUPPORT" (not +after) for configuration information. +* gcj_mlc.c (GC_gcj_malloc_ignore_off_page): Add comment. +* gcj_mlc.c (GC_gcj_malloc_ignore_off_page): Check "op" local +variable for NULL before dereferencing it, return GC_oom_fn() in +this case. +* typd_mlc.c (GC_malloc_explicitly_typed, +GC_malloc_explicitly_typed_ignore_off_page): Transform the code to +suppress compiler warning (for uninitialized "lg" variable). + +* win32_threads.c (GC_unregister_my_thread): add false assertion +in unreachable code. + +* pthread_support.c (GC_inner_start_routine): Don't release the +GC lock between GC_register_my_thread_inner() and +GC_init_thread_local() calls (post the "registered" even after +calling GC_init_thread_local()). +* win32_threads.c (GC_register_my_thread, GC_unregister_my_thread): +Use GC_lookup_thread_inner() instead of GC_lookup_thread() and +acquire the GC lock only once. +* win32_threads.c (GC_thr_init): Call GC_register_my_thread_inner() +directly instead of GC_register_my_thread() since I_HOLD_LOCK +and our (main) thread is not registered yet (add assertion for it). +* win32_threads.c (GC_init_parallel): Call GC_lookup_thread_inner() +directly instead of GC_lookup_thread() (since I_HOLD_LOCK). +* win32_threads.c (GC_lookup_thread): Remove unused function. +* win32_threads.c: Remove "#error GC_DLL untested with Cygwin". +* win32_threads.c (GC_win32_dll_threads): Define as FALSE macro +also if THREAD_LOCAL_ALLOC or GC_PTHREADS. +* win32_threads.c (GC_use_DllMain): Call ABORT also if GC_PTHREADS +(for Cygwin). +* win32_threads.c (GC_push_stack_for): Add parentheses around "&&" +(inside GC_ASSERT) to prevent compiler warning. +* win32_threads.c (GC_push_all_stacks): Remove FIXME for +PARALLEL_MARK. +* win32_threads.c (MAX_MARKERS, GC_markers): Move the definitions +to a place before GC_get_next_stack(). +* win32_threads.c (marker_sp, marker_bsp): New static arrays (same +as in pthread_support.c). +* win32_threads.c (marker_last_stack_min): New static arrays (the +same semantics as for last_stack_min of GC_Thread_Rep). +* win32_threads.c (GC_get_next_stack): Handle marker threads. +* win32_threads.c (GC_mark_thread): Save the current stack pointer +to marker_[b]sp. +* win32_threads.c (start_mark_threads): Initialize +marker_last_stack_min elements (to "unset" value). + +* misc.c (GC_set_oom_fn, GC_set_all_interior_pointers, +GC_set_finalize_on_demand, GC_set_java_finalization, +GC_set_finalizer_notifier, GC_set_dont_expand, GC_set_full_freq, +GC_set_no_dls, GC_set_free_space_divisor, GC_set_max_retries, +GC_set_dont_precollect, GC_set_time_limit, GC_set_warn_proc): +Change return type to void (these API functions no longer return +the old value). +* include/gc.h: Likewise. +* tests/test.c (main, WinMain, test): Remove explicit cast to void +for GC_set_warn_proc(). +* misc.c (GC_get_oom_fn, GC_get_all_interior_pointers, +GC_get_finalize_on_demand, GC_get_java_finalization, +GC_get_finalizer_notifier, GC_get_dont_expand, GC_get_full_freq, +GC_get_no_dls, GC_get_free_space_divisor, GC_get_max_retries, +GC_get_dont_precollect, GC_get_time_limit, GC_get_warn_proc): New +API functions (to get the current value of the corresponding R/W +public variables). +* include/gc.h: Likewise. +* include/gc.h (GC_set_warn_proc, GC_set_free_space_divisor): +Update the comment. +* misc.c (GC_ignore_warn_proc): New API call-back function. +* include/gc.h (GC_ignore_warn_proc): Likewise. +* misc.c (GC_set_find_leak, GC_get_find_leak, GC_set_non_gc_bytes, +GC_get_non_gc_bytes): New API setter and getter functions (for the +public GC_find_leak and GC_non_gc_bytes variables, respectively). +* include/gc.h: Likewise. +* include/gc.h (GC_memalign): Add proto to GC API. +* mallocx.c (GC_memalign): Use GC_API, GC_CALL for the definition. +* tests/test.c (run_one_test): Test GC_memalign() on Win32 too, +remove GC_memalign() proto. +* misc.c (GC_write): Use multi-byte (A) variants of Win32 +GetModuleFileName() and CreateFile(). +* tests/test.c (main): Replace K&R-style function definition with the +ANSI C one. + +* include/private/gcconfig.h (PLATFORM_ANDROID): New macro +recognized (for Linux on ARM32 without glibc). +* include/private/gcconfig.h (STRTOULL): Define for all targets +(define as "strtoul" for most targets except for LLP64/Win64). +* misc.c (GC_init_inner): Use STRTOULL instead of atoi/atol() +(cast the result to word type) to decode values of "GC_TRACE", +"GC_INITIAL_HEAP_SIZE", "GC_MAXIMUM_HEAP_SIZE" environment +variables. + +* include/gc_allocator.h: Add gc_allocator_ignore_off_page. +* tests/test_cpp.cc: Add call to gc_allocator_ignore_off_page. + +* win32_threads.c (GC_release_mark_lock): Correct misspelling of +AO_load in assertion. + +* win32_threads.c (MAX_THREADS): Define as 1 if GC_win32_dll_threads +is defined as FALSE (otherwise the size of dll_thread_table is near +200 KiB for 32-bit). +* win32_threads.c (GC_use_DllMain): Optimize for THREAD_LOCAL_ALLOC. +* win32_threads.c (GC_Thread_Rep): Add backing_store_end and +backing_store_ptr fields for IA64 support. +* win32_threads.c (GC_register_my_thread_inner): Set +backing_store_end field to reg_base value for IA64 (same as in +pthread_support.c). +* win32_threads.c (SET_PTHREAD_MAP_CACHE): Put parentheses in the +"right" places, remove ';'. +* win32_threads.c (GC_fault_handler_lock): Declare only +if MPROTECT_VDB (and not WinCE). +* win32_threads.c (GC_suspend): Acquire and release +GC_fault_handler_lock only if MPROTECT_VDB (and not WinCE). +* win32_threads.c (GC_suspend): Define as STATIC. +* win32_threads.c (GC_push_stack_for): Fix WARN() format specifier +(should be word-compliant, "%p" is used w/o "0x"), don't cast sp. +* win32_threads.c (GC_push_all_stacks): Convert a group of printf() +calls into a single one (for output atomicity). +* win32_threads.c (GC_get_next_stack): Unprotect thread descriptor +before altering its last_stack_min ("thread" variable is added). +* win32_threads.c (GC_get_next_stack): Remove unnecessary checks for +"s" is non-NULL. +* win32_threads.c (GC_get_next_stack): Don't call GC_may_be_in_stack +if WinCE. +* win32_threads.c (GC_get_next_stack): Pass current_min value to +GC_get_stack_min as-is (without -1). +* win32_threads.c (GC_wait_marker): Remove FIXME and use "release" +version of AO_fetch_and_sub1(). +* win32_threads.c (GC_win32_start_inner, GC_win32_start): convert int +to pointer (and vice versa) thru word type to suppress warnings. +* win32_threads.c (GC_mark_mutex_waitcnt): Fix comment, always +access atomically. +* misc.c: Change GC_THREADS tests back to THREADS. + +* allchblk.c (GC_print_hblkfreelist, GC_dump_regions): Convert +a group of printf() calls into a single one (for output atomicity). +* include/gc.h (GC_set_all_interior_pointers, GC_set_full_freq, +GC_set_time_limit): New prototypes. +* misc.c (GC_set_all_interior_pointers, GC_set_full_freq, +GC_set_time_limit): New public setter/getter functions. +* include/gc.h: Fix (and remove outdated) comments for thread-local +allocation. +* include/gc.h: Fix typos in comments. +* misc.c (GC_init_inner, GC_printf): Likewise. +* include/gc.h (GC_unregister_disappearing_link): Refine comment. +* include/gc.h (GC_stack_base): Recognize _M_IA64 macro. +* misc.c (GC_stack_last_cleared, GC_min_sp, GC_high_water, +GC_bytes_allocd_at_reset, DEGRADE_RATE): Define only if THREADS. +* misc.c (GC_stack_last_cleared, GC_min_sp, GC_high_water, +GC_bytes_allocd_at_reset): Define as STATIC. +* misc.c (GC_get_heap_size, GC_get_free_bytes, +GC_get_bytes_since_gc, GC_get_total_bytes): Acquire the GC lock to +avoid data races. +* misc.c (GC_write_cs): Define only if THREADS (Win32/WinCE only). +* misc.c (GC_init_inner): Initialize GC_write_cs only if THREADS. +* misc.c (GC_init_inner): Use GC_INITIAL_HEAP_SIZE (if available) to +set the default initial value of initial_heap_sz. +* misc.c (GC_deinit): Destroy GC_write_cs only if THREADS. +* misc.c (GC_init_inner): Fix WARN() format specifier (should be +word-compliant, "%p" is used w/o "0x"). +* misc.c (GC_init_inner): Don't recognize "GC_PAUSE_TIME_TARGET" +environment variable if SMALL_CONFIG. +* misc.c (GC_init_inner): Recognize "GC_FULL_FREQUENCY" environment +variable to set initial GC_full_freq value (if not SMALL_CONFIG). +* doc/README.environment (GC_FULL_FREQUENCY): Add information. +* doc/README.environment (GC_MARKERS): Refine information. +* misc.c (GC_init_inner): Change GC_ASSERT to GC_STATIC_ASSERT where +possible. +* misc.c (IF_NEED_TO_LOCK): New macro (instead of GC_need_to_lock). +* misc.c (GC_write): Use IF_NEED_TO_LOCK for handling GC_write_cs. +* misc.c (GC_abort): Don't define if SMALL_CONFIG. +* misc.c (GC_abort): Directly use WRITE() instead of GC_err_printf() +(to prevent possible infinite recursion). + +* finalize.c (finalization_mark_proc): Replace K&R-style declaration +with ANSI C one. +* finalize.c (GC_grow_table, GC_register_finalizer_inner, +GC_enqueue_all_finalizers): Remove outdated comments about disabling +signals. +* finalize.c (GC_general_register_disappearing_link): Fix assertion +to catch NULL "obj" value. +* finalize.c (GC_unregister_disappearing_link): Check "link" +alignment before gaining the lock. +* finalize.c (GC_finalize): Refine comment. +* finalize.c (GC_finalize): Fix WARN() format specifier (should be +word-compliant, "%p" is used w/o "0x"). +* finalize.c (GC_invoke_finalizers): Initialize "bytes_freed_before" +variable (to 0) to suppress compiler warning. +* include/gc_gcj.h (MARK_DESCR_OFFSET): Move to private/gc_pmark.h. +* include/gc_gcj.h: add "extern C" header and tail. +* include/private/gc_pmark.h: Remove GC_do_parallel_mark(), +GC_help_wanted, GC_helper_count, GC_active_count declarations (move +the comments to the place where these symbols are defined in mark.c). +* mark.c: Add STATIC GC_do_parallel_mark() declaration (for use by +GC_mark_some_inner, if PARALLEL_MARK only). +* mark.c (GC_mark_some_inner, GC_help_wanted, GC_helper_count, +GC_active_count, GC_do_parallel_mark): Define as STATIC. +* pthread_support.c (GC_mark_thread): Likewise. +* typd_mlc.c (GC_explicit_typing_initialized, GC_explicit_kind, +GC_array_kind, GC_ext_descriptors, GC_ed_size, GC_avail_descr, +GC_typed_mark_proc_index, GC_array_mark_proc_index, GC_eobjfreelist, +GC_arobjfreelist): Likewise. +* include/private/gc_pmark.h (PUSH_CONTENTS_HDR): Change GC_ASSERT +for HBLKSIZE to GC_STATIC_ASSERT. +* mark.c (GC_noop): Define for Borland C the same as for Watcom. +* mark.c (GC_noop, GC_mark_and_push): Add ARGSUSED tag. +* pthread_support.c (GC_do_blocking_inner): Likewise. +* mark.c (GC_mark_from): Initialize "limit" (to 0) in the default +switch branch to suppress compiler warning. +* mark.c (GC_return_mark_stack): Append new-line to printf message. +* mark.c: Remove unused GC_true_func(), GC_PUSH_ALL(). +* pthread_support.c (GC_mark_thread): Add dummy "return 0" to +suppress compiler warning. +* pthread_support.c (start_mark_threads): Move the code limiting +"GC_markers" value (and printing a warning) to GC_thr_init(). +* pthread_support.c (GC_thr_init): Silently limit "GC_markers" value +if based on the number of CPUs. +* pthread_support.c (GC_thr_init): Treat incorrect "GC_markers" +values as one. +* pthread_support.c (GC_register_my_thread_inner): Add a check for +"stack_end" is non-NULL (the same as in win32_threads.c). +* pthread_support.c (pthread_create): Call GC_oom_fn before giving up +with ENOMEM. +* thread_local_alloc.c (return_single_freelist): Convert "for" loop +to "while" one to suppress "possible extraneous ';'" warning. + +* darwin_stop_world.c (GC_push_all_stacks): Recognize ARM32. +* include/private/gc_priv.h (GC_THREAD_STATE_T): Define for ARM32 +(Darwin only). +* include/private/gcconfig.h: Add machine-specific part for DARWIN. +* include/private/gcconfig.h (ARM32): Define config parameters for +DARWIN (iPhone). + +* alloc.c (GC_FULL_FREQ, GC_DONT_EXPAND, GC_FREE_SPACE_DIVISOR, +GC_TIME_LIMIT): New macros (used to control the default initial +values of GC_full_freq variable, GC_dont_expand, +GC_free_space_divisor, GC_time_limit respectively). +* include/private/gc_priv.h (TIME_LIMIT): Remove macro (replaced +with GC_TIME_LIMIT in alloc.c). +* alloc.c (GC_need_full_gc, GC_stopped_mark, GC_finish_collection): +Define as STATIC. +* mark_rts.c (GC_push_current_stack, GC_push_gc_structures): Likewise. +* include/private/gc_priv.h (GC_stopped_mark, GC_finish_collection): +Move the prototypes to alloc.c, make STATIC. +* include/private/gc_priv.h (GC_push_current_stack, +GC_push_gc_structures, GC_push_regs_and_stack): Remove prototypes +(move the comments to the places where these functions are defined). +* mach_dep.c (GC_push_regs_and_stack): Move to mark_rts.c and define +as STATIC. +* alloc.c (GC_timeout_stop_func, GC_stopped_mark, +GC_print_heap_sects): Convert a group of printf() calls into +a single one (for output atomicity). +* mark_rts.c (GC_print_static_roots): Likewise. +* alloc.c (GC_stopped_mark): Output blank line (when logging) for +convenience to delimit collections. +* alloc.c (GC_clear_a_few_frames): Rename NWORDS to CLEAR_NWORDS; +make "frames" local variable volatile (to prevent optimization). +* alloc.c (GC_try_to_collect_inner, GC_stopped_mark, +GC_finish_collection, GC_allocobj): Remove outdated comments about +disabling signals. +* include/private/gc_priv.h (GC_register_displacement_inner, +GC_gcollect_inner): Likewise. +* alloc.c (GC_try_to_collect_inner, GC_stopped_mark, +GC_finish_collection): Initialize "start_time" local variable (to 0) +to suppress compiler warning. +* mark_rts.c (GC_add_roots_inner): Likewise. +* alloc.c (GC_RATE, MAX_PRIOR_ATTEMPTS): Guard with "ifndef". +* include/private/gc_priv.h (clock, GC_stop_world, GC_start_world, +GC_acquire_mark_lock, GC_release_mark_lock, GC_notify_all_builder, +GC_wait_for_reclaim, GC_notify_all_marker, GC_wait_marker): Replace +K&R-style function prototypes with ANSI C one. +* include/private/gc_priv.h (ABORT): Define as DebugBreak() for +Win32/WinCE if SMALL_CONFIG (the same as in GC_abort()). +* include/private/gc_priv.h (ROUNDED_UP_WORDS, abs): Remove unused +macros. +* include/private/gc_priv.h (GC_noop): Declare for Borland C the +same as for Watcom. +* mark_rts.c (GC_push_conditional_with_exclusions): Add ARGSUSED tag. + +* dbg_mlc.c (GC_store_debug_info, GC_store_debug_info_inner): Remove +outdated comment about disabling signals. +* mallocx.c (GC_malloc_uncollectable, +GC_malloc_atomic_uncollectable): Likewise. +* os_dep.c: Likewise. +* dbg_mlc.c (GC_debug_change_stubborn, GC_debug_end_stubborn_change): +Add ARGSUSED tag. +* pthread_stop_world.c (GC_suspend_handler, +GC_suspend_handler_inner): Likewise. +* dbg_mlc.c (GC_debug_free, GC_debug_realloc): Fix printf message. +* dbg_mlc.c (GC_debug_realloc): Set "result" to NULL in the default +switch branch to suppress compiler warning. +* dyn_load.c (GC_init_dyld): Use ABORT() instead of GC_abort(). +* include/private/darwin_semaphore.h (sem_init): Likewise. +* include/javaxfc.h: Replace "GC_H" with "_GC_H". +* include/private/dbg_mlc.h (GC_has_other_debug_info, +GC_store_debug_info): Replace K&R-style function prototypes with ANSI +C one. +* include/private/gcconfig.h (GC_FreeBSDGetDataStart, real_malloc, +GC_win32_get_mem, GC_wince_get_mem, GC_unix_get_mem): Likewise. +* include/private/pthread_support.h (GC_stop_init): Likewise. +* include/private/gcconfig.h: Refine comment about setting +GC_stackbottom. +* include/private/gcconfig.h (FIXUP_POINTER): Put parentheses in the +"right" places. +* include/private/pthread_support.h (GC_Thread_Rep): Refine comment +for "stack_end" field. +* mallocx.c (GC_malloc_uncollectable, +GC_malloc_atomic_uncollectable): Remove cast to undefined "hbklk". +* os_dep.c (GC_USE_MEM_TOP_DOWN): New macro (for setting +GC_mem_top_down to MEM_TOP_DOWN for debug purposes). +* os_dep.c (GC_gww_read_dirty, catch_exception_raise): Fix WARN() +format specifier (should be word-compliant, "%p" is used w/o "0x"). +* pthread_stop_world.c (GC_suspend_handler_inner): Likewise. +* os_dep.c (GC_dirty_init): Append new-line to printf messages. +* os_dep.c (GC_mprotect_thread): Fix GC_err_printf message. +* os_dep.c (GC_save_callers): Change GC_ASSERT to GC_STATIC_ASSERT. +* pthread_stop_world.c (GC_retry_signals, GC_suspend_ack_sem): Define +as STATIC. +* pthread_stop_world.c (GC_push_all_stacks): Add assertion for that +"thread_blocked" is not set for the current thread. +* real_malloc.c: Add "extern GC_quiet" to suppress compiler warning. +* reclaim.c (GC_reclaim_all): Initialize "start_time" (to 0) to +suppress compiler warning. + +* tests/test.c (check_heap_stats): Avoid unbalanced brackets in ifdef. + +* win32_threads.c: restructure parallel marking mutex initialization. +* win32_threads.c, alloc.c, darwin_stop_world.c, mallocx.c, mark.c, +pthread_stop_world.c, pthread_support.c: Add runtime conditions +on GC_parallel were appropriate. +* pthread_support.c: Condition marker_bsp on ia64. +(GC_segment_is_thread_stack): Fix loop upper bound. +* reclaim.c: Limit some assertions to PARALLEL_MARK. +* pthread_support.c: Don't acquire mark lock for thread-local +allocation. +* include/private/gc_priv.h: Don't define parallel mark sync +support just for THREAD_LOCAL_ALLOC. + +* include/private/gcconfig.h: refine MINGW32 test. +* mark.c: Add win64/gcc tests. + +* test.c (fork_a_thread, reverse_test, alloc8bytes, tree_test, +typed_test, run_one_test, check_heap_stats, main, test): Replace +all K&R-style function definitions with ANSI C ones. +* trace_test.c (main): Likewise. +* test.c (GC_COND_INIT): Define as GC_INIT() also in case of +THREAD_LOCAL_ALLOC. +* test.c (reverse_test): Call fork_a_thread() only if GC_PTHREADS +or GC_WIN32_THREADS; remove fork_a_thread() macros definition. +* test.c (reverse_test): Use "volatile" when clearing "b" and "c" +local variables (to suppress "assigned value is never used" +compiler warning). +* test.c (tree_test): Use public GC_noop1() instead of private +GC_noop(). +* test.c (typed_test): Likewise. +* test.c (check_heap_stats): Define and assign value to +"late_finalize_count" local variable only if its value is used +(if FINALIZE_ON_DEMAND defined). +* test.c (main): Remove DJGPP-specific initialization of +GC_stackbottom (not needed anymore, handled in gcconfig.h). +* trace_test.c: Guard #define GC_DEBUG with #ifndef. +* trace_test.c: Include "gc_backptr.h". +* trace_test.c (main): Call GC_INIT(). +* trace_test.c (main): Add "return 0" statement. + +* dyn_load.c (GC_register_dynlib_callback): Use new index j +instead of i in the inner loop. + +* tests/test.c: Increment n_tests with fetch_and_add when possible, +avoiding need to export lock. + +* include/gc_pthread_redirects.h: +- dlfcn.h is included for dlopen() proto before undefining +"dlopen" (so, it's possible now to include dlfcn.h after +gc.h from user code); +- GC_dlopen() proto is added (except for Darwin as +it's missing there); +- "dlopen" is explicitly undefined (before its redefinition). +* include/gc.h: +- "process.h" is included besides "windows.h" +(for _beginthreadex/_endthreadex); win32 only. +- GC_NO_THREAD_DECLS is moved to the right place +(before closing "extern C"). +* pthread_support.c: Fix out of memory handling for Thread_Reps. +* win32_threads.c: Don't include process.h on winCE, +improve out of memory handling for thread structures, don't +define GC_beginthreadex and GC_endthreadex for winCE. + +* tests/test.c: Change gcj vtable decriptor type from size_t to +GC_word. + +* gcj_mlc.c: Add comment. +* tests/test.c: Change NTEST to NTHREADS. Fork 5 threads by default. +Run reverse_test a second time in each thread.Add comments. +Don't rely on AO_fetch_and_add. + +* dyn_load.c (GC_register_dynlib_callback, +GC_register_dynamic_libraries_dl_iterate_phdr): Add support +for GNU_PT_RELRO relocations. + +* Makefile, Makefile.direct: GC_SOLARIS_PTHREADS was replaced +by GC_SOLARIS_THREADS. +* include/gc.h: Improve finalizer documentation. +* mips_sgi_mach_dep.s: Replace _MIPS_SIM_ABI32 with _ABIO32. +* pthread_stop_world.c, Makefile.dj: Fix typos. + +* win32_threads.c (GC_new_thread): Make first_thread +visible to the whole file. +(UNPROTECT): New macro. +(GC_push_stack_for, GC_suspend, GC_start_world): unprotect +thread structures before writing. +(GC_suspend): Acquire GC_fault_handler_lock before suspending +thread. +* os_dep.c: export GC_fault_handler_lock. +(GC_remove_protection): Check if already unprotected. + +* doc/README.win32: Add OpenWatcom warning. +* include/private/gcconfig.h: Really check it in. + +* os_dep.c (GC_get_stack_base, windows): Replace with Dave Korn's +code from gcc version. +* os_dep.c: make gc compilable (optionally) for Cygwin with +GetWriteWatch-based virtual dirty bit implementation ("os_dep.c" file). +* os_dep.c: Make non-win32 GC_write_fault_handler STATIC. +* mark.c (GC_noop): fix declaration definition mismatch for DMC. +* include/private/gcconfig.h: Enable MPROTECT_VDB and GWW_VDB for +Watcom (Win32 only). It works. + +* mach_dep.c: Don't use __builtin_unwind_init for register +state on PowerPC/Darwin. + +* doc/gcdescr.html: Improve description of object freelist +structure. +* include/private/gc_priv.h: Fix comment for _size_map. + +* os_dep.c (GC_linux_stack_base): Relax sanity test. + +* include/private/gc_pmark.h (PUSH_CONTENTS_HDR for +MARK_BIT_PER_OBJ): Add missing backslash before eoln. + +* misc.c (GC_set_warn_proc): Implicitly intialize GC on +non-Cygwin win32. + +* configure.ac: Enable thread-local allocation for sparc-linux. + +* alloc.c (GC_try_to_collect): Remove duplicate initialization +check. +* malloc.c (GC_generic_malloc): Remove lw to eliminate single- +threaded warnings. +* mallocx.c (GC_generic_malloc_ignore_off_page): Likewise. + +* allchblk.c, backgraph.c, dbg_mlc.c, dyn_load.c, +finalize.c, include/private/gc_pmark.h, malloc.c, mark.c, +os_dep.c, pthread_stop_world.c, pthread_support.c, reclaim.c, +thread_local_alloc.c. +* misc.c: Refine comment. + +* os_dep.c: Define GC_GWW_BUF_LEN more intelligently. Add FIXME +comment. + +* win32_threads.c (GC_push_stack_for): Yet another attempt +at the stack_min finding logic. Try to clean up the existing code +while minimizing VirtualQuery calls. +(GC_win32_start_inner): Register thread before GC_printf. +Produce more output with DEBUG_THREADS. +*include/gc.h: Update obsolete comments. + +* tests/test.c: +(gcj_class_struct2): Use cast instead of l suffix. +Cast GetLastError to int in various places. +Avoid unused result warning from incr/decr macros. +Add cast for fake_gcj_mark_proc. +Cast GC_gc_no to unsigned in printf. + +* include/gc.h: Fix two typos in comments. + +* finalize.c: Fix typo in comment. + +* blacklst.c (GC_print_source_pointer): Don't call GC_print_heap_obj +with lock. + +* reclaim.c: (GC_reclaim_block): Scan even nearly full blocks +if we are checking for leaks. + +* win32_threads.c: Remove mark lock spinning. +* win32_threads.c, pthread_support.c: Update GC_unlocked_count, +GC_spin_count, and GC_block_count using atomic operations. +* tests/test.c: Declare n_tests as AO_t only if we have threads. + +* win32_threads.c: Support PARALLEL_MARK. Make printf arg +types agree with format specifiers. +Add STATIC for GC_threads. +* include/private/gcconfig.h: Add FIXME comment. +* tests/test.c (run_ine_test): Replace LOCK/UNLOCK use with +AO_fetch_and_add1_full. Declare n_tests as AO_t. +(WinMain): Don't call GC_use_DllMain. +with PARALLEL_MARK or THREAD_LOCAL_ALLOC. + +* alloc.c (GC_try_to_collect_inner): Don't print redundant +GC_bytes_allocd and GC_gc_no. +(GC_stopped_mark): Print average world stop time. +* include/private/gc_priv.h (MS_TIME_DIFF): Add cast. + +* misc.c, doc/README.environment: Add support for +GC_FREE_SPACE_DIVISOR and GC-disable-incremental. +* include/gc.h: Make GC_set_free_space_divisor correspond to +(somewhat unfortunate) reality. + +(Mostly improves LLP64 support.) +* backgraph.c, checksums.c, dbg_mlc.c, finalize.c, mark.c, +misc.c, reclaim.c: Changed some int and long type to word or size_t +(and vice versa where appropriate) +* gcj_mlc.c, include/private/dbg_mlc.h, include/private/gcconfig.h, +include/private/thread_local_alloc.h, mark.c, +misc.c, thread_local_alloc.c, win32_threads.c: Added intermediate +casts to word type when casting from int to pointer (or pointer +to int, or data pointer to code pointer) - just to remove the +corresponding compiler warning. +* ptr_chck.c (GC_is_visible): cast int const to word type to +prevent left shift overflow. +* os_dep.c: change the type of GC_mem_top_down global variable +(containing a flag) to DWORD. +* include/gc_config_macros.h: define GC_SOLARIS_THREADS if GC_THREADS +is defined on SunOS x86_64. +* misc.c (GC_init_size_map): Ifdef out GC_ASSERT as a workaround +for VC++ 2008 amd64 (v15.00.21022.08 for x64) compiler bug +(the compiler gets hung if invoked with -Ox -D +ALL_INTERIOR_POINTERS -D GC_ASSERTIONS) +* backgraph.c: cast GC_gc_no value to unsigned short when +assigned/compared to height_gc_no field of back_edges. +* os_dep.c (GC_remove_protection): Add ARGSUSED. +* win32_threads.c (GC_thread_exit_proc): Remove unused local +variable. +* mark.c (GC_check_dirty): Move declaration out of func body. + +* doc/gcinterface.html: Improve REDIRECT_MALLOC documentation. +* include/gc.h (GC_register_my_thread): Improve comment. + +* Makefile.direct: Add comment for -DCHECKSUMS. + +* thread_local_alloc.c, include/private/thread_local_alloc.h: +Fix typos in comments. +* finalize.c: Declare mark_procs and GC_register_finalizer_inner +STATIC. +* malloc.c (GC_free): Move size calculation below assertion. + +* win32_threads.c (GC_get_stack_min, GC_may_be_in_stack): +Add one entry VirtualQuery cache, I_HOLD_LOCK assertions. +(GC_push_stack_for, GC_get_next_stack) : Hopefully fix WINCE support. + +* finalize.c (GC_general_register_disappearing_link): Add +assertion. +* malloc.c (GC_generic_malloc): Round lb to granules, not words. +* mallocx.c (GC_generic_malloc_ignore_off_page): Round lb to +granules, not words. + +* mach_dep.c (NO_GETCONTEXT): Define for sparc linux. +* configure.ac: Define mach_dep for sparc-linux. + +* mark_rts.c (GC_approx_sp): Use volatile to avoid common +warning. + +* dyn_load.c (GC_cond_add_roots): Fix GC_get_next_stack argument +order. + +* alloc.c, dbg_mlc.c, dyn_load.c, finalize.c, gcj_mlc.c, +include/gc.h, include/gc_config_macros.h, include/gc_cpp.h, +include/gc_gcj.h, include/gc_mark.h, include/gc_typed.h, +include/javaxfc.h, include/private/gc_locks.h, +include/private/gc_priv.h, malloc.c, mallocx.c, mark.c, mark_rts.c, +misc.c, obj_map.c, os_dep.c, pthread_support.c, ptr_chck.c, +stubborn.c, tests/test.c, thread_local_alloc.c, typd_mlc.c +win32_threads.c: Add GC_CALL and GC_CALLBACK macro invocations. +* test.c: Remove some old K&R code. + +* win32_threads.c (GC_may_be_in_stack): New. (GC_Thread_Rep): +Add last_stack_min. (GC_push_stack_for): Use last_stack_min. +(GC_get_next_stack): Add limit argument, use_last_stack_min. +(GC_suspend): make stack_base assignment conditional. +* dyn_load.c (win32 GC_cod_add_roots): Pass limit to +GC_get_next_stack. +* configure_atomic_ops.sh: Remove. +* build_atomic_ops.sh, build_atomic_ops.sh.cygwin, doc/README.win32, +Makefile.direct: Partially support build directories whose path +name contains blanks. +* Makefile.am: Support new files (build_atomic_ops.sh, +build_atomic_ops.sh.cygwin) + +* include/private/gc_locks.h, include/private/gc_pmark.h, +include/private/gc_priv.h, include/private/gcconfig.h, +mach_dep.c, mark_rts.c, misc.c, os_dep.c, pthread_stop_world.c, +pthread_support.c, thread_local_alloc.c, typd_mlc.c, win32_threads.c: +Fix comments. + +* pthread_support.c: Comment out LOCK_STATS. +* include/gc.h: Fix comments. + +* misc.c (GC_init_inner): Enable GC_LOG_FILE on Cygwin. +* include/private/gcconfig.h: Consider USE_MMAP for Cygwin. +* os_dep.c (GC_get_main_stack_base): Use alternate definition +with USE_MMAP. +* include/private/gc_priv.h: Sometimes define SETJMP on Cygwin. + +* doc/README: Make it clearer when Makefile.direct is assumed. +* cord/cord.am: install include/cord.h. + +* win32_threads.c (GC_pthread_join, GC_pthread_start_inner): +Remove unused variables. +* darwin_stop_world.c: Always declare GC_thr_init(). +* dbg_mlc.c (GC_debug_free_inner): Don't touch oh_sz if +SHORT_DBG_HDRS is defined. +* include/private/gc_pmark.h (OR_WORD_EXIT_IF_SET, parallel +mark, USE_MARK_BITS version): Refer to correct parameter name. + +* finalize.c (GC_general_register_disappearing_link): Remove +redundant code. +* gcj_mlc.c (GC_init_gcj_malloc): Add cast to signed. +* os_dep.c: (GC_write_fault_handler): Remove remaining +references to deleted variable "code". Remove redundant +FREEBSD definitions. +* include/private/gcconfig.h (GWW_VDB): Define for X86_64 when +defined for X86. (STATIC): Define as "static" with NO_DEBUGGING. + +* include/private/gc_priv.h: Update MAX_HEAP_SECTS. + +* dbg_mlc.c (GC_print_smashed_obj): Increase robustness with +smashed string, (GC_debug_free_inner): Mark as free. +* mallocx.c (GC_malloc_many): Always clear new block if +GC_debugging_started. +* reclaim.c: Move GC_debugging_started from +GC_reclaim_small_nonempty_block() to GC_reclaim_generic(), +which is also called directly. +* doc/README: Fix spelling error. Update license summary. +* include/gc.h (GC_PRE_INCR3, GC_POST_INCR3): add (void **) casts. +* tests/test.c: Don't define GC_DEBUG if already defined. + +* doc/simple_example.html: update --enable-full-debug reference, +Make HTML formatting standards compliant. +* doc/debugging.html, doc/leak.html: Fix HTML formatting bugs. +* doc/gcinterface.html: specify encoding. + +* doc/simple_example.html: Update thread-local allocation +description. + +* configure.ac: Check for gc-debug earlier; replace remaining +full-debug tests. +* include/gc.h, ptr_chck.c (GC_pre_incr, GC_post_incr): +Use signed offset type. Use ptr_t internally. +* doc/gcinterface.html: Update LOCAL_MALLOC description. +* doc/README.autoconf, doc/leak.html, doc/README.DGUX386: +Fix full-debug reference. +* include/gc.h: Rewrite GC_..._INCR and friends. +* tests/test.c: Minimally test GC_..._INCR and friends. + +* mark.c: (GC_push_next_marked, GC_push_next_marked_dirty, +GC_push_next_marked_uncollectable): Never invoke GC_push_marked +on free hblk. +* headers.c: Test COUNT_HDR_CACHE_HITS not USE_HDR_CACHE. +(GC_header_cache_miss): Always blacklist pointers for free +hblks. Add assertion and comment. +* pthread_support.c (GC_register_my_thread): Fix #if indentation. +* include/private/gc_hdrs.h: USE_HDR_CACHE is no longer tested. +Delete it. +* include/private/gc_pmark.h: (PUSH_OBJ): Add assertion. + +* alloc.c, include/gc_mark.h, Makefile.direct: Improve comments. + +* configure.ac: Set win32_threads on MinGW. + +Ivan's description of the patch follows. Note that a few pieces like +the GC_malloc(0) patch, were not applied since an alternate had been +previously applied. A few differed stylistically from the rest of +the code (mostly casts to void * instead of target type), +or were classified as too minor to bother. Note that +all of Ivan's static declarations which did not correct outright +naming bugs (as a few did), where replaced by STATIC, which is +ignored by default. + +- minor bug fixing (for FreeBSD, for THREAD_LOCAL_ALLOC and for +GC_malloc(0)); +- addition of missing getter/setter functions for public variables +(may be useful if compiled as Win32 DLL); +- addition of missing GC_API for some exported functions; +- addition of missing "static" declarator for internal functions +and variables (where possible); +- replacement of all remaining K&R-style definitions with ANSI +C ones (__STDC__ macro is not used anymore); +- addition of some Win32 macro definitions (that may be missing in +the standard headers supplied with a compiler) for GWW_VDB mode; +- elimination of most compiler warnings (except for +"uninitialized data" warning); +- several typos correction; +- missing parenthesis addition in macros in some header files of +"libatomic_ops" module. + +My highlights based on reading the patch: + +* allchblk.c: Remove GC_freehblk_ptr decl. +Make free_list_index_of() static. +* include/gc.h: Use __int64 on win64, define GC_oom_func, +GC_finalizer_notifier_proc, GC_finalizer_notifier_proc, +add getter and setters: GC_get_gc_no, GC_get_parallel, +GC_set_oom_fn, GC_set_finalize_on_demand, +GC_set_java_finalization, GC_set_dont_expand, +GC_set_no_dls, GC_set_max_retries, GC_set_dont_precollect, +GC_set_finalizer_notifier. Always define GC_win32_free_heap. +gc_config_macros.h: Define _REENTRANT after processing +GC_THREADS. +* include/gc_cpp.h: Improve GC_PLACEMENT_DELETE test, +handling of operator new[] for old Windows compilers. +* include/gc_inline.h (GC_MALLOC_FAST_GRANS): Add parentheses +around arguments. +* dbg_mlc.c, malloc.c, misc.c: Add many GC_API specs. +* mark.c (GC_mark_and_push_stack): Fix source argument for +blacklist printing. +* misc.c: Fix log file naming based on environment variable +for Windows. Make GC_set_warn_proc and GC_set_free_space_divisor +just return current value with 0 argument. Add DONT_USE_USER32_DLL. +Add various getters and setters as in gc.h. +* os_dep.c: Remove no longer used GC_disable/enable_signals +implementations. (GC_get_stack_base): Add pthread_attr_destroy +call. No longer set GC_old_bus_handler in DARWIN workaround. +* pthread_support.c: GC_register_my_thread must also +call GC_init_thread_local. + +* Makefile.direct, mach_dep.c: Add support for NO_GETCONTEXT. +* mach_dep.c: Include signal.h. +* gc_priv.h: Factor out INLINE declaration. + +* include/private/gcconfig.h: Update MIPS/LINUX config. +* doc/gcdescr.html: Fix typo. +* mach_dep.c (GC_with_callee_saves_pushed): Don't rely on getcontext +for MIPS/LINUX. + +* configure.ac: SPARC fixes. +* thread_local_alloc.c(GC_mark_thread_local_fls_for): Include +size 0, except for gcj. +* doc/gc.man: Expand C++ cautions. +* include/gc_inline.h: Fix comments. + + +== [7.1] 2008-05-03 == + +* doc/gcinterface.html: Improve C++ interface documentation. + +* allchblk.c (GC_allochblk): Check for overflow during size +rounding. +* tests/huge_test.c: New. +* Makefile.direct, tests/tests.am: Add huge_test.c + +* pthread_support.c: Fix typo in comment. +* os_dep.c (GC_win32_get_mem): Add heap section only if +allocation succeeded. + +* malloc.c: (free replacement) Fix caller address space check. + +* finalize.c (GC_grow_table): Dereference table in null-check. + +* win32_threads.c (GC_delete_gc_thread, GC_delete_thread): +Consistently call CloseHandle. (GC_suspend): Call +GC_delete_gc_thread. +* tests/test.c: Don't reference GC_print_stats if not exported. + +* tests/test.c (run_one_test): Don't mention pthread_self(). +* misc.c: Declare GC_thr_init(). + +* allchblk.c (add_to_fl): disable assertions with USE_MUNMAP, +and refine assertions to handle huge unmergable blocks. +(GC_allochblk_nth): Add comment. + +* include/private/gcconfig.h: Add missing FREEBSD macro +consistency test. + +* allchblk.c (GC_enough_large_bytes_left): No longer take +parameters; return free list index bound. +(GC_merge_unmapped): Don't access nexthdr until after null test. +(Fixes bug in 1/29/08 check-in.) (GC_allochblk): Calculate +when splitting is allowable only once here, not when considering each +block. (GC_allchblk_nth): Accept new may_split parameter. +Avoid some redundant tests for exact size matches. +* alloc.c (GC_should_collect): Cache min_bytes_allocd. +(GC_maybe_gc): Make locking assertion testable. +* mark_rts.c: Fix indentation. +* pthread_stop_world.c: Replace old GC_err_printf1 reference. +* tests/test.c: Remove (void) casts. Optionally print some +timing information. + +* windows-untested/gc.def: Remove CreateThread line. +* windows-untested/README: New file. +* win32_threads.c (GC_use_DllMain): Force collector initialization. +* include/gc.h (GC_use_DllMain): Clarify usage rules in comment. +* mark.c (GC_mark_from): Slightly simplify GC_DS_PER_OBJECT code. +* include/gc_cpp.h: Add matching placement delete overloads +everywhere. +* include/private/gc_locks.h (NO_THREAD): Add cast. +* include/private/gcconfig.h: Add test for __HP_aCC. +* configure.ac, tests/tests.am: Avoid libgccpp on HP/UX. + +* doc/README.win32: Fix typo. +* configure.ac: Fix printing of enable-shared result. + +* misc.c (GC_init_inner): Assert !GC_need_to_lock only when +defined. (GC_call_with_stack_base): Add GC_API. +* os_dep.c (GC_get_stack_base): Add GC_API. +* win32_threads.c: (GC_register_my_thread, GC_unregister_my_thread): +Add GC_API. +* include/gc.h: Add GC_API annotations. +* include/private/gc_locks.h: Define UNCOND_LOCK etc. also for +PCR. +* include/private/gc_pmark.h: Fix comments. + +* include/private/gc_priv.h, mark_rts.c, typd_mlc.c: +Add GC_push_typed_structures() to push GC_ext_descriptors. + +* tests/test.c: Call GC_INIT for DARWIN; test system type using +gcconfig.h-defined macros. + +* allchblk.c (GC_merge_unmapped, GC_freehblk): Refuse to create +blocks large enough that their size, when interpreted as a signed +value, would be negative. +* include/private/gc_priv.h: Comment hb_sz range limit. + +* mark.c (GC_push_next_marked): correct comment. +* Makefile.direct: document NO_PROC_STAT. +* include/private/gcconfig.h: Accommodate NO_PROC_STAT. + + +== [7.1alpha2] 2008-01-10 == + +* Makefile.am: Mention atomic_ops.c and atomic_ops_sysdeps.S +again. Refer to build directory as ".". + +* configure.ac: Ignore --enable-parallel-mark on Darwin for now. +* darwin_stop_world.c: Add FIXME comment for parallel marker. + +* include/private/gc_priv.h: Update MAX_ROOT_SETS +and LOG_PHT_ENTRIES to handle larger heaps. + +* include/gc.h (GC_INIT,GC_init): Update comments. + +* allchblk.c, alloc.c, include/private/gc_priv.h: +Track GC_bytes_dropped and use in GC triggering decisions. +* alloc.c (min_bytes_allocd): Weight atomic blocks less. + +* alloc.c (GC_add_to_heap): Call GC_install_header(p) AFTER +adjusting p. + +* Makefile.am: Add NT_X64_THREADS_MAKEFILE. + +* NT_X64_STATIC_THREADS_MAKEFILE: Clean up obsolete comment. +* alloc.c: Add declaration for GC_add_current_malloc_heap. +* win32_threads.c (GC_beginthreadex): Clean up error +return code. +* doc/README.win64, NT_X64_THREADS_MAKEFILE, Makefile.direct: +Add NT_X64_THREADS_MAKEFILE. + +* alloc.c: Define GC_version instead of in version.h. +* version.h: Remove. +* include/gc_version.h: Move most of version.h here. +* include/gc.h: Include gc_version.h. +* gcname.c, add_gc_prefix.c: include gc.h instead of version.h. +* Makefile.direct, Makefile.dj, Makefile.am, include/include.am: +Adjust for version.h rename. + +* configure.ac: Put libatomic_ops links in build directory. +* Makefile.am: Don't mention atomic_ops.c and atomic_ops_sysdeps.S +as nodist sources. + +* include/gc.h, doc/README.macros: Add GC_NO_THREAD_REDIRECTS, +GC_NO_THREAD_DECLS, don't test explicitly for GC_SOLARIS_THREADS. + +* alloc.c: Deal correctly with address wrapping for +GC_greatest_plausible_heap_addr and GC_least_plausible_heap_addr. +* finalize.c, include/gc.h (GC_register_disappearing_link, +GC_register_finalizer_inner): Improve out-of-memory handling. +* include/private/gc_pmark.h: Fix comment spelling. + +* include/gc_inline.h, include/gc_tiny_fl.h: cleanups to make usable +in other contexts. + +* include/gc.h: Don't define GC_HAVE_BUILTIN_BACKTRACE for uclibc. + +* gc_cpp.cc: Don't include gc_cpp.h from local directory. + +* allchblk.c, configure.ac (add --enable-munmap) + +* dyn_load.c (GC_dyld_image_add): Remove ifdef clause and use the macro +GC_GETSECTBYNAME instead. +* include/private/gc_priv.h: Define GC_GETSECTBYNAME according to the +architecture (Darwin). + +* reclaim.c (GC_bytes_found): Expand comment. +* thread_local_alloc.c (GC_malloc_atomic, GC_gcj_malloc): Pass +granules, not bytes, to GC_FAST_MALLOC_GRANS. +* include/gc.h: Never include gc_local_alloc.h. +* tests/test.c: Add size zero allocation tests. + +* malloc.c: Update GC_large_allocd_bytes on explicit deallocation. +* allchblk.c: Sanity check GC_max_large_allocd_bytes. + +* Makefile.direct: Invoke $(MAKE) instead of make. + +* doc/scale.html: Reflect gc7 thread local allocation behavior. + +* include/extra/gc.h, include/extra/gc_cpp.h: New. +* include/include.am: Install gc.h and gc_cpp.h in $(prefix)/include +again. + +* pthread_support.c (GC_thr_init): Use sysconf(_SC_NPROCESSORS_ONLN) +for HURD. + +* include/private/gcconfig.h: Add Linux/mips-64 support. + +* dbg_mlc.c: Use random() on all glibc systems. +* mach_dep.c (GC_with_callee_saves_pushed): Don't use getcontext() on +HURD. Add comment. +* pthread_stop_world.c (GC_suspend_handler, GC_stop_init): Accommodate +systems without SA_SIGINFO. + +* include/gc.h (GC_PTR_STORE): Fix non-DEBUG parentheses. +* tests/test.c (run_one_test): Add GC_PTR_STORE test. +No longer test for RS6000. + +* alloc.c, backgraph.c, headers.c, include/private/gc_priv.h: +Maintain GC_our_memory and GC_n_memory. +* dbg_mlc.c (GC_print_smashed_obj): Improve message. +(GC_print_all_smashed_proc): Pass client object address instead of +base. +* dyn_load.c (sort_heap_sects): New. (GC_register_map_entries): +Register sections that are contiguous and merged with our heap. +* malloc.c, os_dep.c (GC_text_mapping): Check for just base name +of libraries. +* malloc.c (calloc): Check for special callers even with +USE_PROC_FOR_LIBRARIES. Move assertion. Add rudimentary +malloc/free tracing. +* misc.c: No longer call GC_init_lib_bounds explicitly. +* thread_local_alloc.c (GC_malloc, GC_malloc_atomic): Always +initialize on demand. +* tests/test.c: Call GC_INIT only when required. + +* Makefile.direct: Remove comment fragment. +* tests/tests.am: Add smashtest. +* configure.ac: Define GC_USE_DLOPEN_WRAP with redirect-malloc. +* pthread_support.c: Fix comment spelling. +* include/private/gcconfig.h: Define USE_PROC_FOR_LIBRARIES with +GC_LINUX_THREADS and REDIRECT_MALLOC. +* tests/smash_test.c: Initial check-in. +* obj_map.c: Print log entry to correct file. +* include/private/thread_local_alloc.h: Add TlsAlloc error check. + +* alloc.c (GC_stopped_mark): Call GC_add_current_malloc_heap() +while world is still running. +* os_dep.c (GC_is_heap_base): Don't call GC_add_current_malloc_heap() +with world stopped. +* include/gc.h (GC_INIT for cygwin): Always call GC_add_roots. +* misc.c (GC_init/GC_init_inner): Perform all work in +GC_init_inner. +* Makefile.direct: Expand -DUSE_MUNMAP comment. + +* include/gc.h: Define uintptr_t explicitly for VC++6. +* msvc_dbg.c (GetModuleBase): Revert to strcat if strcat_s doesn't +exist. + + +== [7.0] 2007-07-02 == + +* include/gc_config_macros.h: Also check for IA64 when setting +GC_HPUX_THREADS. +* mallocx.c: Change my_bytes_allocd to signed_word. +* include/gc_pthread_redirects.h: Remove obsolete Solaris threads +(as opposed to pthreads) support. + +* mach_dep.c (GC_with_callee_saves_pushed): Don't use getcontext() +on ARM/Linux. Check getcontext() return value. + +* backgraph.c (per_object_func): Make argument types consistent. +(GC_traverse_back_graph): Mark GC_deepest_obj. + +* finalize.c (GC_finalize): Change dl_size and fo_size to size_t. +* os_dep.c (GC_win32_get_mem): Add GC_mem_top_down option. + +* doc/README.win32, doc/README, README.QUICK: Fix some of the worst +anachronisms. +* dyn_load.c: Partially support cygwin, but don't enable it yet. + +* Makefile.am: Use -no-undefined for libgc. +* Makefile.direct: Document USE_PROC_FOR_LIBRARIES. +* dyn_load.c (GC_register_map_entries): Rename prot_buf to prot +consistently. +* misc.c: Fix some WARN calls. Move GC_is_initialized setting and +GC_thr_init() call. +* os_dep.c: Consistently use WARN where appropriate. +* thread_local_alloc.c: Revert change to GC_WIN32_THREADS test. Instead +remove inappropriate pthread.h include. +* doc/README.linux: Remove some anachronisms. + +* alloc.c: Also use GC_check_tls on non-Linux systems. +* mallocx.c (GC_reclaim_generic): Remove bogus declaration. +* include/private/gc_priv.h (GC_reclaim_generic): Declare correctly +with prototype. + +* alloc.c (GC_adj_bytes_allocd): Avoid (long) casts, fix comment. +(GC_print_heap_sects): Use size_t instead of unsigned long. +* thread_local_alloc.c (GC_lookup_thread): Define in the correct +context. +* win32_threads.c, include/gc_config_macros.h: The last of Romano +Paolo Tenca's patch. Move stdint.h include to gc_config_macros.h. +* include/gc_inline.h: Avoid gc_priv.h dependencies. +* tests/test.c (check_heap_stats): Replace unsigned long with size_t. + +* NT_X64_STATIC_THREADS_MAKEFILE: Replace obsolete -debugtype:cv. +* mark_rts.c (GC_push_roots): Fix kind type. + +* doc/README.win64: New file. +* doc/doc.am, Makefile.direct: Add README.win64. + +* Makefile.am, Makefile.direct: Add NT_X64_STATIC_THREADS_MAKEFILE. +* NT_X64_STATIC_THREADS_MAKEFILE: Fix warning flags. +* allochblk.c, alloc.c, blacklst.c, dbg_mlc.c, dyn_load.c, +finalize.c, headers.c, mach_dep.c, malloc.c, mark.c, misc.c, +obj_map.c, os_dep.c, ptr_chck.c, reclaim.c, typd_mlc.c, +win32_threads.c, cord/de_win.c, include/gc_mark.h, +include/private/gc_hdrs.h, include/private/gc_pmark.h, +include/private/gc_priv.h, tests/test_cpp.cc: +Replace old style function declarations. Clean up integral types. +Remove register declarations. The change in malloc.c and the +"int descr" declaration in mark.c are the most likely to have +been real bugs outside of win64. +* msvc_dbg.c: Disable on win64. +* win32_threads.c: Add AMD64 support. +* include/gc.h: no backtrace on AMD64 for now. + +* msvc_dbg.c(GetModuleBase): Replace strcat with strcat_s. + +* include/gc.h: (GC_word, GC_signed_word): Fix win64 definitions. +Don't include windows.h in an extern "C" context. +* include/private/gcconfig.h: Fix win64/X86_64 configuration. +* tests/test.c: Eliminate more old style function definitions. +Cleanup pointer and integer casts for win64. +* tests/test_cpp.cc: Don't include gc_priv.h. +* NT_STATIC_THREADS_MAKEFILE: Restrict suffixes for VC++ 2005. +* NT_X64_STATIC_THREADS_MAKEFILE: New. + +* win32_threads.c: Separate out DEBUG_WIN32_PTHREADS_STACK. Ignore +FINISHED threads for suspension. (GC_pthread_join): Add +pthread_self() cast. (GC_pthread_start_inner): Execute cleanup +handler when popping it. +* include/private/gc_locks.h: Inline THREAD_EQUAL for +GC_WIN32_PTHREADS. Define USE_PTHREAD_LOCKS only if we have +pthreads. + +* gc_dlopen.c, thread_local_alloc.c, threadlibs.c, win32_threads.c, +tests/test.c: Accommodate GC_WIN32_PTHREADS. +* include/gc.h: Don't include windows.h for GC_WIN32_PTHREADS. +* include/gc_config_macros.h: Define both PTHREADS and +GC_WIN32_THREADS. +* include/private/gc_locks.h: Nonstandard definitions of +NUMERIC_THREAD_ID for GC_WIN32_PTHREADS. +* doc/README.win32, Makefile.direct: Include documentation +for GC_WIN32_PTHREADS. +* Makefile.direct: Remove some anachronisms in the documentation. + +* Makefile.am: Move includes to bottom. Add better library +dependencies. Increment library version. Remove "SUBDIRS += .". +* cord/cord.am, tests/tests.am: Add better library dependencies. +Remove now unnecessary dependencies. +* include/gc.h (GC_beginthreadex, GC_endthreadex, GC_ExitThread): +Move to define on all Windows platforms. (_beginthread): define +to generate error if used. + +* include/private/gc_locks.h: Format to 80 columns. + +* malloc.c(GC_free): Ignore bad frees on MSWIN32 with REDIRECT_MALLOC. +* NT_MAKEFILE: msvc_dbg.h is in include/private. Don't use cvars +rc. +* misc.c (WIN32 GC_write): Define GC_need_to_lock in single-threaded +case. +* win32_threads.c: Test for __MINGW32__ in addition to _MINGW_VER. +(GC_CreateThread, GC_beginthreadex): Deallocate args even if we fail. +* include/gc.h: Add GC_reachable_here(). (GC_WinMain): Add GC_API. +(GC_beginthreadex, GC_endthreadex, GC_ExitThread): Declare. +* tests/test.c: Add GC_reachable_here() call. + +* alloc.c (GC_try_to_collect): Call GC_init if necessary. +* tests/thread_leak_test.c: Don't unconditionally define +GC_LINUX_THREADS. + +* Makefile.am: Remove extra_ldflags_libgc definition. + +* include/private/gc_priv.h: Define AO_REQUIRE_CAS. + +* finalize.c (GC_unreachable_finalize_mark_proc): Don't return void +value. + + +== [7.0alpha9] 2007-05-15 == + +* Some gc6.9 changes. +* Change FindTopOfStack decl in darwin_stop_world.c. +* Move some static tests from misc.c to gcconfig.h. Use #error. +* Add GC_print_free_list() function (thanks to Bruce Hoult). +* Add GC_GNU_THREADS support on HURD (thanks to Aleksey Demakov, +Barry DeFreese, and possibly other Debian maintainers). +* __GNUC__ was misspelled as __GNUC in thread_local_alloc.h (thanks to +Peter Wang). +* Integrated various MacOSX patches and tried to reconcile them (thanks to +Allan Hsu, several contributors at Apple, and probably others). +* Added some casts to powerpc.h in libatomic_ops to silence warnings. + +* Makefile.am: Include NT_STSTIC_THREADS_MAKEFILE in dist. +* include/private/gc_locks.h: GC_compare_and_exchange, GC_atomic_add: +remove. NUMERIC_THREAD_ID, THREAD_EQUAL: New. GC_lock_holder: now +unsigned long. I_DONT_HOLD_LOCK, I_HOLD_LOCK: Update. +* pthread_stop_world.c, pthread_support.c, win32_threads.c: Use +NUMERIC_THREAD_ID, THREAD_EQUAL. +* include/private/gcconfig.h: GENERIC_COMPARE_AND_SWAP: Remove. +* include/private/thread_local_alloc.h: Don't USE_COMPILER_TLS on +ARM. + +* dbg_mlc.c, include/gc.h, finalize.c: Merge Alexandre Oliva's +GC_debug_register_finalizer_unreachable() patch from gcc tree. +* thread_local_alloc.c (GC_malloc, GC_malloc_atomic): Add assertions +to check GC has been initialized. + +* include/gc_cpp.h: Documentation updates. +* include/gc_config_macros.h: Don't check for __ppc__ to set +DARWIN_THREADS. +* Makefile.am: Include configure_atomic_ops.sh in dist. + +* Makefile.am: Don't distribute copied atomic_ops files. Include +libatomic_ops with "make dist". +* configure.ac: Enable THREAD_LOCAL_ALLOC for Cygwin with threads. +* win32_threads.c: Report error for Cygwin + GC_DLL. + +* Makefile.direct: Update THREAD_LOCAL_ALLOC documentation. +* cord/de_win.c: Rename and move AboutBox. Call GC_INIT. Remove +MakeProcInstance anachronism. +* doc/README.macros: Officially remove elif prohibition. +Remove documentation for defunct SRC_M3 support. +* include/gc.h: Remove more SRC_M3 references. +* include/private/gcconfig.h: Remove still more SRC_M3 references. +GC_SOLARIS_THREADS no longer needs to be checked separately. + +* thread_local_alloc.c, include/private/thread_local_alloc.h: +Spell __declspec correctly. +* NT_STATIC_THREADS_MAKEFILE: Enable thread-local allocation. + +* doc/README.win32: Adjust GC_win32_dll_threads rules again. + +* mark.c (GC_mark_some wrapper): Restructure for readability, handle +GC_started_thread_while_stopped. +* misc.c (Win32 GC_write): Lock GC_write_cs only if needed. +* win32_threads.c: (client_has_run): remove, +GC_started_thread_while_stopped, GC_attached_thread: add. +(GC_push_all_stacks): Add verbose output. +(DllMain): Avoid initializing collector or the like. +Never update both thread tables. +* doc/README.win32: Adjust GC_win32_dll_threads rules. + +* pthread_stop_world.c (GC_push_all_stacks): Print thread count with +GC_PRINT_VERBOSE_STATS. + +* configure.ac: Comment out redundant +AC_DEFINE(NO_EXECUTE_PERMISSION). +* sparc_mach_dep.S: Remove single quote in comment. +* include/private/gcconfig.h: Fix DATAEND for NONSTOP. +* win32_threads.c: Include stdint.h for Mingw. Add GC_API for DllMain. +(GC_use_DllMain): Fix assertion. + +* configure.ac: Introduce extra_ldflags_libgc. Use it for Darwin. +* Makefile.am (libgc_la_LDFLAGS): Use extra_ldflags_libgc. +* include/private/gcconfig.h: Enable MPROTECT_VDB for all Darwin +targets. Remove comments. +Prepare ppc64 support for Darwin. + +* darwin_stop_world.c (GC_push_all_stacks): Fix compiler warnings. +Make i unsigned. +(GC_stop_world): Likewise. Remove unused GC_thread p. +(GC_start_world): Likewise. + +* os_dep.c: Define GC_darwin_register_mach_handler_thread extern. +Remove double SIG_HNDLR_PTR definition. +(GC_forward_exception): Fix compiler warnings, make i unsigned. +Initialize thread_state to NULL. +(catch_exception_raise): Fix compiler warnings, make i unsigned. + +* include/private/gc_priv.h (NEED_FIND_LIMIT, FREEBSD variant): +also define for X86_64. +* configure.ac: Move generic gnu (Hurd) case to below kfreebsd case. +* README.changes: Point to ChangeLog. + +* darwin_stop_world.c: Move THREAD_FLD defines to ... +* include/private/gc_priv.h: ... here. +Fix THREAD_STATE definitions for ppc64. +* os_dep.c (catch_exception_raise): Use THREAD_FLD for exc_state member +access. + +* configure.ac (i586-darwin): Replaced HAS_I386_THREAD_STATE_* with +HAS_X86_THREAD_STATE32_*. +(x86_64-*-darwin*): Extended the above check for x86_64-*-darwin* with +HAS_X86_THREAD_STATE64_*. +Added value 1 in the above AC_DEFINE's. Important for the upcoming +Leopard. +* include/private/gcconfig.h: Modified X86_64 define for Darwin. +Removed __x86_64__ check in POWERPC section. Added base definitions +for the X86_64 Darwin port. +* include/private/gc_priv.h: Added GC_MACH_HEADER and GC_MACH_SECTION +to distinguish between 32 and 64-bit applications. Added definitions +for X86_64 Darwin. +* darwin_stop_world.c: Added HAS_X86_THREAD_STATE64___RAX. And +replaced HAS_I386_THREAD_STATE___EAX with HAS_X86_THREAD_STATE32___EAX. +(GC_push_all_stacks): Added code for X86_64 Darwin. Even for the +!DARWIN_DONT_PARSE_STACK. Maybe obsolete. +* dyn_load.c (GC_dyld_name_for_hdr): Use GC_MACH_HEADER. +(GC_dyld_image_add): Use GC_MACH_HEADER and GC_MACH_SECTION. +Distinguish between getsectbynamefromheader_64 and +getsectbynamefromheader. +* os_dep.c (catch_exception_raise): Introduce exception definition for +X86_64 Darwin. Replaced old i386_EXCEPTION_STATE_* definition with +x86_EXCEPTION_STATE32_*. Add X86_64 for exc_state.faultvaddr. + + +== [7.0alpha7] 2006-09-19 == + +* More 6.7 changes. +* Declare GC_dump() in gc.h. +* Add --enable-large-config, which just defines the LARGE_CONFIG macro. +* Make GlobalAlloc address alignment a bit more intuitive (thanks to +Charles Mills). +* Use #elif in the definitions of GET_MEM. +* Overhaul porting.html. Remove corresponding text from README. +* Fix typo in DARWIN section of gcconfig.h. +* Fix Darwin thread memory leak (thanks to Bruce Mitchener). +* Update x86 AO_test_and_set implementation to use "=q". +* Add $(EXEEXT) to many tests in tests/tests.am. (Corresponds to a +6.7 fix, which no longer applied.) +* Fix Darwin/PPC port. +* Fix Cygwin/threads port. +* Fix gcj malloc support. +* For GNU-style make, don't build libatomic_ops unless threads are requested. +This should allow single-threaded builds on platforms which do not +currently support libatomic_ops. +* Clean up and hopefully fix the CFLAGS calculation for GNU build. +(Substantially improves things on HP/UX.) +* Integrated Andrei Polushin's Visual C++ patches. These provide for +stack traces, better C++ debug support, and better log file handling. +Note that these change the location of the log file to a the path of the +executable with a .log extension. To get the old behavior back, define +OLD_WIN32_LOG_FILE. For the time being, I'm checking his project +files and the like into a windows-untested subdirectory. They +are almost certainly already out of date, but better than what we had +before. +* Fixed some win32 threads bugs, and added support for _beginthreadex. +* Fix zero size thread local allocation so that explicit deallocation +works correctly. +* Removed serious bug in GC_malloc_uncollectable(large size). +* Do not try to do thread-local gcj allocation in incremental mode. There +are races in setting up the descriptor. +* Add GC_INIT() to middle.c, fix some more GC_printfn calls. +* Some assertions erroneously used I_HOLD_LOCK() negatively, even though +it can now spuriously return TRUE. +* Rename SUNOS5 macro and OS name to SOLARIS and SUNOS5DL to SOLARISDL. +* On Linux and some Un*x variants, allocate memory by first trying sbrk, +and then switching to mmap if that fails. +* Fixed /proc/x/maps reading to deal with asynchronous deletions. +* Fix REDIRECT_MALLOC with threads on Linux. It now usually seems to work +with ugly hacks that include having calloc behave differently when it is +called from ld.so or the pthreads library. A reasonable amount of +infrastructure was added to support some of this. (Thanks to Roland McGrath +for ideas and information.) +* Import various updated build scripts. +* Add GC_register_has_static_roots_callback (thanks to Andrew Haley). +* Fix serious bugs in GC_malloc_atomic_uncollectable(). +* Return GC_SUCCESS form GC_get_stack_base(). +* Fix several atomic_ops problems on IA64 with HP Compiler. +* Update to atomic_ops-1.2. +* Fix hb_n_marks description and reclaim.c assertion. +* Various additional win32 threads fixes. +* Enable GC_ASSERTIONS for Debug build with NT_THREADS_MAKEFILE. + + +== [7.0alpha5] 2005-09-29 == + +* More 6.6, 6.7 changes. +* Some Solaris fixes, including some more general changes in how +the assembly pieces of mach_dep.c are handled. +* Removed a lot of SOLARIS_THREADS-specific code that was only +needed with the old implementation. This included many (mostly no-op) +versions of GC_is_fresh. +* Don't use atomic_ops in gc_locks.h unless we need threads. +* Fixed USE_MARK_BITS, which is once again the default without PARALLEL_MARK. +* Removed Solaris GC_INIT hack. It's a workaround for a long dead bug, +and it seemed to be wrong anyway. +* Changed win32_threads.c to require preprocessor-based interception +of thread routines by default. A client call to GC_use_DllMain is +now required to get the old behavior in which DllMain is used to implicitly +register threads. This was done for uniformity with other platforms, and +because the DllMain solution seemed to require very tricky code which, +at least in the past, imposed hard bounds on the number of threads. +* Many small changes to make thread support work again on Cygwin. +* Moved definition of allocator lock etc. to pthread_support.c and +win32_threads.c for those two cases. +* Got rid of the FASTLOCK() machinery. It doesn't seem useful on modern +platforms. +* Cleaned up the uncollectible allocation routines, speeding up the +slower paths. The code did enough unnecessary work off the critical path +that the underlying logic was getting hard to extract. +* No longer turn off THREAD_LOCAL_ALLOC with DBG_HDRS_ALL. Indications +are it just works, and I think the reasons for it not working disappeared +a while ago. +* Fixed bugs in hb_n_marks calculation and assertion. +* Don't use __builtin_expect for pre-3.0 gcc. +* Define GWW_VDB only for recent Microsoft tool chains. +* Add overview.html to doc directory. +* Fix NT_STATIC_THREADS_MAKEFILE, various compiler warnings. +* Made thread local allocation sort of work with Cygwin. The code should +be there to deal with other Windows variants, But non-Cygwin Windows +threads need more bug fixes. + + +== [7.0alpha4] 2005-08-02 == + +* Various 6.5, 6.6 changes. +* Removed GC_brief_async_signal_safe_sleep and used atomic_ops instead +(thanks to Ben Maurer). +* Integrated build patches from Davide Angelocola and Petter Urkedal. +* Fix dynamic-linker-based pthread call redirection. +* Renamed RS6000 to POWERPC/AIX. +* Allow recovery from SIGSEGV in marker on Linux. This works around +a race in thread stack marking if /proc is used to find roots. We do +that by default with malloc redirection and threads. This involved +moving some GC_find_limit and SETJMP related declarations to gc_priv.h. +* Added doc/porting.html file. +* Added ADD_HEAP_GUARD_PAGES for sbrk/*nix platforms to debug extreme +memory overwrite errors. +* Added trivial NO_INCREMENTAL flag to facilitate debugging. +* Added GC_getattr_np-based GC_get_stack_base (untested). +* Separated thread local allocation into a separate file and added the +beginning of win32 support for that. + + +== [7.0alpha3] 2005-04-28 == + +* Added support for dlopen-based interception of pthread functions. +This is only half done. The gc.h redefinitions currently interfere. +* Integrated major automake overhaul from Petter Urkedal. + + +== [7.0alpha2] 2005-04-07 == + +* GC_bytes_allocd was incremented by a possibly uninitialized variable +in GC_generic_malloc_inner. (Bug introduced in gc7.0alpha1. Thanks +to Ben Hutchings for tracking it down.) +* Win32 fixes (thanks to Ben Hutchings and Maurizio Vairani). +* Integrated Ben Hutchings' GetWriteWatch-based virtual dirty bit +implementation for win32. +* Removed pc_gc.tar and floppy targets in Makefile.direct. Removed +pc_excludes file. +* No longer include GC_bytes_wasted when evaluating allocation progress. +Since we are now counting live memory, it no longer makes sense. +* Applied Davide Angelocola's configure patch. There are now separate +Makefile.am's in the cord and tests subdirectory, more tests, etc. +* Renamed configure.in to configure.ac. +* Merged a very small number of Nathanael Nerode's configure.ac +cleanups from the gcc tree. Unfortunately, that file is a bit +different from ours. +* Changed EINTR handling in sem_wait slightly. +* Restructure the root marking code. Remove all traces of +USE_GENERIC_PUSH_REGS, and effectively make it the default. +Make it easier to pass a context pointer to the mark routine, in +case we ever want to do precise stack marking. +* Replace GC_start_blocking() and GC_end_blocking() with GC_do_blocking(). +This remains undocumented, and only implemented for pthreads. But it +removes an otherwise unavoidable race with stores of callee-save +registers. +* Fix GC_n_mark_bits for the default MARK_BIT_PER_GRANULE case. This +resulted in bogus complaints in heap dumps. +* Upgrade to libatomic_ops-1.0, and update build structure to match. +* Remove SRC_M3 support. Clean up lock initialization code in misc.c. +* Removed gc_local_alloc.h. If THREAD_LOCAL_ALLOC is defined, the +thread local allocation routines are now called automatically. +* Renamed gc_inl.h back to gc_inline.h. Changed the interface appreciably +since locking has turned into a dominant issue, and in-line allocation +only makes sense if it's no worse than thread-local allocation. +Gc_inline.h is now also used to implement thread-local allocation. +* Finished replacing stubborn allocation with manual write barrier. +Untested. +* Use thread-local allocation code by default. +* Added GC_register_my_thread and friends for Posix and win32. +* Patch for GWW_VDB from Ben Hutchings. +* Removed explicit THREAD_LOCAL_ALLOC tests, since that now always +redefines GC_malloc. +* Removed now unused AIX memory allocation code. +* Various minor fixes for bugs introduced in 7.0alpha1. + + +== [7.0alpha1] 2004-11-09 == + +* Remove GC_PROTO, VOLATILE, GC_PTR, and GC_CONST. Assume ANSI C compiler +and use ANSI constructs unconditionally. +* Introduce #elif and #error in some of the appropriate places. +* Remove GC_printf cruft. Use stdargs. +* Remove separate Solaris threads support. Use the more generic Posix +implementation. +* Use atomic_ops for atomic operations and memory barriers. +* Clean up MPROTECT_VDB implementation. Use SA_SIGINFO wherever +possible. +* Remove broken SIGNALS stuff. +* Use size_t instead of word, where appropriate. +* Add .S.o rule to Makefile.am. +* Officially discontinue SunOS4, several old flavors of M68K (SunOS4, +A/UX, HP), IBM PC/RTs and RISCOS/Irix4. (I doubt the old code worked. +If anyone cares, these should be easy to resurrect.) +* Add EXPECT() in some critical places. +* Redefined hb_sz and hb_body to deal with bytes rather than words. +This affected a great deal of code. I would like to consistently use +byte offsets and sizes where there's not a convincing reason to do +otherwise. +* Redefined several other variables (GC_mem_found, GC_words_allocd) +etc. to use units of bytes. Most of these were also renamed to +reflect that fact. +* Killed as many "register" declarations as possible. +* Partially replaced stubborn allocation with manual write barrier. +It's currently broken. +* Restructured mark code, to allow mark bits to be kept either on +a per allocation granule or per object basis. The emphasis is +now on the -DUSE_MARK_BYTES option, since individual bits perform +quite badly on hyper-threaded P4s, and are probably suboptimal on +other architectures. -DUSE_MARK_BITS is currently broken, and may +be resurrected only for the single-threaded case. This significantly +reduced the cache footprint required by auxiliary GC data structures. +It also reduces space overhead for small heaps. It probably slows +things down slightly if interior pointers are very common. +* As part of the above, we now maintain an approximate count of set +mark bits in each heap block. +* As part of the above, the semantics of hb_map changed drastically. +For MARK_BIT_PER_OBJ, it doesn't exist. For MARK_BIT_PER_GRANULE, +it is purely a way to replace a mod instruction with a table lookup. +(Somewhat to my surprise, this still wins on modern hardware.) +* Removed PRINTSTATS, GATHERSTATS, and SILENT macros. Everything is +now controlled by GC_print_stats variable and GC_PRINT_STATS +and new GC_PRINT_VERBOSE_STATS environment variables. +* Add GC_log_printf and use it consistently for logging output. +* Unconditionally count the objects we reclaim in the sweep phase. +For thread local allocation, we need that anyway, and we expect +that's increasingly the only case that matters. And it simplifies +the code. In general expect minor performance hacks that benefit +only the single-threaded case to disappear. +* Remove GC_quiet from gc.h and elsewhere. +* Changed the heap expansion heuristic, and the definition of +GC_free_space_divisor, to refer to live data size, instead of total +heap size. I believe this is much more robust. It wasn't previously +possible, because we didn't have access to live data size. +* Thread local allocation added the extra byte in twice: Once in +thread_local_alloc, and once in malloc_many. +* Removed GC_malloc_words_small and GC_gcj_fast_malloc. A new +mechanism based on the thread local allocation data structures +is expected to be added instead. This should allow inlined code +that is both fast and doesn't rely on collector internals. +* Changed both free lists and reclaim lists to be indexed by granules +instead of words, norming halving their size. +* MERGE_SIZE is now the only option, and the macro was removed. +(Without it, we need a memory reference to GC_all_interior_pointers +anyway. Thus it costs us nothing.) +* Change GC_size_map to map to granules instead of words. Make sure +that every possible size up to TINY_FREELISTS is present. +* Split of macros need for fast inline allocation into gc_tiny_fl.h +in anticipation of a new inline allocator that doesn't rely on GC +internals. +* Changed thread local allocation to use GRANULE_BYTES and TINY_FREELISTS +in anticipation of a merge with the inline allocation code. +* Removed ALIGN_DOUBLE. This is mostly handled by GRANULE_BYTES. +* Make locking on most platforms conditional on GC_need_to_lock. + + +== [6.9] == + +* Fix typo in PREFETCH implementation for X86_64 (thanks to Peter Wang). +* Fix M68K LINUX port (thanks to Debian packagers). +* __GNUC__ was misspelled as __GNUC in new_gc_alloc.h (thanks to Peter Wang). +* Integrated Allan Hsu's patch for OS X VM deallocation problems. +* Applied FreeBSD/X86_64 patch. + + +== [6.8] 2006-07-08 == + +* Added some support for Dragonfly BSD (thanks to Joerg Sonnenberger and +Thomas Klausner). +* Improvements to the HP/UX section of configure.in/configure.ac (thanks +to Andreas Tobler). +* GC_unix_get_mem could neglect to release the malloc lock on Irix, under +extremely unlikely circumstances. (Thanks to Jean-Baptiste Nivois for +some careful code inspection.) +* Added support for kFreeBSD + glibc (thanks to Petr Salinger). +* Fix more MacOS threads memory leaks (thanks to Allan Hsu). +* Added initial Solaris/X86-64 support (thanks to Rainer Orth). + + +== [6.7] 2006-03-03 == + +* Add "int" to Solaris "end" and "etext" declaration in gc.h. Declared +the symbols with underscores and as arrays, since that's what's actually +used. Perhaps this could all just be removed. (Thanks to John Bowman.) +* Fixed ARM GC_test_and_set code (thanks to Kazu Hirata and Paul Brook). +* Added casts for assignments to hb_last_reclaimed, which truncate the +value. Added a cast to GC_adj_words_allocd. Use GetModuleHandleA +when retrieving a handle to kernel32.dll under win32. +* Added Tandem S-Series support. (Thanks to Craig McDaniel. A modified +version of his patch was applied, and hence breakage is probably not +his fault.) +* Remove spurious gc:: qualifier for operator delete[] in gc_cpp.h (thanks +to Hanno Boeck). +* Changed a test for LINUX in config_macros.h to one for __linux__. +* Add prototypes for GC_finalizer_notifier and GC_thr_init (thanks to +David Ayers). +* Use ld instead of nonexistent ldz instruction in Darwin FindTopOfStack +(thanks to Andreas Tobler). +* Add support for Darwin/X86 (thanks to Geoff Norton and the Mono +developers). +* Merge in some recent gcc fixes. Add ppc64 asm code. (Thanks to +Bryce McKinlay and other GCJ developers.) +* Scan MEM_PRIVATE sections under Windows ME and predecessors. +* Interior pointers with some largish offsets into large objects could +be ignored, if GC_all_interior_pointers was set. (Oddly this worked +correctly for stack references if it was not set. Otherwise it failed +for both stack and heap references. Thanks to Andrew McKinlay for the +critical test case.) +* Integrated Tatsuya Bizenn's NETBSD threads support, with some +untested changes. +* Added GC_strdup and friends to make leak detection work correctly +for strdup clients (thanks to Jon Moore). Fixed the existing strdup +with malloc redirection to handle a null malloc return correctly. + + +== [6.6] 2005-09-09 == + +* Fix CPU count detection for Irix and FreeBSD (thanks to Dan Bonachea). +* Integrate Dan Bonachea's patch for the IBM XLC compiler on Darwin. +* Integrated Andreas Tobler's FreeBSD/PowerPC patch. +* Don't access the GC thread structure from the restart handler. It's +unsafe, since the handler may run too late. (Thanks to Ben Maurer for +tracking this down.) +* Applied Christian Thalinger's patch to change comment syntax in +alpha_mach_dep.S. +* Added test for GC_no_dls in GC_dyld_image_add for DARWIN (thanks to +Juan Jose Garcia-Ripoll). +* Use LINUX_STACKBOTTOM for Linux/SH and LINUX/ARM (thanks to +Sugioka Toshinobu and Christian Thalinger). +* Rewrote GC_parse_map_entry. This assumed a fixed column layout of +/proc/self/maps on Linux. This ceased to be true about 2 years ago. +The old code is probably quite problematic with -DREDIRECT_MALLOC. It +is also used by default for IA64, though I haven't seen actual failures +there. +* More consistently define HBLKSIZE to 4096 on 64 bit architectures with +4K pages (thanks to Andrew Haley). +* With win32 threads, GC_stop_world needs to acquire GC_write_cs (thanks +to Ben Hutchings for the observation and patch). +* Move up struct callinfo declaration to make gcc 4.0.2 happy. + + +== [6.5] 2005-05-22 == + +* Integrated Paolo Molaro's patch to deal with EINTR in sem_wait. +* Make GC_approx_sp() write to dummy location to ensure that stack +is grown here, when sp looks reasonable, rather than later, when +it might look like a bad memory reference. (Problem was never +observed that I know of. But on rereading the code it seemed +dubious.) +* Separate out GC_with_callee_saves_pushed and sometimes call +it from GC_suspend_handler in pthread_stop_world.c. Callee-save +register values sometimes failed to get traced under HP/UX on +PA-RISC. Linux/IA64 had the same problem, though non-stacked +callee-save registers seem to be so rarely used there that nobody +ever noticed. +* Integrated an ancient Darwin powerpc_darwin_machine_dep.s patch +from Andreas Tobler, which I had lost. +* Fix compare_and_exchange implementation for gcc/IA64 to deal with +pickier compiler versions. +* Fixed Itanium 32-bit ABI support (HP/UX). In particular, the +compare_and_exchange implementation didn't consider that possibility. +* Undefine GC_pthread_detach in win32_threads.c (thanks to +Tommaso Tagliapietra). +* Fixed inclusion of frame.h for NETBSD in os_dep.c. +* Applied Dan Bonachea's patch to use mmap on AIX. +* Several fixes to resurrect the Irix port on recent OS versions. +* Change ALPHA to use LINUX_STACKBOTTOM. +* Change SPARC64/LINUX to also use LINUX_STACKBOTTOM. Deal with potential +bad values of __libc_stack_end on that platform (thanks to David Miller). +* Relax gctest to allow larger heap if ALIGN_DOUBLE isn't set. +(Unnecessary in 7.0) +* Force a define of __STDC__=0 for the IBM compiler on AIX, so that +we get prototypes. (Unnecessary in 7.0) +* GC_INIT definition for AIX and CYGWIN referred to DATASTART and DATAEND +which are only defined in private include files. +* Integrated some small gcconfig.h patches from Dan Bonachea. Also +relaxed assertion about FreeBSD stack size in pthread_support.c. +* Integrated Andrew Begel's darwin_stop_world.c patch for 64-bit +support. This may need additional work. +* Avoided potentially infinite recursion in GC_save_callers if +the system backtrace calls malloc. The workaround currently requires +__thread support if this code is used with threads. +* Avoided another similar infinite recursion by conditionally +invoking GC_save_callers in alloc.c (thanks to Matthias Andree +for helping to track down both of these). +* Removed all traces of aix_irix_threads.c. AIX and Irix now use +pthread_support.c and pthread_stop_world.c. The old code appeared +to be unreliable for AIX, and was not regularly maintained. +* On Irix, ignore segments with MA_FETCHOP or MA_NOTCACHED attributed; +they're not always safe to read. +* Fixed a previously vacuous assertion (diagnosed by the SGI compiler) +in GC_remove_from_fl. +* Fix stack_size assertion in GC_pthread_create. +* Fix assertion in GC_steal_mark_stack. + + +== [6.4] 2004-12-21 == + +* Merge gcconfig.h changes from gcc tree. +* Unconditionally include gc_priv.h in solaris_pthreads.c, win32_threads.h, +aix_irix_threads.c, and solaris_threads.c to get thread definitions. +* Start marker threads in GC_thr_init, so that they get started even +if no other threads are ever started. (Oddly enough, the parallel +collector worked correctly, though not well, with no helper threads.) +* Go ahead and split large blocks in GC_allochblk_nth if GC_dont_gc +is set (thanks to Alexander Petrossian). +* GC_PRINT_BACK_HEIGHT would deadlock with thread support. +* Let in_progress_space in backgraph.s grow dynamically. +* Fix README.solaris2. The GC_thr_init() hack doesn't work anymore. +* Convert GC_finalizer_mem_freed to bytes in allchblk.c. +* Add missing declaration for GC_generic_malloc_words_small_inner. +Without it, s390x breaks. (Thanks to Ulrich Weigand.) +* Applied several MacOSX patches to support older tool chains (thanks +to Stefan Ring). +* Bug fix for NetBSD/amd64 (thanks to Marc Recht). +* Add NetBSD/sh3 support (thanks to Uchiyama Yasushi). +* Fixed an uninitialized variable in cordprnt.c. +* Eliminated some, but not all, gcc -Wall warnings. +* Changed some old style casts to reinterpret_cast in new_gc_alloc.h +(thanks to Dan Grayson). +* GC_extend_size_map shouldn't adjust for GC_all_interior_pointers if +GC_DONT_ADD_BYTE_AT_END is set. +* Changed some (long) casts to (word) in preparation for win64 (thanks +to Peter Colson). +* Changed "int stack_size" declaration in pthread_support.c to use +size_t. (Only mattered with GC_ASSERTIONS enabled.) +* Added CRIS (etrax) support (thanks to Simon Posnjak and Hans-Peter Nilsson). +* Removed GC_IGNORE_FB frame buffer recognition, and replaced +it with a check that the mapping type is MEM_IMAGE. +In theory, this should work much better, but it is a high +risk change for win32. (Thanks to Ashley Bone for the crucial +experimental data behind this, and to Rutger Ovidius for +some further experiments.) +* GC_allochblk_nth incremented GC_words_wasted by bytes rather than +words. +* Consider GC_words_wasted in GC_adj_words_allocd only if it is within +reason. (A hack to avoid some extremely unlikely scenarios in which +we manage to allocate only "wasted" space. 7.0 has a better fix.) +* Changed PowerPC GC_clear implementation to use lwsync instead of +eieio, since the documentation recommends against eieio, and +it seems to be incorrect if the preceding memory op is a load. +* Fixed print_block_list to print the correct kind number for +STUBBORN (thanks to Rutger Ovidius). +* Have configure.in generate an error if it is asked to support +pthreads, but doesn't know how to. +* Added Kazuhiro Inaoka's patch for Renesas M32R support. +* Have the GNU build mechanism link with -ldl. Rename THREADLIBS +to THREADDLLIBS to reflect this. (Thanks to Sven Verdoolaege.) +* Added Hannes Mehnert's patch for FreeBSD/SPARC support. +* Merged some FreeBSD specific patches to threadlibs.c and dyn_load.c. +(Thanks to John Merryweather Cooper.) +* Define MPROTECT_VDB on MACOSX only if threads are being used, since the +dirty page tracking mechanism uses threads. (This avoids an undefined +reference to _GC_darwin_register_mach_handler_thread.) +* By popular demand, use __libc symbols only if we are built with +USE_LIBC_PRIVATES, which is off by default, and not otherwise documented. +* Ignore GC_enable_incremental() requests when KEEP_BACK_PTRS is set. +The GC itself will dirty lots of pages in this cases, probably making +it counterproductive on all platforms. And the DARWIN port crashes. + + +== [6.3] 2004-07-08 == + +* Compile test_cpp.cc with CXXCOMPILE instead of COMPILE. +* Very large allocations could cause a collector hang. Correct +calculation of GC_collect_at_heapsize. +* GC_print_hblkfreelist printed some bogus results if USE_MUNMAP +was defined. +* Include gc_config_macros.h in threadlibs.c. +* Correct MacOSX thread stop code (thanks to Dick Porter). +* SMALL_OBJ definition was off by one. This could cause crashes +at startup. (Thanks to Zoltan Varga for narrowing this down to +a trivial test case.) +* Integrate Paolo Molaro's patch to deal with a race in the Darwin +thread stopping code. +* Changed X86_64 implementation to use SA_SIGINFO in the MPROTECT_VDB +implementation. The old approach appears to have been broken by +recent kernels. +* Added GC_ATTR_UNUSED to eliminate a warning in gc_allocator.h (thanks +to Andrew Begel). +* Fix GC_task_self declaration in os_dep.c (thanks to Andrew Pinski). +* Increase INITIAL_BUF_SZ in os_dep.c for Solaris /proc reads. + + +== [6.3alpha6] 2004-05-06 == + +* Define USE_GENERIC_PUSH_REGS for NetBSD/M68K. +* Fixed the X86_64 PREFETCH macros to correctly handle ia32e (which uses +different prefetch instructions from AMD64). (Thanks to H.J. Lu.) +* GC_config_macros.h did not correctly define GC_WIN32_THREADS from +GC_THREADS. +* Added simple_example.html. +* Merged Andrew Gray's patch to correctly restore signal handlers on +FreeBSD. +* Merged a patch from Andreas Jaeger to deal with prefetch-related warnings +on x86-64. Added some other casts so that the PREFETCH macros +always get a ptr_t argument. Removed some casts in the PREFETCH +implementations. +* Added a header guard for gc_allocator.h and changed GC_debug_free to +clobber contents of deallocated object (suggested by Jesse Jones). +* The signal masking code in pthread_stop_world.c contained some errors. +In particular SIGSEGV was masked in the handler, in spite of the fact that +it wrote to the heap. This could lead to an uncaught SIGSEGV, which +apparently became much more likely in Linux 2.6. Also fixed some +typos, and reduced code duplication in the same area. +* Remove ltconfig, clean up configure messages for DG/UX (thanks to +Adrian Bunk for the patches). +* Integrated NetBSD/OpenBSD patches from Marc Recht and Matthias Drochner. + + +== [6.3alpha5] 2004-03-30 == + +* Fix & vs && typo in GC_generic_malloc and +GC_generic_malloc_ignore_off_page. (Propagated from the gcc tree.) +* Removed SA_NODEFER hack from NetBSD and Solaris write-protect handler. +(According to Christian Limpach, the NetBSD problem is fixed. +Presumably so is the Solaris 2.3 problem.) +* Removed placement delete from gc_cpp.h for the SGI compiler (thanks +to Simon Gornall for the patch). +* Changed semantics of the GC_IGNORE_FB environment variable, based +on experimentation by Nicolas Cannasse pointing out that the old +interpretation was useless. We still need help in identifying win32 +graphics memory mappings. The current "solution" is a hack. +* Removed "MAKEOVERRIDES =" from Makefile.am and thus Makefile.in. +It probably made more sense in the gcc context. +* Explicitly ensure that NEED_FIND_LIMIT is defined for {Open,Net}BSD/ELF. +* Replaced USE_HPUX_TLS macro by USE_COMPILER_TLS, since gcc often +supports the same extension on various platforms. +* Added some basic (completely untested) defines for win64, in support +of future work. +* Declared GC_jmp_buf in os_dep.s as JMP_BUF instead of jmp_buf, fixing +a memory overwrite bug on Solaris and perhaps other platforms. +* Added 0 != __libc_stack_end test to GC_linux_stack_base (thanks to +Jakub Jelinek for the patch and explaining the problem). +Otherwise pre-linking could cause the collector to fail. +* Changed default thread local storage implementation to USE_PTHREAD_SPECIFIC +for HP/UX with gcc. The compiler-based implementation appears to work +only with the vendor compiler. +* Export GC_debug_header_size and GC_USR_PTR_FROM_BASE from gc_mark.h, +making client mark code cleaner and less dependent on GC version. +* Export several new procedures and GC_generic_malloc from gc_mark.h +to support user-defined kinds. Use the new procedures to replace existing +code in gcj_mlc.c and typd_mlc.c. +* Added support for GC_BACKTRACES. +* Fixed a remaining problem in CORD_str with signed characters (thanks +to Alexandr Petrosian for the patch). +* Removed supposedly redundant, but very buggy, definitions of finalizer +macros from javaxfc.h. Fortunately this file probably has no users. +The correct declarations were already in gc.h. +* Also need to set GC_in_thread_creation while waiting for GC during +thread termination, since it is also possible to collect from an +unregistered thread in that case. +* Define NO_GETENV for Windows CE, since getenv doesn't appear to exist. +Plus some other minor WinCE fixes (thanks to Alain Novak). +* Added GC_register_describe_type_fn. +* Arrange for debugging finalizer registration to ignore non-heap +registrations, since the regular version of the routine also behaves +that way. +* GC_gcj_malloc and friends need to check for finalizers waiting to be run. +One of the more obscure allocation routines with missing a LOCK() call. +* Fixed cvtres invocations in NT_MAKEFILE and NT_STATIC_THREADS_MAKEFILE +to work with VS.NET. +* Cleaned up GC_INIT calls in test. Updated gc.man to encourage GC_INIT +use in portable code. +* Taught the GC to use libunwind if --enable-full-debug is specified on +IA64 and libunwind is present. +* The USE_MUNMAP code could get confused about the age of a block and +prematurely unmap it. GC_unmap_old had a bug related to wrapping of +GC_gc_no. GC_freehblk and GC_merge_unmapped didn't maintain +hb_last_reclaimed reasonably when blocks were merged. The code was +fixed to reflect original intent, but that may not always be an +improvement. + + +== [6.3alpha4] 2004-01-01 == + +* USE_MMAP was broken by confusion in the code dealing with USE_MMAP_ANON. +* Darwin support was broken in alpha3 as a result of my mis-integration of +Andrew Begel's patches. Fixed with another patch from Andrew Begel. +* A new sanity check in pthread_stop_world.c:GC_push_all_stacks() was +overly aggressive. We may collect from an unregistered thread during +thread creation. Fixed by explicitly checking for that case. (Added +GC_in_thread_creation.) + + +== [6.3alpha3] 2003-12-20 == + +* Removed -DSMALL_CONFIG from BCC_MAKEFILE. +* Changed macros to test for an ARM processor (Patch from Richard Earnshaw.) +* Mostly applied a DJGPP patch from Doug Kaufman. Especially Makefile.dj +had suffered from serious bit rot. +* Rewrote GC_apply_to_maps, eliminating an off-by-one subscript error, +and a call to alloca (for lcc compatibility). +* Changed USE_MUNMAP behavior on POSIX platforms to immediately remap +the memory with PROT_NONE instead of unmapping it. The latter risks +an intervening mmap grabbing the address space out from underneath us. +Updated this code to reflect a cleaner patch from Ulrich Drepper. +* Replaced _T with _Tp in new_gc_alloc.h to avoid a MACOS X conflict. +(Patch from Andrew Begel.) +* Dynamically choose whether or not lock should spin on win32 (thanks +to Maurizio Vairani for the patch). This may be a significant performance +improvement for win32. +* Fix Makefile.direct to actually include NT_STATIC_THREADS_MAKEFILE +in the distribution (thanks to Maurizio Vairani). +* Maybe_install_looping_handler() was accidentally exported, violating +our name space convention. +* Made os_dep.c use sigsetjmp and SA_NODEFER for NetBSD. (Thanks to +Christian Limpach. I generalized the patch to use sigsetjmp on all +UNIX_LIKE platforms, admittedly a slightly risky move. But it may avoid +similar problems on some other platforms. I also cleaned up the definition +of UNIX_LIKE a bit.) +* Integrated Andrew Begel's Darwin threads patch, adjusted according to +some of Fergus Hendersons's comments. (Patch didn't apply cleanly, +errors are possible.) +* Added another test or two for the Intel 8.0 compiler to avoid +confusing it with gcc. The single-threaded collector should now build +with icc, at least on ia64. + + +== [6.3alpha2] 2003-11-04 == + +* Re-enabled I_HOLD_LOCK assertion in aix_irix_threads.h. +* Put back the WINABI qualifier for GC_CreateThread. (Thanks to +Danny Smith for the patch. 6.3alpha1 had the qualifier in one place +but not elsewhere, which was clearly wrong.) +* Sometimes explicitly define __private_extern__ before DARWIN dyld.h +include. (Thanks to Andreas Tobler for posting the patch.) +* Included signal.h from pthread_support.c. Removed GC_looping_handler, +which was dead code. +* GC_find_start was misdeclared by gc_pmark.h if PRINT_BLACK_LIST was +defined (thanks to Glauco Masotti for testing and reporting this). +Changed GC_find_start to never just return 0. According to its +comment it doesn't, and it's unclear that's correct. +* GC_alloc_large had several largely compensating bugs in the +computation of GC_words_wasted. (It was confused about bytes vs. +words in two places.) +* Integrated Slava Sysoltsev's patch to support more recent versions of +the Intel compiler on IA64/Linux. +* Changed win32 spinlock initialization to conditionally set a spin count. +(Emmanual Stumpf pointed out that enabling this makes a large performance +difference on win32 multiprocessors.) Also cleaned up the win32 spinlock +initialization code a bit. +* Fixed thread support for HP/UX/IA64. The register backing store base for +the main thread was sometimes not set correctly. (Thanks to +Laurent Morichetti.) +* Added -DEMPTY_GETENV_RESULTS flag to work around Wine problem. +* Declare GC_stack_alloc and GC_stack_free in solaris_threads.h to +avoid 64-bit size mismatches (thanks to Bernie Solomon). +* Fixed GC_generic_push_regs to avoid a potential and very unfortunate +tail call optimization. This could lead to prematurely reclaimed +objects on configurations that used the generic routine and the new +build infrastructure (which potentially optimizes mach_dep.c). +This was a serious bug, but it's unclear whether it has resulted in +any real failures. +* Fixed CORD_str to deal with signed characters (thanks to Alexandr Petrosian +for noticing the problem and supplying the patch). +* Merged a couple of NOSYS/ECOS tests into os_dep.c from gcj (thanks +to Anthony Green). +* Partially merged a win32 patch from Ben Hutchings, and substantially +revised other parts of win32_threads.c. It had several problems. +Under MinGW with a statically linked library, the main thread was +not registered. Cygwin detached threads leaked thread descriptors. +There were several race conditions. For now, unfortunately the +static threads limit remains, though we increased it, and made table +traversal cost depend on the actual thread count. +There is also still some code duplication with pthread_support.c. +(Thread descriptors did become much smaller, since Ben Hutchings +removed the thread context from them.) +* Integrated a Solaris configure.in patch from Rainer Orth. +* Added GC_IGNORE_FB and associated warning to very partially address +the issue of the collector treating a mapped frame buffer as part +of the root set. (Thanks to David Peroutka for providing some +insight. More would be helpful. Is there anything that can be used +to at least partially identify such memory segments?) + + +== [6.3alpha1] 2003-07-26 == + +* Integrated some NetBSD patches by Marc Recht. These +were already in the NetBSD package. +* GC_pthread_create waited for the semaphore even if pthread_create failed. +(Thanks to Dick Porter for the pthread_support.c patch.) Applied the +analogous fix for aix_irix_threads.c. +* Added Rainer Orth's Tru64 fixes. +* The check for exceeding the thread table size in win32 threadDetach +was incorrect (thanks to Alexandr Petrosian for the patch). +* Applied Andrew Begel's patch to correct some reentrancy issues +with dynamic loading on Darwin. +* GC_CreateThread() was neglecting to duplicate the thread handle in +the table (thanks to Tum Nguyen for the patch). +* Pass +ESdbgasm only on PA-RISC machines with vendor compiler (thanks to +Roger Sayle for the patch). +* Applied more AIX threads patches from Scott Ananian. + + +== [6.2] 2003-06-21 == + +* Integrated a second round of Irix/AIX patches from Dan Bonachea. +Renamed mips_sgi_mach_dep.S back to mips_sgi_mach_dep.s, since it requires +the Irix assembler to do the C preprocessing; gcc -E doesn't work. +* Fixed Makefile.direct for DARWIN (thanks to Manuel Serrano). +* There was a race between GC_pthread_detach and thread exit that could +result in a thread structure being deallocated by GC_pthread_detach +even though it was still needed by the thread exit code (thanks to +Dick Porter for the small test case that allowed this to be debugged). +* Fixed version parsing for non-alpha versions in acinclude.m4 and +version checking in version.h. +* Issues identified (not yet fixed): +- A dynamic libgc.so references dlopen unconditionally, but doesn't link +against libdl. +- GC_proc_fd for Solaris is not correctly updated in response to a +fork() call. Thus incremental collection in the child won't work +correctly. (Thanks to Ben Cottrell for pointing this out.) +- --enable-redirect-malloc is mostly untested and known not to work +on some platforms. +- There seem to be outstanding issues on Solaris/X86, possibly with +finding the data segment starting address. +- Very large root set sizes (> 16 MB or so) could cause the collector +to abort with an unexpected mark stack overflow. (Thanks to +Peter Chubb.) NOT YET FIXED. Workaround is to increase the initial +size. +- The SGI version of the collector marks from mmapped pages, even +if they are not part of dynamic library static data areas. This +causes performance problems with some SGI libraries that use mmap +as a bitmap allocator. NOT YET FIXED. It may be possible to turn +off DYNAMIC_LOADING in the collector as a workaround. It may also +be possible to conditionally intercept mmap and use GC_exclude_static_roots. +The real fix is to walk rld data structures, which looks possible. +- Incremental collector should handle large objects better. Currently, +it looks like the whole object is treated as dirty if any part of it is. + + +== [6.2alpha6] 2003-06-05 == + +* There was an extra underscore in the name of GC_save_registers_in_stack +for NetBSD/SPARC (thanks to Jaap Boender for the patch). +* Integrated Brian Alliet's patch for Darwin. This restructured the +linuxthreads/pthreads support to separate generic pthreads support +from more the system-dependent thread-stopping code. I believe this +should make it easier to eliminate the code duplication between +pthreads platforms in the future. The patch included some other +code cleanups. +* Integrated Dan Bonachea's patch to support AIX threads. This required +substantial manual integration, mostly due to conflicts with other +recent threads changes. It may take another iteration to +get it to work. +* Removed HPUX/PA-RISC support from aix_irix_threads.c. It wasn't used +anyway and it cluttered up the code. And anything we can do to migrate +towards generic pthreads support is a good thing. +* Added a more explicit test for tracing of function arguments to test.c. +* Added Akira Tagoh's PowerPC64 patch. +* Fixed some bit rot in the Cygwin port (thanks to Dan Bonachea for +pointing it out). gc.h now includes just windows.h, not winbase.h. +* Declared GC_save_regs_in_stack() in gc_priv.h. Remove other declarations. +* Changed --enable-cplusplus to use automake consistently. The old way +confused libtool. "Make install" didn't work correctly for the old version. +Previously --enable-cplusplus was broken on cygwin. +* Changed the C version of GC_push_regs to fail at compile time if it is +generated with an empty body. This seems to have been the cause of one +or two subtle failures on unusual platforms. Those failures should +now occur at build time and be easily fixable. + + +== [6.2alpha5] 2003-05-14 == + +* GC_invoke_finalizers could, under rare conditions, set +GC_finalizer_mem_freed to an essentially random value. This could +possibly cause unbounded heap growth for long-running applications +under some conditions. (The bug was introduced in 6.1alpha5, and +is not in gcc3.3.) +* Attempted to sanitize the various DLL macros. GC_USE_DLL disappeared. +GC_DLL is used instead. All internal tests are now on GC_DLL. +README.macros is now more precise about the intended meaning. +* Include DllMain in the multi-threaded win32 version only if the +collector is actually built as a dll (thanks to Mohan Embar for +a version of the patch). +* Hide the cygwin threadAttach/Detach functions. They were violating our +namespace rules. +* Fixed an assertion in GC_check_heap_proc. Added GC_STATIC_ASSERT +(thanks again to Ben Hutchings). +* Removed some obsolete definitions for Linux/PowerPC in gcconfig.h. +* CORD_cat was not rebalancing unbalanced trees in some cases, violating +a CORD invariant. Also tweaked the re-balancing rule for +CORD_cat_char_star. (Thanks to Alexandr Petrosian for the bug report +and patch.) +* Added hand-coded structured exception handling support to mark.c. +This should enable support of dynamic libraries under win32 with +gcc-compiled code. (Thanks to Ranjit Mathew for the patch.) +Turned on dynamic library scanning for win32/gcc. +* Removed some remnants of read wrapping (thanks to Kenneth Schalk). +GC_USE_LD_WRAP was probably broken in recent versions. +* The build could fail on some platforms since gcconfig.h could include +declarations mentioning ptr_t, which was not defined, e.g. when if_mach +was built (thanks to Yann Dirson for pointing this out). Also +cleaned up tests for GC_PRIVATE_H in gcconfig.h a bit. +* The GC_LOOP_ON_ABORT environment variable interfered with incremental +collection, since the write fault handler was erroneously overridden. +Handlers are now set up in the correct order. +* It used to be possible to call GC_mark_thread_local_free_lists() while +the world was not stopped during an incremental GC. This was not safe. +Fortunately, it was also unnecessary. Added GC_world_stopped flag +to avoid it. (This caused occasional crashes in GC_set_fl_marks +with thread local allocation and incremental GC. This probably happened +primarily on old, slow multiprocessors.) +* Allowed overriding of MAX_THREADS in win32_threads.c from the build +command line (thanks to Yannis Bres for the patch). +* Taught the IA64/linux code to determine the register backing store base from +/proc/self/maps after checking the __libc symbol, but before guessing. +(__libc symbols are on the endangered list, and the guess is likely to not +always be right for 2.6 kernels.) Restructured the code to read and parse +/proc/self/maps so it only exists in one place (all platforms). +* The -DUSE_PROC_FOR_LIBRARIES code was broken on Linux. It claimed that it +also registered the main data segment, but didn't actually do so. (I don't +think anyone actually uses this configuration, but ...) +* Made another attempt to get --enablecplusplus to do the right thing. +Since there are unavoidable problems with C programs linking against a +dynamic library that includes C++ code, I separated out the c++ code into +libgccpp. + + +== [6.2alpha4] 2003-03-10 == + +* Use LINUX_STACKBOTTOM for >= glibc2.2 on Linux/MIPS. (See Debian bug +# 177204) +* Integrated Jeff Sturm and Jesse Rosenstock's MACOSX threads patches. +* Integrated Grzegorz Jakacki's substantial GNU build patch. "Make dist" +should now work for the GNU build process. Documentation files +are installed under share/gc. +* Tweaked gc_cpp.h to again support the Borland compiler (thanks to +Rene Girard for pointing out the problems). +* Updated BCC_MAKEFILE (thanks to Rene Girard). +* Added GC_ASSERT check for minimum thread stack size. +* Added --enable-gc-assertions. +* Added some web documentation to the distribution. Updated it in the +process. +* Separate gc_conf_macros.h from gc.h. +* Added generic GC_THREADS client-defined macro to set the appropriate +GC_XXX_THREADS internal macro. (gc_config_macros.h.) +* Add debugging versions of _ignore_off_page allocation primitves. +* Moved declarations of GC_make_closure and GC_debug_invoke_finalizer +from gc.h to gc_priv.h. +* Reset GC_fail_count even if only a small allocation succeeds. +* Integrated Brian Alliet's patch for dynamic library support on Darwin. +* gc_cpp.h's gc_cleanup destructor called GC_REGISTER_FINALIZER_IGNORE_SELF +when it should have called the lower case version, since it was +explicitly computing a base pointer. + + +== [6.2alpha3] 2003-01-30 == + +* Don't include execinfo.h in os_dep.c when it's not needed, and may not +exist. + + +== [6.2alpha2] == + +* Fixed the completely broken FreeBSD code in 6.2alpha1 (thanks to +Hironori Sakamoto for the patch). +* Changed IRIX reference in dbg_mlc.c to IRIX5 (thanks to Marcus Herbert). +* Attempted to work around the problems with .S filenames and the SGI +compiler. (Untested.) +* Worked around an HP/UX make issue with the GNU-style build process. +* Fixed the --enable-cplusplus build machinery to allow builds without +a C++ compiler. (That was always the intent ...) +* Changed the debugging allocation macros to explicitly pass the return +address for Linux and XXXBSD on hardware for which we can't get stack +traces. Use __builtin_return_address(0) to generate it when possible. +Some of the configuration work was cleaned up (good) and moved to gc.h +(bad, but necessary). This should make leak detection more useful +on a number of platforms. (Thanks to Fabian Thylman for the suggestion.) +* Fixed compilation problems in dbg_mlc.c with GC_ADD_CALLER. +* Bumped revision number for dynamic library. + + +== [6.2alpha1] 2003-01-23 == + +* Guard the test for GC_DUMP_REGULARLY in misc.c with +"#ifndef NO_DEBUGGING". Otherwise it fails to build with NO_DEBUGGING +defined. (Thanks to Manuel Serrano.) +* Message about retrying suspend signals was incorrectly generated even when +flag was not set. +* Cleaned up MACOSX/NEXT root registration code. There was apparently a +separate ifdef case in GC_register_data_segments() for no reason. +* Removed MPROTECT_VDB for MACOSX port, based on one negative report. +* Arrange for gc.h and friends to be correctly installed with GNU-style +"make install". +* Enable the GNU-style build facility include C++ support in the library +with --enable-cplusplus (thanks to Thomas Maier for some of the patch). +* Mark from GC_thread_key in linux_threads.c, in case that's allocated +from the garbage collected heap, as it is with our own thread-specific +storage implementation (thanks to Jeff Sturm). +* Mark all free list header blocks if they are heap allocated. This avoids +some unnecessary tracing. And it remains correct if we clear the +root set. (Thanks to Jeff Sturm for identifying the bug.) +* Improved S390/Linux support. Add S390/Linux 64-bit support (thanks to +Ulrich Weigand). +* Corrected the spelling of GC_{M,C}ALLOC_EXPLICTLY_TYPED to +GC_{M,C}ALLOC_EXPLICITLY_TYPED in gc_typed.h. This is technically +an interface change. Based on the fact that nobody reported this, +I suspect/hope there were no clients. +* Cleaned up gc_typed.h so that (1) it adds an extern "C" declaration +when appropriate, (2) doesn't generate references to undefined internal +macros, and (3) allows easier manual construction of descriptors. +* Close the file descriptor used by GC_print_address_map(). +* Set the "close-on-exec" bit for various file descriptors maintained +for the collector's internal use. +* Added a hack to find memory segments owned by the system allocator +under win32. Based on my tests, this tends to eventually find all +segments, though it may take a while. There appear to be cleaner, +but slower solutions under NT/XP. But they rely on an API that's +unsupported under 9X. +* Changed Linux PowerPC stack finding to LINUX_STACKBOTTOM. (Thanks +to Akira Tagoh for pointing out that HEURISTIC1 does not work on +64-bit kernels.) +* Added GC_set_free_space_divisor to avoid some Windows dll issues. +* Added FIXUP_POINTER, POINTER_SHIFT, POINTER_MASK to allow preprocessing +of candidate pointers for tagging, etc. +* Always lock around GC_notify_full_gc(). Simplified code for +invoking GC_notify_full_gc(). +* Changed the way DATASTART is defined on FreeBSD to be robust against +an unmapped page after etext. (Thanks to Hironori Sakamoto for +tracking down the intermittent failure.) +* Made GC_enable() and GC_disable() official. Deprecated direct update +of GC_dont_gc. Changed GC_gcollect to be a noop when garbage collection +is disabled. +* Call GC_register_dynamic_libraries before stopping the world on Linux, +in order to avoid a potential deadlock due to the dl_iterate_phdr lock. +* Introduced a more general mechanism for platform-dependent code to +decide whether the main data segment should be handled separately +from dynamic libraries, or registered by GC_register_dynamic_libraries. +The latter is more reliable and easier on Linux with dl_iterate_phdr. + + +== [6.1] == + +* Added GC_MAXIMUM_HEAP_SIZE environment variable. +* Fix configure.in for MIPS/LINUX (thanks to H.J. Lu). +* Double page hash table size for -DLARGE_CONFIG. +* Integrated Bo Thorsen's X86-64 support. +* STACKBOTTOM definition for LINUX/MIPS was partially changed back +(thanks to H.J. Lu and Hiroshi Kawashima for resolving this). +* Replaced all occurrences of LINUX_DATA_START in gcconfig.h with +SEARCH_FOR_DATA_START. It doesn't hurt to fall back to a search. +And __data_start doesn't seem to get defined correctly of the GC +library is loaded with LD_PRELOAD, e.g. for leak detection. +* If the GC_find_leak environment variable is set, do a +atexit(GC_gcollect) to give us at least one chance to detect leaks. +This may report some very benign leaks, but ... +* Addeded REDIRECT_FREE. It's necessary if we want leak detection with +LD_PRELOAD. +* Defer printing of leaked objects, as for smashed objects. +* Fixed process and descriptor leak in GC_print_callers. Try for +line number even if we got function name.) +* Ported parallel GC support and thread local allocation to Alpha. +Not yet well-tested. +* Added GC_DUMP_REGULARLY and added finalization statistics to GC_dump(). +* Fixed Makefile.am to mention alpha_mach_dep.S instead of the defunct +alpha_mach_dep.s. +* Incorporated a change to new_gc_alloc.h, +which should make it work with gcc3.1. +* Use alpha_mach_dep.S only on Linux. (It's not clear that this is +optimal, but it otherwise didn't build on Tru64. Thanks to Fergus Henderson.) +* Added ifdef to guard free() in os_dep.c. Otherwise we get a +compilation error on Irix (thanks to Dai Sato). +* Added an experimental version of GC_memalign to mallocx.c. This can't +always work, since we don't handle alignment requests in the hblk-level +allocator, and we can't handle arbitrary pointer displacements unless +GC_all_interior_pointers is enabled. But it should work for alignment +requests up to HBLKSIZE. This is not yet documented in the standard +places. +* Finally debugged the OSF1/Tru64 thread support. This needs more testing, +since I needed to add a somewhat unconvincing workaround for signal +delivery issues that I don't yet completely understand. But it does +pass my tests, even in parallel GC mode. Incremental GC support is +disabled if thread support is enabled, due to the signal issues. +* Eliminated name-space-incorrect definition of _cdecl from gc_cpp.h. +* Added GC_debug_malloc_replacement and GC_debug_realloc_replacement +declarations to gc.h. On IA64, this is required for REDIRECT_MALLOC +to work correctly with these. +* Fixed Linux USE_PROC_FOR_LIBRARIES to work with a 64-bit /proc format. + + +== [6.1alpha5] 2002-06-19 == + +* Added GC_finalizer_mem_freed, and changed some of the code that +decided on heap expansion to look at it. Memory explicitly +deallocated by finalizers essentially needs to be counted as reclaimed +by the GC. Otherwise there are cases in which the heap can grow +infinitely. (Thanks to Mark Reichert for the test case.) +* Integrated Adam Megacz patches to not scan dynamic libraries if +we are compiling with gcc on win32. Otherwise we need structured +exception handling to deal with asynchronously unmapped root +segments, and gcc doesn't directly support that. +* Integrated Anthony Green's patch to support Wine. +* GC_OPERATOR_NEW_ARRAY was misspelled OPERATOR_NEW_ARRAY in several +places, including gc_cpp.cc (thanks to Wink Saville for pointing this out). +* Integrated Loren J. Rittle's Alpha FreeBSD patches. These also +changed the declarations of symbols like _end on many platforms to +that they wouldn't mistakenly be declared as short data symbols (suggested by +Richard Henderson). +* Integrated changes from the Debian distribution (thanks to Ryan Murray +for pointing these out). +Fix C++ comments in POWERPC port. Add ARM32 +incremental GC support. Get rid of USE_GENERIC_PUSH_REGS for alpha/Linux, +this time for real. Use va_copy to get rid of cord printf problems +(finally). +* Close file descriptor used to count CPUs (thanks to Jeff Sturm for +pointing out the omission). +* Don't just drop gcj free lists in GC_start_reclaim, since that can +eventually cause the marker to see a bogus mark descriptor in the +dropped objects. The usual symptom was a very intermittent segmentation +fault in the marker. This mattered only if one of the GC_gcj_malloc +variants was used (thanks to Michael Smith, Jeff Sturm, Bryce McKinlay and +Tom Tromey for helping to track this down). +* Fixed Linux and Solaris/64 SPARC configuration (thanks to David Miller, +Jeff Sturm, Tom Tromey, and Christian Joensson). +* Fixed a typo in strdup definition (thanks to Gerard A Allan). +* Changed Makefile.direct to invoke $(CC) to assemble alpha_mach_dep.S. +This is needed on Linux. I'm not sure whether it's better or worse +on Tru64. +* Changed gc_cpp.h once more to declare operator new and friends only in +a Microsoft environment. This may need further fine tuning (thanks to +Johannes Schmidt for pointing out that the older code breaks on gcc3.0.4). +* Don't ever override strdup if it's already macro defined (thanks to +Adnan Ali for pointing out the problem). +* Changed gc_cpp.h yet again to also overload placement new. Due to the +C++ overloading rules, the other overloaded new operations otherwise hide +placement new, which causes many STL uses to break (thanks to Reza Shahidi +for reporting this, and to Matt Austern for proposing a fix). +* Integrated cygwin pthreads support from Dan Bonachea. +* Turn on DYNAMIC_LOADING for NetBSD (thanks to Krister Walfridsson). +* Changed printing code to print more complete GC times. +* Applied Mark Mitchell's Irix patch to correct some bit rot. +* Clarified which object-printing routines in dbg_mlc.c should hold +the allocation lock. Restructured the code to allow reasonable object +printing with -DREDIRECT_MALLOC. +* Fix the Linux mmap code to always start with 0x1000 as the initial hint. +Minor patches for 64-bit AIX, particularly to STACKBOTTOM (thanks to +Jeffrey Mark Siskind). +* Renamed "SUSPENDED" flag for Solaris threads support to avoid a conflict +with a system header (thanks to Philip Brown). +* Cause win32_threads.c to handle an out of range stack pointer correctly, +though currently with a warning. (Thanks to Jonathan Clark for +observing that win32 applications may temporarily use the stack +pointer for other purposes, and suggesting a fix. Unfortunately, it's +not clear that there is a complete solution to this problem.) + + +== [6.1alpha4] 2002-06-16 == + +* Fixed typo in sparc_mach_dep.S, preventing the 64-bit version from +building. Increased 64-bit heap size limit in test.c slightly, since +a functional SPARC collector seems to slightly exceed the old limits. +* Use NPRGREG in solaris_threads.c, thus printing all registers if things +go wrong. +* Added GC_MARKERS environment variable to allow use of a single marker +thread on an MP without confusing the lock implementation. +* Collect much less aggressively in incremental mode with GC_TIME_UNLIMITED. +This is really a purely generational mode, and we can afford to +postpone the collection until the heap is (nearly) full. +* Remove read() wrapper for MPROTECT_VDB. It was causing more harm than +good. It is often no longer needed if system calls avoid writing to +pointerfull heap objects. +* Fix MACOSX test in gcconfig.h (thanks to John Clements). +* Change GC_test_and_set so that it consistently has one argument. +Add spaces to ::: in powerpc assembly code in gc_locks.h (thanks to +Ryan Murray). +* Fixed a formatting error in dbg_mlc.c. Added prototype to GC_abort() +declaration (thanks to Michael Smith). +* Removed "source" argument to GC_find_start(). Eliminate GC_FIND_START(). +* Added win32 recognition code in configure.in. Changed some of the +dllimport/export defines in gc.h (thanks to Adam Megacz). +* GC_malloc_many didn't set hb_last_reclaimed when it called +GC_reclaim_generic. (I'm not sure this matters much, but ...) +* Allocating uncollectible objects with debug information sometimes +allocated objects that were one byte too small, since uncollectible +objects don't have the extra byte added at the end (thanks to +Wink Saville for pointing this out). +* Added a bit more assertion checking to make sure that gcj objects +on free lists never have a nonzero second word. +* Replaced BCC_MAKEFILE with an up-to-date one (thanks to Andre Leiradella). +* Upgraded libtool, cinfigure.in and some related files to hopefully +support NetBSD/SPARC (thanks to Adrian Bunk). Unfortunately, +libtool 1.4.2 seemed to be buggy due to missing quotes in several +"test" invocations. Fixed those in the ltmain.sh script. +* Some win32-specific patches, including the introduction of +GC_CreateThread (thanks to Adam Megacz). +* Merged in gcj changes from Anthony Green to support embedded systems. +* Tried to consistently rename preprocessed assembly files with a capital +.S extension. +* Use alpha_mach_dep.S on ALPHA again. It doesn't really matter, but this +makes our distribution consistent with the gcc one, avoiding future merge +problems. +* Move GET_MEM definition into gcconfig.h. Include gcconfig.h slightly +later in gc_priv.h to avoid forward references to ptr_t. +* Add some testing of local allocation to test.c. +* Change definition of INVALID_QTID in specific.h. The -1 value was used +inconsistently, and too likely to collide with a valid stack address. +Some general clean-up of specific.[ch]. Added assertions. (Thanks +to Michael Smith for tracking down an intermittent bug to this +general area. I'm not sure it has been squashed yet, however.) +* On Pthread systems it was not safe to call GC_malloc() between fork() +and exec(). According to the applicable standards, it doesn't appear +to be safe to call malloc() or many other libc functions either, thus +it's not clear this is fixable. Added experimental support for +-DHANDLE_FORK in linux_threads.c which tries to support it. It may +succeed if libc does the right thing. I'm not sure whether it does. +(Thanks to Kenneth Schalk for pointing out this issue.) +* Documented thread local allocation primitives to require an +explicit GC_init call. GC_init_parallel is no longer declared to +be a constructor function, since that isn't portable and often +seems to lead to initialization order problems. +* Changed gc_cpp.cc and gc_cpp.h in one more attempt to make them +compatible with Visual C++ 6 (thanks to Wink Saville for the patch). +* Some more patches for Linux on HP PA-RISC. +* Added include/gc_allocator.h. It implements (hopefully) standard +conforming (as opposed to SGI-style) allocators that allocate +collectible (gc_allocator) or GC-traceable, but not collectible +(traceable_allocator) objects. This borrows heavily from libstc++, +which borrows heavily from the SGI implementation, this part of +which was written by Matt Austern. Changed test_cpp.cc to very +minimally test this. +* On Linux/X86, retry mmap with a different start argument. That should +allow the collector to use more (closer to 3GB) of the address space. +* Force 64 bit alignment with GCJ support (reflects Bryce McKinlay's +patch to the gcc tree). +* Refined the choice of sa_handler vs. sa_sigaction in GC_dirty_init +to accommodate some glibc5 systems (thanks to Dan Fandrich for the patch). +* Compensated for the fact that current versions of glibc set +__libc_stack_end incorrectly on Linux/IA64 while initialization code +is running. This could cause the collector to miss 16 bytes of +the memory stack if GC_malloc or friends where called before main(). +* Mostly integrated Takis Psarogiannakopoulos' port to DG/UX Inix 86. +This will probably take another iteration to work, since his +patch conflicted with the libtool upgrade. +* Added README.arm.cross containing some information about cross- +compiling to an ARM processor from Margaret Fleck (original code provided by +Bradley D. LaRonde; edited by Andrej Cedilnik using some of solutions by +Tilman Vogel; also ported for iPAQ by Oliver Kurth). + + +== [6.1alpha3] 2002-02-07 == + +* Minor cleanup on the gcconfig.h section for SPARC. +* Minor fix to support Intel compiler for I386/Linux (thanks to +Sven Hartrumpf). +* Added SPARC V9 (64-bit) support (thanks to Jeff Sturm). +* Restructured the way in which we determine whether or not to keep +call stacks for debug allocation. By default SAVE_CALL_COUNT is +now zero on all platforms. Added SAVE_CALL_NARGS parameters. +If possible, use execinfo.h to capture call stack. (This should +add support for a number of new platforms, though often at +considerable runtime expense.) +* Try to print symbolic information for call stacks. On Linux, we +do this with a combination of execinfo.h and running addr2line in +a separate process. This is both much more expensive and much more +useful. Amazingly, it seems to be fast enough for most purposes. +* Redefined strdup if -DREDIRECT_MALLOC is given. +* Changed incremental collector and MPROTECT_VDB implementation so that, +under favorable conditions, pointer-free objects are not protected. +Added GC_incremental_protection_needs() to determine ahead of time whether +pointer-free objects may be protected. Replaced GC_write_hint() with +GC_remove_protection(). +* Added test for GC_ENABLE_INCREMENTAL environment variable. +* Made GC_time_limit runtime configurable. Added GC_PAUSE_TIME_TARGET +environment variable. +* Eliminated GC_page_sz, a duplicate of GC_page_size. +* Caused the Solaris and Irix thread creation primitives to call +GC_init_inner(). + + +== [6.1alpha2] 2001-12-20 == + +* No longer wrap read by default in multi-threaded applications. It was +pointed out on the libgcj list that this holds the allocation lock for +way too long if the read blocks. For now, reads into the heap are +broken with incremental collection. It's possible to turn this back on +if you make sure that read calls don't block (e.g. by calling select +first). +* Fix ifdef in Solaris_threads.h to refer to GC_SOLARIS_THREADS. +* Added check for environment variable GC_IGNORE_GCJ_INFO. +* Added printing of stop-the-world GC times if GC_PRINT_STATS environment +variable is set. +* The calloc definition in leak_detector.h was missing parentheses, and +realloc was missing a second argument to GC_REALLOC (thanks to +Elvenlord Elrond). +* Added GC_PRINT_BACK_HEIGHT environment variable and associated +code, mostly in the new file backgraph.c. See doc/README.environment. +* Added -DUSE_GLOBAL_ALLOC to work around a Windows NT issue (thanks to +Jonathan Clark). +* Integrated port to NEC EWS4800 (MIPS-based workstation, with somewhat +different address-space layout). This may help for other machines with +holes in the data segment. (Thanks to Hironori Sakamoto.) +* Changed the order in which GC_push_roots and friends push things onto +the mark stack. GC_push_all calls need to come first, since we can't +necessarily recover if those overflow the mark stack. (Thanks to +Matthew Flatt for tracking down the problem.) +* Some minor cleanups to mostly support the Intel compiler on Linux/IA64. + + +== [6.1alpha1] 2001-09-22 == + +* Non-debug, atomic allocations could result in bogus smashed object +reports with debugging on (thanks to Patrick Doyle for the small test case). +* Fixed GC_get_register_stack_base (Itanium only) to work around a glibc +2.2.4 bug. +* Initial port to HP/UX on Itanium. Thread support and both 32 and 64 +bit ABIs appear to work. Parallel mark support doesn't yet, due to +some inline assembly code issues. Thread local allocation does appear +to work. +* ifdef'ed out glibc2.1/Itanium workaround. I suspect nobody is using +that combination anymore. +* Added a patch to make new_gc_alloc.h usable with gcc3.0 (thanks to +Dimitris Vyzovitis for the patch). +* Debugged 64-bit support on HP/UX PA-RISC. +* Turned on dynamic loading support for FreeBSD/ELF (thanks to Peter Housel). +* Unregistering of finalizers with debugging allocation was broken (thanks +to Jani Kajala for the test case). +* Old finalizers were not returned correctly from GC_debug_register_finalizer. +* Disabled MPROTECT_VDB for Linux/M68K based on a report that it doesn't work. +* Cleaned up some statistics gathering code in reclaim.c (thanks to +Walter Bright). +* Added some support for OpenBSD/ELF/Linux (thanks to Suzuki Toshiya). +* Added Jakub Jelinek's patch to use dl_iterate_phdr for dynamic library +traversal to dyn_load.c. Changed it to weakly reference dl_iterate_phdr, +so that the old code is still used with old versions of glibc. +* Cleaned up feature test macros for various threads packages and +integrated (partially functional) FreeBSD threads code from Loren J. Rittle. +It's likely that the cleanup broke something, since it touched lots of +code. It's also likely that it fixed some unreported bugs in the +less common thread implementations, since some of the original code +didn't stand up to close scrutiny. Support for the next pthreads +implementation should be easier to add. + + +== [6.0] 2001-07-26 == + +* Two more bug fixes for KEEP_BACK_PTRS and DBG_HDRS_ALL. +* Fixed a stack clearing problem that resulted in SIGILL with a +misaligned stack pointer for multi-threaded SPARC builds. +* Integrated another HURD patch (thanks to Igor Khavkine). + + +== [6.0alpha9] == + +* added README.macros. +* Made gc.mak a symbolic link to work around winzip's tendency to ignore +hard links. +* Simplified the setting of NEED_FIND_LIMIT in os_dep.c, possibly breaking +it on untested platforms. +* Integrated initial GNU HURD port (thanks to Chris Lingard and +Igor Khavkine). +* A few more fixes for Digital Mars compiler (by Walter Bright). +* Fixed gcc version recognition. Renamed OPERATOR_NEW_ARRAY to +GC_OPERATOR_NEW_ARRAY. Changed GC_OPERATOR_NEW_ARRAY to be the default. +It can be overridden with -DGC_NO_OPERATOR_NEW_ARRAY (thanks to +Cesar Eduardo Barros). +* Changed the byte size to free-list mapping in thread local allocation +so that size 0 allocations are handled correctly. +* Fixed Linux/MIPS stackbottom for new toolchain (thanks to Ryan Murray). +* Changed finalization registration to invoke GC_oom_fn when it runs out +of memory. +* Removed lvalue cast in finalize.c. This caused some debug configurations +not to build with some non-gcc compilers. + + +== [6.0alpha8] 2001-06-15 == + +* Changed GC_debug_malloc_replacement and GC_debug_realloc_replacement +so that they compile under Irix (thanks to Dave Love). +* Updated powerpc_macosx_mach_dep.s so that it works if the collector +is in a dynamic library (thanks to Andrew Begel). +* Transformed README.debugging into debugging.html, updating and +expanding it in the process. Added gcdescr.html and tree.html +from the web site to the GC distribution. +* Fixed several problems related to PRINT_BLACK_LIST. This involved +restructuring some of the marker macros. +* Fixed some problems with the sizing of objects with debug information. +Finalization was broken KEEP_BACK_PTRS or PRINT_BLACK_LIST. Reduced the +object size with SHORT_DEBUG_HDRS by another word. +* The "Needed to allocate blacklisted ..." warning had inadvertently +been turned off by default, due to a buggy test in allchblk.c. Turned +it back on. +* Removed the marker macros to deal with 2 pointers in interleaved fashion. +They were messy and the performance improvement seemed minimal. We'll +leave such scheduling issues to the compiler. +* Changed Linux/PowerPC test to also check for __powerpc__ in response +to a discussion on the gcc mailing list. +* Removed the "static" from the jmp_buf declaration in GC_generic_push_regs +(suggested by Matthew Flatt). This was causing problems in +systems that register all of their own roots. It looks far more correct +to me without the "static" anyway. +* Fixed several problems with thread local allocation of pointer-free or +typed objects. The collector was reclaiming thread-local free lists, since +it wasn't following the link fields. +* There was apparently a long-standing race condition related to +multi-threaded incremental collection. A collection could be started and +a thread stopped between the memory unprotect system call and the setting of +the corresponding dirt bit. I believe this did not affect Solaris or PCR, +which use a different dirty-bit implementation. Fixed this by installing +signal handlers with sigaction instead of signal, and disabling the thread +suspend signal while in the write-protect handler. (It is unclear +whether this scenario ever actually occurred.) +* Incremental collection did not cooperate correctly with the PARALLEL_MARK +implementation of GC_malloc_many or the local_malloc primitives. It still +doesn't work well, but it shouldn't lose memory anymore. +* Integrated some changes from the gcc source tree that I had previously +missed (thanks to Bryce McKinlay for the reminder and patch). +* Added Makefile.direct as a copy of the default Makefile, which would +normally be overwritten if configure is run. +* Changed the gc.tar target in Makefile.direct to embed the version number +in the gc directory name. This will affect future tar file distributions. +* Changed the Irix dynamic library finding code to no longer try to +eliminate writable text segments under Irix6.x, since that is probably no +longer necessary, and can apparently be unsafe on occasion (thanks to +Shiro Kawai for pointing this out). +* GC_cleanup with GC_DEBUG enabled passed a real object base address to +GC_debug_register_finalizer_ignore_self, which expected a pointer past the +debug header. Call GC_register_finalizer_ignore_self instead, even with +debugging enabled (thanks to Jean-Daniel Fekete for catching this). +* The collector didn't build with call chain saving enabled but NARGS=0. +* Fixed up the GNU-style build files enough so that they work in some +obvious cases (thanks to Maarten Thibaut). +* Added initial port to Digital Mars compiler for win32 (thanks to Walter +Bright). + + +== [6.0alpha7] 2001-04-19 == + +* Added GC_finalizer_notifier. Fixed GC_finalize_on_demand. (The variable +actually wasn't being tested at the right points. The build-time flag +was.) +* Added Tom Tromey's S390 Linux patch. +* Added code to push GC_finalize_now in GC_push_finalizer_structures +(thanks to Matthew Flatt). +* Added GC_push_gc_structures() to push all GC internal roots. +* Integrated some FreeBSD changes from Matthew Flatt. +* It looks like USRSTACK is not always correctly defined under Solaris. +Hacked gcconfig.h to attempt to work around the problem. The result +is not well tested. (Thanks again to Matthew Flatt for pointing this out.) +* Added Ji-Yong Chung's win32 threads and C++ fixes. +* Arranged for hpux_test_and_clear.s to no longer be needed or built. +It was causing build problems with gas, and it's not clear this is +better than the pthreads alternative on this platform. +* Some MINGW32 fixes from Hubert Garavel. +* Added initial Hitachi SH4 port from Kaz Kojima. +* Ported thread-local allocation and parallel mark code to HP/UX on PA_RISC. +* Made include/gc_mark.h more public and separated out the really private +pieces. This is probably still not quite sufficient for clients that +want to supply their own kind of type information. But it's a start. +This involved lots of identifier renaming to make it namespace clean. +* Added GC_dont_precollect for clients that need complete control over +the root set. +* GC_is_visible didn't do the right thing with gcj objects. (Not that +many people are likely to care, but ...) +* Don't redefine read with GC_USE_LD_WRAP. +* Initial port to LINUX/HP_PA. Incremental collection and threads are not +yet supported. (Incremental collection should work if you have the +right kernel. Threads may work with a sufficiently patched pthread +library.) +* Changed gcconfig.h to recognize __i386__ as an alternative to i386 in +many places (thanks to Benjamin Lerman). +* Made win32_threads.c more tolerant of detaching a thread that it didn't +know about (thanks to Paul Nash). +* Added Makefile.am and configure.in from gcc to the distribution, with +minimal changes. For the moment, those are just placeholders. In the +future, we're planning to switch to a GNU-style build environment for +Un*x-like systems, though the old Makefile will remain as a backup. +* Turned off STUBBORN_ALLOC by default, and added it back as a Makefile +option. +* Redistributed some functions between malloc.c and mallocx.c, so that +simple statically linked apps no longer pull in mallocx.o. +* Changed large object allocation to clear the first and last few words +of each block before releasing the lock. Otherwise the marker could see +objects with nonsensical type descriptors. +* Fixed a couple of subtle problems that could result in not recognizing +interior pointers from the stack. (I believe these were introduced +in 6.0alpha6.) +* GC_debug_free_inner called GC_free, which tried to reacquire the +allocator lock, and hence deadlocked. (DBG_HDRS_ALL probably never worked +with threads.) +* Fixed several problems with back traces. Accidental references to a free +list could cause the free list pointer to be overwritten by a back pointer. +There seemed to be some problems with the encoding of root and finalizer +references. + + +== [6.0alpha6] == + +* Changed the definition of DATASTART on ALPHA and IA64, where data_start +and __data_start are not defined by earlier versions of glibc. This might +need to be fixed on other platforms as well. +* Changed the way the stack base and backing store base are found on IA64. +This should now remain reliable on future kernels. But since it relies +on /proc, it will no longer work in the simulated NUE environment. +* Made the call to random() in dbg_mlc.c with -DKEEP_BACK_PTRS dependent +on the OS. On non-Unix systems, rand() should be used instead. Handled +small RAND_MAX (thanks to Peter Ross for pointing this out). +* Fixed the cord make rules to create the cord subdirectory, if necessary +(thanks to Doug Moen). +* Changed fo_object_size calculation in finalize.c. Turned finalization +of non-heap object into a no-op. Removed anachronism from GC_size() +implementation. +* Changed GC_push_dirty call in solaris_threads.c to GC_push_selected. +It was missed in a previous renaming (thanks to Vladimir Tsichevski +for pointing this out). +* Arranged to not mask SIGABRT in linux_threads.c (thanks to Bryce McKinlay). +* Added GC_no_dls hook for applications that want to register their own +roots. +* Integrated Kjetil Matheussen's Amiga changes. +* Added FREEBSD_STACKBOTTOM. Changed the X86/FreeBSD port to use it +(thanks to Matthew Flatt). +* Added pthread_detach interception for platforms supported by linux_threads.c +and irix_threads.c. +* Changed the USE_MMAP code to check for the case in which we got the +high end of the address space, i.e. mem_ptr + mem_sz == 0. It appears +that this can happen under Solaris 7. It seems to be allowed by what +I would claim is an oversight in the mmap specification. (Thanks to +Toshio Endo for pointing out the problem.) +* Cleanup of linux_threads.c. Some code was originally cloned from +irix_threads.c and now unnecessary. Some comments were obviously wrong. +* (Mostly) fixed a longstanding problem with setting of dirty bits from +a signal handler. In the presence of threads, dirty bits could get lost, +since the etting of a bit in the bit vector was not atomic with respect +to other updates. The fix is 100% correct only for platforms for which +GC_test_and_set is defined. The goal is to make that all platforms with +thread support. Matters only if incremental GC and threads are both +enabled. +* made GC_all_interior_pointers (a.k.a. ALL_INTERIOR_POINTERS) an +initialization time, instead of build-time option. This is a +nontrivial, high risk change. It should slow down the code measurably +only if MERGE_SIZES is not defined, which is a very nonstandard +configuration. +* Added doc/README.environment, and implemented what it describes. This +allows a number of additional configuration options to be set through +the environment. It documents a few previously undocumented options. +* Integrated Eric Benson's leak testing improvements. +* Removed the option to throw away the beginning of each page (DISCARD_WORDS). +This became less and less useful as processors enforce stricter alignment. +And it hadn't been tested in ages, and was thus probably broken anyway. + + +== [6.0alpha5] == + +* Changed the definition of GC_pause in linux_threads.c to use a volatile +asm. Some versions of gcc apparently optimize away writes to local volatile +variables. This caused poor locking behavior starting at about +4 processors. +* Added GC_start_blocking(), GC_end_blocking() calls and wrapper for sleep +to linux_threads.c. +The first two calls could be used to generally avoid sending GC signals to +blocked threads, avoiding both premature wakeups and unnecessary overhead. +* Fixed a serious bug in thread-local allocation. At thread termination, +GC_free could get called on small integers. Changed the code for thread +termination to more efficiently return left-over free-lists. +* Integrated Kjetil Matheussen's BeOS support. +* Rearranged the directory structure to create the doc and tests +subdirectories. +* Sort of integrated Eric Benson's patch for OSF1. This provided basic +OSF1 thread support by suitably extending hpux_irix_threads.c. Based +on earlier email conversations with David Butenhof, I suspect that it +will be more reliable in the long run to base this on linux_threads.c +instead. Thus I attempted to patch up linux_threads.c based on Eric's code. +The result is almost certainly broken, but hopefully close enough that +someone with access to a machine can pick it up. +* Integrated lots of minor changes from the NetBSD distribution. (These +were supplied by David Brownlee. I'm not sure about the original +authors.) +* Hacked a bit more on the HP/UX thread-support in linux_threads.c. It +now appears to work in the absence of incremental collection. Renamed +hpux_irix_threads.c back to irix_threads.c, and removed the attempt to +support HPUX there. +* Changed gc.h to define _REENTRANT in cases in which it should already +have been defined. It is still safer to also define it on the command +line. + + +== [6.0alpha4] == + +* Moved up the detection of mostly full blocks to the initiation of the +sweep phase. This eliminates some lock contention in the PARALLEL_MARK case, +as multiple threads try to look at mostly full blocks concurrently. +* Restored the code in GC_malloc_many that grabs a prefix of the global +free list. This avoids the case in which every GC_malloc_many call +tries and fails to allocate a new heap block, and the returns a single +object from the global free list. +* Some minor fixes in new_hblk.c. (Attempted to build free lists in order +of increasing addresses instead of decreasing addresses for cache performance +reasons. But this seems to be only a very minor gain with -DEAGER_SWEEP, +and a loss in other cases. So the change was backed out.) +* Fixed some of the documentation (thanks in large part to Fergus Henderson). +* Fixed the Linux USE_PROC_FOR_LIBRARIES code to deal with apps that perform +large numbers of mmaps (thanks to Eric Benson). Also fixed that code to +deal with short reads. +* Added GC_get_total_bytes(). +* Fixed leak detection mode to avoid spurious messages under linuxthreads. +(This should also now be easy for the other supported threads packages. +But the code is tricky enough that I'm hesitant to do it without being able +to test. Everything allocated in the GC thread support itself should be +explicitly deallocated.) +* Made it possible (with luck) to redirect malloc to GC_local_malloc. + + +== [6.0alpha3] 2000-09-26 == + +* Fixed the /proc/self/maps code to not seek, since that apparently is not +reliable across all interesting kernels. +* Fixed some compilation problems in the absence of PARALLEL_MARK +(introduced in alpha2). +* Fixed an algorithmic problem with PARALLEL_MARK. If work needs to +be given back to the main mark "stack", the BOTTOM entries of the local +stack should be given away, not the top ones. This has substantial +performance impact, especially for > 2 processors, from what I can tell. +* Extracted gc_lock.h from gc_priv.h. This should eventually make it a +bit easier to avoid including gc_priv.h in clients. +* Moved all include files to include/ and removed duplicate links to the +same file. The old scheme was a bad idea because it was too easy to get the +copies out of sync, and many systems don't support hard links. +Unfortunately, it's likely that I broke some of the non-Unix Makefiles in +the process, although I tried to update them appropriately. +* Removed the partial support for a copied nursery. It's not clear that +this would be a tremendous win, since we don't consistently lose to +generational copying collectors. And it would significantly complicate +many things. May be reintroduced if/when it really turns out to win. +* Removed references to IRIX_JDK_THREADS, since I believe there never +were and never will be any clients. +* Added some code to linux_threads.c to possibly support HPUX threads +using the Linux code. Unfortunately, it doesn't work yet, and is +currently disabled. +* Added support under Linux/X86 for saving the call chain, both in (debug) +objects for client debugging, and in GC_arrays._last_stack for GC +debugging. This was previously supported only under Solaris. It is +not enabled by default under X86, since it requires that code be compiled +to explicitly gave frame pointers on the call stack. (With gcc this +currently happens by default, but is often turned off explicitly.) +To turn it on, define SAVE_CALL_CHAIN. + + +== [6.0alpha2] == + +* Added USE_MARK_BYTES to reduce the need for compare-and-swap on platforms +for which that's expensive. +* Fixed a locking bug ib GC_gcj_malloc and some locking assertion problems. +* Added a missing volatile to OR_WORD and renamed the parameter to +GC_compare_and_swap so it's not a C++ reserved word (thanks to +Toshio Endo for pointing out both of those). +* Changed Linux dynamic library registration code to look at /proc/self/maps +instead of the rld data structures when REDIRECT_MALLOC is defined. +Otherwise some of the rld data data structures may be prematurely garbage +collected. +* Fixed USE_LD_WRAP a bit more, so it should now work without threads. +* Renamed XXX_THREADS macros to GC_XXX_THREADS for namespace correctness. +Temporarily added some backward compatibility definitions. Renamed +USE_LD_WRAP to GC_USE_LD_WRAP. +* Many MACOSX POWERPC changes, some additions to the gctest output, and +a few minor generic bug fixes (thanks to Dietmar Planitzer). + + +== [6.0alpha1] == + +* Added HP/PA prefetch support. +* Added -DDBG_HDRS_ALL and -DSHORT_DBG_HDRS to reduce the cost and improve +the reliability of generating pointer backtrace information, e.g. in +the Bigloo environment. +* Added parallel marking support (-DPARALLEL_MARK). This currently +works only under IA32 and IA64 Linux, but it shouldn't be hard to adapt +to other platforms. This is intended to be a lighter-weight (less +new code, probably not as scalable) solution than the work by Toshio Endo +et al, at the University of Tokyo. A number of their ideas were +reused, though the code wasn't, and the underlying data structure +is significantly different. In particular, we keep the global mark +stack as a single shared data structure, but most of the work is done +on smaller thread-local mark stacks. +* Changed GC_malloc_many to be cheaper, and to require less mutual exclusion +with -DPARALLEL_MARK. +* Added full support for thread local allocation under Linux +(-DTHREAD_LOCAL_ALLOC). This is a thin veneer on GC_malloc_many, and +should be easily portable to other platforms, especially those that +support pthreads. +* CLEAR_DOUBLE was not always getting invoked when it should have been. +* GC_gcj_malloc and friends used different out of memory handling than +everything else, probably because I forgot about one when I implemented +the other. They now both call GC_oom_fn(), not GC_oom_action(). +* Integrated Jakub Jelinek's fixes for Linux/SPARC. +* Moved GC_objfreelist, GC_aobjfreelist, and GC_words_allocd out of +GC_arrays, and separately registered the first two as excluded roots. +This makes code compiled with gc_inl.h less dependent on the +collector version. (It would be nice to remove the inclusion of +gc_priv.h by gc_inl.h completely, but we're not there yet. The +locking definitions in gc_priv.h are still referenced.) +This change was later conditioned on SEPARATE_GLOBALS, which +is not defined by default, since it involves a performance hit. +* Register GC_obj_kinds separately as an excluded root region. The +attempt to register it with GC_arrays was usually failing. (This wasn't +serious, but seemed to generate some confusion.) +* Moved backptr.h to gc_backptr.h. + + +== [5.4] == + +* Fixed a typo that prevented compilation with -DUSE_3DNOW_PREFETCH (thanks to +Shawn Wagner for actually testing this). +* Fixed GC_is_thread_stack in solaris_threads.c. It forgot to return a value +in the common case. +* Fixed another silly syntax problem in GC_double_descr (thanks to +Fergus Henderson for finding it). +* Fixed a GC_gcj_malloc bug: It tended to release the allocator lock twice. + + +== [5.3] 2000-09-24 == + +* Fixed _end declaration for OSF1. +* There were lots of spurious leak reports in leak detection mode, caused +by the fact that some pages were not being swept, and hence unmarked +objects weren't making it onto free lists. (This bug dated back to 5.0.) +* Fixed a typo in the liblinuxgc.so Makefile rule. +* Added the GetExitCodeThread to Win32 GC_stop_world to (mostly) work +around a Windows 95 GetOpenFileName problem (thanks to Jacob Navia). + + +== [5.2] == + +* dyn_load.c declared GC_scratch_last_end_ptr as an extern even if it +was defined as a macro. This prevented the collector from building on +Irix. +* We quietly assumed that indirect mark descriptors were never 0. +Our own typed allocation interface violated that. This could result +in segmentation faults in the marker with typed allocation. +* Fixed a _DUSE_MUNMAP bug in the heap block allocation code (thanks to +Ben Hutchings for the patch). +* Taught the collector about VC++ handling array operator new (thanks to +Ben Hutchings for the patch). +* The two copies of gc_hdrs.h had diverged. Made one a link to the other +again. + + +== [5.1] == + +* Fixed a gc.h header bug which showed up under Irix (thanks to Dan Sullivan). +* Fixed a typo in GC_double_descr in typd_mlc.c. +This probably could result in objects described by array descriptors not +getting traced correctly (thanks to Ben Hutchings for pointing this out). +* The block nearly full tests in reclaim.c were not correct for 64 bit +environments. This could result in unnecessary heap growth under unlikely +conditions. + + +== [5.0] == + +* Fixed threadlibs.c for linux threads. -DUSE_LD_WRAP was broken and +-ldl was omitted. Fixed Linux stack finding code to handle +-DUSE_LD_WRAP correctly. +* Added MSWIN32 exception handler around marker, so that the collector +can recover from root segments that are unmapped during the collection. +This caused occasional failures under Windows 98, and may also be +an issue under Windows NT/2000. + + +== [5.0alpha7] == + +* -DREDIRECT_MALLOC was broken in alpha6. Fixed. +* Cleaned up gc_ccp.h slightly, thus also causing the HP C++ compiler to +accept it. +* Removed accidental reference to dbg_mlc.c, which caused dbg_mlc.o to be +linked into every executable. +* Added PREFETCH to bitmap marker. Changed it to use the header cache. +* GC_push_marked sometimes pushed one object too many, resulting in a +segmentation fault in GC_mark_from_mark_stack. This was probably an old +bug. It finally showed up in gctest on win32. +* Gc_priv.h erroneously #defined GC_incremental to be TRUE instead of FALSE +when SMALL_CONFIG was defined. This was no doubt a major performance bug for +the default win32 configuration. +* Removed -DSMALL_CONFIG from NT_MAKEFILE. It seemed like an anachronism now +that the average PC has 64MB or so. +* Integrated Bryce McKinlay's patches for linux threads and dynamic loading +from the libgcj tree. Turned on dynamic loading support for Linux/PPC. +* Changed the stack finding code to use environ on HP/UX (thanks +to Gustavo Rodriguez-Rivera for the suggestion). This should +probably be done on other platforms, too. Since I can't test those, that'll +wait until after 5.0. + + +== [5.0alpha6] == + +* GC_malloc_explicitly_typed and friends sometimes failed to +initialize first word. +* Added allocation routines and support in the marker for mark descriptors +in a type structure referenced by the first word of an object. This was +introduced to support gcj, but hopefully in a way that makes it +generically useful. +* Added GC_requested_heapsize, and inhibited collections in non-incremental +mode if the actual used heap size is less than what was explicitly +requested. +* The Solaris pthreads version of GC_pthread_create didn't handle a NULL +attribute pointer. Solaris thread support used the wrong default thread +stack size (thanks to Melissa O'Neill for the patch). +* Changed PUSH_CONTENTS macro to no longer modify first parameter. +This usually doesn't matter, but it was certainly an accident waiting +to happen ... +* Added GC_register_finalizer_no_order and friends to gc.h. They're +needed by Java implementations. +* Integrated a fix for a win32 deadlock resulting from clock() calling +malloc (thanks to Chris Dodd). +* Integrated Hiroshi Kawashima's port to Linux/MIPS. This was designed +for a handheld platform, and may or may not be sufficient for other +machines. +* Fixed a va_arg problem with the %c specifier in cordprnt.c. It appears +that this was always broken, but recent versions of gcc are the first to +report the (statically detectable) bug. +* Added an attempt at a more general solution to dlopen races/deadlocks. +GC_dlopen now temporarily disables collection. Still not ideal, but ... +* Added -DUSE_I686_PREFETCH, -DUSE_3DNOW_PREFETCH, and support for IA64 +prefetch instructions. May improve performance measurably, but I'm not +sure the code will run correctly on processors that don't support the +instruction. Won't build except with very recent gcc. +* Added caching for header lookups in the marker. This seems to result +in a barely measurable performance gain. Added support for interleaved +lookups of two pointers, but unconfigured that since the performance +gain is currently near zero, and it adds to code size. +* Changed Linux DATA_START definition to check both data_start and +__data_start, since nothing else seems to be portable. +* Added -DUSE_LD_WRAP to optionally take advantage of the GNU ld function +wrapping mechanism. Probably currently useful only on Linux. +* Moved some variables for the scratch allocator into GC_arrays (suggested +by Martin Hirzel). +* Fixed a win32 threads bug that caused the collector to not look for +interior pointers from one of the thread stacks without +ALL_INTERIOR_POINTERS (thanks to Jeff Sturm). +* Added Mingw32 support (thanks to Jeff Sturm for the patch). +* Changed the alpha port to use the generic register scanning code instead +of alpha_mach_dep.s. Alpha_mach_dep.s doesn't look for pointers in fp +registers, but gcc sometimes spills pointers there (thanks to Manuel Serrano +for helping debug this). Changed the IA64 code to +do something similar for similar reasons. + + +== [5.0alpha4] 1999-10-30 == + +* Added protection fault handling patch for Linux/M68K from Fergus +Henderson and Roman Hodek. +* Removed the tests for SGI_SOURCE in new_gc_alloc.h. This was causing that +interface to fail on non-SGI platforms. +* Changed the Linux stack finding code to use /proc, after changing it +to use HEURISTIC1 (thanks to David Mossberger for pointing out /proc hook). +* Added HP/UX incremental GC support and HP/UX 11 thread support. +Thread support is currently still flaky. +* Added basic Linux/IA64 support. +* Integrated Anthony Green's PicoJava support. +* Integrated Scott Ananian's StrongARM/NetBSD support. +* Fixed some fairly serious performance bugs in the incremental +collector. These have probably been there essentially forever. +(Mark bits were sometimes set before scanning dirty pages. +The reclaim phase unnecessarily dirtied full small object pages.) +* Changed the reclaim phase to ignore nearly full pages to avoid +touching them. +* Limited GC_black_list_spacing to roughly the heap growth increment. +* Changed full collection triggering heuristic to decrease full GC +frequency by default, but to explicitly trigger full GCs during +heap growth. This doesn't always improve things, but on average it's +probably a win. +* GC_debug_free(0, ...) failed (thanks to Fergus Henderson for the +bug report and fix). + + +== [5.0alpha3] 1999-09-15 == + +(Also known as 4.15alpha3.) +* Added some highly incomplete code to support a copied young generation. +Comments on nursery.h are appreciated. +* Changed -DFIND_LEAK, -DJAVA_FINALIZATION, and -DFINALIZE_ON_DEMAND, +so the same effect could be obtained with a runtime switch. This is +a step towards standardizing on a single dynamic GC library. +* Significantly changed the way leak detection is handled, as a consequence +of the above. + + +== [5.0alpha2] 1999-07-23 == + +* Fixed bugs introduced in alpha1 (OpenBSD & large block initialization). +* Added -DKEEP_BACK_PTRS and backptr.h interface. (The implementation +idea came from Alan Demers.) + + +== [5.0alpha1] 1999-06-30 == + +(Also known as 4.15alpha1.) +* Reworked large block allocator. Now uses multiple doubly linked free +lists to approximate best fit. +* Changed heap expansion heuristic. Entirely free blocks are no longer +counted towards the heap size. This seems to have a major impact on +heap size stability; the old version could expand the heap way too +much in the presence of large block fragmentation. +* added -DGC_ASSERTIONS and some simple assertions inside the collector. +This is mainlyt for collector debugging. +* added -DUSE_MUNMAP to allow the heap to shrink. Supported on only +a few UNIX-like platforms for now. +* added GC_dump_regions() for debugging of fragmentation issues. +* Changed PowerPC pointer alignment under Linux to 4. +* Changed the Linux/Alpha port to walk the data segment backwards until +it encounters a SIGSEGV. The old way to find the start of the data +segment broke with a recent release. +* cordxtra.c needed to call GC_REGISTER_FINALIZER instead of +GC_register_finalizer, so that it would continue to work with GC_DEBUG. +* allochblk sometimes cleared the wrong block for debugging purposes +when it dropped blacklisted blocks. This could result in spurious +error reports with GC_DEBUG. +* added MACOS X Server support (thanks to Andrew Stone). +* Changed the Solaris threads code to ignore stack limits > 8 MB with +a warning. Empirically, it is not safe to access arbitrary pages +in such large stacks. And the dirty bit implementation does not +guarantee that none of them will be accessed. +* Integrated Martin Tauchmann's Amiga changes. +* Integrated James Dominy's OpenBSD/SPARC port. + + +== [4.14] 1999-04-16 == + +* changed STACKBOTTOM for DJGPP (thanks to Salvador Eduardo Tropea). + + +== [4.14alpha2] 1999-03-26 == + +* -DSMALL_CONFIG did not work reliably with large (> 4K) pages. +Recycling the mark stack during expansion could result in a size +zero heap segment, which confused things. (This was probably also an +issue with the normal config and huge pages.) +* Did more work to make sure that callee-save registers were scanned +completely, even with the setjmp-based code. Added USE_GENERIC_PUSH_REGS +macro to facilitate testing on machines I have access to. +* Added code to explicitly push register contents for win32 threads. +This seems to be necessary. (Thanks to Pierre de Rop.) + + +== [4.14alpha1] 1999-03-10 == + +* Fixed GC_print_source_ptr to not use a prototype. +* generalized CYGWIN test. +* gc::new did the wrong thing with PointerFreeGC placement (thanks to +Rauli Ruohonen). +* In the ALL_INTERIOR_POINTERS (default) case, some callee-save register +values could fail to be scanned if the register was saved and +reused in a GC frame. This showed up in verbose mode with gctest +compiled with an unreleased SGI compiler. I vaguely recall an old +bug report that may have been related. The bug was probably quite old. +(The problem was that the stack scanning could be deferred until +after the relevant frame was overwritten, and the new save location +might be outside the scanned area. Fixed by more eager stack scanning.) +* PRINT_BLACK_LIST had some problems. A few source addresses were garbage. +* Replaced Makefile.dj and added -I flags to cord make targets (thanks to +Gary Leavens). +* GC_try_to_collect was broken with the non-incremental collector. +* gc_cleanup destructors could pass the wrong address to +GC_register_finalizer_ignore_self in the presence of multiple +inheritance (thanks to Darrell Schiebel). +* Changed PowerPC Linux stack finding code. + + +== [4.13] 1999-02-19 == + +* Fixed a crucial bug in the Watcom port. There was a redundant declaration +of GC_push_one in gc_priv.h. +* Added FINALIZE_ON_DEMAND. +* Fixed some pre-ANSI cc problems in test.c. +* Removed getpagesize() use for Solaris. It seems to be missing in one +or two versions. +* Fixed bool handling for SPARCCompiler version 4.2. +* Fixed some files in include that had gotten unlinked from the main +copy. +* Some RS/6000 fixes (missing casts). (Thanks to Toralf Foerster.) +* Fixed several problems in GC_debug_realloc, affecting mostly the +FIND_LEAK case. +* GC_exclude_static_roots contained a buggy unsigned comparison to +terminate a loop (thanks to Wilson Ho). +* CORD_str failed if the substring occurred at the last possible position. +(Only affects cord users.) +* Fixed Linux code to deal with RedHat 5.0 and integrated Peter Bigot's +os_dep.c code for dealing with various Linux versions. +* Added workaround for Irix pthreads sigaction bug and possible signal +misdirection problems. + + +== [4.13alpha3] 1998-12-08 == + +* Fixed MSWIN32 recognition test, which interfered with cygwin. +* Removed unnecessary gc_watcom.asm from distribution. Removed +some obsolete README.win32 text. +* Added Alpha Linux incremental GC support (thanks to Philipp Tomsich +for code for retrieving the fault address in a signal handler). +Changed Linux signal handler context argument to be a pointer. +* Took care of some new warnings generated by the 7.3 SGI compiler. +* Integrated Phillip Musumeci's FreeBSD/ELF fixes. +* -DIRIX_THREADS was broken with the -o32 ABI (typo in gc_priv.h). + + +== [4.13alpha2] 1998-08-08 == + +* Fixed more Linux threads problems. +* Changed default GC_free_space_divisor to 3 with new large block allocation +(thanks to Matthew Flatt for some measurements that suggest the old +value sometimes favors space too much over time). +* More CYGWIN32 fixes. +* Integrated Tyson Dowd's Linux-M68K port. +* Minor HP PA and DEC UNIX fixes from Fergus Henderson. +* Integrated Christoffe Raffali's Linux-SPARC changes. +* Allowed for one more GC fixup iteration after a full GC in incremental +mode. Some quick measurements suggested that this significantly +reduces pause times even with smaller GC_RATE values. +* Moved some more GC data structures into GC_arrays. This decreases +pause times and GC overhead, but makes debugging slightly less convenient. +* Fixed namespace pollution problem ("excl_table"). +* Made GC_incremental a constant for -DSMALL_CONFIG, hopefully shrinking +that slightly. +* Added some win32 threads fixes. +* Integrated Ivan Demakov and David Stes' Watcom fixes. +* Various other minor fixes contributed by many people. +* Renamed config.h to gcconfig.h, since config.h tends to be used for +many other things. +* Integrated Matthew Flatt's support for 68K MacOS "far globals". +* Fixed up some of the dynamic library Makefile targets for consistency +across platforms. +* Fixed a USE_MMAP typo that caused out-of-memory handling to fail +on Solaris. +* Added code to test.c to test thread creation a bit more. +* Integrated GC_win32_free_heap (as suggested by Ivan Demakov). +* Fixed Solaris 2.7 stack base finding problem. (This may actually +have been done in an earlier alpha release.) + + +== [4.13alpha1] 1998-02-17 == + +* Changed RS6000 STACKBOTTOM. +* Integrated Patrick Beard's Mac changes. +* Alpha1 didn't compile on Irix m.n, m < 6. +* Replaced Makefile.dj with a new one from Gary Leavens. +* Added Andrew Stitcher's changes to support SCO OpenServer. +* Added PRINT_BLACK_LIST, to allow debugging of high densities of false +pointers. +* Added code to debug allocator to keep track of return address +in GC_malloc caller, thus giving a bit more context. +* Changed default behavior of large block allocator to more +aggressively avoid fragmentation. This is likely to slow down the +collector when it succeeds at reducing space cost. +* Integrated Fergus Henderson's CYGWIN32 changes. They are untested, +but needed for newer versions. +* USE_MMAP had some serious bugs. This caused the collector to fail +consistently on Solaris with -DSMALL_CONFIG. +* Added Linux threads support (thanks to Fergus Henderson). + + +== [4.12] 1997-08-26 == + +* Fixed ElfW definition in dyn_load.c. +This prevented the dynamic library support from compiling on some +older ELF Linux systems. +* Fixed UTS4 port (which I apparently mangled during the integration). +(Thanks to Alistair Crooks.) +* "Make C++" failed on Suns with SC4.0, due to a problem with "bool". +Fixed in gc_priv.h. +* Added more pieces for GNU win32 (thanks to Timothy N. Newsham). +The current state of things should suffice for at least some +applications. +* Changed the out of memory retry count handling. (This matters only +if GC_max_retries > 0, which is no longer the default.) +* If a /proc read failed repeatedly, GC_written_pages was not updated +correctly (thanks to Peter Chubb for diagnosing this). +* Under unlikely circumstances, the allocator could infinite loop in +an out of memory situation (thanks to Kenjiro Taura for +identifying the problem and supplying a fix). +* Fixed a syntactic error in the DJGPP code. Also fixed a test program +problem with DJGPP (thanks to Fergus Henderson and Peter Monks). +* Atomic uncollectible objects were not treated correctly by the +incremental collector. This resulted in weird log statistics and +occasional performance problems (thanks to Peter Chubb for pointing this out). +* Fixed some problems resulting from compilers that don't define +__STDC__. In this case void * and char * were used inconsistently +in some cases. (Void * should not have been used at all. If +you have an ANSI superset compiler that does not define __STDC__, +please compile with -D__STDC__=0. Thanks to Manuel Serrano and others +for pointing out the problem.) +* Fixed a compilation problem on Irix with -n32 and -DIRIX_THREADS. +Also fixed some other IRIX_THREADS problems which may or may not have +had observable symptoms. +* Fixed an HP PA compilation problem in dyn_load.c (thanks to +Philippe Queinnec). +* SEGV fault handlers sometimes did not get reset correctly (thanks +to David Pickens). +* Added a fix for SOLARIS_THREADS on Intel (thanks to David Pickens). +This probably needs more work to become functional. +* Fixed struct sigcontext_struct in os_dep.c for compilation under +Linux 2.1.X (thanks to Fergus Henderson). +* Changed the DJGPP STACKBOTTOM and DATASTART values to those ones suggested +(by Kristian Kristensen). These may still not be right, but it is +it is likely to work more often than what was there before. They may +even be exactly right. +* Added a #include to test_cpp.cc. This appears to help +with HP/UX and gcc (thanks to Assar Westerlund). +* Version 4.11 failed to run in incremental mode on recent 64-bit Irix +kernels. This was a problem related to page unaligned heap segments. +Changed the code to page align heap sections on all platforms. +(I had mistakenly identified this as a kernel problem earlier. +It was not.) +* Version 4.11 did not make allocated storage executable, except on +one or two platforms, due to a bug in a #if test (thanks to David Grove +for pointing this out). +* Added sparc_sunos4_mach_dep.s to support Sun's compilers under SunOS4. +* Added GC_exclude_static_roots. +* Fixed the object size mapping algorithm. This shouldn't matter, +but the old code was ugly. +* Heap checking code could die if one of the allocated objects was +larger than its base address. (Unsigned underflow problem. Thanks +to Clay Spence for isolating the problem.) +* Added RS6000 (AIX) dynamic library support and fixed STACK_BOTTOM (thanks +to Fred Stearns). +* Added Fergus Henderson's patches for improved robustness with large +heaps and lots of blacklisting. +* Added Peter Chubb's changes to support Solaris Pthreads, to support +MMAP allocation in Solaris, to allow Solaris to find dynamic libraries +through /proc, to add malloc_typed_ignore_off_page, and a few other +minor features and bug fixes. +* The Solaris 2 port should not use sbrk. I received confirmation from +Sun that the use of sbrk and malloc in the same program is not +supported. The collector now defines USE_MMAP by default on Solaris. +* Replaced the djgpp makefile with Gary Leavens' version. +* Fixed MSWIN32 detection test. +* Added Fergus Henderson's patches to allow putting the collector into +a DLL under GNU win32. +* Added Ivan V. Demakov's port to Watcom C on X86. +* Added Ian Piumarta's Linux/PowerPC port. +* Added PointerFreeGC to the placement options in gc_cpp.h (suggested by +Brian Burton). This is of course unsafe, and may be controversial. +On the other hand, it seems to be needed often enough that it's worth +adding as a standard facility. +* Add Lars Farm's suggestions on building the collector for MacOS. + + +== [4.12alpha2] == + +(Changes not specified.) + + +== [4.11] 1996-12-03 == + +* Rationalized (hopefully) GC_try_to_collect in an incremental collection +environment. It appeared to not handle a call while a collection was in +progress, and was otherwise too conservative. +* Merged GC_reclaim_or_delete_all into GC_reclaim_all to get rid of some +code. +* Added Patrick Beard's Mac fixes, with substantial completely untested +modifications. +* Fixed the MPROTECT_VDB code to deal with large pages and imprecise +fault addresses (as on an UltraSPARC running Solaris 2.5). Note that this +was not a problem in the default configuration, which uses PROC_VDB. +* The DEC Alpha assembly code needed to restore $gp between calls (thanks to +Fergus Henderson for tracking this down and supplying a patch). +* The write command for "de" was completely broken for large files. +I used the easiest portable fix, which involved changing the semantics +so that f.new is written instead of overwriting f. That's safer anyway. +* Added README.solaris2 with a discussion of the possible problems of +mixing the collector's sbrk allocation with malloc/realloc. +* Changed the data segment starting address for SGI machines. The +old code failed under IRIX6. +* Required double word alignment for MIPS. +* Various minor fixes to remove warnings. +* Attempted to fix some Solaris threads problems (reported by Zhiying Chen). +In particular, the collector could try to fork a thread with the +world stopped as part of GC_thr_init. It also failed to deal with +the case in which the original thread terminated before the whole +process did. +* Added -DNO_EXECUTE_PERMISSION. This has a major performance impact +on the incremental collector under Irix, and perhaps under other +operating systems. +* Added some code to support allocating the heap with mmap. This may +be preferable under some circumstances. +* Integrated dynamic library support for HP (thanks to Knut Tvedten). +* Integrated James Clark's win32 threads support, and made a number +of changes to it (many of which suggested by Pontus Rydin). This is still +not 100% solid. +* Integrated Alistair G. Crooks' support for UTS4 running on an Amdahl +370-class machine. +* Fixed a serious bug in explicitly typed allocation. Objects requiring +large descriptors where handled in a way that usually resulted in +a segmentation fault in the marker (thanks to Jeremy Fitzhardinge +for helping to track this down). +* Added partial support for GNU win32 development (thanks to +Fergus Henderson). +* Added optional support for Java-style finalization semantics (thanks to +Patrick Bridges). This is recommended only for Java implementations. +* GC_malloc_uncollectable faulted instead of returning 0 when out of +memory (thanks to Daniel R. Grayson for noticing). +* Calls to GC_base before the collector was initialized failed on a +DEC Alpha (thanks to Matthew Flatt). +* Added base pointer checking to GC_REGISTER_FINALIZER in debugging mode +(thanks to Jeremy Fitzhardinge). +* GC_debug_realloc failed for uncollectible objects (thanks to +Jeremy Fitzhardinge). +* Explicitly typed allocation could crash if it ran out of memory (thanks to +Jeremy Fitzhardinge). +* Added minimal support for a DEC Alpha running Linux. +* Fixed a problem with allocation of objects whose size overflowed +ptrdiff_t. (This now fails unconditionally, as it should.) +* Added the beginning of Irix pthread support. +* Integrated Xiaokun Zhu's fixes for djgpp 2.01. +* Added SGI-style STL allocator support (gc_alloc.h). +* Fixed a serious bug in README.solaris2. +Multi-threaded programs must include +gc.h with SOLARIS_THREADS defined. +* Changed GC_free so it actually deallocates uncollectible objects (thanks +to Peter Chubb for pointing out the problem). +* Added Linux ELF support for dynamic libraries (thanks to Patrick Bridges). +* Changed the Borland cc configuration so that the assembler is not +required. +* Fixed a bug in the C++ test that caused it to fail in 64-bit +environments. + + +== [4.10t3] 1996-11-18 == + +Some changes related to threads support. + + +== [4.10] 1996-02-19 == + +* Fixed a typo around a call to GC_collect_or_expand in alloc.c. It broke +handling of out of memory. (Thanks to Patrick C. Beard for noticing.) + + +== [4.9] 1996-02-12 == + +* More README.debugging fixes. +* Objects ready for finalization, but not finalized in the same GC +cycle, could be prematurely collected. This occasionally happened +in test_cpp. +* Too little memory was obtained from the system for very large +objects. That could cause a heap explosion if these objects were +not contiguous (e.g. under PCR), and too much of them was blacklisted. +* Due to an improper initialization, the collector was too hesitant to +allocate blacklisted objects immediately after system startup. +* Moved GC_arrays from the data into the bss segment by not explicitly +initializing it to zero. This significantly +reduces the size of executables, and probably avoids some disk accesses +on program startup. It's conceivable that it might break a port that I +didn't test. +* Fixed EMX_MAKEFILE to reflect the gc_c++.h to gc_cpp.h renaming which +occurred a while ago. + + +== [4.8] 1995-11-20 == + +* Changed a "comment" in a MacOS specific part of mach_dep.c that caused +gcc to fail on other platforms. + + +== [4.7] 1995-11-18 == + +* Fixed some compilation problems with -DCHECKSUMS (thanks to Ian Searle). +* Updated some Mac specific files (to synchronize with Patrick C. Beard). +* Fixed a serious bug for machines with non-word-aligned pointers. +(Thanks to Patrick C. Beard for pointing out the problem. The collector +should fail almost any conceivable test immediately on such machines.) + + +== [4.6] 1995-11-09 == + +* Added Linux ELF support (thanks to Arrigo Triulzi). +* GC_base crashed if it was called before any other GC_ routines. +This could happen if a gc_cleanup object was allocated outside the heap +before any heap allocation. +* The heap expansion heuristic was not stable if all objects had finalization +enabled. Fixed finalize.c to count memory in finalization queue and +avoid explicit deallocation. Changed alloc.c to also consider this count. +(This is still not recommended. It's expensive if nothing else. Thanks +to John Ellis for pointing this out.) +* GC_malloc_uncollectable(0) was broken (thanks to Phong Vo for pointing +this out). +* The collector didn't compile under Linux 1.3.X (thanks to Fred Gilham for +pointing this out). The current workaround is ugly, but expected to be +temporary. +* Fixed a formatting problem for SPARC stack traces. +* Fixed some '=='s in os_dep.c that should have been assignments. +Fortunately these were in code that should never be executed anyway (thanks +to Fergus Henderson). +* Fixed the heap block allocator to only drop blacklisted blocks in small +chunks. Made BL_LIMIT self adjusting. (Both of these were in response +to heap growth observed by Paul Graham.) +* Fixed the Metrowerks/68K Mac code to also mark from a6 (thanks to +Patrick C. Beard). +* Significantly updated README.debugging. +* Fixed some problems with longjmps out of signal handlers, especially under +Solaris. Added a workaround for the fact that siglongjmp doesn't appear to +do the right thing with -lthread under Solaris. +* Added MSDOS/djgpp port (thanks to Mitch Harris). +* Added "make reserved_namespace" and "make user_namespace". The +first renames ALL "GC_xxx" identifiers as "_GC_xxx". The second is the +inverse transformation. Note that doing this is guaranteed to break all +clients written for the other names. +* descriptor field for kind NORMAL in GC_obj_kinds with ADD_BYTE_AT_END +defined should be -ALIGNMENT not WORDS_TO_BYTES(-1). This is +a serious bug on machines with pointer alignment of less than a word. +* GC_ignore_self_finalize_mark_proc didn't handle pointers to very near the +end of the object correctly. Caused failures of the C++ test on a DEC Alpha +with g++. +* gc_inl.h still had problems. Partially fixed. Added warnings at the +beginning to hopefully specify the remaining dangers. +* Added DATAEND definition to config.h. +* Fixed some of the .h file organization. Fixed "make floppy". + + +== [4.5] 1995-06-14 == + +* Fixed many minor and one major README bugs (thanks to Franklin Chen +for pointing out many of them). +* Fixed ALPHA/OSF/1 dynamic library support (thanks to Jonathan Bachrach). +* Added incremental GC support (MPROTECT_VDB) for Linux (with some +help from Bruno Haible). +* Altered SPARC recognition tests in gc.h and config.h (mostly as +suggested by Fergus Henderson). +* Added basic incremental GC support for win32, as implemented by +Windows NT and Windows 95. GC_enable_incremental is a no-op +under win32s, which doesn't implement enough of the VM interface. +* Added -DLARGE_CONFIG. +* Fixed GC_..._ignore_off_page to also function without +-DALL_INTERIOR_POINTERS. +* (Hopefully) fixed RS/6000 port. (Only the test was broken.) +* Fixed a performance bug in the non-incremental collector running +on machines supporting incremental collection with MPROTECT_VDB +(e.g. SunOS 4, DEC AXP). This turned into a correctness bug under +win32s with win32 incremental collection. (Not all memory protection +was disabled.) +* Fixed some ppcr related bit rot. +* Caused dynamic libraries to be unregistered before re-registering. +The old way turned out to be a performance bug on some machines. +* GC_root_size was not properly maintained under MSWIN32. +* Added -DNO_DEBUGGING and GC_dump. +* Fixed a couple of bugs arising with SOLARIS_THREADS + +REDIRECT_MALLOC. +* Added NetBSD/M68K port (thanks to Peter Seebach). +* Fixed a serious realloc bug. For certain object sizes, the collector +wouldn't scan the expanded part of the object. (Thanks to Clay Spence +for noticing the problem, and helping me to track it down.) + + +== [4.4] 1995-02-18 == + +* ASM_CLEAR_CODE was erroneously defined for HP +PA machines, resulting in a compile error. +* Fixed OS/2 Makefile to create a library (thanks to Mark Boulter). +* Gc_cleanup objects didn't work if they were created on +the stack. Fixed. +* One copy of Gc_cpp.h in the distribution was out of +synch, and failed to document some known compiler +problems with explicit destructor invocation. Partially +fixed. There are probably other compilers on which +gc_cleanup is miscompiled. +* Fixed Makefile to pass C compiler flags to C++ compiler. +* Added Mac fixes. +* Fixed os_dep.c to work around what appears to be +a new and different VirtualQuery bug under newer +versions of win32S. +* GC_non_gc_bytes was not correctly maintained by +GC_free. Fixed (thanks to James Clark). +* Added GC_set_max_heap_size. +* Changed allocation code to ignore blacklisting if it is preventing +use of a very large block of memory. This has the advantage +that naive code allocating very large objects is much more +likely to work. The downside is you might no +longer find out that such code should really use +GC_malloc_ignore_off_page. +* Changed GC_printf under win32 to close and reopen the file +between calls. FAT file systems otherwise make the log file +useless for debugging. +* Added GC_try_to_collect and GC_get_bytes_since_gc. These +allow starting an abortable collection during idle times. +This facility does not require special OS support. (Thanks to +Michael Spertus of Geodesic Systems for suggesting this. It was +actually an easy addition. Kumar Srikantan previously added a similar +facility to a now ancient version of the collector. At the time +this was much harder, and the result was less convincing.) +* Added some support for the Borland development environment (thanks +to John Ellis and Michael Spertus). +* Removed a misfeature from checksums.c that caused unexpected +heap growth (thanks to Scott Schwartz). +* Changed finalize.c to call WARN if it encounters a finalization cycle. +WARN is defined in gc_priv.h to write a message, usually to stdout. +In many environments, this may be inappropriate. +* Renamed NO_PARAMS in gc.h to GC_NO_PARAMS, thus adhering to my own +naming convention. +* Added GC_set_warn_proc to intercept warnings. +* Fixed Amiga port (thanks to Michel Schinz). +* Fixed a bug in mark.c that could result in an access to unmapped +memory from GC_mark_from_mark_stack on machines with unaligned +pointers. +* Fixed a win32 specific performance bug that could result in scanning of +objects allocated with the system malloc. +* Added REDIRECT_MALLOC. + + +== [4.3] 1994-12-23 == + +* Fixed SPARC alignment problem with GC_DEBUG. +* Fixed Solaris threads /proc workaround. The real +problem was an interaction with mprotect. +* Incorporated fix from Patrick Beard for gc_c++.h (now gc_cpp.h). +* Slightly improved allocator space utilization by +fixing the GC_size_map mechanism. +* Integrated some Sony News and MIPS RISCos 4.51 +patches (thanks to Nobuyuki Hikichi at Software Research Associates, +Inc., Japan). +* Fixed HP_PA alignment problem (thanks to Brian F. Dennis). +* Added GC_same_obj and friends. Changed GC_base +to return 0 for pointers past the end of large objects. +Improved GC_base performance with ALL_INTERIOR_POINTERS +on machines with a slow integer mod operation. +Added GC_PTR_ADD, GC_PTR_STORE, etc. to prepare +for preprocessor. +* changed the default on most UNIX machines to be that +signals are not disabled during critical GC operations. +This is still ANSI-conforming, though somewhat dangerous +in the presence of signal handlers. But the performance +cost of the alternative is sometimes problematic. +Can be changed back with a minor Makefile edit. +* renamed IS_STRING in gc.h, to CORD_IS_STRING, thus +following my own naming convention. Added the function +CORD_to_const_char_star. +* Fixed a gross bug in GC_finalize. Symptom: occasional +address faults in that function (thanks to Anselm Baird-Smith). +* Added port to ICL DRS6000 running DRS/NX. Restructured +things a bit to factor out common code, and remove obsolete +code. Collector should now run under SUNOS5 with either +mprotect or /proc dirty bits. (Thanks to Douglas Steel.) +* More bug fixes and workarounds for Solaris 2.X. (These were +mostly related to putting the collector in a dynamic library, +which didn't really work before. Also SOLARIS_THREADS +didn't interact well with dl_open.) (Thanks to Brian Lewis.) +* Fixed a serious performance bug on the DEC Alpha. The text +segment was getting registered as part of the root set. +(Amazingly, the result was still fast enough that the bug +was not conspicuous.) The fix works on OSF/1, version 1.3. +Hopefully it also works on other versions of OSF/1 ... +* Fixed a bug in GC_clear_roots. +* Fixed a bug in GC_generic_malloc_words_small that broke +gc_inl.h (reported by Antoine de Maricourt). +* Fixed some problems with cord/de under Linux. +* Fixed some cord problems, notably with CORD_riter4. +* Added DG/UX port (thanks to Ben A. Mesander). +* Added finalization registration routines with weaker ordering +constraints. (This is necessary for C++ finalization with +multiple inheritance, since the compiler often adds self-cycles.) +* Filled the holes in the SCO port (thanks to Michael Arnoldus). +* Completely rewritten the documentation in the interface gc_c++.h +(later renamed gc_cpp.h) making it both clearer and more precise (done by +John Ellis). +* The definition of accessibility now ignores pointers from a finalizable +object (an object with a clean-up function) to itself (done by John Ellis). +This allows objects with virtual base classes to be finalizable by the +collector. Compilers typically implement virtual base classes using +pointers from an object to itself, which under the old definition of +accessibility prevented objects with virtual base classes from ever +being collected or finalized. +* gc_cleanup now includes gc as a virtual base. This was enabled by +the change in the definition of accessibility (by John Ellis). +* Added support for operator new[] (by John Ellis). Since most compilers +don't yet support operator new[], it is conditionalized on +-DOPERATOR_NEW_ARRAY. The code is untested, but its trivial and looks +correct. +* The test program test_gc_c++ (later renamed test_cpp.cc) +tries to test for the C++-specific functionality not tested by the +other programs. +* Added unistd.h include to misc.c. (Needed for ppcr.) +* Added PowerMac port (thanks to Patrick C. Beard). +* Fixed "srcdir"-related Makefile problems. Changed things so +that all externally visible include files always appear in the +include subdirectory of the source. Made gc.h directly +includable from C++ code (thanks to Per Bothner). +* Changed Intel code to also mark from ebp (thanks to Kevin Warne). +* Renamed C++ related files so they could live in a FAT +file system (thanks to Charles Fiterman). +* Changed Windows NT Makefile to include C++ support in +gc.lib. Added C++ test as Makefile target. + + +== [4.2] 1994-08-03 == + +* Multiple bug fixes/workarounds in the Solaris threads version. +(It occasionally failed to locate some register contents for +marking. It also turns out that thr_suspend and friends are +unreliable in Solaris 2.3. Dirty bit reads appear +to be unreliable under some weird +circumstances. My stack marking code +contained a serious performance bug. The new code is +extremely defensive, and has not failed in several CPU +hours of testing. But no guarantees ...) +* Added MacOS support. (Thanks to Patrick C. Beard. +David Chase suggested several improvements.) +* Fixed several syntactic bugs in gc_c++.h and friends. (These +didn't bother g++, but did bother most other compilers.) +Fixed gc_c++.h finalization interface. +* 64 bit alignment for allocated objects was not guaranteed in a +few cases in which it should have been. +* Added GC_malloc_atomic_ignore_off_page. +* Added GC_collect_a_little. +* Added some prototypes to gc.h. +* Some other minor bug fixes (notably in Makefile). +* Fixed OS/2 / EMX port (thanks to Ari Huttunen). +* Fixed AmigaDOS port (thanks to Michel Schinz). +* Fixed the DATASTART definition under Solaris. There +was a 1 in 16K chance of the collector missing the first +64K of static data (and thus crashing). +* Fixed some blatant anachronisms in the README file. +* Fixed PCR-Makefile for upcoming PPCR release. + + +== [4.1] 1994-05-20 == + +* Changed finalization implementation to guarantee that +finalization procedures are called outside of the allocation +lock, making direct use of the interface a little less dangerous. +MAY BREAK EXISTING CLIENTS that assume finalizers +are protected by a lock. Since there seem to be few multi-threaded +clients that use finalization, this is hopefully not much of +a problem. +* Fixed a gross bug in CORD_prev. +* Fixed a bug in blacklst.c that could result in unbounded +heap growth during startup on machines that do not clear +memory obtained from the OS (e.g. win32S). +* Ported de editor to win32/win32S. (This is now the only +version with a mouse-sensitive UI. Thanks to Rob Haack for the +implementation based on the generic Windows application template.) +* Added GC_malloc_ignore_off_page to allocate large arrays +in the presence of ALL_INTERIOR_POINTERS. +* Changed GC_call_with_alloc_lock to not disable signals in +the single-threaded case. +* Reduced retry count in GC_collect_or_expand for garbage +collecting when out of memory. +* Made uncollectible allocations bypass black-listing, as they +should. +* Fixed a bug in typed_test in test.c that could cause (legitimate) +GC crashes. +* Fixed some potential synchronization problems in finalize.c +* Fixed a real locking problem in typd_mlc.c. +* Worked around an AIX 3.2 compiler feature that results in +out of bounds memory references. +* Partially worked around an IRIX5.2 beta problem (which may +or may not persist to the final release). +* Fixed a bug in the heap integrity checking code that could +result in explicitly deallocated objects being identified as +smashed. Fixed a bug in the dbg_mlc stack saving code +that caused old argument pointers to be considered live. +* Fixed a bug in CORD_ncmp (and hence CORD_str). +* Repaired the OS2 port, which had suffered from bit rot +in 4.0. Worked around what appears to be CSet/2 V1.0 +optimizer bug. +* Fixed a Makefile bug for target "c++". + + +== [4.0] 1994-04-07 == + +* Added support for Solaris threads (which was possible +only by reimplementing some fraction of Solaris threads, +since Sun doesn't currently make the thread debugging +interface available). +* Added non-threads win32 and win32S support. +* (Grudgingly, with suitable muttering of obscenities) renamed +files so that the collector distribution could live on a FAT +file system. Files that are guaranteed to be useless on +a PC still have long names. Gc_inline.h and gc_private.h +still exist, but now just include gc_inl.h and gc_priv.h. +* Fixed a really obscure bug in finalization that could cause +undetected mark stack overflows. (I would be surprised if +any real code ever tickled this one.) +* Changed finalization code to dynamically resize the hash +tables it maintains. (This probably does not matter for well- +-written code. It no doubt does for C++ code that overuses +destructors.) +* Added typed allocation primitives. Rewrote the marker to +accommodate them with more reasonable efficiency. This +change should also speed up marking for GC_malloc allocated +objects a little. See gc_typed.h for new primitives. (Thanks to +Zhong Shao performed much of the experimentation that led to the +current typed allocation facility.) +* Improved debugging facilities slightly. Allocation time +stack traces are now kept by default on SPARC/SUNOS4. (Thanks to +Scott Schwartz.) +* Added better support for small heap applications. +* Significantly extended cord package. Fixed a bug in the +implementation of lazily read files. Printf and friends now +have cord variants. Cord traversals are a bit faster. +* Made ALL_INTERIOR_POINTERS recognition the default. +* Fixed de so that it can run in constant space, independent +of file size. Added simple string searching to cords and de. +* Added the Hull-Ellis C++ interface (supplied by Jesse Hull and John Ellis). +* Added dynamic library support for OSF/1 (thanks to Alan Dosser and +Tim Bingham at DEC). +* Changed argument to GC_expand_hp to be expressed +in units of bytes instead of heap blocks. (Necessary +since the heap block size now varies depending on +configuration. The old version was never very clean.) +* Added GC_get_heap_size(). The previous "equivalent" +was broken. +* Restructured the Makefile a bit. +* Added FreeBSD port (provided by Jeffrey Hsu). + + +== [3.7] 1994-03-15 == + +* Added a workaround for an HP/UX compiler bug. +* Fixed another stack clearing performance bug. Reworked +that code once more. + + +== [3.6] 1994-01-14 == + +* fixed a bug in the mark stack growth code that was introduced +in 3.4. +* fixed Makefile to work around DEC AXP compiler tail recursion +bug. + + +== [3.5] == + +* Minor collections now mark from roots only once, if that +doesn't cause an excessive pause. +* The stack clearing heuristic was refined to prevent anomalies +with very heavily recursive programs and sparse stacks. +* Fixed a bug that prevented mark stack growth in some cases. +GC_objects_are_marked should be set to TRUE after a call +to GC_push_roots and as part of GC_push_marked, since +both can now set mark bits. I think this is only a performance +bug, but I wouldn't bet on it. It's certainly very hard to argue +that the old version was correct. +* Fixed an incremental collection bug that prevented it from +working at all when HBLKSIZE != getpagesize() +* Changed dynamic_loading.c to include gc_priv.h before testing +DYNAMIC_LOADING. SunOS dynamic library scanning +must have been broken in 3.4. +* Object size rounding now adapts to program behavior. +* Added a workaround (provided by Manuel Serrano and +colleagues) to a long-standing SunOS 4.X (and 3.X) ld bug +that I had incorrectly assumed to have been squished. +The collector was broken if the text segment size was within +32 bytes of a multiple of 8K bytes, and if the beginning of +the data segment contained interesting roots. The workaround +assumes a demand-loadable executable. The original may have +have "worked" in some other cases. +* Added dynamic library support under IRIX5. +* Added support for EMX under OS/2 (thanks to Ari Huttunen). +* Added support of Motorola 88K processor running CX/UX (by Brent Benson). + + +== [3.4] == + +* Fixed a performance bug in GC_realloc. +* Updated the amiga port. +* Added NetBSD and 386BSD ports (supplied by Alistair G. Crooks). +* Added cord library. +* Added trivial performance enhancement for +ALL_INTERIOR_POINTERS (do not scan last word). + + +== [3.3] 1993-10-02 == + +* PCR-specific bugs (thanks to Neil Sharman). +* Missing locking in GC_free, redundant FASTUNLOCK +in GC_malloc_stubborn, and 2 bugs in +GC_unregister_disappearing_link (pointed out by Neil Sharman). +* Common symbols allocated by the SunOS4.X dynamic loader +were not included in the root set. +* Bug in GC_finalize (reported by Brian Beuning and Alan Dosser). +* Merged Amiga port from Jesper Peterson (untested). +* Merged NeXT port from Thomas Funke (significantly +modified and untested). (Also thanks to Brian D. Carlstrom for +the supplied the NeXT ports.) + + +== [3.2] == + +Fixed a serious and not entirely repeatable bug in +the incremental collector. It appeared only when dirty bit info +on the roots was available, which is normally only under Solaris. +It also added GC_general_register_disappearing_link, and some +testing code. Interface.c disappeared. + + +== [3.1] == + +* A workaround for a SunOS 4.X SPARC C compiler +misfeature that caused problems when the collector was turned into +a dynamic library. +* A fix for a bug in GC_base that could result in a memory fault. +* A fix for a performance bug (and several other misfeatures) pointed +out by Dave Detlefs and Alan Dosser. +* Use of dirty bit information for static data under Solaris 2.X. +* DEC Alpha/OSF1 support (thanks to Alan Dosser). +* Incremental collection on more platforms. +* A more refined heap expansion policy. Less space usage by default. +* Various minor enhancements to reduce space usage, and to reduce +the amount of memory scanned by the collector. +* Uncollectible allocation without per object overhead. +* More conscientious handling of out-of-memory conditions. +* Fixed a bug in debugging stubborn allocation. +* Fixed a bug that resulted in occasional erroneous reporting of smashed +objects with debugging allocation. +* Fixed bogus leak reports of size 4096 blocks with FIND_LEAK. + + +== [3.0] == + +Added generational/incremental collection and stubborn objects. + + +== [2.6] 1993-04-27 == + +(Changes not specified.) + + +== [2.5] == + +* Removed an explicit call to exit(1) +* Fixed calls to GC_printf and GC_err_printf, so the correct number of +arguments are always supplied. The OS/2 C compiler gets confused if +the number of actuals and the number of formals differ. (ANSI C +doesn't require this to work. The ANSI sanctioned way of doing things +causes too many compatibility problems.) + + +== [2.4] 1993-01-26 == + +Added GC_free_space_divisor as a tuning knob, added +support for OS/2 and linux, and fixed the following bugs: +* On machines with unaligned pointers (e.g. Sun 3), every 128th word could +fail to be considered for marking. +* Dynamic_load.c erroneously added 4 bytes to the length of the data and +bss sections of the dynamic library. This could result in a bad memory +reference if the actual length was a multiple of a page. (Observed on +Sun 3. Can probably also happen on a Sun 4.) +(Thanks to Robert Brazile for pointing out that the Sun 3 version +was broken. Dynamic library handling is still broken on Sun 3s +under 4.1.1U1, but apparently not 4.1.1. If you have such a machine, +use -Bstatic.) + + +== [2.3] == + +* Added ALL_INTERIOR_POINTERS. +* Missing declaration of etext in the A/UX version. +* Some PCR root-finding problems. +* Blacklisting was not 100% effective, because the plausible future +heap bounds were being miscalculated. +* GC_realloc didn't handle out-of-memory correctly. +* GC_base could return a nonzero value for addresses inside free blocks. +* test.c wasn't really thread safe, and could erroneously report failure +in a multi-threaded environment. (The locking primitives need to be +replaced for other threads packages.) +* GC_CONS was thoroughly broken. +* On a SPARC with dynamic linking, signals stayed disabled while the +client code was running (thanks to Manuel Serrano). + + +== [2.2] == + +* GC_realloc could fail to extend the size of the object for certain large +object sizes. +* A blatant subscript range error in GC_printf, which unfortunately +wasn't exercised on machines with sufficient stack alignment constraints. +* GC_register_displacement did the wrong thing if it was called after +any allocation had taken place. +* The leak finding code would eventually break after 2048 byte +byte objects leaked. +* interface.c didn't compile. +* The heap size remained much too small for large stacks. +* The stack clearing code behaved badly for large stacks, and perhaps +on HP/PA machines. + + +== [2.1] == + +* The first stable version since 1.9. +* Added support for PPCR. + + +== [2.0] == + +* Introduced a consistent naming convention for collector +routines and added support for registering dynamic library data segments +in the standard mark_roots.c (original code supporting the SunOS dynamic +loader provided by Bill Janssen). Most of the data structures were revamped. +The treatment of interior pointers was completely changed. Finalization +was added. Support for locking was added. Object kinds were added. +We added a black listing facility to avoid allocating at addresses known +to occur as integers somewhere in the address space. Much of this +was accomplished by adapting ideas and code from the PCR collector. +The test program was changed and expanded. + + +== [1.9] 1992-01-29 == + +* fixed a major bug in gc_realloc. + + +== [1.8] == + +* added ULTRIX support in gc_private.h. (Robert Brazile originally supplied +the ULTRIX code. Alan Dosser and Regis Cridlig subsequently provided updates +and information on variation between ULTRIX systems.) + + +== [1.5] == + +* ensure 8 byte alignment for objects allocated on a sparc based machine. + + +== [1.4] == + +* Does not use compile time determined values +for the stack base. This no longer works on Sun 3s, since Sun 3/80s use +a different stack base. We now use a straightforward heuristic on all +machines on which it is known to work (incl. Sun 3s) and compile-time +determined values for the rest. There should really be library calls +to determine such values. + + +== [1.3] == + +* Fixed spurious +assembly language assignments to TMP_SP. Only the assignment in the PC/RT +code is necessary. On other machines, with certain compiler options, +the assignments can lead to an unsaved register being overwritten. +Known to cause problems under SunOS 3.5 WITHOUT the -O option. (With +-O the compiler recognizes it as dead code. It probably shouldn't, +but that's another story.) +The SPARC-specific code was originally contributed by Mark Weiser. +The Encore Multimax modifications were supplied by Kevin Kenny. +The adaptation to the IBM PC/RT is largely +due to Vernon Lee, on machines made available to Rice by IBM. +Much of the HP specific code and a number of good suggestions for improving +the generic code are due to Walter Underwood. +Parag Patel supplied the A/UX code. +Manuel Serrano supplied linux and Sony News specific code. diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/Makefile.am b/unity-2019.4.24f1-mbe/external/bdwgc/Makefile.am new file mode 100644 index 000000000..8f7e8e9fa --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/Makefile.am @@ -0,0 +1,239 @@ +# Copyright (c) 1999-2001 by Red Hat, Inc. All rights reserved. +# +# THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED +# OR IMPLIED. ANY USE IS AT YOUR OWN RISK. +# +# Permission is hereby granted to use or copy this program +# for any purpose, provided the above notices are retained on all copies. +# Permission to modify the code and to distribute modified code is granted, +# provided the above notices are retained, and a notice that the code was +# modified is included with the above copyright notice. + +## Process this file with automake to produce Makefile.in. + +# Info (current:revision:age) for the Libtool versioning system. +# These numbers should be updated at most once just before the release, +# and, optionally, at most once during the development (after the release). +LIBGC_VER_INFO = 5:0:4 +LIBGCCPP_VER_INFO = 4:0:3 + +## FIXME: `make distcheck' in this directory will not currently work. +## This is most likely to the explicit flags passed to submakes. + +# We currently use the source files directly from libatomic_ops, if we +# use the internal version. This is done since libatomic_ops doesn't +# use libtool, since it has no real use for it. But that seems to make +# it hard to use either the resulting object files or libraries. +# Thus there seems to be no real reason to recursively build in the +# libatomic_ops directory. +# if USE_INTERNAL_LIBATOMICS_OPS +# SUBDIRS = @maybe_libatomic_ops@ +# else +# SUBDIRS = +# endif +SUBDIRS = + +ACLOCAL_AMFLAGS = -I m4 +AM_CPPFLAGS = \ + -I$(top_builddir)/include -I$(top_srcdir)/include \ + $(ATOMIC_OPS_CFLAGS) + +# Initialize variables so that we can declare files locally. +EXTRA_DIST = +lib_LTLIBRARIES = +include_HEADERS = +pkginclude_HEADERS = +dist_noinst_HEADERS = +check_PROGRAMS = +check_LTLIBRARIES = +TESTS = + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = bdw-gc.pc + +# C Library +# --------- + +lib_LTLIBRARIES += libgc.la +lib_LTLIBRARIES += libgc-static.la + +if SINGLE_GC_OBJ + +libgc_la_SOURCES = extra/gc.c + +if PTHREAD_START_STANDALONE +AM_CPPFLAGS += -DGC_PTHREAD_START_STANDALONE +libgc_la_SOURCES += pthread_start.c +endif + +else + +EXTRA_DIST += extra/gc.c +libgc_la_SOURCES = \ + allchblk.c alloc.c blacklst.c dbg_mlc.c \ + dyn_load.c finalize.c gc_dlopen.c gcj_mlc.c headers.c \ + mach_dep.c malloc.c mallocx.c mark.c mark_rts.c misc.c new_hblk.c \ + obj_map.c os_dep.c ptr_chck.c reclaim.c specific.c typd_mlc.c + +# C Library: Architecture Dependent +# --------------------------------- + +if WIN32_THREADS +libgc_la_SOURCES += win32_threads.c +else +if PTHREADS +# Not Cygwin or MinGW. +libgc_la_SOURCES += pthread_start.c pthread_support.c +if DARWIN_THREADS +libgc_la_SOURCES += darwin_stop_world.c +else +libgc_la_SOURCES += pthread_stop_world.c +endif +endif +endif + +if THREAD_LOCAL_ALLOC +libgc_la_SOURCES += thread_local_alloc.c +endif + +if MAKE_BACK_GRAPH +libgc_la_SOURCES += backgraph.c +endif + +if CHECKSUMS +libgc_la_SOURCES += checksums.c +endif + +if ENABLE_DISCLAIM +libgc_la_SOURCES += fnlz_mlc.c +endif + +# End of !SINGLE_GC_OBJ +endif + +libgc_static_la_SOURCES = $(libgc_la_SOURCES) + +if USE_INTERNAL_LIBATOMIC_OPS +nodist_libgc_la_SOURCES = libatomic_ops/src/atomic_ops.c +endif + +if NEED_ATOMIC_OPS_ASM +nodist_libgc_la_SOURCES = libatomic_ops/src/atomic_ops_sysdeps.S +endif + +nodist_libgc_static_la_SOURCES = $(nodist_libgc_la_SOURCES) + +# Include THREADDLLIBS here to ensure that the correct versions of +# linuxthread semaphore functions get linked: +libgc_la_LIBADD = @addobjs@ $(THREADDLLIBS) $(UNWINDLIBS) $(ATOMIC_OPS_LIBS) +libgc_la_DEPENDENCIES = @addobjs@ +libgc_la_LDFLAGS = $(extra_ldflags_libgc) -version-info $(LIBGC_VER_INFO) \ + -no-undefined + +EXTRA_libgc_la_SOURCES = ia64_save_regs_in_stack.s sparc_mach_dep.S \ + sparc_netbsd_mach_dep.s sparc_sunos4_mach_dep.s + +libgc_static_la_LIBADD = $(libgc_la_LIBADD) +libgc_static_la_DEPENDENCIES = $(libgc_la_DEPENDENCIES) +libgc_static_la_LDFLAGS = -static + +EXTRA_libgc_static_la_SOURCES = $(EXTRA_libgc_la_SOURCES) + + +# C++ Interface +# ------------- + +if CPLUSPLUS +lib_LTLIBRARIES += libgccpp.la +pkginclude_HEADERS += include/gc_cpp.h +include_HEADERS += include/extra/gc_cpp.h +libgccpp_la_SOURCES = gc_cpp.cc +libgccpp_la_LIBADD = ./libgc.la +libgccpp_la_LDFLAGS = -version-info $(LIBGCCPP_VER_INFO) -no-undefined +endif + +# FIXME: If Visual C++ users use Makefile.am, this should go into +# pkginclude_HEADERS with proper AM_CONDITIONALization. Otherwise +# delete this comment. +EXTRA_DIST += gc_cpp.cpp + + +# Misc +# ---- + +AM_CXXFLAGS = @GC_CFLAGS@ +AM_CFLAGS = $(WERROR_CFLAGS) @GC_CFLAGS@ + +CFLAGS += $(CFLAGS_EXTRA) +CXXFLAGS += $(CFLAGS_EXTRA) + +## FIXME: relies on internal code generated by automake. +## FIXME: ./configure --enable-dependency-tracking should be used +#all_objs = @addobjs@ $(libgc_la_OBJECTS) +#$(all_objs) : include/private/gcconfig.h include/private/gc_priv.h \ +#include/private/gc_hdrs.h include/gc.h include/gc_gcj.h \ +#include/gc_pthread_redirects.h include/gc_config_macros.h \ +#include/private/thread_local_alloc.h include/private_support.h \ +#include/private/pthread_stop_world.h \ +#include/gc_mark.h @addincludes@ + +## FIXME: we shouldn't have to do this, but automake forces us to. +## We use -Wp,-P to strip #line directives. Irix `as' chokes on +## these. +if ASM_WITH_CPP_UNSUPPORTED + ASM_CPP_OPTIONS = +else + ASM_CPP_OPTIONS = -Wp,-P -x assembler-with-cpp +endif + +.s.lo: + $(LTCOMPILE) $(ASM_CPP_OPTIONS) -c $< + +.S.lo: + $(LTCOMPILE) $(ASM_CPP_OPTIONS) -c $< + +## We need to add DEFS to assembler flags +## :FIXME: what if assembler does not accept -D... ? +## (use Autoconf to prepare ASDEFS?) + +CCASFLAGS += $(DEFS) + +# headers which are not installed +# (see include/include.am for more) +# + +# documentation which is not installed +# +EXTRA_DIST += README.QUICK + +# other makefiles +# :GOTCHA: deliberately we do not include 'Makefile' +EXTRA_DIST += BCC_MAKEFILE NT_MAKEFILE \ + OS2_MAKEFILE PCR-Makefile digimars.mak \ + Makefile.direct SMakefile.amiga WCC_MAKEFILE autogen.sh \ + CMakeLists.txt tests/CMakeLists.txt + +# files used by makefiles other than Makefile.am +# +EXTRA_DIST += tools/if_mach.c tools/if_not_there.c tools/setjmp_t.c \ + tools/threadlibs.c gc.mak extra/MacOS.c extra/AmigaOS.c \ + extra/symbian/global_end.cpp extra/symbian/global_start.cpp \ + extra/symbian/init_global_static_roots.cpp extra/symbian.cpp \ + extra/pcr_interface.c extra/real_malloc.c \ + build/s60v3/bld.inf build/s60v3/libgc.mmp \ + extra/Mac_files/datastart.c extra/Mac_files/dataend.c \ + extra/Mac_files/MacOS_config.h \ + include/private/msvc_dbg.h extra/msvc_dbg.c tools/callprocs.sh + +# +# :GOTCHA: GNU make rule for making .s out of .S is flawed, +# it will not remove dest if building fails +.S.s: + if $(CPP) $< >$@ ; then :; else rm -f $@; fi + +include include/include.am +include cord/cord.am +include tests/tests.am +include doc/doc.am +# Putting these at the top causes cord to be built first, and not find libgc.a +# on HP/UX. There may be a better fix. diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/Makefile.direct b/unity-2019.4.24f1-mbe/external/bdwgc/Makefile.direct new file mode 100644 index 000000000..cb44008b8 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/Makefile.direct @@ -0,0 +1,417 @@ +# This is the original manually generated Makefile. It may still be used +# to build the collector. +# +# Primary targets: +# gc.a - builds basic library +# c++ - adds C++ interface to library +# cords - adds cords (heavyweight strings) to library +# check - prints porting information, then builds basic version of gc.a, +# and runs some tests of collector and cords. Does not add cords or +# c++ interface to gc.a +# check-cpp - runs C++ test without adding C++ (and cords) interface to gc.a +# cord/de - builds dumb editor based on cords. + +ABI_FLAG= +# ABI_FLAG should be the cc flag that specifies the ABI. On most +# platforms this will be the empty string. Possible values: +# +DD64 for 64-bit executable on HP/UX. +# -n32, -n64, -o32 for SGI/MIPS ABIs. + +AS_ABI_FLAG=$(ABI_FLAG) +# ABI flag for assembler. On HP/UX this is +A64 for 64 bit +# executables. + +CC=cc $(ABI_FLAG) +# Compiler executable name. For EMX, replace to "gcc". + +CXX=g++ $(ABI_FLAG) +# Needed only for "make c++", which adds the c++ interface. + +AS=as $(AS_ABI_FLAG) +# The above doesn't work with gas, which doesn't run cpp. +# Define AS as `gcc -c -x assembler-with-cpp' instead. +# Under Irix 6, you have to specify the ABI (-o32, -n32, or -64) +# if you use something other than the default ABI on your machine. + +LD=ld + +# Redefining srcdir allows object code for the nonPCR version of the collector +# to be generated in different directories. +srcdir= . +VPATH= $(srcdir) + +# Path to atomic_ops source. +AO_SRC_DIR=$(srcdir)/libatomic_ops + +CFLAGS_EXTRA= +# We need CFLAGS_FOR_PIC because we might be building a shared library. +CFLAGS= -O -I$(srcdir)/include -I$(AO_SRC_DIR)/src \ + -DGC_ATOMIC_UNCOLLECTABLE -DNO_EXECUTE_PERMISSION -DALL_INTERIOR_POINTERS \ + -DENABLE_DISCLAIM $(CFLAGS_FOR_PIC) $(CFLAGS_EXTRA) + +# To build the collector with threads support, add to the above: +# -DGC_THREADS -DPARALLEL_MARK -DTHREAD_LOCAL_ALLOC +# +# To build the preload library that intercepts malloc, add: +# -DGC_USE_DLOPEN_WRAP -DREDIRECT_MALLOC=GC_malloc -fpic + +# HOSTCC and HOSTCFLAGS are used to build executables that will be run as +# part of the build process, i.e. on the build machine. These will usually +# be the same as CC and CFLAGS, except in a cross-compilation environment. +# Note that HOSTCFLAGS should include any -D flags that affect thread support. +HOSTCC=$(CC) +HOSTCFLAGS=$(CFLAGS) + +# For dynamic library builds, it may be necessary to add flags to generate +# PIC code, e.g. -fPIC on Linux. + +# Setjmp_test may yield overly optimistic results when compiled +# without optimization. + +# Look into doc/README.macros for the description of the "define arguments" +# influencing the collector configuration. + +CXXFLAGS= $(CFLAGS) +AR= ar + +RANLIB= ranlib +# For EMX, replace "ranlib" with "ar s". + + +OBJS= alloc.o reclaim.o allchblk.o misc.o mach_dep.o os_dep.o mark_rts.o \ + headers.o mark.o obj_map.o blacklst.o finalize.o new_hblk.o dbg_mlc.o \ + malloc.o checksums.o pthread_support.o pthread_stop_world.o \ + darwin_stop_world.o typd_mlc.o ptr_chck.o mallocx.o gcj_mlc.o specific.o \ + gc_dlopen.o backgraph.o win32_threads.o pthread_start.o \ + thread_local_alloc.o fnlz_mlc.o atomic_ops.o atomic_ops_sysdeps.o + +CSRCS= reclaim.c allchblk.c misc.c alloc.c mach_dep.c os_dep.c mark_rts.c \ + headers.c mark.c obj_map.c blacklst.c finalize.c \ + new_hblk.c dyn_load.c dbg_mlc.c malloc.c \ + checksums.c pthread_support.c pthread_stop_world.c darwin_stop_world.c \ + typd_mlc.c ptr_chck.c mallocx.c gcj_mlc.c specific.c gc_dlopen.c \ + backgraph.c win32_threads.c pthread_start.c thread_local_alloc.c fnlz_mlc.c + +CORD_SRCS= cord/cordbscs.c cord/cordxtra.c cord/cordprnt.c cord/tests/de.c \ + cord/tests/cordtest.c include/cord.h include/ec.h \ + include/cord_pos.h cord/tests/de_win.c cord/tests/de_win.h \ + cord/tests/de_cmds.h cord/tests/de_win.rc + +CORD_OBJS= cord/cordbscs.o cord/cordxtra.o cord/cordprnt.o + +SRCS= $(CSRCS) \ + include/gc.h include/gc_typed.h include/gc_tiny_fl.h \ + include/gc_version.h include/private/gc_hdrs.h include/private/gc_priv.h \ + include/private/gcconfig.h include/private/gc_pmark.h \ + include/gc_inline.h include/gc_mark.h include/gc_disclaim.h \ + tools/threadlibs.c tools/if_mach.c tools/if_not_there.c gc_cpp.cc \ + include/gc_cpp.h include/weakpointer.h include/private/gc_locks.h \ + include/new_gc_alloc.h include/gc_alloc_ptrs.h include/gc_allocator.h \ + include/javaxfc.h \ + include/gc_backptr.h include/gc_gcj.h include/private/dbg_mlc.h \ + include/private/specific.h include/leak_detector.h \ + include/gc_pthread_redirects.h include/private/gc_atomic_ops.h \ + include/gc_config_macros.h include/private/pthread_support.h \ + include/private/pthread_stop_world.h include/private/darwin_semaphore.h \ + include/private/darwin_stop_world.h include/private/thread_local_alloc.h \ + ia64_save_regs_in_stack.s sparc_mach_dep.S \ + sparc_netbsd_mach_dep.s sparc_sunos4_mach_dep.s $(CORD_SRCS) + +CORD_INCLUDE_FILES= $(srcdir)/include/gc.h $(srcdir)/include/cord.h \ + $(srcdir)/include/ec.h $(srcdir)/include/cord_pos.h + +# Executable file name extension. For EMX, specify ".exe". +EXEEXT= + +UTILS= if_mach$(EXEEXT) if_not_there$(EXEEXT) threadlibs$(EXEEXT) + +# Libraries needed for curses applications. Only needed for de. +# It might also require -ltermlib on some targets. +CURSES= -lcurses + +# The following is irrelevant on most systems. But a few +# versions of make otherwise fork the shell specified in +# the SHELL environment variable. +SHELL= /bin/sh + +SPECIALCFLAGS= -I$(srcdir)/include -I$(AO_SRC_DIR)/src $(CFLAGS_FOR_PIC) +# Alternative flags to the C compiler for mach_dep.c. +# Mach_dep.c often doesn't like optimization, and it's +# not time-critical anyway. +# Set SPECIALCFLAGS to -q nodirect_code on Encore. + +all: base_lib gctest$(EXEEXT) + +atomic_ops.o: $(AO_SRC_DIR)/src/atomic_ops.c + $(CC) $(CFLAGS) -c -o $@ $(AO_SRC_DIR)/src/atomic_ops.c +# For some reason, Solaris make does not handle "$<" properly. + +atomic_ops_sysdeps.o: $(AO_SRC_DIR)/src/atomic_ops_sysdeps.S + $(CC) $(CFLAGS) -c -o $@ $(AO_SRC_DIR)/src/atomic_ops_sysdeps.S + +LEAKFLAGS= $(CFLAGS) -DFIND_LEAK + +BSD-pkg-all: bsd-libgc.a bsd-libleak.a + +bsd-libgc.a: + $(MAKE) -f Makefile.direct CFLAGS="$(CFLAGS)" clean c++-t + mv gc.a bsd-libgc.a + +bsd-libleak.a: + $(MAKE) -f Makefile.direct CFLAGS="$(LEAKFLAGS)" clean c++-nt + mv gc.a bsd-libleak.a + +BSD-pkg-install: BSD-pkg-all + ${CP} bsd-libgc.a libgc.a + ${INSTALL_DATA} libgc.a ${PREFIX}/lib + ${INSTALL_DATA} gc.h gc_cpp.h ${PREFIX}/include + ${INSTALL_MAN} doc/gc.man ${PREFIX}/man/man3/gc.3 + +pcr: PCR-Makefile include/private/gc_private.h include/private/gc_hdrs.h \ +include/private/gc_locks.h include/gc.h include/private/gcconfig.h \ +mach_dep.o $(SRCS) + $(MAKE) -f PCR-Makefile depend + $(MAKE) -f PCR-Makefile + +$(OBJS) tests/test.o dyn_load.o dyn_load_sunos53.o: \ + $(srcdir)/include/private/gc_priv.h \ + $(srcdir)/include/private/gc_hdrs.h $(srcdir)/include/private/gc_locks.h \ + $(srcdir)/include/gc.h $(srcdir)/include/gc_pthread_redirects.h \ + $(srcdir)/include/private/gcconfig.h $(srcdir)/include/gc_typed.h \ + $(srcdir)/include/gc_config_macros.h + +mark.o typd_mlc.o finalize.o ptr_chck.o: $(srcdir)/include/gc_mark.h \ + $(srcdir)/include/private/gc_pmark.h + +specific.o pthread_support.o thread_local_alloc.o win32_threads.o: \ + $(srcdir)/include/private/specific.h $(srcdir)/include/gc_inline.h \ + $(srcdir)/include/private/thread_local_alloc.h + +dbg_mlc.o gcj_mlc.o: $(srcdir)/include/private/dbg_mlc.h + +tests/test.o: tests $(srcdir)/tests/test.c + $(CC) $(CFLAGS) -c $(srcdir)/tests/test.c + mv test.o tests/test.o + +tests: + mkdir tests + +base_lib gc.a: $(OBJS) dyn_load.o $(UTILS) + rm -f dont_ar_1 + ./if_mach SPARC SOLARIS touch dont_ar_1 + ./if_mach SPARC SOLARIS $(AR) rus gc.a $(OBJS) dyn_load.o + ./if_mach M68K AMIGA touch dont_ar_1 + ./if_mach M68K AMIGA $(AR) -vrus gc.a $(OBJS) dyn_load.o + ./if_not_there dont_ar_1 || $(AR) ru gc.a $(OBJS) dyn_load.o + ./if_not_there dont_ar_1 || $(RANLIB) gc.a || cat /dev/null + echo > base_lib +# Ignore ranlib failure; that usually means it doesn't exist, and +# isn't needed. + +cords: $(CORD_OBJS) $(UTILS) base_lib + rm -f dont_ar_3 + ./if_mach SPARC SOLARIS touch dont_ar_3 + ./if_mach SPARC SOLARIS $(AR) rus gc.a $(CORD_OBJS) + ./if_mach M68K AMIGA touch dont_ar_3 + ./if_mach M68K AMIGA $(AR) -vrus gc.a $(CORD_OBJS) + ./if_not_there dont_ar_3 || $(AR) ru gc.a $(CORD_OBJS) + ./if_not_there dont_ar_3 || $(RANLIB) gc.a || cat /dev/null + echo > cords + +gc_cpp.o: $(srcdir)/gc_cpp.cc $(srcdir)/include/gc_cpp.h $(srcdir)/include/gc.h + $(CXX) -c $(CXXFLAGS) $(srcdir)/gc_cpp.cc + +test_cpp$(EXEEXT): $(srcdir)/tests/test_cpp.cc $(srcdir)/include/gc_cpp.h \ + $(srcdir)/include/gc.h gc_cpp.o base_lib $(UTILS) + rm -f test_cpp$(EXEEXT) + ./if_mach HP_PA HPUX $(CXX) $(CXXFLAGS) -o test_cpp $(srcdir)/tests/test_cpp.cc gc_cpp.o gc.a -ldld `./threadlibs` + ./if_not_there test_cpp$(EXEEXT) || $(CXX) $(CXXFLAGS) -o test_cpp$(EXEEXT) $(srcdir)/tests/test_cpp.cc gc_cpp.o gc.a `./threadlibs` + +check-cpp: test_cpp$(EXEEXT) + ./test_cpp + +c++-t: c++ test_cpp$(EXEEXT) + ./test_cpp 1 + +c++-nt: c++ + @echo "Use ./test_cpp 1 to test the leak library" + +c++: gc_cpp.o $(srcdir)/include/gc_cpp.h base_lib $(UTILS) + rm -f dont_ar_4 + ./if_mach SPARC SOLARIS touch dont_ar_4 + ./if_mach SPARC SOLARIS $(AR) rus gc.a gc_cpp.o + ./if_mach M68K AMIGA touch dont_ar_4 + ./if_mach M68K AMIGA $(AR) -vrus gc.a gc_cpp.o + ./if_not_there dont_ar_4 || $(AR) ru gc.a gc_cpp.o + ./if_not_there dont_ar_4 || $(RANLIB) gc.a || cat /dev/null + echo > c++ + +dyn_load_sunos53.o: dyn_load.c + $(CC) $(CFLAGS) -DSUNOS53_SHARED_LIB -c $(srcdir)/dyn_load.c -o $@ + +# SunOS5 shared library version of the collector +sunos5gc.so: $(OBJS) dyn_load_sunos53.o + $(CC) -G -o sunos5gc.so $(OBJS) dyn_load_sunos53.o -ldl + ln sunos5gc.so libgc.so + +# Alpha/OSF shared library version of the collector +libalphagc.so: $(OBJS) + $(LD) -shared -o libalphagc.so $(OBJS) dyn_load.o -lc + ln libalphagc.so libgc.so + +# IRIX shared library version of the collector +libirixgc.so: $(OBJS) dyn_load.o + $(LD) -shared $(ABI_FLAG) -o libirixgc.so $(OBJS) dyn_load.o -lc + ln libirixgc.so libgc.so + +# Linux shared library version of the collector +liblinuxgc.so: $(OBJS) dyn_load.o + gcc -shared -o liblinuxgc.so $(OBJS) dyn_load.o + ln liblinuxgc.so libgc.so + +# Build gctest with dynamic library +dyn_test: + $(CC) $(CFLAGS) -o gctest$(EXEEXT) tests/test.c libgc.so `./threadlibs` + ./gctest + +# Alternative Linux rule. This is preferable, but is likely to break the +# Makefile for some non-linux platforms. +# LIBOBJS= $(patsubst %.o, %.lo, $(OBJS)) +# +#.SUFFIXES: .lo $(SUFFIXES) +# +#.c.lo: +# $(CC) $(CFLAGS) $(CPPFLAGS) -fPIC -c $< -o $@ +# +# liblinuxgc.so: $(LIBOBJS) dyn_load.lo +# gcc -shared -Wl,-soname=libgc.so.0 -o libgc.so.0 $(LIBOBJS) dyn_load.lo +# touch liblinuxgc.so + +mach_dep.o: $(srcdir)/mach_dep.c $(srcdir)/sparc_mach_dep.S \ + $(srcdir)/sparc_sunos4_mach_dep.s \ + $(srcdir)/ia64_save_regs_in_stack.s \ + $(srcdir)/sparc_netbsd_mach_dep.s $(UTILS) + rm -f mach_dep.o + ./if_mach SPARC SOLARIS $(CC) -c -o mach_dep2.o $(srcdir)/sparc_mach_dep.S + ./if_mach SPARC OPENBSD $(AS) -o mach_dep2.o $(srcdir)/sparc_sunos4_mach_dep.s + ./if_mach SPARC NETBSD $(AS) -o mach_dep2.o $(srcdir)/sparc_netbsd_mach_dep.s + ./if_mach SPARC "" $(CC) -c -o mach_dep1.o $(SPECIALCFLAGS) $(srcdir)/mach_dep.c + ./if_mach SPARC "" $(LD) -r -o mach_dep.o mach_dep1.o mach_dep2.o + ./if_mach IA64 "" $(AS) -o ia64_save_regs_in_stack.o $(srcdir)/ia64_save_regs_in_stack.s + ./if_mach IA64 "" $(CC) -c -o mach_dep1.o $(SPECIALCFLAGS) $(srcdir)/mach_dep.c + ./if_mach IA64 "" $(LD) -r -o mach_dep.o mach_dep1.o ia64_save_regs_in_stack.o + -./if_not_there mach_dep.o || $(CC) -c $(SPECIALCFLAGS) $(srcdir)/mach_dep.c + -./if_not_there mach_dep.o || `cygpath -w /bin/sh` $(CC) -c $(SPECIALCFLAGS) $(srcdir)/mach_dep.c + -./if_not_there mach_dep.o || /bin/sh $(CC) -c $(SPECIALCFLAGS) $(srcdir)/mach_dep.c + +mark_rts.o: $(srcdir)/mark_rts.c $(UTILS) + rm -f mark_rts.o + -./if_mach ALPHA OSF1 $(CC) -c $(CFLAGS) -Wo,-notail $(srcdir)/mark_rts.c + -./if_not_there mark_rts.o || $(CC) -c $(CFLAGS) $(srcdir)/mark_rts.c + -./if_not_there mark_rts.o || `cygpath -w /bin/sh` $(CC) -c $(CFLAGS) $(srcdir)/mark_rts.c + -./if_not_there mark_rts.o || /bin/sh $(CC) -c $(CFLAGS) $(srcdir)/mark_rts.c +# Work-around for DEC optimizer tail recursion elimination bug. +# The ALPHA-specific line should be removed if gcc is used. + +alloc.o: include/gc_version.h + +cord/cordbscs.o: $(srcdir)/cord/cordbscs.c $(CORD_INCLUDE_FILES) + $(CC) $(CFLAGS) -c -I$(srcdir) $(srcdir)/cord/cordbscs.c + mkdir cord || cat /dev/null + mv cordbscs.o cord/cordbscs.o +# not all compilers understand -o filename + +cord/cordxtra.o: $(srcdir)/cord/cordxtra.c $(CORD_INCLUDE_FILES) + $(CC) $(CFLAGS) -c -I$(srcdir) $(srcdir)/cord/cordxtra.c + mkdir cord || cat /dev/null + mv cordxtra.o cord/cordxtra.o + +cord/cordprnt.o: $(srcdir)/cord/cordprnt.c $(CORD_INCLUDE_FILES) + $(CC) $(CFLAGS) -c -I$(srcdir) $(srcdir)/cord/cordprnt.c + mkdir cord || cat /dev/null + mv cordprnt.o cord/cordprnt.o + +cord/cordtest$(EXEEXT): $(srcdir)/cord/tests/cordtest.c $(CORD_OBJS) \ + base_lib $(UTILS) + rm -f cord/cordtest$(EXEEXT) + ./if_mach SPARC DRSNX $(CC) $(CFLAGS) -o cord/cordtest $(srcdir)/cord/tests/cordtest.c $(CORD_OBJS) gc.a -lucb + ./if_mach HP_PA HPUX $(CC) $(CFLAGS) -o cord/cordtest $(srcdir)/cord/tests/cordtest.c $(CORD_OBJS) gc.a -ldld `./threadlibs` + ./if_mach M68K AMIGA $(CC) $(CFLAGS) -UGC_AMIGA_MAKINGLIB -o cord/cordtest $(srcdir)/cord/tests/cordtest.c $(CORD_OBJS) gc.a `./threadlibs` + ./if_not_there cord/cordtest$(EXEEXT) || $(CC) $(CFLAGS) -o cord/cordtest$(EXEEXT) $(srcdir)/cord/tests/cordtest.c $(CORD_OBJS) gc.a `./threadlibs` + +cord/de: $(srcdir)/cord/tests/de.c cord/cordbscs.o cord/cordxtra.o base_lib \ + $(UTILS) + rm -f cord/de + ./if_mach SPARC DRSNX $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/tests/de.c cord/cordbscs.o cord/cordxtra.o gc.a -lcurses -ltermlib -lucb `./threadlibs` + ./if_mach HP_PA HPUX $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/tests/de.c cord/cordbscs.o cord/cordxtra.o gc.a -lcurses -ltermlib -ldld `./threadlibs` + ./if_mach POWERPC AIX $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/tests/de.c cord/cordbscs.o cord/cordxtra.o gc.a -lcurses + ./if_mach POWERPC DARWIN $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/tests/de.c cord/cordbscs.o cord/cordxtra.o gc.a + ./if_mach I386 LINUX $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/tests/de.c cord/cordbscs.o cord/cordxtra.o gc.a -lcurses `./threadlibs` + ./if_mach ALPHA LINUX $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/tests/de.c cord/cordbscs.o cord/cordxtra.o gc.a -lcurses `./threadlibs` + ./if_mach IA64 LINUX $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/tests/de.c cord/cordbscs.o cord/cordxtra.o gc.a -lcurses `./threadlibs` + ./if_mach M68K AMIGA $(CC) $(CFLAGS) -UGC_AMIGA_MAKINGLIB -o cord/de $(srcdir)/cord/tests/de.c cord/cordbscs.o cord/cordxtra.o gc.a -lcurses + ./if_not_there cord/de$(EXEEXT) || $(CC) $(CFLAGS) -o cord/de$(EXEEXT) $(srcdir)/cord/tests/de.c cord/cordbscs.o cord/cordxtra.o gc.a $(CURSES) `./threadlibs` + +if_mach$(EXEEXT): $(srcdir)/tools/if_mach.c \ + $(srcdir)/include/private/gcconfig.h + $(HOSTCC) $(HOSTCFLAGS) -o if_mach$(EXEEXT) $(srcdir)/tools/if_mach.c + +threadlibs$(EXEEXT): $(srcdir)/tools/threadlibs.c \ + $(srcdir)/include/private/gcconfig.h + $(HOSTCC) $(HOSTCFLAGS) -o threadlibs$(EXEEXT) $(srcdir)/tools/threadlibs.c + +if_not_there$(EXEEXT): $(srcdir)/tools/if_not_there.c + $(HOSTCC) $(HOSTCFLAGS) -o if_not_there$(EXEEXT) $(srcdir)/tools/if_not_there.c + +clean: + rm -f gc.a *.i *.o *.exe tests/*.o gctest gctest_dyn_link test_cpp \ + setjmp_test mon.out gmon.out a.out core if_not_there if_mach \ + base_lib c++ $(CORD_OBJS) cord/cordtest cord/de cords \ + dont_ar_* threadlibs + -rm -f *~ + +gctest$(EXEEXT): tests/test.o base_lib $(UTILS) + rm -f gctest$(EXEEXT) + ./if_mach SPARC DRSNX $(CC) $(CFLAGS) -o gctest tests/test.o gc.a -lucb + ./if_mach HP_PA HPUX $(CC) $(CFLAGS) -o gctest tests/test.o gc.a -ldld `./threadlibs` + ./if_mach M68K AMIGA $(CC) $(CFLAGS) -UGC_AMIGA_MAKINGLIB -o gctest tests/test.o gc.a `./threadlibs` + ./if_not_there gctest$(EXEEXT) || $(CC) $(CFLAGS) -o gctest$(EXEEXT) tests/test.o gc.a `./threadlibs` + +# If an optimized setjmp_test generates a segmentation fault, +# odds are your compiler is broken. Gctest may still work. +# Try compiling setjmp_t.c unoptimized. +setjmp_test$(EXEEXT): $(srcdir)/tools/setjmp_t.c $(srcdir)/include/gc.h \ + $(UTILS) + $(CC) $(CFLAGS) -o setjmp_test$(EXEEXT) $(srcdir)/tools/setjmp_t.c + +check: cord/cordtest$(EXEEXT) gctest$(EXEEXT) setjmp_test$(EXEEXT) + ./setjmp_test + ./gctest + cord/cordtest + +# A synonym to "check" (for compatibility with older GC versions). +test: check + +# BTL: added to test shared library version of collector. +# Currently works only under SunOS5. Requires GC_INIT call from statically +# loaded client code. +ABSDIR= `pwd` +gctest_dyn_link: tests/test.o libgc.so + $(CC) -L$(ABSDIR) -R$(ABSDIR) -o gctest_dyn_link tests/test.o -lgc -ldl -lthread + +gctest_irix_dyn_link: tests/test.o libirixgc.so + $(CC) -L$(ABSDIR) -o gctest_irix_dyn_link tests/test.o -lirixgc + +SYM_PREFIX-libgc=GC + +reserved_namespace: $(SRCS) + for file in $(SRCS) tests/test.c tests/test_cpp.cc; do \ + sed s/GC_/_GC_/g < $$file > tmp; \ + cp tmp $$file; \ + done + +user_namespace: $(SRCS) + for file in $(SRCS) tests/test.c tests/test_cpp.cc; do \ + sed s/_GC_/GC_/g < $$file > tmp; \ + cp tmp $$file; \ + done diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/NT_MAKEFILE b/unity-2019.4.24f1-mbe/external/bdwgc/NT_MAKEFILE new file mode 100644 index 000000000..5c5a53838 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/NT_MAKEFILE @@ -0,0 +1,106 @@ +# Makefile for Windows NT. Assumes Microsoft compiler. +# Use "nmake -f NT_MAKEFILE nodebug=1 all" for optimized versions of library, +# gctest and editor. Please adjust/uncomment CPU, CVTRES_CPU, GC_DLL, GC_LIB, +# CFLAGS_SPECIFIC, LINK_GC variables below for the desired configuration. + +# For building x86 (32-bit) library: +CPU=i386 +CVTRES_CPU=X86 +# +# For building x64 (amd64) library: +#CPU=AMD64 +#CVTRES_CPU=X64 + +!include + +# For a static single-threaded collector library: +#CFLAGS_SPECIFIC=$(cvars) -DGC_NOT_DLL +#GC_DLL= +#GC_LIB=gc.lib +#LINK_GC=lib /out:$(GC_LIB) +# +# For a static multi-threaded collector library: +#CFLAGS_SPECIFIC=$(cvarsmt) -DGC_NOT_DLL -DGC_THREADS -DTHREAD_LOCAL_ALLOC -DPARALLEL_MARK +#GC_DLL= +#GC_LIB=gc.lib +#LINK_GC=lib /out:$(GC_LIB) +# +# For a dynamic 32-bit multi-threaded collector library: +CFLAGS_SPECIFIC=$(cvarsmt) -DGC_DLL -DGC_THREADS -DTHREAD_LOCAL_ALLOC -DPARALLEL_MARK +GC_DLL=gc.dll +GC_LIB=gc_dll.lib +# +# For a dynamic 64-bit multi-threaded collector library: +#CFLAGS_SPECIFIC=$(cvarsmt) -DGC_DLL -DGC_THREADS -DTHREAD_LOCAL_ALLOC -DPARALLEL_MARK +#GC_DLL=gc64.dll +#GC_LIB=gc64_dll.lib + +LINK_DLL_FLAGS=kernel32.lib user32.lib \ + /nologo /subsystem:windows /dll /incremental:no /pdb:"gc.pdb" \ + /out:$(GC_DLL) /implib:$(GC_LIB) + +# For a dynamic 32- or 64-bit multi-threaded collector library: +LINK_GC=link $(ldebug) $(LINK_DLL_FLAGS) + + +# Make sure that .cc is not viewed as a suffix. It is for VC++2005, but +# not earlier versions. We can deal with either, but not inconsistency. +.SUFFIXES: +.SUFFIXES: .obj .cpp .c + +# Atomic_ops installation directory. For win32, the source directory +# should do, since we only need the headers. +# We assume this was manually unpacked. +AO_SRC_DIR=libatomic_ops/src +AO_INCLUDE_DIR=$(AO_SRC_DIR) + +OBJS= alloc.obj reclaim.obj allchblk.obj misc.obj mach_dep.obj os_dep.obj mark_rts.obj headers.obj mark.obj obj_map.obj blacklst.obj finalize.obj new_hblk.obj dbg_mlc.obj fnlz_mlc.obj malloc.obj dyn_load.obj typd_mlc.obj ptr_chck.obj gc_cpp.obj mallocx.obj win32_threads.obj extra\msvc_dbg.obj thread_local_alloc.obj + +all: gctest.exe cord\de.exe test_cpp.exe + +.c.obj: + $(cc) $(cdebug) $(cflags) $(CFLAGS_SPECIFIC) -Iinclude -I$(AO_INCLUDE_DIR) -DALL_INTERIOR_POINTERS -DENABLE_DISCLAIM -DCORD_NOT_DLL -D_CRT_SECURE_NO_DEPRECATE $*.c /Fo$*.obj /wd4100 /wd4127 /wd4701 +# Disable crt security warnings, since unfortunately they warn about all sorts +# of safe uses of strncpy. It would be nice to leave the rest enabled. + +.cpp.obj: + $(cc) $(cdebug) $(cflags) $(CFLAGS_SPECIFIC) -Iinclude -I$(AO_INCLUDE_DIR) -DALL_INTERIOR_POINTERS -D_CRT_SECURE_NO_DEPRECATE $*.cpp /Fo$*.obj + +$(OBJS) tests\test.obj: include\private\gc_priv.h include\private\gc_hdrs.h include\gc.h include\private\gcconfig.h include\private\gc_locks.h include\private\gc_pmark.h include\gc_mark.h include\gc_disclaim.h include\private\msvc_dbg.h + +$(GC_LIB): $(OBJS) + $(LINK_GC) /MACHINE:$(CPU) $(OBJS) + +gctest.exe: tests\test.obj $(GC_LIB) + $(link) /MACHINE:$(CPU) $(ldebug) $(guiflags) -out:$*.exe tests\test.obj $(guilibs) $(GC_LIB) +# mapsympe -n -o gctest.sym gctest.exe +# This produces a GUI app that opens no window and writes to gctest.gc.log. + +cord\tests\de_win.rbj: cord\tests\de_win.res + cvtres /MACHINE:$(CVTRES_CPU) /OUT:cord\tests\de_win.rbj cord\tests\de_win.res + +cord\tests\de.obj cord\tests\de_win.obj: include\cord.h include\cord_pos.h cord\tests\de_win.h cord\tests\de_cmds.h + +cord\tests\de_win.res: cord\tests\de_win.rc cord\tests\de_win.h cord\tests\de_cmds.h + $(rc) $(rcvars) -r -fo cord\tests\de_win.res cord\tests\de_win.rc + +# Cord/de is a real win32 GUI app. +cord\de.exe: cord\cordbscs.obj cord\cordxtra.obj cord\tests\de.obj cord\tests\de_win.obj cord\tests\de_win.rbj $(GC_LIB) + $(link) /MACHINE:$(CPU) $(ldebug) $(guiflags) -out:cord\de.exe cord\cordbscs.obj cord\cordxtra.obj cord\tests\de.obj cord\tests\de_win.obj cord\tests\de_win.rbj $(GC_LIB) $(guilibs) + +gc_cpp.obj: include\gc_cpp.h include\gc.h + +test_cpp.cpp: tests\test_cpp.cc + copy tests\test_cpp.cc test_cpp.cpp + +# This generates the C++ test executable. The executable expects +# a single numeric argument, which is the number of iterations. +# The output appears in test_cpp.gc.log file. +test_cpp.exe: test_cpp.obj include\gc_cpp.h include\gc.h $(GC_LIB) + $(link) /MACHINE:$(CPU) $(ldebug) $(guiflags) -out:test_cpp.exe test_cpp.obj $(GC_LIB) $(guilibs) + +$(AO_SRC_DIR): + tar xvfz $(AO_SRC_DIR).tar.gz + +clean: + del *.exe *.log *.obj *.pdb cord\*.exe cord\*.exp cord\*.lib cord\*.obj cord\*.pdb cord\tests\*.rbj cord\tests\*.res cord\tests\*.obj extra\*.obj gc*.lib gc*.dll gc*.exp test_cpp.cpp tests\*.obj 2> nul diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/OS2_MAKEFILE b/unity-2019.4.24f1-mbe/external/bdwgc/OS2_MAKEFILE new file mode 100644 index 000000000..400fa73d6 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/OS2_MAKEFILE @@ -0,0 +1,45 @@ +# Makefile for OS/2. Assumes IBM's compiler, static linking, and a single thread. +# Adding dynamic linking support seems easy, but takes a little bit of work. +# Adding thread support may be nontrivial, since we haven't yet figured out how to +# look at another thread's registers. + +# Significantly revised for GC version 4.4 by Mark Boulter (Jan 1994). + +OBJS= alloc.obj reclaim.obj allchblk.obj misc.obj mach_dep.obj os_dep.obj mark_rts.obj headers.obj mark.obj obj_map.obj blacklst.obj finalize.obj new_hblk.obj dbg_mlc.obj fnlz_mlc.obj malloc.obj typd_mlc.obj ptr_chck.obj mallocx.obj + +CORDOBJS= cord\cordbscs.obj cord\cordxtra.obj cord\cordprnt.obj + +CC= icc +CFLAGS= /O /Q /DSMALL_CONFIG /DALL_INTERIOR_POINTERS /DENABLE_DISCLAIM +# Use /Ti instead of /O for debugging +# Setjmp_test may yield overly optimistic results when compiled +# without optimization. + +all: $(OBJS) gctest.exe cord\cordtest.exe + +$(OBJS) test.obj: include\private\gc_priv.h include\private\gc_hdrs.h include\gc.h include\private\gcconfig.h + +## ERASE THE LIB FIRST - if it is already there then this command will fail +## (make sure its there or erase will fail!) +gc.lib: $(OBJS) + echo . > gc.lib + erase gc.lib + LIB gc.lib $(OBJS), gc.lst + +mach_dep.obj: mach_dep.c + $(CC) $(CFLAGS) /C mach_dep.c + +gctest.exe: test.obj gc.lib + $(CC) $(CFLAGS) /B"/STACK:524288" /Fegctest test.obj gc.lib + +cord\cordbscs.obj: cord\cordbscs.c include\cord.h include\cord_pos.h + $(CC) $(CFLAGS) /C /Focord\cordbscs cord\cordbscs.c + +cord\cordxtra.obj: cord\cordxtra.c include\cord.h include\cord_pos.h include\ec.h + $(CC) $(CFLAGS) /C /Focord\cordxtra cord\cordxtra.c + +cord\cordprnt.obj: cord\cordprnt.c include\cord.h include\cord_pos.h include\ec.h + $(CC) $(CFLAGS) /C /Focord\cordprnt cord\cordprnt.c + +cord\cordtest.exe: cord\tests\cordtest.c include\cord.h include\cord_pos.h include\ec.h $(CORDOBJS) gc.lib + $(CC) $(CFLAGS) /B"/STACK:65536" /Fecord\cordtest cord\tests\cordtest.c gc.lib $(CORDOBJS) diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/PCR-Makefile b/unity-2019.4.24f1-mbe/external/bdwgc/PCR-Makefile new file mode 100644 index 000000000..4af42ed4d --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/PCR-Makefile @@ -0,0 +1,47 @@ +# +# Default target +# + +default: gc.o + +include ../config/common.mk + +# +# compilation flags, etc. +# + +CPPFLAGS = $(INCLUDE) $(CONFIG_CPPFLAGS) \ + -DPCR_NO_RENAME -DPCR_NO_HOSTDEP_ERR +#CFLAGS = -DPCR $(CONFIG_CFLAGS) +CFLAGS = -DPCR -DENABLE_DISCLAIM $(CONFIG_CFLAGS) +SPECIALCFLAGS = # For code involving asm's + +ASPPFLAGS = $(INCLUDE) $(CONFIG_ASPPFLAGS) \ + -DPCR_NO_RENAME -DPCR_NO_HOSTDEP_ERR -DASM + +ASFLAGS = $(CONFIG_ASFLAGS) + +LDRFLAGS = $(CONFIG_LDRFLAGS) + +LDFLAGS = $(CONFIG_LDFLAGS) + +# +# BEGIN PACKAGE-SPECIFIC PART +# + +# Fix to point to local pcr installation directory. +PCRDIR= .. + +COBJ= alloc.o reclaim.o allchblk.o misc.o os_dep.o mark_rts.o headers.o mark.o obj_map.o blacklst.o finalize.o new_hblk.o extra/pcr_interface.o extra/real_malloc.o dyn_load.o dbg_mlc.o fnlz_mlc.o malloc.o checksums.o typd_mlc.o ptr_chck.o mallocx.o + +CSRC= reclaim.c allchblk.c misc.c alloc.c mach_dep.c os_dep.c mark_rts.c headers.c mark.c obj_map.c blacklst.c finalize.c new_hblk.c extra/pcr_interface.c extra/real_malloc.c dyn_load.c dbg_mlc.c fnlz_mlc.c malloc.c checksums.c typd_mlc.c ptr_chck.c mallocx.c + +SHELL= /bin/sh + +default: gc.o + +gc.o: $(COBJ) mach_dep.o + $(LDR) $(CONFIG_LDRFLAGS) -o gc.o $(COBJ) mach_dep.o + +mach_dep.o: mach_dep.c + $(CC) -c $(SPECIALCFLAGS) mach_dep.c diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/README.QUICK b/unity-2019.4.24f1-mbe/external/bdwgc/README.QUICK new file mode 100644 index 000000000..b11d4efb5 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/README.QUICK @@ -0,0 +1,83 @@ +Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers +Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. +Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. +Copyright (c) 1999-2001 by Hewlett-Packard. All rights reserved. + +THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED +OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + +Permission is hereby granted to use or copy this program +for any purpose, provided the above notices are retained on all copies. +Permission to modify the code and to distribute modified code is granted, +provided the above notices are retained, and a notice that the code was +modified is included with the above copyright notice. + +A few files have other copyright holders. A few of the files needed +to use the GNU-style build procedure come with a modified GPL license +that appears not to significantly restrict use of the collector, though +use of those files for a purpose other than building the collector may +require the resulting code to be covered by the GPL. + +For more details and the names of other contributors, see the README.md, +doc/README.*, AUTHORS and include/gc.h files. These files describe typical +use of the collector on a machine that is already supported. + +For the version number, see README.md or include/gc_version.h files. + +INSTALLATION: +Under UN*X, Linux: +Alternative 1 (the old way): type "make -f Makefile.direct check". + Link against gc.a. + +Alternative 2 (the new way): type + "./configure --prefix=; make; make check; make install". + Link against /lib/libgc.a or /lib/libgc.so. + See doc/README.autoconf for details + +Under Windows 95, 98, Me, NT, or 2000: +copy the appropriate makefile to MAKEFILE, read it, and type "nmake test". +(Under Windows, this assumes you have Microsoft command-line tools +installed, and suitably configured.) +Read the machine specific README.XXX in the doc directory if one exists. + +If you need thread support, you should define GC_THREADS as described in +doc/README.macros (configure defines this implicitly unless --disable-threads +option is given). + +If you wish to use the cord (structured string) library with the stand-alone +Makefile.direct, type "make -f Makefile.direct cords". (You may need to +override CC specified in the Makefile. The CORD_printf implementation in +cordprnt.c is known to be less than perfectly portable. The rest of the +package should still work.) See include/cord.h for the API. + +If you wish to use the collector from C++, type "make c++", or use +--enable-cplusplus with the configure script. With Makefile.direct, +these ones add further files to gc.a and to the include subdirectory. +With the alternate build process, this generates libgccpp. +See include/gc_cpp.h. + +TYPICAL USE: +Include "gc.h" from the include subdirectory. Link against the +appropriate library ("gc.a" under UN*X). Replace calls to malloc +by calls to GC_MALLOC, and calls to realloc by calls to GC_REALLOC. +If the object is known to never contain pointers, use GC_MALLOC_ATOMIC +instead of GC_MALLOC. + +Define GC_DEBUG before including gc.h for additional checking. + +More documentation on the collector interface can be found in README.md, +doc/gcinterface.md, include/gc.h, and other files in the doc directory. + +WARNINGS: + +Do not store the only pointer to an object in memory allocated +with system malloc, since the collector usually does not scan +memory allocated in this way. + +Use with threads may be supported on your system, but requires the +collector to be built with thread support. See Makefile. The collector +does not guarantee to scan thread-local storage (e.g. of the kind +accessed with pthread_getspecific()). The collector does scan +thread stacks though, so generally the best solution is to ensure that +any pointers stored in thread-local storage are also stored on the +thread's stack for the duration of their lifetime. diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/README.md b/unity-2019.4.24f1-mbe/external/bdwgc/README.md new file mode 100644 index 000000000..c25e82a0c --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/README.md @@ -0,0 +1,596 @@ +# Boehm-Demers-Weiser Garbage Collector + +[![Travis-CI build status](https://travis-ci.org/ivmai/bdwgc.svg?branch=master)](https://travis-ci.org/ivmai/bdwgc) +[![AppVeyor CI build status](https://ci.appveyor.com/api/projects/status/github/ivmai/bdwgc?branch=master&svg=true)](https://ci.appveyor.com/project/ivmai/bdwgc) +[![Coveralls test coverage status](https://coveralls.io/repos/github/ivmai/bdwgc/badge.png?branch=master)](https://coveralls.io/github/ivmai/bdwgc) +[![Coverity Scan build status](https://scan.coverity.com/projects/10813/badge.svg)](https://scan.coverity.com/projects/ivmai-bdwgc) + +This is version 7.7.0 (next release development) of a conservative garbage +collector for C and C++. + + +## Download + +You might find a more recent/stable version on the +[Download](https://github.com/ivmai/bdwgc/wiki/Download) page, or +[BDWGC site](http://www.hboehm.info/gc/). + +Also, the latest bug fixes and new features are available in the +[development repository](https://github.com/ivmai/bdwgc). + + +## Overview + +This is intended to be a general purpose, garbage collecting storage +allocator. The algorithms used are described in: + + * Boehm, H., and M. Weiser, "Garbage Collection in an Uncooperative + Environment", Software Practice & Experience, September 1988, pp. 807-820. + + * Boehm, H., A. Demers, and S. Shenker, "Mostly Parallel Garbage Collection", + Proceedings of the ACM SIGPLAN '91 Conference on Programming Language Design + and Implementation, SIGPLAN Notices 26, 6 (June 1991), pp. 157-164. + + * Boehm, H., "Space Efficient Conservative Garbage Collection", Proceedings + of the ACM SIGPLAN '91 Conference on Programming Language Design and + Implementation, SIGPLAN Notices 28, 6 (June 1993), pp. 197-206. + + * Boehm H., "Reducing Garbage Collector Cache Misses", Proceedings of the + 2000 International Symposium on Memory Management. + +Possible interactions between the collector and optimizing compilers are +discussed in + + * Boehm, H., and D. Chase, "A Proposal for GC-safe C Compilation", + The Journal of C Language Translation 4, 2 (December 1992). + +and + + * Boehm H., "Simple GC-safe Compilation", Proceedings of the ACM SIGPLAN '96 + Conference on Programming Language Design and Implementation. + +Unlike the collector described in the second reference, this collector +operates either with the mutator stopped during the entire collection +(default) or incrementally during allocations. (The latter is supported +on fewer machines.) On the most common platforms, it can be built +with or without thread support. On a few platforms, it can take advantage +of a multiprocessor to speed up garbage collection. + +Many of the ideas underlying the collector have previously been explored +by others. Notably, some of the run-time systems developed at Xerox PARC +in the early 1980s conservatively scanned thread stacks to locate possible +pointers (cf. Paul Rovner, "On Adding Garbage Collection and Runtime Types +to a Strongly-Typed Statically Checked, Concurrent Language" Xerox PARC +CSL 84-7). Doug McIlroy wrote a simpler fully conservative collector that +was part of version 8 UNIX (tm), but appears to not have received +widespread use. + +Rudimentary tools for use of the collector as a +[leak detector](doc/leak.md) are included, +as is a fairly sophisticated string package "cord" that makes use of the +collector. (See doc/README.cords and H.-J. Boehm, R. Atkinson, and M. Plass, +"Ropes: An Alternative to Strings", Software Practice and Experience 25, 12 +(December 1995), pp. 1315-1330. This is very similar to the "rope" package +in Xerox Cedar, or the "rope" package in the SGI STL or the g++ distribution.) + +Further collector documentation can be found in the +[overview](doc/overview.md). + + +## General Description + +This is a garbage collecting storage allocator that is intended to be +used as a plug-in replacement for C's malloc. + +Since the collector does not require pointers to be tagged, it does not +attempt to ensure that all inaccessible storage is reclaimed. However, +in our experience, it is typically more successful at reclaiming unused +memory than most C programs using explicit deallocation. Unlike manually +introduced leaks, the amount of unreclaimed memory typically stays +bounded. + +In the following, an "object" is defined to be a region of memory allocated +by the routines described below. + +Any objects not intended to be collected must be pointed to either +from other such accessible objects, or from the registers, +stack, data, or statically allocated bss segments. Pointers from +the stack or registers may point to anywhere inside an object. +The same is true for heap pointers if the collector is compiled with +`ALL_INTERIOR_POINTERS` defined, or `GC_all_interior_pointers` is otherwise +set, as is now the default. + +Compiling without `ALL_INTERIOR_POINTERS` may reduce accidental retention +of garbage objects, by requiring pointers from the heap to the beginning +of an object. But this no longer appears to be a significant +issue for most programs occupying a small fraction of the possible +address space. + +There are a number of routines which modify the pointer recognition +algorithm. `GC_register_displacement` allows certain interior pointers +to be recognized even if `ALL_INTERIOR_POINTERS` is nor defined. +`GC_malloc_ignore_off_page` allows some pointers into the middle of +large objects to be disregarded, greatly reducing the probability of +accidental retention of large objects. For most purposes it seems +best to compile with `ALL_INTERIOR_POINTERS` and to use +`GC_malloc_ignore_off_page` if you get collector warnings from +allocations of very large objects. See [here](doc/debugging.md) for details. + +_WARNING_: pointers inside memory allocated by the standard `malloc` are not +seen by the garbage collector. Thus objects pointed to only from such a +region may be prematurely deallocated. It is thus suggested that the +standard `malloc` be used only for memory regions, such as I/O buffers, that +are guaranteed not to contain pointers to garbage collectible memory. +Pointers in C language automatic, static, or register variables, +are correctly recognized. (Note that `GC_malloc_uncollectable` has +semantics similar to standard malloc, but allocates objects that are +traced by the collector.) + +_WARNING_: the collector does not always know how to find pointers in data +areas that are associated with dynamic libraries. This is easy to +remedy IF you know how to find those data areas on your operating +system (see `GC_add_roots`). Code for doing this under SunOS, IRIX +5.X and 6.X, HP/UX, Alpha OSF/1, Linux, and win32 is included and used +by default. (See doc/README.win32 for Win32 details.) On other systems +pointers from dynamic library data areas may not be considered by the +collector. If you're writing a program that depends on the collector +scanning dynamic library data areas, it may be a good idea to include +at least one call to `GC_is_visible` to ensure that those areas are +visible to the collector. + +Note that the garbage collector does not need to be informed of shared +read-only data. However if the shared library mechanism can introduce +discontiguous data areas that may contain pointers, then the collector does +need to be informed. + +Signal processing for most signals may be deferred during collection, +and during uninterruptible parts of the allocation process. +Like standard ANSI C mallocs, by default it is unsafe to invoke +malloc (and other GC routines) from a signal handler while another +malloc call may be in progress. + +The allocator/collector can also be configured for thread-safe operation. +(Full signal safety can also be achieved, but only at the cost of two system +calls per malloc, which is usually unacceptable.) + +_WARNING_: the collector does not guarantee to scan thread-local storage +(e.g. of the kind accessed with `pthread_getspecific`). The collector +does scan thread stacks, though, so generally the best solution is to +ensure that any pointers stored in thread-local storage are also +stored on the thread's stack for the duration of their lifetime. +(This is arguably a longstanding bug, but it hasn't been fixed yet.) + + +## Installation and Portability + +As distributed, the collector operates silently +In the event of problems, this can usually be changed by defining the +`GC_PRINT_STATS` or `GC_PRINT_VERBOSE_STATS` environment variables. This +will result in a few lines of descriptive output for each collection. +(The given statistics exhibit a few peculiarities. +Things don't appear to add up for a variety of reasons, most notably +fragmentation losses. These are probably much more significant for the +contrived program "test.c" than for your application.) + +On most Unix-like platforms, the collector can be built either using a +GNU autoconf-based build infrastructure (type `./configure; make` in the +simplest case), or with a classic makefile by itself (type +`make -f Makefile.direct`). + +Please note that the collector source repository does not contain configure +and similar auto-generated files, thus the full procedure of autoconf-based +build of `master` branch of the collector could look like: + + git clone git://github.com/ivmai/bdwgc.git + cd bdwgc + git clone git://github.com/ivmai/libatomic_ops.git + ./autogen.sh + ./configure + make -j + make check + +Cloning of `libatomic_ops` is now optional provided the compiler supports +atomic intrinsics. + +If you are getting "syntax error near unexpected token ATOMIC_OPS" during +configure execution, this means pkg.m4 cannot be found, most probably +you should run `pkg-config` once before running `./autogen.sh` (autoreconf). + +Below we focus on the collector build using classic makefile. +For the Makefile.direct-based process, typing `make check` instead of `make` +will automatically build the collector and then run `setjmp_test` and `gctest`. +`Setjmp_test` will give you information about configuring the collector, which is +useful primarily if you have a machine that's not already supported. Gctest is +a somewhat superficial test of collector functionality. Failure is indicated +by a core dump or a message to the effect that the collector is broken. Gctest +takes about a second to two to run on reasonable 2007 vintage desktops. It may +use up to about 30MB of memory. (The multi-threaded version will use more. +64-bit versions may use more.) `make test` will also, as its last step, attempt +to build and test the "cord" string library.) + +Makefile.direct will generate a library gc.a which you should link against. +Typing "make cords" will add the cord library to gc.a. + +The GNU style build process understands the usual targets. `make check` +runs a number of tests. `make install` installs at least libgc, and libcord. +Try `./configure --help` to see the configuration options. It is currently +not possible to exercise all combinations of build options this way. + +It is suggested that if you need to replace a piece of the collector +(e.g. GC_mark_rts.c) you simply list your version ahead of gc.a on the +ld command line, rather than replacing the one in gc.a. (This will +generate numerous warnings under some versions of AIX, but it still +works.) + +All include files that need to be used by clients will be put in the +include subdirectory. (Normally this is just gc.h. `make cords` adds +"cord.h" and "ec.h".) + +The collector currently is designed to run essentially unmodified on +machines that use a flat 32-bit or 64-bit address space. +That includes the vast majority of Workstations and X86 (X >= 3) PCs. +(The list here was deleted because it was getting too long and constantly +out of date.) + +In a few cases (Amiga, OS/2, Win32, MacOS) a separate makefile +or equivalent is supplied. Many of these have separate README.system +files. + +Dynamic libraries are completely supported only under SunOS/Solaris, +(and even that support is not functional on the last Sun 3 release), +Linux, FreeBSD, NetBSD, IRIX 5&6, HP/UX, Win32 (not Win32S) and OSF/1 +on DEC AXP machines plus perhaps a few others listed near the top +of dyn_load.c. On other machines we recommend that you do one of +the following: + + 1) Add dynamic library support (and send us the code). + 2) Use static versions of the libraries. + 3) Arrange for dynamic libraries to use the standard malloc. + This is still dangerous if the library stores a pointer to a + garbage collected object. But nearly all standard interfaces + prohibit this, because they deal correctly with pointers + to stack allocated objects. (Strtok is an exception. Don't + use it.) + +In all cases we assume that pointer alignment is consistent with that +enforced by the standard C compilers. If you use a nonstandard compiler +you may have to adjust the alignment parameters defined in gc_priv.h. +Note that this may also be an issue with packed records/structs, if those +enforce less alignment for pointers. + +A port to a machine that is not byte addressed, or does not use 32 bit +or 64 bit addresses will require a major effort. A port to plain MSDOS +or win16 is hard. + +For machines not already mentioned, or for nonstandard compilers, +some porting suggestions are provided [here](doc/porting.md). + + +## The C Interface to the Allocator + +The following routines are intended to be directly called by the user. +Note that usually only `GC_malloc` is necessary. `GC_clear_roots` and +`GC_add_roots` calls may be required if the collector has to trace +from nonstandard places (e.g. from dynamic library data areas on a +machine on which the collector doesn't already understand them.) On +some machines, it may be desirable to set `GC_stacktop` to a good +approximation of the stack base. (This enhances code portability on +HP PA machines, since there is no good way for the collector to +compute this value.) Client code may include "gc.h", which defines +all of the following, plus many others. + + 1) `GC_malloc(nbytes)` + - Allocate an object of size nbytes. Unlike malloc, the object is + cleared before being returned to the user. `GC_malloc` will + invoke the garbage collector when it determines this to be appropriate. + GC_malloc may return 0 if it is unable to acquire sufficient + space from the operating system. This is the most probable + consequence of running out of space. Other possible consequences + are that a function call will fail due to lack of stack space, + or that the collector will fail in other ways because it cannot + maintain its internal data structures, or that a crucial system + process will fail and take down the machine. Most of these + possibilities are independent of the malloc implementation. + + 2) `GC_malloc_atomic(nbytes)` + - Allocate an object of size nbytes that is guaranteed not to contain any + pointers. The returned object is not guaranteed to be cleared. + (Can always be replaced by `GC_malloc`, but results in faster collection + times. The collector will probably run faster if large character + arrays, etc. are allocated with `GC_malloc_atomic` than if they are + statically allocated.) + + 3) `GC_realloc(object, new_size)` + - Change the size of object to be `new_size`. Returns a pointer to the + new object, which may, or may not, be the same as the pointer to + the old object. The new object is taken to be atomic if and only if the + old one was. If the new object is composite and larger than the original + object,then the newly added bytes are cleared (we hope). This is very + likely to allocate a new object, unless `MERGE_SIZES` is defined in + gc_priv.h. Even then, it is likely to recycle the old object only if the + object is grown in small additive increments (which, we claim, is + generally bad coding practice.) + + 4) `GC_free(object)` + - Explicitly deallocate an object returned by `GC_malloc` or + `GC_malloc_atomic`. Not necessary, but can be used to minimize + collections if performance is critical. Probably a performance + loss for very small objects (<= 8 bytes). + + 5) `GC_expand_hp(bytes)` + - Explicitly increase the heap size. (This is normally done automatically + if a garbage collection failed to `GC_reclaim` enough memory. Explicit + calls to `GC_expand_hp` may prevent unnecessarily frequent collections at + program startup.) + + 6) `GC_malloc_ignore_off_page(bytes)` + - Identical to `GC_malloc`, but the client promises to keep a pointer to + the somewhere within the first 256 bytes of the object while it is + live. (This pointer should normally be declared volatile to prevent + interference from compiler optimizations.) This is the recommended + way to allocate anything that is likely to be larger than 100 Kbytes + or so. (`GC_malloc` may result in failure to reclaim such objects.) + + 7) `GC_set_warn_proc(proc)` + - Can be used to redirect warnings from the collector. Such warnings + should be rare, and should not be ignored during code development. + + 8) `GC_enable_incremental()` + - Enables generational and incremental collection. Useful for large + heaps on machines that provide access to page dirty information. + Some dirty bit implementations may interfere with debugging + (by catching address faults) and place restrictions on heap arguments + to system calls (since write faults inside a system call may not be + handled well). + + 9) Several routines to allow for registration of finalization code. + User supplied finalization code may be invoked when an object becomes + unreachable. To call `(*f)(obj, x)` when obj becomes inaccessible, use + `GC_register_finalizer(obj, f, x, 0, 0);` + For more sophisticated uses, and for finalization ordering issues, + see gc.h. + +The global variable `GC_free_space_divisor` may be adjusted up from it +default value of 3 to use less space and more collection time, or down for +the opposite effect. Setting it to 1 will almost disable collections +and cause all allocations to simply grow the heap. + +The variable `GC_non_gc_bytes`, which is normally 0, may be changed to reflect +the amount of memory allocated by the above routines that should not be +considered as a candidate for collection. Careless use may, of course, result +in excessive memory consumption. + +Some additional tuning is possible through the parameters defined +near the top of gc_priv.h. + +If only `GC_malloc` is intended to be used, it might be appropriate to define: + + #define malloc(n) GC_malloc(n) + #define calloc(m,n) GC_malloc((m)*(n)) + +For small pieces of VERY allocation intensive code, gc_inl.h includes +some allocation macros that may be used in place of `GC_malloc` and +friends. + +All externally visible names in the garbage collector start with `GC_`. +To avoid name conflicts, client code should avoid this prefix, except when +accessing garbage collector routines or variables. + +There are provisions for allocation with explicit type information. +This is rarely necessary. Details can be found in gc_typed.h. + + +## The C++ Interface to the Allocator + +The Ellis-Hull C++ interface to the collector is included in +the collector distribution. If you intend to use this, type +`make c++` after the initial build of the collector is complete. +See gc_cpp.h for the definition of the interface. This interface +tries to approximate the Ellis-Detlefs C++ garbage collection +proposal without compiler changes. + +Very often it will also be necessary to use gc_allocator.h and the +allocator declared there to construct STL data structures. Otherwise +subobjects of STL data structures will be allocated using a system +allocator, and objects they refer to may be prematurely collected. + + +## Use as Leak Detector + +The collector may be used to track down leaks in C programs that are +intended to run with malloc/free (e.g. code with extreme real-time or +portability constraints). To do so define `FIND_LEAK` in Makefile. +This will cause the collector to invoke the `report_leak` +routine defined near the top of reclaim.c whenever an inaccessible +object is found that has not been explicitly freed. Such objects will +also be automatically reclaimed. + +If all objects are allocated with `GC_DEBUG_MALLOC` (see next section), then +the default version of report_leak will report at least the source file and +line number at which the leaked object was allocated. This may sometimes be +sufficient. (On a few machines, it will also report a cryptic stack trace. +If this is not symbolic, it can sometimes be called into a symbolic stack +trace by invoking program "foo" with "tools/callprocs.sh foo". It is a short +shell script that invokes adb to expand program counter values to symbolic +addresses. It was largely supplied by Scott Schwartz.) + +Note that the debugging facilities described in the next section can +sometimes be slightly LESS effective in leak finding mode, since in +leak finding mode, `GC_debug_free` actually results in reuse of the object. +(Otherwise the object is simply marked invalid.) Also note that the test +program is not designed to run meaningfully in `FIND_LEAK` mode. +Use "make gc.a" to build the collector. + + +## Debugging Facilities + +The routines `GC_debug_malloc`, `GC_debug_malloc_atomic`, `GC_debug_realloc`, +and `GC_debug_free` provide an alternate interface to the collector, which +provides some help with memory overwrite errors, and the like. +Objects allocated in this way are annotated with additional +information. Some of this information is checked during garbage +collections, and detected inconsistencies are reported to stderr. + +Simple cases of writing past the end of an allocated object should +be caught if the object is explicitly deallocated, or if the +collector is invoked while the object is live. The first deallocation +of an object will clear the debugging info associated with an +object, so accidentally repeated calls to `GC_debug_free` will report the +deallocation of an object without debugging information. Out of +memory errors will be reported to stderr, in addition to returning `NULL`. + +`GC_debug_malloc` checking during garbage collection is enabled +with the first call to `GC_debug_malloc`. This will result in some +slowdown during collections. If frequent heap checks are desired, +this can be achieved by explicitly invoking `GC_gcollect`, e.g. from +the debugger. + +`GC_debug_malloc` allocated objects should not be passed to `GC_realloc` +or `GC_free`, and conversely. It is however acceptable to allocate only +some objects with `GC_debug_malloc`, and to use `GC_malloc` for other objects, +provided the two pools are kept distinct. In this case, there is a very +low probability that `GC_malloc` allocated objects may be misidentified as +having been overwritten. This should happen with probability at most +one in 2**32. This probability is zero if `GC_debug_malloc` is never called. + +`GC_debug_malloc`, `GC_malloc_atomic`, and `GC_debug_realloc` take two +additional trailing arguments, a string and an integer. These are not +interpreted by the allocator. They are stored in the object (the string is +not copied). If an error involving the object is detected, they are printed. + +The macros `GC_MALLOC`, `GC_MALLOC_ATOMIC`, `GC_REALLOC`, `GC_FREE`, and +`GC_REGISTER_FINALIZER` are also provided. These require the same arguments +as the corresponding (nondebugging) routines. If gc.h is included +with `GC_DEBUG` defined, they call the debugging versions of these +functions, passing the current file name and line number as the two +extra arguments, where appropriate. If gc.h is included without `GC_DEBUG` +defined, then all these macros will instead be defined to their nondebugging +equivalents. (`GC_REGISTER_FINALIZER` is necessary, since pointers to +objects with debugging information are really pointers to a displacement +of 16 bytes form the object beginning, and some translation is necessary +when finalization routines are invoked. For details, about what's stored +in the header, see the definition of the type oh in dbg_mlc.c file.) + + +## Incremental/Generational Collection + +The collector normally interrupts client code for the duration of +a garbage collection mark phase. This may be unacceptable if interactive +response is needed for programs with large heaps. The collector +can also run in a "generational" mode, in which it usually attempts to +collect only objects allocated since the last garbage collection. +Furthermore, in this mode, garbage collections run mostly incrementally, +with a small amount of work performed in response to each of a large number of +`GC_malloc` requests. + +This mode is enabled by a call to `GC_enable_incremental`. + +Incremental and generational collection is effective in reducing +pause times only if the collector has some way to tell which objects +or pages have been recently modified. The collector uses two sources +of information: + +1. Information provided by the VM system. This may be provided in + one of several forms. Under Solaris 2.X (and potentially under other + similar systems) information on dirty pages can be read from the + /proc file system. Under other systems (currently SunOS4.X) it is + possible to write-protect the heap, and catch the resulting faults. + On these systems we require that system calls writing to the heap + (other than read) be handled specially by client code. + See os_dep.c for details. + +2. Information supplied by the programmer. The object is considered dirty + after a call to `GC_end_stubborn_change` provided the library has been + compiled suitably. It is typically not worth using for short-lived objects. + Note that bugs caused by a missing `GC_end_stubborn_change` call are + likely to be observed very infrequently and hard to trace. + + +## Bugs + +Any memory that does not have a recognizable pointer to it will be +reclaimed. Exclusive-or'ing forward and backward links in a list +doesn't cut it. + +Some C optimizers may lose the last undisguised pointer to a memory +object as a consequence of clever optimizations. This has almost +never been observed in practice. + +This is not a real-time collector. In the standard configuration, +percentage of time required for collection should be constant across +heap sizes. But collection pauses will increase for larger heaps. +They will decrease with the number of processors if parallel marking +is enabled. + +(On 2007 vintage machines, GC times may be on the order of 5 msecs +per MB of accessible memory that needs to be scanned and processor. +Your mileage may vary.) The incremental/generational collection facility +may help in some cases. + + +## Feedback, Contribution, Questions and Notifications + +Please address bug reports and new feature ideas to +[GitHub issues](https://github.com/ivmai/bdwgc/issues). Before the +submission please check that it has not been done yet by someone else. + +If you want to contribute, submit +a [pull request](https://github.com/ivmai/bdwgc/pulls) to GitHub. + +If you need help, use +[Stack Overflow](https://stackoverflow.com/questions/tagged/boehm-gc). +Older technical discussions are available in `bdwgc` mailing list archive - it +can be downloaded as a +[compressed file](https://github.com/ivmai/bdwgc/files/1038163/bdwgc-mailing-list-archive-2017_04.tar.gz) +or browsed at [Narkive](http://bdwgc.opendylan.narkive.com). + +To get new release announcements, subscribe to +[RSS feed](https://github.com/ivmai/bdwgc/releases.atom). +(To receive the notifications by email, a 3rd-party free service like +[IFTTT RSS Feed](https://ifttt.com/feed) can be setup.) +To be notified on all issues, please +[watch](https://github.com/ivmai/bdwgc/watchers) the project on +GitHub. + + +## Copyright & Warranty + + * Copyright (c) 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1996 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999-2011 by Hewlett-Packard Development Company. + +The files pthread_stop_world.c and pthread_support.c are also + + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + +The files Makefile.am, and configure.in are + +* Copyright (c) 2001 by Red Hat Inc. All rights reserved. + +Several files supporting GNU-style builds are copyrighted by the Free +Software Foundation, and carry a different license from that given +below. The files included in the libatomic_ops distribution (included +here) use either the license below, or a similar MIT-style license, +or, for some files not actually used by the garbage-collector library, the +GPL. + +THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED +OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + +Permission is hereby granted to use or copy this program +for any purpose, provided the above notices are retained on all copies. +Permission to modify the code and to distribute modified code is granted, +provided the above notices are retained, and a notice that the code was +modified is included with the above copyright notice. + +A few of the files needed to use the GNU-style build procedure come with +slightly different licenses, though they are all similar in spirit. A few +are GPL'ed, but with an exception that should cover all uses in the +collector. (If you are concerned about such things, I recommend you look +at the notice in config.guess or ltmain.sh.) + +The atomic_ops library contains some code that is covered by the GNU General +Public License, but is not needed by, nor linked into the collector library. +It is included here only because the atomic_ops distribution is, for +simplicity, included in its entirety. diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/SMakefile.amiga b/unity-2019.4.24f1-mbe/external/bdwgc/SMakefile.amiga new file mode 100644 index 000000000..5e27b22a4 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/SMakefile.amiga @@ -0,0 +1,172 @@ + +# Rewritten smakefile for amiga / sas/c. -Kjetil M. +# Don't use the cord-package if you define parm=both or parm=reg. + + +#----------------TOOLS-------------------------------- +CC=sc +LINKER=slink +LIBER=oml + +#----------------CPU OPTIONS-------------------------- + +CPU=68060 + +#----------------FPU OPTIONS-------------------------- + +MATH=8 +MATHLIB=LIB:scm881.lib + +#----------------COMPILER OPTIONS--------------------- + +IGNORE= IGNORE=85 IGNORE=154 IGNORE=161 IGNORE=100 + +OPTIMIZE=optimize optimizetime optglobal optimizerdepth=100 optimizerpeephole optloop OPTSCHED optimizerinlocal optimizerrecurdepth=100 +# optimizerinline optimizercomplexity=100 + +OPT= $(OPTIMIZE) CPU=$(CPU) math=$(MATH) NOSTACKCHECK VERBOSE \ +MAPHUNK NOVERSION NOICONS nodebug \ +parm=reg \ +DEFINE __USE_SYSBASE + + +SOPT= $(OPT) $(IGNORE) \ +DEFINE AMIGA_SKIP_SEG \ +DEFINE GC_ATOMIC_UNCOLLECTABLE \ +DEFINE GC_AMIGA_FASTALLOC \ +DEFINE GC_AMIGA_RETRY \ +DEFINE GC_AMIGA_PRINTSTATS \ +DEFINE GC_AMIGA_GC + + +#DEFINE ALL_INTERIOR_POINTERS \ + + +SCOPT= $(SOPT) define GC_AMIGA_MAKINGLIB + +CSCOPT= $(OPT) DEFINE AMIGA IGNORE=100 IGNORE=161 + +#------------------LINKING---------------------------- + + +all: gctest setjmp_t cord/cordtest + +clean: + delete *.lib gctest setjmp_t *.o *.lnk cord/*.o cord/*.lib cord/*.lnk cord/cordtest + smake + +test: setjmp_t gctest cord/cordtest + setjmp_t + gctest + cord/cordtest + +gctest: gc$(CPU).lib GCAmigaOS$(CPU).lib test.o + $(LINKER) LIB:c.o test.o TO gctest LIB gc$(CPU).lib LIB:sc.lib $(MATHLIB) + +setjmp_t: setjmp_t.o gc.h + $(LINKER) LIB:c.o setjmp_t.o to setjmp_t lib LIB:sc.lib + +cord/cordtest: cord/cordtest.o cord/cord$(CPU).lib gc$(CPU).lib + slink LIB:c.o cord/cordtest.o LIB $(MATHLIB) gc$(CPU).lib cord/cord$(CPU).lib LIB:sc.lib TO cord/cordtest + + +#------------------LIBBING---------------------------- + +OBJS= alloc.o reclaim.o allchblk.o misc.o mach_dep.o os_dep.o mark_rts.o headers.o mark.o obj_map.o blacklst.o finalize.o new_hblk.o dyn_load.o dbg_mlc.o malloc.o checksums.o typd_mlc.o ptr_chck.o mallocx.o fnlz_mlc.o + +gc$(CPU).lib: $(OBJS) + $(LIBER) gc$(CPU).lib r $(OBJS) + + +COBJS = cord/cordbscs.o cord/cordprnt.o cord/cordxtra.o + +cord/cord$(CPU).lib: $(COBJS) + oml cord/cord$(CPU).lib r $(COBJS) + +#------------------COMPILING-------------------------- + +INC= gc_private.h gc_hdrs.h gc.h gcconfig.h + +alloc.o : alloc.c $(INC) + $(CC) alloc.c $(SCOPT) ignore=7 + +reclaim.o : reclaim.c $(INC) + $(CC) reclaim.c $(SCOPT) + +allchblk.o : allchblk.c $(INC) + $(CC) allchblk.c $(SCOPT) + +misc.o : misc.c $(INC) + $(CC) misc.c $(SCOPT) + +os_dep.o : os_dep.c $(INC) extra/AmigaOS.c + $(CC) os_dep.c $(SCOPT) + +mark_rts.o : mark_rts.c $(INC) + $(CC) mark_rts.c $(SCOPT) + +headers.o : headers.c $(INC) + $(CC) headers.c $(SCOPT) + +mark.o : mark.c $(INC) + $(CC) mark.c $(SCOPT) + +obj_map.o : obj_map.c $(INC) + $(CC) obj_map.c $(SCOPT) + +blacklst.o : blacklst.c $(INC) + $(CC) blacklst.c $(SCOPT) + +finalize.o : finalize.c $(INC) + $(CC) finalize.c $(SCOPT) noopt +# Could sas/c still have problems with this one? Gctest sometimes fails to finalize all. + +new_hblk.o : new_hblk.c $(INC) + $(CC) new_hblk.c $(SCOPT) + +dyn_load.o : dyn_load.c $(INC) + $(CC) dyn_load.c $(SCOPT) + +dbg_mlc.o : dbg_mlc.c $(INC) + $(CC) dbg_mlc.c $(SCOPT) + +fnlz_mlc.o : fnlz_mlc.c $(INC) + $(CC) fnlz_mlc.c $(SCOPT) + +malloc.o : malloc.c $(INC) + $(CC) malloc.c $(SCOPT) + +mallocx.o : mallocx.c $(INC) + $(CC) mallocx.c $(SCOPT) + +checksums.o : checksums.c $(INC) + $(CC) checksums.c $(SCOPT) + +typd_mlc.o: typd_mlc.c $(INC) + $(CC) typd_mlc.c $(SCOPT) + +mach_dep.o : mach_dep.c $(INC) + $(CC) mach_dep.c $(SCOPT) + +ptr_chck.o: ptr_chck.c $(INC) + $(CC) ptr_chck.c $(SCOPT) + +test.o : test.c $(INC) + $(CC) test.c $(SOPT) + +setjmp_t: tools/setjmp_t.c gc.h + $(CC) tools/setjmp_t.c $(SOPT) + +# cords: + +cord/cordbscs.o: cord/cordbscs.c + sc cord/cordbscs.c $(CSCOPT) + +cord/cordprnt.o: cord/cordprnt.c + sc cord/cordprnt.c $(CSCOPT) + +cord/cordxtra.o: cord/cordxtra.c + sc cord/cordxtra.c $(CSCOPT) + +cord/cordtest.o: cord/tests/cordtest.c + sc cord/tests/cordtest.c $(CSCOPT) diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/WCC_MAKEFILE b/unity-2019.4.24f1-mbe/external/bdwgc/WCC_MAKEFILE new file mode 100644 index 000000000..48ba59ae6 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/WCC_MAKEFILE @@ -0,0 +1,166 @@ +# Makefile for Watcom C/C++ 10.5, 10.6, 11.0 on NT, OS2 and DOS4GW. +# May work with Watcom 10.0. + +# Uncomment one of the lines below for cross compilation. +SYSTEM=MSWIN32 +#SYSTEM=DOS4GW +#SYSTEM=OS2 + +# The collector can be built either as dynamic or as static library. +# Select the library type you need. +#MAKE_AS_DLL=1 +MAKE_AS_LIB=1 + +# Select calling conventions. +# Possible choices are r and s. +CALLING=s + +# Select target CPU. +# Possible choices are 3, 4, 5, and 6. +# The last choice available only since version 11.0. +CPU=5 + +# Set optimization options. +# Watcom before 11.0 does not support option "-oh". +OPTIM=-oneatx -s +#OPTIM=-ohneatx -s + +DEFS=-DALL_INTERIOR_POINTERS -DENABLE_DISCLAIM #-DSMALL_CONFIG #-DGC_DEBUG + + +##### + +!ifndef SYSTEM +!ifdef __MSDOS__ +SYSTEM=DOS4GW +!else ifdef __NT__ +SYSTEM=MSWIN32 +!else ifdef __OS2__ +SYSTEM=OS2 +!else +SYSTEM=Unknown +!endif +!endif + +!define $(SYSTEM) + +!ifdef DOS4GW +SYSFLAG=-DDOS4GW -bt=dos +!else ifdef MSWIN32 +SYSFLAG=-DMSWIN32 -bt=nt +!else ifdef OS2 +SYSFLAG=-DOS2 -bt=os2 +!else +!error undefined or unsupported target platform: $(SYSTEM) +!endif +!ifdef MAKE_AS_DLL +DLLFLAG=-bd -DGC_DLL +TEST_DLLFLAG=-DGC_DLL +!else ifdef MAKE_AS_LIB +DLLFLAG= +TEST_DLLFLAG= +!else +!error Either MAKE_AS_LIB or MAKE_AS_DLL should be defined +!endif + +CC=wcc386 +CXX=wpp386 + +CFLAGS=-$(CPU)$(CALLING) $(OPTIM) -zp4 -zc $(SYSFLAG) $(DLLFLAG) $(DEFS) +CXXFLAGS= $(CFLAGS) +TEST_CFLAGS=-$(CPU)$(CALLING) $(OPTIM) -zp4 -zc $(SYSFLAG) $(TEST_DLLFLAG) $(DEFS) +TEST_CXXFLAGS= $(TEST_CFLAGS) + +OBJS= alloc.obj reclaim.obj allchblk.obj misc.obj & + mach_dep.obj os_dep.obj mark_rts.obj headers.obj mark.obj & + obj_map.obj blacklst.obj finalize.obj new_hblk.obj & + dbg_mlc.obj malloc.obj dyn_load.obj & + typd_mlc.obj ptr_chck.obj mallocx.obj fnlz_mlc.obj + +all: gc.lib gctest.exe test_cpp.exe + +!ifdef MAKE_AS_DLL + +gc.lib: gc.dll gc_cpp.obj + *wlib -b -c -n -p=512 $@ +gc.dll +gc_cpp.obj + +gc.dll: $(OBJS) .AUTODEPEND + @%create $*.lnk +!ifdef DOS4GW + @%append $*.lnk sys os2v2_dll +!else ifdef MSWIN32 + @%append $*.lnk sys nt_dll +!else ifdef OS2 + @%append $*.lnk sys os2v2_dll +!endif + @%append $*.lnk name $* + @for %i in ($(OBJS)) do @%append $*.lnk file '%i' + *wlink @$*.lnk +!else +gc.lib: $(OBJS) gc_cpp.obj + @%create $*.lb1 + @for %i in ($(OBJS)) do @%append $*.lb1 +'%i' + @%append $*.lb1 +'gc_cpp.obj' + *wlib -b -c -n -p=512 $@ @$*.lb1 + +!endif + + +gctest.exe: test.obj gc.lib + %create $*.lnk +!ifdef DOS4GW + @%append $*.lnk sys dos4g +!else ifdef MSWIN32 + @%append $*.lnk sys nt +!else ifdef OS2 + @%append $*.lnk sys os2v2 +!endif + @%append $*.lnk op case + @%append $*.lnk op stack=256K + @%append $*.lnk name $* + @%append $*.lnk file test.obj + @%append $*.lnk library gc.lib + *wlink @$*.lnk +test_cpp.exe: test_cpp.obj gc.lib + %create $*.lnk +!ifdef DOS4GW + @%append $*.lnk sys dos4g +!else ifdef MSWIN32 + @%append $*.lnk sys nt +!else ifdef OS2 + @%append $*.lnk sys os2v2 +!endif + @%append $*.lnk op case + @%append $*.lnk op stack=256K + @%append $*.lnk name $* + @%append $*.lnk file test_cpp.obj + @%append $*.lnk library gc.lib + *wlink @$*.lnk + +gc_cpp.obj: gc_cpp.cc .AUTODEPEND + $(CXX) $(TEST_CXXFLAGS) -iinclude $*.cc +test.obj: tests\test.c .AUTODEPEND + $(CC) $(TEST_CFLAGS) $*.c +test_cpp.obj: tests\test_cpp.cc .AUTODEPEND + $(CXX) $(TEST_CXXFLAGS) -iinclude $*.cc + + +.c.obj: .AUTODEPEND + $(CC) $(CFLAGS) $*.c + +.cc.obj: .AUTODEPEND + $(CXX) $(CXXFLAGS) $*.cc + +clean : .SYMBOLIC + @if exist *.obj del *.obj + @if exist *.map del *.map + @if exist *.lnk del *.lnk + @if exist *.lb1 del *.lb1 + @if exist *.sym del *.sym + @if exist *.err del *.err + @if exist *.tmp del *.tmp + @if exist *.lst del *.lst + @if exist *.exe del *.exe + @if exist *.log del *.log + @if exist *.lib del *.lib + @if exist *.dll del *.dll diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/allchblk.c b/unity-2019.4.24f1-mbe/external/bdwgc/allchblk.c new file mode 100644 index 000000000..7dfa6c591 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/allchblk.c @@ -0,0 +1,923 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1998-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#include "private/gc_priv.h" + +#include + +#ifdef GC_USE_ENTIRE_HEAP + int GC_use_entire_heap = TRUE; +#else + int GC_use_entire_heap = FALSE; +#endif + +/* + * Free heap blocks are kept on one of several free lists, + * depending on the size of the block. Each free list is doubly linked. + * Adjacent free blocks are coalesced. + */ + + +# define MAX_BLACK_LIST_ALLOC (2*HBLKSIZE) + /* largest block we will allocate starting on a black */ + /* listed block. Must be >= HBLKSIZE. */ + + +# define UNIQUE_THRESHOLD 32 + /* Sizes up to this many HBLKs each have their own free list */ +# define HUGE_THRESHOLD 256 + /* Sizes of at least this many heap blocks are mapped to a */ + /* single free list. */ +# define FL_COMPRESSION 8 + /* In between sizes map this many distinct sizes to a single */ + /* bin. */ + +# define N_HBLK_FLS ((HUGE_THRESHOLD - UNIQUE_THRESHOLD) / FL_COMPRESSION \ + + UNIQUE_THRESHOLD) + +#ifndef GC_GCJ_SUPPORT + STATIC +#endif + struct hblk * GC_hblkfreelist[N_HBLK_FLS+1] = { 0 }; + /* List of completely empty heap blocks */ + /* Linked through hb_next field of */ + /* header structure associated with */ + /* block. Remains externally visible */ + /* as used by GNU GCJ currently. */ + +#ifndef GC_GCJ_SUPPORT + STATIC +#endif + word GC_free_bytes[N_HBLK_FLS+1] = { 0 }; + /* Number of free bytes on each list. Remains visible to GCJ. */ + +/* Return the largest n such that the number of free bytes on lists */ +/* n .. N_HBLK_FLS is greater or equal to GC_max_large_allocd_bytes */ +/* minus GC_large_allocd_bytes. If there is no such n, return 0. */ +GC_INLINE int GC_enough_large_bytes_left(void) +{ + int n; + word bytes = GC_large_allocd_bytes; + + GC_ASSERT(GC_max_large_allocd_bytes <= GC_heapsize); + for (n = N_HBLK_FLS; n >= 0; --n) { + bytes += GC_free_bytes[n]; + if (bytes >= GC_max_large_allocd_bytes) return n; + } + return 0; +} + +/* Map a number of blocks to the appropriate large block free list index. */ +STATIC int GC_hblk_fl_from_blocks(word blocks_needed) +{ + if (blocks_needed <= UNIQUE_THRESHOLD) return (int)blocks_needed; + if (blocks_needed >= HUGE_THRESHOLD) return N_HBLK_FLS; + return (int)(blocks_needed - UNIQUE_THRESHOLD)/FL_COMPRESSION + + UNIQUE_THRESHOLD; + +} + +# define PHDR(hhdr) HDR((hhdr) -> hb_prev) +# define NHDR(hhdr) HDR((hhdr) -> hb_next) + +# ifdef USE_MUNMAP +# define IS_MAPPED(hhdr) (((hhdr) -> hb_flags & WAS_UNMAPPED) == 0) +# else +# define IS_MAPPED(hhdr) TRUE +# endif /* !USE_MUNMAP */ + +#if !defined(NO_DEBUGGING) || defined(GC_ASSERTIONS) + /* Should return the same value as GC_large_free_bytes. */ + GC_INNER word GC_compute_large_free_bytes(void) + { + word total_free = 0; + unsigned i; + + for (i = 0; i <= N_HBLK_FLS; ++i) { + struct hblk * h; + hdr * hhdr; + + for (h = GC_hblkfreelist[i]; h != 0; h = hhdr->hb_next) { + hhdr = HDR(h); + total_free += hhdr->hb_sz; + } + } + return total_free; + } +#endif /* !NO_DEBUGGING || GC_ASSERTIONS */ + +# if !defined(NO_DEBUGGING) +void GC_print_hblkfreelist(void) +{ + unsigned i; + word total; + + for (i = 0; i <= N_HBLK_FLS; ++i) { + struct hblk * h = GC_hblkfreelist[i]; + + if (0 != h) GC_printf("Free list %u (total size %lu):\n", + i, (unsigned long)GC_free_bytes[i]); + while (h != 0) { + hdr * hhdr = HDR(h); + + GC_printf("\t%p size %lu %s black listed\n", + (void *)h, (unsigned long) hhdr -> hb_sz, + GC_is_black_listed(h, HBLKSIZE) != 0 ? "start" : + GC_is_black_listed(h, hhdr -> hb_sz) != 0 ? "partially" : + "not"); + h = hhdr -> hb_next; + } + } + GC_printf("GC_large_free_bytes: %lu\n", + (unsigned long)GC_large_free_bytes); + + if ((total = GC_compute_large_free_bytes()) != GC_large_free_bytes) + GC_err_printf("GC_large_free_bytes INCONSISTENT!! Should be: %lu\n", + (unsigned long)total); +} + +/* Return the free list index on which the block described by the header */ +/* appears, or -1 if it appears nowhere. */ +static int free_list_index_of(hdr *wanted) +{ + int i; + + for (i = 0; i <= N_HBLK_FLS; ++i) { + struct hblk * h; + hdr * hhdr; + + for (h = GC_hblkfreelist[i]; h != 0; h = hhdr -> hb_next) { + hhdr = HDR(h); + if (hhdr == wanted) return i; + } + } + return -1; +} + +GC_API void GC_CALL GC_dump_regions(void) +{ + unsigned i; + + for (i = 0; i < GC_n_heap_sects; ++i) { + ptr_t start = GC_heap_sects[i].hs_start; + size_t bytes = GC_heap_sects[i].hs_bytes; + ptr_t end = start + bytes; + ptr_t p; + + /* Merge in contiguous sections. */ + while (i+1 < GC_n_heap_sects && GC_heap_sects[i+1].hs_start == end) { + ++i; + end = GC_heap_sects[i].hs_start + GC_heap_sects[i].hs_bytes; + } + GC_printf("***Section from %p to %p\n", (void *)start, (void *)end); + for (p = start; (word)p < (word)end; ) { + hdr *hhdr = HDR(p); + + if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + GC_printf("\t%p Missing header!!(%p)\n", + (void *)p, (void *)hhdr); + p += HBLKSIZE; + continue; + } + if (HBLK_IS_FREE(hhdr)) { + int correct_index = GC_hblk_fl_from_blocks( + divHBLKSZ(hhdr -> hb_sz)); + int actual_index; + + GC_printf("\t%p\tfree block of size 0x%lx bytes%s\n", + (void *)p, (unsigned long)(hhdr -> hb_sz), + IS_MAPPED(hhdr) ? "" : " (unmapped)"); + actual_index = free_list_index_of(hhdr); + if (-1 == actual_index) { + GC_printf("\t\tBlock not on free list %d!!\n", + correct_index); + } else if (correct_index != actual_index) { + GC_printf("\t\tBlock on list %d, should be on %d!!\n", + actual_index, correct_index); + } + p += hhdr -> hb_sz; + } else { + GC_printf("\t%p\tused for blocks of size 0x%lx bytes\n", + (void *)p, (unsigned long)(hhdr -> hb_sz)); + p += HBLKSIZE * OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz); + } + } + } +} + +# endif /* NO_DEBUGGING */ + +/* Initialize hdr for a block containing the indicated size and */ +/* kind of objects. */ +/* Return FALSE on failure. */ +static GC_bool setup_header(hdr * hhdr, struct hblk *block, size_t byte_sz, + int kind, unsigned flags) +{ + word descr; + +# ifdef MARK_BIT_PER_GRANULE + if (byte_sz > MAXOBJBYTES) + flags |= LARGE_BLOCK; +# endif +# ifdef ENABLE_DISCLAIM + if (GC_obj_kinds[kind].ok_disclaim_proc) + flags |= HAS_DISCLAIM; + if (GC_obj_kinds[kind].ok_mark_unconditionally) + flags |= MARK_UNCONDITIONALLY; +# endif + + /* Set size, kind and mark proc fields */ + hhdr -> hb_sz = byte_sz; + hhdr -> hb_obj_kind = (unsigned char)kind; + hhdr -> hb_flags = (unsigned char)flags; + hhdr -> hb_block = block; + descr = GC_obj_kinds[kind].ok_descriptor; + if (GC_obj_kinds[kind].ok_relocate_descr) descr += byte_sz; + hhdr -> hb_descr = descr; + +# ifdef MARK_BIT_PER_OBJ + /* Set hb_inv_sz as portably as possible. */ + /* We set it to the smallest value such that sz * inv_sz > 2**32 */ + /* This may be more precision than necessary. */ + if (byte_sz > MAXOBJBYTES) { + hhdr -> hb_inv_sz = LARGE_INV_SZ; + } else { + word inv_sz; + +# if CPP_WORDSZ == 64 + inv_sz = ((word)1 << 32)/byte_sz; + if (((inv_sz*byte_sz) >> 32) == 0) ++inv_sz; +# else /* 32 bit words */ + GC_ASSERT(byte_sz >= 4); + inv_sz = ((unsigned)1 << 31)/byte_sz; + inv_sz *= 2; + while (inv_sz*byte_sz > byte_sz) ++inv_sz; +# endif + hhdr -> hb_inv_sz = inv_sz; + } +# endif +# ifdef MARK_BIT_PER_GRANULE + { + size_t granules = BYTES_TO_GRANULES(byte_sz); + + if (EXPECT(!GC_add_map_entry(granules), FALSE)) { + /* Make it look like a valid block. */ + hhdr -> hb_sz = HBLKSIZE; + hhdr -> hb_descr = 0; + hhdr -> hb_flags |= LARGE_BLOCK; + hhdr -> hb_map = 0; + return FALSE; + } + hhdr -> hb_map = GC_obj_map[(hhdr -> hb_flags & LARGE_BLOCK) != 0 ? + 0 : granules]; + } +# endif /* MARK_BIT_PER_GRANULE */ + + /* Clear mark bits */ + GC_clear_hdr_marks(hhdr); + + hhdr -> hb_last_reclaimed = (unsigned short)GC_gc_no; + return(TRUE); +} + +/* Remove hhdr from the free list (it is assumed to specified by index). */ +STATIC void GC_remove_from_fl_at(hdr *hhdr, int index) +{ + GC_ASSERT(((hhdr -> hb_sz) & (HBLKSIZE-1)) == 0); + if (hhdr -> hb_prev == 0) { + GC_ASSERT(HDR(GC_hblkfreelist[index]) == hhdr); + GC_hblkfreelist[index] = hhdr -> hb_next; + } else { + hdr *phdr; + GET_HDR(hhdr -> hb_prev, phdr); + phdr -> hb_next = hhdr -> hb_next; + } + /* We always need index to maintain free counts. */ + GC_ASSERT(GC_free_bytes[index] >= hhdr -> hb_sz); + GC_free_bytes[index] -= hhdr -> hb_sz; + if (0 != hhdr -> hb_next) { + hdr * nhdr; + GC_ASSERT(!IS_FORWARDING_ADDR_OR_NIL(NHDR(hhdr))); + GET_HDR(hhdr -> hb_next, nhdr); + nhdr -> hb_prev = hhdr -> hb_prev; + } +} + +/* Remove hhdr from the appropriate free list (we assume it is on the */ +/* size-appropriate free list). */ +GC_INLINE void GC_remove_from_fl(hdr *hhdr) +{ + GC_remove_from_fl_at(hhdr, GC_hblk_fl_from_blocks(divHBLKSZ(hhdr->hb_sz))); +} + +/* Return a pointer to the free block ending just before h, if any. */ +STATIC struct hblk * GC_free_block_ending_at(struct hblk *h) +{ + struct hblk * p = h - 1; + hdr * phdr; + + GET_HDR(p, phdr); + while (0 != phdr && IS_FORWARDING_ADDR_OR_NIL(phdr)) { + p = FORWARDED_ADDR(p,phdr); + phdr = HDR(p); + } + if (0 != phdr) { + if(HBLK_IS_FREE(phdr)) { + return p; + } else { + return 0; + } + } + p = GC_prev_block(h - 1); + if (0 != p) { + phdr = HDR(p); + if (HBLK_IS_FREE(phdr) && (ptr_t)p + phdr -> hb_sz == (ptr_t)h) { + return p; + } + } + return 0; +} + +/* Add hhdr to the appropriate free list. */ +/* We maintain individual free lists sorted by address. */ +STATIC void GC_add_to_fl(struct hblk *h, hdr *hhdr) +{ + int index = GC_hblk_fl_from_blocks(divHBLKSZ(hhdr -> hb_sz)); + struct hblk *second = GC_hblkfreelist[index]; +# if defined(GC_ASSERTIONS) && !defined(USE_MUNMAP) + struct hblk *next = (struct hblk *)((word)h + hhdr -> hb_sz); + hdr * nexthdr = HDR(next); + struct hblk *prev = GC_free_block_ending_at(h); + hdr * prevhdr = HDR(prev); + + GC_ASSERT(nexthdr == 0 || !HBLK_IS_FREE(nexthdr) + || (GC_heapsize & SIGNB) != 0); + /* In the last case, blocks may be too large to merge. */ + GC_ASSERT(prev == 0 || !HBLK_IS_FREE(prevhdr) + || (GC_heapsize & SIGNB) != 0); +# endif + GC_ASSERT(((hhdr -> hb_sz) & (HBLKSIZE-1)) == 0); + GC_hblkfreelist[index] = h; + GC_free_bytes[index] += hhdr -> hb_sz; + GC_ASSERT(GC_free_bytes[index] <= GC_large_free_bytes); + hhdr -> hb_next = second; + hhdr -> hb_prev = 0; + if (0 != second) { + hdr * second_hdr; + + GET_HDR(second, second_hdr); + second_hdr -> hb_prev = h; + } + hhdr -> hb_flags |= FREE_BLK; +} + +#ifdef USE_MUNMAP + +# ifndef MUNMAP_THRESHOLD +# define MUNMAP_THRESHOLD 6 +# endif + +GC_INNER int GC_unmap_threshold = MUNMAP_THRESHOLD; + +/* Unmap blocks that haven't been recently touched. This is the only way */ +/* way blocks are ever unmapped. */ +GC_INNER void GC_unmap_old(void) +{ + word sz; + unsigned short last_rec, threshold; + int i; + +/* NOTE: Xbox One (DURANGO) may not need to be this aggressive, but the default + * is likely too lax under heavy allocation pressure. The platform does not + * have a virtual paging system, so it does not have a large virtual address + * space that a standard x64 platform has. + */ +#if defined(SN_TARGET_PS3) || defined(SN_TARGET_PSP2) || defined(SN_TARGET_ORBIS) || defined(_XBOX_ONE) +# define UNMAP_THRESHOLD 2 +#else +# define UNMAP_THRESHOLD 6 +#endif + + for (i = 0; i <= N_HBLK_FLS; ++i) { + struct hblk * h; + hdr * hhdr; + + for (h = GC_hblkfreelist[i]; 0 != h; h = hhdr -> hb_next) { + hhdr = HDR(h); + if (!IS_MAPPED(hhdr)) continue; + + threshold = (unsigned short)(GC_gc_no - UNMAP_THRESHOLD); + last_rec = hhdr -> hb_last_reclaimed; + if ((last_rec > GC_gc_no || last_rec < threshold) + && threshold < GC_gc_no /* not recently wrapped */) { + sz = hhdr -> hb_sz; + GC_unmap((ptr_t)h, sz); + hhdr -> hb_flags |= WAS_UNMAPPED; + } + } + } +} + +# ifdef MPROTECT_VDB + GC_INNER GC_bool GC_has_unmapped_memory(void) + { + int i; + + for (i = 0; i <= N_HBLK_FLS; ++i) { + struct hblk * h; + hdr * hhdr; + + for (h = GC_hblkfreelist[i]; h != NULL; h = hhdr -> hb_next) { + hhdr = HDR(h); + if (!IS_MAPPED(hhdr)) return TRUE; + } + } + return FALSE; + } +# endif /* MPROTECT_VDB */ + +/* Merge all unmapped blocks that are adjacent to other free */ +/* blocks. This may involve remapping, since all blocks are either */ +/* fully mapped or fully unmapped. */ +GC_INNER void GC_merge_unmapped(void) +{ + int i; + + for (i = 0; i <= N_HBLK_FLS; ++i) { + struct hblk *h = GC_hblkfreelist[i]; + + while (h != 0) { + struct hblk *next; + hdr *hhdr, *nexthdr; + word size, nextsize; + + GET_HDR(h, hhdr); + size = hhdr->hb_sz; + next = (struct hblk *)((word)h + size); + GET_HDR(next, nexthdr); + /* Coalesce with successor, if possible */ + if (0 != nexthdr && HBLK_IS_FREE(nexthdr) + && (signed_word) (size + (nextsize = nexthdr->hb_sz)) > 0 + /* no pot. overflow */) { + /* Note that we usually try to avoid adjacent free blocks */ + /* that are either both mapped or both unmapped. But that */ + /* isn't guaranteed to hold since we remap blocks when we */ + /* split them, and don't merge at that point. It may also */ + /* not hold if the merged block would be too big. */ + if (IS_MAPPED(hhdr) && !IS_MAPPED(nexthdr)) { + /* make both consistent, so that we can merge */ + if (size > nextsize) { + GC_remap((ptr_t)next, nextsize); + } else { + GC_unmap((ptr_t)h, size); + GC_unmap_gap((ptr_t)h, size, (ptr_t)next, nextsize); + hhdr -> hb_flags |= WAS_UNMAPPED; + } + } else if (IS_MAPPED(nexthdr) && !IS_MAPPED(hhdr)) { + if (size > nextsize) { + GC_unmap((ptr_t)next, nextsize); + GC_unmap_gap((ptr_t)h, size, (ptr_t)next, nextsize); + } else { + GC_remap((ptr_t)h, size); + hhdr -> hb_flags &= ~WAS_UNMAPPED; + hhdr -> hb_last_reclaimed = nexthdr -> hb_last_reclaimed; + } + } else if (!IS_MAPPED(hhdr) && !IS_MAPPED(nexthdr)) { + /* Unmap any gap in the middle */ + GC_unmap_gap((ptr_t)h, size, (ptr_t)next, nextsize); + } + /* If they are both unmapped, we merge, but leave unmapped. */ + GC_remove_from_fl_at(hhdr, i); + GC_remove_from_fl(nexthdr); + hhdr -> hb_sz += nexthdr -> hb_sz; + GC_remove_header(next); + GC_add_to_fl(h, hhdr); + /* Start over at beginning of list */ + h = GC_hblkfreelist[i]; + } else /* not mergable with successor */ { + h = hhdr -> hb_next; + } + } /* while (h != 0) ... */ + } /* for ... */ +} + +#endif /* USE_MUNMAP */ + +/* + * Return a pointer to a block starting at h of length bytes. + * Memory for the block is mapped. + * Remove the block from its free list, and return the remainder (if any) + * to its appropriate free list. + * May fail by returning 0. + * The header for the returned block must be set up by the caller. + * If the return value is not 0, then hhdr is the header for it. + */ +STATIC struct hblk * GC_get_first_part(struct hblk *h, hdr *hhdr, + size_t bytes, int index) +{ + word total_size = hhdr -> hb_sz; + struct hblk * rest; + hdr * rest_hdr; + + GC_ASSERT((total_size & (HBLKSIZE-1)) == 0); + GC_remove_from_fl_at(hhdr, index); + if (total_size == bytes) return h; + rest = (struct hblk *)((word)h + bytes); + rest_hdr = GC_install_header(rest); + if (0 == rest_hdr) { + /* FIXME: This is likely to be very bad news ... */ + WARN("Header allocation failed: dropping block\n", 0); + return(0); + } + rest_hdr -> hb_sz = total_size - bytes; + rest_hdr -> hb_flags = 0; +# ifdef GC_ASSERTIONS + /* Mark h not free, to avoid assertion about adjacent free blocks. */ + hhdr -> hb_flags &= ~FREE_BLK; +# endif + GC_add_to_fl(rest, rest_hdr); + return h; +} + +/* + * H is a free block. N points at an address inside it. + * A new header for n has already been set up. Fix up h's header + * to reflect the fact that it is being split, move it to the + * appropriate free list. + * N replaces h in the original free list. + * + * Nhdr is not completely filled in, since it is about to allocated. + * It may in fact end up on the wrong free list for its size. + * That's not a disaster, since n is about to be allocated + * by our caller. + * (Hence adding it to a free list is silly. But this path is hopefully + * rare enough that it doesn't matter. The code is cleaner this way.) + */ +STATIC void GC_split_block(struct hblk *h, hdr *hhdr, struct hblk *n, + hdr *nhdr, int index /* Index of free list */) +{ + word total_size = hhdr -> hb_sz; + word h_size = (word)n - (word)h; + struct hblk *prev = hhdr -> hb_prev; + struct hblk *next = hhdr -> hb_next; + + /* Replace h with n on its freelist */ + nhdr -> hb_prev = prev; + nhdr -> hb_next = next; + nhdr -> hb_sz = total_size - h_size; + nhdr -> hb_flags = 0; + if (0 != prev) { + HDR(prev) -> hb_next = n; + } else { + GC_hblkfreelist[index] = n; + } + if (0 != next) { + HDR(next) -> hb_prev = n; + } + GC_ASSERT(GC_free_bytes[index] > h_size); + GC_free_bytes[index] -= h_size; +# ifdef USE_MUNMAP + hhdr -> hb_last_reclaimed = (unsigned short)GC_gc_no; +# endif + hhdr -> hb_sz = h_size; + GC_add_to_fl(h, hhdr); + nhdr -> hb_flags |= FREE_BLK; +} + +STATIC struct hblk * +GC_allochblk_nth(size_t sz /* bytes */, int kind, unsigned flags, int n, + int may_split); +#define AVOID_SPLIT_REMAPPED 2 + +/* + * Allocate (and return pointer to) a heap block + * for objects of size sz bytes, searching the nth free list. + * + * NOTE: We set obj_map field in header correctly. + * Caller is responsible for building an object freelist in block. + * + * The client is responsible for clearing the block, if necessary. + */ +GC_INNER struct hblk * +GC_allochblk(size_t sz, int kind, unsigned flags/* IGNORE_OFF_PAGE or 0 */) +{ + word blocks; + int start_list; + struct hblk *result; + int may_split; + int split_limit; /* Highest index of free list whose blocks we */ + /* split. */ + + GC_ASSERT((sz & (GRANULE_BYTES - 1)) == 0); + blocks = OBJ_SZ_TO_BLOCKS_CHECKED(sz); + if ((signed_word)(blocks * HBLKSIZE) < 0) { + return 0; + } + start_list = GC_hblk_fl_from_blocks(blocks); + /* Try for an exact match first. */ + result = GC_allochblk_nth(sz, kind, flags, start_list, FALSE); + if (0 != result) return result; + + may_split = TRUE; + if (GC_use_entire_heap || GC_dont_gc + || USED_HEAP_SIZE < GC_requested_heapsize + || GC_incremental || !GC_should_collect()) { + /* Should use more of the heap, even if it requires splitting. */ + split_limit = N_HBLK_FLS; + } else if (GC_finalizer_bytes_freed > (GC_heapsize >> 4)) { + /* If we are deallocating lots of memory from */ + /* finalizers, fail and collect sooner rather */ + /* than later. */ + split_limit = 0; + } else { + /* If we have enough large blocks left to cover any */ + /* previous request for large blocks, we go ahead */ + /* and split. Assuming a steady state, that should */ + /* be safe. It means that we can use the full */ + /* heap if we allocate only small objects. */ + split_limit = GC_enough_large_bytes_left(); +# ifdef USE_MUNMAP + if (split_limit > 0) + may_split = AVOID_SPLIT_REMAPPED; +# endif + } + if (start_list < UNIQUE_THRESHOLD) { + /* No reason to try start_list again, since all blocks are exact */ + /* matches. */ + ++start_list; + } + for (; start_list <= split_limit; ++start_list) { + result = GC_allochblk_nth(sz, kind, flags, start_list, may_split); + if (0 != result) + break; + } + return result; +} + +STATIC long GC_large_alloc_warn_suppressed = 0; + /* Number of warnings suppressed so far. */ + +/* The same, but with search restricted to nth free list. Flags is */ +/* IGNORE_OFF_PAGE or zero. sz is in bytes. The may_split flag */ +/* indicates whether it is OK to split larger blocks (if set to */ +/* AVOID_SPLIT_REMAPPED then memory remapping followed by splitting */ +/* should be generally avoided). */ +STATIC struct hblk * +GC_allochblk_nth(size_t sz, int kind, unsigned flags, int n, int may_split) +{ + struct hblk *hbp; + hdr * hhdr; /* Header corr. to hbp */ + struct hblk *thishbp; + hdr * thishdr; /* Header corr. to thishbp */ + signed_word size_needed = HBLKSIZE * OBJ_SZ_TO_BLOCKS_CHECKED(sz); + /* number of bytes in requested objects */ + + /* search for a big enough block in free list */ + for (hbp = GC_hblkfreelist[n];; hbp = hhdr -> hb_next) { + signed_word size_avail; /* bytes available in this block */ + + if (NULL == hbp) return NULL; + GET_HDR(hbp, hhdr); /* set hhdr value */ + size_avail = (signed_word)hhdr->hb_sz; + if (size_avail < size_needed) continue; + if (size_avail != size_needed) { + if (!may_split) continue; + /* If the next heap block is obviously better, go on. */ + /* This prevents us from disassembling a single large */ + /* block to get tiny blocks. */ + thishbp = hhdr -> hb_next; + if (thishbp != 0) { + signed_word next_size; + + GET_HDR(thishbp, thishdr); + next_size = (signed_word)(thishdr -> hb_sz); + if (next_size < size_avail + && next_size >= size_needed + && !GC_is_black_listed(thishbp, (word)size_needed)) { + continue; + } + } + } + if (!IS_UNCOLLECTABLE(kind) && (kind != PTRFREE + || size_needed > (signed_word)MAX_BLACK_LIST_ALLOC)) { + struct hblk * lasthbp = hbp; + ptr_t search_end = (ptr_t)hbp + size_avail - size_needed; + signed_word orig_avail = size_avail; + signed_word eff_size_needed = (flags & IGNORE_OFF_PAGE) != 0 ? + (signed_word)HBLKSIZE + : size_needed; + + while ((word)lasthbp <= (word)search_end + && (thishbp = GC_is_black_listed(lasthbp, + (word)eff_size_needed)) != 0) { + lasthbp = thishbp; + } + size_avail -= (ptr_t)lasthbp - (ptr_t)hbp; + thishbp = lasthbp; + if (size_avail >= size_needed) { + if (thishbp != hbp) { +# ifdef USE_MUNMAP + /* Avoid remapping followed by splitting. */ + if (may_split == AVOID_SPLIT_REMAPPED && !IS_MAPPED(hhdr)) + continue; +# endif + thishdr = GC_install_header(thishbp); + if (0 != thishdr) { + /* Make sure it's mapped before we mangle it. */ +# ifdef USE_MUNMAP + if (!IS_MAPPED(hhdr)) { + GC_remap((ptr_t)hbp, (size_t)hhdr->hb_sz); + hhdr -> hb_flags &= ~WAS_UNMAPPED; + } +# endif + /* Split the block at thishbp */ + GC_split_block(hbp, hhdr, thishbp, thishdr, n); + /* Advance to thishbp */ + hbp = thishbp; + hhdr = thishdr; + /* We must now allocate thishbp, since it may */ + /* be on the wrong free list. */ + } + } + } else if (size_needed > (signed_word)BL_LIMIT + && orig_avail - size_needed + > (signed_word)BL_LIMIT) { + /* Punt, since anything else risks unreasonable heap growth. */ + if (++GC_large_alloc_warn_suppressed + >= GC_large_alloc_warn_interval) { + WARN("Repeated allocation of very large block " + "(appr. size %" WARN_PRIdPTR "):\n" + "\tMay lead to memory leak and poor performance\n", + size_needed); + GC_large_alloc_warn_suppressed = 0; + } + size_avail = orig_avail; + } else if (size_avail == 0 && size_needed == HBLKSIZE + && IS_MAPPED(hhdr)) { + if (!GC_find_leak) { + static unsigned count = 0; + + /* The block is completely blacklisted. We need */ + /* to drop some such blocks, since otherwise we spend */ + /* all our time traversing them if pointer-free */ + /* blocks are unpopular. */ + /* A dropped block will be reconsidered at next GC. */ + if ((++count & 3) == 0) { + /* Allocate and drop the block in small chunks, to */ + /* maximize the chance that we will recover some */ + /* later. */ + word total_size = hhdr -> hb_sz; + struct hblk * limit = hbp + divHBLKSZ(total_size); + struct hblk * h; + struct hblk * prev = hhdr -> hb_prev; + + GC_large_free_bytes -= total_size; + GC_bytes_dropped += total_size; + GC_remove_from_fl_at(hhdr, n); + for (h = hbp; (word)h < (word)limit; h++) { + if (h != hbp) { + hhdr = GC_install_header(h); + } + if (NULL != hhdr) { + (void)setup_header(hhdr, h, HBLKSIZE, PTRFREE, 0); + /* Can't fail. */ + if (GC_debugging_started) { + BZERO(h, HBLKSIZE); + } + } + } + /* Restore hbp to point at free block */ + hbp = prev; + if (0 == hbp) { + return GC_allochblk_nth(sz, kind, flags, n, may_split); + } + hhdr = HDR(hbp); + } + } + } + } + if( size_avail >= size_needed ) { +# ifdef USE_MUNMAP + if (!IS_MAPPED(hhdr)) { + GC_remap((ptr_t)hbp, (size_t)hhdr->hb_sz); + hhdr -> hb_flags &= ~WAS_UNMAPPED; + /* Note: This may leave adjacent, mapped free blocks. */ + } +# endif + /* hbp may be on the wrong freelist; the parameter n */ + /* is important. */ + hbp = GC_get_first_part(hbp, hhdr, size_needed, n); + break; + } + } + + if (0 == hbp) return 0; + + /* Add it to map of valid blocks */ + if (!GC_install_counts(hbp, (word)size_needed)) return(0); + /* This leaks memory under very rare conditions. */ + + /* Set up header */ + if (!setup_header(hhdr, hbp, sz, kind, flags)) { + GC_remove_counts(hbp, (word)size_needed); + return(0); /* ditto */ + } +# ifndef GC_DISABLE_INCREMENTAL + /* Notify virtual dirty bit implementation that we are about to */ + /* write. Ensure that pointer-free objects are not protected */ + /* if it is avoidable. This also ensures that newly allocated */ + /* blocks are treated as dirty. Necessary since we don't */ + /* protect free blocks. */ + GC_ASSERT((size_needed & (HBLKSIZE-1)) == 0); + GC_remove_protection(hbp, divHBLKSZ(size_needed), + (hhdr -> hb_descr == 0) /* pointer-free */); +# endif + /* We just successfully allocated a block. Restart count of */ + /* consecutive failures. */ + GC_fail_count = 0; + + GC_large_free_bytes -= size_needed; + GC_ASSERT(IS_MAPPED(hhdr)); + return( hbp ); +} + +/* + * Free a heap block. + * + * Coalesce the block with its neighbors if possible. + * + * All mark words are assumed to be cleared. + */ +GC_INNER void GC_freehblk(struct hblk *hbp) +{ + struct hblk *next, *prev; + hdr *hhdr, *prevhdr, *nexthdr; + word size; + + GET_HDR(hbp, hhdr); + size = HBLKSIZE * OBJ_SZ_TO_BLOCKS(hhdr->hb_sz); + if ((signed_word)size <= 0) + ABORT("Deallocating excessively large block. Too large an allocation?"); + /* Probably possible if we try to allocate more than half the address */ + /* space at once. If we don't catch it here, strange things happen */ + /* later. */ + GC_remove_counts(hbp, size); + hhdr->hb_sz = size; +# ifdef USE_MUNMAP + hhdr -> hb_last_reclaimed = (unsigned short)GC_gc_no; +# endif + + /* Check for duplicate deallocation in the easy case */ + if (HBLK_IS_FREE(hhdr)) { + ABORT_ARG1("Duplicate large block deallocation", + " of %p", (void *)hbp); + } + + GC_ASSERT(IS_MAPPED(hhdr)); + hhdr -> hb_flags |= FREE_BLK; + next = (struct hblk *)((ptr_t)hbp + size); + GET_HDR(next, nexthdr); + prev = GC_free_block_ending_at(hbp); + /* Coalesce with successor, if possible */ + if(0 != nexthdr && HBLK_IS_FREE(nexthdr) && IS_MAPPED(nexthdr) + && (signed_word)(hhdr -> hb_sz + nexthdr -> hb_sz) > 0 + /* no overflow */) { + GC_remove_from_fl(nexthdr); + hhdr -> hb_sz += nexthdr -> hb_sz; + GC_remove_header(next); + } + /* Coalesce with predecessor, if possible. */ + if (0 != prev) { + prevhdr = HDR(prev); + if (IS_MAPPED(prevhdr) + && (signed_word)(hhdr -> hb_sz + prevhdr -> hb_sz) > 0) { + GC_remove_from_fl(prevhdr); + prevhdr -> hb_sz += hhdr -> hb_sz; +# ifdef USE_MUNMAP + prevhdr -> hb_last_reclaimed = (unsigned short)GC_gc_no; +# endif + GC_remove_header(hbp); + hbp = prev; + hhdr = prevhdr; + } + } + /* FIXME: It is not clear we really always want to do these merges */ + /* with USE_MUNMAP, since it updates ages and hence prevents */ + /* unmapping. */ + + GC_large_free_bytes += size; + GC_add_to_fl(hbp, hhdr); +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/alloc.c b/unity-2019.4.24f1-mbe/external/bdwgc/alloc.c new file mode 100644 index 000000000..095e53b43 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/alloc.c @@ -0,0 +1,1570 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1996 by Xerox Corporation. All rights reserved. + * Copyright (c) 1998 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999-2004 Hewlett-Packard Development Company, L.P. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + */ + +#include "private/gc_priv.h" + +#include +#if !defined(MACOS) && !defined(MSWINCE) +# include +# if !defined(SN_TARGET_ORBIS) && !defined(SN_TARGET_PSP2) \ + && !defined(__CC_ARM) +# include +# endif +#endif + +#if VERIFY_UNITY_DEFINES +#if ! (ALL_INTERIOR_POINTERS \ + && GC_GCJ_SUPPORT \ + && JAVA_FINALIZATION \ + && NO_EXECUTE_PERMISSION \ + && GC_VERSION_MAJOR == 7 \ + && GC_VERSION_MINOR == 7 \ + && GC_VERSION_MICRO == 0 \ + && GC_NO_THREADS_DISCOVERY \ + && IGNORE_DYNAMIC_LOADING \ + && GC_DONT_REGISTER_MAIN_STATIC_DATA \ + && GC_THREADS \ + && USE_MMAP \ + && USE_MUNMAP) + #error Unity Defines Incorrect +#endif +#endif + +/* + * Separate free lists are maintained for different sized objects + * up to MAXOBJBYTES. + * The call GC_allocobj(i,k) ensures that the freelist for + * kind k objects of size i points to a non-empty + * free list. It returns a pointer to the first entry on the free list. + * In a single-threaded world, GC_allocobj may be called to allocate + * an object of small size lb (and NORMAL kind) as follows + * (GC_generic_malloc_inner is a wrapper over GC_allocobj which also + * fills in GC_size_map if needed): + * + * lg = GC_size_map[lb]; + * op = GC_objfreelist[lg]; + * if (NULL == op) { + * op = GC_generic_malloc_inner(lb, NORMAL); + * } else { + * GC_objfreelist[lg] = obj_link(op); + * GC_bytes_allocd += GRANULES_TO_BYTES((word)lg); + * } + * + * Note that this is very fast if the free list is non-empty; it should + * only involve the execution of 4 or 5 simple instructions. + * All composite objects on freelists are cleared, except for + * their first word. + */ + +/* + * The allocator uses GC_allochblk to allocate large chunks of objects. + * These chunks all start on addresses which are multiples of + * HBLKSZ. Each allocated chunk has an associated header, + * which can be located quickly based on the address of the chunk. + * (See headers.c for details.) + * This makes it possible to check quickly whether an + * arbitrary address corresponds to an object administered by the + * allocator. + */ + +word GC_non_gc_bytes = 0; /* Number of bytes not intended to be collected */ + +word GC_gc_no = 0; + +#ifndef NO_CLOCK + static unsigned long full_gc_total_time = 0; /* in msecs, may wrap */ + static GC_bool measure_performance = FALSE; + /* Do performance measurements if set to true (e.g., */ + /* accumulation of the total time of full collections). */ + + GC_API void GC_CALL GC_start_performance_measurement(void) + { + measure_performance = TRUE; + } + + GC_API unsigned long GC_CALL GC_get_full_gc_total_time(void) + { + return full_gc_total_time; + } +#endif /* !NO_CLOCK */ + +#ifndef GC_DISABLE_INCREMENTAL + GC_INNER GC_bool GC_incremental = FALSE; /* By default, stop the world. */ +#endif + +GC_API int GC_CALL GC_is_incremental_mode(void) +{ + return (int)GC_incremental; +} + +#ifdef THREADS + int GC_parallel = FALSE; /* By default, parallel GC is off. */ +#endif + +#if defined(GC_FULL_FREQ) && !defined(CPPCHECK) + int GC_full_freq = GC_FULL_FREQ; +#else + int GC_full_freq = 19; /* Every 20th collection is a full */ + /* collection, whether we need it */ + /* or not. */ +#endif + +STATIC GC_bool GC_need_full_gc = FALSE; + /* Need full GC do to heap growth. */ + +#ifdef THREAD_LOCAL_ALLOC + GC_INNER GC_bool GC_world_stopped = FALSE; +#endif + +STATIC word GC_used_heap_size_after_full = 0; + +/* GC_copyright symbol is externally visible. */ +EXTERN_C_BEGIN +extern const char * const GC_copyright[]; +EXTERN_C_END +const char * const GC_copyright[] = +{"Copyright 1988,1989 Hans-J. Boehm and Alan J. Demers ", +"Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. ", +"Copyright (c) 1996-1998 by Silicon Graphics. All rights reserved. ", +"Copyright (c) 1999-2009 by Hewlett-Packard Company. All rights reserved. ", +"THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY", +" EXPRESSED OR IMPLIED. ANY USE IS AT YOUR OWN RISK.", +"See source code for details." }; + +/* Version macros are now defined in gc_version.h, which is included by */ +/* gc.h, which is included by gc_priv.h. */ +#ifndef GC_NO_VERSION_VAR + EXTERN_C_BEGIN + extern const unsigned GC_version; + EXTERN_C_END + const unsigned GC_version = ((GC_VERSION_MAJOR << 16) | + (GC_VERSION_MINOR << 8) | GC_VERSION_MICRO); +#endif + +GC_API unsigned GC_CALL GC_get_version(void) +{ + return (GC_VERSION_MAJOR << 16) | (GC_VERSION_MINOR << 8) | + GC_VERSION_MICRO; +} + +/* some more variables */ + +#ifdef GC_DONT_EXPAND + int GC_dont_expand = TRUE; +#else + int GC_dont_expand = FALSE; +#endif + +#if defined(GC_FREE_SPACE_DIVISOR) && !defined(CPPCHECK) + word GC_free_space_divisor = GC_FREE_SPACE_DIVISOR; /* must be > 0 */ +#else + word GC_free_space_divisor = 3; +#endif + +GC_INNER int GC_CALLBACK GC_never_stop_func(void) +{ + return(0); +} + +#if defined(GC_TIME_LIMIT) && !defined(CPPCHECK) + unsigned long long GC_time_limit = GC_TIME_LIMIT * 1000000; + /* We try to keep pause times from exceeding */ + /* this by much. In milliseconds. */ +#else + unsigned long long GC_time_limit = 50000000; +#endif + +#ifndef NO_CLOCK + STATIC CLOCK_TYPE GC_start_time = 0; + /* Time at which we stopped world. */ + /* used only in GC_timeout_stop_func. */ +#endif + +STATIC int GC_n_attempts = 0; /* Number of attempts at finishing */ + /* collection within GC_time_limit. */ + +STATIC GC_stop_func GC_default_stop_func = GC_never_stop_func; + /* accessed holding the lock. */ + +GC_API void GC_CALL GC_set_stop_func(GC_stop_func stop_func) +{ + DCL_LOCK_STATE; + GC_ASSERT(NONNULL_ARG_NOT_NULL(stop_func)); + LOCK(); + GC_default_stop_func = stop_func; + UNLOCK(); +} + +GC_API GC_stop_func GC_CALL GC_get_stop_func(void) +{ + GC_stop_func stop_func; + DCL_LOCK_STATE; + LOCK(); + stop_func = GC_default_stop_func; + UNLOCK(); + return stop_func; +} + +#if defined(GC_DISABLE_INCREMENTAL) || defined(NO_CLOCK) +# define GC_timeout_stop_func GC_default_stop_func +#else + STATIC int GC_CALLBACK GC_timeout_stop_func (void) + { + CLOCK_TYPE current_time; + static unsigned count = 0; + unsigned long long time_diff; + + if ((*GC_default_stop_func)()) + return(1); + + if ((count++ & 3) != 0) return(0); + GET_TIME(current_time); + time_diff = NS_TIME_DIFF(current_time,GC_start_time); + if (time_diff >= GC_time_limit) { + GC_COND_LOG_PRINTF( + "Abandoning stopped marking after %llu nanoseconds (attempt %d)\n", + time_diff, GC_n_attempts); + return(1); + } + return(0); + } +#endif /* !GC_DISABLE_INCREMENTAL */ + +#ifdef THREADS + GC_INNER word GC_total_stacksize = 0; /* updated on every push_all_stacks */ +#endif + +static size_t min_bytes_allocd_minimum = 1; + /* The lowest value returned by min_bytes_allocd(). */ + +GC_API void GC_CALL GC_set_min_bytes_allocd(size_t value) +{ + GC_ASSERT(value > 0); + min_bytes_allocd_minimum = value; +} + +GC_API size_t GC_CALL GC_get_min_bytes_allocd(void) +{ + return min_bytes_allocd_minimum; +} + +/* Return the minimum number of bytes that must be allocated between */ +/* collections to amortize the collection cost. Should be non-zero. */ +static word min_bytes_allocd(void) +{ + word result; + word stack_size; + word total_root_size; /* includes double stack size, */ + /* since the stack is expensive */ + /* to scan. */ + word scan_size; /* Estimate of memory to be scanned */ + /* during normal GC. */ + +# ifdef THREADS + if (GC_need_to_lock) { + /* We are multi-threaded... */ + stack_size = GC_total_stacksize; + /* For now, we just use the value computed during the latest GC. */ +# ifdef DEBUG_THREADS + GC_log_printf("Total stacks size: %lu\n", + (unsigned long)stack_size); +# endif + } else +# endif + /* else*/ { +# ifdef STACK_NOT_SCANNED + stack_size = 0; +# elif defined(STACK_GROWS_UP) + stack_size = GC_approx_sp() - GC_stackbottom; +# else + stack_size = GC_stackbottom - GC_approx_sp(); +# endif + } + + total_root_size = 2 * stack_size + GC_root_size; + scan_size = 2 * GC_composite_in_use + GC_atomic_in_use / 4 + + total_root_size; + result = scan_size / GC_free_space_divisor; + if (GC_incremental) { + result /= 2; + } + return result > min_bytes_allocd_minimum + ? result : min_bytes_allocd_minimum; +} + +STATIC word GC_non_gc_bytes_at_gc = 0; + /* Number of explicitly managed bytes of storage */ + /* at last collection. */ + +/* Return the number of bytes allocated, adjusted for explicit storage */ +/* management, etc.. This number is used in deciding when to trigger */ +/* collections. */ +STATIC word GC_adj_bytes_allocd(void) +{ + signed_word result; + signed_word expl_managed = (signed_word)GC_non_gc_bytes + - (signed_word)GC_non_gc_bytes_at_gc; + + /* Don't count what was explicitly freed, or newly allocated for */ + /* explicit management. Note that deallocating an explicitly */ + /* managed object should not alter result, assuming the client */ + /* is playing by the rules. */ + result = (signed_word)GC_bytes_allocd + + (signed_word)GC_bytes_dropped + - (signed_word)GC_bytes_freed + + (signed_word)GC_finalizer_bytes_freed + - expl_managed; + if (result > (signed_word)GC_bytes_allocd) { + result = GC_bytes_allocd; + /* probably client bug or unfortunate scheduling */ + } + result += GC_bytes_finalized; + /* We count objects enqueued for finalization as though they */ + /* had been reallocated this round. Finalization is user */ + /* visible progress. And if we don't count this, we have */ + /* stability problems for programs that finalize all objects. */ + if (result < (signed_word)(GC_bytes_allocd >> 3)) { + /* Always count at least 1/8 of the allocations. We don't want */ + /* to collect too infrequently, since that would inhibit */ + /* coalescing of free storage blocks. */ + /* This also makes us partially robust against client bugs. */ + return(GC_bytes_allocd >> 3); + } else { + return(result); + } +} + + +/* Clear up a few frames worth of garbage left at the top of the stack. */ +/* This is used to prevent us from accidentally treating garbage left */ +/* on the stack by other parts of the collector as roots. This */ +/* differs from the code in misc.c, which actually tries to keep the */ +/* stack clear of long-lived, client-generated garbage. */ +STATIC void GC_clear_a_few_frames(void) +{ +# ifndef CLEAR_NWORDS +# define CLEAR_NWORDS 64 +# endif + volatile word frames[CLEAR_NWORDS]; + BZERO((word *)frames, CLEAR_NWORDS * sizeof(word)); +} + +/* Heap size at which we need a collection to avoid expanding past */ +/* limits used by blacklisting. */ +STATIC word GC_collect_at_heapsize = (word)(-1); + +/* Have we allocated enough to amortize a collection? */ +GC_INNER GC_bool GC_should_collect(void) +{ + static word last_min_bytes_allocd; + static word last_gc_no; + if (last_gc_no != GC_gc_no) { + last_gc_no = GC_gc_no; + last_min_bytes_allocd = min_bytes_allocd(); + } + return(GC_adj_bytes_allocd() >= last_min_bytes_allocd + || GC_heapsize >= GC_collect_at_heapsize); +} + +/* STATIC */ GC_start_callback_proc GC_start_call_back = 0; + /* Called at start of full collections. */ + /* Not called if 0. Called with the allocation */ + /* lock held. Not used by GC itself. */ + +GC_API void GC_CALL GC_set_start_callback(GC_start_callback_proc fn) +{ + DCL_LOCK_STATE; + LOCK(); + GC_start_call_back = fn; + UNLOCK(); +} + +GC_API GC_start_callback_proc GC_CALL GC_get_start_callback(void) +{ + GC_start_callback_proc fn; + DCL_LOCK_STATE; + LOCK(); + fn = GC_start_call_back; + UNLOCK(); + return fn; +} + +GC_INLINE void GC_notify_full_gc(void) +{ + if (GC_start_call_back != 0) { + (*GC_start_call_back)(); + } +} + +STATIC GC_bool GC_is_full_gc = FALSE; + +STATIC GC_bool GC_stopped_mark(GC_stop_func stop_func); +STATIC void GC_finish_collection(void); + +/* + * Initiate a garbage collection if appropriate. + * Choose judiciously + * between partial, full, and stop-world collections. + */ +STATIC void GC_maybe_gc(void) +{ + GC_ASSERT(I_HOLD_LOCK()); + ASSERT_CANCEL_DISABLED(); + if (GC_should_collect()) { + static int n_partial_gcs = 0; + + if (!GC_incremental) { + /* FIXME: If possible, GC_default_stop_func should be used here */ + GC_try_to_collect_inner(GC_never_stop_func); + n_partial_gcs = 0; + return; + } else { +# ifdef PARALLEL_MARK + if (GC_parallel) + GC_wait_for_reclaim(); +# endif + if (GC_need_full_gc || n_partial_gcs >= GC_full_freq) { + GC_COND_LOG_PRINTF( + "***>Full mark for collection #%lu after %lu allocd bytes\n", + (unsigned long)GC_gc_no + 1, (unsigned long)GC_bytes_allocd); + GC_promote_black_lists(); + (void)GC_reclaim_all((GC_stop_func)0, TRUE); + GC_notify_full_gc(); + GC_clear_marks(); + n_partial_gcs = 0; + GC_is_full_gc = TRUE; + } else { + n_partial_gcs++; + } + } + /* We try to mark with the world stopped. */ + /* If we run out of time, this turns into */ + /* incremental marking. */ +# ifndef NO_CLOCK + if (GC_time_limit != GC_TIME_UNLIMITED) { GET_TIME(GC_start_time); } +# endif + /* FIXME: If possible, GC_default_stop_func should be */ + /* used instead of GC_never_stop_func here. */ + if (GC_stopped_mark(GC_time_limit == GC_TIME_UNLIMITED? + GC_never_stop_func : GC_timeout_stop_func)) { +# ifdef SAVE_CALL_CHAIN + GC_save_callers(GC_last_stack); +# endif + GC_finish_collection(); + } else { + if (!GC_is_full_gc) { + /* Count this as the first attempt */ + GC_n_attempts++; + } + } + } +} + +STATIC GC_on_collection_event_proc GC_on_collection_event = 0; + +GC_API void GC_CALL GC_set_on_collection_event(GC_on_collection_event_proc fn) +{ + /* fn may be 0 (means no event notifier). */ + DCL_LOCK_STATE; + LOCK(); + GC_on_collection_event = fn; + UNLOCK(); +} + +GC_API GC_on_collection_event_proc GC_CALL GC_get_on_collection_event(void) +{ + GC_on_collection_event_proc fn; + DCL_LOCK_STATE; + LOCK(); + fn = GC_on_collection_event; + UNLOCK(); + return fn; +} + +/* Stop the world garbage collection. If stop_func is not */ +/* GC_never_stop_func then abort if stop_func returns TRUE. */ +/* Return TRUE if we successfully completed the collection. */ +GC_INNER GC_bool GC_try_to_collect_inner(GC_stop_func stop_func) +{ +# ifndef NO_CLOCK + CLOCK_TYPE start_time = 0; /* initialized to prevent warning. */ + GC_bool start_time_valid; +# endif + + ASSERT_CANCEL_DISABLED(); + GC_ASSERT(I_HOLD_LOCK()); + if (GC_dont_gc || (*stop_func)()) return FALSE; + if (GC_on_collection_event) + GC_on_collection_event(GC_EVENT_START); + if (GC_incremental && GC_collection_in_progress()) { + GC_COND_LOG_PRINTF( + "GC_try_to_collect_inner: finishing collection in progress\n"); + /* Just finish collection already in progress. */ + while(GC_collection_in_progress()) { + if ((*stop_func)()) { + /* TODO: Notify GC_EVENT_ABANDON */ + return(FALSE); + } + GC_collect_a_little_inner(1); + } + } + GC_notify_full_gc(); +# ifndef NO_CLOCK + start_time_valid = FALSE; + if ((GC_print_stats | (int)measure_performance) != 0) { + if (GC_print_stats) + GC_log_printf("Initiating full world-stop collection!\n"); + start_time_valid = TRUE; + GET_TIME(start_time); + } +# endif + GC_promote_black_lists(); + /* Make sure all blocks have been reclaimed, so sweep routines */ + /* don't see cleared mark bits. */ + /* If we're guaranteed to finish, then this is unnecessary. */ + /* In the find_leak case, we have to finish to guarantee that */ + /* previously unmarked objects are not reported as leaks. */ +# ifdef PARALLEL_MARK + if (GC_parallel) + GC_wait_for_reclaim(); +# endif + if ((GC_find_leak || stop_func != GC_never_stop_func) + && !GC_reclaim_all(stop_func, FALSE)) { + /* Aborted. So far everything is still consistent. */ + /* TODO: Notify GC_EVENT_ABANDON */ + return(FALSE); + } + GC_invalidate_mark_state(); /* Flush mark stack. */ + GC_clear_marks(); +# ifdef SAVE_CALL_CHAIN + GC_save_callers(GC_last_stack); +# endif + GC_is_full_gc = TRUE; + if (!GC_stopped_mark(stop_func)) { + if (!GC_incremental) { + /* We're partially done and have no way to complete or use */ + /* current work. Reestablish invariants as cheaply as */ + /* possible. */ + GC_invalidate_mark_state(); + GC_unpromote_black_lists(); + } /* else we claim the world is already still consistent. We'll */ + /* finish incrementally. */ + /* TODO: Notify GC_EVENT_ABANDON */ + return(FALSE); + } + GC_finish_collection(); +# ifndef NO_CLOCK + if (start_time_valid) { + CLOCK_TYPE current_time; + unsigned long time_diff; + + GET_TIME(current_time); + time_diff = MS_TIME_DIFF(current_time, start_time); + if (measure_performance) + full_gc_total_time += time_diff; /* may wrap */ + if (GC_print_stats) + GC_log_printf("Complete collection took %lu msecs\n", time_diff); + } +# endif + if (GC_on_collection_event) + GC_on_collection_event(GC_EVENT_END); + return(TRUE); +} + +/* + * Perform n units of garbage collection work. A unit is intended to touch + * roughly GC_rate pages. Every once in a while, we do more than that. + * This needs to be a fairly large number with our current incremental + * GC strategy, since otherwise we allocate too much during GC, and the + * cleanup gets expensive. + */ +#ifndef GC_RATE +# define GC_RATE 10 +#endif + +#ifndef MAX_PRIOR_ATTEMPTS +# define MAX_PRIOR_ATTEMPTS 1 +#endif + /* Maximum number of prior attempts at world stop marking */ + /* A value of 1 means that we finish the second time, no matter */ + /* how long it takes. Doesn't count the initial root scan */ + /* for a full GC. */ + +STATIC int GC_deficit = 0;/* The number of extra calls to GC_mark_some */ + /* that we have made. */ + +STATIC int GC_rate = GC_RATE; + +GC_API void GC_CALL GC_set_rate(int value) +{ + GC_ASSERT(value > 0); + GC_rate = value; +} + +GC_API int GC_CALL GC_get_rate(void) +{ + return GC_rate; +} + +static int max_prior_attempts = MAX_PRIOR_ATTEMPTS; + +GC_API void GC_CALL GC_set_max_prior_attempts(int value) +{ + GC_ASSERT(value >= 0); + max_prior_attempts = value; +} + +GC_API int GC_CALL GC_get_max_prior_attempts(void) +{ + return max_prior_attempts; +} + +GC_INNER void GC_collect_a_little_inner(int n) +{ + IF_CANCEL(int cancel_state;) + + GC_ASSERT(I_HOLD_LOCK()); + if (GC_dont_gc) return; + + DISABLE_CANCEL(cancel_state); + if (GC_incremental && GC_collection_in_progress()) { + int i; + int max_deficit = GC_rate * n; + + for (i = GC_deficit; i < max_deficit; i++) { + if (GC_mark_some((ptr_t)0)) { + /* Need to finish a collection */ +# ifdef SAVE_CALL_CHAIN + GC_save_callers(GC_last_stack); +# endif +# ifdef PARALLEL_MARK + if (GC_parallel) + GC_wait_for_reclaim(); +# endif + if (GC_n_attempts < max_prior_attempts + && GC_time_limit != GC_TIME_UNLIMITED) { +# ifndef NO_CLOCK + GET_TIME(GC_start_time); +# endif + if (!GC_stopped_mark(GC_timeout_stop_func)) { + GC_n_attempts++; + break; + } + } else { + /* FIXME: If possible, GC_default_stop_func should be */ + /* used here. */ + (void)GC_stopped_mark(GC_never_stop_func); + } + GC_finish_collection(); + break; + } + } + if (GC_deficit > 0) { + GC_deficit -= max_deficit; + if (GC_deficit < 0) + GC_deficit = 0; + } + } else { + GC_maybe_gc(); + } + RESTORE_CANCEL(cancel_state); +} + +GC_INNER void (*GC_check_heap)(void) = 0; +GC_INNER void (*GC_print_all_smashed)(void) = 0; + +GC_API int GC_CALL GC_collect_a_little(void) +{ + int result; + DCL_LOCK_STATE; + + LOCK(); + GC_collect_a_little_inner(1); + result = (int)GC_collection_in_progress(); + UNLOCK(); + if (!result && GC_debugging_started) GC_print_all_smashed(); + return(result); +} + +#ifndef NO_CLOCK + /* Variables for world-stop average delay time statistic computation. */ + /* "divisor" is incremented every world-stop and halved when reached */ + /* its maximum (or upon "total_time" overflow). */ + static unsigned world_stopped_total_time = 0; + static unsigned world_stopped_total_divisor = 0; +# ifndef MAX_TOTAL_TIME_DIVISOR + /* We shall not use big values here (so "outdated" delay time */ + /* values would have less impact on "average" delay time value than */ + /* newer ones). */ +# define MAX_TOTAL_TIME_DIVISOR 1000 +# endif +#endif /* !NO_CLOCK */ + +#ifdef USE_MUNMAP +# define IF_USE_MUNMAP(x) x +# define COMMA_IF_USE_MUNMAP(x) /* comma */, x +#else +# define IF_USE_MUNMAP(x) /* empty */ +# define COMMA_IF_USE_MUNMAP(x) /* empty */ +#endif + +/* + * We stop the world and mark from all roots. + * If stop_func() ever returns TRUE, we may fail and return FALSE. + * Increment GC_gc_no if we succeed. + */ +STATIC GC_bool GC_stopped_mark(GC_stop_func stop_func) +{ + unsigned i; +# ifndef NO_CLOCK + CLOCK_TYPE start_time = 0; /* initialized to prevent warning. */ +# endif + + GC_ASSERT(I_HOLD_LOCK()); +# if !defined(REDIRECT_MALLOC) && defined(USE_WINALLOC) + GC_add_current_malloc_heap(); +# endif +# if defined(REGISTER_LIBRARIES_EARLY) + GC_cond_register_dynamic_libraries(); +# endif + +# ifndef NO_CLOCK + if (GC_PRINT_STATS_FLAG) + GET_TIME(start_time); +# endif + +# if !defined(GC_NO_FINALIZATION) && !defined(GC_TOGGLE_REFS_NOT_NEEDED) + GC_process_togglerefs(); +# endif +# ifdef THREADS + if (GC_on_collection_event) + GC_on_collection_event(GC_EVENT_PRE_STOP_WORLD); +# endif + STOP_WORLD(); +# ifdef THREADS + if (GC_on_collection_event) + GC_on_collection_event(GC_EVENT_POST_STOP_WORLD); +# endif + +# ifdef THREAD_LOCAL_ALLOC + GC_world_stopped = TRUE; +# endif + /* Output blank line for convenience here */ + GC_COND_LOG_PRINTF( + "\n--> Marking for collection #%lu after %lu allocated bytes\n", + (unsigned long)GC_gc_no + 1, (unsigned long) GC_bytes_allocd); +# ifdef MAKE_BACK_GRAPH + if (GC_print_back_height) { + GC_build_back_graph(); + } +# endif + + /* Mark from all roots. */ + if (GC_on_collection_event) + GC_on_collection_event(GC_EVENT_MARK_START); + + /* Minimize junk left in my registers and on the stack */ + GC_clear_a_few_frames(); + GC_noop6(0,0,0,0,0,0); + + GC_initiate_gc(); + for (i = 0;;i++) { + if ((*stop_func)()) { + GC_COND_LOG_PRINTF("Abandoned stopped marking after" + " %u iterations\n", i); + GC_deficit = i; /* Give the mutator a chance. */ +# ifdef THREAD_LOCAL_ALLOC + GC_world_stopped = FALSE; +# endif + +# ifdef THREADS + if (GC_on_collection_event) + GC_on_collection_event(GC_EVENT_PRE_START_WORLD); +# endif + + START_WORLD(); + +# ifdef THREADS + if (GC_on_collection_event) + GC_on_collection_event(GC_EVENT_POST_START_WORLD); +# endif + + /* TODO: Notify GC_EVENT_MARK_ABANDON */ + return(FALSE); + } + if (GC_mark_some(GC_approx_sp())) break; + } + + GC_gc_no++; + GC_DBGLOG_PRINTF("GC #%lu freed %ld bytes, heap %lu KiB" + IF_USE_MUNMAP(" (+ %lu KiB unmapped)") "\n", + (unsigned long)GC_gc_no, (long)GC_bytes_found, + TO_KiB_UL(GC_heapsize - GC_unmapped_bytes) /*, */ + COMMA_IF_USE_MUNMAP(TO_KiB_UL(GC_unmapped_bytes))); + + /* Check all debugged objects for consistency */ + if (GC_debugging_started) { + (*GC_check_heap)(); + } + if (GC_on_collection_event) + GC_on_collection_event(GC_EVENT_MARK_END); + +# ifdef THREAD_LOCAL_ALLOC + GC_world_stopped = FALSE; +# endif + +# ifdef THREADS + if (GC_on_collection_event) + GC_on_collection_event(GC_EVENT_PRE_START_WORLD); +# endif + + START_WORLD(); + +# ifdef THREADS + if (GC_on_collection_event) + GC_on_collection_event(GC_EVENT_POST_START_WORLD); +# endif + +# ifndef NO_CLOCK + if (GC_PRINT_STATS_FLAG) { + unsigned long time_diff; + unsigned total_time, divisor; + CLOCK_TYPE current_time; + + GET_TIME(current_time); + time_diff = MS_TIME_DIFF(current_time,start_time); + + /* Compute new world-stop delay total time */ + total_time = world_stopped_total_time; + divisor = world_stopped_total_divisor; + if ((int)total_time < 0 || divisor >= MAX_TOTAL_TIME_DIVISOR) { + /* Halve values if overflow occurs */ + total_time >>= 1; + divisor >>= 1; + } + total_time += time_diff < (((unsigned)-1) >> 1) ? + (unsigned)time_diff : ((unsigned)-1) >> 1; + /* Update old world_stopped_total_time and its divisor */ + world_stopped_total_time = total_time; + world_stopped_total_divisor = ++divisor; + + GC_ASSERT(divisor != 0); + GC_log_printf( + "World-stopped marking took %lu msecs (%u in average)\n", + time_diff, total_time / divisor); + } +# endif + return(TRUE); +} + +/* Set all mark bits for the free list whose first entry is q */ +GC_INNER void GC_set_fl_marks(ptr_t q) +{ + if (q != NULL) { + struct hblk *h = HBLKPTR(q); + struct hblk *last_h = h; + hdr *hhdr = HDR(h); + IF_PER_OBJ(word sz = hhdr->hb_sz;) + + for (;;) { + word bit_no = MARK_BIT_NO((ptr_t)q - (ptr_t)h, sz); + + if (!mark_bit_from_hdr(hhdr, bit_no)) { + set_mark_bit_from_hdr(hhdr, bit_no); + ++hhdr -> hb_n_marks; + } + + q = (ptr_t)obj_link(q); + if (q == NULL) + break; + + h = HBLKPTR(q); + if (h != last_h) { + last_h = h; + hhdr = HDR(h); + IF_PER_OBJ(sz = hhdr->hb_sz;) + } + } + } +} + +#if defined(GC_ASSERTIONS) && defined(THREAD_LOCAL_ALLOC) + /* Check that all mark bits for the free list whose first entry is */ + /* (*pfreelist) are set. Check skipped if points to a special value. */ + void GC_check_fl_marks(void **pfreelist) + { + /* TODO: There is a data race with GC_FAST_MALLOC_GRANS (which does */ + /* not do atomic updates to the free-list). The race seems to be */ + /* harmless, and for now we just skip this check in case of TSan. */ +# if defined(AO_HAVE_load_acquire_read) && !defined(THREAD_SANITIZER) + AO_t *list = (AO_t *)AO_load_acquire_read((AO_t *)pfreelist); + /* Atomic operations are used because the world is running. */ + AO_t *prev; + AO_t *p; + + if ((word)list <= HBLKSIZE) return; + + prev = (AO_t *)pfreelist; + for (p = list; p != NULL;) { + AO_t *next; + + if (!GC_is_marked(p)) { + ABORT_ARG2("Unmarked local free list entry", + ": object %p on list %p", (void *)p, (void *)list); + } + + /* While traversing the free-list, it re-reads the pointer to */ + /* the current node before accepting its next pointer and */ + /* bails out if the latter has changed. That way, it won't */ + /* try to follow the pointer which might be been modified */ + /* after the object was returned to the client. It might */ + /* perform the mark-check on the just allocated object but */ + /* that should be harmless. */ + next = (AO_t *)AO_load_acquire_read(p); + if (AO_load(prev) != (AO_t)p) + break; + prev = p; + p = next; + } +# else + /* FIXME: Not implemented (just skipped). */ + (void)pfreelist; +# endif + } +#endif /* GC_ASSERTIONS && THREAD_LOCAL_ALLOC */ + +/* Clear all mark bits for the free list whose first entry is q */ +/* Decrement GC_bytes_found by number of bytes on free list. */ +STATIC void GC_clear_fl_marks(ptr_t q) +{ + struct hblk *h = HBLKPTR(q); + struct hblk *last_h = h; + hdr *hhdr = HDR(h); + word sz = hhdr->hb_sz; /* Normally set only once. */ + + for (;;) { + word bit_no = MARK_BIT_NO((ptr_t)q - (ptr_t)h, sz); + + if (mark_bit_from_hdr(hhdr, bit_no)) { + size_t n_marks = hhdr -> hb_n_marks; + + GC_ASSERT(n_marks != 0); + clear_mark_bit_from_hdr(hhdr, bit_no); + n_marks--; +# ifdef PARALLEL_MARK + /* Appr. count, don't decrement to zero! */ + if (0 != n_marks || !GC_parallel) { + hhdr -> hb_n_marks = n_marks; + } +# else + hhdr -> hb_n_marks = n_marks; +# endif + } + GC_bytes_found -= sz; + + q = (ptr_t)obj_link(q); + if (q == NULL) + break; + + h = HBLKPTR(q); + if (h != last_h) { + last_h = h; + hhdr = HDR(h); + sz = hhdr->hb_sz; + } + } +} + +#if defined(GC_ASSERTIONS) && defined(THREAD_LOCAL_ALLOC) + void GC_check_tls(void); +#endif + +GC_on_heap_resize_proc GC_on_heap_resize = 0; + +/* Used for logging only. */ +GC_INLINE int GC_compute_heap_usage_percent(void) +{ + word used = GC_composite_in_use + GC_atomic_in_use; + word heap_sz = GC_heapsize - GC_unmapped_bytes; + return used >= heap_sz ? 0 : used < ((word)-1) / 100 ? + (int)((used * 100) / heap_sz) : (int)(used / (heap_sz / 100)); +} + +/* Finish up a collection. Assumes mark bits are consistent, lock is */ +/* held, but the world is otherwise running. */ +STATIC void GC_finish_collection(void) +{ +# ifndef NO_CLOCK + CLOCK_TYPE start_time = 0; /* initialized to prevent warning. */ + CLOCK_TYPE finalize_time = 0; +# endif + + GC_ASSERT(I_HOLD_LOCK()); +# if defined(GC_ASSERTIONS) \ + && defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL) + /* Check that we marked some of our own data. */ + /* FIXME: Add more checks. */ + GC_check_tls(); +# endif + +# ifndef NO_CLOCK + if (GC_print_stats) + GET_TIME(start_time); +# endif + if (GC_on_collection_event) + GC_on_collection_event(GC_EVENT_RECLAIM_START); + +# ifndef GC_GET_HEAP_USAGE_NOT_NEEDED + if (GC_bytes_found > 0) + GC_reclaimed_bytes_before_gc += (word)GC_bytes_found; +# endif + GC_bytes_found = 0; +# if defined(LINUX) && defined(__ELF__) && !defined(SMALL_CONFIG) + if (GETENV("GC_PRINT_ADDRESS_MAP") != 0) { + GC_print_address_map(); + } +# endif + COND_DUMP; + if (GC_find_leak) { + /* Mark all objects on the free list. All objects should be */ + /* marked when we're done. */ + word size; /* current object size */ + unsigned kind; + ptr_t q; + + for (kind = 0; kind < GC_n_kinds; kind++) { + for (size = 1; size <= MAXOBJGRANULES; size++) { + q = (ptr_t)GC_obj_kinds[kind].ok_freelist[size]; + if (q != NULL) + GC_set_fl_marks(q); + } + } + GC_start_reclaim(TRUE); + /* The above just checks; it doesn't really reclaim anything. */ + } + +# ifndef GC_NO_FINALIZATION + GC_finalize(); +# endif +# ifndef NO_CLOCK + if (GC_print_stats) + GET_TIME(finalize_time); +# endif + + if (GC_print_back_height) { +# ifdef MAKE_BACK_GRAPH + GC_traverse_back_graph(); +# elif !defined(SMALL_CONFIG) + GC_err_printf("Back height not available: " + "Rebuild collector with -DMAKE_BACK_GRAPH\n"); +# endif + } + + /* Clear free list mark bits, in case they got accidentally marked */ + /* (or GC_find_leak is set and they were intentionally marked). */ + /* Also subtract memory remaining from GC_bytes_found count. */ + /* Note that composite objects on free list are cleared. */ + /* Thus accidentally marking a free list is not a problem; only */ + /* objects on the list itself will be marked, and that's fixed here. */ + { + word size; /* current object size */ + ptr_t q; /* pointer to current object */ + unsigned kind; + + for (kind = 0; kind < GC_n_kinds; kind++) { + for (size = 1; size <= MAXOBJGRANULES; size++) { + q = (ptr_t)GC_obj_kinds[kind].ok_freelist[size]; + if (q != NULL) + GC_clear_fl_marks(q); + } + } + } + + GC_VERBOSE_LOG_PRINTF("Bytes recovered before sweep - f.l. count = %ld\n", + (long)GC_bytes_found); + + /* Reconstruct free lists to contain everything not marked */ + GC_start_reclaim(FALSE); + GC_DBGLOG_PRINTF("In-use heap: %d%% (%lu KiB pointers + %lu KiB other)\n", + GC_compute_heap_usage_percent(), + TO_KiB_UL(GC_composite_in_use), + TO_KiB_UL(GC_atomic_in_use)); + if (GC_is_full_gc) { + GC_used_heap_size_after_full = USED_HEAP_SIZE; + GC_need_full_gc = FALSE; + } else { + GC_need_full_gc = USED_HEAP_SIZE - GC_used_heap_size_after_full + > min_bytes_allocd(); + } + + GC_VERBOSE_LOG_PRINTF("Immediately reclaimed %ld bytes, heapsize:" + " %lu bytes" IF_USE_MUNMAP(" (%lu unmapped)") "\n", + (long)GC_bytes_found, + (unsigned long)GC_heapsize /*, */ + COMMA_IF_USE_MUNMAP((unsigned long) + GC_unmapped_bytes)); + + /* Reset or increment counters for next cycle */ + GC_n_attempts = 0; + GC_is_full_gc = FALSE; + GC_bytes_allocd_before_gc += GC_bytes_allocd; + GC_non_gc_bytes_at_gc = GC_non_gc_bytes; + GC_bytes_allocd = 0; + GC_bytes_dropped = 0; + GC_bytes_freed = 0; + GC_finalizer_bytes_freed = 0; + + IF_USE_MUNMAP(GC_unmap_old()); + + if (GC_on_collection_event) + GC_on_collection_event(GC_EVENT_RECLAIM_END); +# ifndef NO_CLOCK + if (GC_print_stats) { + CLOCK_TYPE done_time; + + GET_TIME(done_time); +# if !defined(SMALL_CONFIG) && !defined(GC_NO_FINALIZATION) + /* A convenient place to output finalization statistics. */ + GC_print_finalization_stats(); +# endif + GC_log_printf("Finalize plus initiate sweep took %lu + %lu msecs\n", + MS_TIME_DIFF(finalize_time,start_time), + MS_TIME_DIFF(done_time,finalize_time)); + } +# elif !defined(SMALL_CONFIG) && !defined(GC_NO_FINALIZATION) + if (GC_print_stats) + GC_print_finalization_stats(); +# endif +} + +/* If stop_func == 0 then GC_default_stop_func is used instead. */ +STATIC GC_bool GC_try_to_collect_general(GC_stop_func stop_func, + GC_bool force_unmap GC_ATTR_UNUSED) +{ + GC_bool result; + IF_USE_MUNMAP(int old_unmap_threshold;) + IF_CANCEL(int cancel_state;) + DCL_LOCK_STATE; + + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); + if (GC_debugging_started) GC_print_all_smashed(); + GC_INVOKE_FINALIZERS(); + LOCK(); + DISABLE_CANCEL(cancel_state); +# ifdef USE_MUNMAP + old_unmap_threshold = GC_unmap_threshold; + if (force_unmap || + (GC_force_unmap_on_gcollect && old_unmap_threshold > 0)) + GC_unmap_threshold = 1; /* unmap as much as possible */ +# endif + ENTER_GC(); + /* Minimize junk left in my registers */ + GC_noop6(0,0,0,0,0,0); + result = GC_try_to_collect_inner(stop_func != 0 ? stop_func : + GC_default_stop_func); + EXIT_GC(); + IF_USE_MUNMAP(GC_unmap_threshold = old_unmap_threshold); /* restore */ + RESTORE_CANCEL(cancel_state); + UNLOCK(); + if (result) { + if (GC_debugging_started) GC_print_all_smashed(); + GC_INVOKE_FINALIZERS(); + } + return(result); +} + +/* Externally callable routines to invoke full, stop-the-world collection. */ +GC_API int GC_CALL GC_try_to_collect(GC_stop_func stop_func) +{ + GC_ASSERT(NONNULL_ARG_NOT_NULL(stop_func)); + return (int)GC_try_to_collect_general(stop_func, FALSE); +} + +GC_API void GC_CALL GC_gcollect(void) +{ + /* 0 is passed as stop_func to get GC_default_stop_func value */ + /* while holding the allocation lock (to prevent data races). */ + (void)GC_try_to_collect_general(0, FALSE); + if (GC_have_errors) GC_print_all_errors(); +} + +STATIC word GC_heapsize_at_forced_unmap = 0; + +GC_API void GC_CALL GC_gcollect_and_unmap(void) +{ + /* Record current heap size to make heap growth more conservative */ + /* afterwards (as if the heap is growing from zero size again). */ + GC_heapsize_at_forced_unmap = GC_heapsize; + /* Collect and force memory unmapping to OS. */ + (void)GC_try_to_collect_general(GC_never_stop_func, TRUE); +} + +GC_INNER word GC_n_heap_sects = 0; + /* Number of sections currently in heap. */ + +#ifdef USE_PROC_FOR_LIBRARIES + GC_INNER word GC_n_memory = 0; + /* Number of GET_MEM allocated memory sections. */ +#endif + +#ifdef USE_PROC_FOR_LIBRARIES + /* Add HBLKSIZE aligned, GET_MEM-generated block to GC_our_memory. */ + /* Defined to do nothing if USE_PROC_FOR_LIBRARIES not set. */ + GC_INNER void GC_add_to_our_memory(ptr_t p, size_t bytes) + { + if (0 == p) return; + if (GC_n_memory >= MAX_HEAP_SECTS) + ABORT("Too many GC-allocated memory sections: Increase MAX_HEAP_SECTS"); + GC_our_memory[GC_n_memory].hs_start = p; + GC_our_memory[GC_n_memory].hs_bytes = bytes; + GC_n_memory++; + } +#endif + +/* + * Use the chunk of memory starting at p of size bytes as part of the heap. + * Assumes p is HBLKSIZE aligned, and bytes is a multiple of HBLKSIZE. + */ +GC_INNER void GC_add_to_heap(struct hblk *p, size_t bytes) +{ + hdr * phdr; + word endp; + + if (GC_n_heap_sects >= MAX_HEAP_SECTS) { + ABORT("Too many heap sections: Increase MAXHINCR or MAX_HEAP_SECTS"); + } + while ((word)p <= HBLKSIZE) { + /* Can't handle memory near address zero. */ + ++p; + bytes -= HBLKSIZE; + if (0 == bytes) return; + } + endp = (word)p + bytes; + if (endp <= (word)p) { + /* Address wrapped. */ + bytes -= HBLKSIZE; + if (0 == bytes) return; + endp -= HBLKSIZE; + } + phdr = GC_install_header(p); + if (0 == phdr) { + /* This is extremely unlikely. Can't add it. This will */ + /* almost certainly result in a 0 return from the allocator, */ + /* which is entirely appropriate. */ + return; + } + GC_ASSERT(endp > (word)p && endp == (word)p + bytes); + GC_heap_sects[GC_n_heap_sects].hs_start = (ptr_t)p; + GC_heap_sects[GC_n_heap_sects].hs_bytes = bytes; + GC_n_heap_sects++; + phdr -> hb_sz = bytes; + phdr -> hb_flags = 0; + GC_freehblk(p); + GC_heapsize += bytes; + + /* Normally the caller calculates a new GC_collect_at_heapsize, + * but this is also called directly from alloc_mark_stack, so + * adjust here. It will be recalculated when called from + * GC_expand_hp_inner. + */ + GC_collect_at_heapsize += bytes; + if (GC_collect_at_heapsize < GC_heapsize /* wrapped */) + GC_collect_at_heapsize = (word)(-1); + + if ((word)p <= (word)GC_least_plausible_heap_addr + || GC_least_plausible_heap_addr == 0) { + GC_least_plausible_heap_addr = (void *)((ptr_t)p - sizeof(word)); + /* Making it a little smaller than necessary prevents */ + /* us from getting a false hit from the variable */ + /* itself. There's some unintentional reflection */ + /* here. */ + } + if ((word)p + bytes >= (word)GC_greatest_plausible_heap_addr) { + GC_greatest_plausible_heap_addr = (void *)endp; + } +} + +#if !defined(NO_DEBUGGING) + void GC_print_heap_sects(void) + { + unsigned i; + + GC_printf("Total heap size: %lu" IF_USE_MUNMAP(" (%lu unmapped)") "\n", + (unsigned long)GC_heapsize /*, */ + COMMA_IF_USE_MUNMAP((unsigned long)GC_unmapped_bytes)); + + for (i = 0; i < GC_n_heap_sects; i++) { + ptr_t start = GC_heap_sects[i].hs_start; + size_t len = GC_heap_sects[i].hs_bytes; + struct hblk *h; + unsigned nbl = 0; + + for (h = (struct hblk *)start; (word)h < (word)(start + len); h++) { + if (GC_is_black_listed(h, HBLKSIZE)) nbl++; + } + GC_printf("Section %d from %p to %p %u/%lu blacklisted\n", + i, (void *)start, (void *)&start[len], + nbl, (unsigned long)divHBLKSZ(len)); + } + } +#endif + +void * GC_least_plausible_heap_addr = (void *)ONES; +void * GC_greatest_plausible_heap_addr = 0; + +GC_INLINE word GC_max(word x, word y) +{ + return(x > y? x : y); +} + +GC_INLINE word GC_min(word x, word y) +{ + return(x < y? x : y); +} + +STATIC word GC_max_heapsize = 0; + +GC_API void GC_CALL GC_set_max_heap_size(GC_word n) +{ + GC_max_heapsize = n; +} + +GC_word GC_max_retries = 0; + +/* This explicitly increases the size of the heap. It is used */ +/* internally, but may also be invoked from GC_expand_hp by the user. */ +/* The argument is in units of HBLKSIZE (tiny values are rounded up). */ +/* Returns FALSE on failure. */ +GC_INNER GC_bool GC_expand_hp_inner(word n) +{ + size_t bytes; + struct hblk * space; + word expansion_slop; /* Number of bytes by which we expect the */ + /* heap to expand soon. */ + + if (n < MINHINCR) n = MINHINCR; + bytes = ROUNDUP_PAGESIZE((size_t)n * HBLKSIZE); + if (GC_max_heapsize != 0 + && (GC_max_heapsize < (word)bytes + || GC_heapsize > GC_max_heapsize - (word)bytes)) { + /* Exceeded self-imposed limit */ + return(FALSE); + } + space = GET_MEM(bytes); + GC_add_to_our_memory((ptr_t)space, bytes); + if (space == 0) { + WARN("Failed to expand heap by %" WARN_PRIdPTR " bytes\n", + (word)bytes); + return(FALSE); + } + GC_INFOLOG_PRINTF("Grow heap to %lu KiB after %lu bytes allocated\n", + TO_KiB_UL(GC_heapsize + (word)bytes), + (unsigned long)GC_bytes_allocd); + /* Adjust heap limits generously for blacklisting to work better. */ + /* GC_add_to_heap performs minimal adjustment needed for */ + /* correctness. */ + expansion_slop = min_bytes_allocd() + 4*MAXHINCR*HBLKSIZE; + if ((GC_last_heap_addr == 0 && !((word)space & SIGNB)) + || (GC_last_heap_addr != 0 + && (word)GC_last_heap_addr < (word)space)) { + /* Assume the heap is growing up */ + word new_limit = (word)space + (word)bytes + expansion_slop; + if (new_limit > (word)space) { + GC_greatest_plausible_heap_addr = + (void *)GC_max((word)GC_greatest_plausible_heap_addr, + (word)new_limit); + } + } else { + /* Heap is growing down */ + word new_limit = (word)space - expansion_slop; + if (new_limit < (word)space) { + GC_least_plausible_heap_addr = + (void *)GC_min((word)GC_least_plausible_heap_addr, + (word)space - expansion_slop); + } + } + GC_prev_heap_addr = GC_last_heap_addr; + GC_last_heap_addr = (ptr_t)space; + GC_add_to_heap(space, bytes); + /* Force GC before we are likely to allocate past expansion_slop */ + GC_collect_at_heapsize = + GC_heapsize + expansion_slop - 2*MAXHINCR*HBLKSIZE; + if (GC_collect_at_heapsize < GC_heapsize /* wrapped */) + GC_collect_at_heapsize = (word)(-1); + if (GC_on_heap_resize) + (*GC_on_heap_resize)(GC_heapsize); + + return(TRUE); +} + +/* Really returns a bool, but it's externally visible, so that's clumsy. */ +/* Arguments is in bytes. Includes GC_init() call. */ +GC_API int GC_CALL GC_expand_hp(size_t bytes) +{ + int result; + DCL_LOCK_STATE; + + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); + LOCK(); + result = (int)GC_expand_hp_inner(divHBLKSZ((word)bytes)); + if (result) GC_requested_heapsize += bytes; + UNLOCK(); + return(result); +} + +word GC_fo_entries = 0; /* used also in extra/MacOS.c */ + +GC_INNER unsigned GC_fail_count = 0; + /* How many consecutive GC/expansion failures? */ + /* Reset by GC_allochblk. */ + +static word last_fo_entries = 0; +static word last_bytes_finalized = 0; + +#define GC_WORD_MAX (~(word)0) + +/* Collect or expand heap in an attempt make the indicated number of */ +/* free blocks available. Should be called until the blocks are */ +/* available (setting retry value to TRUE unless this is the first call */ +/* in a loop) or until it fails by returning FALSE. */ +GC_INNER GC_bool GC_collect_or_expand(word needed_blocks, + GC_bool ignore_off_page, + GC_bool retry) +{ + GC_bool gc_not_stopped = TRUE; + word blocks_to_get; + IF_CANCEL(int cancel_state;) + + DISABLE_CANCEL(cancel_state); + if (!GC_incremental && !GC_dont_gc && + ((GC_dont_expand && GC_bytes_allocd > 0) + || (GC_fo_entries > (last_fo_entries + 500) + && (last_bytes_finalized | GC_bytes_finalized) != 0) + || GC_should_collect())) { + /* Try to do a full collection using 'default' stop_func (unless */ + /* nothing has been allocated since the latest collection or heap */ + /* expansion is disabled). */ + gc_not_stopped = GC_try_to_collect_inner( + GC_bytes_allocd > 0 && (!GC_dont_expand || !retry) ? + GC_default_stop_func : GC_never_stop_func); + if (gc_not_stopped == TRUE || !retry) { + /* Either the collection hasn't been aborted or this is the */ + /* first attempt (in a loop). */ + last_fo_entries = GC_fo_entries; + last_bytes_finalized = GC_bytes_finalized; + RESTORE_CANCEL(cancel_state); + return(TRUE); + } + } + + blocks_to_get = (GC_heapsize - GC_heapsize_at_forced_unmap) + / (HBLKSIZE * GC_free_space_divisor) + + needed_blocks; + if (blocks_to_get > MAXHINCR) { + word slop; + + /* Get the minimum required to make it likely that we can satisfy */ + /* the current request in the presence of black-listing. */ + /* This will probably be more than MAXHINCR. */ + if (ignore_off_page) { + slop = 4; + } else { + slop = 2 * divHBLKSZ(BL_LIMIT); + if (slop > needed_blocks) slop = needed_blocks; + } + if (needed_blocks + slop > MAXHINCR) { + blocks_to_get = needed_blocks + slop; + } else { + blocks_to_get = MAXHINCR; + } + if (blocks_to_get > divHBLKSZ(GC_WORD_MAX)) + blocks_to_get = divHBLKSZ(GC_WORD_MAX); + } + + if (!GC_expand_hp_inner(blocks_to_get) + && (blocks_to_get == needed_blocks + || !GC_expand_hp_inner(needed_blocks))) { + if (gc_not_stopped == FALSE) { + /* Don't increment GC_fail_count here (and no warning). */ + GC_gcollect_inner(); + GC_ASSERT(GC_bytes_allocd == 0); + } else if (GC_fail_count++ < GC_max_retries) { + WARN("Out of Memory! Trying to continue...\n", 0); + GC_gcollect_inner(); + } else { +# if !defined(AMIGA) || !defined(GC_AMIGA_FASTALLOC) + WARN("Out of Memory! Heap size: %" WARN_PRIdPTR " MiB." + " Returning NULL!\n", (GC_heapsize - GC_unmapped_bytes) >> 20); +# endif + RESTORE_CANCEL(cancel_state); + return(FALSE); + } + } else if (GC_fail_count) { + GC_COND_LOG_PRINTF("Memory available again...\n"); + } + RESTORE_CANCEL(cancel_state); + return(TRUE); +} + +/* + * Make sure the object free list for size gran (in granules) is not empty. + * Return a pointer to the first object on the free list. + * The object MUST BE REMOVED FROM THE FREE LIST BY THE CALLER. + */ +GC_INNER ptr_t GC_allocobj(size_t gran, int kind) +{ + void ** flh = &(GC_obj_kinds[kind].ok_freelist[gran]); + GC_bool tried_minor = FALSE; + GC_bool retry = FALSE; + + GC_ASSERT(I_HOLD_LOCK()); + if (gran == 0) return(0); + + while (*flh == 0) { + ENTER_GC(); + /* Do our share of marking work */ + if(TRUE_INCREMENTAL) GC_collect_a_little_inner(1); + /* Sweep blocks for objects of this size */ + GC_ASSERT(!GC_is_full_gc + || NULL == GC_obj_kinds[kind].ok_reclaim_list + || NULL == GC_obj_kinds[kind].ok_reclaim_list[gran]); + GC_continue_reclaim(gran, kind); + EXIT_GC(); + if (*flh == 0) { + GC_new_hblk(gran, kind); + if (*flh == 0) { + ENTER_GC(); + if (GC_incremental && GC_time_limit == GC_TIME_UNLIMITED + && !tried_minor) { + GC_collect_a_little_inner(1); + tried_minor = TRUE; + } else { + if (!GC_collect_or_expand(1, FALSE, retry)) { + EXIT_GC(); + return(0); + } + retry = TRUE; + } + EXIT_GC(); + } + } + } + /* Successful allocation; reset failure count. */ + GC_fail_count = 0; + + return (ptr_t)(*flh); +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/appveyor.yml b/unity-2019.4.24f1-mbe/external/bdwgc/appveyor.yml new file mode 100644 index 000000000..c14c6f4ac --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/appveyor.yml @@ -0,0 +1,11 @@ +version: 7.7.0-{build} + +clone_depth: 50 + +build_script: + - git clone --depth=50 https://github.com/ivmai/libatomic_ops.git + - cmake -Denable_gc_assertions=ON + - cmake --build . --config Debug + +test_script: + - ctest --build-config Debug -V diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/autogen.sh b/unity-2019.4.24f1-mbe/external/bdwgc/autogen.sh new file mode 100644 index 000000000..8a614f919 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/autogen.sh @@ -0,0 +1,18 @@ +#!/bin/sh +set -e + +# This script creates (or regenerates) configure (as well as aclocal.m4, +# config.h.in, Makefile.in, etc.) missing in the source repository. +# +# If you compile from a distribution tarball, you can skip this. Otherwise, +# make sure that you have Autoconf, Automake, Libtool, and pkg-config +# installed on your system, and that the corresponding *.m4 files are visible +# to the aclocal. The latter can be achieved by using packages shipped by +# your OS, or by installing custom versions of all four packages to the same +# prefix. Otherwise, you may need to invoke autoreconf with the appropriate +# -I options to locate the required *.m4 files. + +autoreconf -i + +echo +echo "Ready to run './configure'." diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/backgraph.c b/unity-2019.4.24f1-mbe/external/bdwgc/backgraph.c new file mode 100644 index 000000000..f52ed1448 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/backgraph.c @@ -0,0 +1,496 @@ +/* + * Copyright (c) 2001 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + */ + +#include "private/dbg_mlc.h" + +/* + * This implements a full, though not well-tuned, representation of the + * backwards points-to graph. This is used to test for non-GC-robust + * data structures; the code is not used during normal garbage collection. + * + * One restriction is that we drop all back-edges from nodes with very + * high in-degree, and simply add them add them to a list of such + * nodes. They are then treated as permanent roots. Id this by itself + * doesn't introduce a space leak, then such nodes can't contribute to + * a growing space leak. + */ + +#ifdef MAKE_BACK_GRAPH + +#define MAX_IN 10 /* Maximum in-degree we handle directly */ + +/* #include */ + +#if !defined(DBG_HDRS_ALL) || (ALIGNMENT != CPP_WORDSZ/8) /* || !defined(UNIX_LIKE) */ +# error The configuration does not support MAKE_BACK_GRAPH +#endif + +/* We store single back pointers directly in the object's oh_bg_ptr field. */ +/* If there is more than one ptr to an object, we store q | FLAG_MANY, */ +/* where q is a pointer to a back_edges object. */ +/* Every once in a while we use a back_edges object even for a single */ +/* pointer, since we need the other fields in the back_edges structure to */ +/* be present in some fraction of the objects. Otherwise we get serious */ +/* performance issues. */ +#define FLAG_MANY 2 + +typedef struct back_edges_struct { + word n_edges; /* Number of edges, including those in continuation */ + /* structures. */ + unsigned short flags; +# define RETAIN 1 /* Directly points to a reachable object; */ + /* retain for next GC. */ + unsigned short height_gc_no; + /* If height > 0, then the GC_gc_no value when it */ + /* was computed. If it was computed this cycle, then */ + /* it is current. If it was computed during the */ + /* last cycle, then it represents the old height, */ + /* which is only saved for live objects referenced by */ + /* dead ones. This may grow due to refs from newly */ + /* dead objects. */ + signed_word height; + /* Longest path through unreachable nodes to this node */ + /* that we found using depth first search. */ + +# define HEIGHT_UNKNOWN ((signed_word)(-2)) +# define HEIGHT_IN_PROGRESS ((signed_word)(-1)) + ptr_t edges[MAX_IN]; + struct back_edges_struct *cont; + /* Pointer to continuation structure; we use only the */ + /* edges field in the continuation. */ + /* also used as free list link. */ +} back_edges; + +/* Allocate a new back edge structure. Should be more sophisticated */ +/* if this were production code. */ +#define MAX_BACK_EDGE_STRUCTS 100000 +static back_edges *back_edge_space = 0; +STATIC int GC_n_back_edge_structs = 0; + /* Serves as pointer to never used */ + /* back_edges space. */ +static back_edges *avail_back_edges = 0; + /* Pointer to free list of deallocated */ + /* back_edges structures. */ + +static back_edges * new_back_edges(void) +{ + if (0 == back_edge_space) { + size_t bytes_to_get = ROUNDUP_PAGESIZE_IF_MMAP(MAX_BACK_EDGE_STRUCTS + * sizeof(back_edges)); + + back_edge_space = (back_edges *)GET_MEM(bytes_to_get); + if (NULL == back_edge_space) + ABORT("Insufficient memory for back edges"); + GC_add_to_our_memory((ptr_t)back_edge_space, bytes_to_get); + } + if (0 != avail_back_edges) { + back_edges * result = avail_back_edges; + avail_back_edges = result -> cont; + result -> cont = 0; + return result; + } + if (GC_n_back_edge_structs >= MAX_BACK_EDGE_STRUCTS - 1) { + ABORT("Needed too much space for back edges: adjust " + "MAX_BACK_EDGE_STRUCTS"); + } + return back_edge_space + (GC_n_back_edge_structs++); +} + +/* Deallocate p and its associated continuation structures. */ +static void deallocate_back_edges(back_edges *p) +{ + back_edges *last = p; + + while (0 != last -> cont) last = last -> cont; + last -> cont = avail_back_edges; + avail_back_edges = p; +} + +/* Table of objects that are currently on the depth-first search */ +/* stack. Only objects with in-degree one are in this table. */ +/* Other objects are identified using HEIGHT_IN_PROGRESS. */ +/* FIXME: This data structure NEEDS IMPROVEMENT. */ +#define INITIAL_IN_PROGRESS 10000 +static ptr_t * in_progress_space = 0; +static size_t in_progress_size = 0; +static size_t n_in_progress = 0; + +static void push_in_progress(ptr_t p) +{ + if (n_in_progress >= in_progress_size) { + ptr_t * new_in_progress_space; + + if (NULL == in_progress_space) { + in_progress_size = ROUNDUP_PAGESIZE_IF_MMAP(INITIAL_IN_PROGRESS + * sizeof(ptr_t)) + / sizeof(ptr_t); + new_in_progress_space = + (ptr_t *)GET_MEM(in_progress_size * sizeof(ptr_t)); + } else { + in_progress_size *= 2; + new_in_progress_space = (ptr_t *) + GET_MEM(in_progress_size * sizeof(ptr_t)); + if (new_in_progress_space != NULL) + BCOPY(in_progress_space, new_in_progress_space, + n_in_progress * sizeof(ptr_t)); + } + GC_add_to_our_memory((ptr_t)new_in_progress_space, + in_progress_size * sizeof(ptr_t)); +# ifndef GWW_VDB + GC_scratch_recycle_no_gww(in_progress_space, + n_in_progress * sizeof(ptr_t)); +# elif defined(LINT2) + /* TODO: implement GWW-aware recycling as in alloc_mark_stack */ + GC_noop1((word)in_progress_space); +# endif + in_progress_space = new_in_progress_space; + } + if (in_progress_space == 0) + ABORT("MAKE_BACK_GRAPH: Out of in-progress space: " + "Huge linear data structure?"); + in_progress_space[n_in_progress++] = p; +} + +static GC_bool is_in_progress(ptr_t p) +{ + size_t i; + for (i = 0; i < n_in_progress; ++i) { + if (in_progress_space[i] == p) return TRUE; + } + return FALSE; +} + +GC_INLINE void pop_in_progress(ptr_t p GC_ATTR_UNUSED) +{ + --n_in_progress; + GC_ASSERT(in_progress_space[n_in_progress] == p); +} + +#define GET_OH_BG_PTR(p) \ + (ptr_t)GC_REVEAL_POINTER(((oh *)(p)) -> oh_bg_ptr) +#define SET_OH_BG_PTR(p,q) (((oh *)(p)) -> oh_bg_ptr = GC_HIDE_POINTER(q)) + +/* Execute s once for each predecessor q of p in the points-to graph. */ +/* s should be a bracketed statement. We declare q. */ +#define FOR_EACH_PRED(q, p, s) \ + do { \ + ptr_t q = GET_OH_BG_PTR(p); \ + if (!((word)q & FLAG_MANY)) { \ + if (q && !((word)q & 1)) s \ + /* !((word)q & 1) checks for a misinterpreted freelist link */ \ + } else { \ + back_edges *orig_be_ = (back_edges *)((word)q & ~FLAG_MANY); \ + back_edges *be_ = orig_be_; \ + int local_; \ + word total_; \ + word n_edges_ = be_ -> n_edges; \ + for (total_ = 0, local_ = 0; total_ < n_edges_; ++local_, ++total_) { \ + if (local_ == MAX_IN) { \ + be_ = be_ -> cont; \ + local_ = 0; \ + } \ + q = be_ -> edges[local_]; s \ + } \ + } \ + } while (0) + +/* Ensure that p has a back_edges structure associated with it. */ +static void ensure_struct(ptr_t p) +{ + ptr_t old_back_ptr = GET_OH_BG_PTR(p); + + if (!((word)old_back_ptr & FLAG_MANY)) { + back_edges *be = new_back_edges(); + be -> flags = 0; + if (0 == old_back_ptr) { + be -> n_edges = 0; + } else { + be -> n_edges = 1; + be -> edges[0] = old_back_ptr; + } + be -> height = HEIGHT_UNKNOWN; + be -> height_gc_no = (unsigned short)(GC_gc_no - 1); + GC_ASSERT((word)be >= (word)back_edge_space); + SET_OH_BG_PTR(p, (word)be | FLAG_MANY); + } +} + +/* Add the (forward) edge from p to q to the backward graph. Both p */ +/* q are pointers to the object base, i.e. pointers to an oh. */ +static void add_edge(ptr_t p, ptr_t q) +{ + ptr_t old_back_ptr = GET_OH_BG_PTR(q); + back_edges * be, *be_cont; + word i; + + GC_ASSERT(p == GC_base(p) && q == GC_base(q)); + if (!GC_HAS_DEBUG_INFO(q) || !GC_HAS_DEBUG_INFO(p)) { + /* This is really a misinterpreted free list link, since we saw */ + /* a pointer to a free list. Don't overwrite it! */ + return; + } + if (0 == old_back_ptr) { + static unsigned random_number = 13; +# define GOT_LUCKY_NUMBER (((++random_number) & 0x7f) == 0) + /* A not very random number we use to occasionally allocate a */ + /* back_edges structure even for a single backward edge. This */ + /* prevents us from repeatedly tracing back through very long */ + /* chains, since we will have some place to store height and */ + /* in_progress flags along the way. */ + + SET_OH_BG_PTR(q, p); + if (GOT_LUCKY_NUMBER) ensure_struct(q); + return; + } + /* Check whether it was already in the list of predecessors. */ + FOR_EACH_PRED(pred, q, { if (p == pred) return; }); + ensure_struct(q); + old_back_ptr = GET_OH_BG_PTR(q); + be = (back_edges *)((word)old_back_ptr & ~FLAG_MANY); + for (i = be -> n_edges, be_cont = be; i > MAX_IN; i -= MAX_IN) + be_cont = be_cont -> cont; + if (i == MAX_IN) { + be_cont -> cont = new_back_edges(); + be_cont = be_cont -> cont; + i = 0; + } + be_cont -> edges[i] = p; + be -> n_edges++; +# ifdef DEBUG_PRINT_BIG_N_EDGES + if (GC_print_stats == VERBOSE && be -> n_edges == 100) { + GC_err_printf("The following object has big in-degree:\n"); + GC_print_heap_obj(q); + } +# endif +} + +typedef void (*per_object_func)(ptr_t p, size_t n_bytes, word gc_descr); + +static void per_object_helper(struct hblk *h, word fn) +{ + hdr * hhdr = HDR(h); + size_t sz = (size_t)hhdr->hb_sz; + word descr = hhdr -> hb_descr; + per_object_func f = (per_object_func)fn; + size_t i = 0; + + do { + f((ptr_t)(h -> hb_body + i), sz, descr); + i += sz; + } while (i + sz <= BYTES_TO_WORDS(HBLKSIZE)); +} + +GC_INLINE void GC_apply_to_each_object(per_object_func f) +{ + GC_apply_to_all_blocks(per_object_helper, (word)f); +} + +static void reset_back_edge(ptr_t p, size_t n_bytes GC_ATTR_UNUSED, + word gc_descr GC_ATTR_UNUSED) +{ + /* Skip any free list links, or dropped blocks */ + if (GC_HAS_DEBUG_INFO(p)) { + ptr_t old_back_ptr = GET_OH_BG_PTR(p); + if ((word)old_back_ptr & FLAG_MANY) { + back_edges *be = (back_edges *)((word)old_back_ptr & ~FLAG_MANY); + if (!(be -> flags & RETAIN)) { + deallocate_back_edges(be); + SET_OH_BG_PTR(p, 0); + } else { + + GC_ASSERT(GC_is_marked(p)); + + /* Back edges may point to objects that will not be retained. */ + /* Delete them for now, but remember the height. */ + /* Some will be added back at next GC. */ + be -> n_edges = 0; + if (0 != be -> cont) { + deallocate_back_edges(be -> cont); + be -> cont = 0; + } + + GC_ASSERT(GC_is_marked(p)); + + /* We only retain things for one GC cycle at a time. */ + be -> flags &= ~RETAIN; + } + } else /* Simple back pointer */ { + /* Clear to avoid dangling pointer. */ + SET_OH_BG_PTR(p, 0); + } + } +} + +static void add_back_edges(ptr_t p, size_t n_bytes, word gc_descr) +{ + word *currentp = (word *)(p + sizeof(oh)); + + /* For now, fix up non-length descriptors conservatively. */ + if((gc_descr & GC_DS_TAGS) != GC_DS_LENGTH) { + gc_descr = n_bytes; + } + while ((word)currentp < (word)(p + gc_descr)) { + word current = *currentp++; + FIXUP_POINTER(current); + if (current >= (word)GC_least_plausible_heap_addr && + current <= (word)GC_greatest_plausible_heap_addr) { + ptr_t target = (ptr_t)GC_base((void *)current); + if (0 != target) { + add_edge(p, target); + } + } + } +} + +/* Rebuild the representation of the backward reachability graph. */ +/* Does not examine mark bits. Can be called before GC. */ +GC_INNER void GC_build_back_graph(void) +{ + GC_ASSERT(I_HOLD_LOCK()); + GC_apply_to_each_object(add_back_edges); +} + +/* Return an approximation to the length of the longest simple path */ +/* through unreachable objects to p. We refer to this as the height */ +/* of p. */ +static word backwards_height(ptr_t p) +{ + word result; + ptr_t back_ptr = GET_OH_BG_PTR(p); + back_edges *be; + + if (0 == back_ptr) return 1; + if (!((word)back_ptr & FLAG_MANY)) { + if (is_in_progress(p)) return 0; /* DFS back edge, i.e. we followed */ + /* an edge to an object already */ + /* on our stack: ignore */ + push_in_progress(p); + result = backwards_height(back_ptr)+1; + pop_in_progress(p); + return result; + } + be = (back_edges *)((word)back_ptr & ~FLAG_MANY); + if (be -> height >= 0 && be -> height_gc_no == (unsigned short)GC_gc_no) + return be -> height; + /* Ignore back edges in DFS */ + if (be -> height == HEIGHT_IN_PROGRESS) return 0; + result = (be -> height > 0? be -> height : 1); + be -> height = HEIGHT_IN_PROGRESS; + FOR_EACH_PRED(q, p, { + word this_height; + if (GC_is_marked(q) && !(FLAG_MANY & (word)GET_OH_BG_PTR(p))) { + GC_COND_LOG_PRINTF("Found bogus pointer from %p to %p\n", + (void *)q, (void *)p); + /* Reachable object "points to" unreachable one. */ + /* Could be caused by our lax treatment of GC descriptors. */ + this_height = 1; + } else { + this_height = backwards_height(q); + } + if (this_height >= result) result = this_height + 1; + }); + be -> height = result; + be -> height_gc_no = (unsigned short)GC_gc_no; + return result; +} + +STATIC word GC_max_height = 0; +STATIC ptr_t GC_deepest_obj = NULL; + +/* Compute the maximum height of every unreachable predecessor p of a */ +/* reachable object. Arrange to save the heights of all such objects p */ +/* so that they can be used in calculating the height of objects in the */ +/* next GC. */ +/* Set GC_max_height to be the maximum height we encounter, and */ +/* GC_deepest_obj to be the corresponding object. */ +static void update_max_height(ptr_t p, size_t n_bytes GC_ATTR_UNUSED, + word gc_descr GC_ATTR_UNUSED) +{ + if (GC_is_marked(p) && GC_HAS_DEBUG_INFO(p)) { + word p_height = 0; + ptr_t p_deepest_obj = 0; + ptr_t back_ptr; + back_edges *be = 0; + + /* If we remembered a height last time, use it as a minimum. */ + /* It may have increased due to newly unreachable chains pointing */ + /* to p, but it can't have decreased. */ + back_ptr = GET_OH_BG_PTR(p); + if (0 != back_ptr && ((word)back_ptr & FLAG_MANY)) { + be = (back_edges *)((word)back_ptr & ~FLAG_MANY); + if (be -> height != HEIGHT_UNKNOWN) p_height = be -> height; + } + FOR_EACH_PRED(q, p, { + if (!GC_is_marked(q) && GC_HAS_DEBUG_INFO(q)) { + word q_height; + + q_height = backwards_height(q); + if (q_height > p_height) { + p_height = q_height; + p_deepest_obj = q; + } + } + }); + if (p_height > 0) { + /* Remember the height for next time. */ + if (be == 0) { + ensure_struct(p); + back_ptr = GET_OH_BG_PTR(p); + be = (back_edges *)((word)back_ptr & ~FLAG_MANY); + } + be -> flags |= RETAIN; + be -> height = p_height; + be -> height_gc_no = (unsigned short)GC_gc_no; + } + if (p_height > GC_max_height) { + GC_max_height = p_height; + GC_deepest_obj = p_deepest_obj; + } + } +} + +STATIC word GC_max_max_height = 0; + +GC_INNER void GC_traverse_back_graph(void) +{ + GC_ASSERT(I_HOLD_LOCK()); + GC_max_height = 0; + GC_apply_to_each_object(update_max_height); + if (0 != GC_deepest_obj) + GC_set_mark_bit(GC_deepest_obj); /* Keep it until we can print it. */ +} + +void GC_print_back_graph_stats(void) +{ + GC_ASSERT(I_HOLD_LOCK()); + GC_printf("Maximum backwards height of reachable objects at GC %lu is %lu\n", + (unsigned long) GC_gc_no, (unsigned long)GC_max_height); + if (GC_max_height > GC_max_max_height) { + ptr_t obj = GC_deepest_obj; + + GC_max_max_height = GC_max_height; + UNLOCK(); + GC_err_printf( + "The following unreachable object is last in a longest chain " + "of unreachable objects:\n"); + GC_print_heap_obj(obj); + LOCK(); + } + GC_COND_LOG_PRINTF("Needed max total of %d back-edge structs\n", + GC_n_back_edge_structs); + GC_apply_to_each_object(reset_back_edge); + GC_deepest_obj = 0; +} + +#endif /* MAKE_BACK_GRAPH */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/bdw-gc.pc.in b/unity-2019.4.24f1-mbe/external/bdwgc/bdw-gc.pc.in new file mode 100644 index 000000000..ef4c23410 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/bdw-gc.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Boehm-Demers-Weiser Conservative Garbage Collector +Description: A garbage collector for C and C++ +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lgc +Cflags: -I${includedir} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/blacklst.c b/unity-2019.4.24f1-mbe/external/bdwgc/blacklst.c new file mode 100644 index 000000000..31511ae18 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/blacklst.c @@ -0,0 +1,302 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#include "private/gc_priv.h" + +/* + * We maintain several hash tables of hblks that have had false hits. + * Each contains one bit per hash bucket; If any page in the bucket + * has had a false hit, we assume that all of them have. + * See the definition of page_hash_table in gc_private.h. + * False hits from the stack(s) are much more dangerous than false hits + * from elsewhere, since the former can pin a large object that spans the + * block, even though it does not start on the dangerous block. + */ + +/* + * Externally callable routines are: + + * GC_add_to_black_list_normal + * GC_add_to_black_list_stack + * GC_promote_black_lists + * GC_is_black_listed + * + * All require that the allocator lock is held. + */ + +/* Pointers to individual tables. We replace one table by another by */ +/* switching these pointers. */ +STATIC word * GC_old_normal_bl = NULL; + /* Nonstack false references seen at last full */ + /* collection. */ +STATIC word * GC_incomplete_normal_bl = NULL; + /* Nonstack false references seen since last */ + /* full collection. */ +STATIC word * GC_old_stack_bl = NULL; +STATIC word * GC_incomplete_stack_bl = NULL; + +STATIC word GC_total_stack_black_listed = 0; + /* Number of bytes on stack blacklist. */ + +GC_INNER word GC_black_list_spacing = MINHINCR * HBLKSIZE; + /* Initial rough guess. */ + +STATIC void GC_clear_bl(word *); + +GC_INNER void GC_default_print_heap_obj_proc(ptr_t p) +{ + ptr_t base = (ptr_t)GC_base(p); + int kind = HDR(base)->hb_obj_kind; + + GC_err_printf("object at %p of appr. %lu bytes (%s)\n", + (void *)base, (unsigned long)GC_size(base), + kind == PTRFREE ? "atomic" : + IS_UNCOLLECTABLE(kind) ? "uncollectable" : "composite"); +} + +GC_INNER void (*GC_print_heap_obj)(ptr_t p) = GC_default_print_heap_obj_proc; + +#ifdef PRINT_BLACK_LIST + STATIC void GC_print_blacklisted_ptr(word p, ptr_t source, + const char *kind_str) + { + ptr_t base = (ptr_t)GC_base(source); + + if (0 == base) { + GC_err_printf("Black listing (%s) %p referenced from %p in %s\n", + kind_str, (void *)p, (void *)source, + NULL != source ? "root set" : "register"); + } else { + /* FIXME: We can't call the debug version of GC_print_heap_obj */ + /* (with PRINT_CALL_CHAIN) here because the lock is held and */ + /* the world is stopped. */ + GC_err_printf("Black listing (%s) %p referenced from %p in" + " object at %p of appr. %lu bytes\n", + kind_str, (void *)p, (void *)source, + (void *)base, (unsigned long)GC_size(base)); + } + } +#endif /* PRINT_BLACK_LIST */ + +GC_INNER void GC_bl_init_no_interiors(void) +{ + if (GC_incomplete_normal_bl == 0) { + GC_old_normal_bl = (word *)GC_scratch_alloc(sizeof(page_hash_table)); + GC_incomplete_normal_bl = (word *)GC_scratch_alloc( + sizeof(page_hash_table)); + if (GC_old_normal_bl == 0 || GC_incomplete_normal_bl == 0) { + GC_err_printf("Insufficient memory for black list\n"); + EXIT(); + } + GC_clear_bl(GC_old_normal_bl); + GC_clear_bl(GC_incomplete_normal_bl); + } +} + +GC_INNER void GC_bl_init(void) +{ + if (!GC_all_interior_pointers) { + GC_bl_init_no_interiors(); + } + GC_ASSERT(NULL == GC_old_stack_bl && NULL == GC_incomplete_stack_bl); + GC_old_stack_bl = (word *)GC_scratch_alloc(sizeof(page_hash_table)); + GC_incomplete_stack_bl = (word *)GC_scratch_alloc(sizeof(page_hash_table)); + if (GC_old_stack_bl == 0 || GC_incomplete_stack_bl == 0) { + GC_err_printf("Insufficient memory for black list\n"); + EXIT(); + } + GC_clear_bl(GC_old_stack_bl); + GC_clear_bl(GC_incomplete_stack_bl); +} + +STATIC void GC_clear_bl(word *doomed) +{ + BZERO(doomed, sizeof(page_hash_table)); +} + +STATIC void GC_copy_bl(word *old, word *dest) +{ + BCOPY(old, dest, sizeof(page_hash_table)); +} + +static word total_stack_black_listed(void); + +/* Signal the completion of a collection. Turn the incomplete black */ +/* lists into new black lists, etc. */ +GC_INNER void GC_promote_black_lists(void) +{ + word * very_old_normal_bl = GC_old_normal_bl; + word * very_old_stack_bl = GC_old_stack_bl; + + GC_old_normal_bl = GC_incomplete_normal_bl; + GC_old_stack_bl = GC_incomplete_stack_bl; + if (!GC_all_interior_pointers) { + GC_clear_bl(very_old_normal_bl); + } + GC_clear_bl(very_old_stack_bl); + GC_incomplete_normal_bl = very_old_normal_bl; + GC_incomplete_stack_bl = very_old_stack_bl; + GC_total_stack_black_listed = total_stack_black_listed(); + GC_VERBOSE_LOG_PRINTF( + "%lu bytes in heap blacklisted for interior pointers\n", + (unsigned long)GC_total_stack_black_listed); + if (GC_total_stack_black_listed != 0) { + GC_black_list_spacing = + HBLKSIZE*(GC_heapsize/GC_total_stack_black_listed); + } + if (GC_black_list_spacing < 3 * HBLKSIZE) { + GC_black_list_spacing = 3 * HBLKSIZE; + } + if (GC_black_list_spacing > MAXHINCR * HBLKSIZE) { + GC_black_list_spacing = MAXHINCR * HBLKSIZE; + /* Makes it easier to allocate really huge blocks, which otherwise */ + /* may have problems with nonuniform blacklist distributions. */ + /* This way we should always succeed immediately after growing the */ + /* heap. */ + } +} + +GC_INNER void GC_unpromote_black_lists(void) +{ + if (!GC_all_interior_pointers) { + GC_copy_bl(GC_old_normal_bl, GC_incomplete_normal_bl); + } + GC_copy_bl(GC_old_stack_bl, GC_incomplete_stack_bl); +} + +#if defined(set_pht_entry_from_index_concurrent) && defined(PARALLEL_MARK) \ + && defined(THREAD_SANITIZER) +# define backlist_set_pht_entry_from_index(db, index) \ + set_pht_entry_from_index_concurrent(db, index) +#else + /* It is safe to set a bit in a blacklist even without */ + /* synchronization, the only drawback is that we might have */ + /* to redo blacklisting sometimes. */ +# define backlist_set_pht_entry_from_index(bl, index) \ + set_pht_entry_from_index(bl, index) +#endif + +/* P is not a valid pointer reference, but it falls inside */ +/* the plausible heap bounds. */ +/* Add it to the normal incomplete black list if appropriate. */ +#ifdef PRINT_BLACK_LIST + GC_INNER void GC_add_to_black_list_normal(word p, ptr_t source) +#else + GC_INNER void GC_add_to_black_list_normal(word p) +#endif +{ + if (GC_modws_valid_offsets[p & (sizeof(word)-1)]) { + word index = PHT_HASH((word)p); + + if (HDR(p) == 0 || get_pht_entry_from_index(GC_old_normal_bl, index)) { +# ifdef PRINT_BLACK_LIST + if (!get_pht_entry_from_index(GC_incomplete_normal_bl, index)) { + GC_print_blacklisted_ptr(p, source, "normal"); + } +# endif + backlist_set_pht_entry_from_index(GC_incomplete_normal_bl, index); + } /* else this is probably just an interior pointer to an allocated */ + /* object, and isn't worth black listing. */ + } +} + +/* And the same for false pointers from the stack. */ +#ifdef PRINT_BLACK_LIST + GC_INNER void GC_add_to_black_list_stack(word p, ptr_t source) +#else + GC_INNER void GC_add_to_black_list_stack(word p) +#endif +{ + word index = PHT_HASH((word)p); + + if (HDR(p) == 0 || get_pht_entry_from_index(GC_old_stack_bl, index)) { +# ifdef PRINT_BLACK_LIST + if (!get_pht_entry_from_index(GC_incomplete_stack_bl, index)) { + GC_print_blacklisted_ptr(p, source, "stack"); + } +# endif + backlist_set_pht_entry_from_index(GC_incomplete_stack_bl, index); + } +} + +/* + * Is the block starting at h of size len bytes black listed? If so, + * return the address of the next plausible r such that (r, len) might not + * be black listed. (R may not actually be in the heap. We guarantee only + * that every smaller value of r after h is also black listed.) + * If (h,len) is not black listed, return 0. + * Knows about the structure of the black list hash tables. + */ +struct hblk * GC_is_black_listed(struct hblk *h, word len) +{ + word index = PHT_HASH((word)h); + word i; + word nblocks; + + if (!GC_all_interior_pointers + && (get_pht_entry_from_index(GC_old_normal_bl, index) + || get_pht_entry_from_index(GC_incomplete_normal_bl, index))) { + return (h+1); + } + + nblocks = divHBLKSZ(len); + for (i = 0;;) { + if (GC_old_stack_bl[divWORDSZ(index)] == 0 + && GC_incomplete_stack_bl[divWORDSZ(index)] == 0) { + /* An easy case */ + i += WORDSZ - modWORDSZ(index); + } else { + if (get_pht_entry_from_index(GC_old_stack_bl, index) + || get_pht_entry_from_index(GC_incomplete_stack_bl, index)) { + return(h+i+1); + } + i++; + } + if (i >= nblocks) break; + index = PHT_HASH((word)(h+i)); + } + return(0); +} + +/* Return the number of blacklisted blocks in a given range. */ +/* Used only for statistical purposes. */ +/* Looks only at the GC_incomplete_stack_bl. */ +STATIC word GC_number_stack_black_listed(struct hblk *start, + struct hblk *endp1) +{ + struct hblk * h; + word result = 0; + + for (h = start; (word)h < (word)endp1; h++) { + word index = PHT_HASH((word)h); + + if (get_pht_entry_from_index(GC_old_stack_bl, index)) result++; + } + return(result); +} + +/* Return the total number of (stack) black-listed bytes. */ +static word total_stack_black_listed(void) +{ + unsigned i; + word total = 0; + + for (i = 0; i < GC_n_heap_sects; i++) { + struct hblk * start = (struct hblk *) GC_heap_sects[i].hs_start; + struct hblk * endp1 = start + divHBLKSZ(GC_heap_sects[i].hs_bytes); + + total += GC_number_stack_black_listed(start, endp1); + } + return(total * HBLKSIZE); +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/build/s60v3/bld.inf b/unity-2019.4.24f1-mbe/external/bdwgc/build/s60v3/bld.inf new file mode 100644 index 000000000..491fcf434 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/build/s60v3/bld.inf @@ -0,0 +1,11 @@ +/* + Name : bld.inf + Description : This file provides the information required for building the + whole of a libgc. +*/ + +PRJ_PLATFORMS +default armv5 + +PRJ_MMPFILES +libgc.mmp diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/build/s60v3/libgc.mmp b/unity-2019.4.24f1-mbe/external/bdwgc/build/s60v3/libgc.mmp new file mode 100644 index 000000000..7fee8ad1a --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/build/s60v3/libgc.mmp @@ -0,0 +1,75 @@ +TARGET libgc.dll + +TARGETTYPE dll +UID 0x1000008d 0x200107C2 // check uid + +EXPORTUNFROZEN +EPOCALLOWDLLDATA +//ALWAYS_BUILD_AS_ARM +//nocompresstarget +//srcdbg +//baseaddress 00500000 +//LINKEROPTION CW -map libgc.map +//LINKEROPTION CW -filealign 0x10000 + +CAPABILITY PowerMgmt ReadDeviceData ReadUserData WriteDeviceData WriteUserData SwEvent LocalServices NetworkServices UserEnvironment + + +MACRO ALL_INTERIOR_POINTERS +MACRO NO_EXECUTE_PERMISSION +MACRO USE_MMAP +MACRO GC_DONT_REGISTER_MAIN_STATIC_DATA +MACRO GC_DLL +MACRO SYMBIAN +MACRO ENABLE_DISCLAIM +//MACRO GC_GCJ_SUPPORT + +USERINCLUDE ..\..\include +USERINCLUDE ..\..\include\private + +SYSTEMINCLUDE \epoc32\include +SYSTEMINCLUDE \epoc32\include\stdapis + +SOURCEPATH ..\..\ + +SOURCE allchblk.c +SOURCE alloc.c +SOURCE blacklst.c +SOURCE dbg_mlc.c +SOURCE dyn_load.c +SOURCE finalize.c +SOURCE fnlz_mlc.c +//SOURCE gc_cpp.cpp +SOURCE gcj_mlc.c +SOURCE headers.c +SOURCE mach_dep.c +SOURCE malloc.c +SOURCE mallocx.c +SOURCE mark.c +SOURCE mark_rts.c +SOURCE misc.c +SOURCE new_hblk.c +SOURCE obj_map.c +SOURCE os_dep.c +SOURCE extra/symbian.cpp +SOURCE ptr_chck.c +SOURCE reclaim.c +SOURCE typd_mlc.c + +/* +#ifdef ENABLE_ABIV2_MODE + DEBUGGABLE_UDEBONLY +#endif +*/ + +// Using main() as entry point +STATICLIBRARY libcrt0.lib + +// libc and euser are always needed when using main() entry point +LIBRARY libc.lib + + +LIBRARY euser.lib +LIBRARY efsrv.lib +LIBRARY avkon.lib +LIBRARY eikcore.lib diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/checksums.c b/unity-2019.4.24f1-mbe/external/bdwgc/checksums.c new file mode 100644 index 000000000..3fd7d8786 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/checksums.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 1992-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#include "private/gc_priv.h" + +#ifdef CHECKSUMS + +/* This is debugging code intended to verify the results of dirty bit */ +/* computations. Works only in a single threaded environment. */ +#define NSUMS 10000 + +#define OFFSET 0x10000 + +typedef struct { + GC_bool new_valid; + word old_sum; + word new_sum; + struct hblk * block; /* Block to which this refers + OFFSET */ + /* to hide it from collector. */ +} page_entry; + +page_entry GC_sums[NSUMS]; + +STATIC word GC_faulted[NSUMS] = { 0 }; + /* Record of pages on which we saw a write fault. */ + +STATIC size_t GC_n_faulted = 0; + +#if defined(MPROTECT_VDB) && !defined(DARWIN) + void GC_record_fault(struct hblk * h) + { + word page = (word)h & ~(GC_page_size - 1); + + GC_ASSERT(GC_page_size != 0); + if (GC_n_faulted >= NSUMS) ABORT("write fault log overflowed"); + GC_faulted[GC_n_faulted++] = page; + } +#endif + +STATIC GC_bool GC_was_faulted(struct hblk *h) +{ + size_t i; + word page = (word)h & ~(GC_page_size - 1); + + for (i = 0; i < GC_n_faulted; ++i) { + if (GC_faulted[i] == page) return TRUE; + } + return FALSE; +} + +STATIC word GC_checksum(struct hblk *h) +{ + word *p = (word *)h; + word *lim = (word *)(h+1); + word result = 0; + + while ((word)p < (word)lim) { + result += *p++; + } + return(result | 0x80000000 /* doesn't look like pointer */); +} + +int GC_n_dirty_errors = 0; +int GC_n_faulted_dirty_errors = 0; +unsigned long GC_n_clean = 0; +unsigned long GC_n_dirty = 0; + +STATIC void GC_update_check_page(struct hblk *h, int index) +{ + page_entry *pe = GC_sums + index; + hdr * hhdr = HDR(h); + struct hblk *b; + + if (pe -> block != 0 && pe -> block != h + OFFSET) ABORT("goofed"); + pe -> old_sum = pe -> new_sum; + pe -> new_sum = GC_checksum(h); +# if !defined(MSWIN32) && !defined(MSWINCE) + if (pe -> new_sum != 0x80000000 && !GC_page_was_ever_dirty(h)) { + GC_err_printf("GC_page_was_ever_dirty(%p) is wrong\n", (void *)h); + } +# endif + if (GC_page_was_dirty(h)) { + GC_n_dirty++; + } else { + GC_n_clean++; + } + b = h; + while (IS_FORWARDING_ADDR_OR_NIL(hhdr) && hhdr != 0) { + b -= (word)hhdr; + hhdr = HDR(b); + } + if (pe -> new_valid + && hhdr != 0 && hhdr -> hb_descr != 0 /* may contain pointers */ + && pe -> old_sum != pe -> new_sum) { + if (!GC_page_was_dirty(h) || !GC_page_was_ever_dirty(h)) { + GC_bool was_faulted = GC_was_faulted(h); + /* Set breakpoint here */GC_n_dirty_errors++; + if (was_faulted) GC_n_faulted_dirty_errors++; + } + } + pe -> new_valid = TRUE; + pe -> block = h + OFFSET; +} + +word GC_bytes_in_used_blocks = 0; + +STATIC void GC_add_block(struct hblk *h, word dummy GC_ATTR_UNUSED) +{ + hdr * hhdr = HDR(h); + + GC_bytes_in_used_blocks += (hhdr->hb_sz + HBLKSIZE-1) & ~(HBLKSIZE-1); +} + +STATIC void GC_check_blocks(void) +{ + word bytes_in_free_blocks = GC_large_free_bytes; + + GC_bytes_in_used_blocks = 0; + GC_apply_to_all_blocks(GC_add_block, (word)0); + GC_COND_LOG_PRINTF("GC_bytes_in_used_blocks = %lu," + " bytes_in_free_blocks = %lu, heapsize = %lu\n", + (unsigned long)GC_bytes_in_used_blocks, + (unsigned long)bytes_in_free_blocks, + (unsigned long)GC_heapsize); + if (GC_bytes_in_used_blocks + bytes_in_free_blocks != GC_heapsize) { + GC_err_printf("LOST SOME BLOCKS!!\n"); + } +} + +/* Should be called immediately after GC_read_dirty. */ +void GC_check_dirty(void) +{ + int index; + unsigned i; + struct hblk *h; + ptr_t start; + + GC_check_blocks(); + + GC_n_dirty_errors = 0; + GC_n_faulted_dirty_errors = 0; + GC_n_clean = 0; + GC_n_dirty = 0; + + index = 0; + for (i = 0; i < GC_n_heap_sects; i++) { + start = GC_heap_sects[i].hs_start; + for (h = (struct hblk *)start; + (word)h < (word)(start + GC_heap_sects[i].hs_bytes); h++) { + GC_update_check_page(h, index); + index++; + if (index >= NSUMS) goto out; + } + } +out: + GC_COND_LOG_PRINTF("Checked %lu clean and %lu dirty pages\n", + GC_n_clean, GC_n_dirty); + if (GC_n_dirty_errors > 0) { + GC_err_printf("Found %d dirty bit errors (%d were faulted)\n", + GC_n_dirty_errors, GC_n_faulted_dirty_errors); + } + for (i = 0; i < GC_n_faulted; ++i) { + GC_faulted[i] = 0; /* Don't expose block pointers to GC */ + } + GC_n_faulted = 0; +} + +#endif /* CHECKSUMS */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/configure.ac b/unity-2019.4.24f1-mbe/external/bdwgc/configure.ac new file mode 100644 index 000000000..31c3be858 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/configure.ac @@ -0,0 +1,1106 @@ +# Copyright (c) 1999-2001 by Red Hat, Inc. All rights reserved. +# +# THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED +# OR IMPLIED. ANY USE IS AT YOUR OWN RISK. +# +# Permission is hereby granted to use or copy this program +# for any purpose, provided the above notices are retained on all copies. +# Permission to modify the code and to distribute modified code is granted, +# provided the above notices are retained, and a notice that the code was +# modified is included with the above copyright notice. + +dnl Process this file with autoconf to produce configure. + +# Initialization +AC_INIT(gc,7.7.0,https://github.com/ivmai/bdwgc/issues) + ## version must conform to [0-9]+[.][0-9]+[.][0-9]+ +AC_CONFIG_SRCDIR(gcj_mlc.c) +AC_CONFIG_MACRO_DIR([m4]) +AC_CANONICAL_TARGET +AC_PREREQ(2.61) +GC_SET_VERSION +AM_INIT_AUTOMAKE([foreign nostdinc subdir-objects]) +AC_CONFIG_HEADERS([include/config.h]) +AM_MAINTAINER_MODE + +AC_SUBST(PACKAGE) +AC_SUBST(GC_VERSION) + +AM_PROG_CC_C_O +AC_PROG_CXX +AM_PROG_AS +AC_PROG_INSTALL +LT_INIT +# Note: If Autoconf reports that LIBTOOL (or AC_ENABLE_SHARED, or +# AC_PROG_LIBTOOL) is undefined, Libtool installation should be checked. + +# Special CFLAGS to use when building +gc_cflags="" + +# gc_use_mmap Set to "yes" on platforms where mmap should be used instead +# of sbrk. This will define USE_MMAP. +gc_use_mmap="" + +# We should set -fexceptions if we are using gcc and might be used +# inside something like gcj. This is the zeroth approximation: +if test :"$GCC": = :yes: ; then + gc_cflags="${gc_cflags} -fexceptions" +else + case "$host" in + hppa*-*-hpux* ) + if test :$GCC: != :"yes": ; then + gc_cflags="${gc_flags} +ESdbgasm" + fi + # :TODO: actually we should check using Autoconf if + # the compiler supports this option. + ;; + esac +fi + +case "${host}" in + *-linux*) + # FIXME: This seems to be no longer needed as configured in gcconfig.h + #gc_use_mmap=yes + ;; +esac + +# target_optspace --enable-target-optspace ("yes", "no", "") +case "${target_optspace}:${host}" in + yes:*) + gc_cflags="${gc_cflags} -Os" + ;; + :m32r-* | :d10v-* | :d30v-*) + gc_cflags="${gc_cflags} -Os" + ;; + no:* | :*) + # Nothing. + ;; +esac + +# Set any host dependent compiler flags. +case "${host}" in + mips-tx39-*|mipstx39-unknown-*) + gc_cflags="${gc_cflags} -G 0" + ;; + *) + ;; +esac + + +GC_CFLAGS=${gc_cflags} +AC_SUBST(GC_CFLAGS) + +# Extra user-defined flags to pass both to C and C++ compilers. +AC_SUBST([CFLAGS_EXTRA]) + +AC_ARG_ENABLE(verify-defines, + [AC_HELP_STRING([--enable-verify-defines=yes|no], + [Verify that the correct defines have been set for Unity.])], + verify_defines=$enableval) + if test "${verify_defines}" = "yes"; then + AC_DEFINE([VERIFY_UNITY_DEFINES], 1, + [Verify that the correct defines have been set for Unity.]) + fi + +AC_ARG_ENABLE(threads, + [AC_HELP_STRING([--enable-threads=TYPE], [choose threading package])], + THREADS=$enableval, + [ AC_MSG_CHECKING([for thread model used by GCC]) + THREADS=`$CC -v 2>&1 | sed -n 's/^Thread model: //p'` + if test -z "$THREADS"; then + THREADS=no + fi + if test "$THREADS" = "posix"; then + case "$host" in + *-*-mingw*) + # Adjust thread model if cross-compiling for MinGW. + THREADS=win32 + ;; + esac + fi + AC_MSG_RESULT([$THREADS]) ]) + +AC_ARG_ENABLE(parallel-mark, + [AC_HELP_STRING([--disable-parallel-mark], + [do not parallelize marking and free list construction])], + [case "$THREADS" in + no | none | single) + if test "${enable_parallel_mark}" != no; then + AC_MSG_ERROR([Parallel mark requires --enable-threads=x spec]) + fi + ;; + esac ] +) + +AC_ARG_ENABLE(thread-local-alloc, + [AC_HELP_STRING([--disable-thread-local-alloc], + [turn off thread-local allocation optimization])], + [case "$THREADS" in + no | none | single) + if test "${enable_thread_local_alloc}" = yes; then + AC_MSG_ERROR( + [Thread-local allocation requires --enable-threads=x spec]) + fi + ;; + esac]) + +AC_ARG_ENABLE(cplusplus, + [AC_HELP_STRING([--enable-cplusplus], [install C++ support])]) + +AC_ARG_ENABLE(no-threads-discovery, + [AC_HELP_STRING([--enable-no-threads-discovery=yes|no], + [Disable threads discovery in GC])], + gc_no_thread_discovery=$enableval) + if test "${gc_no_thread_discovery}" = "yes"; then + AC_DEFINE([GC_NO_THREADS_DISCOVERY], 1, + [Disable threads discovery in GC]) + fi + +AC_ARG_ENABLE(gc-threads, + [AC_HELP_STRING([--enable-gc-threads=yes|no], + [Enable threads])], + gc_threads=$enableval) + if test "${gc_threads}" = "yes"; then + AC_DEFINE([GC_THREADS], 1, + [Enable threads]) + fi + +dnl Features which may be selected in the following thread-detection switch. +AH_TEMPLATE([PARALLEL_MARK], [Define to enable parallel marking.]) +AH_TEMPLATE([THREAD_LOCAL_ALLOC], + [Define to enable thread-local allocation optimization.]) +AH_TEMPLATE([USE_COMPILER_TLS], + [Define to use of compiler-support for thread-local variables.]) + +dnl Thread selection macros. +AH_TEMPLATE([GC_THREADS], [Define to support platform-specific + threads.]) +AH_TEMPLATE([GC_WIN32_PTHREADS], + [Define to support pthreads-win32 or winpthreads.]) + +dnl System header feature requests. +AH_TEMPLATE([_POSIX_C_SOURCE], [The POSIX feature macro.]) +AH_TEMPLATE([_PTHREADS], [Indicates the use of pthreads (NetBSD).]) + +dnl Win32-specific API usage controls. +AH_TEMPLATE([GC_UNDERSCORE_STDCALL], + [Explicitly prefix exported/imported WINAPI symbols with '_'.]) +AH_TEMPLATE([UNICODE], + [Use Unicode (W) variant of Win32 API instead of ASCII (A) one.]) + +dnl GC API symbols export control. +AH_TEMPLATE([GC_DLL], + [Define to build dynamic libraries with only API symbols exposed.]) + +dnl Check for a flavor of supported inline keyword. +old_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS $CFLAGS_EXTRA" +AC_C_INLINE +CFLAGS="$old_CFLAGS" + +THREADDLLIBS= +need_atomic_ops_asm=false +## Libraries needed to support dynamic loading and/or threads. +case "$THREADS" in + no | none | single) + THREADS=none + ;; + posix | pthreads) + THREADS=posix + default_threadlibs=false + # Common defines for most POSIX platforms. + case "$host" in + *-*-aix* | *-*-android* | *-*-cygwin* | *-*-darwin* | *-*-dragonfly* | \ + *-*-freebsd* | *-*-haiku* | *-*-hpux11* | *-*-irix* | \ + *-*-kfreebsd*-gnu | *-*-gnu* | *-*-*linux* | *-*-nacl* | \ + *-*-netbsd* | *-*-openbsd* | *-*-osf* | *-*-solaris*) + AC_DEFINE(GC_THREADS) + AC_DEFINE([_REENTRANT], [1], + [Required define if using POSIX threads.]) + if test "${enable_parallel_mark}" != no; then + AC_DEFINE(PARALLEL_MARK) + fi + if test "${enable_thread_local_alloc}" != no; then + AC_DEFINE(THREAD_LOCAL_ALLOC) + fi + default_threadlibs=true + AC_MSG_WARN("Explicit GC_INIT() calls may be required.") + ;; + esac + AC_CHECK_LIB(pthread, pthread_self, THREADDLLIBS="-lpthread",,) + case "$host" in + *-*-hpux11*) + AC_MSG_WARN("Only HP/UX 11 POSIX threads are supported.") + AC_DEFINE(_POSIX_C_SOURCE,199506L) + THREADDLLIBS="-lpthread -lrt" + # HPUX needs REENTRANT for the _r calls. + ;; + *-*-openbsd*) + AM_CFLAGS="$AM_CFLAGS -pthread" + THREADDLLIBS=-pthread + ;; + *-*-freebsd*) + AM_CFLAGS="$AM_CFLAGS -pthread" + ;; + *-*-kfreebsd*-gnu) + AM_CFLAGS="$AM_CFLAGS -pthread" + THREADDLLIBS=-pthread + ;; + *-*-gnu*) # E.g. linux but excluding kfreebsd. + # The default THREADDLLIBS. + ;; + *-*-netbsd*) + AC_MSG_WARN("Only on NetBSD 2.0 or later.") + AC_DEFINE(_PTHREADS) + THREADDLLIBS="-lpthread -lrt" + ;; + *-*-solaris*) + THREADDLLIBS="-lpthread -lrt" + ;; + *-*-cygwin*) + # Cygwin doesn't have a real libpthread, so Libtool can't link + # against it. + THREADDLLIBS="" + win32_threads=true + ;; + *-*-mingw*) + AC_DEFINE(GC_WIN32_PTHREADS) + # Using pthreads-win32 (or other non-Cygwin pthreads) library. + if test "${enable_parallel_mark}" != no; then + AC_DEFINE(PARALLEL_MARK) + fi + if test "${enable_thread_local_alloc}" != no; then + AC_DEFINE(THREAD_LOCAL_ALLOC) + fi + THREADDLLIBS="-lpthread" + win32_threads=true + ;; + *-*-darwin*) + darwin_threads=true + ;; + *-*-osf*) + AM_CFLAGS="$AM_CFLAGS -pthread" + THREADDLLIBS="-lpthread -lrt" + ;; + *) + AS_IF([test x$default_threadlibs != xtrue], + [ AC_MSG_ERROR( + [Pthreads not supported by the GC on this platform]) ]) + # The default THREADDLLIBS. + ;; + esac + case "$host" in + sparc*-*-solaris*) + if test "$GCC" != yes; then + need_atomic_ops_asm=true + fi + ;; + esac + ;; + win32) + AC_DEFINE(GC_THREADS) + if test "${enable_parallel_mark}" != no; then + AC_DEFINE(PARALLEL_MARK) + fi + if test "${enable_thread_local_alloc}" != no; then + if test "${enable_parallel_mark}" != no \ + -o "${enable_shared}" != yes -o "${enable_static}" != no; then + # Imply THREAD_LOCAL_ALLOC unless GC_DLL. + AC_DEFINE(THREAD_LOCAL_ALLOC) + fi + fi + if test "${enable_win32_dllmain}" = yes; then + AC_DEFINE(GC_INSIDE_DLL, 1, + [Enable Win32 DllMain-based approach of threads registering.]) + fi + win32_threads=true + AC_DEFINE([EMPTY_GETENV_RESULTS], [1], + [Wine getenv may not return NULL for missing entry.]) + ;; + dgux386) + AC_DEFINE(GC_THREADS) + THREADS=dgux386 + AC_MSG_RESULT($THREADDLLIBS) + # Use pthread GCC switch + THREADDLLIBS=-pthread + if test "${enable_parallel_mark}" != no; then + AC_DEFINE(PARALLEL_MARK) + fi + if test "${enable_thread_local_alloc}" != no; then + AC_DEFINE(THREAD_LOCAL_ALLOC) + fi + AC_MSG_WARN("Explicit GC_INIT() calls may be required.") + AM_CFLAGS="-pthread $AM_CFLAGS" + ;; + aix) + THREADS=posix + THREADDLLIBS=-lpthread + AC_DEFINE(GC_THREADS) + AC_DEFINE(_REENTRANT) + if test "${enable_parallel_mark}" != no; then + AC_DEFINE(PARALLEL_MARK) + fi + if test "${enable_thread_local_alloc}" != no; then + AC_DEFINE(THREAD_LOCAL_ALLOC) + fi + ;; + rtems) + THREADS=posix + AC_DEFINE(GC_THREADS) + if test "${enable_parallel_mark}" != no; then + AC_DEFINE(PARALLEL_MARK) + fi + if test "${enable_thread_local_alloc}" != no; then + AC_DEFINE(THREAD_LOCAL_ALLOC) + fi + ;; + decosf1 | irix | mach | os2 | solaris | dce | vxworks) + AC_MSG_ERROR(thread package $THREADS not yet supported) + ;; + *) + AC_MSG_ERROR($THREADS is an unknown thread package) + ;; +esac +AC_SUBST(THREADDLLIBS) +AM_CONDITIONAL(THREADS, test x$THREADS != xnone) +AM_CONDITIONAL(PTHREADS, test x$THREADS = xposix) +AM_CONDITIONAL(DARWIN_THREADS, test x$darwin_threads = xtrue) +AM_CONDITIONAL(WIN32_THREADS, test x$win32_threads = xtrue) +AM_CONDITIONAL(THREAD_LOCAL_ALLOC, + test x$enable_thread_local_alloc != xno -a x$THREADS != xnone) + +compiler_suncc=no +pthread_start_standalone=no +case "$host" in + *-*-*linux*) + # Turn on the workaround described in pthread_start.c. + AS_IF([test "$THREADS" = posix], [pthread_start_standalone=yes]) + ;; + powerpc-*-darwin*) + powerpc_darwin=true + ;; + *-*-solaris*) + if test "$GCC" != yes; then + # Solaris SunCC + compiler_suncc=yes + CFLAGS="-O $CFLAGS" + fi + ;; + *-*-wince*) + if test "$enable_gc_debug" != "no"; then + AC_DEFINE([GC_READ_ENV_FILE], 1, + [Read environment variables from the GC 'env' file.]) + fi + ;; +esac +AM_CONDITIONAL(PTHREAD_START_STANDALONE, + test x$pthread_start_standalone = xyes) + +if test "$GCC" = yes; then + # Output all warnings. + AC_MSG_CHECKING([whether compiler supports -Wextra]) + old_CFLAGS="$CFLAGS" + CFLAGS="-Wextra $CFLAGS" + AC_TRY_COMPILE([],[], [ac_cv_cc_wextra=yes], [ac_cv_cc_wextra=no]) + CFLAGS="$old_CFLAGS" + AC_MSG_RESULT($ac_cv_cc_wextra) + AS_IF([test "$ac_cv_cc_wextra" = yes], [WEXTRA="-Wextra"], [WEXTRA="-W"]) + AC_MSG_CHECKING([whether compiler supports -Wpedantic]) + CFLAGS="-Wpedantic -Wno-long-long $CFLAGS" + AC_TRY_COMPILE([],[ + extern int quiet; + ], [ac_cv_cc_pedantic=yes], [ac_cv_cc_pedantic=no]) + CFLAGS="$old_CFLAGS" + AC_MSG_RESULT($ac_cv_cc_pedantic) + WPEDANTIC= + AS_IF([test "$ac_cv_cc_pedantic" = yes], + [WPEDANTIC="-Wpedantic -Wno-long-long"]) + CFLAGS="-Wall $WEXTRA $WPEDANTIC $CFLAGS" +fi + +AC_MSG_CHECKING(for xlc) +AC_TRY_COMPILE([],[ + #ifndef __xlC__ + # error + #endif +], [compiler_xlc=yes], [compiler_xlc=no]) +AC_MSG_RESULT($compiler_xlc) +if test $compiler_xlc = yes -a "$powerpc_darwin" = true; then + # the darwin stack-frame-walking code is completely broken on xlc + AC_DEFINE([DARWIN_DONT_PARSE_STACK], 1, [See doc/README.macros.]) +fi + +# XLC neither requires nor tolerates the unnecessary assembler goop. +# Similar for the Sun C compiler. +AM_CONDITIONAL([ASM_WITH_CPP_UNSUPPORTED], + [test $compiler_xlc = yes -o $compiler_suncc = yes]) + +if test "$GCC" = yes; then + # Disable aliasing optimization unless forced to. + AC_MSG_CHECKING([whether compiler supports -fno-strict-aliasing]) + ac_cv_fno_strict_aliasing=no + for cflag in $CFLAGS; do + case "$cflag" in + -fstrict-aliasing) + # Opposite option already present + ac_cv_fno_strict_aliasing=skipped + break + ;; + esac + done + if test "$ac_cv_fno_strict_aliasing" != skipped; then + old_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -fno-strict-aliasing" + AC_TRY_COMPILE([],[], [ac_cv_fno_strict_aliasing=yes], []) + CFLAGS="$old_CFLAGS" + AS_IF([test "$ac_cv_fno_strict_aliasing" = yes], + [CFLAGS="$CFLAGS -fno-strict-aliasing"], []) + fi + AC_MSG_RESULT($ac_cv_fno_strict_aliasing) +fi + +# Check for getcontext (uClibc can be configured without it, for example) +AC_CHECK_FUNC([getcontext], [], + [ AC_DEFINE([NO_GETCONTEXT], [1], [Missing getcontext()]) ]) + +# Check whether dl_iterate_phdr exists (as a strong symbol). +AC_CHECK_FUNCS([dl_iterate_phdr]) + +case "$host" in +# While IRIX 6 has libdl for the O32 and N32 ABIs, it's missing for N64 +# and unnecessary everywhere. + mips-sgi-irix6*) ;; +# We never want libdl on darwin. It is a fake libdl that just ends up making +# dyld calls anyway. The same applies to Cygwin. + *-*-darwin*) ;; + *-*-cygwin*) ;; + *) + AC_CHECK_LIB(dl, dlopen, THREADDLLIBS="$THREADDLLIBS -ldl") + ;; +esac + +case "$host" in + *-*-hpux*) + avoid_cpp_lib=yes;; + *) + avoid_cpp_lib=no; + ;; +esac +AM_CONDITIONAL(AVOID_CPP_LIB,test $avoid_cpp_lib = yes) + +# Check for various headers. +AC_CHECK_HEADER([execinfo.h], [], + [ AC_DEFINE([GC_MISSING_EXECINFO_H], [1], [Missing execinfo.h header]) ]) + +# extra LD Flags which are required for targets +case "${host}" in + *-*-darwin*) + extra_ldflags_libgc=-Wl,-single_module + ;; +esac +AC_SUBST(extra_ldflags_libgc) + +AC_SUBST(EXTRA_TEST_LIBS) + +target_all=libgc.la +AC_SUBST(target_all) + +dnl If the target is an eCos system, use the appropriate eCos +dnl I/O routines. +dnl FIXME: this should not be a local option but a global target +dnl system; at present there is no eCos target. +TARGET_ECOS="no" +AC_ARG_WITH(ecos, +[ --with-ecos enable runtime eCos target support], +TARGET_ECOS="$with_ecos" +) + +addobjs= +addlibs= +CXXLIBS= + +case "$TARGET_ECOS" in + no) + ;; + *) + AC_DEFINE([ECOS], 1, [Define to enable eCos target support.]) + AM_CPPFLAGS="-I${TARGET_ECOS}/include $AM_CPPFLAGS" + addobjs="$addobjs ecos.lo" + ;; +esac + +AM_CONDITIONAL(CPLUSPLUS, test "${enable_cplusplus}" = yes) + +if test "$GCC" = yes; then + if test "${enable_cplusplus}" = yes; then + case "$host" in + *-*-cygwin* | *-*-mingw*) + AC_MSG_CHECKING([whether libsupc++ required]) + SUPC="`$CXX -print-file-name=libsupc++.a 2>/dev/null`" + if test -n "$SUPC" -a "$SUPC" != "libsupc++.a"; then + AC_MSG_RESULT(yes) + CXXLIBS="-lsupc++" + else + AC_MSG_RESULT(no) + fi + ;; + esac + fi +fi + +AC_SUBST(CXX) +AC_SUBST(AM_CFLAGS) +AC_SUBST(AM_CPPFLAGS) +AC_SUBST(CXXLIBS) + +# Configuration of shared libraries +# +AC_MSG_CHECKING(whether to build shared libraries) +AC_ENABLE_SHARED + +case "$host" in + alpha-*-openbsd*) + enable_shared=no + ;; + *) + ;; +esac + +AC_MSG_RESULT($enable_shared) + +# Compile with GC_DLL defined unless building static libraries. +if test "${enable_shared}" = yes; then + if test "${enable_static}" = no; then + AC_DEFINE(GC_DLL) + if test "$GCC" = yes; then + # Pass -fvisibility=hidden option if supported + AC_MSG_CHECKING([whether compiler supports -fvisibility]) + old_CFLAGS="$CFLAGS" + CFLAGS="-Werror -fvisibility=hidden $CFLAGS" + AC_TRY_COMPILE([],[], [ac_cv_fvisibility_hidden=yes], + [ac_cv_fvisibility_hidden=no]) + CFLAGS="$old_CFLAGS" + AS_IF([test "$ac_cv_fvisibility_hidden" = yes], + [CFLAGS="-DGC_VISIBILITY_HIDDEN_SET -fvisibility=hidden $CFLAGS"], + [CFLAGS="-DGC_NO_VISIBILITY $CFLAGS"]) + AC_MSG_RESULT($ac_cv_fvisibility_hidden) + fi + fi +fi + +# Configuration of machine-dependent code +# +AC_MSG_CHECKING(which machine-dependent code should be used) +machdep= +case "$host" in + alpha-*-openbsd*) + if test x"${ac_cv_lib_dl_dlopen}" != xyes ; then + AC_MSG_WARN( + "OpenBSD/Alpha without dlopen(). Shared library support is disabled.") + fi + ;; + i?86-*-solaris2.[[89]]) + # PROC_VDB appears to work in 2.8 and 2.9 but not in 2.10+ (for now). + AC_DEFINE([SOLARIS25_PROC_VDB_BUG_FIXED], 1, + [See the comment in gcconfig.h.]) + ;; + mips-*-*) + dnl AC_DEFINE(NO_EXECUTE_PERMISSION) + dnl This is now redundant, but it is also important for incremental GC + dnl performance under Irix. + ;; + sparc-*-netbsd*) + machdep="sparc_netbsd_mach_dep.lo" + ;; + sparc*-*-linux* | sparc*-*-openbsd* | sparc64-*-freebsd* | sparc64-*-netbsd*) + machdep="sparc_mach_dep.lo" + ;; + sparc-sun-solaris2.3) + machdep="sparc_mach_dep.lo" + AC_DEFINE(SUNOS53_SHARED_LIB, 1, + [Define to work around a Solaris 5.3 bug (see dyn_load.c).]) + ;; + sparc*-sun-solaris2*) + machdep="sparc_mach_dep.lo" + ;; + ia64-*-*) + machdep="ia64_save_regs_in_stack.lo" + ;; + *-*-nacl*) + AC_DEFINE(NO_EXECUTE_PERMISSION) + ;; +esac +AC_MSG_RESULT($machdep) +addobjs="$addobjs $machdep" +AC_SUBST(addobjs) +AC_SUBST(addlibs) + +AC_PROG_LIBTOOL + +dnl We use these options to decide which functions to include. +AC_ARG_WITH(target-subdir, +[ --with-target-subdir=SUBDIR + configuring with a cross compiler]) +AC_ARG_WITH(cross-host, +[ --with-cross-host=HOST configuring with a cross compiler]) + +# automake wants to see AC_EXEEXT. But we don't need it. And having +# it is actually a problem, because the compiler we're passed can't +# necessarily do a full link. So we fool automake here. +if false; then + # autoconf 2.50 runs AC_EXEEXT by default, and the macro expands + # to nothing, so nothing would remain between `then' and `fi' if it + # were not for the `:' below. + : + AC_EXEEXT +fi + +dnl As of 4.13a2, the collector will not properly work on Solaris when +dnl built with gcc and -O. So we remove -O in the appropriate case. +dnl Not needed anymore on Solaris. +AC_MSG_CHECKING(whether Solaris gcc optimization fix is necessary) +case "$host" in + *aix*) + if test "$GCC" = yes; then + AC_MSG_RESULT(yes) + new_CFLAGS= + for i in $CFLAGS; do + case "$i" in + -O*) + ;; + *) + new_CFLAGS="$new_CFLAGS $i" + ;; + esac + done + CFLAGS="$new_CFLAGS" + else + AC_MSG_RESULT(no) + fi + ;; + *) AC_MSG_RESULT(no) ;; +esac + +dnl Include defines that have become de facto standard. +dnl ALL_INTERIOR_POINTERS and NO_EXECUTE_PERMISSION can be overridden +dnl in the startup code. +AC_DEFINE([NO_EXECUTE_PERMISSION], [1], + [Define to make the collector not allocate executable memory + by default.]) +AC_DEFINE([ALL_INTERIOR_POINTERS], [1], + [Define to recognise all pointers to the interior of objects.]) + + +dnl Interface Selection +dnl ------------------- +dnl +dnl By default, make the library as general as possible. +dnl enable_gcj_support=no +AC_ARG_ENABLE(gcj-support, + [AC_HELP_STRING([--disable-gcj-support], + [Disable support for gcj.])]) +if test x"$enable_gcj_support" != xno; then + AC_DEFINE(GC_GCJ_SUPPORT, 1, [Define to include support for gcj.]) + case "$host" in + *-*-kfreebsd*-gnu) + # FIXME: For a reason, gctest hangs up on kFreeBSD if both of + # THREAD_LOCAL_ALLOC and GC_ENABLE_SUSPEND_THREAD are defined. + if test "${enable_thread_local_alloc}" = no; then + AC_DEFINE(GC_ENABLE_SUSPEND_THREAD) + fi + ;; + *) + AC_DEFINE([GC_ENABLE_SUSPEND_THREAD], 1, + [Define to turn on GC_suspend_thread support.]) + ;; + esac +fi + +dnl Interaction with other programs that might use signals. +AC_ARG_ENABLE(sigrt-signals, + [AC_HELP_STRING([--enable-sigrt-signals], + [Force GC to use SIGRTMIN-based signals for thread suspend/resume])]) +if test x"${enable_sigrt_signals}" = xyes; then + AC_DEFINE([GC_USESIGRT_SIGNALS], 1, + [Force the GC to use signals based on SIGRTMIN+k.]) +fi + + +dnl Debugging +dnl --------- + +AH_TEMPLATE([GC_HAVE_BUILTIN_BACKTRACE], + [Define if backtrace information is supported.]) +AH_TEMPLATE([MAKE_BACK_GRAPH], [See doc/README.macros.]) +AH_TEMPLATE([SAVE_CALL_COUNT], + [The number of caller frames saved when allocating with the + debugging API.]) +UNWINDLIBS= +AC_ARG_ENABLE(gc-debug, +[AC_HELP_STRING([--enable-gc-debug], + [include full support for pointer backtracing etc.])], +[ if test "$enable_gc_debug" = "yes"; then + AC_MSG_WARN("Should define GC_DEBUG and use debug alloc in clients.") + AC_DEFINE([KEEP_BACK_PTRS], 1, + [Define to save back-pointers in debugging headers.]) + keep_back_ptrs=true + AC_DEFINE([DBG_HDRS_ALL], 1, + [Define to force debug headers on all objects.]) + AH_TEMPLATE([SHORT_DBG_HDRS], + [Shorten the headers to minimize object size at the expense + of checking for writes past the end (see doc/README.macros).]) + + case $host in + ia64-*-linux* ) + AC_DEFINE(MAKE_BACK_GRAPH) + AC_DEFINE(SAVE_CALL_COUNT, 8) + AC_CHECK_LIB(unwind, backtrace, [ + AC_DEFINE(GC_HAVE_BUILTIN_BACKTRACE) + UNWINDLIBS=-lunwind + AC_MSG_WARN("Client code may need to link against libunwind.") + ]) + ;; + x86-*-linux* | i586-*-linux* | i686-*-linux* | x86_64-*-linux* ) + AC_DEFINE(MAKE_BACK_GRAPH) + AC_MSG_WARN("Client must not use -fomit-frame-pointer.") + AC_DEFINE(SAVE_CALL_COUNT, 8) + ;; + i[3456]86-*-dgux*) + AC_DEFINE(MAKE_BACK_GRAPH) + ;; + esac ] + fi) +AM_CONDITIONAL([MAKE_BACK_GRAPH], [test x"$enable_gc_debug" = xyes]) +AM_CONDITIONAL([KEEP_BACK_PTRS], [test x"$keep_back_ptrs" = xtrue]) + +# Check for dladdr (used for debugging). +AC_MSG_CHECKING(for dladdr) +have_dladdr=no +old_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS $CFLAGS_EXTRA" +AC_TRY_COMPILE([ +#define _GNU_SOURCE 1 +#include ], [{ + Dl_info info; + (void)dladdr("", &info); +}], [ have_dladdr=yes ]) +CFLAGS="$old_CFLAGS" +AC_MSG_RESULT($have_dladdr) +if test x"$have_dladdr" = xyes; then + AC_DEFINE([HAVE_DLADDR], 1, [Define to use 'dladdr' function.]) +fi + +# sigsetjmp could be a macro (thus AC_CHECK_FUNCS cannot be used). +AC_MSG_CHECKING(for sigsetjmp) +old_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS $CFLAGS_EXTRA" +AC_TRY_LINK([#include ], + [sigjmp_buf t; sigsetjmp(t, 0)], + [AC_MSG_RESULT(yes)], + [AC_MSG_RESULT(no) + AC_DEFINE([GC_NO_SIGSETJMP], [1], [Missing sigsetjmp])]) +CFLAGS="$old_CFLAGS" + +# Check for AViiON Machines running DGUX +ac_is_dgux=no +AC_CHECK_HEADER(sys/dg_sys_info.h, +[ac_is_dgux=yes;]) + + ## :GOTCHA: we do not check anything but sys/dg_sys_info.h +if test $ac_is_dgux = yes; then + dgux_spec_opts="-DDGUX -D_DGUX_SOURCE -Di386 -mno-legend -O2" + CFLAGS="$dgux_spec_opts $CFLAGS" + CXXFLAGS="$dgux_spec_opts $CXXFLAGS" + if test "$enable_gc_debug" = "yes"; then + CFLAGS="-g -mstandard $CFLAGS" + CXXFLAGS="-g -mstandard $CXXFLAGS" + fi + AC_SUBST(CFLAGS) + AC_SUBST(CXXFLAGS) +fi + +AC_ARG_ENABLE(java-finalization, + [AC_HELP_STRING([--disable-java-finalization], + [Disable support for java finalization.])]) +if test x"$enable_java_finalization" != xno; then + AC_DEFINE([JAVA_FINALIZATION], 1, [See doc/README.macros.]) +fi + +AC_ARG_ENABLE(atomic-uncollectable, + [AC_HELP_STRING([--disable-atomic-uncollectible], + [Disable support for atomic uncollectible allocation.])]) +if test x"$enable_atomic_uncollectible" != x"no"; then + AC_DEFINE([GC_ATOMIC_UNCOLLECTABLE], 1, + [Define to enable atomic uncollectible allocation.]) +fi + +AC_ARG_ENABLE(redirect-malloc, + [AC_HELP_STRING([--enable-redirect-malloc], + [Redirect malloc and friends to GC routines])]) + +if test "${enable_redirect_malloc}" = yes; then + if test "${enable_gc_debug}" = yes; then + AC_DEFINE([REDIRECT_MALLOC], GC_debug_malloc_replacement, + [If defined, redirect malloc to this function.]) + AC_DEFINE([REDIRECT_REALLOC], GC_debug_realloc_replacement, + [If defined, redirect GC_realloc to this function.]) + AC_DEFINE([REDIRECT_FREE], GC_debug_free, + [If defined, redirect free to this function.]) + else + AC_DEFINE(REDIRECT_MALLOC, GC_malloc) + fi + AC_DEFINE([GC_USE_DLOPEN_WRAP], 1, [See doc/README.macros.]) +fi + +AC_ARG_ENABLE(disclaim, + [AC_HELP_STRING([--disable-disclaim], + [Disable alternative (more efficient) finalization interface.])]) +if test x"$enable_disclaim" != xno; then + AC_DEFINE(ENABLE_DISCLAIM, 1, + [Define to enable alternative finalization interface.]) +fi +AM_CONDITIONAL(ENABLE_DISCLAIM, + [test x"$enable_disclaim" != xno]) + +AC_ARG_ENABLE(large-config, + [AC_HELP_STRING([--enable-large-config], + [Optimize for large (> 100 MB) heap or root set])]) + +if test "${enable_large_config}" = yes; then + AC_DEFINE(LARGE_CONFIG, 1, [Define to optimize for large heaps or root sets.]) +fi + +AC_ARG_ENABLE(handle-fork, + [AC_HELP_STRING([--enable-handle-fork[=yes|no|auto|manual]], + [Attempt to ensure a usable collector after fork() + in multi-threaded programs (default: auto; + manual: GC_atfork_prepare/parent/child should be + called by the client)])]) +if test "${enable_handle_fork}" = yes; then + AC_DEFINE(HANDLE_FORK, 1, + [Define to install pthread_atfork() handlers by default.]) +elif test "${enable_handle_fork}" = no; then + AC_DEFINE(NO_HANDLE_FORK, 1, + [Prohibit installation of pthread_atfork() handlers.]) +fi + +dnl This is something of a hack. When cross-compiling we turn off +dnl some functionality. We also enable the "small" configuration. +dnl These is only correct when targeting an embedded system. FIXME. +if test -n "${with_cross_host}"; then + AC_DEFINE([NO_CLOCK], 1, [Define to not use system clock (cross compiling).]) + AC_DEFINE([SMALL_CONFIG], 1, + [Define to tune the collector for small heap sizes.]) +fi + +if test "$enable_gc_debug" = "no"; then + AC_DEFINE([NO_DEBUGGING], 1, + [Disable debugging, like GC_dump and its callees.]) +fi + +AC_SUBST(UNWINDLIBS) + +AC_ARG_ENABLE(gc-assertions, + [AC_HELP_STRING([--enable-gc-assertions], + [collector-internal assertion checking])]) +if test "${enable_gc_assertions}" = yes; then + AC_DEFINE([GC_ASSERTIONS], 1, [Define to enable internal debug assertions.]) +fi + +AC_ARG_ENABLE(munmap, + [AC_HELP_STRING([--enable-munmap=N], + [Return page to the OS if empty for N collections + (default: 6)])], + MUNMAP_THRESHOLD=$enableval) +if test x$enable_munmap != xno; then + AC_DEFINE([USE_MMAP], 1, + [Define to use mmap instead of sbrk to expand the heap.]) + AH_TEMPLATE([USE_WINALLOC], + [Define to use Win32 VirtualAlloc (instead of sbrk or + mmap) to expand the heap.]) + AC_DEFINE([USE_MUNMAP], 1, + [Define to return memory to OS with munmap calls + (see doc/README.macros).]) + if test x$MUNMAP_THRESHOLD = x -o x$MUNMAP_THRESHOLD = xyes; then + MUNMAP_THRESHOLD=6 + fi + AC_DEFINE_UNQUOTED([MUNMAP_THRESHOLD], [${MUNMAP_THRESHOLD}], + [Number of GC cycles to wait before unmapping an unused block.]) +fi + +AC_ARG_ENABLE(mmap, + [AC_HELP_STRING([--enable-mmap=yes|no], + [return page to the os if empty for N collections])], + gc_use_mmap=$enableval) + if test "${gc_use_mmap}" = "yes"; then + AC_DEFINE([USE_MMAP], 1, + [Define to use mmap instead of sbrk to expand the heap.]) + fi + +AC_ARG_ENABLE(ignore-dynamic-loading, + [AC_HELP_STRING([--enable-ignore-dynamic-loading=yes|no], + [Don't define DYNAMIC_LOADING even if supported])], + ignore_dynamic_loading=$enableval) + if test "${ignore_dynamic_loading}" = "yes"; then + AC_DEFINE([IGNORE_DYNAMIC_LOADING], 1, + [Don't define DYNAMIC_LOADING even if supported]) + fi + +AC_ARG_ENABLE(dont-register-main-static-data, + [AC_HELP_STRING([--enable-dont-register-main-static-data=yes|no], + [GC_DONT_REGISTER_MAIN_STATIC_DATA])], + dont_register_main_static_data=$enableval) + if test "${dont_register_main_static_data}" = "yes"; then + AC_DEFINE([GC_DONT_REGISTER_MAIN_STATIC_DATA], 1, + [GC_DONT_REGISTER_MAIN_STATIC_DATA]) + fi + + +AC_ARG_ENABLE(checksums, + [AS_HELP_STRING([--enable-checksums], + [Report on erroneously cleared dirty bits at + substantial performance cost; use only for + debugging of the incremental collector])]) +if test x$enable_checksums = xyes; then + if test x$enable_munmap != xno -o x$THREADS != xnone; then + AC_MSG_ERROR([CHECKSUMS not compatible with USE_MUNMAP or threads]) + fi + AC_DEFINE([CHECKSUMS], 1, + [Erroneously cleared dirty bits checking. Use only for + debugging of the incremental collector.]) +fi +AM_CONDITIONAL([CHECKSUMS], test x$enable_checksums = xyes) + +AM_CONDITIONAL(USE_LIBDIR, test -z "$with_cross_host") + +AC_ARG_ENABLE(werror, [--enable-werror Pass -Werror to the C compiler], + werror_flag=$enableval, werror_flag=no) +if test x$werror_flag = xyes; then + WERROR_CFLAGS="-Werror" + case "$host" in + # _dyld_bind_fully_image_containing_address is deprecated in OS X 10.5+ + *-*-darwin*) + WERROR_CFLAGS="$WERROR_CFLAGS -Wno-deprecated-declarations" + ;; + esac +fi +AC_SUBST([WERROR_CFLAGS]) + +AC_ARG_ENABLE(single-obj-compilation, + [AC_HELP_STRING([--enable-single-obj-compilation], + [Compile all libgc source files into single .o + (default: yes if static libraries are disabled)])], + [], [ AS_IF([test x"$enable_static" = xno], + [enable_single_obj_compilation=yes]) ]) +AM_CONDITIONAL([SINGLE_GC_OBJ], + [test x"$enable_single_obj_compilation" = xyes]) + +AC_ARG_ENABLE(gcov, AC_HELP_STRING([--enable-gcov], + [Turn on code coverage analysis])) +if test "$enable_gcov" = "yes"; then + CFLAGS="$CFLAGS --coverage" + if test "${enable_shared}" = no; then + # FIXME: As of g++-4.8.4/x86_64, in case of shared library build, test_cpp + # linkage fails with "hidden symbol atexit is referenced by DSO" message. + CXXFLAGS="$CXXFLAGS --coverage" + fi + # Turn off optimization to get accurate line numbers. + CFLAGS=`echo "$CFLAGS" | sed -e 's/-O\(1\|2\|3\|4\|s\|fast\)\?//g'` + CXXFLAGS=`echo "$CXXFLAGS" | sed -e 's/-O\(1\|2\|3\|4\|s\|fast\)\?//g'` +fi + +AC_ARG_ENABLE(docs, + [AC_HELP_STRING([--disable-docs], + [Do not build and install documentation])]) +AM_CONDITIONAL(ENABLE_DOCS, test x$enable_docs != xno) + +# Atomic Ops +# ---------- + +# Do we want to use an external libatomic_ops? By default use it if it's +# found. +AC_ARG_WITH([libatomic-ops], + [AS_HELP_STRING([--with-libatomic-ops[=yes|no|check|none]], + [Use an external libatomic_ops? (default: check; + none: use compiler intrinsics or no thread support)])], + [], [ AS_IF([test x"$THREADS" != xnone], + [with_libatomic_ops=check], [with_libatomic_ops=none]) ]) + +# Check whether compiler atomic intrinsics can be used. +if test x"$with_libatomic_ops" = xcheck; then + AC_MSG_CHECKING(for compiler intrinsics support) + old_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $CFLAGS_EXTRA -DGC_BUILTIN_ATOMIC" + CFLAGS="$CFLAGS -I${srcdir}/include -I${srcdir}/tests" + AC_TRY_RUN([#include "test_atomic_ops.c"], + [AC_MSG_RESULT(yes) + with_libatomic_ops=none], + [AC_MSG_RESULT(no)], [AC_MSG_RESULT(skipped because cross-compiling)]) + CFLAGS="$old_CFLAGS" +fi + +# Check for an external libatomic_ops if the above answer is "yes" or "check". +# If not found, fail on "yes", and convert "check" to "no". +# First, check that libatomic_ops usage is not disabled explicitly. +missing_libatomic_ops=false +#AS_IF([test x"$with_libatomic_ops" != xno -a x"$with_libatomic_ops" != xnone], +# [ missing_libatomic_ops=true ]) + +# Note: "syntax error near unexpected token ATOMIC_OPS" reported by configure +# means Autotools pkg.m4 file was not found during aclocal.m4 generation; +# in this case, most probably, you should run pkg-config once before running +# autogen.sh (autoreconf); alternatively, comment out the following 3 lines. +#AS_IF([test x$missing_libatomic_ops = xtrue], +# [ PKG_CHECK_MODULES([ATOMIC_OPS], [atomic_ops], +# [ missing_libatomic_ops=false ], [ [] ]) ]) + +# Retry with AC_CHECK_HEADER if PKG_CHECK_MODULES failed. +AS_IF([test x$missing_libatomic_ops = xtrue], + [ AC_CHECK_HEADER([atomic_ops.h], [missing_libatomic_ops=false]) ]) +AS_IF([test x$missing_libatomic_ops = xtrue], + [ AS_IF([test x"$with_libatomic_ops" != xcheck], + [ AC_MSG_ERROR([An external libatomic_ops was not found]) ]) + with_libatomic_ops=no ]) + +# If we have neither an external or an internal version, offer a useful hint +# and exit. +AS_IF([test x"$with_libatomic_ops" = xno \ + -a ! -e "$srcdir/libatomic_ops/src/atomic_ops.h"], + [ AC_MSG_ERROR([libatomic_ops is required. You can either install it on + your system, or fetch and unpack a recent version into the + source directory and link or rename it to libatomic_ops.]) ]) + +# Finally, emit the definitions for bundled or external AO. +AC_MSG_CHECKING([which libatomic_ops to use]) +AS_IF([test x"$with_libatomic_ops" != xno], + [ AS_IF([test x"$with_libatomic_ops" != xnone -a x"$THREADS" != xnone], + [ AC_MSG_RESULT([external]) ], + [ AC_MSG_RESULT([none]) + AS_IF([test x"$THREADS" != xnone], + [ AC_DEFINE([GC_BUILTIN_ATOMIC], [1], + [Use C11 (GCC) atomic intrinsics instead of + libatomic_ops primitives]) ]) ]) ], + [ AC_MSG_RESULT([internal]) + ATOMIC_OPS_CFLAGS='-I$(top_builddir)/libatomic_ops/src -I$(top_srcdir)/libatomic_ops/src' + ATOMIC_OPS_LIBS="" + AC_SUBST([ATOMIC_OPS_CFLAGS]) + AC_CONFIG_SUBDIRS([libatomic_ops]) + ]) +AM_CONDITIONAL([USE_INTERNAL_LIBATOMIC_OPS], + [test x$with_libatomic_ops = xno -a x"$THREADS" != xnone]) +AM_CONDITIONAL([NEED_ATOMIC_OPS_ASM], + [test x$with_libatomic_ops = xno -a x$need_atomic_ops_asm = xtrue]) + +dnl Produce the Files +dnl ----------------- + +AC_CONFIG_FILES([Makefile bdw-gc.pc]) + +AC_CONFIG_COMMANDS([default],, + [ srcdir="${srcdir}" + host=${host} + CONFIG_SHELL=${CONFIG_SHELL-/bin/sh} + CC="${CC}" + DEFS="$DEFS" ]) + +AC_OUTPUT diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/cord/cord.am b/unity-2019.4.24f1-mbe/external/bdwgc/cord/cord.am new file mode 100644 index 000000000..b8794c295 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/cord/cord.am @@ -0,0 +1,34 @@ +## This file is processed with automake. + +# Info (current:revision:age) for the Libtool versioning system. +# These numbers should be updated at most once just before the release, +# and, optionally, at most once during the development (after the release). +LIBCORD_VER_INFO = 4:0:3 + +lib_LTLIBRARIES += libcord.la + +libcord_la_LIBADD = $(top_builddir)/libgc.la +libcord_la_LDFLAGS = -version-info $(LIBCORD_VER_INFO) -no-undefined +libcord_la_CPPFLAGS = $(AM_CPPFLAGS) + +libcord_la_SOURCES = \ + cord/cordbscs.c \ + cord/cordprnt.c \ + cord/cordxtra.c + +TESTS += cordtest$(EXEEXT) +check_PROGRAMS += cordtest +cordtest_SOURCES = cord/tests/cordtest.c +cordtest_LDADD = $(top_builddir)/libgc.la $(top_builddir)/libcord.la + +EXTRA_DIST += \ + cord/tests/de.c \ + cord/tests/de_cmds.h \ + cord/tests/de_win.c \ + cord/tests/de_win.h \ + cord/tests/de_win.rc + +pkginclude_HEADERS += \ + include/cord.h \ + include/cord_pos.h \ + include/ec.h diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/cord/cordbscs.c b/unity-2019.4.24f1-mbe/external/bdwgc/cord/cordbscs.c new file mode 100644 index 000000000..9e6c8ab5c --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/cord/cordbscs.c @@ -0,0 +1,923 @@ +/* + * Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifndef CORD_BUILD +# define CORD_BUILD +#endif + +# include "gc.h" +# include "cord.h" +# include +# include +# include + +/* An implementation of the cord primitives. These are the only */ +/* Functions that understand the representation. We perform only */ +/* minimal checks on arguments to these functions. Out of bounds */ +/* arguments to the iteration functions may result in client functions */ +/* invoked on garbage data. In most cases, client functions should be */ +/* programmed defensively enough that this does not result in memory */ +/* smashes. */ + +typedef void (* oom_fn)(void); + +oom_fn CORD_oom_fn = (oom_fn) 0; + +# define OUT_OF_MEMORY { if (CORD_oom_fn != (oom_fn) 0) (*CORD_oom_fn)(); \ + ABORT("Out of memory"); } +# define ABORT(msg) { fprintf(stderr, "%s\n", msg); abort(); } + +typedef unsigned long word; + + struct Concatenation { + char null; + char header; + char depth; /* concatenation nesting depth. */ + unsigned char left_len; + /* Length of left child if it is sufficiently */ + /* short; 0 otherwise. */ +# define MAX_LEFT_LEN 255 + word len; + CORD left; /* length(left) > 0 */ + CORD right; /* length(right) > 0 */ + }; + + struct Function { + char null; + char header; + char depth; /* always 0 */ + char left_len; /* always 0 */ + word len; + CORD_fn fn; + void * client_data; + }; + + struct Generic { + char null; + char header; + char depth; + char left_len; + word len; + }; + +typedef union { + struct Concatenation concatenation; + struct Function function; + struct Generic generic; + char string[1]; +} CordRep; + +# define CONCAT_HDR 1 + +# define FN_HDR 4 +# define SUBSTR_HDR 6 + /* Substring nodes are a special case of function nodes. */ + /* The client_data field is known to point to a substr_args */ + /* structure, and the function is either CORD_apply_access_fn */ + /* or CORD_index_access_fn. */ + +/* The following may be applied only to function and concatenation nodes: */ +#define IS_CONCATENATION(s) (((CordRep *)s)->generic.header == CONCAT_HDR) + +#define IS_FUNCTION(s) ((((CordRep *)s)->generic.header & FN_HDR) != 0) + +#define IS_SUBSTR(s) (((CordRep *)s)->generic.header == SUBSTR_HDR) + +#define LEN(s) (((CordRep *)s) -> generic.len) +#define DEPTH(s) (((CordRep *)s) -> generic.depth) +#define GEN_LEN(s) (CORD_IS_STRING(s) ? strlen(s) : LEN(s)) + +#define LEFT_LEN(c) ((c) -> left_len != 0? \ + (c) -> left_len \ + : (CORD_IS_STRING((c) -> left) ? \ + (c) -> len - GEN_LEN((c) -> right) \ + : LEN((c) -> left))) + +#define SHORT_LIMIT (sizeof(CordRep) - 1) + /* Cords shorter than this are C strings */ + + +/* Dump the internal representation of x to stdout, with initial */ +/* indentation level n. */ +void CORD_dump_inner(CORD x, unsigned n) +{ + size_t i; + + for (i = 0; i < (size_t)n; i++) { + fputs(" ", stdout); + } + if (x == 0) { + fputs("NIL\n", stdout); + } else if (CORD_IS_STRING(x)) { + for (i = 0; i <= SHORT_LIMIT; i++) { + if (x[i] == '\0') break; + putchar(x[i]); + } + if (x[i] != '\0') fputs("...", stdout); + putchar('\n'); + } else if (IS_CONCATENATION(x)) { + struct Concatenation * conc = &(((CordRep *)x) -> concatenation); + + printf("Concatenation: %p (len: %d, depth: %d)\n", + (void *)x, (int)(conc -> len), (int)(conc -> depth)); + CORD_dump_inner(conc -> left, n+1); + CORD_dump_inner(conc -> right, n+1); + } else /* function */ { + struct Function * func = &(((CordRep *)x) -> function); + + if (IS_SUBSTR(x)) printf("(Substring) "); + printf("Function: %p (len: %d): ", (void *)x, (int)(func -> len)); + for (i = 0; i < 20 && i < func -> len; i++) { + putchar((*(func -> fn))(i, func -> client_data)); + } + if (i < func -> len) fputs("...", stdout); + putchar('\n'); + } +} + +/* Dump the internal representation of x to stdout */ +void CORD_dump(CORD x) +{ + CORD_dump_inner(x, 0); + fflush(stdout); +} + +CORD CORD_cat_char_star(CORD x, const char * y, size_t leny) +{ + size_t result_len; + size_t lenx; + int depth; + + if (x == CORD_EMPTY) return(y); + if (leny == 0) return(x); + if (CORD_IS_STRING(x)) { + lenx = strlen(x); + result_len = lenx + leny; + if (result_len <= SHORT_LIMIT) { + char * result = (char *)GC_MALLOC_ATOMIC(result_len + 1); + + if (result == 0) OUT_OF_MEMORY; + memcpy(result, x, lenx); + memcpy(result + lenx, y, leny); + result[result_len] = '\0'; + return((CORD) result); + } else { + depth = 1; + } + } else { + CORD right; + CORD left; + char * new_right; + + lenx = LEN(x); + + if (leny <= SHORT_LIMIT/2 + && IS_CONCATENATION(x) + && CORD_IS_STRING(right = ((CordRep *)x) -> concatenation.right)) { + size_t right_len; + + /* Merge y into right part of x. */ + if (!CORD_IS_STRING(left = ((CordRep *)x) -> concatenation.left)) { + right_len = lenx - LEN(left); + } else if (((CordRep *)x) -> concatenation.left_len != 0) { + right_len = lenx - ((CordRep *)x) -> concatenation.left_len; + } else { + right_len = strlen(right); + } + result_len = right_len + leny; /* length of new_right */ + if (result_len <= SHORT_LIMIT) { + new_right = (char *)GC_MALLOC_ATOMIC(result_len + 1); + if (new_right == 0) OUT_OF_MEMORY; + memcpy(new_right, right, right_len); + memcpy(new_right + right_len, y, leny); + new_right[result_len] = '\0'; + y = new_right; + leny = result_len; + x = left; + lenx -= right_len; + /* Now fall through to concatenate the two pieces: */ + } + if (CORD_IS_STRING(x)) { + depth = 1; + } else { + depth = DEPTH(x) + 1; + } + } else { + depth = DEPTH(x) + 1; + } + result_len = lenx + leny; + } + { + /* The general case; lenx, result_len is known: */ + struct Concatenation * result = GC_NEW(struct Concatenation); + + if (NULL == result) OUT_OF_MEMORY; + result->header = CONCAT_HDR; + result->depth = (char)depth; + if (lenx <= MAX_LEFT_LEN) + result->left_len = (unsigned char)lenx; + result->len = (word)result_len; + result->left = x; + result->right = y; + if (depth >= MAX_DEPTH) { + return(CORD_balance((CORD)result)); + } else { + return((CORD) result); + } + } +} + + +CORD CORD_cat(CORD x, CORD y) +{ + size_t result_len; + int depth; + size_t lenx; + + if (x == CORD_EMPTY) return(y); + if (y == CORD_EMPTY) return(x); + if (CORD_IS_STRING(y)) { + return(CORD_cat_char_star(x, y, strlen(y))); + } else if (CORD_IS_STRING(x)) { + lenx = strlen(x); + depth = DEPTH(y) + 1; + } else { + int depthy = DEPTH(y); + + lenx = LEN(x); + depth = DEPTH(x) + 1; + if (depthy >= depth) depth = depthy + 1; + } + result_len = lenx + LEN(y); + { + struct Concatenation * result = GC_NEW(struct Concatenation); + + if (NULL == result) OUT_OF_MEMORY; + result->header = CONCAT_HDR; + result->depth = (char)depth; + if (lenx <= MAX_LEFT_LEN) + result->left_len = (unsigned char)lenx; + result->len = (word)result_len; + result->left = x; + result->right = y; + if (depth >= MAX_DEPTH) { + return(CORD_balance((CORD)result)); + } else { + return((CORD) result); + } + } +} + + +static CordRep *CORD_from_fn_inner(CORD_fn fn, void * client_data, size_t len) +{ + if (len == 0) return(0); + if (len <= SHORT_LIMIT) { + char * result; + size_t i; + char buf[SHORT_LIMIT+1]; + + for (i = 0; i < len; i++) { + char c = (*fn)(i, client_data); + + if (c == '\0') goto gen_case; + buf[i] = c; + } + + result = (char *)GC_MALLOC_ATOMIC(len + 1); + if (result == 0) OUT_OF_MEMORY; + memcpy(result, buf, len); + result[len] = '\0'; + return (CordRep *)result; + } + gen_case: + { + struct Function * result = GC_NEW(struct Function); + + if (NULL == result) OUT_OF_MEMORY; + result->header = FN_HDR; + /* depth is already 0 */ + result->len = (word)len; + result->fn = fn; + result->client_data = client_data; + return (CordRep *)result; + } +} + +CORD CORD_from_fn(CORD_fn fn, void * client_data, size_t len) +{ + return (/* const */ CORD) CORD_from_fn_inner(fn, client_data, len); +} + +size_t CORD_len(CORD x) +{ + if (x == 0) { + return(0); + } else { + return(GEN_LEN(x)); + } +} + +struct substr_args { + CordRep * sa_cord; + size_t sa_index; +}; + +char CORD_index_access_fn(size_t i, void * client_data) +{ + struct substr_args *descr = (struct substr_args *)client_data; + + return(((char *)(descr->sa_cord))[i + descr->sa_index]); +} + +char CORD_apply_access_fn(size_t i, void * client_data) +{ + struct substr_args *descr = (struct substr_args *)client_data; + struct Function * fn_cord = &(descr->sa_cord->function); + + return((*(fn_cord->fn))(i + descr->sa_index, fn_cord->client_data)); +} + +/* A version of CORD_substr that simply returns a function node, thus */ +/* postponing its work. The fourth argument is a function that may */ +/* be used for efficient access to the ith character. */ +/* Assumes i >= 0 and i + n < length(x). */ +CORD CORD_substr_closure(CORD x, size_t i, size_t n, CORD_fn f) +{ + struct substr_args * sa = GC_NEW(struct substr_args); + CordRep * result; + + if (sa == 0) OUT_OF_MEMORY; + sa->sa_cord = (CordRep *)x; + sa->sa_index = i; + result = CORD_from_fn_inner(f, (void *)sa, n); + if ((CORD)result != CORD_EMPTY && 0 == result -> function.null) + result -> function.header = SUBSTR_HDR; + return (CORD)result; +} + +# define SUBSTR_LIMIT (10 * SHORT_LIMIT) + /* Substrings of function nodes and flat strings shorter than */ + /* this are flat strings. Othewise we use a functional */ + /* representation, which is significantly slower to access. */ + +/* A version of CORD_substr that assumes i >= 0, n > 0, and i + n < length(x).*/ +CORD CORD_substr_checked(CORD x, size_t i, size_t n) +{ + if (CORD_IS_STRING(x)) { + if (n > SUBSTR_LIMIT) { + return(CORD_substr_closure(x, i, n, CORD_index_access_fn)); + } else { + char * result = (char *)GC_MALLOC_ATOMIC(n + 1); + + if (result == 0) OUT_OF_MEMORY; + strncpy(result, x+i, n); + result[n] = '\0'; + return(result); + } + } else if (IS_CONCATENATION(x)) { + struct Concatenation * conc = &(((CordRep *)x) -> concatenation); + size_t left_len = LEFT_LEN(conc); + size_t right_len = conc -> len - left_len; + + if (i >= left_len) { + if (n == right_len) return(conc -> right); + return(CORD_substr_checked(conc -> right, i - left_len, n)); + } else if (i+n <= left_len) { + if (n == left_len) return(conc -> left); + return(CORD_substr_checked(conc -> left, i, n)); + } else { + /* Need at least one character from each side. */ + CORD left_part; + CORD right_part; + size_t left_part_len = left_len - i; + + if (i == 0) { + left_part = conc -> left; + } else { + left_part = CORD_substr_checked(conc -> left, i, left_part_len); + } + if (i + n == right_len + left_len) { + right_part = conc -> right; + } else { + right_part = CORD_substr_checked(conc -> right, 0, + n - left_part_len); + } + return(CORD_cat(left_part, right_part)); + } + } else /* function */ { + if (n > SUBSTR_LIMIT) { + if (IS_SUBSTR(x)) { + /* Avoid nesting substring nodes. */ + struct Function * f = &(((CordRep *)x) -> function); + struct substr_args *descr = + (struct substr_args *)(f -> client_data); + + return(CORD_substr_closure((CORD)descr->sa_cord, + i + descr->sa_index, + n, f -> fn)); + } else { + return(CORD_substr_closure(x, i, n, CORD_apply_access_fn)); + } + } else { + char * result; + struct Function * f = &(((CordRep *)x) -> function); + char buf[SUBSTR_LIMIT+1]; + char * p = buf; + size_t j; + size_t lim = i + n; + + for (j = i; j < lim; j++) { + char c = (*(f -> fn))(j, f -> client_data); + + if (c == '\0') { + return(CORD_substr_closure(x, i, n, CORD_apply_access_fn)); + } + *p++ = c; + } + result = (char *)GC_MALLOC_ATOMIC(n + 1); + if (result == 0) OUT_OF_MEMORY; + memcpy(result, buf, n); + result[n] = '\0'; + return(result); + } + } +} + +CORD CORD_substr(CORD x, size_t i, size_t n) +{ + size_t len = CORD_len(x); + + if (i >= len || n == 0) return(0); + if (i + n > len) n = len - i; + return(CORD_substr_checked(x, i, n)); +} + +/* See cord.h for definition. We assume i is in range. */ +int CORD_iter5(CORD x, size_t i, CORD_iter_fn f1, + CORD_batched_iter_fn f2, void * client_data) +{ + if (x == 0) return(0); + if (CORD_IS_STRING(x)) { + const char *p = x+i; + + if (*p == '\0') ABORT("2nd arg to CORD_iter5 too big"); + if (f2 != CORD_NO_FN) { + return((*f2)(p, client_data)); + } else { + while (*p) { + if ((*f1)(*p, client_data)) return(1); + p++; + } + return(0); + } + } else if (IS_CONCATENATION(x)) { + struct Concatenation * conc = &(((CordRep *)x) -> concatenation); + + if (i > 0) { + size_t left_len = LEFT_LEN(conc); + + if (i >= left_len) { + return(CORD_iter5(conc -> right, i - left_len, f1, f2, + client_data)); + } + } + if (CORD_iter5(conc -> left, i, f1, f2, client_data)) { + return(1); + } + return(CORD_iter5(conc -> right, 0, f1, f2, client_data)); + } else /* function */ { + struct Function * f = &(((CordRep *)x) -> function); + size_t j; + size_t lim = f -> len; + + for (j = i; j < lim; j++) { + if ((*f1)((*(f -> fn))(j, f -> client_data), client_data)) { + return(1); + } + } + return(0); + } +} + +#undef CORD_iter +int CORD_iter(CORD x, CORD_iter_fn f1, void * client_data) +{ + return(CORD_iter5(x, 0, f1, CORD_NO_FN, client_data)); +} + +int CORD_riter4(CORD x, size_t i, CORD_iter_fn f1, void * client_data) +{ + if (x == 0) return(0); + if (CORD_IS_STRING(x)) { + const char *p = x + i; + + for(;;) { + char c = *p; + + if (c == '\0') ABORT("2nd arg to CORD_riter4 too big"); + if ((*f1)(c, client_data)) return(1); + if (p == x) break; + p--; + } + return(0); + } else if (IS_CONCATENATION(x)) { + struct Concatenation * conc = &(((CordRep *)x) -> concatenation); + CORD left_part = conc -> left; + size_t left_len = LEFT_LEN(conc); + + if (i >= left_len) { + if (CORD_riter4(conc -> right, i - left_len, f1, client_data)) { + return(1); + } + return(CORD_riter4(left_part, left_len - 1, f1, client_data)); + } else { + return(CORD_riter4(left_part, i, f1, client_data)); + } + } else /* function */ { + struct Function * f = &(((CordRep *)x) -> function); + size_t j; + + for (j = i; ; j--) { + if ((*f1)((*(f -> fn))(j, f -> client_data), client_data)) { + return(1); + } + if (j == 0) return(0); + } + } +} + +int CORD_riter(CORD x, CORD_iter_fn f1, void * client_data) +{ + size_t len = CORD_len(x); + if (len == 0) return(0); + return(CORD_riter4(x, len - 1, f1, client_data)); +} + +/* + * The following functions are concerned with balancing cords. + * Strategy: + * Scan the cord from left to right, keeping the cord scanned so far + * as a forest of balanced trees of exponentially decreasing length. + * When a new subtree needs to be added to the forest, we concatenate all + * shorter ones to the new tree in the appropriate order, and then insert + * the result into the forest. + * Crucial invariants: + * 1. The concatenation of the forest (in decreasing order) with the + * unscanned part of the rope is equal to the rope being balanced. + * 2. All trees in the forest are balanced. + * 3. forest[i] has depth at most i. + */ + +typedef struct { + CORD c; + size_t len; /* Actual length of c */ +} ForestElement; + +static size_t min_len [ MAX_DEPTH ]; + +static int min_len_init = 0; + +int CORD_max_len; + +typedef ForestElement Forest [ MAX_DEPTH ]; + /* forest[i].len >= fib(i+1) */ + /* The string is the concatenation */ + /* of the forest in order of DECREASING */ + /* indices. */ + +void CORD_init_min_len(void) +{ + int i; + size_t last, previous; + + min_len[0] = previous = 1; + min_len[1] = last = 2; + for (i = 2; i < MAX_DEPTH; i++) { + size_t current = last + previous; + + if (current < last) /* overflow */ current = last; + min_len[i] = current; + previous = last; + last = current; + } + CORD_max_len = (int)last - 1; + min_len_init = 1; +} + + +void CORD_init_forest(ForestElement * forest, size_t max_len) +{ + int i; + + for (i = 0; i < MAX_DEPTH; i++) { + forest[i].c = 0; + if (min_len[i] > max_len) return; + } + ABORT("Cord too long"); +} + +/* Add a leaf to the appropriate level in the forest, cleaning */ +/* out lower levels as necessary. */ +/* Also works if x is a balanced tree of concatenations; however */ +/* in this case an extra concatenation node may be inserted above x; */ +/* This node should not be counted in the statement of the invariants. */ +void CORD_add_forest(ForestElement * forest, CORD x, size_t len) +{ + int i = 0; + CORD sum = CORD_EMPTY; + size_t sum_len = 0; + + while (len > min_len[i + 1]) { + if (forest[i].c != 0) { + sum = CORD_cat(forest[i].c, sum); + sum_len += forest[i].len; + forest[i].c = 0; + } + i++; + } + /* Sum has depth at most 1 greter than what would be required */ + /* for balance. */ + sum = CORD_cat(sum, x); + sum_len += len; + /* If x was a leaf, then sum is now balanced. To see this */ + /* consider the two cases in which forest[i-1] either is or is */ + /* not empty. */ + while (sum_len >= min_len[i]) { + if (forest[i].c != 0) { + sum = CORD_cat(forest[i].c, sum); + sum_len += forest[i].len; + /* This is again balanced, since sum was balanced, and has */ + /* allowable depth that differs from i by at most 1. */ + forest[i].c = 0; + } + i++; + } + i--; + forest[i].c = sum; + forest[i].len = sum_len; +} + +CORD CORD_concat_forest(ForestElement * forest, size_t expected_len) +{ + int i = 0; + CORD sum = 0; + size_t sum_len = 0; + + while (sum_len != expected_len) { + if (forest[i].c != 0) { + sum = CORD_cat(forest[i].c, sum); + sum_len += forest[i].len; + } + i++; + } + return(sum); +} + +/* Insert the frontier of x into forest. Balanced subtrees are */ +/* treated as leaves. This potentially adds one to the depth */ +/* of the final tree. */ +void CORD_balance_insert(CORD x, size_t len, ForestElement * forest) +{ + int depth; + + if (CORD_IS_STRING(x)) { + CORD_add_forest(forest, x, len); + } else if (IS_CONCATENATION(x) + && ((depth = DEPTH(x)) >= MAX_DEPTH + || len < min_len[depth])) { + struct Concatenation * conc = &(((CordRep *)x) -> concatenation); + size_t left_len = LEFT_LEN(conc); + + CORD_balance_insert(conc -> left, left_len, forest); + CORD_balance_insert(conc -> right, len - left_len, forest); + } else /* function or balanced */ { + CORD_add_forest(forest, x, len); + } +} + + +CORD CORD_balance(CORD x) +{ + Forest forest; + size_t len; + + if (x == 0) return(0); + if (CORD_IS_STRING(x)) return(x); + if (!min_len_init) CORD_init_min_len(); + len = LEN(x); + CORD_init_forest(forest, len); + CORD_balance_insert(x, len, forest); + return(CORD_concat_forest(forest, len)); +} + + +/* Position primitives */ + +/* Private routines to deal with the hard cases only: */ + +/* P contains a prefix of the path to cur_pos. Extend it to a full */ +/* path and set up leaf info. */ +/* Return 0 if past the end of cord, 1 o.w. */ +void CORD__extend_path(CORD_pos p) +{ + struct CORD_pe * current_pe = &(p[0].path[p[0].path_len]); + CORD top = current_pe -> pe_cord; + size_t pos = p[0].cur_pos; + size_t top_pos = current_pe -> pe_start_pos; + size_t top_len = GEN_LEN(top); + + /* Fill in the rest of the path. */ + while(!CORD_IS_STRING(top) && IS_CONCATENATION(top)) { + struct Concatenation * conc = &(((CordRep *)top) -> concatenation); + size_t left_len; + + left_len = LEFT_LEN(conc); + current_pe++; + if (pos >= top_pos + left_len) { + current_pe -> pe_cord = top = conc -> right; + current_pe -> pe_start_pos = top_pos = top_pos + left_len; + top_len -= left_len; + } else { + current_pe -> pe_cord = top = conc -> left; + current_pe -> pe_start_pos = top_pos; + top_len = left_len; + } + p[0].path_len++; + } + /* Fill in leaf description for fast access. */ + if (CORD_IS_STRING(top)) { + p[0].cur_leaf = top; + p[0].cur_start = top_pos; + p[0].cur_end = top_pos + top_len; + } else { + p[0].cur_end = 0; + } + if (pos >= top_pos + top_len) p[0].path_len = CORD_POS_INVALID; +} + +char CORD__pos_fetch(CORD_pos p) +{ + /* Leaf is a function node */ + struct CORD_pe * pe = &((p)[0].path[(p)[0].path_len]); + CORD leaf = pe -> pe_cord; + struct Function * f = &(((CordRep *)leaf) -> function); + + if (!IS_FUNCTION(leaf)) ABORT("CORD_pos_fetch: bad leaf"); + return ((*(f -> fn))(p[0].cur_pos - pe -> pe_start_pos, f -> client_data)); +} + +void CORD__next(CORD_pos p) +{ + size_t cur_pos = p[0].cur_pos + 1; + struct CORD_pe * current_pe = &((p)[0].path[(p)[0].path_len]); + CORD leaf = current_pe -> pe_cord; + + /* Leaf is not a string or we're at end of leaf */ + p[0].cur_pos = cur_pos; + if (!CORD_IS_STRING(leaf)) { + /* Function leaf */ + struct Function * f = &(((CordRep *)leaf) -> function); + size_t start_pos = current_pe -> pe_start_pos; + size_t end_pos = start_pos + f -> len; + + if (cur_pos < end_pos) { + /* Fill cache and return. */ + size_t i; + size_t limit = cur_pos + FUNCTION_BUF_SZ; + CORD_fn fn = f -> fn; + void * client_data = f -> client_data; + + if (limit > end_pos) { + limit = end_pos; + } + for (i = cur_pos; i < limit; i++) { + p[0].function_buf[i - cur_pos] = + (*fn)(i - start_pos, client_data); + } + p[0].cur_start = cur_pos; + p[0].cur_leaf = p[0].function_buf; + p[0].cur_end = limit; + return; + } + } + /* End of leaf */ + /* Pop the stack until we find two concatenation nodes with the */ + /* same start position: this implies we were in left part. */ + { + while (p[0].path_len > 0 + && current_pe[0].pe_start_pos != current_pe[-1].pe_start_pos) { + p[0].path_len--; + current_pe--; + } + if (p[0].path_len == 0) { + p[0].path_len = CORD_POS_INVALID; + return; + } + } + p[0].path_len--; + CORD__extend_path(p); +} + +void CORD__prev(CORD_pos p) +{ + struct CORD_pe * pe = &(p[0].path[p[0].path_len]); + + if (p[0].cur_pos == 0) { + p[0].path_len = CORD_POS_INVALID; + return; + } + p[0].cur_pos--; + if (p[0].cur_pos >= pe -> pe_start_pos) return; + + /* Beginning of leaf */ + + /* Pop the stack until we find two concatenation nodes with the */ + /* different start position: this implies we were in right part. */ + { + struct CORD_pe * current_pe = &((p)[0].path[(p)[0].path_len]); + + while (p[0].path_len > 0 + && current_pe[0].pe_start_pos == current_pe[-1].pe_start_pos) { + p[0].path_len--; + current_pe--; + } + } + p[0].path_len--; + CORD__extend_path(p); +} + +#undef CORD_pos_fetch +#undef CORD_next +#undef CORD_prev +#undef CORD_pos_to_index +#undef CORD_pos_to_cord +#undef CORD_pos_valid + +char CORD_pos_fetch(CORD_pos p) +{ + if (p[0].cur_end != 0) { + return(p[0].cur_leaf[p[0].cur_pos - p[0].cur_start]); + } else { + return(CORD__pos_fetch(p)); + } +} + +void CORD_next(CORD_pos p) +{ + if (p[0].cur_pos + 1 < p[0].cur_end) { + p[0].cur_pos++; + } else { + CORD__next(p); + } +} + +void CORD_prev(CORD_pos p) +{ + if (p[0].cur_end != 0 && p[0].cur_pos > p[0].cur_start) { + p[0].cur_pos--; + } else { + CORD__prev(p); + } +} + +size_t CORD_pos_to_index(CORD_pos p) +{ + return(p[0].cur_pos); +} + +CORD CORD_pos_to_cord(CORD_pos p) +{ + return(p[0].path[0].pe_cord); +} + +int CORD_pos_valid(CORD_pos p) +{ + return(p[0].path_len != CORD_POS_INVALID); +} + +void CORD_set_pos(CORD_pos p, CORD x, size_t i) +{ + if (x == CORD_EMPTY) { + p[0].path_len = CORD_POS_INVALID; + return; + } + p[0].path[0].pe_cord = x; + p[0].path[0].pe_start_pos = 0; + p[0].path_len = 0; + p[0].cur_pos = i; + CORD__extend_path(p); +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/cord/cordprnt.c b/unity-2019.4.24f1-mbe/external/bdwgc/cord/cordprnt.c new file mode 100644 index 000000000..cfe1de419 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/cord/cordprnt.c @@ -0,0 +1,438 @@ +/* + * Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* An sprintf implementation that understands cords. This is probably */ +/* not terribly portable. It assumes an ANSI stdarg.h. It further */ +/* assumes that I can make copies of va_list variables, and read */ +/* arguments repeatedly by applying va_arg to the copies. This */ +/* could be avoided at some performance cost. */ +/* We also assume that unsigned and signed integers of various kinds */ +/* have the same sizes, and can be cast back and forth. */ +/* We assume that void * and char * have the same size. */ +/* All this cruft is needed because we want to rely on the underlying */ +/* sprintf implementation whenever possible. */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifndef CORD_BUILD +# define CORD_BUILD +#endif + +#include "cord.h" +#include "ec.h" + +#include +#include +#include +#include + +#include "gc.h" + +#define CONV_SPEC_LEN 50 /* Maximum length of a single */ + /* conversion specification. */ +#define CONV_RESULT_LEN 50 /* Maximum length of any */ + /* conversion with default */ + /* width and prec. */ + +#define OUT_OF_MEMORY do { \ + if (CORD_oom_fn != 0) (*CORD_oom_fn)(); \ + fprintf(stderr, "Out of memory\n"); \ + abort(); \ + } while (0) + +static int ec_len(CORD_ec x) +{ + return(CORD_len(x[0].ec_cord) + (x[0].ec_bufptr - x[0].ec_buf)); +} + +/* Possible nonumeric precision values. */ +# define NONE -1 +# define VARIABLE -2 +/* Copy the conversion specification from CORD_pos into the buffer buf */ +/* Return negative on error. */ +/* Source initially points one past the leading %. */ +/* It is left pointing at the conversion type. */ +/* Assign field width and precision to *width and *prec. */ +/* If width or prec is *, VARIABLE is assigned. */ +/* Set *left to 1 if left adjustment flag is present. */ +/* Set *long_arg to 1 if long flag ('l' or 'L') is present, or to */ +/* -1 if 'h' is present. */ +static int extract_conv_spec(CORD_pos source, char *buf, + int * width, int *prec, int *left, int * long_arg) +{ + int result = 0; + int current_number = 0; + int saw_period = 0; + int saw_number = 0; + int chars_so_far = 0; + char current; + + *width = NONE; + buf[chars_so_far++] = '%'; + while(CORD_pos_valid(source)) { + if (chars_so_far >= CONV_SPEC_LEN) return(-1); + current = CORD_pos_fetch(source); + buf[chars_so_far++] = current; + switch(current) { + case '*': + saw_number = 1; + current_number = VARIABLE; + break; + case '0': + if (!saw_number) { + /* Zero fill flag; ignore */ + break; + } + current_number *= 10; + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + saw_number = 1; + current_number *= 10; + current_number += current - '0'; + break; + case '.': + saw_period = 1; + if(saw_number) { + *width = current_number; + saw_number = 0; + } + current_number = 0; + break; + case 'l': + case 'L': + *long_arg = 1; + current_number = 0; + break; + case 'h': + *long_arg = -1; + current_number = 0; + break; + case ' ': + case '+': + case '#': + current_number = 0; + break; + case '-': + *left = 1; + current_number = 0; + break; + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + case 'f': + case 'e': + case 'E': + case 'g': + case 'G': + case 'c': + case 'C': + case 's': + case 'S': + case 'p': + case 'n': + case 'r': + goto done; + default: + return(-1); + } + CORD_next(source); + } + return(-1); + done: + if (saw_number) { + if (saw_period) { + *prec = current_number; + } else { + *prec = NONE; + *width = current_number; + } + } else { + *prec = NONE; + } + buf[chars_so_far] = '\0'; + return(result); +} + +#if defined(DJGPP) || defined(__STRICT_ANSI__) + /* vsnprintf is missing in DJGPP (v2.0.3) */ +# define GC_VSNPRINTF(buf, bufsz, format, args) vsprintf(buf, format, args) +#elif defined(_MSC_VER) +# ifdef MSWINCE + /* _vsnprintf is deprecated in WinCE */ +# define GC_VSNPRINTF StringCchVPrintfA +# else +# define GC_VSNPRINTF _vsnprintf +# endif +#else +# define GC_VSNPRINTF vsnprintf +#endif + +int CORD_vsprintf(CORD * out, CORD format, va_list args) +{ + CORD_ec result; + int count; + char current; + CORD_pos pos; + char conv_spec[CONV_SPEC_LEN + 1]; + + CORD_ec_init(result); + for (CORD_set_pos(pos, format, 0); CORD_pos_valid(pos); CORD_next(pos)) { + current = CORD_pos_fetch(pos); + if (current == '%') { + CORD_next(pos); + if (!CORD_pos_valid(pos)) return(-1); + current = CORD_pos_fetch(pos); + if (current == '%') { + CORD_ec_append(result, current); + } else { + int width, prec; + int left_adj = 0; + int long_arg = 0; + CORD arg; + size_t len; + + if (extract_conv_spec(pos, conv_spec, + &width, &prec, + &left_adj, &long_arg) < 0) { + return(-1); + } + current = CORD_pos_fetch(pos); + switch(current) { + case 'n': + /* Assign length to next arg */ + if (long_arg == 0) { + int * pos_ptr; + pos_ptr = va_arg(args, int *); + *pos_ptr = ec_len(result); + } else if (long_arg > 0) { + long * pos_ptr; + pos_ptr = va_arg(args, long *); + *pos_ptr = ec_len(result); + } else { + short * pos_ptr; + pos_ptr = va_arg(args, short *); + *pos_ptr = ec_len(result); + } + goto done; + case 'r': + /* Append cord and any padding */ + if (width == VARIABLE) width = va_arg(args, int); + if (prec == VARIABLE) prec = va_arg(args, int); + arg = va_arg(args, CORD); + len = CORD_len(arg); + if (prec != NONE && len > (size_t)prec) { + if (prec < 0) return(-1); + arg = CORD_substr(arg, 0, prec); + len = (unsigned)prec; + } + if (width != NONE && len < (size_t)width) { + char * blanks = + (char *)GC_MALLOC_ATOMIC(width - len + 1); + + if (NULL == blanks) OUT_OF_MEMORY; + memset(blanks, ' ', width-len); + blanks[width-len] = '\0'; + if (left_adj) { + arg = CORD_cat(arg, blanks); + } else { + arg = CORD_cat(blanks, arg); + } + } + CORD_ec_append_cord(result, arg); + goto done; + case 'c': + if (width == NONE && prec == NONE) { + char c; + + c = (char)va_arg(args, int); + CORD_ec_append(result, c); + goto done; + } + break; + case 's': + if (width == NONE && prec == NONE) { + char * str = va_arg(args, char *); + char c; + + while ((c = *str++)) { + CORD_ec_append(result, c); + } + goto done; + } + break; + default: + break; + } + /* Use standard sprintf to perform conversion */ + { + char * buf; + va_list vsprintf_args; + int max_size = 0; + int res = 0; + +# if defined(CPPCHECK) + va_copy(vsprintf_args, args); +# elif defined(__va_copy) + __va_copy(vsprintf_args, args); +# elif defined(__GNUC__) && !defined(__DJGPP__) \ + && !defined(__EMX__) /* and probably in other cases */ + va_copy(vsprintf_args, args); +# else + vsprintf_args = args; +# endif + if (width == VARIABLE) width = va_arg(args, int); + if (prec == VARIABLE) prec = va_arg(args, int); + if (width != NONE) max_size = width; + if (prec != NONE && prec > max_size) max_size = prec; + max_size += CONV_RESULT_LEN; + if (max_size >= CORD_BUFSZ) { + buf = (char *)GC_MALLOC_ATOMIC(max_size + 1); + if (NULL == buf) OUT_OF_MEMORY; + } else { + if (CORD_BUFSZ - (result[0].ec_bufptr-result[0].ec_buf) + < max_size) { + CORD_ec_flush_buf(result); + } + buf = result[0].ec_bufptr; + } + switch(current) { + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + case 'c': + if (long_arg <= 0) { + (void) va_arg(args, int); + } else /* long_arg > 0 */ { + (void) va_arg(args, long); + } + break; + case 's': + case 'p': + (void) va_arg(args, char *); + break; + case 'f': + case 'e': + case 'E': + case 'g': + case 'G': + (void) va_arg(args, double); + break; + default: + res = -1; + } + if (0 == res) + res = GC_VSNPRINTF(buf, max_size + 1, conv_spec, + vsprintf_args); +# if defined(CPPCHECK) || defined(__va_copy) \ + || (defined(__GNUC__) && !defined(__DJGPP__) \ + && !defined(__EMX__)) + va_end(vsprintf_args); +# endif + len = (size_t)res; + if ((char *)(GC_word)res == buf) { + /* old style vsprintf */ + len = strlen(buf); + } else if (res < 0) { + return(-1); + } + if (buf != result[0].ec_bufptr) { + char c; + + while ((c = *buf++)) { + CORD_ec_append(result, c); + } + } else { + result[0].ec_bufptr = buf + len; + } + } + done:; + } + } else { + CORD_ec_append(result, current); + } + } + count = ec_len(result); + *out = CORD_balance(CORD_ec_to_cord(result)); + return(count); +} + +int CORD_sprintf(CORD * out, CORD format, ...) +{ + va_list args; + int result; + + va_start(args, format); + result = CORD_vsprintf(out, format, args); + va_end(args); + return(result); +} + +int CORD_fprintf(FILE * f, CORD format, ...) +{ + va_list args; + int result; + CORD out = CORD_EMPTY; /* initialized to prevent compiler warning */ + + va_start(args, format); + result = CORD_vsprintf(&out, format, args); + va_end(args); + if (result > 0) CORD_put(out, f); + return(result); +} + +int CORD_vfprintf(FILE * f, CORD format, va_list args) +{ + int result; + CORD out = CORD_EMPTY; + + result = CORD_vsprintf(&out, format, args); + if (result > 0) CORD_put(out, f); + return(result); +} + +int CORD_printf(CORD format, ...) +{ + va_list args; + int result; + CORD out = CORD_EMPTY; + + va_start(args, format); + result = CORD_vsprintf(&out, format, args); + va_end(args); + if (result > 0) CORD_put(out, stdout); + return(result); +} + +int CORD_vprintf(CORD format, va_list args) +{ + int result; + CORD out = CORD_EMPTY; + + result = CORD_vsprintf(&out, format, args); + if (result > 0) CORD_put(out, stdout); + return(result); +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/cord/cordxtra.c b/unity-2019.4.24f1-mbe/external/bdwgc/cord/cordxtra.c new file mode 100644 index 000000000..5ee4df734 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/cord/cordxtra.c @@ -0,0 +1,638 @@ +/* + * Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* + * These are functions on cords that do not need to understand their + * implementation. They serve also serve as example client code for + * cord_basics. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifndef CORD_BUILD +# define CORD_BUILD +#endif + +# include +# include +# include +# include + +# include "cord.h" +# include "ec.h" + +# define I_HIDE_POINTERS /* So we get access to allocation lock. */ + /* We use this for lazy file reading, */ + /* so that we remain independent */ + /* of the threads primitives. */ +# include "gc.h" + +/* For now we assume that pointer reads and writes are atomic, */ +/* i.e. another thread always sees the state before or after */ +/* a write. This might be false on a Motorola M68K with */ +/* pointers that are not 32-bit aligned. But there probably */ +/* aren't too many threads packages running on those. */ +# define ATOMIC_WRITE(x,y) (x) = (y) +# define ATOMIC_READ(x) (*(x)) + +/* The standard says these are in stdio.h, but they aren't always: */ +# ifndef SEEK_SET +# define SEEK_SET 0 +# endif +# ifndef SEEK_END +# define SEEK_END 2 +# endif + +# define BUFSZ 2048 /* Size of stack allocated buffers when */ + /* we want large buffers. */ + +typedef void (* oom_fn)(void); + +# define OUT_OF_MEMORY { if (CORD_oom_fn != (oom_fn) 0) (*CORD_oom_fn)(); \ + ABORT("Out of memory"); } +# define ABORT(msg) { fprintf(stderr, "%s\n", msg); abort(); } + +#if GC_GNUC_PREREQ(3, 4) +# define CORD_ATTR_UNUSED __attribute__((__unused__)) +#else +# define CORD_ATTR_UNUSED /* empty */ +#endif + +CORD CORD_cat_char(CORD x, char c) +{ + char * string; + + if (c == '\0') return(CORD_cat(x, CORD_nul(1))); + string = (char *)GC_MALLOC_ATOMIC(2); + if (string == 0) OUT_OF_MEMORY; + string[0] = c; + string[1] = '\0'; + return(CORD_cat_char_star(x, string, 1)); +} + +CORD CORD_catn(int nargs, ...) +{ + CORD result = CORD_EMPTY; + va_list args; + int i; + + va_start(args, nargs); + for (i = 0; i < nargs; i++) { + CORD next = va_arg(args, CORD); + result = CORD_cat(result, next); + } + va_end(args); + return(result); +} + +typedef struct { + size_t len; + size_t count; + char * buf; +} CORD_fill_data; + +int CORD_fill_proc(char c, void * client_data) +{ + CORD_fill_data * d = (CORD_fill_data *)client_data; + size_t count = d -> count; + + (d -> buf)[count] = c; + d -> count = ++count; + if (count >= d -> len) { + return(1); + } else { + return(0); + } +} + +int CORD_batched_fill_proc(const char * s, void * client_data) +{ + CORD_fill_data * d = (CORD_fill_data *)client_data; + size_t count = d -> count; + size_t max = d -> len; + char * buf = d -> buf; + const char * t = s; + + while((buf[count] = *t++) != '\0') { + count++; + if (count >= max) { + d -> count = count; + return(1); + } + } + d -> count = count; + return(0); +} + +/* Fill buf with len characters starting at i. */ +/* Assumes len characters are available in buf. */ +/* Return 1 if buf is filled fully (and len is */ +/* non-zero), 0 otherwise. */ +int CORD_fill_buf(CORD x, size_t i, size_t len, char * buf) +{ + CORD_fill_data fd; + + fd.len = len; + fd.buf = buf; + fd.count = 0; + return CORD_iter5(x, i, CORD_fill_proc, CORD_batched_fill_proc, &fd); +} + +int CORD_cmp(CORD x, CORD y) +{ + CORD_pos xpos; + CORD_pos ypos; + + if (y == CORD_EMPTY) return(x != CORD_EMPTY); + if (x == CORD_EMPTY) return(-1); + if (CORD_IS_STRING(y) && CORD_IS_STRING(x)) return(strcmp(x,y)); + CORD_set_pos(xpos, x, 0); + CORD_set_pos(ypos, y, 0); + for(;;) { + size_t avail, yavail; + + if (!CORD_pos_valid(xpos)) { + if (CORD_pos_valid(ypos)) { + return(-1); + } else { + return(0); + } + } + if (!CORD_pos_valid(ypos)) { + return(1); + } + avail = CORD_pos_chars_left(xpos); + if (avail == 0 + || (yavail = CORD_pos_chars_left(ypos)) == 0) { + char xcurrent = CORD_pos_fetch(xpos); + char ycurrent = CORD_pos_fetch(ypos); + if (xcurrent != ycurrent) return(xcurrent - ycurrent); + CORD_next(xpos); + CORD_next(ypos); + } else { + /* process as many characters as we can */ + int result; + + if (avail > yavail) avail = yavail; + result = strncmp(CORD_pos_cur_char_addr(xpos), + CORD_pos_cur_char_addr(ypos), avail); + if (result != 0) return(result); + CORD_pos_advance(xpos, avail); + CORD_pos_advance(ypos, avail); + } + } +} + +int CORD_ncmp(CORD x, size_t x_start, CORD y, size_t y_start, size_t len) +{ + CORD_pos xpos; + CORD_pos ypos; + size_t count; + + CORD_set_pos(xpos, x, x_start); + CORD_set_pos(ypos, y, y_start); + for(count = 0; count < len;) { + long avail, yavail; + + if (!CORD_pos_valid(xpos)) { + if (CORD_pos_valid(ypos)) { + return(-1); + } else { + return(0); + } + } + if (!CORD_pos_valid(ypos)) { + return(1); + } + if ((avail = CORD_pos_chars_left(xpos)) <= 0 + || (yavail = CORD_pos_chars_left(ypos)) <= 0) { + char xcurrent = CORD_pos_fetch(xpos); + char ycurrent = CORD_pos_fetch(ypos); + + if (xcurrent != ycurrent) return(xcurrent - ycurrent); + CORD_next(xpos); + CORD_next(ypos); + count++; + } else { + /* process as many characters as we can */ + int result; + + if (avail > yavail) avail = yavail; + count += avail; + if (count > len) + avail -= (long)(count - len); + result = strncmp(CORD_pos_cur_char_addr(xpos), + CORD_pos_cur_char_addr(ypos), (size_t)avail); + if (result != 0) return(result); + CORD_pos_advance(xpos, (size_t)avail); + CORD_pos_advance(ypos, (size_t)avail); + } + } + return(0); +} + +char * CORD_to_char_star(CORD x) +{ + size_t len = CORD_len(x); + char * result = (char *)GC_MALLOC_ATOMIC(len + 1); + + if (result == 0) OUT_OF_MEMORY; + if (len > 0 && CORD_fill_buf(x, 0, len, result) != 1) + ABORT("CORD_fill_buf malfunction"); + result[len] = '\0'; + return(result); +} + +CORD CORD_from_char_star(const char *s) +{ + char * result; + size_t len = strlen(s); + + if (0 == len) return(CORD_EMPTY); + result = (char *)GC_MALLOC_ATOMIC(len + 1); + if (result == 0) OUT_OF_MEMORY; + memcpy(result, s, len+1); + return(result); +} + +const char * CORD_to_const_char_star(CORD x) +{ + if (x == 0) return(""); + if (CORD_IS_STRING(x)) return((const char *)x); + return(CORD_to_char_star(x)); +} + +char CORD_fetch(CORD x, size_t i) +{ + CORD_pos xpos; + + CORD_set_pos(xpos, x, i); + if (!CORD_pos_valid(xpos)) ABORT("bad index?"); + return(CORD_pos_fetch(xpos)); +} + + +int CORD_put_proc(char c, void * client_data) +{ + FILE * f = (FILE *)client_data; + + return(putc(c, f) == EOF); +} + +int CORD_batched_put_proc(const char * s, void * client_data) +{ + FILE * f = (FILE *)client_data; + + return(fputs(s, f) == EOF); +} + + +int CORD_put(CORD x, FILE * f) +{ + if (CORD_iter5(x, 0, CORD_put_proc, CORD_batched_put_proc, f)) { + return(EOF); + } else { + return(1); + } +} + +typedef struct { + size_t pos; /* Current position in the cord */ + char target; /* Character we're looking for */ +} chr_data; + +int CORD_chr_proc(char c, void * client_data) +{ + chr_data * d = (chr_data *)client_data; + + if (c == d -> target) return(1); + (d -> pos) ++; + return(0); +} + +int CORD_rchr_proc(char c, void * client_data) +{ + chr_data * d = (chr_data *)client_data; + + if (c == d -> target) return(1); + (d -> pos) --; + return(0); +} + +int CORD_batched_chr_proc(const char *s, void * client_data) +{ + chr_data * d = (chr_data *)client_data; + const char * occ = strchr(s, d -> target); + + if (NULL == occ) { + d -> pos += strlen(s); + return(0); + } else { + d -> pos += occ - s; + return(1); + } +} + +size_t CORD_chr(CORD x, size_t i, int c) +{ + chr_data d; + + d.pos = i; + d.target = (char)c; + if (CORD_iter5(x, i, CORD_chr_proc, CORD_batched_chr_proc, &d)) { + return(d.pos); + } else { + return(CORD_NOT_FOUND); + } +} + +size_t CORD_rchr(CORD x, size_t i, int c) +{ + chr_data d; + + d.pos = i; + d.target = (char)c; + if (CORD_riter4(x, i, CORD_rchr_proc, &d)) { + return(d.pos); + } else { + return(CORD_NOT_FOUND); + } +} + +/* Find the first occurrence of s in x at position start or later. */ +/* This uses an asymptotically poor algorithm, which should typically */ +/* perform acceptably. We compare the first few characters directly, */ +/* and call CORD_ncmp whenever there is a partial match. */ +/* This has the advantage that we allocate very little, or not at all. */ +/* It's very fast if there are few close misses. */ +size_t CORD_str(CORD x, size_t start, CORD s) +{ + CORD_pos xpos; + size_t xlen = CORD_len(x); + size_t slen; + size_t start_len; + const char * s_start; + unsigned long s_buf = 0; /* The first few characters of s */ + unsigned long x_buf = 0; /* Start of candidate substring. */ + /* Initialized only to make compilers */ + /* happy. */ + unsigned long mask = 0; + size_t i; + size_t match_pos; + + if (s == CORD_EMPTY) return(start); + if (CORD_IS_STRING(s)) { + s_start = s; + slen = strlen(s); + } else { + s_start = CORD_to_char_star(CORD_substr(s, 0, sizeof(unsigned long))); + slen = CORD_len(s); + } + if (xlen < start || xlen - start < slen) return(CORD_NOT_FOUND); + start_len = slen; + if (start_len > sizeof(unsigned long)) start_len = sizeof(unsigned long); + CORD_set_pos(xpos, x, start); + for (i = 0; i < start_len; i++) { + mask <<= 8; + mask |= 0xff; + s_buf <<= 8; + s_buf |= (unsigned char)s_start[i]; + x_buf <<= 8; + x_buf |= (unsigned char)CORD_pos_fetch(xpos); + CORD_next(xpos); + } + for (match_pos = start; ; match_pos++) { + if ((x_buf & mask) == s_buf) { + if (slen == start_len || + CORD_ncmp(x, match_pos + start_len, + s, start_len, slen - start_len) == 0) { + return(match_pos); + } + } + if ( match_pos == xlen - slen ) { + return(CORD_NOT_FOUND); + } + x_buf <<= 8; + x_buf |= (unsigned char)CORD_pos_fetch(xpos); + CORD_next(xpos); + } +} + +void CORD_ec_flush_buf(CORD_ec x) +{ + size_t len = x[0].ec_bufptr - x[0].ec_buf; + char * s; + + if (len == 0) return; + s = (char *)GC_MALLOC_ATOMIC(len + 1); + if (NULL == s) OUT_OF_MEMORY; + memcpy(s, x[0].ec_buf, len); + s[len] = '\0'; + x[0].ec_cord = CORD_cat_char_star(x[0].ec_cord, s, len); + x[0].ec_bufptr = x[0].ec_buf; +} + +void CORD_ec_append_cord(CORD_ec x, CORD s) +{ + CORD_ec_flush_buf(x); + x[0].ec_cord = CORD_cat(x[0].ec_cord, s); +} + +char CORD_nul_func(size_t i CORD_ATTR_UNUSED, void * client_data) +{ + return (char)(GC_word)client_data; +} + +CORD CORD_chars(char c, size_t i) +{ + return CORD_from_fn(CORD_nul_func, (void *)(GC_word)(unsigned char)c, i); +} + +CORD CORD_from_file_eager(FILE * f) +{ + CORD_ec ecord; + + CORD_ec_init(ecord); + for(;;) { + int c = getc(f); + + if (c == 0) { + /* Append the right number of NULs */ + /* Note that any string of NULs is represented in 4 words, */ + /* independent of its length. */ + size_t count = 1; + + CORD_ec_flush_buf(ecord); + while ((c = getc(f)) == 0) count++; + ecord[0].ec_cord = CORD_cat(ecord[0].ec_cord, CORD_nul(count)); + } + if (c == EOF) break; + CORD_ec_append(ecord, (char)c); + } + (void) fclose(f); + return(CORD_balance(CORD_ec_to_cord(ecord))); +} + +/* The state maintained for a lazily read file consists primarily */ +/* of a large direct-mapped cache of previously read values. */ +/* We could rely more on stdio buffering. That would have 2 */ +/* disadvantages: */ +/* 1) Empirically, not all fseek implementations preserve the */ +/* buffer whenever they could. */ +/* 2) It would fail if 2 different sections of a long cord */ +/* were being read alternately. */ +/* We do use the stdio buffer for read ahead. */ +/* To guarantee thread safety in the presence of atomic pointer */ +/* writes, cache lines are always replaced, and never modified in */ +/* place. */ + +# define LOG_CACHE_SZ 14 +# define CACHE_SZ (1 << LOG_CACHE_SZ) +# define LOG_LINE_SZ 9 +# define LINE_SZ (1 << LOG_LINE_SZ) + +typedef struct { + size_t tag; + char data[LINE_SZ]; + /* data[i%LINE_SZ] = ith char in file if tag = i/LINE_SZ */ +} cache_line; + +typedef struct { + FILE * lf_file; + size_t lf_current; /* Current file pointer value */ + cache_line * volatile lf_cache[CACHE_SZ/LINE_SZ]; +} lf_state; + +# define MOD_CACHE_SZ(n) ((n) & (CACHE_SZ - 1)) +# define DIV_CACHE_SZ(n) ((n) >> LOG_CACHE_SZ) +# define MOD_LINE_SZ(n) ((n) & (LINE_SZ - 1)) +# define DIV_LINE_SZ(n) ((n) >> LOG_LINE_SZ) +# define LINE_START(n) ((n) & ~(LINE_SZ - 1)) + +typedef struct { + lf_state * state; + size_t file_pos; /* Position of needed character. */ + cache_line * new_cache; +} refill_data; + +/* Executed with allocation lock. */ +static char refill_cache(refill_data * client_data) +{ + lf_state * state = client_data -> state; + size_t file_pos = client_data -> file_pos; + FILE *f = state -> lf_file; + size_t line_start = LINE_START(file_pos); + size_t line_no = DIV_LINE_SZ(MOD_CACHE_SZ(file_pos)); + cache_line * new_cache = client_data -> new_cache; + + if (line_start != state -> lf_current + && fseek(f, (long)line_start, SEEK_SET) != 0) { + ABORT("fseek failed"); + } + if (fread(new_cache -> data, sizeof(char), LINE_SZ, f) + <= file_pos - line_start) { + ABORT("fread failed"); + } + new_cache -> tag = DIV_LINE_SZ(file_pos); + /* Store barrier goes here. */ + ATOMIC_WRITE(state -> lf_cache[line_no], new_cache); + state -> lf_current = line_start + LINE_SZ; + return(new_cache->data[MOD_LINE_SZ(file_pos)]); +} + +char CORD_lf_func(size_t i, void * client_data) +{ + lf_state * state = (lf_state *)client_data; + cache_line * volatile * cl_addr = + &(state -> lf_cache[DIV_LINE_SZ(MOD_CACHE_SZ(i))]); + cache_line * cl = (cache_line *)ATOMIC_READ(cl_addr); + + if (cl == 0 || cl -> tag != DIV_LINE_SZ(i)) { + /* Cache miss */ + refill_data rd; + + rd.state = state; + rd.file_pos = i; + rd.new_cache = GC_NEW_ATOMIC(cache_line); + if (rd.new_cache == 0) OUT_OF_MEMORY; + return((char)(GC_word) + GC_call_with_alloc_lock((GC_fn_type) refill_cache, &rd)); + } + return(cl -> data[MOD_LINE_SZ(i)]); +} + +void CORD_lf_close_proc(void * obj, void * client_data CORD_ATTR_UNUSED) +{ + if (fclose(((lf_state *)obj) -> lf_file) != 0) { + ABORT("CORD_lf_close_proc: fclose failed"); + } +} + +CORD CORD_from_file_lazy_inner(FILE * f, size_t len) +{ + lf_state * state = GC_NEW(lf_state); + int i; + + if (state == 0) OUT_OF_MEMORY; + if (len != 0) { + /* Dummy read to force buffer allocation. */ + /* This greatly increases the probability */ + /* of avoiding deadlock if buffer allocation */ + /* is redirected to GC_malloc and the */ + /* world is multi-threaded. */ + char buf[1]; + + if (fread(buf, 1, 1, f) > 1 + || fseek(f, 0l, SEEK_SET) != 0) { + ABORT("Bad f argument or I/O failure"); + } + } + state -> lf_file = f; + for (i = 0; i < CACHE_SZ/LINE_SZ; i++) { + state -> lf_cache[i] = 0; + } + state -> lf_current = 0; + GC_REGISTER_FINALIZER(state, CORD_lf_close_proc, 0, 0, 0); + return(CORD_from_fn(CORD_lf_func, state, len)); +} + +CORD CORD_from_file_lazy(FILE * f) +{ + long len; + + if (fseek(f, 0l, SEEK_END) != 0 + || (len = ftell(f)) < 0 + || fseek(f, 0l, SEEK_SET) != 0) { + ABORT("Bad f argument or I/O failure"); + } + return(CORD_from_file_lazy_inner(f, (size_t)len)); +} + +# define LAZY_THRESHOLD (128*1024 + 1) + +CORD CORD_from_file(FILE * f) +{ + long len; + + if (fseek(f, 0l, SEEK_END) != 0 + || (len = ftell(f)) < 0 + || fseek(f, 0l, SEEK_SET) != 0) { + ABORT("Bad f argument or I/O failure"); + } + if (len < LAZY_THRESHOLD) { + return(CORD_from_file_eager(f)); + } else { + return(CORD_from_file_lazy_inner(f, (size_t)len)); + } +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/cord/tests/cordtest.c b/unity-2019.4.24f1-mbe/external/bdwgc/cord/tests/cordtest.c new file mode 100644 index 000000000..8de6980fa --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/cord/tests/cordtest.c @@ -0,0 +1,316 @@ +/* + * Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +# include "gc.h" /* For GC_INIT() only */ +# include "cord.h" + +# include +# include +# include +# include + +/* This is a very incomplete test of the cord package. It knows about */ +/* a few internals of the package (e.g. when C strings are returned) */ +/* that real clients shouldn't rely on. */ + +# define ABORT(string) \ + { fprintf(stderr, "FAILED: %s\n", string); abort(); } + +#if defined(CPPCHECK) +# undef CORD_iter +# undef CORD_next +# undef CORD_pos_fetch +# undef CORD_pos_to_cord +# undef CORD_pos_to_index +# undef CORD_pos_valid +# undef CORD_prev +#endif + +int count; + +int test_fn(char c, void * client_data) +{ + if (client_data != (void *)13) ABORT("bad client data"); + if (count < 64*1024+1) { + if ((count & 1) == 0) { + if (c != 'b') ABORT("bad char"); + } else { + if (c != 'a') ABORT("bad char"); + } + count++; + return(0); + } else { + if (c != 'c') ABORT("bad char"); + count++; + return(1); + } +} + +char id_cord_fn(size_t i, void * client_data) +{ + if (client_data != 0) ABORT("id_cord_fn: bad client data"); + return((char)i); +} + +void test_basics(void) +{ + CORD x = CORD_from_char_star("ab"); + int i; + CORD y; + CORD_pos p; + + x = CORD_cat(x,x); + if (x == CORD_EMPTY) ABORT("CORD_cat(x,x) returned empty cord"); + if (!CORD_IS_STRING(x)) ABORT("short cord should usually be a string"); + if (strcmp(x, "abab") != 0) ABORT("bad CORD_cat result"); + + for (i = 1; i < 16; i++) { + x = CORD_cat(x,x); + } + x = CORD_cat(x,"c"); + if (CORD_len(x) != 128*1024+1) ABORT("bad length"); + + count = 0; + if (CORD_iter5(x, 64*1024-1, test_fn, CORD_NO_FN, (void *)13) == 0) { + ABORT("CORD_iter5 failed"); + } + if (count != 64*1024 + 2) ABORT("CORD_iter5 failed"); + + count = 0; + CORD_set_pos(p, x, 64*1024-1); + while(CORD_pos_valid(p)) { + (void) test_fn(CORD_pos_fetch(p), (void *)13); + CORD_next(p); + } + if (count != 64*1024 + 2) ABORT("Position based iteration failed"); + + y = CORD_substr(x, 1023, 5); + if (!y) ABORT("CORD_substr returned NULL"); + if (!CORD_IS_STRING(y)) ABORT("short cord should usually be a string"); + if (strcmp(y, "babab") != 0) ABORT("bad CORD_substr result"); + + y = CORD_substr(x, 1024, 8); + if (!y) ABORT("CORD_substr returned NULL"); + if (!CORD_IS_STRING(y)) ABORT("short cord should usually be a string"); + if (strcmp(y, "abababab") != 0) ABORT("bad CORD_substr result"); + + y = CORD_substr(x, 128*1024-1, 8); + if (!y) ABORT("CORD_substr returned NULL"); + if (!CORD_IS_STRING(y)) ABORT("short cord should usually be a string"); + if (strcmp(y, "bc") != 0) ABORT("bad CORD_substr result"); + + x = CORD_balance(x); + if (CORD_len(x) != 128*1024+1) ABORT("bad length"); + + count = 0; + if (CORD_iter5(x, 64*1024-1, test_fn, CORD_NO_FN, (void *)13) == 0) { + ABORT("CORD_iter5 failed"); + } + if (count != 64*1024 + 2) ABORT("CORD_iter5 failed"); + + y = CORD_substr(x, 1023, 5); + if (!y) ABORT("CORD_substr returned NULL"); + if (!CORD_IS_STRING(y)) ABORT("short cord should usually be a string"); + if (strcmp(y, "babab") != 0) ABORT("bad CORD_substr result"); + y = CORD_from_fn(id_cord_fn, 0, 13); + i = 0; + CORD_set_pos(p, y, i); + while(CORD_pos_valid(p)) { + char c = CORD_pos_fetch(p); + + if(c != i) ABORT("Traversal of function node failed"); + CORD_next(p); + i++; + } + if (i != 13) ABORT("Bad apparent length for function node"); +# if defined(CPPCHECK) + /* TODO: Actually test these functions. */ + CORD_prev(p); + (void)CORD_pos_to_cord(p); + (void)CORD_pos_to_index(p); + (void)CORD_iter(CORD_EMPTY, test_fn, NULL); + (void)CORD_riter(CORD_EMPTY, test_fn, NULL); + CORD_dump(y); +# endif +} + +void test_extras(void) +{ +# define FNAME1 "cordtst1.tmp" /* short name (8+3) for portability */ +# define FNAME2 "cordtst2.tmp" + int i; + CORD y = "abcdefghijklmnopqrstuvwxyz0123456789"; + CORD x = "{}"; + CORD u, w, z; + FILE *f; + FILE *f1a, *f1b, *f2; + + w = CORD_cat(CORD_cat(y,y),y); + z = CORD_catn(3,y,y,y); + if (CORD_cmp(w,z) != 0) ABORT("CORD_catn comparison wrong"); + for (i = 1; i < 100; i++) { + x = CORD_cat(x, y); + } + z = CORD_balance(x); + if (CORD_cmp(x,z) != 0) ABORT("balanced string comparison wrong"); + if (CORD_cmp(x,CORD_cat(z, CORD_nul(13))) >= 0) ABORT("comparison 2"); + if (CORD_cmp(CORD_cat(x, CORD_nul(13)), z) <= 0) ABORT("comparison 3"); + if (CORD_cmp(x,CORD_cat(z, "13")) >= 0) ABORT("comparison 4"); + if ((f = fopen(FNAME1, "w")) == 0) ABORT("open failed"); + if (CORD_put(z,f) == EOF) ABORT("CORD_put failed"); + if (fclose(f) == EOF) ABORT("fclose failed"); + f1a = fopen(FNAME1, "rb"); + if (!f1a) ABORT("Unable to open " FNAME1); + w = CORD_from_file(f1a); + if (CORD_len(w) != CORD_len(z)) ABORT("file length wrong"); + if (CORD_cmp(w,z) != 0) ABORT("file comparison wrong"); + if (CORD_cmp(CORD_substr(w, 50*36+2, 36), y) != 0) + ABORT("file substr wrong"); + f1b = fopen(FNAME1, "rb"); + if (!f1b) ABORT("2nd open failed: " FNAME1); + z = CORD_from_file_lazy(f1b); + if (CORD_cmp(w,z) != 0) ABORT("File conversions differ"); + if (CORD_chr(w, 0, '9') != 37) ABORT("CORD_chr failed 1"); + if (CORD_chr(w, 3, 'a') != 38) ABORT("CORD_chr failed 2"); + if (CORD_rchr(w, CORD_len(w) - 1, '}') != 1) ABORT("CORD_rchr failed"); + x = y; + for (i = 1; i < 14; i++) { + x = CORD_cat(x,x); + } + if ((f = fopen(FNAME2, "w")) == 0) ABORT("2nd open failed"); +# ifdef __DJGPP__ + /* FIXME: DJGPP workaround. Why does this help? */ + if (fflush(f) != 0) ABORT("fflush failed"); +# endif + if (CORD_put(x,f) == EOF) ABORT("CORD_put failed"); + if (fclose(f) == EOF) ABORT("fclose failed"); + f2 = fopen(FNAME2, "rb"); + if (!f2) ABORT("Unable to open " FNAME2); + w = CORD_from_file(f2); + if (CORD_len(w) != CORD_len(x)) ABORT("file length wrong"); + if (CORD_cmp(w,x) != 0) ABORT("file comparison wrong"); + if (CORD_cmp(CORD_substr(w, 1000*36, 36), y) != 0) + ABORT("file substr wrong"); + if (strcmp(CORD_to_char_star(CORD_substr(w, 1000*36, 36)), y) != 0) + ABORT("char * file substr wrong"); + u = CORD_substr(w, 1000*36, 2); + if (!u) ABORT("CORD_substr returned NULL"); + if (strcmp(u, "ab") != 0) + ABORT("short file substr wrong"); + if (CORD_str(x,1,"9a") != 35) ABORT("CORD_str failed 1"); + if (CORD_str(x,0,"9abcdefghijk") != 35) ABORT("CORD_str failed 2"); + if (CORD_str(x,0,"9abcdefghijx") != CORD_NOT_FOUND) + ABORT("CORD_str failed 3"); + if (CORD_str(x,0,"9>") != CORD_NOT_FOUND) ABORT("CORD_str failed 4"); + /* Note: f1a, f1b, f2 handles are closed lazily by CORD library. */ + /* TODO: Propose and use CORD_fclose. */ + *(CORD volatile *)&w = CORD_EMPTY; + *(CORD volatile *)&z = CORD_EMPTY; + GC_gcollect(); + GC_invoke_finalizers(); + /* Of course, this does not guarantee the files are closed. */ + if (remove(FNAME1) != 0) { + /* On some systems, e.g. OS2, this may fail if f1 is still open. */ + /* But we cannot call fclose as it might lead to double close. */ + fprintf(stderr, "WARNING: remove failed: " FNAME1 "\n"); + } + if (remove(FNAME2) != 0) { + fprintf(stderr, "WARNING: remove failed: " FNAME2 "\n"); + } +} + +int wrap_vprintf(CORD format, ...) +{ + va_list args; + int result; + + va_start(args, format); + result = CORD_vprintf(format, args); + va_end(args); + return result; +} + +int wrap_vfprintf(FILE * f, CORD format, ...) +{ + va_list args; + int result; + + va_start(args, format); + result = CORD_vfprintf(f, format, args); + va_end(args); + return result; +} + +#if defined(__DJGPP__) || defined(__STRICT_ANSI__) + /* snprintf is missing in DJGPP (v2.0.3) */ +#else +# if defined(_MSC_VER) +# if defined(_WIN32_WCE) + /* _snprintf is deprecated in WinCE */ +# define GC_SNPRINTF StringCchPrintfA +# else +# define GC_SNPRINTF _snprintf +# endif +# else +# define GC_SNPRINTF snprintf +# endif +#endif + +void test_printf(void) +{ + CORD result; + char result2[200]; + long l = -1; + short s = (short)-1; + CORD x; + + if (CORD_sprintf(&result, "%7.2f%ln", 3.14159F, &l) != 7) + ABORT("CORD_sprintf failed 1"); + if (CORD_cmp(result, " 3.14") != 0)ABORT("CORD_sprintf goofed 1"); + if (l != 7) ABORT("CORD_sprintf goofed 2"); + if (CORD_sprintf(&result, "%-7.2s%hn%c%s", "abcd", &s, 'x', "yz") != 10) + ABORT("CORD_sprintf failed 2"); + if (CORD_cmp(result, "ab xyz") != 0)ABORT("CORD_sprintf goofed 3"); + if (s != 7) ABORT("CORD_sprintf goofed 4"); + x = "abcdefghij"; + x = CORD_cat(x,x); + x = CORD_cat(x,x); + x = CORD_cat(x,x); + if (CORD_sprintf(&result, "->%-120.78r!\n", x) != 124) + ABORT("CORD_sprintf failed 3"); +# ifdef GC_SNPRINTF + (void)GC_SNPRINTF(result2, sizeof(result2), "->%-120.78s!\n", + CORD_to_char_star(x)); +# else + (void)sprintf(result2, "->%-120.78s!\n", CORD_to_char_star(x)); +# endif + result2[sizeof(result2) - 1] = '\0'; + if (CORD_cmp(result, result2) != 0)ABORT("CORD_sprintf goofed 5"); + /* TODO: Better test CORD_[v][f]printf. */ + (void)CORD_printf(CORD_EMPTY); + (void)wrap_vfprintf(stdout, CORD_EMPTY); + (void)wrap_vprintf(CORD_EMPTY); +} + +int main(void) +{ +# ifdef THINK_C + printf("cordtest:\n"); +# endif + GC_INIT(); + test_basics(); + test_extras(); + test_printf(); + CORD_fprintf(stdout, "SUCCEEDED\n"); + return(0); +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/cord/tests/de.c b/unity-2019.4.24f1-mbe/external/bdwgc/cord/tests/de.c new file mode 100644 index 000000000..321296c58 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/cord/tests/de.c @@ -0,0 +1,615 @@ +/* + * Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* + * A really simple-minded text editor based on cords. + * Things it does right: + * No size bounds. + * Inbounded undo. + * Shouldn't crash no matter what file you invoke it on (e.g. /vmunix) + * (Make sure /vmunix is not writable before you try this.) + * Scrolls horizontally. + * Things it does wrong: + * It doesn't handle tabs reasonably (use "expand" first). + * The command set is MUCH too small. + * The redisplay algorithm doesn't let curses do the scrolling. + * The rule for moving the window over the file is suboptimal. + */ + +#include +#include /* for exit() */ + +#include "gc.h" +#include "cord.h" + +#ifdef THINK_C +#define MACINTOSH +#endif +#include + +#if (defined(__BORLANDC__) || defined(__CYGWIN__)) && !defined(WIN32) + /* If this is DOS or win16, we'll fail anyway. */ + /* Might as well assume win32. */ +# define WIN32 +#endif + +#if defined(WIN32) +# include +# include "de_win.h" +#elif defined(MACINTOSH) +# include +/* curses emulation. */ +# define initscr() +# define endwin() +# define nonl() +# define noecho() csetmode(C_NOECHO, stdout) +# define cbreak() csetmode(C_CBREAK, stdout) +# define refresh() +# define addch(c) putchar(c) +# define standout() cinverse(1, stdout) +# define standend() cinverse(0, stdout) +# define move(line,col) cgotoxy(col + 1, line + 1, stdout) +# define clrtoeol() ccleol(stdout) +# define de_error(s) { fprintf(stderr, s); getchar(); } +# define LINES 25 +# define COLS 80 +#else +# include +# include /* for sleep() */ +# define de_error(s) { fprintf(stderr, s); sleep(2); } +#endif +#include "de_cmds.h" + +#define OUT_OF_MEMORY do { \ + fprintf(stderr, "Out of memory\n"); \ + exit(3); \ + } while (0) + +/* List of line number to position mappings, in descending order. */ +/* There may be holes. */ +typedef struct LineMapRep { + int line; + size_t pos; + struct LineMapRep * previous; +} * line_map; + +/* List of file versions, one per edit operation */ +typedef struct HistoryRep { + CORD file_contents; + struct HistoryRep * previous; + line_map map; /* Invalid for first record "now" */ +} * history; + +history now = 0; +CORD current; /* == now -> file_contents. */ +size_t current_len; /* Current file length. */ +line_map current_map = 0; /* Current line no. to pos. map */ +size_t current_map_size = 0; /* Number of current_map entries. */ + /* Not always accurate, but reset */ + /* by prune_map. */ +# define MAX_MAP_SIZE 3000 + +/* Current display position */ +int dis_line = 0; +int dis_col = 0; + +# define ALL -1 +# define NONE - 2 +int need_redisplay = 0; /* Line that needs to be redisplayed. */ + + +/* Current cursor position. Always within file. */ +int line = 0; +int col = 0; +size_t file_pos = 0; /* Character position corresponding to cursor. */ + +/* Invalidate line map for lines > i */ +void invalidate_map(int i) +{ + while(current_map -> line > i) { + current_map = current_map -> previous; + current_map_size--; + } +} + +/* Reduce the number of map entries to save space for huge files. */ +/* This also affects maps in histories. */ +void prune_map(void) +{ + line_map map = current_map; + int start_line = map -> line; + + current_map_size = 0; + do { + current_map_size++; + if (map -> line < start_line - LINES && map -> previous != 0) { + map -> previous = map -> previous -> previous; + } + map = map -> previous; + } while (map != 0); +} + +/* Add mapping entry */ +void add_map(int line, size_t pos) +{ + line_map new_map = GC_NEW(struct LineMapRep); + + if (NULL == new_map) OUT_OF_MEMORY; + if (current_map_size >= MAX_MAP_SIZE) prune_map(); + new_map -> line = line; + new_map -> pos = pos; + new_map -> previous = current_map; + current_map = new_map; + current_map_size++; +} + + + +/* Return position of column *c of ith line in */ +/* current file. Adjust *c to be within the line.*/ +/* A 0 pointer is taken as 0 column. */ +/* Returns CORD_NOT_FOUND if i is too big. */ +/* Assumes i > dis_line. */ +size_t line_pos(int i, int *c) +{ + int j; + size_t cur; + line_map map = current_map; + + while (map -> line > i) map = map -> previous; + if (map -> line < i - 2) /* rebuild */ invalidate_map(i); + for (j = map -> line, cur = map -> pos; j < i;) { + cur = CORD_chr(current, cur, '\n'); + if (cur == current_len-1) return(CORD_NOT_FOUND); + cur++; + if (++j > current_map -> line) add_map(j, cur); + } + if (c != 0) { + size_t next = CORD_chr(current, cur, '\n'); + + if (next == CORD_NOT_FOUND) next = current_len - 1; + if (next < cur + *c) { + *c = (int)(next - cur); + } + cur += *c; + } + return(cur); +} + +void add_hist(CORD s) +{ + history new_file = GC_NEW(struct HistoryRep); + + if (NULL == new_file) OUT_OF_MEMORY; + new_file -> file_contents = current = s; + current_len = CORD_len(s); + new_file -> previous = now; + if (now != 0) now -> map = current_map; + now = new_file; +} + +void del_hist(void) +{ + now = now -> previous; + current = now -> file_contents; + current_map = now -> map; + current_len = CORD_len(current); +} + +/* Current screen_contents; a dynamically allocated array of CORDs */ +CORD * screen = 0; +int screen_size = 0; + +# ifndef WIN32 +/* Replace a line in the curses stdscr. All control characters are */ +/* displayed as upper case characters in standout mode. This isn't */ +/* terribly appropriate for tabs. */ +void replace_line(int i, CORD s) +{ + CORD_pos p; +# if !defined(MACINTOSH) + size_t len = CORD_len(s); +# endif + + if (screen == 0 || LINES > screen_size) { + screen_size = LINES; + screen = (CORD *)GC_MALLOC(screen_size * sizeof(CORD)); + if (NULL == screen) OUT_OF_MEMORY; + } +# if !defined(MACINTOSH) + /* A gross workaround for an apparent curses bug: */ + if (i == LINES-1 && len == (unsigned)COLS) { + s = CORD_substr(s, 0, len - 1); + } +# endif + if (CORD_cmp(screen[i], s) != 0) { + move(i, 0); clrtoeol(); move(i,0); + + CORD_FOR (p, s) { + int c = CORD_pos_fetch(p) & 0x7f; + + if (iscntrl(c)) { + standout(); addch(c + 0x40); standend(); + } else { + addch(c); + } + } + screen[i] = s; + } +} +#else +# define replace_line(i,s) invalidate_line(i) +#endif + +/* Return up to COLS characters of the line of s starting at pos, */ +/* returning only characters after the given column. */ +CORD retrieve_line(CORD s, size_t pos, unsigned column) +{ + CORD candidate = CORD_substr(s, pos, column + COLS); + /* avoids scanning very long lines */ + size_t eol = CORD_chr(candidate, 0, '\n'); + int len; + + if (eol == CORD_NOT_FOUND) eol = CORD_len(candidate); + len = (int)eol - (int)column; + if (len < 0) len = 0; + return(CORD_substr(s, pos + column, len)); +} + +# ifdef WIN32 +# define refresh(); + + CORD retrieve_screen_line(int i) + { + size_t pos; + + invalidate_map(dis_line + LINES); /* Prune search */ + pos = line_pos(dis_line + i, 0); + if (pos == CORD_NOT_FOUND) return(CORD_EMPTY); + return(retrieve_line(current, pos, dis_col)); + } +# endif + +/* Display the visible section of the current file */ +void redisplay(void) +{ + int i; + + invalidate_map(dis_line + LINES); /* Prune search */ + for (i = 0; i < LINES; i++) { + if (need_redisplay == ALL || need_redisplay == i) { + size_t pos = line_pos(dis_line + i, 0); + + if (pos == CORD_NOT_FOUND) break; + replace_line(i, retrieve_line(current, pos, dis_col)); + if (need_redisplay == i) goto done; + } + } + for (; i < LINES; i++) replace_line(i, CORD_EMPTY); +done: + refresh(); + need_redisplay = NONE; +} + +int dis_granularity; + +/* Update dis_line, dis_col, and dis_pos to make cursor visible. */ +/* Assumes line, col, dis_line, dis_pos are in bounds. */ +void normalize_display(void) +{ + int old_line = dis_line; + int old_col = dis_col; + + dis_granularity = 1; + if (LINES > 15 && COLS > 15) dis_granularity = 2; + while (dis_line > line) dis_line -= dis_granularity; + while (dis_col > col) dis_col -= dis_granularity; + while (line >= dis_line + LINES) dis_line += dis_granularity; + while (col >= dis_col + COLS) dis_col += dis_granularity; + if (old_line != dis_line || old_col != dis_col) { + need_redisplay = ALL; + } +} + +# if defined(WIN32) +# elif defined(MACINTOSH) +# define move_cursor(x,y) cgotoxy(x + 1, y + 1, stdout) +# else +# define move_cursor(x,y) move(y,x) +# endif + +/* Adjust display so that cursor is visible; move cursor into position */ +/* Update screen if necessary. */ +void fix_cursor(void) +{ + normalize_display(); + if (need_redisplay != NONE) redisplay(); + move_cursor(col - dis_col, line - dis_line); + refresh(); +# ifndef WIN32 + fflush(stdout); +# endif +} + +/* Make sure line, col, and dis_pos are somewhere inside file. */ +/* Recompute file_pos. Assumes dis_pos is accurate or past eof */ +void fix_pos(void) +{ + int my_col = col; + + if ((size_t)line > current_len) + line = (int)current_len; + file_pos = line_pos(line, &my_col); + if (file_pos == CORD_NOT_FOUND) { + for (line = current_map -> line, file_pos = current_map -> pos; + file_pos < current_len; + line++, file_pos = CORD_chr(current, file_pos, '\n') + 1); + line--; + file_pos = line_pos(line, &col); + } else { + col = my_col; + } +} + +#if defined(WIN32) +# define beep() Beep(1000 /* Hz */, 300 /* msecs */) +#elif defined(MACINTOSH) +# define beep() SysBeep(1) +#else +/* + * beep() is part of some curses packages and not others. + * We try to match the type of the builtin one, if any. + */ + int beep(void) + { + putc('\007', stderr); + return(0); + } +#endif /* !WIN32 && !MACINTOSH */ + +# define NO_PREFIX -1 +# define BARE_PREFIX -2 +int repeat_count = NO_PREFIX; /* Current command prefix. */ + +int locate_mode = 0; /* Currently between 2 ^Ls */ +CORD locate_string = CORD_EMPTY; /* Current search string. */ + +char * arg_file_name; + +#ifdef WIN32 +/* Change the current position to whatever is currently displayed at */ +/* the given SCREEN coordinates. */ +void set_position(int c, int l) +{ + line = l + dis_line; + col = c + dis_col; + fix_pos(); + move_cursor(col - dis_col, line - dis_line); +} +#endif /* WIN32 */ + +/* Perform the command associated with character c. C may be an */ +/* integer > 256 denoting a windows command, one of the above control */ +/* characters, or another ASCII character to be used as either a */ +/* character to be inserted, a repeat count, or a search string, */ +/* depending on the current state. */ +void do_command(int c) +{ + int i; + int need_fix_pos; + FILE * out; + + if ( c == '\r') c = '\n'; + if (locate_mode) { + size_t new_pos; + + if (c == LOCATE) { + locate_mode = 0; + locate_string = CORD_EMPTY; + return; + } + locate_string = CORD_cat_char(locate_string, (char)c); + new_pos = CORD_str(current, file_pos - CORD_len(locate_string) + 1, + locate_string); + if (new_pos != CORD_NOT_FOUND) { + need_redisplay = ALL; + new_pos += CORD_len(locate_string); + for (;;) { + file_pos = line_pos(line + 1, 0); + if (file_pos > new_pos) break; + line++; + } + col = (int)(new_pos - line_pos(line, 0)); + file_pos = new_pos; + fix_cursor(); + } else { + locate_string = CORD_substr(locate_string, 0, + CORD_len(locate_string) - 1); + beep(); + } + return; + } + if (c == REPEAT) { + repeat_count = BARE_PREFIX; return; + } else if (c < 0x100 && isdigit(c)){ + if (repeat_count == BARE_PREFIX) { + repeat_count = c - '0'; return; + } else if (repeat_count != NO_PREFIX) { + repeat_count = 10 * repeat_count + c - '0'; return; + } + } + if (repeat_count == NO_PREFIX) repeat_count = 1; + if (repeat_count == BARE_PREFIX && (c == UP || c == DOWN)) { + repeat_count = LINES - dis_granularity; + } + if (repeat_count == BARE_PREFIX) repeat_count = 8; + need_fix_pos = 0; + for (i = 0; i < repeat_count; i++) { + switch(c) { + case LOCATE: + locate_mode = 1; + break; + case TOP: + line = col = 0; + file_pos = 0; + break; + case UP: + if (line != 0) { + line--; + need_fix_pos = 1; + } + break; + case DOWN: + line++; + need_fix_pos = 1; + break; + case LEFT: + if (col != 0) { + col--; file_pos--; + } + break; + case RIGHT: + if (CORD_fetch(current, file_pos) == '\n') break; + col++; file_pos++; + break; + case UNDO: + del_hist(); + need_redisplay = ALL; need_fix_pos = 1; + break; + case BS: + if (col == 0) { + beep(); + break; + } + col--; file_pos--; + /* FALLTHRU */ + case DEL: + if (file_pos == current_len-1) break; + /* Can't delete trailing newline */ + if (CORD_fetch(current, file_pos) == '\n') { + need_redisplay = ALL; need_fix_pos = 1; + } else { + need_redisplay = line - dis_line; + } + add_hist(CORD_cat( + CORD_substr(current, 0, file_pos), + CORD_substr(current, file_pos+1, current_len))); + invalidate_map(line); + break; + case WRITE: + { + CORD name = CORD_cat(CORD_from_char_star(arg_file_name), + ".new"); + + if ((out = fopen(CORD_to_const_char_star(name), "wb")) == NULL + || CORD_put(current, out) == EOF) { + de_error("Write failed\n"); + need_redisplay = ALL; + } else { + fclose(out); + } + } + break; + default: + { + CORD left_part = CORD_substr(current, 0, file_pos); + CORD right_part = CORD_substr(current, file_pos, current_len); + + add_hist(CORD_cat(CORD_cat_char(left_part, (char)c), + right_part)); + invalidate_map(line); + if (c == '\n') { + col = 0; line++; file_pos++; + need_redisplay = ALL; + } else { + col++; file_pos++; + need_redisplay = line - dis_line; + } + break; + } + } + } + if (need_fix_pos) fix_pos(); + fix_cursor(); + repeat_count = NO_PREFIX; +} + +/* OS independent initialization */ + +void generic_init(void) +{ + FILE * f; + CORD initial; + + if ((f = fopen(arg_file_name, "rb")) == NULL) { + initial = "\n"; + } else { + size_t len; + + initial = CORD_from_file(f); + len = CORD_len(initial); + if (0 == len || CORD_fetch(initial, len - 1) != '\n') { + initial = CORD_cat(initial, "\n"); + } + } + add_map(0,0); + add_hist(initial); + now -> map = current_map; + now -> previous = now; /* Can't back up further: beginning of the world */ + need_redisplay = ALL; + fix_cursor(); +} + +#ifndef WIN32 + +int main(int argc, char **argv) +{ + int c; + void *buf; + +# if defined(MACINTOSH) + console_options.title = "\pDumb Editor"; + cshow(stdout); + argc = ccommand(&argv); +# endif + GC_INIT(); + + if (argc != 2) { + fprintf(stderr, "Usage: %s file\n", argv[0]); + fprintf(stderr, "Cursor keys: ^B(left) ^F(right) ^P(up) ^N(down)\n"); + fprintf(stderr, "Undo: ^U Write to .new: ^W"); + fprintf(stderr, "Quit:^D Repeat count: ^R[n]\n"); + fprintf(stderr, "Top: ^T Locate (search, find): ^L text ^L\n"); + exit(1); + } + arg_file_name = argv[1]; + buf = GC_MALLOC_ATOMIC(8192); + if (NULL == buf) OUT_OF_MEMORY; + setvbuf(stdout, (char *)buf, _IOFBF, 8192); + initscr(); + noecho(); nonl(); cbreak(); + generic_init(); + while ((c = getchar()) != QUIT) { + if (c == EOF) break; + do_command(c); + } + move(LINES-1, 0); + clrtoeol(); + refresh(); + nl(); + echo(); + endwin(); + return 0; +} + +#endif /* !WIN32 */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/cord/tests/de_cmds.h b/unity-2019.4.24f1-mbe/external/bdwgc/cord/tests/de_cmds.h new file mode 100644 index 000000000..2a69594ef --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/cord/tests/de_cmds.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#ifndef DE_CMDS_H + +# define DE_CMDS_H + +# define UP 16 /* ^P */ +# define DOWN 14 /* ^N */ +# define LEFT 2 /* ^B */ +# define RIGHT 6 /* ^F */ +# define DEL 127 /* ^? */ +# define BS 8 /* ^H */ +# define UNDO 21 /* ^U */ +# define WRITE 23 /* ^W */ +# define QUIT 4 /* ^D */ +# define REPEAT 18 /* ^R */ +# define LOCATE 12 /* ^L */ +# define TOP 20 /* ^T */ + +#endif diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/cord/tests/de_win.c b/unity-2019.4.24f1-mbe/external/bdwgc/cord/tests/de_win.c new file mode 100644 index 000000000..24f0a6e2c --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/cord/tests/de_win.c @@ -0,0 +1,368 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* + * The MS Windows specific part of de. + * This started as the generic Windows application template + * but significant parts didn't survive to the final version. + * + * This was written by a nonexpert windows programmer. + */ + +#include "windows.h" +#include "gc.h" +#include "cord.h" +#include "de_cmds.h" +#include "de_win.h" + +int LINES = 0; +int COLS = 0; + +#define szAppName TEXT("DE") + +HWND hwnd; + +void de_error(const char *s) +{ + (void)MessageBoxA(hwnd, s, "Demonstration Editor", + MB_ICONINFORMATION | MB_OK); + InvalidateRect(hwnd, NULL, TRUE); +} + +int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, + LPSTR command_line, int nCmdShow) +{ + MSG msg; + WNDCLASS wndclass; + HACCEL hAccel; + + GC_INIT(); +# if defined(CPPCHECK) + GC_noop1((GC_word)&WinMain); +# endif + + if (!hPrevInstance) + { + wndclass.style = CS_HREDRAW | CS_VREDRAW; + wndclass.lpfnWndProc = WndProc; + wndclass.cbClsExtra = 0; + wndclass.cbWndExtra = DLGWINDOWEXTRA; + wndclass.hInstance = hInstance; + wndclass.hIcon = LoadIcon (hInstance, szAppName); + wndclass.hCursor = LoadCursor (NULL, IDC_ARROW); + wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); + wndclass.lpszMenuName = TEXT("DE"); + wndclass.lpszClassName = szAppName; + + if (RegisterClass (&wndclass) == 0) { + de_error("RegisterClass error"); + return(0); + } + } + + /* Empirically, the command line does not include the command name ... + if (command_line != 0) { + while (isspace(*command_line)) command_line++; + while (*command_line != 0 && !isspace(*command_line)) command_line++; + while (isspace(*command_line)) command_line++; + } */ + + if (command_line == 0 || *command_line == 0) { + de_error("File name argument required"); + return( 0 ); + } else { + char *p = command_line; + + while (*p != 0 && !isspace(*(unsigned char *)p)) + p++; + arg_file_name = CORD_to_char_star( + CORD_substr(command_line, 0, p - command_line)); + } + + hwnd = CreateWindow (szAppName, + TEXT("Demonstration Editor"), + WS_OVERLAPPEDWINDOW | WS_CAPTION, /* Window style */ + CW_USEDEFAULT, 0, /* default pos. */ + CW_USEDEFAULT, 0, /* default width, height */ + NULL, /* No parent */ + NULL, /* Window class menu */ + hInstance, NULL); + if (hwnd == NULL) { + de_error("CreateWindow error"); + return(0); + } + + ShowWindow (hwnd, nCmdShow); + + hAccel = LoadAccelerators( hInstance, szAppName ); + + while (GetMessage (&msg, NULL, 0, 0)) + { + if( !TranslateAccelerator( hwnd, hAccel, &msg ) ) + { + TranslateMessage (&msg); + DispatchMessage (&msg); + } + } + return (int)msg.wParam; +} + +/* Return the argument with all control characters replaced by blanks. */ +char * plain_chars(char * text, size_t len) +{ + char * result = (char *)GC_MALLOC_ATOMIC(len + 1); + size_t i; + + if (NULL == result) return NULL; + for (i = 0; i < len; i++) { + if (iscntrl(((unsigned char *)text)[i])) { + result[i] = ' '; + } else { + result[i] = text[i]; + } + } + result[len] = '\0'; + return(result); +} + +/* Return the argument with all non-control-characters replaced by */ +/* blank, and all control characters c replaced by c + 32. */ +char * control_chars(char * text, size_t len) +{ + char * result = (char *)GC_MALLOC_ATOMIC(len + 1); + size_t i; + + if (NULL == result) return NULL; + for (i = 0; i < len; i++) { + if (iscntrl(((unsigned char *)text)[i])) { + result[i] = text[i] + 0x40; + } else { + result[i] = ' '; + } + } + result[len] = '\0'; + return(result); +} + +int char_width; +int char_height; + +void get_line_rect(int line, int win_width, RECT * rectp) +{ + rectp -> top = line * (LONG)char_height; + rectp -> bottom = rectp->top + char_height; + rectp -> left = 0; + rectp -> right = win_width; +} + +int caret_visible = 0; /* Caret is currently visible. */ + +int screen_was_painted = 0;/* Screen has been painted at least once. */ + +void update_cursor(void); + +INT_PTR CALLBACK AboutBoxCallback( HWND hDlg, UINT message, + WPARAM wParam, LPARAM lParam ) +{ + (void)lParam; + switch( message ) + { + case WM_INITDIALOG: + SetFocus( GetDlgItem( hDlg, IDOK ) ); + break; + + case WM_COMMAND: + switch( wParam ) + { + case IDOK: + EndDialog( hDlg, TRUE ); + break; + } + break; + + case WM_CLOSE: + EndDialog( hDlg, TRUE ); + return TRUE; + + } + return FALSE; +} + +LRESULT CALLBACK WndProc (HWND hwnd, UINT message, + WPARAM wParam, LPARAM lParam) +{ + static HINSTANCE hInstance; + HDC dc; + PAINTSTRUCT ps; + RECT client_area; + RECT this_line; + RECT dummy; + TEXTMETRIC tm; + int i; + int id; + + switch (message) + { + case WM_CREATE: + hInstance = ( (LPCREATESTRUCT) lParam)->hInstance; + dc = GetDC(hwnd); + SelectObject(dc, GetStockObject(SYSTEM_FIXED_FONT)); + GetTextMetrics(dc, &tm); + ReleaseDC(hwnd, dc); + char_width = tm.tmAveCharWidth; + char_height = tm.tmHeight + tm.tmExternalLeading; + GetClientRect(hwnd, &client_area); + COLS = (client_area.right - client_area.left)/char_width; + LINES = (client_area.bottom - client_area.top)/char_height; + generic_init(); + return(0); + + case WM_CHAR: + if (wParam == QUIT) { + SendMessage( hwnd, WM_CLOSE, 0, 0L ); + } else { + do_command((int)wParam); + } + return(0); + + case WM_SETFOCUS: + CreateCaret(hwnd, NULL, char_width, char_height); + ShowCaret(hwnd); + caret_visible = 1; + update_cursor(); + return(0); + + case WM_KILLFOCUS: + HideCaret(hwnd); + DestroyCaret(); + caret_visible = 0; + return(0); + + case WM_LBUTTONUP: + { + unsigned xpos = LOWORD(lParam); /* From left */ + unsigned ypos = HIWORD(lParam); /* from top */ + + set_position(xpos / (unsigned)char_width, + ypos / (unsigned)char_height); + return(0); + } + + case WM_COMMAND: + id = LOWORD(wParam); + if (id & EDIT_CMD_FLAG) { + if (id & REPEAT_FLAG) do_command(REPEAT); + do_command(CHAR_CMD(id)); + return( 0 ); + } else { + switch(id) { + case IDM_FILEEXIT: + SendMessage( hwnd, WM_CLOSE, 0, 0L ); + return( 0 ); + + case IDM_HELPABOUT: + if( DialogBox( hInstance, TEXT("ABOUTBOX"), + hwnd, AboutBoxCallback ) ) + InvalidateRect( hwnd, NULL, TRUE ); + return( 0 ); + case IDM_HELPCONTENTS: + de_error( + "Cursor keys: ^B(left) ^F(right) ^P(up) ^N(down)\n" + "Undo: ^U Write: ^W Quit:^D Repeat count: ^R[n]\n" + "Top: ^T Locate (search, find): ^L text ^L\n"); + return( 0 ); + } + } + break; + + case WM_CLOSE: + DestroyWindow( hwnd ); + return 0; + + case WM_DESTROY: + PostQuitMessage (0); + GC_win32_free_heap(); + return 0; + + case WM_PAINT: + dc = BeginPaint(hwnd, &ps); + GetClientRect(hwnd, &client_area); + COLS = (client_area.right - client_area.left)/char_width; + LINES = (client_area.bottom - client_area.top)/char_height; + SelectObject(dc, GetStockObject(SYSTEM_FIXED_FONT)); + for (i = 0; i < LINES; i++) { + get_line_rect(i, client_area.right, &this_line); + if (IntersectRect(&dummy, &this_line, &ps.rcPaint)) { + CORD raw_line = retrieve_screen_line(i); + size_t len = CORD_len(raw_line); + char * text = CORD_to_char_star(raw_line); + /* May contain embedded NULLs */ + char * plain = plain_chars(text, len); + char * blanks = CORD_to_char_star(CORD_chars(' ', + COLS - len)); + char * control = control_chars(text, len); + if (NULL == plain || NULL == control) + de_error("Out of memory!"); + +# define RED RGB(255,0,0) + + SetBkMode(dc, OPAQUE); + SetTextColor(dc, GetSysColor(COLOR_WINDOWTEXT)); + + if (plain != NULL) + TextOutA(dc, this_line.left, this_line.top, + plain, (int)len); + TextOutA(dc, this_line.left + (int)len * char_width, + this_line.top, + blanks, (int)(COLS - len)); + SetBkMode(dc, TRANSPARENT); + SetTextColor(dc, RED); + if (control != NULL) + TextOutA(dc, this_line.left, this_line.top, + control, (int)strlen(control)); + } + } + EndPaint(hwnd, &ps); + screen_was_painted = 1; + return 0; + } + return DefWindowProc (hwnd, message, wParam, lParam); +} + +int last_col; +int last_line; + +void move_cursor(int c, int l) +{ + last_col = c; + last_line = l; + + if (caret_visible) update_cursor(); +} + +void update_cursor(void) +{ + SetCaretPos(last_col * char_width, last_line * char_height); + ShowCaret(hwnd); +} + +void invalidate_line(int i) +{ + RECT line; + + if (!screen_was_painted) return; + /* Invalidating a rectangle before painting seems result in a */ + /* major performance problem. */ + get_line_rect(i, COLS*char_width, &line); + InvalidateRect(hwnd, &line, FALSE); +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/cord/tests/de_win.h b/unity-2019.4.24f1-mbe/external/bdwgc/cord/tests/de_win.h new file mode 100644 index 000000000..33ecfa256 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/cord/tests/de_win.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* cord.h, de_cmds.h, and windows.h should be included before this. */ + +# define OTHER_FLAG 0x100 +# define EDIT_CMD_FLAG 0x200 +# define REPEAT_FLAG 0x400 + +# define CHAR_CMD(i) ((i) & 0xff) + +/* MENU: DE */ +#define IDM_FILESAVE (EDIT_CMD_FLAG + WRITE) +#define IDM_FILEEXIT (OTHER_FLAG + 1) +#define IDM_HELPABOUT (OTHER_FLAG + 2) +#define IDM_HELPCONTENTS (OTHER_FLAG + 3) + +#define IDM_EDITPDOWN (REPEAT_FLAG + EDIT_CMD_FLAG + DOWN) +#define IDM_EDITPUP (REPEAT_FLAG + EDIT_CMD_FLAG + UP) +#define IDM_EDITUNDO (EDIT_CMD_FLAG + UNDO) +#define IDM_EDITLOCATE (EDIT_CMD_FLAG + LOCATE) +#define IDM_EDITDOWN (EDIT_CMD_FLAG + DOWN) +#define IDM_EDITUP (EDIT_CMD_FLAG + UP) +#define IDM_EDITLEFT (EDIT_CMD_FLAG + LEFT) +#define IDM_EDITRIGHT (EDIT_CMD_FLAG + RIGHT) +#define IDM_EDITBS (EDIT_CMD_FLAG + BS) +#define IDM_EDITDEL (EDIT_CMD_FLAG + DEL) +#define IDM_EDITREPEAT (EDIT_CMD_FLAG + REPEAT) +#define IDM_EDITTOP (EDIT_CMD_FLAG + TOP) + + + + +/* Windows UI stuff */ + +LRESULT CALLBACK WndProc (HWND hwnd, UINT message, + WPARAM wParam, LPARAM lParam); + +/* Screen dimensions. Maintained by de_win.c. */ +extern int LINES; +extern int COLS; + +/* File being edited. */ +extern char * arg_file_name; + +/* Current display position in file. Maintained by de.c */ +extern int dis_line; +extern int dis_col; + +/* Current cursor position in file. */ +extern int line; +extern int col; + +/* + * Calls from de_win.c to de.c + */ + +CORD retrieve_screen_line(int i); + /* Get the contents of i'th screen line. */ + /* Relies on COLS. */ + +void set_position(int x, int y); + /* Set column, row. Upper left of window = (0,0). */ + +void do_command(int); + /* Execute an editor command. */ + /* Agument is a command character or one */ + /* of the IDM_ commands. */ + +void generic_init(void); + /* OS independent initialization */ + + +/* + * Calls from de.c to de_win.c + */ + +void move_cursor(int column, int line); + /* Physically move the cursor on the display, */ + /* so that it appears at */ + /* (column, line). */ + +void invalidate_line(int line); + /* Invalidate line i on the screen. */ + +void de_error(const char *s); + /* Display error message. */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/cord/tests/de_win.rc b/unity-2019.4.24f1-mbe/external/bdwgc/cord/tests/de_win.rc new file mode 100644 index 000000000..69a86270c --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/cord/tests/de_win.rc @@ -0,0 +1,67 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to copy this garbage collector for any purpose, + * provided the above notices are retained on all copies. + */ + +#include "windows.h" +#include "de_cmds.h" +#include "de_win.h" + +ABOUTBOX DIALOG 19, 21, 163, 47 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "About Demonstration Text Editor" +BEGIN + LTEXT "Demonstration Text Editor", -1, 44, 8, 118, 8, WS_CHILD | WS_VISIBLE | WS_GROUP + PUSHBUTTON "OK", IDOK, 118, 27, 24, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP +END + +DE MENU +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&Save\t^W", IDM_FILESAVE + MENUITEM "E&xit\t^D", IDM_FILEEXIT + END + + POPUP "&Edit" + BEGIN + MENUITEM "Page &Down\t^R^N", IDM_EDITPDOWN + MENUITEM "Page &Up\t^R^P", IDM_EDITPUP + MENUITEM "U&ndo\t^U", IDM_EDITUNDO + MENUITEM "&Locate\t^L ... ^L", IDM_EDITLOCATE + MENUITEM "D&own\t^N", IDM_EDITDOWN + MENUITEM "U&p\t^P", IDM_EDITUP + MENUITEM "Le&ft\t^B", IDM_EDITLEFT + MENUITEM "&Right\t^F", IDM_EDITRIGHT + MENUITEM "Delete &Backward\tBS", IDM_EDITBS + MENUITEM "Delete F&orward\tDEL", IDM_EDITDEL + MENUITEM "&Top\t^T", IDM_EDITTOP + END + + POPUP "&Help" + BEGIN + MENUITEM "&Contents", IDM_HELPCONTENTS + MENUITEM "&About...", IDM_HELPABOUT + END + + MENUITEM "Page_&Down", IDM_EDITPDOWN + MENUITEM "Page_&Up", IDM_EDITPUP +END + +DE ACCELERATORS +BEGIN + "^R", IDM_EDITREPEAT + "^N", IDM_EDITDOWN + "^P", IDM_EDITUP + "^L", IDM_EDITLOCATE + "^B", IDM_EDITLEFT + "^F", IDM_EDITRIGHT + "^T", IDM_EDITTOP + VK_DELETE, IDM_EDITDEL, VIRTKEY + VK_BACK, IDM_EDITBS, VIRTKEY +END diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/darwin_stop_world.c b/unity-2019.4.24f1-mbe/external/bdwgc/darwin_stop_world.c new file mode 100644 index 000000000..525f31581 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/darwin_stop_world.c @@ -0,0 +1,783 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2010 by Hewlett-Packard Development Company. + * All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#include "private/pthread_support.h" + +/* This probably needs more porting work to ppc64. */ + +#if defined(GC_DARWIN_THREADS) + +#include +#include +#include + +/* From "Inside Mac OS X - Mach-O Runtime Architecture" published by Apple + Page 49: + "The space beneath the stack pointer, where a new stack frame would normally + be allocated, is called the red zone. This area as shown in Figure 3-2 may + be used for any purpose as long as a new stack frame does not need to be + added to the stack." + + Page 50: "If a leaf procedure's red zone usage would exceed 224 bytes, then + it must set up a stack frame just like routines that call other routines." +*/ +#ifdef POWERPC +# if CPP_WORDSZ == 32 +# define PPC_RED_ZONE_SIZE 224 +# elif CPP_WORDSZ == 64 +# define PPC_RED_ZONE_SIZE 320 +# endif +#endif + +#ifndef DARWIN_DONT_PARSE_STACK + +typedef struct StackFrame { + unsigned long savedSP; + unsigned long savedCR; + unsigned long savedLR; + unsigned long reserved[2]; + unsigned long savedRTOC; +} StackFrame; + +GC_INNER ptr_t GC_FindTopOfStack(unsigned long stack_start) +{ + StackFrame *frame = (StackFrame *)stack_start; + + if (stack_start == 0) { +# ifdef POWERPC +# if CPP_WORDSZ == 32 + __asm__ __volatile__ ("lwz %0,0(r1)" : "=r" (frame)); +# else + __asm__ __volatile__ ("ld %0,0(r1)" : "=r" (frame)); +# endif +# elif defined(ARM32) + volatile ptr_t sp_reg; + __asm__ __volatile__ ("mov %0, r7\n" : "=r" (sp_reg)); + frame = (StackFrame *)sp_reg; +# elif defined(AARCH64) + volatile ptr_t sp_reg; + __asm__ __volatile__ ("mov %0, x29\n" : "=r" (sp_reg)); + frame = (StackFrame *)sp_reg; +# else + ABORT("GC_FindTopOfStack(0) is not implemented"); +# endif + } + +# ifdef DEBUG_THREADS_EXTRA + GC_log_printf("FindTopOfStack start at sp = %p\n", (void *)frame); +# endif + while (frame->savedSP != 0) { + /* if there are no more stack frames, stop */ + + frame = (StackFrame*)frame->savedSP; + + /* we do these next two checks after going to the next frame + because the LR for the first stack frame in the loop + is not set up on purpose, so we shouldn't check it. */ + if ((frame->savedLR & ~0x3) == 0 || (frame->savedLR & ~0x3) == ~0x3UL) + break; /* if the next LR is bogus, stop */ + } +# ifdef DEBUG_THREADS_EXTRA + GC_log_printf("FindTopOfStack finish at sp = %p\n", (void *)frame); +# endif + return (ptr_t)frame; +} + +#endif /* !DARWIN_DONT_PARSE_STACK */ + +/* GC_query_task_threads controls whether to obtain the list of */ +/* the threads from the kernel or to use GC_threads table. */ +#ifdef GC_NO_THREADS_DISCOVERY +# define GC_query_task_threads FALSE +#elif defined(GC_DISCOVER_TASK_THREADS) +# define GC_query_task_threads TRUE +#else + STATIC GC_bool GC_query_task_threads = FALSE; +#endif /* !GC_NO_THREADS_DISCOVERY */ + +/* Use implicit threads registration (all task threads excluding the GC */ +/* special ones are stopped and scanned). Should be called before */ +/* GC_INIT() (or, at least, before going multi-threaded). Deprecated. */ +GC_API void GC_CALL GC_use_threads_discovery(void) +{ +# if defined(GC_NO_THREADS_DISCOVERY) || defined(DARWIN_DONT_PARSE_STACK) + ABORT("Darwin task-threads-based stop and push unsupported"); +# else +# ifndef GC_ALWAYS_MULTITHREADED + GC_ASSERT(!GC_need_to_lock); +# endif +# ifndef GC_DISCOVER_TASK_THREADS + GC_query_task_threads = TRUE; +# endif + GC_init_parallel(); /* just to be consistent with Win32 one */ +# endif +} + +#ifndef kCFCoreFoundationVersionNumber_iOS_8_0 +# define kCFCoreFoundationVersionNumber_iOS_8_0 1140.1 +#endif + +/* Evaluates the stack range for a given thread. Returns the lower */ +/* bound and sets *phi to the upper one. */ +STATIC ptr_t GC_stack_range_for(ptr_t *phi, thread_act_t thread, GC_thread p, + GC_bool thread_blocked, mach_port_t my_thread, + ptr_t *paltstack_lo, + ptr_t *paltstack_hi GC_ATTR_UNUSED) +{ + ptr_t lo; + if (thread == my_thread) { + GC_ASSERT(!thread_blocked); + lo = GC_approx_sp(); +# ifndef DARWIN_DONT_PARSE_STACK + *phi = GC_FindTopOfStack(0); +# endif + + } else if (thread_blocked) { +# if defined(CPPCHECK) + if (NULL == p) ABORT("Invalid GC_thread passed to GC_stack_range_for"); +# endif + lo = p->stop_info.stack_ptr; +# ifndef DARWIN_DONT_PARSE_STACK + *phi = p->topOfStack; +# endif + + } else { + /* MACHINE_THREAD_STATE_COUNT does not seem to be defined */ + /* everywhere. Hence we use our own version. Alternatively, */ + /* we could use THREAD_STATE_MAX (but seems to be not optimal). */ + kern_return_t kern_result; + GC_THREAD_STATE_T state; + +# if defined(ARM32) && defined(ARM_THREAD_STATE32) + /* Use ARM_UNIFIED_THREAD_STATE on iOS8+ 32-bit targets and on */ + /* 64-bit H/W (iOS7+ 32-bit mode). */ + size_t size; + static cpu_type_t cputype = 0; + + if (cputype == 0) { + sysctlbyname("hw.cputype", &cputype, &size, NULL, 0); + } + if (cputype == CPU_TYPE_ARM64 + || kCFCoreFoundationVersionNumber + >= kCFCoreFoundationVersionNumber_iOS_8_0) { + arm_unified_thread_state_t unified_state; + mach_msg_type_number_t unified_thread_state_count + = ARM_UNIFIED_THREAD_STATE_COUNT; +# if defined(CPPCHECK) +# define GC_ARM_UNIFIED_THREAD_STATE 1 +# else +# define GC_ARM_UNIFIED_THREAD_STATE ARM_UNIFIED_THREAD_STATE +# endif + kern_result = thread_get_state(thread, GC_ARM_UNIFIED_THREAD_STATE, + (natural_t *)&unified_state, + &unified_thread_state_count); +# if !defined(CPPCHECK) + if (unified_state.ash.flavor != ARM_THREAD_STATE32) { + ABORT("unified_state flavor should be ARM_THREAD_STATE32"); + } +# endif + state = unified_state; + } else +# endif + /* else */ { + mach_msg_type_number_t thread_state_count = GC_MACH_THREAD_STATE_COUNT; + + /* Get the thread state (registers, etc) */ + kern_result = thread_get_state(thread, GC_MACH_THREAD_STATE, + (natural_t *)&state, + &thread_state_count); + } +# ifdef DEBUG_THREADS + GC_log_printf("thread_get_state returns value = %d\n", kern_result); +# endif + if (kern_result != KERN_SUCCESS) + ABORT("thread_get_state failed"); + +# if defined(I386) + lo = (ptr_t)state.THREAD_FLD(esp); +# ifndef DARWIN_DONT_PARSE_STACK + *phi = GC_FindTopOfStack(state.THREAD_FLD(esp)); +# endif + GC_push_one(state.THREAD_FLD(eax)); + GC_push_one(state.THREAD_FLD(ebx)); + GC_push_one(state.THREAD_FLD(ecx)); + GC_push_one(state.THREAD_FLD(edx)); + GC_push_one(state.THREAD_FLD(edi)); + GC_push_one(state.THREAD_FLD(esi)); + GC_push_one(state.THREAD_FLD(ebp)); + +# elif defined(X86_64) + lo = (ptr_t)state.THREAD_FLD(rsp); +# ifndef DARWIN_DONT_PARSE_STACK + *phi = GC_FindTopOfStack(state.THREAD_FLD(rsp)); +# endif + GC_push_one(state.THREAD_FLD(rax)); + GC_push_one(state.THREAD_FLD(rbx)); + GC_push_one(state.THREAD_FLD(rcx)); + GC_push_one(state.THREAD_FLD(rdx)); + GC_push_one(state.THREAD_FLD(rdi)); + GC_push_one(state.THREAD_FLD(rsi)); + GC_push_one(state.THREAD_FLD(rbp)); + /* GC_push_one(state.THREAD_FLD(rsp)); */ + GC_push_one(state.THREAD_FLD(r8)); + GC_push_one(state.THREAD_FLD(r9)); + GC_push_one(state.THREAD_FLD(r10)); + GC_push_one(state.THREAD_FLD(r11)); + GC_push_one(state.THREAD_FLD(r12)); + GC_push_one(state.THREAD_FLD(r13)); + GC_push_one(state.THREAD_FLD(r14)); + GC_push_one(state.THREAD_FLD(r15)); + +# elif defined(POWERPC) + lo = (ptr_t)(state.THREAD_FLD(r1) - PPC_RED_ZONE_SIZE); +# ifndef DARWIN_DONT_PARSE_STACK + *phi = GC_FindTopOfStack(state.THREAD_FLD(r1)); +# endif + GC_push_one(state.THREAD_FLD(r0)); + GC_push_one(state.THREAD_FLD(r2)); + GC_push_one(state.THREAD_FLD(r3)); + GC_push_one(state.THREAD_FLD(r4)); + GC_push_one(state.THREAD_FLD(r5)); + GC_push_one(state.THREAD_FLD(r6)); + GC_push_one(state.THREAD_FLD(r7)); + GC_push_one(state.THREAD_FLD(r8)); + GC_push_one(state.THREAD_FLD(r9)); + GC_push_one(state.THREAD_FLD(r10)); + GC_push_one(state.THREAD_FLD(r11)); + GC_push_one(state.THREAD_FLD(r12)); + GC_push_one(state.THREAD_FLD(r13)); + GC_push_one(state.THREAD_FLD(r14)); + GC_push_one(state.THREAD_FLD(r15)); + GC_push_one(state.THREAD_FLD(r16)); + GC_push_one(state.THREAD_FLD(r17)); + GC_push_one(state.THREAD_FLD(r18)); + GC_push_one(state.THREAD_FLD(r19)); + GC_push_one(state.THREAD_FLD(r20)); + GC_push_one(state.THREAD_FLD(r21)); + GC_push_one(state.THREAD_FLD(r22)); + GC_push_one(state.THREAD_FLD(r23)); + GC_push_one(state.THREAD_FLD(r24)); + GC_push_one(state.THREAD_FLD(r25)); + GC_push_one(state.THREAD_FLD(r26)); + GC_push_one(state.THREAD_FLD(r27)); + GC_push_one(state.THREAD_FLD(r28)); + GC_push_one(state.THREAD_FLD(r29)); + GC_push_one(state.THREAD_FLD(r30)); + GC_push_one(state.THREAD_FLD(r31)); + +# elif defined(ARM32) + lo = (ptr_t)state.THREAD_FLD(sp); +# ifndef DARWIN_DONT_PARSE_STACK + *phi = GC_FindTopOfStack(state.THREAD_FLD(r[7])); /* fp */ +# endif + { + int j; + for (j = 0; j < 7; j++) + GC_push_one(state.THREAD_FLD(r[j])); + j++; /* "r7" is skipped (iOS uses it as a frame pointer) */ + for (; j <= 12; j++) + GC_push_one(state.THREAD_FLD(r[j])); + } + /* "cpsr", "pc" and "sp" are skipped */ + GC_push_one(state.THREAD_FLD(lr)); + +# elif defined(AARCH64) + lo = (ptr_t)state.THREAD_FLD(sp); +# ifndef DARWIN_DONT_PARSE_STACK + *phi = GC_FindTopOfStack(state.THREAD_FLD(fp)); +# endif + { + int j; + for (j = 0; j <= 28; j++) { + GC_push_one(state.THREAD_FLD(x[j])); + } + } + /* "cpsr", "fp", "pc" and "sp" are skipped */ + GC_push_one(state.THREAD_FLD(lr)); + +# elif defined(CPPCHECK) + lo = NULL; +# else +# error FIXME for non-arm/ppc/x86 architectures +# endif + } /* thread != my_thread */ + +# ifdef DARWIN_DONT_PARSE_STACK + /* p is guaranteed to be non-NULL regardless of GC_query_task_threads. */ + *phi = (p->flags & MAIN_THREAD) != 0 ? GC_stackbottom : p->stack_end; +# endif + + /* TODO: Determine p and handle altstack if !DARWIN_DONT_PARSE_STACK */ +# ifdef DARWIN_DONT_PARSE_STACK + if (p->altstack != NULL && (word)p->altstack <= (word)lo + && (word)lo <= (word)p->altstack + p->altstack_size) { + *paltstack_lo = lo; + *paltstack_hi = p->altstack + p->altstack_size; + lo = p->stack; + *phi = p->stack + p->stack_size; + } else +# endif + /* else */ { + *paltstack_lo = NULL; + } +# ifdef DEBUG_THREADS + GC_log_printf("Darwin: Stack for thread %p = [%p,%p)\n", + (void *)(word)thread, (void *)lo, (void *)(*phi)); +# endif + return lo; +} + +GC_INNER void GC_push_all_stacks(void) +{ + ptr_t hi, altstack_lo, altstack_hi; + task_t my_task = current_task(); + mach_port_t my_thread = mach_thread_self(); + GC_bool found_me = FALSE; + int nthreads = 0; + word total_size = 0; + mach_msg_type_number_t listcount = (mach_msg_type_number_t)THREAD_TABLE_SZ; + if (!EXPECT(GC_thr_initialized, TRUE)) + GC_thr_init(); + +# ifndef DARWIN_DONT_PARSE_STACK + if (GC_query_task_threads) { + int i; + kern_return_t kern_result; + thread_act_array_t act_list = 0; + + /* Obtain the list of the threads from the kernel. */ + kern_result = task_threads(my_task, &act_list, &listcount); + if (kern_result != KERN_SUCCESS) + ABORT("task_threads failed"); + + for (i = 0; i < (int)listcount; i++) { + thread_act_t thread = act_list[i]; + ptr_t lo = GC_stack_range_for(&hi, thread, NULL, FALSE, my_thread, + &altstack_lo, &altstack_hi); + + if (lo) { + GC_ASSERT((word)lo <= (word)hi); + total_size += hi - lo; + GC_push_all_stack(lo, hi); + } + /* TODO: Handle altstack */ + nthreads++; + if (thread == my_thread) + found_me = TRUE; + mach_port_deallocate(my_task, thread); + } /* for (i=0; ...) */ + + vm_deallocate(my_task, (vm_address_t)act_list, + sizeof(thread_t) * listcount); + } else +# endif /* !DARWIN_DONT_PARSE_STACK */ + /* else */ { + int i; + + for (i = 0; i < (int)listcount; i++) { + GC_thread p; + + for (p = GC_threads[i]; p != NULL; p = p->next) + if ((p->flags & FINISHED) == 0) { + thread_act_t thread = (thread_act_t)p->stop_info.mach_thread; + ptr_t lo = GC_stack_range_for(&hi, thread, p, + (GC_bool)p->thread_blocked, + my_thread, &altstack_lo, + &altstack_hi); + + if (lo) { + GC_ASSERT((word)lo <= (word)hi); + total_size += hi - lo; + GC_push_all_stack_sections(lo, hi, p->traced_stack_sect); + } + if (altstack_lo) { + total_size += altstack_hi - altstack_lo; + GC_push_all_stack(altstack_lo, altstack_hi); + } + nthreads++; + if (thread == my_thread) + found_me = TRUE; + } + } /* for (i=0; ...) */ + } + + mach_port_deallocate(my_task, my_thread); + GC_VERBOSE_LOG_PRINTF("Pushed %d thread stacks\n", nthreads); + if (!found_me && !GC_in_thread_creation) + ABORT("Collecting from unknown thread"); + GC_total_stacksize = total_size; +} + +#ifndef GC_NO_THREADS_DISCOVERY + +# ifdef MPROTECT_VDB + STATIC mach_port_t GC_mach_handler_thread = 0; + STATIC GC_bool GC_use_mach_handler_thread = FALSE; + + GC_INNER void GC_darwin_register_mach_handler_thread(mach_port_t thread) + { + GC_mach_handler_thread = thread; + GC_use_mach_handler_thread = TRUE; + } +# endif /* MPROTECT_VDB */ + +# ifndef GC_MAX_MACH_THREADS +# define GC_MAX_MACH_THREADS THREAD_TABLE_SZ +# endif + + struct GC_mach_thread { + thread_act_t thread; + GC_bool already_suspended; + }; + + struct GC_mach_thread GC_mach_threads[GC_MAX_MACH_THREADS]; + STATIC int GC_mach_threads_count = 0; + /* FIXME: it is better to implement GC_mach_threads as a hash set. */ + +/* returns true if there's a thread in act_list that wasn't in old_list */ +STATIC GC_bool GC_suspend_thread_list(thread_act_array_t act_list, int count, + thread_act_array_t old_list, + int old_count, mach_port_t my_thread) +{ + int i; + int j = -1; + GC_bool changed = FALSE; + + for (i = 0; i < count; i++) { + thread_act_t thread = act_list[i]; + GC_bool found; + struct thread_basic_info info; + mach_msg_type_number_t outCount; + kern_return_t kern_result; + + if (thread == my_thread +# ifdef MPROTECT_VDB + || (GC_mach_handler_thread == thread && GC_use_mach_handler_thread) +# endif + ) { + /* Don't add our and the handler threads. */ + continue; + } +# ifdef PARALLEL_MARK + if (GC_is_mach_marker(thread)) + continue; /* ignore the parallel marker threads */ +# endif + +# ifdef DEBUG_THREADS + GC_log_printf("Attempting to suspend thread %p\n", + (void *)(word)thread); +# endif + /* find the current thread in the old list */ + found = FALSE; + { + int last_found = j; /* remember the previous found thread index */ + + /* Search for the thread starting from the last found one first. */ + while (++j < old_count) + if (old_list[j] == thread) { + found = TRUE; + break; + } + if (!found) { + /* If not found, search in the rest (beginning) of the list. */ + for (j = 0; j < last_found; j++) + if (old_list[j] == thread) { + found = TRUE; + break; + } + + if (!found) { + /* add it to the GC_mach_threads list */ + if (GC_mach_threads_count == GC_MAX_MACH_THREADS) + ABORT("Too many threads"); + GC_mach_threads[GC_mach_threads_count].thread = thread; + /* default is not suspended */ + GC_mach_threads[GC_mach_threads_count].already_suspended = FALSE; + changed = TRUE; + } + } + } + + outCount = THREAD_INFO_MAX; + kern_result = thread_info(thread, THREAD_BASIC_INFO, + (thread_info_t)&info, &outCount); + if (kern_result != KERN_SUCCESS) { + /* The thread may have quit since the thread_threads() call we */ + /* mark already suspended so it's not dealt with anymore later. */ + if (!found) + GC_mach_threads[GC_mach_threads_count++].already_suspended = TRUE; + continue; + } +# ifdef DEBUG_THREADS + GC_log_printf("Thread state for %p = %d\n", (void *)(word)thread, + info.run_state); +# endif + if (info.suspend_count != 0) { + /* thread is already suspended. */ + if (!found) + GC_mach_threads[GC_mach_threads_count++].already_suspended = TRUE; + continue; + } + +# ifdef DEBUG_THREADS + GC_log_printf("Suspending %p\n", (void *)(word)thread); +# endif + /* Unconditionally suspend the thread. It will do no */ + /* harm if it is already suspended by the client logic. */ + GC_acquire_dirty_lock(); + do { + kern_result = thread_suspend(thread); + } while (kern_result == KERN_ABORTED); + GC_release_dirty_lock(); + + if (kern_result != KERN_SUCCESS) { + /* The thread may have quit since the thread_threads() call we */ + /* mark already suspended so it's not dealt with anymore later. */ + if (!found) + GC_mach_threads[GC_mach_threads_count++].already_suspended = TRUE; + continue; + } + if (!found) + GC_mach_threads_count++; + if (GC_on_thread_event) + GC_on_thread_event(GC_EVENT_THREAD_SUSPENDED, (void *)(word)thread); + } + return changed; +} + +#endif /* !GC_NO_THREADS_DISCOVERY */ + +/* Caller holds allocation lock. */ +GC_INNER void GC_stop_world(void) +{ + task_t my_task = current_task(); + mach_port_t my_thread = mach_thread_self(); + kern_return_t kern_result; + +# ifdef DEBUG_THREADS + GC_log_printf("Stopping the world from thread %p\n", + (void *)(word)my_thread); +# endif +# ifdef PARALLEL_MARK + if (GC_parallel) { + /* Make sure all free list construction has stopped before we */ + /* start. No new construction can start, since free list */ + /* construction is required to acquire and release the GC lock */ + /* before it starts, and we have the lock. */ + GC_acquire_mark_lock(); + GC_ASSERT(GC_fl_builder_count == 0); + /* We should have previously waited for it to become zero. */ + } +# endif /* PARALLEL_MARK */ + + if (GC_query_task_threads) { +# ifndef GC_NO_THREADS_DISCOVERY + unsigned i; + GC_bool changed; + thread_act_array_t act_list, prev_list; + mach_msg_type_number_t listcount, prevcount; + + /* Clear out the mach threads list table. We do not need to */ + /* really clear GC_mach_threads[] as it is used only in the range */ + /* from 0 to GC_mach_threads_count-1, inclusive. */ + GC_mach_threads_count = 0; + + /* Loop stopping threads until you have gone over the whole list */ + /* twice without a new one appearing. thread_create() won't */ + /* return (and thus the thread stop) until the new thread exists, */ + /* so there is no window whereby you could stop a thread, */ + /* recognize it is stopped, but then have a new thread it created */ + /* before stopping show up later. */ + changed = TRUE; + prev_list = NULL; + prevcount = 0; + do { + kern_result = task_threads(my_task, &act_list, &listcount); + + if (kern_result == KERN_SUCCESS) { + changed = GC_suspend_thread_list(act_list, listcount, prev_list, + prevcount, my_thread); + + if (prev_list != NULL) { + for (i = 0; i < prevcount; i++) + mach_port_deallocate(my_task, prev_list[i]); + + vm_deallocate(my_task, (vm_address_t)prev_list, + sizeof(thread_t) * prevcount); + } + + /* Repeat while having changes. */ + prev_list = act_list; + prevcount = listcount; + } + } while (changed); + + GC_ASSERT(prev_list != 0); + for (i = 0; i < prevcount; i++) + mach_port_deallocate(my_task, prev_list[i]); + vm_deallocate(my_task, (vm_address_t)act_list, + sizeof(thread_t) * listcount); +# endif /* !GC_NO_THREADS_DISCOVERY */ + + } else { + unsigned i; + + for (i = 0; i < THREAD_TABLE_SZ; i++) { + GC_thread p; + + for (p = GC_threads[i]; p != NULL; p = p->next) { + if ((p->flags & FINISHED) == 0 && !p->thread_blocked && + p->stop_info.mach_thread != my_thread) { + GC_acquire_dirty_lock(); + do { + kern_result = thread_suspend(p->stop_info.mach_thread); + } while (kern_result == KERN_ABORTED); + GC_release_dirty_lock(); + if (kern_result != KERN_SUCCESS) + ABORT("thread_suspend failed"); + if (GC_on_thread_event) + GC_on_thread_event(GC_EVENT_THREAD_SUSPENDED, + (void *)(word)p->stop_info.mach_thread); + } + } + } + } + +# ifdef MPROTECT_VDB + if(GC_incremental) { + GC_mprotect_stop(); + } +# endif +# ifdef PARALLEL_MARK + if (GC_parallel) + GC_release_mark_lock(); +# endif + +# ifdef DEBUG_THREADS + GC_log_printf("World stopped from %p\n", (void *)(word)my_thread); +# endif + mach_port_deallocate(my_task, my_thread); +} + +GC_INLINE void GC_thread_resume(thread_act_t thread) +{ + kern_return_t kern_result; +# if defined(DEBUG_THREADS) || defined(GC_ASSERTIONS) + struct thread_basic_info info; + mach_msg_type_number_t outCount = THREAD_INFO_MAX; + kern_result = thread_info(thread, THREAD_BASIC_INFO, + (thread_info_t)&info, &outCount); + if (kern_result != KERN_SUCCESS) + ABORT("thread_info failed"); +# endif +# ifdef DEBUG_THREADS + GC_log_printf("Resuming thread %p with state %d\n", (void *)(word)thread, + info.run_state); +# endif + /* Resume the thread */ + kern_result = thread_resume(thread); + if (kern_result != KERN_SUCCESS) + ABORT("thread_resume failed"); + if (GC_on_thread_event) + GC_on_thread_event(GC_EVENT_THREAD_UNSUSPENDED, (void *)(word)thread); +} + +/* Caller holds allocation lock, and has held it continuously since */ +/* the world stopped. */ +GC_INNER void GC_start_world(void) +{ + task_t my_task = current_task(); +# ifdef DEBUG_THREADS + GC_log_printf("World starting\n"); +# endif +# ifdef MPROTECT_VDB + if(GC_incremental) { + GC_mprotect_resume(); + } +# endif + + if (GC_query_task_threads) { +# ifndef GC_NO_THREADS_DISCOVERY + int i; + int j = GC_mach_threads_count; + kern_return_t kern_result; + thread_act_array_t act_list; + mach_msg_type_number_t listcount; + + kern_result = task_threads(my_task, &act_list, &listcount); + if (kern_result != KERN_SUCCESS) + ABORT("task_threads failed"); + + for (i = 0; i < (int)listcount; i++) { + thread_act_t thread = act_list[i]; + int last_found = j; /* The thread index found during the */ + /* previous iteration (count value */ + /* means no thread found yet). */ + + /* Search for the thread starting from the last found one first. */ + while (++j < GC_mach_threads_count) { + if (GC_mach_threads[j].thread == thread) + break; + } + if (j >= GC_mach_threads_count) { + /* If not found, search in the rest (beginning) of the list. */ + for (j = 0; j < last_found; j++) { + if (GC_mach_threads[j].thread == thread) + break; + } + } + + if (j != last_found) { + /* The thread is found in GC_mach_threads. */ + if (GC_mach_threads[j].already_suspended) { +# ifdef DEBUG_THREADS + GC_log_printf("Not resuming already suspended thread %p\n", + (void *)(word)thread); +# endif + } else { + GC_thread_resume(thread); + } + } + + mach_port_deallocate(my_task, thread); + } + vm_deallocate(my_task, (vm_address_t)act_list, + sizeof(thread_t) * listcount); +# endif /* !GC_NO_THREADS_DISCOVERY */ + + } else { + int i; + mach_port_t my_thread = mach_thread_self(); + + for (i = 0; i < THREAD_TABLE_SZ; i++) { + GC_thread p; + for (p = GC_threads[i]; p != NULL; p = p->next) { + if ((p->flags & FINISHED) == 0 && !p->thread_blocked && + p->stop_info.mach_thread != my_thread) + GC_thread_resume(p->stop_info.mach_thread); + } + } + + mach_port_deallocate(my_task, my_thread); + } + +# ifdef DEBUG_THREADS + GC_log_printf("World started\n"); +# endif +} + +#endif /* GC_DARWIN_THREADS */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/dbg_mlc.c b/unity-2019.4.24f1-mbe/external/bdwgc/dbg_mlc.c new file mode 100644 index 000000000..047166239 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/dbg_mlc.c @@ -0,0 +1,1194 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. + * Copyright (c) 1997 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999-2004 Hewlett-Packard Development Company, L.P. + * Copyright (C) 2007 Free Software Foundation, Inc + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#include "private/dbg_mlc.h" + +#ifndef MSWINCE +# include +#endif +#include + +#ifndef SHORT_DBG_HDRS + /* Check whether object with base pointer p has debugging info. */ + /* p is assumed to point to a legitimate object in our part */ + /* of the heap. */ + /* This excludes the check as to whether the back pointer is */ + /* odd, which is added by the GC_HAS_DEBUG_INFO macro. */ + /* Note that if DBG_HDRS_ALL is set, uncollectible objects */ + /* on free lists may not have debug information set. Thus it's */ + /* not always safe to return TRUE (1), even if the client does */ + /* its part. Return -1 if the object with debug info has been */ + /* marked as deallocated. */ + GC_INNER int GC_has_other_debug_info(ptr_t p) + { + ptr_t body = (ptr_t)((oh *)p + 1); + word sz = GC_size(p); + + if (HBLKPTR(p) != HBLKPTR((ptr_t)body) + || sz < DEBUG_BYTES + EXTRA_BYTES) { + return 0; + } + if (((oh *)p) -> oh_sf != (START_FLAG ^ (word)body) + && ((word *)p)[BYTES_TO_WORDS(sz)-1] != (END_FLAG ^ (word)body)) { + return 0; + } + if (((oh *)p)->oh_sz == sz) { + /* Object may have had debug info, but has been deallocated */ + return -1; + } + return 1; + } +#endif /* !SHORT_DBG_HDRS */ + +#ifdef LINT2 + long GC_random(void) + { + static unsigned seed = 1; /* not thread-safe */ + + /* Linear congruential pseudo-random numbers generator. */ + seed = (seed * 1103515245U + 12345) & GC_RAND_MAX; /* overflow is ok */ + return (long)seed; + } +#endif + +#ifdef KEEP_BACK_PTRS + +#ifdef LINT2 +# define RANDOM() GC_random() +#else +# include +# define GC_RAND_MAX RAND_MAX + +# if defined(__GLIBC__) || defined(SOLARIS) \ + || defined(HPUX) || defined(IRIX5) || defined(OSF1) +# define RANDOM() random() +# else +# define RANDOM() (long)rand() +# endif +#endif /* !LINT2 */ + + /* Store back pointer to source in dest, if that appears to be possible. */ + /* This is not completely safe, since we may mistakenly conclude that */ + /* dest has a debugging wrapper. But the error probability is very */ + /* small, and this shouldn't be used in production code. */ + /* We assume that dest is the real base pointer. Source will usually */ + /* be a pointer to the interior of an object. */ + GC_INNER void GC_store_back_pointer(ptr_t source, ptr_t dest) + { + if (GC_HAS_DEBUG_INFO(dest)) { +# ifdef PARALLEL_MARK + AO_store((volatile AO_t *)&((oh *)dest)->oh_back_ptr, + (AO_t)HIDE_BACK_PTR(source)); +# else + ((oh *)dest) -> oh_back_ptr = HIDE_BACK_PTR(source); +# endif + } + } + + GC_INNER void GC_marked_for_finalization(ptr_t dest) + { + GC_store_back_pointer(MARKED_FOR_FINALIZATION, dest); + } + + /* Store information about the object referencing dest in *base_p */ + /* and *offset_p. */ + /* source is root ==> *base_p = address, *offset_p = 0 */ + /* source is heap object ==> *base_p != 0, *offset_p = offset */ + /* Returns 1 on success, 0 if source couldn't be determined. */ + /* Dest can be any address within a heap object. */ + GC_API GC_ref_kind GC_CALL GC_get_back_ptr_info(void *dest, void **base_p, + size_t *offset_p) + { + oh * hdr = (oh *)GC_base(dest); + ptr_t bp; + ptr_t bp_base; + +# ifdef LINT2 + /* Explicitly instruct the code analysis tool that */ + /* GC_get_back_ptr_info is not expected to be called with an */ + /* incorrect "dest" value. */ + if (!hdr) ABORT("Invalid GC_get_back_ptr_info argument"); +# endif + if (!GC_HAS_DEBUG_INFO((ptr_t) hdr)) return GC_NO_SPACE; + bp = (ptr_t)GC_REVEAL_POINTER(hdr -> oh_back_ptr); + if (MARKED_FOR_FINALIZATION == bp) return GC_FINALIZER_REFD; + if (MARKED_FROM_REGISTER == bp) return GC_REFD_FROM_REG; + if (NOT_MARKED == bp) return GC_UNREFERENCED; +# if ALIGNMENT == 1 + /* Heuristically try to fix off by 1 errors we introduced by */ + /* insisting on even addresses. */ + { + ptr_t alternate_ptr = bp + 1; + ptr_t target = *(ptr_t *)bp; + ptr_t alternate_target = *(ptr_t *)alternate_ptr; + + if ((word)alternate_target >= (word)GC_least_plausible_heap_addr + && (word)alternate_target <= (word)GC_greatest_plausible_heap_addr + && ((word)target < (word)GC_least_plausible_heap_addr + || (word)target > (word)GC_greatest_plausible_heap_addr)) { + bp = alternate_ptr; + } + } +# endif + bp_base = (ptr_t)GC_base(bp); + if (NULL == bp_base) { + *base_p = bp; + *offset_p = 0; + return GC_REFD_FROM_ROOT; + } else { + if (GC_HAS_DEBUG_INFO(bp_base)) bp_base += sizeof(oh); + *base_p = bp_base; + *offset_p = bp - bp_base; + return GC_REFD_FROM_HEAP; + } + } + + /* Generate a random heap address. */ + /* The resulting address is in the heap, but */ + /* not necessarily inside a valid object. */ + GC_API void * GC_CALL GC_generate_random_heap_address(void) + { + size_t i; + word heap_offset = RANDOM(); + + if (GC_heapsize > GC_RAND_MAX) { + heap_offset *= GC_RAND_MAX; + heap_offset += RANDOM(); + } + heap_offset %= GC_heapsize; + /* This doesn't yield a uniform distribution, especially if */ + /* e.g. RAND_MAX = 1.5* GC_heapsize. But for typical cases, */ + /* it's not too bad. */ + for (i = 0;; ++i) { + size_t size; + + if (i >= GC_n_heap_sects) + ABORT("GC_generate_random_heap_address: size inconsistency"); + + size = GC_heap_sects[i].hs_bytes; + if (heap_offset < size) { + break; + } else { + heap_offset -= size; + } + } + return GC_heap_sects[i].hs_start + heap_offset; + } + + /* Generate a random address inside a valid marked heap object. */ + GC_API void * GC_CALL GC_generate_random_valid_address(void) + { + ptr_t result; + ptr_t base; + do { + result = (ptr_t)GC_generate_random_heap_address(); + base = (ptr_t)GC_base(result); + } while (NULL == base || !GC_is_marked(base)); + return result; + } + + /* Print back trace for p */ + GC_API void GC_CALL GC_print_backtrace(void *p) + { + void *current = p; + int i; + GC_ref_kind source; + size_t offset; + void *base; + + GC_print_heap_obj((ptr_t)GC_base(current)); + + for (i = 0; ; ++i) { + source = GC_get_back_ptr_info(current, &base, &offset); + if (GC_UNREFERENCED == source) { + GC_err_printf("Reference could not be found\n"); + goto out; + } + if (GC_NO_SPACE == source) { + GC_err_printf("No debug info in object: Can't find reference\n"); + goto out; + } + GC_err_printf("Reachable via %d levels of pointers from ", i); + switch(source) { + case GC_REFD_FROM_ROOT: + GC_err_printf("root at %p\n\n", base); + goto out; + case GC_REFD_FROM_REG: + GC_err_printf("root in register\n\n"); + goto out; + case GC_FINALIZER_REFD: + GC_err_printf("list of finalizable objects\n\n"); + goto out; + case GC_REFD_FROM_HEAP: + GC_err_printf("offset %ld in object:\n", (long)offset); + /* Take GC_base(base) to get real base, i.e. header. */ + GC_print_heap_obj((ptr_t)GC_base(base)); + break; + default: + GC_err_printf("INTERNAL ERROR: UNEXPECTED SOURCE!!!!\n"); + goto out; + } + current = base; + } + out:; + } + + /* Force a garbage collection and generate/print a backtrace */ + /* from a random heap address. */ + GC_INNER void GC_generate_random_backtrace_no_gc(void) + { + void * current; + current = GC_generate_random_valid_address(); + GC_printf("\n****Chosen address %p in object\n", current); + GC_print_backtrace(current); + } + + GC_API void GC_CALL GC_generate_random_backtrace(void) + { + if (GC_try_to_collect(GC_never_stop_func) == 0) { + GC_err_printf("Cannot generate a backtrace: " + "garbage collection is disabled!\n"); + return; + } + GC_generate_random_backtrace_no_gc(); + } + +#endif /* KEEP_BACK_PTRS */ + +# define CROSSES_HBLK(p, sz) \ + (((word)((p) + sizeof(oh) + (sz) - 1) ^ (word)(p)) >= HBLKSIZE) + +GC_INNER void *GC_store_debug_info_inner(void *p, word sz GC_ATTR_UNUSED, + const char *string, int linenum) +{ + word * result = (word *)((oh *)p + 1); + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(GC_size(p) >= sizeof(oh) + sz); + GC_ASSERT(!(SMALL_OBJ(sz) && CROSSES_HBLK((ptr_t)p, sz))); +# ifdef KEEP_BACK_PTRS + ((oh *)p) -> oh_back_ptr = HIDE_BACK_PTR(NOT_MARKED); +# endif +# ifdef MAKE_BACK_GRAPH + ((oh *)p) -> oh_bg_ptr = HIDE_BACK_PTR((ptr_t)0); +# endif + ((oh *)p) -> oh_string = string; + ((oh *)p) -> oh_int = (word)linenum; +# ifndef SHORT_DBG_HDRS + ((oh *)p) -> oh_sz = sz; + ((oh *)p) -> oh_sf = START_FLAG ^ (word)result; + ((word *)p)[BYTES_TO_WORDS(GC_size(p))-1] = + result[SIMPLE_ROUNDED_UP_WORDS(sz)] = END_FLAG ^ (word)result; +# endif + return result; +} + +/* Check the allocation is successful, store debugging info into p, */ +/* start the debugging mode (if not yet), and return displaced pointer. */ +static void *store_debug_info(void *p, size_t lb, + const char *fn, GC_EXTRA_PARAMS) +{ + void *result; + DCL_LOCK_STATE; + + if (NULL == p) { + GC_err_printf("%s(%lu) returning NULL (%s:%d)\n", + fn, (unsigned long)lb, s, i); + return NULL; + } + LOCK(); + if (!GC_debugging_started) + GC_start_debugging_inner(); + ADD_CALL_CHAIN(p, ra); + result = GC_store_debug_info_inner(p, (word)lb, s, i); + UNLOCK(); + return result; +} + +#ifndef SHORT_DBG_HDRS + /* Check the object with debugging info at ohdr. */ + /* Return NULL if it's OK. Else return clobbered */ + /* address. */ + STATIC ptr_t GC_check_annotated_obj(oh *ohdr) + { + ptr_t body = (ptr_t)(ohdr + 1); + word gc_sz = GC_size((ptr_t)ohdr); + if (ohdr -> oh_sz + DEBUG_BYTES > gc_sz) { + return((ptr_t)(&(ohdr -> oh_sz))); + } + if (ohdr -> oh_sf != (START_FLAG ^ (word)body)) { + return((ptr_t)(&(ohdr -> oh_sf))); + } + if (((word *)ohdr)[BYTES_TO_WORDS(gc_sz)-1] != (END_FLAG ^ (word)body)) { + return((ptr_t)((word *)ohdr + BYTES_TO_WORDS(gc_sz)-1)); + } + if (((word *)body)[SIMPLE_ROUNDED_UP_WORDS(ohdr -> oh_sz)] + != (END_FLAG ^ (word)body)) { + return((ptr_t)((word *)body + SIMPLE_ROUNDED_UP_WORDS(ohdr->oh_sz))); + } + return(0); + } +#endif /* !SHORT_DBG_HDRS */ + +STATIC GC_describe_type_fn GC_describe_type_fns[MAXOBJKINDS] = {0}; + +GC_API void GC_CALL GC_register_describe_type_fn(int kind, + GC_describe_type_fn fn) +{ + GC_describe_type_fns[kind] = fn; +} + +#define GET_OH_LINENUM(ohdr) ((int)(ohdr)->oh_int) + +#ifndef SHORT_DBG_HDRS +# define IF_NOT_SHORTDBG_HDRS(x) x +# define COMMA_IFNOT_SHORTDBG_HDRS(x) /* comma */, x +#else +# define IF_NOT_SHORTDBG_HDRS(x) /* empty */ +# define COMMA_IFNOT_SHORTDBG_HDRS(x) /* empty */ +#endif + +/* Print a human-readable description of the object to stderr. */ +/* p points to somewhere inside an object with the debugging info. */ +STATIC void GC_print_obj(ptr_t p) +{ + oh * ohdr = (oh *)GC_base(p); + ptr_t q; + hdr * hhdr; + int kind; + const char *kind_str; + char buffer[GC_TYPE_DESCR_LEN + 1]; + + GC_ASSERT(I_DONT_HOLD_LOCK()); +# ifdef LINT2 + if (!ohdr) ABORT("Invalid GC_print_obj argument"); +# endif + + q = (ptr_t)(ohdr + 1); + /* Print a type description for the object whose client-visible */ + /* address is q. */ + hhdr = GC_find_header(q); + kind = hhdr -> hb_obj_kind; + if (0 != GC_describe_type_fns[kind] && GC_is_marked(ohdr)) { + /* This should preclude free list objects except with */ + /* thread-local allocation. */ + buffer[GC_TYPE_DESCR_LEN] = 0; + (GC_describe_type_fns[kind])(q, buffer); + GC_ASSERT(buffer[GC_TYPE_DESCR_LEN] == 0); + kind_str = buffer; + } else { + switch(kind) { + case PTRFREE: + kind_str = "PTRFREE"; + break; + case NORMAL: + kind_str = "NORMAL"; + break; + case UNCOLLECTABLE: + kind_str = "UNCOLLECTABLE"; + break; +# ifdef GC_ATOMIC_UNCOLLECTABLE + case AUNCOLLECTABLE: + kind_str = "ATOMIC_UNCOLLECTABLE"; + break; +# endif + default: + kind_str = NULL; + /* The alternative is to use snprintf(buffer) but it is */ + /* not quite portable (see vsnprintf in misc.c). */ + } + } + + if (NULL != kind_str) { + GC_err_printf("%p (%s:%d," IF_NOT_SHORTDBG_HDRS(" sz=%lu,") " %s)\n", + (void *)((ptr_t)ohdr + sizeof(oh)), + ohdr->oh_string, GET_OH_LINENUM(ohdr) /*, */ + COMMA_IFNOT_SHORTDBG_HDRS((unsigned long)ohdr->oh_sz), + kind_str); + } else { + GC_err_printf("%p (%s:%d," IF_NOT_SHORTDBG_HDRS(" sz=%lu,") + " kind=%d descr=0x%lx)\n", + (void *)((ptr_t)ohdr + sizeof(oh)), + ohdr->oh_string, GET_OH_LINENUM(ohdr) /*, */ + COMMA_IFNOT_SHORTDBG_HDRS((unsigned long)ohdr->oh_sz), + kind, (unsigned long)hhdr->hb_descr); + } + PRINT_CALL_CHAIN(ohdr); +} + +STATIC void GC_debug_print_heap_obj_proc(ptr_t p) +{ + GC_ASSERT(I_DONT_HOLD_LOCK()); + if (GC_HAS_DEBUG_INFO(p)) { + GC_print_obj(p); + } else { + GC_default_print_heap_obj_proc(p); + } +} + +#ifndef SHORT_DBG_HDRS + /* Use GC_err_printf and friends to print a description of the object */ + /* whose client-visible address is p, and which was smashed at */ + /* clobbered_addr. */ + STATIC void GC_print_smashed_obj(const char *msg, void *p, + ptr_t clobbered_addr) + { + oh * ohdr = (oh *)GC_base(p); + + GC_ASSERT(I_DONT_HOLD_LOCK()); +# ifdef LINT2 + if (!ohdr) ABORT("Invalid GC_print_smashed_obj argument"); +# endif + if ((word)clobbered_addr <= (word)(&ohdr->oh_sz) + || ohdr -> oh_string == 0) { + GC_err_printf( + "%s %p in or near object at %p(, appr. sz = %lu)\n", + msg, (void *)clobbered_addr, p, + (unsigned long)(GC_size((ptr_t)ohdr) - DEBUG_BYTES)); + } else { + GC_err_printf("%s %p in or near object at %p (%s:%d, sz=%lu)\n", + msg, (void *)clobbered_addr, p, + (word)(ohdr -> oh_string) < HBLKSIZE ? "(smashed string)" : + ohdr -> oh_string[0] == '\0' ? "EMPTY(smashed?)" : + ohdr -> oh_string, + GET_OH_LINENUM(ohdr), (unsigned long)(ohdr -> oh_sz)); + PRINT_CALL_CHAIN(ohdr); + } + } + + STATIC void GC_check_heap_proc (void); + STATIC void GC_print_all_smashed_proc (void); +#else + STATIC void GC_do_nothing(void) {} +#endif + +GC_INNER void GC_start_debugging_inner(void) +{ + GC_ASSERT(I_HOLD_LOCK()); +# ifndef SHORT_DBG_HDRS + GC_check_heap = GC_check_heap_proc; + GC_print_all_smashed = GC_print_all_smashed_proc; +# else + GC_check_heap = GC_do_nothing; + GC_print_all_smashed = GC_do_nothing; +# endif + GC_print_heap_obj = GC_debug_print_heap_obj_proc; + GC_debugging_started = TRUE; + GC_register_displacement_inner((word)sizeof(oh)); +} + +size_t GC_debug_header_size = sizeof(oh); + +GC_API void GC_CALL GC_debug_register_displacement(size_t offset) +{ + DCL_LOCK_STATE; + + LOCK(); + GC_register_displacement_inner(offset); + GC_register_displacement_inner((word)sizeof(oh) + offset); + UNLOCK(); +} + +#ifdef GC_ADD_CALLER +# if defined(HAVE_DLADDR) && defined(GC_HAVE_RETURN_ADDR_PARENT) +# include + + STATIC void GC_caller_func_offset(word ad, const char **symp, int *offp) + { + Dl_info caller; + + if (ad && dladdr((void *)ad, &caller) && caller.dli_sname != NULL) { + *symp = caller.dli_sname; + *offp = (int)((char *)ad - (char *)caller.dli_saddr); + } + if (NULL == *symp) { + *symp = "unknown"; + } + } +# else +# define GC_caller_func_offset(ad, symp, offp) (void)(*(symp) = "unknown") +# endif +#endif /* GC_ADD_CALLER */ + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_debug_malloc(size_t lb, + GC_EXTRA_PARAMS) +{ + void * result; + + /* Note that according to malloc() specification, if size is 0 then */ + /* malloc() returns either NULL, or a unique pointer value that can */ + /* later be successfully passed to free(). We always do the latter. */ + result = GC_malloc(SIZET_SAT_ADD(lb, DEBUG_BYTES)); +# ifdef GC_ADD_CALLER + if (s == NULL) { + GC_caller_func_offset(ra, &s, &i); + } +# endif + return store_debug_info(result, lb, "GC_debug_malloc", OPT_RA s, i); +} + +GC_API GC_ATTR_MALLOC void * GC_CALL + GC_debug_malloc_ignore_off_page(size_t lb, GC_EXTRA_PARAMS) +{ + void * result = GC_malloc_ignore_off_page(SIZET_SAT_ADD(lb, DEBUG_BYTES)); + + return store_debug_info(result, lb, "GC_debug_malloc_ignore_off_page", + OPT_RA s, i); +} + +GC_API GC_ATTR_MALLOC void * GC_CALL + GC_debug_malloc_atomic_ignore_off_page(size_t lb, GC_EXTRA_PARAMS) +{ + void * result = GC_malloc_atomic_ignore_off_page( + SIZET_SAT_ADD(lb, DEBUG_BYTES)); + + return store_debug_info(result, lb, + "GC_debug_malloc_atomic_ignore_off_page", + OPT_RA s, i); +} + +STATIC void * GC_debug_generic_malloc(size_t lb, int knd, GC_EXTRA_PARAMS) +{ + void * result = GC_generic_malloc(SIZET_SAT_ADD(lb, DEBUG_BYTES), knd); + + return store_debug_info(result, lb, "GC_debug_generic_malloc", + OPT_RA s, i); +} + +#ifdef DBG_HDRS_ALL + /* An allocation function for internal use. Normally internally */ + /* allocated objects do not have debug information. But in this */ + /* case, we need to make sure that all objects have debug headers. */ + GC_INNER void * GC_debug_generic_malloc_inner(size_t lb, int k) + { + void * result; + + GC_ASSERT(I_HOLD_LOCK()); + result = GC_generic_malloc_inner(SIZET_SAT_ADD(lb, DEBUG_BYTES), k); + if (NULL == result) { + GC_err_printf("GC internal allocation (%lu bytes) returning NULL\n", + (unsigned long) lb); + return(0); + } + if (!GC_debugging_started) { + GC_start_debugging_inner(); + } + ADD_CALL_CHAIN(result, GC_RETURN_ADDR); + return (GC_store_debug_info_inner(result, (word)lb, "INTERNAL", 0)); + } + + GC_INNER void * GC_debug_generic_malloc_inner_ignore_off_page(size_t lb, + int k) + { + void * result; + + GC_ASSERT(I_HOLD_LOCK()); + result = GC_generic_malloc_inner_ignore_off_page( + SIZET_SAT_ADD(lb, DEBUG_BYTES), k); + if (NULL == result) { + GC_err_printf("GC internal allocation (%lu bytes) returning NULL\n", + (unsigned long) lb); + return(0); + } + if (!GC_debugging_started) { + GC_start_debugging_inner(); + } + ADD_CALL_CHAIN(result, GC_RETURN_ADDR); + return (GC_store_debug_info_inner(result, (word)lb, "INTERNAL", 0)); + } +#endif /* DBG_HDRS_ALL */ + +GC_API void * GC_CALL GC_debug_malloc_stubborn(size_t lb, GC_EXTRA_PARAMS) +{ + return GC_debug_malloc(lb, OPT_RA s, i); +} + +GC_API void GC_CALL GC_debug_change_stubborn( + const void * p GC_ATTR_UNUSED) {} + +GC_API void GC_CALL GC_debug_end_stubborn_change(const void *p) +{ + const void * q = GC_base_C(p); + + if (NULL == q) { + ABORT_ARG1("GC_debug_end_stubborn_change: bad arg", ": %p", p); + } + GC_end_stubborn_change(q); +} + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_debug_malloc_atomic(size_t lb, + GC_EXTRA_PARAMS) +{ + void * result = GC_malloc_atomic(SIZET_SAT_ADD(lb, DEBUG_BYTES)); + + return store_debug_info(result, lb, "GC_debug_malloc_atomic", + OPT_RA s, i); +} + +GC_API GC_ATTR_MALLOC char * GC_CALL GC_debug_strdup(const char *str, + GC_EXTRA_PARAMS) +{ + char *copy; + size_t lb; + if (str == NULL) { + if (GC_find_leak) + GC_err_printf("strdup(NULL) behavior is undefined\n"); + return NULL; + } + + lb = strlen(str) + 1; + copy = (char *)GC_debug_malloc_atomic(lb, OPT_RA s, i); + if (copy == NULL) { +# ifndef MSWINCE + errno = ENOMEM; +# endif + return NULL; + } + BCOPY(str, copy, lb); + return copy; +} + +GC_API GC_ATTR_MALLOC char * GC_CALL GC_debug_strndup(const char *str, + size_t size, GC_EXTRA_PARAMS) +{ + char *copy; + size_t len = strlen(str); /* str is expected to be non-NULL */ + if (len > size) + len = size; + copy = (char *)GC_debug_malloc_atomic(len + 1, OPT_RA s, i); + if (copy == NULL) { +# ifndef MSWINCE + errno = ENOMEM; +# endif + return NULL; + } + if (len > 0) + BCOPY(str, copy, len); + copy[len] = '\0'; + return copy; +} + +#ifdef GC_REQUIRE_WCSDUP +# include /* for wcslen() */ + + GC_API GC_ATTR_MALLOC wchar_t * GC_CALL GC_debug_wcsdup(const wchar_t *str, + GC_EXTRA_PARAMS) + { + size_t lb = (wcslen(str) + 1) * sizeof(wchar_t); + wchar_t *copy = (wchar_t *)GC_debug_malloc_atomic(lb, OPT_RA s, i); + if (copy == NULL) { +# ifndef MSWINCE + errno = ENOMEM; +# endif + return NULL; + } + BCOPY(str, copy, lb); + return copy; + } +#endif /* GC_REQUIRE_WCSDUP */ + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_debug_malloc_uncollectable(size_t lb, + GC_EXTRA_PARAMS) +{ + void * result = GC_malloc_uncollectable( + SIZET_SAT_ADD(lb, UNCOLLECTABLE_DEBUG_BYTES)); + + return store_debug_info(result, lb, "GC_debug_malloc_uncollectable", + OPT_RA s, i); +} + +#ifdef GC_ATOMIC_UNCOLLECTABLE + GC_API GC_ATTR_MALLOC void * GC_CALL + GC_debug_malloc_atomic_uncollectable(size_t lb, GC_EXTRA_PARAMS) + { + void * result = GC_malloc_atomic_uncollectable( + SIZET_SAT_ADD(lb, UNCOLLECTABLE_DEBUG_BYTES)); + + return store_debug_info(result, lb, + "GC_debug_malloc_atomic_uncollectable", + OPT_RA s, i); + } +#endif /* GC_ATOMIC_UNCOLLECTABLE */ + +#ifndef GC_FREED_MEM_MARKER +# if CPP_WORDSZ == 32 +# define GC_FREED_MEM_MARKER 0xdeadbeef +# else +# define GC_FREED_MEM_MARKER GC_WORD_C(0xEFBEADDEdeadbeef) +# endif +#endif + +GC_API void GC_CALL GC_debug_free(void * p) +{ + ptr_t base; + if (0 == p) return; + + base = (ptr_t)GC_base(p); + if (NULL == base) { +# if defined(REDIRECT_MALLOC) \ + && ((defined(NEED_CALLINFO) && defined(GC_HAVE_BUILTIN_BACKTRACE)) \ + || defined(GC_LINUX_THREADS) || defined(GC_SOLARIS_THREADS) \ + || defined(MSWIN32)) + /* In some cases, we should ignore objects that do not belong */ + /* to the GC heap. See the comment in GC_free. */ + if (!GC_is_heap_ptr(p)) return; +# endif + ABORT_ARG1("Invalid pointer passed to free()", ": %p", p); + } + if ((ptr_t)p - (ptr_t)base != sizeof(oh)) { +# if defined(REDIRECT_FREE) && defined(USE_PROC_FOR_LIBRARIES) + /* TODO: Suppress the warning if free() caller is in libpthread */ + /* or libdl. */ +# endif + GC_err_printf( + "GC_debug_free called on pointer %p w/o debugging info\n", p); + } else { +# ifndef SHORT_DBG_HDRS + ptr_t clobbered = GC_check_annotated_obj((oh *)base); + word sz = GC_size(base); + if (clobbered != 0) { + GC_have_errors = TRUE; + if (((oh *)base) -> oh_sz == sz) { + GC_print_smashed_obj( + "GC_debug_free: found previously deallocated (?) object at", + p, clobbered); + return; /* ignore double free */ + } else { + GC_print_smashed_obj("GC_debug_free: found smashed location at", + p, clobbered); + } + } + /* Invalidate size (mark the object as deallocated) */ + ((oh *)base) -> oh_sz = sz; +# endif /* SHORT_DBG_HDRS */ + } + if (GC_find_leak +# ifndef SHORT_DBG_HDRS + && ((ptr_t)p - (ptr_t)base != sizeof(oh) || !GC_findleak_delay_free) +# endif + ) { + GC_free(base); + } else { + hdr * hhdr = HDR(p); + if (hhdr -> hb_obj_kind == UNCOLLECTABLE +# ifdef GC_ATOMIC_UNCOLLECTABLE + || hhdr -> hb_obj_kind == AUNCOLLECTABLE +# endif + ) { + GC_free(base); + } else { + word i; + word obj_sz = BYTES_TO_WORDS(hhdr->hb_sz - sizeof(oh)); + + for (i = 0; i < obj_sz; ++i) + ((word *)p)[i] = GC_FREED_MEM_MARKER; + GC_ASSERT((word *)p + i == (word *)(base + hhdr -> hb_sz)); + } + } /* !GC_find_leak */ +} + +#if defined(THREADS) && defined(DBG_HDRS_ALL) + /* Used internally; we assume it's called correctly. */ + GC_INNER void GC_debug_free_inner(void * p) + { + ptr_t base = (ptr_t)GC_base(p); + GC_ASSERT((ptr_t)p - (ptr_t)base == sizeof(oh)); +# ifdef LINT2 + if (!base) ABORT("Invalid GC_debug_free_inner argument"); +# endif +# ifndef SHORT_DBG_HDRS + /* Invalidate size */ + ((oh *)base) -> oh_sz = GC_size(base); +# endif + GC_free_inner(base); + } +#endif + +GC_API void * GC_CALL GC_debug_realloc(void * p, size_t lb, GC_EXTRA_PARAMS) +{ + void * base; + void * result; + hdr * hhdr; + + if (p == 0) { + return GC_debug_malloc(lb, OPT_RA s, i); + } + if (0 == lb) /* and p != NULL */ { + GC_debug_free(p); + return NULL; + } + +# ifdef GC_ADD_CALLER + if (s == NULL) { + GC_caller_func_offset(ra, &s, &i); + } +# endif + base = GC_base(p); + if (base == 0) { + ABORT_ARG1("Invalid pointer passed to realloc()", ": %p", p); + } + if ((ptr_t)p - (ptr_t)base != sizeof(oh)) { + GC_err_printf( + "GC_debug_realloc called on pointer %p w/o debugging info\n", p); + return(GC_realloc(p, lb)); + } + hhdr = HDR(base); + switch (hhdr -> hb_obj_kind) { + case NORMAL: + result = GC_debug_malloc(lb, OPT_RA s, i); + break; + case PTRFREE: + result = GC_debug_malloc_atomic(lb, OPT_RA s, i); + break; + case UNCOLLECTABLE: + result = GC_debug_malloc_uncollectable(lb, OPT_RA s, i); + break; +# ifdef GC_ATOMIC_UNCOLLECTABLE + case AUNCOLLECTABLE: + result = GC_debug_malloc_atomic_uncollectable(lb, OPT_RA s, i); + break; +# endif + default: + result = NULL; /* initialized to prevent warning. */ + ABORT_RET("GC_debug_realloc: encountered bad kind"); + } + + if (result != NULL) { + size_t old_sz; +# ifdef SHORT_DBG_HDRS + old_sz = GC_size(base) - sizeof(oh); +# else + old_sz = ((oh *)base) -> oh_sz; +# endif + if (old_sz > 0) + BCOPY(p, result, old_sz < lb ? old_sz : lb); + GC_debug_free(p); + } + return(result); +} + +GC_API GC_ATTR_MALLOC void * GC_CALL + GC_debug_generic_or_special_malloc(size_t lb, int knd, GC_EXTRA_PARAMS) +{ + switch (knd) { + case PTRFREE: + return GC_debug_malloc_atomic(lb, OPT_RA s, i); + case NORMAL: + return GC_debug_malloc(lb, OPT_RA s, i); + case UNCOLLECTABLE: + return GC_debug_malloc_uncollectable(lb, OPT_RA s, i); +# ifdef GC_ATOMIC_UNCOLLECTABLE + case AUNCOLLECTABLE: + return GC_debug_malloc_atomic_uncollectable(lb, OPT_RA s, i); +# endif + default: + return GC_debug_generic_malloc(lb, knd, OPT_RA s, i); + } +} + +#ifndef SHORT_DBG_HDRS + +/* List of smashed (clobbered) locations. We defer printing these, */ +/* since we can't always print them nicely with the allocation lock */ +/* held. We put them here instead of in GC_arrays, since it may be */ +/* useful to be able to look at them with the debugger. */ +#ifndef MAX_SMASHED +# define MAX_SMASHED 20 +#endif +STATIC ptr_t GC_smashed[MAX_SMASHED] = {0}; +STATIC unsigned GC_n_smashed = 0; + +STATIC void GC_add_smashed(ptr_t smashed) +{ + GC_ASSERT(GC_is_marked(GC_base(smashed))); + /* FIXME: Prevent adding an object while printing smashed list. */ + GC_smashed[GC_n_smashed] = smashed; + if (GC_n_smashed < MAX_SMASHED - 1) ++GC_n_smashed; + /* In case of overflow, we keep the first MAX_SMASHED-1 */ + /* entries plus the last one. */ + GC_have_errors = TRUE; +} + +/* Print all objects on the list. Clear the list. */ +STATIC void GC_print_all_smashed_proc(void) +{ + unsigned i; + + GC_ASSERT(I_DONT_HOLD_LOCK()); + if (GC_n_smashed == 0) return; + GC_err_printf("GC_check_heap_block: found %u smashed heap objects:\n", + GC_n_smashed); + for (i = 0; i < GC_n_smashed; ++i) { + ptr_t base = (ptr_t)GC_base(GC_smashed[i]); + +# ifdef LINT2 + if (!base) ABORT("Invalid GC_smashed element"); +# endif + GC_print_smashed_obj("", base + sizeof(oh), GC_smashed[i]); + GC_smashed[i] = 0; + } + GC_n_smashed = 0; +} + +/* Check all marked objects in the given block for validity */ +/* Avoid GC_apply_to_each_object for performance reasons. */ +STATIC void GC_check_heap_block(struct hblk *hbp, word dummy GC_ATTR_UNUSED) +{ + struct hblkhdr * hhdr = HDR(hbp); + word sz = hhdr -> hb_sz; + word bit_no; + char *p, *plim; + + p = hbp->hb_body; + if (sz > MAXOBJBYTES) { + plim = p; + } else { + plim = hbp->hb_body + HBLKSIZE - sz; + } + /* go through all words in block */ + for (bit_no = 0; (word)p <= (word)plim; + bit_no += MARK_BIT_OFFSET(sz), p += sz) { + if (mark_bit_from_hdr(hhdr, bit_no) && GC_HAS_DEBUG_INFO((ptr_t)p)) { + ptr_t clobbered = GC_check_annotated_obj((oh *)p); + if (clobbered != 0) + GC_add_smashed(clobbered); + } + } +} + +/* This assumes that all accessible objects are marked, and that */ +/* I hold the allocation lock. Normally called by collector. */ +STATIC void GC_check_heap_proc(void) +{ + GC_STATIC_ASSERT((sizeof(oh) & (GRANULE_BYTES - 1)) == 0); + /* FIXME: Should we check for twice that alignment? */ + GC_apply_to_all_blocks(GC_check_heap_block, 0); +} + +GC_INNER GC_bool GC_check_leaked(ptr_t base) +{ + word i; + word obj_sz; + word *p; + + if ( +# if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH) + (*(word *)base & 1) != 0 && +# endif + GC_has_other_debug_info(base) >= 0) + return TRUE; /* object has leaked */ + + /* Validate freed object's content. */ + p = (word *)(base + sizeof(oh)); + obj_sz = BYTES_TO_WORDS(HDR(base)->hb_sz - sizeof(oh)); + for (i = 0; i < obj_sz; ++i) + if (p[i] != GC_FREED_MEM_MARKER) { + GC_set_mark_bit(base); /* do not reclaim it in this cycle */ + GC_add_smashed((ptr_t)(&p[i])); /* alter-after-free detected */ + break; /* don't report any other smashed locations in the object */ + } + + return FALSE; /* GC_debug_free() has been called */ +} + +#endif /* !SHORT_DBG_HDRS */ + +#ifndef GC_NO_FINALIZATION + +struct closure { + GC_finalization_proc cl_fn; + void * cl_data; +}; + +STATIC void * GC_make_closure(GC_finalization_proc fn, void * data) +{ + struct closure * result = +# ifdef DBG_HDRS_ALL + (struct closure *) GC_debug_malloc(sizeof (struct closure), + GC_EXTRAS); +# else + (struct closure *) GC_malloc(sizeof (struct closure)); +# endif + if (result != 0) { + result -> cl_fn = fn; + result -> cl_data = data; + } + return((void *)result); +} + +/* An auxiliary fns to make finalization work correctly with displaced */ +/* pointers introduced by the debugging allocators. */ +STATIC void GC_CALLBACK GC_debug_invoke_finalizer(void * obj, void * data) +{ + struct closure * cl = (struct closure *) data; + (*(cl -> cl_fn))((void *)((char *)obj + sizeof(oh)), cl -> cl_data); +} + +/* Special finalizer_proc value to detect GC_register_finalizer() failure. */ +#define OFN_UNSET ((GC_finalization_proc)~(signed_word)0) + +/* Set ofn and ocd to reflect the values we got back. */ +static void store_old(void *obj, GC_finalization_proc my_old_fn, + struct closure *my_old_cd, GC_finalization_proc *ofn, + void **ocd) +{ + if (0 != my_old_fn) { + if (my_old_fn == OFN_UNSET) { + /* register_finalizer() failed; (*ofn) and (*ocd) are unchanged. */ + return; + } + if (my_old_fn != GC_debug_invoke_finalizer) { + GC_err_printf("Debuggable object at %p had a non-debug finalizer\n", + obj); + /* This should probably be fatal. */ + } else { + if (ofn) *ofn = my_old_cd -> cl_fn; + if (ocd) *ocd = my_old_cd -> cl_data; + } + } else { + if (ofn) *ofn = 0; + if (ocd) *ocd = 0; + } +} + +GC_API void GC_CALL GC_debug_register_finalizer(void * obj, + GC_finalization_proc fn, + void * cd, GC_finalization_proc *ofn, + void * *ocd) +{ + GC_finalization_proc my_old_fn = OFN_UNSET; + void * my_old_cd; + ptr_t base = (ptr_t)GC_base(obj); + if (NULL == base) { + /* We won't collect it, hence finalizer wouldn't be run. */ + if (ocd) *ocd = 0; + if (ofn) *ofn = 0; + return; + } + if ((ptr_t)obj - base != sizeof(oh)) { + GC_err_printf("GC_debug_register_finalizer called with" + " non-base-pointer %p\n", obj); + } + if (0 == fn) { + GC_register_finalizer(base, 0, 0, &my_old_fn, &my_old_cd); + } else { + cd = GC_make_closure(fn, cd); + if (cd == 0) return; /* out of memory */ + GC_register_finalizer(base, GC_debug_invoke_finalizer, + cd, &my_old_fn, &my_old_cd); + } + store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd); +} + +GC_API void GC_CALL GC_debug_register_finalizer_no_order + (void * obj, GC_finalization_proc fn, + void * cd, GC_finalization_proc *ofn, + void * *ocd) +{ + GC_finalization_proc my_old_fn = OFN_UNSET; + void * my_old_cd; + ptr_t base = (ptr_t)GC_base(obj); + if (NULL == base) { + /* We won't collect it, hence finalizer wouldn't be run. */ + if (ocd) *ocd = 0; + if (ofn) *ofn = 0; + return; + } + if ((ptr_t)obj - base != sizeof(oh)) { + GC_err_printf("GC_debug_register_finalizer_no_order called with" + " non-base-pointer %p\n", obj); + } + if (0 == fn) { + GC_register_finalizer_no_order(base, 0, 0, &my_old_fn, &my_old_cd); + } else { + cd = GC_make_closure(fn, cd); + if (cd == 0) return; /* out of memory */ + GC_register_finalizer_no_order(base, GC_debug_invoke_finalizer, + cd, &my_old_fn, &my_old_cd); + } + store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd); +} + +GC_API void GC_CALL GC_debug_register_finalizer_unreachable + (void * obj, GC_finalization_proc fn, + void * cd, GC_finalization_proc *ofn, + void * *ocd) +{ + GC_finalization_proc my_old_fn = OFN_UNSET; + void * my_old_cd; + ptr_t base = (ptr_t)GC_base(obj); + if (NULL == base) { + /* We won't collect it, hence finalizer wouldn't be run. */ + if (ocd) *ocd = 0; + if (ofn) *ofn = 0; + return; + } + if ((ptr_t)obj - base != sizeof(oh)) { + GC_err_printf("GC_debug_register_finalizer_unreachable called with" + " non-base-pointer %p\n", obj); + } + if (0 == fn) { + GC_register_finalizer_unreachable(base, 0, 0, &my_old_fn, &my_old_cd); + } else { + cd = GC_make_closure(fn, cd); + if (cd == 0) return; /* out of memory */ + GC_register_finalizer_unreachable(base, GC_debug_invoke_finalizer, + cd, &my_old_fn, &my_old_cd); + } + store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd); +} + +GC_API void GC_CALL GC_debug_register_finalizer_ignore_self + (void * obj, GC_finalization_proc fn, + void * cd, GC_finalization_proc *ofn, + void * *ocd) +{ + GC_finalization_proc my_old_fn = OFN_UNSET; + void * my_old_cd; + ptr_t base = (ptr_t)GC_base(obj); + if (NULL == base) { + /* We won't collect it, hence finalizer wouldn't be run. */ + if (ocd) *ocd = 0; + if (ofn) *ofn = 0; + return; + } + if ((ptr_t)obj - base != sizeof(oh)) { + GC_err_printf("GC_debug_register_finalizer_ignore_self called with" + " non-base-pointer %p\n", obj); + } + if (0 == fn) { + GC_register_finalizer_ignore_self(base, 0, 0, &my_old_fn, &my_old_cd); + } else { + cd = GC_make_closure(fn, cd); + if (cd == 0) return; /* out of memory */ + GC_register_finalizer_ignore_self(base, GC_debug_invoke_finalizer, + cd, &my_old_fn, &my_old_cd); + } + store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd); +} + +#endif /* !GC_NO_FINALIZATION */ + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_debug_malloc_replacement(size_t lb) +{ + return GC_debug_malloc(lb, GC_DBG_EXTRAS); +} + +GC_API void * GC_CALL GC_debug_realloc_replacement(void *p, size_t lb) +{ + return GC_debug_realloc(p, lb, GC_DBG_EXTRAS); +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/digimars.mak b/unity-2019.4.24f1-mbe/external/bdwgc/digimars.mak new file mode 100644 index 000000000..2d30d3658 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/digimars.mak @@ -0,0 +1,90 @@ +# Makefile to build Hans Boehm garbage collector using the Digital Mars +# compiler from www.digitalmars.com +# Written by Walter Bright + + +DEFINES=-DNDEBUG -D_WINDOWS -DGC_DLL -DALL_INTERIOR_POINTERS -DENABLE_DISCLAIM -DWIN32_THREADS +CFLAGS=-Iinclude $(DEFINES) -wx -g +LFLAGS=/ma/implib/co +CC=sc + +.c.obj: + $(CC) -c $(CFLAGS) $* + +.cpp.obj: + $(CC) -c $(CFLAGS) -Aa $* + +OBJS= \ + allchblk.obj\ + alloc.obj\ + blacklst.obj\ + checksums.obj\ + dbg_mlc.obj\ + fnlz_mlc.obj\ + dyn_load.obj\ + finalize.obj\ + gc_cpp.obj\ + headers.obj\ + mach_dep.obj\ + malloc.obj\ + mallocx.obj\ + mark.obj\ + mark_rts.obj\ + misc.obj\ + new_hblk.obj\ + obj_map.obj\ + os_dep.obj\ + ptr_chck.obj\ + reclaim.obj\ + typd_mlc.obj\ + win32_threads.obj + +targets: gc.dll gc.lib gctest.exe + +gc.dll: $(OBJS) gc.def digimars.mak + sc -ogc.dll $(OBJS) -L$(LFLAGS) gc.def kernel32.lib user32.lib + +gc.def: digimars.mak + echo LIBRARY GC >gc.def + echo DESCRIPTION "Hans Boehm Garbage Collector" >>gc.def + echo EXETYPE NT >>gc.def + echo EXPORTS >>gc.def + echo GC_is_visible_print_proc >>gc.def + echo GC_is_valid_displacement_print_proc >>gc.def + +clean: + del gc.def + del $(OBJS) + + +gctest.exe : gc.lib tests\test.obj + sc -ogctest.exe tests\test.obj gc.lib + +tests\test.obj : tests\test.c + $(CC) -c -g -DNDEBUG -D_WINDOWS -DGC_DLL \ + -DALL_INTERIOR_POINTERS -DWIN32_THREADS \ + -Iinclude tests\test.c -otests\test.obj + +allchblk.obj: allchblk.c +alloc.obj: alloc.c +blacklst.obj: blacklst.c +checksums.obj: checksums.c +dbg_mlc.obj: dbg_mlc.c +dyn_load.obj: dyn_load.c +finalize.obj: finalize.c +fnlz_mlc.obj: fnlz_mlc.c +gc_cpp.obj: gc_cpp.cpp +headers.obj: headers.c +mach_dep.obj: mach_dep.c +malloc.obj: malloc.c +mallocx.obj: mallocx.c +mark.obj: mark.c +mark_rts.obj: mark_rts.c +misc.obj: misc.c +new_hblk.obj: new_hblk.c +obj_map.obj: obj_map.c +os_dep.obj: os_dep.c +ptr_chck.obj: ptr_chck.c +reclaim.obj: reclaim.c +typd_mlc.obj: typd_mlc.c +win32_threads.obj: win32_threads.c diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.DGUX386 b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.DGUX386 new file mode 100644 index 000000000..98f344c0c --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.DGUX386 @@ -0,0 +1,39 @@ + Garbage Collector (parallel version) for x86 DG/UX Release R4.20MU07 + + + *READ* the file README.QUICK. + + You need the GCC-3.0.3 rev (DG/UX) compiler to build this tree. + This compiler has the new "dgux386" threads package implemented. + It also supports the switch "-pthread" needed to link correctly + the DG/UX's -lrte -lthread with -lgcc and the system's -lc. + Finally, we support parallel mark for the SMP DG/UX machines. + To build the garbage collector do: + + ./configure + make + make gctest + + Before you run "gctest" you need to set your LD_LIBRARY_PATH + correctly so that "gctest" can find the shared library libgc. + Alternatively you can do a configuration + + ./configure --disable-shared + + to build only the static version of libgc. + + To enable debugging messages please do: + 1) Add the "--enable-gc-debug" flag during configuration. + 2) Pass "CFLAGS=-DDEBUG_THREADS" to "make". + + In a machine with 4 CPUs (my own machine), parallel mark makes + a BIG difference. + + Takis Psarogiannakopoulos + +Note (HB): + The integration of this patch is currently not complete. + The following patches against 6.1alpha3 where hard to move + to alpha4, and are not integrated. There may also be minor + problems with stylistic corrections made by me. +[The diff for ltconfig and ltmain.sh was removed from this file on 2011-08-22] diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.Mac b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.Mac new file mode 100644 index 000000000..848c1bc50 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.Mac @@ -0,0 +1,337 @@ +The contents of this file are old and pertain to pre-MacOSX versions. +You probably really wanted README.darwin. + +--------------------------------------------- + +Patrick Beard's Notes for building GC v4.12 with CodeWarrior Pro 2: +---------------------------------------------------------------------------- +The current build environment for the collector is CodeWarrior Pro 2. +Projects for CodeWarrior Pro 2 (and for quite a few older versions) +are distributed in the file Mac_projects.sit.hqx. The project file +:Mac_projects:gc.prj builds static library versions of the collector. +:Mac_projects:gctest.prj builds the GC test suite. + +Configuring the collector is still done by editing the file +:extra:Mac_files:MacOS_config.h. + +Lars Farm's suggestions on building the collector: +---------------------------------------------------------------------------- +Garbage Collection on MacOS - a manual 'MakeFile' +------------------------------------------------- + +Project files and IDE's are great on the Macintosh, but they do have +problems when used as distribution media. This note tries to provide +porting instructions in pure TEXT form to avoid those problems. A manual +'makefile' if you like. + + GC version: 4.12a2 + Codewarrior: CWPro1 + date: 18 July 1997 + +The notes may or may not apply to earlier or later versions of the +GC/CWPro. Actually, they do apply to earlier versions of both except that +until recently a project could only build one target so each target was a +separate project. The notes will most likely apply to future versions too. +Possibly with minor tweaks. + +This is just to record my experiences. These notes do not mean I now +provide a supported port of the GC to MacOS. It works for me. If it works +for you, great. If it doesn't, sorry, try again...;-) Still, if you find +errors, please let me know. + + +Porting to MacOS is a bit more complex than it first seems. Which MacOS? +68K/PowerPC? Which compiler? Each supports both 68K and PowerPC and offer a +large number of (unique to each environment) compiler settings. Each +combination of compiler/68K/PPC/settings require a unique combination of +standard libraries. And the IDE's does not select them for you. They don't +even check that the library is built with compatible setting and this is +the major source of problems when porting the GC (and otherwise too). + +You will have to make choices when you configure the GC. I've made some +choices here, but there are other combinations of settings and #defines +that work too. + +As for target settings the major obstacles may be: +- 68K Processor: check "4-byte Ints". +- PPC Processor: uncheck "Store Static Data in TOC". + +What you need to do: +1) Build the GC as a library +2) Test that the library works with 'test.c'. +3) Test that the C++ interface 'gc_cpp.cc/h' works with 'test_cpp.cc'. + +== 1. The Libraries == + +I made one project with four targets (68K/PPC tempmem or appheap). One target +will suffice if you're able to decide which one you want. I wasn't... + +Codewarrior allows a large number of compiler/linker settings. I used these: + +Settings shared by all targets: +------------------------------ +o Access Paths: + - User Paths: the GC folder + - System Paths: {Compiler}:Metrowerks Standard Library: + {Compiler}:MacOS Support:Headers: + {Compiler}:MacOS Support:MacHeaders: +o C/C++ language: + - inlining: normal + - direct to SOM: off + - enable/check: exceptions, RTTI, bool (and if you like pool strings) + +PowerPC target settings +----------------------- +o Target Settings: + - name of target + - MacOS PPC Linker +o PPC Target + - name of library +o C/C++ language + - prefix file as described below +o PPC Processor + - Struct Alignment: PowerPC + - uncheck "Store Static Data in TOC" -- important! + I don't think the others matter, I use full optimization and it is OK +o PPC Linker + - Factory Settings (SYM file with full paths, faster linking, dead-strip + static init, Main: __start) + + +68K target settings +------------------- +o Target Settings: + - name of target + - MacOS 68K Linker +o 68K Target + - name of library + - A5 relative data +o C/C++ language + - prefix file as described below +o 68K Processor + - Code model: smart + - Struct alignment: 68K + - FP: SANE + - enable 4-Byte Ints -- important! + I don't think the others matter. I selected... + - enable: 68020 + - enable: global register allocation +o IR Optimizer + - enable: Optimize Space, Optimize Speed + I suppose the others would work too, but haven't tried... +o 68K Linker + - Factory Settings (New Style MacsBug, SYM file with full paths, + A6 Frames, fast link, Merge compiler glue into segment 1, + dead-strip static init) + +Prefix Files to configure the GC sources +---------------------------------------- +The Codewarrior equivalent of command-line compilers -DNAME=X is to use +prefix-files. A TEXT file that is automatically #included before the first byte +of every source file. I used these: + +---- ( cut here ) ---- gc_prefix_tempmem.h -- 68K and PPC ----- + #include "gc_prefix_common.h" + #undef USE_TEMPORARY_MEMORY + #define USE_TEMPORARY_MEMORY +---- ( cut here ) ---- gc_prefix_appmem.h -- 68K and PPC ----- + #include "gc_prefix_common.h" + #undef USE_TEMPORARY_MEMORY +// #define USE_TEMPORARY_MEMORY + +---- ( cut here ) ---- gc_prefix_common.h -------------------- +// gc_prefix_common.h +// ------------------ +// Codewarrior prefix file to configure the GC libraries +// +// prefix files are the Codewarrior equivalent of the +// command line option -Dname=x frequently seen in makefiles + +#if !__MWERKS__ + #error only tried this with Codewarrior +#endif + +#if macintosh + #define MSL_USE_PRECOMPILED_HEADERS 0 + #include + + // See list of #defines to configure the library in: 'MakeFile' + // see also README + + #define ALL_INTERIOR_POINTERS // follows interior pointers. +//#define DONT_ADD_BYTE_AT_END // disables the padding if defined. +//#define SMALL_CONFIG // whether to use a smaller heap. + #define GC_ATOMIC_UNCOLLECTABLE // GC_malloc_atomic_uncollectable() + + // define either or none as per personal preference + // used in malloc.c + #define REDIRECT_MALLOC GC_malloc +//#define REDIRECT_MALLOC GC_malloc_uncollectable + // if REDIRECT_MALLOC is #defined make sure that the GC library + // is listed before the ANSI/ISO libs in the Codewarrior + // 'Link order' panel +//#define IGNORE_FREE + + // mac specific configs +//#define USE_TEMPORARY_MEMORY // use Macintosh temporary memory. +//#define SHARED_LIBRARY_BUILD // build for use in a shared library. + +#else + // could build Win32 here too, or in the future + // Rhapsody PPC-mach, Rhapsody PPC-MacOS, + // Rhapsody Intel-mach, Rhapsody Intel-Win32,... + // ... ugh this will get messy ... +#endif + +// make sure ints are at least 32-bit +// ( could be set to 16-bit by compiler settings (68K) ) + +struct gc_private_assert_intsize_{ char x[ sizeof(int)>=4 ? 1 : 0 ]; }; + +#if __powerc + #if __option(toc_data) + #error turn off "store static data in TOC" when using GC + // ... or find a way to add TOC to the root set...(?) + #endif +#endif +---- ( cut here ) ---- end of gc_prefix_common.h ----------------- + +Files to build the GC libraries: +-------------------------------- + allchblk.c + alloc.c + blacklst.c + checksums.c + dbg_mlc.c + finalize.c + headers.c + mach_dep.c + MacOS.c -- contains MacOS code + malloc.c + mallocx.c + mark.c + mark_rts.c + misc.c + new_hblk.c + obj_map.c + os_dep.c -- contains MacOS code + ptr_chck.c + reclaim.c + typd_mlc.c + gc++.cc -- this is 'gc_cpp.cc' with less 'inline' and + -- throw std::bad_alloc when out of memory + -- gc_cpp.cc works just fine too + +== 2. Test that the library works with 'test.c' == + +The test app is just an ordinary ANSI-C console app. Make sure settings +match the library you're testing. + +Files +----- + test.c + the GC library to test -- link order before ANSI libs + suitable Mac+ANSI libraries + +prefix: +------ +---- ( cut here ) ---- gc_prefix_testlib.h -- all libs ----- +#define MSL_USE_PRECOMPILED_HEADERS 0 +#include +#undef NDEBUG + +#define ALL_INTERIOR_POINTERS /* for GC_priv.h */ +---- ( cut here ) ---- + +== 3. Test that the C++ interface 'gc_cpp.cc/h' works with 'test_cpp.cc' == + +The test app is just an ordinary ANSI-C console app. Make sure settings match +the library you're testing. + +Files +----- + test_cpp.cc + the GC library to test -- link order before ANSI libs + suitable Mac+ANSI libraries + +prefix: +------ +same as for test.c + +For convenience I used one test-project with several targets so that all +test apps are build at once. Two for each library to test: test.c and +gc_app.cc. When I was satisfied that the libraries were OK. I put the +libraries + gc.h + the c++ interface-file in a folder that I then put into +the MSL hierarchy so that I don't have to alter access-paths in projects +that use the GC. + +After that, just add the proper GC library to your project and the GC is in +action! malloc will call GC_malloc and free GC_free, new/delete too. You +don't have to call free or delete. You may have to be a bit cautious about +delete if you're freeing other resources than RAM. See gc_cpp.h. You can +also keep coding as always with delete/free. That works too. If you want, +include "gc.h" and tweak its use a bit. + +== Symantec SPM == + +It has been a while since I tried the GC in SPM, but I think that the above +instructions should be sufficient to guide you through in SPM too. SPM +needs to know where the global data is. Use the files 'datastart.c' and +'dataend.c'. Put 'datastart.c' at the top of your project and 'dataend.c' +at the bottom of your project so that all data is surrounded. This is not +needed in Codewarrior because it provides intrinsic variables +__datastart__, __data_end__ that wraps all globals. + + + It worked for me, hope it works for you. + + Lars Farm +---------------------------------------------------------------------------- + + +Patrick Beard's instructions (may be dated): + +v4.3 of the collector now runs under Symantec C++/THINK C v7.0.4, and +Metrowerks C/C++ v4.5 both 68K and PowerPC. Project files are provided +to build and test the collector under both development systems. + +Configuration +------------- + +To configure the collector, under both development systems, a prefix file +is used to set preprocessor directives. This file is called "MacOS_config.h". + +Testing +------- + +To test the collector (always a good idea), build one of the gctest projects, +gctest. (Symantec C++/THINK C), mw/gctest.68K, or mw/gctest.PPC. The +test will ask you how many times to run; 1 should be sufficient. + +Building +-------- + +For your convenience project files for the major Macintosh development +systems are provided. + +For Symantec C++/THINK C, you must build the two projects gclib-1 and +gclib-2. It has to be split up because the collector has more than 32k +of static data and no library can have more than this in the Symantec +environment. (Future versions will probably fix this.) + +For Metrowerks C/C++ 4.5 you build gc.68K/PPC and the result will +be a library called gc.68K.lib/gc.PPC.lib. + +Using +----- + +Under Symantec C++/THINK C, you can just add the gclib-1 and gclib-2 +projects to your own project. Under Metrowerks, you add gc.68K.lib or +gc.PPC.lib and two additional files. You add the files called datastart.c +and dataend.c to your project, bracketing all files that use the collector. +See mw/gctest for an example. + +Include the projects/libraries you built above into your own project, +#include "gc.h", and call GC_malloc. You don't have to call GC_free. + +Patrick C. Beard diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.OS2 b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.OS2 new file mode 100644 index 000000000..5345bbd0f --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.OS2 @@ -0,0 +1,6 @@ +The code assumes static linking, and a single thread. The editor de has +not been ported. The cord test program has. The supplied OS2_MAKEFILE +assumes the IBM C Set/2 environment, but the code shouldn't. + +Since we haven't figured out hoe to do perform partial links or to build static +libraries, clients currently need to link against a long list of executables. diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.amiga b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.amiga new file mode 100644 index 000000000..d97251fb4 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.amiga @@ -0,0 +1,288 @@ + Kjetil S. Matheussen's notes (28-11-2000) + +Compiles under SAS/C again. Should also still compile under other +Amiga compilers without big changes. I haven't checked if it still +works under gcc, because I don't have gcc for Amiga. But I have +updated 'Makefile', and hope it compiles fine. + + +WHATS NEW: + +1. + Made a pretty big effort in preventing GCs allocating-functions from returning + chip-mem. + + The lower part of the new file AmigaOS.c does this in various ways, mainly by + wrapping GC_malloc, GC_malloc_atomic, GC_malloc_uncollectable, + GC_malloc_atomic_uncollectable, GC_malloc_ignore_off_page + and GC_malloc_atomic_ignore_off_page. GC_realloc is also wrapped, but + doesn't do the same effort in preventing to return chip-mem. + Other allocating-functions (f.ex. GC_*_typed_) can probably be + used without any problems, but beware that the warn hook will not be called. + In case of problems, don't define GC_AMIGA_FASTALLOC. + + Programs using more time actually using the memory allocated + (instead of just allocate and free rapidly) have + the most to earn on this, but even gctest now normally runs twice + as fast and uses less memory, on my poor 8MB machine. + + The changes have only effect when there is no more + fast-mem left. But with the way GC works, it + could happen quite often. Beware that an atexit handler had to be added, + so using the abort() function will make a big memory-loss. + If you absolutely must call abort() instead of exit(), try calling + the GC_amiga_free_all_mem function before abort(). + + New Amiga-specific compilation flags: + + GC_AMIGA_FASTALLOC - By NOT defining this option, GC will work like before, + it will not try to force fast-mem out of the OS, and + it will use normal calloc for allocation, and the rest + of the following flags will have no effect. + + GC_AMIGA_ONLYFAST - Makes GC never to return chip-mem. GC_AMIGA_RETRY have + no effect if this flag is set. + + GC_AMIGA_GC - If gc returns NULL, do a GC_gcollect, and try again. This + usually is a success with the standard GC configuration. + It is also the most important flag to set to prevent + GC from returning chip-mem. Beware that it slows down a lot + when a program is rapidly allocating/deallocating when + there's either very little fast-memory left or very little + chip-memory left. Its not a very common situation, but gctest + sometimes (very rare) use many minutes because of this. + + GC_AMIGA_RETRY - If gc succeed allocating memory, but it is chip-mem, + try again and see if it is fast-mem. Most of the time, + it will actually return fast-mem for the second try. + I have set max number of retries to 9 or size/5000. You + can change this if you like. (see GC_amiga_rec_alloc()) + + GC_AMIGA_PRINTSTATS - Gather some statistics during the execution of a + program, and prints out the info when the atexit-handler + is called. + + My recommendation is to set all this flags, except GC_AMIGA_PRINTSTATS and + GC_AMIGA_ONLYFAST. + + If your program demands high response-time, you should + not define GC_AMIGA_GC, and possible also define GC_AMIGA_ONLYFAST. + GC_AMIGA_RETRY does not seem to slow down much. + + Also, when compiling up programs, and GC_AMIGA_FASTALLOC was not defined when + compiling gc, you can define GC_AMIGA_MAKINGLIB to avoid having these allocation- + functions wrapped. (see gc.h) + + Note that GC_realloc must not be called before any of + the other above mentioned allocating-functions have been called. (shouldn't be + any programs doing so either, I hope). + + Another note. The allocation-function is wrapped when defining + GC_AMIGA_FASTALLOC by letting the function go thru the new + GC_amiga_allocwrapper_do function-pointer (see gc.h). Means that + sending function-pointers, such as GC_malloc, GC_malloc_atomic, etc., + for later to be called like f.ex this, (*GC_malloc_function_pointer)(size), + will not wrap the function. This is normally not a big problem, unless + all allocation function is called like this, which will cause the + atexit un-allocating function never to be called. Then you either + have to manually add the atexit handler, or call the allocation- + functions function-pointer functions like this; + (*GC_amiga_allocwrapper_do)(size,GC_malloc_function_pointer). + There are probably better ways this problem could be handled, unfortunately, + I didn't find any without rewriting or replacing a lot of the GC-code, which + I really didn't want to. (Making new GC_malloc_* functions, and just + define f.ex GC_malloc as GC_amiga_malloc should work too). + + + New Amiga-specific function: + + void GC_amiga_set_toany(void (*func)(void)); + + 'func' is a function that will be called right before gc has to change + allocation-method from MEMF_FAST to MEMF_ANY. Ie. when it is likely + it will return chip-mem. + + +2. A few small compiler-specific additions to make it compile with SAS/C again. + +3. Updated and rewritten the smakefile, so that it works again and that + the "unnecessary" 'SCOPTIONS' files could be removed. Also included + the cord-smakefile stuff in the main smakefile, so that the cord smakefile + could be removed too. By writing smake -f Smakefile.smk, both gc.lib and + cord.lib will be made. + + + +STILL MISSING: + +Programs can not be started from workbench, at least not for SAS/C. (Martin +Tauchmanns note about that it now works with workbench is definitely wrong +when concerning SAS/C). An iconx-script solves this problem. + + +BEWARE! + +-To run gctest, set the stack to around 200000 bytes first. +-SAS/C-specific: cord will crash if you compile gc.lib with + either parm=reg or parm=both. (missing legal prototypes for + function-pointers someplace is the reason I guess.). + + +tested with software: Radium, http://www.stud.ifi.uio.no/~ksvalast/radium/ +tested with hardware: MC68060 + + + Martin Tauchmann's notes (1-Apr-99) + +Works now, also with the GNU-C compiler V2.7.2.1. +Modify the `Makefile` +CC=cc $(ABI_FLAG) +to +CC=gcc $(ABI_FLAG) + +TECHNICAL NOTES + +- `GC_get_stack_base()`, `GC_register_data_segments()` works now with every + C compiler; also Workbench. + +- Removed AMIGA_SKIP_SEG, but the Code-Segment must not be scanned by GC. + + +PROBLEMS +- When the Linker, does`t merge all Code-Segments to an single one. LD of GCC + do it always. + +- With ixemul.library V47.3, when an GC program launched from another program + (example: `Make` or `if_mach M68K AMIGA gctest`), `GC_register_data_segments()` + found the Segment-List of the caller program. + Can be fixed, if the run-time initialization code (for C programs, usually *crt0*) + support `__data` and `__bss`. + +- PowerPC Amiga currently not supported. + +- Dynamic libraries (dyn_load.c) not supported. + + +TESTED WITH SOFTWARE + +`Optimized Oberon 2 C` (oo2c) + + +TESTED WITH HARDWARE + +MC68030 + + + Michel Schinz's notes + +WHO DID WHAT + +The original Amiga port was made by Jesper Peterson. I (Michel Schinz) +modified it slightly to reflect the changes made in the new official +distributions, and to take advantage of the new SAS/C 6.x features. I also +created a makefile to compile the "cord" package (see the cord +subdirectory). + +TECHNICAL NOTES + +In addition to Jesper's notes, I have the following to say: + +- Starting with version 4.3, gctest checks to see if the code segment is + added to the root set or not, and complains if it is. Previous versions + of this Amiga port added the code segment to the root set, so I tried to + fix that. The only problem is that, as far as I know, it is impossible to + know which segments are code segments and which are data segments (there + are indeed solutions to this problem, like scanning the program on disk + or patch the LoadSeg functions, but they are rather complicated). The + solution I have chosen (see os_dep.c) is to test whether the program + counter is in the segment we are about to add to the root set, and if it + is, to skip the segment. The problems are that this solution is rather + awkward and that it works only for one code segment. This means that if + your program has more than one code segment, all of them but one will be + added to the root set. This isn't a big problem in fact, since the + collector will continue to work correctly, but it may be slower. + + Anyway, the code which decides whether to skip a segment or not can be + removed simply by not defining AMIGA_SKIP_SEG. But notice that if you do + so, gctest will complain (it will say that "GC_is_visible produced wrong + failure indication"). However, it may be useful if you happen to have + pointers stored in a code segment (you really shouldn't). + + If anyone has a good solution to the problem of finding, when a program + is loaded in memory, whether a segment is a code or a data segment, + please let me know. + + + Jesper Peterson's notes + +ADDITIONAL NOTES FOR AMIGA PORT + +These notes assume some familiarity with Amiga internals. + +WHY I PORTED TO THE AMIGA + +The sole reason why I made this port was as a first step in getting +the Sather(*) language on the Amiga. A port of this language will +be done as soon as the Sather 1.0 sources are made available to me. +Given this motivation, the garbage collection (GC) port is rather +minimal. + +(*) For information on Sather read the comp.lang.sather newsgroup. + +LIMITATIONS + +This port assumes that the startup code linked with target programs +is that supplied with SAS/C versions 6.0 or later. This allows +assumptions to be made about where to find the stack base pointer +and data segments when programs are run from WorkBench, as opposed +to running from the CLI. The compiler dependent code is all in the +GC_get_stack_base() and GC_register_data_segments() functions, but +may spread as I add Amiga specific features. + +Given that SAS/C was assumed, the port is set up to be built with +"smake" using the "SMakefile". Compiler options in "SCoptions" can +be set with "scopts" program. Both "smake" and "scopts" are part of +the SAS/C commercial development system. + +In keeping with the porting philosophy outlined above, this port +will not behave well with Amiga specific code. Especially not inter- +process comms via messages, and setting up public structures like +Intuition objects or anything else in the system lists. For the +time being the use of this library is limited to single threaded +ANSI/POSIX compliant or near-compliant code. (ie. Stick to stdio +for now). Given this limitation there is currently no mechanism for +allocating "CHIP" or "PUBLIC" memory under the garbage collector. +I'll add this after giving it considerable thought. The major +problem is the entire physical address space may have to me scanned, +since there is no telling who we may have passed memory to. + +If you allocate your own stack in client code, you will have to +assign the pointer plus stack size to GC_stackbottom. + +The initial stack size of the target program can be compiled in by +setting the __stack symbol (see SAS documentation). It can be over- +ridden from the CLI by running the AmigaDOS "stack" program, or from +the WorkBench by setting the stack size in the tool types window. + +SAS/C COMPILER OPTIONS (SCoptions) + +You may wish to check the "CPU" code option is appropriate for your +intended target system. + +Under no circumstances set the "StackExtend" code option in either +compiling the library or *ANY* client code. + +All benign compiler warnings have been suppressed. These mainly +involve lack of prototypes in the code, and dead assignments +detected by the optimizer. + +THE GOOD NEWS + +The library as it stands is compatible with the GigaMem commercial +virtual memory software, and probably similar PD software. + +The performance of "gctest" on an Amiga 2630 (68030 @ 25Mhz) +compares favorably with an HP9000 with similar architecture (a 325 +with a 68030 I think). + +----------------------------------------------------------------------- diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.arm.cross b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.arm.cross new file mode 100644 index 000000000..0ededbd9b --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.arm.cross @@ -0,0 +1,62 @@ +From: Margaret Fleck + +Here's the key details of what worked for me, in case anyone else needs them. +There may well be better ways to do some of this, but .... + -- Margaret + + +The badge4 has a StrongArm-1110 processor and a StrongArm-1111 coprocessor. + +Assume that the garbage collector distribution is unpacked into /home/arm/gc6.0, +which is visible to both the ARM machine and a linux desktop (e.g. via NFS mounting). + +Assume that you have a file /home/arm/config.site with contents something like the +example attached below. Notice that our local ARM toolchain lives in +/skiff/local. + +Go to /home/arm/gc6.0 directory. Do + CONFIG_SITE=/home/arm/config.site ./configure --target=arm-linux +--prefix=/home/arm/gc6.0 + +On your desktop, do: + make + make install +The main garbage collector library should now be in ../gc6.0/lib/libgc.so. + +To test the garbage collector, first do the following on your desktop + make gctest + ./gctest +Then do the following on the ARM machine + cd .libs + ./lt-gctest + +Do not try to do "make test" (the usual way of running the test +program). This does not work and seems to erase some of the important +files. + +The gctest program claims to have succeeded. Haven't run any further tests +with it, though I'll be doing so in the near future. + +------------------------------- +# config.site for configure + +HOSTCC=gcc + +# Names of the cross-compilers +CC=/skiff/local/bin/arm-linux-gcc +CXX=/skiff/local/bin/arm-linux-gcc + +# The cross compiler specific options +CFLAGS="-O2 -fno-exceptions" +CXXFLAGS="-O2 -fno-exceptions" +CPPFLAGS="-O2 -fno-exceptions" +LDFLAGS="" + +# Some other programs +AR=/skiff/local/bin/arm-linux-ar +RANLIB=/skiff/local/bin/arm-linux-ranlib +NM=/skiff/local/bin/arm-linux-nm +ac_cv_path_NM=/skiff/local/bin/arm-linux-nm +ac_cv_func_setpgrp_void=yes +x_includes=/skiff/local/arm-linux/include/X11 +x_libraries=/skiff/local/arm-linux/lib/X11 diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.autoconf b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.autoconf new file mode 100644 index 000000000..5719075c4 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.autoconf @@ -0,0 +1,59 @@ +Starting from GC v6.0, we support GNU-style builds based on automake, +autoconf and libtool. This is based almost entirely on Tom Tromey's work +with gcj. + +To build and install libraries use + +configure; make; make install + +The advantages of this process are: + +1) It should eventually do a better job of automatically determining the +right compiler to use, etc. It probably already does in some cases. + +2) It tries to automatically set a good set of default GC parameters for +the platform (e.g. thread support). It provides an easier way to configure +some of the others. + +3) It integrates better with other projects using a GNU-style build process. + +4) It builds both dynamic and static libraries. + +The known disadvantages are: + +1) The build scripts are much more complex and harder to debug (though largely +standard). I don't understand them all, and there's probably lots of redundant +stuff. + +2) It probably doesn't work on all Un*x-like platforms yet. It probably will +never work on the rest. + +3) The scripts are not yet complete. Some of the standard GNU targets don't +yet work. (Corrections/additions are very welcome.) + +The distribution should contain all files needed to run "configure" and "make", +as well as the sources needed to regenerate the derived files. (If I missed +some, please let me know.) + +Note that the distribution comes without "Makefile" which is generated by +"configure". The distribution also contains "Makefile.direct" which is not +always equivalent to the generated one. + +Important options to configure: + + --prefix=PREFIX install architecture-independent files in PREFIX + [/usr/local] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [same as prefix] + --enable-threads=TYPE choose threading package + --disable-parallel-mark do not parallelize marking and free list + construction + --enable-gc-debug include full support for pointer back-tracing, etc. + + +Unless --prefix is set (or --exec-prefix or one of the more obscure options), +make install will install libgc.a and libgc.so in /usr/local/bin, which +would typically require the "make install" to be run as root. + +It is not recommended to turn off parallel marking for multiprocessors unless +a poor support of the feature on the platform. diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.cmake b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.cmake new file mode 100644 index 000000000..e691fa952 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.cmake @@ -0,0 +1,50 @@ + +CMAKE +----- + +Win32 binaries (both 32- and 64-bit) can be built using CMake. CMake is an +open-source tool like automake - it generates makefiles. + +Some preliminary work has been done to make this work on other platforms, but +the support is not yet complete. + +CMake will generate: + + Borland Makefiles + MSYS Makefiles + MinGW Makefiles + NMake Makefiles + Unix Makefiles + . Visual Studio project files + Visual Studio 6 + Visual Studio 7 + Visual Studio 7 .NET 2003 + Visual Studio 8 2005 + Visual Studio 8 2005 Win64 + Visual Studio 9 2008 + Visual Studio 9 2008 Win64 + Watcom WMake + + +BUILD PROCESS +------------- + + . install cmake (cmake.org) + . add directory containing cmake.exe to %PATH% + . run cmake from the gc root directory, passing the target with -G: + e.g., + > cmake -G "Visual Studio 8 2005" + use the gc.sln file generated by cmake to build gc + . you can also run cmake from a build directory to build outside of + the source tree. Just specify the path to the source tree: + e.g., + > mkdir build + > cd build + > cmake .. -G "Visual Studio 8 2005" + + +INPUT +----- + +The main input to cmake are the CMakeLists.txt files in each directory. For +help, goto cmake.org. diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.cords b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.cords new file mode 100644 index 000000000..db88b4c48 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.cords @@ -0,0 +1,47 @@ +Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. + +THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED +OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + +Permission is hereby granted to use or copy this program +for any purpose, provided the above notices are retained on all copies. +Permission to modify the code and to distribute modified code is granted, +provided the above notices are retained, and a notice that the code was +modified is included with the above copyright notice. + +Please send bug reports to Hans-J. Boehm. + +This is a string packages that uses a tree-based representation. +See cord.h for a description of the functions provided. Ec.h describes +"extensible cords", which are essentially output streams that write +to a cord. These allow for efficient construction of cords without +requiring a bound on the size of a cord. + +More details on the data structure can be found in + +Boehm, Atkinson, and Plass, "Ropes: An Alternative to Strings", +Software Practice and Experience 25, 12, December 1995, pp. 1315-1330. + +A fundamentally similar "rope" data structure is also part of SGI's standard +template library implementation, and its descendants, which include the +GNU C++ library. That uses reference counting by default. +There is a short description of that data structure at +http://www.sgi.com/tech/stl/ropeimpl.html . + +All of these are descendants of the "ropes" in Xerox Cedar. + +cord/tests/de.c is a very dumb text editor that illustrates the use of cords. +It maintains a list of file versions. Each version is simply a +cord representing the file contents. Nonetheless, standard +editing operations are efficient, even on very large files. +(Its 3 line "user manual" can be obtained by invoking it without +arguments. Note that ^R^N and ^R^P move the cursor by +almost a screen. It does not understand tabs, which will show +up as highlighted "I"s. Use the UNIX "expand" program first.) +To build the editor, type "make cord/de" in the gc directory. + +Note that CORD_printf and friends use C functions with variable numbers +of arguments in non-standard-conforming ways. This code is known to +break on some platforms, notably PowerPC. It should be possible to +build the remainder of the library (everything but cordprnt.c) on +any platform that supports the collector. diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.darwin b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.darwin new file mode 100644 index 000000000..2727d0b15 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.darwin @@ -0,0 +1,144 @@ +Darwin/MacOSX Support - December 16, 2003 + +== Build Notes == + +Building can be done with autoconf as normal. If you want to build +a Universal library using autoconf, you need to disable dependency +tracking and specify your desired architectures in CFLAGS: + +CFLAGS="-arch ppc -arch i386 -arch x86_64" ./configure --disable-dependency-tracking + + +== Important Usage Notes == + +GC_init() MUST be called before calling any other GC functions. This +is necessary to properly register segments in dynamic libraries. This +call is required even if you code does not use dynamic libraries as the +dyld code handles registering all data segments. + +When your use of the garbage collector is confined to dylibs and you +cannot call GC_init() before your libraries' static initializers have +run and perhaps called GC_malloc(), create an initialization routine +for each library to call GC_init(): + +#include "gc.h" +extern "C" void my_library_init() { GC_init(); } + +Compile this code into a my_library_init.o, and link it into your +dylib. When you link the dylib, pass the -init argument with +_my_library_init (e.g. gcc -dynamiclib -o my_library.dylib a.o b.o c.o +my_library_init.o -init _my_library_init). This causes +my_library_init() to be called before any static initializers, and +will initialize the garbage collector properly. + +Note: It doesn't hurt to call GC_init() more than once, so it's best, +if you have an application or set of libraries that all use the +garbage collector, to create an initialization routine for each of +them that calls GC_init(). Better safe than sorry. + +The incremental collector is still a bit flaky on darwin. It seems to +work reliably with workarounds for a few possible bugs in place however +these workaround may not work correctly in all cases. There may also +be additional problems that I have not found. + +Thread-local GC allocation will not work with threads that are not +created using the GC-provided override of pthread_create(). Threads +created without the GC-provided pthread_create() do not have the +necessary data structures in the GC to store this data. + + +== Implementation Information == + +Darwin/MacOSX support is nearly complete. Thread support is reliable on +Darwin 6.x (MacOSX 10.2) and there have been reports of success on older +Darwin versions (MacOSX 10.1). Shared library support had also been +added and the gc can be run from a shared library. + +Thread support is implemented in terms of mach thread_suspend and +thread_resume calls. These provide a very clean interface to thread +suspension. This implementation doesn't rely on pthread_kill so the +code works on Darwin < 6.0 (MacOSX 10.1). All the code to stop and +start the world is located in darwin_stop_world.c. + +Since not all uses of the GC enable clients to override pthread_create() +before threads have been created, the code for stopping the world has +been rewritten to look for threads using Mach kernel calls. Each +thread identified in this way is suspended and resumed as above. In +addition, since Mach kernel threads do not contain pointers to their +stacks, a stack-walking function has been written to find the stack +limits. Given an initial stack pointer (for the current thread, a +pointer to a stack-allocated local variable will do; for a non-active +thread, we grab the value of register 1 (on PowerPC)), it +will walk the PPC Mach-O-ABI compliant stack chain until it reaches the +top of the stack. This appears to work correctly for GCC-compiled C, +C++, Objective-C, and Objective-C++ code, as well as for Java +programs that use JNI. If you run code that does not follow the stack +layout or stack pointer conventions laid out in the PPC Mach-O ABI, +then this will likely crash the garbage collector. + +The original incremental collector support unfortunately no longer works +on recent Darwin versions. It also relied on some undocumented kernel +structures. Mach, however, does have a very clean interface to exception +handing. The current implementation uses Mach's exception handling. + +Much thanks goes to Andrew Stone, Dietmar Planitzer, Andrew Begel, +Jeff Sturm, and Jesse Rosenstock for all their work on the +Darwin/OS X port. + +-Brian Alliet + +== gc_cpp.h usage == + +Replacement of operator new and delete is apparently not supported with +dynamic libraries. This means that applications using gc_cpp.h +(including the built-in test) will probably not work correctly with +the collector in a dynamic library, unless special care is taken. + +See +http://article.gmane.org/gmane.comp.programming.garbage-collection.boehmgc/1421 +for some details. + +- Hans Boehm (based on information from Andrew Begel) + + +== Older Information (Most of this no longer applies to the current code) == + +While the GC should work on MacOS X Server, MacOS X and Darwin, I only tested +it on MacOS X Server. +I've added a PPC assembly version of GC_push_regs(), thus the setjmp() hack is +no longer necessary. Incremental collection is supported via mprotect/signal. +The current solution isn't really optimal because the signal handler must decode +the faulting PPC machine instruction in order to find the correct heap address. +Further, it must poke around in the register state which the kernel saved away +in some obscure register state structure before it calls the signal handler - +needless to say the layout of this structure is no where documented. +Threads and dynamic libraries are not yet supported (adding dynamic library +support via the low-level dyld API shouldn't be that hard). + +The original MacOS X port was brought to you by Andrew Stone. + + +June, 1 2000 + +Dietmar Planitzer + +Note from Andrew Begel: + +One more fix to enable gc.a to link successfully into a shared library for +MacOS X. You have to add -fno-common to the CFLAGS in the Makefile. MacOSX +disallows common symbols in anything that eventually finds its way into a +shared library. (I don't completely understand why, but -fno-common seems to +work and doesn't mess up the garbage collector's functionality). + +Feb 26, 2003 + +Jeff Sturm and Jesse Rosenstock provided a patch that adds thread support. +GC_THREADS should be defined in the build and in clients. Real +dynamic library support is still missing, i.e. dynamic library data segments +are still not scanned. Code that stores pointers to the garbage collected +heap in statically allocated variables should not reside in a dynamic +library. This still doesn't appear to be 100% reliable. + +Mar 10, 2003 +Brian Alliet contributed dynamic library support for MacOSX. It could also +use more testing. diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.environment b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.environment new file mode 100644 index 000000000..c977388aa --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.environment @@ -0,0 +1,185 @@ +The garbage collector looks at a number of environment variables which are, +then, used to affect its operation. + +GC_INITIAL_HEAP_SIZE= - Initial heap size in bytes. May speed up + process start-up. Optionally, may be + specified with a multiplier ('k', 'M' or 'G') + suffix. + +GC_MAXIMUM_HEAP_SIZE= - Maximum collected heap size. Allows + a multiplier suffix. + +GC_LOOP_ON_ABORT - Causes the collector abort routine to enter a tight loop. + This may make it easier to debug, such a process, especially + for multi-threaded platforms that don't produce usable core + files, or if a core file would be too large. On some + platforms, this also causes SIGSEGV to be caught and + result in an infinite loop in a handler, allowing + similar debugging techniques. + +GC_PRINT_STATS - Turn on GC logging. Not functional with SMALL_CONFIG. + +GC_LOG_FILE - The name of the log file. Stderr by default. Not functional + with SMALL_CONFIG. + +GC_ONLY_LOG_TO_FILE - Turns off redirection of GC stdout and stderr to the log + file specified by GC_LOG_FILE. Has no effect unless + GC_LOG_FILE is set. Not functional with SMALL_CONFIG. + +GC_PRINT_VERBOSE_STATS - Turn on even more logging. Not functional with + SMALL_CONFIG. + +GC_DUMP_REGULARLY - Generate a GC debugging dump GC_dump() on startup + and during every collection. Very verbose. Useful + if you have a bug to report, but please include only the + last complete dump. + +GC_COLLECT_AT_MALLOC= - Override the default value specified by + GC_COLLECT_AT_MALLOC macro. Has no effect unless + GC is built with GC_COLLECT_AT_MALLOC defined. + +GC_BACKTRACES= - Generate n random back-traces (for heap profiling) after + each GC. Collector must have been built with + KEEP_BACK_PTRS. This won't generate useful output unless + most objects in the heap were allocated through debug + allocators. This is intended to be only a statistical + sample; individual traces may be erroneous due to + concurrent heap mutation. + +GC_PRINT_ADDRESS_MAP - Linux only. Dump /proc/self/maps, i.e. various address + maps for the process, to stderr on every GC. Useful for + mapping root addresses to source for deciphering leak + reports. + +GC_NPROCS= - Linux w/threads only. Explicitly sets the number of processors + that the GC should expect to use. Note that setting this to 1 + when multiple processors are available will preserve + correctness, but may lead to really horrible performance, + since the lock implementation will immediately yield without + first spinning. + +GC_MARKERS= - Only if compiled with PARALLEL_MARK. Set the number + of marker threads. This is normally set to the number of + processors. It is safer to adjust GC_MARKERS than GC_NPROCS, + since GC_MARKERS has no impact on the lock implementation. + +GC_NO_BLACKLIST_WARNING - Prevents the collector from issuing + warnings about allocations of very large blocks. + Deprecated. Use GC_LARGE_ALLOC_WARN_INTERVAL instead. + +GC_LARGE_ALLOC_WARN_INTERVAL= - Print every nth warning about very large + block allocations, starting with the nth one. Small values + of n are generally benign, in that a bounded number of + such warnings generally indicate at most a bounded leak. + For best results it should be set at 1 during testing. + Default is 5. Very large numbers effectively disable the + warning. + +GC_IGNORE_GCJ_INFO - Ignore the type descriptors implicitly supplied by + GC_gcj_malloc and friends. This is useful for debugging + descriptor generation problems, and possibly for + temporarily working around such problems. It forces a + fully conservative scan of all heap objects except + those known to be pointer-free, and may thus have other + adverse effects. + +GC_PRINT_BACK_HEIGHT - Print max length of chain through unreachable objects + ending in a reachable one. If this number remains + bounded, then the program is "GC robust". This ensures + that a fixed number of misidentified pointers can only + result in a bounded space leak. This currently only + works if debugging allocation is used throughout. + It increases GC space and time requirements appreciably. + This feature is still somewhat experimental, and requires + that the collector have been built with MAKE_BACK_GRAPH + defined. For details, see Boehm, "Bounding Space Usage + of Conservative Garbage Collectors", POPL 2001 + (http://www.hpl.hp.com/techreports/2001/HPL-2001-251.html). + +GC_RETRY_SIGNALS - Try to compensate for lost + thread suspend and restart signals (Pthreads only). + On by default for OSF1 (Tru64) or if the library is + sanitized, off otherwise. Since we've previously seen + similar issues on some other operating systems, it + was turned into a runtime flag to enable last-minute + work-arounds. "0" value means "do not retry signals". + +GC_USE_GETWRITEWATCH= - Only if MPROTECT_VDB and GWW_VDB are both defined + (Win32 only). Explicitly specify which strategy of + keeping track of dirtied pages should be used. + If n=0 then GetWriteWatch() is not used (falling back to + protecting pages and catching memory faults strategy) + else the collector tries to use GetWriteWatch-based + strategy (GWW_VDB) first if available. + +GC_DISABLE_INCREMENTAL - Ignore runtime requests to enable incremental GC. + Useful for debugging. + +The following turn on runtime flags that are also program settable. Checked +only during initialization. We expect that they will usually be set through +other means, but this may help with debugging and testing: + +GC_ENABLE_INCREMENTAL - Turn on incremental collection at startup. Note that, + depending on platform and collector configuration, this + may involve write protecting pieces of the heap to + track modifications. These pieces may include + pointer-free objects or not. Although this is intended + to be transparent, it may cause unintended system call + failures. Use with caution. + +GC_PAUSE_TIME_TARGET - Set the desired garbage collector pause time in msecs. + This only has an effect if incremental collection is + enabled. If a collection requires appreciably more time + than this, the client will be restarted, and the collector + will need to do additional work to compensate. The + special value "999999" indicates that pause time is + unlimited, and the incremental collector will behave + completely like a simple generational collector. If + the collector is configured for parallel marking, and + run on a multiprocessor, incremental collection should + only be used with unlimited pause time. + +GC_FULL_FREQUENCY - Set the desired number of partial collections between full + collections. Matters only if GC_incremental is set. + Not functional with SMALL_CONFIG. + +GC_FREE_SPACE_DIVISOR - Set GC_free_space_divisor to the indicated value. + Setting it to larger values decreases space consumption + and increases GC frequency. + +GC_UNMAP_THRESHOLD - Set the desired memory blocks unmapping threshold (the + number of sequential garbage collections for which + a candidate block for unmapping should remain free). The + special value "0" completely disables unmapping. + +GC_FORCE_UNMAP_ON_GCOLLECT - Turn "unmap as much as possible on explicit GC" + mode on (overrides the default value). Has no effect on + implicitly-initiated garbage collections. Has no effect if + memory unmapping is disabled (or not compiled in) or if the + unmapping threshold is 1. + +GC_FIND_LEAK - Turns on GC_find_leak and thus leak detection. Forces a + collection at program termination to detect leaks that would + otherwise occur after the last GC. + +GC_FINDLEAK_DELAY_FREE - Turns on deferred freeing of objects in the + leak-finding mode (see the corresponding macro + description for more information). + +GC_ABORT_ON_LEAK - Causes the application to be terminated once leaked or + smashed objects are found. + +GC_ALL_INTERIOR_POINTERS - Turns on GC_all_interior_pointers and thus interior + pointer recognition. + +GC_DONT_GC - Turns off garbage collection. Use cautiously. + +GC_USE_ENTIRE_HEAP - Set desired GC_use_entire_heap value at start-up. See + the similar macro description in README.macros. + +GC_TRACE=addr - Intended for collector debugging. Requires that the collector + have been built with ENABLE_TRACE defined. Causes the debugger + to log information about the tracing of address ranges + containing addr. Typically addr is the address that contains + a pointer to an object that mysteriously failed to get marked. + Addr must be specified as a hexadecimal integer. diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.ews4800 b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.ews4800 new file mode 100644 index 000000000..da4cff398 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.ews4800 @@ -0,0 +1,81 @@ +GC on EWS4800 +------------- + +1. About EWS4800 + + EWS4800 is a 32/64-bit workstation. + + Vendor: NEC Corporation + OS: UX/4800 R9.* - R13.* (SystemV R4.2) + CPU: R4000, R4400, R10000 (MIPS) + +2. Compiler + + 32-bit: + Use ANSI C compiler. + CC = /usr/abiccs/bin/cc + + 64-bit: + Use the 64-bit ANSI C compiler. + CC = /usr/ccs64/bin/cc + AR = /usr/ccs64/bin/ar + +3. ELF file format + *** Caution: The following information is empirical. *** + + 32-bit: + ELF file has an unique format. (See a.out(4) and end(3C).) + + &_start + : text segment + &etext + DATASTART + : data segment (initialized) + &edata + DATASTART2 + : data segment (uninitialized) + &end + + Here, DATASTART and DATASTART2 are macros of GC, and are defined as + the following equations. (See include/private/gcconfig.h.) + The algorithm for DATASTART is similar with the function + GC_SysVGetDataStart() in os_dep.c. + + DATASTART = ((&etext + 0x3ffff) & ~0x3ffff) + (&etext & 0xffff) + + Dynamically linked: + DATASTART2 = (&_gp + 0x8000 + 0x3ffff) & ~0x3ffff + + Statically linked: + DATASTART2 = &edata + + GC has to check addresses both between DATASTART and &edata, and + between DATASTART2 and &end. If a program accesses between &etext + and DATASTART, or between &edata and DATASTART2, the segmentation + error occurs and the program stops. + + If a program is statically linked, there is not a gap between + &edata and DATASTART2. The global symbol &_DYNAMIC_LINKING is used + for the detection. + + 64-bit: + ELF file has a simple format. (See end(3C).) + + _ftext + : text segment + _etext + _fdata = DATASTART + : data segment (initialized) + _edata + _fbss + : data segment (uninitialized) + _end = DATAEND + +-- +Hironori SAKAMOTO + + +When using the new "configure; make" build process, please +run configure with the --disable-shared option. "Make check" does not +yet pass with dynamic libraries. The reasons for that are not yet +understood. (HB, paraphrasing message from Hironori SAKAMOTO.) diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.hp b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.hp new file mode 100644 index 000000000..83708ea07 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.hp @@ -0,0 +1,18 @@ +Dynamic loading support requires that executables be linked with -ldld. +The alternative is to build the collector without defining DYNAMIC_LOADING +in gcconfig.h and ensuring that all garbage collectible objects are +accessible without considering statically allocated variables in dynamic +libraries. + +The collector should compile with either plain cc or cc -Ae. Cc -Aa +fails to define _HPUX_SOURCE and thus will not configure the collector +correctly. + +Incremental collection support was added recently, and should now work. + +In spite of past claims, pthread support under HP/UX 11 should now work. +Define GC_THREADS macro for the build. Incremental collection still does not +work in combination with it. + +The stack finding code can be confused by putenv calls before collector +initialization. Call GC_malloc or GC_init before any putenv calls. diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.linux b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.linux new file mode 100644 index 000000000..fa2cd22ef --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.linux @@ -0,0 +1,127 @@ +See README.alpha for Linux on DEC AXP info. + +This file applies mostly to Linux/Intel IA32. Ports to Linux on an M68K, +IA64, SPARC, MIPS, Alpha and PowerPC are integrated too. They should behave +similarly, except that the PowerPC port lacks incremental GC support, and +it is unknown to what extent the Linux threads code is functional. +See below for M68K specific notes. + +Incremental GC is generally supported. + +Dynamic libraries are supported on an ELF system. + +The collector appears to work reliably with Linux threads, but beware +of older versions of glibc and gdb. + +The garbage collector uses SIGPWR and SIGXCPU if it is used with +Linux threads. These should not be touched by the client program. + +To use threads, you need to abide by the following requirements: + +1) You need to use LinuxThreads or NPTL (which are included in libc6). + + The collector relies on some implementation details of the LinuxThreads + package. This code may not work on other + pthread implementations (in particular it will *not* work with + MIT pthreads). + +2) You must compile the collector with "-DGC_THREADS -D_REENTRANT" specified + in the Makefile. + +3a) Every file that makes thread calls should define GC_THREADS, and then + include gc.h. Gc.h redefines some of the pthread primitives as macros + which also provide the collector with information it requires. + +3b) A new alternative to (3a) is to build the collector and compile GC clients + with -DGC_USE_LD_WRAP, and to link the final program with + + (for ld) --wrap dlopen --wrap pthread_create \ + --wrap pthread_join --wrap pthread_detach \ + --wrap pthread_sigmask --wrap pthread_exit --wrap pthread_cancel + + (for gcc) -Wl,--wrap -Wl,dlopen -Wl,--wrap -Wl,pthread_create \ + -Wl,--wrap -Wl,pthread_join -Wl,--wrap -Wl,pthread_detach \ + -Wl,--wrap -Wl,pthread_sigmask -Wl,--wrap -Wl,pthread_exit \ + -Wl,--wrap -Wl,pthread_cancel + + In any case, _REENTRANT should be defined during compilation. + +4) Dlopen() disables collection during its execution. (It can't run + concurrently with the collector, since the collector looks at its + data structures. It can't acquire the allocator lock, since arbitrary + user startup code may run as part of dlopen().) Under unusual + conditions, this may cause unexpected heap growth. + +5) The combination of GC_THREADS, REDIRECT_MALLOC, and incremental + collection is probably not fully reliable, though it now seems to work + in simple cases. + +6) Thread local storage may not be viewed as part of the root set by the + collector. This probably depends on the linuxthreads version. For the + time being, any collectible memory referenced by thread local storage + should also be referenced from elsewhere, or be allocated as uncollectible. + (This is really a bug that should be fixed somehow. The current GC + version probably gets things right if there are not too many tls locations + and if dlopen is not used.) + + +M68K LINUX: +(From Richard Zidlicky) +The bad news is that it can crash every linux-m68k kernel on a 68040, +so an additional test is needed somewhere on startup. I have meanwhile +patches to correct the problem in 68040 buserror handler but it is not +yet in any standard kernel. + +Here is a simple test program to detect whether the kernel has the +problem. It could be run as a separate check in configure or tested +upon startup. If it fails (return !0) than mprotect can't be used +on that system. + +/* + * test for bug that may crash 68040 based Linux + */ + +#include +#include +#include +#include +#include + + +char *membase; +int pagesize=4096; +int pageshift=12; +int x_taken=0; + +int sighandler(int sig) +{ + mprotect(membase,pagesize,PROT_READ|PROT_WRITE); + x_taken=1; +} + +main() +{ + long l; + + signal(SIGSEGV,sighandler); + l=(long)mmap(NULL,pagesize,PROT_READ,MAP_PRIVATE | MAP_ANON,-1,0); + if (l==-1) + { + perror("mmap/malloc"); + abort(); + } + membase=(char*)l; + *(long*)(membase+sizeof(long))=123456789; + if (*(long*)(membase+sizeof(long)) != 123456789 ) + { + fprintf(stderr,"writeback failed !\n"); + exit(1); + } + if (!x_taken) + { + fprintf(stderr,"exception not taken !\n"); + exit(1); + } + fprintf(stderr,"vmtest Ok\n"); + exit(0); +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.macros b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.macros new file mode 100644 index 000000000..b16198b1c --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.macros @@ -0,0 +1,591 @@ +The collector uses a large amount of conditional compilation in order to +deal with platform dependencies. This violates a number of known coding +standards. On the other hand, it seems to be the only practical way to +support this many platforms without excessive code duplication. + +A few guidelines have mostly been followed in order to keep this manageable: + +1) #if and #ifdef directives are properly indented whenever easily possible. +All known C compilers allow whitespace between the "#" and the "if" to make +this possible. ANSI C also allows white space before the "#", though we +avoid that. It has the known disadvantages that it differs from the normal +GNU conventions, and that it makes patches larger than otherwise necessary. +In my opinion, it's still well worth it, for the same reason that we indent +ordinary "if" statements. + +2) Whenever possible, tests are performed on the macros defined in gcconfig.h +instead of directly testing platform-specific predefined macros. This makes +it relatively easy to adapt to new compilers with a different set of +predefined macros. Currently these macros generally identify platforms +instead of features. In many cases, this is a mistake. + +Many of the tested configuration macros are at least somewhat defined in +either include/private/gcconfig.h or in Makefile.direct. Here is an attempt +at documenting these macros: (Thanks to Walter Bright for suggesting +this. This is a work in progress) + +MACRO EXPLANATION +----- ----------- + +GC_DEBUG Tested by gc.h. Causes all-upper-case macros to + expand to calls to debug versions of collector routines. + +GC_NAMESPACE Tested by gc_cpp.h. Causes gc_cpp symbols to be defined + in "boehmgc" namespace. + +GC_DEBUG_REPLACEMENT Tested by gc.h. Causes GC_MALLOC/REALLOC() to be + defined as GC_debug_malloc/realloc_replacement(). + +GC_NO_THREAD_REDIRECTS Tested by gc.h. Prevents redirection of thread + creation routines etc. to GC_ versions. Requires the + programmer to explicitly handle thread registration. + +GC_NO_THREAD_DECLS Tested by gc.h. MS Windows only. Do not declare + Windows thread creation routines and do not include windows.h. + +GC_DONT_INCLUDE_WINDOWS_H Tested by gc.h. MS Windows only. Do not include + windows.h from gc.h (but Windows-specific thread creation + routines are declared). + +GC_UNDERSCORE_STDCALL Tested by gc.h. Explicitly prefix exported/imported + WINAPI (__stdcall) symbols with '_' (underscore). Could be + used with MinGW (for x86) compiler (in conjunction with + GC_DLL) to follow MS conventions for __stdcall symbols naming. + +_ENABLE_ARRAYNEW + #define'd by the Digital Mars C++ compiler when + operator new[] and delete[] are separately + overloadable. Used in gc_cpp.h. + +_DLL Tested by gc_config_macros.h. Defined by Visual C++ if runtime + dynamic libraries are in use. Used (only if none of GC_DLL, + GC_NOT_DLL, __GNUC__ are defined) to test whether + __declspec(dllimport) needs to be added to declarations + to support the case in which the collector is in a DLL. + +GC_DLL Defined by user if dynamic libraries are being built + or used. Also set by gc.h if _DLL is defined (except for + mingw) while GC_NOT_DLL and __GNUC__ are both undefined. + This is the macro that is tested internally to determine + whether the GC is in its own dynamic library. May need + to be set by clients before including gc.h. Note that + inside the GC implementation it indicates that the + collector is in its own dynamic library, should export + its symbols, etc. But in clients it indicates that the + GC resides in a different DLL, its entry points should + be referenced accordingly, and precautions may need to + be taken to properly deal with statically allocated + variables in the main program. Used for MS Windows. + Also used by GCC v4+ (only when the dynamic shared library + is being built) to hide internally used symbols. + +GC_NOT_DLL User-settable macro that overrides _DLL, e.g. if runtime + dynamic libraries are used, but the collector is in a static + library. Tested by gc_config_macros.h. + +GC_REQUIRE_WCSDUP Force GC to export GC_wcsdup() (the Unicode version + of GC_strdup); could be useful in the leak-finding mode. + + +These define arguments influence the collector configuration: + +FIND_LEAK Causes GC_find_leak to be initially set. This causes the + collector to assume that all inaccessible objects should have been + explicitly deallocated, and reports exceptions. Finalization and the test + program are not usable in this mode. + +GC_FINDLEAK_DELAY_FREE Turns on deferred freeing of objects in the + leak-finding mode letting the collector to detect alter-object-after-free + errors as well as detect leaked objects sooner (instead of only when program + terminates). Has no effect if SHORT_DBG_HDRS. + +GC_ABORT_ON_LEAK Causes the application to be terminated once leaked or + smashed (corrupted on use-after-free) objects are found (after printing the + information about that objects). + +SUNOS5SIGS Solaris-like signal handling. This is probably misnamed, + since it really doesn't guarantee much more than POSIX. Currently set only + for Solaris2.X, HPUX, and DRSNX. Should probably be set for some other + platforms. + +PCR Set if the collector is being built as part of the Xerox Portable + Common Runtime. + +IMPORTANT: Any of the _THREADS options must normally also be defined in + the client before including gc.h. This redefines thread primitives to + invoke the GC_ versions instead. Alternatively, linker-based symbol + interception can be used on a few platforms. + +GC_THREADS Should set the appropriate one of the below macros, + except GC_WIN32_PTHREADS, which must be set explicitly. Tested by gc.h. + +GC_SOLARIS_THREADS Enables support for Solaris pthreads. + Must also define _REENTRANT. Deprecated, use GC_THREADS instead. + +GC_IRIX_THREADS Enables support for Irix pthreads. See README.sgi. + Deprecated, use GC_THREADS instead. + +GC_HPUX_THREADS Enables support for HP/UX 11 pthreads. + Also requires _REENTRANT or _POSIX_C_SOURCE=199506L. See README.hp. + Deprecated, use GC_THREADS instead. + +GC_LINUX_THREADS Enables support for Xavier Leroy's Linux threads + or NPTL threads. See README.linux. _REENTRANT may also be required. + Deprecated, use GC_THREADS instead. + +GC_OSF1_THREADS Enables support for Tru64 pthreads. Deprecated, use + GC_THREADS instead. + +GC_FREEBSD_THREADS Enables support for FreeBSD pthreads. Appeared to run + into some underlying thread problems. Deprecated, use GC_THREADS instead. + +GC_NETBSD_THREADS Enables support for NetBSD pthreads. Deprecated, use + GC_THREADS instead. + +GC_OPENBSD_THREADS Enables support for OpenBSD pthreads. Deprecated, + use GC_THREADS instead. + +GC_DARWIN_THREADS Enables support for Mac OS X pthreads. Deprecated, + use GC_THREADS instead. + +GC_AIX_THREADS Enables support for IBM AIX threads. Deprecated, use + GC_THREADS instead. + +GC_DGUX386_THREADS Enables support for DB/UX on I386 threads. + See README.DGUX386. (Probably has not been tested recently.) Deprecated, + use GC_THREADS instead. + +GC_WIN32_THREADS Enables support for Win32 threads. That makes sense + for Makefile (and Makefile.direct) only under Cygwin or MinGW. Deprecated, + use GC_THREADS instead. + +GC_WIN32_PTHREADS Enables support for pthreads-win32 (or other + non-Cygwin pthreads library for Windows). This cannot be enabled + automatically by GC_THREADS, which would assume Win32 native threads. + +PTW32_STATIC_LIB Causes the static version of the Mingw pthreads + library to be used. Requires GC_WIN32_PTHREADS. + +GC_PTHREADS_PARAMARK Causes pthread-based parallel mark implementation + to be used even if GC_WIN32_PTHREADS is undefined. (Useful for WinCE.) + +ALL_INTERIOR_POINTERS Allows all pointers to the interior of objects to be + recognized. (See gc_priv.h for consequences.) Alternatively, + GC_all_interior_pointers can be set at process initialization time. + +SMALL_CONFIG Tries to tune the collector for small heap sizes, + usually causing it to use less space in such situations. Incremental + collection no longer works in this case. Also, removes some + statistic-printing code. Turns off some optimization algorithms (like data + prefetching in the mark routine). + +NO_CLOCK Do not use system clock. Disables some statistic printing. + +GC_DISABLE_INCREMENTAL Turn off the incremental collection support. + +NO_INCREMENTAL Causes the gctest program to not invoke the incremental + collector. This has no impact on the generated library, only on the test + program. (This is often useful for debugging failures unrelated to + incremental GC.) + +LARGE_CONFIG Tunes the collector for unusually large heaps. + Necessary for heaps larger than about 4 GiB on most (64-bit) machines. + Recommended for heaps larger than about 500 MiB. Not recommended for + embedded systems. Could be used in conjunction with SMALL_CONFIG to + generate smaller code (by disabling incremental collection support, + statistic printing and some optimization algorithms). + +DONT_ADD_BYTE_AT_END Meaningful only with ALL_INTERIOR_POINTERS or + GC_all_interior_pointers = 1. Normally ALL_INTERIOR_POINTERS + causes all objects to be padded so that pointers just past the end of + an object can be recognized. This can be expensive. (The padding + is normally more than one byte due to alignment constraints.) + DONT_ADD_BYTE_AT_END disables the padding. + +NO_EXECUTE_PERMISSION May cause some or all of the heap to not + have execute permission, i.e. it may be impossible to execute + code from the heap. Currently this only affects the incremental + collector on UNIX machines. It may greatly improve its performance, + since this may avoid some expensive cache synchronization. Alternatively, + GC_set_pages_executable can be called at the process initialization time. + +GC_NO_OPERATOR_NEW_ARRAY Declares that the C++ compiler does not + support the new syntax "operator new[]" for allocating and deleting arrays. + See gc_cpp.h for details. No effect on the C part of the collector. + This is defined implicitly in a few environments. Must also be defined + by clients that use gc_cpp.h. + +REDIRECT_MALLOC= Causes malloc to be defined as alias for X. + Unless the following macros are defined, realloc is also redirected + to GC_realloc, and free is redirected to GC_free. + Calloc and str[n]dup are redefined in terms of the new malloc. X should + be either GC_malloc or GC_malloc_uncollectable, or + GC_debug_malloc_replacement. (The latter invokes GC_debug_malloc + with dummy source location information, but still results in + properly remembered call stacks on Linux/X86 and Solaris/SPARC. + It requires that the following two macros also be used.) + The former is occasionally useful for working around leaks in code + you don't want to (or can't) look at. It may not work for + existing code, but it often does. Neither works on all platforms, + since some ports use malloc or calloc to obtain system memory. + (Probably works for UNIX, and Win32.) If you build with DBG_HDRS_ALL, + you should only use GC_debug_malloc_replacement as a malloc + replacement. + +REDIRECT_REALLOC= Causes GC_realloc to be redirected to X. + The canonical use is REDIRECT_REALLOC=GC_debug_realloc_replacement, + together with REDIRECT_MALLOC=GC_debug_malloc_replacement to + generate leak reports with call stacks for both malloc and realloc. + This also requires REDIRECT_FREE. + +REDIRECT_FREE= Causes free to be redirected to X. The canonical use + is REDIRECT_FREE=GC_debug_free. + +IGNORE_FREE Turns calls to free into a no-op. Only useful with + REDIRECT_MALLOC. + +NO_DEBUGGING Removes GC_dump and the debugging routines it calls. + Reduces code size slightly at the expense of debuggability. + +GC_DUMP_REGULARLY Generate regular debugging dumps. + +DEBUG_THREADS Turn on printing additional thread-support debugging + information. + +GC_COLLECT_AT_MALLOC= Force garbage collection at every + GC_malloc_* call with the size greater than the specified value. + (Might be useful for application debugging or in find-leak mode.) + +JAVA_FINALIZATION Makes it somewhat safer to finalize objects out of + order by specifying a nonstandard finalization mark procedure (see + finalize.c). Objects reachable from finalizable objects will be marked + in a separate post-pass, and hence their memory won't be reclaimed. + Not recommended unless you are implementing a language that specifies + these semantics. Since 5.0, determines only the initial value + of GC_java_finalization variable. + +FINALIZE_ON_DEMAND Causes finalizers to be run only in response + to explicit GC_invoke_finalizers() calls. + In 5.0 this became runtime adjustable, and this only determines the + initial value of GC_finalize_on_demand. + +GC_NO_FINALIZATION Exclude finalization support (for smaller code size). + +GC_TOGGLE_REFS_NOT_NEEDED Exclude toggle-refs support. + +GC_ATOMIC_UNCOLLECTABLE Includes code for GC_malloc_atomic_uncollectable. + This is useful if either the vendor malloc implementation is poor, + or if REDIRECT_MALLOC is used. + +MARK_BIT_PER_GRANULE Requests that a mark bit (or often byte) + be allocated for each allocation granule, as opposed to each object. + This often improves speed, possibly at some cost in space and/or + cache footprint. Normally it is best to let this decision be + made automatically depending on platform. + +MARK_BIT_PER_OBJ Requests that a mark bit be allocated for each + object instead of allocation granule. The opposite of + MARK_BIT_PER_GRANULE. + +HBLKSIZE= Explicitly sets the heap block size (where ddd is a power of + 2 between 512 and 16384). Each heap block is devoted to a single size and + kind of object. For the incremental collector it makes sense to match + the most likely page size. Otherwise large values result in more + fragmentation, but generally better performance for large heaps. + +USE_MMAP Use MMAP instead of sbrk to get new memory. + Works for Linux, FreeBSD, Cygwin, Solaris and Irix. + +USE_MUNMAP Causes memory to be returned to the OS under the right + circumstances. This currently disables VM-based incremental collection + (except for Win32 with GetWriteWatch() available). + Works under some Unix, Linux and Windows versions. + Requires USE_MMAP except for Windows. + +USE_WINALLOC (Cygwin only) Use Win32 VirtualAlloc (instead of sbrk or mmap) + to get new memory. Useful if memory unmapping (USE_MUNMAP) is enabled. + +MUNMAP_THRESHOLD= Set the desired memory blocks unmapping + threshold (the number of sequential garbage collections for which + a candidate block for unmapping should remain free). + +GC_FORCE_UNMAP_ON_GCOLLECT Set "unmap as much as possible on explicit GC" + mode on by default. The mode could be changed at run-time. Has no effect + unless unmapping is turned on. Has no effect on implicitly-initiated + garbage collections. + +PRINT_BLACK_LIST Whenever a black list entry is added, i.e. whenever + the garbage collector detects a value that looks almost, but not quite, + like a pointer, print both the address containing the value, and the + value of the near-bogus-pointer. Can be used to identify regions of + memory that are likely to contribute misidentified pointers. + +KEEP_BACK_PTRS Add code to save back pointers in debugging headers + for objects allocated with the debugging allocator. If all objects + through GC_MALLOC with GC_DEBUG defined, this allows the client + to determine how particular or randomly chosen objects are reachable + for debugging/profiling purposes. The gc_backptr.h interface is + implemented only if this is defined. + +GC_ASSERTIONS Enable some internal GC assertion checking. Currently + this facility is only used in a few places. It is intended primarily + for debugging of the garbage collector itself, but could also... + +DBG_HDRS_ALL Make sure that all objects have debug headers. Increases + the reliability (from 99.9999% to 100% mod. bugs) of some of the debugging + code (especially KEEP_BACK_PTRS). Makes SHORT_DBG_HDRS possible. + Assumes that all client allocation is done through debugging allocators. + +SHORT_DBG_HDRS Assume that all objects have debug headers. Shorten + the headers to minimize object size, at the expense of checking for + writes past the end of an object. This is intended for environments + in which most client code is written in a "safe" language, such as + Scheme or Java. Assumes that all client allocation is done using + the GC_debug_ functions, or through the macros that expand to these, + or by redirecting malloc to GC_debug_malloc_replacement. + (Also eliminates the field for the requested object size.) + Occasionally could be useful for debugging of client code. Slows down the + collector somewhat, but not drastically. + +SAVE_CALL_COUNT= Set the number of call frames saved with objects + allocated through the debugging interface. Affects the amount of + information generated in leak reports. Only matters on platforms + on which we can quickly generate call stacks, currently Linux/(X86 & SPARC) + and Solaris/SPARC and platforms that provide execinfo.h. + Default is zero. On X86, client + code should NOT be compiled with -fomit-frame-pointer. + +SAVE_CALL_NARGS= Set the number of functions arguments to be saved + with each call frame. Default is zero. Ignored if we don't know how to + retrieve arguments on the platform. + +CHECKSUMS Reports on erroneously clear dirty bits (at a substantial + performance cost). Use only for debugging of the incremental collector. + Not compatible with USE_MUNMAP or threads. + +GC_GCJ_SUPPORT Includes support for gcj (and possibly other systems + that include a pointer to a type descriptor in each allocated object). + +USE_I686_PREFETCH Causes the collector to issue Pentium III style + prefetch instructions. No effect except on X86 Linux platforms. + Assumes a very recent gcc-compatible compiler and assembler. + (Gas prefetcht0 support was added around May 1999.) + Empirically the code appears to still run correctly on Pentium II + processors, though with no performance benefit. May not run on other + X86 processors? In some cases this improves performance by + 15% or so. + +USE_3DNOW_PREFETCH Causes the collector to issue AMD 3DNow style + prefetch instructions. Same restrictions as USE_I686_PREFETCH. + Minimally tested. Didn't appear to be an obvious win on a K6-2/500. + +USE_PPC_PREFETCH Causes the collector to issue PowerPC style + prefetch instructions. No effect except on PowerPC OS X platforms. + Performance impact untested. + +GC_USE_LD_WRAP In combination with the old flags listed in README.linux + causes the collector some system and pthread calls in a more transparent + fashion than the usual macro-based approach. Requires GNU ld, and + currently probably works only with Linux. + +GC_USE_DLOPEN_WRAP Causes the collector to redefine malloc and + intercepted pthread routines with their real names, and causes it to use + dlopen and dlsym to refer to the original versions. This makes it possible + to build an LD_PRELOADable malloc replacement library. + +THREAD_LOCAL_ALLOC Defines GC_malloc(), GC_malloc_atomic() and + GC_gcj_malloc() to use a per-thread set of free-lists. These then allocate + in a way that usually does not involve acquisition of a global lock. + Recommended for multiprocessors. Requires explicit GC_INIT() call, unless + REDIRECT_MALLOC is defined and GC_malloc is used first. + +USE_COMPILER_TLS Causes thread local allocation to use + the compiler-supported "__thread" thread-local variables. This is the + default in HP/UX. It may help performance on recent Linux installations. + (It failed for me on RedHat 8, but appears to work on RedHat 9.) + +GC_ATTR_TLS_FAST Use specific attributes for GC_thread_key like + __attribute__((tls_model("local-exec"))). + +PARALLEL_MARK Allows the marker to run in multiple threads. Recommended + for multiprocessors. + +GC_BUILTIN_ATOMIC Use C11 (GCC) atomic intrinsics instead of + libatomic_ops primitives. + +GC_ALWAYS_MULTITHREADED Force multi-threaded mode at GC initialization. + (Turns GC_allow_register_threads into a no-op routine.) + +GC_ENABLE_SUSPEND_THREAD (Linux only) Turn on thread suspend/resume API +support. + +GC_WINMAIN_REDIRECT (Win32 only) Redirect (rename) an application + WinMain to GC_WinMain; implement the "real" WinMain which starts a new + thread to call GC_WinMain after initializing the GC. Useful for WinCE. + Incompatible with GC_DLL. + +GC_REGISTER_MEM_PRIVATE (Win32 only) Force to register MEM_PRIVATE R/W + sections as data roots. Might be needed for some WinCE 6.0+ custom builds. + (May result in numerous "Data Abort" messages logged to WinCE debugging + console.) Incompatible with GCC toolchains for WinCE. + +NO_GETENV Prevents the collector from looking at environment variables. + These may otherwise alter its configuration, or turn off GC altogether. + I don't know of a reason to disable this, except possibly if the resulting + process runs as a privileged user. (This is on by default for WinCE.) + +EMPTY_GETENV_RESULTS Define to workaround a reputed Wine bug in getenv + (getenv() may return an empty string instead of NULL for a missing entry). + +GC_READ_ENV_FILE (Win32 only) Read environment variables from the GC "env" + file (named as the program name plus ".gc.env" extension). Useful for WinCE + targets (which have no getenv()). In the file, every variable is specified + in a separate line and the format is as "=" (without spaces). + A comment line may start with any character except for the Latin letters, + the digits and the underscore ('_'). The file encoding is Latin-1. + +USE_GLOBAL_ALLOC (Win32 only) Use GlobalAlloc() instead of VirtualAlloc() + to allocate the heap. May be needed to work around a Windows NT/2000 issue. + Incompatible with USE_MUNMAP. See README.win32 for details. + +MAKE_BACK_GRAPH Enable GC_PRINT_BACK_HEIGHT environment variable. + See README.environment for details. Experimental. Limited platform + support. Implies DBG_HDRS_ALL. All allocation should be done using + the debug interface. + +GC_PRINT_BACK_HEIGHT Permanently turn on back-height printing mode + (useful when NO_GETENV). See the similar environment variable description + in README.environment. Requires MAKE_BACK_GRAPH defined. + +HANDLE_FORK (Unix and Cygwin only) Attempt by default to make GC_malloc() + work in a child process fork()'ed from a multi-threaded parent. Not fully + POSIX-compliant and could be disabled at runtime (before GC_INIT). + +TEST_WITH_SYSTEM_MALLOC Causes gctest to allocate (and leak) large + chunks of memory with the standard system malloc. This will cause the root + set and collected heap to grow significantly if malloc'ed memory is somehow + getting traced by the collector. This has no impact on the generated + library; it only affects the test. + +POINTER_MASK=<0x...> Causes candidate pointers to be AND'ed with the given + mask before being considered. If either this or the following macro is + defined, it will be assumed that all pointers stored in the heap need to be + processed this way. Stack and register pointers will be considered both + with and without processing. These macros are normally needed only to + support systems that use high-order pointer tags. EXPERIMENTAL. + +POINTER_SHIFT= Causes the collector to left shift candidate pointers + by the indicated amount before trying to interpret them. Applied after + POINTER_MASK. EXPERIMENTAL. See also the preceding macro. + +ENABLE_TRACE Enables the GC_TRACE=addr environment setting to do its job. + By default this is not supported in order to keep the marker as fast as + possible. + +DARWIN_DONT_PARSE_STACK Causes the Darwin port to discover thread + stack bounds in the same way as other pthread ports, without trying to + walk the frames on the stack. This is recommended only as a fall-back for + applications that don't support proper stack unwinding. + +GC_NO_THREADS_DISCOVERY (Darwin and Win32+DLL only) Exclude DllMain-based + (on Windows) and task-threads-based (on Darwin) thread registration support. + +GC_INSIDE_DLL (Win32 only) Enable DllMain-based approach of threads + registering even in case GC_DLL is not defined. + +GC_DISCOVER_TASK_THREADS (Darwin and Win32+DLL only) Compile the collector + with the implicitly turned on task-threads-based (on Darwin) or + DllMain-based (on Windows) approach of threads registering. Only for + compatibility and for the case when it is not possible to call + GC_use_threads_discovery() early (before other GC calls). + +USE_PROC_FOR_LIBRARIES Causes the Linux collector to treat writable + memory mappings (as reported by /proc) as roots, if it doesn't have + other information about them. It no longer traverses dynamic loader + data structures to find dynamic library static data. This may be + required for applications that store pointers in mmapped segments without + informing the collector. But it typically performs poorly, especially + since it will scan inactive but cached NPTL thread stacks completely. + +IGNORE_DYNAMIC_LOADING Don't define DYNAMIC_LOADING even if supported by the + platform (that is, build the collector with disabled tracing of dynamic + library data roots). + +NO_PROC_STAT Causes the collector to avoid relying on Linux + "/proc/self/stat". + +NO_GETCONTEXT Causes the collector to not assume the existence of the + getcontext() function on linux-like platforms. This currently happens + implicitly on Darwin, Hurd, or ARM or MIPS hardware. It is explicitly + needed for some old versions of FreeBSD. + +STATIC=static Causes various GC_ symbols that could logically be declared + static to be declared (this is the default if NO_DEBUGGING is specified). + Reduces the number of visible symbols (letting the optimizer do its work + better), which is probably cleaner, but may make some kinds of debugging + and profiling harder. + +GC_DLL Build dynamic-link library (or dynamic shared object). For Unix this + causes the exported symbols to have 'default' visibility (ignored unless + GCC v4+) and the internal ones to have 'hidden' visibility. + +DONT_USE_USER32_DLL (Win32 only) Don't use "user32" DLL import library + (containing MessageBox() entry); useful for a static GC library. + +GC_PREFER_MPROTECT_VDB Choose MPROTECT_VDB manually in case of multiple + virtual dirty bit strategies are implemented (at present useful on Win32 and + Solaris to force MPROTECT_VDB strategy instead of the default GWW_VDB or + PROC_VDB ones). + +GC_IGNORE_GCJ_INFO Disable GCJ-style type information (useful for + debugging on WinCE). + +GC_PRINT_VERBOSE_STATS Permanently turn on verbose logging (useful for + debugging and profiling on WinCE). + +GC_ONLY_LOG_TO_FILE Don't redirect GC stdout and stderr to the log file + specified by GC_LOG_FILE environment variable. Has effect only when the + variable is set (to anything other than "0"). + +GC_ANDROID_LOG (Android only) Output error/debug information to Android log. + +GC_DONT_EXPAND Don't expand the heap unless explicitly requested or forced to. + +GC_USE_ENTIRE_HEAP Causes the non-incremental collector to use the + entire heap before collecting. This sometimes results in more large block + fragmentation, since very large blocks will tend to get broken up during + each GC cycle. It is likely to result in a larger working set, but lower + collection frequencies, and hence fewer instructions executed in the + collector. This macro controls only the default GC_use_entire_heap value. + +GC_INITIAL_HEAP_SIZE= Set the desired default initial heap size + in bytes. + +GC_FREE_SPACE_DIVISOR= Set alternate default GC_free_space_divisor + value. + +GC_TIME_LIMIT= Set alternate default GC_time_limit value + (setting this to GC_TIME_UNLIMITED will essentially disable incremental + collection while leaving generational collection enabled). + +GC_FULL_FREQ= Set alternate default number of partial collections + between full collections (matters only if incremental collection is on). + +NO_CANCEL_SAFE (Posix platforms with threads only) Don't bother trying + to make the collector safe for thread cancellation; cancellation is not + used. (Note that if cancellation is used anyway, threads may end up + getting canceled in unexpected places.) Even without this option, + PTHREAD_CANCEL_ASYNCHRONOUS is never safe with the collector. (We could + argue about its safety without the collector.) + +UNICODE (Win32 only) Use the Unicode variant ('W') of the Win32 API instead + of ANSI/ASCII one ('A'). Useful for WinCE. + +HOST_ANDROID (or __ANDROID__) Compile for Android NDK platform. + +SN_TARGET_PS3 Compile for Sony PS/3. + +USE_GET_STACKBASE_FOR_MAIN (Linux only) Use pthread_attr_getstack() instead + of __libc_stack_end (or instead of any hard-coded value) for getting the + primordial thread stack base (useful if the client modifies the program's + address space). diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.rs6000 b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.rs6000 new file mode 100644 index 000000000..f5630b20a --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.rs6000 @@ -0,0 +1,9 @@ +We have so far failed to find a good way to determine the stack base. +It is highly recommended that GC_stackbottom be set explicitly on program +startup. The supplied value sometimes causes failure under AIX 4.1, though +it appears to work under 3.X. HEURISTIC2 seems to work under 4.1, but +involves a substantial performance penalty, and will fail if there is +no limit on stack size. + +There is no thread support. (I assume recent versions of AIX provide +pthreads? I no longer have access to a machine ...) diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.sgi b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.sgi new file mode 100644 index 000000000..e4230336a --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.sgi @@ -0,0 +1,40 @@ +Performance of the incremental collector can be greatly enhanced with +-DNO_EXECUTE_PERMISSION. + +The collector should run with all of the -32, -n32 and -64 ABIs. Remember to +define the AS macro in the Makefile to be "as -64", or "as -n32". + +If you use -DREDIRECT_MALLOC=GC_malloc with C++ code, your code should make +at least one explicit call to malloc instead of new to ensure that the proper +version of malloc is linked in. + +Sproc threads are not supported in this version, though there may exist other +ports. + +Pthreads support is provided. This requires that: + +1) You compile the collector with -DGC_THREADS specified in the Makefile. + +2) You have the latest pthreads patches installed. + +(Though the collector makes only documented pthread calls, +it relies on signal/threads interactions working just right in ways +that are not required by the standard. It is unlikely that this code +will run on other pthreads platforms. But please tell me if it does.) + +3) Every file that makes thread calls should define GC_THREADS and then +include gc.h. Gc.h redefines some of the pthread primitives as macros which +also provide the collector with information it requires. + +4) pthread_cond_wait and pthread_cond_timedwait should be prepared for +premature wakeups. (I believe the pthreads and related standards require this +anyway. Irix pthreads often terminate a wait if a signal arrives. +The garbage collector uses signals to stop threads.) + +5) It is expensive to stop a thread waiting in IO at the time the request is +initiated. Applications with many such threads may not exhibit acceptable +performance with the collector. (Increasing the heap size may help.) + +6) The collector should not be compiled with -DREDIRECT_MALLOC. This +confuses some library calls made by the pthreads implementation, which +expect the standard malloc. diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.solaris2 b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.solaris2 new file mode 100644 index 000000000..b150f9c27 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.solaris2 @@ -0,0 +1,70 @@ +The collector supports both incremental collection and threads under +Solaris 2. The incremental collector normally retrieves page dirty information +through the appropriate /proc calls. But it can also be configured +(by defining MPROTECT_VDB instead of PROC_VDB in gcconfig.h) to use mprotect +and signals. This may result in shorter pause times, but it is no longer +safe to issue arbitrary system calls that write to the heap. + +Under other UNIX versions, +the collector normally obtains memory through sbrk. There is some reason +to expect that this is not safe if the client program also calls the system +malloc, or especially realloc. The sbrk man page strongly suggests this is +not safe: "Many library routines use malloc() internally, so use brk() +and sbrk() only when you know that malloc() definitely will not be used by +any library routine." This doesn't make a lot of sense to me, since there +seems to be no documentation as to which routines can transitively call malloc. +Nonetheless, under Solaris2, the collector now allocates +memory using mmap by default. (It defines USE_MMAP in gcconfig.h.) +You may want to reverse this decisions if you use -DREDIRECT_MALLOC=... + +Note: +Before you run "make check", you need to set your LD_LIBRARY_PATH correctly +(e.g., to "/usr/local/lib") so that tests can find the shared library +libgcc_s.so.1. Alternatively, you can configure with --disable-shared. + +SOLARIS THREADS: + +Unless --disable-threads option is given, threads support is on by default in +configure. This causes the collector to be compiled with -D GC_THREADS +ensuring thread safety. This assumes use of the pthread_ interface; old-style +Solaris threads are no longer supported. +Thread-local allocation is now on by default. Parallel marking is on by +default starting from GC v7.3 but it could be disabled manually +by configure --disable-parallel-mark option. + +It is also essential that gc.h be included in files that call pthread_create, +pthread_join, pthread_detach, or dlopen. gc.h macro defines these to also do +GC bookkeeping, etc. gc.h must be included with one or both of these macros +defined, otherwise these replacements are not visible. A collector built in +this way way only be used by programs that are linked with the threads library. + +Since 5.0 alpha5, dlopen disables collection temporarily, +unless USE_PROC_FOR_LIBRARIES is defined. In some unlikely cases, this +can result in unpleasant heap growth. But it seems better than the +race/deadlock issues we had before. + +If threads are used on an X86 processor with malloc redirected to +GC_malloc, it is necessary to call GC_INIT explicitly before forking the +first thread. (This avoids a deadlock arising from calling GC_thr_init +with the allocation lock held.) + +It appears that there is a problem in using gc_cpp.h in conjunction with +Solaris threads and Sun's C++ runtime. Apparently the overloaded new operator +is invoked by some iostream initialization code before threads are correctly +initialized. As a result, call to thr_self() in garbage collector +initialization SEGV faults. Currently the only known workaround is to not +invoke the garbage collector from a user defined global operator new, or to +have it invoke the garbage-collector's allocators only after main has started. +(Note that the latter requires a moderately expensive test in operator +delete.) + +I encountered "symbol : offset .... is non-aligned" errors. These +appear to be traceable to the use of the GNU assembler with the Sun linker. +The former appears to generate a relocation not understood by the latter. +The fix appears to be to use a consistent tool chain. (As a non-Solaris-expert +my solution involved hacking the libtool script, but I'm sure you can +do something less ugly.) + +Hans-J. Boehm +(The above contains my personal opinions, which are probably not shared +by anyone else.) diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.symbian b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.symbian new file mode 100644 index 000000000..50658c317 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.symbian @@ -0,0 +1,13 @@ +Instructions for Symbian: +1. base version: libgc 7.1 +2. Build: use libgc.mmp +3. Limitations +3.1.No multi-threaded support + +3.2. Be careful with limitation that emulator introduces: Static roots are not +dynamically accessible (there are Symbian APIs for this purpose but are just +stubs, returning irrelevant values). +Consequently, on emulator, you can only use dlls or exe, and retrieve static +roots by calling global_init_static_root per dll (or exe). +On target, only libs are supported, because static roots are retrieved by +linker flags, by calling global_init_static_root in main exe. diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.uts b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.uts new file mode 100644 index 000000000..6be49667d --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.uts @@ -0,0 +1,2 @@ +Alistair Crooks supplied the port. He used Lexa C version 2.1.3 with +-Xa to compile. diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.win32 b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.win32 new file mode 100644 index 000000000..3ff700074 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.win32 @@ -0,0 +1,212 @@ +The collector has at various times been compiled under Windows 95 & later, NT, +and XP, with the original Microsoft SDK, with Visual C++ 2.0, 4.0, and 6, with +the GNU win32 tools, with Borland 4.5, with Watcom C, and recently +with the Digital Mars compiler. It is likely that some of these have been +broken in the meantime. Patches are appreciated. + +For historical reasons, +the collector test program "gctest" is linked as a GUI application, +but does not open any windows. Its output normally appears in the file +"gctest.gc.log". It may be started from the file manager. The hour glass +cursor may appear as long as it's running. If it is started from the +command line, it will usually run in the background. Wait a few +minutes (a few seconds on a modern machine) before you check the output. +You should see either a failure indication or a "Collector appears to +work" message. + +A toy editor (cord/de.exe) based on cords (heavyweight +strings represented as trees) has been ported and is included. +It runs fine under either win32 or win32S. It serves as an example +of a true Windows application, except that it was written by a +nonexpert Windows programmer. (There are some peculiarities +in the way files are displayed. The is displayed explicitly +for standard DOS text files. As in the UNIX version, control +characters are displayed explicitly, but in this case as red text. +This may be suboptimal for some tastes and/or sets of default +window colors.) + +In general -DREDIRECT_MALLOC is unlikely to work unless the +application is completely statically linked. + +The collector normally allocates memory from the OS with VirtualAlloc. +This appears to cause problems under Windows NT and Windows 2000 (but +not Windows 95/98) if the memory is later passed to CreateDIBitmap. +To work around this problem, build the collector with -DUSE_GLOBAL_ALLOC. +This is currently incompatible with -DUSE_MUNMAP. (Thanks to Jonathan +Clark for tracking this down. There's some chance this may be fixed +since we now separate heap sections with an unused page.) + +[Threads and incremental collection are discussed near the end, below.] + +Microsoft Tools +--------------- +For Microsoft development tools, rename NT_MAKEFILE as +MAKEFILE. (Make sure that the CPU environment variable is defined +to be i386.) In order to use the gc_cpp.h C++ interface, all +client code should include gc_cpp.h. + +[See above for gctest.] + +If you would prefer a VC++ .NET project file, ask Hans Boehm. One has +been contributed, but it seems to contain some absolute paths etc., so +it can presumably only be a starting point, and is not in the standard +distribution. It is unclear (to me, Hans Boehm) whether it is feasible to +change that. + +Clients may need to define GC_NOT_DLL before including gc.h, if the +collector was built as a static library. + +GNU Tools +--------- +The collector should be buildable under Cygwin with the +"./configure; make check" machinery. + +MinGW builds (including for x86_64) are available both directly (on a Windows +host) and via cross-compilation, e.g. +"./configure --host=i686-pc-mingw32; make check" + +To build the collector as a DLL, pass "--enable-shared --disable-static" to +configure (this will instruct make to compile with -D GC_DLL). + +Parallel marker is enabled by default; it could be disabled by +"--disable-parallel-mark" option. + +Memory unmapping could be turned off by "--disable-munmap" option. + +Borland Tools +------------- +[Rarely tested.] +For Borland tools, use BCC_MAKEFILE. Note that +Borland's compiler defaults to 1 byte alignment in structures (-a1), +whereas Visual C++ appears to default to 8 byte alignment (/Zp8). +The garbage collector in its default configuration EXPECTS AT +LEAST 4 BYTE ALIGNMENT. Thus the BORLAND DEFAULT MUST +BE OVERRIDDEN. (In my opinion, it should usually be anyway. +I expect that -a1 introduces major performance penalties on a +486 or Pentium.) Note that this changes structure layouts. (As a last +resort, gcconfig.h can be changed to allow 1 byte alignment. But +this has significant negative performance implications.) +The Makefile is set up to assume Borland 4.5. If you have another +version, change the line near the top. + +Digital Mars compiler +--------------------- + +Same as MS Visual C++ but might require +-DAO_OLD_STYLE_INTERLOCKED_COMPARE_EXCHANGE option to compile with the +parallel marker enabled. + +Watcom compiler +--------------- + +Ivan V. Demakov's README for the Watcom port: + +The collector has been compiled with Watcom C 10.6 and 11.0. +It runs under win32, win32s, and even under msdos with dos4gw +dos-extender. It should also run under OS/2, though this isn't +tested. Under win32 the collector can be built either as dll +or as static library. + +Note that all compilations were done under Windows 95 or NT. +For unknown reason compiling under Windows 3.11 for NT (one +attempt has been made) leads to broken executables. + +Incremental collection is not supported. + +cord is not ported. + +Before compiling you may need to edit WCC_MAKEFILE to set target +platform, library type (dynamic or static), calling conventions, and +optimization options. + +To compile the collector and testing programs use the command: + wmake -f WCC_MAKEFILE + +All programs using gc should be compiled with 4-byte alignment. +For further explanations on this see comments about Borland. + +If the gc is compiled as dll, the macro "GC_DLL" should be defined before +including "gc.h" (for example, with -DGC_DLL compiler option). It's +important, otherwise resulting programs will not run. + + +Special note for OpenWatcom users: the C (unlike the C++) compiler (of the +latest stable release, not sure for older ones) doesn't force pointer global +variables (i.e. not struct fields, not sure for locals) to be aligned unless +optimizing for speed (e.g., "-ot" option is set); the "-zp" option (or align +pragma) only controls alignment for structs; I don't know whether it's a bug or +a feature (see an old report of same kind - +http://bugzilla.openwatcom.org/show_bug.cgi?id=664), so You are warned. + + +Incremental Collection +---------------------- +There is some support for incremental collection. By default, the +collector chooses between explicit page protection, and GetWriteWatch-based +write tracking automatically, depending on the platform. + +The former is slow and interacts poorly with a debugger. +Pages are protected. Protection faults are caught by a handler +installed at the bottom of the handler +stack. Whenever possible, I recommend adding a call to +GC_enable_incremental at the last possible moment, after most +debugging is complete. No system +calls are wrapped by the collector itself. It may be necessary +to wrap ReadFile calls that use a buffer in the heap, so that the +call does not encounter a protection fault while it's running. +(As usual, none of this is an issue unless GC_enable_incremental +is called.) + +Note that incremental collection is disabled with -DSMALL_CONFIG. + +Threads +------- + +This version of the collector by default handles threads similarly +to other platforms. James Clark's code which tracks threads attached +to the collector DLL still exists, but requires that both +- the collector is built in a DLL with GC_DLL defined, and +- GC_use_threads_discovery() is called before GC initialization, which + in turn must happen before creating additional threads. +We generally recommend avoiding this if possible, since it seems to +be less than 100% reliable. + +Use gc.mak instead of NT_MAKEFILE to build a version that supports +both kinds of thread tracking. To build the garbage collector +test with VC++ from the command line, use + +nmake /F ".\gc.mak" CFG="gctest - Win32 Release" + +This requires that the subdirectory gctest\Release exist. +The test program and DLL will reside in the Release directory. + +This version currently supports incremental collection only if it is +enabled before any additional threads are created. + +Since 6.3alpha2, threads are also better supported in static library builds +with Microsoft tools (e.g., NT_MAKEFILE) and with the GNU +tools. The collector must be built with GC_THREADS defined (this is the +default in NT_MAKEFILE, ./configure and CMakeLists.txt). + +For the normal, non-dll-based thread tracking to work properly, +threads should be created with GC_CreateThread or GC_beginthreadex, +and exit normally or call GC_endthreadex or GC_ExitThread. (For Cygwin, the +standard pthread_create/exit calls could be used instead.) As in the pthread +case, including gc.h will redefine CreateThread, _beginthreadex, +_endthreadex, and ExitThread to call the GC_ versions instead. + +Note that, as usual, GC_CreateThread tends to introduce resource leaks (in the +C runtime) that are avoided by GC_beginthreadex. There is currently no +equivalent of _beginthread, and it should not be used. + +GC_INIT should be called from the main thread before other GC calls. + +We strongly advise against using the TerminateThread() win32 API call, +especially with the garbage collector. Any use is likely to provoke a +crash in the GC, since it makes it impossible for the collector to +correctly track threads. + +To build the collector for MinGW pthreads-win32 (or other non-Cygwin pthreads +implementation for Windows), use Makefile.direct and explicitly set +GC_WIN32_PTHREADS (or pass --enable-threads=pthreads to configure). +Use -DPTW32_STATIC_LIB for the static threads library. diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.win64 b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.win64 new file mode 100644 index 000000000..82b9456c6 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/README.win64 @@ -0,0 +1,27 @@ +64-bit Windows on AMD64/Intel EM64T is somewhat supported in the 7.0 +and later release. A collector can be built with Microsoft Visual C++ 2005 +or with mingw-w64 gcc. +More testing would clearly be helpful. + +NT_MAKEFILE has been used in this environment. Uncomment the corresponding +definitions of CPU and CVTRES_CPU variables (commenting out the ones for X86), +and then type "nmake -f NT_MAKEFILE" in a Visual C++ command line window to +build the dynamic library with threads support and the usual test programs. +To verify that the collector is at least somewhat functional, run gctest.exe. +This should create gctest.gc.log after a few seconds. + +Test_cpp.exe might not run correctly in case of dynamic GC linking. (It seems +that we're getting wrong instances of operator new/delete in some cases.) + +This process is completely analogous to NT_MAKEFILE usage +for the 32-bit library version. + +A similar procedure using NT_MAKEFILE should be usable to +build the static library (see comments for CFLAGS_SPECIFIC and LINK_GC +variables in NT_MAKEFILE). + +Note that some warnings have been explicitly turned off in the makefile. + +VC++ note: to suppress warnings -D_CRT_SECURE_NO_DEPRECATE is used. + +gcc note: -fno-strict-aliasing should be used if optimizing. diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/debugging.md b/unity-2019.4.24f1-mbe/external/bdwgc/doc/debugging.md new file mode 100644 index 000000000..286268077 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/debugging.md @@ -0,0 +1,277 @@ +# Debugging Garbage Collector Related Problems + +This page contains some hints on debugging issues specific to the +Boehm-Demers-Weiser conservative garbage collector. It applies both +to debugging issues in client code that manifest themselves as collector +misbehavior, and to debugging the collector itself. + +If you suspect a bug in the collector itself, it is strongly recommended that +you try the latest collector release before proceeding. + +## Bus Errors and Segmentation Violations + +If the fault occurred in `GC_find_limit`, or with incremental collection +enabled, this is probably normal. The collector installs handlers to take care +of these. You will not see these unless you are using a debugger. Your +debugger _should_ allow you to continue. It's often preferable to tell the +debugger to ignore SIGBUS and SIGSEGV ("handle SIGSEGV SIGBUS nostop noprint" +in gdb, "ignore SIGSEGV SIGBUS" in most versions of dbx) and set a breakpoint +in `abort`. The collector will call abort if the signal had another cause, and +there was not other handler previously installed. + +We recommend debugging without incremental collection if possible. (This +applies directly to UNIX systems. Debugging with incremental collection under +win32 is worse. See README.win32.) + +If the application generates an unhandled SIGSEGV or equivalent, it may often +be easiest to set the environment variable `GC_LOOP_ON_ABORT`. On many +platforms, this will cause the collector to loop in a handler when the SIGSEGV +is encountered (or when the collector aborts for some other reason), and +a debugger can then be attached to the looping process. This sidesteps common +operating system problems related to incomplete core files for multi-threaded +applications, etc. + +## Other Signals + +On most platforms, the multi-threaded version of the collector needs one or +two other signals for internal use by the collector in stopping threads. It is +normally wise to tell the debugger to ignore these. On Linux, the collector +currently uses SIGPWR and SIGXCPU by default. + +## Warning Messages About Needing to Allocate Blacklisted Blocks + +The garbage collector generates warning messages of the form: + + + Needed to allocate blacklisted block at 0x... + + +or + + + Repeated allocation of very large block ... + + +when it needs to allocate a block at a location that it knows to be referenced +by a false pointer. These false pointers can be either permanent (e.g. +a static integer variable that never changes) or temporary. In the latter +case, the warning is largely spurious, and the block will eventually +be reclaimed normally. In the former case, the program will still run +correctly, but the block will never be reclaimed. Unless the block is intended +to be permanent, the warning indicates a memory leak. + + 1. Ignore these warnings while you are using GC_DEBUG. Some of the routines + mentioned below don't have debugging equivalents. (Alternatively, write the + missing routines and send them to me.) + 2. Replace allocator calls that request large blocks with calls to + `GC_malloc_ignore_off_page` or `GC_malloc_atomic_ignore_off_page`. You may + want to set a breakpoint in `GC_default_warn_proc` to help you identify such + calls. Make sure that a pointer to somewhere near the beginning of the + resulting block is maintained in a (preferably volatile) variable as long + as the block is needed. + 3. If the large blocks are allocated with realloc, we suggest instead + allocating them with something like the following. Note that the realloc + size increment should be fairly large (e.g. a factor of 3/2) for this to + exhibit reasonable performance. But we all know we should do that anyway. + + + void * big_realloc(void *p, size_t new_size) { + size_t old_size = GC_size(p); + void * result; + if (new_size <= 10000) return(GC_realloc(p, new_size)); + if (new_size <= old_size) return(p); + result = GC_malloc_ignore_off_page(new_size); + if (result == 0) return(0); + memcpy(result,p,old_size); + GC_free(p); + return(result); + } + + + 4. In the unlikely case that even relatively small object (<20KB) + allocations are triggering these warnings, then your address space contains + lots of "bogus pointers", i.e. values that appear to be pointers but aren't. + Usually this can be solved by using `GC_malloc_atomic` or the routines + in `gc_typed.h` to allocate large pointer-free regions of bitmaps, etc. + Sometimes the problem can be solved with trivial changes of encoding + in certain values. It is possible, to identify the source of the bogus + pointers by building the collector with `-DPRINT_BLACK_LIST`, which will + cause it to print the "bogus pointers", along with their location. + 5. If you get only a fixed number of these warnings, you are probably only + introducing a bounded leak by ignoring them. If the data structures being + allocated are intended to be permanent, then it is also safe to ignore them. + The warnings can be turned off by calling `GC_set_warn_proc` with + a procedure that ignores these warnings (e.g. by doing absolutely nothing). + +## The Collector References a Bad Address in GC_malloc + +This typically happens while the collector is trying to remove an entry from +its free list, and the free list pointer is bad because the free list link +in the last allocated object was bad. + +With >99% probability, you wrote past the end of an allocated object. Try +setting `GC_DEBUG` before including `gc.h` and allocating with `GC_MALLOC`. +This will try to detect such overwrite errors. + +## Unexpectedly Large Heap + +Unexpected heap growth can be due to one of the following: + + 1. Data structures that are being unintentionally retained. This is commonly + caused by data structures that are no longer being used, but were not + cleared, or by caches growing without bounds. + 2. Pointer misidentification. The garbage collector is interpreting integers + or other data as pointers and retaining the "referenced" objects. A common + symptom is that GC_dump() shows much of the heap as black-listed. + 3. Heap fragmentation. This should never result in unbounded growth, but + it may account for larger heaps. This is most commonly caused by allocation + of large objects. + 4. Per object overhead. This is usually a relatively minor effect, but + it may be worth considering. If the collector recognizes interior pointers, + object sizes are increased, so that one-past-the-end pointers are correctly + recognized. The collector can be configured not to do this + (`-DDONT_ADD_BYTE_AT_END`). + +The collector rounds up object sizes so the result fits well into the chunk +size (`HBLKSIZE`, normally 4K on 32 bit machines, 8K on 64 bit machines) used +by the collector. Thus it may be worth avoiding objects of size 2K + 1 (or 2K +if a byte is being added at the end.) The last two cases can often +be identified by looking at the output of a call to `GC_dump`. Among other +things, it will print the list of free heap blocks, and a very brief +description of all chunks in the heap, the object sizes they correspond to, +and how many live objects were found in the chunk at the last collection. + +Growing data structures can usually be identified by: + + 1. Building the collector with `-DKEEP_BACK_PTRS`, + 2. Preferably using debugging allocation (defining `GC_DEBUG` before + including `gc.h` and allocating with `GC_MALLOC`), so that objects will + be identified by their allocation site, + 3. Running the application long enough so that most of the heap is composed + of "leaked" memory, and + 4. Then calling `GC_generate_random_backtrace` from gc_backptr.h a few times + to determine why some randomly sampled objects in the heap are being + retained. + +The same technique can often be used to identify problems with false pointers, +by noting whether the reference chains printed +by `GC_generate_random_backtrace` involve any misidentified pointers. +An alternate technique is to build the collector with `-DPRINT_BLACK_LIST` +which will cause it to report values that are almost, but not quite, look like +heap pointers. It is very likely that actual false pointers will come from +similar sources. + +In the unlikely case that false pointers are an issue, it can usually +be resolved using one or more of the following techniques: + + 1. Use `GC_malloc_atomic` for objects containing no pointers. This is + especially important for large arrays containing compressed data, + pseudo-random numbers, and the like. It is also likely to improve GC + performance, perhaps drastically so if the application is paging. + 2. If you allocate large objects containing only one or two pointers at the + beginning, either try the typed allocation primitives is`gc_typed.h`, + or separate out the pointer-free component. + 3. Consider using `GC_malloc_ignore_off_page` to allocate large objects. + (See `gc.h` and above for details. Large means >100K in most environments.) + 4. If your heap size is larger than 100MB or so, build the collector with + `-DLARGE_CONFIG`. This allows the collector to keep more precise black-list + information. + 5. If you are using heaps close to, or larger than, a gigabyte on a 32-bit + machine, you may want to consider moving to a platform with 64-bit pointers. + This is very likely to resolve any false pointer issues. + +## Prematurely Reclaimed Objects + +The usual symptom of this is a segmentation fault, or an obviously overwritten +value in a heap object. This should, of course, be impossible. In practice, +it may happen for reasons like the following: + + 1. The collector did not intercept the creation of threads correctly + in a multi-threaded application, e.g. because the client called + `pthread_create` without including `gc.h`, which redefines it. + 2. The last pointer to an object in the garbage collected heap was stored + somewhere were the collector could not see it, e.g. in an object allocated + with system `malloc`, in certain types of `mmap`ed files, or in some data + structure visible only to the OS. (On some platforms, thread-local storage + is one of these.) + 3. The last pointer to an object was somehow disguised, e.g. by XORing + it with another pointer. + 4. Incorrect use of `GC_malloc_atomic` or typed allocation. + 5. An incorrect `GC_free` call. + 6. The client program overwrote an internal garbage collector data + structure. + 7. A garbage collector bug. + 8. (Empirically less likely than any of the above.) A compiler optimization + that disguised the last pointer. + +The following relatively simple techniques should be tried first to narrow +down the problem: + + 1. If you are using the incremental collector try turning it off for + debugging. + 2. If you are using shared libraries, try linking statically. If that works, + ensure that DYNAMIC_LOADING is defined on your platform. + 3. Try to reproduce the problem with fully debuggable unoptimized code. This + will eliminate the last possibility, as well as making debugging easier. + 4. Try replacing any suspect typed allocation and `GC_malloc_atomic` calls + with calls to `GC_malloc`. + 5. Try removing any `GC_free` calls (e.g. with a suitable `#define`). + 6. Rebuild the collector with `-DGC_ASSERTIONS`. + 7. If the following works on your platform (i.e. if gctest still works if + you do this), try building the collector with + `-DREDIRECT_MALLOC=GC_malloc_uncollectable`. This will cause the collector + to scan memory allocated with malloc. + +If all else fails, you will have to attack this with a debugger. The suggested +steps are: + + 1. Call `GC_dump` from the debugger around the time of the failure. Verify + that the collectors idea of the root set (i.e. static data regions which + it should scan for pointers) looks plausible. If not, i.e. if it does not + include some static variables, report this as a collector bug. Be sure + to describe your platform precisely, since this sort of problem is nearly + always very platform dependent. + 2. Especially if the failure is not deterministic, try to isolate + it to a relatively small test case. + 3. Set a break point in `GC_finish_collection`. This is a good point + to examine what has been marked, i.e. found reachable, by the collector. + 4. If the failure is deterministic, run the process up to the last + collection before the failure. Note that the variable `GC_gc_no` counts + collections and can be used to set a conditional breakpoint in the right + one. It is incremented just before the call to `GC_finish_collection`. + If object `p` was prematurely recycled, it may be helpful to look + at `*GC_find_header(p)` at the failure point. The `hb_last_reclaimed` field + will identify the collection number during which its block was last swept. + 5. Verify that the offending object still has its correct contents at this + point. Then call `GC_is_marked(p)` from the debugger to verify that the + object has not been marked, and is about to be reclaimed. Note that + `GC_is_marked(p)` expects the real address of an object (the address of the + debug header if there is one), and thus it may be more appropriate to call + `GC_is_marked(GC_base(p))` instead. + 6. Determine a path from a root, i.e. static variable, stack, or register + variable, to the reclaimed object. Call `GC_is_marked(q)` for each object + `q` along the path, trying to locate the first unmarked object, say `r`. + 7. If `r` is pointed to by a static root, verify that the location pointing + to it is part of the root set printed by `GC_dump`. If it is on the stack + in the main (or only) thread, verify that `GC_stackbottom` is set correctly + to the base of the stack. If it is in another thread stack, check the + collector's thread data structure (`GC_thread[]` on several platforms) + to make sure that stack bounds are set correctly. + 8. If `r` is pointed to by heap object `s`, check that the collector's + layout description for `s` is such that the pointer field will be scanned. + Call `*GC_find_header(s)` to look at the descriptor for the heap chunk. + The `hb_descr` field specifies the layout of objects in that chunk. + See `gc_mark.h` for the meaning of the descriptor. (If its low order 2 bits + are zero, then it is just the length of the object prefix to be scanned. + This form is always used for objects allocated with `GC_malloc` or + `GC_malloc_atomic`.) + 9. If the failure is not deterministic, you may still be able to apply some + of the above technique at the point of failure. But remember that objects + allocated since the last collection will not have been marked, even if the + collector is functioning properly. On some platforms, the collector can + be configured to save call chains in objects for debugging. Enabling this + feature will also cause it to save the call stack at the point of the last + GC in `GC_arrays._last_stack`. + 10. When looking at GC internal data structures remember that a number + of `GC_xxx` variables are really macro defined to `GC_arrays._xxx`, so that + the collector can avoid scanning them. diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/doc.am b/unity-2019.4.24f1-mbe/external/bdwgc/doc/doc.am new file mode 100644 index 000000000..2c88e3185 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/doc.am @@ -0,0 +1,51 @@ +# +# THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED +# OR IMPLIED. ANY USE IS AT YOUR OWN RISK. +# +# Permission is hereby granted to use or copy this program +# for any purpose, provided the above notices are retained on all copies. +# Permission to modify the code and to distribute modified code is granted, +# provided the above notices are retained, and a notice that the code was +# modified is included with the above copyright notice. + +## Process this file with automake to produce Makefile.in. + +# installed documentation +if ENABLE_DOCS +dist_doc_DATA = \ + AUTHORS \ + README.md \ + doc/README.DGUX386 \ + doc/README.Mac \ + doc/README.OS2 \ + doc/README.amiga \ + doc/README.arm.cross \ + doc/README.autoconf \ + doc/README.cmake \ + doc/README.cords \ + doc/README.darwin \ + doc/README.environment \ + doc/README.ews4800 \ + doc/README.hp \ + doc/README.linux \ + doc/README.macros \ + doc/README.rs6000 \ + doc/README.sgi \ + doc/README.solaris2 \ + doc/README.symbian \ + doc/README.uts \ + doc/README.win32 \ + doc/README.win64 \ + doc/debugging.md \ + doc/finalization.md \ + doc/gcdescr.md \ + doc/gcinterface.md \ + doc/leak.md \ + doc/overview.md \ + doc/porting.md \ + doc/scale.md \ + doc/simple_example.md \ + doc/tree.md + +dist_man3_MANS = doc/gc.man +endif diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/finalization.md b/unity-2019.4.24f1-mbe/external/bdwgc/doc/finalization.md new file mode 100644 index 000000000..13c14afba --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/finalization.md @@ -0,0 +1,159 @@ +# Finalization + +Many garbage collectors provide a facility for executing user code just before +an object is collected. This can be used to reclaim any system resources +or non-garbage-collected memory associated with the object. Experience has +shown that this can be a useful facility. It is indispensable in cases +in which system resources are embedded in complex data structures (e.g. file +descriptors in the `include/cord.h`). + +Our collector provides the necessary functionality through +`GC_register_finalizer` in `include/gc.h`, or by inheriting from `gc_cleanup` +in `include/gc_cpp.h`). + +However, finalization should not be used in the same way as C++ destructors. +In well-written programs there will typically be very few uses +of finalization. (Garbage collected programs that interact with explicitly +memory-managed libraries may be an exception.) + +In general the following guidelines should be followed: + + * Actions that must be executed promptly do not belong in finalizers. They + should be handled by explicit calls in the code (or C++ destructors if you + prefer). If you expect the action to occur at a specific point, this + is probably not hard. + * Finalizers are intended for resource reclamation. + * Scarce system resources should be managed explicitly whenever convenient. + Use finalizers only as a backup mechanism for the cases that would be hard + to handle explicitly. + * If scarce resources are managed with finalization, the allocation routine + for that resource (e.g. open for file handles) should force a garbage + collection (two if that does not suffice) if it finds itself short of the + resource. + * If extremely scarce resources are managed by finalization (e.g. file + descriptors on systems which have a limit of 20 open files), it may + be necessary to introduce a descriptor caching scheme to hide the resource + limit. (E.g., the program would keep real file descriptors for the 20 most + recently used logically open files. Any other needed files would be closed + after saving their state. They would then be reopened on demand. + Finalization would logically close the file, closing the real descriptor + only if it happened to be cached.) Note that most modern systems (e.g. Irix) + allow hundreds or thousands of open files, and this is typically not + an issue. + * Finalization code may be run anyplace an allocation or other call to the + collector takes place. In multi-threaded programs, finalizers have to obey + the normal locking conventions to ensure safety. Code run directly from + finalizers should not acquire locks that may be held during allocation. + This restriction can be easily circumvented by registering a finalizer which + enqueues the real action for execution in a separate thread. + +In single-threaded code, it is also often easiest to have finalizers queue +actions, which are then explicitly run during an explicit call by the user's +program. + +# Topologically Ordered Finalization + +Our _conservative garbage collector_ supports a form of finalization (with +`GC_register_finalizer`) in which objects are finalized in topological order. +If _A_ points to _B_ and both are registered for finalization, it is +guaranteed the _A_ will be finalized first. This usually guarantees that +finalization procedures see only unfinalized objects. + +This decision is often questioned, particularly since it has an obvious +disadvantage. The current implementation finalizes long chains of finalizable +objects one per collection. This is hard to avoid, since the first finalizer +invoked may store a pointer to the rest of the chain in a global variable, +making it accessible again. Or it may mutate the rest of the chain. + +Cycles involving one or more finalizable objects are never finalized. + +# Why topological ordering? + +It is important to keep in mind that the choice of finalization ordering +matters only in relatively rare cases. In spite of the fact that it has +received a lot of discussion, it is not one of the more important decisions +in designing a system. Many, especially smaller, applications will never +notice the difference. Nonetheless, we believe that topologically ordered +finalization is the right choice. + +To understand the justification, observe that if _A_'s finalization procedure +does not refer to _B_, we could fairly easily have avoided the dependency. +We could have split _A_ into _A'_ and _A''_ such that any references to _A_ +become references to _A'_, _A'_ points to _A''_ but not vice-versa, only +fields needed for finalization are stored in _A''_, and _A''_ is enabled for +finalization. (`GC_register_disappearing_link` provides an alternative +mechanism that does not require breaking up objects.) + +Thus assume that _A_ actually does need access to _B_ during finalization. +To make things concrete, assume that _B_ is finalizable because it holds +a pointer to a C object, which must be explicitly deallocated. (This is likely +to be one of the most common uses of finalization.) If _B_ happens to be +finalized first, _A_ will see a dangling pointer during its finalization. But +a principal goal of garbage collection was to avoid dangling pointers. + +Note that the client program could enforce topological ordering even if the +system did not. A pointer to _B_ could be stored in some globally visible +place, where it is cleared only by _A_'s finalizer. But this puts the burden +to ensure safety back on the programmer. + +With topologically ordered finalization, the programmer can fail to split +an object, thus leaving an accidental cycle. This results in a leak, which +is arguably less dangerous than a dangling pointer. More importantly, it is +_much_ easier to diagnose, since the garbage collector would have to go out of +its way not to notice finalization cycles. It can trivially report them. + +Furthermore unordered finalization does not really solve the problem +of cycles. Consider the above case in which _A_'s finalization procedure +depends on _B_, and thus a pointer to _B_ is stored in a global data +structure, to be cleared by _A_'s finalizer. If there is an accidental pointer +from _B_ back to _A_, and thus a cycle, neither _B_ nor _A_ will become +unreachable. The leak is there, just as in the topologically ordered case, but +it is hidden from easy diagnosis. + +A number of alternative finalization orderings have been proposed, e.g. based +on statically assigned priorities. In our opinion, these are much more likely +to require complex programming discipline to use in a large modular system. +(Some of them, e.g. Guardians proposed by Dybvig, Bruggeman, and Eby, do avoid +some problems which arise in combination with certain other collection +algorithms.) + +Fundamentally, a garbage collector assumes that objects reachable via pointer +chains may be accessed, and thus should be preserved. Topologically ordered +finalization simply extends this to object finalization; an finalizable object +reachable from another finalizer via a pointer chain is presumed to be +accessible by the finalizer, and thus should not be finalized. + +# Programming with topological finalization + +Experience with Cedar has shown that cycles or long chains of finalizable +objects are typically not a problem. Finalizable objects are typically rare. +There are several ways to reduce spurious dependencies between finalizable +objects. Splitting objects as discussed above is one technique. The collector +also provides `GC_register_disappearing_link`, which explicitly nils a pointer +before determining finalization ordering. + +Some so-called "operating systems" fail to clean up some resources associated +with a process. These resources must be deallocated at all cost before process +exit whether or not they are still referenced. Probably the best way to deal +with those is by not relying exclusively on finalization. They should +be registered in a table of weak pointers (implemented as disguised pointers +cleared by the finalization procedure that deallocates the resource). If any +references are still left at process exit, they can be explicitly deallocated +then. + +# Getting around topological finalization ordering + +There are certain situations in which cycles between finalizable objects are +genuinely unavoidable. Most notably, C++ compilers introduce self-cycles +to represent inheritance. `GC_register_finalizer_ignore_self` tells the +finalization part of the collector to ignore self cycles. This is used by the +C++ interface. + +Finalize.c actually contains an intentionally undocumented mechanism for +registering a finalizable object with user-defined dependencies. The problem +is that this dependency information is also used for memory reclamation, not +just finalization ordering. Thus misuse can result in dangling pointers even +if finalization does not create any. The risk of dangling pointers can be +eliminated by building the collector with `-DJAVA_FINALIZATION`. This forces +objects reachable from finalizers to be marked, even though this dependency +is not considered for finalization ordering. diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/gc.man b/unity-2019.4.24f1-mbe/external/bdwgc/doc/gc.man new file mode 100644 index 000000000..d75811504 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/gc.man @@ -0,0 +1,103 @@ +.TH BDWGC 3 "2 October 2003" +.SH NAME +GC_malloc, GC_malloc_atomic, GC_free, GC_realloc, GC_enable_incremental, GC_register_finalizer, GC_malloc_ignore_off_page, GC_malloc_atomic_ignore_off_page, GC_set_warn_proc \- Garbage collecting malloc replacement +.SH SYNOPSIS +#include "gc.h" +.br +void * GC_malloc(size_t size); +.br +void GC_free(void *ptr); +.br +void * GC_realloc(void *ptr, size_t size); +.br +.sp +cc ... -lgc +.LP +.SH DESCRIPTION +.I GC_malloc +and +.I GC_free +are plug-in replacements for standard malloc and free. However, +.I +GC_malloc +will attempt to reclaim inaccessible space automatically by invoking a conservative garbage collector at appropriate points. The collector traverses all data structures accessible by following pointers from the machines registers, stack(s), data, and bss segments. Inaccessible structures will be reclaimed. A machine word is considered to be a valid pointer if it is an address inside an object allocated by +.I +GC_malloc +or friends. +.LP +In most cases it is preferable to call the macros GC_MALLOC, GC_FREE, etc. +instead of calling GC_malloc and friends directly. This allows debugging +versions of the routines to be substituted by defining GC_DEBUG before +including gc.h. +.LP +See the documentation in the include files gc_cpp.h and gc_allocator.h, +as well as the gcinterface.md file in the distribution, +for an alternate, C++ specific interface to the garbage collector. +Note that C++ programs generally +need to be careful to ensure that all allocated memory (whether via new, +malloc, or STL allocators) that may point to garbage collected memory +is either itself garbage collected, or at least traced by the collector. +.LP +Unlike the standard implementations of malloc, +.I +GC_malloc +clears the newly allocated storage. +.I +GC_malloc_atomic +does not. Furthermore, it informs the collector that the resulting object will never contain any pointers, and should therefore not be scanned by the collector. +.LP +.I +GC_free +can be used to deallocate objects, but its use is optional, and generally discouraged. +.I +GC_realloc +has the standard realloc semantics. It preserves pointer-free-ness. +.I +GC_register_finalizer +allows for registration of functions that are invoked when an object becomes inaccessible. +.LP +The garbage collector tries to avoid allocating memory at locations that already appear to be referenced before allocation. (Such apparent ``pointers'' are usually large integers and the like that just happen to look like an address.) This may make it hard to allocate very large objects. An attempt to do so may generate a warning. +.LP +.I +GC_malloc_ignore_off_page +and +.I +GC_malloc_atomic_ignore_off_page +inform the collector that the client code will always maintain a pointer to near the beginning of the object (within the first 512 bytes), and that pointers beyond that can be ignored by the collector. This makes it much easier for the collector to place large objects. These are recommended for large object allocation. (Objects expected to be larger than about 100KBytes should be allocated this way.) +.LP +It is also possible to use the collector to find storage leaks in programs destined to be run with standard malloc/free. The collector can be compiled for thread-safe operation. Unlike standard malloc, it is safe to call malloc after a previous malloc call was interrupted by a signal, provided the original malloc call is not resumed. +.LP +The collector may, on rare occasion produce warning messages. On UNIX machines these appear on stderr. Warning messages can be filtered, redirected, or ignored with +.I +GC_set_warn_proc +This is recommended for production code. See gc.h for details. +.LP +Fully portable code should call +.I +GC_INIT +from the main program before making any other GC calls. +On most platforms this does nothing and the collector is initialized on first use. +On a few platforms explicit initialization is necessary. And it can never hurt. +.LP +Debugging versions of many of the above routines are provided as macros. Their names are identical to the above, but consist of all capital letters. If GC_DEBUG is defined before gc.h is included, these routines do additional checking, and allow the leak detecting version of the collector to produce slightly more useful output. Without GC_DEBUG defined, they behave exactly like the lower-case versions. +.LP +On some machines, collection will be performed incrementally after a call to +.I +GC_enable_incremental. +This may temporarily write protect pages in the heap. See the README file for more information on how this interacts with system calls that write to the heap. +.LP +Other facilities not discussed here include limited facilities to support incremental collection on machines without appropriate VM support, provisions for providing more explicit object layout information to the garbage collector, more direct support for ``weak'' pointers, support for ``abortable'' garbage collections during idle time, etc. +.LP +.SH "SEE ALSO" +The README and gc.h files in the distribution. More detailed definitions of the functions exported by the collector are given there. (The above list is not complete.) +.LP +The web site at http://www.hboehm.info/gc/ (or https://github.com/ivmai/bdwgc/). +.LP +Boehm, H., and M. Weiser, "Garbage Collection in an Uncooperative Environment", +"Software Practice & Experience", September 1988, pp. 807-820. +.LP +The malloc(3) man page. +.LP +.SH AUTHOR +Hans-J. Boehm (boehm@acm.org). +Some of the code was written by others, most notably Alan Demers. diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/gcdescr.md b/unity-2019.4.24f1-mbe/external/bdwgc/doc/gcdescr.md new file mode 100644 index 000000000..c9d37fc06 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/gcdescr.md @@ -0,0 +1,519 @@ +# Conservative GC Algorithmic Overview + +This is a description of the algorithms and data structures used in our +conservative garbage collector. I expect the level of detail to increase with +time. For a survey of GC algorithms, e.g. see Paul Wilson's +["Uniprocessor Garbage Collection Techniques"](ftp://ftp.cs.utexas.edu/pub/garbage/gcsurvey.ps) +excellent paper. For an overview of the collector interface, see +[here](gcinterface.md). + +This description is targeted primarily at someone trying to understand the +source code. It specifically refers to variable and function names. It may +also be useful for understanding the algorithms at a higher level. + +The description here assumes that the collector is used in default mode. +In particular, we assume that it used as a garbage collector, and not just +a leak detector. We initially assume that it is used in stop-the-world, +non-incremental mode, though the presence of the incremental collector will +be apparent in the design. We assume the default finalization model, but the +code affected by that is very localized. + +## Introduction + +The garbage collector uses a modified mark-sweep algorithm. Conceptually +it operates roughly in four phases, which are performed occasionally as part +of a memory allocation: + + 1. _Preparation_ Each object has an associated mark bit. Clear all mark + bits, indicating that all objects are potentially unreachable. + 2. _Mark phase_ Marks all objects that can be reachable via chains + of pointers from variables. Often the collector has no real information + about the location of pointer variables in the heap, so it views all static + data areas, stacks and registers as potentially containing pointers. Any bit + patterns that represent addresses inside heap objects managed by the + collector are viewed as pointers. Unless the client program has made heap + object layout information available to the collector, any heap objects found + to be reachable from variables are again scanned similarly. + 3. _Sweep phase_ Scans the heap for inaccessible, and hence unmarked, + objects, and returns them to an appropriate free list for reuse. This is not + really a separate phase; even in non incremental mode this is operation + is usually performed on demand during an allocation that discovers an empty + free list. Thus the sweep phase is very unlikely to touch a page that would + not have been touched shortly thereafter anyway. + 4. _Finalization phase_ Unreachable objects which had been registered for + finalization are enqueued for finalization outside the collector. + +The remaining sections describe the memory allocation data structures, and +then the last 3 collection phases in more detail. We conclude by outlining +some of the additional features implemented in the collector. + +## Allocation + +The collector includes its own memory allocator. The allocator obtains memory +from the system in a platform-dependent way. Under UNIX, it uses either +`malloc`, `sbrk`, or `mmap`. + +Most static data used by the allocator, as well as that needed by the rest +of the garbage collector is stored inside the `_GC_arrays` structure. This +allows the garbage collector to easily ignore the collectors own data +structures when it searches for root pointers. Other allocator and collector +internal data structures are allocated dynamically with `GC_scratch_alloc`. +`GC_scratch_alloc` does not allow for deallocation, and is therefore used only +for permanent data structures. + +The allocator allocates objects of different _kinds_. Different kinds are +handled somewhat differently by certain parts of the garbage collector. +Certain kinds are scanned for pointers, others are not. Some may have +per-object type descriptors that determine pointer locations. Or a specific +kind may correspond to one specific object layout. Two built-in kinds are +uncollectible. +In spite of that, it is very likely that most C clients of the collector +currently use at most two kinds: `NORMAL` and `PTRFREE` objects. The +[GCJ](https://gcc.gnu.org/onlinedocs/gcc-4.8.5/gcj/) runtime also makes heavy +use of a kind (allocated with `GC_gcj_malloc`) that stores type information +at a known offset in method tables. + +The collector uses a two level allocator. A large block is defined to be one +larger than half of `HBLKSIZE`, which is a power of 2, typically on the order +of the page size. + +Large block sizes are rounded up to the next multiple of `HBLKSIZE` and then +allocated by `GC_allochblk`. Recent versions of the collector use an +approximate best fit algorithm by keeping free lists for several large block +sizes. The actual implementation of `GC_allochblk` is significantly +complicated by black-listing issues (see below). + +Small blocks are allocated in chunks of size `HBLKSIZE`. Each chunk +is dedicated to only one object size and kind. + +The allocator maintains separate free lists for each size and kind of object. +Associated with each kind is an array of free list pointers, with entry +`freelist[i]` pointing to a free list of size 'i' objects. In recent versions +of the collector, index `i` is expressed in granules, which are the minimum +allocatable unit, typically 8 or 16 bytes. The free lists themselves are +linked through the first word in each object (see `obj_link` macro). + +Once a large block is split for use in smaller objects, it can only be used +for objects of that size, unless the collector discovers a completely empty +chunk. Completely empty chunks are restored to the appropriate large block +free list. + +In order to avoid allocating blocks for too many distinct object sizes, the +collector normally does not directly allocate objects of every possible +request size. Instead, the request is rounded up to one of a smaller number +of allocated sizes, for which free lists are maintained. The exact allocated +sizes are computed on demand, but subject to the constraint that they increase +roughly in geometric progression. Thus objects requested early in the +execution are likely to be allocated with exactly the requested size, subject +to alignment constraints. See `GC_init_size_map` for details. + +The actual size rounding operation during small object allocation +is implemented as a table lookup in `GC_size_map` which maps a requested +allocation size in bytes to a number of granules. + +Both collector initialization and computation of allocated sizes are handled +carefully so that they do not slow down the small object fast allocation path. +An attempt to allocate before the collector is initialized, or before the +appropriate `GC_size_map` entry is computed, will take the same path as an +allocation attempt with an empty free list. This results in a call to the slow +path code (`GC_generic_malloc_inner`) which performs the appropriate +initialization checks. + +In non-incremental mode, we make a decision about whether to garbage collect +whenever an allocation would otherwise have failed with the current heap size. +If the total amount of allocation since the last collection is less than the +heap size divided by `GC_free_space_divisor`, we try to expand the heap. +Otherwise, we initiate a garbage collection. This ensures that the amount +of garbage collection work per allocated byte remains constant. + +The above is in fact an oversimplification of the real heap expansion and GC +triggering heuristic, which adjusts slightly for root size and certain kinds +of fragmentation. In particular: + + * Programs with a large root set size and little live heap memory will + expand the heap to amortize the cost of scanning the roots. + * GC v5 actually collect more frequently in non-incremental mode. The large + block allocator usually refuses to split large heap blocks once the garbage + collection threshold is reached. This often has the effect of collecting + well before the heap fills up, thus reducing fragmentation and working set + size at the expense of GC time. GC v6 chooses an intermediate strategy + depending on how much large object allocation has taken place in the past. + (If the collector is configured to unmap unused pages, GC v6 uses the + strategy of GC v5.) + * In calculating the amount of allocation since the last collection we give + partial credit for objects we expect to be explicitly deallocated. Even + if all objects are explicitly managed, it is often desirable to collect + on rare occasion, since that is our only mechanism for coalescing completely + empty chunks. + +It has been suggested that this should be adjusted so that we favor expansion +if the resulting heap still fits into physical memory. In many cases, that +would no doubt help. But it is tricky to do this in a way that remains robust +if multiple application are contending for a single pool of physical memory. + +## Mark phase + +At each collection, the collector marks all objects that are possibly +reachable from pointer variables. Since it cannot generally tell where pointer +variables are located, it scans the following _root segments_ for pointers: + + * The registers. Depending on the architecture, this may be done using + assembly code, or by calling a `setjmp`-like function which saves register + contents on the stack. + * The stack(s). In the case of a single-threaded application, on most + platforms this is done by scanning the memory between (an approximation of) + the current stack pointer and `GC_stackbottom`. (For Intel Itanium, the + register stack scanned separately.) The `GC_stackbottom` variable is set in + a highly platform-specific way depending on the appropriate configuration + information in `gcconfig.h`. Note that the currently active stack needs + to be scanned carefully, since callee-save registers of client code may + appear inside collector stack frames, which may change during the mark + process. This is addressed by scanning some sections of the stack _eagerly_, + effectively capturing a snapshot at one point in time. + * Static data region(s). In the simplest case, this is the region between + `DATASTART` and `DATAEND`, as defined in `gcconfig.h`. However, in most + cases, this will also involve static data regions associated with dynamic + libraries. These are identified by the mostly platform-specific code + in `dyn_load.c`. The marker maintains an explicit stack of memory regions + that are known to be accessible, but that have not yet been searched for + contained pointers. Each stack entry contains the starting address of the + block to be scanned, as well as a descriptor of the block. If no layout + information is available for the block, then the descriptor is simply + a length. (For other possibilities, see `gc_mark.h`.) + +At the beginning of the mark phase, all root segments (as described above) are +pushed on the stack by `GC_push_roots`. (Registers and eagerly processed stack +sections are processed by pushing the referenced objects instead of the stack +section itself.) If `ALL_INTERIOR_POINTERS` is not defined, then stack roots +require special treatment. In this case, the normal marking code ignores +interior pointers, but `GC_push_all_stack` explicitly checks for interior +pointers and pushes descriptors for target objects. + +The marker is structured to allow incremental marking. Each call +to `GC_mark_some` performs a small amount of work towards marking the heap. +It maintains explicit state in the form of `GC_mark_state`, which identifies +a particular sub-phase. Some other pieces of state, most notably the mark +stack, identify how much work remains to be done in each sub-phase. The normal +progression of mark states for a stop-the-world collection is: + + 1. `MS_INVALID` indicating that there may be accessible unmarked objects. + In this case `GC_objects_are_marked` will simultaneously be false, so the + mark state is advanced to + 2. `MS_PUSH_UNCOLLECTABLE` indicating that it suffices to push uncollectible + objects, roots, and then mark everything reachable from them. `scan_ptr` + is advanced through the heap until all uncollectible objects are pushed, and + objects reachable from them are marked. At that point, the next call + to `GC_mark_some` calls `GC_push_roots` to push the roots. It the advances + the mark state to + 3. `MS_ROOTS_PUSHED` asserting that once the mark stack is empty, all + reachable objects are marked. Once in this state, we work only on emptying + the mark stack. Once this is completed, the state changes to + 4. `MS_NONE` indicating that reachable objects are marked. The core mark + routine `GC_mark_from`, is called repeatedly by several of the sub-phases + when the mark stack starts to fill up. It is also called repeatedly + in `MS_ROOTS_PUSHED` state to empty the mark stack. The routine is designed + to only perform a limited amount of marking at each call, so that it can + also be used by the incremental collector. It is fairly carefully tuned, + since it usually consumes a large majority of the garbage collection time. + +The fact that it performs only a small amount of work per call also allows +it to be used as the core routine of the parallel marker. In that case it is +normally invoked on thread-private mark stacks instead of the global mark +stack. More details can be found [here](scale.md). + +The marker correctly handles mark stack overflows. Whenever the mark stack +overflows, the mark state is reset to `MS_INVALID`. Since there are already +marked objects in the heap, this eventually forces a complete scan of the +heap, searching for pointers, during which any unmarked objects referenced +by marked objects are again pushed on the mark stack. This process is repeated +until the mark phase completes without a stack overflow. Each time the stack +overflows, an attempt is made to grow the mark stack. All pieces of the +collector that push regions onto the mark stack have to be careful to ensure +forward progress, even in case of repeated mark stack overflows. Every mark +attempt results in additional marked objects. + +Each mark stack entry is processed by examining all candidate pointers in the +range described by the entry. If the region has no associated type +information, then this typically requires that each 4-byte aligned quantity +(8-byte aligned with 64-bit pointers) be considered a candidate pointer. + +We determine whether a candidate pointer is actually the address of a heap +block. This is done in the following steps: + + * The candidate pointer is checked against rough heap bounds. These heap + bounds are maintained such that all actual heap objects fall between them. + In order to facilitate black-listing (see below) we also include address + regions that the heap is likely to expand into. Most non-pointers fail this + initial test. + * The candidate pointer is divided into two pieces; the most significant + bits identify a `HBLKSIZE`-sized page in the address space, and the least + significant bits specify an offset within that page. (A hardware page may + actually consist of multiple such pages. HBLKSIZE is usually the page size + divided by a small power of two.) + * The page address part of the candidate pointer is looked up in + a [table](tree.md). Each table entry contains either 0, indicating that + the page is not part of the garbage collected heap, a small integer _n_, + indicating that the page is part of large object, starting at least _n_ + pages back, or a pointer to a descriptor for the page. In the first case, + the candidate pointer `i` not a true pointer and can be safely ignored. + In the last two cases, we can obtain a descriptor for the page containing + the beginning of the object. + * The starting address of the referenced object is computed. The page + descriptor contains the size of the object(s) in that page, the object kind, + and the necessary mark bits for those objects. The size information can be + used to map the candidate pointer to the object starting address. + To accelerate this process, the page header also contains a pointer to + a precomputed map of page offsets to displacements from the beginning of an + object. The use of this map avoids a potentially slow integer remainder + operation in computing the object start address. + * The mark bit for the target object is checked and set. If the object was + previously unmarked, the object is pushed on the mark stack. The descriptor + is read from the page descriptor. (This is computed from information + `GC_obj_kinds` when the page is first allocated.) + +At the end of the mark phase, mark bits for left-over free lists are cleared, +in case a free list was accidentally marked due to a stray pointer. + +## Sweep phase + +At the end of the mark phase, all blocks in the heap are examined. Unmarked +large objects are immediately returned to the large object free list. Each +small object page is checked to see if all mark bits are clear. If so, the +entire page is returned to the large object free list. Small object pages +containing some reachable object are queued for later sweeping, unless +we determine that the page contains very little free space, in which case +it is not examined further. + +This initial sweep pass touches only block headers, not the blocks themselves. +Thus it does not require significant paging, even if large sections of the +heap are not in physical memory. + +Nonempty small object pages are swept when an allocation attempt encounters +an empty free list for that object size and kind. Pages for the correct size +and kind are repeatedly swept until at least one empty block is found. +Sweeping such a page involves scanning the mark bit array in the page header, +and building a free list linked through the first words in the objects +themselves. This does involve touching the appropriate data page, but in most +cases it will be touched only just before it is used for allocation. Hence any +paging is essentially unavoidable. + +Except in the case of pointer-free objects, we maintain the invariant that any +object in a small object free list is cleared (except possibly for the link +field). Thus it becomes the burden of the small object sweep routine to clear +objects. This has the advantage that we can easily recover from accidentally +marking a free list, though that could also be handled by other means. The +collector currently spends a fair amount of time clearing objects, and this +approach should probably be revisited. In most configurations, we use +specialized sweep routines to handle common small object sizes. Since +we allocate one mark bit per word, it becomes easier to examine the relevant +mark bits if the object size divides the word length evenly. We also suitably +unroll the inner sweep loop in each case. (It is conceivable that +profile-based procedure cloning in the compiler could make this unnecessary +and counterproductive. I know of no existing compiler to which this applies.) + +The sweeping of small object pages could be avoided completely at the expense +of examining mark bits directly in the allocator. This would probably be more +expensive, since each allocation call would have to reload a large amount +of state (e.g. next object address to be swept, position in mark bit table) +before it could do its work. The current scheme keeps the allocator simple and +allows useful optimizations in the sweeper. + +## Finalization + +Both `GC_register_disappearing_link` and `GC_register_finalizer` add the +request to a corresponding hash table. The hash table is allocated out of +collected memory, but the reference to the finalizable object is hidden from +the collector. Currently finalization requests are processed non-incrementally +at the end of a mark cycle. + +The collector makes an initial pass over the table of finalizable objects, +pushing the contents of unmarked objects onto the mark stack. After pushing +each object, the marker is invoked to mark all objects reachable from it. The +object itself is not explicitly marked. This assures that objects on which +a finalizer depends are neither collected nor finalized. + +If in the process of marking from an object the object itself becomes marked, +we have uncovered a cycle involving the object. This usually results in +a warning from the collector. Such objects are not finalized, since it may be +unsafe to do so. See the more detailed discussion of +[finalization semantics](finalization.md). + +Any objects remaining unmarked at the end of this process are added to a queue +of objects whose finalizers can be run. Depending on collector configuration, +finalizers are dequeued and run either implicitly during allocation calls, +or explicitly in response to a user request. (Note that the former +is unfortunately both the default and not generally safe. If finalizers +perform synchronization, it may result in deadlocks. Nontrivial finalizers +generally need to perform synchronization, and thus require a different +collector configuration.) + +The collector provides a mechanism for replacing the procedure that is used +to mark through objects. This is used both to provide support for Java-style +unordered finalization, and to ignore certain kinds of cycles, e.g. those +arising from C++ implementations of virtual inheritance. + +## Generational Collection and Dirty Bits + +We basically use the concurrent and generational GC algorithm described in +["Mostly Parallel Garbage Collection"](http://www.hboehm.info/gc/papers/pldi91.ps.Z), +by Boehm, Demers, and Shenker. + +The most significant modification is that the collector always starts running +in the allocating thread. There is no separate garbage collector thread. (If +parallel GC is enabled, helper threads may also be woken up.) If an allocation +attempt either requests a large object, or encounters an empty small object +free list, and notices that there is a collection in progress, it immediately +performs a small amount of marking work as described above. + +This change was made both because we wanted to easily accommodate +single-threaded environments, and because a separate GC thread requires very +careful control over the scheduler to prevent the mutator from out-running the +collector, and hence provoking unneeded heap growth. + +In incremental mode, the heap is always expanded when we encounter +insufficient space for an allocation. Garbage collection is triggered whenever +we notice that more than `GC_heap_size`/2 * `GC_free_space_divisor` bytes +of allocation have taken place. After `GC_full_freq` minor collections a major +collection is started. + +All collections initially run uninterrupted until a predetermined amount +of time (50 msecs by default) has expired. If this allows the collection +to complete entirely, we can avoid correcting for data structure modifications +during the collection. If it does not complete, we return control to the +mutator, and perform small amounts of additional GC work during those later +allocations that cannot be satisfied from small object free lists. When +marking completes, the set of modified pages is retrieved, and we mark once +again from marked objects on those pages, this time with the mutator stopped. + +We keep track of modified pages using one of several distinct mechanisms: + + * (`MPROTECT_VDB`) By write-protecting physical pages and catching write + faults. This is implemented for many Unix-like systems and for Win32. It is + not possible in a few environments. + * (`GWW_VDB`) By using the Win32 `GetWriteWatch` function to read dirty + bits. + * (`PROC_VDB`) By retrieving dirty bit information from /proc. (Currently + only Sun's Solaris supports this. Though this is considerably cleaner, + performance may actually be better with `mprotect` and signals.) + * (`PCR_VDB`) By relying on an external dirty bit implementation, in this + case the one in Xerox PCR. + * (`MANUAL_VDB`) Through explicit mutator cooperation. This requires the + client code to call `GC_end_stubborn_change`, and is rarely used. + * (`DEFAULT_VDB`) By treating all pages as dirty. This is the default + if none of the other techniques is known to be usable. (Practical only for + testing.) + +## Black-listing + +The collector implements _black-listing_ of pages, as described in +["Space Efficient Conservative Collection", PLDI'93](http://dl.acm.org/citation.cfm?doid=155090.155109) +by Boehm, also available +[here](https://www.cs.rice.edu/~javaplt/311/Readings/pldi93.pdf). + +During the mark phase, the collector tracks _near misses_, i.e. attempts +to follow a _pointer_ to just outside the garbage-collected heap, or to +a currently unallocated page inside the heap. Pages that have been the targets +of such near misses are likely to be the targets of misidentified _pointers_ +in the future. To minimize the future damage caused by such misidentification, +they will be allocated only to small pointer-free objects. + +The collector understands two different kinds of black-listing. A page may be +black listed for interior pointer references (`GC_add_to_black_list_stack`), +if it was the target of a near miss from a location that requires interior +pointer recognition, e.g. the stack, or the heap if `GC_all_interior_pointers` +is set. In this case, we also avoid allocating large blocks that include this +page. + +If the near miss came from a source that did not require interior pointer +recognition, it is black-listed with `GC_add_to_black_list_normal`. A page +black-listed in this way may appear inside a large object, so long as it is +not the first page of a large object. + +The `GC_allochblk` routine respects black-listing when assigning a block to +a particular object kind and size. It occasionally drops (i.e. allocates and +forgets) blocks that are completely black-listed in order to avoid excessively +long large block free lists containing only unusable blocks. This would +otherwise become an issue if there is low demand for small pointer-free +objects. + +## Thread support + +We support several different threading models. Unfortunately Pthreads, the +only reasonably well standardized thread model, supports too narrow +an interface for conservative garbage collection. There appears to be no +completely portable way to allow the collector to coexist with various +Pthreads implementations. Hence we currently support only the more common +Pthreads implementations. + +In particular, it is very difficult for the collector to stop all other +threads in the system and examine the register contents. This is currently +accomplished with very different mechanisms for some Pthreads implementations. +For Linux/HPUX/OSF1, Solaris and Irix it sends signals to individual Pthreads +and has them wait in the signal handler. + +The Linux and Irix implementations use only documented Pthreads calls, but +rely on extensions to their semantics. The Linux implementation +`pthread_stop_world.c` relies on only very mild extensions to the pthreads +semantics, and already supports a large number of other Unix-like pthreads +implementations. Our goal is to make this the only pthread support in the +collector. + +All implementations must intercept thread creation and a few other +thread-specific calls to allow enumeration of threads and location of thread +stacks. This is current accomplished with `#define`'s in `gc.h` (really +`gc_pthread_redirects.h`), or optionally by using `ld`'s function call +wrapping mechanism under Linux. + +Recent versions of the collector support several facilities to enhance the +processor-scalability and thread performance of the collector. These are +discussed in more detail [here](scale.md). We briefly outline the data +approach to thread-local allocation in the next section. + +## Thread-local allocation + +If thread-local allocation is enabled, the collector keeps separate arrays +of free lists for each thread. Thread-local allocation is currently only +supported on a few platforms. + +The free list arrays associated with each thread are only used to satisfy +requests for objects that are both very small, and belong to one of a small +number of well-known kinds. These currently include _normal_ and pointer-free +objects. Depending on the configuration, _gcj_ objects may also be included. + +Thread-local free list entries contain either a pointer to the first element +of a free list, or they contain a counter of the number of allocation +granules, corresponding to objects of this size, allocated so far. Initially +they contain the value one, i.e. a small counter value. + +Thread-local allocation allocates directly through the global allocator, +if the object is of a size or kind not covered by the local free lists. + +If there is an appropriate local free list, the allocator checks whether +it contains a sufficiently small counter value. If so, the counter is simply +incremented by the counter value, and the global allocator is used. In this +way, the initial few allocations of a given size bypass the local allocator. +A thread that only allocates a handful of objects of a given size will not +build up its own free list for that size. This avoids wasting space for +unpopular objects sizes or kinds. + +Once the counter passes a threshold, `GC_malloc_many` is called to allocate +roughly `HBLKSIZE` space and put it on the corresponding local free list. +Further allocations of that size and kind then use this free list, and no +longer need to acquire the allocation lock. The allocation procedure +is otherwise similar to the global free lists. The local free lists are also +linked using the first word in the object. In most cases this means they +require considerably less time. + +Local free lists are treated buy most of the rest of the collector as though +they were in-use reachable data. This requires some care, since pointer-free +objects are not normally traced, and hence a special tracing procedure +is required to mark all objects on pointer-free and gcj local free lists. + +On thread exit, any remaining thread-local free list entries are transferred +back to the global free list. + +Note that if the collector is configured for thread-local allocation, +`GC_malloc` only uses thread-local allocation (starting from GC v7). + +For some more details see [here](scale.md), and the technical report entitled +["Fast Multiprocessor Memory Allocation and Garbage Collection"](http://www.hpl.hp.com/techreports/2000/HPL-2000-165.html). diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/gcinterface.md b/unity-2019.4.24f1-mbe/external/bdwgc/doc/gcinterface.md new file mode 100644 index 000000000..4e7d309b7 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/gcinterface.md @@ -0,0 +1,226 @@ +# C/C++ Interface + +On many platforms, a single-threaded garbage collector library can be built +to act as a plug-in `malloc` replacement. (Build with +`-DREDIRECT_MALLOC=GC_malloc -DIGNORE_FREE`.) This is often the best way to +deal with third-party libraries which leak or prematurely free objects. +`-DREDIRECT_MALLOC=GC_malloc` is intended primarily as an easy way to adapt +old code, not for new development. + +New code should use the interface discussed below. + +Code must be linked against the GC library. On most UNIX platforms, depending +on how the collector is built, this will be `gc.a` or `libgc.{a,so}`. + +The following describes the standard C interface to the garbage collector. +It is not a complete definition of the interface. It describes only the most +commonly used functionality, approximately in decreasing order of frequency +of use. The full interface is described in `gc.h` file. + +Clients should include `gc.h` (i.e., not `gc_config_macros.h`, +`gc_pthread_redirects.h`, `gc_version.h`). In the case of multi-threaded code, +`gc.h` should be included after the threads header file, and after defining +`GC_THREADS` macro. The header file `gc.h` must be included in files that use +either GC or threads primitives, since threads primitives will be redefined +to cooperate with the GC on many platforms. + +Thread users should also be aware that on many platforms objects reachable +only from thread-local variables may be prematurely reclaimed. Thus objects +pointed to by thread-local variables should also be pointed to by a globally +visible data structure. (This is viewed as a bug, but as one that +is exceedingly hard to fix without some `libc` hooks.) + +**void * `GC_MALLOC`(size_t _nbytes_)** - Allocates and clears _nbytes_ +of storage. Requires (amortized) time proportional to _nbytes_. The resulting +object will be automatically deallocated when unreferenced. References from +objects allocated with the system malloc are usually not considered by the +collector. (See `GC_MALLOC_UNCOLLECTABLE`, however. Building the collector +with `-DREDIRECT_MALLOC=GC_malloc_uncollectable` is often a way around this.) +`GC_MALLOC` is a macro which invokes `GC_malloc` by default or, if `GC_DEBUG` +is defined before `gc.h` is included, a debugging version that checks +occasionally for overwrite errors, and the like. + +**void * `GC_MALLOC_ATOMIC`(size_t _nbytes_)** - Allocates _nbytes_ +of storage. Requires (amortized) time proportional to _nbytes_. The resulting +object will be automatically deallocated when unreferenced. The client +promises that the resulting object will never contain any pointers. The memory +is not cleared. This is the preferred way to allocate strings, floating point +arrays, bitmaps, etc. More precise information about pointer locations can be +communicated to the collector using the interface in `gc_typed.h`. + +**void * `GC_MALLOC_UNCOLLECTABLE`(size_t _nbytes_)** - Identical +to `GC_MALLOC`, except that the resulting object is not automatically +deallocated. Unlike the system-provided `malloc`, the collector does scan the +object for pointers to garbage-collectible memory, even if the block itself +does not appear to be reachable. (Objects allocated in this way are +effectively treated as roots by the collector.) + +**void * `GC_REALLOC`(void * _old_, size_t _new_size_)** - Allocate a new +object of the indicated size and copy (a prefix of) the old object into the +new object. The old object is reused in place if convenient. If the original +object was allocated with `GC_MALLOC_ATOMIC`, the new object is subject to the +same constraints. If it was allocated as an uncollectible object, then the new +object is uncollectible, and the old object (if different) is deallocated. + +**void `GC_FREE`(void * _dead_)** - Explicitly deallocate an object. Typically +not useful for small collectible objects. + +**void * `GC_MALLOC_IGNORE_OFF_PAGE`(size_t _nbytes_)** and +**void * `GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE`(size_t _nbytes_)** - Analogous +to `GC_MALLOC` and `GC_MALLOC_ATOMIC`, respectively, except that the client +guarantees that as long as the resulting object is of use, a pointer +is maintained to someplace inside the first 512 bytes of the object. This +pointer should be declared volatile to avoid interference from compiler +optimizations. (Other nonvolatile pointers to the object may exist as well.) +This is the preferred way to allocate objects that are likely to be +more than 100 KB in size. It greatly reduces the risk that such objects will +be accidentally retained when they are no longer needed. Thus space usage may +be significantly reduced. + +**void `GC_INIT()`** - On some platforms, it is necessary to invoke this _from +the main executable_, _not from a dynamic library_, before the initial +invocation of a GC routine. It is recommended that this be done in portable +code, though we try to ensure that it expands to a no-op on as many platforms +as possible. In GC v7.0, it was required if thread-local allocation is enabled +in the collector build, and `malloc` is not redirected to `GC_malloc`. + +**void `GC_gcollect`(void)** - Explicitly force a garbage collection. + +**void `GC_enable_incremental`(void)** - Cause the garbage collector +to perform a small amount of work every few invocations of `GC_MALLOC` or the +like, instead of performing an entire collection at once. This is likely +to increase total running time. It will improve response on a platform that +either has suitable support in the garbage collector (Linux and most Unix +versions, Win32 if the collector was suitably built). On many platforms this +interacts poorly with system calls that write to the garbage collected heap. + +**void `GC_set_warn_proc`(GC_warn_proc)** - Replace the default procedure +used by the collector to print warnings. The collector may otherwise +write to `stderr`, most commonly because `GC_malloc` was used in a situation +in which `GC_malloc_ignore_off_page` would have been more appropriate. See +`gc.h` for details. + +**void `GC_REGISTER_FINALIZER`(...)** - Register a function to be called when +an object becomes inaccessible. This is often useful as a backup method for +releasing system resources (e.g. closing files) when the object referencing +them becomes inaccessible. It is not an acceptable method to perform actions +that must be performed in a timely fashion. See `gc.h` for details of the +interface. See also [here](finalization.md) for a more detailed discussion +of the design. + +Note that an object may become inaccessible before client code is done +operating on objects referenced by its fields. Suitable synchronization +is usually required. See +[here](http://portal.acm.org/citation.cfm?doid=604131.604153) +or [here](http://www.hpl.hp.com/techreports/2002/HPL-2002-335.html) for +details. + +If you are concerned with multiprocessor performance and scalability, you +should consider enabling and using thread local allocation. + +If your platform supports it, you should build the collector with parallel +marking support (`-DPARALLEL_MARK`); configure has it on by default. + +If the collector is used in an environment in which pointer location +information for heap objects is easily available, this can be passed on to the +collector using the interfaces in either `gc_typed.h` or `gc_gcj.h`. + +The collector distribution also includes a **string package** that takes +advantage of the collector. For details see `cord.h` file. + +## C++ Interface + +The C++ interface is implemented as a thin layer on the C interface. +Unfortunately, this thin layer appears to be very sensitive to variations +in C++ implementations, particularly since it tries to replace the global +`::new` operator, something that appears to not be well-standardized. Your +platform may need minor adjustments in this layer (`gc_cpp.cc`, `gc_cpp.h`, +and possibly `gc_allocator.h`). Such changes do not require understanding +of collector internals, though they may require a good understanding of your +platform. (Patches enhancing portability are welcome. But it is easy to break +one platform by fixing another.) + +Usage of the collector from C++ is also complicated by the fact that there are +many _standard_ ways to allocate memory in C++. The default `::new` operator, +default `malloc`, and default STL allocators allocate memory that is not +garbage collected, and is not normally _traced_ by the collector. This means +that any pointers in memory allocated by these default allocators will not be +seen by the collector. Garbage-collectible memory referenced only by pointers +stored in such default-allocated objects is likely to be reclaimed prematurely +by the collector. + +It is the programmers responsibility to ensure that garbage-collectible memory +is referenced by pointers stored in one of + + * Program variables + * Garbage-collected objects + * Uncollected but _traceable_ objects + +Traceable objects are not necessarily reclaimed by the collector, but are +scanned for pointers to collectible objects. They are usually allocated +by `GC_MALLOC_UNCOLLECTABLE`, as described above, and through some interfaces +described below. + +On most platforms, the collector may not trace correctly from in-flight +exception objects. Thus objects thrown as exceptions should only point +to otherwise reachable memory. This is another bug whose proper repair +requires platform hooks. + +The easiest way to ensure that collectible objects are properly referenced +is to allocate only collectible objects. This requires that every allocation +go through one of the following interfaces, each one of which replaces +a standard C++ allocation mechanism. Note that this requires that all STL +containers be explicitly instantiated with `gc_allocator`. + +### STL allocators + +Recent versions of the collector include a hopefully standard-conforming +allocator implementation in `gc_allocator.h`. It defines `traceable_allocator` +and `gc_allocator` which may be used either directly to allocate memory or to +instantiate container templates. The former allocates uncollectible but traced +memory. The latter allocates garbage-collected memory. + +These should work with any fully standard-conforming C++ compiler. + +Users of the [SGI extended STL](http://www.sgi.com/tech/stl) or its +derivatives (including most g++ versions) may instead be able to include +`new_gc_alloc.h` before including STL header files. This is increasingly +discouraged. + +This defines SGI-style allocators + + * `alloc` + * `single_client_alloc` + * `gc_alloc` + * `single_client_gc_alloc` + +The first two allocate uncollectible but traced memory, while the second two +allocate collectible memory. The `single_client_...` versions are not safe for +concurrent access by multiple threads, but are faster. + +See sample code [here](http://www.hboehm.info/gc/gc_alloc_exC.txt). + +### Class inheritance based interface for new-based allocation + +Users may include `gc_cpp.h` and then cause members of classes to be allocated +in garbage collectible memory by having those classes inherit from class `gc`. +For details see `gc_cpp.h` file. + +Linking against `libgccpp` in addition to the `gc` library overrides `::new` +(and friends) to allocate traceable memory but uncollectible memory, making +it safe to refer to collectible objects from the resulting memory. + +## C interface + +It is also possible to use the C interface from `gc.h` directly. On platforms +which use `malloc` to implement `::new`, it should usually be possible to use +a version of the collector that has been compiled as a `malloc` replacement. +It is also possible to replace `::new` and other allocation functions +suitably, as is done by `libgccpp`. + +Note that user-implemented small-block allocation often works poorly with +an underlying garbage-collected large block allocator, since the collector has +to view all objects accessible from the user's free list as reachable. This +is likely to cause problems if `GC_MALLOC` is used with something like the +original HP version of STL. This approach works well with the SGI versions +of the STL only if the `malloc_alloc` allocator is used. diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/leak.md b/unity-2019.4.24f1-mbe/external/bdwgc/doc/leak.md new file mode 100644 index 000000000..7d62af7bd --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/leak.md @@ -0,0 +1,159 @@ +# Using the Garbage Collector as Leak Detector + +The garbage collector may be used as a leak detector. In this case, the +primary function of the collector is to report objects that were allocated +(typically with `GC_MALLOC`), not deallocated (normally with `GC_FREE`), but +are no longer accessible. Since the object is no longer accessible, there +in normally no way to deallocate the object at a later time; thus it can +safely be assumed that the object has been "leaked". + +This is substantially different from counting leak detectors, which simply +verify that all allocated objects are eventually deallocated. +A garbage-collector based leak detector can provide somewhat more precise +information when an object was leaked. More importantly, it does not report +objects that are never deallocated because they are part of "permanent" data +structures. Thus it does not require all objects to be deallocated at process +exit time, a potentially useless activity that often triggers large amounts +of paging. + +All non-ancient versions of the garbage collector provide leak detection +support. Version 5.3 adds the following features: + + 1. Leak detection mode can be initiated at run-time by setting + `GC_find_leak` instead of building the collector with `FIND_LEAK` defined. + This variable should be set to a nonzero value at program startup. + 2. Leaked objects should be reported and then correctly garbage collected. + Prior versions either reported leaks or functioned as a garbage collector. + For the rest of this description we will give instructions that work with + any reasonable version of the collector. + +To use the collector as a leak detector, follow the following steps: + + 1. Build the collector with `-DFIND_LEAK`. Otherwise use default build + options. + 2. Change the program so that all allocation and deallocation goes through + the garbage collector. + 3. Arrange to call `GC_gcollect` at appropriate points to check for leaks. + (For sufficiently long running programs, this will happen implicitly, but + probably not with sufficient frequency.) The second step can usually + be accomplished with the `-DREDIRECT_MALLOC=GC_malloc` option when the + collector is built, or by defining `malloc`, `calloc`, `realloc` and `free` + to call the corresponding garbage collector functions. But this, by itself, + will not yield very informative diagnostics, since the collector does not + keep track of information about how objects were allocated. The error + reports will include only object addresses. + +For more precise error reports, as much of the program as possible should use +the all uppercase variants of these functions, after defining `GC_DEBUG`, and +then including `gc.h`. In this environment `GC_MALLOC` is a macro which causes +at least the file name and line number at the allocation point to be saved +as part of the object. Leak reports will then also include this information. + +Many collector features (e.g. finalization and disappearing links) are less +useful in this context, and are not fully supported. Their use will usually +generate additional bogus leak reports, since the collector itself drops some +associated objects. + +The same is generally true of thread support. However, as of 6.0alpha4, +correct leak reports should be generated with linuxthreads. + +On a few platforms (currently Solaris/SPARC, Irix, and, with +-DSAVE_CALL_CHAIN, Linux/X86), `GC_MALLOC` also causes some more information +about its call stack to be saved in the object. Such information is reproduced +in the error reports in very non-symbolic form, but it can be very useful with +the aid of a debugger. + +## An Example + +The `leak_detector.h` file is included in the "include" subdirectory of the +distribution. + +Assume the collector has been built with `-DFIND_LEAK`. (For newer versions +of the collector, we could instead add the statement `GC_set_find_leak(1)` as +the first statement in `main`. + +The program to be tested for leaks can then look like "leak_test.c" file +in the "tests" subdirectory of the distribution. + +On an Intel X86 Linux system this produces on the stderr stream: + + + Leaked composite object at 0x806dff0 (leak_test.c:8, sz=4) + + +(On most unmentioned operating systems, the output is similar to this. If the +collector had been built on Linux/X86 with `-DSAVE_CALL_CHAIN`, the output +would be closer to the Solaris example. For this to work, the program should +not be compiled with `-fomit_frame_pointer`.) + +On Irix it reports: + + + Leaked composite object at 0x10040fe0 (leak_test.c:8, sz=4) + Caller at allocation: + ##PC##= 0x10004910 + + +and on Solaris the error report is: + + + Leaked composite object at 0xef621fc8 (leak_test.c:8, sz=4) + Call chain at allocation: + args: 4 (0x4), 200656 (0x30FD0) + ##PC##= 0x14ADC + args: 1 (0x1), -268436012 (0xEFFFFDD4) + ##PC##= 0x14A64 + + +In the latter two cases some additional information is given about how malloc +was called when the leaked object was allocated. For Solaris, the first line +specifies the arguments to `GC_debug_malloc` (the actual allocation routine), +The second the program counter inside main, the third the arguments to `main`, +and finally the program counter inside the caller to main (i.e. in the +C startup code). + +In the Irix case, only the address inside the caller to main is given. + +In many cases, a debugger is needed to interpret the additional information. +On systems supporting the "adb" debugger, the `tools/callprocs.sh` script can +be used to replace program counter values with symbolic names. As of version +6.1, the collector tries to generate symbolic names for call stacks if it +knows how to do so on the platform. This is true on Linux/X86, but not on most +other platforms. + +## Simplified leak detection under Linux + +Since version 6.1, it should be possible to run the collector in leak +detection mode on a program a.out under Linux/X86 as follows: + + 1. _Ensure that a.out is a single-threaded executable, or you are using + a very recent (7.0alpha7+) collector version on Linux._ On most platforms + this does not work at all for the multi-threaded programs. + 2. If possible, ensure that the `addr2line` program is installed + in `/usr/bin`. (It comes with most Linux distributions.) + 3. If possible, compile your program, which we'll call `a.out`, with full + debug information. This will improve the quality of the leak reports. + With this approach, it is no longer necessary to call `GC_` routines + explicitly, though that can also improve the quality of the leak reports. + 4. Build the collector and install it in directory _foo_ as follows: + * `configure --prefix=_foo_ --enable-gc-debug --enable-redirect-malloc --disable-threads` + * `make` + * `make install` + + With a very recent collector on Linux, it may sometimes be safe to omit + the `--disable-threads`. But the combination of thread support and + `malloc` replacement is not yet rock solid. + 5. Set environment variables as follows: + * `LD_PRELOAD=`_foo_`/lib/libgc.so` + * `GC_FIND_LEAK` + + You may also want to set `GC_PRINT_STATS` (to confirm that the collector + is running) and/or `GC_LOOP_ON_ABORT` (to facilitate debugging from + another window if something goes wrong). + 6. Simply run `a.out` as you normally would. Note that if you run anything + else (e.g. your editor) with those environment variables set, it will also + be leak tested. This may or may not be useful and/or embarrassing. It can + generate mountains of leak reports if the application was not designed + to avoid leaks, e.g. because it's always short-lived. This has not yet + been thoroughly tested on large applications, but it's known to do the right + thing on at least some small ones. diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/overview.md b/unity-2019.4.24f1-mbe/external/bdwgc/doc/overview.md new file mode 100644 index 000000000..fb3aa2a4e --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/overview.md @@ -0,0 +1,388 @@ +[Interface Overview](gcinterface.md) | [Tutorial Slides](http://www.hboehm.info/gc/04tutorial.pdf) | [FAQ](http://www.hboehm.info/gc/faq.html) | [Example](simple_example.md) | [Download](https://github.com/ivmai/bdwgc/wiki/Download) | [License](http://www.hboehm.info/gc/license.txt) +---|---|---|---|---|--- + +# A garbage collector for C and C++ + + * Platforms + * Scalable multiprocessor versions + * Some collector details + * Further reading + * Current users + * Local Links for this collector + * Local Background Links + * Contacts and Mailing List + +[ This is an updated version of the page formerly at +`www.hpl.hp.com/personal/Hans_Boehm/gc/`, before that at +`http://reality.sgi.com/boehm/gc.html` and before that at +`ftp://ftp.parc.xerox.com/pub/gc/gc.html`. ] + +The +[Boehm](http://www.hboehm.info)-[Demers](http://www.cs.cornell.edu/annual_report/00-01/bios.htm#demers)-[Weiser](http://www.ubiq.com/hypertext/weiser/weiser.html) +conservative Garbage Collector (**BDWGC**) can be used as a garbage collecting +replacement for C `malloc` or C++ `new`. It allows you to allocate memory +basically as you normally would, without explicitly deallocating memory that +is no longer useful. The collector automatically recycles memory when +it determines that it can no longer be otherwise accessed. A simple example +of such a use is given [here](simple_example.md). + +The collector is also used by a number of programming language implementations +that either use C as intermediate code, want to facilitate easier +interoperation with C libraries, or just prefer the simple collector +interface. For a more detailed description of the interface, see +[here](gcinterface.md). + +Alternatively, the garbage collector may be used as a [leak detector](leak.md) +for C or C++ programs, though that is not its primary goal. + +Typically several versions are offered for +[downloading](https://github.com/ivmai/bdwgc/wiki/Download): preview, stable, +legacy. Usually you should use the one marked as the _latest stable_ release. +Preview versions may contain additional features, platform support, but are +likely to be less well tested. The list of changes for each version +is specified on the [releases](https://github.com/ivmai/bdwgc/releases) page. + +The arguments for and against conservative garbage collection in C and C++ are +briefly discussed [here](http://www.hboehm.info/gc/issues.html). The +beginnings of a frequently-asked-questions list are +[here](http://www.hboehm.info/gc/faq.html). + +The garbage collector code is copyrighted by +[Hans-J. Boehm](http://www.hboehm.info), Alan J. Demers, +[Xerox Corporation](http://www.xerox.com/), +[Silicon Graphics](http://www.sgi.com/), and +[Hewlett-Packard Company](http://www.hp.com/). It may be used and copied +without payment of a fee under minimal restrictions. See the README.md file +in the distribution or the [license](http://www.hboehm.info/gc/license.txt) +for more details. **IT IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY +EXPRESSED OR IMPLIED. ANY USE IS AT YOUR OWN RISK**. + +Empirically, this collector works with most unmodified C programs, simply +by replacing `malloc` with `GC_malloc` calls, replacing `realloc` with +`GC_realloc` calls, and removing free calls. Exceptions are discussed +[here](http://www.hboehm.info/gc/issues.html). + +## Platforms + +The collector is not completely portable, but the distribution includes ports +to most standard PC and UNIX/Linux platforms. The collector should work +on Linux, *BSD, recent Windows versions, MacOS X, HP/UX, Solaris, Tru64, Irix +and a few other operating systems. Some ports are more polished than others. + +Irix pthreads, Linux threads, Win32 threads, Solaris threads (pthreads only), +HP/UX 11 pthreads, Tru64 pthreads, and MacOS X threads are supported in recent +versions. + +### Separately distributed ports + +For MacOS 9/Classic use, Patrick Beard's latest port is available from +`http://homepage.mac.com/pcbeard/gc/`. (Unfortunately, that's now quite dated. +I'm not in a position to test under MacOS. Although I try to incorporate +changes, it is impossible for me to update the project file.) + +Precompiled versions of the collector for NetBSD are available +[here](ftp://ftp.netbsd.org/pub/pkgsrc/current/pkgsrc/devel/boehm-gc/README.html). + + +[Debian Linux](http://www.debian.org/) includes prepackaged versions of the +collector. + +## Scalable multiprocessor versions + +Kenjiro Taura, Toshio Endo, and Akinori Yonezawa have made available +a [parallel collector](http://ieeexplore.ieee.org/abstract/document/1592629/) +based on this one. Their collector takes advantage of multiple processors +during a collection. Starting with GC v6.0alpha1 we also do this, though with +more modest processor scalability goals. Our approach is discussed briefly +in [this document](scale.md). + +## Some Collector Details + +The collector uses a [mark-sweep](http://www.hboehm.info/gc/complexity.html) +algorithm. It provides incremental and generational collection under operating +systems which provide the right kind of virtual memory support. (Currently +this includes SunOS[45], IRIX, OSF/1, Linux, and Windows, with varying +restrictions.) It allows [_finalization_](finalization.md) code to be invoked +when an object is collected. It can take advantage of type information +to locate pointers if such information is provided, but it is usually used +without such information. See the README and `gc.h` files in the distribution +for more details. + +For an overview of the implementation, see [here](gcdescr.md). + +The garbage collector distribution includes a C string (`cord.h`) package that +provides for fast concatenation and substring operations on long strings. +A simple curses- and win32-based editor that represents the entire file as +a cord is included as a sample application. + +Performance of the non-incremental collector is typically competitive with +`malloc`/`free` implementations. Both space and time overhead are likely to be +only slightly higher for programs written for `malloc`/`free` (see Detlefs, +Dosser and Zorn's +[Memory Allocation Costs in Large C and C++ Programs](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.30.3073&rep=rep1&type=ps)). +For programs allocating primarily very small objects, the collector may be +faster; for programs allocating primarily large objects it will be slower. +If the collector is used in a multi-threaded environment and configured for +thread-local allocation, it may in some cases significantly outperform +`malloc`/`free` allocation in time. + +We also expect that in many cases any additional overhead will be more than +compensated for by decreased copying etc. if programs are written and tuned +for garbage collection. + +# Further Reading: + +**The beginnings of a frequently asked questions list for this collector are +[here](http://www.hboehm.info/gc/faq.html)**. + +**The following provide information on garbage collection in general**: Paul +Wilson's [garbage collection ftp archive](ftp://ftp.cs.utexas.edu/pub/garbage) +and [GC survey](ftp://ftp.cs.utexas.edu/pub/garbage/gcsurvey.ps). + +The Ravenbrook +[Memory Management Reference](http://www.memorymanagement.org/). + +David Chase's [GC FAQ](http://www.iecc.com/gclist/GC-faq.html). + +Richard Jones' +[Garbage Collection Page](https://www.cs.kent.ac.uk/people/staff/rej/gc.html) +and his [book](http://www.cs.kent.ac.uk/people/staff/rej/gcbook/gcbook.html). + +**The following papers describe the collector algorithms we use and the +underlying design decisions at a higher level.** + +(Some of the lower level details can be found [here](gcdescr.md).) + +The first one is not available electronically due to copyright considerations. +Most of the others are subject to ACM copyright. + +Boehm, H., Dynamic Memory Allocation and Garbage Collection, +_Computers in Physics 9_, 3, May/June 1995, pp. 297-303. This is directed +at an otherwise sophisticated audience unfamiliar with memory allocation +issues. The algorithmic details differ from those in the implementation. There +is a related letter to the editor and a minor correction in the next issue. + +Boehm, H., and [M. Weiser](http://www.ubiq.com/hypertext/weiser/weiser.html), +[Garbage Collection in an Uncooperative Environment](http://www.hboehm.info/spe_gc_paper/), +_Software Practice & Experience_, September 1988, pp. 807-820. + +Boehm, H., A. Demers, and S. Shenker, +[Mostly Parallel Garbage Collection](http://www.hboehm.info/gc/papers/pldi91.ps.Z), +Proceedings of the ACM SIGPLAN '91 Conference on Programming Language Design +and Implementation, _SIGPLAN Notices 26_, 6 (June 1991), pp. 157-164. + +Boehm, H., +[Space Efficient Conservative Garbage Collection](http://www.hboehm.info/gc/papers/pldi93.ps.Z), +Proceedings of the ACM SIGPLAN '93 Conference on Programming Language Design +and Implementation, _SIGPLAN Notices 28_, 6 (June 1993), pp. 197-206. + +Boehm, H., Reducing Garbage Collector Cache Misses, +_Proceedings of the 2000 International Symposium on Memory Management_. +[Official version](http://portal.acm.org/citation.cfm?doid=362422.362438). +[Technical report](http://www.hpl.hp.com/techreports/2000/HPL-2000-99.html) +version. Describes the prefetch strategy incorporated into the collector for +some platforms. Explains why the sweep phase of a _mark-sweep_ collector +should not really be a distinct phase. + +M. Serrano, H. Boehm, Understanding Memory Allocation of Scheme Programs, +_Proceedings of the Fifth ACM SIGPLAN International Conference on Functional +Programming_, 2000, Montreal, Canada, pp. 245-256. +[Official version](http://dl.acm.org/citation.cfm?id=351264). Earlier +[Technical Report](http://www.hpl.hp.com/techreports/2000/HPL-2000-62.html) +version. Includes some discussion of the collector debugging facilities for +identifying causes of memory retention. + +Boehm, H., Fast Multiprocessor Memory Allocation and Garbage Collection, +[HP Labs Technical Report HPL 2000-165](http://www.hpl.hp.com/techreports/2000/HPL-2000-165.html). +Discusses the parallel collection algorithms, and presents some performance +results. + +Boehm, H., Bounding Space Usage of Conservative Garbage Collectors, +_Proceedings of the 2002 ACM SIGPLAN-SIGACT Symposium on Principles +of Programming Languages_, Jan. 2002, pp. 93-100. +[Official version](http://portal.acm.org/citation.cfm?doid=503272.503282). +[Technical report](http://www.hpl.hp.com/techreports/2001/HPL-2001-251.html) +version. Includes a discussion of a collector facility to much more reliably +test for the potential of unbounded heap growth. + +**The following papers discuss language and compiler restrictions necessary +to guaranteed safety of conservative garbage collection.** + +We thank John Levine and JCLT for allowing us to make the second paper +available electronically, and providing PostScript for the final version. + +Boehm, H., +[Simple Garbage-Collector-Safety](http://www.hboehm.info/gc/papers/pldi96.ps.gz), +Proceedings of the ACM SIGPLAN '96 Conference on Programming Language Design +and Implementation. + +Boehm, H., and D. Chase, +[A Proposal for Garbage-Collector-Safe C Compilation](http://www.hboehm.info/gc/papers/boecha.ps.gz), +_Journal of C Language Translation 4_, 2 (December 1992), pp. 126-141. + +**Other related information:** + +The Detlefs, Dosser and Zorn's +[Memory Allocation Costs in Large C and C++ Programs](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.30.3073&rep=rep1&type=ps). +This is a performance comparison of the Boehm-Demers-Weiser collector +to `malloc`/`free`, using programs written for `malloc`/`free`. + +Joel Bartlett's +[mostly copying conservative garbage collector for C++](ftp://gatekeeper.dec.com/pub/compaq/WRL/research-reports/WRL-TN-12.ps). + +John Ellis and David Detlef's +[Safe Efficient Garbage Collection for C++](http://dl.acm.org/citation.cfm?id=1267983) +proposal. + +Henry Baker's [paper collection](http://home.pipeline.com/%7Ehbaker1/). + +Slides for Hans Boehm's +[Allocation and GC Myths](http://www.hboehm.info/gc/myths.ps) talk. + +# Current users: + +Known current users of some variant of this collector include: + +The runtime system for [GCJ](https://gcc.gnu.org/onlinedocs/gcc-4.8.5/gcj/), +the static GNU java compiler. + +[W3m](http://w3m.sourceforge.net/), a text-based web browser. + +Some versions of the Xerox DocuPrint printer software. + +The [Mozilla](http://www.mozilla.org/) project, as leak detector. + +The [Mono](http://www.mono-project.com) project, an open source implementation +of the .NET development framework. + +The [DotGNU Portable.NET project](http://www.gnu.org/projects/dotgnu/), +another open source .NET implementation. + +The [Irssi IRC client](http://irssi.org/). + +[The Berkeley Titanium project](http://titanium.cs.berkeley.edu/). + +[The NAGWare f90 Fortran 90 compiler](http://www.nag.co.uk/nagware/NP/NP_desc.asp). + +Elwood Corporation's Eclipse Common Lisp system, C library, and translator. + +The [Bigloo Scheme](http://www-sop.inria.fr/mimosa/fp/Bigloo/) and +[Camloo ML compilers](https://github.com/samoht/camloo) written by Manuel +Serrano and others. + +Brent Benson's +[libscheme](http://www.cs.indiana.edu/scheme-repository/libscheme-vhll/final.html). + +The MzScheme scheme implementation. + +The +[University of Washington Cecil Implementation](http://www.cs.washington.edu/research/projects/cecil/www/cecil-home.html). + +[The Berkeley Sather implementation](http://www1.icsi.berkeley.edu/~sather/). + +[The Berkeley Harmonia Project](http://www.cs.berkeley.edu/~harmonia/harmonia/index.html). + +The +[Toba](http://www.cs.arizona.edu/projects/sumatra/toba/) Java Virtual Machine +to C translator. + +The [Gwydion Dylan compiler](http://www.gwydiondylan.org/). + +The +[GNU Objective C runtime](http://gcc.gnu.org/onlinedocs/gcc/Objective-C.html). + +[Macaulay 2](http://www.math.uiuc.edu/Macaulay2), a system to support research +in algebraic geometry and commutative algebra. + +The [Vesta](http://www.vestasys.org/) configuration management system. + +[Visual Prolog 6](http://www.visual-prolog.com/). + +[Asymptote LaTeX-compatible vector graphics language](http://asymptote.sf.net/). + +# More information on the BDWGC primary site + +[A simple illustration of how to build and use the collector](simple_example.md). + +[Description of alternate interfaces to the garbage collector](gcinterface.md). + +[Slides from an ISMM 2004 tutorial about the GC](http://www.hboehm.info/gc/04tutorial.pdf). + +[A FAQ (frequently asked questions) list](http://www.hboehm.info/gc/faq.html). + +[How to use the garbage collector as a leak detector](leak.md). + +[Some hints on debugging garbage collected applications](debugging.md). + +[An overview of the implementation of the garbage collector](gcdescr.md). + +[The data structure used for fast pointer lookups](tree.md). + +[Scalability of the collector to multiprocessors](scale.md). + +[Directory](http://www.hboehm.info/gc/gc_source/) containing the distribution +files of all garbage collector releases. It duplicates +[Download](https://github.com/ivmai/bdwgc/wiki/Download) page on GitHub. + +# More background information + +[An attempt to establish a bound on space usage of conservative garbage collectors](http://www.hboehm.info/gc/bounds.html). + +[Mark-sweep versus copying garbage collectors and their complexity](http://www.hboehm.info/gc/complexity.html). + +[Pros and cons of conservative garbage collectors, in comparison to other collectors](http://www.hboehm.info/gc/conservative.html). + +[Issues related to garbage collection vs. manual memory management in C/C++](http://www.hboehm.info/gc/issues.html). + +[An example of a case in which garbage collection results in a much faster implementation as a result of reduced synchronization](http://www.hboehm.info/gc/example.html). + +[Slide set discussing performance of nonmoving garbage collectors](http://www.hboehm.info/gc/nonmoving/). + +[Slide set discussing _Destructors, Finalizers, and Synchronization_, POPL 2003](http://www.hboehm.info/popl03/web/). + +[Paper corresponding to above slide set](http://portal.acm.org/citation.cfm?doid=604131.604153) +([Technical Report](http://www.hpl.hp.com/techreports/2002/HPL-2002-335.html) +version). + +[A Java/Scheme/C/C++ garbage collection benchmark](http://www.hboehm.info/gc/gc_bench/). + +[Slides for talk on memory allocation myths](http://www.hboehm.info/gc/myths.ps). + +[Slides for OOPSLA 98 garbage collection talk](http://www.hboehm.info/gc/gctalk.ps). + +[Related papers](http://www.hboehm.info/gc/papers/). + +# Contacts and new release announcements + +GitHub and Stack Overflow are the major two places for communication. + +Technical questions (how to, how does it work, etc.) should be posted +to [Stack Overflow](https://stackoverflow.com/questions/tagged/boehm-gc) with +_boehm-gc_ tag. + +To contribute, please rebase your code to the latest +[master](https://github.com/ivmai/bdwgc/tree/master/) and submit +a [pull request](https://github.com/ivmai/bdwgc/pulls) to GitHub. + +To report a bug, or propose (request) a new feature, create +a [GitHub issue](https://github.com/ivmai/bdwgc/issues). Please make sure +it has not been reported yet by someone else. + +To receive notifications on every release, please subscribe to +[Releases RSS feed](https://github.com/ivmai/bdwgc/releases.atom). +Notifications on all issues and pull requests are available +by [watching](https://github.com/ivmai/bdwgc/watchers) the project. + +Mailing lists (bdwgc-announce@lists.opendylan.org, bdwgc@lists.opendylan.org, +and the former gc-announce@linux.hpl.hp.com and gc@linux.hpl.hp.com) are not +used at this moment. Their content is available +in +[bdwgc-announce](https://github.com/ivmai/bdwgc/files/1037650/bdwgc-announce-mailing-list-archive-2014_02.tar.gz) +and +[bdwgc](https://github.com/ivmai/bdwgc/files/1038163/bdwgc-mailing-list-archive-2017_04.tar.gz) +archive files, respectively. The gc list archive may also be read +at [Narkive](http://bdwgc.opendylan.narkive.com). + +Some prior discussion of the collector has taken place on the gcc java mailing +list, whose archives appear [here](http://gcc.gnu.org/ml/java/), and also +on [gclist@iecc.com](http://lists.tunes.org/mailman/listinfo/gclist). diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/porting.md b/unity-2019.4.24f1-mbe/external/bdwgc/doc/porting.md new file mode 100644 index 000000000..bc2b253c1 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/porting.md @@ -0,0 +1,262 @@ +# Conservative Garbage Collector Porting Directions + +The collector is designed to be relatively easy to port, but is not portable +code per se. The collector inherently has to perform operations, such +as scanning the stack(s), that are not possible in portable C code. + +All of the following assumes that the collector is being ported to +a byte-addressable 32- or 64-bit machine. Currently all successful ports +to 64-bit machines involve LP64 targets. The code base includes some +provisions for P64 targets (notably Win64), but that has not been tested. You +are hereby discouraged from attempting a port to non-byte-addressable, +or 8-bit, or 16-bit machines. + +The difficulty of porting the collector varies greatly depending on the needed +functionality. In the simplest case, only some small additions are needed for +the `include/private/gcconfig.h` file. This is described in the following +section. Later sections discuss some of the optional features, which typically +involve more porting effort. + +Note that the collector makes heavy use of `ifdef`s. Unlike some other +software projects, we have concluded repeatedly that this is preferable +to system dependent files, with code duplicated between the files. However, +to keep this manageable, we do strongly believe in indenting `ifdef`s +correctly (for historical reasons usually without the leading sharp sign). +(Separate source files are of course fine if they do not result in code +duplication.) + +## Adding Platforms to gcconfig.h + +If neither thread support, nor tracing of dynamic library data is required, +these are often the only changes you will need to make. + +The `gcconfig.h` file consists of three sections: + + 1. A section that defines GC-internal macros that identify the architecture + (e.g. `IA64` or `I386`) and operating system (e.g. `LINUX` or `MSWIN32`). + This is usually done by testing predefined macros. By defining our own + macros instead of using the predefined ones directly, we can impose a bit + more consistency, and somewhat isolate ourselves from compiler differences. + It is relatively straightforward to add a new entry here. But please try + to be consistent with the existing code. In particular, 64-bit variants + of 32-bit architectures general are _not_ treated as a new architecture. + Instead we explicitly test for 64-bit-ness in the few places in which + it matters. (The notable exception here is `I386` and `X86_64`. This + is partially historical, and partially justified by the fact that there are + arguably more substantial architecture and ABI differences here than for + RISC variants.) On GNU-based systems, `cpp -dM empty_source_file.c` seems + to generate a set of predefined macros. On some other systems, the "verbose" + compiler option may do so, or the manual page may list them. + 2. A section that defines a small number of platform-specific macros, which + are then used directly by the collector. For simple ports, this is where + most of the effort is required. We describe the macros below. This section + contains a subsection for each architecture (enclosed in a suitable `ifdef`. + Each subsection usually contains some architecture-dependent defines, + followed by several sets of OS-dependent defines, again enclosed in + `ifdef`s. + + 3. A section that fills in defaults for some macros left undefined in the + preceding section, and defines some other macros that rarely need adjustment + for new platforms. You will typically not have to touch these. If you are + porting to an OS that was previously completely unsupported, it is likely + that you will need to add another clause to the definition of `GET_MEM`. + +The following macros must be defined correctly for each architecture and +operating system: + + * `MACH_TYPE` - Defined to a string that represents the machine + architecture. Usually just the macro name used to identify the architecture, + but enclosed in quotes. + * `OS_TYPE` - Defined to a string that represents the operating system name. + Usually just the macro name used to identify the operating system, but + enclosed in quotes. + * `CPP_WORDSZ` - The word size in bits as a constant suitable for + preprocessor tests, i.e. without casts or `sizeof` expressions. Currently + always defined as either 64 or 32. For platforms supporting both 32- and + 64-bit ABIs, this should be conditionally defined depending on the current + ABI. There is a default of 32. + * `ALIGNMENT` - Defined to be the largest _N_ such that all pointer + are guaranteed to be aligned on _N_-byte boundaries. Defining it to be _1_ + will always work, but perform poorly. For all modern 32-bit platforms, this + is 4. For all modern 64-bit platforms, this is 8. Whether or not X86 + qualifies as a modern architecture here is compiler- and OS-dependent. + * `DATASTART` - The beginning of the main data segment. The collector will + trace all memory between `DATASTART` and `DATAEND` for root pointers. + On some platforms, this can be defined to a constant address, though + experience has shown that to be risky. Ideally the linker will define + a symbol (e.g. `_data` whose address is the beginning of the data segment. + Sometimes the value can be computed using the `GC_SysVGetDataStart` + function. Not used if either the next macro is defined, or if dynamic + loading is supported, and the dynamic loading support defines a function + `GC_register_main_static_data` which returns false. + * `SEARCH_FOR_DATA_START` - If this is defined `DATASTART` will be defined + to a dynamically computed value which is obtained by starting with the + address of `_end` and walking backwards until non-addressable memory + is found. This often works on Posix-like platforms. It makes it harder + to debug client programs, since startup involves generating and catching + a segmentation fault, which tends to confuse users. + * `DATAEND` - Set to the end of the main data segment. Defaults to `end`, + where that is declared as an array. This works in some cases, since the + linker introduces a suitable symbol. + * `DATASTART2`, `DATAEND2` - Some platforms have two discontiguous main data + segments, e.g. for initialized and uninitialized data. If so, these two + macros should be defined to the limits of the second main data segment. + * `STACK_GROWS_UP` - Should be defined if the stack (or thread stacks) grow + towards higher addresses. (This appears to be true only on PA-RISC. If your + architecture has more than one stack per thread, and is not supported yet, + you will need to do more work. Grep for "IA64" in the source for an + example.) + * `STACKBOTTOM` - Defined to be the cool end of the stack, which is usually + the highest address in the stack. It must bound the region of the stack that + contains pointers into the GC heap. With thread support, this must be the + cold end of the main stack, which typically cannot be found in the same way + as the other thread stacks. If this is not defined and none of the following + three macros is defined, client code must explicitly set `GC_stackbottom` + to an appropriate value before calling `GC_INIT` or any other `GC_` routine. + * `LINUX_STACKBOTTOM` - May be defined instead of `STACKBOTTOM`. If defined, + then the cold end of the stack will be determined Currently we usually read + it from `/proc`. + * `HEURISTIC1` - May be defined instead of `STACKBOTTOM`. `STACK_GRAN` + should generally also be redefined. The cold end of the stack is determined + by taking an address inside `GC_init`s frame, and rounding it up to the next + multiple of `STACK_GRAN`. This works well if the stack base is always + aligned to a large power of two. (`STACK_GRAN` is predefined to 0x1000000, + which is rarely optimal.) + * `HEURISTIC2` - May be defined instead of `STACKBOTTOM`. The cold end + of the stack is determined by taking an address inside `GC_init`s frame, + incrementing it repeatedly in small steps (decrement if `STACK_GROWS_UP`), + and reading the value at each location. We remember the value when the first + Segmentation violation or Bus error is signaled, round that to the nearest + plausible page boundary, and use that as the stack base. + * `DYNAMIC_LOADING` - Should be defined if `dyn_load.c` has been updated for + this platform and tracing of dynamic library roots is supported. + * `MPROTECT_VDB`, `PROC_VDB` - May be defined if the corresponding + _virtual dirty bit_ implementation in `os_dep.c` is usable on this platform. + This allows incremental/generational garbage collection. `MPROTECT_VDB` + identifies modified pages by write protecting the heap and catching faults. + `PROC_VDB` uses the /proc primitives to read dirty bits. + * `PREFETCH`, `GC_PREFETCH_FOR_WRITE` - The collector uses `PREFETCH(x)` + to preload the cache with the data at _x_ address. This defaults to a no-op. + * `CLEAR_DOUBLE` - If `CLEAR_DOUBLE` is defined, then `CLEAR_DOUBLE(x)` + is used as a fast way to clear the two words at `GC_malloc`-aligned address + _x_. By default, word stores of 0 are used instead. + * `HEAP_START` - May be defined as the initial address hint for mmap-based + allocation. + +## Additional requirements for a basic port + +In some cases, you may have to add additional platform-specific code to other +files. A likely candidate is the implementation +of `GC_with_callee_saves_pushed` in `mach_dep.c`. This ensure that register +contents that the collector must trace from are copied to the stack. Typically +this can be done portably, but on some platforms it may require assembly code, +or just tweaking of conditional compilation tests. + +For GC v7, if your platform supports `getcontext`, then defining the macro +`UNIX_LIKE` for your OS in `gcconfig.h` (if it is not defined there yet) +is likely to solve the problem. otherwise, if you are using gcc, +`_builtin_unwind_init` will be used, and should work fine. If that is not +applicable either, the implementation will try to use `setjmp`. This will work +if your `setjmp` implementation saves all possibly pointer-valued registers +into the buffer, as opposed to trying to unwind the stack at `longjmp` time. +The `setjmp_test` test tries to determine this, but often does not get it +right. + +In GC v6.x versions of the collector, tracing of registers was more commonly +handled with assembly code. In GC v7, this is generally to be avoided. + +Most commonly `os_dep.c` will not require attention, but see below. + +## Thread support + +Supporting threads requires that the collector be able to find and suspend all +threads potentially accessing the garbage-collected heap, and locate any state +associated with each thread that must be traced. + +The functionality needed for thread support is generally implemented in one or +more files specific to the particular thread interface. For example, somewhat +portable pthread support is implemented in `pthread_support.c` and +`pthread_stop_world.c`. The essential functionality consists of: + + * `GC_stop_world` - Stops all threads which may access the garbage collected + heap, other than the caller; + * `GC_start_world` - Restart other threads; + * `GC_push_all_stacks` - Push the contents of all thread stacks (or, + at least, of pointer-containing regions in the thread stacks) onto the mark + stack. + +These very often require that the garbage collector maintain its own data +structures to track active threads. + +In addition, `LOCK` and `UNLOCK` must be implemented in `gc_locks.h`. + +The easiest case is probably a new pthreads platform on which threads can be +stopped with signals. In this case, the changes involve: + + 1. Introducing a suitable `GC_xxx_THREADS` macro, which should + be automatically defined by `gc_config_macros.h` in the right cases. + It should also result in a definition of `GC_PTHREADS`, as for the existing + cases. + 2. For GC v7, ensuring that the `atomic_ops` package at least minimally + supports the platform. If incremental GC is needed, or if pthread locks + do not perform adequately as the allocation lock, you will probably need + to ensure that a sufficient `atomic_ops` port exists for the platform + to provided an atomic test and set operation. The latest GC code can use + GCC atomic intrinsics instead of `atomic_ops` package (see + `include/private/gc_atomic_ops.h`). + 3. Making any needed adjustments to `pthread_stop_world.c` and + `pthread_support.c`. Ideally none should be needed. In fact, not all of this + is as well standardized as one would like, and outright bugs requiring + workarounds are common. Non-preemptive threads packages will probably + require further work. Similarly thread-local allocation and parallel marking + requires further work in `pthread_support.c`, and may require better + `atomic_ops` support. + +## Dynamic library support + +So long as `DATASTART` and `DATAEND` are defined correctly, the collector will +trace memory reachable from file scope or `static` variables defined as part +of the main executable. This is sufficient if either the program is statically +linked, or if pointers to the garbage-collected heap are never stored +in non-stack variables defined in dynamic libraries. + +If dynamic library data sections must also be traced, then: + + * `DYNAMIC_LOADING` must be defined in the appropriate section of + `gcconfig.h`. + * An appropriate versions of the functions `GC_register_dynamic_libraries` + should be defined in `dyn_load.c`. This function should invoke + `GC_cond_add_roots(_region_start, region_end_, TRUE)` on each dynamic + library data section. + +Implementations that scan for writable data segments are error prone, +particularly in the presence of threads. They frequently result in race +conditions when threads exit and stacks disappear. They may also accidentally +trace large regions of graphics memory, or mapped files. On at least one +occasion they have been known to try to trace device memory that could not +safely be read in the manner the GC wanted to read it. + +It is usually safer to walk the dynamic linker data structure, especially +if the linker exports an interface to do so. But beware of poorly documented +locking behavior in this case. + +## Incremental GC support + +For incremental and generational collection to work, `os_dep.c` must contain +a suitable _virtual dirty bit_ implementation, which allows the collector +to track which heap pages (assumed to be a multiple of the collectors block +size) have been written during a certain time interval. The collector provides +several implementations, which might be adapted. The default (`DEFAULT_VDB`) +is a placeholder which treats all pages as having been written. This ensures +correctness, but renders incremental and generational collection essentially +useless. + +## Stack traces for debug support + +If stack traces in objects are need for debug support, `GC_dave_callers` and +`GC_print_callers` must be implemented. + +## Disclaimer + +This is an initial pass at porting guidelines. Some things have no doubt been +overlooked. diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/scale.md b/unity-2019.4.24f1-mbe/external/bdwgc/doc/scale.md new file mode 100644 index 000000000..75fb635be --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/scale.md @@ -0,0 +1,169 @@ +# Garbage collector scalability + +In its default configuration, the Boehm-Demers-Weiser garbage collector is not +thread-safe. It can be made thread-safe for a number of environments +by building the collector with `-DGC_THREADS` compilation flag. This has +primarily two effects: + + 1. It causes the garbage collector to stop all other threads when it needs + to see a consistent memory state. + 2. It causes the collector to acquire a lock around essentially all + allocation and garbage collection activity. Since a single lock is used for + all allocation-related activity, only one thread can be allocating + or collecting at one point. This inherently limits performance + of multi-threaded applications on multiprocessors. + +On most platforms, the allocator/collector lock is implemented as a spin lock +with exponential back-off. Longer wait times are implemented by yielding +and/or sleeping. If a collection is in progress, the pure spinning stage +is skipped. This has the advantage that uncontested and thus most uniprocessor +lock acquisitions are very cheap. It has the disadvantage that the application +may sleep for small periods of time even when there is work to be done. And +threads may be unnecessarily woken up for short periods. Nonetheless, this +scheme empirically outperforms native queue-based mutual exclusion +implementations in most cases, sometimes drastically so. + +## Options for enhanced scalability + +Version 6.0 of the collector adds two facilities to enhance collector +scalability on multiprocessors. As of 6.0alpha1, these are supported only +under Linux on X86 and IA64 processors, though ports to other otherwise +supported Pthreads platforms should be straightforward. They are intended +to be used together. + + * Building the collector with `-DPARALLEL_MARK` allows the collector to run + the mark phase in parallel in multiple threads, and thus on multiple + processors. The mark phase typically consumes the large majority of the + collection time. Thus this largely parallelizes the garbage collector + itself, though not the allocation process. Currently the marking + is performed by the thread that triggered the collection, together with + _N_ - 1 dedicated threads, where _N_ is the number of processors detected + by the collector. The dedicated threads are created once at initialization + time. A second effect of this flag is to switch to a more concurrent + implementation of `GC_malloc_many`, so that free lists can be built, and + memory can be cleared, by more than one thread concurrently. + * Building the collector with `-DTHREAD_LOCAL_ALLOC` adds support for + thread-local allocation. This causes `GC_malloc`, `GC_malloc_atomic`, and + `GC_gcj_malloc` to be redefined to perform thread-local allocation. + +Memory returned from thread-local allocators is completely interchangeable +with that returned by the standard allocators. It may be used by other +threads. The only difference is that, if the thread allocates enough memory +of a certain kind, it will build a thread-local free list for objects of that +kind, and allocate from that. This greatly reduces locking. The thread-local +free lists are refilled using `GC_malloc_many`. + +An important side effect of this flag is to replace the default +spin-then-sleep lock to be replaced by a spin-then-queue based implementation. +This _reduces performance_ for the standard allocation functions, though +it usually improves performance when thread-local allocation is used heavily, +and thus the number of short-duration lock acquisitions is greatly reduced. + +## The Parallel Marking Algorithm + +We use an algorithm similar to +[that developed by Endo, Taura, and Yonezawa](http://www.yl.is.s.u-tokyo.ac.jp/gc/) +at the University of Tokyo. However, the data structures and implementation +are different, and represent a smaller change to the original collector +source, probably at the expense of extreme scalability. Some of the +refinements they suggest, e.g. splitting large objects, were also incorporated +into out approach. + +The global mark stack is transformed into a global work queue. Unlike the +usual case, it never shrinks during a mark phase. The mark threads remove +objects from the queue by copying them to a local mark stack and changing the +global descriptor to zero, indicating that there is no more work to be done +for this entry. This removal is done with no synchronization. Thus it is +possible for more than one worker to remove the same entry, resulting in some +work duplication. + +The global work queue grows only if a marker thread decides to return some +of its local mark stack to the global one. This is done if the global queue +appears to be running low, or if the local stack is in danger of overflowing. +It does require synchronization, but should be relatively rare. + +The sequential marking code is reused to process local mark stacks. Hence the +amount of additional code required for parallel marking is minimal. + +It should be possible to use generational collection in the presence of the +parallel collector, by calling `GC_enable_incremental`. This does not result +in fully incremental collection, since parallel mark phases cannot currently +be interrupted, and doing so may be too expensive. + +Gcj-style mark descriptors do not currently mix with the combination of local +allocation and incremental collection. They should work correctly with one or +the other, but not both. + +The number of marker threads is set on startup to the number of available +processors (or to the value of the `GC_NPROCS` environment variable). If only +a single processor is detected, parallel marking is disabled. + +Note that setting `GC_NPROCS` to 1 also causes some lock acquisitions inside +the collector to immediately yield the processor instead of busy waiting +first. In the case of a multiprocessor and a client with multiple +simultaneously runnable threads, this may have disastrous performance +consequences (e.g. a factor of 10 slowdown). + +## Performance + +We conducted some simple experiments with a version of +[our GC benchmark](http://www.hboehm.info/gc/gc_bench/) that was slightly +modified to run multiple concurrent client threads in the same address space. +Each client thread does the same work as the original benchmark, but they +share a heap. This benchmark involves very little work outside of memory +allocation. This was run with GC 6.0alpha3 on a dual processor Pentium III/500 +machine under Linux 2.2.12. + +Running with a thread-unsafe collector, the benchmark ran in 9 seconds. With +the simple thread-safe collector, built with `-DGC_THREADS`, the execution +time increased to 10.3 seconds, or 23.5 elapsed seconds with two clients. (The +times for the `malloc`/`free` version with glibc `malloc` are 10.51 (standard +library, pthreads not linked), 20.90 (one thread, pthreads linked), and 24.55 +seconds respectively. The benchmark favors a garbage collector, since most +objects are small.) + +The following table gives execution times for the collector built with +parallel marking and thread-local allocation support +(`-DGC_THREADS -DPARALLEL_MARK -DTHREAD_LOCAL_ALLOC`). We tested the client +using either one or two marker threads, and running one or two client threads. +Note that the client uses thread local allocation exclusively. With +`-DTHREAD_LOCAL_ALLOC` the collector switches to a locking strategy that +is better tuned to less frequent lock acquisition. The standard allocation +primitives thus perform slightly worse than without `-DTHREAD_LOCAL_ALLOC`, +and should be avoided in time-critical code. + +(The results using `pthread_mutex_lock` directly for allocation locking would +have been worse still, at least for older versions of linuxthreads. With +`-DTHREAD_LOCAL_ALLOC`, we first repeatedly try to acquire the lock with +`pthread_mutex_try_lock`, busy-waiting between attempts. After a fixed number +of attempts, we use `pthread_mutex_lock`.) + +These measurements do not use incremental collection, nor was prefetching +enabled in the marker. We used the C version of the benchmark. All +measurements are in elapsed seconds on an unloaded machine. + +Number of threads| 1 marker thread (secs.) | 2 marker threads (secs.) +---|---|--- +1 client| 10.45| 7.85 | 2 clients| 19.95| 12.3 + +The execution time for the single threaded case is slightly worse than with +simple locking. However, even the single-threaded benchmark runs faster than +even the thread-unsafe version if a second processor is available. The +execution time for two clients with thread local allocation time is only 1.4 +times the sequential execution time for a single thread in a thread-unsafe +environment, even though it involves twice the client work. That represents +close to a factor of 2 improvement over the 2 client case with the old +collector. The old collector clearly still suffered from some contention +overhead, in spite of the fact that the locking scheme had been fairly well +tuned. + +Full linear speedup (i.e. the same execution time for 1 client on one +processor as 2 clients on 2 processors) is probably not achievable on this +kind of hardware even with such a small number of processors, since the memory +system is a major constraint for the garbage collector, the processors usually +share a single memory bus, and thus the aggregate memory bandwidth does not +increase in proportion to the number of processors. + +These results are likely to be very sensitive to both hardware and OS issues. +Preliminary experiments with an older Pentium Pro machine running an older +kernel were far less encouraging. diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/simple_example.md b/unity-2019.4.24f1-mbe/external/bdwgc/doc/simple_example.md new file mode 100644 index 000000000..e19d56863 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/simple_example.md @@ -0,0 +1,181 @@ +# Using the Garbage Collector: A simple example + +The following consists of step-by-step instructions for building and using the +collector. We'll assume a Linux/gcc platform and a single-threaded +application. The green text contains information about other platforms +or scenarios. It can be skipped, especially on first reading. + +## Building the collector + +If you have not so yet, unpack the collector and enter the newly created +directory with: + + + tar xvfz gc-.tar.gz + cd gc- + + +You can configure, build, and install the collector in a private directory, +say /home/xyz/gc, with the following commands: + + + ./configure --prefix=/home/xyz/gc --disable-threads + make + make check + make install + + +Here the `make check` command is optional, but highly recommended. It runs +a basic correctness test which usually takes well under a minute. + +### Other platforms + +On non-Unix, non-Linux platforms, the collector is usually built by copying +the appropriate makefile (see the platform-specific README in doc/README.xxx +in the distribution) to the file "Makefile", and then typing `make` (or +`nmake` or ...). This builds the library in the source tree. You may want +to move it and the files in the include directory to a more convenient place. + +If you use a makefile that does not require running a configure script, you +should first look at the makefile, and adjust any options that are documented +there. + +If your platform provides a `make` utility, that is generally preferred +to platform- and compiler- dependent "project" files. (At least that is the +strong preference of the would-be maintainer of those project files.) + +### Threads + +If you do not need thread support, configure the collector with: + + + --disable-threads + + +Alternatively, if your target is a real old-fashioned uniprocessor (no +"hyperthreading", etc.), you may just want to turn off parallel marking with +`--disable-parallel-mark`. + +### C++ + +You will need to include the C++ support, which unfortunately tends to be +among the least portable parts of the collector, since it seems to rely +on some corner cases of the language. On Linux, it suffices to add +`--enable-cplusplus` to the configure options. + +## Writing the program + +You will need to include "gc.h" at the beginning of every file that allocates +memory through the garbage collector. Call `GC_MALLOC` wherever you would have +call `malloc`. This initializes memory to zero like `calloc`; there is no need +to explicitly clear the result. + +If you know that an object will not contain pointers to the garbage-collected +heap, and you don't need it to be initialized, call `GC_MALLOC_ATOMIC` +instead. + +A function `GC_FREE` is provided but need not be called. For very small +objects, your program will probably perform better if you do not call it, and +let the collector do its job. + +A `GC_REALLOC` function behaves like the C library `realloc`. It allocates +uninitialized pointer-free memory if the original object was allocated that +way. + +The following program `loop.c` is a trivial example: + + + #include "gc.h" + #include + #include + + int main(void) { + int i; + + GC_INIT(); + for (i = 0; i < 10000000; ++i) { + int **p = (int **) GC_MALLOC(sizeof(int *)); + int *q = (int *) GC_MALLOC_ATOMIC(sizeof(int)); + assert(*p == 0); + *p = (int *) GC_REALLOC(q, 2 * sizeof(int)); + if (i % 100000 == 0) + printf("Heap size = %d\n", GC_get_heap_size()); + } + return 0; + } + + +### Interaction with the system malloc + +It is usually best not to mix garbage-collected allocation with the system +`malloc`-`free`. If you do, you need to be careful not to store pointers +to the garbage-collected heap in memory allocated with the system `malloc`. + +### Other Platforms + +On some other platforms it is necessary to call `GC_INIT` from the main +program, which is presumed to be part of the main executable, not a dynamic +library. This can never hurt, and is thus generally good practice. + +### Threads + +For a multi-threaded program, some more rules apply: + + * Files that either allocate through the GC _or make thread-related calls_ + should first define the macro `GC_THREADS`, and then include `gc.h`. On some + platforms this will redefine some threads primitives, e.g. to let the + collector keep track of thread creation. + +### C++ + +In the case of C++, you need to be especially careful not to store pointers +to the garbage-collected heap in areas that are not traced by the collector. +The collector includes some _alternate interfaces_ to make that easier. + +### Debugging + +Additional debug checks can be performed by defining `GC_DEBUG` before +including `gc.h`. Additional options are available if the collector is also +built with `--enable-gc-debug` (`--enable-full-debug` in some older versions) +and all allocations are performed with `GC_DEBUG` defined. + +### What if I can't rewrite/recompile my program? + +You may be able to build the collector with `--enable-redirect-malloc` and set +the `LD_PRELOAD` environment variable to point to the resulting library, thus +replacing the standard `malloc` with its garbage-collected counterpart. This +is rather platform dependent. See the _GC leak detection documentation_ for +some more details. + +## Compiling and linking + +The above application `loop.c` test program can be compiled and linked with: + + + cc -I/home/xyz/gc/include loop.c /home/xyz/gc/lib/libgc.a -o loop + + +The `-I` option directs the compiler to the right include directory. In this +case, we list the static library directly on the compile line; the dynamic +library could have been used instead, provided we arranged for the dynamic +loader to find it, e.g. by setting `LD_LIBRARY_PATH`. + +### Threads + +On pthread platforms, you will of course also have to link with `-lpthread`, +and compile with any thread-safety options required by your compiler. On some +platforms, you may also need to link with `-ldl` or `-lrt`. Looking +at `tools/threadlibs.c` should give you the appropriate list if a plain +`-lpthread` does not work. + +## Running the executable + +The executable can of course be run normally, e.g. by typing: + + + ./loop + + +The operation of the collector is affected by a number of environment +variables. For example, setting `GC_PRINT_STATS` produces some GC statistics +on stdout. See `README.environment` in the distribution for details. diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/doc/tree.md b/unity-2019.4.24f1-mbe/external/bdwgc/doc/tree.md new file mode 100644 index 000000000..cdf1aa7b5 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/doc/tree.md @@ -0,0 +1,175 @@ +# Two-Level Tree Structure for Fast Pointer Lookup + +The Boehm-Demers-Weiser conservative Garbage Collector uses a 2-level tree +data structure to aid in fast pointer identification. This data structure +is described in a bit more detail here, since + + 1. Variations of the data structure are more generally useful. + 2. It appears to be hard to understand by reading the code. + 3. Some other collectors appear to use inferior data structures to solve the + same problem. + 4. It is central to fast collector operation. A candidate pointer + is divided into three sections, the _high_, _middle_, and _low_ bits. The + exact division between these three groups of bits is dependent on the + detailed collector configuration. + +The high and middle bits are used to look up an entry in the table described +here. The resulting table entry consists of either a block descriptor +(`struct hblkhdr *` or `hdr *`) identifying the layout of objects in the +block, or an indication that this address range corresponds to the middle of +a large block, together with a hint for locating the actual block descriptor. +Such a hint consist of a displacement that can be subtracted from the middle +bits of the candidate pointer without leaving the object. + +In either case, the block descriptor (`struct hblkhdr`) refers to a table +of object starting addresses (the `hb_map` field). The starting address table +is indexed by the low bits if the candidate pointer. The resulting entry +contains a displacement to the beginning of the object, or an indication that +this cannot be a valid object pointer. (If all interior pointer are +recognized, pointers into large objects are handled specially, +as appropriate.) + +## The Tree + +The rest of this discussion focuses on the two level data structure used +to map the high and middle bits to the block descriptor. + +The high bits are used as an index into the `GC_top_index` (really +`GC_arrays._top_index`) array. Each entry points to a `bottom_index` data +structure. This structure in turn consists mostly of an array `index` indexed +by the middle bits of the candidate pointer. The `index` array contains the +actual `hdr` pointers. + +Thus a pointer lookup consists primarily of a handful of memory references, +and can be quite fast: + + 1. The appropriate `bottom_index` pointer is looked up in `GC_top_index`, + based on the high bits of the candidate pointer. + 2. The appropriate `hdr` pointer is looked up in the `bottom_index` + structure, based on the middle bits. + 3. The block layout map pointer is retrieved from the `hdr` structure. (This + memory reference is necessary since we try to share block layout maps.) + 4. The displacement to the beginning of the object is retrieved from the + above map. + +In order to conserve space, not all `GC_top_index` entries in fact point +to distinct `bottom_index` structures. If no address with the corresponding +high bits is part of the heap, then the entry points to `GC_all_nils`, +a single `bottom_index` structure consisting only of `NULL` `hdr` pointers. + +`Bottom_index` structures contain slightly more information than just `hdr` +pointers. The `asc_link` field is used to link all `bottom_index` structures +in ascending order for fast traversal. This list is pointed to be +`GC_all_bottom_indices`. It is maintained with the aid of `key` field that +contains the high bits corresponding to the `bottom_index`. + +## 64-bit addresses + +In the case of 64-bit addresses, this picture is complicated slightly by the +fact that one of the index structures would have to be huge to cover the +entire address space with a two level tree. We deal with this by turning +`GC_top_index` into a chained hash table, instead of a simple array. This adds +a `hash_link` field to the `bottom_index` structure. + +The _hash function_ consists of dropping the high bits. This is cheap +to compute, and guarantees that there will be no collisions if the heap +is contiguous and not excessively large. + +## A picture + +The following is an _ASCII_ diagram of the data structure used by GC_base (as +of GC v3.7, Apr 21, 1994). This was contributed by Dave Barrett. + + + 63 LOG_TOP_SZ[11] LOG_BOTTOM_SZ[10] LOG_HBLKSIZE[13] + +------------------+----------------+------------------+------------------+ + p:| | TL_HASH(hi) | | HBLKDISPL(p) | + +------------------+----------------+------------------+------------------+ + \-----------------------HBLKPTR(p)-------------------/ + \------------hi-------------------/ + \______ ________/ \________ _______/ \________ _______/ + V V V + | | | + GC_top_index[] | | | + --- +--------------+ | | | + ^ | | | | | + | | | | | | + TOP +--------------+<--+ | | + _SZ +-<| [] | * | | + (items)| +--------------+ if 0 < bi< HBLKSIZE | | + | | | | then large object | | + | | | | starts at the bi'th | | + v | | | HBLK before p. | i | + --- | +--------------+ | (word- | + v | aligned) | + bi= |GET_BI(p){->hash_link}->key==hi | | + v | | + | (bottom_index) \ scratch_alloc'd | | + | ( struct bi ) / by get_index() | | + --- +->+--------------+ | | + ^ | | | | + ^ | | | | + BOTTOM | | ha=GET_HDR_ADDR(p) | | + _SZ(items)+--------------+<----------------------+ +-------+ + | +--<| index[] | | + | | +--------------+ GC_obj_map: v + | | | | from / +-+-+-----+-+-+-+-+ --- + v | | | GC_add < 0| | | | | | | | ^ + --- | +--------------+ _map_entry \ +-+-+-----+-+-+-+-+ | + | | asc_link | +-+-+-----+-+-+-+-+ MAXOBJSZ + | +--------------+ +-->| | | j | | | | | +1 + | | key | | +-+-+-----+-+-+-+-+ | + | +--------------+ | +-+-+-----+-+-+-+-+ | + | | hash_link | | | | | | | | | | v + | +--------------+ | +-+-+-----+-+-+-+-+ --- + | | |<--MAX_OFFSET--->| + | | (bytes) + HDR(p)| GC_find_header(p) | |<--MAP_ENTRIES-->| + | \ from | =HBLKSIZE/WORDSZ + | (hdr) (struct hblkhdr) / alloc_hdr() | (1024 on Alpha) + +-->+----------------------+ | (8/16 bits each) + GET_HDR(p)| word hb_sz (words) | | + +----------------------+ | + | struct hblk *hb_next | | + +----------------------+ | + |mark_proc hb_mark_proc| | + +----------------------+ | + | char * hb_map |>-------------+ + +----------------------+ + | ushort hb_obj_kind | + +----------------------+ + | hb_last_reclaimed | + --- +----------------------+ + ^ | | + MARK_BITS| hb_marks[] | * if hdr is free, hb_sz is the size of + _SZ(words)| | a heap chunk (struct hblk) of at least + v | | MININCR*HBLKSIZE bytes (below), + --- +----------------------+ otherwise, size of each object in chunk. + + +Dynamic data structures above are interleaved throughout the heap in blocks +of size `MININCR * HBLKSIZE` bytes as done by `gc_scratch_alloc` which cannot +be freed; free lists are used (e.g. `alloc_hdr`). `HBLK`'s below are +collected. + + + (struct hblk) HDR_BYTES + --- +----------------------+ < HBLKSIZE --- (bytes) + ^ +-----hb_body----------+ (and WORDSZ) ^ --- --- + | | | aligned | ^ ^ + | | | | hb_sz | + | | | | (words) | + | | Object 0 | | | | + | | | i |(word- v | + | + - - - - - - - - - - -+ --- (bytes)|aligned) --- | + | | | ^ | ^ | + | | | j (words) | | | + n * | Object 1 | v v hb_sz BODY_SZ + HBLKSIZE | |--------------- | (words) + (bytes) | | v MAX_OFFSET + | + - - - - - - - - - - -+ --- (bytes) + | | | !ALL_INTERIOR_POINTERS ^ | + | | | sets j only for hb_sz | + | | Object N | valid object offsets. | | + v | | All objects WORDSZ v v + --- +----------------------+ aligned. --- --- diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/dyn_load.c b/unity-2019.4.24f1-mbe/external/bdwgc/dyn_load.c new file mode 100644 index 000000000..6692321c6 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/dyn_load.c @@ -0,0 +1,1580 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1997 by Silicon Graphics. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#include "private/gc_priv.h" + +/* + * This is incredibly OS specific code for tracking down data sections in + * dynamic libraries. There appears to be no way of doing this quickly + * without groveling through undocumented data structures. We would argue + * that this is a bug in the design of the dlopen interface. THIS CODE + * MAY BREAK IN FUTURE OS RELEASES. If this matters to you, don't hesitate + * to let your vendor know ... + * + * None of this is safe with dlclose and incremental collection. + * But then not much of anything is safe in the presence of dlclose. + */ + +#if !defined(MACOS) && !defined(SN_TARGET_ORBIS) && !defined(SN_TARGET_PSP2) \ + && !defined(_WIN32_WCE) && !defined(__CC_ARM) +# include +#endif + +/* BTL: avoid circular redefinition of dlopen if GC_SOLARIS_THREADS defined */ +#undef GC_MUST_RESTORE_REDEFINED_DLOPEN +#if defined(GC_PTHREADS) && !defined(GC_NO_DLOPEN) \ + && !defined(GC_NO_THREAD_REDIRECTS) && !defined(GC_USE_LD_WRAP) + /* To support threads in Solaris, gc.h interposes on dlopen by */ + /* defining "dlopen" to be "GC_dlopen", which is implemented below. */ + /* However, both GC_FirstDLOpenedLinkMap() and GC_dlopen() use the */ + /* real system dlopen() in their implementation. We first remove */ + /* gc.h's dlopen definition and restore it later, after GC_dlopen(). */ +# undef dlopen +# define GC_MUST_RESTORE_REDEFINED_DLOPEN +#endif /* !GC_NO_DLOPEN */ + +/* A user-supplied routine (custom filter) that might be called to */ +/* determine whether a DSO really needs to be scanned by the GC. */ +/* 0 means no filter installed. May be unused on some platforms. */ +/* FIXME: Add filter support for more platforms. */ +STATIC GC_has_static_roots_func GC_has_static_roots = 0; + +#if (defined(DYNAMIC_LOADING) || defined(MSWIN32) || defined(MSWINCE) \ + || defined(CYGWIN32)) && !defined(PCR) + +#if !defined(DARWIN) && !defined(SCO_ELF) && !defined(SOLARISDL) \ + && !defined(AIX) && !defined(DGUX) && !defined(IRIX5) && !defined(HPUX) \ + && !defined(CYGWIN32) && !defined(MSWIN32) && !defined(MSWINCE) \ + && !(defined(ALPHA) && defined(OSF1)) \ + && !(defined(FREEBSD) && defined(__ELF__)) \ + && !((defined(LINUX) || defined(NACL)) && defined(__ELF__)) \ + && !(defined(NETBSD) && defined(__ELF__)) \ + && !defined(HAIKU) && !defined(HURD) \ + && !(defined(OPENBSD) && (defined(__ELF__) || defined(M68K))) \ + && !defined(CPPCHECK) +# error We only know how to find data segments of dynamic libraries for above. +# error Additional SVR4 variants might not be too hard to add. +#endif + +#include +#ifdef SOLARISDL +# include +# include +# include +#endif + +#if defined(NETBSD) +# include +# include +# include +# define ELFSIZE ARCH_ELFSIZE +#endif + +#if defined(OPENBSD) +# include +# if (OpenBSD >= 200519) && !defined(HAVE_DL_ITERATE_PHDR) +# define HAVE_DL_ITERATE_PHDR +# endif +#endif /* OPENBSD */ + +#if defined(SCO_ELF) || defined(DGUX) || defined(HURD) \ + || (defined(__ELF__) && (defined(LINUX) || defined(FREEBSD) \ + || defined(NACL) || defined(NETBSD) \ + || defined(OPENBSD))) +# include +# if !defined(OPENBSD) && !defined(HOST_ANDROID) + /* OpenBSD does not have elf.h file; link.h below is sufficient. */ + /* Exclude Android because linker.h below includes its own version. */ +# include +# endif +# ifdef HOST_ANDROID + /* If you don't need the "dynamic loading" feature, you may build */ + /* the collector with -D IGNORE_DYNAMIC_LOADING. */ +# ifdef BIONIC_ELFDATA_REDEF_BUG + /* Workaround a problem in Bionic (as of Android 4.2) which has */ + /* mismatching ELF_DATA definitions in sys/exec_elf.h and */ + /* asm/elf.h included from linker.h file (similar to EM_ALPHA). */ +# include +# include +# undef ELF_DATA +# undef EM_ALPHA +# endif +# include +# if !defined(GC_DONT_DEFINE_LINK_MAP) && !(__ANDROID_API__ >= 21) + /* link_map and r_debug are defined in link.h of NDK r10+. */ + /* bionic/linker/linker.h defines them too but the header */ + /* itself is a C++ one starting from Android 4.3. */ + struct link_map { + uintptr_t l_addr; + char* l_name; + uintptr_t l_ld; + struct link_map* l_next; + struct link_map* l_prev; + }; + struct r_debug { + int32_t r_version; + struct link_map* r_map; + void (*r_brk)(void); + int32_t r_state; + uintptr_t r_ldbase; + }; +# endif +# else + EXTERN_C_BEGIN /* Workaround missing extern "C" around _DYNAMIC */ + /* symbol in link.h of some Linux hosts. */ +# include + EXTERN_C_END +# endif +#endif + +/* Newer versions of GNU/Linux define this macro. We + * define it similarly for any ELF systems that don't. */ +# ifndef ElfW +# if defined(FREEBSD) +# if __ELF_WORD_SIZE == 32 +# define ElfW(type) Elf32_##type +# else +# define ElfW(type) Elf64_##type +# endif +# elif defined(NETBSD) || defined(OPENBSD) +# if ELFSIZE == 32 +# define ElfW(type) Elf32_##type +# else +# define ElfW(type) Elf64_##type +# endif +# else +# if !defined(ELF_CLASS) || ELF_CLASS == ELFCLASS32 +# define ElfW(type) Elf32_##type +# else +# define ElfW(type) Elf64_##type +# endif +# endif +# endif + +#if defined(SOLARISDL) && !defined(USE_PROC_FOR_LIBRARIES) + + EXTERN_C_BEGIN + extern ElfW(Dyn) _DYNAMIC; + EXTERN_C_END + + STATIC struct link_map * + GC_FirstDLOpenedLinkMap(void) + { + ElfW(Dyn) *dp; + static struct link_map * cachedResult = 0; + static ElfW(Dyn) *dynStructureAddr = 0; + /* BTL: added to avoid Solaris 5.3 ld.so _DYNAMIC bug */ + +# ifdef SUNOS53_SHARED_LIB + /* BTL: Avoid the Solaris 5.3 bug that _DYNAMIC isn't being set */ + /* up properly in dynamically linked .so's. This means we have */ + /* to use its value in the set of original object files loaded */ + /* at program startup. */ + if( dynStructureAddr == 0 ) { + void* startupSyms = dlopen(0, RTLD_LAZY); + dynStructureAddr = (ElfW(Dyn)*)(word)dlsym(startupSyms, "_DYNAMIC"); + } +# else + dynStructureAddr = &_DYNAMIC; +# endif + + if (0 == COVERT_DATAFLOW(dynStructureAddr)) { + /* _DYNAMIC symbol not resolved. */ + return(0); + } + if (cachedResult == 0) { + int tag; + for( dp = ((ElfW(Dyn) *)(&_DYNAMIC)); (tag = dp->d_tag) != 0; dp++ ) { + if (tag == DT_DEBUG) { + struct r_debug *rd = (struct r_debug *)dp->d_un.d_ptr; + if (rd != NULL) { + struct link_map *lm = rd->r_map; + if (lm != NULL) + cachedResult = lm->l_next; /* might be NULL */ + } + break; + } + } + } + return cachedResult; + } + +#endif /* SOLARISDL ... */ + +/* BTL: added to fix circular dlopen definition if GC_SOLARIS_THREADS defined */ +# ifdef GC_MUST_RESTORE_REDEFINED_DLOPEN +# define dlopen GC_dlopen +# endif + +# if defined(SOLARISDL) + +/* Add dynamic library data sections to the root set. */ +# if !defined(PCR) && !defined(GC_SOLARIS_THREADS) && defined(THREADS) \ + && !defined(CPPCHECK) +# error Fix mutual exclusion with dlopen +# endif + +# ifndef USE_PROC_FOR_LIBRARIES +GC_INNER void GC_register_dynamic_libraries(void) +{ + struct link_map *lm; + + for (lm = GC_FirstDLOpenedLinkMap(); lm != 0; lm = lm->l_next) { + ElfW(Ehdr) * e; + ElfW(Phdr) * p; + unsigned long offset; + char * start; + int i; + + e = (ElfW(Ehdr) *) lm->l_addr; + p = ((ElfW(Phdr) *)(((char *)(e)) + e->e_phoff)); + offset = ((unsigned long)(lm->l_addr)); + for( i = 0; i < (int)e->e_phnum; i++, p++ ) { + switch( p->p_type ) { + case PT_LOAD: + { + if( !(p->p_flags & PF_W) ) break; + start = ((char *)(p->p_vaddr)) + offset; + GC_add_roots_inner(start, start + p->p_memsz, TRUE); + } + break; + default: + break; + } + } + } +} + +# endif /* !USE_PROC ... */ +# endif /* SOLARISDL */ + +#if defined(SCO_ELF) || defined(DGUX) || defined(HURD) \ + || (defined(__ELF__) && (defined(LINUX) || defined(FREEBSD) \ + || defined(NACL) || defined(NETBSD) \ + || defined(OPENBSD))) + +#ifdef USE_PROC_FOR_LIBRARIES + +#include + +#include +#include +#include + +#define MAPS_BUF_SIZE (32*1024) + +/* Sort an array of HeapSects by start address. */ +/* Unfortunately at least some versions of */ +/* Linux qsort end up calling malloc by way of sysconf, and hence can't */ +/* be used in the collector. Hence we roll our own. Should be */ +/* reasonably fast if the array is already mostly sorted, as we expect */ +/* it to be. */ +static void sort_heap_sects(struct HeapSect *base, size_t number_of_elements) +{ + signed_word n = (signed_word)number_of_elements; + signed_word nsorted = 1; + + while (nsorted < n) { + signed_word i; + + while (nsorted < n && + (word)base[nsorted-1].hs_start < (word)base[nsorted].hs_start) + ++nsorted; + if (nsorted == n) break; + GC_ASSERT((word)base[nsorted-1].hs_start > (word)base[nsorted].hs_start); + i = nsorted - 1; + while (i >= 0 && (word)base[i].hs_start > (word)base[i+1].hs_start) { + struct HeapSect tmp = base[i]; + base[i] = base[i+1]; + base[i+1] = tmp; + --i; + } + GC_ASSERT((word)base[nsorted-1].hs_start < (word)base[nsorted].hs_start); + ++nsorted; + } +} + +STATIC void GC_register_map_entries(char *maps) +{ + char *prot; + char *buf_ptr = maps; + ptr_t start, end; + unsigned int maj_dev; + ptr_t least_ha, greatest_ha; + unsigned i; + + GC_ASSERT(I_HOLD_LOCK()); + sort_heap_sects(GC_our_memory, GC_n_memory); + least_ha = GC_our_memory[0].hs_start; + greatest_ha = GC_our_memory[GC_n_memory-1].hs_start + + GC_our_memory[GC_n_memory-1].hs_bytes; + + for (;;) { + buf_ptr = GC_parse_map_entry(buf_ptr, &start, &end, &prot, + &maj_dev, 0); + if (NULL == buf_ptr) + break; + if (prot[1] == 'w') { + /* This is a writable mapping. Add it to */ + /* the root set unless it is already otherwise */ + /* accounted for. */ + if ((word)start <= (word)GC_stackbottom + && (word)end >= (word)GC_stackbottom) { + /* Stack mapping; discard */ + continue; + } +# ifdef THREADS + /* This may fail, since a thread may already be */ + /* unregistered, but its thread stack may still be there. */ + /* That can fail because the stack may disappear while */ + /* we're marking. Thus the marker is, and has to be */ + /* prepared to recover from segmentation faults. */ + + if (GC_segment_is_thread_stack(start, end)) continue; + + /* FIXME: NPTL squirrels */ + /* away pointers in pieces of the stack segment that we */ + /* don't scan. We work around this */ + /* by treating anything allocated by libpthread as */ + /* uncollectible, as we do in some other cases. */ + /* A specifically identified problem is that */ + /* thread stacks contain pointers to dynamic thread */ + /* vectors, which may be reused due to thread caching. */ + /* They may not be marked if the thread is still live. */ + /* This specific instance should be addressed by */ + /* INCLUDE_LINUX_THREAD_DESCR, but that doesn't quite */ + /* seem to suffice. */ + /* We currently trace entire thread stacks, if they are */ + /* are currently cached but unused. This is */ + /* very suboptimal for performance reasons. */ +# endif + /* We no longer exclude the main data segment. */ + if ((word)end <= (word)least_ha + || (word)start >= (word)greatest_ha) { + /* The easy case; just trace entire segment */ + GC_add_roots_inner(start, end, TRUE); + continue; + } + /* Add sections that don't belong to us. */ + i = 0; + while ((word)(GC_our_memory[i].hs_start + + GC_our_memory[i].hs_bytes) < (word)start) + ++i; + GC_ASSERT(i < GC_n_memory); + if ((word)GC_our_memory[i].hs_start <= (word)start) { + start = GC_our_memory[i].hs_start + + GC_our_memory[i].hs_bytes; + ++i; + } + while (i < GC_n_memory + && (word)GC_our_memory[i].hs_start < (word)end + && (word)start < (word)end) { + if ((word)start < (word)GC_our_memory[i].hs_start) + GC_add_roots_inner(start, + GC_our_memory[i].hs_start, TRUE); + start = GC_our_memory[i].hs_start + + GC_our_memory[i].hs_bytes; + ++i; + } + if ((word)start < (word)end) + GC_add_roots_inner(start, end, TRUE); + } else if (prot[0] == '-' && prot[1] == '-' && prot[2] == '-') { + /* Even roots added statically might disappear partially */ + /* (e.g. the roots added by INCLUDE_LINUX_THREAD_DESCR). */ + GC_remove_roots_subregion(start, end); + } + } +} + +GC_INNER void GC_register_dynamic_libraries(void) +{ + char *maps = GC_get_maps(); + + if (NULL == maps) + ABORT("Failed to read /proc for library registration"); + GC_register_map_entries(maps); +} + +/* We now take care of the main data segment ourselves: */ +GC_INNER GC_bool GC_register_main_static_data(void) +{ + return FALSE; +} + +# define HAVE_REGISTER_MAIN_STATIC_DATA + +#else /* !USE_PROC_FOR_LIBRARIES */ + +/* The following is the preferred way to walk dynamic libraries */ +/* for glibc 2.2.4+. Unfortunately, it doesn't work for older */ +/* versions. Thanks to Jakub Jelinek for most of the code. */ + +#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) \ + || (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && defined(DT_CONFIG)) \ + || defined(HOST_ANDROID) /* Are others OK here, too? */ +# ifndef HAVE_DL_ITERATE_PHDR +# define HAVE_DL_ITERATE_PHDR +# endif +# ifdef HOST_ANDROID + /* Android headers might have no such definition for some targets. */ + EXTERN_C_BEGIN + extern int dl_iterate_phdr(int (*cb)(struct dl_phdr_info *, + size_t, void *), + void *data); + EXTERN_C_END +# endif +#endif /* __GLIBC__ >= 2 || HOST_ANDROID */ + +#if defined(__DragonFly__) || defined(__FreeBSD_kernel__) \ + || (defined(FREEBSD) && __FreeBSD__ >= 7) + /* On the FreeBSD system, any target system at major version 7 shall */ + /* have dl_iterate_phdr; therefore, we need not make it weak as below. */ +# ifndef HAVE_DL_ITERATE_PHDR +# define HAVE_DL_ITERATE_PHDR +# endif +# define DL_ITERATE_PHDR_STRONG +#elif defined(HAVE_DL_ITERATE_PHDR) + /* We have the header files for a glibc that includes dl_iterate_phdr.*/ + /* It may still not be available in the library on the target system. */ + /* Thus we also treat it as a weak symbol. */ + EXTERN_C_BEGIN +# pragma weak dl_iterate_phdr + EXTERN_C_END +#endif + +#if defined(HAVE_DL_ITERATE_PHDR) + +# ifdef PT_GNU_RELRO +/* Instead of registering PT_LOAD sections directly, we keep them */ +/* in a temporary list, and filter them by excluding PT_GNU_RELRO */ +/* segments. Processing PT_GNU_RELRO sections with */ +/* GC_exclude_static_roots instead would be superficially cleaner. But */ +/* it runs into trouble if a client registers an overlapping segment, */ +/* which unfortunately seems quite possible. */ + +# define MAX_LOAD_SEGS MAX_ROOT_SETS + + static struct load_segment { + ptr_t start; + ptr_t end; + /* Room for a second segment if we remove a RELRO segment */ + /* from the middle. */ + ptr_t start2; + ptr_t end2; + } load_segs[MAX_LOAD_SEGS]; + + static int n_load_segs; + static GC_bool load_segs_overflow; +# endif /* PT_GNU_RELRO */ + +STATIC int GC_register_dynlib_callback(struct dl_phdr_info * info, + size_t size, void * ptr) +{ + const ElfW(Phdr) * p; + ptr_t start, end; + int i; + + /* Make sure struct dl_phdr_info is at least as big as we need. */ + if (size < offsetof (struct dl_phdr_info, dlpi_phnum) + + sizeof (info->dlpi_phnum)) + return -1; + + p = info->dlpi_phdr; + for (i = 0; i < (int)info->dlpi_phnum; i++, p++) { + if (p->p_type == PT_LOAD) { + GC_has_static_roots_func callback = GC_has_static_roots; + if ((p->p_flags & PF_W) == 0) continue; + + start = (ptr_t)p->p_vaddr + info->dlpi_addr; + end = start + p->p_memsz; + if (callback != 0 && !callback(info->dlpi_name, start, p->p_memsz)) + continue; +# ifdef PT_GNU_RELRO +# if CPP_WORDSZ == 64 + /* TODO: GC_push_all eventually does the correct */ + /* rounding to the next multiple of ALIGNMENT, so, most */ + /* probably, we should remove the corresponding assertion */ + /* check in GC_add_roots_inner along with this code line. */ + /* start pointer value may require aligning. */ + start = (ptr_t)((word)start & ~(word)(sizeof(word) - 1)); +# endif + if (n_load_segs >= MAX_LOAD_SEGS) { + if (!load_segs_overflow) { + WARN("Too many PT_LOAD segments;" + " registering as roots directly...\n", 0); + load_segs_overflow = TRUE; + } + GC_add_roots_inner(start, end, TRUE); + } else { + load_segs[n_load_segs].start = start; + load_segs[n_load_segs].end = end; + load_segs[n_load_segs].start2 = 0; + load_segs[n_load_segs].end2 = 0; + ++n_load_segs; + } +# else + GC_add_roots_inner(start, end, TRUE); +# endif /* !PT_GNU_RELRO */ + } + } + +# ifdef PT_GNU_RELRO + p = info->dlpi_phdr; + for (i = 0; i < (int)info->dlpi_phnum; i++, p++) { + if (p->p_type == PT_GNU_RELRO) { + /* This entry is known to be constant and will eventually be */ + /* remapped as read-only. However, the address range covered */ + /* by this entry is typically a subset of a previously */ + /* encountered "LOAD" segment, so we need to exclude it. */ + int j; + + start = (ptr_t)p->p_vaddr + info->dlpi_addr; + end = start + p->p_memsz; + for (j = n_load_segs; --j >= 0; ) { + if ((word)start >= (word)load_segs[j].start + && (word)start < (word)load_segs[j].end) { + if (load_segs[j].start2 != 0) { + WARN("More than one GNU_RELRO segment per load one\n",0); + } else { + GC_ASSERT((word)end <= (word)load_segs[j].end); + /* Remove from the existing load segment */ + load_segs[j].end2 = load_segs[j].end; + load_segs[j].end = start; + load_segs[j].start2 = end; + } + break; + } + if (0 == j && 0 == GC_has_static_roots) + WARN("Failed to find PT_GNU_RELRO segment" + " inside PT_LOAD region\n", 0); + /* No warning reported in case of the callback is present */ + /* because most likely the segment has been excluded. */ + } + } + } +# endif + + *(int *)ptr = 1; /* Signal that we were called */ + return 0; +} + +/* Do we need to separately register the main static data segment? */ +GC_INNER GC_bool GC_register_main_static_data(void) +{ +# ifdef DL_ITERATE_PHDR_STRONG + /* If dl_iterate_phdr is not a weak symbol then don't test against */ + /* zero (otherwise a compiler might issue a warning). */ + return FALSE; +# else + return 0 == COVERT_DATAFLOW(dl_iterate_phdr); +# endif +} + +/* Return TRUE if we succeed, FALSE if dl_iterate_phdr wasn't there. */ +STATIC GC_bool GC_register_dynamic_libraries_dl_iterate_phdr(void) +{ + int did_something; + if (GC_register_main_static_data()) + return FALSE; + +# ifdef PT_GNU_RELRO + { + static GC_bool excluded_segs = FALSE; + n_load_segs = 0; + load_segs_overflow = FALSE; + if (!EXPECT(excluded_segs, TRUE)) { + GC_exclude_static_roots_inner((ptr_t)load_segs, + (ptr_t)load_segs + sizeof(load_segs)); + excluded_segs = TRUE; + } + } +# endif + + did_something = 0; + dl_iterate_phdr(GC_register_dynlib_callback, &did_something); + if (did_something) { +# ifdef PT_GNU_RELRO + int i; + + for (i = 0; i < n_load_segs; ++i) { + if ((word)load_segs[i].end > (word)load_segs[i].start) { + GC_add_roots_inner(load_segs[i].start, load_segs[i].end, TRUE); + } + if ((word)load_segs[i].end2 > (word)load_segs[i].start2) { + GC_add_roots_inner(load_segs[i].start2, load_segs[i].end2, TRUE); + } + } +# endif + } else { + ptr_t datastart, dataend; +# ifdef DATASTART_IS_FUNC + static ptr_t datastart_cached = (ptr_t)(word)-1; + + /* Evaluate DATASTART only once. */ + if (datastart_cached == (ptr_t)(word)-1) { + datastart_cached = DATASTART; + } + datastart = datastart_cached; +# else + datastart = DATASTART; +# endif +# ifdef DATAEND_IS_FUNC + { + static ptr_t dataend_cached = 0; + /* Evaluate DATAEND only once. */ + if (dataend_cached == 0) { + dataend_cached = DATAEND; + } + dataend = dataend_cached; + } +# else + dataend = DATAEND; +# endif + if (NULL == *(char * volatile *)&datastart + || (word)datastart > (word)dataend) + ABORT_ARG2("Wrong DATASTART/END pair", + ": %p .. %p", (void *)datastart, (void *)dataend); + + /* dl_iterate_phdr may forget the static data segment in */ + /* statically linked executables. */ + GC_add_roots_inner(datastart, dataend, TRUE); +# ifdef GC_HAVE_DATAREGION2 + if ((word)DATASTART2 - 1U >= (word)DATAEND2) { + /* Subtract one to check also for NULL */ + /* without a compiler warning. */ + ABORT_ARG2("Wrong DATASTART/END2 pair", + ": %p .. %p", (void *)DATASTART2, (void *)DATAEND2); + } + GC_add_roots_inner(DATASTART2, DATAEND2, TRUE); +# endif + } + return TRUE; +} + +# define HAVE_REGISTER_MAIN_STATIC_DATA + +#else /* !HAVE_DL_ITERATE_PHDR */ + +/* Dynamic loading code for Linux running ELF. Somewhat tested on + * Linux/x86, untested but hopefully should work on Linux/Alpha. + * This code was derived from the Solaris/ELF support. Thanks to + * whatever kind soul wrote that. - Patrick Bridges */ + +/* This doesn't necessarily work in all cases, e.g. with preloaded + * dynamic libraries. */ + +# if defined(NETBSD) || defined(OPENBSD) +# include + /* for compatibility with 1.4.x */ +# ifndef DT_DEBUG +# define DT_DEBUG 21 +# endif +# ifndef PT_LOAD +# define PT_LOAD 1 +# endif +# ifndef PF_W +# define PF_W 2 +# endif +# elif !defined(HOST_ANDROID) +# include +# endif + +# ifndef HOST_ANDROID +# include +# endif + +#endif /* !HAVE_DL_ITERATE_PHDR */ + +EXTERN_C_BEGIN +#ifdef __GNUC__ +# pragma weak _DYNAMIC +#endif +extern ElfW(Dyn) _DYNAMIC[]; +EXTERN_C_END + +STATIC struct link_map * +GC_FirstDLOpenedLinkMap(void) +{ + static struct link_map *cachedResult = 0; + + if (0 == COVERT_DATAFLOW(_DYNAMIC)) { + /* _DYNAMIC symbol not resolved. */ + return(0); + } + if( cachedResult == 0 ) { +# if defined(NETBSD) && defined(RTLD_DI_LINKMAP) +# if defined(CPPCHECK) +# define GC_RTLD_DI_LINKMAP 2 +# else +# define GC_RTLD_DI_LINKMAP RTLD_DI_LINKMAP +# endif + struct link_map *lm = NULL; + if (!dlinfo(RTLD_SELF, GC_RTLD_DI_LINKMAP, &lm) && lm != NULL) { + /* Now lm points link_map object of libgc. Since it */ + /* might not be the first dynamically linked object, */ + /* try to find it (object next to the main object). */ + while (lm->l_prev != NULL) { + lm = lm->l_prev; + } + cachedResult = lm->l_next; + } +# else + ElfW(Dyn) *dp; + int tag; + + for( dp = _DYNAMIC; (tag = dp->d_tag) != 0; dp++ ) { + if (tag == DT_DEBUG) { + struct r_debug *rd = (struct r_debug *)dp->d_un.d_ptr; + /* d_ptr could be null if libs are linked statically. */ + if (rd != NULL) { + struct link_map *lm = rd->r_map; + if (lm != NULL) + cachedResult = lm->l_next; /* might be NULL */ + } + break; + } + } +# endif /* !NETBSD || !RTLD_DI_LINKMAP */ + } + return cachedResult; +} + +GC_INNER void GC_register_dynamic_libraries(void) +{ + struct link_map *lm; + +# ifdef HAVE_DL_ITERATE_PHDR + if (GC_register_dynamic_libraries_dl_iterate_phdr()) { + return; + } +# endif + for (lm = GC_FirstDLOpenedLinkMap(); lm != 0; lm = lm->l_next) + { + ElfW(Ehdr) * e; + ElfW(Phdr) * p; + unsigned long offset; + char * start; + int i; + + e = (ElfW(Ehdr) *) lm->l_addr; +# ifdef HOST_ANDROID + if (e == NULL) + continue; +# endif + p = ((ElfW(Phdr) *)(((char *)(e)) + e->e_phoff)); + offset = ((unsigned long)(lm->l_addr)); + for( i = 0; i < (int)e->e_phnum; i++, p++ ) { + switch( p->p_type ) { + case PT_LOAD: + { + if( !(p->p_flags & PF_W) ) break; + start = ((char *)(p->p_vaddr)) + offset; + GC_add_roots_inner(start, start + p->p_memsz, TRUE); + } + break; + default: + break; + } + } + } +} + +#endif /* !USE_PROC_FOR_LIBRARIES */ + +#endif /* LINUX */ + +#if defined(IRIX5) || (defined(USE_PROC_FOR_LIBRARIES) && !defined(LINUX)) + +#include +#include +#include +#include +#include +#include /* Only for the following test. */ +#ifndef _sigargs +# define IRIX6 +#endif + +/* We use /proc to track down all parts of the address space that are */ +/* mapped by the process, and throw out regions we know we shouldn't */ +/* worry about. This may also work under other SVR4 variants. */ +GC_INNER void GC_register_dynamic_libraries(void) +{ + static int fd = -1; + char buf[30]; + static prmap_t * addr_map = 0; + static int current_sz = 0; /* Number of records currently in addr_map */ + int needed_sz = 0; /* Required size of addr_map */ + int i; + long flags; + ptr_t start; + ptr_t limit; + ptr_t heap_start = HEAP_START; + ptr_t heap_end = heap_start; + +# ifdef SOLARISDL +# define MA_PHYS 0 +# endif /* SOLARISDL */ + + if (fd < 0) { + (void)snprintf(buf, sizeof(buf), "/proc/%ld", (long)getpid()); + buf[sizeof(buf) - 1] = '\0'; + fd = open(buf, O_RDONLY); + if (fd < 0) { + ABORT("/proc open failed"); + } + } + if (ioctl(fd, PIOCNMAP, &needed_sz) < 0) { + ABORT_ARG2("/proc PIOCNMAP ioctl failed", + ": fd = %d, errno = %d", fd, errno); + } + if (needed_sz >= current_sz) { + GC_scratch_recycle_no_gww(addr_map, + (size_t)current_sz * sizeof(prmap_t)); + current_sz = needed_sz * 2 + 1; + /* Expansion, plus room for 0 record */ + addr_map = (prmap_t *)GC_scratch_alloc( + (size_t)current_sz * sizeof(prmap_t)); + if (addr_map == NULL) + ABORT("Insufficient memory for address map"); + } + if (ioctl(fd, PIOCMAP, addr_map) < 0) { + ABORT_ARG3("/proc PIOCMAP ioctl failed", + ": errcode= %d, needed_sz= %d, addr_map= %p", + errno, needed_sz, (void *)addr_map); + }; + if (GC_n_heap_sects > 0) { + heap_end = GC_heap_sects[GC_n_heap_sects-1].hs_start + + GC_heap_sects[GC_n_heap_sects-1].hs_bytes; + if ((word)heap_end < (word)GC_scratch_last_end_ptr) + heap_end = GC_scratch_last_end_ptr; + } + for (i = 0; i < needed_sz; i++) { + flags = addr_map[i].pr_mflags; + if ((flags & (MA_BREAK | MA_STACK | MA_PHYS + | MA_FETCHOP | MA_NOTCACHED)) != 0) goto irrelevant; + if ((flags & (MA_READ | MA_WRITE)) != (MA_READ | MA_WRITE)) + goto irrelevant; + /* The latter test is empirically useless in very old Irix */ + /* versions. Other than the */ + /* main data and stack segments, everything appears to be */ + /* mapped readable, writable, executable, and shared(!!). */ + /* This makes no sense to me. - HB */ + start = (ptr_t)(addr_map[i].pr_vaddr); + if (GC_roots_present(start)) goto irrelevant; + if ((word)start < (word)heap_end && (word)start >= (word)heap_start) + goto irrelevant; + + limit = start + addr_map[i].pr_size; + /* The following seemed to be necessary for very old versions */ + /* of Irix, but it has been reported to discard relevant */ + /* segments under Irix 6.5. */ +# ifndef IRIX6 + if (addr_map[i].pr_off == 0 && strncmp(start, ELFMAG, 4) == 0) { + /* Discard text segments, i.e. 0-offset mappings against */ + /* executable files which appear to have ELF headers. */ + caddr_t arg; + int obj; +# define MAP_IRR_SZ 10 + static ptr_t map_irr[MAP_IRR_SZ]; + /* Known irrelevant map entries */ + static int n_irr = 0; + struct stat buf; + int j; + + for (j = 0; j < n_irr; j++) { + if (map_irr[j] == start) goto irrelevant; + } + arg = (caddr_t)start; + obj = ioctl(fd, PIOCOPENM, &arg); + if (obj >= 0) { + fstat(obj, &buf); + close(obj); + if ((buf.st_mode & 0111) != 0) { + if (n_irr < MAP_IRR_SZ) { + map_irr[n_irr++] = start; + } + goto irrelevant; + } + } + } +# endif /* !IRIX6 */ + GC_add_roots_inner(start, limit, TRUE); + irrelevant: ; + } + /* Don't keep cached descriptor, for now. Some kernels don't like us */ + /* to keep a /proc file descriptor around during kill -9. */ + if (close(fd) < 0) ABORT("Couldn't close /proc file"); + fd = -1; +} + +# endif /* USE_PROC || IRIX5 */ + +# if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32) + +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +# endif +# define NOSERVICE +# include +# include + + /* We traverse the entire address space and register all segments */ + /* that could possibly have been written to. */ + STATIC void GC_cond_add_roots(char *base, char * limit) + { +# ifdef GC_WIN32_THREADS + char * curr_base = base; + char * next_stack_lo; + char * next_stack_hi; + + if (base == limit) return; + for(;;) { + GC_get_next_stack(curr_base, limit, &next_stack_lo, &next_stack_hi); + if ((word)next_stack_lo >= (word)limit) break; + if ((word)next_stack_lo > (word)curr_base) + GC_add_roots_inner(curr_base, next_stack_lo, TRUE); + curr_base = next_stack_hi; + } + if ((word)curr_base < (word)limit) + GC_add_roots_inner(curr_base, limit, TRUE); +# else + char * stack_top + = (char *)((word)GC_approx_sp() & + ~(word)(GC_sysinfo.dwAllocationGranularity - 1)); + + if (base == limit) return; + if ((word)limit > (word)stack_top + && (word)base < (word)GC_stackbottom) { + /* Part of the stack; ignore it. */ + return; + } + GC_add_roots_inner(base, limit, TRUE); +# endif + } + +#ifdef DYNAMIC_LOADING + /* GC_register_main_static_data is not needed unless DYNAMIC_LOADING. */ + GC_INNER GC_bool GC_register_main_static_data(void) + { +# if defined(MSWINCE) || defined(CYGWIN32) + /* Do we need to separately register the main static data segment? */ + return FALSE; +# else + return GC_no_win32_dlls; +# endif + } +# define HAVE_REGISTER_MAIN_STATIC_DATA +#endif /* DYNAMIC_LOADING */ + +# ifdef DEBUG_VIRTUALQUERY + void GC_dump_meminfo(MEMORY_BASIC_INFORMATION *buf) + { + GC_printf("BaseAddress = 0x%lx, AllocationBase = 0x%lx," + " RegionSize = 0x%lx(%lu)\n", buf -> BaseAddress, + buf -> AllocationBase, buf -> RegionSize, buf -> RegionSize); + GC_printf("\tAllocationProtect = 0x%lx, State = 0x%lx, Protect = 0x%lx, " + "Type = 0x%lx\n", buf -> AllocationProtect, buf -> State, + buf -> Protect, buf -> Type); + } +# endif /* DEBUG_VIRTUALQUERY */ + +# if defined(MSWINCE) || defined(CYGWIN32) + /* FIXME: Should we really need to scan MEM_PRIVATE sections? */ + /* For now, we don't add MEM_PRIVATE sections to the data roots for */ + /* WinCE because otherwise SEGV fault sometimes happens to occur in */ + /* GC_mark_from() (and, even if we use WRAP_MARK_SOME, WinCE prints */ + /* a "Data Abort" message to the debugging console). */ + /* To workaround that, use -DGC_REGISTER_MEM_PRIVATE. */ +# define GC_wnt TRUE +# endif + + GC_INNER void GC_register_dynamic_libraries(void) + { + MEMORY_BASIC_INFORMATION buf; + DWORD protect; + LPVOID p; + char * base; + char * limit, * new_limit; + +# ifdef MSWIN32 + if (GC_no_win32_dlls) return; +# endif + p = GC_sysinfo.lpMinimumApplicationAddress; + base = limit = (char *)p; + while ((word)p < (word)GC_sysinfo.lpMaximumApplicationAddress) { + size_t result = VirtualQuery(p, &buf, sizeof(buf)); + +# ifdef MSWINCE + if (result == 0) { + /* Page is free; advance to the next possible allocation base */ + new_limit = (char *) + (((DWORD) p + GC_sysinfo.dwAllocationGranularity) + & ~(GC_sysinfo.dwAllocationGranularity-1)); + } else +# endif + /* else */ { + if (result != sizeof(buf)) { + ABORT("Weird VirtualQuery result"); + } + new_limit = (char *)p + buf.RegionSize; + protect = buf.Protect; + if (buf.State == MEM_COMMIT + && (protect == PAGE_EXECUTE_READWRITE + || protect == PAGE_EXECUTE_WRITECOPY + || protect == PAGE_READWRITE + || protect == PAGE_WRITECOPY) + && (buf.Type == MEM_IMAGE +# ifdef GC_REGISTER_MEM_PRIVATE + || (protect == PAGE_READWRITE && buf.Type == MEM_PRIVATE) +# else + /* There is some evidence that we cannot always */ + /* ignore MEM_PRIVATE sections under Windows ME */ + /* and predecessors. Hence we now also check for */ + /* that case. */ + || (!GC_wnt && buf.Type == MEM_PRIVATE) +# endif + ) + && !GC_is_heap_base(buf.AllocationBase)) { +# ifdef DEBUG_VIRTUALQUERY + GC_dump_meminfo(&buf); +# endif + if ((char *)p != limit) { + GC_cond_add_roots(base, limit); + base = (char *)p; + } + limit = new_limit; + } + } + if ((word)p > (word)new_limit /* overflow */) break; + p = (LPVOID)new_limit; + } + GC_cond_add_roots(base, limit); + } + +#endif /* MSWIN32 || MSWINCE || CYGWIN32 */ + +#if defined(ALPHA) && defined(OSF1) + +#include + +EXTERN_C_BEGIN +extern char *sys_errlist[]; +extern int sys_nerr; +extern int errno; +EXTERN_C_END + +GC_INNER void GC_register_dynamic_libraries(void) +{ + ldr_module_t moduleid = LDR_NULL_MODULE; + ldr_process_t mypid = ldr_my_process(); /* obtain id of this process */ + + /* For each module */ + while (TRUE) { + ldr_module_info_t moduleinfo; + size_t modulereturnsize; + ldr_region_t region; + ldr_region_info_t regioninfo; + size_t regionreturnsize; + int status = ldr_next_module(mypid, &moduleid); + /* Get the next (first) module */ + + /* Any more modules? */ + if (moduleid == LDR_NULL_MODULE) + break; /* No more modules */ + + /* Check status AFTER checking moduleid because */ + /* of a bug in the non-shared ldr_next_module stub. */ + if (status != 0) { + ABORT_ARG3("ldr_next_module failed", + ": status= %d, errcode= %d (%s)", status, errno, + errno < sys_nerr ? sys_errlist[errno] : ""); + } + + /* Get the module information */ + status = ldr_inq_module(mypid, moduleid, &moduleinfo, + sizeof(moduleinfo), &modulereturnsize); + if (status != 0 ) + ABORT("ldr_inq_module failed"); + + /* is module for the main program (i.e. nonshared portion)? */ + if (moduleinfo.lmi_flags & LDR_MAIN) + continue; /* skip the main module */ + +# ifdef DL_VERBOSE + GC_log_printf("---Module---\n"); + GC_log_printf("Module ID\t = %16ld\n", moduleinfo.lmi_modid); + GC_log_printf("Count of regions = %16d\n", moduleinfo.lmi_nregion); + GC_log_printf("flags for module = %16lx\n", moduleinfo.lmi_flags); + GC_log_printf("module pathname\t = \"%s\"\n", moduleinfo.lmi_name); +# endif + + /* For each region in this module */ + for (region = 0; region < moduleinfo.lmi_nregion; region++) { + /* Get the region information */ + status = ldr_inq_region(mypid, moduleid, region, ®ioninfo, + sizeof(regioninfo), ®ionreturnsize); + if (status != 0 ) + ABORT("ldr_inq_region failed"); + + /* only process writable (data) regions */ + if (! (regioninfo.lri_prot & LDR_W)) + continue; + +# ifdef DL_VERBOSE + GC_log_printf("--- Region ---\n"); + GC_log_printf("Region number\t = %16ld\n", + regioninfo.lri_region_no); + GC_log_printf("Protection flags = %016x\n", regioninfo.lri_prot); + GC_log_printf("Virtual address\t = %16p\n", regioninfo.lri_vaddr); + GC_log_printf("Mapped address\t = %16p\n", + regioninfo.lri_mapaddr); + GC_log_printf("Region size\t = %16ld\n", regioninfo.lri_size); + GC_log_printf("Region name\t = \"%s\"\n", regioninfo.lri_name); +# endif + + /* register region as a garbage collection root */ + GC_add_roots_inner((char *)regioninfo.lri_mapaddr, + (char *)regioninfo.lri_mapaddr + regioninfo.lri_size, + TRUE); + + } + } +} +#endif + +#if defined(HPUX) + +#include +#include + +EXTERN_C_BEGIN +extern char *sys_errlist[]; +extern int sys_nerr; +EXTERN_C_END + +GC_INNER void GC_register_dynamic_libraries(void) +{ + int index = 1; /* Ordinal position in shared library search list */ + + /* For each dynamic library loaded */ + while (TRUE) { + struct shl_descriptor *shl_desc; /* Shared library info, see dl.h */ + int status = shl_get(index, &shl_desc); + /* Get info about next shared library */ + + /* Check if this is the end of the list or if some error occurred */ + if (status != 0) { +# ifdef GC_HPUX_THREADS + /* I've seen errno values of 0. The man page is not clear */ + /* as to whether errno should get set on a -1 return. */ + break; +# else + if (errno == EINVAL) { + break; /* Moved past end of shared library list --> finished */ + } else { + ABORT_ARG3("shl_get failed", + ": status= %d, errcode= %d (%s)", status, errno, + errno < sys_nerr ? sys_errlist[errno] : ""); + } +# endif + } + +# ifdef DL_VERBOSE + GC_log_printf("---Shared library---\n"); + GC_log_printf("\tfilename\t= \"%s\"\n", shl_desc->filename); + GC_log_printf("\tindex\t\t= %d\n", index); + GC_log_printf("\thandle\t\t= %08x\n", + (unsigned long) shl_desc->handle); + GC_log_printf("\ttext seg.start\t= %08x\n", shl_desc->tstart); + GC_log_printf("\ttext seg.end\t= %08x\n", shl_desc->tend); + GC_log_printf("\tdata seg.start\t= %08x\n", shl_desc->dstart); + GC_log_printf("\tdata seg.end\t= %08x\n", shl_desc->dend); + GC_log_printf("\tref.count\t= %lu\n", shl_desc->ref_count); +# endif + + /* register shared library's data segment as a garbage collection root */ + GC_add_roots_inner((char *) shl_desc->dstart, + (char *) shl_desc->dend, TRUE); + + index++; + } +} +#endif /* HPUX */ + +#ifdef AIX +# pragma alloca +# include +# include + GC_INNER void GC_register_dynamic_libraries(void) + { + int ldibuflen = 8192; + + for (;;) { + int len; + struct ld_info *ldi; +# if defined(CPPCHECK) + char ldibuf[ldibuflen]; +# else + char *ldibuf = alloca(ldibuflen); +# endif + + len = loadquery(L_GETINFO, ldibuf, ldibuflen); + if (len < 0) { + if (errno != ENOMEM) { + ABORT("loadquery failed"); + } + ldibuflen *= 2; + continue; + } + + ldi = (struct ld_info *)ldibuf; + while (ldi) { + len = ldi->ldinfo_next; + GC_add_roots_inner( + ldi->ldinfo_dataorg, + (ptr_t)(unsigned long)ldi->ldinfo_dataorg + + ldi->ldinfo_datasize, + TRUE); + ldi = len ? (struct ld_info *)((char *)ldi + len) : 0; + } + break; + } + } +#endif /* AIX */ + +#ifdef DARWIN + +/* __private_extern__ hack required for pre-3.4 gcc versions. */ +#ifndef __private_extern__ +# define __private_extern__ extern +# include +# undef __private_extern__ +#else +# include +#endif +#include + +/*#define DARWIN_DEBUG*/ + +/* Writable sections generally available on Darwin. */ +STATIC const struct dyld_sections_s { + const char *seg; + const char *sect; +} GC_dyld_sections[] = { + { SEG_DATA, SECT_DATA }, + /* Used by FSF GCC, but not by OS X system tools, so far. */ + { SEG_DATA, "__static_data" }, + { SEG_DATA, SECT_BSS }, + { SEG_DATA, SECT_COMMON }, + /* FSF GCC - zero-sized object sections for targets */ + /*supporting section anchors. */ + { SEG_DATA, "__zobj_data" }, + { SEG_DATA, "__zobj_bss" } +}; + +/* Additional writable sections: */ +/* GCC on Darwin constructs aligned sections "on demand", where */ +/* the alignment size is embedded in the section name. */ +/* Furthermore, there are distinctions between sections */ +/* containing private vs. public symbols. It also constructs */ +/* sections specifically for zero-sized objects, when the */ +/* target supports section anchors. */ +STATIC const char * const GC_dyld_add_sect_fmts[] = { + "__bss%u", + "__pu_bss%u", + "__zo_bss%u", + "__zo_pu_bss%u" +}; + +/* Currently, mach-o will allow up to the max of 2^15 alignment */ +/* in an object file. */ +#ifndef L2_MAX_OFILE_ALIGNMENT +# define L2_MAX_OFILE_ALIGNMENT 15 +#endif + +STATIC const char *GC_dyld_name_for_hdr(const struct GC_MACH_HEADER *hdr) +{ + unsigned long i, c; + c = _dyld_image_count(); + for (i = 0; i < c; i++) + if ((const struct GC_MACH_HEADER *)_dyld_get_image_header(i) == hdr) + return _dyld_get_image_name(i); + return NULL; +} + +/* This should never be called by a thread holding the lock. */ +STATIC void GC_dyld_image_add(const struct GC_MACH_HEADER *hdr, + intptr_t slide) +{ + unsigned long start, end; + unsigned i, j; + const struct GC_MACH_SECTION *sec; + const char *name; + GC_has_static_roots_func callback = GC_has_static_roots; + DCL_LOCK_STATE; + + if (GC_no_dls) return; +# ifdef DARWIN_DEBUG + name = GC_dyld_name_for_hdr(hdr); +# else + name = callback != 0 ? GC_dyld_name_for_hdr(hdr) : NULL; +# endif + for (i = 0; i < sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]); i++) { + sec = GC_GETSECTBYNAME(hdr, GC_dyld_sections[i].seg, + GC_dyld_sections[i].sect); + if (sec == NULL || sec->size < sizeof(word)) + continue; + start = slide + sec->addr; + end = start + sec->size; + LOCK(); + /* The user callback is called holding the lock. */ + if (callback == 0 || callback(name, (void*)start, (size_t)sec->size)) { +# ifdef DARWIN_DEBUG + GC_log_printf( + "Adding section __DATA,%s at %p-%p (%lu bytes) from image %s\n", + GC_dyld_sections[i].sect, (void*)start, (void*)end, + (unsigned long)sec->size, name); +# endif + GC_add_roots_inner((ptr_t)start, (ptr_t)end, FALSE); + } + UNLOCK(); + } + + /* Sections constructed on demand. */ + for (j = 0; j < sizeof(GC_dyld_add_sect_fmts) / sizeof(char *); j++) { + const char *fmt = GC_dyld_add_sect_fmts[j]; + + /* Add our manufactured aligned BSS sections. */ + for (i = 0; i <= L2_MAX_OFILE_ALIGNMENT; i++) { + char secnam[16]; + + (void)snprintf(secnam, sizeof(secnam), fmt, (unsigned)i); + secnam[sizeof(secnam) - 1] = '\0'; + sec = GC_GETSECTBYNAME(hdr, SEG_DATA, secnam); + if (sec == NULL || sec->size == 0) + continue; + start = slide + sec->addr; + end = start + sec->size; +# ifdef DARWIN_DEBUG + GC_log_printf("Adding on-demand section __DATA,%s at" + " %p-%p (%lu bytes) from image %s\n", + secnam, (void*)start, (void*)end, + (unsigned long)sec->size, name); +# endif + GC_add_roots((char*)start, (char*)end); + } + } + +# if defined(DARWIN_DEBUG) && !defined(NO_DEBUGGING) + LOCK(); + GC_print_static_roots(); + UNLOCK(); +# endif +} + +/* This should never be called by a thread holding the lock. */ +STATIC void GC_dyld_image_remove(const struct GC_MACH_HEADER *hdr, + intptr_t slide) +{ + unsigned long start, end; + unsigned i, j; + const struct GC_MACH_SECTION *sec; +# if defined(DARWIN_DEBUG) && !defined(NO_DEBUGGING) + DCL_LOCK_STATE; +# endif + + for (i = 0; i < sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]); i++) { + sec = GC_GETSECTBYNAME(hdr, GC_dyld_sections[i].seg, + GC_dyld_sections[i].sect); + if (sec == NULL || sec->size == 0) + continue; + start = slide + sec->addr; + end = start + sec->size; +# ifdef DARWIN_DEBUG + GC_log_printf( + "Removing section __DATA,%s at %p-%p (%lu bytes) from image %s\n", + GC_dyld_sections[i].sect, (void*)start, (void*)end, + (unsigned long)sec->size, GC_dyld_name_for_hdr(hdr)); +# endif + GC_remove_roots((char*)start, (char*)end); + } + + /* Remove our on-demand sections. */ + for (j = 0; j < sizeof(GC_dyld_add_sect_fmts) / sizeof(char *); j++) { + const char *fmt = GC_dyld_add_sect_fmts[j]; + + for (i = 0; i <= L2_MAX_OFILE_ALIGNMENT; i++) { + char secnam[16]; + + (void)snprintf(secnam, sizeof(secnam), fmt, (unsigned)i); + secnam[sizeof(secnam) - 1] = '\0'; + sec = GC_GETSECTBYNAME(hdr, SEG_DATA, secnam); + if (sec == NULL || sec->size == 0) + continue; + start = slide + sec->addr; + end = start + sec->size; +# ifdef DARWIN_DEBUG + GC_log_printf("Removing on-demand section __DATA,%s at" + " %p-%p (%lu bytes) from image %s\n", secnam, + (void*)start, (void*)end, (unsigned long)sec->size, + GC_dyld_name_for_hdr(hdr)); +# endif + GC_remove_roots((char*)start, (char*)end); + } + } + +# if defined(DARWIN_DEBUG) && !defined(NO_DEBUGGING) + LOCK(); + GC_print_static_roots(); + UNLOCK(); +# endif +} + +GC_INNER void GC_register_dynamic_libraries(void) +{ + /* Currently does nothing. The callbacks are setup by GC_init_dyld() + The dyld library takes it from there. */ +} + +/* The _dyld_* functions have an internal lock so no _dyld functions + can be called while the world is stopped without the risk of a deadlock. + Because of this we MUST setup callbacks BEFORE we ever stop the world. + This should be called BEFORE any thread in created and WITHOUT the + allocation lock held. */ + +GC_INNER void GC_init_dyld(void) +{ + static GC_bool initialized = FALSE; + + if (initialized) return; + +# ifdef DARWIN_DEBUG + GC_log_printf("Registering dyld callbacks...\n"); +# endif + + /* Apple's Documentation: + When you call _dyld_register_func_for_add_image, the dynamic linker + runtime calls the specified callback (func) once for each of the images + that is currently loaded into the program. When a new image is added to + the program, your callback is called again with the mach_header for the + new image, and the virtual memory slide amount of the new image. + + This WILL properly register already linked libraries and libraries + linked in the future. + */ + _dyld_register_func_for_add_image( + (void (*)(const struct mach_header*, intptr_t))GC_dyld_image_add); + _dyld_register_func_for_remove_image( + (void (*)(const struct mach_header*, intptr_t))GC_dyld_image_remove); + /* Structure mach_header64 has the same fields */ + /* as mach_header except for the reserved one */ + /* at the end, so these casts are OK. */ + + /* Set this early to avoid reentrancy issues. */ + initialized = TRUE; + +# ifdef NO_DYLD_BIND_FULLY_IMAGE + /* FIXME: What should we do in this case? */ +# else + if (GC_no_dls) return; /* skip main data segment registration */ + + /* When the environment variable is set, the dynamic linker binds */ + /* all undefined symbols the application needs at launch time. */ + /* This includes function symbols that are normally bound lazily at */ + /* the time of their first invocation. */ + if (GETENV("DYLD_BIND_AT_LAUNCH") == 0) { + /* The environment variable is unset, so we should bind manually. */ +# ifdef DARWIN_DEBUG + GC_log_printf("Forcing full bind of GC code...\n"); +# endif + /* FIXME: '_dyld_bind_fully_image_containing_address' is deprecated. */ + if (!_dyld_bind_fully_image_containing_address( + (unsigned long *)GC_malloc)) + ABORT("_dyld_bind_fully_image_containing_address failed"); + } +# endif +} + +#define HAVE_REGISTER_MAIN_STATIC_DATA +GC_INNER GC_bool GC_register_main_static_data(void) +{ + /* Already done through dyld callbacks */ + return FALSE; +} + +#endif /* DARWIN */ + +#if defined(HAIKU) +# include + + GC_INNER void GC_register_dynamic_libraries(void) + { + image_info info; + int32 cookie = 0; + + while (get_next_image_info(0, &cookie, &info) == B_OK) { + ptr_t data = (ptr_t)info.data; + GC_add_roots_inner(data, data + info.data_size, TRUE); + } + } +#endif /* HAIKU */ + +#elif defined(PCR) + +# include "il/PCR_IL.h" +# include "th/PCR_ThCtl.h" +# include "mm/PCR_MM.h" + + GC_INNER void GC_register_dynamic_libraries(void) + { + /* Add new static data areas of dynamically loaded modules. */ + PCR_IL_LoadedFile * p = PCR_IL_GetLastLoadedFile(); + PCR_IL_LoadedSegment * q; + + /* Skip uncommitted files */ + while (p != NIL && !(p -> lf_commitPoint)) { + /* The loading of this file has not yet been committed */ + /* Hence its description could be inconsistent. */ + /* Furthermore, it hasn't yet been run. Hence its data */ + /* segments can't possibly reference heap allocated */ + /* objects. */ + p = p -> lf_prev; + } + for (; p != NIL; p = p -> lf_prev) { + for (q = p -> lf_ls; q != NIL; q = q -> ls_next) { + if ((q -> ls_flags & PCR_IL_SegFlags_Traced_MASK) + == PCR_IL_SegFlags_Traced_on) { + GC_add_roots_inner((ptr_t)q->ls_addr, + (ptr_t)q->ls_addr + q->ls_bytes, TRUE); + } + } + } + } +#endif /* PCR && !DYNAMIC_LOADING && !MSWIN32 */ + +#if !defined(HAVE_REGISTER_MAIN_STATIC_DATA) && defined(DYNAMIC_LOADING) + /* Do we need to separately register the main static data segment? */ + GC_INNER GC_bool GC_register_main_static_data(void) + { + return TRUE; + } +#endif /* HAVE_REGISTER_MAIN_STATIC_DATA */ + +/* Register a routine to filter dynamic library registration. */ +GC_API void GC_CALL GC_register_has_static_roots_callback( + GC_has_static_roots_func callback) +{ + GC_has_static_roots = callback; +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/extra/AmigaOS.c b/unity-2019.4.24f1-mbe/external/bdwgc/extra/AmigaOS.c new file mode 100644 index 000000000..d8311eed4 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/extra/AmigaOS.c @@ -0,0 +1,541 @@ + + +/****************************************************************** + + AmigaOS-specific routines for GC. + This file is normally included from os_dep.c + +******************************************************************/ + + +#if !defined(GC_AMIGA_DEF) && !defined(GC_AMIGA_SB) && !defined(GC_AMIGA_DS) && !defined(GC_AMIGA_AM) +# include "private/gc_priv.h" +# include +# include +# define GC_AMIGA_DEF +# define GC_AMIGA_SB +# define GC_AMIGA_DS +# define GC_AMIGA_AM +#endif + + +#ifdef GC_AMIGA_DEF + +# ifndef __GNUC__ +# include +# endif +# include +# include +# include +# include + +#endif + + + + +#ifdef GC_AMIGA_SB + +/****************************************************************** + Find the base of the stack. +******************************************************************/ + +ptr_t GC_get_main_stack_base(void) +{ + struct Process *proc = (struct Process*)SysBase->ThisTask; + + /* Reference: Amiga Guru Book Pages: 42,567,574 */ + if (proc->pr_Task.tc_Node.ln_Type==NT_PROCESS + && proc->pr_CLI != NULL) { + /* first ULONG is StackSize */ + /*longPtr = proc->pr_ReturnAddr; + size = longPtr[0];*/ + + return (char *)proc->pr_ReturnAddr + sizeof(ULONG); + } else { + return (char *)proc->pr_Task.tc_SPUpper; + } +} + +#endif + + +#ifdef GC_AMIGA_DS +/****************************************************************** + Register data segments. +******************************************************************/ + + void GC_register_data_segments(void) + { + struct Process *proc; + struct CommandLineInterface *cli; + BPTR myseglist; + ULONG *data; + +# ifdef __GNUC__ + ULONG dataSegSize; + GC_bool found_segment = FALSE; + extern char __data_size[]; + + dataSegSize=__data_size+8; + /* Can`t find the Location of __data_size, because + it`s possible that is it, inside the segment. */ + +# endif + + proc= (struct Process*)SysBase->ThisTask; + + /* Reference: Amiga Guru Book Pages: 538ff,565,573 + and XOper.asm */ + myseglist = proc->pr_SegList; + if (proc->pr_Task.tc_Node.ln_Type==NT_PROCESS) { + if (proc->pr_CLI != NULL) { + /* ProcLoaded 'Loaded as a command: '*/ + cli = BADDR(proc->pr_CLI); + myseglist = cli->cli_Module; + } + } else { + ABORT("Not a Process."); + } + + if (myseglist == NULL) { + ABORT("Arrrgh.. can't find segments, aborting"); + } + + /* xoper hunks Shell Process */ + + for (data = (ULONG *)BADDR(myseglist); data != NULL; + data = (ULONG *)BADDR(data[0])) { + if ((ULONG)GC_register_data_segments < (ULONG)(&data[1]) + || (ULONG)GC_register_data_segments > (ULONG)(&data[1]) + + data[-1]) { +# ifdef __GNUC__ + if (dataSegSize == data[-1]) { + found_segment = TRUE; + } +# endif + GC_add_roots_inner((char *)&data[1], + ((char *)&data[1]) + data[-1], FALSE); + } + } /* for */ +# ifdef __GNUC__ + if (!found_segment) { + ABORT("Can`t find correct Segments.\nSolution: Use an newer version of ixemul.library"); + } +# endif + } + +#endif + + + +#ifdef GC_AMIGA_AM + +#ifndef GC_AMIGA_FASTALLOC + +void *GC_amiga_allocwrapper(size_t size,void *(*AllocFunction)(size_t size2)){ + return (*AllocFunction)(size); +} + +void *(*GC_amiga_allocwrapper_do)(size_t size,void *(*AllocFunction)(size_t size2)) + =GC_amiga_allocwrapper; + +#else + + + + +void *GC_amiga_allocwrapper_firsttime(size_t size,void *(*AllocFunction)(size_t size2)); + +void *(*GC_amiga_allocwrapper_do)(size_t size,void *(*AllocFunction)(size_t size2)) + =GC_amiga_allocwrapper_firsttime; + + +/****************************************************************** + Amiga-specific routines to obtain memory, and force GC to give + back fast-mem whenever possible. + These hacks makes gc-programs go many times faster when + the Amiga is low on memory, and are therefore strictly necessary. + + -Kjetil S. Matheussen, 2000. +******************************************************************/ + + + +/* List-header for all allocated memory. */ + +struct GC_Amiga_AllocedMemoryHeader{ + ULONG size; + struct GC_Amiga_AllocedMemoryHeader *next; +}; +struct GC_Amiga_AllocedMemoryHeader *GC_AMIGAMEM=(struct GC_Amiga_AllocedMemoryHeader *)(int)~(NULL); + + + +/* Type of memory. Once in the execution of a program, this might change to MEMF_ANY|MEMF_CLEAR */ + +ULONG GC_AMIGA_MEMF = MEMF_FAST | MEMF_CLEAR; + + +/* Prevents GC_amiga_get_mem from allocating memory if this one is TRUE. */ +#ifndef GC_AMIGA_ONLYFAST +BOOL GC_amiga_dontalloc=FALSE; +#endif + +#ifdef GC_AMIGA_PRINTSTATS +int succ=0,succ2=0; +int nsucc=0,nsucc2=0; +int nullretries=0; +int numcollects=0; +int chipa=0; +int allochip=0; +int allocfast=0; +int cur0=0; +int cur1=0; +int cur10=0; +int cur50=0; +int cur150=0; +int cur151=0; +int ncur0=0; +int ncur1=0; +int ncur10=0; +int ncur50=0; +int ncur150=0; +int ncur151=0; +#endif + +/* Free everything at program-end. */ + +void GC_amiga_free_all_mem(void){ + struct GC_Amiga_AllocedMemoryHeader *gc_am=(struct GC_Amiga_AllocedMemoryHeader *)(~(int)(GC_AMIGAMEM)); + +#ifdef GC_AMIGA_PRINTSTATS + printf("\n\n" + "%d bytes of chip-mem, and %d bytes of fast-mem where allocated from the OS.\n", + allochip,allocfast + ); + printf( + "%d bytes of chip-mem were returned from the GC_AMIGA_FASTALLOC supported allocating functions.\n", + chipa + ); + printf("\n"); + printf("GC_gcollect was called %d times to avoid returning NULL or start allocating with the MEMF_ANY flag.\n",numcollects); + printf("%d of them was a success. (the others had to use allocation from the OS.)\n",nullretries); + printf("\n"); + printf("Succeeded forcing %d gc-allocations (%d bytes) of chip-mem to be fast-mem.\n",succ,succ2); + printf("Failed forcing %d gc-allocations (%d bytes) of chip-mem to be fast-mem.\n",nsucc,nsucc2); + printf("\n"); + printf( + "Number of retries before succeeding a chip->fast force:\n" + "0: %d, 1: %d, 2-9: %d, 10-49: %d, 50-149: %d, >150: %d\n", + cur0,cur1,cur10,cur50,cur150,cur151 + ); + printf( + "Number of retries before giving up a chip->fast force:\n" + "0: %d, 1: %d, 2-9: %d, 10-49: %d, 50-149: %d, >150: %d\n", + ncur0,ncur1,ncur10,ncur50,ncur150,ncur151 + ); +#endif + + while(gc_am!=NULL){ + struct GC_Amiga_AllocedMemoryHeader *temp = gc_am->next; + FreeMem(gc_am,gc_am->size); + gc_am=(struct GC_Amiga_AllocedMemoryHeader *)(~(int)(temp)); + } +} + +#ifndef GC_AMIGA_ONLYFAST + +/* All memory with address lower than this one is chip-mem. */ + +char *chipmax; + + +/* + * Always set to the last size of memory tried to be allocated. + * Needed to ensure allocation when the size is bigger than 100000. + * + */ +size_t latestsize; + +#endif + + +#ifdef GC_AMIGA_FASTALLOC + +/* + * The actual function that is called with the GET_MEM macro. + * + */ + +void *GC_amiga_get_mem(size_t size){ + struct GC_Amiga_AllocedMemoryHeader *gc_am; + +#ifndef GC_AMIGA_ONLYFAST + if(GC_amiga_dontalloc==TRUE){ + return NULL; + } + + /* We really don't want to use chip-mem, but if we must, then as little as possible. */ + if(GC_AMIGA_MEMF==(MEMF_ANY|MEMF_CLEAR) && size>100000 && latestsize<50000) return NULL; +#endif + + gc_am=AllocMem((ULONG)(size + sizeof(struct GC_Amiga_AllocedMemoryHeader)),GC_AMIGA_MEMF); + if(gc_am==NULL) return NULL; + + gc_am->next=GC_AMIGAMEM; + gc_am->size=size + sizeof(struct GC_Amiga_AllocedMemoryHeader); + GC_AMIGAMEM=(struct GC_Amiga_AllocedMemoryHeader *)(~(int)(gc_am)); + +#ifdef GC_AMIGA_PRINTSTATS + if((char *)gc_amchipmax || ret==NULL){ + if(ret==NULL){ + nsucc++; + nsucc2+=size; + if(rec==0) ncur0++; + if(rec==1) ncur1++; + if(rec>1 && rec<10) ncur10++; + if(rec>=10 && rec<50) ncur50++; + if(rec>=50 && rec<150) ncur150++; + if(rec>=150) ncur151++; + }else{ + succ++; + succ2+=size; + if(rec==0) cur0++; + if(rec==1) cur1++; + if(rec>1 && rec<10) cur10++; + if(rec>=10 && rec<50) cur50++; + if(rec>=50 && rec<150) cur150++; + if(rec>=150) cur151++; + } + } +#endif + + if (((char *)ret)<=chipmax && ret!=NULL && (rec<(size>500000?9:size/5000))){ + ret=GC_amiga_rec_alloc(size,AllocFunction,rec+1); + } + + return ret; +} +#endif + + +/* The allocating-functions defined inside the Amiga-blocks in gc.h is called + * via these functions. + */ + + +void *GC_amiga_allocwrapper_any(size_t size,void *(*AllocFunction)(size_t size2)){ + void *ret; + + GC_amiga_dontalloc=TRUE; /* Pretty tough thing to do, but its indeed necessary. */ + latestsize=size; + + ret=(*AllocFunction)(size); + + if(((char *)ret) <= chipmax){ + if(ret==NULL){ + /* Give GC access to allocate memory. */ +#ifdef GC_AMIGA_GC + if(!GC_dont_gc){ + GC_gcollect(); +#ifdef GC_AMIGA_PRINTSTATS + numcollects++; +#endif + ret=(*AllocFunction)(size); + } + if(ret==NULL) +#endif + { + GC_amiga_dontalloc=FALSE; + ret=(*AllocFunction)(size); + if(ret==NULL){ + WARN("Out of Memory! Returning NIL!\n", 0); + } + } +#ifdef GC_AMIGA_PRINTSTATS + else{ + nullretries++; + } + if(ret!=NULL && (char *)ret<=chipmax) chipa+=size; +#endif + } +#ifdef GC_AMIGA_RETRY + else{ + void *ret2; + /* We got chip-mem. Better try again and again and again etc., we might get fast-mem sooner or later... */ + /* Using gctest to check the effectiveness of doing this, does seldom give a very good result. */ + /* However, real programs doesn't normally rapidly allocate and deallocate. */ + if( + AllocFunction!=GC_malloc_uncollectable +#ifdef GC_ATOMIC_UNCOLLECTABLE + && AllocFunction!=GC_malloc_atomic_uncollectable +#endif + ){ + ret2=GC_amiga_rec_alloc(size,AllocFunction,0); + }else{ + ret2=(*AllocFunction)(size); +#ifdef GC_AMIGA_PRINTSTATS + if((char *)ret2chipmax){ + GC_free(ret); + ret=ret2; + }else{ + GC_free(ret2); + } + } +#endif + } + + GC_amiga_dontalloc=FALSE; + + return ret; +} + + + +void (*GC_amiga_toany)(void)=NULL; + +void GC_amiga_set_toany(void (*func)(void)){ + GC_amiga_toany=func; +} + +#endif /* !GC_AMIGA_ONLYFAST */ + + +void *GC_amiga_allocwrapper_fast(size_t size,void *(*AllocFunction)(size_t size2)){ + void *ret; + + ret=(*AllocFunction)(size); + + if(ret==NULL){ + /* Enable chip-mem allocation. */ +#ifdef GC_AMIGA_GC + if(!GC_dont_gc){ + GC_gcollect(); +#ifdef GC_AMIGA_PRINTSTATS + numcollects++; +#endif + ret=(*AllocFunction)(size); + } + if(ret==NULL) +#endif + { +#ifndef GC_AMIGA_ONLYFAST + GC_AMIGA_MEMF=MEMF_ANY | MEMF_CLEAR; + if(GC_amiga_toany!=NULL) (*GC_amiga_toany)(); + GC_amiga_allocwrapper_do=GC_amiga_allocwrapper_any; + return GC_amiga_allocwrapper_any(size,AllocFunction); +#endif + } +#ifdef GC_AMIGA_PRINTSTATS + else{ + nullretries++; + } +#endif + } + + return ret; +} + +void *GC_amiga_allocwrapper_firsttime(size_t size,void *(*AllocFunction)(size_t size2)){ + atexit(&GC_amiga_free_all_mem); + chipmax=(char *)SysBase->MaxLocMem; /* For people still having SysBase in chip-mem, this might speed up a bit. */ + GC_amiga_allocwrapper_do=GC_amiga_allocwrapper_fast; + return GC_amiga_allocwrapper_fast(size,AllocFunction); +} + + +#endif /* GC_AMIGA_FASTALLOC */ + + + +/* + * The wrapped realloc function. + * + */ +void *GC_amiga_realloc(void *old_object,size_t new_size_in_bytes){ +#ifndef GC_AMIGA_FASTALLOC + return GC_realloc(old_object,new_size_in_bytes); +#else + void *ret; + latestsize=new_size_in_bytes; + ret=GC_realloc(old_object,new_size_in_bytes); + if(ret==NULL && new_size_in_bytes != 0 + && GC_AMIGA_MEMF==(MEMF_FAST | MEMF_CLEAR)){ + /* Out of fast-mem. */ +#ifdef GC_AMIGA_GC + if(!GC_dont_gc){ + GC_gcollect(); +#ifdef GC_AMIGA_PRINTSTATS + numcollects++; +#endif + ret=GC_realloc(old_object,new_size_in_bytes); + } + if(ret==NULL) +#endif + { +#ifndef GC_AMIGA_ONLYFAST + GC_AMIGA_MEMF=MEMF_ANY | MEMF_CLEAR; + if(GC_amiga_toany!=NULL) (*GC_amiga_toany)(); + GC_amiga_allocwrapper_do=GC_amiga_allocwrapper_any; + ret=GC_realloc(old_object,new_size_in_bytes); +#endif + } +#ifdef GC_AMIGA_PRINTSTATS + else{ + nullretries++; + } +#endif + } + if(ret==NULL && new_size_in_bytes != 0){ + WARN("Out of Memory! Returning NIL!\n", 0); + } +#ifdef GC_AMIGA_PRINTSTATS + if(((char *)ret) + + 11/22/94 pcb StripAddress the temporary memory handle for 24-bit mode. + 11/30/94 pcb Tracking all memory usage so we can deallocate it all at once. + 02/10/96 pcb Added routine to perform a final collection when +unloading shared library. + + by Patrick C. Beard. + */ +/* Boehm, February 15, 1996 2:55 pm PST */ + +#include +#include +#include +#include +#include +#include + +#define GC_BUILD +#include "gc.h" +#include "private/gc_priv.h" + +/* use 'CODE' resource 0 to get exact location of the beginning of global space. */ + +typedef struct { + unsigned long aboveA5; + unsigned long belowA5; + unsigned long JTSize; + unsigned long JTOffset; +} *CodeZeroPtr, **CodeZeroHandle; + +void* GC_MacGetDataStart(void) +{ + CodeZeroHandle code0 = (CodeZeroHandle)GetResource('CODE', 0); + if (code0) { + long belowA5Size = (**code0).belowA5; + ReleaseResource((Handle)code0); + return (LMGetCurrentA5() - belowA5Size); + } + fprintf(stderr, "Couldn't load the jump table."); + exit(-1); + return 0; +} + +#ifdef USE_TEMPORARY_MEMORY + +/* track the use of temporary memory so it can be freed all at once. */ + +typedef struct TemporaryMemoryBlock TemporaryMemoryBlock, **TemporaryMemoryHandle; + +struct TemporaryMemoryBlock { + TemporaryMemoryHandle nextBlock; + char data[]; +}; + +static TemporaryMemoryHandle theTemporaryMemory = NULL; + +void GC_MacFreeTemporaryMemory(void); + +Ptr GC_MacTemporaryNewPtr(size_t size, Boolean clearMemory) +{ +# if !defined(SHARED_LIBRARY_BUILD) + static Boolean firstTime = true; +# endif + OSErr result; + TemporaryMemoryHandle tempMemBlock; + Ptr tempPtr = nil; + + tempMemBlock = (TemporaryMemoryHandle)TempNewHandle(size + sizeof(TemporaryMemoryBlock), &result); + if (tempMemBlock && result == noErr) { + HLockHi((Handle)tempMemBlock); + tempPtr = (**tempMemBlock).data; + if (clearMemory) memset(tempPtr, 0, size); + tempPtr = StripAddress(tempPtr); + + /* keep track of the allocated blocks. */ + (**tempMemBlock).nextBlock = theTemporaryMemory; + theTemporaryMemory = tempMemBlock; + } + +# if !defined(SHARED_LIBRARY_BUILD) + /* install an exit routine to clean up the memory used at the end. */ + if (firstTime) { + atexit(&GC_MacFreeTemporaryMemory); + firstTime = false; + } +# endif + + return tempPtr; +} + +extern word GC_fo_entries; + +static void perform_final_collection(void) +{ + unsigned i; + word last_fo_entries = 0; + + /* adjust the stack bottom, because CFM calls us from another stack + location. */ + GC_stackbottom = (ptr_t)&i; + + /* try to collect and finalize everything in sight */ + for (i = 0; i < 2 || GC_fo_entries < last_fo_entries; i++) { + last_fo_entries = GC_fo_entries; + GC_gcollect(); + } +} + + +void GC_MacFreeTemporaryMemory(void) +{ +# if defined(SHARED_LIBRARY_BUILD) + /* if possible, collect all memory, and invoke all finalizers. */ + perform_final_collection(); +# endif + + if (theTemporaryMemory != NULL) { +# if !defined(SHARED_LIBRARY_BUILD) + long totalMemoryUsed = 0; +# endif + TemporaryMemoryHandle tempMemBlock = theTemporaryMemory; + while (tempMemBlock != NULL) { + TemporaryMemoryHandle nextBlock = (**tempMemBlock).nextBlock; +# if !defined(SHARED_LIBRARY_BUILD) + totalMemoryUsed += GetHandleSize((Handle)tempMemBlock); +# endif + DisposeHandle((Handle)tempMemBlock); + tempMemBlock = nextBlock; + } + theTemporaryMemory = NULL; + +# if !defined(SHARED_LIBRARY_BUILD) + if (GC_print_stats) { + fprintf(stdout, "[total memory used: %ld bytes.]\n", + totalMemoryUsed); + fprintf(stdout, "[total collections: %lu]\n", + (unsigned long)GC_gc_no); + } +# endif + } +} + +#endif /* USE_TEMPORARY_MEMORY */ + +#if __option(far_data) + + void* GC_MacGetDataEnd(void) + { + CodeZeroHandle code0 = (CodeZeroHandle)GetResource('CODE', 0); + if (code0) { + long aboveA5Size = (**code0).aboveA5; + ReleaseResource((Handle)code0); + return (LMGetCurrentA5() + aboveA5Size); + } + fprintf(stderr, "Couldn't load the jump table."); + exit(-1); + return 0; + } + +#endif /* __option(far_data) */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/extra/Mac_files/MacOS_config.h b/unity-2019.4.24f1-mbe/external/bdwgc/extra/Mac_files/MacOS_config.h new file mode 100644 index 000000000..abbf9d8ef --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/extra/Mac_files/MacOS_config.h @@ -0,0 +1,27 @@ +/* + MacOS_config.h + + Configuration flags for Macintosh development systems. + + + + 11/16/95 pcb Updated compilation flags to reflect latest 4.6 Makefile. + + by Patrick C. Beard. + */ +/* Boehm, November 17, 1995 12:10 pm PST */ + +#ifdef __MWERKS__ +/* for CodeWarrior Pro with Metrowerks Standard Library (MSL). */ +/* #define MSL_USE_PRECOMPILED_HEADERS 0 */ +#include +#endif /* __MWERKS__ */ + +/* these are defined again in gc_priv.h. */ +#undef TRUE +#undef FALSE + +#define ALL_INTERIOR_POINTERS /* follows interior pointers. */ +/* #define DONT_ADD_BYTE_AT_END */ /* no padding. */ +/* #define SMALL_CONFIG */ /* whether to use a smaller heap. */ +#define USE_TEMPORARY_MEMORY /* use Macintosh temporary memory. */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/extra/Mac_files/dataend.c b/unity-2019.4.24f1-mbe/external/bdwgc/extra/Mac_files/dataend.c new file mode 100644 index 000000000..0bed46946 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/extra/Mac_files/dataend.c @@ -0,0 +1,9 @@ +/* + dataend.c + + A hack to get the extent of global data for the Macintosh. + + by Patrick C. Beard. + */ + +long __dataend; diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/extra/Mac_files/datastart.c b/unity-2019.4.24f1-mbe/external/bdwgc/extra/Mac_files/datastart.c new file mode 100644 index 000000000..a1dc6f6de --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/extra/Mac_files/datastart.c @@ -0,0 +1,9 @@ +/* + datastart.c + + A hack to get the extent of global data for the Macintosh. + + by Patrick C. Beard. + */ + +long __datastart; diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/extra/gc.c b/unity-2019.4.24f1-mbe/external/bdwgc/extra/gc.c new file mode 100644 index 000000000..ec5b5249e --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/extra/gc.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2009 by Hewlett-Packard Development Company. + * All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* This file could be used for the following purposes: */ +/* - get the complete GC as a single link object file (module); */ +/* - enable more compiler optimizations. */ + +/* Tip: to get the highest level of compiler optimizations, the typical */ +/* compiler options (GCC) to use are: */ +/* -O3 -fno-strict-aliasing -march=native -Wall -fprofile-generate/use */ + +/* Warning: GCC for Linux (for C++ clients only): Use -fexceptions both */ +/* for GC and the client otherwise GC_thread_exit_proc() is not */ +/* guaranteed to be invoked (see the comments in pthread_start.c). */ + +#ifndef __cplusplus + /* static is desirable here for more efficient linkage. */ + /* TODO: Enable this in case of the compilation as C++ code. */ +# define GC_INNER STATIC +# define GC_EXTERN GC_INNER + /* STATIC is defined in gcconfig.h. */ +#endif + +/* Small files go first... */ +#include "../backgraph.c" +#include "../blacklst.c" +#include "../checksums.c" +#include "../gcj_mlc.c" +#include "../headers.c" +#include "../new_hblk.c" +#include "../obj_map.c" +#include "../ptr_chck.c" + +#include "gc_inline.h" +#include "../allchblk.c" +#include "../alloc.c" +#include "../dbg_mlc.c" +#include "../finalize.c" +#include "../fnlz_mlc.c" +#include "../malloc.c" +#include "../mallocx.c" +#include "../mark.c" +#include "../mark_rts.c" +#include "../reclaim.c" +#include "../typd_mlc.c" + +#include "../misc.c" +#include "../os_dep.c" +#include "../thread_local_alloc.c" + +/* Unity specific includes */ +#include "../heapsections.c" + +/* Most platform-specific files go here... */ +#include "../darwin_stop_world.c" +#include "../dyn_load.c" +#include "../gc_dlopen.c" +#include "../mach_dep.c" +#include "../pthread_stop_world.c" +#include "../pthread_support.c" +#include "../specific.c" +#include "../win32_threads.c" + +#ifndef GC_PTHREAD_START_STANDALONE +# include "../pthread_start.c" +#endif + +/* Restore pthread calls redirection (if altered in */ +/* pthread_stop_world.c, pthread_support.c or win32_threads.c). */ +/* This is only useful if directly included from application */ +/* (instead of linking gc). */ +#ifndef GC_NO_THREAD_REDIRECTS +# define GC_PTHREAD_REDIRECTS_ONLY +# include "gc_pthread_redirects.h" +#endif + +/* The files from "extra" folder are not included. */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/extra/krait_signal_handler.c b/unity-2019.4.24f1-mbe/external/bdwgc/extra/krait_signal_handler.c new file mode 100644 index 000000000..df1057d6f --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/extra/krait_signal_handler.c @@ -0,0 +1,48 @@ +#if defined(__ANDROID__) +#include + +#define SIGMAX 64 + +static void (*wrapped_signal_handlers[SIGMAX]) (int, siginfo_t *, void *); + +static void signal_handler(int signum, siginfo_t* siginfo, void* sigcontext) +{ + if (wrapped_signal_handlers[signum]) + wrapped_signal_handlers[signum](signum, siginfo, sigcontext); +} + +extern int __real_sigaction(int signum, const struct sigaction *action, struct sigaction *old_action); +__attribute__((used)) int __wrap_sigaction(int signum, const struct sigaction *action, struct sigaction *old_action) +{ + struct sigaction wrapper_action_data; + struct sigaction* wrapper_action = NULL; + + if (signum >= SIGMAX) + return __real_sigaction(signum, action, old_action); + + // patch sig action with our thumb compiled dispatcher + if (action) + { + wrapper_action = &wrapper_action_data; + memcpy(wrapper_action, action, sizeof(*action)); + wrapper_action->sa_sigaction = signal_handler; + + } + + // install handler (abort on error) + if (__real_sigaction(signum, wrapper_action, old_action) == -1) + return -1; + + // hide any previously installed wrapper + if (old_action && old_action->sa_sigaction == signal_handler) + old_action->sa_sigaction = wrapped_signal_handlers[signum]; + + // add action to dispatch table + if (action) + wrapped_signal_handlers[signum] = action->sa_sigaction; + + return 0; +} + +#undef SIGMAX +#endif /* __ANDROID__ */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/extra/msvc_dbg.c b/unity-2019.4.24f1-mbe/external/bdwgc/extra/msvc_dbg.c new file mode 100644 index 000000000..8abe24738 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/extra/msvc_dbg.c @@ -0,0 +1,389 @@ +/* + Copyright (c) 2004 Andrei Polushin + + 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. +*/ + +#if !defined(_M_AMD64) && defined(_MSC_VER) + +/* X86_64 is currently missing some meachine-dependent code below. */ + +#define GC_BUILD +#include "private/msvc_dbg.h" +#include "gc.h" + +#define WIN32_LEAN_AND_MEAN +#include + +#pragma pack(push, 8) +#include +#pragma pack(pop) + +#pragma comment(lib, "dbghelp.lib") +#pragma optimize("gy", off) + +typedef GC_word word; +#define GC_ULONG_PTR word + +#ifdef _WIN64 + typedef GC_ULONG_PTR ULONG_ADDR; +#else + typedef ULONG ULONG_ADDR; +#endif + +static HANDLE GetSymHandle(void) +{ + static HANDLE symHandle = NULL; + if (!symHandle) { + BOOL bRet = SymInitialize(symHandle = GetCurrentProcess(), NULL, FALSE); + if (bRet) { + DWORD dwOptions = SymGetOptions(); + dwOptions &= ~SYMOPT_UNDNAME; + dwOptions |= SYMOPT_LOAD_LINES; + SymSetOptions(dwOptions); + } + } + return symHandle; +} + +static void* CALLBACK FunctionTableAccess(HANDLE hProcess, + ULONG_ADDR dwAddrBase) +{ + return SymFunctionTableAccess(hProcess, dwAddrBase); +} + +static ULONG_ADDR CALLBACK GetModuleBase(HANDLE hProcess, ULONG_ADDR dwAddress) +{ + MEMORY_BASIC_INFORMATION memoryInfo; + ULONG_ADDR dwAddrBase = SymGetModuleBase(hProcess, dwAddress); + if (dwAddrBase) { + return dwAddrBase; + } + if (VirtualQueryEx(hProcess, (void*)(GC_ULONG_PTR)dwAddress, &memoryInfo, + sizeof(memoryInfo))) { + char filePath[_MAX_PATH]; + char curDir[_MAX_PATH]; + char exePath[_MAX_PATH]; + DWORD size = GetModuleFileNameA((HINSTANCE)memoryInfo.AllocationBase, + filePath, sizeof(filePath)); + + /* Save and restore current directory around SymLoadModule, see KB */ + /* article Q189780. */ + GetCurrentDirectoryA(sizeof(curDir), curDir); + GetModuleFileNameA(NULL, exePath, sizeof(exePath)); +#if defined(_MSC_VER) && _MSC_VER == 1200 + /* use strcat for VC6 */ + strcat(exePath, "\\.."); +#else + strcat_s(exePath, sizeof(exePath), "\\.."); +#endif /* _MSC_VER >= 1200 */ + SetCurrentDirectoryA(exePath); +#ifdef _DEBUG + GetCurrentDirectoryA(sizeof(exePath), exePath); +#endif + SymLoadModule(hProcess, NULL, size ? filePath : NULL, NULL, + (ULONG_ADDR)(GC_ULONG_PTR)memoryInfo.AllocationBase, 0); + SetCurrentDirectoryA(curDir); + } + return (ULONG_ADDR)(GC_ULONG_PTR)memoryInfo.AllocationBase; +} + +static ULONG_ADDR CheckAddress(void* address) +{ + ULONG_ADDR dwAddress = (ULONG_ADDR)(GC_ULONG_PTR)address; + GetModuleBase(GetSymHandle(), dwAddress); + return dwAddress; +} + +size_t GetStackFrames(size_t skip, void* frames[], size_t maxFrames) +{ + HANDLE hProcess = GetSymHandle(); + HANDLE hThread = GetCurrentThread(); + CONTEXT context; + context.ContextFlags = CONTEXT_FULL; + if (!GetThreadContext(hThread, &context)) { + return 0; + } + /* GetThreadContext might return invalid context for the current thread. */ +#if defined(_M_IX86) + __asm mov context.Ebp, ebp +#endif + return GetStackFramesFromContext(hProcess, hThread, &context, skip + 1, + frames, maxFrames); +} + +size_t GetStackFramesFromContext(HANDLE hProcess, HANDLE hThread, + CONTEXT* context, size_t skip, + void* frames[], size_t maxFrames) +{ + size_t frameIndex; + DWORD machineType; + STACKFRAME stackFrame = { 0 }; + stackFrame.AddrPC.Mode = AddrModeFlat; +#if defined(_M_IX86) + machineType = IMAGE_FILE_MACHINE_I386; + stackFrame.AddrPC.Offset = context->Eip; + stackFrame.AddrStack.Mode = AddrModeFlat; + stackFrame.AddrStack.Offset = context->Esp; + stackFrame.AddrFrame.Mode = AddrModeFlat; + stackFrame.AddrFrame.Offset = context->Ebp; +#elif defined(_M_MRX000) + machineType = IMAGE_FILE_MACHINE_R4000; + stackFrame.AddrPC.Offset = context->Fir; +#elif defined(_M_ALPHA) + machineType = IMAGE_FILE_MACHINE_ALPHA; + stackFrame.AddrPC.Offset = (unsigned long)context->Fir; +#elif defined(_M_PPC) + machineType = IMAGE_FILE_MACHINE_POWERPC; + stackFrame.AddrPC.Offset = context->Iar; +#elif defined(_M_IA64) + machineType = IMAGE_FILE_MACHINE_IA64; + stackFrame.AddrPC.Offset = context->StIIP; +#elif defined(_M_ALPHA64) + machineType = IMAGE_FILE_MACHINE_ALPHA64; + stackFrame.AddrPC.Offset = context->Fir; +#elif !defined(CPPCHECK) +# error Unknown CPU +#endif + for (frameIndex = 0; frameIndex < maxFrames; ) { + BOOL bRet = StackWalk(machineType, hProcess, hThread, &stackFrame, + &context, NULL, FunctionTableAccess, GetModuleBase, NULL); + if (!bRet) { + break; + } + if (skip) { + skip--; + } else { + frames[frameIndex++] = (void*)(GC_ULONG_PTR)stackFrame.AddrPC.Offset; + } + } + return frameIndex; +} + +size_t GetModuleNameFromAddress(void* address, char* moduleName, size_t size) +{ + if (size) *moduleName = 0; + { + const char* sourceName; + IMAGEHLP_MODULE moduleInfo = { sizeof (moduleInfo) }; + if (!SymGetModuleInfo(GetSymHandle(), CheckAddress(address), + &moduleInfo)) { + return 0; + } + sourceName = strrchr(moduleInfo.ImageName, '\\'); + if (sourceName) { + sourceName++; + } else { + sourceName = moduleInfo.ImageName; + } + if (size) { + strncpy(moduleName, sourceName, size)[size - 1] = 0; + } + return strlen(sourceName); + } +} + +size_t GetModuleNameFromStack(size_t skip, char* moduleName, size_t size) +{ + void* address = NULL; + GetStackFrames(skip + 1, &address, 1); + if (address) { + return GetModuleNameFromAddress(address, moduleName, size); + } + return 0; +} + +size_t GetSymbolNameFromAddress(void* address, char* symbolName, size_t size, + size_t* offsetBytes) +{ + if (size) *symbolName = 0; + if (offsetBytes) *offsetBytes = 0; + __try { + ULONG_ADDR dwOffset = 0; + union { + IMAGEHLP_SYMBOL sym; + char symNameBuffer[sizeof(IMAGEHLP_SYMBOL) + MAX_SYM_NAME]; + } u; + u.sym.SizeOfStruct = sizeof(u.sym); + u.sym.MaxNameLength = sizeof(u.symNameBuffer) - sizeof(u.sym); + + if (!SymGetSymFromAddr(GetSymHandle(), CheckAddress(address), &dwOffset, + &u.sym)) { + return 0; + } else { + const char* sourceName = u.sym.Name; + char undName[1024]; + if (UnDecorateSymbolName(u.sym.Name, undName, sizeof(undName), + UNDNAME_NO_MS_KEYWORDS | UNDNAME_NO_ACCESS_SPECIFIERS)) { + sourceName = undName; + } else if (SymUnDName(&u.sym, undName, sizeof(undName))) { + sourceName = undName; + } + if (offsetBytes) { + *offsetBytes = dwOffset; + } + if (size) { + strncpy(symbolName, sourceName, size)[size - 1] = 0; + } + return strlen(sourceName); + } + } __except (EXCEPTION_EXECUTE_HANDLER) { + SetLastError(GetExceptionCode()); + } + return 0; +} + +size_t GetSymbolNameFromStack(size_t skip, char* symbolName, size_t size, + size_t* offsetBytes) +{ + void* address = NULL; + GetStackFrames(skip + 1, &address, 1); + if (address) { + return GetSymbolNameFromAddress(address, symbolName, size, offsetBytes); + } + return 0; +} + +size_t GetFileLineFromAddress(void* address, char* fileName, size_t size, + size_t* lineNumber, size_t* offsetBytes) +{ + if (size) *fileName = 0; + if (lineNumber) *lineNumber = 0; + if (offsetBytes) *offsetBytes = 0; + { + char* sourceName; + IMAGEHLP_LINE line = { sizeof (line) }; + GC_ULONG_PTR dwOffset = 0; + if (!SymGetLineFromAddr(GetSymHandle(), CheckAddress(address), &dwOffset, + &line)) { + return 0; + } + if (lineNumber) { + *lineNumber = line.LineNumber; + } + if (offsetBytes) { + *offsetBytes = dwOffset; + } + sourceName = line.FileName; + /* TODO: resolve relative filenames, found in 'source directories' */ + /* registered with MSVC IDE. */ + if (size) { + strncpy(fileName, sourceName, size)[size - 1] = 0; + } + return strlen(sourceName); + } +} + +size_t GetFileLineFromStack(size_t skip, char* fileName, size_t size, + size_t* lineNumber, size_t* offsetBytes) +{ + void* address = NULL; + GetStackFrames(skip + 1, &address, 1); + if (address) { + return GetFileLineFromAddress(address, fileName, size, lineNumber, + offsetBytes); + } + return 0; +} + +size_t GetDescriptionFromAddress(void* address, const char* format, + char* buffer, size_t size) +{ + char*const begin = buffer; + char*const end = buffer + size; + size_t line_number = 0; + + if (size) { + *buffer = 0; + } + buffer += GetFileLineFromAddress(address, buffer, size, &line_number, NULL); + size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer; + + if (line_number) { + char str[128]; + + wsprintf(str, "(%d) : ", (int)line_number); + if (size) { + strncpy(buffer, str, size)[size - 1] = 0; + } + buffer += strlen(str); + size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer; + } + + if (size) { + strncpy(buffer, "at ", size)[size - 1] = 0; + } + buffer += strlen("at "); + size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer; + + buffer += GetSymbolNameFromAddress(address, buffer, size, NULL); + size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer; + + if (size) { + strncpy(buffer, " in ", size)[size - 1] = 0; + } + buffer += strlen(" in "); + size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer; + + buffer += GetModuleNameFromAddress(address, buffer, size); + return buffer - begin; +} + +size_t GetDescriptionFromStack(void* const frames[], size_t count, + const char* format, char* description[], + size_t size) +{ + char*const begin = (char*)description; + char*const end = begin + size; + char* buffer = begin + (count + 1) * sizeof(char*); + size_t i; + (void)format; + for (i = 0; i < count; ++i) { + if (size) + description[i] = buffer; + size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer; + buffer += 1 + GetDescriptionFromAddress(frames[i], NULL, buffer, size); + } + if (size) + description[count] = NULL; + return buffer - begin; +} + +/* Compatibility with */ + +int backtrace(void* addresses[], int count) +{ + return GetStackFrames(1, addresses, count); +} + +char** backtrace_symbols(void*const* addresses, int count) +{ + size_t size = GetDescriptionFromStack(addresses, count, NULL, NULL, 0); + char** symbols = (char**)malloc(size); + if (symbols != NULL) + GetDescriptionFromStack(addresses, count, NULL, symbols, size); + return symbols; +} + +#else + + extern int GC_quiet; + /* ANSI C does not allow translation units to be empty. */ + +#endif /* _M_AMD64 */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/extra/pcr_interface.c b/unity-2019.4.24f1-mbe/external/bdwgc/extra/pcr_interface.c new file mode 100644 index 000000000..ff4618743 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/extra/pcr_interface.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +# include "private/gc_priv.h" + +# ifdef PCR +/* + * We wrap all of the allocator functions to avoid questions of + * compatibility between the prototyped and nonprototyped versions of the f + */ +# include "config/PCR_StdTypes.h" +# include "mm/PCR_MM.h" +# include + +# define MY_MAGIC 17L +# define MY_DEBUGMAGIC 42L + +void * GC_AllocProc(size_t size, PCR_Bool ptrFree, PCR_Bool clear ) +{ + if (ptrFree) { + void * result = (void *)GC_malloc_atomic(size); + if (clear && result != 0) BZERO(result, size); + return(result); + } else { + return((void *)GC_malloc(size)); + } +} + +void * GC_DebugAllocProc(size_t size, PCR_Bool ptrFree, PCR_Bool clear ) +{ + if (ptrFree) { + void * result = (void *)GC_debug_malloc_atomic(size, __FILE__, + __LINE__); + if (clear && result != 0) BZERO(result, size); + return(result); + } else { + return((void *)GC_debug_malloc(size, __FILE__, __LINE__)); + } +} + +# define GC_ReallocProc GC_realloc +void * GC_DebugReallocProc(void * old_object, size_t new_size_in_bytes) +{ + return(GC_debug_realloc(old_object, new_size_in_bytes, __FILE__, __LINE__)); +} + +# define GC_FreeProc GC_free +# define GC_DebugFreeProc GC_debug_free + +typedef struct { + PCR_ERes (*ed_proc)(void *p, size_t size, PCR_Any data); + GC_bool ed_pointerfree; + PCR_ERes ed_fail_code; + PCR_Any ed_client_data; +} enumerate_data; + +void GC_enumerate_block(struct hblk *h, enumerate_data * ed) +{ + hdr * hhdr; + size_t sz; + ptr_t p; + ptr_t lim; + word descr; + +# if !defined(CPPCHECK) +# error This code was updated without testing. +# error and its precursor was clearly broken. +# endif + hhdr = HDR(h); + descr = hhdr -> hb_descr; + sz = (size_t)hhdr->hb_sz; + if (descr != 0 && ed -> ed_pointerfree + || descr == 0 && !(ed -> ed_pointerfree)) return; + lim = (ptr_t)(h+1) - sz; + p = (ptr_t)h; + do { + if (PCR_ERes_IsErr(ed -> ed_fail_code)) return; + ed -> ed_fail_code = + (*(ed -> ed_proc))(p, sz, ed -> ed_client_data); + p+= sz; + } while ((word)p <= (word)lim); +} + +struct PCR_MM_ProcsRep * GC_old_allocator = 0; + +PCR_ERes GC_EnumerateProc( + PCR_Bool ptrFree, + PCR_ERes (*proc)(void *p, size_t size, PCR_Any data), + PCR_Any data +) +{ + enumerate_data ed; + + ed.ed_proc = proc; + ed.ed_pointerfree = ptrFree; + ed.ed_fail_code = PCR_ERes_okay; + ed.ed_client_data = data; + GC_apply_to_all_blocks(GC_enumerate_block, &ed); + if (ed.ed_fail_code != PCR_ERes_okay) { + return(ed.ed_fail_code); + } else { + /* Also enumerate objects allocated by my predecessors */ + return((*(GC_old_allocator->mmp_enumerate))(ptrFree, proc, data)); + } +} + +void GC_DummyFreeProc(void *p) {} + +void GC_DummyShutdownProc(void) {} + +struct PCR_MM_ProcsRep GC_Rep = { + MY_MAGIC, + GC_AllocProc, + GC_ReallocProc, + GC_DummyFreeProc, /* mmp_free */ + GC_FreeProc, /* mmp_unsafeFree */ + GC_EnumerateProc, + GC_DummyShutdownProc /* mmp_shutdown */ +}; + +struct PCR_MM_ProcsRep GC_DebugRep = { + MY_DEBUGMAGIC, + GC_DebugAllocProc, + GC_DebugReallocProc, + GC_DummyFreeProc, /* mmp_free */ + GC_DebugFreeProc, /* mmp_unsafeFree */ + GC_EnumerateProc, + GC_DummyShutdownProc /* mmp_shutdown */ +}; + +GC_bool GC_use_debug = 0; + +void GC_pcr_install() +{ + PCR_MM_Install((GC_use_debug? &GC_DebugRep : &GC_Rep), &GC_old_allocator); +} + +PCR_ERes +PCR_GC_Setup(void) +{ + return PCR_ERes_okay; +} + +PCR_ERes +PCR_GC_Run(void) +{ + + if( !PCR_Base_TestPCRArg("-nogc") ) { + GC_quiet = ( PCR_Base_TestPCRArg("-gctrace") ? 0 : 1 ); + GC_use_debug = (GC_bool)PCR_Base_TestPCRArg("-debug_alloc"); + GC_init(); + if( !PCR_Base_TestPCRArg("-nogc_incremental") ) { + /* + * awful hack to test whether VD is implemented ... + */ + if( PCR_VD_Start( 0, NIL, 0) != PCR_ERes_FromErr(ENOSYS) ) { + GC_enable_incremental(); + } + } + } + return PCR_ERes_okay; +} + +void GC_push_thread_structures(void) +{ + /* PCR doesn't work unless static roots are pushed. Can't get here. */ + ABORT("In GC_push_thread_structures()"); +} + +# endif diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/extra/real_malloc.c b/unity-2019.4.24f1-mbe/external/bdwgc/extra/real_malloc.c new file mode 100644 index 000000000..145e73f3b --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/extra/real_malloc.c @@ -0,0 +1,39 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# ifdef PCR +/* + * This definition should go in its own file that includes no other + * header files. Otherwise, we risk not getting the underlying system + * malloc. + */ +# define PCR_NO_RENAME +# include + +void * real_malloc(size_t size) +{ + return(malloc(size)); +} + +# else + +extern int GC_quiet; + /* ANSI C doesn't allow translation units to be empty. */ + /* So we guarantee this one is nonempty. */ + +#endif /* PCR */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/extra/symbian.cpp b/unity-2019.4.24f1-mbe/external/bdwgc/extra/symbian.cpp new file mode 100644 index 000000000..2ec7afbbd --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/extra/symbian.cpp @@ -0,0 +1,52 @@ + +#include +#include +#include +#include +#include +#include + +extern "C" { + +int GC_get_main_symbian_stack_base() +{ + TThreadStackInfo aInfo; + TInt err = RThread().StackInfo(aInfo); + if ( !err ) + { + return aInfo.iBase; + } + else + { + return 0; + } +} + +char* GC_get_private_path_and_zero_file() +{ + // always on c: drive + RFs fs; + fs.Connect(); + fs.CreatePrivatePath( EDriveC ); + TFileName path; + fs.PrivatePath( path ); + fs.Close(); + _LIT( KCDrive, "c:" ); + path.Insert( 0, KCDrive ); + + + //convert to char*, assume ascii + TBuf8 path8; + path8.Copy( path ); + _LIT8( KZero8, "zero" ); + path8.Append( KZero8 ); + + size_t size = path8.Length() + 1; + char* copyChar = (char*) malloc( size ); + if (copyChar) + memcpy( copyChar, path8.PtrZ(), size ); + + return copyChar; // ownership passed +} + +} /* extern "C" */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/extra/symbian/global_end.cpp b/unity-2019.4.24f1-mbe/external/bdwgc/extra/symbian/global_end.cpp new file mode 100644 index 000000000..14c7710d4 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/extra/symbian/global_end.cpp @@ -0,0 +1,10 @@ +// Symbian-specific file. + +// INCLUDE FILES +#include "private/gcconfig.h" + +extern "C" { + +int winscw_data_end; + +} /* extern "C" */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/extra/symbian/global_start.cpp b/unity-2019.4.24f1-mbe/external/bdwgc/extra/symbian/global_start.cpp new file mode 100644 index 000000000..1b030bc4d --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/extra/symbian/global_start.cpp @@ -0,0 +1,10 @@ +// Symbian-specific file. + +// INCLUDE FILES +#include "private/gcconfig.h" + +extern "C" { + +int winscw_data_start; + +} /* extern "C" */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/extra/symbian/init_global_static_roots.cpp b/unity-2019.4.24f1-mbe/external/bdwgc/extra/symbian/init_global_static_roots.cpp new file mode 100644 index 000000000..683d4adab --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/extra/symbian/init_global_static_roots.cpp @@ -0,0 +1,28 @@ +// Symbian-specific file. + +// INCLUDE FILES +#include + +#include "private/gcconfig.h" +#include "gc.h" + +extern "C" { + +void GC_init_global_static_roots() +{ + ptr_t dataStart = NULL; + ptr_t dataEnd = NULL; +# if defined (__WINS__) + extern int winscw_data_start, winscw_data_end; + dataStart = ((ptr_t)&winscw_data_start); + dataEnd = ((ptr_t)&winscw_data_end); +# else + extern int Image$$RW$$Limit[], Image$$RW$$Base[]; + dataStart = ((ptr_t)Image$$RW$$Base); + dataEnd = ((ptr_t)Image$$RW$$Limit); +# endif + + GC_add_roots(dataStart, dataEnd); +} + +} /* extern "C" */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/finalize.c b/unity-2019.4.24f1-mbe/external/bdwgc/finalize.c new file mode 100644 index 000000000..013f82b4b --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/finalize.c @@ -0,0 +1,1426 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1996 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (C) 2007 Free Software Foundation, Inc + + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#include "private/gc_pmark.h" + +#ifndef GC_NO_FINALIZATION +# include "javaxfc.h" /* to get GC_finalize_all() as extern "C" */ + +/* Type of mark procedure used for marking from finalizable object. */ +/* This procedure normally does not mark the object, only its */ +/* descendants. */ +typedef void (* finalization_mark_proc)(ptr_t /* finalizable_obj_ptr */); + +#define HASH3(addr,size,log_size) \ + ((((word)(addr) >> 3) ^ ((word)(addr) >> (3 + (log_size)))) \ + & ((size) - 1)) +#define HASH2(addr,log_size) HASH3(addr, (word)1 << (log_size), log_size) + +struct hash_chain_entry { + word hidden_key; + struct hash_chain_entry * next; +}; + +struct disappearing_link { + struct hash_chain_entry prolog; +# define dl_hidden_link prolog.hidden_key + /* Field to be cleared. */ +# define dl_next(x) (struct disappearing_link *)((x) -> prolog.next) +# define dl_set_next(x, y) \ + (void)((x)->prolog.next = (struct hash_chain_entry *)(y)) + word dl_hidden_obj; /* Pointer to object base */ +}; + +struct dl_hashtbl_s { + struct disappearing_link **head; + signed_word log_size; + word entries; +}; + +STATIC struct dl_hashtbl_s GC_dl_hashtbl = { + /* head */ NULL, /* log_size */ -1, /* entries */ 0 }; +#ifndef GC_LONG_REFS_NOT_NEEDED + STATIC struct dl_hashtbl_s GC_ll_hashtbl = { NULL, -1, 0 }; +#endif + +struct finalizable_object { + struct hash_chain_entry prolog; +# define fo_hidden_base prolog.hidden_key + /* Pointer to object base. */ + /* No longer hidden once object */ + /* is on finalize_now queue. */ +# define fo_next(x) (struct finalizable_object *)((x) -> prolog.next) +# define fo_set_next(x,y) ((x)->prolog.next = (struct hash_chain_entry *)(y)) + GC_finalization_proc fo_fn; /* Finalizer. */ + ptr_t fo_client_data; + word fo_object_size; /* In bytes. */ + finalization_mark_proc fo_mark_proc; /* Mark-through procedure */ +}; + +static signed_word log_fo_table_size = -1; + +STATIC struct fnlz_roots_s { + struct finalizable_object **fo_head; + /* List of objects that should be finalized now: */ + struct finalizable_object *finalize_now; +} GC_fnlz_roots = { NULL, NULL }; + +#ifdef AO_HAVE_store + /* Update finalize_now atomically as GC_should_invoke_finalizers does */ + /* not acquire the allocation lock. */ +# define SET_FINALIZE_NOW(fo) \ + AO_store((volatile AO_t *)&GC_fnlz_roots.finalize_now, (AO_t)(fo)) +#else +# define SET_FINALIZE_NOW(fo) (void)(GC_fnlz_roots.finalize_now = (fo)) +#endif /* !THREADS */ + +GC_API void GC_CALL GC_push_finalizer_structures(void) +{ + GC_ASSERT((word)(&GC_dl_hashtbl.head) % sizeof(word) == 0); + GC_ASSERT((word)(&GC_fnlz_roots) % sizeof(word) == 0); +# ifndef GC_LONG_REFS_NOT_NEEDED + GC_ASSERT((word)(&GC_ll_hashtbl.head) % sizeof(word) == 0); + GC_PUSH_ALL_SYM(GC_ll_hashtbl.head); +# endif + GC_PUSH_ALL_SYM(GC_dl_hashtbl.head); + GC_PUSH_ALL_SYM(GC_fnlz_roots); +} + +/* Threshold of log_size to initiate full collection before growing */ +/* a hash table. */ +#ifndef GC_ON_GROW_LOG_SIZE_MIN +# define GC_ON_GROW_LOG_SIZE_MIN CPP_LOG_HBLKSIZE +#endif + +/* Double the size of a hash table. *log_size_ptr is the log of its */ +/* current size. May be a no-op. */ +/* *table is a pointer to an array of hash headers. If we succeed, we */ +/* update both *table and *log_size_ptr. Lock is held. */ +STATIC void GC_grow_table(struct hash_chain_entry ***table, + signed_word *log_size_ptr, word *entries_ptr) +{ + word i; + struct hash_chain_entry *p; + signed_word log_old_size = *log_size_ptr; + signed_word log_new_size = log_old_size + 1; + word old_size = log_old_size == -1 ? 0 : (word)1 << log_old_size; + word new_size = (word)1 << log_new_size; + /* FIXME: Power of 2 size often gets rounded up to one more page. */ + struct hash_chain_entry **new_table; + + GC_ASSERT(I_HOLD_LOCK()); + /* Avoid growing the table in case of at least 25% of entries can */ + /* be deleted by enforcing a collection. Ignored for small tables. */ + /* In incremental mode we skip this optimization, as we want to */ + /* avoid triggering a full GC whenever possible. */ + if (log_old_size >= GC_ON_GROW_LOG_SIZE_MIN && !GC_incremental) { + IF_CANCEL(int cancel_state;) + + DISABLE_CANCEL(cancel_state); + (void)GC_try_to_collect_inner(GC_never_stop_func); + RESTORE_CANCEL(cancel_state); + /* GC_finalize might decrease entries value. */ + if (*entries_ptr < ((word)1 << log_old_size) - (*entries_ptr >> 2)) + return; + } + + new_table = (struct hash_chain_entry **) + GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE( + (size_t)new_size * sizeof(struct hash_chain_entry *), + NORMAL); + if (new_table == 0) { + if (*table == 0) { + ABORT("Insufficient space for initial table allocation"); + } else { + return; + } + } + for (i = 0; i < old_size; i++) { + p = (*table)[i]; + while (p != 0) { + ptr_t real_key = (ptr_t)GC_REVEAL_POINTER(p->hidden_key); + struct hash_chain_entry *next = p -> next; + size_t new_hash = HASH3(real_key, new_size, log_new_size); + + p -> next = new_table[new_hash]; + GC_dirty(p); + new_table[new_hash] = p; + p = next; + } + } + *log_size_ptr = log_new_size; + *table = new_table; + GC_dirty(new_table); /* entire object */ +} + +GC_API int GC_CALL GC_register_disappearing_link(void * * link) +{ + ptr_t base; + + base = (ptr_t)GC_base(link); + if (base == 0) + ABORT("Bad arg to GC_register_disappearing_link"); + return(GC_general_register_disappearing_link(link, base)); +} + +STATIC int GC_register_disappearing_link_inner( + struct dl_hashtbl_s *dl_hashtbl, void **link, + const void *obj, const char *tbl_log_name) +{ + struct disappearing_link *curr_dl; + size_t index; + struct disappearing_link * new_dl; + DCL_LOCK_STATE; + + if (EXPECT(GC_find_leak, FALSE)) return GC_UNIMPLEMENTED; + LOCK(); + GC_ASSERT(obj != NULL && GC_base_C(obj) == obj); + if (dl_hashtbl -> log_size == -1 + || dl_hashtbl -> entries > ((word)1 << dl_hashtbl -> log_size)) { + GC_grow_table((struct hash_chain_entry ***)&dl_hashtbl -> head, + &dl_hashtbl -> log_size, &dl_hashtbl -> entries); +# ifdef LINT2 + if (dl_hashtbl->log_size < 0) ABORT("log_size is negative"); +# endif + GC_COND_LOG_PRINTF("Grew %s table to %u entries\n", tbl_log_name, + 1 << (unsigned)dl_hashtbl -> log_size); + } + index = HASH2(link, dl_hashtbl -> log_size); + for (curr_dl = dl_hashtbl -> head[index]; curr_dl != 0; + curr_dl = dl_next(curr_dl)) { + if (curr_dl -> dl_hidden_link == GC_HIDE_POINTER(link)) { + curr_dl -> dl_hidden_obj = GC_HIDE_POINTER(obj); + UNLOCK(); + return GC_DUPLICATE; + } + } + new_dl = (struct disappearing_link *) + GC_INTERNAL_MALLOC(sizeof(struct disappearing_link),NORMAL); + if (0 == new_dl) { + GC_oom_func oom_fn = GC_oom_fn; + UNLOCK(); + new_dl = (struct disappearing_link *) + (*oom_fn)(sizeof(struct disappearing_link)); + if (0 == new_dl) { + return GC_NO_MEMORY; + } + /* It's not likely we'll make it here, but ... */ + LOCK(); + /* Recalculate index since the table may grow. */ + index = HASH2(link, dl_hashtbl -> log_size); + /* Check again that our disappearing link not in the table. */ + for (curr_dl = dl_hashtbl -> head[index]; curr_dl != 0; + curr_dl = dl_next(curr_dl)) { + if (curr_dl -> dl_hidden_link == GC_HIDE_POINTER(link)) { + curr_dl -> dl_hidden_obj = GC_HIDE_POINTER(obj); + UNLOCK(); +# ifndef DBG_HDRS_ALL + /* Free unused new_dl returned by GC_oom_fn() */ + GC_free((void *)new_dl); +# endif + return GC_DUPLICATE; + } + } + } + new_dl -> dl_hidden_obj = GC_HIDE_POINTER(obj); + new_dl -> dl_hidden_link = GC_HIDE_POINTER(link); + dl_set_next(new_dl, dl_hashtbl -> head[index]); + dl_hashtbl -> head[index] = new_dl; + dl_hashtbl -> entries++; + GC_dirty(dl_hashtbl->head + index); + UNLOCK(); + GC_dirty(new_dl); + return GC_SUCCESS; +} + +GC_API int GC_CALL GC_general_register_disappearing_link(void * * link, + const void * obj) +{ + if (((word)link & (ALIGNMENT-1)) != 0 || !NONNULL_ARG_NOT_NULL(link)) + ABORT("Bad arg to GC_general_register_disappearing_link"); + return GC_register_disappearing_link_inner(&GC_dl_hashtbl, link, obj, + "dl"); +} + +#ifdef DBG_HDRS_ALL +# define FREE_DL_ENTRY(curr_dl) dl_set_next(curr_dl, NULL) +#else +# define FREE_DL_ENTRY(curr_dl) GC_free(curr_dl) +#endif + +/* Unregisters given link and returns the link entry to free. */ +GC_INLINE struct disappearing_link *GC_unregister_disappearing_link_inner( + struct dl_hashtbl_s *dl_hashtbl, void **link) +{ + struct disappearing_link *curr_dl; + struct disappearing_link *prev_dl = NULL; + size_t index; + + GC_ASSERT(I_HOLD_LOCK()); + if (dl_hashtbl->log_size == -1) + return NULL; /* prevent integer shift by a negative amount */ + + index = HASH2(link, dl_hashtbl->log_size); + for (curr_dl = dl_hashtbl -> head[index]; curr_dl; + curr_dl = dl_next(curr_dl)) { + if (curr_dl -> dl_hidden_link == GC_HIDE_POINTER(link)) { + /* Remove found entry from the table. */ + if (NULL == prev_dl) { + dl_hashtbl -> head[index] = dl_next(curr_dl); + GC_dirty(dl_hashtbl->head + index); + } else { + dl_set_next(prev_dl, dl_next(curr_dl)); + GC_dirty(prev_dl); + } + dl_hashtbl -> entries--; + break; + } + prev_dl = curr_dl; + } + return curr_dl; +} + +GC_API int GC_CALL GC_unregister_disappearing_link(void * * link) +{ + struct disappearing_link *curr_dl; + DCL_LOCK_STATE; + + if (((word)link & (ALIGNMENT-1)) != 0) return(0); /* Nothing to do. */ + + LOCK(); + curr_dl = GC_unregister_disappearing_link_inner(&GC_dl_hashtbl, link); + UNLOCK(); + if (NULL == curr_dl) return 0; + FREE_DL_ENTRY(curr_dl); + return 1; +} + +/* Toggle-ref support. */ +#ifndef GC_TOGGLE_REFS_NOT_NEEDED + typedef union { + /* Lowest bit is used to distinguish between choices. */ + void *strong_ref; + GC_hidden_pointer weak_ref; + } GCToggleRef; + + STATIC GC_toggleref_func GC_toggleref_callback = 0; + STATIC GCToggleRef *GC_toggleref_arr = NULL; + STATIC int GC_toggleref_array_size = 0; + STATIC int GC_toggleref_array_capacity = 0; + + GC_INNER void GC_process_togglerefs(void) + { + int i; + int new_size = 0; + GC_bool needs_barrier = FALSE; + + GC_ASSERT(I_HOLD_LOCK()); + for (i = 0; i < GC_toggleref_array_size; ++i) { + GCToggleRef r = GC_toggleref_arr[i]; + void *obj = r.strong_ref; + + if (((word)obj & 1) != 0) { + obj = GC_REVEAL_POINTER(r.weak_ref); + } + if (NULL == obj) { + continue; + } + switch (GC_toggleref_callback(obj)) { + case GC_TOGGLE_REF_DROP: + break; + case GC_TOGGLE_REF_STRONG: + GC_toggleref_arr[new_size++].strong_ref = obj; + needs_barrier = TRUE; + break; + case GC_TOGGLE_REF_WEAK: + GC_toggleref_arr[new_size++].weak_ref = GC_HIDE_POINTER(obj); + break; + default: + ABORT("Bad toggle-ref status returned by callback"); + } + } + + if (new_size < GC_toggleref_array_size) { + BZERO(&GC_toggleref_arr[new_size], + (GC_toggleref_array_size - new_size) * sizeof(GCToggleRef)); + GC_toggleref_array_size = new_size; + } + if (needs_barrier) + GC_dirty(GC_toggleref_arr); /* entire object */ + } + + STATIC void GC_normal_finalize_mark_proc(ptr_t); + + static void push_and_mark_object(void *p) + { + GC_normal_finalize_mark_proc((ptr_t)p); + while (!GC_mark_stack_empty()) { + MARK_FROM_MARK_STACK(); + } + GC_set_mark_bit(p); + if (GC_mark_state != MS_NONE) { + while (!GC_mark_some(0)) { + /* Empty. */ + } + } + } + + STATIC void GC_mark_togglerefs(void) + { + int i; + if (NULL == GC_toggleref_arr) + return; + + /* TODO: Hide GC_toggleref_arr to avoid its marking from roots. */ + GC_set_mark_bit(GC_toggleref_arr); + for (i = 0; i < GC_toggleref_array_size; ++i) { + void *obj = GC_toggleref_arr[i].strong_ref; + if (obj != NULL && ((word)obj & 1) == 0) { + push_and_mark_object(obj); + } + } + } + + STATIC void GC_clear_togglerefs(void) + { + int i; + for (i = 0; i < GC_toggleref_array_size; ++i) { + if ((GC_toggleref_arr[i].weak_ref & 1) != 0) { + if (!GC_is_marked(GC_REVEAL_POINTER(GC_toggleref_arr[i].weak_ref))) { + GC_toggleref_arr[i].weak_ref = 0; + } else { + /* No need to copy, BDWGC is a non-moving collector. */ + } + } + } + } + + GC_API void GC_CALL GC_set_toggleref_func(GC_toggleref_func fn) + { + DCL_LOCK_STATE; + + LOCK(); + GC_toggleref_callback = fn; + UNLOCK(); + } + + GC_API GC_toggleref_func GC_CALL GC_get_toggleref_func(void) + { + GC_toggleref_func fn; + DCL_LOCK_STATE; + + LOCK(); + fn = GC_toggleref_callback; + UNLOCK(); + return fn; + } + + static GC_bool ensure_toggleref_capacity(int capacity_inc) + { + GC_ASSERT(capacity_inc >= 0); + GC_ASSERT(I_HOLD_LOCK()); + if (NULL == GC_toggleref_arr) { + GC_toggleref_array_capacity = 32; /* initial capacity */ + GC_toggleref_arr = (GCToggleRef *)GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE( + GC_toggleref_array_capacity * sizeof(GCToggleRef), + NORMAL); + if (NULL == GC_toggleref_arr) + return FALSE; + } + if ((unsigned)GC_toggleref_array_size + (unsigned)capacity_inc + >= (unsigned)GC_toggleref_array_capacity) { + GCToggleRef *new_array; + while ((unsigned)GC_toggleref_array_capacity + < (unsigned)GC_toggleref_array_size + (unsigned)capacity_inc) { + GC_toggleref_array_capacity *= 2; + if (GC_toggleref_array_capacity < 0) /* overflow */ + return FALSE; + } + + new_array = (GCToggleRef *)GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE( + GC_toggleref_array_capacity * sizeof(GCToggleRef), + NORMAL); + if (NULL == new_array) + return FALSE; + if (EXPECT(GC_toggleref_array_size > 0, TRUE)) + BCOPY(GC_toggleref_arr, new_array, + GC_toggleref_array_size * sizeof(GCToggleRef)); + GC_INTERNAL_FREE(GC_toggleref_arr); + GC_toggleref_arr = new_array; + } + return TRUE; + } + + GC_API int GC_CALL GC_toggleref_add(void *obj, int is_strong_ref) + { + int res = GC_SUCCESS; + DCL_LOCK_STATE; + + GC_ASSERT(NONNULL_ARG_NOT_NULL(obj)); + LOCK(); + if (GC_toggleref_callback != 0) { + if (!ensure_toggleref_capacity(1)) { + res = GC_NO_MEMORY; + } else { + GC_toggleref_arr[GC_toggleref_array_size].strong_ref = + is_strong_ref ? obj : (void *)GC_HIDE_POINTER(obj); + if (is_strong_ref) + GC_dirty(GC_toggleref_arr + GC_toggleref_array_size); + GC_toggleref_array_size++; + } + } + UNLOCK(); + return res; + } +#endif /* !GC_TOGGLE_REFS_NOT_NEEDED */ + +/* Finalizer callback support. */ +STATIC GC_await_finalize_proc GC_object_finalized_proc = 0; + +GC_API void GC_CALL GC_set_await_finalize_proc(GC_await_finalize_proc fn) +{ + DCL_LOCK_STATE; + + LOCK(); + GC_object_finalized_proc = fn; + UNLOCK(); +} + +GC_API GC_await_finalize_proc GC_CALL GC_get_await_finalize_proc(void) +{ + GC_await_finalize_proc fn; + DCL_LOCK_STATE; + + LOCK(); + fn = GC_object_finalized_proc; + UNLOCK(); + return fn; +} + +#ifndef GC_LONG_REFS_NOT_NEEDED + GC_API int GC_CALL GC_register_long_link(void * * link, const void * obj) + { + if (((word)link & (ALIGNMENT-1)) != 0 || !NONNULL_ARG_NOT_NULL(link)) + ABORT("Bad arg to GC_register_long_link"); + return GC_register_disappearing_link_inner(&GC_ll_hashtbl, link, obj, + "long dl"); + } + + GC_API int GC_CALL GC_unregister_long_link(void * * link) + { + struct disappearing_link *curr_dl; + DCL_LOCK_STATE; + + if (((word)link & (ALIGNMENT-1)) != 0) return(0); /* Nothing to do. */ + + LOCK(); + curr_dl = GC_unregister_disappearing_link_inner(&GC_ll_hashtbl, link); + UNLOCK(); + if (NULL == curr_dl) return 0; + FREE_DL_ENTRY(curr_dl); + return 1; + } +#endif /* !GC_LONG_REFS_NOT_NEEDED */ + +#ifndef GC_MOVE_DISAPPEARING_LINK_NOT_NEEDED + /* Moves a link. Assume the lock is held. */ + STATIC int GC_move_disappearing_link_inner( + struct dl_hashtbl_s *dl_hashtbl, + void **link, void **new_link) + { + struct disappearing_link *curr_dl, *prev_dl, *new_dl; + size_t curr_index, new_index; + word curr_hidden_link; + word new_hidden_link; + + GC_ASSERT(I_HOLD_LOCK()); + if (dl_hashtbl->log_size == -1) + return GC_NOT_FOUND; /* prevent integer shift by a negative amount */ + + /* Find current link. */ + curr_index = HASH2(link, dl_hashtbl -> log_size); + curr_hidden_link = GC_HIDE_POINTER(link); + prev_dl = NULL; + for (curr_dl = dl_hashtbl -> head[curr_index]; curr_dl; + curr_dl = dl_next(curr_dl)) { + if (curr_dl -> dl_hidden_link == curr_hidden_link) + break; + prev_dl = curr_dl; + } + + if (NULL == curr_dl) { + return GC_NOT_FOUND; + } + + if (link == new_link) { + return GC_SUCCESS; /* Nothing to do. */ + } + + /* link found; now check new_link not present. */ + new_index = HASH2(new_link, dl_hashtbl -> log_size); + new_hidden_link = GC_HIDE_POINTER(new_link); + for (new_dl = dl_hashtbl -> head[new_index]; new_dl; + new_dl = dl_next(new_dl)) { + if (new_dl -> dl_hidden_link == new_hidden_link) { + /* Target already registered; bail. */ + return GC_DUPLICATE; + } + } + + /* Remove from old, add to new, update link. */ + if (NULL == prev_dl) { + dl_hashtbl -> head[curr_index] = dl_next(curr_dl); + } else { + dl_set_next(prev_dl, dl_next(curr_dl)); + GC_dirty(prev_dl); + } + curr_dl -> dl_hidden_link = new_hidden_link; + dl_set_next(curr_dl, dl_hashtbl -> head[new_index]); + dl_hashtbl -> head[new_index] = curr_dl; + GC_dirty(curr_dl); + GC_dirty(dl_hashtbl->head); /* entire object */ + return GC_SUCCESS; + } + + GC_API int GC_CALL GC_move_disappearing_link(void **link, void **new_link) + { + int result; + DCL_LOCK_STATE; + + if (((word)new_link & (ALIGNMENT-1)) != 0 + || !NONNULL_ARG_NOT_NULL(new_link)) + ABORT("Bad new_link arg to GC_move_disappearing_link"); + if (((word)link & (ALIGNMENT-1)) != 0) + return GC_NOT_FOUND; /* Nothing to do. */ + + LOCK(); + result = GC_move_disappearing_link_inner(&GC_dl_hashtbl, link, new_link); + UNLOCK(); + return result; + } + +# ifndef GC_LONG_REFS_NOT_NEEDED + GC_API int GC_CALL GC_move_long_link(void **link, void **new_link) + { + int result; + DCL_LOCK_STATE; + + if (((word)new_link & (ALIGNMENT-1)) != 0 + || !NONNULL_ARG_NOT_NULL(new_link)) + ABORT("Bad new_link arg to GC_move_long_link"); + if (((word)link & (ALIGNMENT-1)) != 0) + return GC_NOT_FOUND; /* Nothing to do. */ + + LOCK(); + result = GC_move_disappearing_link_inner(&GC_ll_hashtbl, link, new_link); + UNLOCK(); + return result; + } +# endif /* !GC_LONG_REFS_NOT_NEEDED */ +#endif /* !GC_MOVE_DISAPPEARING_LINK_NOT_NEEDED */ + +/* Possible finalization_marker procedures. Note that mark stack */ +/* overflow is handled by the caller, and is not a disaster. */ +STATIC void GC_normal_finalize_mark_proc(ptr_t p) +{ + hdr * hhdr = HDR(p); + + PUSH_OBJ(p, hhdr, GC_mark_stack_top, + &(GC_mark_stack[GC_mark_stack_size])); +} + +/* This only pays very partial attention to the mark descriptor. */ +/* It does the right thing for normal and atomic objects, and treats */ +/* most others as normal. */ +STATIC void GC_ignore_self_finalize_mark_proc(ptr_t p) +{ + hdr * hhdr = HDR(p); + word descr = hhdr -> hb_descr; + ptr_t q; + ptr_t scan_limit; + ptr_t target_limit = p + hhdr -> hb_sz - 1; + + if ((descr & GC_DS_TAGS) == GC_DS_LENGTH) { + scan_limit = p + descr - sizeof(word); + } else { + scan_limit = target_limit + 1 - sizeof(word); + } + for (q = p; (word)q <= (word)scan_limit; q += ALIGNMENT) { + word r = *(word *)q; + + if (r < (word)p || r > (word)target_limit) { + GC_PUSH_ONE_HEAP(r, q, GC_mark_stack_top); + } + } +} + +STATIC void GC_null_finalize_mark_proc(ptr_t p GC_ATTR_UNUSED) {} + +/* Possible finalization_marker procedures. Note that mark stack */ +/* overflow is handled by the caller, and is not a disaster. */ + +/* GC_unreachable_finalize_mark_proc is an alias for normal marking, */ +/* but it is explicitly tested for, and triggers different */ +/* behavior. Objects registered in this way are not finalized */ +/* if they are reachable by other finalizable objects, even if those */ +/* other objects specify no ordering. */ +STATIC void GC_unreachable_finalize_mark_proc(ptr_t p) +{ + GC_normal_finalize_mark_proc(p); +} + +/* Register a finalization function. See gc.h for details. */ +/* The last parameter is a procedure that determines */ +/* marking for finalization ordering. Any objects marked */ +/* by that procedure will be guaranteed to not have been */ +/* finalized when this finalizer is invoked. */ +STATIC void GC_register_finalizer_inner(void * obj, + GC_finalization_proc fn, void *cd, + GC_finalization_proc *ofn, void **ocd, + finalization_mark_proc mp) +{ + struct finalizable_object * curr_fo; + size_t index; + struct finalizable_object *new_fo = 0; + hdr *hhdr = NULL; /* initialized to prevent warning. */ + DCL_LOCK_STATE; + + if (EXPECT(GC_find_leak, FALSE)) return; + LOCK(); + if (log_fo_table_size == -1 + || GC_fo_entries > ((word)1 << log_fo_table_size)) { + GC_grow_table((struct hash_chain_entry ***)&GC_fnlz_roots.fo_head, + &log_fo_table_size, &GC_fo_entries); +# ifdef LINT2 + if (log_fo_table_size < 0) ABORT("log_size is negative"); +# endif + GC_COND_LOG_PRINTF("Grew fo table to %u entries\n", + 1 << (unsigned)log_fo_table_size); + } + /* in the THREADS case we hold allocation lock. */ + for (;;) { + struct finalizable_object *prev_fo = NULL; + GC_oom_func oom_fn; + + index = HASH2(obj, log_fo_table_size); + curr_fo = GC_fnlz_roots.fo_head[index]; + while (curr_fo != 0) { + GC_ASSERT(GC_size(curr_fo) >= sizeof(struct finalizable_object)); + if (curr_fo -> fo_hidden_base == GC_HIDE_POINTER(obj)) { + /* Interruption by a signal in the middle of this */ + /* should be safe. The client may see only *ocd */ + /* updated, but we'll declare that to be his problem. */ + if (ocd) *ocd = (void *) (curr_fo -> fo_client_data); + if (ofn) *ofn = curr_fo -> fo_fn; + /* Delete the structure for obj. */ + if (prev_fo == 0) { + GC_fnlz_roots.fo_head[index] = fo_next(curr_fo); + } else { + fo_set_next(prev_fo, fo_next(curr_fo)); + GC_dirty(prev_fo); + } + if (fn == 0) { + GC_fo_entries--; + /* May not happen if we get a signal. But a high */ + /* estimate will only make the table larger than */ + /* necessary. */ +# if !defined(THREADS) && !defined(DBG_HDRS_ALL) + GC_free((void *)curr_fo); +# endif + } else { + curr_fo -> fo_fn = fn; + curr_fo -> fo_client_data = (ptr_t)cd; + curr_fo -> fo_mark_proc = mp; + GC_dirty(curr_fo); + /* Reinsert it. We deleted it first to maintain */ + /* consistency in the event of a signal. */ + if (prev_fo == 0) { + GC_fnlz_roots.fo_head[index] = curr_fo; + } else { + fo_set_next(prev_fo, curr_fo); + GC_dirty(prev_fo); + } + } + if (NULL == prev_fo) + GC_dirty(GC_fnlz_roots.fo_head + index); + UNLOCK(); +# ifndef DBG_HDRS_ALL + if (EXPECT(new_fo != 0, FALSE)) { + /* Free unused new_fo returned by GC_oom_fn() */ + GC_free((void *)new_fo); + } +# endif + return; + } + prev_fo = curr_fo; + curr_fo = fo_next(curr_fo); + } + if (EXPECT(new_fo != 0, FALSE)) { + /* new_fo is returned by GC_oom_fn(). */ + GC_ASSERT(fn != 0); +# ifdef LINT2 + if (NULL == hhdr) ABORT("Bad hhdr in GC_register_finalizer_inner"); +# endif + break; + } + if (fn == 0) { + if (ocd) *ocd = 0; + if (ofn) *ofn = 0; + UNLOCK(); + return; + } + GET_HDR(obj, hhdr); + if (EXPECT(0 == hhdr, FALSE)) { + /* We won't collect it, hence finalizer wouldn't be run. */ + if (ocd) *ocd = 0; + if (ofn) *ofn = 0; + UNLOCK(); + return; + } + new_fo = (struct finalizable_object *) + GC_INTERNAL_MALLOC(sizeof(struct finalizable_object),NORMAL); + if (EXPECT(new_fo != 0, TRUE)) + break; + oom_fn = GC_oom_fn; + UNLOCK(); + new_fo = (struct finalizable_object *) + (*oom_fn)(sizeof(struct finalizable_object)); + if (0 == new_fo) { + /* No enough memory. *ocd and *ofn remains unchanged. */ + return; + } + /* It's not likely we'll make it here, but ... */ + LOCK(); + /* Recalculate index since the table may grow and */ + /* check again that our finalizer is not in the table. */ + } + GC_ASSERT(GC_size(new_fo) >= sizeof(struct finalizable_object)); + if (ocd) *ocd = 0; + if (ofn) *ofn = 0; + new_fo -> fo_hidden_base = GC_HIDE_POINTER(obj); + new_fo -> fo_fn = fn; + new_fo -> fo_client_data = (ptr_t)cd; + new_fo -> fo_object_size = hhdr -> hb_sz; + new_fo -> fo_mark_proc = mp; + fo_set_next(new_fo, GC_fnlz_roots.fo_head[index]); + GC_fo_entries++; + GC_fnlz_roots.fo_head[index] = new_fo; + GC_dirty(GC_fnlz_roots.fo_head + index); + UNLOCK(); + GC_dirty(new_fo); +} + +GC_API void GC_CALL GC_register_finalizer(void * obj, + GC_finalization_proc fn, void * cd, + GC_finalization_proc *ofn, void ** ocd) +{ + GC_register_finalizer_inner(obj, fn, cd, ofn, + ocd, GC_normal_finalize_mark_proc); +} + +GC_API void GC_CALL GC_register_finalizer_ignore_self(void * obj, + GC_finalization_proc fn, void * cd, + GC_finalization_proc *ofn, void ** ocd) +{ + GC_register_finalizer_inner(obj, fn, cd, ofn, + ocd, GC_ignore_self_finalize_mark_proc); +} + +GC_API void GC_CALL GC_register_finalizer_no_order(void * obj, + GC_finalization_proc fn, void * cd, + GC_finalization_proc *ofn, void ** ocd) +{ + GC_register_finalizer_inner(obj, fn, cd, ofn, + ocd, GC_null_finalize_mark_proc); +} + +static GC_bool need_unreachable_finalization = FALSE; + /* Avoid the work if this isn't used. */ + +GC_API void GC_CALL GC_register_finalizer_unreachable(void * obj, + GC_finalization_proc fn, void * cd, + GC_finalization_proc *ofn, void ** ocd) +{ + need_unreachable_finalization = TRUE; + GC_ASSERT(GC_java_finalization); + GC_register_finalizer_inner(obj, fn, cd, ofn, + ocd, GC_unreachable_finalize_mark_proc); +} + +#ifndef NO_DEBUGGING + STATIC void GC_dump_finalization_links( + const struct dl_hashtbl_s *dl_hashtbl) + { + size_t dl_size = dl_hashtbl->log_size == -1 ? 0 : + (size_t)1 << dl_hashtbl->log_size; + size_t i; + + for (i = 0; i < dl_size; i++) { + struct disappearing_link *curr_dl; + + for (curr_dl = dl_hashtbl -> head[i]; curr_dl != 0; + curr_dl = dl_next(curr_dl)) { + ptr_t real_ptr = (ptr_t)GC_REVEAL_POINTER(curr_dl->dl_hidden_obj); + ptr_t real_link = (ptr_t)GC_REVEAL_POINTER(curr_dl->dl_hidden_link); + + GC_printf("Object: %p, link: %p\n", + (void *)real_ptr, (void *)real_link); + } + } + } + + GC_API void GC_CALL GC_dump_finalization(void) + { + struct finalizable_object * curr_fo; + size_t fo_size = log_fo_table_size == -1 ? 0 : + (size_t)1 << log_fo_table_size; + size_t i; + + GC_printf("Disappearing (short) links:\n"); + GC_dump_finalization_links(&GC_dl_hashtbl); +# ifndef GC_LONG_REFS_NOT_NEEDED + GC_printf("Disappearing long links:\n"); + GC_dump_finalization_links(&GC_ll_hashtbl); +# endif + GC_printf("Finalizers:\n"); + for (i = 0; i < fo_size; i++) { + for (curr_fo = GC_fnlz_roots.fo_head[i]; + curr_fo != NULL; curr_fo = fo_next(curr_fo)) { + ptr_t real_ptr = (ptr_t)GC_REVEAL_POINTER(curr_fo->fo_hidden_base); + + GC_printf("Finalizable object: %p\n", (void *)real_ptr); + } + } + } +#endif /* !NO_DEBUGGING */ + +#ifndef SMALL_CONFIG + STATIC word GC_old_dl_entries = 0; /* for stats printing */ +# ifndef GC_LONG_REFS_NOT_NEEDED + STATIC word GC_old_ll_entries = 0; +# endif +#endif /* !SMALL_CONFIG */ + +#ifndef THREADS + /* Global variables to minimize the level of recursion when a client */ + /* finalizer allocates memory. */ + STATIC int GC_finalizer_nested = 0; + /* Only the lowest byte is used, the rest is */ + /* padding for proper global data alignment */ + /* required for some compilers (like Watcom). */ + STATIC unsigned GC_finalizer_skipped = 0; + + /* Checks and updates the level of finalizers recursion. */ + /* Returns NULL if GC_invoke_finalizers() should not be called by the */ + /* collector (to minimize the risk of a deep finalizers recursion), */ + /* otherwise returns a pointer to GC_finalizer_nested. */ + STATIC unsigned char *GC_check_finalizer_nested(void) + { + unsigned nesting_level = *(unsigned char *)&GC_finalizer_nested; + if (nesting_level) { + /* We are inside another GC_invoke_finalizers(). */ + /* Skip some implicitly-called GC_invoke_finalizers() */ + /* depending on the nesting (recursion) level. */ + if (++GC_finalizer_skipped < (1U << nesting_level)) return NULL; + GC_finalizer_skipped = 0; + } + *(char *)&GC_finalizer_nested = (char)(nesting_level + 1); + return (unsigned char *)&GC_finalizer_nested; + } +#endif /* THREADS */ + +#define ITERATE_DL_HASHTBL_BEGIN(dl_hashtbl, curr_dl, prev_dl) \ + { \ + size_t i; \ + size_t dl_size = dl_hashtbl->log_size == -1 ? 0 : \ + (size_t)1 << dl_hashtbl->log_size; \ + GC_bool needs_barrier = FALSE; \ + GC_ASSERT(I_HOLD_LOCK()); \ + for (i = 0; i < dl_size; i++) { \ + struct disappearing_link *prev_dl = NULL; \ + curr_dl = dl_hashtbl -> head[i]; \ + while (curr_dl) { + +#define ITERATE_DL_HASHTBL_END(curr_dl, prev_dl) \ + prev_dl = curr_dl; \ + curr_dl = dl_next(curr_dl); \ + } \ + } \ + if (needs_barrier) \ + GC_dirty(dl_hashtbl -> head); /* entire object */ \ + } + +#define DELETE_DL_HASHTBL_ENTRY(dl_hashtbl, curr_dl, prev_dl, next_dl) \ + { \ + next_dl = dl_next(curr_dl); \ + if (NULL == prev_dl) { \ + dl_hashtbl -> head[i] = next_dl; \ + needs_barrier = TRUE; \ + } else { \ + dl_set_next(prev_dl, next_dl); \ + GC_dirty(prev_dl); \ + } \ + GC_clear_mark_bit(curr_dl); \ + dl_hashtbl -> entries--; \ + curr_dl = next_dl; \ + continue; \ + } + +GC_INLINE void GC_make_disappearing_links_disappear( + struct dl_hashtbl_s* dl_hashtbl) +{ + struct disappearing_link *curr, *next; + + ITERATE_DL_HASHTBL_BEGIN(dl_hashtbl, curr, prev) + ptr_t real_ptr = (ptr_t)GC_REVEAL_POINTER(curr->dl_hidden_obj); + ptr_t real_link = (ptr_t)GC_REVEAL_POINTER(curr->dl_hidden_link); + + if (!GC_is_marked(real_ptr)) { + *(word *)real_link = 0; + GC_clear_mark_bit(curr); + DELETE_DL_HASHTBL_ENTRY(dl_hashtbl, curr, prev, next); + } + ITERATE_DL_HASHTBL_END(curr, prev) +} + +GC_INLINE void GC_remove_dangling_disappearing_links( + struct dl_hashtbl_s* dl_hashtbl) +{ + struct disappearing_link *curr, *next; + + ITERATE_DL_HASHTBL_BEGIN(dl_hashtbl, curr, prev) + ptr_t real_link = + (ptr_t)GC_base(GC_REVEAL_POINTER(curr->dl_hidden_link)); + + if (NULL != real_link && !GC_is_marked(real_link)) { + GC_clear_mark_bit(curr); + DELETE_DL_HASHTBL_ENTRY(dl_hashtbl, curr, prev, next); + } + ITERATE_DL_HASHTBL_END(curr, prev) +} + +/* Called with held lock (but the world is running). */ +/* Cause disappearing links to disappear and unreachable objects to be */ +/* enqueued for finalization. */ +GC_INNER void GC_finalize(void) +{ + struct finalizable_object * curr_fo, * prev_fo, * next_fo; + ptr_t real_ptr; + size_t i; + size_t fo_size = log_fo_table_size == -1 ? 0 : + (size_t)1 << log_fo_table_size; + GC_bool needs_barrier = FALSE; + + GC_ASSERT(I_HOLD_LOCK()); +# ifndef SMALL_CONFIG + /* Save current GC_[dl/ll]_entries value for stats printing */ + GC_old_dl_entries = GC_dl_hashtbl.entries; +# ifndef GC_LONG_REFS_NOT_NEEDED + GC_old_ll_entries = GC_ll_hashtbl.entries; +# endif +# endif + +# ifndef GC_TOGGLE_REFS_NOT_NEEDED + GC_mark_togglerefs(); +# endif + GC_make_disappearing_links_disappear(&GC_dl_hashtbl); + + /* Mark all objects reachable via chains of 1 or more pointers */ + /* from finalizable objects. */ + GC_ASSERT(GC_mark_state == MS_NONE); + for (i = 0; i < fo_size; i++) { + for (curr_fo = GC_fnlz_roots.fo_head[i]; + curr_fo != NULL; curr_fo = fo_next(curr_fo)) { + GC_ASSERT(GC_size(curr_fo) >= sizeof(struct finalizable_object)); + real_ptr = (ptr_t)GC_REVEAL_POINTER(curr_fo->fo_hidden_base); + if (!GC_is_marked(real_ptr)) { + GC_MARKED_FOR_FINALIZATION(real_ptr); + GC_MARK_FO(real_ptr, curr_fo -> fo_mark_proc); + if (GC_is_marked(real_ptr)) { + WARN("Finalization cycle involving %p\n", real_ptr); + } + } + } + } + /* Enqueue for finalization all objects that are still */ + /* unreachable. */ + GC_bytes_finalized = 0; + for (i = 0; i < fo_size; i++) { + curr_fo = GC_fnlz_roots.fo_head[i]; + prev_fo = 0; + while (curr_fo != 0) { + real_ptr = (ptr_t)GC_REVEAL_POINTER(curr_fo->fo_hidden_base); + if (!GC_is_marked(real_ptr)) { + if (!GC_java_finalization) { + GC_set_mark_bit(real_ptr); + } + /* Delete from hash table */ + next_fo = fo_next(curr_fo); + if (NULL == prev_fo) { + GC_fnlz_roots.fo_head[i] = next_fo; + needs_barrier = TRUE; + } else { + fo_set_next(prev_fo, next_fo); + GC_dirty(prev_fo); + } + GC_fo_entries--; + if (GC_object_finalized_proc) + GC_object_finalized_proc(real_ptr); + + /* Add to list of objects awaiting finalization. */ + fo_set_next(curr_fo, GC_fnlz_roots.finalize_now); + GC_dirty(curr_fo); + SET_FINALIZE_NOW(curr_fo); + /* unhide object pointer so any future collections will */ + /* see it. */ + curr_fo -> fo_hidden_base = + (word)GC_REVEAL_POINTER(curr_fo -> fo_hidden_base); + GC_bytes_finalized += + curr_fo -> fo_object_size + + sizeof(struct finalizable_object); + GC_ASSERT(GC_is_marked(GC_base(curr_fo))); + curr_fo = next_fo; + } else { + prev_fo = curr_fo; + curr_fo = fo_next(curr_fo); + } + } + } + + if (GC_java_finalization) { + /* make sure we mark everything reachable from objects finalized + using the no_order mark_proc */ + for (curr_fo = GC_fnlz_roots.finalize_now; + curr_fo != NULL; curr_fo = fo_next(curr_fo)) { + real_ptr = (ptr_t)curr_fo -> fo_hidden_base; + if (!GC_is_marked(real_ptr)) { + if (curr_fo -> fo_mark_proc == GC_null_finalize_mark_proc) { + GC_MARK_FO(real_ptr, GC_normal_finalize_mark_proc); + } + if (curr_fo -> fo_mark_proc != GC_unreachable_finalize_mark_proc) { + GC_set_mark_bit(real_ptr); + } + } + } + + /* now revive finalize-when-unreachable objects reachable from + other finalizable objects */ + if (need_unreachable_finalization) { + curr_fo = GC_fnlz_roots.finalize_now; +# if defined(GC_ASSERTIONS) || defined(LINT2) + if (curr_fo != NULL && log_fo_table_size < 0) + ABORT("log_size is negative"); +# endif + prev_fo = NULL; + while (curr_fo != NULL) { + next_fo = fo_next(curr_fo); + if (curr_fo -> fo_mark_proc == GC_unreachable_finalize_mark_proc) { + real_ptr = (ptr_t)curr_fo -> fo_hidden_base; + if (!GC_is_marked(real_ptr)) { + GC_set_mark_bit(real_ptr); + } else { + if (NULL == prev_fo) { + SET_FINALIZE_NOW(next_fo); + } else { + fo_set_next(prev_fo, next_fo); + GC_dirty(prev_fo); + } + curr_fo -> fo_hidden_base = + GC_HIDE_POINTER(curr_fo -> fo_hidden_base); + GC_bytes_finalized -= + curr_fo->fo_object_size + sizeof(struct finalizable_object); + + i = HASH2(real_ptr, log_fo_table_size); + fo_set_next(curr_fo, GC_fnlz_roots.fo_head[i]); + GC_dirty(curr_fo); + GC_fo_entries++; + GC_fnlz_roots.fo_head[i] = curr_fo; + curr_fo = prev_fo; + needs_barrier = TRUE; + } + } + prev_fo = curr_fo; + curr_fo = next_fo; + } + } + } + if (needs_barrier) + GC_dirty(GC_fnlz_roots.fo_head); /* entire object */ + + GC_remove_dangling_disappearing_links(&GC_dl_hashtbl); +# ifndef GC_TOGGLE_REFS_NOT_NEEDED + GC_clear_togglerefs(); +# endif +# ifndef GC_LONG_REFS_NOT_NEEDED + GC_make_disappearing_links_disappear(&GC_ll_hashtbl); + GC_remove_dangling_disappearing_links(&GC_ll_hashtbl); +# endif + + if (GC_fail_count) { + /* Don't prevent running finalizers if there has been an allocation */ + /* failure recently. */ +# ifdef THREADS + GC_reset_finalizer_nested(); +# else + GC_finalizer_nested = 0; +# endif + } +} + +#ifndef JAVA_FINALIZATION_NOT_NEEDED + + /* Enqueue all remaining finalizers to be run. */ + STATIC void GC_enqueue_all_finalizers(void) + { + struct finalizable_object * next_fo; + int i; + int fo_size; + + GC_ASSERT(I_HOLD_LOCK()); + fo_size = log_fo_table_size == -1 ? 0 : 1 << log_fo_table_size; + GC_bytes_finalized = 0; + for (i = 0; i < fo_size; i++) { + struct finalizable_object * curr_fo = GC_fnlz_roots.fo_head[i]; + + GC_fnlz_roots.fo_head[i] = NULL; + while (curr_fo != NULL) { + ptr_t real_ptr = (ptr_t)GC_REVEAL_POINTER(curr_fo->fo_hidden_base); + + GC_MARK_FO(real_ptr, GC_normal_finalize_mark_proc); + GC_set_mark_bit(real_ptr); + + next_fo = fo_next(curr_fo); + + /* Add to list of objects awaiting finalization. */ + fo_set_next(curr_fo, GC_fnlz_roots.finalize_now); + GC_dirty(curr_fo); + SET_FINALIZE_NOW(curr_fo); + + /* unhide object pointer so any future collections will */ + /* see it. */ + curr_fo -> fo_hidden_base = + (word)GC_REVEAL_POINTER(curr_fo -> fo_hidden_base); + GC_bytes_finalized += + curr_fo -> fo_object_size + sizeof(struct finalizable_object); + curr_fo = next_fo; + } + } + GC_fo_entries = 0; /* all entries deleted from the hash table */ + } + + /* Invoke all remaining finalizers that haven't yet been run. + * This is needed for strict compliance with the Java standard, + * which can make the runtime guarantee that all finalizers are run. + * Unfortunately, the Java standard implies we have to keep running + * finalizers until there are no more left, a potential infinite loop. + * YUCK. + * Note that this is even more dangerous than the usual Java + * finalizers, in that objects reachable from static variables + * may have been finalized when these finalizers are run. + * Finalizers run at this point must be prepared to deal with a + * mostly broken world. + * This routine is externally callable, so is called without + * the allocation lock. + */ + GC_API void GC_CALL GC_finalize_all(void) + { + DCL_LOCK_STATE; + + LOCK(); + while (GC_fo_entries > 0) { + GC_enqueue_all_finalizers(); + UNLOCK(); + GC_invoke_finalizers(); + /* Running the finalizers in this thread is arguably not a good */ + /* idea when we should be notifying another thread to run them. */ + /* But otherwise we don't have a great way to wait for them to */ + /* run. */ + LOCK(); + } + UNLOCK(); + } + +#endif /* !JAVA_FINALIZATION_NOT_NEEDED */ + +/* Returns true if it is worth calling GC_invoke_finalizers. (Useful if */ +/* finalizers can only be called from some kind of "safe state" and */ +/* getting into that safe state is expensive.) */ +GC_API int GC_CALL GC_should_invoke_finalizers(void) +{ +# ifdef AO_HAVE_load + return AO_load((volatile AO_t *)&GC_fnlz_roots.finalize_now) != 0; +# else + return GC_fnlz_roots.finalize_now != NULL; +# endif /* !THREADS */ +} + +/* Invoke finalizers for all objects that are ready to be finalized. */ +/* Should be called without allocation lock. */ +GC_API int GC_CALL GC_invoke_finalizers(void) +{ + int count = 0; + word bytes_freed_before = 0; /* initialized to prevent warning. */ + DCL_LOCK_STATE; + + while (GC_should_invoke_finalizers()) { + struct finalizable_object * curr_fo; + +# ifdef THREADS + LOCK(); +# endif + if (count == 0) { + bytes_freed_before = GC_bytes_freed; + /* Don't do this outside, since we need the lock. */ + } + curr_fo = GC_fnlz_roots.finalize_now; +# ifdef THREADS + if (curr_fo != NULL) + SET_FINALIZE_NOW(fo_next(curr_fo)); + UNLOCK(); + if (curr_fo == 0) break; +# else + GC_fnlz_roots.finalize_now = fo_next(curr_fo); +# endif + fo_set_next(curr_fo, 0); + (*(curr_fo -> fo_fn))((ptr_t)(curr_fo -> fo_hidden_base), + curr_fo -> fo_client_data); + curr_fo -> fo_client_data = 0; + ++count; + /* Explicit freeing of curr_fo is probably a bad idea. */ + /* It throws off accounting if nearly all objects are */ + /* finalizable. Otherwise it should not matter. */ + } + /* bytes_freed_before is initialized whenever count != 0 */ + if (count != 0 +# if defined(THREADS) && !defined(THREAD_SANITIZER) + /* A quick check whether some memory was freed. */ + /* The race with GC_free() is safe to be ignored */ + /* because we only need to know if the current */ + /* thread has deallocated something. */ + && bytes_freed_before != GC_bytes_freed +# endif + ) { + LOCK(); + GC_finalizer_bytes_freed += (GC_bytes_freed - bytes_freed_before); + UNLOCK(); + } + return count; +} + +static word last_finalizer_notification = 0; + +GC_INNER void GC_notify_or_invoke_finalizers(void) +{ + GC_finalizer_notifier_proc notifier_fn = 0; +# if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH) + static word last_back_trace_gc_no = 1; /* Skip first one. */ +# endif + DCL_LOCK_STATE; + +# if defined(THREADS) && !defined(KEEP_BACK_PTRS) \ + && !defined(MAKE_BACK_GRAPH) + /* Quick check (while unlocked) for an empty finalization queue. */ + if (!GC_should_invoke_finalizers()) + return; +# endif + LOCK(); + + /* This is a convenient place to generate backtraces if appropriate, */ + /* since that code is not callable with the allocation lock. */ +# if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH) + if (GC_gc_no > last_back_trace_gc_no) { +# ifdef KEEP_BACK_PTRS + long i; + /* Stops when GC_gc_no wraps; that's OK. */ + last_back_trace_gc_no = (word)(-1); /* disable others. */ + for (i = 0; i < GC_backtraces; ++i) { + /* FIXME: This tolerates concurrent heap mutation, */ + /* which may cause occasional mysterious results. */ + /* We need to release the GC lock, since GC_print_callers */ + /* acquires it. It probably shouldn't. */ + UNLOCK(); + GC_generate_random_backtrace_no_gc(); + LOCK(); + } + last_back_trace_gc_no = GC_gc_no; +# endif +# ifdef MAKE_BACK_GRAPH + if (GC_print_back_height) { + GC_print_back_graph_stats(); + } +# endif + } +# endif + if (NULL == GC_fnlz_roots.finalize_now) { + UNLOCK(); + return; + } + + if (!GC_finalize_on_demand) { + unsigned char *pnested = GC_check_finalizer_nested(); + UNLOCK(); + /* Skip GC_invoke_finalizers() if nested */ + if (pnested != NULL) { + (void) GC_invoke_finalizers(); + *pnested = 0; /* Reset since no more finalizers. */ +# ifndef THREADS + GC_ASSERT(NULL == GC_fnlz_roots.finalize_now); +# endif /* Otherwise GC can run concurrently and add more */ + } + return; + } + + /* These variables require synchronization to avoid data races. */ + if (last_finalizer_notification != GC_gc_no) { + last_finalizer_notification = GC_gc_no; + notifier_fn = GC_finalizer_notifier; + } + UNLOCK(); + if (notifier_fn != 0) + (*notifier_fn)(); /* Invoke the notifier */ +} + +#ifndef SMALL_CONFIG +# ifndef GC_LONG_REFS_NOT_NEEDED +# define IF_LONG_REFS_PRESENT_ELSE(x,y) (x) +# else +# define IF_LONG_REFS_PRESENT_ELSE(x,y) (y) +# endif + + GC_INNER void GC_print_finalization_stats(void) + { + struct finalizable_object *fo; + unsigned long ready = 0; + + GC_log_printf("%lu finalization entries;" + " %lu/%lu short/long disappearing links alive\n", + (unsigned long)GC_fo_entries, + (unsigned long)GC_dl_hashtbl.entries, + (unsigned long)IF_LONG_REFS_PRESENT_ELSE( + GC_ll_hashtbl.entries, 0)); + + for (fo = GC_fnlz_roots.finalize_now; fo != NULL; fo = fo_next(fo)) + ++ready; + GC_log_printf("%lu finalization-ready objects;" + " %ld/%ld short/long links cleared\n", + ready, + (long)GC_old_dl_entries - (long)GC_dl_hashtbl.entries, + (long)IF_LONG_REFS_PRESENT_ELSE( + GC_old_ll_entries - GC_ll_hashtbl.entries, 0)); + } +#endif /* !SMALL_CONFIG */ + +#endif /* !GC_NO_FINALIZATION */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/fnlz_mlc.c b/unity-2019.4.24f1-mbe/external/bdwgc/fnlz_mlc.c new file mode 100644 index 000000000..17c73ae44 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/fnlz_mlc.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2011 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + */ + +#include "private/gc_priv.h" + +#ifdef ENABLE_DISCLAIM + +#include "gc_disclaim.h" +#include "gc_inline.h" /* for GC_malloc_kind */ + +STATIC int GC_finalized_kind = 0; + +#if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH) + /* The first bit is already used for a debug purpose. */ +# define FINALIZER_CLOSURE_FLAG 0x2 +#else +# define FINALIZER_CLOSURE_FLAG 0x1 +#endif + +STATIC int GC_CALLBACK GC_finalized_disclaim(void *obj) +{ + word fc_word = *(word *)obj; + + if ((fc_word & FINALIZER_CLOSURE_FLAG) != 0) { + /* The disclaim function may be passed fragments from the */ + /* free-list, on which it should not run finalization. */ + /* To recognize this case, we use the fact that the first word */ + /* on such fragments is always multiple of 4 (a link to the next */ + /* fragment, or NULL). If it is desirable to have a finalizer */ + /* which does not use the first word for storing finalization */ + /* info, GC_reclaim_with_finalization must be extended to clear */ + /* fragments so that the assumption holds for the selected word. */ + const struct GC_finalizer_closure *fc + = (struct GC_finalizer_closure *)(fc_word + & ~(word)FINALIZER_CLOSURE_FLAG); + (*fc->proc)((word *)obj + 1, fc->cd); + } + return 0; +} + +GC_API void GC_CALL GC_init_finalized_malloc(void) +{ + DCL_LOCK_STATE; + + GC_init(); /* In case it's not already done. */ + LOCK(); + if (GC_finalized_kind != 0) { + UNLOCK(); + return; + } + + /* The finalizer closure is placed in the first word in order to */ + /* use the lower bits to distinguish live objects from objects on */ + /* the free list. The downside of this is that we need one-word */ + /* offset interior pointers, and that GC_base does not return the */ + /* start of the user region. */ + GC_register_displacement_inner(sizeof(word)); + + GC_finalized_kind = GC_new_kind_inner(GC_new_free_list_inner(), + GC_DS_LENGTH, TRUE, TRUE); + GC_ASSERT(GC_finalized_kind != 0); + GC_register_disclaim_proc(GC_finalized_kind, GC_finalized_disclaim, TRUE); + UNLOCK(); +} + +GC_API void GC_CALL GC_register_disclaim_proc(int kind, GC_disclaim_proc proc, + int mark_unconditionally) +{ + GC_ASSERT((unsigned)kind < MAXOBJKINDS); + GC_obj_kinds[kind].ok_disclaim_proc = proc; + GC_obj_kinds[kind].ok_mark_unconditionally = (GC_bool)mark_unconditionally; +} + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_finalized_malloc(size_t lb, + const struct GC_finalizer_closure *fclos) +{ + word *op; + + GC_ASSERT(GC_finalized_kind != 0); + op = (word *)GC_malloc_kind(SIZET_SAT_ADD(lb, sizeof(word)), + GC_finalized_kind); + if (EXPECT(NULL == op, FALSE)) + return NULL; + *op = (word)fclos | FINALIZER_CLOSURE_FLAG; + return op + 1; +} + +#endif /* ENABLE_DISCLAIM */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/gc.mak b/unity-2019.4.24f1-mbe/external/bdwgc/gc.mak new file mode 100644 index 000000000..987dbb584 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/gc.mak @@ -0,0 +1,2237 @@ +# Microsoft Developer Studio Generated NMAKE File, Format Version 4.10 +# This has been hand-edited way too many times. +# A clean, manually generated makefile would be an improvement. + +# TARGTYPE "Win32 (x86) Application" 0x0101 +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +!IF "$(CFG)" == "" +CFG=gctest - Win32 Release +!MESSAGE No configuration specified. Defaulting to gctest - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "gc - Win32 Release" && "$(CFG)" != "gc - Win32 Debug" &&\ + "$(CFG)" != "gctest - Win32 Release" && "$(CFG)" != "gctest - Win32 Debug" &&\ + "$(CFG)" != "cord - Win32 Release" && "$(CFG)" != "cord - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE on this makefile +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "gc.mak" CFG="cord - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "gc - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "gc - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "gctest - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "gctest - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE "cord - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "cord - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF +################################################################################ +# Begin Project +# PROP Target_Last_Scanned "gctest - Win32 Debug" + +!IF "$(CFG)" == "gc - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +OUTDIR=.\Release +INTDIR=.\Release + +ALL : ".\Release\gc.dll" ".\Release\gc.bsc" + +CLEAN : + -@erase ".\Release\allchblk.obj" + -@erase ".\Release\allchblk.sbr" + -@erase ".\Release\alloc.obj" + -@erase ".\Release\alloc.sbr" + -@erase ".\Release\blacklst.obj" + -@erase ".\Release\blacklst.sbr" + -@erase ".\Release\checksums.obj" + -@erase ".\Release\checksums.sbr" + -@erase ".\Release\dbg_mlc.obj" + -@erase ".\Release\dbg_mlc.sbr" + -@erase ".\Release\dyn_load.obj" + -@erase ".\Release\dyn_load.sbr" + -@erase ".\Release\finalize.obj" + -@erase ".\Release\finalize.sbr" + -@erase ".\Release\fnlz_mlc.obj" + -@erase ".\Release\fnlz_mlc.sbr" + -@erase ".\Release\gc.bsc" + -@erase ".\Release\gc_cpp.obj" + -@erase ".\Release\gc_cpp.sbr" + -@erase ".\Release\gc.dll" + -@erase ".\Release\gc.exp" + -@erase ".\Release\gc.lib" + -@erase ".\Release\headers.obj" + -@erase ".\Release\headers.sbr" + -@erase ".\Release\mach_dep.obj" + -@erase ".\Release\mach_dep.sbr" + -@erase ".\Release\malloc.obj" + -@erase ".\Release\malloc.sbr" + -@erase ".\Release\mallocx.obj" + -@erase ".\Release\mallocx.sbr" + -@erase ".\Release\mark.obj" + -@erase ".\Release\mark.sbr" + -@erase ".\Release\mark_rts.obj" + -@erase ".\Release\mark_rts.sbr" + -@erase ".\Release\misc.obj" + -@erase ".\Release\misc.sbr" + -@erase ".\Release\new_hblk.obj" + -@erase ".\Release\new_hblk.sbr" + -@erase ".\Release\obj_map.obj" + -@erase ".\Release\obj_map.sbr" + -@erase ".\Release\os_dep.obj" + -@erase ".\Release\os_dep.sbr" + -@erase ".\Release\ptr_chck.obj" + -@erase ".\Release\ptr_chck.sbr" + -@erase ".\Release\reclaim.obj" + -@erase ".\Release\reclaim.sbr" + -@erase ".\Release\typd_mlc.obj" + -@erase ".\Release\typd_mlc.sbr" + -@erase ".\Release\win32_threads.obj" + -@erase ".\Release\win32_threads.sbr" + -@erase ".\Release\msvc_dbg.copied.obj" + -@erase ".\Release\msvc_dbg.copied.sbr" + -@erase ".\msvc_dbg.copied.c" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I include /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "ALL_INTERIOR_POINTERS" /D "GC_THREADS" /FR /YX /c +CPP_PROJ=/nologo /MD /W3 /EHsc /O2 /I include /D "NDEBUG" /D "WIN32"\ + /D "_WINDOWS" /D "ALL_INTERIOR_POINTERS" /D "ENABLE_DISCLAIM"\ + /D "GC_THREADS" /D "_CRT_SECURE_NO_DEPRECATE"\ + /FR"$(INTDIR)/" /Fp"$(INTDIR)/gc.pch"\ + /I./libatomic_ops/src /Fo"$(INTDIR)/" /c +CPP_OBJS=.\Release/ +CPP_SBRS=.\Release/ + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.c{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +MTL=mktyplib.exe +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /win32 +MTL_PROJ=/nologo /D "NDEBUG" /win32 +RSC=rc.exe +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/gc.bsc" +BSC32_SBRS=\ + ".\Release\allchblk.sbr"\ + ".\Release\alloc.sbr"\ + ".\Release\blacklst.sbr"\ + ".\Release\checksums.sbr"\ + ".\Release\dbg_mlc.sbr"\ + ".\Release\dyn_load.sbr"\ + ".\Release\finalize.sbr"\ + ".\Release\fnlz_mlc.sbr"\ + ".\Release\gc_cpp.sbr"\ + ".\Release\headers.sbr"\ + ".\Release\mach_dep.sbr"\ + ".\Release\malloc.sbr"\ + ".\Release\mallocx.sbr"\ + ".\Release\mark.sbr"\ + ".\Release\mark_rts.sbr"\ + ".\Release\misc.sbr"\ + ".\Release\new_hblk.sbr"\ + ".\Release\obj_map.sbr"\ + ".\Release\os_dep.sbr"\ + ".\Release\ptr_chck.sbr"\ + ".\Release\reclaim.sbr"\ + ".\Release\typd_mlc.sbr"\ + ".\Release\msvc_dbg.copied.sbr"\ + ".\Release\win32_threads.sbr" + +".\Release\gc.bsc" : "$(OUTDIR)" $(BSC32_SBRS) + $(BSC32) @<< + $(BSC32_FLAGS) $(BSC32_SBRS) +<< + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\ + odbccp32.lib /nologo /subsystem:windows /dll /incremental:no\ + /pdb:"$(OUTDIR)/gc.pdb" /machine:I386 /out:"$(OUTDIR)/gc.dll"\ + /implib:"$(OUTDIR)/gc.lib" +LINK32_OBJS=\ + ".\Release\allchblk.obj"\ + ".\Release\alloc.obj"\ + ".\Release\blacklst.obj"\ + ".\Release\checksums.obj"\ + ".\Release\dbg_mlc.obj"\ + ".\Release\dyn_load.obj"\ + ".\Release\finalize.obj"\ + ".\Release\fnlz_mlc.obj"\ + ".\Release\gc_cpp.obj"\ + ".\Release\headers.obj"\ + ".\Release\mach_dep.obj"\ + ".\Release\malloc.obj"\ + ".\Release\mallocx.obj"\ + ".\Release\mark.obj"\ + ".\Release\mark_rts.obj"\ + ".\Release\misc.obj"\ + ".\Release\new_hblk.obj"\ + ".\Release\obj_map.obj"\ + ".\Release\os_dep.obj"\ + ".\Release\ptr_chck.obj"\ + ".\Release\reclaim.obj"\ + ".\Release\typd_mlc.obj"\ + ".\Release\msvc_dbg.copied.obj"\ + ".\Release\win32_threads.obj" + +".\Release\gc.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +OUTDIR=.\Debug +INTDIR=.\Debug + +ALL : ".\Debug\gc.dll" ".\Debug\gc.bsc" + +CLEAN : + -@erase ".\Debug\allchblk.obj" + -@erase ".\Debug\allchblk.sbr" + -@erase ".\Debug\alloc.obj" + -@erase ".\Debug\alloc.sbr" + -@erase ".\Debug\blacklst.obj" + -@erase ".\Debug\blacklst.sbr" + -@erase ".\Debug\checksums.obj" + -@erase ".\Debug\checksums.sbr" + -@erase ".\Debug\dbg_mlc.obj" + -@erase ".\Debug\dbg_mlc.sbr" + -@erase ".\Debug\dyn_load.obj" + -@erase ".\Debug\dyn_load.sbr" + -@erase ".\Debug\finalize.obj" + -@erase ".\Debug\finalize.sbr" + -@erase ".\Debug\fnlz_mlc.obj" + -@erase ".\Debug\fnlz_mlc.sbr" + -@erase ".\Debug\gc_cpp.obj" + -@erase ".\Debug\gc_cpp.sbr" + -@erase ".\Debug\gc.bsc" + -@erase ".\Debug\gc.dll" + -@erase ".\Debug\gc.exp" + -@erase ".\Debug\gc.lib" + -@erase ".\Debug\gc.map" + -@erase ".\Debug\gc.pdb" + -@erase ".\Debug\headers.obj" + -@erase ".\Debug\headers.sbr" + -@erase ".\Debug\mach_dep.obj" + -@erase ".\Debug\mach_dep.sbr" + -@erase ".\Debug\malloc.obj" + -@erase ".\Debug\malloc.sbr" + -@erase ".\Debug\mallocx.obj" + -@erase ".\Debug\mallocx.sbr" + -@erase ".\Debug\mark.obj" + -@erase ".\Debug\mark.sbr" + -@erase ".\Debug\mark_rts.obj" + -@erase ".\Debug\mark_rts.sbr" + -@erase ".\Debug\misc.obj" + -@erase ".\Debug\misc.sbr" + -@erase ".\Debug\new_hblk.obj" + -@erase ".\Debug\new_hblk.sbr" + -@erase ".\Debug\obj_map.obj" + -@erase ".\Debug\obj_map.sbr" + -@erase ".\Debug\os_dep.obj" + -@erase ".\Debug\os_dep.sbr" + -@erase ".\Debug\ptr_chck.obj" + -@erase ".\Debug\ptr_chck.sbr" + -@erase ".\Debug\reclaim.obj" + -@erase ".\Debug\reclaim.sbr" + -@erase ".\Debug\typd_mlc.obj" + -@erase ".\Debug\typd_mlc.sbr" + -@erase ".\Debug\vc40.idb" + -@erase ".\Debug\vc40.pdb" + -@erase ".\Debug\win32_threads.obj" + -@erase ".\Debug\win32_threads.sbr" + -@erase ".\Debug\msvc_dbg.copied.obj" + -@erase ".\Debug\msvc_dbg.copied.sbr" + -@erase ".\msvc_dbg.copied.c" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I include /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "ALL_INTERIOR_POINTERS" /D "GC_THREADS" /FR /YX /c +CPP_PROJ=/nologo /MDd /W3 /Gm /EHsc /Zi /Od /I include /D "_DEBUG"\ + /D "WIN32" /D "_WINDOWS" /D "ALL_INTERIOR_POINTERS" /D "ENABLE_DISCLAIM"\ + /D "GC_ASSERTIONS" /D "GC_THREADS" /D "_CRT_SECURE_NO_DEPRECATE"\ + /FR"$(INTDIR)/" /Fp"$(INTDIR)/gc.pch" /Fo"$(INTDIR)/"\ + /I./libatomic_ops/src /Fd"$(INTDIR)/" /c +CPP_OBJS=.\Debug/ +CPP_SBRS=.\Debug/ + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.c{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +MTL=mktyplib.exe +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /win32 +MTL_PROJ=/nologo /D "_DEBUG" /win32 +RSC=rc.exe +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/gc.bsc" +BSC32_SBRS=\ + ".\Debug\allchblk.sbr"\ + ".\Debug\alloc.sbr"\ + ".\Debug\blacklst.sbr"\ + ".\Debug\checksums.sbr"\ + ".\Debug\dbg_mlc.sbr"\ + ".\Debug\dyn_load.sbr"\ + ".\Debug\finalize.sbr"\ + ".\Debug\fnlz_mlc.sbr"\ + ".\Debug\gc_cpp.sbr"\ + ".\Debug\headers.sbr"\ + ".\Debug\mach_dep.sbr"\ + ".\Debug\malloc.sbr"\ + ".\Debug\mallocx.sbr"\ + ".\Debug\mark.sbr"\ + ".\Debug\mark_rts.sbr"\ + ".\Debug\misc.sbr"\ + ".\Debug\new_hblk.sbr"\ + ".\Debug\obj_map.sbr"\ + ".\Debug\os_dep.sbr"\ + ".\Debug\ptr_chck.sbr"\ + ".\Debug\reclaim.sbr"\ + ".\Debug\typd_mlc.sbr"\ + ".\Debug\msvc_dbg.copied.sbr"\ + ".\Debug\win32_threads.sbr" + +".\Debug\gc.bsc" : "$(OUTDIR)" $(BSC32_SBRS) + $(BSC32) @<< + $(BSC32_FLAGS) $(BSC32_SBRS) +<< + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /incremental:no /map /debug /machine:I386 +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\ + odbccp32.lib /nologo /subsystem:windows /dll /incremental:no\ + /pdb:"$(OUTDIR)/gc.pdb" /map:"$(INTDIR)/gc.map" /debug /machine:I386\ + /out:"$(OUTDIR)/gc.dll" /implib:"$(OUTDIR)/gc.lib" +LINK32_OBJS=\ + ".\Debug\allchblk.obj"\ + ".\Debug\alloc.obj"\ + ".\Debug\blacklst.obj"\ + ".\Debug\checksums.obj"\ + ".\Debug\dbg_mlc.obj"\ + ".\Debug\dyn_load.obj"\ + ".\Debug\finalize.obj"\ + ".\Debug\fnlz_mlc.obj"\ + ".\Debug\gc_cpp.obj"\ + ".\Debug\headers.obj"\ + ".\Debug\mach_dep.obj"\ + ".\Debug\malloc.obj"\ + ".\Debug\mallocx.obj"\ + ".\Debug\mark.obj"\ + ".\Debug\mark_rts.obj"\ + ".\Debug\misc.obj"\ + ".\Debug\new_hblk.obj"\ + ".\Debug\obj_map.obj"\ + ".\Debug\os_dep.obj"\ + ".\Debug\ptr_chck.obj"\ + ".\Debug\reclaim.obj"\ + ".\Debug\typd_mlc.obj"\ + ".\Debug\msvc_dbg.copied.obj"\ + ".\Debug\win32_threads.obj" + +".\Debug\gc.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ELSEIF "$(CFG)" == "gctest - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "gctest\Release" +# PROP BASE Intermediate_Dir "gctest\Release" +# PROP BASE Target_Dir "gctest" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "gctest\Release" +# PROP Intermediate_Dir "gctest\Release" +# PROP Target_Dir "gctest" +OUTDIR=.\gctest\Release +INTDIR=.\gctest\Release + +ALL : "gc - Win32 Release" ".\Release\gctest.exe" + +CLEAN : + -@erase ".\gctest\Release\test.copied.obj" + -@erase ".\test.copied.c" + -@erase ".\Release\gctest.exe" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +test.copied.c : tests\test.c + copy tests\test.c test.copied.c + +CPP=cl.exe +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I include /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "ALL_INTERIOR_POINTERS" /D "GC_THREADS" /YX /c +CPP_PROJ=/nologo /MD /W3 /EHsc /O2 /I include /D "NDEBUG" /D "WIN32" /D "_WINDOWS"\ + /D "ALL_INTERIOR_POINTERS" /D "ENABLE_DISCLAIM" /D "GC_THREADS"\ + /D "_CRT_SECURE_NO_DEPRECATE" /I./libatomic_ops/src /Fp"$(INTDIR)/gctest.pch"\ + /Fo"$(INTDIR)/" /c +CPP_OBJS=.\gctest\Release/ +CPP_SBRS=.\. + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.c{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +MTL=mktyplib.exe +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /win32 +MTL_PROJ=/nologo /D "NDEBUG" /win32 +RSC=rc.exe +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/gctest.bsc" +BSC32_SBRS=\ + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /out:"Release/gctest.exe" +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\ + odbccp32.lib /nologo /subsystem:windows /incremental:no\ + /pdb:"$(OUTDIR)/gctest.pdb" /machine:I386 /out:"Release/gctest.exe" +LINK32_OBJS=\ + ".\gctest\Release\test.copied.obj"\ + ".\Release\gc.lib" + +".\Release\gctest.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ELSEIF "$(CFG)" == "gctest - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "gctest\Debug" +# PROP BASE Intermediate_Dir "gctest\Debug" +# PROP BASE Target_Dir "gctest" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "gctest\Debug" +# PROP Intermediate_Dir "gctest\Debug" +# PROP Target_Dir "gctest" +OUTDIR=.\gctest\Debug +INTDIR=.\gctest\Debug + +ALL : "gc - Win32 Debug" ".\Debug\gctest.exe" ".\gctest\Debug\gctest.bsc" + +CLEAN : + -@erase ".\Debug\gctest.exe" + -@erase ".\gctest\Debug\gctest.bsc" + -@erase ".\gctest\Debug\gctest.map" + -@erase ".\gctest\Debug\gctest.pdb" + -@erase ".\gctest\Debug\test.copied.obj" + -@erase ".\gctest\Debug\test.copied.sbr" + -@erase ".\test.copied.c" + -@erase ".\gctest\Debug\vc40.idb" + -@erase ".\gctest\Debug\vc40.pdb" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "ALL_INTERIOR_POINTERS" /D "GC_THREADS" /FR /YX /c +CPP_PROJ=/nologo /MDd /W3 /Gm /EHsc /Zi /Od /I include /D "_DEBUG" /D "WIN32" /D "_WINDOWS"\ + /D "ALL_INTERIOR_POINTERS" /D "ENABLE_DISCLAIM" /D "GC_THREADS"\ + /D "_CRT_SECURE_NO_DEPRECATE" /FR"$(INTDIR)/"\ + /I./libatomic_ops/src /Fp"$(INTDIR)/gctest.pch" /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c +CPP_OBJS=.\gctest\Debug/ +CPP_SBRS=.\gctest\Debug/ + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.c{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +MTL=mktyplib.exe +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /win32 +MTL_PROJ=/nologo /D "_DEBUG" /win32 +RSC=rc.exe +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/gctest.bsc" +BSC32_SBRS=\ + ".\gctest\Debug\test.copied.sbr" + +".\gctest\Debug\gctest.bsc" : "$(OUTDIR)" $(BSC32_SBRS) + $(BSC32) @<< + $(BSC32_FLAGS) $(BSC32_SBRS) +<< + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /incremental:no /map /debug /machine:I386 /out:"Debug/gctest.exe" +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\ + odbccp32.lib /nologo /subsystem:windows /incremental:no\ + /pdb:"$(OUTDIR)/gctest.pdb" /map:"$(INTDIR)/gctest.map" /debug /machine:I386\ + /out:"Debug/gctest.exe" +LINK32_OBJS=\ + ".\Debug\gc.lib"\ + ".\gctest\Debug\test.copied.obj" + +".\Debug\gctest.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ELSEIF "$(CFG)" == "cord - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "cord\Release" +# PROP BASE Intermediate_Dir "cord\Release" +# PROP BASE Target_Dir "cord" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "cord\Release" +# PROP Intermediate_Dir "cord\Release" +# PROP Target_Dir "cord" +OUTDIR=.\cord\Release +INTDIR=.\cord\Release + +ALL : "gc - Win32 Release" ".\Release\de.exe" + +CLEAN : + -@erase ".\cord\Release\cordbscs.obj" + -@erase ".\cord\Release\cordxtra.obj" + -@erase ".\cord\Release\de.obj" + -@erase ".\cord\Release\de_win.obj" + -@erase ".\cord\Release\de_win.res" + -@erase ".\Release\de.exe" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "." /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "ALL_INTERIOR_POINTERS" /YX /c +CPP_PROJ=/nologo /MD /W3 /EHsc /O2 /I "." /I include /D "NDEBUG" /D "WIN32" /D "_WINDOWS"\ + /D "ALL_INTERIOR_POINTERS" /D "ENABLE_DISCLAIM"\ + /I./libatomic_ops/src /Fp"$(INTDIR)/cord.pch" /Fo"$(INTDIR)/" /c +CPP_OBJS=.\cord\Release/ +CPP_SBRS=.\. + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.c{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +MTL=mktyplib.exe +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /win32 +MTL_PROJ=/nologo /D "NDEBUG" /win32 +RSC=rc.exe +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +RSC_PROJ=/l 0x809 /fo"$(INTDIR)/de_win.res" /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/cord.bsc" +BSC32_SBRS=\ + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /out:"Release/de.exe" +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\ + odbccp32.lib /nologo /subsystem:windows /incremental:no /pdb:"$(OUTDIR)/de.pdb"\ + /machine:I386 /out:"Release/de.exe" +LINK32_OBJS=\ + ".\cord\Release\cordbscs.obj"\ + ".\cord\Release\cordxtra.obj"\ + ".\cord\Release\de.obj"\ + ".\cord\Release\de_win.obj"\ + ".\cord\Release\de_win.res"\ + ".\Release\gc.lib" + +".\Release\de.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ELSEIF "$(CFG)" == "cord - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "cord\Debug" +# PROP BASE Intermediate_Dir "cord\Debug" +# PROP BASE Target_Dir "cord" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "cord\Debug" +# PROP Intermediate_Dir "cord\Debug" +# PROP Target_Dir "cord" +OUTDIR=.\cord\Debug +INTDIR=.\cord\Debug + +ALL : "gc - Win32 Debug" ".\Debug\de.exe" + +CLEAN : + -@erase ".\cord\Debug\cordbscs.obj" + -@erase ".\cord\Debug\cordxtra.obj" + -@erase ".\cord\Debug\de.obj" + -@erase ".\cord\Debug\de.pdb" + -@erase ".\cord\Debug\de_win.obj" + -@erase ".\cord\Debug\de_win.res" + -@erase ".\cord\Debug\vc40.idb" + -@erase ".\cord\Debug\vc40.pdb" + -@erase ".\Debug\de.exe" + -@erase ".\Debug\de.ilk" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "." /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "ALL_INTERIOR_POINTERS" /YX /c +CPP_PROJ=/nologo /MDd /W3 /Gm /EHsc /Zi /Od /I "." /I include /D "_DEBUG" /D "WIN32" /D "_WINDOWS"\ + /D "ALL_INTERIOR_POINTERS" /D "ENABLE_DISCLAIM" /Fp"$(INTDIR)/cord.pch"\ + /I./libatomic_ops/src /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c +CPP_OBJS=.\cord\Debug/ +CPP_SBRS=.\. + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.c{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +MTL=mktyplib.exe +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /win32 +MTL_PROJ=/nologo /D "_DEBUG" /win32 +RSC=rc.exe +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +RSC_PROJ=/l 0x809 /fo"$(INTDIR)/de_win.res" /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/cord.bsc" +BSC32_SBRS=\ + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /out:"Debug/de.exe" +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\ + odbccp32.lib /nologo /subsystem:windows /incremental:yes\ + /pdb:"$(OUTDIR)/de.pdb" /debug /machine:I386 /out:"Debug/de.exe" +LINK32_OBJS=\ + ".\cord\Debug\cordbscs.obj"\ + ".\cord\Debug\cordxtra.obj"\ + ".\cord\Debug\de.obj"\ + ".\cord\Debug\de_win.obj"\ + ".\cord\Debug\de_win.res"\ + ".\Debug\gc.lib" + +".\Debug\de.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ENDIF + +################################################################################ +# Begin Target + +# Name "gc - Win32 Release" +# Name "gc - Win32 Debug" + +!IF "$(CFG)" == "gc - Win32 Release" + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +!ENDIF + +################################################################################ +# Begin Source File + +SOURCE=.\gc_cpp.cpp + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_RECLA=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + ".\include\gc_cpp.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_RECLA=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\gc_cpp.obj" : $(SOURCE) $(DEP_CPP_RECLA) "$(INTDIR)" + +".\Release\gc_cpp.sbr" : $(SOURCE) $(DEP_CPP_RECLA) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_RECLA=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + ".\include\gc_cpp.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_RECLA=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\gc_cpp.obj" : $(SOURCE) $(DEP_CPP_RECLA) "$(INTDIR)" + +".\Debug\gc_cpp.sbr" : $(SOURCE) $(DEP_CPP_RECLA) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\reclaim.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_RECLA=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_RECLA=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\reclaim.obj" : $(SOURCE) $(DEP_CPP_RECLA) "$(INTDIR)" + +".\Release\reclaim.sbr" : $(SOURCE) $(DEP_CPP_RECLA) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_RECLA=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_RECLA=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\reclaim.obj" : $(SOURCE) $(DEP_CPP_RECLA) "$(INTDIR)" + +".\Debug\reclaim.sbr" : $(SOURCE) $(DEP_CPP_RECLA) "$(INTDIR)" + + +!ENDIF + +# End Source File + +################################################################################ +# Begin Source File + +SOURCE=.\os_dep.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_OS_DE=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\STAT.H"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_OS_DE=\ + ".\il\PCR_IL.h"\ + ".\mm\PCR_MM.h"\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + ".\vd\PCR_VD.h"\ + + +".\Release\os_dep.obj" : $(SOURCE) $(DEP_CPP_OS_DE) "$(INTDIR)" + +".\Release\os_dep.sbr" : $(SOURCE) $(DEP_CPP_OS_DE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_OS_DE=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\STAT.H"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_OS_DE=\ + ".\il\PCR_IL.h"\ + ".\mm\PCR_MM.h"\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + ".\vd\PCR_VD.h"\ + + +".\Debug\os_dep.obj" : $(SOURCE) $(DEP_CPP_OS_DE) "$(INTDIR)" + +".\Debug\os_dep.sbr" : $(SOURCE) $(DEP_CPP_OS_DE) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\misc.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_MISC_=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_MISC_=\ + ".\il\PCR_IL.h"\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\misc.obj" : $(SOURCE) $(DEP_CPP_MISC_) "$(INTDIR)" + +".\Release\misc.sbr" : $(SOURCE) $(DEP_CPP_MISC_) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_MISC_=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_MISC_=\ + ".\il\PCR_IL.h"\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\misc.obj" : $(SOURCE) $(DEP_CPP_MISC_) "$(INTDIR)" + +".\Debug\misc.sbr" : $(SOURCE) $(DEP_CPP_MISC_) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\mark_rts.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_MARK_=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_MARK_=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\mark_rts.obj" : $(SOURCE) $(DEP_CPP_MARK_) "$(INTDIR)" + +".\Release\mark_rts.sbr" : $(SOURCE) $(DEP_CPP_MARK_) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_MARK_=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_MARK_=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\mark_rts.obj" : $(SOURCE) $(DEP_CPP_MARK_) "$(INTDIR)" + +".\Debug\mark_rts.sbr" : $(SOURCE) $(DEP_CPP_MARK_) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\mach_dep.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_MACH_=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_MACH_=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\mach_dep.obj" : $(SOURCE) $(DEP_CPP_MACH_) "$(INTDIR)" + +".\Release\mach_dep.sbr" : $(SOURCE) $(DEP_CPP_MACH_) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_MACH_=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_MACH_=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\mach_dep.obj" : $(SOURCE) $(DEP_CPP_MACH_) "$(INTDIR)" + +".\Debug\mach_dep.sbr" : $(SOURCE) $(DEP_CPP_MACH_) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\headers.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_HEADE=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_HEADE=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\headers.obj" : $(SOURCE) $(DEP_CPP_HEADE) "$(INTDIR)" + +".\Release\headers.sbr" : $(SOURCE) $(DEP_CPP_HEADE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_HEADE=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_HEADE=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\headers.obj" : $(SOURCE) $(DEP_CPP_HEADE) "$(INTDIR)" + +".\Debug\headers.sbr" : $(SOURCE) $(DEP_CPP_HEADE) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\alloc.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_ALLOC=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_ALLOC=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\alloc.obj" : $(SOURCE) $(DEP_CPP_ALLOC) "$(INTDIR)" + +".\Release\alloc.sbr" : $(SOURCE) $(DEP_CPP_ALLOC) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_ALLOC=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_ALLOC=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\alloc.obj" : $(SOURCE) $(DEP_CPP_ALLOC) "$(INTDIR)" + +".\Debug\alloc.sbr" : $(SOURCE) $(DEP_CPP_ALLOC) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\allchblk.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_ALLCH=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_ALLCH=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\allchblk.obj" : $(SOURCE) $(DEP_CPP_ALLCH) "$(INTDIR)" + +".\Release\allchblk.sbr" : $(SOURCE) $(DEP_CPP_ALLCH) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_ALLCH=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_ALLCH=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\allchblk.obj" : $(SOURCE) $(DEP_CPP_ALLCH) "$(INTDIR)" + +".\Debug\allchblk.sbr" : $(SOURCE) $(DEP_CPP_ALLCH) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\obj_map.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_OBJ_M=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_OBJ_M=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\obj_map.obj" : $(SOURCE) $(DEP_CPP_OBJ_M) "$(INTDIR)" + +".\Release\obj_map.sbr" : $(SOURCE) $(DEP_CPP_OBJ_M) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_OBJ_M=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_OBJ_M=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\obj_map.obj" : $(SOURCE) $(DEP_CPP_OBJ_M) "$(INTDIR)" + +".\Debug\obj_map.sbr" : $(SOURCE) $(DEP_CPP_OBJ_M) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\new_hblk.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_NEW_H=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_NEW_H=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\new_hblk.obj" : $(SOURCE) $(DEP_CPP_NEW_H) "$(INTDIR)" + +".\Release\new_hblk.sbr" : $(SOURCE) $(DEP_CPP_NEW_H) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_NEW_H=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_NEW_H=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\new_hblk.obj" : $(SOURCE) $(DEP_CPP_NEW_H) "$(INTDIR)" + +".\Debug\new_hblk.sbr" : $(SOURCE) $(DEP_CPP_NEW_H) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\mark.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_MARK_C=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_pmark.h"\ + ".\include\gc_mark.h"\ + ".\include\gc_disclaim.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_MARK_C=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\mark.obj" : $(SOURCE) $(DEP_CPP_MARK_C) "$(INTDIR)" + +".\Release\mark.sbr" : $(SOURCE) $(DEP_CPP_MARK_C) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_MARK_C=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_pmark.h"\ + ".\include\gc_mark.h"\ + ".\include\gc_disclaim.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_MARK_C=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\mark.obj" : $(SOURCE) $(DEP_CPP_MARK_C) "$(INTDIR)" + +".\Debug\mark.sbr" : $(SOURCE) $(DEP_CPP_MARK_C) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\malloc.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_MALLO=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_MALLO=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\malloc.obj" : $(SOURCE) $(DEP_CPP_MALLO) "$(INTDIR)" + +".\Release\malloc.sbr" : $(SOURCE) $(DEP_CPP_MALLO) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_MALLO=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_MALLO=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\malloc.obj" : $(SOURCE) $(DEP_CPP_MALLO) "$(INTDIR)" + +".\Debug\malloc.sbr" : $(SOURCE) $(DEP_CPP_MALLO) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\mallocx.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_MALLX=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_MALLX=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\mallocx.obj" : $(SOURCE) $(DEP_CPP_MALLX) "$(INTDIR)" + +".\Release\mallocx.sbr" : $(SOURCE) $(DEP_CPP_MALLX) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_MALLX=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_MALLX=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\mallocx.obj" : $(SOURCE) $(DEP_CPP_MALLX) "$(INTDIR)" + +".\Debug\mallocx.sbr" : $(SOURCE) $(DEP_CPP_MALLX) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\finalize.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_FINAL=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_pmark.h"\ + ".\include\gc_mark.h"\ + ".\include\gc_disclaim.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_FINAL=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\finalize.obj" : $(SOURCE) $(DEP_CPP_FINAL) "$(INTDIR)" + +".\Release\finalize.sbr" : $(SOURCE) $(DEP_CPP_FINAL) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_FINAL=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_pmark.h"\ + ".\include\gc_mark.h"\ + ".\include\gc_disclaim.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_FINAL=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\finalize.obj" : $(SOURCE) $(DEP_CPP_FINAL) "$(INTDIR)" + +".\Debug\finalize.sbr" : $(SOURCE) $(DEP_CPP_FINAL) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\dbg_mlc.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_DBG_M=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_DBG_M=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\dbg_mlc.obj" : $(SOURCE) $(DEP_CPP_DBG_M) "$(INTDIR)" + +".\Release\dbg_mlc.sbr" : $(SOURCE) $(DEP_CPP_DBG_M) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_DBG_M=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_DBG_M=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\dbg_mlc.obj" : $(SOURCE) $(DEP_CPP_DBG_M) "$(INTDIR)" + +".\Debug\dbg_mlc.sbr" : $(SOURCE) $(DEP_CPP_DBG_M) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\fnlz_mlc.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_DBG_M=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_DBG_M=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\fnlz_mlc.obj" : $(SOURCE) $(DEP_CPP_DBG_M) "$(INTDIR)" + +".\Release\fnlz_mlc.sbr" : $(SOURCE) $(DEP_CPP_DBG_M) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_DBG_M=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_DBG_M=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\fnlz_mlc.obj" : $(SOURCE) $(DEP_CPP_DBG_M) "$(INTDIR)" + +".\Debug\fnlz_mlc.sbr" : $(SOURCE) $(DEP_CPP_DBG_M) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\blacklst.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_BLACK=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_BLACK=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\blacklst.obj" : $(SOURCE) $(DEP_CPP_BLACK) "$(INTDIR)" + +".\Release\blacklst.sbr" : $(SOURCE) $(DEP_CPP_BLACK) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_BLACK=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_BLACK=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\blacklst.obj" : $(SOURCE) $(DEP_CPP_BLACK) "$(INTDIR)" + +".\Debug\blacklst.sbr" : $(SOURCE) $(DEP_CPP_BLACK) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\typd_mlc.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_TYPD_=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_pmark.h"\ + ".\include\gc_mark.h"\ + ".\include\gc_disclaim.h"\ + ".\include\private\gc_priv.h"\ + ".\include\gc_typed.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_TYPD_=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\typd_mlc.obj" : $(SOURCE) $(DEP_CPP_TYPD_) "$(INTDIR)" + +".\Release\typd_mlc.sbr" : $(SOURCE) $(DEP_CPP_TYPD_) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_TYPD_=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_pmark.h"\ + ".\include\gc_mark.h"\ + ".\include\gc_disclaim.h"\ + ".\include\private\gc_priv.h"\ + ".\include\gc_typed.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_TYPD_=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\typd_mlc.obj" : $(SOURCE) $(DEP_CPP_TYPD_) "$(INTDIR)" + +".\Debug\typd_mlc.sbr" : $(SOURCE) $(DEP_CPP_TYPD_) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\ptr_chck.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_PTR_C=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_pmark.h"\ + ".\include\gc_mark.h"\ + ".\include\gc_disclaim.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_PTR_C=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\ptr_chck.obj" : $(SOURCE) $(DEP_CPP_PTR_C) "$(INTDIR)" + +".\Release\ptr_chck.sbr" : $(SOURCE) $(DEP_CPP_PTR_C) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_PTR_C=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_pmark.h"\ + ".\include\gc_mark.h"\ + ".\include\gc_disclaim.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_PTR_C=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\ptr_chck.obj" : $(SOURCE) $(DEP_CPP_PTR_C) "$(INTDIR)" + +".\Debug\ptr_chck.sbr" : $(SOURCE) $(DEP_CPP_PTR_C) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\dyn_load.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_DYN_L=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\STAT.H"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_DYN_L=\ + ".\il\PCR_IL.h"\ + ".\mm\PCR_MM.h"\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\dyn_load.obj" : $(SOURCE) $(DEP_CPP_DYN_L) "$(INTDIR)" + +".\Release\dyn_load.sbr" : $(SOURCE) $(DEP_CPP_DYN_L) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_DYN_L=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\STAT.H"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_DYN_L=\ + ".\il\PCR_IL.h"\ + ".\mm\PCR_MM.h"\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\dyn_load.obj" : $(SOURCE) $(DEP_CPP_DYN_L) "$(INTDIR)" + +".\Debug\dyn_load.sbr" : $(SOURCE) $(DEP_CPP_DYN_L) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\win32_threads.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_WIN32=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_WIN32=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\win32_threads.obj" : $(SOURCE) $(DEP_CPP_WIN32) "$(INTDIR)" + +".\Release\win32_threads.sbr" : $(SOURCE) $(DEP_CPP_WIN32) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_WIN32=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_WIN32=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\win32_threads.obj" : $(SOURCE) $(DEP_CPP_WIN32) "$(INTDIR)" + +".\Debug\win32_threads.sbr" : $(SOURCE) $(DEP_CPP_WIN32) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\extra\msvc_dbg.c + +msvc_dbg.copied.c : extra\msvc_dbg.c + copy extra\msvc_dbg.c msvc_dbg.copied.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_WIN32=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + ".\include\private\msvc_dbg.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_WIN32=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\msvc_dbg.copied.obj" : $(SOURCE) $(DEP_CPP_WIN32) "$(INTDIR)" + +".\Release\msvc_dbg.copied.sbr" : $(SOURCE) $(DEP_CPP_WIN32) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_WIN32=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + ".\include\private\msvc_dbg.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_WIN32=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\msvc_dbg.copied.obj" : $(SOURCE) $(DEP_CPP_WIN32) "$(INTDIR)" + +".\Debug\msvc_dbg.copied.sbr" : $(SOURCE) $(DEP_CPP_WIN32) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\checksums.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_CHECK=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_CHECK=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\checksums.obj" : $(SOURCE) $(DEP_CPP_CHECK) "$(INTDIR)" + +".\Release\checksums.sbr" : $(SOURCE) $(DEP_CPP_CHECK) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_CHECK=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_CHECK=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\checksums.obj" : $(SOURCE) $(DEP_CPP_CHECK) "$(INTDIR)" + +".\Debug\checksums.sbr" : $(SOURCE) $(DEP_CPP_CHECK) "$(INTDIR)" + + +!ENDIF + +# End Source File +# End Target +################################################################################ +# Begin Target + +# Name "gctest - Win32 Release" +# Name "gctest - Win32 Debug" + +!IF "$(CFG)" == "gctest - Win32 Release" + +!ELSEIF "$(CFG)" == "gctest - Win32 Debug" + +!ENDIF + +################################################################################ +# Begin Project Dependency + +# Project_Dep_Name "gc" + +!IF "$(CFG)" == "gctest - Win32 Release" + +"gc - Win32 Release" : + $(MAKE) /$(MAKEFLAGS) /F ".\gc.mak" CFG="gc - Win32 Release" + +!ELSEIF "$(CFG)" == "gctest - Win32 Debug" + +"gc - Win32 Debug" : + $(MAKE) /$(MAKEFLAGS) /F ".\gc.mak" CFG="gc - Win32 Debug" + +!ENDIF + +# End Project Dependency +################################################################################ +# Begin Source File + +SOURCE=.\tests\test.c +DEP_CPP_TEST_=\ + ".\include\private\gcconfig.h"\ + ".\include\gc.h"\ + ".\include\private\gc_hdrs.h"\ + ".\include\private\gc_priv.h"\ + ".\include\gc_typed.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_TEST_=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +!IF "$(CFG)" == "gctest - Win32 Release" + + +".\gctest\Release\test.copied.obj" : $(SOURCE) $(DEP_CPP_TEST_) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gctest - Win32 Debug" + + +".\gctest\Debug\test.copied.obj" : $(SOURCE) $(DEP_CPP_TEST_) "$(INTDIR)" + +".\gctest\Debug\test.copied.sbr" : $(SOURCE) $(DEP_CPP_TEST_) "$(INTDIR)" + + +!ENDIF + +# End Source File +# End Target +################################################################################ +# Begin Target + +# Name "cord - Win32 Release" +# Name "cord - Win32 Debug" + +!IF "$(CFG)" == "cord - Win32 Release" + +!ELSEIF "$(CFG)" == "cord - Win32 Debug" + +!ENDIF + +################################################################################ +# Begin Project Dependency + +# Project_Dep_Name "gc" + +!IF "$(CFG)" == "cord - Win32 Release" + +"gc - Win32 Release" : + $(MAKE) /$(MAKEFLAGS) /F ".\gc.mak" CFG="gc - Win32 Release" + +!ELSEIF "$(CFG)" == "cord - Win32 Debug" + +"gc - Win32 Debug" : + $(MAKE) /$(MAKEFLAGS) /F ".\gc.mak" CFG="gc - Win32 Debug" + +!ENDIF + +# End Project Dependency +################################################################################ +# Begin Source File + +SOURCE=.\cord\tests\de_win.c +DEP_CPP_DE_WI=\ + ".\include\cord.h"\ + ".\cord\tests\de_cmds.h"\ + ".\cord\tests\de_win.h"\ + ".\include\cord_pos.h"\ + +NODEP_CPP_DE_WI=\ + ".\include\gc.h"\ + + +!IF "$(CFG)" == "cord - Win32 Release" + + +".\cord\Release\de_win.obj" : $(SOURCE) $(DEP_CPP_DE_WI) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "cord - Win32 Debug" + + +".\cord\Debug\de_win.obj" : $(SOURCE) $(DEP_CPP_DE_WI) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\cord\tests\de.c +DEP_CPP_DE_C2e=\ + ".\include\cord.h"\ + ".\cord\tests\de_cmds.h"\ + ".\cord\tests\de_win.h"\ + ".\include\cord_pos.h"\ + +NODEP_CPP_DE_C2e=\ + ".\include\gc.h"\ + + +!IF "$(CFG)" == "cord - Win32 Release" + + +".\cord\Release\de.obj" : $(SOURCE) $(DEP_CPP_DE_C2e) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "cord - Win32 Debug" + + +".\cord\Debug\de.obj" : $(SOURCE) $(DEP_CPP_DE_C2e) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\cord\cordxtra.c +DEP_CPP_CORDX=\ + ".\include\cord.h"\ + ".\include\ec.h"\ + ".\include\cord_pos.h"\ + +NODEP_CPP_CORDX=\ + ".\include\gc.h"\ + + +!IF "$(CFG)" == "cord - Win32 Release" + + +".\cord\Release\cordxtra.obj" : $(SOURCE) $(DEP_CPP_CORDX) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "cord - Win32 Debug" + + +".\cord\Debug\cordxtra.obj" : $(SOURCE) $(DEP_CPP_CORDX) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\cord\cordbscs.c +DEP_CPP_CORDB=\ + ".\include\cord.h"\ + ".\include\cord_pos.h"\ + +NODEP_CPP_CORDB=\ + ".\include\gc.h"\ + + +!IF "$(CFG)" == "cord - Win32 Release" + + +".\cord\Release\cordbscs.obj" : $(SOURCE) $(DEP_CPP_CORDB) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "cord - Win32 Debug" + + +".\cord\Debug\cordbscs.obj" : $(SOURCE) $(DEP_CPP_CORDB) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\cord\tests\de_win.rc + +!IF "$(CFG)" == "cord - Win32 Release" + + +".\cord\Release\de_win.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x809 /fo"$(INTDIR)/de_win.res" /i "cord" /d "NDEBUG" $(SOURCE) + + +!ELSEIF "$(CFG)" == "cord - Win32 Debug" + + +".\cord\Debug\de_win.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x809 /fo"$(INTDIR)/de_win.res" /i "cord" /d "_DEBUG" $(SOURCE) + + +!ENDIF + +# End Source File +# End Target +# End Project +################################################################################ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/gc_cpp.cc b/unity-2019.4.24f1-mbe/external/bdwgc/gc_cpp.cc new file mode 100644 index 000000000..80a9708ac --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/gc_cpp.cc @@ -0,0 +1,79 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to copy this code for any purpose, + * provided the above notices are retained on all copies. + */ + +/************************************************************************* +This implementation module for gc_c++.h provides an implementation of +the global operators "new" and "delete" that calls the Boehm +allocator. All objects allocated by this implementation will be +uncollectible but part of the root set of the collector. + +You should ensure (using implementation-dependent techniques) that the +linker finds this module before the library that defines the default +built-in "new" and "delete". +**************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifndef GC_BUILD +# define GC_BUILD +#endif + +#include "gc_cpp.h" + +#if GC_GNUC_PREREQ(4, 2) && !defined(GC_NEW_DELETE_NEED_THROW) +# define GC_NEW_DELETE_NEED_THROW +#endif + +#ifdef GC_NEW_DELETE_NEED_THROW +# include /* for std::bad_alloc */ +# define GC_DECL_NEW_THROW throw(std::bad_alloc) +# define GC_DECL_DELETE_THROW throw() +#else +# define GC_DECL_NEW_THROW /* empty */ +# define GC_DECL_DELETE_THROW /* empty */ +#endif // !GC_NEW_DELETE_NEED_THROW + +#ifndef _MSC_VER + + void* operator new(size_t size) GC_DECL_NEW_THROW { + return GC_MALLOC_UNCOLLECTABLE(size); + } + + void operator delete(void* obj) GC_DECL_DELETE_THROW { + GC_FREE(obj); + } + +# if defined(GC_OPERATOR_NEW_ARRAY) && !defined(CPPCHECK) + void* operator new[](size_t size) GC_DECL_NEW_THROW { + return GC_MALLOC_UNCOLLECTABLE(size); + } + + void operator delete[](void* obj) GC_DECL_DELETE_THROW { + GC_FREE(obj); + } +# endif // GC_OPERATOR_NEW_ARRAY + +# if __cplusplus > 201103L // C++14 + void operator delete(void* obj, size_t size) GC_DECL_DELETE_THROW { + (void)size; // size is ignored + GC_FREE(obj); + } + +# if defined(GC_OPERATOR_NEW_ARRAY) && !defined(CPPCHECK) + void operator delete[](void* obj, size_t size) GC_DECL_DELETE_THROW { + (void)size; + GC_FREE(obj); + } +# endif +# endif // C++14 + +#endif // !_MSC_VER diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/gc_cpp.cpp b/unity-2019.4.24f1-mbe/external/bdwgc/gc_cpp.cpp new file mode 100644 index 000000000..f6bd95e59 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/gc_cpp.cpp @@ -0,0 +1,2 @@ +// Visual C++ seems to prefer a .cpp extension to .cc +#include "gc_cpp.cc" diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/gc_dlopen.c b/unity-2019.4.24f1-mbe/external/bdwgc/gc_dlopen.c new file mode 100644 index 000000000..0da822786 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/gc_dlopen.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1997 by Silicon Graphics. All rights reserved. + * Copyright (c) 2000 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#include "private/gc_priv.h" + +/* This used to be in dyn_load.c. It was extracted into a separate */ +/* file to avoid having to link against libdl.{a,so} if the client */ +/* doesn't call dlopen. Of course this fails if the collector is in */ +/* a dynamic library. -HB */ +#if defined(GC_PTHREADS) && !defined(GC_NO_DLOPEN) + +#undef GC_MUST_RESTORE_REDEFINED_DLOPEN +#if defined(dlopen) && !defined(GC_USE_LD_WRAP) + /* To support various threads pkgs, gc.h interposes on dlopen by */ + /* defining "dlopen" to be "GC_dlopen", which is implemented below. */ + /* However, both GC_FirstDLOpenedLinkMap() and GC_dlopen() use the */ + /* real system dlopen() in their implementation. We first remove */ + /* gc.h's dlopen definition and restore it later, after GC_dlopen(). */ +# undef dlopen +# define GC_MUST_RESTORE_REDEFINED_DLOPEN +#endif + +/* Make sure we're not in the middle of a collection, and make sure we */ +/* don't start any. This is invoked prior to a dlopen call to avoid */ +/* synchronization issues. We can't just acquire the allocation lock, */ +/* since startup code in dlopen may try to allocate. This solution */ +/* risks heap growth (or, even, heap overflow) in the presence of many */ +/* dlopen calls in either a multi-threaded environment, or if the */ +/* library initialization code allocates substantial amounts of GC'ed */ +/* memory. */ +#ifndef USE_PROC_FOR_LIBRARIES + static void disable_gc_for_dlopen(void) + { + DCL_LOCK_STATE; + LOCK(); + while (GC_incremental && GC_collection_in_progress()) { + GC_collect_a_little_inner(1000); + } + ++GC_dont_gc; + UNLOCK(); + } +#endif + +/* Redefine dlopen to guarantee mutual exclusion with */ +/* GC_register_dynamic_libraries. Should probably happen for */ +/* other operating systems, too. */ + +/* This is similar to WRAP/REAL_FUNC() in pthread_support.c. */ +#ifdef GC_USE_LD_WRAP +# define WRAP_DLFUNC(f) __wrap_##f +# define REAL_DLFUNC(f) __real_##f + void * REAL_DLFUNC(dlopen)(const char *, int); +#else +# define WRAP_DLFUNC(f) GC_##f +# define REAL_DLFUNC(f) f +#endif + +GC_API void * WRAP_DLFUNC(dlopen)(const char *path, int mode) +{ + void * result; + +# ifndef USE_PROC_FOR_LIBRARIES + /* Disable collections. This solution risks heap growth (or, */ + /* even, heap overflow) but there seems no better solutions. */ + disable_gc_for_dlopen(); +# endif + result = REAL_DLFUNC(dlopen)(path, mode); +# ifndef USE_PROC_FOR_LIBRARIES + GC_enable(); /* undoes disable_gc_for_dlopen */ +# endif + return(result); +} + +#ifdef GC_USE_LD_WRAP + /* Define GC_ function as an alias for the plain one, which will be */ + /* intercepted. This allows files which include gc.h, and hence */ + /* generate references to the GC_ symbol, to see the right symbol. */ + GC_API void *GC_dlopen(const char *path, int mode) + { + return dlopen(path, mode); + } +#endif /* GC_USE_LD_WRAP */ + +#ifdef GC_MUST_RESTORE_REDEFINED_DLOPEN +# define dlopen GC_dlopen +#endif + +#endif /* GC_PTHREADS && !GC_NO_DLOPEN */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/gcj_mlc.c b/unity-2019.4.24f1-mbe/external/bdwgc/gcj_mlc.c new file mode 100644 index 000000000..0090d1b43 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/gcj_mlc.c @@ -0,0 +1,365 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1999-2004 Hewlett-Packard Development Company, L.P. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + */ + +#include "private/gc_pmark.h" /* includes gc_priv.h */ + +#ifdef GC_GCJ_SUPPORT + +/* + * This is an allocator interface tuned for gcj (the GNU static + * java compiler). + * + * Each allocated object has a pointer in its first word to a vtable, + * which for our purposes is simply a structure describing the type of + * the object. + * This descriptor structure contains a GC marking descriptor at offset + * MARK_DESCR_OFFSET. + * + * It is hoped that this interface may also be useful for other systems, + * possibly with some tuning of the constants. But the immediate goal + * is to get better gcj performance. + * + * We assume: + * 1) Counting on explicit initialization of this interface is OK; + * 2) FASTLOCK is not a significant win. + */ + +#include "gc_gcj.h" +#include "private/dbg_mlc.h" + +#ifdef GC_ASSERTIONS + GC_INNER /* variable is also used in thread_local_alloc.c */ +#else + STATIC +#endif +GC_bool GC_gcj_malloc_initialized = FALSE; + +#ifdef GC_ASSERTIONS + GC_INNER /* variable is also used in thread_local_alloc.c */ +#else + STATIC +#endif +GC_bool GC_gcj_vector_initialized = FALSE; + +int GC_gcj_kind = 0; /* Object kind for objects with descriptors */ + /* in "vtable". */ +int GC_gcj_debug_kind = 0; + /* The kind of objects that is always marked */ + /* with a mark proc call. */ + +int GC_gcj_vector_kind = 0; /* Object kind for objects with descriptors */ + /* in "vtable". */ + +GC_INNER ptr_t * GC_gcjobjfreelist = NULL; +GC_INNER ptr_t * GC_gcjvecfreelist = NULL; + +STATIC struct GC_ms_entry * GC_gcj_fake_mark_proc(word * addr GC_ATTR_UNUSED, + struct GC_ms_entry *mark_stack_ptr, + struct GC_ms_entry * mark_stack_limit GC_ATTR_UNUSED, + word env GC_ATTR_UNUSED) +{ + ABORT_RET("No client gcj mark proc is specified"); + return mark_stack_ptr; +} + +/* Caller does not hold allocation lock. */ +GC_API void GC_CALL GC_init_gcj_malloc(int mp_index, + void * /* really GC_mark_proc */mp) +{ + GC_bool ignore_gcj_info; + DCL_LOCK_STATE; + + if (mp == 0) /* In case GC_DS_PROC is unused. */ + mp = (void *)(word)GC_gcj_fake_mark_proc; + + GC_init(); /* In case it's not already done. */ + LOCK(); + if (GC_gcj_malloc_initialized) { + UNLOCK(); + return; + } + GC_gcj_malloc_initialized = TRUE; +# ifdef GC_IGNORE_GCJ_INFO + /* This is useful for debugging on platforms with missing getenv(). */ + ignore_gcj_info = 1; +# else + ignore_gcj_info = (0 != GETENV("GC_IGNORE_GCJ_INFO")); +# endif + if (ignore_gcj_info) { + GC_COND_LOG_PRINTF("Gcj-style type information is disabled!\n"); + } + GC_ASSERT(GC_mark_procs[mp_index] == (GC_mark_proc)0); /* unused */ + GC_mark_procs[mp_index] = (GC_mark_proc)(word)mp; + if ((unsigned)mp_index >= GC_n_mark_procs) + ABORT("GC_init_gcj_malloc: bad index"); + /* Set up object kind gcj-style indirect descriptor. */ + GC_gcjobjfreelist = (ptr_t *)GC_new_free_list_inner(); + if (ignore_gcj_info) { + /* Use a simple length-based descriptor, thus forcing a fully */ + /* conservative scan. */ + GC_gcj_kind = GC_new_kind_inner((void **)GC_gcjobjfreelist, + /* 0 | */ GC_DS_LENGTH, + TRUE, TRUE); + } else { + GC_gcj_kind = GC_new_kind_inner( + (void **)GC_gcjobjfreelist, + (((word)(-(signed_word)MARK_DESCR_OFFSET + - GC_INDIR_PER_OBJ_BIAS)) + | GC_DS_PER_OBJECT), + FALSE, TRUE); + } + /* Set up object kind for objects that require mark proc call. */ + if (ignore_gcj_info) { + GC_gcj_debug_kind = GC_gcj_kind; + } else { + GC_gcj_debug_kind = GC_new_kind_inner(GC_new_free_list_inner(), + GC_MAKE_PROC(mp_index, + 1 /* allocated with debug info */), + FALSE, TRUE); + } + UNLOCK(); +} + +/* Caller does not hold allocation lock. */ +GC_API void GC_CALL GC_init_gcj_vector (int mp_index, + void * /* really GC_mark_proc */mp) +{ + DCL_LOCK_STATE; + + if (mp == 0) /* In case GC_DS_PROC is unused. */ + ABORT ("GC_init_gcj_vector: bad index"); + + GC_init (); /* In case it's not already done. */ + LOCK (); + if (GC_gcj_vector_initialized) { + UNLOCK (); + return; + } + GC_gcj_vector_initialized = TRUE; + + GC_ASSERT (GC_mark_procs[mp_index] == (GC_mark_proc)0); /* unused */ + GC_mark_procs[mp_index ] = (GC_mark_proc)(word)mp; + if ((unsigned)mp_index >= GC_n_mark_procs) + ABORT ("GC_init_gcj_vector: bad index"); + GC_gcjvecfreelist = (ptr_t *)GC_new_free_list_inner (); + GC_gcj_vector_kind = GC_new_kind_inner ((void **)GC_gcjvecfreelist, + GC_MAKE_PROC (mp_index, + 0), + FALSE, TRUE); + + UNLOCK (); +} + +#define GENERAL_MALLOC_INNER(lb,k) \ + GC_clear_stack(GC_generic_malloc_inner(lb, k)) + +#define GENERAL_MALLOC_INNER_IOP(lb,k) \ + GC_clear_stack(GC_generic_malloc_inner_ignore_off_page(lb, k)) + +/* We need a mechanism to release the lock and invoke finalizers. */ +/* We don't really have an opportunity to do this on a rarely executed */ +/* path on which the lock is not held. Thus we check at a */ +/* rarely executed point at which it is safe to release the lock. */ +/* We do this even where we could just call GC_INVOKE_FINALIZERS, */ +/* since it's probably cheaper and certainly more uniform. */ +/* FIXME - Consider doing the same elsewhere? */ +static void maybe_finalize(void) +{ + static word last_finalized_no = 0; + DCL_LOCK_STATE; + + if (GC_gc_no == last_finalized_no || + !EXPECT(GC_is_initialized, TRUE)) return; + UNLOCK(); + GC_INVOKE_FINALIZERS(); + LOCK(); + last_finalized_no = GC_gc_no; +} + +/* Allocate an object, clear it, and store the pointer to the */ +/* type structure (vtable in gcj). */ +/* This adds a byte at the end of the object if GC_malloc would.*/ +#if !IL2CPP_ENABLE_WRITE_BARRIER_VALIDATION +#ifdef THREAD_LOCAL_ALLOC + GC_INNER void * GC_core_gcj_malloc(size_t lb, + void * ptr_to_struct_containing_descr) +#else + GC_API GC_ATTR_MALLOC void * GC_CALL GC_gcj_malloc(size_t lb, + void * ptr_to_struct_containing_descr) +#endif +{ + ptr_t op; + DCL_LOCK_STATE; + + GC_DBG_COLLECT_AT_MALLOC(lb); + if(SMALL_OBJ(lb)) { + word lg; + + LOCK(); + lg = GC_size_map[lb]; + op = GC_gcjobjfreelist[lg]; + if(EXPECT(0 == op, FALSE)) { + maybe_finalize(); + op = (ptr_t)GENERAL_MALLOC_INNER((word)lb, GC_gcj_kind); + if (0 == op) { + GC_oom_func oom_fn = GC_oom_fn; + UNLOCK(); + return((*oom_fn)(lb)); + } + } else { + GC_gcjobjfreelist[lg] = (ptr_t)obj_link(op); + GC_bytes_allocd += GRANULES_TO_BYTES((word)lg); + } + GC_ASSERT(((void **)op)[1] == 0); + } else { + LOCK(); + maybe_finalize(); + op = (ptr_t)GENERAL_MALLOC_INNER((word)lb, GC_gcj_kind); + if (0 == op) { + GC_oom_func oom_fn = GC_oom_fn; + UNLOCK(); + return((*oom_fn)(lb)); + } + } + *(void **)op = ptr_to_struct_containing_descr; + UNLOCK(); + GC_dirty(op); + return((void *) op); +} + +#ifdef THREAD_LOCAL_ALLOC +#error No THREAD_LOCAL_ALLOC support for GC_gcj_vector_malloc +#else + GC_API GC_ATTR_MALLOC void * GC_CALL GC_gcj_vector_malloc (size_t lb, + void * ptr_to_struct_containing_descr) +#endif + { + ptr_t op; + DCL_LOCK_STATE; + + GC_DBG_COLLECT_AT_MALLOC (lb); + if (SMALL_OBJ (lb)) { + word lg; + + LOCK (); + lg = GC_size_map[lb]; + op = GC_gcjvecfreelist[lg]; + if (EXPECT (0 == op, FALSE)) { + maybe_finalize (); + op = (ptr_t)GENERAL_MALLOC_INNER ((word)lb, GC_gcj_vector_kind); + if (0 == op) { + GC_oom_func oom_fn = GC_oom_fn; + UNLOCK (); + return((*oom_fn)(lb)); + } + } + else { + GC_gcjvecfreelist[lg] = (ptr_t)obj_link (op); + GC_bytes_allocd += GRANULES_TO_BYTES ((word)lg); + } + GC_ASSERT (((void **)op)[1] == 0); + } + else { + LOCK (); + maybe_finalize (); + op = (ptr_t)GENERAL_MALLOC_INNER ((word)lb, GC_gcj_vector_kind); + if (0 == op) { + GC_oom_func oom_fn = GC_oom_fn; + UNLOCK (); + return((*oom_fn)(lb)); + } + } + *(void **)op = ptr_to_struct_containing_descr; + UNLOCK (); + GC_dirty (op); + return((void *)op); + } +#endif + +/* Similar to GC_gcj_malloc, but add debug info. This is allocated */ +/* with GC_gcj_debug_kind. */ +GC_API GC_ATTR_MALLOC void * GC_CALL GC_debug_gcj_malloc(size_t lb, + void * ptr_to_struct_containing_descr, GC_EXTRA_PARAMS) +{ + void * result; + DCL_LOCK_STATE; + + /* We're careful to avoid extra calls, which could */ + /* confuse the backtrace. */ + LOCK(); + maybe_finalize(); + result = GC_generic_malloc_inner(SIZET_SAT_ADD(lb, DEBUG_BYTES), + GC_gcj_debug_kind); + if (result == 0) { + GC_oom_func oom_fn = GC_oom_fn; + UNLOCK(); + GC_err_printf("GC_debug_gcj_malloc(%lu, %p) returning NULL (%s:%d)\n", + (unsigned long)lb, ptr_to_struct_containing_descr, s, i); + return((*oom_fn)(lb)); + } + *((void **)((ptr_t)result + sizeof(oh))) = ptr_to_struct_containing_descr; + if (!GC_debugging_started) { + GC_start_debugging_inner(); + } + ADD_CALL_CHAIN(result, ra); + result = GC_store_debug_info_inner(result, (word)lb, s, i); + UNLOCK(); + GC_dirty(result); + return result; +} + +/* There is no THREAD_LOCAL_ALLOC for GC_gcj_malloc_ignore_off_page(). */ +GC_API GC_ATTR_MALLOC void * GC_CALL GC_gcj_malloc_ignore_off_page(size_t lb, + void * ptr_to_struct_containing_descr) +{ + ptr_t op; + DCL_LOCK_STATE; + + GC_DBG_COLLECT_AT_MALLOC(lb); + if(SMALL_OBJ(lb)) { + word lg; + + LOCK(); + lg = GC_size_map[lb]; + op = GC_gcjobjfreelist[lg]; + if (EXPECT(0 == op, FALSE)) { + maybe_finalize(); + op = (ptr_t)GENERAL_MALLOC_INNER_IOP(lb, GC_gcj_kind); + if (0 == op) { + GC_oom_func oom_fn = GC_oom_fn; + UNLOCK(); + return((*oom_fn)(lb)); + } + } else { + GC_gcjobjfreelist[lg] = (ptr_t)obj_link(op); + GC_bytes_allocd += GRANULES_TO_BYTES((word)lg); + } + } else { + LOCK(); + maybe_finalize(); + op = (ptr_t)GENERAL_MALLOC_INNER_IOP(lb, GC_gcj_kind); + if (0 == op) { + GC_oom_func oom_fn = GC_oom_fn; + UNLOCK(); + return((*oom_fn)(lb)); + } + } + *(void **)op = ptr_to_struct_containing_descr; + UNLOCK(); + GC_dirty(op); + return((void *) op); +} + +#endif /* GC_GCJ_SUPPORT */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/headers.c b/unity-2019.4.24f1-mbe/external/bdwgc/headers.c new file mode 100644 index 000000000..d2c283c56 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/headers.c @@ -0,0 +1,415 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#include "private/gc_priv.h" + +/* + * This implements: + * 1. allocation of heap block headers + * 2. A map from addresses to heap block addresses to heap block headers + * + * Access speed is crucial. We implement an index structure based on a 2 + * level tree. + */ + +STATIC bottom_index * GC_all_bottom_indices = 0; + /* Pointer to the first (lowest address) */ + /* bottom_index. Assumes the lock is held. */ + +STATIC bottom_index * GC_all_bottom_indices_end = 0; + /* Pointer to the last (highest address) */ + /* bottom_index. Assumes the lock is held. */ + +/* Non-macro version of header location routine */ +GC_INNER hdr * GC_find_header(ptr_t h) +{ +# ifdef HASH_TL + hdr * result; + GET_HDR(h, result); + return(result); +# else + return(HDR_INNER(h)); +# endif +} + +/* Handle a header cache miss. Returns a pointer to the */ +/* header corresponding to p, if p can possibly be a valid */ +/* object pointer, and 0 otherwise. */ +/* GUARANTEED to return 0 for a pointer past the first page */ +/* of an object unless both GC_all_interior_pointers is set */ +/* and p is in fact a valid object pointer. */ +/* Never returns a pointer to a free hblk. */ +GC_INNER hdr * +#ifdef PRINT_BLACK_LIST + GC_header_cache_miss(ptr_t p, hdr_cache_entry *hce, ptr_t source) +#else + GC_header_cache_miss(ptr_t p, hdr_cache_entry *hce) +#endif +{ + hdr *hhdr; + HC_MISS(); + GET_HDR(p, hhdr); + if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + if (GC_all_interior_pointers) { + if (hhdr != 0) { + ptr_t current = p; + + current = (ptr_t)HBLKPTR(current); + do { + current = current - HBLKSIZE*(word)hhdr; + hhdr = HDR(current); + } while(IS_FORWARDING_ADDR_OR_NIL(hhdr)); + /* current points to near the start of the large object */ + if (hhdr -> hb_flags & IGNORE_OFF_PAGE) + return 0; + if (HBLK_IS_FREE(hhdr) + || p - current >= (ptrdiff_t)(hhdr->hb_sz)) { + GC_ADD_TO_BLACK_LIST_NORMAL(p, source); + /* Pointer past the end of the block */ + return 0; + } + } else { + GC_ADD_TO_BLACK_LIST_NORMAL(p, source); + /* And return zero: */ + } + GC_ASSERT(hhdr == 0 || !HBLK_IS_FREE(hhdr)); + return hhdr; + /* Pointers past the first page are probably too rare */ + /* to add them to the cache. We don't. */ + /* And correctness relies on the fact that we don't. */ + } else { + if (hhdr == 0) { + GC_ADD_TO_BLACK_LIST_NORMAL(p, source); + } + return 0; + } + } else { + if (HBLK_IS_FREE(hhdr)) { + GC_ADD_TO_BLACK_LIST_NORMAL(p, source); + return 0; + } else { + hce -> block_addr = (word)(p) >> LOG_HBLKSIZE; + hce -> hce_hdr = hhdr; + return hhdr; + } + } +} + +/* Routines to dynamically allocate collector data structures that will */ +/* never be freed. */ + +static ptr_t scratch_free_ptr = 0; + +/* GC_scratch_last_end_ptr is end point of last obtained scratch area. */ +/* GC_scratch_end_ptr is end point of current scratch area. */ + +GC_INNER ptr_t GC_scratch_alloc(size_t bytes) +{ + ptr_t result = scratch_free_ptr; + size_t bytes_to_get; + + bytes = ROUNDUP_GRANULE_SIZE(bytes); + for (;;) { + scratch_free_ptr += bytes; + if ((word)scratch_free_ptr <= (word)GC_scratch_end_ptr) { + /* Unallocated space of scratch buffer has enough size. */ + return result; + } + + if (bytes >= MINHINCR * HBLKSIZE) { + bytes_to_get = ROUNDUP_PAGESIZE_IF_MMAP(bytes); + result = (ptr_t)GET_MEM(bytes_to_get); + GC_add_to_our_memory(result, bytes_to_get); + /* Undo scratch free area pointer update; get memory directly. */ + scratch_free_ptr -= bytes; + if (result != NULL) { + /* Update end point of last obtained area (needed only */ + /* by GC_register_dynamic_libraries for some targets). */ + GC_scratch_last_end_ptr = result + bytes; + } + return result; + } + + bytes_to_get = ROUNDUP_PAGESIZE_IF_MMAP(MINHINCR * HBLKSIZE); + /* round up for safety */ + result = (ptr_t)GET_MEM(bytes_to_get); + GC_add_to_our_memory(result, bytes_to_get); + if (NULL == result) { + WARN("Out of memory - trying to allocate requested amount" + " (%" WARN_PRIdPTR " bytes)...\n", (word)bytes); + scratch_free_ptr -= bytes; /* Undo free area pointer update */ + bytes_to_get = ROUNDUP_PAGESIZE_IF_MMAP(bytes); + result = (ptr_t)GET_MEM(bytes_to_get); + GC_add_to_our_memory(result, bytes_to_get); + return result; + } + /* Update scratch area pointers and retry. */ + scratch_free_ptr = result; + GC_scratch_end_ptr = scratch_free_ptr + bytes_to_get; + GC_scratch_last_end_ptr = GC_scratch_end_ptr; + } +} + +static hdr * hdr_free_list = 0; + +/* Return an uninitialized header */ +static hdr * alloc_hdr(void) +{ + hdr * result; + + if (NULL == hdr_free_list) { + result = (hdr *)GC_scratch_alloc(sizeof(hdr)); + } else { + result = hdr_free_list; + hdr_free_list = (hdr *) (result -> hb_next); + } + return(result); +} + +GC_INLINE void free_hdr(hdr * hhdr) +{ + hhdr -> hb_next = (struct hblk *) hdr_free_list; + hdr_free_list = hhdr; +} + +#ifdef COUNT_HDR_CACHE_HITS + /* Used for debugging/profiling (the symbols are externally visible). */ + word GC_hdr_cache_hits = 0; + word GC_hdr_cache_misses = 0; +#endif + +GC_INNER void GC_init_headers(void) +{ + unsigned i; + + GC_all_nils = (bottom_index *)GC_scratch_alloc(sizeof(bottom_index)); + if (GC_all_nils == NULL) { + GC_err_printf("Insufficient memory for GC_all_nils\n"); + EXIT(); + } + BZERO(GC_all_nils, sizeof(bottom_index)); + for (i = 0; i < TOP_SZ; i++) { + GC_top_index[i] = GC_all_nils; + } +} + +/* Make sure that there is a bottom level index block for address addr. */ +/* Return FALSE on failure. */ +static GC_bool get_index(word addr) +{ + word hi = (word)(addr) >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE); + bottom_index * r; + bottom_index * p; + bottom_index ** prev; + bottom_index *pi; /* old_p */ + word i; + + GC_ASSERT(I_HOLD_LOCK()); +# ifdef HASH_TL + i = TL_HASH(hi); + + pi = p = GC_top_index[i]; + while(p != GC_all_nils) { + if (p -> key == hi) return(TRUE); + p = p -> hash_link; + } +# else + if (GC_top_index[hi] != GC_all_nils) + return TRUE; + i = hi; +# endif + r = (bottom_index *)GC_scratch_alloc(sizeof(bottom_index)); + if (EXPECT(NULL == r, FALSE)) + return FALSE; + BZERO(r, sizeof(bottom_index)); + r -> key = hi; +# ifdef HASH_TL + r -> hash_link = pi; +# endif + + /* Add it to the list of bottom indices */ + prev = &GC_all_bottom_indices; /* pointer to p */ + pi = 0; /* bottom_index preceding p */ + while ((p = *prev) != 0 && p -> key < hi) { + pi = p; + prev = &(p -> asc_link); + } + r -> desc_link = pi; + if (0 == p) { + GC_all_bottom_indices_end = r; + } else { + p -> desc_link = r; + } + r -> asc_link = p; + *prev = r; + + GC_top_index[i] = r; + return(TRUE); +} + +/* Install a header for block h. */ +/* The header is uninitialized. */ +/* Returns the header or 0 on failure. */ +GC_INNER struct hblkhdr * GC_install_header(struct hblk *h) +{ + hdr * result; + + if (!get_index((word) h)) return(0); + result = alloc_hdr(); + if (result) { + SET_HDR(h, result); +# ifdef USE_MUNMAP + result -> hb_last_reclaimed = (unsigned short)GC_gc_no; +# endif + } + return(result); +} + +/* Set up forwarding counts for block h of size sz */ +GC_INNER GC_bool GC_install_counts(struct hblk *h, size_t sz/* bytes */) +{ + struct hblk * hbp; + + for (hbp = h; (word)hbp < (word)h + sz; hbp += BOTTOM_SZ) { + if (!get_index((word) hbp)) return(FALSE); + } + if (!get_index((word)h + sz - 1)) return(FALSE); + for (hbp = h + 1; (word)hbp < (word)h + sz; hbp += 1) { + word i = HBLK_PTR_DIFF(hbp, h); + + SET_HDR(hbp, (hdr *)(i > MAX_JUMP? MAX_JUMP : i)); + } + return(TRUE); +} + +/* Remove the header for block h */ +GC_INNER void GC_remove_header(struct hblk *h) +{ + hdr **ha; + GET_HDR_ADDR(h, ha); + free_hdr(*ha); + *ha = 0; +} + +/* Remove forwarding counts for h */ +GC_INNER void GC_remove_counts(struct hblk *h, size_t sz/* bytes */) +{ + struct hblk * hbp; + + for (hbp = h+1; (word)hbp < (word)h + sz; hbp += 1) { + SET_HDR(hbp, 0); + } +} + +/* Apply fn to all allocated blocks. It is the caller responsibility */ +/* to avoid data race during the function execution (e.g. by holding */ +/* the allocation lock). */ +void GC_apply_to_all_blocks(void (*fn)(struct hblk *h, word client_data), + word client_data) +{ + signed_word j; + bottom_index * index_p; + + for (index_p = GC_all_bottom_indices; index_p != 0; + index_p = index_p -> asc_link) { + for (j = BOTTOM_SZ-1; j >= 0;) { + if (!IS_FORWARDING_ADDR_OR_NIL(index_p->index[j])) { + if (!HBLK_IS_FREE(index_p->index[j])) { + (*fn)(((struct hblk *) + (((index_p->key << LOG_BOTTOM_SZ) + (word)j) + << LOG_HBLKSIZE)), + client_data); + } + j--; + } else if (index_p->index[j] == 0) { + j--; + } else { + j -= (signed_word)(index_p->index[j]); + } + } + } +} + +/* Get the next valid block whose address is at least h */ +/* Return 0 if there is none. */ +GC_INNER struct hblk * GC_next_used_block(struct hblk *h) +{ + REGISTER bottom_index * bi; + REGISTER word j = ((word)h >> LOG_HBLKSIZE) & (BOTTOM_SZ-1); + + GC_ASSERT(I_HOLD_LOCK()); + GET_BI(h, bi); + if (bi == GC_all_nils) { + REGISTER word hi = (word)h >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE); + + bi = GC_all_bottom_indices; + while (bi != 0 && bi -> key < hi) bi = bi -> asc_link; + j = 0; + } + while(bi != 0) { + while (j < BOTTOM_SZ) { + hdr * hhdr = bi -> index[j]; + if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + j++; + } else { + if (!HBLK_IS_FREE(hhdr)) { + return((struct hblk *) + (((bi -> key << LOG_BOTTOM_SZ) + j) + << LOG_HBLKSIZE)); + } else { + j += divHBLKSZ(hhdr -> hb_sz); + } + } + } + j = 0; + bi = bi -> asc_link; + } + return(0); +} + +/* Get the last (highest address) block whose address is */ +/* at most h. Return 0 if there is none. */ +/* Unlike the above, this may return a free block. */ +GC_INNER struct hblk * GC_prev_block(struct hblk *h) +{ + bottom_index * bi; + signed_word j = ((word)h >> LOG_HBLKSIZE) & (BOTTOM_SZ-1); + + GC_ASSERT(I_HOLD_LOCK()); + GET_BI(h, bi); + if (bi == GC_all_nils) { + word hi = (word)h >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE); + bi = GC_all_bottom_indices_end; + while (bi != 0 && bi -> key > hi) bi = bi -> desc_link; + j = BOTTOM_SZ - 1; + } + while(bi != 0) { + while (j >= 0) { + hdr * hhdr = bi -> index[j]; + if (0 == hhdr) { + --j; + } else if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + j -= (signed_word)hhdr; + } else { + return((struct hblk *) + (((bi -> key << LOG_BOTTOM_SZ) + j) + << LOG_HBLKSIZE)); + } + } + j = BOTTOM_SZ - 1; + bi = bi -> desc_link; + } + return(0); +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/heapsections.c b/unity-2019.4.24f1-mbe/external/bdwgc/heapsections.c new file mode 100644 index 000000000..45bff49a5 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/heapsections.c @@ -0,0 +1,98 @@ +#include "private/gc_priv.h" + +static struct hblk* GetNextFreeBlock(ptr_t ptr) +{ + struct hblk* result = NULL; + unsigned i; + + for (i = 0; i < N_HBLK_FLS + 1; i++) + { + struct hblk* freeBlock = GC_hblkfreelist[i]; + + for (freeBlock = GC_hblkfreelist[i]; freeBlock != NULL; freeBlock = HDR(freeBlock)->hb_next) + { + /* We're only interested in pointers after "ptr" argument */ + if ((ptr_t)freeBlock < ptr) + continue; + + /* If we haven't had a result before or our previous result is */ + /* ahead of the current freeBlock, mark the current freeBlock as result */ + if (result == NULL || result > freeBlock) + result = freeBlock; + } + } + + return result; +} + +static void CallHeapSectionCallback(void* user_data, ptr_t start, ptr_t end, GC_heap_section_proc callback) +{ + hdr *hhdr = HDR(start); + + // Validate that the heap block is valid, then fire our callback. + if (IS_FORWARDING_ADDR_OR_NIL(hhdr) || HBLK_IS_FREE(hhdr)) { + return; + } + + callback(user_data, start, end); +} + +void GC_foreach_heap_section(void* user_data, GC_heap_section_proc callback) +{ + unsigned i; + struct hblk* nextFreeBlock = NULL; + + GC_ASSERT(I_HOLD_LOCK()); + + if (callback == NULL) + return; + + for (i = 0; i < GC_n_heap_sects; i++) + { + ptr_t sectionStart = GC_heap_sects[i].hs_start; + ptr_t sectionEnd = sectionStart + GC_heap_sects[i].hs_bytes; + + /* Merge in contiguous sections. Copied from GC_dump_regions + + A free block might start in one heap section and extend + into the next one. Merging the section avoids crashes when + trying to copy the start of section that is a free block + continued from the previous section. */ + while (i + 1 < GC_n_heap_sects && GC_heap_sects[i + 1].hs_start == sectionEnd) + { + ++i; + sectionEnd = GC_heap_sects[i].hs_start + GC_heap_sects[i].hs_bytes; + } + + while (sectionStart < sectionEnd) + { + nextFreeBlock = GetNextFreeBlock(sectionStart); + + if (nextFreeBlock == NULL || (ptr_t)nextFreeBlock > sectionEnd) + { + CallHeapSectionCallback(user_data, sectionStart, sectionEnd, callback); + break; + } + else + { + size_t sectionLength = (char*)nextFreeBlock - sectionStart; + if (sectionLength > 0) + CallHeapSectionCallback(user_data, sectionStart, sectionStart + sectionLength, callback); + sectionStart = (char*)nextFreeBlock + HDR(nextFreeBlock)->hb_sz; + } + } + } +} + +void HeapSectionCountIncrementer(void* context, GC_PTR start, GC_PTR end) +{ + GC_word* countPtr = (GC_word*)context; + (*countPtr)++; +} + +GC_word GC_get_heap_section_count() +{ + GC_word count = 0; + GC_foreach_heap_section(&count, HeapSectionCountIncrementer); + return count; +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/ia64_save_regs_in_stack.s b/unity-2019.4.24f1-mbe/external/bdwgc/ia64_save_regs_in_stack.s new file mode 100644 index 000000000..2b81edfaa --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/ia64_save_regs_in_stack.s @@ -0,0 +1,11 @@ + .text + .align 16 + .global GC_save_regs_in_stack + .proc GC_save_regs_in_stack +GC_save_regs_in_stack: + .body + flushrs + ;; + mov r8=ar.bsp + br.ret.sptk.few rp + .endp GC_save_regs_in_stack diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/cord.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/cord.h new file mode 100644 index 000000000..4ce89ffd7 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/cord.h @@ -0,0 +1,377 @@ +/* + * Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* + * Cords are immutable character strings. A number of operations + * on long cords are much more efficient than their strings.h counterpart. + * In particular, concatenation takes constant time independent of the length + * of the arguments. (Cords are represented as trees, with internal + * nodes representing concatenation and leaves consisting of either C + * strings or a functional description of the string.) + * + * The following are reasonable applications of cords. They would perform + * unacceptably if C strings were used: + * - A compiler that produces assembly language output by repeatedly + * concatenating instructions onto a cord representing the output file. + * - A text editor that converts the input file to a cord, and then + * performs editing operations by producing a new cord representing + * the file after each character change (and keeping the old ones in an + * edit history) + * + * For optimal performance, cords should be built by + * concatenating short sections. + * This interface is designed for maximum compatibility with C strings. + * ASCII NUL characters may be embedded in cords using CORD_from_fn. + * This is handled correctly, but CORD_to_char_star will produce a string + * with embedded NULs when given such a cord. + * + * This interface is fairly big, largely for performance reasons. + * The most basic constants and functions: + * + * CORD - the type of a cord; + * CORD_EMPTY - empty cord; + * CORD_len(cord) - length of a cord; + * CORD_cat(cord1,cord2) - concatenation of two cords; + * CORD_substr(cord, start, len) - substring (or subcord); + * CORD_pos i; CORD_FOR(i, cord) { ... CORD_pos_fetch(i) ... } - + * examine each character in a cord. CORD_pos_fetch(i) is the char. + * CORD_fetch(int i) - Retrieve i'th character (slowly). + * CORD_cmp(cord1, cord2) - compare two cords. + * CORD_from_file(FILE * f) - turn a read-only file into a cord. + * CORD_to_char_star(cord) - convert to C string. + * (Non-NULL C constant strings are cords.) + * CORD_printf (etc.) - cord version of printf. Use %r for cords. + */ +#ifndef CORD_H +#define CORD_H + +#include +#include + +#ifdef __cplusplus + extern "C" { +#endif + +#if defined(GC_DLL) && !defined(CORD_NOT_DLL) + /* Same as for GC_API in gc_config_macros.h. */ +# ifdef CORD_BUILD +# if defined(__MINGW32__) || defined(__CEGCC__) +# define CORD_API __declspec(dllexport) +# elif defined(_MSC_VER) || defined(__DMC__) || defined(__BORLANDC__) \ + || defined(__CYGWIN__) || defined(__WATCOMC__) +# define CORD_API extern __declspec(dllexport) +# elif defined(__GNUC__) && !defined(GC_NO_VISIBILITY) \ + && (__GNUC__ >= 4 || defined(GC_VISIBILITY_HIDDEN_SET)) + /* Only matters if used in conjunction with -fvisibility=hidden option. */ +# define CORD_API extern __attribute__((__visibility__("default"))) +# endif +# else +# if defined(__MINGW32__) || defined(__CEGCC__) || defined(_MSC_VER) \ + || defined(__DMC__) || defined(__BORLANDC__) || defined(__CYGWIN__) +# define CORD_API __declspec(dllimport) +# elif defined(__WATCOMC__) +# define CORD_API extern __declspec(dllimport) +# endif +# endif /* !CORD_BUILD */ +#endif /* GC_DLL */ + +#ifndef CORD_API +# define CORD_API extern +#endif + +/* Cords have type const char *. This is cheating quite a bit, and not */ +/* 100% portable. But it means that nonempty character string */ +/* constants may be used as cords directly, provided the string is */ +/* never modified in place. The empty cord is represented by, and */ +/* can be written as, 0. */ + +typedef const char * CORD; + +/* An empty cord is always represented as nil */ +#define CORD_EMPTY 0 + +/* Is a nonempty cord represented as a C string? */ +#define CORD_IS_STRING(s) (*(s) != '\0') + +/* Concatenate two cords. If the arguments are C strings, they may */ +/* not be subsequently altered. */ +CORD_API CORD CORD_cat(CORD x, CORD y); + +/* Concatenate a cord and a C string with known length. Except for the */ +/* empty string case, this is a special case of CORD_cat. Since the */ +/* length is known, it can be faster. */ +/* The string y is shared with the resulting CORD. Hence it should */ +/* not be altered by the caller. */ +CORD_API CORD CORD_cat_char_star(CORD x, const char * y, size_t leny); + +/* Compute the length of a cord */ +CORD_API size_t CORD_len(CORD x); + +/* Cords may be represented by functions defining the ith character */ +typedef char (* CORD_fn)(size_t i, void * client_data); + +/* Turn a functional description into a cord. */ +CORD_API CORD CORD_from_fn(CORD_fn fn, void * client_data, size_t len); + +/* Return the substring (subcord really) of x with length at most n, */ +/* starting at position i. (The initial character has position 0.) */ +CORD_API CORD CORD_substr(CORD x, size_t i, size_t n); + +/* Return the argument, but rebalanced to allow more efficient */ +/* character retrieval, substring operations, and comparisons. */ +/* This is useful only for cords that were built using repeated */ +/* concatenation. Guarantees log time access to the result, unless */ +/* x was obtained through a large number of repeated substring ops */ +/* or the embedded functional descriptions take longer to evaluate. */ +/* May reallocate significant parts of the cord. The argument is not */ +/* modified; only the result is balanced. */ +CORD_API CORD CORD_balance(CORD x); + +/* The following traverse a cord by applying a function to each */ +/* character. This is occasionally appropriate, especially where */ +/* speed is crucial. But, since C doesn't have nested functions, */ +/* clients of this sort of traversal are clumsy to write. Consider */ +/* the functions that operate on cord positions instead. */ + +/* Function to iteratively apply to individual characters in cord. */ +typedef int (* CORD_iter_fn)(char c, void * client_data); + +/* Function to apply to substrings of a cord. Each substring is a */ +/* a C character string, not a general cord. */ +typedef int (* CORD_batched_iter_fn)(const char * s, void * client_data); +#define CORD_NO_FN ((CORD_batched_iter_fn)0) + +/* Apply f1 to each character in the cord, in ascending order, */ +/* starting at position i. If */ +/* f2 is not CORD_NO_FN, then multiple calls to f1 may be replaced by */ +/* a single call to f2. The parameter f2 is provided only to allow */ +/* some optimization by the client. This terminates when the right */ +/* end of this string is reached, or when f1 or f2 return != 0. In the */ +/* latter case CORD_iter returns != 0. Otherwise it returns 0. */ +/* The specified value of i must be < CORD_len(x). */ +CORD_API int CORD_iter5(CORD x, size_t i, CORD_iter_fn f1, + CORD_batched_iter_fn f2, void * client_data); + +/* A simpler version that starts at 0, and without f2: */ +CORD_API int CORD_iter(CORD x, CORD_iter_fn f1, void * client_data); +#define CORD_iter(x, f1, cd) CORD_iter5(x, 0, f1, CORD_NO_FN, cd) + +/* Similar to CORD_iter5, but end-to-beginning. No provisions for */ +/* CORD_batched_iter_fn. */ +CORD_API int CORD_riter4(CORD x, size_t i, CORD_iter_fn f1, void * client_data); + +/* A simpler version that starts at the end: */ +CORD_API int CORD_riter(CORD x, CORD_iter_fn f1, void * client_data); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +/* Functions that operate on cord positions. The easy way to traverse */ +/* cords. A cord position is logically a pair consisting of a cord */ +/* and an index into that cord. But it is much faster to retrieve a */ +/* character based on a position than on an index. Unfortunately, */ +/* positions are big (order of a few 100 bytes), so allocate them with */ +/* caution. */ +/* Things in cord_pos.h should be treated as opaque, except as */ +/* described below. Also note that */ +/* CORD_pos_fetch, CORD_next and CORD_prev have both macro and function */ +/* definitions. The former may evaluate their argument more than once. */ +#include "cord_pos.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/* + Visible definitions from above: + + typedef CORD_pos[1]; + + * Extract the cord from a position: + CORD CORD_pos_to_cord(CORD_pos p); + + * Extract the current index from a position: + size_t CORD_pos_to_index(CORD_pos p); + + * Fetch the character located at the given position: + char CORD_pos_fetch(CORD_pos p); + + * Initialize the position to refer to the given cord and index. + * Note that this is the most expensive function on positions: + void CORD_set_pos(CORD_pos p, CORD x, size_t i); + + * Advance the position to the next character. + * P must be initialized and valid. + * Invalidates p if past end: + void CORD_next(CORD_pos p); + + * Move the position to the preceding character. + * P must be initialized and valid. + * Invalidates p if past beginning: + void CORD_prev(CORD_pos p); + + * Is the position valid, i.e. inside the cord? + int CORD_pos_valid(CORD_pos p); +*/ +#define CORD_FOR(pos, cord) \ + for (CORD_set_pos(pos, cord, 0); CORD_pos_valid(pos); CORD_next(pos)) + + +/* An out of memory handler to call. May be supplied by client. */ +/* Must not return. */ +extern void (* CORD_oom_fn)(void); + +/* Dump the representation of x to stdout in an implementation defined */ +/* manner. Intended for debugging only. */ +CORD_API void CORD_dump(CORD x); + +/* The following could easily be implemented by the client. They are */ +/* provided in cordxtra.c for convenience. */ + +/* Concatenate a character to the end of a cord. */ +CORD_API CORD CORD_cat_char(CORD x, char c); + +/* Concatenate n cords. */ +CORD_API CORD CORD_catn(int n, /* CORD */ ...); + +/* Return the character in CORD_substr(x, i, 1) */ +CORD_API char CORD_fetch(CORD x, size_t i); + +/* Return < 0, 0, or > 0, depending on whether x < y, x = y, x > y */ +CORD_API int CORD_cmp(CORD x, CORD y); + +/* A generalization that takes both starting positions for the */ +/* comparison, and a limit on the number of characters to be compared. */ +CORD_API int CORD_ncmp(CORD x, size_t x_start, CORD y, size_t y_start, + size_t len); + +/* Find the first occurrence of s in x at position start or later. */ +/* Return the position of the first character of s in x, or */ +/* CORD_NOT_FOUND if there is none. */ +CORD_API size_t CORD_str(CORD x, size_t start, CORD s); + +/* Return a cord consisting of i copies of (possibly NUL) c. Dangerous */ +/* in conjunction with CORD_to_char_star. */ +/* The resulting representation takes constant space, independent of i. */ +CORD_API CORD CORD_chars(char c, size_t i); +#define CORD_nul(i) CORD_chars('\0', (i)) + +/* Turn a file into cord. The file must be seekable. Its contents */ +/* must remain constant. The file may be accessed as an immediate */ +/* result of this call and/or as a result of subsequent accesses to */ +/* the cord. Short files are likely to be immediately read, but */ +/* long files are likely to be read on demand, possibly relying on */ +/* stdio for buffering. */ +/* We must have exclusive access to the descriptor f, i.e. we may */ +/* read it at any time, and expect the file pointer to be */ +/* where we left it. Normally this should be invoked as */ +/* CORD_from_file(fopen(...)) */ +/* CORD_from_file arranges to close the file descriptor when it is no */ +/* longer needed (e.g. when the result becomes inaccessible). */ +/* The file f must be such that ftell reflects the actual character */ +/* position in the file, i.e. the number of characters that can be */ +/* or were read with fread. On UNIX systems this is always true. On */ +/* MS Windows systems, f must be opened in binary mode. */ +CORD_API CORD CORD_from_file(FILE * f); + +/* Equivalent to the above, except that the entire file will be read */ +/* and the file pointer will be closed immediately. */ +/* The binary mode restriction from above does not apply. */ +CORD_API CORD CORD_from_file_eager(FILE * f); + +/* Equivalent to the above, except that the file will be read on demand.*/ +/* The binary mode restriction applies. */ +CORD_API CORD CORD_from_file_lazy(FILE * f); + +/* Turn a cord into a C string. The result shares no structure with */ +/* x, and is thus modifiable. */ +CORD_API char * CORD_to_char_star(CORD x); + +/* Turn a C string into a CORD. The C string is copied, and so may */ +/* subsequently be modified. */ +CORD_API CORD CORD_from_char_star(const char *s); + +/* Identical to the above, but the result may share structure with */ +/* the argument and is thus not modifiable. */ +CORD_API const char * CORD_to_const_char_star(CORD x); + +/* Write a cord to a file, starting at the current position. No */ +/* trailing NULs are newlines are added. */ +/* Returns EOF if a write error occurs, 1 otherwise. */ +CORD_API int CORD_put(CORD x, FILE * f); + +/* "Not found" result for the following two functions. */ +#define CORD_NOT_FOUND ((size_t)(-1)) + +/* A vague analog of strchr. Returns the position (an integer, not */ +/* a pointer) of the first occurrence of (char) c inside x at position */ +/* i or later. The value i must be < CORD_len(x). */ +CORD_API size_t CORD_chr(CORD x, size_t i, int c); + +/* A vague analog of strrchr. Returns index of the last occurrence */ +/* of (char) c inside x at position i or earlier. The value i */ +/* must be < CORD_len(x). */ +CORD_API size_t CORD_rchr(CORD x, size_t i, int c); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +/* The following are also not primitive, but are implemented in */ +/* cordprnt.c. They provide functionality similar to the ANSI C */ +/* functions with corresponding names, but with the following */ +/* additions and changes: */ +/* 1. A %r conversion specification specifies a CORD argument. Field */ +/* width, precision, etc. have the same semantics as for %s. */ +/* (Note that %c, %C, and %S were already taken.) */ +/* 2. The format string is represented as a CORD. */ +/* 3. CORD_sprintf and CORD_vsprintf assign the result through the 1st */ +/* argument. Unlike their ANSI C versions, there is no need to guess */ +/* the correct buffer size. */ +/* 4. Most of the conversions are implement through the native */ +/* vsprintf. Hence they are usually no faster, and */ +/* idiosyncrasies of the native printf are preserved. However, */ +/* CORD arguments to CORD_sprintf and CORD_vsprintf are NOT copied; */ +/* the result shares the original structure. This may make them */ +/* very efficient in some unusual applications. */ +/* The format string is copied. */ +/* All functions return the number of characters generated or -1 on */ +/* error. This complies with the ANSI standard, but is inconsistent */ +/* with some older implementations of sprintf. */ + +/* The implementation of these is probably less portable than the rest */ +/* of this package. */ + +#ifndef CORD_NO_IO + +#include + +# ifdef __cplusplus + extern "C" { +# endif + +CORD_API int CORD_sprintf(CORD * out, CORD format, ...); +CORD_API int CORD_vsprintf(CORD * out, CORD format, va_list args); +CORD_API int CORD_fprintf(FILE * f, CORD format, ...); +CORD_API int CORD_vfprintf(FILE * f, CORD format, va_list args); +CORD_API int CORD_printf(CORD format, ...); +CORD_API int CORD_vprintf(CORD format, va_list args); + +# ifdef __cplusplus + } /* extern "C" */ +# endif + +#endif /* CORD_NO_IO */ + +#endif /* CORD_H */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/cord_pos.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/cord_pos.h new file mode 100644 index 000000000..29d5df902 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/cord_pos.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* This should never be included directly; included only from cord.h. */ +#if !defined(CORD_POSITION_H) && defined(CORD_H) +#define CORD_POSITION_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* The representation of CORD_position. This is private to the */ +/* implementation, but the size is known to clients. Also */ +/* the implementation of some exported macros relies on it. */ +/* Don't use anything defined here and not in cord.h. */ + +# define MAX_DEPTH 48 + /* The maximum depth of a balanced cord + 1. */ + /* We don't let cords get deeper than MAX_DEPTH. */ + +struct CORD_pe { + CORD pe_cord; + size_t pe_start_pos; +}; + +/* A structure describing an entry on the path from the root */ +/* to current position. */ +typedef struct CORD_Pos { + size_t cur_pos; + int path_len; +# define CORD_POS_INVALID (0x55555555) + /* path_len == INVALID <==> position invalid */ + const char *cur_leaf; /* Current leaf, if it is a string. */ + /* If the current leaf is a function, */ + /* then this may point to function_buf */ + /* containing the next few characters. */ + /* Always points to a valid string */ + /* containing the current character */ + /* unless cur_end is 0. */ + size_t cur_start; /* Start position of cur_leaf */ + size_t cur_end; /* Ending position of cur_leaf */ + /* 0 if cur_leaf is invalid. */ + struct CORD_pe path[MAX_DEPTH + 1]; + /* path[path_len] is the leaf corresponding to cur_pos */ + /* path[0].pe_cord is the cord we point to. */ +# define FUNCTION_BUF_SZ 8 + char function_buf[FUNCTION_BUF_SZ]; /* Space for next few chars */ + /* from function node. */ +} CORD_pos[1]; + +/* Extract the cord from a position: */ +CORD_API CORD CORD_pos_to_cord(CORD_pos p); + +/* Extract the current index from a position: */ +CORD_API size_t CORD_pos_to_index(CORD_pos p); + +/* Fetch the character located at the given position: */ +CORD_API char CORD_pos_fetch(CORD_pos p); + +/* Initialize the position to refer to the give cord and index. */ +/* Note that this is the most expensive function on positions: */ +CORD_API void CORD_set_pos(CORD_pos p, CORD x, size_t i); + +/* Advance the position to the next character. */ +/* P must be initialized and valid. */ +/* Invalidates p if past end: */ +CORD_API void CORD_next(CORD_pos p); + +/* Move the position to the preceding character. */ +/* P must be initialized and valid. */ +/* Invalidates p if past beginning: */ +CORD_API void CORD_prev(CORD_pos p); + +/* Is the position valid, i.e. inside the cord? */ +CORD_API int CORD_pos_valid(CORD_pos p); + +CORD_API char CORD__pos_fetch(CORD_pos); +CORD_API void CORD__next(CORD_pos); +CORD_API void CORD__prev(CORD_pos); + +#define CORD_pos_fetch(p) \ + (((p)[0].cur_end != 0)? \ + (p)[0].cur_leaf[(p)[0].cur_pos - (p)[0].cur_start] \ + : CORD__pos_fetch(p)) + +#define CORD_next(p) \ + (((p)[0].cur_pos + 1 < (p)[0].cur_end)? \ + (p)[0].cur_pos++ \ + : (CORD__next(p), 0)) + +#define CORD_prev(p) \ + (((p)[0].cur_end != 0 && (p)[0].cur_pos > (p)[0].cur_start)? \ + (p)[0].cur_pos-- \ + : (CORD__prev(p), 0)) + +#define CORD_pos_to_index(p) ((p)[0].cur_pos) + +#define CORD_pos_to_cord(p) ((p)[0].path[0].pe_cord) + +#define CORD_pos_valid(p) ((p)[0].path_len != CORD_POS_INVALID) + +/* Some grubby stuff for performance-critical friends: */ +#define CORD_pos_chars_left(p) ((long)((p)[0].cur_end) - (long)((p)[0].cur_pos)) + /* Number of characters in cache. <= 0 ==> none */ + +#define CORD_pos_advance(p,n) ((p)[0].cur_pos += (n) - 1, CORD_next(p)) + /* Advance position by n characters */ + /* 0 < n < CORD_pos_chars_left(p) */ + +#define CORD_pos_cur_char_addr(p) \ + (p)[0].cur_leaf + ((p)[0].cur_pos - (p)[0].cur_start) + /* address of current character in cache. */ + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/ec.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/ec.h new file mode 100644 index 000000000..904a05a65 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/ec.h @@ -0,0 +1,76 @@ +# ifndef EC_H +# define EC_H + +# ifndef CORD_H +# include "cord.h" +# endif + +#ifdef __cplusplus + extern "C" { +#endif + +/* Extensible cords are strings that may be destructively appended to. */ +/* They allow fast construction of cords from characters that are */ +/* being read from a stream. */ +/* + * A client might look like: + * + * { + * CORD_ec x; + * CORD result; + * char c; + * FILE *f; + * + * ... + * CORD_ec_init(x); + * while(...) { + * c = getc(f); + * ... + * CORD_ec_append(x, c); + * } + * result = CORD_balance(CORD_ec_to_cord(x)); + * + * If a C string is desired as the final result, the call to CORD_balance + * may be replaced by a call to CORD_to_char_star. + */ + +# ifndef CORD_BUFSZ +# define CORD_BUFSZ 128 +# endif + +typedef struct CORD_ec_struct { + CORD ec_cord; + char * ec_bufptr; + char ec_buf[CORD_BUFSZ+1]; +} CORD_ec[1]; + +/* This structure represents the concatenation of ec_cord with */ +/* ec_buf[0 ... (ec_bufptr-ec_buf-1)] */ + +/* Flush the buffer part of the extended chord into ec_cord. */ +/* Note that this is almost the only real function, and it is */ +/* implemented in 6 lines in cordxtra.c */ +void CORD_ec_flush_buf(CORD_ec x); + +/* Convert an extensible cord to a cord. */ +# define CORD_ec_to_cord(x) (CORD_ec_flush_buf(x), (x)[0].ec_cord) + +/* Initialize an extensible cord. */ +#define CORD_ec_init(x) \ + ((x)[0].ec_cord = 0, (void)((x)[0].ec_bufptr = (x)[0].ec_buf)) + +/* Append a character to an extensible cord. */ +#define CORD_ec_append(x, c) \ + (((x)[0].ec_bufptr == (x)[0].ec_buf + CORD_BUFSZ ? \ + (CORD_ec_flush_buf(x), 0) : 0), \ + (void)(*(x)[0].ec_bufptr++ = (c))) + +/* Append a cord to an extensible cord. Structure remains shared with */ +/* original. */ +void CORD_ec_append_cord(CORD_ec x, CORD s); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +# endif /* EC_H */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/extra/gc.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/extra/gc.h new file mode 100644 index 000000000..55ae4c6c1 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/extra/gc.h @@ -0,0 +1,2 @@ +/* This file is installed for backward compatibility. */ +#include diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/extra/gc_cpp.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/extra/gc_cpp.h new file mode 100644 index 000000000..36669f9c8 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/extra/gc_cpp.h @@ -0,0 +1,2 @@ +/* This file is installed for backward compatibility. */ +#include diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/gc.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/gc.h new file mode 100644 index 000000000..ffb2d37a2 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/gc.h @@ -0,0 +1,2048 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. + * Copyright 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright 1999 by Hewlett-Packard Company. All rights reserved. + * Copyright (C) 2007 Free Software Foundation, Inc + * Copyright (c) 2000-2011 by Hewlett-Packard Development Company. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* + * Note that this defines a large number of tuning hooks, which can + * safely be ignored in nearly all cases. For normal use it suffices + * to call only GC_MALLOC and perhaps GC_REALLOC. + * For better performance, also look at GC_MALLOC_ATOMIC, and + * GC_enable_incremental. If you need an action to be performed + * immediately before an object is collected, look at GC_register_finalizer. + * If you are using Solaris threads, look at the end of this file. + * Everything else is best ignored unless you encounter performance + * problems. + */ + +#ifndef GC_H +#define GC_H + +/* Help debug mixed up preprocessor symbols. */ +#if (defined(WIN64) && !defined(_WIN64)) && defined(_MSC_VER) +#pragma message("Warning: Expecting _WIN64 for x64 targets! Notice the leading underscore!") +#endif + +#include "gc_version.h" + /* Define version numbers here to allow test on build machine */ + /* for cross-builds. Note that this defines the header */ + /* version number, which may or may not match that of the */ + /* dynamic library. GC_get_version() can be used to obtain */ + /* the latter. */ + +#include "gc_config_macros.h" + +#ifdef __cplusplus + extern "C" { +#endif + +typedef void * GC_PTR; /* preserved only for backward compatibility */ + +/* Define word and signed_word to be unsigned and signed types of the */ +/* size as char * or void *. There seems to be no way to do this */ +/* even semi-portably. The following is probably no better/worse */ +/* than almost anything else. */ +/* The ANSI standard suggests that size_t and ptrdiff_t might be */ +/* better choices. But those had incorrect definitions on some older */ +/* systems. Notably "typedef int size_t" is WRONG. */ +#ifdef _WIN64 +# if defined(__int64) && !defined(CPPCHECK) + typedef unsigned __int64 GC_word; + typedef __int64 GC_signed_word; +# else + typedef unsigned long long GC_word; + typedef long long GC_signed_word; +# endif +#else + typedef unsigned long GC_word; + typedef long GC_signed_word; +#endif + +/* Get the GC library version. The returned value is a constant in the */ +/* form: ((version_major<<16) | (version_minor<<8) | version_micro). */ +GC_API unsigned GC_CALL GC_get_version(void); + +/* Public read-only variables */ +/* The supplied getter functions are preferred for new code. */ + +GC_API GC_ATTR_DEPRECATED GC_word GC_gc_no; + /* Counter incremented per collection. */ + /* Includes empty GCs at startup. */ +GC_API GC_word GC_CALL GC_get_gc_no(void); + /* GC_get_gc_no() is unsynchronized, so */ + /* it requires GC_call_with_alloc_lock() to */ + /* avoid data races on multiprocessors. */ + +#ifdef GC_THREADS + GC_API GC_ATTR_DEPRECATED int GC_parallel; + /* GC is parallelized for performance on */ + /* multiprocessors. Currently set only */ + /* implicitly if collector is built with */ + /* PARALLEL_MARK defined and if either: */ + /* Env variable GC_NPROC is set to > 1, or */ + /* GC_NPROC is not set and this is an MP. */ + /* If GC_parallel is on (non-zero), incremental */ + /* collection is only partially functional, */ + /* and may not be desirable. The getter does */ + /* not use or need synchronization (i.e. */ + /* acquiring the GC lock). Starting from */ + /* GC v7.3, GC_parallel value is equal to the */ + /* number of marker threads minus one (i.e. */ + /* number of existing parallel marker threads */ + /* excluding the initiating one). */ + GC_API int GC_CALL GC_get_parallel(void); +#endif + + +/* Public R/W variables */ +/* The supplied setter and getter functions are preferred for new code. */ + +typedef void * (GC_CALLBACK * GC_oom_func)(size_t /* bytes_requested */); +GC_API GC_ATTR_DEPRECATED GC_oom_func GC_oom_fn; + /* When there is insufficient memory to satisfy */ + /* an allocation request, we return */ + /* (*GC_oom_fn)(size). By default this just */ + /* returns NULL. */ + /* If it returns, it must return 0 or a valid */ + /* pointer to a previously allocated heap */ + /* object. GC_oom_fn must not be 0. */ + /* Both the supplied setter and the getter */ + /* acquire the GC lock (to avoid data races). */ +GC_API void GC_CALL GC_set_oom_fn(GC_oom_func) GC_ATTR_NONNULL(1); +GC_API GC_oom_func GC_CALL GC_get_oom_fn(void); + +typedef void (GC_CALLBACK * GC_on_heap_resize_proc)(GC_word /* new_size */); +GC_API GC_ATTR_DEPRECATED GC_on_heap_resize_proc GC_on_heap_resize; + /* Invoked when the heap grows or shrinks. */ + /* Called with the world stopped (and the */ + /* allocation lock held). May be 0. */ +GC_API void GC_CALL GC_set_on_heap_resize(GC_on_heap_resize_proc); +GC_API GC_on_heap_resize_proc GC_CALL GC_get_on_heap_resize(void); + /* Both the supplied setter and the getter */ + /* acquire the GC lock (to avoid data races). */ + +typedef enum { + GC_EVENT_START /* COLLECTION */, + GC_EVENT_MARK_START, + GC_EVENT_MARK_END, + GC_EVENT_RECLAIM_START, + GC_EVENT_RECLAIM_END, + GC_EVENT_END /* COLLECTION */, + GC_EVENT_PRE_STOP_WORLD /* STOPWORLD_BEGIN */, + GC_EVENT_POST_STOP_WORLD /* STOPWORLD_END */, + GC_EVENT_PRE_START_WORLD /* STARTWORLD_BEGIN */, + GC_EVENT_POST_START_WORLD /* STARTWORLD_END */, + GC_EVENT_THREAD_SUSPENDED, + GC_EVENT_THREAD_UNSUSPENDED +} GC_EventType; + +typedef void (GC_CALLBACK * GC_on_collection_event_proc)(GC_EventType); + /* Invoked to indicate progress through the */ + /* collection process. Not used for thread */ + /* suspend/resume notifications. Called with */ + /* the GC lock held (or, even, the world */ + /* stopped). May be 0 (means no notifier). */ +GC_API void GC_CALL GC_set_on_collection_event(GC_on_collection_event_proc); +GC_API GC_on_collection_event_proc GC_CALL GC_get_on_collection_event(void); + /* Both the supplied setter and the getter */ + /* acquire the GC lock (to avoid data races). */ + +#if defined(GC_THREADS) || defined(NN_PLATFORM_CTR) || defined(NN_BUILD_TARGET_PLATFORM_NX) + typedef void (GC_CALLBACK * GC_on_thread_event_proc)(GC_EventType, + void * /* thread_id */); + /* Invoked when a thread is suspended or */ + /* resumed during collection. Called with the */ + /* GC lock held (and the world stopped */ + /* partially). May be 0 (means no notifier). */ + GC_API void GC_CALL GC_set_on_thread_event(GC_on_thread_event_proc); + GC_API GC_on_thread_event_proc GC_CALL GC_get_on_thread_event(void); + /* Both the supplied setter and the getter */ + /* acquire the GC lock (to avoid data races). */ +#endif + +GC_API GC_ATTR_DEPRECATED int GC_find_leak; + /* Set to true to turn on the leak-finding mode */ + /* (do not actually garbage collect, but simply */ + /* report inaccessible memory that was not */ + /* deallocated with GC_FREE). Initial value */ + /* is determined by FIND_LEAK macro. */ + /* The value should not typically be modified */ + /* after GC initialization (and, thus, it does */ + /* not use or need synchronization). */ +GC_API void GC_CALL GC_set_find_leak(int); +GC_API int GC_CALL GC_get_find_leak(void); + +GC_API GC_ATTR_DEPRECATED int GC_all_interior_pointers; + /* Arrange for pointers to object interiors to */ + /* be recognized as valid. Typically should */ + /* not be changed after GC initialization (in */ + /* case of calling it after the GC is */ + /* initialized, the setter acquires the GC lock */ + /* (to avoid data races). The initial value */ + /* depends on whether the GC is built with */ + /* ALL_INTERIOR_POINTERS macro defined or not. */ + /* Unless DONT_ADD_BYTE_AT_END is defined, this */ + /* also affects whether sizes are increased by */ + /* at least a byte to allow "off the end" */ + /* pointer recognition. Must be only 0 or 1. */ +GC_API void GC_CALL GC_set_all_interior_pointers(int); +GC_API int GC_CALL GC_get_all_interior_pointers(void); + +GC_API GC_ATTR_DEPRECATED int GC_finalize_on_demand; + /* If nonzero, finalizers will only be run in */ + /* response to an explicit GC_invoke_finalizers */ + /* call. The default is determined by whether */ + /* the FINALIZE_ON_DEMAND macro is defined */ + /* when the collector is built. */ + /* The setter and getter are unsynchronized. */ +GC_API void GC_CALL GC_set_finalize_on_demand(int); +GC_API int GC_CALL GC_get_finalize_on_demand(void); + +GC_API GC_ATTR_DEPRECATED int GC_java_finalization; + /* Mark objects reachable from finalizable */ + /* objects in a separate post-pass. This makes */ + /* it a bit safer to use non-topologically- */ + /* ordered finalization. Default value is */ + /* determined by JAVA_FINALIZATION macro. */ + /* Enables register_finalizer_unreachable to */ + /* work correctly. */ + /* The setter and getter are unsynchronized. */ +GC_API void GC_CALL GC_set_java_finalization(int); +GC_API int GC_CALL GC_get_java_finalization(void); + +typedef void (GC_CALLBACK * GC_finalizer_notifier_proc)(void); +GC_API GC_ATTR_DEPRECATED GC_finalizer_notifier_proc GC_finalizer_notifier; + /* Invoked by the collector when there are */ + /* objects to be finalized. Invoked at most */ + /* once per GC cycle. Never invoked unless */ + /* GC_finalize_on_demand is set. */ + /* Typically this will notify a finalization */ + /* thread, which will call GC_invoke_finalizers */ + /* in response. May be 0 (means no notifier). */ + /* Both the supplied setter and the getter */ + /* acquire the GC lock (to avoid data races). */ +GC_API void GC_CALL GC_set_finalizer_notifier(GC_finalizer_notifier_proc); +GC_API GC_finalizer_notifier_proc GC_CALL GC_get_finalizer_notifier(void); + +GC_API +# ifndef GC_DONT_GC + GC_ATTR_DEPRECATED +# endif + int GC_dont_gc; /* != 0 ==> Don't collect. In versions 6.2a1+, */ + /* this overrides explicit GC_gcollect() calls. */ + /* Used as a counter, so that nested enabling */ + /* and disabling work correctly. Should */ + /* normally be updated with GC_enable() and */ + /* GC_disable() calls. Direct assignment to */ + /* GC_dont_gc is deprecated. To check whether */ + /* GC is disabled, GC_is_disabled() is */ + /* preferred for new code. */ + +GC_API GC_ATTR_DEPRECATED int GC_dont_expand; + /* Do not expand the heap unless explicitly */ + /* requested or forced to. The setter and */ + /* getter are unsynchronized. */ +GC_API void GC_CALL GC_set_dont_expand(int); +GC_API int GC_CALL GC_get_dont_expand(void); + +GC_API GC_ATTR_DEPRECATED int GC_use_entire_heap; + /* Causes the non-incremental collector to use the */ + /* entire heap before collecting. This was the only */ + /* option for GC versions < 5.0. This sometimes */ + /* results in more large block fragmentation, since */ + /* very large blocks will tend to get broken up */ + /* during each GC cycle. It is likely to result in a */ + /* larger working set, but lower collection */ + /* frequencies, and hence fewer instructions executed */ + /* in the collector. */ + +GC_API GC_ATTR_DEPRECATED int GC_full_freq; + /* Number of partial collections between */ + /* full collections. Matters only if */ + /* GC_is_incremental_mode(). */ + /* Full collections are also triggered if */ + /* the collector detects a substantial */ + /* increase in the number of in-use heap */ + /* blocks. Values in the tens are now */ + /* perfectly reasonable, unlike for */ + /* earlier GC versions. */ + /* The setter and getter are unsynchronized, so */ + /* GC_call_with_alloc_lock() is required to */ + /* avoid data races (if the value is modified */ + /* after the GC is put to multi-threaded mode). */ +GC_API void GC_CALL GC_set_full_freq(int); +GC_API int GC_CALL GC_get_full_freq(void); + +GC_API GC_ATTR_DEPRECATED GC_word GC_non_gc_bytes; + /* Bytes not considered candidates for */ + /* collection. Used only to control scheduling */ + /* of collections. Updated by */ + /* GC_malloc_uncollectable and GC_free. */ + /* Wizards only. */ + /* The setter and getter are unsynchronized, so */ + /* GC_call_with_alloc_lock() is required to */ + /* avoid data races (if the value is modified */ + /* after the GC is put to multi-threaded mode). */ +GC_API void GC_CALL GC_set_non_gc_bytes(GC_word); +GC_API GC_word GC_CALL GC_get_non_gc_bytes(void); + +GC_API GC_ATTR_DEPRECATED int GC_no_dls; + /* Don't register dynamic library data segments. */ + /* Wizards only. Should be used only if the */ + /* application explicitly registers all roots. */ + /* (In some environments like Microsoft Windows */ + /* and Apple's Darwin, this may also prevent */ + /* registration of the main data segment as part */ + /* of the root set.) */ + /* The setter and getter are unsynchronized. */ +GC_API void GC_CALL GC_set_no_dls(int); +GC_API int GC_CALL GC_get_no_dls(void); + +GC_API GC_ATTR_DEPRECATED GC_word GC_free_space_divisor; + /* We try to make sure that we allocate at */ + /* least N/GC_free_space_divisor bytes between */ + /* collections, where N is twice the number */ + /* of traced bytes, plus the number of untraced */ + /* bytes (bytes in "atomic" objects), plus */ + /* a rough estimate of the root set size. */ + /* N approximates GC tracing work per GC. */ + /* The initial value is GC_FREE_SPACE_DIVISOR. */ + /* Increasing its value will use less space */ + /* but more collection time. Decreasing it */ + /* will appreciably decrease collection time */ + /* at the expense of space. */ + /* The setter and getter are unsynchronized, so */ + /* GC_call_with_alloc_lock() is required to */ + /* avoid data races (if the value is modified */ + /* after the GC is put to multi-threaded mode). */ + /* In version 7.1 (and before), the setter */ + /* returned the old value. */ +GC_API void GC_CALL GC_set_free_space_divisor(GC_word); +GC_API GC_word GC_CALL GC_get_free_space_divisor(void); + +GC_API GC_ATTR_DEPRECATED GC_word GC_max_retries; + /* The maximum number of GCs attempted before */ + /* reporting out of memory after heap */ + /* expansion fails. Initially 0. */ + /* The setter and getter are unsynchronized, so */ + /* GC_call_with_alloc_lock() is required to */ + /* avoid data races (if the value is modified */ + /* after the GC is put to multi-threaded mode). */ +GC_API void GC_CALL GC_set_max_retries(GC_word); +GC_API GC_word GC_CALL GC_get_max_retries(void); + + +GC_API GC_ATTR_DEPRECATED char *GC_stackbottom; + /* Cool end of user stack. */ + /* May be set in the client prior to */ + /* calling any GC_ routines. This */ + /* avoids some overhead, and */ + /* potentially some signals that can */ + /* confuse debuggers. Otherwise the */ + /* collector attempts to set it */ + /* automatically. */ + /* For multi-threaded code, this is the */ + /* cold end of the stack for the */ + /* primordial thread. Portable clients */ + /* should use GC_get_stack_base(), */ + /* GC_call_with_gc_active() and */ + /* GC_register_my_thread() instead. */ + +GC_API GC_ATTR_DEPRECATED int GC_dont_precollect; + /* Do not collect as part of GC */ + /* initialization. Should be set only */ + /* if the client wants a chance to */ + /* manually initialize the root set */ + /* before the first collection. */ + /* Interferes with blacklisting. */ + /* Wizards only. The setter and getter */ + /* are unsynchronized (and no external */ + /* locking is needed since the value is */ + /* accessed at GC initialization only). */ +GC_API void GC_CALL GC_set_dont_precollect(int); +GC_API int GC_CALL GC_get_dont_precollect(void); + +GC_API GC_ATTR_DEPRECATED unsigned long long GC_time_limit; + /* If incremental collection is enabled, */ + /* We try to terminate collections */ + /* after this many milliseconds. Not a */ + /* hard time bound. Setting this to */ + /* GC_TIME_UNLIMITED will essentially */ + /* disable incremental collection while */ + /* leaving generational collection */ + /* enabled. */ +#define GC_TIME_UNLIMITED 999999999999 + /* Setting GC_time_limit to this value */ + /* will disable the "pause time exceeded"*/ + /* tests. */ + /* The setter and getter are unsynchronized, so */ + /* GC_call_with_alloc_lock() is required to */ + /* avoid data races (if the value is modified */ + /* after the GC is put to multi-threaded mode). */ +GC_API void GC_CALL GC_set_time_limit(unsigned long); +GC_API void GC_CALL GC_set_time_limit_ns(unsigned long long); +GC_API unsigned long GC_CALL GC_get_time_limit(void); +GC_API unsigned long long GC_CALL GC_get_time_limit_ns(void); + +/* Public procedures */ + +/* Tell the collector to start various performance measurements. */ +/* Only the total time taken by full collections is calculated, as */ +/* of now. And, currently, there is no way to stop the measurements. */ +/* The function does not use any synchronization. Defined only if the */ +/* library has been compiled without NO_CLOCK. */ +GC_API void GC_CALL GC_start_performance_measurement(void); + +/* Get the total time of all full collections since the start of the */ +/* performance measurements. The measurement unit is one millisecond. */ +/* Note that the returned value wraps around on overflow. */ +/* The function does not use any synchronization. Defined only if the */ +/* library has been compiled without NO_CLOCK. */ +GC_API unsigned long GC_CALL GC_get_full_gc_total_time(void); + +/* Set whether the GC will allocate executable memory pages or not. */ +/* A non-zero argument instructs the collector to allocate memory with */ +/* the executable flag on. Must be called before the collector is */ +/* initialized. May have no effect on some platforms. The default */ +/* value is controlled by NO_EXECUTE_PERMISSION macro (if present then */ +/* the flag is off). Portable clients should have */ +/* GC_set_pages_executable(1) call (before GC_INIT) provided they are */ +/* going to execute code on any of the GC-allocated memory objects. */ +GC_API void GC_CALL GC_set_pages_executable(int); + +/* Returns non-zero value if the GC is set to the allocate-executable */ +/* mode. The mode could be changed by GC_set_pages_executable (before */ +/* GC_INIT) unless the former has no effect on the platform. Does not */ +/* use or need synchronization (i.e. acquiring the allocator lock). */ +GC_API int GC_CALL GC_get_pages_executable(void); + +/* The setter and getter of the minimum value returned by the internal */ +/* min_bytes_allocd(). The value should not be zero; the default value */ +/* is one. Not synchronized. */ +GC_API void GC_CALL GC_set_min_bytes_allocd(size_t); +GC_API size_t GC_CALL GC_get_min_bytes_allocd(void); + +/* Set/get the size in pages of units operated by GC_collect_a_little. */ +/* The value should not be zero. Not synchronized. */ +GC_API void GC_CALL GC_set_rate(int); +GC_API int GC_CALL GC_get_rate(void); + +/* Set/get the maximum number of prior attempts at the world-stop */ +/* marking. Not synchronized. */ +GC_API void GC_CALL GC_set_max_prior_attempts(int); +GC_API int GC_CALL GC_get_max_prior_attempts(void); + +/* Overrides the default handle-fork mode. Non-zero value means GC */ +/* should install proper pthread_atfork handlers. Has effect only if */ +/* called before GC_INIT. Clients should invoke GC_set_handle_fork */ +/* with non-zero argument if going to use fork with GC functions called */ +/* in the forked child. (Note that such client and atfork handlers */ +/* activities are not fully POSIX-compliant.) GC_set_handle_fork */ +/* instructs GC_init to setup GC fork handlers using pthread_atfork, */ +/* the latter might fail (or, even, absent on some targets) causing */ +/* abort at GC initialization. Starting from 7.3alpha3, problems with */ +/* missing (or failed) pthread_atfork() could be avoided by invocation */ +/* of GC_set_handle_fork(-1) at application start-up and surrounding */ +/* each fork() with the relevant GC_atfork_prepare/parent/child calls. */ +GC_API void GC_CALL GC_set_handle_fork(int); + +/* Routines to handle POSIX fork() manually (no-op if handled */ +/* automatically). GC_atfork_prepare should be called immediately */ +/* before fork(); GC_atfork_parent should be invoked just after fork in */ +/* the branch that corresponds to parent process (i.e., fork result is */ +/* non-zero); GC_atfork_child is to be called immediately in the child */ +/* branch (i.e., fork result is 0). Note that GC_atfork_child() call */ +/* should, of course, precede GC_start_mark_threads call (if any). */ +GC_API void GC_CALL GC_atfork_prepare(void); +GC_API void GC_CALL GC_atfork_parent(void); +GC_API void GC_CALL GC_atfork_child(void); + +/* Initialize the collector. Portable clients should call GC_INIT() */ +/* from the main program instead. */ +GC_API void GC_CALL GC_init(void); + +/* Returns non-zero (TRUE) if and only if the collector is initialized */ +/* (or, at least, the initialization is in progress). */ +GC_API int GC_CALL GC_is_init_called(void); + +/* Perform the collector shutdown. (E.g. dispose critical sections on */ +/* Win32 target.) A duplicate invocation is a no-op. GC_INIT should */ +/* not be called after the shutdown. See also GC_win32_free_heap(). */ +GC_API void GC_CALL GC_deinit(void); + +/* General purpose allocation routines, with roughly malloc calling */ +/* conv. The atomic versions promise that no relevant pointers are */ +/* contained in the object. The non-atomic versions guarantee that the */ +/* new object is cleared. GC_malloc_uncollectable allocates */ +/* an object that is scanned for pointers to collectible */ +/* objects, but is not itself collectible. The object is scanned even */ +/* if it does not appear to be reachable. GC_malloc_uncollectable and */ +/* GC_free called on the resulting object implicitly update */ +/* GC_non_gc_bytes appropriately. */ +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_malloc(size_t /* size_in_bytes */); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_malloc_atomic(size_t /* size_in_bytes */); +GC_API GC_ATTR_MALLOC char * GC_CALL GC_strdup(const char *); +GC_API GC_ATTR_MALLOC char * GC_CALL + GC_strndup(const char *, size_t) GC_ATTR_NONNULL(1); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_malloc_uncollectable(size_t /* size_in_bytes */); +GC_API GC_ATTR_DEPRECATED void * GC_CALL GC_malloc_stubborn(size_t); + +/* GC_memalign() is not well tested. */ +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(2) void * GC_CALL + GC_memalign(size_t /* align */, size_t /* lb */); +GC_API int GC_CALL GC_posix_memalign(void ** /* memptr */, size_t /* align */, + size_t /* lb */) GC_ATTR_NONNULL(1); + +/* Explicitly deallocate an object. Dangerous if used incorrectly. */ +/* Requires a pointer to the base of an object. */ +/* An object should not be enabled for finalization (and it should not */ +/* contain registered disappearing links of any kind) when it is */ +/* explicitly deallocated. */ +/* GC_free(0) is a no-op, as required by ANSI C for free. */ +GC_API void GC_CALL GC_free(void *); + +/* The "stubborn" objects allocation is not supported anymore. Exists */ +/* only for the backward compatibility. */ +#define GC_MALLOC_STUBBORN(sz) GC_MALLOC(sz) +#define GC_NEW_STUBBORN(t) GC_NEW(t) +#define GC_CHANGE_STUBBORN(p) GC_change_stubborn(p) +GC_API GC_ATTR_DEPRECATED void GC_CALL GC_change_stubborn(const void *); + +/* Inform the collector that the object has been changed. */ +/* Only non-NULL pointer stores into the object are considered to be */ +/* changes. Matters only if the library has been compiled with */ +/* MANUAL_VDB defined (otherwise the function does nothing). */ +GC_API void GC_CALL GC_end_stubborn_change(const void *) GC_ATTR_NONNULL(1); + +/* Return a pointer to the base (lowest address) of an object given */ +/* a pointer to a location within the object. */ +/* I.e., map an interior pointer to the corresponding base pointer. */ +/* Note that with debugging allocation, this returns a pointer to the */ +/* actual base of the object, i.e. the debug information, not to */ +/* the base of the user object. */ +/* Return 0 if displaced_pointer doesn't point to within a valid */ +/* object. */ +/* Note that a deallocated object in the garbage collected heap */ +/* may be considered valid, even if it has been deallocated with */ +/* GC_free. */ +GC_API void * GC_CALL GC_base(void * /* displaced_pointer */); + +/* Return non-zero (TRUE) if and only if the argument points to */ +/* somewhere in GC heap. Primary use is as a fast alternative to */ +/* GC_base to check whether the pointed object is allocated by GC */ +/* or not. It is assumed that the collector is already initialized. */ +GC_API int GC_CALL GC_is_heap_ptr(const void *); + +/* Given a pointer to the base of an object, return its size in bytes. */ +/* The returned size may be slightly larger than what was originally */ +/* requested. */ +GC_API size_t GC_CALL GC_size(const void * /* obj_addr */) GC_ATTR_NONNULL(1); + +/* For compatibility with C library. This is occasionally faster than */ +/* a malloc followed by a bcopy. But if you rely on that, either here */ +/* or with the standard C library, your code is broken. In my */ +/* opinion, it shouldn't have been invented, but now we're stuck. -HB */ +/* The resulting object has the same kind as the original. */ +/* It is an error to have changes enabled for the original object. */ +/* It does not change the content of the object from its beginning to */ +/* the minimum of old size and new_size_in_bytes; the content above in */ +/* case of object size growth is initialized to zero (not guaranteed */ +/* for atomic object type). The function follows ANSI conventions for */ +/* NULL old_object (i.e., equivalent to GC_malloc regardless of new */ +/* size). If new size is zero (and old_object is non-NULL) then the */ +/* call is equivalent to GC_free (and NULL is returned). If old_object */ +/* is non-NULL, it must have been returned by an earlier call to */ +/* GC_malloc* or GC_realloc. In case of the allocation failure, the */ +/* memory pointed by old_object is untouched (and not freed). */ +/* If the returned pointer is not the same as old_object and both of */ +/* them are non-NULL then old_object is freed. Returns either NULL (in */ +/* case of the allocation failure or zero new size) or pointer to the */ +/* allocated memory. */ +GC_API void * GC_CALL GC_realloc(void * /* old_object */, + size_t /* new_size_in_bytes */) + /* 'realloc' attr */ GC_ATTR_ALLOC_SIZE(2); + +/* Explicitly increase the heap size. */ +/* Returns 0 on failure, 1 on success. */ +GC_API int GC_CALL GC_expand_hp(size_t /* number_of_bytes */); + +/* Limit the heap size to n bytes. Useful when you're debugging, */ +/* especially on systems that don't handle running out of memory well. */ +/* n == 0 ==> unbounded. This is the default. This setter function is */ +/* unsynchronized (so it might require GC_call_with_alloc_lock to avoid */ +/* data races). */ +GC_API void GC_CALL GC_set_max_heap_size(GC_word /* n */); + +/* Inform the collector that a certain section of statically allocated */ +/* memory contains no pointers to garbage collected memory. Thus it */ +/* need not be scanned. This is sometimes important if the application */ +/* maps large read/write files into the address space, which could be */ +/* mistaken for dynamic library data segments on some systems. */ +/* Both section start and end are not needed to be pointer-aligned. */ +GC_API void GC_CALL GC_exclude_static_roots(void * /* low_address */, + void * /* high_address_plus_1 */); + +/* Clear the set of root segments. Wizards only. */ +GC_API void GC_CALL GC_clear_roots(void); + +/* Add a root segment. Wizards only. */ +/* Both segment start and end are not needed to be pointer-aligned. */ +/* low_address must not be greater than high_address_plus_1. */ +GC_API void GC_CALL GC_add_roots(void * /* low_address */, + void * /* high_address_plus_1 */); + +/* Remove a root segment. Wizards only. */ +/* May be unimplemented on some platforms. */ +GC_API void GC_CALL GC_remove_roots(void * /* low_address */, + void * /* high_address_plus_1 */); + +/* Add a displacement to the set of those considered valid by the */ +/* collector. GC_register_displacement(n) means that if p was returned */ +/* by GC_malloc, then (char *)p + n will be considered to be a valid */ +/* pointer to p. N must be small and less than the size of p. */ +/* (All pointers to the interior of objects from the stack are */ +/* considered valid in any case. This applies to heap objects and */ +/* static data.) */ +/* Preferably, this should be called before any other GC procedures. */ +/* Calling it later adds to the probability of excess memory */ +/* retention. */ +/* This is a no-op if the collector has recognition of */ +/* arbitrary interior pointers enabled, which is now the default. */ +GC_API void GC_CALL GC_register_displacement(size_t /* n */); + +/* The following version should be used if any debugging allocation is */ +/* being done. */ +GC_API void GC_CALL GC_debug_register_displacement(size_t /* n */); + +/* Explicitly trigger a full, world-stop collection. */ +GC_API void GC_CALL GC_gcollect(void); + +/* Same as above but ignores the default stop_func setting and tries to */ +/* unmap as much memory as possible (regardless of the corresponding */ +/* switch setting). The recommended usage: on receiving a system */ +/* low-memory event; before retrying a system call failed because of */ +/* the system is running out of resources. */ +GC_API void GC_CALL GC_gcollect_and_unmap(void); + +/* Trigger a full world-stopped collection. Abort the collection if */ +/* and when stop_func returns a nonzero value. Stop_func will be */ +/* called frequently, and should be reasonably fast. (stop_func is */ +/* called with the allocation lock held and the world might be stopped; */ +/* it's not allowed for stop_func to manipulate pointers to the garbage */ +/* collected heap or call most of GC functions.) This works even */ +/* if virtual dirty bits, and hence incremental collection is not */ +/* available for this architecture. Collections can be aborted faster */ +/* than normal pause times for incremental collection. However, */ +/* aborted collections do no useful work; the next collection needs */ +/* to start from the beginning. stop_func must not be 0. */ +/* GC_try_to_collect() returns 0 if the collection was aborted (or the */ +/* collections are disabled), 1 if it succeeded. */ +typedef int (GC_CALLBACK * GC_stop_func)(void); +GC_API int GC_CALL GC_try_to_collect(GC_stop_func /* stop_func */) + GC_ATTR_NONNULL(1); + +/* Set and get the default stop_func. The default stop_func is used by */ +/* GC_gcollect() and by implicitly trigged collections (except for the */ +/* case when handling out of memory). Must not be 0. */ +/* Both the setter and getter acquire the GC lock to avoid data races. */ +GC_API void GC_CALL GC_set_stop_func(GC_stop_func /* stop_func */) + GC_ATTR_NONNULL(1); +GC_API GC_stop_func GC_CALL GC_get_stop_func(void); + +/* Return the number of bytes in the heap. Excludes collector private */ +/* data structures. Excludes the unmapped memory (returned to the OS). */ +/* Includes empty blocks and fragmentation loss. Includes some pages */ +/* that were allocated but never written. */ +/* This is an unsynchronized getter, so it should be called typically */ +/* with the GC lock held to avoid data races on multiprocessors (the */ +/* alternative is to use GC_get_heap_usage_safe or GC_get_prof_stats */ +/* API calls instead). */ +/* This getter remains lock-free (unsynchronized) for compatibility */ +/* reason since some existing clients call it from a GC callback */ +/* holding the allocator lock. (This API function and the following */ +/* four ones below were made thread-safe in GC v7.2alpha1 and */ +/* reverted back in v7.2alpha7 for the reason described.) */ +GC_API size_t GC_CALL GC_get_heap_size(void); + +/* Return a lower bound on the number of free bytes in the heap */ +/* (excluding the unmapped memory space). This is an unsynchronized */ +/* getter (see GC_get_heap_size comment regarding thread-safety). */ +GC_API size_t GC_CALL GC_get_free_bytes(void); + +/* Return the size (in bytes) of the unmapped memory (which is returned */ +/* to the OS but could be remapped back by the collector later unless */ +/* the OS runs out of system/virtual memory). This is an unsynchronized */ +/* getter (see GC_get_heap_size comment regarding thread-safety). */ +GC_API size_t GC_CALL GC_get_unmapped_bytes(void); + +/* Return the number of bytes allocated since the last collection. */ +/* This is an unsynchronized getter (see GC_get_heap_size comment */ +/* regarding thread-safety). */ +GC_API size_t GC_CALL GC_get_bytes_since_gc(void); + +/* Return the number of explicitly deallocated bytes of memory since */ +/* the recent collection. This is an unsynchronized getter. */ +GC_API size_t GC_CALL GC_get_expl_freed_bytes_since_gc(void); + +/* Return the total number of bytes allocated in this process. */ +/* Never decreases, except due to wrapping. This is an unsynchronized */ +/* getter (see GC_get_heap_size comment regarding thread-safety). */ +GC_API size_t GC_CALL GC_get_total_bytes(void); + +/* Return the heap usage information. This is a thread-safe (atomic) */ +/* alternative for the five above getters. (This function acquires */ +/* the allocator lock thus preventing data racing and returning the */ +/* consistent result.) Passing NULL pointer is allowed for any */ +/* argument. Returned (filled in) values are of word type. */ +/* (This API function was introduced in GC v7.2alpha7 at the same time */ +/* when GC_get_heap_size and the friends were made lock-free again.) */ +GC_API void GC_CALL GC_get_heap_usage_safe(GC_word * /* pheap_size */, + GC_word * /* pfree_bytes */, + GC_word * /* punmapped_bytes */, + GC_word * /* pbytes_since_gc */, + GC_word * /* ptotal_bytes */); + +/* Structure used to query GC statistics (profiling information). */ +/* More fields could be added in the future. To preserve compatibility */ +/* new fields should be added only to the end, and no deprecated fields */ +/* should be removed from. */ +struct GC_prof_stats_s { + GC_word heapsize_full; + /* Heap size in bytes (including the area unmapped to OS). */ + /* Same as GC_get_heap_size() + GC_get_unmapped_bytes(). */ + GC_word free_bytes_full; + /* Total bytes contained in free and unmapped blocks. */ + /* Same as GC_get_free_bytes() + GC_get_unmapped_bytes(). */ + GC_word unmapped_bytes; + /* Amount of memory unmapped to OS. Same as the value */ + /* returned by GC_get_unmapped_bytes(). */ + GC_word bytes_allocd_since_gc; + /* Number of bytes allocated since the recent collection. */ + /* Same as returned by GC_get_bytes_since_gc(). */ + GC_word allocd_bytes_before_gc; + /* Number of bytes allocated before the recent garbage */ + /* collection. The value may wrap. Same as the result of */ + /* GC_get_total_bytes() - GC_get_bytes_since_gc(). */ + GC_word non_gc_bytes; + /* Number of bytes not considered candidates for garbage */ + /* collection. Same as returned by GC_get_non_gc_bytes(). */ + GC_word gc_no; + /* Garbage collection cycle number. The value may wrap */ + /* (and could be -1). Same as returned by GC_get_gc_no(). */ + GC_word markers_m1; + /* Number of marker threads (excluding the initiating one). */ + /* Same as returned by GC_get_parallel (or 0 if the */ + /* collector is single-threaded). */ + GC_word bytes_reclaimed_since_gc; + /* Approximate number of reclaimed bytes after recent GC. */ + GC_word reclaimed_bytes_before_gc; + /* Approximate number of bytes reclaimed before the recent */ + /* garbage collection. The value may wrap. */ + GC_word expl_freed_bytes_since_gc; + /* Number of bytes freed explicitly since the recent GC. */ + /* Same as returned by GC_get_expl_freed_bytes_since_gc(). */ +}; + +/* Atomically get GC statistics (various global counters). Clients */ +/* should pass the size of the buffer (of GC_prof_stats_s type) to fill */ +/* in the values - this is for interoperability between different GC */ +/* versions, an old client could have fewer fields, and vice versa, */ +/* client could use newer gc.h (with more entries declared in the */ +/* structure) than that of the linked libgc binary; in the latter case, */ +/* unsupported (unknown) fields are filled in with -1. Return the size */ +/* (in bytes) of the filled in part of the structure (excluding all */ +/* unknown fields, if any). */ +GC_API size_t GC_CALL GC_get_prof_stats(struct GC_prof_stats_s *, + size_t /* stats_sz */); +#ifdef GC_THREADS + /* Same as above but unsynchronized (i.e., not holding the allocation */ + /* lock). Clients should call it using GC_call_with_alloc_lock to */ + /* avoid data races on multiprocessors. */ + GC_API size_t GC_CALL GC_get_prof_stats_unsafe(struct GC_prof_stats_s *, + size_t /* stats_sz */); +#endif + +/* Get the element value (converted to bytes) at a given index of */ +/* size_map table which provides requested-to-actual allocation size */ +/* mapping. Assumes the collector is initialized. Returns -1 if the */ +/* index is out of size_map table bounds. Does not use synchronization, */ +/* thus clients should call it using GC_call_with_alloc_lock typically */ +/* to avoid data races on multiprocessors. */ +GC_API size_t GC_CALL GC_get_size_map_at(int i); + +/* Count total memory use in bytes by all allocated blocks. Acquires */ +/* the lock. */ +GC_API size_t GC_CALL GC_get_memory_use(void); + +/* Disable garbage collection. Even GC_gcollect calls will be */ +/* ineffective. */ +GC_API void GC_CALL GC_disable(void); + +/* Return non-zero (TRUE) if and only if garbage collection is disabled */ +/* (i.e., GC_dont_gc value is non-zero). Does not acquire the lock. */ +GC_API int GC_CALL GC_is_disabled(void); + +/* Try to re-enable garbage collection. GC_disable() and GC_enable() */ +/* calls nest. Garbage collection is enabled if the number of calls to */ +/* both functions is equal. */ +GC_API void GC_CALL GC_enable(void); + +/* Enable incremental/generational collection. Not advisable unless */ +/* dirty bits are available or most heap objects are pointer-free */ +/* (atomic) or immutable. Don't use in leak finding mode. Ignored if */ +/* GC_dont_gc is non-zero. Only the generational piece of this is */ +/* functional if GC_parallel is non-zero or if GC_time_limit is */ +/* GC_TIME_UNLIMITED. Causes thread-local variant of GC_gcj_malloc() */ +/* to revert to locked allocation. Must be called before any such */ +/* GC_gcj_malloc() calls. For best performance, should be called as */ +/* early as possible. On some platforms, calling it later may have */ +/* adverse effects. */ +/* Safe to call before GC_INIT(). Includes a GC_init() call. */ +GC_API void GC_CALL GC_enable_incremental(void); + +/* Return non-zero (TRUE) if and only if the incremental mode is on. */ +/* Does not acquire the lock. */ +GC_API int GC_CALL GC_is_incremental_mode(void); + +/* Does incremental mode write-protect pages? Returns zero or */ +/* more of the following, or'ed together: */ +#define GC_PROTECTS_POINTER_HEAP 1 /* May protect non-atomic objects. */ +#define GC_PROTECTS_PTRFREE_HEAP 2 +#define GC_PROTECTS_STATIC_DATA 4 /* Currently never. */ +#define GC_PROTECTS_STACK 8 /* Probably impractical. */ + +#define GC_PROTECTS_NONE 0 +/* The collector is assumed to be initialized before this call. */ +GC_API int GC_CALL GC_incremental_protection_needs(void); + +/* Perform some garbage collection work, if appropriate. */ +/* Return 0 if there is no more work to be done (including the */ +/* case when garbage collection is not appropriate). */ +/* Typically performs an amount of work corresponding roughly */ +/* to marking from one page. May do more work if further */ +/* progress requires it, e.g. if incremental collection is */ +/* disabled. It is reasonable to call this in a wait loop */ +/* until it returns 0. */ +GC_API int GC_CALL GC_collect_a_little(void); + +/* Allocate an object of size lb bytes. The client guarantees that */ +/* as long as the object is live, it will be referenced by a pointer */ +/* that points to somewhere within the first 256 bytes of the object. */ +/* (This should normally be declared volatile to prevent the compiler */ +/* from invalidating this assertion.) This routine is only useful */ +/* if a large array is being allocated. It reduces the chance of */ +/* accidentally retaining such an array as a result of scanning an */ +/* integer that happens to be an address inside the array. (Actually, */ +/* it reduces the chance of the allocator not finding space for such */ +/* an array, since it will try hard to avoid introducing such a false */ +/* reference.) On a SunOS 4.X or MS Windows system this is recommended */ +/* for arrays likely to be larger than 100K or so. For other systems, */ +/* or if the collector is not configured to recognize all interior */ +/* pointers, the threshold is normally much higher. */ +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_malloc_ignore_off_page(size_t /* lb */); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_malloc_atomic_ignore_off_page(size_t /* lb */); + +#ifdef GC_ADD_CALLER +# define GC_EXTRAS GC_RETURN_ADDR, __FILE__, __LINE__ +# define GC_EXTRA_PARAMS GC_word ra, const char * s, int i +#else +# define GC_EXTRAS __FILE__, __LINE__ +# define GC_EXTRA_PARAMS const char * s, int i +#endif + +/* The following is only defined if the library has been suitably */ +/* compiled: */ +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_malloc_atomic_uncollectable(size_t /* size_in_bytes */); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_debug_malloc_atomic_uncollectable(size_t, GC_EXTRA_PARAMS); + +/* Debugging (annotated) allocation. GC_gcollect will check */ +/* objects allocated in this way for overwrites, etc. */ +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_debug_malloc(size_t /* size_in_bytes */, GC_EXTRA_PARAMS); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_debug_malloc_atomic(size_t /* size_in_bytes */, GC_EXTRA_PARAMS); +GC_API GC_ATTR_MALLOC char * GC_CALL + GC_debug_strdup(const char *, GC_EXTRA_PARAMS); +GC_API GC_ATTR_MALLOC char * GC_CALL + GC_debug_strndup(const char *, size_t, GC_EXTRA_PARAMS) + GC_ATTR_NONNULL(1); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_debug_malloc_uncollectable(size_t /* size_in_bytes */, + GC_EXTRA_PARAMS); +GC_API GC_ATTR_DEPRECATED void * GC_CALL + GC_debug_malloc_stubborn(size_t /* size_in_bytes */, GC_EXTRA_PARAMS); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_debug_malloc_ignore_off_page(size_t /* size_in_bytes */, + GC_EXTRA_PARAMS); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_debug_malloc_atomic_ignore_off_page(size_t /* size_in_bytes */, + GC_EXTRA_PARAMS); +GC_API void GC_CALL GC_debug_free(void *); +GC_API void * GC_CALL GC_debug_realloc(void * /* old_object */, + size_t /* new_size_in_bytes */, GC_EXTRA_PARAMS) + /* 'realloc' attr */ GC_ATTR_ALLOC_SIZE(2); +GC_API +#if !defined(CPPCHECK) + GC_ATTR_DEPRECATED +#endif +void GC_CALL GC_debug_change_stubborn(const void *); +GC_API void GC_CALL GC_debug_end_stubborn_change(const void *) + GC_ATTR_NONNULL(1); + +/* Routines that allocate objects with debug information (like the */ +/* above), but just fill in dummy file and line number information. */ +/* Thus they can serve as drop-in malloc/realloc replacements. This */ +/* can be useful for two reasons: */ +/* 1) It allows the collector to be built with DBG_HDRS_ALL defined */ +/* even if some allocation calls come from 3rd party libraries */ +/* that can't be recompiled. */ +/* 2) On some platforms, the file and line information is redundant, */ +/* since it can be reconstructed from a stack trace. On such */ +/* platforms it may be more convenient not to recompile, e.g. for */ +/* leak detection. This can be accomplished by instructing the */ +/* linker to replace malloc/realloc with these. */ +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_debug_malloc_replacement(size_t /* size_in_bytes */); +GC_API /* 'realloc' attr */ GC_ATTR_ALLOC_SIZE(2) void * GC_CALL + GC_debug_realloc_replacement(void * /* object_addr */, + size_t /* size_in_bytes */); + +#ifdef GC_DEBUG_REPLACEMENT +# define GC_MALLOC(sz) GC_debug_malloc_replacement(sz) +# define GC_REALLOC(old, sz) GC_debug_realloc_replacement(old, sz) +#elif defined(GC_DEBUG) +# define GC_MALLOC(sz) GC_debug_malloc(sz, GC_EXTRAS) +# define GC_REALLOC(old, sz) GC_debug_realloc(old, sz, GC_EXTRAS) +#else +# define GC_MALLOC(sz) GC_malloc(sz) +# define GC_REALLOC(old, sz) GC_realloc(old, sz) +#endif /* !GC_DEBUG_REPLACEMENT && !GC_DEBUG */ + +#ifdef GC_DEBUG +# define GC_MALLOC_ATOMIC(sz) GC_debug_malloc_atomic(sz, GC_EXTRAS) +# define GC_STRDUP(s) GC_debug_strdup(s, GC_EXTRAS) +# define GC_STRNDUP(s, sz) GC_debug_strndup(s, sz, GC_EXTRAS) +# define GC_MALLOC_ATOMIC_UNCOLLECTABLE(sz) \ + GC_debug_malloc_atomic_uncollectable(sz, GC_EXTRAS) +# define GC_MALLOC_UNCOLLECTABLE(sz) \ + GC_debug_malloc_uncollectable(sz, GC_EXTRAS) +# define GC_MALLOC_IGNORE_OFF_PAGE(sz) \ + GC_debug_malloc_ignore_off_page(sz, GC_EXTRAS) +# define GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE(sz) \ + GC_debug_malloc_atomic_ignore_off_page(sz, GC_EXTRAS) +# define GC_FREE(p) GC_debug_free(p) +# define GC_REGISTER_FINALIZER(p, f, d, of, od) \ + GC_debug_register_finalizer(p, f, d, of, od) +# define GC_REGISTER_FINALIZER_IGNORE_SELF(p, f, d, of, od) \ + GC_debug_register_finalizer_ignore_self(p, f, d, of, od) +# define GC_REGISTER_FINALIZER_NO_ORDER(p, f, d, of, od) \ + GC_debug_register_finalizer_no_order(p, f, d, of, od) +# define GC_REGISTER_FINALIZER_UNREACHABLE(p, f, d, of, od) \ + GC_debug_register_finalizer_unreachable(p, f, d, of, od) +# define GC_END_STUBBORN_CHANGE(p) GC_debug_end_stubborn_change(p) +# define GC_GENERAL_REGISTER_DISAPPEARING_LINK(link, obj) \ + GC_general_register_disappearing_link(link, \ + GC_base((/* no const */ void *)(obj))) +# define GC_REGISTER_LONG_LINK(link, obj) \ + GC_register_long_link(link, GC_base((/* no const */ void *)(obj))) +# define GC_REGISTER_DISPLACEMENT(n) GC_debug_register_displacement(n) +#else +# define GC_MALLOC_ATOMIC(sz) GC_malloc_atomic(sz) +# define GC_STRDUP(s) GC_strdup(s) +# define GC_STRNDUP(s, sz) GC_strndup(s, sz) +# define GC_MALLOC_ATOMIC_UNCOLLECTABLE(sz) GC_malloc_atomic_uncollectable(sz) +# define GC_MALLOC_UNCOLLECTABLE(sz) GC_malloc_uncollectable(sz) +# define GC_MALLOC_IGNORE_OFF_PAGE(sz) \ + GC_malloc_ignore_off_page(sz) +# define GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE(sz) \ + GC_malloc_atomic_ignore_off_page(sz) +# define GC_FREE(p) GC_free(p) +# define GC_REGISTER_FINALIZER(p, f, d, of, od) \ + GC_register_finalizer(p, f, d, of, od) +# define GC_REGISTER_FINALIZER_IGNORE_SELF(p, f, d, of, od) \ + GC_register_finalizer_ignore_self(p, f, d, of, od) +# define GC_REGISTER_FINALIZER_NO_ORDER(p, f, d, of, od) \ + GC_register_finalizer_no_order(p, f, d, of, od) +# define GC_REGISTER_FINALIZER_UNREACHABLE(p, f, d, of, od) \ + GC_register_finalizer_unreachable(p, f, d, of, od) +# define GC_END_STUBBORN_CHANGE(p) GC_end_stubborn_change(p) +# define GC_GENERAL_REGISTER_DISAPPEARING_LINK(link, obj) \ + GC_general_register_disappearing_link(link, obj) +# define GC_REGISTER_LONG_LINK(link, obj) \ + GC_register_long_link(link, obj) +# define GC_REGISTER_DISPLACEMENT(n) GC_register_displacement(n) +#endif /* !GC_DEBUG */ + +/* The following are included because they are often convenient, and */ +/* reduce the chance for a misspecified size argument. But calls may */ +/* expand to something syntactically incorrect if t is a complicated */ +/* type expression. Note that, unlike C++ new operator, these ones */ +/* may return NULL (if out of memory). */ +#define GC_NEW(t) ((t*)GC_MALLOC(sizeof(t))) +#define GC_NEW_ATOMIC(t) ((t*)GC_MALLOC_ATOMIC(sizeof(t))) +#define GC_NEW_UNCOLLECTABLE(t) ((t*)GC_MALLOC_UNCOLLECTABLE(sizeof(t))) + +#ifdef GC_REQUIRE_WCSDUP + /* This might be unavailable on some targets (or not needed). */ + /* wchar_t should be defined in stddef.h */ + GC_API GC_ATTR_MALLOC wchar_t * GC_CALL + GC_wcsdup(const wchar_t *) GC_ATTR_NONNULL(1); + GC_API GC_ATTR_MALLOC wchar_t * GC_CALL + GC_debug_wcsdup(const wchar_t *, GC_EXTRA_PARAMS) GC_ATTR_NONNULL(1); +# ifdef GC_DEBUG +# define GC_WCSDUP(s) GC_debug_wcsdup(s, GC_EXTRAS) +# else +# define GC_WCSDUP(s) GC_wcsdup(s) +# endif +#endif /* GC_REQUIRE_WCSDUP */ + +/* Finalization. Some of these primitives are grossly unsafe. */ +/* The idea is to make them both cheap, and sufficient to build */ +/* a safer layer, closer to Modula-3, Java, or PCedar finalization. */ +/* The interface represents my conclusions from a long discussion */ +/* with Alan Demers, Dan Greene, Carl Hauser, Barry Hayes, */ +/* Christian Jacobi, and Russ Atkinson. It's not perfect, and */ +/* probably nobody else agrees with it. Hans-J. Boehm 3/13/92 */ +typedef void (GC_CALLBACK * GC_finalization_proc)(void * /* obj */, + void * /* client_data */); + +GC_API void GC_CALL GC_register_finalizer(void * /* obj */, + GC_finalization_proc /* fn */, void * /* cd */, + GC_finalization_proc * /* ofn */, void ** /* ocd */) + GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_debug_register_finalizer(void * /* obj */, + GC_finalization_proc /* fn */, void * /* cd */, + GC_finalization_proc * /* ofn */, void ** /* ocd */) + GC_ATTR_NONNULL(1); + /* When obj is no longer accessible, invoke */ + /* (*fn)(obj, cd). If a and b are inaccessible, and */ + /* a points to b (after disappearing links have been */ + /* made to disappear), then only a will be */ + /* finalized. (If this does not create any new */ + /* pointers to b, then b will be finalized after the */ + /* next collection.) Any finalizable object that */ + /* is reachable from itself by following one or more */ + /* pointers will not be finalized (or collected). */ + /* Thus cycles involving finalizable objects should */ + /* be avoided, or broken by disappearing links. */ + /* All but the last finalizer registered for an object */ + /* is ignored. */ + /* No-op in the leak-finding mode. */ + /* Finalization may be removed by passing 0 as fn. */ + /* Finalizers are implicitly unregistered when they are */ + /* enqueued for finalization (i.e. become ready to be */ + /* finalized). */ + /* The old finalizer and client data are stored in */ + /* *ofn and *ocd. (ofn and/or ocd may be NULL. */ + /* The allocation lock is held while *ofn and *ocd are */ + /* updated. In case of error (no memory to register */ + /* new finalizer), *ofn and *ocd remain unchanged.) */ + /* Fn is never invoked on an accessible object, */ + /* provided hidden pointers are converted to real */ + /* pointers only if the allocation lock is held, and */ + /* such conversions are not performed by finalization */ + /* routines. */ + /* If GC_register_finalizer is aborted as a result of */ + /* a signal, the object may be left with no */ + /* finalization, even if neither the old nor new */ + /* finalizer were NULL. */ + /* Obj should be the starting address of an object */ + /* allocated by GC_malloc or friends. Obj may also be */ + /* NULL or point to something outside GC heap (in this */ + /* case, fn is ignored, *ofn and *ocd are set to NULL). */ + /* Note that any garbage collectible object referenced */ + /* by cd will be considered accessible until the */ + /* finalizer is invoked. */ + +/* Another versions of the above follow. It ignores */ +/* self-cycles, i.e. pointers from a finalizable object to */ +/* itself. There is a stylistic argument that this is wrong, */ +/* but it's unavoidable for C++, since the compiler may */ +/* silently introduce these. It's also benign in that specific */ +/* case. And it helps if finalizable objects are split to */ +/* avoid cycles. */ +/* Note that cd will still be viewed as accessible, even if it */ +/* refers to the object itself. */ +GC_API void GC_CALL GC_register_finalizer_ignore_self(void * /* obj */, + GC_finalization_proc /* fn */, void * /* cd */, + GC_finalization_proc * /* ofn */, void ** /* ocd */) + GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_debug_register_finalizer_ignore_self(void * /* obj */, + GC_finalization_proc /* fn */, void * /* cd */, + GC_finalization_proc * /* ofn */, void ** /* ocd */) + GC_ATTR_NONNULL(1); + +/* Another version of the above. It ignores all cycles. */ +/* It should probably only be used by Java implementations. */ +/* Note that cd will still be viewed as accessible, even if it */ +/* refers to the object itself. */ +GC_API void GC_CALL GC_register_finalizer_no_order(void * /* obj */, + GC_finalization_proc /* fn */, void * /* cd */, + GC_finalization_proc * /* ofn */, void ** /* ocd */) + GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_debug_register_finalizer_no_order(void * /* obj */, + GC_finalization_proc /* fn */, void * /* cd */, + GC_finalization_proc * /* ofn */, void ** /* ocd */) + GC_ATTR_NONNULL(1); + +/* This is a special finalizer that is useful when an object's */ +/* finalizer must be run when the object is known to be no */ +/* longer reachable, not even from other finalizable objects. */ +/* It behaves like "normal" finalization, except that the */ +/* finalizer is not run while the object is reachable from */ +/* other objects specifying unordered finalization. */ +/* Effectively it allows an object referenced, possibly */ +/* indirectly, from an unordered finalizable object to override */ +/* the unordered finalization request. */ +/* This can be used in combination with finalizer_no_order so */ +/* as to release resources that must not be released while an */ +/* object can still be brought back to life by other */ +/* finalizers. */ +/* Only works if GC_java_finalization is set. Probably only */ +/* of interest when implementing a language that requires */ +/* unordered finalization (e.g. Java, C#). */ +GC_API void GC_CALL GC_register_finalizer_unreachable(void * /* obj */, + GC_finalization_proc /* fn */, void * /* cd */, + GC_finalization_proc * /* ofn */, void ** /* ocd */) + GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_debug_register_finalizer_unreachable(void * /* obj */, + GC_finalization_proc /* fn */, void * /* cd */, + GC_finalization_proc * /* ofn */, void ** /* ocd */) + GC_ATTR_NONNULL(1); + +#define GC_NO_MEMORY 2 /* Failure due to lack of memory. */ + +/* The following routine may be used to break cycles between */ +/* finalizable objects, thus causing cyclic finalizable */ +/* objects to be finalized in the correct order. Standard */ +/* use involves calling GC_register_disappearing_link(&p), */ +/* where p is a pointer that is not followed by finalization */ +/* code, and should not be considered in determining */ +/* finalization order. */ +GC_API int GC_CALL GC_register_disappearing_link(void ** /* link */) + GC_ATTR_NONNULL(1); + /* Link should point to a field of a heap allocated */ + /* object obj. *link will be cleared when obj is */ + /* found to be inaccessible. This happens BEFORE any */ + /* finalization code is invoked, and BEFORE any */ + /* decisions about finalization order are made. */ + /* This is useful in telling the finalizer that */ + /* some pointers are not essential for proper */ + /* finalization. This may avoid finalization cycles. */ + /* Note that obj may be resurrected by another */ + /* finalizer, and thus the clearing of *link may */ + /* be visible to non-finalization code. */ + /* There's an argument that an arbitrary action should */ + /* be allowed here, instead of just clearing a pointer. */ + /* But this causes problems if that action alters, or */ + /* examines connectivity. Returns GC_DUPLICATE if link */ + /* was already registered, GC_SUCCESS if registration */ + /* succeeded, GC_NO_MEMORY if it failed for lack of */ + /* memory, and GC_oom_fn did not handle the problem. */ + /* Only exists for backward compatibility. See below: */ + +GC_API int GC_CALL GC_general_register_disappearing_link(void ** /* link */, + const void * /* obj */) + GC_ATTR_NONNULL(1) GC_ATTR_NONNULL(2); + /* A slight generalization of the above. *link is */ + /* cleared when obj first becomes inaccessible. This */ + /* can be used to implement weak pointers easily and */ + /* safely. Typically link will point to a location */ + /* holding a disguised pointer to obj. (A pointer */ + /* inside an "atomic" object is effectively disguised.) */ + /* In this way, weak pointers are broken before any */ + /* object reachable from them gets finalized. */ + /* Each link may be registered only with one obj value, */ + /* i.e. all objects but the last one (link registered */ + /* with) are ignored. This was added after a long */ + /* email discussion with John Ellis. */ + /* link must be non-NULL (and be properly aligned). */ + /* obj must be a pointer to the first word of an object */ + /* allocated by GC_malloc or friends. A link */ + /* disappears when it is unregistered manually, or when */ + /* (*link) is cleared, or when the object containing */ + /* this link is garbage collected. It is unsafe to */ + /* explicitly deallocate the object containing link. */ + /* Explicit deallocation of obj may or may not cause */ + /* link to eventually be cleared. */ + /* No-op in the leak-finding mode. */ + /* This function can be used to implement certain types */ + /* of weak pointers. Note, however, this generally */ + /* requires that the allocation lock is held (see */ + /* GC_call_with_alloc_lock() below) when the disguised */ + /* pointer is accessed. Otherwise a strong pointer */ + /* could be recreated between the time the collector */ + /* decides to reclaim the object and the link is */ + /* cleared. Returns GC_SUCCESS if registration */ + /* succeeded (a new link is registered), GC_DUPLICATE */ + /* if link was already registered (with some object), */ + /* GC_NO_MEMORY if registration failed for lack of */ + /* memory (and GC_oom_fn did not handle the problem), */ + /* GC_UNIMPLEMENTED if GC_find_leak is true. */ + +GC_API int GC_CALL GC_move_disappearing_link(void ** /* link */, + void ** /* new_link */) + GC_ATTR_NONNULL(2); + /* Moves a link previously registered via */ + /* GC_general_register_disappearing_link (or */ + /* GC_register_disappearing_link). Does not change the */ + /* target object of the weak reference. Does not */ + /* change (*new_link) content. May be called with */ + /* new_link equal to link (to check whether link has */ + /* been registered). Returns GC_SUCCESS on success, */ + /* GC_DUPLICATE if there is already another */ + /* disappearing link at the new location (never */ + /* returned if new_link is equal to link), GC_NOT_FOUND */ + /* if no link is registered at the original location. */ + +GC_API int GC_CALL GC_unregister_disappearing_link(void ** /* link */); + /* Undoes a registration by either of the above two */ + /* routines. Returns 0 if link was not actually */ + /* registered (otherwise returns 1). */ + +GC_API int GC_CALL GC_register_long_link(void ** /* link */, + const void * /* obj */) + GC_ATTR_NONNULL(1) GC_ATTR_NONNULL(2); + /* Similar to GC_general_register_disappearing_link but */ + /* *link only gets cleared when obj becomes truly */ + /* inaccessible. An object becomes truly inaccessible */ + /* when it can no longer be resurrected from its */ + /* finalizer (e.g. by assigning itself to a pointer */ + /* traceable from root). This can be used to implement */ + /* long weak pointers easily and safely. */ + +GC_API int GC_CALL GC_move_long_link(void ** /* link */, + void ** /* new_link */) + GC_ATTR_NONNULL(2); + /* Similar to GC_move_disappearing_link but for a link */ + /* previously registered via GC_register_long_link. */ + +GC_API int GC_CALL GC_unregister_long_link(void ** /* link */); + /* Similar to GC_unregister_disappearing_link but for a */ + /* registration by either of the above two routines. */ + +/* Support of toggle-ref style of external memory management */ +/* without hooking up to the host retain/release machinery. */ +/* The idea of toggle-ref is that an external reference to */ +/* an object is kept and it can be either a strong or weak */ +/* reference; a weak reference is used when the external peer */ +/* has no interest in the object, and a strong otherwise. */ +typedef enum { + GC_TOGGLE_REF_DROP, + GC_TOGGLE_REF_STRONG, + GC_TOGGLE_REF_WEAK +} GC_ToggleRefStatus; + +/* The callback is to decide (return) the new state of a given */ +/* object. Invoked by the collector for all objects registered */ +/* for toggle-ref processing. Invoked with the allocation lock */ +/* held (but the "world" is running). */ +typedef GC_ToggleRefStatus (GC_CALLBACK *GC_toggleref_func)(void * /* obj */); + +/* Set (register) a callback that decides the state of a given */ +/* object (by, probably, inspecting its native state). */ +/* The argument may be 0 (means no callback). Both the setter */ +/* and the getter acquire the allocation lock (to avoid data */ +/* races). */ +GC_API void GC_CALL GC_set_toggleref_func(GC_toggleref_func); +GC_API GC_toggleref_func GC_CALL GC_get_toggleref_func(void); + +/* Register a given object for toggle-ref processing. It will */ +/* be stored internally and the toggle-ref callback will be */ +/* invoked on the object until the callback returns */ +/* GC_TOGGLE_REF_DROP or the object is collected. If is_strong */ +/* is true then the object is registered with a strong ref, */ +/* a weak one otherwise. Returns GC_SUCCESS if registration */ +/* succeeded (or no callback registered yet), GC_NO_MEMORY if */ +/* it failed for lack of memory. */ +GC_API int GC_CALL GC_toggleref_add(void * /* obj */, int /* is_strong */) + GC_ATTR_NONNULL(1); + +/* Finalizer callback support. Invoked by the collector (with */ +/* the allocation lock held) for each unreachable object */ +/* enqueued for finalization. */ +typedef void (GC_CALLBACK * GC_await_finalize_proc)(void * /* obj */); +GC_API void GC_CALL GC_set_await_finalize_proc(GC_await_finalize_proc); +GC_API GC_await_finalize_proc GC_CALL GC_get_await_finalize_proc(void); + /* Zero means no callback. The setter */ + /* and getter acquire the lock too. */ + +/* Returns !=0 if GC_invoke_finalizers has something to do. */ +/* Does not use any synchronization. */ +GC_API int GC_CALL GC_should_invoke_finalizers(void); + +GC_API int GC_CALL GC_invoke_finalizers(void); + /* Run finalizers for all objects that are ready to */ + /* be finalized. Return the number of finalizers */ + /* that were run. Normally this is also called */ + /* implicitly during some allocations. If */ + /* GC_finalize_on_demand is nonzero, it must be called */ + /* explicitly. */ + +/* Explicitly tell the collector that an object is reachable */ +/* at a particular program point. This prevents the argument */ +/* pointer from being optimized away, even it is otherwise no */ +/* longer needed. It should have no visible effect in the */ +/* absence of finalizers or disappearing links. But it may be */ +/* needed to prevent finalizers from running while the */ +/* associated external resource is still in use. */ +/* The function is sometimes called keep_alive in other */ +/* settings. */ +#if defined(__GNUC__) && !defined(__INTEL_COMPILER) +# define GC_reachable_here(ptr) \ + __asm__ __volatile__(" " : : "X"(ptr) : "memory") +#else + GC_API void GC_CALL GC_noop1(GC_word); +# define GC_reachable_here(ptr) GC_noop1((GC_word)(ptr)) +#endif + +/* GC_set_warn_proc can be used to redirect or filter warning messages. */ +/* p may not be a NULL pointer. msg is printf format string (arg must */ +/* match the format). Both the setter and the getter acquire the GC */ +/* lock (to avoid data races). In version 7.1 (and before), the setter */ +/* returned the old warn_proc value. */ +typedef void (GC_CALLBACK * GC_warn_proc)(char * /* msg */, + GC_word /* arg */); +GC_API void GC_CALL GC_set_warn_proc(GC_warn_proc /* p */) GC_ATTR_NONNULL(1); +/* GC_get_warn_proc returns the current warn_proc. */ +GC_API GC_warn_proc GC_CALL GC_get_warn_proc(void); + +/* GC_ignore_warn_proc may be used as an argument for GC_set_warn_proc */ +/* to suppress all warnings (unless statistics printing is turned on). */ +GC_API void GC_CALLBACK GC_ignore_warn_proc(char *, GC_word); + +/* Change file descriptor of GC log. Unavailable on some targets. */ +GC_API void GC_CALL GC_set_log_fd(int); + +/* abort_func is invoked on GC fatal aborts (just before OS-dependent */ +/* abort or exit(1) is called). Must be non-NULL. The default one */ +/* outputs msg to stderr provided msg is non-NULL. msg is NULL if */ +/* invoked before exit(1) otherwise msg is non-NULL (i.e., if invoked */ +/* before abort). Both the setter and getter acquire the GC lock. */ +/* Both the setter and getter are defined only if the library has been */ +/* compiled without SMALL_CONFIG. */ +typedef void (GC_CALLBACK * GC_abort_func)(const char * /* msg */); +GC_API void GC_CALL GC_set_abort_func(GC_abort_func) GC_ATTR_NONNULL(1); +GC_API GC_abort_func GC_CALL GC_get_abort_func(void); + +/* The following is intended to be used by a higher level */ +/* (e.g. Java-like) finalization facility. It is expected */ +/* that finalization code will arrange for hidden pointers to */ +/* disappear. Otherwise objects can be accessed after they */ +/* have been collected. */ +/* Note that putting pointers in atomic objects or in */ +/* non-pointer slots of "typed" objects is equivalent to */ +/* disguising them in this way, and may have other advantages. */ +typedef GC_word GC_hidden_pointer; +#define GC_HIDE_POINTER(p) (~(GC_hidden_pointer)(p)) +/* Converting a hidden pointer to a real pointer requires verifying */ +/* that the object still exists. This involves acquiring the */ +/* allocator lock to avoid a race with the collector. */ +#define GC_REVEAL_POINTER(p) ((void *)GC_HIDE_POINTER(p)) + +#if defined(I_HIDE_POINTERS) || defined(GC_I_HIDE_POINTERS) + /* This exists only for compatibility (the GC-prefixed symbols are */ + /* preferred for new code). */ +# define HIDE_POINTER(p) GC_HIDE_POINTER(p) +# define REVEAL_POINTER(p) GC_REVEAL_POINTER(p) +#endif + +typedef void * (GC_CALLBACK * GC_fn_type)(void * /* client_data */); +GC_API void * GC_CALL GC_call_with_alloc_lock(GC_fn_type /* fn */, + void * /* client_data */) GC_ATTR_NONNULL(1); + +/* These routines are intended to explicitly notify the collector */ +/* of new threads. Often this is unnecessary because thread creation */ +/* is implicitly intercepted by the collector, using header-file */ +/* defines, or linker-based interception. In the long run the intent */ +/* is to always make redundant registration safe. In the short run, */ +/* this is being implemented a platform at a time. */ +/* The interface is complicated by the fact that we probably will not */ +/* ever be able to automatically determine the stack base for thread */ +/* stacks on all platforms. */ + +/* Structure representing the base of a thread stack. On most */ +/* platforms this contains just a single address. */ +struct GC_stack_base { + void * mem_base; /* Base of memory stack. */ +# if defined(__ia64) || defined(__ia64__) || defined(_M_IA64) + void * reg_base; /* Base of separate register stack. */ +# endif +}; + +typedef void * (GC_CALLBACK * GC_stack_base_func)( + struct GC_stack_base * /* sb */, void * /* arg */); + +/* Call a function with a stack base structure corresponding to */ +/* somewhere in the GC_call_with_stack_base frame. This often can */ +/* be used to provide a sufficiently accurate stack base. And we */ +/* implement it everywhere. */ +GC_API void * GC_CALL GC_call_with_stack_base(GC_stack_base_func /* fn */, + void * /* arg */) GC_ATTR_NONNULL(1); + +#define GC_SUCCESS 0 +#define GC_DUPLICATE 1 /* Was already registered. */ +#define GC_NO_THREADS 2 /* No thread support in GC. */ + /* GC_NO_THREADS is not returned by any GC function anymore. */ +#define GC_UNIMPLEMENTED 3 /* Not yet implemented on this platform. */ +#define GC_NOT_FOUND 4 /* Requested link not found (returned */ + /* by GC_move_disappearing_link). */ + +#if defined(GC_DARWIN_THREADS) || defined(GC_WIN32_THREADS) + /* Use implicit thread registration and processing (via Win32 DllMain */ + /* or Darwin task_threads). Deprecated. Must be called before */ + /* GC_INIT() and other GC routines. Should be avoided if */ + /* GC_pthread_create, GC_beginthreadex (or GC_CreateThread) could be */ + /* called instead. Disables parallelized GC on Win32. */ + GC_API void GC_CALL GC_use_threads_discovery(void); +#endif + +#ifdef GC_THREADS + /* Suggest the GC to use the specific signal to suspend threads. */ + /* Has no effect after GC_init and on non-POSIX systems. */ + GC_API void GC_CALL GC_set_suspend_signal(int); + + /* Suggest the GC to use the specific signal to resume threads. */ + /* Has no effect after GC_init and on non-POSIX systems. */ + GC_API void GC_CALL GC_set_thr_restart_signal(int); + + /* Return the signal number (constant after initialization) used by */ + /* the GC to suspend threads on POSIX systems. Return -1 otherwise. */ + GC_API int GC_CALL GC_get_suspend_signal(void); + + /* Return the signal number (constant after initialization) used by */ + /* the garbage collector to restart (resume) threads on POSIX */ + /* systems. Return -1 otherwise. */ + GC_API int GC_CALL GC_get_thr_restart_signal(void); + + /* Restart marker threads after POSIX fork in child. Meaningless in */ + /* other situations. Should not be called if fork followed by exec. */ + GC_API void GC_CALL GC_start_mark_threads(void); + + /* Explicitly enable GC_register_my_thread() invocation. */ + /* Done implicitly if a GC thread-creation function is called (or */ + /* implicit thread registration is activated, or the collector is */ + /* compiled with GC_ALWAYS_MULTITHREADED defined). Otherwise, it */ + /* must be called from the main (or any previously registered) thread */ + /* between the collector initialization and the first explicit */ + /* registering of a thread (it should be called as late as possible). */ + GC_API void GC_CALL GC_allow_register_threads(void); + + /* Register the current thread, with the indicated stack base, as */ + /* a new thread whose stack(s) should be traced by the GC. If it */ + /* is not implicitly called by the GC, this must be called before a */ + /* thread can allocate garbage collected memory, or assign pointers */ + /* to the garbage collected heap. Once registered, a thread will be */ + /* stopped during garbage collections. */ + /* This call must be previously enabled (see above). */ + /* This should never be called from the main thread, where it is */ + /* always done implicitly. This is normally done implicitly if GC_ */ + /* functions are called to create the thread, e.g. by including gc.h */ + /* (which redefines some system functions) before calling the system */ + /* thread creation function. Nonetheless, thread cleanup routines */ + /* (e.g., pthread key destructor) typically require manual thread */ + /* registering (and unregistering) if pointers to GC-allocated */ + /* objects are manipulated inside. */ + /* It is also always done implicitly on some platforms if */ + /* GC_use_threads_discovery() is called at start-up. Except for the */ + /* latter case, the explicit call is normally required for threads */ + /* created by third-party libraries. */ + /* A manually registered thread requires manual unregistering. */ + /* Returns GC_SUCCESS on success, GC_DUPLICATE if already registered. */ + GC_API int GC_CALL GC_register_my_thread(const struct GC_stack_base *) + GC_ATTR_NONNULL(1); + + /* Return non-zero (TRUE) if and only if the calling thread is */ + /* registered with the garbage collector. */ + GC_API int GC_CALL GC_thread_is_registered(void); + + /* Notify the collector about the stack and the alt-stack of the */ + /* current thread. stack_start/size is used to determine the stack */ + /* boundaries when a thread is suspended while it is on an alt-stack. */ + GC_API void GC_CALL GC_register_altstack(void * /* stack_start */, + GC_word /* stack_size */, + void * /* altstack_base */, + GC_word /* altstack_size */); + + /* Unregister the current thread. Only an explicitly registered */ + /* thread (i.e. for which GC_register_my_thread() returns GC_SUCCESS) */ + /* is allowed (and required) to call this function. (As a special */ + /* exception, it is also allowed to once unregister the main thread.) */ + /* The thread may no longer allocate garbage collected memory or */ + /* manipulate pointers to the garbage collected heap after making */ + /* this call. Specifically, if it wants to return or otherwise */ + /* communicate a pointer to the garbage-collected heap to another */ + /* thread, it must do this before calling GC_unregister_my_thread, */ + /* most probably by saving it in a global data structure. Must not */ + /* be called inside a GC callback function (except for */ + /* GC_call_with_stack_base() one). */ + GC_API int GC_CALL GC_unregister_my_thread(void); +#endif /* GC_THREADS */ + +/* Wrapper for functions that are likely to block (or, at least, do not */ +/* allocate garbage collected memory and/or manipulate pointers to the */ +/* garbage collected heap) for an appreciable length of time. While fn */ +/* is running, the collector is said to be in the "inactive" state for */ +/* the current thread (this means that the thread is not suspended and */ +/* the thread's stack frames "belonging" to the functions in the */ +/* "inactive" state are not scanned during garbage collections). It is */ +/* allowed for fn to call GC_call_with_gc_active() (even recursively), */ +/* thus temporarily toggling the collector's state back to "active". */ +GC_API void * GC_CALL GC_do_blocking(GC_fn_type /* fn */, + void * /* client_data */) GC_ATTR_NONNULL(1); + +/* Call a function switching to the "active" state of the collector for */ +/* the current thread (i.e. the user function is allowed to call any */ +/* GC function and/or manipulate pointers to the garbage collected */ +/* heap). GC_call_with_gc_active() has the functionality opposite to */ +/* GC_do_blocking() one. It is assumed that the collector is already */ +/* initialized and the current thread is registered. fn may toggle */ +/* the collector thread's state temporarily to "inactive" one by using */ +/* GC_do_blocking. GC_call_with_gc_active() often can be used to */ +/* provide a sufficiently accurate stack base. */ +GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type /* fn */, + void * /* client_data */) GC_ATTR_NONNULL(1); + +/* Attempt to fill in the GC_stack_base structure with the stack base */ +/* for this thread. This appears to be required to implement anything */ +/* like the JNI AttachCurrentThread in an environment in which new */ +/* threads are not automatically registered with the collector. */ +/* It is also unfortunately hard to implement well on many platforms. */ +/* Returns GC_SUCCESS or GC_UNIMPLEMENTED. This function acquires the */ +/* GC lock on some platforms. */ +GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *) + GC_ATTR_NONNULL(1); + +/* The following routines are primarily intended for use with a */ +/* preprocessor which inserts calls to check C pointer arithmetic. */ +/* They indicate failure by invoking the corresponding _print_proc. */ + +/* Check that p and q point to the same object. */ +/* Fail conspicuously if they don't. */ +/* Returns the first argument. */ +/* Succeeds if neither p nor q points to the heap. */ +/* May succeed if both p and q point to between heap objects. */ +GC_API void * GC_CALL GC_same_obj(void * /* p */, void * /* q */); + +/* Checked pointer pre- and post- increment operations. Note that */ +/* the second argument is in units of bytes, not multiples of the */ +/* object size. This should either be invoked from a macro, or the */ +/* call should be automatically generated. */ +GC_API void * GC_CALL GC_pre_incr(void **, ptrdiff_t /* how_much */) + GC_ATTR_NONNULL(1); +GC_API void * GC_CALL GC_post_incr(void **, ptrdiff_t /* how_much */) + GC_ATTR_NONNULL(1); + +/* Check that p is visible */ +/* to the collector as a possibly pointer containing location. */ +/* If it isn't fail conspicuously. */ +/* Returns the argument in all cases. May erroneously succeed */ +/* in hard cases. (This is intended for debugging use with */ +/* untyped allocations. The idea is that it should be possible, though */ +/* slow, to add such a call to all indirect pointer stores.) */ +/* Currently useless for multi-threaded worlds. */ +GC_API void * GC_CALL GC_is_visible(void * /* p */); + +/* Check that if p is a pointer to a heap page, then it points to */ +/* a valid displacement within a heap object. */ +/* Fail conspicuously if this property does not hold. */ +/* Uninteresting with GC_all_interior_pointers. */ +/* Always returns its argument. */ +GC_API void * GC_CALL GC_is_valid_displacement(void * /* p */); + +/* Explicitly dump the GC state. This is most often called from the */ +/* debugger, or by setting the GC_DUMP_REGULARLY environment variable, */ +/* but it may be useful to call it from client code during debugging. */ +/* The current collection number is printed in the header of the dump. */ +/* Acquires the GC lock to avoid data races. */ +/* Defined only if the library has been compiled without NO_DEBUGGING. */ +GC_API void GC_CALL GC_dump(void); + +/* The same as GC_dump but allows to specify the name of dump and does */ +/* not acquire the lock. If name is non-NULL, it is printed to help */ +/* identifying individual dumps. Otherwise the current collection */ +/* number is used as the name. */ +/* Defined only if the library has been compiled without NO_DEBUGGING. */ +GC_API void GC_CALL GC_dump_named(const char * /* name */); + +/* Dump information about each block of every GC memory section. */ +/* Defined only if the library has been compiled without NO_DEBUGGING. */ +GC_API void GC_CALL GC_dump_regions(void); + +/* Dump information about every registered disappearing link and */ +/* finalizable object. */ +/* Defined only if the library has been compiled without NO_DEBUGGING. */ +GC_API void GC_CALL GC_dump_finalization(void); + +/* Safer, but slow, pointer addition. Probably useful mainly with */ +/* a preprocessor. Useful only for heap pointers. */ +/* Only the macros without trailing digits are meant to be used */ +/* by clients. These are designed to model the available C pointer */ +/* arithmetic expressions. */ +/* Even then, these are probably more useful as */ +/* documentation than as part of the API. */ +/* Note that GC_PTR_ADD evaluates the first argument more than once. */ +#if defined(GC_DEBUG) && defined(__GNUC__) +# define GC_PTR_ADD3(x, n, type_of_result) \ + ((type_of_result)GC_same_obj((x)+(n), (x))) +# define GC_PRE_INCR3(x, n, type_of_result) \ + ((type_of_result)GC_pre_incr((void **)(&(x)), (n)*sizeof(*x))) +# define GC_POST_INCR3(x, n, type_of_result) \ + ((type_of_result)GC_post_incr((void **)(&(x)), (n)*sizeof(*x))) +# define GC_PTR_ADD(x, n) GC_PTR_ADD3(x, n, typeof(x)) +# define GC_PRE_INCR(x, n) GC_PRE_INCR3(x, n, typeof(x)) +# define GC_POST_INCR(x) GC_POST_INCR3(x, 1, typeof(x)) +# define GC_POST_DECR(x) GC_POST_INCR3(x, -1, typeof(x)) +#else /* !GC_DEBUG || !__GNUC__ */ + /* We can't do this right without typeof, which ANSI decided was not */ + /* sufficiently useful. Without it we resort to the non-debug version. */ + /* FIXME: This should eventually support C++0x decltype. */ +# define GC_PTR_ADD(x, n) ((x)+(n)) +# define GC_PRE_INCR(x, n) ((x) += (n)) +# define GC_POST_INCR(x) ((x)++) +# define GC_POST_DECR(x) ((x)--) +#endif /* !GC_DEBUG || !__GNUC__ */ + +/* Safer assignment of a pointer to a non-stack location. */ +#ifdef GC_DEBUG +# define GC_PTR_STORE(p, q) \ + (*(void **)GC_is_visible(p) = GC_is_valid_displacement(q)) +#else +# define GC_PTR_STORE(p, q) (*(p) = (q)) +#endif + +/* Functions called to report pointer checking errors */ +GC_API void (GC_CALLBACK * GC_same_obj_print_proc)(void * /* p */, + void * /* q */); +GC_API void (GC_CALLBACK * GC_is_valid_displacement_print_proc)(void *); +GC_API void (GC_CALLBACK * GC_is_visible_print_proc)(void *); + +#ifdef GC_PTHREADS + /* For pthread support, we generally need to intercept a number of */ + /* thread library calls. We do that here by macro defining them. */ +# ifdef __cplusplus + } /* extern "C" */ +# endif +# include "gc_pthread_redirects.h" +# ifdef __cplusplus + extern "C" { +# endif +#endif + +/* This returns a list of objects, linked through their first word. */ +/* Its use can greatly reduce lock contention problems, since the */ +/* allocation lock can be acquired and released many fewer times. */ +GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_many(size_t /* lb */); +#define GC_NEXT(p) (*(void * *)(p)) /* Retrieve the next element */ + /* in returned list. */ + +/* A filter function to control the scanning of dynamic libraries. */ +/* If implemented, called by GC before registering a dynamic library */ +/* (discovered by GC) section as a static data root (called only as */ +/* a last reason not to register). The filename of the library, the */ +/* address and the length of the memory region (section) are passed. */ +/* This routine should return nonzero if that region should be scanned. */ +/* Always called with the allocation lock held. Depending on the */ +/* platform, might be called with the "world" stopped. */ +typedef int (GC_CALLBACK * GC_has_static_roots_func)( + const char * /* dlpi_name */, + void * /* section_start */, + size_t /* section_size */); + +/* Register a new callback (a user-supplied filter) to control the */ +/* scanning of dynamic libraries. Replaces any previously registered */ +/* callback. May be 0 (means no filtering). May be unused on some */ +/* platforms (if the filtering is unimplemented or inappropriate). */ +GC_API void GC_CALL GC_register_has_static_roots_callback( + GC_has_static_roots_func); + +#if !defined(CPPCHECK) && !defined(GC_WINDOWS_H_INCLUDED) && defined(WINAPI) + /* windows.h is included before gc.h */ +# define GC_WINDOWS_H_INCLUDED +#endif + +#if defined(GC_WIN32_THREADS) \ + && (!defined(GC_PTHREADS) || defined(GC_BUILD) \ + || defined(GC_WINDOWS_H_INCLUDED)) + /* Note: for Cygwin and pthreads-win32, this is skipped */ + /* unless windows.h is included before gc.h. */ + +# if !defined(GC_NO_THREAD_DECLS) || defined(GC_BUILD) + +# ifdef __cplusplus + } /* Including windows.h in an extern "C" context no longer works. */ +# endif + +# if !defined(_WIN32_WCE) && !defined(__CEGCC__) +# include /* For _beginthreadex, _endthreadex */ +# endif + +# if defined(GC_BUILD) || !defined(GC_DONT_INCLUDE_WINDOWS_H) +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +# define GC_WINDOWS_H_INCLUDED +# endif + +# ifdef __cplusplus + extern "C" { +# endif + +# ifdef GC_UNDERSCORE_STDCALL + /* Explicitly prefix exported/imported WINAPI (__stdcall) symbols */ + /* with '_' (underscore). Might be useful if MinGW/x86 is used. */ +# define GC_CreateThread _GC_CreateThread +# define GC_ExitThread _GC_ExitThread +# endif + +# ifndef DECLSPEC_NORETURN + /* Typically defined in winnt.h. */ +# ifdef GC_WINDOWS_H_INCLUDED +# define DECLSPEC_NORETURN /* empty */ +# else +# define DECLSPEC_NORETURN __declspec(noreturn) +# endif +# endif + +# if !defined(_UINTPTR_T) && !defined(_UINTPTR_T_DEFINED) \ + && !defined(UINTPTR_MAX) + typedef GC_word GC_uintptr_t; +# else + typedef uintptr_t GC_uintptr_t; +# endif + +# ifdef _WIN64 +# define GC_WIN32_SIZE_T GC_uintptr_t +# elif defined(GC_WINDOWS_H_INCLUDED) +# define GC_WIN32_SIZE_T DWORD +# else +# define GC_WIN32_SIZE_T unsigned long +# endif + +# ifdef GC_INSIDE_DLL + /* Export GC DllMain to be invoked from client DllMain. */ +# ifdef GC_UNDERSCORE_STDCALL +# define GC_DllMain _GC_DllMain +# endif +# ifdef GC_WINDOWS_H_INCLUDED + GC_API BOOL WINAPI GC_DllMain(HINSTANCE /* inst */, + ULONG /* reason */, + LPVOID /* reserved */); +# else + GC_API int __stdcall GC_DllMain(void *, unsigned long, void *); +# endif +# endif /* GC_INSIDE_DLL */ + + /* All threads must be created using GC_CreateThread or */ + /* GC_beginthreadex, or must explicitly call GC_register_my_thread */ + /* (and call GC_unregister_my_thread before thread termination), so */ + /* that they will be recorded in the thread table. For backward */ + /* compatibility, it is possible to build the GC with GC_DLL */ + /* defined, and to call GC_use_threads_discovery. This implicitly */ + /* registers all created threads, but appears to be less robust. */ + /* Currently the collector expects all threads to fall through and */ + /* terminate normally, or call GC_endthreadex() or GC_ExitThread, */ + /* so that the thread is properly unregistered. */ +# ifdef GC_WINDOWS_H_INCLUDED + GC_API HANDLE WINAPI GC_CreateThread( + LPSECURITY_ATTRIBUTES /* lpThreadAttributes */, + GC_WIN32_SIZE_T /* dwStackSize */, + LPTHREAD_START_ROUTINE /* lpStartAddress */, + LPVOID /* lpParameter */, DWORD /* dwCreationFlags */, + LPDWORD /* lpThreadId */); + + GC_API DECLSPEC_NORETURN void WINAPI GC_ExitThread( + DWORD /* dwExitCode */); +# else + struct _SECURITY_ATTRIBUTES; + GC_API void *__stdcall GC_CreateThread(struct _SECURITY_ATTRIBUTES *, + GC_WIN32_SIZE_T, + unsigned long (__stdcall *)(void *), + void *, unsigned long, unsigned long *); + GC_API DECLSPEC_NORETURN void __stdcall GC_ExitThread(unsigned long); +# endif + +# if !defined(_WIN32_WCE) && !defined(__CEGCC__) + GC_API GC_uintptr_t GC_CALL GC_beginthreadex( + void * /* security */, unsigned /* stack_size */, + unsigned (__stdcall *)(void *), + void * /* arglist */, unsigned /* initflag */, + unsigned * /* thrdaddr */); + + /* Note: _endthreadex() is not currently marked as no-return in */ + /* VC++ and MinGW headers, so we don't mark it neither. */ + GC_API void GC_CALL GC_endthreadex(unsigned /* retval */); +# endif /* !_WIN32_WCE */ + +# endif /* !GC_NO_THREAD_DECLS */ + +# ifdef GC_WINMAIN_REDIRECT + /* win32_threads.c implements the real WinMain(), which will start */ + /* a new thread to call GC_WinMain() after initializing the garbage */ + /* collector. */ +# define WinMain GC_WinMain +# endif + + /* For compatibility only. */ +# define GC_use_DllMain GC_use_threads_discovery + +# ifndef GC_NO_THREAD_REDIRECTS +# define CreateThread GC_CreateThread +# define ExitThread GC_ExitThread +# undef _beginthreadex +# define _beginthreadex GC_beginthreadex +# undef _endthreadex +# define _endthreadex GC_endthreadex +/* #define _beginthread { > "Please use _beginthreadex instead of _beginthread" < } */ +# endif /* !GC_NO_THREAD_REDIRECTS */ + +#endif /* GC_WIN32_THREADS */ + +/* Public setter and getter for switching "unmap as much as possible" */ +/* mode on(1) and off(0). Has no effect unless unmapping is turned on. */ +/* Has no effect on implicitly-initiated garbage collections. Initial */ +/* value is controlled by GC_FORCE_UNMAP_ON_GCOLLECT. The setter and */ +/* getter are unsynchronized. */ +GC_API void GC_CALL GC_set_force_unmap_on_gcollect(int); +GC_API int GC_CALL GC_get_force_unmap_on_gcollect(void); + +/* Fully portable code should call GC_INIT() from the main program */ +/* before making any other GC_ calls. On most platforms this is a */ +/* no-op and the collector self-initializes. But a number of */ +/* platforms make that too hard. */ +/* A GC_INIT call is required if the collector is built with */ +/* THREAD_LOCAL_ALLOC defined and the initial allocation call is not */ +/* to GC_malloc() or GC_malloc_atomic(). */ + +#if defined(__CYGWIN32__) || defined(__CYGWIN__) + /* Similarly gnu-win32 DLLs need explicit initialization from the */ + /* main program, as does AIX. */ +# ifdef __x86_64__ + /* Cygwin/x64 does not add leading underscore to symbols anymore. */ + extern int __data_start__[], __data_end__[]; + extern int __bss_start__[], __bss_end__[]; +# define GC_DATASTART ((GC_word)__data_start__ < (GC_word)__bss_start__ \ + ? (void *)__data_start__ : (void *)__bss_start__) +# define GC_DATAEND ((GC_word)__data_end__ > (GC_word)__bss_end__ \ + ? (void *)__data_end__ : (void *)__bss_end__) +# else + extern int _data_start__[], _data_end__[], _bss_start__[], _bss_end__[]; +# define GC_DATASTART ((GC_word)_data_start__ < (GC_word)_bss_start__ \ + ? (void *)_data_start__ : (void *)_bss_start__) +# define GC_DATAEND ((GC_word)_data_end__ > (GC_word)_bss_end__ \ + ? (void *)_data_end__ : (void *)_bss_end__) +# endif /* !__x86_64__ */ +# define GC_INIT_CONF_ROOTS GC_add_roots(GC_DATASTART, GC_DATAEND); \ + GC_gcollect() /* For blacklisting. */ + /* Required at least if GC is in a DLL. And doesn't hurt. */ +#elif defined(_AIX) + extern int _data[], _end[]; +# define GC_DATASTART ((void *)((ulong)_data)) +# define GC_DATAEND ((void *)((ulong)_end)) +# define GC_INIT_CONF_ROOTS GC_add_roots(GC_DATASTART, GC_DATAEND) +#elif (defined(HOST_ANDROID) || defined(__ANDROID__)) \ + && !defined(GC_NOT_DLL) && defined(IGNORE_DYNAMIC_LOADING) + /* It causes the entire binary section of memory be pushed as a root. */ + /* This might be a bad idea though because on some Android devices */ + /* some of the binary data might become unmapped thus causing SIGSEGV */ + /* with code SEGV_MAPERR. */ +# pragma weak _etext +# pragma weak __data_start +# pragma weak __dso_handle + extern int _etext[], __data_start[], __dso_handle[]; +# pragma weak __end__ + extern int __end__[], _end[]; + /* Explicitly register caller static data roots. Workaround for */ + /* __data_start: NDK "gold" linker might miss it or place it */ + /* incorrectly, __dso_handle is an alternative data start reference. */ + /* Workaround for _end: NDK Clang 3.5+ does not place it at correct */ + /* offset (as of NDK r10e) but "bfd" linker provides __end__ symbol */ + /* that could be used instead. */ +# define GC_INIT_CONF_ROOTS \ + (void)((GC_word)__data_start < (GC_word)_etext \ + && (GC_word)_etext < (GC_word)__dso_handle \ + ? (__end__ != 0 \ + ? (GC_add_roots(__dso_handle, __end__), 0) \ + : (GC_word)__dso_handle < (GC_word)_end \ + ? (GC_add_roots(__dso_handle, _end), 0) : 0) \ + : __data_start != 0 ? (__end__ != 0 \ + ? (GC_add_roots(__data_start, __end__), 0) \ + : (GC_word)__data_start < (GC_word)_end \ + ? (GC_add_roots(__data_start, _end), 0) : 0) : 0) +#else +# define GC_INIT_CONF_ROOTS /* empty */ +#endif + +#ifdef GC_DONT_EXPAND + /* Set GC_dont_expand to TRUE at start-up */ +# define GC_INIT_CONF_DONT_EXPAND GC_set_dont_expand(1) +#else +# define GC_INIT_CONF_DONT_EXPAND /* empty */ +#endif + +#ifdef GC_FORCE_UNMAP_ON_GCOLLECT + /* Turn on "unmap as much as possible on explicit GC" mode at start-up */ +# define GC_INIT_CONF_FORCE_UNMAP_ON_GCOLLECT \ + GC_set_force_unmap_on_gcollect(1) +#else +# define GC_INIT_CONF_FORCE_UNMAP_ON_GCOLLECT /* empty */ +#endif + +#ifdef GC_DONT_GC + /* This is for debugging only (useful if environment variables are */ + /* unsupported); cannot call GC_disable as goes before GC_init. */ +# define GC_INIT_CONF_MAX_RETRIES (void)(GC_dont_gc = 1) +#elif defined(GC_MAX_RETRIES) && !defined(CPPCHECK) + /* Set GC_max_retries to the desired value at start-up */ +# define GC_INIT_CONF_MAX_RETRIES GC_set_max_retries(GC_MAX_RETRIES) +#else +# define GC_INIT_CONF_MAX_RETRIES /* empty */ +#endif + +#if defined(GC_FREE_SPACE_DIVISOR) && !defined(CPPCHECK) + /* Set GC_free_space_divisor to the desired value at start-up */ +# define GC_INIT_CONF_FREE_SPACE_DIVISOR \ + GC_set_free_space_divisor(GC_FREE_SPACE_DIVISOR) +#else +# define GC_INIT_CONF_FREE_SPACE_DIVISOR /* empty */ +#endif + +#if defined(GC_FULL_FREQ) && !defined(CPPCHECK) + /* Set GC_full_freq to the desired value at start-up */ +# define GC_INIT_CONF_FULL_FREQ GC_set_full_freq(GC_FULL_FREQ) +#else +# define GC_INIT_CONF_FULL_FREQ /* empty */ +#endif + +#if defined(GC_TIME_LIMIT) && !defined(CPPCHECK) + /* Set GC_time_limit to the desired value at start-up */ +# define GC_INIT_CONF_TIME_LIMIT GC_set_time_limit(GC_TIME_LIMIT) +#else +# define GC_INIT_CONF_TIME_LIMIT /* empty */ +#endif + +#if defined(GC_SIG_SUSPEND) && defined(GC_THREADS) && !defined(CPPCHECK) +# define GC_INIT_CONF_SUSPEND_SIGNAL GC_set_suspend_signal(GC_SIG_SUSPEND) +#else +# define GC_INIT_CONF_SUSPEND_SIGNAL /* empty */ +#endif + +#if defined(GC_SIG_THR_RESTART) && defined(GC_THREADS) && !defined(CPPCHECK) +# define GC_INIT_CONF_THR_RESTART_SIGNAL \ + GC_set_thr_restart_signal(GC_SIG_THR_RESTART) +#else +# define GC_INIT_CONF_THR_RESTART_SIGNAL /* empty */ +#endif + +#if defined(GC_MAXIMUM_HEAP_SIZE) && !defined(CPPCHECK) + /* Limit the heap size to the desired value (useful for debugging). */ + /* The limit could be overridden either at the program start-up by */ + /* the similar environment variable or anytime later by the */ + /* corresponding API function call. */ +# define GC_INIT_CONF_MAXIMUM_HEAP_SIZE \ + GC_set_max_heap_size(GC_MAXIMUM_HEAP_SIZE) +#else +# define GC_INIT_CONF_MAXIMUM_HEAP_SIZE /* empty */ +#endif + +#ifdef GC_IGNORE_WARN + /* Turn off all warnings at start-up (after GC initialization) */ +# define GC_INIT_CONF_IGNORE_WARN GC_set_warn_proc(GC_ignore_warn_proc) +#else +# define GC_INIT_CONF_IGNORE_WARN /* empty */ +#endif + +#if defined(GC_INITIAL_HEAP_SIZE) && !defined(CPPCHECK) + /* Set heap size to the desired value at start-up */ +# define GC_INIT_CONF_INITIAL_HEAP_SIZE \ + { size_t heap_size = GC_get_heap_size(); \ + if (heap_size < (GC_INITIAL_HEAP_SIZE)) \ + (void)GC_expand_hp((GC_INITIAL_HEAP_SIZE) - heap_size); } +#else +# define GC_INIT_CONF_INITIAL_HEAP_SIZE /* empty */ +#endif + +/* Portable clients should call this at the program start-up. More */ +/* over, some platforms require this call to be done strictly from the */ +/* primordial thread. */ +#define GC_INIT() { GC_INIT_CONF_DONT_EXPAND; /* pre-init */ \ + GC_INIT_CONF_FORCE_UNMAP_ON_GCOLLECT; \ + GC_INIT_CONF_MAX_RETRIES; \ + GC_INIT_CONF_FREE_SPACE_DIVISOR; \ + GC_INIT_CONF_FULL_FREQ; \ + GC_INIT_CONF_TIME_LIMIT; \ + GC_INIT_CONF_SUSPEND_SIGNAL; \ + GC_INIT_CONF_THR_RESTART_SIGNAL; \ + GC_INIT_CONF_MAXIMUM_HEAP_SIZE; \ + GC_init(); /* real GC initialization */ \ + GC_INIT_CONF_ROOTS; /* post-init */ \ + GC_INIT_CONF_IGNORE_WARN; \ + GC_INIT_CONF_INITIAL_HEAP_SIZE; } + +/* win32S may not free all resources on process exit. */ +/* This explicitly deallocates the heap. */ +GC_API void GC_CALL GC_win32_free_heap(void); + +#if defined(__SYMBIAN32__) + void GC_init_global_static_roots(void); +#endif + +#if defined(_AMIGA) && !defined(GC_AMIGA_MAKINGLIB) + /* Allocation really goes through GC_amiga_allocwrapper_do. */ + void *GC_amiga_realloc(void *, size_t); +# define GC_realloc(a,b) GC_amiga_realloc(a,b) + void GC_amiga_set_toany(void (*)(void)); + extern int GC_amiga_free_space_divisor_inc; + extern void *(*GC_amiga_allocwrapper_do)(size_t, void *(GC_CALL *)(size_t)); +# define GC_malloc(a) \ + (*GC_amiga_allocwrapper_do)(a,GC_malloc) +# define GC_malloc_atomic(a) \ + (*GC_amiga_allocwrapper_do)(a,GC_malloc_atomic) +# define GC_malloc_uncollectable(a) \ + (*GC_amiga_allocwrapper_do)(a,GC_malloc_uncollectable) +# define GC_malloc_atomic_uncollectable(a) \ + (*GC_amiga_allocwrapper_do)(a,GC_malloc_atomic_uncollectable) +# define GC_malloc_ignore_off_page(a) \ + (*GC_amiga_allocwrapper_do)(a,GC_malloc_ignore_off_page) +# define GC_malloc_atomic_ignore_off_page(a) \ + (*GC_amiga_allocwrapper_do)(a,GC_malloc_atomic_ignore_off_page) +#endif /* _AMIGA && !GC_AMIGA_MAKINGLIB */ + +/* Unity specific APIs */ +GC_API void GC_CALL GC_stop_world_external(); +GC_API void GC_CALL GC_start_world_external(); + +GC_API void GC_CALL GC_disable_incremental(void); + +/* APIs for getting access to raw GC heap */ +/* These are NOT thread safe, so should be called with GC lock held */ +typedef void (*GC_heap_section_proc)(void* user_data, GC_PTR start, GC_PTR end); +GC_API void GC_foreach_heap_section(void* user_data, GC_heap_section_proc callback); +GC_API GC_word GC_get_heap_section_count(); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* GC_H */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_alloc_ptrs.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_alloc_ptrs.h new file mode 100644 index 000000000..4d559e5f5 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_alloc_ptrs.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 1996-1998 by Silicon Graphics. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* This file should never be included by clients directly. */ + +#ifndef GC_ALLOC_PTRS_H +#define GC_ALLOC_PTRS_H + +#include "gc.h" + +#ifdef __cplusplus + extern "C" { +#endif + +GC_API void ** const GC_objfreelist_ptr; +GC_API void ** const GC_aobjfreelist_ptr; +GC_API void ** const GC_uobjfreelist_ptr; + +#ifdef GC_ATOMIC_UNCOLLECTABLE + GC_API void ** const GC_auobjfreelist_ptr; +#endif + +GC_API void GC_CALL GC_incr_bytes_allocd(size_t bytes); +GC_API void GC_CALL GC_incr_bytes_freed(size_t bytes); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* GC_ALLOC_PTRS_H */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_allocator.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_allocator.h new file mode 100644 index 000000000..9d70ed5ad --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_allocator.h @@ -0,0 +1,328 @@ +/* + * Copyright (c) 1996-1997 + * Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Silicon Graphics makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * Copyright (c) 2002 + * Hewlett-Packard Company + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Hewlett-Packard Company makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + */ + +/* + * This implements standard-conforming allocators that interact with + * the garbage collector. Gc_allocator allocates garbage-collectible + * objects of type T. Traceable_allocator allocates objects that + * are not themselves garbage collected, but are scanned by the + * collector for pointers to collectible objects. Traceable_alloc + * should be used for explicitly managed STL containers that may + * point to collectible objects. + * + * This code was derived from an earlier version of the GNU C++ standard + * library, which itself was derived from the SGI STL implementation. + * + * Ignore-off-page allocator: George T. Talbot + */ + +#ifndef GC_ALLOCATOR_H + +#define GC_ALLOCATOR_H + +#include "gc.h" +#include // for placement new + +#ifndef GC_ATTR_EXPLICIT +# if (__cplusplus >= 201103L) || defined(CPPCHECK) +# define GC_ATTR_EXPLICIT explicit +# else +# define GC_ATTR_EXPLICIT /* empty */ +# endif +#endif + +/* First some helpers to allow us to dispatch on whether or not a type + * is known to be pointer-free. + * These are private, except that the client may invoke the + * GC_DECLARE_PTRFREE macro. + */ + +struct GC_true_type {}; +struct GC_false_type {}; + +template +struct GC_type_traits { + GC_false_type GC_is_ptr_free; +}; + +# define GC_DECLARE_PTRFREE(T) \ +template<> struct GC_type_traits { GC_true_type GC_is_ptr_free; } + +GC_DECLARE_PTRFREE(char); +GC_DECLARE_PTRFREE(signed char); +GC_DECLARE_PTRFREE(unsigned char); +GC_DECLARE_PTRFREE(signed short); +GC_DECLARE_PTRFREE(unsigned short); +GC_DECLARE_PTRFREE(signed int); +GC_DECLARE_PTRFREE(unsigned int); +GC_DECLARE_PTRFREE(signed long); +GC_DECLARE_PTRFREE(unsigned long); +GC_DECLARE_PTRFREE(float); +GC_DECLARE_PTRFREE(double); +GC_DECLARE_PTRFREE(long double); +/* The client may want to add others. */ + +// In the following GC_Tp is GC_true_type if we are allocating a +// pointer-free object. +template +inline void * GC_selective_alloc(size_t n, GC_Tp, bool ignore_off_page) { + return ignore_off_page?GC_MALLOC_IGNORE_OFF_PAGE(n):GC_MALLOC(n); +} + +template <> +inline void * GC_selective_alloc(size_t n, GC_true_type, + bool ignore_off_page) { + return ignore_off_page? GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE(n) + : GC_MALLOC_ATOMIC(n); +} + +/* Now the public gc_allocator class: + */ +template +class gc_allocator { +public: + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef GC_Tp* pointer; + typedef const GC_Tp* const_pointer; + typedef GC_Tp& reference; + typedef const GC_Tp& const_reference; + typedef GC_Tp value_type; + + template struct rebind { + typedef gc_allocator other; + }; + + gc_allocator() {} + gc_allocator(const gc_allocator&) throw() {} +# if !(GC_NO_MEMBER_TEMPLATES || 0 < _MSC_VER && _MSC_VER <= 1200) + // MSVC++ 6.0 do not support member templates + template GC_ATTR_EXPLICIT + gc_allocator(const gc_allocator&) throw() {} +# endif + ~gc_allocator() throw() {} + + pointer address(reference GC_x) const { return &GC_x; } + const_pointer address(const_reference GC_x) const { return &GC_x; } + + // GC_n is permitted to be 0. The C++ standard says nothing about what + // the return value is when GC_n == 0. + GC_Tp* allocate(size_type GC_n, const void* = 0) { + GC_type_traits traits; + return static_cast + (GC_selective_alloc(GC_n * sizeof(GC_Tp), + traits.GC_is_ptr_free, false)); + } + + // __p is not permitted to be a null pointer. + void deallocate(pointer __p, size_type /* GC_n */) + { GC_FREE(__p); } + + size_type max_size() const throw() + { return size_t(-1) / sizeof(GC_Tp); } + + void construct(pointer __p, const GC_Tp& __val) { new(__p) GC_Tp(__val); } + void destroy(pointer __p) { __p->~GC_Tp(); } +}; + +template<> +class gc_allocator { + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef void* pointer; + typedef const void* const_pointer; + typedef void value_type; + + template struct rebind { + typedef gc_allocator other; + }; +}; + + +template +inline bool operator==(const gc_allocator&, const gc_allocator&) +{ + return true; +} + +template +inline bool operator!=(const gc_allocator&, const gc_allocator&) +{ + return false; +} + + +/* Now the public gc_allocator_ignore_off_page class: + */ +template +class gc_allocator_ignore_off_page { +public: + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef GC_Tp* pointer; + typedef const GC_Tp* const_pointer; + typedef GC_Tp& reference; + typedef const GC_Tp& const_reference; + typedef GC_Tp value_type; + + template struct rebind { + typedef gc_allocator_ignore_off_page other; + }; + + gc_allocator_ignore_off_page() {} + gc_allocator_ignore_off_page(const gc_allocator_ignore_off_page&) throw() {} +# if !(GC_NO_MEMBER_TEMPLATES || 0 < _MSC_VER && _MSC_VER <= 1200) + // MSVC++ 6.0 do not support member templates + template GC_ATTR_EXPLICIT + gc_allocator_ignore_off_page(const gc_allocator_ignore_off_page&) + throw() {} +# endif + ~gc_allocator_ignore_off_page() throw() {} + + pointer address(reference GC_x) const { return &GC_x; } + const_pointer address(const_reference GC_x) const { return &GC_x; } + + // GC_n is permitted to be 0. The C++ standard says nothing about what + // the return value is when GC_n == 0. + GC_Tp* allocate(size_type GC_n, const void* = 0) { + GC_type_traits traits; + return static_cast + (GC_selective_alloc(GC_n * sizeof(GC_Tp), + traits.GC_is_ptr_free, true)); + } + + // __p is not permitted to be a null pointer. + void deallocate(pointer __p, size_type /* GC_n */) + { GC_FREE(__p); } + + size_type max_size() const throw() + { return size_t(-1) / sizeof(GC_Tp); } + + void construct(pointer __p, const GC_Tp& __val) { new(__p) GC_Tp(__val); } + void destroy(pointer __p) { __p->~GC_Tp(); } +}; + +template<> +class gc_allocator_ignore_off_page { + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef void* pointer; + typedef const void* const_pointer; + typedef void value_type; + + template struct rebind { + typedef gc_allocator_ignore_off_page other; + }; +}; + +template +inline bool operator==(const gc_allocator_ignore_off_page&, const gc_allocator_ignore_off_page&) +{ + return true; +} + +template +inline bool operator!=(const gc_allocator_ignore_off_page&, const gc_allocator_ignore_off_page&) +{ + return false; +} + +/* + * And the public traceable_allocator class. + */ + +// Note that we currently don't specialize the pointer-free case, since a +// pointer-free traceable container doesn't make that much sense, +// though it could become an issue due to abstraction boundaries. +template +class traceable_allocator { +public: + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef GC_Tp* pointer; + typedef const GC_Tp* const_pointer; + typedef GC_Tp& reference; + typedef const GC_Tp& const_reference; + typedef GC_Tp value_type; + + template struct rebind { + typedef traceable_allocator other; + }; + + traceable_allocator() throw() {} + traceable_allocator(const traceable_allocator&) throw() {} +# if !(GC_NO_MEMBER_TEMPLATES || 0 < _MSC_VER && _MSC_VER <= 1200) + // MSVC++ 6.0 do not support member templates + template GC_ATTR_EXPLICIT + traceable_allocator(const traceable_allocator&) throw() {} +# endif + ~traceable_allocator() throw() {} + + pointer address(reference GC_x) const { return &GC_x; } + const_pointer address(const_reference GC_x) const { return &GC_x; } + + // GC_n is permitted to be 0. The C++ standard says nothing about what + // the return value is when GC_n == 0. + GC_Tp* allocate(size_type GC_n, const void* = 0) { + return static_cast(GC_MALLOC_UNCOLLECTABLE(GC_n * sizeof(GC_Tp))); + } + + // __p is not permitted to be a null pointer. + void deallocate(pointer __p, size_type /* GC_n */) + { GC_FREE(__p); } + + size_type max_size() const throw() + { return size_t(-1) / sizeof(GC_Tp); } + + void construct(pointer __p, const GC_Tp& __val) { new(__p) GC_Tp(__val); } + void destroy(pointer __p) { __p->~GC_Tp(); } +}; + +template<> +class traceable_allocator { + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef void* pointer; + typedef const void* const_pointer; + typedef void value_type; + + template struct rebind { + typedef traceable_allocator other; + }; +}; + + +template +inline bool operator==(const traceable_allocator&, const traceable_allocator&) +{ + return true; +} + +template +inline bool operator!=(const traceable_allocator&, const traceable_allocator&) +{ + return false; +} + +#endif /* GC_ALLOCATOR_H */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_backptr.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_backptr.h new file mode 100644 index 000000000..5fd919592 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_backptr.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2009 by Hewlett-Packard Development Company. + * All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* + * This is a simple API to implement pointer back tracing, i.e. + * to answer questions such as "who is pointing to this" or + * "why is this object being retained by the collector" + * + * Most of these calls yield useful information on only after + * a garbage collection. Usually the client will first force + * a full collection and then gather information, preferably + * before much intervening allocation. + * + * The implementation of the interface is only about 99.9999% + * correct. It is intended to be good enough for profiling, + * but is not intended to be used with production code. + * + * Results are likely to be much more useful if all allocation is + * accomplished through the debugging allocators. + * + * The implementation idea is due to A. Demers. + */ + +#ifndef GC_BACKPTR_H +#define GC_BACKPTR_H + +#ifndef GC_H +# include "gc.h" +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +/* Store information about the object referencing dest in *base_p */ +/* and *offset_p. */ +/* If multiple objects or roots point to dest, the one reported */ +/* will be the last on used by the garbage collector to trace the */ +/* object. */ +/* source is root ==> *base_p = address, *offset_p = 0 */ +/* source is heap object ==> *base_p != 0, *offset_p = offset */ +/* Returns 1 on success, 0 if source couldn't be determined. */ +/* Dest can be any address within a heap object. */ +typedef enum { + GC_UNREFERENCED, /* No reference info available. */ + GC_NO_SPACE, /* Dest not allocated with debug alloc. */ + GC_REFD_FROM_ROOT, /* Referenced directly by root *base_p. */ + GC_REFD_FROM_REG, /* Referenced from a register, i.e. */ + /* a root without an address. */ + GC_REFD_FROM_HEAP, /* Referenced from another heap obj. */ + GC_FINALIZER_REFD /* Finalizable and hence accessible. */ +} GC_ref_kind; + +GC_API GC_ref_kind GC_CALL GC_get_back_ptr_info(void * /* dest */, + void ** /* base_p */, size_t * /* offset_p */) + GC_ATTR_NONNULL(1); + +/* Generate a random heap address. */ +/* The resulting address is in the heap, but */ +/* not necessarily inside a valid object. */ +GC_API void * GC_CALL GC_generate_random_heap_address(void); + +/* Generate a random address inside a valid marked heap object. */ +GC_API void * GC_CALL GC_generate_random_valid_address(void); + +/* Force a garbage collection and generate a backtrace from a */ +/* random heap address. */ +/* This uses the GC logging mechanism (GC_printf) to produce */ +/* output. It can often be called from a debugger. The */ +/* source in dbg_mlc.c also serves as a sample client. */ +GC_API void GC_CALL GC_generate_random_backtrace(void); + +/* Print a backtrace from a specific address. Used by the */ +/* above. The client should call GC_gcollect() immediately */ +/* before invocation. */ +GC_API void GC_CALL GC_print_backtrace(void *) GC_ATTR_NONNULL(1); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* GC_BACKPTR_H */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_config_macros.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_config_macros.h new file mode 100644 index 000000000..a043db8dd --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_config_macros.h @@ -0,0 +1,408 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2009 by Hewlett-Packard Development Company. + * All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* This should never be included directly; it is included only from gc.h. */ +/* We separate it only to make gc.h more suitable as documentation. */ +#if defined(GC_H) + +/* Convenient internal macro to test version of GCC. */ +#if defined(__GNUC__) && defined(__GNUC_MINOR__) +# define GC_GNUC_PREREQ(major, minor) \ + ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((major) << 16) + (minor)) +#else +# define GC_GNUC_PREREQ(major, minor) 0 /* FALSE */ +#endif + +/* Some tests for old macros. These violate our namespace rules and */ +/* will disappear shortly. Use the GC_ names. */ +#if defined(SOLARIS_THREADS) || defined(_SOLARIS_THREADS) \ + || defined(_SOLARIS_PTHREADS) || defined(GC_SOLARIS_PTHREADS) + /* We no longer support old style Solaris threads. */ + /* GC_SOLARIS_THREADS now means pthreads. */ +# ifndef GC_SOLARIS_THREADS +# define GC_SOLARIS_THREADS +# endif +#endif +#if defined(IRIX_THREADS) +# define GC_IRIX_THREADS +#endif +#if defined(DGUX_THREADS) && !defined(GC_DGUX386_THREADS) +# define GC_DGUX386_THREADS +#endif +#if defined(AIX_THREADS) +# define GC_AIX_THREADS +#endif +#if defined(HPUX_THREADS) +# define GC_HPUX_THREADS +#endif +#if defined(OSF1_THREADS) +# define GC_OSF1_THREADS +#endif +#if defined(LINUX_THREADS) +# define GC_LINUX_THREADS +#endif +#if defined(WIN32_THREADS) +# define GC_WIN32_THREADS +#endif +#if defined(RTEMS_THREADS) +# define GC_RTEMS_PTHREADS +#endif +#if defined(USE_LD_WRAP) +# define GC_USE_LD_WRAP +#endif + +#if defined(GC_WIN32_PTHREADS) && !defined(GC_WIN32_THREADS) + /* Using pthreads-win32 library (or other Win32 implementation). */ +# define GC_WIN32_THREADS +#endif + +#if defined(GC_AIX_THREADS) || defined(GC_DARWIN_THREADS) \ + || defined(GC_DGUX386_THREADS) || defined(GC_FREEBSD_THREADS) \ + || defined(GC_HPUX_THREADS) \ + || defined(GC_IRIX_THREADS) || defined(GC_LINUX_THREADS) \ + || defined(GC_NETBSD_THREADS) || defined(GC_OPENBSD_THREADS) \ + || defined(GC_OSF1_THREADS) || defined(GC_SOLARIS_THREADS) \ + || defined(GC_WIN32_THREADS) || defined(GC_RTEMS_PTHREADS) \ + || defined(SN_TARGET_ORBIS) || defined(SN_TARGET_PSP2) +# ifndef GC_THREADS +# define GC_THREADS +# endif +#elif defined(GC_THREADS) +# if defined(__linux__) +# define GC_LINUX_THREADS +# elif defined(_PA_RISC1_1) || defined(_PA_RISC2_0) || defined(hppa) \ + || defined(__HPPA) || (defined(__ia64) && defined(_HPUX_SOURCE)) +# define GC_HPUX_THREADS +# elif defined(__HAIKU__) +# define GC_HAIKU_THREADS +# elif defined(__OpenBSD__) +# define GC_OPENBSD_THREADS +# elif defined(__DragonFly__) || defined(__FreeBSD_kernel__) \ + || (defined(__FreeBSD__) && !defined(SN_TARGET_ORBIS)) +# define GC_FREEBSD_THREADS +# elif defined(__NetBSD__) +# define GC_NETBSD_THREADS +# elif defined(__alpha) || defined(__alpha__) /* && !Linux && !xBSD */ +# define GC_OSF1_THREADS +# elif (defined(mips) || defined(__mips) || defined(_mips)) \ + && !(defined(nec_ews) || defined(_nec_ews) \ + || defined(ultrix) || defined(__ultrix)) +# define GC_IRIX_THREADS +# elif defined(__sparc) /* && !Linux */ \ + || ((defined(sun) || defined(__sun)) \ + && (defined(i386) || defined(__i386__) \ + || defined(__amd64) || defined(__amd64__))) +# define GC_SOLARIS_THREADS +# elif defined(__APPLE__) && defined(__MACH__) +# define GC_DARWIN_THREADS +# endif +# if defined(DGUX) && (defined(i386) || defined(__i386__)) +# define GC_DGUX386_THREADS +# endif +# if defined(_AIX) +# define GC_AIX_THREADS +# endif +# if (defined(_WIN32) || defined(_MSC_VER) || defined(__BORLANDC__) \ + || defined(__CYGWIN32__) || defined(__CYGWIN__) || defined(__CEGCC__) \ + || defined(_WIN32_WCE) || defined(__MINGW32__)) \ + && !defined(GC_WIN32_THREADS) + /* Either posix or native Win32 threads. */ +# define GC_WIN32_THREADS +# endif +# if defined(__rtems__) && (defined(i386) || defined(__i386__)) +# define GC_RTEMS_PTHREADS +# endif +#endif /* GC_THREADS */ + +#undef GC_PTHREADS +#if (!defined(GC_WIN32_THREADS) || defined(GC_WIN32_PTHREADS) \ + || defined(__CYGWIN32__) || defined(__CYGWIN__)) && defined(GC_THREADS) \ + && !defined(NN_PLATFORM_CTR) && !defined(NN_BUILD_TARGET_PLATFORM_NX) + /* Posix threads. */ +# define GC_PTHREADS +#endif + +#if !defined(_PTHREADS) && defined(GC_NETBSD_THREADS) +# define _PTHREADS +#endif + +#if defined(GC_DGUX386_THREADS) && !defined(_POSIX4A_DRAFT10_SOURCE) +# define _POSIX4A_DRAFT10_SOURCE 1 +#endif + +#if !defined(_REENTRANT) && defined(GC_PTHREADS) && !defined(GC_WIN32_THREADS) + /* Better late than never. This fails if system headers that depend */ + /* on this were previously included. */ +# define _REENTRANT +#endif + +#define __GC +#if !defined(_WIN32_WCE) || defined(__GNUC__) +# include +# if defined(__MINGW32__) && !defined(_WIN32_WCE) +# include + /* We mention uintptr_t. */ + /* Perhaps this should be included in pure msft environments */ + /* as well? */ +# endif +#else /* _WIN32_WCE */ + /* Yet more kludges for WinCE. */ +# include /* size_t is defined here */ +# ifndef _PTRDIFF_T_DEFINED + /* ptrdiff_t is not defined */ +# define _PTRDIFF_T_DEFINED + typedef long ptrdiff_t; +# endif +#endif /* _WIN32_WCE */ + +#if !defined(GC_NOT_DLL) && !defined(GC_DLL) \ + && ((defined(_DLL) && !defined(__GNUC__)) \ + || (defined(DLL_EXPORT) && defined(GC_BUILD))) +# define GC_DLL +#endif + +#if defined(GC_DLL) && !defined(GC_API) + +# if defined(__MINGW32__) || defined(__CEGCC__) +# if defined(GC_BUILD) || defined(__MINGW32_DELAY_LOAD__) +# define GC_API __declspec(dllexport) +# else +# define GC_API __declspec(dllimport) +# endif + +# elif defined(_MSC_VER) || defined(__DMC__) || defined(__BORLANDC__) \ + || defined(__CYGWIN__) +# ifdef GC_BUILD +# define GC_API extern __declspec(dllexport) +# else +# define GC_API __declspec(dllimport) +# endif + +# elif defined(__WATCOMC__) +# ifdef GC_BUILD +# define GC_API extern __declspec(dllexport) +# else +# define GC_API extern __declspec(dllimport) +# endif + +# elif defined(__SYMBIAN32__) +# ifdef GC_BUILD +# define GC_API extern EXPORT_C +# else +# define GC_API extern IMPORT_C +# endif + +# elif defined(__GNUC__) + /* Only matters if used in conjunction with -fvisibility=hidden option. */ +# if defined(GC_BUILD) && !defined(GC_NO_VISIBILITY) \ + && (GC_GNUC_PREREQ(4, 0) || defined(GC_VISIBILITY_HIDDEN_SET)) +# define GC_API extern __attribute__((__visibility__("default"))) +# endif +# endif +#endif /* GC_DLL */ + +#ifndef GC_API +# define GC_API extern +#endif + +#ifndef GC_CALL +# define GC_CALL +#endif + +#ifndef GC_CALLBACK +# define GC_CALLBACK GC_CALL +#endif + +#ifndef GC_ATTR_MALLOC + /* 'malloc' attribute should be used for all malloc-like functions */ + /* (to tell the compiler that a function may be treated as if any */ + /* non-NULL pointer it returns cannot alias any other pointer valid */ + /* when the function returns). If the client code violates this rule */ + /* by using custom GC_oom_func then define GC_OOM_FUNC_RETURNS_ALIAS. */ +# ifdef GC_OOM_FUNC_RETURNS_ALIAS +# define GC_ATTR_MALLOC /* empty */ +# elif GC_GNUC_PREREQ(3, 1) +# define GC_ATTR_MALLOC __attribute__((__malloc__)) +# elif defined(_MSC_VER) && (_MSC_VER >= 1900) && !defined(__EDG__) +# define GC_ATTR_MALLOC \ + __declspec(allocator) __declspec(noalias) __declspec(restrict) +# elif defined(_MSC_VER) && _MSC_VER >= 1400 +# define GC_ATTR_MALLOC __declspec(noalias) __declspec(restrict) +# else +# define GC_ATTR_MALLOC +# endif +#endif + +#ifndef GC_ATTR_ALLOC_SIZE + /* 'alloc_size' attribute improves __builtin_object_size correctness. */ +# undef GC_ATTR_CALLOC_SIZE +# ifdef __clang__ +# if __has_attribute(__alloc_size__) +# define GC_ATTR_ALLOC_SIZE(argnum) __attribute__((__alloc_size__(argnum))) +# define GC_ATTR_CALLOC_SIZE(n, s) __attribute__((__alloc_size__(n, s))) +# else +# define GC_ATTR_ALLOC_SIZE(argnum) /* empty */ +# endif +# elif GC_GNUC_PREREQ(4, 3) && !defined(__ICC) +# define GC_ATTR_ALLOC_SIZE(argnum) __attribute__((__alloc_size__(argnum))) +# define GC_ATTR_CALLOC_SIZE(n, s) __attribute__((__alloc_size__(n, s))) +# else +# define GC_ATTR_ALLOC_SIZE(argnum) /* empty */ +# endif +#endif + +#ifndef GC_ATTR_CALLOC_SIZE +# define GC_ATTR_CALLOC_SIZE(n, s) /* empty */ +#endif + +#ifndef GC_ATTR_NONNULL +# if GC_GNUC_PREREQ(4, 0) +# define GC_ATTR_NONNULL(argnum) __attribute__((__nonnull__(argnum))) +# else +# define GC_ATTR_NONNULL(argnum) /* empty */ +# endif +#endif + +#ifndef GC_ATTR_DEPRECATED +# ifdef GC_BUILD +# undef GC_ATTR_DEPRECATED +# define GC_ATTR_DEPRECATED /* empty */ +# elif GC_GNUC_PREREQ(4, 0) +# define GC_ATTR_DEPRECATED __attribute__((__deprecated__)) +# elif defined(_MSC_VER) && _MSC_VER >= 1200 +# define GC_ATTR_DEPRECATED __declspec(deprecated) +# else +# define GC_ATTR_DEPRECATED /* empty */ +# endif +#endif + +#if defined(__sgi) && !defined(__GNUC__) && _COMPILER_VERSION >= 720 +# define GC_ADD_CALLER +# define GC_RETURN_ADDR (GC_word)__return_address +#endif + +#if defined(__linux__) || defined(__GLIBC__) +# if !defined(__native_client__) +# include +# endif +# if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1 || __GLIBC__ > 2) \ + && !defined(__ia64__) \ + && !defined(GC_MISSING_EXECINFO_H) \ + && !defined(GC_HAVE_BUILTIN_BACKTRACE) +# define GC_HAVE_BUILTIN_BACKTRACE +# endif +# if defined(__i386__) || defined(__amd64__) || defined(__x86_64__) +# define GC_CAN_SAVE_CALL_STACKS +# endif +#endif /* GLIBC */ + +#if defined(_MSC_VER) && _MSC_VER >= 1200 /* version 12.0+ (MSVC 6.0+) */ \ + && !defined(_AMD64_) && !defined(_M_X64) && !defined(_WIN32_WCE) \ + && !defined(GC_HAVE_NO_BUILTIN_BACKTRACE) \ + && !defined(GC_HAVE_BUILTIN_BACKTRACE) +# define GC_HAVE_BUILTIN_BACKTRACE +#endif + +#if defined(GC_HAVE_BUILTIN_BACKTRACE) && !defined(GC_CAN_SAVE_CALL_STACKS) +# define GC_CAN_SAVE_CALL_STACKS +#endif + +#if defined(__sparc__) +# define GC_CAN_SAVE_CALL_STACKS +#endif + +/* If we're on a platform on which we can't save call stacks, but */ +/* gcc is normally used, we go ahead and define GC_ADD_CALLER. */ +/* We make this decision independent of whether gcc is actually being */ +/* used, in order to keep the interface consistent, and allow mixing */ +/* of compilers. */ +/* This may also be desirable if it is possible but expensive to */ +/* retrieve the call chain. */ +#if (defined(__linux__) || defined(__DragonFly__) || defined(__FreeBSD__) \ + || defined(__FreeBSD_kernel__) || defined(__HAIKU__) \ + || defined(__NetBSD__) || defined(__OpenBSD__) \ + || defined(HOST_ANDROID) || defined(__ANDROID__)) \ + && !defined(GC_CAN_SAVE_CALL_STACKS) +# define GC_ADD_CALLER +# if GC_GNUC_PREREQ(2, 95) + /* gcc knows how to retrieve return address, but we don't know */ + /* how to generate call stacks. */ +# define GC_RETURN_ADDR (GC_word)__builtin_return_address(0) +# if GC_GNUC_PREREQ(4, 0) && (defined(__i386__) || defined(__amd64__) \ + || defined(__x86_64__) /* and probably others... */) +# define GC_HAVE_RETURN_ADDR_PARENT +# define GC_RETURN_ADDR_PARENT \ + (GC_word)__builtin_extract_return_addr(__builtin_return_address(1)) +# endif +# else + /* Just pass 0 for gcc compatibility. */ +# define GC_RETURN_ADDR 0 +# endif +#endif /* !GC_CAN_SAVE_CALL_STACKS */ + +#ifdef GC_PTHREADS + +# if (defined(GC_DARWIN_THREADS) || defined(GC_WIN32_PTHREADS) \ + || defined(__native_client__) || defined(GC_RTEMS_PTHREADS)) \ + && !defined(GC_NO_DLOPEN) + /* Either there is no dlopen() or we do not need to intercept it. */ +# define GC_NO_DLOPEN +# endif + +# if (defined(GC_DARWIN_THREADS) || defined(GC_WIN32_PTHREADS) \ + || defined(GC_OPENBSD_THREADS) || defined(__native_client__)) \ + && !defined(GC_NO_PTHREAD_SIGMASK) + /* Either there is no pthread_sigmask() or no need to intercept it. */ +# define GC_NO_PTHREAD_SIGMASK +# endif + +# if defined(__native_client__) + /* At present, NaCl pthread_create() prototype does not have */ + /* "const" for its "attr" argument; also, NaCl pthread_exit() one */ + /* does not have "noreturn" attribute. */ +# ifndef GC_PTHREAD_CREATE_CONST +# define GC_PTHREAD_CREATE_CONST /* empty */ +# endif +# ifndef GC_HAVE_PTHREAD_EXIT +# define GC_HAVE_PTHREAD_EXIT +# define GC_PTHREAD_EXIT_ATTRIBUTE /* empty */ +# endif +# endif + +# if !defined(GC_HAVE_PTHREAD_EXIT) \ + && !defined(HOST_ANDROID) && !defined(__ANDROID__) \ + && (defined(GC_LINUX_THREADS) || defined(GC_SOLARIS_THREADS)) +# define GC_HAVE_PTHREAD_EXIT + /* Intercept pthread_exit on Linux and Solaris. */ +# if GC_GNUC_PREREQ(2, 7) +# define GC_PTHREAD_EXIT_ATTRIBUTE __attribute__((__noreturn__)) +# elif defined(__NORETURN) /* used in Solaris */ +# define GC_PTHREAD_EXIT_ATTRIBUTE __NORETURN +# else +# define GC_PTHREAD_EXIT_ATTRIBUTE /* empty */ +# endif +# endif + +# if (!defined(GC_HAVE_PTHREAD_EXIT) || defined(__native_client__)) \ + && !defined(GC_NO_PTHREAD_CANCEL) + /* Either there is no pthread_cancel() or no need to intercept it. */ +# define GC_NO_PTHREAD_CANCEL +# endif + +#endif /* GC_PTHREADS */ + +#endif diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_cpp.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_cpp.h new file mode 100644 index 000000000..875c64e29 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_cpp.h @@ -0,0 +1,495 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program for any + * purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + */ + +#ifndef GC_CPP_H +#define GC_CPP_H + +/**************************************************************************** +C++ Interface to the Boehm Collector + + John R. Ellis and Jesse Hull + +This interface provides access to the Boehm collector. It provides +basic facilities similar to those described in "Safe, Efficient +Garbage Collection for C++", by John R. Ellis and David L. Detlefs +(ftp://ftp.parc.xerox.com/pub/ellis/gc). + +All heap-allocated objects are either "collectible" or +"uncollectible". Programs must explicitly delete uncollectible +objects, whereas the garbage collector will automatically delete +collectible objects when it discovers them to be inaccessible. +Collectible objects may freely point at uncollectible objects and vice +versa. + +Objects allocated with the built-in "::operator new" are uncollectible. + +Objects derived from class "gc" are collectible. For example: + + class A: public gc {...}; + A* a = new A; // a is collectible. + +Collectible instances of non-class types can be allocated using the GC +(or UseGC) placement: + + typedef int A[ 10 ]; + A* a = new (GC) A; + +Uncollectible instances of classes derived from "gc" can be allocated +using the NoGC placement: + + class A: public gc {...}; + A* a = new (NoGC) A; // a is uncollectible. + +The new(PointerFreeGC) syntax allows the allocation of collectible +objects that are not scanned by the collector. This useful if you +are allocating compressed data, bitmaps, or network packets. (In +the latter case, it may remove danger of unfriendly network packets +intentionally containing values that cause spurious memory retention.) + +Both uncollectible and collectible objects can be explicitly deleted +with "delete", which invokes an object's destructors and frees its +storage immediately. + +A collectible object may have a clean-up function, which will be +invoked when the collector discovers the object to be inaccessible. +An object derived from "gc_cleanup" or containing a member derived +from "gc_cleanup" has a default clean-up function that invokes the +object's destructors. Explicit clean-up functions may be specified as +an additional placement argument: + + A* a = ::new (GC, MyCleanup) A; + +An object is considered "accessible" by the collector if it can be +reached by a path of pointers from static variables, automatic +variables of active functions, or from some object with clean-up +enabled; pointers from an object to itself are ignored. + +Thus, if objects A and B both have clean-up functions, and A points at +B, B is considered accessible. After A's clean-up is invoked and its +storage released, B will then become inaccessible and will have its +clean-up invoked. If A points at B and B points to A, forming a +cycle, then that's considered a storage leak, and neither will be +collectible. See the interface gc.h for low-level facilities for +handling such cycles of objects with clean-up. + +The collector cannot guarantee that it will find all inaccessible +objects. In practice, it finds almost all of them. + + +Cautions: + +1. Be sure the collector has been augmented with "make c++" or +"--enable-cplusplus". + +2. If your compiler supports the new "operator new[]" syntax, then +add -DGC_OPERATOR_NEW_ARRAY to the Makefile. + +If your compiler doesn't support "operator new[]", beware that an +array of type T, where T is derived from "gc", may or may not be +allocated as a collectible object (it depends on the compiler). Use +the explicit GC placement to make the array collectible. For example: + + class A: public gc {...}; + A* a1 = new A[ 10 ]; // collectible or uncollectible? + A* a2 = new (GC) A[ 10 ]; // collectible. + +3. The destructors of collectible arrays of objects derived from +"gc_cleanup" will not be invoked properly. For example: + + class A: public gc_cleanup {...}; + A* a = new (GC) A[ 10 ]; // destructors not invoked correctly + +Typically, only the destructor for the first element of the array will +be invoked when the array is garbage-collected. To get all the +destructors of any array executed, you must supply an explicit +clean-up function: + + A* a = new (GC, MyCleanUp) A[ 10 ]; + +(Implementing clean-up of arrays correctly, portably, and in a way +that preserves the correct exception semantics requires a language +extension, e.g. the "gc" keyword.) + +4. Compiler bugs (now hopefully history): + +* Solaris 2's CC (SC3.0) doesn't implement t->~T() correctly, so the +destructors of classes derived from gc_cleanup won't be invoked. +You'll have to explicitly register a clean-up function with +new-placement syntax. + +* Evidently cfront 3.0 does not allow destructors to be explicitly +invoked using the ANSI-conforming syntax t->~T(). If you're using +cfront 3.0, you'll have to comment out the class gc_cleanup, which +uses explicit invocation. + +5. GC name conflicts: + +Many other systems seem to use the identifier "GC" as an abbreviation +for "Graphics Context". Since version 5.0, GC placement has been replaced +by UseGC. GC is an alias for UseGC, unless GC_NAME_CONFLICT is defined. + +****************************************************************************/ + +#include "gc.h" + +#ifdef GC_NAMESPACE +# define GC_NS_QUALIFY(T) boehmgc::T +#else +# define GC_NS_QUALIFY(T) T +#endif + +#ifndef THINK_CPLUS +# define GC_cdecl GC_CALLBACK +#else +# define GC_cdecl _cdecl +#endif + +#if !defined(GC_NO_OPERATOR_NEW_ARRAY) \ + && !defined(_ENABLE_ARRAYNEW) /* Digimars */ \ + && (defined(__BORLANDC__) && (__BORLANDC__ < 0x450) \ + || (defined(__GNUC__) && !GC_GNUC_PREREQ(2, 6)) \ + || (defined(_MSC_VER) && _MSC_VER <= 1020) \ + || (defined(__WATCOMC__) && __WATCOMC__ < 1050)) +# define GC_NO_OPERATOR_NEW_ARRAY +#endif + +#if !defined(GC_NO_OPERATOR_NEW_ARRAY) && !defined(GC_OPERATOR_NEW_ARRAY) +# define GC_OPERATOR_NEW_ARRAY +#endif + +#if (!defined(__BORLANDC__) || __BORLANDC__ > 0x0620) \ + && ! defined (__sgi) && ! defined(__WATCOMC__) \ + && (!defined(_MSC_VER) || _MSC_VER > 1020) +# define GC_PLACEMENT_DELETE +#endif + +#ifdef GC_NAMESPACE +namespace boehmgc +{ +#endif + +enum GCPlacement +{ + UseGC, +# ifndef GC_NAME_CONFLICT + GC = UseGC, +# endif + NoGC, + PointerFreeGC +# ifdef GC_ATOMIC_UNCOLLECTABLE + , PointerFreeNoGC +# endif +}; + +/** + * Instances of classes derived from "gc" will be allocated in the collected + * heap by default, unless an explicit NoGC placement is specified. + */ +class gc +{ +public: + inline void* operator new(size_t size); + inline void* operator new(size_t size, GCPlacement gcp); + inline void* operator new(size_t size, void* p); + // Must be redefined here, since the other overloadings hide + // the global definition. + inline void operator delete(void* obj); + +# ifdef GC_PLACEMENT_DELETE + inline void operator delete(void*, GCPlacement); + // Called if construction fails. + inline void operator delete(void*, void*); +# endif // GC_PLACEMENT_DELETE + +# ifdef GC_OPERATOR_NEW_ARRAY + inline void* operator new[](size_t size); + inline void* operator new[](size_t size, GCPlacement gcp); + inline void* operator new[](size_t size, void* p); + inline void operator delete[](void* obj); +# ifdef GC_PLACEMENT_DELETE + inline void operator delete[](void*, GCPlacement); + inline void operator delete[](void*, void*); +# endif +# endif // GC_OPERATOR_NEW_ARRAY +}; + +/** + * Instances of classes derived from "gc_cleanup" will be allocated + * in the collected heap by default. When the collector discovers + * an inaccessible object derived from "gc_cleanup" or containing + * a member derived from "gc_cleanup", its destructors will be invoked. + */ +class gc_cleanup: virtual public gc +{ +public: + inline gc_cleanup(); + inline virtual ~gc_cleanup(); + +private: + inline static void GC_cdecl cleanup(void* obj, void* clientData); +}; + +extern "C" { + typedef void (GC_CALLBACK * GCCleanUpFunc)(void* obj, void* clientData); +} + +#ifdef GC_NAMESPACE +} +#endif + +#ifdef _MSC_VER + // Disable warning that "no matching operator delete found; memory will + // not be freed if initialization throws an exception" +# pragma warning(disable:4291) +#endif + +inline void* operator new(size_t size, GC_NS_QUALIFY(GCPlacement) gcp, + GC_NS_QUALIFY(GCCleanUpFunc) /* cleanup */ = 0, + void* /* clientData */ = 0); + // Allocates a collectible or uncollectible object, according to the + // value of "gcp". + // + // For collectible objects, if "cleanup" is non-null, then when the + // allocated object "obj" becomes inaccessible, the collector will + // invoke the function "cleanup(obj, clientData)" but will not + // invoke the object's destructors. It is an error to explicitly + // delete an object allocated with a non-null "cleanup". + // + // It is an error to specify a non-null "cleanup" with NoGC or for + // classes derived from "gc_cleanup" or containing members derived + // from "gc_cleanup". + +#ifdef GC_PLACEMENT_DELETE + inline void operator delete(void*, GC_NS_QUALIFY(GCPlacement), + GC_NS_QUALIFY(GCCleanUpFunc), void*); +#endif + +#ifdef _MSC_VER + // The following ensures that the system default operator new[] does not + // get undefined, which is what seems to happen on VC++ 6 for some reason + // if we define a multi-argument operator new[]. + // There seems to be no way to redirect new in this environment without + // including this everywhere. + // Inlining done to avoid mix up of new and delete operators by VC++ 9 (due + // to arbitrary ordering during linking). + +# if _MSC_VER > 1020 + inline void* operator new[](size_t size) + { + return GC_MALLOC_UNCOLLECTABLE(size); + } + + inline void operator delete[](void* obj) + { + GC_FREE(obj); + } +# endif + + inline void* operator new(size_t size) + { + return GC_MALLOC_UNCOLLECTABLE(size); + } + + inline void operator delete(void* obj) + { + GC_FREE(obj); + } + + // This new operator is used by VC++ in case of Debug builds: +# ifdef GC_DEBUG + inline void* operator new(size_t size, int /* nBlockUse */, + const char* szFileName, int nLine) + { + return GC_debug_malloc_uncollectable(size, szFileName, nLine); + } +# else + inline void* operator new(size_t size, int /* nBlockUse */, + const char* /* szFileName */, int /* nLine */) + { + return GC_malloc_uncollectable(size); + } +# endif /* !GC_DEBUG */ + +# if _MSC_VER > 1020 + // This new operator is used by VC++ 7+ in Debug builds: + inline void* operator new[](size_t size, int nBlockUse, + const char* szFileName, int nLine) + { + return operator new(size, nBlockUse, szFileName, nLine); + } +# endif + +#endif // _MSC_VER + +#ifdef GC_OPERATOR_NEW_ARRAY + // The operator new for arrays, identical to the above. + inline void* operator new[](size_t size, GC_NS_QUALIFY(GCPlacement) gcp, + GC_NS_QUALIFY(GCCleanUpFunc) /* cleanup */ = 0, + void* /* clientData */ = 0); +#endif // GC_OPERATOR_NEW_ARRAY + +/* Inline implementation */ + +#ifdef GC_NAMESPACE +namespace boehmgc +{ +#endif + +inline void* gc::operator new(size_t size) +{ + return GC_MALLOC(size); +} + +inline void* gc::operator new(size_t size, GCPlacement gcp) +{ + switch (gcp) { + case UseGC: + return GC_MALLOC(size); + case PointerFreeGC: + return GC_MALLOC_ATOMIC(size); +# ifdef GC_ATOMIC_UNCOLLECTABLE + case PointerFreeNoGC: + return GC_MALLOC_ATOMIC_UNCOLLECTABLE(size); +# endif + case NoGC: + default: + return GC_MALLOC_UNCOLLECTABLE(size); + } +} + +inline void* gc::operator new(size_t /* size */, void* p) +{ + return p; +} + +inline void gc::operator delete(void* obj) +{ + GC_FREE(obj); +} + +#ifdef GC_PLACEMENT_DELETE + inline void gc::operator delete(void*, void*) {} + + inline void gc::operator delete(void* p, GCPlacement /* gcp */) + { + GC_FREE(p); + } +#endif // GC_PLACEMENT_DELETE + +#ifdef GC_OPERATOR_NEW_ARRAY + inline void* gc::operator new[](size_t size) + { + return gc::operator new(size); + } + + inline void* gc::operator new[](size_t size, GCPlacement gcp) + { + return gc::operator new(size, gcp); + } + + inline void* gc::operator new[](size_t /* size */, void* p) + { + return p; + } + + inline void gc::operator delete[](void* obj) + { + gc::operator delete(obj); + } + +# ifdef GC_PLACEMENT_DELETE + inline void gc::operator delete[](void*, void*) {} + + inline void gc::operator delete[](void* p, GCPlacement /* gcp */) + { + gc::operator delete(p); + } +# endif +#endif // GC_OPERATOR_NEW_ARRAY + +inline gc_cleanup::~gc_cleanup() +{ + void* base = GC_base(this); + if (0 == base) return; // Non-heap object. + GC_register_finalizer_ignore_self(base, 0, 0, 0, 0); +} + +inline void GC_CALLBACK gc_cleanup::cleanup(void* obj, void* displ) +{ + ((gc_cleanup*) ((char*) obj + (ptrdiff_t) displ))->~gc_cleanup(); +} + +inline gc_cleanup::gc_cleanup() +{ + GC_finalization_proc oldProc; + void* oldData; + void* this_ptr = (void*)this; + void* base = GC_base(this_ptr); + if (base != 0) { + // Don't call the debug version, since this is a real base address. + GC_register_finalizer_ignore_self(base, (GC_finalization_proc) cleanup, + (void*)((char*)this_ptr - (char*)base), + &oldProc, &oldData); + if (oldProc != 0) { + GC_register_finalizer_ignore_self(base, oldProc, oldData, 0, 0); + } + } +} + +#ifdef GC_NAMESPACE +} +#endif + +inline void* operator new(size_t size, GC_NS_QUALIFY(GCPlacement) gcp, + GC_NS_QUALIFY(GCCleanUpFunc) cleanup, + void* clientData) +{ + void* obj; + switch (gcp) { + case GC_NS_QUALIFY(UseGC): + obj = GC_MALLOC(size); + if (cleanup != 0) { + GC_REGISTER_FINALIZER_IGNORE_SELF(obj, cleanup, clientData, 0, 0); + } + return obj; + case GC_NS_QUALIFY(PointerFreeGC): + return GC_MALLOC_ATOMIC(size); +# ifdef GC_ATOMIC_UNCOLLECTABLE + case GC_NS_QUALIFY(PointerFreeNoGC): + return GC_MALLOC_ATOMIC_UNCOLLECTABLE(size); +# endif + case GC_NS_QUALIFY(NoGC): + default: + return GC_MALLOC_UNCOLLECTABLE(size); + } +} + +#ifdef GC_PLACEMENT_DELETE + inline void operator delete(void* p, GC_NS_QUALIFY(GCPlacement) /* gcp */, + GC_NS_QUALIFY(GCCleanUpFunc) /* cleanup */, + void* /* clientData */) + { + GC_FREE(p); + } +#endif // GC_PLACEMENT_DELETE + +#ifdef GC_OPERATOR_NEW_ARRAY + inline void* operator new[](size_t size, GC_NS_QUALIFY(GCPlacement) gcp, + GC_NS_QUALIFY(GCCleanUpFunc) cleanup, + void* clientData) + { + return ::operator new(size, gcp, cleanup, clientData); + } +#endif // GC_OPERATOR_NEW_ARRAY + +#endif /* GC_CPP_H */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_disclaim.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_disclaim.h new file mode 100644 index 000000000..b72097723 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_disclaim.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2007-2011 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + */ + +#ifndef GC_DISCLAIM_H +#define GC_DISCLAIM_H + +#include "gc.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/* This API is defined only if the library has been suitably compiled */ +/* (i.e. with ENABLE_DISCLAIM defined). */ + +/* Prepare the object kind used by GC_finalized_malloc. Call it from */ +/* your initialization code or, at least, at some point before using */ +/* finalized allocations. The function is thread-safe. */ +GC_API void GC_CALL GC_init_finalized_malloc(void); + +/* Type of a disclaim call-back. */ +typedef int (GC_CALLBACK * GC_disclaim_proc)(void * /*obj*/); + +/* Register "proc" to be called on each object of "kind" ready to be */ +/* reclaimed. If "proc" returns non-zero, the collector will not */ +/* reclaim the object on this GC cycle. Objects reachable from "proc" */ +/* will be protected from collection if "mark_from_all" is non-zero, */ +/* but at the expense that long chains of objects will take many cycles */ +/* to reclaim. */ +GC_API void GC_CALL GC_register_disclaim_proc(int /*kind*/, + GC_disclaim_proc /*proc*/, + int /*mark_from_all*/); + +/* The finalizer closure used by GC_finalized_malloc. */ +struct GC_finalizer_closure { + GC_finalization_proc proc; + void *cd; +}; + +/* Allocate "size" bytes which is finalized by "fc". This uses a */ +/* dedicated object kind with a disclaim procedure, and is more */ +/* efficient than GC_register_finalizer and friends. */ +/* GC_init_finalized_malloc must be called before using this. */ +/* Note that GC_size (applied to such allocated object) returns a value */ +/* slightly bigger than the specified allocation size, and that GC_base */ +/* result points to a word prior to the start of the allocated object. */ +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_finalized_malloc(size_t /*size*/, + const struct GC_finalizer_closure * /*fc*/); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_gcj.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_gcj.h new file mode 100644 index 000000000..deca0fee2 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_gcj.h @@ -0,0 +1,116 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. + * Copyright 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright 1999 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* This file assumes the collector has been compiled with GC_GCJ_SUPPORT. */ + +/* + * We allocate objects whose first word contains a pointer to a struct + * describing the object type. This struct contains a garbage collector mark + * descriptor at offset MARK_DESCR_OFFSET. Alternatively, the objects + * may be marked by the mark procedure passed to GC_init_gcj_malloc. + */ + +#ifndef GC_GCJ_H +#define GC_GCJ_H + + /* Gcj keeps GC descriptor as second word of vtable. This */ + /* probably needs to be adjusted for other clients. */ + /* We currently assume that this offset is such that: */ + /* - all objects of this kind are large enough to have */ + /* a value at that offset, and */ + /* - it is not zero. */ + /* These assumptions allow objects on the free list to be */ + /* marked normally. */ + +#ifndef GC_H +# include "gc.h" +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +/* The following allocators signal an out of memory condition with */ +/* return GC_oom_fn(bytes); */ + +/* The following function must be called before the gcj allocators */ +/* can be invoked. */ +/* mp_index and mp are the index and mark_proc (see gc_mark.h) */ +/* respectively for the allocated objects. Mark_proc will be */ +/* used to build the descriptor for objects allocated through the */ +/* debugging interface. The mark_proc will be invoked on all such */ +/* objects with an "environment" value of 1. The client may choose */ +/* to use the same mark_proc for some of its generated mark descriptors.*/ +/* In that case, it should use a different "environment" value to */ +/* detect the presence or absence of the debug header. */ +/* Mp is really of type mark_proc, as defined in gc_mark.h. We don't */ +/* want to include that here for namespace pollution reasons. */ +/* Passing in mp_index here instead of having GC_init_gcj_malloc() */ +/* internally call GC_new_proc() is quite ugly, but in typical usage */ +/* scenarios a compiler also has to know about mp_index, so */ +/* generating it dynamically is not acceptable. Mp_index will */ +/* typically be an integer < RESERVED_MARK_PROCS, so that it doesn't */ +/* collide with GC_new_proc allocated indices. If the application */ +/* needs no other reserved indices, zero */ +/* (GC_GCJ_RESERVED_MARK_PROC_INDEX in gc_mark.h) is an obvious choice. */ +GC_API void GC_CALL GC_init_gcj_malloc(int /* mp_index */, + void * /* really mark_proc */ /* mp */); +GC_API void GC_CALL GC_init_gcj_vector (int /* mp_index */, + void * /* really mark_proc */ /* mp */); + +/* Allocate an object, clear it, and store the pointer to the */ +/* type structure (vtable in gcj). */ +/* This adds a byte at the end of the object if GC_malloc would.*/ +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_gcj_malloc(size_t /* lb */, + void * /* ptr_to_struct_containing_descr */); + +/* The debug versions allocate such that the specified mark_proc */ +/* is always invoked. */ +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_debug_gcj_malloc(size_t /* lb */, + void * /* ptr_to_struct_containing_descr */, + GC_EXTRA_PARAMS); + +/* Similar to GC_gcj_malloc, but assumes that a pointer to near the */ +/* beginning of the resulting object is always maintained. */ +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_gcj_malloc_ignore_off_page(size_t /* lb */, + void * /* ptr_to_struct_containing_descr */); + +GC_API GC_ATTR_MALLOC void * GC_CALL + GC_gcj_vector_malloc(size_t /* lb */, + void * /* ptr_to_struct_containing_descr */); + +/* The kind numbers of normal and debug gcj objects. */ +/* Useful only for debug support, we hope. */ +GC_API int GC_gcj_kind; + +GC_API int GC_gcj_debug_kind; + +#ifdef GC_DEBUG +# define GC_GCJ_MALLOC(s,d) GC_debug_gcj_malloc(s,d,GC_EXTRAS) +# define GC_GCJ_MALLOC_IGNORE_OFF_PAGE(s,d) GC_debug_gcj_malloc(s,d,GC_EXTRAS) +#else +# define GC_GCJ_MALLOC(s,d) GC_gcj_malloc(s,d) +# define GC_GCJ_MALLOC_IGNORE_OFF_PAGE(s,d) GC_gcj_malloc_ignore_off_page(s,d) +#endif + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* GC_GCJ_H */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_inline.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_inline.h new file mode 100644 index 000000000..dca8fa3a1 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_inline.h @@ -0,0 +1,205 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. + * Copyright (c) 2005 Hewlett-Packard Development Company, L.P. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#ifndef GC_INLINE_H +#define GC_INLINE_H + +/* WARNING: */ +/* Note that for these routines, it is the clients responsibility to */ +/* add the extra byte at the end to deal with one-past-the-end pointers.*/ +/* In the standard collector configuration, the collector assumes that */ +/* such a byte has been added, and hence does not trace the last word */ +/* in the resulting object. */ +/* This is not an issue if the collector is compiled with */ +/* DONT_ADD_BYTE_AT_END, or if GC_all_interior_pointers is not set. */ +/* This interface is most useful for compilers that generate C. */ +/* It is also used internally for thread-local allocation. */ +/* Manual use is hereby discouraged. */ + +#include "gc.h" +#include "gc_tiny_fl.h" + +#if GC_GNUC_PREREQ(3, 0) +# define GC_EXPECT(expr, outcome) __builtin_expect(expr,outcome) + /* Equivalent to (expr), but predict that usually (expr)==outcome. */ +#else +# define GC_EXPECT(expr, outcome) (expr) +#endif + +#ifndef GC_ASSERT +# ifdef NDEBUG +# define GC_ASSERT(expr) /* empty */ +# else +# include +# define GC_ASSERT(expr) assert(expr) +# endif +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +#ifndef GC_PREFETCH_FOR_WRITE +# if GC_GNUC_PREREQ(3, 0) && !defined(GC_NO_PREFETCH_FOR_WRITE) +# define GC_PREFETCH_FOR_WRITE(x) __builtin_prefetch((x), 1) +# else +# define GC_PREFETCH_FOR_WRITE(x) (void)0 +# endif +#endif + +/* Object kinds; must match PTRFREE, NORMAL in gc_priv.h. */ +#define GC_I_PTRFREE 0 +#define GC_I_NORMAL 1 + +/* Store a pointer to a list of newly allocated objects of kind k and */ +/* size lb in *result. The caller must make sure that *result is */ +/* traced even if objects are ptrfree. */ +GC_API void GC_CALL GC_generic_malloc_many(size_t /* lb */, int /* k */, + void ** /* result */); + +/* Generalized version of GC_malloc and GC_malloc_atomic. */ +/* Uses appropriately the thread-local (if available) or the global */ +/* free-list of the specified kind. */ +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_malloc_kind(size_t /* lb */, int /* k */); + +#ifdef GC_THREADS + /* Same as above but uses only the global free-list. */ + GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_malloc_kind_global(size_t /* lb */, int /* k */); +#else +# define GC_malloc_kind_global GC_malloc_kind +#endif + +/* An internal macro to update the free list pointer atomically (if */ +/* the AO primitives are available) to avoid race with the marker. */ +#if defined(GC_THREADS) && defined(AO_HAVE_store) +# define GC_FAST_M_AO_STORE(my_fl, next) \ + AO_store((volatile AO_t *)(my_fl), (AO_t)(next)) +#else +# define GC_FAST_M_AO_STORE(my_fl, next) (void)(*(my_fl) = (next)) +#endif + +/* The ultimately general inline allocation macro. Allocate an object */ +/* of size granules, putting the resulting pointer in result. Tiny_fl */ +/* is a "tiny" free list array, which will be used first, if the size */ +/* is appropriate. If granules is too large, we allocate with */ +/* default_expr instead. If we need to refill the free list, we use */ +/* GC_generic_malloc_many with the indicated kind. */ +/* Tiny_fl should be an array of GC_TINY_FREELISTS void * pointers. */ +/* If num_direct is nonzero, and the individual free list pointers */ +/* are initialized to (void *)1, then we allocate num_direct granules */ +/* directly using generic_malloc before putting multiple objects into */ +/* the tiny_fl entry. If num_direct is zero, then the free lists may */ +/* also be initialized to (void *)0. */ +/* Note that we use the zeroth free list to hold objects 1 granule in */ +/* size that are used to satisfy size 0 allocation requests. */ +/* We rely on much of this hopefully getting optimized away in the */ +/* num_direct = 0 case. */ +/* Particularly if granules is constant, this should generate a small */ +/* amount of code. */ +# define GC_FAST_MALLOC_GRANS(result,granules,tiny_fl,num_direct, \ + kind,default_expr,init) \ + do { \ + if (GC_EXPECT((granules) >= GC_TINY_FREELISTS,0)) { \ + result = (default_expr); \ + } else { \ + void **my_fl = (tiny_fl) + (granules); \ + void *my_entry=*my_fl; \ + void *next; \ + \ + for (;;) { \ + if (GC_EXPECT((GC_word)my_entry \ + > (num_direct) + GC_TINY_FREELISTS + 1, 1)) { \ + next = *(void **)(my_entry); \ + result = (void *)my_entry; \ + GC_FAST_M_AO_STORE(my_fl, next); \ + init; \ + GC_PREFETCH_FOR_WRITE(next); \ + if ((kind) != GC_I_PTRFREE) GC_end_stubborn_change(my_fl); \ + GC_ASSERT(GC_size(result) >= (granules)*GC_GRANULE_BYTES); \ + GC_ASSERT((kind) == GC_I_PTRFREE \ + || ((GC_word *)result)[1] == 0); \ + break; \ + } \ + /* Entry contains counter or NULL */ \ + if ((GC_signed_word)my_entry - (GC_signed_word)(num_direct) <= 0 \ + /* (GC_word)my_entry <= (num_direct) */ \ + && my_entry != NULL) { \ + /* Small counter value, not NULL */ \ + GC_FAST_M_AO_STORE(my_fl, (char *)my_entry \ + + (granules) + 1); \ + if ((kind) != GC_I_PTRFREE) GC_end_stubborn_change(my_fl); \ + result = (default_expr); \ + break; \ + } else { \ + /* Large counter or NULL */ \ + GC_generic_malloc_many(((granules) == 0? GC_GRANULE_BYTES : \ + GC_RAW_BYTES_FROM_INDEX(granules)), \ + kind, my_fl); \ + my_entry = *my_fl; \ + if (my_entry == 0) { \ + result = (*GC_get_oom_fn())((granules)*GC_GRANULE_BYTES); \ + break; \ + } \ + } \ + } \ + } \ + } while (0) + +# define GC_WORDS_TO_WHOLE_GRANULES(n) \ + GC_WORDS_TO_GRANULES((n) + GC_GRANULE_WORDS - 1) + +/* Allocate n words (NOT BYTES). X is made to point to the result. */ +/* This should really only be used if GC_all_interior_pointers is */ +/* not set, or DONT_ADD_BYTE_AT_END is set. See above. */ +/* The semantics changed in version 7.0; we no longer lock, and */ +/* the caller is responsible for supplying a cleared tiny_fl */ +/* free list array. For single-threaded applications, this may be */ +/* a global array. */ +# define GC_MALLOC_WORDS_KIND(result,n,tiny_fl,kind,init) \ + do { \ + size_t granules = GC_WORDS_TO_WHOLE_GRANULES(n); \ + GC_FAST_MALLOC_GRANS(result, granules, tiny_fl, 0, kind, \ + GC_malloc_kind(granules*GC_GRANULE_BYTES, kind), \ + init); \ + } while (0) + +# define GC_MALLOC_WORDS(result,n,tiny_fl) \ + GC_MALLOC_WORDS_KIND(result, n, tiny_fl, GC_I_NORMAL, \ + *(void **)(result) = 0) + +# define GC_MALLOC_ATOMIC_WORDS(result,n,tiny_fl) \ + GC_MALLOC_WORDS_KIND(result, n, tiny_fl, GC_I_PTRFREE, (void)0) + +/* And once more for two word initialized objects: */ +# define GC_CONS(result, first, second, tiny_fl) \ + do { \ + GC_MALLOC_WORDS_KIND(result, 2, tiny_fl, GC_I_NORMAL, (void)0); \ + if ((result) != NULL) { \ + *(void **)(result) = (void *)(first); \ + ((void **)(result))[1] = (void *)(second); \ + GC_end_stubborn_change(result); \ + } \ + } while (0) + +GC_API void GC_CALL GC_print_free_list(int /* kind */, + size_t /* sz_in_granules */); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* !GC_INLINE_H */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_mark.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_mark.h new file mode 100644 index 000000000..108794de1 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_mark.h @@ -0,0 +1,317 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 2001 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + */ + +/* + * This contains interfaces to the GC marker that are likely to be useful to + * clients that provide detailed heap layout information to the collector. + * This interface should not be used by normal C or C++ clients. + * It will be useful to runtimes for other languages. + * + * This is an experts-only interface! There are many ways to break the + * collector in subtle ways by using this functionality. + */ +#ifndef GC_MARK_H +#define GC_MARK_H + +#ifndef GC_H +# include "gc.h" +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +/* A client supplied mark procedure. Returns new mark stack pointer. */ +/* Primary effect should be to push new entries on the mark stack. */ +/* Mark stack pointer values are passed and returned explicitly. */ +/* Global variables describing mark stack are not necessarily valid. */ +/* (This usually saves a few cycles by keeping things in registers.) */ +/* Assumed to scan about GC_PROC_BYTES on average. If it needs to do */ +/* much more work than that, it should do it in smaller pieces by */ +/* pushing itself back on the mark stack. */ +/* Note that it should always do some work (defined as marking some */ +/* objects) before pushing more than one entry on the mark stack. */ +/* This is required to ensure termination in the event of mark stack */ +/* overflows. */ +/* This procedure is always called with at least one empty entry on the */ +/* mark stack. */ +/* Currently we require that mark procedures look for pointers in a */ +/* subset of the places the conservative marker would. It must be safe */ +/* to invoke the normal mark procedure instead. */ +/* WARNING: Such a mark procedure may be invoked on an unused object */ +/* residing on a free list. Such objects are cleared, except for a */ +/* free list link field in the first word. Thus mark procedures may */ +/* not count on the presence of a type descriptor, and must handle this */ +/* case correctly somehow. */ +#define GC_PROC_BYTES 100 + +#ifdef GC_BUILD + struct GC_ms_entry; +#else + struct GC_ms_entry { void *opaque; }; +#endif +typedef struct GC_ms_entry * (*GC_mark_proc)(GC_word * /* addr */, + struct GC_ms_entry * /* mark_stack_ptr */, + struct GC_ms_entry * /* mark_stack_limit */, + GC_word /* env */); + +#define GC_LOG_MAX_MARK_PROCS 6 +#define GC_MAX_MARK_PROCS (1 << GC_LOG_MAX_MARK_PROCS) + +/* In a few cases it's necessary to assign statically known indices to */ +/* certain mark procs. Thus we reserve a few for well known clients. */ +/* (This is necessary if mark descriptors are compiler generated.) */ +#define GC_RESERVED_MARK_PROCS 8 +#define GC_GCJ_RESERVED_MARK_PROC_INDEX 0 + +/* Object descriptors on mark stack or in objects. Low order two */ +/* bits are tags distinguishing among the following 4 possibilities */ +/* for the high order 30 bits. */ +#define GC_DS_TAG_BITS 2 +#define GC_DS_TAGS ((1 << GC_DS_TAG_BITS) - 1) +#define GC_DS_LENGTH 0 /* The entire word is a length in bytes that */ + /* must be a multiple of 4. */ +#define GC_DS_BITMAP 1 /* 30 (62) bits are a bitmap describing pointer */ + /* fields. The msb is 1 if the first word */ + /* is a pointer. */ + /* (This unconventional ordering sometimes */ + /* makes the marker slightly faster.) */ + /* Zeroes indicate definite nonpointers. Ones */ + /* indicate possible pointers. */ + /* Only usable if pointers are word aligned. */ +#define GC_DS_PROC 2 + /* The objects referenced by this object can be */ + /* pushed on the mark stack by invoking */ + /* PROC(descr). ENV(descr) is passed as the */ + /* last argument. */ +#define GC_MAKE_PROC(proc_index, env) \ + (((((env) << GC_LOG_MAX_MARK_PROCS) \ + | (proc_index)) << GC_DS_TAG_BITS) | GC_DS_PROC) +#define GC_DS_PER_OBJECT 3 /* The real descriptor is at the */ + /* byte displacement from the beginning of the */ + /* object given by descr & ~GC_DS_TAGS. */ + /* If the descriptor is negative, the real */ + /* descriptor is at (*) - */ + /* (descr&~GC_DS_TAGS) - GC_INDIR_PER_OBJ_BIAS */ + /* The latter alternative can be used if each */ + /* object contains a type descriptor in the */ + /* first word. */ + /* Note that in the multi-threaded environments */ + /* per-object descriptors must be located in */ + /* either the first two or last two words of */ + /* the object, since only those are guaranteed */ + /* to be cleared while the allocation lock is */ + /* held. */ +#define GC_INDIR_PER_OBJ_BIAS 0x10 + +GC_API void * GC_least_plausible_heap_addr; +GC_API void * GC_greatest_plausible_heap_addr; + /* Bounds on the heap. Guaranteed valid */ + /* Likely to include future heap expansion. */ + /* Hence usually includes not-yet-mapped */ + /* memory. */ + +/* Handle nested references in a custom mark procedure. */ +/* Check if obj is a valid object. If so, ensure that it is marked. */ +/* If it was not previously marked, push its contents onto the mark */ +/* stack for future scanning. The object will then be scanned using */ +/* its mark descriptor. */ +/* Returns the new mark stack pointer. */ +/* Handles mark stack overflows correctly. */ +/* Since this marks first, it makes progress even if there are mark */ +/* stack overflows. */ +/* Src is the address of the pointer to obj, which is used only */ +/* for back pointer-based heap debugging. */ +/* It is strongly recommended that most objects be handled without mark */ +/* procedures, e.g. with bitmap descriptors, and that mark procedures */ +/* be reserved for exceptional cases. That will ensure that */ +/* performance of this call is not extremely performance critical. */ +/* (Otherwise we would need to inline GC_mark_and_push completely, */ +/* which would tie the client code to a fixed collector version.) */ +/* Note that mark procedures should explicitly call FIXUP_POINTER() */ +/* if required. */ +GC_API struct GC_ms_entry * GC_CALL GC_mark_and_push(void * /* obj */, + struct GC_ms_entry * /* mark_stack_ptr */, + struct GC_ms_entry * /* mark_stack_limit */, + void ** /* src */); + +#define GC_MARK_AND_PUSH(obj, msp, lim, src) \ + ((GC_word)(obj) >= (GC_word)GC_least_plausible_heap_addr && \ + (GC_word)(obj) <= (GC_word)GC_greatest_plausible_heap_addr ? \ + GC_mark_and_push(obj, msp, lim, src) : (msp)) + +GC_API size_t GC_debug_header_size; + /* The size of the header added to objects allocated through */ + /* the GC_debug routines. */ + /* Defined as a variable so that client mark procedures don't */ + /* need to be recompiled for collector version changes. */ +#define GC_USR_PTR_FROM_BASE(p) ((void *)((char *)(p) + GC_debug_header_size)) + +/* And some routines to support creation of new "kinds", e.g. with */ +/* custom mark procedures, by language runtimes. */ +/* The _inner versions assume the caller holds the allocation lock. */ + +/* Return a new free list array. */ +GC_API void ** GC_CALL GC_new_free_list(void); +GC_API void ** GC_CALL GC_new_free_list_inner(void); + +/* Return a new kind, as specified. */ +GC_API unsigned GC_CALL GC_new_kind(void ** /* free_list */, + GC_word /* mark_descriptor_template */, + int /* add_size_to_descriptor */, + int /* clear_new_objects */) GC_ATTR_NONNULL(1); + /* The last two parameters must be zero or one. */ +GC_API unsigned GC_CALL GC_new_kind_inner(void ** /* free_list */, + GC_word /* mark_descriptor_template */, + int /* add_size_to_descriptor */, + int /* clear_new_objects */) GC_ATTR_NONNULL(1); + +/* Return a new mark procedure identifier, suitable for use as */ +/* the first argument in GC_MAKE_PROC. */ +GC_API unsigned GC_CALL GC_new_proc(GC_mark_proc); +GC_API unsigned GC_CALL GC_new_proc_inner(GC_mark_proc); + +/* Allocate an object of a given kind. By default, there are only */ +/* a few kinds: composite (pointer-free), atomic, uncollectible, etc. */ +/* We claim it is possible for clever client code that understands the */ +/* GC internals to add more, e.g. to communicate object layout */ +/* information to the collector. Note that in the multi-threaded */ +/* contexts, this is usually unsafe for kinds that have the descriptor */ +/* in the object itself, since there is otherwise a window in which */ +/* the descriptor is not correct. Even in the single-threaded case, */ +/* we need to be sure that cleared objects on a free list don't */ +/* cause a GC crash if they are accidentally traced. */ +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL GC_generic_malloc( + size_t /* lb */, + int /* knd */); + +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_generic_malloc_ignore_off_page( + size_t /* lb */, int /* knd */); + /* As above, but pointers to past the */ + /* first page of the resulting object */ + /* are ignored. */ + +/* Generalized version of GC_malloc_[atomic_]uncollectable. */ +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_generic_malloc_uncollectable( + size_t /* lb */, int /* knd */); + +/* Same as above but primary for allocating an object of the same kind */ +/* as an existing one (kind obtained by GC_get_kind_and_size). */ +/* Not suitable for GCJ and typed-malloc kinds. */ +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_generic_or_special_malloc( + size_t /* size */, int /* knd */); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_debug_generic_or_special_malloc( + size_t /* size */, int /* knd */, + GC_EXTRA_PARAMS); + +#ifdef GC_DEBUG +# define GC_GENERIC_OR_SPECIAL_MALLOC(sz, knd) \ + GC_debug_generic_or_special_malloc(sz, knd, GC_EXTRAS) +#else +# define GC_GENERIC_OR_SPECIAL_MALLOC(sz, knd) \ + GC_generic_or_special_malloc(sz, knd) +#endif /* !GC_DEBUG */ + +/* Similar to GC_size but returns object kind. Size is returned too */ +/* if psize is not NULL. */ +GC_API int GC_CALL GC_get_kind_and_size(const void *, size_t * /* psize */) + GC_ATTR_NONNULL(1); + +typedef void (GC_CALLBACK * GC_describe_type_fn)(void * /* p */, + char * /* out_buf */); + /* A procedure which */ + /* produces a human-readable */ + /* description of the "type" of object */ + /* p into the buffer out_buf of length */ + /* GC_TYPE_DESCR_LEN. This is used by */ + /* the debug support when printing */ + /* objects. */ + /* These functions should be as robust */ + /* as possible, though we do avoid */ + /* invoking them on objects on the */ + /* global free list. */ +#define GC_TYPE_DESCR_LEN 40 + +GC_API void GC_CALL GC_register_describe_type_fn(int /* kind */, + GC_describe_type_fn); + /* Register a describe_type function */ + /* to be used when printing objects */ + /* of a particular kind. */ + +/* Clear some of the inaccessible part of the stack. Returns its */ +/* argument, so it can be used in a tail call position, hence clearing */ +/* another frame. Argument may be NULL. */ +GC_API void * GC_CALL GC_clear_stack(void *); + +/* Set and get the client notifier on collections. The client function */ +/* is called at the start of every full GC (called with the allocation */ +/* lock held). May be 0. This is a really tricky interface to use */ +/* correctly. Unless you really understand the collector internals, */ +/* the callback should not, directly or indirectly, make any GC_ or */ +/* potentially blocking calls. In particular, it is not safe to */ +/* allocate memory using the garbage collector from within the callback */ +/* function. Both the setter and getter acquire the GC lock. */ +typedef void (GC_CALLBACK * GC_start_callback_proc)(void); +GC_API void GC_CALL GC_set_start_callback(GC_start_callback_proc); +GC_API GC_start_callback_proc GC_CALL GC_get_start_callback(void); + +/* Slow/general mark bit manipulation. The caller must hold the */ +/* allocation lock. GC_is_marked returns 1 (TRUE) or 0. */ +GC_API int GC_CALL GC_is_marked(const void *) GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_clear_mark_bit(const void *) GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_set_mark_bit(const void *) GC_ATTR_NONNULL(1); + +/* Push everything in the given range onto the mark stack. */ +/* (GC_push_conditional pushes either all or only dirty pages depending */ +/* on the third argument.) GC_push_all_eager also ensures that stack */ +/* is scanned immediately, not just scheduled for scanning. */ +GC_API void GC_CALL GC_push_all(void * /* bottom */, void * /* top */); +GC_API void GC_CALL GC_push_all_eager(void * /* bottom */, void * /* top */); +GC_API void GC_CALL GC_push_conditional(void * /* bottom */, void * /* top */, + int /* bool all */); +GC_API void GC_CALL GC_push_finalizer_structures(void); + +/* Set and get the client push-other-roots procedure. A client */ +/* supplied procedure should also call the original procedure. */ +/* Note that both the setter and getter require some external */ +/* synchronization to avoid data race. */ +typedef void (GC_CALLBACK * GC_push_other_roots_proc)(void); +GC_API void GC_CALL GC_set_push_other_roots(GC_push_other_roots_proc); +GC_API GC_push_other_roots_proc GC_CALL GC_get_push_other_roots(void); + +/* Walk the GC heap visiting all reachable objects. Assume the caller */ +/* holds the allocation lock. Object base pointer, object size and */ +/* client custom data are passed to the callback (holding the lock). */ +typedef void (GC_CALLBACK *GC_reachable_object_proc)(void * /* obj */, + size_t /* bytes */, + void * /* client_data */); +GC_API void GC_CALL GC_enumerate_reachable_objects_inner( + GC_reachable_object_proc, + void * /* client_data */) GC_ATTR_NONNULL(1); + +GC_API int GC_CALL GC_is_tmp_root(void *); + +GC_API void GC_CALL GC_print_trace(GC_word /* gc_no */); +GC_API void GC_CALL GC_print_trace_inner(GC_word /* gc_no */); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* GC_MARK_H */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_pthread_redirects.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_pthread_redirects.h new file mode 100644 index 000000000..92951c3c6 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_pthread_redirects.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2010 by Hewlett-Packard Development Company. + * All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* Our pthread support normally needs to intercept a number of thread */ +/* calls. We arrange to do that here, if appropriate. */ + +/* Included from gc.h only. Included only if GC_PTHREADS. */ +#if defined(GC_H) && defined(GC_PTHREADS) + +/* We need to intercept calls to many of the threads primitives, so */ +/* that we can locate thread stacks and stop the world. */ +/* Note also that the collector cannot always see thread specific data. */ +/* Thread specific data should generally consist of pointers to */ +/* uncollectible objects (allocated with GC_malloc_uncollectable, */ +/* not the system malloc), which are deallocated using the destructor */ +/* facility in thr_keycreate. Alternatively, keep a redundant pointer */ +/* to thread specific data on the thread stack. */ + +#ifndef GC_PTHREAD_REDIRECTS_ONLY + +# include +# ifndef GC_NO_DLOPEN +# include +# endif +# ifndef GC_NO_PTHREAD_SIGMASK +# include /* needed anyway for proper redirection */ +# endif + +# ifdef __cplusplus + extern "C" { +# endif + +# ifndef GC_SUSPEND_THREAD_ID +# define GC_SUSPEND_THREAD_ID pthread_t +# endif + +# ifndef GC_NO_DLOPEN + GC_API void *GC_dlopen(const char * /* path */, int /* mode */); +# endif /* !GC_NO_DLOPEN */ + +# ifndef GC_NO_PTHREAD_SIGMASK +# if defined(GC_PTHREAD_SIGMASK_NEEDED) \ + || defined(_BSD_SOURCE) || defined(_GNU_SOURCE) \ + || (_POSIX_C_SOURCE >= 199506L) || (_XOPEN_SOURCE >= 500) + GC_API int GC_pthread_sigmask(int /* how */, const sigset_t *, + sigset_t * /* oset */); +# endif +# endif /* !GC_NO_PTHREAD_SIGMASK */ + +# ifndef GC_PTHREAD_CREATE_CONST + /* This is used for pthread_create() only. */ +# define GC_PTHREAD_CREATE_CONST const +# endif + + GC_API int GC_pthread_create(pthread_t *, + GC_PTHREAD_CREATE_CONST pthread_attr_t *, + void *(*)(void *), void * /* arg */); + GC_API int GC_pthread_join(pthread_t, void ** /* retval */); + GC_API int GC_pthread_detach(pthread_t); + +# ifndef GC_NO_PTHREAD_CANCEL + GC_API int GC_pthread_cancel(pthread_t); +# endif + +# if defined(GC_HAVE_PTHREAD_EXIT) && !defined(GC_PTHREAD_EXIT_DECLARED) +# define GC_PTHREAD_EXIT_DECLARED + GC_API void GC_pthread_exit(void *) GC_PTHREAD_EXIT_ATTRIBUTE; +# endif + +# ifdef __cplusplus + } /* extern "C" */ +# endif + +#endif /* !GC_PTHREAD_REDIRECTS_ONLY */ + +#if !defined(GC_NO_THREAD_REDIRECTS) && !defined(GC_USE_LD_WRAP) + /* Unless the compiler supports #pragma extern_prefix, the Tru64 */ + /* UNIX redefines some POSIX thread functions to use */ + /* mangled names. Anyway, it's safe to undef them before redefining. */ +# undef pthread_create +# undef pthread_join +# undef pthread_detach +# define pthread_create GC_pthread_create +# define pthread_join GC_pthread_join +# define pthread_detach GC_pthread_detach + +# ifndef GC_NO_PTHREAD_SIGMASK +# undef pthread_sigmask +# define pthread_sigmask GC_pthread_sigmask +# endif +# ifndef GC_NO_DLOPEN +# undef dlopen +# define dlopen GC_dlopen +# endif +# ifndef GC_NO_PTHREAD_CANCEL +# undef pthread_cancel +# define pthread_cancel GC_pthread_cancel +# endif +# ifdef GC_HAVE_PTHREAD_EXIT +# undef pthread_exit +# define pthread_exit GC_pthread_exit +# endif +#endif /* !GC_NO_THREAD_REDIRECTS */ + +#endif /* GC_PTHREADS */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_tiny_fl.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_tiny_fl.h new file mode 100644 index 000000000..0382b4179 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_tiny_fl.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 1999-2005 Hewlett-Packard Development Company, L.P. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#ifndef GC_TINY_FL_H +#define GC_TINY_FL_H +/* + * Constants and data structures for "tiny" free lists. + * These are used for thread-local allocation or in-lined allocators. + * Each global free list also essentially starts with one of these. + * However, global free lists are known to the GC. "Tiny" free lists + * are basically private to the client. Their contents are viewed as + * "in use" and marked accordingly by the core of the GC. + * + * Note that inlined code might know about the layout of these and the constants + * involved. Thus any change here may invalidate clients, and such changes should + * be avoided. Hence we keep this as simple as possible. + */ + +/* + * We always set GC_GRANULE_BYTES to twice the length of a pointer. + * This means that all allocation requests are rounded up to the next + * multiple of 16 on 64-bit architectures or 8 on 32-bit architectures. + * This appears to be a reasonable compromise between fragmentation overhead + * and space usage for mark bits (usually mark bytes). + * On many 64-bit architectures some memory references require 16-byte + * alignment, making this necessary anyway. + * For a few 32-bit architecture (e.g. x86), we may also need 16-byte alignment + * for certain memory references. But currently that does not seem to be the + * default for all conventional malloc implementations, so we ignore that + * problem. + * It would always be safe, and often useful, to be able to allocate very + * small objects with smaller alignment. But that would cost us mark bit + * space, so we no longer do so. + */ +#ifndef GC_GRANULE_BYTES + /* GC_GRANULE_BYTES should not be overridden in any instances of the GC */ + /* library that may be shared between applications, since it affects */ + /* the binary interface to the library. */ +# if defined(__LP64__) || defined (_LP64) || defined(_WIN64) \ + || defined(__s390x__) \ + || (defined(__x86_64__) && !defined(__ILP32__)) \ + || defined(__alpha__) || defined(__powerpc64__) \ + || defined(__arch64__) +# define GC_GRANULE_BYTES 16 +# define GC_GRANULE_WORDS 2 +# else +# define GC_GRANULE_BYTES 8 +# define GC_GRANULE_WORDS 2 +# endif +#endif /* !GC_GRANULE_BYTES */ + +#if GC_GRANULE_WORDS == 2 +# define GC_WORDS_TO_GRANULES(n) ((n)>>1) +#else +# define GC_WORDS_TO_GRANULES(n) ((n)*sizeof(void *)/GC_GRANULE_BYTES) +#endif + +/* A "tiny" free list header contains TINY_FREELISTS pointers to */ +/* singly linked lists of objects of different sizes, the ith one */ +/* containing objects i granules in size. Note that there is a list */ +/* of size zero objects. */ +#ifndef GC_TINY_FREELISTS +# if GC_GRANULE_BYTES == 16 +# define GC_TINY_FREELISTS 25 +# else +# define GC_TINY_FREELISTS 33 /* Up to and including 256 bytes */ +# endif +#endif /* !GC_TINY_FREELISTS */ + +/* The ith free list corresponds to size i*GC_GRANULE_BYTES */ +/* Internally to the collector, the index can be computed with */ +/* ROUNDED_UP_GRANULES. Externally, we don't know whether */ +/* DONT_ADD_BYTE_AT_END is set, but the client should know. */ + +/* Convert a free list index to the actual size of objects */ +/* on that list, including extra space we added. Not an */ +/* inverse of the above. */ +#define GC_RAW_BYTES_FROM_INDEX(i) ((i) * GC_GRANULE_BYTES) + +#endif /* GC_TINY_FL_H */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_typed.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_typed.h new file mode 100644 index 000000000..1ddc3f84e --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_typed.h @@ -0,0 +1,121 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright 1996 Silicon Graphics. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* + * Some simple primitives for allocation with explicit type information. + * Facilities for dynamic type inference may be added later. + * Should be used only for extremely performance critical applications, + * or if conservative collector leakage is otherwise a problem (unlikely). + * Note that this is implemented completely separately from the rest + * of the collector, and is not linked in unless referenced. + * This does not currently support GC_DEBUG in any interesting way. + */ + +#ifndef GC_TYPED_H +#define GC_TYPED_H + +#ifndef GC_H +# include "gc.h" +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +typedef GC_word * GC_bitmap; + /* The least significant bit of the first word is one if */ + /* the first word in the object may be a pointer. */ + +#define GC_WORDSZ (8 * sizeof(GC_word)) +#define GC_get_bit(bm, index) \ + (((bm)[(index) / GC_WORDSZ] >> ((index) % GC_WORDSZ)) & 1) +#define GC_set_bit(bm, index) \ + ((bm)[(index) / GC_WORDSZ] |= (GC_word)1 << ((index) % GC_WORDSZ)) +#define GC_WORD_OFFSET(t, f) (offsetof(t,f) / sizeof(GC_word)) +#define GC_WORD_LEN(t) (sizeof(t) / sizeof(GC_word)) +#define GC_BITMAP_SIZE(t) ((GC_WORD_LEN(t) + GC_WORDSZ - 1) / GC_WORDSZ) + +typedef GC_word GC_descr; + +GC_API GC_descr GC_CALL GC_make_descriptor(const GC_word * /* GC_bitmap bm */, + size_t /* len (number_of_bits_in_bitmap) */); + /* Return a type descriptor for the object whose layout */ + /* is described by the argument. */ + /* The least significant bit of the first word is one */ + /* if the first word in the object may be a pointer. */ + /* The second argument specifies the number of */ + /* meaningful bits in the bitmap. The actual object */ + /* may be larger (but not smaller). Any additional */ + /* words in the object are assumed not to contain */ + /* pointers. */ + /* Returns a conservative approximation in the */ + /* (unlikely) case of insufficient memory to build */ + /* the descriptor. Calls to GC_make_descriptor */ + /* may consume some amount of a finite resource. This */ + /* is intended to be called once per type, not once */ + /* per allocation. */ + +/* It is possible to generate a descriptor for a C type T with */ +/* word aligned pointer fields f1, f2, ... as follows: */ +/* */ +/* GC_descr T_descr; */ +/* GC_word T_bitmap[GC_BITMAP_SIZE(T)] = {0}; */ +/* GC_set_bit(T_bitmap, GC_WORD_OFFSET(T,f1)); */ +/* GC_set_bit(T_bitmap, GC_WORD_OFFSET(T,f2)); */ +/* ... */ +/* T_descr = GC_make_descriptor(T_bitmap, GC_WORD_LEN(T)); */ + +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_malloc_explicitly_typed(size_t /* size_in_bytes */, + GC_descr /* d */); + /* Allocate an object whose layout is described by d. */ + /* The size may NOT be less than the number of */ + /* meaningful bits in the bitmap of d multiplied by */ + /* sizeof GC_word. The returned object is cleared. */ + /* The returned object may NOT be passed to GC_realloc. */ + +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_malloc_explicitly_typed_ignore_off_page(size_t /* size_in_bytes */, + GC_descr /* d */); + +GC_API GC_ATTR_MALLOC GC_ATTR_CALLOC_SIZE(1, 2) void * GC_CALL + GC_calloc_explicitly_typed(size_t /* nelements */, + size_t /* element_size_in_bytes */, + GC_descr /* d */); + /* Allocate an array of nelements elements, each of the */ + /* given size, and with the given descriptor. */ + /* The element size must be a multiple of the byte */ + /* alignment required for pointers. E.g. on a 32-bit */ + /* machine with 16-bit aligned pointers, size_in_bytes */ + /* must be a multiple of 2. The element size may NOT */ + /* be less than the number of meaningful bits in the */ + /* bitmap of d multiplied by sizeof GC_word. */ + /* Returned object is cleared. */ + +#ifdef GC_DEBUG +# define GC_MALLOC_EXPLICITLY_TYPED(bytes, d) GC_MALLOC(bytes) +# define GC_CALLOC_EXPLICITLY_TYPED(n, bytes, d) GC_MALLOC((n) * (bytes)) +#else +# define GC_MALLOC_EXPLICITLY_TYPED(bytes, d) \ + GC_malloc_explicitly_typed(bytes, d) +# define GC_CALLOC_EXPLICITLY_TYPED(n, bytes, d) \ + GC_calloc_explicitly_typed(n, bytes, d) +#endif + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* GC_TYPED_H */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_version.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_version.h new file mode 100644 index 000000000..54ec0112b --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/gc_version.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2009 by Hewlett-Packard Development Company. + * All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* This should never be included directly; it is included only from gc.h. */ +#if defined(GC_H) + +/* The policy regarding version numbers: development code has odd */ +/* "minor" number (and "micro" part is 0); when development is finished */ +/* and a release is prepared, "minor" number is incremented (keeping */ +/* "micro" number still zero), whenever a defect is fixed a new release */ +/* is prepared incrementing "micro" part to odd value (the most stable */ +/* release has the biggest "micro" number). */ + +/* The version here should match that in configure/configure.ac */ +/* Eventually this one may become unnecessary. For now we need */ +/* it to keep the old-style build process working. */ +#define GC_TMP_VERSION_MAJOR 7 +#define GC_TMP_VERSION_MINOR 7 +#define GC_TMP_VERSION_MICRO 0 /* 7.7.0 */ + +#ifdef GC_VERSION_MAJOR +# if GC_TMP_VERSION_MAJOR != GC_VERSION_MAJOR \ + || GC_TMP_VERSION_MINOR != GC_VERSION_MINOR \ + || GC_TMP_VERSION_MICRO != GC_VERSION_MICRO +# error Inconsistent version info. Check README.md, include/gc_version.h and configure.ac. +# endif +#else +# define GC_VERSION_MAJOR GC_TMP_VERSION_MAJOR +# define GC_VERSION_MINOR GC_TMP_VERSION_MINOR +# define GC_VERSION_MICRO GC_TMP_VERSION_MICRO +#endif /* !GC_VERSION_MAJOR */ + +#endif diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/include.am b/unity-2019.4.24f1-mbe/external/bdwgc/include/include.am new file mode 100644 index 000000000..fe4e16b1e --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/include.am @@ -0,0 +1,57 @@ +# +# THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED +# OR IMPLIED. ANY USE IS AT YOUR OWN RISK. +# +# Permission is hereby granted to use or copy this program +# for any purpose, provided the above notices are retained on all copies. +# Permission to modify the code and to distribute modified code is granted, +# provided the above notices are retained, and a notice that the code was +# modified is included with the above copyright notice. + +## Process this file with automake to produce part of Makefile.in. + +# installed headers +# +pkginclude_HEADERS += \ + include/gc.h \ + include/gc_allocator.h \ + include/gc_backptr.h \ + include/gc_config_macros.h \ + include/gc_disclaim.h \ + include/gc_gcj.h \ + include/gc_inline.h \ + include/gc_mark.h \ + include/gc_pthread_redirects.h \ + include/gc_tiny_fl.h \ + include/gc_typed.h \ + include/gc_version.h \ + include/javaxfc.h \ + include/leak_detector.h \ + include/weakpointer.h + +# headers which are not installed +# +dist_noinst_HEADERS += \ + include/cord.h \ + include/cord_pos.h \ + include/ec.h \ + include/gc_alloc_ptrs.h \ + include/new_gc_alloc.h \ + include/private/darwin_semaphore.h \ + include/private/darwin_stop_world.h \ + include/private/dbg_mlc.h \ + include/private/gc_atomic_ops.h \ + include/private/gc_hdrs.h \ + include/private/gc_locks.h \ + include/private/gc_pmark.h \ + include/private/gc_priv.h \ + include/private/gcconfig.h \ + include/private/msvc_dbg.h \ + include/private/pthread_stop_world.h \ + include/private/pthread_support.h \ + include/private/specific.h \ + include/private/thread_local_alloc.h + +# unprefixed header +include_HEADERS += \ + include/extra/gc.h diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/javaxfc.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/javaxfc.h new file mode 100644 index 000000000..27da68b3d --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/javaxfc.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2009 by Hewlett-Packard Development Company. + * All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#ifndef GC_H +# include "gc.h" +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +/* + * Invoke all remaining finalizers that haven't yet been run. (Since the + * notifier is not called, this should be called from a separate thread.) + * This function is needed for strict compliance with the Java standard, + * which can make the runtime guarantee that all finalizers are run. + * This is problematic for several reasons: + * 1) It means that finalizers, and all methods called by them, + * must be prepared to deal with objects that have been finalized in + * spite of the fact that they are still referenced by statically + * allocated pointer variables. + * 2) It may mean that we get stuck in an infinite loop running + * finalizers which create new finalizable objects, though that's + * probably unlikely. + * Thus this is not recommended for general use. + */ +GC_API void GC_CALL GC_finalize_all(void); + +#ifdef GC_THREADS + /* External thread suspension support. No thread suspension count */ + /* (so a thread which has been suspended numerous times will be */ + /* resumed with the very first call to GC_resume_thread). */ + /* Acquire the allocation lock. Thread should be registered in GC */ + /* (otherwise no-op, GC_is_thread_suspended returns false). */ + /* Unimplemented on some platforms. Not recommended for general use. */ +# ifndef GC_SUSPEND_THREAD_ID +# define GC_SUSPEND_THREAD_ID void* +# endif + GC_API void GC_CALL GC_suspend_thread(GC_SUSPEND_THREAD_ID); + GC_API void GC_CALL GC_resume_thread(GC_SUSPEND_THREAD_ID); + GC_API int GC_CALL GC_is_thread_suspended(GC_SUSPEND_THREAD_ID); +#endif /* GC_THREADS */ + +#ifdef __cplusplus + } /* extern "C" */ +#endif diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/leak_detector.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/leak_detector.h new file mode 100644 index 000000000..0c27eda08 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/leak_detector.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2000-2011 by Hewlett-Packard Development Company. + * All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#ifndef GC_LEAK_DETECTOR_H +#define GC_LEAK_DETECTOR_H + +/* Include leak_detector.h (e.g., via GCC --include directive) */ +/* to turn BoehmGC into a Leak Detector. */ + +#ifndef GC_DEBUG +# define GC_DEBUG +#endif +#include "gc.h" + +#ifndef GC_DONT_INCLUDE_STDLIB + /* We ensure stdlib.h and string.h are included before */ + /* redirecting malloc() and the accompanying functions. */ +# include +# include +#endif + +#undef malloc +#define malloc(n) GC_MALLOC(n) +#undef calloc +#define calloc(m,n) GC_MALLOC((m)*(n)) +#undef free +#define free(p) GC_FREE(p) +#undef realloc +#define realloc(p,n) GC_REALLOC(p,n) + +#undef strdup +#define strdup(s) GC_STRDUP(s) +#undef strndup +#define strndup(s,n) GC_STRNDUP(s,n) + +#ifdef GC_REQUIRE_WCSDUP + /* The collector should be built with GC_REQUIRE_WCSDUP */ + /* defined as well to redirect wcsdup(). */ +# include +# undef wcsdup +# define wcsdup(s) GC_WCSDUP(s) +#endif + +#undef memalign +#define memalign(a,n) GC_memalign(a,n) +#undef posix_memalign +#define posix_memalign(p,a,n) GC_posix_memalign(p,a,n) + +#ifndef CHECK_LEAKS +# define CHECK_LEAKS() GC_gcollect() + /* Note 1: CHECK_LEAKS does not have GC prefix (preserved for */ + /* backward compatibility). */ + /* Note 2: GC_gcollect() is also called automatically in the */ + /* leak-finding mode at program exit. */ +#endif + +#endif /* GC_LEAK_DETECTOR_H */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/new_gc_alloc.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/new_gc_alloc.h new file mode 100644 index 000000000..866136b44 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/new_gc_alloc.h @@ -0,0 +1,472 @@ +/* + * Copyright (c) 1996-1998 by Silicon Graphics. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +// +// This is a revision of gc_allocator.h for SGI STL versions > 3.0. +// Unlike earlier versions, it supplements the standard (STL) alloc.h +// instead of replacing it. +// +// This is sloppy about variable names used in header files. +// It also doesn't yet understand the new header file names or +// namespaces. +// +// This assumes the collector has been compiled with -DGC_ATOMIC_UNCOLLECTABLE. +// The user should also consider -DREDIRECT_MALLOC=GC_uncollectable_malloc, +// to ensure that object allocated through malloc are traced. +// +// Some of this could be faster in the explicit deallocation case. +// In particular, we spend too much time clearing objects on the +// free lists. That could be avoided. +// +// This uses template classes with static members, and hence does not work +// with g++ 2.7.2 and earlier. +// +// Unlike its predecessor, this one simply defines +// gc_alloc +// single_client_gc_alloc +// traceable_alloc +// single_client_traceable_alloc +// +// It does not redefine alloc. Nor does it change the default allocator, +// though the user may wish to do so. (The argument against changing +// the default allocator is that it may introduce subtle link compatibility +// problems. The argument for changing it is that the usual default +// allocator is usually a very bad choice for a garbage collected environment.) +// + +#ifndef GC_ALLOC_H + +#include "gc.h" + +#if GC_GNUC_PREREQ(3, 0) +# include +# ifndef __STL_BEGIN_NAMESPACE +# define __STL_BEGIN_NAMESPACE namespace std { +# define __STL_END_NAMESPACE }; +# endif +# ifndef __STL_USE_STD_ALLOCATORS +# define __STL_USE_STD_ALLOCATORS +# endif +#else +# include // A more portable way to get stl_alloc.h file. +#endif + +/* A hack to deal with gcc 3.1. If you are using gcc3.1 and later, */ +/* you should probably really use gc_allocator.h instead. */ +#if GC_GNUC_PREREQ(3, 1) +# define simple_alloc __simple_alloc +#endif + +#define GC_ALLOC_H + +#include +#include + +// We can't include gc_priv.h, since that pulls in way too much stuff. +#include "gc_alloc_ptrs.h" + +#define GC_generic_malloc_words_small(lw, k) \ + GC_generic_malloc((lw) * sizeof(GC_word), k) + +// Object kinds; must match PTRFREE, NORMAL, UNCOLLECTABLE, and +// AUNCOLLECTABLE in gc_priv.h. + +enum { GC_PTRFREE = 0, GC_NORMAL = 1, GC_UNCOLLECTABLE = 2, + GC_AUNCOLLECTABLE = 3 }; + +enum { GC_max_fast_bytes = 255 }; + +enum { GC_byte_alignment = 8 }; + +#if defined(CPPCHECK) + const unsigned GC_bytes_per_word = sizeof(char *); + const unsigned GC_word_alignment = GC_byte_alignment/GC_bytes_per_word; +#else + enum { GC_bytes_per_word = sizeof(char *) }; + enum { GC_word_alignment = GC_byte_alignment/GC_bytes_per_word }; +#endif + +inline void * &GC_obj_link(void * p) +{ return *reinterpret_cast(p); } + +// Compute a number of words >= n+1 bytes. +// The +1 allows for pointers one past the end. +inline size_t GC_round_up(size_t n) +{ + return ((n + GC_byte_alignment)/GC_byte_alignment)*GC_word_alignment; +} + +// The same but don't allow for extra byte. +inline size_t GC_round_up_uncollectable(size_t n) +{ + return ((n + GC_byte_alignment - 1)/GC_byte_alignment)*GC_word_alignment; +} + +template +class GC_aux_template { +public: + // File local count of allocated words. Occasionally this is + // added into the global count. A separate count is necessary since the + // real one must be updated with a procedure call. + static size_t GC_bytes_recently_allocd; + + // Same for uncollectible memory. Not yet reflected in either + // GC_bytes_recently_allocd or GC_non_gc_bytes. + static size_t GC_uncollectable_bytes_recently_allocd; + + // Similar counter for explicitly deallocated memory. + static size_t GC_bytes_recently_freed; + + // Again for uncollectible memory. + static size_t GC_uncollectable_bytes_recently_freed; + + static void * GC_out_of_line_malloc(size_t nwords, int kind); +}; + +template +size_t GC_aux_template::GC_bytes_recently_allocd = 0; + +template +size_t GC_aux_template::GC_uncollectable_bytes_recently_allocd = 0; + +template +size_t GC_aux_template::GC_bytes_recently_freed = 0; + +template +size_t GC_aux_template::GC_uncollectable_bytes_recently_freed = 0; + +template +void * GC_aux_template::GC_out_of_line_malloc(size_t nwords, int kind) +{ + GC_bytes_recently_allocd += GC_uncollectable_bytes_recently_allocd; + GC_non_gc_bytes += + GC_uncollectable_bytes_recently_allocd; + GC_uncollectable_bytes_recently_allocd = 0; + + GC_bytes_recently_freed += GC_uncollectable_bytes_recently_freed; + GC_non_gc_bytes -= GC_uncollectable_bytes_recently_freed; + GC_uncollectable_bytes_recently_freed = 0; + + GC_incr_bytes_allocd(GC_bytes_recently_allocd); + GC_bytes_recently_allocd = 0; + + GC_incr_bytes_freed(GC_bytes_recently_freed); + GC_bytes_recently_freed = 0; + + return GC_generic_malloc_words_small(nwords, kind); +} + +typedef GC_aux_template<0> GC_aux; + +// A fast, single-threaded, garbage-collected allocator +// We assume the first word will be immediately overwritten. +// In this version, deallocation is not a no-op, and explicit +// deallocation is likely to help performance. +template +class single_client_gc_alloc_template { + public: + static void * allocate(size_t n) + { + size_t nwords = GC_round_up(n); + void ** flh; + void * op; + + if (n > GC_max_fast_bytes) return GC_malloc(n); + flh = &GC_objfreelist_ptr[nwords]; + op = *flh; + if (0 == op) { + return GC_aux::GC_out_of_line_malloc(nwords, GC_NORMAL); + } + *flh = GC_obj_link(op); + GC_aux::GC_bytes_recently_allocd += nwords * GC_bytes_per_word; + return op; + } + static void * ptr_free_allocate(size_t n) + { + size_t nwords = GC_round_up(n); + void ** flh; + void * op; + + if (n > GC_max_fast_bytes) return GC_malloc_atomic(n); + flh = &GC_aobjfreelist_ptr[nwords]; + op = *flh; + if (0 == op) { + return GC_aux::GC_out_of_line_malloc(nwords, GC_PTRFREE); + } + *flh = GC_obj_link(op); + GC_aux::GC_bytes_recently_allocd += nwords * GC_bytes_per_word; + return op; + } + static void deallocate(void *p, size_t n) + { + if (n > GC_max_fast_bytes) { + GC_free(p); + } else { + size_t nwords = GC_round_up(n); + void ** flh = &GC_objfreelist_ptr[nwords]; + + GC_obj_link(p) = *flh; + memset(reinterpret_cast(p) + GC_bytes_per_word, 0, + GC_bytes_per_word * (nwords - 1)); + *flh = p; + GC_aux::GC_bytes_recently_freed += nwords * GC_bytes_per_word; + } + } + static void ptr_free_deallocate(void *p, size_t n) + { + if (n > GC_max_fast_bytes) { + GC_free(p); + } else { + size_t nwords = GC_round_up(n); + void ** flh = &GC_aobjfreelist_ptr[nwords]; + + GC_obj_link(p) = *flh; + *flh = p; + GC_aux::GC_bytes_recently_freed += nwords * GC_bytes_per_word; + } + } +}; + +typedef single_client_gc_alloc_template<0> single_client_gc_alloc; + +// Once more, for uncollectible objects. +template +class single_client_traceable_alloc_template { + public: + static void * allocate(size_t n) + { + size_t nwords = GC_round_up_uncollectable(n); + void ** flh; + void * op; + + if (n > GC_max_fast_bytes) return GC_malloc_uncollectable(n); + flh = &GC_uobjfreelist_ptr[nwords]; + op = *flh; + if (0 == op) { + return GC_aux::GC_out_of_line_malloc(nwords, GC_UNCOLLECTABLE); + } + *flh = GC_obj_link(op); + GC_aux::GC_uncollectable_bytes_recently_allocd += + nwords * GC_bytes_per_word; + return op; + } + static void * ptr_free_allocate(size_t n) + { + size_t nwords = GC_round_up_uncollectable(n); + void ** flh; + void * op; + + if (n > GC_max_fast_bytes) return GC_malloc_atomic_uncollectable(n); + flh = &GC_auobjfreelist_ptr[nwords]; + op = *flh; + if (0 == op) { + return GC_aux::GC_out_of_line_malloc(nwords, GC_AUNCOLLECTABLE); + } + *flh = GC_obj_link(op); + GC_aux::GC_uncollectable_bytes_recently_allocd += + nwords * GC_bytes_per_word; + return op; + } + static void deallocate(void *p, size_t n) + { + if (n > GC_max_fast_bytes) { + GC_free(p); + } else { + size_t nwords = GC_round_up_uncollectable(n); + void ** flh = &GC_uobjfreelist_ptr[nwords]; + + GC_obj_link(p) = *flh; + *flh = p; + GC_aux::GC_uncollectable_bytes_recently_freed += + nwords * GC_bytes_per_word; + } + } + static void ptr_free_deallocate(void *p, size_t n) + { + if (n > GC_max_fast_bytes) { + GC_free(p); + } else { + size_t nwords = GC_round_up_uncollectable(n); + void ** flh = &GC_auobjfreelist_ptr[nwords]; + + GC_obj_link(p) = *flh; + *flh = p; + GC_aux::GC_uncollectable_bytes_recently_freed += + nwords * GC_bytes_per_word; + } + } +}; + +typedef single_client_traceable_alloc_template<0> single_client_traceable_alloc; + +template < int dummy > +class gc_alloc_template { + public: + static void * allocate(size_t n) { return GC_malloc(n); } + static void * ptr_free_allocate(size_t n) + { return GC_malloc_atomic(n); } + static void deallocate(void *, size_t) { } + static void ptr_free_deallocate(void *, size_t) { } +}; + +typedef gc_alloc_template < 0 > gc_alloc; + +template < int dummy > +class traceable_alloc_template { + public: + static void * allocate(size_t n) { return GC_malloc_uncollectable(n); } + static void * ptr_free_allocate(size_t n) + { return GC_malloc_atomic_uncollectable(n); } + static void deallocate(void *p, size_t) { GC_free(p); } + static void ptr_free_deallocate(void *p, size_t) { GC_free(p); } +}; + +typedef traceable_alloc_template < 0 > traceable_alloc; + +// We want to specialize simple_alloc so that it does the right thing +// for all pointer-free types. At the moment there is no portable way to +// even approximate that. The following approximation should work for +// SGI compilers, and recent versions of g++. + +// GC_SPECIALIZE() is used internally. +#define GC_SPECIALIZE(T,alloc) \ + class simple_alloc { \ + public: \ + static T *allocate(size_t n) \ + { return 0 == n? 0 : \ + reinterpret_cast(alloc::ptr_free_allocate(n * sizeof(T))); } \ + static T *allocate(void) \ + { return reinterpret_cast(alloc::ptr_free_allocate(sizeof(T))); } \ + static void deallocate(T *p, size_t n) \ + { if (0 != n) alloc::ptr_free_deallocate(p, n * sizeof(T)); } \ + static void deallocate(T *p) \ + { alloc::ptr_free_deallocate(p, sizeof(T)); } \ + }; + +__STL_BEGIN_NAMESPACE + +GC_SPECIALIZE(char, gc_alloc) +GC_SPECIALIZE(int, gc_alloc) +GC_SPECIALIZE(unsigned, gc_alloc) +GC_SPECIALIZE(float, gc_alloc) +GC_SPECIALIZE(double, gc_alloc) + +GC_SPECIALIZE(char, traceable_alloc) +GC_SPECIALIZE(int, traceable_alloc) +GC_SPECIALIZE(unsigned, traceable_alloc) +GC_SPECIALIZE(float, traceable_alloc) +GC_SPECIALIZE(double, traceable_alloc) + +GC_SPECIALIZE(char, single_client_gc_alloc) +GC_SPECIALIZE(int, single_client_gc_alloc) +GC_SPECIALIZE(unsigned, single_client_gc_alloc) +GC_SPECIALIZE(float, single_client_gc_alloc) +GC_SPECIALIZE(double, single_client_gc_alloc) + +GC_SPECIALIZE(char, single_client_traceable_alloc) +GC_SPECIALIZE(int, single_client_traceable_alloc) +GC_SPECIALIZE(unsigned, single_client_traceable_alloc) +GC_SPECIALIZE(float, single_client_traceable_alloc) +GC_SPECIALIZE(double, single_client_traceable_alloc) + +__STL_END_NAMESPACE + +#ifdef __STL_USE_STD_ALLOCATORS + +__STL_BEGIN_NAMESPACE + +template +struct _Alloc_traits<_Tp, gc_alloc > +{ + static const bool _S_instanceless = true; + typedef simple_alloc<_Tp, gc_alloc > _Alloc_type; + typedef __allocator<_Tp, gc_alloc > allocator_type; +}; + +inline bool operator==(const gc_alloc&, + const gc_alloc&) +{ + return true; +} + +inline bool operator!=(const gc_alloc&, + const gc_alloc&) +{ + return false; +} + +template +struct _Alloc_traits<_Tp, single_client_gc_alloc > +{ + static const bool _S_instanceless = true; + typedef simple_alloc<_Tp, single_client_gc_alloc > _Alloc_type; + typedef __allocator<_Tp, single_client_gc_alloc > allocator_type; +}; + +inline bool operator==(const single_client_gc_alloc&, + const single_client_gc_alloc&) +{ + return true; +} + +inline bool operator!=(const single_client_gc_alloc&, + const single_client_gc_alloc&) +{ + return false; +} + +template +struct _Alloc_traits<_Tp, traceable_alloc > +{ + static const bool _S_instanceless = true; + typedef simple_alloc<_Tp, traceable_alloc > _Alloc_type; + typedef __allocator<_Tp, traceable_alloc > allocator_type; +}; + +inline bool operator==(const traceable_alloc&, + const traceable_alloc&) +{ + return true; +} + +inline bool operator!=(const traceable_alloc&, + const traceable_alloc&) +{ + return false; +} + +template +struct _Alloc_traits<_Tp, single_client_traceable_alloc > +{ + static const bool _S_instanceless = true; + typedef simple_alloc<_Tp, single_client_traceable_alloc > _Alloc_type; + typedef __allocator<_Tp, single_client_traceable_alloc > allocator_type; +}; + +inline bool operator==(const single_client_traceable_alloc&, + const single_client_traceable_alloc&) +{ + return true; +} + +inline bool operator!=(const single_client_traceable_alloc&, + const single_client_traceable_alloc&) +{ + return false; +} + +__STL_END_NAMESPACE + +#endif /* __STL_USE_STD_ALLOCATORS */ + +#endif /* GC_ALLOC_H */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/private/darwin_semaphore.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/private/darwin_semaphore.h new file mode 100644 index 000000000..22cbb8f28 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/private/darwin_semaphore.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2009 by Hewlett-Packard Development Company. + * All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#ifndef GC_DARWIN_SEMAPHORE_H +#define GC_DARWIN_SEMAPHORE_H + +#if !defined(GC_DARWIN_THREADS) +# error darwin_semaphore.h included with GC_DARWIN_THREADS not defined +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +/* This is a very simple semaphore implementation for Darwin. It is */ +/* implemented in terms of pthread calls so it is not async signal */ +/* safe. But this is not a problem because signals are not used to */ +/* suspend threads on Darwin. */ + +typedef struct { + pthread_mutex_t mutex; + pthread_cond_t cond; + int value; +} sem_t; + +GC_INLINE int sem_init(sem_t *sem, int pshared, int value) { + if (pshared != 0) { + errno = EPERM; /* unsupported */ + return -1; + } + sem->value = value; + if (pthread_mutex_init(&sem->mutex, NULL) != 0) + return -1; + if (pthread_cond_init(&sem->cond, NULL) != 0) { + (void)pthread_mutex_destroy(&sem->mutex); + return -1; + } + return 0; +} + +GC_INLINE int sem_post(sem_t *sem) { + if (pthread_mutex_lock(&sem->mutex) != 0) + return -1; + sem->value++; + if (pthread_cond_signal(&sem->cond) != 0) { + (void)pthread_mutex_unlock(&sem->mutex); + return -1; + } + return pthread_mutex_unlock(&sem->mutex) != 0 ? -1 : 0; +} + +GC_INLINE int sem_wait(sem_t *sem) { + if (pthread_mutex_lock(&sem->mutex) != 0) + return -1; + while (sem->value == 0) { + if (pthread_cond_wait(&sem->cond, &sem->mutex) != 0) { + (void)pthread_mutex_unlock(&sem->mutex); + return -1; + } + } + sem->value--; + return pthread_mutex_unlock(&sem->mutex) != 0 ? -1 : 0; +} + +GC_INLINE int sem_destroy(sem_t *sem) { + return pthread_cond_destroy(&sem->cond) != 0 + || pthread_mutex_destroy(&sem->mutex) != 0 ? -1 : 0; +} + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/private/darwin_stop_world.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/private/darwin_stop_world.h new file mode 100644 index 000000000..ee97cbf66 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/private/darwin_stop_world.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2009 by Hewlett-Packard Development Company. + * All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#ifndef GC_DARWIN_STOP_WORLD_H +#define GC_DARWIN_STOP_WORLD_H + +#if !defined(GC_DARWIN_THREADS) +# error darwin_stop_world.h included without GC_DARWIN_THREADS defined +#endif + +#include +#include + +EXTERN_C_BEGIN + +struct thread_stop_info { + mach_port_t mach_thread; + ptr_t stack_ptr; /* Valid only when thread is in a "blocked" state. */ +}; + +#ifndef DARWIN_DONT_PARSE_STACK + GC_INNER ptr_t GC_FindTopOfStack(unsigned long); +#endif + +#ifdef MPROTECT_VDB + GC_INNER void GC_mprotect_stop(void); + GC_INNER void GC_mprotect_resume(void); +# ifndef GC_NO_THREADS_DISCOVERY + GC_INNER void GC_darwin_register_mach_handler_thread(mach_port_t thread); +# endif +#endif + +#if defined(PARALLEL_MARK) && !defined(GC_NO_THREADS_DISCOVERY) + GC_INNER GC_bool GC_is_mach_marker(thread_act_t); +#endif + +EXTERN_C_END + +#endif diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/private/dbg_mlc.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/private/dbg_mlc.h new file mode 100644 index 000000000..69cf95c3f --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/private/dbg_mlc.h @@ -0,0 +1,182 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. + * Copyright (c) 1997 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* + * This is mostly an internal header file. Typical clients should + * not use it. Clients that define their own object kinds with + * debugging allocators will probably want to include this, however. + * No attempt is made to keep the namespace clean. This should not be + * included from header files that are frequently included by clients. + */ + +#ifndef _DBG_MLC_H +#define _DBG_MLC_H + +#include "gc_priv.h" +#ifdef KEEP_BACK_PTRS +# include "gc_backptr.h" +#endif + +EXTERN_C_BEGIN + +#if CPP_WORDSZ == 32 +# define START_FLAG (word)0xfedcedcb +# define END_FLAG (word)0xbcdecdef +#else +# define START_FLAG GC_WORD_C(0xFEDCEDCBfedcedcb) +# define END_FLAG GC_WORD_C(0xBCDECDEFbcdecdef) +#endif + /* Stored both one past the end of user object, and one before */ + /* the end of the object as seen by the allocator. */ + +#if defined(KEEP_BACK_PTRS) || defined(PRINT_BLACK_LIST) \ + || defined(MAKE_BACK_GRAPH) + /* Pointer "source"s that aren't real locations. */ + /* Used in oh_back_ptr fields and as "source" */ + /* argument to some marking functions. */ +# define NOT_MARKED (ptr_t)0 +# define MARKED_FOR_FINALIZATION ((ptr_t)(word)2) + /* Object was marked because it is finalizable. */ +# define MARKED_FROM_REGISTER ((ptr_t)(word)4) + /* Object was marked from a register. Hence the */ + /* source of the reference doesn't have an address. */ +#endif /* KEEP_BACK_PTRS || PRINT_BLACK_LIST */ + +/* Object header */ +typedef struct { +# if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH) + /* We potentially keep two different kinds of back */ + /* pointers. KEEP_BACK_PTRS stores a single back */ + /* pointer in each reachable object to allow reporting */ + /* of why an object was retained. MAKE_BACK_GRAPH */ + /* builds a graph containing the inverse of all */ + /* "points-to" edges including those involving */ + /* objects that have just become unreachable. This */ + /* allows detection of growing chains of unreachable */ + /* objects. It may be possible to eventually combine */ + /* both, but for now we keep them separate. Both */ + /* kinds of back pointers are hidden using the */ + /* following macros. In both cases, the plain version */ + /* is constrained to have an least significant bit of 1, */ + /* to allow it to be distinguished from a free list */ + /* link. This means the plain version must have an */ + /* lsb of 0. */ + /* Note that blocks dropped by black-listing will */ + /* also have the lsb clear once debugging has */ + /* started. */ + /* We're careful never to overwrite a value with lsb 0. */ +# if ALIGNMENT == 1 + /* Fudge back pointer to be even. */ +# define HIDE_BACK_PTR(p) GC_HIDE_POINTER(~1 & (word)(p)) +# else +# define HIDE_BACK_PTR(p) GC_HIDE_POINTER(p) +# endif +# ifdef KEEP_BACK_PTRS + GC_hidden_pointer oh_back_ptr; +# endif +# ifdef MAKE_BACK_GRAPH + GC_hidden_pointer oh_bg_ptr; +# endif +# if defined(KEEP_BACK_PTRS) != defined(MAKE_BACK_GRAPH) + /* Keep double-pointer-sized alignment. */ + word oh_dummy; +# endif +# endif + const char * oh_string; /* object descriptor string */ + word oh_int; /* object descriptor integers */ +# ifdef NEED_CALLINFO + struct callinfo oh_ci[NFRAMES]; +# endif +# ifndef SHORT_DBG_HDRS + word oh_sz; /* Original malloc arg. */ + word oh_sf; /* start flag */ +# endif /* SHORT_DBG_HDRS */ +} oh; +/* The size of the above structure is assumed not to de-align things, */ +/* and to be a multiple of the word length. */ + +#ifdef SHORT_DBG_HDRS +# define DEBUG_BYTES (sizeof (oh)) +# define UNCOLLECTABLE_DEBUG_BYTES DEBUG_BYTES +#else + /* Add space for END_FLAG, but use any extra space that was already */ + /* added to catch off-the-end pointers. */ + /* For uncollectible objects, the extra byte is not added. */ +# define UNCOLLECTABLE_DEBUG_BYTES (sizeof (oh) + sizeof (word)) +# define DEBUG_BYTES (UNCOLLECTABLE_DEBUG_BYTES - EXTRA_BYTES) +#endif + +/* Round bytes to words without adding extra byte at end. */ +#define SIMPLE_ROUNDED_UP_WORDS(n) BYTES_TO_WORDS((n) + WORDS_TO_BYTES(1) - 1) + +/* ADD_CALL_CHAIN stores a (partial) call chain into an object */ +/* header; it should be called with the allocation lock held. */ +/* PRINT_CALL_CHAIN prints the call chain stored in an object */ +/* to stderr. It requires that we do not hold the lock. */ +#if defined(SAVE_CALL_CHAIN) + struct callinfo; + GC_INNER void GC_save_callers(struct callinfo info[NFRAMES]); + GC_INNER void GC_print_callers(struct callinfo info[NFRAMES]); +# define ADD_CALL_CHAIN(base, ra) GC_save_callers(((oh *)(base)) -> oh_ci) +# define PRINT_CALL_CHAIN(base) GC_print_callers(((oh *)(base)) -> oh_ci) +#elif defined(GC_ADD_CALLER) + struct callinfo; + GC_INNER void GC_print_callers(struct callinfo info[NFRAMES]); +# define ADD_CALL_CHAIN(base, ra) ((oh *)(base)) -> oh_ci[0].ci_pc = (ra) +# define PRINT_CALL_CHAIN(base) GC_print_callers(((oh *)(base)) -> oh_ci) +#else +# define ADD_CALL_CHAIN(base, ra) +# define PRINT_CALL_CHAIN(base) +#endif + +#ifdef GC_ADD_CALLER +# define OPT_RA ra, +#else +# define OPT_RA +#endif + +/* Check whether object with base pointer p has debugging info */ +/* p is assumed to point to a legitimate object in our part */ +/* of the heap. */ +#ifdef SHORT_DBG_HDRS +# define GC_has_other_debug_info(p) 1 +#else + GC_INNER int GC_has_other_debug_info(ptr_t p); +#endif + +#if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH) +# ifdef SHORT_DBG_HDRS +# error Non-ptr stored in object results in GC_HAS_DEBUG_INFO malfunction + /* We may mistakenly conclude that p has a debugging wrapper. */ +# endif +# if defined(PARALLEL_MARK) && defined(KEEP_BACK_PTRS) +# define GC_HAS_DEBUG_INFO(p) \ + ((AO_load((volatile AO_t *)(p)) & 1) != 0 \ + && GC_has_other_debug_info(p) > 0) + /* Atomic load is used as GC_store_back_pointer */ + /* stores oh_back_ptr atomically (p might point */ + /* to the field); this prevents a TSan warning. */ +# else +# define GC_HAS_DEBUG_INFO(p) \ + ((*(word *)(p) & 1) && GC_has_other_debug_info(p) > 0) +# endif +#else +# define GC_HAS_DEBUG_INFO(p) (GC_has_other_debug_info(p) > 0) +#endif /* !KEEP_BACK_PTRS && !MAKE_BACK_GRAPH */ + +EXTERN_C_END + +#endif /* _DBG_MLC_H */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/private/gc_atomic_ops.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/private/gc_atomic_ops.h new file mode 100644 index 000000000..060b8f5bc --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/private/gc_atomic_ops.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2017 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* This is a private GC header which provides an implementation of */ +/* libatomic_ops subset primitives sufficient for GC assuming that C11 */ +/* atomic intrinsics are available (and have correct implementation). */ +/* This is enabled by defining GC_BUILTIN_ATOMIC macro. Otherwise, */ +/* libatomic_ops library is used to define the primitives. */ + +#ifndef GC_ATOMIC_OPS_H +#define GC_ATOMIC_OPS_H + +#ifdef GC_BUILTIN_ATOMIC + +# include "gc.h" /* for GC_word */ + +# ifdef __cplusplus + extern "C" { +# endif + + typedef GC_word AO_t; + +# ifdef GC_PRIVATE_H /* have GC_INLINE */ +# define AO_INLINE GC_INLINE +# else +# define AO_INLINE static __inline +# endif + + typedef unsigned char AO_TS_t; +# define AO_TS_CLEAR 0 +# define AO_TS_INITIALIZER (AO_TS_t)AO_TS_CLEAR +# if defined(__GCC_ATOMIC_TEST_AND_SET_TRUEVAL) && !defined(CPPCHECK) +# define AO_TS_SET __GCC_ATOMIC_TEST_AND_SET_TRUEVAL +# else +# define AO_TS_SET (AO_TS_t)1 /* true */ +# endif +# define AO_CLEAR(p) __atomic_clear(p, __ATOMIC_RELEASE) +# define AO_test_and_set_acquire(p) __atomic_test_and_set(p, __ATOMIC_ACQUIRE) +# define AO_HAVE_test_and_set_acquire + +# define AO_compiler_barrier() __atomic_signal_fence(__ATOMIC_SEQ_CST) +# define AO_nop_full() __atomic_thread_fence(__ATOMIC_SEQ_CST) +# define AO_HAVE_nop_full + +# define AO_fetch_and_add(p, v) __atomic_fetch_add(p, v, __ATOMIC_RELAXED) +# define AO_HAVE_fetch_and_add +# define AO_fetch_and_add1(p) AO_fetch_and_add(p, 1) +# define AO_HAVE_fetch_and_add1 + +# define AO_or(p, v) (void)__atomic_or_fetch(p, v, __ATOMIC_RELAXED) +# define AO_HAVE_or + +# define AO_load(p) __atomic_load_n(p, __ATOMIC_RELAXED) +# define AO_HAVE_load +# define AO_load_acquire(p) __atomic_load_n(p, __ATOMIC_ACQUIRE) +# define AO_HAVE_load_acquire +# define AO_load_acquire_read(p) AO_load_acquire(p) +# define AO_HAVE_load_acquire_read + +# define AO_store(p, v) __atomic_store_n(p, v, __ATOMIC_RELAXED) +# define AO_HAVE_store +# define AO_store_release(p, v) __atomic_store_n(p, v, __ATOMIC_RELEASE) +# define AO_HAVE_store_release +# define AO_store_release_write(p, v) AO_store_release(p, v) +# define AO_HAVE_store_release_write + +# define AO_char_load(p) __atomic_load_n(p, __ATOMIC_RELAXED) +# define AO_HAVE_char_load +# define AO_char_store(p, v) __atomic_store_n(p, v, __ATOMIC_RELAXED) +# define AO_HAVE_char_store + +# ifdef AO_REQUIRE_CAS + AO_INLINE int + AO_compare_and_swap(volatile AO_t *p, AO_t ov, AO_t nv) + { + return (int)__atomic_compare_exchange_n(p, &ov, nv, 0, + __ATOMIC_RELAXED, __ATOMIC_RELAXED); + } + + AO_INLINE int + AO_compare_and_swap_release(volatile AO_t *p, AO_t ov, AO_t nv) + { + return (int)__atomic_compare_exchange_n(p, &ov, nv, 0, + __ATOMIC_RELEASE, __ATOMIC_RELAXED); + } +# define AO_HAVE_compare_and_swap_release +# endif + +# ifdef __cplusplus + } /* extern "C" */ +# endif + +#elif !defined(NN_PLATFORM_CTR) + /* Fallback to libatomic_ops. */ +# include "atomic_ops.h" + + /* AO_compiler_barrier, AO_load and AO_store should be defined for */ + /* all targets; the rest of the primitives are guaranteed to exist */ + /* only if AO_REQUIRE_CAS is defined (or if the corresponding */ + /* AO_HAVE_x macro is defined). x86/x64 targets have AO_nop_full, */ + /* AO_load_acquire, AO_store_release, at least. */ +# if !defined(AO_HAVE_load) || !defined(AO_HAVE_store) +# error AO_load or AO_store is missing; probably old version of atomic_ops +# endif + +#endif /* !GC_BUILTIN_ATOMIC */ + +#endif /* GC_ATOMIC_OPS_H */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/private/gc_hdrs.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/private/gc_hdrs.h new file mode 100644 index 000000000..03889febf --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/private/gc_hdrs.h @@ -0,0 +1,214 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#ifndef GC_HEADERS_H +#define GC_HEADERS_H + +#if CPP_WORDSZ != 32 && CPP_WORDSZ < 36 +# error Get a real machine +#endif + +EXTERN_C_BEGIN + +typedef struct hblkhdr hdr; + +/* + * The 2 level tree data structure that is used to find block headers. + * If there are more than 32 bits in a pointer, the top level is a hash + * table. + * + * This defines HDR, GET_HDR, and SET_HDR, the main macros used to + * retrieve and set object headers. + * + * We take advantage of a header lookup + * cache. This is a locally declared direct mapped cache, used inside + * the marker. The HC_GET_HDR macro uses and maintains this + * cache. Assuming we get reasonable hit rates, this shaves a few + * memory references from each pointer validation. + */ + +#if CPP_WORDSZ > 32 +# define HASH_TL +#endif + +/* Define appropriate out-degrees for each of the two tree levels */ +#if defined(LARGE_CONFIG) || !defined(SMALL_CONFIG) +# define LOG_BOTTOM_SZ 10 +#else +# define LOG_BOTTOM_SZ 11 + /* Keep top index size reasonable with smaller blocks. */ +#endif +#define BOTTOM_SZ (1 << LOG_BOTTOM_SZ) + +#ifndef HASH_TL +# define LOG_TOP_SZ (WORDSZ - LOG_BOTTOM_SZ - LOG_HBLKSIZE) +#else +# define LOG_TOP_SZ 11 +#endif +#define TOP_SZ (1 << LOG_TOP_SZ) + +/* #define COUNT_HDR_CACHE_HITS */ + +#ifdef COUNT_HDR_CACHE_HITS + extern word GC_hdr_cache_hits; /* used for debugging/profiling */ + extern word GC_hdr_cache_misses; +# define HC_HIT() ++GC_hdr_cache_hits +# define HC_MISS() ++GC_hdr_cache_misses +#else +# define HC_HIT() +# define HC_MISS() +#endif + +typedef struct hce { + word block_addr; /* right shifted by LOG_HBLKSIZE */ + hdr * hce_hdr; +} hdr_cache_entry; + +#define HDR_CACHE_SIZE 8 /* power of 2 */ + +#define DECLARE_HDR_CACHE \ + hdr_cache_entry hdr_cache[HDR_CACHE_SIZE] + +#define INIT_HDR_CACHE BZERO(hdr_cache, sizeof(hdr_cache)) + +#define HCE(h) hdr_cache + (((word)(h) >> LOG_HBLKSIZE) & (HDR_CACHE_SIZE-1)) + +#define HCE_VALID_FOR(hce,h) ((hce) -> block_addr == \ + ((word)(h) >> LOG_HBLKSIZE)) + +#define HCE_HDR(h) ((hce) -> hce_hdr) + +#ifdef PRINT_BLACK_LIST + GC_INNER hdr * GC_header_cache_miss(ptr_t p, hdr_cache_entry *hce, + ptr_t source); +# define HEADER_CACHE_MISS(p, hce, source) \ + GC_header_cache_miss(p, hce, source) +#else + GC_INNER hdr * GC_header_cache_miss(ptr_t p, hdr_cache_entry *hce); +# define HEADER_CACHE_MISS(p, hce, source) GC_header_cache_miss(p, hce) +#endif + +/* Set hhdr to the header for p. Analogous to GET_HDR below, */ +/* except that in the case of large objects, it gets the header for */ +/* the object beginning if GC_all_interior_pointers is set. */ +/* Returns zero if p points to somewhere other than the first page */ +/* of an object, and it is not a valid pointer to the object. */ +#define HC_GET_HDR(p, hhdr, source) \ + { /* cannot use do-while(0) here */ \ + hdr_cache_entry * hce = HCE(p); \ + if (EXPECT(HCE_VALID_FOR(hce, p), TRUE)) { \ + HC_HIT(); \ + hhdr = hce -> hce_hdr; \ + } else { \ + hhdr = HEADER_CACHE_MISS(p, hce, source); \ + if (NULL == hhdr) break; /* go to the enclosing loop end */ \ + } \ + } + +typedef struct bi { + hdr * index[BOTTOM_SZ]; + /* + * The bottom level index contains one of three kinds of values: + * 0 means we're not responsible for this block, + * or this is a block other than the first one in a free block. + * 1 < (long)X <= MAX_JUMP means the block starts at least + * X * HBLKSIZE bytes before the current address. + * A valid pointer points to a hdr structure. (The above can't be + * valid pointers due to the GET_MEM return convention.) + */ + struct bi * asc_link; /* All indices are linked in */ + /* ascending order... */ + struct bi * desc_link; /* ... and in descending order. */ + word key; /* high order address bits. */ +# ifdef HASH_TL + struct bi * hash_link; /* Hash chain link. */ +# endif +} bottom_index; + +/* bottom_index GC_all_nils; - really part of GC_arrays */ + +/* extern bottom_index * GC_top_index []; - really part of GC_arrays */ + /* Each entry points to a bottom_index. */ + /* On a 32 bit machine, it points to */ + /* the index for a set of high order */ + /* bits equal to the index. For longer */ + /* addresses, we hash the high order */ + /* bits to compute the index in */ + /* GC_top_index, and each entry points */ + /* to a hash chain. */ + /* The last entry in each chain is */ + /* GC_all_nils. */ + + +#define MAX_JUMP (HBLKSIZE - 1) + +#define HDR_FROM_BI(bi, p) \ + ((bi)->index[((word)(p) >> LOG_HBLKSIZE) & (BOTTOM_SZ - 1)]) +#ifndef HASH_TL +# define BI(p) (GC_top_index \ + [(word)(p) >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE)]) +# define HDR_INNER(p) HDR_FROM_BI(BI(p),p) +# ifdef SMALL_CONFIG +# define HDR(p) GC_find_header((ptr_t)(p)) +# else +# define HDR(p) HDR_INNER(p) +# endif +# define GET_BI(p, bottom_indx) (void)((bottom_indx) = BI(p)) +# define GET_HDR(p, hhdr) (void)((hhdr) = HDR(p)) +# define SET_HDR(p, hhdr) (void)(HDR_INNER(p) = (hhdr)) +# define GET_HDR_ADDR(p, ha) (void)((ha) = &HDR_INNER(p)) +#else /* hash */ + /* Hash function for tree top level */ +# define TL_HASH(hi) ((hi) & (TOP_SZ - 1)) + /* Set bottom_indx to point to the bottom index for address p */ +# define GET_BI(p, bottom_indx) \ + do { \ + REGISTER word hi = (word)(p) >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE); \ + REGISTER bottom_index * _bi = GC_top_index[TL_HASH(hi)]; \ + while (_bi -> key != hi && _bi != GC_all_nils) \ + _bi = _bi -> hash_link; \ + (bottom_indx) = _bi; \ + } while (0) +# define GET_HDR_ADDR(p, ha) \ + do { \ + REGISTER bottom_index * bi; \ + GET_BI(p, bi); \ + (ha) = &HDR_FROM_BI(bi, p); \ + } while (0) +# define GET_HDR(p, hhdr) \ + do { \ + REGISTER hdr ** _ha; \ + GET_HDR_ADDR(p, _ha); \ + (hhdr) = *_ha; \ + } while (0) +# define SET_HDR(p, hhdr) \ + do { \ + REGISTER hdr ** _ha; \ + GET_HDR_ADDR(p, _ha); \ + *_ha = (hhdr); \ + } while (0) +# define HDR(p) GC_find_header((ptr_t)(p)) +#endif + +/* Is the result a forwarding address to someplace closer to the */ +/* beginning of the block or NULL? */ +#define IS_FORWARDING_ADDR_OR_NIL(hhdr) ((size_t) (hhdr) <= MAX_JUMP) + +/* Get an HBLKSIZE aligned address closer to the beginning of the block */ +/* h. Assumes hhdr == HDR(h) and IS_FORWARDING_ADDR(hhdr). */ +#define FORWARDED_ADDR(h, hhdr) ((struct hblk *)(h) - (size_t)(hhdr)) + +EXTERN_C_END + +#endif /* GC_HEADERS_H */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/private/gc_locks.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/private/gc_locks.h new file mode 100644 index 000000000..3c1041924 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/private/gc_locks.h @@ -0,0 +1,292 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999 by Hewlett-Packard Company. All rights reserved. + * + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#ifndef GC_LOCKS_H +#define GC_LOCKS_H + +/* + * Mutual exclusion between allocator/collector routines. + * Needed if there is more than one allocator thread. + * DCL_LOCK_STATE declares any local variables needed by LOCK and UNLOCK. + * + * Note that I_HOLD_LOCK and I_DONT_HOLD_LOCK are used only positively + * in assertions, and may return TRUE in the "don't know" case. + */ +# ifdef THREADS + +# ifdef PCR +# include +# include +# endif + + EXTERN_C_BEGIN + +# ifdef PCR + GC_EXTERN PCR_Th_ML GC_allocate_ml; +# if defined(CPPCHECK) +# define DCL_LOCK_STATE /* empty */ +# else +# define DCL_LOCK_STATE \ + PCR_ERes GC_fastLockRes; PCR_sigset_t GC_old_sig_mask +# endif +# define UNCOND_LOCK() PCR_Th_ML_Acquire(&GC_allocate_ml) +# define UNCOND_UNLOCK() PCR_Th_ML_Release(&GC_allocate_ml) +# endif + +# if (!defined(AO_HAVE_test_and_set_acquire) || defined(GC_RTEMS_PTHREADS) \ + || defined(SN_TARGET_ORBIS) || defined(SN_TARGET_PS3) \ + || defined(GC_WIN32_THREADS) || defined(LINT2)) && defined(GC_PTHREADS) +# define USE_PTHREAD_LOCKS +# undef USE_SPIN_LOCK +# endif + +# if defined(GC_WIN32_THREADS) && !defined(USE_PTHREAD_LOCKS) +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +# endif +# define NOSERVICE + EXTERN_C_END +# include + EXTERN_C_BEGIN +# define NO_THREAD (DWORD)(-1) + GC_EXTERN CRITICAL_SECTION GC_allocate_ml; +# ifdef GC_ASSERTIONS + GC_EXTERN DWORD GC_lock_holder; +# define SET_LOCK_HOLDER() GC_lock_holder = GetCurrentThreadId() +# define UNSET_LOCK_HOLDER() GC_lock_holder = NO_THREAD +# define I_HOLD_LOCK() (!GC_need_to_lock \ + || GC_lock_holder == GetCurrentThreadId()) +# ifdef THREAD_SANITIZER +# define I_DONT_HOLD_LOCK() TRUE /* Conservatively say yes */ +# else +# define I_DONT_HOLD_LOCK() (!GC_need_to_lock \ + || GC_lock_holder != GetCurrentThreadId()) +# endif +# define UNCOND_LOCK() \ + { GC_ASSERT(I_DONT_HOLD_LOCK()); \ + EnterCriticalSection(&GC_allocate_ml); \ + SET_LOCK_HOLDER(); } +# define UNCOND_UNLOCK() \ + { GC_ASSERT(I_HOLD_LOCK()); UNSET_LOCK_HOLDER(); \ + LeaveCriticalSection(&GC_allocate_ml); } +# else +# define UNCOND_LOCK() EnterCriticalSection(&GC_allocate_ml) +# define UNCOND_UNLOCK() LeaveCriticalSection(&GC_allocate_ml) +# endif /* !GC_ASSERTIONS */ +# elif defined(GC_PTHREADS) + EXTERN_C_END +# include + EXTERN_C_BEGIN + /* Posix allows pthread_t to be a struct, though it rarely is. */ + /* Unfortunately, we need to use a pthread_t to index a data */ + /* structure. It also helps if comparisons don't involve a */ + /* function call. Hence we introduce platform-dependent macros */ + /* to compare pthread_t ids and to map them to integers. */ + /* The mapping to integers does not need to result in different */ + /* integers for each thread, though that should be true as much */ + /* as possible. */ + /* Refine to exclude platforms on which pthread_t is struct. */ +# if !defined(GC_WIN32_PTHREADS) +# define NUMERIC_THREAD_ID(id) ((unsigned long)(id)) +# define THREAD_EQUAL(id1, id2) ((id1) == (id2)) +# define NUMERIC_THREAD_ID_UNIQUE +# elif defined(__WINPTHREADS_VERSION_MAJOR) /* winpthreads */ +# define NUMERIC_THREAD_ID(id) ((unsigned long)(id)) +# define THREAD_EQUAL(id1, id2) ((id1) == (id2)) +# ifndef _WIN64 + /* NUMERIC_THREAD_ID is 32-bit and not unique on Win64. */ +# define NUMERIC_THREAD_ID_UNIQUE +# endif +# else /* pthreads-win32 */ +# define NUMERIC_THREAD_ID(id) ((unsigned long)(word)(id.p)) + /* Using documented internal details of pthreads-win32 library. */ + /* Faster than pthread_equal(). Should not change with */ + /* future versions of pthreads-win32 library. */ +# define THREAD_EQUAL(id1, id2) ((id1.p == id2.p) && (id1.x == id2.x)) +# undef NUMERIC_THREAD_ID_UNIQUE + /* Generic definitions based on pthread_equal() always work but */ + /* will result in poor performance (as NUMERIC_THREAD_ID is */ + /* defined to just a constant) and weak assertion checking. */ +# endif +# define NO_THREAD ((unsigned long)(-1l)) + /* != NUMERIC_THREAD_ID(pthread_self()) for any thread */ + +# ifdef SN_TARGET_PSP2 + EXTERN_C_END +# include "psp2-support.h" + EXTERN_C_BEGIN + GC_EXTERN WapiMutex GC_allocate_ml_PSP2; +# define UNCOND_LOCK() { int res; GC_ASSERT(I_DONT_HOLD_LOCK()); \ + res = PSP2_MutexLock(&GC_allocate_ml_PSP2); \ + GC_ASSERT(0 == res); (void)res; \ + SET_LOCK_HOLDER(); } +# define UNCOND_UNLOCK() { int res; GC_ASSERT(I_HOLD_LOCK()); \ + UNSET_LOCK_HOLDER(); \ + res = PSP2_MutexUnlock(&GC_allocate_ml_PSP2); \ + GC_ASSERT(0 == res); (void)res; } + +# elif (!defined(THREAD_LOCAL_ALLOC) || defined(USE_SPIN_LOCK)) \ + && !defined(USE_PTHREAD_LOCKS) + /* In the THREAD_LOCAL_ALLOC case, the allocation lock tends to */ + /* be held for long periods, if it is held at all. Thus spinning */ + /* and sleeping for fixed periods are likely to result in */ + /* significant wasted time. We thus rely mostly on queued locks. */ +# undef USE_SPIN_LOCK +# define USE_SPIN_LOCK + GC_EXTERN volatile AO_TS_t GC_allocate_lock; + GC_INNER void GC_lock(void); + /* Allocation lock holder. Only set if acquired by client through */ + /* GC_call_with_alloc_lock. */ +# ifdef GC_ASSERTIONS +# define UNCOND_LOCK() \ + { GC_ASSERT(I_DONT_HOLD_LOCK()); \ + if (AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_SET) \ + GC_lock(); \ + SET_LOCK_HOLDER(); } +# define UNCOND_UNLOCK() \ + { GC_ASSERT(I_HOLD_LOCK()); UNSET_LOCK_HOLDER(); \ + AO_CLEAR(&GC_allocate_lock); } +# else +# define UNCOND_LOCK() \ + { if (AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_SET) \ + GC_lock(); } +# define UNCOND_UNLOCK() AO_CLEAR(&GC_allocate_lock) +# endif /* !GC_ASSERTIONS */ +# else /* THREAD_LOCAL_ALLOC || USE_PTHREAD_LOCKS */ +# ifndef USE_PTHREAD_LOCKS +# define USE_PTHREAD_LOCKS +# endif +# endif /* THREAD_LOCAL_ALLOC || USE_PTHREAD_LOCKS */ +# ifdef USE_PTHREAD_LOCKS + EXTERN_C_END +# include + EXTERN_C_BEGIN + GC_EXTERN pthread_mutex_t GC_allocate_ml; +# ifdef GC_ASSERTIONS +# define UNCOND_LOCK() { GC_ASSERT(I_DONT_HOLD_LOCK()); \ + GC_lock(); SET_LOCK_HOLDER(); } +# define UNCOND_UNLOCK() \ + { GC_ASSERT(I_HOLD_LOCK()); UNSET_LOCK_HOLDER(); \ + pthread_mutex_unlock(&GC_allocate_ml); } +# else /* !GC_ASSERTIONS */ +# if defined(NO_PTHREAD_TRYLOCK) +# define UNCOND_LOCK() pthread_mutex_lock(&GC_allocate_ml) +# else +# define UNCOND_LOCK() \ + { if (0 != pthread_mutex_trylock(&GC_allocate_ml)) \ + GC_lock(); } +# endif +# define UNCOND_UNLOCK() pthread_mutex_unlock(&GC_allocate_ml) +# endif /* !GC_ASSERTIONS */ +# endif /* USE_PTHREAD_LOCKS */ +# ifdef GC_ASSERTIONS + GC_EXTERN unsigned long GC_lock_holder; +# define SET_LOCK_HOLDER() \ + GC_lock_holder = NUMERIC_THREAD_ID(pthread_self()) +# define UNSET_LOCK_HOLDER() GC_lock_holder = NO_THREAD +# define I_HOLD_LOCK() \ + (!GC_need_to_lock \ + || GC_lock_holder == NUMERIC_THREAD_ID(pthread_self())) +# if !defined(NUMERIC_THREAD_ID_UNIQUE) || defined(THREAD_SANITIZER) +# define I_DONT_HOLD_LOCK() TRUE /* Conservatively say yes */ +# else +# define I_DONT_HOLD_LOCK() \ + (!GC_need_to_lock \ + || GC_lock_holder != NUMERIC_THREAD_ID(pthread_self())) +# endif +# endif /* GC_ASSERTIONS */ +# ifndef GC_WIN32_THREADS + GC_EXTERN volatile GC_bool GC_collecting; +# ifdef AO_HAVE_char_store +# define ENTER_GC() AO_char_store((unsigned char*)&GC_collecting, TRUE) +# define EXIT_GC() AO_char_store((unsigned char*)&GC_collecting, FALSE) +# else +# define ENTER_GC() (void)(GC_collecting = TRUE) +# define EXIT_GC() (void)(GC_collecting = FALSE) +# endif +# endif + GC_INNER void GC_lock(void); +# endif /* GC_PTHREADS */ +# if defined(GC_ALWAYS_MULTITHREADED) \ + && (defined(USE_PTHREAD_LOCKS) || defined(USE_SPIN_LOCK)) +# define GC_need_to_lock TRUE +# define set_need_to_lock() (void)0 +# else +# if defined(GC_ALWAYS_MULTITHREADED) && !defined(CPPCHECK) +# error Runtime initialization of GC lock is needed! +# endif +# undef GC_ALWAYS_MULTITHREADED + GC_EXTERN GC_bool GC_need_to_lock; +# ifdef THREAD_SANITIZER + /* To workaround TSan false positive (e.g., when */ + /* GC_pthread_create is called from multiple threads in */ + /* parallel), do not set GC_need_to_lock if it is already set. */ +# define set_need_to_lock() \ + (void)(*(GC_bool volatile *)&GC_need_to_lock \ + ? FALSE \ + : (GC_need_to_lock = TRUE)) +# else +# define set_need_to_lock() (void)(GC_need_to_lock = TRUE) + /* We are multi-threaded now. */ +# endif +# endif + +#if !defined(UNCOND_LOCK) + extern void GC_lock(void); + extern void GC_unlock(void); +# define UNCOND_LOCK() GC_lock() +# define UNCOND_UNLOCK() GC_unlock() +#endif + + EXTERN_C_END + +# else /* !THREADS */ +# define LOCK() (void)0 +# define UNLOCK() (void)0 +# ifdef GC_ASSERTIONS +# define I_HOLD_LOCK() TRUE +# define I_DONT_HOLD_LOCK() TRUE + /* Used only in positive assertions or to test whether */ + /* we still need to acquire the lock. TRUE works in */ + /* either case. */ +# endif +# endif /* !THREADS */ + +#if defined(UNCOND_LOCK) && !defined(LOCK) +# if (defined(LINT2) && defined(USE_PTHREAD_LOCKS)) \ + || defined(GC_ALWAYS_MULTITHREADED) + /* Instruct code analysis tools not to care about GC_need_to_lock */ + /* influence to LOCK/UNLOCK semantic. */ +# define LOCK() UNCOND_LOCK() +# define UNLOCK() UNCOND_UNLOCK() +# else + /* At least two thread running; need to lock. */ +# define LOCK() do { if (GC_need_to_lock) UNCOND_LOCK(); } while (0) +# define UNLOCK() do { if (GC_need_to_lock) UNCOND_UNLOCK(); } while (0) +# endif +#endif + +# ifndef ENTER_GC +# define ENTER_GC() +# define EXIT_GC() +# endif + +# ifndef DCL_LOCK_STATE +# define DCL_LOCK_STATE +# endif + +#endif /* GC_LOCKS_H */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/private/gc_pmark.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/private/gc_pmark.h new file mode 100644 index 000000000..f12851a55 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/private/gc_pmark.h @@ -0,0 +1,510 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 2001 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + */ + +/* Private declarations of GC marker data structures and macros */ + +/* + * Declarations of mark stack. Needed by marker and client supplied mark + * routines. Transitively include gc_priv.h. + */ +#ifndef GC_PMARK_H +#define GC_PMARK_H + +#if defined(HAVE_CONFIG_H) && !defined(GC_PRIVATE_H) +# include "config.h" +#endif + +#ifndef GC_BUILD +# define GC_BUILD +#endif + +#if (defined(__linux__) || defined(__GLIBC__) || defined(__GNU__)) \ + && !defined(_GNU_SOURCE) && defined(GC_PTHREADS) \ + && !defined(GC_NO_PTHREAD_SIGMASK) +# define _GNU_SOURCE 1 +#endif + +#if defined(KEEP_BACK_PTRS) || defined(PRINT_BLACK_LIST) +# include "dbg_mlc.h" +#endif + +#ifndef GC_MARK_H +# include "../gc_mark.h" +#endif + +#ifndef GC_PRIVATE_H +# include "gc_priv.h" +#endif + +EXTERN_C_BEGIN + +/* The real declarations of the following is in gc_priv.h, so that */ +/* we can avoid scanning the following table. */ +/* +mark_proc GC_mark_procs[MAX_MARK_PROCS]; +*/ + +#ifndef MARK_DESCR_OFFSET +# define MARK_DESCR_OFFSET sizeof(word) +#endif + +/* + * Mark descriptor stuff that should remain private for now, mostly + * because it's hard to export WORDSZ without including gcconfig.h. + */ +#define BITMAP_BITS (WORDSZ - GC_DS_TAG_BITS) +#define PROC(descr) \ + (GC_mark_procs[((descr) >> GC_DS_TAG_BITS) & (GC_MAX_MARK_PROCS-1)]) +#define ENV(descr) \ + ((descr) >> (GC_DS_TAG_BITS + GC_LOG_MAX_MARK_PROCS)) +#define MAX_ENV \ + (((word)1 << (WORDSZ - GC_DS_TAG_BITS - GC_LOG_MAX_MARK_PROCS)) - 1) + +GC_EXTERN unsigned GC_n_mark_procs; + +/* Number of mark stack entries to discard on overflow. */ +#define GC_MARK_STACK_DISCARDS (INITIAL_MARK_STACK_SIZE/8) + +GC_EXTERN size_t GC_mark_stack_size; + +#ifdef PARALLEL_MARK + /* + * Allow multiple threads to participate in the marking process. + * This works roughly as follows: + * The main mark stack never shrinks, but it can grow. + * + * The initiating threads holds the GC lock, and sets GC_help_wanted. + * + * Other threads: + * 1) update helper_count (while holding mark_lock.) + * 2) allocate a local mark stack + * repeatedly: + * 3) Steal a global mark stack entry by atomically replacing + * its descriptor with 0. + * 4) Copy it to the local stack. + * 5) Mark on the local stack until it is empty, or + * it may be profitable to copy it back. + * 6) If necessary, copy local stack to global one, + * holding mark lock. + * 7) Stop when the global mark stack is empty. + * 8) decrement helper_count (holding mark_lock). + * + * This is an experiment to see if we can do something along the lines + * of the University of Tokyo SGC in a less intrusive, though probably + * also less performant, way. + */ + + /* GC_mark_stack_top is protected by mark lock. */ + + /* + * GC_notify_all_marker() is used when GC_help_wanted is first set, + * when the last helper becomes inactive, + * when something is added to the global mark stack, and just after + * GC_mark_no is incremented. + * This could be split into multiple CVs (and probably should be to + * scale to really large numbers of processors.) + */ +#endif /* PARALLEL_MARK */ + +GC_INNER mse * GC_signal_mark_stack_overflow(mse *msp); + +/* Push the object obj with corresponding heap block header hhdr onto */ +/* the mark stack. */ +#define PUSH_OBJ(obj, hhdr, mark_stack_top, mark_stack_limit) \ + do { \ + word _descr = (hhdr) -> hb_descr; \ + GC_ASSERT(!HBLK_IS_FREE(hhdr)); \ + if (_descr != 0) { \ + mark_stack_top++; \ + if ((word)mark_stack_top >= (word)(mark_stack_limit)) { \ + mark_stack_top = GC_signal_mark_stack_overflow(mark_stack_top); \ + } \ + mark_stack_top -> mse_start = (obj); \ + mark_stack_top -> mse_descr.w = _descr; \ + } \ + } while (0) + +/* Push the contents of current onto the mark stack if it is a valid */ +/* ptr to a currently unmarked object. Mark it. */ +#define PUSH_CONTENTS(current, mark_stack_top, mark_stack_limit, source) \ + do { \ + hdr * my_hhdr; \ + HC_GET_HDR(current, my_hhdr, source); /* contains "break" */ \ + PUSH_CONTENTS_HDR(current, mark_stack_top, mark_stack_limit, \ + source, my_hhdr, TRUE); \ + } while (0) + +/* Set mark bit, exit (using "break" statement) if it is already set. */ +#ifdef USE_MARK_BYTES +# if defined(PARALLEL_MARK) && defined(AO_HAVE_char_store) \ + && !defined(AO_USE_PTHREAD_DEFS) + /* There is a race here, and we may set the bit twice in the */ + /* concurrent case. This can result in the object being pushed */ + /* twice. But that is only a performance issue. */ +# define SET_MARK_BIT_EXIT_IF_SET(hhdr, bit_no) \ + { /* cannot use do-while(0) here */ \ + volatile unsigned char * mark_byte_addr = \ + (unsigned char *)(hhdr)->hb_marks + (bit_no); \ + /* Unordered atomic load and store are sufficient here. */ \ + if (AO_char_load(mark_byte_addr) != 0) \ + break; /* go to the enclosing loop end */ \ + AO_char_store(mark_byte_addr, 1); \ + } +# else +# define SET_MARK_BIT_EXIT_IF_SET(hhdr, bit_no) \ + { /* cannot use do-while(0) here */ \ + char * mark_byte_addr = (char *)(hhdr)->hb_marks + (bit_no); \ + if (*mark_byte_addr != 0) break; /* go to the enclosing loop end */ \ + *mark_byte_addr = 1; \ + } +# endif /* !PARALLEL_MARK */ +#else +# ifdef PARALLEL_MARK + /* This is used only if we explicitly set USE_MARK_BITS. */ + /* The following may fail to exit even if the bit was already set. */ + /* For our uses, that's benign: */ +# ifdef THREAD_SANITIZER +# define OR_WORD_EXIT_IF_SET(addr, bits) \ + { /* cannot use do-while(0) here */ \ + if (!((word)AO_load((volatile AO_t *)(addr)) & (bits))) { \ + /* Atomic load is just to avoid TSan false positive. */ \ + AO_or((volatile AO_t *)(addr), (AO_t)(bits)); \ + } else { \ + break; /* go to the enclosing loop end */ \ + } \ + } +# else +# define OR_WORD_EXIT_IF_SET(addr, bits) \ + { /* cannot use do-while(0) here */ \ + if (!(*(addr) & (bits))) { \ + AO_or((volatile AO_t *)(addr), (AO_t)(bits)); \ + } else { \ + break; /* go to the enclosing loop end */ \ + } \ + } +# endif /* !THREAD_SANITIZER */ +# else +# define OR_WORD_EXIT_IF_SET(addr, bits) \ + { /* cannot use do-while(0) here */ \ + word old = *(addr); \ + word my_bits = (bits); \ + if ((old & my_bits) != 0) \ + break; /* go to the enclosing loop end */ \ + *(addr) = old | my_bits; \ + } +# endif /* !PARALLEL_MARK */ +# define SET_MARK_BIT_EXIT_IF_SET(hhdr, bit_no) \ + { /* cannot use do-while(0) here */ \ + word * mark_word_addr = (hhdr)->hb_marks + divWORDSZ(bit_no); \ + OR_WORD_EXIT_IF_SET(mark_word_addr, \ + (word)1 << modWORDSZ(bit_no)); /* contains "break" */ \ + } +#endif /* !USE_MARK_BYTES */ + +#ifdef PARALLEL_MARK +# define INCR_MARKS(hhdr) \ + AO_store(&hhdr->hb_n_marks, AO_load(&hhdr->hb_n_marks) + 1) +#else +# define INCR_MARKS(hhdr) (void)(++hhdr->hb_n_marks) +#endif + +#ifdef ENABLE_TRACE +# define TRACE(source, cmd) \ + if (GC_trace_addr != 0 && (ptr_t)(source) == GC_trace_addr) cmd +# define TRACE_TARGET(target, cmd) \ + if (GC_trace_addr != 0 && (target) == *(ptr_t *)GC_trace_addr) cmd +#else +# define TRACE(source, cmd) +# define TRACE_TARGET(source, cmd) +#endif + +#if defined(I386) && defined(__GNUC__) && !defined(NACL) +# define LONG_MULT(hprod, lprod, x, y) \ + do { \ + __asm__ __volatile__("mull %2" : "=a"(lprod), "=d"(hprod) \ + : "g"(y), "0"(x)); \ + } while (0) +#else +# if defined(__int64) && !defined(__GNUC__) && !defined(CPPCHECK) +# define ULONG_MULT_T unsigned __int64 +# else +# define ULONG_MULT_T unsigned long long +# endif +# define LONG_MULT(hprod, lprod, x, y) \ + do { \ + ULONG_MULT_T prod = (ULONG_MULT_T)(x) * (ULONG_MULT_T)(y); \ + GC_STATIC_ASSERT(sizeof(x) + sizeof(y) <= sizeof(prod)); \ + hprod = prod >> 32; \ + lprod = (unsigned32)prod; \ + } while (0) +#endif /* !I386 */ + +/* If the mark bit corresponding to current is not set, set it, and */ +/* push the contents of the object on the mark stack. Current points */ +/* to the beginning of the object. We rely on the fact that the */ +/* preceding header calculation will succeed for a pointer past the */ +/* first page of an object, only if it is in fact a valid pointer */ +/* to the object. Thus we can omit the otherwise necessary tests */ +/* here. Note in particular that the "displ" value is the displacement */ +/* from the beginning of the heap block, which may itself be in the */ +/* interior of a large object. */ +#ifdef MARK_BIT_PER_GRANULE +# define PUSH_CONTENTS_HDR(current, mark_stack_top, mark_stack_limit, \ + source, hhdr, do_offset_check) \ + do { \ + size_t displ = HBLKDISPL(current); /* Displacement in block; in bytes. */\ + /* displ is always within range. If current doesn't point to */ \ + /* first block, then we are in the all_interior_pointers case, and */ \ + /* it is safe to use any displacement value. */ \ + size_t gran_displ = BYTES_TO_GRANULES(displ); \ + size_t gran_offset = hhdr -> hb_map[gran_displ]; \ + size_t byte_offset = displ & (GRANULE_BYTES - 1); \ + ptr_t base = (ptr_t)(current); \ + /* The following always fails for large block references. */ \ + if (EXPECT((gran_offset | byte_offset) != 0, FALSE)) { \ + if ((hhdr -> hb_flags & LARGE_BLOCK) != 0) { \ + /* gran_offset is bogus. */ \ + size_t obj_displ; \ + base = (ptr_t)(hhdr -> hb_block); \ + obj_displ = (ptr_t)(current) - base; \ + if (obj_displ != displ) { \ + GC_ASSERT(obj_displ < hhdr -> hb_sz); \ + /* Must be in all_interior_pointer case, not first block */ \ + /* already did validity check on cache miss. */ \ + } else { \ + if (do_offset_check && !GC_valid_offsets[obj_displ]) { \ + GC_ADD_TO_BLACK_LIST_NORMAL(current, source); \ + break; /* go to the end of PUSH_CONTENTS_HDR */ \ + } \ + } \ + gran_displ = 0; \ + GC_ASSERT(hhdr -> hb_sz > HBLKSIZE || \ + hhdr -> hb_block == HBLKPTR(current)); \ + GC_ASSERT((word)hhdr->hb_block <= (word)(current)); \ + } else { \ + size_t obj_displ = GRANULES_TO_BYTES(gran_offset) \ + + byte_offset; \ + if (do_offset_check && !GC_valid_offsets[obj_displ]) { \ + GC_ADD_TO_BLACK_LIST_NORMAL(current, source); \ + break; \ + } \ + gran_displ -= gran_offset; \ + base -= obj_displ; \ + } \ + } \ + GC_ASSERT(hhdr == GC_find_header(base)); \ + GC_ASSERT(gran_displ % BYTES_TO_GRANULES(hhdr -> hb_sz) == 0); \ + TRACE(source, GC_log_printf("GC #%u: passed validity tests\n", \ + (unsigned)GC_gc_no)); \ + SET_MARK_BIT_EXIT_IF_SET(hhdr, gran_displ); \ + TRACE(source, GC_log_printf("GC #%u: previously unmarked\n", \ + (unsigned)GC_gc_no)); \ + TRACE_TARGET(base, \ + GC_log_printf("GC #%u: marking %p from %p instead\n", \ + (unsigned)GC_gc_no, (void *)base, (void *)(source))); \ + INCR_MARKS(hhdr); \ + GC_STORE_BACK_PTR((ptr_t)(source), base); \ + PUSH_OBJ(base, hhdr, mark_stack_top, mark_stack_limit); \ + } while (0) +#endif /* MARK_BIT_PER_GRANULE */ + +#ifdef MARK_BIT_PER_OBJ +# define PUSH_CONTENTS_HDR(current, mark_stack_top, mark_stack_limit, \ + source, hhdr, do_offset_check) \ + do { \ + size_t displ = HBLKDISPL(current); /* Displacement in block; in bytes. */\ + unsigned32 low_prod, high_prod; \ + unsigned32 inv_sz = hhdr -> hb_inv_sz; \ + ptr_t base = (ptr_t)(current); \ + LONG_MULT(high_prod, low_prod, (unsigned32)displ, inv_sz); \ + /* product is > and within sz_in_bytes of displ * sz_in_bytes * 2**32 */ \ + if (EXPECT(low_prod >> 16 != 0, FALSE)) { \ + /* FIXME: fails if offset is a multiple of HBLKSIZE which becomes 0 */ \ + if (inv_sz == LARGE_INV_SZ) { \ + size_t obj_displ; \ + base = (ptr_t)(hhdr -> hb_block); \ + obj_displ = (ptr_t)(current) - base; \ + if (obj_displ != displ) { \ + GC_ASSERT(obj_displ < hhdr -> hb_sz); \ + /* Must be in all_interior_pointer case, not first block */ \ + /* already did validity check on cache miss. */ \ + } else { \ + if (do_offset_check && !GC_valid_offsets[obj_displ]) { \ + GC_ADD_TO_BLACK_LIST_NORMAL(current, source); \ + break; /* go to the end of PUSH_CONTENTS_HDR */ \ + } \ + } \ + GC_ASSERT(hhdr -> hb_sz > HBLKSIZE || \ + hhdr -> hb_block == HBLKPTR(current)); \ + GC_ASSERT((word)hhdr->hb_block < (word)(current)); \ + } else { \ + size_t obj_displ; \ + /* Accurate enough if HBLKSIZE <= 2**15. */ \ + GC_STATIC_ASSERT(HBLKSIZE <= (1 << 15)); \ + obj_displ = (((low_prod >> 16) + 1) * (size_t)hhdr->hb_sz) >> 16; \ + if (do_offset_check && !GC_valid_offsets[obj_displ]) { \ + GC_ADD_TO_BLACK_LIST_NORMAL(current, source); \ + break; \ + } \ + base -= obj_displ; \ + } \ + } \ + /* May get here for pointer to start of block not at */ \ + /* beginning of object. If so, it's valid, and we're fine. */ \ + GC_ASSERT(high_prod <= HBLK_OBJS(hhdr -> hb_sz)); \ + TRACE(source, GC_log_printf("GC #%u: passed validity tests\n", \ + (unsigned)GC_gc_no)); \ + SET_MARK_BIT_EXIT_IF_SET(hhdr, high_prod); \ + TRACE(source, GC_log_printf("GC #%u: previously unmarked\n", \ + (unsigned)GC_gc_no)); \ + TRACE_TARGET(base, \ + GC_log_printf("GC #%u: marking %p from %p instead\n", \ + (unsigned)GC_gc_no, (void *)base, (void *)(source))); \ + INCR_MARKS(hhdr); \ + GC_STORE_BACK_PTR((ptr_t)(source), base); \ + PUSH_OBJ(base, hhdr, mark_stack_top, mark_stack_limit); \ + } while (0) +#endif /* MARK_BIT_PER_OBJ */ + +#if defined(PRINT_BLACK_LIST) || defined(KEEP_BACK_PTRS) +# define PUSH_ONE_CHECKED_STACK(p, source) \ + GC_mark_and_push_stack((ptr_t)(p), (ptr_t)(source)) +#else +# define PUSH_ONE_CHECKED_STACK(p, source) \ + GC_mark_and_push_stack((ptr_t)(p)) +#endif + +/* + * Push a single value onto mark stack. Mark from the object pointed to by p. + * Invoke FIXUP_POINTER(p) before any further processing. + * P is considered valid even if it is an interior pointer. + * Previously marked objects are not pushed. Hence we make progress even + * if the mark stack overflows. + */ + +#ifdef NEED_FIXUP_POINTER + /* Try both the raw version and the fixed up one. */ +# define GC_PUSH_ONE_STACK(p, source) \ + do { \ + if ((word)(p) >= (word)GC_least_plausible_heap_addr \ + && (word)(p) < (word)GC_greatest_plausible_heap_addr) { \ + PUSH_ONE_CHECKED_STACK(p, source); \ + } \ + FIXUP_POINTER(p); \ + if ((word)(p) >= (word)GC_least_plausible_heap_addr \ + && (word)(p) < (word)GC_greatest_plausible_heap_addr) { \ + PUSH_ONE_CHECKED_STACK(p, source); \ + } \ + } while (0) +#else /* !NEED_FIXUP_POINTER */ +# define GC_PUSH_ONE_STACK(p, source) \ + do { \ + if ((word)(p) >= (word)GC_least_plausible_heap_addr \ + && (word)(p) < (word)GC_greatest_plausible_heap_addr) { \ + PUSH_ONE_CHECKED_STACK(p, source); \ + } \ + } while (0) +#endif + +/* As above, but interior pointer recognition as for normal heap pointers. */ +#define GC_PUSH_ONE_HEAP(p,source,mark_stack_top) \ + do { \ + FIXUP_POINTER(p); \ + if ((word)(p) >= (word)GC_least_plausible_heap_addr \ + && (word)(p) < (word)GC_greatest_plausible_heap_addr) \ + mark_stack_top = GC_mark_and_push((void *)(p), mark_stack_top, \ + GC_mark_stack_limit, (void * *)(source)); \ + } while (0) + +/* Mark starting at mark stack entry top (incl.) down to */ +/* mark stack entry bottom (incl.). Stop after performing */ +/* about one page worth of work. Return the new mark stack */ +/* top entry. */ +GC_INNER mse * GC_mark_from(mse * top, mse * bottom, mse *limit); + +#define MARK_FROM_MARK_STACK() \ + GC_mark_stack_top = GC_mark_from(GC_mark_stack_top, \ + GC_mark_stack, \ + GC_mark_stack + GC_mark_stack_size); + +#define GC_mark_stack_empty() ((word)GC_mark_stack_top < (word)GC_mark_stack) + +/* + * Mark from one finalizable object using the specified + * mark proc. May not mark the object pointed to by + * real_ptr. That is the job of the caller, if appropriate. + * Note that this is called with the mutator running, but + * with us holding the allocation lock. This is safe only if the + * mutator needs the allocation lock to reveal hidden pointers. + * FIXME: Why do we need the GC_mark_state test below? + */ +#define GC_MARK_FO(real_ptr, mark_proc) \ + do { \ + (*(mark_proc))(real_ptr); \ + while (!GC_mark_stack_empty()) MARK_FROM_MARK_STACK(); \ + if (GC_mark_state != MS_NONE) { \ + GC_set_mark_bit(real_ptr); \ + while (!GC_mark_some((ptr_t)0)) { /* empty */ } \ + } \ + } while (0) + +GC_EXTERN GC_bool GC_mark_stack_too_small; + /* We need a larger mark stack. May be */ + /* set by client supplied mark routines.*/ + +typedef int mark_state_t; /* Current state of marking, as follows:*/ + /* Used to remember where we are during */ + /* concurrent marking. */ + + /* We say something is dirty if it was */ + /* written since the last time we */ + /* retrieved dirty bits. We say it's */ + /* grungy if it was marked dirty in the */ + /* last set of bits we retrieved. */ + + /* Invariant "I": all roots and marked */ + /* objects p are either dirty, or point */ + /* to objects q that are either marked */ + /* or a pointer to q appears in a range */ + /* on the mark stack. */ + +#define MS_NONE 0 /* No marking in progress. "I" holds. */ + /* Mark stack is empty. */ + +#define MS_PUSH_RESCUERS 1 /* Rescuing objects are currently */ + /* being pushed. "I" holds, except */ + /* that grungy roots may point to */ + /* unmarked objects, as may marked */ + /* grungy objects above scan_ptr. */ + +#define MS_PUSH_UNCOLLECTABLE 2 /* "I" holds, except that marked */ + /* uncollectible objects above scan_ptr */ + /* may point to unmarked objects. */ + /* Roots may point to unmarked objects */ + +#define MS_ROOTS_PUSHED 3 /* "I" holds, mark stack may be nonempty. */ + +#define MS_PARTIALLY_INVALID 4 /* "I" may not hold, e.g. because of */ + /* the mark stack overflow. However */ + /* marked heap objects below scan_ptr */ + /* point to marked or stacked objects. */ + +#define MS_INVALID 5 /* "I" may not hold. */ + +GC_EXTERN mark_state_t GC_mark_state; + +EXTERN_C_END + +#endif /* GC_PMARK_H */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/private/gc_priv.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/private/gc_priv.h new file mode 100644 index 000000000..6acaabb4d --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/private/gc_priv.h @@ -0,0 +1,2698 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999-2004 Hewlett-Packard Development Company, L.P. + * + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#ifndef GC_PRIVATE_H +#define GC_PRIVATE_H + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifndef GC_BUILD +# define GC_BUILD +#endif + +#if (defined(__linux__) || defined(__GLIBC__) || defined(__GNU__)) \ + && !defined(_GNU_SOURCE) + /* Can't test LINUX, since this must be defined before other includes. */ +# define _GNU_SOURCE 1 +#endif + +#if (defined(DGUX) && defined(GC_THREADS) || defined(DGUX386_THREADS) \ + || defined(GC_DGUX386_THREADS)) && !defined(_USING_POSIX4A_DRAFT10) +# define _USING_POSIX4A_DRAFT10 1 +#endif + +#if defined(__MINGW32__) && !defined(__MINGW_EXCPT_DEFINE_PSDK) \ + && defined(__i386__) && defined(GC_EXTERN) /* defined in gc.c */ + /* See the description in mark.c. */ +# define __MINGW_EXCPT_DEFINE_PSDK 1 +#endif + +# if defined(NO_DEBUGGING) && !defined(GC_ASSERTIONS) && !defined(NDEBUG) + /* To turn off assertion checking (in atomic_ops.h). */ +# define NDEBUG 1 +# endif + +#ifndef GC_H +# include "../gc.h" +#endif + +#include +#if !defined(sony_news) +# include +#endif + +#ifdef DGUX +# include +# include +# include +#endif /* DGUX */ + +#ifdef BSD_TIME +# include +# include +# include +#endif /* BSD_TIME */ + +#ifdef PARALLEL_MARK +# define AO_REQUIRE_CAS +# if !defined(__GNUC__) && !defined(AO_ASSUME_WINDOWS98) +# define AO_ASSUME_WINDOWS98 +# endif +#endif + +#ifndef GC_TINY_FL_H +# include "../gc_tiny_fl.h" +#endif + +#ifndef GC_MARK_H +# include "../gc_mark.h" +#endif + +typedef GC_word word; +typedef GC_signed_word signed_word; +typedef unsigned int unsigned32; + +typedef int GC_bool; +#define TRUE 1 +#define FALSE 0 + +typedef char * ptr_t; /* A generic pointer to which we can add */ + /* byte displacements and which can be used */ + /* for address comparisons. */ + +#ifndef SIZE_MAX +# include +#endif +#if defined(SIZE_MAX) && !defined(CPPCHECK) +# define GC_SIZE_MAX ((size_t)SIZE_MAX) + /* Extra cast to workaround some buggy SIZE_MAX definitions. */ +#else +# define GC_SIZE_MAX (~(size_t)0) +#endif + +#if GC_GNUC_PREREQ(3, 0) && !defined(LINT2) +# define EXPECT(expr, outcome) __builtin_expect(expr,outcome) + /* Equivalent to (expr), but predict that usually (expr)==outcome. */ +#else +# define EXPECT(expr, outcome) (expr) +#endif /* __GNUC__ */ + +/* Saturated addition of size_t values. Used to avoid value wrap */ +/* around on overflow. The arguments should have no side effects. */ +#define SIZET_SAT_ADD(a, b) \ + (EXPECT((a) < GC_SIZE_MAX - (b), TRUE) ? (a) + (b) : GC_SIZE_MAX) + +#ifndef GCCONFIG_H +# include "gcconfig.h" +#endif + +#if !defined(GC_ATOMIC_UNCOLLECTABLE) && defined(ATOMIC_UNCOLLECTABLE) + /* For compatibility with old-style naming. */ +# define GC_ATOMIC_UNCOLLECTABLE +#endif + +#ifndef GC_INNER + /* This tagging macro must be used at the start of every variable */ + /* definition which is declared with GC_EXTERN. Should be also used */ + /* for the GC-scope function definitions and prototypes. Must not be */ + /* used in gcconfig.h. Shouldn't be used for the debugging-only */ + /* functions. Currently, not used for the functions declared in or */ + /* called from the "dated" source files (located in "extra" folder). */ +# if defined(GC_DLL) && defined(__GNUC__) && !defined(MSWIN32) \ + && !defined(MSWINCE) && !defined(CYGWIN32) +# if GC_GNUC_PREREQ(4, 0) && !defined(GC_NO_VISIBILITY) + /* See the corresponding GC_API definition. */ +# define GC_INNER __attribute__((__visibility__("hidden"))) +# else + /* The attribute is unsupported. */ +# define GC_INNER /* empty */ +# endif +# else +# define GC_INNER /* empty */ +# endif + +# define GC_EXTERN extern GC_INNER + /* Used only for the GC-scope variables (prefixed with "GC_") */ + /* declared in the header files. Must not be used for thread-local */ + /* variables. Must not be used in gcconfig.h. Shouldn't be used for */ + /* the debugging-only or profiling-only variables. Currently, not */ + /* used for the variables accessed from the "dated" source files */ + /* (specific.c/h, and in the "extra" folder). */ + /* The corresponding variable definition must start with GC_INNER. */ +#endif /* !GC_INNER */ + +#ifdef __cplusplus + /* Register storage specifier is deprecated in C++11. */ +# define REGISTER /* empty */ +#else + /* Used only for several local variables in the performance-critical */ + /* functions. Should not be used for new code. */ +# define REGISTER register +#endif + +#ifndef HEADERS_H +# include "gc_hdrs.h" +#endif + +#ifndef GC_ATTR_NO_SANITIZE_ADDR +# ifndef ADDRESS_SANITIZER +# define GC_ATTR_NO_SANITIZE_ADDR /* empty */ +# elif GC_CLANG_PREREQ(3, 8) +# define GC_ATTR_NO_SANITIZE_ADDR __attribute__((no_sanitize("address"))) +# else +# define GC_ATTR_NO_SANITIZE_ADDR __attribute__((no_sanitize_address)) +# endif +#endif /* !GC_ATTR_NO_SANITIZE_ADDR */ + +#ifndef GC_ATTR_NO_SANITIZE_MEMORY +# ifndef MEMORY_SANITIZER +# define GC_ATTR_NO_SANITIZE_MEMORY /* empty */ +# elif GC_CLANG_PREREQ(3, 8) +# define GC_ATTR_NO_SANITIZE_MEMORY __attribute__((no_sanitize("memory"))) +# else +# define GC_ATTR_NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory)) +# endif +#endif /* !GC_ATTR_NO_SANITIZE_MEMORY */ + +#ifndef GC_ATTR_NO_SANITIZE_THREAD +# ifndef THREAD_SANITIZER +# define GC_ATTR_NO_SANITIZE_THREAD /* empty */ +# elif GC_CLANG_PREREQ(3, 8) +# define GC_ATTR_NO_SANITIZE_THREAD __attribute__((no_sanitize("thread"))) +# else +# define GC_ATTR_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread)) +# endif +#endif /* !GC_ATTR_NO_SANITIZE_THREAD */ + +#ifndef GC_ATTR_UNUSED +# if GC_GNUC_PREREQ(3, 4) +# define GC_ATTR_UNUSED __attribute__((__unused__)) +# else +# define GC_ATTR_UNUSED /* empty */ +# endif +#endif /* !GC_ATTR_UNUSED */ + +#ifdef HAVE_CONFIG_H + /* The "inline" keyword is determined by Autoconf AC_C_INLINE. */ +# define GC_INLINE static inline +#elif defined(_MSC_VER) || defined(__INTEL_COMPILER) || defined(__DMC__) \ + || (GC_GNUC_PREREQ(3, 0) && defined(__STRICT_ANSI__)) \ + || defined(__WATCOMC__) +# define GC_INLINE static __inline +#elif GC_GNUC_PREREQ(3, 0) || defined(__sun) +# define GC_INLINE static inline +#else +# define GC_INLINE static +#endif + +#ifndef GC_ATTR_NOINLINE +# if GC_GNUC_PREREQ(4, 0) +# define GC_ATTR_NOINLINE __attribute__((__noinline__)) +# elif _MSC_VER >= 1400 +# define GC_ATTR_NOINLINE __declspec(noinline) +# else +# define GC_ATTR_NOINLINE /* empty */ +# endif +#endif + +#ifndef GC_API_OSCALL + /* This is used to identify GC routines called by name from OS. */ +# if defined(__GNUC__) +# if GC_GNUC_PREREQ(4, 0) && !defined(GC_NO_VISIBILITY) + /* Same as GC_API if GC_DLL. */ +# define GC_API_OSCALL extern __attribute__((__visibility__("default"))) +# else + /* The attribute is unsupported. */ +# define GC_API_OSCALL extern +# endif +# else +# define GC_API_OSCALL GC_API +# endif +#endif + +#ifndef GC_API_PRIV +# define GC_API_PRIV GC_API +#endif + +#if defined(THREADS) +# include "gc_atomic_ops.h" +#endif + +#ifndef GC_LOCKS_H +# include "gc_locks.h" +#endif + +#define ONES ((word)(signed_word)(-1)) + +# ifdef STACK_GROWS_DOWN +# define COOLER_THAN > +# define HOTTER_THAN < +# define MAKE_COOLER(x,y) if ((word)((x) + (y)) > (word)(x)) {(x) += (y);} \ + else (x) = (ptr_t)ONES +# define MAKE_HOTTER(x,y) (x) -= (y) +# else +# define COOLER_THAN < +# define HOTTER_THAN > +# define MAKE_COOLER(x,y) if ((word)((x) - (y)) < (word)(x)) {(x) -= (y);} \ + else (x) = 0 +# define MAKE_HOTTER(x,y) (x) += (y) +# endif + +#if defined(AMIGA) && defined(__SASC) +# define GC_FAR __far +#else +# define GC_FAR +#endif + + +/*********************************/ +/* */ +/* Definitions for conservative */ +/* collector */ +/* */ +/*********************************/ + +/*********************************/ +/* */ +/* Easily changeable parameters */ +/* */ +/*********************************/ + +/* #define ALL_INTERIOR_POINTERS */ + /* Forces all pointers into the interior of an */ + /* object to be considered valid. Also causes the */ + /* sizes of all objects to be inflated by at least */ + /* one byte. This should suffice to guarantee */ + /* that in the presence of a compiler that does */ + /* not perform garbage-collector-unsafe */ + /* optimizations, all portable, strictly ANSI */ + /* conforming C programs should be safely usable */ + /* with malloc replaced by GC_malloc and free */ + /* calls removed. There are several disadvantages: */ + /* 1. There are probably no interesting, portable, */ + /* strictly ANSI conforming C programs. */ + /* 2. This option makes it hard for the collector */ + /* to allocate space that is not "pointed to" */ + /* by integers, etc. Under SunOS 4.X with a */ + /* statically linked libc, we empirically */ + /* observed that it would be difficult to */ + /* allocate individual objects larger than 100K. */ + /* Even if only smaller objects are allocated, */ + /* more swap space is likely to be needed. */ + /* Fortunately, much of this will never be */ + /* touched. */ + /* If you can easily avoid using this option, do. */ + /* If not, try to keep individual objects small. */ + /* This is now really controlled at startup, */ + /* through GC_all_interior_pointers. */ + +EXTERN_C_BEGIN + +#ifndef GC_NO_FINALIZATION +# define GC_INVOKE_FINALIZERS() GC_notify_or_invoke_finalizers() + GC_INNER void GC_notify_or_invoke_finalizers(void); + /* If GC_finalize_on_demand is not set, invoke */ + /* eligible finalizers. Otherwise: */ + /* Call *GC_finalizer_notifier if there are */ + /* finalizers to be run, and we haven't called */ + /* this procedure yet this GC cycle. */ + + GC_INNER void GC_finalize(void); + /* Perform all indicated finalization actions */ + /* on unmarked objects. */ + /* Unreachable finalizable objects are enqueued */ + /* for processing by GC_invoke_finalizers. */ + /* Invoked with lock. */ + +# ifndef GC_TOGGLE_REFS_NOT_NEEDED + GC_INNER void GC_process_togglerefs(void); + /* Process the toggle-refs before GC starts. */ +# endif +# ifndef SMALL_CONFIG + GC_INNER void GC_print_finalization_stats(void); +# endif +#else +# define GC_INVOKE_FINALIZERS() (void)0 +#endif /* GC_NO_FINALIZATION */ + +#if !defined(DONT_ADD_BYTE_AT_END) +# ifdef LINT2 + /* Explicitly instruct the code analysis tool that */ + /* GC_all_interior_pointers is assumed to have only 0 or 1 value. */ +# define EXTRA_BYTES ((size_t)(GC_all_interior_pointers? 1 : 0)) +# else +# define EXTRA_BYTES (size_t)GC_all_interior_pointers +# endif +# define MAX_EXTRA_BYTES 1 +#else +# define EXTRA_BYTES 0 +# define MAX_EXTRA_BYTES 0 +#endif + + +# ifndef LARGE_CONFIG +# define MINHINCR 16 /* Minimum heap increment, in blocks of HBLKSIZE */ + /* Must be multiple of largest page size. */ +# define MAXHINCR 2048 /* Maximum heap increment, in blocks */ +# else +# define MINHINCR 64 +# define MAXHINCR 4096 +# endif + +# define BL_LIMIT GC_black_list_spacing + /* If we need a block of N bytes, and we have */ + /* a block of N + BL_LIMIT bytes available, */ + /* and N > BL_LIMIT, */ + /* but all possible positions in it are */ + /* blacklisted, we just use it anyway (and */ + /* print a warning, if warnings are enabled). */ + /* This risks subsequently leaking the block */ + /* due to a false reference. But not using */ + /* the block risks unreasonable immediate */ + /* heap growth. */ + +/*********************************/ +/* */ +/* Stack saving for debugging */ +/* */ +/*********************************/ + +#ifdef NEED_CALLINFO + struct callinfo { + word ci_pc; /* Caller, not callee, pc */ +# if NARGS > 0 + word ci_arg[NARGS]; /* bit-wise complement to avoid retention */ +# endif +# if (NFRAMES * (NARGS + 1)) % 2 == 1 + /* Likely alignment problem. */ + word ci_dummy; +# endif + }; +#endif + +#ifdef SAVE_CALL_CHAIN + /* Fill in the pc and argument information for up to NFRAMES of my */ + /* callers. Ignore my frame and my callers frame. */ + GC_INNER void GC_save_callers(struct callinfo info[NFRAMES]); + GC_INNER void GC_print_callers(struct callinfo info[NFRAMES]); +#endif + +EXTERN_C_END + +/*********************************/ +/* */ +/* OS interface routines */ +/* */ +/*********************************/ + +#ifndef NO_CLOCK +#ifdef BSD_TIME +# undef CLOCK_TYPE +# undef GET_TIME +# undef MS_TIME_DIFF +# define CLOCK_TYPE struct timeval +# define GET_TIME(x) \ + do { \ + struct rusage rusage; \ + getrusage(RUSAGE_SELF, &rusage); \ + x = rusage.ru_utime; \ + } while (0) +# define MS_TIME_DIFF(a,b) ((unsigned long)(a.tv_sec - b.tv_sec) * 1000 \ + + (unsigned long)(a.tv_usec - b.tv_usec) / 1000) +# define NS_TIME_DIFF(a,b) ((unsigned long long)(a.tv_sec - b.tv_sec) * 1000000000 \ + + (unsigned long long)(a.tv_usec - b.tv_usec) * 1000) +#elif defined(MSWIN32) || defined(MSWINCE) +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +# endif +# define NOSERVICE +# include +# include +# define CLOCK_TYPE LONGLONG +# define GET_TIME(x) \ + do { \ + LARGE_INTEGER freq, t; \ + QueryPerformanceFrequency(&freq); \ + QueryPerformanceCounter(&t); \ + x = (LONGLONG)(t.QuadPart * (1000000000.0 / (double)freq.QuadPart)); \ + } while (0) +# define MS_TIME_DIFF(a,b) ((unsigned long)(((a) - (b)) / 1000000)) +# define NS_TIME_DIFF(a,b) ((a) - (b)) +#elif defined(NN_PLATFORM_CTR) +# define CLOCK_TYPE long long + EXTERN_C_BEGIN + CLOCK_TYPE n3ds_get_system_tick(void); + CLOCK_TYPE n3ds_convert_tick_to_ms(CLOCK_TYPE tick); + EXTERN_C_END +# define GET_TIME(x) (void)(x = n3ds_get_system_tick()) +# define MS_TIME_DIFF(a,b) ((long)n3ds_convert_tick_to_ms((a)-(b))) +# define NS_TIME_DIFF(a,b) ((long long)n3ds_convert_tick_to_ms((a)-(b)) * 1000000) +#else /* !BSD_TIME && !NN_PLATFORM_CTR && !MSWIN32 && !MSWINCE */ +# include +# if defined(FREEBSD) && !defined(CLOCKS_PER_SEC) +# include +# define CLOCKS_PER_SEC CLK_TCK +# endif +# if !defined(CLOCKS_PER_SEC) +# define CLOCKS_PER_SEC 1000000 + /* This is technically a bug in the implementation. */ + /* ANSI requires that CLOCKS_PER_SEC be defined. But at least */ + /* under SunOS 4.1.1, it isn't. Also note that the combination of */ + /* ANSI C and POSIX is incredibly gross here. The type clock_t */ + /* is used by both clock() and times(). But on some machines */ + /* these use different notions of a clock tick, CLOCKS_PER_SEC */ + /* seems to apply only to clock. Hence we use it here. On many */ + /* machines, including SunOS, clock actually uses units of */ + /* microseconds (which are not really clock ticks). */ +# endif +# define CLOCK_TYPE clock_t +# define GET_TIME(x) (void)(x = clock()) +# define MS_TIME_DIFF(a,b) (CLOCKS_PER_SEC % 1000 == 0 ? \ + (unsigned long)((a) - (b)) / (unsigned long)(CLOCKS_PER_SEC / 1000) \ + : ((unsigned long)((a) - (b)) * 1000) / (unsigned long)CLOCKS_PER_SEC) +# define NS_TIME_DIFF(a,b) (((unsigned long long)((a) - (b)) * 1000000000) / (unsigned long long)CLOCKS_PER_SEC) + /* Avoid using double type since some targets (like ARM) might */ + /* require -lm option for double-to-long conversion. */ +#endif /* !BSD_TIME && !MSWIN32 */ +#endif /* !NO_CLOCK */ + +/* We use bzero and bcopy internally. They may not be available. */ +# if defined(SPARC) && defined(SUNOS4) \ + || (defined(M68K) && defined(NEXT)) || defined(VAX) +# define BCOPY_EXISTS +# elif defined(AMIGA) || defined(DARWIN) +# include +# define BCOPY_EXISTS +# elif defined(MACOS) && defined(POWERPC) +# include +# define bcopy(x,y,n) BlockMoveData(x, y, n) +# define bzero(x,n) BlockZero(x, n) +# define BCOPY_EXISTS +# endif + +# if !defined(BCOPY_EXISTS) || defined(CPPCHECK) +# include +# define BCOPY(x,y,n) memcpy(y, x, (size_t)(n)) +# define BZERO(x,n) memset(x, 0, (size_t)(n)) +# else +# define BCOPY(x,y,n) bcopy((void *)(x),(void *)(y),(size_t)(n)) +# define BZERO(x,n) bzero((void *)(x),(size_t)(n)) +# endif + +#ifdef PCR +# include "th/PCR_ThCtl.h" +#endif + +EXTERN_C_BEGIN + +/* + * Stop and restart mutator threads. + */ +# ifdef PCR +# define STOP_WORLD() \ + PCR_ThCtl_SetExclusiveMode(PCR_ThCtl_ExclusiveMode_stopNormal, \ + PCR_allSigsBlocked, \ + PCR_waitForever) +# define START_WORLD() \ + PCR_ThCtl_SetExclusiveMode(PCR_ThCtl_ExclusiveMode_null, \ + PCR_allSigsBlocked, \ + PCR_waitForever) +# else +# if defined(NN_PLATFORM_CTR) || defined(NINTENDO_SWITCH) \ + || defined(GC_WIN32_THREADS) || defined(GC_PTHREADS) + GC_INNER void GC_stop_world(void); + GC_INNER void GC_start_world(void); +# define STOP_WORLD() GC_stop_world() +# define START_WORLD() GC_start_world() +# else + /* Just do a sanity check: we are not inside GC_do_blocking(). */ +# define STOP_WORLD() GC_ASSERT(GC_blocked_sp == NULL) +# define START_WORLD() +# endif +# endif + +#ifdef THREADS + GC_EXTERN GC_on_thread_event_proc GC_on_thread_event; +#endif + +/* Abandon ship */ +# if defined(SMALL_CONFIG) || defined(PCR) +# define GC_on_abort(msg) (void)0 /* be silent on abort */ +# else + GC_API_PRIV GC_abort_func GC_on_abort; +# endif +# if defined(CPPCHECK) +# define ABORT(msg) { GC_on_abort(msg); abort(); } +# elif defined(PCR) +# define ABORT(s) PCR_Base_Panic(s) +# else +# if defined(MSWINCE) && !defined(DebugBreak) \ + && (!defined(UNDER_CE) || (defined(__MINGW32CE__) && !defined(ARM32))) + /* This simplifies linking for WinCE (and, probably, doesn't */ + /* hurt debugging much); use -DDebugBreak=DebugBreak to override */ + /* this behavior if really needed. This is also a workaround for */ + /* x86mingw32ce toolchain (if it is still declaring DebugBreak() */ + /* instead of defining it as a macro). */ +# define DebugBreak() _exit(-1) /* there is no abort() in WinCE */ +# endif +# if defined(MSWIN32) && (defined(NO_DEBUGGING) || defined(LINT2)) + /* A more user-friendly abort after showing fatal message. */ +# define ABORT(msg) (GC_on_abort(msg), _exit(-1)) + /* Exit on error without running "at-exit" callbacks. */ +# elif defined(MSWINCE) && defined(NO_DEBUGGING) +# define ABORT(msg) (GC_on_abort(msg), ExitProcess(-1)) +# elif defined(MSWIN32) || defined(MSWINCE) +# if defined(_CrtDbgBreak) && defined(_DEBUG) && defined(_MSC_VER) +# define ABORT(msg) { GC_on_abort(msg); \ + _CrtDbgBreak() /* __debugbreak() */; } +# else +# define ABORT(msg) { GC_on_abort(msg); __debugbreak(); } + /* Note that: on a WinCE box, this could be silently */ + /* ignored (i.e., the program is not aborted); */ + /* DebugBreak is a statement in some toolchains. */ +# endif +# else +# define ABORT(msg) (GC_on_abort(msg), abort()) +# endif /* !MSWIN32 */ +# endif /* !PCR */ + +/* For abort message with 1-3 arguments. C_msg and C_fmt should be */ +/* literals. C_msg should not contain format specifiers. Arguments */ +/* should match their format specifiers. */ +#define ABORT_ARG1(C_msg, C_fmt, arg1) \ + do { \ + GC_INFOLOG_PRINTF(C_msg /* + */ C_fmt "\n", arg1); \ + ABORT(C_msg); \ + } while (0) +#define ABORT_ARG2(C_msg, C_fmt, arg1, arg2) \ + do { \ + GC_INFOLOG_PRINTF(C_msg /* + */ C_fmt "\n", arg1, arg2); \ + ABORT(C_msg); \ + } while (0) +#define ABORT_ARG3(C_msg, C_fmt, arg1, arg2, arg3) \ + do { \ + GC_INFOLOG_PRINTF(C_msg /* + */ C_fmt "\n", \ + arg1, arg2, arg3); \ + ABORT(C_msg); \ + } while (0) + +/* Same as ABORT but does not have 'no-return' attribute. */ +/* ABORT on a dummy condition (which is always true). */ +#define ABORT_RET(msg) \ + if ((signed_word)GC_current_warn_proc == -1) {} else ABORT(msg) + +/* Exit abnormally, but without making a mess (e.g. out of memory) */ +# ifdef PCR +# define EXIT() PCR_Base_Exit(1,PCR_waitForever) +# else +# define EXIT() (GC_on_abort(NULL), exit(1 /* EXIT_FAILURE */)) +# endif + +/* Print warning message, e.g. almost out of memory. */ +/* The argument (if any) format specifier should be: */ +/* "%s", "%p" or "%"WARN_PRIdPTR. */ +#define WARN(msg, arg) \ + (*GC_current_warn_proc)((/* no const */ char *)("GC Warning: " msg), \ + (word)(arg)) +GC_EXTERN GC_warn_proc GC_current_warn_proc; + +/* Print format type macro for decimal signed_word value passed WARN(). */ +/* This could be redefined for Win64 or LLP64, but typically should */ +/* not be done as the WARN format string is, possibly, processed on the */ +/* client side, so non-standard print type modifiers (like MS "I64d") */ +/* should be avoided here if possible. */ +#ifndef WARN_PRIdPTR + /* Assume sizeof(void *) == sizeof(long) (or a little-endian machine) */ +# define WARN_PRIdPTR "ld" +#endif + +/* A tagging macro (for a code static analyzer) to indicate that the */ +/* string obtained from an untrusted source (e.g., argv[], getenv) is */ +/* safe to use in a vulnerable operation (e.g., open, exec). */ +#define TRUSTED_STRING(s) (char*)COVERT_DATAFLOW(s) + +/* Get environment entry */ +#ifdef GC_READ_ENV_FILE + GC_INNER char * GC_envfile_getenv(const char *name); +# define GETENV(name) GC_envfile_getenv(name) +#elif defined(NO_GETENV) && !defined(CPPCHECK) +# define GETENV(name) NULL +#elif defined(EMPTY_GETENV_RESULTS) + /* Workaround for a reputed Wine bug. */ + GC_INLINE char * fixed_getenv(const char *name) + { + char *value = getenv(name); + return value != NULL && *value != '\0' ? value : NULL; + } +# define GETENV(name) fixed_getenv(name) +#else +# define GETENV(name) getenv(name) +#endif + +EXTERN_C_END + +#if defined(DARWIN) +# include +# ifndef MAC_OS_X_VERSION_MAX_ALLOWED +# include + /* Include this header just to import the above macro. */ +# endif +# if defined(POWERPC) +# if CPP_WORDSZ == 32 +# define GC_THREAD_STATE_T ppc_thread_state_t +# else +# define GC_THREAD_STATE_T ppc_thread_state64_t +# define GC_MACH_THREAD_STATE PPC_THREAD_STATE64 +# define GC_MACH_THREAD_STATE_COUNT PPC_THREAD_STATE64_COUNT +# endif +# elif defined(I386) || defined(X86_64) +# if CPP_WORDSZ == 32 +# if defined(i386_THREAD_STATE_COUNT) && !defined(x86_THREAD_STATE32_COUNT) + /* Use old naming convention for 32-bit x86. */ +# define GC_THREAD_STATE_T i386_thread_state_t +# define GC_MACH_THREAD_STATE i386_THREAD_STATE +# define GC_MACH_THREAD_STATE_COUNT i386_THREAD_STATE_COUNT +# else +# define GC_THREAD_STATE_T x86_thread_state32_t +# define GC_MACH_THREAD_STATE x86_THREAD_STATE32 +# define GC_MACH_THREAD_STATE_COUNT x86_THREAD_STATE32_COUNT +# endif +# else +# define GC_THREAD_STATE_T x86_thread_state64_t +# define GC_MACH_THREAD_STATE x86_THREAD_STATE64 +# define GC_MACH_THREAD_STATE_COUNT x86_THREAD_STATE64_COUNT +# endif +# elif defined(ARM32) && defined(ARM_UNIFIED_THREAD_STATE) \ + && !defined(CPPCHECK) +# define GC_THREAD_STATE_T arm_unified_thread_state_t +# define GC_MACH_THREAD_STATE ARM_UNIFIED_THREAD_STATE +# define GC_MACH_THREAD_STATE_COUNT ARM_UNIFIED_THREAD_STATE_COUNT +# elif defined(ARM32) +# define GC_THREAD_STATE_T arm_thread_state_t +# ifdef ARM_MACHINE_THREAD_STATE_COUNT +# define GC_MACH_THREAD_STATE ARM_MACHINE_THREAD_STATE +# define GC_MACH_THREAD_STATE_COUNT ARM_MACHINE_THREAD_STATE_COUNT +# endif +# elif defined(AARCH64) +# define GC_THREAD_STATE_T arm_thread_state64_t +# define GC_MACH_THREAD_STATE ARM_THREAD_STATE64 +# define GC_MACH_THREAD_STATE_COUNT ARM_THREAD_STATE64_COUNT +# elif !defined(CPPCHECK) +# error define GC_THREAD_STATE_T +# endif +# ifndef GC_MACH_THREAD_STATE +# define GC_MACH_THREAD_STATE MACHINE_THREAD_STATE +# define GC_MACH_THREAD_STATE_COUNT MACHINE_THREAD_STATE_COUNT +# endif + +# if CPP_WORDSZ == 32 +# define GC_MACH_HEADER mach_header +# define GC_MACH_SECTION section +# define GC_GETSECTBYNAME getsectbynamefromheader +# else +# define GC_MACH_HEADER mach_header_64 +# define GC_MACH_SECTION section_64 +# define GC_GETSECTBYNAME getsectbynamefromheader_64 +# endif + + /* Try to work out the right way to access thread state structure */ + /* members. The structure has changed its definition in different */ + /* Darwin versions. This now defaults to the (older) names */ + /* without __, thus hopefully, not breaking any existing */ + /* Makefile.direct builds. */ +# if __DARWIN_UNIX03 +# define THREAD_FLD_NAME(x) __ ## x +# else +# define THREAD_FLD_NAME(x) x +# endif +# if defined(ARM32) && defined(ARM_UNIFIED_THREAD_STATE) +# define THREAD_FLD(x) ts_32.THREAD_FLD_NAME(x) +# else +# define THREAD_FLD(x) THREAD_FLD_NAME(x) +# endif +#endif /* DARWIN */ + +#include "../gc_tiny_fl.h" + +#include + +EXTERN_C_BEGIN + +/*********************************/ +/* */ +/* Word-size-dependent defines */ +/* */ +/*********************************/ + +#if CPP_WORDSZ == 32 +# define WORDS_TO_BYTES(x) ((x)<<2) +# define BYTES_TO_WORDS(x) ((x)>>2) +# define LOGWL ((word)5) /* log[2] of CPP_WORDSZ */ +# define modWORDSZ(n) ((n) & 0x1f) /* n mod size of word */ +# if ALIGNMENT != 4 +# define UNALIGNED_PTRS +# endif +#endif + +#if CPP_WORDSZ == 64 +# define WORDS_TO_BYTES(x) ((x)<<3) +# define BYTES_TO_WORDS(x) ((x)>>3) +# define LOGWL ((word)6) /* log[2] of CPP_WORDSZ */ +# define modWORDSZ(n) ((n) & 0x3f) /* n mod size of word */ +# if ALIGNMENT != 8 +# define UNALIGNED_PTRS +# endif +#endif + +/* The first TINY_FREELISTS free lists correspond to the first */ +/* TINY_FREELISTS multiples of GRANULE_BYTES, i.e. we keep */ +/* separate free lists for each multiple of GRANULE_BYTES */ +/* up to (TINY_FREELISTS-1) * GRANULE_BYTES. After that they */ +/* may be spread out further. */ + +#define GRANULE_BYTES GC_GRANULE_BYTES +#define TINY_FREELISTS GC_TINY_FREELISTS + +#define WORDSZ ((word)CPP_WORDSZ) +#define SIGNB ((word)1 << (WORDSZ-1)) +#define BYTES_PER_WORD ((word)(sizeof (word))) +#define divWORDSZ(n) ((n) >> LOGWL) /* divide n by size of word */ + +#if GRANULE_BYTES == 8 +# define BYTES_TO_GRANULES(n) ((n)>>3) +# define GRANULES_TO_BYTES(n) ((n)<<3) +# if CPP_WORDSZ == 64 +# define GRANULES_TO_WORDS(n) (n) +# elif CPP_WORDSZ == 32 +# define GRANULES_TO_WORDS(n) ((n)<<1) +# else +# define GRANULES_TO_WORDS(n) BYTES_TO_WORDS(GRANULES_TO_BYTES(n)) +# endif +#elif GRANULE_BYTES == 16 +# define BYTES_TO_GRANULES(n) ((n)>>4) +# define GRANULES_TO_BYTES(n) ((n)<<4) +# if CPP_WORDSZ == 64 +# define GRANULES_TO_WORDS(n) ((n)<<1) +# elif CPP_WORDSZ == 32 +# define GRANULES_TO_WORDS(n) ((n)<<2) +# else +# define GRANULES_TO_WORDS(n) BYTES_TO_WORDS(GRANULES_TO_BYTES(n)) +# endif +#else +# error Bad GRANULE_BYTES value +#endif + +/*********************/ +/* */ +/* Size Parameters */ +/* */ +/*********************/ + +/* Heap block size, bytes. Should be power of 2. */ +/* Incremental GC with MPROTECT_VDB currently requires the */ +/* page size to be a multiple of HBLKSIZE. Since most modern */ +/* architectures support variable page sizes down to 4K, and */ +/* X86 is generally 4K, we now default to 4K, except for */ +/* Alpha: Seems to be used with 8K pages. */ +/* SMALL_CONFIG: Want less block-level fragmentation. */ +#ifndef HBLKSIZE +# if defined(LARGE_CONFIG) || !defined(SMALL_CONFIG) +# ifdef ALPHA +# define CPP_LOG_HBLKSIZE 13 +# elif defined(SN_TARGET_ORBIS) || defined(SN_TARGET_PSP2) +# define CPP_LOG_HBLKSIZE 16 /* page size is set to 64K */ +# else +# define CPP_LOG_HBLKSIZE 12 +# endif +# else +# define CPP_LOG_HBLKSIZE 10 +# endif +#else +# if HBLKSIZE == 512 +# define CPP_LOG_HBLKSIZE 9 +# elif HBLKSIZE == 1024 +# define CPP_LOG_HBLKSIZE 10 +# elif HBLKSIZE == 2048 +# define CPP_LOG_HBLKSIZE 11 +# elif HBLKSIZE == 4096 +# define CPP_LOG_HBLKSIZE 12 +# elif HBLKSIZE == 8192 +# define CPP_LOG_HBLKSIZE 13 +# elif HBLKSIZE == 16384 +# define CPP_LOG_HBLKSIZE 14 +# else +# error fix HBLKSIZE +# endif +# undef HBLKSIZE +#endif + +# define CPP_HBLKSIZE (1 << CPP_LOG_HBLKSIZE) +# define LOG_HBLKSIZE ((size_t)CPP_LOG_HBLKSIZE) +# define HBLKSIZE ((size_t)CPP_HBLKSIZE) + +#define GC_SQRT_SIZE_MAX ((((size_t)1) << (WORDSZ / 2)) - 1) + +/* Max size objects supported by freelist (larger objects are */ +/* allocated directly with allchblk(), by rounding to the next */ +/* multiple of HBLKSIZE). */ +#define CPP_MAXOBJBYTES (CPP_HBLKSIZE/2) +#define MAXOBJBYTES ((size_t)CPP_MAXOBJBYTES) +#define CPP_MAXOBJWORDS BYTES_TO_WORDS(CPP_MAXOBJBYTES) +#define MAXOBJWORDS ((size_t)CPP_MAXOBJWORDS) +#define CPP_MAXOBJGRANULES BYTES_TO_GRANULES(CPP_MAXOBJBYTES) +#define MAXOBJGRANULES ((size_t)CPP_MAXOBJGRANULES) + +# define divHBLKSZ(n) ((n) >> LOG_HBLKSIZE) + +# define HBLK_PTR_DIFF(p,q) divHBLKSZ((ptr_t)p - (ptr_t)q) + /* Equivalent to subtracting 2 hblk pointers. */ + /* We do it this way because a compiler should */ + /* find it hard to use an integer division */ + /* instead of a shift. The bundled SunOS 4.1 */ + /* o.w. sometimes pessimizes the subtraction to */ + /* involve a call to .div. */ + +# define modHBLKSZ(n) ((n) & (HBLKSIZE-1)) + +# define HBLKPTR(objptr) ((struct hblk *)(((word)(objptr)) \ + & ~(word)(HBLKSIZE-1))) +# define HBLKDISPL(objptr) (((size_t) (objptr)) & (HBLKSIZE-1)) + +/* Round up allocation size (in bytes) to a multiple of a granule. */ +#define ROUNDUP_GRANULE_SIZE(lb) /* lb should have no side-effect */ \ + (SIZET_SAT_ADD(lb, GRANULE_BYTES - 1) & ~(GRANULE_BYTES - 1)) + +/* Round up byte allocation requests to integral number of words, etc. */ +# define ROUNDED_UP_GRANULES(lb) /* lb should have no side-effect */ \ + BYTES_TO_GRANULES(SIZET_SAT_ADD(lb, GRANULE_BYTES - 1 + EXTRA_BYTES)) +# if MAX_EXTRA_BYTES == 0 +# define SMALL_OBJ(bytes) EXPECT((bytes) <= (MAXOBJBYTES), TRUE) +# else +# define SMALL_OBJ(bytes) \ + (EXPECT((bytes) <= (MAXOBJBYTES - MAX_EXTRA_BYTES), TRUE) \ + || (bytes) <= MAXOBJBYTES - EXTRA_BYTES) + /* This really just tests bytes <= MAXOBJBYTES - EXTRA_BYTES. */ + /* But we try to avoid looking up EXTRA_BYTES. */ +# endif +# define ADD_SLOP(lb) /* lb should have no side-effect */ \ + SIZET_SAT_ADD(lb, EXTRA_BYTES) +# ifndef MIN_WORDS +# define MIN_WORDS 2 /* FIXME: obsolete */ +# endif + +/* + * Hash table representation of sets of pages. + * Implements a map from aligned HBLKSIZE chunks of the address space to one + * bit each. + * This assumes it is OK to spuriously set bits, e.g. because multiple + * addresses are represented by a single location. + * Used by black-listing code, and perhaps by dirty bit maintenance code. + */ + +# ifdef LARGE_CONFIG +# if CPP_WORDSZ == 32 +# define LOG_PHT_ENTRIES 20 /* Collisions likely at 1M blocks, */ + /* which is >= 4GB. Each table takes */ + /* 128KB, some of which may never be */ + /* touched. */ +# else +# define LOG_PHT_ENTRIES 21 /* Collisions likely at 2M blocks, */ + /* which is >= 8GB. Each table takes */ + /* 256KB, some of which may never be */ + /* touched. */ +# endif +# elif !defined(SMALL_CONFIG) +# define LOG_PHT_ENTRIES 18 /* Collisions are likely if heap grows */ + /* to more than 256K hblks >= 1GB. */ + /* Each hash table occupies 32K bytes. */ + /* Even for somewhat smaller heaps, */ + /* say half that, collisions may be an */ + /* issue because we blacklist */ + /* addresses outside the heap. */ +# else +# define LOG_PHT_ENTRIES 15 /* Collisions are likely if heap grows */ + /* to more than 32K hblks = 128MB. */ + /* Each hash table occupies 4K bytes. */ +# endif +# define PHT_ENTRIES ((word)1 << LOG_PHT_ENTRIES) +# define PHT_SIZE (PHT_ENTRIES >> LOGWL) +typedef word page_hash_table[PHT_SIZE]; + +# define PHT_HASH(addr) ((((word)(addr)) >> LOG_HBLKSIZE) & (PHT_ENTRIES - 1)) + +# define get_pht_entry_from_index(bl, index) \ + (((bl)[divWORDSZ(index)] >> modWORDSZ(index)) & 1) +# define set_pht_entry_from_index(bl, index) \ + (void)((bl)[divWORDSZ(index)] |= (word)1 << modWORDSZ(index)) + +#if defined(THREADS) && defined(AO_HAVE_or) + /* And, one more version for GC_add_to_black_list_normal/stack */ + /* (invoked indirectly by GC_do_local_mark) and */ + /* async_set_pht_entry_from_index (invoked by GC_dirty or the write */ + /* fault handler). */ +# define set_pht_entry_from_index_concurrent(bl, index) \ + AO_or((volatile AO_t *)&(bl)[divWORDSZ(index)], \ + (AO_t)((word)1 << modWORDSZ(index))) +#endif + + +/********************************************/ +/* */ +/* H e a p B l o c k s */ +/* */ +/********************************************/ + +/* heap block header */ +#define HBLKMASK (HBLKSIZE-1) + +#define MARK_BITS_PER_HBLK (HBLKSIZE/GRANULE_BYTES) + /* upper bound */ + /* We allocate 1 bit per allocation granule. */ + /* If MARK_BIT_PER_GRANULE is defined, we use */ + /* every nth bit, where n is the number of */ + /* allocation granules per object. If */ + /* MARK_BIT_PER_OBJ is defined, we only use the */ + /* initial group of mark bits, and it is safe */ + /* to allocate smaller header for large objects. */ + +union word_ptr_ao_u { + word w; + signed_word sw; + void *vp; +# ifdef PARALLEL_MARK + volatile AO_t ao; +# endif +}; + +/* We maintain layout maps for heap blocks containing objects of a given */ +/* size. Each entry in this map describes a byte offset and has the */ +/* following type. */ +struct hblkhdr { + struct hblk * hb_next; /* Link field for hblk free list */ + /* and for lists of chunks waiting to be */ + /* reclaimed. */ + struct hblk * hb_prev; /* Backwards link for free list. */ + struct hblk * hb_block; /* The corresponding block. */ + unsigned char hb_obj_kind; + /* Kind of objects in the block. Each kind */ + /* identifies a mark procedure and a set of */ + /* list headers. Sometimes called regions. */ + unsigned char hb_flags; +# define IGNORE_OFF_PAGE 1 /* Ignore pointers that do not */ + /* point to the first page of */ + /* this object. */ +# define WAS_UNMAPPED 2 /* This is a free block, which has */ + /* been unmapped from the address */ + /* space. */ + /* GC_remap must be invoked on it */ + /* before it can be reallocated. */ + /* Only set with USE_MUNMAP. */ +# define FREE_BLK 4 /* Block is free, i.e. not in use. */ +# ifdef ENABLE_DISCLAIM +# define HAS_DISCLAIM 8 + /* This kind has a callback on reclaim. */ +# define MARK_UNCONDITIONALLY 0x10 + /* Mark from all objects, marked or */ + /* not. Used to mark objects needed by */ + /* reclaim notifier. */ +# endif +# ifdef MARK_BIT_PER_GRANULE +# define LARGE_BLOCK 0x20 +# endif + unsigned short hb_last_reclaimed; + /* Value of GC_gc_no when block was */ + /* last allocated or swept. May wrap. */ + /* For a free block, this is maintained */ + /* only for USE_MUNMAP, and indicates */ + /* when the header was allocated, or */ + /* when the size of the block last */ + /* changed. */ +# ifdef MARK_BIT_PER_OBJ + unsigned32 hb_inv_sz; /* A good upper bound for 2**32/hb_sz. */ + /* For large objects, we use */ + /* LARGE_INV_SZ. */ +# define LARGE_INV_SZ (1 << 16) +# endif + word hb_sz; /* If in use, size in bytes, of objects in the block. */ + /* if free, the size in bytes of the whole block. */ + /* We assume that this is convertible to signed_word */ + /* without generating a negative result. We avoid */ + /* generating free blocks larger than that. */ + word hb_descr; /* object descriptor for marking. See */ + /* gc_mark.h. */ +# ifdef MARK_BIT_PER_GRANULE + unsigned short * hb_map; /* Essentially a table of remainders */ + /* mod BYTES_TO_GRANULES(hb_sz), except */ + /* for large blocks. See GC_obj_map. */ +# endif +# ifdef PARALLEL_MARK + volatile AO_t hb_n_marks; /* Number of set mark bits, excluding */ + /* the one always set at the end. */ + /* Currently it is concurrently */ + /* updated and hence only approximate. */ + /* But a zero value does guarantee that */ + /* the block contains no marked */ + /* objects. */ + /* Ensuring this property means that we */ + /* never decrement it to zero during a */ + /* collection, and hence the count may */ + /* be one too high. Due to concurrent */ + /* updates, an arbitrary number of */ + /* increments, but not all of them (!) */ + /* may be lost, hence it may in theory */ + /* be much too low. */ + /* The count may also be too high if */ + /* multiple mark threads mark the */ + /* same object due to a race. */ +# else + size_t hb_n_marks; /* Without parallel marking, the count */ + /* is accurate. */ +# endif +# ifdef USE_MARK_BYTES +# define MARK_BITS_SZ (MARK_BITS_PER_HBLK + 1) + /* Unlike the other case, this is in units of bytes. */ + /* Since we force double-word alignment, we need at most one */ + /* mark bit per 2 words. But we do allocate and set one */ + /* extra mark bit to avoid an explicit check for the */ + /* partial object at the end of each block. */ + union { + char _hb_marks[MARK_BITS_SZ]; + /* The i'th byte is 1 if the object */ + /* starting at granule i or object i is */ + /* marked, 0 o.w. */ + /* The mark bit for the "one past the */ + /* end" object is always set to avoid a */ + /* special case test in the marker. */ + word dummy; /* Force word alignment of mark bytes. */ + } _mark_byte_union; +# define hb_marks _mark_byte_union._hb_marks +# else +# define MARK_BITS_SZ (MARK_BITS_PER_HBLK/CPP_WORDSZ + 1) + word hb_marks[MARK_BITS_SZ]; +# endif /* !USE_MARK_BYTES */ +}; + +# define ANY_INDEX 23 /* "Random" mark bit index for assertions */ + +/* heap block body */ + +# define HBLK_WORDS (HBLKSIZE/sizeof(word)) +# define HBLK_GRANULES (HBLKSIZE/GRANULE_BYTES) + +/* The number of objects in a block dedicated to a certain size. */ +/* may erroneously yield zero (instead of one) for large objects. */ +# define HBLK_OBJS(sz_in_bytes) (HBLKSIZE/(sz_in_bytes)) + +struct hblk { + char hb_body[HBLKSIZE]; +}; + +# define HBLK_IS_FREE(hdr) (((hdr) -> hb_flags & FREE_BLK) != 0) + +# define OBJ_SZ_TO_BLOCKS(lb) divHBLKSZ((lb) + HBLKSIZE-1) +# define OBJ_SZ_TO_BLOCKS_CHECKED(lb) /* lb should have no side-effect */ \ + divHBLKSZ(SIZET_SAT_ADD(lb, HBLKSIZE - 1)) + /* Size of block (in units of HBLKSIZE) needed to hold objects of */ + /* given lb (in bytes). The checked variant prevents wrap around. */ + +/* Object free list link */ +# define obj_link(p) (*(void **)(p)) + +# define LOG_MAX_MARK_PROCS 6 +# define MAX_MARK_PROCS (1 << LOG_MAX_MARK_PROCS) + +/* Root sets. Logically private to mark_rts.c. But we don't want the */ +/* tables scanned, so we put them here. */ +/* MAX_ROOT_SETS is the maximum number of ranges that can be */ +/* registered as static roots. */ +# ifdef LARGE_CONFIG +# define MAX_ROOT_SETS 8192 +# elif !defined(SMALL_CONFIG) +# define MAX_ROOT_SETS 2048 +# else +# define MAX_ROOT_SETS 512 +# endif + +# define MAX_EXCLUSIONS (MAX_ROOT_SETS/4) +/* Maximum number of segments that can be excluded from root sets. */ + +/* + * Data structure for excluded static roots. + */ +struct exclusion { + ptr_t e_start; + ptr_t e_end; +}; + +/* Data structure for list of root sets. */ +/* We keep a hash table, so that we can filter out duplicate additions. */ +/* Under Win32, we need to do a better job of filtering overlaps, so */ +/* we resort to sequential search, and pay the price. */ +struct roots { + ptr_t r_start;/* multiple of word size */ + ptr_t r_end; /* multiple of word size and greater than r_start */ +# if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) + struct roots * r_next; +# endif + GC_bool r_tmp; + /* Delete before registering new dynamic libraries */ +}; + +#if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) + /* Size of hash table index to roots. */ +# define LOG_RT_SIZE 6 +# define RT_SIZE (1 << LOG_RT_SIZE) /* Power of 2, may be != MAX_ROOT_SETS */ +#endif + +#ifndef MAX_HEAP_SECTS +# ifdef LARGE_CONFIG +# if CPP_WORDSZ > 32 +# define MAX_HEAP_SECTS 81920 +# else +# define MAX_HEAP_SECTS 7680 +# endif +# elif defined(SMALL_CONFIG) && !defined(USE_PROC_FOR_LIBRARIES) +# if defined(PARALLEL_MARK) && (defined(MSWIN32) || defined(CYGWIN32)) +# define MAX_HEAP_SECTS 384 +# else +# define MAX_HEAP_SECTS 128 /* Roughly 256MB (128*2048*1K) */ +# endif +# elif CPP_WORDSZ > 32 +# define MAX_HEAP_SECTS 1024 /* Roughly 8GB */ +# else +# define MAX_HEAP_SECTS 512 /* Roughly 4GB */ +# endif +#endif /* !MAX_HEAP_SECTS */ + +typedef struct GC_ms_entry { + ptr_t mse_start; /* First word of object, word aligned. */ + union word_ptr_ao_u mse_descr; + /* Descriptor; low order two bits are tags, */ + /* as described in gc_mark.h. */ +} mse; + +/* Lists of all heap blocks and free lists */ +/* as well as other random data structures */ +/* that should not be scanned by the */ +/* collector. */ +/* These are grouped together in a struct */ +/* so that they can be easily skipped by the */ +/* GC_mark routine. */ +/* The ordering is weird to make GC_malloc */ +/* faster by keeping the important fields */ +/* sufficiently close together that a */ +/* single load of a base register will do. */ +/* Scalars that could easily appear to */ +/* be pointers are also put here. */ +/* The main fields should precede any */ +/* conditionally included fields, so that */ +/* gc_inl.h will work even if a different set */ +/* of macros is defined when the client is */ +/* compiled. */ + +struct _GC_arrays { + word _heapsize; /* Heap size in bytes (value never goes down). */ + word _requested_heapsize; /* Heap size due to explicit expansion. */ + ptr_t _last_heap_addr; + ptr_t _prev_heap_addr; + word _large_free_bytes; + /* Total bytes contained in blocks on large object free */ + /* list. */ + word _large_allocd_bytes; + /* Total number of bytes in allocated large objects blocks. */ + /* For the purposes of this counter and the next one only, a */ + /* large object is one that occupies a block of at least */ + /* 2*HBLKSIZE. */ + word _max_large_allocd_bytes; + /* Maximum number of bytes that were ever allocated in */ + /* large object blocks. This is used to help decide when it */ + /* is safe to split up a large block. */ + word _bytes_allocd_before_gc; + /* Number of bytes allocated before this */ + /* collection cycle. */ +# ifndef SEPARATE_GLOBALS +# define GC_bytes_allocd GC_arrays._bytes_allocd + word _bytes_allocd; + /* Number of bytes allocated during this collection cycle. */ +# endif + word _bytes_dropped; + /* Number of black-listed bytes dropped during GC cycle */ + /* as a result of repeated scanning during allocation */ + /* attempts. These are treated largely as allocated, */ + /* even though they are not useful to the client. */ + word _bytes_finalized; + /* Approximate number of bytes in objects (and headers) */ + /* that became ready for finalization in the last */ + /* collection. */ + word _bytes_freed; + /* Number of explicitly deallocated bytes of memory */ + /* since last collection. */ + word _finalizer_bytes_freed; + /* Bytes of memory explicitly deallocated while */ + /* finalizers were running. Used to approximate memory */ + /* explicitly deallocated by finalizers. */ + ptr_t _scratch_end_ptr; + ptr_t _scratch_last_end_ptr; + /* Used by headers.c, and can easily appear to point to */ + /* heap. Also used by GC_register_dynamic_libraries(). */ + mse *_mark_stack; + /* Limits of stack for GC_mark routine. All ranges */ + /* between GC_mark_stack (incl.) and GC_mark_stack_top */ + /* (incl.) still need to be marked from. */ + mse *_mark_stack_limit; +# ifdef PARALLEL_MARK + mse *volatile _mark_stack_top; + /* Updated only with mark lock held, but read asynchronously. */ + /* TODO: Use union to avoid casts to AO_t */ +# else + mse *_mark_stack_top; +# endif + word _composite_in_use; /* Number of bytes in the accessible */ + /* composite objects. */ + word _atomic_in_use; /* Number of bytes in the accessible */ + /* atomic objects. */ +# ifdef USE_MUNMAP +# define GC_unmapped_bytes GC_arrays._unmapped_bytes + word _unmapped_bytes; +# else +# define GC_unmapped_bytes 0 +# endif + bottom_index * _all_nils; +# ifdef ENABLE_TRACE +# define GC_trace_addr GC_arrays._trace_addr + ptr_t _trace_addr; +# endif + GC_mark_proc _mark_procs[MAX_MARK_PROCS]; + /* Table of user-defined mark procedures. There is */ + /* a small number of these, which can be referenced */ + /* by DS_PROC mark descriptors. See gc_mark.h. */ + char _modws_valid_offsets[sizeof(word)]; + /* GC_valid_offsets[i] ==> */ + /* GC_modws_valid_offsets[i%sizeof(word)] */ +# if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) +# define GC_root_index GC_arrays._root_index + struct roots * _root_index[RT_SIZE]; +# endif +# ifdef SAVE_CALL_CHAIN +# define GC_last_stack GC_arrays._last_stack + struct callinfo _last_stack[NFRAMES]; + /* Stack at last garbage collection. Useful for */ + /* debugging mysterious object disappearances. In the */ + /* multi-threaded case, we currently only save the */ + /* calling stack. */ +# endif +# ifndef SEPARATE_GLOBALS +# define GC_objfreelist GC_arrays._objfreelist + void *_objfreelist[MAXOBJGRANULES+1]; + /* free list for objects */ +# define GC_aobjfreelist GC_arrays._aobjfreelist + void *_aobjfreelist[MAXOBJGRANULES+1]; + /* free list for atomic objects */ +# endif + void *_uobjfreelist[MAXOBJGRANULES+1]; + /* Uncollectible but traced objects. */ + /* Objects on this and _auobjfreelist */ + /* are always marked, except during */ + /* garbage collections. */ +# ifdef GC_ATOMIC_UNCOLLECTABLE +# define GC_auobjfreelist GC_arrays._auobjfreelist + void *_auobjfreelist[MAXOBJGRANULES+1]; + /* Atomic uncollectible but traced objects. */ +# endif + size_t _size_map[MAXOBJBYTES+1]; + /* Number of granules to allocate when asked for a certain */ + /* number of bytes. Should be accessed with the allocation */ + /* lock held. */ +# ifdef MARK_BIT_PER_GRANULE +# define GC_obj_map GC_arrays._obj_map + unsigned short * _obj_map[MAXOBJGRANULES + 1]; + /* If not NULL, then a pointer to a map of valid */ + /* object addresses. */ + /* _obj_map[sz_in_granules][i] is */ + /* i % sz_in_granules. */ + /* This is now used purely to replace a */ + /* division in the marker by a table lookup. */ + /* _obj_map[0] is used for large objects and */ + /* contains all nonzero entries. This gets us */ + /* out of the marker fast path without an extra */ + /* test. */ +# define MAP_LEN BYTES_TO_GRANULES(HBLKSIZE) +# endif +# define VALID_OFFSET_SZ HBLKSIZE + char _valid_offsets[VALID_OFFSET_SZ]; + /* GC_valid_offsets[i] == TRUE ==> i */ + /* is registered as a displacement. */ +# if defined(PROC_VDB) || defined(MPROTECT_VDB) \ + || defined(GWW_VDB) || defined(MANUAL_VDB) +# define GC_grungy_pages GC_arrays._grungy_pages + page_hash_table _grungy_pages; /* Pages that were dirty at last */ + /* GC_read_dirty. */ +# endif +# if defined(MPROTECT_VDB) || defined(MANUAL_VDB) +# define GC_dirty_pages GC_arrays._dirty_pages + volatile page_hash_table _dirty_pages; + /* Pages dirtied since last GC_read_dirty. */ +# endif +# if (defined(CHECKSUMS) && defined(GWW_VDB)) || defined(PROC_VDB) +# define GC_written_pages GC_arrays._written_pages + page_hash_table _written_pages; /* Pages ever dirtied */ +# endif +# define GC_heap_sects GC_arrays._heap_sects + struct HeapSect { + ptr_t hs_start; + size_t hs_bytes; + } _heap_sects[MAX_HEAP_SECTS]; /* Heap segments potentially */ + /* client objects. */ +# if defined(USE_PROC_FOR_LIBRARIES) +# define GC_our_memory GC_arrays._our_memory + struct HeapSect _our_memory[MAX_HEAP_SECTS]; + /* All GET_MEM allocated */ + /* memory. Includes block */ + /* headers and the like. */ +# endif +# if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32) +# define GC_heap_bases GC_arrays._heap_bases + ptr_t _heap_bases[MAX_HEAP_SECTS]; + /* Start address of memory regions obtained from kernel. */ +# endif +# ifdef MSWINCE +# define GC_heap_lengths GC_arrays._heap_lengths + word _heap_lengths[MAX_HEAP_SECTS]; + /* Committed lengths of memory regions obtained from kernel. */ +# endif + struct roots _static_roots[MAX_ROOT_SETS]; + struct exclusion _excl_table[MAX_EXCLUSIONS]; + /* Block header index; see gc_headers.h */ + bottom_index * _top_index[TOP_SZ]; +}; + +GC_API_PRIV GC_FAR struct _GC_arrays GC_arrays; + +#define GC_all_nils GC_arrays._all_nils +#define GC_atomic_in_use GC_arrays._atomic_in_use +#define GC_bytes_allocd_before_gc GC_arrays._bytes_allocd_before_gc +#define GC_bytes_dropped GC_arrays._bytes_dropped +#define GC_bytes_finalized GC_arrays._bytes_finalized +#define GC_bytes_freed GC_arrays._bytes_freed +#define GC_composite_in_use GC_arrays._composite_in_use +#define GC_excl_table GC_arrays._excl_table +#define GC_finalizer_bytes_freed GC_arrays._finalizer_bytes_freed +#define GC_heapsize GC_arrays._heapsize +#define GC_large_allocd_bytes GC_arrays._large_allocd_bytes +#define GC_large_free_bytes GC_arrays._large_free_bytes +#define GC_last_heap_addr GC_arrays._last_heap_addr +#define GC_mark_stack GC_arrays._mark_stack +#define GC_mark_stack_limit GC_arrays._mark_stack_limit +#define GC_mark_stack_top GC_arrays._mark_stack_top +#define GC_mark_procs GC_arrays._mark_procs +#define GC_max_large_allocd_bytes GC_arrays._max_large_allocd_bytes +#define GC_modws_valid_offsets GC_arrays._modws_valid_offsets +#define GC_prev_heap_addr GC_arrays._prev_heap_addr +#define GC_requested_heapsize GC_arrays._requested_heapsize +#define GC_scratch_end_ptr GC_arrays._scratch_end_ptr +#define GC_scratch_last_end_ptr GC_arrays._scratch_last_end_ptr +#define GC_size_map GC_arrays._size_map +#define GC_static_roots GC_arrays._static_roots +#define GC_top_index GC_arrays._top_index +#define GC_uobjfreelist GC_arrays._uobjfreelist +#define GC_valid_offsets GC_arrays._valid_offsets + +#define beginGC_arrays ((ptr_t)(&GC_arrays)) +#define endGC_arrays (((ptr_t)(&GC_arrays)) + (sizeof GC_arrays)) +#define USED_HEAP_SIZE (GC_heapsize - GC_large_free_bytes) + +/* Object kinds: */ +#ifndef MAXOBJKINDS +# define MAXOBJKINDS 16 +#endif +GC_EXTERN struct obj_kind { + void **ok_freelist; /* Array of free list headers for this kind of */ + /* object. Point either to GC_arrays or to */ + /* storage allocated with GC_scratch_alloc. */ + struct hblk **ok_reclaim_list; + /* List headers for lists of blocks waiting to */ + /* be swept. Indexed by object size in */ + /* granules. */ + word ok_descriptor; /* Descriptor template for objects in this */ + /* block. */ + GC_bool ok_relocate_descr; + /* Add object size in bytes to descriptor */ + /* template to obtain descriptor. Otherwise */ + /* template is used as is. */ + GC_bool ok_init; /* Clear objects before putting them on the free list. */ +# ifdef ENABLE_DISCLAIM + GC_bool ok_mark_unconditionally; + /* Mark from all, including unmarked, objects */ + /* in block. Used to protect objects reachable */ + /* from reclaim notifiers. */ + int (GC_CALLBACK *ok_disclaim_proc)(void * /*obj*/); + /* The disclaim procedure is called before obj */ + /* is reclaimed, but must also tolerate being */ + /* called with object from freelist. Non-zero */ + /* exit prevents object from being reclaimed. */ +# define OK_DISCLAIM_INITZ /* comma */, FALSE, 0 +# else +# define OK_DISCLAIM_INITZ /* empty */ +# endif /* !ENABLE_DISCLAIM */ +} GC_obj_kinds[MAXOBJKINDS]; + +#define beginGC_obj_kinds ((ptr_t)(&GC_obj_kinds)) +#define endGC_obj_kinds (beginGC_obj_kinds + (sizeof GC_obj_kinds)) + +/* Variables that used to be in GC_arrays, but need to be accessed by */ +/* inline allocation code. If they were in GC_arrays, the inlined */ +/* allocation code would include GC_arrays offsets (as it did), which */ +/* introduce maintenance problems. */ + +#ifdef SEPARATE_GLOBALS + extern word GC_bytes_allocd; + /* Number of bytes allocated during this collection cycle. */ + extern ptr_t GC_objfreelist[MAXOBJGRANULES+1]; + /* free list for NORMAL objects */ +# define beginGC_objfreelist ((ptr_t)(&GC_objfreelist)) +# define endGC_objfreelist (beginGC_objfreelist + sizeof(GC_objfreelist)) + + extern ptr_t GC_aobjfreelist[MAXOBJGRANULES+1]; + /* free list for atomic (PTRFREE) objects */ +# define beginGC_aobjfreelist ((ptr_t)(&GC_aobjfreelist)) +# define endGC_aobjfreelist (beginGC_aobjfreelist + sizeof(GC_aobjfreelist)) +#endif /* SEPARATE_GLOBALS */ + +/* Predefined kinds: */ +#define PTRFREE 0 +#define NORMAL 1 +#define UNCOLLECTABLE 2 +#ifdef GC_ATOMIC_UNCOLLECTABLE +# define AUNCOLLECTABLE 3 +# define IS_UNCOLLECTABLE(k) (((k) & ~1) == UNCOLLECTABLE) +# define GC_N_KINDS_INITIAL_VALUE 4 +#else +# define IS_UNCOLLECTABLE(k) ((k) == UNCOLLECTABLE) +# define GC_N_KINDS_INITIAL_VALUE 3 +#endif + +GC_EXTERN unsigned GC_n_kinds; + +GC_EXTERN word GC_n_heap_sects; /* Number of separately added heap */ + /* sections. */ + +#ifdef USE_PROC_FOR_LIBRARIES + GC_EXTERN word GC_n_memory; /* Number of GET_MEM allocated memory */ + /* sections. */ +#endif + +GC_EXTERN size_t GC_page_size; + +/* Round up allocation size to a multiple of a page size. */ +/* GC_setpagesize() is assumed to be already invoked. */ +#define ROUNDUP_PAGESIZE(lb) /* lb should have no side-effect */ \ + (SIZET_SAT_ADD(lb, GC_page_size - 1) & ~(GC_page_size - 1)) + +/* Same as above but used to make GET_MEM() argument safe. */ +#ifdef MMAP_SUPPORTED +# define ROUNDUP_PAGESIZE_IF_MMAP(lb) ROUNDUP_PAGESIZE(lb) +#else +# define ROUNDUP_PAGESIZE_IF_MMAP(lb) (lb) +#endif + +#if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32) + struct _SYSTEM_INFO; + GC_EXTERN struct _SYSTEM_INFO GC_sysinfo; + GC_INNER GC_bool GC_is_heap_base(void *p); +#endif + +GC_EXTERN word GC_black_list_spacing; + /* Average number of bytes between blacklisted */ + /* blocks. Approximate. */ + /* Counts only blocks that are */ + /* "stack-blacklisted", i.e. that are */ + /* problematic in the interior of an object. */ + +#ifdef GC_GCJ_SUPPORT + extern struct hblk * GC_hblkfreelist[]; + extern word GC_free_bytes[]; /* Both remain visible to GNU GCJ. */ +#endif + +#ifdef GC_DISABLE_INCREMENTAL +# define GC_incremental FALSE + /* Hopefully allow optimizer to remove some code. */ +# define TRUE_INCREMENTAL FALSE +#else + GC_EXTERN GC_bool GC_incremental; + /* Using incremental/generational collection. */ + /* Assumes dirty bits are being maintained. */ +# define TRUE_INCREMENTAL \ + (GC_incremental && GC_time_limit != GC_TIME_UNLIMITED) + /* True incremental, not just generational, mode */ +#endif /* !GC_DISABLE_INCREMENTAL */ + +GC_EXTERN word GC_root_size; /* Total size of registered root sections. */ + +GC_EXTERN GC_bool GC_debugging_started; + /* GC_debug_malloc has been called. */ + +/* This is used by GC_do_blocking[_inner](). */ +struct blocking_data { + GC_fn_type fn; + void * client_data; /* and result */ +}; + +/* This is used by GC_call_with_gc_active(), GC_push_all_stack_sections(). */ +struct GC_traced_stack_sect_s { + ptr_t saved_stack_ptr; +# ifdef IA64 + ptr_t saved_backing_store_ptr; + ptr_t backing_store_end; +# endif + struct GC_traced_stack_sect_s *prev; +}; + +#ifdef THREADS + /* Process all "traced stack sections" - scan entire stack except for */ + /* frames belonging to the user functions invoked by GC_do_blocking. */ + GC_INNER void GC_push_all_stack_sections(ptr_t lo, ptr_t hi, + struct GC_traced_stack_sect_s *traced_stack_sect); + GC_EXTERN word GC_total_stacksize; /* updated on every push_all_stacks */ +#else + GC_EXTERN ptr_t GC_blocked_sp; + GC_EXTERN struct GC_traced_stack_sect_s *GC_traced_stack_sect; + /* Points to the "frame" data held in stack by */ + /* the innermost GC_call_with_gc_active(). */ + /* NULL if no such "frame" active. */ +#endif /* !THREADS */ + +#ifdef IA64 + /* Similar to GC_push_all_stack_sections() but for IA-64 registers store. */ + GC_INNER void GC_push_all_register_sections(ptr_t bs_lo, ptr_t bs_hi, + int eager, struct GC_traced_stack_sect_s *traced_stack_sect); +#endif + +/* Marks are in a reserved area in */ +/* each heap block. Each word has one mark bit associated */ +/* with it. Only those corresponding to the beginning of an */ +/* object are used. */ + +/* Mark bit operations */ + +/* + * Retrieve, set, clear the nth mark bit in a given heap block. + * + * (Recall that bit n corresponds to nth object or allocation granule + * relative to the beginning of the block, including unused words) + */ + +#ifdef USE_MARK_BYTES +# define mark_bit_from_hdr(hhdr,n) ((hhdr)->hb_marks[n]) +# define set_mark_bit_from_hdr(hhdr,n) ((hhdr)->hb_marks[n] = 1) +# define clear_mark_bit_from_hdr(hhdr,n) ((hhdr)->hb_marks[n] = 0) +#else +/* Set mark bit correctly, even if mark bits may be concurrently */ +/* accessed. */ +# if defined(PARALLEL_MARK) || (defined(THREAD_SANITIZER) && defined(THREADS)) + /* Workaround TSan false positive: there is no race between */ + /* mark_bit_from_hdr and set_mark_bit_from_hdr when n is different */ + /* (alternatively, USE_MARK_BYTES could be used). If TSan is off, */ + /* AO_or() is used only if we set USE_MARK_BITS explicitly. */ +# define OR_WORD(addr, bits) AO_or((volatile AO_t *)(addr), (AO_t)(bits)) +# else +# define OR_WORD(addr, bits) (void)(*(addr) |= (bits)) +# endif +# define mark_bit_from_hdr(hhdr,n) \ + (((hhdr)->hb_marks[divWORDSZ(n)] >> modWORDSZ(n)) & (word)1) +# define set_mark_bit_from_hdr(hhdr,n) \ + OR_WORD((hhdr)->hb_marks+divWORDSZ(n), (word)1 << modWORDSZ(n)) +# define clear_mark_bit_from_hdr(hhdr,n) \ + ((hhdr)->hb_marks[divWORDSZ(n)] &= ~((word)1 << modWORDSZ(n))) +#endif /* !USE_MARK_BYTES */ + +#ifdef MARK_BIT_PER_OBJ +# define MARK_BIT_NO(offset, sz) (((word)(offset))/(sz)) + /* Get the mark bit index corresponding to the given byte */ + /* offset and size (in bytes). */ +# define MARK_BIT_OFFSET(sz) 1 + /* Spacing between useful mark bits. */ +# define IF_PER_OBJ(x) x +# define FINAL_MARK_BIT(sz) ((sz) > MAXOBJBYTES? 1 : HBLK_OBJS(sz)) + /* Position of final, always set, mark bit. */ +#else /* MARK_BIT_PER_GRANULE */ +# define MARK_BIT_NO(offset, sz) BYTES_TO_GRANULES((word)(offset)) +# define MARK_BIT_OFFSET(sz) BYTES_TO_GRANULES(sz) +# define IF_PER_OBJ(x) +# define FINAL_MARK_BIT(sz) \ + ((sz) > MAXOBJBYTES ? MARK_BITS_PER_HBLK \ + : BYTES_TO_GRANULES((sz) * HBLK_OBJS(sz))) +#endif + +/* Important internal collector routines */ + +GC_INNER ptr_t GC_approx_sp(void); + +GC_INNER GC_bool GC_should_collect(void); + +void GC_apply_to_all_blocks(void (*fn)(struct hblk *h, word client_data), + word client_data); + /* Invoke fn(hbp, client_data) for each */ + /* allocated heap block. */ +GC_INNER struct hblk * GC_next_used_block(struct hblk * h); + /* Return first in-use block >= h */ +GC_INNER struct hblk * GC_prev_block(struct hblk * h); + /* Return last block <= h. Returned block */ + /* is managed by GC, but may or may not be in */ + /* use. */ +GC_INNER void GC_mark_init(void); +GC_INNER void GC_clear_marks(void); + /* Clear mark bits for all heap objects. */ +GC_INNER void GC_invalidate_mark_state(void); + /* Tell the marker that marked */ + /* objects may point to unmarked */ + /* ones, and roots may point to */ + /* unmarked objects. Reset mark stack. */ +GC_INNER GC_bool GC_mark_some(ptr_t cold_gc_frame); + /* Perform about one pages worth of marking */ + /* work of whatever kind is needed. Returns */ + /* quickly if no collection is in progress. */ + /* Return TRUE if mark phase finished. */ +GC_INNER void GC_initiate_gc(void); + /* initiate collection. */ + /* If the mark state is invalid, this */ + /* becomes full collection. Otherwise */ + /* it's partial. */ + +GC_INNER GC_bool GC_collection_in_progress(void); + /* Collection is in progress, or was abandoned. */ + +#define GC_PUSH_ALL_SYM(sym) \ + GC_push_all((/* no volatile */ void *)&(sym), \ + (/* no volatile */ void *)(&(sym) + 1)) + +GC_INNER void GC_push_all_stack(ptr_t b, ptr_t t); + /* As GC_push_all but consider */ + /* interior pointers as valid. */ + +#if defined(WRAP_MARK_SOME) && defined(PARALLEL_MARK) + /* GC_mark_local does not handle memory protection faults yet. So, */ + /* the static data regions are scanned immediately by GC_push_roots. */ + GC_INNER void GC_push_conditional_eager(void *bottom, void *top, + GC_bool all); +#endif + + /* In the threads case, we push part of the current thread stack */ + /* with GC_push_all_eager when we push the registers. This gets the */ + /* callee-save registers that may disappear. The remainder of the */ + /* stacks are scheduled for scanning in *GC_push_other_roots, which */ + /* is thread-package-specific. */ + +GC_INNER void GC_push_roots(GC_bool all, ptr_t cold_gc_frame); + /* Push all or dirty roots. */ + +GC_API_PRIV GC_push_other_roots_proc GC_push_other_roots; + /* Push system or application specific roots */ + /* onto the mark stack. In some environments */ + /* (e.g. threads environments) this is */ + /* predefined to be non-zero. A client */ + /* supplied replacement should also call the */ + /* original function. Remains externally */ + /* visible as used by some well-known 3rd-party */ + /* software (e.g., ECL) currently. */ + +#ifdef THREADS + void GC_push_thread_structures(void); +#endif +GC_EXTERN void (*GC_push_typed_structures)(void); + /* A pointer such that we can avoid linking in */ + /* the typed allocation support if unused. */ + +GC_INNER void GC_with_callee_saves_pushed(void (*fn)(ptr_t, void *), + volatile ptr_t arg); + +#if defined(SPARC) || defined(IA64) + /* Cause all stacked registers to be saved in memory. Return a */ + /* pointer to the top of the corresponding memory stack. */ + ptr_t GC_save_regs_in_stack(void); +#endif + /* Push register contents onto mark stack. */ + +#if defined(MSWIN32) || defined(MSWINCE) + void __cdecl GC_push_one(word p); +#else + void GC_push_one(word p); + /* If p points to an object, mark it */ + /* and push contents on the mark stack */ + /* Pointer recognition test always */ + /* accepts interior pointers, i.e. this */ + /* is appropriate for pointers found on */ + /* stack. */ +#endif + +#if defined(PRINT_BLACK_LIST) || defined(KEEP_BACK_PTRS) + GC_INNER void GC_mark_and_push_stack(ptr_t p, ptr_t source); + /* Ditto, omits plausibility test */ +#else + GC_INNER void GC_mark_and_push_stack(ptr_t p); +#endif + +GC_INNER void GC_clear_hdr_marks(hdr * hhdr); + /* Clear the mark bits in a header */ +GC_INNER void GC_set_hdr_marks(hdr * hhdr); + /* Set the mark bits in a header */ +GC_INNER void GC_set_fl_marks(ptr_t p); + /* Set all mark bits associated with */ + /* a free list. */ +#if defined(GC_ASSERTIONS) && defined(THREAD_LOCAL_ALLOC) + void GC_check_fl_marks(void **); + /* Check that all mark bits */ + /* associated with a free list are */ + /* set. Abort if not. */ +#endif +void GC_add_roots_inner(ptr_t b, ptr_t e, GC_bool tmp); +#ifdef USE_PROC_FOR_LIBRARIES + GC_INNER void GC_remove_roots_subregion(ptr_t b, ptr_t e); +#endif +GC_INNER void GC_exclude_static_roots_inner(void *start, void *finish); +#if defined(DYNAMIC_LOADING) || defined(MSWIN32) || defined(MSWINCE) \ + || defined(CYGWIN32) || defined(PCR) + GC_INNER void GC_register_dynamic_libraries(void); + /* Add dynamic library data sections to the root set. */ +#endif +GC_INNER void GC_cond_register_dynamic_libraries(void); + /* Remove and reregister dynamic libraries if we're */ + /* configured to do that at each GC. */ + +/* Machine dependent startup routines */ +ptr_t GC_get_main_stack_base(void); /* Cold end of stack. */ +#ifdef IA64 + GC_INNER ptr_t GC_get_register_stack_base(void); + /* Cold end of register stack. */ +#endif +void GC_register_data_segments(void); + +#ifdef THREADS + GC_INNER void GC_thr_init(void); + GC_INNER void GC_init_parallel(void); +#else + GC_INNER GC_bool GC_is_static_root(void *p); + /* Is the address p in one of the registered static */ + /* root sections? */ +# ifdef TRACE_BUF + void GC_add_trace_entry(char *kind, word arg1, word arg2); +# endif +#endif /* !THREADS */ + +/* Black listing: */ +#ifdef PRINT_BLACK_LIST + GC_INNER void GC_add_to_black_list_normal(word p, ptr_t source); + /* Register bits as a possible future false */ + /* reference from the heap or static data */ +# define GC_ADD_TO_BLACK_LIST_NORMAL(bits, source) \ + if (GC_all_interior_pointers) { \ + GC_add_to_black_list_stack((word)(bits), (source)); \ + } else \ + GC_add_to_black_list_normal((word)(bits), (source)) + GC_INNER void GC_add_to_black_list_stack(word p, ptr_t source); +# define GC_ADD_TO_BLACK_LIST_STACK(bits, source) \ + GC_add_to_black_list_stack((word)(bits), (source)) +#else + GC_INNER void GC_add_to_black_list_normal(word p); +# define GC_ADD_TO_BLACK_LIST_NORMAL(bits, source) \ + if (GC_all_interior_pointers) { \ + GC_add_to_black_list_stack((word)(bits)); \ + } else \ + GC_add_to_black_list_normal((word)(bits)) + GC_INNER void GC_add_to_black_list_stack(word p); +# define GC_ADD_TO_BLACK_LIST_STACK(bits, source) \ + GC_add_to_black_list_stack((word)(bits)) +#endif /* PRINT_BLACK_LIST */ + +struct hblk * GC_is_black_listed(struct hblk * h, word len); + /* If there are likely to be false references */ + /* to a block starting at h of the indicated */ + /* length, then return the next plausible */ + /* starting location for h that might avoid */ + /* these false references. Remains externally */ + /* visible as used by GNU GCJ currently. */ + +GC_INNER void GC_promote_black_lists(void); + /* Declare an end to a black listing phase. */ +GC_INNER void GC_unpromote_black_lists(void); + /* Approximately undo the effect of the above. */ + /* This actually loses some information, but */ + /* only in a reasonably safe way. */ + +GC_INNER ptr_t GC_scratch_alloc(size_t bytes); + /* GC internal memory allocation for */ + /* small objects. Deallocation is not */ + /* possible. May return NULL. */ + +#ifdef GWW_VDB + /* GC_scratch_recycle_no_gww() not used. */ +#else +# define GC_scratch_recycle_no_gww GC_scratch_recycle_inner +#endif +GC_INNER void GC_scratch_recycle_inner(void *ptr, size_t bytes); + /* Reuse the memory region by the heap. */ + +/* Heap block layout maps: */ +GC_INNER GC_bool GC_add_map_entry(size_t sz); + /* Add a heap block map for objects of */ + /* size sz to obj_map. */ + /* Return FALSE on failure. */ +GC_INNER void GC_register_displacement_inner(size_t offset); + /* Version of GC_register_displacement */ + /* that assumes lock is already held. */ + +/* hblk allocation: */ +GC_INNER void GC_new_hblk(size_t size_in_granules, int kind); + /* Allocate a new heap block, and build */ + /* a free list in it. */ + +GC_INNER ptr_t GC_build_fl(struct hblk *h, size_t words, GC_bool clear, + ptr_t list); + /* Build a free list for objects of */ + /* size sz in block h. Append list to */ + /* end of the free lists. Possibly */ + /* clear objects on the list. Normally */ + /* called by GC_new_hblk, but also */ + /* called explicitly without GC lock. */ + +GC_INNER struct hblk * GC_allochblk(size_t size_in_bytes, int kind, + unsigned flags); + /* Allocate a heap block, inform */ + /* the marker that block is valid */ + /* for objects of indicated size. */ + +GC_INNER ptr_t GC_alloc_large(size_t lb, int k, unsigned flags); + /* Allocate a large block of size lb bytes. */ + /* The block is not cleared. */ + /* Flags is 0 or IGNORE_OFF_PAGE. */ + /* Calls GC_allchblk to do the actual */ + /* allocation, but also triggers GC and/or */ + /* heap expansion as appropriate. */ + /* Does not update GC_bytes_allocd, but does */ + /* other accounting. */ + +GC_INNER void GC_freehblk(struct hblk * p); + /* Deallocate a heap block and mark it */ + /* as invalid. */ + +/* Misc GC: */ +GC_INNER GC_bool GC_expand_hp_inner(word n); +GC_INNER void GC_start_reclaim(GC_bool abort_if_found); + /* Restore unmarked objects to free */ + /* lists, or (if abort_if_found is */ + /* TRUE) report them. */ + /* Sweeping of small object pages is */ + /* largely deferred. */ +GC_INNER void GC_continue_reclaim(word sz, int kind); + /* Sweep pages of the given size and */ + /* kind, as long as possible, and */ + /* as long as the corr. free list is */ + /* empty. Sz is in granules. */ + +GC_INNER GC_bool GC_reclaim_all(GC_stop_func stop_func, GC_bool ignore_old); + /* Reclaim all blocks. Abort (in a */ + /* consistent state) if f returns TRUE. */ +GC_INNER ptr_t GC_reclaim_generic(struct hblk * hbp, hdr *hhdr, size_t sz, + GC_bool init, ptr_t list, + signed_word *count); + /* Rebuild free list in hbp with */ + /* header hhdr, with objects of size sz */ + /* bytes. Add list to the end of the */ + /* free list. Add the number of */ + /* reclaimed bytes to *count. */ +GC_INNER GC_bool GC_block_empty(hdr * hhdr); + /* Block completely unmarked? */ +GC_INNER int GC_CALLBACK GC_never_stop_func(void); + /* Always returns 0 (FALSE). */ +GC_INNER GC_bool GC_try_to_collect_inner(GC_stop_func f); + + /* Collect; caller must have acquired */ + /* lock. Collection is aborted if f */ + /* returns TRUE. Returns TRUE if it */ + /* completes successfully. */ +#define GC_gcollect_inner() \ + (void)GC_try_to_collect_inner(GC_never_stop_func) + +GC_EXTERN GC_bool GC_is_initialized; /* GC_init() has been run. */ + +GC_INNER void GC_collect_a_little_inner(int n); + /* Do n units worth of garbage */ + /* collection work, if appropriate. */ + /* A unit is an amount appropriate for */ + /* HBLKSIZE bytes of allocation. */ + +GC_INNER void * GC_generic_malloc_inner(size_t lb, int k); + /* Allocate an object of the given */ + /* kind but assuming lock already held. */ +#if defined(DBG_HDRS_ALL) || defined(GC_GCJ_SUPPORT) \ + || !defined(GC_NO_FINALIZATION) + GC_INNER void * GC_generic_malloc_inner_ignore_off_page(size_t lb, int k); + /* Allocate an object, where */ + /* the client guarantees that there */ + /* will always be a pointer to the */ + /* beginning of the object while the */ + /* object is live. */ +#endif + +GC_INNER GC_bool GC_collect_or_expand(word needed_blocks, + GC_bool ignore_off_page, GC_bool retry); + +GC_INNER ptr_t GC_allocobj(size_t sz, int kind); + /* Make the indicated */ + /* free list nonempty, and return its */ + /* head. Sz is in granules. */ + +#ifdef GC_ADD_CALLER + /* GC_DBG_EXTRAS is used by GC debug API functions (unlike GC_EXTRAS */ + /* used by GC debug API macros) thus GC_RETURN_ADDR_PARENT (pointing */ + /* to client caller) should be used if possible. */ +# ifdef GC_HAVE_RETURN_ADDR_PARENT +# define GC_DBG_EXTRAS GC_RETURN_ADDR_PARENT, NULL, 0 +# else +# define GC_DBG_EXTRAS GC_RETURN_ADDR, NULL, 0 +# endif +#else +# define GC_DBG_EXTRAS "unknown", 0 +#endif + +#ifdef GC_COLLECT_AT_MALLOC + extern size_t GC_dbg_collect_at_malloc_min_lb; + /* variable visible outside for debugging */ +# define GC_DBG_COLLECT_AT_MALLOC(lb) \ + (void)((lb) >= GC_dbg_collect_at_malloc_min_lb ? \ + (GC_gcollect(), 0) : 0) +#else +# define GC_DBG_COLLECT_AT_MALLOC(lb) (void)0 +#endif /* !GC_COLLECT_AT_MALLOC */ + +/* Allocation routines that bypass the thread local cache. */ +#if defined(THREAD_LOCAL_ALLOC) && defined(GC_GCJ_SUPPORT) + GC_INNER void * GC_core_gcj_malloc(size_t, void *); +#endif + +GC_INNER void GC_init_headers(void); +GC_INNER struct hblkhdr * GC_install_header(struct hblk *h); + /* Install a header for block h. */ + /* Return 0 on failure, or the header */ + /* otherwise. */ +GC_INNER GC_bool GC_install_counts(struct hblk * h, size_t sz); + /* Set up forwarding counts for block */ + /* h of size sz. */ + /* Return FALSE on failure. */ +GC_INNER void GC_remove_header(struct hblk * h); + /* Remove the header for block h. */ +GC_INNER void GC_remove_counts(struct hblk * h, size_t sz); + /* Remove forwarding counts for h. */ +GC_INNER hdr * GC_find_header(ptr_t h); + +GC_INNER void GC_add_to_heap(struct hblk *p, size_t bytes); + /* Add a HBLKSIZE aligned chunk to the heap. */ + +#ifdef USE_PROC_FOR_LIBRARIES + GC_INNER void GC_add_to_our_memory(ptr_t p, size_t bytes); + /* Add a chunk to GC_our_memory. */ + /* If p == 0, do nothing. */ +#else +# define GC_add_to_our_memory(p, bytes) +#endif + +GC_INNER void GC_print_all_errors(void); + /* Print smashed and leaked objects, if any. */ + /* Clear the lists of such objects. */ + +GC_EXTERN void (*GC_check_heap)(void); + /* Check that all objects in the heap with */ + /* debugging info are intact. */ + /* Add any that are not to GC_smashed list. */ +GC_EXTERN void (*GC_print_all_smashed)(void); + /* Print GC_smashed if it's not empty. */ + /* Clear GC_smashed list. */ +GC_EXTERN void (*GC_print_heap_obj)(ptr_t p); + /* If possible print (using GC_err_printf) */ + /* a more detailed description (terminated with */ + /* "\n") of the object referred to by p. */ + +#if defined(LINUX) && defined(__ELF__) && !defined(SMALL_CONFIG) + void GC_print_address_map(void); + /* Print an address map of the process. */ +#endif + +#ifndef SHORT_DBG_HDRS + GC_EXTERN GC_bool GC_findleak_delay_free; + /* Do not immediately deallocate object on */ + /* free() in the leak-finding mode, just mark */ + /* it as freed (and deallocate it after GC). */ + GC_INNER GC_bool GC_check_leaked(ptr_t base); /* from dbg_mlc.c */ +#endif + +GC_EXTERN GC_bool GC_have_errors; /* We saw a smashed or leaked object. */ + /* Call error printing routine */ + /* occasionally. It is OK to read it */ + /* without acquiring the lock. */ + +#define VERBOSE 2 +#if !defined(NO_CLOCK) || !defined(SMALL_CONFIG) + /* GC_print_stats should be visible to extra/MacOS.c. */ + extern int GC_print_stats; /* Nonzero generates basic GC log. */ + /* VERBOSE generates add'l messages. */ +#else /* SMALL_CONFIG */ +# define GC_print_stats 0 + /* Will this remove the message character strings from the executable? */ + /* With a particular level of optimizations, it should... */ +#endif + +#ifdef KEEP_BACK_PTRS + GC_EXTERN long GC_backtraces; + GC_INNER void GC_generate_random_backtrace_no_gc(void); +#endif + +#ifdef LINT2 +# define GC_RAND_MAX (~0U >> 1) + GC_API_PRIV long GC_random(void); +#endif + +GC_EXTERN GC_bool GC_print_back_height; + +#ifdef MAKE_BACK_GRAPH + void GC_print_back_graph_stats(void); +#endif + +#ifdef THREADS + GC_INNER void GC_free_inner(void * p); +#endif + +/* Macros used for collector internal allocation. */ +/* These assume the collector lock is held. */ +#ifdef DBG_HDRS_ALL + GC_INNER void * GC_debug_generic_malloc_inner(size_t lb, int k); + GC_INNER void * GC_debug_generic_malloc_inner_ignore_off_page(size_t lb, + int k); +# define GC_INTERNAL_MALLOC GC_debug_generic_malloc_inner +# define GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE \ + GC_debug_generic_malloc_inner_ignore_off_page +# ifdef THREADS + GC_INNER void GC_debug_free_inner(void * p); +# define GC_INTERNAL_FREE GC_debug_free_inner +# else +# define GC_INTERNAL_FREE GC_debug_free +# endif +#else +# define GC_INTERNAL_MALLOC GC_generic_malloc_inner +# define GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE \ + GC_generic_malloc_inner_ignore_off_page +# ifdef THREADS +# define GC_INTERNAL_FREE GC_free_inner +# else +# define GC_INTERNAL_FREE GC_free +# endif +#endif /* !DBG_HDRS_ALL */ + +#ifdef USE_MUNMAP + /* Memory unmapping: */ + GC_INNER void GC_unmap_old(void); + GC_INNER void GC_merge_unmapped(void); + GC_INNER void GC_unmap(ptr_t start, size_t bytes); + GC_INNER void GC_remap(ptr_t start, size_t bytes); + GC_INNER void GC_unmap_gap(ptr_t start1, size_t bytes1, ptr_t start2, + size_t bytes2); +#endif + +#ifdef CAN_HANDLE_FORK + GC_EXTERN int GC_handle_fork; + /* Fork-handling mode: */ + /* 0 means no fork handling requested (but client could */ + /* anyway call fork() provided it is surrounded with */ + /* GC_atfork_prepare/parent/child calls); */ + /* -1 means GC tries to use pthread_at_fork if it is */ + /* available (if it succeeds then GC_handle_fork value */ + /* is changed to 1), client should nonetheless surround */ + /* fork() with GC_atfork_prepare/parent/child (for the */ + /* case of pthread_at_fork failure or absence); */ + /* 1 (or other values) means client fully relies on */ + /* pthread_at_fork (so if it is missing or failed then */ + /* abort occurs in GC_init), GC_atfork_prepare and the */ + /* accompanying routines are no-op in such a case. */ +#endif + +#ifndef GC_DISABLE_INCREMENTAL + /* Virtual dirty bit implementation: */ + /* Each implementation exports the following: */ + GC_INNER void GC_read_dirty(GC_bool output_unneeded); + /* Retrieve dirty bits. Set output_unneeded to */ + /* indicate that reading of the retrieved dirty */ + /* bits is not planned till the next retrieval. */ + GC_INNER GC_bool GC_page_was_dirty(struct hblk *h); + /* Read retrieved dirty bits. */ + + GC_INNER void GC_remove_protection(struct hblk *h, word nblocks, + GC_bool pointerfree); + /* h is about to be written or allocated. Ensure that */ + /* it is not write protected by the virtual dirty bit */ + /* implementation. I.e., this is a call that: */ + /* - hints that [h, h+nblocks) is about to be written; */ + /* - guarantees that protection is removed; */ + /* - may speed up some dirty bit implementations; */ + /* - may be essential if we need to ensure that */ + /* pointer-free system call buffers in the heap are */ + /* not protected. */ + + GC_INNER GC_bool GC_dirty_init(void); + /* Returns true if dirty bits are maintained (otherwise */ + /* it is OK to be called again if the client invokes */ + /* GC_enable_incremental once more). */ +#endif /* !GC_DISABLE_INCREMENTAL */ + +#ifdef MANUAL_VDB + GC_API void GC_dirty_inner(const void *p); /* does not require locking */ +# define GC_dirty(p) (GC_incremental ? GC_dirty_inner(p) : (void)0) +#else +# define GC_dirty(p) (void)(p) +#endif + +/* Same as GC_base but excepts and returns a pointer to const object. */ +#define GC_base_C(p) ((const void *)GC_base((/* no const */ void *)(p))) + +/* Debugging print routines: */ +void GC_print_block_list(void); +void GC_print_hblkfreelist(void); +void GC_print_heap_sects(void); +void GC_print_static_roots(void); + +extern word GC_fo_entries; /* should be visible in extra/MacOS.c */ + +#ifdef KEEP_BACK_PTRS + GC_INNER void GC_store_back_pointer(ptr_t source, ptr_t dest); + GC_INNER void GC_marked_for_finalization(ptr_t dest); +# define GC_STORE_BACK_PTR(source, dest) GC_store_back_pointer(source, dest) +# define GC_MARKED_FOR_FINALIZATION(dest) GC_marked_for_finalization(dest) +#else +# define GC_STORE_BACK_PTR(source, dest) +# define GC_MARKED_FOR_FINALIZATION(dest) +#endif + +/* Make arguments appear live to compiler */ +void GC_noop6(word, word, word, word, word, word); + +GC_API void GC_CALL GC_noop1(word); + +#ifndef GC_ATTR_FORMAT_PRINTF +# if GC_GNUC_PREREQ(3, 0) +# define GC_ATTR_FORMAT_PRINTF(spec_argnum, first_checked) \ + __attribute__((__format__(__printf__, spec_argnum, first_checked))) +# else +# define GC_ATTR_FORMAT_PRINTF(spec_argnum, first_checked) +# endif +#endif + +/* Logging and diagnostic output: */ +/* GC_printf is used typically on client explicit print requests. */ +/* For all GC_X_printf routines, it is recommended to put "\n" at */ +/* 'format' string end (for output atomicity). */ +GC_API_PRIV void GC_printf(const char * format, ...) + GC_ATTR_FORMAT_PRINTF(1, 2); + /* A version of printf that doesn't allocate, */ + /* 1K total output length. */ + /* (We use sprintf. Hopefully that doesn't */ + /* allocate for long arguments.) */ +GC_API_PRIV void GC_err_printf(const char * format, ...) + GC_ATTR_FORMAT_PRINTF(1, 2); + +/* Basic logging routine. Typically, GC_log_printf is called directly */ +/* only inside various DEBUG_x blocks. */ +GC_API_PRIV void GC_log_printf(const char * format, ...) + GC_ATTR_FORMAT_PRINTF(1, 2); + +#ifndef GC_ANDROID_LOG +# define GC_PRINT_STATS_FLAG (GC_print_stats != 0) +# define GC_INFOLOG_PRINTF GC_COND_LOG_PRINTF + /* GC_verbose_log_printf is called only if GC_print_stats is VERBOSE. */ +# define GC_verbose_log_printf GC_log_printf +#else + extern GC_bool GC_quiet; +# define GC_PRINT_STATS_FLAG (!GC_quiet) + /* INFO/DBG loggers are enabled even if GC_print_stats is off. */ +# ifndef GC_INFOLOG_PRINTF +# define GC_INFOLOG_PRINTF if (GC_quiet) {} else GC_info_log_printf +# endif + GC_INNER void GC_info_log_printf(const char *format, ...) + GC_ATTR_FORMAT_PRINTF(1, 2); + GC_INNER void GC_verbose_log_printf(const char *format, ...) + GC_ATTR_FORMAT_PRINTF(1, 2); +#endif /* GC_ANDROID_LOG */ + +/* Convenient macros for GC_[verbose_]log_printf invocation. */ +#define GC_COND_LOG_PRINTF \ + if (EXPECT(!GC_print_stats, TRUE)) {} else GC_log_printf +#define GC_VERBOSE_LOG_PRINTF \ + if (EXPECT(GC_print_stats != VERBOSE, TRUE)) {} else GC_verbose_log_printf +#ifndef GC_DBGLOG_PRINTF +# define GC_DBGLOG_PRINTF if (!GC_PRINT_STATS_FLAG) {} else GC_log_printf +#endif + +void GC_err_puts(const char *s); + /* Write s to stderr, don't buffer, don't add */ + /* newlines, don't ... */ + +/* Handy macro for logging size values (of word type) in KiB (rounding */ +/* to nearest value). */ +#define TO_KiB_UL(v) ((unsigned long)(((v) + ((1 << 9) - 1)) >> 10)) + +GC_EXTERN unsigned GC_fail_count; + /* How many consecutive GC/expansion failures? */ + /* Reset by GC_allochblk(); defined in alloc.c. */ + +GC_EXTERN long GC_large_alloc_warn_interval; /* defined in misc.c */ + +GC_EXTERN signed_word GC_bytes_found; + /* Number of reclaimed bytes after garbage collection; */ + /* protected by GC lock; defined in reclaim.c. */ + +#ifndef GC_GET_HEAP_USAGE_NOT_NEEDED + GC_EXTERN word GC_reclaimed_bytes_before_gc; + /* Number of bytes reclaimed before this */ + /* collection cycle; used for statistics only. */ +#endif + +#ifdef USE_MUNMAP + GC_EXTERN int GC_unmap_threshold; /* defined in allchblk.c */ + GC_EXTERN GC_bool GC_force_unmap_on_gcollect; /* defined in misc.c */ +#endif + +#ifdef MSWIN32 + GC_EXTERN GC_bool GC_no_win32_dlls; /* defined in os_dep.c */ + GC_EXTERN GC_bool GC_wnt; /* Is Windows NT derivative; */ + /* defined and set in os_dep.c. */ +#endif + +#ifdef THREADS +# if defined(MSWIN32) || defined(MSWINCE) || defined(MSWIN_XBOX1) + GC_EXTERN CRITICAL_SECTION GC_write_cs; /* defined in misc.c */ +# endif +# if defined(GC_ASSERTIONS) && (defined(MSWIN32) || defined(MSWINCE)) + GC_EXTERN GC_bool GC_write_disabled; + /* defined in win32_threads.c; */ + /* protected by GC_write_cs. */ + +# endif +# if defined(GC_DISABLE_INCREMENTAL) \ + || defined(set_pht_entry_from_index_concurrent) +# define GC_acquire_dirty_lock() (void)0 +# define GC_release_dirty_lock() (void)0 +# else + /* Acquire the spin lock we use to update dirty bits. */ + /* Threads should not get stopped holding it. But we may */ + /* acquire and release it during GC_remove_protection call. */ +# define GC_acquire_dirty_lock() \ + do { /* empty */ \ + } while (AO_test_and_set_acquire(&GC_fault_handler_lock) == AO_TS_SET) +# define GC_release_dirty_lock() AO_CLEAR(&GC_fault_handler_lock) + GC_EXTERN volatile AO_TS_t GC_fault_handler_lock; + /* defined in os_dep.c */ +# endif +# ifdef MSWINCE + GC_EXTERN GC_bool GC_dont_query_stack_min; + /* Defined and set in os_dep.c. */ +# endif +#elif defined(IA64) + GC_EXTERN ptr_t GC_save_regs_ret_val; /* defined in mach_dep.c. */ + /* Previously set to backing store pointer. */ +#endif /* !THREADS */ + +#ifdef THREAD_LOCAL_ALLOC + GC_EXTERN GC_bool GC_world_stopped; /* defined in alloc.c */ + GC_INNER void GC_mark_thread_local_free_lists(void); +#endif + +#ifdef GC_GCJ_SUPPORT +# ifdef GC_ASSERTIONS + GC_EXTERN GC_bool GC_gcj_malloc_initialized; /* defined in gcj_mlc.c */ +# endif + GC_EXTERN ptr_t * GC_gcjobjfreelist; +#endif + +#ifdef MPROTECT_VDB +# ifdef GWW_VDB + GC_INNER GC_bool GC_gww_dirty_init(void); + /* Returns TRUE if GetWriteWatch is available. */ + /* May be called repeatedly. */ +# endif +# ifdef USE_MUNMAP + GC_INNER GC_bool GC_mprotect_dirty_init(void); + GC_INNER GC_bool GC_has_unmapped_memory(void); +# endif +#endif /* MPROTECT_VDB */ + +#if defined(CHECKSUMS) || defined(PROC_VDB) + GC_INNER GC_bool GC_page_was_ever_dirty(struct hblk * h); + /* Could the page contain valid heap pointers? */ +#endif + +#ifdef CHECKSUMS +# if defined(MPROTECT_VDB) && !defined(DARWIN) + void GC_record_fault(struct hblk * h); +# endif + void GC_check_dirty(void); +#endif + +GC_INNER void GC_default_print_heap_obj_proc(ptr_t p); + +GC_INNER void GC_setpagesize(void); + +GC_INNER void GC_initialize_offsets(void); /* defined in obj_map.c */ + +GC_INNER void GC_bl_init(void); +GC_INNER void GC_bl_init_no_interiors(void); /* defined in blacklst.c */ + +GC_INNER void GC_start_debugging_inner(void); /* defined in dbg_mlc.c. */ + /* Should not be called if GC_debugging_started. */ + +/* Store debugging info into p. Return displaced pointer. */ +/* Assumes we hold the allocation lock. */ +GC_INNER void *GC_store_debug_info_inner(void *p, word sz, const char *str, + int linenum); + +#ifdef REDIRECT_MALLOC +# ifdef GC_LINUX_THREADS + GC_INNER GC_bool GC_text_mapping(char *nm, ptr_t *startp, ptr_t *endp); + /* from os_dep.c */ +# endif +#elif defined(USE_WINALLOC) + GC_INNER void GC_add_current_malloc_heap(void); +#endif /* !REDIRECT_MALLOC */ + +#ifdef MAKE_BACK_GRAPH + GC_INNER void GC_build_back_graph(void); + GC_INNER void GC_traverse_back_graph(void); +#endif + +#ifdef MSWIN32 + GC_INNER void GC_init_win32(void); +#endif + +#if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) + GC_INNER void * GC_roots_present(ptr_t); + /* The type is a lie, since the real type doesn't make sense here, */ + /* and we only test for NULL. */ +#endif + +#ifdef GC_WIN32_THREADS + GC_INNER void GC_get_next_stack(char *start, char * limit, char **lo, + char **hi); +# ifdef MPROTECT_VDB + GC_INNER void GC_set_write_fault_handler(void); +# endif +# if defined(WRAP_MARK_SOME) && !defined(GC_PTHREADS) + GC_INNER GC_bool GC_started_thread_while_stopped(void); + /* Did we invalidate mark phase with an unexpected thread start? */ +# endif +#endif /* GC_WIN32_THREADS */ + +#ifdef THREADS + GC_INNER void GC_reset_finalizer_nested(void); + GC_INNER unsigned char *GC_check_finalizer_nested(void); + GC_INNER void GC_do_blocking_inner(ptr_t data, void * context); + GC_INNER void GC_push_all_stacks(void); +# ifdef USE_PROC_FOR_LIBRARIES + GC_INNER GC_bool GC_segment_is_thread_stack(ptr_t lo, ptr_t hi); +# endif +# ifdef IA64 + GC_INNER ptr_t GC_greatest_stack_base_below(ptr_t bound); +# endif +#endif /* THREADS */ + +#ifdef DYNAMIC_LOADING + GC_INNER GC_bool GC_register_main_static_data(void); +# ifdef DARWIN + GC_INNER void GC_init_dyld(void); +# endif +#endif /* DYNAMIC_LOADING */ + +#ifdef SEARCH_FOR_DATA_START + GC_INNER void GC_init_linux_data_start(void); + ptr_t GC_find_limit(ptr_t, GC_bool); +#endif + +#if defined(NETBSD) && defined(__ELF__) + GC_INNER void GC_init_netbsd_elf(void); + ptr_t GC_find_limit(ptr_t, GC_bool); +#endif + +#ifdef UNIX_LIKE + GC_INNER void GC_set_and_save_fault_handler(void (*handler)(int)); +#endif + +#ifdef NEED_PROC_MAPS +# if defined(DYNAMIC_LOADING) && defined(USE_PROC_FOR_LIBRARIES) + GC_INNER char *GC_parse_map_entry(char *buf_ptr, ptr_t *start, ptr_t *end, + char **prot, unsigned int *maj_dev, + char **mapping_name); +# endif +# if defined(IA64) || defined(INCLUDE_LINUX_THREAD_DESCR) + GC_INNER GC_bool GC_enclosing_mapping(ptr_t addr, + ptr_t *startp, ptr_t *endp); +# endif + GC_INNER char *GC_get_maps(void); /* from os_dep.c */ +#endif /* NEED_PROC_MAPS */ + +#ifdef GC_ASSERTIONS +# define GC_ASSERT(expr) \ + do { \ + if (!(expr)) { \ + GC_err_printf("Assertion failure: %s:%d\n", \ + __FILE__, __LINE__); \ + ABORT("assertion failure"); \ + } \ + } while (0) + GC_INNER word GC_compute_large_free_bytes(void); + GC_INNER word GC_compute_root_size(void); +#else +# define GC_ASSERT(expr) +#endif + +/* Check a compile time assertion at compile time. */ +#if defined(static_assert) && (__STDC_VERSION__ >= 201112L) +# define GC_STATIC_ASSERT(expr) static_assert(expr, "") +#elif defined(mips) && !defined(__GNUC__) +/* DOB: MIPSPro C gets an internal error taking the sizeof an array type. + This code works correctly (ugliness is to avoid "unused var" warnings) */ +# define GC_STATIC_ASSERT(expr) \ + do { if (0) { char j[(expr)? 1 : -1]; j[0]='\0'; j[0]=j[0]; } } while(0) +#else + /* The error message for failure is a bit baroque, but ... */ +# define GC_STATIC_ASSERT(expr) (void)sizeof(char[(expr)? 1 : -1]) +#endif + +/* Runtime check for an argument declared as non-null is actually not null. */ +#if GC_GNUC_PREREQ(4, 0) + /* Workaround tautological-pointer-compare Clang warning. */ +# define NONNULL_ARG_NOT_NULL(arg) (*(volatile void **)&(arg) != NULL) +#else +# define NONNULL_ARG_NOT_NULL(arg) (NULL != (arg)) +#endif + +#define COND_DUMP_CHECKS \ + do { \ + GC_ASSERT(GC_compute_large_free_bytes() == GC_large_free_bytes); \ + GC_ASSERT(GC_compute_root_size() == GC_root_size); \ + } while (0) + +#ifndef NO_DEBUGGING + GC_EXTERN GC_bool GC_dump_regularly; + /* Generate regular debugging dumps. */ +# define COND_DUMP if (EXPECT(GC_dump_regularly, FALSE)) { \ + GC_dump_named(NULL); \ + } else COND_DUMP_CHECKS +#else +# define COND_DUMP COND_DUMP_CHECKS +#endif + +#if defined(PARALLEL_MARK) + /* We need additional synchronization facilities from the thread */ + /* support. We believe these are less performance critical */ + /* than the main garbage collector lock; standard pthreads-based */ + /* implementations should be sufficient. */ + +# define GC_markers_m1 GC_parallel + /* Number of mark threads we would like to have */ + /* excluding the initiating thread. */ + + /* The mark lock and condition variable. If the GC lock is also */ + /* acquired, the GC lock must be acquired first. The mark lock is */ + /* used to both protect some variables used by the parallel */ + /* marker, and to protect GC_fl_builder_count, below. */ + /* GC_notify_all_marker() is called when */ + /* the state of the parallel marker changes */ + /* in some significant way (see gc_mark.h for details). The */ + /* latter set of events includes incrementing GC_mark_no. */ + /* GC_notify_all_builder() is called when GC_fl_builder_count */ + /* reaches 0. */ + + GC_INNER void GC_wait_for_markers_init(void); + GC_INNER void GC_acquire_mark_lock(void); + GC_INNER void GC_release_mark_lock(void); + GC_INNER void GC_notify_all_builder(void); + GC_INNER void GC_wait_for_reclaim(void); + + GC_EXTERN signed_word GC_fl_builder_count; /* Protected by mark lock. */ + + GC_INNER void GC_notify_all_marker(void); + GC_INNER void GC_wait_marker(void); + GC_EXTERN word GC_mark_no; /* Protected by mark lock. */ + + GC_INNER void GC_help_marker(word my_mark_no); + /* Try to help out parallel marker for mark cycle */ + /* my_mark_no. Returns if the mark cycle finishes or */ + /* was already done, or there was nothing to do for */ + /* some other reason. */ + + GC_INNER void GC_start_mark_threads_inner(void); +#endif /* PARALLEL_MARK */ + +#if defined(GC_PTHREADS) && !defined(GC_WIN32_THREADS) && !defined(NACL) \ + && !defined(GC_DARWIN_THREADS) && !defined(SIG_SUSPEND) + /* We define the thread suspension signal here, so that we can refer */ + /* to it in the dirty bit implementation, if necessary. Ideally we */ + /* would allocate a (real-time?) signal using the standard mechanism. */ + /* unfortunately, there is no standard mechanism. (There is one */ + /* in Linux glibc, but it's not exported.) Thus we continue to use */ + /* the same hard-coded signals we've always used. */ +# if (defined(GC_LINUX_THREADS) || defined(GC_DGUX386_THREADS)) \ + && !defined(GC_USESIGRT_SIGNALS) +# if defined(SPARC) && !defined(SIGPWR) + /* SPARC/Linux doesn't properly define SIGPWR in . */ + /* It is aliased to SIGLOST in asm/signal.h, though. */ +# define SIG_SUSPEND SIGLOST +# else + /* Linuxthreads itself uses SIGUSR1 and SIGUSR2. */ +# define SIG_SUSPEND SIGPWR +# endif +# elif defined(GC_OPENBSD_THREADS) +# ifndef GC_OPENBSD_UTHREADS +# define SIG_SUSPEND SIGXFSZ +# endif +# elif defined(_SIGRTMIN) && !defined(CPPCHECK) +# define SIG_SUSPEND _SIGRTMIN + 6 +# else +# define SIG_SUSPEND SIGRTMIN + 6 +# endif +#endif /* GC_PTHREADS && !SIG_SUSPEND */ + +#if defined(GC_PTHREADS) && !defined(GC_SEM_INIT_PSHARED) +# define GC_SEM_INIT_PSHARED 0 +#endif + +/* Some macros for setjmp that works across signal handlers */ +/* were possible, and a couple of routines to facilitate */ +/* catching accesses to bad addresses when that's */ +/* possible/needed. */ +#if (defined(UNIX_LIKE) || (defined(NEED_FIND_LIMIT) && defined(CYGWIN32))) \ + && !defined(GC_NO_SIGSETJMP) +# if defined(SUNOS5SIGS) && !defined(FREEBSD) && !defined(LINUX) + EXTERN_C_END +# include + EXTERN_C_BEGIN +# endif + /* Define SETJMP and friends to be the version that restores */ + /* the signal mask. */ +# define SETJMP(env) sigsetjmp(env, 1) +# define LONGJMP(env, val) siglongjmp(env, val) +# define JMP_BUF sigjmp_buf +#else +# ifdef ECOS +# define SETJMP(env) hal_setjmp(env) +# else +# define SETJMP(env) setjmp(env) +# endif +# define LONGJMP(env, val) longjmp(env, val) +# define JMP_BUF jmp_buf +#endif /* !UNIX_LIKE || GC_NO_SIGSETJMP */ + +/* Do we need the GC_find_limit machinery to find the end of a */ +/* data segment. */ +#if defined(HEURISTIC2) || defined(SEARCH_FOR_DATA_START) \ + || (!defined(STACKBOTTOM) && defined(HEURISTIC2)) \ + || ((defined(SVR4) || defined(AIX) || defined(DGUX) \ + || (defined(LINUX) && defined(SPARC))) && !defined(PCR)) +# define NEED_FIND_LIMIT +#endif + +#if defined(DATASTART_USES_BSDGETDATASTART) + EXTERN_C_END +# include + EXTERN_C_BEGIN +# if !defined(PCR) +# define NEED_FIND_LIMIT +# endif + GC_INNER ptr_t GC_FreeBSDGetDataStart(size_t, ptr_t); +# define DATASTART_IS_FUNC +#endif /* DATASTART_USES_BSDGETDATASTART */ + +#if (defined(NETBSD) || defined(OPENBSD)) && defined(__ELF__) \ + && !defined(NEED_FIND_LIMIT) + /* Used by GC_init_netbsd_elf() in os_dep.c. */ +# define NEED_FIND_LIMIT +#endif + +#if defined(IA64) && !defined(NEED_FIND_LIMIT) +# define NEED_FIND_LIMIT + /* May be needed for register backing store base. */ +#endif + +#if defined(NEED_FIND_LIMIT) \ + || (defined(USE_PROC_FOR_LIBRARIES) && defined(THREADS)) + GC_EXTERN JMP_BUF GC_jmp_buf; + + /* Set up a handler for address faults which will longjmp to */ + /* GC_jmp_buf. */ + GC_INNER void GC_setup_temporary_fault_handler(void); + /* Undo the effect of GC_setup_temporary_fault_handler. */ + GC_INNER void GC_reset_fault_handler(void); +#endif /* NEED_FIND_LIMIT || USE_PROC_FOR_LIBRARIES */ + +/* Some convenience macros for cancellation support. */ +#if defined(CANCEL_SAFE) +# if defined(GC_ASSERTIONS) \ + && (defined(USE_COMPILER_TLS) \ + || (defined(LINUX) && !defined(ARM32) && GC_GNUC_PREREQ(3, 3) \ + || defined(HPUX) /* and probably others ... */)) + extern __thread unsigned char GC_cancel_disable_count; +# define NEED_CANCEL_DISABLE_COUNT +# define INCR_CANCEL_DISABLE() ++GC_cancel_disable_count +# define DECR_CANCEL_DISABLE() --GC_cancel_disable_count +# define ASSERT_CANCEL_DISABLED() GC_ASSERT(GC_cancel_disable_count > 0) +# else +# define INCR_CANCEL_DISABLE() +# define DECR_CANCEL_DISABLE() +# define ASSERT_CANCEL_DISABLED() (void)0 +# endif /* GC_ASSERTIONS & ... */ +# define DISABLE_CANCEL(state) \ + do { pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state); \ + INCR_CANCEL_DISABLE(); } while (0) +# define RESTORE_CANCEL(state) \ + do { ASSERT_CANCEL_DISABLED(); \ + pthread_setcancelstate(state, NULL); \ + DECR_CANCEL_DISABLE(); } while (0) +#else /* !CANCEL_SAFE */ +# define DISABLE_CANCEL(state) (void)0 +# define RESTORE_CANCEL(state) (void)0 +# define ASSERT_CANCEL_DISABLED() (void)0 +#endif /* !CANCEL_SAFE */ + +EXTERN_C_END + +#endif /* GC_PRIVATE_H */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/private/gcconfig.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/private/gcconfig.h new file mode 100644 index 000000000..7982ce39f --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/private/gcconfig.h @@ -0,0 +1,3608 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 2000-2004 Hewlett-Packard Development Company, L.P. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* + * This header is private to the gc. It is almost always included from + * gc_priv.h. However it is possible to include it by itself if just the + * configuration macros are needed. In that + * case, a few declarations relying on types declared in gc_priv.h will be + * omitted. + */ + +#ifndef GCCONFIG_H +#define GCCONFIG_H + +#define MANUAL_VDB + +# ifndef GC_PRIVATE_H + /* Fake ptr_t declaration, just to avoid compilation errors. */ + /* This avoids many instances if "ifndef GC_PRIVATE_H" below. */ + typedef struct GC_undefined_struct * ptr_t; +# include /* For size_t etc. */ +# endif + +/* Note: Only wrap our own declarations, and not the included headers. */ +/* In this case, wrap our entire file, but temporarily unwrap/rewrap */ +/* around #includes. Types and macros do not need such wrapping, only */ +/* the declared global data and functions. */ +#ifdef __cplusplus +# define EXTERN_C_BEGIN extern "C" { +# define EXTERN_C_END } /* extern "C" */ +#else +# define EXTERN_C_BEGIN /* empty */ +# define EXTERN_C_END /* empty */ +#endif + +EXTERN_C_BEGIN + +/* Convenient internal macro to test version of Clang. */ +#if defined(__clang__) && defined(__clang_major__) +# define GC_CLANG_PREREQ(major, minor) \ + ((__clang_major__ << 16) + __clang_minor__ >= ((major) << 16) + (minor)) +# define GC_CLANG_PREREQ_FULL(major, minor, patchlevel) \ + (GC_CLANG_PREREQ(major, (minor) + 1) \ + || (__clang_major__ == (major) && __clang_minor__ == (minor) \ + && __clang_patchlevel__ >= (patchlevel))) +#else +# define GC_CLANG_PREREQ(major, minor) 0 /* FALSE */ +# define GC_CLANG_PREREQ_FULL(major, minor, patchlevel) 0 +#endif + +#ifdef LINT2 + /* A macro (based on a tricky expression) to prevent false warnings */ + /* like "Array compared to 0", "Comparison of identical expressions", */ + /* "Untrusted loop bound" output by some static code analysis tools. */ + /* The argument should not be a literal value. The result is */ + /* converted to word type. (Actually, GC_word is used instead of */ + /* word type as the latter might be undefined at the place of use.) */ +# define COVERT_DATAFLOW(w) (~(GC_word)(w)^(~(GC_word)0)) +#else +# define COVERT_DATAFLOW(w) ((GC_word)(w)) +#endif + +/* Machine dependent parameters. Some tuning parameters can be found */ +/* near the top of gc_private.h. */ + +/* Machine specific parts contributed by various people. See README file. */ + +#if defined(__ANDROID__) && !defined(HOST_ANDROID) + /* __ANDROID__ macro is defined by Android NDK gcc. */ +# define HOST_ANDROID 1 +#endif + +#if defined(HOST_ANDROID) +#define IGNORE_PROG_DATA_START +#endif + +#if defined(TIZEN) && !defined(HOST_TIZEN) +# define HOST_TIZEN 1 +#endif + +#if defined(__SYMBIAN32__) && !defined(SYMBIAN) +# define SYMBIAN +# ifdef __WINS__ +# pragma data_seg(".data2") +# endif +#endif + +/* First a unified test for Linux: */ +# if (defined(linux) || defined(__linux__) || defined(HOST_ANDROID)) \ + && !defined(LINUX) && !defined(__native_client__) +# define LINUX +# endif + +/* And one for QNX: */ +# if defined(__QNX__) +# define I386 +# define OS_TYPE "QNX" +# define SA_RESTART 0 +# define HEURISTIC1 + extern char etext[]; + extern int _end[]; +# define DATASTART ((ptr_t)(etext)) +# define DATAEND ((ptr_t)(_end)) +# define mach_type_known +# endif + +/* And one for NetBSD: */ +# if defined(__NetBSD__) +# define NETBSD +# endif + +/* And one for OpenBSD: */ +# if defined(__OpenBSD__) +# define OPENBSD +# endif + +/* And one for FreeBSD: */ +# if (defined(__FreeBSD__) || defined(__DragonFly__) \ + || defined(__FreeBSD_kernel__)) && !defined(FREEBSD) \ + && !defined(SN_TARGET_ORBIS) /* Orbis compiler defines __FreeBSD__ */ +# define FREEBSD +# endif + +/* And one for Darwin: */ +# if defined(macosx) || (defined(__APPLE__) && defined(__MACH__)) +# define DARWIN + EXTERN_C_END +# include + EXTERN_C_BEGIN +# endif + +/* Determine the machine type: */ +# if defined(__native_client__) +# define NACL +# if !defined(__portable_native_client__) && !defined(__arm__) +# define I386 +# define mach_type_known +# else + /* Here we will rely upon arch-specific defines. */ +# endif +# endif +# if defined(__aarch64__) +# define AARCH64 +# if !defined(LINUX) && !defined(DARWIN) && !defined(FREEBSD) \ + && !defined(NN_BUILD_TARGET_PLATFORM_NX) +# define NOSYS +# define mach_type_known +# endif +# endif +# if defined(__arm) || defined(__arm__) || defined(__thumb__) +# define ARM32 +# if defined(NACL) +# define mach_type_known +# elif !defined(LINUX) && !defined(NETBSD) && !defined(FREEBSD) \ + && !defined(OPENBSD) && !defined(DARWIN) && !defined(_WIN32) \ + && !defined(__CEGCC__) && !defined(NN_PLATFORM_CTR) \ + && !defined(NN_BUILD_TARGET_PLATFORM_NX) \ + && !defined(SN_TARGET_ORBIS) && !defined(SN_TARGET_PSP2) \ + && !defined(SYMBIAN) +# define NOSYS +# define mach_type_known +# endif +# endif +# if defined(sun) && defined(mc68000) && !defined(CPPCHECK) +# error SUNOS4 no longer supported +# endif +# if defined(hp9000s300) && !defined(CPPCHECK) +# error M68K based HP machines no longer supported. +# endif +# if defined(OPENBSD) && defined(m68k) + /* FIXME: Should we remove this case? */ +# define M68K +# define mach_type_known +# endif +# if defined(OPENBSD) && defined(__sparc__) +# define SPARC +# define mach_type_known +# endif +# if defined(OPENBSD) && defined(__arm__) +# define ARM32 +# define mach_type_known +# endif +# if defined(OPENBSD) && defined(__sh__) +# define SH +# define mach_type_known +# endif +# if defined(NETBSD) && (defined(m68k) || defined(__m68k__)) +# define M68K +# define mach_type_known +# endif +# if defined(NETBSD) && defined(__powerpc__) +# define POWERPC +# define mach_type_known +# endif +# if defined(NETBSD) && (defined(__arm32__) || defined(__arm__)) +# define ARM32 +# define mach_type_known +# endif +# if defined(NETBSD) && defined(__sh__) +# define SH +# define mach_type_known +# endif +# if defined(vax) || defined(__vax__) +# define VAX +# ifdef ultrix +# define ULTRIX +# else +# define BSD +# endif +# define mach_type_known +# endif +# if defined(__NetBSD__) && defined(__vax__) +# define VAX +# define mach_type_known +# endif +# if defined(mips) || defined(__mips) || defined(_mips) +# define MIPS +# if defined(nec_ews) || defined(_nec_ews) +# define EWS4800 +# endif +# if !defined(LINUX) && !defined(EWS4800) && !defined(NETBSD) \ + && !defined(OPENBSD) +# if defined(ultrix) || defined(__ultrix) +# define ULTRIX +# else +# define IRIX5 /* or IRIX 6.X */ +# endif +# endif /* !LINUX */ +# if defined(__NetBSD__) && defined(__MIPSEL__) +# undef ULTRIX +# endif +# define mach_type_known +# endif +# if defined(__NIOS2__) || defined(__NIOS2) || defined(__nios2__) +# define NIOS2 /* Altera NIOS2 */ +# define mach_type_known +# endif +# if defined(__or1k__) +# define OR1K /* OpenRISC/or1k */ +# define mach_type_known +# endif +# if defined(DGUX) && (defined(i386) || defined(__i386__)) +# define I386 +# ifndef _USING_DGUX +# define _USING_DGUX +# endif +# define mach_type_known +# endif +# if defined(sequent) && (defined(i386) || defined(__i386__)) +# define I386 +# define SEQUENT +# define mach_type_known +# endif +# if (defined(sun) || defined(__sun)) && (defined(i386) || defined(__i386__)) +# define I386 +# define SOLARIS +# define mach_type_known +# endif +# if (defined(sun) || defined(__sun)) && defined(__amd64) +# define X86_64 +# define SOLARIS +# define mach_type_known +# endif +# if (defined(__OS2__) || defined(__EMX__)) && defined(__32BIT__) +# define I386 +# define OS2 +# define mach_type_known +# endif +# if defined(ibm032) && !defined(CPPCHECK) +# error IBM PC/RT no longer supported. +# endif +# if (defined(sun) || defined(__sun)) && (defined(sparc) || defined(__sparc)) + /* Test for SunOS 5.x */ + EXTERN_C_END +# include + EXTERN_C_BEGIN +# define SPARC +# define SOLARIS +# define mach_type_known +# elif defined(sparc) && defined(unix) && !defined(sun) && !defined(linux) \ + && !defined(__OpenBSD__) && !defined(__NetBSD__) \ + && !defined(__FreeBSD__) && !defined(__DragonFly__) +# define SPARC +# define DRSNX +# define mach_type_known +# endif +# if defined(_IBMR2) +# define POWERPC +# define AIX +# define mach_type_known +# endif +# if defined(__NetBSD__) && defined(__sparc__) +# define SPARC +# define mach_type_known +# endif +# if defined(_M_XENIX) && defined(_M_SYSV) && defined(_M_I386) + /* The above test may need refinement */ +# define I386 +# if defined(_SCO_ELF) +# define SCO_ELF +# else +# define SCO +# endif +# define mach_type_known +# endif +# if defined(_AUX_SOURCE) && !defined(CPPCHECK) +# error A/UX no longer supported +# endif +# if defined(_PA_RISC1_0) || defined(_PA_RISC1_1) || defined(_PA_RISC2_0) \ + || defined(hppa) || defined(__hppa__) +# define HP_PA +# if !defined(LINUX) && !defined(HPUX) && !defined(OPENBSD) +# define HPUX +# endif +# define mach_type_known +# endif +# if defined(__ia64) && (defined(_HPUX_SOURCE) || defined(__HP_aCC)) +# define IA64 +# ifndef HPUX +# define HPUX +# endif +# define mach_type_known +# endif +# if (defined(__BEOS__) || defined(__HAIKU__)) && defined(_X86_) +# define I386 +# define HAIKU +# define mach_type_known +# endif +# if defined(__HAIKU__) && (defined(__amd64__) || defined(__x86_64__)) +# define X86_64 +# define HAIKU +# define mach_type_known +# endif +# if defined(OPENBSD) && defined(__amd64__) +# define X86_64 +# define mach_type_known +# endif +# if defined(LINUX) && (defined(i386) || defined(__i386__)) +# define I386 +# define mach_type_known +# endif +# if defined(LINUX) && defined(__x86_64__) +# define X86_64 +# define mach_type_known +# endif +# if defined(LINUX) && (defined(__ia64__) || defined(__ia64)) +# define IA64 +# define mach_type_known +# endif +# if defined(LINUX) && defined(__aarch64__) +# define AARCH64 +# define mach_type_known +# endif +# if defined(LINUX) && (defined(__arm) || defined(__arm__)) +# define ARM32 +# define mach_type_known +# endif +# if defined(LINUX) && defined(__cris__) +# ifndef CRIS +# define CRIS +# endif +# define mach_type_known +# endif +# if defined(LINUX) && (defined(powerpc) || defined(__powerpc__) \ + || defined(powerpc64) || defined(__powerpc64__)) +# define POWERPC +# define mach_type_known +# endif +# if defined(LINUX) && defined(__mc68000__) +# define M68K +# define mach_type_known +# endif +# if defined(LINUX) && (defined(sparc) || defined(__sparc__)) +# define SPARC +# define mach_type_known +# endif +# if defined(LINUX) && defined(__sh__) +# define SH +# define mach_type_known +# endif +# if defined(LINUX) && defined(__avr32__) +# define AVR32 +# define mach_type_known +# endif +# if defined(LINUX) && defined(__m32r__) +# define M32R +# define mach_type_known +# endif +# if defined(__alpha) || defined(__alpha__) +# define ALPHA +# if !defined(LINUX) && !defined(NETBSD) && !defined(OPENBSD) \ + && !defined(FREEBSD) +# define OSF1 /* a.k.a Digital Unix */ +# endif +# define mach_type_known +# endif +# if defined(_AMIGA) && !defined(AMIGA) +# define AMIGA +# endif +# ifdef AMIGA +# define M68K +# define mach_type_known +# endif +# if defined(THINK_C) \ + || (defined(__MWERKS__) && !defined(__powerc) && !defined(SYMBIAN)) +# define M68K +# define MACOS +# define mach_type_known +# endif +# if defined(__MWERKS__) && defined(__powerc) && !defined(__MACH__) \ + && !defined(SYMBIAN) +# define POWERPC +# define MACOS +# define mach_type_known +# endif +# if defined(__OpenBSD__) && defined(__powerpc__) +# define POWERPC +# define OPENBSD +# define mach_type_known +# endif +# if defined(DARWIN) +# if defined(__ppc__) || defined(__ppc64__) +# define POWERPC +# define mach_type_known +# elif defined(__x86_64__) || defined(__x86_64) +# define X86_64 +# define mach_type_known +# elif defined(__i386__) +# define I386 +# define mach_type_known +# elif defined(__arm__) +# define ARM32 +# define mach_type_known +# elif defined(__aarch64__) +# define AARCH64 +# define mach_type_known +# endif +# endif +# if defined(__rtems__) && (defined(i386) || defined(__i386__)) +# define I386 +# define RTEMS +# define mach_type_known +# endif +# if defined(NeXT) && defined(mc68000) +# define M68K +# define NEXT +# define mach_type_known +# endif +# if defined(NeXT) && (defined(i386) || defined(__i386__)) +# define I386 +# define NEXT +# define mach_type_known +# endif +# if defined(__OpenBSD__) && (defined(i386) || defined(__i386__)) +# define I386 +# define OPENBSD +# define mach_type_known +# endif +# if defined(__NetBSD__) && (defined(i386) || defined(__i386__)) +# define I386 +# define mach_type_known +# endif +# if defined(__NetBSD__) && defined(__x86_64__) +# define X86_64 +# define mach_type_known +# endif +# if defined(FREEBSD) && (defined(i386) || defined(__i386__)) +# define I386 +# define mach_type_known +# endif +# if (defined(FREEBSD) || defined(SN_TARGET_ORBIS)) \ + && (defined(__amd64__) || defined(__x86_64__)) +# define X86_64 +# define mach_type_known +# endif +# if defined(FREEBSD) && defined(__sparc__) +# define SPARC +# define mach_type_known +# endif +# if defined(FREEBSD) && (defined(powerpc) || defined(__powerpc__)) +# define POWERPC +# define mach_type_known +# endif +# if defined(FREEBSD) && defined(__arm__) +# define ARM32 +# define mach_type_known +# endif +# if defined(FREEBSD) && defined(__aarch64__) +# define AARCH64 +# define mach_type_known +# endif +# if defined(FREEBSD) && (defined(mips) || defined(__mips) || defined(_mips)) +# define MIPS +# define mach_type_known +# endif +# if defined(bsdi) && (defined(i386) || defined(__i386__)) +# define I386 +# define BSDI +# define mach_type_known +# endif +# if !defined(mach_type_known) && defined(__386BSD__) +# define I386 +# define THREE86BSD +# define mach_type_known +# endif +# if defined(_CX_UX) && defined(_M88K) +# define M88K +# define CX_UX +# define mach_type_known +# endif +# if defined(DGUX) && defined(m88k) +# define M88K + /* DGUX defined */ +# define mach_type_known +# endif +# if defined(_WIN32_WCE) || defined(__CEGCC__) || defined(__MINGW32CE__) + /* SH3, SH4, MIPS already defined for corresponding architectures */ +# if defined(SH3) || defined(SH4) +# define SH +# endif +# if defined(x86) || defined(__i386__) +# define I386 +# endif +# if defined(_M_ARM) || defined(ARM) || defined(_ARM_) +# define ARM32 +# endif +# define MSWINCE +# define mach_type_known +# else +# if ((defined(_MSDOS) || defined(_MSC_VER)) && (_M_IX86 >= 300)) \ + || (defined(_WIN32) && !defined(__CYGWIN32__) && !defined(__CYGWIN__) \ + && !defined(SYMBIAN)) +# if defined(__LP64__) || defined(_M_X64) +# define X86_64 +# elif defined(_M_ARM) +# define ARM32 +# elif defined(_M_ARM64) +# define AARCH64 +# else /* _M_IX86 */ +# define I386 +# endif +# ifdef _XBOX_ONE +# define MSWIN_XBOX1 +# else +# define MSWIN32 /* or Win64 */ +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +# define MSWINRT_FLAVOR +# endif +# endif +# define mach_type_known +# endif +# if defined(_MSC_VER) && defined(_M_IA64) +# define IA64 +# define MSWIN32 /* Really win64, but we don't treat 64-bit */ + /* variants as a different platform. */ +# endif +# endif +# if defined(__DJGPP__) +# define I386 +# ifndef DJGPP +# define DJGPP /* MSDOS running the DJGPP port of GCC */ +# endif +# define mach_type_known +# endif +# if defined(__CYGWIN32__) || defined(__CYGWIN__) +# if defined(__LP64__) +# define X86_64 +# else +# define I386 +# endif +# define CYGWIN32 +# define mach_type_known +# endif +# if defined(__MINGW32__) && !defined(mach_type_known) +# define I386 +# define MSWIN32 +# define mach_type_known +# endif +# if defined(__BORLANDC__) +# define I386 +# define MSWIN32 +# define mach_type_known +# endif +# if defined(_UTS) && !defined(mach_type_known) +# define S370 +# define UTS4 +# define mach_type_known +# endif +# if defined(__pj__) && !defined(CPPCHECK) +# error PicoJava no longer supported + /* The implementation had problems, and I haven't heard of users */ + /* in ages. If you want it resurrected, let me know. */ +# endif +# if defined(__embedded__) && defined(PPC) +# define POWERPC +# define NOSYS +# define mach_type_known +# endif + +# if defined(__WATCOMC__) && defined(__386__) +# define I386 +# if !defined(OS2) && !defined(MSWIN32) && !defined(DOS4GW) +# if defined(__OS2__) +# define OS2 +# else +# if defined(__WINDOWS_386__) || defined(__NT__) +# define MSWIN32 +# else +# define DOS4GW +# endif +# endif +# endif +# define mach_type_known +# endif +# if defined(__s390__) && defined(LINUX) +# define S390 +# define mach_type_known +# endif +# if defined(__GNU__) +# if defined(__i386__) +/* The Debian Hurd running on generic PC */ +# define HURD +# define I386 +# define mach_type_known +# endif +# endif +# if defined(__TANDEM) + /* Nonstop S-series */ + /* FIXME: Should recognize Integrity series? */ +# define MIPS +# define NONSTOP +# define mach_type_known +# endif +# if defined(__hexagon__) && defined(LINUX) +# define HEXAGON +# define mach_type_known +# endif +# if defined(__tile__) && defined(LINUX) +# ifdef __tilegx__ +# define TILEGX +# else +# define TILEPRO +# endif +# define mach_type_known +# endif +# if defined(__riscv) && defined(LINUX) +# define RISCV +# define mach_type_known +# endif + +# if defined(SN_TARGET_PSP2) +# define mach_type_known +# endif + +# if defined(NN_PLATFORM_CTR) +# define mach_type_known +# endif + +# if defined(NN_BUILD_TARGET_PLATFORM_NX) +# define NINTENDO_SWITCH +# define mach_type_known +# endif + +# if defined(SYMBIAN) +# define mach_type_known +# endif + +# if defined(__EMSCRIPTEN__) +# define I386 +# define mach_type_known +# endif + +/* Feel free to add more clauses here */ + +/* Or manually define the machine type here. A machine type is */ +/* characterized by the architecture. Some */ +/* machine types are further subdivided by OS. */ +/* Macros such as LINUX, FREEBSD, etc. distinguish them. */ +/* SYSV on an M68K actually means A/UX. */ +/* The distinction in these cases is usually the stack starting address */ +# if !defined(mach_type_known) && !defined(CPPCHECK) +# error "The collector has not been ported to this machine/OS combination." +# endif + /* Mapping is: M68K ==> Motorola 680X0 */ + /* (NEXT, and SYSV (A/UX), */ + /* MACOS and AMIGA variants) */ + /* I386 ==> Intel 386 */ + /* (SEQUENT, OS2, SCO, LINUX, NETBSD, */ + /* FREEBSD, THREE86BSD, MSWIN32, */ + /* BSDI, SOLARIS, NEXT and others) */ + /* NS32K ==> Encore Multimax */ + /* MIPS ==> R2000 through R14K */ + /* (many variants) */ + /* VAX ==> DEC VAX */ + /* (BSD, ULTRIX variants) */ + /* HP_PA ==> HP9000/700 & /800 */ + /* HP/UX, LINUX */ + /* SPARC ==> SPARC v7/v8/v9 */ + /* (SOLARIS, LINUX, DRSNX variants) */ + /* ALPHA ==> DEC Alpha */ + /* (OSF1 and LINUX variants) */ + /* M88K ==> Motorola 88XX0 */ + /* (CX_UX and DGUX) */ + /* S370 ==> 370-like machine */ + /* running Amdahl UTS4 */ + /* S390 ==> 390-like machine */ + /* running LINUX */ + /* AARCH64 ==> ARM AArch64 */ + /* ARM32 ==> Intel StrongARM */ + /* IA64 ==> Intel IPF */ + /* (e.g. Itanium) */ + /* (LINUX and HPUX) */ + /* SH ==> Hitachi SuperH */ + /* (LINUX & MSWINCE) */ + /* X86_64 ==> AMD x86-64 */ + /* POWERPC ==> IBM/Apple PowerPC */ + /* (MACOS(<=9),DARWIN(incl.MACOSX),*/ + /* LINUX, NETBSD, AIX, NOSYS */ + /* variants) */ + /* Handles 32 and 64-bit variants. */ + /* CRIS ==> Axis Etrax */ + /* M32R ==> Renesas M32R */ + /* HEXAGON ==> Qualcomm Hexagon */ + /* TILEPRO ==> Tilera TILEPro */ + /* TILEGX ==> Tilera TILE-Gx */ + + +/* + * For each architecture and OS, the following need to be defined: + * + * CPP_WORDSZ is a simple integer constant representing the word size. + * in bits. We assume byte addressability, where a byte has 8 bits. + * We also assume CPP_WORDSZ is either 32 or 64. + * (We care about the length of pointers, not hardware + * bus widths. Thus a 64 bit processor with a C compiler that uses + * 32 bit pointers should use CPP_WORDSZ of 32, not 64. Default is 32.) + * + * MACH_TYPE is a string representation of the machine type. + * OS_TYPE is analogous for the OS. + * + * ALIGNMENT is the largest N, such that + * all pointer are guaranteed to be aligned on N byte boundaries. + * defining it to be 1 will always work, but perform poorly. + * + * DATASTART is the beginning of the data segment. + * On some platforms SEARCH_FOR_DATA_START is defined. + * SEARCH_FOR_DATASTART will cause GC_data_start to + * be set to an address determined by accessing data backwards from _end + * until an unmapped page is found. DATASTART will be defined to be + * GC_data_start. + * On UNIX-like systems, the collector will scan the area between DATASTART + * and DATAEND for root pointers. + * + * DATAEND, if not "end", where "end" is defined as "extern int end[]". + * RTH suggests gaining access to linker script synth'd values with + * this idiom instead of "&end", where "end" is defined as "extern int end". + * Otherwise, "GCC will assume these are in .sdata/.sbss" and it will, e.g., + * cause failures on alpha*-*-* with -msmall-data or -fpic or mips-*-* + * without any special options. + * + * STACKBOTTOM is the cool end of the stack, which is usually the + * highest address in the stack. + * Under PCR or OS/2, we have other ways of finding thread stacks. + * For each machine, the following should: + * 1) define STACK_GROWS_UP if the stack grows toward higher addresses, and + * 2) define exactly one of + * STACKBOTTOM (should be defined to be an expression) + * LINUX_STACKBOTTOM + * HEURISTIC1 + * HEURISTIC2 + * If STACKBOTTOM is defined, then its value will be used directly as the + * stack base. If LINUX_STACKBOTTOM is defined, then it will be determined + * with a method appropriate for most Linux systems. Currently we look + * first for __libc_stack_end (currently only if USE_LIBC_PRIVATES is + * defined), and if that fails read it from /proc. (If USE_LIBC_PRIVATES + * is not defined and NO_PROC_STAT is defined, we revert to HEURISTIC2.) + * If either of the last two macros are defined, then STACKBOTTOM is computed + * during collector startup using one of the following two heuristics: + * HEURISTIC1: Take an address inside GC_init's frame, and round it up to + * the next multiple of STACK_GRAN. + * HEURISTIC2: Take an address inside GC_init's frame, increment it repeatedly + * in small steps (decrement if STACK_GROWS_UP), and read the value + * at each location. Remember the value when the first + * Segmentation violation or Bus error is signaled. Round that + * to the nearest plausible page boundary, and use that instead + * of STACKBOTTOM. + * + * Gustavo Rodriguez-Rivera points out that on most (all?) Unix machines, + * the value of environ is a pointer that can serve as STACKBOTTOM. + * I expect that HEURISTIC2 can be replaced by this approach, which + * interferes far less with debugging. However it has the disadvantage + * that it's confused by a putenv call before the collector is initialized. + * This could be dealt with by intercepting putenv ... + * + * If no expression for STACKBOTTOM can be found, and neither of the above + * heuristics are usable, the collector can still be used with all of the above + * undefined, provided one of the following is done: + * 1) GC_mark_roots can be changed to somehow mark from the correct stack(s) + * without reference to STACKBOTTOM. This is appropriate for use in + * conjunction with thread packages, since there will be multiple stacks. + * (Allocating thread stacks in the heap, and treating them as ordinary + * heap data objects is also possible as a last resort. However, this is + * likely to introduce significant amounts of excess storage retention + * unless the dead parts of the thread stacks are periodically cleared.) + * 2) Client code may set GC_stackbottom before calling any GC_ routines. + * If the author of the client code controls the main program, this is + * easily accomplished by introducing a new main program, setting + * GC_stackbottom to the address of a local variable, and then calling + * the original main program. The new main program would read something + * like (provided real_main() is not inlined by the compiler): + * + * #include "gc.h" + * + * main(argc, argv, envp) + * int argc; + * char **argv, **envp; + * { + * volatile int dummy; + * + * GC_stackbottom = (ptr_t)(&dummy); + * return(real_main(argc, argv, envp)); + * } + * + * + * Each architecture may also define the style of virtual dirty bit + * implementation to be used: + * MPROTECT_VDB: Write protect the heap and catch faults. + * GWW_VDB: Use win32 GetWriteWatch primitive. + * PROC_VDB: Use the SVR4 /proc primitives to read dirty bits. + * + * The first and second one may be combined, in which case a runtime + * selection will be made, based on GetWriteWatch availability. + * + * An architecture may define DYNAMIC_LOADING if dyn_load.c + * defined GC_register_dynamic_libraries() for the architecture. + * + * An architecture may define PREFETCH(x) to preload the cache with *x. + * This defaults to GCC built-in operation (or a no-op for other compilers). + * + * GC_PREFETCH_FOR_WRITE(x) is used if *x is about to be written. + * + * An architecture may also define CLEAR_DOUBLE(x) to be a fast way to + * clear the two words at GC_malloc-aligned address x. By default, + * word stores of 0 are used instead. + * + * HEAP_START may be defined as the initial address hint for mmap-based + * allocation. + */ + +/* If available, we can use __builtin_unwind_init() to push the */ +/* relevant registers onto the stack. */ +# if GC_GNUC_PREREQ(2, 8) \ + && !defined(__INTEL_COMPILER) && !defined(__PATHCC__) \ + && !defined(__FUJITSU) /* for FX10 system */ \ + && !(defined(POWERPC) && defined(DARWIN)) /* for MacOS X 10.3.9 */ \ + && !defined(RTEMS) \ + && !defined(__ARMCC_VERSION) /* does not exist in armcc gnu emu */ \ + && !defined(__clang__) /* since no-op in clang (3.0) */ +# define HAVE_BUILTIN_UNWIND_INIT +# endif + +# ifdef SYMBIAN +# define MACH_TYPE "SYMBIAN" +# define OS_TYPE "SYMBIAN" +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 +# define DATASTART (ptr_t)ALIGNMENT /* cannot be null */ +# define DATAEND (ptr_t)ALIGNMENT +# endif + +# ifdef __EMSCRIPTEN__ +# define OS_TYPE "EMSCRIPTEN" +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 +# define DATASTART (ptr_t)ALIGNMENT +# define DATAEND (ptr_t)ALIGNMENT + /* Since JavaScript/asm.js/WebAssembly is not able to access the */ + /* function call stack or the local data stack, it is not possible */ + /* for GC to perform its stack walking operation to find roots on */ + /* the stack. To work around that, the clients generally only do */ + /* BDWGC steps when the stack is empty so it is known that there */ + /* are no objects that would be found on the stack, and BDWGC is */ + /* compiled with stack walking disabled. */ +# define STACK_NOT_SCANNED +# endif + +# define STACK_GRAN 0x1000000 +# ifdef M68K +# define MACH_TYPE "M68K" +# define ALIGNMENT 2 +# ifdef OPENBSD + /* FIXME: Should we remove this case? */ +# define OS_TYPE "OPENBSD" +# define HEURISTIC2 +# ifdef __ELF__ + extern ptr_t GC_data_start; +# define DATASTART GC_data_start +# define DYNAMIC_LOADING +# else + extern char etext[]; +# define DATASTART ((ptr_t)(etext)) +# endif +# endif +# ifdef NETBSD +# define OS_TYPE "NETBSD" +# define HEURISTIC2 +# ifdef __ELF__ + extern ptr_t GC_data_start; +# define DATASTART GC_data_start +# define DYNAMIC_LOADING +# else + extern char etext[]; +# define DATASTART ((ptr_t)(etext)) +# endif +# endif +# ifdef LINUX +# define OS_TYPE "LINUX" +# define LINUX_STACKBOTTOM +# define MPROTECT_VDB +# ifdef __ELF__ +# define DYNAMIC_LOADING + EXTERN_C_END +# include + EXTERN_C_BEGIN +# if defined(__GLIBC__) && __GLIBC__ >= 2 +# define SEARCH_FOR_DATA_START +# else /* !GLIBC2 */ + extern char **__environ; +# define DATASTART ((ptr_t)(&__environ)) + /* hideous kludge: __environ is the first */ + /* word in crt0.o, and delimits the start */ + /* of the data segment, no matter which */ + /* ld options were passed through. */ + /* We could use _etext instead, but that */ + /* would include .rodata, which may */ + /* contain large read-only data tables */ + /* that we'd rather not scan. */ +# endif /* !GLIBC2 */ + extern int _end[]; +# define DATAEND ((ptr_t)(_end)) +# else + extern int etext[]; +# define DATASTART ((ptr_t)((((word)(etext)) + 0xfff) & ~0xfff)) +# endif +# endif +# ifdef AMIGA +# define OS_TYPE "AMIGA" + /* STACKBOTTOM and DATASTART handled specially */ + /* in os_dep.c */ +# define DATAEND /* not needed */ +# define GETPAGESIZE() 4096 +# endif +# ifdef MACOS +# ifndef __LOWMEM__ + EXTERN_C_END +# include + EXTERN_C_BEGIN +# endif +# define OS_TYPE "MACOS" + /* see os_dep.c for details of global data segments. */ +# define STACKBOTTOM ((ptr_t)LMGetCurStackBase()) +# define DATAEND /* not needed */ +# define GETPAGESIZE() 4096 +# endif +# ifdef NEXT +# define OS_TYPE "NEXT" +# define DATASTART ((ptr_t)get_etext()) +# define DATASTART_IS_FUNC +# define STACKBOTTOM ((ptr_t)0x4000000) +# define DATAEND /* not needed */ +# endif +# endif + +# if defined(POWERPC) +# define MACH_TYPE "POWERPC" +# ifdef MACOS +# define ALIGNMENT 2 /* Still necessary? Could it be 4? */ +# ifndef __LOWMEM__ + EXTERN_C_END +# include + EXTERN_C_BEGIN +# endif +# define OS_TYPE "MACOS" + /* see os_dep.c for details of global data segments. */ +# define STACKBOTTOM ((ptr_t)LMGetCurStackBase()) +# define DATAEND /* not needed */ +# endif +# ifdef LINUX +# if defined(__powerpc64__) +# define ALIGNMENT 8 +# define CPP_WORDSZ 64 +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# else +# define ALIGNMENT 4 +# endif +# define OS_TYPE "LINUX" + /* HEURISTIC1 has been reliably reported to fail for a 32-bit */ + /* executable on a 64 bit kernel. */ +# if defined(__bg__) + /* The Linux Compute Node Kernel (used on BlueGene systems) */ + /* does not support LINUX_STACKBOTTOM way. */ +# define HEURISTIC2 +# define NO_PTHREAD_GETATTR_NP +# else +# define LINUX_STACKBOTTOM +# endif +# define DYNAMIC_LOADING +# define SEARCH_FOR_DATA_START + extern int _end[]; +# define DATAEND ((ptr_t)(_end)) +# endif +# ifdef DARWIN +# define OS_TYPE "DARWIN" +# define DYNAMIC_LOADING +# if defined(__ppc64__) +# define ALIGNMENT 8 +# define CPP_WORDSZ 64 +# define STACKBOTTOM ((ptr_t)0x7fff5fc00000) +# define CACHE_LINE_SIZE 64 +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# else +# define ALIGNMENT 4 +# define STACKBOTTOM ((ptr_t)0xc0000000) +# endif + /* XXX: see get_end(3), get_etext() and get_end() should not be used. */ + /* These aren't used when dyld support is enabled (it is by default). */ +# define DATASTART ((ptr_t)get_etext()) +# define DATAEND ((ptr_t)get_end()) +# define USE_MMAP_ANON +# define MPROTECT_VDB + EXTERN_C_END +# include + EXTERN_C_BEGIN +# define GETPAGESIZE() (unsigned)getpagesize() +# if defined(USE_PPC_PREFETCH) && defined(__GNUC__) + /* The performance impact of prefetches is untested */ +# define PREFETCH(x) \ + __asm__ __volatile__ ("dcbt 0,%0" : : "r" ((const void *) (x))) +# define GC_PREFETCH_FOR_WRITE(x) \ + __asm__ __volatile__ ("dcbtst 0,%0" : : "r" ((const void *) (x))) +# endif + /* There seems to be some issues with trylock hanging on darwin. */ + /* This should be looked into some more. */ +# define NO_PTHREAD_TRYLOCK +# endif +# ifdef OPENBSD +# define OS_TYPE "OPENBSD" +# define ALIGNMENT 4 +# ifndef GC_OPENBSD_THREADS + EXTERN_C_END +# include +# include + EXTERN_C_BEGIN + /* USRSTACK is defined in but that is */ + /* protected by _KERNEL in file. */ +# ifdef USRSTACK +# define STACKBOTTOM ((ptr_t)USRSTACK) +# else +# define HEURISTIC2 +# endif +# endif + extern int __data_start[]; +# define DATASTART ((ptr_t)__data_start) + extern int _end[]; +# define DATAEND ((ptr_t)(&_end)) +# define DYNAMIC_LOADING +# endif +# ifdef FREEBSD +# if defined(__powerpc64__) +# define ALIGNMENT 8 +# define CPP_WORDSZ 64 +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# else +# define ALIGNMENT 4 +# endif +# define OS_TYPE "FREEBSD" +# ifndef GC_FREEBSD_THREADS +# define MPROTECT_VDB +# endif +# define SIG_SUSPEND SIGUSR1 +# define SIG_THR_RESTART SIGUSR2 +# define FREEBSD_STACKBOTTOM +# ifdef __ELF__ +# define DYNAMIC_LOADING +# endif + extern char etext[]; +# define DATASTART GC_FreeBSDGetDataStart(0x1000, (ptr_t)etext) +# define DATASTART_USES_BSDGETDATASTART +# endif +# ifdef NETBSD +# define ALIGNMENT 4 +# define OS_TYPE "NETBSD" +# define HEURISTIC2 + extern ptr_t GC_data_start; +# define DATASTART GC_data_start +# define DYNAMIC_LOADING +# endif +# ifdef SN_TARGET_PS3 +# define NO_GETENV +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 + extern int _end[]; + extern int __bss_start; +# define DATASTART ((ptr_t)(__bss_start)) +# define DATAEND ((ptr_t)(_end)) +# define STACKBOTTOM ((ptr_t)ps3_get_stack_bottom()) +# define NO_PTHREAD_TRYLOCK + /* Current GC LOCK() implementation for PS3 explicitly */ + /* use pthread_mutex_lock for some reason. */ +# endif +# ifdef AIX +# define OS_TYPE "AIX" +# undef ALIGNMENT /* in case it's defined */ +# undef IA64 + /* DOB: some AIX installs stupidly define IA64 in */ + /* /usr/include/sys/systemcfg.h */ +# ifdef __64BIT__ +# define ALIGNMENT 8 +# define CPP_WORDSZ 64 +# define STACKBOTTOM ((ptr_t)0x1000000000000000) +# else +# define ALIGNMENT 4 +# define CPP_WORDSZ 32 +# define STACKBOTTOM ((ptr_t)((ulong)&errno)) +# endif +# define USE_MMAP_ANON + /* From AIX linker man page: + _text Specifies the first location of the program. + _etext Specifies the first location after the program. + _data Specifies the first location of the data. + _edata Specifies the first location after the initialized data + _end or end Specifies the first location after all data. + */ + extern int _data[], _end[]; +# define DATASTART ((ptr_t)((ulong)_data)) +# define DATAEND ((ptr_t)((ulong)_end)) + extern int errno; +# define DYNAMIC_LOADING + /* For really old versions of AIX, this may have to be removed. */ +# endif + +# ifdef NOSYS +# define ALIGNMENT 4 +# define OS_TYPE "NOSYS" + extern void __end[], __dso_handle[]; +# define DATASTART ((ptr_t)__dso_handle) /* OK, that's ugly. */ +# define DATAEND ((ptr_t)(__end)) + /* Stack starts at 0xE0000000 for the simulator. */ +# undef STACK_GRAN +# define STACK_GRAN 0x10000000 +# define HEURISTIC1 +# endif +# endif + +# ifdef NACL +# define OS_TYPE "NACL" +# if defined(__GLIBC__) +# define DYNAMIC_LOADING +# endif +# define DATASTART ((ptr_t)0x10020000) + extern int _end[]; +# define DATAEND ((ptr_t)_end) +# undef STACK_GRAN +# define STACK_GRAN 0x10000 +# define HEURISTIC1 +# define NO_PTHREAD_GETATTR_NP +# define USE_MUNMAP +# define USE_MMAP_ANON +# define GETPAGESIZE() 65536 +# define MAX_NACL_GC_THREADS 1024 +# endif + +# ifdef VAX +# define MACH_TYPE "VAX" +# define ALIGNMENT 4 /* Pointers are longword aligned by 4.2 C compiler */ + extern char etext[]; +# define DATASTART ((ptr_t)(etext)) +# ifdef BSD +# define OS_TYPE "BSD" +# define HEURISTIC1 + /* HEURISTIC2 may be OK, but it's hard to test. */ +# endif +# ifdef ULTRIX +# define OS_TYPE "ULTRIX" +# define STACKBOTTOM ((ptr_t)0x7fffc800) +# endif +# endif + +# ifdef SPARC +# define MACH_TYPE "SPARC" +# if defined(__arch64__) || defined(__sparcv9) +# define ALIGNMENT 8 +# define CPP_WORDSZ 64 +# define ELF_CLASS ELFCLASS64 +# else +# define ALIGNMENT 4 /* Required by hardware */ +# define CPP_WORDSZ 32 +# endif + /* Don't define USE_ASM_PUSH_REGS. We do use an asm helper, but */ + /* not to push the registers on the mark stack. */ +# ifdef SOLARIS +# define OS_TYPE "SOLARIS" + extern int _etext[]; + extern int _end[]; + ptr_t GC_SysVGetDataStart(size_t, ptr_t); +# define DATASTART GC_SysVGetDataStart(0x10000, (ptr_t)_etext) +# define DATASTART_IS_FUNC +# define DATAEND ((ptr_t)(_end)) +# if !defined(USE_MMAP) && defined(REDIRECT_MALLOC) +# define USE_MMAP + /* Otherwise we now use calloc. Mmap may result in the */ + /* heap interleaved with thread stacks, which can result in */ + /* excessive blacklisting. Sbrk is unusable since it */ + /* doesn't interact correctly with the system malloc. */ +# endif +# ifdef USE_MMAP +# define HEAP_START (ptr_t)0x40000000 +# else +# define HEAP_START DATAEND +# endif +# define PROC_VDB + /* HEURISTIC1 reportedly no longer works under 2.7. */ + /* HEURISTIC2 probably works, but this appears to be preferable.*/ + /* Apparently USRSTACK is defined to be USERLIMIT, but in some */ + /* installations that's undefined. We work around this with a */ + /* gross hack: */ + EXTERN_C_END +# include +# include + EXTERN_C_BEGIN +# ifdef USERLIMIT + /* This should work everywhere, but doesn't. */ +# define STACKBOTTOM ((ptr_t)USRSTACK) +# else +# define HEURISTIC2 +# endif +# define GETPAGESIZE() (unsigned)sysconf(_SC_PAGESIZE) + /* getpagesize() appeared to be missing from at least one */ + /* Solaris 5.4 installation. Weird. */ +# define DYNAMIC_LOADING +# endif +# ifdef DRSNX +# define OS_TYPE "DRSNX" + extern int etext[]; + ptr_t GC_SysVGetDataStart(size_t, ptr_t); +# define DATASTART GC_SysVGetDataStart(0x10000, (ptr_t)etext) +# define DATASTART_IS_FUNC +# define MPROTECT_VDB +# define STACKBOTTOM ((ptr_t)0xdfff0000) +# define DYNAMIC_LOADING +# endif +# ifdef LINUX +# define OS_TYPE "LINUX" +# ifdef __ELF__ +# define DYNAMIC_LOADING +# else +# error --> Linux SPARC a.out not supported +# endif + extern int _end[]; + extern int _etext[]; +# define DATAEND ((ptr_t)(_end)) +# define SVR4 + ptr_t GC_SysVGetDataStart(size_t, ptr_t); +# ifdef __arch64__ +# define DATASTART GC_SysVGetDataStart(0x100000, (ptr_t)_etext) +# else +# define DATASTART GC_SysVGetDataStart(0x10000, (ptr_t)_etext) +# endif +# define DATASTART_IS_FUNC +# define LINUX_STACKBOTTOM +# endif +# ifdef OPENBSD +# define OS_TYPE "OPENBSD" +# ifndef GC_OPENBSD_THREADS + EXTERN_C_END +# include +# include + EXTERN_C_BEGIN +# ifdef USRSTACK +# define STACKBOTTOM ((ptr_t)USRSTACK) +# else +# define HEURISTIC2 +# endif +# endif + extern int __data_start[]; +# define DATASTART ((ptr_t)__data_start) + extern int _end[]; +# define DATAEND ((ptr_t)(&_end)) +# define DYNAMIC_LOADING +# endif +# ifdef NETBSD +# define OS_TYPE "NETBSD" +# define HEURISTIC2 +# ifdef __ELF__ + extern ptr_t GC_data_start; +# define DATASTART GC_data_start +# define DYNAMIC_LOADING +# else + extern char etext[]; +# define DATASTART ((ptr_t)(etext)) +# endif +# endif +# ifdef FREEBSD +# define OS_TYPE "FREEBSD" +# define SIG_SUSPEND SIGUSR1 +# define SIG_THR_RESTART SIGUSR2 +# define FREEBSD_STACKBOTTOM +# ifdef __ELF__ +# define DYNAMIC_LOADING +# endif + extern char etext[]; + extern char edata[]; + extern char end[]; +# define NEED_FIND_LIMIT +# define DATASTART ((ptr_t)(&etext)) + ptr_t GC_find_limit(ptr_t, GC_bool); +# define DATAEND GC_find_limit(DATASTART, TRUE) +# define DATAEND_IS_FUNC +# define GC_HAVE_DATAREGION2 +# define DATASTART2 ((ptr_t)(&edata)) +# define DATAEND2 ((ptr_t)(&end)) +# endif +# endif + +# ifdef I386 +# define MACH_TYPE "I386" +# if defined(__LP64__) || defined(_WIN64) +# error This should be handled as X86_64 +# else +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 + /* Appears to hold for all "32 bit" compilers */ + /* except Borland. The -a4 option fixes */ + /* Borland. For Watcom the option is -zp4. */ +# endif +# ifdef SEQUENT +# define OS_TYPE "SEQUENT" + extern int etext[]; +# define DATASTART ((ptr_t)((((word)(etext)) + 0xfff) & ~0xfff)) +# define STACKBOTTOM ((ptr_t)0x3ffff000) +# endif +# ifdef HAIKU +# define OS_TYPE "HAIKU" + EXTERN_C_END +# include + EXTERN_C_BEGIN +# define GETPAGESIZE() (unsigned)B_PAGE_SIZE + extern int etext[]; +# define DATASTART ((ptr_t)((((word)(etext)) + 0xfff) & ~0xfff)) +# define DYNAMIC_LOADING +# define MPROTECT_VDB +# endif +# ifdef SOLARIS +# define OS_TYPE "SOLARIS" + extern int _etext[], _end[]; + ptr_t GC_SysVGetDataStart(size_t, ptr_t); +# define DATASTART GC_SysVGetDataStart(0x1000, (ptr_t)_etext) +# define DATASTART_IS_FUNC +# define DATAEND ((ptr_t)(_end)) + /* # define STACKBOTTOM ((ptr_t)(_start)) worked through 2.7, */ + /* but reportedly breaks under 2.8. It appears that the stack */ + /* base is a property of the executable, so this should not */ + /* break old executables. */ + /* HEURISTIC2 probably works, but this appears to be preferable.*/ + /* Apparently USRSTACK is defined to be USERLIMIT, but in some */ + /* installations that's undefined. We work around this with a */ + /* gross hack: */ + EXTERN_C_END +# include + EXTERN_C_BEGIN +# ifdef USERLIMIT + /* This should work everywhere, but doesn't. */ +# define STACKBOTTOM ((ptr_t)USRSTACK) +# else +# define HEURISTIC2 +# endif +/* At least in Solaris 2.5, PROC_VDB gives wrong values for dirty bits. */ +/* It appears to be fixed in 2.8 and 2.9. */ +# ifdef SOLARIS25_PROC_VDB_BUG_FIXED +# define PROC_VDB +# endif +# ifndef GC_THREADS +# define MPROTECT_VDB +# endif +# define DYNAMIC_LOADING +# if !defined(USE_MMAP) && defined(REDIRECT_MALLOC) +# define USE_MMAP + /* Otherwise we now use calloc. Mmap may result in the */ + /* heap interleaved with thread stacks, which can result in */ + /* excessive blacklisting. Sbrk is unusable since it */ + /* doesn't interact correctly with the system malloc. */ +# endif +# ifdef USE_MMAP +# define HEAP_START (ptr_t)0x40000000 +# else +# define HEAP_START DATAEND +# endif +# endif +# ifdef SCO +# define OS_TYPE "SCO" + extern int etext[]; +# define DATASTART ((ptr_t)((((word)(etext)) + 0x3fffff) & ~0x3fffff) \ + + ((word)(etext) & 0xfff)) +# define STACKBOTTOM ((ptr_t)0x7ffffffc) +# endif +# ifdef SCO_ELF +# define OS_TYPE "SCO_ELF" + extern int etext[]; +# define DATASTART ((ptr_t)(etext)) +# define STACKBOTTOM ((ptr_t)0x08048000) +# define DYNAMIC_LOADING +# define ELF_CLASS ELFCLASS32 +# endif +# ifdef DGUX +# define OS_TYPE "DGUX" + extern int _etext, _end; + ptr_t GC_SysVGetDataStart(size_t, ptr_t); +# define DATASTART GC_SysVGetDataStart(0x1000, (ptr_t)(&_etext)) +# define DATASTART_IS_FUNC +# define DATAEND ((ptr_t)(&_end)) +# define STACK_GROWS_DOWN +# define HEURISTIC2 + EXTERN_C_END +# include + EXTERN_C_BEGIN +# define GETPAGESIZE() (unsigned)sysconf(_SC_PAGESIZE) +# define DYNAMIC_LOADING +# ifndef USE_MMAP +# define USE_MMAP +# endif +# define MAP_FAILED (void *) ((word)-1) +# define HEAP_START (ptr_t)0x40000000 +# endif /* DGUX */ +# ifdef LINUX +# define OS_TYPE "LINUX" +# define LINUX_STACKBOTTOM +# if !defined(GC_LINUX_THREADS) || !defined(REDIRECT_MALLOC) +# define MPROTECT_VDB +# else + /* We seem to get random errors in incremental mode, */ + /* possibly because Linux threads is itself a malloc client */ + /* and can't deal with the signals. */ +# endif +# define HEAP_START (ptr_t)0x1000 + /* This encourages mmap to give us low addresses, */ + /* thus allowing the heap to grow to ~3GB */ +# ifdef __ELF__ +# define DYNAMIC_LOADING + EXTERN_C_END +# include + EXTERN_C_BEGIN +# if defined(__GLIBC__) && __GLIBC__ >= 2 \ + || defined(HOST_ANDROID) || defined(HOST_TIZEN) +# define SEARCH_FOR_DATA_START +# else + extern char **__environ; +# define DATASTART ((ptr_t)(&__environ)) + /* hideous kludge: __environ is the first */ + /* word in crt0.o, and delimits the start */ + /* of the data segment, no matter which */ + /* ld options were passed through. */ + /* We could use _etext instead, but that */ + /* would include .rodata, which may */ + /* contain large read-only data tables */ + /* that we'd rather not scan. */ +# endif + extern int _end[]; +# define DATAEND ((ptr_t)(_end)) +# if !defined(GC_NO_SIGSETJMP) && (defined(HOST_TIZEN) \ + || (defined(HOST_ANDROID) \ + && !(GC_GNUC_PREREQ(4, 8) || GC_CLANG_PREREQ(3, 2) \ + || __ANDROID_API__ >= 18))) + /* Older Android NDK releases lack sigsetjmp in x86 libc */ + /* (setjmp is used instead to find data_start). The bug */ + /* is fixed in Android NDK r8e (so, ok to use sigsetjmp */ + /* if gcc4.8+, clang3.2+ or Android API level 18+). */ +# define GC_NO_SIGSETJMP +# endif +# else + extern int etext[]; +# define DATASTART ((ptr_t)((((word)(etext)) + 0xfff) & ~0xfff)) +# endif +# ifdef USE_I686_PREFETCH +# define PREFETCH(x) \ + __asm__ __volatile__ ("prefetchnta %0" : : "m"(*(char *)(x))) + /* Empirically prefetcht0 is much more effective at reducing */ + /* cache miss stalls for the targeted load instructions. But it */ + /* seems to interfere enough with other cache traffic that the */ + /* net result is worse than prefetchnta. */ +# ifdef FORCE_WRITE_PREFETCH + /* Using prefetches for write seems to have a slight negative */ + /* impact on performance, at least for a PIII/500. */ +# define GC_PREFETCH_FOR_WRITE(x) \ + __asm__ __volatile__ ("prefetcht0 %0" : : "m"(*(char *)(x))) +# else +# define GC_NO_PREFETCH_FOR_WRITE +# endif +# elif defined(USE_3DNOW_PREFETCH) +# define PREFETCH(x) \ + __asm__ __volatile__ ("prefetch %0" : : "m"(*(char *)(x))) +# define GC_PREFETCH_FOR_WRITE(x) \ + __asm__ __volatile__ ("prefetchw %0" : : "m"(*(char *)(x))) +# endif +# if defined(__GLIBC__) && !defined(__UCLIBC__) + /* Workaround lock elision implementation for some glibc. */ +# define GLIBC_2_19_TSX_BUG + EXTERN_C_END +# include /* for gnu_get_libc_version() */ + EXTERN_C_BEGIN +# endif +# endif +# ifdef CYGWIN32 +# define OS_TYPE "CYGWIN32" +# define DATASTART ((ptr_t)GC_DATASTART) /* From gc.h */ +# define DATAEND ((ptr_t)GC_DATAEND) +# undef STACK_GRAN +# define STACK_GRAN 0x10000 +# ifdef USE_MMAP +# define NEED_FIND_LIMIT +# define USE_MMAP_ANON +# endif +# endif +# ifdef OS2 +# define OS_TYPE "OS2" + /* STACKBOTTOM and DATASTART are handled specially in */ + /* os_dep.c. OS2 actually has the right */ + /* system call! */ +# define DATAEND /* not needed */ +# endif +# ifdef MSWIN32 +# define OS_TYPE "MSWIN32" + /* STACKBOTTOM and DATASTART are handled specially in */ + /* os_dep.c. */ +# define MPROTECT_VDB +# define GWW_VDB +# define DATAEND /* not needed */ +# endif +# ifdef MSWINCE +# define OS_TYPE "MSWINCE" +# define DATAEND /* not needed */ +# endif +# ifdef DJGPP +# define OS_TYPE "DJGPP" + EXTERN_C_END +# include "stubinfo.h" + EXTERN_C_BEGIN + extern int etext[]; + extern int _stklen; + extern int __djgpp_stack_limit; +# define DATASTART ((ptr_t)((((word)(etext)) + 0x1ff) & ~0x1ff)) +/* #define STACKBOTTOM ((ptr_t)((word)_stubinfo+_stubinfo->size+_stklen)) */ +# define STACKBOTTOM ((ptr_t)((word)__djgpp_stack_limit + _stklen)) + /* This may not be right. */ +# endif +# ifdef OPENBSD +# define OS_TYPE "OPENBSD" +# ifndef GC_OPENBSD_THREADS + EXTERN_C_END +# include +# include + EXTERN_C_BEGIN +# ifdef USRSTACK +# define STACKBOTTOM ((ptr_t)USRSTACK) +# else +# define HEURISTIC2 +# endif +# endif + extern int __data_start[]; +# define DATASTART ((ptr_t)__data_start) + extern int _end[]; +# define DATAEND ((ptr_t)(&_end)) +# define DYNAMIC_LOADING +# endif +# ifdef FREEBSD +# define OS_TYPE "FREEBSD" +# ifndef GC_FREEBSD_THREADS +# define MPROTECT_VDB +# endif +# ifdef __GLIBC__ +# define SIG_SUSPEND (32+6) +# define SIG_THR_RESTART (32+5) + extern int _end[]; +# define DATAEND ((ptr_t)(_end)) +# else +# define SIG_SUSPEND SIGUSR1 +# define SIG_THR_RESTART SIGUSR2 + /* SIGTSTP and SIGCONT could be used alternatively. */ +# endif +# define FREEBSD_STACKBOTTOM +# ifdef __ELF__ +# define DYNAMIC_LOADING +# endif + extern char etext[]; +# define DATASTART GC_FreeBSDGetDataStart(0x1000, (ptr_t)etext) +# define DATASTART_USES_BSDGETDATASTART +# endif +# ifdef NETBSD +# define OS_TYPE "NETBSD" +# ifdef __ELF__ +# define DYNAMIC_LOADING +# endif +# endif +# ifdef THREE86BSD +# define OS_TYPE "THREE86BSD" +# endif +# ifdef BSDI +# define OS_TYPE "BSDI" +# endif +# if defined(NETBSD) || defined(THREE86BSD) || defined(BSDI) +# define HEURISTIC2 + extern char etext[]; +# define DATASTART ((ptr_t)(etext)) +# endif +# ifdef NEXT +# define OS_TYPE "NEXT" +# define DATASTART ((ptr_t)get_etext()) +# define DATASTART_IS_FUNC +# define STACKBOTTOM ((ptr_t)0xc0000000) +# define DATAEND /* not needed */ +# endif +# ifdef RTEMS +# define OS_TYPE "RTEMS" + EXTERN_C_END +# include + EXTERN_C_BEGIN + extern int etext[]; + extern int end[]; + void *rtems_get_stack_bottom(void); +# define InitStackBottom rtems_get_stack_bottom() +# define DATASTART ((ptr_t)etext) +# define DATAEND ((ptr_t)end) +# define STACKBOTTOM ((ptr_t)InitStackBottom) +# define SIG_SUSPEND SIGUSR1 +# define SIG_THR_RESTART SIGUSR2 +# endif +# ifdef DOS4GW +# define OS_TYPE "DOS4GW" + extern long __nullarea; + extern char _end; + extern char *_STACKTOP; + /* Depending on calling conventions Watcom C either precedes */ + /* or does not precedes with underscore names of C-variables. */ + /* Make sure startup code variables always have the same names. */ + #pragma aux __nullarea "*"; + #pragma aux _end "*"; +# define STACKBOTTOM ((ptr_t)_STACKTOP) + /* confused? me too. */ +# define DATASTART ((ptr_t)(&__nullarea)) +# define DATAEND ((ptr_t)(&_end)) +# endif +# ifdef HURD +# define OS_TYPE "HURD" +# define STACK_GROWS_DOWN +# define HEURISTIC2 +# define SIG_SUSPEND SIGUSR1 +# define SIG_THR_RESTART SIGUSR2 +# define SEARCH_FOR_DATA_START + extern int _end[]; +# define DATAEND ((ptr_t)(_end)) +/* # define MPROTECT_VDB Not quite working yet? */ +# define DYNAMIC_LOADING +# define USE_MMAP_ANON +# endif +# ifdef DARWIN +# define OS_TYPE "DARWIN" +# define DARWIN_DONT_PARSE_STACK +# ifndef GC_DONT_REGISTER_MAIN_STATIC_DATA +# define DYNAMIC_LOADING +# endif + /* XXX: see get_end(3), get_etext() and get_end() should not be used. */ + /* These aren't used when dyld support is enabled (it is by default). */ +# define DATASTART ((ptr_t)get_etext()) +# define DATAEND ((ptr_t)get_end()) +# define STACKBOTTOM ((ptr_t)0xc0000000) +# define USE_MMAP_ANON +# define MPROTECT_VDB + EXTERN_C_END +# include + EXTERN_C_BEGIN +# define GETPAGESIZE() (unsigned)getpagesize() + /* There seems to be some issues with trylock hanging on darwin. */ + /* This should be looked into some more. */ +# define NO_PTHREAD_TRYLOCK +# if TARGET_OS_IPHONE && !defined(NO_DYLD_BIND_FULLY_IMAGE) + /* iPhone/iPad simulator */ +# define NO_DYLD_BIND_FULLY_IMAGE +# endif +# endif /* DARWIN */ +# endif + +# ifdef NS32K +# define MACH_TYPE "NS32K" +# define ALIGNMENT 4 + extern char **environ; +# define DATASTART ((ptr_t)(&environ)) + /* hideous kludge: environ is the first */ + /* word in crt0.o, and delimits the start */ + /* of the data segment, no matter which */ + /* ld options were passed through. */ +# define STACKBOTTOM ((ptr_t)0xfffff000) /* for Encore */ +# endif + +# ifdef MIPS +# define MACH_TYPE "MIPS" +# ifdef LINUX +# define OS_TYPE "LINUX" +# define DYNAMIC_LOADING + extern int _end[]; +# pragma weak __data_start + extern int __data_start[]; +# define DATASTART ((ptr_t)(__data_start)) +# define DATAEND ((ptr_t)(_end)) +# ifdef _MIPS_SZPTR +# define CPP_WORDSZ _MIPS_SZPTR +# define ALIGNMENT (_MIPS_SZPTR/8) +# else +# define ALIGNMENT 4 +# endif +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# if __GLIBC__ == 2 && __GLIBC_MINOR__ >= 2 || __GLIBC__ > 2 +# define LINUX_STACKBOTTOM +# else +# define STACKBOTTOM ((ptr_t)0x7fff8000) +# endif +# endif /* Linux */ +# ifdef EWS4800 +# define HEURISTIC2 +# if defined(_MIPS_SZPTR) && (_MIPS_SZPTR == 64) + extern int _fdata[], _end[]; +# define DATASTART ((ptr_t)_fdata) +# define DATAEND ((ptr_t)_end) +# define CPP_WORDSZ _MIPS_SZPTR +# define ALIGNMENT (_MIPS_SZPTR/8) +# else + extern int etext[], edata[], end[]; + extern int _DYNAMIC_LINKING[], _gp[]; +# define DATASTART ((ptr_t)((((word)(etext) + 0x3ffff) & ~0x3ffff) \ + + ((word)(etext) & 0xffff))) +# define DATAEND ((ptr_t)(edata)) +# define GC_HAVE_DATAREGION2 +# define DATASTART2 (_DYNAMIC_LINKING \ + ? (ptr_t)(((word)_gp + 0x8000 + 0x3ffff) & ~0x3ffff) \ + : (ptr_t)edata) +# define DATAEND2 ((ptr_t)(end)) +# define ALIGNMENT 4 +# endif +# define OS_TYPE "EWS4800" +# endif +# ifdef ULTRIX +# define HEURISTIC2 +# define DATASTART ((ptr_t)0x10000000) + /* Could probably be slightly higher since */ + /* startup code allocates lots of stuff. */ +# define OS_TYPE "ULTRIX" +# define ALIGNMENT 4 +# endif +# ifdef IRIX5 +# define HEURISTIC2 + extern int _fdata[]; +# define DATASTART ((ptr_t)(_fdata)) +# ifdef USE_MMAP +# define HEAP_START (ptr_t)0x30000000 +# else +# define HEAP_START DATASTART +# endif + /* Lowest plausible heap address. */ + /* In the MMAP case, we map there. */ + /* In either case it is used to identify */ + /* heap sections so they're not */ + /* considered as roots. */ +# define OS_TYPE "IRIX5" +/*# define MPROTECT_VDB DOB: this should work, but there is evidence */ +/* of recent breakage. */ +# ifdef _MIPS_SZPTR +# define CPP_WORDSZ _MIPS_SZPTR +# define ALIGNMENT (_MIPS_SZPTR/8) +# else +# define ALIGNMENT 4 +# endif +# define DYNAMIC_LOADING +# endif +# ifdef MSWINCE +# define OS_TYPE "MSWINCE" +# define ALIGNMENT 4 +# define DATAEND /* not needed */ +# endif +# if defined(NETBSD) +# define OS_TYPE "NETBSD" +# define ALIGNMENT 4 +# define HEURISTIC2 +# ifdef __ELF__ + extern ptr_t GC_data_start; +# define DATASTART GC_data_start +# define NEED_FIND_LIMIT +# define DYNAMIC_LOADING +# else +# define DATASTART ((ptr_t)0x10000000) +# define STACKBOTTOM ((ptr_t)0x7ffff000) +# endif /* _ELF_ */ +# endif +# ifdef OPENBSD +# define OS_TYPE "OPENBSD" +# define ALIGNMENT 4 +# ifndef GC_OPENBSD_THREADS + EXTERN_C_END +# include +# include + EXTERN_C_BEGIN +# ifdef USRSTACK +# define STACKBOTTOM ((ptr_t)USRSTACK) +# else +# define HEURISTIC2 +# endif +# endif + extern int _fdata[]; +# define DATASTART ((ptr_t)_fdata) + extern int _end[]; +# define DATAEND ((ptr_t)(&_end)) +# define DYNAMIC_LOADING +# endif +# ifdef FREEBSD +# define OS_TYPE "FREEBSD" +# define ALIGNMENT 4 +# ifndef GC_FREEBSD_THREADS +# define MPROTECT_VDB +# endif +# define SIG_SUSPEND SIGUSR1 +# define SIG_THR_RESTART SIGUSR2 +# define FREEBSD_STACKBOTTOM +# ifdef __ELF__ +# define DYNAMIC_LOADING +# endif + extern char etext[]; +# define DATASTART GC_FreeBSDGetDataStart(0x1000, (ptr_t)etext) +# define DATASTART_USES_BSDGETDATASTART +# endif /* FreeBSD */ +# if defined(NONSTOP) +# define CPP_WORDSZ 32 +# define OS_TYPE "NONSTOP" +# define ALIGNMENT 4 +# define DATASTART ((ptr_t)0x08000000) + extern char **environ; +# define DATAEND ((ptr_t)(environ - 0x10)) +# define STACKBOTTOM ((ptr_t)0x4fffffff) +# endif +# endif + +# ifdef NIOS2 +# define CPP_WORDSZ 32 +# define MACH_TYPE "NIOS2" +# ifdef LINUX +# define OS_TYPE "LINUX" +# define DYNAMIC_LOADING + extern int _end[]; + extern int __data_start[]; +# define DATASTART ((ptr_t)(__data_start)) +# define DATAEND ((ptr_t)(_end)) +# define ALIGNMENT 4 +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# define LINUX_STACKBOTTOM +# endif /* Linux */ +# endif + +# ifdef OR1K +# define CPP_WORDSZ 32 +# define MACH_TYPE "OR1K" +# ifdef LINUX +# define OS_TYPE "LINUX" +# define DYNAMIC_LOADING + extern int _end[]; + extern int __data_start[]; +# define DATASTART ((ptr_t)(__data_start)) +# define DATAEND ((ptr_t)(_end)) +# define ALIGNMENT 4 +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# define LINUX_STACKBOTTOM +# endif /* Linux */ +# endif + +# ifdef HP_PA +# define MACH_TYPE "HP_PA" +# ifdef __LP64__ +# define CPP_WORDSZ 64 +# define ALIGNMENT 8 +# else +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 +# endif +# if !defined(GC_HPUX_THREADS) && !defined(GC_LINUX_THREADS) \ + && !defined(OPENBSD) && !defined(LINUX) /* For now. */ +# define MPROTECT_VDB +# endif +# define STACK_GROWS_UP +# ifdef HPUX +# define OS_TYPE "HPUX" + extern int __data_start[]; +# define DATASTART ((ptr_t)(__data_start)) +# ifdef USE_HPUX_FIXED_STACKBOTTOM + /* The following appears to work for 7xx systems running HP/UX */ + /* 9.xx. Furthermore, it might result in much faster */ + /* collections than HEURISTIC2, which may involve scanning */ + /* segments that directly precede the stack. It is not the */ + /* default, since it may not work on older machine/OS */ + /* combinations. (Thanks to Raymond X.T. Nijssen for uncovering */ + /* this.) */ +# define STACKBOTTOM ((ptr_t)0x7b033000) /* from /etc/conf/h/param.h */ +# else + /* Gustavo Rodriguez-Rivera suggested changing HEURISTIC2 */ + /* to this. Note that the GC must be initialized before the */ + /* first putenv call. */ + extern char ** environ; +# define STACKBOTTOM ((ptr_t)environ) +# endif +# define DYNAMIC_LOADING + EXTERN_C_END +# include + EXTERN_C_BEGIN +# define GETPAGESIZE() (unsigned)sysconf(_SC_PAGE_SIZE) +# ifndef __GNUC__ +# define PREFETCH(x) do { \ + register long addr = (long)(x); \ + (void) _asm ("LDW", 0, 0, addr, 0); \ + } while (0) +# endif +# endif /* HPUX */ +# ifdef LINUX +# define OS_TYPE "LINUX" +# define LINUX_STACKBOTTOM +# define DYNAMIC_LOADING +# define SEARCH_FOR_DATA_START + extern int _end[]; +# define DATAEND ((ptr_t)(&_end)) +# endif /* LINUX */ +# ifdef OPENBSD +# define OS_TYPE "OPENBSD" +# ifndef GC_OPENBSD_THREADS + EXTERN_C_END +# include +# include + EXTERN_C_BEGIN +# ifdef USRSTACK +# define STACKBOTTOM ((ptr_t)USRSTACK) +# else +# define HEURISTIC2 +# endif +# endif + extern int __data_start[]; +# define DATASTART ((ptr_t)__data_start) + extern int _end[]; +# define DATAEND ((ptr_t)(&_end)) +# define DYNAMIC_LOADING +# endif +# endif /* HP_PA */ + +# ifdef ALPHA +# define MACH_TYPE "ALPHA" +# define ALIGNMENT 8 +# define CPP_WORDSZ 64 +# ifdef NETBSD +# define OS_TYPE "NETBSD" +# define HEURISTIC2 + extern ptr_t GC_data_start; +# define DATASTART GC_data_start +# define ELFCLASS32 32 +# define ELFCLASS64 64 +# define ELF_CLASS ELFCLASS64 +# define DYNAMIC_LOADING +# endif +# ifdef OPENBSD +# define OS_TYPE "OPENBSD" +# define ELF_CLASS ELFCLASS64 +# ifndef GC_OPENBSD_THREADS + EXTERN_C_END +# include +# include + EXTERN_C_BEGIN +# ifdef USRSTACK +# define STACKBOTTOM ((ptr_t)USRSTACK) +# else +# define HEURISTIC2 +# endif +# endif + extern int __data_start[]; +# define DATASTART ((ptr_t)__data_start) + extern int _end[]; +# define DATAEND ((ptr_t)(&_end)) +# define DYNAMIC_LOADING +# endif +# ifdef FREEBSD +# define OS_TYPE "FREEBSD" +/* MPROTECT_VDB is not yet supported at all on FreeBSD/alpha. */ +# define SIG_SUSPEND SIGUSR1 +# define SIG_THR_RESTART SIGUSR2 + /* SIGTSTP and SIGCONT could be used alternatively. */ +# define FREEBSD_STACKBOTTOM +# ifdef __ELF__ +# define DYNAMIC_LOADING +# endif +/* Handle unmapped hole alpha*-*-freebsd[45]* puts between etext and edata. */ + extern char etext[]; + extern char edata[]; + extern char end[]; +# define NEED_FIND_LIMIT +# define DATASTART ((ptr_t)(&etext)) + ptr_t GC_find_limit(ptr_t, GC_bool); +# define DATAEND GC_find_limit(DATASTART, TRUE) +# define DATAEND_IS_FUNC +# define GC_HAVE_DATAREGION2 +# define DATASTART2 ((ptr_t)(&edata)) +# define DATAEND2 ((ptr_t)(&end)) +# endif +# ifdef OSF1 +# define OS_TYPE "OSF1" +# define DATASTART ((ptr_t)0x140000000) + extern int _end[]; +# define DATAEND ((ptr_t)(&_end)) + extern char ** environ; + /* round up from the value of environ to the nearest page boundary */ + /* Probably breaks if putenv is called before collector */ + /* initialization. */ +# define STACKBOTTOM ((ptr_t)(((word)(environ) | (getpagesize()-1))+1)) +/* # define HEURISTIC2 */ + /* Normally HEURISTIC2 is too conservative, since */ + /* the text segment immediately follows the stack. */ + /* Hence we give an upper pound. */ + /* This is currently unused, since we disabled HEURISTIC2 */ + extern int __start[]; +# define HEURISTIC2_LIMIT ((ptr_t)((word)(__start) & ~(getpagesize()-1))) +# ifndef GC_OSF1_THREADS + /* Unresolved signal issues with threads. */ +# define MPROTECT_VDB +# endif +# define DYNAMIC_LOADING +# endif +# ifdef LINUX +# define OS_TYPE "LINUX" +# define LINUX_STACKBOTTOM +# ifdef __ELF__ +# define SEARCH_FOR_DATA_START +# define DYNAMIC_LOADING +# else +# define DATASTART ((ptr_t)0x140000000) +# endif + extern int _end[]; +# define DATAEND ((ptr_t)(_end)) +# define MPROTECT_VDB + /* Has only been superficially tested. May not */ + /* work on all versions. */ +# endif +# endif + +# ifdef IA64 +# define MACH_TYPE "IA64" +# ifdef HPUX +# ifdef _ILP32 +# define CPP_WORDSZ 32 + /* Requires 8 byte alignment for malloc */ +# define ALIGNMENT 4 +# else +# ifndef _LP64 +# error --> unknown ABI +# endif +# define CPP_WORDSZ 64 + /* Requires 16 byte alignment for malloc */ +# define ALIGNMENT 8 +# endif +# define OS_TYPE "HPUX" + extern int __data_start[]; +# define DATASTART ((ptr_t)(__data_start)) + /* Gustavo Rodriguez-Rivera suggested changing HEURISTIC2 */ + /* to this. Note that the GC must be initialized before the */ + /* first putenv call. */ + extern char ** environ; +# define STACKBOTTOM ((ptr_t)environ) +# define HPUX_STACKBOTTOM +# define DYNAMIC_LOADING + EXTERN_C_END +# include + EXTERN_C_BEGIN +# define GETPAGESIZE() (unsigned)sysconf(_SC_PAGE_SIZE) + /* The following was empirically determined, and is probably */ + /* not very robust. */ + /* Note that the backing store base seems to be at a nice */ + /* address minus one page. */ +# define BACKING_STORE_DISPLACEMENT 0x1000000 +# define BACKING_STORE_ALIGNMENT 0x1000 + extern ptr_t GC_register_stackbottom; +# define BACKING_STORE_BASE GC_register_stackbottom + /* Known to be wrong for recent HP/UX versions!!! */ +# endif +# ifdef LINUX +# define CPP_WORDSZ 64 +# define ALIGNMENT 8 +# define OS_TYPE "LINUX" + /* The following works on NUE and older kernels: */ +/* # define STACKBOTTOM ((ptr_t) 0xa000000000000000l) */ + /* This does not work on NUE: */ +# define LINUX_STACKBOTTOM + /* We also need the base address of the register stack */ + /* backing store. */ + extern ptr_t GC_register_stackbottom; +# define BACKING_STORE_BASE GC_register_stackbottom +# define SEARCH_FOR_DATA_START +# ifdef __GNUC__ +# define DYNAMIC_LOADING +# else + /* In the Intel compiler environment, we seem to end up with */ + /* statically linked executables and an undefined reference */ + /* to _DYNAMIC */ +# endif +# define MPROTECT_VDB + /* Requires Linux 2.3.47 or later. */ + extern int _end[]; +# define DATAEND ((ptr_t)(_end)) +# ifdef __GNUC__ +# ifndef __INTEL_COMPILER +# define PREFETCH(x) \ + __asm__ (" lfetch [%0]": : "r"(x)) +# define GC_PREFETCH_FOR_WRITE(x) \ + __asm__ (" lfetch.excl [%0]": : "r"(x)) +# define CLEAR_DOUBLE(x) \ + __asm__ (" stf.spill [%0]=f0": : "r"((void *)(x))) +# else + EXTERN_C_END +# include + EXTERN_C_BEGIN +# define PREFETCH(x) __lfetch(__lfhint_none, (x)) +# define GC_PREFETCH_FOR_WRITE(x) __lfetch(__lfhint_nta, (x)) +# define CLEAR_DOUBLE(x) __stf_spill((void *)(x), 0) +# endif /* __INTEL_COMPILER */ +# endif +# endif +# ifdef CYGWIN32 +# define OS_TYPE "CYGWIN32" +# define DATASTART ((ptr_t)GC_DATASTART) /* From gc.h */ +# define DATAEND ((ptr_t)GC_DATAEND) +# undef STACK_GRAN +# define STACK_GRAN 0x10000 +# ifdef USE_MMAP +# define NEED_FIND_LIMIT +# define USE_MMAP_ANON +# endif +# endif +# ifdef MSWIN32 + /* FIXME: This is a very partial guess. There is no port, yet. */ +# define OS_TYPE "MSWIN32" + /* STACKBOTTOM and DATASTART are handled specially in */ + /* os_dep.c. */ +# define DATAEND /* not needed */ +# if defined(_WIN64) +# define CPP_WORDSZ 64 +# else +# define CPP_WORDSZ 32 /* Is this possible? */ +# endif +# define ALIGNMENT 8 +# endif +# endif + +# ifdef M88K +# define MACH_TYPE "M88K" +# define ALIGNMENT 4 + extern int etext[]; +# ifdef CX_UX +# define OS_TYPE "CX_UX" +# define DATASTART ((ptr_t)((((word)(etext) + 0x3fffff) & ~0x3fffff) \ + + 0x10000)) +# endif +# ifdef DGUX +# define OS_TYPE "DGUX" + ptr_t GC_SysVGetDataStart(size_t, ptr_t); +# define DATASTART GC_SysVGetDataStart(0x10000, (ptr_t)etext) +# define DATASTART_IS_FUNC +# endif +# define STACKBOTTOM ((char*)0xf0000000) /* determined empirically */ +# endif + +# ifdef S370 + /* If this still works, and if anyone cares, this should probably */ + /* be moved to the S390 category. */ +# define MACH_TYPE "S370" +# define ALIGNMENT 4 /* Required by hardware */ +# ifdef UTS4 +# define OS_TYPE "UTS4" + extern int etext[]; + extern int _etext[]; + extern int _end[]; + ptr_t GC_SysVGetDataStart(size_t, ptr_t); +# define DATASTART GC_SysVGetDataStart(0x10000, (ptr_t)_etext) +# define DATASTART_IS_FUNC +# define DATAEND ((ptr_t)(_end)) +# define HEURISTIC2 +# endif +# endif + +# ifdef S390 +# define MACH_TYPE "S390" +# ifndef __s390x__ +# define ALIGNMENT 4 +# define CPP_WORDSZ 32 +# else +# define ALIGNMENT 8 +# define CPP_WORDSZ 64 +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# endif +# ifdef LINUX +# define OS_TYPE "LINUX" +# define LINUX_STACKBOTTOM +# define DYNAMIC_LOADING + extern int __data_start[] __attribute__((__weak__)); +# define DATASTART ((ptr_t)(__data_start)) + extern int _end[] __attribute__((__weak__)); +# define DATAEND ((ptr_t)(_end)) +# define CACHE_LINE_SIZE 256 +# define GETPAGESIZE() 4096 +# endif +# endif + +# ifdef AARCH64 +# define MACH_TYPE "AARCH64" +# ifdef __ILP32__ +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 +# else +# define CPP_WORDSZ 64 +# define ALIGNMENT 8 +# endif +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# ifdef LINUX +# define OS_TYPE "LINUX" +# define LINUX_STACKBOTTOM +# define DYNAMIC_LOADING + extern int __data_start[]; + extern int _end[]; +# define DATASTART ((ptr_t)__data_start) +# define DATAEND ((ptr_t)(&_end)) +# endif +# ifdef DARWIN + /* iOS */ +# define OS_TYPE "DARWIN" +# define DARWIN_DONT_PARSE_STACK +# ifndef GC_DONT_REGISTER_MAIN_STATIC_DATA +# define DYNAMIC_LOADING +# endif +# define DATASTART ((ptr_t)get_etext()) +# define DATAEND ((ptr_t)get_end()) +# define STACKBOTTOM ((ptr_t)0x16fdfffff) +# define USE_MMAP_ANON +# define MPROTECT_VDB + EXTERN_C_END +# include + EXTERN_C_BEGIN +# define GETPAGESIZE() (unsigned)getpagesize() + /* FIXME: There seems to be some issues with trylock hanging on */ + /* darwin. This should be looked into some more. */ +# define NO_PTHREAD_TRYLOCK +# if TARGET_OS_IPHONE && !defined(NO_DYLD_BIND_FULLY_IMAGE) +# define NO_DYLD_BIND_FULLY_IMAGE +# endif +# endif +# ifdef FREEBSD +# define OS_TYPE "FREEBSD" +# ifndef GC_FREEBSD_THREADS +# define MPROTECT_VDB +# endif +# define FREEBSD_STACKBOTTOM +# ifdef __ELF__ +# define DYNAMIC_LOADING +# endif + extern char etext[]; +# define DATASTART GC_FreeBSDGetDataStart(0x1000, (ptr_t)etext) +# define DATASTART_USES_BSDGETDATASTART +# endif +# ifdef NINTENDO_SWITCH + static int zero_fd = -1; +# define OPT_MAP_ANON 0 + extern int __bss_end[]; +# define NO_HANDLE_FORK +# define GETPAGESIZE() 4096 +# define DATASTART (ptr_t)ALIGNMENT /* cannot be null */ +# define DATAEND (ptr_t)(&__bss_end) + void *switch_get_stack_bottom(void); +# define STACKBOTTOM ((ptr_t)switch_get_stack_bottom()) +# undef USE_MMAP +# undef USE_MUNMAP +# endif +# ifdef NOSYS + /* __data_start is usually defined in the target linker script. */ + extern int __data_start[]; +# define DATASTART ((ptr_t)__data_start) + extern void *__stack_base__; +# define STACKBOTTOM ((ptr_t)__stack_base__) +# endif +# endif + +# ifdef ARM32 +# if defined(NACL) +# define MACH_TYPE "NACL" +# else +# define MACH_TYPE "ARM32" +# endif +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 +# ifdef NETBSD +# define OS_TYPE "NETBSD" +# define HEURISTIC2 +# ifdef __ELF__ + extern ptr_t GC_data_start; +# define DATASTART GC_data_start +# define DYNAMIC_LOADING +# else + extern char etext[]; +# define DATASTART ((ptr_t)(etext)) +# endif +# endif +# ifdef LINUX +# define OS_TYPE "LINUX" +# define LINUX_STACKBOTTOM +# undef STACK_GRAN +# define STACK_GRAN 0x10000000 +# ifdef __ELF__ +# define DYNAMIC_LOADING + EXTERN_C_END +# include + EXTERN_C_BEGIN +# if defined(__GLIBC__) && __GLIBC__ >= 2 \ + || defined(HOST_ANDROID) || defined(HOST_TIZEN) +# define SEARCH_FOR_DATA_START +# else + extern char **__environ; +# define DATASTART ((ptr_t)(&__environ)) + /* hideous kludge: __environ is the first */ + /* word in crt0.o, and delimits the start */ + /* of the data segment, no matter which */ + /* ld options were passed through. */ + /* We could use _etext instead, but that */ + /* would include .rodata, which may */ + /* contain large read-only data tables */ + /* that we'd rather not scan. */ +# endif + extern int _end[]; +# define DATAEND ((ptr_t)(_end)) +# else + extern int etext[]; +# define DATASTART ((ptr_t)((((word)(etext)) + 0xfff) & ~0xfff)) +# endif +# endif +# ifdef MSWINCE +# define OS_TYPE "MSWINCE" +# define DATAEND /* not needed */ +# endif +# ifdef FREEBSD + /* FreeBSD/arm */ +# define OS_TYPE "FREEBSD" +# ifndef GC_FREEBSD_THREADS +# define MPROTECT_VDB +# endif +# define SIG_SUSPEND SIGUSR1 +# define SIG_THR_RESTART SIGUSR2 +# define FREEBSD_STACKBOTTOM +# ifdef __ELF__ +# define DYNAMIC_LOADING +# endif + extern char etext[]; +# define DATASTART GC_FreeBSDGetDataStart(0x1000, (ptr_t)etext) +# define DATASTART_USES_BSDGETDATASTART +# endif +# ifdef DARWIN + /* iOS */ +# define OS_TYPE "DARWIN" +# define DARWIN_DONT_PARSE_STACK +# ifndef GC_DONT_REGISTER_MAIN_STATIC_DATA +# define DYNAMIC_LOADING +# endif +# define DATASTART ((ptr_t)get_etext()) +# define DATAEND ((ptr_t)get_end()) +# define STACKBOTTOM ((ptr_t)0x30000000) +# define USE_MMAP_ANON +# define MPROTECT_VDB + EXTERN_C_END +# include + EXTERN_C_BEGIN +# define GETPAGESIZE() (unsigned)getpagesize() + /* FIXME: There seems to be some issues with trylock hanging on */ + /* darwin. This should be looked into some more. */ +# define NO_PTHREAD_TRYLOCK +# if TARGET_OS_IPHONE && !defined(NO_DYLD_BIND_FULLY_IMAGE) +# define NO_DYLD_BIND_FULLY_IMAGE +# endif +# endif +# ifdef OPENBSD +# define OS_TYPE "OPENBSD" +# ifndef GC_OPENBSD_THREADS + EXTERN_C_END +# include +# include + EXTERN_C_BEGIN +# ifdef USRSTACK +# define STACKBOTTOM ((ptr_t)USRSTACK) +# else +# define HEURISTIC2 +# endif +# endif + extern int __data_start[]; +# define DATASTART ((ptr_t)__data_start) + extern int _end[]; +# define DATAEND ((ptr_t)(&_end)) +# define DYNAMIC_LOADING +# endif +# ifdef SN_TARGET_PSP2 +# define NO_HANDLE_FORK +# define DATASTART (ptr_t)ALIGNMENT +# define DATAEND (ptr_t)ALIGNMENT + void *psp2_get_stack_bottom(void); +# define STACKBOTTOM ((ptr_t)psp2_get_stack_bottom()) +# endif +# ifdef NN_PLATFORM_CTR + extern unsigned char Image$$ZI$$ZI$$Base[]; +# define DATASTART (ptr_t)(Image$$ZI$$ZI$$Base) + extern unsigned char Image$$ZI$$ZI$$Limit[]; +# define DATAEND (ptr_t)(Image$$ZI$$ZI$$Limit) + void *n3ds_get_stack_bottom(void); +# define STACKBOTTOM ((ptr_t)n3ds_get_stack_bottom()) +# endif +# ifdef NOSYS + /* __data_start is usually defined in the target linker script. */ + extern int __data_start[]; +# define DATASTART ((ptr_t)(__data_start)) + /* __stack_base__ is set in newlib/libc/sys/arm/crt0.S */ + extern void *__stack_base__; +# define STACKBOTTOM ((ptr_t)(__stack_base__)) +# endif +#endif + +# ifdef CRIS +# define MACH_TYPE "CRIS" +# define CPP_WORDSZ 32 +# define ALIGNMENT 1 +# define OS_TYPE "LINUX" +# define DYNAMIC_LOADING +# define LINUX_STACKBOTTOM +# define SEARCH_FOR_DATA_START + extern int _end[]; +# define DATAEND ((ptr_t)(_end)) +# endif + +# if defined(SH) && !defined(SH4) +# define MACH_TYPE "SH" +# define ALIGNMENT 4 +# ifdef MSWINCE +# define OS_TYPE "MSWINCE" +# define DATAEND /* not needed */ +# endif +# ifdef LINUX +# define OS_TYPE "LINUX" +# define LINUX_STACKBOTTOM +# define DYNAMIC_LOADING +# define SEARCH_FOR_DATA_START + extern int _end[]; +# define DATAEND ((ptr_t)(_end)) +# endif +# ifdef NETBSD +# define OS_TYPE "NETBSD" +# define HEURISTIC2 + extern ptr_t GC_data_start; +# define DATASTART GC_data_start +# define DYNAMIC_LOADING +# endif +# ifdef OPENBSD +# define OS_TYPE "OPENBSD" +# ifndef GC_OPENBSD_THREADS + EXTERN_C_END +# include +# include + EXTERN_C_BEGIN +# ifdef USRSTACK +# define STACKBOTTOM ((ptr_t)USRSTACK) +# else +# define HEURISTIC2 +# endif +# endif + extern int __data_start[]; +# define DATASTART ((ptr_t)__data_start) + extern int _end[]; +# define DATAEND ((ptr_t)(&_end)) +# define DYNAMIC_LOADING +# endif +# endif + +# ifdef SH4 +# define MACH_TYPE "SH4" +# define OS_TYPE "MSWINCE" +# define ALIGNMENT 4 +# define DATAEND /* not needed */ +# endif + +# ifdef AVR32 +# define MACH_TYPE "AVR32" +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 +# define OS_TYPE "LINUX" +# define DYNAMIC_LOADING +# define LINUX_STACKBOTTOM +# define SEARCH_FOR_DATA_START + extern int _end[]; +# define DATAEND ((ptr_t)(_end)) +# endif + +# ifdef M32R +# define CPP_WORDSZ 32 +# define MACH_TYPE "M32R" +# define ALIGNMENT 4 +# ifdef LINUX +# define OS_TYPE "LINUX" +# define LINUX_STACKBOTTOM +# undef STACK_GRAN +# define STACK_GRAN 0x10000000 +# define DYNAMIC_LOADING +# define SEARCH_FOR_DATA_START + extern int _end[]; +# define DATAEND ((ptr_t)(_end)) +# endif +# endif + +# ifdef X86_64 +# define MACH_TYPE "X86_64" +# ifdef __ILP32__ +# define ALIGNMENT 4 +# define CPP_WORDSZ 32 +# else +# define ALIGNMENT 8 +# define CPP_WORDSZ 64 +# endif +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# ifndef CACHE_LINE_SIZE +# define CACHE_LINE_SIZE 64 +# endif +# ifdef SN_TARGET_ORBIS +# define DATASTART (ptr_t)ALIGNMENT +# define DATAEND (ptr_t)ALIGNMENT + EXTERN_C_END +# include + EXTERN_C_BEGIN + void *ps4_get_stack_bottom(void); +# define STACKBOTTOM ((ptr_t)ps4_get_stack_bottom()) +# endif +# ifdef OPENBSD +# define OS_TYPE "OPENBSD" +# define ELF_CLASS ELFCLASS64 +# ifndef GC_OPENBSD_THREADS + EXTERN_C_END +# include +# include + EXTERN_C_BEGIN +# ifdef USRSTACK +# define STACKBOTTOM ((ptr_t)USRSTACK) +# else +# define HEURISTIC2 +# endif +# endif + extern int __data_start[]; + extern int _end[]; +# define DATASTART ((ptr_t)__data_start) +# define DATAEND ((ptr_t)(&_end)) +# define DYNAMIC_LOADING +# endif +# ifdef LINUX +# define OS_TYPE "LINUX" +# define LINUX_STACKBOTTOM +# if !defined(GC_LINUX_THREADS) || !defined(REDIRECT_MALLOC) +# define MPROTECT_VDB +# else + /* We seem to get random errors in incremental mode, */ + /* possibly because Linux threads is itself a malloc client */ + /* and can't deal with the signals. */ +# endif +# ifdef __ELF__ +# define DYNAMIC_LOADING + EXTERN_C_END +# include + EXTERN_C_BEGIN +# define SEARCH_FOR_DATA_START + extern int _end[]; +# define DATAEND ((ptr_t)(_end)) +# else + extern int etext[]; +# define DATASTART ((ptr_t)((((word)(etext)) + 0xfff) & ~0xfff)) +# endif +# if defined(__GLIBC__) && !defined(__UCLIBC__) + /* At present, there's a bug in GLibc getcontext() on */ + /* Linux/x64 (it clears FPU exception mask). We define this */ + /* macro to workaround it. */ + /* FIXME: This seems to be fixed in GLibc v2.14. */ +# define GETCONTEXT_FPU_EXCMASK_BUG +# endif +# if defined(__GLIBC__) && !defined(__UCLIBC__) + /* Workaround lock elision implementation for some glibc. */ +# define GLIBC_2_19_TSX_BUG + EXTERN_C_END +# include /* for gnu_get_libc_version() */ + EXTERN_C_BEGIN +# endif +# endif +# ifdef DARWIN +# define OS_TYPE "DARWIN" +# define DARWIN_DONT_PARSE_STACK +# ifndef GC_DONT_REGISTER_MAIN_STATIC_DATA +# define DYNAMIC_LOADING +# endif + /* XXX: see get_end(3), get_etext() and get_end() should not be used. */ + /* These aren't used when dyld support is enabled (it is by default) */ +# define DATASTART ((ptr_t)get_etext()) +# define DATAEND ((ptr_t)get_end()) +# define STACKBOTTOM ((ptr_t)0x7fff5fc00000) +# define USE_MMAP_ANON +# define MPROTECT_VDB + EXTERN_C_END +# include + EXTERN_C_BEGIN +# define GETPAGESIZE() (unsigned)getpagesize() + /* There seems to be some issues with trylock hanging on darwin. */ + /* This should be looked into some more. */ +# define NO_PTHREAD_TRYLOCK +# if TARGET_OS_IPHONE && !defined(NO_DYLD_BIND_FULLY_IMAGE) + /* iPhone/iPad simulator */ +# define NO_DYLD_BIND_FULLY_IMAGE +# endif +# endif +# ifdef FREEBSD +# define OS_TYPE "FREEBSD" +# ifndef GC_FREEBSD_THREADS +# define MPROTECT_VDB +# endif +# ifdef __GLIBC__ +# define SIG_SUSPEND (32+6) +# define SIG_THR_RESTART (32+5) + extern int _end[]; +# define DATAEND ((ptr_t)(_end)) +# else +# define SIG_SUSPEND SIGUSR1 +# define SIG_THR_RESTART SIGUSR2 + /* SIGTSTP and SIGCONT could be used alternatively. */ +# endif +# define FREEBSD_STACKBOTTOM +# ifdef __ELF__ +# define DYNAMIC_LOADING +# endif + extern char etext[]; +# define DATASTART GC_FreeBSDGetDataStart(0x1000, (ptr_t)etext) +# define DATASTART_USES_BSDGETDATASTART +# endif +# ifdef NETBSD +# define OS_TYPE "NETBSD" +# define HEURISTIC2 +# ifdef __ELF__ + extern ptr_t GC_data_start; +# define DATASTART GC_data_start +# define DYNAMIC_LOADING +# else +# define SEARCH_FOR_DATA_START +# endif +# endif +# ifdef HAIKU +# define OS_TYPE "HAIKU" + EXTERN_C_END +# include + EXTERN_C_BEGIN +# define GETPAGESIZE() (unsigned)B_PAGE_SIZE +# define HEURISTIC2 +# define SEARCH_FOR_DATA_START +# define DYNAMIC_LOADING +# define MPROTECT_VDB +# endif +# ifdef SOLARIS +# define OS_TYPE "SOLARIS" +# define ELF_CLASS ELFCLASS64 + extern int _etext[], _end[]; + ptr_t GC_SysVGetDataStart(size_t, ptr_t); +# define DATASTART GC_SysVGetDataStart(0x1000, (ptr_t)_etext) +# define DATASTART_IS_FUNC +# define DATAEND ((ptr_t)(_end)) + /* # define STACKBOTTOM ((ptr_t)(_start)) worked through 2.7, */ + /* but reportedly breaks under 2.8. It appears that the stack */ + /* base is a property of the executable, so this should not */ + /* break old executables. */ + /* HEURISTIC2 probably works, but this appears to be preferable.*/ + /* Apparently USRSTACK is defined to be USERLIMIT, but in some */ + /* installations that's undefined. We work around this with a */ + /* gross hack: */ + EXTERN_C_END +# include + EXTERN_C_BEGIN +# ifdef USERLIMIT + /* This should work everywhere, but doesn't. */ +# define STACKBOTTOM ((ptr_t)USRSTACK) +# else +# define HEURISTIC2 +# endif +/* At least in Solaris 2.5, PROC_VDB gives wrong values for dirty bits. */ +/* It appears to be fixed in 2.8 and 2.9. */ +# ifdef SOLARIS25_PROC_VDB_BUG_FIXED +# define PROC_VDB +# endif +# ifndef GC_THREADS +# define MPROTECT_VDB +# endif +# define DYNAMIC_LOADING +# if !defined(USE_MMAP) && defined(REDIRECT_MALLOC) +# define USE_MMAP + /* Otherwise we now use calloc. Mmap may result in the */ + /* heap interleaved with thread stacks, which can result in */ + /* excessive blacklisting. Sbrk is unusable since it */ + /* doesn't interact correctly with the system malloc. */ +# endif +# ifdef USE_MMAP +# define HEAP_START (ptr_t)0x40000000 +# else +# define HEAP_START DATAEND +# endif +# endif +# ifdef MSWIN_XBOX1 +# define NO_GETENV +# define DATASTART (ptr_t)ALIGNMENT +# define DATAEND (ptr_t)ALIGNMENT + LONG64 durango_get_stack_bottom(void); +# define STACKBOTTOM ((ptr_t)durango_get_stack_bottom()) +# define GETPAGESIZE() 4096 +# ifndef USE_MMAP +# define USE_MMAP +# endif + /* The following is from sys/mman.h: */ +# define PROT_NONE 0 +# define PROT_READ 1 +# define PROT_WRITE 2 +# define PROT_EXEC 4 +# define MAP_PRIVATE 2 +# define MAP_FIXED 0x10 +# define MAP_FAILED ((void *)-1) +# endif +# ifdef MSWIN32 +# define OS_TYPE "MSWIN32" + /* STACKBOTTOM and DATASTART are handled specially in */ + /* os_dep.c. */ +# if !defined(__GNUC__) || defined(__INTEL_COMPILER) \ + || GC_GNUC_PREREQ(4, 7) + /* Older GCC has not supported SetUnhandledExceptionFilter */ + /* properly on x64 (e.g. SEH unwinding information missed). */ +# define MPROTECT_VDB +# endif +# define GWW_VDB +# ifndef DATAEND +# define DATAEND /* not needed */ +# endif +# endif +# endif /* X86_64 */ + +# ifdef HEXAGON +# define CPP_WORDSZ 32 +# define MACH_TYPE "HEXAGON" +# define ALIGNMENT 4 +# ifdef LINUX +# define OS_TYPE "LINUX" +# define LINUX_STACKBOTTOM +# define MPROTECT_VDB +# ifdef __ELF__ +# define DYNAMIC_LOADING + EXTERN_C_END +# include + EXTERN_C_BEGIN +# if defined(__GLIBC__) && __GLIBC__ >= 2 +# define SEARCH_FOR_DATA_START +# else +# error --> unknown Hexagon libc configuration +# endif + extern int _end[]; +# define DATAEND ((ptr_t)(_end)) +# elif !defined(CPPCHECK) +# error --> bad Hexagon Linux configuration +# endif +# else +# error --> unknown Hexagon OS configuration +# endif +# endif + +# ifdef TILEPRO +# define CPP_WORDSZ 32 +# define MACH_TYPE "TILEPro" +# define ALIGNMENT 4 +# define PREFETCH(x) __insn_prefetch(x) +# define CACHE_LINE_SIZE 64 +# ifdef LINUX +# define OS_TYPE "LINUX" + extern int __data_start[]; +# define DATASTART ((ptr_t)__data_start) +# define LINUX_STACKBOTTOM +# define DYNAMIC_LOADING +# endif +# endif + +# ifdef TILEGX +# define CPP_WORDSZ (__SIZEOF_POINTER__ * 8) +# define MACH_TYPE "TILE-Gx" +# define ALIGNMENT __SIZEOF_POINTER__ +# if CPP_WORDSZ < 64 +# define CLEAR_DOUBLE(x) (*(long long *)(x) = 0) +# endif +# define PREFETCH(x) __insn_prefetch_l1(x) +# define CACHE_LINE_SIZE 64 +# ifdef LINUX +# define OS_TYPE "LINUX" + extern int __data_start[]; +# define DATASTART ((ptr_t)__data_start) +# define LINUX_STACKBOTTOM +# define DYNAMIC_LOADING +# endif +# endif + +# ifdef RISCV +# define MACH_TYPE "RISC-V" +# define CPP_WORDSZ __riscv_xlen /* 32 or 64 */ +# define ALIGNMENT (CPP_WORDSZ/8) +# ifdef LINUX +# define OS_TYPE "LINUX" + extern int __data_start[]; +# define DATASTART ((ptr_t)__data_start) +# define LINUX_STACKBOTTOM +# define DYNAMIC_LOADING +# endif +# endif /* RISCV */ + +#if defined(__GLIBC__) && !defined(DONT_USE_LIBC_PRIVATES) + /* Use glibc's stack-end marker. */ +# define USE_LIBC_PRIVATES +#endif + +#if defined(LINUX_STACKBOTTOM) && defined(NO_PROC_STAT) \ + && !defined(USE_LIBC_PRIVATES) + /* This combination will fail, since we have no way to get */ + /* the stack base. Use HEURISTIC2 instead. */ +# undef LINUX_STACKBOTTOM +# define HEURISTIC2 + /* This may still fail on some architectures like IA64. */ + /* We tried ... */ +#endif + +#if defined(USE_MMAP_ANON) && !defined(USE_MMAP) +# define USE_MMAP +#elif defined(LINUX) && defined(USE_MMAP) + /* The kernel may do a somewhat better job merging mappings etc. */ + /* with anonymous mappings. */ +# define USE_MMAP_ANON +#endif + +#if defined(GC_LINUX_THREADS) && defined(REDIRECT_MALLOC) \ + && !defined(USE_PROC_FOR_LIBRARIES) + /* Nptl allocates thread stacks with mmap, which is fine. But it */ + /* keeps a cache of thread stacks. Thread stacks contain the */ + /* thread control blocks. These in turn contain a pointer to */ + /* (sizeof (void *) from the beginning of) the dtv for thread-local */ + /* storage, which is calloc allocated. If we don't scan the cached */ + /* thread stacks, we appear to lose the dtv. This tends to */ + /* result in something that looks like a bogus dtv count, which */ + /* tends to result in a memset call on a block that is way too */ + /* large. Sometimes we're lucky and the process just dies ... */ + /* There seems to be a similar issue with some other memory */ + /* allocated by the dynamic loader. */ + /* This should be avoidable by either: */ + /* - Defining USE_PROC_FOR_LIBRARIES here. */ + /* That performs very poorly, precisely because we end up */ + /* scanning cached stacks. */ + /* - Have calloc look at its callers. */ + /* In spite of the fact that it is gross and disgusting. */ + /* In fact neither seems to suffice, probably in part because */ + /* even with USE_PROC_FOR_LIBRARIES, we don't scan parts of stack */ + /* segments that appear to be out of bounds. Thus we actually */ + /* do both, which seems to yield the best results. */ +# define USE_PROC_FOR_LIBRARIES +#endif + +#ifndef STACK_GROWS_UP +# define STACK_GROWS_DOWN +#endif + +#ifndef CPP_WORDSZ +# define CPP_WORDSZ 32 +#endif + +#ifndef OS_TYPE +# define OS_TYPE "" +#endif + +#ifndef DATAEND + extern int end[]; +# define DATAEND ((ptr_t)(end)) +#endif + +/* Workaround for Android NDK clang 3.5+ (as of NDK r10e) which does */ +/* not provide correct _end symbol. Unfortunately, alternate __end__ */ +/* symbol is provided only by NDK "bfd" linker. */ +#if defined(HOST_ANDROID) && defined(__clang__) +# undef DATAEND +# pragma weak __end__ + extern int __end__[]; +# define DATAEND (__end__ != 0 ? (ptr_t)__end__ : (ptr_t)_end) +#endif + +#if (defined(SVR4) || defined(HOST_ANDROID) || defined(HOST_TIZEN)) \ + && !defined(GETPAGESIZE) + EXTERN_C_END +# include + EXTERN_C_BEGIN +# define GETPAGESIZE() (unsigned)sysconf(_SC_PAGESIZE) +#endif + +#ifndef GETPAGESIZE +# if defined(SOLARIS) || defined(IRIX5) || defined(LINUX) \ + || defined(NETBSD) || defined(FREEBSD) || defined(HPUX) + EXTERN_C_END +# include + EXTERN_C_BEGIN +# endif +# define GETPAGESIZE() (unsigned)getpagesize() +#endif + +#if defined(HOST_ANDROID) && !(__ANDROID_API__ >= 23) \ + && ((defined(MIPS) && (CPP_WORDSZ == 32)) \ + || defined(ARM32) || defined(I386) /* but not x32 */) + /* tkill() exists only on arm32/mips(32)/x86. */ + /* NDK r11+ deprecates tkill() but keeps it for Mono clients. */ +/* Unity: Android NDK r13 removed this for good. We should not need it. */ +/*# define USE_TKILL_ON_ANDROID*/ +#endif + +#if defined(SOLARIS) || defined(DRSNX) || defined(UTS4) + /* OS has SVR4 generic features. */ + /* Probably others also qualify. */ +# define SVR4 +#endif + +#if defined(SOLARIS) || defined(DRSNX) + /* OS has SOLARIS style semi-undocumented interface */ + /* to dynamic loader. */ +# define SOLARISDL + /* OS has SOLARIS style signal handlers. */ +# define SUNOS5SIGS +#endif + +#if defined(HPUX) +# define SUNOS5SIGS +#endif + +#if defined(FREEBSD) && (defined(__DragonFly__) || __FreeBSD__ >= 4 \ + || (__FreeBSD_kernel__ >= 4)) +# define SUNOS5SIGS +#endif + +#if !defined(GC_EXPLICIT_SIGNALS_UNBLOCK) && defined(SUNOS5SIGS) \ + && !defined(GC_NO_PTHREAD_SIGMASK) +# define GC_EXPLICIT_SIGNALS_UNBLOCK +#endif + +#if !defined(NO_SIGNALS_UNBLOCK_IN_MAIN) && defined(GC_NO_PTHREAD_SIGMASK) +# define NO_SIGNALS_UNBLOCK_IN_MAIN +#endif + +#if !defined(NO_MARKER_SPECIAL_SIGMASK) \ + && (defined(NACL) || defined(GC_WIN32_PTHREADS)) + /* Either there is no pthread_sigmask(), or GC marker thread cannot */ + /* steal and drop user signal calls. */ +# define NO_MARKER_SPECIAL_SIGMASK +#endif + +#ifdef GC_NETBSD_THREADS +# define SIGRTMIN 33 +# define SIGRTMAX 63 + /* It seems to be necessary to wait until threads have restarted. */ + /* But it is unclear why that is the case. */ +# define GC_NETBSD_THREADS_WORKAROUND +#endif + +#ifdef GC_OPENBSD_THREADS + EXTERN_C_END +# include + EXTERN_C_BEGIN + /* Prior to 5.2 release, OpenBSD had user threads and required */ + /* special handling. */ +# if OpenBSD < 201211 +# define GC_OPENBSD_UTHREADS 1 +# endif +#endif /* GC_OPENBSD_THREADS */ + +#if defined(SVR4) || defined(LINUX) || defined(IRIX5) || defined(HPUX) \ + || defined(OPENBSD) || defined(NETBSD) || defined(FREEBSD) \ + || defined(DGUX) || defined(BSD) || defined(HAIKU) || defined(HURD) \ + || defined(AIX) || defined(DARWIN) || defined(OSF1) +# define UNIX_LIKE /* Basic Unix-like system calls work. */ +#endif + +#if defined(CPPCHECK) +# undef CPP_WORDSZ +# define CPP_WORDSZ (__SIZEOF_POINTER__ * 8) +#endif + +#if CPP_WORDSZ != 32 && CPP_WORDSZ != 64 +# error --> bad word size +#endif + +#if !defined(ALIGNMENT) && !defined(CPPCHECK) +# error --> undefined ALIGNMENT +#endif + +#ifdef PCR +# undef DYNAMIC_LOADING +# undef STACKBOTTOM +# undef HEURISTIC1 +# undef HEURISTIC2 +# undef PROC_VDB +# undef MPROTECT_VDB +# define PCR_VDB +#endif + +#if !defined(STACKBOTTOM) && (defined(ECOS) || defined(NOSYS)) \ + && !defined(CPPCHECK) +# error --> undefined STACKBOTTOM +#endif + +#ifdef IGNORE_DYNAMIC_LOADING +# undef DYNAMIC_LOADING +#endif + +#if defined(SMALL_CONFIG) && !defined(GC_DISABLE_INCREMENTAL) + /* Presumably not worth the space it takes. */ +# define GC_DISABLE_INCREMENTAL +#endif + +#if (defined(MSWIN32) || defined(MSWINCE)) && !defined(USE_WINALLOC) + /* USE_WINALLOC is only an option for Cygwin. */ +# define USE_WINALLOC +#endif + +#ifdef USE_WINALLOC +# undef USE_MMAP +#endif + +#if defined(LINUX) || defined(FREEBSD) || defined(SOLARIS) || defined(IRIX5) \ + || ((defined(USE_MMAP) || defined(USE_MUNMAP)) && !defined(USE_WINALLOC)) +# define MMAP_SUPPORTED +#endif + +/* Xbox One (DURANGO) may not need to be this aggressive, but the */ +/* default is likely too lax under heavy allocation pressure. */ +/* The platform does not have a virtual paging system, so it does not */ +/* have a large virtual address space that a standard x64 platform has. */ +#if defined(USE_MUNMAP) && !defined(MUNMAP_THRESHOLD) \ + && (defined(SN_TARGET_ORBIS) || defined(SN_TARGET_PS3) \ + || defined(SN_TARGET_PSP2) || defined(MSWIN_XBOX1)) +# define MUNMAP_THRESHOLD 2 +#endif + +#if defined(GC_DISABLE_INCREMENTAL) || defined(DEFAULT_VDB) \ + || defined(MANUAL_VDB) +# undef GWW_VDB +# undef MPROTECT_VDB +# undef PCR_VDB +# undef PROC_VDB +#endif + +#ifdef GC_DISABLE_INCREMENTAL +# undef CHECKSUMS +#endif + +#ifdef USE_GLOBAL_ALLOC + /* Cannot pass MEM_WRITE_WATCH to GlobalAlloc(). */ +# undef GWW_VDB +#endif + +#if defined(USE_MUNMAP) && defined(GWW_VDB) +# undef MPROTECT_VDB /* TODO: Cannot deal with address space holes. */ + /* Else if MPROTECT_VDB is available but not GWW_VDB then decide */ + /* whether to disable memory unmapping or mprotect-based virtual */ + /* dirty bits at runtime when GC_enable_incremental is called. */ +#endif + +/* PARALLEL_MARK does not cause undef MPROTECT_VDB any longer. */ + +#if defined(MPROTECT_VDB) && defined(GC_PREFER_MPROTECT_VDB) + /* Choose MPROTECT_VDB manually (if multiple strategies available). */ +# undef PCR_VDB +# undef PROC_VDB + /* #undef GWW_VDB - handled in os_dep.c */ +#endif + +#ifdef PROC_VDB + /* Multi-VDB mode is not implemented. */ +# undef MPROTECT_VDB +#endif + +#if defined(SIGBUS) && !defined(HAVE_SIGBUS) && !defined(CPPCHECK) +# define HAVE_SIGBUS +#endif + +#ifndef SA_SIGINFO +# define NO_SA_SIGACTION +#endif + +#if defined(NO_SA_SIGACTION) && defined(MPROTECT_VDB) && !defined(DARWIN) \ + && !defined(MSWIN32) && !defined(MSWINCE) +# undef MPROTECT_VDB +#endif + +#if !defined(PCR_VDB) && !defined(PROC_VDB) && !defined(MPROTECT_VDB) \ + && !defined(GWW_VDB) && !defined(DEFAULT_VDB) && !defined(MANUAL_VDB) \ + && !defined(GC_DISABLE_INCREMENTAL) +# define DEFAULT_VDB +#endif + +#if ((defined(UNIX_LIKE) && (defined(DARWIN) || defined(HAIKU) \ + || defined(HURD) || defined(OPENBSD) \ + || defined(ARM32) \ + || defined(AVR32) || defined(MIPS) \ + || defined(NIOS2) || defined(OR1K))) \ + || (defined(LINUX) && !defined(__gnu_linux__)) \ + || (defined(RTEMS) && defined(I386)) || defined(HOST_ANDROID)) \ + && !defined(NO_GETCONTEXT) +# define NO_GETCONTEXT +#endif + +#ifndef PREFETCH +# if GC_GNUC_PREREQ(3, 0) && !defined(NO_PREFETCH) +# define PREFETCH(x) __builtin_prefetch((x), 0, 0) +# else +# define PREFETCH(x) (void)0 +# endif +#endif + +#ifndef GC_PREFETCH_FOR_WRITE +# if GC_GNUC_PREREQ(3, 0) && !defined(GC_NO_PREFETCH_FOR_WRITE) +# define GC_PREFETCH_FOR_WRITE(x) __builtin_prefetch((x), 1) +# else +# define GC_PREFETCH_FOR_WRITE(x) (void)0 +# endif +#endif + +#ifndef CACHE_LINE_SIZE +# define CACHE_LINE_SIZE 32 /* Wild guess */ +#endif + +#ifndef STATIC +# ifndef NO_DEBUGGING +# define STATIC /* ignore to aid profiling and possibly debugging */ +# else +# define STATIC static +# endif +#endif + +#if defined(LINUX) && (defined(USE_PROC_FOR_LIBRARIES) || defined(IA64) \ + || !defined(SMALL_CONFIG)) +# define NEED_PROC_MAPS +#endif + +#if defined(LINUX) || defined(HURD) || defined(__GLIBC__) +# define REGISTER_LIBRARIES_EARLY + /* We sometimes use dl_iterate_phdr, which may acquire an internal */ + /* lock. This isn't safe after the world has stopped. So we must */ + /* call GC_register_dynamic_libraries before stopping the world. */ + /* For performance reasons, this may be beneficial on other */ + /* platforms as well, though it should be avoided in win32. */ +#endif /* LINUX */ + +#if defined(SEARCH_FOR_DATA_START) + extern ptr_t GC_data_start; +# define DATASTART GC_data_start +#endif + +#ifndef HEAP_START +# define HEAP_START ((ptr_t)0) +#endif + +#ifndef CLEAR_DOUBLE +# define CLEAR_DOUBLE(x) (((word*)(x))[0] = 0, ((word*)(x))[1] = 0) +#endif + +#if defined(GC_LINUX_THREADS) && defined(REDIRECT_MALLOC) \ + && !defined(INCLUDE_LINUX_THREAD_DESCR) + /* Will not work, since libc and the dynamic loader use thread */ + /* locals, sometimes as the only reference. */ +# define INCLUDE_LINUX_THREAD_DESCR +#endif + +#if !defined(CPPCHECK) +#if defined(GC_IRIX_THREADS) && !defined(IRIX5) +# error --> inconsistent configuration +#endif +#if defined(GC_LINUX_THREADS) && !defined(LINUX) && !defined(NACL) +# error --> inconsistent configuration +#endif +#if defined(GC_NETBSD_THREADS) && !defined(NETBSD) +# error --> inconsistent configuration +#endif +#if defined(GC_FREEBSD_THREADS) && !defined(FREEBSD) +# error --> inconsistent configuration +#endif +#if defined(GC_SOLARIS_THREADS) && !defined(SOLARIS) +# error --> inconsistent configuration +#endif +#if defined(GC_HPUX_THREADS) && !defined(HPUX) +# error --> inconsistent configuration +#endif +#if defined(GC_AIX_THREADS) && !defined(_AIX) +# error --> inconsistent configuration +#endif +#if defined(GC_WIN32_THREADS) && !defined(CYGWIN32) && !defined(MSWIN32) \ + && !defined(MSWINCE) && !defined(MSWIN_XBOX1) +# error --> inconsistent configuration +#endif +# if defined(GC_WIN32_PTHREADS) && defined(CYGWIN32) +# error --> inconsistent configuration +# endif +#endif /* !CPPCHECK */ + +#if defined(PCR) || defined(GC_WIN32_THREADS) || defined(GC_PTHREADS) \ + || defined(NN_PLATFORM_CTR) || defined(NINTENDO_SWITCH) \ + || defined(SN_TARGET_ORBIS) || defined(SN_TARGET_PS3) \ + || defined(SN_TARGET_PSP2) +# define THREADS +#endif + +#if defined(PARALLEL_MARK) && !defined(THREADS) && !defined(CPPCHECK) +# error "invalid config - PARALLEL_MARK requires GC_THREADS" +#endif + +#if (((defined(MSWIN32) || defined(MSWINCE)) && !defined(__GNUC__)) \ + || (defined(MSWIN32) && defined(I386)) /* for Win98 */ \ + || (defined(USE_PROC_FOR_LIBRARIES) && defined(THREADS))) \ + && !defined(NO_WRAP_MARK_SOME) + /* Under rare conditions, we may end up marking from nonexistent */ + /* memory. Hence we need to be prepared to recover by running */ + /* GC_mark_some with a suitable handler in place. */ + /* TODO: Probably replace __GNUC__ above with ndef GC_PTHREADS. */ + /* FIXME: Should we really need it for WinCE? If yes then */ + /* WRAP_MARK_SOME should be also defined for CeGCC which requires */ + /* CPU/OS-specific code in mark_ex_handler and GC_mark_some (for */ + /* manual stack unwinding and exception handler installation). */ +# define WRAP_MARK_SOME +#endif + +#if defined(PARALLEL_MARK) && !defined(DEFAULT_STACK_MAYBE_SMALL) \ + && (defined(HPUX) || defined(GC_DGUX386_THREADS) \ + || defined(NO_GETCONTEXT) /* e.g. musl */) + /* TODO: Test default stack size in configure. */ +# define DEFAULT_STACK_MAYBE_SMALL +#endif + +#ifdef PARALLEL_MARK + /* The minimum stack size for a marker thread. */ +# define MIN_STACK_SIZE (8 * HBLKSIZE * sizeof(word)) +#endif + +#if defined(HOST_ANDROID) && !defined(THREADS) \ + && !defined(USE_GET_STACKBASE_FOR_MAIN) + /* Always use pthread_attr_getstack on Android ("-lpthread" option is */ + /* not needed to be specified manually) since GC_linux_main_stack_base */ + /* causes app crash if invoked inside Dalvik VM. */ +# define USE_GET_STACKBASE_FOR_MAIN +#endif + +/* Outline pthread primitives to use in GC_get_[main_]stack_base. */ +#if ((defined(FREEBSD) && defined(__GLIBC__)) /* kFreeBSD */ \ + || defined(LINUX) || defined(NETBSD) || defined(HOST_ANDROID)) \ + && !defined(NO_PTHREAD_GETATTR_NP) +# define HAVE_PTHREAD_GETATTR_NP 1 +#elif defined(FREEBSD) && !defined(__GLIBC__) \ + && !defined(NO_PTHREAD_ATTR_GET_NP) +# define HAVE_PTHREAD_NP_H 1 /* requires include pthread_np.h */ +# define HAVE_PTHREAD_ATTR_GET_NP 1 +#endif + +#if defined(UNIX_LIKE) && defined(THREADS) && !defined(NO_CANCEL_SAFE) \ + && !defined(HOST_ANDROID) + /* Make the code cancellation-safe. This basically means that we */ + /* ensure that cancellation requests are ignored while we are in */ + /* the collector. This applies only to Posix deferred cancellation; */ + /* we don't handle Posix asynchronous cancellation. */ + /* Note that this only works if pthread_setcancelstate is */ + /* async-signal-safe, at least in the absence of asynchronous */ + /* cancellation. This appears to be true for the glibc version, */ + /* though it is not documented. Without that assumption, there */ + /* seems to be no way to safely wait in a signal handler, which */ + /* we need to do for thread suspension. */ + /* Also note that little other code appears to be cancellation-safe. */ + /* Hence it may make sense to turn this off for performance. */ +# define CANCEL_SAFE +#endif + +#ifdef CANCEL_SAFE +# define IF_CANCEL(x) x +#else +# define IF_CANCEL(x) /* empty */ +#endif + +#if !defined(CAN_HANDLE_FORK) && !defined(NO_HANDLE_FORK) \ + && !defined(HAVE_NO_FORK) \ + && ((defined(GC_PTHREADS) && !defined(NACL) \ + && !defined(GC_WIN32_PTHREADS) && !defined(USE_WINALLOC)) \ + || (defined(DARWIN) && defined(MPROTECT_VDB)) || defined(HANDLE_FORK)) + /* Attempts (where supported and requested) to make GC_malloc work in */ + /* a child process fork'ed from a multi-threaded parent. */ +# define CAN_HANDLE_FORK +#endif + +#if defined(CAN_HANDLE_FORK) && !defined(CAN_CALL_ATFORK) \ + && !defined(HURD) && !defined(SN_TARGET_ORBIS) && !defined(HOST_TIZEN) \ + && (!defined(HOST_ANDROID) || __ANDROID_API__ >= 21) + /* Have working pthread_atfork(). */ +# define CAN_CALL_ATFORK +#endif + +#if !defined(CAN_HANDLE_FORK) && !defined(HAVE_NO_FORK) \ + && (defined(MSWIN32) || defined(MSWINCE) || defined(DOS4GW) \ + || defined(OS2) || defined(SYMBIAN) /* and probably others ... */) +# define HAVE_NO_FORK +#endif + +#if !defined(USE_MARK_BITS) && !defined(USE_MARK_BYTES) \ + && defined(PARALLEL_MARK) + /* Minimize compare-and-swap usage. */ +# define USE_MARK_BYTES +#endif + +#if (defined(MSWINCE) && !defined(__CEGCC__) || defined(MSWINRT_FLAVOR)) \ + && !defined(NO_GETENV) +# define NO_GETENV +#endif + +#if (defined(NO_GETENV) || defined(MSWINCE)) && !defined(NO_GETENV_WIN32) +# define NO_GETENV_WIN32 +#endif + +#ifndef STRTOULL +# if defined(_WIN64) && !defined(__GNUC__) +# define STRTOULL _strtoui64 +# elif defined(_LLP64) || defined(__LLP64__) || defined(_WIN64) +# define STRTOULL strtoull +# else + /* strtoul() fits since sizeof(long) >= sizeof(word). */ +# define STRTOULL strtoul +# endif +#endif /* !STRTOULL */ + +#ifndef GC_WORD_C +# if defined(_WIN64) && !defined(__GNUC__) +# define GC_WORD_C(val) val##ui64 +# elif defined(_LLP64) || defined(__LLP64__) || defined(_WIN64) +# define GC_WORD_C(val) val##ULL +# else +# define GC_WORD_C(val) ((word)val##UL) +# endif +#endif /* !GC_WORD_C */ + +#if defined(__has_feature) + /* __has_feature() is supported. */ +# if __has_feature(address_sanitizer) && !defined(ADDRESS_SANITIZER) +# define ADDRESS_SANITIZER +# endif +# if __has_feature(memory_sanitizer) && !defined(MEMORY_SANITIZER) +# define MEMORY_SANITIZER +# endif +# if __has_feature(thread_sanitizer) && !defined(THREAD_SANITIZER) +# define THREAD_SANITIZER +# endif +#else +# ifdef __SANITIZE_ADDRESS__ + /* GCC v4.8+ */ +# define ADDRESS_SANITIZER +# endif +#endif /* !__has_feature */ + +#if defined(SPARC) +# define ASM_CLEAR_CODE /* Stack clearing is crucial, and we */ + /* include assembly code to do it well. */ +#endif + +/* Can we save call chain in objects for debugging? */ +/* SET NFRAMES (# of saved frames) and NARGS (#of args for each */ +/* frame) to reasonable values for the platform. */ +/* Set SAVE_CALL_CHAIN if we can. SAVE_CALL_COUNT can be specified */ +/* at build time, though we feel free to adjust it slightly. */ +/* Define NEED_CALLINFO if we either save the call stack or */ +/* GC_ADD_CALLER is defined. */ +/* GC_CAN_SAVE_CALL_STACKS is set in gc.h. */ +#if defined(SPARC) +# define CAN_SAVE_CALL_ARGS +#endif +#if (defined(I386) || defined(X86_64)) \ + && (defined(LINUX) || defined(__GLIBC__)) + /* SAVE_CALL_CHAIN is supported if the code is compiled to save */ + /* frame pointers by default, i.e. no -fomit-frame-pointer flag. */ +# define CAN_SAVE_CALL_ARGS +#endif + +#if defined(SAVE_CALL_COUNT) && !defined(GC_ADD_CALLER) \ + && defined(GC_CAN_SAVE_CALL_STACKS) +# define SAVE_CALL_CHAIN +#endif +#ifdef SAVE_CALL_CHAIN +# if defined(SAVE_CALL_NARGS) && defined(CAN_SAVE_CALL_ARGS) +# define NARGS SAVE_CALL_NARGS +# else +# define NARGS 0 /* Number of arguments to save for each call. */ +# endif +#endif +#ifdef SAVE_CALL_CHAIN +# if !defined(SAVE_CALL_COUNT) || defined(CPPCHECK) +# define NFRAMES 6 /* Number of frames to save. Even for */ + /* alignment reasons. */ +# else +# define NFRAMES ((SAVE_CALL_COUNT + 1) & ~1) +# endif +# define NEED_CALLINFO +#endif /* SAVE_CALL_CHAIN */ +#ifdef GC_ADD_CALLER +# define NFRAMES 1 +# define NARGS 0 +# define NEED_CALLINFO +#endif + +#if (defined(FREEBSD) || (defined(DARWIN) && !defined(_POSIX_C_SOURCE)) \ + || (defined(SOLARIS) && (!defined(_XOPEN_SOURCE) \ + || defined(__EXTENSIONS__))) \ + || defined(LINUX)) && !defined(HAVE_DLADDR) +# define HAVE_DLADDR +#endif + +#if defined(MAKE_BACK_GRAPH) && !defined(DBG_HDRS_ALL) +# define DBG_HDRS_ALL +#endif + +#if defined(POINTER_MASK) && !defined(POINTER_SHIFT) +# define POINTER_SHIFT 0 +#endif + +#if defined(POINTER_SHIFT) && !defined(POINTER_MASK) +# define POINTER_MASK ((word)(-1)) +#endif + +#if !defined(FIXUP_POINTER) && defined(POINTER_MASK) +# if defined(CPPCHECK) +# define FIXUP_POINTER(p) (p = (p) << 4) /* e.g. */ +# else +# define FIXUP_POINTER(p) (p = ((p) & POINTER_MASK) << POINTER_SHIFT) +# endif +#endif + +#if defined(FIXUP_POINTER) +# define NEED_FIXUP_POINTER +#else +# define FIXUP_POINTER(p) +#endif + +#if !defined(MARK_BIT_PER_GRANULE) && !defined(MARK_BIT_PER_OBJ) +# define MARK_BIT_PER_GRANULE /* Usually faster */ +#endif + +/* Some static sanity tests. */ +#if !defined(CPPCHECK) +#if defined(MARK_BIT_PER_GRANULE) && defined(MARK_BIT_PER_OBJ) +# error Define only one of MARK_BIT_PER_GRANULE and MARK_BIT_PER_OBJ. +#endif + +#if defined(STACK_GROWS_UP) && defined(STACK_GROWS_DOWN) +# error "Only one of STACK_GROWS_UP and STACK_GROWS_DOWN should be defd." +#endif +#if !defined(STACK_GROWS_UP) && !defined(STACK_GROWS_DOWN) +# error "One of STACK_GROWS_UP and STACK_GROWS_DOWN should be defd." +#endif + +#if defined(REDIRECT_MALLOC) && defined(THREADS) && !defined(LINUX) \ + && !defined(REDIRECT_MALLOC_IN_HEADER) +# error "REDIRECT_MALLOC with THREADS works at most on Linux." +#endif +#endif /* !CPPCHECK */ + +#ifdef GC_PRIVATE_H + /* This relies on some type definitions from gc_priv.h, from */ + /* where it's normally included. */ + /* */ + /* How to get heap memory from the OS: */ + /* Note that sbrk()-like allocation is preferred, since it */ + /* usually makes it possible to merge consecutively allocated */ + /* chunks. It also avoids unintended recursion with */ + /* REDIRECT_MALLOC macro defined. */ + /* GET_MEM() argument should be of size_t type and have */ + /* no side-effect. GET_MEM() returns HBLKSIZE-aligned chunk; */ + /* 0 is taken to mean failure. */ + /* In case of MMAP_SUPPORTED, the argument must also be */ + /* a multiple of a physical page size. */ + /* GET_MEM is currently not assumed to retrieve 0 filled space, */ + /* though we should perhaps take advantage of the case in which */ + /* does. */ + struct hblk; /* See gc_priv.h. */ +# if defined(PCR) + char * real_malloc(size_t bytes); +# define GET_MEM(bytes) HBLKPTR(real_malloc(SIZET_SAT_ADD(bytes, \ + GC_page_size)) \ + + GC_page_size-1) +# elif defined(OS2) + void * os2_alloc(size_t bytes); +# define GET_MEM(bytes) HBLKPTR((ptr_t)os2_alloc( \ + SIZET_SAT_ADD(bytes, \ + GC_page_size)) \ + + GC_page_size-1) +# elif defined(NEXT) || defined(DOS4GW) || defined(NONSTOP) \ + || (defined(AMIGA) && !defined(GC_AMIGA_FASTALLOC)) \ + || (defined(SOLARIS) && !defined(USE_MMAP)) || defined(RTEMS) \ + || defined(__CC_ARM) +# define GET_MEM(bytes) HBLKPTR((size_t)calloc(1, \ + SIZET_SAT_ADD(bytes, \ + GC_page_size)) \ + + GC_page_size - 1) +# elif defined(MSWIN_XBOX1) + void *durango_get_mem(size_t bytes, size_t page_size); +# define GET_MEM(bytes) (struct hblk *)durango_get_mem(bytes, 0) +# elif defined(MSWIN32) || defined(CYGWIN32) + ptr_t GC_win32_get_mem(size_t bytes); +# define GET_MEM(bytes) (struct hblk *)GC_win32_get_mem(bytes) +# elif defined(MACOS) +# if defined(USE_TEMPORARY_MEMORY) + Ptr GC_MacTemporaryNewPtr(size_t size, Boolean clearMemory); +# define GET_MEM(bytes) HBLKPTR(GC_MacTemporaryNewPtr( \ + SIZET_SAT_ADD(bytes, \ + GC_page_size), true) \ + + GC_page_size-1) +# else +# define GET_MEM(bytes) HBLKPTR(NewPtrClear(SIZET_SAT_ADD(bytes, \ + GC_page_size)) \ + + GC_page_size-1) +# endif +# elif defined(MSWINCE) + ptr_t GC_wince_get_mem(size_t bytes); +# define GET_MEM(bytes) (struct hblk *)GC_wince_get_mem(bytes) +# elif defined(AMIGA) && defined(GC_AMIGA_FASTALLOC) + void *GC_amiga_get_mem(size_t bytes); +# define GET_MEM(bytes) HBLKPTR((size_t)GC_amiga_get_mem( \ + SIZET_SAT_ADD(bytes, \ + GC_page_size)) \ + + GC_page_size-1) +# elif defined(SN_TARGET_ORBIS) + void *ps4_get_mem(size_t bytes); +# define GET_MEM(bytes) (struct hblk*)ps4_get_mem(bytes) +# elif defined(SN_TARGET_PS3) + void *ps3_get_mem(size_t bytes); +# define GET_MEM(bytes) (struct hblk*)ps3_get_mem(bytes) +# elif defined(SN_TARGET_PSP2) + void *psp2_get_mem(size_t bytes); +# define GET_MEM(bytes) (struct hblk*)psp2_get_mem(bytes) +# elif defined(NINTENDO_SWITCH) + void *switch_get_mem(size_t bytes); +# define GET_MEM(bytes) (struct hblk*)switch_get_mem(bytes) +# elif defined(HAIKU) + ptr_t GC_haiku_get_mem(size_t bytes); +# define GET_MEM(bytes) (struct hblk*)GC_haiku_get_mem(bytes) +# else + ptr_t GC_unix_get_mem(size_t bytes); +# define GET_MEM(bytes) (struct hblk *)GC_unix_get_mem(bytes) +# endif +#endif /* GC_PRIVATE_H */ + +EXTERN_C_END + +#ifndef SMALL_CONFIG +/* We were running out of memory due to the fact that + * GC has a static sized array for heap segments, and without + * LARGE_CONFIG you go OOM at around 1.8 GB on 64-bit. + * Note, we define it everywhere on Mono. */ +#define LARGE_CONFIG +#endif + +#endif /* GCCONFIG_H */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/private/msvc_dbg.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/private/msvc_dbg.h new file mode 100644 index 000000000..10dd1035c --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/private/msvc_dbg.h @@ -0,0 +1,70 @@ +/* + Copyright (c) 2004-2005 Andrei Polushin + + 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. +*/ + +#ifndef GC_MSVC_DBG_H +#define GC_MSVC_DBG_H + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +#if !MSVC_DBG_DLL +#define MSVC_DBG_EXPORT +#elif MSVC_DBG_BUILD +#define MSVC_DBG_EXPORT __declspec(dllexport) +#else +#define MSVC_DBG_EXPORT __declspec(dllimport) +#endif + +#ifndef MAX_SYM_NAME +#define MAX_SYM_NAME 2000 +#endif + +typedef void* HANDLE; +typedef struct _CONTEXT CONTEXT; + +MSVC_DBG_EXPORT size_t GetStackFrames(size_t skip, void* frames[], size_t maxFrames); +MSVC_DBG_EXPORT size_t GetStackFramesFromContext(HANDLE hProcess, HANDLE hThread, CONTEXT* context, size_t skip, void* frames[], size_t maxFrames); + +MSVC_DBG_EXPORT size_t GetModuleNameFromAddress(void* address, char* moduleName, size_t size); +MSVC_DBG_EXPORT size_t GetModuleNameFromStack(size_t skip, char* moduleName, size_t size); + +MSVC_DBG_EXPORT size_t GetSymbolNameFromAddress(void* address, char* symbolName, size_t size, size_t* offsetBytes); +MSVC_DBG_EXPORT size_t GetSymbolNameFromStack(size_t skip, char* symbolName, size_t size, size_t* offsetBytes); + +MSVC_DBG_EXPORT size_t GetFileLineFromAddress(void* address, char* fileName, size_t size, size_t* lineNumber, size_t* offsetBytes); +MSVC_DBG_EXPORT size_t GetFileLineFromStack(size_t skip, char* fileName, size_t size, size_t* lineNumber, size_t* offsetBytes); + +MSVC_DBG_EXPORT size_t GetDescriptionFromAddress(void* address, const char* format, char* description, size_t size); +MSVC_DBG_EXPORT size_t GetDescriptionFromStack(void*const frames[], size_t count, const char* format, char* description[], size_t size); + +/* Compatibility with */ +MSVC_DBG_EXPORT int backtrace(void* addresses[], int count); +MSVC_DBG_EXPORT char** backtrace_symbols(void*const addresses[], int count); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* GC_MSVC_DBG_H */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/private/pthread_stop_world.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/private/pthread_stop_world.h new file mode 100644 index 000000000..780c0d9af --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/private/pthread_stop_world.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2009 by Hewlett-Packard Development Company. + * All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#ifndef GC_PTHREAD_STOP_WORLD_H +#define GC_PTHREAD_STOP_WORLD_H + +EXTERN_C_BEGIN + +struct thread_stop_info { +# if !defined(GC_OPENBSD_UTHREADS) && !defined(NACL) +# ifdef GC_ATOMIC_OPS_H + volatile AO_t last_stop_count; +# else + word last_stop_count; +# endif + /* The value of GC_stop_count when the thread */ + /* last successfully handled a suspend signal. */ +# endif + + ptr_t stack_ptr; /* Valid only when stopped. */ + +# ifdef NACL + /* Grab NACL_GC_REG_STORAGE_SIZE pointers off the stack when */ + /* going into a syscall. 20 is more than we need, but it's an */ + /* overestimate in case the instrumented function uses any callee */ + /* saved registers, they may be pushed to the stack much earlier. */ + /* Also, on amd64 'push' puts 8 bytes on the stack even though */ + /* our pointers are 4 bytes. */ +# ifdef ARM32 + /* Space for r4-r8, r10-r12, r14. */ +# define NACL_GC_REG_STORAGE_SIZE 9 +# else +# define NACL_GC_REG_STORAGE_SIZE 20 +# endif + ptr_t reg_storage[NACL_GC_REG_STORAGE_SIZE]; +# endif + +#if defined(SN_TARGET_ORBIS) +# define ORBIS_GC_REG_STORAGE_SIZE 27 + __uint64_t registers[ORBIS_GC_REG_STORAGE_SIZE]; +#endif +}; + +GC_INNER void GC_stop_init(void); + +EXTERN_C_END + +#endif diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/private/pthread_support.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/private/pthread_support.h new file mode 100644 index 000000000..ae5c2e9e7 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/private/pthread_support.h @@ -0,0 +1,195 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2009 by Hewlett-Packard Development Company. + * All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#ifndef GC_PTHREAD_SUPPORT_H +#define GC_PTHREAD_SUPPORT_H + +#include "private/gc_priv.h" + +#if defined(GC_PTHREADS) && !defined(GC_WIN32_THREADS) + +#if defined(GC_DARWIN_THREADS) +# include "private/darwin_stop_world.h" +#else +# include "private/pthread_stop_world.h" +#endif + +#ifdef THREAD_LOCAL_ALLOC +# include "thread_local_alloc.h" +#endif + +#ifdef THREAD_SANITIZER +# include "dbg_mlc.h" /* for oh type */ +#endif + +EXTERN_C_BEGIN + +/* We use the allocation lock to protect thread-related data structures. */ + +/* The set of all known threads. We intercept thread creation and */ +/* joins. */ +/* Protected by allocation/GC lock. */ +/* Some of this should be declared volatile, but that's inconsistent */ +/* with some library routine declarations. */ +typedef struct GC_Thread_Rep { +# ifdef THREAD_SANITIZER + char dummy[sizeof(oh)]; /* A dummy field to avoid TSan false */ + /* positive about the race between */ + /* GC_has_other_debug_info and */ + /* GC_suspend_handler_inner (which */ + /* sets store_stop.stack_ptr). */ +# endif + + struct GC_Thread_Rep * next; /* More recently allocated threads */ + /* with a given pthread id come */ + /* first. (All but the first are */ + /* guaranteed to be dead, but we may */ + /* not yet have registered the join.) */ + pthread_t id; +# ifdef USE_TKILL_ON_ANDROID + pid_t kernel_id; +# endif + /* Extra bookkeeping information the stopping code uses */ + struct thread_stop_info stop_info; + +# if defined(GC_ENABLE_SUSPEND_THREAD) && !defined(GC_DARWIN_THREADS) \ + && !defined(GC_OPENBSD_UTHREADS) && !defined(NACL) + volatile AO_t suspended_ext; /* Thread was suspended externally. */ +# endif + + unsigned char flags; +# define FINISHED 1 /* Thread has exited. */ +# define DETACHED 2 /* Thread is treated as detached. */ + /* Thread may really be detached, or */ + /* it may have been explicitly */ + /* registered, in which case we can */ + /* deallocate its GC_Thread_Rep once */ + /* it unregisters itself, since it */ + /* may not return a GC pointer. */ +# define MAIN_THREAD 4 /* True for the original thread only. */ +# define DISABLED_GC 0x10 /* Collections are disabled while the */ + /* thread is exiting. */ + + unsigned char thread_blocked; + /* Protected by GC lock. */ + /* Treated as a boolean value. If set, */ + /* thread will acquire GC lock before */ + /* doing any pointer manipulations, and */ + /* has set its SP value. Thus it does */ + /* not need to be sent a signal to stop */ + /* it. */ + + unsigned short finalizer_skipped; + unsigned char finalizer_nested; + /* Used by GC_check_finalizer_nested() */ + /* to minimize the level of recursion */ + /* when a client finalizer allocates */ + /* memory (initially both are 0). */ + + ptr_t stack_end; /* Cold end of the stack (except for */ + /* main thread). */ + ptr_t altstack; /* The start of the alt-stack if there */ + /* is one, NULL otherwise. */ + word altstack_size; /* The size of the alt-stack if exists. */ + ptr_t stack; /* The start and size of the normal */ + /* stack (set by GC_register_altstack). */ + word stack_size; +# if defined(GC_DARWIN_THREADS) && !defined(DARWIN_DONT_PARSE_STACK) + ptr_t topOfStack; /* Result of GC_FindTopOfStack(0); */ + /* valid only if the thread is blocked; */ + /* non-NULL value means already set. */ +# endif +# ifdef IA64 + ptr_t backing_store_end; + ptr_t backing_store_ptr; +# endif + + struct GC_traced_stack_sect_s *traced_stack_sect; + /* Points to the "frame" data held in stack by */ + /* the innermost GC_call_with_gc_active() of */ + /* this thread. May be NULL. */ + + void * status; /* The value returned from the thread. */ + /* Used only to avoid premature */ + /* reclamation of any data it might */ + /* reference. */ + /* This is unfortunately also the */ + /* reason we need to intercept join */ + /* and detach. */ + +# ifdef THREAD_LOCAL_ALLOC + struct thread_local_freelists tlfs; +# endif +} * GC_thread; + +#ifndef THREAD_TABLE_SZ +# define THREAD_TABLE_SZ 256 /* Power of 2 (for speed). */ +#endif + +#if CPP_WORDSZ == 64 +# define THREAD_TABLE_INDEX(id) \ + (int)(((((NUMERIC_THREAD_ID(id) >> 8) ^ NUMERIC_THREAD_ID(id)) >> 16) \ + ^ ((NUMERIC_THREAD_ID(id) >> 8) ^ NUMERIC_THREAD_ID(id))) \ + % THREAD_TABLE_SZ) +#else +# define THREAD_TABLE_INDEX(id) \ + (int)(((NUMERIC_THREAD_ID(id) >> 16) \ + ^ (NUMERIC_THREAD_ID(id) >> 8) \ + ^ NUMERIC_THREAD_ID(id)) % THREAD_TABLE_SZ) +#endif + +GC_EXTERN volatile GC_thread GC_threads[THREAD_TABLE_SZ]; + +GC_EXTERN GC_bool GC_thr_initialized; + +GC_INNER GC_thread GC_lookup_thread(pthread_t id); + +GC_EXTERN GC_bool GC_in_thread_creation; + /* We may currently be in thread creation or destruction. */ + /* Only set to TRUE while allocation lock is held. */ + /* When set, it is OK to run GC from unknown thread. */ + +#ifdef NACL + GC_EXTERN __thread GC_thread GC_nacl_gc_thread_self; + GC_INNER void GC_nacl_initialize_gc_thread(void); + GC_INNER void GC_nacl_shutdown_gc_thread(void); +#endif + +#ifdef GC_EXPLICIT_SIGNALS_UNBLOCK + GC_INNER void GC_unblock_gc_signals(void); +#endif + +#ifdef GC_PTHREAD_START_STANDALONE +# define GC_INNER_PTHRSTART /* empty */ +#else +# define GC_INNER_PTHRSTART GC_INNER +#endif + +GC_INNER_PTHRSTART void * GC_CALLBACK GC_inner_start_routine( + struct GC_stack_base *sb, void *arg); + +GC_INNER_PTHRSTART GC_thread GC_start_rtn_prepare_thread( + void *(**pstart)(void *), + void **pstart_arg, + struct GC_stack_base *sb, void *arg); +GC_INNER_PTHRSTART void GC_thread_exit_proc(void *); + +EXTERN_C_END + +#endif /* GC_PTHREADS && !GC_WIN32_THREADS */ + +#endif /* GC_PTHREAD_SUPPORT_H */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/private/specific.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/private/specific.h new file mode 100644 index 000000000..1cc1f6b00 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/private/specific.h @@ -0,0 +1,103 @@ +/* + * This is a reimplementation of a subset of the pthread_getspecific/setspecific + * interface. This appears to outperform the standard linuxthreads one + * by a significant margin. + * The major restriction is that each thread may only make a single + * pthread_setspecific call on a single key. (The current data structure + * doesn't really require that. The restriction should be easily removable.) + * We don't currently support the destruction functions, though that + * could be done. + * We also currently assume that only one pthread_setspecific call + * can be executed at a time, though that assumption would be easy to remove + * by adding a lock. + */ + +#include + +EXTERN_C_BEGIN + +/* Called during key creation or setspecific. */ +/* For the GC we already hold lock. */ +/* Currently allocated objects leak on thread exit. */ +/* That's hard to fix, but OK if we allocate garbage */ +/* collected memory. */ +#define MALLOC_CLEAR(n) GC_INTERNAL_MALLOC(n, NORMAL) + +#define TS_CACHE_SIZE 1024 +#define CACHE_HASH(n) ((((n) >> 8) ^ (n)) & (TS_CACHE_SIZE - 1)) + +#define TS_HASH_SIZE 1024 +#define HASH(p) \ + ((unsigned)((((word)(p)) >> 8) ^ (word)(p)) & (TS_HASH_SIZE - 1)) + +/* An entry describing a thread-specific value for a given thread. */ +/* All such accessible structures preserve the invariant that if either */ +/* thread is a valid pthread id or qtid is a valid "quick thread id" */ +/* for a thread, then value holds the corresponding thread specific */ +/* value. This invariant must be preserved at ALL times, since */ +/* asynchronous reads are allowed. */ +typedef struct thread_specific_entry { + volatile AO_t qtid; /* quick thread id, only for cache */ + void * value; + struct thread_specific_entry *next; + pthread_t thread; +} tse; + +/* We represent each thread-specific datum as two tables. The first is */ +/* a cache, indexed by a "quick thread identifier". The "quick" thread */ +/* identifier is an easy to compute value, which is guaranteed to */ +/* determine the thread, though a thread may correspond to more than */ +/* one value. We typically use the address of a page in the stack. */ +/* The second is a hash table, indexed by pthread_self(). It is used */ +/* only as a backup. */ + +/* Return the "quick thread id". Default version. Assumes page size, */ +/* or at least thread stack separation, is at least 4K. */ +/* Must be defined so that it never returns 0. (Page 0 can't really be */ +/* part of any stack, since that would make 0 a valid stack pointer.) */ +#define quick_thread_id() (((word)GC_approx_sp()) >> 12) + +#define INVALID_QTID ((word)0) +#define INVALID_THREADID ((pthread_t)0) + +union ptse_ao_u { + tse *p; + volatile AO_t ao; +}; + +typedef struct thread_specific_data { + tse * volatile cache[TS_CACHE_SIZE]; + /* A faster index to the hash table */ + union ptse_ao_u hash[TS_HASH_SIZE]; + pthread_mutex_t lock; +} tsd; + +typedef tsd * GC_key_t; + +#define GC_key_create(key, d) GC_key_create_inner(key) +GC_INNER int GC_key_create_inner(tsd ** key_ptr); +GC_INNER int GC_setspecific(tsd * key, void * value); +#define GC_remove_specific(key) \ + GC_remove_specific_after_fork(key, pthread_self()) +GC_INNER void GC_remove_specific_after_fork(tsd * key, pthread_t t); + +/* An internal version of getspecific that assumes a cache miss. */ +GC_INNER void * GC_slow_getspecific(tsd * key, word qtid, + tse * volatile * cache_entry); + +/* GC_INLINE is defined in gc_priv.h. */ +GC_INLINE void * GC_getspecific(tsd * key) +{ + word qtid = quick_thread_id(); + tse * volatile * entry_ptr = &key->cache[CACHE_HASH(qtid)]; + tse * entry = *entry_ptr; /* Must be loaded only once. */ + + GC_ASSERT(qtid != INVALID_QTID); + if (EXPECT(entry -> qtid == qtid, TRUE)) { + GC_ASSERT(entry -> thread == pthread_self()); + return entry -> value; + } + return GC_slow_getspecific(key, qtid, entry_ptr); +} + +EXTERN_C_END diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/private/thread_local_alloc.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/private/thread_local_alloc.h new file mode 100644 index 000000000..276de402f --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/private/thread_local_alloc.h @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2000-2005 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* Included indirectly from a thread-library-specific file. */ +/* This is the interface for thread-local allocation, whose */ +/* implementation is mostly thread-library-independent. */ +/* Here we describe only the interface that needs to be known */ +/* and invoked from the thread support layer; the actual */ +/* implementation also exports GC_malloc and friends, which */ +/* are declared in gc.h. */ + +#ifndef GC_THREAD_LOCAL_ALLOC_H +#define GC_THREAD_LOCAL_ALLOC_H + +#include "private/gc_priv.h" + +#ifdef THREAD_LOCAL_ALLOC + +#include "gc_inline.h" + +#if defined(USE_HPUX_TLS) +# error USE_HPUX_TLS macro was replaced by USE_COMPILER_TLS +#endif + +#include + +EXTERN_C_BEGIN + +#if !defined(USE_PTHREAD_SPECIFIC) && !defined(USE_WIN32_SPECIFIC) \ + && !defined(USE_WIN32_COMPILER_TLS) && !defined(USE_COMPILER_TLS) \ + && !defined(USE_CUSTOM_SPECIFIC) +# if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32) +# if defined(CYGWIN32) && GC_GNUC_PREREQ(4, 0) +# if defined(__clang__) + /* As of Cygwin clang3.5.2, thread-local storage is unsupported. */ +# define USE_PTHREAD_SPECIFIC +# else +# define USE_COMPILER_TLS +# endif +# elif defined(__GNUC__) || defined(MSWINCE) +# define USE_WIN32_SPECIFIC +# else +# define USE_WIN32_COMPILER_TLS +# endif /* !GNU */ +# elif (defined(LINUX) && !defined(ARM32) && !defined(AVR32) \ + && GC_GNUC_PREREQ(3, 3) \ + && !(defined(__clang__) && defined(HOST_ANDROID))) \ + || (defined(FREEBSD) && defined(__GLIBC__) /* kFreeBSD */ \ + && GC_GNUC_PREREQ(4, 4)) \ + || (defined(HOST_ANDROID) && defined(ARM32) \ + && (GC_GNUC_PREREQ(4, 6) || GC_CLANG_PREREQ_FULL(3, 8, 256229))) +# define USE_COMPILER_TLS +# elif defined(GC_DGUX386_THREADS) || defined(GC_OSF1_THREADS) \ + || defined(GC_AIX_THREADS) || defined(GC_DARWIN_THREADS) \ + || defined(GC_FREEBSD_THREADS) || defined(GC_NETBSD_THREADS) \ + || defined(GC_LINUX_THREADS) || defined(GC_HAIKU_THREADS) \ + || defined(GC_RTEMS_PTHREADS) +# define USE_PTHREAD_SPECIFIC +# elif defined(GC_HPUX_THREADS) +# ifdef __GNUC__ +# define USE_PTHREAD_SPECIFIC + /* Empirically, as of gcc 3.3, USE_COMPILER_TLS doesn't work. */ +# else +# define USE_COMPILER_TLS +# endif +# else +# define USE_CUSTOM_SPECIFIC /* Use our own. */ +# endif +#endif + +#ifndef THREAD_FREELISTS_KINDS +# ifdef ENABLE_DISCLAIM +# define THREAD_FREELISTS_KINDS (NORMAL+2) +# else +# define THREAD_FREELISTS_KINDS (NORMAL+1) +# endif +#endif /* !THREAD_FREELISTS_KINDS */ + +/* One of these should be declared as the tlfs field in the */ +/* structure pointed to by a GC_thread. */ +typedef struct thread_local_freelists { + void * _freelists[THREAD_FREELISTS_KINDS][TINY_FREELISTS]; +# define ptrfree_freelists _freelists[PTRFREE] +# define normal_freelists _freelists[NORMAL] + /* Note: Preserve *_freelists names for some clients. */ +# ifdef GC_GCJ_SUPPORT + void * gcj_freelists[TINY_FREELISTS]; +# define ERROR_FL ((void *)(word)-1) + /* Value used for gcj_freelists[-1]; allocation is */ + /* erroneous. */ +# endif + /* Free lists contain either a pointer or a small count */ + /* reflecting the number of granules allocated at that */ + /* size. */ + /* 0 ==> thread-local allocation in use, free list */ + /* empty. */ + /* > 0, <= DIRECT_GRANULES ==> Using global allocation, */ + /* too few objects of this size have been */ + /* allocated by this thread. */ + /* >= HBLKSIZE => pointer to nonempty free list. */ + /* > DIRECT_GRANULES, < HBLKSIZE ==> transition to */ + /* local alloc, equivalent to 0. */ +# define DIRECT_GRANULES (HBLKSIZE/GRANULE_BYTES) + /* Don't use local free lists for up to this much */ + /* allocation. */ +} *GC_tlfs; + +#if defined(USE_PTHREAD_SPECIFIC) +# define GC_getspecific pthread_getspecific +# define GC_setspecific pthread_setspecific +# define GC_key_create pthread_key_create +# define GC_remove_specific(key) pthread_setspecific(key, NULL) + /* Explicitly delete the value to stop the TLS */ + /* destructor from being called repeatedly. */ +# define GC_remove_specific_after_fork(key, t) (void)0 + /* Should not need any action. */ + typedef pthread_key_t GC_key_t; +#elif defined(USE_COMPILER_TLS) || defined(USE_WIN32_COMPILER_TLS) +# define GC_getspecific(x) (x) +# define GC_setspecific(key, v) ((key) = (v), 0) +# define GC_key_create(key, d) 0 +# define GC_remove_specific(key) /* No need for cleanup on exit. */ +# define GC_remove_specific_after_fork(key, t) (void)0 + typedef void * GC_key_t; +#elif defined(USE_WIN32_SPECIFIC) +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +# endif +# define NOSERVICE + EXTERN_C_END +# include + EXTERN_C_BEGIN +# define GC_getspecific TlsGetValue +# define GC_setspecific(key, v) !TlsSetValue(key, v) + /* We assume 0 == success, msft does the opposite. */ +# ifndef TLS_OUT_OF_INDEXES + /* this is currently missing in WinCE */ +# define TLS_OUT_OF_INDEXES (DWORD)0xFFFFFFFF +# endif +# define GC_key_create(key, d) \ + ((d) != 0 || (*(key) = TlsAlloc()) == TLS_OUT_OF_INDEXES ? -1 : 0) +# define GC_remove_specific(key) /* No need for cleanup on exit. */ + /* Need TlsFree on process exit/detach? */ +# define GC_remove_specific_after_fork(key, t) (void)0 + typedef DWORD GC_key_t; +#elif defined(USE_CUSTOM_SPECIFIC) + EXTERN_C_END +# include "private/specific.h" + EXTERN_C_BEGIN +#else +# error implement me +#endif + +/* Each thread structure must be initialized. */ +/* This call must be made from the new thread. */ +/* Caller holds allocation lock. */ +GC_INNER void GC_init_thread_local(GC_tlfs p); + +/* Called when a thread is unregistered, or exits. */ +/* We hold the allocator lock. */ +GC_INNER void GC_destroy_thread_local(GC_tlfs p); + +/* The thread support layer must arrange to mark thread-local */ +/* free lists explicitly, since the link field is often */ +/* invisible to the marker. It knows how to find all threads; */ +/* we take care of an individual thread freelist structure. */ +GC_INNER void GC_mark_thread_local_fls_for(GC_tlfs p); + +#ifdef GC_ASSERTIONS + GC_bool GC_is_thread_tsd_valid(void *tsd); + void GC_check_tls_for(GC_tlfs p); +# if defined(USE_CUSTOM_SPECIFIC) + void GC_check_tsd_marks(tsd *key); +# endif +#endif /* GC_ASSERTIONS */ + +#ifndef GC_ATTR_TLS_FAST +# define GC_ATTR_TLS_FAST /* empty */ +#endif + +extern +#if defined(USE_COMPILER_TLS) + __thread GC_ATTR_TLS_FAST +#elif defined(USE_WIN32_COMPILER_TLS) + __declspec(thread) GC_ATTR_TLS_FAST +#endif + GC_key_t GC_thread_key; +/* This is set up by the thread_local_alloc implementation. No need */ +/* for cleanup on thread exit. But the thread support layer makes sure */ +/* that GC_thread_key is traced, if necessary. */ + +EXTERN_C_END + +#endif /* THREAD_LOCAL_ALLOC */ + +#endif /* GC_THREAD_LOCAL_ALLOC_H */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/include/weakpointer.h b/unity-2019.4.24f1-mbe/external/bdwgc/include/weakpointer.h new file mode 100644 index 000000000..b9af99b59 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/include/weakpointer.h @@ -0,0 +1,217 @@ +#ifndef _weakpointer_h_ +#define _weakpointer_h_ + +/**************************************************************************** + +WeakPointer and CleanUp + + Copyright (c) 1991 by Xerox Corporation. All rights reserved. + + THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + + Permission is hereby granted to copy this code for any purpose, + provided the above notices are retained on all copies. + +****************************************************************************/ + +/**************************************************************************** + +WeakPointer + +A weak pointer is a pointer to a heap-allocated object that doesn't +prevent the object from being garbage collected. Weak pointers can be +used to track which objects haven't yet been reclaimed by the +collector. A weak pointer is deactivated when the collector discovers +its referent object is unreachable by normal pointers (reachability +and deactivation are defined more precisely below). A deactivated weak +pointer remains deactivated forever. + +****************************************************************************/ + + +template< class T > class WeakPointer { +public: + +WeakPointer( T* t = 0 ) + /* Constructs a weak pointer for *t. t may be null. It is an error + if t is non-null and *t is not a collected object. */ + {impl = _WeakPointer_New( t );} + +T* Pointer() + /* wp.Pointer() returns a pointer to the referent object of wp or + null if wp has been deactivated (because its referent object + has been discovered unreachable by the collector). */ + {return (T*) _WeakPointer_Pointer( this->impl );} + +int operator==( WeakPointer< T > wp2 ) + /* Given weak pointers wp1 and wp2, if wp1 == wp2, then wp1 and + wp2 refer to the same object. If wp1 != wp2, then either wp1 + and wp2 don't refer to the same object, or if they do, one or + both of them has been deactivated. (Note: If objects t1 and t2 + are never made reachable by their clean-up functions, then + WeakPointer(t1) == WeakPointer(t2) if and only t1 == t2.) */ + {return _WeakPointer_Equal( this->impl, wp2.impl );} + +int Hash() + /* Returns a hash code suitable for use by multiplicative- and + division-based hash tables. If wp1 == wp2, then wp1.Hash() == + wp2.Hash(). */ + {return _WeakPointer_Hash( this->impl );} + +private: +void* impl; +}; + +/***************************************************************************** + +CleanUp + +A garbage-collected object can have an associated clean-up function +that will be invoked some time after the collector discovers the +object is unreachable via normal pointers. Clean-up functions can be +used to release resources such as open-file handles or window handles +when their containing objects become unreachable. If a C++ object has +a non-empty explicit destructor (i.e. it contains programmer-written +code), the destructor will be automatically registered as the object's +initial clean-up function. + +There is no guarantee that the collector will detect every unreachable +object (though it will find almost all of them). Clients should not +rely on clean-up to cause some action to occur immediately -- clean-up +is only a mechanism for improving resource usage. + +Every object with a clean-up function also has a clean-up queue. When +the collector finds the object is unreachable, it enqueues it on its +queue. The clean-up function is applied when the object is removed +from the queue. By default, objects are enqueued on the garbage +collector's queue, and the collector removes all objects from its +queue after each collection. If a client supplies another queue for +objects, it is his responsibility to remove objects (and cause their +functions to be called) by polling it periodically. + +Clean-up queues allow clean-up functions accessing global data to +synchronize with the main program. Garbage collection can occur at any +time, and clean-ups invoked by the collector might access data in an +inconsistent state. A client can control this by defining an explicit +queue for objects and polling it at safe points. + +The following definitions are used by the specification below: + +Given a pointer t to a collected object, the base object BO(t) is the +value returned by new when it created the object. (Because of multiple +inheritance, t and BO(t) may not be the same address.) + +A weak pointer wp references an object *t if BO(wp.Pointer()) == +BO(t). + +***************************************************************************/ + +template< class T, class Data > class CleanUp { +public: + +static void Set( T* t, void c( Data* d, T* t ), Data* d = 0 ) + /* Sets the clean-up function of object BO(t) to be , + replacing any previously defined clean-up function for BO(t); c + and d can be null, but t cannot. Sets the clean-up queue for + BO(t) to be the collector's queue. When t is removed from its + clean-up queue, its clean-up will be applied by calling c(d, + t). It is an error if *t is not a collected object. */ + {_CleanUp_Set( t, c, d );} + +static void Call( T* t ) + /* Sets the new clean-up function for BO(t) to be null and, if the + old one is non-null, calls it immediately, even if BO(t) is + still reachable. Deactivates any weak pointers to BO(t). */ + {_CleanUp_Call( t );} + +class Queue {public: + Queue() + /* Constructs a new queue. */ + {this->head = _CleanUp_Queue_NewHead();} + + void Set( T* t ) + /* q.Set(t) sets the clean-up queue of BO(t) to be q. */ + {_CleanUp_Queue_Set( this->head, t );} + + int Call() + /* If q is non-empty, q.Call() removes the first object and + calls its clean-up function; does nothing if q is + empty. Returns true if there are more objects in the + queue. */ + {return _CleanUp_Queue_Call( this->head );} + + private: + void* head; + }; +}; + +/********************************************************************** + +Reachability and Clean-up + +An object O is reachable if it can be reached via a non-empty path of +normal pointers from the registers, stacks, global variables, or an +object with a non-null clean-up function (including O itself), +ignoring pointers from an object to itself. + +This definition of reachability ensures that if object B is accessible +from object A (and not vice versa) and if both A and B have clean-up +functions, then A will always be cleaned up before B. Note that as +long as an object with a clean-up function is contained in a cycle of +pointers, it will always be reachable and will never be cleaned up or +collected. + +When the collector finds an unreachable object with a null clean-up +function, it atomically deactivates all weak pointers referencing the +object and recycles its storage. If object B is accessible from object +A via a path of normal pointers, A will be discovered unreachable no +later than B, and a weak pointer to A will be deactivated no later +than a weak pointer to B. + +When the collector finds an unreachable object with a non-null +clean-up function, the collector atomically deactivates all weak +pointers referencing the object, redefines its clean-up function to be +null, and enqueues it on its clean-up queue. The object then becomes +reachable again and remains reachable at least until its clean-up +function executes. + +The clean-up function is assured that its argument is the only +accessible pointer to the object. Nothing prevents the function from +redefining the object's clean-up function or making the object +reachable again (for example, by storing the pointer in a global +variable). + +If the clean-up function does not make its object reachable again and +does not redefine its clean-up function, then the object will be +collected by a subsequent collection (because the object remains +unreachable and now has a null clean-up function). If the clean-up +function does make its object reachable again and a clean-up function +is subsequently redefined for the object, then the new clean-up +function will be invoked the next time the collector finds the object +unreachable. + +Note that a destructor for a collected object cannot safely redefine a +clean-up function for its object, since after the destructor executes, +the object has been destroyed into "raw memory". (In most +implementations, destroying an object mutates its vtbl.) + +Finally, note that calling delete t on a collected object first +deactivates any weak pointers to t and then invokes its clean-up +function (destructor). + +**********************************************************************/ + +extern "C" { + void* _WeakPointer_New( void* t ); + void* _WeakPointer_Pointer( void* wp ); + int _WeakPointer_Equal( void* wp1, void* wp2 ); + int _WeakPointer_Hash( void* wp ); + void _CleanUp_Set( void* t, void (*c)( void* d, void* t ), void* d ); + void _CleanUp_Call( void* t ); + void* _CleanUp_Queue_NewHead (); + void _CleanUp_Queue_Set( void* h, void* t ); + int _CleanUp_Queue_Call( void* h ); +} + +#endif /* _weakpointer_h_ */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/m4/gc_set_version.m4 b/unity-2019.4.24f1-mbe/external/bdwgc/m4/gc_set_version.m4 new file mode 100644 index 000000000..465e4f0f9 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/m4/gc_set_version.m4 @@ -0,0 +1,40 @@ +# +# THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED +# OR IMPLIED. ANY USE IS AT YOUR OWN RISK. +# +# Permission is hereby granted to use or copy this program +# for any purpose, provided the above notices are retained on all copies. +# Permission to modify the code and to distribute modified code is granted, +# provided the above notices are retained, and a notice that the code was +# modified is included with the above copyright notice. + +# GC_SET_VERSION +# sets and AC_DEFINEs GC_VERSION_MAJOR, GC_VERSION_MINOR and GC_VERSION_MICRO +# based on the contents of PACKAGE_VERSION; PACKAGE_VERSION must conform to +# [0-9]+[.][0-9]+[.][0-9]+ +# +AC_DEFUN([GC_SET_VERSION], [ + AC_MSG_CHECKING(GC version numbers) + GC_VERSION_MAJOR=`echo $PACKAGE_VERSION | sed 's/^\([[0-9]][[0-9]]*\)[[.]].*$/\1/g'` + GC_VERSION_MINOR=`echo $PACKAGE_VERSION | sed 's/^[[^.]]*[[.]]\([[0-9]][[0-9]]*\).*$/\1/g'` + GC_VERSION_MICRO=`echo $PACKAGE_VERSION | sed 's/^[[^.]]*[[.]][[^.]]*[[.]]\([[0-9]][[0-9]]*\)$/\1/g'` + + if test :$GC_VERSION_MAJOR: = :: \ + -o :$GC_VERSION_MINOR: = :: \ + -o :$GC_VERSION_MICRO: = :: ; + then + AC_MSG_RESULT(invalid) + AC_MSG_ERROR([nonconforming PACKAGE_VERSION='$PACKAGE_VERSION']) + fi + + AC_DEFINE_UNQUOTED([GC_VERSION_MAJOR], $GC_VERSION_MAJOR, + [The major version number of this GC release.]) + AC_DEFINE_UNQUOTED([GC_VERSION_MINOR], $GC_VERSION_MINOR, + [The minor version number of this GC release.]) + AC_DEFINE_UNQUOTED([GC_VERSION_MICRO], $GC_VERSION_MICRO, + [The micro version number of this GC release.]) + AC_MSG_RESULT(major=$GC_VERSION_MAJOR minor=$GC_VERSION_MINOR \ + micro=$GC_VERSION_MICRO) +]) + +sinclude(libtool.m4) diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/mach_dep.c b/unity-2019.4.24f1-mbe/external/bdwgc/mach_dep.c new file mode 100644 index 000000000..1267bbcde --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/mach_dep.c @@ -0,0 +1,340 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#include "private/gc_priv.h" + +#if !defined(SN_TARGET_ORBIS) && !defined(SN_TARGET_PSP2) + +#include + +#ifdef AMIGA +# ifndef __GNUC__ +# include +# else +# include +# endif +#endif + +#if defined(MACOS) && defined(__MWERKS__) + +#if defined(POWERPC) + +# define NONVOLATILE_GPR_COUNT 19 + struct ppc_registers { + unsigned long gprs[NONVOLATILE_GPR_COUNT]; /* R13-R31 */ + }; + typedef struct ppc_registers ppc_registers; + +# if defined(CPPCHECK) + void getRegisters(ppc_registers* regs); +# else + asm static void getRegisters(register ppc_registers* regs) + { + stmw r13,regs->gprs /* save R13-R31 */ + blr + } +# endif + + static void PushMacRegisters(void) + { + ppc_registers regs; + int i; + getRegisters(®s); + for (i = 0; i < NONVOLATILE_GPR_COUNT; i++) + GC_push_one(regs.gprs[i]); + } + +#else /* M68K */ + + asm static void PushMacRegisters(void) + { + sub.w #4,sp /* reserve space for one parameter */ + move.l a2,(sp) + jsr GC_push_one + move.l a3,(sp) + jsr GC_push_one + move.l a4,(sp) + jsr GC_push_one +# if !__option(a6frames) + /* perhaps a6 should be pushed if stack frames are not being used */ + move.l a6,(sp) + jsr GC_push_one +# endif + /* skip a5 (globals), a6 (frame pointer), and a7 (stack pointer) */ + move.l d2,(sp) + jsr GC_push_one + move.l d3,(sp) + jsr GC_push_one + move.l d4,(sp) + jsr GC_push_one + move.l d5,(sp) + jsr GC_push_one + move.l d6,(sp) + jsr GC_push_one + move.l d7,(sp) + jsr GC_push_one + add.w #4,sp /* fix stack */ + rts + } + +#endif /* M68K */ + +#endif /* MACOS && __MWERKS__ */ + +# if defined(SPARC) || defined(IA64) + /* Value returned from register flushing routine; either sp (SPARC) */ + /* or ar.bsp (IA64). */ + GC_INNER ptr_t GC_save_regs_ret_val = NULL; +# endif + +/* Routine to mark from registers that are preserved by the C compiler. */ +/* This must be ported to every new architecture. It is not optional, */ +/* and should not be used on platforms that are either UNIX-like, or */ +/* require thread support. */ + +#undef HAVE_PUSH_REGS + +#if defined(USE_ASM_PUSH_REGS) +# define HAVE_PUSH_REGS +#else /* No asm implementation */ + +# ifdef STACK_NOT_SCANNED + void GC_push_regs(void) + { + /* empty */ + } +# define HAVE_PUSH_REGS + +# elif defined(M68K) && defined(AMIGA) + /* This function is not static because it could also be */ + /* erroneously defined in .S file, so this error would be caught */ + /* by the linker. */ + void GC_push_regs(void) + { + /* AMIGA - could be replaced by generic code */ + /* a0, a1, d0 and d1 are caller save */ + +# ifdef __GNUC__ + asm("subq.w &0x4,%sp"); /* allocate word on top of stack */ + + asm("mov.l %a2,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %a3,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %a4,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %a5,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %a6,(%sp)"); asm("jsr _GC_push_one"); + /* Skip frame pointer and stack pointer */ + asm("mov.l %d2,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %d3,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %d4,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %d5,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %d6,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %d7,(%sp)"); asm("jsr _GC_push_one"); + + asm("addq.w &0x4,%sp"); /* put stack back where it was */ +# else /* !__GNUC__ */ + GC_push_one(getreg(REG_A2)); + GC_push_one(getreg(REG_A3)); +# ifndef __SASC + /* Can probably be changed to #if 0 -Kjetil M. (a4=globals) */ + GC_push_one(getreg(REG_A4)); +# endif + GC_push_one(getreg(REG_A5)); + GC_push_one(getreg(REG_A6)); + /* Skip stack pointer */ + GC_push_one(getreg(REG_D2)); + GC_push_one(getreg(REG_D3)); + GC_push_one(getreg(REG_D4)); + GC_push_one(getreg(REG_D5)); + GC_push_one(getreg(REG_D6)); + GC_push_one(getreg(REG_D7)); +# endif /* !__GNUC__ */ + } +# define HAVE_PUSH_REGS + +# elif defined(MACOS) + +# if defined(M68K) && defined(THINK_C) && !defined(CPPCHECK) +# define PushMacReg(reg) \ + move.l reg,(sp) \ + jsr GC_push_one + void GC_push_regs(void) + { + asm { + sub.w #4,sp ; reserve space for one parameter. + PushMacReg(a2); + PushMacReg(a3); + PushMacReg(a4); + ; skip a5 (globals), a6 (frame pointer), and a7 (stack pointer) + PushMacReg(d2); + PushMacReg(d3); + PushMacReg(d4); + PushMacReg(d5); + PushMacReg(d6); + PushMacReg(d7); + add.w #4,sp ; fix stack. + } + } +# define HAVE_PUSH_REGS +# undef PushMacReg +# elif defined(__MWERKS__) + void GC_push_regs(void) + { + PushMacRegisters(); + } +# define HAVE_PUSH_REGS +# endif /* __MWERKS__ */ +# endif /* MACOS */ + +#endif /* !USE_ASM_PUSH_REGS */ + +#if defined(HAVE_PUSH_REGS) && defined(THREADS) +# error GC_push_regs cannot be used with threads + /* Would fail for GC_do_blocking. There are probably other safety */ + /* issues. */ +# undef HAVE_PUSH_REGS +#endif + +#if !defined(HAVE_PUSH_REGS) && defined(UNIX_LIKE) +# include +# ifndef NO_GETCONTEXT +# if defined(DARWIN) \ + && (MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 /*MAC_OS_X_VERSION_10_6*/) +# include +# else +# include +# endif /* !DARWIN */ +# ifdef GETCONTEXT_FPU_EXCMASK_BUG +# include +# endif +# endif +#endif /* !HAVE_PUSH_REGS */ + +/* Ensure that either registers are pushed, or callee-save registers */ +/* are somewhere on the stack, and then call fn(arg, ctxt). */ +/* ctxt is either a pointer to a ucontext_t we generated, or NULL. */ +GC_ATTR_NO_SANITIZE_ADDR +GC_INNER void GC_with_callee_saves_pushed(void (*fn)(ptr_t, void *), + volatile ptr_t arg) +{ + volatile int dummy; + void * volatile context = 0; + +# if defined(HAVE_PUSH_REGS) + GC_push_regs(); +# else +# if defined(UNIX_LIKE) && !defined(NO_GETCONTEXT) + /* Older versions of Darwin seem to lack getcontext(). */ + /* ARM and MIPS Linux often doesn't support a real */ + /* getcontext(). */ + static signed char getcontext_works = 0; /* (-1) - broken, 1 - works */ + ucontext_t ctxt; +# ifdef GETCONTEXT_FPU_EXCMASK_BUG + /* Workaround a bug (clearing the FPU exception mask) in */ + /* getcontext on Linux/x86_64. */ +# ifdef X86_64 + /* We manipulate FPU control word here just not to force the */ + /* client application to use -lm linker option. */ + unsigned short old_fcw; + +# if defined(CPPCHECK) + GC_noop1((word)&old_fcw); +# endif + __asm__ __volatile__ ("fstcw %0" : "=m" (*&old_fcw)); +# else + int except_mask = fegetexcept(); +# endif +# endif + + if (getcontext_works >= 0) { + if (getcontext(&ctxt) < 0) { + WARN("getcontext failed:" + " using another register retrieval method...\n", 0); + /* getcontext() is broken, do not try again. */ + /* E.g., to workaround a bug in Docker ubuntu_32bit. */ + } else { + context = &ctxt; + } + if (EXPECT(0 == getcontext_works, FALSE)) + getcontext_works = context != NULL ? 1 : -1; + } +# ifdef GETCONTEXT_FPU_EXCMASK_BUG +# ifdef X86_64 + __asm__ __volatile__ ("fldcw %0" : : "m" (*&old_fcw)); + { + unsigned mxcsr; + /* And now correct the exception mask in SSE MXCSR. */ + __asm__ __volatile__ ("stmxcsr %0" : "=m" (*&mxcsr)); + mxcsr = (mxcsr & ~(FE_ALL_EXCEPT << 7)) | + ((old_fcw & FE_ALL_EXCEPT) << 7); + __asm__ __volatile__ ("ldmxcsr %0" : : "m" (*&mxcsr)); + } +# else /* !X86_64 */ + if (feenableexcept(except_mask) < 0) + ABORT("feenableexcept failed"); +# endif +# endif /* GETCONTEXT_FPU_EXCMASK_BUG */ +# if defined(SPARC) || defined(IA64) + /* On a register window machine, we need to save register */ + /* contents on the stack for this to work. This may already be */ + /* subsumed by the getcontext() call. */ + GC_save_regs_ret_val = GC_save_regs_in_stack(); +# endif + if (NULL == context) /* getcontext failed */ +# endif /* !NO_GETCONTEXT */ + { +# if defined(HAVE_BUILTIN_UNWIND_INIT) + /* This was suggested by Richard Henderson as the way to */ + /* force callee-save registers and register windows onto */ + /* the stack. */ + __builtin_unwind_init(); +# elif defined(NO_CRT) && defined(MSWIN32) + CONTEXT ctx; + RtlCaptureContext(&ctx); +# else + /* Generic code */ + /* The idea is due to Parag Patel at HP. */ + /* We're not sure whether he would like */ + /* to be acknowledged for it or not. */ + jmp_buf regs; + word * i = (word *)®s; + ptr_t lim = (ptr_t)(®s) + sizeof(regs); + + /* Setjmp doesn't always clear all of the buffer. */ + /* That tends to preserve garbage. Clear it. */ + for (; (word)i < (word)lim; i++) { + *i = 0; + } +# if defined(MSWIN32) || defined(MSWINCE) || defined(UTS4) \ + || defined(OS2) || defined(CX_UX) || defined(__CC_ARM) \ + || defined(LINUX) || defined(EWS4800) || defined(RTEMS) + (void) setjmp(regs); +# else + (void) _setjmp(regs); + /* We don't want to mess with signals. According to */ + /* SUSV3, setjmp() may or may not save signal mask. */ + /* _setjmp won't, but is less portable. */ +# endif +# endif /* !HAVE_BUILTIN_UNWIND_INIT */ + } +# endif /* !HAVE_PUSH_REGS */ + /* FIXME: context here is sometimes just zero. At the moment the */ + /* callees don't really need it. */ + fn(arg, context); + /* Strongly discourage the compiler from treating the above */ + /* as a tail-call, since that would pop the register */ + /* contents before we get a chance to look at them. */ + GC_noop1((word)(&dummy)); +} + +#endif /* !SN_TARGET_ORBIS && !SN_TARGET_PSP2 */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/malloc.c b/unity-2019.4.24f1-mbe/external/bdwgc/malloc.c new file mode 100644 index 000000000..ea97f1a8f --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/malloc.c @@ -0,0 +1,703 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1999-2004 Hewlett-Packard Development Company, L.P. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#include "private/gc_priv.h" +#include "gc_inline.h" /* for GC_malloc_kind */ + +#include +#include + +/* Allocate reclaim list for kind: */ +/* Return TRUE on success */ +STATIC GC_bool GC_alloc_reclaim_list(struct obj_kind *kind) +{ + struct hblk ** result = (struct hblk **) + GC_scratch_alloc((MAXOBJGRANULES+1) * sizeof(struct hblk *)); + if (result == 0) return(FALSE); + BZERO(result, (MAXOBJGRANULES+1)*sizeof(struct hblk *)); + kind -> ok_reclaim_list = result; + return(TRUE); +} + +/* Allocate a large block of size lb bytes. */ +/* The block is not cleared. */ +/* Flags is 0 or IGNORE_OFF_PAGE. */ +/* EXTRA_BYTES were already added to lb. */ +GC_INNER ptr_t GC_alloc_large(size_t lb, int k, unsigned flags) +{ + struct hblk * h; + word n_blocks; + ptr_t result; + GC_bool retry = FALSE; + + GC_ASSERT(I_HOLD_LOCK()); + lb = ROUNDUP_GRANULE_SIZE(lb); + n_blocks = OBJ_SZ_TO_BLOCKS_CHECKED(lb); + if (!EXPECT(GC_is_initialized, TRUE)) { + DCL_LOCK_STATE; + UNLOCK(); /* just to unset GC_lock_holder */ + GC_init(); + LOCK(); + } + /* Do our share of marking work */ + if (GC_incremental && !GC_dont_gc) + GC_collect_a_little_inner((int)n_blocks); + h = GC_allochblk(lb, k, flags); +# ifdef USE_MUNMAP + if (0 == h) { + GC_merge_unmapped(); + h = GC_allochblk(lb, k, flags); + } +# endif + while (0 == h && GC_collect_or_expand(n_blocks, flags != 0, retry)) { + h = GC_allochblk(lb, k, flags); + retry = TRUE; + } + if (h == 0) { + result = 0; + } else { + size_t total_bytes = n_blocks * HBLKSIZE; + if (n_blocks > 1) { + GC_large_allocd_bytes += total_bytes; + if (GC_large_allocd_bytes > GC_max_large_allocd_bytes) + GC_max_large_allocd_bytes = GC_large_allocd_bytes; + } + /* FIXME: Do we need some way to reset GC_max_large_allocd_bytes? */ + result = h -> hb_body; + } + return result; +} + +/* Allocate a large block of size lb bytes. Clear if appropriate. */ +/* EXTRA_BYTES were already added to lb. */ +STATIC ptr_t GC_alloc_large_and_clear(size_t lb, int k, unsigned flags) +{ + ptr_t result; + + GC_ASSERT(I_HOLD_LOCK()); + result = GC_alloc_large(lb, k, flags); + if (result != NULL + && (GC_debugging_started || GC_obj_kinds[k].ok_init)) { + word n_blocks = OBJ_SZ_TO_BLOCKS(lb); + + /* Clear the whole block, in case of GC_realloc call. */ + BZERO(result, n_blocks * HBLKSIZE); + } + return result; +} + +/* Fill in additional entries in GC_size_map, including the i-th one. */ +/* Note that a filled in section of the array ending at n always */ +/* has the length of at least n/4. */ +STATIC void GC_extend_size_map(size_t i) +{ + size_t orig_granule_sz = ROUNDED_UP_GRANULES(i); + size_t granule_sz; + size_t byte_sz = GRANULES_TO_BYTES(orig_granule_sz); + /* The size we try to preserve. */ + /* Close to i, unless this would */ + /* introduce too many distinct sizes. */ + size_t smaller_than_i = byte_sz - (byte_sz >> 3); + size_t low_limit; /* The lowest indexed entry we initialize. */ + size_t number_of_objs; + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(0 == GC_size_map[i]); + if (0 == GC_size_map[smaller_than_i]) { + low_limit = byte_sz - (byte_sz >> 2); /* much smaller than i */ + granule_sz = orig_granule_sz; + while (GC_size_map[low_limit] != 0) + low_limit++; + } else { + low_limit = smaller_than_i + 1; + while (GC_size_map[low_limit] != 0) + low_limit++; + + granule_sz = ROUNDED_UP_GRANULES(low_limit); + granule_sz += granule_sz >> 3; + if (granule_sz < orig_granule_sz) + granule_sz = orig_granule_sz; + } + + /* For these larger sizes, we use an even number of granules. */ + /* This makes it easier to, e.g., construct a 16-byte-aligned */ + /* allocator even if GRANULE_BYTES is 8. */ + granule_sz = (granule_sz + 1) & ~1; + if (granule_sz > MAXOBJGRANULES) + granule_sz = MAXOBJGRANULES; + + /* If we can fit the same number of larger objects in a block, do so. */ + number_of_objs = HBLK_GRANULES / granule_sz; + GC_ASSERT(number_of_objs != 0); + granule_sz = (HBLK_GRANULES / number_of_objs) & ~1; + + byte_sz = GRANULES_TO_BYTES(granule_sz) - EXTRA_BYTES; + /* We may need one extra byte; do not always */ + /* fill in GC_size_map[byte_sz]. */ + + for (; low_limit <= byte_sz; low_limit++) + GC_size_map[low_limit] = granule_sz; +} + +/* Allocate lb bytes for an object of kind k. */ +/* Should not be used to directly to allocate objects */ +/* that require special handling on allocation. */ +GC_INNER void * GC_generic_malloc_inner(size_t lb, int k) +{ + void *op; + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(k < MAXOBJKINDS); + if (SMALL_OBJ(lb)) { + struct obj_kind * kind = GC_obj_kinds + k; + size_t lg = GC_size_map[lb]; + void ** opp = &(kind -> ok_freelist[lg]); + + op = *opp; + if (EXPECT(0 == op, FALSE)) { + if (lg == 0) { + if (!EXPECT(GC_is_initialized, TRUE)) { + DCL_LOCK_STATE; + UNLOCK(); /* just to unset GC_lock_holder */ + GC_init(); + LOCK(); + lg = GC_size_map[lb]; + } + if (0 == lg) { + GC_extend_size_map(lb); + lg = GC_size_map[lb]; + GC_ASSERT(lg != 0); + } + /* Retry */ + opp = &(kind -> ok_freelist[lg]); + op = *opp; + } + if (0 == op) { + if (0 == kind -> ok_reclaim_list && + !GC_alloc_reclaim_list(kind)) + return NULL; + op = GC_allocobj(lg, k); + if (0 == op) + return NULL; + } + } + *opp = obj_link(op); + obj_link(op) = 0; + GC_bytes_allocd += GRANULES_TO_BYTES((word)lg); + } else { + op = (ptr_t)GC_alloc_large_and_clear(ADD_SLOP(lb), k, 0); + if (op != NULL) + GC_bytes_allocd += lb; + } + + return op; +} + +#if defined(DBG_HDRS_ALL) || defined(GC_GCJ_SUPPORT) \ + || !defined(GC_NO_FINALIZATION) + /* Allocate a composite object of size n bytes. The caller */ + /* guarantees that pointers past the first page are not relevant. */ + GC_INNER void * GC_generic_malloc_inner_ignore_off_page(size_t lb, int k) + { + word lb_adjusted; + void * op; + + GC_ASSERT(I_HOLD_LOCK()); + if (lb <= HBLKSIZE) + return GC_generic_malloc_inner(lb, k); + GC_ASSERT(k < MAXOBJKINDS); + lb_adjusted = ADD_SLOP(lb); + op = GC_alloc_large_and_clear(lb_adjusted, k, IGNORE_OFF_PAGE); + if (op != NULL) + GC_bytes_allocd += lb_adjusted; + return op; + } +#endif + +#ifdef GC_COLLECT_AT_MALLOC + /* Parameter to force GC at every malloc of size greater or equal to */ + /* the given value. This might be handy during debugging. */ +# if defined(CPPCHECK) + size_t GC_dbg_collect_at_malloc_min_lb = 16*1024; /* e.g. */ +# else + size_t GC_dbg_collect_at_malloc_min_lb = (GC_COLLECT_AT_MALLOC); +# endif +#endif + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_generic_malloc(size_t lb, int k) +{ + void * result; + DCL_LOCK_STATE; + + GC_ASSERT(k < MAXOBJKINDS); + if (EXPECT(GC_have_errors, FALSE)) + GC_print_all_errors(); + GC_INVOKE_FINALIZERS(); + GC_DBG_COLLECT_AT_MALLOC(lb); + if (SMALL_OBJ(lb)) { + LOCK(); + result = GC_generic_malloc_inner(lb, k); + UNLOCK(); + } else { + size_t lg; + size_t lb_rounded; + word n_blocks; + GC_bool init; + + lg = ROUNDED_UP_GRANULES(lb); + lb_rounded = GRANULES_TO_BYTES(lg); + n_blocks = OBJ_SZ_TO_BLOCKS(lb_rounded); + init = GC_obj_kinds[k].ok_init; + LOCK(); + result = (ptr_t)GC_alloc_large(lb_rounded, k, 0); + if (0 != result) { + if (GC_debugging_started) { + BZERO(result, n_blocks * HBLKSIZE); + } else { +# ifdef THREADS + /* Clear any memory that might be used for GC descriptors */ + /* before we release the lock. */ + ((word *)result)[0] = 0; + ((word *)result)[1] = 0; + ((word *)result)[GRANULES_TO_WORDS(lg)-1] = 0; + ((word *)result)[GRANULES_TO_WORDS(lg)-2] = 0; +# endif + } + GC_bytes_allocd += lb_rounded; + } + UNLOCK(); + if (init && !GC_debugging_started && 0 != result) { + BZERO(result, n_blocks * HBLKSIZE); + } + } + if (0 == result) { + return((*GC_get_oom_fn())(lb)); + } else { + return(result); + } +} + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_kind_global(size_t lb, int k) +{ + GC_ASSERT(k < MAXOBJKINDS); + if (SMALL_OBJ(lb)) { + void *op; + void **opp; + size_t lg; + DCL_LOCK_STATE; + + GC_DBG_COLLECT_AT_MALLOC(lb); + LOCK(); + lg = GC_size_map[lb]; + opp = &GC_obj_kinds[k].ok_freelist[lg]; + op = *opp; + if (EXPECT(op != NULL, TRUE)) { + if (k == PTRFREE) { + *opp = obj_link(op); + } else { + GC_ASSERT(0 == obj_link(op) + || ((word)obj_link(op) + <= (word)GC_greatest_plausible_heap_addr + && (word)obj_link(op) + >= (word)GC_least_plausible_heap_addr)); + *opp = obj_link(op); + obj_link(op) = 0; + } + GC_bytes_allocd += GRANULES_TO_BYTES((word)lg); + UNLOCK(); + return op; + } + UNLOCK(); + } + + /* We make the GC_clear_stack() call a tail one, hoping to get more */ + /* of the stack. */ + return GC_clear_stack(GC_generic_malloc(lb, k)); +} + +#if defined(THREADS) && !defined(THREAD_LOCAL_ALLOC) + GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_kind(size_t lb, int k) + { + return GC_malloc_kind_global(lb, k); + } +#endif + +#if !IL2CPP_ENABLE_WRITE_BARRIER_VALIDATION +/* Allocate lb bytes of atomic (pointer-free) data. */ +GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_atomic(size_t lb) +{ + return GC_malloc_kind(lb, PTRFREE); +} + +/* Allocate lb bytes of composite (pointerful) data. */ +GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc(size_t lb) +{ + return GC_malloc_kind(lb, NORMAL); +} +#endif + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_generic_malloc_uncollectable( + size_t lb, int k) +{ + void *op; + DCL_LOCK_STATE; + + GC_ASSERT(k < MAXOBJKINDS); + if (SMALL_OBJ(lb)) { + void **opp; + size_t lg; + + GC_DBG_COLLECT_AT_MALLOC(lb); + if (EXTRA_BYTES != 0 && lb != 0) lb--; + /* We don't need the extra byte, since this won't be */ + /* collected anyway. */ + LOCK(); + lg = GC_size_map[lb]; + opp = &GC_obj_kinds[k].ok_freelist[lg]; + op = *opp; + if (EXPECT(op != NULL, TRUE)) { + *opp = obj_link(op); + obj_link(op) = 0; + GC_bytes_allocd += GRANULES_TO_BYTES((word)lg); + /* Mark bit was already set on free list. It will be */ + /* cleared only temporarily during a collection, as a */ + /* result of the normal free list mark bit clearing. */ + GC_non_gc_bytes += GRANULES_TO_BYTES((word)lg); + UNLOCK(); + } else { + UNLOCK(); + op = GC_generic_malloc(lb, k); + /* For small objects, the free lists are completely marked. */ + } + GC_ASSERT(0 == op || GC_is_marked(op)); + } else { + hdr * hhdr; + + op = GC_generic_malloc(lb, k); + if (NULL == op) + return NULL; + + GC_ASSERT(((word)op & (HBLKSIZE - 1)) == 0); /* large block */ + hhdr = HDR(op); + /* We don't need the lock here, since we have an undisguised */ + /* pointer. We do need to hold the lock while we adjust */ + /* mark bits. */ + LOCK(); + set_mark_bit_from_hdr(hhdr, 0); /* Only object. */ +# ifndef THREADS + GC_ASSERT(hhdr -> hb_n_marks == 0); + /* This is not guaranteed in the multi-threaded case */ + /* because the counter could be updated before locking. */ +# endif + hhdr -> hb_n_marks = 1; + UNLOCK(); + } + return op; +} + +#if !IL2CPP_ENABLE_WRITE_BARRIER_VALIDATION +/* Allocate lb bytes of pointerful, traced, but not collectible data. */ +GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_uncollectable(size_t lb) +{ + return GC_generic_malloc_uncollectable(lb, UNCOLLECTABLE); +} +#endif + +#ifdef GC_ATOMIC_UNCOLLECTABLE + /* Allocate lb bytes of pointer-free, untraced, uncollectible data */ + /* This is normally roughly equivalent to the system malloc. */ + /* But it may be useful if malloc is redefined. */ + GC_API GC_ATTR_MALLOC void * GC_CALL + GC_malloc_atomic_uncollectable(size_t lb) + { + return GC_generic_malloc_uncollectable(lb, AUNCOLLECTABLE); + } +#endif /* GC_ATOMIC_UNCOLLECTABLE */ + +#if defined(REDIRECT_MALLOC) && !defined(REDIRECT_MALLOC_IN_HEADER) + +# ifndef MSWINCE +# include +# endif + + /* Avoid unnecessary nested procedure calls here, by #defining some */ + /* malloc replacements. Otherwise we end up saving a meaningless */ + /* return address in the object. It also speeds things up, but it is */ + /* admittedly quite ugly. */ +# define GC_debug_malloc_replacement(lb) GC_debug_malloc(lb, GC_DBG_EXTRAS) + +# if defined(CPPCHECK) +# define REDIRECT_MALLOC_F GC_malloc /* e.g. */ +# else +# define REDIRECT_MALLOC_F REDIRECT_MALLOC +# endif + + void * malloc(size_t lb) + { + /* It might help to manually inline the GC_malloc call here. */ + /* But any decent compiler should reduce the extra procedure call */ + /* to at most a jump instruction in this case. */ +# if defined(I386) && defined(GC_SOLARIS_THREADS) + /* Thread initialization can call malloc before we are ready for. */ + /* It is not clear that this is enough to help matters. */ + /* The thread implementation may well call malloc at other */ + /* inopportune times. */ + if (!EXPECT(GC_is_initialized, TRUE)) return sbrk(lb); +# endif + return (void *)REDIRECT_MALLOC_F(lb); + } + +# if defined(GC_LINUX_THREADS) + STATIC ptr_t GC_libpthread_start = 0; + STATIC ptr_t GC_libpthread_end = 0; + STATIC ptr_t GC_libld_start = 0; + STATIC ptr_t GC_libld_end = 0; + + STATIC void GC_init_lib_bounds(void) + { + IF_CANCEL(int cancel_state;) + + if (GC_libpthread_start != 0) return; + DISABLE_CANCEL(cancel_state); + GC_init(); /* if not called yet */ + if (!GC_text_mapping("libpthread-", + &GC_libpthread_start, &GC_libpthread_end)) { + WARN("Failed to find libpthread.so text mapping: Expect crash\n", 0); + /* This might still work with some versions of libpthread, */ + /* so we don't abort. Perhaps we should. */ + /* Generate message only once: */ + GC_libpthread_start = (ptr_t)1; + } + if (!GC_text_mapping("ld-", &GC_libld_start, &GC_libld_end)) { + WARN("Failed to find ld.so text mapping: Expect crash\n", 0); + } + RESTORE_CANCEL(cancel_state); + } +# endif /* GC_LINUX_THREADS */ + + void * calloc(size_t n, size_t lb) + { + if ((lb | n) > GC_SQRT_SIZE_MAX /* fast initial test */ + && lb && n > GC_SIZE_MAX / lb) + return (*GC_get_oom_fn())(GC_SIZE_MAX); /* n*lb overflow */ +# if defined(GC_LINUX_THREADS) + /* libpthread allocated some memory that is only pointed to by */ + /* mmapped thread stacks. Make sure it is not collectible. */ + { + static GC_bool lib_bounds_set = FALSE; + ptr_t caller = (ptr_t)__builtin_return_address(0); + /* This test does not need to ensure memory visibility, since */ + /* the bounds will be set when/if we create another thread. */ + if (!EXPECT(lib_bounds_set, TRUE)) { + GC_init_lib_bounds(); + lib_bounds_set = TRUE; + } + if (((word)caller >= (word)GC_libpthread_start + && (word)caller < (word)GC_libpthread_end) + || ((word)caller >= (word)GC_libld_start + && (word)caller < (word)GC_libld_end)) + return GC_generic_malloc_uncollectable(n * lb, UNCOLLECTABLE); + /* The two ranges are actually usually adjacent, so there may */ + /* be a way to speed this up. */ + } +# endif + return (void *)REDIRECT_MALLOC_F(n * lb); + } + +# ifndef strdup + char *strdup(const char *s) + { + size_t lb = strlen(s) + 1; + char *result = (char *)REDIRECT_MALLOC_F(lb); + if (result == 0) { + errno = ENOMEM; + return 0; + } + BCOPY(s, result, lb); + return result; + } +# endif /* !defined(strdup) */ + /* If strdup is macro defined, we assume that it actually calls malloc, */ + /* and thus the right thing will happen even without overriding it. */ + /* This seems to be true on most Linux systems. */ + +# ifndef strndup + /* This is similar to strdup(). */ + char *strndup(const char *str, size_t size) + { + char *copy; + size_t len = strlen(str); + if (len > size) + len = size; + copy = (char *)REDIRECT_MALLOC_F(len + 1); + if (copy == NULL) { + errno = ENOMEM; + return NULL; + } + if (EXPECT(len > 0, TRUE)) + BCOPY(str, copy, len); + copy[len] = '\0'; + return copy; + } +# endif /* !strndup */ + +# undef GC_debug_malloc_replacement + +#endif /* REDIRECT_MALLOC */ + +#if !IL2CPP_ENABLE_WRITE_BARRIER_VALIDATION +/* Explicitly deallocate an object p. */ +GC_API void GC_CALL GC_free(void * p) +{ + struct hblk *h; + hdr *hhdr; + size_t sz; /* In bytes */ + size_t ngranules; /* sz in granules */ + int knd; + struct obj_kind * ok; + DCL_LOCK_STATE; + + if (p == 0) return; + /* Required by ANSI. It's not my fault ... */ +# ifdef LOG_ALLOCS + GC_log_printf("GC_free(%p) after GC #%lu\n", + p, (unsigned long)GC_gc_no); +# endif + h = HBLKPTR(p); + hhdr = HDR(h); +# if defined(REDIRECT_MALLOC) && \ + ((defined(NEED_CALLINFO) && defined(GC_HAVE_BUILTIN_BACKTRACE)) \ + || defined(GC_SOLARIS_THREADS) || defined(GC_LINUX_THREADS) \ + || defined(MSWIN32)) + /* This might be called indirectly by GC_print_callers to free */ + /* the result of backtrace_symbols. */ + /* For Solaris, we have to redirect malloc calls during */ + /* initialization. For the others, this seems to happen */ + /* implicitly. */ + /* Don't try to deallocate that memory. */ + if (0 == hhdr) return; +# endif + GC_ASSERT(GC_base(p) == p); + sz = (size_t)hhdr->hb_sz; + ngranules = BYTES_TO_GRANULES(sz); + knd = hhdr -> hb_obj_kind; + ok = &GC_obj_kinds[knd]; + if (EXPECT(ngranules <= MAXOBJGRANULES, TRUE)) { + void **flh; + + LOCK(); + GC_bytes_freed += sz; + if (IS_UNCOLLECTABLE(knd)) GC_non_gc_bytes -= sz; + /* Its unnecessary to clear the mark bit. If the */ + /* object is reallocated, it doesn't matter. O.w. the */ + /* collector will do it, since it's on a free list. */ + if (ok -> ok_init && EXPECT(sz > sizeof(word), TRUE)) { + BZERO((word *)p + 1, sz-sizeof(word)); + } + flh = &(ok -> ok_freelist[ngranules]); + obj_link(p) = *flh; + *flh = (ptr_t)p; + UNLOCK(); + } else { + size_t nblocks = OBJ_SZ_TO_BLOCKS(sz); + + LOCK(); + GC_bytes_freed += sz; + if (IS_UNCOLLECTABLE(knd)) GC_non_gc_bytes -= sz; + if (nblocks > 1) { + GC_large_allocd_bytes -= nblocks * HBLKSIZE; + } + GC_freehblk(h); + UNLOCK(); + } +} +#endif + +/* Explicitly deallocate an object p when we already hold lock. */ +/* Only used for internally allocated objects, so we can take some */ +/* shortcuts. */ +#ifdef THREADS + GC_INNER void GC_free_inner(void * p) + { + struct hblk *h; + hdr *hhdr; + size_t sz; /* bytes */ + size_t ngranules; /* sz in granules */ + int knd; + struct obj_kind * ok; + + h = HBLKPTR(p); + hhdr = HDR(h); + knd = hhdr -> hb_obj_kind; + sz = (size_t)hhdr->hb_sz; + ngranules = BYTES_TO_GRANULES(sz); + ok = &GC_obj_kinds[knd]; + if (ngranules <= MAXOBJGRANULES) { + void ** flh; + + GC_bytes_freed += sz; + if (IS_UNCOLLECTABLE(knd)) GC_non_gc_bytes -= sz; + if (ok -> ok_init && EXPECT(sz > sizeof(word), TRUE)) { + BZERO((word *)p + 1, sz-sizeof(word)); + } + flh = &(ok -> ok_freelist[ngranules]); + obj_link(p) = *flh; + *flh = (ptr_t)p; + } else { + size_t nblocks = OBJ_SZ_TO_BLOCKS(sz); + GC_bytes_freed += sz; + if (IS_UNCOLLECTABLE(knd)) GC_non_gc_bytes -= sz; + if (nblocks > 1) { + GC_large_allocd_bytes -= nblocks * HBLKSIZE; + } + GC_freehblk(h); + } + } +#endif /* THREADS */ + +#if defined(REDIRECT_MALLOC) && !defined(REDIRECT_FREE) +# define REDIRECT_FREE GC_free +#endif + +#if defined(REDIRECT_FREE) && !defined(REDIRECT_MALLOC_IN_HEADER) + +# if defined(CPPCHECK) +# define REDIRECT_FREE_F GC_free /* e.g. */ +# else +# define REDIRECT_FREE_F REDIRECT_FREE +# endif + + void free(void * p) + { +# ifndef IGNORE_FREE +# if defined(GC_LINUX_THREADS) && !defined(USE_PROC_FOR_LIBRARIES) + /* Don't bother with initialization checks. If nothing */ + /* has been initialized, the check fails, and that's safe, */ + /* since we have not allocated uncollectible objects neither. */ + ptr_t caller = (ptr_t)__builtin_return_address(0); + /* This test does not need to ensure memory visibility, since */ + /* the bounds will be set when/if we create another thread. */ + if (((word)caller >= (word)GC_libpthread_start + && (word)caller < (word)GC_libpthread_end) + || ((word)caller >= (word)GC_libld_start + && (word)caller < (word)GC_libld_end)) { + GC_free(p); + return; + } +# endif + REDIRECT_FREE_F(p); +# endif + } +#endif /* REDIRECT_FREE */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/mallocx.c b/unity-2019.4.24f1-mbe/external/bdwgc/mallocx.c new file mode 100644 index 000000000..7b5cd5c3f --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/mallocx.c @@ -0,0 +1,617 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 2000 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#include "private/gc_priv.h" +#include "gc_inline.h" /* for GC_malloc_kind */ + +/* + * These are extra allocation routines which are likely to be less + * frequently used than those in malloc.c. They are separate in the + * hope that the .o file will be excluded from statically linked + * executables. We should probably break this up further. + */ + +#include +#include + +#ifdef MSWINCE +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +# endif +# define NOSERVICE +# include +#else +# include +#endif + +/* Some externally visible but unadvertised variables to allow access to */ +/* free lists from inlined allocators without including gc_priv.h */ +/* or introducing dependencies on internal data structure layouts. */ +#include "gc_alloc_ptrs.h" +void ** const GC_objfreelist_ptr = GC_objfreelist; +void ** const GC_aobjfreelist_ptr = GC_aobjfreelist; +void ** const GC_uobjfreelist_ptr = GC_uobjfreelist; +# ifdef GC_ATOMIC_UNCOLLECTABLE + void ** const GC_auobjfreelist_ptr = GC_auobjfreelist; +# endif + +GC_API int GC_CALL GC_get_kind_and_size(const void * p, size_t * psize) +{ + hdr * hhdr = HDR(p); + + if (psize != NULL) { + *psize = (size_t)hhdr->hb_sz; + } + return hhdr -> hb_obj_kind; +} + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_generic_or_special_malloc(size_t lb, + int knd) +{ + switch(knd) { + case PTRFREE: + case NORMAL: + return GC_malloc_kind(lb, knd); + case UNCOLLECTABLE: +# ifdef GC_ATOMIC_UNCOLLECTABLE + case AUNCOLLECTABLE: +# endif + return GC_generic_malloc_uncollectable(lb, knd); + default: + return GC_generic_malloc(lb, knd); + } +} + +/* Change the size of the block pointed to by p to contain at least */ +/* lb bytes. The object may be (and quite likely will be) moved. */ +/* The kind (e.g. atomic) is the same as that of the old. */ +/* Shrinking of large blocks is not implemented well. */ +GC_API void * GC_CALL GC_realloc(void * p, size_t lb) +{ + struct hblk * h; + hdr * hhdr; + void * result; + size_t sz; /* Current size in bytes */ + size_t orig_sz; /* Original sz in bytes */ + int obj_kind; + + if (p == 0) return(GC_malloc(lb)); /* Required by ANSI */ + if (0 == lb) /* and p != NULL */ { +# ifndef IGNORE_FREE + GC_free(p); +# endif + return NULL; + } + h = HBLKPTR(p); + hhdr = HDR(h); + sz = (size_t)hhdr->hb_sz; + obj_kind = hhdr -> hb_obj_kind; + orig_sz = sz; + + if (sz > MAXOBJBYTES) { + /* Round it up to the next whole heap block */ + word descr = GC_obj_kinds[obj_kind].ok_descriptor; + + sz = (sz + HBLKSIZE-1) & ~HBLKMASK; + if (GC_obj_kinds[obj_kind].ok_relocate_descr) + descr += sz; + /* GC_realloc might be changing the block size while */ + /* GC_reclaim_block or GC_clear_hdr_marks is examining it. */ + /* The change to the size field is benign, in that GC_reclaim */ + /* (and GC_clear_hdr_marks) would work correctly with either */ + /* value, since we are not changing the number of objects in */ + /* the block. But seeing a half-updated value (though unlikely */ + /* to occur in practice) could be probably bad. */ + /* Using unordered atomic accesses on the size and hb_descr */ + /* fields would solve the issue. (The alternate solution might */ + /* be to initially overallocate large objects, so we do not */ + /* have to adjust the size in GC_realloc, if they still fit. */ + /* But that is probably more expensive, since we may end up */ + /* scanning a bunch of zeros during GC.) */ +# ifdef AO_HAVE_store + GC_STATIC_ASSERT(sizeof(hhdr->hb_sz) == sizeof(AO_t)); + AO_store((volatile AO_t *)&hhdr->hb_sz, (AO_t)sz); + AO_store((volatile AO_t *)&hhdr->hb_descr, (AO_t)descr); +# else + { + DCL_LOCK_STATE; + + LOCK(); + hhdr -> hb_sz = sz; + hhdr -> hb_descr = descr; + UNLOCK(); + } +# endif + +# ifdef MARK_BIT_PER_OBJ + GC_ASSERT(hhdr -> hb_inv_sz == LARGE_INV_SZ); +# endif +# ifdef MARK_BIT_PER_GRANULE + GC_ASSERT((hhdr -> hb_flags & LARGE_BLOCK) != 0 + && hhdr -> hb_map[ANY_INDEX] == 1); +# endif + if (IS_UNCOLLECTABLE(obj_kind)) GC_non_gc_bytes += (sz - orig_sz); + /* Extra area is already cleared by GC_alloc_large_and_clear. */ + } + if (ADD_SLOP(lb) <= sz) { + if (lb >= (sz >> 1)) { + if (orig_sz > lb) { + /* Clear unneeded part of object to avoid bogus pointer */ + /* tracing. */ + BZERO(((ptr_t)p) + lb, orig_sz - lb); + } + return(p); + } + /* shrink */ + sz = lb; + } + result = GC_generic_or_special_malloc((word)lb, obj_kind); + if (result != NULL) { + /* In case of shrink, it could also return original object. */ + /* But this gives the client warning of imminent disaster. */ + BCOPY(p, result, sz); +# ifndef IGNORE_FREE + GC_free(p); +# endif + } + return result; +} + +# if defined(REDIRECT_MALLOC) && !defined(REDIRECT_REALLOC) +# define REDIRECT_REALLOC GC_realloc +# endif + +# ifdef REDIRECT_REALLOC + +/* As with malloc, avoid two levels of extra calls here. */ +# define GC_debug_realloc_replacement(p, lb) \ + GC_debug_realloc(p, lb, GC_DBG_EXTRAS) + +# if !defined(REDIRECT_MALLOC_IN_HEADER) + void * realloc(void * p, size_t lb) + { + return(REDIRECT_REALLOC(p, lb)); + } +# endif + +# undef GC_debug_realloc_replacement +# endif /* REDIRECT_REALLOC */ + +/* Allocate memory such that only pointers to near the */ +/* beginning of the object are considered. */ +/* We avoid holding allocation lock while we clear the memory. */ +GC_API GC_ATTR_MALLOC void * GC_CALL + GC_generic_malloc_ignore_off_page(size_t lb, int k) +{ + void *result; + size_t lg; + size_t lb_rounded; + word n_blocks; + GC_bool init; + DCL_LOCK_STATE; + + if (SMALL_OBJ(lb)) + return GC_generic_malloc(lb, k); + GC_ASSERT(k < MAXOBJKINDS); + lg = ROUNDED_UP_GRANULES(lb); + lb_rounded = GRANULES_TO_BYTES(lg); + n_blocks = OBJ_SZ_TO_BLOCKS(lb_rounded); + init = GC_obj_kinds[k].ok_init; + if (EXPECT(GC_have_errors, FALSE)) + GC_print_all_errors(); + GC_INVOKE_FINALIZERS(); + GC_DBG_COLLECT_AT_MALLOC(lb); + LOCK(); + result = (ptr_t)GC_alloc_large(ADD_SLOP(lb), k, IGNORE_OFF_PAGE); + if (NULL == result) { + GC_oom_func oom_fn = GC_oom_fn; + UNLOCK(); + return (*oom_fn)(lb); + } + + if (GC_debugging_started) { + BZERO(result, n_blocks * HBLKSIZE); + } else { +# ifdef THREADS + /* Clear any memory that might be used for GC descriptors */ + /* before we release the lock. */ + ((word *)result)[0] = 0; + ((word *)result)[1] = 0; + ((word *)result)[GRANULES_TO_WORDS(lg)-1] = 0; + ((word *)result)[GRANULES_TO_WORDS(lg)-2] = 0; +# endif + } + GC_bytes_allocd += lb_rounded; + UNLOCK(); + if (init && !GC_debugging_started) { + BZERO(result, n_blocks * HBLKSIZE); + } + return(result); +} + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_ignore_off_page(size_t lb) +{ + return GC_generic_malloc_ignore_off_page(lb, NORMAL); +} + +GC_API GC_ATTR_MALLOC void * GC_CALL + GC_malloc_atomic_ignore_off_page(size_t lb) +{ + return GC_generic_malloc_ignore_off_page(lb, PTRFREE); +} + +/* Increment GC_bytes_allocd from code that doesn't have direct access */ +/* to GC_arrays. */ +GC_API void GC_CALL GC_incr_bytes_allocd(size_t n) +{ + GC_bytes_allocd += n; +} + +/* The same for GC_bytes_freed. */ +GC_API void GC_CALL GC_incr_bytes_freed(size_t n) +{ + GC_bytes_freed += n; +} + +GC_API size_t GC_CALL GC_get_expl_freed_bytes_since_gc(void) +{ + return (size_t)GC_bytes_freed; +} + +# ifdef PARALLEL_MARK + STATIC volatile AO_t GC_bytes_allocd_tmp = 0; + /* Number of bytes of memory allocated since */ + /* we released the GC lock. Instead of */ + /* reacquiring the GC lock just to add this in, */ + /* we add it in the next time we reacquire */ + /* the lock. (Atomically adding it doesn't */ + /* work, since we would have to atomically */ + /* update it in GC_malloc, which is too */ + /* expensive.) */ +# endif /* PARALLEL_MARK */ + +/* Return a list of 1 or more objects of the indicated size, linked */ +/* through the first word in the object. This has the advantage that */ +/* it acquires the allocation lock only once, and may greatly reduce */ +/* time wasted contending for the allocation lock. Typical usage would */ +/* be in a thread that requires many items of the same size. It would */ +/* keep its own free list in thread-local storage, and call */ +/* GC_malloc_many or friends to replenish it. (We do not round up */ +/* object sizes, since a call indicates the intention to consume many */ +/* objects of exactly this size.) */ +/* We assume that the size is a multiple of GRANULE_BYTES. */ +/* We return the free-list by assigning it to *result, since it is */ +/* not safe to return, e.g. a linked list of pointer-free objects, */ +/* since the collector would not retain the entire list if it were */ +/* invoked just as we were returning. */ +/* Note that the client should usually clear the link field. */ +GC_API void GC_CALL GC_generic_malloc_many(size_t lb, int k, void **result) +{ + void *op; + void *p; + void **opp; + size_t lw; /* Length in words. */ + size_t lg; /* Length in granules. */ + signed_word my_bytes_allocd = 0; + struct obj_kind * ok = &(GC_obj_kinds[k]); + struct hblk ** rlh; + DCL_LOCK_STATE; + + GC_ASSERT(lb != 0 && (lb & (GRANULE_BYTES-1)) == 0); + if (!SMALL_OBJ(lb)) { + op = GC_generic_malloc(lb, k); + if (EXPECT(0 != op, TRUE)) + obj_link(op) = 0; + *result = op; + return; + } + GC_ASSERT(k < MAXOBJKINDS); + lw = BYTES_TO_WORDS(lb); + lg = BYTES_TO_GRANULES(lb); + if (EXPECT(GC_have_errors, FALSE)) + GC_print_all_errors(); + GC_INVOKE_FINALIZERS(); + GC_DBG_COLLECT_AT_MALLOC(lb); + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); + LOCK(); + /* Do our share of marking work */ + if (GC_incremental && !GC_dont_gc) { + ENTER_GC(); + GC_collect_a_little_inner(1); + EXIT_GC(); + } + /* First see if we can reclaim a page of objects waiting to be */ + /* reclaimed. */ + rlh = ok -> ok_reclaim_list; + if (rlh != NULL) { + struct hblk * hbp; + hdr * hhdr; + + rlh += lg; + while ((hbp = *rlh) != 0) { + hhdr = HDR(hbp); + *rlh = hhdr -> hb_next; + GC_ASSERT(hhdr -> hb_sz == lb); + hhdr -> hb_last_reclaimed = (unsigned short) GC_gc_no; +# ifdef PARALLEL_MARK + if (GC_parallel) { + signed_word my_bytes_allocd_tmp = + (signed_word)AO_load(&GC_bytes_allocd_tmp); + GC_ASSERT(my_bytes_allocd_tmp >= 0); + /* We only decrement it while holding the GC lock. */ + /* Thus we can't accidentally adjust it down in more */ + /* than one thread simultaneously. */ + + if (my_bytes_allocd_tmp != 0) { + (void)AO_fetch_and_add(&GC_bytes_allocd_tmp, + (AO_t)(-my_bytes_allocd_tmp)); + GC_bytes_allocd += my_bytes_allocd_tmp; + } + GC_acquire_mark_lock(); + ++ GC_fl_builder_count; + UNLOCK(); + GC_release_mark_lock(); + } +# endif + op = GC_reclaim_generic(hbp, hhdr, lb, + ok -> ok_init, 0, &my_bytes_allocd); + if (op != 0) { +# ifdef PARALLEL_MARK + if (GC_parallel) { + *result = op; + (void)AO_fetch_and_add(&GC_bytes_allocd_tmp, + (AO_t)my_bytes_allocd); + GC_acquire_mark_lock(); + -- GC_fl_builder_count; + if (GC_fl_builder_count == 0) GC_notify_all_builder(); +# ifdef THREAD_SANITIZER + GC_release_mark_lock(); + LOCK(); + GC_bytes_found += my_bytes_allocd; + UNLOCK(); +# else + GC_bytes_found += my_bytes_allocd; + /* The result may be inaccurate. */ + GC_release_mark_lock(); +# endif + (void) GC_clear_stack(0); + return; + } +# endif + /* We also reclaimed memory, so we need to adjust */ + /* that count. */ + GC_bytes_found += my_bytes_allocd; + GC_bytes_allocd += my_bytes_allocd; + goto out; + } +# ifdef PARALLEL_MARK + if (GC_parallel) { + GC_acquire_mark_lock(); + -- GC_fl_builder_count; + if (GC_fl_builder_count == 0) GC_notify_all_builder(); + GC_release_mark_lock(); + LOCK(); + /* GC lock is needed for reclaim list access. We */ + /* must decrement fl_builder_count before reacquiring */ + /* the lock. Hopefully this path is rare. */ + } +# endif + } + } + /* Next try to use prefix of global free list if there is one. */ + /* We don't refill it, but we need to use it up before allocating */ + /* a new block ourselves. */ + opp = &(GC_obj_kinds[k].ok_freelist[lg]); + if ( (op = *opp) != 0 ) { + *opp = 0; + my_bytes_allocd = 0; + for (p = op; p != 0; p = obj_link(p)) { + my_bytes_allocd += lb; + if ((word)my_bytes_allocd >= HBLKSIZE) { + *opp = obj_link(p); + obj_link(p) = 0; + break; + } + } + GC_bytes_allocd += my_bytes_allocd; + goto out; + } + /* Next try to allocate a new block worth of objects of this size. */ + { + struct hblk *h = GC_allochblk(lb, k, 0); + if (h != 0) { + if (IS_UNCOLLECTABLE(k)) GC_set_hdr_marks(HDR(h)); + GC_bytes_allocd += HBLKSIZE - HBLKSIZE % lb; +# ifdef PARALLEL_MARK + if (GC_parallel) { + GC_acquire_mark_lock(); + ++ GC_fl_builder_count; + UNLOCK(); + GC_release_mark_lock(); + + op = GC_build_fl(h, lw, + (ok -> ok_init || GC_debugging_started), 0); + + *result = op; + GC_acquire_mark_lock(); + -- GC_fl_builder_count; + if (GC_fl_builder_count == 0) GC_notify_all_builder(); + GC_release_mark_lock(); + (void) GC_clear_stack(0); + return; + } +# endif + op = GC_build_fl(h, lw, (ok -> ok_init || GC_debugging_started), 0); + goto out; + } + } + + /* As a last attempt, try allocating a single object. Note that */ + /* this may trigger a collection or expand the heap. */ + op = GC_generic_malloc_inner(lb, k); + if (0 != op) obj_link(op) = 0; + + out: + *result = op; + UNLOCK(); + (void) GC_clear_stack(0); +} + +/* Note that the "atomic" version of this would be unsafe, since the */ +/* links would not be seen by the collector. */ +GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_many(size_t lb) +{ + void *result; + + /* Add EXTRA_BYTES and round up to a multiple of a granule. */ + lb = SIZET_SAT_ADD(lb, EXTRA_BYTES + GRANULE_BYTES - 1) + & ~(GRANULE_BYTES - 1); + + GC_generic_malloc_many(lb, NORMAL, &result); + return result; +} + +#include + +/* Debug version is tricky and currently missing. */ +GC_API GC_ATTR_MALLOC void * GC_CALL GC_memalign(size_t align, size_t lb) +{ + size_t new_lb; + size_t offset; + ptr_t result; + + if (align <= GRANULE_BYTES) return GC_malloc(lb); + if (align >= HBLKSIZE/2 || lb >= HBLKSIZE/2) { + if (align > HBLKSIZE) { + return (*GC_get_oom_fn())(LONG_MAX-1024); /* Fail */ + } + return GC_malloc(lb <= HBLKSIZE? HBLKSIZE : lb); + /* Will be HBLKSIZE aligned. */ + } + /* We could also try to make sure that the real rounded-up object size */ + /* is a multiple of align. That would be correct up to HBLKSIZE. */ + new_lb = SIZET_SAT_ADD(lb, align - 1); + result = (ptr_t)GC_malloc(new_lb); + /* It is OK not to check result for NULL as in that case */ + /* GC_memalign returns NULL too since (0 + 0 % align) is 0. */ + offset = (word)result % align; + if (offset != 0) { + offset = align - offset; + if (!GC_all_interior_pointers) { + GC_STATIC_ASSERT(VALID_OFFSET_SZ <= HBLKSIZE); + GC_ASSERT(offset < VALID_OFFSET_SZ); + GC_register_displacement(offset); + } + } + result += offset; + GC_ASSERT((word)result % align == 0); + return result; +} + +/* This one exists largely to redirect posix_memalign for leaks finding. */ +GC_API int GC_CALL GC_posix_memalign(void **memptr, size_t align, size_t lb) +{ + /* Check alignment properly. */ + size_t align_minus_one = align - 1; /* to workaround a cppcheck warning */ + if (align < sizeof(void *) || (align_minus_one & align) != 0) { +# ifdef MSWINCE + return ERROR_INVALID_PARAMETER; +# else + return EINVAL; +# endif + } + + if ((*memptr = GC_memalign(align, lb)) == NULL) { +# ifdef MSWINCE + return ERROR_NOT_ENOUGH_MEMORY; +# else + return ENOMEM; +# endif + } + return 0; +} + +/* provide a version of strdup() that uses the collector to allocate the + copy of the string */ +GC_API GC_ATTR_MALLOC char * GC_CALL GC_strdup(const char *s) +{ + char *copy; + size_t lb; + if (s == NULL) return NULL; + lb = strlen(s) + 1; + copy = (char *)GC_malloc_atomic(lb); + if (NULL == copy) { +# ifndef MSWINCE + errno = ENOMEM; +# endif + return NULL; + } + BCOPY(s, copy, lb); + return copy; +} + +GC_API GC_ATTR_MALLOC char * GC_CALL GC_strndup(const char *str, size_t size) +{ + char *copy; + size_t len = strlen(str); /* str is expected to be non-NULL */ + if (len > size) + len = size; + copy = (char *)GC_malloc_atomic(len + 1); + if (copy == NULL) { +# ifndef MSWINCE + errno = ENOMEM; +# endif + return NULL; + } + if (EXPECT(len > 0, TRUE)) + BCOPY(str, copy, len); + copy[len] = '\0'; + return copy; +} + +#ifdef GC_REQUIRE_WCSDUP +# include /* for wcslen() */ + + GC_API GC_ATTR_MALLOC wchar_t * GC_CALL GC_wcsdup(const wchar_t *str) + { + size_t lb = (wcslen(str) + 1) * sizeof(wchar_t); + wchar_t *copy = (wchar_t *)GC_malloc_atomic(lb); + + if (copy == NULL) { +# ifndef MSWINCE + errno = ENOMEM; +# endif + return NULL; + } + BCOPY(str, copy, lb); + return copy; + } +#endif /* GC_REQUIRE_WCSDUP */ + +GC_API void * GC_CALL GC_malloc_stubborn(size_t lb) +{ + return GC_malloc(lb); +} + +GC_API void GC_CALL GC_change_stubborn(const void *p GC_ATTR_UNUSED) +{ + /* Empty. */ +} + +GC_API void GC_CALL GC_end_stubborn_change(const void *p) +{ + GC_dirty(p); /* entire object */ +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/mark.c b/unity-2019.4.24f1-mbe/external/bdwgc/mark.c new file mode 100644 index 000000000..06f95ab36 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/mark.c @@ -0,0 +1,2046 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. + * Copyright (c) 2000 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + */ + +#if defined(__MINGW32__) && !defined(__MINGW_EXCPT_DEFINE_PSDK) \ + && defined(__i386__) /* cannot use macros from gcconfig.h */ + /* Otherwise EXCEPTION_REGISTRATION type declaration from winnt.h */ + /* might be used. That declaration has "handler" callback with NTAPI */ + /* attribute. The proper type (with "handler" field compatible with */ + /* GC mark_ex_handler) is declared in excpt.h. The given macro is */ + /* defined before any system header include. */ +# define __MINGW_EXCPT_DEFINE_PSDK 1 +#endif + +#include "private/gc_pmark.h" + +#include + +#if defined(MSWIN32) && defined(__GNUC__) +# include +#endif + +/* Make arguments appear live to compiler. Put here to minimize the */ +/* risk of inlining. Used to minimize junk left in registers. */ +GC_ATTR_NOINLINE +void GC_noop6(word arg1 GC_ATTR_UNUSED, word arg2 GC_ATTR_UNUSED, + word arg3 GC_ATTR_UNUSED, word arg4 GC_ATTR_UNUSED, + word arg5 GC_ATTR_UNUSED, word arg6 GC_ATTR_UNUSED) +{ + /* Avoid GC_noop6 calls to be optimized away. */ +# ifdef AO_CLEAR + AO_compiler_barrier(); /* to serve as a special side-effect */ +# else + GC_noop1(0); +# endif +} + +volatile word GC_noop_sink; + +/* Single argument version, robust against whole program analysis. */ +GC_ATTR_NO_SANITIZE_THREAD +GC_API void GC_CALL GC_noop1(word x) +{ + GC_noop_sink = x; +} + +/* mark_proc GC_mark_procs[MAX_MARK_PROCS] = {0} -- declared in gc_priv.h */ + +GC_INNER unsigned GC_n_mark_procs = GC_RESERVED_MARK_PROCS; + +/* Initialize GC_obj_kinds properly and standard free lists properly. */ +/* This must be done statically since they may be accessed before */ +/* GC_init is called. */ +/* It's done here, since we need to deal with mark descriptors. */ +GC_INNER struct obj_kind GC_obj_kinds[MAXOBJKINDS] = { +/* PTRFREE */ { &GC_aobjfreelist[0], 0 /* filled in dynamically */, + /* 0 | */ GC_DS_LENGTH, FALSE, FALSE + /*, */ OK_DISCLAIM_INITZ }, +/* NORMAL */ { &GC_objfreelist[0], 0, + /* 0 | */ GC_DS_LENGTH, + /* adjusted in GC_init for EXTRA_BYTES */ + TRUE /* add length to descr */, TRUE + /*, */ OK_DISCLAIM_INITZ }, +/* UNCOLLECTABLE */ + { &GC_uobjfreelist[0], 0, + /* 0 | */ GC_DS_LENGTH, TRUE /* add length to descr */, TRUE + /*, */ OK_DISCLAIM_INITZ }, +# ifdef GC_ATOMIC_UNCOLLECTABLE + { &GC_auobjfreelist[0], 0, + /* 0 | */ GC_DS_LENGTH, FALSE /* add length to descr */, FALSE + /*, */ OK_DISCLAIM_INITZ }, +# endif +}; + +GC_INNER unsigned GC_n_kinds = GC_N_KINDS_INITIAL_VALUE; + +# ifndef INITIAL_MARK_STACK_SIZE +# define INITIAL_MARK_STACK_SIZE (1*HBLKSIZE) + /* INITIAL_MARK_STACK_SIZE * sizeof(mse) should be a */ + /* multiple of HBLKSIZE. */ + /* The incremental collector actually likes a larger */ + /* size, since it wants to push all marked dirty */ + /* objects before marking anything new. Currently we */ + /* let it grow dynamically. */ +# endif + +#if !defined(GC_DISABLE_INCREMENTAL) + STATIC word GC_n_rescuing_pages = 0; + /* Number of dirty pages we marked from */ + /* excludes ptrfree pages, etc. */ + /* Used for logging only. */ +#endif + +GC_INNER size_t GC_mark_stack_size = 0; + +#ifdef PARALLEL_MARK + STATIC volatile AO_t GC_first_nonempty = 0; + /* Lowest entry on mark stack */ + /* that may be nonempty. */ + /* Updated only by initiating */ + /* thread. */ +#endif + +GC_INNER mark_state_t GC_mark_state = MS_NONE; + +GC_INNER GC_bool GC_mark_stack_too_small = FALSE; + +static struct hblk * scan_ptr; + +STATIC GC_bool GC_objects_are_marked = FALSE; + /* Are there collectible marked objects in the heap? */ + +/* Is a collection in progress? Note that this can return true in the */ +/* nonincremental case, if a collection has been abandoned and the */ +/* mark state is now MS_INVALID. */ +GC_INNER GC_bool GC_collection_in_progress(void) +{ + return(GC_mark_state != MS_NONE); +} + +/* clear all mark bits in the header */ +GC_INNER void GC_clear_hdr_marks(hdr *hhdr) +{ + size_t last_bit; + +# ifdef AO_HAVE_load + /* Atomic access is used to avoid racing with GC_realloc. */ + last_bit = FINAL_MARK_BIT((size_t)AO_load((volatile AO_t *)&hhdr->hb_sz)); +# else + /* No race as GC_realloc holds the lock while updating hb_sz. */ + last_bit = FINAL_MARK_BIT((size_t)hhdr->hb_sz); +# endif + + BZERO(hhdr -> hb_marks, sizeof(hhdr->hb_marks)); + set_mark_bit_from_hdr(hhdr, last_bit); + hhdr -> hb_n_marks = 0; +} + +/* Set all mark bits in the header. Used for uncollectible blocks. */ +GC_INNER void GC_set_hdr_marks(hdr *hhdr) +{ + unsigned i; + size_t sz = (size_t)hhdr->hb_sz; + unsigned n_marks = (unsigned)FINAL_MARK_BIT(sz); + +# ifdef USE_MARK_BYTES + for (i = 0; i <= n_marks; i += (unsigned)MARK_BIT_OFFSET(sz)) { + hhdr -> hb_marks[i] = 1; + } +# else + for (i = 0; i < divWORDSZ(n_marks + WORDSZ); ++i) { + hhdr -> hb_marks[i] = ONES; + } +# endif +# ifdef MARK_BIT_PER_OBJ + hhdr -> hb_n_marks = n_marks; +# else + hhdr -> hb_n_marks = HBLK_OBJS(sz); +# endif +} + +/* + * Clear all mark bits associated with block h. + */ +static void clear_marks_for_block(struct hblk *h, word dummy GC_ATTR_UNUSED) +{ + hdr * hhdr = HDR(h); + + if (IS_UNCOLLECTABLE(hhdr -> hb_obj_kind)) return; + /* Mark bit for these is cleared only once the object is */ + /* explicitly deallocated. This either frees the block, or */ + /* the bit is cleared once the object is on the free list. */ + GC_clear_hdr_marks(hhdr); +} + +/* Slow but general routines for setting/clearing/asking about mark bits */ +GC_API void GC_CALL GC_set_mark_bit(const void *p) +{ + struct hblk *h = HBLKPTR(p); + hdr * hhdr = HDR(h); + word bit_no = MARK_BIT_NO((ptr_t)p - (ptr_t)h, hhdr -> hb_sz); + + if (!mark_bit_from_hdr(hhdr, bit_no)) { + set_mark_bit_from_hdr(hhdr, bit_no); + ++hhdr -> hb_n_marks; + } +} + +GC_API void GC_CALL GC_clear_mark_bit(const void *p) +{ + struct hblk *h = HBLKPTR(p); + hdr * hhdr = HDR(h); + word bit_no = MARK_BIT_NO((ptr_t)p - (ptr_t)h, hhdr -> hb_sz); + + if (mark_bit_from_hdr(hhdr, bit_no)) { + size_t n_marks = hhdr -> hb_n_marks; + + GC_ASSERT(n_marks != 0); + clear_mark_bit_from_hdr(hhdr, bit_no); + n_marks--; +# ifdef PARALLEL_MARK + if (n_marks != 0 || !GC_parallel) + hhdr -> hb_n_marks = n_marks; + /* Don't decrement to zero. The counts are approximate due to */ + /* concurrency issues, but we need to ensure that a count of */ + /* zero implies an empty block. */ +# else + hhdr -> hb_n_marks = n_marks; +# endif + } +} + +GC_API int GC_CALL GC_is_marked(const void *p) +{ + struct hblk *h = HBLKPTR(p); + hdr * hhdr = HDR(h); + word bit_no = MARK_BIT_NO((ptr_t)p - (ptr_t)h, hhdr -> hb_sz); + + return (int)mark_bit_from_hdr(hhdr, bit_no); /* 0 or 1 */ +} + +/* + * Clear mark bits in all allocated heap blocks. This invalidates + * the marker invariant, and sets GC_mark_state to reflect this. + * (This implicitly starts marking to reestablish the invariant.) + */ +GC_INNER void GC_clear_marks(void) +{ + GC_apply_to_all_blocks(clear_marks_for_block, (word)0); + GC_objects_are_marked = FALSE; + GC_mark_state = MS_INVALID; + scan_ptr = 0; +} + +/* Initiate a garbage collection. Initiates a full collection if the */ +/* mark state is invalid. */ +GC_INNER void GC_initiate_gc(void) +{ + GC_ASSERT(I_HOLD_LOCK()); +# ifndef GC_DISABLE_INCREMENTAL + if (GC_incremental) { +# ifdef CHECKSUMS + GC_read_dirty(FALSE); +# else + GC_read_dirty(GC_mark_state == MS_INVALID); +# endif + } +# endif +# ifdef CHECKSUMS + if (GC_incremental) GC_check_dirty(); +# endif +# if !defined(GC_DISABLE_INCREMENTAL) + GC_n_rescuing_pages = 0; +# endif + if (GC_mark_state == MS_NONE) { + GC_mark_state = MS_PUSH_RESCUERS; + } else if (GC_mark_state != MS_INVALID) { + ABORT("Unexpected state"); + } /* else this is really a full collection, and mark */ + /* bits are invalid. */ + scan_ptr = 0; +} + +#ifdef PARALLEL_MARK + STATIC void GC_do_parallel_mark(void); /* initiate parallel marking. */ +#endif /* PARALLEL_MARK */ + +#ifdef GC_DISABLE_INCREMENTAL +# define GC_push_next_marked_dirty(h) GC_push_next_marked(h) +#else + STATIC struct hblk * GC_push_next_marked_dirty(struct hblk *h); + /* Invoke GC_push_marked on next dirty block above h. */ + /* Return a pointer just past the end of this block. */ +#endif /* !GC_DISABLE_INCREMENTAL */ +STATIC struct hblk * GC_push_next_marked(struct hblk *h); + /* Ditto, but also mark from clean pages. */ +STATIC struct hblk * GC_push_next_marked_uncollectable(struct hblk *h); + /* Ditto, but mark only from uncollectible pages. */ + +static void alloc_mark_stack(size_t); + +/* Perform a small amount of marking. */ +/* We try to touch roughly a page of memory. */ +/* Return TRUE if we just finished a mark phase. */ +/* Cold_gc_frame is an address inside a GC frame that */ +/* remains valid until all marking is complete. */ +/* A zero value indicates that it's OK to miss some */ +/* register values. */ +/* We hold the allocation lock. In the case of */ +/* incremental collection, the world may not be stopped.*/ +#ifdef WRAP_MARK_SOME + /* For win32, this is called after we establish a structured */ + /* exception handler, in case Windows unmaps one of our root */ + /* segments. See below. In either case, we acquire the */ + /* allocator lock long before we get here. */ + STATIC GC_bool GC_mark_some_inner(ptr_t cold_gc_frame) +#else + GC_INNER GC_bool GC_mark_some(ptr_t cold_gc_frame) +#endif +{ + switch(GC_mark_state) { + case MS_NONE: + break; + + case MS_PUSH_RESCUERS: + if ((word)GC_mark_stack_top + >= (word)(GC_mark_stack_limit - INITIAL_MARK_STACK_SIZE/2)) { + /* Go ahead and mark, even though that might cause us to */ + /* see more marked dirty objects later on. Avoid this */ + /* in the future. */ + GC_mark_stack_too_small = TRUE; + MARK_FROM_MARK_STACK(); + break; + } else { + scan_ptr = GC_push_next_marked_dirty(scan_ptr); + if (scan_ptr == 0) { +# if !defined(GC_DISABLE_INCREMENTAL) + GC_COND_LOG_PRINTF("Marked from %lu dirty pages\n", + (unsigned long)GC_n_rescuing_pages); +# endif + GC_push_roots(FALSE, cold_gc_frame); + GC_objects_are_marked = TRUE; + if (GC_mark_state != MS_INVALID) { + GC_mark_state = MS_ROOTS_PUSHED; + } + } + } + break; + + case MS_PUSH_UNCOLLECTABLE: + if ((word)GC_mark_stack_top + >= (word)(GC_mark_stack + GC_mark_stack_size/4)) { +# ifdef PARALLEL_MARK + /* Avoid this, since we don't parallelize the marker */ + /* here. */ + if (GC_parallel) GC_mark_stack_too_small = TRUE; +# endif + MARK_FROM_MARK_STACK(); + break; + } else { + scan_ptr = GC_push_next_marked_uncollectable(scan_ptr); + if (scan_ptr == 0) { + GC_push_roots(TRUE, cold_gc_frame); + GC_objects_are_marked = TRUE; + if (GC_mark_state != MS_INVALID) { + GC_mark_state = MS_ROOTS_PUSHED; + } + } + } + break; + + case MS_ROOTS_PUSHED: +# ifdef PARALLEL_MARK + /* In the incremental GC case, this currently doesn't */ + /* quite do the right thing, since it runs to */ + /* completion. On the other hand, starting a */ + /* parallel marker is expensive, so perhaps it is */ + /* the right thing? */ + /* Eventually, incremental marking should run */ + /* asynchronously in multiple threads, without grabbing */ + /* the allocation lock. */ + if (GC_parallel) { + GC_do_parallel_mark(); + GC_ASSERT((word)GC_mark_stack_top < (word)GC_first_nonempty); + GC_mark_stack_top = GC_mark_stack - 1; + if (GC_mark_stack_too_small) { + alloc_mark_stack(2*GC_mark_stack_size); + } + if (GC_mark_state == MS_ROOTS_PUSHED) { + GC_mark_state = MS_NONE; + return(TRUE); + } + break; + } +# endif + if ((word)GC_mark_stack_top >= (word)GC_mark_stack) { + MARK_FROM_MARK_STACK(); + break; + } else { + GC_mark_state = MS_NONE; + if (GC_mark_stack_too_small) { + alloc_mark_stack(2*GC_mark_stack_size); + } + return(TRUE); + } + + case MS_INVALID: + case MS_PARTIALLY_INVALID: + if (!GC_objects_are_marked) { + GC_mark_state = MS_PUSH_UNCOLLECTABLE; + break; + } + if ((word)GC_mark_stack_top >= (word)GC_mark_stack) { + MARK_FROM_MARK_STACK(); + break; + } + if (scan_ptr == 0 && GC_mark_state == MS_INVALID) { + /* About to start a heap scan for marked objects. */ + /* Mark stack is empty. OK to reallocate. */ + if (GC_mark_stack_too_small) { + alloc_mark_stack(2*GC_mark_stack_size); + } + GC_mark_state = MS_PARTIALLY_INVALID; + } + scan_ptr = GC_push_next_marked(scan_ptr); + if (scan_ptr == 0 && GC_mark_state == MS_PARTIALLY_INVALID) { + GC_push_roots(TRUE, cold_gc_frame); + GC_objects_are_marked = TRUE; + if (GC_mark_state != MS_INVALID) { + GC_mark_state = MS_ROOTS_PUSHED; + } + } + break; + + default: + ABORT("GC_mark_some: bad state"); + } + return(FALSE); +} + +#ifdef WRAP_MARK_SOME + +# if (defined(MSWIN32) || defined(MSWINCE)) && defined(__GNUC__) + + typedef struct { + EXCEPTION_REGISTRATION ex_reg; + void *alt_path; + } ext_ex_regn; + + static EXCEPTION_DISPOSITION mark_ex_handler( + struct _EXCEPTION_RECORD *ex_rec, + void *est_frame, + struct _CONTEXT *context, + void *disp_ctxt GC_ATTR_UNUSED) + { + if (ex_rec->ExceptionCode == STATUS_ACCESS_VIOLATION) { + ext_ex_regn *xer = (ext_ex_regn *)est_frame; + + /* Unwind from the inner function assuming the standard */ + /* function prologue. */ + /* Assumes code has not been compiled with */ + /* -fomit-frame-pointer. */ + context->Esp = context->Ebp; + context->Ebp = *((DWORD *)context->Esp); + context->Esp = context->Esp - 8; + + /* Resume execution at the "real" handler within the */ + /* wrapper function. */ + context->Eip = (DWORD )(xer->alt_path); + + return ExceptionContinueExecution; + + } else { + return ExceptionContinueSearch; + } + } +# endif /* __GNUC__ && MSWIN32 */ + + GC_INNER GC_bool GC_mark_some(ptr_t cold_gc_frame) + { + GC_bool ret_val; + +# if defined(MSWIN32) || defined(MSWINCE) +# ifndef __GNUC__ + /* Windows 98 appears to asynchronously create and remove */ + /* writable memory mappings, for reasons we haven't yet */ + /* understood. Since we look for writable regions to */ + /* determine the root set, we may try to mark from an */ + /* address range that disappeared since we started the */ + /* collection. Thus we have to recover from faults here. */ + /* This code does not appear to be necessary for Windows */ + /* 95/NT/2000+. Note that this code should never generate */ + /* an incremental GC write fault. */ + /* This code seems to be necessary for WinCE (at least in */ + /* the case we'd decide to add MEM_PRIVATE sections to */ + /* data roots in GC_register_dynamic_libraries()). */ + /* It's conceivable that this is the same issue with */ + /* terminating threads that we see with Linux and */ + /* USE_PROC_FOR_LIBRARIES. */ +#ifndef NO_CRT + __try { +#endif + ret_val = GC_mark_some_inner(cold_gc_frame); +#ifndef NO_CRT + } __except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { + goto handle_ex; + } +#endif +# if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) + /* With DllMain-based thread tracking, a thread may have */ + /* started while we were marking. This is logically equivalent */ + /* to the exception case; our results are invalid and we have */ + /* to start over. This cannot be prevented since we can't */ + /* block in DllMain. */ + if (GC_started_thread_while_stopped()) goto handle_ex; +# endif + rm_handler: + return ret_val; + +# else /* __GNUC__ */ + + /* Manually install an exception handler since GCC does */ + /* not yet support Structured Exception Handling (SEH) on */ + /* Win32. */ + + ext_ex_regn er; + +# if GC_GNUC_PREREQ(4, 7) || GC_CLANG_PREREQ(3, 3) +# pragma GCC diagnostic push + /* Suppress "taking the address of label is non-standard" warning. */ +# if defined(__clang__) || GC_GNUC_PREREQ(6, 4) +# pragma GCC diagnostic ignored "-Wpedantic" +# else + /* GCC before ~4.8 does not accept "-Wpedantic" quietly. */ +# pragma GCC diagnostic ignored "-pedantic" +# endif + er.alt_path = &&handle_ex; +# pragma GCC diagnostic pop +# else /* pragma diagnostic is not supported */ + er.alt_path = &&handle_ex; +# endif + er.ex_reg.handler = mark_ex_handler; + __asm__ __volatile__ ("movl %%fs:0, %0" : "=r" (er.ex_reg.prev)); + __asm__ __volatile__ ("movl %0, %%fs:0" : : "r" (&er)); + ret_val = GC_mark_some_inner(cold_gc_frame); + /* Prevent GCC from considering the following code unreachable */ + /* and thus eliminating it. */ + if (er.alt_path == 0) + goto handle_ex; +# if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) + if (GC_started_thread_while_stopped()) + goto handle_ex; +# endif + rm_handler: + /* Uninstall the exception handler */ + __asm__ __volatile__ ("mov %0, %%fs:0" : : "r" (er.ex_reg.prev)); + return ret_val; + +# endif /* __GNUC__ */ +# else /* !MSWIN32 */ + /* Here we are handling the case in which /proc is used for root */ + /* finding, and we have threads. We may find a stack for a */ + /* thread that is in the process of exiting, and disappears */ + /* while we are marking it. This seems extremely difficult to */ + /* avoid otherwise. */ + if (GC_incremental) { + WARN("Incremental GC incompatible with /proc roots\n", 0); + /* I'm not sure if this could still work ... */ + } + GC_setup_temporary_fault_handler(); + if(SETJMP(GC_jmp_buf) != 0) goto handle_ex; + ret_val = GC_mark_some_inner(cold_gc_frame); + rm_handler: + GC_reset_fault_handler(); + return ret_val; + +# endif /* !MSWIN32 */ + +handle_ex: + /* Exception handler starts here for all cases. */ + { + static word warned_gc_no; + + /* Warn about it at most once per collection. */ + if (warned_gc_no != GC_gc_no) { + warned_gc_no = GC_gc_no; + WARN("Caught ACCESS_VIOLATION in marker;" + " memory mapping disappeared\n", 0); + } + } + /* We have bad roots on the stack. Discard mark stack. */ + /* Rescan from marked objects. Redetermine roots. */ +# ifdef REGISTER_LIBRARIES_EARLY + START_WORLD(); + GC_cond_register_dynamic_libraries(); + STOP_WORLD(); +# endif + GC_invalidate_mark_state(); + scan_ptr = 0; + + ret_val = FALSE; + goto rm_handler; /* Back to platform-specific code. */ + } +#endif /* WRAP_MARK_SOME */ + +GC_INNER void GC_invalidate_mark_state(void) +{ + GC_mark_state = MS_INVALID; + GC_mark_stack_top = GC_mark_stack-1; +} + +GC_INNER mse * GC_signal_mark_stack_overflow(mse *msp) +{ + GC_mark_state = MS_INVALID; +# ifdef PARALLEL_MARK + /* We are using a local_mark_stack in parallel mode, so */ + /* do not signal the global mark stack to be resized. */ + /* That will be done if required in GC_return_mark_stack. */ + if (!GC_parallel) + GC_mark_stack_too_small = TRUE; +# else + GC_mark_stack_too_small = TRUE; +# endif + GC_COND_LOG_PRINTF("Mark stack overflow; current size = %lu entries\n", + (unsigned long)GC_mark_stack_size); + return(msp - GC_MARK_STACK_DISCARDS); +} + +/* + * Mark objects pointed to by the regions described by + * mark stack entries between mark_stack and mark_stack_top, + * inclusive. Assumes the upper limit of a mark stack entry + * is never 0. A mark stack entry never has size 0. + * We try to traverse on the order of a hblk of memory before we return. + * Caller is responsible for calling this until the mark stack is empty. + * Note that this is the most performance critical routine in the + * collector. Hence it contains all sorts of ugly hacks to speed + * things up. In particular, we avoid procedure calls on the common + * path, we take advantage of peculiarities of the mark descriptor + * encoding, we optionally maintain a cache for the block address to + * header mapping, we prefetch when an object is "grayed", etc. + */ +GC_ATTR_NO_SANITIZE_ADDR GC_ATTR_NO_SANITIZE_MEMORY GC_ATTR_NO_SANITIZE_THREAD +GC_INNER mse * GC_mark_from(mse *mark_stack_top, mse *mark_stack, + mse *mark_stack_limit) +{ + signed_word credit = HBLKSIZE; /* Remaining credit for marking work */ + ptr_t current_p; /* Pointer to current candidate ptr. */ + word current; /* Candidate pointer. */ + ptr_t limit = 0; /* (Incl) limit of current candidate range. */ + word descr; + ptr_t greatest_ha = (ptr_t)GC_greatest_plausible_heap_addr; + ptr_t least_ha = (ptr_t)GC_least_plausible_heap_addr; + DECLARE_HDR_CACHE; + +# define SPLIT_RANGE_WORDS 128 /* Must be power of 2. */ + + GC_objects_are_marked = TRUE; + INIT_HDR_CACHE; +# ifdef OS2 /* Use untweaked version to circumvent compiler problem */ + while ((word)mark_stack_top >= (word)mark_stack && credit >= 0) +# else + while ((((ptr_t)mark_stack_top - (ptr_t)mark_stack) | credit) >= 0) +# endif + { + current_p = mark_stack_top -> mse_start; + descr = mark_stack_top -> mse_descr.w; + retry: + /* current_p and descr describe the current object. */ + /* *mark_stack_top is vacant. */ + /* The following is 0 only for small objects described by a simple */ + /* length descriptor. For many applications this is the common */ + /* case, so we try to detect it quickly. */ + if (descr & ((~(WORDS_TO_BYTES(SPLIT_RANGE_WORDS) - 1)) | GC_DS_TAGS)) { + word tag = descr & GC_DS_TAGS; + + GC_STATIC_ASSERT(GC_DS_TAGS == 0x3); + switch(tag) { + case GC_DS_LENGTH: + /* Large length. */ + /* Process part of the range to avoid pushing too much on the */ + /* stack. */ + GC_ASSERT(descr < (word)GC_greatest_plausible_heap_addr + - (word)GC_least_plausible_heap_addr + || (word)(current_p + descr) + <= (word)GC_least_plausible_heap_addr + || (word)current_p >= (word)GC_greatest_plausible_heap_addr); +# ifdef PARALLEL_MARK +# define SHARE_BYTES 2048 + if (descr > SHARE_BYTES && GC_parallel + && (word)mark_stack_top < (word)(mark_stack_limit - 1)) { + word new_size = (descr/2) & ~(word)(sizeof(word)-1); + + mark_stack_top -> mse_start = current_p; + mark_stack_top -> mse_descr.w = new_size + sizeof(word); + /* makes sure we handle */ + /* misaligned pointers. */ + mark_stack_top++; +# ifdef ENABLE_TRACE + if ((word)GC_trace_addr >= (word)current_p + && (word)GC_trace_addr < (word)(current_p + descr)) { + GC_log_printf("GC #%u: large section; start %p, len %lu," + " splitting (parallel) at %p\n", + (unsigned)GC_gc_no, (void *)current_p, + (unsigned long)descr, + (void *)(current_p + new_size)); + } +# endif + current_p += new_size; + descr -= new_size; + goto retry; + } +# endif /* PARALLEL_MARK */ + mark_stack_top -> mse_start = + limit = current_p + WORDS_TO_BYTES(SPLIT_RANGE_WORDS-1); + mark_stack_top -> mse_descr.w = + descr - WORDS_TO_BYTES(SPLIT_RANGE_WORDS-1); +# ifdef ENABLE_TRACE + if ((word)GC_trace_addr >= (word)current_p + && (word)GC_trace_addr < (word)(current_p + descr)) { + GC_log_printf("GC #%u: large section; start %p, len %lu," + " splitting at %p\n", + (unsigned)GC_gc_no, (void *)current_p, + (unsigned long)descr, (void *)limit); + } +# endif + /* Make sure that pointers overlapping the two ranges are */ + /* considered. */ + limit += sizeof(word) - ALIGNMENT; + break; + case GC_DS_BITMAP: + mark_stack_top--; +# ifdef ENABLE_TRACE + if ((word)GC_trace_addr >= (word)current_p + && (word)GC_trace_addr < (word)(current_p + + WORDS_TO_BYTES(WORDSZ-2))) { + GC_log_printf("GC #%u: tracing from %p bitmap descr %lu\n", + (unsigned)GC_gc_no, (void *)current_p, + (unsigned long)descr); + } +# endif /* ENABLE_TRACE */ + descr &= ~GC_DS_TAGS; + credit -= WORDS_TO_BYTES(WORDSZ/2); /* guess */ + while (descr != 0) { + if ((descr & SIGNB) != 0) { + current = *(word *)current_p; + FIXUP_POINTER(current); + if (current >= (word)least_ha && current < (word)greatest_ha) { + PREFETCH((ptr_t)current); +# ifdef ENABLE_TRACE + if (GC_trace_addr == current_p) { + GC_log_printf("GC #%u: considering(3) %p -> %p\n", + (unsigned)GC_gc_no, (void *)current_p, + (void *)current); + } +# endif /* ENABLE_TRACE */ + PUSH_CONTENTS((ptr_t)current, mark_stack_top, + mark_stack_limit, current_p); + } + } + descr <<= 1; + current_p += sizeof(word); + } + continue; + case GC_DS_PROC: + mark_stack_top--; +# ifdef ENABLE_TRACE + if ((word)GC_trace_addr >= (word)current_p + && GC_base(current_p) != 0 + && GC_base(current_p) == GC_base(GC_trace_addr)) { + GC_log_printf("GC #%u: tracing from %p, proc descr %lu\n", + (unsigned)GC_gc_no, (void *)current_p, + (unsigned long)descr); + } +# endif /* ENABLE_TRACE */ + credit -= GC_PROC_BYTES; + mark_stack_top = (*PROC(descr))((word *)current_p, mark_stack_top, + mark_stack_limit, ENV(descr)); + continue; + case GC_DS_PER_OBJECT: + if ((signed_word)descr >= 0) { + /* Descriptor is in the object. */ + descr = *(word *)(current_p + descr - GC_DS_PER_OBJECT); + } else { + /* Descriptor is in type descriptor pointed to by first */ + /* word in object. */ + ptr_t type_descr = *(ptr_t *)current_p; + /* type_descr is either a valid pointer to the descriptor */ + /* structure, or this object was on a free list. */ + /* If it was anything but the last object on the free list, */ + /* we will misinterpret the next object on the free list as */ + /* the type descriptor, and get a 0 GC descriptor, which */ + /* is ideal. Unfortunately, we need to check for the last */ + /* object case explicitly. */ + if (EXPECT(0 == type_descr, FALSE)) { + mark_stack_top--; + continue; + } + descr = *(word *)(type_descr + - ((signed_word)descr + (GC_INDIR_PER_OBJ_BIAS + - GC_DS_PER_OBJECT))); + } + if (0 == descr) { + /* Can happen either because we generated a 0 descriptor */ + /* or we saw a pointer to a free object. */ + mark_stack_top--; + continue; + } + goto retry; + } + } else /* Small object with length descriptor */ { + mark_stack_top--; +# ifndef SMALL_CONFIG + if (descr < sizeof(word)) + continue; +# endif +# ifdef ENABLE_TRACE + if ((word)GC_trace_addr >= (word)current_p + && (word)GC_trace_addr < (word)(current_p + descr)) { + GC_log_printf("GC #%u: small object; start %p, len %lu\n", + (unsigned)GC_gc_no, (void *)current_p, + (unsigned long)descr); + } +# endif + limit = current_p + (word)descr; + } + /* The simple case in which we're scanning a range. */ + GC_ASSERT(!((word)current_p & (ALIGNMENT-1))); + credit -= limit - current_p; + limit -= sizeof(word); + { +# define PREF_DIST 4 + +# ifndef SMALL_CONFIG + word deferred; + + /* Try to prefetch the next pointer to be examined ASAP. */ + /* Empirically, this also seems to help slightly without */ + /* prefetches, at least on linux/X86. Presumably this loop */ + /* ends up with less register pressure, and gcc thus ends up */ + /* generating slightly better code. Overall gcc code quality */ + /* for this loop is still not great. */ + for(;;) { + PREFETCH(limit - PREF_DIST*CACHE_LINE_SIZE); + GC_ASSERT((word)limit >= (word)current_p); + deferred = *(word *)limit; + FIXUP_POINTER(deferred); + limit -= ALIGNMENT; + if (deferred >= (word)least_ha && deferred < (word)greatest_ha) { + PREFETCH((ptr_t)deferred); + break; + } + if ((word)current_p > (word)limit) goto next_object; + /* Unroll once, so we don't do too many of the prefetches */ + /* based on limit. */ + deferred = *(word *)limit; + FIXUP_POINTER(deferred); + limit -= ALIGNMENT; + if (deferred >= (word)least_ha && deferred < (word)greatest_ha) { + PREFETCH((ptr_t)deferred); + break; + } + if ((word)current_p > (word)limit) goto next_object; + } +# endif + + while ((word)current_p <= (word)limit) { + /* Empirically, unrolling this loop doesn't help a lot. */ + /* Since PUSH_CONTENTS expands to a lot of code, */ + /* we don't. */ + current = *(word *)current_p; + FIXUP_POINTER(current); + PREFETCH(current_p + PREF_DIST*CACHE_LINE_SIZE); + if (current >= (word)least_ha && current < (word)greatest_ha) { + /* Prefetch the contents of the object we just pushed. It's */ + /* likely we will need them soon. */ + PREFETCH((ptr_t)current); +# ifdef ENABLE_TRACE + if (GC_trace_addr == current_p) { + GC_log_printf("GC #%u: considering(1) %p -> %p\n", + (unsigned)GC_gc_no, (void *)current_p, + (void *)current); + } +# endif /* ENABLE_TRACE */ + PUSH_CONTENTS((ptr_t)current, mark_stack_top, + mark_stack_limit, current_p); + } + current_p += ALIGNMENT; + } + +# ifndef SMALL_CONFIG + /* We still need to mark the entry we previously prefetched. */ + /* We already know that it passes the preliminary pointer */ + /* validity test. */ +# ifdef ENABLE_TRACE + if (GC_trace_addr == current_p) { + GC_log_printf("GC #%u: considering(2) %p -> %p\n", + (unsigned)GC_gc_no, (void *)current_p, + (void *)deferred); + } +# endif /* ENABLE_TRACE */ + PUSH_CONTENTS((ptr_t)deferred, mark_stack_top, + mark_stack_limit, current_p); + next_object:; +# endif + } + } + return mark_stack_top; +} + +#ifdef PARALLEL_MARK + +STATIC GC_bool GC_help_wanted = FALSE; /* Protected by mark lock */ +STATIC unsigned GC_helper_count = 0; /* Number of running helpers. */ + /* Protected by mark lock */ +STATIC unsigned GC_active_count = 0; /* Number of active helpers. */ + /* Protected by mark lock */ + /* May increase and decrease */ + /* within each mark cycle. But */ + /* once it returns to 0, it */ + /* stays zero for the cycle. */ + +GC_INNER word GC_mark_no = 0; + +static mse *main_local_mark_stack; + +#ifdef LINT2 +# define LOCAL_MARK_STACK_SIZE (HBLKSIZE / 8) +#else +# define LOCAL_MARK_STACK_SIZE HBLKSIZE + /* Under normal circumstances, this is big enough to guarantee */ + /* we don't overflow half of it in a single call to */ + /* GC_mark_from. */ +#endif + +/* Wait all markers to finish initialization (i.e. store */ +/* marker_[b]sp, marker_mach_threads, GC_marker_Id). */ +GC_INNER void GC_wait_for_markers_init(void) +{ + signed_word count; + + if (GC_markers_m1 == 0) + return; + + /* Allocate the local mark stack for the thread that holds GC lock. */ +# ifndef CAN_HANDLE_FORK + GC_ASSERT(NULL == main_local_mark_stack); +# else + if (NULL == main_local_mark_stack) +# endif + { + size_t bytes_to_get = + ROUNDUP_PAGESIZE_IF_MMAP(LOCAL_MARK_STACK_SIZE * sizeof(mse)); + main_local_mark_stack = (mse *)GET_MEM(bytes_to_get); + if (NULL == main_local_mark_stack) + ABORT("Insufficient memory for main local_mark_stack"); + GC_add_to_our_memory((ptr_t)main_local_mark_stack, bytes_to_get); + } + + /* Reuse marker lock and builders count to synchronize */ + /* marker threads startup. */ + GC_acquire_mark_lock(); + GC_fl_builder_count += GC_markers_m1; + count = GC_fl_builder_count; + GC_release_mark_lock(); + if (count != 0) { + GC_ASSERT(count > 0); + GC_wait_for_reclaim(); + } +} + +/* Steal mark stack entries starting at mse low into mark stack local */ +/* until we either steal mse high, or we have max entries. */ +/* Return a pointer to the top of the local mark stack. */ +/* *next is replaced by a pointer to the next unscanned mark stack */ +/* entry. */ +STATIC mse * GC_steal_mark_stack(mse * low, mse * high, mse * local, + unsigned max, mse **next) +{ + mse *p; + mse *top = local - 1; + unsigned i = 0; + + GC_ASSERT((word)high >= (word)(low - 1) + && (word)(high - low + 1) <= GC_mark_stack_size); + for (p = low; (word)p <= (word)high && i <= max; ++p) { + word descr = (word)AO_load(&p->mse_descr.ao); + if (descr != 0) { + /* Must be ordered after read of descr: */ + AO_store_release_write(&p->mse_descr.ao, 0); + /* More than one thread may get this entry, but that's only */ + /* a minor performance problem. */ + ++top; + top -> mse_descr.w = descr; + top -> mse_start = p -> mse_start; + GC_ASSERT((descr & GC_DS_TAGS) != GC_DS_LENGTH + || descr < (word)GC_greatest_plausible_heap_addr + - (word)GC_least_plausible_heap_addr + || (word)(p->mse_start + descr) + <= (word)GC_least_plausible_heap_addr + || (word)p->mse_start + >= (word)GC_greatest_plausible_heap_addr); + /* If this is a big object, count it as */ + /* size/256 + 1 objects. */ + ++i; + if ((descr & GC_DS_TAGS) == GC_DS_LENGTH) i += (int)(descr >> 8); + } + } + *next = p; + return top; +} + +/* Copy back a local mark stack. */ +/* low and high are inclusive bounds. */ +STATIC void GC_return_mark_stack(mse * low, mse * high) +{ + mse * my_top; + mse * my_start; + size_t stack_size; + + if ((word)high < (word)low) return; + stack_size = high - low + 1; + GC_acquire_mark_lock(); + my_top = GC_mark_stack_top; /* Concurrent modification impossible. */ + my_start = my_top + 1; + if ((word)(my_start - GC_mark_stack + stack_size) + > (word)GC_mark_stack_size) { + GC_COND_LOG_PRINTF("No room to copy back mark stack\n"); + GC_mark_state = MS_INVALID; + GC_mark_stack_too_small = TRUE; + /* We drop the local mark stack. We'll fix things later. */ + } else { + BCOPY(low, my_start, stack_size * sizeof(mse)); + GC_ASSERT((mse *)AO_load((volatile AO_t *)(&GC_mark_stack_top)) + == my_top); + AO_store_release_write((volatile AO_t *)(&GC_mark_stack_top), + (AO_t)(my_top + stack_size)); + /* Ensures visibility of previously written stack contents. */ + } + GC_release_mark_lock(); + GC_notify_all_marker(); +} + +#ifndef N_LOCAL_ITERS +# define N_LOCAL_ITERS 1 +#endif + +/* This function is only called when the local */ +/* and the main mark stacks are both empty. */ +static GC_bool has_inactive_helpers(void) +{ + GC_bool res; + + GC_acquire_mark_lock(); + res = GC_active_count < GC_helper_count; + GC_release_mark_lock(); + return res; +} + +/* Mark from the local mark stack. */ +/* On return, the local mark stack is empty. */ +/* But this may be achieved by copying the */ +/* local mark stack back into the global one. */ +/* We do not hold the mark lock. */ +STATIC void GC_do_local_mark(mse *local_mark_stack, mse *local_top) +{ + unsigned n; + + for (;;) { + for (n = 0; n < N_LOCAL_ITERS; ++n) { + local_top = GC_mark_from(local_top, local_mark_stack, + local_mark_stack + LOCAL_MARK_STACK_SIZE); + if ((word)local_top < (word)local_mark_stack) return; + if ((word)(local_top - local_mark_stack) + >= LOCAL_MARK_STACK_SIZE / 2) { + GC_return_mark_stack(local_mark_stack, local_top); + return; + } + } + if ((word)AO_load((volatile AO_t *)&GC_mark_stack_top) + < (word)AO_load(&GC_first_nonempty) + && (word)local_top > (word)(local_mark_stack + 1) + && has_inactive_helpers()) { + /* Try to share the load, since the main stack is empty, */ + /* and helper threads are waiting for a refill. */ + /* The entries near the bottom of the stack are likely */ + /* to require more work. Thus we return those, even though */ + /* it's harder. */ + mse * new_bottom = local_mark_stack + + (local_top - local_mark_stack)/2; + GC_ASSERT((word)new_bottom > (word)local_mark_stack + && (word)new_bottom < (word)local_top); + GC_return_mark_stack(local_mark_stack, new_bottom - 1); + memmove(local_mark_stack, new_bottom, + (local_top - new_bottom + 1) * sizeof(mse)); + local_top -= (new_bottom - local_mark_stack); + } + } +} + +#ifndef ENTRIES_TO_GET +# define ENTRIES_TO_GET 5 +#endif + +/* Mark using the local mark stack until the global mark stack is empty */ +/* and there are no active workers. Update GC_first_nonempty to reflect */ +/* progress. Caller holds the mark lock. */ +/* Caller has already incremented GC_helper_count. We decrement it, */ +/* and maintain GC_active_count. */ +STATIC void GC_mark_local(mse *local_mark_stack, int id) +{ + mse * my_first_nonempty; + + GC_active_count++; + my_first_nonempty = (mse *)AO_load(&GC_first_nonempty); + GC_ASSERT((word)GC_mark_stack <= (word)my_first_nonempty); + GC_ASSERT((word)my_first_nonempty + <= (word)AO_load((volatile AO_t *)&GC_mark_stack_top) + sizeof(mse)); + GC_VERBOSE_LOG_PRINTF("Starting mark helper %d\n", id); + GC_release_mark_lock(); + for (;;) { + size_t n_on_stack; + unsigned n_to_get; + mse * my_top; + mse * local_top; + mse * global_first_nonempty = (mse *)AO_load(&GC_first_nonempty); + + GC_ASSERT((word)my_first_nonempty >= (word)GC_mark_stack && + (word)my_first_nonempty <= + (word)AO_load((volatile AO_t *)&GC_mark_stack_top) + + sizeof(mse)); + GC_ASSERT((word)global_first_nonempty >= (word)GC_mark_stack); + if ((word)my_first_nonempty < (word)global_first_nonempty) { + my_first_nonempty = global_first_nonempty; + } else if ((word)global_first_nonempty < (word)my_first_nonempty) { + (void)AO_compare_and_swap(&GC_first_nonempty, + (AO_t)global_first_nonempty, + (AO_t)my_first_nonempty); + /* If this fails, we just go ahead, without updating */ + /* GC_first_nonempty. */ + } + /* Perhaps we should also update GC_first_nonempty, if it */ + /* is less. But that would require using atomic updates. */ + my_top = (mse *)AO_load_acquire((volatile AO_t *)(&GC_mark_stack_top)); + if ((word)my_top < (word)my_first_nonempty) { + GC_acquire_mark_lock(); + my_top = GC_mark_stack_top; + /* Asynchronous modification impossible here, */ + /* since we hold mark lock. */ + n_on_stack = my_top - my_first_nonempty + 1; + if (0 == n_on_stack) { + GC_active_count--; + GC_ASSERT(GC_active_count <= GC_helper_count); + /* Other markers may redeposit objects */ + /* on the stack. */ + if (0 == GC_active_count) GC_notify_all_marker(); + while (GC_active_count > 0 + && (word)AO_load(&GC_first_nonempty) + > (word)GC_mark_stack_top) { + /* We will be notified if either GC_active_count */ + /* reaches zero, or if more objects are pushed on */ + /* the global mark stack. */ + GC_wait_marker(); + } + if (GC_active_count == 0 + && (word)AO_load(&GC_first_nonempty) + > (word)GC_mark_stack_top) { + GC_bool need_to_notify = FALSE; + /* The above conditions can't be falsified while we */ + /* hold the mark lock, since neither */ + /* GC_active_count nor GC_mark_stack_top can */ + /* change. GC_first_nonempty can only be */ + /* incremented asynchronously. Thus we know that */ + /* both conditions actually held simultaneously. */ + GC_helper_count--; + if (0 == GC_helper_count) need_to_notify = TRUE; + GC_VERBOSE_LOG_PRINTF("Finished mark helper %d\n", id); + if (need_to_notify) GC_notify_all_marker(); + return; + } + /* else there's something on the stack again, or */ + /* another helper may push something. */ + GC_active_count++; + GC_ASSERT(GC_active_count > 0); + GC_release_mark_lock(); + continue; + } else { + GC_release_mark_lock(); + } + } else { + n_on_stack = my_top - my_first_nonempty + 1; + } + n_to_get = ENTRIES_TO_GET; + if (n_on_stack < 2 * ENTRIES_TO_GET) n_to_get = 1; + local_top = GC_steal_mark_stack(my_first_nonempty, my_top, + local_mark_stack, n_to_get, + &my_first_nonempty); + GC_ASSERT((word)my_first_nonempty >= (word)GC_mark_stack && + (word)my_first_nonempty <= + (word)AO_load((volatile AO_t *)&GC_mark_stack_top) + + sizeof(mse)); + GC_do_local_mark(local_mark_stack, local_top); + } +} + +/* Perform Parallel mark. */ +/* We hold the GC lock, not the mark lock. */ +/* Currently runs until the mark stack is */ +/* empty. */ +STATIC void GC_do_parallel_mark(void) +{ + GC_acquire_mark_lock(); + GC_ASSERT(I_HOLD_LOCK()); + /* This could be a GC_ASSERT, but it seems safer to keep it on */ + /* all the time, especially since it's cheap. */ + if (GC_help_wanted || GC_active_count != 0 || GC_helper_count != 0) + ABORT("Tried to start parallel mark in bad state"); + GC_VERBOSE_LOG_PRINTF("Starting marking for mark phase number %lu\n", + (unsigned long)GC_mark_no); + GC_first_nonempty = (AO_t)GC_mark_stack; + GC_active_count = 0; + GC_helper_count = 1; + GC_help_wanted = TRUE; + GC_notify_all_marker(); + /* Wake up potential helpers. */ + GC_mark_local(main_local_mark_stack, 0); + GC_help_wanted = FALSE; + /* Done; clean up. */ + while (GC_helper_count > 0) { + GC_wait_marker(); + } + /* GC_helper_count cannot be incremented while GC_help_wanted == FALSE */ + GC_VERBOSE_LOG_PRINTF("Finished marking for mark phase number %lu\n", + (unsigned long)GC_mark_no); + GC_mark_no++; + GC_release_mark_lock(); + GC_notify_all_marker(); +} + + +/* Try to help out the marker, if it's running. */ +/* We do not hold the GC lock, but the requestor does. */ +/* And we hold the mark lock. */ +GC_INNER void GC_help_marker(word my_mark_no) +{ +# define my_id my_id_mse.mse_descr.w + mse my_id_mse; /* align local_mark_stack explicitly */ + mse local_mark_stack[LOCAL_MARK_STACK_SIZE]; + /* Note: local_mark_stack is quite big (up to 128 KiB). */ + + GC_ASSERT(GC_parallel); + while (GC_mark_no < my_mark_no + || (!GC_help_wanted && GC_mark_no == my_mark_no)) { + GC_wait_marker(); + } + my_id = GC_helper_count; + if (GC_mark_no != my_mark_no || my_id > (unsigned)GC_markers_m1) { + /* Second test is useful only if original threads can also */ + /* act as helpers. Under Linux they can't. */ + return; + } + GC_helper_count = (unsigned)my_id + 1; + GC_mark_local(local_mark_stack, (int)my_id); + /* GC_mark_local decrements GC_helper_count. */ +# undef my_id +} + +#endif /* PARALLEL_MARK */ + +GC_INNER void GC_scratch_recycle_inner(void *ptr, size_t bytes) +{ + if (ptr != NULL) { + size_t page_offset = (word)ptr & (GC_page_size - 1); + size_t displ = 0; + size_t recycled_bytes; + + GC_ASSERT(bytes != 0); + GC_ASSERT(GC_page_size != 0); + /* TODO: Assert correct memory flags if GWW_VDB */ + if (page_offset != 0) + displ = GC_page_size - page_offset; + recycled_bytes = (bytes - displ) & ~(GC_page_size - 1); + GC_COND_LOG_PRINTF("Recycle %lu/%lu scratch-allocated bytes at %p\n", + (unsigned long)recycled_bytes, (unsigned long)bytes, + ptr); + if (recycled_bytes > 0) + GC_add_to_heap((struct hblk *)((word)ptr + displ), recycled_bytes); + } +} + +/* Allocate or reallocate space for mark stack of size n entries. */ +/* May silently fail. */ +static void alloc_mark_stack(size_t n) +{ + mse * new_stack = (mse *)GC_scratch_alloc(n * sizeof(struct GC_ms_entry)); +# ifdef GWW_VDB + /* Don't recycle a stack segment obtained with the wrong flags. */ + /* Win32 GetWriteWatch requires the right kind of memory. */ + static GC_bool GC_incremental_at_stack_alloc = FALSE; + GC_bool recycle_old = (!GC_incremental || GC_incremental_at_stack_alloc); + + GC_incremental_at_stack_alloc = GC_incremental; +# else +# define recycle_old TRUE +# endif + + GC_mark_stack_too_small = FALSE; + if (GC_mark_stack != NULL) { + if (new_stack != 0) { + if (recycle_old) { + /* Recycle old space */ + GC_scratch_recycle_inner(GC_mark_stack, + GC_mark_stack_size * sizeof(struct GC_ms_entry)); + } + GC_mark_stack = new_stack; + GC_mark_stack_size = n; + /* FIXME: Do we need some way to reset GC_mark_stack_size? */ + GC_mark_stack_limit = new_stack + n; + GC_COND_LOG_PRINTF("Grew mark stack to %lu frames\n", + (unsigned long)GC_mark_stack_size); + } else { + WARN("Failed to grow mark stack to %" WARN_PRIdPTR " frames\n", n); + } + } else { + if (new_stack == 0) { + GC_err_printf("No space for mark stack\n"); + EXIT(); + } + GC_mark_stack = new_stack; + GC_mark_stack_size = n; + GC_mark_stack_limit = new_stack + n; + } + GC_mark_stack_top = GC_mark_stack-1; +} + +GC_INNER void GC_mark_init(void) +{ + alloc_mark_stack(INITIAL_MARK_STACK_SIZE); +} + +/* + * Push all locations between b and t onto the mark stack. + * b is the first location to be checked. t is one past the last + * location to be checked. + * Should only be used if there is no possibility of mark stack + * overflow. + */ +GC_API void GC_CALL GC_push_all(void *bottom, void *top) +{ + word length; + + bottom = (void *)(((word)bottom + ALIGNMENT-1) & ~(ALIGNMENT-1)); + top = (void *)((word)top & ~(ALIGNMENT-1)); + if ((word)bottom >= (word)top) return; + + GC_mark_stack_top++; + if ((word)GC_mark_stack_top >= (word)GC_mark_stack_limit) { + ABORT("Unexpected mark stack overflow"); + } + length = (word)top - (word)bottom; +# if GC_DS_TAGS > ALIGNMENT - 1 + length += GC_DS_TAGS; + length &= ~GC_DS_TAGS; +# endif + GC_mark_stack_top -> mse_start = (ptr_t)bottom; + GC_mark_stack_top -> mse_descr.w = length; +} + +#ifndef GC_DISABLE_INCREMENTAL + + /* Analogous to the above, but push only those pages h with */ + /* dirty_fn(h) != 0. We use GC_push_all to actually push the block. */ + /* Used both to selectively push dirty pages, or to push a block in */ + /* piecemeal fashion, to allow for more marking concurrency. */ + /* Will not overflow mark stack if GC_push_all pushes a small fixed */ + /* number of entries. (This is invoked only if GC_push_all pushes */ + /* a single entry, or if it marks each object before pushing it, thus */ + /* ensuring progress in the event of a stack overflow.) */ + STATIC void GC_push_selected(ptr_t bottom, ptr_t top, + GC_bool (*dirty_fn)(struct hblk *)) + { + struct hblk * h; + + bottom = (ptr_t)(((word) bottom + ALIGNMENT-1) & ~(ALIGNMENT-1)); + top = (ptr_t)(((word) top) & ~(ALIGNMENT-1)); + if ((word)bottom >= (word)top) return; + + h = HBLKPTR(bottom + HBLKSIZE); + if ((word)top <= (word)h) { + if ((*dirty_fn)(h-1)) { + GC_push_all(bottom, top); + } + return; + } + if ((*dirty_fn)(h-1)) { + GC_push_all(bottom, h); + } + + while ((word)(h+1) <= (word)top) { + if ((*dirty_fn)(h)) { + if ((word)(GC_mark_stack_top - GC_mark_stack) + > 3 * GC_mark_stack_size / 4) { + /* Danger of mark stack overflow */ + GC_push_all(h, top); + return; + } else { + GC_push_all(h, h + 1); + } + } + h++; + } + + if ((ptr_t)h != top && (*dirty_fn)(h)) { + GC_push_all(h, top); + } + if ((word)GC_mark_stack_top >= (word)GC_mark_stack_limit) { + ABORT("Unexpected mark stack overflow"); + } + } + + GC_API void GC_CALL GC_push_conditional(void *bottom, void *top, int all) + { + if (!all) { + GC_push_selected((ptr_t)bottom, (ptr_t)top, GC_page_was_dirty); + } else { +# ifdef PROC_VDB + if (GC_incremental) { + /* Pages that were never dirtied cannot contain pointers. */ + GC_push_selected((ptr_t)bottom, (ptr_t)top, GC_page_was_ever_dirty); + } else +# endif + /* else */ { + GC_push_all(bottom, top); + } + } + } +#else + GC_API void GC_CALL GC_push_conditional(void *bottom, void *top, + int all GC_ATTR_UNUSED) + { + GC_push_all(bottom, top); + } +#endif /* GC_DISABLE_INCREMENTAL */ + +#if defined(MSWIN32) || defined(MSWINCE) + void __cdecl GC_push_one(word p) +#else + void GC_push_one(word p) +#endif +{ + GC_PUSH_ONE_STACK(p, MARKED_FROM_REGISTER); +} + +GC_API struct GC_ms_entry * GC_CALL GC_mark_and_push(void *obj, + mse *mark_stack_ptr, + mse *mark_stack_limit, + void ** src GC_ATTR_UNUSED) +{ + hdr * hhdr; + + PREFETCH(obj); + GET_HDR(obj, hhdr); + if ((EXPECT(IS_FORWARDING_ADDR_OR_NIL(hhdr), FALSE) + && (!GC_all_interior_pointers + || NULL == (hhdr = GC_find_header((ptr_t)GC_base(obj))))) + || EXPECT(HBLK_IS_FREE(hhdr), FALSE)) { + GC_ADD_TO_BLACK_LIST_NORMAL(obj, (ptr_t)src); + return mark_stack_ptr; + } + + PUSH_CONTENTS_HDR(obj, mark_stack_ptr /* modified */, mark_stack_limit, + (ptr_t)src, hhdr, TRUE); + return mark_stack_ptr; +} + +/* Mark and push (i.e. gray) a single object p onto the main */ +/* mark stack. Consider p to be valid if it is an interior */ +/* pointer. */ +/* The object p has passed a preliminary pointer validity */ +/* test, but we do not definitely know whether it is valid. */ +/* Mark bits are NOT atomically updated. Thus this must be the */ +/* only thread setting them. */ +# if defined(PRINT_BLACK_LIST) || defined(KEEP_BACK_PTRS) + GC_INNER void GC_mark_and_push_stack(ptr_t p, ptr_t source) +# else + GC_INNER void GC_mark_and_push_stack(ptr_t p) +# define source ((ptr_t)0) +# endif +{ + hdr * hhdr; + ptr_t r = p; + + PREFETCH(p); + GET_HDR(p, hhdr); + if (EXPECT(IS_FORWARDING_ADDR_OR_NIL(hhdr), FALSE) + && (NULL == hhdr + || (r = (ptr_t)GC_base(p)) == NULL + || (hhdr = HDR(r)) == NULL)) { + GC_ADD_TO_BLACK_LIST_STACK(p, source); + return; + } + if (EXPECT(HBLK_IS_FREE(hhdr), FALSE)) { + GC_ADD_TO_BLACK_LIST_NORMAL(p, source); + return; + } +# ifdef THREADS + /* Pointer is on the stack. We may have dirtied the object */ + /* it points to, but have not called GC_dirty yet. */ + GC_dirty(p); /* entire object */ +# endif + PUSH_CONTENTS_HDR(r, GC_mark_stack_top, GC_mark_stack_limit, + source, hhdr, FALSE); + /* We silently ignore pointers to near the end of a block, */ + /* which is very mildly suboptimal. */ + /* FIXME: We should probably add a header word to address */ + /* this. */ +} +# undef source + +#ifdef TRACE_BUF + +# ifndef TRACE_ENTRIES +# define TRACE_ENTRIES 1000 +# endif + +struct trace_entry { + char * kind; + word gc_no; + word bytes_allocd; + word arg1; + word arg2; +} GC_trace_buf[TRACE_ENTRIES]; + +int GC_trace_buf_ptr = 0; + +void GC_add_trace_entry(char *kind, word arg1, word arg2) +{ + GC_trace_buf[GC_trace_buf_ptr].kind = kind; + GC_trace_buf[GC_trace_buf_ptr].gc_no = GC_gc_no; + GC_trace_buf[GC_trace_buf_ptr].bytes_allocd = GC_bytes_allocd; + GC_trace_buf[GC_trace_buf_ptr].arg1 = arg1 ^ 0x80000000; + GC_trace_buf[GC_trace_buf_ptr].arg2 = arg2 ^ 0x80000000; + GC_trace_buf_ptr++; + if (GC_trace_buf_ptr >= TRACE_ENTRIES) GC_trace_buf_ptr = 0; +} + +GC_API void GC_CALL GC_print_trace_inner(word gc_no) +{ + int i; + + for (i = GC_trace_buf_ptr-1; i != GC_trace_buf_ptr; i--) { + struct trace_entry *p; + + if (i < 0) i = TRACE_ENTRIES-1; + p = GC_trace_buf + i; + if (p -> gc_no < gc_no || p -> kind == 0) { + return; + } + GC_printf("Trace:%s (gc:%u, bytes:%lu) 0x%lX, 0x%lX\n", + p -> kind, (unsigned)p -> gc_no, + (unsigned long)p -> bytes_allocd, + (long)p->arg1 ^ 0x80000000L, (long)p->arg2 ^ 0x80000000L); + } + GC_printf("Trace incomplete\n"); +} + +GC_API void GC_CALL GC_print_trace(word gc_no) +{ + DCL_LOCK_STATE; + + LOCK(); + GC_print_trace_inner(gc_no); + UNLOCK(); +} + +#endif /* TRACE_BUF */ + +/* + * A version of GC_push_all that treats all interior pointers as valid + * and scans the entire region immediately, in case the contents + * change. + */ +GC_ATTR_NO_SANITIZE_ADDR GC_ATTR_NO_SANITIZE_MEMORY GC_ATTR_NO_SANITIZE_THREAD +GC_API void GC_CALL GC_push_all_eager(void *bottom, void *top) +{ + word * b = (word *)(((word) bottom + ALIGNMENT-1) & ~(ALIGNMENT-1)); + word * t = (word *)(((word) top) & ~(ALIGNMENT-1)); + REGISTER word *p; + REGISTER word *lim; + REGISTER ptr_t greatest_ha = (ptr_t)GC_greatest_plausible_heap_addr; + REGISTER ptr_t least_ha = (ptr_t)GC_least_plausible_heap_addr; +# define GC_greatest_plausible_heap_addr greatest_ha +# define GC_least_plausible_heap_addr least_ha + + if (top == 0) return; + /* check all pointers in range and push if they appear */ + /* to be valid. */ + lim = t - 1 /* longword */; + for (p = b; (word)p <= (word)lim; + p = (word *)(((ptr_t)p) + ALIGNMENT)) { + REGISTER word q = *p; + + GC_PUSH_ONE_STACK(q, p); + } +# undef GC_greatest_plausible_heap_addr +# undef GC_least_plausible_heap_addr +} + +GC_INNER void GC_push_all_stack(ptr_t bottom, ptr_t top) +{ +# if defined(THREADS) && defined(MPROTECT_VDB) + GC_push_all_eager(bottom, top); +# else +# ifndef NEED_FIXUP_POINTER + if (GC_all_interior_pointers) { + GC_push_all(bottom, top); + } else +# endif + /* else */ { + GC_push_all_eager(bottom, top); + } +# endif +} + +#if defined(WRAP_MARK_SOME) && defined(PARALLEL_MARK) + /* Similar to GC_push_conditional but scans the whole region immediately. */ + GC_ATTR_NO_SANITIZE_ADDR GC_ATTR_NO_SANITIZE_MEMORY + GC_ATTR_NO_SANITIZE_THREAD + GC_INNER void GC_push_conditional_eager(void *bottom, void *top, + GC_bool all) + { + word * b = (word *)(((word) bottom + ALIGNMENT-1) & ~(ALIGNMENT-1)); + word * t = (word *)(((word) top) & ~(ALIGNMENT-1)); + REGISTER word *p; + REGISTER word *lim; + REGISTER ptr_t greatest_ha = (ptr_t)GC_greatest_plausible_heap_addr; + REGISTER ptr_t least_ha = (ptr_t)GC_least_plausible_heap_addr; +# define GC_greatest_plausible_heap_addr greatest_ha +# define GC_least_plausible_heap_addr least_ha + + if (top == NULL) + return; + (void)all; /* TODO: If !all then scan only dirty pages. */ + + lim = t - 1; + for (p = b; (word)p <= (word)lim; p = (word *)((ptr_t)p + ALIGNMENT)) { + REGISTER word q = *p; + + GC_PUSH_ONE_HEAP(q, p, GC_mark_stack_top); + } +# undef GC_greatest_plausible_heap_addr +# undef GC_least_plausible_heap_addr + } +#endif /* WRAP_MARK_SOME && PARALLEL_MARK */ + +#if !defined(SMALL_CONFIG) && !defined(USE_MARK_BYTES) && \ + defined(MARK_BIT_PER_GRANULE) +# if GC_GRANULE_WORDS == 1 +# define USE_PUSH_MARKED_ACCELERATORS +# define PUSH_GRANULE(q) \ + do { \ + word qcontents = (q)[0]; \ + GC_PUSH_ONE_HEAP(qcontents, q, GC_mark_stack_top); \ + } while (0) +# elif GC_GRANULE_WORDS == 2 +# define USE_PUSH_MARKED_ACCELERATORS +# define PUSH_GRANULE(q) \ + do { \ + word qcontents = (q)[0]; \ + GC_PUSH_ONE_HEAP(qcontents, q, GC_mark_stack_top); \ + qcontents = (q)[1]; \ + GC_PUSH_ONE_HEAP(qcontents, (q)+1, GC_mark_stack_top); \ + } while (0) +# elif GC_GRANULE_WORDS == 4 +# define USE_PUSH_MARKED_ACCELERATORS +# define PUSH_GRANULE(q) \ + do { \ + word qcontents = (q)[0]; \ + GC_PUSH_ONE_HEAP(qcontents, q, GC_mark_stack_top); \ + qcontents = (q)[1]; \ + GC_PUSH_ONE_HEAP(qcontents, (q)+1, GC_mark_stack_top); \ + qcontents = (q)[2]; \ + GC_PUSH_ONE_HEAP(qcontents, (q)+2, GC_mark_stack_top); \ + qcontents = (q)[3]; \ + GC_PUSH_ONE_HEAP(qcontents, (q)+3, GC_mark_stack_top); \ + } while (0) +# endif +#endif /* !USE_MARK_BYTES && MARK_BIT_PER_GRANULE */ + +#ifdef USE_PUSH_MARKED_ACCELERATORS +/* Push all objects reachable from marked objects in the given block */ +/* containing objects of size 1 granule. */ +STATIC void GC_push_marked1(struct hblk *h, hdr *hhdr) +{ + word * mark_word_addr = &(hhdr->hb_marks[0]); + word *p; + word *plim; + + /* Allow registers to be used for some frequently accessed */ + /* global variables. Otherwise aliasing issues are likely */ + /* to prevent that. */ + ptr_t greatest_ha = (ptr_t)GC_greatest_plausible_heap_addr; + ptr_t least_ha = (ptr_t)GC_least_plausible_heap_addr; + mse * mark_stack_top = GC_mark_stack_top; + mse * mark_stack_limit = GC_mark_stack_limit; + +# undef GC_mark_stack_top +# undef GC_mark_stack_limit +# define GC_mark_stack_top mark_stack_top +# define GC_mark_stack_limit mark_stack_limit +# define GC_greatest_plausible_heap_addr greatest_ha +# define GC_least_plausible_heap_addr least_ha + + p = (word *)(h->hb_body); + plim = (word *)(((word)h) + HBLKSIZE); + + /* go through all words in block */ + while ((word)p < (word)plim) { + word mark_word = *mark_word_addr++; + word *q = p; + + while(mark_word != 0) { + if (mark_word & 1) { + PUSH_GRANULE(q); + } + q += GC_GRANULE_WORDS; + mark_word >>= 1; + } + p += WORDSZ*GC_GRANULE_WORDS; + } + +# undef GC_greatest_plausible_heap_addr +# undef GC_least_plausible_heap_addr +# undef GC_mark_stack_top +# undef GC_mark_stack_limit +# define GC_mark_stack_limit GC_arrays._mark_stack_limit +# define GC_mark_stack_top GC_arrays._mark_stack_top + GC_mark_stack_top = mark_stack_top; +} + + +#ifndef UNALIGNED_PTRS + +/* Push all objects reachable from marked objects in the given block */ +/* of size 2 (granules) objects. */ +STATIC void GC_push_marked2(struct hblk *h, hdr *hhdr) +{ + word * mark_word_addr = &(hhdr->hb_marks[0]); + word *p; + word *plim; + + ptr_t greatest_ha = (ptr_t)GC_greatest_plausible_heap_addr; + ptr_t least_ha = (ptr_t)GC_least_plausible_heap_addr; + mse * mark_stack_top = GC_mark_stack_top; + mse * mark_stack_limit = GC_mark_stack_limit; + +# undef GC_mark_stack_top +# undef GC_mark_stack_limit +# define GC_mark_stack_top mark_stack_top +# define GC_mark_stack_limit mark_stack_limit +# define GC_greatest_plausible_heap_addr greatest_ha +# define GC_least_plausible_heap_addr least_ha + + p = (word *)(h->hb_body); + plim = (word *)(((word)h) + HBLKSIZE); + + /* go through all words in block */ + while ((word)p < (word)plim) { + word mark_word = *mark_word_addr++; + word *q = p; + + while(mark_word != 0) { + if (mark_word & 1) { + PUSH_GRANULE(q); + PUSH_GRANULE(q + GC_GRANULE_WORDS); + } + q += 2 * GC_GRANULE_WORDS; + mark_word >>= 2; + } + p += WORDSZ*GC_GRANULE_WORDS; + } + +# undef GC_greatest_plausible_heap_addr +# undef GC_least_plausible_heap_addr +# undef GC_mark_stack_top +# undef GC_mark_stack_limit +# define GC_mark_stack_limit GC_arrays._mark_stack_limit +# define GC_mark_stack_top GC_arrays._mark_stack_top + GC_mark_stack_top = mark_stack_top; +} + +# if GC_GRANULE_WORDS < 4 +/* Push all objects reachable from marked objects in the given block */ +/* of size 4 (granules) objects. */ +/* There is a risk of mark stack overflow here. But we handle that. */ +/* And only unmarked objects get pushed, so it's not very likely. */ +STATIC void GC_push_marked4(struct hblk *h, hdr *hhdr) +{ + word * mark_word_addr = &(hhdr->hb_marks[0]); + word *p; + word *plim; + + ptr_t greatest_ha = (ptr_t)GC_greatest_plausible_heap_addr; + ptr_t least_ha = (ptr_t)GC_least_plausible_heap_addr; + mse * mark_stack_top = GC_mark_stack_top; + mse * mark_stack_limit = GC_mark_stack_limit; + +# undef GC_mark_stack_top +# undef GC_mark_stack_limit +# define GC_mark_stack_top mark_stack_top +# define GC_mark_stack_limit mark_stack_limit +# define GC_greatest_plausible_heap_addr greatest_ha +# define GC_least_plausible_heap_addr least_ha + + p = (word *)(h->hb_body); + plim = (word *)(((word)h) + HBLKSIZE); + + /* go through all words in block */ + while ((word)p < (word)plim) { + word mark_word = *mark_word_addr++; + word *q = p; + + while(mark_word != 0) { + if (mark_word & 1) { + PUSH_GRANULE(q); + PUSH_GRANULE(q + GC_GRANULE_WORDS); + PUSH_GRANULE(q + 2*GC_GRANULE_WORDS); + PUSH_GRANULE(q + 3*GC_GRANULE_WORDS); + } + q += 4 * GC_GRANULE_WORDS; + mark_word >>= 4; + } + p += WORDSZ*GC_GRANULE_WORDS; + } +# undef GC_greatest_plausible_heap_addr +# undef GC_least_plausible_heap_addr +# undef GC_mark_stack_top +# undef GC_mark_stack_limit +# define GC_mark_stack_limit GC_arrays._mark_stack_limit +# define GC_mark_stack_top GC_arrays._mark_stack_top + GC_mark_stack_top = mark_stack_top; +} + +#endif /* GC_GRANULE_WORDS < 4 */ + +#endif /* UNALIGNED_PTRS */ + +#endif /* USE_PUSH_MARKED_ACCELERATORS */ + +/* Push all objects reachable from marked objects in the given block */ +STATIC void GC_push_marked(struct hblk *h, hdr *hhdr) +{ + word sz = hhdr -> hb_sz; + word descr = hhdr -> hb_descr; + ptr_t p; + word bit_no; + ptr_t lim; + mse * GC_mark_stack_top_reg; + mse * mark_stack_limit = GC_mark_stack_limit; + + /* Some quick shortcuts: */ + if ((/* 0 | */ GC_DS_LENGTH) == descr) return; + if (GC_block_empty(hhdr)/* nothing marked */) return; +# if !defined(GC_DISABLE_INCREMENTAL) + GC_n_rescuing_pages++; +# endif + GC_objects_are_marked = TRUE; + if (sz > MAXOBJBYTES) { + lim = h -> hb_body; + } else { + lim = (ptr_t)((word)(h + 1)->hb_body - sz); + } + + switch(BYTES_TO_GRANULES(sz)) { +# if defined(USE_PUSH_MARKED_ACCELERATORS) + case 1: + GC_push_marked1(h, hhdr); + break; +# if !defined(UNALIGNED_PTRS) + case 2: + GC_push_marked2(h, hhdr); + break; +# if GC_GRANULE_WORDS < 4 + case 4: + GC_push_marked4(h, hhdr); + break; +# endif +# endif +# endif + default: + GC_mark_stack_top_reg = GC_mark_stack_top; + for (p = h -> hb_body, bit_no = 0; (word)p <= (word)lim; + p += sz, bit_no += MARK_BIT_OFFSET(sz)) { + if (mark_bit_from_hdr(hhdr, bit_no)) { + /* Mark from fields inside the object */ + PUSH_OBJ(p, hhdr, GC_mark_stack_top_reg, mark_stack_limit); + } + } + GC_mark_stack_top = GC_mark_stack_top_reg; + } +} + +#ifdef ENABLE_DISCLAIM +/* Unconditionally mark from all objects which have not been reclaimed. */ +/* This is useful in order to retain pointers which are reachable from */ +/* the disclaim notifiers. */ +/* */ +/* To determine whether an object has been reclaimed, we require that */ +/* any live object has a non-zero as one of the two lowest bits of the */ +/* first word. On the other hand, a reclaimed object is a members of */ +/* free-lists, and thus contains a word-aligned next-pointer as the */ +/* first word. */ + STATIC void GC_push_unconditionally(struct hblk *h, hdr *hhdr) + { + word sz = hhdr -> hb_sz; + word descr = hhdr -> hb_descr; + ptr_t p; + ptr_t lim; + mse * GC_mark_stack_top_reg; + mse * mark_stack_limit = GC_mark_stack_limit; + + if ((/* 0 | */ GC_DS_LENGTH) == descr) + return; + +# if !defined(GC_DISABLE_INCREMENTAL) + GC_n_rescuing_pages++; +# endif + GC_objects_are_marked = TRUE; + if (sz > MAXOBJBYTES) + lim = h -> hb_body; + else + lim = (ptr_t)((word)(h + 1)->hb_body - sz); + + GC_mark_stack_top_reg = GC_mark_stack_top; + for (p = h -> hb_body; (word)p <= (word)lim; p += sz) + if ((*(word *)p & 0x3) != 0) + PUSH_OBJ(p, hhdr, GC_mark_stack_top_reg, mark_stack_limit); + GC_mark_stack_top = GC_mark_stack_top_reg; + } +#endif /* ENABLE_DISCLAIM */ + +#ifndef GC_DISABLE_INCREMENTAL + /* Test whether any page in the given block is dirty. */ + STATIC GC_bool GC_block_was_dirty(struct hblk *h, hdr *hhdr) + { + word sz = hhdr -> hb_sz; + + if (sz <= MAXOBJBYTES) { + return(GC_page_was_dirty(h)); + } else { + ptr_t p = (ptr_t)h; + while ((word)p < (word)h + sz) { + if (GC_page_was_dirty((struct hblk *)p)) return(TRUE); + p += HBLKSIZE; + } + return(FALSE); + } + } +#endif /* GC_DISABLE_INCREMENTAL */ + +/* Similar to GC_push_marked, but skip over unallocated blocks */ +/* and return address of next plausible block. */ +STATIC struct hblk * GC_push_next_marked(struct hblk *h) +{ + hdr * hhdr = HDR(h); + + if (EXPECT(IS_FORWARDING_ADDR_OR_NIL(hhdr) || HBLK_IS_FREE(hhdr), FALSE)) { + h = GC_next_used_block(h); + if (h == 0) return(0); + hhdr = GC_find_header((ptr_t)h); + } else { +# ifdef LINT2 + if (NULL == h) ABORT("Bad HDR() definition"); +# endif + } + GC_push_marked(h, hhdr); + return(h + OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz)); +} + +#ifndef GC_DISABLE_INCREMENTAL + /* Identical to above, but mark only from dirty pages */ + STATIC struct hblk * GC_push_next_marked_dirty(struct hblk *h) + { + hdr * hhdr = HDR(h); + + if (!GC_incremental) ABORT("Dirty bits not set up"); + for (;;) { + if (EXPECT(IS_FORWARDING_ADDR_OR_NIL(hhdr) + || HBLK_IS_FREE(hhdr), FALSE)) { + h = GC_next_used_block(h); + if (h == 0) return(0); + hhdr = GC_find_header((ptr_t)h); + } else { +# ifdef LINT2 + if (NULL == h) ABORT("Bad HDR() definition"); +# endif + } + if (GC_block_was_dirty(h, hhdr)) + break; + h += OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz); + hhdr = HDR(h); + } +# ifdef ENABLE_DISCLAIM + if ((hhdr -> hb_flags & MARK_UNCONDITIONALLY) != 0) { + GC_push_unconditionally(h, hhdr); + + /* Then we may ask, why not also add the MARK_UNCONDITIONALLY */ + /* case to GC_push_next_marked, which is also applied to */ + /* uncollectible blocks? But it seems to me that the function */ + /* does not need to scan uncollectible (and unconditionally */ + /* marked) blocks since those are already handled in the */ + /* MS_PUSH_UNCOLLECTABLE phase. */ + } else +# endif + /* else */ { + GC_push_marked(h, hhdr); + } + return(h + OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz)); + } +#endif /* !GC_DISABLE_INCREMENTAL */ + +/* Similar to above, but for uncollectible pages. Needed since we */ +/* do not clear marks for such pages, even for full collections. */ +STATIC struct hblk * GC_push_next_marked_uncollectable(struct hblk *h) +{ + hdr * hhdr = HDR(h); + + for (;;) { + if (EXPECT(IS_FORWARDING_ADDR_OR_NIL(hhdr) + || HBLK_IS_FREE(hhdr), FALSE)) { + h = GC_next_used_block(h); + if (h == 0) return(0); + hhdr = GC_find_header((ptr_t)h); + } else { +# ifdef LINT2 + if (NULL == h) ABORT("Bad HDR() definition"); +# endif + } + if (hhdr -> hb_obj_kind == UNCOLLECTABLE) { + GC_push_marked(h, hhdr); + break; + } +# ifdef ENABLE_DISCLAIM + if ((hhdr -> hb_flags & MARK_UNCONDITIONALLY) != 0) { + GC_push_unconditionally(h, hhdr); + break; + } +# endif + h += OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz); + hhdr = HDR(h); + } + return(h + OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz)); +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/mark_rts.c b/unity-2019.4.24f1-mbe/external/bdwgc/mark_rts.c new file mode 100644 index 000000000..0e610aa03 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/mark_rts.c @@ -0,0 +1,938 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#include "private/gc_priv.h" + +#include + +/* Data structure for list of root sets. */ +/* We keep a hash table, so that we can filter out duplicate additions. */ +/* Under Win32, we need to do a better job of filtering overlaps, so */ +/* we resort to sequential search, and pay the price. */ +/* This is really declared in gc_priv.h: +struct roots { + ptr_t r_start; + ptr_t r_end; +# if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) + struct roots * r_next; +# endif + GC_bool r_tmp; + -- Delete before registering new dynamic libraries +}; + +struct roots GC_static_roots[MAX_ROOT_SETS]; +*/ + +int GC_no_dls = 0; /* Register dynamic library data segments. */ + +static int n_root_sets = 0; + /* GC_static_roots[0..n_root_sets) contains the valid root sets. */ + +#if !defined(NO_DEBUGGING) || defined(GC_ASSERTIONS) + /* Should return the same value as GC_root_size. */ + GC_INNER word GC_compute_root_size(void) + { + int i; + word size = 0; + + for (i = 0; i < n_root_sets; i++) { + size += GC_static_roots[i].r_end - GC_static_roots[i].r_start; + } + return size; + } +#endif /* !NO_DEBUGGING || GC_ASSERTIONS */ + +#if !defined(NO_DEBUGGING) + /* For debugging: */ + void GC_print_static_roots(void) + { + int i; + word size; + + for (i = 0; i < n_root_sets; i++) { + GC_printf("From %p to %p%s\n", + (void *)GC_static_roots[i].r_start, + (void *)GC_static_roots[i].r_end, + GC_static_roots[i].r_tmp ? " (temporary)" : ""); + } + GC_printf("GC_root_size: %lu\n", (unsigned long)GC_root_size); + + if ((size = GC_compute_root_size()) != GC_root_size) + GC_err_printf("GC_root_size incorrect!! Should be: %lu\n", + (unsigned long)size); + } +#endif /* !NO_DEBUGGING */ + +#ifndef THREADS + /* Primarily for debugging support: */ + /* Is the address p in one of the registered static root sections? */ + GC_INNER GC_bool GC_is_static_root(void *p) + { + static int last_root_set = MAX_ROOT_SETS; + int i; + + if (last_root_set < n_root_sets + && (word)p >= (word)GC_static_roots[last_root_set].r_start + && (word)p < (word)GC_static_roots[last_root_set].r_end) + return(TRUE); + for (i = 0; i < n_root_sets; i++) { + if ((word)p >= (word)GC_static_roots[i].r_start + && (word)p < (word)GC_static_roots[i].r_end) { + last_root_set = i; + return(TRUE); + } + } + return(FALSE); + } +#endif /* !THREADS */ + +#if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) +/* +# define LOG_RT_SIZE 6 +# define RT_SIZE (1 << LOG_RT_SIZE) -- Power of 2, may be != MAX_ROOT_SETS + + struct roots * GC_root_index[RT_SIZE]; + -- Hash table header. Used only to check whether a range is + -- already present. + -- really defined in gc_priv.h +*/ + + GC_INLINE int rt_hash(ptr_t addr) + { + word result = (word) addr; +# if CPP_WORDSZ > 8*LOG_RT_SIZE + result ^= result >> 8*LOG_RT_SIZE; +# endif +# if CPP_WORDSZ > 4*LOG_RT_SIZE + result ^= result >> 4*LOG_RT_SIZE; +# endif + result ^= result >> 2*LOG_RT_SIZE; + result ^= result >> LOG_RT_SIZE; + result &= (RT_SIZE-1); + return(result); + } + + /* Is a range starting at b already in the table? If so return a */ + /* pointer to it, else NULL. */ + GC_INNER void * GC_roots_present(ptr_t b) + { + int h = rt_hash(b); + struct roots *p = GC_root_index[h]; + + while (p != 0) { + if (p -> r_start == (ptr_t)b) return(p); + p = p -> r_next; + } + return NULL; + } + + /* Add the given root structure to the index. */ + GC_INLINE void add_roots_to_index(struct roots *p) + { + int h = rt_hash(p -> r_start); + + p -> r_next = GC_root_index[h]; + GC_root_index[h] = p; + } +#endif /* !MSWIN32 && !MSWINCE && !CYGWIN32 */ + +GC_INNER word GC_root_size = 0; + +GC_API void GC_CALL GC_add_roots(void *b, void *e) +{ + DCL_LOCK_STATE; + + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); + LOCK(); + GC_add_roots_inner((ptr_t)b, (ptr_t)e, FALSE); + UNLOCK(); +} + + +/* Add [b,e) to the root set. Adding the same interval a second time */ +/* is a moderately fast no-op, and hence benign. We do not handle */ +/* different but overlapping intervals efficiently. (We do handle */ +/* them correctly.) */ +/* Tmp specifies that the interval may be deleted before */ +/* re-registering dynamic libraries. */ +void GC_add_roots_inner(ptr_t b, ptr_t e, GC_bool tmp) +{ + GC_ASSERT((word)b <= (word)e); + b = (ptr_t)(((word)b + (sizeof(word) - 1)) & ~(word)(sizeof(word) - 1)); + /* round b up to word boundary */ + e = (ptr_t)((word)e & ~(word)(sizeof(word) - 1)); + /* round e down to word boundary */ + if ((word)b >= (word)e) return; /* nothing to do */ + +# if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32) + /* Spend the time to ensure that there are no overlapping */ + /* or adjacent intervals. */ + /* This could be done faster with e.g. a */ + /* balanced tree. But the execution time here is */ + /* virtually guaranteed to be dominated by the time it */ + /* takes to scan the roots. */ + { + int i; + struct roots * old = NULL; /* initialized to prevent warning. */ + + for (i = 0; i < n_root_sets; i++) { + old = GC_static_roots + i; + if ((word)b <= (word)old->r_end + && (word)e >= (word)old->r_start) { + if ((word)b < (word)old->r_start) { + GC_root_size += old->r_start - b; + old -> r_start = b; + } + if ((word)e > (word)old->r_end) { + GC_root_size += e - old->r_end; + old -> r_end = e; + } + old -> r_tmp &= tmp; + break; + } + } + if (i < n_root_sets) { + /* merge other overlapping intervals */ + struct roots *other; + + for (i++; i < n_root_sets; i++) { + other = GC_static_roots + i; + b = other -> r_start; + e = other -> r_end; + if ((word)b <= (word)old->r_end + && (word)e >= (word)old->r_start) { + if ((word)b < (word)old->r_start) { + GC_root_size += old->r_start - b; + old -> r_start = b; + } + if ((word)e > (word)old->r_end) { + GC_root_size += e - old->r_end; + old -> r_end = e; + } + old -> r_tmp &= other -> r_tmp; + /* Delete this entry. */ + GC_root_size -= (other -> r_end - other -> r_start); + other -> r_start = GC_static_roots[n_root_sets-1].r_start; + other -> r_end = GC_static_roots[n_root_sets-1].r_end; + n_root_sets--; + } + } + return; + } + } +# else + { + struct roots * old = (struct roots *)GC_roots_present(b); + + if (old != 0) { + if ((word)e <= (word)old->r_end) { + old -> r_tmp &= tmp; + return; /* already there */ + } + if (old -> r_tmp == tmp || !tmp) { + /* Extend the existing root. */ + GC_root_size += e - old -> r_end; + old -> r_end = e; + old -> r_tmp = tmp; + return; + } + b = old -> r_end; + } + } +# endif + if (n_root_sets == MAX_ROOT_SETS) { + ABORT("Too many root sets"); + } + +# ifdef DEBUG_ADD_DEL_ROOTS + GC_log_printf("Adding data root section %d: %p .. %p%s\n", + n_root_sets, (void *)b, (void *)e, + tmp ? " (temporary)" : ""); +# endif + GC_static_roots[n_root_sets].r_start = (ptr_t)b; + GC_static_roots[n_root_sets].r_end = (ptr_t)e; + GC_static_roots[n_root_sets].r_tmp = tmp; +# if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) + GC_static_roots[n_root_sets].r_next = 0; + add_roots_to_index(GC_static_roots + n_root_sets); +# endif + GC_root_size += e - b; + n_root_sets++; +} + +static GC_bool roots_were_cleared = FALSE; + +GC_API void GC_CALL GC_clear_roots(void) +{ + DCL_LOCK_STATE; + + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); + LOCK(); + roots_were_cleared = TRUE; + n_root_sets = 0; + GC_root_size = 0; +# if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) + BZERO(GC_root_index, RT_SIZE * sizeof(void *)); +# endif +# ifdef DEBUG_ADD_DEL_ROOTS + GC_log_printf("Clear all data root sections\n"); +# endif + UNLOCK(); +} + +/* Internal use only; lock held. */ +STATIC void GC_remove_root_at_pos(int i) +{ +# ifdef DEBUG_ADD_DEL_ROOTS + GC_log_printf("Remove data root section at %d: %p .. %p%s\n", + i, (void *)GC_static_roots[i].r_start, + (void *)GC_static_roots[i].r_end, + GC_static_roots[i].r_tmp ? " (temporary)" : ""); +# endif + GC_root_size -= (GC_static_roots[i].r_end - GC_static_roots[i].r_start); + GC_static_roots[i].r_start = GC_static_roots[n_root_sets-1].r_start; + GC_static_roots[i].r_end = GC_static_roots[n_root_sets-1].r_end; + GC_static_roots[i].r_tmp = GC_static_roots[n_root_sets-1].r_tmp; + n_root_sets--; +} + +#if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) + STATIC void GC_rebuild_root_index(void) + { + int i; + BZERO(GC_root_index, RT_SIZE * sizeof(void *)); + for (i = 0; i < n_root_sets; i++) + add_roots_to_index(GC_static_roots + i); + } +#endif + +#if defined(DYNAMIC_LOADING) || defined(MSWIN32) || defined(MSWINCE) \ + || defined(PCR) || defined(CYGWIN32) +/* Internal use only; lock held. */ +STATIC void GC_remove_tmp_roots(void) +{ + int i; +# if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) + int old_n_roots = n_root_sets; +# endif + + for (i = 0; i < n_root_sets; ) { + if (GC_static_roots[i].r_tmp) { + GC_remove_root_at_pos(i); + } else { + i++; + } + } +# if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) + if (n_root_sets < old_n_roots) + GC_rebuild_root_index(); +# endif +} +#endif + +#if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) + STATIC void GC_remove_roots_inner(ptr_t b, ptr_t e); + + GC_API void GC_CALL GC_remove_roots(void *b, void *e) + { + DCL_LOCK_STATE; + + /* Quick check whether has nothing to do */ + if ((((word)b + (sizeof(word) - 1)) & ~(word)(sizeof(word) - 1)) >= + ((word)e & ~(word)(sizeof(word) - 1))) + return; + + LOCK(); + GC_remove_roots_inner((ptr_t)b, (ptr_t)e); + UNLOCK(); + } + + /* Should only be called when the lock is held */ + STATIC void GC_remove_roots_inner(ptr_t b, ptr_t e) + { + int i; + GC_bool rebuild = FALSE; + + for (i = 0; i < n_root_sets; ) { + if ((word)GC_static_roots[i].r_start >= (word)b + && (word)GC_static_roots[i].r_end <= (word)e) { + GC_remove_root_at_pos(i); + rebuild = TRUE; + } else { + i++; + } + } + if (rebuild) + GC_rebuild_root_index(); + } +#endif /* !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) */ + +#ifdef USE_PROC_FOR_LIBRARIES + /* Remove given range from every static root which intersects with */ + /* the range. It is assumed GC_remove_tmp_roots is called before */ + /* this function is called repeatedly by GC_register_map_entries. */ + GC_INNER void GC_remove_roots_subregion(ptr_t b, ptr_t e) + { + int i; + GC_bool rebuild = FALSE; + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT((word)b % sizeof(word) == 0 && (word)e % sizeof(word) == 0); + for (i = 0; i < n_root_sets; i++) { + ptr_t r_start, r_end; + + if (GC_static_roots[i].r_tmp) { + /* The remaining roots are skipped as they are all temporary. */ +# ifdef GC_ASSERTIONS + int j; + for (j = i + 1; j < n_root_sets; j++) { + GC_ASSERT(GC_static_roots[j].r_tmp); + } +# endif + break; + } + r_start = GC_static_roots[i].r_start; + r_end = GC_static_roots[i].r_end; + if (!EXPECT((word)e <= (word)r_start || (word)r_end <= (word)b, TRUE)) { +# ifdef DEBUG_ADD_DEL_ROOTS + GC_log_printf("Removing %p .. %p from root section %d (%p .. %p)\n", + (void *)b, (void *)e, + i, (void *)r_start, (void *)r_end); +# endif + if ((word)r_start < (word)b) { + GC_root_size -= r_end - b; + GC_static_roots[i].r_end = b; + /* No need to rebuild as hash does not use r_end value. */ + if ((word)e < (word)r_end) { + int j; + + if (rebuild) { + GC_rebuild_root_index(); + rebuild = FALSE; + } + GC_add_roots_inner(e, r_end, FALSE); /* updates n_root_sets */ + for (j = i + 1; j < n_root_sets; j++) + if (GC_static_roots[j].r_tmp) + break; + if (j < n_root_sets-1 && !GC_static_roots[n_root_sets-1].r_tmp) { + /* Exchange the roots to have all temporary ones at the end. */ + ptr_t tmp_r_start = GC_static_roots[j].r_start; + ptr_t tmp_r_end = GC_static_roots[j].r_end; + + GC_static_roots[j].r_start = + GC_static_roots[n_root_sets-1].r_start; + GC_static_roots[j].r_end = GC_static_roots[n_root_sets-1].r_end; + GC_static_roots[j].r_tmp = FALSE; + GC_static_roots[n_root_sets-1].r_start = tmp_r_start; + GC_static_roots[n_root_sets-1].r_end = tmp_r_end; + GC_static_roots[n_root_sets-1].r_tmp = TRUE; + rebuild = TRUE; + } + } + } else { + if ((word)e < (word)r_end) { + GC_root_size -= e - r_start; + GC_static_roots[i].r_start = e; + } else { + GC_remove_root_at_pos(i); + i--; + } + rebuild = TRUE; + } + } + } + if (rebuild) + GC_rebuild_root_index(); + } +#endif /* USE_PROC_FOR_LIBRARIES */ + +#if !defined(NO_DEBUGGING) + /* For the debugging purpose only. */ + /* Workaround for the OS mapping and unmapping behind our back: */ + /* Is the address p in one of the temporary static root sections? */ + GC_API int GC_CALL GC_is_tmp_root(void *p) + { + static int last_root_set = MAX_ROOT_SETS; + int i; + + if (last_root_set < n_root_sets + && (word)p >= (word)GC_static_roots[last_root_set].r_start + && (word)p < (word)GC_static_roots[last_root_set].r_end) + return GC_static_roots[last_root_set].r_tmp; + for (i = 0; i < n_root_sets; i++) { + if ((word)p >= (word)GC_static_roots[i].r_start + && (word)p < (word)GC_static_roots[i].r_end) { + last_root_set = i; + return GC_static_roots[i].r_tmp; + } + } + return(FALSE); + } +#endif /* !NO_DEBUGGING */ + +GC_INNER ptr_t GC_approx_sp(void) +{ + volatile word sp; +# if (defined(CPPCHECK) || (__GNUC__ >= 4)) && !defined(STACK_NOT_SCANNED) /* GC_GNUC_PREREQ(4, 0) */ + /* TODO: Use GC_GNUC_PREREQ after fixing a bug in cppcheck. */ + sp = (word)__builtin_frame_address(0); +# else + sp = (word)&sp; +# endif + /* Also force stack to grow if necessary. Otherwise the */ + /* later accesses might cause the kernel to think we're */ + /* doing something wrong. */ + return((ptr_t)sp); +} + +/* + * Data structure for excluded static roots. + * Real declaration is in gc_priv.h. + +struct exclusion { + ptr_t e_start; + ptr_t e_end; +}; + +struct exclusion GC_excl_table[MAX_EXCLUSIONS]; + -- Array of exclusions, ascending + -- address order. +*/ + +STATIC size_t GC_excl_table_entries = 0;/* Number of entries in use. */ + +/* Return the first exclusion range that includes an address >= start_addr */ +/* Assumes the exclusion table contains at least one entry (namely the */ +/* GC data structures). */ +STATIC struct exclusion * GC_next_exclusion(ptr_t start_addr) +{ + size_t low = 0; + size_t high = GC_excl_table_entries - 1; + + while (high > low) { + size_t mid = (low + high) >> 1; + + /* low <= mid < high */ + if ((word) GC_excl_table[mid].e_end <= (word) start_addr) { + low = mid + 1; + } else { + high = mid; + } + } + if ((word) GC_excl_table[low].e_end <= (word) start_addr) return 0; + return GC_excl_table + low; +} + +/* Should only be called when the lock is held. The range boundaries */ +/* should be properly aligned and valid. */ +GC_INNER void GC_exclude_static_roots_inner(void *start, void *finish) +{ + struct exclusion * next; + size_t next_index; + + GC_ASSERT((word)start % sizeof(word) == 0); + GC_ASSERT((word)start < (word)finish); + + if (0 == GC_excl_table_entries) { + next = 0; + } else { + next = GC_next_exclusion((ptr_t)start); + } + if (0 != next) { + size_t i; + + if ((word)(next -> e_start) < (word) finish) { + /* incomplete error check. */ + ABORT("Exclusion ranges overlap"); + } + if ((word)(next -> e_start) == (word) finish) { + /* extend old range backwards */ + next -> e_start = (ptr_t)start; + return; + } + next_index = next - GC_excl_table; + for (i = GC_excl_table_entries; i > next_index; --i) { + GC_excl_table[i] = GC_excl_table[i-1]; + } + } else { + next_index = GC_excl_table_entries; + } + if (GC_excl_table_entries == MAX_EXCLUSIONS) ABORT("Too many exclusions"); + GC_excl_table[next_index].e_start = (ptr_t)start; + GC_excl_table[next_index].e_end = (ptr_t)finish; + ++GC_excl_table_entries; +} + +GC_API void GC_CALL GC_exclude_static_roots(void *b, void *e) +{ + DCL_LOCK_STATE; + + if (b == e) return; /* nothing to exclude? */ + + /* Round boundaries (in direction reverse to that of GC_add_roots). */ + b = (void *)((word)b & ~(word)(sizeof(word) - 1)); + e = (void *)(((word)e + (sizeof(word) - 1)) & ~(word)(sizeof(word) - 1)); + if (NULL == e) + e = (void *)(~(word)(sizeof(word) - 1)); /* handle overflow */ + + LOCK(); + GC_exclude_static_roots_inner(b, e); + UNLOCK(); +} + +#if defined(WRAP_MARK_SOME) && defined(PARALLEL_MARK) +# define GC_PUSH_CONDITIONAL(b, t, all) \ + (GC_parallel \ + ? GC_push_conditional_eager(b, t, all) \ + : GC_push_conditional(b, t, all)) +#elif defined(GC_DISABLE_INCREMENTAL) +# define GC_PUSH_CONDITIONAL(b, t, all) GC_push_all(b, t) +#else +# define GC_PUSH_CONDITIONAL(b, t, all) GC_push_conditional(b, t, all) + /* Do either of GC_push_all or GC_push_selected */ + /* depending on the third arg. */ +#endif + +/* Invoke push_conditional on ranges that are not excluded. */ +STATIC void GC_push_conditional_with_exclusions(ptr_t bottom, ptr_t top, + GC_bool all GC_ATTR_UNUSED) +{ + while ((word)bottom < (word)top) { + struct exclusion *next = GC_next_exclusion(bottom); + ptr_t excl_start; + + if (0 == next + || (word)(excl_start = next -> e_start) >= (word)top) { + GC_PUSH_CONDITIONAL(bottom, top, all); + break; + } + if ((word)excl_start > (word)bottom) + GC_PUSH_CONDITIONAL(bottom, excl_start, all); + bottom = next -> e_end; + } +} + +#ifdef IA64 + /* Similar to GC_push_all_stack_sections() but for IA-64 registers store. */ + GC_INNER void GC_push_all_register_sections(ptr_t bs_lo, ptr_t bs_hi, + int eager, struct GC_traced_stack_sect_s *traced_stack_sect) + { + while (traced_stack_sect != NULL) { + ptr_t frame_bs_lo = traced_stack_sect -> backing_store_end; + GC_ASSERT((word)frame_bs_lo <= (word)bs_hi); + if (eager) { + GC_push_all_eager(frame_bs_lo, bs_hi); + } else { + GC_push_all_stack(frame_bs_lo, bs_hi); + } + bs_hi = traced_stack_sect -> saved_backing_store_ptr; + traced_stack_sect = traced_stack_sect -> prev; + } + GC_ASSERT((word)bs_lo <= (word)bs_hi); + if (eager) { + GC_push_all_eager(bs_lo, bs_hi); + } else { + GC_push_all_stack(bs_lo, bs_hi); + } + } +#endif /* IA64 */ + +#ifdef THREADS + +GC_INNER void GC_push_all_stack_sections(ptr_t lo, ptr_t hi, + struct GC_traced_stack_sect_s *traced_stack_sect) +{ + while (traced_stack_sect != NULL) { + GC_ASSERT((word)lo HOTTER_THAN (word)traced_stack_sect); +# ifdef STACK_GROWS_UP + GC_push_all_stack((ptr_t)traced_stack_sect, lo); +# else /* STACK_GROWS_DOWN */ + GC_push_all_stack(lo, (ptr_t)traced_stack_sect); +# endif + lo = traced_stack_sect -> saved_stack_ptr; + GC_ASSERT(lo != NULL); + traced_stack_sect = traced_stack_sect -> prev; + } + GC_ASSERT(!((word)hi HOTTER_THAN (word)lo)); +# ifdef STACK_GROWS_UP + /* We got them backwards! */ + GC_push_all_stack(hi, lo); +# else /* STACK_GROWS_DOWN */ + GC_push_all_stack(lo, hi); +# endif +} + +#else /* !THREADS */ + + /* Similar to GC_push_all_eager, but only the */ + /* part hotter than cold_gc_frame is scanned */ + /* immediately. Needed to ensure that callee- */ + /* save registers are not missed. */ +/* + * A version of GC_push_all that treats all interior pointers as valid + * and scans part of the area immediately, to make sure that saved + * register values are not lost. + * Cold_gc_frame delimits the stack section that must be scanned + * eagerly. A zero value indicates that no eager scanning is needed. + * We don't need to worry about the MANUAL_VDB case here, since this + * is only called in the single-threaded case. We assume that we + * cannot collect between an assignment and the corresponding + * GC_dirty() call. + */ +STATIC void GC_push_all_stack_partially_eager(ptr_t bottom, ptr_t top, + ptr_t cold_gc_frame) +{ +#ifndef NEED_FIXUP_POINTER + if (GC_all_interior_pointers) { + /* Push the hot end of the stack eagerly, so that register values */ + /* saved inside GC frames are marked before they disappear. */ + /* The rest of the marking can be deferred until later. */ + if (0 == cold_gc_frame) { + GC_push_all_stack(bottom, top); + return; + } + GC_ASSERT((word)bottom <= (word)cold_gc_frame + && (word)cold_gc_frame <= (word)top); +# ifdef STACK_GROWS_DOWN + GC_push_all(cold_gc_frame - sizeof(ptr_t), top); + GC_push_all_eager(bottom, cold_gc_frame); +# else /* STACK_GROWS_UP */ + GC_push_all(bottom, cold_gc_frame + sizeof(ptr_t)); + GC_push_all_eager(cold_gc_frame, top); +# endif /* STACK_GROWS_UP */ + } else +#endif + /* else */ { + GC_push_all_eager(bottom, top); + } +# ifdef TRACE_BUF + GC_add_trace_entry("GC_push_all_stack", (word)bottom, (word)top); +# endif +} + +/* Similar to GC_push_all_stack_sections() but also uses cold_gc_frame. */ +STATIC void GC_push_all_stack_part_eager_sections(ptr_t lo, ptr_t hi, + ptr_t cold_gc_frame, struct GC_traced_stack_sect_s *traced_stack_sect) +{ + GC_ASSERT(traced_stack_sect == NULL || cold_gc_frame == NULL || + (word)cold_gc_frame HOTTER_THAN (word)traced_stack_sect); + + while (traced_stack_sect != NULL) { + GC_ASSERT((word)lo HOTTER_THAN (word)traced_stack_sect); +# ifdef STACK_GROWS_UP + GC_push_all_stack_partially_eager((ptr_t)traced_stack_sect, lo, + cold_gc_frame); +# else /* STACK_GROWS_DOWN */ + GC_push_all_stack_partially_eager(lo, (ptr_t)traced_stack_sect, + cold_gc_frame); +# endif + lo = traced_stack_sect -> saved_stack_ptr; + GC_ASSERT(lo != NULL); + traced_stack_sect = traced_stack_sect -> prev; + cold_gc_frame = NULL; /* Use at most once. */ + } + + GC_ASSERT(!((word)hi HOTTER_THAN (word)lo)); +# ifdef STACK_GROWS_UP + /* We got them backwards! */ + GC_push_all_stack_partially_eager(hi, lo, cold_gc_frame); +# else /* STACK_GROWS_DOWN */ + GC_push_all_stack_partially_eager(lo, hi, cold_gc_frame); +# endif +} + +#endif /* !THREADS */ + + /* Push enough of the current stack eagerly to */ + /* ensure that callee-save registers saved in */ + /* GC frames are scanned. */ + /* In the non-threads case, schedule entire */ + /* stack for scanning. */ + /* The second argument is a pointer to the */ + /* (possibly null) thread context, for */ + /* (currently hypothetical) more precise */ + /* stack scanning. */ +/* + * In the absence of threads, push the stack contents. + * In the presence of threads, push enough of the current stack + * to ensure that callee-save registers saved in collector frames have been + * seen. + * FIXME: Merge with per-thread stuff. + */ +STATIC void GC_push_current_stack(ptr_t cold_gc_frame, + void * context GC_ATTR_UNUSED) +{ +# if defined(THREADS) + if (0 == cold_gc_frame) return; +# ifdef STACK_GROWS_DOWN + GC_push_all_eager(GC_approx_sp(), cold_gc_frame); + /* For IA64, the register stack backing store is handled */ + /* in the thread-specific code. */ +# else + GC_push_all_eager(cold_gc_frame, GC_approx_sp()); +# endif +# else + GC_push_all_stack_part_eager_sections(GC_approx_sp(), GC_stackbottom, + cold_gc_frame, GC_traced_stack_sect); +# ifdef IA64 + /* We also need to push the register stack backing store. */ + /* This should really be done in the same way as the */ + /* regular stack. For now we fudge it a bit. */ + /* Note that the backing store grows up, so we can't use */ + /* GC_push_all_stack_partially_eager. */ + { + ptr_t bsp = GC_save_regs_ret_val; + ptr_t cold_gc_bs_pointer = bsp - 2048; + if (GC_all_interior_pointers + && (word)cold_gc_bs_pointer > (word)BACKING_STORE_BASE) { + /* Adjust cold_gc_bs_pointer if below our innermost */ + /* "traced stack section" in backing store. */ + if (GC_traced_stack_sect != NULL + && (word)cold_gc_bs_pointer + < (word)GC_traced_stack_sect->backing_store_end) + cold_gc_bs_pointer = + GC_traced_stack_sect->backing_store_end; + GC_push_all_register_sections(BACKING_STORE_BASE, + cold_gc_bs_pointer, FALSE, GC_traced_stack_sect); + GC_push_all_eager(cold_gc_bs_pointer, bsp); + } else { + GC_push_all_register_sections(BACKING_STORE_BASE, bsp, + TRUE /* eager */, GC_traced_stack_sect); + } + /* All values should be sufficiently aligned that we */ + /* don't have to worry about the boundary. */ + } +# endif +# endif /* !THREADS */ +} + +GC_INNER void (*GC_push_typed_structures)(void) = 0; + + /* Push GC internal roots. These are normally */ + /* included in the static data segment, and */ + /* Thus implicitly pushed. But we must do this */ + /* explicitly if normal root processing is */ + /* disabled. */ +/* + * Push GC internal roots. Only called if there is some reason to believe + * these would not otherwise get registered. + */ +STATIC void GC_push_gc_structures(void) +{ +# ifndef GC_NO_FINALIZATION + GC_push_finalizer_structures(); +# endif +# if defined(THREADS) + GC_push_thread_structures(); +# endif + if( GC_push_typed_structures ) + GC_push_typed_structures(); +} + +GC_INNER void GC_cond_register_dynamic_libraries(void) +{ +# if (defined(DYNAMIC_LOADING) && !defined(MSWIN_XBOX1)) \ + || defined(CYGWIN32) || defined(MSWIN32) || defined(MSWINCE) \ + || defined(PCR) + GC_remove_tmp_roots(); + if (!GC_no_dls) GC_register_dynamic_libraries(); +# else + GC_no_dls = TRUE; +# endif +} + +STATIC void GC_push_regs_and_stack(ptr_t cold_gc_frame) +{ + GC_with_callee_saves_pushed(GC_push_current_stack, cold_gc_frame); +} + +/* + * Call the mark routines (GC_push_one for a single pointer, + * GC_push_conditional on groups of pointers) on every top level + * accessible pointer. + * If all is FALSE, arrange to push only possibly altered values. + * Cold_gc_frame is an address inside a GC frame that + * remains valid until all marking is complete. + * A zero value indicates that it's OK to miss some + * register values. + */ +GC_INNER void GC_push_roots(GC_bool all, ptr_t cold_gc_frame GC_ATTR_UNUSED) +{ + int i; + unsigned kind; + + /* + * Next push static data. This must happen early on, since it's + * not robust against mark stack overflow. + */ + /* Re-register dynamic libraries, in case one got added. */ + /* There is some argument for doing this as late as possible, */ + /* especially on win32, where it can change asynchronously. */ + /* In those cases, we do it here. But on other platforms, it's */ + /* not safe with the world stopped, so we do it earlier. */ +# if !defined(REGISTER_LIBRARIES_EARLY) + GC_cond_register_dynamic_libraries(); +# endif + + /* Mark everything in static data areas */ + for (i = 0; i < n_root_sets; i++) { + GC_push_conditional_with_exclusions( + GC_static_roots[i].r_start, + GC_static_roots[i].r_end, all); + } + + /* Mark all free list header blocks, if those were allocated from */ + /* the garbage collected heap. This makes sure they don't */ + /* disappear if we are not marking from static data. It also */ + /* saves us the trouble of scanning them, and possibly that of */ + /* marking the freelists. */ + for (kind = 0; kind < GC_n_kinds; kind++) { + void *base = GC_base(GC_obj_kinds[kind].ok_freelist); + if (0 != base) { + GC_set_mark_bit(base); + } + } + + /* Mark from GC internal roots if those might otherwise have */ + /* been excluded. */ + if (GC_no_dls || roots_were_cleared) { + GC_push_gc_structures(); + } + + /* Mark thread local free lists, even if their mark */ + /* descriptor excludes the link field. */ + /* If the world is not stopped, this is unsafe. It is */ + /* also unnecessary, since we will do this again with the */ + /* world stopped. */ +# if defined(THREAD_LOCAL_ALLOC) + if (GC_world_stopped) GC_mark_thread_local_free_lists(); +# endif + + /* + * Now traverse stacks, and mark from register contents. + * These must be done last, since they can legitimately overflow + * the mark stack. + * This is usually done by saving the current context on the + * stack, and then just tracing from the stack. + */ +# ifndef STACK_NOT_SCANNED + GC_push_regs_and_stack(cold_gc_frame); +# endif + + if (GC_push_other_roots != 0) (*GC_push_other_roots)(); + /* In the threads case, this also pushes thread stacks. */ + /* Note that without interior pointer recognition lots */ + /* of stuff may have been pushed already, and this */ + /* should be careful about mark stack overflows. */ +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/misc.c b/unity-2019.4.24f1-mbe/external/bdwgc/misc.c new file mode 100644 index 000000000..dc2fbe810 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/misc.c @@ -0,0 +1,2598 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1999-2001 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#include "private/gc_pmark.h" +#include "private/gc_priv.h" + +#include +#include +#include + +#ifndef MSWINCE +# include +#endif + +#ifdef GC_SOLARIS_THREADS +# include +#endif + +#if defined(MSWIN32) || defined(MSWINCE) \ + || (defined(CYGWIN32) && defined(GC_READ_ENV_FILE)) +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +# endif +# define NOSERVICE +# include +#ifdef MSWINRT +# include + // This API is defined in roapi.h, but we cannot include it here since it does not compile in C -_- + DECLSPEC_IMPORT HRESULT WINAPI RoGetActivationFactory(HSTRING activatableClassId, REFIID iid, void** factory); +#endif +#endif + +#if defined(UNIX_LIKE) || defined(CYGWIN32) || defined(SYMBIAN) +# include +# include +# include +#endif + +#ifdef NONSTOP +# include +#endif + +#ifdef THREADS +# ifdef PCR +# include "il/PCR_IL.h" + GC_INNER PCR_Th_ML GC_allocate_ml; +# elif defined(SN_TARGET_PSP2) + GC_INNER WapiMutex GC_allocate_ml_PSP2 = { 0, NULL }; +# elif defined(SN_TARGET_ORBIS) || defined(SN_TARGET_PS3) +# include + GC_INNER pthread_mutex_t GC_allocate_ml; +# endif + /* For other platforms with threads, the lock and possibly */ + /* GC_lock_holder variables are defined in the thread support code. */ +#endif /* THREADS */ + +#ifdef DYNAMIC_LOADING + /* We need to register the main data segment. Returns TRUE unless */ + /* this is done implicitly as part of dynamic library registration. */ +# define GC_REGISTER_MAIN_STATIC_DATA() GC_register_main_static_data() +#elif defined(GC_DONT_REGISTER_MAIN_STATIC_DATA) +# define GC_REGISTER_MAIN_STATIC_DATA() FALSE +#else + /* Don't unnecessarily call GC_register_main_static_data() in case */ + /* dyn_load.c isn't linked in. */ +# define GC_REGISTER_MAIN_STATIC_DATA() TRUE +#endif + +#ifdef NEED_CANCEL_DISABLE_COUNT + __thread unsigned char GC_cancel_disable_count = 0; +#endif + +GC_FAR struct _GC_arrays GC_arrays /* = { 0 } */; + +GC_INNER GC_bool GC_debugging_started = FALSE; + /* defined here so we don't have to load dbg_mlc.o */ + +ptr_t GC_stackbottom = 0; + +#ifdef IA64 + ptr_t GC_register_stackbottom = 0; +#endif + +int GC_dont_gc = FALSE; + +int GC_dont_precollect = FALSE; + +GC_bool GC_quiet = 0; /* used also in pcr_interface.c */ + +#if !defined(NO_CLOCK) || !defined(SMALL_CONFIG) + int GC_print_stats = 0; +#endif + +#ifdef GC_PRINT_BACK_HEIGHT + GC_INNER GC_bool GC_print_back_height = TRUE; +#else + GC_INNER GC_bool GC_print_back_height = FALSE; +#endif + +#ifndef NO_DEBUGGING +# ifdef GC_DUMP_REGULARLY + GC_INNER GC_bool GC_dump_regularly = TRUE; + /* Generate regular debugging dumps. */ +# else + GC_INNER GC_bool GC_dump_regularly = FALSE; +# endif +# ifndef NO_CLOCK + STATIC CLOCK_TYPE GC_init_time; + /* The time that the GC was initialized at. */ +# endif +#endif /* !NO_DEBUGGING */ + +#ifdef KEEP_BACK_PTRS + GC_INNER long GC_backtraces = 0; + /* Number of random backtraces to generate for each GC. */ +#endif + +#ifdef FIND_LEAK + int GC_find_leak = 1; +#else + int GC_find_leak = 0; +#endif + +#ifndef SHORT_DBG_HDRS +# ifdef GC_FINDLEAK_DELAY_FREE + GC_INNER GC_bool GC_findleak_delay_free = TRUE; +# else + GC_INNER GC_bool GC_findleak_delay_free = FALSE; +# endif +#endif /* !SHORT_DBG_HDRS */ + +#ifdef ALL_INTERIOR_POINTERS + int GC_all_interior_pointers = 1; +#else + int GC_all_interior_pointers = 0; +#endif + +#ifdef FINALIZE_ON_DEMAND + int GC_finalize_on_demand = 1; +#else + int GC_finalize_on_demand = 0; +#endif + +#ifdef JAVA_FINALIZATION + int GC_java_finalization = 1; +#else + int GC_java_finalization = 0; +#endif + +/* All accesses to it should be synchronized to avoid data races. */ +GC_finalizer_notifier_proc GC_finalizer_notifier = + (GC_finalizer_notifier_proc)0; + +#ifdef GC_FORCE_UNMAP_ON_GCOLLECT + /* Has no effect unless USE_MUNMAP. */ + /* Has no effect on implicitly-initiated garbage collections. */ + GC_INNER GC_bool GC_force_unmap_on_gcollect = TRUE; +#else + GC_INNER GC_bool GC_force_unmap_on_gcollect = FALSE; +#endif + +#ifndef GC_LARGE_ALLOC_WARN_INTERVAL +# define GC_LARGE_ALLOC_WARN_INTERVAL 5 +#endif +GC_INNER long GC_large_alloc_warn_interval = GC_LARGE_ALLOC_WARN_INTERVAL; + /* Interval between unsuppressed warnings. */ + +STATIC void * GC_CALLBACK GC_default_oom_fn( + size_t bytes_requested GC_ATTR_UNUSED) +{ + return(0); +} + +/* All accesses to it should be synchronized to avoid data races. */ +GC_oom_func GC_oom_fn = GC_default_oom_fn; + +#ifdef CAN_HANDLE_FORK +# ifdef HANDLE_FORK + GC_INNER int GC_handle_fork = 1; + /* The value is examined by GC_thr_init. */ +# else + GC_INNER int GC_handle_fork = FALSE; +# endif + +#elif !defined(HAVE_NO_FORK) + + /* Same as above but with GC_CALL calling conventions. */ + GC_API void GC_CALL GC_atfork_prepare(void) + { +# ifdef THREADS + ABORT("fork() handling unsupported"); +# endif + } + + GC_API void GC_CALL GC_atfork_parent(void) + { + /* empty */ + } + + GC_API void GC_CALL GC_atfork_child(void) + { + /* empty */ + } +#endif /* !CAN_HANDLE_FORK && !HAVE_NO_FORK */ + +/* Overrides the default automatic handle-fork mode. Has effect only */ +/* if called before GC_INIT. */ +GC_API void GC_CALL GC_set_handle_fork(int value GC_ATTR_UNUSED) +{ +# ifdef CAN_HANDLE_FORK + if (!GC_is_initialized) + GC_handle_fork = value >= -1 ? value : 1; + /* Map all negative values except for -1 to a positive one. */ +# elif defined(THREADS) || (defined(DARWIN) && defined(MPROTECT_VDB)) + if (!GC_is_initialized && value) { +# ifndef SMALL_CONFIG + GC_init(); /* just to initialize GC_stderr */ +# endif + ABORT("fork() handling unsupported"); + } +# else + /* No at-fork handler is needed in the single-threaded mode. */ +# endif +} + +/* Set things up so that GC_size_map[i] >= granules(i), */ +/* but not too much bigger */ +/* and so that size_map contains relatively few distinct entries */ +/* This was originally stolen from Russ Atkinson's Cedar */ +/* quantization algorithm (but we precompute it). */ +STATIC void GC_init_size_map(void) +{ + size_t i; + + /* Map size 0 to something bigger. */ + /* This avoids problems at lower levels. */ + GC_size_map[0] = 1; + for (i = 1; i <= GRANULES_TO_BYTES(TINY_FREELISTS-1) - EXTRA_BYTES; i++) { + GC_size_map[i] = ROUNDED_UP_GRANULES(i); +# ifndef _MSC_VER + GC_ASSERT(GC_size_map[i] < TINY_FREELISTS); + /* Seems to tickle bug in VC++ 2008 for AMD64 */ +# endif + } + /* We leave the rest of the array to be filled in on demand. */ +} + +/* + * The following is a gross hack to deal with a problem that can occur + * on machines that are sloppy about stack frame sizes, notably SPARC. + * Bogus pointers may be written to the stack and not cleared for + * a LONG time, because they always fall into holes in stack frames + * that are not written. We partially address this by clearing + * sections of the stack whenever we get control. + */ + +#ifndef SMALL_CLEAR_SIZE +# define SMALL_CLEAR_SIZE 256 /* Clear this much every time. */ +#endif + +#if defined(ALWAYS_SMALL_CLEAR_STACK) || defined(STACK_NOT_SCANNED) + GC_API void * GC_CALL GC_clear_stack(void *arg) + { +# ifndef STACK_NOT_SCANNED + word volatile dummy[SMALL_CLEAR_SIZE]; + BZERO((/* no volatile */ void *)dummy, sizeof(dummy)); +# endif + return arg; + } +#else + +# ifdef THREADS +# define BIG_CLEAR_SIZE 2048 /* Clear this much now and then. */ +# else + STATIC word GC_stack_last_cleared = 0; /* GC_no when we last did this */ + STATIC ptr_t GC_min_sp = NULL; + /* Coolest stack pointer value from which */ + /* we've already cleared the stack. */ + STATIC ptr_t GC_high_water = NULL; + /* "hottest" stack pointer value we have seen */ + /* recently. Degrades over time. */ + STATIC word GC_bytes_allocd_at_reset = 0; +# define DEGRADE_RATE 50 +# endif + +# if defined(ASM_CLEAR_CODE) + void *GC_clear_stack_inner(void *, ptr_t); +# else + /* Clear the stack up to about limit. Return arg. This function */ + /* is not static because it could also be erroneously defined in .S */ + /* file, so this error would be caught by the linker. */ + void *GC_clear_stack_inner(void *arg, +# if defined(__APPLE_CC__) && !GC_CLANG_PREREQ(6, 0) + volatile /* to workaround some bug */ +# endif + ptr_t limit) + { +# define CLEAR_SIZE 213 /* granularity */ + volatile word dummy[CLEAR_SIZE]; + + BZERO((/* no volatile */ void *)dummy, sizeof(dummy)); + if ((word)GC_approx_sp() COOLER_THAN (word)limit) { + (void)GC_clear_stack_inner(arg, limit); + } + /* Make sure the recursive call is not a tail call, and the bzero */ + /* call is not recognized as dead code. */ + GC_noop1((word)dummy); + return(arg); + } +# endif /* !ASM_CLEAR_CODE */ + +# ifdef THREADS + /* Used to occasionally clear a bigger chunk. */ + /* TODO: Should be more random than it is ... */ + GC_ATTR_NO_SANITIZE_THREAD + static unsigned next_random_no(void) + { + static unsigned random_no = 0; + return ++random_no % 13; + } +# endif /* THREADS */ + +/* Clear some of the inaccessible part of the stack. Returns its */ +/* argument, so it can be used in a tail call position, hence clearing */ +/* another frame. */ + GC_API void * GC_CALL GC_clear_stack(void *arg) + { + ptr_t sp = GC_approx_sp(); /* Hotter than actual sp */ +# ifdef THREADS + word volatile dummy[SMALL_CLEAR_SIZE]; +# endif + +# define SLOP 400 + /* Extra bytes we clear every time. This clears our own */ + /* activation record, and should cause more frequent */ + /* clearing near the cold end of the stack, a good thing. */ +# define GC_SLOP 4000 + /* We make GC_high_water this much hotter than we really saw */ + /* it, to cover for GC noise etc. above our current frame. */ +# define CLEAR_THRESHOLD 100000 + /* We restart the clearing process after this many bytes of */ + /* allocation. Otherwise very heavily recursive programs */ + /* with sparse stacks may result in heaps that grow almost */ + /* without bounds. As the heap gets larger, collection */ + /* frequency decreases, thus clearing frequency would decrease, */ + /* thus more junk remains accessible, thus the heap gets */ + /* larger ... */ +# ifdef THREADS + if (next_random_no() == 0) { + ptr_t limit = sp; + + MAKE_HOTTER(limit, BIG_CLEAR_SIZE*sizeof(word)); + limit = (ptr_t)((word)limit & ~0xf); + /* Make it sufficiently aligned for assembly */ + /* implementations of GC_clear_stack_inner. */ + return GC_clear_stack_inner(arg, limit); + } + BZERO((void *)dummy, SMALL_CLEAR_SIZE*sizeof(word)); +# else + if (GC_gc_no > GC_stack_last_cleared) { + /* Start things over, so we clear the entire stack again */ + if (GC_stack_last_cleared == 0) + GC_high_water = (ptr_t)GC_stackbottom; + GC_min_sp = GC_high_water; + GC_stack_last_cleared = GC_gc_no; + GC_bytes_allocd_at_reset = GC_bytes_allocd; + } + /* Adjust GC_high_water */ + MAKE_COOLER(GC_high_water, WORDS_TO_BYTES(DEGRADE_RATE) + GC_SLOP); + if ((word)sp HOTTER_THAN (word)GC_high_water) { + GC_high_water = sp; + } + MAKE_HOTTER(GC_high_water, GC_SLOP); + { + ptr_t limit = GC_min_sp; + + MAKE_HOTTER(limit, SLOP); + if ((word)sp COOLER_THAN (word)limit) { + limit = (ptr_t)((word)limit & ~0xf); + /* Make it sufficiently aligned for assembly */ + /* implementations of GC_clear_stack_inner. */ + GC_min_sp = sp; + return GC_clear_stack_inner(arg, limit); + } + } + if (GC_bytes_allocd - GC_bytes_allocd_at_reset > CLEAR_THRESHOLD) { + /* Restart clearing process, but limit how much clearing we do. */ + GC_min_sp = sp; + MAKE_HOTTER(GC_min_sp, CLEAR_THRESHOLD/4); + if ((word)GC_min_sp HOTTER_THAN (word)GC_high_water) + GC_min_sp = GC_high_water; + GC_bytes_allocd_at_reset = GC_bytes_allocd; + } +# endif + return arg; + } + +#endif /* !ALWAYS_SMALL_CLEAR_STACK && !STACK_NOT_SCANNED */ + +/* Return a pointer to the base address of p, given a pointer to a */ +/* an address within an object. Return 0 o.w. */ +GC_API void * GC_CALL GC_base(void * p) +{ + ptr_t r; + struct hblk *h; + bottom_index *bi; + hdr *candidate_hdr; + + r = (ptr_t)p; + if (!EXPECT(GC_is_initialized, TRUE)) return 0; + h = HBLKPTR(r); + GET_BI(r, bi); + candidate_hdr = HDR_FROM_BI(bi, r); + if (candidate_hdr == 0) return(0); + /* If it's a pointer to the middle of a large object, move it */ + /* to the beginning. */ + while (IS_FORWARDING_ADDR_OR_NIL(candidate_hdr)) { + h = FORWARDED_ADDR(h,candidate_hdr); + r = (ptr_t)h; + candidate_hdr = HDR(h); + } + if (HBLK_IS_FREE(candidate_hdr)) return(0); + /* Make sure r points to the beginning of the object */ + r = (ptr_t)((word)r & ~(WORDS_TO_BYTES(1) - 1)); + { + size_t offset = HBLKDISPL(r); + word sz = candidate_hdr -> hb_sz; + size_t obj_displ = offset % sz; + ptr_t limit; + + r -= obj_displ; + limit = r + sz; + if ((word)limit > (word)(h + 1) && sz <= HBLKSIZE) { + return(0); + } + if ((word)p >= (word)limit) return(0); + } + return((void *)r); +} + +/* Return TRUE if and only if p points to somewhere in GC heap. */ +GC_API int GC_CALL GC_is_heap_ptr(const void *p) +{ + bottom_index *bi; + + GC_ASSERT(GC_is_initialized); + GET_BI(p, bi); + return HDR_FROM_BI(bi, p) != 0; +} + +/* Return the size of an object, given a pointer to its base. */ +/* (For small objects this also happens to work from interior pointers, */ +/* but that shouldn't be relied upon.) */ +GC_API size_t GC_CALL GC_size(const void * p) +{ + hdr * hhdr = HDR(p); + + return (size_t)hhdr->hb_sz; +} + + +/* These getters remain unsynchronized for compatibility (since some */ +/* clients could call some of them from a GC callback holding the */ +/* allocator lock). */ +GC_API size_t GC_CALL GC_get_heap_size(void) +{ + /* ignore the memory space returned to OS (i.e. count only the */ + /* space owned by the garbage collector) */ + return (size_t)(GC_heapsize - GC_unmapped_bytes); +} + +GC_API size_t GC_CALL GC_get_free_bytes(void) +{ + /* ignore the memory space returned to OS */ + return (size_t)(GC_large_free_bytes - GC_unmapped_bytes); +} + +GC_API size_t GC_CALL GC_get_unmapped_bytes(void) +{ + return (size_t)GC_unmapped_bytes; +} + +GC_API size_t GC_CALL GC_get_bytes_since_gc(void) +{ + return (size_t)GC_bytes_allocd; +} + +GC_API size_t GC_CALL GC_get_total_bytes(void) +{ + return (size_t)(GC_bytes_allocd + GC_bytes_allocd_before_gc); +} + +#ifndef GC_GET_HEAP_USAGE_NOT_NEEDED + +GC_API size_t GC_CALL GC_get_size_map_at(int i) +{ + if ((unsigned)i > MAXOBJBYTES) + return GC_SIZE_MAX; + return GRANULES_TO_BYTES(GC_size_map[i]); +} + +/* Return the heap usage information. This is a thread-safe (atomic) */ +/* alternative for the five above getters. NULL pointer is allowed for */ +/* any argument. Returned (filled in) values are of word type. */ +GC_API void GC_CALL GC_get_heap_usage_safe(GC_word *pheap_size, + GC_word *pfree_bytes, GC_word *punmapped_bytes, + GC_word *pbytes_since_gc, GC_word *ptotal_bytes) +{ + DCL_LOCK_STATE; + + LOCK(); + if (pheap_size != NULL) + *pheap_size = GC_heapsize - GC_unmapped_bytes; + if (pfree_bytes != NULL) + *pfree_bytes = GC_large_free_bytes - GC_unmapped_bytes; + if (punmapped_bytes != NULL) + *punmapped_bytes = GC_unmapped_bytes; + if (pbytes_since_gc != NULL) + *pbytes_since_gc = GC_bytes_allocd; + if (ptotal_bytes != NULL) + *ptotal_bytes = GC_bytes_allocd + GC_bytes_allocd_before_gc; + UNLOCK(); +} + + GC_INNER word GC_reclaimed_bytes_before_gc = 0; + + /* Fill in GC statistics provided the destination is of enough size. */ + static void fill_prof_stats(struct GC_prof_stats_s *pstats) + { + pstats->heapsize_full = GC_heapsize; + pstats->free_bytes_full = GC_large_free_bytes; + pstats->unmapped_bytes = GC_unmapped_bytes; + pstats->bytes_allocd_since_gc = GC_bytes_allocd; + pstats->allocd_bytes_before_gc = GC_bytes_allocd_before_gc; + pstats->non_gc_bytes = GC_non_gc_bytes; + pstats->gc_no = GC_gc_no; /* could be -1 */ +# ifdef PARALLEL_MARK + pstats->markers_m1 = (word)GC_markers_m1; +# else + pstats->markers_m1 = 0; /* one marker */ +# endif + pstats->bytes_reclaimed_since_gc = GC_bytes_found > 0 ? + (word)GC_bytes_found : 0; + pstats->reclaimed_bytes_before_gc = GC_reclaimed_bytes_before_gc; + pstats->expl_freed_bytes_since_gc = GC_bytes_freed; /* since gc-7.7 */ + } + +# include /* for memset() */ + + GC_API size_t GC_CALL GC_get_prof_stats(struct GC_prof_stats_s *pstats, + size_t stats_sz) + { + struct GC_prof_stats_s stats; + DCL_LOCK_STATE; + + LOCK(); + fill_prof_stats(stats_sz >= sizeof(stats) ? pstats : &stats); + UNLOCK(); + + if (stats_sz == sizeof(stats)) { + return sizeof(stats); + } else if (stats_sz > sizeof(stats)) { + /* Fill in the remaining part with -1. */ + memset((char *)pstats + sizeof(stats), 0xff, stats_sz - sizeof(stats)); + return sizeof(stats); + } else { + if (EXPECT(stats_sz > 0, TRUE)) + BCOPY(&stats, pstats, stats_sz); + return stats_sz; + } + } + +# ifdef THREADS + /* The _unsafe version assumes the caller holds the allocation lock. */ + GC_API size_t GC_CALL GC_get_prof_stats_unsafe( + struct GC_prof_stats_s *pstats, + size_t stats_sz) + { + struct GC_prof_stats_s stats; + + if (stats_sz >= sizeof(stats)) { + fill_prof_stats(pstats); + if (stats_sz > sizeof(stats)) + memset((char *)pstats + sizeof(stats), 0xff, + stats_sz - sizeof(stats)); + return sizeof(stats); + } else { + if (EXPECT(stats_sz > 0, TRUE)) { + fill_prof_stats(&stats); + BCOPY(&stats, pstats, stats_sz); + } + return stats_sz; + } + } +# endif /* THREADS */ + +#endif /* !GC_GET_HEAP_USAGE_NOT_NEEDED */ + +#if defined(GC_DARWIN_THREADS) || defined(GC_OPENBSD_UTHREADS) \ + || defined(GC_WIN32_THREADS) || (defined(NACL) && defined(THREADS)) + /* GC does not use signals to suspend and restart threads. */ + GC_API void GC_CALL GC_set_suspend_signal(int sig GC_ATTR_UNUSED) + { + /* empty */ + } + + GC_API void GC_CALL GC_set_thr_restart_signal(int sig GC_ATTR_UNUSED) + { + /* empty */ + } + + GC_API int GC_CALL GC_get_suspend_signal(void) + { + return -1; + } + + GC_API int GC_CALL GC_get_thr_restart_signal(void) + { + return -1; + } +#endif /* GC_DARWIN_THREADS || GC_WIN32_THREADS || ... */ + +#if !defined(_MAX_PATH) && (defined(MSWIN32) || defined(MSWINCE) \ + || defined(CYGWIN32)) +# define _MAX_PATH MAX_PATH +#endif + +#ifdef GC_READ_ENV_FILE + /* This works for Win32/WinCE for now. Really useful only for WinCE. */ + STATIC char *GC_envfile_content = NULL; + /* The content of the GC "env" file with CR and */ + /* LF replaced to '\0'. NULL if the file is */ + /* missing or empty. Otherwise, always ends */ + /* with '\0'. */ + STATIC unsigned GC_envfile_length = 0; + /* Length of GC_envfile_content (if non-NULL). */ + +# ifndef GC_ENVFILE_MAXLEN +# define GC_ENVFILE_MAXLEN 0x4000 +# endif + +# define GC_ENV_FILE_EXT ".gc.env" + + /* The routine initializes GC_envfile_content from the GC "env" file. */ + STATIC void GC_envfile_init(void) + { +# if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32) + HANDLE hFile; + char *content; + unsigned ofs; + unsigned len; + DWORD nBytesRead; + TCHAR path[_MAX_PATH + 0x10]; /* buffer for path + ext */ + len = (unsigned)GetModuleFileName(NULL /* hModule */, path, + _MAX_PATH + 1); + /* If GetModuleFileName() has failed then len is 0. */ + if (len > 4 && path[len - 4] == (TCHAR)'.') { + len -= 4; /* strip executable file extension */ + } + BCOPY(TEXT(GC_ENV_FILE_EXT), &path[len], sizeof(TEXT(GC_ENV_FILE_EXT))); + hFile = CreateFile(path, GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL /* lpSecurityAttributes */, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL /* hTemplateFile */); + if (hFile == INVALID_HANDLE_VALUE) + return; /* the file is absent or the operation is failed */ + len = (unsigned)GetFileSize(hFile, NULL); + if (len <= 1 || len >= GC_ENVFILE_MAXLEN) { + CloseHandle(hFile); + return; /* invalid file length - ignoring the file content */ + } + /* At this execution point, GC_setpagesize() and GC_init_win32() */ + /* must already be called (for GET_MEM() to work correctly). */ + content = (char *)GET_MEM(ROUNDUP_PAGESIZE_IF_MMAP((size_t)len + 1)); + if (content == NULL) { + CloseHandle(hFile); + return; /* allocation failure */ + } + ofs = 0; + nBytesRead = (DWORD)-1L; + /* Last ReadFile() call should clear nBytesRead on success. */ + while (ReadFile(hFile, content + ofs, len - ofs + 1, &nBytesRead, + NULL /* lpOverlapped */) && nBytesRead != 0) { + if ((ofs += nBytesRead) > len) + break; + } + CloseHandle(hFile); + if (ofs != len || nBytesRead != 0) + return; /* read operation is failed - ignoring the file content */ + content[ofs] = '\0'; + while (ofs-- > 0) { + if (content[ofs] == '\r' || content[ofs] == '\n') + content[ofs] = '\0'; + } + GC_ASSERT(NULL == GC_envfile_content); + GC_envfile_length = len + 1; + GC_envfile_content = content; +# endif + } + + /* This routine scans GC_envfile_content for the specified */ + /* environment variable (and returns its value if found). */ + GC_INNER char * GC_envfile_getenv(const char *name) + { + char *p; + char *end_of_content; + unsigned namelen; +# ifndef NO_GETENV + p = getenv(name); /* try the standard getenv() first */ + if (p != NULL) + return *p != '\0' ? p : NULL; +# endif + p = GC_envfile_content; + if (p == NULL) + return NULL; /* "env" file is absent (or empty) */ + namelen = strlen(name); + if (namelen == 0) /* a sanity check */ + return NULL; + for (end_of_content = p + GC_envfile_length; + p != end_of_content; p += strlen(p) + 1) { + if (strncmp(p, name, namelen) == 0 && *(p += namelen) == '=') { + p++; /* the match is found; skip '=' */ + return *p != '\0' ? p : NULL; + } + /* If not matching then skip to the next line. */ + } + return NULL; /* no match found */ + } +#endif /* GC_READ_ENV_FILE */ + +GC_INNER GC_bool GC_is_initialized = FALSE; + +GC_API int GC_CALL GC_is_init_called(void) +{ + return GC_is_initialized; +} + +#if (defined(MSWIN32) || defined(MSWINCE) || defined(MSWIN_XBOX1)) \ + && defined(THREADS) + GC_INNER CRITICAL_SECTION GC_write_cs; +#endif + +#ifndef DONT_USE_ATEXIT +# if !defined(PCR) && !defined(SMALL_CONFIG) + /* A dedicated variable to avoid a garbage collection on abort. */ + /* GC_find_leak cannot be used for this purpose as otherwise */ + /* TSan finds a data race (between GC_default_on_abort and, e.g., */ + /* GC_finish_collection). */ + static GC_bool skip_gc_atexit = FALSE; +# else +# define skip_gc_atexit FALSE +# endif + + STATIC void GC_exit_check(void) + { + if (GC_find_leak && !skip_gc_atexit) { + GC_gcollect(); + } + } +#endif + +#if defined(UNIX_LIKE) && !defined(NO_DEBUGGING) + static void looping_handler(int sig) + { + GC_err_printf("Caught signal %d: looping in handler\n", sig); + for (;;) { + /* empty */ + } + } + + static GC_bool installed_looping_handler = FALSE; + + static void maybe_install_looping_handler(void) + { + /* Install looping handler before the write fault handler, so we */ + /* handle write faults correctly. */ + if (!installed_looping_handler && 0 != GETENV("GC_LOOP_ON_ABORT")) { + GC_set_and_save_fault_handler(looping_handler); + installed_looping_handler = TRUE; + } + } + +#else /* !UNIX_LIKE */ +# define maybe_install_looping_handler() +#endif + +#define GC_DEFAULT_STDOUT_FD 1 +#define GC_DEFAULT_STDERR_FD 2 + +#if !defined(OS2) && !defined(MACOS) && !defined(GC_ANDROID_LOG) \ + && !defined(NN_PLATFORM_CTR) && !defined(NINTENDO_SWITCH) \ + && !defined(MSWIN32) && !defined(MSWINCE) + STATIC int GC_stdout = GC_DEFAULT_STDOUT_FD; + STATIC int GC_stderr = GC_DEFAULT_STDERR_FD; + STATIC int GC_log = GC_DEFAULT_STDERR_FD; + + GC_API void GC_CALL GC_set_log_fd(int fd) + { + GC_log = fd; + } +#endif + +#if defined(MSWIN32) && !defined(MSWINRT_FLAVOR) && (!defined(SMALL_CONFIG) \ + || (!defined(_WIN64) && defined(GC_WIN32_THREADS) \ + && defined(CHECK_NOT_WOW64))) && !defined(_XBOX_ONE) + STATIC void GC_win32_MessageBoxA(const char *msg, const char *caption, + unsigned flags) + { +# ifndef DONT_USE_USER32_DLL + /* Use static binding to "user32.dll". */ + (void)MessageBoxA(NULL, msg, caption, flags); +# else + /* This simplifies linking - resolve "MessageBoxA" at run-time. */ + HINSTANCE hU32 = LoadLibrary(TEXT("user32.dll")); + if (hU32) { + FARPROC pfn = GetProcAddress(hU32, "MessageBoxA"); + if (pfn) + (void)(*(int (WINAPI *)(HWND, LPCSTR, LPCSTR, UINT))pfn)( + NULL /* hWnd */, msg, caption, flags); + (void)FreeLibrary(hU32); + } +# endif + } +#endif /* MSWIN32 */ + +#if defined(THREADS) && defined(UNIX_LIKE) && !defined(NO_GETCONTEXT) + static void callee_saves_pushed_dummy_fn(ptr_t data GC_ATTR_UNUSED, + void * context GC_ATTR_UNUSED) {} +#endif + +STATIC word GC_parse_mem_size_arg(const char *str) +{ + word result = 0; /* bad value */ + + if (*str != '\0') { + char *endptr; + char ch; + + result = (word)STRTOULL(str, &endptr, 10); + ch = *endptr; + if (ch != '\0') { + if (*(endptr + 1) != '\0') + return 0; + /* Allow k, M or G suffix. */ + switch (ch) { + case 'K': + case 'k': + result <<= 10; + break; + case 'M': + case 'm': + result <<= 20; + break; + case 'G': + case 'g': + result <<= 30; + break; + default: + result = 0; + } + } + } + return result; +} + +#define GC_LOG_STD_NAME "gc.log" + +GC_API void GC_CALL GC_init(void) +{ + /* LOCK(); -- no longer does anything this early. */ + word initial_heap_sz; + IF_CANCEL(int cancel_state;) +# if defined(GC_ASSERTIONS) && defined(GC_ALWAYS_MULTITHREADED) + DCL_LOCK_STATE; +# endif + + if (EXPECT(GC_is_initialized, TRUE)) return; +# ifdef REDIRECT_MALLOC + { + static GC_bool init_started = FALSE; + if (init_started) + ABORT("Redirected malloc() called during GC init"); + init_started = TRUE; + } +# endif + +# if defined(GC_INITIAL_HEAP_SIZE) && !defined(CPPCHECK) + initial_heap_sz = GC_INITIAL_HEAP_SIZE; +# else + initial_heap_sz = MINHINCR * HBLKSIZE; +# endif + +# if defined(MSWIN32) && !defined(_WIN64) && defined(GC_WIN32_THREADS) \ + && defined(CHECK_NOT_WOW64) && !defined(_XBOX_ONE) + { + /* Windows: running 32-bit GC on 64-bit system is broken! */ + /* WoW64 bug affects SuspendThread, no workaround exists. */ + HMODULE hK32 = GetModuleHandle(TEXT("kernel32.dll")); + if (hK32) { + FARPROC pfn = GetProcAddress(hK32, "IsWow64Process"); + BOOL bIsWow64 = FALSE; + if (pfn + && (*(BOOL (WINAPI*)(HANDLE, BOOL*))pfn)(GetCurrentProcess(), + &bIsWow64) + && bIsWow64) { + GC_win32_MessageBoxA("This program uses BDWGC garbage collector" + " compiled for 32-bit but running on 64-bit Windows.\n" + "This is known to be broken due to a design flaw" + " in Windows itself! Expect erratic behavior...", + "32-bit program running on 64-bit system", + MB_ICONWARNING | MB_OK); + } + } + } +# endif + + DISABLE_CANCEL(cancel_state); + /* Note that although we are nominally called with the */ + /* allocation lock held, the allocation lock is now */ + /* only really acquired once a second thread is forked.*/ + /* And the initialization code needs to run before */ + /* then. Thus we really don't hold any locks, and can */ + /* in fact safely initialize them here. */ +# ifdef THREADS +# ifndef GC_ALWAYS_MULTITHREADED + GC_ASSERT(!GC_need_to_lock); +# endif +# ifdef SN_TARGET_PS3 + { + pthread_mutexattr_t mattr; + + if (0 != pthread_mutexattr_init(&mattr)) { + ABORT("pthread_mutexattr_init failed"); + } + if (0 != pthread_mutex_init(&GC_allocate_ml, &mattr)) { + ABORT("pthread_mutex_init failed"); + } + (void)pthread_mutexattr_destroy(&mattr); + } +# endif +# endif /* THREADS */ +# if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) +# ifndef SPIN_COUNT +# define SPIN_COUNT 4000 +# endif +# ifdef MSWINRT_FLAVOR + InitializeCriticalSectionAndSpinCount(&GC_allocate_ml, SPIN_COUNT); +# else + { +# ifndef MSWINCE +# ifndef MSWINRT + BOOL (WINAPI *pfn)(LPCRITICAL_SECTION, DWORD) = 0; + HMODULE hK32 = GetModuleHandle(TEXT("kernel32.dll")); + if (hK32) + pfn = (BOOL (WINAPI *)(LPCRITICAL_SECTION, DWORD)) + GetProcAddress(hK32, + "InitializeCriticalSectionAndSpinCount"); + if (pfn) { + pfn(&GC_allocate_ml, SPIN_COUNT); + } else +# else + InitializeCriticalSectionAndSpinCount(&GC_allocate_ml, 4000); +# endif +# endif /* !MSWINCE */ +# ifndef MSWINRT + /* else */ InitializeCriticalSection(&GC_allocate_ml); +# endif + } +# endif +# endif /* GC_WIN32_THREADS */ +# if (defined(MSWIN32) || defined(MSWINCE)) && defined(THREADS) + InitializeCriticalSection(&GC_write_cs); +# endif + GC_setpagesize(); +# ifdef MSWIN32 + GC_init_win32(); +# endif +# ifdef GC_READ_ENV_FILE + GC_envfile_init(); +# endif +# if !defined(NO_CLOCK) || !defined(SMALL_CONFIG) +# ifdef GC_PRINT_VERBOSE_STATS + /* This is useful for debugging and profiling on platforms with */ + /* missing getenv() (like WinCE). */ + GC_print_stats = VERBOSE; +# else + if (0 != GETENV("GC_PRINT_VERBOSE_STATS")) { + GC_print_stats = VERBOSE; + } else if (0 != GETENV("GC_PRINT_STATS")) { + GC_print_stats = 1; + } +# endif +# endif +# if ((defined(UNIX_LIKE) && !defined(GC_ANDROID_LOG)) \ + || defined(CYGWIN32) || defined(SYMBIAN)) && !defined(SMALL_CONFIG) + { + char * file_name = TRUSTED_STRING(GETENV("GC_LOG_FILE")); +# ifdef GC_LOG_TO_FILE_ALWAYS + if (NULL == file_name) + file_name = GC_LOG_STD_NAME; +# else + if (0 != file_name) +# endif + { + int log_d = open(file_name, O_CREAT|O_WRONLY|O_APPEND, 0666); + if (log_d < 0) { + GC_err_printf("Failed to open %s as log file\n", file_name); + } else { + char *str; + GC_log = log_d; + str = GETENV("GC_ONLY_LOG_TO_FILE"); +# ifdef GC_ONLY_LOG_TO_FILE + /* The similar environment variable set to "0" */ + /* overrides the effect of the macro defined. */ + if (str != NULL && *str == '0' && *(str + 1) == '\0') +# else + /* Otherwise setting the environment variable */ + /* to anything other than "0" will prevent from */ + /* redirecting stdout/err to the log file. */ + if (str == NULL || (*str == '0' && *(str + 1) == '\0')) +# endif + { + GC_stdout = log_d; + GC_stderr = log_d; + } + } + } + } +# endif +# if !defined(NO_DEBUGGING) && !defined(GC_DUMP_REGULARLY) + if (0 != GETENV("GC_DUMP_REGULARLY")) { + GC_dump_regularly = TRUE; + } +# endif +# ifdef KEEP_BACK_PTRS + { + char * backtraces_string = GETENV("GC_BACKTRACES"); + if (0 != backtraces_string) { + GC_backtraces = atol(backtraces_string); + if (backtraces_string[0] == '\0') GC_backtraces = 1; + } + } +# endif + if (0 != GETENV("GC_FIND_LEAK")) { + GC_find_leak = 1; + } +# ifndef SHORT_DBG_HDRS + if (0 != GETENV("GC_FINDLEAK_DELAY_FREE")) { + GC_findleak_delay_free = TRUE; + } +# endif + if (0 != GETENV("GC_ALL_INTERIOR_POINTERS")) { + GC_all_interior_pointers = 1; + } + if (0 != GETENV("GC_DONT_GC")) { + GC_dont_gc = 1; + } + if (0 != GETENV("GC_PRINT_BACK_HEIGHT")) { + GC_print_back_height = TRUE; + } + if (0 != GETENV("GC_NO_BLACKLIST_WARNING")) { + GC_large_alloc_warn_interval = LONG_MAX; + } + { + char * addr_string = GETENV("GC_TRACE"); + if (0 != addr_string) { +# ifndef ENABLE_TRACE + WARN("Tracing not enabled: Ignoring GC_TRACE value\n", 0); +# else + word addr = (word)STRTOULL(addr_string, NULL, 16); + if (addr < 0x1000) + WARN("Unlikely trace address: %p\n", (void *)addr); + GC_trace_addr = (ptr_t)addr; +# endif + } + } +# ifdef GC_COLLECT_AT_MALLOC + { + char * string = GETENV("GC_COLLECT_AT_MALLOC"); + if (0 != string) { + size_t min_lb = (size_t)STRTOULL(string, NULL, 10); + if (min_lb > 0) + GC_dbg_collect_at_malloc_min_lb = min_lb; + } + } +# endif +# ifndef GC_DISABLE_INCREMENTAL + { + char * time_limit_string = GETENV("GC_PAUSE_TIME_TARGET"); + if (0 != time_limit_string) { + long time_limit = atol(time_limit_string); + if (time_limit < 5) { + WARN("GC_PAUSE_TIME_TARGET environment variable value too small " + "or bad syntax: Ignoring\n", 0); + } else { + GC_time_limit = time_limit * 1000000; + } + } + } +# endif +# ifndef SMALL_CONFIG + { + char * full_freq_string = GETENV("GC_FULL_FREQUENCY"); + if (full_freq_string != NULL) { + int full_freq = atoi(full_freq_string); + if (full_freq > 0) + GC_full_freq = full_freq; + } + } +# endif + { + char * interval_string = GETENV("GC_LARGE_ALLOC_WARN_INTERVAL"); + if (0 != interval_string) { + long interval = atol(interval_string); + if (interval <= 0) { + WARN("GC_LARGE_ALLOC_WARN_INTERVAL environment variable has " + "bad value: Ignoring\n", 0); + } else { + GC_large_alloc_warn_interval = interval; + } + } + } + { + char * space_divisor_string = GETENV("GC_FREE_SPACE_DIVISOR"); + if (space_divisor_string != NULL) { + int space_divisor = atoi(space_divisor_string); + if (space_divisor > 0) + GC_free_space_divisor = (word)space_divisor; + } + } +# ifdef USE_MUNMAP + { + char * string = GETENV("GC_UNMAP_THRESHOLD"); + if (string != NULL) { + if (*string == '0' && *(string + 1) == '\0') { + /* "0" is used to disable unmapping. */ + GC_unmap_threshold = 0; + } else { + int unmap_threshold = atoi(string); + if (unmap_threshold > 0) + GC_unmap_threshold = unmap_threshold; + } + } + } + { + char * string = GETENV("GC_FORCE_UNMAP_ON_GCOLLECT"); + if (string != NULL) { + if (*string == '0' && *(string + 1) == '\0') { + /* "0" is used to turn off the mode. */ + GC_force_unmap_on_gcollect = FALSE; + } else { + GC_force_unmap_on_gcollect = TRUE; + } + } + } + { + char * string = GETENV("GC_USE_ENTIRE_HEAP"); + if (string != NULL) { + if (*string == '0' && *(string + 1) == '\0') { + /* "0" is used to turn off the mode. */ + GC_use_entire_heap = FALSE; + } else { + GC_use_entire_heap = TRUE; + } + } + } +# endif +# if !defined(NO_DEBUGGING) && !defined(NO_CLOCK) + GET_TIME(GC_init_time); +# endif + maybe_install_looping_handler(); +# if ALIGNMENT > GC_DS_TAGS + /* Adjust normal object descriptor for extra allocation. */ + if (EXTRA_BYTES != 0) + GC_obj_kinds[NORMAL].ok_descriptor = (word)(-ALIGNMENT) | GC_DS_LENGTH; +# endif + GC_exclude_static_roots_inner(beginGC_arrays, endGC_arrays); + GC_exclude_static_roots_inner(beginGC_obj_kinds, endGC_obj_kinds); +# ifdef SEPARATE_GLOBALS + GC_exclude_static_roots_inner(beginGC_objfreelist, endGC_objfreelist); + GC_exclude_static_roots_inner(beginGC_aobjfreelist, endGC_aobjfreelist); +# endif +# if defined(USE_PROC_FOR_LIBRARIES) && defined(GC_LINUX_THREADS) + WARN("USE_PROC_FOR_LIBRARIES + GC_LINUX_THREADS performs poorly.\n", 0); + /* If thread stacks are cached, they tend to be scanned in */ + /* entirety as part of the root set. This wil grow them to */ + /* maximum size, and is generally not desirable. */ +# endif +# if defined(SEARCH_FOR_DATA_START) + GC_init_linux_data_start(); +# endif +# if defined(NETBSD) && defined(__ELF__) + GC_init_netbsd_elf(); +# endif +# if !defined(THREADS) || defined(GC_PTHREADS) \ + || defined(NN_PLATFORM_CTR) || defined(NINTENDO_SWITCH) \ + || defined(GC_WIN32_THREADS) || defined(GC_SOLARIS_THREADS) + if (GC_stackbottom == 0) { + GC_stackbottom = GC_get_main_stack_base(); +# if (defined(LINUX) || defined(HPUX)) && defined(IA64) + GC_register_stackbottom = GC_get_register_stack_base(); +# endif + } else { +# if (defined(LINUX) || defined(HPUX)) && defined(IA64) + if (GC_register_stackbottom == 0) { + WARN("GC_register_stackbottom should be set with GC_stackbottom\n", 0); + /* The following may fail, since we may rely on */ + /* alignment properties that may not hold with a user set */ + /* GC_stackbottom. */ + GC_register_stackbottom = GC_get_register_stack_base(); + } +# endif + } +# endif +# if !defined(CPPCHECK) + GC_STATIC_ASSERT(sizeof(ptr_t) == sizeof(word)); + GC_STATIC_ASSERT(sizeof(signed_word) == sizeof(word)); +# if !defined(_AUX_SOURCE) || defined(__GNUC__) + GC_STATIC_ASSERT((word)(-1) > (word)0); + /* word should be unsigned */ +# endif + /* We no longer check for ((void*)(-1) > NULL) since all pointers */ + /* are explicitly cast to word in every less/greater comparison. */ + GC_STATIC_ASSERT((signed_word)(-1) < (signed_word)0); +# endif + GC_STATIC_ASSERT(sizeof (struct hblk) == HBLKSIZE); +# ifndef THREADS + GC_ASSERT(!((word)GC_stackbottom HOTTER_THAN (word)GC_approx_sp())); +# endif +# ifndef GC_DISABLE_INCREMENTAL + if (GC_incremental || 0 != GETENV("GC_ENABLE_INCREMENTAL")) { + /* For GWW_VDB on Win32, this needs to happen before any */ + /* heap memory is allocated. */ + GC_incremental = GC_dirty_init(); + GC_ASSERT(GC_bytes_allocd == 0); + } +# endif + + /* Add initial guess of root sets. Do this first, since sbrk(0) */ + /* might be used. */ + if (GC_REGISTER_MAIN_STATIC_DATA()) GC_register_data_segments(); + GC_init_headers(); + GC_bl_init(); + GC_mark_init(); + { + char * sz_str = GETENV("GC_INITIAL_HEAP_SIZE"); + if (sz_str != NULL) { + initial_heap_sz = GC_parse_mem_size_arg(sz_str); + if (initial_heap_sz <= MINHINCR * HBLKSIZE) { + WARN("Bad initial heap size %s - ignoring it.\n", sz_str); + } + } + } + { + char * sz_str = GETENV("GC_MAXIMUM_HEAP_SIZE"); + if (sz_str != NULL) { + word max_heap_sz = GC_parse_mem_size_arg(sz_str); + if (max_heap_sz < initial_heap_sz) { + WARN("Bad maximum heap size %s - ignoring it.\n", sz_str); + } + if (0 == GC_max_retries) GC_max_retries = 2; + GC_set_max_heap_size(max_heap_sz); + } + } + if (!GC_expand_hp_inner(divHBLKSZ(initial_heap_sz))) { + GC_err_printf("Can't start up: not enough memory\n"); + EXIT(); + } else { + GC_requested_heapsize += initial_heap_sz; + } + if (GC_all_interior_pointers) + GC_initialize_offsets(); + GC_register_displacement_inner(0L); +# if defined(GC_LINUX_THREADS) && defined(REDIRECT_MALLOC) + if (!GC_all_interior_pointers) { + /* TLS ABI uses pointer-sized offsets for dtv. */ + GC_register_displacement_inner(sizeof(void *)); + } +# endif + GC_init_size_map(); +# ifdef PCR + if (PCR_IL_Lock(PCR_Bool_false, PCR_allSigsBlocked, PCR_waitForever) + != PCR_ERes_okay) { + ABORT("Can't lock load state"); + } else if (PCR_IL_Unlock() != PCR_ERes_okay) { + ABORT("Can't unlock load state"); + } + PCR_IL_Unlock(); + GC_pcr_install(); +# endif + GC_is_initialized = TRUE; +# if defined(GC_ASSERTIONS) && defined(GC_ALWAYS_MULTITHREADED) + LOCK(); /* just to set GC_lock_holder */ +# endif +# if defined(GC_PTHREADS) || defined(GC_WIN32_THREADS) + GC_thr_init(); +# ifdef PARALLEL_MARK + /* Actually start helper threads. */ +# if defined(GC_ASSERTIONS) && defined(GC_ALWAYS_MULTITHREADED) + UNLOCK(); +# endif + GC_start_mark_threads_inner(); +# if defined(GC_ASSERTIONS) && defined(GC_ALWAYS_MULTITHREADED) + LOCK(); +# endif +# endif +# endif + COND_DUMP; + /* Get black list set up and/or incremental GC started */ + if (!GC_dont_precollect || GC_incremental) { + GC_gcollect_inner(); + } +# if defined(GC_ASSERTIONS) && defined(GC_ALWAYS_MULTITHREADED) + UNLOCK(); +# endif +# if defined(THREADS) && defined(UNIX_LIKE) && !defined(NO_GETCONTEXT) + /* Ensure getcontext_works is set to avoid potential data race. */ + if (GC_dont_gc || GC_dont_precollect) + GC_with_callee_saves_pushed(callee_saves_pushed_dummy_fn, NULL); +# endif +# ifndef DONT_USE_ATEXIT + if (GC_find_leak) { + /* This is to give us at least one chance to detect leaks. */ + /* This may report some very benign leaks, but ... */ + atexit(GC_exit_check); + } +# endif + + /* The rest of this again assumes we don't really hold */ + /* the allocation lock. */ +# if defined(PARALLEL_MARK) || defined(THREAD_LOCAL_ALLOC) \ + || (defined(GC_ALWAYS_MULTITHREADED) && defined(GC_WIN32_THREADS) \ + && !defined(GC_NO_THREADS_DISCOVERY)) + /* Make sure marker threads are started and thread local */ + /* allocation is initialized, in case we didn't get */ + /* called from GC_init_parallel. */ + GC_init_parallel(); +# endif /* PARALLEL_MARK || THREAD_LOCAL_ALLOC */ + +# if defined(DYNAMIC_LOADING) && defined(DARWIN) + /* This must be called WITHOUT the allocation lock held */ + /* and before any threads are created. */ + GC_init_dyld(); +# endif + RESTORE_CANCEL(cancel_state); +} + +GC_API void GC_CALL GC_enable_incremental(void) +{ +# if !defined(GC_DISABLE_INCREMENTAL) && !defined(KEEP_BACK_PTRS) + DCL_LOCK_STATE; + /* If we are keeping back pointers, the GC itself dirties all */ + /* pages on which objects have been marked, making */ + /* incremental GC pointless. */ + if (!GC_find_leak && 0 == GETENV("GC_DISABLE_INCREMENTAL")) { + LOCK(); + if (!GC_incremental) { + GC_setpagesize(); + /* if (GC_no_win32_dlls) goto out; Should be win32S test? */ + maybe_install_looping_handler(); /* Before write fault handler! */ + if (!GC_is_initialized) { + UNLOCK(); + GC_incremental = TRUE; /* indicate intention to turn it on */ + GC_init(); + LOCK(); + } else { + GC_incremental = GC_dirty_init(); + } + if (GC_incremental && !GC_dont_gc) { + /* Can't easily do it if GC_dont_gc. */ + IF_CANCEL(int cancel_state;) + + DISABLE_CANCEL(cancel_state); + if (GC_bytes_allocd > 0) { + /* There may be unmarked reachable objects. */ + GC_gcollect_inner(); + } + /* else we're OK in assuming everything's */ + /* clean since nothing can point to an */ + /* unmarked object. */ + GC_read_dirty(FALSE); + RESTORE_CANCEL(cancel_state); + } + } + UNLOCK(); + return; + } +# endif + GC_init(); +} + +#if defined(THREADS) + GC_API void GC_CALL GC_start_mark_threads(void) + { +# if defined(PARALLEL_MARK) && defined(CAN_HANDLE_FORK) \ + && !defined(THREAD_SANITIZER) + /* TSan does not support threads creation in the child process. */ + IF_CANCEL(int cancel_state;) + + DISABLE_CANCEL(cancel_state); + GC_start_mark_threads_inner(); + RESTORE_CANCEL(cancel_state); +# else + /* No action since parallel markers are disabled (or no POSIX fork). */ + GC_ASSERT(I_DONT_HOLD_LOCK()); +# endif + } +#endif + + GC_API void GC_CALL GC_deinit(void) + { + if (GC_is_initialized) { + /* Prevent duplicate resource close. */ + GC_is_initialized = FALSE; +# if defined(THREADS) && (defined(MSWIN32) || defined(MSWINCE)) + DeleteCriticalSection(&GC_write_cs); + DeleteCriticalSection(&GC_allocate_ml); +# endif + } + } + +#if defined(MSWIN32) || defined(MSWINCE) + +# if defined(_MSC_VER) && defined(_DEBUG) && !defined(MSWINCE) +# include +# endif + + STATIC HANDLE GC_log = 0; + +# ifdef THREADS +# if defined(PARALLEL_MARK) && !defined(GC_ALWAYS_MULTITHREADED) +# define IF_NEED_TO_LOCK(x) if (GC_parallel || GC_need_to_lock) x +# else +# define IF_NEED_TO_LOCK(x) if (GC_need_to_lock) x +# endif +# else +# define IF_NEED_TO_LOCK(x) +# endif /* !THREADS */ + +# ifdef MSWINRT_FLAVOR +# include + + /* This API is defined in roapi.h, but we cannot include it here */ + /* since it does not compile in C. */ + DECLSPEC_IMPORT HRESULT WINAPI RoGetActivationFactory( + HSTRING activatableClassId, + REFIID iid, void** factory); + + static GC_bool getWinRTLogPath(wchar_t* buf, size_t bufLen) + { + static const GUID kIID_IApplicationDataStatics = { + 0x5612147B, 0xE843, 0x45E3, + 0x94, 0xD8, 0x06, 0x16, 0x9E, 0x3C, 0x8E, 0x17 + }; + static const GUID kIID_IStorageItem = { + 0x4207A996, 0xCA2F, 0x42F7, + 0xBD, 0xE8, 0x8B, 0x10, 0x45, 0x7A, 0x7F, 0x30 + }; + GC_bool result = FALSE; + HSTRING_HEADER appDataClassNameHeader; + HSTRING appDataClassName; + __x_ABI_CWindows_CStorage_CIApplicationDataStatics* appDataStatics = 0; + + GC_ASSERT(bufLen > 0); + if (SUCCEEDED(WindowsCreateStringReference( + RuntimeClass_Windows_Storage_ApplicationData, + (sizeof(RuntimeClass_Windows_Storage_ApplicationData)-1) + / sizeof(wchar_t), + &appDataClassNameHeader, &appDataClassName)) + && SUCCEEDED(RoGetActivationFactory(appDataClassName, + &kIID_IApplicationDataStatics, + &appDataStatics))) { + __x_ABI_CWindows_CStorage_CIApplicationData* appData = NULL; + __x_ABI_CWindows_CStorage_CIStorageFolder* tempFolder = NULL; + __x_ABI_CWindows_CStorage_CIStorageItem* tempFolderItem = NULL; + HSTRING tempPath = NULL; + + if (SUCCEEDED(appDataStatics->lpVtbl->get_Current(appDataStatics, + &appData)) + && SUCCEEDED(appData->lpVtbl->get_TemporaryFolder(appData, + &tempFolder)) + && SUCCEEDED(tempFolder->lpVtbl->QueryInterface(tempFolder, + &kIID_IStorageItem, + &tempFolderItem)) + && SUCCEEDED(tempFolderItem->lpVtbl->get_Path(tempFolderItem, + &tempPath))) { + UINT32 tempPathLen; + const wchar_t* tempPathBuf = + WindowsGetStringRawBuffer(tempPath, &tempPathLen); + + buf[0] = '\0'; + if (wcsncat_s(buf, bufLen, tempPathBuf, tempPathLen) == 0 + && wcscat_s(buf, bufLen, L"\\") == 0 + && wcscat_s(buf, bufLen, TEXT(GC_LOG_STD_NAME)) == 0) + result = TRUE; + WindowsDeleteString(tempPath); + } + + if (tempFolderItem != NULL) + tempFolderItem->lpVtbl->Release(tempFolderItem); + if (tempFolder != NULL) + tempFolder->lpVtbl->Release(tempFolder); + if (appData != NULL) + appData->lpVtbl->Release(appData); + appDataStatics->lpVtbl->Release(appDataStatics); + } + return result; + } +# endif /* MSWINRT_FLAVOR */ + + STATIC HANDLE GC_CreateLogFile(void) + { + HANDLE hFile; +# ifdef MSWINRT_FLAVOR + TCHAR pathBuf[_MAX_PATH + 0x10]; /* buffer for path + ext */ + + hFile = INVALID_HANDLE_VALUE; + if (getWinRTLogPath(pathBuf, _MAX_PATH + 1)) { + CREATEFILE2_EXTENDED_PARAMETERS extParams; + + BZERO(&extParams, sizeof(extParams)); + extParams.dwSize = sizeof(extParams); + extParams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL; + extParams.dwFileFlags = GC_print_stats == VERBOSE ? 0 + : FILE_FLAG_WRITE_THROUGH; + hFile = CreateFile2(pathBuf, GENERIC_WRITE, FILE_SHARE_READ, + CREATE_ALWAYS, &extParams); + } + +# else + TCHAR *logPath; + BOOL appendToFile = FALSE; +# if !defined(NO_GETENV_WIN32) || !defined(OLD_WIN32_LOG_FILE) + TCHAR pathBuf[_MAX_PATH + 0x10]; /* buffer for path + ext */ + + logPath = pathBuf; +# endif + +# ifndef MSWINRT + /* Use GetEnvironmentVariable instead of GETENV() for unicode support. */ +# ifndef NO_GETENV_WIN32 + if (GetEnvironmentVariable(TEXT("GC_LOG_FILE"), pathBuf, + _MAX_PATH + 1) - 1U < (DWORD)_MAX_PATH) { + appendToFile = TRUE; + } else +# endif + /* else */ { + /* Env var not found or its value too long. */ +# ifdef OLD_WIN32_LOG_FILE + logPath = TEXT(GC_LOG_STD_NAME); +# else + int len = (int)GetModuleFileName(NULL /* hModule */, pathBuf, + _MAX_PATH + 1); + /* If GetModuleFileName() has failed then len is 0. */ + if (len > 4 && pathBuf[len - 4] == (TCHAR)'.') { + len -= 4; /* strip executable file extension */ + } + BCOPY(TEXT(".") TEXT(GC_LOG_STD_NAME), &pathBuf[len], + sizeof(TEXT(".") TEXT(GC_LOG_STD_NAME))); +# endif + } +# endif + +# ifndef MSWINRT + hFile = CreateFile(logPath, GENERIC_WRITE, FILE_SHARE_READ, + NULL /* lpSecurityAttributes */, + appendToFile ? OPEN_ALWAYS : CREATE_ALWAYS, + GC_print_stats == VERBOSE ? FILE_ATTRIBUTE_NORMAL : + /* immediately flush writes unless very verbose */ + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, + NULL /* hTemplateFile */); +# else + { + if (GetWinRTLogPath(pathBuf, MAX_PATH + 1)) + { + CREATEFILE2_EXTENDED_PARAMETERS extendedParameters; + ZeroMemory(&extendedParameters, sizeof(extendedParameters)); + + extendedParameters.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS); + extendedParameters.dwFileFlags = GC_print_stats == VERBOSE + ? 0 + : /* immediately flush writes unless very verbose */ FILE_FLAG_WRITE_THROUGH; + extendedParameters.dwFileAttributes = FILE_ATTRIBUTE_NORMAL; + + hFile = CreateFile2(logPath, GENERIC_WRITE, FILE_SHARE_READ, + appendToFile ? OPEN_ALWAYS : CREATE_ALWAYS, + &extendedParameters); + } + else + { + hFile = INVALID_HANDLE_VALUE; + } + } +# endif + +# ifndef NO_GETENV_WIN32 + if (appendToFile && hFile != INVALID_HANDLE_VALUE) { + LONG posHigh = 0; + (void)SetFilePointer(hFile, 0, &posHigh, FILE_END); + /* Seek to file end (ignoring any error) */ + } +# endif +# endif + return hFile; + } + + STATIC int GC_write(const char *buf, size_t len) + { + BOOL res; + DWORD written; +# if defined(THREADS) && defined(GC_ASSERTIONS) + static GC_bool inside_write = FALSE; + /* to prevent infinite recursion at abort. */ + if (inside_write) + return -1; +# endif + + if (len == 0) + return 0; + IF_NEED_TO_LOCK(EnterCriticalSection(&GC_write_cs)); +# if defined(THREADS) && defined(GC_ASSERTIONS) + if (GC_write_disabled) { + inside_write = TRUE; + ABORT("Assertion failure: GC_write called with write_disabled"); + } +# endif + if (GC_log == 0) { + GC_log = GC_CreateLogFile(); + } + if (GC_log == INVALID_HANDLE_VALUE) { + IF_NEED_TO_LOCK(LeaveCriticalSection(&GC_write_cs)); +# ifdef NO_DEBUGGING + /* Ignore open log failure (e.g., it might be caused by */ + /* read-only folder of the client application). */ + return 0; +# else + return -1; +# endif + } + res = WriteFile(GC_log, buf, (DWORD)len, &written, NULL); +# if defined(_MSC_VER) && defined(_DEBUG) && !defined(NO_CRT) +# ifdef MSWINCE + /* There is no CrtDbgReport() in WinCE */ + { + WCHAR wbuf[1024]; + /* Always use Unicode variant of OutputDebugString() */ + wbuf[MultiByteToWideChar(CP_ACP, 0 /* dwFlags */, + buf, len, wbuf, + sizeof(wbuf) / sizeof(wbuf[0]) - 1)] = 0; + OutputDebugStringW(wbuf); + } +# else + _CrtDbgReport(_CRT_WARN, NULL, 0, NULL, "%.*s", len, buf); +# endif +# endif + IF_NEED_TO_LOCK(LeaveCriticalSection(&GC_write_cs)); + return res ? (int)written : -1; + } + + /* FIXME: This is pretty ugly ... */ +# define WRITE(f, buf, len) GC_write(buf, len) + +#elif defined(OS2) || defined(MACOS) + STATIC FILE * GC_stdout = NULL; + STATIC FILE * GC_stderr = NULL; + STATIC FILE * GC_log = NULL; + + /* Initialize GC_log (and the friends) passed to GC_write(). */ + STATIC void GC_set_files(void) + { + if (GC_stdout == NULL) { + GC_stdout = stdout; + } + if (GC_stderr == NULL) { + GC_stderr = stderr; + } + if (GC_log == NULL) { + GC_log = stderr; + } + } + + GC_INLINE int GC_write(FILE *f, const char *buf, size_t len) + { + int res = fwrite(buf, 1, len, f); + fflush(f); + return res; + } + +# define WRITE(f, buf, len) (GC_set_files(), GC_write(f, buf, len)) + +#elif defined(GC_ANDROID_LOG) + +# include + +# ifndef GC_ANDROID_LOG_TAG +# define GC_ANDROID_LOG_TAG "BDWGC" +# endif + +# define GC_stdout ANDROID_LOG_DEBUG +# define GC_stderr ANDROID_LOG_ERROR +# define GC_log GC_stdout + +# define WRITE(level, buf, unused_len) \ + __android_log_write(level, GC_ANDROID_LOG_TAG, buf) + +# elif defined(NN_PLATFORM_CTR) + int n3ds_log_write(const char* text, int length); +# define WRITE(level, buf, len) n3ds_log_write(buf, len) +# elif defined(NINTENDO_SWITCH) + int switch_log_write(const char* text, int length); +# define WRITE(level, buf, len) switch_log_write(buf, len) + +#else +# if !defined(AMIGA) && !defined(MSWIN_XBOX1) && !defined(SN_TARGET_ORBIS) \ + && !defined(SN_TARGET_PSP2) && !defined(__CC_ARM) +# include +# endif + + STATIC int GC_write(int fd, const char *buf, size_t len) + { +# if defined(ECOS) || defined(SN_TARGET_ORBIS) || defined(SN_TARGET_PSP2) \ + || defined(NOSYS) +# ifdef ECOS + /* FIXME: This seems to be defined nowhere at present. */ + /* _Jv_diag_write(buf, len); */ +# else + /* No writing. */ +# endif + return len; +# else + int bytes_written = 0; + IF_CANCEL(int cancel_state;) + + DISABLE_CANCEL(cancel_state); + while ((size_t)bytes_written < len) { +# ifdef GC_SOLARIS_THREADS + int result = syscall(SYS_write, fd, buf + bytes_written, + len - bytes_written); +# else + int result = write(fd, buf + bytes_written, len - bytes_written); +# endif + + if (-1 == result) { + RESTORE_CANCEL(cancel_state); + return(result); + } + bytes_written += result; + } + RESTORE_CANCEL(cancel_state); + return(bytes_written); +# endif + } + +# define WRITE(f, buf, len) GC_write(f, buf, len) +#endif /* !MSWIN32 && !OS2 && !MACOS && !GC_ANDROID_LOG */ + +#define BUFSZ 1024 + +#if !defined(NO_CRT) +#if defined(DJGPP) || defined(__STRICT_ANSI__) + /* vsnprintf is missing in DJGPP (v2.0.3) */ +# define GC_VSNPRINTF(buf, bufsz, format, args) vsprintf(buf, format, args) +#elif defined(_MSC_VER) +# ifdef MSWINCE + /* _vsnprintf is deprecated in WinCE */ +# define GC_VSNPRINTF StringCchVPrintfA +# else +# define GC_VSNPRINTF _vsnprintf +# endif +#else +# define GC_VSNPRINTF vsnprintf +#endif +#endif +/* A version of printf that is unlikely to call malloc, and is thus safer */ +/* to call from the collector in case malloc has been bound to GC_malloc. */ +/* Floating point arguments and formats should be avoided, since FP */ +/* conversion is more likely to allocate memory. */ +/* Assumes that no more than BUFSZ-1 characters are written at once. */ +#if defined(GC_VSNPRINTF) +#define GC_PRINTF_FILLBUF(buf, format) \ + do { \ + va_list args; \ + va_start(args, format); \ + (buf)[sizeof(buf) - 1] = 0x15; /* guard */ \ + (void)GC_VSNPRINTF(buf, sizeof(buf) - 1, format, args); \ + va_end(args); \ + if ((buf)[sizeof(buf) - 1] != 0x15) \ + ABORT("GC_printf clobbered stack"); \ + } while (0) +#else +#define GC_PRINTF_FILLBUF(buf, format) \ + do { \ + } while (0) +#endif +void GC_printf(const char *format, ...) +{ + if (!GC_quiet) { + char buf[BUFSZ + 1]; + + GC_PRINTF_FILLBUF(buf, format); +# ifdef NACL + (void)WRITE(GC_stdout, buf, strlen(buf)); + /* Ignore errors silently. */ +# else + if (WRITE(GC_stdout, buf, strlen(buf)) < 0) + ABORT("write to stdout failed"); +# endif + } +} + +void GC_err_printf(const char *format, ...) +{ + char buf[BUFSZ + 1]; + + GC_PRINTF_FILLBUF(buf, format); + GC_err_puts(buf); +} + +void GC_log_printf(const char *format, ...) +{ + char buf[BUFSZ + 1]; + + GC_PRINTF_FILLBUF(buf, format); +# ifdef NACL + (void)WRITE(GC_log, buf, strlen(buf)); +# else + if (WRITE(GC_log, buf, strlen(buf)) < 0) + ABORT("write to GC log failed"); +# endif +} + +#ifndef GC_ANDROID_LOG + +# define GC_warn_printf GC_err_printf + +#else + + GC_INNER void GC_info_log_printf(const char *format, ...) + { + char buf[BUFSZ + 1]; + + GC_PRINTF_FILLBUF(buf, format); + (void)WRITE(ANDROID_LOG_INFO, buf, 0 /* unused */); + } + + GC_INNER void GC_verbose_log_printf(const char *format, ...) + { + char buf[BUFSZ + 1]; + + GC_PRINTF_FILLBUF(buf, format); + (void)WRITE(ANDROID_LOG_VERBOSE, buf, 0); /* ignore write errors */ + } + + STATIC void GC_warn_printf(const char *format, ...) + { + char buf[BUFSZ + 1]; + + GC_PRINTF_FILLBUF(buf, format); + (void)WRITE(ANDROID_LOG_WARN, buf, 0); + } + +#endif /* GC_ANDROID_LOG */ + +void GC_err_puts(const char *s) +{ + (void)WRITE(GC_stderr, s, strlen(s)); /* ignore errors */ +} + +STATIC void GC_CALLBACK GC_default_warn_proc(char *msg, GC_word arg) +{ + /* TODO: Add assertion on arg comply with msg (format). */ + GC_warn_printf(msg, arg); +} + +GC_INNER GC_warn_proc GC_current_warn_proc = GC_default_warn_proc; + +/* This is recommended for production code (release). */ +GC_API void GC_CALLBACK GC_ignore_warn_proc(char *msg, GC_word arg) +{ + if (GC_print_stats) { + /* Don't ignore warnings if stats printing is on. */ + GC_default_warn_proc(msg, arg); + } +} + +GC_API void GC_CALL GC_set_warn_proc(GC_warn_proc p) +{ + DCL_LOCK_STATE; + GC_ASSERT(NONNULL_ARG_NOT_NULL(p)); +# ifdef GC_WIN32_THREADS +# ifdef CYGWIN32 + /* Need explicit GC_INIT call */ + GC_ASSERT(GC_is_initialized); +# else + if (!GC_is_initialized) GC_init(); +# endif +# endif + LOCK(); + GC_current_warn_proc = p; + UNLOCK(); +} + +GC_API GC_warn_proc GC_CALL GC_get_warn_proc(void) +{ + GC_warn_proc result; + DCL_LOCK_STATE; + LOCK(); + result = GC_current_warn_proc; + UNLOCK(); + return(result); +} + +#if !defined(PCR) && !defined(SMALL_CONFIG) + /* Print (or display) a message before abnormal exit (including */ + /* abort). Invoked from ABORT(msg) macro (there msg is non-NULL) */ + /* and from EXIT() macro (msg is NULL in that case). */ + STATIC void GC_CALLBACK GC_default_on_abort(const char *msg) + { +# ifndef DONT_USE_ATEXIT + skip_gc_atexit = TRUE; /* disable at-exit GC_gcollect() */ +# endif + + if (msg != NULL) { +# if defined(MSWIN32) && !defined(MSWINRT_FLAVOR) && !defined(_XBOX_ONE) + GC_win32_MessageBoxA(msg, "Fatal error in GC", MB_ICONERROR | MB_OK); + /* Also duplicate msg to GC log file. */ +# endif + +# ifndef GC_ANDROID_LOG + /* Avoid calling GC_err_printf() here, as GC_on_abort() could be */ + /* called from it. Note 1: this is not an atomic output. */ + /* Note 2: possible write errors are ignored. */ +# if defined(THREADS) && defined(GC_ASSERTIONS) \ + && (defined(MSWIN32) || defined(MSWINCE)) + if (!GC_write_disabled) +# endif + { + if (WRITE(GC_stderr, msg, strlen(msg)) >= 0) + (void)WRITE(GC_stderr, "\n", 1); + } +# else + __android_log_assert("*" /* cond */, GC_ANDROID_LOG_TAG, "%s\n", msg); +# endif + } + +# if !defined(NO_DEBUGGING) && !defined(GC_ANDROID_LOG) + if (GETENV("GC_LOOP_ON_ABORT") != NULL) { + /* In many cases it's easier to debug a running process. */ + /* It's arguably nicer to sleep, but that makes it harder */ + /* to look at the thread if the debugger doesn't know much */ + /* about threads. */ + for(;;) { + /* Empty */ + } + } +# endif + } + + GC_abort_func GC_on_abort = GC_default_on_abort; + + GC_API void GC_CALL GC_set_abort_func(GC_abort_func fn) + { + DCL_LOCK_STATE; + GC_ASSERT(NONNULL_ARG_NOT_NULL(fn)); + LOCK(); + GC_on_abort = fn; + UNLOCK(); + } + + GC_API GC_abort_func GC_CALL GC_get_abort_func(void) + { + GC_abort_func fn; + DCL_LOCK_STATE; + LOCK(); + fn = GC_on_abort; + UNLOCK(); + return fn; + } +#endif /* !SMALL_CONFIG */ + +GC_API void GC_CALL GC_enable(void) +{ + DCL_LOCK_STATE; + + LOCK(); + GC_ASSERT(GC_dont_gc != 0); /* ensure no counter underflow */ + GC_dont_gc--; + UNLOCK(); +} + +GC_API void GC_CALL GC_disable(void) +{ + DCL_LOCK_STATE; + LOCK(); + GC_dont_gc++; + UNLOCK(); +} + +GC_API int GC_CALL GC_is_disabled(void) +{ + return GC_dont_gc != 0; +} + +/* Helper procedures for new kind creation. */ +GC_API void ** GC_CALL GC_new_free_list_inner(void) +{ + void *result; + + GC_ASSERT(I_HOLD_LOCK()); + result = GC_INTERNAL_MALLOC((MAXOBJGRANULES+1) * sizeof(ptr_t), PTRFREE); + if (NULL == result) ABORT("Failed to allocate freelist for new kind"); + BZERO(result, (MAXOBJGRANULES+1)*sizeof(ptr_t)); + return (void **)result; +} + +GC_API void ** GC_CALL GC_new_free_list(void) +{ + void ** result; + DCL_LOCK_STATE; + LOCK(); + result = GC_new_free_list_inner(); + UNLOCK(); + return result; +} + +GC_API unsigned GC_CALL GC_new_kind_inner(void **fl, GC_word descr, + int adjust, int clear) +{ + unsigned result = GC_n_kinds; + + GC_ASSERT(adjust == FALSE || adjust == TRUE); + /* If an object is not needed to be cleared (when moved to the */ + /* free list) then its descriptor should be zero to denote */ + /* a pointer-free object (and, as a consequence, the size of the */ + /* object should not be added to the descriptor template). */ + GC_ASSERT(clear == TRUE + || (descr == 0 && adjust == FALSE && clear == FALSE)); + if (result < MAXOBJKINDS) { + GC_n_kinds++; + GC_obj_kinds[result].ok_freelist = fl; + GC_obj_kinds[result].ok_reclaim_list = 0; + GC_obj_kinds[result].ok_descriptor = descr; + GC_obj_kinds[result].ok_relocate_descr = adjust; + GC_obj_kinds[result].ok_init = (GC_bool)clear; +# ifdef ENABLE_DISCLAIM + GC_obj_kinds[result].ok_mark_unconditionally = FALSE; + GC_obj_kinds[result].ok_disclaim_proc = 0; +# endif + } else { + ABORT("Too many kinds"); + } + return result; +} + +GC_API unsigned GC_CALL GC_new_kind(void **fl, GC_word descr, int adjust, + int clear) +{ + unsigned result; + DCL_LOCK_STATE; + LOCK(); + result = GC_new_kind_inner(fl, descr, adjust, clear); + UNLOCK(); + return result; +} + +GC_API unsigned GC_CALL GC_new_proc_inner(GC_mark_proc proc) +{ + unsigned result = GC_n_mark_procs; + + if (result < MAX_MARK_PROCS) { + GC_n_mark_procs++; + GC_mark_procs[result] = proc; + } else { + ABORT("Too many mark procedures"); + } + return result; +} + +GC_API unsigned GC_CALL GC_new_proc(GC_mark_proc proc) +{ + unsigned result; + DCL_LOCK_STATE; + LOCK(); + result = GC_new_proc_inner(proc); + UNLOCK(); + return result; +} + +GC_API void * GC_CALL GC_call_with_alloc_lock(GC_fn_type fn, void *client_data) +{ + void * result; + DCL_LOCK_STATE; + +# ifdef THREADS + LOCK(); +# endif + result = (*fn)(client_data); +# ifdef THREADS + UNLOCK(); +# endif + return(result); +} + +GC_API void * GC_CALL GC_call_with_stack_base(GC_stack_base_func fn, void *arg) +{ + struct GC_stack_base base; + void *result; + + base.mem_base = (void *)&base; +# ifdef IA64 + base.reg_base = (void *)GC_save_regs_in_stack(); + /* Unnecessarily flushes register stack, */ + /* but that probably doesn't hurt. */ +# endif + result = fn(&base, arg); + /* Strongly discourage the compiler from treating the above */ + /* as a tail call. */ + GC_noop1((word)(&base)); + return result; +} + +#ifndef THREADS + +GC_INNER ptr_t GC_blocked_sp = NULL; + /* NULL value means we are not inside GC_do_blocking() call. */ +# ifdef IA64 + STATIC ptr_t GC_blocked_register_sp = NULL; +# endif + +GC_INNER struct GC_traced_stack_sect_s *GC_traced_stack_sect = NULL; + +/* This is nearly the same as in win32_threads.c */ +GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type fn, + void * client_data) +{ + struct GC_traced_stack_sect_s stacksect; + GC_ASSERT(GC_is_initialized); + + /* Adjust our stack base value (this could happen if */ + /* GC_get_main_stack_base() is unimplemented or broken for */ + /* the platform). */ + if ((word)GC_stackbottom HOTTER_THAN (word)(&stacksect)) + GC_stackbottom = (ptr_t)(&stacksect); + + if (GC_blocked_sp == NULL) { + /* We are not inside GC_do_blocking() - do nothing more. */ + client_data = fn(client_data); + /* Prevent treating the above as a tail call. */ + GC_noop1((word)(&stacksect)); + return client_data; /* result */ + } + + /* Setup new "stack section". */ + stacksect.saved_stack_ptr = GC_blocked_sp; +# ifdef IA64 + /* This is the same as in GC_call_with_stack_base(). */ + stacksect.backing_store_end = GC_save_regs_in_stack(); + /* Unnecessarily flushes register stack, */ + /* but that probably doesn't hurt. */ + stacksect.saved_backing_store_ptr = GC_blocked_register_sp; +# endif + stacksect.prev = GC_traced_stack_sect; + GC_blocked_sp = NULL; + GC_traced_stack_sect = &stacksect; + + client_data = fn(client_data); + GC_ASSERT(GC_blocked_sp == NULL); + GC_ASSERT(GC_traced_stack_sect == &stacksect); + +# if defined(CPPCHECK) + GC_noop1((word)GC_traced_stack_sect - (word)GC_blocked_sp); +# endif + /* Restore original "stack section". */ + GC_traced_stack_sect = stacksect.prev; +# ifdef IA64 + GC_blocked_register_sp = stacksect.saved_backing_store_ptr; +# endif + GC_blocked_sp = stacksect.saved_stack_ptr; + + return client_data; /* result */ +} + +/* This is nearly the same as in win32_threads.c */ +STATIC void GC_do_blocking_inner(ptr_t data, void * context GC_ATTR_UNUSED) +{ + struct blocking_data * d = (struct blocking_data *) data; + GC_ASSERT(GC_is_initialized); + GC_ASSERT(GC_blocked_sp == NULL); +# ifdef SPARC + GC_blocked_sp = GC_save_regs_in_stack(); +# else + GC_blocked_sp = (ptr_t) &d; /* save approx. sp */ +# endif +# ifdef IA64 + GC_blocked_register_sp = GC_save_regs_in_stack(); +# endif + + d -> client_data = (d -> fn)(d -> client_data); + +# ifdef SPARC + GC_ASSERT(GC_blocked_sp != NULL); +# else + GC_ASSERT(GC_blocked_sp == (ptr_t)(&d)); +# endif +# if defined(CPPCHECK) + GC_noop1((word)GC_blocked_sp); +# endif + GC_blocked_sp = NULL; +} + +#endif /* !THREADS */ + +/* Wrapper for functions that are likely to block (or, at least, do not */ +/* allocate garbage collected memory and/or manipulate pointers to the */ +/* garbage collected heap) for an appreciable length of time. */ +/* In the single threaded case, GC_do_blocking() (together */ +/* with GC_call_with_gc_active()) might be used to make stack scanning */ +/* more precise (i.e. scan only stack frames of functions that allocate */ +/* garbage collected memory and/or manipulate pointers to the garbage */ +/* collected heap). */ +GC_API void * GC_CALL GC_do_blocking(GC_fn_type fn, void * client_data) +{ + struct blocking_data my_data; + + my_data.fn = fn; + my_data.client_data = client_data; + GC_with_callee_saves_pushed(GC_do_blocking_inner, (ptr_t)(&my_data)); + return my_data.client_data; /* result */ +} + +#if !defined(NO_DEBUGGING) + GC_API void GC_CALL GC_dump(void) + { + DCL_LOCK_STATE; + + LOCK(); + GC_dump_named(NULL); + UNLOCK(); + } + + GC_API void GC_CALL GC_dump_named(const char *name) + { +# ifndef NO_CLOCK + CLOCK_TYPE current_time; + + GET_TIME(current_time); +# endif + if (name != NULL) { + GC_printf("***GC Dump %s\n", name); + } else { + GC_printf("***GC Dump collection #%lu\n", (unsigned long)GC_gc_no); + } +# ifndef NO_CLOCK + /* Note that the time is wrapped in ~49 days if sizeof(long)==4. */ + GC_printf("Time since GC init: %lu msecs\n", + MS_TIME_DIFF(current_time, GC_init_time)); +# endif + + GC_printf("\n***Static roots:\n"); + GC_print_static_roots(); + GC_printf("\n***Heap sections:\n"); + GC_print_heap_sects(); + GC_printf("\n***Free blocks:\n"); + GC_print_hblkfreelist(); + GC_printf("\n***Blocks in use:\n"); + GC_print_block_list(); + } +#endif /* !NO_DEBUGGING */ + +static void block_add_size(struct hblk *h, word pbytes) +{ + hdr *hhdr = HDR(h); + *(word *)pbytes += (WORDS_TO_BYTES(hhdr->hb_sz) + (HBLKSIZE - 1)) + & ~(word)(HBLKSIZE - 1); +} + +GC_API size_t GC_CALL GC_get_memory_use(void) +{ + word bytes = 0; + DCL_LOCK_STATE; + + LOCK(); + GC_apply_to_all_blocks(block_add_size, (word)(&bytes)); + UNLOCK(); + return (size_t)bytes; +} + +/* Getter functions for the public Read-only variables. */ + +/* GC_get_gc_no() is unsynchronized and should be typically called */ +/* inside the context of GC_call_with_alloc_lock() to prevent data */ +/* races (on multiprocessors). */ +GC_API GC_word GC_CALL GC_get_gc_no(void) +{ + return GC_gc_no; +} + +#ifdef THREADS + GC_API int GC_CALL GC_get_parallel(void) + { + /* GC_parallel is initialized at start-up. */ + return GC_parallel; + } + + GC_INNER GC_on_thread_event_proc GC_on_thread_event = 0; + + GC_API void GC_CALL GC_set_on_thread_event(GC_on_thread_event_proc fn) + { + /* fn may be 0 (means no event notifier). */ + DCL_LOCK_STATE; + LOCK(); + GC_on_thread_event = fn; + UNLOCK(); + } + + GC_API GC_on_thread_event_proc GC_CALL GC_get_on_thread_event(void) + { + GC_on_thread_event_proc fn; + DCL_LOCK_STATE; + LOCK(); + fn = GC_on_thread_event; + UNLOCK(); + return fn; + } +#endif /* THREADS */ + +/* Setter and getter functions for the public R/W function variables. */ +/* These functions are synchronized (like GC_set_warn_proc() and */ +/* GC_get_warn_proc()). */ + +GC_API void GC_CALL GC_set_oom_fn(GC_oom_func fn) +{ + GC_ASSERT(NONNULL_ARG_NOT_NULL(fn)); + DCL_LOCK_STATE; + LOCK(); + GC_oom_fn = fn; + UNLOCK(); +} + +GC_API GC_oom_func GC_CALL GC_get_oom_fn(void) +{ + GC_oom_func fn; + DCL_LOCK_STATE; + LOCK(); + fn = GC_oom_fn; + UNLOCK(); + return fn; +} + +GC_API void GC_CALL GC_set_on_heap_resize(GC_on_heap_resize_proc fn) +{ + /* fn may be 0 (means no event notifier). */ + DCL_LOCK_STATE; + LOCK(); + GC_on_heap_resize = fn; + UNLOCK(); +} + +GC_API GC_on_heap_resize_proc GC_CALL GC_get_on_heap_resize(void) +{ + GC_on_heap_resize_proc fn; + DCL_LOCK_STATE; + LOCK(); + fn = GC_on_heap_resize; + UNLOCK(); + return fn; +} + +GC_API void GC_CALL GC_set_finalizer_notifier(GC_finalizer_notifier_proc fn) +{ + /* fn may be 0 (means no finalizer notifier). */ + DCL_LOCK_STATE; + LOCK(); + GC_finalizer_notifier = fn; + UNLOCK(); +} + +GC_API GC_finalizer_notifier_proc GC_CALL GC_get_finalizer_notifier(void) +{ + GC_finalizer_notifier_proc fn; + DCL_LOCK_STATE; + LOCK(); + fn = GC_finalizer_notifier; + UNLOCK(); + return fn; +} + +/* Setter and getter functions for the public numeric R/W variables. */ +/* It is safe to call these functions even before GC_INIT(). */ +/* These functions are unsynchronized and should be typically called */ +/* inside the context of GC_call_with_alloc_lock() (if called after */ +/* GC_INIT()) to prevent data races (unless it is guaranteed the */ +/* collector is not multi-threaded at that execution point). */ + +GC_API void GC_CALL GC_set_find_leak(int value) +{ + /* value is of boolean type. */ + GC_find_leak = value; +} + +GC_API int GC_CALL GC_get_find_leak(void) +{ + return GC_find_leak; +} + +GC_API void GC_CALL GC_set_all_interior_pointers(int value) +{ + DCL_LOCK_STATE; + + GC_all_interior_pointers = value ? 1 : 0; + if (GC_is_initialized) { + /* It is not recommended to change GC_all_interior_pointers value */ + /* after GC is initialized but it seems GC could work correctly */ + /* even after switching the mode. */ + LOCK(); + GC_initialize_offsets(); /* NOTE: this resets manual offsets as well */ + if (!GC_all_interior_pointers) + GC_bl_init_no_interiors(); + UNLOCK(); + } +} + +GC_API int GC_CALL GC_get_all_interior_pointers(void) +{ + return GC_all_interior_pointers; +} + +GC_API void GC_CALL GC_set_finalize_on_demand(int value) +{ + GC_ASSERT(value != -1); + /* value is of boolean type. */ + GC_finalize_on_demand = value; +} + +GC_API int GC_CALL GC_get_finalize_on_demand(void) +{ + return GC_finalize_on_demand; +} + +GC_API void GC_CALL GC_set_java_finalization(int value) +{ + GC_ASSERT(value != -1); + /* value is of boolean type. */ + GC_java_finalization = value; +} + +GC_API int GC_CALL GC_get_java_finalization(void) +{ + return GC_java_finalization; +} + +GC_API void GC_CALL GC_set_dont_expand(int value) +{ + GC_ASSERT(value != -1); + /* value is of boolean type. */ + GC_dont_expand = value; +} + +GC_API int GC_CALL GC_get_dont_expand(void) +{ + return GC_dont_expand; +} + +GC_API void GC_CALL GC_set_no_dls(int value) +{ + GC_ASSERT(value != -1); + /* value is of boolean type. */ + GC_no_dls = value; +} + +GC_API int GC_CALL GC_get_no_dls(void) +{ + return GC_no_dls; +} + +GC_API void GC_CALL GC_set_non_gc_bytes(GC_word value) +{ + GC_non_gc_bytes = value; +} + +GC_API GC_word GC_CALL GC_get_non_gc_bytes(void) +{ + return GC_non_gc_bytes; +} + +GC_API void GC_CALL GC_set_free_space_divisor(GC_word value) +{ + GC_ASSERT(value > 0); + GC_free_space_divisor = value; +} + +GC_API GC_word GC_CALL GC_get_free_space_divisor(void) +{ + return GC_free_space_divisor; +} + +GC_API void GC_CALL GC_set_max_retries(GC_word value) +{ + GC_ASSERT(value != ~(word)0); + GC_max_retries = value; +} + +GC_API GC_word GC_CALL GC_get_max_retries(void) +{ + return GC_max_retries; +} + +GC_API void GC_CALL GC_set_dont_precollect(int value) +{ + GC_ASSERT(value != -1); + /* value is of boolean type. */ + GC_dont_precollect = value; +} + +GC_API int GC_CALL GC_get_dont_precollect(void) +{ + return GC_dont_precollect; +} + +GC_API void GC_CALL GC_set_full_freq(int value) +{ + GC_ASSERT(value >= 0); + GC_full_freq = value; +} + +GC_API int GC_CALL GC_get_full_freq(void) +{ + return GC_full_freq; +} + +GC_API void GC_CALL GC_set_time_limit(unsigned long value) +{ + GC_ASSERT(value != (unsigned long)-1L); + GC_time_limit = value * 1000000; +} + +GC_API unsigned long GC_CALL GC_get_time_limit(void) +{ + return (unsigned long)(GC_time_limit / 1000000); +} + +GC_API void GC_CALL GC_set_time_limit_ns(unsigned long long value) +{ + GC_ASSERT(value != (unsigned long long)-1L); + GC_time_limit = value; +} + +GC_API unsigned long long GC_CALL GC_get_time_limit_ns(void) +{ + return GC_time_limit; +} + +GC_API void GC_CALL GC_set_force_unmap_on_gcollect(int value) +{ + GC_force_unmap_on_gcollect = (GC_bool)value; +} + +GC_API int GC_CALL GC_get_force_unmap_on_gcollect(void) +{ + return (int)GC_force_unmap_on_gcollect; +} + +/* Unity specific APIs */ +GC_API void GC_CALL GC_stop_world_external() +{ + LOCK(); + STOP_WORLD(); +} + +GC_API void GC_CALL GC_start_world_external() +{ + START_WORLD(); + UNLOCK(); +} + +/* Disable incremental GC. Only tested with MANUAL_VDB mode. Might */ +/* require extra teardown work when using other VDB configs.*/ +GC_API void GC_CALL GC_disable_incremental(void) +{ + LOCK(); + GC_gcollect_inner(); +#ifndef GC_DISABLE_INCREMENTAL + GC_incremental = FALSE; +#endif + UNLOCK(); +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/new_hblk.c b/unity-2019.4.24f1-mbe/external/bdwgc/new_hblk.c new file mode 100644 index 000000000..5e3ce0682 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/new_hblk.c @@ -0,0 +1,191 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 2000 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#include "private/gc_priv.h" + +/* + * This file contains the functions: + * ptr_t GC_build_flXXX(h, old_fl) + * void GC_new_hblk(size) + */ + +#include + +#ifndef SMALL_CONFIG + /* Build a free list for size 2 (words) cleared objects inside */ + /* hblk h. Set the last link to be ofl. Return a pointer to the */ + /* first free list entry. */ + STATIC ptr_t GC_build_fl_clear2(struct hblk *h, ptr_t ofl) + { + word * p = (word *)(h -> hb_body); + word * lim = (word *)(h + 1); + + p[0] = (word)ofl; + p[1] = 0; + p[2] = (word)p; + p[3] = 0; + p += 4; + for (; (word)p < (word)lim; p += 4) { + p[0] = (word)(p-2); + p[1] = 0; + p[2] = (word)p; + p[3] = 0; + }; + return((ptr_t)(p-2)); + } + + /* The same for size 4 cleared objects. */ + STATIC ptr_t GC_build_fl_clear4(struct hblk *h, ptr_t ofl) + { + word * p = (word *)(h -> hb_body); + word * lim = (word *)(h + 1); + + p[0] = (word)ofl; + p[1] = 0; + p[2] = 0; + p[3] = 0; + p += 4; + for (; (word)p < (word)lim; p += 4) { + GC_PREFETCH_FOR_WRITE((ptr_t)(p + 64)); + p[0] = (word)(p-4); + p[1] = 0; + CLEAR_DOUBLE(p+2); + }; + return((ptr_t)(p-4)); + } + + /* The same for size 2 uncleared objects. */ + STATIC ptr_t GC_build_fl2(struct hblk *h, ptr_t ofl) + { + word * p = (word *)(h -> hb_body); + word * lim = (word *)(h + 1); + + p[0] = (word)ofl; + p[2] = (word)p; + p += 4; + for (; (word)p < (word)lim; p += 4) { + p[0] = (word)(p-2); + p[2] = (word)p; + }; + return((ptr_t)(p-2)); + } + + /* The same for size 4 uncleared objects. */ + STATIC ptr_t GC_build_fl4(struct hblk *h, ptr_t ofl) + { + word * p = (word *)(h -> hb_body); + word * lim = (word *)(h + 1); + + p[0] = (word)ofl; + p[4] = (word)p; + p += 8; + for (; (word)p < (word)lim; p += 8) { + GC_PREFETCH_FOR_WRITE((ptr_t)(p + 64)); + p[0] = (word)(p-4); + p[4] = (word)p; + }; + return((ptr_t)(p-4)); + } +#endif /* !SMALL_CONFIG */ + +/* Build a free list for objects of size sz inside heap block h. */ +/* Clear objects inside h if clear is set. Add list to the end of */ +/* the free list we build. Return the new free list. */ +/* This could be called without the main GC lock, if we ensure that */ +/* there is no concurrent collection which might reclaim objects that */ +/* we have not yet allocated. */ +GC_INNER ptr_t GC_build_fl(struct hblk *h, size_t sz, GC_bool clear, + ptr_t list) +{ + word *p, *prev; + word *last_object; /* points to last object in new hblk */ + + /* Do a few prefetches here, just because its cheap. */ + /* If we were more serious about it, these should go inside */ + /* the loops. But write prefetches usually don't seem to */ + /* matter much. */ + GC_PREFETCH_FOR_WRITE((ptr_t)h); + GC_PREFETCH_FOR_WRITE((ptr_t)h + 128); + GC_PREFETCH_FOR_WRITE((ptr_t)h + 256); + GC_PREFETCH_FOR_WRITE((ptr_t)h + 378); +# ifndef SMALL_CONFIG + /* Handle small objects sizes more efficiently. For larger objects */ + /* the difference is less significant. */ + switch (sz) { + case 2: if (clear) { + return GC_build_fl_clear2(h, list); + } else { + return GC_build_fl2(h, list); + } + case 4: if (clear) { + return GC_build_fl_clear4(h, list); + } else { + return GC_build_fl4(h, list); + } + default: + break; + } +# endif /* !SMALL_CONFIG */ + + /* Clear the page if necessary. */ + if (clear) BZERO(h, HBLKSIZE); + + /* Add objects to free list */ + p = (word *)(h -> hb_body) + sz; /* second object in *h */ + prev = (word *)(h -> hb_body); /* One object behind p */ + last_object = (word *)((char *)h + HBLKSIZE); + last_object -= sz; + /* Last place for last object to start */ + + /* make a list of all objects in *h with head as last object */ + while ((word)p <= (word)last_object) { + /* current object's link points to last object */ + obj_link(p) = (ptr_t)prev; + prev = p; + p += sz; + } + p -= sz; /* p now points to last object */ + + /* Put p (which is now head of list of objects in *h) as first */ + /* pointer in the appropriate free list for this size. */ + *(ptr_t *)h = list; + return ((ptr_t)p); +} + +/* Allocate a new heapblock for small objects of size gran granules. */ +/* Add all of the heapblock's objects to the free list for objects */ +/* of that size. Set all mark bits if objects are uncollectible. */ +/* Will fail to do anything if we are out of memory. */ +GC_INNER void GC_new_hblk(size_t gran, int kind) +{ + struct hblk *h; /* the new heap block */ + GC_bool clear = GC_obj_kinds[kind].ok_init; + + GC_STATIC_ASSERT((sizeof (struct hblk)) == HBLKSIZE); + + if (GC_debugging_started) clear = TRUE; + + /* Allocate a new heap block */ + h = GC_allochblk(GRANULES_TO_BYTES(gran), kind, 0); + if (h == 0) return; + + /* Mark all objects if appropriate. */ + if (IS_UNCOLLECTABLE(kind)) GC_set_hdr_marks(HDR(h)); + + /* Build the free list */ + GC_obj_kinds[kind].ok_freelist[gran] = + GC_build_fl(h, GRANULES_TO_WORDS(gran), clear, + (ptr_t)GC_obj_kinds[kind].ok_freelist[gran]); +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/obj_map.c b/unity-2019.4.24f1-mbe/external/bdwgc/obj_map.c new file mode 100644 index 000000000..9fdeb0afc --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/obj_map.c @@ -0,0 +1,90 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991, 1992 by Xerox Corporation. All rights reserved. + * Copyright (c) 1999-2001 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#include "private/gc_priv.h" + +/* Routines for maintaining maps describing heap block + * layouts for various object sizes. Allows fast pointer validity checks + * and fast location of object start locations on machines (such as SPARC) + * with slow division. + */ + +/* Consider pointers that are offset bytes displaced from the beginning */ +/* of an object to be valid. */ + +GC_API void GC_CALL GC_register_displacement(size_t offset) +{ + DCL_LOCK_STATE; + + LOCK(); + GC_register_displacement_inner(offset); + UNLOCK(); +} + +GC_INNER void GC_register_displacement_inner(size_t offset) +{ + if (offset >= VALID_OFFSET_SZ) { + ABORT("Bad argument to GC_register_displacement"); + } + if (!GC_valid_offsets[offset]) { + GC_valid_offsets[offset] = TRUE; + GC_modws_valid_offsets[offset % sizeof(word)] = TRUE; + } +} + +#ifdef MARK_BIT_PER_GRANULE + /* Add a heap block map for objects of size granules to obj_map. */ + /* Return FALSE on failure. */ + /* A size of 0 granules is used for large objects. */ + GC_INNER GC_bool GC_add_map_entry(size_t granules) + { + unsigned displ; + unsigned short * new_map; + + if (granules > BYTES_TO_GRANULES(MAXOBJBYTES)) granules = 0; + if (GC_obj_map[granules] != 0) { + return(TRUE); + } + new_map = (unsigned short *)GC_scratch_alloc(MAP_LEN * sizeof(short)); + if (new_map == 0) return(FALSE); + GC_COND_LOG_PRINTF( + "Adding block map for size of %u granules (%u bytes)\n", + (unsigned)granules, (unsigned)GRANULES_TO_BYTES(granules)); + if (granules == 0) { + for (displ = 0; displ < BYTES_TO_GRANULES(HBLKSIZE); displ++) { + new_map[displ] = 1; /* Nonzero to get us out of marker fast path. */ + } + } else { + for (displ = 0; displ < BYTES_TO_GRANULES(HBLKSIZE); displ++) { + new_map[displ] = (unsigned short)(displ % granules); + } + } + GC_obj_map[granules] = new_map; + return(TRUE); + } +#endif /* MARK_BIT_PER_GRANULE */ + +GC_INNER void GC_initialize_offsets(void) +{ + unsigned i; + if (GC_all_interior_pointers) { + for (i = 0; i < VALID_OFFSET_SZ; ++i) + GC_valid_offsets[i] = TRUE; + } else { + BZERO(GC_valid_offsets, sizeof(GC_valid_offsets)); + for (i = 0; i < sizeof(word); ++i) + GC_modws_valid_offsets[i] = FALSE; + } +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/os_dep.c b/unity-2019.4.24f1-mbe/external/bdwgc/os_dep.c new file mode 100644 index 000000000..7bee84c1e --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/os_dep.c @@ -0,0 +1,4884 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#include "private/gc_priv.h" + +#if !defined(OS2) && !defined(PCR) && !defined(AMIGA) && !defined(MACOS) \ + && !defined(MSWINCE) && !defined(SN_TARGET_ORBIS) \ + && !defined(SN_TARGET_PSP2) && !defined(__CC_ARM) +# include +# if !defined(MSWIN32) && !defined(MSWIN_XBOX1) +# include +# endif +#endif + +#include +#if defined(MSWINCE) || defined(SN_TARGET_PS3) +# define SIGSEGV 0 /* value is irrelevant */ +#else +# include +#endif + +#if defined(UNIX_LIKE) || defined(CYGWIN32) || defined(NACL) \ + || defined(SYMBIAN) +# include +#endif + +#if defined(LINUX) || defined(LINUX_STACKBOTTOM) +# include +#endif + +/* Blatantly OS dependent routines, except for those that are related */ +/* to dynamic loading. */ + +#ifdef AMIGA +# define GC_AMIGA_DEF +# include "extra/AmigaOS.c" +# undef GC_AMIGA_DEF +#endif + +#if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32) +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +# endif +# define NOSERVICE +# include + /* It's not clear this is completely kosher under Cygwin. But it */ + /* allows us to get a working GC_get_stack_base. */ +#endif + +#ifdef MACOS +# include +#endif + +#ifdef IRIX5 +# include +# include /* for locking */ +#endif + +#if defined(MMAP_SUPPORTED) || defined(ADD_HEAP_GUARD_PAGES) +# if defined(USE_MUNMAP) && !defined(USE_MMAP) && !defined(CPPCHECK) +# error "invalid config - USE_MUNMAP requires USE_MMAP" +# endif +# include +# include +# include +# include +#endif + +#ifdef DARWIN + /* for get_etext and friends */ +# include +#endif + +#ifdef DJGPP + /* Apparently necessary for djgpp 2.01. May cause problems with */ + /* other versions. */ + typedef long unsigned int caddr_t; +#endif + +#ifdef PCR +# include "il/PCR_IL.h" +# include "th/PCR_ThCtl.h" +# include "mm/PCR_MM.h" +#endif + +#if defined(GC_DARWIN_THREADS) && defined(MPROTECT_VDB) + /* Declare GC_mprotect_stop and GC_mprotect_resume as extern "C". */ +# include "private/darwin_stop_world.h" +#endif + +#if !defined(NO_EXECUTE_PERMISSION) + STATIC GC_bool GC_pages_executable = TRUE; +#else + STATIC GC_bool GC_pages_executable = FALSE; +#endif +#define IGNORE_PAGES_EXECUTABLE 1 + /* Undefined on GC_pages_executable real use. */ + +#ifdef NEED_PROC_MAPS +/* We need to parse /proc/self/maps, either to find dynamic libraries, */ +/* and/or to find the register backing store base (IA64). Do it once */ +/* here. */ + +#define READ read + +/* Repeatedly perform a read call until the buffer is filled or */ +/* we encounter EOF. */ +STATIC ssize_t GC_repeat_read(int fd, char *buf, size_t count) +{ + size_t num_read = 0; + + ASSERT_CANCEL_DISABLED(); + while (num_read < count) { + ssize_t result = READ(fd, buf + num_read, count - num_read); + + if (result < 0) return result; + if (result == 0) break; + num_read += result; + } + return num_read; +} + +#ifdef THREADS + /* Determine the length of a file by incrementally reading it into a */ + /* buffer. This would be silly to use it on a file supporting lseek, */ + /* but Linux /proc files usually do not. */ + STATIC size_t GC_get_file_len(int f) + { + size_t total = 0; + ssize_t result; +# define GET_FILE_LEN_BUF_SZ 500 + char buf[GET_FILE_LEN_BUF_SZ]; + + do { + result = read(f, buf, GET_FILE_LEN_BUF_SZ); + if (result == -1) return 0; + total += result; + } while (result > 0); + return total; + } + + STATIC size_t GC_get_maps_len(void) + { + int f = open("/proc/self/maps", O_RDONLY); + size_t result; + if (f < 0) return 0; /* treat missing file as empty */ + result = GC_get_file_len(f); + close(f); + return result; + } +#endif /* THREADS */ + +/* Copy the contents of /proc/self/maps to a buffer in our address */ +/* space. Return the address of the buffer, or zero on failure. */ +/* This code could be simplified if we could determine its size ahead */ +/* of time. */ +GC_INNER char * GC_get_maps(void) +{ + ssize_t result; + static char *maps_buf = NULL; + static size_t maps_buf_sz = 1; + size_t maps_size, old_maps_size = 0; + + /* The buffer is essentially static, so there must be a single client. */ + GC_ASSERT(I_HOLD_LOCK()); + + /* Note that in the presence of threads, the maps file can */ + /* essentially shrink asynchronously and unexpectedly as */ + /* threads that we already think of as dead release their */ + /* stacks. And there is no easy way to read the entire */ + /* file atomically. This is arguably a misfeature of the */ + /* /proc/.../maps interface. */ + /* Since we expect the file can grow asynchronously in rare */ + /* cases, it should suffice to first determine */ + /* the size (using lseek or read), and then to reread the */ + /* file. If the size is inconsistent we have to retry. */ + /* This only matters with threads enabled, and if we use */ + /* this to locate roots (not the default). */ + +# ifdef THREADS + /* Determine the initial size of /proc/self/maps. */ + /* Note that lseek doesn't work, at least as of 2.6.15. */ + maps_size = GC_get_maps_len(); + if (0 == maps_size) return 0; +# else + maps_size = 4000; /* Guess */ +# endif + + /* Read /proc/self/maps, growing maps_buf as necessary. */ + /* Note that we may not allocate conventionally, and */ + /* thus can't use stdio. */ + do { + int f; + + while (maps_size >= maps_buf_sz) { + GC_scratch_recycle_no_gww(maps_buf, maps_buf_sz); + /* Grow only by powers of 2, since we leak "too small" buffers.*/ + while (maps_size >= maps_buf_sz) maps_buf_sz *= 2; + maps_buf = GC_scratch_alloc(maps_buf_sz); +# ifdef THREADS + /* Recompute initial length, since we allocated. */ + /* This can only happen a few times per program */ + /* execution. */ + maps_size = GC_get_maps_len(); + if (0 == maps_size) return 0; +# endif + if (maps_buf == 0) return 0; + } + GC_ASSERT(maps_buf_sz >= maps_size + 1); + f = open("/proc/self/maps", O_RDONLY); + if (-1 == f) return 0; +# ifdef THREADS + old_maps_size = maps_size; +# endif + maps_size = 0; + do { + result = GC_repeat_read(f, maps_buf, maps_buf_sz-1); + if (result <= 0) + break; + maps_size += result; + } while ((size_t)result == maps_buf_sz-1); + close(f); + if (result <= 0) + return 0; +# ifdef THREADS + if (maps_size > old_maps_size) { + /* This might be caused by e.g. thread creation. */ + WARN("Unexpected asynchronous /proc/self/maps growth" + " (to %" WARN_PRIdPTR " bytes)\n", maps_size); + } +# endif + } while (maps_size >= maps_buf_sz || maps_size < old_maps_size); + /* In the single-threaded case, the second clause is false. */ + maps_buf[maps_size] = '\0'; + return maps_buf; +} + +/* + * GC_parse_map_entry parses an entry from /proc/self/maps so we can + * locate all writable data segments that belong to shared libraries. + * The format of one of these entries and the fields we care about + * is as follows: + * XXXXXXXX-XXXXXXXX r-xp 00000000 30:05 260537 name of mapping...\n + * ^^^^^^^^ ^^^^^^^^ ^^^^ ^^ + * start end prot maj_dev + * + * Note that since about august 2003 kernels, the columns no longer have + * fixed offsets on 64-bit kernels. Hence we no longer rely on fixed offsets + * anywhere, which is safer anyway. + */ + +/* Assign various fields of the first line in buf_ptr to (*start), */ +/* (*end), (*prot), (*maj_dev) and (*mapping_name). mapping_name may */ +/* be NULL. (*prot) and (*mapping_name) are assigned pointers into the */ +/* original buffer. */ +#if (defined(DYNAMIC_LOADING) && defined(USE_PROC_FOR_LIBRARIES)) \ + || defined(IA64) || defined(INCLUDE_LINUX_THREAD_DESCR) \ + || defined(REDIRECT_MALLOC) + GC_INNER char *GC_parse_map_entry(char *buf_ptr, ptr_t *start, ptr_t *end, + char **prot, unsigned int *maj_dev, + char **mapping_name) + { + unsigned char *start_start, *end_start, *maj_dev_start; + unsigned char *p; /* unsigned for isspace, isxdigit */ + + if (buf_ptr == NULL || *buf_ptr == '\0') { + return NULL; + } + + p = (unsigned char *)buf_ptr; + while (isspace(*p)) ++p; + start_start = p; + GC_ASSERT(isxdigit(*start_start)); + *start = (ptr_t)strtoul((char *)start_start, (char **)&p, 16); + GC_ASSERT(*p=='-'); + + ++p; + end_start = p; + GC_ASSERT(isxdigit(*end_start)); + *end = (ptr_t)strtoul((char *)end_start, (char **)&p, 16); + GC_ASSERT(isspace(*p)); + + while (isspace(*p)) ++p; + GC_ASSERT(*p == 'r' || *p == '-'); + *prot = (char *)p; + /* Skip past protection field to offset field */ + while (!isspace(*p)) ++p; while (isspace(*p)) ++p; + GC_ASSERT(isxdigit(*p)); + /* Skip past offset field, which we ignore */ + while (!isspace(*p)) ++p; while (isspace(*p)) ++p; + maj_dev_start = p; + GC_ASSERT(isxdigit(*maj_dev_start)); + *maj_dev = strtoul((char *)maj_dev_start, NULL, 16); + + if (mapping_name == 0) { + while (*p && *p++ != '\n'); + } else { + while (*p && *p != '\n' && *p != '/' && *p != '[') p++; + *mapping_name = (char *)p; + while (*p && *p++ != '\n'); + } + return (char *)p; + } +#endif /* REDIRECT_MALLOC || DYNAMIC_LOADING || IA64 || ... */ + +#if defined(IA64) || defined(INCLUDE_LINUX_THREAD_DESCR) + /* Try to read the backing store base from /proc/self/maps. */ + /* Return the bounds of the writable mapping with a 0 major device, */ + /* which includes the address passed as data. */ + /* Return FALSE if there is no such mapping. */ + GC_INNER GC_bool GC_enclosing_mapping(ptr_t addr, ptr_t *startp, + ptr_t *endp) + { + char *prot; + ptr_t my_start, my_end; + unsigned int maj_dev; + char *maps = GC_get_maps(); + char *buf_ptr = maps; + + if (0 == maps) return(FALSE); + for (;;) { + buf_ptr = GC_parse_map_entry(buf_ptr, &my_start, &my_end, + &prot, &maj_dev, 0); + + if (buf_ptr == NULL) return FALSE; + if (prot[1] == 'w' && maj_dev == 0) { + if ((word)my_end > (word)addr && (word)my_start <= (word)addr) { + *startp = my_start; + *endp = my_end; + return TRUE; + } + } + } + return FALSE; + } +#endif /* IA64 || INCLUDE_LINUX_THREAD_DESCR */ + +#if defined(REDIRECT_MALLOC) + /* Find the text(code) mapping for the library whose name, after */ + /* stripping the directory part, starts with nm. */ + GC_INNER GC_bool GC_text_mapping(char *nm, ptr_t *startp, ptr_t *endp) + { + size_t nm_len = strlen(nm); + char *prot; + char *map_path; + ptr_t my_start, my_end; + unsigned int maj_dev; + char *maps = GC_get_maps(); + char *buf_ptr = maps; + + if (0 == maps) return(FALSE); + for (;;) { + buf_ptr = GC_parse_map_entry(buf_ptr, &my_start, &my_end, + &prot, &maj_dev, &map_path); + + if (buf_ptr == NULL) return FALSE; + if (prot[0] == 'r' && prot[1] == '-' && prot[2] == 'x') { + char *p = map_path; + /* Set p to point just past last slash, if any. */ + while (*p != '\0' && *p != '\n' && *p != ' ' && *p != '\t') ++p; + while (*p != '/' && (word)p >= (word)map_path) --p; + ++p; + if (strncmp(nm, p, nm_len) == 0) { + *startp = my_start; + *endp = my_end; + return TRUE; + } + } + } + return FALSE; + } +#endif /* REDIRECT_MALLOC */ + +#ifdef IA64 + static ptr_t backing_store_base_from_proc(void) + { + ptr_t my_start, my_end; + if (!GC_enclosing_mapping(GC_save_regs_in_stack(), &my_start, &my_end)) { + GC_COND_LOG_PRINTF("Failed to find backing store base from /proc\n"); + return 0; + } + return my_start; + } +#endif + +#endif /* NEED_PROC_MAPS */ + +#if defined(SEARCH_FOR_DATA_START) + /* The I386 case can be handled without a search. The Alpha case */ + /* used to be handled differently as well, but the rules changed */ + /* for recent Linux versions. This seems to be the easiest way to */ + /* cover all versions. */ + +# if defined(LINUX) || defined(HURD) + /* Some Linux distributions arrange to define __data_start. Some */ + /* define data_start as a weak symbol. The latter is technically */ + /* broken, since the user program may define data_start, in which */ + /* case we lose. Nonetheless, we try both, preferring __data_start.*/ + /* We assume gcc-compatible pragmas. */ + EXTERN_C_BEGIN +# pragma weak __data_start +# pragma weak data_start + extern int __data_start[], data_start[]; +# ifdef HOST_ANDROID +# pragma weak _etext +# pragma weak __dso_handle + extern int _etext[], __dso_handle[]; +# endif + EXTERN_C_END +# endif /* LINUX */ + + ptr_t GC_data_start = NULL; + + GC_INNER void GC_init_linux_data_start(void) + { + ptr_t data_end = DATAEND; + +# if (defined(LINUX) || defined(HURD)) && !defined(IGNORE_PROG_DATA_START) + /* Try the easy approaches first: */ +# ifdef HOST_ANDROID + /* Workaround for "gold" (default) linker (as of Android NDK r10e). */ + if ((word)__data_start < (word)_etext + && (word)_etext < (word)__dso_handle) { + GC_data_start = (ptr_t)(__dso_handle); +# ifdef DEBUG_ADD_DEL_ROOTS + GC_log_printf( + "__data_start is wrong; using __dso_handle as data start\n"); +# endif + } else +# endif + /* else */ if (COVERT_DATAFLOW(__data_start) != 0) { + GC_data_start = (ptr_t)(__data_start); + } else { + GC_data_start = (ptr_t)(data_start); + } + if (COVERT_DATAFLOW(GC_data_start) != 0) { + if ((word)GC_data_start > (word)data_end) + ABORT_ARG2("Wrong __data_start/_end pair", + ": %p .. %p", (void *)GC_data_start, (void *)data_end); + return; + } +# ifdef DEBUG_ADD_DEL_ROOTS + GC_log_printf("__data_start not provided\n"); +# endif +# endif /* LINUX */ + + if (GC_no_dls) { + /* Not needed, avoids the SIGSEGV caused by */ + /* GC_find_limit which complicates debugging. */ + GC_data_start = data_end; /* set data root size to 0 */ + return; + } + + GC_data_start = GC_find_limit(data_end, FALSE); + } +#endif /* SEARCH_FOR_DATA_START */ + +#ifdef ECOS + +# ifndef ECOS_GC_MEMORY_SIZE +# define ECOS_GC_MEMORY_SIZE (448 * 1024) +# endif /* ECOS_GC_MEMORY_SIZE */ + + /* FIXME: This is a simple way of allocating memory which is */ + /* compatible with ECOS early releases. Later releases use a more */ + /* sophisticated means of allocating memory than this simple static */ + /* allocator, but this method is at least bound to work. */ + static char ecos_gc_memory[ECOS_GC_MEMORY_SIZE]; + static char *ecos_gc_brk = ecos_gc_memory; + + static void *tiny_sbrk(ptrdiff_t increment) + { + void *p = ecos_gc_brk; + ecos_gc_brk += increment; + if ((word)ecos_gc_brk > (word)(ecos_gc_memory + sizeof(ecos_gc_memory))) { + ecos_gc_brk -= increment; + return NULL; + } + return p; + } +# define sbrk tiny_sbrk +#endif /* ECOS */ + +#if defined(NETBSD) && defined(__ELF__) + ptr_t GC_data_start = NULL; + + EXTERN_C_BEGIN + extern char **environ; + EXTERN_C_END + + GC_INNER void GC_init_netbsd_elf(void) + { + /* This may need to be environ, without the underscore, for */ + /* some versions. */ + GC_data_start = GC_find_limit((ptr_t)&environ, FALSE); + } +#endif /* NETBSD */ + +#if defined(ADDRESS_SANITIZER) && (defined(UNIX_LIKE) \ + || defined(NEED_FIND_LIMIT) || defined(MPROTECT_VDB)) \ + && !defined(CUSTOM_ASAN_DEF_OPTIONS) + /* To tell ASan to allow GC to use its own SIGBUS/SEGV handlers. */ + /* The function is exported just to be visible to ASan library. */ + GC_API const char *__asan_default_options(void) + { + return "allow_user_segv_handler=1"; + } +#endif + +#ifdef OPENBSD + static struct sigaction old_segv_act; + STATIC JMP_BUF GC_jmp_buf_openbsd; + +# ifdef THREADS +# include + EXTERN_C_BEGIN + extern sigset_t __syscall(quad_t, ...); + EXTERN_C_END +# endif + + /* Don't use GC_find_limit() because siglongjmp() outside of the */ + /* signal handler by-passes our userland pthreads lib, leaving */ + /* SIGSEGV and SIGPROF masked. Instead, use this custom one that */ + /* works-around the issues. */ + + STATIC void GC_fault_handler_openbsd(int sig GC_ATTR_UNUSED) + { + LONGJMP(GC_jmp_buf_openbsd, 1); + } + + /* Return the first non-addressable location > p or bound. */ + /* Requires the allocation lock. */ + STATIC ptr_t GC_find_limit_openbsd(ptr_t p, ptr_t bound) + { + static volatile ptr_t result; + /* Safer if static, since otherwise it may not be */ + /* preserved across the longjmp. Can safely be */ + /* static since it's only called with the */ + /* allocation lock held. */ + + struct sigaction act; + word pgsz = (word)sysconf(_SC_PAGESIZE); + + GC_ASSERT((word)bound >= pgsz); + GC_ASSERT(I_HOLD_LOCK()); + + act.sa_handler = GC_fault_handler_openbsd; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_NODEFER | SA_RESTART; + /* act.sa_restorer is deprecated and should not be initialized. */ + sigaction(SIGSEGV, &act, &old_segv_act); + + if (SETJMP(GC_jmp_buf_openbsd) == 0) { + result = (ptr_t)((word)p & ~(pgsz-1)); + for (;;) { + if ((word)result >= (word)bound - pgsz) { + result = bound; + break; + } + result += pgsz; /* no overflow expected */ + GC_noop1((word)(*result)); + } + } + +# ifdef THREADS + /* Due to the siglongjump we need to manually unmask SIGPROF. */ + __syscall(SYS_sigprocmask, SIG_UNBLOCK, sigmask(SIGPROF)); +# endif + + sigaction(SIGSEGV, &old_segv_act, 0); + return(result); + } + + /* Return first addressable location > p or bound. */ + /* Requires the allocation lock. */ + STATIC ptr_t GC_skip_hole_openbsd(ptr_t p, ptr_t bound) + { + static volatile ptr_t result; + static volatile int firstpass; + + struct sigaction act; + word pgsz = (word)sysconf(_SC_PAGESIZE); + + GC_ASSERT((word)bound >= pgsz); + GC_ASSERT(I_HOLD_LOCK()); + + act.sa_handler = GC_fault_handler_openbsd; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_NODEFER | SA_RESTART; + /* act.sa_restorer is deprecated and should not be initialized. */ + sigaction(SIGSEGV, &act, &old_segv_act); + + firstpass = 1; + result = (ptr_t)((word)p & ~(pgsz-1)); + if (SETJMP(GC_jmp_buf_openbsd) != 0 || firstpass) { + firstpass = 0; + if ((word)result >= (word)bound - pgsz) { + result = bound; + } else { + result += pgsz; /* no overflow expected */ + GC_noop1((word)(*result)); + } + } + + sigaction(SIGSEGV, &old_segv_act, 0); + return(result); + } +#endif /* OPENBSD */ + +# ifdef OS2 + +# include + +# if !defined(__IBMC__) && !defined(__WATCOMC__) /* e.g. EMX */ + +struct exe_hdr { + unsigned short magic_number; + unsigned short padding[29]; + long new_exe_offset; +}; + +#define E_MAGIC(x) (x).magic_number +#define EMAGIC 0x5A4D +#define E_LFANEW(x) (x).new_exe_offset + +struct e32_exe { + unsigned char magic_number[2]; + unsigned char byte_order; + unsigned char word_order; + unsigned long exe_format_level; + unsigned short cpu; + unsigned short os; + unsigned long padding1[13]; + unsigned long object_table_offset; + unsigned long object_count; + unsigned long padding2[31]; +}; + +#define E32_MAGIC1(x) (x).magic_number[0] +#define E32MAGIC1 'L' +#define E32_MAGIC2(x) (x).magic_number[1] +#define E32MAGIC2 'X' +#define E32_BORDER(x) (x).byte_order +#define E32LEBO 0 +#define E32_WORDER(x) (x).word_order +#define E32LEWO 0 +#define E32_CPU(x) (x).cpu +#define E32CPU286 1 +#define E32_OBJTAB(x) (x).object_table_offset +#define E32_OBJCNT(x) (x).object_count + +struct o32_obj { + unsigned long size; + unsigned long base; + unsigned long flags; + unsigned long pagemap; + unsigned long mapsize; + unsigned long reserved; +}; + +#define O32_FLAGS(x) (x).flags +#define OBJREAD 0x0001L +#define OBJWRITE 0x0002L +#define OBJINVALID 0x0080L +#define O32_SIZE(x) (x).size +#define O32_BASE(x) (x).base + +# else /* IBM's compiler */ + +/* A kludge to get around what appears to be a header file bug */ +# ifndef WORD +# define WORD unsigned short +# endif +# ifndef DWORD +# define DWORD unsigned long +# endif + +# define EXE386 1 +# include +# include + +# endif /* __IBMC__ */ + +# define INCL_DOSEXCEPTIONS +# define INCL_DOSPROCESS +# define INCL_DOSERRORS +# define INCL_DOSMODULEMGR +# define INCL_DOSMEMMGR +# include + +# endif /* OS/2 */ + +/* Find the page size */ +GC_INNER size_t GC_page_size = 0; + +#if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32) +# ifndef VER_PLATFORM_WIN32_CE +# define VER_PLATFORM_WIN32_CE 3 +# endif + +# if defined(MSWINCE) && defined(THREADS) + GC_INNER GC_bool GC_dont_query_stack_min = FALSE; +# endif + + GC_INNER SYSTEM_INFO GC_sysinfo; + + GC_INNER void GC_setpagesize(void) + { + GetSystemInfo(&GC_sysinfo); +# if defined(CYGWIN32) && defined(USE_MUNMAP) + /* Allocations made with mmap() are aligned to the allocation */ + /* granularity, which (at least on 64-bit Windows OS) is not the */ + /* same as the page size. Probably a separate variable could */ + /* be added to distinguish the allocation granularity from the */ + /* actual page size, but in practice there is no good reason to */ + /* make allocations smaller than dwAllocationGranularity, so we */ + /* just use it instead of the actual page size here (as Cygwin */ + /* itself does in many cases). */ + GC_page_size = (size_t)GC_sysinfo.dwAllocationGranularity; + GC_ASSERT(GC_page_size >= (size_t)GC_sysinfo.dwPageSize); +# else + GC_page_size = (size_t)GC_sysinfo.dwPageSize; +# endif +# if defined(MSWINCE) && !defined(_WIN32_WCE_EMULATION) + { + OSVERSIONINFO verInfo; + /* Check the current WinCE version. */ + verInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + if (!GetVersionEx(&verInfo)) + ABORT("GetVersionEx failed"); + if (verInfo.dwPlatformId == VER_PLATFORM_WIN32_CE && + verInfo.dwMajorVersion < 6) { + /* Only the first 32 MB of address space belongs to the */ + /* current process (unless WinCE 6.0+ or emulation). */ + GC_sysinfo.lpMaximumApplicationAddress = (LPVOID)((word)32 << 20); +# ifdef THREADS + /* On some old WinCE versions, it's observed that */ + /* VirtualQuery calls don't work properly when used to */ + /* get thread current stack committed minimum. */ + if (verInfo.dwMajorVersion < 5) + GC_dont_query_stack_min = TRUE; +# endif + } + } +# endif + } + +# ifndef CYGWIN32 +# define is_writable(prot) ((prot) == PAGE_READWRITE \ + || (prot) == PAGE_WRITECOPY \ + || (prot) == PAGE_EXECUTE_READWRITE \ + || (prot) == PAGE_EXECUTE_WRITECOPY) + /* Return the number of bytes that are writable starting at p. */ + /* The pointer p is assumed to be page aligned. */ + /* If base is not 0, *base becomes the beginning of the */ + /* allocation region containing p. */ + STATIC word GC_get_writable_length(ptr_t p, ptr_t *base) + { + MEMORY_BASIC_INFORMATION buf; + word result; + word protect; + + result = VirtualQuery(p, &buf, sizeof(buf)); + if (result != sizeof(buf)) ABORT("Weird VirtualQuery result"); + if (base != 0) *base = (ptr_t)(buf.AllocationBase); + protect = (buf.Protect & ~(PAGE_GUARD | PAGE_NOCACHE)); + if (!is_writable(protect)) { + return(0); + } + if (buf.State != MEM_COMMIT) return(0); + return(buf.RegionSize); + } + + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb) + { + ptr_t trunc_sp; + word size; + + /* Set page size if it is not ready (so client can use this */ + /* function even before GC is initialized). */ + if (!GC_page_size) GC_setpagesize(); + + trunc_sp = (ptr_t)((word)GC_approx_sp() & ~(GC_page_size - 1)); + /* FIXME: This won't work if called from a deeply recursive */ + /* client code (and the committed stack space has grown). */ + size = GC_get_writable_length(trunc_sp, 0); + GC_ASSERT(size != 0); + sb -> mem_base = trunc_sp + size; + return GC_SUCCESS; + } +# else /* CYGWIN32 */ + /* An alternate version for Cygwin (adapted from Dave Korn's */ + /* gcc version of boehm-gc). */ + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb) + { +# ifdef X86_64 + sb -> mem_base = ((NT_TIB*)NtCurrentTeb())->StackBase; +# else + void * _tlsbase; + + __asm__ ("movl %%fs:4, %0" + : "=r" (_tlsbase)); + sb -> mem_base = _tlsbase; +# endif + return GC_SUCCESS; + } +# endif /* CYGWIN32 */ +# define HAVE_GET_STACK_BASE + +#else /* !MSWIN32 */ + GC_INNER void GC_setpagesize(void) + { +# if defined(MPROTECT_VDB) || defined(PROC_VDB) || defined(USE_MMAP) + GC_page_size = (size_t)GETPAGESIZE(); +# if !defined(CPPCHECK) + if (0 == GC_page_size) + ABORT("getpagesize failed"); +# endif +# else + /* It's acceptable to fake it. */ + GC_page_size = HBLKSIZE; +# endif + } +#endif /* !MSWIN32 */ + +#ifdef HAIKU +# include + + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb) + { + thread_info th; + get_thread_info(find_thread(NULL),&th); + sb->mem_base = th.stack_end; + return GC_SUCCESS; + } +# define HAVE_GET_STACK_BASE +#endif /* HAIKU */ + +#ifdef OS2 + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb) + { + PTIB ptib; /* thread information block */ + PPIB ppib; + if (DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR) { + WARN("DosGetInfoBlocks failed\n", 0); + return GC_UNIMPLEMENTED; + } + sb->mem_base = ptib->tib_pstacklimit; + return GC_SUCCESS; + } +# define HAVE_GET_STACK_BASE +#endif /* OS2 */ + +# ifdef AMIGA +# define GC_AMIGA_SB +# include "extra/AmigaOS.c" +# undef GC_AMIGA_SB +# define GET_MAIN_STACKBASE_SPECIAL +# endif /* AMIGA */ + +# if defined(NEED_FIND_LIMIT) || defined(UNIX_LIKE) + + typedef void (*GC_fault_handler_t)(int); + +# if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1) \ + || defined(HAIKU) || defined(HURD) || defined(FREEBSD) \ + || defined(NETBSD) + static struct sigaction old_segv_act; +# if defined(_sigargs) /* !Irix6.x */ \ + || defined(HURD) || defined(NETBSD) || defined(FREEBSD) + static struct sigaction old_bus_act; +# endif +# else + static GC_fault_handler_t old_segv_handler; +# ifdef HAVE_SIGBUS + static GC_fault_handler_t old_bus_handler; +# endif +# endif + + GC_INNER void GC_set_and_save_fault_handler(GC_fault_handler_t h) + { +# if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1) \ + || defined(HAIKU) || defined(HURD) || defined(FREEBSD) \ + || defined(NETBSD) + struct sigaction act; + + act.sa_handler = h; +# ifdef SIGACTION_FLAGS_NODEFER_HACK + /* Was necessary for Solaris 2.3 and very temporary */ + /* NetBSD bugs. */ + act.sa_flags = SA_RESTART | SA_NODEFER; +# else + act.sa_flags = SA_RESTART; +# endif + + (void) sigemptyset(&act.sa_mask); + /* act.sa_restorer is deprecated and should not be initialized. */ +# ifdef GC_IRIX_THREADS + /* Older versions have a bug related to retrieving and */ + /* and setting a handler at the same time. */ + (void) sigaction(SIGSEGV, 0, &old_segv_act); + (void) sigaction(SIGSEGV, &act, 0); +# else + (void) sigaction(SIGSEGV, &act, &old_segv_act); +# if defined(IRIX5) && defined(_sigargs) /* Irix 5.x, not 6.x */ \ + || defined(HURD) || defined(NETBSD) || defined(FREEBSD) + /* Under Irix 5.x or HP/UX, we may get SIGBUS. */ + /* Pthreads doesn't exist under Irix 5.x, so we */ + /* don't have to worry in the threads case. */ + (void) sigaction(SIGBUS, &act, &old_bus_act); +# endif +# endif /* !GC_IRIX_THREADS */ +# else + old_segv_handler = signal(SIGSEGV, h); +# ifdef HAVE_SIGBUS + old_bus_handler = signal(SIGBUS, h); +# endif +# endif +# if defined(CPPCHECK) && defined(ADDRESS_SANITIZER) + GC_noop1((word)&__asan_default_options); +# endif + } +# endif /* NEED_FIND_LIMIT || UNIX_LIKE */ + +# if defined(NEED_FIND_LIMIT) \ + || (defined(USE_PROC_FOR_LIBRARIES) && defined(THREADS)) + /* Some tools to implement HEURISTIC2 */ +# define MIN_PAGE_SIZE 256 /* Smallest conceivable page size, bytes */ + + GC_INNER JMP_BUF GC_jmp_buf; + + STATIC void GC_fault_handler(int sig GC_ATTR_UNUSED) + { + LONGJMP(GC_jmp_buf, 1); + } + + GC_INNER void GC_setup_temporary_fault_handler(void) + { + /* Handler is process-wide, so this should only happen in */ + /* one thread at a time. */ + GC_ASSERT(I_HOLD_LOCK()); + GC_set_and_save_fault_handler(GC_fault_handler); + } + + GC_INNER void GC_reset_fault_handler(void) + { +# if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1) \ + || defined(HAIKU) || defined(HURD) || defined(FREEBSD) \ + || defined(NETBSD) + (void) sigaction(SIGSEGV, &old_segv_act, 0); +# if defined(IRIX5) && defined(_sigargs) /* Irix 5.x, not 6.x */ \ + || defined(HURD) || defined(NETBSD) + (void) sigaction(SIGBUS, &old_bus_act, 0); +# endif +# else + (void) signal(SIGSEGV, old_segv_handler); +# ifdef HAVE_SIGBUS + (void) signal(SIGBUS, old_bus_handler); +# endif +# endif + } + + /* Return the first non-addressable location > p (up) or */ + /* the smallest location q s.t. [q,p) is addressable (!up). */ + /* We assume that p (up) or p-1 (!up) is addressable. */ + /* Requires allocation lock. */ + STATIC ptr_t GC_find_limit_with_bound(ptr_t p, GC_bool up, ptr_t bound) + { + static volatile ptr_t result; + /* Safer if static, since otherwise it may not be */ + /* preserved across the longjmp. Can safely be */ + /* static since it's only called with the */ + /* allocation lock held. */ + + GC_ASSERT(up ? (word)bound >= MIN_PAGE_SIZE + : (word)bound <= ~(word)MIN_PAGE_SIZE); + GC_ASSERT(I_HOLD_LOCK()); + GC_setup_temporary_fault_handler(); + if (SETJMP(GC_jmp_buf) == 0) { + result = (ptr_t)(((word)(p)) + & ~(MIN_PAGE_SIZE-1)); + for (;;) { + if (up) { + if ((word)result >= (word)bound - MIN_PAGE_SIZE) { + result = bound; + break; + } + result += MIN_PAGE_SIZE; /* no overflow expected */ + } else { + if ((word)result <= (word)bound + MIN_PAGE_SIZE) { + result = bound - MIN_PAGE_SIZE; + /* This is to compensate */ + /* further result increment (we */ + /* do not modify "up" variable */ + /* since it might be clobbered */ + /* by setjmp otherwise). */ + break; + } + result -= MIN_PAGE_SIZE; /* no underflow expected */ + } + GC_noop1((word)(*result)); + } + } + GC_reset_fault_handler(); + if (!up) { + result += MIN_PAGE_SIZE; + } + return(result); + } + + ptr_t GC_find_limit(ptr_t p, GC_bool up) + { + return GC_find_limit_with_bound(p, up, up ? (ptr_t)(word)(-1) : 0); + } +# endif /* NEED_FIND_LIMIT || USE_PROC_FOR_LIBRARIES */ + +#ifdef HPUX_STACKBOTTOM + +#include +#include + + GC_INNER ptr_t GC_get_register_stack_base(void) + { + struct pst_vm_status vm_status; + + int i = 0; + while (pstat_getprocvm(&vm_status, sizeof(vm_status), 0, i++) == 1) { + if (vm_status.pst_type == PS_RSESTACK) { + return (ptr_t) vm_status.pst_vaddr; + } + } + + /* old way to get the register stackbottom */ + return (ptr_t)(((word)GC_stackbottom - BACKING_STORE_DISPLACEMENT - 1) + & ~(BACKING_STORE_ALIGNMENT - 1)); + } + +#endif /* HPUX_STACK_BOTTOM */ + +#ifdef LINUX_STACKBOTTOM + +# include +# include + +# define STAT_SKIP 27 /* Number of fields preceding startstack */ + /* field in /proc/self/stat */ + +# ifdef USE_LIBC_PRIVATES + EXTERN_C_BEGIN +# pragma weak __libc_stack_end + extern ptr_t __libc_stack_end; +# ifdef IA64 +# pragma weak __libc_ia64_register_backing_store_base + extern ptr_t __libc_ia64_register_backing_store_base; +# endif + EXTERN_C_END +# endif + +# ifdef IA64 + GC_INNER ptr_t GC_get_register_stack_base(void) + { + ptr_t result; + +# ifdef USE_LIBC_PRIVATES + if (0 != &__libc_ia64_register_backing_store_base + && 0 != __libc_ia64_register_backing_store_base) { + /* Glibc 2.2.4 has a bug such that for dynamically linked */ + /* executables __libc_ia64_register_backing_store_base is */ + /* defined but uninitialized during constructor calls. */ + /* Hence we check for both nonzero address and value. */ + return __libc_ia64_register_backing_store_base; + } +# endif + result = backing_store_base_from_proc(); + if (0 == result) { + result = GC_find_limit(GC_save_regs_in_stack(), FALSE); + /* Now seems to work better than constant displacement */ + /* heuristic used in 6.X versions. The latter seems to */ + /* fail for 2.6 kernels. */ + } + return result; + } +# endif /* IA64 */ + + STATIC ptr_t GC_linux_main_stack_base(void) + { + /* We read the stack base value from /proc/self/stat. We do this */ + /* using direct I/O system calls in order to avoid calling malloc */ + /* in case REDIRECT_MALLOC is defined. */ +# ifndef STAT_READ + /* Also defined in pthread_support.c. */ +# define STAT_BUF_SIZE 4096 +# define STAT_READ read +# endif + /* Should probably call the real read, if read is wrapped. */ + char stat_buf[STAT_BUF_SIZE]; + int f; + word result; + int i, buf_offset = 0, len; + + /* First try the easy way. This should work for glibc 2.2 */ + /* This fails in a prelinked ("prelink" command) executable */ + /* since the correct value of __libc_stack_end never */ + /* becomes visible to us. The second test works around */ + /* this. */ +# ifdef USE_LIBC_PRIVATES + if (0 != &__libc_stack_end && 0 != __libc_stack_end ) { +# if defined(IA64) + /* Some versions of glibc set the address 16 bytes too */ + /* low while the initialization code is running. */ + if (((word)__libc_stack_end & 0xfff) + 0x10 < 0x1000) { + return __libc_stack_end + 0x10; + } /* Otherwise it's not safe to add 16 bytes and we fall */ + /* back to using /proc. */ +# elif defined(SPARC) + /* Older versions of glibc for 64-bit SPARC do not set this */ + /* variable correctly, it gets set to either zero or one. */ + if (__libc_stack_end != (ptr_t) (unsigned long)0x1) + return __libc_stack_end; +# else + return __libc_stack_end; +# endif + } +# endif + f = open("/proc/self/stat", O_RDONLY); + if (f < 0) + ABORT("Couldn't read /proc/self/stat"); + len = STAT_READ(f, stat_buf, STAT_BUF_SIZE); + close(f); + + /* Skip the required number of fields. This number is hopefully */ + /* constant across all Linux implementations. */ + for (i = 0; i < STAT_SKIP; ++i) { + while (buf_offset < len && isspace(stat_buf[buf_offset++])) { + /* empty */ + } + while (buf_offset < len && !isspace(stat_buf[buf_offset++])) { + /* empty */ + } + } + /* Skip spaces. */ + while (buf_offset < len && isspace(stat_buf[buf_offset])) { + buf_offset++; + } + /* Find the end of the number and cut the buffer there. */ + for (i = 0; buf_offset + i < len; i++) { + if (!isdigit(stat_buf[buf_offset + i])) break; + } + if (buf_offset + i >= len) ABORT("Could not parse /proc/self/stat"); + stat_buf[buf_offset + i] = '\0'; + + result = (word)STRTOULL(&stat_buf[buf_offset], NULL, 10); + if (result < 0x100000 || (result & (sizeof(word) - 1)) != 0) + ABORT("Absurd stack bottom value"); + return (ptr_t)result; + } +#endif /* LINUX_STACKBOTTOM */ + +#ifdef FREEBSD_STACKBOTTOM + /* This uses an undocumented sysctl call, but at least one expert */ + /* believes it will stay. */ + +# include +# include +# include + + STATIC ptr_t GC_freebsd_main_stack_base(void) + { + int nm[2] = {CTL_KERN, KERN_USRSTACK}; + ptr_t base; + size_t len = sizeof(ptr_t); + int r = sysctl(nm, 2, &base, &len, NULL, 0); + if (r) ABORT("Error getting main stack base"); + return base; + } +#endif /* FREEBSD_STACKBOTTOM */ + +#if defined(ECOS) || defined(NOSYS) + ptr_t GC_get_main_stack_base(void) + { + return STACKBOTTOM; + } +# define GET_MAIN_STACKBASE_SPECIAL +#elif defined(SYMBIAN) + EXTERN_C_BEGIN + extern int GC_get_main_symbian_stack_base(void); + EXTERN_C_END + + ptr_t GC_get_main_stack_base(void) + { + return (ptr_t)GC_get_main_symbian_stack_base(); + } +# define GET_MAIN_STACKBASE_SPECIAL +#elif !defined(AMIGA) && !defined(HAIKU) && !defined(OS2) \ + && !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) \ + && !defined(GC_OPENBSD_THREADS) \ + && (!defined(GC_SOLARIS_THREADS) || defined(_STRICT_STDC)) + +# if (defined(HAVE_PTHREAD_ATTR_GET_NP) || defined(HAVE_PTHREAD_GETATTR_NP)) \ + && (defined(THREADS) || defined(USE_GET_STACKBASE_FOR_MAIN)) +# include +# ifdef HAVE_PTHREAD_NP_H +# include /* for pthread_attr_get_np() */ +# endif +# elif defined(DARWIN) && !defined(NO_PTHREAD_GET_STACKADDR_NP) + /* We could use pthread_get_stackaddr_np even in case of a */ + /* single-threaded gclib (there is no -lpthread on Darwin). */ +# include +# undef STACKBOTTOM +# define STACKBOTTOM (ptr_t)pthread_get_stackaddr_np(pthread_self()) +# endif + + ptr_t GC_get_main_stack_base(void) + { + ptr_t result; +# if (defined(HAVE_PTHREAD_ATTR_GET_NP) \ + || defined(HAVE_PTHREAD_GETATTR_NP)) \ + && (defined(USE_GET_STACKBASE_FOR_MAIN) \ + || (defined(THREADS) && !defined(REDIRECT_MALLOC))) + pthread_attr_t attr; + void *stackaddr; + size_t size; + +# ifdef HAVE_PTHREAD_ATTR_GET_NP + if (pthread_attr_init(&attr) == 0 + && (pthread_attr_get_np(pthread_self(), &attr) == 0 + ? TRUE : (pthread_attr_destroy(&attr), FALSE))) +# else /* HAVE_PTHREAD_GETATTR_NP */ + if (pthread_getattr_np(pthread_self(), &attr) == 0) +# endif + { + if (pthread_attr_getstack(&attr, &stackaddr, &size) == 0 + && stackaddr != NULL) { + (void)pthread_attr_destroy(&attr); +# ifdef STACK_GROWS_DOWN + stackaddr = (char *)stackaddr + size; +# endif + return (ptr_t)stackaddr; + } + (void)pthread_attr_destroy(&attr); + } + WARN("pthread_getattr_np or pthread_attr_getstack failed" + " for main thread\n", 0); +# endif +# ifdef STACKBOTTOM + result = STACKBOTTOM; +# else +# define STACKBOTTOM_ALIGNMENT_M1 ((word)STACK_GRAN - 1) +# ifdef HEURISTIC1 +# ifdef STACK_GROWS_DOWN + result = (ptr_t)(((word)GC_approx_sp() + STACKBOTTOM_ALIGNMENT_M1) + & ~STACKBOTTOM_ALIGNMENT_M1); +# else + result = (ptr_t)((word)GC_approx_sp() & ~STACKBOTTOM_ALIGNMENT_M1); +# endif +# elif defined(LINUX_STACKBOTTOM) + result = GC_linux_main_stack_base(); +# elif defined(FREEBSD_STACKBOTTOM) + result = GC_freebsd_main_stack_base(); +# elif defined(HEURISTIC2) + { + ptr_t sp = GC_approx_sp(); +# ifdef STACK_GROWS_DOWN + result = GC_find_limit(sp, TRUE); +# if defined(HEURISTIC2_LIMIT) && !defined(CPPCHECK) + if ((word)result > (word)HEURISTIC2_LIMIT + && (word)sp < (word)HEURISTIC2_LIMIT) { + result = HEURISTIC2_LIMIT; + } +# endif +# else + result = GC_find_limit(sp, FALSE); +# if defined(HEURISTIC2_LIMIT) && !defined(CPPCHECK) + if ((word)result < (word)HEURISTIC2_LIMIT + && (word)sp > (word)HEURISTIC2_LIMIT) { + result = HEURISTIC2_LIMIT; + } +# endif +# endif + } +# elif defined(STACK_NOT_SCANNED) || defined(CPPCHECK) + result = NULL; +# else +# error None of HEURISTIC* and *STACKBOTTOM defined! +# endif +# if defined(STACK_GROWS_DOWN) && !defined(CPPCHECK) + if (result == 0) + result = (ptr_t)(signed_word)(-sizeof(ptr_t)); +# endif +# endif + GC_ASSERT((word)GC_approx_sp() HOTTER_THAN (word)result); + return(result); + } +# define GET_MAIN_STACKBASE_SPECIAL +#endif /* !AMIGA, !HAIKU, !OPENBSD, !OS2, !Windows */ + +#if (defined(HAVE_PTHREAD_ATTR_GET_NP) || defined(HAVE_PTHREAD_GETATTR_NP)) \ + && defined(THREADS) && !defined(HAVE_GET_STACK_BASE) +# include +# ifdef HAVE_PTHREAD_NP_H +# include +# endif + + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *b) + { + pthread_attr_t attr; + size_t size; +# ifdef IA64 + DCL_LOCK_STATE; +# endif + +# ifdef HAVE_PTHREAD_ATTR_GET_NP + if (pthread_attr_init(&attr) != 0) + ABORT("pthread_attr_init failed"); + if (pthread_attr_get_np(pthread_self(), &attr) != 0) { + WARN("pthread_attr_get_np failed\n", 0); + (void)pthread_attr_destroy(&attr); + return GC_UNIMPLEMENTED; + } +# else /* HAVE_PTHREAD_GETATTR_NP */ + if (pthread_getattr_np(pthread_self(), &attr) != 0) { + WARN("pthread_getattr_np failed\n", 0); + return GC_UNIMPLEMENTED; + } +# endif + if (pthread_attr_getstack(&attr, &(b -> mem_base), &size) != 0) { + ABORT("pthread_attr_getstack failed"); + } + (void)pthread_attr_destroy(&attr); +# ifdef STACK_GROWS_DOWN + b -> mem_base = (char *)(b -> mem_base) + size; +# endif +# ifdef IA64 + /* We could try backing_store_base_from_proc, but that's safe */ + /* only if no mappings are being asynchronously created. */ + /* Subtracting the size from the stack base doesn't work for at */ + /* least the main thread. */ + LOCK(); + { + IF_CANCEL(int cancel_state;) + ptr_t bsp; + ptr_t next_stack; + + DISABLE_CANCEL(cancel_state); + bsp = GC_save_regs_in_stack(); + next_stack = GC_greatest_stack_base_below(bsp); + if (0 == next_stack) { + b -> reg_base = GC_find_limit(bsp, FALSE); + } else { + /* Avoid walking backwards into preceding memory stack and */ + /* growing it. */ + b -> reg_base = GC_find_limit_with_bound(bsp, FALSE, next_stack); + } + RESTORE_CANCEL(cancel_state); + } + UNLOCK(); +# endif + return GC_SUCCESS; + } +# define HAVE_GET_STACK_BASE +#endif /* THREADS && (HAVE_PTHREAD_ATTR_GET_NP || HAVE_PTHREAD_GETATTR_NP) */ + +#if defined(GC_DARWIN_THREADS) && !defined(NO_PTHREAD_GET_STACKADDR_NP) +# include + + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *b) + { + /* pthread_get_stackaddr_np() should return stack bottom (highest */ + /* stack address plus 1). */ + b->mem_base = pthread_get_stackaddr_np(pthread_self()); + GC_ASSERT((word)GC_approx_sp() HOTTER_THAN (word)b->mem_base); + return GC_SUCCESS; + } +# define HAVE_GET_STACK_BASE +#endif /* GC_DARWIN_THREADS */ + +#ifdef GC_OPENBSD_THREADS +# include +# include +# include + + /* Find the stack using pthread_stackseg_np(). */ + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb) + { + stack_t stack; + if (pthread_stackseg_np(pthread_self(), &stack)) + ABORT("pthread_stackseg_np(self) failed"); + sb->mem_base = stack.ss_sp; + return GC_SUCCESS; + } +# define HAVE_GET_STACK_BASE +#endif /* GC_OPENBSD_THREADS */ + +#if defined(GC_SOLARIS_THREADS) && !defined(_STRICT_STDC) + +# include +# include +# include + + /* These variables are used to cache ss_sp value for the primordial */ + /* thread (it's better not to call thr_stksegment() twice for this */ + /* thread - see JDK bug #4352906). */ + static pthread_t stackbase_main_self = 0; + /* 0 means stackbase_main_ss_sp value is unset. */ + static void *stackbase_main_ss_sp = NULL; + + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *b) + { + stack_t s; + pthread_t self = pthread_self(); + + if (self == stackbase_main_self) + { + /* If the client calls GC_get_stack_base() from the main thread */ + /* then just return the cached value. */ + b -> mem_base = stackbase_main_ss_sp; + GC_ASSERT(b -> mem_base != NULL); + return GC_SUCCESS; + } + + if (thr_stksegment(&s)) { + /* According to the manual, the only failure error code returned */ + /* is EAGAIN meaning "the information is not available due to the */ + /* thread is not yet completely initialized or it is an internal */ + /* thread" - this shouldn't happen here. */ + ABORT("thr_stksegment failed"); + } + /* s.ss_sp holds the pointer to the stack bottom. */ + GC_ASSERT((word)GC_approx_sp() HOTTER_THAN (word)s.ss_sp); + + if (!stackbase_main_self && thr_main() != 0) + { + /* Cache the stack base value for the primordial thread (this */ + /* is done during GC_init, so there is no race). */ + stackbase_main_ss_sp = s.ss_sp; + stackbase_main_self = self; + } + + b -> mem_base = s.ss_sp; + return GC_SUCCESS; + } +# define HAVE_GET_STACK_BASE +#endif /* GC_SOLARIS_THREADS */ + +#ifdef GC_RTEMS_PTHREADS + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb) + { + sb->mem_base = rtems_get_stack_bottom(); + return GC_SUCCESS; + } +# define HAVE_GET_STACK_BASE +#endif /* GC_RTEMS_PTHREADS */ + +#ifndef HAVE_GET_STACK_BASE +# ifdef NEED_FIND_LIMIT + /* Retrieve stack base. */ + /* Using the GC_find_limit version is risky. */ + /* On IA64, for example, there is no guard page between the */ + /* stack of one thread and the register backing store of the */ + /* next. Thus this is likely to identify way too large a */ + /* "stack" and thus at least result in disastrous performance. */ + /* FIXME - Implement better strategies here. */ + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *b) + { + IF_CANCEL(int cancel_state;) + DCL_LOCK_STATE; + + LOCK(); + DISABLE_CANCEL(cancel_state); /* May be unnecessary? */ +# ifdef STACK_GROWS_DOWN + b -> mem_base = GC_find_limit(GC_approx_sp(), TRUE); +# ifdef IA64 + b -> reg_base = GC_find_limit(GC_save_regs_in_stack(), FALSE); +# endif +# else + b -> mem_base = GC_find_limit(GC_approx_sp(), FALSE); +# endif + RESTORE_CANCEL(cancel_state); + UNLOCK(); + return GC_SUCCESS; + } +# else + GC_API int GC_CALL GC_get_stack_base( + struct GC_stack_base *b GC_ATTR_UNUSED) + { +# if defined(GET_MAIN_STACKBASE_SPECIAL) && !defined(THREADS) \ + && !defined(IA64) + b->mem_base = GC_get_main_stack_base(); + return GC_SUCCESS; +# else + return GC_UNIMPLEMENTED; +# endif + } +# endif /* !NEED_FIND_LIMIT */ +#endif /* !HAVE_GET_STACK_BASE */ + +#ifndef GET_MAIN_STACKBASE_SPECIAL + /* This is always called from the main thread. Default implementation. */ + ptr_t GC_get_main_stack_base(void) + { + struct GC_stack_base sb; + + if (GC_get_stack_base(&sb) != GC_SUCCESS) + ABORT("GC_get_stack_base failed"); + GC_ASSERT((word)GC_approx_sp() HOTTER_THAN (word)sb.mem_base); + return (ptr_t)sb.mem_base; + } +#endif /* !GET_MAIN_STACKBASE_SPECIAL */ + +/* Register static data segment(s) as roots. If more data segments are */ +/* added later then they need to be registered at that point (as we do */ +/* with SunOS dynamic loading), or GC_mark_roots needs to check for */ +/* them (as we do with PCR). Called with allocator lock held. */ +# ifdef OS2 + +void GC_register_data_segments(void) +{ + PTIB ptib; + PPIB ppib; + HMODULE module_handle; +# define PBUFSIZ 512 + UCHAR path[PBUFSIZ]; + FILE * myexefile; + struct exe_hdr hdrdos; /* MSDOS header. */ + struct e32_exe hdr386; /* Real header for my executable */ + struct o32_obj seg; /* Current segment */ + int nsegs; + +# if defined(CPPCHECK) + hdrdos.padding[0] = 0; /* to prevent "field unused" warnings */ + hdr386.exe_format_level = 0; + hdr386.os = 0; + hdr386.padding1[0] = 0; + hdr386.padding2[0] = 0; + seg.pagemap = 0; + seg.mapsize = 0; + seg.reserved = 0; +# endif + if (DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR) { + ABORT("DosGetInfoBlocks failed"); + } + module_handle = ppib -> pib_hmte; + if (DosQueryModuleName(module_handle, PBUFSIZ, path) != NO_ERROR) { + ABORT("DosQueryModuleName failed"); + } + myexefile = fopen(path, "rb"); + if (myexefile == 0) { + ABORT_ARG1("Failed to open executable", ": %s", path); + } + if (fread((char *)(&hdrdos), 1, sizeof(hdrdos), myexefile) + < sizeof(hdrdos)) { + ABORT_ARG1("Could not read MSDOS header", " from: %s", path); + } + if (E_MAGIC(hdrdos) != EMAGIC) { + ABORT_ARG1("Bad DOS magic number", " in file: %s", path); + } + if (fseek(myexefile, E_LFANEW(hdrdos), SEEK_SET) != 0) { + ABORT_ARG1("Bad DOS magic number", " in file: %s", path); + } + if (fread((char *)(&hdr386), 1, sizeof(hdr386), myexefile) + < sizeof(hdr386)) { + ABORT_ARG1("Could not read OS/2 header", " from: %s", path); + } + if (E32_MAGIC1(hdr386) != E32MAGIC1 || E32_MAGIC2(hdr386) != E32MAGIC2) { + ABORT_ARG1("Bad OS/2 magic number", " in file: %s", path); + } + if (E32_BORDER(hdr386) != E32LEBO || E32_WORDER(hdr386) != E32LEWO) { + ABORT_ARG1("Bad byte order in executable", " file: %s", path); + } + if (E32_CPU(hdr386) == E32CPU286) { + ABORT_ARG1("GC cannot handle 80286 executables", ": %s", path); + } + if (fseek(myexefile, E_LFANEW(hdrdos) + E32_OBJTAB(hdr386), + SEEK_SET) != 0) { + ABORT_ARG1("Seek to object table failed", " in file: %s", path); + } + for (nsegs = E32_OBJCNT(hdr386); nsegs > 0; nsegs--) { + int flags; + if (fread((char *)(&seg), 1, sizeof(seg), myexefile) < sizeof(seg)) { + ABORT_ARG1("Could not read obj table entry", " from file: %s", path); + } + flags = O32_FLAGS(seg); + if (!(flags & OBJWRITE)) continue; + if (!(flags & OBJREAD)) continue; + if (flags & OBJINVALID) { + GC_err_printf("Object with invalid pages?\n"); + continue; + } + GC_add_roots_inner((ptr_t)O32_BASE(seg), + (ptr_t)(O32_BASE(seg)+O32_SIZE(seg)), FALSE); + } + (void)fclose(myexefile); +} + +# else /* !OS2 */ + +# if defined(GWW_VDB) +# ifndef MEM_WRITE_WATCH +# define MEM_WRITE_WATCH 0x200000 +# endif +# ifndef WRITE_WATCH_FLAG_RESET +# define WRITE_WATCH_FLAG_RESET 1 +# endif + + /* Since we can't easily check whether ULONG_PTR and SIZE_T are */ + /* defined in Win32 basetsd.h, we define own ULONG_PTR. */ +# define GC_ULONG_PTR word + + typedef UINT (WINAPI * GetWriteWatch_type)( + DWORD, PVOID, GC_ULONG_PTR /* SIZE_T */, + PVOID *, GC_ULONG_PTR *, PULONG); + static GetWriteWatch_type GetWriteWatch_func; + static DWORD GetWriteWatch_alloc_flag; + +# define GC_GWW_AVAILABLE() (GetWriteWatch_func != NULL) + + static void detect_GetWriteWatch(void) + { + static GC_bool done; + HMODULE hK32; + if (done) + return; + +# if defined(MPROTECT_VDB) + { + char * str = GETENV("GC_USE_GETWRITEWATCH"); +# if defined(GC_PREFER_MPROTECT_VDB) + if (str == NULL || (*str == '0' && *(str + 1) == '\0')) { + /* GC_USE_GETWRITEWATCH is unset or set to "0". */ + done = TRUE; /* falling back to MPROTECT_VDB strategy. */ + /* This should work as if GWW_VDB is undefined. */ + return; + } +# else + if (str != NULL && *str == '0' && *(str + 1) == '\0') { + /* GC_USE_GETWRITEWATCH is set "0". */ + done = TRUE; /* falling back to MPROTECT_VDB strategy. */ + return; + } +# endif + } +# endif + +# ifdef MSWINRT_FLAVOR + { + MEMORY_BASIC_INFORMATION memInfo; + SIZE_T result = VirtualQuery(GetProcAddress, + &memInfo, sizeof(memInfo)); + if (result != sizeof(memInfo)) + ABORT("Weird VirtualQuery result"); + hK32 = (HMODULE)memInfo.AllocationBase; + } +# else + hK32 = GetModuleHandle(TEXT("kernel32.dll")); +# endif + if (hK32 != (HMODULE)0 && + (GetWriteWatch_func = (GetWriteWatch_type)GetProcAddress(hK32, + "GetWriteWatch")) != NULL) { + /* Also check whether VirtualAlloc accepts MEM_WRITE_WATCH, */ + /* as some versions of kernel32.dll have one but not the */ + /* other, making the feature completely broken. */ + void * page = VirtualAlloc(NULL, GC_page_size, + MEM_WRITE_WATCH | MEM_RESERVE, + PAGE_READWRITE); + if (page != NULL) { + PVOID pages[16]; + GC_ULONG_PTR count = 16; + DWORD page_size; + /* Check that it actually works. In spite of some */ + /* documentation it actually seems to exist on W2K. */ + /* This test may be unnecessary, but ... */ + if (GetWriteWatch_func(WRITE_WATCH_FLAG_RESET, + page, GC_page_size, + pages, + &count, + &page_size) != 0) { + /* GetWriteWatch always fails. */ + GetWriteWatch_func = NULL; + } else { + GetWriteWatch_alloc_flag = MEM_WRITE_WATCH; + } + VirtualFree(page, 0 /* dwSize */, MEM_RELEASE); + } else { + /* GetWriteWatch will be useless. */ + GetWriteWatch_func = NULL; + } + } +# ifndef SMALL_CONFIG + if (GetWriteWatch_func == NULL) { + GC_COND_LOG_PRINTF("Did not find a usable GetWriteWatch()\n"); + } else { + GC_COND_LOG_PRINTF("Using GetWriteWatch()\n"); + } +# endif + done = TRUE; + } + +# else +# define GetWriteWatch_alloc_flag 0 +# endif /* !GWW_VDB */ + +# if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32) + +# ifdef MSWIN32 + /* Unfortunately, we have to handle win32s very differently from NT, */ + /* Since VirtualQuery has very different semantics. In particular, */ + /* under win32s a VirtualQuery call on an unmapped page returns an */ + /* invalid result. Under NT, GC_register_data_segments is a no-op */ + /* and all real work is done by GC_register_dynamic_libraries. Under */ + /* win32s, we cannot find the data segments associated with dll's. */ + /* We register the main data segment here. */ + GC_INNER GC_bool GC_no_win32_dlls = FALSE; + /* This used to be set for gcc, to avoid dealing with */ + /* the structured exception handling issues. But we now have */ + /* assembly code to do that right. */ + + GC_INNER GC_bool GC_wnt = FALSE; + /* This is a Windows NT derivative, i.e. NT, W2K, XP or later. */ + + GC_INNER void GC_init_win32(void) + { +# if defined(_WIN64) || (defined(_MSC_VER) && _MSC_VER >= 1800) + /* MS Visual Studio 2013 deprecates GetVersion, but on the other */ + /* hand it cannot be used to target pre-Win2K. */ + GC_wnt = TRUE; +# else + /* Set GC_wnt. If we're running under win32s, assume that no */ + /* DLLs will be loaded. I doubt anyone still runs win32s, but... */ + DWORD v = GetVersion(); + + GC_wnt = !(v & 0x80000000); + GC_no_win32_dlls |= ((!GC_wnt) && (v & 0xff) <= 3); +# endif +# ifdef USE_MUNMAP + if (GC_no_win32_dlls) { + /* Turn off unmapping for safety (since may not work well with */ + /* GlobalAlloc). */ + GC_unmap_threshold = 0; + } +# endif + } + + /* Return the smallest address a such that VirtualQuery */ + /* returns correct results for all addresses between a and start. */ + /* Assumes VirtualQuery returns correct information for start. */ + STATIC ptr_t GC_least_described_address(ptr_t start) + { + MEMORY_BASIC_INFORMATION buf; + LPVOID limit; + ptr_t p; + + limit = GC_sysinfo.lpMinimumApplicationAddress; + p = (ptr_t)((word)start & ~(GC_page_size - 1)); + for (;;) { + size_t result; + LPVOID q = (LPVOID)(p - GC_page_size); + + if ((word)q > (word)p /* underflow */ || (word)q < (word)limit) break; + result = VirtualQuery(q, &buf, sizeof(buf)); + if (result != sizeof(buf) || buf.AllocationBase == 0) break; + p = (ptr_t)(buf.AllocationBase); + } + return p; + } +# endif /* MSWIN32 */ + +# ifndef REDIRECT_MALLOC + /* We maintain a linked list of AllocationBase values that we know */ + /* correspond to malloc heap sections. Currently this is only called */ + /* during a GC. But there is some hope that for long running */ + /* programs we will eventually see most heap sections. */ + + /* In the long run, it would be more reliable to occasionally walk */ + /* the malloc heap with HeapWalk on the default heap. But that */ + /* apparently works only for NT-based Windows. */ + + STATIC size_t GC_max_root_size = 100000; /* Appr. largest root size. */ + +# ifdef USE_WINALLOC + /* In the long run, a better data structure would also be nice ... */ + STATIC struct GC_malloc_heap_list { + void * allocation_base; + struct GC_malloc_heap_list *next; + } *GC_malloc_heap_l = 0; + + /* Is p the base of one of the malloc heap sections we already know */ + /* about? */ + STATIC GC_bool GC_is_malloc_heap_base(void *p) + { + struct GC_malloc_heap_list *q = GC_malloc_heap_l; + + while (0 != q) { + if (q -> allocation_base == p) return TRUE; + q = q -> next; + } + return FALSE; + } + + STATIC void *GC_get_allocation_base(void *p) + { + MEMORY_BASIC_INFORMATION buf; + size_t result = VirtualQuery(p, &buf, sizeof(buf)); + if (result != sizeof(buf)) { + ABORT("Weird VirtualQuery result"); + } + return buf.AllocationBase; + } + + GC_INNER void GC_add_current_malloc_heap(void) + { + struct GC_malloc_heap_list *new_l = + malloc(sizeof(struct GC_malloc_heap_list)); + void * candidate = GC_get_allocation_base(new_l); + + if (new_l == 0) return; + if (GC_is_malloc_heap_base(candidate)) { + /* Try a little harder to find malloc heap. */ + size_t req_size = 10000; + do { + void *p = malloc(req_size); + if (0 == p) { + free(new_l); + return; + } + candidate = GC_get_allocation_base(p); + free(p); + req_size *= 2; + } while (GC_is_malloc_heap_base(candidate) + && req_size < GC_max_root_size/10 && req_size < 500000); + if (GC_is_malloc_heap_base(candidate)) { + free(new_l); + return; + } + } + GC_COND_LOG_PRINTF("Found new system malloc AllocationBase at %p\n", + candidate); + new_l -> allocation_base = candidate; + new_l -> next = GC_malloc_heap_l; + GC_malloc_heap_l = new_l; + } +# endif /* USE_WINALLOC */ + +# endif /* !REDIRECT_MALLOC */ + + STATIC word GC_n_heap_bases = 0; /* See GC_heap_bases. */ + + /* Is p the start of either the malloc heap, or of one of our */ + /* heap sections? */ + GC_INNER GC_bool GC_is_heap_base(void *p) + { + unsigned i; +# ifndef REDIRECT_MALLOC + if (GC_root_size > GC_max_root_size) GC_max_root_size = GC_root_size; +# ifdef USE_WINALLOC + if (GC_is_malloc_heap_base(p)) return TRUE; +# endif +# endif + for (i = 0; i < GC_n_heap_bases; i++) { + if (GC_heap_bases[i] == p) return TRUE; + } + return FALSE; + } + +#ifdef MSWIN32 + STATIC void GC_register_root_section(ptr_t static_root) + { + MEMORY_BASIC_INFORMATION buf; + LPVOID p; + char * base; + char * limit; + + if (!GC_no_win32_dlls) return; + p = base = limit = GC_least_described_address(static_root); + while ((word)p < (word)GC_sysinfo.lpMaximumApplicationAddress) { + size_t result = VirtualQuery(p, &buf, sizeof(buf)); + char * new_limit; + DWORD protect; + + if (result != sizeof(buf) || buf.AllocationBase == 0 + || GC_is_heap_base(buf.AllocationBase)) break; + new_limit = (char *)p + buf.RegionSize; + protect = buf.Protect; + if (buf.State == MEM_COMMIT + && is_writable(protect)) { + if ((char *)p == limit) { + limit = new_limit; + } else { + if (base != limit) GC_add_roots_inner(base, limit, FALSE); + base = (char *)p; + limit = new_limit; + } + } + if ((word)p > (word)new_limit /* overflow */) break; + p = (LPVOID)new_limit; + } + if (base != limit) GC_add_roots_inner(base, limit, FALSE); + } +#endif /* MSWIN32 */ + + void GC_register_data_segments(void) + { +# ifdef MSWIN32 + GC_register_root_section((ptr_t)&GC_pages_executable); + /* any other GC global variable would fit too. */ +# endif + } + +# else /* !OS2 && !Windows */ + +# if (defined(SVR4) || defined(AIX) || defined(DGUX) \ + || (defined(LINUX) && defined(SPARC))) && !defined(PCR) + ptr_t GC_SysVGetDataStart(size_t max_page_size, ptr_t etext_addr) + { + word text_end = ((word)(etext_addr) + sizeof(word) - 1) + & ~(word)(sizeof(word) - 1); + /* etext rounded to word boundary */ + word next_page = ((text_end + (word)max_page_size - 1) + & ~((word)max_page_size - 1)); + word page_offset = (text_end & ((word)max_page_size - 1)); + char * volatile result = (char *)(next_page + page_offset); + /* Note that this isn't equivalent to just adding */ + /* max_page_size to &etext if &etext is at a page boundary */ + + GC_setup_temporary_fault_handler(); + if (SETJMP(GC_jmp_buf) == 0) { + /* Try writing to the address. */ +# ifdef AO_HAVE_fetch_and_add + volatile AO_t zero = 0; + (void)AO_fetch_and_add((volatile AO_t *)result, zero); +# else + /* Fallback to non-atomic fetch-and-store. */ + char v = *result; +# if defined(CPPCHECK) + GC_noop1((word)&v); +# endif + *result = v; +# endif + GC_reset_fault_handler(); + } else { + GC_reset_fault_handler(); + /* We got here via a longjmp. The address is not readable. */ + /* This is known to happen under Solaris 2.4 + gcc, which place */ + /* string constants in the text segment, but after etext. */ + /* Use plan B. Note that we now know there is a gap between */ + /* text and data segments, so plan A brought us something. */ + result = (char *)GC_find_limit(DATAEND, FALSE); + } + return((ptr_t)result); + } +# endif + +#ifdef DATASTART_USES_BSDGETDATASTART +/* Its unclear whether this should be identical to the above, or */ +/* whether it should apply to non-X86 architectures. */ +/* For now we don't assume that there is always an empty page after */ +/* etext. But in some cases there actually seems to be slightly more. */ +/* This also deals with holes between read-only data and writable data. */ + GC_INNER ptr_t GC_FreeBSDGetDataStart(size_t max_page_size, + ptr_t etext_addr) + { + word text_end = ((word)(etext_addr) + sizeof(word) - 1) + & ~(word)(sizeof(word) - 1); + /* etext rounded to word boundary */ + volatile word next_page = (text_end + (word)max_page_size - 1) + & ~((word)max_page_size - 1); + volatile ptr_t result = (ptr_t)text_end; + GC_setup_temporary_fault_handler(); + if (SETJMP(GC_jmp_buf) == 0) { + /* Try reading at the address. */ + /* This should happen before there is another thread. */ + for (; next_page < (word)DATAEND; next_page += (word)max_page_size) + *(volatile char *)next_page; + GC_reset_fault_handler(); + } else { + GC_reset_fault_handler(); + /* As above, we go to plan B */ + result = GC_find_limit(DATAEND, FALSE); + } + return(result); + } +#endif /* DATASTART_USES_BSDGETDATASTART */ + +#ifdef AMIGA + +# define GC_AMIGA_DS +# include "extra/AmigaOS.c" +# undef GC_AMIGA_DS + +#elif defined(OPENBSD) + +/* Depending on arch alignment, there can be multiple holes */ +/* between DATASTART and DATAEND. Scan in DATASTART .. DATAEND */ +/* and register each region. */ +void GC_register_data_segments(void) +{ + ptr_t region_start = DATASTART; + + if ((word)region_start - 1U >= (word)DATAEND) + ABORT_ARG2("Wrong DATASTART/END pair", + ": %p .. %p", (void *)region_start, (void *)DATAEND); + for (;;) { + ptr_t region_end = GC_find_limit_openbsd(region_start, DATAEND); + + GC_add_roots_inner(region_start, region_end, FALSE); + if ((word)region_end >= (word)DATAEND) + break; + region_start = GC_skip_hole_openbsd(region_end, DATAEND); + } +} + +# else /* !OS2 && !Windows && !AMIGA && !OPENBSD */ + +# if !defined(PCR) && !defined(MACOS) && defined(REDIRECT_MALLOC) \ + && defined(GC_SOLARIS_THREADS) + EXTERN_C_BEGIN + extern caddr_t sbrk(int); + EXTERN_C_END +# endif + + void GC_register_data_segments(void) + { +# if !defined(PCR) && !defined(MACOS) +# if defined(REDIRECT_MALLOC) && defined(GC_SOLARIS_THREADS) + /* As of Solaris 2.3, the Solaris threads implementation */ + /* allocates the data structure for the initial thread with */ + /* sbrk at process startup. It needs to be scanned, so that */ + /* we don't lose some malloc allocated data structures */ + /* hanging from it. We're on thin ice here ... */ + GC_ASSERT(DATASTART); + { + ptr_t p = (ptr_t)sbrk(0); + if ((word)DATASTART < (word)p) + GC_add_roots_inner(DATASTART, p, FALSE); + } +# else + if ((word)DATASTART - 1U >= (word)DATAEND) { + /* Subtract one to check also for NULL */ + /* without a compiler warning. */ + ABORT_ARG2("Wrong DATASTART/END pair", + ": %p .. %p", (void *)DATASTART, (void *)DATAEND); + } + GC_add_roots_inner(DATASTART, DATAEND, FALSE); +# ifdef GC_HAVE_DATAREGION2 + if ((word)DATASTART2 - 1U >= (word)DATAEND2) + ABORT_ARG2("Wrong DATASTART/END2 pair", + ": %p .. %p", (void *)DATASTART2, (void *)DATAEND2); + GC_add_roots_inner(DATASTART2, DATAEND2, FALSE); +# endif +# endif +# endif +# if defined(MACOS) + { +# if defined(THINK_C) + extern void* GC_MacGetDataStart(void); + /* globals begin above stack and end at a5. */ + GC_add_roots_inner((ptr_t)GC_MacGetDataStart(), + (ptr_t)LMGetCurrentA5(), FALSE); +# else +# if defined(__MWERKS__) +# if !__POWERPC__ + extern void* GC_MacGetDataStart(void); + /* MATTHEW: Function to handle Far Globals (CW Pro 3) */ +# if __option(far_data) + extern void* GC_MacGetDataEnd(void); +# endif + /* globals begin above stack and end at a5. */ + GC_add_roots_inner((ptr_t)GC_MacGetDataStart(), + (ptr_t)LMGetCurrentA5(), FALSE); + /* MATTHEW: Handle Far Globals */ +# if __option(far_data) + /* Far globals follow he QD globals: */ + GC_add_roots_inner((ptr_t)LMGetCurrentA5(), + (ptr_t)GC_MacGetDataEnd(), FALSE); +# endif +# else + extern char __data_start__[], __data_end__[]; + GC_add_roots_inner((ptr_t)&__data_start__, + (ptr_t)&__data_end__, FALSE); +# endif /* __POWERPC__ */ +# endif /* __MWERKS__ */ +# endif /* !THINK_C */ + } +# endif /* MACOS */ + + /* Dynamic libraries are added at every collection, since they may */ + /* change. */ + } + +# endif /* !AMIGA */ +# endif /* !MSWIN32 && !MSWINCE */ +# endif /* !OS2 */ + +/* + * Auxiliary routines for obtaining memory from OS. + */ + +# if !defined(OS2) && !defined(PCR) && !defined(AMIGA) \ + && !defined(USE_WINALLOC) && !defined(MACOS) && !defined(DOS4GW) \ + && !defined(NINTENDO_SWITCH) && !defined(NONSTOP) \ + && !defined(SN_TARGET_ORBIS) && !defined(SN_TARGET_PS3) \ + && !defined(SN_TARGET_PSP2) && !defined(RTEMS) && !defined(__CC_ARM) + +# define SBRK_ARG_T ptrdiff_t + +#if defined(MMAP_SUPPORTED) + +#ifdef USE_MMAP_FIXED +# define GC_MMAP_FLAGS MAP_FIXED | MAP_PRIVATE + /* Seems to yield better performance on Solaris 2, but can */ + /* be unreliable if something is already mapped at the address. */ +#else +# define GC_MMAP_FLAGS MAP_PRIVATE +#endif + +#ifdef USE_MMAP_ANON +# define zero_fd -1 +# if defined(MAP_ANONYMOUS) && !defined(CPPCHECK) +# define OPT_MAP_ANON MAP_ANONYMOUS +# else +# define OPT_MAP_ANON MAP_ANON +# endif +#else + static int zero_fd = -1; +# define OPT_MAP_ANON 0 +#endif + +# ifndef MSWIN_XBOX1 +# if defined(SYMBIAN) && !defined(USE_MMAP_ANON) + EXTERN_C_BEGIN + extern char *GC_get_private_path_and_zero_file(void); + EXTERN_C_END +# endif + + STATIC ptr_t GC_unix_mmap_get_mem(size_t bytes) + { + void *result; + static ptr_t last_addr = HEAP_START; + +# ifndef USE_MMAP_ANON + static GC_bool initialized = FALSE; + + if (!EXPECT(initialized, TRUE)) { +# ifdef SYMBIAN + char *path = GC_get_private_path_and_zero_file(); + if (path != NULL) { + zero_fd = open(path, O_RDWR | O_CREAT, 0666); + free(path); + } +# else + zero_fd = open("/dev/zero", O_RDONLY); +# endif + if (zero_fd == -1) + ABORT("Could not open /dev/zero"); + if (fcntl(zero_fd, F_SETFD, FD_CLOEXEC) == -1) + WARN("Could not set FD_CLOEXEC for /dev/zero\n", 0); + + initialized = TRUE; + } +# endif + + if (bytes & (GC_page_size - 1)) ABORT("Bad GET_MEM arg"); + result = mmap(last_addr, bytes, (PROT_READ | PROT_WRITE) + | (GC_pages_executable ? PROT_EXEC : 0), + GC_MMAP_FLAGS | OPT_MAP_ANON, zero_fd, 0/* offset */); +# undef IGNORE_PAGES_EXECUTABLE + + if (result == MAP_FAILED) return(0); + last_addr = (ptr_t)(((word)result + bytes + GC_page_size - 1) + & ~(GC_page_size - 1)); +# if !defined(LINUX) + if (last_addr == 0) { + /* Oops. We got the end of the address space. This isn't */ + /* usable by arbitrary C code, since one-past-end pointers */ + /* don't work, so we discard it and try again. */ + munmap(result, ~GC_page_size - (size_t)result + 1); + /* Leave last page mapped, so we can't repeat. */ + return GC_unix_mmap_get_mem(bytes); + } +# else + GC_ASSERT(last_addr != 0); +# endif + if (((word)result % HBLKSIZE) != 0) + ABORT( + "GC_unix_get_mem: Memory returned by mmap is not aligned to HBLKSIZE."); + return((ptr_t)result); + } +# endif /* !MSWIN_XBOX1 */ + +#endif /* MMAP_SUPPORTED */ + +#if defined(USE_MMAP) + ptr_t GC_unix_get_mem(size_t bytes) + { + return GC_unix_mmap_get_mem(bytes); + } +#else /* !USE_MMAP */ + +STATIC ptr_t GC_unix_sbrk_get_mem(size_t bytes) +{ + ptr_t result; +# ifdef IRIX5 + /* Bare sbrk isn't thread safe. Play by malloc rules. */ + /* The equivalent may be needed on other systems as well. */ + __LOCK_MALLOC(); +# endif + { + ptr_t cur_brk = (ptr_t)sbrk(0); + SBRK_ARG_T lsbs = (word)cur_brk & (GC_page_size-1); + + if ((SBRK_ARG_T)bytes < 0) { + result = 0; /* too big */ + goto out; + } + if (lsbs != 0) { + if((ptr_t)sbrk((SBRK_ARG_T)GC_page_size - lsbs) == (ptr_t)(-1)) { + result = 0; + goto out; + } + } +# ifdef ADD_HEAP_GUARD_PAGES + /* This is useful for catching severe memory overwrite problems that */ + /* span heap sections. It shouldn't otherwise be turned on. */ + { + ptr_t guard = (ptr_t)sbrk((SBRK_ARG_T)GC_page_size); + if (mprotect(guard, GC_page_size, PROT_NONE) != 0) + ABORT("ADD_HEAP_GUARD_PAGES: mprotect failed"); + } +# endif /* ADD_HEAP_GUARD_PAGES */ + result = (ptr_t)sbrk((SBRK_ARG_T)bytes); + if (result == (ptr_t)(-1)) result = 0; + } + out: +# ifdef IRIX5 + __UNLOCK_MALLOC(); +# endif + return(result); +} + +ptr_t GC_unix_get_mem(size_t bytes) +{ +# if defined(MMAP_SUPPORTED) + /* By default, we try both sbrk and mmap, in that order. */ + static GC_bool sbrk_failed = FALSE; + ptr_t result = 0; + + if (!sbrk_failed) result = GC_unix_sbrk_get_mem(bytes); + if (0 == result) { + sbrk_failed = TRUE; + result = GC_unix_mmap_get_mem(bytes); + } + if (0 == result) { + /* Try sbrk again, in case sbrk memory became available. */ + result = GC_unix_sbrk_get_mem(bytes); + } + return result; +# else /* !MMAP_SUPPORTED */ + return GC_unix_sbrk_get_mem(bytes); +# endif +} + +#endif /* !USE_MMAP */ + +# endif /* UN*X */ + +# ifdef OS2 + +void * os2_alloc(size_t bytes) +{ + void * result; + + if (DosAllocMem(&result, bytes, (PAG_READ | PAG_WRITE | PAG_COMMIT) + | (GC_pages_executable ? PAG_EXECUTE : 0)) + != NO_ERROR) { + return(0); + } + /* FIXME: What's the purpose of this recursion? (Probably, if */ + /* DosAllocMem returns memory at 0 address then just retry once.) */ + if (result == 0) return(os2_alloc(bytes)); + return(result); +} + +# endif /* OS2 */ + +# ifdef MSWIN_XBOX1 + void *durango_get_mem(size_t bytes, size_t page_size) + { + if (0 == bytes) return NULL; + return VirtualAlloc(NULL, bytes, MEM_COMMIT | MEM_TOP_DOWN, + PAGE_READWRITE); + } +#endif + +#ifdef MSWINCE + ptr_t GC_wince_get_mem(size_t bytes) + { + ptr_t result = 0; /* initialized to prevent warning. */ + word i; + + bytes = ROUNDUP_PAGESIZE(bytes); + + /* Try to find reserved, uncommitted pages */ + for (i = 0; i < GC_n_heap_bases; i++) { + if (((word)(-(signed_word)GC_heap_lengths[i]) + & (GC_sysinfo.dwAllocationGranularity-1)) + >= bytes) { + result = GC_heap_bases[i] + GC_heap_lengths[i]; + break; + } + } + + if (i == GC_n_heap_bases) { + /* Reserve more pages */ + size_t res_bytes = + SIZET_SAT_ADD(bytes, (size_t)GC_sysinfo.dwAllocationGranularity-1) + & ~((size_t)GC_sysinfo.dwAllocationGranularity-1); + /* If we ever support MPROTECT_VDB here, we will probably need to */ + /* ensure that res_bytes is strictly > bytes, so that VirtualProtect */ + /* never spans regions. It seems to be OK for a VirtualFree */ + /* argument to span regions, so we should be OK for now. */ + result = (ptr_t) VirtualAlloc(NULL, res_bytes, + MEM_RESERVE | MEM_TOP_DOWN, + GC_pages_executable ? PAGE_EXECUTE_READWRITE : + PAGE_READWRITE); + if (HBLKDISPL(result) != 0) ABORT("Bad VirtualAlloc result"); + /* If I read the documentation correctly, this can */ + /* only happen if HBLKSIZE > 64k or not a power of 2. */ + if (GC_n_heap_bases >= MAX_HEAP_SECTS) ABORT("Too many heap sections"); + if (result == NULL) return NULL; + GC_heap_bases[GC_n_heap_bases] = result; + GC_heap_lengths[GC_n_heap_bases] = 0; + GC_n_heap_bases++; + } + + /* Commit pages */ + result = (ptr_t) VirtualAlloc(result, bytes, MEM_COMMIT, + GC_pages_executable ? PAGE_EXECUTE_READWRITE : + PAGE_READWRITE); +# undef IGNORE_PAGES_EXECUTABLE + + if (result != NULL) { + if (HBLKDISPL(result) != 0) ABORT("Bad VirtualAlloc result"); + GC_heap_lengths[i] += bytes; + } + + return(result); + } + +#elif (defined(USE_WINALLOC) && !defined(MSWIN_XBOX1)) || defined(CYGWIN32) + +# ifdef USE_GLOBAL_ALLOC +# define GLOBAL_ALLOC_TEST 1 +# else +# define GLOBAL_ALLOC_TEST GC_no_win32_dlls +# endif + +# if (defined(GC_USE_MEM_TOP_DOWN) && defined(USE_WINALLOC)) \ + || defined(CPPCHECK) + DWORD GC_mem_top_down = MEM_TOP_DOWN; + /* Use GC_USE_MEM_TOP_DOWN for better 64-bit */ + /* testing. Otherwise all addresses tend to */ + /* end up in first 4GB, hiding bugs. */ +# else +# define GC_mem_top_down 0 +# endif /* !GC_USE_MEM_TOP_DOWN */ + + ptr_t GC_win32_get_mem(size_t bytes) + { + ptr_t result; + +# ifndef USE_WINALLOC + result = GC_unix_get_mem(bytes); +# else +# if defined(MSWIN32) && !defined(MSWINRT_FLAVOR) + if (GLOBAL_ALLOC_TEST) { + /* VirtualAlloc doesn't like PAGE_EXECUTE_READWRITE. */ + /* There are also unconfirmed rumors of other */ + /* problems, so we dodge the issue. */ + result = (ptr_t)GlobalAlloc(0, SIZET_SAT_ADD(bytes, HBLKSIZE)); + /* Align it at HBLKSIZE boundary. */ + result = (ptr_t)(((word)result + HBLKSIZE - 1) + & ~(word)(HBLKSIZE - 1)); + } else +# endif + /* else */ { + /* VirtualProtect only works on regions returned by a */ + /* single VirtualAlloc call. Thus we allocate one */ + /* extra page, which will prevent merging of blocks */ + /* in separate regions, and eliminate any temptation */ + /* to call VirtualProtect on a range spanning regions. */ + /* This wastes a small amount of memory, and risks */ + /* increased fragmentation. But better alternatives */ + /* would require effort. */ +# ifdef MPROTECT_VDB + /* We can't check for GC_incremental here (because */ + /* GC_enable_incremental() might be called some time */ + /* later after the GC initialization). */ +# ifdef GWW_VDB +# define VIRTUAL_ALLOC_PAD (GC_GWW_AVAILABLE() ? 0 : 1) +# else +# define VIRTUAL_ALLOC_PAD 1 +# endif +# else +# define VIRTUAL_ALLOC_PAD 0 +# endif + /* Pass the MEM_WRITE_WATCH only if GetWriteWatch-based */ + /* VDBs are enabled and the GetWriteWatch function is */ + /* available. Otherwise we waste resources or possibly */ + /* cause VirtualAlloc to fail (observed in Windows 2000 */ + /* SP2). */ + result = (ptr_t) VirtualAlloc(NULL, + SIZET_SAT_ADD(bytes, VIRTUAL_ALLOC_PAD), + GetWriteWatch_alloc_flag + | (MEM_COMMIT | MEM_RESERVE) + | GC_mem_top_down, + GC_pages_executable ? PAGE_EXECUTE_READWRITE : + PAGE_READWRITE); +# undef IGNORE_PAGES_EXECUTABLE + } +# endif /* USE_WINALLOC */ + if (HBLKDISPL(result) != 0) ABORT("Bad VirtualAlloc result"); + /* If I read the documentation correctly, this can */ + /* only happen if HBLKSIZE > 64k or not a power of 2. */ + if (GC_n_heap_bases >= MAX_HEAP_SECTS) ABORT("Too many heap sections"); + if (0 != result) GC_heap_bases[GC_n_heap_bases++] = result; + return(result); + } + + GC_API void GC_CALL GC_win32_free_heap(void) + { +# ifndef MSWINRT_FLAVOR +# ifndef CYGWIN32 + if (GLOBAL_ALLOC_TEST) +# endif + { + while (GC_n_heap_bases-- > 0) { +# ifdef CYGWIN32 + /* FIXME: Is it OK to use non-GC free() here? */ +# else + GlobalFree(GC_heap_bases[GC_n_heap_bases]); +# endif + GC_heap_bases[GC_n_heap_bases] = 0; + } + return; + } +# endif +# ifndef CYGWIN32 + /* Avoiding VirtualAlloc leak. */ + while (GC_n_heap_bases > 0) { + VirtualFree(GC_heap_bases[--GC_n_heap_bases], 0, MEM_RELEASE); + GC_heap_bases[GC_n_heap_bases] = 0; + } +# endif + } +#endif /* USE_WINALLOC || CYGWIN32 */ + +#ifdef AMIGA +# define GC_AMIGA_AM +# include "extra/AmigaOS.c" +# undef GC_AMIGA_AM +#endif + +#if defined(HAIKU) +# include + ptr_t GC_haiku_get_mem(size_t bytes) + { + void* mem; + + GC_ASSERT(GC_page_size != 0); + if (posix_memalign(&mem, GC_page_size, bytes) == 0) + return mem; + return NULL; + } +#endif /* HAIKU */ + +#ifdef USE_MUNMAP + +/* For now, this only works on Win32/WinCE and some Unix-like */ +/* systems. If you have something else, don't define */ +/* USE_MUNMAP. */ + +#if !defined(NN_PLATFORM_CTR) && !defined(MSWIN32) && !defined(MSWINCE) \ + && !defined(MSWIN_XBOX1) +# include +# ifdef SN_TARGET_PS3 +# include +# else +# include +# endif +# include +# include +#endif + +/* Compute a page aligned starting address for the unmap */ +/* operation on a block of size bytes starting at start. */ +/* Return 0 if the block is too small to make this feasible. */ +STATIC ptr_t GC_unmap_start(ptr_t start, size_t bytes) +{ + ptr_t result = (ptr_t)(((word)start + GC_page_size - 1) + & ~(GC_page_size - 1)); + + if ((word)(result + GC_page_size) > (word)(start + bytes)) return 0; + return result; +} + +/* Compute end address for an unmap operation on the indicated */ +/* block. */ +STATIC ptr_t GC_unmap_end(ptr_t start, size_t bytes) +{ + return (ptr_t)((word)(start + bytes) & ~(GC_page_size - 1)); +} + +/* Under Win32/WinCE we commit (map) and decommit (unmap) */ +/* memory using VirtualAlloc and VirtualFree. These functions */ +/* work on individual allocations of virtual memory, made */ +/* previously using VirtualAlloc with the MEM_RESERVE flag. */ +/* The ranges we need to (de)commit may span several of these */ +/* allocations; therefore we use VirtualQuery to check */ +/* allocation lengths, and split up the range as necessary. */ + +/* We assume that GC_remap is called on exactly the same range */ +/* as a previous call to GC_unmap. It is safe to consistently */ +/* round the endpoints in both places. */ +GC_INNER void GC_unmap(ptr_t start, size_t bytes) +{ + ptr_t start_addr = GC_unmap_start(start, bytes); + ptr_t end_addr = GC_unmap_end(start, bytes); + word len = end_addr - start_addr; + + if (0 == start_addr) return; +# ifdef USE_WINALLOC + while (len != 0) { + MEMORY_BASIC_INFORMATION mem_info; + word free_len; + + if (VirtualQuery(start_addr, &mem_info, sizeof(mem_info)) + != sizeof(mem_info)) + ABORT("Weird VirtualQuery result"); + free_len = (len < mem_info.RegionSize) ? len : mem_info.RegionSize; + if (!VirtualFree(start_addr, free_len, MEM_DECOMMIT)) + ABORT("VirtualFree failed"); + GC_unmapped_bytes += free_len; + start_addr += free_len; + len -= free_len; + } +# elif defined(SN_TARGET_PS3) + ps3_free_mem(start_addr, len); +# else + /* We immediately remap it to prevent an intervening mmap from */ + /* accidentally grabbing the same address space. */ + { +# ifdef CYGWIN32 + /* Calling mmap() with the new protection flags on an */ + /* existing memory map with MAP_FIXED is broken on Cygwin. */ + /* However, calling mprotect() on the given address range */ + /* with PROT_NONE seems to work fine. */ + if (mprotect(start_addr, len, PROT_NONE)) + ABORT("mprotect(PROT_NONE) failed"); +# else + void * result = mmap(start_addr, len, PROT_NONE, + MAP_PRIVATE | MAP_FIXED | OPT_MAP_ANON, + zero_fd, 0/* offset */); + + if (result != (void *)start_addr) + ABORT("mmap(PROT_NONE) failed"); +# if defined(CPPCHECK) || defined(LINT2) + /* Explicitly store the resource handle to a global variable. */ + GC_noop1((word)result); +# endif +# endif /* !CYGWIN32 */ + } + GC_unmapped_bytes += len; +# endif +} + +GC_INNER void GC_remap(ptr_t start, size_t bytes) +{ + ptr_t start_addr = GC_unmap_start(start, bytes); + ptr_t end_addr = GC_unmap_end(start, bytes); + word len = end_addr - start_addr; + if (0 == start_addr) return; + + /* FIXME: Handle out-of-memory correctly (at least for Win32) */ +# ifdef USE_WINALLOC + while (len != 0) { + MEMORY_BASIC_INFORMATION mem_info; + word alloc_len; + ptr_t result; + + if (VirtualQuery(start_addr, &mem_info, sizeof(mem_info)) + != sizeof(mem_info)) + ABORT("Weird VirtualQuery result"); + alloc_len = (len < mem_info.RegionSize) ? len : mem_info.RegionSize; + result = VirtualAlloc(start_addr, alloc_len, MEM_COMMIT, + GC_pages_executable ? PAGE_EXECUTE_READWRITE : + PAGE_READWRITE); + if (result != start_addr) { + if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY || + GetLastError() == ERROR_OUTOFMEMORY) { + ABORT("Not enough memory to process remapping"); + } else { + ABORT("VirtualAlloc remapping failed"); + } + } +# ifdef LINT2 + GC_noop1((word)result); +# endif + GC_unmapped_bytes -= alloc_len; + start_addr += alloc_len; + len -= alloc_len; + } +# else + /* It was already remapped with PROT_NONE. */ + { +# ifdef NACL + /* NaCl does not expose mprotect, but mmap should work fine. */ + void *result = mmap(start_addr, len, (PROT_READ | PROT_WRITE) + | (GC_pages_executable ? PROT_EXEC : 0), + MAP_PRIVATE | MAP_FIXED | OPT_MAP_ANON, + zero_fd, 0 /* offset */); + if (result != (void *)start_addr) + ABORT("mmap as mprotect failed"); +# if defined(CPPCHECK) || defined(LINT2) + GC_noop1((word)result); +# endif +# else + if (mprotect(start_addr, len, (PROT_READ | PROT_WRITE) + | (GC_pages_executable ? PROT_EXEC : 0)) != 0) { + ABORT_ARG3("mprotect remapping failed", + " at %p (length %lu), errcode= %d", + (void *)start_addr, (unsigned long)len, errno); + } +# endif /* !NACL */ + } +# undef IGNORE_PAGES_EXECUTABLE + GC_unmapped_bytes -= len; +# endif +} + +/* Two adjacent blocks have already been unmapped and are about to */ +/* be merged. Unmap the whole block. This typically requires */ +/* that we unmap a small section in the middle that was not previously */ +/* unmapped due to alignment constraints. */ +GC_INNER void GC_unmap_gap(ptr_t start1, size_t bytes1, ptr_t start2, + size_t bytes2) +{ + ptr_t start1_addr = GC_unmap_start(start1, bytes1); + ptr_t end1_addr = GC_unmap_end(start1, bytes1); + ptr_t start2_addr = GC_unmap_start(start2, bytes2); + ptr_t start_addr = end1_addr; + ptr_t end_addr = start2_addr; + size_t len; + + GC_ASSERT(start1 + bytes1 == start2); + if (0 == start1_addr) start_addr = GC_unmap_start(start1, bytes1 + bytes2); + if (0 == start2_addr) end_addr = GC_unmap_end(start1, bytes1 + bytes2); + if (0 == start_addr) return; + len = end_addr - start_addr; +# ifdef USE_WINALLOC + while (len != 0) { + MEMORY_BASIC_INFORMATION mem_info; + word free_len; + + if (VirtualQuery(start_addr, &mem_info, sizeof(mem_info)) + != sizeof(mem_info)) + ABORT("Weird VirtualQuery result"); + free_len = (len < mem_info.RegionSize) ? len : mem_info.RegionSize; + if (!VirtualFree(start_addr, free_len, MEM_DECOMMIT)) + ABORT("VirtualFree failed"); + GC_unmapped_bytes += free_len; + start_addr += free_len; + len -= free_len; + } +# else + if (len != 0) { + /* Immediately remap as above. */ +# ifdef CYGWIN32 + if (mprotect(start_addr, len, PROT_NONE)) + ABORT("mprotect(PROT_NONE) failed"); +# else + void * result = mmap(start_addr, len, PROT_NONE, + MAP_PRIVATE | MAP_FIXED | OPT_MAP_ANON, + zero_fd, 0/* offset */); + + if (result != (void *)start_addr) + ABORT("mmap(PROT_NONE) failed"); +# if defined(CPPCHECK) || defined(LINT2) + GC_noop1((word)result); +# endif +# endif /* !CYGWIN32 */ + GC_unmapped_bytes += len; + } +# endif +} + +#endif /* USE_MUNMAP */ + +/* Routine for pushing any additional roots. In THREADS */ +/* environment, this is also responsible for marking from */ +/* thread stacks. */ +#ifndef THREADS + GC_push_other_roots_proc GC_push_other_roots = 0; +#else /* THREADS */ + +# ifdef PCR +PCR_ERes GC_push_thread_stack(PCR_Th_T *t, PCR_Any dummy) +{ + struct PCR_ThCtl_TInfoRep info; + PCR_ERes result; + + info.ti_stkLow = info.ti_stkHi = 0; + result = PCR_ThCtl_GetInfo(t, &info); + GC_push_all_stack((ptr_t)(info.ti_stkLow), (ptr_t)(info.ti_stkHi)); + return(result); +} + +/* Push the contents of an old object. We treat this as stack */ +/* data only because that makes it robust against mark stack */ +/* overflow. */ +PCR_ERes GC_push_old_obj(void *p, size_t size, PCR_Any data) +{ + GC_push_all_stack((ptr_t)p, (ptr_t)p + size); + return(PCR_ERes_okay); +} + +extern struct PCR_MM_ProcsRep * GC_old_allocator; + /* defined in pcr_interface.c. */ + +STATIC void GC_CALLBACK GC_default_push_other_roots(void) +{ + /* Traverse data allocated by previous memory managers. */ + if ((*(GC_old_allocator->mmp_enumerate))(PCR_Bool_false, + GC_push_old_obj, 0) + != PCR_ERes_okay) { + ABORT("Old object enumeration failed"); + } + /* Traverse all thread stacks. */ + if (PCR_ERes_IsErr( + PCR_ThCtl_ApplyToAllOtherThreads(GC_push_thread_stack,0)) + || PCR_ERes_IsErr(GC_push_thread_stack(PCR_Th_CurrThread(), 0))) { + ABORT("Thread stack marking failed"); + } +} + +# endif /* PCR */ + +# if defined(NN_PLATFORM_CTR) || defined(NINTENDO_SWITCH) \ + || defined(GC_PTHREADS) || defined(GC_WIN32_THREADS) + STATIC void GC_CALLBACK GC_default_push_other_roots(void) + { + GC_push_all_stacks(); + } +# endif + +# ifdef SN_TARGET_PS3 + STATIC void GC_CALLBACK GC_default_push_other_roots(void) + { + ABORT("GC_default_push_other_roots is not implemented"); + } + + void GC_push_thread_structures(void) + { + ABORT("GC_push_thread_structures is not implemented"); + } +# endif /* SN_TARGET_PS3 */ + + GC_push_other_roots_proc GC_push_other_roots = GC_default_push_other_roots; +#endif /* THREADS */ + +GC_API void GC_CALL GC_set_push_other_roots(GC_push_other_roots_proc fn) +{ + GC_push_other_roots = fn; +} + +GC_API GC_push_other_roots_proc GC_CALL GC_get_push_other_roots(void) +{ + return GC_push_other_roots; +} + +/* + * Routines for accessing dirty bits on virtual pages. + * There are six ways to maintain this information: + * DEFAULT_VDB: A simple dummy implementation that treats every page + * as possibly dirty. This makes incremental collection + * useless, but the implementation is still correct. + * MANUAL_VDB: Stacks and static data are always considered dirty. + * Heap pages are considered dirty if GC_dirty(p) has been + * called on some pointer p pointing to somewhere inside + * an object on that page. A GC_dirty() call on a large + * object directly dirties only a single page, but for + * MANUAL_VDB we are careful to treat an object with a dirty + * page as completely dirty. + * In order to avoid races, an object must be marked dirty + * after it is written, and a reference to the object + * must be kept on a stack or in a register in the interim. + * With threads enabled, an object directly reachable from the + * stack at the time of a collection is treated as dirty. + * In single-threaded mode, it suffices to ensure that no + * collection can take place between the pointer assignment + * and the GC_dirty() call. + * PCR_VDB: Use PPCRs virtual dirty bit facility. + * PROC_VDB: Use the /proc facility for reading dirty bits. Only + * works under some SVR4 variants. Even then, it may be + * too slow to be entirely satisfactory. Requires reading + * dirty bits for entire address space. Implementations tend + * to assume that the client is a (slow) debugger. + * MPROTECT_VDB:Protect pages and then catch the faults to keep track of + * dirtied pages. The implementation (and implementability) + * is highly system dependent. This usually fails when system + * calls write to a protected page. We prevent the read system + * call from doing so. It is the clients responsibility to + * make sure that other system calls are similarly protected + * or write only to the stack. + * GWW_VDB: Use the Win32 GetWriteWatch functions, if available, to + * read dirty bits. In case it is not available (because we + * are running on Windows 95, Windows 2000 or earlier), + * MPROTECT_VDB may be defined as a fallback strategy. + */ + +#if defined(GWW_VDB) || defined(MPROTECT_VDB) || defined(PROC_VDB) \ + || defined(MANUAL_VDB) + /* Is the HBLKSIZE sized page at h marked dirty in the local buffer? */ + /* If the actual page size is different, this returns TRUE if any */ + /* of the pages overlapping h are dirty. This routine may err on the */ + /* side of labeling pages as dirty (and this implementation does). */ + GC_INNER GC_bool GC_page_was_dirty(struct hblk * h) + { + word index; + + if (HDR(h) == 0) + return TRUE; + index = PHT_HASH(h); + return get_pht_entry_from_index(GC_grungy_pages, index); + } +#endif + +#if (defined(CHECKSUMS) && defined(GWW_VDB)) || defined(PROC_VDB) + /* Add all pages in pht2 to pht1. */ + STATIC void GC_or_pages(page_hash_table pht1, page_hash_table pht2) + { + unsigned i; + for (i = 0; i < PHT_SIZE; i++) pht1[i] |= pht2[i]; + } + + /* Used only if GWW_VDB. */ +# ifdef MPROTECT_VDB + STATIC GC_bool GC_gww_page_was_ever_dirty(struct hblk * h) +# else + GC_INNER GC_bool GC_page_was_ever_dirty(struct hblk * h) +# endif + { + word index; + + if (HDR(h) == 0) + return TRUE; + index = PHT_HASH(h); + return get_pht_entry_from_index(GC_written_pages, index); + } +#endif /* CHECKSUMS && GWW_VDB || PROC_VDB */ + +#if ((defined(GWW_VDB) || defined(PROC_VDB)) && !defined(MPROTECT_VDB)) \ + || defined(MANUAL_VDB) || defined(DEFAULT_VDB) + /* Ignore write hints. They don't help us here. */ + GC_INNER void GC_remove_protection(struct hblk * h GC_ATTR_UNUSED, + word nblocks GC_ATTR_UNUSED, + GC_bool is_ptrfree GC_ATTR_UNUSED) {} +#endif + +#ifdef GWW_VDB + +# define GC_GWW_BUF_LEN (MAXHINCR * HBLKSIZE / 4096 /* X86 page size */) + /* Still susceptible to overflow, if there are very large allocations, */ + /* and everything is dirty. */ + static PVOID gww_buf[GC_GWW_BUF_LEN]; + +# ifndef MPROTECT_VDB +# define GC_gww_dirty_init GC_dirty_init +# endif + + GC_INNER GC_bool GC_gww_dirty_init(void) + { + detect_GetWriteWatch(); + return GC_GWW_AVAILABLE(); + } + +# ifdef MPROTECT_VDB + STATIC void GC_gww_read_dirty(GC_bool output_unneeded) +# else + GC_INNER void GC_read_dirty(GC_bool output_unneeded) +# endif + { + word i; + + if (!output_unneeded) + BZERO(GC_grungy_pages, sizeof(GC_grungy_pages)); + + for (i = 0; i != GC_n_heap_sects; ++i) { + GC_ULONG_PTR count; + + do { + PVOID * pages = gww_buf; + DWORD page_size; + + count = GC_GWW_BUF_LEN; + /* GetWriteWatch is documented as returning non-zero when it */ + /* fails, but the documentation doesn't explicitly say why it */ + /* would fail or what its behaviour will be if it fails. */ + /* It does appear to fail, at least on recent W2K instances, if */ + /* the underlying memory was not allocated with the appropriate */ + /* flag. This is common if GC_enable_incremental is called */ + /* shortly after GC initialization. To avoid modifying the */ + /* interface, we silently work around such a failure, it only */ + /* affects the initial (small) heap allocation. If there are */ + /* more dirty pages than will fit in the buffer, this is not */ + /* treated as a failure; we must check the page count in the */ + /* loop condition. Since each partial call will reset the */ + /* status of some pages, this should eventually terminate even */ + /* in the overflow case. */ + if (GetWriteWatch_func(WRITE_WATCH_FLAG_RESET, + GC_heap_sects[i].hs_start, + GC_heap_sects[i].hs_bytes, + pages, + &count, + &page_size) != 0) { + static int warn_count = 0; + struct hblk * start = (struct hblk *)GC_heap_sects[i].hs_start; + static struct hblk *last_warned = 0; + size_t nblocks = divHBLKSZ(GC_heap_sects[i].hs_bytes); + + if (i != 0 && last_warned != start && warn_count++ < 5) { + last_warned = start; + WARN("GC_gww_read_dirty unexpectedly failed at %p: " + "Falling back to marking all pages dirty\n", start); + } + if (!output_unneeded) { + unsigned j; + + for (j = 0; j < nblocks; ++j) { + word hash = PHT_HASH(start + j); + set_pht_entry_from_index(GC_grungy_pages, hash); + } + } + count = 1; /* Done with this section. */ + } else /* succeeded */ if (!output_unneeded) { + PVOID * pages_end = pages + count; + + while (pages != pages_end) { + struct hblk * h = (struct hblk *) *pages++; + struct hblk * h_end = (struct hblk *) ((char *) h + page_size); + do { + set_pht_entry_from_index(GC_grungy_pages, PHT_HASH(h)); + } while ((word)(++h) < (word)h_end); + } + } + } while (count == GC_GWW_BUF_LEN); + /* FIXME: It's unclear from Microsoft's documentation if this loop */ + /* is useful. We suspect the call just fails if the buffer fills */ + /* up. But that should still be handled correctly. */ + } + +# ifdef CHECKSUMS + GC_ASSERT(!output_unneeded); + GC_or_pages(GC_written_pages, GC_grungy_pages); +# endif + } +#endif /* GWW_VDB */ + +#ifdef DEFAULT_VDB + /* All of the following assume the allocation lock is held. */ + + /* The client asserts that unallocated pages in the heap are never */ + /* written. */ + + /* Initialize virtual dirty bit implementation. */ + GC_INNER GC_bool GC_dirty_init(void) + { + GC_VERBOSE_LOG_PRINTF("Initializing DEFAULT_VDB...\n"); + return TRUE; + } + + /* Retrieve system dirty bits for heap to a local buffer. */ + /* Restore the systems notion of which pages are dirty. */ + GC_INNER void GC_read_dirty(GC_bool output_unneeded GC_ATTR_UNUSED) {} + + /* Is the HBLKSIZE sized page at h marked dirty in the local buffer? */ + /* If the actual page size is different, this returns TRUE if any */ + /* of the pages overlapping h are dirty. This routine may err on the */ + /* side of labeling pages as dirty (and this implementation does). */ + GC_INNER GC_bool GC_page_was_dirty(struct hblk * h GC_ATTR_UNUSED) + { + return(TRUE); + } + + /* The following two routines are typically less crucial. */ + /* They matter most with large dynamic libraries, or if we can't */ + /* accurately identify stacks, e.g. under Solaris 2.X. Otherwise the */ + /* following default versions are adequate. */ +# ifdef CHECKSUMS + /* Could any valid GC heap pointer ever have been written to this page? */ + GC_INNER GC_bool GC_page_was_ever_dirty(struct hblk * h GC_ATTR_UNUSED) + { + return(TRUE); + } +# endif /* CHECKSUMS */ + +#endif /* DEFAULT_VDB */ + +#ifdef MANUAL_VDB + /* Initialize virtual dirty bit implementation. */ + GC_INNER GC_bool GC_dirty_init(void) + { + GC_VERBOSE_LOG_PRINTF("Initializing MANUAL_VDB...\n"); + /* GC_dirty_pages and GC_grungy_pages are already cleared. */ + return TRUE; + } + + /* Retrieve system dirty bits for the heap to a local buffer */ + /* (unless output_unneeded). Restore the systems notion of */ + /* which pages are dirty. */ + GC_INNER void GC_read_dirty(GC_bool output_unneeded) + { + if (!output_unneeded) + BCOPY((word *)GC_dirty_pages, GC_grungy_pages, sizeof(GC_dirty_pages)); + BZERO((word *)GC_dirty_pages, (sizeof GC_dirty_pages)); + } + +#ifndef GC_DISABLE_INCREMENTAL +# ifndef THREADS +# define async_set_pht_entry_from_index(db, index) \ + set_pht_entry_from_index(db, index) +# elif defined(set_pht_entry_from_index_concurrent) +# define async_set_pht_entry_from_index(db, index) \ + set_pht_entry_from_index_concurrent(db, index) +# elif defined(AO_HAVE_test_and_set_acquire) + /* We need to lock around the bitmap update (in the write fault */ + /* handler or GC_dirty) in order to avoid the risk of losing a bit. */ + /* We do this with a test-and-set spin lock if possible. */ + GC_INNER volatile AO_TS_t GC_fault_handler_lock = AO_TS_INITIALIZER; + + static void async_set_pht_entry_from_index(volatile page_hash_table db, + size_t index) + { + GC_acquire_dirty_lock(); + set_pht_entry_from_index(db, index); + GC_release_dirty_lock(); + } +# else +# error No test_and_set operation: Introduces a race. +# endif /* THREADS && !AO_HAVE_test_and_set_acquire */ +#else +# define async_set_pht_entry_from_index(db, index) +#endif /* !GC_DISABLE_INCREMENTAL */ + + /* Mark the page containing p as dirty. Logically, this dirties the */ + /* entire object. */ +#if !IL2CPP_ENABLE_WRITE_BARRIER_VALIDATION + GC_API void GC_dirty_inner(const void *p) + { + word index = PHT_HASH(p); + async_set_pht_entry_from_index(GC_dirty_pages, index); + } +#endif + +# ifdef CHECKSUMS + /* Could any valid GC heap pointer ever have been written to this page? */ + GC_INNER GC_bool GC_page_was_ever_dirty(struct hblk * h GC_ATTR_UNUSED) + { + /* FIXME - implement me. */ + return(TRUE); + } +# endif /* CHECKSUMS */ + +#endif /* MANUAL_VDB */ + +#ifdef MPROTECT_VDB + /* See DEFAULT_VDB for interface descriptions. */ + + /* + * This implementation maintains dirty bits itself by catching write + * faults and keeping track of them. We assume nobody else catches + * SIGBUS or SIGSEGV. We assume no write faults occur in system calls. + * This means that clients must ensure that system calls don't write + * to the write-protected heap. Probably the best way to do this is to + * ensure that system calls write at most to pointer-free objects in the + * heap, and do even that only if we are on a platform on which those + * are not protected. Another alternative is to wrap system calls + * (see example for read below), but the current implementation holds + * applications. + * We assume the page size is a multiple of HBLKSIZE. + * We prefer them to be the same. We avoid protecting pointer-free + * objects only if they are the same. + */ +# ifdef DARWIN + /* Using vm_protect (mach syscall) over mprotect (BSD syscall) seems to + decrease the likelihood of some of the problems described below. */ +# include + STATIC mach_port_t GC_task_self = 0; +# define PROTECT(addr,len) \ + if (vm_protect(GC_task_self, (vm_address_t)(addr), (vm_size_t)(len), \ + FALSE, VM_PROT_READ \ + | (GC_pages_executable ? VM_PROT_EXECUTE : 0)) \ + == KERN_SUCCESS) {} else ABORT("vm_protect(PROTECT) failed") +# define UNPROTECT(addr,len) \ + if (vm_protect(GC_task_self, (vm_address_t)(addr), (vm_size_t)(len), \ + FALSE, (VM_PROT_READ | VM_PROT_WRITE) \ + | (GC_pages_executable ? VM_PROT_EXECUTE : 0)) \ + == KERN_SUCCESS) {} else ABORT("vm_protect(UNPROTECT) failed") + +# elif !defined(USE_WINALLOC) +# include +# include +# if !defined(HAIKU) +# include +# endif + +# define PROTECT(addr, len) \ + if (mprotect((caddr_t)(addr), (size_t)(len), \ + PROT_READ \ + | (GC_pages_executable ? PROT_EXEC : 0)) >= 0) { \ + } else ABORT("mprotect failed") +# define UNPROTECT(addr, len) \ + if (mprotect((caddr_t)(addr), (size_t)(len), \ + (PROT_READ | PROT_WRITE) \ + | (GC_pages_executable ? PROT_EXEC : 0)) >= 0) { \ + } else ABORT(GC_pages_executable ? \ + "un-mprotect executable page failed" \ + " (probably disabled by OS)" : \ + "un-mprotect failed") +# undef IGNORE_PAGES_EXECUTABLE + +# else /* USE_WINALLOC */ +# ifndef MSWINCE +# include +# endif + + static DWORD protect_junk; +# define PROTECT(addr, len) \ + if (VirtualProtect((addr), (len), \ + GC_pages_executable ? PAGE_EXECUTE_READ : \ + PAGE_READONLY, \ + &protect_junk)) { \ + } else ABORT_ARG1("VirtualProtect failed", \ + ": errcode= 0x%X", (unsigned)GetLastError()) +# define UNPROTECT(addr, len) \ + if (VirtualProtect((addr), (len), \ + GC_pages_executable ? PAGE_EXECUTE_READWRITE : \ + PAGE_READWRITE, \ + &protect_junk)) { \ + } else ABORT("un-VirtualProtect failed") +# endif /* USE_WINALLOC */ + +# if defined(MSWIN32) + typedef LPTOP_LEVEL_EXCEPTION_FILTER SIG_HNDLR_PTR; +# undef SIG_DFL +# define SIG_DFL (LPTOP_LEVEL_EXCEPTION_FILTER)((signed_word)-1) +# elif defined(MSWINCE) + typedef LONG (WINAPI *SIG_HNDLR_PTR)(struct _EXCEPTION_POINTERS *); +# undef SIG_DFL +# define SIG_DFL (SIG_HNDLR_PTR) (-1) +# elif defined(DARWIN) + typedef void (* SIG_HNDLR_PTR)(); +# else + typedef void (* SIG_HNDLR_PTR)(int, siginfo_t *, void *); + typedef void (* PLAIN_HNDLR_PTR)(int); +# endif + +# if defined(__GLIBC__) +# if __GLIBC__ < 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ < 2 +# error glibc too old? +# endif +# endif + +#ifndef DARWIN + STATIC SIG_HNDLR_PTR GC_old_segv_handler = 0; + /* Also old MSWIN32 ACCESS_VIOLATION filter */ +# if !defined(MSWIN32) && !defined(MSWINCE) + STATIC SIG_HNDLR_PTR GC_old_bus_handler = 0; +# if defined(FREEBSD) || defined(HURD) || defined(HPUX) + STATIC GC_bool GC_old_bus_handler_used_si = FALSE; +# endif + STATIC GC_bool GC_old_segv_handler_used_si = FALSE; +# endif /* !MSWIN32 */ +#endif /* !DARWIN */ + +#ifdef THREADS + /* This function is used only by the fault handler. Potential data */ + /* race between this function and GC_install_header, GC_remove_header */ + /* should not be harmful because the added or removed header should */ + /* be already unprotected. */ + GC_ATTR_NO_SANITIZE_THREAD + static GC_bool is_header_found_async(void *addr) + { +# ifdef HASH_TL + hdr *result; + GET_HDR((ptr_t)addr, result); + return result != NULL; +# else + return HDR_INNER(addr) != NULL; +# endif + } +#else +# define is_header_found_async(addr) (HDR(addr) != NULL) +#endif /* !THREADS */ + +#ifndef DARWIN + +# if !defined(MSWIN32) && !defined(MSWINCE) +# include +# if defined(FREEBSD) || defined(HURD) || defined(HPUX) +# define SIG_OK (sig == SIGBUS || sig == SIGSEGV) +# else +# define SIG_OK (sig == SIGSEGV) + /* Catch SIGSEGV but ignore SIGBUS. */ +# endif +# if defined(FREEBSD) +# ifndef SEGV_ACCERR +# define SEGV_ACCERR 2 +# endif +# if defined(AARCH64) || defined(ARM32) || defined(MIPS) +# define CODE_OK (si -> si_code == SEGV_ACCERR) +# elif defined(POWERPC) +# define AIM /* Pretend that we're AIM. */ +# include +# define CODE_OK (si -> si_code == EXC_DSI \ + || si -> si_code == SEGV_ACCERR) +# else +# define CODE_OK (si -> si_code == BUS_PAGE_FAULT \ + || si -> si_code == SEGV_ACCERR) +# endif +# elif defined(OSF1) +# define CODE_OK (si -> si_code == 2 /* experimentally determined */) +# elif defined(IRIX5) +# define CODE_OK (si -> si_code == EACCES) +# elif defined(HAIKU) || defined(HURD) +# define CODE_OK TRUE +# elif defined(LINUX) +# define CODE_OK TRUE + /* Empirically c.trapno == 14, on IA32, but is that useful? */ + /* Should probably consider alignment issues on other */ + /* architectures. */ +# elif defined(HPUX) +# define CODE_OK (si -> si_code == SEGV_ACCERR \ + || si -> si_code == BUS_ADRERR \ + || si -> si_code == BUS_UNKNOWN \ + || si -> si_code == SEGV_UNKNOWN \ + || si -> si_code == BUS_OBJERR) +# elif defined(SUNOS5SIGS) +# define CODE_OK (si -> si_code == SEGV_ACCERR) +# endif +# ifndef NO_GETCONTEXT +# include +# endif + STATIC void GC_write_fault_handler(int sig, siginfo_t *si, void *raw_sc) +# else +# define SIG_OK (exc_info -> ExceptionRecord -> ExceptionCode \ + == STATUS_ACCESS_VIOLATION) +# define CODE_OK (exc_info -> ExceptionRecord -> ExceptionInformation[0] \ + == 1) /* Write fault */ + STATIC LONG WINAPI GC_write_fault_handler( + struct _EXCEPTION_POINTERS *exc_info) +# endif /* MSWIN32 || MSWINCE */ + { +# if !defined(MSWIN32) && !defined(MSWINCE) + char *addr = (char *)si->si_addr; +# else + char * addr = (char *) (exc_info -> ExceptionRecord + -> ExceptionInformation[1]); +# endif + + if (SIG_OK && CODE_OK) { + struct hblk * h = (struct hblk *)((word)addr & ~(GC_page_size-1)); + GC_bool in_allocd_block; + size_t i; + +# ifdef CHECKSUMS + GC_record_fault(h); +# endif +# ifdef SUNOS5SIGS + /* Address is only within the correct physical page. */ + in_allocd_block = FALSE; + for (i = 0; i < divHBLKSZ(GC_page_size); i++) { + if (is_header_found_async(&h[i])) { + in_allocd_block = TRUE; + break; + } + } +# else + in_allocd_block = is_header_found_async(addr); +# endif + if (!in_allocd_block) { + /* FIXME - We should make sure that we invoke the */ + /* old handler with the appropriate calling */ + /* sequence, which often depends on SA_SIGINFO. */ + + /* Heap blocks now begin and end on page boundaries */ + SIG_HNDLR_PTR old_handler; + +# if defined(MSWIN32) || defined(MSWINCE) + old_handler = GC_old_segv_handler; +# else + GC_bool used_si; + +# if defined(FREEBSD) || defined(HURD) || defined(HPUX) + if (sig == SIGBUS) { + old_handler = GC_old_bus_handler; + used_si = GC_old_bus_handler_used_si; + } else +# endif + /* else */ { + old_handler = GC_old_segv_handler; + used_si = GC_old_segv_handler_used_si; + } +# endif + + if (old_handler == (SIG_HNDLR_PTR)SIG_DFL) { +# if !defined(MSWIN32) && !defined(MSWINCE) + ABORT_ARG1("Unexpected bus error or segmentation fault", + " at %p", (void *)addr); +# else + return(EXCEPTION_CONTINUE_SEARCH); +# endif + } else { + /* + * FIXME: This code should probably check if the + * old signal handler used the traditional style and + * if so call it using that style. + */ +# if defined(MSWIN32) || defined(MSWINCE) + return((*old_handler)(exc_info)); +# else + if (used_si) + ((SIG_HNDLR_PTR)old_handler) (sig, si, raw_sc); + else + /* FIXME: should pass nonstandard args as well. */ + ((PLAIN_HNDLR_PTR)old_handler) (sig); + return; +# endif + } + } + UNPROTECT(h, GC_page_size); + /* We need to make sure that no collection occurs between */ + /* the UNPROTECT and the setting of the dirty bit. Otherwise */ + /* a write by a third thread might go unnoticed. Reversing */ + /* the order is just as bad, since we would end up unprotecting */ + /* a page in a GC cycle during which it's not marked. */ + /* Currently we do this by disabling the thread stopping */ + /* signals while this handler is running. An alternative might */ + /* be to record the fact that we're about to unprotect, or */ + /* have just unprotected a page in the GC's thread structure, */ + /* and then to have the thread stopping code set the dirty */ + /* flag, if necessary. */ + for (i = 0; i < divHBLKSZ(GC_page_size); i++) { + word index = PHT_HASH(h+i); + + async_set_pht_entry_from_index(GC_dirty_pages, index); + } + /* The write may not take place before dirty bits are read. */ + /* But then we'll fault again ... */ +# if defined(MSWIN32) || defined(MSWINCE) + return(EXCEPTION_CONTINUE_EXECUTION); +# else + return; +# endif + } +# if defined(MSWIN32) || defined(MSWINCE) + return EXCEPTION_CONTINUE_SEARCH; +# else + ABORT_ARG1("Unexpected bus error or segmentation fault", + " at %p", (void *)addr); +# endif + } + +# ifdef GC_WIN32_THREADS + GC_INNER void GC_set_write_fault_handler(void) + { + SetUnhandledExceptionFilter(GC_write_fault_handler); + } +# endif +#endif /* !DARWIN */ + +/* We hold the allocation lock. We expect block h to be written */ +/* shortly. Ensure that all pages containing any part of the n hblks */ +/* starting at h are no longer protected. If is_ptrfree is false, also */ +/* ensure that they will subsequently appear to be dirty. Not allowed */ +/* to call GC_printf (and the friends) here, see Win32 GC_stop_world() */ +/* for the information. */ +GC_INNER void GC_remove_protection(struct hblk *h, word nblocks, + GC_bool is_ptrfree) +{ + struct hblk * h_trunc; /* Truncated to page boundary */ + struct hblk * h_end; /* Page boundary following block end */ + struct hblk * current; + +# if defined(GWW_VDB) + if (GC_GWW_AVAILABLE()) return; +# endif + if (!GC_incremental) return; + h_trunc = (struct hblk *)((word)h & ~(GC_page_size-1)); + h_end = (struct hblk *)(((word)(h + nblocks) + GC_page_size - 1) + & ~(GC_page_size - 1)); + if (h_end == h_trunc + 1 && + get_pht_entry_from_index(GC_dirty_pages, PHT_HASH(h_trunc))) { + /* already marked dirty, and hence unprotected. */ + return; + } + for (current = h_trunc; (word)current < (word)h_end; ++current) { + word index = PHT_HASH(current); + + if (!is_ptrfree || (word)current < (word)h + || (word)current >= (word)(h + nblocks)) { + async_set_pht_entry_from_index(GC_dirty_pages, index); + } + } + UNPROTECT(h_trunc, (ptr_t)h_end - (ptr_t)h_trunc); +} + +#ifdef USE_MUNMAP + /* MPROTECT_VDB cannot deal with address space holes (for now), */ + /* so if the collector is configured with both MPROTECT_VDB and */ + /* USE_MUNMAP then, as a work around, select only one of them */ + /* during GC_init or GC_enable_incremental. */ + GC_INNER GC_bool GC_dirty_init(void) + { + if (GC_unmap_threshold != 0) { + if (GETENV("GC_UNMAP_THRESHOLD") != NULL + || GETENV("GC_FORCE_UNMAP_ON_GCOLLECT") != NULL + || GC_has_unmapped_memory()) { + WARN("Can't maintain mprotect-based dirty bits" + " in case of unmapping\n", 0); + return FALSE; + } + GC_unmap_threshold = 0; /* in favor of incremental collection */ + WARN("Memory unmapping is disabled as incompatible" + " with MPROTECT_VDB\n", 0); + } + return GC_mprotect_dirty_init(); + } +#else +# define GC_mprotect_dirty_init GC_dirty_init +#endif /* !USE_MUNMAP */ + +#if !defined(DARWIN) + GC_INNER GC_bool GC_mprotect_dirty_init(void) + { +# if !defined(MSWIN32) && !defined(MSWINCE) + struct sigaction act, oldact; + act.sa_flags = SA_RESTART | SA_SIGINFO; + act.sa_sigaction = GC_write_fault_handler; + (void)sigemptyset(&act.sa_mask); +# if defined(THREADS) && !defined(GC_OPENBSD_UTHREADS) \ + && !defined(GC_WIN32_THREADS) && !defined(NACL) + /* Arrange to postpone the signal while we are in a write fault */ + /* handler. This effectively makes the handler atomic w.r.t. */ + /* stopping the world for GC. */ + (void)sigaddset(&act.sa_mask, GC_get_suspend_signal()); +# endif +# endif /* !MSWIN32 */ + GC_VERBOSE_LOG_PRINTF( + "Initializing mprotect virtual dirty bit implementation\n"); + if (GC_page_size % HBLKSIZE != 0) { + ABORT("Page size not multiple of HBLKSIZE"); + } +# if !defined(MSWIN32) && !defined(MSWINCE) + /* act.sa_restorer is deprecated and should not be initialized. */ +# if defined(GC_IRIX_THREADS) + sigaction(SIGSEGV, 0, &oldact); + sigaction(SIGSEGV, &act, 0); +# else + { + int res = sigaction(SIGSEGV, &act, &oldact); + if (res != 0) ABORT("Sigaction failed"); + } +# endif + if (oldact.sa_flags & SA_SIGINFO) { + GC_old_segv_handler = oldact.sa_sigaction; + GC_old_segv_handler_used_si = TRUE; + } else { + GC_old_segv_handler = (SIG_HNDLR_PTR)oldact.sa_handler; + GC_old_segv_handler_used_si = FALSE; + } + if (GC_old_segv_handler == (SIG_HNDLR_PTR)SIG_IGN) { + WARN("Previously ignored segmentation violation!?\n", 0); + GC_old_segv_handler = (SIG_HNDLR_PTR)SIG_DFL; + } + if (GC_old_segv_handler != (SIG_HNDLR_PTR)SIG_DFL) { + GC_VERBOSE_LOG_PRINTF("Replaced other SIGSEGV handler\n"); + } +# if defined(HPUX) || defined(LINUX) || defined(HURD) \ + || (defined(FREEBSD) && (defined(__GLIBC__) || defined(SUNOS5SIGS))) + sigaction(SIGBUS, &act, &oldact); + if ((oldact.sa_flags & SA_SIGINFO) != 0) { + GC_old_bus_handler = oldact.sa_sigaction; +# if !defined(LINUX) + GC_old_bus_handler_used_si = TRUE; +# endif + } else { + GC_old_bus_handler = (SIG_HNDLR_PTR)oldact.sa_handler; +# if !defined(LINUX) + GC_old_bus_handler_used_si = FALSE; +# endif + } + if (GC_old_bus_handler == (SIG_HNDLR_PTR)SIG_IGN) { + WARN("Previously ignored bus error!?\n", 0); +# if !defined(LINUX) + GC_old_bus_handler = (SIG_HNDLR_PTR)SIG_DFL; +# else + /* GC_old_bus_handler is not used by GC_write_fault_handler. */ +# endif + } else if (GC_old_bus_handler != (SIG_HNDLR_PTR)SIG_DFL) { + GC_VERBOSE_LOG_PRINTF("Replaced other SIGBUS handler\n"); + } +# endif /* HPUX || LINUX || HURD || (FREEBSD && SUNOS5SIGS) */ +# endif /* ! MS windows */ +# if defined(GWW_VDB) + if (GC_gww_dirty_init()) + return TRUE; +# endif +# if defined(MSWIN32) + GC_old_segv_handler = SetUnhandledExceptionFilter(GC_write_fault_handler); + if (GC_old_segv_handler != NULL) { + GC_COND_LOG_PRINTF("Replaced other UnhandledExceptionFilter\n"); + } else { + GC_old_segv_handler = SIG_DFL; + } +# elif defined(MSWINCE) + /* MPROTECT_VDB is unsupported for WinCE at present. */ + /* FIXME: implement it (if possible). */ +# endif +# if defined(CPPCHECK) && defined(ADDRESS_SANITIZER) + GC_noop1((word)&__asan_default_options); +# endif + return TRUE; + } +#endif /* !DARWIN */ + +GC_API int GC_CALL GC_incremental_protection_needs(void) +{ + GC_ASSERT(GC_is_initialized); + + if (GC_page_size == HBLKSIZE) { + return GC_PROTECTS_POINTER_HEAP; + } else { + return GC_PROTECTS_POINTER_HEAP | GC_PROTECTS_PTRFREE_HEAP; + } +} +#define HAVE_INCREMENTAL_PROTECTION_NEEDS + +#define IS_PTRFREE(hhdr) ((hhdr)->hb_descr == 0) +#define PAGE_ALIGNED(x) !((word)(x) & (GC_page_size - 1)) + +STATIC void GC_protect_heap(void) +{ + unsigned i; + GC_bool protect_all = + (0 != (GC_incremental_protection_needs() & GC_PROTECTS_PTRFREE_HEAP)); + + for (i = 0; i < GC_n_heap_sects; i++) { + ptr_t start = GC_heap_sects[i].hs_start; + size_t len = GC_heap_sects[i].hs_bytes; + + if (protect_all) { + PROTECT(start, len); + } else { + struct hblk * current; + struct hblk * current_start; /* Start of block to be protected. */ + struct hblk * limit; + + GC_ASSERT(PAGE_ALIGNED(len)); + GC_ASSERT(PAGE_ALIGNED(start)); + current_start = current = (struct hblk *)start; + limit = (struct hblk *)(start + len); + while ((word)current < (word)limit) { + hdr * hhdr; + word nhblks; + GC_bool is_ptrfree; + + GC_ASSERT(PAGE_ALIGNED(current)); + GET_HDR(current, hhdr); + if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + /* This can happen only if we're at the beginning of a */ + /* heap segment, and a block spans heap segments. */ + /* We will handle that block as part of the preceding */ + /* segment. */ + GC_ASSERT(current_start == current); + current_start = ++current; + continue; + } + if (HBLK_IS_FREE(hhdr)) { + GC_ASSERT(PAGE_ALIGNED(hhdr -> hb_sz)); + nhblks = divHBLKSZ(hhdr -> hb_sz); + is_ptrfree = TRUE; /* dirty on alloc */ + } else { + nhblks = OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz); + is_ptrfree = IS_PTRFREE(hhdr); + } + if (is_ptrfree) { + if ((word)current_start < (word)current) { + PROTECT(current_start, (ptr_t)current - (ptr_t)current_start); + } + current_start = (current += nhblks); + } else { + current += nhblks; + } + } + if ((word)current_start < (word)current) { + PROTECT(current_start, (ptr_t)current - (ptr_t)current_start); + } + } + } +} + +/* We assume that either the world is stopped or its OK to lose dirty */ +/* bits while this is happening (as in GC_enable_incremental). */ +GC_INNER void GC_read_dirty(GC_bool output_unneeded) +{ +# if defined(GWW_VDB) + if (GC_GWW_AVAILABLE()) { + GC_gww_read_dirty(output_unneeded); + return; + } +# endif + if (!output_unneeded) + BCOPY((word *)GC_dirty_pages, GC_grungy_pages, sizeof(GC_dirty_pages)); + BZERO((word *)GC_dirty_pages, (sizeof GC_dirty_pages)); + GC_protect_heap(); +} + +/* + * Acquiring the allocation lock here is dangerous, since this + * can be called from within GC_call_with_alloc_lock, and the cord + * package does so. On systems that allow nested lock acquisition, this + * happens to work. + */ + +/* We no longer wrap read by default, since that was causing too many */ +/* problems. It is preferred that the client instead avoids writing */ +/* to the write-protected heap with a system call. */ + +# ifdef CHECKSUMS + GC_INNER GC_bool GC_page_was_ever_dirty(struct hblk * h GC_ATTR_UNUSED) + { +# if defined(GWW_VDB) + if (GC_GWW_AVAILABLE()) + return GC_gww_page_was_ever_dirty(h); +# endif + return(TRUE); + } +# endif /* CHECKSUMS */ + +#endif /* MPROTECT_VDB */ + +#ifdef PROC_VDB +/* See DEFAULT_VDB for interface descriptions. */ + +/* This implementation assumes a Solaris 2.X like /proc */ +/* pseudo-file-system from which we can read page modified bits. This */ +/* facility is far from optimal (e.g. we would like to get the info for */ +/* only some of the address space), but it avoids intercepting system */ +/* calls. */ + +# include +# include +# include +# include +# include + +# ifdef GC_NO_SYS_FAULT_H + /* This exists only to check PROC_VDB code compilation (on Linux). */ +# define PG_MODIFIED 1 + struct prpageheader { + int dummy[2]; /* pr_tstamp */ + unsigned long pr_nmap; + unsigned long pr_npage; + }; + struct prasmap { + char *pr_vaddr; + size_t pr_npage; + char dummy1[64+8]; /* pr_mapname, pr_offset */ + unsigned pr_mflags; + unsigned pr_pagesize; + int dummy2[2]; + }; +# else +# include +# include +# endif + +# define INITIAL_BUF_SZ 16384 + STATIC size_t GC_proc_buf_size = INITIAL_BUF_SZ; + STATIC char *GC_proc_buf = NULL; + STATIC int GC_proc_fd = 0; + +GC_INNER GC_bool GC_dirty_init(void) +{ + char buf[40]; + + if (GC_bytes_allocd != 0 || GC_bytes_allocd_before_gc != 0) { + memset(GC_written_pages, 0xff, sizeof(page_hash_table)); + GC_VERBOSE_LOG_PRINTF( + "Allocated %lu bytes: all pages may have been written\n", + (unsigned long)(GC_bytes_allocd + GC_bytes_allocd_before_gc)); + } + + (void)snprintf(buf, sizeof(buf), "/proc/%ld/pagedata", (long)getpid()); + buf[sizeof(buf) - 1] = '\0'; + GC_proc_fd = open(buf, O_RDONLY); + if (GC_proc_fd < 0) { + WARN("/proc open failed; cannot enable GC incremental mode\n", 0); + return FALSE; + } + if (syscall(SYS_fcntl, GC_proc_fd, F_SETFD, FD_CLOEXEC) == -1) + WARN("Could not set FD_CLOEXEC for /proc\n", 0); + + GC_proc_buf = GC_scratch_alloc(GC_proc_buf_size); + if (GC_proc_buf == NULL) + ABORT("Insufficient space for /proc read"); + return TRUE; +} + +# define READ read + +GC_INNER void GC_read_dirty(GC_bool output_unneeded) +{ + int nmaps; + char * bufp = GC_proc_buf; + int i; + + BZERO(GC_grungy_pages, sizeof(GC_grungy_pages)); + if (READ(GC_proc_fd, bufp, GC_proc_buf_size) <= 0) { + /* Retry with larger buffer. */ + size_t new_size = 2 * GC_proc_buf_size; + char *new_buf; + + WARN("/proc read failed: GC_proc_buf_size = %" WARN_PRIdPTR "\n", + (signed_word)GC_proc_buf_size); + new_buf = GC_scratch_alloc(new_size); + if (new_buf != 0) { + GC_scratch_recycle_no_gww(bufp, GC_proc_buf_size); + GC_proc_buf = bufp = new_buf; + GC_proc_buf_size = new_size; + } + if (READ(GC_proc_fd, bufp, GC_proc_buf_size) <= 0) { + WARN("Insufficient space for /proc read\n", 0); + /* Punt: */ + if (!output_unneeded) + memset(GC_grungy_pages, 0xff, sizeof (page_hash_table)); + memset(GC_written_pages, 0xff, sizeof(page_hash_table)); + return; + } + } + + /* Copy dirty bits into GC_grungy_pages */ + nmaps = ((struct prpageheader *)bufp) -> pr_nmap; +# ifdef DEBUG_DIRTY_BITS + GC_log_printf("Proc VDB read: pr_nmap= %u, pr_npage= %lu\n", + nmaps, ((struct prpageheader *)bufp)->pr_npage); +# endif +# if defined(GC_NO_SYS_FAULT_H) && defined(CPPCHECK) + GC_noop1(((struct prpageheader *)bufp)->dummy[0]); +# endif + bufp += sizeof(struct prpageheader); + for (i = 0; i < nmaps; i++) { + struct prasmap * map = (struct prasmap *)bufp; + ptr_t vaddr = (ptr_t)(map -> pr_vaddr); + unsigned long npages = map -> pr_npage; + unsigned pagesize = map -> pr_pagesize; + ptr_t limit; + +# if defined(GC_NO_SYS_FAULT_H) && defined(CPPCHECK) + GC_noop1(map->dummy1[0] + map->dummy2[0]); +# endif +# ifdef DEBUG_DIRTY_BITS + GC_log_printf( + "pr_vaddr= %p, npage= %lu, mflags= 0x%x, pagesize= 0x%x\n", + (void *)vaddr, npages, map->pr_mflags, pagesize); +# endif + + bufp += sizeof(struct prasmap); + limit = vaddr + pagesize * npages; + for (; (word)vaddr < (word)limit; vaddr += pagesize) { + if ((*bufp++) & PG_MODIFIED) { + struct hblk * h; + ptr_t next_vaddr = vaddr + pagesize; +# ifdef DEBUG_DIRTY_BITS + GC_log_printf("dirty page at: %p\n", (void *)vaddr); +# endif + for (h = (struct hblk *)vaddr; + (word)h < (word)next_vaddr; h++) { + word index = PHT_HASH(h); + + set_pht_entry_from_index(GC_grungy_pages, index); + } + } + } + bufp = (char *)(((word)bufp + (sizeof(long)-1)) + & ~(word)(sizeof(long)-1)); + } +# ifdef DEBUG_DIRTY_BITS + GC_log_printf("Proc VDB read done\n"); +# endif + + /* Update GC_written_pages (even if output_unneeded). */ + GC_or_pages(GC_written_pages, GC_grungy_pages); +} + +# undef READ +#endif /* PROC_VDB */ + +#ifdef PCR_VDB + +# include "vd/PCR_VD.h" + +# define NPAGES (32*1024) /* 128 MB */ + +PCR_VD_DB GC_grungy_bits[NPAGES]; + +STATIC ptr_t GC_vd_base = NULL; + /* Address corresponding to GC_grungy_bits[0] */ + /* HBLKSIZE aligned. */ + +GC_INNER GC_bool GC_dirty_init(void) +{ + /* For the time being, we assume the heap generally grows up */ + GC_vd_base = GC_heap_sects[0].hs_start; + if (GC_vd_base == 0) { + ABORT("Bad initial heap segment"); + } + if (PCR_VD_Start(HBLKSIZE, GC_vd_base, NPAGES*HBLKSIZE) + != PCR_ERes_okay) { + ABORT("Dirty bit initialization failed"); + } + return TRUE; +} + +GC_INNER void GC_read_dirty(GC_bool output_unneeded GC_ATTR_UNUSED) +{ + /* lazily enable dirty bits on newly added heap sects */ + { + static int onhs = 0; + int nhs = GC_n_heap_sects; + for(; onhs < nhs; onhs++) { + PCR_VD_WriteProtectEnable( + GC_heap_sects[onhs].hs_start, + GC_heap_sects[onhs].hs_bytes ); + } + } + + if (PCR_VD_Clear(GC_vd_base, NPAGES*HBLKSIZE, GC_grungy_bits) + != PCR_ERes_okay) { + ABORT("Dirty bit read failed"); + } +} + +GC_INNER GC_bool GC_page_was_dirty(struct hblk *h) +{ + if ((word)h < (word)GC_vd_base + || (word)h >= (word)(GC_vd_base + NPAGES*HBLKSIZE)) { + return(TRUE); + } + return(GC_grungy_bits[h - (struct hblk *)GC_vd_base] & PCR_VD_DB_dirtyBit); +} + +GC_INNER void GC_remove_protection(struct hblk *h, word nblocks, + GC_bool is_ptrfree GC_ATTR_UNUSED) +{ + PCR_VD_WriteProtectDisable(h, nblocks*HBLKSIZE); + PCR_VD_WriteProtectEnable(h, nblocks*HBLKSIZE); +} + +#endif /* PCR_VDB */ + +#if defined(MPROTECT_VDB) && defined(DARWIN) +/* The following sources were used as a "reference" for this exception + handling code: + 1. Apple's mach/xnu documentation + 2. Timothy J. Wood's "Mach Exception Handlers 101" post to the + omnigroup's macosx-dev list. + www.omnigroup.com/mailman/archive/macosx-dev/2000-June/014178.html + 3. macosx-nat.c from Apple's GDB source code. +*/ + +/* The bug that caused all this trouble should now be fixed. This should + eventually be removed if all goes well. */ + +/* #define BROKEN_EXCEPTION_HANDLING */ + +#include +#include +#include +#include +#include + +EXTERN_C_BEGIN + +/* Some of the following prototypes are missing in any header, although */ +/* they are documented. Some are in mach/exc.h file. */ +extern boolean_t +exc_server(mach_msg_header_t *, mach_msg_header_t *); + +extern kern_return_t +exception_raise(mach_port_t, mach_port_t, mach_port_t, exception_type_t, + exception_data_t, mach_msg_type_number_t); + +extern kern_return_t +exception_raise_state(mach_port_t, mach_port_t, mach_port_t, exception_type_t, + exception_data_t, mach_msg_type_number_t, + thread_state_flavor_t*, thread_state_t, + mach_msg_type_number_t, thread_state_t, + mach_msg_type_number_t*); + +extern kern_return_t +exception_raise_state_identity(mach_port_t, mach_port_t, mach_port_t, + exception_type_t, exception_data_t, + mach_msg_type_number_t, thread_state_flavor_t*, + thread_state_t, mach_msg_type_number_t, + thread_state_t, mach_msg_type_number_t*); + +GC_API_OSCALL kern_return_t +catch_exception_raise(mach_port_t exception_port, mach_port_t thread, + mach_port_t task, exception_type_t exception, + exception_data_t code, + mach_msg_type_number_t code_count); + +GC_API_OSCALL kern_return_t +catch_exception_raise_state(mach_port_name_t exception_port, + int exception, exception_data_t code, + mach_msg_type_number_t codeCnt, int flavor, + thread_state_t old_state, int old_stateCnt, + thread_state_t new_state, int new_stateCnt); + +GC_API_OSCALL kern_return_t +catch_exception_raise_state_identity(mach_port_name_t exception_port, + mach_port_t thread, mach_port_t task, int exception, + exception_data_t code, mach_msg_type_number_t codeCnt, + int flavor, thread_state_t old_state, int old_stateCnt, + thread_state_t new_state, int new_stateCnt); + +EXTERN_C_END + +/* These should never be called, but just in case... */ +GC_API_OSCALL kern_return_t +catch_exception_raise_state(mach_port_name_t exception_port GC_ATTR_UNUSED, + int exception GC_ATTR_UNUSED, exception_data_t code GC_ATTR_UNUSED, + mach_msg_type_number_t codeCnt GC_ATTR_UNUSED, int flavor GC_ATTR_UNUSED, + thread_state_t old_state GC_ATTR_UNUSED, int old_stateCnt GC_ATTR_UNUSED, + thread_state_t new_state GC_ATTR_UNUSED, int new_stateCnt GC_ATTR_UNUSED) +{ + ABORT_RET("Unexpected catch_exception_raise_state invocation"); + return(KERN_INVALID_ARGUMENT); +} + +GC_API_OSCALL kern_return_t +catch_exception_raise_state_identity( + mach_port_name_t exception_port GC_ATTR_UNUSED, + mach_port_t thread GC_ATTR_UNUSED, mach_port_t task GC_ATTR_UNUSED, + int exception GC_ATTR_UNUSED, exception_data_t code GC_ATTR_UNUSED, + mach_msg_type_number_t codeCnt GC_ATTR_UNUSED, int flavor GC_ATTR_UNUSED, + thread_state_t old_state GC_ATTR_UNUSED, int old_stateCnt GC_ATTR_UNUSED, + thread_state_t new_state GC_ATTR_UNUSED, int new_stateCnt GC_ATTR_UNUSED) +{ + ABORT_RET("Unexpected catch_exception_raise_state_identity invocation"); + return(KERN_INVALID_ARGUMENT); +} + +#define MAX_EXCEPTION_PORTS 16 + +static struct { + mach_msg_type_number_t count; + exception_mask_t masks[MAX_EXCEPTION_PORTS]; + exception_handler_t ports[MAX_EXCEPTION_PORTS]; + exception_behavior_t behaviors[MAX_EXCEPTION_PORTS]; + thread_state_flavor_t flavors[MAX_EXCEPTION_PORTS]; +} GC_old_exc_ports; + +STATIC struct ports_s { + void (*volatile os_callback[3])(void); + mach_port_t exception; +# if defined(THREADS) + mach_port_t reply; +# endif +} GC_ports = { + { + /* This is to prevent stripping these routines as dead. */ + (void (*)(void))catch_exception_raise, + (void (*)(void))catch_exception_raise_state, + (void (*)(void))catch_exception_raise_state_identity + }, +# ifdef THREADS + 0, /* for 'exception' */ +# endif + 0 +}; + +typedef struct { + mach_msg_header_t head; +} GC_msg_t; + +typedef enum { + GC_MP_NORMAL, + GC_MP_DISCARDING, + GC_MP_STOPPED +} GC_mprotect_state_t; + +#ifdef THREADS + /* FIXME: 1 and 2 seem to be safe to use in the msgh_id field, but it */ + /* is not documented. Use the source and see if they should be OK. */ +# define ID_STOP 1 +# define ID_RESUME 2 + + /* This value is only used on the reply port. */ +# define ID_ACK 3 + + STATIC GC_mprotect_state_t GC_mprotect_state = GC_MP_NORMAL; + + /* The following should ONLY be called when the world is stopped. */ + STATIC void GC_mprotect_thread_notify(mach_msg_id_t id) + { + struct buf_s { + GC_msg_t msg; + mach_msg_trailer_t trailer; + } buf; + mach_msg_return_t r; + + /* remote, local */ + buf.msg.head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); + buf.msg.head.msgh_size = sizeof(buf.msg); + buf.msg.head.msgh_remote_port = GC_ports.exception; + buf.msg.head.msgh_local_port = MACH_PORT_NULL; + buf.msg.head.msgh_id = id; + + r = mach_msg(&buf.msg.head, MACH_SEND_MSG | MACH_RCV_MSG | MACH_RCV_LARGE, + sizeof(buf.msg), sizeof(buf), GC_ports.reply, + MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + if (r != MACH_MSG_SUCCESS) + ABORT("mach_msg failed in GC_mprotect_thread_notify"); + if (buf.msg.head.msgh_id != ID_ACK) + ABORT("Invalid ack in GC_mprotect_thread_notify"); + } + + /* Should only be called by the mprotect thread */ + STATIC void GC_mprotect_thread_reply(void) + { + GC_msg_t msg; + mach_msg_return_t r; + /* remote, local */ + + msg.head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); + msg.head.msgh_size = sizeof(msg); + msg.head.msgh_remote_port = GC_ports.reply; + msg.head.msgh_local_port = MACH_PORT_NULL; + msg.head.msgh_id = ID_ACK; + + r = mach_msg(&msg.head, MACH_SEND_MSG, sizeof(msg), 0, MACH_PORT_NULL, + MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + if (r != MACH_MSG_SUCCESS) + ABORT("mach_msg failed in GC_mprotect_thread_reply"); + } + + GC_INNER void GC_mprotect_stop(void) + { + GC_mprotect_thread_notify(ID_STOP); + } + + GC_INNER void GC_mprotect_resume(void) + { + GC_mprotect_thread_notify(ID_RESUME); + } + +#else + /* The compiler should optimize away any GC_mprotect_state computations */ +# define GC_mprotect_state GC_MP_NORMAL +#endif /* !THREADS */ + +STATIC void *GC_mprotect_thread(void *arg) +{ + mach_msg_return_t r; + /* These two structures contain some private kernel data. We don't */ + /* need to access any of it so we don't bother defining a proper */ + /* struct. The correct definitions are in the xnu source code. */ + struct reply_s { + mach_msg_header_t head; + char data[256]; + } reply; + struct msg_s { + mach_msg_header_t head; + mach_msg_body_t msgh_body; + char data[1024]; + } msg; + mach_msg_id_t id; + + if ((word)arg == (word)-1) return 0; /* to make compiler happy */ +# if defined(CPPCHECK) + reply.data[0] = 0; /* to prevent "field unused" warnings */ + msg.data[0] = 0; +# endif + +# if defined(THREADS) && !defined(GC_NO_THREADS_DISCOVERY) + GC_darwin_register_mach_handler_thread(mach_thread_self()); +# endif + + for(;;) { + r = mach_msg(&msg.head, MACH_RCV_MSG | MACH_RCV_LARGE | + (GC_mprotect_state == GC_MP_DISCARDING ? MACH_RCV_TIMEOUT : 0), + 0, sizeof(msg), GC_ports.exception, + GC_mprotect_state == GC_MP_DISCARDING ? 0 + : MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + id = r == MACH_MSG_SUCCESS ? msg.head.msgh_id : -1; + +# if defined(THREADS) + if(GC_mprotect_state == GC_MP_DISCARDING) { + if(r == MACH_RCV_TIMED_OUT) { + GC_mprotect_state = GC_MP_STOPPED; + GC_mprotect_thread_reply(); + continue; + } + if(r == MACH_MSG_SUCCESS && (id == ID_STOP || id == ID_RESUME)) + ABORT("Out of order mprotect thread request"); + } +# endif /* THREADS */ + + if (r != MACH_MSG_SUCCESS) { + ABORT_ARG2("mach_msg failed", + ": errcode= %d (%s)", (int)r, mach_error_string(r)); + } + + switch(id) { +# if defined(THREADS) + case ID_STOP: + if(GC_mprotect_state != GC_MP_NORMAL) + ABORT("Called mprotect_stop when state wasn't normal"); + GC_mprotect_state = GC_MP_DISCARDING; + break; + case ID_RESUME: + if(GC_mprotect_state != GC_MP_STOPPED) + ABORT("Called mprotect_resume when state wasn't stopped"); + GC_mprotect_state = GC_MP_NORMAL; + GC_mprotect_thread_reply(); + break; +# endif /* THREADS */ + default: + /* Handle the message (calls catch_exception_raise) */ + if(!exc_server(&msg.head, &reply.head)) + ABORT("exc_server failed"); + /* Send the reply */ + r = mach_msg(&reply.head, MACH_SEND_MSG, reply.head.msgh_size, 0, + MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + if(r != MACH_MSG_SUCCESS) { + /* This will fail if the thread dies, but the thread */ + /* shouldn't die... */ +# ifdef BROKEN_EXCEPTION_HANDLING + GC_err_printf("mach_msg failed with %d %s while sending " + "exc reply\n", (int)r, mach_error_string(r)); +# else + ABORT("mach_msg failed while sending exception reply"); +# endif + } + } /* switch */ + } /* for(;;) */ +} + +/* All this SIGBUS code shouldn't be necessary. All protection faults should + be going through the mach exception handler. However, it seems a SIGBUS is + occasionally sent for some unknown reason. Even more odd, it seems to be + meaningless and safe to ignore. */ +#ifdef BROKEN_EXCEPTION_HANDLING + + /* Updates to this aren't atomic, but the SIGBUS'es seem pretty rare. */ + /* Even if this doesn't get updated property, it isn't really a problem. */ + STATIC int GC_sigbus_count = 0; + + STATIC void GC_darwin_sigbus(int num, siginfo_t *sip, void *context) + { + if (num != SIGBUS) + ABORT("Got a non-sigbus signal in the sigbus handler"); + + /* Ugh... some seem safe to ignore, but too many in a row probably means + trouble. GC_sigbus_count is reset for each mach exception that is + handled */ + if (GC_sigbus_count >= 8) { + ABORT("Got more than 8 SIGBUSs in a row!"); + } else { + GC_sigbus_count++; + WARN("Ignoring SIGBUS\n", 0); + } + } +#endif /* BROKEN_EXCEPTION_HANDLING */ + +GC_INNER GC_bool GC_mprotect_dirty_init(void) +{ + kern_return_t r; + mach_port_t me; + pthread_t thread; + pthread_attr_t attr; + exception_mask_t mask; + +# ifdef CAN_HANDLE_FORK + if (GC_handle_fork) { + /* To both support GC incremental mode and GC functions usage in */ + /* the forked child, pthread_atfork should be used to install */ + /* handlers that switch off GC_incremental in the child */ + /* gracefully (unprotecting all pages and clearing */ + /* GC_mach_handler_thread). For now, we just disable incremental */ + /* mode if fork() handling is requested by the client. */ + WARN("Can't turn on GC incremental mode as fork()" + " handling requested\n", 0); + return FALSE; + } +# endif + + GC_VERBOSE_LOG_PRINTF("Initializing mach/darwin mprotect" + " virtual dirty bit implementation\n"); +# ifdef BROKEN_EXCEPTION_HANDLING + WARN("Enabling workarounds for various darwin " + "exception handling bugs\n", 0); +# endif + if (GC_page_size % HBLKSIZE != 0) { + ABORT("Page size not multiple of HBLKSIZE"); + } + + GC_task_self = me = mach_task_self(); + + r = mach_port_allocate(me, MACH_PORT_RIGHT_RECEIVE, &GC_ports.exception); + /* TODO: WARN and return FALSE in case of a failure. */ + if (r != KERN_SUCCESS) + ABORT("mach_port_allocate failed (exception port)"); + + r = mach_port_insert_right(me, GC_ports.exception, GC_ports.exception, + MACH_MSG_TYPE_MAKE_SEND); + if (r != KERN_SUCCESS) + ABORT("mach_port_insert_right failed (exception port)"); + +# if defined(THREADS) + r = mach_port_allocate(me, MACH_PORT_RIGHT_RECEIVE, &GC_ports.reply); + if(r != KERN_SUCCESS) + ABORT("mach_port_allocate failed (reply port)"); +# endif + + /* The exceptions we want to catch */ + mask = EXC_MASK_BAD_ACCESS; + + r = task_get_exception_ports(me, mask, GC_old_exc_ports.masks, + &GC_old_exc_ports.count, GC_old_exc_ports.ports, + GC_old_exc_ports.behaviors, + GC_old_exc_ports.flavors); + if (r != KERN_SUCCESS) + ABORT("task_get_exception_ports failed"); + + r = task_set_exception_ports(me, mask, GC_ports.exception, EXCEPTION_DEFAULT, + GC_MACH_THREAD_STATE); + if (r != KERN_SUCCESS) + ABORT("task_set_exception_ports failed"); + if (pthread_attr_init(&attr) != 0) + ABORT("pthread_attr_init failed"); + if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0) + ABORT("pthread_attr_setdetachedstate failed"); + +# undef pthread_create + /* This will call the real pthread function, not our wrapper */ + if (pthread_create(&thread, &attr, GC_mprotect_thread, NULL) != 0) + ABORT("pthread_create failed"); + (void)pthread_attr_destroy(&attr); + + /* Setup the sigbus handler for ignoring the meaningless SIGBUSs */ +# ifdef BROKEN_EXCEPTION_HANDLING + { + struct sigaction sa, oldsa; + sa.sa_handler = (SIG_HNDLR_PTR)GC_darwin_sigbus; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART|SA_SIGINFO; + /* sa.sa_restorer is deprecated and should not be initialized. */ + if (sigaction(SIGBUS, &sa, &oldsa) < 0) + ABORT("sigaction failed"); + if ((SIG_HNDLR_PTR)oldsa.sa_handler != SIG_DFL) { + GC_VERBOSE_LOG_PRINTF("Replaced other SIGBUS handler\n"); + } + } +# endif /* BROKEN_EXCEPTION_HANDLING */ + return TRUE; +} + +/* The source code for Apple's GDB was used as a reference for the */ +/* exception forwarding code. This code is similar to be GDB code only */ +/* because there is only one way to do it. */ +STATIC kern_return_t GC_forward_exception(mach_port_t thread, mach_port_t task, + exception_type_t exception, + exception_data_t data, + mach_msg_type_number_t data_count) +{ + unsigned int i; + kern_return_t r; + mach_port_t port; + exception_behavior_t behavior; + thread_state_flavor_t flavor; + + thread_state_data_t thread_state; + mach_msg_type_number_t thread_state_count = THREAD_STATE_MAX; + + for (i=0; i < GC_old_exc_ports.count; i++) + if (GC_old_exc_ports.masks[i] & (1 << exception)) + break; + if (i == GC_old_exc_ports.count) + ABORT("No handler for exception!"); + + port = GC_old_exc_ports.ports[i]; + behavior = GC_old_exc_ports.behaviors[i]; + flavor = GC_old_exc_ports.flavors[i]; + + if (behavior == EXCEPTION_STATE || behavior == EXCEPTION_STATE_IDENTITY) { + r = thread_get_state(thread, flavor, thread_state, &thread_state_count); + if(r != KERN_SUCCESS) + ABORT("thread_get_state failed in forward_exception"); + } + + switch(behavior) { + case EXCEPTION_STATE: + r = exception_raise_state(port, thread, task, exception, data, data_count, + &flavor, thread_state, thread_state_count, + thread_state, &thread_state_count); + break; + case EXCEPTION_STATE_IDENTITY: + r = exception_raise_state_identity(port, thread, task, exception, data, + data_count, &flavor, thread_state, + thread_state_count, thread_state, + &thread_state_count); + break; + /* case EXCEPTION_DEFAULT: */ /* default signal handlers */ + default: /* user-supplied signal handlers */ + r = exception_raise(port, thread, task, exception, data, data_count); + } + + if (behavior == EXCEPTION_STATE || behavior == EXCEPTION_STATE_IDENTITY) { + r = thread_set_state(thread, flavor, thread_state, thread_state_count); + if (r != KERN_SUCCESS) + ABORT("thread_set_state failed in forward_exception"); + } + return r; +} + +#define FWD() GC_forward_exception(thread, task, exception, code, code_count) + +#ifdef ARM32 +# define DARWIN_EXC_STATE ARM_EXCEPTION_STATE +# define DARWIN_EXC_STATE_COUNT ARM_EXCEPTION_STATE_COUNT +# define DARWIN_EXC_STATE_T arm_exception_state_t +# define DARWIN_EXC_STATE_DAR THREAD_FLD_NAME(far) +#elif defined(AARCH64) +# define DARWIN_EXC_STATE ARM_EXCEPTION_STATE64 +# define DARWIN_EXC_STATE_COUNT ARM_EXCEPTION_STATE64_COUNT +# define DARWIN_EXC_STATE_T arm_exception_state64_t +# define DARWIN_EXC_STATE_DAR THREAD_FLD_NAME(far) +#elif defined(POWERPC) +# if CPP_WORDSZ == 32 +# define DARWIN_EXC_STATE PPC_EXCEPTION_STATE +# define DARWIN_EXC_STATE_COUNT PPC_EXCEPTION_STATE_COUNT +# define DARWIN_EXC_STATE_T ppc_exception_state_t +# else +# define DARWIN_EXC_STATE PPC_EXCEPTION_STATE64 +# define DARWIN_EXC_STATE_COUNT PPC_EXCEPTION_STATE64_COUNT +# define DARWIN_EXC_STATE_T ppc_exception_state64_t +# endif +# define DARWIN_EXC_STATE_DAR THREAD_FLD_NAME(dar) +#elif defined(I386) || defined(X86_64) +# if CPP_WORDSZ == 32 +# if defined(i386_EXCEPTION_STATE_COUNT) \ + && !defined(x86_EXCEPTION_STATE32_COUNT) + /* Use old naming convention for 32-bit x86. */ +# define DARWIN_EXC_STATE i386_EXCEPTION_STATE +# define DARWIN_EXC_STATE_COUNT i386_EXCEPTION_STATE_COUNT +# define DARWIN_EXC_STATE_T i386_exception_state_t +# else +# define DARWIN_EXC_STATE x86_EXCEPTION_STATE32 +# define DARWIN_EXC_STATE_COUNT x86_EXCEPTION_STATE32_COUNT +# define DARWIN_EXC_STATE_T x86_exception_state32_t +# endif +# else +# define DARWIN_EXC_STATE x86_EXCEPTION_STATE64 +# define DARWIN_EXC_STATE_COUNT x86_EXCEPTION_STATE64_COUNT +# define DARWIN_EXC_STATE_T x86_exception_state64_t +# endif +# define DARWIN_EXC_STATE_DAR THREAD_FLD_NAME(faultvaddr) +#elif !defined(CPPCHECK) +# error FIXME for non-arm/ppc/x86 darwin +#endif + +/* This violates the namespace rules but there isn't anything that can */ +/* be done about it. The exception handling stuff is hard coded to */ +/* call this. catch_exception_raise, catch_exception_raise_state and */ +/* and catch_exception_raise_state_identity are called from OS. */ +GC_API_OSCALL kern_return_t +catch_exception_raise(mach_port_t exception_port GC_ATTR_UNUSED, + mach_port_t thread, mach_port_t task GC_ATTR_UNUSED, + exception_type_t exception, exception_data_t code, + mach_msg_type_number_t code_count GC_ATTR_UNUSED) +{ + kern_return_t r; + char *addr; + thread_state_flavor_t flavor = DARWIN_EXC_STATE; + mach_msg_type_number_t exc_state_count = DARWIN_EXC_STATE_COUNT; + DARWIN_EXC_STATE_T exc_state; + + if (exception != EXC_BAD_ACCESS || code[0] != KERN_PROTECTION_FAILURE) { +# ifdef DEBUG_EXCEPTION_HANDLING + /* We aren't interested, pass it on to the old handler */ + GC_log_printf("Exception: 0x%x Code: 0x%x 0x%x in catch...\n", + exception, code_count > 0 ? code[0] : -1, + code_count > 1 ? code[1] : -1); +# endif + return FWD(); + } + + r = thread_get_state(thread, flavor, (natural_t*)&exc_state, + &exc_state_count); + if(r != KERN_SUCCESS) { + /* The thread is supposed to be suspended while the exception */ + /* handler is called. This shouldn't fail. */ +# ifdef BROKEN_EXCEPTION_HANDLING + GC_err_printf("thread_get_state failed in catch_exception_raise\n"); + return KERN_SUCCESS; +# else + ABORT("thread_get_state failed in catch_exception_raise"); +# endif + } + + /* This is the address that caused the fault */ + addr = (char*) exc_state.DARWIN_EXC_STATE_DAR; + if (!is_header_found_async(addr)) { + /* Ugh... just like the SIGBUS problem above, it seems we get */ + /* a bogus KERN_PROTECTION_FAILURE every once and a while. We wait */ + /* till we get a bunch in a row before doing anything about it. */ + /* If a "real" fault ever occurs it'll just keep faulting over and */ + /* over and we'll hit the limit pretty quickly. */ +# ifdef BROKEN_EXCEPTION_HANDLING + static char *last_fault; + static int last_fault_count; + + if(addr != last_fault) { + last_fault = addr; + last_fault_count = 0; + } + if(++last_fault_count < 32) { + if(last_fault_count == 1) + WARN("Ignoring KERN_PROTECTION_FAILURE at %p\n", addr); + return KERN_SUCCESS; + } + + GC_err_printf("Unexpected KERN_PROTECTION_FAILURE at %p; aborting...\n", + (void *)addr); + /* Can't pass it along to the signal handler because that is */ + /* ignoring SIGBUS signals. We also shouldn't call ABORT here as */ + /* signals don't always work too well from the exception handler. */ + EXIT(); +# else /* BROKEN_EXCEPTION_HANDLING */ + /* Pass it along to the next exception handler + (which should call SIGBUS/SIGSEGV) */ + return FWD(); +# endif /* !BROKEN_EXCEPTION_HANDLING */ + } + +# ifdef BROKEN_EXCEPTION_HANDLING + /* Reset the number of consecutive SIGBUSs */ + GC_sigbus_count = 0; +# endif + + if (GC_mprotect_state == GC_MP_NORMAL) { /* common case */ + struct hblk * h = (struct hblk*)((word)addr & ~(GC_page_size-1)); + size_t i; + + UNPROTECT(h, GC_page_size); + for (i = 0; i < divHBLKSZ(GC_page_size); i++) { + word index = PHT_HASH(h+i); + async_set_pht_entry_from_index(GC_dirty_pages, index); + } + } else if (GC_mprotect_state == GC_MP_DISCARDING) { + /* Lie to the thread for now. No sense UNPROTECT()ing the memory + when we're just going to PROTECT() it again later. The thread + will just fault again once it resumes */ + } else { + /* Shouldn't happen, i don't think */ + GC_err_printf("KERN_PROTECTION_FAILURE while world is stopped\n"); + return FWD(); + } + return KERN_SUCCESS; +} +#undef FWD + +#ifndef NO_DESC_CATCH_EXCEPTION_RAISE + /* These symbols should have REFERENCED_DYNAMICALLY (0x10) bit set to */ + /* let strip know they are not to be stripped. */ + __asm__(".desc _catch_exception_raise, 0x10"); + __asm__(".desc _catch_exception_raise_state, 0x10"); + __asm__(".desc _catch_exception_raise_state_identity, 0x10"); +#endif + +#endif /* DARWIN && MPROTECT_VDB */ + +#ifndef HAVE_INCREMENTAL_PROTECTION_NEEDS + GC_API int GC_CALL GC_incremental_protection_needs(void) + { + return GC_PROTECTS_NONE; + } +#endif /* !HAVE_INCREMENTAL_PROTECTION_NEEDS */ + +#ifdef ECOS + /* Undo sbrk() redirection. */ +# undef sbrk +#endif + +/* If value is non-zero then allocate executable memory. */ +GC_API void GC_CALL GC_set_pages_executable(int value) +{ + GC_ASSERT(!GC_is_initialized); + /* Even if IGNORE_PAGES_EXECUTABLE is defined, GC_pages_executable is */ + /* touched here to prevent a compiler warning. */ + GC_pages_executable = (GC_bool)(value != 0); +} + +/* Returns non-zero if the GC-allocated memory is executable. */ +/* GC_get_pages_executable is defined after all the places */ +/* where GC_get_pages_executable is undefined. */ +GC_API int GC_CALL GC_get_pages_executable(void) +{ +# ifdef IGNORE_PAGES_EXECUTABLE + return 1; /* Always allocate executable memory. */ +# else + return (int)GC_pages_executable; +# endif +} + +/* Call stack save code for debugging. Should probably be in */ +/* mach_dep.c, but that requires reorganization. */ + +/* I suspect the following works for most X86 *nix variants, so */ +/* long as the frame pointer is explicitly stored. In the case of gcc, */ +/* compiler flags (e.g. -fomit-frame-pointer) determine whether it is. */ +#if defined(I386) && defined(LINUX) && defined(SAVE_CALL_CHAIN) +# include + + struct frame { + struct frame *fr_savfp; + long fr_savpc; +# if NARGS > 0 + long fr_arg[NARGS]; /* All the arguments go here. */ +# endif + }; +#endif + +#if defined(SPARC) +# if defined(LINUX) +# include + + struct frame { + long fr_local[8]; + long fr_arg[6]; + struct frame *fr_savfp; + long fr_savpc; +# ifndef __arch64__ + char *fr_stret; +# endif + long fr_argd[6]; + long fr_argx[0]; + }; +# elif defined (DRSNX) +# include +# elif defined(OPENBSD) +# include +# elif defined(FREEBSD) || defined(NETBSD) +# include +# else +# include +# endif +# if NARGS > 6 +# error We only know how to get the first 6 arguments +# endif +#endif /* SPARC */ + +#ifdef NEED_CALLINFO +/* Fill in the pc and argument information for up to NFRAMES of my */ +/* callers. Ignore my frame and my callers frame. */ + +#ifdef LINUX +# include +#endif + +#endif /* NEED_CALLINFO */ + +#if defined(GC_HAVE_BUILTIN_BACKTRACE) +# ifdef _MSC_VER +# include "private/msvc_dbg.h" +# else +# include +# endif +#endif + +#ifdef SAVE_CALL_CHAIN + +#if NARGS == 0 && NFRAMES % 2 == 0 /* No padding */ \ + && defined(GC_HAVE_BUILTIN_BACKTRACE) + +#ifdef REDIRECT_MALLOC + /* Deal with possible malloc calls in backtrace by omitting */ + /* the infinitely recursing backtrace. */ +# ifdef THREADS + __thread /* If your compiler doesn't understand this */ + /* you could use something like pthread_getspecific. */ +# endif + GC_bool GC_in_save_callers = FALSE; +#endif + +GC_INNER void GC_save_callers(struct callinfo info[NFRAMES]) +{ + void * tmp_info[NFRAMES + 1]; + int npcs, i; +# define IGNORE_FRAMES 1 + + /* We retrieve NFRAMES+1 pc values, but discard the first, since it */ + /* points to our own frame. */ +# ifdef REDIRECT_MALLOC + if (GC_in_save_callers) { + info[0].ci_pc = (word)(&GC_save_callers); + for (i = 1; i < NFRAMES; ++i) info[i].ci_pc = 0; + return; + } + GC_in_save_callers = TRUE; +# endif + + GC_ASSERT(I_HOLD_LOCK()); + /* backtrace may call dl_iterate_phdr which is also */ + /* used by GC_register_dynamic_libraries, and */ + /* dl_iterate_phdr is not guaranteed to be reentrant. */ + + GC_STATIC_ASSERT(sizeof(struct callinfo) == sizeof(void *)); + npcs = backtrace((void **)tmp_info, NFRAMES + IGNORE_FRAMES); + if (npcs > IGNORE_FRAMES) + BCOPY(&tmp_info[IGNORE_FRAMES], info, + (npcs - IGNORE_FRAMES) * sizeof(void *)); + for (i = npcs - IGNORE_FRAMES; i < NFRAMES; ++i) info[i].ci_pc = 0; +# ifdef REDIRECT_MALLOC + GC_in_save_callers = FALSE; +# endif +} + +#else /* No builtin backtrace; do it ourselves */ + +#if (defined(OPENBSD) || defined(NETBSD) || defined(FREEBSD)) && defined(SPARC) +# define FR_SAVFP fr_fp +# define FR_SAVPC fr_pc +#else +# define FR_SAVFP fr_savfp +# define FR_SAVPC fr_savpc +#endif + +#if defined(SPARC) && (defined(__arch64__) || defined(__sparcv9)) +# define BIAS 2047 +#else +# define BIAS 0 +#endif + +GC_INNER void GC_save_callers(struct callinfo info[NFRAMES]) +{ + struct frame *frame; + struct frame *fp; + int nframes = 0; +# ifdef I386 + /* We assume this is turned on only with gcc as the compiler. */ + asm("movl %%ebp,%0" : "=r"(frame)); + fp = frame; +# else + frame = (struct frame *)GC_save_regs_in_stack(); + fp = (struct frame *)((long) frame -> FR_SAVFP + BIAS); +#endif + + for (; !((word)fp HOTTER_THAN (word)frame) + && !((word)GC_stackbottom HOTTER_THAN (word)fp) + && nframes < NFRAMES; + fp = (struct frame *)((long) fp -> FR_SAVFP + BIAS), nframes++) { +# if NARGS > 0 + int i; +# endif + + info[nframes].ci_pc = fp->FR_SAVPC; +# if NARGS > 0 + for (i = 0; i < NARGS; i++) { + info[nframes].ci_arg[i] = ~(fp->fr_arg[i]); + } +# endif /* NARGS > 0 */ + } + if (nframes < NFRAMES) info[nframes].ci_pc = 0; +} + +#endif /* No builtin backtrace */ + +#endif /* SAVE_CALL_CHAIN */ + +#ifdef NEED_CALLINFO + +/* Print info to stderr. We do NOT hold the allocation lock */ +GC_INNER void GC_print_callers(struct callinfo info[NFRAMES]) +{ + int i; + static int reentry_count = 0; + GC_bool stop = FALSE; + DCL_LOCK_STATE; + + /* FIXME: This should probably use a different lock, so that we */ + /* become callable with or without the allocation lock. */ + LOCK(); + ++reentry_count; + UNLOCK(); + +# if NFRAMES == 1 + GC_err_printf("\tCaller at allocation:\n"); +# else + GC_err_printf("\tCall chain at allocation:\n"); +# endif + for (i = 0; i < NFRAMES && !stop; i++) { + if (info[i].ci_pc == 0) break; +# if NARGS > 0 + { + int j; + + GC_err_printf("\t\targs: "); + for (j = 0; j < NARGS; j++) { + if (j != 0) GC_err_printf(", "); + GC_err_printf("%d (0x%X)", ~(info[i].ci_arg[j]), + ~(info[i].ci_arg[j])); + } + GC_err_printf("\n"); + } +# endif + if (reentry_count > 1) { + /* We were called during an allocation during */ + /* a previous GC_print_callers call; punt. */ + GC_err_printf("\t\t##PC##= 0x%lx\n", + (unsigned long)info[i].ci_pc); + continue; + } + { + char buf[40]; + char *name; +# if defined(GC_HAVE_BUILTIN_BACKTRACE) \ + && !defined(GC_BACKTRACE_SYMBOLS_BROKEN) + char **sym_name = + backtrace_symbols((void **)(&(info[i].ci_pc)), 1); + if (sym_name != NULL) { + name = sym_name[0]; + } else +# endif + /* else */ { + (void)snprintf(buf, sizeof(buf), "##PC##= 0x%lx", + (unsigned long)info[i].ci_pc); + buf[sizeof(buf) - 1] = '\0'; + name = buf; + } +# if defined(LINUX) && !defined(SMALL_CONFIG) + /* Try for a line number. */ + { + FILE *pipe; +# define EXE_SZ 100 + static char exe_name[EXE_SZ]; +# define CMD_SZ 200 + char cmd_buf[CMD_SZ]; +# define RESULT_SZ 200 + static char result_buf[RESULT_SZ]; + size_t result_len; + char *old_preload; +# define PRELOAD_SZ 200 + char preload_buf[PRELOAD_SZ]; + static GC_bool found_exe_name = FALSE; + static GC_bool will_fail = FALSE; + int ret_code; + /* Try to get it via a hairy and expensive scheme. */ + /* First we get the name of the executable: */ + if (will_fail) goto out; + if (!found_exe_name) { + ret_code = readlink("/proc/self/exe", exe_name, EXE_SZ); + if (ret_code < 0 || ret_code >= EXE_SZ + || exe_name[0] != '/') { + will_fail = TRUE; /* Don't try again. */ + goto out; + } + exe_name[ret_code] = '\0'; + found_exe_name = TRUE; + } + /* Then we use popen to start addr2line -e */ + /* There are faster ways to do this, but hopefully this */ + /* isn't time critical. */ + (void)snprintf(cmd_buf, sizeof(cmd_buf), + "/usr/bin/addr2line -f -e %s 0x%lx", + exe_name, (unsigned long)info[i].ci_pc); + cmd_buf[sizeof(cmd_buf) - 1] = '\0'; + old_preload = GETENV("LD_PRELOAD"); + if (0 != old_preload) { + size_t old_len = strlen(old_preload); + if (old_len >= PRELOAD_SZ) { + will_fail = TRUE; + goto out; + } + BCOPY(old_preload, preload_buf, old_len + 1); + unsetenv ("LD_PRELOAD"); + } + pipe = popen(cmd_buf, "r"); + if (0 != old_preload + && 0 != setenv ("LD_PRELOAD", preload_buf, 0)) { + WARN("Failed to reset LD_PRELOAD\n", 0); + } + if (pipe == NULL + || (result_len = fread(result_buf, 1, + RESULT_SZ - 1, pipe)) == 0) { + if (pipe != NULL) pclose(pipe); + will_fail = TRUE; + goto out; + } + if (result_buf[result_len - 1] == '\n') --result_len; + result_buf[result_len] = 0; + if (result_buf[0] == '?' + || (result_buf[result_len-2] == ':' + && result_buf[result_len-1] == '0')) { + pclose(pipe); + goto out; + } + /* Get rid of embedded newline, if any. Test for "main" */ + { + char * nl = strchr(result_buf, '\n'); + if (nl != NULL + && (word)nl < (word)(result_buf + result_len)) { + *nl = ':'; + } + if (strncmp(result_buf, "main", nl - result_buf) == 0) { + stop = TRUE; + } + } + if (result_len < RESULT_SZ - 25) { + /* Add in hex address */ + (void)snprintf(&result_buf[result_len], + sizeof(result_buf) - result_len, + " [0x%lx]", (unsigned long)info[i].ci_pc); + result_buf[sizeof(result_buf) - 1] = '\0'; + } + name = result_buf; + pclose(pipe); + out:; + } +# endif /* LINUX */ + GC_err_printf("\t\t%s\n", name); +# if defined(GC_HAVE_BUILTIN_BACKTRACE) \ + && !defined(GC_BACKTRACE_SYMBOLS_BROKEN) + if (sym_name != NULL) + free(sym_name); /* May call GC_[debug_]free; that's OK */ +# endif + } + } + LOCK(); + --reentry_count; + UNLOCK(); +} + +#endif /* NEED_CALLINFO */ + +#if defined(LINUX) && defined(__ELF__) && !defined(SMALL_CONFIG) + /* Dump /proc/self/maps to GC_stderr, to enable looking up names for */ + /* addresses in FIND_LEAK output. */ + void GC_print_address_map(void) + { + char *maps; + + GC_err_printf("---------- Begin address map ----------\n"); + maps = GC_get_maps(); + GC_err_puts(maps != NULL ? maps : "Failed to get map!\n"); + GC_err_printf("---------- End address map ----------\n"); + } +#endif /* LINUX && ELF */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/pthread_start.c b/unity-2019.4.24f1-mbe/external/bdwgc/pthread_start.c new file mode 100644 index 000000000..f83dc434c --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/pthread_start.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2010 by Hewlett-Packard Development Company. + * All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* We want to make sure that GC_thread_exit_proc() is unconditionally */ +/* invoked, even if the client is not compiled with -fexceptions, but */ +/* the GC is. The workaround is to put GC_inner_start_routine() in its */ +/* own file (pthread_start.c), and undefine __EXCEPTIONS in the GCC */ +/* case at the top of the file. FIXME: it's still unclear whether this */ +/* will actually cause the exit handler to be invoked last when */ +/* thread_exit is called (and if -fexceptions is used). */ +#if defined(__GNUC__) && defined(__linux__) + /* We undefine __EXCEPTIONS to avoid using GCC __cleanup__ attribute. */ + /* The current NPTL implementation of pthread_cleanup_push uses */ + /* __cleanup__ attribute when __EXCEPTIONS is defined (-fexceptions). */ + /* The stack unwinding and cleanup with __cleanup__ attributes work */ + /* correctly when everything is compiled with -fexceptions, but it is */ + /* not the requirement for this library clients to use -fexceptions */ + /* everywhere. With __EXCEPTIONS undefined, the cleanup routines are */ + /* registered with __pthread_register_cancel thus should work anyway. */ +# undef __EXCEPTIONS +#endif + +#include "private/pthread_support.h" + +#if defined(GC_PTHREADS) && !defined(GC_WIN32_THREADS) + +#include +#include + +/* Invoked from GC_start_routine(). */ +GC_INNER_PTHRSTART void * GC_CALLBACK GC_inner_start_routine( + struct GC_stack_base *sb, void *arg) +{ + void * (*start)(void *); + void * start_arg; + void * result; + volatile GC_thread me = + GC_start_rtn_prepare_thread(&start, &start_arg, sb, arg); + +# ifndef NACL + pthread_cleanup_push(GC_thread_exit_proc, me); +# endif + result = (*start)(start_arg); +# if defined(DEBUG_THREADS) && !defined(GC_PTHREAD_START_STANDALONE) + GC_log_printf("Finishing thread %p\n", (void *)pthread_self()); +# endif + me -> status = result; + GC_dirty(me); +# ifndef NACL + pthread_cleanup_pop(1); + /* Cleanup acquires lock, ensuring that we can't exit while */ + /* a collection that thinks we're alive is trying to stop us. */ +# endif + return result; +} + +#endif /* GC_PTHREADS */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/pthread_stop_world.c b/unity-2019.4.24f1-mbe/external/bdwgc/pthread_stop_world.c new file mode 100644 index 000000000..3349b2e50 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/pthread_stop_world.c @@ -0,0 +1,1256 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2009 by Hewlett-Packard Development Company. + * All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#include "private/pthread_support.h" + +#if defined(GC_PTHREADS) && !defined(GC_WIN32_THREADS) && \ + !defined(GC_DARWIN_THREADS) && !defined(SN_TARGET_ORBIS) \ + && !defined(SN_TARGET_PSP2) + +#ifdef NACL + +# include +# include + + STATIC int GC_nacl_num_gc_threads = 0; + STATIC __thread int GC_nacl_thread_idx = -1; + STATIC volatile int GC_nacl_park_threads_now = 0; + STATIC volatile pthread_t GC_nacl_thread_parker = -1; + + GC_INNER __thread GC_thread GC_nacl_gc_thread_self = NULL; + + volatile int GC_nacl_thread_parked[MAX_NACL_GC_THREADS]; + int GC_nacl_thread_used[MAX_NACL_GC_THREADS]; + +#elif defined(GC_OPENBSD_UTHREADS) + +# include + +#else /* !GC_OPENBSD_UTHREADS && !NACL */ + +#include +#include +#include +#include /* for nanosleep() */ +#include + +#if (!defined(AO_HAVE_load_acquire) || !defined(AO_HAVE_store_release)) \ + && !defined(CPPCHECK) +# error AO_load_acquire and/or AO_store_release are missing; +# error please define AO_REQUIRE_CAS manually +#endif + +/* It's safe to call original pthread_sigmask() here. */ +#undef pthread_sigmask + +#ifdef GC_ENABLE_SUSPEND_THREAD + static void *GC_CALLBACK suspend_self_inner(void *client_data); +#endif + +#ifdef DEBUG_THREADS +# ifndef NSIG +# if defined(MAXSIG) +# define NSIG (MAXSIG+1) +# elif defined(_NSIG) +# define NSIG _NSIG +# elif defined(__SIGRTMAX) +# define NSIG (__SIGRTMAX+1) +# else +# error define NSIG +# endif +# endif /* NSIG */ + + void GC_print_sig_mask(void) + { + sigset_t blocked; + int i; + + if (pthread_sigmask(SIG_BLOCK, NULL, &blocked) != 0) + ABORT("pthread_sigmask failed"); + for (i = 1; i < NSIG; i++) { + if (sigismember(&blocked, i)) + GC_printf("Signal blocked: %d\n", i); + } + } +#endif /* DEBUG_THREADS */ + +/* Remove the signals that we want to allow in thread stopping */ +/* handler from a set. */ +STATIC void GC_remove_allowed_signals(sigset_t *set) +{ + if (sigdelset(set, SIGINT) != 0 + || sigdelset(set, SIGQUIT) != 0 + || sigdelset(set, SIGABRT) != 0 + || sigdelset(set, SIGTERM) != 0) { + ABORT("sigdelset failed"); + } + +# ifdef MPROTECT_VDB + /* Handlers write to the thread structure, which is in the heap, */ + /* and hence can trigger a protection fault. */ + if (sigdelset(set, SIGSEGV) != 0 +# ifdef HAVE_SIGBUS + || sigdelset(set, SIGBUS) != 0 +# endif + ) { + ABORT("sigdelset failed"); + } +# endif +} + +static sigset_t suspend_handler_mask; + +STATIC volatile AO_t GC_stop_count = 0; + /* Incremented by two at the beginning of */ + /* GC_stop_world (the lowest bit is always 0). */ + +STATIC volatile AO_t GC_world_is_stopped = FALSE; + /* FALSE ==> it is safe for threads to restart, */ + /* i.e. they will see another suspend signal */ + /* before they are expected to stop (unless */ + /* they have stopped voluntarily). */ + +#if defined(GC_OSF1_THREADS) || defined(THREAD_SANITIZER) \ + || defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) + STATIC GC_bool GC_retry_signals = TRUE; +#else + STATIC GC_bool GC_retry_signals = FALSE; +#endif + +/* + * We use signals to stop threads during GC. + * + * Suspended threads wait in signal handler for SIG_THR_RESTART. + * That's more portable than semaphores or condition variables. + * (We do use sem_post from a signal handler, but that should be portable.) + * + * The thread suspension signal SIG_SUSPEND is now defined in gc_priv.h. + * Note that we can't just stop a thread; we need it to save its stack + * pointer(s) and acknowledge. + */ +#ifndef SIG_THR_RESTART +# if defined(GC_HPUX_THREADS) || defined(GC_OSF1_THREADS) \ + || defined(GC_NETBSD_THREADS) || defined(GC_USESIGRT_SIGNALS) +# if defined(_SIGRTMIN) && !defined(CPPCHECK) +# define SIG_THR_RESTART _SIGRTMIN + 5 +# else +# define SIG_THR_RESTART SIGRTMIN + 5 +# endif +# else +# define SIG_THR_RESTART SIGXCPU +# endif +#endif + +#define SIGNAL_UNSET (-1) + /* Since SIG_SUSPEND and/or SIG_THR_RESTART could represent */ + /* a non-constant expression (e.g., in case of SIGRTMIN), */ + /* actual signal numbers are determined by GC_stop_init() */ + /* unless manually set (before GC initialization). */ +STATIC int GC_sig_suspend = SIGNAL_UNSET; +STATIC int GC_sig_thr_restart = SIGNAL_UNSET; + +GC_API void GC_CALL GC_set_suspend_signal(int sig) +{ + if (GC_is_initialized) return; + + GC_sig_suspend = sig; +} + +GC_API void GC_CALL GC_set_thr_restart_signal(int sig) +{ + if (GC_is_initialized) return; + + GC_sig_thr_restart = sig; +} + +GC_API int GC_CALL GC_get_suspend_signal(void) +{ + return GC_sig_suspend != SIGNAL_UNSET ? GC_sig_suspend : SIG_SUSPEND; +} + +GC_API int GC_CALL GC_get_thr_restart_signal(void) +{ + return GC_sig_thr_restart != SIGNAL_UNSET + ? GC_sig_thr_restart : SIG_THR_RESTART; +} + +#if defined(GC_EXPLICIT_SIGNALS_UNBLOCK) \ + || !defined(NO_SIGNALS_UNBLOCK_IN_MAIN) + /* Some targets (e.g., Solaris) might require this to be called when */ + /* doing thread registering from the thread destructor. */ + GC_INNER void GC_unblock_gc_signals(void) + { + sigset_t set; + sigemptyset(&set); + GC_ASSERT(GC_sig_suspend != SIGNAL_UNSET); + GC_ASSERT(GC_sig_thr_restart != SIGNAL_UNSET); + sigaddset(&set, GC_sig_suspend); + sigaddset(&set, GC_sig_thr_restart); + if (pthread_sigmask(SIG_UNBLOCK, &set, NULL) != 0) + ABORT("pthread_sigmask failed"); + } +#endif /* GC_EXPLICIT_SIGNALS_UNBLOCK */ + +STATIC sem_t GC_suspend_ack_sem; /* also used to acknowledge restart */ + +STATIC void GC_suspend_handler_inner(ptr_t dummy, void *context); + +#ifndef NO_SA_SIGACTION + STATIC void GC_suspend_handler(int sig, siginfo_t * info GC_ATTR_UNUSED, + void * context GC_ATTR_UNUSED) +#else + STATIC void GC_suspend_handler(int sig) +#endif +{ + int old_errno = errno; + + if (sig != GC_sig_suspend) { +# if defined(GC_FREEBSD_THREADS) + /* Workaround "deferred signal handling" bug in FreeBSD 9.2. */ + if (0 == sig) return; +# endif + ABORT("Bad signal in suspend_handler"); + } + +# if defined(IA64) || defined(HP_PA) || defined(M68K) + GC_with_callee_saves_pushed(GC_suspend_handler_inner, NULL); +# else + /* We believe that in all other cases the full context is already */ + /* in the signal handler frame. */ + { +# ifdef NO_SA_SIGACTION + void *context = 0; +# endif + GC_suspend_handler_inner(NULL, context); + } +# endif + errno = old_errno; +} + +/* The lookup here is safe, since this is done on behalf */ +/* of a thread which holds the allocation lock in order */ +/* to stop the world. Thus concurrent modification of the */ +/* data structure is impossible. Unfortunately, we have to */ +/* instruct TSan that the lookup is safe. */ +#ifdef THREAD_SANITIZER + /* The implementation of the function is the same as that of */ + /* GC_lookup_thread except for the attribute added here. */ + GC_ATTR_NO_SANITIZE_THREAD + static GC_thread GC_lookup_thread_async(pthread_t id) + { + GC_thread p = GC_threads[THREAD_TABLE_INDEX(id)]; + + while (p != NULL && !THREAD_EQUAL(p->id, id)) + p = p->next; + return p; + } +#else +# define GC_lookup_thread_async GC_lookup_thread +#endif + +GC_INLINE void GC_store_stack_ptr(GC_thread me) +{ + /* There is no data race between the suspend handler (storing */ + /* stack_ptr) and GC_push_all_stacks (fetching stack_ptr) because */ + /* GC_push_all_stacks is executed after GC_stop_world exits and the */ + /* latter runs sem_wait repeatedly waiting for all the suspended */ + /* threads to call sem_post. Nonetheless, stack_ptr is stored (here) */ + /* and fetched (by GC_push_all_stacks) using the atomic primitives to */ + /* avoid the related TSan warning. */ +# ifdef SPARC + AO_store((volatile AO_t *)&me->stop_info.stack_ptr, + (AO_t)GC_save_regs_in_stack()); +# else +# ifdef IA64 + me -> backing_store_ptr = GC_save_regs_in_stack(); +# endif + AO_store((volatile AO_t *)&me->stop_info.stack_ptr, (AO_t)GC_approx_sp()); +# endif +} + +STATIC void GC_suspend_handler_inner(ptr_t dummy GC_ATTR_UNUSED, + void * context GC_ATTR_UNUSED) +{ + pthread_t self = pthread_self(); + GC_thread me; + IF_CANCEL(int cancel_state;) + AO_t my_stop_count = AO_load_acquire(&GC_stop_count); + /* After the barrier, this thread should see */ + /* the actual content of GC_threads. */ + + DISABLE_CANCEL(cancel_state); + /* pthread_setcancelstate is not defined to be async-signal-safe. */ + /* But the glibc version appears to be in the absence of */ + /* asynchronous cancellation. And since this signal handler */ + /* to block on sigsuspend, which is both async-signal-safe */ + /* and a cancellation point, there seems to be no obvious way */ + /* out of it. In fact, it looks to me like an async-signal-safe */ + /* cancellation point is inherently a problem, unless there is */ + /* some way to disable cancellation in the handler. */ +# ifdef DEBUG_THREADS + GC_log_printf("Suspending %p\n", (void *)self); +# endif + GC_ASSERT(((word)my_stop_count & 1) == 0); + + me = GC_lookup_thread_async(self); + +# ifdef GC_ENABLE_SUSPEND_THREAD + if (AO_load(&me->suspended_ext)) { + GC_store_stack_ptr(me); + sem_post(&GC_suspend_ack_sem); + suspend_self_inner(me); +# ifdef DEBUG_THREADS + GC_log_printf("Continuing %p on GC_resume_thread\n", (void *)self); +# endif + RESTORE_CANCEL(cancel_state); + return; + } +# endif + + if (((word)me->stop_info.last_stop_count & ~(word)0x1) + == (word)my_stop_count) { + /* Duplicate signal. OK if we are retrying. */ + if (!GC_retry_signals) { + WARN("Duplicate suspend signal in thread %p\n", self); + } + RESTORE_CANCEL(cancel_state); + return; + } + GC_store_stack_ptr(me); + +# ifdef THREAD_SANITIZER + /* TSan disables signals around signal handlers. Without */ + /* a pthread_sigmask call, sigsuspend may block forever. */ + { + sigset_t set; + sigemptyset(&set); + GC_ASSERT(GC_sig_suspend != SIGNAL_UNSET); + GC_ASSERT(GC_sig_thr_restart != SIGNAL_UNSET); + sigaddset(&set, GC_sig_suspend); + sigaddset(&set, GC_sig_thr_restart); + if (pthread_sigmask(SIG_UNBLOCK, &set, NULL) != 0) + ABORT("pthread_sigmask failed in suspend handler"); + } +# endif + /* Tell the thread that wants to stop the world that this */ + /* thread has been stopped. Note that sem_post() is */ + /* the only async-signal-safe primitive in LinuxThreads. */ + sem_post(&GC_suspend_ack_sem); + AO_store_release(&me->stop_info.last_stop_count, my_stop_count); + + /* Wait until that thread tells us to restart by sending */ + /* this thread a GC_sig_thr_restart signal (should be masked */ + /* at this point thus there is no race). */ + /* We do not continue until we receive that signal, */ + /* but we do not take that as authoritative. (We may be */ + /* accidentally restarted by one of the user signals we */ + /* don't block.) After we receive the signal, we use a */ + /* primitive and expensive mechanism to wait until it's */ + /* really safe to proceed. Under normal circumstances, */ + /* this code should not be executed. */ + do { + sigsuspend (&suspend_handler_mask); + } while (AO_load_acquire(&GC_world_is_stopped) + && AO_load(&GC_stop_count) == my_stop_count); + +# ifdef DEBUG_THREADS + GC_log_printf("Continuing %p\n", (void *)self); +# endif +# ifndef GC_NETBSD_THREADS_WORKAROUND + if (GC_retry_signals) +# endif + { + /* If the RESTART signal loss is possible (though it should be */ + /* less likely than losing the SUSPEND signal as we do not do */ + /* much between the first sem_post and sigsuspend calls), more */ + /* handshaking is provided to work around it. */ + sem_post(&GC_suspend_ack_sem); +# ifdef GC_NETBSD_THREADS_WORKAROUND + if (GC_retry_signals) +# endif + { + /* Set the flag (the lowest bit of last_stop_count) that the */ + /* thread has been restarted. */ + AO_store_release(&me->stop_info.last_stop_count, + (AO_t)((word)my_stop_count | 1)); + } + } + RESTORE_CANCEL(cancel_state); +} + +static void suspend_restart_barrier(int n_live_threads) +{ + int i; + + for (i = 0; i < n_live_threads; i++) { + while (0 != sem_wait(&GC_suspend_ack_sem)) { + /* On Linux, sem_wait is documented to always return zero. */ + /* But the documentation appears to be incorrect. */ + /* EINTR seems to happen with some versions of gdb. */ + if (errno != EINTR) + ABORT("sem_wait failed"); + } + } +# ifdef GC_ASSERTIONS + sem_getvalue(&GC_suspend_ack_sem, &i); + GC_ASSERT(0 == i); +# endif +} + +static int resend_lost_signals(int n_live_threads, + int (*suspend_restart_all)(void)) +{ +# define WAIT_UNIT 3000 +# define RETRY_INTERVAL 100000 + + if (n_live_threads > 0) { + unsigned long wait_usecs = 0; /* Total wait since retry. */ + for (;;) { + int ack_count; + + sem_getvalue(&GC_suspend_ack_sem, &ack_count); + if (ack_count == n_live_threads) + break; + if (wait_usecs > RETRY_INTERVAL) { + int newly_sent = suspend_restart_all(); + + GC_COND_LOG_PRINTF("Resent %d signals after timeout\n", newly_sent); + sem_getvalue(&GC_suspend_ack_sem, &ack_count); + if (newly_sent < n_live_threads - ack_count) { + WARN("Lost some threads while stopping or starting world?!\n", 0); + n_live_threads = ack_count + newly_sent; + } + wait_usecs = 0; + } + +# ifdef LINT2 + /* Workaround "waiting while holding a lock" warning. */ +# undef WAIT_UNIT +# define WAIT_UNIT 1 + sched_yield(); +# elif defined(CPPCHECK) /* || _POSIX_C_SOURCE >= 199309L */ + { + struct timespec ts; + + ts.tv_sec = 0; + ts.tv_nsec = WAIT_UNIT * 1000; + (void)nanosleep(&ts, NULL); + } +# else + usleep(WAIT_UNIT); +# endif + wait_usecs += WAIT_UNIT; + } + } + return n_live_threads; +} + +STATIC void GC_restart_handler(int sig) +{ +# if defined(DEBUG_THREADS) + int old_errno = errno; /* Preserve errno value. */ +# endif + + if (sig != GC_sig_thr_restart) + ABORT("Bad signal in restart handler"); + + /* + ** Note: even if we don't do anything useful here, + ** it would still be necessary to have a signal handler, + ** rather than ignoring the signals, otherwise + ** the signals will not be delivered at all, and + ** will thus not interrupt the sigsuspend() above. + */ +# ifdef DEBUG_THREADS + GC_log_printf("In GC_restart_handler for %p\n", (void *)pthread_self()); + errno = old_errno; +# endif +} + +# ifdef USE_TKILL_ON_ANDROID + EXTERN_C_BEGIN + extern int tkill(pid_t tid, int sig); /* from sys/linux-unistd.h */ + EXTERN_C_END + + static int android_thread_kill(pid_t tid, int sig) + { + int ret; + int old_errno = errno; + + ret = tkill(tid, sig); + if (ret < 0) { + ret = errno; + errno = old_errno; + } + return ret; + } + +# define THREAD_SYSTEM_ID(t) (t)->kernel_id +# define RAISE_SIGNAL(t, sig) android_thread_kill(THREAD_SYSTEM_ID(t), sig) +# else +# define THREAD_SYSTEM_ID(t) (t)->id +# define RAISE_SIGNAL(t, sig) pthread_kill(THREAD_SYSTEM_ID(t), sig) +# endif /* !USE_TKILL_ON_ANDROID */ + +# ifdef GC_ENABLE_SUSPEND_THREAD +# include +# include "javaxfc.h" /* to get the prototypes as extern "C" */ + + STATIC void GC_brief_async_signal_safe_sleep(void) + { + struct timeval tv; + tv.tv_sec = 0; +# if defined(GC_TIME_LIMIT) && !defined(CPPCHECK) + tv.tv_usec = 1000 * GC_TIME_LIMIT / 2; +# else + tv.tv_usec = 1000 * 50 / 2; +# endif + (void)select(0, 0, 0, 0, &tv); + } + + static void *GC_CALLBACK suspend_self_inner(void *client_data) { + GC_thread me = (GC_thread)client_data; + + while (AO_load_acquire(&me->suspended_ext)) { + /* TODO: Use sigsuspend() instead. */ + GC_brief_async_signal_safe_sleep(); + } + return NULL; + } + + GC_API void GC_CALL GC_suspend_thread(GC_SUSPEND_THREAD_ID thread) { + GC_thread t; + IF_CANCEL(int cancel_state;) + DCL_LOCK_STATE; + + LOCK(); + t = GC_lookup_thread((pthread_t)thread); + if (t == NULL || t -> suspended_ext) { + UNLOCK(); + return; + } + + /* Set the flag making the change visible to the signal handler. */ + /* This also removes the protection for t object, preventing */ + /* write faults in GC_store_stack_ptr (thus double-locking should */ + /* not occur in async_set_pht_entry_from_index). */ + AO_store_release(&t->suspended_ext, TRUE); + + if (THREAD_EQUAL((pthread_t)thread, pthread_self())) { + UNLOCK(); + /* It is safe as "t" cannot become invalid here (no race with */ + /* GC_unregister_my_thread). */ + (void)GC_do_blocking(suspend_self_inner, t); + return; + } + if ((t -> flags & FINISHED) != 0) { + /* Terminated but not joined yet. */ + UNLOCK(); + return; + } + + DISABLE_CANCEL(cancel_state); + /* GC_suspend_thread is not a cancellation point. */ +# ifdef PARALLEL_MARK + /* Ensure we do not suspend a thread while it is rebuilding */ + /* a free list, otherwise such a dead-lock is possible: */ + /* thread 1 is blocked in GC_wait_for_reclaim holding */ + /* the allocation lock, thread 2 is suspended in */ + /* GC_reclaim_generic invoked from GC_generic_malloc_many */ + /* (with GC_fl_builder_count > 0), and thread 3 is blocked */ + /* acquiring the allocation lock in GC_resume_thread. */ + if (GC_parallel) + GC_wait_for_reclaim(); +# endif + + /* TODO: Support GC_retry_signals (not needed for TSan) */ + GC_acquire_dirty_lock(); + switch (RAISE_SIGNAL(t, GC_sig_suspend)) { + /* ESRCH cannot happen as terminated threads are handled above. */ + case 0: + break; + default: + ABORT("pthread_kill failed"); + } + + /* Wait for the thread to complete threads table lookup and */ + /* stack_ptr assignment. */ + GC_ASSERT(GC_thr_initialized); + while (sem_wait(&GC_suspend_ack_sem) != 0) { + if (errno != EINTR) + ABORT("sem_wait for handler failed (suspend_self)"); + } + GC_release_dirty_lock(); + RESTORE_CANCEL(cancel_state); + UNLOCK(); + } + + GC_API void GC_CALL GC_resume_thread(GC_SUSPEND_THREAD_ID thread) { + GC_thread t; + DCL_LOCK_STATE; + + LOCK(); + t = GC_lookup_thread((pthread_t)thread); + if (t != NULL) + AO_store(&t->suspended_ext, FALSE); + UNLOCK(); + } + + GC_API int GC_CALL GC_is_thread_suspended(GC_SUSPEND_THREAD_ID thread) { + GC_thread t; + int is_suspended = 0; + DCL_LOCK_STATE; + + LOCK(); + t = GC_lookup_thread((pthread_t)thread); + if (t != NULL && t -> suspended_ext) + is_suspended = (int)TRUE; + UNLOCK(); + return is_suspended; + } +# endif /* GC_ENABLE_SUSPEND_THREAD */ + +#endif /* !GC_OPENBSD_UTHREADS && !NACL */ + +#ifdef IA64 +# define IF_IA64(x) x +#else +# define IF_IA64(x) +#endif +/* We hold allocation lock. Should do exactly the right thing if the */ +/* world is stopped. Should not fail if it isn't. */ +GC_INNER void GC_push_all_stacks(void) +{ + GC_bool found_me = FALSE; + size_t nthreads = 0; + int i; + GC_thread p; + ptr_t lo, hi; + /* On IA64, we also need to scan the register backing store. */ + IF_IA64(ptr_t bs_lo; ptr_t bs_hi;) + struct GC_traced_stack_sect_s *traced_stack_sect; + pthread_t self = pthread_self(); + word total_size = 0; + + if (!EXPECT(GC_thr_initialized, TRUE)) + GC_thr_init(); +# ifdef DEBUG_THREADS + GC_log_printf("Pushing stacks from thread %p\n", (void *)self); +# endif + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (p = GC_threads[i]; p != 0; p = p -> next) { + if (p -> flags & FINISHED) continue; + ++nthreads; + traced_stack_sect = p -> traced_stack_sect; + if (THREAD_EQUAL(p -> id, self)) { + GC_ASSERT(!p->thread_blocked); +# ifdef SPARC + lo = (ptr_t)GC_save_regs_in_stack(); +# else + lo = GC_approx_sp(); +# endif + found_me = TRUE; + IF_IA64(bs_hi = (ptr_t)GC_save_regs_in_stack();) + } else { + lo = (ptr_t)AO_load((volatile AO_t *)&p->stop_info.stack_ptr); + IF_IA64(bs_hi = p -> backing_store_ptr;) + if (traced_stack_sect != NULL + && traced_stack_sect->saved_stack_ptr == lo) { + /* If the thread has never been stopped since the recent */ + /* GC_call_with_gc_active invocation then skip the top */ + /* "stack section" as stack_ptr already points to. */ + traced_stack_sect = traced_stack_sect->prev; + } + } + if ((p -> flags & MAIN_THREAD) == 0) { + hi = p -> stack_end; + IF_IA64(bs_lo = p -> backing_store_end); + } else { + /* The original stack. */ + hi = GC_stackbottom; + IF_IA64(bs_lo = BACKING_STORE_BASE;) + } +# ifdef DEBUG_THREADS + GC_log_printf("Stack for thread %p = [%p,%p)\n", + (void *)p->id, (void *)lo, (void *)hi); +# endif + if (0 == lo) ABORT("GC_push_all_stacks: sp not set!"); + if (p->altstack != NULL && (word)p->altstack <= (word)lo + && (word)lo <= (word)p->altstack + p->altstack_size) { + hi = p->altstack + p->altstack_size; + /* FIXME: Need to scan the normal stack too, but how ? */ + /* FIXME: Assume stack grows down */ + } + GC_push_all_stack_sections(lo, hi, traced_stack_sect); +# ifdef STACK_GROWS_UP + total_size += lo - hi; +# else + total_size += hi - lo; /* lo <= hi */ +# endif +# ifdef NACL + /* Push reg_storage as roots, this will cover the reg context. */ + GC_push_all_stack((ptr_t)p -> stop_info.reg_storage, + (ptr_t)(p -> stop_info.reg_storage + NACL_GC_REG_STORAGE_SIZE)); + total_size += NACL_GC_REG_STORAGE_SIZE * sizeof(ptr_t); +# endif +# ifdef IA64 +# ifdef DEBUG_THREADS + GC_log_printf("Reg stack for thread %p = [%p,%p)\n", + (void *)p->id, (void *)bs_lo, (void *)bs_hi); +# endif + /* FIXME: This (if p->id==self) may add an unbounded number of */ + /* entries, and hence overflow the mark stack, which is bad. */ + GC_push_all_register_sections(bs_lo, bs_hi, + THREAD_EQUAL(p -> id, self), + traced_stack_sect); + total_size += bs_hi - bs_lo; /* bs_lo <= bs_hi */ +# endif + } + } + GC_VERBOSE_LOG_PRINTF("Pushed %d thread stacks\n", (int)nthreads); + if (!found_me && !GC_in_thread_creation) + ABORT("Collecting from unknown thread"); + GC_total_stacksize = total_size; +} + +#ifdef DEBUG_THREADS + /* There seems to be a very rare thread stopping problem. To help us */ + /* debug that, we save the ids of the stopping thread. */ + pthread_t GC_stopping_thread; + int GC_stopping_pid = 0; +#endif + +/* We hold the allocation lock. Suspend all threads that might */ +/* still be running. Return the number of suspend signals that */ +/* were sent. */ +STATIC int GC_suspend_all(void) +{ + int n_live_threads = 0; + int i; +# ifndef NACL + GC_thread p; +# ifndef GC_OPENBSD_UTHREADS + int result; +# endif + pthread_t self = pthread_self(); + + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (p = GC_threads[i]; p != 0; p = p -> next) { + if (!THREAD_EQUAL(p -> id, self)) { + if ((p -> flags & FINISHED) != 0) continue; + if (p -> thread_blocked) /* Will wait */ continue; +# ifndef GC_OPENBSD_UTHREADS +# ifdef GC_ENABLE_SUSPEND_THREAD + if (p -> suspended_ext) continue; +# endif + if (AO_load(&p->stop_info.last_stop_count) == GC_stop_count) + continue; /* matters only if GC_retry_signals */ + n_live_threads++; +# endif +# ifdef DEBUG_THREADS + GC_log_printf("Sending suspend signal to %p\n", (void *)p->id); +# endif + +# ifdef GC_OPENBSD_UTHREADS + { + stack_t stack; + + GC_acquire_dirty_lock(); + if (pthread_suspend_np(p -> id) != 0) + ABORT("pthread_suspend_np failed"); + GC_release_dirty_lock(); + if (pthread_stackseg_np(p->id, &stack)) + ABORT("pthread_stackseg_np failed"); + p -> stop_info.stack_ptr = (ptr_t)stack.ss_sp - stack.ss_size; + if (GC_on_thread_event) + GC_on_thread_event(GC_EVENT_THREAD_SUSPENDED, + (void *)p->id); + } +# else + /* The synchronization between GC_dirty (based on */ + /* test-and-set) and the signal-based thread suspension */ + /* is performed in GC_stop_world because */ + /* GC_release_dirty_lock cannot be called before */ + /* acknowledging the thread is really suspended. */ + result = RAISE_SIGNAL(p, GC_sig_suspend); + switch(result) { + case ESRCH: + /* Not really there anymore. Possible? */ + n_live_threads--; + break; + case 0: + if (GC_on_thread_event) + GC_on_thread_event(GC_EVENT_THREAD_SUSPENDED, + (void *)(word)THREAD_SYSTEM_ID(p)); + /* Note: thread id might be truncated. */ + break; + default: + ABORT_ARG1("pthread_kill failed at suspend", + ": errcode= %d", result); + } +# endif + } + } + } + +# else /* NACL */ +# ifndef NACL_PARK_WAIT_NANOSECONDS +# define NACL_PARK_WAIT_NANOSECONDS (100 * 1000) +# endif +# define NANOS_PER_SECOND (1000UL * 1000 * 1000) + unsigned long num_sleeps = 0; + +# ifdef DEBUG_THREADS + GC_log_printf("pthread_stop_world: num_threads=%d\n", + GC_nacl_num_gc_threads - 1); +# endif + GC_nacl_thread_parker = pthread_self(); + GC_nacl_park_threads_now = 1; + + while (1) { + int num_threads_parked = 0; + struct timespec ts; + int num_used = 0; + + /* Check the 'parked' flag for each thread the GC knows about. */ + for (i = 0; i < MAX_NACL_GC_THREADS + && num_used < GC_nacl_num_gc_threads; i++) { + if (GC_nacl_thread_used[i] == 1) { + num_used++; + if (GC_nacl_thread_parked[i] == 1) { + num_threads_parked++; + if (GC_on_thread_event) + GC_on_thread_event(GC_EVENT_THREAD_SUSPENDED, (void *)(word)i); + } + } + } + /* -1 for the current thread. */ + if (num_threads_parked >= GC_nacl_num_gc_threads - 1) + break; + ts.tv_sec = 0; + ts.tv_nsec = NACL_PARK_WAIT_NANOSECONDS; +# ifdef DEBUG_THREADS + GC_log_printf("Sleep waiting for %d threads to park...\n", + GC_nacl_num_gc_threads - num_threads_parked - 1); +# endif + /* This requires _POSIX_TIMERS feature. */ + nanosleep(&ts, 0); + if (++num_sleeps > NANOS_PER_SECOND / NACL_PARK_WAIT_NANOSECONDS) { + WARN("GC appears stalled waiting for %" WARN_PRIdPTR + " threads to park...\n", + GC_nacl_num_gc_threads - num_threads_parked - 1); + num_sleeps = 0; + } + } +# endif /* NACL */ + return n_live_threads; +} + +GC_INNER void GC_stop_world(void) +{ +# if !defined(GC_OPENBSD_UTHREADS) && !defined(NACL) + int n_live_threads; +# endif + GC_ASSERT(I_HOLD_LOCK()); +# ifdef DEBUG_THREADS + GC_stopping_thread = pthread_self(); + GC_stopping_pid = getpid(); + GC_log_printf("Stopping the world from %p\n", (void *)GC_stopping_thread); +# endif + + /* Make sure all free list construction has stopped before we start. */ + /* No new construction can start, since free list construction is */ + /* required to acquire and release the GC lock before it starts, */ + /* and we have the lock. */ +# ifdef PARALLEL_MARK + if (GC_parallel) { + GC_acquire_mark_lock(); + GC_ASSERT(GC_fl_builder_count == 0); + /* We should have previously waited for it to become zero. */ + } +# endif /* PARALLEL_MARK */ + +# if defined(GC_OPENBSD_UTHREADS) || defined(NACL) + (void)GC_suspend_all(); +# else + AO_store(&GC_stop_count, (AO_t)((word)GC_stop_count + 2)); + /* Only concurrent reads are possible. */ +# ifdef MANUAL_VDB + GC_acquire_dirty_lock(); + /* The write fault handler cannot be called if GC_manual_vdb */ + /* (thus double-locking should not occur in */ + /* async_set_pht_entry_from_index based on test-and-set). */ +# endif + AO_store_release(&GC_world_is_stopped, TRUE); + n_live_threads = GC_suspend_all(); + if (GC_retry_signals) + n_live_threads = resend_lost_signals(n_live_threads, GC_suspend_all); + suspend_restart_barrier(n_live_threads); +# ifdef MANUAL_VDB + GC_release_dirty_lock(); /* cannot be done in GC_suspend_all */ +# endif +# endif + +# ifdef PARALLEL_MARK + if (GC_parallel) + GC_release_mark_lock(); +# endif +# ifdef DEBUG_THREADS + GC_log_printf("World stopped from %p\n", (void *)pthread_self()); + GC_stopping_thread = 0; +# endif +} + +#ifdef NACL +# if defined(__x86_64__) +# define NACL_STORE_REGS() \ + do { \ + __asm__ __volatile__ ("push %rbx"); \ + __asm__ __volatile__ ("push %rbp"); \ + __asm__ __volatile__ ("push %r12"); \ + __asm__ __volatile__ ("push %r13"); \ + __asm__ __volatile__ ("push %r14"); \ + __asm__ __volatile__ ("push %r15"); \ + __asm__ __volatile__ ("mov %%esp, %0" \ + : "=m" (GC_nacl_gc_thread_self->stop_info.stack_ptr)); \ + BCOPY(GC_nacl_gc_thread_self->stop_info.stack_ptr, \ + GC_nacl_gc_thread_self->stop_info.reg_storage, \ + NACL_GC_REG_STORAGE_SIZE * sizeof(ptr_t)); \ + __asm__ __volatile__ ("naclasp $48, %r15"); \ + } while (0) +# elif defined(__i386__) +# define NACL_STORE_REGS() \ + do { \ + __asm__ __volatile__ ("push %ebx"); \ + __asm__ __volatile__ ("push %ebp"); \ + __asm__ __volatile__ ("push %esi"); \ + __asm__ __volatile__ ("push %edi"); \ + __asm__ __volatile__ ("mov %%esp, %0" \ + : "=m" (GC_nacl_gc_thread_self->stop_info.stack_ptr)); \ + BCOPY(GC_nacl_gc_thread_self->stop_info.stack_ptr, \ + GC_nacl_gc_thread_self->stop_info.reg_storage, \ + NACL_GC_REG_STORAGE_SIZE * sizeof(ptr_t));\ + __asm__ __volatile__ ("add $16, %esp"); \ + } while (0) +# elif defined(__arm__) +# define NACL_STORE_REGS() \ + do { \ + __asm__ __volatile__ ("push {r4-r8,r10-r12,lr}"); \ + __asm__ __volatile__ ("mov r0, %0" \ + : : "r" (&GC_nacl_gc_thread_self->stop_info.stack_ptr)); \ + __asm__ __volatile__ ("bic r0, r0, #0xc0000000"); \ + __asm__ __volatile__ ("str sp, [r0]"); \ + BCOPY(GC_nacl_gc_thread_self->stop_info.stack_ptr, \ + GC_nacl_gc_thread_self->stop_info.reg_storage, \ + NACL_GC_REG_STORAGE_SIZE * sizeof(ptr_t)); \ + __asm__ __volatile__ ("add sp, sp, #40"); \ + __asm__ __volatile__ ("bic sp, sp, #0xc0000000"); \ + } while (0) +# else +# error TODO Please port NACL_STORE_REGS +# endif + + GC_API_OSCALL void nacl_pre_syscall_hook(void) + { + if (GC_nacl_thread_idx != -1) { + NACL_STORE_REGS(); + GC_nacl_gc_thread_self->stop_info.stack_ptr = GC_approx_sp(); + GC_nacl_thread_parked[GC_nacl_thread_idx] = 1; + } + } + + GC_API_OSCALL void __nacl_suspend_thread_if_needed(void) + { + if (GC_nacl_park_threads_now) { + pthread_t self = pthread_self(); + + /* Don't try to park the thread parker. */ + if (GC_nacl_thread_parker == self) + return; + + /* This can happen when a thread is created outside of the GC */ + /* system (wthread mostly). */ + if (GC_nacl_thread_idx < 0) + return; + + /* If it was already 'parked', we're returning from a syscall, */ + /* so don't bother storing registers again, the GC has a set. */ + if (!GC_nacl_thread_parked[GC_nacl_thread_idx]) { + NACL_STORE_REGS(); + GC_nacl_gc_thread_self->stop_info.stack_ptr = GC_approx_sp(); + } + GC_nacl_thread_parked[GC_nacl_thread_idx] = 1; + while (GC_nacl_park_threads_now) { + /* Just spin. */ + } + GC_nacl_thread_parked[GC_nacl_thread_idx] = 0; + + /* Clear out the reg storage for next suspend. */ + BZERO(GC_nacl_gc_thread_self->stop_info.reg_storage, + NACL_GC_REG_STORAGE_SIZE * sizeof(ptr_t)); + } + } + + GC_API_OSCALL void nacl_post_syscall_hook(void) + { + /* Calling __nacl_suspend_thread_if_needed right away should */ + /* guarantee we don't mutate the GC set. */ + __nacl_suspend_thread_if_needed(); + if (GC_nacl_thread_idx != -1) { + GC_nacl_thread_parked[GC_nacl_thread_idx] = 0; + } + } + + STATIC GC_bool GC_nacl_thread_parking_inited = FALSE; + STATIC pthread_mutex_t GC_nacl_thread_alloc_lock = PTHREAD_MUTEX_INITIALIZER; + + struct nacl_irt_blockhook { + int (*register_block_hooks)(void (*pre)(void), void (*post)(void)); + }; + + EXTERN_C_BEGIN + extern size_t nacl_interface_query(const char *interface_ident, + void *table, size_t tablesize); + EXTERN_C_END + + GC_INNER void GC_nacl_initialize_gc_thread(void) + { + int i; + static struct nacl_irt_blockhook gc_hook; + + pthread_mutex_lock(&GC_nacl_thread_alloc_lock); + if (!EXPECT(GC_nacl_thread_parking_inited, TRUE)) { + BZERO(GC_nacl_thread_parked, sizeof(GC_nacl_thread_parked)); + BZERO(GC_nacl_thread_used, sizeof(GC_nacl_thread_used)); + /* TODO: replace with public 'register hook' function when */ + /* available from glibc. */ + nacl_interface_query("nacl-irt-blockhook-0.1", + &gc_hook, sizeof(gc_hook)); + gc_hook.register_block_hooks(nacl_pre_syscall_hook, + nacl_post_syscall_hook); + GC_nacl_thread_parking_inited = TRUE; + } + GC_ASSERT(GC_nacl_num_gc_threads <= MAX_NACL_GC_THREADS); + for (i = 0; i < MAX_NACL_GC_THREADS; i++) { + if (GC_nacl_thread_used[i] == 0) { + GC_nacl_thread_used[i] = 1; + GC_nacl_thread_idx = i; + GC_nacl_num_gc_threads++; + break; + } + } + pthread_mutex_unlock(&GC_nacl_thread_alloc_lock); + } + + GC_INNER void GC_nacl_shutdown_gc_thread(void) + { + pthread_mutex_lock(&GC_nacl_thread_alloc_lock); + GC_ASSERT(GC_nacl_thread_idx >= 0); + GC_ASSERT(GC_nacl_thread_idx < MAX_NACL_GC_THREADS); + GC_ASSERT(GC_nacl_thread_used[GC_nacl_thread_idx] != 0); + GC_nacl_thread_used[GC_nacl_thread_idx] = 0; + GC_nacl_thread_idx = -1; + GC_nacl_num_gc_threads--; + pthread_mutex_unlock(&GC_nacl_thread_alloc_lock); + } + +#else /* !NACL */ + + /* Restart all threads that were suspended by the collector. */ + /* Return the number of restart signals that were sent. */ + STATIC int GC_restart_all(void) + { + int n_live_threads = 0; + int i; + pthread_t self = pthread_self(); + GC_thread p; +# ifndef GC_OPENBSD_UTHREADS + int result; +# endif + + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (p = GC_threads[i]; p != NULL; p = p -> next) { + if (!THREAD_EQUAL(p -> id, self)) { + if ((p -> flags & FINISHED) != 0) continue; + if (p -> thread_blocked) continue; +# ifndef GC_OPENBSD_UTHREADS +# ifdef GC_ENABLE_SUSPEND_THREAD + if (p -> suspended_ext) continue; +# endif + if (GC_retry_signals && AO_load(&p->stop_info.last_stop_count) + == (AO_t)((word)GC_stop_count | 1)) + continue; /* The thread has been restarted. */ + n_live_threads++; +# endif +# ifdef DEBUG_THREADS + GC_log_printf("Sending restart signal to %p\n", (void *)p->id); +# endif +# ifdef GC_OPENBSD_UTHREADS + if (pthread_resume_np(p -> id) != 0) + ABORT("pthread_resume_np failed"); + if (GC_on_thread_event) + GC_on_thread_event(GC_EVENT_THREAD_UNSUSPENDED, (void *)p->id); +# else + result = RAISE_SIGNAL(p, GC_sig_thr_restart); + switch(result) { + case ESRCH: + /* Not really there anymore. Possible? */ + n_live_threads--; + break; + case 0: + if (GC_on_thread_event) + GC_on_thread_event(GC_EVENT_THREAD_UNSUSPENDED, + (void *)(word)THREAD_SYSTEM_ID(p)); + break; + default: + ABORT_ARG1("pthread_kill failed at resume", + ": errcode= %d", result); + } +# endif + } + } + } + return n_live_threads; + } +#endif /* !NACL */ + +/* Caller holds allocation lock, and has held it continuously since */ +/* the world stopped. */ +GC_INNER void GC_start_world(void) +{ +# ifndef NACL + int n_live_threads; + + GC_ASSERT(I_HOLD_LOCK()); +# ifdef DEBUG_THREADS + GC_log_printf("World starting\n"); +# endif +# ifndef GC_OPENBSD_UTHREADS + AO_store_release(&GC_world_is_stopped, FALSE); + /* The updated value should now be visible to the */ + /* signal handler (note that pthread_kill is not on */ + /* the list of functions which synchronize memory). */ +# endif + n_live_threads = GC_restart_all(); +# ifndef GC_OPENBSD_UTHREADS + if (GC_retry_signals) + n_live_threads = resend_lost_signals(n_live_threads, GC_restart_all); +# ifdef GC_NETBSD_THREADS_WORKAROUND + suspend_restart_barrier(n_live_threads); +# else + if (GC_retry_signals) + suspend_restart_barrier(n_live_threads); +# endif +# else + (void)n_live_threads; +# endif +# ifdef DEBUG_THREADS + GC_log_printf("World started\n"); +# endif +# else /* NACL */ +# ifdef DEBUG_THREADS + GC_log_printf("World starting...\n"); +# endif + GC_nacl_park_threads_now = 0; + if (GC_on_thread_event) + GC_on_thread_event(GC_EVENT_THREAD_UNSUSPENDED, NULL); + /* TODO: Send event for every unsuspended thread. */ +# endif +} + +GC_INNER void GC_stop_init(void) +{ +# if !defined(GC_OPENBSD_UTHREADS) && !defined(NACL) + struct sigaction act; + char *str; + + if (SIGNAL_UNSET == GC_sig_suspend) + GC_sig_suspend = SIG_SUSPEND; + if (SIGNAL_UNSET == GC_sig_thr_restart) + GC_sig_thr_restart = SIG_THR_RESTART; + if (GC_sig_suspend == GC_sig_thr_restart) + ABORT("Cannot use same signal for thread suspend and resume"); + + if (sem_init(&GC_suspend_ack_sem, GC_SEM_INIT_PSHARED, 0) != 0) + ABORT("sem_init failed"); + +# ifdef SA_RESTART + act.sa_flags = SA_RESTART +# else + act.sa_flags = 0 +# endif +# ifndef NO_SA_SIGACTION + | SA_SIGINFO +# endif + ; + if (sigfillset(&act.sa_mask) != 0) { + ABORT("sigfillset failed"); + } +# ifdef GC_RTEMS_PTHREADS + if(sigprocmask(SIG_UNBLOCK, &act.sa_mask, NULL) != 0) { + ABORT("sigprocmask failed"); + } +# endif + GC_remove_allowed_signals(&act.sa_mask); + /* GC_sig_thr_restart is set in the resulting mask. */ + /* It is unmasked by the handler when necessary. */ +# ifndef NO_SA_SIGACTION + act.sa_sigaction = GC_suspend_handler; +# else + act.sa_handler = GC_suspend_handler; +# endif + /* act.sa_restorer is deprecated and should not be initialized. */ + if (sigaction(GC_sig_suspend, &act, NULL) != 0) { + ABORT("Cannot set SIG_SUSPEND handler"); + } + +# ifndef NO_SA_SIGACTION + act.sa_flags &= ~SA_SIGINFO; +# endif + act.sa_handler = GC_restart_handler; + if (sigaction(GC_sig_thr_restart, &act, NULL) != 0) { + ABORT("Cannot set SIG_THR_RESTART handler"); + } + + /* Initialize suspend_handler_mask (excluding GC_sig_thr_restart). */ + if (sigfillset(&suspend_handler_mask) != 0) ABORT("sigfillset failed"); + GC_remove_allowed_signals(&suspend_handler_mask); + if (sigdelset(&suspend_handler_mask, GC_sig_thr_restart) != 0) + ABORT("sigdelset failed"); + + /* Override the default value of GC_retry_signals. */ + str = GETENV("GC_RETRY_SIGNALS"); + if (str != NULL) { + if (*str == '0' && *(str + 1) == '\0') { + /* Do not retry if the environment variable is set to "0". */ + GC_retry_signals = FALSE; + } else { + GC_retry_signals = TRUE; + } + } + if (GC_retry_signals) { + GC_COND_LOG_PRINTF( + "Will retry suspend and restart signals if necessary\n"); + } +# ifndef NO_SIGNALS_UNBLOCK_IN_MAIN + /* Explicitly unblock the signals once before new threads creation. */ + GC_unblock_gc_signals(); +# endif +# endif /* !GC_OPENBSD_UTHREADS && !NACL */ +} + +#endif /* GC_PTHREADS && !GC_DARWIN_THREADS && !GC_WIN32_THREADS */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/pthread_support.c b/unity-2019.4.24f1-mbe/external/bdwgc/pthread_support.c new file mode 100644 index 000000000..43b1e775b --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/pthread_support.c @@ -0,0 +1,2295 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2005 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#include "private/pthread_support.h" + +/* + * Support code originally for LinuxThreads, the clone()-based kernel + * thread package for Linux which is included in libc6. + * + * This code no doubt makes some assumptions beyond what is + * guaranteed by the pthread standard, though it now does + * very little of that. It now also supports NPTL, and many + * other Posix thread implementations. We are trying to merge + * all flavors of pthread support code into this file. + */ + +#if defined(GC_PTHREADS) && !defined(GC_WIN32_THREADS) + +# include +# include +# include +# include +# include +# include +# if !defined(SN_TARGET_ORBIS) && !defined(SN_TARGET_PSP2) +# if !defined(GC_RTEMS_PTHREADS) +# include +# endif +# include +# include +# include +# include +# endif +# include + +# include "gc_inline.h" + +#if defined(GC_DARWIN_THREADS) +# include "private/darwin_semaphore.h" +#else +# include +#endif /* !GC_DARWIN_THREADS */ + +#if defined(GC_DARWIN_THREADS) || defined(GC_FREEBSD_THREADS) +# include +#endif /* GC_DARWIN_THREADS */ + +#if defined(GC_NETBSD_THREADS) || defined(GC_OPENBSD_THREADS) +# include +# include +#endif /* GC_NETBSD_THREADS */ + +/* Allocator lock definitions. */ +#if !defined(USE_SPIN_LOCK) + GC_INNER pthread_mutex_t GC_allocate_ml = PTHREAD_MUTEX_INITIALIZER; +#endif + +#ifdef GC_ASSERTIONS + GC_INNER unsigned long GC_lock_holder = NO_THREAD; + /* Used only for assertions. */ +#endif + +#if defined(GC_DGUX386_THREADS) +# include +# include + /* sem_t is an uint in DG/UX */ + typedef unsigned int sem_t; +#endif /* GC_DGUX386_THREADS */ + +/* Undefine macros used to redirect pthread primitives. */ +# undef pthread_create +# ifndef GC_NO_PTHREAD_SIGMASK +# undef pthread_sigmask +# endif +# ifndef GC_NO_PTHREAD_CANCEL +# undef pthread_cancel +# endif +# ifdef GC_HAVE_PTHREAD_EXIT +# undef pthread_exit +# endif +# undef pthread_join +# undef pthread_detach +# if defined(GC_OSF1_THREADS) && defined(_PTHREAD_USE_MANGLED_NAMES_) \ + && !defined(_PTHREAD_USE_PTDNAM_) + /* Restore the original mangled names on Tru64 UNIX. */ +# define pthread_create __pthread_create +# define pthread_join __pthread_join +# define pthread_detach __pthread_detach +# ifndef GC_NO_PTHREAD_CANCEL +# define pthread_cancel __pthread_cancel +# endif +# ifdef GC_HAVE_PTHREAD_EXIT +# define pthread_exit __pthread_exit +# endif +# endif + +#ifdef GC_USE_LD_WRAP +# define WRAP_FUNC(f) __wrap_##f +# define REAL_FUNC(f) __real_##f + int REAL_FUNC(pthread_create)(pthread_t *, + GC_PTHREAD_CREATE_CONST pthread_attr_t *, + void *(*start_routine)(void *), void *); + int REAL_FUNC(pthread_join)(pthread_t, void **); + int REAL_FUNC(pthread_detach)(pthread_t); +# ifndef GC_NO_PTHREAD_SIGMASK + int REAL_FUNC(pthread_sigmask)(int, const sigset_t *, sigset_t *); +# endif +# ifndef GC_NO_PTHREAD_CANCEL + int REAL_FUNC(pthread_cancel)(pthread_t); +# endif +# ifdef GC_HAVE_PTHREAD_EXIT + void REAL_FUNC(pthread_exit)(void *) GC_PTHREAD_EXIT_ATTRIBUTE; +# endif +#else +# ifdef GC_USE_DLOPEN_WRAP +# include +# define WRAP_FUNC(f) f +# define REAL_FUNC(f) GC_real_##f + /* We define both GC_f and plain f to be the wrapped function. */ + /* In that way plain calls work, as do calls from files that */ + /* included gc.h, which redefined f to GC_f. */ + /* FIXME: Needs work for DARWIN and True64 (OSF1) */ + typedef int (* GC_pthread_create_t)(pthread_t *, + GC_PTHREAD_CREATE_CONST pthread_attr_t *, + void * (*)(void *), void *); + static GC_pthread_create_t REAL_FUNC(pthread_create); +# ifndef GC_NO_PTHREAD_SIGMASK + typedef int (* GC_pthread_sigmask_t)(int, const sigset_t *, + sigset_t *); + static GC_pthread_sigmask_t REAL_FUNC(pthread_sigmask); +# endif + typedef int (* GC_pthread_join_t)(pthread_t, void **); + static GC_pthread_join_t REAL_FUNC(pthread_join); + typedef int (* GC_pthread_detach_t)(pthread_t); + static GC_pthread_detach_t REAL_FUNC(pthread_detach); +# ifndef GC_NO_PTHREAD_CANCEL + typedef int (* GC_pthread_cancel_t)(pthread_t); + static GC_pthread_cancel_t REAL_FUNC(pthread_cancel); +# endif +# ifdef GC_HAVE_PTHREAD_EXIT + typedef void (* GC_pthread_exit_t)(void *) GC_PTHREAD_EXIT_ATTRIBUTE; + static GC_pthread_exit_t REAL_FUNC(pthread_exit); +# endif +# else +# define WRAP_FUNC(f) GC_##f +# if !defined(GC_DGUX386_THREADS) +# define REAL_FUNC(f) f +# else /* GC_DGUX386_THREADS */ +# define REAL_FUNC(f) __d10_##f +# endif /* GC_DGUX386_THREADS */ +# endif +#endif + +#if defined(GC_USE_LD_WRAP) || defined(GC_USE_DLOPEN_WRAP) + /* Define GC_ functions as aliases for the plain ones, which will */ + /* be intercepted. This allows files which include gc.h, and hence */ + /* generate references to the GC_ symbols, to see the right symbols. */ + GC_API int GC_pthread_create(pthread_t * t, + GC_PTHREAD_CREATE_CONST pthread_attr_t *a, + void * (* fn)(void *), void * arg) + { + return pthread_create(t, a, fn, arg); + } + +# ifndef GC_NO_PTHREAD_SIGMASK + GC_API int GC_pthread_sigmask(int how, const sigset_t *mask, + sigset_t *old) + { + return pthread_sigmask(how, mask, old); + } +# endif /* !GC_NO_PTHREAD_SIGMASK */ + + GC_API int GC_pthread_join(pthread_t t, void **res) + { + return pthread_join(t, res); + } + + GC_API int GC_pthread_detach(pthread_t t) + { + return pthread_detach(t); + } + +# ifndef GC_NO_PTHREAD_CANCEL + GC_API int GC_pthread_cancel(pthread_t t) + { + return pthread_cancel(t); + } +# endif /* !GC_NO_PTHREAD_CANCEL */ + +# ifdef GC_HAVE_PTHREAD_EXIT + GC_API GC_PTHREAD_EXIT_ATTRIBUTE void GC_pthread_exit(void *retval) + { + pthread_exit(retval); + } +# endif +#endif /* Linker-based interception. */ + +#ifdef GC_USE_DLOPEN_WRAP + STATIC GC_bool GC_syms_initialized = FALSE; + + STATIC void GC_init_real_syms(void) + { + void *dl_handle; + + if (GC_syms_initialized) return; +# ifdef RTLD_NEXT + dl_handle = RTLD_NEXT; +# else + dl_handle = dlopen("libpthread.so.0", RTLD_LAZY); + if (NULL == dl_handle) { + dl_handle = dlopen("libpthread.so", RTLD_LAZY); /* without ".0" */ + } + if (NULL == dl_handle) ABORT("Couldn't open libpthread"); +# endif + REAL_FUNC(pthread_create) = (GC_pthread_create_t)(word) + dlsym(dl_handle, "pthread_create"); +# ifdef RTLD_NEXT + if (REAL_FUNC(pthread_create) == 0) + ABORT("pthread_create not found" + " (probably -lgc is specified after -lpthread)"); +# endif +# ifndef GC_NO_PTHREAD_SIGMASK + REAL_FUNC(pthread_sigmask) = (GC_pthread_sigmask_t)(word) + dlsym(dl_handle, "pthread_sigmask"); +# endif + REAL_FUNC(pthread_join) = (GC_pthread_join_t)(word) + dlsym(dl_handle, "pthread_join"); + REAL_FUNC(pthread_detach) = (GC_pthread_detach_t)(word) + dlsym(dl_handle, "pthread_detach"); +# ifndef GC_NO_PTHREAD_CANCEL + REAL_FUNC(pthread_cancel) = (GC_pthread_cancel_t)(word) + dlsym(dl_handle, "pthread_cancel"); +# endif +# ifdef GC_HAVE_PTHREAD_EXIT + REAL_FUNC(pthread_exit) = (GC_pthread_exit_t)(word) + dlsym(dl_handle, "pthread_exit"); +# endif + GC_syms_initialized = TRUE; + } + +# define INIT_REAL_SYMS() if (EXPECT(GC_syms_initialized, TRUE)) {} \ + else GC_init_real_syms() +#else +# define INIT_REAL_SYMS() (void)0 +#endif + +static GC_bool parallel_initialized = FALSE; + +#ifndef GC_ALWAYS_MULTITHREADED + GC_INNER GC_bool GC_need_to_lock = FALSE; +#endif + +STATIC int GC_nprocs = 1; + /* Number of processors. We may not have */ + /* access to all of them, but this is as good */ + /* a guess as any ... */ + +#ifdef THREAD_LOCAL_ALLOC + /* We must explicitly mark ptrfree and gcj free lists, since the free */ + /* list links wouldn't otherwise be found. We also set them in the */ + /* normal free lists, since that involves touching less memory than */ + /* if we scanned them normally. */ + GC_INNER void GC_mark_thread_local_free_lists(void) + { + int i; + GC_thread p; + + for (i = 0; i < THREAD_TABLE_SZ; ++i) { + for (p = GC_threads[i]; 0 != p; p = p -> next) { + if (!(p -> flags & FINISHED)) + GC_mark_thread_local_fls_for(&(p->tlfs)); + } + } + } + +# if defined(GC_ASSERTIONS) + /* Check that all thread-local free-lists are completely marked. */ + /* Also check that thread-specific-data structures are marked. */ + void GC_check_tls(void) + { + int i; + GC_thread p; + + for (i = 0; i < THREAD_TABLE_SZ; ++i) { + for (p = GC_threads[i]; 0 != p; p = p -> next) { + if (!(p -> flags & FINISHED)) + GC_check_tls_for(&(p->tlfs)); + } + } +# if defined(USE_CUSTOM_SPECIFIC) + if (GC_thread_key != 0) + GC_check_tsd_marks(GC_thread_key); +# endif + } +# endif /* GC_ASSERTIONS */ + +#endif /* THREAD_LOCAL_ALLOC */ + +#ifdef PARALLEL_MARK + +# ifndef MAX_MARKERS +# define MAX_MARKERS 16 +# endif + +static ptr_t marker_sp[MAX_MARKERS - 1] = {0}; +#ifdef IA64 + static ptr_t marker_bsp[MAX_MARKERS - 1] = {0}; +#endif + +#if defined(GC_DARWIN_THREADS) && !defined(GC_NO_THREADS_DISCOVERY) + static mach_port_t marker_mach_threads[MAX_MARKERS - 1] = {0}; + + /* Used only by GC_suspend_thread_list(). */ + GC_INNER GC_bool GC_is_mach_marker(thread_act_t thread) + { + int i; + for (i = 0; i < GC_markers_m1; i++) { + if (marker_mach_threads[i] == thread) + return TRUE; + } + return FALSE; + } +#endif /* GC_DARWIN_THREADS */ + +STATIC void * GC_mark_thread(void * id) +{ + word my_mark_no = 0; + IF_CANCEL(int cancel_state;) + + if ((word)id == (word)-1) return 0; /* to make compiler happy */ + DISABLE_CANCEL(cancel_state); + /* Mark threads are not cancellable; they */ + /* should be invisible to client. */ + marker_sp[(word)id] = GC_approx_sp(); +# ifdef IA64 + marker_bsp[(word)id] = GC_save_regs_in_stack(); +# endif +# if defined(GC_DARWIN_THREADS) && !defined(GC_NO_THREADS_DISCOVERY) + marker_mach_threads[(word)id] = mach_thread_self(); +# endif + + /* Inform GC_start_mark_threads about completion of marker data init. */ + GC_acquire_mark_lock(); + if (0 == --GC_fl_builder_count) /* count may have a negative value */ + GC_notify_all_builder(); + + for (;; ++my_mark_no) { + /* GC_mark_no is passed only to allow GC_help_marker to terminate */ + /* promptly. This is important if it were called from the signal */ + /* handler or from the GC lock acquisition code. Under Linux, it's */ + /* not safe to call it from a signal handler, since it uses mutexes */ + /* and condition variables. Since it is called only here, the */ + /* argument is unnecessary. */ + if (my_mark_no < GC_mark_no || my_mark_no > GC_mark_no + 2) { + /* resynchronize if we get far off, e.g. because GC_mark_no */ + /* wrapped. */ + my_mark_no = GC_mark_no; + } +# ifdef DEBUG_THREADS + GC_log_printf("Starting mark helper for mark number %lu\n", + (unsigned long)my_mark_no); +# endif + GC_help_marker(my_mark_no); + } +} + +STATIC pthread_t GC_mark_threads[MAX_MARKERS]; + +#ifdef CAN_HANDLE_FORK + static int available_markers_m1 = 0; + static pthread_cond_t mark_cv; + /* initialized by GC_start_mark_threads_inner */ +#else +# define available_markers_m1 GC_markers_m1 + static pthread_cond_t mark_cv = PTHREAD_COND_INITIALIZER; +#endif + +GC_INNER void GC_start_mark_threads_inner(void) +{ + int i; + pthread_attr_t attr; +# ifndef NO_MARKER_SPECIAL_SIGMASK + sigset_t set, oldset; +# endif + + GC_ASSERT(I_DONT_HOLD_LOCK()); + if (available_markers_m1 <= 0) return; + /* Skip if parallel markers disabled or already started. */ +# ifdef CAN_HANDLE_FORK + if (GC_parallel) return; + + /* Initialize mark_cv (for the first time), or cleanup its value */ + /* after forking in the child process. All the marker threads in */ + /* the parent process were blocked on this variable at fork, so */ + /* pthread_cond_wait() malfunction (hang) is possible in the */ + /* child process without such a cleanup. */ + /* TODO: This is not portable, it is better to shortly unblock */ + /* all marker threads in the parent process at fork. */ + { + pthread_cond_t mark_cv_local = PTHREAD_COND_INITIALIZER; + BCOPY(&mark_cv_local, &mark_cv, sizeof(mark_cv)); + } +# endif + + GC_ASSERT(GC_fl_builder_count == 0); + INIT_REAL_SYMS(); /* for pthread_create */ + if (0 != pthread_attr_init(&attr)) ABORT("pthread_attr_init failed"); + if (0 != pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) + ABORT("pthread_attr_setdetachstate failed"); + +# ifdef DEFAULT_STACK_MAYBE_SMALL + /* Default stack size is usually too small: increase it. */ + /* Otherwise marker threads or GC may run out of space. */ + { + size_t old_size; + + if (pthread_attr_getstacksize(&attr, &old_size) != 0) + ABORT("pthread_attr_getstacksize failed"); + if (old_size < MIN_STACK_SIZE + && old_size != 0 /* stack size is known */) { + if (pthread_attr_setstacksize(&attr, MIN_STACK_SIZE) != 0) + ABORT("pthread_attr_setstacksize failed"); + } + } +# endif /* DEFAULT_STACK_MAYBE_SMALL */ + +# ifndef NO_MARKER_SPECIAL_SIGMASK + /* Apply special signal mask to GC marker threads, and don't drop */ + /* user defined signals by GC marker threads. */ + if (sigfillset(&set) != 0) + ABORT("sigfillset failed"); + +# if !defined(GC_DARWIN_THREADS) && !defined(GC_OPENBSD_UTHREADS) \ + && !defined(NACL) + /* These are used by GC to stop and restart the world. */ + if (sigdelset(&set, GC_get_suspend_signal()) != 0 + || sigdelset(&set, GC_get_thr_restart_signal()) != 0) + ABORT("sigdelset failed"); +# endif + + if (pthread_sigmask(SIG_BLOCK, &set, &oldset) < 0) { + WARN("pthread_sigmask set failed, no markers started," + " errno = %" WARN_PRIdPTR "\n", errno); + GC_markers_m1 = 0; + (void)pthread_attr_destroy(&attr); + return; + } +# endif /* !NO_MARKER_SPECIAL_SIGMASK */ + +# ifdef CAN_HANDLE_FORK + /* To have proper GC_parallel value in GC_help_marker. */ + GC_markers_m1 = available_markers_m1; +# endif + for (i = 0; i < available_markers_m1; ++i) { + if (0 != REAL_FUNC(pthread_create)(GC_mark_threads + i, &attr, + GC_mark_thread, (void *)(word)i)) { + WARN("Marker thread creation failed, errno = %" WARN_PRIdPTR "\n", + errno); + /* Don't try to create other marker threads. */ + GC_markers_m1 = i; + break; + } + } + +# ifndef NO_MARKER_SPECIAL_SIGMASK + /* Restore previous signal mask. */ + if (pthread_sigmask(SIG_SETMASK, &oldset, NULL) < 0) { + WARN("pthread_sigmask restore failed, errno = %" WARN_PRIdPTR "\n", + errno); + } +# endif + + (void)pthread_attr_destroy(&attr); + GC_wait_for_markers_init(); + GC_COND_LOG_PRINTF("Started %d mark helper threads\n", GC_markers_m1); +} + +#endif /* PARALLEL_MARK */ + +GC_INNER GC_bool GC_thr_initialized = FALSE; + +GC_INNER volatile GC_thread GC_threads[THREAD_TABLE_SZ] = {0}; + +void GC_push_thread_structures(void) +{ + GC_ASSERT(I_HOLD_LOCK()); + GC_PUSH_ALL_SYM(GC_threads); +# if defined(THREAD_LOCAL_ALLOC) + GC_PUSH_ALL_SYM(GC_thread_key); +# endif +} + +#ifdef DEBUG_THREADS + STATIC int GC_count_threads(void) + { + int i; + int count = 0; + GC_ASSERT(I_HOLD_LOCK()); + for (i = 0; i < THREAD_TABLE_SZ; ++i) { + GC_thread th = GC_threads[i]; + while (th) { + if (!(th->flags & FINISHED)) + ++count; + th = th->next; + } + } + return count; + } +#endif /* DEBUG_THREADS */ + +/* It may not be safe to allocate when we register the first thread. */ +/* As "next" and "status" fields are unused, no need to push this. */ +static struct GC_Thread_Rep first_thread; + +/* Add a thread to GC_threads. We assume it wasn't already there. */ +/* Caller holds allocation lock. */ +STATIC GC_thread GC_new_thread(pthread_t id) +{ + int hv = THREAD_TABLE_INDEX(id); + GC_thread result; + static GC_bool first_thread_used = FALSE; + +# ifdef DEBUG_THREADS + GC_log_printf("Creating thread %p\n", (void *)id); + for (result = GC_threads[hv]; result != NULL; result = result->next) + if (!THREAD_EQUAL(result->id, id)) { + GC_log_printf("Hash collision at GC_threads[%d]\n", hv); + break; + } +# endif + GC_ASSERT(I_HOLD_LOCK()); + if (!EXPECT(first_thread_used, TRUE)) { + result = &first_thread; + first_thread_used = TRUE; + GC_ASSERT(NULL == GC_threads[hv]); +# if defined(THREAD_SANITIZER) && defined(CPPCHECK) + GC_noop1(result->dummy[0]); +# endif + } else { + result = (struct GC_Thread_Rep *) + GC_INTERNAL_MALLOC(sizeof(struct GC_Thread_Rep), NORMAL); + if (result == 0) return(0); + } + result -> id = id; +# ifdef USE_TKILL_ON_ANDROID + result -> kernel_id = gettid(); +# endif + result -> next = GC_threads[hv]; + GC_threads[hv] = result; +# ifdef NACL + GC_nacl_gc_thread_self = result; + GC_nacl_initialize_gc_thread(); +# endif + GC_ASSERT(result -> flags == 0 && result -> thread_blocked == 0); + if (EXPECT(result != &first_thread, TRUE)) + GC_dirty(result); + return(result); +} + +/* Delete a thread from GC_threads. We assume it is there. */ +/* (The code intentionally traps if it wasn't.) */ +/* It is safe to delete the main thread. */ +STATIC void GC_delete_thread(pthread_t id) +{ + int hv = THREAD_TABLE_INDEX(id); + GC_thread p = GC_threads[hv]; + GC_thread prev = NULL; + +# ifdef DEBUG_THREADS + GC_log_printf("Deleting thread %p, n_threads = %d\n", + (void *)id, GC_count_threads()); +# endif + +# ifdef NACL + GC_nacl_shutdown_gc_thread(); + GC_nacl_gc_thread_self = NULL; +# endif + + GC_ASSERT(I_HOLD_LOCK()); + while (!THREAD_EQUAL(p -> id, id)) { + prev = p; + p = p -> next; + } + if (prev == 0) { + GC_threads[hv] = p -> next; + } else { + GC_ASSERT(prev != &first_thread); + prev -> next = p -> next; + GC_dirty(prev); + } + if (p != &first_thread) { +# ifdef GC_DARWIN_THREADS + mach_port_deallocate(mach_task_self(), p->stop_info.mach_thread); +# endif + GC_INTERNAL_FREE(p); + } +} + +/* If a thread has been joined, but we have not yet */ +/* been notified, then there may be more than one thread */ +/* in the table with the same pthread id. */ +/* This is OK, but we need a way to delete a specific one. */ +STATIC void GC_delete_gc_thread(GC_thread t) +{ + pthread_t id = t -> id; + int hv = THREAD_TABLE_INDEX(id); + GC_thread p = GC_threads[hv]; + GC_thread prev = NULL; + + GC_ASSERT(I_HOLD_LOCK()); + while (p != t) { + prev = p; + p = p -> next; + } + if (prev == 0) { + GC_threads[hv] = p -> next; + } else { + GC_ASSERT(prev != &first_thread); + prev -> next = p -> next; + GC_dirty(prev); + } +# ifdef GC_DARWIN_THREADS + mach_port_deallocate(mach_task_self(), p->stop_info.mach_thread); +# endif + GC_INTERNAL_FREE(p); + +# ifdef DEBUG_THREADS + GC_log_printf("Deleted thread %p, n_threads = %d\n", + (void *)id, GC_count_threads()); +# endif +} + +/* Return a GC_thread corresponding to a given pthread_t. */ +/* Returns 0 if it's not there. */ +/* Caller holds allocation lock or otherwise inhibits */ +/* updates. */ +/* If there is more than one thread with the given id we */ +/* return the most recent one. */ +GC_INNER GC_thread GC_lookup_thread(pthread_t id) +{ + GC_thread p = GC_threads[THREAD_TABLE_INDEX(id)]; + + while (p != 0 && !THREAD_EQUAL(p -> id, id)) p = p -> next; + return(p); +} + +/* Called by GC_finalize() (in case of an allocation failure observed). */ +GC_INNER void GC_reset_finalizer_nested(void) +{ + GC_thread me = GC_lookup_thread(pthread_self()); + me->finalizer_nested = 0; +} + +/* Checks and updates the thread-local level of finalizers recursion. */ +/* Returns NULL if GC_invoke_finalizers() should not be called by the */ +/* collector (to minimize the risk of a deep finalizers recursion), */ +/* otherwise returns a pointer to the thread-local finalizer_nested. */ +/* Called by GC_notify_or_invoke_finalizers() only (the lock is held). */ +GC_INNER unsigned char *GC_check_finalizer_nested(void) +{ + GC_thread me = GC_lookup_thread(pthread_self()); + unsigned nesting_level = me->finalizer_nested; + if (nesting_level) { + /* We are inside another GC_invoke_finalizers(). */ + /* Skip some implicitly-called GC_invoke_finalizers() */ + /* depending on the nesting (recursion) level. */ + if (++me->finalizer_skipped < (1U << nesting_level)) return NULL; + me->finalizer_skipped = 0; + } + me->finalizer_nested = (unsigned char)(nesting_level + 1); + return &me->finalizer_nested; +} + +#if defined(GC_ASSERTIONS) && defined(THREAD_LOCAL_ALLOC) + /* This is called from thread-local GC_malloc(). */ + GC_bool GC_is_thread_tsd_valid(void *tsd) + { + GC_thread me; + DCL_LOCK_STATE; + + LOCK(); + me = GC_lookup_thread(pthread_self()); + UNLOCK(); + return (word)tsd >= (word)(&me->tlfs) + && (word)tsd < (word)(&me->tlfs) + sizeof(me->tlfs); + } +#endif /* GC_ASSERTIONS && THREAD_LOCAL_ALLOC */ + +GC_API int GC_CALL GC_thread_is_registered(void) +{ + pthread_t self = pthread_self(); + GC_thread me; + DCL_LOCK_STATE; + + LOCK(); + me = GC_lookup_thread(self); + UNLOCK(); + return me != NULL; +} + +static pthread_t main_pthread_id; +static void *main_stack, *main_altstack; +static word main_stack_size, main_altstack_size; + +GC_API void GC_CALL GC_register_altstack(void *stack, GC_word stack_size, + void *altstack, + GC_word altstack_size) +{ + GC_thread me; + pthread_t self = pthread_self(); + DCL_LOCK_STATE; + + LOCK(); + me = GC_lookup_thread(self); + if (me != NULL) { + me->stack = (ptr_t)stack; + me->stack_size = stack_size; + me->altstack = (ptr_t)altstack; + me->altstack_size = altstack_size; + } else { + /* This happens if we are called before GC_thr_init. */ + main_pthread_id = self; + main_stack = stack; + main_stack_size = stack_size; + main_altstack = altstack; + main_altstack_size = altstack_size; + } + UNLOCK(); +} + +#ifdef CAN_HANDLE_FORK + + /* Prevent TSan false positive about the race during items removal */ + /* from GC_threads. (The race cannot happen since only one thread */ + /* survives in the child.) */ +# ifdef CAN_CALL_ATFORK + GC_ATTR_NO_SANITIZE_THREAD +# endif + static void store_to_threads_table(int hv, GC_thread me) + { + GC_threads[hv] = me; + } + +/* Remove all entries from the GC_threads table, except the */ +/* one for the current thread. We need to do this in the child */ +/* process after a fork(), since only the current thread */ +/* survives in the child. */ +STATIC void GC_remove_all_threads_but_me(void) +{ + pthread_t self = pthread_self(); + int hv; + GC_thread p, next, me; + + for (hv = 0; hv < THREAD_TABLE_SZ; ++hv) { + me = 0; + for (p = GC_threads[hv]; 0 != p; p = next) { + next = p -> next; + if (THREAD_EQUAL(p -> id, self) + && me == NULL) { /* ignore dead threads with the same id */ + me = p; + p -> next = 0; +# ifdef GC_DARWIN_THREADS + /* Update thread Id after fork (it is OK to call */ + /* GC_destroy_thread_local and GC_free_internal */ + /* before update). */ + me -> stop_info.mach_thread = mach_thread_self(); +# endif +# ifdef USE_TKILL_ON_ANDROID + me -> kernel_id = gettid(); +# endif +# if defined(THREAD_LOCAL_ALLOC) && !defined(USE_CUSTOM_SPECIFIC) + { + int res; + + /* Some TLS implementations might be not fork-friendly, so */ + /* we re-assign thread-local pointer to 'tlfs' for safety */ + /* instead of the assertion check (again, it is OK to call */ + /* GC_destroy_thread_local and GC_free_internal before). */ + res = GC_setspecific(GC_thread_key, &me->tlfs); + if (COVERT_DATAFLOW(res) != 0) + ABORT("GC_setspecific failed (in child)"); + } +# endif + } else { +# ifdef THREAD_LOCAL_ALLOC + if (!(p -> flags & FINISHED)) { + /* Cannot call GC_destroy_thread_local here. The free */ + /* lists may be in an inconsistent state (as thread p may */ + /* be updating one of the lists by GC_generic_malloc_many */ + /* or GC_FAST_MALLOC_GRANS when fork is invoked). */ + /* This should not be a problem because the lost elements */ + /* of the free lists will be collected during GC. */ + GC_remove_specific_after_fork(GC_thread_key, p -> id); + } +# endif + /* TODO: To avoid TSan hang (when updating GC_bytes_freed), */ + /* we just skip explicit freeing of GC_threads entries. */ +# if !defined(THREAD_SANITIZER) || !defined(CAN_CALL_ATFORK) + if (p != &first_thread) GC_INTERNAL_FREE(p); +# endif + } + } + store_to_threads_table(hv, me); + } +} +#endif /* CAN_HANDLE_FORK */ + +#ifdef USE_PROC_FOR_LIBRARIES + GC_INNER GC_bool GC_segment_is_thread_stack(ptr_t lo, ptr_t hi) + { + int i; + GC_thread p; + + GC_ASSERT(I_HOLD_LOCK()); +# ifdef PARALLEL_MARK + for (i = 0; i < GC_markers_m1; ++i) { + if ((word)marker_sp[i] > (word)lo && (word)marker_sp[i] < (word)hi) + return TRUE; +# ifdef IA64 + if ((word)marker_bsp[i] > (word)lo + && (word)marker_bsp[i] < (word)hi) + return TRUE; +# endif + } +# endif + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (p = GC_threads[i]; p != 0; p = p -> next) { + if (0 != p -> stack_end) { +# ifdef STACK_GROWS_UP + if ((word)p->stack_end >= (word)lo + && (word)p->stack_end < (word)hi) + return TRUE; +# else /* STACK_GROWS_DOWN */ + if ((word)p->stack_end > (word)lo + && (word)p->stack_end <= (word)hi) + return TRUE; +# endif + } + } + } + return FALSE; + } +#endif /* USE_PROC_FOR_LIBRARIES */ + +#ifdef IA64 + /* Find the largest stack_base smaller than bound. May be used */ + /* to find the boundary between a register stack and adjacent */ + /* immediately preceding memory stack. */ + GC_INNER ptr_t GC_greatest_stack_base_below(ptr_t bound) + { + int i; + GC_thread p; + ptr_t result = 0; + + GC_ASSERT(I_HOLD_LOCK()); +# ifdef PARALLEL_MARK + for (i = 0; i < GC_markers_m1; ++i) { + if ((word)marker_sp[i] > (word)result + && (word)marker_sp[i] < (word)bound) + result = marker_sp[i]; + } +# endif + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (p = GC_threads[i]; p != 0; p = p -> next) { + if ((word)p->stack_end > (word)result + && (word)p->stack_end < (word)bound) { + result = p -> stack_end; + } + } + } + return result; + } +#endif /* IA64 */ + +#ifndef STAT_READ + /* Also defined in os_dep.c. */ +# define STAT_BUF_SIZE 4096 +# define STAT_READ read + /* If read is wrapped, this may need to be redefined to call */ + /* the real one. */ +#endif + +#ifdef GC_HPUX_THREADS +# define GC_get_nprocs() pthread_num_processors_np() + +#elif defined(GC_OSF1_THREADS) || defined(GC_AIX_THREADS) \ + || defined(GC_HAIKU_THREADS) || defined(GC_SOLARIS_THREADS) \ + || defined(HURD) || defined(HOST_ANDROID) || defined(NACL) + GC_INLINE int GC_get_nprocs(void) + { + int nprocs = (int)sysconf(_SC_NPROCESSORS_ONLN); + return nprocs > 0 ? nprocs : 1; /* ignore error silently */ + } + +#elif defined(GC_IRIX_THREADS) + GC_INLINE int GC_get_nprocs(void) + { + int nprocs = (int)sysconf(_SC_NPROC_ONLN); + return nprocs > 0 ? nprocs : 1; /* ignore error silently */ + } + +#elif defined(GC_LINUX_THREADS) /* && !HOST_ANDROID && !NACL */ + /* Return the number of processors. */ + STATIC int GC_get_nprocs(void) + { + /* Should be "return sysconf(_SC_NPROCESSORS_ONLN);" but that */ + /* appears to be buggy in many cases. */ + /* We look for lines "cpu" in /proc/stat. */ + char stat_buf[STAT_BUF_SIZE]; + int f; + int result, i, len; + + f = open("/proc/stat", O_RDONLY); + if (f < 0) { + WARN("Couldn't read /proc/stat\n", 0); + return 1; /* assume an uniprocessor */ + } + len = STAT_READ(f, stat_buf, STAT_BUF_SIZE); + close(f); + + result = 1; + /* Some old kernels only have a single "cpu nnnn ..." */ + /* entry in /proc/stat. We identify those as */ + /* uniprocessors. */ + + for (i = 0; i < len - 100; ++i) { + if (stat_buf[i] == '\n' && stat_buf[i+1] == 'c' + && stat_buf[i+2] == 'p' && stat_buf[i+3] == 'u') { + int cpu_no = atoi(&stat_buf[i + 4]); + if (cpu_no >= result) + result = cpu_no + 1; + } + } + return result; + } + +#elif defined(GC_DGUX386_THREADS) + /* Return the number of processors, or i <= 0 if it can't be determined. */ + STATIC int GC_get_nprocs(void) + { + int numCpus; + struct dg_sys_info_pm_info pm_sysinfo; + int status = 0; + + status = dg_sys_info((long int *) &pm_sysinfo, + DG_SYS_INFO_PM_INFO_TYPE, DG_SYS_INFO_PM_CURRENT_VERSION); + if (status < 0) + /* set -1 for error */ + numCpus = -1; + else + /* Active CPUs */ + numCpus = pm_sysinfo.idle_vp_count; + return(numCpus); + } + +#elif defined(GC_DARWIN_THREADS) || defined(GC_FREEBSD_THREADS) \ + || defined(GC_NETBSD_THREADS) || defined(GC_OPENBSD_THREADS) + STATIC int GC_get_nprocs(void) + { + int mib[] = {CTL_HW,HW_NCPU}; + int res; + size_t len = sizeof(res); + + sysctl(mib, sizeof(mib)/sizeof(int), &res, &len, NULL, 0); + return res; + } + +#else + /* E.g., GC_RTEMS_PTHREADS */ +# define GC_get_nprocs() 1 /* not implemented */ +#endif /* !GC_LINUX_THREADS && !GC_DARWIN_THREADS && ... */ + +#if defined(ARM32) && defined(GC_LINUX_THREADS) && !defined(NACL) + /* Some buggy Linux/arm kernels show only non-sleeping CPUs in */ + /* /proc/stat (and /proc/cpuinfo), so another data system source is */ + /* tried first. Result <= 0 on error. */ + STATIC int GC_get_nprocs_present(void) + { + char stat_buf[16]; + int f; + int len; + + f = open("/sys/devices/system/cpu/present", O_RDONLY); + if (f < 0) + return -1; /* cannot open the file */ + + len = STAT_READ(f, stat_buf, sizeof(stat_buf)); + close(f); + + /* Recognized file format: "0\n" or "0-\n" */ + /* The file might probably contain a comma-separated list */ + /* but we do not need to handle it (just silently ignore). */ + if (len < 2 || stat_buf[0] != '0' || stat_buf[len - 1] != '\n') { + return 0; /* read error or unrecognized content */ + } else if (len == 2) { + return 1; /* an uniprocessor */ + } else if (stat_buf[1] != '-') { + return 0; /* unrecognized content */ + } + + stat_buf[len - 1] = '\0'; /* terminate the string */ + return atoi(&stat_buf[2]) + 1; /* skip "0-" and parse max_cpu_num */ + } +#endif /* ARM32 && GC_LINUX_THREADS && !NACL */ + +/* We hold the GC lock. Wait until an in-progress GC has finished. */ +/* Repeatedly RELEASES GC LOCK in order to wait. */ +/* If wait_for_all is true, then we exit with the GC lock held and no */ +/* collection in progress; otherwise we just wait for the current GC */ +/* to finish. */ +STATIC void GC_wait_for_gc_completion(GC_bool wait_for_all) +{ + DCL_LOCK_STATE; +# if !defined(THREAD_SANITIZER) || !defined(CAN_CALL_ATFORK) + /* GC_lock_holder is accessed with the lock held, so there is no */ + /* data race actually (unlike what is reported by TSan). */ + GC_ASSERT(I_HOLD_LOCK()); +# endif + ASSERT_CANCEL_DISABLED(); + if (GC_incremental && GC_collection_in_progress()) { + word old_gc_no = GC_gc_no; + + /* Make sure that no part of our stack is still on the mark stack, */ + /* since it's about to be unmapped. */ + while (GC_incremental && GC_collection_in_progress() + && (wait_for_all || old_gc_no == GC_gc_no)) { + ENTER_GC(); + GC_in_thread_creation = TRUE; + GC_collect_a_little_inner(1); + GC_in_thread_creation = FALSE; + EXIT_GC(); + UNLOCK(); + sched_yield(); + LOCK(); + } + } +} + +#ifdef CAN_HANDLE_FORK +/* Procedures called before and after a fork. The goal here is to make */ +/* it safe to call GC_malloc() in a forked child. It's unclear that is */ +/* attainable, since the single UNIX spec seems to imply that one */ +/* should only call async-signal-safe functions, and we probably can't */ +/* quite guarantee that. But we give it our best shot. (That same */ +/* spec also implies that it's not safe to call the system malloc */ +/* between fork() and exec(). Thus we're doing no worse than it.) */ + +IF_CANCEL(static int fork_cancel_state;) + /* protected by allocation lock. */ + +/* Called before a fork() */ +#if defined(GC_ASSERTIONS) && defined(CAN_CALL_ATFORK) + /* GC_lock_holder is updated safely (no data race actually). */ + GC_ATTR_NO_SANITIZE_THREAD +#endif +static void fork_prepare_proc(void) +{ + /* Acquire all relevant locks, so that after releasing the locks */ + /* the child will see a consistent state in which monitor */ + /* invariants hold. Unfortunately, we can't acquire libc locks */ + /* we might need, and there seems to be no guarantee that libc */ + /* must install a suitable fork handler. */ + /* Wait for an ongoing GC to finish, since we can't finish it in */ + /* the (one remaining thread in) the child. */ + LOCK(); + DISABLE_CANCEL(fork_cancel_state); + /* Following waits may include cancellation points. */ +# if defined(PARALLEL_MARK) + if (GC_parallel) + GC_wait_for_reclaim(); +# endif + GC_wait_for_gc_completion(TRUE); +# if defined(PARALLEL_MARK) + if (GC_parallel) + GC_acquire_mark_lock(); +# endif +} + +/* Called in parent after a fork() (even if the latter failed). */ +#if defined(GC_ASSERTIONS) && defined(CAN_CALL_ATFORK) + GC_ATTR_NO_SANITIZE_THREAD +#endif +static void fork_parent_proc(void) +{ +# if defined(PARALLEL_MARK) + if (GC_parallel) + GC_release_mark_lock(); +# endif + RESTORE_CANCEL(fork_cancel_state); + UNLOCK(); +} + +/* Called in child after a fork() */ +#if defined(GC_ASSERTIONS) && defined(CAN_CALL_ATFORK) + GC_ATTR_NO_SANITIZE_THREAD +#endif +static void fork_child_proc(void) +{ + /* Clean up the thread table, so that just our thread is left. */ +# if defined(PARALLEL_MARK) + if (GC_parallel) + GC_release_mark_lock(); +# endif + GC_remove_all_threads_but_me(); +# ifdef PARALLEL_MARK + /* Turn off parallel marking in the child, since we are probably */ + /* just going to exec, and we would have to restart mark threads. */ + GC_parallel = FALSE; +# endif /* PARALLEL_MARK */ + RESTORE_CANCEL(fork_cancel_state); + UNLOCK(); + /* Even though after a fork the child only inherits the single */ + /* thread that called the fork(), if another thread in the parent */ + /* was attempting to lock the mutex while being held in */ + /* fork_child_prepare(), the mutex will be left in an inconsistent */ + /* state in the child after the UNLOCK. This is the case, at */ + /* least, in Mac OS X and leads to an unusable GC in the child */ + /* which will block when attempting to perform any GC operation */ + /* that acquires the allocation mutex. */ +# ifdef USE_PTHREAD_LOCKS + GC_ASSERT(I_DONT_HOLD_LOCK()); + /* Reinitialize the mutex. It should be safe since we are */ + /* running this in the child which only inherits a single thread. */ + /* mutex_destroy() may return EBUSY, which makes no sense, but */ + /* that is the reason for the need of the reinitialization. */ + (void)pthread_mutex_destroy(&GC_allocate_ml); + /* TODO: Probably some targets might need the default mutex */ + /* attribute to be passed instead of NULL. */ + if (0 != pthread_mutex_init(&GC_allocate_ml, NULL)) + ABORT("pthread_mutex_init failed (in child)"); +# endif +} + + /* Routines for fork handling by client (no-op if pthread_atfork works). */ + GC_API void GC_CALL GC_atfork_prepare(void) + { + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); +# if defined(GC_DARWIN_THREADS) && defined(MPROTECT_VDB) + if (GC_incremental) { + GC_ASSERT(0 == GC_handle_fork); + ABORT("Unable to fork while mprotect_thread is running"); + } +# endif + if (GC_handle_fork <= 0) + fork_prepare_proc(); + } + + GC_API void GC_CALL GC_atfork_parent(void) + { + if (GC_handle_fork <= 0) + fork_parent_proc(); + } + + GC_API void GC_CALL GC_atfork_child(void) + { + if (GC_handle_fork <= 0) + fork_child_proc(); + } +#endif /* CAN_HANDLE_FORK */ + +#ifdef INCLUDE_LINUX_THREAD_DESCR + __thread int GC_dummy_thread_local; +#endif + +#ifdef PARALLEL_MARK + static void setup_mark_lock(void); +#endif + +GC_INNER void GC_thr_init(void) +{ + GC_ASSERT(I_HOLD_LOCK()); + if (GC_thr_initialized) return; + GC_thr_initialized = TRUE; + + GC_ASSERT((word)&GC_threads % sizeof(word) == 0); +# ifdef CAN_HANDLE_FORK + /* Prepare for forks if requested. */ + if (GC_handle_fork) { +# ifdef CAN_CALL_ATFORK + if (pthread_atfork(fork_prepare_proc, fork_parent_proc, + fork_child_proc) == 0) { + /* Handlers successfully registered. */ + GC_handle_fork = 1; + } else +# endif + /* else */ if (GC_handle_fork != -1) + ABORT("pthread_atfork failed"); + } +# endif +# ifdef INCLUDE_LINUX_THREAD_DESCR + /* Explicitly register the region including the address */ + /* of a thread local variable. This should include thread */ + /* locals for the main thread, except for those allocated */ + /* in response to dlopen calls. */ + { + ptr_t thread_local_addr = (ptr_t)(&GC_dummy_thread_local); + ptr_t main_thread_start, main_thread_end; + if (!GC_enclosing_mapping(thread_local_addr, &main_thread_start, + &main_thread_end)) { + ABORT("Failed to find mapping for main thread thread locals"); + } else { + /* main_thread_start and main_thread_end are initialized. */ + GC_add_roots_inner(main_thread_start, main_thread_end, FALSE); + } + } +# endif + /* Add the initial thread, so we can stop it. */ + { + pthread_t self = pthread_self(); + GC_thread t = GC_new_thread(self); + + if (t == NULL) + ABORT("Failed to allocate memory for the initial thread"); +# ifdef GC_DARWIN_THREADS + t -> stop_info.mach_thread = mach_thread_self(); +# else + t -> stop_info.stack_ptr = GC_approx_sp(); +# endif + t -> flags = DETACHED | MAIN_THREAD; + if (THREAD_EQUAL(self, main_pthread_id)) { + t -> stack = (ptr_t)main_stack; + t -> stack_size = main_stack_size; + t -> altstack = (ptr_t)main_altstack; + t -> altstack_size = main_altstack_size; + } + } + +# ifndef GC_DARWIN_THREADS + GC_stop_init(); +# endif + + /* Set GC_nprocs. */ + { + char * nprocs_string = GETENV("GC_NPROCS"); + GC_nprocs = -1; + if (nprocs_string != NULL) GC_nprocs = atoi(nprocs_string); + } + if (GC_nprocs <= 0 +# if defined(ARM32) && defined(GC_LINUX_THREADS) && !defined(NACL) + && (GC_nprocs = GC_get_nprocs_present()) <= 1 + /* Workaround for some Linux/arm kernels */ +# endif + ) + { + GC_nprocs = GC_get_nprocs(); + } + if (GC_nprocs <= 0) { + WARN("GC_get_nprocs() returned %" WARN_PRIdPTR "\n", GC_nprocs); + GC_nprocs = 2; /* assume dual-core */ +# ifdef PARALLEL_MARK + available_markers_m1 = 0; /* but use only one marker */ +# endif + } else { +# ifdef PARALLEL_MARK + { + char * markers_string = GETENV("GC_MARKERS"); + int markers; + + if (markers_string != NULL) { + markers = atoi(markers_string); + if (markers <= 0 || markers > MAX_MARKERS) { + WARN("Too big or invalid number of mark threads: %" WARN_PRIdPTR + "; using maximum threads\n", (signed_word)markers); + markers = MAX_MARKERS; + } + } else { + markers = GC_nprocs; +# if defined(GC_MIN_MARKERS) && !defined(CPPCHECK) + /* This is primarily for targets without getenv(). */ + if (markers < GC_MIN_MARKERS) + markers = GC_MIN_MARKERS; +# endif + if (markers > MAX_MARKERS) + markers = MAX_MARKERS; /* silently limit the value */ + } + available_markers_m1 = markers - 1; + } +# endif + } + GC_COND_LOG_PRINTF("Number of processors = %d\n", GC_nprocs); +# ifdef PARALLEL_MARK + if (available_markers_m1 <= 0) { + /* Disable parallel marking. */ + GC_parallel = FALSE; + GC_COND_LOG_PRINTF( + "Single marker thread, turning off parallel marking\n"); + } else { + /* Disable true incremental collection, but generational is OK. */ + GC_time_limit = GC_TIME_UNLIMITED; + setup_mark_lock(); + } +# endif +} + +/* Perform all initializations, including those that */ +/* may require allocation. */ +/* Called without allocation lock. */ +/* Must be called before a second thread is created. */ +/* Did we say it's called without the allocation lock? */ +GC_INNER void GC_init_parallel(void) +{ +# if defined(THREAD_LOCAL_ALLOC) + DCL_LOCK_STATE; +# endif + if (parallel_initialized) return; + parallel_initialized = TRUE; + + /* GC_init() calls us back, so set flag first. */ + if (!GC_is_initialized) GC_init(); + /* Initialize thread local free lists if used. */ +# if defined(THREAD_LOCAL_ALLOC) + LOCK(); + GC_init_thread_local(&(GC_lookup_thread(pthread_self())->tlfs)); + UNLOCK(); +# endif +} + +#ifndef GC_NO_PTHREAD_SIGMASK + GC_API int WRAP_FUNC(pthread_sigmask)(int how, const sigset_t *set, + sigset_t *oset) + { + sigset_t fudged_set; + + INIT_REAL_SYMS(); + if (set != NULL && (how == SIG_BLOCK || how == SIG_SETMASK)) { + int sig_suspend = GC_get_suspend_signal(); + + fudged_set = *set; + GC_ASSERT(sig_suspend >= 0); + if (sigdelset(&fudged_set, sig_suspend) != 0) + ABORT("sigdelset failed"); + set = &fudged_set; + } + return(REAL_FUNC(pthread_sigmask)(how, set, oset)); + } +#endif /* !GC_NO_PTHREAD_SIGMASK */ + +/* Wrapper for functions that are likely to block for an appreciable */ +/* length of time. */ + +GC_INNER void GC_do_blocking_inner(ptr_t data, void * context GC_ATTR_UNUSED) +{ + struct blocking_data * d = (struct blocking_data *) data; + pthread_t self = pthread_self(); + GC_thread me; +# if defined(SPARC) || defined(IA64) + ptr_t stack_ptr = GC_save_regs_in_stack(); +# endif +# if defined(GC_DARWIN_THREADS) && !defined(DARWIN_DONT_PARSE_STACK) + GC_bool topOfStackUnset = FALSE; +# endif + DCL_LOCK_STATE; + + LOCK(); + me = GC_lookup_thread(self); + GC_ASSERT(!(me -> thread_blocked)); +# ifdef SPARC + me -> stop_info.stack_ptr = stack_ptr; +# else + me -> stop_info.stack_ptr = GC_approx_sp(); +# endif +# if defined(GC_DARWIN_THREADS) && !defined(DARWIN_DONT_PARSE_STACK) + if (me -> topOfStack == NULL) { + /* GC_do_blocking_inner is not called recursively, */ + /* so topOfStack should be computed now. */ + topOfStackUnset = TRUE; + me -> topOfStack = GC_FindTopOfStack(0); + } +# endif +# ifdef IA64 + me -> backing_store_ptr = stack_ptr; +# endif + me -> thread_blocked = (unsigned char)TRUE; + /* Save context here if we want to support precise stack marking */ + UNLOCK(); + d -> client_data = (d -> fn)(d -> client_data); + LOCK(); /* This will block if the world is stopped. */ + me -> thread_blocked = FALSE; +# if defined(GC_DARWIN_THREADS) && !defined(DARWIN_DONT_PARSE_STACK) + if (topOfStackUnset) + me -> topOfStack = NULL; /* make topOfStack unset again */ +# endif + UNLOCK(); +} + +/* GC_call_with_gc_active() has the opposite to GC_do_blocking() */ +/* functionality. It might be called from a user function invoked by */ +/* GC_do_blocking() to temporarily back allow calling any GC function */ +/* and/or manipulating pointers to the garbage collected heap. */ +GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type fn, + void * client_data) +{ + struct GC_traced_stack_sect_s stacksect; + pthread_t self = pthread_self(); + GC_thread me; + DCL_LOCK_STATE; + + LOCK(); /* This will block if the world is stopped. */ + me = GC_lookup_thread(self); + + /* Adjust our stack base value (this could happen unless */ + /* GC_get_stack_base() was used which returned GC_SUCCESS). */ + if ((me -> flags & MAIN_THREAD) == 0) { + GC_ASSERT(me -> stack_end != NULL); + if ((word)me->stack_end HOTTER_THAN (word)(&stacksect)) + me -> stack_end = (ptr_t)(&stacksect); + } else { + /* The original stack. */ + if ((word)GC_stackbottom HOTTER_THAN (word)(&stacksect)) + GC_stackbottom = (ptr_t)(&stacksect); + } + + if (!me->thread_blocked) { + /* We are not inside GC_do_blocking() - do nothing more. */ + UNLOCK(); + client_data = fn(client_data); + /* Prevent treating the above as a tail call. */ + GC_noop1((word)(&stacksect)); + return client_data; /* result */ + } + + /* Setup new "stack section". */ + stacksect.saved_stack_ptr = me -> stop_info.stack_ptr; +# ifdef IA64 + /* This is the same as in GC_call_with_stack_base(). */ + stacksect.backing_store_end = GC_save_regs_in_stack(); + /* Unnecessarily flushes register stack, */ + /* but that probably doesn't hurt. */ + stacksect.saved_backing_store_ptr = me -> backing_store_ptr; +# endif + stacksect.prev = me -> traced_stack_sect; + me -> thread_blocked = FALSE; + me -> traced_stack_sect = &stacksect; + + UNLOCK(); + client_data = fn(client_data); + GC_ASSERT(me -> thread_blocked == FALSE); + GC_ASSERT(me -> traced_stack_sect == &stacksect); + + /* Restore original "stack section". */ + LOCK(); + me -> traced_stack_sect = stacksect.prev; +# ifdef IA64 + me -> backing_store_ptr = stacksect.saved_backing_store_ptr; +# endif + me -> thread_blocked = (unsigned char)TRUE; + me -> stop_info.stack_ptr = stacksect.saved_stack_ptr; + UNLOCK(); + + return client_data; /* result */ +} + +STATIC void GC_unregister_my_thread_inner(GC_thread me) +{ +# ifdef DEBUG_THREADS + GC_log_printf( + "Unregistering thread %p, gc_thread = %p, n_threads = %d\n", + (void *)me->id, (void *)me, GC_count_threads()); +# endif + GC_ASSERT(!(me -> flags & FINISHED)); +# if defined(THREAD_LOCAL_ALLOC) + GC_ASSERT(GC_getspecific(GC_thread_key) == &me->tlfs); + GC_destroy_thread_local(&(me->tlfs)); +# endif +# if defined(GC_HAVE_PTHREAD_EXIT) || !defined(GC_NO_PTHREAD_CANCEL) + /* Handle DISABLED_GC flag which is set by the */ + /* intercepted pthread_cancel or pthread_exit. */ + if ((me -> flags & DISABLED_GC) != 0) { + GC_dont_gc--; + } +# endif + if (me -> flags & DETACHED) { + GC_delete_thread(pthread_self()); + } else { + me -> flags |= FINISHED; + } +# if defined(THREAD_LOCAL_ALLOC) + /* It is required to call remove_specific defined in specific.c. */ + GC_remove_specific(GC_thread_key); +# endif +} + +GC_API int GC_CALL GC_unregister_my_thread(void) +{ + pthread_t self = pthread_self(); + GC_thread me; + IF_CANCEL(int cancel_state;) + DCL_LOCK_STATE; + + LOCK(); + DISABLE_CANCEL(cancel_state); + /* Wait for any GC that may be marking from our stack to */ + /* complete before we remove this thread. */ + GC_wait_for_gc_completion(FALSE); + me = GC_lookup_thread(self); +# ifdef DEBUG_THREADS + GC_log_printf( + "Called GC_unregister_my_thread on %p, gc_thread = %p\n", + (void *)self, (void *)me); +# endif + GC_ASSERT(THREAD_EQUAL(me->id, self)); + GC_unregister_my_thread_inner(me); + RESTORE_CANCEL(cancel_state); + UNLOCK(); + return GC_SUCCESS; +} + +/* Called at thread exit. */ +/* Never called for main thread. That's OK, since it */ +/* results in at most a tiny one-time leak. And */ +/* linuxthreads doesn't reclaim the main threads */ +/* resources or id anyway. */ +GC_INNER_PTHRSTART void GC_thread_exit_proc(void *arg) +{ + IF_CANCEL(int cancel_state;) + DCL_LOCK_STATE; + +# ifdef DEBUG_THREADS + GC_log_printf("Called GC_thread_exit_proc on %p, gc_thread = %p\n", + (void *)((GC_thread)arg)->id, arg); +# endif + LOCK(); + DISABLE_CANCEL(cancel_state); + GC_wait_for_gc_completion(FALSE); + GC_unregister_my_thread_inner((GC_thread)arg); + RESTORE_CANCEL(cancel_state); + UNLOCK(); +} + +#if !defined(SN_TARGET_ORBIS) && !defined(SN_TARGET_PSP2) + GC_API int WRAP_FUNC(pthread_join)(pthread_t thread, void **retval) + { + int result; + GC_thread t; + DCL_LOCK_STATE; + + INIT_REAL_SYMS(); + LOCK(); + t = GC_lookup_thread(thread); + /* This is guaranteed to be the intended one, since the thread id */ + /* can't have been recycled by pthreads. */ + UNLOCK(); + result = REAL_FUNC(pthread_join)(thread, retval); +# if defined(GC_FREEBSD_THREADS) + /* On FreeBSD, the wrapped pthread_join() sometimes returns (what + appears to be) a spurious EINTR which caused the test and real code + to gratuitously fail. Having looked at system pthread library source + code, I see how this return code may be generated. In one path of + code, pthread_join() just returns the errno setting of the thread + being joined. This does not match the POSIX specification or the + local man pages thus I have taken the liberty to catch this one + spurious return value properly conditionalized on GC_FREEBSD_THREADS. */ + if (result == EINTR) result = 0; +# endif + if (result == 0) { + LOCK(); + /* Here the pthread thread id may have been recycled. */ + /* Delete the thread from GC_threads (unless it has been */ + /* registered again from the client thread key destructor). */ + if ((t -> flags & FINISHED) != 0) + GC_delete_gc_thread(t); + UNLOCK(); + } + return result; + } + + GC_API int WRAP_FUNC(pthread_detach)(pthread_t thread) + { + int result; + GC_thread t; + DCL_LOCK_STATE; + + INIT_REAL_SYMS(); + LOCK(); + t = GC_lookup_thread(thread); + UNLOCK(); + result = REAL_FUNC(pthread_detach)(thread); + if (result == 0) { + LOCK(); + t -> flags |= DETACHED; + /* Here the pthread thread id may have been recycled. */ + if ((t -> flags & FINISHED) != 0) { + GC_delete_gc_thread(t); + } + UNLOCK(); + } + return result; + } +#endif /* !SN_TARGET_ORBIS && !SN_TARGET_PSP2 */ + +#ifndef GC_NO_PTHREAD_CANCEL + /* We should deal with the fact that apparently on Solaris and, */ + /* probably, on some Linux we can't collect while a thread is */ + /* exiting, since signals aren't handled properly. This currently */ + /* gives rise to deadlocks. The only workaround seen is to intercept */ + /* pthread_cancel() and pthread_exit(), and disable the collections */ + /* until the thread exit handler is called. That's ugly, because we */ + /* risk growing the heap unnecessarily. But it seems that we don't */ + /* really have an option in that the process is not in a fully */ + /* functional state while a thread is exiting. */ + GC_API int WRAP_FUNC(pthread_cancel)(pthread_t thread) + { +# ifdef CANCEL_SAFE + GC_thread t; + DCL_LOCK_STATE; +# endif + + INIT_REAL_SYMS(); +# ifdef CANCEL_SAFE + LOCK(); + t = GC_lookup_thread(thread); + /* We test DISABLED_GC because pthread_exit could be called at */ + /* the same time. (If t is NULL then pthread_cancel should */ + /* return ESRCH.) */ + if (t != NULL && (t -> flags & DISABLED_GC) == 0) { + t -> flags |= DISABLED_GC; + GC_dont_gc++; + } + UNLOCK(); +# endif + return REAL_FUNC(pthread_cancel)(thread); + } +#endif /* !GC_NO_PTHREAD_CANCEL */ + +#ifdef GC_HAVE_PTHREAD_EXIT + GC_API GC_PTHREAD_EXIT_ATTRIBUTE void WRAP_FUNC(pthread_exit)(void *retval) + { + pthread_t self = pthread_self(); + GC_thread me; + DCL_LOCK_STATE; + + INIT_REAL_SYMS(); + LOCK(); + me = GC_lookup_thread(self); + /* We test DISABLED_GC because someone else could call */ + /* pthread_cancel at the same time. */ + if (me != 0 && (me -> flags & DISABLED_GC) == 0) { + me -> flags |= DISABLED_GC; + GC_dont_gc++; + } + UNLOCK(); + + REAL_FUNC(pthread_exit)(retval); + } +#endif /* GC_HAVE_PTHREAD_EXIT */ + +GC_INNER GC_bool GC_in_thread_creation = FALSE; + /* Protected by allocation lock. */ + +GC_INLINE void GC_record_stack_base(GC_thread me, + const struct GC_stack_base *sb) +{ +# ifndef GC_DARWIN_THREADS + me -> stop_info.stack_ptr = (ptr_t)sb->mem_base; +# endif + me -> stack_end = (ptr_t)sb->mem_base; + if (me -> stack_end == NULL) + ABORT("Bad stack base in GC_register_my_thread"); +# ifdef IA64 + me -> backing_store_end = (ptr_t)sb->reg_base; +# endif +} + +STATIC GC_thread GC_register_my_thread_inner(const struct GC_stack_base *sb, + pthread_t my_pthread) +{ + GC_thread me; + + GC_in_thread_creation = TRUE; /* OK to collect from unknown thread. */ + me = GC_new_thread(my_pthread); + GC_in_thread_creation = FALSE; + if (me == 0) + ABORT("Failed to allocate memory for thread registering"); +# ifdef GC_DARWIN_THREADS + me -> stop_info.mach_thread = mach_thread_self(); +# endif + GC_record_stack_base(me, sb); +# ifdef GC_EXPLICIT_SIGNALS_UNBLOCK + /* Since this could be executed from a detached thread */ + /* destructor, our signals might already be blocked. */ + GC_unblock_gc_signals(); +# endif + return me; +} + +GC_API void GC_CALL GC_allow_register_threads(void) +{ + /* Check GC is initialized and the current thread is registered. */ + GC_ASSERT(GC_lookup_thread(pthread_self()) != 0); + set_need_to_lock(); +} + +GC_API int GC_CALL GC_register_my_thread(const struct GC_stack_base *sb) +{ + pthread_t self = pthread_self(); + GC_thread me; + DCL_LOCK_STATE; + + if (GC_need_to_lock == FALSE) + ABORT("Threads explicit registering is not previously enabled"); + + LOCK(); + me = GC_lookup_thread(self); + if (0 == me) { + me = GC_register_my_thread_inner(sb, self); + me -> flags |= DETACHED; + /* Treat as detached, since we do not need to worry about */ + /* pointer results. */ +# if defined(THREAD_LOCAL_ALLOC) + GC_init_thread_local(&(me->tlfs)); +# endif + UNLOCK(); + return GC_SUCCESS; + } else if ((me -> flags & FINISHED) != 0) { + /* This code is executed when a thread is registered from the */ + /* client thread key destructor. */ +# ifdef GC_DARWIN_THREADS + /* Reinitialize mach_thread to avoid thread_suspend fail */ + /* with MACH_SEND_INVALID_DEST error. */ + me -> stop_info.mach_thread = mach_thread_self(); +# endif + GC_record_stack_base(me, sb); + me -> flags &= ~FINISHED; /* but not DETACHED */ +# ifdef GC_EXPLICIT_SIGNALS_UNBLOCK + /* Since this could be executed from a thread destructor, */ + /* our signals might be blocked. */ + GC_unblock_gc_signals(); +# endif +# if defined(THREAD_LOCAL_ALLOC) + GC_init_thread_local(&(me->tlfs)); +# endif + UNLOCK(); + return GC_SUCCESS; + } else { + UNLOCK(); + return GC_DUPLICATE; + } +} + +struct start_info { + void *(*start_routine)(void *); + void *arg; + word flags; + sem_t registered; /* 1 ==> in our thread table, but */ + /* parent hasn't yet noticed. */ +}; + +/* Called from GC_inner_start_routine(). Defined in this file to */ +/* minimize the number of include files in pthread_start.c (because */ +/* sem_t and sem_post() are not used that file directly). */ +GC_INNER_PTHRSTART GC_thread GC_start_rtn_prepare_thread( + void *(**pstart)(void *), + void **pstart_arg, + struct GC_stack_base *sb, void *arg) +{ + struct start_info * si = (struct start_info *)arg; + pthread_t self = pthread_self(); + GC_thread me; + DCL_LOCK_STATE; + +# ifdef DEBUG_THREADS + GC_log_printf("Starting thread %p, pid = %ld, sp = %p\n", + (void *)self, (long)getpid(), (void *)&arg); +# endif + LOCK(); + me = GC_register_my_thread_inner(sb, self); + me -> flags = si -> flags; +# if defined(THREAD_LOCAL_ALLOC) + GC_init_thread_local(&(me->tlfs)); +# endif + UNLOCK(); + *pstart = si -> start_routine; +# ifdef DEBUG_THREADS + GC_log_printf("start_routine = %p\n", (void *)(signed_word)(*pstart)); +# endif + *pstart_arg = si -> arg; + sem_post(&(si -> registered)); /* Last action on si. */ + /* OK to deallocate. */ + return me; +} + +#if !defined(SN_TARGET_ORBIS) && !defined(SN_TARGET_PSP2) + STATIC void * GC_start_routine(void * arg) + { +# ifdef INCLUDE_LINUX_THREAD_DESCR + struct GC_stack_base sb; + +# ifdef REDIRECT_MALLOC + /* GC_get_stack_base may call pthread_getattr_np, which can */ + /* unfortunately call realloc, which may allocate from an */ + /* unregistered thread. This is unpleasant, since it might */ + /* force heap growth (or, even, heap overflow). */ + GC_disable(); +# endif + if (GC_get_stack_base(&sb) != GC_SUCCESS) + ABORT("Failed to get thread stack base"); +# ifdef REDIRECT_MALLOC + GC_enable(); +# endif + return GC_inner_start_routine(&sb, arg); +# else + return GC_call_with_stack_base(GC_inner_start_routine, arg); +# endif + } + + GC_API int WRAP_FUNC(pthread_create)(pthread_t *new_thread, + GC_PTHREAD_CREATE_CONST pthread_attr_t *attr, + void *(*start_routine)(void *), void *arg) + { + int result; + int detachstate; + word my_flags = 0; + struct start_info * si; + DCL_LOCK_STATE; + /* This is otherwise saved only in an area mmapped by the thread */ + /* library, which isn't visible to the collector. */ + + /* We resist the temptation to muck with the stack size here, */ + /* even if the default is unreasonably small. That's the client's */ + /* responsibility. */ + + INIT_REAL_SYMS(); + LOCK(); + si = (struct start_info *)GC_INTERNAL_MALLOC(sizeof(struct start_info), + NORMAL); + UNLOCK(); + if (!EXPECT(parallel_initialized, TRUE)) + GC_init_parallel(); + if (EXPECT(0 == si, FALSE) && + (si = (struct start_info *) + (*GC_get_oom_fn())(sizeof(struct start_info))) == 0) + return(ENOMEM); + if (sem_init(&(si -> registered), GC_SEM_INIT_PSHARED, 0) != 0) + ABORT("sem_init failed"); + + si -> start_routine = start_routine; + si -> arg = arg; + LOCK(); + if (!EXPECT(GC_thr_initialized, TRUE)) + GC_thr_init(); +# ifdef GC_ASSERTIONS + { + size_t stack_size = 0; + if (NULL != attr) { + if (pthread_attr_getstacksize(attr, &stack_size) != 0) + ABORT("pthread_attr_getstacksize failed"); + } + if (0 == stack_size) { + pthread_attr_t my_attr; + + if (pthread_attr_init(&my_attr) != 0) + ABORT("pthread_attr_init failed"); + if (pthread_attr_getstacksize(&my_attr, &stack_size) != 0) + ABORT("pthread_attr_getstacksize failed"); + (void)pthread_attr_destroy(&my_attr); + } + /* On Solaris 10, with default attr initialization, */ + /* stack_size remains 0. Fudge it. */ + if (0 == stack_size) { +# ifndef SOLARIS + WARN("Failed to get stack size for assertion checking\n", 0); +# endif + stack_size = 1000000; + } + GC_ASSERT(stack_size >= 65536); + /* Our threads may need to do some work for the GC. */ + /* Ridiculously small threads won't work, and they */ + /* probably wouldn't work anyway. */ + } +# endif + if (NULL == attr) { + detachstate = PTHREAD_CREATE_JOINABLE; + } else { + pthread_attr_getdetachstate(attr, &detachstate); + } + if (PTHREAD_CREATE_DETACHED == detachstate) my_flags |= DETACHED; + si -> flags = my_flags; + UNLOCK(); +# ifdef DEBUG_THREADS + GC_log_printf("About to start new thread from thread %p\n", + (void *)pthread_self()); +# endif + set_need_to_lock(); + result = REAL_FUNC(pthread_create)(new_thread, attr, GC_start_routine, si); + + /* Wait until child has been added to the thread table. */ + /* This also ensures that we hold onto si until the child is done */ + /* with it. Thus it doesn't matter whether it is otherwise */ + /* visible to the collector. */ + if (0 == result) { + IF_CANCEL(int cancel_state;) + +# ifdef DEBUG_THREADS + /* new_thread is non-NULL because pthread_create requires it. */ + GC_log_printf("Started thread %p\n", (void *)(*new_thread)); +# endif + DISABLE_CANCEL(cancel_state); + /* pthread_create is not a cancellation point. */ + while (0 != sem_wait(&(si -> registered))) { +# if defined(GC_HAIKU_THREADS) + /* To workaround some bug in Haiku semaphores. */ + if (EACCES == errno) continue; +# endif + if (EINTR != errno) ABORT("sem_wait failed"); + } + RESTORE_CANCEL(cancel_state); + } + sem_destroy(&(si -> registered)); + LOCK(); + GC_INTERNAL_FREE(si); + UNLOCK(); + + return(result); + } +#endif /* !SN_TARGET_ORBIS && !SN_TARGET_PSP2 */ + +#if defined(USE_SPIN_LOCK) || !defined(NO_PTHREAD_TRYLOCK) +/* Spend a few cycles in a way that can't introduce contention with */ +/* other threads. */ +#define GC_PAUSE_SPIN_CYCLES 10 +STATIC void GC_pause(void) +{ + int i; + +#ifndef GC_ATOMIC_OPS_H + volatile word dummy = 0; +#endif + + for (i = 0; i < GC_PAUSE_SPIN_CYCLES; ++i) { + /* Something that's unlikely to be optimized away. */ +#ifndef GC_ATOMIC_OPS_H + GC_noop1(++dummy); +#else + AO_compiler_barrier(); +#endif + } +} +#endif + +#ifndef SPIN_MAX +# define SPIN_MAX 128 /* Maximum number of calls to GC_pause before */ + /* give up. */ +#endif + +GC_INNER volatile GC_bool GC_collecting = FALSE; + /* A hint that we're in the collector and */ + /* holding the allocation lock for an */ + /* extended period. */ + +#if (!defined(USE_SPIN_LOCK) && !defined(NO_PTHREAD_TRYLOCK)) \ + || defined(PARALLEL_MARK) +/* If we don't want to use the below spinlock implementation, either */ +/* because we don't have a GC_test_and_set implementation, or because */ +/* we don't want to risk sleeping, we can still try spinning on */ +/* pthread_mutex_trylock for a while. This appears to be very */ +/* beneficial in many cases. */ +/* I suspect that under high contention this is nearly always better */ +/* than the spin lock. But it's a bit slower on a uniprocessor. */ +/* Hence we still default to the spin lock. */ +/* This is also used to acquire the mark lock for the parallel */ +/* marker. */ + +/* Here we use a strict exponential backoff scheme. I don't know */ +/* whether that's better or worse than the above. We eventually */ +/* yield by calling pthread_mutex_lock(); it never makes sense to */ +/* explicitly sleep. */ + +/* #define LOCK_STATS */ +/* Note that LOCK_STATS requires AO_HAVE_test_and_set. */ +#ifdef LOCK_STATS + volatile AO_t GC_spin_count = 0; + volatile AO_t GC_block_count = 0; + volatile AO_t GC_unlocked_count = 0; +#endif + +STATIC void GC_generic_lock(pthread_mutex_t * lock) +{ +#ifndef NO_PTHREAD_TRYLOCK + unsigned pause_length = 1; + unsigned i; + + if (0 == pthread_mutex_trylock(lock)) { +# ifdef LOCK_STATS + (void)AO_fetch_and_add1(&GC_unlocked_count); +# endif + return; + } + for (; pause_length <= SPIN_MAX; pause_length <<= 1) { + for (i = 0; i < pause_length; ++i) { + GC_pause(); + } + switch(pthread_mutex_trylock(lock)) { + case 0: +# ifdef LOCK_STATS + (void)AO_fetch_and_add1(&GC_spin_count); +# endif + return; + case EBUSY: + break; + default: + ABORT("Unexpected error from pthread_mutex_trylock"); + } + } +#endif /* !NO_PTHREAD_TRYLOCK */ +# ifdef LOCK_STATS + (void)AO_fetch_and_add1(&GC_block_count); +# endif + pthread_mutex_lock(lock); +} + +#endif /* !USE_SPIN_LOCK || ... */ + +#ifdef AO_HAVE_char_load +# define is_collecting() \ + ((GC_bool)AO_char_load((unsigned char *)&GC_collecting)) +#else + /* GC_collecting is a hint, a potential data race between */ + /* GC_lock() and ENTER/EXIT_GC() is OK to ignore. */ +# define is_collecting() GC_collecting +#endif + +#if defined(USE_SPIN_LOCK) + +/* Reasonably fast spin locks. Basically the same implementation */ +/* as STL alloc.h. This isn't really the right way to do this. */ +/* but until the POSIX scheduling mess gets straightened out ... */ + +GC_INNER volatile AO_TS_t GC_allocate_lock = AO_TS_INITIALIZER; + +# define low_spin_max 30 /* spin cycles if we suspect uniprocessor */ +# define high_spin_max SPIN_MAX /* spin cycles for multiprocessor */ + + static volatile AO_t spin_max = low_spin_max; + static volatile AO_t last_spins = 0; + /* A potential data race between */ + /* threads invoking GC_lock which reads */ + /* and updates spin_max and last_spins */ + /* could be ignored because these */ + /* variables are hints only. */ + +GC_INNER void GC_lock(void) +{ + unsigned my_spin_max; + unsigned my_last_spins; + unsigned i; + + if (AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_CLEAR) { + return; + } + my_spin_max = (unsigned)AO_load(&spin_max); + my_last_spins = (unsigned)AO_load(&last_spins); + for (i = 0; i < my_spin_max; i++) { + if (is_collecting() || GC_nprocs == 1) + goto yield; + if (i < my_last_spins/2) { + GC_pause(); + continue; + } + if (AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_CLEAR) { + /* + * got it! + * Spinning worked. Thus we're probably not being scheduled + * against the other process with which we were contending. + * Thus it makes sense to spin longer the next time. + */ + AO_store(&last_spins, (AO_t)i); + AO_store(&spin_max, (AO_t)high_spin_max); + return; + } + } + /* We are probably being scheduled against the other process. Sleep. */ + AO_store(&spin_max, (AO_t)low_spin_max); +yield: + for (i = 0;; ++i) { + if (AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_CLEAR) { + return; + } +# define SLEEP_THRESHOLD 12 + /* Under Linux very short sleeps tend to wait until */ + /* the current time quantum expires. On old Linux */ + /* kernels nanosleep (<= 2 msecs) just spins. */ + /* (Under 2.4, this happens only for real-time */ + /* processes.) We want to minimize both behaviors */ + /* here. */ + if (i < SLEEP_THRESHOLD) { + sched_yield(); + } else { + struct timespec ts; + + if (i > 24) i = 24; + /* Don't wait for more than about 15 msecs, */ + /* even under extreme contention. */ + ts.tv_sec = 0; + ts.tv_nsec = 1 << i; + nanosleep(&ts, 0); + } + } +} + +#else /* !USE_SPIN_LOCK */ + +GC_INNER void GC_lock(void) +{ +#ifndef NO_PTHREAD_TRYLOCK + if (1 == GC_nprocs || is_collecting()) { + pthread_mutex_lock(&GC_allocate_ml); + } else { + GC_generic_lock(&GC_allocate_ml); + } +#else /* !NO_PTHREAD_TRYLOCK */ + pthread_mutex_lock(&GC_allocate_ml); +#endif /* !NO_PTHREAD_TRYLOCK */ +} + +#endif /* !USE_SPIN_LOCK */ + +#ifdef PARALLEL_MARK + +# ifdef GC_ASSERTIONS + STATIC unsigned long GC_mark_lock_holder = NO_THREAD; +# define SET_MARK_LOCK_HOLDER \ + (void)(GC_mark_lock_holder = NUMERIC_THREAD_ID(pthread_self())) +# define UNSET_MARK_LOCK_HOLDER \ + do { \ + GC_ASSERT(GC_mark_lock_holder \ + == NUMERIC_THREAD_ID(pthread_self())); \ + GC_mark_lock_holder = NO_THREAD; \ + } while (0) +# else +# define SET_MARK_LOCK_HOLDER (void)0 +# define UNSET_MARK_LOCK_HOLDER (void)0 +# endif /* !GC_ASSERTIONS */ + +#ifdef GLIBC_2_1_MUTEX_HACK + /* Ugly workaround for a linux threads bug in the final versions */ + /* of glibc2.1. Pthread_mutex_trylock sets the mutex owner */ + /* field even when it fails to acquire the mutex. This causes */ + /* pthread_cond_wait to die. Remove for glibc2.2. */ + /* According to the man page, we should use */ + /* PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP, but that isn't actually */ + /* defined. */ + static pthread_mutex_t mark_mutex = + {0, 0, 0, PTHREAD_MUTEX_ERRORCHECK_NP, {0, 0}}; +#else + static pthread_mutex_t mark_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif + +static pthread_cond_t builder_cv = PTHREAD_COND_INITIALIZER; + +#ifdef GLIBC_2_19_TSX_BUG + /* Parse string like [.[]] and return major value. */ + static int parse_version(int *pminor, const char *pverstr) { + char *endp; + unsigned long value = strtoul(pverstr, &endp, 10); + int major = (int)value; + + if (major < 0 || (char *)pverstr == endp || (unsigned)major != value) { + /* Parse error */ + return -1; + } + if (*endp != '.') { + /* No minor part. */ + *pminor = -1; + } else { + value = strtoul(endp + 1, &endp, 10); + *pminor = (int)value; + if (*pminor < 0 || (unsigned)(*pminor) != value) { + return -1; + } + } + return major; + } +#endif /* GLIBC_2_19_TSX_BUG */ + +static void setup_mark_lock(void) +{ +# ifdef GLIBC_2_19_TSX_BUG + pthread_mutexattr_t mattr; + int glibc_minor = -1; + int glibc_major = parse_version(&glibc_minor, gnu_get_libc_version()); + + if (glibc_major > 2 || (glibc_major == 2 && glibc_minor >= 19)) { + /* TODO: disable this workaround for glibc with fixed TSX */ + /* This disables lock elision to workaround a bug in glibc 2.19+ */ + if (0 != pthread_mutexattr_init(&mattr)) { + ABORT("pthread_mutexattr_init failed"); + } + if (0 != pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_NORMAL)) { + ABORT("pthread_mutexattr_settype failed"); + } + if (0 != pthread_mutex_init(&mark_mutex, &mattr)) { + ABORT("pthread_mutex_init failed"); + } + (void)pthread_mutexattr_destroy(&mattr); + } +# endif +} + +GC_INNER void GC_acquire_mark_lock(void) +{ +# if defined(NUMERIC_THREAD_ID_UNIQUE) && !defined(THREAD_SANITIZER) + GC_ASSERT(GC_mark_lock_holder != NUMERIC_THREAD_ID(pthread_self())); +# endif + GC_generic_lock(&mark_mutex); + SET_MARK_LOCK_HOLDER; +} + +GC_INNER void GC_release_mark_lock(void) +{ + UNSET_MARK_LOCK_HOLDER; + if (pthread_mutex_unlock(&mark_mutex) != 0) { + ABORT("pthread_mutex_unlock failed"); + } +} + +/* Collector must wait for a freelist builders for 2 reasons: */ +/* 1) Mark bits may still be getting examined without lock. */ +/* 2) Partial free lists referenced only by locals may not be scanned */ +/* correctly, e.g. if they contain "pointer-free" objects, since the */ +/* free-list link may be ignored. */ +STATIC void GC_wait_builder(void) +{ + ASSERT_CANCEL_DISABLED(); + UNSET_MARK_LOCK_HOLDER; + if (pthread_cond_wait(&builder_cv, &mark_mutex) != 0) { + ABORT("pthread_cond_wait failed"); + } + GC_ASSERT(GC_mark_lock_holder == NO_THREAD); + SET_MARK_LOCK_HOLDER; +} + +GC_INNER void GC_wait_for_reclaim(void) +{ + GC_acquire_mark_lock(); + while (GC_fl_builder_count > 0) { + GC_wait_builder(); + } + GC_release_mark_lock(); +} + +GC_INNER void GC_notify_all_builder(void) +{ + GC_ASSERT(GC_mark_lock_holder == NUMERIC_THREAD_ID(pthread_self())); + if (pthread_cond_broadcast(&builder_cv) != 0) { + ABORT("pthread_cond_broadcast failed"); + } +} + +GC_INNER void GC_wait_marker(void) +{ + ASSERT_CANCEL_DISABLED(); + GC_ASSERT(GC_parallel); + UNSET_MARK_LOCK_HOLDER; + if (pthread_cond_wait(&mark_cv, &mark_mutex) != 0) { + ABORT("pthread_cond_wait failed"); + } + GC_ASSERT(GC_mark_lock_holder == NO_THREAD); + SET_MARK_LOCK_HOLDER; +} + +GC_INNER void GC_notify_all_marker(void) +{ + GC_ASSERT(GC_parallel); + if (pthread_cond_broadcast(&mark_cv) != 0) { + ABORT("pthread_cond_broadcast failed"); + } +} + +#endif /* PARALLEL_MARK */ + +#ifdef PTHREAD_REGISTER_CANCEL_WEAK_STUBS + /* Workaround "undefined reference" linkage errors on some targets. */ + EXTERN_C_BEGIN + extern void __pthread_register_cancel(void) __attribute__((__weak__)); + extern void __pthread_unregister_cancel(void) __attribute__((__weak__)); + EXTERN_C_END + + void __pthread_register_cancel(void) {} + void __pthread_unregister_cancel(void) {} +#endif + +#endif /* GC_PTHREADS */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/ptr_chck.c b/unity-2019.4.24f1-mbe/external/bdwgc/ptr_chck.c new file mode 100644 index 000000000..d41a0ad21 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/ptr_chck.c @@ -0,0 +1,278 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#include "private/gc_pmark.h" + +/* + * These are checking routines calls to which could be inserted by a + * preprocessor to validate C pointer arithmetic. + */ + +STATIC void GC_CALLBACK GC_default_same_obj_print_proc(void * p, void * q) +{ + ABORT_ARG2("GC_same_obj test failed", + ": %p and %p are not in the same object", p, q); +} + +void (GC_CALLBACK *GC_same_obj_print_proc) (void *, void *) + = GC_default_same_obj_print_proc; + +/* Check that p and q point to the same object. Call */ +/* *GC_same_obj_print_proc if they don't. */ +/* Returns the first argument. (Return value may be hard */ +/* to use due to typing issues. But if we had a suitable */ +/* preprocessor...) */ +/* Succeeds if neither p nor q points to the heap. */ +/* We assume this is performance critical. (It shouldn't */ +/* be called by production code, but this can easily make */ +/* debugging intolerably slow.) */ +GC_API void * GC_CALL GC_same_obj(void *p, void *q) +{ + struct hblk *h; + hdr *hhdr; + ptr_t base, limit; + word sz; + + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); + hhdr = HDR((word)p); + if (hhdr == 0) { + if (divHBLKSZ((word)p) != divHBLKSZ((word)q) + && HDR((word)q) != 0) { + goto fail; + } + return(p); + } + /* If it's a pointer to the middle of a large object, move it */ + /* to the beginning. */ + if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + h = HBLKPTR(p) - (word)hhdr; + hhdr = HDR(h); + while (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + h = FORWARDED_ADDR(h, hhdr); + hhdr = HDR(h); + } + limit = (ptr_t)h + hhdr -> hb_sz; + if ((word)p >= (word)limit || (word)q >= (word)limit + || (word)q < (word)h) { + goto fail; + } + return(p); + } + sz = hhdr -> hb_sz; + if (sz > MAXOBJBYTES) { + base = (ptr_t)HBLKPTR(p); + limit = base + sz; + if ((word)p >= (word)limit) { + goto fail; + } + } else { + size_t offset; + size_t pdispl = HBLKDISPL(p); + + offset = pdispl % sz; + if (HBLKPTR(p) != HBLKPTR(q)) goto fail; + /* W/o this check, we might miss an error if */ + /* q points to the first object on a page, and */ + /* points just before the page. */ + base = (ptr_t)p - offset; + limit = base + sz; + } + /* [base, limit) delimits the object containing p, if any. */ + /* If p is not inside a valid object, then either q is */ + /* also outside any valid object, or it is outside */ + /* [base, limit). */ + if ((word)q >= (word)limit || (word)q < (word)base) { + goto fail; + } + return(p); +fail: + (*GC_same_obj_print_proc)((ptr_t)p, (ptr_t)q); + return(p); +} + +STATIC void GC_CALLBACK GC_default_is_valid_displacement_print_proc (void *p) +{ + ABORT_ARG1("GC_is_valid_displacement test failed", ": %p not valid", p); +} + +void (GC_CALLBACK *GC_is_valid_displacement_print_proc)(void *) = + GC_default_is_valid_displacement_print_proc; + +/* Check that if p is a pointer to a heap page, then it points to */ +/* a valid displacement within a heap object. */ +/* Uninteresting with GC_all_interior_pointers. */ +/* Always returns its argument. */ +/* Note that we don't lock, since nothing relevant about the header */ +/* should change while we have a valid object pointer to the block. */ +GC_API void * GC_CALL GC_is_valid_displacement(void *p) +{ + hdr *hhdr; + word pdispl; + word offset; + struct hblk *h; + word sz; + + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); + hhdr = HDR((word)p); + if (hhdr == 0) return(p); + h = HBLKPTR(p); + if (GC_all_interior_pointers) { + while (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + h = FORWARDED_ADDR(h, hhdr); + hhdr = HDR(h); + } + } + if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + goto fail; + } + sz = hhdr -> hb_sz; + pdispl = HBLKDISPL(p); + offset = pdispl % sz; + if ((sz > MAXOBJBYTES && (word)p >= (word)h + sz) + || !GC_valid_offsets[offset] + || (word)p - offset + sz > (word)(h + 1)) { + goto fail; + } + return(p); +fail: + (*GC_is_valid_displacement_print_proc)((ptr_t)p); + return(p); +} + +STATIC void GC_CALLBACK GC_default_is_visible_print_proc(void * p) +{ + ABORT_ARG1("GC_is_visible test failed", ": %p not GC-visible", p); +} + +void (GC_CALLBACK *GC_is_visible_print_proc)(void * p) = + GC_default_is_visible_print_proc; + +#ifndef THREADS +/* Could p be a stack address? */ + STATIC GC_bool GC_on_stack(void *p) + { +# ifdef STACK_GROWS_DOWN + if ((word)p >= (word)GC_approx_sp() + && (word)p < (word)GC_stackbottom) { + return(TRUE); + } +# else + if ((word)p <= (word)GC_approx_sp() + && (word)p > (word)GC_stackbottom) { + return(TRUE); + } +# endif + return(FALSE); + } +#endif + +/* Check that p is visible */ +/* to the collector as a possibly pointer containing location. */ +/* If it isn't, invoke *GC_is_visible_print_proc. */ +/* Returns the argument in all cases. May erroneously succeed */ +/* in hard cases. (This is intended for debugging use with */ +/* untyped allocations. The idea is that it should be possible, though */ +/* slow, to add such a call to all indirect pointer stores.) */ +/* Currently useless for the multi-threaded worlds. */ +GC_API void * GC_CALL GC_is_visible(void *p) +{ + hdr *hhdr; + + if ((word)p & (ALIGNMENT - 1)) goto fail; + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); +# ifdef THREADS + hhdr = HDR((word)p); + if (hhdr != 0 && GC_base(p) == 0) { + goto fail; + } else { + /* May be inside thread stack. We can't do much. */ + return(p); + } +# else + /* Check stack first: */ + if (GC_on_stack(p)) return(p); + hhdr = HDR((word)p); + if (hhdr == 0) { + if (GC_is_static_root(p)) return(p); + /* Else do it again correctly: */ +# if defined(DYNAMIC_LOADING) || defined(MSWIN32) \ + || defined(MSWINCE) || defined(CYGWIN32) || defined(PCR) + GC_register_dynamic_libraries(); + if (GC_is_static_root(p)) + return(p); +# endif + goto fail; + } else { + /* p points to the heap. */ + word descr; + ptr_t base = (ptr_t)GC_base(p); /* Should be manually inlined? */ + + if (base == 0) goto fail; + if (HBLKPTR(base) != HBLKPTR(p)) hhdr = HDR((word)p); + descr = hhdr -> hb_descr; + retry: + switch(descr & GC_DS_TAGS) { + case GC_DS_LENGTH: + if ((word)p - (word)base > descr) goto fail; + break; + case GC_DS_BITMAP: + if ((word)p - (word)base >= WORDS_TO_BYTES(BITMAP_BITS) + || ((word)p & (sizeof(word) - 1))) goto fail; + if (!(((word)1 << (WORDSZ - ((ptr_t)p - (ptr_t)base) - 1)) + & descr)) goto fail; + break; + case GC_DS_PROC: + /* We could try to decipher this partially. */ + /* For now we just punt. */ + break; + case GC_DS_PER_OBJECT: + if ((signed_word)descr >= 0) { + descr = *(word *)((ptr_t)base + (descr & ~GC_DS_TAGS)); + } else { + ptr_t type_descr = *(ptr_t *)base; + descr = *(word *)(type_descr + - (descr - (word)(GC_DS_PER_OBJECT + - GC_INDIR_PER_OBJ_BIAS))); + } + goto retry; + } + return(p); + } +# endif +fail: + (*GC_is_visible_print_proc)((ptr_t)p); + return(p); +} + +GC_API void * GC_CALL GC_pre_incr (void **p, ptrdiff_t how_much) +{ + void * initial = *p; + void * result = GC_same_obj((void *)((ptr_t)initial + how_much), initial); + + if (!GC_all_interior_pointers) { + (void) GC_is_valid_displacement(result); + } + return (*p = result); +} + +GC_API void * GC_CALL GC_post_incr (void **p, ptrdiff_t how_much) +{ + void * initial = *p; + void * result = GC_same_obj((void *)((ptr_t)initial + how_much), initial); + + if (!GC_all_interior_pointers) { + (void) GC_is_valid_displacement(result); + } + *p = result; + return(initial); +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/reclaim.c b/unity-2019.4.24f1-mbe/external/bdwgc/reclaim.c new file mode 100644 index 000000000..85591a30e --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/reclaim.c @@ -0,0 +1,837 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1996 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999-2004 Hewlett-Packard Development Company, L.P. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#include "private/gc_priv.h" + +#ifdef ENABLE_DISCLAIM +# include "gc_disclaim.h" +#endif + +#include + +GC_INNER signed_word GC_bytes_found = 0; + /* Number of bytes of memory reclaimed */ + /* minus the number of bytes originally */ + /* on free lists which we had to drop. */ + +#if defined(PARALLEL_MARK) + GC_INNER signed_word GC_fl_builder_count = 0; + /* Number of threads currently building free lists without */ + /* holding GC lock. It is not safe to collect if this is */ + /* nonzero. Also, together with the mark lock, it is used as */ + /* a semaphore during marker threads startup. */ +#endif /* PARALLEL_MARK */ + +/* We defer printing of leaked objects until we're done with the GC */ +/* cycle, since the routine for printing objects needs to run outside */ +/* the collector, e.g. without the allocation lock. */ +#ifndef MAX_LEAKED +# define MAX_LEAKED 40 +#endif +STATIC ptr_t GC_leaked[MAX_LEAKED] = { NULL }; +STATIC unsigned GC_n_leaked = 0; + +GC_INNER GC_bool GC_have_errors = FALSE; + +#if !defined(EAGER_SWEEP) && defined(ENABLE_DISCLAIM) + STATIC void GC_reclaim_unconditionally_marked(void); +#endif + +GC_INLINE void GC_add_leaked(ptr_t leaked) +{ +# ifndef SHORT_DBG_HDRS + if (GC_findleak_delay_free && !GC_check_leaked(leaked)) + return; +# endif + + GC_have_errors = TRUE; + if (GC_n_leaked < MAX_LEAKED) { + GC_leaked[GC_n_leaked++] = leaked; + /* Make sure it's not reclaimed this cycle */ + GC_set_mark_bit(leaked); + } +} + +/* Print all objects on the list after printing any smashed objects. */ +/* Clear both lists. Called without the allocation lock held. */ +GC_INNER void GC_print_all_errors(void) +{ + static GC_bool printing_errors = FALSE; + GC_bool have_errors; + unsigned i, n_leaked; + ptr_t leaked[MAX_LEAKED]; + DCL_LOCK_STATE; + + LOCK(); + if (printing_errors) { + UNLOCK(); + return; + } + have_errors = GC_have_errors; + printing_errors = TRUE; + n_leaked = GC_n_leaked; + if (n_leaked > 0) { + GC_ASSERT(n_leaked <= MAX_LEAKED); + BCOPY(GC_leaked, leaked, n_leaked * sizeof(ptr_t)); + GC_n_leaked = 0; + BZERO(GC_leaked, n_leaked * sizeof(ptr_t)); + } + UNLOCK(); + + if (GC_debugging_started) { + GC_print_all_smashed(); + } else { + have_errors = FALSE; + } + + if (n_leaked > 0) { + GC_err_printf("Found %u leaked objects:\n", n_leaked); + have_errors = TRUE; + } + for (i = 0; i < n_leaked; i++) { + ptr_t p = leaked[i]; + GC_print_heap_obj(p); + GC_free(p); + } + + if (have_errors +# ifndef GC_ABORT_ON_LEAK + && GETENV("GC_ABORT_ON_LEAK") != NULL +# endif + ) { + ABORT("Leaked or smashed objects encountered"); + } + + LOCK(); + printing_errors = FALSE; + UNLOCK(); +} + + +/* + * reclaim phase + * + */ + +/* Test whether a block is completely empty, i.e. contains no marked */ +/* objects. This does not require the block to be in physical memory. */ +GC_INNER GC_bool GC_block_empty(hdr *hhdr) +{ + return (hhdr -> hb_n_marks == 0); +} + +STATIC GC_bool GC_block_nearly_full(hdr *hhdr) +{ + return (hhdr -> hb_n_marks > 7 * HBLK_OBJS(hhdr -> hb_sz)/8); +} + +/* FIXME: This should perhaps again be specialized for USE_MARK_BYTES */ +/* and USE_MARK_BITS cases. */ + +/* + * Restore unmarked small objects in h of size sz to the object + * free list. Returns the new list. + * Clears unmarked objects. Sz is in bytes. + */ +STATIC ptr_t GC_reclaim_clear(struct hblk *hbp, hdr *hhdr, word sz, + ptr_t list, signed_word *count) +{ + word bit_no = 0; + word *p, *q, *plim; + signed_word n_bytes_found = 0; + + GC_ASSERT(hhdr == GC_find_header((ptr_t)hbp)); + GC_ASSERT(sz == hhdr -> hb_sz); + GC_ASSERT((sz & (BYTES_PER_WORD-1)) == 0); + p = (word *)(hbp->hb_body); + plim = (word *)(hbp->hb_body + HBLKSIZE - sz); + + /* go through all words in block */ + while ((word)p <= (word)plim) { + if (mark_bit_from_hdr(hhdr, bit_no)) { + p = (word *)((ptr_t)p + sz); + } else { + n_bytes_found += sz; + /* object is available - put on list */ + obj_link(p) = list; + list = ((ptr_t)p); + /* Clear object, advance p to next object in the process */ + q = (word *)((ptr_t)p + sz); +# ifdef USE_MARK_BYTES + GC_ASSERT(!(sz & 1) + && !((word)p & (2 * sizeof(word) - 1))); + p[1] = 0; + p += 2; + while ((word)p < (word)q) { + CLEAR_DOUBLE(p); + p += 2; + } +# else + p++; /* Skip link field */ + while ((word)p < (word)q) { + *p++ = 0; + } +# endif + } + bit_no += MARK_BIT_OFFSET(sz); + } + *count += n_bytes_found; + return(list); +} + +/* The same thing, but don't clear objects: */ +STATIC ptr_t GC_reclaim_uninit(struct hblk *hbp, hdr *hhdr, word sz, + ptr_t list, signed_word *count) +{ + word bit_no = 0; + word *p, *plim; + signed_word n_bytes_found = 0; + + GC_ASSERT(sz == hhdr -> hb_sz); + p = (word *)(hbp->hb_body); + plim = (word *)((ptr_t)hbp + HBLKSIZE - sz); + + /* go through all words in block */ + while ((word)p <= (word)plim) { + if (!mark_bit_from_hdr(hhdr, bit_no)) { + n_bytes_found += sz; + /* object is available - put on list */ + obj_link(p) = list; + list = ((ptr_t)p); + } + p = (word *)((ptr_t)p + sz); + bit_no += MARK_BIT_OFFSET(sz); + } + *count += n_bytes_found; + return(list); +} + +#ifdef ENABLE_DISCLAIM + /* Call reclaim notifier for block's kind on each unmarked object in */ + /* block, all within a pair of corresponding enter/leave callbacks. */ + STATIC ptr_t GC_disclaim_and_reclaim(struct hblk *hbp, hdr *hhdr, word sz, + ptr_t list, signed_word *count) + { + word bit_no = 0; + word *p, *q, *plim; + signed_word n_bytes_found = 0; + struct obj_kind *ok = &GC_obj_kinds[hhdr->hb_obj_kind]; + int (GC_CALLBACK *disclaim)(void *) = ok->ok_disclaim_proc; + + GC_ASSERT(sz == hhdr -> hb_sz); + p = (word *)(hbp -> hb_body); + plim = (word *)((ptr_t)p + HBLKSIZE - sz); + + while ((word)p <= (word)plim) { + int marked = mark_bit_from_hdr(hhdr, bit_no); + if (!marked && (*disclaim)(p)) { + hhdr -> hb_n_marks++; + marked = 1; + } + if (marked) + p = (word *)((ptr_t)p + sz); + else { + n_bytes_found += sz; + /* object is available - put on list */ + obj_link(p) = list; + list = ((ptr_t)p); + /* Clear object, advance p to next object in the process */ + q = (word *)((ptr_t)p + sz); +# ifdef USE_MARK_BYTES + GC_ASSERT((sz & 1) == 0); + GC_ASSERT(((word)p & (2 * sizeof(word) - 1)) == 0); + p[1] = 0; + p += 2; + while ((word)p < (word)q) { + CLEAR_DOUBLE(p); + p += 2; + } +# else + p++; /* Skip link field */ + while ((word)p < (word)q) { + *p++ = 0; + } +# endif + } + bit_no += MARK_BIT_OFFSET(sz); + } + *count += n_bytes_found; + return list; + } +#endif /* ENABLE_DISCLAIM */ + +/* Don't really reclaim objects, just check for unmarked ones: */ +STATIC void GC_reclaim_check(struct hblk *hbp, hdr *hhdr, word sz) +{ + word bit_no; + ptr_t p, plim; + GC_ASSERT(sz == hhdr -> hb_sz); + + /* go through all words in block */ + p = hbp->hb_body; + plim = p + HBLKSIZE - sz; + for (bit_no = 0; (word)p <= (word)plim; + p += sz, bit_no += MARK_BIT_OFFSET(sz)) { + if (!mark_bit_from_hdr(hhdr, bit_no)) { + GC_add_leaked(p); + } + } +} + +/* Is a pointer-free block? Same as IS_PTRFREE macro (in os_dep.c) but */ +/* uses unordered atomic access to avoid racing with GC_realloc. */ +#ifdef AO_HAVE_load +# define IS_PTRFREE_SAFE(hhdr) \ + (AO_load((volatile AO_t *)&(hhdr)->hb_descr) == 0) +#else + /* No race as GC_realloc holds the lock while updating hb_descr. */ +# define IS_PTRFREE_SAFE(hhdr) ((hhdr)->hb_descr == 0) +#endif + +/* + * Generic procedure to rebuild a free list in hbp. + * Also called directly from GC_malloc_many. + * Sz is now in bytes. + */ +GC_INNER ptr_t GC_reclaim_generic(struct hblk * hbp, hdr *hhdr, size_t sz, + GC_bool init, ptr_t list, + signed_word *count) +{ + ptr_t result; + + GC_ASSERT(GC_find_header((ptr_t)hbp) == hhdr); +# ifndef GC_DISABLE_INCREMENTAL + GC_remove_protection(hbp, 1, IS_PTRFREE_SAFE(hhdr)); +# endif +# ifdef ENABLE_DISCLAIM + if ((hhdr -> hb_flags & HAS_DISCLAIM) != 0) { + result = GC_disclaim_and_reclaim(hbp, hhdr, sz, list, count); + } else +# endif + /* else */ if (init || GC_debugging_started) { + result = GC_reclaim_clear(hbp, hhdr, sz, list, count); + } else { + GC_ASSERT(IS_PTRFREE_SAFE(hhdr)); + result = GC_reclaim_uninit(hbp, hhdr, sz, list, count); + } + if (IS_UNCOLLECTABLE(hhdr -> hb_obj_kind)) GC_set_hdr_marks(hhdr); + return result; +} + +/* + * Restore unmarked small objects in the block pointed to by hbp + * to the appropriate object free list. + * If entirely empty blocks are to be completely deallocated, then + * caller should perform that check. + */ +STATIC void GC_reclaim_small_nonempty_block(struct hblk *hbp, + GC_bool report_if_found) +{ + hdr *hhdr = HDR(hbp); + word sz = hhdr -> hb_sz; + struct obj_kind * ok = &GC_obj_kinds[hhdr -> hb_obj_kind]; + void **flh = &(ok -> ok_freelist[BYTES_TO_GRANULES(sz)]); + + hhdr -> hb_last_reclaimed = (unsigned short) GC_gc_no; + + if (report_if_found) { + GC_reclaim_check(hbp, hhdr, sz); + } else { + *flh = GC_reclaim_generic(hbp, hhdr, sz, ok -> ok_init, + (ptr_t)(*flh), &GC_bytes_found); + } +} + +#ifdef ENABLE_DISCLAIM + STATIC void GC_disclaim_and_reclaim_or_free_small_block(struct hblk *hbp) + { + hdr *hhdr = HDR(hbp); + word sz = hhdr -> hb_sz; + struct obj_kind * ok = &GC_obj_kinds[hhdr -> hb_obj_kind]; + void **flh = &(ok -> ok_freelist[BYTES_TO_GRANULES(sz)]); + void *flh_next; + + hhdr -> hb_last_reclaimed = (unsigned short) GC_gc_no; + flh_next = GC_reclaim_generic(hbp, hhdr, sz, ok -> ok_init, + (ptr_t)(*flh), &GC_bytes_found); + if (hhdr -> hb_n_marks) + *flh = flh_next; + else { + GC_bytes_found += HBLKSIZE; + GC_freehblk(hbp); + } + } +#endif /* ENABLE_DISCLAIM */ + +/* + * Restore an unmarked large object or an entirely empty blocks of small objects + * to the heap block free list. + * Otherwise enqueue the block for later processing + * by GC_reclaim_small_nonempty_block. + * If report_if_found is TRUE, then process any block immediately, and + * simply report free objects; do not actually reclaim them. + */ +STATIC void GC_reclaim_block(struct hblk *hbp, word report_if_found) +{ + hdr * hhdr = HDR(hbp); + word sz = hhdr -> hb_sz; /* size of objects in current block */ + struct obj_kind * ok = &GC_obj_kinds[hhdr -> hb_obj_kind]; + + if( sz > MAXOBJBYTES ) { /* 1 big object */ + if( !mark_bit_from_hdr(hhdr, 0) ) { + if (report_if_found) { + GC_add_leaked((ptr_t)hbp); + } else { + word blocks; + +# ifdef ENABLE_DISCLAIM + if (EXPECT(hhdr->hb_flags & HAS_DISCLAIM, 0)) { + struct obj_kind *ok = &GC_obj_kinds[hhdr->hb_obj_kind]; + if ((*ok->ok_disclaim_proc)(hbp)) { + /* Not disclaimed => resurrect the object. */ + set_mark_bit_from_hdr(hhdr, 0); + goto in_use; + } + } +# endif + blocks = OBJ_SZ_TO_BLOCKS(sz); + if (blocks > 1) { + GC_large_allocd_bytes -= blocks * HBLKSIZE; + } + GC_bytes_found += sz; + GC_freehblk(hbp); + } + } else { +# ifdef ENABLE_DISCLAIM + in_use: +# endif + if (IS_PTRFREE_SAFE(hhdr)) { + GC_atomic_in_use += sz; + } else { + GC_composite_in_use += sz; + } + } + } else { + GC_bool empty = GC_block_empty(hhdr); +# ifdef PARALLEL_MARK + /* Count can be low or one too high because we sometimes */ + /* have to ignore decrements. Objects can also potentially */ + /* be repeatedly marked by each marker. */ + /* Here we assume two markers, but this is extremely */ + /* unlikely to fail spuriously with more. And if it does, it */ + /* should be looked at. */ + GC_ASSERT(hhdr -> hb_n_marks <= 2 * (HBLKSIZE/sz + 1) + 16); +# else + GC_ASSERT(sz * hhdr -> hb_n_marks <= HBLKSIZE); +# endif + if (report_if_found) { + GC_reclaim_small_nonempty_block(hbp, TRUE /* report_if_found */); + } else if (empty) { +# ifdef ENABLE_DISCLAIM + if ((hhdr -> hb_flags & HAS_DISCLAIM) != 0) { + GC_disclaim_and_reclaim_or_free_small_block(hbp); + } else +# endif + /* else */ { + GC_bytes_found += HBLKSIZE; + GC_freehblk(hbp); + } + } else if (GC_find_leak || !GC_block_nearly_full(hhdr)) { + /* group of smaller objects, enqueue the real work */ + struct hblk **rlh = ok -> ok_reclaim_list; + + if (rlh != NULL) { + rlh += BYTES_TO_GRANULES(sz); + hhdr -> hb_next = *rlh; + *rlh = hbp; + } + } /* else not worth salvaging. */ + /* We used to do the nearly_full check later, but we */ + /* already have the right cache context here. Also */ + /* doing it here avoids some silly lock contention in */ + /* GC_malloc_many. */ + if (IS_PTRFREE_SAFE(hhdr)) { + GC_atomic_in_use += sz * hhdr -> hb_n_marks; + } else { + GC_composite_in_use += sz * hhdr -> hb_n_marks; + } + } +} + +#if !defined(NO_DEBUGGING) +/* Routines to gather and print heap block info */ +/* intended for debugging. Otherwise should be called */ +/* with lock. */ + +struct Print_stats +{ + size_t number_of_blocks; + size_t total_bytes; +}; + +#ifdef USE_MARK_BYTES + +/* Return the number of set mark bits in the given header. */ +/* Remains externally visible as used by GNU GCJ currently. */ +int GC_n_set_marks(hdr *hhdr) +{ + int result = 0; + int i; + word sz = hhdr -> hb_sz; + int offset = (int)MARK_BIT_OFFSET(sz); + int limit = (int)FINAL_MARK_BIT(sz); + + for (i = 0; i < limit; i += offset) { + result += hhdr -> hb_marks[i]; + } + GC_ASSERT(hhdr -> hb_marks[limit]); + return(result); +} + +#else + +/* Number of set bits in a word. Not performance critical. */ +static int set_bits(word n) +{ + word m = n; + int result = 0; + + while (m > 0) { + if (m & 1) result++; + m >>= 1; + } + return(result); +} + +int GC_n_set_marks(hdr *hhdr) +{ + int result = 0; + int i; + int n_mark_words; +# ifdef MARK_BIT_PER_OBJ + int n_objs = (int)HBLK_OBJS(hhdr -> hb_sz); + + if (0 == n_objs) n_objs = 1; + n_mark_words = divWORDSZ(n_objs + WORDSZ - 1); +# else /* MARK_BIT_PER_GRANULE */ + n_mark_words = MARK_BITS_SZ; +# endif + for (i = 0; i < n_mark_words - 1; i++) { + result += set_bits(hhdr -> hb_marks[i]); + } +# ifdef MARK_BIT_PER_OBJ + result += set_bits((hhdr -> hb_marks[n_mark_words - 1]) + << (n_mark_words * WORDSZ - n_objs)); +# else + result += set_bits(hhdr -> hb_marks[n_mark_words - 1]); +# endif + return(result - 1); +} + +#endif /* !USE_MARK_BYTES */ + +STATIC void GC_print_block_descr(struct hblk *h, + word /* struct PrintStats */ raw_ps) +{ + hdr * hhdr = HDR(h); + size_t bytes = hhdr -> hb_sz; + struct Print_stats *ps; + unsigned n_marks = GC_n_set_marks(hhdr); + unsigned n_objs = (unsigned)HBLK_OBJS(bytes); + + if (0 == n_objs) n_objs = 1; + if (hhdr -> hb_n_marks != n_marks) { + GC_printf("%u,%u,%u!=%u,%u\n", hhdr->hb_obj_kind, (unsigned)bytes, + (unsigned)hhdr->hb_n_marks, n_marks, n_objs); + } else { + GC_printf("%u,%u,%u,%u\n", hhdr->hb_obj_kind, (unsigned)bytes, + n_marks, n_objs); + } + + ps = (struct Print_stats *)raw_ps; + ps->total_bytes += (bytes + (HBLKSIZE-1)) & ~(HBLKSIZE-1); /* round up */ + ps->number_of_blocks++; +} + +void GC_print_block_list(void) +{ + struct Print_stats pstats; + + GC_printf("kind(0=ptrfree,1=normal,2=unc.)," + "size_in_bytes,#_marks_set,#objs\n"); + pstats.number_of_blocks = 0; + pstats.total_bytes = 0; + GC_apply_to_all_blocks(GC_print_block_descr, (word)&pstats); + GC_printf("blocks= %lu, bytes= %lu\n", + (unsigned long)pstats.number_of_blocks, + (unsigned long)pstats.total_bytes); +} + +#include "gc_inline.h" /* for GC_print_free_list prototype */ + +/* Currently for debugger use only: */ +GC_API void GC_CALL GC_print_free_list(int kind, size_t sz_in_granules) +{ + void *flh_next; + int n; + + GC_ASSERT(kind < MAXOBJKINDS); + GC_ASSERT(sz_in_granules <= MAXOBJGRANULES); + flh_next = GC_obj_kinds[kind].ok_freelist[sz_in_granules]; + for (n = 0; flh_next; n++) { + GC_printf("Free object in heap block %p [%d]: %p\n", + (void *)HBLKPTR(flh_next), n, flh_next); + flh_next = obj_link(flh_next); + } +} + +#endif /* !NO_DEBUGGING */ + +/* + * Clear all obj_link pointers in the list of free objects *flp. + * Clear *flp. + * This must be done before dropping a list of free gcj-style objects, + * since may otherwise end up with dangling "descriptor" pointers. + * It may help for other pointer-containing objects. + */ +STATIC void GC_clear_fl_links(void **flp) +{ + void *next = *flp; + + while (0 != next) { + *flp = 0; + flp = &(obj_link(next)); + next = *flp; + } +} + +/* + * Perform GC_reclaim_block on the entire heap, after first clearing + * small object free lists (if we are not just looking for leaks). + */ +GC_INNER void GC_start_reclaim(GC_bool report_if_found) +{ + unsigned kind; + +# if defined(PARALLEL_MARK) + GC_ASSERT(0 == GC_fl_builder_count); +# endif + /* Reset in use counters. GC_reclaim_block recomputes them. */ + GC_composite_in_use = 0; + GC_atomic_in_use = 0; + /* Clear reclaim- and free-lists */ + for (kind = 0; kind < GC_n_kinds; kind++) { + struct hblk ** rlist = GC_obj_kinds[kind].ok_reclaim_list; + GC_bool should_clobber = (GC_obj_kinds[kind].ok_descriptor != 0); + + if (rlist == 0) continue; /* This kind not used. */ + if (!report_if_found) { + void **fop; + void **lim = &(GC_obj_kinds[kind].ok_freelist[MAXOBJGRANULES+1]); + + for (fop = GC_obj_kinds[kind].ok_freelist; + (word)fop < (word)lim; (*(word **)&fop)++) { + if (*fop != 0) { + if (should_clobber) { + GC_clear_fl_links(fop); + } else { + *fop = 0; + } + } + } + } /* otherwise free list objects are marked, */ + /* and its safe to leave them */ + BZERO(rlist, (MAXOBJGRANULES + 1) * sizeof(void *)); + } + + + /* Go through all heap blocks (in hblklist) and reclaim unmarked objects */ + /* or enqueue the block for later processing. */ + GC_apply_to_all_blocks(GC_reclaim_block, (word)report_if_found); + +# ifdef EAGER_SWEEP + /* This is a very stupid thing to do. We make it possible anyway, */ + /* so that you can convince yourself that it really is very stupid. */ + GC_reclaim_all((GC_stop_func)0, FALSE); +# elif defined(ENABLE_DISCLAIM) + /* However, make sure to clear reclaimable objects of kinds with */ + /* unconditional marking enabled before we do any significant */ + /* marking work. */ + GC_reclaim_unconditionally_marked(); +# endif +# if defined(PARALLEL_MARK) + GC_ASSERT(0 == GC_fl_builder_count); +# endif + +} + +/* + * Sweep blocks of the indicated object size and kind until either the + * appropriate free list is nonempty, or there are no more blocks to + * sweep. + */ +GC_INNER void GC_continue_reclaim(word sz /* granules */, int kind) +{ + hdr * hhdr; + struct hblk * hbp; + struct obj_kind * ok = &(GC_obj_kinds[kind]); + struct hblk ** rlh = ok -> ok_reclaim_list; + void **flh = &(ok -> ok_freelist[sz]); + + if (rlh == 0) return; /* No blocks of this kind. */ + rlh += sz; + while ((hbp = *rlh) != 0) { + hhdr = HDR(hbp); + *rlh = hhdr -> hb_next; + GC_reclaim_small_nonempty_block(hbp, FALSE); + if (*flh != 0) break; + } +} + +/* + * Reclaim all small blocks waiting to be reclaimed. + * Abort and return FALSE when/if (*stop_func)() returns TRUE. + * If this returns TRUE, then it's safe to restart the world + * with incorrectly cleared mark bits. + * If ignore_old is TRUE, then reclaim only blocks that have been + * recently reclaimed, and discard the rest. + * Stop_func may be 0. + */ +GC_INNER GC_bool GC_reclaim_all(GC_stop_func stop_func, GC_bool ignore_old) +{ + word sz; + unsigned kind; + hdr * hhdr; + struct hblk * hbp; + struct obj_kind * ok; + struct hblk ** rlp; + struct hblk ** rlh; +# ifndef NO_CLOCK + CLOCK_TYPE start_time = 0; /* initialized to prevent warning. */ + + if (GC_print_stats == VERBOSE) + GET_TIME(start_time); +# endif + + for (kind = 0; kind < GC_n_kinds; kind++) { + ok = &(GC_obj_kinds[kind]); + rlp = ok -> ok_reclaim_list; + if (rlp == 0) continue; + for (sz = 1; sz <= MAXOBJGRANULES; sz++) { + rlh = rlp + sz; + while ((hbp = *rlh) != 0) { + if (stop_func != (GC_stop_func)0 && (*stop_func)()) { + return(FALSE); + } + hhdr = HDR(hbp); + *rlh = hhdr -> hb_next; + if (!ignore_old || hhdr -> hb_last_reclaimed == GC_gc_no - 1) { + /* It's likely we'll need it this time, too */ + /* It's been touched recently, so this */ + /* shouldn't trigger paging. */ + GC_reclaim_small_nonempty_block(hbp, FALSE); + } + } + } + } +# ifndef NO_CLOCK + if (GC_print_stats == VERBOSE) { + CLOCK_TYPE done_time; + + GET_TIME(done_time); + GC_verbose_log_printf("Disposing of reclaim lists took %lu msecs\n", + MS_TIME_DIFF(done_time,start_time)); + } +# endif + return(TRUE); +} + +#if !defined(EAGER_SWEEP) && defined(ENABLE_DISCLAIM) +/* We do an eager sweep on heap blocks where unconditional marking has */ +/* been enabled, so that any reclaimable objects have been reclaimed */ +/* before we start marking. This is a simplified GC_reclaim_all */ +/* restricted to kinds where ok_mark_unconditionally is true. */ + STATIC void GC_reclaim_unconditionally_marked(void) + { + word sz; + unsigned kind; + hdr * hhdr; + struct hblk * hbp; + struct obj_kind * ok; + struct hblk ** rlp; + struct hblk ** rlh; + + for (kind = 0; kind < GC_n_kinds; kind++) { + ok = &(GC_obj_kinds[kind]); + if (!ok->ok_mark_unconditionally) + continue; + rlp = ok->ok_reclaim_list; + if (rlp == 0) + continue; + for (sz = 1; sz <= MAXOBJGRANULES; sz++) { + rlh = rlp + sz; + while ((hbp = *rlh) != 0) { + hhdr = HDR(hbp); + *rlh = hhdr->hb_next; + GC_reclaim_small_nonempty_block(hbp, FALSE); + } + } + } + } +#endif /* !EAGER_SWEEP && ENABLE_DISCLAIM */ + +struct enumerate_reachable_s { + GC_reachable_object_proc proc; + void *client_data; +}; + +STATIC void GC_do_enumerate_reachable_objects(struct hblk *hbp, word ped) +{ + struct hblkhdr *hhdr = HDR(hbp); + size_t sz = (size_t)hhdr->hb_sz; + size_t bit_no; + char *p, *plim; + + if (GC_block_empty(hhdr)) { + return; + } + + p = hbp->hb_body; + if (sz > MAXOBJBYTES) { /* one big object */ + plim = p; + } else { + plim = hbp->hb_body + HBLKSIZE - sz; + } + /* Go through all words in block. */ + for (bit_no = 0; p <= plim; bit_no += MARK_BIT_OFFSET(sz), p += sz) { + if (mark_bit_from_hdr(hhdr, bit_no)) { + ((struct enumerate_reachable_s *)ped)->proc(p, sz, + ((struct enumerate_reachable_s *)ped)->client_data); + } + } +} + +GC_API void GC_CALL GC_enumerate_reachable_objects_inner( + GC_reachable_object_proc proc, + void *client_data) +{ + struct enumerate_reachable_s ed; + + GC_ASSERT(I_HOLD_LOCK()); + ed.proc = proc; + ed.client_data = client_data; + GC_apply_to_all_blocks(GC_do_enumerate_reachable_objects, (word)&ed); +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/sparc_mach_dep.S b/unity-2019.4.24f1-mbe/external/bdwgc/sparc_mach_dep.S new file mode 100644 index 000000000..d204dc436 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/sparc_mach_dep.S @@ -0,0 +1,61 @@ +! SPARCompiler 3.0 and later apparently no longer handles +! asm outside functions. So we need a separate .s file +! This is only set up for SunOS 5, not SunOS 4. +! Assumes this is called before the stack contents are +! examined. + + .seg "text" + .globl GC_save_regs_in_stack +GC_save_regs_in_stack: +#if defined(__arch64__) || defined(__sparcv9) + save %sp,-128,%sp + flushw + ret + restore %sp,2047+128,%o0 +#else /* 32 bit SPARC */ + ta 0x3 ! ST_FLUSH_WINDOWS + mov %sp,%o0 + retl + nop +#endif /* 32 bit SPARC */ +.GC_save_regs_in_stack_end: + .size GC_save_regs_in_stack,.GC_save_regs_in_stack_end-GC_save_regs_in_stack + +! GC_clear_stack_inner(arg, limit) clears stack area up to limit and +! returns arg. Stack clearing is crucial on SPARC, so we supply +! an assembly version that s more careful. Assumes limit is hotter +! than sp, and limit is 8 byte aligned. + .globl GC_clear_stack_inner +GC_clear_stack_inner: +#if defined(__arch64__) || defined(__sparcv9) + mov %sp,%o2 ! Save sp + add %sp,2047-8,%o3 ! p = sp+bias-8 + add %o1,-2047-192,%sp ! Move sp out of the way, + ! so that traps still work. + ! Includes some extra words + ! so we can be sloppy below. +loop: + stx %g0,[%o3] ! *(long *)p = 0 + cmp %o3,%o1 + bgu,pt %xcc, loop ! if (p > limit) goto loop + add %o3,-8,%o3 ! p -= 8 (delay slot) + retl + mov %o2,%sp ! Restore sp., delay slot +#else /* 32 bit SPARC */ + mov %sp,%o2 ! Save sp + add %sp,-8,%o3 ! p = sp-8 + clr %g1 ! [g0,g1] = 0 + add %o1,-0x60,%sp ! Move sp out of the way, + ! so that traps still work. + ! Includes some extra words + ! so we can be sloppy below. +loop: + std %g0,[%o3] ! *(long long *)p = 0 + cmp %o3,%o1 + bgu loop ! if (p > limit) goto loop + add %o3,-8,%o3 ! p -= 8 (delay slot) + retl + mov %o2,%sp ! Restore sp., delay slot +#endif /* 32 bit SPARC */ +.GC_clear_stack_inner_end: + .size GC_clear_stack_inner,.GC_clear_stack_inner_end-GC_clear_stack_inner diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/sparc_netbsd_mach_dep.s b/unity-2019.4.24f1-mbe/external/bdwgc/sparc_netbsd_mach_dep.s new file mode 100644 index 000000000..14feb15ee --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/sparc_netbsd_mach_dep.s @@ -0,0 +1,34 @@ +! SPARCompiler 3.0 and later apparently no longer handles +! asm outside functions. So we need a separate .s file +! This is only set up for SunOS 4. +! Assumes this is called before the stack contents are +! examined. + +#include "machine/asm.h" + + .seg "text" + .globl _C_LABEL(GC_save_regs_in_stack) + .globl _C_LABEL(GC_push_regs) +_C_LABEL(GC_save_regs_in_stack): +_C_LABEL(GC_push_regs): + ta 0x3 ! ST_FLUSH_WINDOWS + mov %sp,%o0 + retl + nop + + .globl _C_LABEL(GC_clear_stack_inner) +_C_LABEL(GC_clear_stack_inner): + mov %sp,%o2 ! Save sp + add %sp,-8,%o3 ! p = sp-8 + clr %g1 ! [g0,g1] = 0 + add %o1,-0x60,%sp ! Move sp out of the way, + ! so that traps still work. + ! Includes some extra words + ! so we can be sloppy below. +loop: + std %g0,[%o3] ! *(long long *)p = 0 + cmp %o3,%o1 + bgu loop ! if (p > limit) goto loop + add %o3,-8,%o3 ! p -= 8 (delay slot) + retl + mov %o2,%sp ! Restore sp., delay slot diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/sparc_sunos4_mach_dep.s b/unity-2019.4.24f1-mbe/external/bdwgc/sparc_sunos4_mach_dep.s new file mode 100644 index 000000000..923f5eaa2 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/sparc_sunos4_mach_dep.s @@ -0,0 +1,32 @@ +! SPARCompiler 3.0 and later apparently no longer handles +! asm outside functions. So we need a separate .s file +! This is only set up for SunOS 4. +! Assumes this is called before the stack contents are +! examined. + + .seg "text" + .globl _GC_save_regs_in_stack + .globl _GC_push_regs +_GC_save_regs_in_stack: +_GC_push_regs: + ta 0x3 ! ST_FLUSH_WINDOWS + mov %sp,%o0 + retl + nop + + .globl _GC_clear_stack_inner +_GC_clear_stack_inner: + mov %sp,%o2 ! Save sp + add %sp,-8,%o3 ! p = sp-8 + clr %g1 ! [g0,g1] = 0 + add %o1,-0x60,%sp ! Move sp out of the way, + ! so that traps still work. + ! Includes some extra words + ! so we can be sloppy below. +loop: + std %g0,[%o3] ! *(long long *)p = 0 + cmp %o3,%o1 + bgu loop ! if (p > limit) goto loop + add %o3,-8,%o3 ! p -= 8 (delay slot) + retl + mov %o2,%sp ! Restore sp., delay slot diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/specific.c b/unity-2019.4.24f1-mbe/external/bdwgc/specific.c new file mode 100644 index 000000000..f17d3254b --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/specific.c @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2000 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#include "private/thread_local_alloc.h" + /* To determine type of tsd impl. */ + /* Includes private/specific.h */ + /* if needed. */ + +#if defined(USE_CUSTOM_SPECIFIC) + +static const tse invalid_tse = {INVALID_QTID, 0, 0, INVALID_THREADID}; + /* A thread-specific data entry which will never */ + /* appear valid to a reader. Used to fill in empty */ + /* cache entries to avoid a check for 0. */ + +GC_INNER int GC_key_create_inner(tsd ** key_ptr) +{ + int i; + int ret; + tsd * result; + + GC_ASSERT(I_HOLD_LOCK()); + /* A quick alignment check, since we need atomic stores */ + GC_ASSERT((word)(&invalid_tse.next) % sizeof(tse *) == 0); + result = (tsd *)MALLOC_CLEAR(sizeof(tsd)); + if (NULL == result) return ENOMEM; + ret = pthread_mutex_init(&result->lock, NULL); + if (ret != 0) return ret; + for (i = 0; i < TS_CACHE_SIZE; ++i) { + result -> cache[i] = (/* no const */ tse *)&invalid_tse; + } +# ifdef GC_ASSERTIONS + for (i = 0; i < TS_HASH_SIZE; ++i) { + GC_ASSERT(result -> hash[i].p == 0); + } +# endif + *key_ptr = result; + return 0; +} + +GC_INNER int GC_setspecific(tsd * key, void * value) +{ + pthread_t self = pthread_self(); + int hash_val = HASH(self); + volatile tse * entry; + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(self != INVALID_THREADID); + GC_dont_gc++; /* disable GC */ + entry = (volatile tse *)MALLOC_CLEAR(sizeof(tse)); + GC_dont_gc--; + if (0 == entry) return ENOMEM; + + pthread_mutex_lock(&(key -> lock)); + /* Could easily check for an existing entry here. */ + entry -> next = key->hash[hash_val].p; + entry -> thread = self; + entry -> value = value; + GC_ASSERT(entry -> qtid == INVALID_QTID); + /* There can only be one writer at a time, but this needs to be */ + /* atomic with respect to concurrent readers. */ + AO_store_release(&key->hash[hash_val].ao, (AO_t)entry); + GC_dirty((/* no volatile */ void *)entry); + GC_dirty(key->hash + hash_val); + pthread_mutex_unlock(&(key -> lock)); + return 0; +} + +/* Remove thread-specific data for a given thread. This function is */ +/* called at fork from the child process for all threads except for the */ +/* survived one. GC_remove_specific() should be called on thread exit. */ +GC_INNER void GC_remove_specific_after_fork(tsd * key, pthread_t t) +{ + unsigned hash_val = HASH(t); + tse *entry; + tse *prev = NULL; + +# ifdef CAN_HANDLE_FORK + /* Both GC_setspecific and GC_remove_specific should be called */ + /* with the allocation lock held to ensure the consistency of */ + /* the hash table in the forked child. */ + GC_ASSERT(I_HOLD_LOCK()); +# endif + pthread_mutex_lock(&(key -> lock)); + entry = key->hash[hash_val].p; + while (entry != NULL && !THREAD_EQUAL(entry->thread, t)) { + prev = entry; + entry = entry->next; + } + /* Invalidate qtid field, since qtids may be reused, and a later */ + /* cache lookup could otherwise find this entry. */ + if (entry != NULL) { + entry -> qtid = INVALID_QTID; + if (NULL == prev) { + key->hash[hash_val].p = entry->next; + GC_dirty(key->hash + hash_val); + } else { + prev->next = entry->next; + GC_dirty(prev); + } + /* Atomic! concurrent accesses still work. */ + /* They must, since readers don't lock. */ + /* We shouldn't need a volatile access here, */ + /* since both this and the preceding write */ + /* should become visible no later than */ + /* the pthread_mutex_unlock() call. */ + } + /* If we wanted to deallocate the entry, we'd first have to clear */ + /* any cache entries pointing to it. That probably requires */ + /* additional synchronization, since we can't prevent a concurrent */ + /* cache lookup, which should still be examining deallocated memory.*/ + /* This can only happen if the concurrent access is from another */ + /* thread, and hence has missed the cache, but still... */ +# ifdef LINT2 + GC_noop1((word)entry); +# endif + + /* With GC, we're done, since the pointers from the cache will */ + /* be overwritten, all local pointers to the entries will be */ + /* dropped, and the entry will then be reclaimed. */ + pthread_mutex_unlock(&(key -> lock)); +} + +/* Note that even the slow path doesn't lock. */ +GC_INNER void * GC_slow_getspecific(tsd * key, word qtid, + tse * volatile * cache_ptr) +{ + pthread_t self = pthread_self(); + unsigned hash_val = HASH(self); + tse *entry = key->hash[hash_val].p; + + GC_ASSERT(qtid != INVALID_QTID); + while (entry != NULL && !THREAD_EQUAL(entry->thread, self)) { + entry = entry -> next; + } + if (entry == NULL) return NULL; + /* Set cache_entry. */ + entry -> qtid = (AO_t)qtid; + /* It's safe to do this asynchronously. Either value */ + /* is safe, though may produce spurious misses. */ + /* We're replacing one qtid with another one for the */ + /* same thread. */ + *cache_ptr = entry; + /* Again this is safe since pointer assignments are */ + /* presumed atomic, and either pointer is valid. */ + return entry -> value; +} + +#ifdef GC_ASSERTIONS + /* Check that that all elements of the data structure associated */ + /* with key are marked. */ + void GC_check_tsd_marks(tsd *key) + { + int i; + tse *p; + + if (!GC_is_marked(GC_base(key))) { + ABORT("Unmarked thread-specific-data table"); + } + for (i = 0; i < TS_HASH_SIZE; ++i) { + for (p = key->hash[i].p; p != 0; p = p -> next) { + if (!GC_is_marked(GC_base(p))) { + ABORT_ARG1("Unmarked thread-specific-data entry", + " at %p", (void *)p); + } + } + } + for (i = 0; i < TS_CACHE_SIZE; ++i) { + p = key -> cache[i]; + if (p != &invalid_tse && !GC_is_marked(GC_base(p))) { + ABORT_ARG1("Unmarked cached thread-specific-data entry", + " at %p", (void *)p); + } + } + } +#endif /* GC_ASSERTIONS */ + +#endif /* USE_CUSTOM_SPECIFIC */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/tests/CMakeLists.txt b/unity-2019.4.24f1-mbe/external/bdwgc/tests/CMakeLists.txt new file mode 100644 index 000000000..3c84220b9 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/tests/CMakeLists.txt @@ -0,0 +1,47 @@ +# +# Copyright (c) 1994 by Xerox Corporation. All rights reserved. +# Copyright (c) 1996 by Silicon Graphics. All rights reserved. +# Copyright (c) 1998 by Fergus Henderson. All rights reserved. +# Copyright (c) 2000-2010 by Hewlett-Packard Company. All rights reserved. +## +# THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED +# OR IMPLIED. ANY USE IS AT YOUR OWN RISK. +## +# Permission is hereby granted to use or copy this program +# for any purpose, provided the above notices are retained on all copies. +# Permission to modify the code and to distribute modified code is granted, +# provided the above notices are retained, and a notice that the code was +# modified is included with the above copyright notice. +## + +ADD_DEFINITIONS(-DGC_NOT_DLL) + +# Compile some tests as C++ to test extern "C" in header files. +SET_SOURCE_FILES_PROPERTIES( + leak_test.c + test.c + PROPERTIES LANGUAGE CXX) + +ADD_EXECUTABLE(gctest WIN32 test.c) +TARGET_LINK_LIBRARIES(gctest gc-lib) +ADD_TEST(NAME gctest COMMAND gctest) + +ADD_EXECUTABLE(hugetest huge_test.c) +TARGET_LINK_LIBRARIES(hugetest gc-lib) +ADD_TEST(NAME hugetest COMMAND hugetest) + +ADD_EXECUTABLE(leaktest leak_test.c) +TARGET_LINK_LIBRARIES(leaktest gc-lib) +ADD_TEST(NAME leaktest COMMAND leaktest) + +ADD_EXECUTABLE(middletest middle.c) +TARGET_LINK_LIBRARIES(middletest gc-lib) +ADD_TEST(NAME middletest COMMAND middletest) + +ADD_EXECUTABLE(realloc_test realloc_test.c) +TARGET_LINK_LIBRARIES(realloc_test gc-lib) +ADD_TEST(NAME realloc_test COMMAND realloc_test) + +ADD_EXECUTABLE(smashtest smash_test.c) +TARGET_LINK_LIBRARIES(smashtest gc-lib) +ADD_TEST(NAME smashtest COMMAND smashtest) diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/tests/disclaim_bench.c b/unity-2019.4.24f1-mbe/external/bdwgc/tests/disclaim_bench.c new file mode 100644 index 000000000..8be81cf69 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/tests/disclaim_bench.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2011 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + */ + +#include +#include +#include + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "gc_disclaim.h" + +/* Include gc_priv.h is done after including GC public headers, so */ +/* that GC_BUILD has no effect on the public prototypes. */ +#include "private/gc_priv.h" /* for CLOCK_TYPE, COVERT_DATAFLOW, GC_random */ + +#ifdef LINT2 +# undef rand +# define rand() (int)GC_random() +#endif + +#define my_assert(e) \ + if (!(e)) { \ + fprintf(stderr, "Assertion failure, line %d: " #e "\n", __LINE__); \ + exit(-1); \ + } + +static int free_count = 0; + +struct testobj_s { + struct testobj_s *keep_link; + int i; +}; + +typedef struct testobj_s *testobj_t; + +void GC_CALLBACK testobj_finalize(void *obj, void *carg) +{ + ++*(int *)carg; + my_assert(((testobj_t)obj)->i == 109); + ((testobj_t)obj)->i = 110; +} + +static const struct GC_finalizer_closure fclos = { + testobj_finalize, + &free_count +}; + +testobj_t testobj_new(int model) +{ + testobj_t obj; + switch (model) { + case 0: + obj = GC_NEW(struct testobj_s); + if (obj != NULL) + GC_REGISTER_FINALIZER_NO_ORDER(obj, testobj_finalize, + &free_count, NULL, NULL); + break; + case 1: + obj = (testobj_t)GC_finalized_malloc(sizeof(struct testobj_s), + &fclos); + break; + case 2: + obj = GC_NEW(struct testobj_s); + break; + default: + exit(-1); + } + if (obj == NULL) { + fprintf(stderr, "Out of memory!\n"); + exit(3); + } + my_assert(obj->i == 0 && obj->keep_link == NULL); + obj->i = 109; + return obj; +} + +#define ALLOC_CNT (4*1024*1024) +#define KEEP_CNT (32*1024) + +static char const *model_str[3] = { + "regular finalization", + "finalize on reclaim", + "no finalization" +}; + +int main(int argc, char **argv) +{ + int i; + int model, model_min, model_max; + testobj_t *keep_arr; + + GC_INIT(); + GC_init_finalized_malloc(); + if (argc == 2 && strcmp(argv[1], "--help") == 0) { + fprintf(stderr, + "Usage: %s [FINALIZATION_MODEL]\n" + "\t0 -- original finalization\n" + "\t1 -- finalization on reclaim\n" + "\t2 -- no finalization\n", argv[0]); + return 1; + } + if (argc == 2) { + model_min = model_max = (int)COVERT_DATAFLOW(atoi(argv[1])); + if (model_min < 0 || model_max > 2) + exit(2); + } + else { + model_min = 0; + model_max = 2; + } + + keep_arr = (testobj_t *)GC_MALLOC(sizeof(void *) * KEEP_CNT); + if (NULL == keep_arr) { + fprintf(stderr, "Out of memory!\n"); + exit(3); + } + + printf("\t\t\tfin. ratio time/s time/fin.\n"); + for (model = model_min; model <= model_max; ++model) { + double t = 0.0; +# ifndef NO_CLOCK + CLOCK_TYPE tI, tF; + + GET_TIME(tI); +# endif + free_count = 0; + for (i = 0; i < ALLOC_CNT; ++i) { + int k = rand() % KEEP_CNT; + keep_arr[k] = testobj_new(model); + } + GC_gcollect(); +# ifndef NO_CLOCK + GET_TIME(tF); + t = MS_TIME_DIFF(tF, tI)*1e-3; +# endif + + if (model < 2 && free_count > 0) + printf("%20s: %12.4f %12g %12g\n", model_str[model], + free_count/(double)ALLOC_CNT, t, t/free_count); + else + printf("%20s: %12.4f %12g %12s\n", + model_str[model], 0.0, t, "N/A"); + } + return 0; +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/tests/disclaim_test.c b/unity-2019.4.24f1-mbe/external/bdwgc/tests/disclaim_test.c new file mode 100644 index 000000000..8cfb53990 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/tests/disclaim_test.c @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2011 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + */ + +/* Test that objects reachable from an object allocated with */ +/* GC_malloc_with_finalizer is not reclaimable before the finalizer */ +/* is called. */ + +#include +#include +#include + +#ifdef HAVE_CONFIG_H + /* For GC_[P]THREADS */ +# include "config.h" +#endif + +#undef GC_NO_THREAD_REDIRECTS +#include "gc_disclaim.h" + +#ifdef LINT2 + /* Avoid include gc_priv.h. */ +# ifndef GC_API_PRIV +# define GC_API_PRIV GC_API +# endif +# ifdef __cplusplus + extern "C" { +# endif + GC_API_PRIV long GC_random(void); +# ifdef __cplusplus + } /* extern "C" */ +# endif +# undef rand +# define rand() (int)GC_random() +#endif /* LINT2 */ + +#define my_assert(e) \ + if (!(e)) { \ + fflush(stdout); \ + fprintf(stderr, "Assertion failure, line %d: " #e "\n", __LINE__); \ + exit(-1); \ + } + +int memeq(void *s, int c, size_t len) +{ + while (len--) { + if (*(char *)s != c) + return 0; + s = (char *)s + 1; + } + return 1; +} + +void GC_CALLBACK misc_sizes_dct(void *obj, void *cd) +{ + unsigned log_size = *(unsigned char *)obj; + size_t size; + + my_assert(log_size < sizeof(size_t) * 8); + my_assert(cd == NULL); + size = (size_t)1 << log_size; + my_assert(memeq((char *)obj + 1, 0x56, size - 1)); +} + +void test_misc_sizes(void) +{ + static const struct GC_finalizer_closure fc = { misc_sizes_dct, NULL }; + int i; + for (i = 1; i <= 20; ++i) { /* Up to 1 MiB. */ + void *p = GC_finalized_malloc((size_t)1 << i, &fc); + if (p == NULL) { + fprintf(stderr, "Out of memory!\n"); + exit(3); + } + my_assert(memeq(p, 0, (size_t)1 << i)); + memset(p, 0x56, (size_t)1 << i); + *(unsigned char *)p = i; + } +} + +typedef struct pair_s *pair_t; + +struct pair_s { + char magic[16]; + int checksum; + pair_t car; + pair_t cdr; +}; + +static const char * const pair_magic = "PAIR_MAGIC_BYTES"; + +int is_pair(pair_t p) +{ + return memcmp(p->magic, pair_magic, sizeof(p->magic)) == 0; +} + +void GC_CALLBACK pair_dct(void *obj, void *cd) +{ + pair_t p = (pair_t)obj; + int checksum; + + /* Check that obj and its car and cdr are not trashed. */ +# ifdef DEBUG_DISCLAIM_DESTRUCT + printf("Destruct %p = (%p, %p)\n", + (void *)p, (void *)p->car, (void *)p->cdr); +# endif + my_assert(GC_base(obj)); + my_assert(is_pair(p)); + my_assert(!p->car || is_pair(p->car)); + my_assert(!p->cdr || is_pair(p->cdr)); + checksum = 782; + if (p->car) checksum += p->car->checksum; + if (p->cdr) checksum += p->cdr->checksum; + my_assert(p->checksum == checksum); + + /* Invalidate it. */ + memset(p->magic, '*', sizeof(p->magic)); + p->checksum = 0; + p->car = (pair_t)cd; + p->cdr = NULL; + GC_end_stubborn_change(p); +} + +pair_t +pair_new(pair_t car, pair_t cdr) +{ + pair_t p; + static const struct GC_finalizer_closure fc = { pair_dct, NULL }; + + p = (pair_t)GC_finalized_malloc(sizeof(struct pair_s), &fc); + if (p == NULL) { + fprintf(stderr, "Out of memory!\n"); + exit(3); + } + my_assert(!is_pair(p)); + my_assert(memeq(p, 0, sizeof(struct pair_s))); + memcpy(p->magic, pair_magic, sizeof(p->magic)); + p->checksum = 782 + (car? car->checksum : 0) + (cdr? cdr->checksum : 0); + p->car = car; + p->cdr = cdr; + GC_end_stubborn_change(p); +# ifdef DEBUG_DISCLAIM_DESTRUCT + printf("Construct %p = (%p, %p)\n", + (void *)p, (void *)p->car, (void *)p->cdr); +# endif + return p; +} + +void +pair_check_rec(pair_t p) +{ + while (p) { + int checksum = 782; + if (p->car) checksum += p->car->checksum; + if (p->cdr) checksum += p->cdr->checksum; + my_assert(p->checksum == checksum); + if (rand() % 2) + p = p->car; + else + p = p->cdr; + } +} + +#ifdef GC_PTHREADS +# ifndef NTHREADS +# define NTHREADS 6 +# endif +# include +#else +# undef NTHREADS +# define NTHREADS 1 +#endif + +#define POP_SIZE 1000 +#if NTHREADS > 1 +# define MUTATE_CNT (2000000/NTHREADS) +#else +# define MUTATE_CNT 10000000 +#endif +#define GROW_LIMIT (MUTATE_CNT/10) + +void *test(void *data) +{ + int i; + pair_t pop[POP_SIZE]; + memset(pop, 0, sizeof(pop)); + for (i = 0; i < MUTATE_CNT; ++i) { + int t = rand() % POP_SIZE; + switch (rand() % (i > GROW_LIMIT? 5 : 3)) { + case 0: case 3: + if (pop[t]) + pop[t] = pop[t]->car; + break; + case 1: case 4: + if (pop[t]) + pop[t] = pop[t]->cdr; + break; + case 2: + pop[t] = pair_new(pop[rand() % POP_SIZE], + pop[rand() % POP_SIZE]); + break; + } + if (rand() % 8 == 1) + pair_check_rec(pop[rand() % POP_SIZE]); + } + return data; +} + +int main(void) +{ +# if NTHREADS > 1 + pthread_t th[NTHREADS]; + int i; +# endif + + GC_set_all_interior_pointers(0); /* for a stricter test */ + GC_INIT(); + GC_init_finalized_malloc(); +# ifndef NO_INCREMENTAL + GC_enable_incremental(); +# endif + + test_misc_sizes(); + +# if NTHREADS > 1 + printf("Threaded disclaim test.\n"); + for (i = 0; i < NTHREADS; ++i) { + int err = pthread_create(&th[i], NULL, test, NULL); + if (err) { + fprintf(stderr, "Failed to create thread # %d: %s\n", i, + strerror(err)); + exit(1); + } + } + for (i = 0; i < NTHREADS; ++i) { + int err = pthread_join(th[i], NULL); + if (err) { + fprintf(stderr, "Failed to join thread # %d: %s\n", i, + strerror(err)); + exit(69); + } + } +# else + printf("Unthreaded disclaim test.\n"); + test(NULL); +# endif + return 0; +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/tests/huge_test.c b/unity-2019.4.24f1-mbe/external/bdwgc/tests/huge_test.c new file mode 100644 index 000000000..bd3ad6c74 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/tests/huge_test.c @@ -0,0 +1,64 @@ + +#include +#include +#include + +#ifndef GC_IGNORE_WARN + /* Ignore misleading "Out of Memory!" warning (which is printed on */ + /* every GC_MALLOC call below) by defining this macro before "gc.h" */ + /* inclusion. */ +# define GC_IGNORE_WARN +#endif + +#ifndef GC_MAXIMUM_HEAP_SIZE +# define GC_MAXIMUM_HEAP_SIZE 100 * 1024 * 1024 +# define GC_INITIAL_HEAP_SIZE GC_MAXIMUM_HEAP_SIZE / 20 + /* Otherwise heap expansion aborts when deallocating large block. */ + /* That's OK. We test this corner case mostly to make sure that */ + /* it fails predictably. */ +#endif + +#ifndef GC_ATTR_ALLOC_SIZE + /* Omit alloc_size attribute to avoid compiler warnings about */ + /* exceeding maximum object size when values close to GC_SWORD_MAX */ + /* are passed to GC_MALLOC. */ +# define GC_ATTR_ALLOC_SIZE(argnum) /* empty */ +#endif + +#include "gc.h" + +/* + * Check that very large allocation requests fail. "Success" would usually + * indicate that the size was somehow converted to a negative + * number. Clients shouldn't do this, but we should fail in the + * expected manner. + */ + +#define CHECK_ALLOC_FAILED(r, sz_str) \ + do { \ + if (NULL != (r)) { \ + fprintf(stderr, \ + "Size " sz_str " allocation unexpectedly succeeded\n"); \ + exit(1); \ + } \ + } while (0) + +#define GC_WORD_MAX ((GC_word)-1) +#define GC_SWORD_MAX ((GC_signed_word)(GC_WORD_MAX >> 1)) + +int main(void) +{ + GC_INIT(); + + CHECK_ALLOC_FAILED(GC_MALLOC(GC_SWORD_MAX - 1024), "SWORD_MAX-1024"); + CHECK_ALLOC_FAILED(GC_MALLOC(GC_SWORD_MAX), "SWORD_MAX"); + CHECK_ALLOC_FAILED(GC_MALLOC((GC_word)GC_SWORD_MAX + 1), "SWORD_MAX+1"); + CHECK_ALLOC_FAILED(GC_MALLOC((GC_word)GC_SWORD_MAX + 1024), + "SWORD_MAX+1024"); + CHECK_ALLOC_FAILED(GC_MALLOC(GC_WORD_MAX - 1024), "WORD_MAX-1024"); + CHECK_ALLOC_FAILED(GC_MALLOC(GC_WORD_MAX - 16), "WORD_MAX-16"); + CHECK_ALLOC_FAILED(GC_MALLOC(GC_WORD_MAX - 8), "WORD_MAX-8"); + CHECK_ALLOC_FAILED(GC_MALLOC(GC_WORD_MAX - 4), "WORD_MAX-4"); + CHECK_ALLOC_FAILED(GC_MALLOC(GC_WORD_MAX), "WORD_MAX"); + return 0; +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/tests/initsecondarythread.c b/unity-2019.4.24f1-mbe/external/bdwgc/tests/initsecondarythread.c new file mode 100644 index 000000000..96a21e2ef --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/tests/initsecondarythread.c @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2011 Ludovic Courtes + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* Make sure 'GC_INIT' can be called from threads other than the initial + * thread. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifndef GC_THREADS +# define GC_THREADS +#endif + +#define GC_NO_THREAD_REDIRECTS 1 + /* Do not redirect thread creation and join calls. */ + +#include "gc.h" + +#ifdef GC_PTHREADS +# include +#else +# include +#endif + +#include +#include + +#ifdef GC_PTHREADS + static void *thread(void *arg) +#else + static DWORD WINAPI thread(LPVOID arg) +#endif +{ + GC_INIT(); + (void)GC_MALLOC(123); + (void)GC_MALLOC(12345); +# ifdef GC_PTHREADS + return arg; +# else + return (DWORD)(GC_word)arg; +# endif +} + +#include "private/gcconfig.h" + +int main(void) +{ +# ifdef GC_PTHREADS + int code; + pthread_t t; + +# ifdef LINT2 + t = pthread_self(); /* explicitly initialize to some value */ +# endif +# else + HANDLE t; + DWORD thread_id; +# endif +# if !(defined(BEOS) || defined(MSWIN32) || defined(MSWINCE) \ + || defined(CYGWIN32) || defined(GC_OPENBSD_UTHREADS) \ + || (defined(DARWIN) && !defined(NO_PTHREAD_GET_STACKADDR_NP)) \ + || ((defined(FREEBSD) || defined(LINUX) || defined(NETBSD) \ + || defined(HOST_ANDROID)) && !defined(NO_PTHREAD_GETATTR_NP) \ + && !defined(NO_PTHREAD_ATTR_GET_NP)) \ + || (defined(GC_SOLARIS_THREADS) && !defined(_STRICT_STDC)) \ + || (!defined(STACKBOTTOM) && (defined(HEURISTIC1) \ + || (!defined(LINUX_STACKBOTTOM) && !defined(FREEBSD_STACKBOTTOM))))) + /* GC_INIT() must be called from main thread only. */ + GC_INIT(); +# endif + (void)GC_get_parallel(); /* linking fails if no threads support */ +# ifdef GC_PTHREADS + if ((code = pthread_create (&t, NULL, thread, NULL)) != 0) { + fprintf(stderr, "Thread creation failed %d\n", code); + return 1; + } + if ((code = pthread_join (t, NULL)) != 0) { + fprintf(stderr, "Thread join failed %d\n", code); + return 1; + } +# else + t = CreateThread(NULL, 0, thread, 0, 0, &thread_id); + if (t == NULL) { + fprintf(stderr, "Thread creation failed %d\n", (int)GetLastError()); + return 1; + } + if (WaitForSingleObject(t, INFINITE) != WAIT_OBJECT_0) { + fprintf(stderr, "Thread join failed %d\n", (int)GetLastError()); + CloseHandle(t); + return 1; + } + CloseHandle(t); +# endif + return 0; +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/tests/leak_test.c b/unity-2019.4.24f1-mbe/external/bdwgc/tests/leak_test.c new file mode 100644 index 000000000..532821d0f --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/tests/leak_test.c @@ -0,0 +1,25 @@ +#include "leak_detector.h" + +int main(void) { + int *p[10]; + int i; + GC_set_find_leak(1); /* for new collect versions not compiled */ + /* with -DFIND_LEAK. */ + + GC_INIT(); /* Needed if thread-local allocation is enabled. */ + /* FIXME: This is not ideal. */ + for (i = 0; i < 10; ++i) { + p[i] = (int*)malloc(sizeof(int)+i); + } + CHECK_LEAKS(); + for (i = 1; i < 10; ++i) { + free(p[i]); + } + for (i = 0; i < 9; ++i) { + p[i] = (int*)malloc(sizeof(int)+i); + } + CHECK_LEAKS(); + CHECK_LEAKS(); + CHECK_LEAKS(); + return 0; +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/tests/middle.c b/unity-2019.4.24f1-mbe/external/bdwgc/tests/middle.c new file mode 100644 index 000000000..55686f759 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/tests/middle.c @@ -0,0 +1,25 @@ +/* + * Test at the boundary between small and large objects. + * Inspired by a test case from Zoltan Varga. + */ +#include "gc.h" +#include + +int main (void) +{ + int i; + + GC_set_all_interior_pointers(0); + GC_INIT(); + + for (i = 0; i < 20000; ++i) { + (void)GC_malloc_atomic(4096); + (void)GC_malloc(4096); + } + for (i = 0; i < 20000; ++i) { + (void)GC_malloc_atomic(2048); + (void)GC_malloc(2048); + } + printf("Final heap size is %lu\n", (unsigned long)GC_get_heap_size()); + return 0; +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/tests/realloc_test.c b/unity-2019.4.24f1-mbe/external/bdwgc/tests/realloc_test.c new file mode 100644 index 000000000..c7c80d738 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/tests/realloc_test.c @@ -0,0 +1,34 @@ + +#include +#include +#include "gc.h" + +#define COUNT 10000000 + +int main(void) { + int i; + unsigned long last_heap_size = 0; + + GC_INIT(); + + for (i = 0; i < COUNT; i++) { + int **p = GC_NEW(int *); + int *q = (int*)GC_MALLOC_ATOMIC(sizeof(int)); + + if (p == 0 || *p != 0) { + fprintf(stderr, "GC_malloc returned garbage (or NULL)\n"); + exit(1); + } + + *p = (int*)GC_REALLOC(q, 2 * sizeof(int)); + + if (i % 10 == 0) { + unsigned long heap_size = (unsigned long)GC_get_heap_size(); + if (heap_size != last_heap_size) { + printf("Heap size: %lu\n", heap_size); + last_heap_size = heap_size; + } + } + } + return 0; +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/tests/smash_test.c b/unity-2019.4.24f1-mbe/external/bdwgc/tests/smash_test.c new file mode 100644 index 000000000..d591ca2b3 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/tests/smash_test.c @@ -0,0 +1,28 @@ +/* + * Test that overwrite error detection works reasonably. + */ +#define GC_DEBUG +#include "gc.h" + +#include + +#define COUNT 7000 +#define SIZE 40 + +char * A[COUNT]; + +int main(void) +{ + int i; + char *p; + + GC_INIT(); + + for (i = 0; i < COUNT; ++i) { + A[i] = p = (char*)GC_MALLOC(SIZE); + + if (i%3000 == 0) GC_gcollect(); + if (i%5678 == 0 && p != 0) p[SIZE + i/2000] = 42; + } + return 0; +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/tests/staticrootslib.c b/unity-2019.4.24f1-mbe/external/bdwgc/tests/staticrootslib.c new file mode 100644 index 000000000..f5725c6e0 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/tests/staticrootslib.c @@ -0,0 +1,59 @@ + +/* This test file is intended to be compiled into a DLL. */ + +#ifndef GC_DEBUG +# define GC_DEBUG +#endif + +#include "gc.h" + +#ifndef GC_TEST_EXPORT_API +# if defined(GC_VISIBILITY_HIDDEN_SET) \ + && !defined(__CEGCC__) && !defined(__CYGWIN__) && !defined(__MINGW32__) +# define GC_TEST_EXPORT_API \ + extern __attribute__((__visibility__("default"))) +# else +# define GC_TEST_EXPORT_API extern +# endif +#endif + +struct treenode { + struct treenode *x; + struct treenode *y; +}; + +static struct treenode *root[10] = { 0 }; +static struct treenode *root_nz[10] = { (struct treenode *)(GC_word)2 }; + +#ifdef STATICROOTSLIB2 +# define libsrl_getpelem libsrl_getpelem2 +#else + + GC_TEST_EXPORT_API struct treenode * libsrl_mktree(int i) + { + struct treenode * r = GC_NEW(struct treenode); + if (0 == i) + return 0; + if (1 == i) + r = (struct treenode *)GC_MALLOC_ATOMIC(sizeof(struct treenode)); + if (r) { + r -> x = libsrl_mktree(i-1); + r -> y = libsrl_mktree(i-1); + } + return r; + } + + GC_TEST_EXPORT_API void * libsrl_init(void) + { +# ifndef STATICROOTSLIB_INIT_IN_MAIN + GC_INIT(); +# endif + return GC_MALLOC(sizeof(struct treenode)); + } + +#endif /* !STATICROOTSLIB2 */ + +GC_TEST_EXPORT_API struct treenode ** libsrl_getpelem(int i, int j) +{ + return &((j & 1) != 0 ? root_nz : root)[i]; +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/tests/staticrootstest.c b/unity-2019.4.24f1-mbe/external/bdwgc/tests/staticrootstest.c new file mode 100644 index 000000000..b43765d08 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/tests/staticrootstest.c @@ -0,0 +1,75 @@ + +#include +#include + +#ifndef GC_DEBUG +# define GC_DEBUG +#endif + +#include "gc.h" +#include "gc_backptr.h" + +#ifndef GC_TEST_IMPORT_API +# define GC_TEST_IMPORT_API extern +#endif + +/* Should match that in staticrootslib.c. */ +struct treenode { + struct treenode *x; + struct treenode *y; +}; + +struct treenode *root[10] = { NULL }; + +/* Same as "root" variable but initialized to some non-zero value (to */ +/* be placed to .data section instead of .bss). */ +struct treenode *root_nz[10] = { (struct treenode *)(GC_word)1 }; + +static char *staticroot; /* intentionally static */ + +GC_TEST_IMPORT_API struct treenode * libsrl_mktree(int i); +GC_TEST_IMPORT_API void * libsrl_init(void); +GC_TEST_IMPORT_API struct treenode ** libsrl_getpelem(int i, int j); + +GC_TEST_IMPORT_API struct treenode ** libsrl_getpelem2(int i, int j); + +void init_staticroot(void) +{ + /* Intentionally put staticroot initialization in a function other */ + /* than main to prevent CSA warning that staticroot variable can be */ + /* changed to be a local one). */ + staticroot = (char *)libsrl_init(); +} + +int main(void) +{ + int i, j; + +# ifdef STATICROOTSLIB_INIT_IN_MAIN + GC_INIT(); +# endif + init_staticroot(); + if (NULL == staticroot) { + fprintf(stderr, "GC_malloc returned NULL\n"); + return 2; + } + memset(staticroot, 0x42, sizeof(struct treenode)); + GC_gcollect(); + for (j = 0; j < 4; j++) { + for (i = 0; i < (int)(sizeof(root) / sizeof(root[0])); ++i) { +# ifdef STATICROOTSLIB2 + *libsrl_getpelem2(i, j) = libsrl_mktree(12); +# endif + *libsrl_getpelem(i, j) = libsrl_mktree(12); + ((j & 1) != 0 ? root_nz : root)[i] = libsrl_mktree(12); + GC_gcollect(); + } + for (i = 0; i < (int)sizeof(struct treenode); ++i) { + if (staticroot[i] != 0x42) { + fprintf(stderr, "Memory check failed\n"); + return -1; + } + } + } + return 0; +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/tests/subthread_create.c b/unity-2019.4.24f1-mbe/external/bdwgc/tests/subthread_create.c new file mode 100644 index 000000000..5d9c831d0 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/tests/subthread_create.c @@ -0,0 +1,168 @@ + +#ifdef HAVE_CONFIG_H + /* For PARALLEL_MARK */ +# include "config.h" +#endif + +#ifndef GC_THREADS +# define GC_THREADS +#endif +#include "gc.h" + +#ifdef PARALLEL_MARK +# define AO_REQUIRE_CAS +#endif +#include "private/gc_atomic_ops.h" + +#include + +#ifdef AO_HAVE_fetch_and_add1 + +#ifdef GC_PTHREADS +# include +#else +# include +#endif + +#if defined(__HAIKU__) +# include +#endif + +#include +#include + +#ifndef NTHREADS +# define NTHREADS 31 /* number of initial threads */ +#endif + +#ifndef MAX_SUBTHREAD_DEPTH +# define MAX_ALIVE_THREAD_COUNT 55 +# define MAX_SUBTHREAD_DEPTH 7 +# define MAX_SUBTHREAD_COUNT 200 +#endif + +#ifndef DECAY_NUMER +# define DECAY_NUMER 15 +# define DECAY_DENOM 16 +#endif + +volatile AO_t thread_created_cnt = 0; +volatile AO_t thread_ended_cnt = 0; + +#ifdef GC_PTHREADS + void *entry(void *arg) +#else + DWORD WINAPI entry(LPVOID arg) +#endif +{ + int thread_num = AO_fetch_and_add1(&thread_created_cnt); + GC_word my_depth = (GC_word)arg + 1; + + if (my_depth <= MAX_SUBTHREAD_DEPTH + && thread_num < MAX_SUBTHREAD_COUNT + && (thread_num % DECAY_DENOM) < DECAY_NUMER + && thread_num - (int)AO_load(&thread_ended_cnt) + <= MAX_ALIVE_THREAD_COUNT) { +# ifdef GC_PTHREADS + int err; + pthread_t th; + + err = pthread_create(&th, NULL, entry, (void *)my_depth); + if (err != 0) { + fprintf(stderr, "Thread #%d creation failed: %s\n", thread_num, + strerror(err)); + exit(2); + } + err = pthread_detach(th); + if (err != 0) { + fprintf(stderr, "Thread #%d detach failed: %s\n", thread_num, + strerror(err)); + exit(2); + } +# else + HANDLE th; + DWORD thread_id; + + th = CreateThread(NULL, 0, entry, (LPVOID)my_depth, 0, &thread_id); + if (th == NULL) { + fprintf(stderr, "Thread #%d creation failed: %d\n", thread_num, + (int)GetLastError()); + exit(2); + } + CloseHandle(th); +# endif + } + + (void)AO_fetch_and_add1(&thread_ended_cnt); + return 0; +} + +int main(void) +{ +#if NTHREADS > 0 + int i; +# ifdef GC_PTHREADS + int err; + pthread_t th[NTHREADS]; +# else + HANDLE th[NTHREADS]; +# endif + + GC_INIT(); + for (i = 0; i < NTHREADS; ++i) { +# ifdef GC_PTHREADS + err = pthread_create(&th[i], NULL, entry, 0); + if (err) { + fprintf(stderr, "Thread creation failed: %s\n", strerror(err)); + exit(1); + } +# else + DWORD thread_id; + th[i] = CreateThread(NULL, 0, entry, 0, 0, &thread_id); + if (th[i] == NULL) { + fprintf(stderr, "Thread creation failed: %d\n", + (int)GetLastError()); + exit(1); + } +# endif + } + + for (i = 0; i < NTHREADS; ++i) { +# ifdef GC_PTHREADS + void *res; + err = pthread_join(th[i], &res); + if (err) { + fprintf(stderr, "Failed to join thread: %s\n", strerror(err)); +# if defined(__HAIKU__) + /* The error is just ignored (and the test is ended) to */ + /* workaround some bug in Haiku pthread_join. */ + /* TODO: The thread is not deleted from GC_threads. */ + if (ESRCH == err) break; +# endif + exit(1); + } +# else + if (WaitForSingleObject(th[i], INFINITE) != WAIT_OBJECT_0) { + fprintf(stderr, "Failed to join thread: %d\n", + (int)GetLastError()); + CloseHandle(th[i]); + exit(1); + } + CloseHandle(th[i]); +# endif + } +#endif + printf("subthread_create: created %d threads (%d ended)\n", + (int)AO_load(&thread_created_cnt), (int)AO_load(&thread_ended_cnt)); + return 0; +} + +#else + +int main(void) +{ + printf("subthread_create test skipped\n"); + return 0; +} + +#endif /* !AO_HAVE_fetch_and_add1 */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/tests/test.c b/unity-2019.4.24f1-mbe/external/bdwgc/tests/test.c new file mode 100644 index 000000000..4700a8c70 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/tests/test.c @@ -0,0 +1,2345 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* An incomplete test for the garbage collector. */ +/* Some more obscure entry points are not tested at all. */ +/* This must be compiled with the same flags used to build the */ +/* GC. It uses GC internals to allow more precise results */ +/* checking for some of the tests. */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# undef GC_BUILD + +#if (defined(DBG_HDRS_ALL) || defined(MAKE_BACK_GRAPH)) \ + && !defined(GC_DEBUG) && !defined(CPPCHECK) +# define GC_DEBUG +#endif + +#ifdef DEFAULT_VDB /* specified manually (e.g. passed to CFLAGS) */ +# define TEST_DEFAULT_VDB +#endif + +#if defined(CPPCHECK) && defined(GC_PTHREADS) && !defined(_GNU_SOURCE) +# define _GNU_SOURCE 1 +#endif +#undef GC_NO_THREAD_REDIRECTS +#include "gc.h" + +#ifndef NTHREADS /* Number of additional threads to fork. */ +# define NTHREADS 5 /* excludes main thread, which also runs a test. */ + /* Not respected by PCR test. */ +#endif + +# if defined(mips) && defined(SYSTYPE_BSD43) + /* MIPS RISCOS 4 */ +# else +# include +# endif +# include +# if defined(_WIN32_WCE) && !defined(__GNUC__) +# include +/* # define assert ASSERT */ +# else +# include /* Not normally used, but handy for debugging. */ +# endif + +# include "gc_typed.h" +# include "private/gc_priv.h" /* For output, locking, MIN_WORDS, */ + /* some statistics and gcconfig.h. */ + +# if defined(MSWIN32) || defined(MSWINCE) +# include +# endif + +#ifdef GC_PRINT_VERBOSE_STATS +# define print_stats VERBOSE +# define INIT_PRINT_STATS /* empty */ +#else + /* Use own variable as GC_print_stats might not be exported. */ + static int print_stats = 0; +# ifdef GC_READ_ENV_FILE + /* GETENV uses GC internal function in this case. */ +# define INIT_PRINT_STATS /* empty */ +# else +# define INIT_PRINT_STATS \ + { \ + if (0 != GETENV("GC_PRINT_VERBOSE_STATS")) \ + print_stats = VERBOSE; \ + else if (0 != GETENV("GC_PRINT_STATS")) \ + print_stats = 1; \ + } +# endif +#endif /* !GC_PRINT_VERBOSE_STATS */ + +# ifdef PCR +# include "th/PCR_ThCrSec.h" +# include "th/PCR_Th.h" +# define GC_printf printf +# endif + +# if defined(GC_PTHREADS) && !defined(GC_WIN32_PTHREADS) +# include +# else +# define NO_TEST_HANDLE_FORK +# endif + +# if (!defined(THREADS) || !defined(HANDLE_FORK) \ + || (defined(DARWIN) && defined(MPROTECT_VDB) \ + && !defined(NO_INCREMENTAL) && !defined(MAKE_BACK_GRAPH))) \ + && !defined(NO_TEST_HANDLE_FORK) && !defined(TEST_HANDLE_FORK) \ + && !defined(TEST_FORK_WITHOUT_ATFORK) +# define NO_TEST_HANDLE_FORK +# endif + +# ifndef NO_TEST_HANDLE_FORK +# include +# include +# include +# if defined(HANDLE_FORK) && defined(CAN_CALL_ATFORK) +# define INIT_FORK_SUPPORT GC_set_handle_fork(1) + /* Causes abort in GC_init on pthread_atfork failure. */ +# elif !defined(TEST_FORK_WITHOUT_ATFORK) +# define INIT_FORK_SUPPORT GC_set_handle_fork(-1) + /* Passing -1 implies fork() should be as well manually */ + /* surrounded with GC_atfork_prepare/parent/child. */ +# endif +# endif + +# ifndef INIT_FORK_SUPPORT +# define INIT_FORK_SUPPORT /* empty */ +# endif + +#ifdef PCR +# define FINALIZER_LOCK() PCR_ThCrSec_EnterSys() +# define FINALIZER_UNLOCK() PCR_ThCrSec_ExitSys() +#elif defined(GC_PTHREADS) + static pthread_mutex_t incr_lock = PTHREAD_MUTEX_INITIALIZER; +# define FINALIZER_LOCK() pthread_mutex_lock(&incr_lock) +# define FINALIZER_UNLOCK() pthread_mutex_unlock(&incr_lock) +#elif defined(GC_WIN32_THREADS) + static CRITICAL_SECTION incr_cs; +# define FINALIZER_LOCK() EnterCriticalSection(&incr_cs) +# define FINALIZER_UNLOCK() LeaveCriticalSection(&incr_cs) +#else +# define FINALIZER_LOCK() (void)0 +# define FINALIZER_UNLOCK() (void)0 +#endif /* !THREADS */ + +#include + +#define CHECK_GCLIB_VERSION \ + if (GC_get_version() != ((GC_VERSION_MAJOR<<16) \ + | (GC_VERSION_MINOR<<8) \ + | GC_VERSION_MICRO)) { \ + GC_printf("libgc version mismatch\n"); \ + exit(1); \ + } + +/* Call GC_INIT only on platforms on which we think we really need it, */ +/* so that we can test automatic initialization on the rest. */ +#if defined(TEST_EXPLICIT_GC_INIT) || defined(AIX) || defined(CYGWIN32) \ + || defined(DARWIN) || defined(HOST_ANDROID) \ + || (defined(MSWINCE) && !defined(GC_WINMAIN_REDIRECT)) +# define GC_OPT_INIT GC_INIT() +#else +# define GC_OPT_INIT /* empty */ +#endif + +#ifdef NO_CLOCK +# define INIT_PERF_MEASUREMENT (void)0 +#else +# define INIT_PERF_MEASUREMENT GC_start_performance_measurement() +#endif + +#define GC_COND_INIT() \ + INIT_FORK_SUPPORT; GC_OPT_INIT; CHECK_GCLIB_VERSION; \ + INIT_PRINT_STATS; INIT_PERF_MEASUREMENT + +#define CHECK_OUT_OF_MEMORY(p) \ + if ((p) == NULL) { \ + GC_printf("Out of memory\n"); \ + exit(1); \ + } + +/* Define AO primitives for a single-threaded mode. */ +#ifndef AO_CLEAR + /* AO_t not defined. */ +# define AO_t GC_word +#endif +#ifndef AO_HAVE_load_acquire + static AO_t AO_load_acquire(const volatile AO_t *addr) + { + AO_t result; + + FINALIZER_LOCK(); + result = *addr; + FINALIZER_UNLOCK(); + return result; + } +#endif +#ifndef AO_HAVE_store_release + /* Not a macro as new_val argument should be evaluated before the lock. */ + static void AO_store_release(volatile AO_t *addr, AO_t new_val) + { + FINALIZER_LOCK(); + *addr = new_val; + FINALIZER_UNLOCK(); + } +#endif +#ifndef AO_HAVE_fetch_and_add1 +# define AO_fetch_and_add1(p) ((*(p))++) + /* This is used only to update counters. */ +#endif + +/* Allocation Statistics. Synchronization is not strictly necessary. */ +volatile AO_t uncollectable_count = 0; +volatile AO_t collectable_count = 0; +volatile AO_t atomic_count = 0; +volatile AO_t realloc_count = 0; + +volatile AO_t extra_count = 0; /* Amount of space wasted in cons node; */ + /* also used in gcj_cons, mktree and */ + /* chktree (for other purposes). */ + +#if defined(GC_AMIGA_FASTALLOC) && defined(AMIGA) + EXTERN_C_BEGIN + void GC_amiga_free_all_mem(void); + EXTERN_C_END + + void Amiga_Fail(void){GC_amiga_free_all_mem();abort();} +# define FAIL Amiga_Fail() + void *GC_amiga_gctest_malloc_explicitly_typed(size_t lb, GC_descr d){ + void *ret=GC_malloc_explicitly_typed(lb,d); + if(ret==NULL){ + GC_gcollect(); + ret=GC_malloc_explicitly_typed(lb,d); + if(ret==NULL){ + GC_printf("Out of memory, (typed allocations are not directly " + "supported with the GC_AMIGA_FASTALLOC option.)\n"); + FAIL; + } + } + return ret; + } + void *GC_amiga_gctest_calloc_explicitly_typed(size_t a,size_t lb, GC_descr d){ + void *ret=GC_calloc_explicitly_typed(a,lb,d); + if(ret==NULL){ + GC_gcollect(); + ret=GC_calloc_explicitly_typed(a,lb,d); + if(ret==NULL){ + GC_printf("Out of memory, (typed allocations are not directly " + "supported with the GC_AMIGA_FASTALLOC option.)\n"); + FAIL; + } + } + return ret; + } +# define GC_malloc_explicitly_typed(a,b) GC_amiga_gctest_malloc_explicitly_typed(a,b) +# define GC_calloc_explicitly_typed(a,b,c) GC_amiga_gctest_calloc_explicitly_typed(a,b,c) + +#else /* !AMIGA_FASTALLOC */ + +# if defined(PCR) || defined(LINT2) +# define FAIL abort() +# else +# define FAIL ABORT("Test failed") +# endif + +#endif /* !AMIGA_FASTALLOC */ + +/* AT_END may be defined to exercise the interior pointer test */ +/* if the collector is configured with ALL_INTERIOR_POINTERS. */ +/* As it stands, this test should succeed with either */ +/* configuration. In the FIND_LEAK configuration, it should */ +/* find lots of leaks, since we free almost nothing. */ + +struct SEXPR { + struct SEXPR * sexpr_car; + struct SEXPR * sexpr_cdr; +}; + + +typedef struct SEXPR * sexpr; + +# define INT_TO_SEXPR(x) ((sexpr)(GC_word)(x)) +# define SEXPR_TO_INT(x) ((int)(GC_word)(x)) + +# undef nil +# define nil (INT_TO_SEXPR(0)) +# define car(x) ((x) -> sexpr_car) +# define cdr(x) ((x) -> sexpr_cdr) +# define is_nil(x) ((x) == nil) + +/* Silly implementation of Lisp cons. Intentionally wastes lots of space */ +/* to test collector. */ +# ifdef VERY_SMALL_CONFIG +# define cons small_cons +# else +sexpr cons (sexpr x, sexpr y) +{ + sexpr r; + int *p; + unsigned my_extra = (unsigned)AO_fetch_and_add1(&extra_count) % 5000; + + r = (sexpr)GC_MALLOC(sizeof(struct SEXPR) + my_extra); + CHECK_OUT_OF_MEMORY(r); + AO_fetch_and_add1(&collectable_count); + for (p = (int *)r; + (word)p < (word)r + my_extra + sizeof(struct SEXPR); p++) { + if (*p) { + GC_printf("Found nonzero at %p - allocator is broken\n", + (void *)p); + FAIL; + } + *p = (int)((13 << 12) + ((p - (int *)r) & 0xfff)); + } +# ifdef AT_END + r = (sexpr)((char *)r + (my_extra & ~7)); +# endif + r -> sexpr_car = x; + r -> sexpr_cdr = y; + GC_END_STUBBORN_CHANGE(r); + return(r); +} +# endif + +#include "gc_mark.h" + +#ifdef GC_GCJ_SUPPORT + +#include "gc_gcj.h" + +/* The following struct emulates the vtable in gcj. */ +/* This assumes the default value of MARK_DESCR_OFFSET. */ +struct fake_vtable { + void * dummy; /* class pointer in real gcj. */ + GC_word descr; +}; + +struct fake_vtable gcj_class_struct1 = { 0, sizeof(struct SEXPR) + + sizeof(struct fake_vtable *) }; + /* length based descriptor. */ +struct fake_vtable gcj_class_struct2 = + { 0, ((GC_word)3 << (CPP_WORDSZ - 3)) | GC_DS_BITMAP}; + /* Bitmap based descriptor. */ + +struct GC_ms_entry * fake_gcj_mark_proc(word * addr, + struct GC_ms_entry *mark_stack_ptr, + struct GC_ms_entry *mark_stack_limit, + word env ) +{ + sexpr x; + if (1 == env) { + /* Object allocated with debug allocator. */ + addr = (word *)GC_USR_PTR_FROM_BASE(addr); + } + x = (sexpr)(addr + 1); /* Skip the vtable pointer. */ + mark_stack_ptr = GC_MARK_AND_PUSH( + (void *)(x -> sexpr_cdr), mark_stack_ptr, + mark_stack_limit, (void * *)&(x -> sexpr_cdr)); + mark_stack_ptr = GC_MARK_AND_PUSH( + (void *)(x -> sexpr_car), mark_stack_ptr, + mark_stack_limit, (void * *)&(x -> sexpr_car)); + return(mark_stack_ptr); +} + +#endif /* GC_GCJ_SUPPORT */ + + +sexpr small_cons (sexpr x, sexpr y) +{ + sexpr r = GC_NEW(struct SEXPR); + + CHECK_OUT_OF_MEMORY(r); + AO_fetch_and_add1(&collectable_count); + r -> sexpr_car = x; + r -> sexpr_cdr = y; + GC_END_STUBBORN_CHANGE(r); + return(r); +} + +sexpr small_cons_uncollectable (sexpr x, sexpr y) +{ + sexpr r = (sexpr)GC_MALLOC_UNCOLLECTABLE(sizeof(struct SEXPR)); + + CHECK_OUT_OF_MEMORY(r); + AO_fetch_and_add1(&uncollectable_count); + r -> sexpr_car = x; + r -> sexpr_cdr = (sexpr)(~(GC_word)y); + GC_END_STUBBORN_CHANGE(r); + return(r); +} + +#ifdef GC_GCJ_SUPPORT + sexpr gcj_cons(sexpr x, sexpr y) + { + sexpr result; + GC_word * r = (GC_word *)GC_GCJ_MALLOC( + sizeof(struct SEXPR) + sizeof(struct fake_vtable*), + (AO_fetch_and_add1(&extra_count) & 1) != 0 + ? &gcj_class_struct1 + : &gcj_class_struct2); + + CHECK_OUT_OF_MEMORY(r); + result = (sexpr)(r + 1); + result -> sexpr_car = x; + result -> sexpr_cdr = y; + GC_END_STUBBORN_CHANGE(r); + return(result); + } +#endif /* GC_GCJ_SUPPORT */ + +/* Return reverse(x) concatenated with y */ +sexpr reverse1(sexpr x, sexpr y) +{ + if (is_nil(x)) { + return(y); + } else { + return( reverse1(cdr(x), cons(car(x), y)) ); + } +} + +sexpr reverse(sexpr x) +{ +# ifdef TEST_WITH_SYSTEM_MALLOC + GC_noop1(GC_HIDE_POINTER(malloc(100000))); +# endif + return( reverse1(x, nil) ); +} + +sexpr ints(int low, int up) +{ + if (low > up) { + return(nil); + } else { + return(small_cons(small_cons(INT_TO_SEXPR(low), nil), ints(low+1, up))); + } +} + +#ifdef GC_GCJ_SUPPORT +/* Return reverse(x) concatenated with y */ +sexpr gcj_reverse1(sexpr x, sexpr y) +{ + if (is_nil(x)) { + return(y); + } else { + return( gcj_reverse1(cdr(x), gcj_cons(car(x), y)) ); + } +} + +sexpr gcj_reverse(sexpr x) +{ + return( gcj_reverse1(x, nil) ); +} + +sexpr gcj_ints(int low, int up) +{ + if (low > up) { + return(nil); + } else { + return(gcj_cons(gcj_cons(INT_TO_SEXPR(low), nil), gcj_ints(low+1, up))); + } +} +#endif /* GC_GCJ_SUPPORT */ + +/* To check uncollectible allocation we build lists with disguised cdr */ +/* pointers, and make sure they don't go away. */ +sexpr uncollectable_ints(int low, int up) +{ + if (low > up) { + return(nil); + } else { + return(small_cons_uncollectable(small_cons(INT_TO_SEXPR(low), nil), + uncollectable_ints(low+1, up))); + } +} + +void check_ints(sexpr list, int low, int up) +{ + if (is_nil(list)) { + GC_printf("list is nil\n"); + FAIL; + } + if (SEXPR_TO_INT(car(car(list))) != low) { + GC_printf( + "List reversal produced incorrect list - collector is broken\n"); + FAIL; + } + if (low == up) { + if (cdr(list) != nil) { + GC_printf("List too long - collector is broken\n"); + FAIL; + } + } else { + check_ints(cdr(list), low+1, up); + } +} + +# define UNCOLLECTABLE_CDR(x) (sexpr)(~(GC_word)(cdr(x))) + +void check_uncollectable_ints(sexpr list, int low, int up) +{ + if (SEXPR_TO_INT(car(car(list))) != low) { + GC_printf("Uncollectable list corrupted - collector is broken\n"); + FAIL; + } + if (low == up) { + if (UNCOLLECTABLE_CDR(list) != nil) { + GC_printf("Uncollectable list too long - collector is broken\n"); + FAIL; + } + } else { + check_uncollectable_ints(UNCOLLECTABLE_CDR(list), low+1, up); + } +} + +/* Not used, but useful for debugging: */ +void print_int_list(sexpr x) +{ + if (is_nil(x)) { + GC_printf("NIL\n"); + } else { + GC_printf("(%d)", SEXPR_TO_INT(car(car(x)))); + if (!is_nil(cdr(x))) { + GC_printf(", "); + print_int_list(cdr(x)); + } else { + GC_printf("\n"); + } + } +} + +/* ditto: */ +void check_marks_int_list(sexpr x) +{ + if (!GC_is_marked(x)) + GC_printf("[unm:%p]", (void *)x); + else + GC_printf("[mkd:%p]", (void *)x); + if (is_nil(x)) { + GC_printf("NIL\n"); + } else { + if (!GC_is_marked(car(x))) + GC_printf("[unm car:%p]", (void *)car(x)); + GC_printf("(%d)", SEXPR_TO_INT(car(car(x)))); + if (!is_nil(cdr(x))) { + GC_printf(", "); + check_marks_int_list(cdr(x)); + } else { + GC_printf("\n"); + } + } +} + +/* + * A tiny list reversal test to check thread creation. + */ +#ifdef THREADS + +# ifdef VERY_SMALL_CONFIG +# define TINY_REVERSE_UPPER_VALUE 4 +# else +# define TINY_REVERSE_UPPER_VALUE 10 +# endif + +# if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) + DWORD __stdcall tiny_reverse_test(void * arg GC_ATTR_UNUSED) +# else + void * tiny_reverse_test(void * arg GC_ATTR_UNUSED) +# endif +{ + int i; + for (i = 0; i < 5; ++i) { + check_ints(reverse(reverse(ints(1, TINY_REVERSE_UPPER_VALUE))), + 1, TINY_REVERSE_UPPER_VALUE); + } +# if defined(GC_ENABLE_SUSPEND_THREAD) + /* Force collection from a thread. */ + GC_gcollect(); +# endif + return 0; +} + +# if defined(GC_PTHREADS) +# if defined(GC_ENABLE_SUSPEND_THREAD) +# include "javaxfc.h" +# endif + + void fork_a_thread(void) + { + pthread_t t; + int code; + + code = pthread_create(&t, NULL, tiny_reverse_test, 0); + if (code != 0) { + GC_printf("Small thread creation failed %d\n", code); + FAIL; + } +# if defined(GC_ENABLE_SUSPEND_THREAD) && !defined(GC_DARWIN_THREADS) \ + && !defined(GC_OPENBSD_UTHREADS) && !defined(GC_WIN32_THREADS) \ + && !defined(NACL) && !defined(GC_OSF1_THREADS) + if (GC_is_thread_suspended(t)) { + GC_printf("Running thread should be not suspended\n"); + FAIL; + } + /* Thread could be running or already terminated (but not joined). */ + GC_suspend_thread(t); + if (!GC_is_thread_suspended(t)) { + GC_printf("Thread expected to be suspended\n"); + FAIL; + } + GC_suspend_thread(t); /* should be no-op */ + GC_resume_thread(t); + if (GC_is_thread_suspended(t)) { + GC_printf("Resumed thread should be not suspended\n"); + FAIL; + } + GC_resume_thread(t); /* should be no-op */ +# endif + if ((code = pthread_join(t, 0)) != 0) { + GC_printf("Small thread join failed %d\n", code); + FAIL; + } + } + +# elif defined(GC_WIN32_THREADS) + void fork_a_thread(void) + { + DWORD thread_id; + HANDLE h; + h = GC_CreateThread((SECURITY_ATTRIBUTES *)NULL, (word)0, + tiny_reverse_test, NULL, (DWORD)0, &thread_id); + /* Explicitly specify types of the */ + /* arguments to test the prototype. */ + if (h == (HANDLE)NULL) { + GC_printf("Small thread creation failed %d\n", + (int)GetLastError()); + FAIL; + } + if (WaitForSingleObject(h, INFINITE) != WAIT_OBJECT_0) { + GC_printf("Small thread wait failed %d\n", + (int)GetLastError()); + FAIL; + } + } + +# endif + +#endif + +void test_generic_malloc_or_special(void *p) { + size_t size; + int kind = GC_get_kind_and_size(p, &size); + void *p2; + + if (size != GC_size(p)) { + GC_printf("GC_get_kind_and_size returned size not matching GC_size\n"); + FAIL; + } + p2 = GC_GENERIC_OR_SPECIAL_MALLOC(10, kind); + CHECK_OUT_OF_MEMORY(p2); + if (GC_get_kind_and_size(p2, NULL) != kind) { + GC_printf("GC_generic_or_special_malloc:" + " unexpected kind of returned object\n"); + FAIL; + } + GC_FREE(p2); +} + +/* Try to force a to be strangely aligned */ +volatile struct A_s { + char dummy; + AO_t aa; +} A; +#define a_set(p) AO_store_release(&A.aa, (AO_t)(p)) +#define a_get() (sexpr)AO_load_acquire(&A.aa) + +/* + * Repeatedly reverse lists built out of very different sized cons cells. + * Check that we didn't lose anything. + */ +void *GC_CALLBACK reverse_test_inner(void *data) +{ + int i; + sexpr b; + sexpr c; + sexpr d; + sexpr e; + sexpr *f, *g, *h; + + if (data == 0) { + /* This stack frame is not guaranteed to be scanned. */ + return GC_call_with_gc_active(reverse_test_inner, (void*)(word)1); + } + +# if defined(MACOS) \ + || (defined(UNIX_LIKE) && defined(NO_GETCONTEXT)) /* e.g. musl */ + /* Assume 128K stacks at least. */ +# define BIG 1000 +# elif defined(PCR) + /* PCR default stack is 100K. Stack frames are up to 120 bytes. */ +# define BIG 700 +# elif defined(MSWINCE) || defined(RTEMS) + /* WinCE only allows 64K stacks */ +# define BIG 500 +# elif defined(OSF1) + /* OSF has limited stack space by default, and large frames. */ +# define BIG 200 +# elif defined(__MACH__) && defined(__ppc64__) +# define BIG 2500 +# else +# define BIG 4500 +# endif + + a_set(ints(1, 49)); + b = ints(1, 50); + c = ints(1, BIG); + d = uncollectable_ints(1, 100); + test_generic_malloc_or_special(d); + e = uncollectable_ints(1, 1); + /* Check that realloc updates object descriptors correctly */ + AO_fetch_and_add1(&collectable_count); + f = (sexpr *)GC_MALLOC(4 * sizeof(sexpr)); + f = (sexpr *)GC_REALLOC((void *)f, 6 * sizeof(sexpr)); + CHECK_OUT_OF_MEMORY(f); + AO_fetch_and_add1(&realloc_count); + f[5] = ints(1,17); + AO_fetch_and_add1(&collectable_count); + g = (sexpr *)GC_MALLOC(513 * sizeof(sexpr)); + test_generic_malloc_or_special(g); + g = (sexpr *)GC_REALLOC((void *)g, 800 * sizeof(sexpr)); + CHECK_OUT_OF_MEMORY(g); + AO_fetch_and_add1(&realloc_count); + g[799] = ints(1,18); + AO_fetch_and_add1(&collectable_count); + h = (sexpr *)GC_MALLOC(1025 * sizeof(sexpr)); + h = (sexpr *)GC_REALLOC((void *)h, 2000 * sizeof(sexpr)); + CHECK_OUT_OF_MEMORY(h); + AO_fetch_and_add1(&realloc_count); +# ifdef GC_GCJ_SUPPORT + h[1999] = gcj_ints(1,200); + for (i = 0; i < 51; ++i) + h[1999] = gcj_reverse(h[1999]); + /* Leave it as the reversed list for now. */ +# else + h[1999] = ints(1,200); +# endif + /* Try to force some collections and reuse of small list elements */ + for (i = 0; i < 10; i++) { + (void)ints(1, BIG); + } + /* Superficially test interior pointer recognition on stack */ + c = (sexpr)((char *)c + sizeof(char *)); + d = (sexpr)((char *)d + sizeof(char *)); + + GC_FREE((void *)e); + + check_ints(b,1,50); + check_ints(a_get(),1,49); + for (i = 0; i < 50; i++) { + check_ints(b,1,50); + b = reverse(reverse(b)); + } + check_ints(b,1,50); + check_ints(a_get(),1,49); + for (i = 0; i < 60; i++) { +# if (defined(GC_PTHREADS) || defined(GC_WIN32_THREADS)) \ + && (NTHREADS > 0) + if (i % 10 == 0) fork_a_thread(); +# endif + /* This maintains the invariant that a always points to a list */ + /* of 49 integers. Thus, this is thread safe without locks, */ + /* assuming acquire/release barriers in a_get/set() and atomic */ + /* pointer assignments (otherwise, e.g., check_ints() may see */ + /* an uninitialized object returned by GC_MALLOC). */ + a_set(reverse(reverse(a_get()))); +# if !defined(AT_END) && !defined(THREADS) + /* This is not thread safe, since realloc explicitly deallocates */ + a_set(GC_REALLOC(a_get(), (i & 1) != 0 ? 500 : 8200)); + AO_fetch_and_add1(&realloc_count); +# endif + } + check_ints(a_get(),1,49); + check_ints(b,1,50); + + /* Restore c and d values. */ + c = (sexpr)((char *)c - sizeof(char *)); + d = (sexpr)((char *)d - sizeof(char *)); + + check_ints(c,1,BIG); + check_uncollectable_ints(d, 1, 100); + check_ints(f[5], 1,17); + check_ints(g[799], 1,18); +# ifdef GC_GCJ_SUPPORT + h[1999] = gcj_reverse(h[1999]); +# endif + check_ints(h[1999], 1,200); +# ifndef THREADS + a_set(NULL); +# endif + *(sexpr volatile *)&b = 0; + *(sexpr volatile *)&c = 0; + return 0; +} + +void reverse_test(void) +{ + /* Test GC_do_blocking/GC_call_with_gc_active. */ + (void)GC_do_blocking(reverse_test_inner, 0); +} + +/* + * The rest of this builds balanced binary trees, checks that they don't + * disappear, and tests finalization. + */ +typedef struct treenode { + int level; + struct treenode * lchild; + struct treenode * rchild; +} tn; + +int finalizable_count = 0; +int finalized_count = 0; +int dropped_something = 0; + +void GC_CALLBACK finalizer(void * obj, void * client_data) +{ + tn * t = (tn *)obj; + + FINALIZER_LOCK(); + if ((int)(GC_word)client_data != t -> level) { + GC_printf("Wrong finalization data - collector is broken\n"); + FAIL; + } + finalized_count++; + t -> level = -1; /* detect duplicate finalization immediately */ + FINALIZER_UNLOCK(); +} + +# define MAX_FINALIZED ((NTHREADS+1)*4000) + +# if !defined(MACOS) + GC_FAR GC_word live_indicators[MAX_FINALIZED] = {0}; +# ifndef GC_LONG_REFS_NOT_NEEDED + GC_FAR void *live_long_refs[MAX_FINALIZED] = { NULL }; +# endif +#else + /* Too big for THINK_C. have to allocate it dynamically. */ + GC_word *live_indicators = 0; +# ifndef GC_LONG_REFS_NOT_NEEDED +# define GC_LONG_REFS_NOT_NEEDED +# endif +#endif + +int live_indicators_count = 0; + +tn * mktree(int n) +{ + tn * result = GC_NEW(tn); + + AO_fetch_and_add1(&collectable_count); +# if defined(MACOS) + /* get around static data limitations. */ + if (!live_indicators) { + live_indicators = + (GC_word*)NewPtrClear(MAX_FINALIZED * sizeof(GC_word)); + CHECK_OUT_OF_MEMORY(live_indicators); + } +# endif + if (n == 0) return(0); + CHECK_OUT_OF_MEMORY(result); + result -> level = n; + result -> lchild = mktree(n-1); + result -> rchild = mktree(n-1); + if (AO_fetch_and_add1(&extra_count) % 17 == 0 && n >= 2) { + tn * tmp; + tn * left = result -> lchild; + tn * right = result -> rchild; + + CHECK_OUT_OF_MEMORY(left); + tmp = left -> rchild; + CHECK_OUT_OF_MEMORY(right); + left -> rchild = right -> lchild; + right -> lchild = tmp; + GC_END_STUBBORN_CHANGE(left); + GC_END_STUBBORN_CHANGE(right); + } + if (AO_fetch_and_add1(&extra_count) % 119 == 0) { +# ifndef GC_NO_FINALIZATION + int my_index; + void *new_link; +# endif + + { + FINALIZER_LOCK(); + /* Losing a count here causes erroneous report of failure. */ + finalizable_count++; +# ifndef GC_NO_FINALIZATION + my_index = live_indicators_count++; +# endif + FINALIZER_UNLOCK(); + } + +# ifndef GC_NO_FINALIZATION + GC_REGISTER_FINALIZER((void *)result, finalizer, (void *)(GC_word)n, + (GC_finalization_proc *)0, (void * *)0); + if (my_index >= MAX_FINALIZED) { + GC_printf("live_indicators overflowed\n"); + FAIL; + } + live_indicators[my_index] = 13; + if (GC_GENERAL_REGISTER_DISAPPEARING_LINK( + (void * *)(&(live_indicators[my_index])), result) != 0) { + GC_printf("GC_general_register_disappearing_link failed\n"); + FAIL; + } + if (GC_move_disappearing_link((void **)(&(live_indicators[my_index])), + (void **)(&(live_indicators[my_index]))) != GC_SUCCESS) { + GC_printf("GC_move_disappearing_link(link,link) failed\n"); + FAIL; + } + new_link = (void *)live_indicators[my_index]; + if (GC_move_disappearing_link((void **)(&(live_indicators[my_index])), + &new_link) != GC_SUCCESS) { + GC_printf("GC_move_disappearing_link(new_link) failed\n"); + FAIL; + } + if (GC_unregister_disappearing_link(&new_link) == 0) { + GC_printf("GC_unregister_disappearing_link failed\n"); + FAIL; + } + if (GC_move_disappearing_link((void **)(&(live_indicators[my_index])), + &new_link) != GC_NOT_FOUND) { + GC_printf("GC_move_disappearing_link(new_link) failed 2\n"); + FAIL; + } + if (GC_GENERAL_REGISTER_DISAPPEARING_LINK( + (void * *)(&(live_indicators[my_index])), result) != 0) { + GC_printf("GC_general_register_disappearing_link failed 2\n"); + FAIL; + } +# ifndef GC_LONG_REFS_NOT_NEEDED + if (GC_REGISTER_LONG_LINK(&live_long_refs[my_index], result) != 0) { + GC_printf("GC_register_long_link failed\n"); + FAIL; + } + if (GC_move_long_link(&live_long_refs[my_index], + &live_long_refs[my_index]) != GC_SUCCESS) { + GC_printf("GC_move_long_link(link,link) failed\n"); + FAIL; + } + new_link = live_long_refs[my_index]; + if (GC_move_long_link(&live_long_refs[my_index], + &new_link) != GC_SUCCESS) { + GC_printf("GC_move_long_link(new_link) failed\n"); + FAIL; + } + if (GC_unregister_long_link(&new_link) == 0) { + GC_printf("GC_unregister_long_link failed\n"); + FAIL; + } + if (GC_move_long_link(&live_long_refs[my_index], + &new_link) != GC_NOT_FOUND) { + GC_printf("GC_move_long_link(new_link) failed 2\n"); + FAIL; + } + if (GC_REGISTER_LONG_LINK(&live_long_refs[my_index], result) != 0) { + GC_printf("GC_register_long_link failed 2\n"); + FAIL; + } +# endif +# endif + GC_reachable_here(result); + } + GC_END_STUBBORN_CHANGE(result); + return(result); +} + +void chktree(tn *t, int n) +{ + if (0 == n) { + if (NULL == t) /* is a leaf? */ + return; + GC_printf("Clobbered a leaf - collector is broken\n"); + FAIL; + } + if (t -> level != n) { + GC_printf("Lost a node at level %d - collector is broken\n", n); + FAIL; + } + if (AO_fetch_and_add1(&extra_count) % 373 == 0) { + (void)GC_MALLOC((unsigned)AO_fetch_and_add1(&extra_count) % 5001); + AO_fetch_and_add1(&collectable_count); + } + chktree(t -> lchild, n-1); + if (AO_fetch_and_add1(&extra_count) % 73 == 0) { + (void)GC_MALLOC((unsigned)AO_fetch_and_add1(&extra_count) % 373); + AO_fetch_and_add1(&collectable_count); + } + chktree(t -> rchild, n-1); +} + + +#if defined(GC_PTHREADS) +pthread_key_t fl_key; + +void * alloc8bytes(void) +{ +# if defined(SMALL_CONFIG) || defined(GC_DEBUG) + AO_fetch_and_add1(&collectable_count); + return(GC_MALLOC(8)); +# else + void ** my_free_list_ptr; + void * my_free_list; + + my_free_list_ptr = (void **)pthread_getspecific(fl_key); + if (my_free_list_ptr == 0) { + my_free_list_ptr = GC_NEW_UNCOLLECTABLE(void *); + CHECK_OUT_OF_MEMORY(my_free_list_ptr); + AO_fetch_and_add1(&uncollectable_count); + if (pthread_setspecific(fl_key, my_free_list_ptr) != 0) { + GC_printf("pthread_setspecific failed\n"); + FAIL; + } + } + my_free_list = *my_free_list_ptr; + if (my_free_list == 0) { + my_free_list = GC_malloc_many(8); + CHECK_OUT_OF_MEMORY(my_free_list); + } + *my_free_list_ptr = GC_NEXT(my_free_list); + GC_NEXT(my_free_list) = 0; + GC_END_STUBBORN_CHANGE(my_free_list_ptr); + AO_fetch_and_add1(&collectable_count); + return(my_free_list); +# endif +} + +#else +# define alloc8bytes() GC_MALLOC_ATOMIC(8) +#endif + +#include "gc_inline.h" + +void test_tinyfl(void) +{ + void *results[3]; + void *tfls[3][GC_TINY_FREELISTS]; + +# ifndef DONT_ADD_BYTE_AT_END + if (GC_get_all_interior_pointers()) return; /* skip */ +# endif + BZERO(tfls, sizeof(tfls)); + /* TODO: Improve testing of FAST_MALLOC functionality. */ + GC_MALLOC_WORDS(results[0], 11, tfls[0]); + GC_MALLOC_ATOMIC_WORDS(results[1], 20, tfls[1]); + GC_CONS(results[2], results[0], results[1], tfls[2]); +} + +void alloc_small(int n) +{ + int i; + + for (i = 0; i < n; i += 8) { + if (alloc8bytes() == 0) { + GC_printf("Out of memory\n"); + FAIL; + } + AO_fetch_and_add1(&atomic_count); + } +} + +# if defined(THREADS) && defined(GC_DEBUG) +# ifdef VERY_SMALL_CONFIG +# define TREE_HEIGHT 12 +# else +# define TREE_HEIGHT 15 +# endif +# else +# ifdef VERY_SMALL_CONFIG +# define TREE_HEIGHT 13 +# else +# define TREE_HEIGHT 16 +# endif +# endif +void tree_test(void) +{ + tn * root; + int i; + + root = mktree(TREE_HEIGHT); +# ifndef VERY_SMALL_CONFIG + alloc_small(5000000); +# endif + chktree(root, TREE_HEIGHT); + FINALIZER_LOCK(); + if (finalized_count && !dropped_something) { + GC_printf("Premature finalization - collector is broken\n"); + FAIL; + } + dropped_something = 1; + FINALIZER_UNLOCK(); + GC_noop1((word)root); /* Root needs to remain live until */ + /* dropped_something is set. */ + root = mktree(TREE_HEIGHT); + chktree(root, TREE_HEIGHT); + for (i = TREE_HEIGHT; i >= 0; i--) { + root = mktree(i); + chktree(root, i); + } +# ifndef VERY_SMALL_CONFIG + alloc_small(5000000); +# endif +} + +unsigned n_tests = 0; + +const GC_word bm_huge[320 / CPP_WORDSZ] = { +# if CPP_WORDSZ == 32 + 0xffffffff, + 0xffffffff, + 0xffffffff, + 0xffffffff, + 0xffffffff, +# endif + (GC_word)((GC_signed_word)-1), + (GC_word)((GC_signed_word)-1), + (GC_word)((GC_signed_word)-1), + (GC_word)((GC_signed_word)-1), + ((GC_word)((GC_signed_word)-1)) >> 8 /* highest byte is zero */ +}; + +/* A very simple test of explicitly typed allocation */ +void typed_test(void) +{ + GC_word * old, * newP; + GC_word bm3[1] = {0}; + GC_word bm2[1] = {0}; + GC_word bm_large[1] = { 0xf7ff7fff }; + GC_descr d1; + GC_descr d2; + GC_descr d3 = GC_make_descriptor(bm_large, 32); + GC_descr d4 = GC_make_descriptor(bm_huge, 320); + GC_word * x = (GC_word *)GC_malloc_explicitly_typed( + 320 * sizeof(GC_word) + 123, d4); + int i; + + AO_fetch_and_add1(&collectable_count); + (void)GC_make_descriptor(bm_large, 32); + if (GC_get_bit(bm_huge, 32) == 0 || GC_get_bit(bm_huge, 311) == 0 + || GC_get_bit(bm_huge, 319) != 0) { + GC_printf("Bad GC_get_bit() or bm_huge initialization\n"); + FAIL; + } + GC_set_bit(bm3, 0); + GC_set_bit(bm3, 1); + d1 = GC_make_descriptor(bm3, 2); + GC_set_bit(bm2, 1); + d2 = GC_make_descriptor(bm2, 2); + old = 0; + for (i = 0; i < 4000; i++) { + newP = (GC_word *)GC_malloc_explicitly_typed(4 * sizeof(GC_word), d1); + CHECK_OUT_OF_MEMORY(newP); + AO_fetch_and_add1(&collectable_count); + if (newP[0] != 0 || newP[1] != 0) { + GC_printf("Bad initialization by GC_malloc_explicitly_typed\n"); + FAIL; + } + newP[0] = 17; + newP[1] = (GC_word)old; + old = newP; + AO_fetch_and_add1(&collectable_count); + newP = (GC_word *)GC_malloc_explicitly_typed(4 * sizeof(GC_word), d2); + CHECK_OUT_OF_MEMORY(newP); + newP[0] = 17; + newP[1] = (GC_word)old; + GC_END_STUBBORN_CHANGE(newP); + old = newP; + AO_fetch_and_add1(&collectable_count); + newP = (GC_word*)GC_malloc_explicitly_typed(33 * sizeof(GC_word), d3); + CHECK_OUT_OF_MEMORY(newP); + newP[0] = 17; + newP[1] = (GC_word)old; + GC_END_STUBBORN_CHANGE(newP); + old = newP; + AO_fetch_and_add1(&collectable_count); + newP = (GC_word *)GC_calloc_explicitly_typed(4, 2 * sizeof(GC_word), + d1); + CHECK_OUT_OF_MEMORY(newP); + newP[0] = 17; + newP[1] = (GC_word)old; + GC_END_STUBBORN_CHANGE(newP); + old = newP; + AO_fetch_and_add1(&collectable_count); + if (i & 0xff) { + newP = (GC_word *)GC_calloc_explicitly_typed(7, 3 * sizeof(GC_word), + d2); + } else { + newP = (GC_word *)GC_calloc_explicitly_typed(1001, + 3 * sizeof(GC_word), + d2); + if (newP != NULL && (newP[0] != 0 || newP[1] != 0)) { + GC_printf("Bad initialization by GC_malloc_explicitly_typed\n"); + FAIL; + } + } + CHECK_OUT_OF_MEMORY(newP); + newP[0] = 17; + newP[1] = (GC_word)old; + GC_END_STUBBORN_CHANGE(newP); + old = newP; + } + for (i = 0; i < 20000; i++) { + if (newP[0] != 17) { + GC_printf("Typed alloc failed at %d\n", i); + FAIL; + } + newP[0] = 0; + old = newP; + newP = (GC_word *)old[1]; + } + GC_gcollect(); + GC_noop1((word)x); +} + +#ifdef DBG_HDRS_ALL +# define set_print_procs() (void)(A.dummy = 17) +#else + int fail_count = 0; + + void GC_CALLBACK fail_proc1(void *x GC_ATTR_UNUSED) + { + fail_count++; + } + + void set_print_procs(void) + { + /* Set these global variables just once to avoid TSan false positives. */ + A.dummy = 17; + GC_is_valid_displacement_print_proc = fail_proc1; + GC_is_visible_print_proc = fail_proc1; + } + +# ifdef THREADS +# define TEST_FAIL_COUNT(n) 1 +# else +# define TEST_FAIL_COUNT(n) (fail_count >= (n)) +# endif +#endif /* !DBG_HDRS_ALL */ + +static void uniq(void *p, ...) { + va_list a; + void *q[100]; + int n = 0, i, j; + q[n++] = p; + va_start(a,p); + for (;(q[n] = va_arg(a,void *)) != NULL;n++) ; + va_end(a); + for (i=0; i finalizable_count + || finalized_count < finalizable_count/2) { + GC_printf("finalization is probably broken\n"); + FAIL; + } else { + GC_printf("finalization is probably ok\n"); + } + still_live = 0; + for (i = 0; i < MAX_FINALIZED; i++) { + if (live_indicators[i] != 0) { + still_live++; + } +# ifndef GC_LONG_REFS_NOT_NEEDED + if (live_long_refs[i] != NULL) { + still_long_live++; + } +# endif + } + i = finalizable_count - finalized_count - still_live; + if (0 != i) { + GC_printf("%d disappearing links remain and %d more objects " + "were not finalized\n", still_live, i); + if (i > 10) { + GC_printf("\tVery suspicious!\n"); + } else { + GC_printf("\tSlightly suspicious, but probably OK\n"); + } + } +# ifndef GC_LONG_REFS_NOT_NEEDED + if (0 != still_long_live) { + GC_printf("%d 'long' links remain\n", still_long_live); + } +# endif +# endif + GC_printf("Total number of bytes allocated is %lu\n", + (unsigned long)GC_get_total_bytes()); + GC_printf("Total memory use by allocated blocks is %lu bytes\n", + (unsigned long)GC_get_memory_use()); + GC_printf("Final heap size is %lu bytes\n", + (unsigned long)GC_get_heap_size()); + if (GC_get_total_bytes() < (size_t)n_tests * +# ifdef VERY_SMALL_CONFIG + 2700000 +# else + 33500000 +# endif + ) { + GC_printf("Incorrect execution - missed some allocations\n"); + FAIL; + } + if (GC_get_heap_size() + GC_get_unmapped_bytes() > max_heap_sz) { + GC_printf("Unexpected heap growth - collector may be broken" + " (heapsize: %lu, expected: %lu)\n", + (unsigned long)(GC_get_heap_size() + GC_get_unmapped_bytes()), + (unsigned long)max_heap_sz); + FAIL; + } + GC_printf("Final number of reachable objects is %u\n", obj_count); + +# ifndef GC_GET_HEAP_USAGE_NOT_NEEDED + /* Get global counters (just to check the functions work). */ + GC_get_heap_usage_safe(NULL, NULL, NULL, NULL, NULL); + { + struct GC_prof_stats_s stats; + (void)GC_get_prof_stats(&stats, sizeof(stats)); +# ifdef THREADS + (void)GC_get_prof_stats_unsafe(&stats, sizeof(stats)); +# endif + } + (void)GC_get_size_map_at(-1); + (void)GC_get_size_map_at(1); +# endif + +# ifdef THREADS + GC_unregister_my_thread(); /* just to check it works (for main) */ +# endif + GC_printf("Completed %u collections", (unsigned)GC_get_gc_no()); +# ifndef NO_CLOCK + GC_printf(" in %lu msecs", GC_get_full_gc_total_time()); +# endif +# ifdef PARALLEL_MARK + GC_printf(" (using %d marker threads)", GC_get_parallel() + 1); +# endif + GC_printf("\n" "Collector appears to work\n"); +} + +#if defined(MACOS) +void SetMinimumStack(long minSize) +{ + if (minSize > LMGetDefltStack()) + { + long newApplLimit = (long) GetApplLimit() + - (minSize - LMGetDefltStack()); + SetApplLimit((Ptr) newApplLimit); + MaxApplZone(); + } +} + +#define cMinStackSpace (512L * 1024L) + +#endif + +void GC_CALLBACK warn_proc(char *msg, GC_word p) +{ + GC_printf(msg, (unsigned long)p); + /*FAIL;*/ +} + +#if defined(CPPCHECK) +# include "javaxfc.h" /* for GC_finalize_all */ +# define UNTESTED(sym) GC_noop1((word)&sym) +#endif + +#if defined(MSWINCE) && defined(UNDER_CE) +# define WINMAIN_LPTSTR LPWSTR +#else +# define WINMAIN_LPTSTR LPSTR +#endif + +#if !defined(PCR) && !defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) + +#if ((defined(MSWIN32) && !defined(__MINGW32__)) || defined(MSWINCE)) \ + && !defined(NO_WINMAIN_ENTRY) + int APIENTRY WinMain(HINSTANCE instance GC_ATTR_UNUSED, + HINSTANCE prev GC_ATTR_UNUSED, + WINMAIN_LPTSTR cmd GC_ATTR_UNUSED, + int n GC_ATTR_UNUSED) +#elif defined(RTEMS) +# include +# define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER +# define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER +# define CONFIGURE_RTEMS_INIT_TASKS_TABLE +# define CONFIGURE_MAXIMUM_TASKS 1 +# define CONFIGURE_INIT +# define CONFIGURE_INIT_TASK_STACK_SIZE (64*1024) +# include + rtems_task Init(rtems_task_argument ignord) +#else + int main(void) +#endif +{ +# if defined(CPPCHECK) && !defined(NO_WINMAIN_ENTRY) \ + && ((defined(MSWIN32) && !defined(__MINGW32__)) || defined(MSWINCE)) + GC_noop1((GC_word)&WinMain); +# elif defined(CPPCHECK) && defined(RTEMS) + GC_noop1((GC_word)&Init); +# endif + n_tests = 0; +# if defined(MACOS) + /* Make sure we have lots and lots of stack space. */ + SetMinimumStack(cMinStackSpace); + /* Cheat and let stdio initialize toolbox for us. */ + printf("Testing GC Macintosh port\n"); +# endif + GC_COND_INIT(); + GC_set_warn_proc(warn_proc); +# if !defined(GC_DISABLE_INCREMENTAL) \ + && (defined(TEST_DEFAULT_VDB) || !defined(DEFAULT_VDB)) +# if !defined(MAKE_BACK_GRAPH) && !defined(NO_INCREMENTAL) \ + && !(defined(MPROTECT_VDB) && defined(USE_MUNMAP)) + GC_enable_incremental(); +# endif + if (GC_is_incremental_mode()) { + GC_printf("Switched to incremental mode\n"); +# ifdef PROC_VDB + GC_printf("Reading dirty bits from /proc\n"); +# elif defined(GWW_VDB) + GC_printf("Using GetWriteWatch-based implementation\n"); +# ifdef MPROTECT_VDB + GC_printf("Or emulating dirty bits with mprotect/signals\n"); +# endif +# elif defined(MPROTECT_VDB) + GC_printf("Emulating dirty bits with mprotect/signals\n"); +# endif /* MPROTECT_VDB && !GWW_VDB */ + } +# endif + set_print_procs(); + run_one_test(); + check_heap_stats(); +# ifndef MSWINCE + fflush(stdout); +# endif +# if defined(CPPCHECK) + /* Entry points we should be testing, but aren't. */ +# ifndef GC_DEBUG + UNTESTED(GC_debug_generic_or_special_malloc); + UNTESTED(GC_debug_register_displacement); + UNTESTED(GC_post_incr); + UNTESTED(GC_pre_incr); +# ifdef GC_GCJ_SUPPORT + UNTESTED(GC_debug_gcj_malloc); +# endif +# endif +# ifdef AMIGA +# ifdef GC_AMIGA_FASTALLOC + UNTESTED(GC_amiga_get_mem); +# endif +# ifndef GC_AMIGA_ONLYFAST + UNTESTED(GC_amiga_set_toany); +# endif +# endif +# if defined(MACOS) && defined(USE_TEMPORARY_MEMORY) + UNTESTED(GC_MacTemporaryNewPtr); +# endif +# if !defined(_M_AMD64) && defined(_MSC_VER) + UNTESTED(GetFileLineFromStack); + UNTESTED(GetModuleNameFromStack); + UNTESTED(GetSymbolNameFromStack); +# endif + UNTESTED(GC_get_bytes_since_gc); + UNTESTED(GC_get_dont_expand); + UNTESTED(GC_get_dont_precollect); + UNTESTED(GC_get_finalize_on_demand); + UNTESTED(GC_get_finalizer_notifier); + UNTESTED(GC_get_find_leak); + UNTESTED(GC_get_force_unmap_on_gcollect); + UNTESTED(GC_get_free_bytes); + UNTESTED(GC_get_free_space_divisor); + UNTESTED(GC_get_full_freq); + UNTESTED(GC_get_java_finalization); + UNTESTED(GC_get_max_retries); + UNTESTED(GC_get_no_dls); + UNTESTED(GC_get_non_gc_bytes); + UNTESTED(GC_get_on_collection_event); + UNTESTED(GC_get_on_heap_resize); + UNTESTED(GC_get_pages_executable); + UNTESTED(GC_get_push_other_roots); + UNTESTED(GC_get_start_callback); + UNTESTED(GC_get_stop_func); + UNTESTED(GC_get_time_limit); + UNTESTED(GC_get_warn_proc); + UNTESTED(GC_is_disabled); + UNTESTED(GC_set_dont_precollect); + UNTESTED(GC_set_finalize_on_demand); + UNTESTED(GC_set_finalizer_notifier); + UNTESTED(GC_set_free_space_divisor); + UNTESTED(GC_set_full_freq); + UNTESTED(GC_set_java_finalization); + UNTESTED(GC_set_max_retries); + UNTESTED(GC_set_no_dls); + UNTESTED(GC_set_non_gc_bytes); + UNTESTED(GC_set_on_collection_event); + UNTESTED(GC_set_on_heap_resize); + UNTESTED(GC_set_oom_fn); + UNTESTED(GC_set_pages_executable); + UNTESTED(GC_set_push_other_roots); + UNTESTED(GC_set_start_callback); + UNTESTED(GC_set_stop_func); + UNTESTED(GC_set_time_limit); + UNTESTED(GC_malloc_explicitly_typed_ignore_off_page); + UNTESTED(GC_debug_change_stubborn); + UNTESTED(GC_debug_strndup); + UNTESTED(GC_deinit); + UNTESTED(GC_strndup); + UNTESTED(GC_posix_memalign); + UNTESTED(GC_new_free_list); + UNTESTED(GC_new_kind); + UNTESTED(GC_new_proc); + UNTESTED(GC_clear_roots); + UNTESTED(GC_exclude_static_roots); + UNTESTED(GC_expand_hp); + UNTESTED(GC_register_describe_type_fn); + UNTESTED(GC_register_has_static_roots_callback); +# if !defined(PCR) && !defined(SMALL_CONFIG) + UNTESTED(GC_get_abort_func); + UNTESTED(GC_set_abort_func); +# endif +# ifdef GC_GCJ_SUPPORT + UNTESTED(GC_gcj_malloc_ignore_off_page); +# endif +# ifndef NO_DEBUGGING + UNTESTED(GC_dump_regions); + UNTESTED(GC_is_tmp_root); + UNTESTED(GC_print_free_list); +# endif +# ifdef TRACE_BUF + UNTESTED(GC_print_trace); +# endif +# ifndef GC_NO_FINALIZATION + UNTESTED(GC_debug_register_finalizer_unreachable); + UNTESTED(GC_get_await_finalize_proc); + UNTESTED(GC_register_disappearing_link); + UNTESTED(GC_set_await_finalize_proc); + UNTESTED(GC_should_invoke_finalizers); +# ifndef JAVA_FINALIZATION_NOT_NEEDED + UNTESTED(GC_finalize_all); +# endif +# ifndef NO_DEBUGGING + UNTESTED(GC_dump_finalization); +# endif +# ifndef GC_TOGGLE_REFS_NOT_NEEDED + UNTESTED(GC_get_toggleref_func); + UNTESTED(GC_set_toggleref_func); + UNTESTED(GC_toggleref_add); +# endif +# endif +# if !defined(OS2) && !defined(MACOS) && !defined(GC_ANDROID_LOG) \ + && !defined(MSWIN32) && !defined(MSWINCE) + UNTESTED(GC_set_log_fd); +# endif +# ifdef THREADS + UNTESTED(GC_allow_register_threads); + UNTESTED(GC_get_on_thread_event); + UNTESTED(GC_register_altstack); + UNTESTED(GC_set_on_thread_event); +# endif +# ifndef REDIRECT_MALLOC_IN_HEADER +# ifdef REDIRECT_MALLOC +# ifndef strndup + UNTESTED(strndup); +# endif +# ifndef strdup + UNTESTED(strdup); +# endif +# endif +# ifdef REDIRECT_REALLOC + UNTESTED(realloc); +# endif +# endif /* !REDIRECT_MALLOC_IN_HEADER */ +# ifdef GC_REQUIRE_WCSDUP + UNTESTED(GC_wcsdup); + UNTESTED(GC_debug_wcsdup); +# endif +# endif +# ifdef MSWIN32 + GC_win32_free_heap(); +# endif +# ifdef RTEMS + exit(0); +# else + return(0); +# endif +} +# endif /* !GC_WIN32_THREADS && !GC_PTHREADS */ + +#if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) + +DWORD __stdcall thr_run_one_test(void * arg GC_ATTR_UNUSED) +{ + run_one_test(); + return 0; +} + +#ifdef MSWINCE +HANDLE win_created_h; +HWND win_handle; + +LRESULT CALLBACK window_proc(HWND hwnd, UINT uMsg, WPARAM wParam, + LPARAM lParam) +{ + LRESULT ret = 0; + switch (uMsg) { + case WM_HIBERNATE: + GC_printf("Received WM_HIBERNATE, calling GC_gcollect\n"); + /* Force "unmap as much memory as possible" mode. */ + GC_gcollect_and_unmap(); + break; + case WM_CLOSE: + GC_printf("Received WM_CLOSE, closing window\n"); + DestroyWindow(hwnd); + break; + case WM_DESTROY: + PostQuitMessage(0); + break; + default: + ret = DefWindowProc(hwnd, uMsg, wParam, lParam); + break; + } + return ret; +} + +DWORD __stdcall thr_window(void * arg GC_ATTR_UNUSED) +{ + WNDCLASS win_class = { + CS_NOCLOSE, + window_proc, + 0, + 0, + GetModuleHandle(NULL), + NULL, + NULL, + (HBRUSH)(COLOR_APPWORKSPACE+1), + NULL, + TEXT("GCtestWindow") + }; + MSG msg; + + if (!RegisterClass(&win_class)) + FAIL; + + win_handle = CreateWindowEx( + 0, + TEXT("GCtestWindow"), + TEXT("GCtest"), + 0, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + NULL, + NULL, + GetModuleHandle(NULL), + NULL); + + if (win_handle == NULL) + FAIL; + + SetEvent(win_created_h); + + ShowWindow(win_handle, SW_SHOW); + UpdateWindow(win_handle); + + while (GetMessage(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + return 0; +} +#endif + +#if !defined(NO_WINMAIN_ENTRY) + int APIENTRY WinMain(HINSTANCE instance GC_ATTR_UNUSED, + HINSTANCE prev GC_ATTR_UNUSED, + WINMAIN_LPTSTR cmd GC_ATTR_UNUSED, + int n GC_ATTR_UNUSED) +#else + int main(void) +#endif +{ +# if NTHREADS > 0 + HANDLE h[NTHREADS]; + int i; +# endif +# ifdef MSWINCE + HANDLE win_thr_h; +# endif + DWORD thread_id; + +# if defined(CPPCHECK) && !defined(NO_WINMAIN_ENTRY) + GC_noop1((GC_word)&WinMain); +# endif +# if defined(GC_DLL) && !defined(GC_NO_THREADS_DISCOVERY) \ + && !defined(MSWINCE) && !defined(THREAD_LOCAL_ALLOC) \ + && !defined(PARALLEL_MARK) + GC_use_threads_discovery(); + /* Test with implicit thread registration if possible. */ + GC_printf("Using DllMain to track threads\n"); +# endif + GC_COND_INIT(); +# if !defined(MAKE_BACK_GRAPH) && !defined(NO_INCREMENTAL) + GC_enable_incremental(); +# endif + InitializeCriticalSection(&incr_cs); + GC_set_warn_proc(warn_proc); +# ifdef MSWINCE + win_created_h = CreateEvent(NULL, FALSE, FALSE, NULL); + if (win_created_h == (HANDLE)NULL) { + GC_printf("Event creation failed %d\n", (int)GetLastError()); + FAIL; + } + win_thr_h = GC_CreateThread(NULL, 0, thr_window, 0, 0, &thread_id); + if (win_thr_h == (HANDLE)NULL) { + GC_printf("Thread creation failed %d\n", (int)GetLastError()); + FAIL; + } + if (WaitForSingleObject(win_created_h, INFINITE) != WAIT_OBJECT_0) + FAIL; + CloseHandle(win_created_h); +# endif + set_print_procs(); +# if NTHREADS > 0 + for (i = 0; i < NTHREADS; i++) { + h[i] = GC_CreateThread(NULL, 0, thr_run_one_test, 0, 0, &thread_id); + if (h[i] == (HANDLE)NULL) { + GC_printf("Thread creation failed %d\n", (int)GetLastError()); + FAIL; + } + } +# endif /* NTHREADS > 0 */ + run_one_test(); +# if NTHREADS > 0 + for (i = 0; i < NTHREADS; i++) { + if (WaitForSingleObject(h[i], INFINITE) != WAIT_OBJECT_0) { + GC_printf("Thread wait failed %d\n", (int)GetLastError()); + FAIL; + } + } +# endif /* NTHREADS > 0 */ +# ifdef MSWINCE + PostMessage(win_handle, WM_CLOSE, 0, 0); + if (WaitForSingleObject(win_thr_h, INFINITE) != WAIT_OBJECT_0) + FAIL; +# endif + check_heap_stats(); +# if defined(CPPCHECK) && defined(GC_WIN32_THREADS) + UNTESTED(GC_ExitThread); +# if !defined(MSWINCE) && !defined(CYGWIN32) + UNTESTED(GC_beginthreadex); + UNTESTED(GC_endthreadex); +# endif +# endif + return(0); +} + +#endif /* GC_WIN32_THREADS */ + + +#ifdef PCR +int test(void) +{ + PCR_Th_T * th1; + PCR_Th_T * th2; + int code; + +# if defined(CPPCHECK) + GC_noop1((word)&PCR_GC_Run); + GC_noop1((word)&PCR_GC_Setup); + GC_noop1((word)&test); +# endif + n_tests = 0; + /* GC_enable_incremental(); */ + GC_set_warn_proc(warn_proc); + set_print_procs(); + th1 = PCR_Th_Fork(run_one_test, 0); + th2 = PCR_Th_Fork(run_one_test, 0); + run_one_test(); + if (PCR_Th_T_Join(th1, &code, NIL, PCR_allSigsBlocked, PCR_waitForever) + != PCR_ERes_okay || code != 0) { + GC_printf("Thread 1 failed\n"); + } + if (PCR_Th_T_Join(th2, &code, NIL, PCR_allSigsBlocked, PCR_waitForever) + != PCR_ERes_okay || code != 0) { + GC_printf("Thread 2 failed\n"); + } + check_heap_stats(); + return(0); +} +#endif + +#if defined(GC_PTHREADS) +void * thr_run_one_test(void * arg GC_ATTR_UNUSED) +{ + run_one_test(); + return(0); +} + +#ifdef GC_DEBUG +# define GC_free GC_debug_free +#endif + +int main(void) +{ +# if NTHREADS > 0 + pthread_t th[NTHREADS]; + int i; +# endif + pthread_attr_t attr; + int code; +# ifdef GC_IRIX_THREADS + /* Force a larger stack to be preallocated */ + /* Since the initial can't always grow later. */ + *((volatile char *)&code - 1024*1024) = 0; /* Require 1 MB */ +# endif /* GC_IRIX_THREADS */ +# if defined(GC_HPUX_THREADS) + /* Default stack size is too small, especially with the 64 bit ABI */ + /* Increase it. */ + if (pthread_default_stacksize_np(1024*1024, 0) != 0) { + GC_printf("pthread_default_stacksize_np failed\n"); + } +# endif /* GC_HPUX_THREADS */ +# ifdef PTW32_STATIC_LIB + pthread_win32_process_attach_np (); + pthread_win32_thread_attach_np (); +# endif +# if defined(GC_DARWIN_THREADS) && !defined(GC_NO_THREADS_DISCOVERY) \ + && !defined(DARWIN_DONT_PARSE_STACK) && !defined(THREAD_LOCAL_ALLOC) + /* Test with the Darwin implicit thread registration. */ + GC_use_threads_discovery(); + GC_printf("Using Darwin task-threads-based world stop and push\n"); +# endif + GC_COND_INIT(); + + if ((code = pthread_attr_init(&attr)) != 0) { + GC_printf("pthread_attr_init failed, error=%d\n", code); + FAIL; + } +# if defined(GC_IRIX_THREADS) || defined(GC_FREEBSD_THREADS) \ + || defined(GC_DARWIN_THREADS) || defined(GC_AIX_THREADS) \ + || defined(GC_OPENBSD_THREADS) + if ((code = pthread_attr_setstacksize(&attr, 1000 * 1024)) != 0) { + GC_printf("pthread_attr_setstacksize failed, error=%d\n", code); + FAIL; + } +# endif + n_tests = 0; +# if !defined(GC_DISABLE_INCREMENTAL) \ + && (defined(TEST_DEFAULT_VDB) || !defined(DEFAULT_VDB)) +# if !defined(REDIRECT_MALLOC) && !defined(MAKE_BACK_GRAPH) \ + && !defined(USE_PROC_FOR_LIBRARIES) && !defined(NO_INCREMENTAL) \ + && !defined(USE_MUNMAP) + GC_enable_incremental(); +# endif + if (GC_is_incremental_mode()) { + GC_printf("Switched to incremental mode\n"); +# ifdef MPROTECT_VDB + GC_printf("Emulating dirty bits with mprotect/signals\n"); +# endif + } +# endif + GC_set_min_bytes_allocd(1); + if (GC_get_min_bytes_allocd() != 1) + FAIL; + GC_set_rate(10); + GC_set_max_prior_attempts(1); + if (GC_get_rate() != 10 || GC_get_max_prior_attempts() != 1) + FAIL; + GC_set_warn_proc(warn_proc); + if ((code = pthread_key_create(&fl_key, 0)) != 0) { + GC_printf("Key creation failed %d\n", code); + FAIL; + } + set_print_procs(); +# if NTHREADS > 0 + for (i = 0; i < NTHREADS; ++i) { + if ((code = pthread_create(th+i, &attr, thr_run_one_test, 0)) != 0) { + GC_printf("Thread %d creation failed %d\n", i, code); + FAIL; + } + } +# endif + run_one_test(); +# if NTHREADS > 0 + for (i = 0; i < NTHREADS; ++i) { + if ((code = pthread_join(th[i], 0)) != 0) { + GC_printf("Thread %d failed %d\n", i, code); + FAIL; + } + } +# endif + check_heap_stats(); + (void)fflush(stdout); + (void)pthread_attr_destroy(&attr); +# if defined(CPPCHECK) + UNTESTED(GC_set_suspend_signal); + UNTESTED(GC_set_thr_restart_signal); +# ifndef GC_NO_DLOPEN + UNTESTED(GC_dlopen); +# endif +# ifndef GC_NO_PTHREAD_CANCEL + UNTESTED(GC_pthread_cancel); +# endif +# ifdef GC_HAVE_PTHREAD_EXIT + UNTESTED(GC_pthread_exit); +# endif +# ifndef GC_NO_PTHREAD_SIGMASK + UNTESTED(GC_pthread_sigmask); +# endif +# ifdef NO_TEST_HANDLE_FORK + UNTESTED(GC_atfork_child); + UNTESTED(GC_atfork_parent); + UNTESTED(GC_atfork_prepare); + UNTESTED(GC_set_handle_fork); + UNTESTED(GC_start_mark_threads); +# endif +# endif /* CPPCHECK */ +# ifdef PTW32_STATIC_LIB + pthread_win32_thread_detach_np (); + pthread_win32_process_detach_np (); +# endif + return(0); +} +#endif /* GC_PTHREADS */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/tests/test_atomic_ops.c b/unity-2019.4.24f1-mbe/external/bdwgc/tests/test_atomic_ops.c new file mode 100644 index 000000000..23bb405b7 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/tests/test_atomic_ops.c @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2017 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* Minimal testing of atomic operations used by the BDWGC. Primary use */ +/* is to determine whether compiler atomic intrinsics can be relied on. */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#if defined(GC_BUILTIN_ATOMIC) || defined(GC_THREADS) + +# include + +# ifdef PARALLEL_MARK +# define AO_REQUIRE_CAS +# endif + +# include "private/gc_atomic_ops.h" + +# define TA_assert(e) \ + if (!(e)) { \ + fprintf(stderr, "Assertion failure, line %d: " #e "\n", __LINE__); \ + exit(-1); \ + } + + int main(void) { + AO_t x = 13; +# if defined(AO_HAVE_char_load) || defined(AO_HAVE_char_store) + unsigned char c = 117; +# endif +# ifdef AO_HAVE_test_and_set_acquire + AO_TS_t z = AO_TS_INITIALIZER; + + TA_assert(AO_test_and_set_acquire(&z) == AO_TS_CLEAR); + TA_assert(AO_test_and_set_acquire(&z) == AO_TS_SET); + AO_CLEAR(&z); +# endif + AO_compiler_barrier(); +# ifdef AO_HAVE_nop_full + AO_nop_full(); +# endif +# ifdef AO_HAVE_char_load + TA_assert(AO_char_load(&c) == 117); +# endif +# ifdef AO_HAVE_char_store + AO_char_store(&c, 119); + TA_assert(c == 119); +# endif +# ifdef AO_HAVE_load_acquire + TA_assert(AO_load_acquire(&x) == 13); +# endif +# if defined(AO_HAVE_fetch_and_add) && defined(AO_HAVE_fetch_and_add1) + TA_assert(AO_fetch_and_add(&x, 42) == 13); + TA_assert(AO_fetch_and_add(&x, (AO_t)(-43)) == 55); + TA_assert(AO_fetch_and_add1(&x) == 12); +# endif +# ifdef AO_HAVE_compare_and_swap_release + TA_assert(!AO_compare_and_swap(&x, 14, 42)); + TA_assert(x == 13); + TA_assert(AO_compare_and_swap_release(&x, 13, 42)); + TA_assert(x == 42); +# else + if (*(volatile AO_t *)&x == 13) + *(volatile AO_t *)&x = 42; +# endif +# ifdef AO_HAVE_or + AO_or(&x, 66); + TA_assert(x == 106); +# endif +# ifdef AO_HAVE_store_release + AO_store_release(&x, 113); + TA_assert(x == 113); +# endif + return 0; + } + +#else + + int main(void) + { + printf("test_atomic_ops skipped\n"); + return 0; + } + +#endif diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/tests/test_cpp.cc b/unity-2019.4.24f1-mbe/external/bdwgc/tests/test_cpp.cc new file mode 100644 index 000000000..d1146d9bb --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/tests/test_cpp.cc @@ -0,0 +1,395 @@ +/**************************************************************************** +Copyright (c) 1994 by Xerox Corporation. All rights reserved. + +THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED +OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + +Permission is hereby granted to use or copy this program for any +purpose, provided the above notices are retained on all copies. +Permission to modify the code and to distribute modified code is +granted, provided the above notices are retained, and a notice that +the code was modified is included with the above copyright notice. +**************************************************************************** + +usage: test_cpp number-of-iterations + +This program tries to test the specific C++ functionality provided by +gc_c++.h that isn't tested by the more general test routines of the +collector. + +A recommended value for number-of-iterations is 10, which will take a +few minutes to complete. + +***************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#undef GC_BUILD + +#include "gc_cpp.h" + +#include +#include +#include + +#ifndef DONT_USE_STD_ALLOCATOR +# include "gc_allocator.h" +#else + /* Note: This works only for ancient STL versions. */ +# include "new_gc_alloc.h" +#endif + +# include "private/gcconfig.h" + +# ifndef GC_API_PRIV +# define GC_API_PRIV GC_API +# endif +extern "C" { + GC_API_PRIV void GC_printf(const char * format, ...); + /* Use GC private output to reach the same log file. */ + /* Don't include gc_priv.h, since that may include Windows system */ + /* header files that don't take kindly to this context. */ +} + +#ifdef MSWIN32 +# include +#endif + +#ifdef GC_NAME_CONFLICT +# define USE_GC GC_NS_QUALIFY(UseGC) + struct foo * GC; +#else +# define USE_GC GC_NS_QUALIFY(GC) +#endif + +#define my_assert( e ) \ + if (! (e)) { \ + GC_printf( "Assertion failure in " __FILE__ ", line %d: " #e "\n", \ + __LINE__ ); \ + exit( 1 ); } + +#ifndef GC_ATTR_EXPLICIT +# if (__cplusplus >= 201103L) || defined(CPPCHECK) +# define GC_ATTR_EXPLICIT explicit +# else +# define GC_ATTR_EXPLICIT /* empty */ +# endif +#endif + +class A {public: + /* An uncollectible class. */ + + GC_ATTR_EXPLICIT A( int iArg ): i( iArg ) {} + void Test( int iArg ) { + my_assert( i == iArg );} + int i;}; + + +class B: public GC_NS_QUALIFY(gc), public A { public: + /* A collectible class. */ + + GC_ATTR_EXPLICIT B( int j ): A( j ) {} + virtual ~B() { + my_assert( deleting );} + static void Deleting( int on ) { + deleting = on;} + static int deleting;}; + +int B::deleting = 0; + + +class C: public GC_NS_QUALIFY(gc_cleanup), public A { public: + /* A collectible class with cleanup and virtual multiple inheritance. */ + + // The class uses dynamic memory/resource allocation, so provide both + // a copy constructor and an assignment operator to workaround a cppcheck + // warning. + C(const C& c) : A(c.i), level(c.level) { + left = c.left ? new C(*c.left) : 0; + right = c.right ? new C(*c.right) : 0; + } + + C& operator=(const C& c) { + if (this != &c) { + delete left; + delete right; + i = c.i; + level = c.level; + left = c.left ? new C(*c.left) : 0; + right = c.right ? new C(*c.right) : 0; + } + return *this; + } + + GC_ATTR_EXPLICIT C( int levelArg ): A( levelArg ), level( levelArg ) { + nAllocated++; + if (level > 0) { + left = new C( level - 1 ); + right = new C( level - 1 );} + else { + left = right = 0;}} + ~C() { + this->A::Test( level ); + nFreed++; + my_assert( level == 0 ? + left == 0 && right == 0 : + level == left->level + 1 && level == right->level + 1 ); + left = right = 0; + level = -123456;} + static void Test() { + my_assert( nFreed <= nAllocated && nFreed >= .8 * nAllocated );} + + static int nFreed; + static int nAllocated; + int level; + C* left; + C* right;}; + +int C::nFreed = 0; +int C::nAllocated = 0; + + +class D: public GC_NS_QUALIFY(gc) { public: + /* A collectible class with a static member function to be used as + an explicit clean-up function supplied to ::new. */ + + GC_ATTR_EXPLICIT D( int iArg ): i( iArg ) { + nAllocated++;} + static void CleanUp( void* obj, void* data ) { + D* self = static_cast(obj); + nFreed++; + my_assert( (GC_word)self->i == (GC_word)data );} + static void Test() { + my_assert( nFreed >= .8 * nAllocated );} + + int i; + static int nFreed; + static int nAllocated;}; + +int D::nFreed = 0; +int D::nAllocated = 0; + + +class E: public GC_NS_QUALIFY(gc_cleanup) { public: + /* A collectible class with clean-up for use by F. */ + + E() { + nAllocated++;} + ~E() { + nFreed++;} + + static int nFreed; + static int nAllocated;}; + +int E::nFreed = 0; +int E::nAllocated = 0; + + +class F: public E {public: + /* A collectible class with clean-up, a base with clean-up, and a + member with clean-up. */ + + F() { + nAllocatedF++; + } + + ~F() { + nFreedF++; + } + + static void Test() { + my_assert(nFreedF >= .8 * nAllocatedF); + my_assert(2 * nFreedF == nFreed); + } + + E e; + static int nFreedF; + static int nAllocatedF; +}; + +int F::nFreedF = 0; +int F::nAllocatedF = 0; + + +GC_word Disguise( void* p ) { + return ~ (GC_word) p;} + +void* Undisguise( GC_word i ) { + return (void*) ~ i;} + +#define GC_CHECKED_DELETE(p) \ + do { \ + size_t freed_before = GC_get_expl_freed_bytes_since_gc(); \ + delete p; /* the operator should invoke GC_FREE() */ \ + size_t freed_after = GC_get_expl_freed_bytes_since_gc(); \ + my_assert(freed_before != freed_after); \ + } while (0) + +#if ((defined(MSWIN32) && !defined(__MINGW32__)) || defined(MSWINCE)) \ + && !defined(NO_WINMAIN_ENTRY) + int APIENTRY WinMain( HINSTANCE /* instance */, HINSTANCE /* prev */, + LPSTR cmd, int /* cmdShow */) + { + int argc = 0; + char* argv[ 3 ]; + +# if defined(CPPCHECK) + GC_noop1((GC_word)&WinMain); +# endif + if (cmd != 0) + for (argc = 1; argc < (int)(sizeof(argv) / sizeof(argv[0])); argc++) { + // Parse the command-line string. Non-reentrant strtok() is not used + // to avoid complains of static analysis tools. (And, strtok_r() is + // not available on some platforms.) The code is equivalent to: + // if (!(argv[argc] = strtok(argc == 1 ? cmd : 0, " \t"))) break; + if (NULL == cmd) { + argv[argc] = NULL; + break; + } + argv[argc] = cmd; + for (; *cmd != '\0'; cmd++) { + if (*cmd != ' ' && *cmd != '\t') + break; + } + if ('\0' == *cmd) { + argv[argc] = NULL; + break; + } + argv[argc] = cmd; + while (*(++cmd) != '\0') { + if (*cmd == ' ' || *cmd == '\t') + break; + } + if (*cmd != '\0') { + *(cmd++) = '\0'; + } else { + cmd = NULL; + } + } +#elif defined(MACOS) + int main() { + char* argv_[] = {"test_cpp", "10"}; // MacOS doesn't have a command line + argv = argv_; + argc = sizeof(argv_)/sizeof(argv_[0]); +#else + int main( int argc, char* argv[] ) { +#endif + + GC_set_all_interior_pointers(1); + /* needed due to C++ multiple inheritance used */ + + GC_INIT(); + + int i, iters, n; +# ifndef DONT_USE_STD_ALLOCATOR + int *x = gc_allocator().allocate(1); + int *xio; + xio = gc_allocator_ignore_off_page().allocate(1); + (void)xio; + int **xptr = traceable_allocator().allocate(1); +# else + int *x = (int *)gc_alloc::allocate(sizeof(int)); +# endif + *x = 29; +# ifndef DONT_USE_STD_ALLOCATOR + if (!xptr) { + fprintf(stderr, "Out of memory!\n"); + exit(3); + } + *xptr = x; + x = 0; +# endif + if (argc != 2 + || (n = (int)COVERT_DATAFLOW(atoi(argv[1]))) <= 0) { + GC_printf("usage: test_cpp number-of-iterations\n" + "Assuming 10 iters\n"); + n = 10; + } + + for (iters = 1; iters <= n; iters++) { + GC_printf( "Starting iteration %d\n", iters ); + + /* Allocate some uncollectible As and disguise their pointers. + Later we'll check to see if the objects are still there. We're + checking to make sure these objects really are uncollectible. */ + GC_word as[ 1000 ]; + GC_word bs[ 1000 ]; + for (i = 0; i < 1000; i++) { + as[ i ] = Disguise( new (GC_NS_QUALIFY(NoGC)) A(i) ); + bs[ i ] = Disguise( new (GC_NS_QUALIFY(NoGC)) B(i) ); } + + /* Allocate a fair number of finalizable Cs, Ds, and Fs. + Later we'll check to make sure they've gone away. */ + for (i = 0; i < 1000; i++) { + C* c = new C( 2 ); + C c1( 2 ); /* stack allocation should work too */ + D* d; + F* f; + d = ::new (USE_GC, D::CleanUp, (void*)(GC_word)i) D( i ); + (void)d; + f = new F; + F** fa = new F*[1]; + fa[0] = f; + (void)fa; + delete[] fa; + if (0 == i % 10) + GC_CHECKED_DELETE(c); + } + + /* Allocate a very large number of collectible As and Bs and + drop the references to them immediately, forcing many + collections. */ + for (i = 0; i < 1000000; i++) { + A* a; + a = new (USE_GC) A( i ); + (void)a; + B* b; + b = new B( i ); + (void)b; + b = new (USE_GC) B( i ); + if (0 == i % 10) { + B::Deleting( 1 ); + GC_CHECKED_DELETE(b); + B::Deleting( 0 );} +# ifdef FINALIZE_ON_DEMAND + GC_invoke_finalizers(); +# endif + } + + /* Make sure the uncollectible As and Bs are still there. */ + for (i = 0; i < 1000; i++) { + A* a = static_cast(Undisguise(as[i])); + B* b = static_cast(Undisguise(bs[i])); + a->Test( i ); +# if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) + // Workaround for ASan/MSan: the linker uses operator delete + // implementation from libclang_rt instead of gc_cpp (thus + // causing incompatible alloc/free). + GC_FREE(a); +# else + GC_CHECKED_DELETE(a); +# endif + b->Test( i ); + B::Deleting( 1 ); + GC_CHECKED_DELETE(b); + B::Deleting( 0 ); +# ifdef FINALIZE_ON_DEMAND + GC_invoke_finalizers(); +# endif + } + + /* Make sure most of the finalizable Cs, Ds, and Fs have + gone away. */ + C::Test(); + D::Test(); + F::Test();} + +# ifndef DONT_USE_STD_ALLOCATOR + x = *xptr; +# endif + my_assert (29 == x[0]); + GC_printf( "The test appears to have succeeded.\n" ); + return( 0 ); +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/tests/tests.am b/unity-2019.4.24f1-mbe/external/bdwgc/tests/tests.am new file mode 100644 index 000000000..b851f35a0 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/tests/tests.am @@ -0,0 +1,149 @@ +# +# THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED +# OR IMPLIED. ANY USE IS AT YOUR OWN RISK. +# +# Permission is hereby granted to use or copy this program +# for any purpose, provided the above notices are retained on all copies. +# Permission to modify the code and to distribute modified code is granted, +# provided the above notices are retained, and a notice that the code was +# modified is included with the above copyright notice. + +# Common libs to _LDADD for all tests. +test_ldadd = $(top_builddir)/libgc.la $(EXTRA_TEST_LIBS) + +TESTS += gctest$(EXEEXT) +check_PROGRAMS += gctest +gctest_SOURCES = tests/test.c +gctest_LDADD = $(test_ldadd) +if THREADS +gctest_LDADD += $(THREADDLLIBS) +endif +gctest_DEPENDENCIES = $(top_builddir)/libgc.la + +TESTS += leaktest$(EXEEXT) +check_PROGRAMS += leaktest +leaktest_SOURCES = tests/leak_test.c +leaktest_LDADD = $(test_ldadd) + +TESTS += middletest$(EXEEXT) +check_PROGRAMS += middletest +middletest_SOURCES = tests/middle.c +middletest_LDADD = $(test_ldadd) + +TESTS += smashtest$(EXEEXT) +check_PROGRAMS += smashtest +smashtest_SOURCES = tests/smash_test.c +smashtest_LDADD = $(test_ldadd) + +TESTS += hugetest$(EXEEXT) +check_PROGRAMS += hugetest +hugetest_SOURCES = tests/huge_test.c +hugetest_LDADD = $(test_ldadd) + +TESTS += realloc_test$(EXEEXT) +check_PROGRAMS += realloc_test +realloc_test_SOURCES = tests/realloc_test.c +realloc_test_LDADD = $(test_ldadd) + +TESTS += staticrootstest$(EXEEXT) +check_PROGRAMS += staticrootstest +staticrootstest_SOURCES = tests/staticrootstest.c +staticrootstest_CFLAGS = -DSTATICROOTSLIB2 +staticrootstest_LDADD = $(test_ldadd) libstaticrootslib_test.la \ + libstaticrootslib2_test.la +check_LTLIBRARIES += libstaticrootslib_test.la libstaticrootslib2_test.la +libstaticrootslib_test_la_SOURCES = tests/staticrootslib.c +libstaticrootslib_test_la_LIBADD = $(test_ldadd) +libstaticrootslib_test_la_LDFLAGS = -no-undefined -rpath /nowhere +libstaticrootslib_test_la_DEPENDENCIES = $(top_builddir)/libgc.la +libstaticrootslib2_test_la_SOURCES = tests/staticrootslib.c +libstaticrootslib2_test_la_LIBADD = $(test_ldadd) +libstaticrootslib2_test_la_CFLAGS = -DSTATICROOTSLIB2 +libstaticrootslib2_test_la_LDFLAGS = -no-undefined -rpath /nowhere + +if KEEP_BACK_PTRS +TESTS += tracetest$(EXEEXT) +check_PROGRAMS += tracetest +tracetest_SOURCES = tests/trace_test.c +tracetest_LDADD = $(test_ldadd) +endif + +if THREADS + +TESTS += test_atomic_ops$(EXEEXT) +check_PROGRAMS += test_atomic_ops +test_atomic_ops_SOURCES = tests/test_atomic_ops.c +# Really should need only $(ATOMIC_OPS_LIBS) +test_atomic_ops_LDADD = $(test_ldadd) $(THREADDLLIBS) + +TESTS += threadleaktest$(EXEEXT) +check_PROGRAMS += threadleaktest +threadleaktest_SOURCES = tests/thread_leak_test.c +threadleaktest_LDADD = $(test_ldadd) $(THREADDLLIBS) + +TESTS += threadkey_test$(EXEEXT) +check_PROGRAMS += threadkey_test +threadkey_test_SOURCES = tests/threadkey_test.c +threadkey_test_LDADD = $(test_ldadd) $(THREADDLLIBS) + +TESTS += subthreadcreate_test$(EXEEXT) +check_PROGRAMS += subthreadcreate_test +subthreadcreate_test_SOURCES = tests/subthread_create.c +subthreadcreate_test_LDADD = $(test_ldadd) $(THREADDLLIBS) + +TESTS += initsecondarythread_test$(EXEEXT) +check_PROGRAMS += initsecondarythread_test +initsecondarythread_test_SOURCES = tests/initsecondarythread.c +initsecondarythread_test_LDADD = $(test_ldadd) $(THREADDLLIBS) + +endif + +if CPLUSPLUS +TESTS += test_cpp$(EXEEXT) +check_PROGRAMS += test_cpp +test_cpp_SOURCES = tests/test_cpp.cc +if AVOID_CPP_LIB +test_cpp_LDADD = gc_cpp.o $(test_ldadd) $(CXXLIBS) +else +test_cpp_LDADD = libgccpp.la $(test_ldadd) $(CXXLIBS) +endif +endif + +if ENABLE_DISCLAIM + +TESTS += disclaim_test$(EXEEXT) +check_PROGRAMS += disclaim_test +disclaim_test_SOURCES = tests/disclaim_test.c +disclaim_test_LDADD = $(test_ldadd) +if THREADS +disclaim_test_LDADD += $(THREADDLLIBS) +endif + +TESTS += disclaim_bench$(EXEEXT) +check_PROGRAMS += disclaim_bench +disclaim_bench_SOURCES = tests/disclaim_bench.c +disclaim_bench_LDADD = $(test_ldadd) + +endif + +# Run the tests directly (without test-driver): +.PHONY: check-without-test-driver +check-without-test-driver: $(TESTS) + ./gctest$(EXEEXT) + ./hugetest$(EXEEXT) + ./leaktest$(EXEEXT) + ./middletest$(EXEEXT) + ./realloc_test$(EXEEXT) + ./smashtest$(EXEEXT) + ./staticrootstest$(EXEEXT) + test ! -f disclaim_bench$(EXEEXT) || ./disclaim_bench$(EXEEXT) + test ! -f disclaim_test$(EXEEXT) || ./disclaim_test$(EXEEXT) + test ! -f initsecondarythread_test$(EXEEXT) \ + || ./initsecondarythread_test$(EXEEXT) + test ! -f test_atomic_ops$(EXEEXT) || ./test_atomic_ops$(EXEEXT) + test ! -f threadkey_test$(EXEEXT) || ./threadkey_test$(EXEEXT) + test ! -f threadleaktest$(EXEEXT) || ./threadleaktest$(EXEEXT) + test ! -f subthreadcreate_test$(EXEEXT) || ./subthreadcreate_test$(EXEEXT) + test ! -f test_cpp$(EXEEXT) || ./test_cpp$(EXEEXT) + test ! -f tracetest$(EXEEXT) || ./tracetest$(EXEEXT) + ./cordtest$(EXEEXT) diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/tests/thread_leak_test.c b/unity-2019.4.24f1-mbe/external/bdwgc/tests/thread_leak_test.c new file mode 100644 index 000000000..c4fb74e9b --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/tests/thread_leak_test.c @@ -0,0 +1,95 @@ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifndef GC_THREADS +# define GC_THREADS +#endif + +#undef GC_NO_THREAD_REDIRECTS +#include "leak_detector.h" + +#ifdef GC_PTHREADS +# include +#else +# include +#endif + +#include + +#ifdef GC_PTHREADS + void * test(void * arg) +#else + DWORD WINAPI test(LPVOID arg) +#endif +{ + int *p[10]; + int i; + for (i = 0; i < 10; ++i) { + p[i] = (int *)malloc(sizeof(int) + i); + } + CHECK_LEAKS(); + for (i = 1; i < 10; ++i) { + free(p[i]); + } +# ifdef GC_PTHREADS + return arg; +# else + return (DWORD)(GC_word)arg; +# endif +} + +#ifndef NTHREADS +# define NTHREADS 5 +#endif + +int main(void) { +# if NTHREADS > 0 + int i; +# ifdef GC_PTHREADS + pthread_t t[NTHREADS]; +# else + HANDLE t[NTHREADS]; + DWORD thread_id; +# endif + int code; +# endif + + GC_set_find_leak(1); /* for new collect versions not compiled */ + /* with -DFIND_LEAK. */ + GC_INIT(); + +# if NTHREADS > 0 + for (i = 0; i < NTHREADS; ++i) { +# ifdef GC_PTHREADS + code = pthread_create(t + i, 0, test, 0); +# else + t[i] = CreateThread(NULL, 0, test, 0, 0, &thread_id); + code = t[i] != NULL ? 0 : (int)GetLastError(); +# endif + if (code != 0) { + fprintf(stderr, "Thread creation failed %d\n", code); + exit(2); + } + } + + for (i = 0; i < NTHREADS; ++i) { +# ifdef GC_PTHREADS + code = pthread_join(t[i], 0); +# else + code = WaitForSingleObject(t[i], INFINITE) == WAIT_OBJECT_0 ? 0 : + (int)GetLastError(); +# endif + if (code != 0) { + fprintf(stderr, "Thread join failed %d\n", code); + exit(2); + } + } +# endif + + CHECK_LEAKS(); + CHECK_LEAKS(); + CHECK_LEAKS(); + return 0; +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/tests/threadkey_test.c b/unity-2019.4.24f1-mbe/external/bdwgc/tests/threadkey_test.c new file mode 100644 index 000000000..e348d7248 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/tests/threadkey_test.c @@ -0,0 +1,115 @@ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifndef GC_THREADS +# define GC_THREADS +#endif + +#define GC_NO_THREAD_REDIRECTS 1 + +#include "gc.h" + +#include +#include + +#if (!defined(GC_PTHREADS) || defined(GC_SOLARIS_THREADS) \ + || defined(__native_client__)) && !defined(SKIP_THREADKEY_TEST) + /* FIXME: Skip this test on Solaris for now. The test may fail on */ + /* other targets as well. Currently, tested only on Linux, Cygwin */ + /* and Darwin. */ +# define SKIP_THREADKEY_TEST +#endif + +#ifdef SKIP_THREADKEY_TEST + +int main (void) +{ + printf("threadkey_test skipped\n"); + return 0; +} + +#else + +#include + +pthread_key_t key; + +#ifdef GC_SOLARIS_THREADS + /* pthread_once_t key_once = { PTHREAD_ONCE_INIT }; */ +#else + pthread_once_t key_once = PTHREAD_ONCE_INIT; +#endif + +void * entry (void *arg) +{ + pthread_setspecific(key, + (void *)GC_HIDE_POINTER(GC_STRDUP("hello, world"))); + return arg; +} + +void * GC_CALLBACK on_thread_exit_inner (struct GC_stack_base * sb, void * arg) +{ + int res = GC_register_my_thread (sb); + pthread_t t; + int creation_res; /* Used to suppress a warning about */ + /* unchecked pthread_create() result. */ + pthread_attr_t attr; + + if (pthread_attr_init(&attr) != 0 + || pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0) { + fprintf(stderr, "Thread attribute init or setdetachstate failed\n"); + exit(2); + } + creation_res = GC_pthread_create(&t, &attr, entry, NULL); + (void)pthread_attr_destroy(&attr); + if (res == GC_SUCCESS) + GC_unregister_my_thread (); + + return arg ? (void*)(GC_word)creation_res : 0; +} + +void on_thread_exit (void *v) +{ + GC_call_with_stack_base (on_thread_exit_inner, v); +} + +void make_key (void) +{ + pthread_key_create (&key, on_thread_exit); +} + +#ifndef NTHREADS +# define NTHREADS 30 /* number of initial threads */ +#endif + +int main (void) +{ + int i; + GC_INIT (); + +# ifdef GC_SOLARIS_THREADS + pthread_key_create (&key, on_thread_exit); +# else + pthread_once (&key_once, make_key); +# endif + for (i = 0; i < NTHREADS; i++) { + pthread_t t; + + if (GC_pthread_create(&t, NULL, entry, NULL) == 0) { + void *res; + int code = (i & 1) != 0 ? GC_pthread_join(t, &res) + : GC_pthread_detach(t); + + if (code != 0) { + fprintf(stderr, "Thread %s failed %d\n", + (i & 1) != 0 ? "join" : "detach", code); + exit(2); + } + } + } + return 0; +} + +#endif /* !SKIP_THREADKEY_TEST */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/tests/trace_test.c b/unity-2019.4.24f1-mbe/external/bdwgc/tests/trace_test.c new file mode 100644 index 000000000..d5af89176 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/tests/trace_test.c @@ -0,0 +1,43 @@ +#include +#include + +#ifndef GC_DEBUG +# define GC_DEBUG +#endif + +#include "gc.h" +#include "gc_backptr.h" + +struct treenode { + struct treenode *x; + struct treenode *y; +} * root[10]; + +struct treenode * mktree(int i) { + struct treenode * r = GC_NEW(struct treenode); + if (0 == i) + return 0; + if (1 == i) + r = (struct treenode *)GC_MALLOC_ATOMIC(sizeof(struct treenode)); + if (r == NULL) { + fprintf(stderr, "Out of memory\n"); + exit(1); + } + r -> x = mktree(i-1); + r -> y = mktree(i-1); + return r; +} + +int main(void) +{ + int i; + GC_INIT(); + for (i = 0; i < 10; ++i) { + root[i] = mktree(12); + } + GC_generate_random_backtrace(); + GC_generate_random_backtrace(); + GC_generate_random_backtrace(); + GC_generate_random_backtrace(); + return 0; +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/thread_local_alloc.c b/unity-2019.4.24f1-mbe/external/bdwgc/thread_local_alloc.c new file mode 100644 index 000000000..3a04fbe8d --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/thread_local_alloc.c @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2000-2005 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#include "private/gc_priv.h" + +#if defined(THREAD_LOCAL_ALLOC) + +#ifndef THREADS +# error "invalid config - THREAD_LOCAL_ALLOC requires GC_THREADS" +#endif + +#include "private/thread_local_alloc.h" + +#include + +#if defined(USE_COMPILER_TLS) + __thread GC_ATTR_TLS_FAST +#elif defined(USE_WIN32_COMPILER_TLS) + __declspec(thread) GC_ATTR_TLS_FAST +#endif +GC_key_t GC_thread_key; + +static GC_bool keys_initialized; + +/* Return a single nonempty freelist fl to the global one pointed to */ +/* by gfl. */ + +static void return_single_freelist(void *fl, void **gfl) +{ + if (*gfl == 0) { + *gfl = fl; + } else { + void *q, **qptr; + + GC_ASSERT(GC_size(fl) == GC_size(*gfl)); + /* Concatenate: */ + qptr = &(obj_link(fl)); + while ((word)(q = *qptr) >= HBLKSIZE) + qptr = &(obj_link(q)); + GC_ASSERT(0 == q); + *qptr = *gfl; + *gfl = fl; + } +} + +/* Recover the contents of the freelist array fl into the global one gfl.*/ +/* We hold the allocator lock. */ +static void return_freelists(void **fl, void **gfl) +{ + int i; + + for (i = 1; i < TINY_FREELISTS; ++i) { + if ((word)(fl[i]) >= HBLKSIZE) { + return_single_freelist(fl[i], &gfl[i]); + } + /* Clear fl[i], since the thread structure may hang around. */ + /* Do it in a way that is likely to trap if we access it. */ + fl[i] = (ptr_t)HBLKSIZE; + } + /* The 0 granule freelist really contains 1 granule objects. */ +# ifdef GC_GCJ_SUPPORT + if (fl[0] == ERROR_FL) return; +# endif + if ((word)(fl[0]) >= HBLKSIZE) { + return_single_freelist(fl[0], &gfl[1]); + } +} + +#ifdef USE_PTHREAD_SPECIFIC + /* Re-set the TLS value on thread cleanup to allow thread-local */ + /* allocations to happen in the TLS destructors. */ + /* GC_unregister_my_thread (and similar routines) will finally set */ + /* the GC_thread_key to NULL preventing this destructor from being */ + /* called repeatedly. */ + static void reset_thread_key(void* v) { + pthread_setspecific(GC_thread_key, v); + } +#else +# define reset_thread_key 0 +#endif + +/* Each thread structure must be initialized. */ +/* This call must be made from the new thread. */ +GC_INNER void GC_init_thread_local(GC_tlfs p) +{ + int i, j, res; + + GC_ASSERT(I_HOLD_LOCK()); + if (!EXPECT(keys_initialized, TRUE)) { + GC_ASSERT((word)&GC_thread_key % sizeof(word) == 0); + res = GC_key_create(&GC_thread_key, reset_thread_key); + if (COVERT_DATAFLOW(res) != 0) { + ABORT("Failed to create key for local allocator"); + } + keys_initialized = TRUE; + } + res = GC_setspecific(GC_thread_key, p); + if (COVERT_DATAFLOW(res) != 0) { + ABORT("Failed to set thread specific allocation pointers"); + } + for (j = 0; j < TINY_FREELISTS; ++j) { + for (i = 0; i < THREAD_FREELISTS_KINDS; ++i) { + p -> _freelists[i][j] = (void *)(word)1; + } +# ifdef GC_GCJ_SUPPORT + p -> gcj_freelists[j] = (void *)(word)1; +# endif + } + /* The size 0 free lists are handled like the regular free lists, */ + /* to ensure that the explicit deallocation works. However, */ + /* allocation of a size 0 "gcj" object is always an error. */ +# ifdef GC_GCJ_SUPPORT + p -> gcj_freelists[0] = ERROR_FL; +# endif +} + +/* We hold the allocator lock. */ +GC_INNER void GC_destroy_thread_local(GC_tlfs p) +{ + int k; + + /* We currently only do this from the thread itself. */ + GC_STATIC_ASSERT(THREAD_FREELISTS_KINDS <= MAXOBJKINDS); + for (k = 0; k < THREAD_FREELISTS_KINDS; ++k) { + if (k == (int)GC_n_kinds) + break; /* kind is not created */ + return_freelists(p -> _freelists[k], GC_obj_kinds[k].ok_freelist); + } +# ifdef GC_GCJ_SUPPORT + return_freelists(p -> gcj_freelists, (void **)GC_gcjobjfreelist); +# endif +} + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_kind(size_t bytes, int kind) +{ + size_t granules; + void *tsd; + void *result; + +# if MAXOBJKINDS > THREAD_FREELISTS_KINDS + if (EXPECT(kind >= THREAD_FREELISTS_KINDS, FALSE)) { + return GC_malloc_kind_global(bytes, kind); + } +# endif +# if !defined(USE_PTHREAD_SPECIFIC) && !defined(USE_WIN32_SPECIFIC) + { + GC_key_t k = GC_thread_key; + + if (EXPECT(0 == k, FALSE)) { + /* We haven't yet run GC_init_parallel. That means */ + /* we also aren't locking, so this is fairly cheap. */ + return GC_malloc_kind_global(bytes, kind); + } + tsd = GC_getspecific(k); + } +# else + if (!EXPECT(keys_initialized, TRUE)) + return GC_malloc_kind_global(bytes, kind); + tsd = GC_getspecific(GC_thread_key); +# endif +# if !defined(USE_COMPILER_TLS) && !defined(USE_WIN32_COMPILER_TLS) + if (EXPECT(0 == tsd, FALSE)) { + return GC_malloc_kind_global(bytes, kind); + } +# endif + GC_ASSERT(GC_is_initialized); + GC_ASSERT(GC_is_thread_tsd_valid(tsd)); + granules = ROUNDED_UP_GRANULES(bytes); + GC_FAST_MALLOC_GRANS(result, granules, + ((GC_tlfs)tsd) -> _freelists[kind], DIRECT_GRANULES, + kind, GC_malloc_kind_global(bytes, kind), + (void)(kind == PTRFREE ? NULL + : (obj_link(result) = 0))); +# ifdef LOG_ALLOCS + GC_log_printf("GC_malloc_kind(%lu, %d) returned %p, recent GC #%lu\n", + (unsigned long)bytes, kind, result, + (unsigned long)GC_gc_no); +# endif + return result; +} + +#ifdef GC_GCJ_SUPPORT + +# include "gc_gcj.h" + +/* Gcj-style allocation without locks is extremely tricky. The */ +/* fundamental issue is that we may end up marking a free list, which */ +/* has freelist links instead of "vtable" pointers. That is usually */ +/* OK, since the next object on the free list will be cleared, and */ +/* will thus be interpreted as containing a zero descriptor. That's */ +/* fine if the object has not yet been initialized. But there are */ +/* interesting potential races. */ +/* In the case of incremental collection, this seems hopeless, since */ +/* the marker may run asynchronously, and may pick up the pointer to */ +/* the next freelist entry (which it thinks is a vtable pointer), get */ +/* suspended for a while, and then see an allocated object instead */ +/* of the vtable. This may be avoidable with either a handshake with */ +/* the collector or, probably more easily, by moving the free list */ +/* links to the second word of each object. The latter isn't a */ +/* universal win, since on architecture like Itanium, nonzero offsets */ +/* are not necessarily free. And there may be cache fill order issues. */ +/* For now, we punt with incremental GC. This probably means that */ +/* incremental GC should be enabled before we fork a second thread. */ +/* Unlike the other thread local allocation calls, we assume that the */ +/* collector has been explicitly initialized. */ +GC_API GC_ATTR_MALLOC void * GC_CALL GC_gcj_malloc(size_t bytes, + void * ptr_to_struct_containing_descr) +{ + if (EXPECT(GC_incremental, FALSE)) { + return GC_core_gcj_malloc(bytes, ptr_to_struct_containing_descr); + } else { + size_t granules = ROUNDED_UP_GRANULES(bytes); + void *result; + void **tiny_fl; + + GC_ASSERT(GC_gcj_malloc_initialized); + tiny_fl = ((GC_tlfs)GC_getspecific(GC_thread_key))->gcj_freelists; + GC_FAST_MALLOC_GRANS(result, granules, tiny_fl, DIRECT_GRANULES, + GC_gcj_kind, + GC_core_gcj_malloc(bytes, + ptr_to_struct_containing_descr), + {AO_compiler_barrier(); + *(void **)result = ptr_to_struct_containing_descr;}); + /* This forces the initialization of the "method ptr". */ + /* This is necessary to ensure some very subtle properties */ + /* required if a GC is run in the middle of such an allocation. */ + /* Here we implicitly also assume atomicity for the free list. */ + /* and method pointer assignments. */ + /* We must update the freelist before we store the pointer. */ + /* Otherwise a GC at this point would see a corrupted */ + /* free list. */ + /* A real memory barrier is not needed, since the */ + /* action of stopping this thread will cause prior writes */ + /* to complete. */ + /* We assert that any concurrent marker will stop us. */ + /* Thus it is impossible for a mark procedure to see the */ + /* allocation of the next object, but to see this object */ + /* still containing a free list pointer. Otherwise the */ + /* marker, by misinterpreting the freelist link as a vtable */ + /* pointer, might find a random "mark descriptor" in the next */ + /* object. */ + return result; + } +} + +#endif /* GC_GCJ_SUPPORT */ + +/* The thread support layer must arrange to mark thread-local */ +/* free lists explicitly, since the link field is often */ +/* invisible to the marker. It knows how to find all threads; */ +/* we take care of an individual thread freelist structure. */ +GC_INNER void GC_mark_thread_local_fls_for(GC_tlfs p) +{ + ptr_t q; + int i, j; + + for (j = 0; j < TINY_FREELISTS; ++j) { + for (i = 0; i < THREAD_FREELISTS_KINDS; ++i) { + /* Load the pointer atomically as it might be updated */ + /* concurrently by GC_FAST_MALLOC_GRANS. */ + q = (ptr_t)AO_load((volatile AO_t *)&p->_freelists[i][j]); + if ((word)q > HBLKSIZE) + GC_set_fl_marks(q); + } +# ifdef GC_GCJ_SUPPORT + if (EXPECT(j > 0, TRUE)) { + q = (ptr_t)AO_load((volatile AO_t *)&p->gcj_freelists[j]); + if ((word)q > HBLKSIZE) + GC_set_fl_marks(q); + } +# endif + } +} + +#if defined(GC_ASSERTIONS) + /* Check that all thread-local free-lists in p are completely marked. */ + void GC_check_tls_for(GC_tlfs p) + { + int i, j; + + for (j = 1; j < TINY_FREELISTS; ++j) { + for (i = 0; i < THREAD_FREELISTS_KINDS; ++i) { + GC_check_fl_marks(&p->_freelists[i][j]); + } +# ifdef GC_GCJ_SUPPORT + GC_check_fl_marks(&p->gcj_freelists[j]); +# endif + } + } +#endif /* GC_ASSERTIONS */ + +#endif /* THREAD_LOCAL_ALLOC */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/tools/callprocs.sh b/unity-2019.4.24f1-mbe/external/bdwgc/tools/callprocs.sh new file mode 100644 index 000000000..a8793f0b7 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/tools/callprocs.sh @@ -0,0 +1,4 @@ +#!/bin/sh +GC_DEBUG=1 +export GC_DEBUG +$* 2>&1 | awk '{print "0x3e=c\""$0"\""};/^\t##PC##=/ {if ($2 != 0) {print $2"?i"}}' | adb $1 | sed "s/^ >/>/" diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/tools/if_mach.c b/unity-2019.4.24f1-mbe/external/bdwgc/tools/if_mach.c new file mode 100644 index 000000000..2ddad6f2e --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/tools/if_mach.c @@ -0,0 +1,32 @@ +/* Conditionally execute a command based on machine and OS from gcconfig.h */ + +# include "private/gc_priv.h" +# include +# include +# include + +#ifdef __cplusplus +# define EXECV_ARGV_T char** +#else + /* The 2nd argument of execvp() prototype may be either char**, or */ + /* char* const*, or const char* const*. */ +# define EXECV_ARGV_T void* +#endif + +int main(int argc, char **argv) +{ + if (argc < 4) goto Usage; + if (strcmp(MACH_TYPE, argv[1]) != 0) return(0); + if (strlen(OS_TYPE) > 0 && strlen(argv[2]) > 0 + && strcmp(OS_TYPE, argv[2]) != 0) return(0); + fprintf(stderr, "^^^^Starting command^^^^\n"); + fflush(stdout); + execvp(TRUSTED_STRING(argv[3]), (EXECV_ARGV_T)(argv + 3)); + perror("Couldn't execute"); + +Usage: + fprintf(stderr, "Usage: %s mach_type os_type command\n", argv[0]); + fprintf(stderr, "Currently mach_type = %s, os_type = %s\n", + MACH_TYPE, OS_TYPE); + return(1); +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/tools/if_not_there.c b/unity-2019.4.24f1-mbe/external/bdwgc/tools/if_not_there.c new file mode 100644 index 000000000..d388d04b5 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/tools/if_not_there.c @@ -0,0 +1,58 @@ +/* Conditionally execute the command argv[2] based if the file argv[1] */ +/* does not exist. If the command is omitted (and the file does not */ +/* exist) then just exit with a non-zero code. */ + +# include "private/gc_priv.h" +# include +# include +# include +#ifdef __DJGPP__ +#include +#endif /* __DJGPP__ */ + +#ifdef __cplusplus +# define EXECV_ARGV_T char** +#else +# define EXECV_ARGV_T void* /* see the comment in if_mach.c */ +#endif + +int main(int argc, char **argv) +{ + FILE * f; +#ifdef __DJGPP__ + DIR * d; +#endif /* __DJGPP__ */ + char *fname; + + if (argc < 2 || argc > 3) + goto Usage; + + fname = TRUSTED_STRING(argv[1]); + f = fopen(fname, "rb"); + if (f != NULL) { + fclose(f); + return(0); + } + f = fopen(fname, "r"); + if (f != NULL) { + fclose(f); + return(0); + } +#ifdef __DJGPP__ + if ((d = opendir(fname)) != 0) { + closedir(d); + return(0); + } +#endif + printf("^^^^Starting command^^^^\n"); + fflush(stdout); + if (argc == 2) + return(2); /* the file does not exist but no command is given */ + + execvp(TRUSTED_STRING(argv[2]), (EXECV_ARGV_T)(argv + 2)); + exit(1); + +Usage: + fprintf(stderr, "Usage: %s file_name [command]\n", argv[0]); + return(1); +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/tools/setjmp_t.c b/unity-2019.4.24f1-mbe/external/bdwgc/tools/setjmp_t.c new file mode 100644 index 000000000..bef71f21f --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/tools/setjmp_t.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* Check whether setjmp actually saves registers in jmp_buf. */ +/* If it doesn't, the generic mark_regs code won't work. */ +/* Compilers vary as to whether they will put x in a */ +/* (callee-save) register without -O. The code is */ +/* contrived such that any decent compiler should put x in */ +/* a callee-save register with -O. Thus it is */ +/* recommended that this be run optimized. (If the machine */ +/* has no callee-save registers, then the generic code is */ +/* safe, but this will not be noticed by this piece of */ +/* code.) This test appears to be far from perfect. */ +#include +#include +#include +#include "private/gc_priv.h" + +#ifdef OS2 +/* GETPAGESIZE() is set to getpagesize() by default, but that */ +/* doesn't really exist, and the collector doesn't need it. */ +#define INCL_DOSFILEMGR +#define INCL_DOSMISC +#define INCL_DOSERRORS +#include + +int getpagesize(void) +{ + ULONG result[1]; + + if (DosQuerySysInfo(QSV_PAGE_SIZE, QSV_PAGE_SIZE, + (void *)result, sizeof(ULONG)) != NO_ERROR) { + fprintf(stderr, "DosQuerySysInfo failed\n"); + result[0] = 4096; + } + return((int)(result[0])); +} +#elif defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32) +# include + int getpagesize(void) + { + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + return sysinfo.dwPageSize; + } +#endif + +struct a_s { + char a_a; + char * a_b; +} a; + +word nested_sp(void) +{ +# if GC_GNUC_PREREQ(4, 0) + return (word)__builtin_frame_address(0); +# else + volatile word sp; + sp = (word)(&sp); + return sp; +# endif +} + +/* To prevent nested_sp inlining. */ +word (*volatile nested_sp_fn)(void) = nested_sp; + +int g(int x); + +int main(void) +{ + volatile word sp; + unsigned ps = GETPAGESIZE(); + JMP_BUF b; + register int x = (int)strlen("a"); /* 1, slightly disguised */ + static volatile int y = 0; + + sp = (word)(&sp); + printf("This appears to be a %s running %s\n", MACH_TYPE, OS_TYPE); +# if defined(CPPCHECK) + (void)nested_sp(); /* to workaround a bug in cppcheck */ +# endif + if (nested_sp_fn() < sp) { + printf("Stack appears to grow down, which is the default.\n"); + printf("A good guess for STACKBOTTOM on this machine is 0x%lx.\n", + ((unsigned long)sp + ps) & ~(ps-1)); + } else { + printf("Stack appears to grow up.\n"); + printf("Define STACK_GROWS_UP in gc_private.h\n"); + printf("A good guess for STACKBOTTOM on this machine is 0x%lx.\n", + ((unsigned long)sp + ps) & ~(ps-1)); + } + printf("Note that this may vary between machines of ostensibly\n"); + printf("the same architecture (e.g. Sun 3/50s and 3/80s).\n"); + printf("On many machines the value is not fixed.\n"); + printf("A good guess for ALIGNMENT on this machine is %lu.\n", + (unsigned long)((word)(&(a.a_b)) - (word)(&a))); + + printf("The following is a very dubious test of one root marking" + " strategy.\n"); + printf("Results may not be accurate/useful:\n"); + /* Encourage the compiler to keep x in a callee-save register */ + x = 2*x-1; + printf("\n"); + x = 2*x-1; + (void)SETJMP(b); + if (y == 1) { + if (x == 2) { + printf("Setjmp-based generic mark_regs code probably won't work.\n"); + printf("But we rarely try that anymore. If you have getcontect()\n"); + printf("this probably doesn't matter.\n"); + } else if (x == 1) { + printf("Setjmp-based register marking code may work.\n"); + } else { + printf("Very strange setjmp implementation.\n"); + } + } + y++; + x = 2; + if (y == 1) LONGJMP(b, 1); + printf("Some GC internal configuration stuff: \n"); + printf("\tWORDSZ = %lu, ALIGNMENT = %d, GC_GRANULE_BYTES = %d\n", + (unsigned long)WORDSZ, ALIGNMENT, GC_GRANULE_BYTES); + printf("\tUsing one mark "); +# if defined(USE_MARK_BYTES) + printf("byte"); +# else + printf("bit"); +# endif + printf(" per "); +# if defined(MARK_BIT_PER_OBJ) + printf("object.\n"); +# elif defined(MARK_BIT_PER_GRANULE) + printf("granule.\n"); +# endif +# ifdef THREAD_LOCAL_ALLOC + printf("Thread local allocation enabled.\n"); +# endif +# ifdef PARALLEL_MARK + printf("Parallel marking enabled.\n"); +# endif + (void)g(x); + return(0); +} + +int g(int x) +{ + return(x); +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/tools/threadlibs.c b/unity-2019.4.24f1-mbe/external/bdwgc/tools/threadlibs.c new file mode 100644 index 000000000..832b3d134 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/tools/threadlibs.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2010 by Hewlett-Packard Development Company. + * All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +# include "private/gc_priv.h" + +# include + +int main(void) +{ +# if defined(GC_USE_LD_WRAP) + printf("-Wl,--wrap -Wl,dlopen " + "-Wl,--wrap -Wl,pthread_create -Wl,--wrap -Wl,pthread_join " + "-Wl,--wrap -Wl,pthread_detach -Wl,--wrap -Wl,pthread_sigmask " + "-Wl,--wrap -Wl,pthread_exit -Wl,--wrap -Wl,pthread_cancel\n"); +# endif +# if (defined(GC_LINUX_THREADS) && !defined(HOST_ANDROID)) \ + || defined(GC_IRIX_THREADS) || defined(GC_DARWIN_THREADS) \ + || defined(GC_AIX_THREADS) || (defined(HURD) && defined(GC_THREADS)) +# ifdef GC_USE_DLOPEN_WRAP + printf("-ldl "); +# endif + printf("-lpthread\n"); +# endif +# if defined(GC_OPENBSD_THREADS) + printf("-pthread\n"); +# endif +# if defined(GC_FREEBSD_THREADS) +# ifdef GC_USE_DLOPEN_WRAP + printf("-ldl "); +# endif +# if (__FREEBSD_version < 500000) + printf("-pthread\n"); +# else /* __FREEBSD__ || __DragonFly__ */ + printf("-lpthread\n"); +# endif +# endif +# if defined(GC_NETBSD_THREADS) + printf("-lpthread -lrt\n"); +# endif + +# if defined(GC_HPUX_THREADS) || defined(GC_OSF1_THREADS) + printf("-lpthread -lrt\n"); +# endif +# if defined(GC_SOLARIS_THREADS) + printf("-lthread -lposix4\n"); + /* Is this right for recent versions? */ +# endif +# if defined(GC_WIN32_THREADS) && defined(CYGWIN32) + printf("-lpthread\n"); +# endif +# if defined(GC_WIN32_PTHREADS) +# ifdef PTW32_STATIC_LIB + /* assume suffix s for static version of the win32 pthread library */ + printf("-lpthreadGC2s -lws2_32\n"); +# else + printf("-lpthreadGC2\n"); +# endif +# endif +# if defined(GC_OSF1_THREADS) + printf("-pthread -lrt\n"); /* DOB: must be -pthread, not -lpthread */ +# endif + /* You need GCC 3.0.3 to build this one! */ + /* DG/UX native gcc doesn't know what "-pthread" is */ +# if defined(GC_DGUX386_THREADS) + printf("-ldl -pthread\n"); +# endif + return 0; +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/typd_mlc.c b/unity-2019.4.24f1-mbe/external/bdwgc/typd_mlc.c new file mode 100644 index 000000000..823447b03 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/typd_mlc.c @@ -0,0 +1,714 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1999-2000 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + */ + +#include "private/gc_pmark.h" +#include "gc_inline.h" /* for GC_malloc_kind */ + +/* + * Some simple primitives for allocation with explicit type information. + * Simple objects are allocated such that they contain a GC_descr at the + * end (in the last allocated word). This descriptor may be a procedure + * which then examines an extended descriptor passed as its environment. + * + * Arrays are treated as simple objects if they have sufficiently simple + * structure. Otherwise they are allocated from an array kind that supplies + * a special mark procedure. These arrays contain a pointer to a + * complex_descriptor as their last word. + * This is done because the environment field is too small, and the collector + * must trace the complex_descriptor. + * + * Note that descriptors inside objects may appear cleared, if we encounter a + * false reference to an object on a free list. In the GC_descr case, this + * is OK, since a 0 descriptor corresponds to examining no fields. + * In the complex_descriptor case, we explicitly check for that case. + * + * MAJOR PARTS OF THIS CODE HAVE NOT BEEN TESTED AT ALL and are not testable, + * since they are not accessible through the current interface. + */ + +#include "gc_typed.h" + +#define TYPD_EXTRA_BYTES (sizeof(word) - EXTRA_BYTES) + +STATIC int GC_explicit_kind = 0; + /* Object kind for objects with indirect */ + /* (possibly extended) descriptors. */ + +STATIC int GC_array_kind = 0; + /* Object kind for objects with complex */ + /* descriptors and GC_array_mark_proc. */ + +/* Extended descriptors. GC_typed_mark_proc understands these. */ +/* These are used for simple objects that are larger than what */ +/* can be described by a BITMAP_BITS sized bitmap. */ +typedef struct { + word ed_bitmap; /* lsb corresponds to first word. */ + GC_bool ed_continued; /* next entry is continuation. */ +} ext_descr; + +/* Array descriptors. GC_array_mark_proc understands these. */ +/* We may eventually need to add provisions for headers and */ +/* trailers. Hence we provide for tree structured descriptors, */ +/* though we don't really use them currently. */ + + struct LeafDescriptor { /* Describes simple array */ + word ld_tag; +# define LEAF_TAG 1 + size_t ld_size; /* bytes per element */ + /* multiple of ALIGNMENT. */ + size_t ld_nelements; /* Number of elements. */ + GC_descr ld_descriptor; /* A simple length, bitmap, */ + /* or procedure descriptor. */ + } ld; + + struct ComplexArrayDescriptor { + word ad_tag; +# define ARRAY_TAG 2 + size_t ad_nelements; + union ComplexDescriptor * ad_element_descr; + } ad; + + struct SequenceDescriptor { + word sd_tag; +# define SEQUENCE_TAG 3 + union ComplexDescriptor * sd_first; + union ComplexDescriptor * sd_second; + } sd; + +typedef union ComplexDescriptor { + struct LeafDescriptor ld; + struct ComplexArrayDescriptor ad; + struct SequenceDescriptor sd; +} complex_descriptor; +#define TAG ad.ad_tag + +STATIC ext_descr * GC_ext_descriptors = NULL; + /* Points to array of extended */ + /* descriptors. */ + +STATIC size_t GC_ed_size = 0; /* Current size of above arrays. */ +#define ED_INITIAL_SIZE 100 + +STATIC size_t GC_avail_descr = 0; /* Next available slot. */ + +STATIC int GC_typed_mark_proc_index = 0; /* Indices of my mark */ +STATIC int GC_array_mark_proc_index = 0; /* procedures. */ + +#ifdef AO_HAVE_load_acquire + STATIC volatile AO_t GC_explicit_typing_initialized = FALSE; +#else + STATIC GC_bool GC_explicit_typing_initialized = FALSE; +#endif + +STATIC void GC_push_typed_structures_proc(void) +{ + GC_PUSH_ALL_SYM(GC_ext_descriptors); +} + +/* Add a multiword bitmap to GC_ext_descriptors arrays. Return */ +/* starting index. */ +/* Returns -1 on failure. */ +/* Caller does not hold allocation lock. */ +STATIC signed_word GC_add_ext_descriptor(const word * bm, word nbits) +{ + size_t nwords = divWORDSZ(nbits + WORDSZ-1); + signed_word result; + size_t i; + word last_part; + size_t extra_bits; + DCL_LOCK_STATE; + + LOCK(); + while (GC_avail_descr + nwords >= GC_ed_size) { + ext_descr * newExtD; + size_t new_size; + word ed_size = GC_ed_size; + + if (ed_size == 0) { + GC_ASSERT((word)(&GC_ext_descriptors) % sizeof(word) == 0); + GC_push_typed_structures = GC_push_typed_structures_proc; + UNLOCK(); + new_size = ED_INITIAL_SIZE; + } else { + UNLOCK(); + new_size = 2 * ed_size; + if (new_size > MAX_ENV) return(-1); + } + newExtD = (ext_descr *)GC_malloc_atomic(new_size * sizeof(ext_descr)); + if (NULL == newExtD) + return -1; + LOCK(); + if (ed_size == GC_ed_size) { + if (GC_avail_descr != 0) { + BCOPY(GC_ext_descriptors, newExtD, + GC_avail_descr * sizeof(ext_descr)); + } + GC_ed_size = new_size; + GC_ext_descriptors = newExtD; + } /* else another thread already resized it in the meantime */ + } + result = GC_avail_descr; + for (i = 0; i < nwords-1; i++) { + GC_ext_descriptors[result + i].ed_bitmap = bm[i]; + GC_ext_descriptors[result + i].ed_continued = TRUE; + } + last_part = bm[i]; + /* Clear irrelevant bits. */ + extra_bits = nwords * WORDSZ - nbits; + last_part <<= extra_bits; + last_part >>= extra_bits; + GC_ext_descriptors[result + i].ed_bitmap = last_part; + GC_ext_descriptors[result + i].ed_continued = FALSE; + GC_avail_descr += nwords; + UNLOCK(); + return(result); +} + +/* Table of bitmap descriptors for n word long all pointer objects. */ +STATIC GC_descr GC_bm_table[WORDSZ/2]; + +/* Return a descriptor for the concatenation of 2 nwords long objects, */ +/* each of which is described by descriptor. */ +/* The result is known to be short enough to fit into a bitmap */ +/* descriptor. */ +/* Descriptor is a GC_DS_LENGTH or GC_DS_BITMAP descriptor. */ +STATIC GC_descr GC_double_descr(GC_descr descriptor, word nwords) +{ + if ((descriptor & GC_DS_TAGS) == GC_DS_LENGTH) { + descriptor = GC_bm_table[BYTES_TO_WORDS((word)descriptor)]; + }; + descriptor |= (descriptor & ~GC_DS_TAGS) >> nwords; + return(descriptor); +} + +STATIC complex_descriptor * +GC_make_sequence_descriptor(complex_descriptor *first, + complex_descriptor *second); + +/* Build a descriptor for an array with nelements elements, */ +/* each of which can be described by a simple descriptor. */ +/* We try to optimize some common cases. */ +/* If the result is COMPLEX, then a complex_descr* is returned */ +/* in *complex_d. */ +/* If the result is LEAF, then we built a LeafDescriptor in */ +/* the structure pointed to by leaf. */ +/* The tag in the leaf structure is not set. */ +/* If the result is SIMPLE, then a GC_descr */ +/* is returned in *simple_d. */ +/* If the result is NO_MEM, then */ +/* we failed to allocate the descriptor. */ +/* The implementation knows that GC_DS_LENGTH is 0. */ +/* *leaf, *complex_d, and *simple_d may be used as temporaries */ +/* during the construction. */ +#define COMPLEX 2 +#define LEAF 1 +#define SIMPLE 0 +#define NO_MEM (-1) +STATIC int GC_make_array_descriptor(size_t nelements, size_t size, + GC_descr descriptor, GC_descr *simple_d, + complex_descriptor **complex_d, + struct LeafDescriptor * leaf) +{ +# define OPT_THRESHOLD 50 + /* For larger arrays, we try to combine descriptors of adjacent */ + /* descriptors to speed up marking, and to reduce the amount */ + /* of space needed on the mark stack. */ + if ((descriptor & GC_DS_TAGS) == GC_DS_LENGTH) { + if (descriptor == (GC_descr)size) { + *simple_d = nelements * descriptor; + return(SIMPLE); + } else if ((word)descriptor == 0) { + *simple_d = (GC_descr)0; + return(SIMPLE); + } + } + if (nelements <= OPT_THRESHOLD) { + if (nelements <= 1) { + if (nelements == 1) { + *simple_d = descriptor; + return(SIMPLE); + } else { + *simple_d = (GC_descr)0; + return(SIMPLE); + } + } + } else if (size <= BITMAP_BITS/2 + && (descriptor & GC_DS_TAGS) != GC_DS_PROC + && (size & (sizeof(word)-1)) == 0) { + int result = + GC_make_array_descriptor(nelements/2, 2*size, + GC_double_descr(descriptor, + BYTES_TO_WORDS(size)), + simple_d, complex_d, leaf); + if ((nelements & 1) == 0) { + return(result); + } else { + struct LeafDescriptor * one_element = + (struct LeafDescriptor *) + GC_malloc_atomic(sizeof(struct LeafDescriptor)); + + if (result == NO_MEM || one_element == 0) return(NO_MEM); + one_element -> ld_tag = LEAF_TAG; + one_element -> ld_size = size; + one_element -> ld_nelements = 1; + one_element -> ld_descriptor = descriptor; + switch(result) { + case SIMPLE: + { + struct LeafDescriptor * beginning = + (struct LeafDescriptor *) + GC_malloc_atomic(sizeof(struct LeafDescriptor)); + if (beginning == 0) return(NO_MEM); + beginning -> ld_tag = LEAF_TAG; + beginning -> ld_size = size; + beginning -> ld_nelements = 1; + beginning -> ld_descriptor = *simple_d; + *complex_d = GC_make_sequence_descriptor( + (complex_descriptor *)beginning, + (complex_descriptor *)one_element); + break; + } + case LEAF: + { + struct LeafDescriptor * beginning = + (struct LeafDescriptor *) + GC_malloc_atomic(sizeof(struct LeafDescriptor)); + if (beginning == 0) return(NO_MEM); + beginning -> ld_tag = LEAF_TAG; + beginning -> ld_size = leaf -> ld_size; + beginning -> ld_nelements = leaf -> ld_nelements; + beginning -> ld_descriptor = leaf -> ld_descriptor; + *complex_d = GC_make_sequence_descriptor( + (complex_descriptor *)beginning, + (complex_descriptor *)one_element); + break; + } + case COMPLEX: + *complex_d = GC_make_sequence_descriptor( + *complex_d, + (complex_descriptor *)one_element); + break; + } + return(COMPLEX); + } + } + + leaf -> ld_size = size; + leaf -> ld_nelements = nelements; + leaf -> ld_descriptor = descriptor; + return(LEAF); +} + +STATIC complex_descriptor * +GC_make_sequence_descriptor(complex_descriptor *first, + complex_descriptor *second) +{ + struct SequenceDescriptor * result = + (struct SequenceDescriptor *) + GC_malloc(sizeof(struct SequenceDescriptor)); + /* Can't result in overly conservative marking, since tags are */ + /* very small integers. Probably faster than maintaining type */ + /* info. */ + if (result != 0) { + result -> sd_tag = SEQUENCE_TAG; + result -> sd_first = first; + result -> sd_second = second; + GC_dirty(result); + } + return((complex_descriptor *)result); +} + +STATIC ptr_t * GC_eobjfreelist = NULL; + +STATIC mse * GC_typed_mark_proc(word * addr, mse * mark_stack_ptr, + mse * mark_stack_limit, word env); + +STATIC mse * GC_array_mark_proc(word * addr, mse * mark_stack_ptr, + mse * mark_stack_limit, word env); + +STATIC void GC_init_explicit_typing(void) +{ + unsigned i; + + GC_STATIC_ASSERT(sizeof(struct LeafDescriptor) % sizeof(word) == 0); + /* Set up object kind with simple indirect descriptor. */ + GC_eobjfreelist = (ptr_t *)GC_new_free_list_inner(); + GC_explicit_kind = GC_new_kind_inner( + (void **)GC_eobjfreelist, + (WORDS_TO_BYTES((word)-1) | GC_DS_PER_OBJECT), + TRUE, TRUE); + /* Descriptors are in the last word of the object. */ + GC_typed_mark_proc_index = GC_new_proc_inner(GC_typed_mark_proc); + /* Set up object kind with array descriptor. */ + GC_array_mark_proc_index = GC_new_proc_inner(GC_array_mark_proc); + GC_array_kind = GC_new_kind_inner(GC_new_free_list_inner(), + GC_MAKE_PROC(GC_array_mark_proc_index, 0), + FALSE, TRUE); + GC_bm_table[0] = GC_DS_BITMAP; + for (i = 1; i < WORDSZ/2; i++) { + GC_bm_table[i] = (((word)-1) << (WORDSZ - i)) | GC_DS_BITMAP; + } +} + +STATIC mse * GC_typed_mark_proc(word * addr, mse * mark_stack_ptr, + mse * mark_stack_limit, word env) +{ + word bm = GC_ext_descriptors[env].ed_bitmap; + word * current_p = addr; + word current; + ptr_t greatest_ha = (ptr_t)GC_greatest_plausible_heap_addr; + ptr_t least_ha = (ptr_t)GC_least_plausible_heap_addr; + DECLARE_HDR_CACHE; + + INIT_HDR_CACHE; + for (; bm != 0; bm >>= 1, current_p++) { + if (bm & 1) { + current = *current_p; + FIXUP_POINTER(current); + if (current >= (word)least_ha && current <= (word)greatest_ha) { + PUSH_CONTENTS((ptr_t)current, mark_stack_ptr, + mark_stack_limit, (ptr_t)current_p); + } + } + } + if (GC_ext_descriptors[env].ed_continued) { + /* Push an entry with the rest of the descriptor back onto the */ + /* stack. Thus we never do too much work at once. Note that */ + /* we also can't overflow the mark stack unless we actually */ + /* mark something. */ + mark_stack_ptr++; + if ((word)mark_stack_ptr >= (word)mark_stack_limit) { + mark_stack_ptr = GC_signal_mark_stack_overflow(mark_stack_ptr); + } + mark_stack_ptr -> mse_start = (ptr_t)(addr + WORDSZ); + mark_stack_ptr -> mse_descr.w = + GC_MAKE_PROC(GC_typed_mark_proc_index, env + 1); + } + return(mark_stack_ptr); +} + +/* Return the size of the object described by d. It would be faster to */ +/* store this directly, or to compute it as part of */ +/* GC_push_complex_descriptor, but hopefully it doesn't matter. */ +STATIC word GC_descr_obj_size(complex_descriptor *d) +{ + switch(d -> TAG) { + case LEAF_TAG: + return(d -> ld.ld_nelements * d -> ld.ld_size); + case ARRAY_TAG: + return(d -> ad.ad_nelements + * GC_descr_obj_size(d -> ad.ad_element_descr)); + case SEQUENCE_TAG: + return(GC_descr_obj_size(d -> sd.sd_first) + + GC_descr_obj_size(d -> sd.sd_second)); + default: + ABORT_RET("Bad complex descriptor"); + return 0; + } +} + +/* Push descriptors for the object at addr with complex descriptor d */ +/* onto the mark stack. Return 0 if the mark stack overflowed. */ +STATIC mse * GC_push_complex_descriptor(word *addr, complex_descriptor *d, + mse *msp, mse *msl) +{ + ptr_t current = (ptr_t)addr; + word nelements; + word sz; + word i; + + switch(d -> TAG) { + case LEAF_TAG: + { + GC_descr descr = d -> ld.ld_descriptor; + + nelements = d -> ld.ld_nelements; + if (msl - msp <= (ptrdiff_t)nelements) return(0); + sz = d -> ld.ld_size; + for (i = 0; i < nelements; i++) { + msp++; + msp -> mse_start = current; + msp -> mse_descr.w = descr; + current += sz; + } + return(msp); + } + case ARRAY_TAG: + { + complex_descriptor *descr = d -> ad.ad_element_descr; + + nelements = d -> ad.ad_nelements; + sz = GC_descr_obj_size(descr); + for (i = 0; i < nelements; i++) { + msp = GC_push_complex_descriptor((word *)current, descr, + msp, msl); + if (msp == 0) return(0); + current += sz; + } + return(msp); + } + case SEQUENCE_TAG: + { + sz = GC_descr_obj_size(d -> sd.sd_first); + msp = GC_push_complex_descriptor((word *)current, d -> sd.sd_first, + msp, msl); + if (msp == 0) return(0); + current += sz; + msp = GC_push_complex_descriptor((word *)current, d -> sd.sd_second, + msp, msl); + return(msp); + } + default: + ABORT_RET("Bad complex descriptor"); + return 0; + } +} + +STATIC mse * GC_array_mark_proc(word * addr, mse * mark_stack_ptr, + mse * mark_stack_limit, + word env GC_ATTR_UNUSED) +{ + hdr * hhdr = HDR(addr); + word sz = hhdr -> hb_sz; + word nwords = BYTES_TO_WORDS(sz); + complex_descriptor * descr = (complex_descriptor *)(addr[nwords-1]); + mse * orig_mark_stack_ptr = mark_stack_ptr; + mse * new_mark_stack_ptr; + + if (descr == 0) { + /* Found a reference to a free list entry. Ignore it. */ + return(orig_mark_stack_ptr); + } + /* In use counts were already updated when array descriptor was */ + /* pushed. Here we only replace it by subobject descriptors, so */ + /* no update is necessary. */ + new_mark_stack_ptr = GC_push_complex_descriptor(addr, descr, + mark_stack_ptr, + mark_stack_limit-1); + if (new_mark_stack_ptr == 0) { + /* Explicitly instruct Clang Static Analyzer that ptr is non-null. */ + if (NULL == mark_stack_ptr) ABORT("Bad mark_stack_ptr"); + + /* Doesn't fit. Conservatively push the whole array as a unit */ + /* and request a mark stack expansion. */ + /* This cannot cause a mark stack overflow, since it replaces */ + /* the original array entry. */ +# ifdef PARALLEL_MARK + /* We might be using a local_mark_stack in parallel mode. */ + if (GC_mark_stack + GC_mark_stack_size == mark_stack_limit) +# endif + { + GC_mark_stack_too_small = TRUE; + } + new_mark_stack_ptr = orig_mark_stack_ptr + 1; + new_mark_stack_ptr -> mse_start = (ptr_t)addr; + new_mark_stack_ptr -> mse_descr.w = sz | GC_DS_LENGTH; + } else { + /* Push descriptor itself */ + new_mark_stack_ptr++; + new_mark_stack_ptr -> mse_start = (ptr_t)(addr + nwords - 1); + new_mark_stack_ptr -> mse_descr.w = sizeof(word) | GC_DS_LENGTH; + } + return new_mark_stack_ptr; +} + +GC_API GC_descr GC_CALL GC_make_descriptor(const GC_word * bm, size_t len) +{ + signed_word last_set_bit = len - 1; + GC_descr result; + DCL_LOCK_STATE; + +# if defined(AO_HAVE_load_acquire) && defined(AO_HAVE_store_release) + if (!EXPECT(AO_load_acquire(&GC_explicit_typing_initialized), TRUE)) { + LOCK(); + if (!GC_explicit_typing_initialized) { + GC_init_explicit_typing(); + AO_store_release(&GC_explicit_typing_initialized, TRUE); + } + UNLOCK(); + } +# else + LOCK(); + if (!EXPECT(GC_explicit_typing_initialized, TRUE)) { + GC_init_explicit_typing(); + GC_explicit_typing_initialized = TRUE; + } + UNLOCK(); +# endif + + while (last_set_bit >= 0 && !GC_get_bit(bm, last_set_bit)) + last_set_bit--; + if (last_set_bit < 0) return(0 /* no pointers */); + +# if ALIGNMENT == CPP_WORDSZ/8 + { + signed_word i; + + for (i = 0; i < last_set_bit; i++) { + if (!GC_get_bit(bm, i)) { + break; + } + } + if (i == last_set_bit) { + /* An initial section contains all pointers. Use length descriptor. */ + return (WORDS_TO_BYTES(last_set_bit+1) | GC_DS_LENGTH); + } + } +# endif + if ((word)last_set_bit < BITMAP_BITS) { + signed_word i; + + /* Hopefully the common case. */ + /* Build bitmap descriptor (with bits reversed) */ + result = SIGNB; + for (i = last_set_bit - 1; i >= 0; i--) { + result >>= 1; + if (GC_get_bit(bm, i)) result |= SIGNB; + } + result |= GC_DS_BITMAP; + } else { + signed_word index = GC_add_ext_descriptor(bm, (word)last_set_bit + 1); + if (index == -1) return(WORDS_TO_BYTES(last_set_bit+1) | GC_DS_LENGTH); + /* Out of memory: use conservative */ + /* approximation. */ + result = GC_MAKE_PROC(GC_typed_mark_proc_index, (word)index); + } + return result; +} + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_explicitly_typed(size_t lb, + GC_descr d) +{ + word *op; + size_t lg; + + GC_ASSERT(GC_explicit_typing_initialized); + lb = SIZET_SAT_ADD(lb, TYPD_EXTRA_BYTES); + op = (word *)GC_malloc_kind(lb, GC_explicit_kind); + if (EXPECT(NULL == op, FALSE)) + return NULL; + /* It is not safe to use GC_size_map[lb] to compute lg here as the */ + /* the former might be updated asynchronously. */ + lg = BYTES_TO_GRANULES(GC_size(op)); + op[GRANULES_TO_WORDS(lg) - 1] = d; + GC_dirty(op + GRANULES_TO_WORDS(lg) - 1); + return op; +} + +/* We make the GC_clear_stack() call a tail one, hoping to get more of */ +/* the stack. */ +#define GENERAL_MALLOC_IOP(lb, k) \ + GC_clear_stack(GC_generic_malloc_ignore_off_page(lb, k)) + +GC_API GC_ATTR_MALLOC void * GC_CALL + GC_malloc_explicitly_typed_ignore_off_page(size_t lb, GC_descr d) +{ + ptr_t op; + size_t lg; + DCL_LOCK_STATE; + + GC_ASSERT(GC_explicit_typing_initialized); + lb = SIZET_SAT_ADD(lb, TYPD_EXTRA_BYTES); + if (SMALL_OBJ(lb)) { + GC_DBG_COLLECT_AT_MALLOC(lb); + LOCK(); + lg = GC_size_map[lb]; + op = GC_eobjfreelist[lg]; + if (EXPECT(0 == op, FALSE)) { + UNLOCK(); + op = (ptr_t)GENERAL_MALLOC_IOP(lb, GC_explicit_kind); + if (0 == op) return 0; + /* See the comment in GC_malloc_explicitly_typed. */ + lg = BYTES_TO_GRANULES(GC_size(op)); + } else { + GC_eobjfreelist[lg] = (ptr_t)obj_link(op); + obj_link(op) = 0; + GC_bytes_allocd += GRANULES_TO_BYTES((word)lg); + UNLOCK(); + } + } else { + op = (ptr_t)GENERAL_MALLOC_IOP(lb, GC_explicit_kind); + if (NULL == op) return NULL; + lg = BYTES_TO_GRANULES(GC_size(op)); + } + ((word *)op)[GRANULES_TO_WORDS(lg) - 1] = d; + GC_dirty(op + GRANULES_TO_WORDS(lg) - 1); + return op; +} + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_calloc_explicitly_typed(size_t n, + size_t lb, GC_descr d) +{ + word *op; + size_t lg; + GC_descr simple_descr; + complex_descriptor *complex_descr; + int descr_type; + struct LeafDescriptor leaf; + + GC_ASSERT(GC_explicit_typing_initialized); + descr_type = GC_make_array_descriptor((word)n, (word)lb, d, &simple_descr, + &complex_descr, &leaf); + if ((lb | n) > GC_SQRT_SIZE_MAX /* fast initial check */ + && lb > 0 && n > GC_SIZE_MAX / lb) + return (*GC_get_oom_fn())(GC_SIZE_MAX); /* n*lb overflow */ + lb *= n; + switch(descr_type) { + case NO_MEM: return(0); + case SIMPLE: + return GC_malloc_explicitly_typed(lb, simple_descr); + case LEAF: + lb = SIZET_SAT_ADD(lb, + sizeof(struct LeafDescriptor) + TYPD_EXTRA_BYTES); + break; + case COMPLEX: + lb = SIZET_SAT_ADD(lb, TYPD_EXTRA_BYTES); + break; + } + op = (word *)GC_malloc_kind(lb, GC_array_kind); + if (EXPECT(NULL == op, FALSE)) + return NULL; + lg = BYTES_TO_GRANULES(GC_size(op)); + if (descr_type == LEAF) { + /* Set up the descriptor inside the object itself. */ + volatile struct LeafDescriptor * lp = + (struct LeafDescriptor *) + (op + GRANULES_TO_WORDS(lg) + - (BYTES_TO_WORDS(sizeof(struct LeafDescriptor)) + 1)); + + lp -> ld_tag = LEAF_TAG; + lp -> ld_size = leaf.ld_size; + lp -> ld_nelements = leaf.ld_nelements; + lp -> ld_descriptor = leaf.ld_descriptor; + ((volatile word *)op)[GRANULES_TO_WORDS(lg) - 1] = (word)lp; + } else { +# ifndef GC_NO_FINALIZATION + size_t lw = GRANULES_TO_WORDS(lg); + + op[lw - 1] = (word)complex_descr; + GC_dirty(op + lw - 1); + /* Make sure the descriptor is cleared once there is any danger */ + /* it may have been collected. */ + if (EXPECT(GC_general_register_disappearing_link( + (void **)(op + lw - 1), op) + == GC_NO_MEMORY, FALSE)) +# endif + { + /* Couldn't register it due to lack of memory. Punt. */ + return (*GC_get_oom_fn())(lb); + } + } + return op; +} diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/win32_threads.c b/unity-2019.4.24f1-mbe/external/bdwgc/win32_threads.c new file mode 100644 index 000000000..252335cd4 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/win32_threads.c @@ -0,0 +1,2966 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2008 by Hewlett-Packard Development Company. + * All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#include "private/gc_priv.h" + +#if defined(GC_WIN32_THREADS) + +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +#endif +#define NOSERVICE +#include + +#ifdef THREAD_LOCAL_ALLOC +# include "private/thread_local_alloc.h" +#endif /* THREAD_LOCAL_ALLOC */ + +/* Allocation lock declarations. */ +#if !defined(USE_PTHREAD_LOCKS) + GC_INNER CRITICAL_SECTION GC_allocate_ml; +# ifdef GC_ASSERTIONS + GC_INNER DWORD GC_lock_holder = NO_THREAD; + /* Thread id for current holder of allocation lock */ +# endif +#else + GC_INNER pthread_mutex_t GC_allocate_ml = PTHREAD_MUTEX_INITIALIZER; +# ifdef GC_ASSERTIONS + GC_INNER unsigned long GC_lock_holder = NO_THREAD; +# endif +#endif + +#undef CreateThread +#undef ExitThread +#undef _beginthreadex +#undef _endthreadex + +#ifdef GC_PTHREADS +# include /* for EAGAIN */ + + /* Cygwin-specific forward decls */ +# undef pthread_create +# undef pthread_join +# undef pthread_detach + +# ifndef GC_NO_PTHREAD_SIGMASK +# undef pthread_sigmask +# endif + + STATIC void * GC_pthread_start(void * arg); + STATIC void GC_thread_exit_proc(void *arg); + +# include +# ifdef CAN_CALL_ATFORK +# include +# endif + +#elif !defined(MSWINCE) +# include /* For _beginthreadex, _endthreadex */ +# include /* for errno, EAGAIN */ + +#endif /* !GC_PTHREADS && !MSWINCE */ + +/* DllMain-based thread registration is currently incompatible */ +/* with thread-local allocation, pthreads and WinCE. */ +#if (defined(GC_DLL) || defined(GC_INSIDE_DLL)) \ + && !defined(GC_NO_THREADS_DISCOVERY) && !defined(MSWINCE) \ + && !defined(THREAD_LOCAL_ALLOC) && !defined(GC_PTHREADS) + + /* This code operates in two distinct modes, depending on */ + /* the setting of GC_win32_dll_threads. */ + /* If GC_win32_dll_threads is set, all threads in the process */ + /* are implicitly registered with the GC by DllMain. */ + /* No explicit registration is required, and attempts at */ + /* explicit registration are ignored. This mode is */ + /* very different from the Posix operation of the collector. */ + /* In this mode access to the thread table is lock-free. */ + /* Hence there is a static limit on the number of threads. */ + +# ifdef GC_DISCOVER_TASK_THREADS + /* GC_DISCOVER_TASK_THREADS should be used if DllMain-based */ + /* thread registration is required but it is impossible to */ + /* call GC_use_threads_discovery before other GC routines. */ +# define GC_win32_dll_threads TRUE +# else + STATIC GC_bool GC_win32_dll_threads = FALSE; + /* GC_win32_dll_threads must be set (if needed) at the */ + /* application initialization time, i.e. before any */ + /* collector or thread calls. We make it a "dynamic" */ + /* option only to avoid multiple library versions. */ +# endif + +#else + /* If GC_win32_dll_threads is FALSE (or the collector is */ + /* built without GC_DLL defined), things operate in a way */ + /* that is very similar to Posix platforms, and new threads */ + /* must be registered with the collector, e.g. by using */ + /* preprocessor-based interception of the thread primitives. */ + /* In this case, we use a real data structure for the thread */ + /* table. Note that there is no equivalent of linker-based */ + /* call interception, since we don't have ELF-like */ + /* facilities. The Windows analog appears to be "API */ + /* hooking", which really seems to be a standard way to */ + /* do minor binary rewriting (?). I'd prefer not to have */ + /* the basic collector rely on such facilities, but an */ + /* optional package that intercepts thread calls this way */ + /* would probably be nice. */ +# ifndef GC_NO_THREADS_DISCOVERY +# define GC_NO_THREADS_DISCOVERY +# endif +# define GC_win32_dll_threads FALSE +# undef MAX_THREADS +# define MAX_THREADS 1 /* dll_thread_table[] is always empty. */ +#endif /* GC_NO_THREADS_DISCOVERY */ + +/* We have two versions of the thread table. Which one */ +/* we us depends on whether or not GC_win32_dll_threads */ +/* is set. Note that before initialization, we don't */ +/* add any entries to either table, even if DllMain is */ +/* called. The main thread will be added on */ +/* initialization. */ + +/* The type of the first argument to InterlockedExchange. */ +/* Documented to be LONG volatile *, but at least gcc likes */ +/* this better. */ +typedef LONG * IE_t; + +STATIC GC_bool GC_thr_initialized = FALSE; + +#ifndef GC_ALWAYS_MULTITHREADED + GC_INNER GC_bool GC_need_to_lock = FALSE; +#endif + +static GC_bool parallel_initialized = FALSE; + +/* GC_use_threads_discovery() is currently incompatible with pthreads */ +/* and WinCE. It might be possible to get DllMain-based thread */ +/* registration to work with Cygwin, but if you try it then you are on */ +/* your own. */ +GC_API void GC_CALL GC_use_threads_discovery(void) +{ +# ifdef GC_NO_THREADS_DISCOVERY + ABORT("GC DllMain-based thread registration unsupported"); +# else + /* Turn on GC_win32_dll_threads. */ + GC_ASSERT(!parallel_initialized); + /* Note that GC_use_threads_discovery is expected to be called by */ + /* the client application (not from DllMain) at start-up. */ +# ifndef GC_DISCOVER_TASK_THREADS + GC_win32_dll_threads = TRUE; +# endif + GC_init_parallel(); +# endif +} + +STATIC DWORD GC_main_thread = 0; + +#define ADDR_LIMIT ((ptr_t)(word)-1) + +struct GC_Thread_Rep { + union { +# ifndef GC_NO_THREADS_DISCOVERY + volatile AO_t in_use; + /* Updated without lock. */ + /* We assert that unused */ + /* entries have invalid ids of */ + /* zero and zero stack fields. */ + /* Used only with GC_win32_dll_threads. */ +# endif + struct GC_Thread_Rep * next; + /* Hash table link without */ + /* GC_win32_dll_threads. */ + /* More recently allocated threads */ + /* with a given pthread id come */ + /* first. (All but the first are */ + /* guaranteed to be dead, but we may */ + /* not yet have registered the join.) */ + } tm; /* table_management */ + DWORD id; + +# ifdef MSWINCE + /* According to MSDN specs for WinCE targets: */ + /* - DuplicateHandle() is not applicable to thread handles; and */ + /* - the value returned by GetCurrentThreadId() could be used as */ + /* a "real" thread handle (for SuspendThread(), ResumeThread() and */ + /* GetThreadContext()). */ +# define THREAD_HANDLE(t) (HANDLE)(word)(t)->id +# else + HANDLE handle; +# define THREAD_HANDLE(t) (t)->handle +# endif + + ptr_t stack_base; /* The cold end of the stack. */ + /* 0 ==> entry not valid. */ + /* !in_use ==> stack_base == 0 */ + ptr_t last_stack_min; /* Last known minimum (hottest) address */ + /* in stack or ADDR_LIMIT if unset */ +# ifdef IA64 + ptr_t backing_store_end; + ptr_t backing_store_ptr; +# endif + + ptr_t thread_blocked_sp; /* Protected by GC lock. */ + /* NULL value means thread unblocked. */ + /* If set to non-NULL, thread will */ + /* acquire GC lock before doing any */ + /* pointer manipulations. Thus it does */ + /* not need to stop this thread. */ + + struct GC_traced_stack_sect_s *traced_stack_sect; + /* Points to the "stack section" data */ + /* held in stack by the innermost */ + /* GC_call_with_gc_active() of this */ + /* thread. May be NULL. */ + + unsigned short finalizer_skipped; + unsigned char finalizer_nested; + /* Used by GC_check_finalizer_nested() */ + /* to minimize the level of recursion */ + /* when a client finalizer allocates */ + /* memory (initially both are 0). */ + + unsigned char suspended; /* really of GC_bool type */ + CONTEXT saved_context; /* populated as part of GC_suspend as */ + /* resume/suspend loop may be needed for call */ + /* to GetThreadContext to succeed */ + +# ifdef GC_PTHREADS + unsigned char flags; /* Protected by GC lock. */ +# define FINISHED 1 /* Thread has exited. */ +# define DETACHED 2 /* Thread is intended to be detached. */ +# define KNOWN_FINISHED(t) (((t) -> flags) & FINISHED) + pthread_t pthread_id; + void *status; /* hold exit value until join in case it's a pointer */ +# else +# define KNOWN_FINISHED(t) 0 +# endif + +# ifdef THREAD_LOCAL_ALLOC + struct thread_local_freelists tlfs; +# endif +}; + +typedef struct GC_Thread_Rep * GC_thread; +typedef volatile struct GC_Thread_Rep * GC_vthread; + +#ifndef GC_NO_THREADS_DISCOVERY + /* We assumed that volatile ==> memory ordering, at least among */ + /* volatiles. This code should consistently use atomic_ops. */ + STATIC volatile GC_bool GC_please_stop = FALSE; +#elif defined(GC_ASSERTIONS) + STATIC GC_bool GC_please_stop = FALSE; +#endif + +/* + * We track thread attachments while the world is supposed to be stopped. + * Unfortunately, we can't stop them from starting, since blocking in + * DllMain seems to cause the world to deadlock. Thus we have to recover + * If we notice this in the middle of marking. + */ + +#ifndef GC_NO_THREADS_DISCOVERY + STATIC volatile AO_t GC_attached_thread = FALSE; +#endif + +#if defined(WRAP_MARK_SOME) && !defined(GC_PTHREADS) + /* Return TRUE if an thread was attached since we last asked or */ + /* since GC_attached_thread was explicitly reset. */ + GC_INNER GC_bool GC_started_thread_while_stopped(void) + { +# ifndef GC_NO_THREADS_DISCOVERY + if (GC_win32_dll_threads) { +# ifdef AO_HAVE_compare_and_swap_release + if (AO_compare_and_swap_release(&GC_attached_thread, TRUE, + FALSE /* stored */)) + return TRUE; +# else + AO_nop_full(); /* Prior heap reads need to complete earlier. */ + if (AO_load(&GC_attached_thread)) { + AO_store(&GC_attached_thread, FALSE); + return TRUE; + } +# endif + } +# endif + return FALSE; + } +#endif /* WRAP_MARK_SOME */ + +/* Thread table used if GC_win32_dll_threads is set. */ +/* This is a fixed size array. */ +/* Since we use runtime conditionals, both versions */ +/* are always defined. */ +# ifndef MAX_THREADS +# define MAX_THREADS 512 +# endif + +/* Things may get quite slow for large numbers of threads, */ +/* since we look them up with sequential search. */ +volatile struct GC_Thread_Rep dll_thread_table[MAX_THREADS]; + +STATIC volatile LONG GC_max_thread_index = 0; + /* Largest index in dll_thread_table */ + /* that was ever used. */ + +/* And now the version used if GC_win32_dll_threads is not set. */ +/* This is a chained hash table, with much of the code borrowed */ +/* From the Posix implementation. */ +#ifndef THREAD_TABLE_SZ +# define THREAD_TABLE_SZ 256 /* Power of 2 (for speed). */ +#endif +#define THREAD_TABLE_INDEX(id) /* id is of DWORD type */ \ + (int)((((id) >> 8) ^ (id)) % THREAD_TABLE_SZ) +STATIC GC_thread GC_threads[THREAD_TABLE_SZ]; + +/* It may not be safe to allocate when we register the first thread. */ +/* Thus we allocated one statically. It does not contain any field we */ +/* need to push ("next" and "status" fields are unused). */ +static struct GC_Thread_Rep first_thread; +static GC_bool first_thread_used = FALSE; + +/* Add a thread to GC_threads. We assume it wasn't already there. */ +/* Caller holds allocation lock. */ +/* Unlike the pthreads version, the id field is set by the caller. */ +STATIC GC_thread GC_new_thread(DWORD id) +{ + int hv = THREAD_TABLE_INDEX(id); + GC_thread result; + +# ifdef DEBUG_THREADS + GC_log_printf("Creating thread 0x%lx\n", (long)id); + if (GC_threads[hv] != NULL) + GC_log_printf("Hash collision at GC_threads[%d]\n", hv); +# endif + GC_ASSERT(I_HOLD_LOCK()); + if (!EXPECT(first_thread_used, TRUE)) { + result = &first_thread; + first_thread_used = TRUE; + GC_ASSERT(NULL == GC_threads[hv]); + } else { + GC_ASSERT(!GC_win32_dll_threads); + result = (struct GC_Thread_Rep *) + GC_INTERNAL_MALLOC(sizeof(struct GC_Thread_Rep), NORMAL); + if (result == 0) return(0); + } + /* result -> id = id; Done by caller. */ + result -> tm.next = GC_threads[hv]; + GC_threads[hv] = result; +# ifdef GC_PTHREADS + GC_ASSERT(result -> flags == 0); +# endif + GC_ASSERT(result -> thread_blocked_sp == NULL); + if (EXPECT(result != &first_thread, TRUE)) + GC_dirty(result); + return(result); +} + +STATIC GC_bool GC_in_thread_creation = FALSE; + /* Protected by allocation lock. */ + +GC_INLINE void GC_record_stack_base(GC_vthread me, + const struct GC_stack_base *sb) +{ + me -> stack_base = (ptr_t)sb->mem_base; +# ifdef IA64 + me -> backing_store_end = (ptr_t)sb->reg_base; +# endif + if (me -> stack_base == NULL) + ABORT("Bad stack base in GC_register_my_thread"); +} + +/* This may be called from DllMain, and hence operates under unusual */ +/* constraints. In particular, it must be lock-free if */ +/* GC_win32_dll_threads is set. Always called from the thread being */ +/* added. If GC_win32_dll_threads is not set, we already hold the */ +/* allocation lock except possibly during single-threaded startup code. */ +STATIC GC_thread GC_register_my_thread_inner(const struct GC_stack_base *sb, + DWORD thread_id) +{ + GC_vthread me; + + /* The following should be a no-op according to the win32 */ + /* documentation. There is empirical evidence that it */ + /* isn't. - HB */ +# if defined(MPROTECT_VDB) + if (GC_incremental +# ifdef GWW_VDB + && !GC_gww_dirty_init() +# endif + ) + GC_set_write_fault_handler(); +# endif + +# ifndef GC_NO_THREADS_DISCOVERY + if (GC_win32_dll_threads) { + int i; + /* It appears to be unsafe to acquire a lock here, since this */ + /* code is apparently not preemptible on some systems. */ + /* (This is based on complaints, not on Microsoft's official */ + /* documentation, which says this should perform "only simple */ + /* initialization tasks".) */ + /* Hence we make do with nonblocking synchronization. */ + /* It has been claimed that DllMain is really only executed with */ + /* a particular system lock held, and thus careful use of locking */ + /* around code that doesn't call back into the system libraries */ + /* might be OK. But this hasn't been tested across all win32 */ + /* variants. */ + /* cast away volatile qualifier */ + for (i = 0; + InterlockedExchange((void*)&dll_thread_table[i].tm.in_use, 1) != 0; + i++) { + /* Compare-and-swap would make this cleaner, but that's not */ + /* supported before Windows 98 and NT 4.0. In Windows 2000, */ + /* InterlockedExchange is supposed to be replaced by */ + /* InterlockedExchangePointer, but that's not really what I */ + /* want here. */ + /* FIXME: We should eventually declare Win95 dead and use AO_ */ + /* primitives here. */ + if (i == MAX_THREADS - 1) + ABORT("Too many threads"); + } + /* Update GC_max_thread_index if necessary. The following is */ + /* safe, and unlike CompareExchange-based solutions seems to work */ + /* on all Windows95 and later platforms. */ + /* Unfortunately, GC_max_thread_index may be temporarily out of */ + /* bounds, so readers have to compensate. */ + while (i > GC_max_thread_index) { + InterlockedIncrement((IE_t)&GC_max_thread_index); + } + if (GC_max_thread_index >= MAX_THREADS) { + /* We overshot due to simultaneous increments. */ + /* Setting it to MAX_THREADS-1 is always safe. */ + GC_max_thread_index = MAX_THREADS - 1; + } + me = dll_thread_table + i; + } else +# endif + /* else */ /* Not using DllMain */ { + GC_ASSERT(I_HOLD_LOCK()); + GC_in_thread_creation = TRUE; /* OK to collect from unknown thread. */ + me = GC_new_thread(thread_id); + GC_in_thread_creation = FALSE; + if (me == 0) + ABORT("Failed to allocate memory for thread registering"); + } +# ifdef GC_PTHREADS + /* me can be NULL -> segfault */ + me -> pthread_id = pthread_self(); +# endif +# ifndef MSWINCE + /* GetCurrentThread() returns a pseudohandle (a const value). */ + if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), + GetCurrentProcess(), + (HANDLE*)&(me -> handle), + 0 /* dwDesiredAccess */, FALSE /* bInheritHandle */, + DUPLICATE_SAME_ACCESS)) { + ABORT_ARG1("DuplicateHandle failed", + ": errcode= 0x%X", (unsigned)GetLastError()); + } +# endif + me -> last_stack_min = ADDR_LIMIT; + GC_record_stack_base(me, sb); + /* Up until this point, GC_push_all_stacks considers this thread */ + /* invalid. */ + /* Up until this point, this entry is viewed as reserved but invalid */ + /* by GC_delete_thread. */ + me -> id = thread_id; +# if defined(THREAD_LOCAL_ALLOC) + GC_init_thread_local((GC_tlfs)(&(me->tlfs))); +# endif +# ifndef GC_NO_THREADS_DISCOVERY + if (GC_win32_dll_threads) { + if (GC_please_stop) { + AO_store(&GC_attached_thread, TRUE); + AO_nop_full(); /* Later updates must become visible after this. */ + } + /* We'd like to wait here, but can't, since waiting in DllMain */ + /* provokes deadlocks. */ + /* Thus we force marking to be restarted instead. */ + } else +# endif + /* else */ { + GC_ASSERT(!GC_please_stop); + /* Otherwise both we and the thread stopping code would be */ + /* holding the allocation lock. */ + } + return (GC_thread)(me); +} + +/* + * GC_max_thread_index may temporarily be larger than MAX_THREADS. + * To avoid subscript errors, we check on access. + */ +GC_INLINE LONG GC_get_max_thread_index(void) +{ + LONG my_max = GC_max_thread_index; + if (my_max >= MAX_THREADS) return MAX_THREADS - 1; + return my_max; +} + +/* Return the GC_thread corresponding to a thread id. May be called */ +/* without a lock, but should be called in contexts in which the */ +/* requested thread cannot be asynchronously deleted, e.g. from the */ +/* thread itself. */ +/* This version assumes that either GC_win32_dll_threads is set, or */ +/* we hold the allocator lock. */ +/* Also used (for assertion checking only) from thread_local_alloc.c. */ +STATIC GC_thread GC_lookup_thread_inner(DWORD thread_id) +{ +# ifndef GC_NO_THREADS_DISCOVERY + if (GC_win32_dll_threads) { + int i; + LONG my_max = GC_get_max_thread_index(); + for (i = 0; i <= my_max && + (!AO_load_acquire(&dll_thread_table[i].tm.in_use) + || dll_thread_table[i].id != thread_id); + /* Must still be in_use, since nobody else can store our */ + /* thread_id. */ + i++) { + /* empty */ + } + return i <= my_max ? (GC_thread)(dll_thread_table + i) : NULL; + } else +# endif + /* else */ { + GC_thread p = GC_threads[THREAD_TABLE_INDEX(thread_id)]; + + GC_ASSERT(I_HOLD_LOCK()); + while (p != 0 && p -> id != thread_id) p = p -> tm.next; + return(p); + } +} + +#ifdef LINT2 +# define CHECK_LOOKUP_MY_THREAD(me) \ + if (!(me)) ABORT("GC_lookup_thread_inner(GetCurrentThreadId) failed") +#else +# define CHECK_LOOKUP_MY_THREAD(me) /* empty */ +#endif + +/* Called by GC_finalize() (in case of an allocation failure observed). */ +/* GC_reset_finalizer_nested() is the same as in pthread_support.c. */ +GC_INNER void GC_reset_finalizer_nested(void) +{ + GC_thread me = GC_lookup_thread_inner(GetCurrentThreadId()); + CHECK_LOOKUP_MY_THREAD(me); + me->finalizer_nested = 0; +} + +/* Checks and updates the thread-local level of finalizers recursion. */ +/* Returns NULL if GC_invoke_finalizers() should not be called by the */ +/* collector (to minimize the risk of a deep finalizers recursion), */ +/* otherwise returns a pointer to the thread-local finalizer_nested. */ +/* Called by GC_notify_or_invoke_finalizers() only (the lock is held). */ +/* GC_check_finalizer_nested() is the same as in pthread_support.c. */ +GC_INNER unsigned char *GC_check_finalizer_nested(void) +{ + GC_thread me = GC_lookup_thread_inner(GetCurrentThreadId()); + unsigned nesting_level; + CHECK_LOOKUP_MY_THREAD(me); + nesting_level = me->finalizer_nested; + if (nesting_level) { + /* We are inside another GC_invoke_finalizers(). */ + /* Skip some implicitly-called GC_invoke_finalizers() */ + /* depending on the nesting (recursion) level. */ + if (++me->finalizer_skipped < (1U << nesting_level)) return NULL; + me->finalizer_skipped = 0; + } + me->finalizer_nested = (unsigned char)(nesting_level + 1); + return &me->finalizer_nested; +} + +#if defined(GC_ASSERTIONS) && defined(THREAD_LOCAL_ALLOC) + /* This is called from thread-local GC_malloc(). */ + GC_bool GC_is_thread_tsd_valid(void *tsd) + { + GC_thread me; + DCL_LOCK_STATE; + + LOCK(); + me = GC_lookup_thread_inner(GetCurrentThreadId()); + UNLOCK(); + return (word)tsd >= (word)(&me->tlfs) + && (word)tsd < (word)(&me->tlfs) + sizeof(me->tlfs); + } +#endif /* GC_ASSERTIONS && THREAD_LOCAL_ALLOC */ + +GC_API int GC_CALL GC_thread_is_registered(void) +{ + DWORD thread_id = GetCurrentThreadId(); + GC_thread me; + DCL_LOCK_STATE; + + LOCK(); + me = GC_lookup_thread_inner(thread_id); + UNLOCK(); + return me != NULL; +} + +GC_API void GC_CALL GC_register_altstack(void *stack GC_ATTR_UNUSED, + GC_word stack_size GC_ATTR_UNUSED, + void *altstack GC_ATTR_UNUSED, + GC_word altstack_size GC_ATTR_UNUSED) +{ + /* TODO: Implement */ +} + +/* Make sure thread descriptor t is not protected by the VDB */ +/* implementation. */ +/* Used to prevent write faults when the world is (partially) stopped, */ +/* since it may have been stopped with a system lock held, and that */ +/* lock may be required for fault handling. */ +#if defined(MPROTECT_VDB) +# define UNPROTECT_THREAD(t) \ + if (!GC_win32_dll_threads && GC_incremental && t != &first_thread) { \ + GC_ASSERT(SMALL_OBJ(GC_size(t))); \ + GC_remove_protection(HBLKPTR(t), 1, FALSE); \ + } else (void)0 +#else +# define UNPROTECT_THREAD(t) (void)0 +#endif + +#ifdef CYGWIN32 +# define GC_PTHREAD_PTRVAL(pthread_id) pthread_id +#elif defined(GC_WIN32_PTHREADS) || defined(GC_PTHREADS_PARAMARK) +# include /* to check for winpthreads */ +# if defined(__WINPTHREADS_VERSION_MAJOR) +# define GC_PTHREAD_PTRVAL(pthread_id) pthread_id +# else +# define GC_PTHREAD_PTRVAL(pthread_id) pthread_id.p +# endif +#endif + +/* If a thread has been joined, but we have not yet */ +/* been notified, then there may be more than one thread */ +/* in the table with the same win32 id. */ +/* This is OK, but we need a way to delete a specific one. */ +/* Assumes we hold the allocation lock unless */ +/* GC_win32_dll_threads is set. Does not actually free */ +/* GC_thread entry (only unlinks it). */ +/* If GC_win32_dll_threads is set it should be called from the */ +/* thread being deleted. */ +STATIC void GC_delete_gc_thread_no_free(GC_vthread t) +{ +# ifndef MSWINCE + CloseHandle(t->handle); +# endif +# ifndef GC_NO_THREADS_DISCOVERY + if (GC_win32_dll_threads) { + /* This is intended to be lock-free. */ + /* It is either called synchronously from the thread being */ + /* deleted, or by the joining thread. */ + /* In this branch asynchronous changes to (*t) are possible. */ + /* It's not allowed to call GC_printf (and the friends) here, */ + /* see GC_stop_world() for the information. */ + t -> stack_base = 0; + t -> id = 0; + AO_store_release(&t->tm.in_use, FALSE); + } else +# endif + /* else */ { + DWORD id = ((GC_thread)t) -> id; + /* Cast away volatile qualifier, since we have lock. */ + int hv = THREAD_TABLE_INDEX(id); + GC_thread p = GC_threads[hv]; + GC_thread prev = NULL; + + GC_ASSERT(I_HOLD_LOCK()); + while (p != (GC_thread)t) { + prev = p; + p = p -> tm.next; + } + if (prev == 0) { + GC_threads[hv] = p -> tm.next; + } else { + GC_ASSERT(prev != &first_thread); + prev -> tm.next = p -> tm.next; + GC_dirty(prev); + } + } +} + +/* Delete a thread from GC_threads. We assume it is there. */ +/* (The code intentionally traps if it wasn't.) Assumes we */ +/* hold the allocation lock unless GC_win32_dll_threads is set. */ +/* If GC_win32_dll_threads is set then it should be called from */ +/* the thread being deleted. It is also safe to delete the */ +/* main thread (unless GC_win32_dll_threads). */ +STATIC void GC_delete_thread(DWORD id) +{ + if (GC_win32_dll_threads) { + GC_vthread t = GC_lookup_thread_inner(id); + + if (0 == t) { + WARN("Removing nonexistent thread, id = %" WARN_PRIdPTR "\n", id); + } else { + GC_delete_gc_thread_no_free(t); + } + } else { + int hv = THREAD_TABLE_INDEX(id); + GC_thread p = GC_threads[hv]; + GC_thread prev = NULL; + + GC_ASSERT(I_HOLD_LOCK()); + while (p -> id != id) { + prev = p; + p = p -> tm.next; + } +# ifndef MSWINCE + CloseHandle(p->handle); +# endif + if (prev == 0) { + GC_threads[hv] = p -> tm.next; + } else { + GC_ASSERT(prev != &first_thread); + prev -> tm.next = p -> tm.next; + GC_dirty(prev); + } + if (EXPECT(p != &first_thread, TRUE)) { + GC_INTERNAL_FREE(p); + } + } +} + +GC_API void GC_CALL GC_allow_register_threads(void) +{ + /* Check GC is initialized and the current thread is registered. */ + GC_ASSERT(GC_lookup_thread_inner(GetCurrentThreadId()) != 0); +# if !defined(GC_ALWAYS_MULTITHREADED) && !defined(PARALLEL_MARK) \ + && !defined(GC_NO_THREADS_DISCOVERY) + /* GC_init() does not call GC_init_parallel() in this case. */ + parallel_initialized = TRUE; +# endif + set_need_to_lock(); +} + +GC_API int GC_CALL GC_register_my_thread(const struct GC_stack_base *sb) +{ + GC_thread me; + DWORD thread_id = GetCurrentThreadId(); + DCL_LOCK_STATE; + + if (GC_need_to_lock == FALSE) + ABORT("Threads explicit registering is not previously enabled"); + + /* We lock here, since we want to wait for an ongoing GC. */ + LOCK(); + me = GC_lookup_thread_inner(thread_id); + if (me == 0) { +# ifdef GC_PTHREADS + me = GC_register_my_thread_inner(sb, thread_id); + me -> flags |= DETACHED; + /* Treat as detached, since we do not need to worry about */ + /* pointer results. */ +# else + GC_register_my_thread_inner(sb, thread_id); +# endif + UNLOCK(); + return GC_SUCCESS; + } else +# ifdef GC_PTHREADS + /* else */ if ((me -> flags & FINISHED) != 0) { + GC_record_stack_base(me, sb); + me -> flags &= ~FINISHED; /* but not DETACHED */ +# ifdef THREAD_LOCAL_ALLOC + GC_init_thread_local((GC_tlfs)(&me->tlfs)); +# endif + UNLOCK(); + return GC_SUCCESS; + } else +# endif + /* else */ { + UNLOCK(); + return GC_DUPLICATE; + } +} + +/* Similar to that in pthread_support.c. */ +STATIC void GC_wait_for_gc_completion(GC_bool wait_for_all) +{ + GC_ASSERT(I_HOLD_LOCK()); + if (GC_incremental && GC_collection_in_progress()) { + word old_gc_no = GC_gc_no; + + /* Make sure that no part of our stack is still on the mark stack, */ + /* since it's about to be unmapped. */ + do { + ENTER_GC(); + GC_in_thread_creation = TRUE; + GC_collect_a_little_inner(1); + GC_in_thread_creation = FALSE; + EXIT_GC(); + + UNLOCK(); + Sleep(0); /* yield */ + LOCK(); + } while (GC_incremental && GC_collection_in_progress() + && (wait_for_all || old_gc_no == GC_gc_no)); + } +} + +GC_API int GC_CALL GC_unregister_my_thread(void) +{ + DCL_LOCK_STATE; + +# ifdef DEBUG_THREADS + GC_log_printf("Unregistering thread 0x%lx\n", (long)GetCurrentThreadId()); +# endif + + if (GC_win32_dll_threads) { +# if defined(THREAD_LOCAL_ALLOC) + /* Can't happen: see GC_use_threads_discovery(). */ + GC_ASSERT(FALSE); +# else + /* FIXME: Should we just ignore this? */ + GC_delete_thread(GetCurrentThreadId()); +# endif + } else { +# if defined(THREAD_LOCAL_ALLOC) || defined(GC_PTHREADS) + GC_thread me; +# endif + DWORD thread_id = GetCurrentThreadId(); + + LOCK(); + GC_wait_for_gc_completion(FALSE); +# if defined(THREAD_LOCAL_ALLOC) || defined(GC_PTHREADS) + me = GC_lookup_thread_inner(thread_id); + CHECK_LOOKUP_MY_THREAD(me); + GC_ASSERT(!KNOWN_FINISHED(me)); +# endif +# if defined(THREAD_LOCAL_ALLOC) + GC_ASSERT(GC_getspecific(GC_thread_key) == &me->tlfs); + GC_destroy_thread_local(&(me->tlfs)); +# endif +# ifdef GC_PTHREADS + if ((me -> flags & DETACHED) == 0) { + me -> flags |= FINISHED; + } else +# endif + /* else */ { + GC_delete_thread(thread_id); + } +# if defined(THREAD_LOCAL_ALLOC) + /* It is required to call remove_specific defined in specific.c. */ + GC_remove_specific(GC_thread_key); +# endif + UNLOCK(); + } + return GC_SUCCESS; +} + +/* Wrapper for functions that are likely to block for an appreciable */ +/* length of time. */ + +/* GC_do_blocking_inner() is nearly the same as in pthread_support.c */ +GC_INNER void GC_do_blocking_inner(ptr_t data, void * context GC_ATTR_UNUSED) +{ + struct blocking_data * d = (struct blocking_data *) data; + DWORD thread_id = GetCurrentThreadId(); + GC_thread me; +# ifdef IA64 + ptr_t stack_ptr = GC_save_regs_in_stack(); +# endif + DCL_LOCK_STATE; + + LOCK(); + me = GC_lookup_thread_inner(thread_id); + CHECK_LOOKUP_MY_THREAD(me); + GC_ASSERT(me -> thread_blocked_sp == NULL); +# ifdef IA64 + me -> backing_store_ptr = stack_ptr; +# endif + me -> thread_blocked_sp = (ptr_t) &d; /* save approx. sp */ + /* Save context here if we want to support precise stack marking */ + UNLOCK(); + d -> client_data = (d -> fn)(d -> client_data); + LOCK(); /* This will block if the world is stopped. */ +# if defined(CPPCHECK) + GC_noop1((word)me->thread_blocked_sp); +# endif + me -> thread_blocked_sp = NULL; + UNLOCK(); +} + +/* GC_call_with_gc_active() has the opposite to GC_do_blocking() */ +/* functionality. It might be called from a user function invoked by */ +/* GC_do_blocking() to temporarily back allow calling any GC function */ +/* and/or manipulating pointers to the garbage collected heap. */ +GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type fn, + void * client_data) +{ + struct GC_traced_stack_sect_s stacksect; + DWORD thread_id = GetCurrentThreadId(); + GC_thread me; + DCL_LOCK_STATE; + + LOCK(); /* This will block if the world is stopped. */ + me = GC_lookup_thread_inner(thread_id); + CHECK_LOOKUP_MY_THREAD(me); + /* Adjust our stack base value (this could happen unless */ + /* GC_get_stack_base() was used which returned GC_SUCCESS). */ + GC_ASSERT(me -> stack_base != NULL); + if ((word)me->stack_base < (word)(&stacksect)) + me -> stack_base = (ptr_t)(&stacksect); + + if (me -> thread_blocked_sp == NULL) { + /* We are not inside GC_do_blocking() - do nothing more. */ + UNLOCK(); + client_data = fn(client_data); + /* Prevent treating the above as a tail call. */ + GC_noop1((word)(&stacksect)); + return client_data; /* result */ + } + + /* Setup new "stack section". */ + stacksect.saved_stack_ptr = me -> thread_blocked_sp; +# ifdef IA64 + /* This is the same as in GC_call_with_stack_base(). */ + stacksect.backing_store_end = GC_save_regs_in_stack(); + /* Unnecessarily flushes register stack, */ + /* but that probably doesn't hurt. */ + stacksect.saved_backing_store_ptr = me -> backing_store_ptr; +# endif + stacksect.prev = me -> traced_stack_sect; + me -> thread_blocked_sp = NULL; + me -> traced_stack_sect = &stacksect; + + UNLOCK(); + client_data = fn(client_data); + GC_ASSERT(me -> thread_blocked_sp == NULL); + GC_ASSERT(me -> traced_stack_sect == &stacksect); + + /* Restore original "stack section". */ + LOCK(); +# if defined(CPPCHECK) + GC_noop1((word)me->traced_stack_sect); +# endif + me -> traced_stack_sect = stacksect.prev; +# ifdef IA64 + me -> backing_store_ptr = stacksect.saved_backing_store_ptr; +# endif + me -> thread_blocked_sp = stacksect.saved_stack_ptr; + UNLOCK(); + + return client_data; /* result */ +} + +#ifdef GC_PTHREADS + + /* A quick-and-dirty cache of the mapping between pthread_t */ + /* and win32 thread id. */ +# define PTHREAD_MAP_SIZE 512 + DWORD GC_pthread_map_cache[PTHREAD_MAP_SIZE] = {0}; +# define PTHREAD_MAP_INDEX(pthread_id) \ + ((NUMERIC_THREAD_ID(pthread_id) >> 5) % PTHREAD_MAP_SIZE) + /* It appears pthread_t is really a pointer type ... */ +# define SET_PTHREAD_MAP_CACHE(pthread_id, win32_id) \ + (void)(GC_pthread_map_cache[PTHREAD_MAP_INDEX(pthread_id)] = (win32_id)) +# define GET_PTHREAD_MAP_CACHE(pthread_id) \ + GC_pthread_map_cache[PTHREAD_MAP_INDEX(pthread_id)] + + /* Return a GC_thread corresponding to a given pthread_t. */ + /* Returns 0 if it's not there. */ + /* We assume that this is only called for pthread ids that */ + /* have not yet terminated or are still joinable, and */ + /* cannot be concurrently terminated. */ + /* Assumes we do NOT hold the allocation lock. */ + STATIC GC_thread GC_lookup_pthread(pthread_t id) + { +# ifndef GC_NO_THREADS_DISCOVERY + if (GC_win32_dll_threads) { + int i; + LONG my_max = GC_get_max_thread_index(); + + for (i = 0; i <= my_max && + (!AO_load_acquire(&dll_thread_table[i].tm.in_use) + || THREAD_EQUAL(dll_thread_table[i].pthread_id, id)); + /* Must still be in_use, since nobody else can */ + /* store our thread_id. */ + i++) { + /* empty */ + } + return i <= my_max ? (GC_thread)(dll_thread_table + i) : NULL; + } else +# endif + /* else */ { + /* We first try the cache. If that fails, we use a very slow */ + /* approach. */ + DWORD win32_id = GET_PTHREAD_MAP_CACHE(id); + int hv_guess = THREAD_TABLE_INDEX(win32_id); + int hv; + GC_thread p; + DCL_LOCK_STATE; + + LOCK(); + for (p = GC_threads[hv_guess]; 0 != p; p = p -> tm.next) { + if (THREAD_EQUAL(p -> pthread_id, id)) + goto foundit; + } + for (hv = 0; hv < THREAD_TABLE_SZ; ++hv) { + for (p = GC_threads[hv]; 0 != p; p = p -> tm.next) { + if (THREAD_EQUAL(p -> pthread_id, id)) + goto foundit; + } + } + p = 0; + foundit: + UNLOCK(); + return p; + } + } + +#endif /* GC_PTHREADS */ + +#ifdef CAN_HANDLE_FORK + /* Similar to that in pthread_support.c but also rehashes the table */ + /* since hash map key (thread_id) differs from that in the parent. */ + STATIC void GC_remove_all_threads_but_me(void) + { + int hv; + GC_thread p, next, me = NULL; + DWORD thread_id; + pthread_t pthread_id = pthread_self(); /* same as in parent */ + + GC_ASSERT(!GC_win32_dll_threads); + for (hv = 0; hv < THREAD_TABLE_SZ; ++hv) { + for (p = GC_threads[hv]; 0 != p; p = next) { + next = p -> tm.next; + if (THREAD_EQUAL(p -> pthread_id, pthread_id) + && me == NULL) { /* ignore dead threads with the same id */ + me = p; + p -> tm.next = 0; + } else { +# ifdef THREAD_LOCAL_ALLOC + if ((p -> flags & FINISHED) == 0) { + /* Cannot call GC_destroy_thread_local here (see the */ + /* corresponding comment in pthread_support.c). */ + GC_remove_specific_after_fork(GC_thread_key, p -> pthread_id); + } +# endif + if (&first_thread != p) + GC_INTERNAL_FREE(p); + } + } + GC_threads[hv] = NULL; + } + + /* Put "me" back to GC_threads. */ + GC_ASSERT(me != NULL); + thread_id = GetCurrentThreadId(); /* differs from that in parent */ + GC_threads[THREAD_TABLE_INDEX(thread_id)] = me; + + /* Update Win32 thread Id and handle. */ + me -> id = thread_id; +# ifndef MSWINCE + if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), + GetCurrentProcess(), (HANDLE *)&me->handle, + 0 /* dwDesiredAccess */, FALSE /* bInheritHandle */, + DUPLICATE_SAME_ACCESS)) + ABORT("DuplicateHandle failed"); +# endif + +# if defined(THREAD_LOCAL_ALLOC) && !defined(USE_CUSTOM_SPECIFIC) + /* For Cygwin, we need to re-assign thread-local pointer to */ + /* 'tlfs' (it is OK to call GC_destroy_thread_local and */ + /* GC_free_internal before this action). */ + if (GC_setspecific(GC_thread_key, &me->tlfs) != 0) + ABORT("GC_setspecific failed (in child)"); +# endif + } + + static void fork_prepare_proc(void) + { + LOCK(); +# ifdef PARALLEL_MARK + if (GC_parallel) + GC_wait_for_reclaim(); +# endif + GC_wait_for_gc_completion(TRUE); +# ifdef PARALLEL_MARK + if (GC_parallel) + GC_acquire_mark_lock(); +# endif + } + + static void fork_parent_proc(void) + { +# ifdef PARALLEL_MARK + if (GC_parallel) + GC_release_mark_lock(); +# endif + UNLOCK(); + } + + static void fork_child_proc(void) + { +# ifdef PARALLEL_MARK + if (GC_parallel) { + GC_release_mark_lock(); + GC_parallel = FALSE; /* or GC_markers_m1 = 0 */ + /* Turn off parallel marking in the child, since we are */ + /* probably just going to exec, and we would have to */ + /* restart mark threads. */ + } +# endif + GC_remove_all_threads_but_me(); + UNLOCK(); + } + + /* Routines for fork handling by client (no-op if pthread_atfork works). */ + GC_API void GC_CALL GC_atfork_prepare(void) + { + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); + if (GC_handle_fork <= 0) + fork_prepare_proc(); + } + + GC_API void GC_CALL GC_atfork_parent(void) + { + if (GC_handle_fork <= 0) + fork_parent_proc(); + } + + GC_API void GC_CALL GC_atfork_child(void) + { + if (GC_handle_fork <= 0) + fork_child_proc(); + } +#endif /* CAN_HANDLE_FORK */ + +void GC_push_thread_structures(void) +{ + GC_ASSERT(I_HOLD_LOCK()); +# ifndef GC_NO_THREADS_DISCOVERY + if (GC_win32_dll_threads) { + /* Unlike the other threads implementations, the thread table */ + /* here contains no pointers to the collectible heap (note also */ + /* that GC_PTHREADS is incompatible with DllMain-based thread */ + /* registration). Thus we have no private structures we need */ + /* to preserve. */ + } else +# endif + /* else */ { + GC_PUSH_ALL_SYM(GC_threads); + } +# if defined(THREAD_LOCAL_ALLOC) + GC_PUSH_ALL_SYM(GC_thread_key); + /* Just in case we ever use our own TLS implementation. */ +# endif +} + +/* Suspend the given thread, if it's still active. */ +STATIC void GC_suspend(GC_thread t) +{ +# ifndef MSWINCE + int iterations; +# endif + UNPROTECT_THREAD(t); + GC_acquire_dirty_lock (); +# ifdef MSWINCE + /* SuspendThread() will fail if thread is running kernel code. */ + while (SuspendThread(THREAD_HANDLE(t)) == (DWORD)-1) + Sleep(10); /* in millis */ +# else + iterations = 0; + while (TRUE) + { + /* Apparently the Windows 95 GetOpenFileName call creates */ + /* a thread that does not properly get cleaned up, and */ + /* SuspendThread on its descriptor may provoke a crash. */ + /* This reduces the probability of that event, though it still */ + /* appears there's a race here. */ + DWORD exitCode; + + if (GetExitCodeThread (t->handle, &exitCode) && + exitCode != STILL_ACTIVE) + { +# ifdef GC_PTHREADS + t->stack_base = 0; /* prevent stack from being pushed */ +# else + /* this breaks pthread_join on Cygwin, which is guaranteed to */ + /* only see user pthreads */ + GC_ASSERT (GC_win32_dll_threads); + GC_delete_gc_thread_no_free (t); +# endif + GC_release_dirty_lock (); + return; + } + + { + DWORD res; + do { + res = SuspendThread (t->handle); + } while (res == (DWORD)-1); + } + + t->saved_context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL; + if (GetThreadContext (t->handle, &t->saved_context)) { + t->suspended = (unsigned char)TRUE; + break; /* success case break out of loop */ + } + + /* resume thread and try to suspend in better location */ + if (ResumeThread (t->handle) == (DWORD)-1) + ABORT ("ResumeThread failed"); + + /* after a million tries something must be wrong */ + if (iterations++ == 1000 * 1000) + ABORT ("SuspendThread loop failed"); + } +# endif /* !MSWINCE */ + t->suspended = (unsigned char)TRUE; + GC_release_dirty_lock(); + if (GC_on_thread_event) + GC_on_thread_event(GC_EVENT_THREAD_SUSPENDED, THREAD_HANDLE(t)); +} + +#if defined(GC_ASSERTIONS) && (defined(MSWIN32) || defined(MSWINCE)) + GC_INNER GC_bool GC_write_disabled = FALSE; + /* TRUE only if GC_stop_world() acquired GC_write_cs. */ +#endif + +/* Defined in misc.c */ +extern CRITICAL_SECTION GC_write_cs; + +GC_INNER void GC_stop_world(void) +{ + DWORD thread_id = GetCurrentThreadId(); + + if (!GC_thr_initialized) + ABORT("GC_stop_world() called before GC_thr_init()"); + GC_ASSERT(I_HOLD_LOCK()); + + /* This code is the same as in pthread_stop_world.c */ +# ifdef PARALLEL_MARK + if (GC_parallel) { + GC_acquire_mark_lock(); + GC_ASSERT(GC_fl_builder_count == 0); + /* We should have previously waited for it to become zero. */ + } +# endif /* PARALLEL_MARK */ + +# if !defined(GC_NO_THREADS_DISCOVERY) || defined(GC_ASSERTIONS) + GC_please_stop = TRUE; +# endif +# ifndef CYGWIN32 +# ifndef MSWIN_XBOX1 + GC_ASSERT(!GC_write_disabled); +# endif + EnterCriticalSection(&GC_write_cs); +# endif +# if defined(GC_ASSERTIONS) && (defined(MSWIN32) || defined(MSWINCE)) + /* It's not allowed to call GC_printf() (and friends) here down to */ + /* LeaveCriticalSection (same applies recursively to GC_suspend, */ + /* GC_delete_gc_thread_no_free, GC_get_max_thread_index, GC_size */ + /* and GC_remove_protection). */ + GC_write_disabled = TRUE; +# endif +# ifndef GC_NO_THREADS_DISCOVERY + if (GC_win32_dll_threads) { + int i; + int my_max; + /* Any threads being created during this loop will end up setting */ + /* GC_attached_thread when they start. This will force marking */ + /* to restart. This is not ideal, but hopefully correct. */ + AO_store(&GC_attached_thread, FALSE); + my_max = (int)GC_get_max_thread_index(); + for (i = 0; i <= my_max; i++) { + GC_vthread t = dll_thread_table + i; + if (t -> stack_base != 0 && t -> thread_blocked_sp == NULL + && t -> id != thread_id) { + GC_suspend((GC_thread)t); + } + } + } else +# endif + /* else */ { + GC_thread t; + int i; + + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (t = GC_threads[i]; t != 0; t = t -> tm.next) { + if (t -> stack_base != 0 && t -> thread_blocked_sp == NULL + && !KNOWN_FINISHED(t) && t -> id != thread_id) { + GC_suspend(t); + } + } + } + } +# if defined(GC_ASSERTIONS) && (defined(MSWIN32) || defined(MSWINCE)) + GC_write_disabled = FALSE; +# endif +# ifndef CYGWIN32 + LeaveCriticalSection(&GC_write_cs); +# endif +# ifdef PARALLEL_MARK + if (GC_parallel) + GC_release_mark_lock(); +# endif +} + +GC_INNER void GC_start_world(void) +{ +# ifdef GC_ASSERTIONS + DWORD thread_id = GetCurrentThreadId(); +# endif + + GC_ASSERT(I_HOLD_LOCK()); + if (GC_win32_dll_threads) { + LONG my_max = GC_get_max_thread_index(); + int i; + + for (i = 0; i <= my_max; i++) { + GC_thread t = (GC_thread)(dll_thread_table + i); + if (t -> suspended) { + GC_ASSERT(t -> stack_base != 0 && t -> id != thread_id); + if (ResumeThread(THREAD_HANDLE(t)) == (DWORD)-1) + ABORT("ResumeThread failed"); + t -> suspended = FALSE; + if (GC_on_thread_event) + GC_on_thread_event(GC_EVENT_THREAD_UNSUSPENDED, THREAD_HANDLE(t)); + } + } + } else { + GC_thread t; + int i; + + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (t = GC_threads[i]; t != 0; t = t -> tm.next) { + if (t -> suspended) { + GC_ASSERT(t -> stack_base != 0 && t -> id != thread_id); + if (ResumeThread(THREAD_HANDLE(t)) == (DWORD)-1) + ABORT("ResumeThread failed"); + UNPROTECT_THREAD(t); + t -> suspended = FALSE; + if (GC_on_thread_event) + GC_on_thread_event(GC_EVENT_THREAD_UNSUSPENDED, THREAD_HANDLE(t)); + } + } + } + } +# if !defined(GC_NO_THREADS_DISCOVERY) || defined(GC_ASSERTIONS) + GC_please_stop = FALSE; +# endif +} + +#ifdef MSWINCE + /* The VirtualQuery calls below won't work properly on some old WinCE */ + /* versions, but since each stack is restricted to an aligned 64 KiB */ + /* region of virtual memory we can just take the next lowest multiple */ + /* of 64 KiB. The result of this macro must not be used as its */ + /* argument later and must not be used as the lower bound for sp */ + /* check (since the stack may be bigger than 64 KiB). */ +# define GC_wince_evaluate_stack_min(s) \ + (ptr_t)(((word)(s) - 1) & ~(word)0xFFFF) +#elif defined(GC_ASSERTIONS) +# define GC_dont_query_stack_min FALSE +#endif + +/* A cache holding the results of the recent VirtualQuery call. */ +/* Protected by the allocation lock. */ +static ptr_t last_address = 0; +static MEMORY_BASIC_INFORMATION last_info; + +/* Probe stack memory region (starting at "s") to find out its */ +/* lowest address (i.e. stack top). */ +/* S must be a mapped address inside the region, NOT the first */ +/* unmapped address. */ +STATIC ptr_t GC_get_stack_min(ptr_t s) +{ + ptr_t bottom; + + GC_ASSERT(I_HOLD_LOCK()); + if (s != last_address) { + VirtualQuery(s, &last_info, sizeof(last_info)); + last_address = s; + } + do { + bottom = (ptr_t)last_info.BaseAddress; + VirtualQuery(bottom - 1, &last_info, sizeof(last_info)); + last_address = bottom - 1; + } while ((last_info.Protect & PAGE_READWRITE) + && !(last_info.Protect & PAGE_GUARD)); + return(bottom); +} + +/* Return true if the page at s has protections appropriate */ +/* for a stack page. */ +static GC_bool may_be_in_stack(ptr_t s) +{ + GC_ASSERT(I_HOLD_LOCK()); + if (s != last_address) { + VirtualQuery(s, &last_info, sizeof(last_info)); + last_address = s; + } + return (last_info.Protect & PAGE_READWRITE) + && !(last_info.Protect & PAGE_GUARD); +} + +STATIC word GC_push_stack_for(GC_thread thread, DWORD me) +{ + ptr_t sp, stack_min; + + struct GC_traced_stack_sect_s *traced_stack_sect = + thread -> traced_stack_sect; + if (thread -> id == me) { + GC_ASSERT(thread -> thread_blocked_sp == NULL); + sp = GC_approx_sp(); + } else if ((sp = thread -> thread_blocked_sp) == NULL) { + /* Use saved sp value for blocked threads. */ + /* For unblocked threads call GetThreadContext(). */ + /* we cache context when suspending the thread since it may require looping */ + CONTEXT context = thread->saved_context; + + /* Push all registers that might point into the heap. Frame */ + /* pointer registers are included in case client code was */ + /* compiled with the 'omit frame pointer' optimization. */ +# define PUSH1(reg) GC_push_one((word)context.reg) +# define PUSH2(r1,r2) (PUSH1(r1), PUSH1(r2)) +# define PUSH4(r1,r2,r3,r4) (PUSH2(r1,r2), PUSH2(r3,r4)) +# if defined(I386) + PUSH4(Edi,Esi,Ebx,Edx), PUSH2(Ecx,Eax), PUSH1(Ebp); + sp = (ptr_t)context.Esp; +# elif defined(X86_64) + PUSH4(Rax,Rcx,Rdx,Rbx); PUSH2(Rbp, Rsi); PUSH1(Rdi); + PUSH4(R8, R9, R10, R11); PUSH4(R12, R13, R14, R15); + sp = (ptr_t)context.Rsp; +# elif defined(ARM32) + PUSH4(R0,R1,R2,R3),PUSH4(R4,R5,R6,R7),PUSH4(R8,R9,R10,R11); + PUSH1(R12); + sp = (ptr_t)context.Sp; +# elif defined(AARCH64) + PUSH4(X0,X1,X2,X3),PUSH4(X4,X5,X6,X7),PUSH4(X8,X9,X10,X11); + PUSH4(X12,X13,X14,X15),PUSH4(X16,X17,X18,X19),PUSH4(X20,X21,X22,X23); + PUSH4(X24,X25,X26,X27),PUSH1(X28); + PUSH1(Lr); + sp = (ptr_t)context.Sp; +# elif defined(SHx) + PUSH4(R0,R1,R2,R3), PUSH4(R4,R5,R6,R7), PUSH4(R8,R9,R10,R11); + PUSH2(R12,R13), PUSH1(R14); + sp = (ptr_t)context.R15; +# elif defined(MIPS) + PUSH4(IntAt,IntV0,IntV1,IntA0), PUSH4(IntA1,IntA2,IntA3,IntT0); + PUSH4(IntT1,IntT2,IntT3,IntT4), PUSH4(IntT5,IntT6,IntT7,IntS0); + PUSH4(IntS1,IntS2,IntS3,IntS4), PUSH4(IntS5,IntS6,IntS7,IntT8); + PUSH4(IntT9,IntK0,IntK1,IntS8); + sp = (ptr_t)context.IntSp; +# elif defined(PPC) + PUSH4(Gpr0, Gpr3, Gpr4, Gpr5), PUSH4(Gpr6, Gpr7, Gpr8, Gpr9); + PUSH4(Gpr10,Gpr11,Gpr12,Gpr14), PUSH4(Gpr15,Gpr16,Gpr17,Gpr18); + PUSH4(Gpr19,Gpr20,Gpr21,Gpr22), PUSH4(Gpr23,Gpr24,Gpr25,Gpr26); + PUSH4(Gpr27,Gpr28,Gpr29,Gpr30), PUSH1(Gpr31); + sp = (ptr_t)context.Gpr1; +# elif defined(ALPHA) + PUSH4(IntV0,IntT0,IntT1,IntT2), PUSH4(IntT3,IntT4,IntT5,IntT6); + PUSH4(IntT7,IntS0,IntS1,IntS2), PUSH4(IntS3,IntS4,IntS5,IntFp); + PUSH4(IntA0,IntA1,IntA2,IntA3), PUSH4(IntA4,IntA5,IntT8,IntT9); + PUSH4(IntT10,IntT11,IntT12,IntAt); + sp = (ptr_t)context.IntSp; +# elif !defined(CPPCHECK) +# error "architecture is not supported" +# endif + } /* ! current thread */ + + /* Set stack_min to the lowest address in the thread stack, */ + /* or to an address in the thread stack no larger than sp, */ + /* taking advantage of the old value to avoid slow traversals */ + /* of large stacks. */ + if (thread -> last_stack_min == ADDR_LIMIT) { +# ifdef MSWINCE + if (GC_dont_query_stack_min) { + stack_min = GC_wince_evaluate_stack_min(traced_stack_sect != NULL ? + (ptr_t)traced_stack_sect : thread -> stack_base); + /* Keep last_stack_min value unmodified. */ + } else +# endif + /* else */ { + stack_min = GC_get_stack_min(traced_stack_sect != NULL ? + (ptr_t)traced_stack_sect : thread -> stack_base); + UNPROTECT_THREAD(thread); + thread -> last_stack_min = stack_min; + } + } else { + /* First, adjust the latest known minimum stack address if we */ + /* are inside GC_call_with_gc_active(). */ + if (traced_stack_sect != NULL && + (word)thread->last_stack_min > (word)traced_stack_sect) { + UNPROTECT_THREAD(thread); + thread -> last_stack_min = (ptr_t)traced_stack_sect; + } + + if ((word)sp < (word)thread->stack_base + && (word)sp >= (word)thread->last_stack_min) { + stack_min = sp; + } else { + /* In the current thread it is always safe to use sp value. */ + if (may_be_in_stack(thread -> id == me && + (word)sp < (word)thread->last_stack_min ? + sp : thread -> last_stack_min)) { + stack_min = (ptr_t)last_info.BaseAddress; + /* Do not probe rest of the stack if sp is correct. */ + if ((word)sp < (word)stack_min + || (word)sp >= (word)thread->stack_base) + stack_min = GC_get_stack_min(thread -> last_stack_min); + } else { + /* Stack shrunk? Is this possible? */ + stack_min = GC_get_stack_min(thread -> stack_base); + } + UNPROTECT_THREAD(thread); + thread -> last_stack_min = stack_min; + } + } + + GC_ASSERT(GC_dont_query_stack_min + || stack_min == GC_get_stack_min(thread -> stack_base) + || ((word)sp >= (word)stack_min + && (word)stack_min < (word)thread->stack_base + && (word)stack_min + > (word)GC_get_stack_min(thread -> stack_base))); + + if ((word)sp >= (word)stack_min && (word)sp < (word)thread->stack_base) { +# ifdef DEBUG_THREADS + GC_log_printf("Pushing stack for 0x%x from sp %p to %p from 0x%x\n", + (int)thread->id, (void *)sp, (void *)thread->stack_base, + (int)me); +# endif + GC_push_all_stack_sections(sp, thread->stack_base, traced_stack_sect); + } else { + /* If not current thread then it is possible for sp to point to */ + /* the guarded (untouched yet) page just below the current */ + /* stack_min of the thread. */ + if (thread -> id == me || (word)sp >= (word)thread->stack_base + || (word)(sp + GC_page_size) < (word)stack_min) + WARN("Thread stack pointer %p out of range, pushing everything\n", + sp); +# ifdef DEBUG_THREADS + GC_log_printf("Pushing stack for 0x%x from (min) %p to %p from 0x%x\n", + (int)thread->id, (void *)stack_min, + (void *)thread->stack_base, (int)me); +# endif + /* Push everything - ignore "traced stack section" data. */ + GC_push_all_stack(stack_min, thread->stack_base); + } + return thread->stack_base - sp; /* stack grows down */ +} + +GC_INNER void GC_push_all_stacks(void) +{ + DWORD thread_id = GetCurrentThreadId(); + GC_bool found_me = FALSE; +# ifndef SMALL_CONFIG + unsigned nthreads = 0; +# endif + word total_size = 0; +# ifndef GC_NO_THREADS_DISCOVERY + if (GC_win32_dll_threads) { + int i; + LONG my_max = GC_get_max_thread_index(); + + for (i = 0; i <= my_max; i++) { + GC_thread t = (GC_thread)(dll_thread_table + i); + if (t -> tm.in_use && t -> stack_base) { +# ifndef SMALL_CONFIG + ++nthreads; +# endif + total_size += GC_push_stack_for(t, thread_id); + if (t -> id == thread_id) found_me = TRUE; + } + } + } else +# endif + /* else */ { + int i; + for (i = 0; i < THREAD_TABLE_SZ; i++) { + GC_thread t; + for (t = GC_threads[i]; t != 0; t = t -> tm.next) { + if (!KNOWN_FINISHED(t) && t -> stack_base) { +# ifndef SMALL_CONFIG + ++nthreads; +# endif + total_size += GC_push_stack_for(t, thread_id); + if (t -> id == thread_id) found_me = TRUE; + } + } + } + } +# ifndef SMALL_CONFIG + GC_VERBOSE_LOG_PRINTF("Pushed %d thread stacks%s\n", nthreads, + GC_win32_dll_threads ? + " based on DllMain thread tracking" : ""); +# endif + if (!found_me && !GC_in_thread_creation) + ABORT("Collecting from unknown thread"); + GC_total_stacksize = total_size; +} + +#ifdef PARALLEL_MARK + +# ifndef MAX_MARKERS +# define MAX_MARKERS 16 +# endif + + static ptr_t marker_sp[MAX_MARKERS - 1]; /* The cold end of the stack */ + /* for markers. */ +# ifdef IA64 + static ptr_t marker_bsp[MAX_MARKERS - 1]; +# endif + + static ptr_t marker_last_stack_min[MAX_MARKERS - 1]; + /* Last known minimum (hottest) address */ + /* in stack (or ADDR_LIMIT if unset) */ + /* for markers. */ + +#endif /* PARALLEL_MARK */ + +/* Find stack with the lowest address which overlaps the */ +/* interval [start, limit). */ +/* Return stack bounds in *lo and *hi. If no such stack */ +/* is found, both *hi and *lo will be set to an address */ +/* higher than limit. */ +GC_INNER void GC_get_next_stack(char *start, char *limit, + char **lo, char **hi) +{ + int i; + char * current_min = ADDR_LIMIT; /* Least in-range stack base */ + ptr_t *plast_stack_min = NULL; /* Address of last_stack_min */ + /* field for thread corresponding */ + /* to current_min. */ + GC_thread thread = NULL; /* Either NULL or points to the */ + /* thread's hash table entry */ + /* containing *plast_stack_min. */ + + /* First set current_min, ignoring limit. */ + if (GC_win32_dll_threads) { + LONG my_max = GC_get_max_thread_index(); + + for (i = 0; i <= my_max; i++) { + ptr_t s = (ptr_t)(dll_thread_table[i].stack_base); + + if ((word)s > (word)start && (word)s < (word)current_min) { + /* Update address of last_stack_min. */ + plast_stack_min = (ptr_t * /* no volatile */) + &dll_thread_table[i].last_stack_min; + current_min = s; +# if defined(CPPCHECK) + /* To avoid a warning that thread is always null. */ + thread = (GC_thread)&dll_thread_table[i]; +# endif + } + } + } else { + for (i = 0; i < THREAD_TABLE_SZ; i++) { + GC_thread t; + + for (t = GC_threads[i]; t != 0; t = t -> tm.next) { + ptr_t s = t -> stack_base; + + if ((word)s > (word)start && (word)s < (word)current_min) { + /* Update address of last_stack_min. */ + plast_stack_min = &t -> last_stack_min; + thread = t; /* Remember current thread to unprotect. */ + current_min = s; + } + } + } +# ifdef PARALLEL_MARK + for (i = 0; i < GC_markers_m1; ++i) { + ptr_t s = marker_sp[i]; +# ifdef IA64 + /* FIXME: not implemented */ +# endif + if ((word)s > (word)start && (word)s < (word)current_min) { + GC_ASSERT(marker_last_stack_min[i] != NULL); + plast_stack_min = &marker_last_stack_min[i]; + current_min = s; + thread = NULL; /* Not a thread's hash table entry. */ + } + } +# endif + } + + *hi = current_min; + if (current_min == ADDR_LIMIT) { + *lo = ADDR_LIMIT; + return; + } + + GC_ASSERT((word)current_min > (word)start && plast_stack_min != NULL); +# ifdef MSWINCE + if (GC_dont_query_stack_min) { + *lo = GC_wince_evaluate_stack_min(current_min); + /* Keep last_stack_min value unmodified. */ + return; + } +# endif + + if ((word)current_min > (word)limit && !may_be_in_stack(limit)) { + /* Skip the rest since the memory region at limit address is */ + /* not a stack (so the lowest address of the found stack would */ + /* be above the limit value anyway). */ + *lo = ADDR_LIMIT; + return; + } + + /* Get the minimum address of the found stack by probing its memory */ + /* region starting from the recent known minimum (if set). */ + if (*plast_stack_min == ADDR_LIMIT + || !may_be_in_stack(*plast_stack_min)) { + /* Unsafe to start from last_stack_min value. */ + *lo = GC_get_stack_min(current_min); + } else { + /* Use the recent value to optimize search for min address. */ + *lo = GC_get_stack_min(*plast_stack_min); + } + + /* Remember current stack_min value. */ + if (thread != NULL) { + UNPROTECT_THREAD(thread); + } + *plast_stack_min = *lo; +} + +#ifdef PARALLEL_MARK + +# if defined(GC_PTHREADS) && !defined(GC_PTHREADS_PARAMARK) + /* Use pthread-based parallel mark implementation. */ + + /* Workaround a deadlock in winpthreads-3.0b internals (observed */ + /* with MinGW 32/64). */ +# if !defined(__MINGW32__) +# define GC_PTHREADS_PARAMARK +# endif +# endif + +# if !defined(GC_PTHREADS_PARAMARK) + STATIC HANDLE GC_marker_cv[MAX_MARKERS - 1] = {0}; + /* Events with manual reset (one for each */ + /* mark helper). */ + + STATIC DWORD GC_marker_Id[MAX_MARKERS - 1] = {0}; + /* This table is used for mapping helper */ + /* threads ID to mark helper index (linear */ + /* search is used since the mapping contains */ + /* only a few entries). */ +# endif + + /* GC_mark_thread() is the same as in pthread_support.c */ +# ifdef GC_PTHREADS_PARAMARK + STATIC void * GC_mark_thread(void * id) +# elif defined(MSWINCE) + STATIC DWORD WINAPI GC_mark_thread(LPVOID id) +# else + STATIC unsigned __stdcall GC_mark_thread(void * id) +# endif + { + word my_mark_no = 0; + + if ((word)id == (word)-1) return 0; /* to make compiler happy */ + marker_sp[(word)id] = GC_approx_sp(); +# ifdef IA64 + marker_bsp[(word)id] = GC_save_regs_in_stack(); +# endif +# if !defined(GC_PTHREADS_PARAMARK) + GC_marker_Id[(word)id] = GetCurrentThreadId(); +# endif + + /* Inform GC_start_mark_threads about completion of marker data init. */ + GC_acquire_mark_lock(); + if (0 == --GC_fl_builder_count) /* count may have a negative value */ + GC_notify_all_builder(); + + for (;; ++my_mark_no) { + if (my_mark_no - GC_mark_no > (word)2) { + /* resynchronize if we get far off, e.g. because GC_mark_no */ + /* wrapped. */ + my_mark_no = GC_mark_no; + } +# ifdef DEBUG_THREADS + GC_log_printf("Starting mark helper for mark number %lu\n", + (unsigned long)my_mark_no); +# endif + GC_help_marker(my_mark_no); + } + } + +# ifndef GC_ASSERTIONS +# define SET_MARK_LOCK_HOLDER (void)0 +# define UNSET_MARK_LOCK_HOLDER (void)0 +# endif + + /* GC_mark_threads[] is unused here unlike that in pthread_support.c */ + +# ifdef CAN_HANDLE_FORK + static int available_markers_m1 = 0; +# else +# define available_markers_m1 GC_markers_m1 +# endif + +# ifdef GC_PTHREADS_PARAMARK +# include + +# if defined(GC_ASSERTIONS) && !defined(NUMERIC_THREAD_ID) +# define NUMERIC_THREAD_ID(id) (unsigned long)(word)GC_PTHREAD_PTRVAL(id) + /* Id not guaranteed to be unique. */ +# endif + +# ifdef CAN_HANDLE_FORK + static pthread_cond_t mark_cv; + /* initialized by GC_start_mark_threads_inner */ +# else + static pthread_cond_t mark_cv = PTHREAD_COND_INITIALIZER; +# endif + + /* GC_start_mark_threads is the same as in pthread_support.c except */ + /* for thread stack that is assumed to be large enough. */ + + GC_INNER void GC_start_mark_threads_inner(void) + { + int i; + pthread_attr_t attr; + pthread_t new_thread; +# ifndef NO_MARKER_SPECIAL_SIGMASK + sigset_t set, oldset; +# endif + + GC_ASSERT(I_DONT_HOLD_LOCK()); + if (available_markers_m1 <= 0) return; + /* Skip if parallel markers disabled or already started. */ +# ifdef CAN_HANDLE_FORK + if (GC_parallel) return; + + /* Reset mark_cv state after forking (as in pthread_support.c). */ + { + pthread_cond_t mark_cv_local = PTHREAD_COND_INITIALIZER; + BCOPY(&mark_cv_local, &mark_cv, sizeof(mark_cv)); + } +# endif + + GC_ASSERT(GC_fl_builder_count == 0); + if (0 != pthread_attr_init(&attr)) ABORT("pthread_attr_init failed"); + if (0 != pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) + ABORT("pthread_attr_setdetachstate failed"); + +# ifndef NO_MARKER_SPECIAL_SIGMASK + /* Apply special signal mask to GC marker threads, and don't drop */ + /* user defined signals by GC marker threads. */ + if (sigfillset(&set) != 0) + ABORT("sigfillset failed"); + if (pthread_sigmask(SIG_BLOCK, &set, &oldset) < 0) { + WARN("pthread_sigmask set failed, no markers started," + " errno = %" WARN_PRIdPTR "\n", errno); + GC_markers_m1 = 0; + (void)pthread_attr_destroy(&attr); + return; + } +# endif /* !NO_MARKER_SPECIAL_SIGMASK */ + +# ifdef CAN_HANDLE_FORK + /* To have proper GC_parallel value in GC_help_marker. */ + GC_markers_m1 = available_markers_m1; +# endif + for (i = 0; i < available_markers_m1; ++i) { + marker_last_stack_min[i] = ADDR_LIMIT; + if (0 != pthread_create(&new_thread, &attr, + GC_mark_thread, (void *)(word)i)) { + WARN("Marker thread creation failed\n", 0); + /* Don't try to create other marker threads. */ + GC_markers_m1 = i; + break; + } + } + +# ifndef NO_MARKER_SPECIAL_SIGMASK + /* Restore previous signal mask. */ + if (pthread_sigmask(SIG_SETMASK, &oldset, NULL) < 0) { + WARN("pthread_sigmask restore failed, errno = %" WARN_PRIdPTR "\n", + errno); + } +# endif + + (void)pthread_attr_destroy(&attr); + GC_wait_for_markers_init(); + GC_COND_LOG_PRINTF("Started %d mark helper threads\n", GC_markers_m1); + } + +# ifdef GC_ASSERTIONS + STATIC unsigned long GC_mark_lock_holder = NO_THREAD; +# define SET_MARK_LOCK_HOLDER \ + (void)(GC_mark_lock_holder = NUMERIC_THREAD_ID(pthread_self())) +# define UNSET_MARK_LOCK_HOLDER \ + do { \ + GC_ASSERT(GC_mark_lock_holder \ + == NUMERIC_THREAD_ID(pthread_self())); \ + GC_mark_lock_holder = NO_THREAD; \ + } while (0) +# endif /* GC_ASSERTIONS */ + + static pthread_mutex_t mark_mutex = PTHREAD_MUTEX_INITIALIZER; + + static pthread_cond_t builder_cv = PTHREAD_COND_INITIALIZER; + + /* GC_acquire/release_mark_lock(), GC_wait_builder/marker(), */ + /* GC_wait_for_reclaim(), GC_notify_all_builder/marker() are the same */ + /* as in pthread_support.c except that GC_generic_lock() is not used. */ + +# ifdef LOCK_STATS + volatile AO_t GC_block_count = 0; +# endif + + GC_INNER void GC_acquire_mark_lock(void) + { +# if defined(NUMERIC_THREAD_ID_UNIQUE) && !defined(THREAD_SANITIZER) + GC_ASSERT(GC_mark_lock_holder != NUMERIC_THREAD_ID(pthread_self())); +# endif + if (pthread_mutex_lock(&mark_mutex) != 0) { + ABORT("pthread_mutex_lock failed"); + } +# ifdef LOCK_STATS + (void)AO_fetch_and_add1(&GC_block_count); +# endif + /* GC_generic_lock(&mark_mutex); */ + SET_MARK_LOCK_HOLDER; + } + + GC_INNER void GC_release_mark_lock(void) + { + UNSET_MARK_LOCK_HOLDER; + if (pthread_mutex_unlock(&mark_mutex) != 0) { + ABORT("pthread_mutex_unlock failed"); + } + } + + /* Collector must wait for a freelist builders for 2 reasons: */ + /* 1) Mark bits may still be getting examined without lock. */ + /* 2) Partial free lists referenced only by locals may not be */ + /* scanned correctly, e.g. if they contain "pointer-free" objects, */ + /* since the free-list link may be ignored. */ + STATIC void GC_wait_builder(void) + { + UNSET_MARK_LOCK_HOLDER; + if (pthread_cond_wait(&builder_cv, &mark_mutex) != 0) { + ABORT("pthread_cond_wait failed"); + } + GC_ASSERT(GC_mark_lock_holder == NO_THREAD); + SET_MARK_LOCK_HOLDER; + } + + GC_INNER void GC_wait_for_reclaim(void) + { + GC_acquire_mark_lock(); + while (GC_fl_builder_count > 0) { + GC_wait_builder(); + } + GC_release_mark_lock(); + } + + GC_INNER void GC_notify_all_builder(void) + { + GC_ASSERT(GC_mark_lock_holder == NUMERIC_THREAD_ID(pthread_self())); + if (pthread_cond_broadcast(&builder_cv) != 0) { + ABORT("pthread_cond_broadcast failed"); + } + } + + GC_INNER void GC_wait_marker(void) + { + GC_ASSERT(GC_parallel); + UNSET_MARK_LOCK_HOLDER; + if (pthread_cond_wait(&mark_cv, &mark_mutex) != 0) { + ABORT("pthread_cond_wait failed"); + } + GC_ASSERT(GC_mark_lock_holder == NO_THREAD); + SET_MARK_LOCK_HOLDER; + } + + GC_INNER void GC_notify_all_marker(void) + { + GC_ASSERT(GC_parallel); + if (pthread_cond_broadcast(&mark_cv) != 0) { + ABORT("pthread_cond_broadcast failed"); + } + } + +# else /* ! GC_PTHREADS_PARAMARK */ + +# ifndef MARK_THREAD_STACK_SIZE +# define MARK_THREAD_STACK_SIZE 0 /* default value */ +# endif + + /* mark_mutex_event, builder_cv, mark_cv are initialized in GC_thr_init */ + static HANDLE mark_mutex_event = (HANDLE)0; /* Event with auto-reset. */ + static HANDLE builder_cv = (HANDLE)0; /* Event with manual reset. */ + static HANDLE mark_cv = (HANDLE)0; /* Event with manual reset. */ + + GC_INNER void GC_start_mark_threads_inner(void) + { + int i; + + GC_ASSERT(I_DONT_HOLD_LOCK()); + if (available_markers_m1 <= 0) return; + + GC_ASSERT(GC_fl_builder_count == 0); + /* Initialize GC_marker_cv[] fully before starting the */ + /* first helper thread. */ + for (i = 0; i < GC_markers_m1; ++i) { + if ((GC_marker_cv[i] = CreateEvent(NULL /* attrs */, + TRUE /* isManualReset */, + FALSE /* initialState */, + NULL /* name (A/W) */)) == (HANDLE)0) + ABORT("CreateEvent failed"); + } + + for (i = 0; i < GC_markers_m1; ++i) { +# if defined(MSWINCE) || defined(MSWIN_XBOX1) + HANDLE handle; + DWORD thread_id; + + marker_last_stack_min[i] = ADDR_LIMIT; + /* There is no _beginthreadex() in WinCE. */ + handle = CreateThread(NULL /* lpsa */, + MARK_THREAD_STACK_SIZE /* ignored */, + GC_mark_thread, (LPVOID)(word)i, + 0 /* fdwCreate */, &thread_id); + if (handle == NULL) { + WARN("Marker thread creation failed\n", 0); + /* The most probable failure reason is "not enough memory". */ + /* Don't try to create other marker threads. */ + break; + } else { + /* It's safe to detach the thread. */ + CloseHandle(handle); + } +# else + GC_uintptr_t handle; + unsigned thread_id; + + marker_last_stack_min[i] = ADDR_LIMIT; + handle = _beginthreadex(NULL /* security_attr */, + MARK_THREAD_STACK_SIZE, GC_mark_thread, + (void *)(word)i, 0 /* flags */, &thread_id); + if (!handle || handle == (GC_uintptr_t)-1L) { + WARN("Marker thread creation failed\n", 0); + /* Don't try to create other marker threads. */ + break; + } else {/* We may detach the thread (if handle is of HANDLE type) */ + /* CloseHandle((HANDLE)handle); */ + } +# endif + } + + /* Adjust GC_markers_m1 (and free unused resources) if failed. */ + while (GC_markers_m1 > i) { + GC_markers_m1--; + CloseHandle(GC_marker_cv[GC_markers_m1]); + } + GC_wait_for_markers_init(); + GC_COND_LOG_PRINTF("Started %d mark helper threads\n", GC_markers_m1); + if (i == 0) { + CloseHandle(mark_cv); + CloseHandle(builder_cv); + CloseHandle(mark_mutex_event); + } + } + +# ifdef GC_ASSERTIONS + STATIC DWORD GC_mark_lock_holder = NO_THREAD; +# define SET_MARK_LOCK_HOLDER \ + (void)(GC_mark_lock_holder = GetCurrentThreadId()) +# define UNSET_MARK_LOCK_HOLDER \ + do { \ + GC_ASSERT(GC_mark_lock_holder == GetCurrentThreadId()); \ + GC_mark_lock_holder = NO_THREAD; \ + } while (0) +# endif /* GC_ASSERTIONS */ + + STATIC /* volatile */ LONG GC_mark_mutex_state = 0; + /* Mutex state: 0 - unlocked, */ + /* 1 - locked and no other waiters, */ + /* -1 - locked and waiters may exist. */ + /* Accessed by InterlockedExchange(). */ + + /* #define LOCK_STATS */ +# ifdef LOCK_STATS + volatile AO_t GC_block_count = 0; + volatile AO_t GC_unlocked_count = 0; +# endif + + GC_INNER void GC_acquire_mark_lock(void) + { +# ifndef THREAD_SANITIZER + GC_ASSERT(GC_mark_lock_holder != GetCurrentThreadId()); +# endif + if (InterlockedExchange(&GC_mark_mutex_state, 1 /* locked */) != 0) { +# ifdef LOCK_STATS + (void)AO_fetch_and_add1(&GC_block_count); +# endif + /* Repeatedly reset the state and wait until acquire the lock. */ + while (InterlockedExchange(&GC_mark_mutex_state, + -1 /* locked_and_has_waiters */) != 0) { + if (WaitForSingleObject(mark_mutex_event, INFINITE) == WAIT_FAILED) + ABORT("WaitForSingleObject failed"); + } + } +# ifdef LOCK_STATS + else { + (void)AO_fetch_and_add1(&GC_unlocked_count); + } +# endif + + GC_ASSERT(GC_mark_lock_holder == NO_THREAD); + SET_MARK_LOCK_HOLDER; + } + + GC_INNER void GC_release_mark_lock(void) + { + UNSET_MARK_LOCK_HOLDER; + if (InterlockedExchange(&GC_mark_mutex_state, 0 /* unlocked */) < 0) { + /* wake a waiter */ + if (SetEvent(mark_mutex_event) == FALSE) + ABORT("SetEvent failed"); + } + } + + /* In GC_wait_for_reclaim/GC_notify_all_builder() we emulate POSIX */ + /* cond_wait/cond_broadcast() primitives with WinAPI Event object */ + /* (working in "manual reset" mode). This works here because */ + /* GC_notify_all_builder() is always called holding lock on */ + /* mark_mutex and the checked condition (GC_fl_builder_count == 0) */ + /* is the only one for which broadcasting on builder_cv is performed. */ + + GC_INNER void GC_wait_for_reclaim(void) + { + GC_ASSERT(builder_cv != 0); + for (;;) { + GC_acquire_mark_lock(); + if (GC_fl_builder_count == 0) + break; + if (ResetEvent(builder_cv) == FALSE) + ABORT("ResetEvent failed"); + GC_release_mark_lock(); + if (WaitForSingleObject(builder_cv, INFINITE) == WAIT_FAILED) + ABORT("WaitForSingleObject failed"); + } + GC_release_mark_lock(); + } + + GC_INNER void GC_notify_all_builder(void) + { + GC_ASSERT(GC_mark_lock_holder == GetCurrentThreadId()); + GC_ASSERT(builder_cv != 0); + GC_ASSERT(GC_fl_builder_count == 0); + if (SetEvent(builder_cv) == FALSE) + ABORT("SetEvent failed"); + } + + /* mark_cv is used (for waiting) by a non-helper thread. */ + + GC_INNER void GC_wait_marker(void) + { + HANDLE event = mark_cv; + DWORD thread_id = GetCurrentThreadId(); + int i = GC_markers_m1; + + while (i-- > 0) { + if (GC_marker_Id[i] == thread_id) { + event = GC_marker_cv[i]; + break; + } + } + + if (ResetEvent(event) == FALSE) + ABORT("ResetEvent failed"); + GC_release_mark_lock(); + if (WaitForSingleObject(event, INFINITE) == WAIT_FAILED) + ABORT("WaitForSingleObject failed"); + GC_acquire_mark_lock(); + } + + GC_INNER void GC_notify_all_marker(void) + { + DWORD thread_id = GetCurrentThreadId(); + int i = GC_markers_m1; + + while (i-- > 0) { + /* Notify every marker ignoring self (for efficiency). */ + if (SetEvent(GC_marker_Id[i] != thread_id ? GC_marker_cv[i] : + mark_cv) == FALSE) + ABORT("SetEvent failed"); + } + } + +# endif /* ! GC_PTHREADS_PARAMARK */ + +#endif /* PARALLEL_MARK */ + + /* We have no DllMain to take care of new threads. Thus we */ + /* must properly intercept thread creation. */ + + typedef struct { + LPTHREAD_START_ROUTINE start; + LPVOID param; + } thread_args; + + STATIC void * GC_CALLBACK GC_win32_start_inner(struct GC_stack_base *sb, + void *arg) + { + void * ret = NULL; + LPTHREAD_START_ROUTINE start = ((thread_args *)arg)->start; + LPVOID param = ((thread_args *)arg)->param; + + GC_register_my_thread(sb); /* This waits for an in-progress GC. */ + +# ifdef DEBUG_THREADS + GC_log_printf("thread 0x%lx starting...\n", (long)GetCurrentThreadId()); +# endif + + GC_free(arg); + + /* Clear the thread entry even if we exit with an exception. */ + /* This is probably pointless, since an uncaught exception is */ + /* supposed to result in the process being killed. */ +#if !defined(__GNUC__) && !defined(NO_CRT) + __try +# endif + { + ret = (void *)(word)(*start)(param); + } +#if !defined(__GNUC__) && !defined(NO_CRT) + __finally +# endif + { + GC_unregister_my_thread(); + } + +# ifdef DEBUG_THREADS + GC_log_printf("thread 0x%lx returned from start routine\n", + (long)GetCurrentThreadId()); +# endif + return ret; + } + + STATIC DWORD WINAPI GC_win32_start(LPVOID arg) + { + return (DWORD)(word)GC_call_with_stack_base(GC_win32_start_inner, arg); + } + + GC_API HANDLE WINAPI GC_CreateThread( + LPSECURITY_ATTRIBUTES lpThreadAttributes, + GC_WIN32_SIZE_T dwStackSize, + LPTHREAD_START_ROUTINE lpStartAddress, + LPVOID lpParameter, DWORD dwCreationFlags, + LPDWORD lpThreadId) + { + if (!EXPECT(parallel_initialized, TRUE)) + GC_init_parallel(); + /* make sure GC is initialized (i.e. main thread is */ + /* attached, tls initialized). */ + +# ifdef DEBUG_THREADS + GC_log_printf("About to create a thread from 0x%lx\n", + (long)GetCurrentThreadId()); +# endif + if (GC_win32_dll_threads) { + return CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress, + lpParameter, dwCreationFlags, lpThreadId); + } else { + thread_args *args = + (thread_args *)GC_malloc_uncollectable(sizeof(thread_args)); + /* Handed off to and deallocated by child thread. */ + HANDLE thread_h; + + if (NULL == args) { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return NULL; + } + + /* set up thread arguments */ + args -> start = lpStartAddress; + args -> param = lpParameter; + GC_dirty(args); + + set_need_to_lock(); + thread_h = CreateThread(lpThreadAttributes, dwStackSize, GC_win32_start, + args, dwCreationFlags, lpThreadId); + if (thread_h == 0) GC_free(args); + return thread_h; + } + } + + GC_API DECLSPEC_NORETURN void WINAPI GC_ExitThread(DWORD dwExitCode) + { + GC_unregister_my_thread(); + ExitThread(dwExitCode); + } + +# if !defined(NO_CRT) && !defined(CYGWIN32) && !defined(MSWINCE) && !defined(MSWIN_XBOX1) + GC_API GC_uintptr_t GC_CALL GC_beginthreadex( + void *security, unsigned stack_size, + unsigned (__stdcall *start_address)(void *), + void *arglist, unsigned initflag, + unsigned *thrdaddr) + { + if (!EXPECT(parallel_initialized, TRUE)) + GC_init_parallel(); + /* make sure GC is initialized (i.e. main thread is */ + /* attached, tls initialized). */ +# ifdef DEBUG_THREADS + GC_log_printf("About to create a thread from 0x%lx\n", + (long)GetCurrentThreadId()); +# endif + + if (GC_win32_dll_threads) { + return _beginthreadex(security, stack_size, start_address, + arglist, initflag, thrdaddr); + } else { + GC_uintptr_t thread_h; + thread_args *args = + (thread_args *)GC_malloc_uncollectable(sizeof(thread_args)); + /* Handed off to and deallocated by child thread. */ + + if (NULL == args) { + /* MSDN docs say _beginthreadex() returns 0 on error and sets */ + /* errno to either EAGAIN (too many threads) or EINVAL (the */ + /* argument is invalid or the stack size is incorrect), so we */ + /* set errno to EAGAIN on "not enough memory". */ + errno = EAGAIN; + return 0; + } + + /* set up thread arguments */ + args -> start = (LPTHREAD_START_ROUTINE)start_address; + args -> param = arglist; + GC_dirty(args); + + set_need_to_lock(); + thread_h = _beginthreadex(security, stack_size, + (unsigned (__stdcall *)(void *))GC_win32_start, + args, initflag, thrdaddr); + if (thread_h == 0) GC_free(args); + return thread_h; + } + } + + GC_API void GC_CALL GC_endthreadex(unsigned retval) + { + GC_unregister_my_thread(); + _endthreadex(retval); + } +# endif /* !CYGWIN32 && !MSWINCE && !MSWIN_XBOX1 */ + +#ifdef GC_WINMAIN_REDIRECT + /* This might be useful on WinCE. Shouldn't be used with GC_DLL. */ + +# if defined(MSWINCE) && defined(UNDER_CE) +# define WINMAIN_LPTSTR LPWSTR +# else +# define WINMAIN_LPTSTR LPSTR +# endif + + /* This is defined in gc.h. */ +# undef WinMain + + /* Defined outside GC by an application. */ + int WINAPI GC_WinMain(HINSTANCE, HINSTANCE, WINMAIN_LPTSTR, int); + + typedef struct { + HINSTANCE hInstance; + HINSTANCE hPrevInstance; + WINMAIN_LPTSTR lpCmdLine; + int nShowCmd; + } main_thread_args; + + static DWORD WINAPI main_thread_start(LPVOID arg) + { + main_thread_args * args = (main_thread_args *) arg; + return (DWORD)GC_WinMain(args->hInstance, args->hPrevInstance, + args->lpCmdLine, args->nShowCmd); + } + + STATIC void * GC_waitForSingleObjectInfinite(void * handle) + { + return (void *)(word)WaitForSingleObject((HANDLE)handle, INFINITE); + } + +# ifndef WINMAIN_THREAD_STACK_SIZE +# define WINMAIN_THREAD_STACK_SIZE 0 /* default value */ +# endif + + int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, + WINMAIN_LPTSTR lpCmdLine, int nShowCmd) + { + DWORD exit_code = 1; + + main_thread_args args = { + hInstance, hPrevInstance, lpCmdLine, nShowCmd + }; + HANDLE thread_h; + DWORD thread_id; + + /* initialize everything */ + GC_INIT(); + + /* start the main thread */ + thread_h = GC_CreateThread(NULL /* lpsa */, + WINMAIN_THREAD_STACK_SIZE /* ignored on WinCE */, + main_thread_start, &args, 0 /* fdwCreate */, + &thread_id); + + if (thread_h != NULL) { + if ((DWORD)(word)GC_do_blocking(GC_waitForSingleObjectInfinite, + (void *)thread_h) == WAIT_FAILED) + ABORT("WaitForSingleObject(main_thread) failed"); + GetExitCodeThread (thread_h, &exit_code); + CloseHandle (thread_h); + } else { + ABORT("GC_CreateThread(main_thread) failed"); + } + +# ifdef MSWINCE + GC_deinit(); +# endif + return (int) exit_code; + } + +#endif /* GC_WINMAIN_REDIRECT */ + +GC_INNER void GC_thr_init(void) +{ + struct GC_stack_base sb; +# ifdef GC_ASSERTIONS + int sb_result; +# endif + + GC_ASSERT(I_HOLD_LOCK()); + if (GC_thr_initialized) return; + + GC_ASSERT((word)&GC_threads % sizeof(word) == 0); + GC_main_thread = GetCurrentThreadId(); + GC_thr_initialized = TRUE; + +# ifdef CAN_HANDLE_FORK + /* Prepare for forks if requested. */ + if (GC_handle_fork) { +# ifdef CAN_CALL_ATFORK + if (pthread_atfork(fork_prepare_proc, fork_parent_proc, + fork_child_proc) == 0) { + /* Handlers successfully registered. */ + GC_handle_fork = 1; + } else +# endif + /* else */ if (GC_handle_fork != -1) + ABORT("pthread_atfork failed"); + } +# endif + + /* Add the initial thread, so we can stop it. */ +# ifdef GC_ASSERTIONS + sb_result = +# endif + GC_get_stack_base(&sb); + GC_ASSERT(sb_result == GC_SUCCESS); + +# if defined(PARALLEL_MARK) + { + char * markers_string = GETENV("GC_MARKERS"); + int markers; + + if (markers_string != NULL) { + markers = atoi(markers_string); + if (markers <= 0 || markers > MAX_MARKERS) { + WARN("Too big or invalid number of mark threads: %" WARN_PRIdPTR + "; using maximum threads\n", (signed_word)markers); + markers = MAX_MARKERS; + } + } else { +# ifdef MSWINCE + /* There is no GetProcessAffinityMask() in WinCE. */ + /* GC_sysinfo is already initialized. */ + markers = (int)GC_sysinfo.dwNumberOfProcessors; +# else +# ifdef _WIN64 + DWORD_PTR procMask = 0; + DWORD_PTR sysMask; +# else + DWORD procMask = 0; + DWORD sysMask; +# endif + int ncpu = 0; + if ( +# ifdef __cplusplus + GetProcessAffinityMask(GetCurrentProcess(), &procMask, &sysMask) +# else + /* Cast args to void* for compatibility with some old SDKs. */ + GetProcessAffinityMask(GetCurrentProcess(), + (void *)&procMask, (void *)&sysMask) +# endif + && procMask) { + do { + ncpu++; + } while ((procMask &= procMask - 1) != 0); + } + markers = ncpu; +# endif +# if defined(GC_MIN_MARKERS) && !defined(CPPCHECK) + /* This is primarily for testing on systems without getenv(). */ + if (markers < GC_MIN_MARKERS) + markers = GC_MIN_MARKERS; +# endif + if (markers > MAX_MARKERS) + markers = MAX_MARKERS; /* silently limit the value */ + } + available_markers_m1 = markers - 1; + } + + /* Check whether parallel mode could be enabled. */ + { + if (GC_win32_dll_threads || available_markers_m1 <= 0) { + /* Disable parallel marking. */ + GC_parallel = FALSE; + GC_COND_LOG_PRINTF( + "Single marker thread, turning off parallel marking\n"); + } else { +# ifndef GC_PTHREADS_PARAMARK + /* Initialize Win32 event objects for parallel marking. */ + mark_mutex_event = CreateEvent(NULL /* attrs */, + FALSE /* isManualReset */, + FALSE /* initialState */, NULL /* name */); + builder_cv = CreateEvent(NULL /* attrs */, + TRUE /* isManualReset */, + FALSE /* initialState */, NULL /* name */); + mark_cv = CreateEvent(NULL /* attrs */, TRUE /* isManualReset */, + FALSE /* initialState */, NULL /* name */); + if (mark_mutex_event == (HANDLE)0 || builder_cv == (HANDLE)0 + || mark_cv == (HANDLE)0) + ABORT("CreateEvent failed"); +# endif + /* Disable true incremental collection, but generational is OK. */ + GC_time_limit = GC_TIME_UNLIMITED; + } + } +# endif /* PARALLEL_MARK */ + + GC_ASSERT(0 == GC_lookup_thread_inner(GC_main_thread)); + GC_register_my_thread_inner(&sb, GC_main_thread); +} + +#ifdef GC_PTHREADS + + struct start_info { + void *(*start_routine)(void *); + void *arg; + GC_bool detached; + }; + + GC_API int GC_pthread_join(pthread_t pthread_id, void **retval) + { + int result; + GC_thread t; + DCL_LOCK_STATE; + + GC_ASSERT(!GC_win32_dll_threads); +# ifdef DEBUG_THREADS + GC_log_printf("thread %p(0x%lx) is joining thread %p\n", + (void *)GC_PTHREAD_PTRVAL(pthread_self()), + (long)GetCurrentThreadId(), + (void *)GC_PTHREAD_PTRVAL(pthread_id)); +# endif + + /* Thread being joined might not have registered itself yet. */ + /* After the join, thread id may have been recycled. */ + /* FIXME: It would be better if this worked more like */ + /* pthread_support.c. */ +# ifndef GC_WIN32_PTHREADS + while ((t = GC_lookup_pthread(pthread_id)) == 0) + Sleep(10); +# endif + result = pthread_join(pthread_id, retval); + if (0 == result) { +# ifdef GC_WIN32_PTHREADS + /* pthreads-win32 and winpthreads id are unique (not recycled). */ + t = GC_lookup_pthread(pthread_id); + if (NULL == t) ABORT("Thread not registered"); +# endif + + LOCK(); + if ((t -> flags & FINISHED) != 0) { + GC_delete_gc_thread_no_free(t); + GC_INTERNAL_FREE(t); + } + UNLOCK(); + } + +# ifdef DEBUG_THREADS + GC_log_printf("thread %p(0x%lx) join with thread %p %s\n", + (void *)GC_PTHREAD_PTRVAL(pthread_self()), + (long)GetCurrentThreadId(), + (void *)GC_PTHREAD_PTRVAL(pthread_id), + result != 0 ? "failed" : "succeeded"); +# endif + return result; + } + + /* Cygwin-pthreads calls CreateThread internally, but it's not easily */ + /* interceptable by us..., so intercept pthread_create instead. */ + GC_API int GC_pthread_create(pthread_t *new_thread, + GC_PTHREAD_CREATE_CONST pthread_attr_t *attr, + void *(*start_routine)(void *), void *arg) + { + int result; + struct start_info * si; + + if (!EXPECT(parallel_initialized, TRUE)) + GC_init_parallel(); + /* make sure GC is initialized (i.e. main thread is attached) */ + GC_ASSERT(!GC_win32_dll_threads); + + /* This is otherwise saved only in an area mmapped by the thread */ + /* library, which isn't visible to the collector. */ + si = (struct start_info *)GC_malloc_uncollectable( + sizeof(struct start_info)); + if (NULL == si) + return EAGAIN; + + si -> start_routine = start_routine; + si -> arg = arg; + GC_dirty(si); + if (attr != 0 && + pthread_attr_getdetachstate(attr, &si->detached) + == PTHREAD_CREATE_DETACHED) { + si->detached = TRUE; + } + +# ifdef DEBUG_THREADS + GC_log_printf("About to create a thread from %p(0x%lx)\n", + (void *)GC_PTHREAD_PTRVAL(pthread_self()), + (long)GetCurrentThreadId()); +# endif + set_need_to_lock(); + result = pthread_create(new_thread, attr, GC_pthread_start, si); + + if (result) { /* failure */ + GC_free(si); + } + return(result); + } + + STATIC void * GC_CALLBACK GC_pthread_start_inner(struct GC_stack_base *sb, + void * arg) + { + struct start_info * si = (struct start_info *)arg; + void * result; + void *(*start)(void *); + void *start_arg; + DWORD thread_id = GetCurrentThreadId(); + pthread_t pthread_id = pthread_self(); + GC_thread me; + DCL_LOCK_STATE; + +# ifdef DEBUG_THREADS + GC_log_printf("thread %p(0x%x) starting...\n", + (void *)GC_PTHREAD_PTRVAL(pthread_id), (int)thread_id); +# endif + + GC_ASSERT(!GC_win32_dll_threads); + /* If a GC occurs before the thread is registered, that GC will */ + /* ignore this thread. That's fine, since it will block trying to */ + /* acquire the allocation lock, and won't yet hold interesting */ + /* pointers. */ + LOCK(); + /* We register the thread here instead of in the parent, so that */ + /* we don't need to hold the allocation lock during pthread_create. */ + me = GC_register_my_thread_inner(sb, thread_id); + SET_PTHREAD_MAP_CACHE(pthread_id, thread_id); + GC_ASSERT(me != &first_thread); + me -> pthread_id = pthread_id; + if (si->detached) me -> flags |= DETACHED; + UNLOCK(); + + start = si -> start_routine; + start_arg = si -> arg; + + GC_free(si); /* was allocated uncollectible */ + + pthread_cleanup_push(GC_thread_exit_proc, (void *)me); + result = (*start)(start_arg); + me -> status = result; + GC_dirty(me); + pthread_cleanup_pop(1); + +# ifdef DEBUG_THREADS + GC_log_printf("thread %p(0x%x) returned from start routine\n", + (void *)GC_PTHREAD_PTRVAL(pthread_id), (int)thread_id); +# endif + return(result); + } + + STATIC void * GC_pthread_start(void * arg) + { + return GC_call_with_stack_base(GC_pthread_start_inner, arg); + } + + STATIC void GC_thread_exit_proc(void *arg) + { + GC_thread me = (GC_thread)arg; + DCL_LOCK_STATE; + + GC_ASSERT(!GC_win32_dll_threads); +# ifdef DEBUG_THREADS + GC_log_printf("thread %p(0x%lx) called pthread_exit()\n", + (void *)GC_PTHREAD_PTRVAL(pthread_self()), + (long)GetCurrentThreadId()); +# endif + + LOCK(); + GC_wait_for_gc_completion(FALSE); +# if defined(THREAD_LOCAL_ALLOC) + GC_ASSERT(GC_getspecific(GC_thread_key) == &me->tlfs); + GC_destroy_thread_local(&(me->tlfs)); +# endif + if (me -> flags & DETACHED) { + GC_delete_thread(GetCurrentThreadId()); + } else { + /* deallocate it as part of join */ + me -> flags |= FINISHED; + } +# if defined(THREAD_LOCAL_ALLOC) + /* It is required to call remove_specific defined in specific.c. */ + GC_remove_specific(GC_thread_key); +# endif + UNLOCK(); + } + +# ifndef GC_NO_PTHREAD_SIGMASK + /* Win32 pthread does not support sigmask. */ + /* So, nothing required here... */ + GC_API int GC_pthread_sigmask(int how, const sigset_t *set, + sigset_t *oset) + { + return pthread_sigmask(how, set, oset); + } +# endif /* !GC_NO_PTHREAD_SIGMASK */ + + GC_API int GC_pthread_detach(pthread_t thread) + { + int result; + GC_thread t; + DCL_LOCK_STATE; + + GC_ASSERT(!GC_win32_dll_threads); + /* The thread might not have registered itself yet. */ + /* TODO: Wait for registration of the created thread in pthread_create. */ + while ((t = GC_lookup_pthread(thread)) == NULL) + Sleep(10); + result = pthread_detach(thread); + if (result == 0) { + LOCK(); + t -> flags |= DETACHED; + /* Here the pthread thread id may have been recycled. */ + if ((t -> flags & FINISHED) != 0) { + GC_delete_gc_thread_no_free(t); + GC_INTERNAL_FREE(t); + } + UNLOCK(); + } + return result; + } + +#elif !defined(GC_NO_THREADS_DISCOVERY) + /* We avoid acquiring locks here, since this doesn't seem to be */ + /* preemptible. This may run with an uninitialized collector, in */ + /* which case we don't do much. This implies that no threads other */ + /* than the main one should be created with an uninitialized */ + /* collector. (The alternative of initializing the collector here */ + /* seems dangerous, since DllMain is limited in what it can do.) */ + +# ifdef GC_INSIDE_DLL + /* Export only if needed by client. */ + GC_API +# else +# define GC_DllMain DllMain +# endif + BOOL WINAPI GC_DllMain(HINSTANCE inst GC_ATTR_UNUSED, ULONG reason, + LPVOID reserved GC_ATTR_UNUSED) + { + DWORD thread_id; + + /* Note that GC_use_threads_discovery should be called by the */ + /* client application at start-up to activate automatic thread */ + /* registration (it is the default GC behavior since v7.0alpha7); */ + /* to always have automatic thread registration turned on, the GC */ + /* should be compiled with -D GC_DISCOVER_TASK_THREADS. */ + if (!GC_win32_dll_threads && parallel_initialized) return TRUE; + + switch (reason) { + case DLL_THREAD_ATTACH: +# ifdef PARALLEL_MARK + /* Don't register marker threads. */ + if (GC_parallel) { + /* We could reach here only if parallel_initialized == FALSE. */ + break; + } +# endif + /* FALLTHRU */ + case DLL_PROCESS_ATTACH: + /* This may run with the collector uninitialized. */ + thread_id = GetCurrentThreadId(); + if (parallel_initialized && GC_main_thread != thread_id) { +# ifdef PARALLEL_MARK + ABORT("Cannot initialize parallel marker from DllMain"); +# else + struct GC_stack_base sb; + /* Don't lock here. */ +# ifdef GC_ASSERTIONS + int sb_result = +# endif + GC_get_stack_base(&sb); + GC_ASSERT(sb_result == GC_SUCCESS); + GC_register_my_thread_inner(&sb, thread_id); +# endif + } /* o.w. we already did it during GC_thr_init, called by GC_init */ + break; + + case DLL_THREAD_DETACH: + /* We are hopefully running in the context of the exiting thread. */ + GC_ASSERT(parallel_initialized); + if (GC_win32_dll_threads) { + GC_delete_thread(GetCurrentThreadId()); + } + break; + + case DLL_PROCESS_DETACH: + if (GC_win32_dll_threads) { + int i; + int my_max = (int)GC_get_max_thread_index(); + + for (i = 0; i <= my_max; ++i) { + if (AO_load(&(dll_thread_table[i].tm.in_use))) + GC_delete_gc_thread_no_free(&dll_thread_table[i]); + } + GC_deinit(); + } + break; + } + return TRUE; + } +#endif /* !GC_NO_THREADS_DISCOVERY && !GC_PTHREADS */ + +/* Perform all initializations, including those that */ +/* may require allocation. */ +/* Called without allocation lock. */ +/* Must be called before a second thread is created. */ +GC_INNER void GC_init_parallel(void) +{ +# if defined(THREAD_LOCAL_ALLOC) + GC_thread me; + DCL_LOCK_STATE; +# endif + + if (parallel_initialized) return; + parallel_initialized = TRUE; + /* GC_init() calls us back, so set flag first. */ + + if (!GC_is_initialized) GC_init(); +# if defined(CPPCHECK) && !defined(GC_NO_THREADS_DISCOVERY) + GC_noop1((word)&GC_DllMain); +# endif + if (GC_win32_dll_threads) { + set_need_to_lock(); + /* Cannot intercept thread creation. Hence we don't know if */ + /* other threads exist. However, client is not allowed to */ + /* create other threads before collector initialization. */ + /* Thus it's OK not to lock before this. */ + } + /* Initialize thread local free lists if used. */ +# if defined(THREAD_LOCAL_ALLOC) + LOCK(); + me = GC_lookup_thread_inner(GetCurrentThreadId()); + CHECK_LOOKUP_MY_THREAD(me); + GC_init_thread_local(&me->tlfs); + UNLOCK(); +# endif +} + +#if defined(USE_PTHREAD_LOCKS) + /* Support for pthread locking code. */ + /* pthread_mutex_trylock may not win here, */ + /* due to builtin support for spinning first? */ + + GC_INNER void GC_lock(void) + { + pthread_mutex_lock(&GC_allocate_ml); + } +#endif /* USE_PTHREAD_LOCKS */ + +#if defined(THREAD_LOCAL_ALLOC) + + /* Add thread-local allocation support. VC++ uses __declspec(thread). */ + + /* We must explicitly mark ptrfree and gcj free lists, since the free */ + /* list links wouldn't otherwise be found. We also set them in the */ + /* normal free lists, since that involves touching less memory than if */ + /* we scanned them normally. */ + GC_INNER void GC_mark_thread_local_free_lists(void) + { + int i; + GC_thread p; + + for (i = 0; i < THREAD_TABLE_SZ; ++i) { + for (p = GC_threads[i]; 0 != p; p = p -> tm.next) { + if (!KNOWN_FINISHED(p)) { +# ifdef DEBUG_THREADS + GC_log_printf("Marking thread locals for 0x%x\n", (int)p -> id); +# endif + GC_mark_thread_local_fls_for(&(p->tlfs)); + } + } + } + } + +# if defined(GC_ASSERTIONS) + /* Check that all thread-local free-lists are completely marked. */ + /* also check that thread-specific-data structures are marked. */ + void GC_check_tls(void) + { + int i; + GC_thread p; + + for (i = 0; i < THREAD_TABLE_SZ; ++i) { + for (p = GC_threads[i]; 0 != p; p = p -> tm.next) { + if (!KNOWN_FINISHED(p)) + GC_check_tls_for(&(p->tlfs)); + } + } +# if defined(USE_CUSTOM_SPECIFIC) + if (GC_thread_key != 0) + GC_check_tsd_marks(GC_thread_key); +# endif + } +# endif /* GC_ASSERTIONS */ + +#endif /* THREAD_LOCAL_ALLOC ... */ + +# ifndef GC_NO_THREAD_REDIRECTS + /* Restore thread calls redirection. */ +# define CreateThread GC_CreateThread +# define ExitThread GC_ExitThread +# undef _beginthreadex +# define _beginthreadex GC_beginthreadex +# undef _endthreadex +# define _endthreadex GC_endthreadex +# endif /* !GC_NO_THREAD_REDIRECTS */ + +#endif /* GC_WIN32_THREADS */ diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/README b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/README new file mode 100644 index 000000000..0de3ce74f --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/README @@ -0,0 +1,4 @@ +gc.def should probably be removed completely. + +I removed an apparently erroneous line for GC_CreateThread. Unfortunately +gc.def is referenced in various other places I cannot easily edit. -HB diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/gc.def b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/gc.def new file mode 100644 index 000000000..2853518fe --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/gc.def @@ -0,0 +1,2 @@ +EXPORTS + GC_version DATA diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/gc.rc b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/gc.rc new file mode 100644 index 000000000..7de7af497 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/gc.rc @@ -0,0 +1 @@ +#include "gc.ver" diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/gc.ver b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/gc.ver new file mode 100644 index 000000000..44a7af386 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/gc.ver @@ -0,0 +1,81 @@ +#include +#include + +#include "../include/gc_version.h" + +#define GC_VERSION_REVISION 0 + +#define GC_VERSION ((GC_VERSION_MAJOR) * 100 + GC_VERSION_MINOR) +#define GC_FULL_VERSION ((GC_VERSION) * 10000 + GC_VERSION_MICRO) + +#ifndef __T +# ifdef UNICODE +# define __T(x) L ## x +# else +# define __T(x) x +# endif +#endif + +#define PP_TSTR(x) __T(#x) +#define PP_EVAL_TSTR(x) PP_TSTR(x) + +#define GC_VERSION_STR PP_EVAL_TSTR(GC_VERSION_MAJOR) __T(".") PP_EVAL_TSTR(GC_VERSION_MINOR) +#define GC_FULL_VERSION_STR PP_EVAL_TSTR(GC_VERSION_MAJOR) __T(".") PP_EVAL_TSTR(GC_VERSION_MINOR) __T(".") PP_EVAL_TSTR(GC_VERSION_MICRO) __T(".") PP_EVAL_TSTR(GC_VERSION_REVISION) +#define GC_FULL_VERSION_CSV GC_VERSION_MAJOR, GC_VERSION_MINOR, GC_VERSION_MICRO, GC_VERSION_REVISION + +#ifdef _DEBUG +#define VER_DEBUG VS_FF_DEBUG +#else +#define VER_DEBUG 0 +#endif + +#ifdef _BETA +#define VER_PRERELEASE VS_FF_PRERELEASE +#else +#define VER_PRERELEASE 0 +#endif + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) + +VS_VERSION_INFO VERSIONINFO + FILEVERSION GC_FULL_VERSION_CSV + PRODUCTVERSION GC_FULL_VERSION_CSV + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS VER_DEBUG | VER_PRERELEASE + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "\ +Hans-J. Boehm, \ +Alan J. Demers, \ +Xerox Corporation, \ +Silicon Graphics, \ +and Hewlett-Packard Company. \ +\0" + VALUE "LegalCopyright", "\ +Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers. \ +Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. \ +Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. \ +Copyright (c) 1999-2004 by Hewlett-Packard. All rights reserved. \ +\0" + + VALUE "OriginalFilename", "GC.DLL\0" + VALUE "InternalName", "GC\0" + VALUE "FileDescription", "Conservative Garbage Collector for C and C++\0" + VALUE "FileVersion", GC_FULL_VERSION_STR + + VALUE "ProductName", "Conservative Garbage Collector for C and C++\0" + VALUE "ProductVersion", GC_FULL_VERSION_STR + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/stdafx.c b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/stdafx.c new file mode 100644 index 000000000..fd4f341c7 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/stdafx.c @@ -0,0 +1 @@ +#include "stdafx.h" diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/stdafx.h b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/stdafx.h new file mode 100644 index 000000000..7f5dcf1c3 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/stdafx.h @@ -0,0 +1,15 @@ +#define WIN32_LEAN_AND_MEAN +#include + +#pragma warning(error: 4013) // function undefined; assuming extern returning int + +#ifdef _MT +# define GC_THREADS 1 +#endif + +#ifdef _DEBUG +# define GC_DEBUG +#endif + +#define SAVE_CALL_CHAIN +#define SAVE_CALL_COUNT 8 diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/all.dsp b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/all.dsp new file mode 100644 index 000000000..b195df6d6 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/all.dsp @@ -0,0 +1,63 @@ +# Microsoft Developer Studio Project File - Name="all" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Generic Project" 0x010a + +CFG=all - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "all.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "all.mak" CFG="all - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "all - Win32 Release" (based on "Win32 (x86) Generic Project") +!MESSAGE "all - Win32 Debug" (based on "Win32 (x86) Generic Project") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +MTL=midl.exe + +!IF "$(CFG)" == "all - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "..\..\..\bin" +# PROP Intermediate_Dir "..\..\..\obj\Release" +# PROP Target_Dir "" + +!ELSEIF "$(CFG)" == "all - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "..\..\..\bin" +# PROP Intermediate_Dir "..\..\..\obj\Debug" +# PROP Target_Dir "" + +!ENDIF + +# Begin Target + +# Name "all - Win32 Release" +# Name "all - Win32 Debug" +# End Target +# End Project diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/gc.dsp b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/gc.dsp new file mode 100644 index 000000000..b28f898ce --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/gc.dsp @@ -0,0 +1,328 @@ +# Microsoft Developer Studio Project File - Name="gc" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=gc - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "gc.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "gc.mak" CFG="gc - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "gc - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "gc - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "gc - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "..\..\..\bin" +# PROP Intermediate_Dir "..\..\..\obj\Release\gc" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GC_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /I "..\..\include" /FI"stdafx.h" /D "NDEBUG" /D "_WINDOWS" /D "_USRDLL" /D "GC_DLL" /D "WIN32" /D "_MBCS" /D "GC_THREADS" /Yu"stdafx.h" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /i "..\..\include" /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /base:"0x37C30000" /subsystem:console /dll /debug /machine:I386 /out:"..\..\..\bin/gc60.dll" /implib:"..\..\..\lib/gc.lib" /opt:ref /release +# SUBTRACT LINK32 /pdb:none +# Begin Special Build Tool +OutDir=.\..\..\..\bin +SOURCE="$(InputPath)" +PostBuild_Cmds=del $(OutDir)\..\lib\gc.exp +# End Special Build Tool + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "..\..\..\bin" +# PROP Intermediate_Dir "..\..\..\obj\Debug\gc" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GC_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /I "..\..\include" /FI"stdafx.h" /D "_DEBUG" /D "_WINDOWS" /D "_USRDLL" /D "GC_DLL" /D "WIN32" /D "_MBCS" /D "GC_THREADS" /Yu"stdafx.h" /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /i "..\..\include" /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo /o"..\..\..\bin/gcd.bsc" +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /base:"0x37C30000" /subsystem:console /dll /incremental:no /debug /machine:I386 /out:"..\..\..\bin/gc60d.dll" /implib:"..\..\..\lib/gcd.lib" /pdbtype:sept +# SUBTRACT LINK32 /pdb:none +# Begin Special Build Tool +OutDir=.\..\..\..\bin +SOURCE="$(InputPath)" +PostBuild_Cmds=del $(OutDir)\..\lib\gcd.exp +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "gc - Win32 Release" +# Name "gc - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "c;cpp;cc;cxx;tcc;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\allchblk.c +# End Source File +# Begin Source File + +SOURCE=..\..\alloc.c +# End Source File +# Begin Source File + +SOURCE=..\..\backgraph.c +# End Source File +# Begin Source File + +SOURCE=..\..\blacklst.c +# End Source File +# Begin Source File + +SOURCE=..\..\dbg_mlc.c +# End Source File +# Begin Source File + +SOURCE=..\..\gcj_mlc.c +# End Source File +# Begin Source File + +SOURCE=..\..\fnlz_mlc.c +# End Source File +# Begin Source File + +SOURCE=..\..\dyn_load.c +# End Source File +# Begin Source File + +SOURCE=..\..\finalize.c +# End Source File +# Begin Source File + +SOURCE=..\..\headers.c +# End Source File +# Begin Source File + +SOURCE=..\..\mach_dep.c +# End Source File +# Begin Source File + +SOURCE=..\..\malloc.c +# End Source File +# Begin Source File + +SOURCE=..\..\mallocx.c +# End Source File +# Begin Source File + +SOURCE=..\..\mark.c +# End Source File +# Begin Source File + +SOURCE=..\..\mark_rts.c +# End Source File +# Begin Source File + +SOURCE=..\..\misc.c +# End Source File +# Begin Source File + +SOURCE=..\..\extra\msvc_dbg.c +# End Source File +# Begin Source File + +SOURCE=..\..\new_hblk.c +# End Source File +# Begin Source File + +SOURCE=..\..\obj_map.c +# End Source File +# Begin Source File + +SOURCE=..\..\os_dep.c +# End Source File +# Begin Source File + +SOURCE=..\..\ptr_chck.c +# End Source File +# Begin Source File + +SOURCE=..\..\reclaim.c +# End Source File +# Begin Source File + +SOURCE=..\stdafx.c +# ADD CPP /Yc"stdafx.h" +# End Source File +# Begin Source File + +SOURCE=..\..\typd_mlc.c +# End Source File +# Begin Source File + +SOURCE=..\..\win32_threads.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hh;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\..\include\gc.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gc_allocator.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gc_backptr.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gc_config_macros.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gc_cpp.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gc_gcj.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\private\gc_hdrs.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gc_inl.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gc_inline.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\private\gc_locks.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gc_mark.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gc_disclaim.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\private\gc_pmark.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\private\gc_priv.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gc_pthread_redirects.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gc_typed.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\private\gcconfig.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\javaxfc.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\leak_detector.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\private\msvc_dbg.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gc_alloc_ptrs.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\new_gc_alloc.h +# End Source File +# Begin Source File + +SOURCE=..\stdafx.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gc_version.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\weakpointer.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=..\gc.def +# End Source File +# Begin Source File + +SOURCE=..\gc.rc +# End Source File +# Begin Source File + +SOURCE=..\gc.ver +# End Source File +# End Group +# End Target +# End Project diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/gc.dsw b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/gc.dsw new file mode 100644 index 000000000..c0f26184d --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/gc.dsw @@ -0,0 +1,194 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "all"=".\all.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name gc + End Project Dependency + Begin Project Dependency + Project_Dep_Name libgc + End Project Dependency + Begin Project Dependency + Project_Dep_Name libgcmt + End Project Dependency +}}} + +############################################################################### + +Project: "gc"=".\gc.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "libgc"=".\libgc.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "libgcmt"=".\libgcmt.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "test"=".\test.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name test_gc + End Project Dependency + Begin Project Dependency + Project_Dep_Name test_libgc + End Project Dependency + Begin Project Dependency + Project_Dep_Name test_libgcmt + End Project Dependency + Begin Project Dependency + Project_Dep_Name test_leak_gc + End Project Dependency + Begin Project Dependency + Project_Dep_Name test_leak_libgc + End Project Dependency + Begin Project Dependency + Project_Dep_Name test_leak_libgcmt + End Project Dependency +}}} + +############################################################################### + +Project: "test_gc"=".\test_gc.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name gc + End Project Dependency +}}} + +############################################################################### + +Project: "test_leak_gc"=".\test_leak_gc.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name gc + End Project Dependency +}}} + +############################################################################### + +Project: "test_leak_libgc"=".\test_leak_libgc.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgc + End Project Dependency +}}} + +############################################################################### + +Project: "test_leak_libgcmt"=".\test_leak_libgcmt.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgcmt + End Project Dependency +}}} + +############################################################################### + +Project: "test_libgc"=".\test_libgc.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgc + End Project Dependency +}}} + +############################################################################### + +Project: "test_libgcmt"=".\test_libgcmt.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgcmt + End Project Dependency +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/libgc.dsp b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/libgc.dsp new file mode 100644 index 000000000..5b323f16e --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/libgc.dsp @@ -0,0 +1,265 @@ +# Microsoft Developer Studio Project File - Name="libgc" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=libgc - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "libgc.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "libgc.mak" CFG="libgc - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "libgc - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "libgc - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "libgc - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "..\..\..\lib" +# PROP Intermediate_Dir "..\..\..\obj\Release\libgc" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /W3 /GX /Zi /O2 /I "..\..\include" /FI"stdafx.h" /D "NDEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /Yu"stdafx.h" /Fd"..\..\..\lib\libgc.pdb" /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo +# Begin Special Build Tool +OutDir=.\..\..\..\lib +TargetName=libgc +SOURCE="$(InputPath)" +PostBuild_Cmds=del $(OutDir)\$(TargetName).idb +# End Special Build Tool + +!ELSEIF "$(CFG)" == "libgc - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "..\..\..\lib" +# PROP Intermediate_Dir "..\..\..\obj\Debug\libgc" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /GX /Zi /Od /I "..\..\include" /FI"stdafx.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /Yu"stdafx.h" /Fd"..\..\..\lib\libgcd.pdb" /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo /o"..\..\..\lib/libgcd.bsc" +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"..\..\..\lib\libgcd.lib" +# Begin Special Build Tool +OutDir=.\..\..\..\lib +TargetName=libgcd +SOURCE="$(InputPath)" +PostBuild_Cmds=del $(OutDir)\$(TargetName).idb +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "libgc - Win32 Release" +# Name "libgc - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "c;cpp;cc;cxx;tcc;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\allchblk.c +# End Source File +# Begin Source File + +SOURCE=..\..\alloc.c +# End Source File +# Begin Source File + +SOURCE=..\..\backgraph.c +# End Source File +# Begin Source File + +SOURCE=..\..\blacklst.c +# End Source File +# Begin Source File + +SOURCE=..\..\dbg_mlc.c +# End Source File +# Begin Source File + +SOURCE=..\..\gcj_mlc.c +# End Source File +# Begin Source File + +SOURCE=..\..\fnlz_mlc.c +# End Source File +# Begin Source File + +SOURCE=..\..\dyn_load.c +# End Source File +# Begin Source File + +SOURCE=..\..\finalize.c +# End Source File +# Begin Source File + +SOURCE=..\..\headers.c +# End Source File +# Begin Source File + +SOURCE=..\..\mach_dep.c +# End Source File +# Begin Source File + +SOURCE=..\..\malloc.c +# End Source File +# Begin Source File + +SOURCE=..\..\mallocx.c +# End Source File +# Begin Source File + +SOURCE=..\..\mark.c +# End Source File +# Begin Source File + +SOURCE=..\..\mark_rts.c +# End Source File +# Begin Source File + +SOURCE=..\..\misc.c +# End Source File +# Begin Source File + +SOURCE=..\..\extra\msvc_dbg.c +# End Source File +# Begin Source File + +SOURCE=..\..\new_hblk.c +# End Source File +# Begin Source File + +SOURCE=..\..\obj_map.c +# End Source File +# Begin Source File + +SOURCE=..\..\os_dep.c +# End Source File +# Begin Source File + +SOURCE=..\..\ptr_chck.c +# End Source File +# Begin Source File + +SOURCE=..\..\reclaim.c +# End Source File +# Begin Source File + +SOURCE=..\stdafx.c +# ADD CPP /Yc"stdafx.h" +# End Source File +# Begin Source File + +SOURCE=..\..\typd_mlc.c +# End Source File +# Begin Source File + +SOURCE=..\..\win32_threads.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hh;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\..\include\gc_allocator.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gc_backptr.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gc_cpp.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gc_gcj.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gc_inl.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gc_inline.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gc_pthread_redirects.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\javaxfc.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\leak_detector.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\private\msvc_dbg.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gc_alloc_ptrs.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\new_gc_alloc.h +# End Source File +# Begin Source File + +SOURCE=..\stdafx.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gc_version.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\weakpointer.h +# End Source File +# End Group +# End Target +# End Project diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/libgcmt.dsp b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/libgcmt.dsp new file mode 100644 index 000000000..979a081e8 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/libgcmt.dsp @@ -0,0 +1,217 @@ +# Microsoft Developer Studio Project File - Name="libgcmt" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=libgcmt - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "libgcmt.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "libgcmt.mak" CFG="libgcmt - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "libgcmt - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "libgcmt - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "libgcmt - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "..\..\..\lib" +# PROP Intermediate_Dir "..\..\..\obj\Release\libgcmt" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /Zi /O2 /I "..\..\include" /FI"stdafx.h" /D "NDEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /D "GC_THREADS" /Yu"stdafx.h" /Fd"..\..\..\lib\libgcmt.pdb" /Zl /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo +# Begin Special Build Tool +OutDir=.\..\..\..\lib +TargetName=libgcmt +SOURCE="$(InputPath)" +PostBuild_Cmds=del $(OutDir)\$(TargetName).idb +# End Special Build Tool + +!ELSEIF "$(CFG)" == "libgcmt - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "..\..\..\lib" +# PROP Intermediate_Dir "..\..\..\obj\Debug\libgcmt" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /GX /Zi /Od /I "..\..\include" /FI"stdafx.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /D "GC_THREADS" /Yu"stdafx.h" /Fd"..\..\..\lib\libgcmtd.pdb" /Zl /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo /o"..\..\..\lib/libgcmtd.bsc" +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"..\..\..\lib\libgcmtd.lib" +# Begin Special Build Tool +OutDir=.\..\..\..\lib +TargetName=libgcmtd +SOURCE="$(InputPath)" +PostBuild_Cmds=del $(OutDir)\$(TargetName).idb +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "libgcmt - Win32 Release" +# Name "libgcmt - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "c;cpp;cc;cxx;tcc;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\allchblk.c +# End Source File +# Begin Source File + +SOURCE=..\..\alloc.c +# End Source File +# Begin Source File + +SOURCE=..\..\backgraph.c +# End Source File +# Begin Source File + +SOURCE=..\..\blacklst.c +# End Source File +# Begin Source File + +SOURCE=..\..\dbg_mlc.c +# End Source File +# Begin Source File + +SOURCE=..\..\gcj_mlc.c +# End Source File +# Begin Source File + +SOURCE=..\..\fnlz_mlc.c +# End Source File +# Begin Source File + +SOURCE=..\..\dyn_load.c +# End Source File +# Begin Source File + +SOURCE=..\..\finalize.c +# End Source File +# Begin Source File + +SOURCE=..\..\headers.c +# End Source File +# Begin Source File + +SOURCE=..\..\mach_dep.c +# End Source File +# Begin Source File + +SOURCE=..\..\malloc.c +# End Source File +# Begin Source File + +SOURCE=..\..\mallocx.c +# End Source File +# Begin Source File + +SOURCE=..\..\mark.c +# End Source File +# Begin Source File + +SOURCE=..\..\mark_rts.c +# End Source File +# Begin Source File + +SOURCE=..\..\misc.c +# End Source File +# Begin Source File + +SOURCE=..\..\extra\msvc_dbg.c +# End Source File +# Begin Source File + +SOURCE=..\..\new_hblk.c +# End Source File +# Begin Source File + +SOURCE=..\..\obj_map.c +# End Source File +# Begin Source File + +SOURCE=..\..\os_dep.c +# End Source File +# Begin Source File + +SOURCE=..\..\ptr_chck.c +# End Source File +# Begin Source File + +SOURCE=..\..\reclaim.c +# End Source File +# Begin Source File + +SOURCE=..\stdafx.c +# ADD CPP /Yc"stdafx.h" +# End Source File +# Begin Source File + +SOURCE=..\..\typd_mlc.c +# End Source File +# Begin Source File + +SOURCE=..\..\win32_threads.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hh;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\..\include\private\msvc_dbg.h +# End Source File +# Begin Source File + +SOURCE=..\stdafx.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gc_version.h +# End Source File +# End Group +# End Target +# End Project diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/test.dsp b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/test.dsp new file mode 100644 index 000000000..3648da48a --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/test.dsp @@ -0,0 +1,63 @@ +# Microsoft Developer Studio Project File - Name="test" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Generic Project" 0x010a + +CFG=test - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "test.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "test.mak" CFG="test - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "test - Win32 Release" (based on "Win32 (x86) Generic Project") +!MESSAGE "test - Win32 Debug" (based on "Win32 (x86) Generic Project") +!MESSAGE + +# Begin Project +# PROP testowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +MTL=midl.exe + +!IF "$(CFG)" == "test - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "..\..\..\bin" +# PROP Intermediate_Dir "..\..\..\obj\Release" +# PROP Target_Dir "" + +!ELSEIF "$(CFG)" == "test - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "..\..\..\bin" +# PROP Intermediate_Dir "..\..\..\obj\Debug" +# PROP Target_Dir "" + +!ENDIF + +# Begin Target + +# Name "test - Win32 Release" +# Name "test - Win32 Debug" +# End Target +# End Project diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/test_gc.dsp b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/test_gc.dsp new file mode 100644 index 000000000..a92256312 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/test_gc.dsp @@ -0,0 +1,93 @@ +# Microsoft Developer Studio Project File - Name="test_gc" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=test_gc - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "test_gc.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "test_gc.mak" CFG="test_gc - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "test_gc - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "test_gc - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "test_gc - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "..\..\..\bin" +# PROP Intermediate_Dir "..\..\..\obj\Release\test_gc" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /I "..\..\include" /D "NDEBUG" /D "_CONSOLE" /D "GC_DLL" /D "WIN32" /D "_MBCS" /D "GC_THREADS" /FD /c +# SUBTRACT CPP /Fr /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /debug /machine:I386 /release /opt:ref +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "test_gc - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "..\..\..\bin" +# PROP Intermediate_Dir "..\..\..\obj\Debug\test_gc" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "_CONSOLE" /D "GC_DLL" /D "WIN32" /D "_MBCS" /D "GC_THREADS" /YX /FD /GZ /c +# SUBTRACT CPP /Fr +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo /o"..\..\..\bin/test_gcd.bsc" +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /incremental:no /debug /machine:I386 /out:"..\..\..\bin/test_gcd.exe" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "test_gc - Win32 Release" +# Name "test_gc - Win32 Debug" +# Begin Source File + +SOURCE=..\..\tests\test.c +# End Source File +# End Target +# End Project diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/test_leak_gc.dsp b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/test_leak_gc.dsp new file mode 100644 index 000000000..273c27d84 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/test_leak_gc.dsp @@ -0,0 +1,93 @@ +# Microsoft Developer Studio Project File - Name="test_leak_gc" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=test_leak_gc - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "test_leak_gc.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "test_leak_gc.mak" CFG="test_leak_gc - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "test_leak_gc - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "test_leak_gc - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "test_leak_gc - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "..\..\..\bin" +# PROP Intermediate_Dir "..\..\..\obj\Release\test_leak_gc" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /I "..\..\include" /D "NDEBUG" /D "_CONSOLE" /D "GC_DLL" /D "WIN32" /D "_MBCS" /D "GC_THREADS" /FD /c +# SUBTRACT CPP /Fr /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /debug /machine:I386 /release /opt:ref +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "test_leak_gc - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "..\..\..\bin" +# PROP Intermediate_Dir "..\..\..\obj\Debug\test_leak_gc" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "_CONSOLE" /D "GC_DLL" /D "WIN32" /D "_MBCS" /D "GC_THREADS" /YX /FD /GZ /c +# SUBTRACT CPP /Fr +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo /o"..\..\..\bin/test_leak_gcd.bsc" +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /incremental:no /debug /machine:I386 /out:"..\..\..\bin/test_leak_gcd.exe" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "test_leak_gc - Win32 Release" +# Name "test_leak_gc - Win32 Debug" +# Begin Source File + +SOURCE=..\..\tests\leak_test.c +# End Source File +# End Target +# End Project diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/test_leak_libgc.dsp b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/test_leak_libgc.dsp new file mode 100644 index 000000000..2d9225715 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/test_leak_libgc.dsp @@ -0,0 +1,93 @@ +# Microsoft Developer Studio Project File - Name="test_leak_libgc" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=test_leak_libgc - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "test_leak_libgc.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "test_leak_libgc.mak" CFG="test_leak_libgc - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "test_leak_libgc - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "test_leak_libgc - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "test_leak_libgc - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "..\..\..\bin" +# PROP Intermediate_Dir "..\..\..\obj\Release\test_leak_libgc" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /Zi /O2 /I "..\..\include" /D "NDEBUG" /D "_CONSOLE" /D "WIN32" /D "_MBCS" /FD /c +# SUBTRACT CPP /Fr /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /debug /machine:I386 /release /opt:ref +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "test_leak_libgc - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "..\..\..\bin" +# PROP Intermediate_Dir "..\..\..\obj\Debug\test_leak_libgc" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "_CONSOLE" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c +# SUBTRACT CPP /Fr +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo /o"..\..\..\bin/test_leak_libgcd.bsc" +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /incremental:no /debug /machine:I386 /out:"..\..\..\bin/test_leak_libgcd.exe" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "test_leak_libgc - Win32 Release" +# Name "test_leak_libgc - Win32 Debug" +# Begin Source File + +SOURCE=..\..\tests\leak_test.c +# End Source File +# End Target +# End Project diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/test_leak_libgcmt.dsp b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/test_leak_libgcmt.dsp new file mode 100644 index 000000000..42f97899e --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/test_leak_libgcmt.dsp @@ -0,0 +1,93 @@ +# Microsoft Developer Studio Project File - Name="test_leak_libgcmt" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=test_leak_libgcmt - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "test_leak_libgcmt.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "test_leak_libgcmt.mak" CFG="test_leak_libgcmt - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "test_leak_libgcmt - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "test_leak_libgcmt - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "test_leak_libgcmt - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "..\..\..\bin" +# PROP Intermediate_Dir "..\..\..\obj\Release\test_leak_libgcmt" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /Zi /O2 /I "..\..\include" /D "NDEBUG" /D "_CONSOLE" /D "WIN32" /D "_MBCS" /D "GC_THREADS" /FD /c +# SUBTRACT CPP /Fr /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /debug /machine:I386 /release /opt:ref +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "test_leak_libgcmt - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "..\..\..\bin" +# PROP Intermediate_Dir "..\..\..\obj\Debug\test_leak_libgcmt" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "_CONSOLE" /D "WIN32" /D "_MBCS" /D "GC_THREADS" /YX /FD /GZ /c +# SUBTRACT CPP /Fr +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo /o"..\..\..\bin/test_leak_libgcmtd.bsc" +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /incremental:no /debug /machine:I386 /out:"..\..\..\bin/test_leak_libgcmtd.exe" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "test_leak_libgcmt - Win32 Release" +# Name "test_leak_libgcmt - Win32 Debug" +# Begin Source File + +SOURCE=..\..\tests\leak_test.c +# End Source File +# End Target +# End Project diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/test_libgc.dsp b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/test_libgc.dsp new file mode 100644 index 000000000..d7fa50aab --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/test_libgc.dsp @@ -0,0 +1,93 @@ +# Microsoft Developer Studio Project File - Name="test_libgc" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=test_libgc - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "test_libgc.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "test_libgc.mak" CFG="test_libgc - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "test_libgc - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "test_libgc - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "test_libgc - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "..\..\..\bin" +# PROP Intermediate_Dir "..\..\..\obj\Release\test_libgc" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /Zi /O2 /I "..\..\include" /D "NDEBUG" /D "_CONSOLE" /D "WIN32" /D "_MBCS" /FD /c +# SUBTRACT CPP /Fr /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /debug /machine:I386 /release /opt:ref +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "test_libgc - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "..\..\..\bin" +# PROP Intermediate_Dir "..\..\..\obj\Debug\test_libgc" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "_CONSOLE" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c +# SUBTRACT CPP /Fr +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo /o"..\..\..\bin/test_libgcd.bsc" +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /incremental:no /debug /machine:I386 /out:"..\..\..\bin/test_libgcd.exe" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "test_libgc - Win32 Release" +# Name "test_libgc - Win32 Debug" +# Begin Source File + +SOURCE=..\..\tests\test.c +# End Source File +# End Target +# End Project diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/test_libgcmt.dsp b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/test_libgcmt.dsp new file mode 100644 index 000000000..6f81a3466 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/test_libgcmt.dsp @@ -0,0 +1,93 @@ +# Microsoft Developer Studio Project File - Name="test_libgcmt" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=test_libgcmt - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "test_libgcmt.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "test_libgcmt.mak" CFG="test_libgcmt - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "test_libgcmt - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "test_libgcmt - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "test_libgcmt - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "..\..\..\bin" +# PROP Intermediate_Dir "..\..\..\obj\Release\test_libgcmt" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /Zi /O2 /I "..\..\include" /D "NDEBUG" /D "_CONSOLE" /D "WIN32" /D "_MBCS" /D "GC_THREADS" /FD /c +# SUBTRACT CPP /Fr /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /debug /machine:I386 /release /opt:ref +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "test_libgcmt - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "..\..\..\bin" +# PROP Intermediate_Dir "..\..\..\obj\Debug\test_libgcmt" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "_CONSOLE" /D "WIN32" /D "_MBCS" /D "GC_THREADS" /YX /FD /GZ /c +# SUBTRACT CPP /Fr +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo /o"..\..\..\bin/test_libgcmtd.bsc" +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /incremental:no /debug /machine:I386 /out:"..\..\..\bin/test_libgcmtd.exe" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "test_libgcmt - Win32 Release" +# Name "test_libgcmt - Win32 Debug" +# Begin Source File + +SOURCE=..\..\tests\test.c +# End Source File +# End Target +# End Project diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/vc60crlf.cmd b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/vc60crlf.cmd new file mode 100644 index 000000000..28ec5360c --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc60/vc60crlf.cmd @@ -0,0 +1,15 @@ +@echo off +rem This script will convert Unix-style line endings into Windows format. + +for %%P in (*.ds?) do call :fixline %%P +goto :eof + +:fixline +@echo on +if exist "%~1.new" del "%~1.new" +for /f %%S in (%1) do ( + echo %%S>>"%~1.new" +) +ren %1 "%~1.bak" +ren "%~1.new" %1 +goto :eof diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/all.vcproj b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/all.vcproj new file mode 100644 index 000000000..512e9a062 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/all.vcproj @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/gc.sln b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/gc.sln new file mode 100644 index 000000000..aaa1be64e --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/gc.sln @@ -0,0 +1,96 @@ +Microsoft Visual Studio Solution File, Format Version 7.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "all", "all.vcproj", "{CED9D953-AC1A-4795-9853-6D60857509EE}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gc", "gc.vcproj", "{D7ADAD9A-14FF-4C93-9BF1-ACD03FB6A2FA}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libgc", "libgc.vcproj", "{F80C47A7-2B2D-4BA9-BEED-AAFA7541650D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libgcmt", "libgcmt.vcproj", "{39802D97-BEF7-499D-8570-294AEA39ED7D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "test.vcproj", "{997208FE-7A7D-435A-945A-C61C57D8070C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_gc", "test_gc.vcproj", "{D1F56655-8C27-4320-9436-2A11729A337B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_leak_gc", "test_leak_gc.vcproj", "{6E545988-1AE7-41FB-A981-D256A84F4C3A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_leak_libgc", "test_leak_libgc.vcproj", "{A561AE5C-33FE-4DBC-A4D4-52B7F196D20F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_leak_libgcmt", "test_leak_libgcmt.vcproj", "{92046CBF-2EF9-408D-B997-8445E945D687}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_libgc", "test_libgc.vcproj", "{8CFE55AA-676C-4B5A-B133-390B4BF02AB8}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_libgcmt", "test_libgcmt.vcproj", "{8C63DB39-DBF4-49D3-A908-172ADA21753B}" +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + ConfigName.0 = Debug + ConfigName.1 = Release + EndGlobalSection + GlobalSection(ProjectDependencies) = postSolution + {CED9D953-AC1A-4795-9853-6D60857509EE}.0 = {39802D97-BEF7-499D-8570-294AEA39ED7D} + {CED9D953-AC1A-4795-9853-6D60857509EE}.1 = {F80C47A7-2B2D-4BA9-BEED-AAFA7541650D} + {CED9D953-AC1A-4795-9853-6D60857509EE}.2 = {D7ADAD9A-14FF-4C93-9BF1-ACD03FB6A2FA} + {997208FE-7A7D-435A-945A-C61C57D8070C}.0 = {92046CBF-2EF9-408D-B997-8445E945D687} + {997208FE-7A7D-435A-945A-C61C57D8070C}.1 = {A561AE5C-33FE-4DBC-A4D4-52B7F196D20F} + {997208FE-7A7D-435A-945A-C61C57D8070C}.2 = {6E545988-1AE7-41FB-A981-D256A84F4C3A} + {997208FE-7A7D-435A-945A-C61C57D8070C}.3 = {8C63DB39-DBF4-49D3-A908-172ADA21753B} + {997208FE-7A7D-435A-945A-C61C57D8070C}.4 = {8CFE55AA-676C-4B5A-B133-390B4BF02AB8} + {997208FE-7A7D-435A-945A-C61C57D8070C}.5 = {D1F56655-8C27-4320-9436-2A11729A337B} + {D1F56655-8C27-4320-9436-2A11729A337B}.0 = {D7ADAD9A-14FF-4C93-9BF1-ACD03FB6A2FA} + {6E545988-1AE7-41FB-A981-D256A84F4C3A}.0 = {D7ADAD9A-14FF-4C93-9BF1-ACD03FB6A2FA} + {A561AE5C-33FE-4DBC-A4D4-52B7F196D20F}.0 = {F80C47A7-2B2D-4BA9-BEED-AAFA7541650D} + {92046CBF-2EF9-408D-B997-8445E945D687}.0 = {39802D97-BEF7-499D-8570-294AEA39ED7D} + {8CFE55AA-676C-4B5A-B133-390B4BF02AB8}.0 = {F80C47A7-2B2D-4BA9-BEED-AAFA7541650D} + {8C63DB39-DBF4-49D3-A908-172ADA21753B}.0 = {39802D97-BEF7-499D-8570-294AEA39ED7D} + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {CED9D953-AC1A-4795-9853-6D60857509EE}.Debug.ActiveCfg = Debug|Win32 + {CED9D953-AC1A-4795-9853-6D60857509EE}.Debug.Build.0 = Debug|Win32 + {CED9D953-AC1A-4795-9853-6D60857509EE}.Release.ActiveCfg = Release|Win32 + {CED9D953-AC1A-4795-9853-6D60857509EE}.Release.Build.0 = Release|Win32 + {D7ADAD9A-14FF-4C93-9BF1-ACD03FB6A2FA}.Debug.ActiveCfg = Debug|Win32 + {D7ADAD9A-14FF-4C93-9BF1-ACD03FB6A2FA}.Debug.Build.0 = Debug|Win32 + {D7ADAD9A-14FF-4C93-9BF1-ACD03FB6A2FA}.Release.ActiveCfg = Release|Win32 + {D7ADAD9A-14FF-4C93-9BF1-ACD03FB6A2FA}.Release.Build.0 = Release|Win32 + {F80C47A7-2B2D-4BA9-BEED-AAFA7541650D}.Debug.ActiveCfg = Debug|Win32 + {F80C47A7-2B2D-4BA9-BEED-AAFA7541650D}.Debug.Build.0 = Debug|Win32 + {F80C47A7-2B2D-4BA9-BEED-AAFA7541650D}.Release.ActiveCfg = Release|Win32 + {F80C47A7-2B2D-4BA9-BEED-AAFA7541650D}.Release.Build.0 = Release|Win32 + {39802D97-BEF7-499D-8570-294AEA39ED7D}.Debug.ActiveCfg = Debug|Win32 + {39802D97-BEF7-499D-8570-294AEA39ED7D}.Debug.Build.0 = Debug|Win32 + {39802D97-BEF7-499D-8570-294AEA39ED7D}.Release.ActiveCfg = Release|Win32 + {39802D97-BEF7-499D-8570-294AEA39ED7D}.Release.Build.0 = Release|Win32 + {997208FE-7A7D-435A-945A-C61C57D8070C}.Debug.ActiveCfg = Debug|Win32 + {997208FE-7A7D-435A-945A-C61C57D8070C}.Debug.Build.0 = Debug|Win32 + {997208FE-7A7D-435A-945A-C61C57D8070C}.Release.ActiveCfg = Release|Win32 + {997208FE-7A7D-435A-945A-C61C57D8070C}.Release.Build.0 = Release|Win32 + {D1F56655-8C27-4320-9436-2A11729A337B}.Debug.ActiveCfg = Debug|Win32 + {D1F56655-8C27-4320-9436-2A11729A337B}.Debug.Build.0 = Debug|Win32 + {D1F56655-8C27-4320-9436-2A11729A337B}.Release.ActiveCfg = Release|Win32 + {D1F56655-8C27-4320-9436-2A11729A337B}.Release.Build.0 = Release|Win32 + {6E545988-1AE7-41FB-A981-D256A84F4C3A}.Debug.ActiveCfg = Debug|Win32 + {6E545988-1AE7-41FB-A981-D256A84F4C3A}.Debug.Build.0 = Debug|Win32 + {6E545988-1AE7-41FB-A981-D256A84F4C3A}.Release.ActiveCfg = Release|Win32 + {6E545988-1AE7-41FB-A981-D256A84F4C3A}.Release.Build.0 = Release|Win32 + {A561AE5C-33FE-4DBC-A4D4-52B7F196D20F}.Debug.ActiveCfg = Debug|Win32 + {A561AE5C-33FE-4DBC-A4D4-52B7F196D20F}.Debug.Build.0 = Debug|Win32 + {A561AE5C-33FE-4DBC-A4D4-52B7F196D20F}.Release.ActiveCfg = Release|Win32 + {A561AE5C-33FE-4DBC-A4D4-52B7F196D20F}.Release.Build.0 = Release|Win32 + {92046CBF-2EF9-408D-B997-8445E945D687}.Debug.ActiveCfg = Debug|Win32 + {92046CBF-2EF9-408D-B997-8445E945D687}.Debug.Build.0 = Debug|Win32 + {92046CBF-2EF9-408D-B997-8445E945D687}.Release.ActiveCfg = Release|Win32 + {92046CBF-2EF9-408D-B997-8445E945D687}.Release.Build.0 = Release|Win32 + {8CFE55AA-676C-4B5A-B133-390B4BF02AB8}.Debug.ActiveCfg = Debug|Win32 + {8CFE55AA-676C-4B5A-B133-390B4BF02AB8}.Debug.Build.0 = Debug|Win32 + {8CFE55AA-676C-4B5A-B133-390B4BF02AB8}.Release.ActiveCfg = Release|Win32 + {8CFE55AA-676C-4B5A-B133-390B4BF02AB8}.Release.Build.0 = Release|Win32 + {8C63DB39-DBF4-49D3-A908-172ADA21753B}.Debug.ActiveCfg = Debug|Win32 + {8C63DB39-DBF4-49D3-A908-172ADA21753B}.Debug.Build.0 = Debug|Win32 + {8C63DB39-DBF4-49D3-A908-172ADA21753B}.Release.ActiveCfg = Release|Win32 + {8C63DB39-DBF4-49D3-A908-172ADA21753B}.Release.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/gc.vcproj b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/gc.vcproj new file mode 100644 index 000000000..a0c9aaf8f --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/gc.vcproj @@ -0,0 +1,344 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/libgc.vcproj b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/libgc.vcproj new file mode 100644 index 000000000..1ef9c4a0a --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/libgc.vcproj @@ -0,0 +1,255 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/libgcmt.vcproj b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/libgcmt.vcproj new file mode 100644 index 000000000..2c2704af8 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/libgcmt.vcproj @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/test.vcproj b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/test.vcproj new file mode 100644 index 000000000..4b6170ddd --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/test.vcproj @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/test_gc.vcproj b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/test_gc.vcproj new file mode 100644 index 000000000..8802cf6d9 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/test_gc.vcproj @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/test_leak_gc.vcproj b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/test_leak_gc.vcproj new file mode 100644 index 000000000..4b12168ee --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/test_leak_gc.vcproj @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/test_leak_libgc.vcproj b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/test_leak_libgc.vcproj new file mode 100644 index 000000000..05a1be4e5 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/test_leak_libgc.vcproj @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/test_leak_libgcmt.vcproj b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/test_leak_libgcmt.vcproj new file mode 100644 index 000000000..8f4f8bd78 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/test_leak_libgcmt.vcproj @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/test_libgc.vcproj b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/test_libgc.vcproj new file mode 100644 index 000000000..110c39621 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/test_libgc.vcproj @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/test_libgcmt.vcproj b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/test_libgcmt.vcproj new file mode 100644 index 000000000..1353cdf02 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc70/test_libgcmt.vcproj @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/all.vcproj b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/all.vcproj new file mode 100644 index 000000000..2f4d7ae52 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/all.vcproj @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/gc.sln b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/gc.sln new file mode 100644 index 000000000..fa1acc8d6 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/gc.sln @@ -0,0 +1,116 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "all", "all.vcproj", "{684E465A-3944-4BA0-BA8D-52A064B43A5D}" + ProjectSection(ProjectDependencies) = postProject + {EDA8C8B6-2538-4F1E-9C9A-F5C4FAF1EC16} = {EDA8C8B6-2538-4F1E-9C9A-F5C4FAF1EC16} + {1C32FB8B-6F91-4190-9F05-CE1E772BB5E0} = {1C32FB8B-6F91-4190-9F05-CE1E772BB5E0} + {93622AAF-633A-4D02-B023-674D4CDA266B} = {93622AAF-633A-4D02-B023-674D4CDA266B} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gc", "gc.vcproj", "{93622AAF-633A-4D02-B023-674D4CDA266B}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libgc", "libgc.vcproj", "{1C32FB8B-6F91-4190-9F05-CE1E772BB5E0}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libgcmt", "libgcmt.vcproj", "{EDA8C8B6-2538-4F1E-9C9A-F5C4FAF1EC16}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "test.vcproj", "{1891ADD8-C39B-494B-B69D-D64F76729E5A}" + ProjectSection(ProjectDependencies) = postProject + {8A595901-33E6-4959-A8E9-6DACC2D57330} = {8A595901-33E6-4959-A8E9-6DACC2D57330} + {EB1D2ECA-926D-4B6E-BF65-B429C713381C} = {EB1D2ECA-926D-4B6E-BF65-B429C713381C} + {500B7CE2-FD16-42C5-B738-1406C13A68B4} = {500B7CE2-FD16-42C5-B738-1406C13A68B4} + {9551B5E4-94A5-4B04-ACA1-9FB28DFD28AA} = {9551B5E4-94A5-4B04-ACA1-9FB28DFD28AA} + {B0F6C137-5153-48E3-ABED-6C02D3912EDA} = {B0F6C137-5153-48E3-ABED-6C02D3912EDA} + {1911057C-30C3-41CE-AF9E-232AEB37BCD3} = {1911057C-30C3-41CE-AF9E-232AEB37BCD3} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_gc", "test_gc.vcproj", "{1911057C-30C3-41CE-AF9E-232AEB37BCD3}" + ProjectSection(ProjectDependencies) = postProject + {93622AAF-633A-4D02-B023-674D4CDA266B} = {93622AAF-633A-4D02-B023-674D4CDA266B} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_leak_gc", "test_leak_gc.vcproj", "{500B7CE2-FD16-42C5-B738-1406C13A68B4}" + ProjectSection(ProjectDependencies) = postProject + {93622AAF-633A-4D02-B023-674D4CDA266B} = {93622AAF-633A-4D02-B023-674D4CDA266B} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_leak_libgc", "test_leak_libgc.vcproj", "{EB1D2ECA-926D-4B6E-BF65-B429C713381C}" + ProjectSection(ProjectDependencies) = postProject + {1C32FB8B-6F91-4190-9F05-CE1E772BB5E0} = {1C32FB8B-6F91-4190-9F05-CE1E772BB5E0} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_leak_libgcmt", "test_leak_libgcmt.vcproj", "{8A595901-33E6-4959-A8E9-6DACC2D57330}" + ProjectSection(ProjectDependencies) = postProject + {EDA8C8B6-2538-4F1E-9C9A-F5C4FAF1EC16} = {EDA8C8B6-2538-4F1E-9C9A-F5C4FAF1EC16} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_libgc", "test_libgc.vcproj", "{B0F6C137-5153-48E3-ABED-6C02D3912EDA}" + ProjectSection(ProjectDependencies) = postProject + {1C32FB8B-6F91-4190-9F05-CE1E772BB5E0} = {1C32FB8B-6F91-4190-9F05-CE1E772BB5E0} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_libgcmt", "test_libgcmt.vcproj", "{9551B5E4-94A5-4B04-ACA1-9FB28DFD28AA}" + ProjectSection(ProjectDependencies) = postProject + {EDA8C8B6-2538-4F1E-9C9A-F5C4FAF1EC16} = {EDA8C8B6-2538-4F1E-9C9A-F5C4FAF1EC16} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {684E465A-3944-4BA0-BA8D-52A064B43A5D}.Debug.ActiveCfg = Debug|Win32 + {684E465A-3944-4BA0-BA8D-52A064B43A5D}.Debug.Build.0 = Debug|Win32 + {684E465A-3944-4BA0-BA8D-52A064B43A5D}.Release.ActiveCfg = Release|Win32 + {684E465A-3944-4BA0-BA8D-52A064B43A5D}.Release.Build.0 = Release|Win32 + {93622AAF-633A-4D02-B023-674D4CDA266B}.Debug.ActiveCfg = Debug|Win32 + {93622AAF-633A-4D02-B023-674D4CDA266B}.Debug.Build.0 = Debug|Win32 + {93622AAF-633A-4D02-B023-674D4CDA266B}.Release.ActiveCfg = Release|Win32 + {93622AAF-633A-4D02-B023-674D4CDA266B}.Release.Build.0 = Release|Win32 + {1C32FB8B-6F91-4190-9F05-CE1E772BB5E0}.Debug.ActiveCfg = Debug|Win32 + {1C32FB8B-6F91-4190-9F05-CE1E772BB5E0}.Debug.Build.0 = Debug|Win32 + {1C32FB8B-6F91-4190-9F05-CE1E772BB5E0}.Release.ActiveCfg = Release|Win32 + {1C32FB8B-6F91-4190-9F05-CE1E772BB5E0}.Release.Build.0 = Release|Win32 + {EDA8C8B6-2538-4F1E-9C9A-F5C4FAF1EC16}.Debug.ActiveCfg = Debug|Win32 + {EDA8C8B6-2538-4F1E-9C9A-F5C4FAF1EC16}.Debug.Build.0 = Debug|Win32 + {EDA8C8B6-2538-4F1E-9C9A-F5C4FAF1EC16}.Release.ActiveCfg = Release|Win32 + {EDA8C8B6-2538-4F1E-9C9A-F5C4FAF1EC16}.Release.Build.0 = Release|Win32 + {1891ADD8-C39B-494B-B69D-D64F76729E5A}.Debug.ActiveCfg = Debug|Win32 + {1891ADD8-C39B-494B-B69D-D64F76729E5A}.Debug.Build.0 = Debug|Win32 + {1891ADD8-C39B-494B-B69D-D64F76729E5A}.Release.ActiveCfg = Release|Win32 + {1891ADD8-C39B-494B-B69D-D64F76729E5A}.Release.Build.0 = Release|Win32 + {1911057C-30C3-41CE-AF9E-232AEB37BCD3}.Debug.ActiveCfg = Debug|Win32 + {1911057C-30C3-41CE-AF9E-232AEB37BCD3}.Debug.Build.0 = Debug|Win32 + {1911057C-30C3-41CE-AF9E-232AEB37BCD3}.Release.ActiveCfg = Release|Win32 + {1911057C-30C3-41CE-AF9E-232AEB37BCD3}.Release.Build.0 = Release|Win32 + {500B7CE2-FD16-42C5-B738-1406C13A68B4}.Debug.ActiveCfg = Debug|Win32 + {500B7CE2-FD16-42C5-B738-1406C13A68B4}.Debug.Build.0 = Debug|Win32 + {500B7CE2-FD16-42C5-B738-1406C13A68B4}.Release.ActiveCfg = Release|Win32 + {500B7CE2-FD16-42C5-B738-1406C13A68B4}.Release.Build.0 = Release|Win32 + {EB1D2ECA-926D-4B6E-BF65-B429C713381C}.Debug.ActiveCfg = Debug|Win32 + {EB1D2ECA-926D-4B6E-BF65-B429C713381C}.Debug.Build.0 = Debug|Win32 + {EB1D2ECA-926D-4B6E-BF65-B429C713381C}.Release.ActiveCfg = Release|Win32 + {EB1D2ECA-926D-4B6E-BF65-B429C713381C}.Release.Build.0 = Release|Win32 + {8A595901-33E6-4959-A8E9-6DACC2D57330}.Debug.ActiveCfg = Debug|Win32 + {8A595901-33E6-4959-A8E9-6DACC2D57330}.Debug.Build.0 = Debug|Win32 + {8A595901-33E6-4959-A8E9-6DACC2D57330}.Release.ActiveCfg = Release|Win32 + {8A595901-33E6-4959-A8E9-6DACC2D57330}.Release.Build.0 = Release|Win32 + {B0F6C137-5153-48E3-ABED-6C02D3912EDA}.Debug.ActiveCfg = Debug|Win32 + {B0F6C137-5153-48E3-ABED-6C02D3912EDA}.Debug.Build.0 = Debug|Win32 + {B0F6C137-5153-48E3-ABED-6C02D3912EDA}.Release.ActiveCfg = Release|Win32 + {B0F6C137-5153-48E3-ABED-6C02D3912EDA}.Release.Build.0 = Release|Win32 + {9551B5E4-94A5-4B04-ACA1-9FB28DFD28AA}.Debug.ActiveCfg = Debug|Win32 + {9551B5E4-94A5-4B04-ACA1-9FB28DFD28AA}.Debug.Build.0 = Debug|Win32 + {9551B5E4-94A5-4B04-ACA1-9FB28DFD28AA}.Release.ActiveCfg = Release|Win32 + {9551B5E4-94A5-4B04-ACA1-9FB28DFD28AA}.Release.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/gc.vcproj b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/gc.vcproj new file mode 100644 index 000000000..a7387a53b --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/gc.vcproj @@ -0,0 +1,828 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/libgc.vcproj b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/libgc.vcproj new file mode 100644 index 000000000..2d0ff1847 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/libgc.vcproj @@ -0,0 +1,735 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/libgcmt.vcproj b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/libgcmt.vcproj new file mode 100644 index 000000000..bce8c3865 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/libgcmt.vcproj @@ -0,0 +1,699 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/test.vcproj b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/test.vcproj new file mode 100644 index 000000000..41681979f --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/test.vcproj @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/test_gc.vcproj b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/test_gc.vcproj new file mode 100644 index 000000000..d477d70a6 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/test_gc.vcproj @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/test_leak_gc.vcproj b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/test_leak_gc.vcproj new file mode 100644 index 000000000..b6c980cf7 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/test_leak_gc.vcproj @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/test_leak_libgc.vcproj b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/test_leak_libgc.vcproj new file mode 100644 index 000000000..0486f1ef6 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/test_leak_libgc.vcproj @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/test_leak_libgcmt.vcproj b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/test_leak_libgcmt.vcproj new file mode 100644 index 000000000..297464d43 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/test_leak_libgcmt.vcproj @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/test_libgc.vcproj b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/test_libgc.vcproj new file mode 100644 index 000000000..93d35f46d --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/test_libgc.vcproj @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/test_libgcmt.vcproj b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/test_libgcmt.vcproj new file mode 100644 index 000000000..276aed5b1 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc71/test_libgcmt.vcproj @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc9/libgc.vcproj b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc9/libgc.vcproj new file mode 100644 index 000000000..25512c2a2 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc9/libgc.vcproj @@ -0,0 +1,433 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc9/test_cpp_libgc.vcproj b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc9/test_cpp_libgc.vcproj new file mode 100644 index 000000000..0a6a88c28 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc9/test_cpp_libgc.vcproj @@ -0,0 +1,293 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc9/test_huge.vcproj b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc9/test_huge.vcproj new file mode 100644 index 000000000..0ad1b2d6c --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc9/test_huge.vcproj @@ -0,0 +1,295 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc9/test_leak.vcproj b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc9/test_leak.vcproj new file mode 100644 index 000000000..1ee9243a1 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc9/test_leak.vcproj @@ -0,0 +1,295 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc9/test_libgc.vcproj b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc9/test_libgc.vcproj new file mode 100644 index 000000000..65e900590 --- /dev/null +++ b/unity-2019.4.24f1-mbe/external/bdwgc/windows-untested/vc9/test_libgc.vcproj @@ -0,0 +1,289 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unity-2019.4.24f1-mbe/mono-uninstalled.pc.in b/unity-2019.4.24f1-mbe/mono-uninstalled.pc.in new file mode 100644 index 000000000..7fa3f12dc --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono-uninstalled.pc.in @@ -0,0 +1,6 @@ +Name: Mono +Description: Mono Runtime +Version: @VERSION@ +Requires: glib-2.0 gthread-2.0 +Libs: -L@mono_build_root@/mono/mini/.libs @export_ldflags@ -lmono @libmono_ldflags@ +Cflags: -I@abs_top_srcdir@ -I@abs_top_srcdir@/mono @libmono_cflags@ diff --git a/unity-2019.4.24f1-mbe/mono/.gitignore b/unity-2019.4.24f1-mbe/mono/.gitignore new file mode 100644 index 000000000..f7903956c --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/.gitignore @@ -0,0 +1,11 @@ +/ +/Makefile +/Makefile.in +/semantic.cache +/TAGS +/.metadata + +# Global patterns +.gdb_history +xdb.il +*.[oa] diff --git a/unity-2019.4.24f1-mbe/mono/Makefile.am b/unity-2019.4.24f1-mbe/mono/Makefile.am new file mode 100644 index 000000000..de46d63aa --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/Makefile.am @@ -0,0 +1,40 @@ +if SUPPORT_SGEN +sgen_dirs = sgen +endif + +if BTLS +btls_dirs = btls +endif + +if CROSS_COMPILING +SUBDIRS = $(btls_dirs) eglib arch utils cil metadata $(sgen_dirs) mini dis profiler +else +if INSTALL_MONOTOUCH +SUBDIRS = $(btls_dirs) eglib arch utils metadata $(sgen_dirs) mini profiler + +monotouch-do-build: + @list='$(SUBDIRS)'; for subdir in $$list; do \ + case "x$$subdir" in \ + xmetadata ) target="monotouch-do-build" ;; \ + xmini ) target="monotouch-do-build" ;; \ + * ) target="all" ;; \ + esac; \ + echo "Making $$target in $$subdir"; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$target); \ + done; + +monotouch-do-clean: + @list='$(SUBDIRS)'; for subdir in $$list; do \ + case "x$$subdir" in \ + xmetadata ) target="monotouch-do-clean" ;; \ + xmini ) target="monotouch-do-clean" ;; \ + * ) target="clean" ;; \ + esac; \ + echo "Making $$target in $$subdir"; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$target); \ + done; +else +SUBDIRS = $(btls_dirs) eglib arch utils cil metadata $(sgen_dirs) mini dis tests unit-tests benchmark profiler +endif +endif +DIST_SUBDIRS = btls eglib arch utils cil metadata $(sgen_dirs) mini dis tests unit-tests benchmark profiler diff --git a/unity-2019.4.24f1-mbe/mono/arch/.gitignore b/unity-2019.4.24f1-mbe/mono/arch/.gitignore new file mode 100644 index 000000000..16c984055 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/.gitignore @@ -0,0 +1,6 @@ +/Makefile +/Makefile.in +/.deps +/.libs +/*.la +/*.lo diff --git a/unity-2019.4.24f1-mbe/mono/arch/Makefile.am b/unity-2019.4.24f1-mbe/mono/arch/Makefile.am new file mode 100644 index 000000000..a5c81d135 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/Makefile.am @@ -0,0 +1,10 @@ +DIST_SUBDIRS = x86 ppc sparc arm arm64 s390x amd64 mips + +AM_CPPFLAGS = $(GLIB_CFLAGS) -I$(top_srcdir) + +if ARM +# arm needs to build some stuff even in JIT mode +SUBDIRS = $(arch_target) +endif + + diff --git a/unity-2019.4.24f1-mbe/mono/arch/amd64/.gitignore b/unity-2019.4.24f1-mbe/mono/arch/amd64/.gitignore new file mode 100644 index 000000000..6930f61fc --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/amd64/.gitignore @@ -0,0 +1,4 @@ +/Makefile.in +/Makefile +/.deps +/.libs diff --git a/unity-2019.4.24f1-mbe/mono/arch/amd64/Makefile.am b/unity-2019.4.24f1-mbe/mono/arch/amd64/Makefile.am new file mode 100644 index 000000000..47daaaff6 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/amd64/Makefile.am @@ -0,0 +1,2 @@ +EXTRA_DIST = amd64-codegen.h + diff --git a/unity-2019.4.24f1-mbe/mono/arch/amd64/amd64-codegen.h b/unity-2019.4.24f1-mbe/mono/arch/amd64/amd64-codegen.h new file mode 100644 index 000000000..078e3edb9 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/amd64/amd64-codegen.h @@ -0,0 +1,1540 @@ +/* + * amd64-codegen.h: Macros for generating amd64 code + * + * Authors: + * Paolo Molaro (lupus@ximian.com) + * Intel Corporation (ORP Project) + * Sergey Chaban (serge@wildwestsoftware.com) + * Dietmar Maurer (dietmar@ximian.com) + * Patrik Torstensson + * Zalman Stern + * + * Copyright (C) 2000 Intel Corporation. All rights reserved. + * Copyright (C) 2001, 2002 Ximian, Inc. + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#ifndef AMD64_H +#define AMD64_H + +// Conventions in this file: + +// body: implementation. other macros call this one +// disp: displacement +// inst: instruction +// is_half: short if true, byte if false (then why is it named is_half...?) +// imm: immediate +// mem: read from (immediate-supplied address?) +// membase: read from address in a base register plus a displacement +// memindex: SIP addressing: (address in base register) + (displacement in index register)<<(shift) +// reg: register, encode modR/M bits 00 +// regp: register, encode modR/M bits 11 +// size: Expected 1,2,4 or 8 +// widen: extends from 1 or 2 bytes + +#include + +typedef enum { + AMD64_RAX = 0, + AMD64_RCX = 1, + AMD64_RDX = 2, + AMD64_RBX = 3, + AMD64_RSP = 4, + AMD64_RBP = 5, + AMD64_RSI = 6, + AMD64_RDI = 7, + AMD64_R8 = 8, + AMD64_R9 = 9, + AMD64_R10 = 10, + AMD64_R11 = 11, + AMD64_R12 = 12, + AMD64_R13 = 13, + AMD64_R14 = 14, + AMD64_R15 = 15, + AMD64_RIP = 16, + AMD64_NREG +} AMD64_Reg_No; + +typedef enum { + AMD64_XMM0 = 0, + AMD64_XMM1 = 1, + AMD64_XMM2 = 2, + AMD64_XMM3 = 3, + AMD64_XMM4 = 4, + AMD64_XMM5 = 5, + AMD64_XMM6 = 6, + AMD64_XMM7 = 7, + AMD64_XMM8 = 8, + AMD64_XMM9 = 9, + AMD64_XMM10 = 10, + AMD64_XMM11 = 11, + AMD64_XMM12 = 12, + AMD64_XMM13 = 13, + AMD64_XMM14 = 14, + AMD64_XMM15 = 15, + AMD64_XMM_NREG = 16, +} AMD64_XMM_Reg_No; + +typedef enum +{ + AMD64_REX_B = 1, /* The register in r/m field, base register in SIB byte, or reg in opcode is 8-15 rather than 0-7 */ + AMD64_REX_X = 2, /* The index register in SIB byte is 8-15 rather than 0-7 */ + AMD64_REX_R = 4, /* The reg field of ModRM byte is 8-15 rather than 0-7 */ + AMD64_REX_W = 8 /* Opeartion is 64-bits instead of 32 (default) or 16 (with 0x66 prefix) */ +} AMD64_REX_Bits; + +#define amd64_codegen_pre(inst) +#define amd64_codegen_post(inst) + +#ifdef TARGET_WIN32 +#define AMD64_ARG_REG1 AMD64_RCX +#define AMD64_ARG_REG2 AMD64_RDX +#define AMD64_ARG_REG3 AMD64_R8 +#define AMD64_ARG_REG4 AMD64_R9 +#else +#define AMD64_ARG_REG1 AMD64_RDI +#define AMD64_ARG_REG2 AMD64_RSI +#define AMD64_ARG_REG3 AMD64_RDX +#define AMD64_ARG_REG4 AMD64_RCX +#endif + +#ifdef TARGET_WIN32 +#define AMD64_CALLEE_REGS ((1< 4) ? AMD64_REX_W : 0) | \ + (((reg_modrm) > 7) ? AMD64_REX_R : 0) | \ + (((reg_index) > 7) ? AMD64_REX_X : 0) | \ + (((reg_rm_base_opcode) > 7) ? AMD64_REX_B : 0); \ + if ((_amd64_rex_bits != 0) || (((width) == 1))) *(inst)++ = AMD64_REX(_amd64_rex_bits); \ + } while (0) + +typedef union { + guint64 val; + unsigned char b [8]; +} amd64_imm_buf; + +#include "../x86/x86-codegen.h" + +/* In 64 bit mode, all registers have a low byte subregister */ +#undef X86_IS_BYTE_REG +#define X86_IS_BYTE_REG(reg) 1 + +#define amd64_modrm_mod(modrm) ((modrm) >> 6) +#define amd64_modrm_reg(modrm) (((modrm) >> 3) & 0x7) +#define amd64_modrm_rm(modrm) ((modrm) & 0x7) + +#define amd64_rex_r(rex) ((((rex) >> 2) & 0x1) << 3) +#define amd64_rex_x(rex) ((((rex) >> 1) & 0x1) << 3) +#define amd64_rex_b(rex) ((((rex) >> 0) & 0x1) << 3) + +#define amd64_sib_scale(sib) ((sib) >> 6) +#define amd64_sib_index(sib) (((sib) >> 3) & 0x7) +#define amd64_sib_base(sib) ((sib) & 0x7) + +#define amd64_is_imm32(val) ((gint64)val >= -((gint64)1<<31) && (gint64)val <= (((gint64)1<<31)-1)) + +#define x86_imm_emit64(inst,imm) \ + do { \ + amd64_imm_buf imb; \ + imb.val = (guint64) (imm); \ + *(inst)++ = imb.b [0]; \ + *(inst)++ = imb.b [1]; \ + *(inst)++ = imb.b [2]; \ + *(inst)++ = imb.b [3]; \ + *(inst)++ = imb.b [4]; \ + *(inst)++ = imb.b [5]; \ + *(inst)++ = imb.b [6]; \ + *(inst)++ = imb.b [7]; \ + } while (0) + +#define amd64_membase_emit(inst,reg,basereg,disp) do { \ + if ((basereg) == AMD64_RIP) { \ + x86_address_byte ((inst), 0, (reg)&0x7, 5); \ + x86_imm_emit32 ((inst), (disp)); \ + } \ + else \ + x86_membase_emit ((inst),(reg)&0x7, (basereg)&0x7, (disp)); \ +} while (0) + +#define amd64_alu_reg_imm_size_body(inst,opc,reg,imm,size) \ + do { \ + if (x86_is_imm8((imm))) { \ + amd64_emit_rex(inst, size, 0, 0, (reg)); \ + *(inst)++ = (unsigned char)0x83; \ + x86_reg_emit ((inst), (opc), (reg)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else if ((reg) == AMD64_RAX) { \ + amd64_emit_rex(inst, size, 0, 0, 0); \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 5; \ + x86_imm_emit32 ((inst), (imm)); \ + } else { \ + amd64_emit_rex(inst, size, 0, 0, (reg)); \ + *(inst)++ = (unsigned char)0x81; \ + x86_reg_emit ((inst), (opc), (reg)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define amd64_alu_reg_reg_size_body(inst,opc,dreg,reg,size) \ + do { \ + amd64_emit_rex(inst, size, (dreg), 0, (reg)); \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + } while (0) + +#define amd64_test_reg_imm_size_body(inst,reg,imm,size) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex ((inst),(size),0,0,(reg)); \ + if ((reg) == AMD64_RAX) { \ + *(inst)++ = (unsigned char)0xa9; \ + } \ + else { \ + *(inst)++ = (unsigned char)0xf7; \ + x86_reg_emit((inst), 0, (reg)); \ + } \ + x86_imm_emit32((inst), (imm)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_alu_reg_imm_size(inst,opc,reg,imm,size) \ + amd64_alu_reg_imm_size_body((inst), (opc), (reg), (imm), (size)) + +#define amd64_alu_reg_reg_size(inst,opc,dreg,reg,size) \ + amd64_alu_reg_reg_size_body((inst), (opc), (dreg), (reg), (size)) + +#define amd64_test_reg_imm_size(inst, reg, imm, size) \ + amd64_test_reg_imm_size_body(inst, reg, imm, size) + +#define amd64_alu_reg_imm(inst,opc,reg,imm) amd64_alu_reg_imm_size((inst),(opc),(reg),(imm),8) + +#define amd64_alu_reg_reg(inst,opc,dreg,reg) amd64_alu_reg_reg_size ((inst),(opc),(dreg),(reg),8) + +#define amd64_test_reg_imm(inst,reg,imm) amd64_test_reg_imm_size(inst,reg,imm,8) + +#define amd64_alu_reg_membase_size(inst,opc,reg,basereg,disp,size) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex ((inst),(size),(reg),0,(basereg)); \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ + amd64_membase_emit (inst, reg, basereg, disp); \ + amd64_codegen_post(inst); \ +} while (0) + +#define amd64_mov_regp_reg(inst,regp,reg,size) \ + do { \ + amd64_codegen_pre(inst); \ + if ((size) == 2) \ + x86_prefix((inst), X86_OPERAND_PREFIX); \ + amd64_emit_rex(inst, (size), (reg), 0, (regp)); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x88; break; \ + case 2: case 4: case 8: *(inst)++ = (unsigned char)0x89; break; \ + default: assert (0); \ + } \ + x86_regp_emit ((inst), (reg), (regp)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_mov_membase_reg(inst,basereg,disp,reg,size) \ + do { \ + amd64_codegen_pre(inst); \ + if ((size) == 2) \ + x86_prefix((inst), X86_OPERAND_PREFIX); \ + amd64_emit_rex(inst, (size), (reg), 0, (basereg)); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x88; break; \ + case 2: case 4: case 8: *(inst)++ = (unsigned char)0x89; break; \ + default: assert (0); \ + } \ + x86_membase_emit ((inst), ((reg)&0x7), ((basereg)&0x7), (disp)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_mov_mem_reg(inst,mem,reg,size) \ + do { \ + amd64_codegen_pre(inst); \ + if ((size) == 2) \ + x86_prefix((inst), X86_OPERAND_PREFIX); \ + amd64_emit_rex(inst, (size), (reg), 0, 0); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x88; break; \ + case 2: case 4: case 8: *(inst)++ = (unsigned char)0x89; break; \ + default: assert (0); \ + } \ + x86_address_byte ((inst), 0, (reg), 4); \ + x86_address_byte ((inst), 0, 4, 5); \ + x86_imm_emit32 ((inst), (mem)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_mov_reg_reg(inst,dreg,reg,size) \ + do { \ + amd64_codegen_pre(inst); \ + if ((size) == 2) \ + x86_prefix((inst), X86_OPERAND_PREFIX); \ + amd64_emit_rex(inst, (size), (dreg), 0, (reg)); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x8a; break; \ + case 2: case 4: case 8: *(inst)++ = (unsigned char)0x8b; break; \ + default: assert (0); \ + } \ + x86_reg_emit ((inst), (dreg), (reg)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_mov_reg_mem_body(inst,reg,mem,size) \ + do { \ + amd64_codegen_pre(inst); \ + if ((size) == 2) \ + x86_prefix((inst), X86_OPERAND_PREFIX); \ + amd64_emit_rex(inst, (size), (reg), 0, 0); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x8a; break; \ + case 2: case 4: case 8: *(inst)++ = (unsigned char)0x8b; break; \ + default: assert (0); \ + } \ + x86_address_byte ((inst), 0, (reg), 4); \ + x86_address_byte ((inst), 0, 4, 5); \ + x86_imm_emit32 ((inst), (mem)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_mov_reg_mem(inst,reg,mem,size) \ + do { \ + amd64_mov_reg_mem_body((inst),(reg),(mem),(size)); \ + } while (0) + +#define amd64_mov_reg_membase_body(inst,reg,basereg,disp,size) \ + do { \ + if ((size) == 2) \ + x86_prefix((inst), X86_OPERAND_PREFIX); \ + amd64_emit_rex(inst, (size), (reg), 0, (basereg)); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x8a; break; \ + case 2: case 4: case 8: *(inst)++ = (unsigned char)0x8b; break; \ + default: assert (0); \ + } \ + amd64_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define amd64_mov_reg_memindex_size_body(inst,reg,basereg,disp,indexreg,shift,size) \ + do { \ + amd64_emit_rex ((inst),(size),(reg),(indexreg),(basereg)); \ + x86_mov_reg_memindex((inst),((reg)&0x7),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),(size) == 8 ? 4 : (size)); \ + } while (0) + +#define amd64_mov_reg_memindex_size(inst,reg,basereg,disp,indexreg,shift,size) \ + amd64_mov_reg_memindex_size_body((inst),(reg),(basereg),(disp),(indexreg),(shift),(size)) +#define amd64_mov_reg_membase(inst,reg,basereg,disp,size) \ + do { \ + amd64_mov_reg_membase_body((inst), (reg), (basereg), (disp), (size)); \ + } while (0) + +#define amd64_movzx_reg_membase(inst,reg,basereg,disp,size) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex(inst, (size), (reg), 0, (basereg)); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x0f; *(inst)++ = (unsigned char)0xb6; break; \ + case 2: *(inst)++ = (unsigned char)0x0f; *(inst)++ = (unsigned char)0xb7; break; \ + case 4: case 8: *(inst)++ = (unsigned char)0x8b; break; \ + default: assert (0); \ + } \ + x86_membase_emit ((inst), ((reg)&0x7), ((basereg)&0x7), (disp)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_movsxd_reg_mem(inst,reg,mem) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex(inst,8,(reg),0,0); \ + *(inst)++ = (unsigned char)0x63; \ + x86_mem_emit ((inst), ((reg)&0x7), (mem)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_movsxd_reg_membase(inst,reg,basereg,disp) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex(inst,8,(reg),0,(basereg)); \ + *(inst)++ = (unsigned char)0x63; \ + x86_membase_emit ((inst), ((reg)&0x7), ((basereg)&0x7), (disp)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_movsxd_reg_reg(inst,dreg,reg) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex(inst,8,(dreg),0,(reg)); \ + *(inst)++ = (unsigned char)0x63; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + amd64_codegen_post(inst); \ + } while (0) + +/* Pretty much the only instruction that supports a 64-bit immediate. Optimize for common case of + * 32-bit immediate. Pepper with casts to avoid warnings. + */ +#define amd64_mov_reg_imm_size(inst,reg,imm,size) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex(inst, (size), 0, 0, (reg)); \ + *(inst)++ = (unsigned char)0xb8 + ((reg) & 0x7); \ + if ((size) == 8) \ + x86_imm_emit64 ((inst), (guint64)(imm)); \ + else \ + x86_imm_emit32 ((inst), (int)(guint64)(imm)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_mov_reg_imm(inst,reg,imm) \ + do { \ + int _amd64_width_temp = ((guint64)(imm) == (guint64)(int)(guint64)(imm)); \ + amd64_codegen_pre(inst); \ + amd64_mov_reg_imm_size ((inst), (reg), (imm), (_amd64_width_temp ? 4 : 8)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_set_reg_template(inst,reg) amd64_mov_reg_imm_size ((inst),(reg), 0, 8) + +#define amd64_set_template(inst,reg) amd64_set_reg_template((inst),(reg)) + +#define amd64_mov_membase_imm(inst,basereg,disp,imm,size) \ + do { \ + amd64_codegen_pre(inst); \ + if ((size) == 2) \ + x86_prefix((inst), X86_OPERAND_PREFIX); \ + amd64_emit_rex(inst, (size) == 1 ? 0 : (size), 0, 0, (basereg)); \ + if ((size) == 1) { \ + *(inst)++ = (unsigned char)0xc6; \ + x86_membase_emit ((inst), 0, (basereg) & 0x7, (disp)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else if ((size) == 2) { \ + *(inst)++ = (unsigned char)0xc7; \ + x86_membase_emit ((inst), 0, (basereg) & 0x7, (disp)); \ + x86_imm_emit16 ((inst), (imm)); \ + } else { \ + *(inst)++ = (unsigned char)0xc7; \ + x86_membase_emit ((inst), 0, (basereg) & 0x7, (disp)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + amd64_codegen_post(inst); \ + } while (0) + + +#define amd64_lea_membase_body(inst,reg,basereg,disp) \ + do { \ + amd64_emit_rex(inst, 8, (reg), 0, (basereg)); \ + *(inst)++ = (unsigned char)0x8d; \ + amd64_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define amd64_lea_membase(inst,reg,basereg,disp) \ + amd64_lea_membase_body((inst), (reg), (basereg), (disp)) + +/* Instruction are implicitly 64-bits so don't generate REX for just the size. */ +#define amd64_push_reg(inst,reg) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex(inst, 0, 0, 0, (reg)); \ + *(inst)++ = (unsigned char)0x50 + ((reg) & 0x7); \ + amd64_codegen_post(inst); \ + } while (0) + +/* Instruction is implicitly 64-bits so don't generate REX for just the size. */ +#define amd64_push_membase(inst,basereg,disp) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex(inst, 0, 0, 0, (basereg)); \ + *(inst)++ = (unsigned char)0xff; \ + x86_membase_emit ((inst), 6, (basereg) & 0x7, (disp)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_pop_reg_body(inst,reg) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex(inst, 0, 0, 0, (reg)); \ + *(inst)++ = (unsigned char)0x58 + ((reg) & 0x7); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_call_reg(inst,reg) \ + do { \ + amd64_emit_rex(inst, 0, 0, 0, (reg)); \ + *(inst)++ = (unsigned char)0xff; \ + x86_reg_emit ((inst), 2, ((reg) & 0x7)); \ + } while (0) + + +#define amd64_ret(inst) do { *(inst)++ = (unsigned char)0xc3; } while (0) +#define amd64_leave(inst) do { *(inst)++ = (unsigned char)0xc9; } while (0) + +#define amd64_pop_reg(inst,reg) amd64_pop_reg_body((inst), (reg)) + +#define amd64_movsd_reg_regp(inst,reg,regp) \ + do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), 0xf2); \ + amd64_emit_rex(inst, 0, (reg), 0, (regp)); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x10; \ + x86_regp_emit ((inst), (reg) & 0x7, (regp) & 0x7); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_movsd_regp_reg(inst,regp,reg) \ + do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), 0xf2); \ + amd64_emit_rex(inst, 0, (reg), 0, (regp)); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x11; \ + x86_regp_emit ((inst), (reg) & 0x7, (regp) & 0x7); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_movss_reg_regp(inst,reg,regp) \ + do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), 0xf3); \ + amd64_emit_rex(inst, 0, (reg), 0, (regp)); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x10; \ + x86_regp_emit ((inst), (reg) & 0x7, (regp) & 0x7); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_movss_regp_reg(inst,regp,reg) \ + do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), 0xf3); \ + amd64_emit_rex(inst, 0, (reg), 0, (regp)); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x11; \ + x86_regp_emit ((inst), (reg) & 0x7, (regp) & 0x7); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_movdqu_reg_membase(inst,reg,basereg,disp) \ + do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), 0xf3); \ + amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x6f; \ + x86_membase_emit ((inst), (reg) & 0x7, (basereg) & 0x7, (disp)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_movsd_reg_membase(inst,reg,basereg,disp) \ + do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), 0xf2); \ + amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x10; \ + x86_membase_emit ((inst), (reg) & 0x7, (basereg) & 0x7, (disp)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_movss_reg_membase(inst,reg,basereg,disp) \ + do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), 0xf3); \ + amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x10; \ + x86_membase_emit ((inst), (reg) & 0x7, (basereg) & 0x7, (disp)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_movdqu_membase_reg(inst,basereg,disp,reg) \ + do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), 0xf3); \ + amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x7f; \ + x86_membase_emit ((inst), (reg) & 0x7, (basereg) & 0x7, (disp)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_movsd_membase_reg(inst,basereg,disp,reg) \ + do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), 0xf2); \ + amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x11; \ + x86_membase_emit ((inst), (reg) & 0x7, (basereg) & 0x7, (disp)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_movss_membase_reg(inst,basereg,disp,reg) \ + do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), 0xf3); \ + amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x11; \ + x86_membase_emit ((inst), (reg) & 0x7, (basereg) & 0x7, (disp)); \ + amd64_codegen_post(inst); \ + } while (0) + +/* The original inc_reg opcode is used as the REX prefix */ +#define amd64_inc_reg_size(inst,reg,size) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex ((inst),(size),0,0,(reg)); \ + *(inst)++ = (unsigned char)0xff; \ + x86_reg_emit ((inst),0,(reg) & 0x7); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_dec_reg_size(inst,reg,size) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex ((inst),(size),0,0,(reg)); \ + *(inst)++ = (unsigned char)0xff; \ + x86_reg_emit ((inst),1,(reg) & 0x7); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_fld_membase_size(inst,basereg,disp,is_double,size) do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex ((inst),0,0,0,(basereg)); \ + *(inst)++ = (is_double) ? (unsigned char)0xdd : (unsigned char)0xd9; \ + amd64_membase_emit ((inst), 0, (basereg), (disp)); \ + amd64_codegen_post(inst); \ +} while (0) + +/* From the AMD64 Software Optimization Manual */ +#define amd64_padding_size(inst,size) \ + do { \ + switch ((size)) { \ + case 1: *(inst)++ = 0x90; break; \ + case 2: *(inst)++ = 0x66; *(inst)++ = 0x90; break; \ + case 3: *(inst)++ = 0x66; *(inst)++ = 0x66; *(inst)++ = 0x90; break; \ + default: amd64_emit_rex ((inst),8,0,0,0); x86_padding ((inst), (size) - 1); \ + }; \ + } while (0) + +#define amd64_call_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),0,0,0,(basereg)); *(inst)++ = (unsigned char)0xff; amd64_membase_emit ((inst),2, (basereg),(disp)); } while (0) +#define amd64_jump_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),0,0,0,(basereg)); *(inst)++ = (unsigned char)0xff; amd64_membase_emit ((inst), 4, (basereg), (disp)); } while (0) + +#define amd64_jump_code_size(inst,target,size) do { \ + if (amd64_is_imm32 ((gint64)(target) - (gint64)(inst))) { \ + x86_jump_code((inst),(target)); \ + } else { \ + amd64_jump_membase ((inst), AMD64_RIP, 0); \ + *(guint64*)(inst) = (guint64)(target); \ + (inst) += 8; \ + } \ +} while (0) + +/* + * SSE + */ + +//TODO Reorganize SSE opcode defines. + +/* Two opcode SSE defines */ + +#define emit_sse_reg_reg_op2_size(inst,dreg,reg,op1,op2,size) do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex ((inst), size, (dreg), 0, (reg)); \ + *(inst)++ = (unsigned char)(op1); \ + *(inst)++ = (unsigned char)(op2); \ + x86_reg_emit ((inst), (dreg), (reg)); \ + amd64_codegen_post(inst); \ +} while (0) + +#define emit_sse_reg_reg_op2(inst,dreg,reg,op1,op2) emit_sse_reg_reg_op2_size ((inst), (dreg), (reg), (op1), (op2), 0) + +#define emit_sse_reg_reg_op2_imm(inst,dreg,reg,op1,op2,imm) do { \ + amd64_codegen_pre(inst); \ + emit_sse_reg_reg_op2 ((inst), (dreg), (reg), (op1), (op2)); \ + x86_imm_emit8 ((inst), (imm)); \ + amd64_codegen_post(inst); \ +} while (0) + +#define emit_sse_membase_reg_op2(inst,basereg,disp,reg,op1,op2) do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex ((inst), 0, (reg), 0, (basereg)); \ + *(inst)++ = (unsigned char)(op1); \ + *(inst)++ = (unsigned char)(op2); \ + amd64_membase_emit ((inst), (reg), (basereg), (disp)); \ + amd64_codegen_post(inst); \ +} while (0) + +#define emit_sse_reg_membase_op2(inst,dreg,basereg,disp,op1,op2) do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex ((inst), 0, (dreg), 0, (basereg) == AMD64_RIP ? 0 : (basereg)); \ + *(inst)++ = (unsigned char)(op1); \ + *(inst)++ = (unsigned char)(op2); \ + amd64_membase_emit ((inst), (dreg), (basereg), (disp)); \ + amd64_codegen_post(inst); \ +} while (0) + +/* Three opcode SSE defines */ + +#define emit_opcode3(inst,op1,op2,op3) do { \ + *(inst)++ = (unsigned char)(op1); \ + *(inst)++ = (unsigned char)(op2); \ + *(inst)++ = (unsigned char)(op3); \ +} while (0) + +#define emit_sse_reg_reg_size(inst,dreg,reg,op1,op2,op3,size) do { \ + amd64_codegen_pre(inst); \ + *(inst)++ = (unsigned char)(op1); \ + amd64_emit_rex ((inst), size, (dreg), 0, (reg)); \ + *(inst)++ = (unsigned char)(op2); \ + *(inst)++ = (unsigned char)(op3); \ + x86_reg_emit ((inst), (dreg), (reg)); \ + amd64_codegen_post(inst); \ +} while (0) + +#define emit_sse_reg_reg(inst,dreg,reg,op1,op2,op3) emit_sse_reg_reg_size ((inst), (dreg), (reg), (op1), (op2), (op3), 0) + +#define emit_sse_reg_reg_imm(inst,dreg,reg,op1,op2,op3,imm) do { \ + amd64_codegen_pre(inst); \ + emit_sse_reg_reg ((inst), (dreg), (reg), (op1), (op2), (op3)); \ + x86_imm_emit8 ((inst), (imm)); \ + amd64_codegen_post(inst); \ +} while (0) + +#define emit_sse_membase_reg(inst,basereg,disp,reg,op1,op2,op3) do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), (unsigned char)(op1)); \ + amd64_emit_rex ((inst), 0, (reg), 0, (basereg)); \ + *(inst)++ = (unsigned char)(op2); \ + *(inst)++ = (unsigned char)(op3); \ + amd64_membase_emit ((inst), (reg), (basereg), (disp)); \ + amd64_codegen_post(inst); \ +} while (0) + +#define emit_sse_reg_membase(inst,dreg,basereg,disp,op1,op2,op3) do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), (unsigned char)(op1)); \ + amd64_emit_rex ((inst), 0, (dreg), 0, (basereg) == AMD64_RIP ? 0 : (basereg)); \ + *(inst)++ = (unsigned char)(op2); \ + *(inst)++ = (unsigned char)(op3); \ + amd64_membase_emit ((inst), (dreg), (basereg), (disp)); \ + amd64_codegen_post(inst); \ +} while (0) + +/* Four opcode SSE defines */ + +#define emit_sse_reg_reg_op4_size(inst,dreg,reg,op1,op2,op3,op4,size) do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), (unsigned char)(op1)); \ + amd64_emit_rex ((inst), size, (dreg), 0, (reg)); \ + *(inst)++ = (unsigned char)(op2); \ + *(inst)++ = (unsigned char)(op3); \ + *(inst)++ = (unsigned char)(op4); \ + x86_reg_emit ((inst), (dreg), (reg)); \ + amd64_codegen_post(inst); \ +} while (0) + +#define emit_sse_reg_reg_op4(inst,dreg,reg,op1,op2,op3,op4) emit_sse_reg_reg_op4_size ((inst), (dreg), (reg), (op1), (op2), (op3), (op4), 0) + +/* specific SSE opcode defines */ + +#define amd64_sse_xorpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst),(dreg),(reg), 0x66, 0x0f, 0x57) + +#define amd64_sse_xorpd_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst),(dreg),(basereg), (disp), 0x66, 0x0f, 0x57) + +#define amd64_sse_andpd_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst),(dreg),(basereg), (disp), 0x66, 0x0f, 0x54) + +#define amd64_sse_movsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x10) +#define amd64_sse_movss_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf3, 0x0f, 0x10) + +#define amd64_sse_movsd_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst), (dreg), (basereg), (disp), 0xf2, 0x0f, 0x10) + +#define amd64_sse_movsd_membase_reg(inst,basereg,disp,reg) emit_sse_membase_reg ((inst), (basereg), (disp), (reg), 0xf2, 0x0f, 0x11) + +#define amd64_sse_movss_membase_reg(inst,basereg,disp,reg) emit_sse_membase_reg ((inst), (basereg), (disp), (reg), 0xf3, 0x0f, 0x11) + +#define amd64_sse_movss_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst), (dreg), (basereg), (disp), 0xf3, 0x0f, 0x10) + +#define amd64_sse_comisd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst),(dreg),(reg),0x66,0x0f,0x2f) +#define amd64_sse_comiss_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst),(dreg),(reg),0x67,0x0f,0x2f) + +#define amd64_sse_comisd_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst), (dreg), (basereg), (disp), 0x66, 0x0f, 0x2f) + +#define amd64_sse_ucomisd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst),(dreg),(reg),0x66,0x0f,0x2e) + +#define amd64_sse_cvtsd2si_reg_reg(inst,dreg,reg) emit_sse_reg_reg_size ((inst), (dreg), (reg), 0xf2, 0x0f, 0x2d, 8) +#define amd64_sse_cvtss2si_reg_reg(inst,dreg,reg) emit_sse_reg_reg_size ((inst), (dreg), (reg), 0xf3, 0x0f, 0x2d, 8) + +#define amd64_sse_cvttsd2si_reg_reg_size(inst,dreg,reg,size) emit_sse_reg_reg_size ((inst), (dreg), (reg), 0xf2, 0x0f, 0x2c, (size)) +#define amd64_sse_cvtss2si_reg_reg_size(inst,dreg,reg,size) emit_sse_reg_reg_size ((inst), (dreg), (reg), 0xf3, 0x0f, 0x2c, (size)) + +#define amd64_sse_cvttsd2si_reg_reg(inst,dreg,reg) amd64_sse_cvttsd2si_reg_reg_size ((inst), (dreg), (reg), 8) + +#define amd64_sse_cvtsi2sd_reg_reg_size(inst,dreg,reg,size) emit_sse_reg_reg_size ((inst), (dreg), (reg), 0xf2, 0x0f, 0x2a, (size)) + +#define amd64_sse_cvtsi2sd_reg_reg(inst,dreg,reg) amd64_sse_cvtsi2sd_reg_reg_size ((inst), (dreg), (reg), 8) + +#define amd64_sse_cvtsi2ss_reg_reg_size(inst,dreg,reg,size) emit_sse_reg_reg_size ((inst), (dreg), (reg), 0xf3, 0x0f, 0x2a, (size)) + +#define amd64_sse_cvtsi2ss_reg_reg(inst,dreg,reg) amd64_sse_cvtsi2ss_reg_reg_size ((inst), (dreg), (reg), 8) + +#define amd64_sse_cvtsd2ss_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x5a) + +#define amd64_sse_cvtss2sd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf3, 0x0f, 0x5a) + +#define amd64_sse_addsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x58) +#define amd64_sse_addss_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf3, 0x0f, 0x58) + +#define amd64_sse_subsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x5c) +#define amd64_sse_subss_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf3, 0x0f, 0x5c) + +#define amd64_sse_mulsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x59) +#define amd64_sse_mulss_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf3, 0x0f, 0x59) + +#define amd64_sse_divsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x5e) +#define amd64_sse_divss_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf3, 0x0f, 0x5e) + +#define amd64_sse_sqrtsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf2, 0x0f, 0x51) + + +#define amd64_sse_pinsrw_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm ((inst), (dreg), (reg), 0x66, 0x0f, 0xc4, (imm)) + +#define amd64_sse_pextrw_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm ((inst), (dreg), (reg), 0x66, 0x0f, 0xc5, (imm)) + + +#define amd64_sse_cvttsd2si_reg_xreg_size(inst,reg,xreg,size) emit_sse_reg_reg_size ((inst), (reg), (xreg), 0xf2, 0x0f, 0x2c, (size)) + + +#define amd64_sse_addps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x58) + +#define amd64_sse_divps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x5e) + +#define amd64_sse_mulps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x59) + +#define amd64_sse_subps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x5c) + +#define amd64_sse_maxps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x5f) + +#define amd64_sse_minps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x5d) + +#define amd64_sse_cmpps_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_op2_imm((inst), (dreg), (reg), 0x0f, 0xc2, (imm)) + +#define amd64_sse_andps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x54) + +#define amd64_sse_andnps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x55) + +#define amd64_sse_orps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x56) + +#define amd64_sse_xorps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x57) + +#define amd64_sse_sqrtps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x51) + +#define amd64_sse_rsqrtps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x52) + +#define amd64_sse_rcpps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x53) + +#define amd64_sse_addsubps_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf2, 0x0f, 0xd0) + +#define amd64_sse_haddps_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf2, 0x0f, 0x7c) + +#define amd64_sse_hsubps_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf2, 0x0f, 0x7d) + +#define amd64_sse_movshdup_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf3, 0x0f, 0x16) + +#define amd64_sse_movsldup_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf3, 0x0f, 0x12) + + +#define amd64_sse_pshufhw_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm((inst), (dreg), (reg), 0xf3, 0x0f, 0x70, (imm)) + +#define amd64_sse_pshuflw_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm((inst), (dreg), (reg), 0xf2, 0x0f, 0x70, (imm)) + +#define amd64_sse_pshufd_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm((inst), (dreg), (reg), 0x66, 0x0f, 0x70, (imm)) + +#define amd64_sse_shufps_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_op2_imm((inst), (dreg), (reg), 0x0f, 0xC6, (imm)) + +#define amd64_sse_shufpd_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm((inst), (dreg), (reg), 0x66, 0x0f, 0xC6, (imm)) + + +#define amd64_sse_addpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x58) + +#define amd64_sse_divpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x5e) + +#define amd64_sse_mulpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x59) + +#define amd64_sse_subpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x5c) + +#define amd64_sse_maxpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x5f) + +#define amd64_sse_minpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x5d) + +#define amd64_sse_cmppd_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm((inst), (dreg), (reg), 0x66, 0x0f, 0xc2, (imm)) + +#define amd64_sse_andpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x54) + +#define amd64_sse_andnpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x55) + +#define amd64_sse_orpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x56) + +#define amd64_sse_sqrtpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x51) + +#define amd64_sse_rsqrtpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x52) + +#define amd64_sse_rcppd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x53) + +#define amd64_sse_addsubpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd0) + +#define amd64_sse_haddpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x7c) + +#define amd64_sse_hsubpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x7d) + +#define amd64_sse_movddup_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf2, 0x0f, 0x12) + + +#define amd64_sse_pmovmskb_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd7) + + +#define amd64_sse_pand_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xdb) + +#define amd64_sse_por_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xeb) + +#define amd64_sse_pxor_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xef) + + +#define amd64_sse_paddb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xfc) + +#define amd64_sse_paddw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xfd) + +#define amd64_sse_paddd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xfe) + +#define amd64_sse_paddq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd4) + + +#define amd64_sse_psubb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf8) + +#define amd64_sse_psubw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf9) + +#define amd64_sse_psubd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xfa) + +#define amd64_sse_psubq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xfb) + + +#define amd64_sse_pmaxub_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xde) + +#define amd64_sse_pmaxuw_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x3e) + +#define amd64_sse_pmaxud_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x3f) + + +#define amd64_sse_pmaxsb_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x3c) + +#define amd64_sse_pmaxsw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xee) + +#define amd64_sse_pmaxsd_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x3d) + + +#define amd64_sse_pavgb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe0) + +#define amd64_sse_pavgw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe3) + + +#define amd64_sse_pminub_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xda) + +#define amd64_sse_pminuw_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x3a) + +#define amd64_sse_pminud_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x3b) + + +#define amd64_sse_pminsb_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x38) + +#define amd64_sse_pminsw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xea) + +#define amd64_sse_pminsd_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x39) + + +#define amd64_sse_pcmpeqb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x74) + +#define amd64_sse_pcmpeqw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x75) + +#define amd64_sse_pcmpeqd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x76) + +#define amd64_sse_pcmpeqq_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x29) + + +#define amd64_sse_pcmpgtb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x64) + +#define amd64_sse_pcmpgtw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x65) + +#define amd64_sse_pcmpgtd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x66) + +#define amd64_sse_pcmpgtq_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x37) + + +#define amd64_sse_psadbw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf6) + + +#define amd64_sse_punpcklbw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x60) + +#define amd64_sse_punpcklwd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x61) + +#define amd64_sse_punpckldq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x62) + +#define amd64_sse_punpcklqdq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x6c) + +#define amd64_sse_unpcklpd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x14) + +#define amd64_sse_unpcklps_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x14) + + +#define amd64_sse_punpckhbw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x68) + +#define amd64_sse_punpckhwd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x69) + +#define amd64_sse_punpckhdq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x6a) + +#define amd64_sse_punpckhqdq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x6d) + +#define amd64_sse_unpckhpd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x15) + +#define amd64_sse_unpckhps_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x15) + + +#define amd64_sse_packsswb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x63) + +#define amd64_sse_packssdw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x6b) + +#define amd64_sse_packuswb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x67) + +#define amd64_sse_packusdw_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x2b) + + +#define amd64_sse_paddusb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xdc) + +#define amd64_sse_psubusb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd8) + +#define amd64_sse_paddusw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xdd) + +#define amd64_sse_psubusw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd8) + + +#define amd64_sse_paddsb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xec) + +#define amd64_sse_psubsb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe8) + +#define amd64_sse_paddsw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xed) + +#define amd64_sse_psubsw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe9) + + +#define amd64_sse_pmullw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd5) + +#define amd64_sse_pmulld_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x40) + +#define amd64_sse_pmuludq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf4) + +#define amd64_sse_pmulhuw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe4) + +#define amd64_sse_pmulhw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe5) + + +#define amd64_sse_psrlw_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SHR, (reg), 0x66, 0x0f, 0x71, (imm)) + +#define amd64_sse_psrlw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd1) + + +#define amd64_sse_psraw_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SAR, (reg), 0x66, 0x0f, 0x71, (imm)) + +#define amd64_sse_psraw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe1) + + +#define amd64_sse_psllw_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SHL, (reg), 0x66, 0x0f, 0x71, (imm)) + +#define amd64_sse_psllw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf1) + + +#define amd64_sse_psrld_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SHR, (reg), 0x66, 0x0f, 0x72, (imm)) + +#define amd64_sse_psrld_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd2) + + +#define amd64_sse_psrad_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SAR, (reg), 0x66, 0x0f, 0x72, (imm)) + +#define amd64_sse_psrad_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe2) + + +#define amd64_sse_pslld_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SHL, (reg), 0x66, 0x0f, 0x72, (imm)) + +#define amd64_sse_pslld_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf2) + + +#define amd64_sse_psrlq_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SHR, (reg), 0x66, 0x0f, 0x73, (imm)) + +#define amd64_sse_psrlq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd3) + + +#define amd64_sse_psraq_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SAR, (reg), 0x66, 0x0f, 0x73, (imm)) + +#define amd64_sse_psraq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe3) + + +#define amd64_sse_psllq_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SHL, (reg), 0x66, 0x0f, 0x73, (imm)) + +#define amd64_sse_psllq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf3) + + +#define amd64_sse_cvtdq2pd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xF3, 0x0F, 0xE6) + +#define amd64_sse_cvtdq2ps_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0F, 0x5B) + +#define amd64_sse_cvtpd2dq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xF2, 0x0F, 0xE6) + +#define amd64_sse_cvtpd2ps_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0F, 0x5A) + +#define amd64_sse_cvtps2dq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0F, 0x5B) + +#define amd64_sse_cvtps2pd_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0F, 0x5A) + +#define amd64_sse_cvttpd2dq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0F, 0xE6) + +#define amd64_sse_cvttps2dq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xF3, 0x0F, 0x5B) + + +#define amd64_movd_xreg_reg_size(inst,dreg,sreg,size) emit_sse_reg_reg_size((inst), (dreg), (sreg), 0x66, 0x0f, 0x6e, (size)) + +#define amd64_movd_reg_xreg_size(inst,dreg,sreg,size) emit_sse_reg_reg_size((inst), (sreg), (dreg), 0x66, 0x0f, 0x7e, (size)) + +#define amd64_movd_xreg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase((inst), (dreg), (basereg), (disp), 0x66, 0x0f, 0x6e) + + +#define amd64_movlhps_reg_reg(inst,dreg,sreg) emit_sse_reg_reg_op2((inst), (dreg), (sreg), 0x0f, 0x16) + +#define amd64_movhlps_reg_reg(inst,dreg,sreg) emit_sse_reg_reg_op2((inst), (dreg), (sreg), 0x0f, 0x12) + + +#define amd64_sse_movups_membase_reg(inst, basereg, disp, reg) emit_sse_membase_reg_op2((inst), (basereg), (disp), (reg), 0x0f, 0x11) + +#define amd64_sse_movups_reg_membase(inst, dreg, basereg, disp) emit_sse_reg_membase_op2((inst), (dreg), (basereg), (disp), 0x0f, 0x10) + +#define amd64_sse_movaps_membase_reg(inst, basereg, disp, reg) emit_sse_membase_reg_op2((inst), (basereg), (disp), (reg), 0x0f, 0x29) + +#define amd64_sse_movaps_reg_membase(inst, dreg, basereg, disp) emit_sse_reg_membase_op2((inst), (dreg), (basereg), (disp), 0x0f, 0x28) + +#define amd64_sse_movaps_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x28) + +#define amd64_sse_movntps_reg_membase(inst, dreg, basereg, disp) emit_sse_reg_membase_op2((inst), (dreg), (basereg), (disp), 0x0f, 0x2b) + +#define amd64_sse_prefetch_reg_membase(inst, arg, basereg, disp) emit_sse_reg_membase_op2((inst), (arg), (basereg), (disp), 0x0f, 0x18) + +/* Generated from x86-codegen.h */ + +#define amd64_breakpoint_size(inst,size) do { x86_breakpoint(inst); } while (0) +#define amd64_cld_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_cld(inst); amd64_codegen_post(inst); } while (0) +#define amd64_stosb_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_stosb(inst); amd64_codegen_post(inst); } while (0) +#define amd64_stosl_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_stosl(inst); amd64_codegen_post(inst); } while (0) +#define amd64_stosd_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_stosd(inst); amd64_codegen_post(inst); } while (0) +#define amd64_movsb_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_movsb(inst); amd64_codegen_post(inst); } while (0) +#define amd64_movsl_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_movsl(inst); amd64_codegen_post(inst); } while (0) +#define amd64_movsd_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_movsd(inst); amd64_codegen_post(inst); } while (0) +#define amd64_prefix_size(inst,p,size) do { x86_prefix((inst), p); } while (0) +#define amd64_rdtsc_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_rdtsc(inst); amd64_codegen_post(inst); } while (0) +#define amd64_cmpxchg_reg_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_cmpxchg_reg_reg((inst),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_cmpxchg_mem_reg_size(inst,mem,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_cmpxchg_mem_reg((inst),(mem),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_cmpxchg_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_cmpxchg_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_xchg_reg_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_xchg_reg_reg((inst),((dreg)&0x7),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +#define amd64_xchg_mem_reg_size(inst,mem,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_xchg_mem_reg((inst),(mem),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +#define amd64_xchg_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_xchg_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +#define amd64_inc_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_inc_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_inc_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_inc_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +//#define amd64_inc_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_inc_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_dec_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_dec_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_dec_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_dec_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +//#define amd64_dec_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_dec_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_not_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_not_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_not_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_not_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_not_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_not_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_neg_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_neg_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_neg_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_neg_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_neg_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_neg_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_nop_size(inst,size) do { amd64_codegen_pre(inst); x86_nop(inst); amd64_codegen_post(inst); } while (0) +//#define amd64_alu_reg_imm_size(inst,opc,reg,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_alu_reg_imm((inst),(opc),((reg)&0x7),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_alu_mem_imm_size(inst,opc,mem,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_alu_mem_imm((inst),(opc),(mem),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_alu_membase_imm_size(inst,opc,basereg,disp,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_alu_membase_imm((inst),(opc),((basereg)&0x7),(disp),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_alu_membase8_imm_size(inst,opc,basereg,disp,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_alu_membase8_imm((inst),(opc),((basereg)&0x7),(disp),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_alu_mem_reg_size(inst,opc,mem,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_alu_mem_reg((inst),(opc),(mem),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_alu_membase_reg_size(inst,opc,basereg,disp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_alu_membase_reg((inst),(opc),((basereg)&0x7),(disp),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +//#define amd64_alu_reg_reg_size(inst,opc,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_alu_reg_reg((inst),(opc),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_alu_reg8_reg8_size(inst,opc,dreg,reg,is_dreg_h,is_reg_h,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_alu_reg8_reg8((inst),(opc),((dreg)&0x7),((reg)&0x7),(is_dreg_h),(is_reg_h)); amd64_codegen_post(inst); } while (0) +#define amd64_alu_reg_mem_size(inst,opc,reg,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_alu_reg_mem((inst),(opc),((reg)&0x7),(mem)); amd64_codegen_post(inst); } while (0) +//#define amd64_alu_reg_membase_size(inst,opc,reg,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_alu_reg_membase((inst),(opc),((reg)&0x7),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +//#define amd64_test_reg_imm_size(inst,reg,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_test_reg_imm((inst),((reg)&0x7),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_test_mem_imm_size(inst,mem,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_test_mem_imm((inst),(mem),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_test_membase_imm_size(inst,basereg,disp,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_test_membase_imm((inst),((basereg)&0x7),(disp),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_test_reg_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_test_reg_reg((inst),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_test_mem_reg_size(inst,mem,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_test_mem_reg((inst),(mem),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_test_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_test_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_shift_reg_imm_size(inst,opc,reg,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_shift_reg_imm((inst),(opc),((reg)&0x7),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_shift_mem_imm_size(inst,opc,mem,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_shift_mem_imm((inst),(opc),(mem),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_shift_membase_imm_size(inst,opc,basereg,disp,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_shift_membase_imm((inst),(opc),((basereg)&0x7),(disp),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_shift_reg_size(inst,opc,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_shift_reg((inst),(opc),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_shift_mem_size(inst,opc,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_shift_mem((inst),(opc),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_shift_membase_size(inst,opc,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_shift_membase((inst),(opc),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_shrd_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_shrd_reg((inst),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_shrd_reg_imm_size(inst,dreg,reg,shamt,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_shrd_reg_imm((inst),((dreg)&0x7),((reg)&0x7),(shamt)); amd64_codegen_post(inst); } while (0) +#define amd64_shld_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_shld_reg((inst),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_shld_reg_imm_size(inst,dreg,reg,shamt,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_shld_reg_imm((inst),((dreg)&0x7),((reg)&0x7),(shamt)); amd64_codegen_post(inst); } while (0) +#define amd64_mul_reg_size(inst,reg,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_mul_reg((inst),((reg)&0x7),(is_signed)); amd64_codegen_post(inst); } while (0) +#define amd64_mul_mem_size(inst,mem,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_mul_mem((inst),(mem),(is_signed)); amd64_codegen_post(inst); } while (0) +#define amd64_mul_membase_size(inst,basereg,disp,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_mul_membase((inst),((basereg)&0x7),(disp),(is_signed)); amd64_codegen_post(inst); } while (0) +#define amd64_imul_reg_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_imul_reg_reg((inst),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_imul_reg_mem_size(inst,reg,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_imul_reg_mem((inst),((reg)&0x7),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_imul_reg_membase_size(inst,reg,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_imul_reg_membase((inst),((reg)&0x7),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_imul_reg_reg_imm_size(inst,dreg,reg,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_imul_reg_reg_imm((inst),((dreg)&0x7),((reg)&0x7),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_imul_reg_mem_imm_size(inst,reg,mem,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_imul_reg_mem_imm((inst),((reg)&0x7),(mem),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_imul_reg_membase_imm_size(inst,reg,basereg,disp,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_imul_reg_membase_imm((inst),((reg)&0x7),((basereg)&0x7),(disp),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_div_reg_size(inst,reg,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_div_reg((inst),((reg)&0x7),(is_signed)); amd64_codegen_post(inst); } while (0) +#define amd64_div_mem_size(inst,mem,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_div_mem((inst),(mem),(is_signed)); amd64_codegen_post(inst); } while (0) +#define amd64_div_membase_size(inst,basereg,disp,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_div_membase((inst),((basereg)&0x7),(disp),(is_signed)); amd64_codegen_post(inst); } while (0) +#define amd64_mov_mem_reg_size(inst,mem,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_mov_mem_reg((inst),(mem),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +//#define amd64_mov_regp_reg_size(inst,regp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(regp),0,(reg)); x86_mov_regp_reg((inst),(regp),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +//#define amd64_mov_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_mov_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +#define amd64_mov_memindex_reg_size(inst,basereg,disp,indexreg,shift,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),(indexreg),(basereg)); x86_mov_memindex_reg((inst),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +#define amd64_mov_reg_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_mov_reg_reg((inst),((dreg)&0x7),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +//#define amd64_mov_reg_mem_size(inst,reg,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_mov_reg_mem((inst),((reg)&0x7),(mem),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +//#define amd64_mov_reg_membase_size(inst,reg,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_mov_reg_membase((inst),((reg)&0x7),((basereg)&0x7),(disp),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +//#define amd64_mov_reg_memindex_size(inst,reg,basereg,disp,indexreg,shift,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),(indexreg),(basereg)); x86_mov_reg_memindex((inst),((reg)&0x7),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +#define amd64_clear_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_clear_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +//#define amd64_mov_reg_imm_size(inst,reg,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_mov_reg_imm((inst),((reg)&0x7),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_mov_mem_imm_size(inst,mem,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_mov_mem_imm((inst),(mem),(imm),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +//#define amd64_mov_membase_imm_size(inst,basereg,disp,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_mov_membase_imm((inst),((basereg)&0x7),(disp),(imm),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +#define amd64_mov_memindex_imm_size(inst,basereg,disp,indexreg,shift,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,(indexreg),(basereg)); x86_mov_memindex_imm((inst),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),(imm),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +#define amd64_lea_mem_size(inst,reg,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_lea_mem((inst),((reg)&0x7),(mem)); amd64_codegen_post(inst); } while (0) +//#define amd64_lea_membase_size(inst,reg,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_lea_membase((inst),((reg)&0x7),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_lea_memindex_size(inst,reg,basereg,disp,indexreg,shift,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),(indexreg),(basereg)); x86_lea_memindex((inst),((reg)&0x7),((basereg)&0x7),(disp),((indexreg)&0x7),(shift)); amd64_codegen_post(inst); } while (0) +#define amd64_widen_reg_size(inst,dreg,reg,is_signed,is_half,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_widen_reg((inst),((dreg)&0x7),((reg)&0x7),(is_signed),(is_half)); amd64_codegen_post(inst); } while (0) +#define amd64_widen_mem_size(inst,dreg,mem,is_signed,is_half,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,0); x86_widen_mem((inst),((dreg)&0x7),(mem),(is_signed),(is_half)); amd64_codegen_post(inst); } while (0) +#define amd64_widen_membase_size(inst,dreg,basereg,disp,is_signed,is_half,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(basereg)); x86_widen_membase((inst),((dreg)&0x7),((basereg)&0x7),(disp),(is_signed),(is_half)); amd64_codegen_post(inst); } while (0) +#define amd64_widen_memindex_size(inst,dreg,basereg,disp,indexreg,shift,is_signed,is_half,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),(indexreg),(basereg)); x86_widen_memindex((inst),((dreg)&0x7),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),(is_signed),(is_half)); amd64_codegen_post(inst); } while (0) +#define amd64_cdq_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_cdq(inst); amd64_codegen_post(inst); } while (0) +#define amd64_wait_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_wait(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fp_op_mem_size(inst,opc,mem,is_double,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fp_op_mem((inst),(opc),(mem),(is_double)); amd64_codegen_post(inst); } while (0) +#define amd64_fp_op_membase_size(inst,opc,basereg,disp,is_double,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fp_op_membase((inst),(opc),((basereg)&0x7),(disp),(is_double)); amd64_codegen_post(inst); } while (0) +#define amd64_fp_op_size(inst,opc,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fp_op((inst),(opc),(index)); amd64_codegen_post(inst); } while (0) +#define amd64_fp_op_reg_size(inst,opc,index,pop_stack,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fp_op_reg((inst),(opc),(index),(pop_stack)); amd64_codegen_post(inst); } while (0) +#define amd64_fp_int_op_membase_size(inst,opc,basereg,disp,is_int,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fp_int_op_membase((inst),(opc),((basereg)&0x7),(disp),(is_int)); amd64_codegen_post(inst); } while (0) +#define amd64_fstp_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fstp((inst),(index)); amd64_codegen_post(inst); } while (0) +#define amd64_fcompp_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fcompp(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fucompp_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fucompp(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fnstsw_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fnstsw(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fnstcw_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fnstcw((inst),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_fnstcw_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_fnstcw_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_fldcw_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fldcw((inst),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_fldcw_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fldcw_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_fchs_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fchs(inst); amd64_codegen_post(inst); } while (0) +#define amd64_frem_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_frem(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fxch_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fxch((inst),(index)); amd64_codegen_post(inst); } while (0) +#define amd64_fcomi_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fcomi((inst),(index)); amd64_codegen_post(inst); } while (0) +#define amd64_fcomip_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fcomip((inst),(index)); amd64_codegen_post(inst); } while (0) +#define amd64_fucomi_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fucomi((inst),(index)); amd64_codegen_post(inst); } while (0) +#define amd64_fucomip_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fucomip((inst),(index)); amd64_codegen_post(inst); } while (0) +#define amd64_fld_size(inst,mem,is_double,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fld((inst),(mem),(is_double)); amd64_codegen_post(inst); } while (0) +//#define amd64_fld_membase_size(inst,basereg,disp,is_double,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fld_membase((inst),((basereg)&0x7),(disp),(is_double)); amd64_codegen_post(inst); } while (0) +#define amd64_fld80_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fld80_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_fld80_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_fld80_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_fild_size(inst,mem,is_long,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fild((inst),(mem),(is_long)); amd64_codegen_post(inst); } while (0) +#define amd64_fild_membase_size(inst,basereg,disp,is_long,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fild_membase((inst),((basereg)&0x7),(disp),(is_long)); amd64_codegen_post(inst); } while (0) +#define amd64_fld_reg_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fld_reg((inst),(index)); amd64_codegen_post(inst); } while (0) +#define amd64_fldz_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fldz(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fld1_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fld1(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fldpi_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fldpi(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fst_size(inst,mem,is_double,pop_stack,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fst((inst),(mem),(is_double),(pop_stack)); amd64_codegen_post(inst); } while (0) +#define amd64_fst_membase_size(inst,basereg,disp,is_double,pop_stack,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fst_membase((inst),((basereg)&0x7),(disp),(is_double),(pop_stack)); amd64_codegen_post(inst); } while (0) +#define amd64_fst80_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fst80_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_fst80_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fst80_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_fist_pop_size(inst,mem,is_long,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fist_pop((inst),(mem),(is_long)); amd64_codegen_post(inst); } while (0) +#define amd64_fist_pop_membase_size(inst,basereg,disp,is_long,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fist_pop_membase((inst),((basereg)&0x7),(disp),(is_long)); amd64_codegen_post(inst); } while (0) +#define amd64_fstsw_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_fstsw(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fist_membase_size(inst,basereg,disp,is_int,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fist_membase((inst),((basereg)&0x7),(disp),(is_int)); amd64_codegen_post(inst); } while (0) +//#define amd64_push_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_push_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_push_regp_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_push_regp((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_push_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_push_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) +//#define amd64_push_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_push_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_push_memindex_size(inst,basereg,disp,indexreg,shift,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,(indexreg),(basereg)); x86_push_memindex((inst),((basereg)&0x7),(disp),((indexreg)&0x7),(shift)); amd64_codegen_post(inst); } while (0) +#define amd64_push_imm_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_push_imm((inst),(imm)); amd64_codegen_post(inst); } while (0) +//#define amd64_pop_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_pop_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_pop_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_pop_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_pop_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_pop_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_pushad_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_pushad(inst); amd64_codegen_post(inst); } while (0) +#define amd64_pushfd_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_pushfd(inst); amd64_codegen_post(inst); } while (0) +#define amd64_popad_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_popad(inst); amd64_codegen_post(inst); } while (0) +#define amd64_popfd_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_popfd(inst); amd64_codegen_post(inst); } while (0) +#define amd64_loop_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_loop((inst),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_loope_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_loope((inst),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_loopne_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_loopne((inst),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_jump32_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_jump32((inst),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_jump8_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_jump8((inst),(imm)); amd64_codegen_post(inst); } while (0) +/* Defined above for Native Client, so they can be used in other macros */ +#define amd64_jump_reg_size(inst,reg,size) do { amd64_emit_rex ((inst),0,0,0,(reg)); x86_jump_reg((inst),((reg)&0x7)); } while (0) +#define amd64_jump_mem_size(inst,mem,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_jump_mem((inst),(mem)); } while (0) +#define amd64_jump_disp_size(inst,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_jump_disp((inst),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_branch8_size(inst,cond,imm,is_signed,size) do { x86_branch8((inst),(cond),(imm),(is_signed)); } while (0) +#define amd64_branch32_size(inst,cond,imm,is_signed,size) do { x86_branch32((inst),(cond),(imm),(is_signed)); } while (0) +#define amd64_branch_size_body(inst,cond,target,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_branch((inst),(cond),(target),(is_signed)); amd64_codegen_post(inst); } while (0) +#define amd64_branch_size(inst,cond,target,is_signed,size) do { amd64_branch_size_body((inst),(cond),(target),(is_signed),(size)); } while (0) + +#define amd64_branch_disp_size(inst,cond,disp,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_branch_disp((inst),(cond),(disp),(is_signed)); amd64_codegen_post(inst); } while (0) +#define amd64_set_reg_size(inst,cond,reg,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex((inst),1,0,0,(reg)); x86_set_reg((inst),(cond),((reg)&0x7),(is_signed)); amd64_codegen_post(inst); } while (0) +#define amd64_set_mem_size(inst,cond,mem,is_signed,size) do { amd64_codegen_pre(inst); x86_set_mem((inst),(cond),(mem),(is_signed)); amd64_codegen_post(inst); } while (0) +#define amd64_set_membase_size(inst,cond,basereg,disp,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_set_membase((inst),(cond),((basereg)&0x7),(disp),(is_signed)); amd64_codegen_post(inst); } while (0) +//#define amd64_call_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_call_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_call_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_call_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) + +#define amd64_call_imm_size(inst,disp,size) do { x86_call_imm((inst),(disp)); } while (0) +#define amd64_call_code_size(inst,target,size) do { x86_call_code((inst),(target)); } while (0) + +//#define amd64_ret_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_ret(inst); amd64_codegen_post(inst); } while (0) +#define amd64_ret_imm_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_ret_imm((inst),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_cmov_reg_size(inst,cond,is_signed,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_cmov_reg((inst),(cond),(is_signed),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_cmov_mem_size(inst,cond,is_signed,reg,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_cmov_mem((inst),(cond),(is_signed),((reg)&0x7),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_cmov_membase_size(inst,cond,is_signed,reg,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_cmov_membase((inst),(cond),(is_signed),((reg)&0x7),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_enter_size(inst,framesize) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_enter((inst),(framesize)); amd64_codegen_post(inst); } while (0) +//#define amd64_leave_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_leave(inst); amd64_codegen_post(inst); } while (0) +#define amd64_sahf_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_sahf(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fsin_size(inst,size) do { amd64_codegen_pre(inst); x86_fsin(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fcos_size(inst,size) do { amd64_codegen_pre(inst); x86_fcos(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fabs_size(inst,size) do { amd64_codegen_pre(inst); x86_fabs(inst); amd64_codegen_post(inst); } while (0) +#define amd64_ftst_size(inst,size) do { amd64_codegen_pre(inst); x86_ftst(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fxam_size(inst,size) do { amd64_codegen_pre(inst); x86_fxam(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fpatan_size(inst,size) do { amd64_codegen_pre(inst); x86_fpatan(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fprem_size(inst,size) do { amd64_codegen_pre(inst); x86_fprem(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fprem1_size(inst,size) do { amd64_codegen_pre(inst); x86_fprem1(inst); amd64_codegen_post(inst); } while (0) +#define amd64_frndint_size(inst,size) do { amd64_codegen_pre(inst); x86_frndint(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fsqrt_size(inst,size) do { amd64_codegen_pre(inst); x86_fsqrt(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fptan_size(inst,size) do { amd64_codegen_pre(inst); x86_fptan(inst); amd64_codegen_post(inst); } while (0) +//#define amd64_padding_size(inst,size) do { amd64_codegen_pre(inst); x86_padding((inst),(size)); amd64_codegen_post(inst); } while (0) +#define amd64_prolog_size(inst,frame_size,reg_mask,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_prolog((inst),(frame_size),(reg_mask)); amd64_codegen_post(inst); } while (0) +#define amd64_epilog_size(inst,reg_mask,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_epilog((inst),(reg_mask)); amd64_codegen_post(inst); } while (0) +#define amd64_xadd_reg_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_xadd_reg_reg ((inst), (dreg), (reg), (size)); amd64_codegen_post(inst); } while (0) +#define amd64_xadd_mem_reg_size(inst,mem,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_xadd_mem_reg((inst),(mem),((reg)&0x7), (size)); amd64_codegen_post(inst); } while (0) +#define amd64_xadd_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_xadd_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7),(size)); amd64_codegen_post(inst); } while (0) + + + + +#define amd64_breakpoint(inst) amd64_breakpoint_size(inst,8) +#define amd64_cld(inst) amd64_cld_size(inst,8) +#define amd64_stosb(inst) amd64_stosb_size(inst,8) +#define amd64_stosl(inst) amd64_stosl_size(inst,8) +#define amd64_stosd(inst) amd64_stosd_size(inst,8) +#define amd64_movsb(inst) amd64_movsb_size(inst,8) +#define amd64_movsl(inst) amd64_movsl_size(inst,8) +#define amd64_movsd(inst) amd64_movsd_size(inst,8) +#define amd64_prefix(inst,p) amd64_prefix_size(inst,p,8) +#define amd64_rdtsc(inst) amd64_rdtsc_size(inst,8) +#define amd64_cmpxchg_reg_reg(inst,dreg,reg) amd64_cmpxchg_reg_reg_size(inst,dreg,reg,8) +#define amd64_cmpxchg_mem_reg(inst,mem,reg) amd64_cmpxchg_mem_reg_size(inst,mem,reg,8) +#define amd64_cmpxchg_membase_reg(inst,basereg,disp,reg) amd64_cmpxchg_membase_reg_size(inst,basereg,disp,reg,8) +#define amd64_xchg_reg_reg(inst,dreg,reg,size) amd64_xchg_reg_reg_size(inst,dreg,reg,size) +#define amd64_xchg_mem_reg(inst,mem,reg,size) amd64_xchg_mem_reg_size(inst,mem,reg,size) +#define amd64_xchg_membase_reg(inst,basereg,disp,reg,size) amd64_xchg_membase_reg_size(inst,basereg,disp,reg,size) +#define amd64_xadd_reg_reg(inst,dreg,reg,size) amd64_xadd_reg_reg_size(inst,dreg,reg,size) +#define amd64_xadd_mem_reg(inst,mem,reg,size) amd64_xadd_mem_reg_size(inst,mem,reg,size) +#define amd64_xadd_membase_reg(inst,basereg,disp,reg,size) amd64_xadd_membase_reg_size(inst,basereg,disp,reg,size) +#define amd64_inc_mem(inst,mem) amd64_inc_mem_size(inst,mem,8) +#define amd64_inc_membase(inst,basereg,disp) amd64_inc_membase_size(inst,basereg,disp,8) +#define amd64_inc_reg(inst,reg) amd64_inc_reg_size(inst,reg,8) +#define amd64_dec_mem(inst,mem) amd64_dec_mem_size(inst,mem,8) +#define amd64_dec_membase(inst,basereg,disp) amd64_dec_membase_size(inst,basereg,disp,8) +#define amd64_dec_reg(inst,reg) amd64_dec_reg_size(inst,reg,8) +#define amd64_not_mem(inst,mem) amd64_not_mem_size(inst,mem,8) +#define amd64_not_membase(inst,basereg,disp) amd64_not_membase_size(inst,basereg,disp,8) +#define amd64_not_reg(inst,reg) amd64_not_reg_size(inst,reg,8) +#define amd64_neg_mem(inst,mem) amd64_neg_mem_size(inst,mem,8) +#define amd64_neg_membase(inst,basereg,disp) amd64_neg_membase_size(inst,basereg,disp,8) +#define amd64_neg_reg(inst,reg) amd64_neg_reg_size(inst,reg,8) +#define amd64_nop(inst) amd64_nop_size(inst,8) +//#define amd64_alu_reg_imm(inst,opc,reg,imm) amd64_alu_reg_imm_size(inst,opc,reg,imm,8) +#define amd64_alu_mem_imm(inst,opc,mem,imm) amd64_alu_mem_imm_size(inst,opc,mem,imm,8) +#define amd64_alu_membase_imm(inst,opc,basereg,disp,imm) amd64_alu_membase_imm_size(inst,opc,basereg,disp,imm,8) +#define amd64_alu_mem_reg(inst,opc,mem,reg) amd64_alu_mem_reg_size(inst,opc,mem,reg,8) +#define amd64_alu_membase_reg(inst,opc,basereg,disp,reg) amd64_alu_membase_reg_size(inst,opc,basereg,disp,reg,8) +//#define amd64_alu_reg_reg(inst,opc,dreg,reg) amd64_alu_reg_reg_size(inst,opc,dreg,reg,8) +#define amd64_alu_reg8_reg8(inst,opc,dreg,reg,is_dreg_h,is_reg_h) amd64_alu_reg8_reg8_size(inst,opc,dreg,reg,is_dreg_h,is_reg_h,8) +#define amd64_alu_reg_mem(inst,opc,reg,mem) amd64_alu_reg_mem_size(inst,opc,reg,mem,8) +#define amd64_alu_reg_membase(inst,opc,reg,basereg,disp) amd64_alu_reg_membase_size(inst,opc,reg,basereg,disp,8) +//#define amd64_test_reg_imm(inst,reg,imm) amd64_test_reg_imm_size(inst,reg,imm,8) +#define amd64_test_mem_imm(inst,mem,imm) amd64_test_mem_imm_size(inst,mem,imm,8) +#define amd64_test_membase_imm(inst,basereg,disp,imm) amd64_test_membase_imm_size(inst,basereg,disp,imm,8) +#define amd64_test_reg_reg(inst,dreg,reg) amd64_test_reg_reg_size(inst,dreg,reg,8) +#define amd64_test_mem_reg(inst,mem,reg) amd64_test_mem_reg_size(inst,mem,reg,8) +#define amd64_test_membase_reg(inst,basereg,disp,reg) amd64_test_membase_reg_size(inst,basereg,disp,reg,8) +#define amd64_shift_reg_imm(inst,opc,reg,imm) amd64_shift_reg_imm_size(inst,opc,reg,imm,8) +#define amd64_shift_mem_imm(inst,opc,mem,imm) amd64_shift_mem_imm_size(inst,opc,mem,imm,8) +#define amd64_shift_membase_imm(inst,opc,basereg,disp,imm) amd64_shift_membase_imm_size(inst,opc,basereg,disp,imm,8) +#define amd64_shift_reg(inst,opc,reg) amd64_shift_reg_size(inst,opc,reg,8) +#define amd64_shift_mem(inst,opc,mem) amd64_shift_mem_size(inst,opc,mem,8) +#define amd64_shift_membase(inst,opc,basereg,disp) amd64_shift_membase_size(inst,opc,basereg,disp,8) +#define amd64_shrd_reg(inst,dreg,reg) amd64_shrd_reg_size(inst,dreg,reg,8) +#define amd64_shrd_reg_imm(inst,dreg,reg,shamt) amd64_shrd_reg_imm_size(inst,dreg,reg,shamt,8) +#define amd64_shld_reg(inst,dreg,reg) amd64_shld_reg_size(inst,dreg,reg,8) +#define amd64_shld_reg_imm(inst,dreg,reg,shamt) amd64_shld_reg_imm_size(inst,dreg,reg,shamt,8) +#define amd64_mul_reg(inst,reg,is_signed) amd64_mul_reg_size(inst,reg,is_signed,8) +#define amd64_mul_mem(inst,mem,is_signed) amd64_mul_mem_size(inst,mem,is_signed,8) +#define amd64_mul_membase(inst,basereg,disp,is_signed) amd64_mul_membase_size(inst,basereg,disp,is_signed,8) +#define amd64_imul_reg_reg(inst,dreg,reg) amd64_imul_reg_reg_size(inst,dreg,reg,8) +#define amd64_imul_reg_mem(inst,reg,mem) amd64_imul_reg_mem_size(inst,reg,mem,8) +#define amd64_imul_reg_membase(inst,reg,basereg,disp) amd64_imul_reg_membase_size(inst,reg,basereg,disp,8) +#define amd64_imul_reg_reg_imm(inst,dreg,reg,imm) amd64_imul_reg_reg_imm_size(inst,dreg,reg,imm,8) +#define amd64_imul_reg_mem_imm(inst,reg,mem,imm) amd64_imul_reg_mem_imm_size(inst,reg,mem,imm,8) +#define amd64_imul_reg_membase_imm(inst,reg,basereg,disp,imm) amd64_imul_reg_membase_imm_size(inst,reg,basereg,disp,imm,8) +#define amd64_div_reg(inst,reg,is_signed) amd64_div_reg_size(inst,reg,is_signed,8) +#define amd64_div_mem(inst,mem,is_signed) amd64_div_mem_size(inst,mem,is_signed,8) +#define amd64_div_membase(inst,basereg,disp,is_signed) amd64_div_membase_size(inst,basereg,disp,is_signed,8) +//#define amd64_mov_mem_reg(inst,mem,reg,size) amd64_mov_mem_reg_size(inst,mem,reg,size) +//#define amd64_mov_regp_reg(inst,regp,reg,size) amd64_mov_regp_reg_size(inst,regp,reg,size) +//#define amd64_mov_membase_reg(inst,basereg,disp,reg,size) amd64_mov_membase_reg_size(inst,basereg,disp,reg,size) +#define amd64_mov_memindex_reg(inst,basereg,disp,indexreg,shift,reg,size) amd64_mov_memindex_reg_size(inst,basereg,disp,indexreg,shift,reg,size) +//#define amd64_mov_reg_reg(inst,dreg,reg,size) amd64_mov_reg_reg_size(inst,dreg,reg,size) +//#define amd64_mov_reg_mem(inst,reg,mem,size) amd64_mov_reg_mem_size(inst,reg,mem,size) +//#define amd64_mov_reg_membase(inst,reg,basereg,disp,size) amd64_mov_reg_membase_size(inst,reg,basereg,disp,size) +#define amd64_mov_reg_memindex(inst,reg,basereg,disp,indexreg,shift,size) amd64_mov_reg_memindex_size(inst,reg,basereg,disp,indexreg,shift,size) +#define amd64_clear_reg(inst,reg) amd64_clear_reg_size(inst,reg,8) +//#define amd64_mov_reg_imm(inst,reg,imm) amd64_mov_reg_imm_size(inst,reg,imm,8) +#define amd64_mov_mem_imm(inst,mem,imm,size) amd64_mov_mem_imm_size(inst,mem,imm,size) +//#define amd64_mov_membase_imm(inst,basereg,disp,imm,size) amd64_mov_membase_imm_size(inst,basereg,disp,imm,size) +#define amd64_mov_memindex_imm(inst,basereg,disp,indexreg,shift,imm,size) amd64_mov_memindex_imm_size(inst,basereg,disp,indexreg,shift,imm,size) +#define amd64_lea_mem(inst,reg,mem) amd64_lea_mem_size(inst,reg,mem,8) +//#define amd64_lea_membase(inst,reg,basereg,disp) amd64_lea_membase_size(inst,reg,basereg,disp,8) +#define amd64_lea_memindex(inst,reg,basereg,disp,indexreg,shift) amd64_lea_memindex_size(inst,reg,basereg,disp,indexreg,shift,8) +#define amd64_widen_reg(inst,dreg,reg,is_signed,is_half) amd64_widen_reg_size(inst,dreg,reg,is_signed,is_half,8) +#define amd64_widen_mem(inst,dreg,mem,is_signed,is_half) amd64_widen_mem_size(inst,dreg,mem,is_signed,is_half,8) +#define amd64_widen_membase(inst,dreg,basereg,disp,is_signed,is_half) amd64_widen_membase_size(inst,dreg,basereg,disp,is_signed,is_half,8) +#define amd64_widen_memindex(inst,dreg,basereg,disp,indexreg,shift,is_signed,is_half) amd64_widen_memindex_size(inst,dreg,basereg,disp,indexreg,shift,is_signed,is_half,8) +#define amd64_cdq(inst) amd64_cdq_size(inst,8) +#define amd64_wait(inst) amd64_wait_size(inst,8) +#define amd64_fp_op_mem(inst,opc,mem,is_double) amd64_fp_op_mem_size(inst,opc,mem,is_double,8) +#define amd64_fp_op_membase(inst,opc,basereg,disp,is_double) amd64_fp_op_membase_size(inst,opc,basereg,disp,is_double,8) +#define amd64_fp_op(inst,opc,index) amd64_fp_op_size(inst,opc,index,8) +#define amd64_fp_op_reg(inst,opc,index,pop_stack) amd64_fp_op_reg_size(inst,opc,index,pop_stack,8) +#define amd64_fp_int_op_membase(inst,opc,basereg,disp,is_int) amd64_fp_int_op_membase_size(inst,opc,basereg,disp,is_int,8) +#define amd64_fstp(inst,index) amd64_fstp_size(inst,index,8) +#define amd64_fcompp(inst) amd64_fcompp_size(inst,8) +#define amd64_fucompp(inst) amd64_fucompp_size(inst,8) +#define amd64_fnstsw(inst) amd64_fnstsw_size(inst,8) +#define amd64_fnstcw(inst,mem) amd64_fnstcw_size(inst,mem,8) +#define amd64_fnstcw_membase(inst,basereg,disp) amd64_fnstcw_membase_size(inst,basereg,disp,8) +#define amd64_fldcw(inst,mem) amd64_fldcw_size(inst,mem,8) +#define amd64_fldcw_membase(inst,basereg,disp) amd64_fldcw_membase_size(inst,basereg,disp,8) +#define amd64_fchs(inst) amd64_fchs_size(inst,8) +#define amd64_frem(inst) amd64_frem_size(inst,8) +#define amd64_fxch(inst,index) amd64_fxch_size(inst,index,8) +#define amd64_fcomi(inst,index) amd64_fcomi_size(inst,index,8) +#define amd64_fcomip(inst,index) amd64_fcomip_size(inst,index,8) +#define amd64_fucomi(inst,index) amd64_fucomi_size(inst,index,8) +#define amd64_fucomip(inst,index) amd64_fucomip_size(inst,index,8) +#define amd64_fld(inst,mem,is_double) amd64_fld_size(inst,mem,is_double,8) +#define amd64_fld_membase(inst,basereg,disp,is_double) amd64_fld_membase_size(inst,basereg,disp,is_double,8) +#define amd64_fld80_mem(inst,mem) amd64_fld80_mem_size(inst,mem,8) +#define amd64_fld80_membase(inst,basereg,disp) amd64_fld80_membase_size(inst,basereg,disp,8) +#define amd64_fild(inst,mem,is_long) amd64_fild_size(inst,mem,is_long,8) +#define amd64_fild_membase(inst,basereg,disp,is_long) amd64_fild_membase_size(inst,basereg,disp,is_long,8) +#define amd64_fld_reg(inst,index) amd64_fld_reg_size(inst,index,8) +#define amd64_fldz(inst) amd64_fldz_size(inst,8) +#define amd64_fld1(inst) amd64_fld1_size(inst,8) +#define amd64_fldpi(inst) amd64_fldpi_size(inst,8) +#define amd64_fst(inst,mem,is_double,pop_stack) amd64_fst_size(inst,mem,is_double,pop_stack,8) +#define amd64_fst_membase(inst,basereg,disp,is_double,pop_stack) amd64_fst_membase_size(inst,basereg,disp,is_double,pop_stack,8) +#define amd64_fst80_mem(inst,mem) amd64_fst80_mem_size(inst,mem,8) +#define amd64_fst80_membase(inst,basereg,disp) amd64_fst80_membase_size(inst,basereg,disp,8) +#define amd64_fist_pop(inst,mem,is_long) amd64_fist_pop_size(inst,mem,is_long,8) +#define amd64_fist_pop_membase(inst,basereg,disp,is_long) amd64_fist_pop_membase_size(inst,basereg,disp,is_long,8) +#define amd64_fstsw(inst) amd64_fstsw_size(inst,8) +#define amd64_fist_membase(inst,basereg,disp,is_int) amd64_fist_membase_size(inst,basereg,disp,is_int,8) +//#define amd64_push_reg(inst,reg) amd64_push_reg_size(inst,reg,8) +#define amd64_push_regp(inst,reg) amd64_push_regp_size(inst,reg,8) +#define amd64_push_mem(inst,mem) amd64_push_mem_size(inst,mem,8) +//#define amd64_push_membase(inst,basereg,disp) amd64_push_membase_size(inst,basereg,disp,8) +#define amd64_push_memindex(inst,basereg,disp,indexreg,shift) amd64_push_memindex_size(inst,basereg,disp,indexreg,shift,8) +#define amd64_push_imm(inst,imm) amd64_push_imm_size(inst,imm,8) +//#define amd64_pop_reg(inst,reg) amd64_pop_reg_size(inst,reg,8) +#define amd64_pop_mem(inst,mem) amd64_pop_mem_size(inst,mem,8) +#define amd64_pop_membase(inst,basereg,disp) amd64_pop_membase_size(inst,basereg,disp,8) +#define amd64_pushad(inst) amd64_pushad_size(inst,8) +#define amd64_pushfd(inst) amd64_pushfd_size(inst,8) +#define amd64_popad(inst) amd64_popad_size(inst,8) +#define amd64_popfd(inst) amd64_popfd_size(inst,8) +#define amd64_loop(inst,imm) amd64_loop_size(inst,imm,8) +#define amd64_loope(inst,imm) amd64_loope_size(inst,imm,8) +#define amd64_loopne(inst,imm) amd64_loopne_size(inst,imm,8) +#define amd64_jump32(inst,imm) amd64_jump32_size(inst,imm,8) +#define amd64_jump8(inst,imm) amd64_jump8_size(inst,imm,8) +#define amd64_jump_reg(inst,reg) amd64_jump_reg_size(inst,reg,8) +#define amd64_jump_mem(inst,mem) amd64_jump_mem_size(inst,mem,8) +#define amd64_jump_membase(inst,basereg,disp) amd64_jump_membase_size(inst,basereg,disp,8) +#define amd64_jump_code(inst,target) amd64_jump_code_size(inst,target,8) +#define amd64_jump_disp(inst,disp) amd64_jump_disp_size(inst,disp,8) +#define amd64_branch8(inst,cond,imm,is_signed) amd64_branch8_size(inst,cond,imm,is_signed,8) +#define amd64_branch32(inst,cond,imm,is_signed) amd64_branch32_size(inst,cond,imm,is_signed,8) +#define amd64_branch(inst,cond,target,is_signed) amd64_branch_size(inst,cond,target,is_signed,8) +#define amd64_branch_disp(inst,cond,disp,is_signed) amd64_branch_disp_size(inst,cond,disp,is_signed,8) +#define amd64_set_reg(inst,cond,reg,is_signed) amd64_set_reg_size(inst,cond,reg,is_signed,8) +#define amd64_set_mem(inst,cond,mem,is_signed) amd64_set_mem_size(inst,cond,mem,is_signed,8) +#define amd64_set_membase(inst,cond,basereg,disp,is_signed) amd64_set_membase_size(inst,cond,basereg,disp,is_signed,8) +#define amd64_call_imm(inst,disp) amd64_call_imm_size(inst,disp,8) +//#define amd64_call_reg(inst,reg) amd64_call_reg_size(inst,reg,8) +#define amd64_call_mem(inst,mem) amd64_call_mem_size(inst,mem,8) +#define amd64_call_membase(inst,basereg,disp) amd64_call_membase_size(inst,basereg,disp,8) +#define amd64_call_code(inst,target) amd64_call_code_size(inst,target,8) +//#define amd64_ret(inst) amd64_ret_size(inst,8) +#define amd64_ret_imm(inst,imm) amd64_ret_imm_size(inst,imm,8) +#define amd64_cmov_reg(inst,cond,is_signed,dreg,reg) amd64_cmov_reg_size(inst,cond,is_signed,dreg,reg,8) +#define amd64_cmov_mem(inst,cond,is_signed,reg,mem) amd64_cmov_mem_size(inst,cond,is_signed,reg,mem,8) +#define amd64_cmov_membase(inst,cond,is_signed,reg,basereg,disp) amd64_cmov_membase_size(inst,cond,is_signed,reg,basereg,disp,8) +#define amd64_enter(inst,framesize) amd64_enter_size(inst,framesize) +//#define amd64_leave(inst) amd64_leave_size(inst,8) +#define amd64_sahf(inst) amd64_sahf_size(inst,8) +#define amd64_fsin(inst) amd64_fsin_size(inst,8) +#define amd64_fcos(inst) amd64_fcos_size(inst,8) +#define amd64_fabs(inst) amd64_fabs_size(inst,8) +#define amd64_ftst(inst) amd64_ftst_size(inst,8) +#define amd64_fxam(inst) amd64_fxam_size(inst,8) +#define amd64_fpatan(inst) amd64_fpatan_size(inst,8) +#define amd64_fprem(inst) amd64_fprem_size(inst,8) +#define amd64_fprem1(inst) amd64_fprem1_size(inst,8) +#define amd64_frndint(inst) amd64_frndint_size(inst,8) +#define amd64_fsqrt(inst) amd64_fsqrt_size(inst,8) +#define amd64_fptan(inst) amd64_fptan_size(inst,8) +#define amd64_padding(inst,size) amd64_padding_size(inst,size) +#define amd64_prolog(inst,frame,reg_mask) amd64_prolog_size(inst,frame,reg_mask,8) +#define amd64_epilog(inst,reg_mask) amd64_epilog_size(inst,reg_mask,8) + +#endif // AMD64_H diff --git a/unity-2019.4.24f1-mbe/mono/arch/arm/.gitattributes b/unity-2019.4.24f1-mbe/mono/arch/arm/.gitattributes new file mode 100644 index 000000000..4819db1bc --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/arm/.gitattributes @@ -0,0 +1 @@ +/arm-wmmx.h -crlf diff --git a/unity-2019.4.24f1-mbe/mono/arch/arm/.gitignore b/unity-2019.4.24f1-mbe/mono/arch/arm/.gitignore new file mode 100644 index 000000000..7040246be --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/arm/.gitignore @@ -0,0 +1,12 @@ +/Makefile +/Makefile.in +/.deps +/.libs +/*.o +/*.la +/*.lo +/*.lib +/*.obj +/*.exe +/*.dll +/fixeol.sh diff --git a/unity-2019.4.24f1-mbe/mono/arch/arm/Makefile.am b/unity-2019.4.24f1-mbe/mono/arch/arm/Makefile.am new file mode 100644 index 000000000..6c13092a2 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/arm/Makefile.am @@ -0,0 +1,18 @@ + +AM_CPPFLAGS = $(GLIB_CFLAGS) -I$(top_srcdir) + +arm_dpimacros.h: dpiops.sh mov_macros.th dpi_macros.th cmp_macros.th + (cd $(srcdir); bash ./dpiops.sh) > $@t + mv $@t $@ + +arm_vfpmacros.h: vfpops.sh vfpm_macros.th vfp_macros.th + (cd $(srcdir); bash ./vfpops.sh) > $@t + mv $@t $@ + +gen: arm_dpimacros.h arm_vfpmacros.h + +EXTRA_DIST = arm-codegen.h dpiops.sh mov_macros.th dpi_macros.th cmp_macros.th \ + vfpm_macros.th vfp_macros.th arm-vfp-codegen.h vfpops.sh \ + arm_dpimacros.h arm_vfpmacros.h + + diff --git a/unity-2019.4.24f1-mbe/mono/arch/arm/arm-codegen.h b/unity-2019.4.24f1-mbe/mono/arch/arm/arm-codegen.h new file mode 100644 index 000000000..9b2141521 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/arm/arm-codegen.h @@ -0,0 +1,1127 @@ +/* + * arm-codegen.h + * Copyright (c) 2002-2003 Sergey Chaban + * Copyright 2005-2011 Novell Inc + * Copyright 2011 Xamarin Inc + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + + +#ifndef ARM_H +#define ARM_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned int arminstr_t; +typedef unsigned int armword_t; + +#if defined(_MSC_VER) || defined(__CC_NORCROFT) + void __inline _arm_emit(arminstr_t** p, arminstr_t i) {**p = i; (*p)++;} +# define ARM_EMIT(p, i) _arm_emit((arminstr_t**)&p, (arminstr_t)(i)) +#else +# define ARM_EMIT(p, i) do { arminstr_t *__ainstrp = (void*)(p); *__ainstrp = (arminstr_t)(i); (p) = (void*)(__ainstrp+1);} while (0) +#endif + +#if defined(_MSC_VER) && !defined(ARM_NOIASM) +# define ARM_IASM(_expr) __emit (_expr) +#else +# define ARM_IASM(_expr) +#endif + +/* even_scale = rot << 1 */ +#define ARM_SCALE(imm8, even_scale) ( ((imm8) >> (even_scale)) | ((imm8) << (32 - even_scale)) ) + + + +typedef enum { + ARMREG_R0 = 0, + ARMREG_R1, + ARMREG_R2, + ARMREG_R3, + ARMREG_R4, + ARMREG_R5, + ARMREG_R6, + ARMREG_R7, + ARMREG_R8, + ARMREG_R9, + ARMREG_R10, + ARMREG_R11, + ARMREG_R12, + ARMREG_R13, + ARMREG_R14, + ARMREG_R15, + + + /* aliases */ + /* args */ + ARMREG_A1 = ARMREG_R0, + ARMREG_A2 = ARMREG_R1, + ARMREG_A3 = ARMREG_R2, + ARMREG_A4 = ARMREG_R3, + + /* local vars */ + ARMREG_V1 = ARMREG_R4, + ARMREG_V2 = ARMREG_R5, + ARMREG_V3 = ARMREG_R6, + ARMREG_V4 = ARMREG_R7, + ARMREG_V5 = ARMREG_R8, + ARMREG_V6 = ARMREG_R9, + ARMREG_V7 = ARMREG_R10, + + ARMREG_FP = ARMREG_R11, + ARMREG_IP = ARMREG_R12, + ARMREG_SP = ARMREG_R13, + ARMREG_LR = ARMREG_R14, + ARMREG_PC = ARMREG_R15, + + /* co-processor */ + ARMREG_CR0 = 0, + ARMREG_CR1, + ARMREG_CR2, + ARMREG_CR3, + ARMREG_CR4, + ARMREG_CR5, + ARMREG_CR6, + ARMREG_CR7, + ARMREG_CR8, + ARMREG_CR9, + ARMREG_CR10, + ARMREG_CR11, + ARMREG_CR12, + ARMREG_CR13, + ARMREG_CR14, + ARMREG_CR15, + + /* XScale: acc0 on CP0 */ + ARMREG_ACC0 = ARMREG_CR0, + + ARMREG_MAX = ARMREG_R15 +} ARMReg; + +/* number of argument registers */ +#define ARM_NUM_ARG_REGS 4 + +/* bitvector for all argument regs (A1-A4) */ +#define ARM_ALL_ARG_REGS \ + (1 << ARMREG_A1) | (1 << ARMREG_A2) | (1 << ARMREG_A3) | (1 << ARMREG_A4) + + +typedef enum { + ARMCOND_EQ = 0x0, /* Equal; Z = 1 */ + ARMCOND_NE = 0x1, /* Not equal, or unordered; Z = 0 */ + ARMCOND_CS = 0x2, /* Carry set; C = 1 */ + ARMCOND_HS = ARMCOND_CS, /* Unsigned higher or same; */ + ARMCOND_CC = 0x3, /* Carry clear; C = 0 */ + ARMCOND_LO = ARMCOND_CC, /* Unsigned lower */ + ARMCOND_MI = 0x4, /* Negative; N = 1 */ + ARMCOND_PL = 0x5, /* Positive or zero; N = 0 */ + ARMCOND_VS = 0x6, /* Overflow; V = 1 */ + ARMCOND_VC = 0x7, /* No overflow; V = 0 */ + ARMCOND_HI = 0x8, /* Unsigned higher; C = 1 && Z = 0 */ + ARMCOND_LS = 0x9, /* Unsigned lower or same; C = 0 || Z = 1 */ + ARMCOND_GE = 0xA, /* Signed greater than or equal; N = V */ + ARMCOND_LT = 0xB, /* Signed less than; N != V */ + ARMCOND_GT = 0xC, /* Signed greater than; Z = 0 && N = V */ + ARMCOND_LE = 0xD, /* Signed less than or equal; Z = 1 && N != V */ + ARMCOND_AL = 0xE, /* Always */ + ARMCOND_NV = 0xF, /* Never */ + + ARMCOND_SHIFT = 28 +} ARMCond; + +#define ARMCOND_MASK (ARMCOND_NV << ARMCOND_SHIFT) + +#define ARM_DEF_COND(cond) (((cond) & 0xF) << ARMCOND_SHIFT) + + + +typedef enum { + ARMSHIFT_LSL = 0, + ARMSHIFT_LSR = 1, + ARMSHIFT_ASR = 2, + ARMSHIFT_ROR = 3, + + ARMSHIFT_ASL = ARMSHIFT_LSL + /* rrx = (ror, 1) */ +} ARMShiftType; + + +typedef struct { + armword_t PSR_c : 8; + armword_t PSR_x : 8; + armword_t PSR_s : 8; + armword_t PSR_f : 8; +} ARMPSR; + +typedef enum { + ARMOP_AND = 0x0, + ARMOP_EOR = 0x1, + ARMOP_SUB = 0x2, + ARMOP_RSB = 0x3, + ARMOP_ADD = 0x4, + ARMOP_ADC = 0x5, + ARMOP_SBC = 0x6, + ARMOP_RSC = 0x7, + ARMOP_TST = 0x8, + ARMOP_TEQ = 0x9, + ARMOP_CMP = 0xa, + ARMOP_CMN = 0xb, + ARMOP_ORR = 0xc, + ARMOP_MOV = 0xd, + ARMOP_BIC = 0xe, + ARMOP_MVN = 0xf, + + + /* not really opcodes */ + + ARMOP_STR = 0x0, + ARMOP_LDR = 0x1, + + /* ARM2+ */ + ARMOP_MUL = 0x0, /* Rd := Rm*Rs */ + ARMOP_MLA = 0x1, /* Rd := (Rm*Rs)+Rn */ + + /* ARM3M+ */ + ARMOP_UMULL = 0x4, + ARMOP_UMLAL = 0x5, + ARMOP_SMULL = 0x6, + ARMOP_SMLAL = 0x7, + + /* for data transfers with register offset */ + ARM_UP = 1, + ARM_DOWN = 0 +} ARMOpcode; + +typedef enum { + THUMBOP_AND = 0, + THUMBOP_EOR = 1, + THUMBOP_LSL = 2, + THUMBOP_LSR = 3, + THUMBOP_ASR = 4, + THUMBOP_ADC = 5, + THUMBOP_SBC = 6, + THUMBOP_ROR = 7, + THUMBOP_TST = 8, + THUMBOP_NEG = 9, + THUMBOP_CMP = 10, + THUMBOP_CMN = 11, + THUMBOP_ORR = 12, + THUMBOP_MUL = 13, + THUMBOP_BIC = 14, + THUMBOP_MVN = 15, + THUMBOP_MOV = 16, + THUMBOP_CMPI = 17, + THUMBOP_ADD = 18, + THUMBOP_SUB = 19, + THUMBOP_CMPH = 19, + THUMBOP_MOVH = 20 +} ThumbOpcode; + + +/* Generic form - all ARM instructions are conditional. */ +typedef struct { + arminstr_t icode : 28; + arminstr_t cond : 4; +} ARMInstrGeneric; + + + +/* Branch or Branch with Link instructions. */ +typedef struct { + arminstr_t offset : 24; + arminstr_t link : 1; + arminstr_t tag : 3; /* 1 0 1 */ + arminstr_t cond : 4; +} ARMInstrBR; + +#define ARM_BR_ID 5 +#define ARM_BR_MASK 7 << 25 +#define ARM_BR_TAG ARM_BR_ID << 25 + +#define ARM_DEF_BR(offs, l, cond) ((offs) | ((l) << 24) | (ARM_BR_TAG) | (cond << ARMCOND_SHIFT)) + +/* branch */ +#define ARM_B_COND(p, cond, offset) ARM_EMIT(p, ARM_DEF_BR(offset, 0, cond)) +#define ARM_B(p, offs) ARM_B_COND((p), ARMCOND_AL, (offs)) +/* branch with link */ +#define ARM_BL_COND(p, cond, offset) ARM_EMIT(p, ARM_DEF_BR(offset, 1, cond)) +#define ARM_BL(p, offs) ARM_BL_COND((p), ARMCOND_AL, (offs)) + +#define ARM_DEF_BX(reg,sub,cond) (0x12fff << 8 | (reg) | ((sub) << 4) | ((cond) << ARMCOND_SHIFT)) + +#define ARM_BX_COND(p, cond, reg) ARM_EMIT(p, ARM_DEF_BX(reg, 1, cond)) +#define ARM_BX(p, reg) ARM_BX_COND((p), ARMCOND_AL, (reg)) + +#define ARM_BLX_REG_COND(p, cond, reg) ARM_EMIT(p, ARM_DEF_BX(reg, 3, cond)) +#define ARM_BLX_REG(p, reg) ARM_BLX_REG_COND((p), ARMCOND_AL, (reg)) + +/* Data Processing Instructions - there are 3 types. */ + +typedef struct { + arminstr_t imm : 8; + arminstr_t rot : 4; +} ARMDPI_op2_imm; + +typedef struct { + arminstr_t rm : 4; + arminstr_t tag : 1; /* 0 - immediate shift, 1 - reg shift */ + arminstr_t type : 2; /* shift type - logical, arithmetic, rotate */ +} ARMDPI_op2_reg_shift; + + +/* op2 is reg shift by imm */ +typedef union { + ARMDPI_op2_reg_shift r2; + struct { + arminstr_t _dummy_r2 : 7; + arminstr_t shift : 5; + } imm; +} ARMDPI_op2_reg_imm; + +/* op2 is reg shift by reg */ +typedef union { + ARMDPI_op2_reg_shift r2; + struct { + arminstr_t _dummy_r2 : 7; + arminstr_t pad : 1; /* always 0, to differentiate from HXFER etc. */ + arminstr_t rs : 4; + } reg; +} ARMDPI_op2_reg_reg; + +/* Data processing instrs */ +typedef union { + ARMDPI_op2_imm op2_imm; + + ARMDPI_op2_reg_shift op2_reg; + ARMDPI_op2_reg_imm op2_reg_imm; + ARMDPI_op2_reg_reg op2_reg_reg; + + struct { + arminstr_t op2 : 12; /* raw operand 2 */ + arminstr_t rd : 4; /* destination reg */ + arminstr_t rn : 4; /* first operand reg */ + arminstr_t s : 1; /* S-bit controls PSR update */ + arminstr_t opcode : 4; /* arithmetic/logic operation */ + arminstr_t type : 1; /* type of op2, 0 = register, 1 = immediate */ + arminstr_t tag : 2; /* 0 0 */ + arminstr_t cond : 4; + } all; +} ARMInstrDPI; + +#define ARM_DPI_ID 0 +#define ARM_DPI_MASK 3 << 26 +#define ARM_DPI_TAG ARM_DPI_ID << 26 + +#define ARM_DEF_DPI_IMM_COND(imm8, rot, rd, rn, s, op, cond) \ + ((imm8) & 0xFF) | \ + (((rot) & 0xF) << 8) | \ + ((rd) << 12) | \ + ((rn) << 16) | \ + ((s) << 20) | \ + ((op) << 21) | \ + (1 << 25) | \ + (ARM_DPI_TAG) | \ + ARM_DEF_COND(cond) + + +#define ARM_DEF_DPI_IMM(imm8, rot, rd, rn, s, op) \ + ARM_DEF_DPI_IMM_COND(imm8, rot, rd, rn, s, op, ARMCOND_AL) + +/* codegen */ +#define ARM_DPIOP_REG_IMM8ROT_COND(p, op, rd, rn, imm8, rot, cond) \ + ARM_EMIT(p, ARM_DEF_DPI_IMM_COND((imm8), ((rot) >> 1), (rd), (rn), 0, (op), cond)) +#define ARM_DPIOP_S_REG_IMM8ROT_COND(p, op, rd, rn, imm8, rot, cond) \ + ARM_EMIT(p, ARM_DEF_DPI_IMM_COND((imm8), ((rot) >> 1), (rd), (rn), 1, (op), cond)) + +/* inline */ +#define ARM_IASM_DPIOP_REG_IMM8ROT_COND(p, op, rd, rn, imm8, rot, cond) \ + ARM_IASM(ARM_DEF_DPI_IMM_COND((imm8), ((rot) >> 1), (rd), (rn), 0, (op), cond)) +#define ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(p, op, rd, rn, imm8, rot, cond) \ + ARM_IASM(ARM_DEF_DPI_IMM_COND((imm8), ((rot) >> 1), (rd), (rn), 1, (op), cond)) + + + +#define ARM_DEF_DPI_REG_IMMSHIFT_COND(rm, shift_type, imm_shift, rd, rn, s, op, cond) \ + (rm) | \ + ((shift_type & 3) << 5) | \ + (((imm_shift) & 0x1F) << 7) | \ + ((rd) << 12) | \ + ((rn) << 16) | \ + ((s) << 20) | \ + ((op) << 21) | \ + (ARM_DPI_TAG) | \ + ARM_DEF_COND(cond) + +/* codegen */ +#define ARM_DPIOP_REG_IMMSHIFT_COND(p, op, rd, rn, rm, shift_t, imm_shift, cond) \ + ARM_EMIT(p, ARM_DEF_DPI_REG_IMMSHIFT_COND((rm), shift_t, imm_shift, (rd), (rn), 0, (op), cond)) + +#define ARM_DPIOP_S_REG_IMMSHIFT_COND(p, op, rd, rn, rm, shift_t, imm_shift, cond) \ + ARM_EMIT(p, ARM_DEF_DPI_REG_IMMSHIFT_COND((rm), shift_t, imm_shift, (rd), (rn), 1, (op), cond)) + +#define ARM_DPIOP_REG_REG_COND(p, op, rd, rn, rm, cond) \ + ARM_EMIT(p, ARM_DEF_DPI_REG_IMMSHIFT_COND((rm), ARMSHIFT_LSL, 0, (rd), (rn), 0, (op), cond)) + +#define ARM_DPIOP_S_REG_REG_COND(p, op, rd, rn, rm, cond) \ + ARM_EMIT(p, ARM_DEF_DPI_REG_IMMSHIFT_COND((rm), ARMSHIFT_LSL, 0, (rd), (rn), 1, (op), cond)) + +/* inline */ +#define ARM_IASM_DPIOP_REG_IMMSHIFT_COND(p, op, rd, rn, rm, shift_t, imm_shift, cond) \ + ARM_IASM(ARM_DEF_DPI_REG_IMMSHIFT_COND((rm), shift_t, imm_shift, (rd), (rn), 0, (op), cond)) + +#define ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(p, op, rd, rn, rm, shift_t, imm_shift, cond) \ + ARM_IASM(ARM_DEF_DPI_REG_IMMSHIFT_COND((rm), shift_t, imm_shift, (rd), (rn), 1, (op), cond)) + +#define ARM_IASM_DPIOP_REG_REG_COND(p, op, rd, rn, rm, cond) \ + ARM_IASM(ARM_DEF_DPI_REG_IMMSHIFT_COND((rm), ARMSHIFT_LSL, 0, (rd), (rn), 0, (op), cond)) + +#define ARM_IASM_DPIOP_S_REG_REG_COND(p, op, rd, rn, rm, cond) \ + ARM_IASM_EMIT(ARM_DEF_DPI_REG_IMMSHIFT_COND((rm), ARMSHIFT_LSL, 0, (rd), (rn), 1, (op), cond)) + + +/* Rd := Rn op (Rm shift_type Rs) */ +#define ARM_DEF_DPI_REG_REGSHIFT_COND(rm, shift_type, rs, rd, rn, s, op, cond) \ + (rm) | \ + (1 << 4) | \ + ((shift_type & 3) << 5) | \ + ((rs) << 8) | \ + ((rd) << 12) | \ + ((rn) << 16) | \ + ((s) << 20) | \ + ((op) << 21) | \ + (ARM_DPI_TAG) | \ + ARM_DEF_COND(cond) + +/* codegen */ +#define ARM_DPIOP_REG_REGSHIFT_COND(p, op, rd, rn, rm, shift_t, rs, cond) \ + ARM_EMIT(p, ARM_DEF_DPI_REG_REGSHIFT_COND((rm), shift_t, (rs), (rd), (rn), 0, (op), cond)) + +#define ARM_DPIOP_S_REG_REGSHIFT_COND(p, op, rd, rn, rm, shift_t, rs, cond) \ + ARM_EMIT(p, ARM_DEF_DPI_REG_REGSHIFT_COND((rm), shift_t, (rs), (rd), (rn), 1, (op), cond)) + +/* inline */ +#define ARM_IASM_DPIOP_REG_REGSHIFT_COND(p, op, rd, rn, rm, shift_t, rs, cond) \ + ARM_IASM(ARM_DEF_DPI_REG_REGSHIFT_COND((rm), shift_t, (rs), (rd), (rn), 0, (op), cond)) + +#define ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(p, op, rd, rn, rm, shift_t, rs, cond) \ + ARM_IASM(ARM_DEF_DPI_REG_REGSHIFT_COND((rm), shift_t, (rs), (rd), (rn), 1, (op), cond)) + + + +/* Multiple register transfer. */ +typedef struct { + arminstr_t reg_list : 16; /* bitfield */ + arminstr_t rn : 4; /* base reg */ + arminstr_t ls : 1; /* load(1)/store(0) */ + arminstr_t wb : 1; /* write-back "!" */ + arminstr_t s : 1; /* restore PSR, force user bit */ + arminstr_t u : 1; /* up/down */ + arminstr_t p : 1; /* pre(1)/post(0) index */ + arminstr_t tag : 3; /* 1 0 0 */ + arminstr_t cond : 4; +} ARMInstrMRT; + +#define ARM_MRT_ID 4 +#define ARM_MRT_MASK 7 << 25 +#define ARM_MRT_TAG ARM_MRT_ID << 25 + +#define ARM_DEF_MRT(regs, rn, l, w, s, u, p, cond) \ + (regs) | \ + (rn << 16) | \ + (l << 20) | \ + (w << 21) | \ + (s << 22) | \ + (u << 23) | \ + (p << 24) | \ + (ARM_MRT_TAG) | \ + ARM_DEF_COND(cond) + + +#define ARM_LDM(p, base, regs) ARM_EMIT(p, ARM_DEF_MRT(regs, base, 1, 0, 0, 1, 0, ARMCOND_AL)) +#define ARM_STM(p, base, regs) ARM_EMIT(p, ARM_DEF_MRT(regs, base, 0, 0, 0, 1, 0, ARMCOND_AL)) + +/* stmdb sp!, {regs} */ +#define ARM_PUSH(p, regs) ARM_EMIT(p, ARM_DEF_MRT(regs, ARMREG_SP, 0, 1, 0, 0, 1, ARMCOND_AL)) +#define ARM_IASM_PUSH(regs) ARM_IASM(ARM_DEF_MRT(regs, ARMREG_SP, 0, 1, 0, 0, 1, ARMCOND_AL)) + +/* ldmia sp!, {regs} */ +#define ARM_POP(p, regs) ARM_EMIT(p, ARM_DEF_MRT(regs, ARMREG_SP, 1, 1, 0, 1, 0, ARMCOND_AL)) +#define ARM_IASM_POP(regs) ARM_IASM_EMIT(ARM_DEF_MRT(regs, ARMREG_SP, 1, 1, 0, 1, 0, ARMCOND_AL)) + +/* ldmia sp, {regs} ; (no write-back) */ +#define ARM_POP_NWB(p, regs) ARM_EMIT(p, ARM_DEF_MRT(regs, ARMREG_SP, 1, 0, 0, 1, 0, ARMCOND_AL)) +#define ARM_IASM_POP_NWB(regs) ARM_IASM_EMIT(ARM_DEF_MRT(regs, ARMREG_SP, 1, 0, 0, 1, 0, ARMCOND_AL)) + +#define ARM_PUSH1(p, r1) ARM_PUSH(p, (1 << r1)) +#define ARM_PUSH2(p, r1, r2) ARM_PUSH(p, (1 << r1) | (1 << r2)) +#define ARM_PUSH3(p, r1, r2, r3) ARM_PUSH(p, (1 << r1) | (1 << r2) | (1 << r3)) +#define ARM_PUSH4(p, r1, r2, r3, r4) ARM_PUSH(p, (1 << r1) | (1 << r2) | (1 << r3) | (1 << r4)) +#define ARM_PUSH5(p, r1, r2, r3, r4, r5) ARM_PUSH(p, (1 << r1) | (1 << r2) | (1 << r3) | (1 << r4) | (1 << r5)) +#define ARM_PUSH6(p, r1, r2, r3, r4, r5, r6) ARM_PUSH(p, (1 << r1) | (1 << r2) | (1 << r3) | (1 << r4) | (1 << r5) | (1 << r6)) +#define ARM_PUSH7(p, r1, r2, r3, r4, r5, r6, r7) ARM_PUSH(p, (1 << r1) | (1 << r2) | (1 << r3) | (1 << r4) | (1 << r5) | (1 << r6) | (1 << r7)) +#define ARM_PUSH8(p, r1, r2, r3, r4, r5, r6, r7, r8) ARM_PUSH(p, (1 << r1) | (1 << r2) | (1 << r3) | (1 << r4) | (1 << r5) | (1 << r6) | (1 << r7) | (1 << r8)) + +#define ARM_POP8(p, r1, r2, r3, r4, r5, r6, r7, r8) ARM_POP(p, (1 << r1) | (1 << r2) | (1 << r3) | (1 << r4) | (1 << r5) | (1 << r6) | (1 << r7) | (1 << r8)) +#define ARM_POP7(p, r1, r2, r3, r4, r5, r6, r7) ARM_POP(p, (1 << r1) | (1 << r2) | (1 << r3) | (1 << r4) | (1 << r5) | (1 << r6) | (1 << r7)) +#define ARM_POP6(p, r1, r2, r3, r4, r5, r6) ARM_POP(p, (1 << r1) | (1 << r2) | (1 << r3) | (1 << r4) | (1 << r5) | (1 << r6)) +#define ARM_POP5(p, r1, r2, r3, r4, r5) ARM_POP(p, (1 << r1) | (1 << r2) | (1 << r3) | (1 << r4) | (1 << r5)) +#define ARM_POP4(p, r1, r2, r3, r4) ARM_POP(p, (1 << r1) | (1 << r2) | (1 << r3) | (1 << r4)) +#define ARM_POP3(p, r1, r2, r3) ARM_POP(p, (1 << r1) | (1 << r2) | (1 << r3)) +#define ARM_POP2(p, r1, r2) ARM_POP(p, (1 << r1) | (1 << r2)) +#define ARM_POP1(p, r1) ARM_POP(p, (1 << r1)) + + +/* Multiply instructions */ +typedef struct { + arminstr_t rm : 4; + arminstr_t tag2 : 4; /* 9 */ + arminstr_t rs : 4; + arminstr_t rn : 4; + arminstr_t rd : 4; + arminstr_t s : 1; + arminstr_t opcode : 3; + arminstr_t tag : 4; + arminstr_t cond : 4; +} ARMInstrMul; + +#define ARM_MUL_ID 9 +#define ARM_DEF_MUL_COND(op, rd, rm, rs, rn, s, cond) \ + (rm) | \ + ARM_MUL_ID << 4 | \ + ((rs) << 8) | \ + ((rn) << 12) | \ + ((rd) << 16) | \ + ((s & 1) << 20) | \ + ((op & 7) << 21) | \ + ARM_DEF_COND(cond) + +/* Rd := (Rm * Rs)[31:0]; 32 x 32 -> 32 */ +#define ARM_MUL_COND(p, rd, rm, rs, cond) \ + ARM_EMIT(p, ARM_DEF_MUL_COND(ARMOP_MUL, rd, rm, rs, 0, 0, cond)) +#define ARM_UMULL_COND(p, rdhi, rdlo, rm, rs, cond) \ + ARM_EMIT(p, ARM_DEF_MUL_COND(ARMOP_UMULL, rdhi, rm, rs, rdlo, 0, cond)) +#define ARM_SMULL_COND(p, rdhi, rdlo, rm, rs, cond) \ + ARM_EMIT(p, ARM_DEF_MUL_COND(ARMOP_SMULL, rdhi, rm, rs, rdlo, 0, cond)) +#define ARM_MUL(p, rd, rm, rs) \ + ARM_MUL_COND(p, rd, rm, rs, ARMCOND_AL) +#define ARM_UMULL(p, rdhi, rdlo, rm, rs) \ + ARM_UMULL_COND(p, rdhi, rdlo, rm, rs, ARMCOND_AL) +#define ARM_SMULL(p, rdhi, rdlo, rm, rs) \ + ARM_SMULL_COND(p, rdhi, rdlo, rm, rs, ARMCOND_AL) +#define ARM_MULS_COND(p, rd, rm, rs, cond) \ + ARM_EMIT(p, ARM_DEF_MUL_COND(ARMOP_MUL, rd, rm, rs, 0, 1, cond)) +#define ARM_MULS(p, rd, rm, rs) \ + ARM_MULS_COND(p, rd, rm, rs, ARMCOND_AL) +#define ARM_MUL_REG_REG(p, rd, rm, rs) ARM_MUL(p, rd, rm, rs) +#define ARM_MULS_REG_REG(p, rd, rm, rs) ARM_MULS(p, rd, rm, rs) +#define ARM_UMULL_REG_REG(p, rdhi, rdlo, rm, rs) ARM_UMULL(p, rdhi, rdlo, rm, rs) +#define ARM_SMULL_REG_REG(p, rdhi, rdlo, rm, rs) ARM_SMULL(p, rdhi, rdlo, rm, rs) + +/* inline */ +#define ARM_IASM_MUL_COND(rd, rm, rs, cond) \ + ARM_IASM_EMIT(ARM_DEF_MUL_COND(ARMOP_MUL, rd, rm, rs, 0, 0, cond)) +#define ARM_IASM_MUL(rd, rm, rs) \ + ARM_IASM_MUL_COND(rd, rm, rs, ARMCOND_AL) +#define ARM_IASM_MULS_COND(rd, rm, rs, cond) \ + ARM_IASM_EMIT(ARM_DEF_MUL_COND(ARMOP_MUL, rd, rm, rs, 0, 1, cond)) +#define ARM_IASM_MULS(rd, rm, rs) \ + ARM_IASM_MULS_COND(rd, rm, rs, ARMCOND_AL) + + +/* Rd := (Rm * Rs) + Rn; 32x32+32->32 */ +#define ARM_MLA_COND(p, rd, rm, rs, rn, cond) \ + ARM_EMIT(p, ARM_DEF_MUL_COND(ARMOP_MLA, rd, rm, rs, rn, 0, cond)) +#define ARM_MLA(p, rd, rm, rs, rn) \ + ARM_MLA_COND(p, rd, rm, rs, rn, ARMCOND_AL) +#define ARM_MLAS_COND(p, rd, rm, rs, rn, cond) \ + ARM_EMIT(p, ARM_DEF_MUL_COND(ARMOP_MLA, rd, rm, rs, rn, 1, cond)) +#define ARM_MLAS(p, rd, rm, rs, rn) \ + ARM_MLAS_COND(p, rd, rm, rs, rn, ARMCOND_AL) + +/* inline */ +#define ARM_IASM_MLA_COND(rd, rm, rs, rn, cond) \ + ARM_IASM_EMIT(ARM_DEF_MUL_COND(ARMOP_MLA, rd, rm, rs, rn, 0, cond)) +#define ARM_IASM_MLA(rd, rm, rs, rn) \ + ARM_IASM_MLA_COND(rd, rm, rs, rn, ARMCOND_AL) +#define ARM_IASM_MLAS_COND(rd, rm, rs, rn, cond) \ + ARM_IASM_EMIT(ARM_DEF_MUL_COND(ARMOP_MLA, rd, rm, rs, rn, 1, cond)) +#define ARM_IASM_MLAS(rd, rm, rs, rn) \ + ARM_IASM_MLAS_COND(rd, rm, rs, rn, ARMCOND_AL) + + + +/* Word/byte transfer */ +typedef union { + ARMDPI_op2_reg_imm op2_reg_imm; + struct { + arminstr_t op2_imm : 12; + arminstr_t rd : 4; + arminstr_t rn : 4; + arminstr_t ls : 1; + arminstr_t wb : 1; + arminstr_t b : 1; + arminstr_t u : 1; /* down(0) / up(1) */ + arminstr_t p : 1; /* post-index(0) / pre-index(1) */ + arminstr_t type : 1; /* imm(0) / register(1) */ + arminstr_t tag : 2; /* 0 1 */ + arminstr_t cond : 4; + } all; +} ARMInstrWXfer; + +#define ARM_WXFER_ID 1 +#define ARM_WXFER_MASK 3 << 26 +#define ARM_WXFER_TAG ARM_WXFER_ID << 26 + + +#define ARM_DEF_WXFER_IMM(imm12, rd, rn, ls, wb, b, p, cond) \ + ((((int)imm12) < 0) ? -(int)(imm12) : (imm12)) | \ + ((rd) << 12) | \ + ((rn) << 16) | \ + ((ls) << 20) | \ + ((wb) << 21) | \ + ((b) << 22) | \ + (((int)(imm12) >= 0) << 23) | \ + ((p) << 24) | \ + ARM_WXFER_TAG | \ + ARM_DEF_COND(cond) + +#define ARM_WXFER_MAX_OFFS 0xFFF + +/* this macro checks for imm12 bounds */ +#define ARM_EMIT_WXFER_IMM(ptr, imm12, rd, rn, ls, wb, b, p, cond) \ + do { \ + int _imm12 = (int)(imm12) < -ARM_WXFER_MAX_OFFS \ + ? -ARM_WXFER_MAX_OFFS \ + : (int)(imm12) > ARM_WXFER_MAX_OFFS \ + ? ARM_WXFER_MAX_OFFS \ + : (int)(imm12); \ + ARM_EMIT((ptr), \ + ARM_DEF_WXFER_IMM(_imm12, (rd), (rn), (ls), (wb), (b), (p), (cond))); \ + } while (0) + + +/* LDRx */ +/* immediate offset, post-index */ +#define ARM_LDR_IMM_POST_COND(p, rd, rn, imm, cond) \ + ARM_EMIT(p, ARM_DEF_WXFER_IMM(imm, rd, rn, ARMOP_LDR, 0, 0, 0, cond)) + +#define ARM_LDR_IMM_POST(p, rd, rn, imm) ARM_LDR_IMM_POST_COND(p, rd, rn, imm, ARMCOND_AL) + +#define ARM_LDRB_IMM_POST_COND(p, rd, rn, imm, cond) \ + ARM_EMIT(p, ARM_DEF_WXFER_IMM(imm, rd, rn, ARMOP_LDR, 0, 1, 0, cond)) + +#define ARM_LDRB_IMM_POST(p, rd, rn, imm) ARM_LDRB_IMM_POST_COND(p, rd, rn, imm, ARMCOND_AL) + +/* immediate offset, pre-index */ +#define ARM_LDR_IMM_COND(p, rd, rn, imm, cond) \ + ARM_EMIT(p, ARM_DEF_WXFER_IMM(imm, rd, rn, ARMOP_LDR, 0, 0, 1, cond)) + +#define ARM_LDR_IMM(p, rd, rn, imm) ARM_LDR_IMM_COND(p, rd, rn, imm, ARMCOND_AL) + +#define ARM_LDRB_IMM_COND(p, rd, rn, imm, cond) \ + ARM_EMIT(p, ARM_DEF_WXFER_IMM(imm, rd, rn, ARMOP_LDR, 0, 1, 1, cond)) + +#define ARM_LDRB_IMM(p, rd, rn, imm) ARM_LDRB_IMM_COND(p, rd, rn, imm, ARMCOND_AL) + +/* STRx */ +/* immediate offset, post-index */ +#define ARM_STR_IMM_POST_COND(p, rd, rn, imm, cond) \ + ARM_EMIT(p, ARM_DEF_WXFER_IMM(imm, rd, rn, ARMOP_STR, 0, 0, 0, cond)) + +#define ARM_STR_IMM_POST(p, rd, rn, imm) ARM_STR_IMM_POST_COND(p, rd, rn, imm, ARMCOND_AL) + +#define ARM_STRB_IMM_POST_COND(p, rd, rn, imm, cond) \ + ARM_EMIT(p, ARM_DEF_WXFER_IMM(imm, rd, rn, ARMOP_STR, 0, 1, 0, cond)) + +#define ARM_STRB_IMM_POST(p, rd, rn, imm) ARM_STRB_IMM_POST_COND(p, rd, rn, imm, ARMCOND_AL) + +/* immediate offset, pre-index */ +#define ARM_STR_IMM_COND(p, rd, rn, imm, cond) \ + ARM_EMIT_WXFER_IMM(p, imm, rd, rn, ARMOP_STR, 0, 0, 1, cond) +/* ARM_EMIT(p, ARM_DEF_WXFER_IMM(imm, rd, rn, ARMOP_STR, 0, 0, 1, cond)) */ + +#define ARM_STR_IMM(p, rd, rn, imm) ARM_STR_IMM_COND(p, rd, rn, imm, ARMCOND_AL) + +#define ARM_STRB_IMM_COND(p, rd, rn, imm, cond) \ + ARM_EMIT(p, ARM_DEF_WXFER_IMM(imm, rd, rn, ARMOP_STR, 0, 1, 1, cond)) + +#define ARM_STRB_IMM(p, rd, rn, imm) ARM_STRB_IMM_COND(p, rd, rn, imm, ARMCOND_AL) + +/* write-back */ +#define ARM_STR_IMM_WB_COND(p, rd, rn, imm, cond) \ + ARM_EMIT_WXFER_IMM(p, imm, rd, rn, ARMOP_STR, 1, 0, 1, cond) +#define ARM_STR_IMM_WB(p, rd, rn, imm) ARM_STR_IMM_WB_COND(p, rd, rn, imm, ARMCOND_AL) + + +#define ARM_DEF_WXFER_REG_REG_UPDOWN_COND(rm, shift_type, shift, rd, rn, ls, wb, b, u, p, cond) \ + (rm) | \ + ((shift_type) << 5) | \ + ((shift) << 7) | \ + ((rd) << 12) | \ + ((rn) << 16) | \ + ((ls) << 20) | \ + ((wb) << 21) | \ + ((b) << 22) | \ + ((u) << 23) | \ + ((p) << 24) | \ + (1 << 25) | \ + ARM_WXFER_TAG | \ + ARM_DEF_COND(cond) + +#define ARM_DEF_WXFER_REG_REG_COND(rm, shift_type, shift, rd, rn, ls, wb, b, p, cond) \ + ARM_DEF_WXFER_REG_REG_UPDOWN_COND(rm, shift_type, shift, rd, rn, ls, wb, b, ARM_UP, p, cond) +#define ARM_DEF_WXFER_REG_MINUS_REG_COND(rm, shift_type, shift, rd, rn, ls, wb, b, p, cond) \ + ARM_DEF_WXFER_REG_REG_UPDOWN_COND(rm, shift_type, shift, rd, rn, ls, wb, b, ARM_DOWN, p, cond) + + +#define ARM_LDR_REG_REG_SHIFT_COND(p, rd, rn, rm, shift_type, shift, cond) \ + ARM_EMIT(p, ARM_DEF_WXFER_REG_REG_COND(rm, shift_type, shift, rd, rn, ARMOP_LDR, 0, 0, 1, cond)) +#define ARM_LDR_REG_REG_SHIFT(p, rd, rn, rm, shift_type, shift) \ + ARM_LDR_REG_REG_SHIFT_COND(p, rd, rn, rm, shift_type, shift, ARMCOND_AL) +#define ARM_LDR_REG_REG(p, rd, rn, rm) \ + ARM_LDR_REG_REG_SHIFT(p, rd, rn, rm, ARMSHIFT_LSL, 0) + +#define ARM_LDRB_REG_REG_SHIFT_COND(p, rd, rn, rm, shift_type, shift, cond) \ + ARM_EMIT(p, ARM_DEF_WXFER_REG_REG_COND(rm, shift_type, shift, rd, rn, ARMOP_LDR, 0, 1, 1, cond)) +#define ARM_LDRB_REG_REG_SHIFT(p, rd, rn, rm, shift_type, shift) \ + ARM_LDRB_REG_REG_SHIFT_COND(p, rd, rn, rm, shift_type, shift, ARMCOND_AL) +#define ARM_LDRB_REG_REG(p, rd, rn, rm) \ + ARM_LDRB_REG_REG_SHIFT(p, rd, rn, rm, ARMSHIFT_LSL, 0) + +#define ARM_STR_REG_REG_SHIFT_COND(p, rd, rn, rm, shift_type, shift, cond) \ + ARM_EMIT(p, ARM_DEF_WXFER_REG_REG_COND(rm, shift_type, shift, rd, rn, ARMOP_STR, 0, 0, 1, cond)) +#define ARM_STR_REG_REG_SHIFT(p, rd, rn, rm, shift_type, shift) \ + ARM_STR_REG_REG_SHIFT_COND(p, rd, rn, rm, shift_type, shift, ARMCOND_AL) +#define ARM_STR_REG_REG(p, rd, rn, rm) \ + ARM_STR_REG_REG_SHIFT(p, rd, rn, rm, ARMSHIFT_LSL, 0) + +/* zero-extend */ +#define ARM_STRB_REG_REG_SHIFT_COND(p, rd, rn, rm, shift_type, shift, cond) \ + ARM_EMIT(p, ARM_DEF_WXFER_REG_REG_COND(rm, shift_type, shift, rd, rn, ARMOP_STR, 0, 1, 1, cond)) +#define ARM_STRB_REG_REG_SHIFT(p, rd, rn, rm, shift_type, shift) \ + ARM_STRB_REG_REG_SHIFT_COND(p, rd, rn, rm, shift_type, shift, ARMCOND_AL) +#define ARM_STRB_REG_REG(p, rd, rn, rm) \ + ARM_STRB_REG_REG_SHIFT(p, rd, rn, rm, ARMSHIFT_LSL, 0) + + +/* ARMv4+ */ +/* Half-word or byte (signed) transfer. */ +typedef struct { + arminstr_t rm : 4; /* imm_lo */ + arminstr_t tag3 : 1; /* 1 */ + arminstr_t h : 1; /* half-word or byte */ + arminstr_t s : 1; /* sign-extend or zero-extend */ + arminstr_t tag2 : 1; /* 1 */ + arminstr_t imm_hi : 4; + arminstr_t rd : 4; + arminstr_t rn : 4; + arminstr_t ls : 1; + arminstr_t wb : 1; + arminstr_t type : 1; /* imm(1) / reg(0) */ + arminstr_t u : 1; /* +- */ + arminstr_t p : 1; /* pre/post-index */ + arminstr_t tag : 3; + arminstr_t cond : 4; +} ARMInstrHXfer; + +#define ARM_HXFER_ID 0 +#define ARM_HXFER_ID2 1 +#define ARM_HXFER_ID3 1 +#define ARM_HXFER_MASK ((0x7 << 25) | (0x9 << 4)) +#define ARM_HXFER_TAG ((ARM_HXFER_ID << 25) | (ARM_HXFER_ID2 << 7) | (ARM_HXFER_ID3 << 4)) + +#define ARM_DEF_HXFER_IMM_COND(imm, h, s, rd, rn, ls, wb, p, cond) \ + ((imm) < 0?(-(imm)) & 0xF:(imm) & 0xF) | \ + ((h) << 5) | \ + ((s) << 6) | \ + ((imm) < 0?((-(imm)) << 4) & 0xF00:((imm) << 4) & 0xF00) | \ + ((rd) << 12) | \ + ((rn) << 16) | \ + ((ls) << 20) | \ + ((wb) << 21) | \ + (1 << 22) | \ + (((int)(imm) >= 0) << 23) | \ + ((p) << 24) | \ + ARM_HXFER_TAG | \ + ARM_DEF_COND(cond) + +#define ARM_LDRH_IMM_COND(p, rd, rn, imm, cond) \ + ARM_EMIT(p, ARM_DEF_HXFER_IMM_COND(imm, 1, 0, rd, rn, ARMOP_LDR, 0, 1, cond)) +#define ARM_LDRH_IMM(p, rd, rn, imm) \ + ARM_LDRH_IMM_COND(p, rd, rn, imm, ARMCOND_AL) +#define ARM_LDRSH_IMM_COND(p, rd, rn, imm, cond) \ + ARM_EMIT(p, ARM_DEF_HXFER_IMM_COND(imm, 1, 1, rd, rn, ARMOP_LDR, 0, 1, cond)) +#define ARM_LDRSH_IMM(p, rd, rn, imm) \ + ARM_LDRSH_IMM_COND(p, rd, rn, imm, ARMCOND_AL) +#define ARM_LDRSB_IMM_COND(p, rd, rn, imm, cond) \ + ARM_EMIT(p, ARM_DEF_HXFER_IMM_COND(imm, 0, 1, rd, rn, ARMOP_LDR, 0, 1, cond)) +#define ARM_LDRSB_IMM(p, rd, rn, imm) \ + ARM_LDRSB_IMM_COND(p, rd, rn, imm, ARMCOND_AL) + + +#define ARM_STRH_IMM_COND(p, rd, rn, imm, cond) \ + ARM_EMIT(p, ARM_DEF_HXFER_IMM_COND(imm, 1, 0, rd, rn, ARMOP_STR, 0, 1, cond)) +#define ARM_STRH_IMM(p, rd, rn, imm) \ + ARM_STRH_IMM_COND(p, rd, rn, imm, ARMCOND_AL) + + +#define ARM_DEF_HXFER_REG_REG_UPDOWN_COND(rm, h, s, rd, rn, ls, wb, u, p, cond) \ + ((rm) & 0xF) | \ + ((h) << 5) | \ + ((s) << 6) | \ + ((rd) << 12) | \ + ((rn) << 16) | \ + ((ls) << 20) | \ + ((wb) << 21) | \ + (0 << 22) | \ + ((u) << 23) | \ + ((p) << 24) | \ + ARM_HXFER_TAG | \ + ARM_DEF_COND(cond) + +#define ARM_DEF_HXFER_REG_REG_COND(rm, h, s, rd, rn, ls, wb, p, cond) \ + ARM_DEF_HXFER_REG_REG_UPDOWN_COND(rm, h, s, rd, rn, ls, wb, ARM_UP, p, cond) +#define ARM_DEF_HXFER_REG_MINUS_REG_COND(rm, h, s, rd, rn, ls, wb, p, cond) \ + ARM_DEF_HXFER_REG_REG_UPDOWN_COND(rm, h, s, rd, rn, ls, wb, ARM_DOWN, p, cond) + +#define ARM_LDRH_REG_REG_COND(p, rd, rm, rn, cond) \ + ARM_EMIT(p, ARM_DEF_HXFER_REG_REG_COND(rm, 1, 0, rd, rn, ARMOP_LDR, 0, 1, cond)) +#define ARM_LDRH_REG_REG(p, rd, rm, rn) \ + ARM_LDRH_REG_REG_COND(p, rd, rm, rn, ARMCOND_AL) +#define ARM_LDRSH_REG_REG_COND(p, rd, rm, rn, cond) \ + ARM_EMIT(p, ARM_DEF_HXFER_REG_REG_COND(rm, 1, 1, rd, rn, ARMOP_LDR, 0, 1, cond)) +#define ARM_LDRSH_REG_REG(p, rd, rm, rn) \ + ARM_LDRSH_REG_REG_COND(p, rd, rm, rn, ARMCOND_AL) +#define ARM_LDRSB_REG_REG_COND(p, rd, rm, rn, cond) \ + ARM_EMIT(p, ARM_DEF_HXFER_REG_REG_COND(rm, 0, 1, rd, rn, ARMOP_LDR, 0, 1, cond)) +#define ARM_LDRSB_REG_REG(p, rd, rm, rn) ARM_LDRSB_REG_REG_COND(p, rd, rm, rn, ARMCOND_AL) + +#define ARM_STRH_REG_REG_COND(p, rd, rm, rn, cond) \ + ARM_EMIT(p, ARM_DEF_HXFER_REG_REG_COND(rm, 1, 0, rd, rn, ARMOP_STR, 0, 1, cond)) +#define ARM_STRH_REG_REG(p, rd, rm, rn) \ + ARM_STRH_REG_REG_COND(p, rd, rm, rn, ARMCOND_AL) + + + +/* Swap */ +typedef struct { + arminstr_t rm : 4; + arminstr_t tag3 : 8; /* 0x9 */ + arminstr_t rd : 4; + arminstr_t rn : 4; + arminstr_t tag2 : 2; + arminstr_t b : 1; + arminstr_t tag : 5; /* 0x2 */ + arminstr_t cond : 4; +} ARMInstrSwap; + +#define ARM_SWP_ID 2 +#define ARM_SWP_ID2 9 +#define ARM_SWP_MASK ((0x1F << 23) | (3 << 20) | (0xFF << 4)) +#define ARM_SWP_TAG ((ARM_SWP_ID << 23) | (ARM_SWP_ID2 << 4)) + + + +/* Software interrupt */ +typedef struct { + arminstr_t num : 24; + arminstr_t tag : 4; + arminstr_t cond : 4; +} ARMInstrSWI; + +#define ARM_SWI_ID 0xF +#define ARM_SWI_MASK (0xF << 24) +#define ARM_SWI_TAG (ARM_SWI_ID << 24) + + + +/* Co-processor Data Processing */ +typedef struct { + arminstr_t crm : 4; + arminstr_t tag2 : 1; /* 0 */ + arminstr_t op2 : 3; + arminstr_t cpn : 4; /* CP number */ + arminstr_t crd : 4; + arminstr_t crn : 4; + arminstr_t op : 4; + arminstr_t tag : 4; /* 0xE */ + arminstr_t cond : 4; +} ARMInstrCDP; + +#define ARM_CDP_ID 0xE +#define ARM_CDP_ID2 0 +#define ARM_CDP_MASK ((0xF << 24) | (1 << 4)) +#define ARM_CDP_TAG ((ARM_CDP_ID << 24) | (ARM_CDP_ID2 << 4)) + + +/* Co-processor Data Transfer (ldc/stc) */ +typedef struct { + arminstr_t offs : 8; + arminstr_t cpn : 4; + arminstr_t crd : 4; + arminstr_t rn : 4; + arminstr_t ls : 1; + arminstr_t wb : 1; + arminstr_t n : 1; + arminstr_t u : 1; + arminstr_t p : 1; + arminstr_t tag : 3; + arminstr_t cond : 4; +} ARMInstrCDT; + +#define ARM_CDT_ID 6 +#define ARM_CDT_MASK (7 << 25) +#define ARM_CDT_TAG (ARM_CDT_ID << 25) + + +/* Co-processor Register Transfer (mcr/mrc) */ +typedef struct { + arminstr_t crm : 4; + arminstr_t tag2 : 1; + arminstr_t op2 : 3; + arminstr_t cpn : 4; + arminstr_t rd : 4; + arminstr_t crn : 4; + arminstr_t ls : 1; + arminstr_t op1 : 3; + arminstr_t tag : 4; + arminstr_t cond : 4; +} ARMInstrCRT; + +#define ARM_CRT_ID 0xE +#define ARM_CRT_ID2 0x1 +#define ARM_CRT_MASK ((0xF << 24) | (1 << 4)) +#define ARM_CRT_TAG ((ARM_CRT_ID << 24) | (ARM_CRT_ID2 << 4)) + +/* Move register to PSR. */ +typedef union { + ARMDPI_op2_imm op2_imm; + struct { + arminstr_t rm : 4; + arminstr_t pad : 8; /* 0 */ + arminstr_t tag4 : 4; /* 0xF */ + arminstr_t fld : 4; + arminstr_t tag3 : 2; /* 0x2 */ + arminstr_t sel : 1; + arminstr_t tag2 : 2; /* 0x2 */ + arminstr_t type : 1; + arminstr_t tag : 2; /* 0 */ + arminstr_t cond : 4; + } all; +} ARMInstrMSR; + +#define ARM_MSR_ID 0 +#define ARM_MSR_ID2 2 +#define ARM_MSR_ID3 2 +#define ARM_MSR_ID4 0xF +#define ARM_MSR_MASK ((3 << 26) | \ + (3 << 23) | \ + (3 << 20) | \ + (0xF << 12)) +#define ARM_MSR_TAG ((ARM_MSR_ID << 26) | \ + (ARM_MSR_ID2 << 23) | \ + (ARM_MSR_ID3 << 20) | \ + (ARM_MSR_ID4 << 12)) + + +/* Move PSR to register. */ +typedef struct { + arminstr_t tag3 : 12; + arminstr_t rd : 4; + arminstr_t tag2 : 6; + arminstr_t sel : 1; /* CPSR | SPSR */ + arminstr_t tag : 5; + arminstr_t cond : 4; +} ARMInstrMRS; + +#define ARM_MRS_ID 2 +#define ARM_MRS_ID2 0xF +#define ARM_MRS_ID3 0 +#define ARM_MRS_MASK ((0x1F << 23) | (0x3F << 16) | 0xFFF) +#define ARM_MRS_TAG ((ARM_MRS_ID << 23) | (ARM_MRS_ID2 << 16) | ARM_MRS_ID3) + + + +#include "mono/arch/arm/arm_dpimacros.h" + +#define ARM_NOP(p) ARM_MOV_REG_REG(p, ARMREG_R0, ARMREG_R0) + + +#define ARM_SHL_IMM_COND(p, rd, rm, imm, cond) \ + ARM_MOV_REG_IMMSHIFT_COND(p, rd, rm, ARMSHIFT_LSL, imm, cond) +#define ARM_SHL_IMM(p, rd, rm, imm) \ + ARM_SHL_IMM_COND(p, rd, rm, imm, ARMCOND_AL) +#define ARM_SHLS_IMM_COND(p, rd, rm, imm, cond) \ + ARM_MOVS_REG_IMMSHIFT_COND(p, rd, rm, ARMSHIFT_LSL, imm, cond) +#define ARM_SHLS_IMM(p, rd, rm, imm) \ + ARM_SHLS_IMM_COND(p, rd, rm, imm, ARMCOND_AL) + +#define ARM_SHR_IMM_COND(p, rd, rm, imm, cond) \ + ARM_MOV_REG_IMMSHIFT_COND(p, rd, rm, ARMSHIFT_LSR, imm, cond) +#define ARM_SHR_IMM(p, rd, rm, imm) \ + ARM_SHR_IMM_COND(p, rd, rm, imm, ARMCOND_AL) +#define ARM_SHRS_IMM_COND(p, rd, rm, imm, cond) \ + ARM_MOVS_REG_IMMSHIFT_COND(p, rd, rm, ARMSHIFT_LSR, imm, cond) +#define ARM_SHRS_IMM(p, rd, rm, imm) \ + ARM_SHRS_IMM_COND(p, rd, rm, imm, ARMCOND_AL) + +#define ARM_SAR_IMM_COND(p, rd, rm, imm, cond) \ + ARM_MOV_REG_IMMSHIFT_COND(p, rd, rm, ARMSHIFT_ASR, imm, cond) +#define ARM_SAR_IMM(p, rd, rm, imm) \ + ARM_SAR_IMM_COND(p, rd, rm, imm, ARMCOND_AL) +#define ARM_SARS_IMM_COND(p, rd, rm, imm, cond) \ + ARM_MOVS_REG_IMMSHIFT_COND(p, rd, rm, ARMSHIFT_ASR, imm, cond) +#define ARM_SARS_IMM(p, rd, rm, imm) \ + ARM_SARS_IMM_COND(p, rd, rm, imm, ARMCOND_AL) + +#define ARM_ROR_IMM_COND(p, rd, rm, imm, cond) \ + ARM_MOV_REG_IMMSHIFT_COND(p, rd, rm, ARMSHIFT_ROR, imm, cond) +#define ARM_ROR_IMM(p, rd, rm, imm) \ + ARM_ROR_IMM_COND(p, rd, rm, imm, ARMCOND_AL) +#define ARM_RORS_IMM_COND(p, rd, rm, imm, cond) \ + ARM_MOVS_REG_IMMSHIFT_COND(p, rd, rm, ARMSHIFT_ROR, imm, cond) +#define ARM_RORS_IMM(p, rd, rm, imm) \ + ARM_RORS_IMM_COND(p, rd, rm, imm, ARMCOND_AL) + +#define ARM_SHL_REG_COND(p, rd, rm, rs, cond) \ + ARM_MOV_REG_REGSHIFT_COND(p, rd, rm, ARMSHIFT_LSL, rs, cond) +#define ARM_SHL_REG(p, rd, rm, rs) \ + ARM_SHL_REG_COND(p, rd, rm, rs, ARMCOND_AL) +#define ARM_SHLS_REG_COND(p, rd, rm, rs, cond) \ + ARM_MOVS_REG_REGSHIFT_COND(p, rd, rm, ARMSHIFT_LSL, rs, cond) +#define ARM_SHLS_REG(p, rd, rm, rs) \ + ARM_SHLS_REG_COND(p, rd, rm, rs, ARMCOND_AL) +#define ARM_SHLS_REG_REG(p, rd, rm, rs) ARM_SHLS_REG(p, rd, rm, rs) + +#define ARM_SHR_REG_COND(p, rd, rm, rs, cond) \ + ARM_MOV_REG_REGSHIFT_COND(p, rd, rm, ARMSHIFT_LSR, rs, cond) +#define ARM_SHR_REG(p, rd, rm, rs) \ + ARM_SHR_REG_COND(p, rd, rm, rs, ARMCOND_AL) +#define ARM_SHRS_REG_COND(p, rd, rm, rs, cond) \ + ARM_MOVS_REG_REGSHIFT_COND(p, rd, rm, ARMSHIFT_LSR, rs, cond) +#define ARM_SHRS_REG(p, rd, rm, rs) \ + ARM_SHRS_REG_COND(p, rd, rm, rs, ARMCOND_AL) +#define ARM_SHRS_REG_REG(p, rd, rm, rs) ARM_SHRS_REG(p, rd, rm, rs) + +#define ARM_SAR_REG_COND(p, rd, rm, rs, cond) \ + ARM_MOV_REG_REGSHIFT_COND(p, rd, rm, ARMSHIFT_ASR, rs, cond) +#define ARM_SAR_REG(p, rd, rm, rs) \ + ARM_SAR_REG_COND(p, rd, rm, rs, ARMCOND_AL) +#define ARM_SARS_REG_COND(p, rd, rm, rs, cond) \ + ARM_MOVS_REG_REGSHIFT_COND(p, rd, rm, ARMSHIFT_ASR, rs, cond) +#define ARM_SARS_REG(p, rd, rm, rs) \ + ARM_SARS_REG_COND(p, rd, rm, rs, ARMCOND_AL) +#define ARM_SARS_REG_REG(p, rd, rm, rs) ARM_SARS_REG(p, rd, rm, rs) + +#define ARM_ROR_REG_COND(p, rd, rm, rs, cond) \ + ARM_MOV_REG_REGSHIFT_COND(p, rd, rm, ARMSHIFT_ROR, rs, cond) +#define ARM_ROR_REG(p, rd, rm, rs) \ + ARM_ROR_REG_COND(p, rd, rm, rs, ARMCOND_AL) +#define ARM_RORS_REG_COND(p, rd, rm, rs, cond) \ + ARM_MOVS_REG_REGSHIFT_COND(p, rd, rm, ARMSHIFT_ROR, rs, cond) +#define ARM_RORS_REG(p, rd, rm, rs) \ + ARM_RORS_REG_COND(p, rd, rm, rs, ARMCOND_AL) +#define ARM_RORS_REG_REG(p, rd, rm, rs) ARM_RORS_REG(p, rd, rm, rs) + +#define ARM_DBRK(p) ARM_EMIT(p, 0xE6000010) +#define ARM_IASM_DBRK() ARM_IASM_EMIT(0xE6000010) + +#define ARM_INC(p, reg) ARM_ADD_REG_IMM8(p, reg, reg, 1) +#define ARM_DEC(p, reg) ARM_SUB_REG_IMM8(p, reg, reg, 1) + +#define ARM_MLS(p, rd, rn, rm, ra) ARM_EMIT((p), (ARMCOND_AL << 28) | (0x6 << 20) | ((rd) << 16) | ((ra) << 12) | ((rm) << 8) | (0x9 << 4) | ((rn) << 0)) + +/* ARM V5 */ + +/* Count leading zeros, CLZ{cond} Rd, Rm */ +typedef struct { + arminstr_t rm : 4; + arminstr_t tag2 : 8; + arminstr_t rd : 4; + arminstr_t tag : 12; + arminstr_t cond : 4; +} ARMInstrCLZ; + +#define ARM_CLZ_ID 0x16F +#define ARM_CLZ_ID2 0xF1 +#define ARM_CLZ_MASK ((0xFFF << 16) | (0xFF < 4)) +#define ARM_CLZ_TAG ((ARM_CLZ_ID << 16) | (ARM_CLZ_ID2 << 4)) + + + + +typedef union { + ARMInstrBR br; + ARMInstrDPI dpi; + ARMInstrMRT mrt; + ARMInstrMul mul; + ARMInstrWXfer wxfer; + ARMInstrHXfer hxfer; + ARMInstrSwap swp; + ARMInstrCDP cdp; + ARMInstrCDT cdt; + ARMInstrCRT crt; + ARMInstrSWI swi; + ARMInstrMSR msr; + ARMInstrMRS mrs; + ARMInstrCLZ clz; + + ARMInstrGeneric generic; + arminstr_t raw; +} ARMInstr; + +/* ARMv6t2 */ + +#define ARM_MOVW_REG_IMM_COND(p, rd, imm16, cond) ARM_EMIT(p, (((cond) << 28) | (3 << 24) | (0 << 20) | ((((guint32)(imm16)) >> 12) << 16) | ((rd) << 12) | (((guint32)(imm16)) & 0xfff))) +#define ARM_MOVW_REG_IMM(p, rd, imm16) ARM_MOVW_REG_IMM_COND ((p), (rd), (imm16), ARMCOND_AL) + +#define ARM_MOVT_REG_IMM_COND(p, rd, imm16, cond) ARM_EMIT(p, (((cond) << 28) | (3 << 24) | (4 << 20) | ((((guint32)(imm16)) >> 12) << 16) | ((rd) << 12) | (((guint32)(imm16)) & 0xfff))) +#define ARM_MOVT_REG_IMM(p, rd, imm16) ARM_MOVT_REG_IMM_COND ((p), (rd), (imm16), ARMCOND_AL) + +/* MCR */ +#define ARM_DEF_MCR_COND(coproc, opc1, rt, crn, crm, opc2, cond) \ + ARM_DEF_COND ((cond)) | ((0xe << 24) | (((opc1) & 0x7) << 21) | (0 << 20) | (((crn) & 0xf) << 16) | (((rt) & 0xf) << 12) | (((coproc) & 0xf) << 8) | (((opc2) & 0x7) << 5) | (1 << 4) | (((crm) & 0xf) << 0)) + +#define ARM_MCR_COND(p, coproc, opc1, rt, crn, crm, opc2, cond) \ + ARM_EMIT(p, ARM_DEF_MCR_COND ((coproc), (opc1), (rt), (crn), (crm), (opc2), (cond))) + +#define ARM_MCR(p, coproc, opc1, rt, crn, crm, opc2) \ + ARM_MCR_COND ((p), (coproc), (opc1), (rt), (crn), (crm), (opc2), ARMCOND_AL) + +/* MRC */ +#define ARM_DEF_MRC_COND(coproc, opc1, rt, crn, crm, opc2, cond) \ + ARM_DEF_COND ((cond)) | ((0xe << 24) | (((opc1) & 0x7) << 21) | (1 << 20) | (((crn) & 0xf) << 16) | (((rt) & 0xf) << 12) | (((coproc) & 0xf) << 8) | (((opc2) & 0x7) << 5) | (1 << 4) | (((crm) & 0xf) << 0)) + +#define ARM_MRC_COND(p, coproc, opc1, rt, crn, crm, opc2, cond) \ + ARM_EMIT(p, ARM_DEF_MRC_COND ((coproc), (opc1), (rt), (crn), (crm), (opc2), (cond))) + +#define ARM_MRC(p, coproc, opc1, rt, crn, crm, opc2) \ + ARM_MRC_COND ((p), (coproc), (opc1), (rt), (crn), (crm), (opc2), ARMCOND_AL) + +/* ARMv7VE */ +#define ARM_SDIV_COND(p, rd, rn, rm, cond) ARM_EMIT (p, (((cond) << 28) | (0xe << 23) | (0x1 << 20) | ((rd) << 16) | (0xf << 12) | ((rm) << 8) | (0x0 << 5) | (0x1 << 4) | ((rn) << 0))) +#define ARM_SDIV(p, rd, rn, rm) ARM_SDIV_COND ((p), (rd), (rn), (rm), ARMCOND_AL) + +#define ARM_UDIV_COND(p, rd, rn, rm, cond) ARM_EMIT (p, (((cond) << 28) | (0xe << 23) | (0x3 << 20) | ((rd) << 16) | (0xf << 12) | ((rm) << 8) | (0x0 << 5) | (0x1 << 4) | ((rn) << 0))) +#define ARM_UDIV(p, rd, rn, rm) ARM_UDIV_COND ((p), (rd), (rn), (rm), ARMCOND_AL) + +/* ARMv7 */ + +typedef enum { + ARM_DMB_SY = 0xf, +} ArmDmbFlags; + +#define ARM_DMB(p, option) ARM_EMIT ((p), ((0xf << 28) | (0x57 << 20) | (0xf << 16) | (0xf << 12) | (0x0 << 8) | (0x5 << 4) | ((option) << 0))) + +#define ARM_LDREX_REG(p, rt, rn) ARM_EMIT ((p), ((ARMCOND_AL << 28) | (0xc << 21) | (0x1 << 20) | ((rn) << 16) | ((rt) << 12)) | (0xf << 8) | (0x9 << 4) | 0xf << 0) + +#define ARM_STREX_REG(p, rd, rt, rn) ARM_EMIT ((p), ((ARMCOND_AL << 28) | (0xc << 21) | (0x0 << 20) | ((rn) << 16) | ((rd) << 12)) | (0xf << 8) | (0x9 << 4) | ((rt) << 0)) + +#ifdef __cplusplus +} +#endif + +#endif /* ARM_H */ + diff --git a/unity-2019.4.24f1-mbe/mono/arch/arm/arm-vfp-codegen.h b/unity-2019.4.24f1-mbe/mono/arch/arm/arm-vfp-codegen.h new file mode 100644 index 000000000..edf558da2 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/arm/arm-vfp-codegen.h @@ -0,0 +1,248 @@ +// +// Copyright 2011 Xamarin Inc +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef __MONO_ARM_VFP_CODEGEN_H__ +#define __MONO_ARM_VFP_CODEGEN_H__ + +#include "arm-codegen.h" + +enum { + /* VFP registers */ + ARM_VFP_F0, + ARM_VFP_F1, + ARM_VFP_F2, + ARM_VFP_F3, + ARM_VFP_F4, + ARM_VFP_F5, + ARM_VFP_F6, + ARM_VFP_F7, + ARM_VFP_F8, + ARM_VFP_F9, + ARM_VFP_F10, + ARM_VFP_F11, + ARM_VFP_F12, + ARM_VFP_F13, + ARM_VFP_F14, + ARM_VFP_F15, + ARM_VFP_F16, + ARM_VFP_F17, + ARM_VFP_F18, + ARM_VFP_F19, + ARM_VFP_F20, + ARM_VFP_F21, + ARM_VFP_F22, + ARM_VFP_F23, + ARM_VFP_F24, + ARM_VFP_F25, + ARM_VFP_F26, + ARM_VFP_F27, + ARM_VFP_F28, + ARM_VFP_F29, + ARM_VFP_F30, + ARM_VFP_F31, + + ARM_VFP_D0 = ARM_VFP_F0, + ARM_VFP_D1 = ARM_VFP_F2, + ARM_VFP_D2 = ARM_VFP_F4, + ARM_VFP_D3 = ARM_VFP_F6, + ARM_VFP_D4 = ARM_VFP_F8, + ARM_VFP_D5 = ARM_VFP_F10, + ARM_VFP_D6 = ARM_VFP_F12, + ARM_VFP_D7 = ARM_VFP_F14, + ARM_VFP_D8 = ARM_VFP_F16, + ARM_VFP_D9 = ARM_VFP_F18, + ARM_VFP_D10 = ARM_VFP_F20, + ARM_VFP_D11 = ARM_VFP_F22, + ARM_VFP_D12 = ARM_VFP_F24, + ARM_VFP_D13 = ARM_VFP_F26, + ARM_VFP_D14 = ARM_VFP_F28, + ARM_VFP_D15 = ARM_VFP_F30, + + ARM_VFP_COPROC_SINGLE = 10, + ARM_VFP_COPROC_DOUBLE = 11, + +#define ARM_VFP_OP(p,q,r,s) (((p) << 23) | ((q) << 21) | ((r) << 20) | ((s) << 6)) +#define ARM_VFP_OP2(Fn,N) (ARM_VFP_OP (1,1,1,1) | ((Fn) << 16) | ((N) << 7)) + + ARM_VFP_MUL = ARM_VFP_OP (0,1,0,0), + ARM_VFP_NMUL = ARM_VFP_OP (0,1,0,1), + ARM_VFP_ADD = ARM_VFP_OP (0,1,1,0), + ARM_VFP_SUB = ARM_VFP_OP (0,1,1,1), + ARM_VFP_DIV = ARM_VFP_OP (1,0,0,0), + + ARM_VFP_CPY = ARM_VFP_OP2 (0,0), + ARM_VFP_ABS = ARM_VFP_OP2 (0,1), + ARM_VFP_NEG = ARM_VFP_OP2 (1,0), + ARM_VFP_SQRT = ARM_VFP_OP2 (1,1), + ARM_VFP_CMP = ARM_VFP_OP2 (4,0), + ARM_VFP_CMPE = ARM_VFP_OP2 (4,1), + ARM_VFP_CMPZ = ARM_VFP_OP2 (5,0), + ARM_VFP_CMPEZ = ARM_VFP_OP2 (5,1), + ARM_VFP_CVT = ARM_VFP_OP2 (7,1), + ARM_VFP_UITO = ARM_VFP_OP2 (8,0), + ARM_VFP_SITO = ARM_VFP_OP2 (8,1), + ARM_VFP_TOUI = ARM_VFP_OP2 (12,0), + ARM_VFP_TOSI = ARM_VFP_OP2 (13,0), + ARM_VFP_TOUIZ = ARM_VFP_OP2 (12,1), + ARM_VFP_TOSIZ = ARM_VFP_OP2 (13,1), + + ARM_VFP_SID = 0, + ARM_VFP_SCR = 1 << 1, + ARM_VFP_EXC = 8 << 1 +}; + +#define ARM_DEF_VFP_DYADIC(cond,cp,op,Fd,Fn,Fm) \ + (14 << 24) | \ + ((cp) << 8) | \ + (op) | \ + (((Fd) >> 1) << 12) | \ + (((Fd) & 1) << 22) | \ + (((Fn) >> 1) << 16) | \ + (((Fn) & 1) << 7) | \ + (((Fm) >> 1) << 0) | \ + (((Fm) & 1) << 5) | \ + ARM_DEF_COND(cond) + +#define ARM_DEF_VFP_MONADIC(cond,cp,op,Fd,Fm) \ + (14 << 24) | \ + ((cp) << 8) | \ + (op) | \ + (((Fd) >> 1) << 12) | \ + (((Fd) & 1) << 22) | \ + (((Fm) >> 1) << 0) | \ + (((Fm) & 1) << 5) | \ + ARM_DEF_COND(cond) + +#define ARM_DEF_VFP_LSF(cond,cp,post,ls,wback,basereg,Fd,offset) \ + ((offset) >= 0? (offset)>>2: -(offset)>>2) | \ + (6 << 25) | \ + ((cp) << 8) | \ + (((Fd) >> 1) << 12) | \ + (((Fd) & 1) << 22) | \ + ((basereg) << 16) | \ + ((ls) << 20) | \ + ((wback) << 21) | \ + (((offset) >= 0) << 23) | \ + ((wback) << 21) | \ + ((post) << 24) | \ + ARM_DEF_COND(cond) + +#define ARM_DEF_VFP_CPT(cond,cp,op,L,Fn,Rd) \ + (14 << 24) | \ + (1 << 4) | \ + ((cp) << 8) | \ + ((op) << 21) | \ + ((L) << 20) | \ + ((Rd) << 12) | \ + (((Fn) >> 1) << 16) | \ + (((Fn) & 1) << 7) | \ + ARM_DEF_COND(cond) + +/* FP load and stores */ +#define ARM_FLDS_COND(p,freg,base,offset,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_LSF((cond),ARM_VFP_COPROC_SINGLE,1,ARMOP_LDR,0,(base),(freg),(offset))) +#define ARM_FLDS(p,freg,base,offset) \ + ARM_FLDS_COND(p,freg,base,offset,ARMCOND_AL) + +#define ARM_FLDD_COND(p,freg,base,offset,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_LSF((cond),ARM_VFP_COPROC_DOUBLE,1,ARMOP_LDR,0,(base),(freg),(offset))) +#define ARM_FLDD(p,freg,base,offset) \ + ARM_FLDD_COND(p,freg,base,offset,ARMCOND_AL) + +#define ARM_FSTS_COND(p,freg,base,offset,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_LSF((cond),ARM_VFP_COPROC_SINGLE,1,ARMOP_STR,0,(base),(freg),(offset))) +#define ARM_FSTS(p,freg,base,offset) \ + ARM_FSTS_COND(p,freg,base,offset,ARMCOND_AL) + +#define ARM_FSTD_COND(p,freg,base,offset,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_LSF((cond),ARM_VFP_COPROC_DOUBLE,1,ARMOP_STR,0,(base),(freg),(offset))) +#define ARM_FSTD(p,freg,base,offset) \ + ARM_FSTD_COND(p,freg,base,offset,ARMCOND_AL) + +#define ARM_FLDMD_COND(p,first_reg,nregs,base,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_LSF((cond),ARM_VFP_COPROC_DOUBLE,0,ARMOP_LDR,0,(base),(first_reg),((nregs) * 2) << 2)) + +#define ARM_FLDMD(p,first_reg,nregs,base) \ + ARM_FLDMD_COND(p,first_reg,nregs,base,ARMCOND_AL) + +#define ARM_FSTMD_COND(p,first_reg,nregs,base,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_LSF((cond),ARM_VFP_COPROC_DOUBLE,0,ARMOP_STR,0,(base),(first_reg),((nregs) * 2) << 2)) + +#define ARM_FSTMD(p,first_reg,nregs,base) \ + ARM_FSTMD_COND(p,first_reg,nregs,base,ARMCOND_AL) + +#include + +/* coprocessor register transfer */ +#define ARM_FMSR(p,freg,reg) \ + ARM_EMIT((p), ARM_DEF_VFP_CPT(ARMCOND_AL,ARM_VFP_COPROC_SINGLE,0,0,(freg),(reg))) +#define ARM_FMRS(p,reg,freg) \ + ARM_EMIT((p), ARM_DEF_VFP_CPT(ARMCOND_AL,ARM_VFP_COPROC_SINGLE,0,1,(freg),(reg))) + +#define ARM_FMDLR(p,freg,reg) \ + ARM_EMIT((p), ARM_DEF_VFP_CPT(ARMCOND_AL,ARM_VFP_COPROC_DOUBLE,0,0,(freg),(reg))) +#define ARM_FMRDL(p,reg,freg) \ + ARM_EMIT((p), ARM_DEF_VFP_CPT(ARMCOND_AL,ARM_VFP_COPROC_DOUBLE,0,1,(freg),(reg))) +#define ARM_FMDHR(p,freg,reg) \ + ARM_EMIT((p), ARM_DEF_VFP_CPT(ARMCOND_AL,ARM_VFP_COPROC_DOUBLE,1,0,(freg),(reg))) +#define ARM_FMRDH(p,reg,freg) \ + ARM_EMIT((p), ARM_DEF_VFP_CPT(ARMCOND_AL,ARM_VFP_COPROC_DOUBLE,1,1,(freg),(reg))) + +#define ARM_FMXR(p,freg,reg) \ + ARM_EMIT((p), ARM_DEF_VFP_CPT(ARMCOND_AL,ARM_VFP_COPROC_SINGLE,7,0,(freg),(reg))) +#define ARM_FMRX(p,reg,fcreg) \ + ARM_EMIT((p), ARM_DEF_VFP_CPT(ARMCOND_AL,ARM_VFP_COPROC_SINGLE,7,1,(fcreg),(reg))) + +#define ARM_FMSTAT(p) \ + ARM_FMRX((p),ARMREG_R15,ARM_VFP_SCR) + +#define ARM_DEF_MCRR(cond,cp,rn,rd,Fm,M) \ + ((Fm) << 0) | \ + (1 << 4) | \ + ((M) << 5) | \ + ((cp) << 8) | \ + ((rd) << 12) | \ + ((rn) << 16) | \ + ((2) << 21) | \ + (12 << 24) | \ + ARM_DEF_COND(cond) + +#define ARM_FMDRR(p,rd,rn,dm) \ + ARM_EMIT((p), ARM_DEF_MCRR(ARMCOND_AL,ARM_VFP_COPROC_DOUBLE,(rn),(rd),(dm) >> 1, (dm) & 1)) + +#define ARM_DEF_FMRRD(cond,cp,rn,rd,Dm,D) \ + ((Dm) << 0) | \ + (1 << 4) | \ + ((cp) << 8) | \ + ((rd) << 12) | \ + ((rn) << 16) | \ + ((0xc5) << 20) | \ + ARM_DEF_COND(cond) + +#define ARM_FMRRD(p,rd,rn,dm) \ + ARM_EMIT((p), ARM_DEF_FMRRD(ARMCOND_AL,ARM_VFP_COPROC_DOUBLE,(rn),(rd),(dm) >> 1, (dm) & 1)) + +#define ARM_DEF_FUITOS(cond,Dd,D,Fm,M) ((cond) << 28) | ((0x1d) << 23) | ((D) << 22) | ((0x3) << 20) | ((8) << 16) | ((Dd) << 12) | ((0xa) << 8) | ((1) << 6) | ((M) << 5) | ((Fm) << 0) + +#define ARM_FUITOS(p,dreg,sreg) \ + ARM_EMIT((p), ARM_DEF_FUITOS (ARMCOND_AL, (dreg) >> 1, (dreg) & 1, (sreg) >> 1, (sreg) & 1)) + +#define ARM_DEF_FUITOD(cond,Dd,D,Fm,M) ((cond) << 28) | ((0x1d) << 23) | ((D) << 22) | ((0x3) << 20) | ((8) << 16) | ((Dd) << 12) | ((0xb) << 8) | ((1) << 6) | ((M) << 5) | ((Fm) << 0) + +#define ARM_FUITOD(p,dreg,sreg) \ + ARM_EMIT((p), ARM_DEF_FUITOD (ARMCOND_AL, (dreg) >> 1, (dreg) & 1, (sreg) >> 1, (sreg) & 1)) + +#define ARM_DEF_FSITOS(cond,Dd,D,Fm,M) ((cond) << 28) | ((0x1d) << 23) | ((D) << 22) | ((0x3) << 20) | ((8) << 16) | ((Dd) << 12) | ((0xa) << 8) | ((1) << 7) | ((1) << 6) | ((M) << 5) | ((Fm) << 0) + +#define ARM_FSITOS(p,dreg,sreg) \ + ARM_EMIT((p), ARM_DEF_FSITOS (ARMCOND_AL, (dreg) >> 1, (dreg) & 1, (sreg) >> 1, (sreg) & 1)) + +#define ARM_DEF_FSITOD(cond,Dd,D,Fm,M) ((cond) << 28) | ((0x1d) << 23) | ((D) << 22) | ((0x3) << 20) | ((8) << 16) | ((Dd) << 12) | ((0xb) << 8) | ((1) << 7) | ((1) << 6) | ((M) << 5) | ((Fm) << 0) + +#define ARM_FSITOD(p,dreg,sreg) \ + ARM_EMIT((p), ARM_DEF_FSITOD (ARMCOND_AL, (dreg) >> 1, (dreg) & 1, (sreg) >> 1, (sreg) & 1)) + +#endif /* __MONO_ARM_VFP_CODEGEN_H__ */ + diff --git a/unity-2019.4.24f1-mbe/mono/arch/arm/arm-wmmx.h b/unity-2019.4.24f1-mbe/mono/arch/arm/arm-wmmx.h new file mode 100644 index 000000000..6f2fc785d --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/arm/arm-wmmx.h @@ -0,0 +1,178 @@ +/* + * ARM CodeGen + * XScale WirelessMMX extensions + * Copyright 2002 Wild West Software + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#ifndef __WMMX_H__ +#define __WMMX_H__ 1 + +#if 0 +#include +#endif + +#if defined(ARM_IASM) +# define WM_ASM(_expr) ARM_IASM(_expr) +#else +# define WM_ASM(_expr) __emit (_expr) +#endif + +#if defined(ARM_EMIT) +# define WM_EMIT(p, i) ARM_EMIT(p, i) +#else +# define WM_EMIT(p, i) +#endif + +enum { + WM_CC_EQ = 0x0, + WM_CC_NE = 0x1, + WM_CC_CS = 0x2, + WM_CC_HS = WM_CC_CS, + WM_CC_CC = 0x3, + WM_CC_LO = WM_CC_CC, + WM_CC_MI = 0x4, + WM_CC_PL = 0x5, + WM_CC_VS = 0x6, + WM_CC_VC = 0x7, + WM_CC_HI = 0x8, + WM_CC_LS = 0x9, + WM_CC_GE = 0xA, + WM_CC_LT = 0xB, + WM_CC_GT = 0xC, + WM_CC_LE = 0xD, + WM_CC_AL = 0xE, + WM_CC_NV = 0xF, + WM_CC_SHIFT = 28 +}; + +#if defined(ARM_DEF_COND) +# define WM_DEF_CC(_cc) ARM_DEF_COND(_cc) +#else +# define WM_DEF_CC(_cc) ((_cc & 0xF) << WM_CC_SHIFT) +#endif + + +enum { + WM_R0 = 0x0, + WM_R1 = 0x1, + WM_R2 = 0x2, + WM_R3 = 0x3, + WM_R4 = 0x4, + WM_R5 = 0x5, + WM_R6 = 0x6, + WM_R7 = 0x7, + WM_R8 = 0x8, + WM_R9 = 0x9, + WM_R10 = 0xA, + WM_R11 = 0xB, + WM_R12 = 0xC, + WM_R13 = 0xD, + WM_R14 = 0xE, + WM_R15 = 0xF, + + WM_wR0 = 0x0, + WM_wR1 = 0x1, + WM_wR2 = 0x2, + WM_wR3 = 0x3, + WM_wR4 = 0x4, + WM_wR5 = 0x5, + WM_wR6 = 0x6, + WM_wR7 = 0x7, + WM_wR8 = 0x8, + WM_wR9 = 0x9, + WM_wR10 = 0xA, + WM_wR11 = 0xB, + WM_wR12 = 0xC, + WM_wR13 = 0xD, + WM_wR14 = 0xE, + WM_wR15 = 0xF +}; + + +/* + * Qualifiers: + * H - 16-bit (HalfWord) SIMD + * W - 32-bit (Word) SIMD + * D - 64-bit (Double) + */ +enum { + WM_B = 0, + WM_H = 1, + WM_D = 2 +}; + +/* + * B.2.3 Transfers From Coprocessor Register (MRC) + * Table B-5 + */ +enum { + WM_TMRC_OP2 = 0, + WM_TMRC_CPNUM = 1, + + WM_TMOVMSK_OP2 = 1, + WM_TMOVMSK_CPNUM = 0, + + WM_TANDC_OP2 = 1, + WM_TANDC_CPNUM = 1, + + WM_TORC_OP2 = 2, + WM_TORC_CPNUM = 1, + + WM_TEXTRC_OP2 = 3, + WM_TEXTRC_CPNUM = 1, + + WM_TEXTRM_OP2 = 3, + WM_TEXTRM_CPNUM = 0 +}; + + +/* + * TANDC{Cond} R15 + * Performs AND across the fields of the SIMD PSR register (wCASF) and sends the result + * to CPSR; can be performed after a Byte, Half-word or Word operation that sets the flags. + * NOTE: R15 is omitted from the macro declaration; + */ +#define DEF_WM_TNADC_CC(_q, _cc) WM_DEF_CC((_cc)) + ((_q) << 0x16) + 0xE13F130 + +#define _WM_TNADC_CC(_q, _cc) WM_ASM(DEF_WM_TNADC_CC(_q, _cc)) +#define ARM_WM_TNADC_CC(_p, _q, _cc) WM_EMIT(_p, DEF_WM_TNADC_CC(_q, _cc)) + +/* inline assembly */ +#define _WM_TNADC(_q) _WM_TNADC_CC((_q), WM_CC_AL) +#define _WM_TNADCB() _WM_TNADC(WM_B) +#define _WM_TNADCH() _WM_TNADC(WM_H) +#define _WM_TNADCD() _WM_TNADC(WM_D) + +/* codegen */ +#define ARM_WM_TNADC(_p, _q) ARM_WM_TNADC_CC((_p), (_q), WM_CC_AL) +#define ARM_WM_TNADCB(_p) ARM_WM_TNADC(_p, WM_B) +#define ARM_WM_TNADCH(_p) ARM_WM_TNADC(_p, WM_H) +#define ARM_WM_TNADCD(_p) ARM_WM_TNADC(_p, WM_D) + + +/* + * TBCST{Cond} wRd, Rn + * Broadcasts a value from the ARM Source reg (Rn) to every SIMD position + * in the WMMX Destination reg (wRd). + */ +#define DEF_WM_TBCST_CC(_q, _cc, _wrd, _rn) \ + WM_DEF_CC((_cc)) + ((_q) << 6) + ((_wrd) << 16) + ((_rn) << 12) + 0xE200010 + +#define _WM_TBCST_CC(_q, _cc, _wrd, _rn) WM_ASM(DEF_WM_TBCST_CC(_q, _cc, _wrd, _rn)) +#define ARM_WM_TBCST_CC(_p, _q, _cc, _wrd, _rn) WM_EMIT(_p, DEF_WM_TBCST_CC(_q, _cc, _wrd, _rn)) + +/* inline */ +#define _WM_TBCST(_q, _wrd, _rn) _WM_TBCST_CC(_q, WM_CC_AL, _wrd, _rn) +#define _WM_TBCSTB(_wrd, _rn) _WM_TBCST(WM_B) +#define _WM_TBCSTH(_wrd, _rn) _WM_TBCST(WM_H) +#define _WM_TBCSTD(_wrd, _rn) _WM_TBCST(WM_D) + +/* codegen */ +#define ARM_WM_TBCST(_p, _q, _wrd, _rn) ARM_WM_TBCST_CC(_p, _q, WM_CC_AL, _wrd, _rn) +#define ARM_WM_TBCSTB(_p, _wrd, _rn) _WM_TBCST(_p, WM_B) +#define ARM_WM_TBCSTH(_p, _wrd, _rn) _WM_TBCST(_p, WM_H) +#define ARM_WM_TBCSTD(_p, _wrd, _rn) _WM_TBCST(_p, WM_D) + + +#endif /* __WMMX_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/arch/arm/arm_dpimacros.h b/unity-2019.4.24f1-mbe/mono/arch/arm/arm_dpimacros.h new file mode 100644 index 000000000..d8ff66699 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/arm/arm_dpimacros.h @@ -0,0 +1,1603 @@ +/* Macros for DPI ops, auto-generated from template */ + + +/* mov/mvn */ + +/* Rd := imm8 ROR rot */ +#define ARM_MOV_REG_IMM_COND(p, reg, imm8, rot, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_MOV, reg, 0, imm8, rot, cond) +#define ARM_MOV_REG_IMM(p, reg, imm8, rot) \ + ARM_MOV_REG_IMM_COND(p, reg, imm8, rot, ARMCOND_AL) +/* S */ +#define ARM_MOVS_REG_IMM_COND(p, reg, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_MOV, reg, 0, imm8, rot, cond) +#define ARM_MOVS_REG_IMM(p, reg, imm8, rot) \ + ARM_MOVS_REG_IMM_COND(p, reg, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _MOV_REG_IMM_COND(reg, imm8, rot, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_MOV, reg, 0, imm8, rot, cond) +#define _MOV_REG_IMM(reg, imm8, rot) \ + _MOV_REG_IMM_COND(reg, imm8, rot, ARMCOND_AL) +/* S */ +#define _MOVS_REG_IMM_COND(reg, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_MOV, reg, 0, imm8, rot, cond) +#define _MOVS_REG_IMM(reg, imm8, rot) \ + _MOVS_REG_IMM_COND(reg, imm8, rot, ARMCOND_AL) +#endif + + +/* Rd := imm8 */ +#define ARM_MOV_REG_IMM8_COND(p, reg, imm8, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_MOV, reg, 0, imm8, 0, cond) +#define ARM_MOV_REG_IMM8(p, reg, imm8) \ + ARM_MOV_REG_IMM8_COND(p, reg, imm8, ARMCOND_AL) +/* S */ +#define ARM_MOVS_REG_IMM8_COND(p, reg, imm8, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_MOV, reg, 0, imm8, 0, cond) +#define ARM_MOVS_REG_IMM8(p, reg, imm8) \ + ARM_MOVS_REG_IMM8_COND(p, reg, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _MOV_REG_IMM8_COND(reg, imm8, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_MOV, reg, 0, imm8, 0, cond) +#define _MOV_REG_IMM8(reg, imm8) \ + _MOV_REG_IMM8_COND(reg, imm8, ARMCOND_AL) +/* S */ +#define _MOVS_REG_IMM8_COND(reg, imm8, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_MOV, reg, 0, imm8, 0, cond) +#define _MOVS_REG_IMM8(reg, imm8) \ + _MOVS_REG_IMM8_COND(reg, imm8, ARMCOND_AL) +#endif + + +/* Rd := Rm */ +#define ARM_MOV_REG_REG_COND(p, rd, rm, cond) \ + ARM_DPIOP_REG_REG_COND(p, ARMOP_MOV, rd, 0, rm, cond) +#define ARM_MOV_REG_REG(p, rd, rm) \ + ARM_MOV_REG_REG_COND(p, rd, rm, ARMCOND_AL) +/* S */ +#define ARM_MOVS_REG_REG_COND(p, rd, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_MOV, rd, 0, rm, cond) +#define ARM_MOVS_REG_REG(p, rd, rm) \ + ARM_MOVS_REG_REG_COND(p, rd, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _MOV_REG_REG_COND(rd, rm, cond) \ + ARM_IASM_DPIOP_REG_REG_COND(ARMOP_MOV, rd, 0, rm, cond) +#define _MOV_REG_REG(rd, rm) \ + _MOV_REG_REG_COND(rd, rm, ARMCOND_AL) +/* S */ +#define _MOVS_REG_REG_COND(rd, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_MOV, rd, 0, rm, cond) +#define _MOVS_REG_REG(rd, rm) \ + _MOVS_REG_REG_COND(rd, rm, ARMCOND_AL) +#endif + + +/* Rd := Rm imm_shift */ +#define ARM_MOV_REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_MOV, rd, 0, rm, shift_type, imm_shift, cond) +#define ARM_MOV_REG_IMMSHIFT(p, rd, rm, shift_type, imm_shift) \ + ARM_MOV_REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, ARMCOND_AL) +/* S */ +#define ARM_MOVS_REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_MOV, rd, 0, rm, shift_type, imm_shift, cond) +#define ARM_MOVS_REG_IMMSHIFT(p, rd, rm, shift_type, imm_shift) \ + ARM_MOVS_REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _MOV_REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_MOV, rd, 0, rm, shift_type, imm_shift, cond) +#define _MOV_REG_IMMSHIFT(rd, rm, shift_type, imm_shift) \ + _MOV_REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, ARMCOND_AL) +/* S */ +#define _MOVS_REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_MOV, rd, 0, rm, shift_type, imm_shift, cond) +#define _MOVS_REG_IMMSHIFT(rd, rm, shift_type, imm_shift) \ + _MOVS_REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + + +/* Rd := (Rm Rs) */ +#define ARM_MOV_REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, cond) \ + ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_MOV, rd, 0, rm, shift_type, rs, cond) +#define ARM_MOV_REG_REGSHIFT(p, rd, rm, shift_type, rs) \ + ARM_MOV_REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, ARMCOND_AL) +/* S */ +#define ARM_MOVS_REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, cond) \ + ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_MOV, rd, 0, rm, shift_type, rs, cond) +#define ARM_MOVS_REG_REGSHIFT(p, rd, rm, shift_type, rs) \ + ARM_MOVS_REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _MOV_REG_REGSHIFT_COND(rd, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_MOV, rd, 0, rm, shift_type, rs, cond) +#define _MOV_REG_REGSHIFT(rd, rm, shift_type, rs) \ + _MOV_REG_REGSHIFT_COND(rd, rm, shift_type, rs, ARMCOND_AL) +/* S */ +#define _MOVS_REG_REGSHIFT_COND(rd, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_MOV, rd, 0, rm, shift_type, rs, cond) +#define _MOVS_REG_REGSHIFT(rd, rm, shift_type, rs) \ + _MOVS_REG_REGSHIFT_COND(rd, rm, shift_type, rs, ARMCOND_AL) +#endif + + +/* Rd := imm8 ROR rot */ +#define ARM_MVN_REG_IMM_COND(p, reg, imm8, rot, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_MVN, reg, 0, imm8, rot, cond) +#define ARM_MVN_REG_IMM(p, reg, imm8, rot) \ + ARM_MVN_REG_IMM_COND(p, reg, imm8, rot, ARMCOND_AL) +/* S */ +#define ARM_MVNS_REG_IMM_COND(p, reg, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_MVN, reg, 0, imm8, rot, cond) +#define ARM_MVNS_REG_IMM(p, reg, imm8, rot) \ + ARM_MVNS_REG_IMM_COND(p, reg, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _MVN_REG_IMM_COND(reg, imm8, rot, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_MVN, reg, 0, imm8, rot, cond) +#define _MVN_REG_IMM(reg, imm8, rot) \ + _MVN_REG_IMM_COND(reg, imm8, rot, ARMCOND_AL) +/* S */ +#define _MVNS_REG_IMM_COND(reg, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_MVN, reg, 0, imm8, rot, cond) +#define _MVNS_REG_IMM(reg, imm8, rot) \ + _MVNS_REG_IMM_COND(reg, imm8, rot, ARMCOND_AL) +#endif + + +/* Rd := imm8 */ +#define ARM_MVN_REG_IMM8_COND(p, reg, imm8, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_MVN, reg, 0, imm8, 0, cond) +#define ARM_MVN_REG_IMM8(p, reg, imm8) \ + ARM_MVN_REG_IMM8_COND(p, reg, imm8, ARMCOND_AL) +/* S */ +#define ARM_MVNS_REG_IMM8_COND(p, reg, imm8, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_MVN, reg, 0, imm8, 0, cond) +#define ARM_MVNS_REG_IMM8(p, reg, imm8) \ + ARM_MVNS_REG_IMM8_COND(p, reg, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _MVN_REG_IMM8_COND(reg, imm8, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_MVN, reg, 0, imm8, 0, cond) +#define _MVN_REG_IMM8(reg, imm8) \ + _MVN_REG_IMM8_COND(reg, imm8, ARMCOND_AL) +/* S */ +#define _MVNS_REG_IMM8_COND(reg, imm8, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_MVN, reg, 0, imm8, 0, cond) +#define _MVNS_REG_IMM8(reg, imm8) \ + _MVNS_REG_IMM8_COND(reg, imm8, ARMCOND_AL) +#endif + + +/* Rd := Rm */ +#define ARM_MVN_REG_REG_COND(p, rd, rm, cond) \ + ARM_DPIOP_REG_REG_COND(p, ARMOP_MVN, rd, 0, rm, cond) +#define ARM_MVN_REG_REG(p, rd, rm) \ + ARM_MVN_REG_REG_COND(p, rd, rm, ARMCOND_AL) +/* S */ +#define ARM_MVNS_REG_REG_COND(p, rd, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_MVN, rd, 0, rm, cond) +#define ARM_MVNS_REG_REG(p, rd, rm) \ + ARM_MVNS_REG_REG_COND(p, rd, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _MVN_REG_REG_COND(rd, rm, cond) \ + ARM_IASM_DPIOP_REG_REG_COND(ARMOP_MVN, rd, 0, rm, cond) +#define _MVN_REG_REG(rd, rm) \ + _MVN_REG_REG_COND(rd, rm, ARMCOND_AL) +/* S */ +#define _MVNS_REG_REG_COND(rd, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_MVN, rd, 0, rm, cond) +#define _MVNS_REG_REG(rd, rm) \ + _MVNS_REG_REG_COND(rd, rm, ARMCOND_AL) +#endif + + +/* Rd := Rm imm_shift */ +#define ARM_MVN_REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_MVN, rd, 0, rm, shift_type, imm_shift, cond) +#define ARM_MVN_REG_IMMSHIFT(p, rd, rm, shift_type, imm_shift) \ + ARM_MVN_REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, ARMCOND_AL) +/* S */ +#define ARM_MVNS_REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_MVN, rd, 0, rm, shift_type, imm_shift, cond) +#define ARM_MVNS_REG_IMMSHIFT(p, rd, rm, shift_type, imm_shift) \ + ARM_MVNS_REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _MVN_REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_MVN, rd, 0, rm, shift_type, imm_shift, cond) +#define _MVN_REG_IMMSHIFT(rd, rm, shift_type, imm_shift) \ + _MVN_REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, ARMCOND_AL) +/* S */ +#define _MVNS_REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_MVN, rd, 0, rm, shift_type, imm_shift, cond) +#define _MVNS_REG_IMMSHIFT(rd, rm, shift_type, imm_shift) \ + _MVNS_REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + + +/* Rd := (Rm Rs) */ +#define ARM_MVN_REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, cond) \ + ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_MVN, rd, 0, rm, shift_type, rs, cond) +#define ARM_MVN_REG_REGSHIFT(p, rd, rm, shift_type, rs) \ + ARM_MVN_REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, ARMCOND_AL) +/* S */ +#define ARM_MVNS_REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, cond) \ + ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_MVN, rd, 0, rm, shift_type, rs, cond) +#define ARM_MVNS_REG_REGSHIFT(p, rd, rm, shift_type, rs) \ + ARM_MVNS_REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _MVN_REG_REGSHIFT_COND(rd, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_MVN, rd, 0, rm, shift_type, rs, cond) +#define _MVN_REG_REGSHIFT(rd, rm, shift_type, rs) \ + _MVN_REG_REGSHIFT_COND(rd, rm, shift_type, rs, ARMCOND_AL) +/* S */ +#define _MVNS_REG_REGSHIFT_COND(rd, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_MVN, rd, 0, rm, shift_type, rs, cond) +#define _MVNS_REG_REGSHIFT(rd, rm, shift_type, rs) \ + _MVNS_REG_REGSHIFT_COND(rd, rm, shift_type, rs, ARMCOND_AL) +#endif + + + +/* DPIs, arithmetic and logical */ + +/* -- AND -- */ + +/* Rd := Rn AND (imm8 ROR rot) ; rot is power of 2 */ +#define ARM_AND_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_AND, rd, rn, imm8, rot, cond) +#define ARM_AND_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_AND_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) +#define ARM_ANDS_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_AND, rd, rn, imm8, rot, cond) +#define ARM_ANDS_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_ANDS_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _AND_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_AND, rd, rn, imm8, rot, cond) +#define _AND_REG_IMM(rd, rn, imm8, rot) \ + _AND_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#define _ANDS_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_AND, rd, rn, imm8, rot, cond) +#define _ANDS_REG_IMM(rd, rn, imm8, rot) \ + _ANDS_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#endif + + +/* Rd := Rn AND imm8 */ +#define ARM_AND_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_AND_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_AND_REG_IMM8(p, rd, rn, imm8) \ + ARM_AND_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) +#define ARM_ANDS_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_ANDS_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_ANDS_REG_IMM8(p, rd, rn, imm8) \ + ARM_ANDS_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _AND_REG_IMM8_COND(rd, rn, imm8, cond) \ + _AND_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _AND_REG_IMM8(rd, rn, imm8) \ + _AND_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#define _ANDS_REG_IMM8_COND(rd, rn, imm8, cond) \ + _ANDS_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _ANDS_REG_IMM8(rd, rn, imm8) \ + _ANDS_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#endif + + +/* Rd := Rn AND Rm */ +#define ARM_AND_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_REG_REG_COND(p, ARMOP_AND, rd, rn, rm, cond) +#define ARM_AND_REG_REG(p, rd, rn, rm) \ + ARM_AND_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) +#define ARM_ANDS_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_AND, rd, rn, rm, cond) +#define ARM_ANDS_REG_REG(p, rd, rn, rm) \ + ARM_ANDS_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _AND_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_REG_REG_COND(ARMOP_AND, rd, rn, rm, cond) +#define _AND_REG_REG(rd, rn, rm) \ + _AND_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#define _ANDS_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_AND, rd, rn, rm, cond) +#define _ANDS_REG_REG(rd, rn, rm) \ + _ANDS_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#endif + + +/* Rd := Rn AND (Rm imm_shift) */ +#define ARM_AND_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_AND, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_AND_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_AND_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define ARM_ANDS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_AND, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_ANDS_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_ANDS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _AND_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_AND, rd, rn, rm, shift_type, imm_shift, cond) +#define _AND_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _AND_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define _ANDS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_AND, rd, rn, rm, shift_type, imm_shift, cond) +#define _ANDS_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _ANDS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + +/* Rd := Rn AND (Rm Rs) */ +#define ARM_AND_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_AND, rd, rn, rm, shift_t, rs, cond) +#define ARM_AND_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_AND_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define ARM_ANDS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_AND, rd, rn, rm, shift_t, rs, cond) +#define ARM_ANDS_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_ANDS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _AND_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_AND, rd, rn, rm, shift_t, rs, cond) +#define _AND_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _AND_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define _ANDS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_AND, rd, rn, rm, shift_t, rs, cond) +#define _ANDS_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _ANDS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#endif + + +/* -- EOR -- */ + +/* Rd := Rn EOR (imm8 ROR rot) ; rot is power of 2 */ +#define ARM_EOR_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_EOR, rd, rn, imm8, rot, cond) +#define ARM_EOR_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_EOR_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) +#define ARM_EORS_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_EOR, rd, rn, imm8, rot, cond) +#define ARM_EORS_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_EORS_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _EOR_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_EOR, rd, rn, imm8, rot, cond) +#define _EOR_REG_IMM(rd, rn, imm8, rot) \ + _EOR_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#define _EORS_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_EOR, rd, rn, imm8, rot, cond) +#define _EORS_REG_IMM(rd, rn, imm8, rot) \ + _EORS_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#endif + + +/* Rd := Rn EOR imm8 */ +#define ARM_EOR_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_EOR_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_EOR_REG_IMM8(p, rd, rn, imm8) \ + ARM_EOR_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) +#define ARM_EORS_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_EORS_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_EORS_REG_IMM8(p, rd, rn, imm8) \ + ARM_EORS_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _EOR_REG_IMM8_COND(rd, rn, imm8, cond) \ + _EOR_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _EOR_REG_IMM8(rd, rn, imm8) \ + _EOR_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#define _EORS_REG_IMM8_COND(rd, rn, imm8, cond) \ + _EORS_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _EORS_REG_IMM8(rd, rn, imm8) \ + _EORS_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#endif + + +/* Rd := Rn EOR Rm */ +#define ARM_EOR_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_REG_REG_COND(p, ARMOP_EOR, rd, rn, rm, cond) +#define ARM_EOR_REG_REG(p, rd, rn, rm) \ + ARM_EOR_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) +#define ARM_EORS_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_EOR, rd, rn, rm, cond) +#define ARM_EORS_REG_REG(p, rd, rn, rm) \ + ARM_EORS_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _EOR_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_REG_REG_COND(ARMOP_EOR, rd, rn, rm, cond) +#define _EOR_REG_REG(rd, rn, rm) \ + _EOR_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#define _EORS_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_EOR, rd, rn, rm, cond) +#define _EORS_REG_REG(rd, rn, rm) \ + _EORS_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#endif + + +/* Rd := Rn EOR (Rm imm_shift) */ +#define ARM_EOR_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_EOR, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_EOR_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_EOR_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define ARM_EORS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_EOR, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_EORS_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_EORS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _EOR_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_EOR, rd, rn, rm, shift_type, imm_shift, cond) +#define _EOR_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _EOR_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define _EORS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_EOR, rd, rn, rm, shift_type, imm_shift, cond) +#define _EORS_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _EORS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + +/* Rd := Rn EOR (Rm Rs) */ +#define ARM_EOR_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_EOR, rd, rn, rm, shift_t, rs, cond) +#define ARM_EOR_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_EOR_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define ARM_EORS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_EOR, rd, rn, rm, shift_t, rs, cond) +#define ARM_EORS_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_EORS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _EOR_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_EOR, rd, rn, rm, shift_t, rs, cond) +#define _EOR_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _EOR_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define _EORS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_EOR, rd, rn, rm, shift_t, rs, cond) +#define _EORS_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _EORS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#endif + + +/* -- SUB -- */ + +/* Rd := Rn SUB (imm8 ROR rot) ; rot is power of 2 */ +#define ARM_SUB_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_SUB, rd, rn, imm8, rot, cond) +#define ARM_SUB_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_SUB_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) +#define ARM_SUBS_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_SUB, rd, rn, imm8, rot, cond) +#define ARM_SUBS_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_SUBS_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _SUB_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_SUB, rd, rn, imm8, rot, cond) +#define _SUB_REG_IMM(rd, rn, imm8, rot) \ + _SUB_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#define _SUBS_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_SUB, rd, rn, imm8, rot, cond) +#define _SUBS_REG_IMM(rd, rn, imm8, rot) \ + _SUBS_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#endif + + +/* Rd := Rn SUB imm8 */ +#define ARM_SUB_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_SUB_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_SUB_REG_IMM8(p, rd, rn, imm8) \ + ARM_SUB_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) +#define ARM_SUBS_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_SUBS_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_SUBS_REG_IMM8(p, rd, rn, imm8) \ + ARM_SUBS_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _SUB_REG_IMM8_COND(rd, rn, imm8, cond) \ + _SUB_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _SUB_REG_IMM8(rd, rn, imm8) \ + _SUB_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#define _SUBS_REG_IMM8_COND(rd, rn, imm8, cond) \ + _SUBS_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _SUBS_REG_IMM8(rd, rn, imm8) \ + _SUBS_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#endif + + +/* Rd := Rn SUB Rm */ +#define ARM_SUB_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_REG_REG_COND(p, ARMOP_SUB, rd, rn, rm, cond) +#define ARM_SUB_REG_REG(p, rd, rn, rm) \ + ARM_SUB_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) +#define ARM_SUBS_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_SUB, rd, rn, rm, cond) +#define ARM_SUBS_REG_REG(p, rd, rn, rm) \ + ARM_SUBS_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _SUB_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_REG_REG_COND(ARMOP_SUB, rd, rn, rm, cond) +#define _SUB_REG_REG(rd, rn, rm) \ + _SUB_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#define _SUBS_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_SUB, rd, rn, rm, cond) +#define _SUBS_REG_REG(rd, rn, rm) \ + _SUBS_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#endif + + +/* Rd := Rn SUB (Rm imm_shift) */ +#define ARM_SUB_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_SUB, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_SUB_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_SUB_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define ARM_SUBS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_SUB, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_SUBS_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_SUBS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _SUB_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_SUB, rd, rn, rm, shift_type, imm_shift, cond) +#define _SUB_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _SUB_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define _SUBS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_SUB, rd, rn, rm, shift_type, imm_shift, cond) +#define _SUBS_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _SUBS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + +/* Rd := Rn SUB (Rm Rs) */ +#define ARM_SUB_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_SUB, rd, rn, rm, shift_t, rs, cond) +#define ARM_SUB_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_SUB_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define ARM_SUBS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_SUB, rd, rn, rm, shift_t, rs, cond) +#define ARM_SUBS_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_SUBS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _SUB_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_SUB, rd, rn, rm, shift_t, rs, cond) +#define _SUB_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _SUB_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define _SUBS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_SUB, rd, rn, rm, shift_t, rs, cond) +#define _SUBS_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _SUBS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#endif + + +/* -- RSB -- */ + +/* Rd := Rn RSB (imm8 ROR rot) ; rot is power of 2 */ +#define ARM_RSB_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_RSB, rd, rn, imm8, rot, cond) +#define ARM_RSB_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_RSB_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) +#define ARM_RSBS_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_RSB, rd, rn, imm8, rot, cond) +#define ARM_RSBS_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_RSBS_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _RSB_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_RSB, rd, rn, imm8, rot, cond) +#define _RSB_REG_IMM(rd, rn, imm8, rot) \ + _RSB_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#define _RSBS_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_RSB, rd, rn, imm8, rot, cond) +#define _RSBS_REG_IMM(rd, rn, imm8, rot) \ + _RSBS_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#endif + + +/* Rd := Rn RSB imm8 */ +#define ARM_RSB_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_RSB_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_RSB_REG_IMM8(p, rd, rn, imm8) \ + ARM_RSB_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) +#define ARM_RSBS_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_RSBS_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_RSBS_REG_IMM8(p, rd, rn, imm8) \ + ARM_RSBS_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _RSB_REG_IMM8_COND(rd, rn, imm8, cond) \ + _RSB_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _RSB_REG_IMM8(rd, rn, imm8) \ + _RSB_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#define _RSBS_REG_IMM8_COND(rd, rn, imm8, cond) \ + _RSBS_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _RSBS_REG_IMM8(rd, rn, imm8) \ + _RSBS_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#endif + + +/* Rd := Rn RSB Rm */ +#define ARM_RSB_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_REG_REG_COND(p, ARMOP_RSB, rd, rn, rm, cond) +#define ARM_RSB_REG_REG(p, rd, rn, rm) \ + ARM_RSB_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) +#define ARM_RSBS_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_RSB, rd, rn, rm, cond) +#define ARM_RSBS_REG_REG(p, rd, rn, rm) \ + ARM_RSBS_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _RSB_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_REG_REG_COND(ARMOP_RSB, rd, rn, rm, cond) +#define _RSB_REG_REG(rd, rn, rm) \ + _RSB_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#define _RSBS_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_RSB, rd, rn, rm, cond) +#define _RSBS_REG_REG(rd, rn, rm) \ + _RSBS_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#endif + + +/* Rd := Rn RSB (Rm imm_shift) */ +#define ARM_RSB_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_RSB, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_RSB_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_RSB_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define ARM_RSBS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_RSB, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_RSBS_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_RSBS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _RSB_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_RSB, rd, rn, rm, shift_type, imm_shift, cond) +#define _RSB_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _RSB_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define _RSBS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_RSB, rd, rn, rm, shift_type, imm_shift, cond) +#define _RSBS_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _RSBS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + +/* Rd := Rn RSB (Rm Rs) */ +#define ARM_RSB_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_RSB, rd, rn, rm, shift_t, rs, cond) +#define ARM_RSB_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_RSB_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define ARM_RSBS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_RSB, rd, rn, rm, shift_t, rs, cond) +#define ARM_RSBS_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_RSBS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _RSB_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_RSB, rd, rn, rm, shift_t, rs, cond) +#define _RSB_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _RSB_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define _RSBS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_RSB, rd, rn, rm, shift_t, rs, cond) +#define _RSBS_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _RSBS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#endif + + +/* -- ADD -- */ + +/* Rd := Rn ADD (imm8 ROR rot) ; rot is power of 2 */ +#define ARM_ADD_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_ADD, rd, rn, imm8, rot, cond) +#define ARM_ADD_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_ADD_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) +#define ARM_ADDS_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_ADD, rd, rn, imm8, rot, cond) +#define ARM_ADDS_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_ADDS_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _ADD_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_ADD, rd, rn, imm8, rot, cond) +#define _ADD_REG_IMM(rd, rn, imm8, rot) \ + _ADD_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#define _ADDS_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_ADD, rd, rn, imm8, rot, cond) +#define _ADDS_REG_IMM(rd, rn, imm8, rot) \ + _ADDS_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#endif + + +/* Rd := Rn ADD imm8 */ +#define ARM_ADD_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_ADD_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_ADD_REG_IMM8(p, rd, rn, imm8) \ + ARM_ADD_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) +#define ARM_ADDS_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_ADDS_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_ADDS_REG_IMM8(p, rd, rn, imm8) \ + ARM_ADDS_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _ADD_REG_IMM8_COND(rd, rn, imm8, cond) \ + _ADD_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _ADD_REG_IMM8(rd, rn, imm8) \ + _ADD_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#define _ADDS_REG_IMM8_COND(rd, rn, imm8, cond) \ + _ADDS_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _ADDS_REG_IMM8(rd, rn, imm8) \ + _ADDS_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#endif + + +/* Rd := Rn ADD Rm */ +#define ARM_ADD_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_REG_REG_COND(p, ARMOP_ADD, rd, rn, rm, cond) +#define ARM_ADD_REG_REG(p, rd, rn, rm) \ + ARM_ADD_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) +#define ARM_ADDS_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_ADD, rd, rn, rm, cond) +#define ARM_ADDS_REG_REG(p, rd, rn, rm) \ + ARM_ADDS_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _ADD_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_REG_REG_COND(ARMOP_ADD, rd, rn, rm, cond) +#define _ADD_REG_REG(rd, rn, rm) \ + _ADD_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#define _ADDS_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_ADD, rd, rn, rm, cond) +#define _ADDS_REG_REG(rd, rn, rm) \ + _ADDS_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#endif + + +/* Rd := Rn ADD (Rm imm_shift) */ +#define ARM_ADD_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_ADD, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_ADD_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_ADD_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define ARM_ADDS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_ADD, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_ADDS_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_ADDS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _ADD_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_ADD, rd, rn, rm, shift_type, imm_shift, cond) +#define _ADD_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _ADD_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define _ADDS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_ADD, rd, rn, rm, shift_type, imm_shift, cond) +#define _ADDS_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _ADDS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + +/* Rd := Rn ADD (Rm Rs) */ +#define ARM_ADD_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_ADD, rd, rn, rm, shift_t, rs, cond) +#define ARM_ADD_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_ADD_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define ARM_ADDS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_ADD, rd, rn, rm, shift_t, rs, cond) +#define ARM_ADDS_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_ADDS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _ADD_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_ADD, rd, rn, rm, shift_t, rs, cond) +#define _ADD_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _ADD_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define _ADDS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_ADD, rd, rn, rm, shift_t, rs, cond) +#define _ADDS_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _ADDS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#endif + + +/* -- ADC -- */ + +/* Rd := Rn ADC (imm8 ROR rot) ; rot is power of 2 */ +#define ARM_ADC_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_ADC, rd, rn, imm8, rot, cond) +#define ARM_ADC_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_ADC_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) +#define ARM_ADCS_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_ADC, rd, rn, imm8, rot, cond) +#define ARM_ADCS_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_ADCS_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _ADC_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_ADC, rd, rn, imm8, rot, cond) +#define _ADC_REG_IMM(rd, rn, imm8, rot) \ + _ADC_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#define _ADCS_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_ADC, rd, rn, imm8, rot, cond) +#define _ADCS_REG_IMM(rd, rn, imm8, rot) \ + _ADCS_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#endif + + +/* Rd := Rn ADC imm8 */ +#define ARM_ADC_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_ADC_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_ADC_REG_IMM8(p, rd, rn, imm8) \ + ARM_ADC_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) +#define ARM_ADCS_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_ADCS_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_ADCS_REG_IMM8(p, rd, rn, imm8) \ + ARM_ADCS_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _ADC_REG_IMM8_COND(rd, rn, imm8, cond) \ + _ADC_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _ADC_REG_IMM8(rd, rn, imm8) \ + _ADC_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#define _ADCS_REG_IMM8_COND(rd, rn, imm8, cond) \ + _ADCS_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _ADCS_REG_IMM8(rd, rn, imm8) \ + _ADCS_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#endif + + +/* Rd := Rn ADC Rm */ +#define ARM_ADC_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_REG_REG_COND(p, ARMOP_ADC, rd, rn, rm, cond) +#define ARM_ADC_REG_REG(p, rd, rn, rm) \ + ARM_ADC_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) +#define ARM_ADCS_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_ADC, rd, rn, rm, cond) +#define ARM_ADCS_REG_REG(p, rd, rn, rm) \ + ARM_ADCS_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _ADC_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_REG_REG_COND(ARMOP_ADC, rd, rn, rm, cond) +#define _ADC_REG_REG(rd, rn, rm) \ + _ADC_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#define _ADCS_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_ADC, rd, rn, rm, cond) +#define _ADCS_REG_REG(rd, rn, rm) \ + _ADCS_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#endif + + +/* Rd := Rn ADC (Rm imm_shift) */ +#define ARM_ADC_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_ADC, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_ADC_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_ADC_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define ARM_ADCS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_ADC, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_ADCS_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_ADCS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _ADC_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_ADC, rd, rn, rm, shift_type, imm_shift, cond) +#define _ADC_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _ADC_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define _ADCS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_ADC, rd, rn, rm, shift_type, imm_shift, cond) +#define _ADCS_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _ADCS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + +/* Rd := Rn ADC (Rm Rs) */ +#define ARM_ADC_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_ADC, rd, rn, rm, shift_t, rs, cond) +#define ARM_ADC_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_ADC_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define ARM_ADCS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_ADC, rd, rn, rm, shift_t, rs, cond) +#define ARM_ADCS_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_ADCS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _ADC_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_ADC, rd, rn, rm, shift_t, rs, cond) +#define _ADC_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _ADC_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define _ADCS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_ADC, rd, rn, rm, shift_t, rs, cond) +#define _ADCS_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _ADCS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#endif + + +/* -- SBC -- */ + +/* Rd := Rn SBC (imm8 ROR rot) ; rot is power of 2 */ +#define ARM_SBC_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_SBC, rd, rn, imm8, rot, cond) +#define ARM_SBC_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_SBC_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) +#define ARM_SBCS_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_SBC, rd, rn, imm8, rot, cond) +#define ARM_SBCS_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_SBCS_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _SBC_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_SBC, rd, rn, imm8, rot, cond) +#define _SBC_REG_IMM(rd, rn, imm8, rot) \ + _SBC_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#define _SBCS_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_SBC, rd, rn, imm8, rot, cond) +#define _SBCS_REG_IMM(rd, rn, imm8, rot) \ + _SBCS_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#endif + + +/* Rd := Rn SBC imm8 */ +#define ARM_SBC_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_SBC_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_SBC_REG_IMM8(p, rd, rn, imm8) \ + ARM_SBC_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) +#define ARM_SBCS_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_SBCS_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_SBCS_REG_IMM8(p, rd, rn, imm8) \ + ARM_SBCS_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _SBC_REG_IMM8_COND(rd, rn, imm8, cond) \ + _SBC_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _SBC_REG_IMM8(rd, rn, imm8) \ + _SBC_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#define _SBCS_REG_IMM8_COND(rd, rn, imm8, cond) \ + _SBCS_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _SBCS_REG_IMM8(rd, rn, imm8) \ + _SBCS_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#endif + + +/* Rd := Rn SBC Rm */ +#define ARM_SBC_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_REG_REG_COND(p, ARMOP_SBC, rd, rn, rm, cond) +#define ARM_SBC_REG_REG(p, rd, rn, rm) \ + ARM_SBC_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) +#define ARM_SBCS_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_SBC, rd, rn, rm, cond) +#define ARM_SBCS_REG_REG(p, rd, rn, rm) \ + ARM_SBCS_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _SBC_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_REG_REG_COND(ARMOP_SBC, rd, rn, rm, cond) +#define _SBC_REG_REG(rd, rn, rm) \ + _SBC_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#define _SBCS_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_SBC, rd, rn, rm, cond) +#define _SBCS_REG_REG(rd, rn, rm) \ + _SBCS_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#endif + + +/* Rd := Rn SBC (Rm imm_shift) */ +#define ARM_SBC_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_SBC, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_SBC_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_SBC_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define ARM_SBCS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_SBC, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_SBCS_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_SBCS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _SBC_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_SBC, rd, rn, rm, shift_type, imm_shift, cond) +#define _SBC_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _SBC_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define _SBCS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_SBC, rd, rn, rm, shift_type, imm_shift, cond) +#define _SBCS_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _SBCS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + +/* Rd := Rn SBC (Rm Rs) */ +#define ARM_SBC_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_SBC, rd, rn, rm, shift_t, rs, cond) +#define ARM_SBC_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_SBC_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define ARM_SBCS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_SBC, rd, rn, rm, shift_t, rs, cond) +#define ARM_SBCS_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_SBCS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _SBC_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_SBC, rd, rn, rm, shift_t, rs, cond) +#define _SBC_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _SBC_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define _SBCS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_SBC, rd, rn, rm, shift_t, rs, cond) +#define _SBCS_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _SBCS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#endif + + +/* -- RSC -- */ + +/* Rd := Rn RSC (imm8 ROR rot) ; rot is power of 2 */ +#define ARM_RSC_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_RSC, rd, rn, imm8, rot, cond) +#define ARM_RSC_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_RSC_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) +#define ARM_RSCS_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_RSC, rd, rn, imm8, rot, cond) +#define ARM_RSCS_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_RSCS_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _RSC_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_RSC, rd, rn, imm8, rot, cond) +#define _RSC_REG_IMM(rd, rn, imm8, rot) \ + _RSC_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#define _RSCS_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_RSC, rd, rn, imm8, rot, cond) +#define _RSCS_REG_IMM(rd, rn, imm8, rot) \ + _RSCS_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#endif + + +/* Rd := Rn RSC imm8 */ +#define ARM_RSC_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_RSC_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_RSC_REG_IMM8(p, rd, rn, imm8) \ + ARM_RSC_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) +#define ARM_RSCS_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_RSCS_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_RSCS_REG_IMM8(p, rd, rn, imm8) \ + ARM_RSCS_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _RSC_REG_IMM8_COND(rd, rn, imm8, cond) \ + _RSC_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _RSC_REG_IMM8(rd, rn, imm8) \ + _RSC_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#define _RSCS_REG_IMM8_COND(rd, rn, imm8, cond) \ + _RSCS_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _RSCS_REG_IMM8(rd, rn, imm8) \ + _RSCS_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#endif + + +/* Rd := Rn RSC Rm */ +#define ARM_RSC_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_REG_REG_COND(p, ARMOP_RSC, rd, rn, rm, cond) +#define ARM_RSC_REG_REG(p, rd, rn, rm) \ + ARM_RSC_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) +#define ARM_RSCS_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_RSC, rd, rn, rm, cond) +#define ARM_RSCS_REG_REG(p, rd, rn, rm) \ + ARM_RSCS_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _RSC_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_REG_REG_COND(ARMOP_RSC, rd, rn, rm, cond) +#define _RSC_REG_REG(rd, rn, rm) \ + _RSC_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#define _RSCS_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_RSC, rd, rn, rm, cond) +#define _RSCS_REG_REG(rd, rn, rm) \ + _RSCS_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#endif + + +/* Rd := Rn RSC (Rm imm_shift) */ +#define ARM_RSC_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_RSC, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_RSC_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_RSC_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define ARM_RSCS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_RSC, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_RSCS_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_RSCS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _RSC_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_RSC, rd, rn, rm, shift_type, imm_shift, cond) +#define _RSC_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _RSC_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define _RSCS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_RSC, rd, rn, rm, shift_type, imm_shift, cond) +#define _RSCS_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _RSCS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + +/* Rd := Rn RSC (Rm Rs) */ +#define ARM_RSC_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_RSC, rd, rn, rm, shift_t, rs, cond) +#define ARM_RSC_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_RSC_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define ARM_RSCS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_RSC, rd, rn, rm, shift_t, rs, cond) +#define ARM_RSCS_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_RSCS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _RSC_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_RSC, rd, rn, rm, shift_t, rs, cond) +#define _RSC_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _RSC_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define _RSCS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_RSC, rd, rn, rm, shift_t, rs, cond) +#define _RSCS_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _RSCS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#endif + + +/* -- ORR -- */ + +/* Rd := Rn ORR (imm8 ROR rot) ; rot is power of 2 */ +#define ARM_ORR_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_ORR, rd, rn, imm8, rot, cond) +#define ARM_ORR_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_ORR_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) +#define ARM_ORRS_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_ORR, rd, rn, imm8, rot, cond) +#define ARM_ORRS_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_ORRS_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _ORR_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_ORR, rd, rn, imm8, rot, cond) +#define _ORR_REG_IMM(rd, rn, imm8, rot) \ + _ORR_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#define _ORRS_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_ORR, rd, rn, imm8, rot, cond) +#define _ORRS_REG_IMM(rd, rn, imm8, rot) \ + _ORRS_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#endif + + +/* Rd := Rn ORR imm8 */ +#define ARM_ORR_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_ORR_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_ORR_REG_IMM8(p, rd, rn, imm8) \ + ARM_ORR_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) +#define ARM_ORRS_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_ORRS_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_ORRS_REG_IMM8(p, rd, rn, imm8) \ + ARM_ORRS_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _ORR_REG_IMM8_COND(rd, rn, imm8, cond) \ + _ORR_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _ORR_REG_IMM8(rd, rn, imm8) \ + _ORR_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#define _ORRS_REG_IMM8_COND(rd, rn, imm8, cond) \ + _ORRS_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _ORRS_REG_IMM8(rd, rn, imm8) \ + _ORRS_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#endif + + +/* Rd := Rn ORR Rm */ +#define ARM_ORR_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_REG_REG_COND(p, ARMOP_ORR, rd, rn, rm, cond) +#define ARM_ORR_REG_REG(p, rd, rn, rm) \ + ARM_ORR_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) +#define ARM_ORRS_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_ORR, rd, rn, rm, cond) +#define ARM_ORRS_REG_REG(p, rd, rn, rm) \ + ARM_ORRS_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _ORR_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_REG_REG_COND(ARMOP_ORR, rd, rn, rm, cond) +#define _ORR_REG_REG(rd, rn, rm) \ + _ORR_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#define _ORRS_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_ORR, rd, rn, rm, cond) +#define _ORRS_REG_REG(rd, rn, rm) \ + _ORRS_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#endif + + +/* Rd := Rn ORR (Rm imm_shift) */ +#define ARM_ORR_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_ORR, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_ORR_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_ORR_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define ARM_ORRS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_ORR, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_ORRS_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_ORRS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _ORR_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_ORR, rd, rn, rm, shift_type, imm_shift, cond) +#define _ORR_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _ORR_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define _ORRS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_ORR, rd, rn, rm, shift_type, imm_shift, cond) +#define _ORRS_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _ORRS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + +/* Rd := Rn ORR (Rm Rs) */ +#define ARM_ORR_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_ORR, rd, rn, rm, shift_t, rs, cond) +#define ARM_ORR_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_ORR_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define ARM_ORRS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_ORR, rd, rn, rm, shift_t, rs, cond) +#define ARM_ORRS_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_ORRS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _ORR_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_ORR, rd, rn, rm, shift_t, rs, cond) +#define _ORR_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _ORR_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define _ORRS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_ORR, rd, rn, rm, shift_t, rs, cond) +#define _ORRS_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _ORRS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#endif + + +/* -- BIC -- */ + +/* Rd := Rn BIC (imm8 ROR rot) ; rot is power of 2 */ +#define ARM_BIC_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_BIC, rd, rn, imm8, rot, cond) +#define ARM_BIC_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_BIC_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) +#define ARM_BICS_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_BIC, rd, rn, imm8, rot, cond) +#define ARM_BICS_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_BICS_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _BIC_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_BIC, rd, rn, imm8, rot, cond) +#define _BIC_REG_IMM(rd, rn, imm8, rot) \ + _BIC_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#define _BICS_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_BIC, rd, rn, imm8, rot, cond) +#define _BICS_REG_IMM(rd, rn, imm8, rot) \ + _BICS_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#endif + + +/* Rd := Rn BIC imm8 */ +#define ARM_BIC_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_BIC_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_BIC_REG_IMM8(p, rd, rn, imm8) \ + ARM_BIC_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) +#define ARM_BICS_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_BICS_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_BICS_REG_IMM8(p, rd, rn, imm8) \ + ARM_BICS_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _BIC_REG_IMM8_COND(rd, rn, imm8, cond) \ + _BIC_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _BIC_REG_IMM8(rd, rn, imm8) \ + _BIC_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#define _BICS_REG_IMM8_COND(rd, rn, imm8, cond) \ + _BICS_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _BICS_REG_IMM8(rd, rn, imm8) \ + _BICS_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#endif + + +/* Rd := Rn BIC Rm */ +#define ARM_BIC_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_REG_REG_COND(p, ARMOP_BIC, rd, rn, rm, cond) +#define ARM_BIC_REG_REG(p, rd, rn, rm) \ + ARM_BIC_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) +#define ARM_BICS_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_BIC, rd, rn, rm, cond) +#define ARM_BICS_REG_REG(p, rd, rn, rm) \ + ARM_BICS_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _BIC_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_REG_REG_COND(ARMOP_BIC, rd, rn, rm, cond) +#define _BIC_REG_REG(rd, rn, rm) \ + _BIC_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#define _BICS_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_BIC, rd, rn, rm, cond) +#define _BICS_REG_REG(rd, rn, rm) \ + _BICS_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#endif + + +/* Rd := Rn BIC (Rm imm_shift) */ +#define ARM_BIC_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_BIC, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_BIC_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_BIC_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define ARM_BICS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_BIC, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_BICS_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_BICS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _BIC_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_BIC, rd, rn, rm, shift_type, imm_shift, cond) +#define _BIC_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _BIC_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define _BICS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_BIC, rd, rn, rm, shift_type, imm_shift, cond) +#define _BICS_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _BICS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + +/* Rd := Rn BIC (Rm Rs) */ +#define ARM_BIC_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_BIC, rd, rn, rm, shift_t, rs, cond) +#define ARM_BIC_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_BIC_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define ARM_BICS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_BIC, rd, rn, rm, shift_t, rs, cond) +#define ARM_BICS_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_BICS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _BIC_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_BIC, rd, rn, rm, shift_t, rs, cond) +#define _BIC_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _BIC_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define _BICS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_BIC, rd, rn, rm, shift_t, rs, cond) +#define _BICS_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _BICS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#endif + + + + + + +/* DPIs, comparison */ + +/* PSR := TST Rn, (imm8 ROR 2*rot) */ +#define ARM_TST_REG_IMM_COND(p, rn, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_TST, 0, rn, imm8, rot, cond) +#define ARM_TST_REG_IMM(p, rn, imm8, rot) \ + ARM_TST_REG_IMM_COND(p, rn, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _TST_REG_IMM_COND(rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_TST, 0, rn, imm8, rot, cond) +#define _TST_REG_IMM(rn, imm8, rot) \ + _TST_REG_IMM_COND(rn, imm8, rot, ARMCOND_AL) +#endif + + +/* PSR := TST Rn, imm8 */ +#define ARM_TST_REG_IMM8_COND(p, rn, imm8, cond) \ + ARM_TST_REG_IMM_COND(p, rn, imm8, 0, cond) +#define ARM_TST_REG_IMM8(p, rn, imm8) \ + ARM_TST_REG_IMM8_COND(p, rn, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _TST_REG_IMM8_COND(rn, imm8, cond) \ + _TST_REG_IMM_COND(rn, imm8, 0, cond) +#define _TST_REG_IMM8(rn, imm8) \ + _TST_REG_IMM8_COND(rn, imm8, ARMCOND_AL) +#endif + + +/* PSR := TST Rn, Rm */ +#define ARM_TST_REG_REG_COND(p, rn, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_TST, 0, rn, rm, cond) +#define ARM_TST_REG_REG(p, rn, rm) \ + ARM_TST_REG_REG_COND(p, rn, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _TST_REG_REG_COND(rn, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_TST, 0, rn, rm, cond) +#define _TST_REG_REG(rn, rm) \ + _TST_REG_REG_COND(rn, rm, ARMCOND_AL) +#endif + + +/* PSR := TST Rn, (Rm imm8) */ +#define ARM_TST_REG_IMMSHIFT_COND(p, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_TST, 0, rn, rm, shift_type, imm_shift, cond) +#define ARM_TST_REG_IMMSHIFT(p, rn, rm, shift_type, imm_shift) \ + ARM_TST_REG_IMMSHIFT_COND(p, rn, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _TST_REG_IMMSHIFT_COND(rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_TST, 0, rn, rm, shift_type, imm_shift, cond) +#define _TST_REG_IMMSHIFT(rn, rm, shift_type, imm_shift) \ + _TST_REG_IMMSHIFT_COND(rn, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + +/* PSR := TEQ Rn, (imm8 ROR 2*rot) */ +#define ARM_TEQ_REG_IMM_COND(p, rn, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_TEQ, 0, rn, imm8, rot, cond) +#define ARM_TEQ_REG_IMM(p, rn, imm8, rot) \ + ARM_TEQ_REG_IMM_COND(p, rn, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _TEQ_REG_IMM_COND(rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_TEQ, 0, rn, imm8, rot, cond) +#define _TEQ_REG_IMM(rn, imm8, rot) \ + _TEQ_REG_IMM_COND(rn, imm8, rot, ARMCOND_AL) +#endif + + +/* PSR := TEQ Rn, imm8 */ +#define ARM_TEQ_REG_IMM8_COND(p, rn, imm8, cond) \ + ARM_TEQ_REG_IMM_COND(p, rn, imm8, 0, cond) +#define ARM_TEQ_REG_IMM8(p, rn, imm8) \ + ARM_TEQ_REG_IMM8_COND(p, rn, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _TEQ_REG_IMM8_COND(rn, imm8, cond) \ + _TEQ_REG_IMM_COND(rn, imm8, 0, cond) +#define _TEQ_REG_IMM8(rn, imm8) \ + _TEQ_REG_IMM8_COND(rn, imm8, ARMCOND_AL) +#endif + + +/* PSR := TEQ Rn, Rm */ +#define ARM_TEQ_REG_REG_COND(p, rn, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_TEQ, 0, rn, rm, cond) +#define ARM_TEQ_REG_REG(p, rn, rm) \ + ARM_TEQ_REG_REG_COND(p, rn, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _TEQ_REG_REG_COND(rn, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_TEQ, 0, rn, rm, cond) +#define _TEQ_REG_REG(rn, rm) \ + _TEQ_REG_REG_COND(rn, rm, ARMCOND_AL) +#endif + + +/* PSR := TEQ Rn, (Rm imm8) */ +#define ARM_TEQ_REG_IMMSHIFT_COND(p, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_TEQ, 0, rn, rm, shift_type, imm_shift, cond) +#define ARM_TEQ_REG_IMMSHIFT(p, rn, rm, shift_type, imm_shift) \ + ARM_TEQ_REG_IMMSHIFT_COND(p, rn, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _TEQ_REG_IMMSHIFT_COND(rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_TEQ, 0, rn, rm, shift_type, imm_shift, cond) +#define _TEQ_REG_IMMSHIFT(rn, rm, shift_type, imm_shift) \ + _TEQ_REG_IMMSHIFT_COND(rn, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + +/* PSR := CMP Rn, (imm8 ROR 2*rot) */ +#define ARM_CMP_REG_IMM_COND(p, rn, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_CMP, 0, rn, imm8, rot, cond) +#define ARM_CMP_REG_IMM(p, rn, imm8, rot) \ + ARM_CMP_REG_IMM_COND(p, rn, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _CMP_REG_IMM_COND(rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_CMP, 0, rn, imm8, rot, cond) +#define _CMP_REG_IMM(rn, imm8, rot) \ + _CMP_REG_IMM_COND(rn, imm8, rot, ARMCOND_AL) +#endif + + +/* PSR := CMP Rn, imm8 */ +#define ARM_CMP_REG_IMM8_COND(p, rn, imm8, cond) \ + ARM_CMP_REG_IMM_COND(p, rn, imm8, 0, cond) +#define ARM_CMP_REG_IMM8(p, rn, imm8) \ + ARM_CMP_REG_IMM8_COND(p, rn, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _CMP_REG_IMM8_COND(rn, imm8, cond) \ + _CMP_REG_IMM_COND(rn, imm8, 0, cond) +#define _CMP_REG_IMM8(rn, imm8) \ + _CMP_REG_IMM8_COND(rn, imm8, ARMCOND_AL) +#endif + + +/* PSR := CMP Rn, Rm */ +#define ARM_CMP_REG_REG_COND(p, rn, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_CMP, 0, rn, rm, cond) +#define ARM_CMP_REG_REG(p, rn, rm) \ + ARM_CMP_REG_REG_COND(p, rn, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _CMP_REG_REG_COND(rn, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_CMP, 0, rn, rm, cond) +#define _CMP_REG_REG(rn, rm) \ + _CMP_REG_REG_COND(rn, rm, ARMCOND_AL) +#endif + + +/* PSR := CMP Rn, (Rm imm8) */ +#define ARM_CMP_REG_IMMSHIFT_COND(p, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_CMP, 0, rn, rm, shift_type, imm_shift, cond) +#define ARM_CMP_REG_IMMSHIFT(p, rn, rm, shift_type, imm_shift) \ + ARM_CMP_REG_IMMSHIFT_COND(p, rn, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _CMP_REG_IMMSHIFT_COND(rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_CMP, 0, rn, rm, shift_type, imm_shift, cond) +#define _CMP_REG_IMMSHIFT(rn, rm, shift_type, imm_shift) \ + _CMP_REG_IMMSHIFT_COND(rn, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + +/* PSR := CMN Rn, (imm8 ROR 2*rot) */ +#define ARM_CMN_REG_IMM_COND(p, rn, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_CMN, 0, rn, imm8, rot, cond) +#define ARM_CMN_REG_IMM(p, rn, imm8, rot) \ + ARM_CMN_REG_IMM_COND(p, rn, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _CMN_REG_IMM_COND(rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_CMN, 0, rn, imm8, rot, cond) +#define _CMN_REG_IMM(rn, imm8, rot) \ + _CMN_REG_IMM_COND(rn, imm8, rot, ARMCOND_AL) +#endif + + +/* PSR := CMN Rn, imm8 */ +#define ARM_CMN_REG_IMM8_COND(p, rn, imm8, cond) \ + ARM_CMN_REG_IMM_COND(p, rn, imm8, 0, cond) +#define ARM_CMN_REG_IMM8(p, rn, imm8) \ + ARM_CMN_REG_IMM8_COND(p, rn, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _CMN_REG_IMM8_COND(rn, imm8, cond) \ + _CMN_REG_IMM_COND(rn, imm8, 0, cond) +#define _CMN_REG_IMM8(rn, imm8) \ + _CMN_REG_IMM8_COND(rn, imm8, ARMCOND_AL) +#endif + + +/* PSR := CMN Rn, Rm */ +#define ARM_CMN_REG_REG_COND(p, rn, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_CMN, 0, rn, rm, cond) +#define ARM_CMN_REG_REG(p, rn, rm) \ + ARM_CMN_REG_REG_COND(p, rn, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _CMN_REG_REG_COND(rn, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_CMN, 0, rn, rm, cond) +#define _CMN_REG_REG(rn, rm) \ + _CMN_REG_REG_COND(rn, rm, ARMCOND_AL) +#endif + + +/* PSR := CMN Rn, (Rm imm8) */ +#define ARM_CMN_REG_IMMSHIFT_COND(p, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_CMN, 0, rn, rm, shift_type, imm_shift, cond) +#define ARM_CMN_REG_IMMSHIFT(p, rn, rm, shift_type, imm_shift) \ + ARM_CMN_REG_IMMSHIFT_COND(p, rn, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _CMN_REG_IMMSHIFT_COND(rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_CMN, 0, rn, rm, shift_type, imm_shift, cond) +#define _CMN_REG_IMMSHIFT(rn, rm, shift_type, imm_shift) \ + _CMN_REG_IMMSHIFT_COND(rn, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + + +/* end generated */ + diff --git a/unity-2019.4.24f1-mbe/mono/arch/arm/arm_vfpmacros.h b/unity-2019.4.24f1-mbe/mono/arch/arm/arm_vfpmacros.h new file mode 100644 index 000000000..f502645ae --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/arm/arm_vfpmacros.h @@ -0,0 +1,299 @@ +/* Macros for VFP ops, auto-generated from template */ + + +/* dyadic */ + +/* -- ADD -- */ + + +/* Fd := Fn ADD Fm */ +#define ARM_VFP_ADDD_COND(p, rd, rn, rm, cond) \ + ARM_EMIT((p), ARM_DEF_VFP_DYADIC(cond,ARM_VFP_COPROC_DOUBLE,ARM_VFP_ADD,rd,rn,rm)) +#define ARM_VFP_ADDD(p, rd, rn, rm) \ + ARM_VFP_ADDD_COND(p, rd, rn, rm, ARMCOND_AL) + +#define ARM_VFP_ADDS_COND(p, rd, rn, rm, cond) \ + ARM_EMIT((p), ARM_DEF_VFP_DYADIC(cond,ARM_VFP_COPROC_SINGLE,ARM_VFP_ADD,rd,rn,rm)) +#define ARM_VFP_ADDS(p, rd, rn, rm) \ + ARM_VFP_ADDS_COND(p, rd, rn, rm, ARMCOND_AL) + + +/* -- SUB -- */ + + +/* Fd := Fn SUB Fm */ +#define ARM_VFP_SUBD_COND(p, rd, rn, rm, cond) \ + ARM_EMIT((p), ARM_DEF_VFP_DYADIC(cond,ARM_VFP_COPROC_DOUBLE,ARM_VFP_SUB,rd,rn,rm)) +#define ARM_VFP_SUBD(p, rd, rn, rm) \ + ARM_VFP_SUBD_COND(p, rd, rn, rm, ARMCOND_AL) + +#define ARM_VFP_SUBS_COND(p, rd, rn, rm, cond) \ + ARM_EMIT((p), ARM_DEF_VFP_DYADIC(cond,ARM_VFP_COPROC_SINGLE,ARM_VFP_SUB,rd,rn,rm)) +#define ARM_VFP_SUBS(p, rd, rn, rm) \ + ARM_VFP_SUBS_COND(p, rd, rn, rm, ARMCOND_AL) + + +/* -- MUL -- */ + + +/* Fd := Fn MUL Fm */ +#define ARM_VFP_MULD_COND(p, rd, rn, rm, cond) \ + ARM_EMIT((p), ARM_DEF_VFP_DYADIC(cond,ARM_VFP_COPROC_DOUBLE,ARM_VFP_MUL,rd,rn,rm)) +#define ARM_VFP_MULD(p, rd, rn, rm) \ + ARM_VFP_MULD_COND(p, rd, rn, rm, ARMCOND_AL) + +#define ARM_VFP_MULS_COND(p, rd, rn, rm, cond) \ + ARM_EMIT((p), ARM_DEF_VFP_DYADIC(cond,ARM_VFP_COPROC_SINGLE,ARM_VFP_MUL,rd,rn,rm)) +#define ARM_VFP_MULS(p, rd, rn, rm) \ + ARM_VFP_MULS_COND(p, rd, rn, rm, ARMCOND_AL) + + +/* -- NMUL -- */ + + +/* Fd := Fn NMUL Fm */ +#define ARM_VFP_NMULD_COND(p, rd, rn, rm, cond) \ + ARM_EMIT((p), ARM_DEF_VFP_DYADIC(cond,ARM_VFP_COPROC_DOUBLE,ARM_VFP_NMUL,rd,rn,rm)) +#define ARM_VFP_NMULD(p, rd, rn, rm) \ + ARM_VFP_NMULD_COND(p, rd, rn, rm, ARMCOND_AL) + +#define ARM_VFP_NMULS_COND(p, rd, rn, rm, cond) \ + ARM_EMIT((p), ARM_DEF_VFP_DYADIC(cond,ARM_VFP_COPROC_SINGLE,ARM_VFP_NMUL,rd,rn,rm)) +#define ARM_VFP_NMULS(p, rd, rn, rm) \ + ARM_VFP_NMULS_COND(p, rd, rn, rm, ARMCOND_AL) + + +/* -- DIV -- */ + + +/* Fd := Fn DIV Fm */ +#define ARM_VFP_DIVD_COND(p, rd, rn, rm, cond) \ + ARM_EMIT((p), ARM_DEF_VFP_DYADIC(cond,ARM_VFP_COPROC_DOUBLE,ARM_VFP_DIV,rd,rn,rm)) +#define ARM_VFP_DIVD(p, rd, rn, rm) \ + ARM_VFP_DIVD_COND(p, rd, rn, rm, ARMCOND_AL) + +#define ARM_VFP_DIVS_COND(p, rd, rn, rm, cond) \ + ARM_EMIT((p), ARM_DEF_VFP_DYADIC(cond,ARM_VFP_COPROC_SINGLE,ARM_VFP_DIV,rd,rn,rm)) +#define ARM_VFP_DIVS(p, rd, rn, rm) \ + ARM_VFP_DIVS_COND(p, rd, rn, rm, ARMCOND_AL) + + + +/* monadic */ + +/* -- CPY -- */ + + +/* Fd := CPY Fm */ + +#define ARM_CPYD_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_DOUBLE,ARM_VFP_CPY,(dreg),(sreg))) +#define ARM_CPYD(p,dreg,sreg) ARM_CPYD_COND(p,dreg,sreg,ARMCOND_AL) + +#define ARM_CPYS_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_SINGLE,ARM_VFP_CPY,(dreg),(sreg))) +#define ARM_CPYS(p,dreg,sreg) ARM_CPYS_COND(p,dreg,sreg,ARMCOND_AL) + + +/* -- ABS -- */ + + +/* Fd := ABS Fm */ + +#define ARM_ABSD_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_DOUBLE,ARM_VFP_ABS,(dreg),(sreg))) +#define ARM_ABSD(p,dreg,sreg) ARM_ABSD_COND(p,dreg,sreg,ARMCOND_AL) + +#define ARM_ABSS_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_SINGLE,ARM_VFP_ABS,(dreg),(sreg))) +#define ARM_ABSS(p,dreg,sreg) ARM_ABSS_COND(p,dreg,sreg,ARMCOND_AL) + + +/* -- NEG -- */ + + +/* Fd := NEG Fm */ + +#define ARM_NEGD_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_DOUBLE,ARM_VFP_NEG,(dreg),(sreg))) +#define ARM_NEGD(p,dreg,sreg) ARM_NEGD_COND(p,dreg,sreg,ARMCOND_AL) + +#define ARM_NEGS_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_SINGLE,ARM_VFP_NEG,(dreg),(sreg))) +#define ARM_NEGS(p,dreg,sreg) ARM_NEGS_COND(p,dreg,sreg,ARMCOND_AL) + + +/* -- SQRT -- */ + + +/* Fd := SQRT Fm */ + +#define ARM_SQRTD_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_DOUBLE,ARM_VFP_SQRT,(dreg),(sreg))) +#define ARM_SQRTD(p,dreg,sreg) ARM_SQRTD_COND(p,dreg,sreg,ARMCOND_AL) + +#define ARM_SQRTS_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_SINGLE,ARM_VFP_SQRT,(dreg),(sreg))) +#define ARM_SQRTS(p,dreg,sreg) ARM_SQRTS_COND(p,dreg,sreg,ARMCOND_AL) + + +/* -- CMP -- */ + + +/* Fd := CMP Fm */ + +#define ARM_CMPD_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_DOUBLE,ARM_VFP_CMP,(dreg),(sreg))) +#define ARM_CMPD(p,dreg,sreg) ARM_CMPD_COND(p,dreg,sreg,ARMCOND_AL) + +#define ARM_CMPS_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_SINGLE,ARM_VFP_CMP,(dreg),(sreg))) +#define ARM_CMPS(p,dreg,sreg) ARM_CMPS_COND(p,dreg,sreg,ARMCOND_AL) + + +/* -- CMPE -- */ + + +/* Fd := CMPE Fm */ + +#define ARM_CMPED_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_DOUBLE,ARM_VFP_CMPE,(dreg),(sreg))) +#define ARM_CMPED(p,dreg,sreg) ARM_CMPED_COND(p,dreg,sreg,ARMCOND_AL) + +#define ARM_CMPES_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_SINGLE,ARM_VFP_CMPE,(dreg),(sreg))) +#define ARM_CMPES(p,dreg,sreg) ARM_CMPES_COND(p,dreg,sreg,ARMCOND_AL) + + +/* -- CMPZ -- */ + + +/* Fd := CMPZ Fm */ + +#define ARM_CMPZD_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_DOUBLE,ARM_VFP_CMPZ,(dreg),(sreg))) +#define ARM_CMPZD(p,dreg,sreg) ARM_CMPZD_COND(p,dreg,sreg,ARMCOND_AL) + +#define ARM_CMPZS_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_SINGLE,ARM_VFP_CMPZ,(dreg),(sreg))) +#define ARM_CMPZS(p,dreg,sreg) ARM_CMPZS_COND(p,dreg,sreg,ARMCOND_AL) + + +/* -- CMPEZ -- */ + + +/* Fd := CMPEZ Fm */ + +#define ARM_CMPEZD_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_DOUBLE,ARM_VFP_CMPEZ,(dreg),(sreg))) +#define ARM_CMPEZD(p,dreg,sreg) ARM_CMPEZD_COND(p,dreg,sreg,ARMCOND_AL) + +#define ARM_CMPEZS_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_SINGLE,ARM_VFP_CMPEZ,(dreg),(sreg))) +#define ARM_CMPEZS(p,dreg,sreg) ARM_CMPEZS_COND(p,dreg,sreg,ARMCOND_AL) + + +/* -- CVT -- */ + + +/* Fd := CVT Fm */ + +#define ARM_CVTD_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_DOUBLE,ARM_VFP_CVT,(dreg),(sreg))) +#define ARM_CVTD(p,dreg,sreg) ARM_CVTD_COND(p,dreg,sreg,ARMCOND_AL) + +#define ARM_CVTS_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_SINGLE,ARM_VFP_CVT,(dreg),(sreg))) +#define ARM_CVTS(p,dreg,sreg) ARM_CVTS_COND(p,dreg,sreg,ARMCOND_AL) + + +/* -- UITO -- */ + + +/* Fd := UITO Fm */ + +#define ARM_UITOD_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_DOUBLE,ARM_VFP_UITO,(dreg),(sreg))) +#define ARM_UITOD(p,dreg,sreg) ARM_UITOD_COND(p,dreg,sreg,ARMCOND_AL) + +#define ARM_UITOS_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_SINGLE,ARM_VFP_UITO,(dreg),(sreg))) +#define ARM_UITOS(p,dreg,sreg) ARM_UITOS_COND(p,dreg,sreg,ARMCOND_AL) + + +/* -- SITO -- */ + + +/* Fd := SITO Fm */ + +#define ARM_SITOD_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_DOUBLE,ARM_VFP_SITO,(dreg),(sreg))) +#define ARM_SITOD(p,dreg,sreg) ARM_SITOD_COND(p,dreg,sreg,ARMCOND_AL) + +#define ARM_SITOS_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_SINGLE,ARM_VFP_SITO,(dreg),(sreg))) +#define ARM_SITOS(p,dreg,sreg) ARM_SITOS_COND(p,dreg,sreg,ARMCOND_AL) + + +/* -- TOUI -- */ + + +/* Fd := TOUI Fm */ + +#define ARM_TOUID_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_DOUBLE,ARM_VFP_TOUI,(dreg),(sreg))) +#define ARM_TOUID(p,dreg,sreg) ARM_TOUID_COND(p,dreg,sreg,ARMCOND_AL) + +#define ARM_TOUIS_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_SINGLE,ARM_VFP_TOUI,(dreg),(sreg))) +#define ARM_TOUIS(p,dreg,sreg) ARM_TOUIS_COND(p,dreg,sreg,ARMCOND_AL) + + +/* -- TOSI -- */ + + +/* Fd := TOSI Fm */ + +#define ARM_TOSID_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_DOUBLE,ARM_VFP_TOSI,(dreg),(sreg))) +#define ARM_TOSID(p,dreg,sreg) ARM_TOSID_COND(p,dreg,sreg,ARMCOND_AL) + +#define ARM_TOSIS_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_SINGLE,ARM_VFP_TOSI,(dreg),(sreg))) +#define ARM_TOSIS(p,dreg,sreg) ARM_TOSIS_COND(p,dreg,sreg,ARMCOND_AL) + + +/* -- TOUIZ -- */ + + +/* Fd := TOUIZ Fm */ + +#define ARM_TOUIZD_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_DOUBLE,ARM_VFP_TOUIZ,(dreg),(sreg))) +#define ARM_TOUIZD(p,dreg,sreg) ARM_TOUIZD_COND(p,dreg,sreg,ARMCOND_AL) + +#define ARM_TOUIZS_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_SINGLE,ARM_VFP_TOUIZ,(dreg),(sreg))) +#define ARM_TOUIZS(p,dreg,sreg) ARM_TOUIZS_COND(p,dreg,sreg,ARMCOND_AL) + + +/* -- TOSIZ -- */ + + +/* Fd := TOSIZ Fm */ + +#define ARM_TOSIZD_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_DOUBLE,ARM_VFP_TOSIZ,(dreg),(sreg))) +#define ARM_TOSIZD(p,dreg,sreg) ARM_TOSIZD_COND(p,dreg,sreg,ARMCOND_AL) + +#define ARM_TOSIZS_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_SINGLE,ARM_VFP_TOSIZ,(dreg),(sreg))) +#define ARM_TOSIZS(p,dreg,sreg) ARM_TOSIZS_COND(p,dreg,sreg,ARMCOND_AL) + + + + + + +/* end generated */ + diff --git a/unity-2019.4.24f1-mbe/mono/arch/arm/cmp_macros.th b/unity-2019.4.24f1-mbe/mono/arch/arm/cmp_macros.th new file mode 100644 index 000000000..cb2639dec --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/arm/cmp_macros.th @@ -0,0 +1,56 @@ +/* PSR := Rn, (imm8 ROR 2*rot) */ +#define ARM__REG_IMM_COND(p, rn, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_, 0, rn, imm8, rot, cond) +#define ARM__REG_IMM(p, rn, imm8, rot) \ + ARM__REG_IMM_COND(p, rn, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define __REG_IMM_COND(rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_, 0, rn, imm8, rot, cond) +#define __REG_IMM(rn, imm8, rot) \ + __REG_IMM_COND(rn, imm8, rot, ARMCOND_AL) +#endif + + +/* PSR := Rn, imm8 */ +#define ARM__REG_IMM8_COND(p, rn, imm8, cond) \ + ARM__REG_IMM_COND(p, rn, imm8, 0, cond) +#define ARM__REG_IMM8(p, rn, imm8) \ + ARM__REG_IMM8_COND(p, rn, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define __REG_IMM8_COND(rn, imm8, cond) \ + __REG_IMM_COND(rn, imm8, 0, cond) +#define __REG_IMM8(rn, imm8) \ + __REG_IMM8_COND(rn, imm8, ARMCOND_AL) +#endif + + +/* PSR := Rn, Rm */ +#define ARM__REG_REG_COND(p, rn, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_, 0, rn, rm, cond) +#define ARM__REG_REG(p, rn, rm) \ + ARM__REG_REG_COND(p, rn, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define __REG_REG_COND(rn, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_, 0, rn, rm, cond) +#define __REG_REG(rn, rm) \ + __REG_REG_COND(rn, rm, ARMCOND_AL) +#endif + + +/* PSR := Rn, (Rm imm8) */ +#define ARM__REG_IMMSHIFT_COND(p, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_, 0, rn, rm, shift_type, imm_shift, cond) +#define ARM__REG_IMMSHIFT(p, rn, rm, shift_type, imm_shift) \ + ARM__REG_IMMSHIFT_COND(p, rn, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define __REG_IMMSHIFT_COND(rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_, 0, rn, rm, shift_type, imm_shift, cond) +#define __REG_IMMSHIFT(rn, rm, shift_type, imm_shift) \ + __REG_IMMSHIFT_COND(rn, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + diff --git a/unity-2019.4.24f1-mbe/mono/arch/arm/dpi_macros.th b/unity-2019.4.24f1-mbe/mono/arch/arm/dpi_macros.th new file mode 100644 index 000000000..be43d1fe7 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/arm/dpi_macros.th @@ -0,0 +1,112 @@ +/* -- -- */ + +/* Rd := Rn (imm8 ROR rot) ; rot is power of 2 */ +#define ARM__REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_, rd, rn, imm8, rot, cond) +#define ARM__REG_IMM(p, rd, rn, imm8, rot) \ + ARM__REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) +#define ARM_S_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_, rd, rn, imm8, rot, cond) +#define ARM_S_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_S_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define __REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_, rd, rn, imm8, rot, cond) +#define __REG_IMM(rd, rn, imm8, rot) \ + __REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#define _S_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_, rd, rn, imm8, rot, cond) +#define _S_REG_IMM(rd, rn, imm8, rot) \ + _S_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#endif + + +/* Rd := Rn imm8 */ +#define ARM__REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM__REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM__REG_IMM8(p, rd, rn, imm8) \ + ARM__REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) +#define ARM_S_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_S_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_S_REG_IMM8(p, rd, rn, imm8) \ + ARM_S_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define __REG_IMM8_COND(rd, rn, imm8, cond) \ + __REG_IMM_COND(rd, rn, imm8, 0, cond) +#define __REG_IMM8(rd, rn, imm8) \ + __REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#define _S_REG_IMM8_COND(rd, rn, imm8, cond) \ + _S_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _S_REG_IMM8(rd, rn, imm8) \ + _S_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#endif + + +/* Rd := Rn Rm */ +#define ARM__REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_REG_REG_COND(p, ARMOP_, rd, rn, rm, cond) +#define ARM__REG_REG(p, rd, rn, rm) \ + ARM__REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) +#define ARM_S_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_, rd, rn, rm, cond) +#define ARM_S_REG_REG(p, rd, rn, rm) \ + ARM_S_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define __REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_REG_REG_COND(ARMOP_, rd, rn, rm, cond) +#define __REG_REG(rd, rn, rm) \ + __REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#define _S_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_, rd, rn, rm, cond) +#define _S_REG_REG(rd, rn, rm) \ + _S_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#endif + + +/* Rd := Rn (Rm imm_shift) */ +#define ARM__REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM__REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM__REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define ARM_S_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_S_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_S_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define __REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_, rd, rn, rm, shift_type, imm_shift, cond) +#define __REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + __REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define _S_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_, rd, rn, rm, shift_type, imm_shift, cond) +#define _S_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _S_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + +/* Rd := Rn (Rm Rs) */ +#define ARM__REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_, rd, rn, rm, shift_t, rs, cond) +#define ARM__REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM__REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define ARM_S_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_, rd, rn, rm, shift_t, rs, cond) +#define ARM_S_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_S_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define __REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_, rd, rn, rm, shift_t, rs, cond) +#define __REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + __REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define _S_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_, rd, rn, rm, shift_t, rs, cond) +#define _S_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _S_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#endif + + diff --git a/unity-2019.4.24f1-mbe/mono/arch/arm/dpiops.sh b/unity-2019.4.24f1-mbe/mono/arch/arm/dpiops.sh new file mode 100644 index 000000000..d3b93ff52 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/arm/dpiops.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +OPCODES="AND EOR SUB RSB ADD ADC SBC RSC ORR BIC" +CMP_OPCODES="TST TEQ CMP CMN" +MOV_OPCODES="MOV MVN" + +# $1: opcode list +# $2: template +gen() { + for i in $1; do + sed "s//$i/g" $2.th + done +} + + + +echo -e "/* Macros for DPI ops, auto-generated from template */\n" + +echo -e "\n/* mov/mvn */\n" +gen "$MOV_OPCODES" mov_macros + +echo -e "\n/* DPIs, arithmetic and logical */\n" +gen "$OPCODES" dpi_macros + +echo -e "\n\n" + +echo -e "\n/* DPIs, comparison */\n" +gen "$CMP_OPCODES" cmp_macros + +echo -e "\n/* end generated */\n" diff --git a/unity-2019.4.24f1-mbe/mono/arch/arm/mov_macros.th b/unity-2019.4.24f1-mbe/mono/arch/arm/mov_macros.th new file mode 100644 index 000000000..6bac29003 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/arm/mov_macros.th @@ -0,0 +1,121 @@ +/* Rd := imm8 ROR rot */ +#define ARM__REG_IMM_COND(p, reg, imm8, rot, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_, reg, 0, imm8, rot, cond) +#define ARM__REG_IMM(p, reg, imm8, rot) \ + ARM__REG_IMM_COND(p, reg, imm8, rot, ARMCOND_AL) +/* S */ +#define ARM_S_REG_IMM_COND(p, reg, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_, reg, 0, imm8, rot, cond) +#define ARM_S_REG_IMM(p, reg, imm8, rot) \ + ARM_S_REG_IMM_COND(p, reg, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define __REG_IMM_COND(reg, imm8, rot, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_, reg, 0, imm8, rot, cond) +#define __REG_IMM(reg, imm8, rot) \ + __REG_IMM_COND(reg, imm8, rot, ARMCOND_AL) +/* S */ +#define _S_REG_IMM_COND(reg, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_, reg, 0, imm8, rot, cond) +#define _S_REG_IMM(reg, imm8, rot) \ + _S_REG_IMM_COND(reg, imm8, rot, ARMCOND_AL) +#endif + + +/* Rd := imm8 */ +#define ARM__REG_IMM8_COND(p, reg, imm8, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_, reg, 0, imm8, 0, cond) +#define ARM__REG_IMM8(p, reg, imm8) \ + ARM__REG_IMM8_COND(p, reg, imm8, ARMCOND_AL) +/* S */ +#define ARM_S_REG_IMM8_COND(p, reg, imm8, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_, reg, 0, imm8, 0, cond) +#define ARM_S_REG_IMM8(p, reg, imm8) \ + ARM_S_REG_IMM8_COND(p, reg, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define __REG_IMM8_COND(reg, imm8, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_, reg, 0, imm8, 0, cond) +#define __REG_IMM8(reg, imm8) \ + __REG_IMM8_COND(reg, imm8, ARMCOND_AL) +/* S */ +#define _S_REG_IMM8_COND(reg, imm8, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_, reg, 0, imm8, 0, cond) +#define _S_REG_IMM8(reg, imm8) \ + _S_REG_IMM8_COND(reg, imm8, ARMCOND_AL) +#endif + + +/* Rd := Rm */ +#define ARM__REG_REG_COND(p, rd, rm, cond) \ + ARM_DPIOP_REG_REG_COND(p, ARMOP_, rd, 0, rm, cond) +#define ARM__REG_REG(p, rd, rm) \ + ARM__REG_REG_COND(p, rd, rm, ARMCOND_AL) +/* S */ +#define ARM_S_REG_REG_COND(p, rd, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_, rd, 0, rm, cond) +#define ARM_S_REG_REG(p, rd, rm) \ + ARM_S_REG_REG_COND(p, rd, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define __REG_REG_COND(rd, rm, cond) \ + ARM_IASM_DPIOP_REG_REG_COND(ARMOP_, rd, 0, rm, cond) +#define __REG_REG(rd, rm) \ + __REG_REG_COND(rd, rm, ARMCOND_AL) +/* S */ +#define _S_REG_REG_COND(rd, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_, rd, 0, rm, cond) +#define _S_REG_REG(rd, rm) \ + _S_REG_REG_COND(rd, rm, ARMCOND_AL) +#endif + + +/* Rd := Rm imm_shift */ +#define ARM__REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_, rd, 0, rm, shift_type, imm_shift, cond) +#define ARM__REG_IMMSHIFT(p, rd, rm, shift_type, imm_shift) \ + ARM__REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, ARMCOND_AL) +/* S */ +#define ARM_S_REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_, rd, 0, rm, shift_type, imm_shift, cond) +#define ARM_S_REG_IMMSHIFT(p, rd, rm, shift_type, imm_shift) \ + ARM_S_REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define __REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_, rd, 0, rm, shift_type, imm_shift, cond) +#define __REG_IMMSHIFT(rd, rm, shift_type, imm_shift) \ + __REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, ARMCOND_AL) +/* S */ +#define _S_REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_, rd, 0, rm, shift_type, imm_shift, cond) +#define _S_REG_IMMSHIFT(rd, rm, shift_type, imm_shift) \ + _S_REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + + +/* Rd := (Rm Rs) */ +#define ARM__REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, cond) \ + ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_, rd, 0, rm, shift_type, rs, cond) +#define ARM__REG_REGSHIFT(p, rd, rm, shift_type, rs) \ + ARM__REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, ARMCOND_AL) +/* S */ +#define ARM_S_REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, cond) \ + ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_, rd, 0, rm, shift_type, rs, cond) +#define ARM_S_REG_REGSHIFT(p, rd, rm, shift_type, rs) \ + ARM_S_REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define __REG_REGSHIFT_COND(rd, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_, rd, 0, rm, shift_type, rs, cond) +#define __REG_REGSHIFT(rd, rm, shift_type, rs) \ + __REG_REGSHIFT_COND(rd, rm, shift_type, rs, ARMCOND_AL) +/* S */ +#define _S_REG_REGSHIFT_COND(rd, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_, rd, 0, rm, shift_type, rs, cond) +#define _S_REG_REGSHIFT(rd, rm, shift_type, rs) \ + _S_REG_REGSHIFT_COND(rd, rm, shift_type, rs, ARMCOND_AL) +#endif + + diff --git a/unity-2019.4.24f1-mbe/mono/arch/arm/vfp_macros.th b/unity-2019.4.24f1-mbe/mono/arch/arm/vfp_macros.th new file mode 100644 index 000000000..cca67dc8f --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/arm/vfp_macros.th @@ -0,0 +1,15 @@ +/* -- -- */ + + +/* Fd := Fn Fm */ +#define ARM_VFP_D_COND(p, rd, rn, rm, cond) \ + ARM_EMIT((p), ARM_DEF_VFP_DYADIC(cond,ARM_VFP_COPROC_DOUBLE,ARM_VFP_,rd,rn,rm)) +#define ARM_VFP_D(p, rd, rn, rm) \ + ARM_VFP_D_COND(p, rd, rn, rm, ARMCOND_AL) + +#define ARM_VFP_S_COND(p, rd, rn, rm, cond) \ + ARM_EMIT((p), ARM_DEF_VFP_DYADIC(cond,ARM_VFP_COPROC_SINGLE,ARM_VFP_,rd,rn,rm)) +#define ARM_VFP_S(p, rd, rn, rm) \ + ARM_VFP_S_COND(p, rd, rn, rm, ARMCOND_AL) + + diff --git a/unity-2019.4.24f1-mbe/mono/arch/arm/vfpm_macros.th b/unity-2019.4.24f1-mbe/mono/arch/arm/vfpm_macros.th new file mode 100644 index 000000000..25ad72158 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/arm/vfpm_macros.th @@ -0,0 +1,14 @@ +/* -- -- */ + + +/* Fd := Fm */ + +#define ARM_D_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_DOUBLE,ARM_VFP_,(dreg),(sreg))) +#define ARM_D(p,dreg,sreg) ARM_D_COND(p,dreg,sreg,ARMCOND_AL) + +#define ARM_S_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_SINGLE,ARM_VFP_,(dreg),(sreg))) +#define ARM_S(p,dreg,sreg) ARM_S_COND(p,dreg,sreg,ARMCOND_AL) + + diff --git a/unity-2019.4.24f1-mbe/mono/arch/arm/vfpops.sh b/unity-2019.4.24f1-mbe/mono/arch/arm/vfpops.sh new file mode 100644 index 000000000..bed4a9c80 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/arm/vfpops.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +DYADIC="ADD SUB MUL NMUL DIV" +MONADIC="CPY ABS NEG SQRT CMP CMPE CMPZ CMPEZ CVT UITO SITO TOUI TOSI TOUIZ TOSIZ" + +# $1: opcode list +# $2: template +gen() { + for i in $1; do + sed "s//$i/g" $2.th + done +} + +echo -e "/* Macros for VFP ops, auto-generated from template */\n" + +echo -e "\n/* dyadic */\n" +gen "$DYADIC" vfp_macros + +echo -e "\n/* monadic */\n" +gen "$MONADIC" vfpm_macros + +echo -e "\n\n" + +echo -e "\n/* end generated */\n" diff --git a/unity-2019.4.24f1-mbe/mono/arch/arm64/.gitignore b/unity-2019.4.24f1-mbe/mono/arch/arm64/.gitignore new file mode 100644 index 000000000..13efac709 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/arm64/.gitignore @@ -0,0 +1,6 @@ +/ +/Makefile +/Makefile.in +/*.o +/*.lo +/.deps diff --git a/unity-2019.4.24f1-mbe/mono/arch/arm64/Makefile.am b/unity-2019.4.24f1-mbe/mono/arch/arm64/Makefile.am new file mode 100644 index 000000000..8e5609731 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/arm64/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = arm64-codegen.h codegen-test.c diff --git a/unity-2019.4.24f1-mbe/mono/arch/arm64/arm64-codegen.h b/unity-2019.4.24f1-mbe/mono/arch/arm64/arm64-codegen.h new file mode 100644 index 000000000..9bee54852 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/arm64/arm64-codegen.h @@ -0,0 +1,860 @@ +/* + * arm64-codegen.h: ARM64 code generation macros + * + * Author: + * Zoltan Varga (vargaz@gmail.com) + * + * Copyright 2013 Xamarin, Inc (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#ifndef __ARM64_CODEGEN_H__ +#define __ARM64_CODEGEN_H__ + +#include + +enum { + ARMREG_R0 = 0, + ARMREG_R1 = 1, + ARMREG_R2 = 2, + ARMREG_R3 = 3, + ARMREG_R4 = 4, + ARMREG_R5 = 5, + ARMREG_R6 = 6, + ARMREG_R7 = 7, + ARMREG_R8 = 8, + ARMREG_R9 = 9, + ARMREG_R10 = 10, + ARMREG_R11 = 11, + ARMREG_R12 = 12, + ARMREG_R13 = 13, + ARMREG_R14 = 14, + ARMREG_R15 = 15, + ARMREG_R16 = 16, + ARMREG_R17 = 17, + ARMREG_R18 = 18, + ARMREG_R19 = 19, + ARMREG_R20 = 20, + ARMREG_R21 = 21, + ARMREG_R22 = 22, + ARMREG_R23 = 23, + ARMREG_R24 = 24, + ARMREG_R25 = 25, + ARMREG_R26 = 26, + ARMREG_R27 = 27, + ARMREG_R28 = 28, + ARMREG_R29 = 29, + ARMREG_R30 = 30, + ARMREG_SP = 31, + ARMREG_RZR = 31, + + ARMREG_IP0 = ARMREG_R16, + ARMREG_IP1 = ARMREG_R17, + ARMREG_FP = ARMREG_R29, + ARMREG_LR = ARMREG_R30 +}; + +enum { + ARMREG_D0 = 0, + ARMREG_D1 = 1, + ARMREG_D2 = 2, + ARMREG_D3 = 3, + ARMREG_D4 = 4, + ARMREG_D5 = 5, + ARMREG_D6 = 6, + ARMREG_D7 = 7, + ARMREG_D8 = 8, + ARMREG_D9 = 9, + ARMREG_D10 = 10, + ARMREG_D11 = 11, + ARMREG_D12 = 12, + ARMREG_D13 = 13, + ARMREG_D14 = 14, + ARMREG_D15 = 15, + ARMREG_D16 = 16, + ARMREG_D17 = 17, + ARMREG_D18 = 18, + ARMREG_D19 = 19, + ARMREG_D20 = 20, + ARMREG_D21 = 21, + ARMREG_D22 = 22, + ARMREG_D23 = 23, + ARMREG_D24 = 24, + ARMREG_D25 = 25, + ARMREG_D26 = 26, + ARMREG_D27 = 27, + ARMREG_D28 = 28, + ARMREG_D29 = 29, + ARMREG_D30 = 30, + ARMREG_D31 = 31 +}; + +typedef enum { + ARMCOND_EQ = 0x0, /* Equal; Z = 1 */ + ARMCOND_NE = 0x1, /* Not equal, or unordered; Z = 0 */ + ARMCOND_CS = 0x2, /* Carry set; C = 1 */ + ARMCOND_HS = ARMCOND_CS, /* Unsigned higher or same; */ + ARMCOND_CC = 0x3, /* Carry clear; C = 0 */ + ARMCOND_LO = ARMCOND_CC, /* Unsigned lower */ + ARMCOND_MI = 0x4, /* Negative; N = 1 */ + ARMCOND_PL = 0x5, /* Positive or zero; N = 0 */ + ARMCOND_VS = 0x6, /* Overflow; V = 1 */ + ARMCOND_VC = 0x7, /* No overflow; V = 0 */ + ARMCOND_HI = 0x8, /* Unsigned higher; C = 1 && Z = 0 */ + ARMCOND_LS = 0x9, /* Unsigned lower or same; C = 0 || Z = 1 */ + ARMCOND_GE = 0xA, /* Signed greater than or equal; N = V */ + ARMCOND_LT = 0xB, /* Signed less than; N != V */ + ARMCOND_GT = 0xC, /* Signed greater than; Z = 0 && N = V */ + ARMCOND_LE = 0xD, /* Signed less than or equal; Z = 1 || N != V */ + ARMCOND_AL = 0xE, /* Always */ + ARMCOND_NV = 0xF, /* Never */ +} ARMCond; + +typedef enum { + ARMSHIFT_LSL = 0x0, + ARMSHIFT_LSR = 0x1, + ARMSHIFT_ASR = 0x2 +} ARMShift; + +typedef enum { + ARMSIZE_B = 0x0, + ARMSIZE_H = 0x1, + ARMSIZE_W = 0x2, + ARMSIZE_X = 0x3 +} ARMSize; + +#define arm_emit(p, ins) do { *(guint32*)(p) = (ins); (p) += 4; } while (0) + +/* Overwrite bits [offset,offset+nbits] with value */ +static G_GNUC_UNUSED inline void +arm_set_ins_bits (void *p, int offset, int nbits, guint32 value) +{ + *(guint32*)p = (*(guint32*)p & ~(((1 << nbits) - 1) << offset)) | (value << offset); +} + +/* + * Naming conventions for codegen macros: + * - 64 bit opcodes have an 'X' suffix + * - 32 bit opcodes have a 'W' suffix + * - the order of operands is the same as in assembly + */ + +/* + * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0487a/index.html + */ + +/* Uncoditional branch (register) */ + +// 0b1101011 == 0x6b +#define arm_format_breg(p, opc, op2, op3, op4, rn) arm_emit ((p), (0x6b << 25) | ((opc) << 21) | ((op2) << 16) | ((op3) << 10) | ((rn) << 5) | ((op4) << 0)) + +// 0b0000 == 0x0, 0b11111 == 0x1f +#define arm_brx(p, reg) arm_format_breg ((p), 0x0, 0x1f, 0x0, 0x0, (reg)) + +// 0b0001 == 0x1 +#define arm_blrx(p, reg) arm_format_breg ((p), 0x1, 0x1f, 0x0, 0x0, (reg)) + +//0b0010 == 0x2 +#define arm_retx(p, reg) arm_format_breg ((p), 0x2, 0x1f, 0x0, 0x0, (reg)) + +/* Unconditional branch (immeditate) */ + +static G_GNUC_UNUSED inline gboolean +arm_is_bl_disp (void *code, void *target) +{ + gint64 disp = ((char*)(target) - (char*)(code)) / 4; + + return (disp > -(1 << 25)) && (disp < (1 << 25)); +} + +static G_GNUC_UNUSED inline unsigned int +arm_get_disp (void *p, void *target) +{ + unsigned int disp = (unsigned int)(((char*)target - (char*)p) / 4); + + if (target) + g_assert (arm_is_bl_disp (p, target)); + + return (disp & 0x3ffffff); +} + +// 0b00101 == 0x5 +#define arm_b(p, target) do { if ((target)) g_assert (arm_is_bl_disp ((p), (target))); arm_emit (p, (0x0 << 31) | (0x5 << 26) | ((arm_get_disp ((p), (target)) << 0))); } while (0) + +#define arm_bl(p, target) do { if ((target)) g_assert (arm_is_bl_disp ((p), (target))); arm_emit (p, (0x1 << 31) | (0x5 << 26) | ((arm_get_disp ((p), (target)) << 0))); } while (0) + +/* Conditional branch */ + +static G_GNUC_UNUSED inline gboolean +arm_is_disp19 (void *code, void *target) +{ + gint64 disp = ((char*)(target) - (char*)(code)) / 4; + + return (disp > -(1 << 18)) && (disp < (1 << 18)); +} + +static G_GNUC_UNUSED inline unsigned int +arm_get_disp19 (void *p, void *target) +{ + unsigned int disp = (unsigned int)(((char*)target - (char*)p) / 4); + + if (target) + g_assert (arm_is_disp19 (p, target)); + + return (disp & 0x7ffff); +} + +// 0b0101010 == 0x2a +#define arm_format_condbr(p, o1, o0, cond, disp) arm_emit ((p), (0x2a << 25) | ((o1) << 24) | ((disp) << 5) | ((o0) << 4) | ((cond) << 0)) +#define arm_get_bcc_cond(p) ((*(guint32*)p) & 0xf) + +#define arm_bcc(p, cond, target) arm_format_condbr ((p), 0x0, 0x0, (cond), arm_get_disp19 ((p), (target))) + +// 0b011010 == 0x1a +#define arm_format_cmpbr(p, sf, op, rt, target) arm_emit ((p), ((sf) << 31) | (0x1a << 25) | ((op) << 24) | (arm_get_disp19 ((p), (target)) << 5) | ((rt) << 0)) + +#define arm_set_cbz_target(p, target) arm_set_ins_bits (p, 5, 19, arm_get_disp19 ((p), (target))) + +#define arm_cbzx(p, rt, target) arm_format_cmpbr ((p), 0x1, 0x0, (rt), (target)) +#define arm_cbzw(p, rt, target) arm_format_cmpbr ((p), 0x0, 0x0, (rt), (target)) + +#define arm_cbnzx(p, rt, target) arm_format_cmpbr ((p), 0x1, 0x1, (rt), (target)) +#define arm_cbnzw(p, rt, target) arm_format_cmpbr ((p), 0x0, 0x1, (rt), (target)) + +static G_GNUC_UNUSED inline unsigned int +arm_get_disp15 (void *p, void *target) +{ + unsigned int disp = (unsigned int)(((char*)target - (char*)p) / 4); + return (disp & 0x7fff); +} + +// 0b011011 == 0x1b +#define arm_format_tbimm(p, op, rt, bit, target) arm_emit ((p), ((((bit) >> 5) & 1) << 31) | (0x1b << 25) | ((op) << 24) | (((bit) & 0x1f) << 19) | (arm_get_disp15 ((p), (target)) << 5) | ((rt) << 0)) + +#define arm_tbz(p, rt, bit, target) arm_format_tbimm ((p), 0x0, (rt), (bit), (target)) +#define arm_tbnz(p, rt, bit, target) arm_format_tbimm ((p), 0x1, (rt), (bit), (target)) + +/* Memory access */ + +#define arm_is_pimm12_scaled(pimm,size) ((pimm) >= 0 && (pimm) / (size) <= 0xfff && ((pimm) % (size)) == 0) + +static G_GNUC_UNUSED unsigned int +arm_encode_pimm12 (int pimm, int size) +{ + g_assert (arm_is_pimm12_scaled (pimm, size)); + return ((unsigned int)(pimm / size)) & 0xfff; +} + +#define arm_is_strb_imm(pimm) arm_is_pimm12_scaled((pimm), 1) +#define arm_is_strh_imm(pimm) arm_is_pimm12_scaled((pimm), 2) +#define arm_is_strw_imm(pimm) arm_is_pimm12_scaled((pimm), 4) +#define arm_is_strx_imm(pimm) arm_is_pimm12_scaled((pimm), 8) + +/* Load/Store register + scaled immediate */ +/* No pre-index/post-index yet */ +#define arm_format_mem_imm(p, size, opc, rt, rn, pimm, scale) arm_emit ((p), ((size) << 30) | (0x39 << 24) | ((opc) << 22) | (arm_encode_pimm12 ((pimm), (scale)) << 10) | ((rn) << 5) | ((rt) << 0)) + +/* C5.6.83 LDR (immediate) */ +#define arm_ldrx(p, rt, rn, pimm) arm_format_mem_imm (p, ARMSIZE_X, 0x1, (rt), (rn), (pimm), 8) +#define arm_ldrw(p, rt, rn, pimm) arm_format_mem_imm (p, ARMSIZE_W, 0x1, (rt), (rn), (pimm), 4) +/* C5.6.86 LDRB (immediate) */ +#define arm_ldrb(p, rt, rn, pimm) arm_format_mem_imm (p, ARMSIZE_B, 0x1, (rt), (rn), (pimm), 1) +/* C5.6.88 LDRH (immediate) */ +#define arm_ldrh(p, rt, rn, pimm) arm_format_mem_imm (p, ARMSIZE_H, 0x1, (rt), (rn), (pimm), 2) +/* C5.6.90 LDRSB (immediate) */ +#define arm_ldrsbx(p, rt, rn, pimm) arm_format_mem_imm (p, ARMSIZE_B, 0x2, (rt), (rn), (pimm), 1) +#define arm_ldrsbw(p, rt, rn, pimm) arm_format_mem_imm (p, ARMSIZE_B, 0x3, (rt), (rn), (pimm), 1) +/* C5.6.92 LDRSH (immediate) */ +#define arm_ldrshx(p, rt, rn, pimm) arm_format_mem_imm (p, ARMSIZE_H, 0x2, (rt), (rn), (pimm), 2) +#define arm_ldrshw(p, rt, rn, pimm) arm_format_mem_imm (p, ARMSIZE_H, 0x3, (rt), (rn), (pimm), 2) +/* C5.6.94 LDRSW (immediate) */ +#define arm_ldrswx(p, rt, rn, pimm) arm_format_mem_imm (p, ARMSIZE_W, 0x2, (rt), (rn), (pimm), 4) + +/* C5.6.178 STR (immediate) */ +#define arm_strx(p, rt, rn, pimm) arm_format_mem_imm (p, ARMSIZE_X, 0x0, (rt), (rn), (pimm), 8) +#define arm_strw(p, rt, rn, pimm) arm_format_mem_imm (p, ARMSIZE_W, 0x0, (rt), (rn), (pimm), 4) +/* C5.6.182 STR (immediate) */ +#define arm_strh(p, rt, rn, pimm) arm_format_mem_imm (p, ARMSIZE_H, 0x0, (rt), (rn), (pimm), 2) +#define arm_strb(p, rt, rn, pimm) arm_format_mem_imm (p, ARMSIZE_B, 0x0, (rt), (rn), (pimm), 1) + +/* C3.3.9 Load/store register (immediate post-indexed) */ +static G_GNUC_UNUSED unsigned int +arm_encode_simm9 (int simm) +{ + g_assert (simm >= -256 && simm <= 255); + return ((unsigned int)simm) & 0x1ff; +} + +#define arm_format_mem_imm_post(p, size, V, opc, rt, rn, simm) arm_emit ((p), ((size) << 30) | (0x7 << 27) | ((V) << 26) | (0x0 << 24) | ((opc) << 22) | (arm_encode_simm9 ((simm)) << 12) | (0x1 << 10) | ((rn) << 5) | ((rt) << 0)) + +#define arm_ldrx_post(p, rt, rn, simm) arm_format_mem_imm_post (p, ARMSIZE_X, 0x0, 0x1, (rt), (rn), (simm)) +#define arm_ldrw_post(p, rt, rn, simm) arm_format_mem_imm_post (p, ARMSIZE_W, 0x0, 0x1, (rt), (rn), (simm)) + +#define arm_strx_post(p, rt, rn, simm) arm_format_mem_imm_post (p, ARMSIZE_X, 0x0, 0x0, (rt), (rn), (simm)) +#define arm_strw_post(p, rt, rn, simm) arm_format_mem_imm_post (p, ARMSIZE_W, 0x0, 0x0, (rt), (rn), (simm)) + +/* C3.3.9 Load/store register (immediate pre-indexed) */ +#define arm_format_mem_imm_pre(p, size, V, opc, rt, rn, simm) arm_emit ((p), ((size) << 30) | (0x7 << 27) | ((V) << 26) | (0x0 << 24) | ((opc) << 22) | (arm_encode_simm9 ((simm)) << 12) | (0x3 << 10) | ((rn) << 5) | ((rt) << 0)) + +#define arm_ldrx_pre(p, rt, rn, simm) arm_format_mem_imm_pre (p, ARMSIZE_X, 0x0, 0x1, (rt), (rn), (simm)) +#define arm_ldrw_pre(p, rt, rn, simm) arm_format_mem_imm_pre (p, ARMSIZE_W, 0x0, 0x1, (rt), (rn), (simm)) + +#define arm_strx_pre(p, rt, rn, simm) arm_format_mem_imm_pre (p, ARMSIZE_X, 0x0, 0x0, (rt), (rn), (simm)) +#define arm_strw_pre(p, rt, rn, simm) arm_format_mem_imm_pre (p, ARMSIZE_W, 0x0, 0x0, (rt), (rn), (simm)) + +/* Load/Store register + register */ +/* No extend/scale yet */ +#define arm_format_mem_reg(p, size, opc, rt, rn, rm) arm_emit ((p), ((size) << 30) | (0x38 << 24) | ((opc) << 22) | (0x1 << 21) | ((rm) << 16) | (0x3 << 13) | (0 << 12) | (0x2 << 10) | ((rn) << 5) | ((rt) << 0)) + +/* C5.6.85 LDR (register) */ +#define arm_ldrx_reg(p, rt, rn, rm) arm_format_mem_reg ((p), ARMSIZE_X, 0x1, (rt), (rn), (rm)) +#define arm_ldrw_reg(p, rt, rn, rm) arm_format_mem_reg ((p), ARMSIZE_W, 0x1, (rt), (rn), (rm)) +/* C5.6.87 LDRB (register) */ +#define arm_ldrb_reg(p, rt, rn, rm) arm_format_mem_reg ((p), ARMSIZE_B, 0x1, (rt), (rn), (rm)) +/* C5.6.88 LDRH (register) */ +#define arm_ldrh_reg(p, rt, rn, rm) arm_format_mem_reg ((p), ARMSIZE_H, 0x1, (rt), (rn), (rm)) +/* C5.6.91 LDRSB (register) */ +#define arm_ldrsbx_reg(p, rt, rn, rm) arm_format_mem_reg ((p), ARMSIZE_B, 0x2, (rt), (rn), (rm)) +#define arm_ldrsbw_reg(p, rt, rn, rm) arm_format_mem_reg ((p), ARMSIZE_B, 0x3, (rt), (rn), (rm)) +/* C5.6.93 LDRSH (register) */ +#define arm_ldrshx_reg(p, rt, rn, rm) arm_format_mem_reg ((p), ARMSIZE_H, 0x2, (rt), (rn), (rm)) +#define arm_ldrshw_reg(p, rt, rn, rm) arm_format_mem_reg ((p), ARMSIZE_H, 0x3, (rt), (rn), (rm)) +/* C5.6.96 LDRSW (register) */ +#define arm_ldrswx_reg(p, rt, rn, rm) arm_format_mem_reg ((p), ARMSIZE_W, 0x2, (rt), (rn), (rm)) + +/* C5.6.179 STR (register) */ +#define arm_strx_reg(p, rt, rn, rm) arm_format_mem_reg ((p), ARMSIZE_X, 0x0, (rt), (rn), (rm)) +#define arm_strw_reg(p, rt, rn, rm) arm_format_mem_reg ((p), ARMSIZE_W, 0x0, (rt), (rn), (rm)) +/* C5.6.181 STRB (register) */ +#define arm_strb_reg(p, rt, rn, rm) arm_format_mem_reg ((p), ARMSIZE_B, 0x0, (rt), (rn), (rm)) +/* C5.6.183 STRH (register) */ +#define arm_strh_reg(p, rt, rn, rm) arm_format_mem_reg ((p), ARMSIZE_H, 0x0, (rt), (rn), (rm)) + +/* PC relative */ + +/* C5.6.84 LDR (literal) */ + +#define arm_get_ldr_lit_reg(p) (*(guint32*)(p) & 0x1f) + +#define arm_ldrx_lit(p, rt, target) arm_emit ((p), (0x01 << 30) | (0x18 << 24) | (arm_get_disp19 ((p), (target)) << 5) | ((rt) << 0)) +#define arm_ldrw_lit(p, rt, target) arm_emit ((p), (0x00 << 30) | (0x18 << 24) | (arm_get_disp19 ((p), (target)) << 5) | ((rt) << 0)) +#define arm_ldrswx_lit(p, rt, target) arm_emit ((p), (0x2 << 30) | (0x18 << 24) | (arm_get_disp19 ((p), (target)) << 5) | ((rt) << 0)) + +/* Unscaled offset */ +/* FIXME: Not yet */ + +/* Load/Store Pair */ + +static G_GNUC_UNUSED unsigned int +arm_encode_imm7 (int imm, int size) +{ + g_assert (imm / size >= -64 && imm / size <= 63 && (imm % size) == 0); + return ((unsigned int)(imm / size)) & 0x7f; +} + +#define arm_is_imm7_scaled(imm, size) ((imm) / (size) >= -64 && (imm) / (size) <= 63 && ((imm) % (size)) == 0) + +#define arm_is_ldpx_imm(imm) arm_is_imm7_scaled ((imm), 8) + +/* C3.3.14 */ +#define arm_format_mem_p(p, size, opc, L, rt1, rt2, rn, imm) arm_emit ((p), (opc << 30) | (0x52 << 23) | ((L) << 22) | (arm_encode_imm7 (imm, size) << 15) | ((rt2) << 10) | ((rn) << 5) | ((rt1) << 0)) + +#define arm_ldpx(p, rt1, rt2, rn, imm) arm_format_mem_p ((p), 8, 0x2, 1, (rt1), (rt2), (rn), (imm)) +#define arm_ldpw(p, rt1, rt2, rn, imm) arm_format_mem_p ((p), 4, 0x0, 1, (rt1), (rt2), (rn), (imm)) +#define arm_ldpsw(p, rt1, rt2, rn, imm) arm_format_mem_p ((p), 4, 0x1, 1, (rt1), (rt2), (rn), (imm)) +#define arm_stpx(p, rt1, rt2, rn, imm) arm_format_mem_p ((p), 8, 0x2, 0, (rt1), (rt2), (rn), (imm)) +#define arm_stpw(p, rt1, rt2, rn, imm) arm_format_mem_p ((p), 4, 0x0, 0, (rt1), (rt2), (rn), (imm)) + +/* Load/Store Pair (Pre-indexed) */ +/* C3.3.16 */ +#define arm_format_mem_p_pre(p, size, opc, L, rt1, rt2, rn, imm) arm_emit ((p), (opc << 30) | (0x53 << 23) | ((L) << 22) | (arm_encode_imm7 (imm, size) << 15) | ((rt2) << 10) | ((rn) << 5) | ((rt1) << 0)) + +#define arm_ldpx_pre(p, rt1, rt2, rn, imm) arm_format_mem_p_pre ((p), 8, 0x2, 1, (rt1), (rt2), (rn), (imm)) +#define arm_ldpw_pre(p, rt1, rt2, rn, imm) arm_format_mem_p_pre ((p), 4, 0x0, 1, (rt1), (rt2), (rn), (imm)) +#define arm_ldpsw_pre(p, rt1, rt2, rn, imm) arm_format_mem_p_pre ((p), 4, 0x1, 1, (rt1), (rt2), (rn), (imm)) +#define arm_stpx_pre(p, rt1, rt2, rn, imm) arm_format_mem_p_pre ((p), 8, 0x2, 0, (rt1), (rt2), (rn), (imm)) +#define arm_stpw_pre(p, rt1, rt2, rn, imm) arm_format_mem_p_pre ((p), 4, 0x0, 0, (rt1), (rt2), (rn), (imm)) + +/* Not an official alias */ +#define arm_pushpx (p, rt1, rt2) arm_LDPX_pre (p, rt1, rt2, ARMREG_RSP, -8) + +/* Load/Store Pair (Post-indexed) */ +/* C3.3.15 */ +#define arm_format_mem_p_post(p, size, opc, L, rt1, rt2, rn, imm) arm_emit ((p), (opc << 30) | (0x51 << 23) | ((L) << 22) | (arm_encode_imm7 (imm, size) << 15) | ((rt2) << 10) | ((rn) << 5) | ((rt1) << 0)) + +#define arm_ldpx_post(p, rt1, rt2, rn, imm) arm_format_mem_p_post ((p), 8, 0x2, 1, (rt1), (rt2), (rn), (imm)) +#define arm_ldpw_post(p, rt1, rt2, rn, imm) arm_format_mem_p_post ((p), 4, 0x0, 1, (rt1), (rt2), (rn), (imm)) +#define arm_ldpsw_post(p, rt1, rt2, rn, imm) arm_format_mem_p_post ((p), 4, 0x1, 1, (rt1), (rt2), (rn), (imm)) +#define arm_stpx_post(p, rt1, rt2, rn, imm) arm_format_mem_p_post ((p), 8, 0x2, 0, (rt1), (rt2), (rn), (imm)) +#define arm_stpw_post(p, rt1, rt2, rn, imm) arm_format_mem_p_post ((p), 4, 0x0, 0, (rt1), (rt2), (rn), (imm)) + +/* Not an official alias */ +#define arm_poppx (p, rt1, rt2) arm_ldpx_post (p, rt1, rt2, ARMREG_RSP, 8) + +/* Load/Store Exclusive */ +#define arm_format_ldxr(p, size, rt, rn) arm_emit ((p), ((size) << 30) | (0x8 << 24) | (0x0 << 23) | (0x1 << 22) | (0x0 << 21) | (0x1f << 16) | (0x0 << 15) | (0x1f << 10) | ((rn) << 5) | ((rt) << 0)) +#define arm_format_ldxp(p, size, rt1, rt2, rn) arm_emit ((p), ((size) << 30) | (0x8 << 24) | (0x0 << 23) | (0x1 << 22) | (0x1 << 21) | (0x1f << 16) | (0x0 << 15) | ((rt2) << 10)| ((rn) << 5) | ((rt1) << 0)) +#define arm_format_stxr(p, size, rs, rt, rn) arm_emit ((p), ((size) << 30) | (0x8 << 24) | (0x0 << 23) | (0x0 << 22) | (0x0 << 21) | ((rs) << 16) | (0x0 << 15) | (0x1f << 10) | ((rn) << 5) | ((rt) << 0)) +#define arm_format_stxp(p, size, rs, rt1, rt2, rn) arm_emit ((p), ((size) << 30) | (0x8 << 24) | (0x0 << 23) | (0x0 << 22) | (0x1 << 21) | ((rs) << 16) | (0x0 << 15) | ((rt2) << 10)| ((rn) << 5) | ((rt1) << 0)) + +#define arm_ldxrx(p, rt, rn) arm_format_ldxr ((p), ARMSIZE_X, (rt), (rn)) +#define arm_ldxrw(p, rt, rn) arm_format_ldxr ((p), ARMSIZE_W, (rt), (rn)) +#define arm_ldxrh(p, rt, rn) arm_format_ldxr ((p), ARMSIZE_H, (rt), (rn)) +#define arm_ldxrb(p, rt, rn) arm_format_ldxr ((p), ARMSIZE_B, (rt), (rn)) +#define arm_ldxpx(p, rt1, rt2, rn) arm_format_ldxp ((p), ARMSIZE_X, (rt1), (rt2), (rn)) +#define arm_ldxpw(p, rt1, rt2, rn) arm_format_ldxp ((p), ARMSIZE_W, (rt1), (rt2), (rn)) +#define arm_stxrx(p, rs, rt, rn) arm_format_stxr ((p), ARMSIZE_X, (rs), (rt), (rn)) +#define arm_stxrw(p, rs, rt, rn) arm_format_stxr ((p), ARMSIZE_W, (rs), (rt), (rn)) +#define arm_stxrh(p, rs, rt, rn) arm_format_stxr ((p), ARMSIZE_H, (rs), (rt), (rn)) +#define arm_stxrb(p, rs, rt, rn) arm_format_stxr ((p), ARMSIZE_B, (rs), (rt), (rn)) +#define arm_stxpx(p, rs, rt1, rt2, rn) arm_format_stxp ((p), ARMSIZE_X, (rs), (rt1), (rt2), (rn)) +#define arm_stxpw(p, rs, rt1, rt2, rn) arm_format_stxp ((p), ARMSIZE_W, (rs), (rt1), (rt2), (rn)) + +/* C5.6.73 LDAR: Load-Acquire Register */ + +#define arm_format_ldar(p, size, rt, rn) arm_emit ((p), ((size) << 30) | (0x8 << 24) | (0x1 << 23) | (0x1 << 22) | (0x0 << 21) | (0x1f << 16) | (0x1 << 15) | (0x1f << 10) | ((rn) << 5) | ((rt) << 0)) + +#define arm_ldarx(p, rt, rn) arm_format_ldar ((p), ARMSIZE_X, (rt), (rn)) +#define arm_ldarw(p, rt, rn) arm_format_ldar ((p), ARMSIZE_W, (rt), (rn)) +#define arm_ldarh(p, rt, rn) arm_format_ldar ((p), ARMSIZE_H, (rt), (rn)) +#define arm_ldarb(p, rt, rn) arm_format_ldar ((p), ARMSIZE_B, (rt), (rn)) + +/* C5.6.169 STLR: Store-Release Register */ + +#define arm_format_stlr(p, size, rt, rn) arm_emit ((p), ((size) << 30) | (0x8 << 24) | (0x1 << 23) | (0x0 << 22) | (0x0 << 21) | (0x1f << 16) | (0x1 << 15) | (0x1f << 10) | ((rn) << 5) | ((rt) << 0)) + +#define arm_stlrx(p, rn, rt) arm_format_stlr ((p), ARMSIZE_X, (rt), (rn)) +#define arm_stlrw(p, rn, rt) arm_format_stlr ((p), ARMSIZE_W, (rt), (rn)) +#define arm_stlrh(p, rn, rt) arm_format_stlr ((p), ARMSIZE_H, (rt), (rn)) +#define arm_stlrb(p, rn, rt) arm_format_stlr ((p), ARMSIZE_B, (rt), (rn)) + +/* C5.6.77 LDAXR */ +#define arm_format_ldaxr(p, size, rn, rt) arm_emit ((p), ((size) << 30) | (0x8 << 24) | (0x0 << 23) | (0x1 << 22) | (0x0 << 21) | (0x1f << 16) | (0x1 << 15) | (0x1f << 10) | ((rn) << 5) | ((rt) << 0)) + +#define arm_ldaxrx(p, rt, rn) arm_format_ldaxr ((p), 0x3, (rn), (rt)) +#define arm_ldaxrw(p, rt, rn) arm_format_ldaxr ((p), 0x2, (rn), (rt)) + +/* C5.6.173 STLXR */ +#define arm_format_stlxr(p, size, rs, rn, rt) arm_emit ((p), ((size) << 30) | (0x8 << 24) | (0x0 << 23) | (0x0 << 22) | (0x0 << 21) | ((rs) << 16) | (0x1 << 15) | (0x1f << 10) | ((rn) << 5) | ((rt) << 0)) + +#define arm_stlxrx(p, rs, rt, rn) arm_format_stlxr ((p), 0x3, (rs), (rn), (rt)) +#define arm_stlxrw(p, rs, rt, rn) arm_format_stlxr ((p), 0x2, (rs), (rn), (rt)) + +/* Load/Store SIMD&FP */ + +/* C6.3.285 STR (immediate, SIMD&FP) */ +#define arm_format_strfp_imm(p, size, opc, rt, rn, pimm, scale) arm_emit ((p), ((size) << 30) | (0xf << 26) | (0x1 << 24) | ((opc) << 22) | (arm_encode_pimm12 ((pimm), (scale)) << 10) | ((rn) << 5) | ((rt) << 0)) + +/* Store double */ +#define arm_strfpx(p, dt, xn, simm) arm_format_strfp_imm ((p), ARMSIZE_X, 0x0, (dt), (xn), (simm), 8) +/* Store single */ +#define arm_strfpw(p, st, xn, simm) arm_format_strfp_imm ((p), ARMSIZE_W, 0x0, (st), (xn), (simm), 4) + +/* C6.3.166 LDR (immediate, SIMD&FP) */ +#define arm_format_ldrfp_imm(p, size, opc, rt, rn, pimm, scale) arm_emit ((p), ((size) << 30) | (0xf << 26) | (0x1 << 24) | ((opc) << 22) | (arm_encode_pimm12 ((pimm), (scale)) << 10) | ((rn) << 5) | ((rt) << 0)) + +/* Load double */ +#define arm_ldrfpx(p, dt, xn, simm) arm_format_ldrfp_imm ((p), ARMSIZE_X, 0x1, dt, xn, simm, 8) +/* Load single */ +#define arm_ldrfpw(p, dt, xn, simm) arm_format_ldrfp_imm ((p), ARMSIZE_W, 0x1, dt, xn, simm, 4) + +/* Arithmetic (immediate) */ +static G_GNUC_UNUSED inline guint32 +arm_encode_arith_imm (int imm, guint32 *shift) +{ + // FIXME: + g_assert ((imm >= 0) && (imm < 0xfff)); + *shift = 0; + return (guint32)imm; +} + +// FIXME: +#define arm_is_arith_imm(imm) (((imm) >= 0) && ((imm) < 0xfff)) + +#define arm_format_alu_imm(p, sf, op, S, rd, rn, imm) do { \ + guint32 _imm12, _shift; \ + _imm12 = arm_encode_arith_imm ((imm), &_shift); arm_emit ((p), ((sf) << 31) | ((op) << 30) | ((S) << 29) | (0x11 << 24) | ((_shift) << 22) | ((_imm12) << 10) | ((rn) << 5) | ((rd) << 0)); \ +} while (0) + +/* rd/rn can be SP for addx/subx */ +#define arm_addx_imm(p, rd, rn, imm) arm_format_alu_imm ((p), 0x1, 0x0, 0x0, (rd), (rn), (imm)) +#define arm_addw_imm(p, rd, rn, imm) arm_format_alu_imm ((p), 0x0, 0x0, 0x0, (rd), (rn), (imm)) +#define arm_addsx_imm(p, rd, rn, imm) arm_format_alu_imm ((p), 0x1, 0x0, 0x1, (rd), (rn), (imm)) +#define arm_addsw_imm(p, rd, rn, imm) arm_format_alu_imm ((p), 0x0, 0x0, 0x1, (rd), (rn), (imm)) +#define arm_subx_imm(p, rd, rn, imm) arm_format_alu_imm ((p), 0x1, 0x1, 0x0, (rd), (rn), (imm)) +#define arm_subw_imm(p, rd, rn, imm) arm_format_alu_imm ((p), 0x0, 0x1, 0x0, (rd), (rn), (imm)) +#define arm_subsx_imm(p, rd, rn, imm) arm_format_alu_imm ((p), 0x1, 0x1, 0x1, (rd), (rn), (imm)) +#define arm_subsw_imm(p, rd, rn, imm) arm_format_alu_imm ((p), 0x0, 0x1, 0x1, (rd), (rn), (imm)) + +#define arm_cmpx_imm(p, rn, imm) arm_subsx_imm ((p), ARMREG_RZR, (rn), (imm)) +#define arm_cmpw_imm(p, rn, imm) arm_subsw_imm ((p), ARMREG_RZR, (rn), (imm)) +#define arm_cmnx_imm(p, rn, imm) arm_addsx_imm ((p), ARMREG_RZR, (rn), (imm)) +#define arm_cmnw_imm(p, rn, imm) arm_addsw_imm ((p), ARMREG_RZR, (rn), (imm)) + +/* Logical (immediate) */ + +// FIXME: imm +#if 0 +#define arm_format_and(p, sf, opc, rd, rn, imm) arm_emit ((p), ((sf) << 31) | ((opc) << 29) | (0x24 << 23) | ((0) << 22) | ((imm) << 10) | ((rn) << 5) | ((rd) << 0)) + +#define arm_andx_imm(p, rd, rn, imm) arm_format_and ((p), 0x1, 0x0, (rd), (rn), (imm)) +#define arm_andw_imm(p, rd, rn, imm) arm_format_and ((p), 0x0, 0x0, (rd), (rn), (imm)) +#define arm_andsx_imm(p, rd, rn, imm) arm_format_and ((p), 0x1, 0x3, (rd), (rn), (imm)) +#define arm_andsw_imm(p, rd, rn, imm) arm_format_and ((p), 0x0, 0x3, (rd), (rn), (imm)) +#define arm_eorx_imm(p, rd, rn, imm) arm_format_and ((p), 0x1, 0x2, (rd), (rn), (imm)) +#define arm_eorw_imm(p, rd, rn, imm) arm_format_and ((p), 0x0, 0x2, (rd), (rn), (imm)) +#define arm_orrx_imm(p, rd, rn, imm) arm_format_and ((p), 0x1, 0x1, (rd), (rn), (imm)) +#define arm_orrw_imm(p, rd, rn, imm) arm_format_and ((p), 0x0, 0x1, (rd), (rn), (imm)) + +#define arm_tstx_imm(p, rn, imm) arm_andsx_imm ((p), ARMREG_RZR, (rn), (imm)) +#define arm_tstw_imm(p, rn, imm) arm_andsw_imm ((p), ARMREG_RZR, (rn), (imm)) +#endif + +/* Move (wide immediate) */ +#define arm_format_mov(p, sf, opc, hw, rd, imm16) arm_emit ((p), ((sf) << 31) | ((opc) << 29) | (0x25 << 23) | ((hw) << 21) | (((guint32)(imm16) & 0xffff) << 5) | ((rd) << 0)) + +#define arm_get_movzx_rd(p) ((*(guint32*)p) & 0x1f) + +#define arm_movzx(p, rd, imm, shift) do { g_assert ((shift) % 16 == 0); arm_format_mov ((p), 0x1, 0x2, (shift) / 16, (rd), (imm)); } while (0) +#define arm_movzw(p, rd, imm, shift) do { g_assert ((shift) % 16 == 0); arm_format_mov ((p), 0x0, 0x2, (shift) / 16, (rd), (imm)); } while (0) +#define arm_movnx(p, rd, imm, shift) do { g_assert ((shift) % 16 == 0); arm_format_mov ((p), 0x1, 0x0, (shift) / 16, (rd), (imm)); } while (0) +#define arm_movnw(p, rd, imm, shift) do { g_assert ((shift) % 16 == 0); arm_format_mov ((p), 0x0, 0x0, (shift) / 16, (rd), (imm)); } while (0) +#define arm_movkx(p, rd, imm, shift) do { g_assert ((shift) % 16 == 0); arm_format_mov ((p), 0x1, 0x3, (shift) / 16, (rd), (imm)); } while (0) +#define arm_movkw(p, rd, imm, shift) do { g_assert ((shift) % 16 == 0); arm_format_mov ((p), 0x0, 0x3, (shift) / 16, (rd), (imm)); } while (0) + +/* PC-relative address calculation */ +#define arm_format_adrp(p, op, rd, target) do { guint64 imm1 = (guint64)(target); guint64 imm2 = (guint64)(p); int _imm = imm1 - imm2; arm_emit ((p), ((op) << 31) | (((_imm) & 0x3) << 29) | (0x10 << 24) | (((_imm >> 2) & 0x7ffff) << 5) | ((rd) << 0)); } while (0) + +#define arm_adrpx(p, rd, target) arm_format_adrp ((p), 0x1, (rd), (target)) +#define arm_adrx(p, rd, target) arm_format_adrp ((p), 0x0, (rd), (target)) + +/* Bitfield move */ +#define arm_format_bfm(p, sf, opc, N, immr, imms, rn, rd) arm_emit ((p), ((sf) << 31) | ((opc) << 29) | (0x26 << 23) | ((N) << 22) | ((N) << 22) | ((immr) << 16) | ((imms) << 10) | ((rn) << 5) | ((rd) << 0)) + +#define arm_bfmx(p, rd, rn, immr, imms) arm_format_bfm ((p), 0x1, 0x1, 0x1, (immr), (imms), (rn), (rd)) +#define arm_bfmw(p, rd, rn, immr, imms) arm_format_bfm ((p), 0x0, 0x1, 0x0, (immr), (imms), (rn), (rd)) +#define arm_sbfmx(p, rd, rn, immr, imms) arm_format_bfm ((p), 0x1, 0x0, 0x1, (immr), (imms), (rn), (rd)) +#define arm_sbfmw(p, rd, rn, immr, imms) arm_format_bfm ((p), 0x0, 0x0, 0x0, (immr), (imms), (rn), (rd)) +#define arm_ubfmx(p, rd, rn, immr, imms) arm_format_bfm ((p), 0x1, 0x2, 0x1, (immr), (imms), (rn), (rd)) +#define arm_ubfmw(p, rd, rn, immr, imms) arm_format_bfm ((p), 0x0, 0x2, 0x0, (immr), (imms), (rn), (rd)) + +/* Sign extend and Zero-extend */ +#define arm_sxtbx(p, rd, rn) arm_sbfmx ((p), (rd), (rn), 0, 7) +#define arm_sxtbw(p, rd, rn) arm_sbfmw ((p), (rd), (rn), 0, 7) +#define arm_sxthx(p, rd, rn) arm_sbfmx ((p), (rd), (rn), 0, 15) +#define arm_sxthw(p, rd, rn) arm_sbfmw ((p), (rd), (rn), 0, 15) +#define arm_sxtwx(p, rd, rn) arm_sbfmx ((p), (rd), (rn), 0, 31) +#define arm_uxtbx(p, rd, rn) arm_ubfmx ((p), (rd), (rn), 0, 7) +#define arm_uxtbw(p, rd, rn) arm_ubfmw ((p), (rd), (rn), 0, 7) +#define arm_uxthx(p, rd, rn) arm_ubfmx ((p), (rd), (rn), 0, 15) +#define arm_uxthw(p, rd, rn) arm_ubfmw ((p), (rd), (rn), 0, 15) + +/* Extract register */ +#define arm_format_extr(p, sf, N, rd, rn, rm, imms) arm_emit ((p), ((sf) << 31) | (0x27 << 23) | ((N) << 22) | (0x0 << 21) | ((rm) << 16) | ((imms) << 10) | ((rn) << 5) | ((rd) << 0)) +#define arm_extrx(p, rd, rn, rm, lsb) arm_format_extr ((p), 0x1, 0x1, (rd), (rn), (rm), (lsb)) +#define arm_extrw(p, rd, rn, rm, lsb) arm_format_extr ((p), 0x0, 0x0, (rd), (rn), (rm), (lsb)) + +/* Shift (immediate) */ +#define arm_asrx(p, rd, rn, shift) arm_sbfmx ((p), (rd), (rn), (shift), 63) +#define arm_asrw(p, rd, rn, shift) arm_sbfmw ((p), (rd), (rn), (shift), 31) +#define arm_lslx(p, rd, rn, shift) arm_ubfmx ((p), (rd), (rn), 64 - ((shift) % 64), 63 - ((shift) % 64)) +#define arm_lslw(p, rd, rn, shift) arm_ubfmw ((p), (rd), (rn), 32 - ((shift) % 32), 31 - ((shift) % 32)) +#define arm_lsrx(p, rd, rn, shift) arm_ubfmx ((p), (rd), (rn), shift, 63) +#define arm_lsrw(p, rd, rn, shift) arm_ubfmw ((p), (rd), (rn), shift, 31) +#define arm_rorx(p, rd, rs, shift) arm_extrx ((p), (rd), (rs), (rs), (shift)) +#define arm_rorw(p, rd, rs, shift) arm_extrw ((p), (rd), (rs), (rs), (shift)) + +/* Arithmetic (shifted register) */ +#define arm_format_alu_shift(p, sf, op, S, rd, rn, rm, shift, imm6) arm_emit ((p), ((sf) << 31) | ((op) << 30) | ((S) << 29) | (0xb << 24) | ((shift) << 22) | (0x0 << 21) | ((rm) << 16) | ((imm6) << 10) | ((rn) << 5) | ((rd) << 0)) + +#define arm_addx_shift(p, rd, rn, rm, shift_type, amount) arm_format_alu_shift ((p), 0x1, 0x0, 0x0, (rd), (rn), (rm), (shift_type), (amount)) +#define arm_addw_shift(p, rd, rn, rm, shift_type, amount) arm_format_alu_shift ((p), 0x0, 0x0, 0x0, (rd), (rn), (rm), (shift_type), (amount)) +#define arm_addsx_shift(p, rd, rn, rm, shift_type, amount) arm_format_alu_shift ((p), 0x1, 0x0, 0x1, (rd), (rn), (rm), (shift_type), (amount)) +#define arm_addsw_shift(p, rd, rn, rm, shift_type, amount) arm_format_alu_shift ((p), 0x0, 0x0, 0x1, (rd), (rn), (rm), (shift_type), (amount)) +#define arm_subx_shift(p, rd, rn, rm, shift_type, amount) arm_format_alu_shift ((p), 0x1, 0x1, 0x0, (rd), (rn), (rm), (shift_type), (amount)) +#define arm_subw_shift(p, rd, rn, rm, shift_type, amount) arm_format_alu_shift ((p), 0x0, 0x1, 0x0, (rd), (rn), (rm), (shift_type), (amount)) +#define arm_subsx_shift(p, rd, rn, rm, shift_type, amount) arm_format_alu_shift ((p), 0x1, 0x1, 0x1, (rd), (rn), (rm), (shift_type), (amount)) +#define arm_subsw_shift(p, rd, rn, rm, shift_type, amount) arm_format_alu_shift ((p), 0x0, 0x1, 0x1, (rd), (rn), (rm), (shift_type), (amount)) +#define arm_cmnx_shift(p, rn, rm, shift_type, amount) arm_addsx_shift ((p), ARMREG_RZR, (rn), (rm), (shift_type), (amount)) +#define arm_cmnw_shift(p, rn, rm, shift_type, amount) arm_addsw_shift ((p), ARMREG_RZR, (rn), (rm), (shift_type), (amount)) +#define arm_cmpx_shift(p, rn, rm, shift_type, amount) arm_subsx_shift ((p), ARMREG_RZR, (rn), (rm), (shift_type), (amount)) +#define arm_cmpw_shift(p, rn, rm, shift_type, amount) arm_subsw_shift ((p), ARMREG_RZR, (rn), (rm), (shift_type), (amount)) +#define arm_negx_shift(p, rd, rm, shift_type, amount) arm_subx_shift ((p), (rd), ARMREG_RZR, (rm), (shift_type), (amount)) +#define arm_negw_shift(p, rd, rm, shift_type, amount) arm_subw_shift ((p), (rd), ARMREG_RZR, (rm), (shift_type), (amount)) +#define arm_negsx_shift(p, rd, rm, shift_type, amount) arm_subsx_shift ((p), (rd), ARMREG_RZR, (rm), (shift_type), (amount)) +#define arm_negsw_shift(p, rd, rm, shift_type, amount) arm_subsw_shift ((p), (rd), ARMREG_RZR, (rm), (shift_type), (amount)) + +#define arm_addx(p, rd, rn, rm) arm_addx_shift ((p), (rd), (rn), (rm), 0, 0) +#define arm_addw(p, rd, rn, rm) arm_addw_shift ((p), (rd), (rn), (rm), 0, 0) +#define arm_subx(p, rd, rn, rm) arm_subx_shift ((p), (rd), (rn), (rm), 0, 0) +#define arm_subw(p, rd, rn, rm) arm_subw_shift ((p), (rd), (rn), (rm), 0, 0) +#define arm_addsx(p, rd, rn, rm) arm_addsx_shift ((p), (rd), (rn), (rm), 0, 0) +#define arm_addsw(p, rd, rn, rm) arm_addsw_shift ((p), (rd), (rn), (rm), 0, 0) +#define arm_subsx(p, rd, rn, rm) arm_subsx_shift ((p), (rd), (rn), (rm), 0, 0) +#define arm_subsw(p, rd, rn, rm) arm_subsw_shift ((p), (rd), (rn), (rm), 0, 0) +#define arm_cmpx(p, rd, rn) arm_cmpx_shift ((p), (rd), (rn), 0, 0) +#define arm_cmpw(p, rd, rn) arm_cmpw_shift ((p), (rd), (rn), 0, 0) +#define arm_negx(p, rd, rn) arm_negx_shift ((p), (rd), (rn), 0, 0) +#define arm_negw(p, rd, rn) arm_negw_shift ((p), (rd), (rn), 0, 0) + +/* Arithmetic with carry */ +#define arm_format_adc(p, sf, op, S, rd, rn, rm) arm_emit ((p), ((sf) << 31) | ((op) << 30) | ((S) << 29) | (0xd0 << 21) | ((rm) << 16) | (0x0 << 10) | ((rn) << 5) | ((rd) << 0)) + +#define arm_adcx(p, rd, rn, rm) arm_format_adc ((p), 0x1, 0x0, 0x0, (rd), (rn), (rm)) +#define arm_adcw(p, rd, rn, rm) arm_format_adc ((p), 0x0, 0x0, 0x0, (rd), (rn), (rm)) +#define arm_adcsx(p, rd, rn, rm) arm_format_adc ((p), 0x1, 0x0, 0x1, (rd), (rn), (rm)) +#define arm_adcsw(p, rd, rn, rm) arm_format_adc ((p), 0x0, 0x0, 0x1, (rd), (rn), (rm)) +#define arm_sbcx(p, rd, rn, rm) arm_format_adc ((p), 0x1, 0x1, 0x0, (rd), (rn), (rm)) +#define arm_sbcw(p, rd, rn, rm) arm_format_adc ((p), 0x0, 0x1, 0x0, (rd), (rn), (rm)) +#define arm_sbcsx(p, rd, rn, rm) arm_format_adc ((p), 0x1, 0x1, 0x1, (rd), (rn), (rm)) +#define arm_sbcsw(p, rd, rn, rm) arm_format_adc ((p), 0x0, 0x1, 0x1, (rd), (rn), (rm)) +#define arm_ngcx(p, rd, rm) arm_sbcx ((p), (rd), ARMREG_RZR, (rm)) +#define arm_ngcw(p, rd, rm) arm_sbcw ((p), (rd), ARMREG_RZR, (rm)) +#define arm_ngcsx(p, rd, rm) arm_sbcsx ((p), (rd), ARMREG_RZR, (rm)) +#define arm_ngcsw(p, rd, rm) arm_sbcsw ((p), (rd), ARMREG_RZR, (rm)) + +/* Logical (shifted register) */ +#define arm_format_logical_shift(p, sf, op, N, rd, rn, rm, shift, imm6) arm_emit ((p), ((sf) << 31) | ((op) << 29) | (0xa << 24) | ((shift) << 22) | ((N) << 21) | ((rm) << 16) | ((imm6) << 10) | ((rn) << 5) | ((rd) << 0)) + +#define arm_andx_shift(p, rd, rn, rm, shift_type, amount) arm_format_logical_shift ((p), 0x1, 0x0, 0x0, (rd), (rn), (rm), (shift_type), (amount)) +#define arm_andw_shift(p, rd, rn, rm, shift_type, amount) arm_format_logical_shift ((p), 0x0, 0x0, 0x0, (rd), (rn), (rm), (shift_type), (amount)) +#define arm_andsx_shift(p, rd, rn, rm, shift_type, amount) arm_format_logical_shift ((p), 0x1, 0x3, 0x0, (rd), (rn), (rm), (shift_type), (amount)) +#define arm_andsw_shift(p, rd, rn, rm, shift_type, amount) arm_format_logical_shift ((p), 0x0, 0x3, 0x0, (rd), (rn), (rm), (shift_type), (amount)) +#define arm_bicx_shift(p, rd, rn, rm, shift_type, amount) arm_format_logical_shift ((p), 0x1, 0x0, 0x1, (rd), (rn), (rm), (shift_type), (amount)) +#define arm_bicw_shift(p, rd, rn, rm, shift_type, amount) arm_format_logical_shift ((p), 0x0, 0x0, 0x1, (rd), (rn), (rm), (shift_type), (amount)) +#define arm_bicsx_shift(p, rd, rn, rm, shift_type, amount) arm_format_logical_shift ((p), 0x1, 0x3, 0x1, (rd), (rn), (rm), (shift_type), (amount)) +#define arm_bicsw_shift(p, rd, rn, rm, shift_type, amount) arm_format_logical_shift ((p), 0x0, 0x3, 0x1, (rd), (rn), (rm), (shift_type), (amount)) +#define arm_eonx_shift(p, rd, rn, rm, shift_type, amount) arm_format_logical_shift ((p), 0x1, 0x2, 0x1, (rd), (rn), (rm), (shift_type), (amount)) +#define arm_eonw_shift(p, rd, rn, rm, shift_type, amount) arm_format_logical_shift ((p), 0x0, 0x2, 0x1, (rd), (rn), (rm), (shift_type), (amount)) +#define arm_eorx_shift(p, rd, rn, rm, shift_type, amount) arm_format_logical_shift ((p), 0x1, 0x2, 0x0, (rd), (rn), (rm), (shift_type), (amount)) +#define arm_eorw_shift(p, rd, rn, rm, shift_type, amount) arm_format_logical_shift ((p), 0x0, 0x2, 0x0, (rd), (rn), (rm), (shift_type), (amount)) +#define arm_orrx_shift(p, rd, rn, rm, shift_type, amount) arm_format_logical_shift ((p), 0x1, 0x1, 0x0, (rd), (rn), (rm), (shift_type), (amount)) +#define arm_orrw_shift(p, rd, rn, rm, shift_type, amount) arm_format_logical_shift ((p), 0x0, 0x1, 0x0, (rd), (rn), (rm), (shift_type), (amount)) +#define arm_ornx_shift(p, rd, rn, rm, shift_type, amount) arm_format_logical_shift ((p), 0x1, 0x1, 0x1, (rd), (rn), (rm), (shift_type), (amount)) +#define arm_ornw_shift(p, rd, rn, rm, shift_type, amount) arm_format_logical_shift ((p), 0x0, 0x1, 0x1, (rd), (rn), (rm), (shift_type), (amount)) +#define arm_mvnx_shift(p, rd, rm, shift_type, amount) arm_ornx_shift ((p), (rd), ARMREG_RZR, (rm), (shift_type), (amount)) +#define arm_mvnw_shift(p, rd, rm, shift_type, amount) arm_ornw_shift ((p), (rd), ARMREG_RZR, (rm), (shift_type), (amount)) +#define arm_tstx_shift(p, rn, rm, shift_type, amount) arm_andsx_shift ((p), ARMREG_RZR, (rn), (rm), (shift_type), (amount)) +#define arm_tstw_shift(p, rn, rm, shift_type, amount) arm_andsw_shift ((p), ARMREG_RZR, (rn), (rm), (shift_type), (amount)) +/* Aliases */ +#define arm_andx(p, rd, rn, rm) arm_andx_shift(p, rd, rn, rm, ARMSHIFT_LSL, 0) +#define arm_andw(p, rd, rn, rm) arm_andw_shift(p, rd, rn, rm, ARMSHIFT_LSL, 0) +#define arm_andsx(p, rd, rn, rm) arm_andsx_shift(p, rd, rn, rm, ARMSHIFT_LSL, 0) +#define arm_andsw(p, rd, rn, rm) arm_andsw_shift(p, rd, rn, rm, ARMSHIFT_LSL, 0) +#define arm_bixx(p, rd, rn, rm) arm_bixx_shift(p, rd, rn, rm, ARMSHIFT_LSL, 0) +#define arm_bixw(p, rd, rn, rm) arm_bixw_shift(p, rd, rn, rm, ARMSHIFT_LSL, 0) +#define arm_bixsx(p, rd, rn, rm) arm_bixsx_shift(p, rd, rn, rm, ARMSHIFT_LSL, 0) +#define arm_bixsw(p, rd, rn, rm) arm_bixsw_shift(p, rd, rn, rm, ARMSHIFT_LSL, 0) +#define arm_eonx(p, rd, rn, rm) arm_eonx_shift(p, rd, rn, rm, ARMSHIFT_LSL, 0) +#define arm_eonw(p, rd, rn, rm) arm_eonw_shift(p, rd, rn, rm, ARMSHIFT_LSL, 0) +#define arm_eorx(p, rd, rn, rm) arm_eorx_shift(p, rd, rn, rm, ARMSHIFT_LSL, 0) +#define arm_eorw(p, rd, rn, rm) arm_eorw_shift(p, rd, rn, rm, ARMSHIFT_LSL, 0) +#define arm_orrx(p, rd, rn, rm) arm_orrx_shift(p, rd, rn, rm, ARMSHIFT_LSL, 0) +#define arm_orrw(p, rd, rn, rm) arm_orrw_shift(p, rd, rn, rm, ARMSHIFT_LSL, 0) +#define arm_ornx(p, rd, rn, rm) arm_ornx_shift(p, rd, rn, rm, ARMSHIFT_LSL, 0) +#define arm_ornw(p, rd, rn, rm) arm_ornw_shift(p, rd, rn, rm, ARMSHIFT_LSL, 0) +#define arm_mvnx(p, rd, rm) arm_mvnx_shift(p, rd, rm, ARMSHIFT_LSL, 0) +#define arm_mvnw(p, rd, rm) arm_mvnw_shift(p, rd, rm, ARMSHIFT_LSL, 0) +#define arm_tstx(p, rn, rm) arm_tstx_shift(p, rn, rm, ARMSHIFT_LSL, 0) +#define arm_tstw(p, rn, rm) arm_tstw_shift(p, rn, rm, ARMSHIFT_LSL, 0) + +/* Move (register) */ +#define arm_movx(p, rn, rm) arm_orrx_shift ((p), (rn), ARMREG_RZR, (rm), ARMSHIFT_LSL, 0) +#define arm_movw(p, rn, rm) arm_orrw_shift ((p), (rn), ARMREG_RZR, (rm), ARMSHIFT_LSL, 0) + +/* Not an official alias */ +#define arm_movspx(p, rn, rm) arm_addx_imm ((p), (rn), (rm), 0) + +/* Shift (register) */ +#define arm_format_shift_reg(p, sf, op2, rd, rn, rm) arm_emit ((p), ((sf) << 31) | (0xd6 << 21) | ((rm) << 16) | (0x2 << 12) | ((op2) << 10) | ((rn) << 5) | ((rd) << 0)) + +#define arm_asrvx(p, rd, rn, rm) arm_format_shift_reg ((p), 0x1, 0x2, (rd), (rn), (rm)) +#define arm_asrvw(p, rd, rn, rm) arm_format_shift_reg ((p), 0x0, 0x2, (rd), (rn), (rm)) +#define arm_lslvx(p, rd, rn, rm) arm_format_shift_reg ((p), 0x1, 0x0, (rd), (rn), (rm)) +#define arm_lslvw(p, rd, rn, rm) arm_format_shift_reg ((p), 0x0, 0x0, (rd), (rn), (rm)) +#define arm_lsrvx(p, rd, rn, rm) arm_format_shift_reg ((p), 0x1, 0x1, (rd), (rn), (rm)) +#define arm_lsrvw(p, rd, rn, rm) arm_format_shift_reg ((p), 0x0, 0x1, (rd), (rn), (rm)) +#define arm_rorvx(p, rd, rn, rm) arm_format_shift_reg ((p), 0x1, 0x3, (rd), (rn), (rm)) +#define arm_rorvw(p, rd, rn, rm) arm_format_shift_reg ((p), 0x0, 0x3, (rd), (rn), (rm)) + +/* Multiply */ +#define arm_format_mul(p, sf, o0, rd, rn, rm, ra) arm_emit ((p), ((sf) << 31) | (0x0 << 29) | (0x1b << 24) | (0x0 << 21) | ((rm) << 16) | ((o0) << 15) | ((ra) << 10) | ((rn) << 5) | ((rd) << 0)) + +#define arm_maddx(p, rd, rn, rm, ra) arm_format_mul((p), 0x1, 0x0, (rd), (rn), (rm), (ra)) +#define arm_maddw(p, rd, rn, rm, ra) arm_format_mul((p), 0x0, 0x0, (rd), (rn), (rm), (ra)) +#define arm_msubx(p, rd, rn, rm, ra) arm_format_mul((p), 0x1, 0x1, (rd), (rn), (rm), (ra)) +#define arm_msubw(p, rd, rn, rm, ra) arm_format_mul((p), 0x0, 0x1, (rd), (rn), (rm), (ra)) +#define arm_mnegx(p, rd, rn, rm) arm_msubx ((p), (rd), (rn), (rm), ARMREG_RZR) +#define arm_mnegw(p, rd, rn, rm) arm_msubw ((p), (rd), (rn), (rm), ARMREG_RZR) +#define arm_mulx(p, rd, rn, rm) arm_maddx ((p), (rd), (rn), (rm), ARMREG_RZR) +#define arm_mulw(p, rd, rn, rm) arm_maddw ((p), (rd), (rn), (rm), ARMREG_RZR) + +/* FIXME: Missing multiple opcodes */ + +/* Division */ +#define arm_format_div(p, sf, o1, rd, rn, rm) arm_emit ((p), ((sf) << 31) | (0xd6 << 21) | ((rm) << 16) | (0x1 << 11) | ((o1) << 10) | ((rn) << 5) | ((rd) << 0)) + +#define arm_sdivx(p, rd, rn, rm) arm_format_div ((p), 0x1, 0x1, (rd), (rn), (rm)) +#define arm_sdivw(p, rd, rn, rm) arm_format_div ((p), 0x0, 0x1, (rd), (rn), (rm)) +#define arm_udivx(p, rd, rn, rm) arm_format_div ((p), 0x1, 0x0, (rd), (rn), (rm)) +#define arm_udivw(p, rd, rn, rm) arm_format_div ((p), 0x0, 0x0, (rd), (rn), (rm)) + +/* Conditional select */ +#define arm_format_csel(p, sf, op, op2, cond, rd, rn, rm) arm_emit ((p), ((sf) << 31) | ((op) << 30) | (0xd4 << 21) | ((rm) << 16) | ((cond) << 12) | ((op2) << 10) | ((rn) << 5) | ((rd) << 0)) + +#define arm_cselx(p, cond, rd, rn, rm) arm_format_csel ((p), 0x1, 0x0, 0x0, (cond), (rd), (rn), (rm)) +#define arm_cselw(p, cond, rd, rn, rm) arm_format_csel ((p), 0x0, 0x0, 0x0, (cond), (rd), (rn), (rm)) +#define arm_csincx(p, cond, rd, rn, rm) arm_format_csel ((p), 0x1, 0x0, 0x1, (cond), (rd), (rn), (rm)) +#define arm_csincw(p, cond, rd, rn, rm) arm_format_csel ((p), 0x0, 0x0, 0x1, (cond), (rd), (rn), (rm)) +#define arm_csinvx(p, cond, rd, rn, rm) arm_format_csel ((p), 0x1, 0x1, 0x0, (cond), (rd), (rn), (rm)) +#define arm_csinvw(p, cond, rd, rn, rm) arm_format_csel ((p), 0x0, 0x1, 0x0, (cond), (rd), (rn), (rm)) +#define arm_csnegx(p, cond, rd, rn, rm) arm_format_csel ((p), 0x1, 0x1, 0x1, (cond), (rd), (rn), (rm)) +#define arm_csnegw(p, cond, rd, rn, rm) arm_format_csel ((p), 0x0, 0x1, 0x1, (cond), (rd), (rn), (rm)) + +#define arm_cset(p, cond, rd) arm_csincx ((p), ((cond) ^ 0x1), (rd), ARMREG_RZR, ARMREG_RZR) + +/* C5.6.68 (HINT) */ +#define arm_hint(p, imm) arm_emit ((p), (0xd5032 << 12) | ((imm) << 5) | (0x1f << 0)) +#define arm_nop(p) arm_hint ((p), 0x0) + +/* C5.6.29 BRK */ +#define arm_brk(p, imm) arm_emit ((p), (0xd4 << 24) | (0x1 << 21) | ((imm) << 5)) + +/* C6.3.114 FMOV (General) */ +#define arm_format_fmov_gr(p, sf, type, rmode, opcode, rn, rd) arm_emit ((p), ((sf) << 31) | (0x1e << 24) | ((type) << 22) | (0x1 << 21) | ((rmode) << 19) | ((opcode) << 16) | ((rn) << 5) | ((rd) << 0)) + +/* Move gr->vfp */ +#define arm_fmov_rx_to_double(p, dd, xn) arm_format_fmov_gr ((p), 0x1, 0x1, 0x0, 0x7, (xn), (dd)) + +/* Move vfp->gr */ +#define arm_fmov_double_to_rx(p, xd, dn) arm_format_fmov_gr ((p), 0x1, 0x1, 0x0, 0x6, (dn), (xd)) + +/* C6.3.113 FMOV (register) */ +#define arm_format_fmov(p, type, rn, rd) arm_emit ((p), (0x1e << 24) | ((type) << 22) | (0x1 << 21) | (0x10 << 10) | ((rn) << 5) | ((rd) << 0)) + +#define arm_fmovd(p, dd, dn) arm_format_fmov ((p), 0x1, (dn), (dd)) +#define arm_fmovs(p, dd, dn) arm_format_fmov ((p), 0x0, (dn), (dd)) + +/* C6.3.54 FCMP */ +#define arm_format_fcmp(p, type, opc, rn, rm) arm_emit ((p), (0x1e << 24) | ((type) << 22) | (0x1 << 21) | ((rm) << 16) | (0x8 << 10) | ((rn) << 5) | ((opc) << 3)) + +#define arm_fcmpd(p, dn, dm) arm_format_fcmp (p, 0x1, 0x0, (dn), (dm)) +#define arm_fcmps(p, dn, dm) arm_format_fcmp (p, 0x0, 0x0, (dn), (dm)) + +/* Float precision */ +#define arm_format_fcvt(p, type, opc, rn, rd) arm_emit ((p), (0x1e << 24) | ((type) << 22) | (0x1 << 21) | (0x1 << 17) | ((opc) << 15) | (0x10 << 10) | ((rn) << 5) | ((rd) << 0)) + +/* C6.3.57 FCVT */ +/* single->double */ +#define arm_fcvt_sd(p, dd, sn) arm_format_fcvt ((p), 0x0, 0x1, (sn), (dd)) +/* double->single */ +#define arm_fcvt_ds(p, sd, dn) arm_format_fcvt ((p), 0x1, 0x0, (dn), (sd)) + +/* Float conversion to integer conversion */ +#define arm_format_fcvtz(p, sf, type, rmode, opcode, rn, rd) arm_emit ((p), ((sf) << 31) | (0x1e << 24) | ((type) << 22) | (0x1 << 21) | ((rmode) << 19) | ((opcode) << 16) | ((rn) << 5) | ((rd) << 0)) + +/* C6.3.80 FCVTZS (scalar, integer) */ +#define arm_fcvtzs_dw(p, rd, rn) arm_format_fcvtz ((p), 0x0, 0x1, 0x3, 0x0, (rn), (rd)) +#define arm_fcvtzs_dx(p, rd, rn) arm_format_fcvtz ((p), 0x1, 0x1, 0x3, 0x0, (rn), (rd)) +#define arm_fcvtzs_sw(p, rd, rn) arm_format_fcvtz ((p), 0x0, 0x0, 0x3, 0x0, (rn), (rd)) +#define arm_fcvtzs_sx(p, rd, rn) arm_format_fcvtz ((p), 0x1, 0x0, 0x3, 0x0, (rn), (rd)) + +/* C6.3.84 FCVTZU (scalar, integer) */ +#define arm_fcvtzu_dw(p, rd, rn) arm_format_fcvtz ((p), 0x0, 0x1, 0x3, 0x1, (rn), (rd)) +#define arm_fcvtzu_dx(p, rd, rn) arm_format_fcvtz ((p), 0x1, 0x1, 0x3, 0x1, (rn), (rd)) +#define arm_fcvtzu_sw(p, rd, rn) arm_format_fcvtz ((p), 0x0, 0x0, 0x3, 0x1, (rn), (rd)) +#define arm_fcvtzu_sx(p, rd, rn) arm_format_fcvtz ((p), 0x1, 0x0, 0x3, 0x1, (rn), (rd)) + +/* C6.3.208 SCVTF (vector, integer) */ +#define arm_format_scvtf_vector(p, sz, rn, rd) arm_emit ((p), (0x1 << 30) | (0x0 << 29) | (0x1e << 24) | ((sz) << 22) | (0x10 << 17) | (0x1d << 12) | (0x2 << 10) | ((rn) << 5) | ((rd) << 0)) + +#define arm_scvtf_d(p, dd, dn) arm_format_scvtf_vector ((p), 0x1, (dn), (dd)) +#define arm_scvtf_s(p, sd, sn) arm_format_scvtf_vector ((p), 0x0, (sn), (sd)) + +/* C6.3.210 SCVTF (scalar, integer) */ +#define arm_format_scvtf_scalar(p, sf, type, rn, rd) arm_emit ((p), ((sf) << 31) | (0x1e << 24) | ((type) << 22) | (0x1 << 21) | (0x2 << 16) | (0x0 << 10) | ((rn) << 5) | ((rd) << 0)) + +#define arm_scvtf_rx_to_d(p, dd, rn) arm_format_scvtf_scalar ((p), 0x1, 0x1, rn, dd) +#define arm_scvtf_rw_to_d(p, dd, rn) arm_format_scvtf_scalar ((p), 0x0, 0x1, rn, dd) +#define arm_scvtf_rx_to_s(p, dd, rn) arm_format_scvtf_scalar ((p), 0x1, 0x0, rn, dd) +#define arm_scvtf_rw_to_s(p, dd, rn) arm_format_scvtf_scalar ((p), 0x0, 0x0, rn, dd) + +/* C6.3.306 UCVTF (vector, integer) */ +#define arm_format_ucvtf_vector(p, sz, rn, rd) arm_emit ((p), (0x1 << 30) | (0x1 << 29) | (0x1e << 24) | ((sz) << 22) | (0x10 << 17) | (0x1d << 12) | (0x2 << 10) | ((rn) << 5) | ((rd) << 0)) + +#define arm_ucvtf_d(p, dd, dn) arm_format_ucvtf_vector ((p), 0x1, (dn), (dd)) +#define arm_ucvtf_s(p, sd, sn) arm_format_ucvtf_vector ((p), 0x0, (sn), (sd)) + +/* C6.3.308 UCVTF (scalar, integer) */ +#define arm_format_ucvtf_scalar(p, sf, type, rn, rd) arm_emit ((p), ((sf) << 31) | (0x1e << 24) | ((type) << 22) | (0x1 << 21) | (0x3 << 16) | (0x0 << 10) | ((rn) << 5) | ((rd) << 0)) + +#define arm_ucvtf_rx_to_d(p, dd, rn) arm_format_ucvtf_scalar ((p), 0x1, 0x1, rn, dd) +#define arm_ucvtf_rw_to_d(p, dd, rn) arm_format_ucvtf_scalar ((p), 0x0, 0x1, rn, dd) + +/* C6.3.41 FADD (scalar) */ +#define arm_format_fadd_scalar(p, type, rd, rn, rm) arm_emit ((p), (0x1e << 24) | ((type) << 22) | (0x1 << 21) | ((rm) << 16) | (0x1 << 13) | (0x2 << 10) | ((rn) << 5) | ((rd) << 0)) + +#define arm_fadd_d(p, rd, rn, rm) arm_format_fadd_scalar ((p), 0x1, (rd), (rn), (rm)) +#define arm_fadd_s(p, rd, rn, rm) arm_format_fadd_scalar ((p), 0x0, (rd), (rn), (rm)) + +/* C6.3.149 FSUB (scalar) */ +#define arm_format_fsub_scalar(p, type, rd, rn, rm) arm_emit ((p), (0x1e << 24) | ((type) << 22) | (0x1 << 21) | ((rm) << 16) | (0x1 << 13) | (0x1 << 12) | (0x2 << 10) | ((rn) << 5) | ((rd) << 0)) + +#define arm_fsub_d(p, rd, rn, rm) arm_format_fsub_scalar ((p), 0x1, (rd), (rn), (rm)) +#define arm_fsub_s(p, rd, rn, rm) arm_format_fsub_scalar ((p), 0x0, (rd), (rn), (rm)) + +/* C6.3.119 FMUL (scalar) */ +#define arm_format_fmul_scalar(p, type, rd, rn, rm) arm_emit ((p), (0x1e << 24) | ((type) << 22) | (0x1 << 21) | ((rm) << 16) | (0x2 << 10) | ((rn) << 5) | ((rd) << 0)) + +#define arm_fmul_d(p, rd, rn, rm) arm_format_fmul_scalar ((p), 0x1, (rd), (rn), (rm)) +#define arm_fmul_s(p, rd, rn, rm) arm_format_fmul_scalar ((p), 0x0, (rd), (rn), (rm)) + +/* C6.3.86 FDIV (scalar) */ +#define arm_format_fdiv_scalar(p, type, rd, rn, rm) arm_emit ((p), (0x1e << 24) | ((type) << 22) | (0x1 << 21) | ((rm) << 16) | (0x1 << 12) | (0x2 << 10) | ((rn) << 5) | ((rd) << 0)) + +#define arm_fdiv_d(p, rd, rn, rm) arm_format_fdiv_scalar ((p), 0x1, (rd), (rn), (rm)) +#define arm_fdiv_s(p, rd, rn, rm) arm_format_fdiv_scalar ((p), 0x0, (rd), (rn), (rm)) + +/* C6.3.116 FMSUB */ +#define arm_format_fmsub(p, type, rd, rn, rm, ra) arm_emit ((p), (0x1f << 24) | ((type) << 22) | (0x0 << 21) | ((rm) << 16) | (0x1 << 15) | ((ra) << 10) | ((rn) << 5) | ((rd) << 0)) + +#define arm_fmsub_d(p, rd, rn, rm, ra) arm_format_fmsub ((p), 0x1, (rd), (rn), (rm), (ra)) + +/* C6.3.123 FNEG */ +#define arm_format_fneg(p, type, rd, rn) arm_emit ((p), (0x1e << 24) | ((type) << 22) | (0x1 << 21) | (0x2 << 15) | (0x10 << 10) | ((rn) << 5) | ((rd) << 0)) + +#define arm_fneg_d(p, rd, rn) arm_format_fneg ((p), 0x1, (rd), (rn)) +#define arm_fneg_s(p, rd, rn) arm_format_fneg ((p), 0x0, (rd), (rn)) + +/* C6.3.37 FABS (scalar) */ +#define arm_format_fabs(p, type, opc, rd, rn) arm_emit ((p), (0x1e << 24) | ((type) << 22) | (0x1 << 21) | ((opc) << 15) | (0x10 << 10) | ((rn) << 5) | ((rd) << 0)) + +#define arm_fabs_d(p, rd, rn) arm_format_fabs ((p), 0x1, 0x1, (rd), (rn)) + +/* C5.6.60 DMB */ +#define arm_format_dmb(p, opc, CRm) arm_emit ((p), (0x354 << 22) | (0x3 << 16) | (0x3 << 12) | ((CRm) << 8) | (0x1 << 7) | ((opc) << 5) | (0x1f << 0)) + +#define ARM_DMB_OSHLD 0x1 +#define ARM_DMB_OSHST 0x2 +#define ARM_DMB_OSH 0x3 +#define ARM_DMB_NSHLD 0x5 +#define ARM_DMB_NSHST 0x6 +#define ARM_DMB_NSH 0x7 +#define ARM_DMB_ISHLD 0x9 +#define ARM_DMB_ISHST 0xa +#define ARM_DMB_ISH 0xb +#define ARM_DMB_LD 0xd +#define ARM_DMB_ST 0xe +#define ARM_DMB_SY 0xf + +#define arm_dmb(p, imm) arm_format_dmb ((p), 0x1, (imm)) + +/* C5.6.129 MRS */ + +#define ARM_MRS_REG_TPIDR_EL0 0x5e82 + +#define arm_format_mrs(p, sysreg, rt) arm_emit ((p), (0x354 << 22) | (0x1 << 21) | (0x1 << 20) | ((sysreg) << 5) | ((rt) << 0)) + +#define arm_mrs(p, rt, sysreg) arm_format_mrs ((p), (sysreg), (rt)) + +#endif /* __arm_CODEGEN_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/arch/arm64/codegen-test.c b/unity-2019.4.24f1-mbe/mono/arch/arm64/codegen-test.c new file mode 100644 index 000000000..bb9d9dcde --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/arm64/codegen-test.c @@ -0,0 +1,424 @@ +#include +#include "glib.h" + +int +main (int argc, char *argv []) +{ + guint8 buf [4096]; + guint8 *code; + int i; + + code = buf; + + arm_nop (code); + arm_brx (code, ARMREG_R1); + arm_blrx (code, ARMREG_R1); + arm_retx (code, ARMREG_R1); + + arm_nop (code); + arm_b (code, code + 4); + arm_b (code, code); + arm_b (code, code - 4); + arm_bl (code, code + 4); + arm_bl (code, code); + arm_bl (code, code - 4); + + arm_nop (code); + arm_bcc (code, ARMCOND_NE, code + 4); + arm_bcc (code, ARMCOND_NE, code); + arm_bcc (code, ARMCOND_NE, code - 4); + arm_cbzx (code, ARMREG_R1, code + 4); + arm_cbzx (code, ARMREG_R1, code); + arm_cbzx (code, ARMREG_R1, code - 4); + arm_cbzw (code, ARMREG_R1, code + 4); + arm_cbzw (code, ARMREG_R1, code); + arm_cbzw (code, ARMREG_R1, code - 4); + arm_cbnzx (code, ARMREG_R1, code + 4); + arm_cbnzx (code, ARMREG_R1, code); + arm_cbnzx (code, ARMREG_R1, code - 4); + arm_cbnzw (code, ARMREG_R1, code + 4); + arm_cbnzw (code, ARMREG_R1, code); + arm_cbnzw (code, ARMREG_R1, code - 4); + arm_tbz (code, ARMREG_R1, 1, code + 4); + arm_tbz (code, ARMREG_R1, 1, code); + arm_tbz (code, ARMREG_R1, 1, code - 4); + arm_tbz (code, ARMREG_R1, 33, code + 4); + arm_tbz (code, ARMREG_R1, 33, code); + arm_tbz (code, ARMREG_R1, 33, code - 4); + arm_tbnz (code, ARMREG_R1, 1, code + 4); + arm_tbnz (code, ARMREG_R1, 1, code); + arm_tbnz (code, ARMREG_R1, 1, code - 4); + arm_tbnz (code, ARMREG_R1, 33, code + 4); + arm_tbnz (code, ARMREG_R1, 33, code); + arm_tbnz (code, ARMREG_R1, 33, code - 4); + + arm_nop (code); + arm_ldrx (code, ARMREG_R1, ARMREG_R2, 0); + arm_ldrx (code, ARMREG_R1, ARMREG_R2, 16); + arm_ldrw (code, ARMREG_R1, ARMREG_R2, 0); + arm_ldrw (code, ARMREG_R1, ARMREG_R2, 16); + arm_ldrb (code, ARMREG_R1, ARMREG_R2, 0); + arm_ldrb (code, ARMREG_R1, ARMREG_R2, 16); + arm_ldrh (code, ARMREG_R1, ARMREG_R2, 0); + arm_ldrh (code, ARMREG_R1, ARMREG_R2, 16); + arm_ldrsbx (code, ARMREG_R1, ARMREG_R2, 0); + arm_ldrsbx (code, ARMREG_R1, ARMREG_R2, 16); + arm_ldrsbw (code, ARMREG_R1, ARMREG_R2, 0); + arm_ldrsbw (code, ARMREG_R1, ARMREG_R2, 16); + arm_ldrshx (code, ARMREG_R1, ARMREG_R2, 0); + arm_ldrshx (code, ARMREG_R1, ARMREG_R2, 16); + arm_ldrshw (code, ARMREG_R1, ARMREG_R2, 0); + arm_ldrshw (code, ARMREG_R1, ARMREG_R2, 16); + arm_ldrswx (code, ARMREG_R1, ARMREG_R2, 0); + arm_ldrswx (code, ARMREG_R1, ARMREG_R2, 16); + arm_strx (code, ARMREG_R1, ARMREG_R2, 0); + arm_strx (code, ARMREG_R1, ARMREG_R2, 16); + arm_strw (code, ARMREG_R1, ARMREG_R2, 0); + arm_strw (code, ARMREG_R1, ARMREG_R2, 16); + arm_strh (code, ARMREG_R1, ARMREG_R2, 0); + arm_strh (code, ARMREG_R1, ARMREG_R2, 16); + arm_strb (code, ARMREG_R1, ARMREG_R2, 0); + arm_strb (code, ARMREG_R1, ARMREG_R2, 16); + + arm_nop (code); + arm_ldrx_post (code, ARMREG_R1, ARMREG_R2, 0); + arm_ldrx_post (code, ARMREG_R1, ARMREG_R2, 16); + arm_ldrx_post (code, ARMREG_R1, ARMREG_R2, -16); + arm_ldrw_post (code, ARMREG_R1, ARMREG_R2, 0); + arm_ldrw_post (code, ARMREG_R1, ARMREG_R2, 16); + arm_ldrw_post (code, ARMREG_R1, ARMREG_R2, -16); + arm_strx_post (code, ARMREG_R1, ARMREG_R2, 0); + arm_strx_post (code, ARMREG_R1, ARMREG_R2, 16); + arm_strx_post (code, ARMREG_R1, ARMREG_R2, -16); + arm_strw_post (code, ARMREG_R1, ARMREG_R2, 0); + arm_strw_post (code, ARMREG_R1, ARMREG_R2, 16); + arm_strw_post (code, ARMREG_R1, ARMREG_R2, -16); + + arm_nop (code); + arm_ldrx_pre (code, ARMREG_R1, ARMREG_R2, 0); + arm_ldrx_pre (code, ARMREG_R1, ARMREG_R2, 16); + arm_ldrx_pre (code, ARMREG_R1, ARMREG_R2, -16); + arm_ldrw_pre (code, ARMREG_R1, ARMREG_R2, 0); + arm_ldrw_pre (code, ARMREG_R1, ARMREG_R2, 16); + arm_ldrw_pre (code, ARMREG_R1, ARMREG_R2, -16); + arm_strx_pre (code, ARMREG_R1, ARMREG_R2, 0); + arm_strx_pre (code, ARMREG_R1, ARMREG_R2, 16); + arm_strx_pre (code, ARMREG_R1, ARMREG_R2, -16); + arm_strw_pre (code, ARMREG_R1, ARMREG_R2, 0); + arm_strw_pre (code, ARMREG_R1, ARMREG_R2, 16); + arm_strw_pre (code, ARMREG_R1, ARMREG_R2, -16); + + arm_nop (code); + arm_ldrx_reg (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_ldrw_reg (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_ldrb_reg (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_ldrh_reg (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_ldrsbx_reg (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_ldrsbw_reg (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_ldrshx_reg (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_ldrshw_reg (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_ldrswx_reg (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_strx_reg (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_strw_reg (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_strh_reg (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_strb_reg (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + + arm_nop (code); + arm_ldrx_lit (code, ARMREG_R1, code + 4); + arm_ldrx_lit (code, ARMREG_R1, code); + arm_ldrx_lit (code, ARMREG_R1, code - 4); + arm_ldrw_lit (code, ARMREG_R1, code + 4); + arm_ldrw_lit (code, ARMREG_R1, code); + arm_ldrw_lit (code, ARMREG_R1, code - 4); + arm_ldrswx_lit (code, ARMREG_R1, code + 4); + arm_ldrswx_lit (code, ARMREG_R1, code); + arm_ldrswx_lit (code, ARMREG_R1, code - 4); + + arm_nop (code); + arm_ldpx (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, 0); + arm_ldpx (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, 16); + arm_ldpx (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, -16); + arm_ldpw (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, 0); + arm_ldpw (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, 16); + arm_ldpw (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, -16); + arm_ldpsw (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, 0); + arm_ldpsw (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, 16); + arm_ldpsw (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, -16); + arm_stpx (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, 0); + arm_stpx (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, 16); + arm_stpx (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, -16); + arm_stpw (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, 0); + arm_stpw (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, 16); + arm_stpw (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, -16); + + arm_nop (code); + arm_ldpx_pre (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, 0); + arm_ldpx_pre (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, 16); + arm_ldpx_pre (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, -16); + arm_ldpw_pre (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, 0); + arm_ldpw_pre (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, 16); + arm_ldpw_pre (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, -16); + arm_ldpsw_pre (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, 0); + arm_ldpsw_pre (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, 16); + arm_ldpsw_pre (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, -16); + + arm_nop (code); + arm_ldpx_post (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, 0); + arm_ldpx_post (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, 16); + arm_ldpx_post (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, -16); + arm_ldpw_post (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, 0); + arm_ldpw_post (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, 16); + arm_ldpw_post (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, -16); + arm_ldpsw_post (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, 0); + arm_ldpsw_post (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, 16); + arm_ldpsw_post (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, -16); + + arm_nop (code); + arm_ldxrx (code, ARMREG_R1, ARMREG_R2); + arm_ldxrw (code, ARMREG_R1, ARMREG_R2); + arm_ldxrh (code, ARMREG_R1, ARMREG_R2); + arm_ldxrb (code, ARMREG_R1, ARMREG_R2); + arm_ldxpx (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_ldxpw (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_stxrx (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_stxrw (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_stxrh (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_stxrb (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_stxpx (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, ARMREG_R4); + arm_stxpw (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, ARMREG_R4); + + // fixme: immeditate tests + arm_nop (code); + arm_addx_imm (code, ARMREG_R1, ARMREG_R2, 16); + arm_addw_imm (code, ARMREG_R1, ARMREG_R2, 16); + arm_addsx_imm (code, ARMREG_R1, ARMREG_R2, 16); + arm_addsw_imm (code, ARMREG_R1, ARMREG_R2, 16); + arm_subx_imm (code, ARMREG_R1, ARMREG_R2, 16); + arm_subw_imm (code, ARMREG_R1, ARMREG_R2, 16); + arm_subsx_imm (code, ARMREG_R1, ARMREG_R2, 16); + arm_subsw_imm (code, ARMREG_R1, ARMREG_R2, 16); + arm_cmpx_imm (code, ARMREG_R1, 16); + arm_cmpw_imm (code, ARMREG_R1, 16); + arm_cmnx_imm (code, ARMREG_R1, 16); + arm_cmnw_imm (code, ARMREG_R1, 16); + +#if 0 + // fixme: bitmasks + // fixme: bitmask tests + arm_nop (code); + arm_andx_imm (code, ARMREG_R1, ARMREG_R2, 1); + arm_andw_imm (code, ARMREG_R1, ARMREG_R2, 1); + arm_andsx_imm (code, ARMREG_R1, ARMREG_R2, 1); + arm_andsw_imm (code, ARMREG_R1, ARMREG_R2, 1); + arm_eorx_imm (code, ARMREG_R1, ARMREG_R2, 1); + arm_eorw_imm (code, ARMREG_R1, ARMREG_R2, 1); + arm_orrx_imm (code, ARMREG_R1, ARMREG_R2, 1); + arm_orrw_imm (code, ARMREG_R1, ARMREG_R2, 1); + arm_tstx_imm (code, ARMREG_R1, 1); + arm_tstw_imm (code, ARMREG_R1, 1); +#endif + + arm_nop (code); + arm_movzx (code, ARMREG_R1, 16, 0); + arm_movzx (code, ARMREG_R1, 16, 16); + arm_movzx (code, ARMREG_R1, 16, 32); + arm_movzx (code, ARMREG_R1, 16, 48); + arm_movzw (code, ARMREG_R1, 16, 0); + arm_movzw (code, ARMREG_R1, 16, 16); + arm_movzw (code, ARMREG_R1, 16, 32); + arm_movzw (code, ARMREG_R1, 16, 48); + arm_movnx (code, ARMREG_R1, 16, 0); + arm_movnx (code, ARMREG_R1, 16, 16); + arm_movnx (code, ARMREG_R1, 16, 32); + arm_movnx (code, ARMREG_R1, 16, 48); + arm_movnw (code, ARMREG_R1, 16, 0); + arm_movnw (code, ARMREG_R1, 16, 16); + arm_movnw (code, ARMREG_R1, 16, 32); + arm_movnw (code, ARMREG_R1, 16, 48); + arm_movkx (code, ARMREG_R1, 16, 0); + arm_movkx (code, ARMREG_R1, 16, 16); + arm_movkx (code, ARMREG_R1, 16, 32); + arm_movkx (code, ARMREG_R1, 16, 48); + arm_movkw (code, ARMREG_R1, 16, 0); + arm_movkw (code, ARMREG_R1, 16, 16); + arm_movkw (code, ARMREG_R1, 16, 32); + arm_movkw (code, ARMREG_R1, 16, 48); + + arm_nop (code); + arm_adrpx (code, ARMREG_R1, code); + arm_adrx (code, ARMREG_R1, code + 4); + arm_adrx (code, ARMREG_R1, code + 16); + arm_adrx (code, ARMREG_R1, code); + arm_adrx (code, ARMREG_R1, code - 4); + + // fixme: bitfield encodings + arm_nop (code); + arm_bfmx (code, ARMREG_R1, ARMREG_R2, 0, 5); + arm_bfmw (code, ARMREG_R1, ARMREG_R2, 0, 5); + arm_sbfmx (code, ARMREG_R1, ARMREG_R2, 0, 5); + arm_sbfmw (code, ARMREG_R1, ARMREG_R2, 0, 5); + arm_ubfmx (code, ARMREG_R1, ARMREG_R2, 0, 5); + arm_ubfmw (code, ARMREG_R1, ARMREG_R2, 0, 5); + arm_asrx (code, ARMREG_R1, ARMREG_R2, 11); + arm_asrw (code, ARMREG_R1, ARMREG_R2, 11); + arm_sxtbx (code, ARMREG_R1, ARMREG_R2); + arm_sxtbw (code, ARMREG_R1, ARMREG_R2); + arm_sxthx (code, ARMREG_R1, ARMREG_R2); + arm_sxthw (code, ARMREG_R1, ARMREG_R2); + arm_sxtwx (code, ARMREG_R1, ARMREG_R2); + arm_uxtbw (code, ARMREG_R1, ARMREG_R2); + arm_uxthw (code, ARMREG_R1, ARMREG_R2); + arm_lslx (code, ARMREG_R1, ARMREG_R2, 16); + arm_lslw (code, ARMREG_R1, ARMREG_R2, 16); + arm_lsrx (code, ARMREG_R1, ARMREG_R2, 16); + arm_lsrw (code, ARMREG_R1, ARMREG_R2, 16); + arm_extrx (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, 16); + arm_extrw (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, 16); + arm_rorx (code, ARMREG_R1, ARMREG_R2, 16); + arm_rorw (code, ARMREG_R1, ARMREG_R2, 16); + + arm_nop (code); + arm_adcx (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_adcw (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_adcsx (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_adcsw (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_sbcx (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_sbcw (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_sbcsx (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_sbcsw (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_ngcx (code, ARMREG_R1, ARMREG_R2); + arm_ngcw (code, ARMREG_R1, ARMREG_R2); + arm_ngcsx (code, ARMREG_R1, ARMREG_R2); + arm_ngcsw (code, ARMREG_R1, ARMREG_R2); + + arm_nop (code); + arm_addx_shift (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, ARMSHIFT_LSL, 4); + arm_addx_shift (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, ARMSHIFT_LSR, 4); + arm_addx_shift (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, ARMSHIFT_ASR, 4); + arm_addw_shift (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, ARMSHIFT_ASR, 4); + arm_addsx_shift (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, ARMSHIFT_ASR, 4); + arm_addsw_shift (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, ARMSHIFT_ASR, 4); + arm_subx_shift (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, ARMSHIFT_ASR, 4); + arm_subw_shift (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, ARMSHIFT_ASR, 4); + arm_subsx_shift (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, ARMSHIFT_ASR, 4); + arm_subsw_shift (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, ARMSHIFT_ASR, 4); + arm_cmpx_shift (code, ARMREG_R1, ARMREG_R2, ARMSHIFT_ASR, 4); + arm_cmpw_shift (code, ARMREG_R1, ARMREG_R2, ARMSHIFT_ASR, 4); + arm_cmnx_shift (code, ARMREG_R1, ARMREG_R2, ARMSHIFT_ASR, 4); + arm_cmnw_shift (code, ARMREG_R1, ARMREG_R2, ARMSHIFT_ASR, 4); + arm_negx_shift (code, ARMREG_R1, ARMREG_R2, ARMSHIFT_ASR, 4); + arm_negw_shift (code, ARMREG_R1, ARMREG_R2, ARMSHIFT_ASR, 4); + arm_negsx_shift (code, ARMREG_R1, ARMREG_R2, ARMSHIFT_ASR, 4); + arm_negsw_shift (code, ARMREG_R1, ARMREG_R2, ARMSHIFT_ASR, 4); + + arm_nop (code); + arm_andx_shift (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, ARMSHIFT_LSL, 4); + arm_andw_shift (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, ARMSHIFT_LSL, 4); + arm_andsx_shift (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, ARMSHIFT_LSL, 4); + arm_andsw_shift (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, ARMSHIFT_LSL, 4); + arm_bicx_shift (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, ARMSHIFT_LSL, 4); + arm_bicw_shift (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, ARMSHIFT_LSL, 4); + arm_bicsx_shift (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, ARMSHIFT_LSL, 4); + arm_bicsw_shift (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, ARMSHIFT_LSL, 4); + arm_eonx_shift (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, ARMSHIFT_LSL, 4); + arm_eonw_shift (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, ARMSHIFT_LSL, 4); + arm_eorx_shift (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, ARMSHIFT_LSL, 4); + arm_eorw_shift (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, ARMSHIFT_LSL, 4); + arm_orrx_shift (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, ARMSHIFT_LSL, 4); + arm_orrw_shift (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, ARMSHIFT_LSL, 4); + arm_ornx_shift (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, ARMSHIFT_LSL, 4); + arm_ornw_shift (code, ARMREG_R1, ARMREG_R2, ARMREG_R3, ARMSHIFT_LSL, 4); + arm_mvnx_shift (code, ARMREG_R1, ARMREG_R2, ARMSHIFT_LSL, 4); + arm_mvnw_shift (code, ARMREG_R1, ARMREG_R2, ARMSHIFT_LSL, 4); + arm_tstx_shift (code, ARMREG_R1, ARMREG_R2, ARMSHIFT_LSL, 4); + arm_tstw_shift (code, ARMREG_R1, ARMREG_R2, ARMSHIFT_LSL, 4); + + arm_nop (code); + arm_movx (code, ARMREG_R1, ARMREG_R2); + arm_movw (code, ARMREG_R1, ARMREG_R2); + + arm_nop (code); + arm_asrvx (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_asrvw (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_lslvx (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_lslvw (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_lsrvx (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_lsrvw (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_rorvx (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_rorvw (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + + arm_nop (code); + arm_sdivx (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_sdivw (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_udivx (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_udivw (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + + arm_nop (code); + arm_cselx (code, ARMCOND_NE, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_cselw (code, ARMCOND_NE, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_csincx (code, ARMCOND_NE, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_csincw (code, ARMCOND_NE, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_csinvx (code, ARMCOND_NE, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_csinvw (code, ARMCOND_NE, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_csnegx (code, ARMCOND_NE, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_csnegw (code, ARMCOND_NE, ARMREG_R1, ARMREG_R2, ARMREG_R3); + + arm_brk (code, 0x1); + + arm_maddx (code, ARMREG_R0, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_maddw (code, ARMREG_R0, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_msubx (code, ARMREG_R0, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_msubw (code, ARMREG_R0, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_mnegx (code, ARMREG_R0, ARMREG_R1, ARMREG_R2); + arm_mnegw (code, ARMREG_R0, ARMREG_R1, ARMREG_R2); + + + arm_nop (code); + arm_fmovd (code, ARMREG_D1, ARMREG_D2); + arm_fmov_rx_to_double (code, ARMREG_D1, ARMREG_R2); + arm_strfpx (code, ARMREG_D1, ARMREG_R2, 16); + arm_ldrfpx (code, ARMREG_D1, ARMREG_R2, 16); + arm_strfpw (code, ARMREG_D1, ARMREG_R2, 16); + arm_ldrfpw (code, ARMREG_D1, ARMREG_R2, 16); + arm_fcmpd (code, ARMREG_D1, ARMREG_D2); + arm_fcvtzs_dx (code, ARMREG_R1, ARMREG_D2); + arm_fcvtzs_dw (code, ARMREG_R1, ARMREG_D2); + arm_fcvtzu_dx (code, ARMREG_R1, ARMREG_D2); + arm_fcvtzu_dw (code, ARMREG_R1, ARMREG_D2); + arm_fcvt_sd (code, ARMREG_D1, ARMREG_D2); + arm_fcvt_ds (code, ARMREG_D1, ARMREG_D2); + arm_scvtf_d (code, ARMREG_D1, ARMREG_D2); + arm_scvtf_s (code, ARMREG_D1, ARMREG_D2); + arm_scvtf_rx_to_d (code, ARMREG_D1, ARMREG_R2); + arm_scvtf_rw_to_d (code, ARMREG_D1, ARMREG_R2); + arm_ucvtf_d (code, ARMREG_D1, ARMREG_D2); + arm_ucvtf_s (code, ARMREG_D1, ARMREG_D2); + arm_ucvtf_rx_to_d (code, ARMREG_D1, ARMREG_R2); + arm_ucvtf_rw_to_d (code, ARMREG_D1, ARMREG_R2); + arm_fadd_d (code, ARMREG_D1, ARMREG_D2, ARMREG_D3); + arm_fsub_d (code, ARMREG_D1, ARMREG_D2, ARMREG_D3); + arm_fmul_d (code, ARMREG_D1, ARMREG_D2, ARMREG_D3); + arm_fdiv_d (code, ARMREG_D1, ARMREG_D2, ARMREG_D3); + arm_fmsub_d (code, ARMREG_D1, ARMREG_D2, ARMREG_D3, ARMREG_D4); + arm_fneg_d (code, ARMREG_D1, ARMREG_D2); + arm_fabs_d (code, ARMREG_D1, ARMREG_D2); + + arm_nop (code); + arm_dmb (code, 0x0); + arm_dmb (code, 0xd); + + arm_nop (code); + arm_mrs (code, ARMREG_R1, ARM_MRS_REG_TPIDR_EL0); + + arm_nop (code); + arm_ldaxrx (code, ARMREG_R0, ARMREG_R1); + arm_ldaxrw (code, ARMREG_R0, ARMREG_R1); + arm_stlxrx (code, ARMREG_R0, ARMREG_R1, ARMREG_R2); + arm_stlxrw (code, ARMREG_R0, ARMREG_R1, ARMREG_R2); + + for (i = 0; i < code - buf; ++i) + printf (".byte %d\n", buf [i]); + printf ("\n"); + + return 0; +} diff --git a/unity-2019.4.24f1-mbe/mono/arch/mips/.gitignore b/unity-2019.4.24f1-mbe/mono/arch/mips/.gitignore new file mode 100644 index 000000000..13efac709 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/mips/.gitignore @@ -0,0 +1,6 @@ +/ +/Makefile +/Makefile.in +/*.o +/*.lo +/.deps diff --git a/unity-2019.4.24f1-mbe/mono/arch/mips/Makefile.am b/unity-2019.4.24f1-mbe/mono/arch/mips/Makefile.am new file mode 100644 index 000000000..1063365b9 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/mips/Makefile.am @@ -0,0 +1,8 @@ + +AM_CPPFLAGS = $(GLIB_CFLAGS) -I$(top_srcdir) + +noinst_LTLIBRARIES = libmonoarch-mips.la + +libmonoarch_mips_la_SOURCES = mips-codegen.h + +noinst_PROGRAMS = test diff --git a/unity-2019.4.24f1-mbe/mono/arch/mips/mips-codegen.h b/unity-2019.4.24f1-mbe/mono/arch/mips/mips-codegen.h new file mode 100644 index 000000000..c579c88a9 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/mips/mips-codegen.h @@ -0,0 +1,436 @@ +#ifndef __MIPS_CODEGEN_H__ +#define __MIPS_CODEGEN_H__ +/* + * Copyright (c) 2004 Novell, Inc + * Author: Paolo Molaro (lupus@ximian.com) + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +/* registers */ +enum { + mips_zero, + mips_at, /* assembler temp */ + mips_v0, /* return values */ + mips_v1, + mips_a0, /* 4 - func arguments */ + mips_a1, + mips_a2, + mips_a3, +#if _MIPS_SIM == _ABIO32 + mips_t0, /* 8 temporaries */ + mips_t1, + mips_t2, + mips_t3, + mips_t4, + mips_t5, + mips_t6, + mips_t7, +#elif _MIPS_SIM == _ABIN32 + mips_a4, /* 4 more argument registers */ + mips_a5, + mips_a6, + mips_a7, + mips_t0, /* 4 temporaries */ + mips_t1, + mips_t2, + mips_t3, +#endif + mips_s0, /* 16 calle saved */ + mips_s1, + mips_s2, + mips_s3, + mips_s4, + mips_s5, + mips_s6, + mips_s7, + mips_t8, /* 24 temps */ + mips_t9, /* 25 temp / pic call-through register */ + mips_k0, /* 26 kernel-reserved */ + mips_k1, + mips_gp, /* 28 */ + mips_sp, /* stack pointer */ + mips_fp, /* frame pointer */ + mips_ra /* return address */ +}; + +/* we treat the register file as containing just doubles... */ +enum { + mips_f0, /* return regs */ + mips_f1, + mips_f2, + mips_f3, + mips_f4, /* temps */ + mips_f5, + mips_f6, + mips_f7, + mips_f8, + mips_f9, + mips_f10, + mips_f11, + mips_f12, /* first arg */ + mips_f13, + mips_f14, /* second arg */ + mips_f15, + mips_f16, /* temps */ + mips_f17, + mips_f18, + mips_f19, + mips_f20, /* callee saved */ + mips_f21, + mips_f22, + mips_f23, + mips_f24, + mips_f25, + mips_f26, + mips_f27, + mips_f28, + mips_f29, + mips_f30, + mips_f31 +}; + +/* prefetch hints */ +enum { + MIPS_FOR_LOAD, + MIPS_FOR_STORE, + MIPS_FOR_LOAD_STREAMED = 4, + MIPS_FOR_STORE_STREAMED, + MIPS_FOR_LOAD_RETAINED, + MIPS_FOR_STORE_RETAINED +}; + +/* coprocessors */ +enum { + MIPS_COP0, + MIPS_COP1, + MIPS_COP2, + MIPS_COP3 +}; + +enum { + MIPS_FMT_SINGLE = 16, + MIPS_FMT_DOUBLE = 17, + MIPS_FMT_WORD = 20, + MIPS_FMT_LONG = 21, + MIPS_FMT3_SINGLE = 0, + MIPS_FMT3_DOUBLE = 1 +}; + +/* fpu rounding mode */ +enum { + MIPS_ROUND_TO_NEAREST, + MIPS_ROUND_TO_ZERO, + MIPS_ROUND_TO_POSINF, + MIPS_ROUND_TO_NEGINF, + MIPS_ROUND_MASK = 3 +}; + +/* fpu enable/cause flags, cc */ +enum { + MIPS_FPU_C_MASK = 1 << 23, + MIPS_INEXACT = 1, + MIPS_UNDERFLOW = 2, + MIPS_OVERFLOW = 4, + MIPS_DIVZERO = 8, + MIPS_INVALID = 16, + MIPS_NOTIMPL = 32, + MIPS_FPU_FLAGS_OFFSET = 2, + MIPS_FPU_ENABLES_OFFSET = 7, + MIPS_FPU_CAUSES_OFFSET = 12 +}; + +/* fpu condition values - see manual entry for C.cond.fmt instructions */ +enum { + MIPS_FPU_F, + MIPS_FPU_UN, + MIPS_FPU_EQ, + MIPS_FPU_UEQ, + MIPS_FPU_OLT, + MIPS_FPU_ULT, + MIPS_FPU_OLE, + MIPS_FPU_ULE, + MIPS_FPU_SF, + MIPS_FPU_NGLE, + MIPS_FPU_SEQ, + MIPS_FPU_NGL, + MIPS_FPU_LT, + MIPS_FPU_NGE, + MIPS_FPU_LE, + MIPS_FPU_NGT +}; + +#if SIZEOF_REGISTER == 4 + +#define MIPS_SW mips_sw +#define MIPS_LW mips_lw +#define MIPS_ADDU mips_addu +#define MIPS_ADDIU mips_addiu +#define MIPS_SWC1 mips_swc1 +#define MIPS_LWC1 mips_lwc1 +#define MIPS_MOVE mips_move + +#elif SIZEOF_REGISTER == 8 + +#define MIPS_SW mips_sd +#define MIPS_LW mips_ld +#define MIPS_ADDU mips_daddu +#define MIPS_ADDIU mips_daddiu +#define MIPS_SWC1 mips_sdc1 +#define MIPS_LWC1 mips_ldc1 +#define MIPS_MOVE mips_dmove + +#else +#error Unknown SIZEOF_REGISTER +#endif + +#define mips_emit32(c,x) do { \ + *((guint32 *) (void *)(c)) = x; \ + (c) = (typeof(c))(((guint32 *)(void *)(c)) + 1); \ + } while (0) + +#define mips_format_i(code,op,rs,rt,imm) mips_emit32 ((code), (((op)<<26)|((rs)<<21)|((rt)<<16)|((imm)&0xffff))) +#define mips_format_j(code,op,imm) mips_emit32 ((code), (((op)<<26)|((imm)&0x03ffffff))) +#define mips_format_r(code,op,rs,rt,rd,sa,func) mips_emit32 ((code), (((op)<<26)|((rs)<<21)|((rt)<<16)|((rd)<<11)|((sa)<<6)|(func))) +#define mips_format_divmul(code,op,src1,src2,fun) mips_emit32 ((code), (((op)<<26)|((src1)<<21)|((src2)<<16)|(fun))) + +#define mips_is_imm16(val) ((gint)(gshort)(gint)(val) == (gint)(val)) + +/* Load always using lui/addiu pair (for later patching) */ +#define mips_load(c,D,v) do { \ + if (((guint32)(v)) & (1 << 15)) { \ + mips_lui ((c), (D), mips_zero, (((guint32)(v))>>16)+1); \ + } \ + else { \ + mips_lui ((c), (D), mips_zero, (((guint32)(v))>>16)); \ + } \ + mips_addiu ((c), (D), (D), ((guint32)(v)) & 0xffff); \ + } while (0) + +/* load constant - no patch-up */ +#define mips_load_const(c,D,v) do { \ + if (!mips_is_imm16 ((v))) { \ + if (((guint32)(v)) & (1 << 15)) { \ + mips_lui ((c), (D), mips_zero, (((guint32)(v))>>16)+1); \ + } \ + else { \ + mips_lui ((c), (D), mips_zero, (((guint32)(v))>>16)); \ + } \ + if (((guint32)(v)) & 0xffff) \ + mips_addiu ((c), (D), (D), ((guint32)(v)) & 0xffff); \ + } \ + else \ + mips_addiu ((c), (D), mips_zero, ((guint32)(v)) & 0xffff); \ + } while (0) + +/* arithmetric ops */ +#define mips_add(c,dest,src1,src2) mips_format_r(c,0,src1,src2,dest,0,32) +#define mips_addi(c,dest,src1,imm) mips_format_i(c,8,src1,dest,imm) +#define mips_addu(c,dest,src1,src2) mips_format_r(c,0,src1,src2,dest,0,33) +#define mips_addiu(c,dest,src1,imm) mips_format_i(c,9,src1,dest,imm) +#define mips_dadd(c,dest,src1,src2) mips_format_r(c,0,src1,src2,dest,0,44) +#define mips_daddi(c,dest,src1,imm) mips_format_i(c,24,src1,dest,imm) +#define mips_daddu(c,dest,src1,src2) mips_format_r(c,0,src1,src2,dest,0,45) +#define mips_daddiu(c,dest,src1,imm) mips_format_i(c,25,src1,dest,imm) +#define mips_dsub(c,dest,src1,src2) mips_format_r(c,0,src1,src2,dest,0,46) +#define mips_dsubu(c,dest,src1,src2) mips_format_r(c,0,src1,src2,dest,0,47) +#define mips_mul(c,dest,src1,src2) mips_format_r(c,28,src1,src2,dest,0,2) +#define mips_sub(c,dest,src1,src2) mips_format_r(c,0,src1,src2,dest,0,34) +#define mips_subu(c,dest,src1,src2) mips_format_r(c,0,src1,src2,dest,0,35) + +/* div and mul ops */ +#define mips_ddiv(c,src1,src2) mips_format_divmul(c,0,src1,src2,30) +#define mips_ddivu(c,src1,src2) mips_format_divmul(c,0,src1,src2,31) +#define mips_div(c,src1,src2) mips_format_divmul(c,0,src1,src2,26) +#define mips_divu(c,src1,src2) mips_format_divmul(c,0,src1,src2,27) +#define mips_dmult(c,src1,src2) mips_format_divmul(c,0,src1,src2,28) +#define mips_dmultu(c,src1,src2) mips_format_divmul(c,0,src1,src2,29) +#define mips_mult(c,src1,src2) mips_format_divmul(c,0,src1,src2,24) +#define mips_multu(c,src1,src2) mips_format_divmul(c,0,src1,src2,25) + +/* shift ops */ +#define mips_dsll(c,dest,src1,imm) mips_format_r(c,0,0,src1,dest,imm,56) +#define mips_dsll32(c,dest,src1,imm) mips_format_r(c,0,0,src1,dest,imm,60) +#define mips_dsllv(c,dest,src1,src2) mips_format_r(c,0,src2,src1,dest,0,20) +#define mips_dsra(c,dest,src1,imm) mips_format_r(c,0,0,src1,dest,imm,59) +#define mips_dsra32(c,dest,src1,imm) mips_format_r(c,0,0,src1,dest,imm,63) +#define mips_dsrav(c,dest,src1,src2) mips_format_r(c,0,src2,src1,dest,0,23) +#define mips_dsrl(c,dest,src1,imm) mips_format_r(c,0,0,src1,dest,imm,58) +#define mips_dsrl32(c,dest,src1,imm) mips_format_r(c,0,0,src1,dest,imm,62) +#define mips_dsrlv(c,dest,src1,src2) mips_format_r(c,0,src2,src1,dest,0,22) +#define mips_sll(c,dest,src1,imm) mips_format_r(c,0,0,src1,dest,imm,0) +#define mips_sllv(c,dest,src1,src2) mips_format_r(c,0,src2,src1,dest,0,4) +#define mips_sra(c,dest,src1,imm) mips_format_r(c,0,0,src1,dest,imm,3) +#define mips_srav(c,dest,src1,src2) mips_format_r(c,0,src2,src1,dest,0,7) +#define mips_srl(c,dest,src1,imm) mips_format_r(c,0,0,src1,dest,imm,2) +#define mips_srlv(c,dest,src1,src2) mips_format_r(c,0,src2,src1,dest,0,6) + +/* logical ops */ +#define mips_and(c,dest,src1,src2) mips_format_r(c,0,src1,src2,dest,0,36) +#define mips_andi(c,dest,src1,imm) mips_format_i(c,12,src1,dest,imm) +#define mips_nor(c,dest,src1,src2) mips_format_r(c,0,src1,src2,dest,0,39) +#define mips_or(c,dest,src1,src2) mips_format_r(c,0,src1,src2,dest,0,37) +#define mips_ori(c,dest,src1,uimm) mips_format_i(c,13,src1,dest,uimm) +#define mips_xor(c,dest,src1,src2) mips_format_r(c,0,src1,src2,dest,0,38) +#define mips_xori(c,dest,src1,uimm) mips_format_i(c,14,src1,dest,uimm) + +/* compares */ +#define mips_slt(c,dest,src1,src2) mips_format_r(c,0,src1,src2,dest,0,42) +#define mips_slti(c,dest,src1,imm) mips_format_i(c,10,src1,dest,imm) +#define mips_sltiu(c,dest,src1,imm) mips_format_i(c,11,src1,dest,imm) +#define mips_sltu(c,dest,src1,src2) mips_format_r(c,0,src1,src2,dest,0,43) +/* missing traps: teq, teqi, tge, tgei, tgeiu, tgeu, tlt, tlti, tltiu, tltu, tne, tnei, */ + +/* conditional branches */ +#define mips_beq(c,src1,src2,offset) mips_format_i(c,4,src1,src2,offset) +#define mips_beql(c,src1,src2,offset) mips_format_i(c,20,src1,src2,offset) +#define mips_bgez(c,src1,offset) mips_format_i(c,1,src1,1,offset) +#define mips_bgezal(c,src1,offset) mips_format_i(c,1,src1,17,offset) +#define mips_bgezall(c,src1,offset) mips_format_i(c,1,src1,19,offset) +#define mips_bgezl(c,src1,offset) mips_format_i(c,1,src1,3,offset) +#define mips_bgtz(c,src1,offset) mips_format_i(c,7,src1,0,offset) +#define mips_bgtzl(c,src1,offset) mips_format_i(c,23,src1,0,offset) +#define mips_blez(c,src1,offset) mips_format_i(c,6,src1,0,offset) +#define mips_blezl(c,src1,offset) mips_format_i(c,22,src1,0,offset) +#define mips_bltz(c,src1,offset) mips_format_i(c,1,src1,0,offset) +#define mips_bltzal(c,src1,offset) mips_format_i(c,1,src1,16,offset) +#define mips_bltzall(c,src1,offset) mips_format_i(c,1,src1,18,offset) +#define mips_bltzl(c,src1,offset) mips_format_i(c,1,src1,2,offset) +#define mips_bne(c,src1,src2,offset) mips_format_i(c,5,src1,src2,offset) +#define mips_bnel(c,src1,src2,offset) mips_format_i(c,21,src1,src2,offset) + +/* uncond branches and calls */ +#define mips_jump(c,target) mips_format_j(c,2,target) +#define mips_jumpl(c,target) mips_format_j(c,3,target) +#define mips_jalr(c,src1,retreg) mips_format_r(c,0,src1,0,retreg,0,9) +#define mips_jr(c,src1) mips_emit32(c,((src1)<<21)|8) + +/* loads and stores */ +#define mips_lb(c,dest,base,offset) mips_format_i(c,32,base,dest,offset) +#define mips_lbu(c,dest,base,offset) mips_format_i(c,36,base,dest,offset) +#define mips_ld(c,dest,base,offset) mips_format_i(c,55,base,dest,offset) +#define mips_ldl(c,dest,base,offset) mips_format_i(c,26,base,dest,offset) +#define mips_ldr(c,dest,base,offset) mips_format_i(c,27,base,dest,offset) +#define mips_lh(c,dest,base,offset) mips_format_i(c,33,base,dest,offset) +#define mips_lhu(c,dest,base,offset) mips_format_i(c,37,base,dest,offset) +#define mips_ll(c,dest,base,offset) mips_format_i(c,48,base,dest,offset) +#define mips_lld(c,dest,base,offset) mips_format_i(c,52,base,dest,offset) +#define mips_lui(c,dest,base,uimm) mips_format_i(c,15,base,dest,uimm) +#define mips_lw(c,dest,base,offset) mips_format_i(c,35,base,dest,offset) +#define mips_lwl(c,dest,base,offset) mips_format_i(c,34,base,dest,offset) +#define mips_lwr(c,dest,base,offset) mips_format_i(c,38,base,dest,offset) +#define mips_lwu(c,dest,base,offset) mips_format_i(c,39,base,dest,offset) + +#define mips_sb(c,src,base,offset) mips_format_i(c,40,base,src,offset) +#define mips_sc(c,src,base,offset) mips_format_i(c,56,base,src,offset) +#define mips_scd(c,src,base,offset) mips_format_i(c,60,base,src,offset) +#define mips_sd(c,src,base,offset) mips_format_i(c,63,base,src,offset) +#define mips_sdl(c,src,base,offset) mips_format_i(c,44,base,src,offset) +#define mips_sdr(c,src,base,offset) mips_format_i(c,45,base,src,offset) +#define mips_sh(c,src,base,offset) mips_format_i(c,41,base,src,offset) +#define mips_sw(c,src,base,offset) mips_format_i(c,43,base,src,offset) +#define mips_swl(c,src,base,offset) mips_format_i(c,50,base,src,offset) +#define mips_swr(c,src,base,offset) mips_format_i(c,54,base,src,offset) + +/* misc and coprocessor ops */ +#define mips_move(c,dest,src) mips_addu(c,dest,src,mips_zero) +#define mips_dmove(c,dest,src) mips_daddu(c,dest,src,mips_zero) +#define mips_nop(c) mips_or(c,mips_at,mips_at,0) +#define mips_break(c,code) mips_emit32(c, ((code)<<6)|13) +#define mips_mfhi(c,dest) mips_format_r(c,0,0,0,dest,0,16) +#define mips_mflo(c,dest) mips_format_r(c,0,0,0,dest,0,18) +#define mips_mthi(c,src) mips_format_r(c,0,src,0,0,0,17) +#define mips_mtlo(c,src) mips_format_r(c,0,src,0,0,0,19) +#define mips_movn(c,dest,src,test) mips_format_r(c,0,src,test,dest,0,11) +#define mips_movz(c,dest,src,test) mips_format_r(c,0,src,test,dest,0,10) +#define mips_pref(c,hint,base,offset) mips_format_i(c,51,base,hint,offset) +#define mips_prefidx(c,hint,base,idx) mips_format_r(c,19,base,idx,hint,0,15) +#define mips_sync(c,stype) mips_emit32(c, ((stype)<<6)|15) +#define mips_syscall(c,code) mips_emit32(c, ((code)<<6)|12) + +#define mips_cop(c,cop,fun) mips_emit32(c, ((16|(cop))<<26)|(fun)) +#define mips_ldc(c,cop,dest,base,offset) mips_format_i(c,(52|(cop)),base,dest,offset) +#define mips_lwc(c,cop,dest,base,offset) mips_format_i(c,(48|(cop)),base,dest,offset) +#define mips_sdc(c,cop,src,base,offset) mips_format_i(c,(60|(cop)),base,src,offset) +#define mips_swc(c,cop,src,base,offset) mips_format_i(c,(56|(cop)),base,src,offset) +#define mips_cfc1(c,dest,src) mips_format_r(c,17,2,dest,src,0,0) +#define mips_ctc1(c,dest,src) mips_format_r(c,17,6,dest,src,0,0) + +/* fpu ops */ +#define mips_fabss(c,dest,src) mips_format_r(c,17,MIPS_FMT_SINGLE,0,src,dest,5) +#define mips_fabsd(c,dest,src) mips_format_r(c,17,MIPS_FMT_DOUBLE,0,src,dest,5) +#define mips_fadds(c,dest,src1,src2) mips_format_r(c,17,MIPS_FMT_SINGLE,src2,src1,dest,0) +#define mips_faddd(c,dest,src1,src2) mips_format_r(c,17,MIPS_FMT_DOUBLE,src2,src1,dest,0) +#define mips_fdivs(c,dest,src1,src2) mips_format_r(c,17,MIPS_FMT_SINGLE,src2,src1,dest,3) +#define mips_fdivd(c,dest,src1,src2) mips_format_r(c,17,MIPS_FMT_DOUBLE,src2,src1,dest,3) +#define mips_fmuls(c,dest,src1,src2) mips_format_r(c,17,MIPS_FMT_SINGLE,src2,src1,dest,2) +#define mips_fmuld(c,dest,src1,src2) mips_format_r(c,17,MIPS_FMT_DOUBLE,src2,src1,dest,2) +#define mips_fnegs(c,dest,src) mips_format_r(c,17,MIPS_FMT_SINGLE,0,src,dest,7) +#define mips_fnegd(c,dest,src) mips_format_r(c,17,MIPS_FMT_DOUBLE,0,src,dest,7) +#define mips_fsqrts(c,dest,src) mips_format_r(c,17,MIPS_FMT_SINGLE,0,src,dest,4) +#define mips_fsqrtd(c,dest,src) mips_format_r(c,17,MIPS_FMT_DOUBLE,0,src,dest,4) +#define mips_fsubs(c,dest,src1,src2) mips_format_r(c,17,MIPS_FMT_SINGLE,src2,src1,dest,1) +#define mips_fsubd(c,dest,src1,src2) mips_format_r(c,17,MIPS_FMT_DOUBLE,src2,src1,dest,1) +#define mips_madds(c,dest,src1,src2,srcadd) mips_format_r(c,19,srcadd,src2,src1,dest,32|MIPS_FMT_SINGLE) +#define mips_maddd(c,dest,src1,src2,srcadd) mips_format_r(c,19,srcadd,src2,src1,dest,32|MIPS_FMT_DOUBLE) +#define mips_nmadds(c,dest,src1,src2,srcadd) mips_format_r(c,19,srcadd,src2,src1,dest,48|MIPS_FMT_SINGLE) +#define mips_nmaddd(c,dest,src1,src2,srcadd) mips_format_r(c,19,srcadd,src2,src1,dest,48|MIPS_FMT_DOUBLE) +#define mips_msubs(c,dest,src1,src2,srcsub) mips_format_r(c,19,srcsub,src2,src1,dest,40|MIPS_FMT_SINGLE) +#define mips_msubd(c,dest,src1,src2,srcsub) mips_format_r(c,19,srcsub,src2,src1,dest,40|MIPS_FMT_DOUBLE) +#define mips_nmsubs(c,dest,src1,src2,srcsub) mips_format_r(c,19,srcsub,src2,src1,dest,56|MIPS_FMT_SINGLE) +#define mips_nmsubd(c,dest,src1,src2,srcsub) mips_format_r(c,19,srcsub,src2,src1,dest,56|MIPS_FMT_DOUBLE) + +/* fp compare and branch */ +#define mips_fcmps(c,cond,src1,src2) mips_format_r(c,17,MIPS_FMT_SINGLE,src2,src1,0,(3<<4)|(cond)) +#define mips_fcmpd(c,cond,src1,src2) mips_format_r(c,17,MIPS_FMT_DOUBLE,src2,src1,0,(3<<4)|(cond)) +#define mips_fbfalse(c,offset) mips_format_i(c,17,8,0,offset) +#define mips_fbfalsel(c,offset) mips_format_i(c,17,8,2,offset) +#define mips_fbtrue(c,offset) mips_format_i(c,17,8,1,offset) +#define mips_fbtruel(c,offset) mips_format_i(c,17,8,3,offset) + +/* fp convert */ +#define mips_ceills(c,dest,src) mips_format_r(c,17,MIPS_FMT_SINGLE,0,src,dest,10) +#define mips_ceilld(c,dest,src) mips_format_r(c,17,MIPS_FMT_DOUBLE,0,src,dest,10) +#define mips_ceilws(c,dest,src) mips_format_r(c,17,MIPS_FMT_SINGLE,0,src,dest,14) +#define mips_ceilwd(c,dest,src) mips_format_r(c,17,MIPS_FMT_DOUBLE,0,src,dest,14) +#define mips_cvtds(c,dest,src) mips_format_r(c,17,MIPS_FMT_SINGLE,0,src,dest,33) +#define mips_cvtdw(c,dest,src) mips_format_r(c,17,MIPS_FMT_WORD,0,src,dest,33) +#define mips_cvtdl(c,dest,src) mips_format_r(c,17,MIPS_FMT_LONG,0,src,dest,33) +#define mips_cvtls(c,dest,src) mips_format_r(c,17,MIPS_FMT_SINGLE,0,src,dest,37) +#define mips_cvtld(c,dest,src) mips_format_r(c,17,MIPS_FMT_DOUBLE,0,src,dest,37) +#define mips_cvtsd(c,dest,src) mips_format_r(c,17,MIPS_FMT_DOUBLE,0,src,dest,32) +#define mips_cvtsw(c,dest,src) mips_format_r(c,17,MIPS_FMT_WORD,0,src,dest,32) +#define mips_cvtsl(c,dest,src) mips_format_r(c,17,MIPS_FMT_LONG,0,src,dest,32) +#define mips_cvtws(c,dest,src) mips_format_r(c,17,MIPS_FMT_SINGLE,0,src,dest,36) +#define mips_cvtwd(c,dest,src) mips_format_r(c,17,MIPS_FMT_DOUBLE,0,src,dest,36) +#define mips_floorls(c,dest,src) mips_format_r(c,17,MIPS_FMT_SINGLE,0,src,dest,11) +#define mips_floorld(c,dest,src) mips_format_r(c,17,MIPS_FMT_DOUBLE,0,src,dest,11) +#define mips_floorws(c,dest,src) mips_format_r(c,17,MIPS_FMT_SINGLE,0,src,dest,15) +#define mips_floorwd(c,dest,src) mips_format_r(c,17,MIPS_FMT_DOUBLE,0,src,dest,15) +#define mips_roundls(c,dest,src) mips_format_r(c,17,MIPS_FMT_SINGLE,0,src,dest,8) +#define mips_roundld(c,dest,src) mips_format_r(c,17,MIPS_FMT_DOUBLE,0,src,dest,8) +#define mips_roundws(c,dest,src) mips_format_r(c,17,MIPS_FMT_SINGLE,0,src,dest,12) +#define mips_roundwd(c,dest,src) mips_format_r(c,17,MIPS_FMT_DOUBLE,0,src,dest,12) +#define mips_truncls(c,dest,src) mips_format_r(c,17,MIPS_FMT_SINGLE,0,src,dest,9) +#define mips_truncld(c,dest,src) mips_format_r(c,17,MIPS_FMT_DOUBLE,0,src,dest,9) +#define mips_truncws(c,dest,src) mips_format_r(c,17,MIPS_FMT_SINGLE,0,src,dest,13) +#define mips_truncwd(c,dest,src) mips_format_r(c,17,MIPS_FMT_DOUBLE,0,src,dest,13) + +/* fp moves, loads */ +#define mips_fmovs(c,dest,src) mips_format_r(c,17,MIPS_FMT_SINGLE,0,src,dest,6) +#define mips_fmovd(c,dest,src) mips_format_r(c,17,MIPS_FMT_DOUBLE,0,src,dest,6) +#define mips_mfc1(c,dest,src) mips_format_r(c,17,0,dest,src,0,0) +#define mips_mtc1(c,dest,src) mips_format_r(c,17,4,src,dest,0,0) +#define mips_dmfc1(c,dest,src) mips_format_r(c,17,1,0,dest,src,0) +#define mips_dmtc1(c,dest,src) mips_format_r(c,17,1,0,src,dest,0) +#define mips_ldc1(c,dest,base,offset) mips_ldc(c,1,dest,base,offset) +#define mips_ldxc1(c,dest,base,idx) mips_format_r(c,19,base,idx,0,dest,1) +#define mips_lwc1(c,dest,base,offset) mips_lwc(c,1,dest,base,offset) +#define mips_lwxc1(c,dest,base,idx) mips_format_r(c,19,base,idx,0,dest,0) +#define mips_sdc1(c,src,base,offset) mips_sdc(c,1,src,base,offset) +#define mips_sdxc1(c,src,base,idx) mips_format_r(c,19,base,idx,src,0,9) +#define mips_swc1(c,src,base,offset) mips_swc(c,1,src,base,offset) +#define mips_swxc1(c,src,base,idx) mips_format_r(c,19,base,idx,src,0,8) + +#endif /* __MIPS_CODEGEN_H__ */ + diff --git a/unity-2019.4.24f1-mbe/mono/arch/mips/test.c b/unity-2019.4.24f1-mbe/mono/arch/mips/test.c new file mode 100644 index 000000000..4f5e1adee --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/mips/test.c @@ -0,0 +1,159 @@ +#include "config.h" +#include +#include + +#define NO_MIPS_JIT_DEBUG + +#include "mips-codegen.h" +#include "mono/metadata/class.h" + +/* don't run the resulting program, it will destroy your computer, + * just objdump -d it to inspect we generated the correct assembler. + */ + +int main (int argc, char *argv[]) { + guint32 *code, * p; + + code = p = (guint32 *) malloc (sizeof (guint32) * 1024); + + mips_add (p, 3, 4, 5); + mips_addi (p, 3, 4, 5); + mips_addu (p, 3, 4, 5); + mips_addiu (p, 3, 4, 5); + mips_sub (p, 3, 4, 5); + mips_subu (p, 3, 4, 5); + mips_dadd (p, 3, 4, 5); + mips_daddi (p, 3, 4, 5); + mips_daddu (p, 3, 4, 5); + mips_daddiu (p, 3, 4, 5); + mips_dsub (p, 3, 4, 5); + mips_dsubu (p, 3, 4, 5); + + mips_mult (p, 6, 7); + mips_multu (p, 6, 7); + mips_div (p, 6, 7); + mips_divu (p, 6, 7); + mips_dmult (p, 6, 7); + mips_dmultu (p, 6, 7); + mips_ddiv (p, 6, 7); + mips_ddivu (p, 6, 7); + + mips_sll (p, 3, 4, 5); + mips_sllv (p, 3, 4, 5); + mips_sra (p, 3, 4, 5); + mips_srav (p, 3, 4, 5); + mips_srl (p, 3, 4, 5); + mips_srlv (p, 3, 4, 5); + mips_dsll (p, 3, 4, 5); + mips_dsll32 (p, 3, 4, 5); + mips_dsllv (p, 3, 4, 5); + mips_dsra (p, 3, 4, 5); + mips_dsra32 (p, 3, 4, 5); + mips_dsrav (p, 3, 4, 5); + mips_dsrl (p, 3, 4, 5); + mips_dsrl32 (p, 3, 4, 5); + mips_dsrlv (p, 3, 4, 5); + + mips_and (p, 8, 9, 10); + mips_andi (p, 8, 9, 10); + mips_nor (p, 8, 9, 10); + mips_or (p, 8, 9, 10); + mips_ori (p, 8, 9, 10); + mips_xor (p, 8, 9, 10); + mips_xori (p, 8, 9, 10); + + mips_slt (p, 8, 9, 10); + mips_slti (p, 8, 9, 10); + mips_sltu (p, 8, 9, 10); + mips_sltiu (p, 8, 9, 10); + + mips_beq (p, 8, 9, 0xff1f); + mips_beql (p, 8, 9, 0xff1f); + mips_bne (p, 8, 9, 0xff1f); + mips_bnel (p, 8, 9, 0xff1f); + mips_bgez (p, 11, 0xff1f); + mips_bgezal (p, 11, 0xff1f); + mips_bgezall (p, 11, 0xff1f); + mips_bgezl (p, 11, 0xff1f); + mips_bgtz (p, 11, 0xff1f); + mips_bgtzl (p, 11, 0xff1f); + mips_blez (p, 11, 0xff1f); + mips_blezl (p, 11, 0xff1f); + mips_bltz (p, 11, 0xff1f); + mips_bltzal (p, 11, 0xff1f); + mips_bltzall (p, 11, 0xff1f); + mips_bltzl (p, 11, 0xff1f); + + mips_jump (p, 0xff1f); + mips_jumpl (p, 0xff1f); + mips_jalr (p, 12, mips_ra); + mips_jr (p, 12); + + mips_lb (p, 13, 14, 128); + mips_lbu (p, 13, 14, 128); + mips_ld (p, 13, 14, 128); + mips_ldl (p, 13, 14, 128); + mips_ldr (p, 13, 14, 128); + mips_lh (p, 13, 14, 128); + mips_lhu (p, 13, 14, 128); + mips_ll (p, 13, 14, 128); + mips_lld (p, 13, 14, 128); + mips_lui (p, 13, 14, 128); + mips_lw (p, 13, 14, 128); + mips_lwl (p, 13, 14, 128); + mips_lwr (p, 13, 14, 128); + mips_lwu (p, 13, 14, 128); + mips_sb (p, 13, 14, 128); + mips_sc (p, 13, 14, 128); + mips_scd (p, 13, 14, 128); + mips_sd (p, 13, 14, 128); + mips_sdl (p, 13, 14, 128); + mips_sdr (p, 13, 14, 128); + mips_sh (p, 13, 14, 128); + mips_sw (p, 13, 14, 128); + mips_swl (p, 13, 14, 128); + mips_swr (p, 13, 14, 128); + + mips_move (p, 15, 16); + mips_nop (p); + mips_break (p, 0); + mips_sync (p, 0); + mips_mfhi (p, 17); + mips_mflo (p, 17); + mips_mthi (p, 17); + mips_mtlo (p, 17); + + mips_fabsd (p, 16, 18); + mips_fnegd (p, 16, 18); + mips_fsqrtd (p, 16, 18); + mips_faddd (p, 16, 18, 20); + mips_fdivd (p, 16, 18, 20); + mips_fmuld (p, 16, 18, 20); + mips_fsubd (p, 16, 18, 20); + + mips_fcmpd (p, MIPS_FPU_EQ, 18, 20); + mips_fbfalse (p, 0xff1f); + mips_fbfalsel (p, 0xff1f); + mips_fbtrue (p, 0xff1f); + mips_fbtruel (p, 0xff1f); + + mips_ceilwd (p, 20, 22); + mips_ceilld (p, 20, 22); + mips_floorwd (p, 20, 22); + mips_floorld (p, 20, 22); + mips_roundwd (p, 20, 22); + mips_roundld (p, 20, 22); + mips_truncwd (p, 20, 22); + mips_truncld (p, 20, 22); + mips_cvtdw (p, 20, 22); + mips_cvtds (p, 20, 22); + mips_cvtdl (p, 20, 22); + mips_cvtld (p, 20, 22); + mips_cvtsd (p, 20, 22); + mips_cvtwd (p, 20, 22); + + mips_fmovd (p, 20, 22); + printf ("size: %d\n", p - code); + + return 0; +} diff --git a/unity-2019.4.24f1-mbe/mono/arch/ppc/.gitignore b/unity-2019.4.24f1-mbe/mono/arch/ppc/.gitignore new file mode 100644 index 000000000..c577ff628 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/ppc/.gitignore @@ -0,0 +1,7 @@ +/Makefile +/Makefile.in +/.libs +/.deps +/*.la +/*.lo +/test diff --git a/unity-2019.4.24f1-mbe/mono/arch/ppc/Makefile.am b/unity-2019.4.24f1-mbe/mono/arch/ppc/Makefile.am new file mode 100644 index 000000000..9b209ef94 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/ppc/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = ppc-codegen.h \ No newline at end of file diff --git a/unity-2019.4.24f1-mbe/mono/arch/ppc/ppc-codegen.h b/unity-2019.4.24f1-mbe/mono/arch/ppc/ppc-codegen.h new file mode 100644 index 000000000..98fed7525 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/ppc/ppc-codegen.h @@ -0,0 +1,958 @@ +/* + Authors: + Radek Doulik + Christopher Taylor + Andreas Faerber + + Copyright (C) 2001 Radek Doulik + Copyright (C) 2007-2008 Andreas Faerber + + for testing do the following: ./test | as -o test.o + Licensed under the MIT license. See LICENSE file in the project root for full license information. +*/ + +#ifndef __MONO_PPC_CODEGEN_H__ +#define __MONO_PPC_CODEGEN_H__ +#include +#include + +typedef enum { + ppc_r0 = 0, + ppc_r1, + ppc_sp = ppc_r1, + ppc_r2, + ppc_r3, + ppc_r4, + ppc_r5, + ppc_r6, + ppc_r7, + ppc_r8, + ppc_r9, + ppc_r10, + ppc_r11, + ppc_r12, + ppc_r13, + ppc_r14, + ppc_r15, + ppc_r16, + ppc_r17, + ppc_r18, + ppc_r19, + ppc_r20, + ppc_r21, + ppc_r22, + ppc_r23, + ppc_r24, + ppc_r25, + ppc_r26, + ppc_r27, + ppc_r28, + ppc_r29, + ppc_r30, + ppc_r31 +} PPCIntRegister; + +typedef enum { + ppc_f0 = 0, + ppc_f1, + ppc_f2, + ppc_f3, + ppc_f4, + ppc_f5, + ppc_f6, + ppc_f7, + ppc_f8, + ppc_f9, + ppc_f10, + ppc_f11, + ppc_f12, + ppc_f13, + ppc_f14, + ppc_f15, + ppc_f16, + ppc_f17, + ppc_f18, + ppc_f19, + ppc_f20, + ppc_f21, + ppc_f22, + ppc_f23, + ppc_f24, + ppc_f25, + ppc_f26, + ppc_f27, + ppc_f28, + ppc_f29, + ppc_f30, + ppc_f31 +} PPCFloatRegister; + +typedef enum { + ppc_lr = 256, + ppc_ctr = 256 + 32, + ppc_xer = 32 +} PPCSpecialRegister; + +enum { + /* B0 operand for branches */ + PPC_BR_DEC_CTR_NONZERO_FALSE = 0, + PPC_BR_LIKELY = 1, /* can be or'ed with the conditional variants */ + PPC_BR_DEC_CTR_ZERO_FALSE = 2, + PPC_BR_FALSE = 4, + PPC_BR_DEC_CTR_NONZERO_TRUE = 8, + PPC_BR_DEC_CTR_ZERO_TRUE = 10, + PPC_BR_TRUE = 12, + PPC_BR_DEC_CTR_NONZERO = 16, + PPC_BR_DEC_CTR_ZERO = 18, + PPC_BR_ALWAYS = 20, + /* B1 operand for branches */ + PPC_BR_LT = 0, + PPC_BR_GT = 1, + PPC_BR_EQ = 2, + PPC_BR_SO = 3 +}; + +enum { + PPC_TRAP_LT = 1, + PPC_TRAP_GT = 2, + PPC_TRAP_EQ = 4, + PPC_TRAP_LT_UN = 8, + PPC_TRAP_GT_UN = 16, + PPC_TRAP_LE = 1 + PPC_TRAP_EQ, + PPC_TRAP_GE = 2 + PPC_TRAP_EQ, + PPC_TRAP_LE_UN = 8 + PPC_TRAP_EQ, + PPC_TRAP_GE_UN = 16 + PPC_TRAP_EQ +}; + +#define ppc_emit32(c,x) do { *((guint32 *) (c)) = (guint32) (x); (c) = (gpointer)((guint8 *)(c) + sizeof (guint32));} while (0) + +#define ppc_is_imm16(val) ((((val)>> 15) == 0) || (((val)>> 15) == -1)) +#define ppc_is_uimm16(val) ((glong)(val) >= 0L && (glong)(val) <= 65535L) +#define ppc_ha(val) (((val >> 16) + ((val & 0x8000) ? 1 : 0)) & 0xffff) + +#define ppc_load32(c,D,v) G_STMT_START { \ + ppc_lis ((c), (D), (guint32)(v) >> 16); \ + ppc_ori ((c), (D), (D), (guint32)(v) & 0xffff); \ + } G_STMT_END + +/* Macros to load/store pointer sized quantities */ + +#if defined(__mono_ppc64__) && !defined(__mono_ilp32__) + +#define ppc_ldptr(c,D,d,A) ppc_ld ((c), (D), (d), (A)) +#define ppc_ldptr_update(c,D,d,A) ppc_ldu ((c), (D), (d), (A)) +#define ppc_ldptr_indexed(c,D,A,B) ppc_ldx ((c), (D), (A), (B)) +#define ppc_ldptr_update_indexed(c,D,A,B) ppc_ldux ((c), (D), (A), (B)) + +#define ppc_stptr(c,S,d,A) ppc_std ((c), (S), (d), (A)) +#define ppc_stptr_update(c,S,d,A) ppc_stdu ((c), (S), (d), (A)) +#define ppc_stptr_indexed(c,S,A,B) ppc_stdx ((c), (S), (A), (B)) +#define ppc_stptr_update_indexed(c,S,A,B) ppc_stdux ((c), (S), (A), (B)) + +#else + +/* Same as ppc32 */ +#define ppc_ldptr(c,D,d,A) ppc_lwz ((c), (D), (d), (A)) +#define ppc_ldptr_update(c,D,d,A) ppc_lwzu ((c), (D), (d), (A)) +#define ppc_ldptr_indexed(c,D,A,B) ppc_lwzx ((c), (D), (A), (B)) +#define ppc_ldptr_update_indexed(c,D,A,B) ppc_lwzux ((c), (D), (A), (B)) + +#define ppc_stptr(c,S,d,A) ppc_stw ((c), (S), (d), (A)) +#define ppc_stptr_update(c,S,d,A) ppc_stwu ((c), (S), (d), (A)) +#define ppc_stptr_indexed(c,S,A,B) ppc_stwx ((c), (S), (A), (B)) +#define ppc_stptr_update_indexed(c,S,A,B) ppc_stwux ((c), (S), (A), (B)) + +#endif + +/* Macros to load pointer sized immediates */ +#define ppc_load_ptr(c,D,v) ppc_load ((c),(D),(gsize)(v)) +#define ppc_load_ptr_sequence(c,D,v) ppc_load_sequence ((c),(D),(gsize)(v)) + +/* Macros to load/store regsize quantities */ + +#ifdef __mono_ppc64__ +#define ppc_ldr(c,D,d,A) ppc_ld ((c), (D), (d), (A)) +#define ppc_ldr_indexed(c,D,A,B) ppc_ldx ((c), (D), (A), (B)) +#define ppc_str(c,S,d,A) ppc_std ((c), (S), (d), (A)) +#define ppc_str_update(c,S,d,A) ppc_stdu ((c), (S), (d), (A)) +#define ppc_str_indexed(c,S,A,B) ppc_stdx ((c), (S), (A), (B)) +#define ppc_str_update_indexed(c,S,A,B) ppc_stdux ((c), (S), (A), (B)) +#else +#define ppc_ldr(c,D,d,A) ppc_lwz ((c), (D), (d), (A)) +#define ppc_ldr_indexed(c,D,A,B) ppc_lwzx ((c), (D), (A), (B)) +#define ppc_str(c,S,d,A) ppc_stw ((c), (S), (d), (A)) +#define ppc_str_update(c,S,d,A) ppc_stwu ((c), (S), (d), (A)) +#define ppc_str_indexed(c,S,A,B) ppc_stwx ((c), (S), (A), (B)) +#define ppc_str_update_indexed(c,S,A,B) ppc_stwux ((c), (S), (A), (B)) +#endif + +#define ppc_str_multiple(c,S,d,A) ppc_store_multiple_regs((c),(S),(d),(A)) +#define ppc_ldr_multiple(c,D,d,A) ppc_load_multiple_regs((c),(D),(d),(A)) + +/* PPC32 macros */ + +#ifndef __mono_ppc64__ + +#define ppc_load_sequence(c,D,v) ppc_load32 ((c), (D), (guint32)(v)) + +#define PPC_LOAD_SEQUENCE_LENGTH 8 + +#define ppc_load(c,D,v) G_STMT_START { \ + if (ppc_is_imm16 ((guint32)(v))) { \ + ppc_li ((c), (D), (guint16)(guint32)(v)); \ + } else { \ + ppc_load32 ((c), (D), (guint32)(v)); \ + } \ + } G_STMT_END + +#define ppc_load_func(c,D,V) ppc_load_sequence ((c), (D), (V)) + +#define ppc_load_multiple_regs(c,D,d,A) ppc_lmw ((c), (D), (d), (A)) + +#define ppc_store_multiple_regs(c,S,d,A) ppc_stmw ((c), (S), (d), (A)) + +#define ppc_compare(c,cfrD,A,B) ppc_cmp((c), (cfrD), 0, (A), (B)) +#define ppc_compare_reg_imm(c,cfrD,A,B) ppc_cmpi((c), (cfrD), 0, (A), (B)) +#define ppc_compare_log(c,cfrD,A,B) ppc_cmpl((c), (cfrD), 0, (A), (B)) + +#define ppc_shift_left(c,A,S,B) ppc_slw((c), (S), (A), (B)) +#define ppc_shift_left_imm(c,A,S,n) ppc_slwi((c), (A), (S), (n)) + +#define ppc_shift_right_imm(c,A,S,B) ppc_srwi((c), (A), (S), (B)) +#define ppc_shift_right_arith_imm(c,A,S,B) ppc_srawi((c), (A), (S), (B)) + +#define ppc_multiply(c,D,A,B) ppc_mullw((c), (D), (A), (B)) + +#define ppc_clear_right_imm(c,A,S,n) ppc_clrrwi((c), (A), (S), (n)) + +#endif + +#define ppc_opcode(c) ((c) >> 26) +#define ppc_split_5_1_1(x) (((x) >> 5) & 0x1) +#define ppc_split_5_1_5(x) ((x) & 0x1F) +#define ppc_split_5_1(x) ((ppc_split_5_1_5(x) << 1) | ppc_split_5_1_1(x)) + +#define ppc_break(c) ppc_tw((c),31,0,0) +#define ppc_addi(c,D,A,i) ppc_emit32 (c, (14 << 26) | ((D) << 21) | ((A) << 16) | (guint16)(i)) +#define ppc_addis(c,D,A,i) ppc_emit32 (c, (15 << 26) | ((D) << 21) | ((A) << 16) | (guint16)(i)) +#define ppc_li(c,D,v) ppc_addi (c, D, 0, (guint16)(v)) +#define ppc_lis(c,D,v) ppc_addis (c, D, 0, (guint16)(v)) +#define ppc_lwz(c,D,d,A) ppc_emit32 (c, (32 << 26) | ((D) << 21) | ((A) << 16) | (guint16)(d)) +#define ppc_lhz(c,D,d,A) ppc_emit32 (c, (40 << 26) | ((D) << 21) | ((A) << 16) | (guint16)(d)) +#define ppc_lbz(c,D,d,A) ppc_emit32 (c, (34 << 26) | ((D) << 21) | ((A) << 16) | (guint16)(d)) +#define ppc_stw(c,S,d,A) ppc_emit32 (c, (36 << 26) | ((S) << 21) | ((A) << 16) | (guint16)(d)) +#define ppc_sth(c,S,d,A) ppc_emit32 (c, (44 << 26) | ((S) << 21) | ((A) << 16) | (guint16)(d)) +#define ppc_stb(c,S,d,A) ppc_emit32 (c, (38 << 26) | ((S) << 21) | ((A) << 16) | (guint16)(d)) +#define ppc_stwu(c,s,d,A) ppc_emit32 (c, (37 << 26) | ((s) << 21) | ((A) << 16) | (guint16)(d)) +#define ppc_or(c,a,s,b) ppc_emit32 (c, (31 << 26) | ((s) << 21) | ((a) << 16) | ((b) << 11) | 888) +#define ppc_mr(c,a,s) ppc_or (c, a, s, s) +#define ppc_ori(c,S,A,ui) ppc_emit32 (c, (24 << 26) | ((S) << 21) | ((A) << 16) | (guint16)(ui)) +#define ppc_nop(c) ppc_ori (c, 0, 0, 0) +#define ppc_mfspr(c,D,spr) ppc_emit32 (c, (31 << 26) | ((D) << 21) | ((spr) << 11) | (339 << 1)) +#define ppc_mflr(c,D) ppc_mfspr (c, D, ppc_lr) +#define ppc_mtspr(c,spr,S) ppc_emit32 (c, (31 << 26) | ((S) << 21) | ((spr) << 11) | (467 << 1)) +#define ppc_mtlr(c,S) ppc_mtspr (c, ppc_lr, S) +#define ppc_mtctr(c,S) ppc_mtspr (c, ppc_ctr, S) +#define ppc_mtxer(c,S) ppc_mtspr (c, ppc_xer, S) + +#define ppc_b(c,li) ppc_emit32 (c, (18 << 26) | ((li) << 2)) +#define ppc_bl(c,li) ppc_emit32 (c, (18 << 26) | ((li) << 2) | 1) +#define ppc_ba(c,li) ppc_emit32 (c, (18 << 26) | ((li) << 2) | 2) +#define ppc_bla(c,li) ppc_emit32 (c, (18 << 26) | ((li) << 2) | 3) +#define ppc_blrl(c) ppc_emit32 (c, 0x4e800021) +#define ppc_blr(c) ppc_emit32 (c, 0x4e800020) + +#define ppc_lfs(c,D,d,A) ppc_emit32 (c, (48 << 26) | ((D) << 21) | ((A) << 16) | (guint16)(d)) +#define ppc_lfd(c,D,d,A) ppc_emit32 (c, (50 << 26) | ((D) << 21) | ((A) << 16) | (guint16)(d)) +#define ppc_stfs(c,S,d,a) ppc_emit32 (c, (52 << 26) | ((S) << 21) | ((a) << 16) | (guint16)(d)) +#define ppc_stfd(c,S,d,a) ppc_emit32 (c, (54 << 26) | ((S) << 21) | ((a) << 16) | (guint16)(d)) + +/*********************************************************************** +The macros below were tapped out by Christopher Taylor +from 18 November 2002 to 19 December 2002. + +Special thanks to rodo, lupus, dietmar, miguel, and duncan for patience, +and motivation. + +The macros found in this file are based on the assembler instructions found +in Motorola and Digital DNA's: + +"Programming Enviornments Manual For 32-bit Implementations of the PowerPC Architecture" + +MPCFPE32B/AD +12/2001 +REV2 + +see pages 326 - 524 for detailed information regarding each instruction + +Also see the "Ximian Copyright Agreement, 2002" for more information regarding +my and Ximian's copyright to this code. ;) +*************************************************************************/ + +#define ppc_addx(c,D,A,B,OE,Rc) ppc_emit32(c, (31 << 26) | ((D) << 21) | ((A) << 16) | ((B) << 11) | (OE << 10) | (266 << 1) | Rc) +#define ppc_add(c,D,A,B) ppc_addx(c,D,A,B,0,0) +#define ppc_addd(c,D,A,B) ppc_addx(c,D,A,B,0,1) +#define ppc_addo(c,D,A,B) ppc_addx(c,D,A,B,1,0) +#define ppc_addod(c,D,A,B) ppc_addx(c,D,A,B,1,1) + +#define ppc_addcx(c,D,A,B,OE,Rc) ppc_emit32(c, (31 << 26) | ((D) << 21) | ((A) << 16) | ((B) << 11) | (OE << 10) | (10 << 1) | Rc) +#define ppc_addc(c,D,A,B) ppc_addcx(c,D,A,B,0,0) +#define ppc_addcd(c,D,A,B) ppc_addcx(c,D,A,B,0,1) +#define ppc_addco(c,D,A,B) ppc_addcx(c,D,A,B,1,0) +#define ppc_addcod(c,D,A,B) ppc_addcx(c,D,A,B,1,1) + +#define ppc_addex(c,D,A,B,OE,Rc) ppc_emit32(c, (31 << 26) | ((D) << 21) | ((A) << 16) | ((B) << 11) | (OE << 10) | (138 << 1) | Rc) +#define ppc_adde(c,D,A,B) ppc_addex(c,D,A,B,0,0) +#define ppc_added(c,D,A,B) ppc_addex(c,D,A,B,0,1) +#define ppc_addeo(c,D,A,B) ppc_addex(c,D,A,B,1,0) +#define ppc_addeod(c,D,A,B) ppc_addex(c,D,A,B,1,1) + +#define ppc_addic(c,D,A,i) ppc_emit32(c, (12 << 26) | ((D) << 21) | ((A) << 16) | (guint16)(i)) +#define ppc_addicd(c,D,A,i) ppc_emit32(c, (13 << 26) | ((D) << 21) | ((A) << 16) | (guint16)(i)) + +#define ppc_addmex(c,D,A,OE,RC) ppc_emit32(c, (31 << 26) | ((D) << 21 ) | ((A) << 16) | (0 << 11) | ((OE) << 10) | (234 << 1) | RC) +#define ppc_addme(c,D,A) ppc_addmex(c,D,A,0,0) +#define ppc_addmed(c,D,A) ppc_addmex(c,D,A,0,1) +#define ppc_addmeo(c,D,A) ppc_addmex(c,D,A,1,0) +#define ppc_addmeod(c,D,A) ppc_addmex(c,D,A,1,1) + +#define ppc_addzex(c,D,A,OE,RC) ppc_emit32(c, (31 << 26) | ((D) << 21 ) | ((A) << 16) | (0 << 11) | ((OE) << 10) | (202 << 1) | RC) +#define ppc_addze(c,D,A) ppc_addzex(c,D,A,0,0) +#define ppc_addzed(c,D,A) ppc_addzex(c,D,A,0,1) +#define ppc_addzeo(c,D,A) ppc_addzex(c,D,A,1,0) +#define ppc_addzeod(c,D,A) ppc_addzex(c,D,A,1,1) + +#define ppc_andx(c,S,A,B,RC) ppc_emit32(c, (31 << 26) | ((S) << 21 ) | ((A) << 16) | ((B) << 11) | (28 << 1) | RC) +#define ppc_and(c,S,A,B) ppc_andx(c,S,A,B,0) +#define ppc_andd(c,S,A,B) ppc_andx(c,S,A,B,1) + +#define ppc_andcx(c,S,A,B,RC) ppc_emit32(c, (31 << 26) | ((S) << 21 ) | ((A) << 16) | ((B) << 11) | (60 << 1) | RC) +#define ppc_andc(c,S,A,B) ppc_andcx(c,S,A,B,0) +#define ppc_andcd(c,S,A,B) ppc_andcx(c,S,A,B,1) + +#define ppc_andid(c,S,A,ui) ppc_emit32(c, (28 << 26) | ((S) << 21 ) | ((A) << 16) | ((guint16)(ui))) +#define ppc_andisd(c,S,A,ui) ppc_emit32(c, (29 << 26) | ((S) << 21 ) | ((A) << 16) | ((guint16)(ui))) + +#define ppc_bcx(c,BO,BI,BD,AA,LK) ppc_emit32(c, (16 << 26) | ((BO) << 21 )| ((BI) << 16) | (BD << 2) | ((AA) << 1) | LK) +#define ppc_bc(c,BO,BI,BD) ppc_bcx(c,BO,BI,BD,0,0) +#define ppc_bca(c,BO,BI,BD) ppc_bcx(c,BO,BI,BD,1,0) +#define ppc_bcl(c,BO,BI,BD) ppc_bcx(c,BO,BI,BD,0,1) +#define ppc_bcla(c,BO,BI,BD) ppc_bcx(c,BO,BI,BD,1,1) + +#define ppc_bcctrx(c,BO,BI,LK) ppc_emit32(c, (19 << 26) | (BO << 21 )| (BI << 16) | (0 << 11) | (528 << 1) | LK) +#define ppc_bcctr(c,BO,BI) ppc_bcctrx(c,BO,BI,0) +#define ppc_bcctrl(c,BO,BI) ppc_bcctrx(c,BO,BI,1) + +#define ppc_bnectrp(c,BO,BI) ppc_bcctr(c,BO,BI) +#define ppc_bnectrlp(c,BO,BI) ppc_bcctr(c,BO,BI) + +#define ppc_bclrx(c,BO,BI,BH,LK) ppc_emit32(c, (19 << 26) | ((BO) << 21 )| ((BI) << 16) | (0 << 13) | ((BH) << 11) | (16 << 1) | (LK)) +#define ppc_bclr(c,BO,BI,BH) ppc_bclrx(c,BO,BI,BH,0) +#define ppc_bclrl(c,BO,BI,BH) ppc_bclrx(c,BO,BI,BH,1) + +#define ppc_bnelrp(c,BO,BI) ppc_bclr(c,BO,BI,0) +#define ppc_bnelrlp(c,BO,BI) ppc_bclr(c,BO,BI,0) + +#define ppc_cmp(c,cfrD,L,A,B) ppc_emit32(c, (31 << 26) | ((cfrD) << 23) | (0 << 22) | ((L) << 21) | ((A) << 16) | ((B) << 11) | (0 << 1) | 0) +#define ppc_cmpi(c,cfrD,L,A,B) ppc_emit32(c, (11 << 26) | (cfrD << 23) | (0 << 22) | (L << 21) | (A << 16) | (guint16)(B)) +#define ppc_cmpl(c,cfrD,L,A,B) ppc_emit32(c, (31 << 26) | ((cfrD) << 23) | (0 << 22) | ((L) << 21) | ((A) << 16) | ((B) << 11) | (32 << 1) | 0) +#define ppc_cmpli(c,cfrD,L,A,B) ppc_emit32(c, (10 << 26) | (cfrD << 23) | (0 << 22) | (L << 21) | (A << 16) | (guint16)(B)) +#define ppc_cmpw(c,cfrD,A,B) ppc_cmp(c, (cfrD), 0, (A), (B)) + +#define ppc_cntlzwx(c,S,A,Rc) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (0 << 11) | (26 << 1) | Rc) +#define ppc_cntlzw(c,S,A) ppc_cntlzwx(c,S,A,0) +#define ppc_cntlzwd(c,S,A) ppc_cntlzwx(c,S,A,1) + +#define ppc_crand(c,D,A,B) ppc_emit32(c, (19 << 26) | (D << 21) | (A << 16) | (B << 11) | (257 << 1) | 0) +#define ppc_crandc(c,D,A,B) ppc_emit32(c, (19 << 26) | (D << 21) | (A << 16) | (B << 11) | (129 << 1) | 0) +#define ppc_creqv(c,D,A,B) ppc_emit32(c, (19 << 26) | (D << 21) | (A << 16) | (B << 11) | (289 << 1) | 0) +#define ppc_crnand(c,D,A,B) ppc_emit32(c, (19 << 26) | (D << 21) | (A << 16) | (B << 11) | (225 << 1) | 0) +#define ppc_crnor(c,D,A,B) ppc_emit32(c, (19 << 26) | (D << 21) | (A << 16) | (B << 11) | (33 << 1) | 0) +#define ppc_cror(c,D,A,B) ppc_emit32(c, (19 << 26) | (D << 21) | (A << 16) | (B << 11) | (449 << 1) | 0) +#define ppc_crorc(c,D,A,B) ppc_emit32(c, (19 << 26) | (D << 21) | (A << 16) | (B << 11) | (417 << 1) | 0) +#define ppc_crxor(c,D,A,B) ppc_emit32(c, (19 << 26) | (D << 21) | (A << 16) | (B << 11) | (193 << 1) | 0) + +#define ppc_dcba(c,A,B) ppc_emit32(c, (31 << 26) | (0 << 21) | (A << 16) | (B << 11) | (758 << 1) | 0) +#define ppc_dcbf(c,A,B) ppc_emit32(c, (31 << 26) | (0 << 21) | (A << 16) | (B << 11) | (86 << 1) | 0) +#define ppc_dcbi(c,A,B) ppc_emit32(c, (31 << 26) | (0 << 21) | (A << 16) | (B << 11) | (470 << 1) | 0) +#define ppc_dcbst(c,A,B) ppc_emit32(c, (31 << 26) | (0 << 21) | (A << 16) | (B << 11) | (54 << 1) | 0) +#define ppc_dcbt(c,A,B) ppc_emit32(c, (31 << 26) | (0 << 21) | (A << 16) | (B << 11) | (278 << 1) | 0) +#define ppc_dcbtst(c,A,B) ppc_emit32(c, (31 << 26) | (0 << 21) | (A << 16) | (B << 11) | (246 << 1) | 0) +#define ppc_dcbz(c,A,B) ppc_emit32(c, (31 << 26) | (0 << 21) | (A << 16) | (B << 11) | (1014 << 1) | 0) + +#define ppc_divwx(c,D,A,B,OE,Rc) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (OE << 10) | (491 << 1) | Rc) +#define ppc_divw(c,D,A,B) ppc_divwx(c,D,A,B,0,0) +#define ppc_divwd(c,D,A,B) ppc_divwx(c,D,A,B,0,1) +#define ppc_divwo(c,D,A,B) ppc_divwx(c,D,A,B,1,0) +#define ppc_divwod(c,D,A,B) ppc_divwx(c,D,A,B,1,1) + +#define ppc_divwux(c,D,A,B,OE,Rc) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (OE << 10) | (459 << 1) | Rc) +#define ppc_divwu(c,D,A,B) ppc_divwux(c,D,A,B,0,0) +#define ppc_divwud(c,D,A,B) ppc_divwux(c,D,A,B,0,1) +#define ppc_divwuo(c,D,A,B) ppc_divwux(c,D,A,B,1,0) +#define ppc_divwuod(c,D,A,B) ppc_divwux(c,D,A,B,1,1) + +#define ppc_eciwx(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (310 << 1) | 0) +#define ppc_ecowx(c,S,A,B) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (438 << 1) | 0) +#define ppc_eieio(c) ppc_emit32(c, (31 << 26) | (0 << 21) | (0 << 16) | (0 << 11) | (854 << 1) | 0) + +#define ppc_eqvx(c,A,S,B,Rc) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (284 << 1) | Rc) +#define ppc_eqv(c,A,S,B) ppc_eqvx(c,A,S,B,0) +#define ppc_eqvd(c,A,S,B) ppc_eqvx(c,A,S,B,1) + +#define ppc_extsbx(c,A,S,Rc) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (0 << 11) | (954 << 1) | Rc) +#define ppc_extsb(c,A,S) ppc_extsbx(c,A,S,0) +#define ppc_extsbd(c,A,S) ppc_extsbx(c,A,S,1) + +#define ppc_extshx(c,A,S,Rc) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (0 << 11) | (922 << 1) | Rc) +#define ppc_extsh(c,A,S) ppc_extshx(c,A,S,0) +#define ppc_extshd(c,A,S) ppc_extshx(c,A,S,1) + +#define ppc_fabsx(c,D,B,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (0 << 16) | (B << 11) | (264 << 1) | Rc) +#define ppc_fabs(c,D,B) ppc_fabsx(c,D,B,0) +#define ppc_fabsd(c,D,B) ppc_fabsx(c,D,B,1) + +#define ppc_faddx(c,D,A,B,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (A << 16) | (B << 11) | (0 << 6) | (21 << 1) | Rc) +#define ppc_fadd(c,D,A,B) ppc_faddx(c,D,A,B,0) +#define ppc_faddd(c,D,A,B) ppc_faddx(c,D,A,B,1) + +#define ppc_faddsx(c,D,A,B,Rc) ppc_emit32(c, (59 << 26) | (D << 21) | (A << 16) | (B << 11) | (0 << 6) | (21 << 1) | Rc) +#define ppc_fadds(c,D,A,B) ppc_faddsx(c,D,A,B,0) +#define ppc_faddsd(c,D,A,B) ppc_faddsx(c,D,A,B,1) + +#define ppc_fcmpo(c,crfD,A,B) ppc_emit32(c, (63 << 26) | (crfD << 23) | (0 << 21) | (A << 16) | (B << 11) | (32 << 1) | 0) +#define ppc_fcmpu(c,crfD,A,B) ppc_emit32(c, (63 << 26) | (crfD << 23) | (0 << 21) | (A << 16) | (B << 11) | (0 << 1) | 0) + +#define ppc_fctiwx(c,D,B,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (0 << 16) | (B << 11) | (14 << 1) | Rc) +#define ppc_fctiw(c,D,B) ppc_fctiwx(c,D,B,0) +#define ppc_fctiwd(c,D,B) ppc_fctiwx(c,D,B,1) + +#define ppc_fctiwzx(c,D,B,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (0 << 16) | (B << 11) | (15 << 1) | Rc) +#define ppc_fctiwz(c,D,B) ppc_fctiwzx(c,D,B,0) +#define ppc_fctiwzd(c,D,B) ppc_fctiwzx(c,D,B,1) + +#define ppc_fdivx(c,D,A,B,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (A << 16) | (B << 11) | (0 << 6) | (18 << 1) | Rc) +#define ppc_fdiv(c,D,A,B) ppc_fdivx(c,D,A,B,0) +#define ppc_fdivd(c,D,A,B) ppc_fdivx(c,D,A,B,1) + +#define ppc_fdivsx(c,D,A,B,Rc) ppc_emit32(c, (59 << 26) | (D << 21) | (A << 16) | (B << 11) | (0 << 6) | (18 << 1) | Rc) +#define ppc_fdivs(c,D,A,B) ppc_fdivsx(c,D,A,B,0) +#define ppc_fdivsd(c,D,A,B) ppc_fdivsx(c,D,A,B,1) + +#define ppc_fmaddx(c,D,A,B,C,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (A << 16) | (B << 11) | (C << 6) | (29 << 1) | Rc) +#define ppc_fmadd(c,D,A,B,C) ppc_fmaddx(c,D,A,B,C,0) +#define ppc_fmaddd(c,D,A,B,C) ppc_fmaddx(c,D,A,B,C,1) + +#define ppc_fmaddsx(c,D,A,B,C,Rc) ppc_emit32(c, (59 << 26) | (D << 21) | (A << 16) | (B << 11) | (C << 6) | (29 << 1) | Rc) +#define ppc_fmadds(c,D,A,B,C) ppc_fmaddsx(c,D,A,B,C,0) +#define ppc_fmaddsd(c,D,A,B,C) ppc_fmaddsx(c,D,A,B,C,1) + +#define ppc_fmrx(c,D,B,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (0 << 16) | (B << 11) | (72 << 1) | Rc) +#define ppc_fmr(c,D,B) ppc_fmrx(c,D,B,0) +#define ppc_fmrd(c,D,B) ppc_fmrx(c,D,B,1) + +#define ppc_fmsubx(c,D,A,C,B,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (A << 16) | (B << 11) | (C << 6) | (28 << 1) | Rc) +#define ppc_fmsub(c,D,A,C,B) ppc_fmsubx(c,D,A,C,B,0) +#define ppc_fmsubd(c,D,A,C,B) ppc_fmsubx(c,D,A,C,B,1) + +#define ppc_fmsubsx(c,D,A,C,B,Rc) ppc_emit32(c, (59 << 26) | (D << 21) | (A << 16) | (B << 11) | (C << 6) | (28 << 1) | Rc) +#define ppc_fmsubs(c,D,A,C,B) ppc_fmsubsx(c,D,A,C,B,0) +#define ppc_fmsubsd(c,D,A,C,B) ppc_fmsubsx(c,D,A,C,B,1) + +#define ppc_fmulx(c,D,A,C,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (A << 16) | (0 << 11) | (C << 6) | (25 << 1) | Rc) +#define ppc_fmul(c,D,A,C) ppc_fmulx(c,D,A,C,0) +#define ppc_fmuld(c,D,A,C) ppc_fmulx(c,D,A,C,1) + +#define ppc_fmulsx(c,D,A,C,Rc) ppc_emit32(c, (59 << 26) | (D << 21) | (A << 16) | (0 << 11) | (C << 6) | (25 << 1) | Rc) +#define ppc_fmuls(c,D,A,C) ppc_fmulsx(c,D,A,C,0) +#define ppc_fmulsd(c,D,A,C) ppc_fmulsx(c,D,A,C,1) + +#define ppc_fnabsx(c,D,B,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (0 << 16) | (B << 11) | (136 << 1) | Rc) +#define ppc_fnabs(c,D,B) ppc_fnabsx(c,D,B,0) +#define ppc_fnabsd(c,D,B) ppc_fnabsx(c,D,B,1) + +#define ppc_fnegx(c,D,B,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (0 << 16) | (B << 11) | (40 << 1) | Rc) +#define ppc_fneg(c,D,B) ppc_fnegx(c,D,B,0) +#define ppc_fnegd(c,D,B) ppc_fnegx(c,D,B,1) + +#define ppc_fnmaddx(c,D,A,C,B,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (A << 16) | (B << 11) | (C << 6) | (31 << 1) | Rc) +#define ppc_fnmadd(c,D,A,C,B) ppc_fnmaddx(c,D,A,C,B,0) +#define ppc_fnmaddd(c,D,A,C,B) ppc_fnmaddx(c,D,A,C,B,1) + +#define ppc_fnmaddsx(c,D,A,C,B,Rc) ppc_emit32(c, (59 << 26) | (D << 21) | (A << 16) | (B << 11) | (C << 6) | (31 << 1) | Rc) +#define ppc_fnmadds(c,D,A,C,B) ppc_fnmaddsx(c,D,A,C,B,0) +#define ppc_fnmaddsd(c,D,A,C,B) ppc_fnmaddsx(c,D,A,C,B,1) + +#define ppc_fnmsubx(c,D,A,C,B,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (A << 16) | (B << 11) | (C << 6) | (30 << 1) | Rc) +#define ppc_fnmsub(c,D,A,C,B) ppc_fnmsubx(c,D,A,C,B,0) +#define ppc_fnmsubd(c,D,A,C,B) ppc_fnmsubx(c,D,A,C,B,1) + +#define ppc_fnmsubsx(c,D,A,C,B,Rc) ppc_emit32(c, (59 << 26) | (D << 21) | (A << 16) | (B << 11) | (C << 6) | (30 << 1) | Rc) +#define ppc_fnmsubs(c,D,A,C,B) ppc_fnmsubsx(c,D,A,C,B,0) +#define ppc_fnmsubsd(c,D,A,C,B) ppc_fnmsubsx(c,D,A,C,B,1) + +#define ppc_fresx(c,D,B,Rc) ppc_emit32(c, (59 << 26) | (D << 21) | (0 << 16) | (B << 11) | (0 << 6) | (24 << 1) | Rc) +#define ppc_fres(c,D,B) ppc_fresx(c,D,B,0) +#define ppc_fresd(c,D,B) ppc_fresx(c,D,B,1) + +#define ppc_frspx(c,D,B,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (0 << 16) | (B << 11) | (12 << 1) | Rc) +#define ppc_frsp(c,D,B) ppc_frspx(c,D,B,0) +#define ppc_frspd(c,D,B) ppc_frspx(c,D,B,1) + +#define ppc_frsqrtex(c,D,B,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (0 << 16) | (B << 11) | (0 << 6) | (26 << 1) | Rc) +#define ppc_frsqrte(c,D,B) ppc_frsqrtex(c,D,B,0) +#define ppc_frsqrted(c,D,B) ppc_frsqrtex(c,D,B,1) + +#define ppc_fselx(c,D,A,C,B,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (A << 16) | (B << 11) | (C << 6) | (23 << 1) | Rc) +#define ppc_fsel(c,D,A,C,B) ppc_fselx(c,D,A,C,B,0) +#define ppc_fseld(c,D,A,C,B) ppc_fselx(c,D,A,C,B,1) + +#define ppc_fsqrtx(c,D,B,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (0 << 16) | (B << 11) | (0 << 6) | (22 << 1) | Rc) +#define ppc_fsqrt(c,D,B) ppc_fsqrtx(c,D,B,0) +#define ppc_fsqrtd(c,D,B) ppc_fsqrtx(c,D,B,1) + +#define ppc_fsqrtsx(c,D,B,Rc) ppc_emit32(c, (59 << 26) | (D << 21) | (0 << 16) | (B << 11) | (0 << 6) | (22 << 1) | Rc) +#define ppc_fsqrts(c,D,B) ppc_fsqrtsx(c,D,B,0) +#define ppc_fsqrtsd(c,D,B) ppc_fsqrtsx(c,D,B,1) + +#define ppc_fsubx(c,D,A,B,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (A << 16) | (B << 11) | (0 << 6) | (20 << 1) | Rc) +#define ppc_fsub(c,D,A,B) ppc_fsubx(c,D,A,B,0) +#define ppc_fsubd(c,D,A,B) ppc_fsubx(c,D,A,B,1) + +#define ppc_fsubsx(c,D,A,B,Rc) ppc_emit32(c, (59 << 26) | (D << 21) | (A << 16) | (B << 11) | (0 << 6) | (20 << 1) | Rc) +#define ppc_fsubs(c,D,A,B) ppc_fsubsx(c,D,A,B,0) +#define ppc_fsubsd(c,D,A,B) ppc_fsubsx(c,D,A,B,1) + +#define ppc_icbi(c,A,B) ppc_emit32(c, (31 << 26) | (0 << 21) | (A << 16) | (B << 11) | (982 << 1) | 0) + +#define ppc_isync(c) ppc_emit32(c, (19 << 26) | (0 << 11) | (150 << 1) | 0) + +#define ppc_lbzu(c,D,d,A) ppc_emit32(c, (35 << 26) | (D << 21) | (A << 16) | (guint16)d) +#define ppc_lbzux(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (119 << 1) | 0) +#define ppc_lbzx(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (87 << 1) | 0) + +#define ppc_lfdu(c,D,d,A) ppc_emit32(c, (51 << 26) | (D << 21) | (A << 16) | (guint16)d) +#define ppc_lfdux(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (631 << 1) | 0) +#define ppc_lfdx(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (599 << 1) | 0) + +#define ppc_lfsu(c,D,d,A) ppc_emit32(c, (49 << 26) | (D << 21) | (A << 16) | (guint16)d) +#define ppc_lfsux(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (567 << 1) | 0) +#define ppc_lfsx(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (535 << 1) | 0) + +#define ppc_lha(c,D,d,A) ppc_emit32(c, (42 << 26) | (D << 21) | (A << 16) | (guint16)d) +#define ppc_lhau(c,D,d,A) ppc_emit32(c, (43 << 26) | (D << 21) | (A << 16) | (guint16)d) +#define ppc_lhaux(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (375 << 1) | 0) +#define ppc_lhax(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (343 << 1) | 0) +#define ppc_lhbrx(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (790 << 1) | 0) +#define ppc_lhzu(c,D,d,A) ppc_emit32(c, (41 << 26) | (D << 21) | (A << 16) | (guint16)d) + +#define ppc_lhzux(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (311 << 1) | 0) +#define ppc_lhzx(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (279 << 1) | 0) + +#define ppc_lmw(c,D,d,A) ppc_emit32(c, (46 << 26) | (D << 21) | (A << 16) | (guint16)d) + +#define ppc_lswi(c,D,A,NB) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (NB << 11) | (597 << 1) | 0) +#define ppc_lswx(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (533 << 1) | 0) +#define ppc_lwarx(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (20 << 1) | 0) +#define ppc_lwbrx(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (534 << 1) | 0) + +#define ppc_lwzu(c,D,d,A) ppc_emit32(c, (33 << 26) | (D << 21) | (A << 16) | (guint16)d) +#define ppc_lwzux(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (55 << 1) | 0) +#define ppc_lwzx(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (23 << 1) | 0) + +#define ppc_mcrf(c,crfD,crfS) ppc_emit32(c, (19 << 26) | (crfD << 23) | (0 << 21) | (crfS << 18) | 0) +#define ppc_mcrfs(c,crfD,crfS) ppc_emit32(c, (63 << 26) | (crfD << 23) | (0 << 21) | (crfS << 18) | (0 << 16) | (64 << 1) | 0) +#define ppc_mcrxr(c,crfD) ppc_emit32(c, (31 << 26) | (crfD << 23) | (0 << 16) | (512 << 1) | 0) + +#define ppc_mfcr(c,D) ppc_emit32(c, (31 << 26) | (D << 21) | (0 << 16) | (19 << 1) | 0) +#define ppc_mffsx(c,D,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (0 << 16) | (583 << 1) | Rc) +#define ppc_mffs(c,D) ppc_mffsx(c,D,0) +#define ppc_mffsd(c,D) ppc_mffsx(c,D,1) +#define ppc_mfmsr(c,D) ppc_emit32(c, (31 << 26) | (D << 21) | (0 << 16) | (83 << 1) | 0) +#define ppc_mfsr(c,D,SR) ppc_emit32(c, (31 << 26) | (D << 21) | (0 << 20) | (SR << 16) | (0 << 11) | (595 << 1) | 0) +#define ppc_mfsrin(c,D,B) ppc_emit32(c, (31 << 26) | (D << 21) | (0 << 16) | (B << 11) | (659 << 1) | 0) +#define ppc_mftb(c,D,TBR) ppc_emit32(c, (31 << 26) | (D << 21) | (TBR << 11) | (371 << 1) | 0) + +#define ppc_mtcrf(c,CRM,S) ppc_emit32(c, (31 << 26) | (S << 21) | (0 << 20) | (CRM << 12) | (0 << 11) | (144 << 1) | 0) + +#define ppc_mtfsb0x(c,CRB,Rc) ppc_emit32(c, (63 << 26) | (CRB << 21) | (0 << 11) | (70 << 1) | Rc) +#define ppc_mtfsb0(c,CRB) ppc_mtfsb0x(c,CRB,0) +#define ppc_mtfsb0d(c,CRB) ppc_mtfsb0x(c,CRB,1) + +#define ppc_mtfsb1x(c,CRB,Rc) ppc_emit32(c, (63 << 26) | (CRB << 21) | (0 << 11) | (38 << 1) | Rc) +#define ppc_mtfsb1(c,CRB) ppc_mtfsb1x(c,CRB,0) +#define ppc_mtfsb1d(c,CRB) ppc_mtfsb1x(c,CRB,1) + +#define ppc_mtfsfx(c,FM,B,Rc) ppc_emit32(c, (63 << 26) | (0 << 25) | (FM << 22) | (0 << 21) | (B << 11) | (711 << 1) | Rc) +#define ppc_mtfsf(c,FM,B) ppc_mtfsfx(c,FM,B,0) +#define ppc_mtfsfd(c,FM,B) ppc_mtfsfx(c,FM,B,1) + +#define ppc_mtfsfix(c,crfD,IMM,Rc) ppc_emit32(c, (63 << 26) | (crfD << 23) | (0 << 16) | (IMM << 12) | (0 << 11) | (134 << 1) | Rc) +#define ppc_mtfsfi(c,crfD,IMM) ppc_mtfsfix(c,crfD,IMM,0) +#define ppc_mtfsfid(c,crfD,IMM) ppc_mtfsfix(c,crfD,IMM,1) + +#define ppc_mtmsr(c, S) ppc_emit32(c, (31 << 26) | (S << 21) | (0 << 11) | (146 << 1) | 0) + +#define ppc_mtsr(c,SR,S) ppc_emit32(c, (31 << 26) | (S << 21) | (0 << 20) | (SR << 16) | (0 << 11) | (210 << 1) | 0) +#define ppc_mtsrin(c,S,B) ppc_emit32(c, (31 << 26) | (S << 21) | (0 << 16) | (B << 11) | (242 << 1) | 0) + +#define ppc_mulhwx(c,D,A,B,Rc) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (0 << 10) | (75 << 1) | Rc) +#define ppc_mulhw(c,D,A,B) ppc_mulhwx(c,D,A,B,0) +#define ppc_mulhwd(c,D,A,B) ppc_mulhwx(c,D,A,B,1) + +#define ppc_mulhwux(c,D,A,B,Rc) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (0 << 10) | (11 << 1) | Rc) +#define ppc_mulhwu(c,D,A,B) ppc_mulhwux(c,D,A,B,0) +#define ppc_mulhwud(c,D,A,B) ppc_mulhwux(c,D,A,B,1) + +#define ppc_mulli(c,D,A,SIMM) ppc_emit32(c, ((07) << 26) | (D << 21) | (A << 16) | (guint16)(SIMM)) + +#define ppc_mullwx(c,D,A,B,OE,Rc) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (OE << 10) | (235 << 1) | Rc) +#define ppc_mullw(c,D,A,B) ppc_mullwx(c,D,A,B,0,0) +#define ppc_mullwd(c,D,A,B) ppc_mullwx(c,D,A,B,0,1) +#define ppc_mullwo(c,D,A,B) ppc_mullwx(c,D,A,B,1,0) +#define ppc_mullwod(c,D,A,B) ppc_mullwx(c,D,A,B,1,1) + +#define ppc_nandx(c,A,S,B,Rc) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (476 << 1) | Rc) +#define ppc_nand(c,A,S,B) ppc_nandx(c,A,S,B,0) +#define ppc_nandd(c,A,S,B) ppc_nandx(c,A,S,B,1) + +#define ppc_negx(c,D,A,OE,Rc) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (0 << 11) | (OE << 10) | (104 << 1) | Rc) +#define ppc_neg(c,D,A) ppc_negx(c,D,A,0,0) +#define ppc_negd(c,D,A) ppc_negx(c,D,A,0,1) +#define ppc_nego(c,D,A) ppc_negx(c,D,A,1,0) +#define ppc_negod(c,D,A) ppc_negx(c,D,A,1,1) + +#define ppc_norx(c,A,S,B,Rc) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (124 << 1) | Rc) +#define ppc_nor(c,A,S,B) ppc_norx(c,A,S,B,0) +#define ppc_nord(c,A,S,B) ppc_norx(c,A,S,B,1) + +#define ppc_not(c,A,S) ppc_norx(c,A,S,S,0) + +#define ppc_orx(c,A,S,B,Rc) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (444 << 1) | Rc) +#define ppc_ord(c,A,S,B) ppc_orx(c,A,S,B,1) + +#define ppc_orcx(c,A,S,B,Rc) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (412 << 1) | Rc) +#define ppc_orc(c,A,S,B) ppc_orcx(c,A,S,B,0) +#define ppc_orcd(c,A,S,B) ppc_orcx(c,A,S,B,1) + +#define ppc_oris(c,A,S,UIMM) ppc_emit32(c, (25 << 26) | (S << 21) | (A << 16) | (guint16)(UIMM)) + +#define ppc_rfi(c) ppc_emit32(c, (19 << 26) | (0 << 11) | (50 << 1) | 0) + +#define ppc_rlwimix(c,A,S,SH,MB,ME,Rc) ppc_emit32(c, (20 << 26) | (S << 21) | (A << 16) | (SH << 11) | (MB << 6) | (ME << 1) | Rc) +#define ppc_rlwimi(c,A,S,SH,MB,ME) ppc_rlwimix(c,A,S,SH,MB,ME,0) +#define ppc_rlwimid(c,A,S,SH,MB,ME) ppc_rlwimix(c,A,S,SH,MB,ME,1) + +#define ppc_rlwinmx(c,A,S,SH,MB,ME,Rc) ppc_emit32(c, (21 << 26) | ((S) << 21) | ((A) << 16) | ((SH) << 11) | ((MB) << 6) | ((ME) << 1) | (Rc)) +#define ppc_rlwinm(c,A,S,SH,MB,ME) ppc_rlwinmx(c,A,S,SH,MB,ME,0) +#define ppc_rlwinmd(c,A,S,SH,MB,ME) ppc_rlwinmx(c,A,S,SH,MB,ME,1) +#define ppc_extlwi(c,A,S,n,b) ppc_rlwinm(c,A,S, b, 0, (n) - 1) +#define ppc_extrwi(c,A,S,n,b) ppc_rlwinm(c,A,S, (b) + (n), 32 - (n), 31) +#define ppc_rotlwi(c,A,S,n) ppc_rlwinm(c,A,S, n, 0, 31) +#define ppc_rotrwi(c,A,S,n) ppc_rlwinm(c,A,S, 32 - (n), 0, 31) +#define ppc_slwi(c,A,S,n) ppc_rlwinm(c,A,S, n, 0, 31 - (n)) +#define ppc_srwi(c,A,S,n) ppc_rlwinm(c,A,S, 32 - (n), n, 31) +#define ppc_clrlwi(c,A,S,n) ppc_rlwinm(c,A,S, 0, n, 31) +#define ppc_clrrwi(c,A,S,n) ppc_rlwinm(c,A,S, 0, 0, 31 - (n)) +#define ppc_clrlslwi(c,A,S,b,n) ppc_rlwinm(c,A,S, n, (b) - (n), 31 - (n)) + +#define ppc_rlwnmx(c,A,S,SH,MB,ME,Rc) ppc_emit32(c, (23 << 26) | (S << 21) | (A << 16) | (SH << 11) | (MB << 6) | (ME << 1) | Rc) +#define ppc_rlwnm(c,A,S,SH,MB,ME) ppc_rlwnmx(c,A,S,SH,MB,ME,0) +#define ppc_rlwnmd(c,A,S,SH,MB,ME) ppc_rlwnmx(c,A,S,SH,MB,ME,1) + +#define ppc_sc(c) ppc_emit32(c, (17 << 26) | (0 << 2) | (1 << 1) | 0) + +#define ppc_slwx(c,S,A,B,Rc) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (24 << 1) | Rc) +#define ppc_slw(c,S,A,B) ppc_slwx(c,S,A,B,0) +#define ppc_slwd(c,S,A,B) ppc_slwx(c,S,A,B,1) + +#define ppc_srawx(c,A,S,B,Rc) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (792 << 1) | Rc) +#define ppc_sraw(c,A,S,B) ppc_srawx(c,A,S,B,0) +#define ppc_srawd(c,A,S,B) ppc_srawx(c,A,S,B,1) + +#define ppc_srawix(c,A,S,SH,Rc) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (SH << 11) | (824 << 1) | Rc) +#define ppc_srawi(c,A,S,B) ppc_srawix(c,A,S,B,0) +#define ppc_srawid(c,A,S,B) ppc_srawix(c,A,S,B,1) + +#define ppc_srwx(c,A,S,SH,Rc) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (SH << 11) | (536 << 1) | Rc) +#define ppc_srw(c,A,S,B) ppc_srwx(c,A,S,B,0) +#define ppc_srwd(c,A,S,B) ppc_srwx(c,A,S,B,1) + +#define ppc_stbu(c,S,d,A) ppc_emit32(c, (39 << 26) | (S << 21) | (A << 16) | (guint16)(d)) + +#define ppc_stbux(c,S,A,B) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (247 << 1) | 0) +#define ppc_stbx(c,S,A,B) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (215 << 1) | 0) + +#define ppc_stfdu(c,S,d,A) ppc_emit32(c, (55 << 26) | (S << 21) | (A << 16) | (guint16)(d)) + +#define ppc_stfdx(c,S,A,B) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (727 << 1) | 0) +#define ppc_stfiwx(c,S,A,B) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (983 << 1) | 0) + +#define ppc_stfsu(c,S,d,A) ppc_emit32(c, (53 << 26) | (S << 21) | (A << 16) | (guint16)(d)) +#define ppc_stfsux(c,S,A,B) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (695 << 1) | 0) +#define ppc_stfsx(c,S,A,B) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (663 << 1) | 0) +#define ppc_sthbrx(c,S,A,B) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (918 << 1) | 0) +#define ppc_sthu(c,S,d,A) ppc_emit32(c, (45 << 26) | (S << 21) | (A << 16) | (guint16)(d)) +#define ppc_sthux(c,S,A,B) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (439 << 1) | 0) +#define ppc_sthx(c,S,A,B) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (407 << 1) | 0) +#define ppc_stmw(c,S,d,A) ppc_emit32(c, (47 << 26) | (S << 21) | (A << 16) | (guint16)d) +#define ppc_stswi(c,S,A,NB) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (NB << 11) | (725 << 1) | 0) +#define ppc_stswx(c,S,A,NB) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (NB << 11) | (661 << 1) | 0) +#define ppc_stwbrx(c,S,A,B) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (662 << 1) | 0) +#define ppc_stwcxd(c,S,A,B) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (150 << 1) | 1) +#define ppc_stwux(c,S,A,B) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (183 << 1) | 0) +#define ppc_stwx(c,S,A,B) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (151 << 1) | 0) + +#define ppc_subfx(c,D,A,B,OE,Rc) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (OE << 10) | (40 << 1) | Rc) +#define ppc_subf(c,D,A,B) ppc_subfx(c,D,A,B,0,0) +#define ppc_subfd(c,D,A,B) ppc_subfx(c,D,A,B,0,1) +#define ppc_subfo(c,D,A,B) ppc_subfx(c,D,A,B,1,0) +#define ppc_subfod(c,D,A,B) ppc_subfx(c,D,A,B,1,1) + +#define ppc_sub(c,D,A,B) ppc_subf(c,D,B,A) + +#define ppc_subfcx(c,D,A,B,OE,Rc) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (OE << 10) | (8 << 1) | Rc) +#define ppc_subfc(c,D,A,B) ppc_subfcx(c,D,A,B,0,0) +#define ppc_subfcd(c,D,A,B) ppc_subfcx(c,D,A,B,0,1) +#define ppc_subfco(c,D,A,B) ppc_subfcx(c,D,A,B,1,0) +#define ppc_subfcod(c,D,A,B) ppc_subfcx(c,D,A,B,1,1) + +#define ppc_subfex(c,D,A,B,OE,Rc) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (OE << 10) | (136 << 1) | Rc) +#define ppc_subfe(c,D,A,B) ppc_subfex(c,D,A,B,0,0) +#define ppc_subfed(c,D,A,B) ppc_subfex(c,D,A,B,0,1) +#define ppc_subfeo(c,D,A,B) ppc_subfex(c,D,A,B,1,0) +#define ppc_subfeod(c,D,A,B) ppc_subfex(c,D,A,B,1,1) + +#define ppc_subfic(c,D,A,SIMM) ppc_emit32(c, (8 << 26) | (D << 21) | (A << 16) | (guint16)(SIMM)) + +#define ppc_subfmex(c,D,A,OE,Rc) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (0 << 11) | (OE << 10) | (232 << 1) | Rc) +#define ppc_subfme(c,D,A) ppc_subfmex(c,D,A,0,0) +#define ppc_subfmed(c,D,A) ppc_subfmex(c,D,A,0,1) +#define ppc_subfmeo(c,D,A) ppc_subfmex(c,D,A,1,0) +#define ppc_subfmeod(c,D,A) ppc_subfmex(c,D,A,1,1) + +#define ppc_subfzex(c,D,A,OE,Rc) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (0 << 11) | (OE << 10) | (200 << 1) | Rc) +#define ppc_subfze(c,D,A) ppc_subfzex(c,D,A,0,0) +#define ppc_subfzed(c,D,A) ppc_subfzex(c,D,A,0,1) +#define ppc_subfzeo(c,D,A) ppc_subfzex(c,D,A,1,0) +#define ppc_subfzeod(c,D,A) ppc_subfzex(c,D,A,1,1) + +#define ppc_sync(c) ppc_emit32(c, (31 << 26) | (0 << 11) | (598 << 1) | 0) +#define ppc_tlbia(c) ppc_emit32(c, (31 << 26) | (0 << 11) | (370 << 1) | 0) +#define ppc_tlbie(c,B) ppc_emit32(c, (31 << 26) | (0 << 16) | (B << 11) | (306 << 1) | 0) +#define ppc_tlbsync(c) ppc_emit32(c, (31 << 26) | (0 << 11) | (566 << 1) | 0) + +#define ppc_tw(c,TO,A,B) ppc_emit32(c, (31 << 26) | (TO << 21) | (A << 16) | (B << 11) | (4 << 1) | 0) +#define ppc_twi(c,TO,A,SIMM) ppc_emit32(c, (3 << 26) | (TO << 21) | (A << 16) | (guint16)(SIMM)) + +#define ppc_xorx(c,A,S,B,RC) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (316 << 1) | RC) +#define ppc_xor(c,A,S,B) ppc_xorx(c,A,S,B,0) +#define ppc_xord(c,A,S,B) ppc_xorx(c,A,S,B,1) + +#define ppc_xori(c,S,A,UIMM) ppc_emit32(c, (26 << 26) | (S << 21) | (A << 16) | (guint16)(UIMM)) +#define ppc_xoris(c,S,A,UIMM) ppc_emit32(c, (27 << 26) | (S << 21) | (A << 16) | (guint16)(UIMM)) + +/* this marks the end of my work, ct */ + +/* PPC64 */ + +/* The following FP instructions are not are available to 32-bit + implementations (prior to PowerISA-V2.01 but are available to + 32-bit mode programs on 64-bit PowerPC implementations and all + processors compliant with PowerISA-2.01 or later. */ + +#define ppc_fcfidx(c,D,B,Rc) ppc_emit32(c, (63 << 26) | ((D) << 21) | (0 << 16) | ((B) << 11) | (846 << 1) | (Rc)) +#define ppc_fcfid(c,D,B) ppc_fcfidx(c,D,B,0) +#define ppc_fcfidd(c,D,B) ppc_fcfidx(c,D,B,1) + +#define ppc_fctidx(c,D,B,Rc) ppc_emit32(c, (63 << 26) | ((D) << 21) | (0 << 16) | ((B) << 11) | (814 << 1) | (Rc)) +#define ppc_fctid(c,D,B) ppc_fctidx(c,D,B,0) +#define ppc_fctidd(c,D,B) ppc_fctidx(c,D,B,1) + +#define ppc_fctidzx(c,D,B,Rc) ppc_emit32(c, (63 << 26) | ((D) << 21) | (0 << 16) | ((B) << 11) | (815 << 1) | (Rc)) +#define ppc_fctidz(c,D,B) ppc_fctidzx(c,D,B,0) +#define ppc_fctidzd(c,D,B) ppc_fctidzx(c,D,B,1) + +#ifdef __mono_ppc64__ + +#define ppc_load_sequence(c,D,v) G_STMT_START { \ + ppc_lis ((c), (D), ((guint64)(v) >> 48) & 0xffff); \ + ppc_ori ((c), (D), (D), ((guint64)(v) >> 32) & 0xffff); \ + ppc_sldi ((c), (D), (D), 32); \ + ppc_oris ((c), (D), (D), ((guint64)(v) >> 16) & 0xffff); \ + ppc_ori ((c), (D), (D), (guint64)(v) & 0xffff); \ + } G_STMT_END + +#define PPC_LOAD_SEQUENCE_LENGTH 20 + +#define ppc_is_imm32(val) (((((gint64)val)>> 31) == 0) || ((((gint64)val)>> 31) == -1)) +#define ppc_is_imm48(val) (((((gint64)val)>> 47) == 0) || ((((gint64)val)>> 47) == -1)) + +#define ppc_load48(c,D,v) G_STMT_START { \ + ppc_li ((c), (D), ((gint64)(v) >> 32) & 0xffff); \ + ppc_sldi ((c), (D), (D), 32); \ + ppc_oris ((c), (D), (D), ((guint64)(v) >> 16) & 0xffff); \ + ppc_ori ((c), (D), (D), (guint64)(v) & 0xffff); \ + } G_STMT_END + +#define ppc_load(c,D,v) G_STMT_START { \ + if (ppc_is_imm16 ((guint64)(v))) { \ + ppc_li ((c), (D), (guint16)(guint64)(v)); \ + } else if (ppc_is_imm32 ((guint64)(v))) { \ + ppc_load32 ((c), (D), (guint32)(guint64)(v)); \ + } else if (ppc_is_imm48 ((guint64)(v))) { \ + ppc_load48 ((c), (D), (guint64)(v)); \ + } else { \ + ppc_load_sequence ((c), (D), (guint64)(v)); \ + } \ + } G_STMT_END + +#if _CALL_ELF == 2 +#define ppc_load_func(c,D,V) ppc_load_sequence ((c), (D), (V)) +#else +#define ppc_load_func(c,D,v) G_STMT_START { \ + ppc_load_sequence ((c), ppc_r12, (guint64)(gsize)(v)); \ + ppc_ldptr ((c), ppc_r2, sizeof (gpointer), ppc_r12); \ + ppc_ldptr ((c), (D), 0, ppc_r12); \ + } G_STMT_END +#endif + +#define ppc_load_multiple_regs(c,D,d,A) G_STMT_START { \ + int __i, __o = (d); \ + for (__i = (D); __i <= 31; ++__i) { \ + ppc_ldr ((c), __i, __o, (A)); \ + __o += sizeof (guint64); \ + } \ + } G_STMT_END + +#define ppc_store_multiple_regs(c,S,d,A) G_STMT_START { \ + int __i, __o = (d); \ + for (__i = (S); __i <= 31; ++__i) { \ + ppc_str ((c), __i, __o, (A)); \ + __o += sizeof (guint64); \ + } \ + } G_STMT_END + +#define ppc_compare(c,cfrD,A,B) ppc_cmp((c), (cfrD), 1, (A), (B)) +#define ppc_compare_reg_imm(c,cfrD,A,B) ppc_cmpi((c), (cfrD), 1, (A), (B)) +#define ppc_compare_log(c,cfrD,A,B) ppc_cmpl((c), (cfrD), 1, (A), (B)) + +#define ppc_shift_left(c,A,S,B) ppc_sld((c), (A), (S), (B)) +#define ppc_shift_left_imm(c,A,S,n) ppc_sldi((c), (A), (S), (n)) + +#define ppc_shift_right_imm(c,A,S,B) ppc_srdi((c), (A), (S), (B)) +#define ppc_shift_right_arith_imm(c,A,S,B) ppc_sradi((c), (A), (S), (B)) + +#define ppc_multiply(c,D,A,B) ppc_mulld((c), (D), (A), (B)) + +#define ppc_clear_right_imm(c,A,S,n) ppc_clrrdi((c), (A), (S), (n)) + +#define ppc_divdx(c,D,A,B,OE,Rc) ppc_emit32(c, (31 << 26) | ((D) << 21) | ((A) << 16) | ((B) << 11) | ((OE) << 10) | (489 << 1) | (Rc)) +#define ppc_divd(c,D,A,B) ppc_divdx(c,D,A,B,0,0) +#define ppc_divdd(c,D,A,B) ppc_divdx(c,D,A,B,0,1) +#define ppc_divdo(c,D,A,B) ppc_divdx(c,D,A,B,1,0) +#define ppc_divdod(c,D,A,B) ppc_divdx(c,D,A,B,1,1) + +#define ppc_divdux(c,D,A,B,OE,Rc) ppc_emit32(c, (31 << 26) | ((D) << 21) | ((A) << 16) | ((B) << 11) | ((OE) << 10) | (457 << 1) | (Rc)) +#define ppc_divdu(c,D,A,B) ppc_divdux(c,D,A,B,0,0) +#define ppc_divdud(c,D,A,B) ppc_divdux(c,D,A,B,0,1) +#define ppc_divduo(c,D,A,B) ppc_divdux(c,D,A,B,1,0) +#define ppc_divduod(c,D,A,B) ppc_divdux(c,D,A,B,1,1) + +#define ppc_extswx(c,S,A,Rc) ppc_emit32(c, (31 << 26) | ((S) << 21) | ((A) << 16) | (0 << 11) | (986 << 1) | (Rc)) +#define ppc_extsw(c,A,S) ppc_extswx(c,S,A,0) +#define ppc_extswd(c,A,S) ppc_extswx(c,S,A,1) + +/* These move float to/from instuctions are only available on POWER6 in + native mode. These instruction are faster then the equivalent + store/load because they avoid the store queue and associated delays. + These instructions should only be used in 64-bit mode unless the + kernel preserves the 64-bit GPR on signals and dispatch in 32-bit + mode. The Linux kernel does not. */ +#define ppc_mftgpr(c,T,B) ppc_emit32(c, (31 << 26) | ((T) << 21) | (0 << 16) | ((B) << 11) | (735 << 1) | 0) +#define ppc_mffgpr(c,T,B) ppc_emit32(c, (31 << 26) | ((T) << 21) | (0 << 16) | ((B) << 11) | (607 << 1) | 0) + +#define ppc_ld(c,D,ds,A) ppc_emit32(c, (58 << 26) | ((D) << 21) | ((A) << 16) | ((guint32)(ds) & 0xfffc) | 0) +#define ppc_lwa(c,D,ds,A) ppc_emit32(c, (58 << 26) | ((D) << 21) | ((A) << 16) | ((ds) & 0xfffc) | 2) +#define ppc_ldarx(c,D,A,B) ppc_emit32(c, (31 << 26) | ((D) << 21) | ((A) << 16) | ((B) << 11) | (84 << 1) | 0) +#define ppc_ldu(c,D,ds,A) ppc_emit32(c, (58 << 26) | ((D) << 21) | ((A) << 16) | ((guint32)(ds) & 0xfffc) | 1) +#define ppc_ldux(c,D,A,B) ppc_emit32(c, (31 << 26) | ((D) << 21) | ((A) << 16) | ((B) << 11) | (53 << 1) | 0) +#define ppc_lwaux(c,D,A,B) ppc_emit32(c, (31 << 26) | ((D) << 21) | ((A) << 16) | ((B) << 11) | (373 << 1) | 0) +#define ppc_ldx(c,D,A,B) ppc_emit32(c, (31 << 26) | ((D) << 21) | ((A) << 16) | ((B) << 11) | (21 << 1) | 0) +#define ppc_lwax(c,D,A,B) ppc_emit32(c, (31 << 26) | ((D) << 21) | ((A) << 16) | ((B) << 11) | (341 << 1) | 0) + +#define ppc_mulhdx(c,D,A,B,Rc) ppc_emit32(c, (31 << 26) | ((D) << 21) | ((A) << 16) | ((B) << 11) | (0 << 10) | (73 << 1) | (Rc)) +#define ppc_mulhd(c,D,A,B) ppc_mulhdx(c,D,A,B,0) +#define ppc_mulhdd(c,D,A,B) ppc_mulhdx(c,D,A,B,1) +#define ppc_mulhdux(c,D,A,B,Rc) ppc_emit32(c, (31 << 26) | ((D) << 21) | ((A) << 16) | ((B) << 11) | (0 << 10) | (9 << 1) | (Rc)) +#define ppc_mulhdu(c,D,A,B) ppc_mulhdux(c,D,A,B,0) +#define ppc_mulhdud(c,D,A,B) ppc_mulhdux(c,D,A,B,1) + +#define ppc_mulldx(c,D,A,B,OE,Rc) ppc_emit32(c, (31 << 26) | ((D) << 21) | ((A) << 16) | ((B) << 11) | ((OE) << 10) | (233 << 1) | (Rc)) +#define ppc_mulld(c,D,A,B) ppc_mulldx(c,D,A,B,0,0) +#define ppc_mulldd(c,D,A,B) ppc_mulldx(c,D,A,B,0,1) +#define ppc_mulldo(c,D,A,B) ppc_mulldx(c,D,A,B,1,0) +#define ppc_mulldod(c,D,A,B) ppc_mulldx(c,D,A,B,1,1) + +#define ppc_rldclx(c,A,S,B,MB,Rc) ppc_emit32(c, (30 << 26) | ((S) << 21) | ((A) << 16) | ((B) << 11) | (ppc_split_5_1(MB) << 5) | (8 << 1) | (Rc)) +#define ppc_rldcl(c,A,S,B,MB) ppc_rldclx(c,A,S,B,MB,0) +#define ppc_rldcld(c,A,S,B,MB) ppc_rldclx(c,A,S,B,MB,1) +#define ppc_rotld(c,A,S,B) ppc_rldcl(c, A, S, B, 0) + +#define ppc_rldcrx(c,A,S,B,ME,Rc) ppc_emit32(c, (30 << 26) | ((S) << 21) | ((A) << 16) | ((B) << 11) | (ppc_split_5_1(ME) << 5) | (9 << 1) | (Rc)) +#define ppc_rldcr(c,A,S,B,ME) ppc_rldcrx(c,A,S,B,ME,0) +#define ppc_rldcrd(c,A,S,B,ME) ppc_rldcrx(c,A,S,B,ME,1) + +#define ppc_rldicx(c,S,A,SH,MB,Rc) ppc_emit32(c, (30 << 26) | ((S) << 21) | ((A) << 16) | (ppc_split_5_1_5(SH) << 11) | (ppc_split_5_1(MB) << 5) | (2 << 2) | (ppc_split_5_1_1(SH) << 1) | (Rc)) +#define ppc_rldic(c,A,S,SH,MB) ppc_rldicx(c,S,A,SH,MB,0) +#define ppc_rldicd(c,A,S,SH,MB) ppc_rldicx(c,S,A,SH,MB,1) + +#define ppc_rldiclx(c,S,A,SH,MB,Rc) ppc_emit32(c, (30 << 26) | ((S) << 21) | ((A) << 16) | (ppc_split_5_1_5(SH) << 11) | (ppc_split_5_1(MB) << 5) | (0 << 2) | (ppc_split_5_1_1(SH) << 1) | (Rc)) +#define ppc_rldicl(c,A,S,SH,MB) ppc_rldiclx(c,S,A,SH,MB,0) +#define ppc_rldicld(c,A,S,SH,MB) ppc_rldiclx(c,S,A,SH,MB,1) +#define ppc_extrdi(c,A,S,n,b) ppc_rldicl(c,A,S, (b) + (n), 64 - (n)) +#define ppc_rotldi(c,A,S,n) ppc_rldicl(c,A,S, n, 0) +#define ppc_rotrdi(c,A,S,n) ppc_rldicl(c,A,S, 64 - (n), 0) +#define ppc_srdi(c,A,S,n) ppc_rldicl(c,A,S, 64 - (n), n) +#define ppc_clrldi(c,A,S,n) ppc_rldicl(c,A,S, 0, n) + +#define ppc_rldicrx(c,A,S,SH,ME,Rc) ppc_emit32(c, (30 << 26) | ((S) << 21) | ((A) << 16) | (ppc_split_5_1_5(SH) << 11) | (ppc_split_5_1(ME) << 5) | (1 << 2) | (ppc_split_5_1_1(SH) << 1) | (Rc)) +#define ppc_rldicr(c,A,S,SH,ME) ppc_rldicrx(c,A,S,SH,ME,0) +#define ppc_rldicrd(c,A,S,SH,ME) ppc_rldicrx(c,A,S,SH,ME,1) +#define ppc_extldi(c,A,S,n,b) ppc_rldicr(c, A, S, b, (n) - 1) +#define ppc_sldi(c,A,S,n) ppc_rldicr(c, A, S, n, 63 - (n)) +#define ppc_clrrdi(c,A,S,n) ppc_rldicr(c, A, S, 0, 63 - (n)) + +#define ppc_rldimix(c,S,A,SH,MB,Rc) ppc_emit32(c, (30 << 26) | ((S) << 21) | ((A) << 16) | (ppc_split_5_1_5(SH) << 11) | (ppc_split_5_1(MB) << 5) | (3 << 2) | (ppc_split_5_1_1(SH) << 1) | (Rc)) +#define ppc_rldimi(c,A,S,SH,MB) ppc_rldimix(c,S,A,SH,MB,0) +#define ppc_rldimid(c,A,S,SH,MB) ppc_rldimix(c,S,A,SH,MB,1) + +#define ppc_slbia(c) ppc_emit32(c, (31 << 26) | (0 << 21) | (0 << 16) | (0 << 11) | (498 << 1) | 0) +#define ppc_slbie(c,B) ppc_emit32(c, (31 << 26) | (0 << 21) | (0 << 16) | ((B) << 11) | (434 << 1) | 0) +#define ppc_sldx(c,S,A,B,Rc) ppc_emit32(c, (31 << 26) | ((S) << 21) | ((A) << 16) | ((B) << 11) | (27 << 1) | (Rc)) +#define ppc_sld(c,A,S,B) ppc_sldx(c,S,A,B,0) +#define ppc_sldd(c,A,S,B) ppc_sldx(c,S,A,B,1) + +#define ppc_sradx(c,S,A,B,Rc) ppc_emit32(c, (31 << 26) | ((S) << 21) | ((A) << 16) | ((B) << 11) | (794 << 1) | (Rc)) +#define ppc_srad(c,A,S,B) ppc_sradx(c,S,A,B,0) +#define ppc_sradd(c,A,S,B) ppc_sradx(c,S,A,B,1) +#define ppc_sradix(c,S,A,SH,Rc) ppc_emit32(c, (31 << 26) | ((S) << 21) | ((A) << 16) | (((SH) & 31) << 11) | (413 << 2) | (((SH) >> 5) << 1) | (Rc)) +#define ppc_sradi(c,A,S,SH) ppc_sradix(c,S,A,SH,0) +#define ppc_sradid(c,A,S,SH) ppc_sradix(c,S,A,SH,1) + +#define ppc_srdx(c,S,A,B,Rc) ppc_emit32(c, (31 << 26) | ((S) << 21) | ((A) << 16) | ((B) << 11) | (539 << 1) | (Rc)) +#define ppc_srd(c,A,S,B) ppc_srdx(c,S,A,B,0) +#define ppc_srdd(c,A,S,B) ppc_srdx(c,S,A,B,1) + +#define ppc_std(c,S,ds,A) ppc_emit32(c, (62 << 26) | ((S) << 21) | ((A) << 16) | ((guint32)(ds) & 0xfffc) | 0) +#define ppc_stdcxd(c,S,A,B) ppc_emit32(c, (31 << 26) | ((S) << 21) | ((A) << 16) | ((B) << 11) | (214 << 1) | 1) +#define ppc_stdu(c,S,ds,A) ppc_emit32(c, (62 << 26) | ((S) << 21) | ((A) << 16) | ((guint32)(ds) & 0xfffc) | 1) +#define ppc_stdux(c,S,A,B) ppc_emit32(c, (31 << 26) | ((S) << 21) | ((A) << 16) | ((B) << 11) | (181 << 1) | 0) +#define ppc_stdx(c,S,A,B) ppc_emit32(c, (31 << 26) | ((S) << 21) | ((A) << 16) | ((B) << 11) | (149 << 1) | 0) + +#else +/* Always true for 32-bit */ +#define ppc_is_imm32(val) (1) +#endif + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/arch/s390x/.gitignore b/unity-2019.4.24f1-mbe/mono/arch/s390x/.gitignore new file mode 100644 index 000000000..341daec37 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/s390x/.gitignore @@ -0,0 +1,6 @@ +/Makefile +/Makefile.in +/.libs +/.deps +/*.la +/*.lo diff --git a/unity-2019.4.24f1-mbe/mono/arch/s390x/Makefile.am b/unity-2019.4.24f1-mbe/mono/arch/s390x/Makefile.am new file mode 100644 index 000000000..ce7f47097 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/s390x/Makefile.am @@ -0,0 +1,7 @@ + +AM_CPPFLAGS = $(GLIB_CFLAGS) -I$(top_srcdir) + +noinst_LTLIBRARIES = libmonoarch-s390x.la + +libmonoarch_s390x_la_SOURCES = tramp.c s390x-codegen.h + diff --git a/unity-2019.4.24f1-mbe/mono/arch/s390x/s390x-codegen.h b/unity-2019.4.24f1-mbe/mono/arch/s390x/s390x-codegen.h new file mode 100644 index 000000000..9c3750e21 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/s390x/s390x-codegen.h @@ -0,0 +1,1500 @@ +/* + Copyright (C) 2001 Radek Doulik + * Licensed under the MIT license. See LICENSE file in the project root for full license information. +*/ + +#ifndef S390X_H +#define S390X_H +#include +#include +#include + +#define FLOAT_REGS 2 /* No. float registers for parms */ +#define GENERAL_REGS 5 /* No. general registers for parms */ + +#define ARG_BASE s390_r10 /* Register for addressing arguments*/ +#define STKARG \ + (i*(sizeof(stackval))) /* Displacement of ith argument */ + +#define MINV_POS 160 /* MonoInvocation stack offset */ +#define STACK_POS (MINV_POS - sizeof (stackval) * sig->param_count) +#define OBJ_POS 8 +#define TYPE_OFFSET (G_STRUCT_OFFSET (stackval, type)) + +#define MIN_CACHE_LINE 256 + +/*------------------------------------------------------------------*/ +/* Sequence to add an int/long long to parameters to stack_from_data*/ +/*------------------------------------------------------------------*/ +#define ADD_ISTACK_PARM(r, i) \ + if (reg_param < GENERAL_REGS-(r)) { \ + s390_lay (p, s390_r4, 0, STK_BASE, \ + local_start + (reg_param - this_flag) * sizeof(long)); \ + reg_param += (i); \ + } else { \ + s390_lay (p, s390_r4, 0, STK_BASE, \ + sz.stack_size + MINV_POS + stack_param * sizeof(long)); \ + stack_param += (i); \ + } + +/*------------------------------------------------------------------*/ +/* Sequence to add a float/double to parameters to stack_from_data */ +/*------------------------------------------------------------------*/ +#define ADD_RSTACK_PARM(i) \ + if (fpr_param < FLOAT_REGS) { \ + s390_lay (p, s390_r4, 0, STK_BASE, \ + float_pos + (fpr_param * sizeof(float) * (i))); \ + fpr_param++; \ + } else { \ + stack_param += (stack_param % (i)); \ + s390_lay (p, s390_r4, 0, STK_BASE, \ + sz.stack_size + MINV_POS + stack_param * sizeof(float) * (i)); \ + stack_param += (i); \ + } + +/*------------------------------------------------------------------*/ +/* Sequence to add a structure ptr to parameters to stack_from_data */ +/*------------------------------------------------------------------*/ +#define ADD_TSTACK_PARM \ + if (reg_param < GENERAL_REGS) { \ + s390_ly (p, s390_r4, 0, STK_BASE, \ + local_start + (reg_param - this_flag) * sizeof(long)); \ + reg_param++; \ + } else { \ + s390_ly (p, s390_r4, 0, STK_BASE, \ + sz.stack_size + MINV_POS + stack_param * sizeof(long)); \ + stack_param++; \ + } + +#define ADD_PSTACK_PARM(r, i) \ + if (reg_param < GENERAL_REGS-(r)) { \ + s390_lay (p, s390_r4, 0, STK_BASE, \ + local_start + (reg_param - this_flag) * sizeof(long)); \ + reg_param += (i); \ + } else { \ + s390_ly (p, s390_r4, 0, STK_BASE, \ + sz.stack_size + MINV_POS + stack_param * sizeof(long)); \ + stack_param++; \ + } + +typedef enum { + s390_r0 = 0, + s390_r1, + s390_r2, + s390_r3, + s390_r4, + s390_r5, + s390_r6, + s390_r7, + s390_r8, + s390_r9, + s390_r10, + s390_r11, + s390_r12, + s390_r13, + s390_r14, + s390_r15, +} S390IntRegister; + +typedef enum { + s390_f0 = 0, + s390_f1, + s390_f2, + s390_f3, + s390_f4, + s390_f5, + s390_f6, + s390_f7, + s390_f8, + s390_f9, + s390_f10, + s390_f11, + s390_f12, + s390_f13, + s390_f14, + s390_f15, +} S390FloatRegister; + +typedef enum { + s390_a0 = 0, + s390_a1, + s390_a2, + s390_a3, + s390_a4, + s390_a5, + s390_a6, + s390_a7, + s390_a8, + s390_a9, + s390_a10, + s390_a11, + s390_a12, + s390_a13, + s390_a14, + s390_a15, +} S390AccRegister; + +typedef enum { + s390_fpc = 256, +} S390SpecialRegister; + +typedef enum { + s390_VR0 = 0, + s390_VR1 = 1, + s390_VR2 = 2, + s390_VR3 = 3, + s390_VR4 = 4, + s390_VR5 = 5, + s390_VR6 = 6, + s390_VR7 = 7, + s390_VR8 = 8, + s390_VR9 = 9, + s390_VR10 = 10, + s390_VR11 = 11, + s390_VR12 = 12, + s390_VR13 = 13, + s390_VR14 = 14, + s390_VR15 = 15, + s390_VR16 = 16, + s390_VR17 = 17, + s390_VR18 = 18, + s390_VR19 = 19, + s390_VR20 = 20, + s390_VR21 = 21, + s390_VR22 = 22, + s390_VR23 = 23, + s390_VR24 = 24, + s390_VR25 = 25, + s390_VR26 = 26, + s390_VR27 = 27, + s390_VR28 = 28, + s390_VR29 = 29, + s390_VR30 = 30, + s390_VR31 = 31, + s390_VR_NREG = 32, +} s390_VR_Reg_No; + +#define s390_is_imm16(val) ((glong)val >= (glong) SHRT_MIN && \ + (glong)val <= (glong) SHRT_MAX) +#define s390_is_imm32(val) ((glong)val >= (glong) INT_MIN && \ + (glong)val <= (glong) INT_MAX) +#define s390_is_uimm16(val) ((glong)val >= 0 && (glong)val <= (glong) USHRT_MAX) +#define s390_is_uimm32(val) ((glong)val >= 0 && (glong)val <= (glong) UINT_MAX) +#define s390_is_uimm20(val) ((glong)val >= 0 && (glong)val <= 1048575) +#define s390_is_imm20(val) ((glong)val >= -524288 && (glong)val <= 524287) +#define s390_is_imm12(val) ((glong)val >= (glong)-4096 && \ + (glong)val <= (glong)4095) +#define s390_is_uimm12(val) ((glong)val >= 0 && (glong)val <= 4095) + +#define STK_BASE s390_r15 +#define S390_SP s390_r15 +#define S390_FP s390_r11 +#define S390_MINIMAL_STACK_SIZE 160 +#define S390_REG_SAVE_OFFSET 48 +#define S390_PARM_SAVE_OFFSET 16 +#define S390_RET_ADDR_OFFSET 112 +#define S390_FLOAT_SAVE_OFFSET 128 + +#define S390_CC_ZR 8 +#define S390_CC_NE 7 +#define S390_CC_NZ 7 +#define S390_CC_LT 4 +#define S390_CC_GT 2 +#define S390_CC_GE 11 +#define S390_CC_NM 11 +#define S390_CC_LE 13 +#define S390_CC_OV 1 +#define S390_CC_NO 14 +#define S390_CC_CY 3 +#define S390_CC_NC 12 +#define S390_CC_UN 15 + +#define s390_word(addr, value) do \ +{ \ + * (guint32 *) addr = (guint32) value; \ + addr += sizeof(guint32); \ +} while (0) + +#define s390_float(addr, value) do \ +{ \ + * (gfloat *) addr = (gfloat) value; \ + addr += sizeof(gfloat); \ +} while (0) + +#define s390_llong(addr, value) do \ +{ \ + * (guint64 *) addr = (guint64) value; \ + addr += sizeof(guint64); \ +} while (0) + +#define s390_double(addr, value) do \ +{ \ + * (gdouble *) addr = (gdouble) value; \ + addr += sizeof(gdouble); \ +} while (0) + +typedef struct { + short op; +} E_Format; + +typedef struct { + char op; + int im; +} I_Format; + +typedef struct { + short op; + char xx; + char ri1 : 4; + char ri2 : 4; +} IE_Format; + +typedef struct { + short op; + short m1 : 4; + short ri2 : 12; + short i3; +} MII_Format; + +typedef struct { + char op; + char r1 : 4; + char r2 : 4; +} RR_Format; + +typedef struct { + short op; + char r1 : 4; + char xx : 4; + char r3 : 4; + char r4 : 4; +} __attribute__ ((__packed__)) RRD_Format; + +typedef struct { + short op; + char xx; + char r1 : 4; + char r2 : 4; +} RRE_Format; + +typedef struct { + short op; + char r1 : 4; + char xx : 4; + char r3 : 4; + char r2 : 4; +} RRF_Format_1; + +typedef struct { + short op; + char m3 : 4; + char xx : 4; + char r1 : 4; + char r2 : 4; +} RRF_Format_2; + +typedef struct { + short op; + char r3 : 4; + char m4 : 4; + char r1 : 4; + char r2 : 4; +} RRF_Format_3; + +typedef struct { + short op; + char m3 : 4; + char m4 : 4; + char r1 : 4; + char r2 : 4; +} RRF_Format_4; + +typedef struct { + char op1; + char r1 : 4; + char r2 : 4; + short b4 : 4; + short d4 : 12; + char m3 : 4; + char xx : 4; + char op2; +} RRS_Format; + +typedef struct { + char op; + char r1 : 4; + char x2 : 4; + short b2 : 4; + short d2 : 12; +} RX_Format; + +typedef struct { + char op1; + char r3 : 4; + char x2 : 4; + short b2 : 4; + short d2 : 12; + char r1 : 4; + char xx : 4; + char op2; +} RXF_Format; + +typedef struct { + char op1; + char r1 : 4; + char x2 : 4; + int b2 : 4; + int d2 : 20; + char op2; +} __attribute__ ((__packed__)) RXY_Format; + +typedef struct { + char op; + char r1 : 4; + char r3 : 4; + short b2 : 4; + short d2 : 12; +} RS_Format_1; + +typedef struct { + char op; + char r1 : 4; + char m3 : 4; + short b2 : 4; + short d2 : 12; +} RS_Format_2; + +typedef struct { + char op; + char r1 : 4; + char xx : 4; + short b2 : 4; + short dl2 : 12; + char dh2; +} RS_Format_3; + +typedef struct { + char op1; + char r1 : 4; + char r3 : 4; + short b2 : 4; + short dl2 : 12; + char dh2; + char op2; +} __attribute__ ((__packed__)) RSY_Format_1; + +typedef struct { + char op1; + char r1 : 4; + char m3 : 4; + short b2 : 4; + short dl2 : 12; + char dh2; + char op2; +} __attribute__ ((__packed__)) RSY_Format_2; + +typedef struct { + char op1; + char l1 : 4; + char xx : 4; + short b1 : 4; + short d1 : 12; + char yy; + char op2; +} __attribute__ ((__packed__)) RSL_Format; + +typedef struct { + char op; + char r1 : 4; + char r3 : 4; + short i2; +} __attribute__ ((__packed__)) RSI_Format; + +typedef struct { + char op1; + char m1 : 4; + char op2 : 4; + short i2; +} __attribute__ ((__packed__)) RI_Format; + +typedef struct { + char op1; + char r1 : 4; + char r3 : 4; + short i2; + char xx; + char op2; +} __attribute__ ((__packed__)) RIE_Format_1; + +typedef struct { + char op1; + char r1 : 4; + char r3 : 4; + short i2; + char m2 : 4; + char xx : 4; + char op2; +} __attribute__ ((__packed__)) RIE_Format_2; + +typedef struct { + char op1; + char r1 : 4; + char r3 : 4; + short d; + char i; + char op2; +} __attribute__ ((__packed__)) RIE_Format_3; + +typedef struct { + char op1; + char r1 : 4; + char yy : 4; + short i2; + char m3 : 4; + char xx : 4; + char op2; +} __attribute__ ((__packed__)) RIE_Format_4; + +typedef struct { + char op1; + char r1 : 4; + char r3 : 4; + short ri2; + char xx; + char op2; +} __attribute__ ((__packed__)) RIE_Format_5; + +typedef struct { + char op1; + char r1 : 4; + char r2 : 4; + char i3; + char i4; + char i5; + char op2; +} __attribute__ ((__packed__)) RIE_Format_6; + +typedef struct { + char op1; + char r1 : 4; + char m3 : 4; + short i2; + char xx; + char op2; +} __attribute__ ((__packed__)) RIE_Format_7; + +typedef struct { + char op1; + char r1 : 4; + char m3 : 4; + int b4 : 4; + int d4 : 12; + char i2; + char op2; +} __attribute__ ((__packed__)) RIS_Format; + +typedef struct { + char op1; + char r1 : 4; + char op2 : 4; + int i2; +} __attribute__ ((__packed__)) RIL_Format_1; + +typedef struct { + char op1; + char m1 : 4; + char op2 : 4; + int i2; +} __attribute__ ((__packed__)) RIL_Format_2; + +typedef struct { + short op1; + char r1 : 4; + char x2 : 4; + short b2 : 4; + short d1 : 12; + char m3 : 4; + char xx : 4; + char op2; +} __attribute__ ((__packed__)) RXE_Format; + +typedef struct { + char op; + char i2; + short b1 : 4; + short d1 : 12; +} __attribute__ ((__packed__)) SI_Format; + +typedef struct { + short op; + char b1 : 4; + short d1 : 12; + short i2; +} __attribute__ ((__packed__)) SIL_Format; + +typedef struct { + char op1; + char i2; + char b1 : 4; + int d1 : 20; + char op2; +} __attribute__ ((__packed__)) SIY_Format; + +typedef struct { + char op1; + char m1 : 4; + char xx : 4; + short b3 : 4; + short d3 : 12; + short ri2; +} __attribute__ ((__packed__)) SMI_Format; + +typedef struct { + short op; + short b2 : 4; + short d2 : 12; +} __attribute__ ((__packed__)) S_Format; + +typedef struct { + char op; + char ll; + short b1 : 4; + short d1 : 12; + short b2 : 4; + short d2 : 12; +} __attribute__ ((__packed__)) SS_Format_1; + +typedef struct { + char op; + char l1 : 4; + char l2 : 4; + short b1 : 4; + short d1 : 12; + short b2 : 4; + short d2 : 12; +} __attribute__ ((__packed__)) SS_Format_2; + +typedef struct { + char op; + char r1 : 4; + char r3 : 4; + short b1 : 4; + short d1 : 12; + short b2 : 4; + short d2 : 12; +} __attribute__ ((__packed__)) SS_Format_3; + +typedef struct { + char op; + char r1 : 4; + char r3 : 4; + short b2 : 4; + short d2 : 12; + short b4 : 4; + short d4 : 12; +} __attribute__ ((__packed__)) SS_Format_4; + +typedef struct { + short op; + short tb1 : 4; + short d1 : 12; + short b2 : 4; + short d2 : 12; +} __attribute__ ((__packed__)) SSE_Format; + +typedef struct { + short op; + char r3 : 4; + char o2 : 4; + short b1 : 4; + short d1 : 12; + short b2 : 4; + short d2 : 12; +} __attribute__ ((__packed__)) SSF_Format; + +typedef struct { + short op1; + char v1 : 4; + char xx : 4; + short i2; + char m3 : 4; + char rxb : 4; + char op2; +} __attribute__ ((__packed__)) VRIa_Format; + +typedef struct { + short op1; + char v1 : 4; + char xx : 4; + char i2; + char i3; + char m4 : 4; + char rxb : 4; + char op2; +} __attribute__ ((__packed__)) VRIb_Format; + +typedef struct { + short op1; + char v1 : 4; + char v3 : 4; + short i2; + char m4 : 4; + char rxb : 4; + char op2; +} __attribute__ ((__packed__)) VRIc_Format; + +typedef struct { + short op1; + char v1 : 4; + char v2 : 4; + char v3 : 4; + char xx : 4; + char i4; + char m5 : 4; + char rxb : 4; + char op2; +} __attribute__ ((__packed__)) VRId_Format; + +typedef struct { + short op1; + char v1 : 4; + char v2 : 4; + short i3 : 12; + char m5 : 4; + char m4 : 4; + char rxb : 4; + char op2; +} __attribute__ ((__packed__)) VRIe_Format; + +typedef struct { + short op1; + char v1 : 4; + char v2 : 4; + char xx; + char m5 : 4; + char m4 : 4; + char m3 : 4; + char rxb : 4; + char op2; +} __attribute__ ((__packed__)) VRRa_Format; + +typedef struct { + short op1; + char v1 : 4; + char v2 : 4; + char v3 : 4; + char xx : 4; + char m5 : 4; + char yy : 4; + char m4 : 4; + char rxb : 4; + char op2; +} __attribute__ ((__packed__)) VRRb_Format; + +typedef struct { + short op1; + char v1 : 4; + char v2 : 4; + char v3 : 4; + char xx : 4; + char m5 : 4; + char m4 : 4; + char m3 : 4; + char rxb : 4; + char op2; +} __attribute__ ((__packed__)) VRRc_Format; + +typedef struct { + short op1; + char v1 : 4; + char v2 : 4; + char v3 : 4; + char m5 : 4; + char m6 : 4; + char xx : 4; + char v4 : 4; + char rxb : 4; + char op2; +} __attribute__ ((__packed__)) VRRd_Format; + +typedef struct { + short op1; + char v1 : 4; + char v2 : 4; + char v3 : 4; + char m6 : 4; + char xx : 4; + char m5 : 4; + char v4 : 4; + char rxb : 4; + char op2; +} __attribute__ ((__packed__)) VRRe_Format; + +typedef struct { + short op1; + char v1 : 4; + char r2 : 4; + char r3 : 4; + short xx; + char rxb : 4; + char op2; +} __attribute__ ((__packed__)) VRRf_Format; + +typedef struct { + short op1; + char v1 : 4; + char v3 : 4; + char b2 : 4; + short d2 : 12; + char m4 : 4; + char rxb : 4; + char op2; +} __attribute__ ((__packed__)) VRSa_Format; + +typedef struct { + short op1; + char v1 : 4; + char r3 : 4; + char b2 : 4; + short d2 : 12; + char m4 : 4; + char rxb : 4; + char op2; +} __attribute__ ((__packed__)) VRSb_Format; + +typedef struct { + short op1; + char r1 : 4; + char v3 : 4; + char b2 : 4; + short d2 : 12; + char m4 : 4; + char rxb : 4; + char op2; +} __attribute__ ((__packed__)) VRSc_Format; + +typedef struct { + short op1; + char v1 : 4; + char v2 : 4; + char b2 : 4; + short d2 : 12; + char m3 : 4; + char rxb : 4; + char op2; +} __attribute__ ((__packed__)) VRV_Format; + +typedef struct { + short op1; + char v1 : 4; + char x2 : 4; + char b2 : 4; + short d2 : 12; + char m3 : 4; + char rxb : 4; + char op2; +} __attribute__ ((__packed__)) VRX_Format; + +#define s390_emit16(c, x) do \ +{ \ + *((guint16 *) c) = (guint16) x; \ + c += sizeof(guint16); \ +} while(0) + +#define s390_emit32(c, x) do \ +{ \ + *((guint32 *) c) = (guint32) x; \ + c += sizeof(guint32); \ +} while(0) + +#define S390_E(c,opc) s390_emit16(c,opc) + +#define S390_I(c,opc,imm) s390_emit16(c, (opc << 8 | imm)) + +#define S390_RR(c,opc,g1,g2) s390_emit16(c, (opc << 8 | (g1) << 4 | g2)) + +#define S390_RRE(c,opc,g1,g2) s390_emit32(c, (opc << 16 | (g1) << 4 | g2)) + +#define S390_RRF_1(c,opc,g1,g2,g3) s390_emit32(c, (opc << 16 | (g3) << 12 | (g1) << 4 | g2)) + +#define S390_RRF_2(c,opc,g1,k3,g2) s390_emit32(c, (opc << 16 | (k3) << 12 | (g1) << 4 | g2)) + +#define S390_RRF_3(c,opc,g1,g2,k4,g3) s390_emit32(c, (opc << 16 | (g3) << 12 | (k4) << 8 | (g1) << 4 | g2)) + +#define S390_RRF_4(c,opc,g1,m3,g2,m4) s390_emit32(c, (opc << 16 | (m3) << 12 | (m4) << 8 | (g1) << 4 | g2)) + +#define S390_RX(c,opc,g1,n2,s2,p2) s390_emit32(c, (opc << 24 | (g1) << 20 | (n2) << 16 | (s2) << 12 | ((p2) & 0xfff))) + +#define S390_RXE(c,opc,g1,n2,s2,p2) do \ +{ \ + s390_emit16(c, ((opc & 0xff00) | (g1) << 4 | n2)); \ + s390_emit32(c, ((s2) << 28 | (((p2) & 0xfff) << 16) | \ + (opc & 0xff))); \ +} while (0) + +#define S390_RXY(c,opc,g1,n2,s2,p2) do \ +{ \ + s390_emit16(c, ((opc & 0xff00) | (g1) << 4 | n2)); \ + s390_emit32(c, ((s2) << 28 | (((p2) & 0xfff) << 16) | \ + ((((p2) & 0xff000) >> 12) << 8) | \ + (opc & 0xff))); \ +} while (0) + +#define S390_RS_1(c,opc,g1,g3,s2,p2) s390_emit32(c, (opc << 24 | (g1) << 20 | (g3) << 16 | (s2) << 12 | ((p2) & 0xfff))) + +#define S390_RS_2(c,opc,g1,k3,s2,p2) s390_emit32(c, (opc << 24 | (g1) << 20 | (k3) << 16 | (s2) << 12 | ((p2) & 0xfff))) + +#define S390_RS_3(c,opc,g1,s2,p2) s390_emit32(c, (opc << 24 | (g1) << 20 | (s2) << 12 | ((p2) & 0xfff))) + +#define S390_RSY_1(c,opc,g1,g3,s2,p2) do \ +{ \ + s390_emit16(c, ((opc & 0xff00) | (g1) << 4 | g3)); \ + s390_emit32(c, ((s2) << 28 | (((p2) & 0xfff) << 16) | \ + ((((p2) & 0xff000) >> 12) << 8) | \ + (opc & 0xff))); \ +} while (0) + +#define S390_RSY_2(c,opc,g1,k3,s2,p2) do \ +{ \ + s390_emit16(c, ((opc & 0xff00) | (g1) << 4 | k3)); \ + s390_emit32(c, ((s2) << 28 | (((p2) & 0xfff) << 16) | \ + ((((p2) & 0xff000) >> 12) << 8) | \ + (opc & 0xff))); \ +} while (0) + +#define S390_RSL(c,opc,ln,s1,p1) do \ +{ \ + s390_emit16(c, ((opc & 0xff00) | (ln) << 4)); \ + s390_emit32(c, ((s1) << 28 | ((s1 & 0xfff) << 16) | \ + (opc & 0xff))); \ +} while (0) + +#define S390_RSI(c,opc,g1,g3,m2) s390_emit32(c, (opc << 24 | (g1) << 20 | (g3) << 16 | (m2 & 0xffff))) + +#define S390_RI(c,opc,g1,m2) s390_emit32(c, ((opc >> 4) << 24 | (g1) << 20 | (opc & 0x0f) << 16 | (m2 & 0xffff))) + +#define S390_RIE_1(c,opc,g1,g3,m2) do \ +{ \ + s390_emit16(c, ((opc & 0xff00) | (g1) << 4 | g3)); \ + s390_emit32(c, ((m2) << 16 | (opc & 0xff))); \ +} while (0) + +#define S390_RIE_2(c,opc,g1,g2,m3,v) do \ +{ \ + s390_emit16(c, ((opc & 0xff00) | (g1) << 4 | g2)); \ + s390_emit16(c, (v)); \ + s390_emit16(c, ((m3) << 12 | (opc & 0xff))); \ +} while (0) + +#define S390_RIE_3(c,opc,g1,i,m3,d) do \ +{ \ + s390_emit16(c, ((opc & 0xff00) | (g1) << 4 | m3)); \ + s390_emit16(c, (d)); \ + s390_emit16(c, ((i) << 8 | (opc & 0xff))); \ +} while (0) + +#define S390_RIE_4(c,opc,g1,i2,m3) do \ +{ \ + s390_emit16(c, ((opc & 0xff00) | (g1) << 4); \ + s390_emit16(c, (i2)); \ + s390_emit16(c, ((m3) << 12 | (opc & 0xff))); \ +} while (0) + +#define S390_RIL_1(c,opc,g1,m2) do \ +{ \ + s390_emit16(c, ((opc >> 4) << 8 | (g1) << 4 | (opc & 0xf))); \ + s390_emit32(c, m2); \ +} while (0) + +#define S390_RIL_2(c,opc,k1,m2) do \ +{ \ + s390_emit16(c, ((opc >> 4) << 8 | (k1) << 4 | (opc & 0xf))); \ + s390_emit32(c, m2); \ +} while (0) + +#define S390_RIS(c,opc,r,i,m3,b,d) do \ +{ \ + s390_emit16(c, ((opc, & 0xff00) | (r1) << 4) | (r2)); \ + s390_emit16(c, ((b) << 12) | (d)); \ + s390_emit16(c, ((i) << 4) | ((opc) & 0xff)); \ +} + +#define S390_RRS(c,opc,r1,r2,m3,b,d) do \ +{ \ + s390_emit16(c, ((opc, & 0xff00) | (r1) << 4) | (r2)); \ + s390_emit16(c, ((b) << 12) | (d)); \ + s390_emit16(c, ((m3) << 12) | ((opc) & 0xff)); \ +} + +#define S390_SI(c,opc,s1,p1,m2) s390_emit32(c, (opc << 24 | (m2) << 16 | (s1) << 12 | ((p1) & 0xfff))); + +#define S390_SIY(c,opc,s1,p1,m2) do \ +{ \ + s390_emit16(c, ((opc & 0xff00) | m2)); \ + s390_emit32(c, ((s1) << 24 | (((p2) & 0xfffff) << 8) | \ + (opc & 0xff))); \ +} while (0) + +#define S390_SIY_1(c,opc,d1,b1,i2) do \ +{ \ + s390_emit16(c, ((opc & 0xff00) | i2)); \ + s390_emit32(c, ((b1) << 28 | (((d1) & 0xfff) << 16) | \ + ((((d1) & 0xff000) >> 12) << 8) | \ + (opc & 0xff))); \ +} while (0) + +#define S390_S(c,opc,s2,p2) s390_emit32(c, (opc << 16 | (s2) << 12 | ((p2) & 0xfff))) + +#define S390_SS_1(c,opc,ln,s1,p1,s2,p2) do \ +{ \ + s390_emit32(c, (opc << 24 | ((ln-1) & 0xff) << 16 | \ + (s1) << 12 | ((p1) & 0xfff))); \ + s390_emit16(c, ((s2) << 12 | ((p2) & 0xfff))); \ +} while (0) + +#define S390_SS_2(c,opc,n1,n2,s1,p1,s2,p2) do \ +{ \ + s390_emit32(c, (opc << 24 | (n1) << 16 | (n2) << 12 | \ + (s1) << 12 | ((p1) & 0xfff))); \ + s390_emit16(c, ((s2) << 12 | ((p2) & 0xfff))); \ +} while (0) + +#define S390_SS_3(c,opc,g1,g3,s1,p1,s2,p2) do \ +{ \ + s390_emit32(c, (opc << 24 | (g1) << 16 | (g3) << 12 | \ + (s1) << 12 | ((p1) & 0xfff))); \ + s390_emit16(c, ((s2) << 12 | ((p2) & 0xfff))); \ +} while (0) + +#define S390_SS_4(c,opc,g1,g3,s2,p2,s4,p4) do \ +{ \ + s390_emit32(c, (opc << 24 | (g1) << 16 | (g3) << 12 | \ + (s2) << 12 | ((p2) & 0xfff))); \ + s390_emit16(c, ((s4) << 12 | ((p4) & 0xfff))); \ +} while (0) + +#define S390_SSE(c,opc,s1,p1,s2,p2) do \ +{ \ + s390_emit16(c, opc); \ + s390_emit16(c, ((s1) << 12 | ((p1) & 0xfff))); \ + s390_emit16(c, ((s2) << 12 | ((p2) & 0xfff))); \ +} while (0) + +#define S390_SSF(c,opc,r3,s1,p1,s2,p2) do \ +{ \ + s390_emit16(c, (((opc) & 0xff00) << 8) | ((r3) << 4) | \ + ((opc) & 0xf)); \ + s390_emit16(c, ((s1) << 12 | ((p1) & 0xfff))); \ + s390_emit16(c, ((s2) << 12 | ((p2) & 0xfff))); \ +} while (0) + +#define S390_VRIa(c,opc,v1,i2,m3) do \ +{ \ + char rxb = (((v1) > 15) << 7); \ + s390_emit16(c, (((opc) & 0xff00) << 8) | ((v1) << 4)); \ + s390_emit16(c, (i2)); \ + s390_emit16(c, (((m3) << 12) | ((rxb) << 8) | ((opc) & 0xff))); \ +} while (0) + +#define S390_VRIb(c,opc,v1,i2,i3,m4) do \ +{ \ + char rxb = (((v1) > 15) << 7); \ + s390_emit16(c, (((opc) & 0xff00) << 8) | ((v1) << 4)); \ + s390_emit16(c, (((i2) << 8) | (i3))); \ + s390_emit16(c, (((m4) << 12) | ((rxb) << 8) | ((opc) & 0xff))); \ +} while (0) + +#define S390_VRIc(c,opc,v1,v3,i2,m4) do \ +{ \ + char rxb = (((v1) > 15) << 7) | (((v2) > 15) << 6) | \ + (((v3) > 15) << 5); \ + s390_emit16(c, (((opc) & 0xff00) << 8) | ((v1) << 4) | ((v2))); \ + s390_emit16(c, (v4)); \ + s390_emit16(c, (((m4) << 12) | ((rxb) << 8) | ((opc) & 0xff))); \ +} while (0) + +#define S390_VRId(c,opc,v1,v2,v3,i4,m5) do \ +{ \ + char rxb = (((v1) > 15) << 7) | (((v2) > 15) << 6) | \ + (((v3) > 15) << 5); \ + s390_emit16(c, (((opc) & 0xff00) << 8) | ((v1) << 4) | ((v2))); \ + s390_emit16(c, ((v3) << 12) | (i2)); \ + s390_emit16(c, (((m5) << 12) | ((rxb) << 8) | ((opc) & 0xff))); \ +} while (0) + +#define S390_VRIe(c,opc,v1,v2,i3,m4,m5) do \ +{ \ + char rxb = (((v1) > 15) << 7) | (((v2) > 15) << 6); \ + s390_emit16(c, (((opc) & 0xff00) << 8) | ((v1) << 4) | ((v2))); \ + s390_emit16(c, ((i2) << 8) | (m5)); \ + s390_emit16(c, (((m4) << 12) | ((rxb) << 8) | ((opc) & 0xff))); \ +} while (0) + +#define S390_VRRa(c,opc,v1,v2,m3,m4,m5) do \ +{ \ + char rxb = (((v1) > 15) << 7) | (((v2) > 15) << 6); \ + s390_emit16(c, (((opc) & 0xff00) << 8) | ((v1) << 4) | ((v2))); \ + s390_emit16(c, ((m5) << 4) | (m4)); \ + s390_emit16(c, (((m3) << 12) | ((rxb) << 8) | ((opc) & 0xff))); \ +} while (0) + +#define S390_VRRb(c,opc,v1,v2,v3,m4,m5) do \ +{ \ + char rxb = (((v1) > 15) << 7) | (((v2) > 15) << 6) | \ + (((v3) > 15) << 5); \ + s390_emit16(c, (((opc) & 0xff00) << 8) | ((v1) << 4) | ((v2))); \ + s390_emit16(c, ((v3) << 12) | ((m5) << 4) | (m4)); \ + s390_emit16(c, (((m3) << 12) | ((rxb) << 8) | ((opc) & 0xff))); \ +} while (0) + +#define S390_VRRc(c,opc,v1,v2,m3,m4,m5) do \ +{ \ + char rxb = (((v1) > 15) << 7) | (((v2) > 15) << 6) | \ + (((v3) > 15) << 5); \ + s390_emit16(c, (((opc) & 0xff00) << 8) | ((v1) << 4) | ((v2))); \ + s390_emit16(c, (((v3) << 12)| (m5) << 4)); \ + s390_emit16(c, (((m4) << 12) | ((rxb) << 8) | ((opc) & 0xff))); \ +} while (0) + +#define S390_VRRd(c,opc,v1,v2,v3,v4,m5,m6) do \ +{ \ + char rxb = (((v1) > 15) << 7) | (((v2) > 15) << 6) | \ + (((v3) > 15) << 5) | (((v4) > 15) << 4); \ + s390_emit16(c, (((opc) & 0xff00) << 8) | ((v1) << 4) | ((v2))); \ + s390_emit16(c, (((v3) << 12)| ((m6) << 8)) | ((m5) << 4)); \ + s390_emit16(c, (((v4) << 12) | ((rxb) << 8) | ((opc) & 0xff))); \ +} while (0) + +#define S390_VRRe(c,opc,v1,v2,v3,m4,m5,m6) do \ +{ \ + char rxb = (((v1) > 15) << 7) | (((v2) > 15) << 6) | \ + (((v3) > 15) << 5); \ + s390_emit16(c, (((opc) & 0xff00) << 8) | ((v1) << 4) | ((v2))); \ + s390_emit16(c, (((v3) << 12)| ((m6) << 8)) | (m5)); \ + s390_emit16(c, (((m4) << 12) | ((rxb) << 8) | ((opc) & 0xff))); \ +} while (0) + +#define S390_VRRf(c,opc,v1,r2) do \ +{ \ + char rxb = (((v1) > 15) << 7) | (((v2) > 15) << 6); \ + s390_emit16(c, (((opc) & 0xff00) << 8) | ((v1) << 4) | ((v2))); \ + s390_emit16(c, ((r2) << 12)| ((r3) << r8) | (m5)); \ + s390_emit16(c, (((rxb) << 8) | ((opc) & 0xff))); \ +} while (0) + +#define S390_VRSa(c,opc,v1,v3,b2,d2,m4) do \ +{ \ + char rxb = (((v1) > 15) << 7) | (((v2) > 15) << 6); \ + s390_emit16(c, (((opc) & 0xff00) << 8) | ((v1) << 4) | ((v3))); \ + s390_emit16(c, ((b2) << 12)| (d2)); \ + s390_emit16(c, (((m4) << 12) | ((rxb) << 8) | ((opc) & 0xff))); \ +} while (0) + +#define S390_VRSb(c,opc,v1,r3,b2,d2,m4) do \ +{ \ + char rxb = (((v1) > 15) << 7); \ + s390_emit16(c, (((opc) & 0xff00) << 8) | ((v1) << 4) | ((r3))); \ + s390_emit16(c, ((b2) << 12)| (d2)); \ + s390_emit16(c, (((m4) << 12) | ((rxb) << 8) | ((opc) & 0xff))); \ +} while (0) + +#define S390_VRSc(c,opc,r1,v3,b2,d2,m4) do \ +{ \ + char rxb = (((v1) > 15) << 7); \ + s390_emit16(c, (((opc) & 0xff00) << 8) | ((r1) << 4) | ((v3))); \ + s390_emit16(c, ((b2) << 12)| (d2)); \ + s390_emit16(c, (((m4) << 12) | ((rxb) << 8) | ((opc) & 0xff))); \ +} while (0) + +#define S390_VRV(c,opc,v1,v2,b2,d2,m3) do \ +{ \ + char rxb = (((v1) > 15) << 7) | (((v2) > 15) << 6); \ + s390_emit16(c, (((opc) & 0xff00) << 8) | ((v1) << 4) | ((v2))); \ + s390_emit16(c, ((b2) << 12)| (d2)); \ + s390_emit16(c, (((m3) << 12) | ((rxb) << 8) | ((opc) & 0xff))); \ +} while (0) + +#define S390_VRX(c,opc,v1,x2,b2,d2,m3) do \ +{ \ + char rxb = ((v1) > 15) << 7; \ + s390_emit16(c, (((opc) & 0xff00) << 8) | ((v1) << 4) | ((x2))); \ + s390_emit16(c, ((b2) << 12)| (d2)); \ + s390_emit16(c, (((m3) << 12) | ((rxb) << 8) | ((opc) & 0xff))); \ +} while (0) + +#define s390_a(c, r, x, b, d) S390_RX(c, 0x5a, r, x, b, d) +#define s390_adb(c, r, x, b, d) S390_RXE(c, 0xed1a, r, x, b, d) +#define s390_adbr(c, r1, r2) S390_RRE(c, 0xb31a, r1, r2) +#define s390_aebr(c, r1, r2) S390_RRE(c, 0xb30a, r1, r2) +#define s390_afi(c, r, v) S390_RIL_1(c, 0xc29, r, v); +#define s390_ag(c, r, x, b, d) S390_RXY(c, 0xe308, r, x, b, d) +#define s390_agf(c, r, x, b, d) S390_RXY(c, 0xe318, r, x, b, d) +#define s390_agfi(c, r, v) S390_RIL_1(c, 0xc28, r, v) +#define s390_afgr(c, r1, r2) S390_RRE(c, 0xb918, r1, r2) +#define s390_agh(c, r, x, b, d) S390_RXY(c, 0xe338, r, b, d) +#define s390_aghi(c, r, v) S390_RI(c, 0xa7b, r, v) +#define s390_aghik(c, r1, r3, v) S390_RIE_1(c, 0xecd9, r1, r3, v) +#define s390_agr(c, r1, r2) S390_RRE(c, 0xb908, r1, r2) +#define s390_agrk(c, r1, r2, r3) S390_RRF_1(c, 0xb9e8, r1, r2, r3) +#define s390_agsi(c, r, v) S390_SIY(c, 0xeb7a, r v) +#define s390_ahhhr(c, r1, r2, r3) S390_RRF_1(c, 0xb9c8, r1, r2, r3) +#define s390_ahhlr(c, r1, r2, r3) S390_RRF_1(c, 0xb9d8, r1, r2, r3) +#define s390_ahi(c, r, v) S390_RI(c, 0xa7a, r, v) +#define s390_ahik(c, r1, r3, v) S390_RIE_1(c, 0xecd8, r1, r3, v) +#define s390_ahy(c, r, x, b, d) S390_RXY(c, 0xe37a, r, b, d) +#define s390_aih(c, r, v) S390_RIL_1(c, 0xcc8, r, v) +#define s390_al(c, r, x, b, d) S390_RX(c, 0x5e, r, x, b, d) +#define s390_alc(c, r, x, b, d) S390_RXY(c, 0xe398, r, x, b, d) +#define s390_alcg(c, r, x, b, d) S390_RXY(c, 0xe388, r, x, b, d) +#define s390_alcgr(c, r1, r2) S390_RRE(c, 0xb988, r1, r2) +#define s390_alcr(c, r1, r2) S390_RRE(c, 0xb998, r1, r2) +#define s390_alfi(c, r, v) S390_RIL_1(c, 0xc2b, r, v) +#define s390_alg(c, r, x, b, d) S390_RXY(c, 0xe30a, r, x, b, d) +#define s390_algf(c, r, x, b, d) S390_RXY(c, 0xe31a, r, x, b, d) +#define s390_algfi(c, r, v) S390_RIL_1(c, 0xc2a, r, v) +#define s390_algfr(c, r1, r2) S390_RRE(c, 0xb91a, r1, r2) +#define s390_alghsik(c, r1, r3, v) S390_RIE_1(c, 0xecdb, r1, r3, v) +#define s390_algr(c, r1, r2) S390_RRE(c, 0xb90a, r1, r2) +#define s390_algrk(c, r1, r2, r3) S390_RRF_1(c, 0xb9ea, r1, r2, r3) +#define s390_algsi(c, d1, b1, i2) S390_SIY_1(c, 0xeb7e, d1, b1, i2) +#define s390_alhhhr(c, r1, r2, r3) S390_RRF_1(c, 0xb9ca, r1, r2, r3) +#define s390_alhhlr(c, r1, r2, r3) S390_RRF_1(c, 0xb9da, r1, r2, r3) +#define s390_alhsik(c, r1, r3, v) S390_RIE_1(c, 0xecda, r1, r3, v) +#define s390_alr(c, r1, r2) S390_RR(c, 0x1e, r1, r2) +#define s390_alrk(c, r1, r2) S390_RRF(c, 0xb9fa, r1, r2) +#define s390_alsi(c, d1, b1, i2) S390_SIY_1(c, 0xeb6e, d1, b1, i2) +#define s390_alsih(c, r, v) S390_RIL_1(c, 0xcca, r, v) +#define s390_alsihn(c, r, v) S390_RIL_1(c, 0xccb, r, v) +#define s390_aly(c, r, x, b, d) S390_RXY(c, 0xe35e, r, x, b, d) +#define s390_ar(c, r1, r2) S390_RR(c, 0x1a, r1, r2) +#define s390_ark(c, r1, r2, r3) S390_RRF_1(c, 0xb9f8, r1, r2, r3) +#define s390_asi(c, r, v) S390_SIY(c, 0xeb6a, r, v) +#define s390_axbr(c, r1, r2) S390_RRE(c, 0xb34a, r1, r2) +#define s390_ay(c, r, x, b, d) S390_RXY(c, 0xe35a, r, x, b, d) +#define s390_basr(c, r1, r2) S390_RR(c, 0x0d, r1, r2) +#define s390_bctr(c, r1, r2) S390_RR(c, 0x06, r1, r2) +#define s390_bctrg(c, r1, r2) S390_RRE(c, 0xb946, r1, r2) +#define s390_bi(c, x, b, d) S390_RXY(c, 0xe347, S390_CC_UN, x, b, d) +#define s390_bic(c, m, x, b, d) S390_RXY(c, 0xe347, m, x, b, d) +#define s390_bnzr(c, r) S390_RR(c, 0x07, 0x07, r) +#define s390_bras(c, r, o) S390_RI(c, 0xa75, r, o) +#define s390_brasl(c, r, o) S390_RIL_1(c, 0xc05, r, o) +#define s390_brc(c, m, d) S390_RI(c, 0xa74, m, d) +#define s390_brcl(c, m, d) S390_RIL_2(c, 0xc04, m, d) +#define s390_br(c, r) S390_RR(c, 0x07, 0xf, r) +#define s390_break(c) S390_RR(c, 0, 0, 0) +#define s390_bzr(c, r) S390_RR(c, 0x07, 0x08, r) +#define s390_c(c, r, x, b, d) S390_RX(c, 0x59, r, x, b, d) +#define s390_cdb(c, r, x, b, d) S390_RXE(c, 0xed19, r, x, b, d) +#define s390_cdbr(c, r1, r2) S390_RRE(c, 0xb319, r1, r2) +#define s390_cdfbr(c, r1, r2) S390_RRE(c, 0xb395, r1, r2) +#define s390_cdgbr(c, r1, r2) S390_RRE(c, 0xb3a5, r1, r2) +#define s390_cdlfbr(c, r1, m3, r2, m4) S390_RRF_4(c, 0xb391, r1, m3, r2, m4) +#define s390_cdlgbr(c, r1, m3, r2, m4) S390_RRF_4(c, 0xb3a1, r1, m3, r2, m4) +#define s390_cds(c, r1, r2, b, d) S390_RX(c, 0xbb, r1, r2, b, d) +#define s390_cdsg(c, r1, r2, b, d) S390_RSY_1(c, 0xeb3e, r1, r2, b, d) +#define s390_cdsy(c, r1, r2, b, d) S390_RSY_1(c, 0xeb31, r1, r2, b, d) +#define s390_cebr(c, r1, r2) S390_RRE(c, 0xb309, r1, r2) +#define s390_cegbr(c, r1, r2) S390_RRE(c, 0xb3a4, r1, r2) +#define s390_cfdbr(c, r1, m, r2) S390_RRF_2(c, 0xb399, r1, m, r2) +#define s390_cfi(c, r, v) S390_RIL_1(c, 0xc2d, r, v) +#define s390_cgdbr(c, r1, m, r2) S390_RRF_2(c, 0xb3a9, r1, m, r2) +#define s390_cg(c, r, x, b, d) S390_RXY(c, 0xe320, r, x, b, d) +#define s390_cgfi(c, r, v) S390_RIL_1(c, 0xc2c, r, v) +#define s390_cgfrl(c, r, v) S390_RIL_1(c, 0xc6c, r, v) +#define s390_cghi(c, r, i) S390_RI(c, 0xa7f, r, i) +#define s390_cgib(c, r, i, m, b, d) S390_RIS(c, 0xecfc, r, i, m, b, d) +#define s390_cgij(c, r, i, m, d) S390_RIE_3(c, 0xec7c, r, i, m, d) +#define s390_cgit(c, r, i, m) S390_RIE_4(c, 0xec70, r, i m); +#define s390_cgr(c, r1, r2) S390_RRE(c, 0xb920, r1, r2) +#define s390_cgrb(c, r1, r2, m3, b, d) S390_RRS(c, 0xece4, r1, r2, m3, b, d) +#define s390_cgrj(c, r1, r2, m3, v) S390_RIE_2(c, 0xec64, r1, r2, m3, v) +#define s390_cgrl(c, r, v) S390_RIL_1(c, 0xc68, r, v) +#define s390_chi(c, r, i) S390_RI(c, 0xa7e, r, i) +#define s390_cib(c, r, i, m, b, d) S390_RIS(c, 0xecfe, r, i, m, b, d) +#define s390_cij(c, r, i, m, d) S390_RIE_3(c, 0xec7e, r, i, m, d) +#define s390_cit(c, r, i, m) S390_RIE_4(c, 0xec72, r, i m); +#define s390_cl(c, r, x, b, d) S390_RX(c, 0x55, r, x, b, d) +#define s390_clfdbr(c, r1, m3, r2, m4) S390_RRF_4(c, 0xb39d, r1, m3, r2, m4) +#define s390_clg(c, r, x, b, d) S390_RXY(c, 0xe321, r, x, b, d) +#define s390_clgib(c, r, i, m, b, d) S390_RIS(c, 0xecfd, r, i, m, b, d) +#define s390_clgij(c, r, i, b) S390_RIE_3(c, 0xec7d, r, i, m, d) +#define s390_clgr(c, r1, r2) S390_RRE(c, 0xb921, r1, r2) +#define s390_clgdbr(c, r1, m3, r2, m4) S390_RRF_4(c, 0xb3ad, r1, m3, r2, m4) +#define s390_clgrj(c, r1, r2, m, v) S390_RIE_2(c, 0xec65, r1, r2, m, v) +#define s390_clgrb(c, r1, r2, m3, b, d) S390_RRS(c, 0xece5, r1, r2, m3, b, d) +#define s390_cli(c, b, d, v) S390_SI(c, 0x95, b, d, v) +#define s390_clib(c, r, i, m, b, d) S390_RIS(c, 0xecff, r, i, m, b, d) +#define s390_clij(c, r, i, b) S390_RIE_3(c, 0xec7f, r, i, m, d) +#define s390_clr(c, r1, r2) S390_RR(c, 0x15, r1, r2) +#define s390_clrb(c, r1, r2, m3, b, d) S390_RRS(c, 0xecf7, r1, r2, m3, b, d) +#define s390_clrj(c, r1, r2, m, v) S390_RIE_2(c, 0xec77, r1, r2, m, v) +#define s390_cr(c, r1, r2) S390_RR(c, 0x19, r1, r2) +#define s390_crb(c, r1, r2, m3, b, d) S390_RRS(c, 0xecf6, r1, r2, m3, b, d) +#define s390_crj(c, r1, r2, m3, v) S390_RIE_2(c, 0xec76, r1, r2, m3, v) +#define s390_crl(c, r, v) S390_RIL_1(c, 0xc6d, r, v) +#define s390_crt(c, r1, r2, m3) S390_RRF_2(c, 0xb972, r1, r2, m3); +#define s390_cgrt(c, r1, r2, m3) S390_RRF_2(c, 0xb960, r1, r2, m3); +#define s390_cs(c, r1, r2, b, d) S390_RX(c, 0xba, r1, r2, b, d) +#define s390_csg(c, r1, r2, b, d) S390_RSY_1(c, 0xeb30, r1, r2, b, d) +#define s390_csst(c, d1, b1, d2, b2, r) S390_SSF(c, 0xc82, b1, d1, b2, d2, r) +#define s390_csy(c, r1, r2, b, d) S390_RSY_1(c, 0xeb14, r1, r2, b, d) +#define s390_cxgbr(c, r1, r2) S390_RRE(c, 0xb3a6, r1, r2) +#define s390_ddbr(c, r1, r2) S390_RRE(c, 0xb31d, r1, r2) +#define s390_debr(c, r1, r2) S390_RRE(c, 0xb30d, r1, r2) +#define s390_didbr(c, r1, r2, m, r3) S390_RRF_3(c, 0xb35b, r1, r2, m, r3) +#define s390_dlgr(c, r1, r2) S390_RRE(c, 0xb987, r1, r2) +#define s390_dlr(c, r1, r2) S390_RRE(c, 0xb997, r1, r2) +#define s390_dr(c, r1, r2) S390_RR(c, 0x1d, r1, r2) +#define s390_dsgfr(c, r1, r2) S390_RRE(c, 0xb91d, r1, r2) +#define s390_dsgr(c, r1, r2) S390_RRE(c, 0xb90d, r1, r2) +#define s390_ear(c, r1, r2) S390_RRE(c, 0xb24f, r1, r2) +#define s390_ic(c, r, x, b, d) S390_RX(c, 0x43, r, x, b, d) +#define s390_icm(c, r, m, b, d) S390_RX(c, 0xbf, r, m, b, d) +#define s390_icmy(c, r, x, b, d) S390_RXY(c, 0xeb81, r, x, b, d) +#define s390_icy(c, r, x, b, d) S390_RXY(c, 0xe373, r, x, b, d) +#define s390_iihf(c, r, v) S390_RIL_1(c, 0xc08, r, v) +#define s390_iihh(c, r, v) S390_RI(c, 0xa50, r, v) +#define s390_iihl(c, r, v) S390_RI(c, 0xa51, r, v) +#define s390_iilf(c, r, v) S390_RIL_1(c, 0xc09, r, v) +#define s390_iilh(c, r, v) S390_RI(c, 0xa52, r, v) +#define s390_iill(c, r, v) S390_RI(c, 0xa53, r, v) +#define s390_j(c,d) s390_brc(c, S390_CC_UN, d) +#define s390_jc(c, m, d) s390_brc(c, m, d) +#define s390_jcl(c, m, d) s390_brcl(c, m, d) +#define s390_jcy(c, d) s390_brc(c, S390_CC_CY, d) +#define s390_je(c, d) s390_brc(c, S390_CC_EQ, d) +#define s390_jeo(c, d) s390_brc(c, S390_CC_ZR|S390_CC_OV, d) +#define s390_jh(c, d) s390_brc(c, S390_CC_GT, d) +#define s390_jhe(c, d) s390_brc(c, S390_CC_GT|S390_CC_EQ, d) +#define s390_jho(c, d) s390_brc(c, S390_CC_GT|S390_CC_OV, d) +#define s390_jl(c, d) s390_brc(c, S390_CC_LT, d) +#define s390_jle(c, d) s390_brc(c, S390_CC_LT|S390_CC_EQ, d) +#define s390_jlo(c, d) s390_brc(c, S390_CC_LT|S390_CC_OV, d) +#define s390_jm(c, d) s390_brc(c, S390_CC_LT, d) +#define s390_jnc(c, d) s390_brc(c, S390_CC_NC, d) +#define s390_jne(c, d) s390_brc(c, S390_CC_NZ, d) +#define s390_jnh(c, d) s390_brc(c, S390_CC_LE, d) +#define s390_jnl(c, d) s390_brc(c, S390_CC_GE, d) +#define s390_jnz(c, d) s390_brc(c, S390_CC_NZ, d) +#define s390_jo(c, d) s390_brc(c, S390_CC_OV, d) +#define s390_jno(c, d) s390_brc(c, S390_CC_NO, d) +#define s390_jp(c, d) s390_brc(c, S390_CC_GT, d) +#define s390_jz(c, d) s390_brc(c, S390_CC_ZR, d) +#define s390_jg(c,d) s390_brcl(c, S390_CC_UN, d) +#define s390_jgcy(c, d) s390_brcl(c, S390_CC_CY, d) +#define s390_jge(c, d) s390_brcl(c, S390_CC_EQ, d) +#define s390_jgeo(c, d) s390_brcl(c, S390_CC_ZR|S390_CC_OV, d) +#define s390_jgh(c, d) s390_brcl(c, S390_CC_GT, d) +#define s390_jgho(c, d) s390_brcl(c, S390_CC_GT|S390_CC_OV, d) +#define s390_jgl(c, d) s390_brcl(c, S390_CC_LT, d) +#define s390_jglo(c, d) s390_brcl(c, S390_CC_LT|S390_CC_OV, d) +#define s390_jgm(c, d) s390_brcl(c, S390_CC_LT, d) +#define s390_jgnc(c, d) s390_brcl(c, S390_CC_NC, d) +#define s390_jgne(c, d) s390_brcl(c, S390_CC_NZ, d) +#define s390_jgnh(c, d) s390_brcl(c, S390_CC_LE, d) +#define s390_jgnl(c, d) s390_brcl(c, S390_CC_GE, d) +#define s390_jgnz(c, d) s390_brcl(c, S390_CC_NZ, d) +#define s390_jgo(c, d) s390_brcl(c, S390_CC_OV, d) +#define s390_jgno(c, d) s390_brcl(c, S390_CC_NO, d) +#define s390_jgp(c, d) s390_brcl(c, S390_CC_GT, d) +#define s390_jgz(c, d) s390_brcl(c, S390_CC_ZR, d) +#define s390_l(c, r, x, b, d) S390_RX(c, 0x58, r, x, b, d) +#define s390_ly(c, r, x, b, d) S390_RXY(c, 0xe358, r, x, b, d) +#define s390_la(c, r, x, b, d) S390_RX(c, 0x41, r, x, b, d) +#define s390_laa(c, r1, r3, b, d) S390_RSY_1(c, 0xebf8, r1, r3, b, d) +#define s390_laag(c, r1, r3, b, d) S390_RSY_1(c, 0xebe8, r1, r3, b, d) +#define s390_lay(c, r, x, b, d) S390_RXY(c, 0xe371, r, x, b, d) +#define s390_lam(c, r1, r2, b, d) S390_RS_1(c, 0x9a, r1, r2, b, d) +#define s390_larl(c, r, o) S390_RIL_1(c, 0xc00, r, o) +#define s390_lb(c, r, x, b, d) S390_RXY(c, 0xe376, r, x, b, d) +#define s390_lbr(c, r1, r2) S390_RRE(c, 0xb926, r1, r2) +#define s390_lcdbr(c, r1, r2) S390_RRE(c, 0xb313, r1, r2) +#define s390_lcgr(c, r1, r2) S390_RRE(c, 0xb903, r1, r2) +#define s390_lcr(c, r1, r2) S390_RR(c, 0x13, r1, r2) +#define s390_ld(c, f, x, b, d) S390_RX(c, 0x68, f, x, b, d) +#define s390_ldy(c, r, x, b, d) S390_RXY(c, 0xed65, r, x, b, d) +#define s390_ldeb(c, r, x, b, d) S390_RXE(c, 0xed04, r, x, b, d) +#define s390_ldebr(c, r1, r2) S390_RRE(c, 0xb304, r1, r2) +#define s390_ldgr(c, r1, r2) S390_RRE(c, 0xb3c1, r1, r2) +#define s390_ldxbr(c, r1, r2) S390_RRE(c, 0xb345, r1, r2) +#define s390_ldr(c, r1, r2) S390_RR(c, 0x28, r1, r2) +#define s390_le(c, f, x, b, d) S390_RX(c, 0x78, f, x, b, d) +#define s390_ledbr(c, r1, r2) S390_RRE(c, 0xb344, r1, r2) +#define s390_ler(c, r1, r2) S390_RR(c, 0x38, r1, r2) +#define s390_ley(c, r, x, b, d) S390_RXY(c, 0xed64, r, x, b, d) +#define s390_lg(c, r, x, b, d) S390_RXY(c, 0xe304, r, x, b, d) +#define s390_lgb(c, r, x, b, d) S390_RXY(c, 0xe377, r, x, b, d) +#define s390_lgbr(c, r1, r2) S390_RRE(c, 0xb906, r1, r2) +#define s390_lgdr(c, r1, r2) S390_RRE(c, 0xb3cd, r1, r2) +#define s390_lgf(c, r, x, b, d) S390_RXY(c, 0xe314, r, x, b, d) +#define s390_lgfi(c, r, v) S390_RIL_1(c, 0xc01, r, v) +#define s390_lgfrl(c, r1, d) S390_RIL_1(c, 0xc4c, r1, d) +#define s390_lgfr(c, r1, r2) S390_RRE(c, 0xb914, r1, r2) +#define s390_lgg(c, r, x, b, d) S390_RXY(c, 0xe34c, r, x, b, d) +#define s390_lgh(c, r, x, b, d) S390_RXY(c, 0xe315, r, x, b, d) +#define s390_lghi(c, r, v) S390_RI(c, 0xa79, r, v) +#define s390_lghr(c, r1, r2) S390_RRE(c, 0xb907, r1, r2) +#define s390_lgr(c, r1, r2) S390_RRE(c, 0xb904, r1, r2) +#define s390_lgrl(c, r1, d) S390_RIL_1(c, 0xc48, r1, d) +#define s390_lgsc(c, r, x, b, d) S390_RXY(c, 0xe34d, r, x, b, d) +#define s390_lh(c, r, x, b, d) S390_RX(c, 0x48, r, x, b, d) +#define s390_lhr(c, r1, r2) S390_RRE(c, 0xb927, r1, r2) +#define s390_lhg(c, r, x, b, d) S390_RXY(c, 0xe315, r, x, b, d) +#define s390_lhi(c, r, v) S390_RI(c, 0xa78, r, v) +#define s390_lhy(c, r, x, b, d) S390_RXY(c, 0xe378, r, x, b, d) +#define s390_llcr(c, r1, r2) S390_RRE(c, 0xb994, r1, r2) +#define s390_llgc(c, r, x, b, d) S390_RXY(c, 0xe390, r, x, b, d) +#define s390_llgcr(c, r1, r2) S390_RRE(c, 0xb984, r1, r2) +#define s390_llgf(c, r, x, b, d) S390_RXY(c, 0xe316, r, x, b, d) +#define s390_llgfr(c, r1, r2) S390_RRE(c, 0xb916, r1, r2) +#define s390_llgh(c, r, x, b, d) S390_RXY(c, 0xe391, r, x, b, d) +#define s390_llghr(c, r1, r2) S390_RRE(c, 0xb985, r1, r2) +#define s390_llgfsg(c, r, x, b, d) S390_RXY(c, 0xe348, r, x, b, d) +#define s390_llhr(c, r1, r2) S390_RRE(c, 0xb995, r1, r2) +#define s390_llihf(c, r, v) S390_RIL_1(c, 0xc0e, r, v) +#define s390_llihh(c, r, v) S390_RI(c, 0xa5c, r, v) +#define s390_llihl(c, r, v) S390_RI(c, 0xa5d, r, v) +#define s390_llilf(c, r, v) S390_RIL_1(c, 0xc0f, r, v) +#define s390_llilh(c, r, v) S390_RI(c, 0xa5e, r, v) +#define s390_llill(c, r, v) S390_RI(c, 0xa5f, r, v) +#define s390_lm(c, r1, r2, b, d) S390_RS_1(c, 0x98, r1, r2, b, d) +#define s390_lmg(c, r1, r2, b, d) S390_RSY_1(c, 0xeb04, r1, r2, b, d) +#define s390_lndbr(c, r1, r2) S390_RRE(c, 0xb311, r1, r2) +#define s390_lngr(c, r1, r2) S390_RRE(c, 0xb901, r1, r2) +#define s390_lnr(c, r1, r2) S390_RR(c, 0x11, r1, r2) +#define s390_lpdbr(c, r1, r2) S390_RRE(c, 0xb310, r1, r2) +#define s390_lpgr(c, r1, r2) S390_RRE(c, 0xb900, r1, r2) +#define s390_lpr(c, r1, r2) S390_RR(c, 0x10, r1, r2) +#define s390_lr(c, r1, r2) S390_RR(c, 0x18, r1, r2) +#define s390_lrl(c, r1, d) S390_RIL_1(c, 0xc4d, r1, d) +#define s390_lt(c, r, x, b, d) S390_RXY(c, 0xe312, r, x, b, d) +#define s390_ltg(c, r, x, b, d) S390_RXY(c, 0xe302, r, x, b, d) +#define s390_ltgfr(c, r1, r2) S390_RRE(c, 0xb912, r1, r2) +#define s390_ltgr(c, r1, r2) S390_RRE(c, 0xb902, r1, r2) +#define s390_ltr(c, r1, r2) S390_RR(c, 0x12, r1, r2) +#define s390_lzdr(c, r) S390_RRE(c, 0xb375, r, 0) +#define s390_lzer(c, r) S390_RRE(c, 0xb374, r, 0) +#define s390_m(c, r, x, b, d) S390_RX(c, 0x5c, r, x, b, d) +#define s390_mdbr(c, r1, r2) S390_RRE(c, 0xb31c, r1, r2) +#define s390_meebr(c, r1, r2) S390_RRE(c, 0xb317, r1, r2) +#define s390_mg(c, r, x, b, d) S390_RXY(c, 0xe384, r, x, b, d) +#define s390_mgh(c, r, x, b, d) S390_RXY(c, 0xe33c, r, x, b, d) +#define s390_mgrk(c, r1, r2, r3) S390_RRF_1(c, 0xb9ec, r1, r2, r3) +#define s390_mfy(c, r, x, b, d) S390_RXY(c, 0xe35c, r, x, b, d) +#define s390_mlgr(c, r1, r2) S390_RRE(c, 0xb986, r1, r2) +#define s390_mlr(c, r1, r2) S390_RRE(c, 0xb996, r1, r2) +#define s390_mr(c, r1, r2) S390_RR(c, 0x1c, r1, r2) +#define s390_ms(c, r, x, b, d) S390_RX(c, 0x71, r, x, b, d) +#define s390_msfi(c, r, v) S390_RIL_1(c, 0xc20, r, v) +#define s390_msgfi(c, r, v) S390_RIL_1(c, 0xc21, r, v) +#define s390_msgfr(c, r1, r2) S390_RRE(c, 0xb91c, r1, r2) +#define s390_msgr(c, r1, r2) S390_RRE(c, 0xb90c, r1, r2) +#define s390_msgrkc(c, r1, r2, r3) S390_RRF_1(c, 0xb9ed, r1, r2, r3) +#define s390_msr(c, r1, r2) S390_RRE(c, 0xb252, r1, r2) +#define s390_mvc(c, l, b1, d1, b2, d2) S390_SS_1(c, 0xd2, l, b1, d1, b2, d2) +#define s390_mvcl(c, r1, r2) S390_RR(c, 0x0e, r1, r2) +#define s390_mvcle(c, r1, r3, d2, b2) S390_RS_1(c, 0xa8, r1, r3, d2, b2) +#define s390_mvi(c, b, d, v) S390_SI(c, 0x92, b, d, v) +#define s390_n(c, r, x, b, d) S390_RX(c, 0x54, r, x, b, d) +#define s390_nc(c, l, b1, d1, b2, d2) S390_SS_1(c, 0xd4, l, b1, d1, b2, d2) +#define s390_ng(c, r, x, b, d) S390_RXY(c, 0xe380, r, x, b, d) +#define s390_ngr(c, r1, r2) S390_RRE(c, 0xb980, r1, r2) +#define s390_ngrk(c, r1, r2, r3) S390_RRF_1(c, 0xb9e4, r1, r2, r3) +#define s390_ni(c, b, d, v) S390_SI(c, 0x94, b, d, v) +#define s390_nihf(c, r, v) S390_RIL_1(c, 0xc0a, r, v) +#define s390_nihh(c, r, v) S390_RI(c, 0xa54, r, v) +#define s390_nihl(c, r, v) S390_RI(c, 0xa55, r, v) +#define s390_nilf(c, r, v) S390_RIL_1(c, 0xc0b, r, v) +#define s390_nilh(c, r, v) S390_RI(c, 0xa56, r, v) +#define s390_nill(c, r, v) S390_RI(c, 0xa57, r, v) +#define s390_niy(c, b, d, v) S390_SIY(c, 0xeb54, b, d, v) +#define s390_nop(c) S390_RR(c, 0x07, 0x0, 0) +#define s390_mem(c) S390_RR(c, 0x07, 0xe, 0) +#define s390_nr(c, r1, r2) S390_RR(c, 0x14, r1, r2) +#define s390_nrk(c, r1, r2) S390_RRF_1(c, 0xb9f4, r1, r2) +#define s390_ny(c, r, x, b, d) S390_RRY(c, 0xe354, r1, r2) +#define s390_o(c, r, x, b, d) S390_RX(c, 0x56, r, x, b, d) +#define s390_oihf(c, r, v) S390_RIL_1(c, 0xc0c, r, v) +#define s390_oihh(c, r, v) S390_RI(c, 0xa58, r, v) +#define s390_oihl(c, r, v) S390_RI(c, 0xa59, r, v) +#define s390_oilf(c, r, v) S390_RIL_1(c, 0xc0d, r, v) +#define s390_oilh(c, r, v) S390_RI(c, 0xa5a, r, v) +#define s390_oill(c, r, v) S390_RI(c, 0xa5b, r, v) +#define s390_oiy(c, b, d, v) S390_SIY(c, 0xeb56 b, d, v) +#define s390_og(c, r, x, b, d) S390_RXY(c, 0xe381, r, x, b, d) +#define s390_ogr(c, r1, r2) S390_RRE(c, 0xb981, r1, r2) +#define s390_ogrk(c, r1, r2, r3) S390_RRF_1(c, 0xb9e6, r1, r2, r3) +#define s390_or(c, r1, r2) S390_RR(c, 0x16, r1, r2) +#define s390_s(c, r, x, b, d) S390_RX(c, 0x5b, r, x, b, d) +#define s390_sdb(c, r, x, b, d) S390_RXE(c, 0xed1b, r, x, b, d) +#define s390_sdbr(c, r1, r2) S390_RRE(c, 0xb31b, r1, r2) +#define s390_sebr(c, r1, r2) S390_RRE(c, 0xb30b, r1, r2) +#define s390_sg(c, r, x, b, d) S390_RXY(c, 0xe309, r, x, b, d) +#define s390_sgf(c, r, x, b, d) S390_RXY(c, 0xe319, r, x, b, d) +#define s390_sgh(c, r, x, b, d) S390_RXY(c, 0xe339, r, x, b, d) +#define s390_sgr(c, r1, r2) S390_RRE(c, 0xb909, r1, r2) +#define s390_sgrk(c, r1, r2, r3) S390_RRF_1(c, 0xb9e9, r1, r2, r3) +#define s390_sl(c, r, x, b, d) S390_RX(c, 0x5f, r, x, b, d) +#define s390_sla(c, r, b, d) S390_RS_3(c, 0x8b, r, b, d) +#define s390_slag(c, r1, r2, b, d) S390_RSY_1(c, 0xeb0b, r1, r2, b, d) +#define s390_slbg(c, r, x, b, d) S390_RXY(c, 0xe389, r, x, b, d) +#define s390_slbgr(c, r1, r2) S390_RRE(c, 0xb989, r1, r2) +#define s390_slbr(c, r1, r2) S390_RRE(c, 0xb999, r1, r2) +#define s390_slda(c, r, b, d) S390_RS_3(c, 0x8f, r, b, d) +#define s390_sldl(c, r, b, d) S390_RS_3(c, 0x8d, r, b, d) +#define s390_slfi(c, r, v) S390_RIL_1(c, 0xc25, r, v) +#define s390_slg(c, r, x, b, d) S390_RXY(c, 0xe30b, r, x, b, d) +#define s390_slgf(c, r, x, b, d) S390_RXY(c, 0xe31b, r, x, b, d) +#define s390_slgfr(c, r1, r2) S390_RRE(c, 0xb91b, r1, r2) +#define s390_slgfi(c, r, v) S390_RIL_1(c, 0xc24, r, v) +#define s390_slgr(c, r1, r2) S390_RRE(c, 0xb90b, r1, r2) +#define s390_slgrk(c, r1, r2, r3) S390_RRF_1(c, 0xb9eb, r1, r2, r3) +#define s390_sll(c, r, b, d) S390_RS_3(c, 0x89, r, b, d) +#define s390_sllg(c, r1, r2, b, d) S390_RSY_1(c, 0xeb0d, r1, r2, b, d) +#define s390_slr(c, r1, r2) S390_RR(c, 0x1f, r1, r2) +#define s390_slrk(c, r1, r2, r3) S390_RRF_1(c, 0xb9fb, r1, r2, r3) +#define s390_sqdbr(c, r1, r2) S390_RRE(c, 0xb315, r1, r2) +#define s390_sqebr(c, r1, r2) S390_RRE(c, 0xb314, r1, r2) +#define s390_sra(c, r, b, d) S390_RS_3(c, 0x8a, r, b, d) +#define s390_srag(c, r1, r2, b, d) S390_RSY_1(c, 0xeb0a, r1, r2, b, d) +#define s390_sr(c, r1, r2) S390_RR(c, 0x1b, r1, r2) +#define s390_srda(c, r, b, d) S390_RS_3(c, 0x8e, r, b, d) +#define s390_srdl(c, r, b, d) S390_RS_3(c, 0x8c, r, b, d) +#define s390_srk(c, r1, r2, r3) S390_RRF_1(c, 0xb9f9, r1, r2, r3) +#define s390_srl(c, r, b, d) S390_RS_3(c, 0x88, r, b, d) +#define s390_srlg(c, r1, r2, b, d) S390_RSY_1(c, 0xeb0c, r1, r2, b, d) +#define s390_st(c, r, x, b, d) S390_RX(c, 0x50, r, x, b, d) +#define s390_stam(c, r1, r2, b, d) S390_RS_1(c, 0x9b, r1, r2, b, d) +#define s390_stc(c, r, x, b, d) S390_RX(c, 0x42, r, x, b, d) +#define s390_stcm(c, r, m, b, d) S390_RX(c, 0xbe, r, m, b, d) +#define s390_stcmy(c, r, x, b, d) S390_RXY(c, 0xeb2d, r, x, b, d) +#define s390_stcy(c, r, x, b, d) S390_RXY(c, 0xe372, r, x, b, d) +#define s390_std(c, f, x, b, d) S390_RX(c, 0x60, f, x, b, d) +#define s390_stdy(c, r, x, b, d) S390_RXY(c, 0xed67, r, x, b, d) +#define s390_ste(c, f, x, b, d) S390_RX(c, 0x70, f, x, b, d) +#define s390_stey(c, r, x, b, d) S390_RXY(c, 0xed66, r, x, b, d) +#define s390_stfpc(c, b, d) S390_S(c, 0xb29c, b, d) +#define s390_stg(c, r, x, b, d) S390_RXY(c, 0xe324, r, x, b, d) +#define s390_stgsc(c, r, x, b, d) S390_RXY(c, 0xe349, r, x, b, d) +#define s390_sth(c, r, x, b, d) S390_RX(c, 0x40, r, x, b, d) +#define s390_sthy(c, r, x, b, d) S390_RXY(c, 0xe370, r, x, b, d) +#define s390_stm(c, r1, r2, b, d) S390_RS_1(c, 0x90, r1, r2, b, d) +#define s390_stmg(c, r1, r2, b, d) S390_RSY_1(c, 0xeb24, r1, r2, b, d) +#define s390_sty(c, r, x, b, d) S390_RXY(c, 0xe350, r, x, b, d) +#define s390_tcdb(c, r, x, b, d) S390_RXE(c, 0xed11, r, x, b, d) +#define s390_tceb(c, r, x, b, d) S390_RXE(c, 0xed10, r, x, b, d) +#define s390_tmhh(c, r, m) S390_RI(c, 0xa73, r, m) +#define s390_tmhl(c, r, m) S390_RI(c, 0xa72, r, m) +#define s390_tmlh(c, r, m) S390_RI(c, 0xa70, r, m) +#define s390_tmll(c, r, m) S390_RI(c, 0xa71, r, m) +#define s390_tm(c, b, d, v) S390_SI(c, 0x91, b, d, v) +#define s390_x(c, r, x, b, d) S390_RX(c, 0x57, r, x, b, d) +#define s390_xihf(c, r, v) S390_RIL_1(c, 0xc06, r, v) +#define s390_xilf(c, r, v) S390_RIL_1(c, 0xc07, r, v) +#define s390_xg(c, r, x, b, d) S390_RXY(c, 0xe382, r, x, b, d) +#define s390_xgr(c, r1, r2) S390_RRE(c, 0xb982, r1, r2) +#define s390_xgrk(c, r1, r2, r3) S390_RRF_1(c, 0xb9e7, r1, r2, r3) +#define s390_xr(c, r1, r2) S390_RR(c, 0x17, r1, r2) +#define s390_xy(c, r, x, b, d) S390_RXY(c, 0xe357, r, x, b, d) +#endif diff --git a/unity-2019.4.24f1-mbe/mono/arch/s390x/tramp.c b/unity-2019.4.24f1-mbe/mono/arch/s390x/tramp.c new file mode 100644 index 000000000..a79f64e00 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/s390x/tramp.c @@ -0,0 +1,1149 @@ +/*------------------------------------------------------------------*/ +/* */ +/* Name - tramp.c */ +/* */ +/* Function - Create trampolines to invoke arbitrary functions. */ +/* */ +/* Name - Neale Ferguson. */ +/* */ +/* Date - October, 2002 */ +/* */ +/* */ +/*------------------------------------------------------------------*/ + +/*------------------------------------------------------------------*/ +/* D e f i n e s */ +/*------------------------------------------------------------------*/ + +#define PROLOG_INS 24 /* Size of emitted prolog */ +#define CALL_INS 4 /* Size of emitted call */ +#define EPILOG_INS 18 /* Size of emitted epilog */ + +#define DEBUG(x) + +/*========================= End of Defines =========================*/ + +/*------------------------------------------------------------------*/ +/* I n c l u d e s */ +/*------------------------------------------------------------------*/ + +#ifdef NEED_MPROTECT +# include +# include /* for PAGESIZE */ +# ifndef PAGESIZE +# define PAGESIZE 4096 +# endif +#endif + +#include "config.h" +#include +#include +#include "s390x-codegen.h" +#include "mono/metadata/class.h" +#include "mono/metadata/tabledefs.h" +#include "mono/interpreter/interp.h" +#include "mono/metadata/appdomain.h" +#include "mono/metadata/marshal.h" + +/*========================= End of Includes ========================*/ + +/*------------------------------------------------------------------*/ +/* T y p e d e f s */ +/*------------------------------------------------------------------*/ + +/*------------------------------------------------------------------*/ +/* Structure used to accummulate size of stack, code, and locals */ +/*------------------------------------------------------------------*/ +typedef struct { + guint stack_size, + local_size, + code_size, + retStruct; +} size_data; + +/*========================= End of Typedefs ========================*/ + +/*------------------------------------------------------------------*/ +/* */ +/* Name - add_general */ +/* */ +/* Function - Determine code and stack size incremements for a */ +/* parameter. */ +/* */ +/*------------------------------------------------------------------*/ + +static void inline +add_general (guint *gr, size_data *sz, gboolean simple) +{ + if (simple) { + if (*gr >= GENERAL_REGS) { + sz->stack_size += sizeof(long); + sz->code_size += 12; + } else { + sz->code_size += 8; + } + } else { + if (*gr >= GENERAL_REGS - 1) { + sz->stack_size += 8 + (sz->stack_size % 8); + sz->code_size += 10; + } else { + sz->code_size += 8; + } + (*gr) ++; + } + (*gr) ++; +} + +/*========================= End of Function ========================*/ + +/*------------------------------------------------------------------*/ +/* */ +/* Name - calculate_sizes */ +/* */ +/* Function - Determine the amount of space required for code */ +/* and stack. In addition determine starting points */ +/* for stack-based parameters, and area for struct- */ +/* ures being returned on the stack. */ +/* */ +/*------------------------------------------------------------------*/ + +static void inline +calculate_sizes (MonoMethodSignature *sig, size_data *sz, + gboolean string_ctor) +{ + guint i, fr, gr, size; + guint32 simpletype, align; + + fr = 0; + gr = 2; + sz->retStruct = 0; + sz->stack_size = S390_MINIMAL_STACK_SIZE; + sz->code_size = (PROLOG_INS + CALL_INS + EPILOG_INS); + sz->local_size = 0; + + if (sig->hasthis) { + add_general (&gr, sz, TRUE); + } + + /*----------------------------------------------------------*/ + /* We determine the size of the return code/stack in case we*/ + /* need to reserve a register to be used to address a stack */ + /* area that the callee will use. */ + /*----------------------------------------------------------*/ + + if (sig->ret->byref || string_ctor) { + sz->code_size += 8; + } else { + simpletype = sig->ret->type; +enum_retvalue: + switch (simpletype) { + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_CHAR: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_CLASS: + case MONO_TYPE_OBJECT: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + case MONO_TYPE_PTR: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_ARRAY: + case MONO_TYPE_STRING: + sz->code_size += 4; + break; + case MONO_TYPE_I8: + sz->code_size += 4; + break; + case MONO_TYPE_VALUETYPE: + if (sig->ret->data.klass->enumtype) { + simpletype = sig->ret->data.klass->enum_basetype->type; + goto enum_retvalue; + } + gr++; + if (sig->pinvoke) + size = mono_class_native_size (sig->ret->data.klass, &align); + else + size = mono_class_value_size (sig->ret->data.klass, &align); + if (align > 1) + sz->code_size += 10; + switch (size) { + /*----------------------------------*/ + /* On S/390, structures of size 1, */ + /* 2, 4, and 8 bytes are returned */ + /* in (a) register(s). */ + /*----------------------------------*/ + case 1: + case 2: + case 4: + case 8: + sz->code_size += 16; + sz->stack_size += 4; + break; + default: + sz->retStruct = 1; + sz->code_size += 32; + } + break; + case MONO_TYPE_VOID: + break; + default: + g_error ("Can't handle as return value 0x%x", sig->ret->type); + } + } + + /*----------------------------------------------------------*/ + /* We determine the size of the parameter code and stack */ + /* requirements by checking the types and sizes of the */ + /* parameters. */ + /*----------------------------------------------------------*/ + + for (i = 0; i < sig->param_count; ++i) { + if (sig->params [i]->byref) { + add_general (&gr, sz, TRUE); + continue; + } + simpletype = sig->params [i]->type; + enum_calc_size: + switch (simpletype) { + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_CHAR: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_PTR: + case MONO_TYPE_CLASS: + case MONO_TYPE_OBJECT: + case MONO_TYPE_STRING: + add_general (&gr, sz, TRUE); + break; + case MONO_TYPE_SZARRAY: + add_general (&gr, sz, TRUE); + break; + case MONO_TYPE_VALUETYPE: + if (sig->params [i]->data.klass->enumtype) { + simpletype = sig->params [i]->data.klass->enum_basetype->type; + goto enum_calc_size; + } + if (sig->pinvoke) + size = mono_class_native_size (sig->params [i]->data.klass, &align); + else + size = mono_class_value_size (sig->params [i]->data.klass, &align); + DEBUG(printf("%d typesize: %d (%d)\n",i,size,align)); + switch (size) { + /*----------------------------------*/ + /* On S/390, structures of size 1, */ + /* 2, 4, and 8 bytes are passed in */ + /* (a) register(s). */ + /*----------------------------------*/ + case 0: + case 1: + case 2: + case 4: + add_general(&gr, sz, TRUE); + break; + case 8: + add_general(&gr, sz, FALSE); + break; + default: + sz->local_size += (size + (size % align)); + sz->code_size += 40; + } + break; + case MONO_TYPE_I8: + add_general (&gr, sz, FALSE); + break; + case MONO_TYPE_R4: + if (fr < FLOAT_REGS) { + sz->code_size += 4; + fr++; + } + else { + sz->code_size += 4; + sz->stack_size += 8; + } + break; + case MONO_TYPE_R8: + if (fr < FLOAT_REGS) { + sz->code_size += 4; + fr++; + } else { + sz->code_size += 4; + sz->stack_size += 8 + (sz->stack_size % 8); + } + break; + default: + g_error ("Can't trampoline 0x%x", sig->params [i]->type); + } + } + + + /* align stack size to 8 */ + DEBUG (printf (" stack size: %d (%d)\n" + " code size: %d\n" + " local size: %d\n", + (sz->stack_size + 8) & ~8, sz->stack_size, + (sz->code_size),(sz->local_size + 8) & ~8)); + sz->stack_size = (sz->stack_size + 8) & ~8; + sz->local_size = (sz->local_size + 8) & ~8; +} + +/*========================= End of Function ========================*/ + +/*------------------------------------------------------------------*/ +/* */ +/* Name - emit_prolog */ +/* */ +/* Function - Create the instructions that implement the stand- */ +/* ard function prolog according to the S/390 ABI. */ +/* */ +/*------------------------------------------------------------------*/ + +static inline guint8 * +emit_prolog (guint8 *p, MonoMethodSignature *sig, size_data *sz) +{ + guint stack_size; + + stack_size = sz->stack_size + sz->local_size; + + /* function prolog */ + s390_stmg(p, s390_r6, s390_r14, STK_BASE, S390_REG_SAVE_OFFSET); + s390_lg (p, s390_r7, 0, STK_BASE, MINV_POS); + s390_lgr (p, s390_r11, STK_BASE); + s390_aghi(p, STK_BASE, -stack_size); + s390_stg (p, s390_r11, 0, STK_BASE, 0); + + /*-----------------------------------------*/ + /* Save: */ + /* - address of "callme" */ + /* - address of "retval" */ + /* - address of "arguments" */ + /*-----------------------------------------*/ + s390_lgr (p, s390_r9, s390_r2); + s390_lgr (p, s390_r8, s390_r3); + s390_lgr (p, s390_r10, s390_r5); + + return p; +} + +/*========================= End of Function ========================*/ + +/*------------------------------------------------------------------*/ +/* */ +/* Name - emit_save_parameters */ +/* */ +/* Function - Create the instructions that load registers with */ +/* parameters, place others on the stack according */ +/* to the S/390 ABI. */ +/* */ +/* The resulting function takes the form: */ +/* void func (void (*callme)(), void *retval, */ +/* void *this_obj, stackval *arguments); */ +/* */ +/*------------------------------------------------------------------*/ + +inline static guint8* +emit_save_parameters (guint8 *p, MonoMethodSignature *sig, size_data *sz) +{ + guint i, fr, gr, act_strs, align, + stack_par_pos, size, local_pos; + guint32 simpletype; + + /*----------------------------------------------------------*/ + /* If a structure on stack is being returned, reserve r2 */ + /* to point to an area where it can be passed. */ + /*----------------------------------------------------------*/ + if (sz->retStruct) + gr = 1; + else + gr = 0; + fr = 0; + act_strs = 0; + stack_par_pos = S390_MINIMAL_STACK_SIZE; + local_pos = sz->stack_size; + + if (sig->hasthis) { + s390_lr (p, s390_r2 + gr, s390_r4); + gr++; + } + + act_strs = 0; + for (i = 0; i < sig->param_count; ++i) { + DEBUG(printf("par: %d type: %d ref: %d\n",i,sig->params[i]->type,sig->params[i]->byref)); + if (sig->params [i]->byref) { + if (gr < GENERAL_REGS) { + s390_lg (p, s390_r2 + gr, 0, ARG_BASE, STKARG); + gr ++; + } else { + s390_lg (p, s390_r0, 0, ARG_BASE, STKARG); + s390_stg(p, s390_r0, 0, STK_BASE, stack_par_pos); + stack_par_pos += sizeof(long); + } + continue; + } + simpletype = sig->params [i]->type; + enum_calc_size: + switch (simpletype) { + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_CHAR: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_PTR: + case MONO_TYPE_CLASS: + case MONO_TYPE_OBJECT: + case MONO_TYPE_STRING: + case MONO_TYPE_SZARRAY: + if (gr < GENERAL_REGS) { + s390_lg (p, s390_r2 + gr, 0, ARG_BASE, STKARG); + gr ++; + } else { + s390_lg (p, s390_r0, 0, ARG_BASE, STKARG); + s390_stg(p, s390_r0, 0, STK_BASE, stack_par_pos); + stack_par_pos += sizeof(long); + } + break; + case MONO_TYPE_VALUETYPE: + if (sig->params [i]->data.klass->enumtype) { + simpletype = sig->params [i]->data.klass->enum_basetype->type; + goto enum_calc_size; + } + if (sig->pinvoke) + size = mono_class_native_size (sig->params [i]->data.klass, &align); + else + size = mono_class_value_size (sig->params [i]->data.klass, &align); + DEBUG(printf("parStruct - size %d pinvoke: %d\n",size,sig->pinvoke)); + switch (size) { + case 0: + case 1: + case 2: + case 4: + if (gr < GENERAL_REGS) { + s390_lg (p, s390_r2 + gr, 0,ARG_BASE, STKARG); + s390_lgf(p, s390_r2 + gr, 0, s390_r2 + gr, 0); + gr++; + } else { + stack_par_pos += (stack_par_pos % align); + s390_lg (p, s390_r10, 0,ARG_BASE, STKARG); + s390_lgf(p, s390_r10, 0, s390_r10, 0); + s390_st (p, s390_r10, 0, STK_BASE, stack_par_pos); + stack_par_pos += sizeof(long); + } + break; + case 8: + if (gr < GENERAL_REGS) { + s390_lg (p, s390_r2 + gr, 0, ARG_BASE, STKARG); + s390_lg (p, s390_r2 + gr, 0, s390_r2 + gr, 0); + } else { + stack_par_pos += (stack_par_pos % align); + s390_lg (p, s390_r10, 0, ARG_BASE, STKARG); + s390_mvc (p, sizeof(long long), STK_BASE, stack_par_pos, s390_r10, 0); + stack_par_pos += sizeof(long long); + } + break; + default: + if (size <= 256) { + local_pos += (local_pos % align); + s390_lg (p, s390_r13, 0, ARG_BASE, STKARG); + s390_mvc (p, size, STK_BASE, local_pos, s390_r13, 0); + s390_la (p, s390_r13, 0, STK_BASE, local_pos); + local_pos += size; + } else { + local_pos += (local_pos % align); + s390_bras (p, s390_r13, 4); + s390_llong(p, size); + s390_lg (p, s390_r1, 0, s390_r13, 0); + s390_lg (p, s390_r0, 0, ARG_BASE, STKARG); + s390_lgr (p, s390_r14, s390_r12); + s390_la (p, s390_r12, 0, STK_BASE, local_pos); + s390_lgr (p, s390_r13, s390_r1); + s390_mvcl (p, s390_r12, s390_r0); + s390_lgr (p, s390_r12, s390_r14); + s390_la (p, s390_r13, 0, STK_BASE, local_pos); + local_pos += size; + } + if (gr < GENERAL_REGS) { + s390_lgr(p, s390_r2 + gr, s390_r13); + gr++; + } else { + s390_stg(p, s390_r13, 0, STK_BASE, stack_par_pos); + stack_par_pos += sizeof(long); + } + } + break; + case MONO_TYPE_I8: + if (gr < GENERAL_REGS) { + s390_lg (p, s390_r2 + gr, 0, ARG_BASE, STKARG); + gr += 2; + } else { + *(guint32 *) p += 7; + *(guint32 *) p &= ~7; + s390_mvc (p, sizeof(long long), STK_BASE, stack_par_pos, ARG_BASE, STKARG); + stack_par_pos += sizeof(long long) + (stack_par_pos % sizeof(long long)); + } + break; + case MONO_TYPE_R4: + if (fr < FLOAT_REGS) { + s390_le (p, s390_r0 + fr, 0, ARG_BASE, STKARG); + fr++; + } else { + s390_mvc (p, sizeof(float), STK_BASE, stack_par_pos, ARG_BASE, STKARG); + stack_par_pos += sizeof(float); + } + break; + case MONO_TYPE_R8: + if (fr < FLOAT_REGS) { + s390_ld (p, s390_r0 + fr, 0, ARG_BASE, STKARG); + fr++; + } else { + *(guint32 *) p += 7; + *(guint32 *) p &= ~7; + s390_mvc (p, sizeof(double), STK_BASE, stack_par_pos, ARG_BASE, STKARG); + stack_par_pos += sizeof(long long) + (stack_par_pos % sizeof(long long)); + } + break; + default: + g_error ("Can't trampoline 0x%x", sig->params [i]->type); + } + } + + /*----------------------------------------------------------*/ + /* If we're returning a structure but not in a register */ + /* then point the result area for the called routine */ + /*----------------------------------------------------------*/ + if (sz->retStruct) { + s390_lg (p, s390_r2, 0, s390_r8, 0); + } + + return p; +} + +/*========================= End of Function ========================*/ + +/*------------------------------------------------------------------*/ +/* */ +/* Name - alloc_code_memory */ +/* */ +/* Function - Allocate space to place the emitted code. */ +/* */ +/*------------------------------------------------------------------*/ + +static inline guint8 * +alloc_code_memory (guint code_size) +{ + guint8 *p; + +#ifdef NEED_MPROTECT + p = g_malloc (code_size + PAGESIZE - 1); + + /* Align to a multiple of PAGESIZE, assumed to be a power of two */ + p = (char *)(((int) p + PAGESIZE-1) & ~(PAGESIZE-1)); +#else + p = g_malloc (code_size); +#endif + DEBUG (printf (" align: %p (%d)\n", p, (guint)p % 4)); + + return p; +} + +/*========================= End of Function ========================*/ + +/*------------------------------------------------------------------*/ +/* */ +/* Name - emit_call_and_store_retval */ +/* */ +/* Function - Emit code that will implement the call to the */ +/* desired function, and unload the result according */ +/* to the S390 ABI for the type of value returned */ +/* */ +/*------------------------------------------------------------------*/ + +static inline guint8 * +emit_call_and_store_retval (guint8 *p, MonoMethodSignature *sig, + size_data *sz, gboolean string_ctor) +{ + guint32 simpletype; + guint retSize, align; + + /* call "callme" */ + s390_basr (p, s390_r14, s390_r9); + + /* get return value */ + if (sig->ret->byref || string_ctor) { + s390_stg(p, s390_r2, 0, s390_r8, 0); + } else { + simpletype = sig->ret->type; +enum_retvalue: + switch (simpletype) { + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + s390_stc (p, s390_r2, 0, s390_r8, 0); + break; + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_CHAR: + s390_sth (p, s390_r2, 0, s390_r8, 0); + break; + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_CLASS: + case MONO_TYPE_OBJECT: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_ARRAY: + case MONO_TYPE_STRING: + s390_st (p, s390_r2, 0, s390_r8, 0); + break; + case MONO_TYPE_R4: + s390_ste (p, s390_f0, 0, s390_r8, 0); + break; + case MONO_TYPE_R8: + s390_std (p, s390_f0, 0, s390_r8, 0); + break; + case MONO_TYPE_I8: + s390_stg (p, s390_r2, 0, s390_r8, 0); + break; + case MONO_TYPE_VALUETYPE: + if (sig->ret->data.klass->enumtype) { + simpletype = sig->ret->data.klass->enum_basetype->type; + goto enum_retvalue; + } + if (sig->pinvoke) + retSize = mono_class_native_size (sig->ret->data.klass, &align); + else + retSize = mono_class_value_size (sig->ret->data.klass, &align); +printf("Returning %d bytes for type %d (%d)\n",retSize,simpletype,sig->pinvoke); + switch(retSize) { + case 0: + break; + case 1: + s390_stc (p, s390_r2, 0, s390_r8, 0); + break; + case 2: + s390_sth (p, s390_r2, 0, s390_r8, 0); + break; + case 4: + s390_st (p, s390_r2, 0, s390_r8, 0); + break; + case 8: + s390_stg (p, s390_r2, 0, s390_r8, 0); + break; + default: ; + /*------------------------------------------*/ + /* The callee has already placed the result */ + /* in the required area */ + /*------------------------------------------*/ + } + break; + case MONO_TYPE_VOID: + break; + default: + g_error ("Can't handle as return value 0x%x", + sig->ret->type); + } + } + + return p; +} + +/*========================= End of Function ========================*/ + +/*------------------------------------------------------------------*/ +/* */ +/* Name - emit_epilog */ +/* */ +/* Function - Create the instructions that implement the stand- */ +/* ard function epilog according to the S/390 ABI. */ +/* */ +/*------------------------------------------------------------------*/ + +static inline guint8 * +emit_epilog (guint8 *p, MonoMethodSignature *sig, size_data *sz) +{ + /* function epilog */ + s390_lg (p, STK_BASE, 0, STK_BASE, 0); + s390_lg (p, s390_r4, 0, STK_BASE, S390_RET_ADDR_OFFSET); + s390_lmg (p, s390_r6, STK_BASE, STK_BASE, S390_REG_SAVE_OFFSET); + s390_br (p, s390_r4); + + return p; +} + +/*========================= End of Function ========================*/ + +/*------------------------------------------------------------------*/ +/* */ +/* Name - mono_arch_create_trampoline. */ +/* */ +/* Function - Create the code that will allow a mono method to */ +/* invoke a system subroutine. */ +/* */ +/*------------------------------------------------------------------*/ + +MonoPIFunc +mono_arch_create_trampoline (MonoMethodSignature *sig, gboolean string_ctor) +{ + guint8 *p, *code_buffer; + size_data sz; + + DEBUG (printf ("\nPInvoke [start emiting]\n")); + calculate_sizes (sig, &sz, string_ctor); + + p = code_buffer = alloc_code_memory (sz.code_size); + p = emit_prolog (p, sig, &sz); + p = emit_save_parameters (p, sig, &sz); + p = emit_call_and_store_retval (p, sig, &sz, string_ctor); + p = emit_epilog (p, sig, &sz); + +#ifdef NEED_MPROTECT + if (mprotect (code_buffer, 1024, PROT_READ | PROT_WRITE | PROT_EXEC)) { + g_error ("Cannot mprotect trampoline\n"); + } +#endif + + DEBUG (printf ("emited code size: %d\n", p - code_buffer)); + + DEBUG (printf ("PInvoke [end emiting]\n")); + + return (MonoPIFunc) code_buffer; +} + +/*========================= End of Function ========================*/ + +/*------------------------------------------------------------------*/ +/* */ +/* Name - mono_arch_create_method_pointer */ +/* */ +/* Function - Returns a pointer to a native function that can */ +/* be used to call the specified method. */ +/* */ +/* The function created will receive the arguments */ +/* according to the calling convention specified in */ +/* in the method. */ +/* */ +/* This function works by creating a MonoInvocation */ +/* structure, filling the fields in and calling */ +/* ves_exec_method() on it. */ +/* */ +/* Logic: */ +/* ------ */ +/* mono_arch_create_method_pointer (MonoMethod *method) */ +/* create the unmanaged->managed wrapper */ +/* register it with mono_jit_info_table_add() */ +/* */ +/* What does the unmanaged->managed wrapper do? */ +/* allocate a MonoInvocation structure (inv) on the stack */ +/* allocate an array of stackval on the stack with length = */ +/* method->signature->param_count + 1 [call it stack_args] */ +/* set inv->ex, inv->ex_handler, inv->parent to NULL */ +/* set inv->method to method */ +/* if method is an instance method, set inv->obj to the */ +/* 'this' argument (the first argument) else set to NULL */ +/* for each argument to the method call: */ +/* stackval_from_data (sig->params[i], &stack_args[i], */ +/* arg, sig->pinvoke); */ +/* Where: */ +/* ------ */ +/* sig - is method->signature */ +/* &stack_args[i] - is the pointer to the ith element */ +/* in the stackval array */ +/* arg - is a pointer to the argument re- */ +/* ceived by the function according */ +/* to the call convention. If it */ +/* gets passed in a register, save */ +/* on the stack first. */ +/* */ +/* set inv->retval to the address of the last element of */ +/* stack_args [recall we allocated param_count+1 of them] */ +/* call ves_exec_method(inv) */ +/* copy the returned value from inv->retval where the calling */ +/* convention expects to find it on return from the wrap- */ +/* per [if it's a structure, use stackval_to_data] */ +/* */ +/*------------------------------------------------------------------*/ + +void * +mono_arch_create_method_pointer (MonoMethod *method) +{ + MonoMethodSignature *sig; + MonoJitInfo *ji; + guint8 *p, *code_buffer; + guint i, align = 0, simple_type, retSize, reg_save = 0, + stackval_arg_pos, local_pos, float_pos, + local_start, reg_param = 0, stack_param, + this_flag, arg_pos, fpr_param, parSize; + guint32 simpletype; + size_data sz; + int *vtbuf, cpos, vt_cur; + + sz.code_size = 1024; + sz.stack_size = 1024; + stack_param = 0; + fpr_param = 0; + arg_pos = 0; + + sig = method->signature; + + p = code_buffer = g_malloc (sz.code_size); + + DEBUG (printf ("\nDelegate [start emiting] %s at 0x%08x\n", + method->name,p)); + + /*----------------------------------------------------------*/ + /* prolog */ + /*----------------------------------------------------------*/ + s390_stmg(p, s390_r6, STK_BASE, STK_BASE, S390_REG_SAVE_OFFSET); + s390_lg (p, s390_r7, 0, STK_BASE, MINV_POS); + s390_lgr (p, s390_r0, STK_BASE); + s390_aghi(p, STK_BASE, -(sz.stack_size+MINV_POS)); + s390_stg (p, s390_r0, 0, STK_BASE, 0); + s390_la (p, s390_r8, 0, STK_BASE, 4); + s390_lgr (p, s390_r10, s390_r8); + s390_lghi(p, s390_r9, sz.stack_size+92); + s390_lghi(p, s390_r11, 0); + s390_mvcl(p, s390_r8, s390_r10); + + /*----------------------------------------------------------*/ + /* Let's fill MonoInvocation - first zero some fields */ + /*----------------------------------------------------------*/ + s390_lghi (p, s390_r0, 0); + s390_stg (p, s390_r0, 0, STK_BASE, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, ex))); + s390_stg (p, s390_r0, 0, STK_BASE, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, ex_handler))); + s390_stg (p, s390_r0, 0, STK_BASE, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, parent))); + s390_lghi (p, s390_r0, 1); + s390_stg (p, s390_r0, 0, STK_BASE, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, invoke_trap))); + + /*----------------------------------------------------------*/ + /* set method pointer */ + /*----------------------------------------------------------*/ + s390_bras (p, s390_r13, 4); + s390_llong(p, method); + s390_lg (p, s390_r0, 0, s390_r13, 0); + s390_stg (p, s390_r0, 0, STK_BASE, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, method))); + + local_start = local_pos = MINV_POS + + sizeof (MonoInvocation) + (sig->param_count + 1) * sizeof (stackval); + this_flag = (sig->hasthis ? 1 : 0); + + /*----------------------------------------------------------*/ + /* if we are returning a structure, checks it's length to */ + /* see if there's a "hidden" parameter that points to the */ + /* area. If necessary save this hidden parameter for later */ + /*----------------------------------------------------------*/ + if (MONO_TYPE_ISSTRUCT(sig->ret)) { + if (sig->pinvoke) + retSize = mono_class_native_size (sig->ret->data.klass, &align); + else + retSize = mono_class_value_size (sig->ret->data.klass, &align); + switch(retSize) { + case 0: + case 1: + case 2: + case 4: + case 8: + sz.retStruct = 0; + break; + default: + sz.retStruct = 1; + s390_lgr(p, s390_r8, s390_r2); + reg_save = 1; + } + } else { + reg_save = 0; + } + + if (this_flag) { + s390_stg (p, s390_r2 + reg_save, 0, STK_BASE, + (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, obj))); + reg_param++; + } else { + s390_stg (p, s390_r2 + reg_save, 0, STK_BASE, local_pos); + local_pos += sizeof(int); + s390_stg (p, s390_r0, 0, STK_BASE, + (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, obj))); + } + + s390_stmg (p, s390_r3 + reg_param, s390_r6, STK_BASE, local_pos); + local_pos += 4 * sizeof(long); + float_pos = local_pos; + s390_std (p, s390_f0, 0, STK_BASE, local_pos); + local_pos += sizeof(double); + s390_std (p, s390_f2, 0, STK_BASE, local_pos); + local_pos += sizeof(double); + + /*----------------------------------------------------------*/ + /* prepare space for valuetypes */ + /*----------------------------------------------------------*/ + vt_cur = local_pos; + vtbuf = alloca (sizeof(int)*sig->param_count); + cpos = 0; + for (i = 0; i < sig->param_count; i++) { + MonoType *type = sig->params [i]; + vtbuf [i] = -1; + DEBUG(printf("par: %d type: %d ref: %d\n",i,type->type,type->byref)); + if (type->type == MONO_TYPE_VALUETYPE) { + MonoClass *klass = type->data.klass; + gint size; + + if (klass->enumtype) + continue; + size = mono_class_native_size (klass, &align); + cpos += align - 1; + cpos &= ~(align - 1); + vtbuf [i] = cpos; + cpos += size; + } + } + cpos += 3; + cpos &= ~3; + + local_pos += cpos; + + /*----------------------------------------------------------*/ + /* set MonoInvocation::stack_args */ + /*----------------------------------------------------------*/ + stackval_arg_pos = MINV_POS + sizeof (MonoInvocation); + s390_la (p, s390_r0, 0, STK_BASE, stackval_arg_pos); + s390_stg (p, s390_r0, 0, STK_BASE, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, stack_args))); + + /*----------------------------------------------------------*/ + /* add stackval arguments */ + /*----------------------------------------------------------*/ + for (i = 0; i < sig->param_count; ++i) { + if (sig->params [i]->byref) { + ADD_ISTACK_PARM(0, 1); + } else { + simple_type = sig->params [i]->type; + enum_savechk: + switch (simple_type) { + case MONO_TYPE_I8: + ADD_ISTACK_PARM(-1, 2); + break; + case MONO_TYPE_R4: + ADD_RSTACK_PARM(1); + break; + case MONO_TYPE_R8: + ADD_RSTACK_PARM(2); + break; + case MONO_TYPE_VALUETYPE: + if (sig->params [i]->data.klass->enumtype) { + simple_type = sig->params [i]->data.klass->enum_basetype->type; + goto enum_savechk; + } + if (sig->pinvoke) + parSize = mono_class_native_size (sig->params [i]->data.klass, &align); + else + parSize = mono_class_value_size (sig->params [i]->data.klass, &align); + switch(parSize) { + case 0: + case 1: + case 2: + case 4: + ADD_PSTACK_PARM(0, 1); + break; + case 8: + ADD_PSTACK_PARM(-1, 2); + break; + default: + ADD_TSTACK_PARM; + } + break; + default: + ADD_ISTACK_PARM(0, 1); + } + } + + if (vtbuf [i] >= 0) { + s390_la (p, s390_r3, 0, STK_BASE, vt_cur); + s390_stg (p, s390_r3, 0, STK_BASE, stackval_arg_pos); + s390_la (p, s390_r3, 0, STK_BASE, stackval_arg_pos); + vt_cur += vtbuf [i]; + } else { + s390_la (p, s390_r3, 0, STK_BASE, stackval_arg_pos); + } + + /*--------------------------------------*/ + /* Load the parameter registers for the */ + /* call to stackval_from_data */ + /*--------------------------------------*/ + s390_bras (p, s390_r13, 8); + s390_llong(p, sig->params [i]); + s390_llong(p, sig->pinvoke); + s390_llong(p, stackval_from_data); + s390_lg (p, s390_r2, 0, s390_r13, 0); + s390_lg (p, s390_r5, 0, s390_r13, 4); + s390_lg (p, s390_r1, 0, s390_r13, 8); + s390_basr (p, s390_r14, s390_r1); + + stackval_arg_pos += sizeof(stackval); + + /* fixme: alignment */ + DEBUG (printf ("arg_pos %d --> ", arg_pos)); + if (sig->pinvoke) + arg_pos += mono_type_native_stack_size (sig->params [i], &align); + else + arg_pos += mono_type_stack_size (sig->params [i], &align); + + DEBUG (printf ("%d\n", stackval_arg_pos)); + } + + /*----------------------------------------------------------*/ + /* Set return area pointer. */ + /*----------------------------------------------------------*/ + s390_la (p, s390_r10, 0, STK_BASE, stackval_arg_pos); + s390_stg(p, s390_r10, 0, STK_BASE, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, retval))); + if (sig->ret->type == MONO_TYPE_VALUETYPE && !sig->ret->byref) { + MonoClass *klass = sig->ret->data.klass; + if (!klass->enumtype) { + s390_la (p, s390_r9, 0, s390_r10, sizeof(stackval)); + s390_st (p, s390_r9, 0,STK_BASE, stackval_arg_pos); + stackval_arg_pos += sizeof(stackval); + } + } + + /*----------------------------------------------------------*/ + /* call ves_exec_method */ + /*----------------------------------------------------------*/ + s390_bras (p, s390_r13, 4); + s390_llong(p, ves_exec_method); + s390_lg (p, s390_r1, 0, s390_r13, 0); + s390_la (p, s390_r2, 0, STK_BASE, MINV_POS); + s390_basr (p, s390_r14, s390_r1); + + /*----------------------------------------------------------*/ + /* move retval from stackval to proper place (r3/r4/...) */ + /*----------------------------------------------------------*/ + DEBUG(printf("retType: %d byRef: %d\n",sig->ret->type,sig->ret->byref)); + if (sig->ret->byref) { + DEBUG (printf ("ret by ref\n")); + s390_stg(p, s390_r2, 0, s390_r10, 0); + } else { + enum_retvalue: + switch (sig->ret->type) { + case MONO_TYPE_VOID: + break; + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_U1: + s390_lghi(p, s390_r2, 0); + s390_ic (p, s390_r2, 0, s390_r10, 0); + break; + case MONO_TYPE_I2: + case MONO_TYPE_U2: + s390_lh (p, s390_r2, 0,s390_r10, 0); + break; + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I: + case MONO_TYPE_U: + s390_lgf(p, s390_r2, 0, s390_r10, 0); + break; + case MONO_TYPE_OBJECT: + case MONO_TYPE_STRING: + case MONO_TYPE_CLASS: + case MONO_TYPE_I8: + s390_lg (p, s390_r2, 0, s390_r10, 0); + break; + case MONO_TYPE_R4: + s390_le (p, s390_f0, 0, s390_r10, 0); + break; + case MONO_TYPE_R8: + s390_ld (p, s390_f0, 0, s390_r10, 0); + break; + case MONO_TYPE_VALUETYPE: + if (sig->ret->data.klass->enumtype) { + simpletype = sig->ret->data.klass->enum_basetype->type; + goto enum_retvalue; + } + /*---------------------------------*/ + /* Call stackval_to_data to return */ + /* the structure */ + /*---------------------------------*/ + s390_bras (p, s390_r13, 8); + s390_llong(p, sig->ret); + s390_llong(p, sig->pinvoke); + s390_llong(p, stackval_to_data); + s390_lg (p, s390_r2, 0, s390_r13, 0); + s390_lg (p, s390_r3, 0, STK_BASE, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, retval))); + if (sz.retStruct) { + /*------------------------------------------*/ + /* Get stackval_to_data to set result area */ + /*------------------------------------------*/ + s390_lgr (p, s390_r4, s390_r8); + } else { + /*------------------------------------------*/ + /* Give stackval_to_data a temp result area */ + /*------------------------------------------*/ + s390_la (p, s390_r4, 0, STK_BASE, stackval_arg_pos); + } + s390_lg (p, s390_r5, 0,s390_r13, 4); + s390_lg (p, s390_r1, 0, s390_r13, 8); + s390_basr (p, s390_r14, s390_r1); + switch (retSize) { + case 0: + break; + case 1: + s390_lghi(p, s390_r2, 0); + s390_ic (p, s390_r2, 0, s390_r10, 0); + break; + case 2: + s390_lh (p, s390_r2, 0, s390_r10, 0); + break; + case 4: + s390_lgf(p, s390_r2, 0, s390_r10, 0); + break; + case 8: + s390_lg (p, s390_r2, 0, s390_r10, 0); + break; + default: ; + /*-------------------------------------------------*/ + /* stackval_to_data has placed data in result area */ + /*-------------------------------------------------*/ + } + break; + default: + g_error ("Type 0x%x not handled yet in thunk creation", + sig->ret->type); + break; + } + } + + /*----------------------------------------------------------*/ + /* epilog */ + /*----------------------------------------------------------*/ + s390_lg (p, STK_BASE, 0, STK_BASE, 0); + s390_lg (p, s390_r4, 0, STK_BASE, S390_RET_ADDR_OFFSET); + s390_lmg (p, s390_r6, STK_BASE, STK_BASE, S390_REG_SAVE_OFFSET); + s390_br (p, s390_r4); + + DEBUG (printf ("emited code size: %d\n", p - code_buffer)); + + DEBUG (printf ("Delegate [end emiting]\n")); + + ji = g_new0 (MonoJitInfo, 1); + ji->method = method; + ji->code_size = p - code_buffer; + ji->code_start = code_buffer; + + mono_jit_info_table_add (mono_get_root_domain (), ji); + + return ji->code_start; +} + +/*========================= End of Function ========================*/ diff --git a/unity-2019.4.24f1-mbe/mono/arch/sparc/.gitignore b/unity-2019.4.24f1-mbe/mono/arch/sparc/.gitignore new file mode 100644 index 000000000..dc1ebd209 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/sparc/.gitignore @@ -0,0 +1,3 @@ +/Makefile +/Makefile.in +/.deps diff --git a/unity-2019.4.24f1-mbe/mono/arch/sparc/Makefile.am b/unity-2019.4.24f1-mbe/mono/arch/sparc/Makefile.am new file mode 100644 index 000000000..272d0c599 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/sparc/Makefile.am @@ -0,0 +1,2 @@ +EXTRA_DIST = sparc-codegen.h + diff --git a/unity-2019.4.24f1-mbe/mono/arch/sparc/sparc-codegen.h b/unity-2019.4.24f1-mbe/mono/arch/sparc/sparc-codegen.h new file mode 100644 index 000000000..eb421bbcf --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/sparc/sparc-codegen.h @@ -0,0 +1,955 @@ +#ifndef __SPARC_CODEGEN_H__ +#define __SPARC_CODEGEN_H__ + +#if SIZEOF_VOID_P == 8 +#define SPARCV9 1 +#else +#endif + +typedef enum { + sparc_r0 = 0, + sparc_r1 = 1, + sparc_r2 = 2, + sparc_r3 = 3, + sparc_r4 = 4, + sparc_r5 = 5, + sparc_r6 = 6, + sparc_r7 = 7, + sparc_r8 = 8, + sparc_r9 = 9, + sparc_r10 = 10, + sparc_r11 = 11, + sparc_r12 = 12, + sparc_r13 = 13, + sparc_r14 = 14, + sparc_r15 = 15, + sparc_r16 = 16, + sparc_r17 = 17, + sparc_r18 = 18, + sparc_r19 = 19, + sparc_r20 = 20, + sparc_r21 = 21, + sparc_r22 = 22, + sparc_r23 = 23, + sparc_r24 = 24, + sparc_r25 = 25, + sparc_r26 = 26, + sparc_r27 = 27, + sparc_r28 = 28, + sparc_r29 = 29, + sparc_r30 = 30, + sparc_r31 = 31, + /* aliases */ + /* global registers */ + sparc_g0 = 0, sparc_zero = 0, + sparc_g1 = 1, + sparc_g2 = 2, + sparc_g3 = 3, + sparc_g4 = 4, + sparc_g5 = 5, + sparc_g6 = 6, + sparc_g7 = 7, + /* out registers */ + sparc_o0 = 8, + sparc_o1 = 9, + sparc_o2 = 10, + sparc_o3 = 11, + sparc_o4 = 12, + sparc_o5 = 13, + sparc_o6 = 14, sparc_sp = 14, + sparc_o7 = 15, sparc_callsite = 15, + /* local registers */ + sparc_l0 = 16, + sparc_l1 = 17, + sparc_l2 = 18, + sparc_l3 = 19, + sparc_l4 = 20, + sparc_l5 = 21, + sparc_l6 = 22, + sparc_l7 = 23, + /* in registers */ + sparc_i0 = 24, + sparc_i1 = 25, + sparc_i2 = 26, + sparc_i3 = 27, + sparc_i4 = 28, + sparc_i5 = 29, + sparc_i6 = 30, sparc_fp = 30, + sparc_i7 = 31, + sparc_nreg = 32, + /* floating point registers */ + sparc_f0 = 0, + sparc_f1 = 1, + sparc_f2 = 2, + sparc_f3 = 3, + sparc_f4 = 4, + sparc_f5 = 5, + sparc_f6 = 6, + sparc_f7 = 7, + sparc_f8 = 8, + sparc_f9 = 9, + sparc_f10 = 10, + sparc_f11 = 11, + sparc_f12 = 12, + sparc_f13 = 13, + sparc_f14 = 14, + sparc_f15 = 15, + sparc_f16 = 16, + sparc_f17 = 17, + sparc_f18 = 18, + sparc_f19 = 19, + sparc_f20 = 20, + sparc_f21 = 21, + sparc_f22 = 22, + sparc_f23 = 23, + sparc_f24 = 24, + sparc_f25 = 25, + sparc_f26 = 26, + sparc_f27 = 27, + sparc_f28 = 28, + sparc_f29 = 29, + sparc_f30 = 30, + sparc_f31 = 31, +} SparcRegister; + +typedef enum { + sparc_bn = 0, sparc_bnever = 0, + sparc_be = 1, + sparc_ble = 2, + sparc_bl = 3, + sparc_bleu = 4, + sparc_bcs = 5, sparc_blu = 5, + sparc_bneg = 6, + sparc_bvs = 7, sparc_boverflow = 7, + sparc_ba = 8, sparc_balways = 8, + sparc_bne = 9, + sparc_bg = 10, + sparc_bge = 11, + sparc_bgu = 12, + sparc_bcc = 13, sparc_beu = 13, + sparc_bpos = 14, + sparc_bvc = 15 +} SparcCond; + +typedef enum { + /* with fcmp */ + sparc_feq = 0, + sparc_fl = 1, + sparc_fg = 2, + sparc_unordered = 3, + /* branch ops */ + sparc_fba = 8, + sparc_fbn = 0, + sparc_fbu = 7, + sparc_fbg = 6, + sparc_fbug = 5, + sparc_fbl = 4, + sparc_fbul = 3, + sparc_fblg = 2, + sparc_fbne = 1, + sparc_fbe = 9, + sparc_fbue = 10, + sparc_fbge = 11, + sparc_fbuge = 12, + sparc_fble = 13, + sparc_fbule = 14, + sparc_fbo = 15 +} SparcFCond; + +typedef enum { + sparc_icc = 4, + sparc_xcc = 6, + sparc_fcc0 = 0, + sparc_fcc1 = 1, + sparc_fcc2 = 2, + sparc_fcc3 = 3 +} SparcCC; + +typedef enum { + sparc_icc_short = 0, + sparc_xcc_short = 2 +} SparcCCShort; + +typedef enum { + /* fop1 format */ + sparc_fitos_val = 196, + sparc_fitod_val = 200, + sparc_fitoq_val = 204, + sparc_fxtos_val = 132, + sparc_fxtod_val = 136, + sparc_fxtoq_val = 140, + sparc_fstoi_val = 209, + sparc_fdtoi_val = 210, + sparc_fqtoi_val = 211, + sparc_fstod_val = 201, + sparc_fstoq_val = 205, + sparc_fdtos_val = 198, + sparc_fdtoq_val = 206, + sparc_fqtos_val = 199, + sparc_fqtod_val = 203, + sparc_fmovs_val = 1, + sparc_fmovd_val = 2, + sparc_fnegs_val = 5, + sparc_fnegd_val = 6, + sparc_fabss_val = 9, + sparc_fabsd_val = 10, + sparc_fsqrts_val = 41, + sparc_fsqrtd_val = 42, + sparc_fsqrtq_val = 43, + sparc_fadds_val = 65, + sparc_faddd_val = 66, + sparc_faddq_val = 67, + sparc_fsubs_val = 69, + sparc_fsubd_val = 70, + sparc_fsubq_val = 71, + sparc_fmuls_val = 73, + sparc_fmuld_val = 74, + sparc_fmulq_val = 75, + sparc_fsmuld_val = 105, + sparc_fdmulq_val = 111, + sparc_fdivs_val = 77, + sparc_fdivd_val = 78, + sparc_fdivq_val = 79, + /* fop2 format */ + sparc_fcmps_val = 81, + sparc_fcmpd_val = 82, + sparc_fcmpq_val = 83, + sparc_fcmpes_val = 85, + sparc_fcmped_val = 86, + sparc_fcmpeq_val = 87 +} SparcFOp; + +typedef enum { + sparc_membar_load_load = 0x1, + sparc_membar_store_load = 0x2, + sparc_membar_load_store = 0x4, + sparc_membar_store_store = 0x8, + + sparc_membar_lookaside = 0x10, + sparc_membar_memissue = 0x20, + sparc_membar_sync = 0x40, + + sparc_membar_all = 0x4f +} SparcMembarFlags; + +typedef struct { + unsigned int op : 2; /* always 1 */ + unsigned int disp : 30; +} sparc_format1; + +typedef struct { + unsigned int op : 2; /* always 0 */ + unsigned int rd : 5; + unsigned int op2 : 3; + unsigned int disp : 22; +} sparc_format2a; + +typedef struct { + unsigned int op : 2; /* always 0 */ + unsigned int a : 1; + unsigned int cond : 4; + unsigned int op2 : 3; + unsigned int disp : 22; +} sparc_format2b; + +typedef struct { + unsigned int op : 2; /* always 0 */ + unsigned int a : 1; + unsigned int cond : 4; + unsigned int op2 : 3; + unsigned int cc01 : 2; + unsigned int p : 1; + unsigned int d19 : 19; +} sparc_format2c; + +typedef struct { + unsigned int op : 2; /* always 0 */ + unsigned int a : 1; + unsigned int res : 1; + unsigned int rcond: 3; + unsigned int op2 : 3; + unsigned int d16hi: 2; + unsigned int p : 1; + unsigned int rs1 : 5; + unsigned int d16lo: 14; +} sparc_format2d; + +typedef struct { + unsigned int op : 2; /* 2 or 3 */ + unsigned int rd : 5; + unsigned int op3 : 6; + unsigned int rs1 : 5; + unsigned int i : 1; + unsigned int asi : 8; + unsigned int rs2 : 5; +} sparc_format3a; + +typedef struct { + unsigned int op : 2; /* 2 or 3 */ + unsigned int rd : 5; + unsigned int op3 : 6; + unsigned int rs1 : 5; + unsigned int i : 1; + unsigned int x : 1; + unsigned int asi : 7; + unsigned int rs2 : 5; +} sparc_format3ax; + +typedef struct { + unsigned int op : 2; /* 2 or 3 */ + unsigned int rd : 5; + unsigned int op3 : 6; + unsigned int rs1 : 5; + unsigned int i : 1; + unsigned int imm : 13; +} sparc_format3b; + +typedef struct { + unsigned int op : 2; /* 2 or 3 */ + unsigned int rd : 5; + unsigned int op3 : 6; + unsigned int rs1 : 5; + unsigned int i : 1; + unsigned int x : 1; + unsigned int imm : 12; +} sparc_format3bx; + +typedef struct { + unsigned int op : 2; /* 2 or 3 */ + unsigned int rd : 5; + unsigned int op3 : 6; + unsigned int rs1 : 5; + unsigned int opf : 9; + unsigned int rs2 : 5; +} sparc_format3c; + +typedef struct { + unsigned int op : 2; + unsigned int rd : 5; + unsigned int op3 : 6; + unsigned int rs1 : 5; + unsigned int i : 1; + unsigned int cc01 : 2; + unsigned int res : 6; + unsigned int rs2 : 5; +} sparc_format4a; + +typedef struct { + unsigned int op : 2; + unsigned int rd : 5; + unsigned int op3 : 6; + unsigned int rs1 : 5; + unsigned int i : 1; + unsigned int cc01 : 2; + unsigned int simm : 11; +} sparc_format4b; + +typedef struct { + unsigned int op : 2; + unsigned int rd : 5; + unsigned int op3 : 6; + unsigned int cc2 : 1; + unsigned int cond : 4; + unsigned int i : 1; + unsigned int cc01 : 2; + unsigned int res : 6; + unsigned int rs2 : 5; +} sparc_format4c; + +typedef struct { + unsigned int op : 2; + unsigned int rd : 5; + unsigned int op3 : 6; + unsigned int cc2 : 1; + unsigned int cond : 4; + unsigned int i : 1; + unsigned int cc01 : 2; + unsigned int simm : 11; +} sparc_format4d; + +/* for use in logical ops, use 0 to not set flags */ +#define sparc_cc 16 + +#define sparc_is_imm13(val) ((glong)val >= (glong)-(1<<12) && (glong)val <= (glong)((1<<12)-1)) +#define sparc_is_imm22(val) ((glong)val >= (glong)-(1<<21) && (glong)val <= (glong)((1<<21)-1)) +#define sparc_is_imm16(val) ((glong)val >= (glong)-(1<<15) && (glong)val <= (glong)((1<<15)-1)) +#define sparc_is_imm19(val) ((glong)val >= (glong)-(1<<18) && (glong)val <= (glong)((1<<18)-1)) +#define sparc_is_imm30(val) ((glong)val >= (glong)-(1<<29) && (glong)val <= (glong)((1<<29)-1)) + +/* disassembly */ +#define sparc_inst_op(inst) ((inst) >> 30) +#define sparc_inst_op2(inst) (((inst) >> 22) & 0x7) +#define sparc_inst_rd(inst) (((inst) >> 25) & 0x1f) +#define sparc_inst_op3(inst) (((inst) >> 19) & 0x3f) +#define sparc_inst_i(inst) (((inst) >> 13) & 0x1) +#define sparc_inst_rs1(inst) (((inst) >> 14) & 0x1f) +#define sparc_inst_rs2(inst) (((inst) >> 0) & 0x1f) +#define sparc_inst_imm(inst) (((inst) >> 13) & 0x1) +#define sparc_inst_imm13(inst) (((inst) >> 0) & 0x1fff) + +#define sparc_encode_call(ins,addr) \ + do { \ + sparc_format1 *__f = (sparc_format1*)(ins); \ + __f->op = 1; \ + __f->disp = ((unsigned int)(addr) >> 2); \ + (ins) = (unsigned int*)__f + 1; \ + } while (0) + +#define sparc_encode_format2a(ins,val,oper,dest) \ + do { \ + sparc_format2a *__f = (sparc_format2a*)(ins); \ + __f->op = 0; \ + __f->rd = (dest); \ + __f->op2 = (oper); \ + __f->disp = (val) & 0x3fffff; \ + (ins) = (unsigned int*)__f + 1; \ + } while (0) + +#define sparc_encode_format2b(ins,aval,bcond,oper,disp22) \ + do { \ + sparc_format2b *__f = (sparc_format2b*)(ins); \ + __f->op = 0; \ + __f->a = (aval); \ + __f->cond = (bcond); \ + __f->op2 = (oper); \ + __f->disp = (disp22); \ + (ins) = (unsigned int*)__f + 1; \ + } while (0) + +#define sparc_encode_format2c(ins,aval,bcond,oper,xcc,predict,disp19) \ + do { \ + sparc_format2c *__f = (sparc_format2c*)(ins); \ + __f->op = 0; \ + __f->a = (aval); \ + __f->cond = (bcond); \ + __f->op2 = (oper); \ + __f->cc01 = (xcc); \ + __f->p = (predict); \ + __f->d19 = (disp19); \ + (ins) = (unsigned int*)__f + 1; \ + } while (0) + +#define sparc_encode_format2d(ins,aval,bcond,oper,predict,r1,disp16) \ + do { \ + sparc_format2d *__f = (sparc_format2d*)(ins); \ + __f->op = 0; \ + __f->a = (aval); \ + __f->res = 0; \ + __f->rcond = (bcond); \ + __f->op2 = (oper); \ + __f->d16hi = ((disp16) >> 14); \ + __f->p = (predict); \ + __f->rs1 = (r1); \ + __f->d16lo = ((disp16) & 0x3fff); \ + (ins) = (unsigned int*)__f + 1; \ + } while (0) + +#define sparc_encode_format3a(ins,opval,asival,r1,r2,oper,dest) \ + do { \ + sparc_format3a *__f = (sparc_format3a*)(ins); \ + __f->op = (opval); \ + __f->asi = (asival); \ + __f->i = 0; \ + __f->rd = (dest); \ + __f->rs1 = (r1); \ + __f->rs2 = (r2); \ + __f->op3 = (oper); \ + (ins) = (unsigned int*)__f + 1; \ + } while (0) + +#define sparc_encode_format3ax(ins,opval,asival,r1,r2,oper,dest) \ + do { \ + sparc_format3ax *__f = (sparc_format3ax*)(ins); \ + __f->op = (opval); \ + __f->asi = (asival); \ + __f->i = 0; \ + __f->x = 1; \ + __f->rd = (dest); \ + __f->rs1 = (r1); \ + __f->rs2 = (r2); \ + __f->op3 = (oper); \ + (ins) = (unsigned int*)__f + 1; \ + } while (0) + +#define sparc_encode_format3b(ins,opval,r1,val,oper,dest) \ + do { \ + sparc_format3b *__f = (sparc_format3b*)(ins); \ + __f->op = (opval); \ + __f->imm = (val); \ + __f->i = 1; \ + __f->rd = (dest); \ + __f->rs1 = (r1); \ + __f->op3 = (oper); \ + (ins) = (unsigned int*)__f + 1; \ + } while (0) + +#define sparc_encode_format3bx(ins,opval,r1,val,oper,dest) \ + do { \ + sparc_format3bx *__f = (sparc_format3bx*)(ins); \ + __f->op = (opval); \ + __f->imm = (val); \ + __f->i = 1; \ + __f->x = 1; \ + __f->rd = (dest); \ + __f->rs1 = (r1); \ + __f->op3 = (oper); \ + (ins) = (unsigned int*)__f + 1; \ + } while (0) + +#define sparc_encode_format3c(ins,opval,opfval,r1,oper,r2,dest) \ + do { \ + sparc_format3c *__f = (sparc_format3c*)(ins); \ + __f->op = (opval); \ + __f->opf = (opfval); \ + __f->rd = (dest); \ + __f->rs1 = (r1); \ + __f->rs2 = (r2); \ + __f->op3 = (oper); \ + (ins) = (unsigned int*)__f + 1; \ + } while (0) + +#define sparc_encode_format4a(ins,opval,oper,cc,r1,r2,dest) \ + do { \ + sparc_format4a *__f = (sparc_format4a*)(ins); \ + __f->op = (opval); \ + __f->rd = (dest); \ + __f->op3 = (oper); \ + __f->rs1 = (r1); \ + __f->i = 0; \ + __f->cc01= (cc) & 0x3; \ + __f->res = 0; \ + __f->rs2 = (r2); \ + (ins) = (unsigned int*)__f + 1; \ + } while (0) + +#define sparc_encode_format4b(ins,opval,oper,cc,r1,imm,dest) \ + do { \ + sparc_format4b *__f = (sparc_format4b*)(ins); \ + __f->op = (opval); \ + __f->rd = (dest); \ + __f->op3 = (oper); \ + __f->rs1 = (r1); \ + __f->i = 1; \ + __f->cc01= (cc) & 0x3; \ + __f->simm = (imm); \ + (ins) = (unsigned int*)__f + 1; \ + } while (0) + +#define sparc_encode_format4c(ins,opval,oper,cc,bcond,r2,dest) \ + do { \ + sparc_format4c *__f = (sparc_format4c*)(ins); \ + __f->op = (opval); \ + __f->rd = (dest); \ + __f->op3 = (oper); \ + __f->cc2 = ((xcc) >> 2) & 0x1; \ + __f->cond = bcond; \ + __f->i = 0; \ + __f->cc01= (xcc) & 0x3; \ + __f->res = 0; \ + __f->rs2 = (r2); \ + (ins) = (unsigned int*)__f + 1; \ + } while (0) + +#define sparc_encode_format4d(ins,opval,oper,xcc,bcond,imm,dest) \ + do { \ + sparc_format4d *__f = (sparc_format4d*)(ins); \ + __f->op = (opval); \ + __f->rd = (dest); \ + __f->op3 = (oper); \ + __f->cc2 = ((xcc) >> 2) & 0x1; \ + __f->cond = bcond; \ + __f->i = 1; \ + __f->cc01= (xcc) & 0x3; \ + __f->simm = (imm); \ + (ins) = (unsigned int*)__f + 1; \ + } while (0) + +/* is it useful to provide a non-default value? */ +#define sparc_asi 0x0 + +/* load */ +#define sparc_ldsb(ins,base,disp,dest) sparc_encode_format3a((ins),3,sparc_asi,(base),(disp),9,(dest)) +#define sparc_ldsb_imm(ins,base,disp,dest) sparc_encode_format3b((ins),3,(base),(disp),9,(dest)) + +#define sparc_ldsh(ins,base,disp,dest) sparc_encode_format3a((ins),3,sparc_asi,(base),(disp),10,(dest)) +#define sparc_ldsh_imm(ins,base,disp,dest) sparc_encode_format3b((ins),3,(base),(disp),10,(dest)) + +#define sparc_ldub(ins,base,disp,dest) sparc_encode_format3a((ins),3,sparc_asi,(base),(disp),1,(dest)) +#define sparc_ldub_imm(ins,base,disp,dest) sparc_encode_format3b((ins),3,(base),(disp),1,(dest)) + +#define sparc_lduh(ins,base,disp,dest) sparc_encode_format3a((ins),3,sparc_asi,(base),(disp),2,(dest)) +#define sparc_lduh_imm(ins,base,disp,dest) sparc_encode_format3b((ins),3,(base),(disp),2,(dest)) + +#define sparc_ld(ins,base,disp,dest) sparc_encode_format3a((ins),3,sparc_asi,(base),(disp),0,(dest)) +#define sparc_ld_imm(ins,base,disp,dest) sparc_encode_format3b((ins),3,(base),(disp),0,(dest)) + +/* Sparc V9 */ +#define sparc_ldx(ins,base,disp,dest) sparc_encode_format3a((ins),3,sparc_asi,(base),(disp),11,(dest)) +#define sparc_ldx_imm(ins,base,disp,dest) sparc_encode_format3b((ins),3,(base),(disp),11,(dest)) + +#define sparc_ldsw(ins,base,disp,dest) sparc_encode_format3a((ins),3,sparc_asi,(base),(disp),8,(dest)) +#define sparc_ldsw_imm(ins,base,disp,dest) sparc_encode_format3b((ins),3,(base),(disp),8,(dest)) + +#define sparc_ldd(ins,base,disp,dest) sparc_encode_format3a((ins),3,sparc_asi,(base),(disp),3,(dest)) +#define sparc_ldd_imm(ins,base,disp,dest) sparc_encode_format3b((ins),3,(base),(disp),3,(dest)) + +#define sparc_ldf(ins,base,disp,dest) sparc_encode_format3a((ins),3,0,(base),(disp),32,(dest)) +#define sparc_ldf_imm(ins,base,disp,dest) sparc_encode_format3b((ins),3,(base),(disp),32,(dest)) + +#define sparc_lddf(ins,base,disp,dest) sparc_encode_format3a((ins),3,0,(base),(disp),35,(dest)) +#define sparc_lddf_imm(ins,base,disp,dest) sparc_encode_format3b((ins),3,(base),(disp),35,(dest)) + +/* store */ +#define sparc_stb(ins,src,base,disp) sparc_encode_format3a((ins),3,sparc_asi,(base),(disp),5,(src)) +#define sparc_stb_imm(ins,src,base,disp) sparc_encode_format3b((ins),3,(base),(disp),5,(src)) + +#define sparc_sth(ins,src,base,disp) sparc_encode_format3a((ins),3,sparc_asi,(base),(disp),6,(src)) +#define sparc_sth_imm(ins,src,base,disp) sparc_encode_format3b((ins),3,(base),(disp),6,(src)) + +#define sparc_st(ins,src,base,disp) sparc_encode_format3a((ins),3,sparc_asi,(base),(disp),4,(src)) +#define sparc_st_imm(ins,src,base,disp) sparc_encode_format3b((ins),3,(base),(disp),4,(src)) + +/* Sparc V9 */ +#define sparc_stx(ins,src,base,disp) sparc_encode_format3a((ins),3,sparc_asi,(base),(disp),14,(src)) +#define sparc_stx_imm(ins,src,base,disp) sparc_encode_format3b((ins),3,(base),(disp),14,(src)) + +#define sparc_std(ins,src,base,disp) sparc_encode_format3a((ins),3,sparc_asi,(base),(disp),7,(src)) +#define sparc_std_imm(ins,src,base,disp) sparc_encode_format3b((ins),3,(base),(disp),7,(src)) + +#define sparc_stf(ins,src,base,disp) sparc_encode_format3a((ins),3,sparc_asi,(base),(disp),36,(src)) +#define sparc_stf_imm(ins,src,base,disp) sparc_encode_format3b((ins),3,(base),(disp),36,(src)) + +#define sparc_stdf(ins,src,base,disp) sparc_encode_format3a((ins),3,sparc_asi,(base),(disp),39,(src)) +#define sparc_stdf_imm(ins,src,base,disp) sparc_encode_format3b((ins),3,(base),(disp),39,(src)) + +/* swap */ +#define sparc_ldstub(ins,base,disp,dest) sparc_encode_format3a((ins),3,sparc_asi,(base),(disp),13,(dest)) +#define sparc_ldstub_imm(ins,base,disp,dest) sparc_encode_format3b((ins),3,(base),(disp),13,(dest)) + +#define sparc_swap(ins,base,disp,dest) sparc_encode_format3a((ins),3,sparc_asi,(base),(disp),15,(dest)) +#define sparc_swap_imm(ins,base,disp,dest) sparc_encode_format3b((ins),3,(base),(disp),15,(dest)) + +/* misc */ +/* note: with sethi val is the full 32 bit value (think of it as %hi(val)) */ +#define sparc_sethi(ins,val,dest) sparc_encode_format2a((ins),((val)>>10),4,(dest)) + +#define sparc_nop(ins) sparc_sethi((ins),0,sparc_zero) + +#define sparc_save(ins,src,disp,dest) sparc_encode_format3a((ins),2,0,(src),(disp),60,(dest)) +#define sparc_save_imm(ins,src,disp,dest) sparc_encode_format3b((ins),2,(src),(disp),60,(dest)) + +#define sparc_restore(ins,src,disp,dest) sparc_encode_format3a((ins),2,0,(src),(disp),61,(dest)) +#define sparc_restore_imm(ins,src,disp,dest) sparc_encode_format3b((ins),2,(src),(disp),61,(dest)) + +#define sparc_rett(ins,src,disp) sparc_encode_format3a((ins),2,0,(src),(disp),0x39,0) +#define sparc_rett_imm(ins,src,disp) sparc_encode_format3b((ins),2,(src),(disp),0x39,0) + +#define sparc_jmpl(ins,base,disp,dest) sparc_encode_format3a((ins),2,0,(base),(disp),56,(dest)) +#define sparc_jmpl_imm(ins,base,disp,dest) sparc_encode_format3b((ins),2,(base),(disp),56,(dest)) + +#define sparc_call_simple(ins,disp) sparc_encode_call((ins),((unsigned int)(disp))) + +#define sparc_rdy(ins,dest) sparc_encode_format3a((ins),2,0,0,0,40,(dest)) + +#define sparc_wry(ins,base,disp) sparc_encode_format3a((ins),2,0,(base),(disp),48,0) +#define sparc_wry_imm(ins,base,disp) sparc_encode_format3b((ins),2,(base),(disp),48,0) + +/* stbar, unimp, flush */ +#define sparc_stbar(ins) sparc_encode_format3a((ins),2,0,15,0,40,0) +#define sparc_unimp(ins,val) sparc_encode_format2b((ins),0,0,0,(val)) + +#define sparc_flush(ins,base,disp) sparc_encode_format3a((ins),2,0,(base),(disp),59,0) +#define sparc_flush_imm(ins,base,disp) sparc_encode_format3b((ins),2,(base),(disp),59,0) + +#define sparc_flushw(ins) sparc_encode_format3a((ins),2,0,0,0,43,0) + +#define sparc_membar(ins,flags) sparc_encode_format3b ((ins), 2, 0xf, (flags), 0x28, 0) + +/* trap */ + +#define sparc_ta(ins,tt) sparc_encode_format3b((ins),2,0,(tt),58,0x8) + +/* alu fop */ +/* provide wrappers for: fitos, fitod, fstoi, fdtoi, fstod, fdtos, fmov, fneg, fabs */ + +#define sparc_fop(ins,r1,op,r2,dest) sparc_encode_format3c((ins),2,(op),(r1),52,(r2),(dest)) +#define sparc_fcmp(ins,r1,op,r2) sparc_encode_format3c((ins),2,(op),(r1),53,(r2),0) + +/* format 1 fops */ +#define sparc_fadds(ins, r1, r2, dest) sparc_fop( ins, r1, sparc_fadds_val, r2, dest ) +#define sparc_faddd(ins, r1, r2, dest) sparc_fop( ins, r1, sparc_faddd_val, r2, dest ) +#define sparc_faddq(ins, r1, r2, dest) sparc_fop( ins, r1, sparc_faddq_val, r2, dest ) + +#define sparc_fsubs(ins, r1, r2, dest) sparc_fop( ins, r1, sparc_fsubs_val, r2, dest ) +#define sparc_fsubd(ins, r1, r2, dest) sparc_fop( ins, r1, sparc_fsubd_val, r2, dest ) +#define sparc_fsubq(ins, r1, r2, dest) sparc_fop( ins, r1, sparc_fsubq_val, r2, dest ) + +#define sparc_fmuls( ins, r1, r2, dest ) sparc_fop( ins, r1, sparc_fmuls_val, r2, dest ) +#define sparc_fmuld( ins, r1, r2, dest ) sparc_fop( ins, r1, sparc_fmuld_val, r2, dest ) +#define sparc_fmulq( ins, r1, r2, dest ) sparc_fop( ins, r1, sparc_fmulq_val, r2, dest ) + +#define sparc_fsmuld( ins, r1, r2, dest ) sparc_fop( ins, r1, sparc_fsmuld_val, r2, dest ) +#define sparc_fdmulq( ins, r1, r2, dest ) sparc_fop( ins, r1, sparc_fdmulq_val, r2, dest ) + +#define sparc_fdivs( ins, r1, r2, dest ) sparc_fop( ins, r1, sparc_fdivs_val, r2, dest ) +#define sparc_fdivd( ins, r1, r2, dest ) sparc_fop( ins, r1, sparc_fdivd_val, r2, dest ) +#define sparc_fdivq( ins, r1, r2, dest ) sparc_fop( ins, r1, sparc_fdivq_val, r2, dest ) + +#define sparc_fitos( ins, r2, dest ) sparc_fop( ins, 0, sparc_fitos_val, r2, dest ) +#define sparc_fitod( ins, r2, dest ) sparc_fop( ins, 0, sparc_fitod_val, r2, dest ) +#define sparc_fitoq( ins, r2, dest ) sparc_fop( ins, 0, sparc_fitoq_val, r2, dest ) + +#define sparc_fxtos( ins, r2, dest) sparc_fop( ins, 0, sparc_fxtos_val, r2, dest ) +#define sparc_fxtod( ins, r2, dest) sparc_fop( ins, 0, sparc_fxtod_val, r2, dest ) +#define sparc_fxtoq( ins, r2, dest) sparc_fop( ins, 0, sparc_fxtoq_val, r2, dest ) + +#define sparc_fstoi( ins, r2, dest ) sparc_fop( ins, 0, sparc_fstoi_val, r2, dest ) +#define sparc_fdtoi( ins, r2, dest ) sparc_fop( ins, 0, sparc_fdtoi_val, r2, dest ) +#define sparc_fqtoi( ins, r2, dest ) sparc_fop( ins, 0, sparc_fqtoi_val, r2, dest ) + +#define sparc_fstod( ins, r2, dest ) sparc_fop( ins, 0, sparc_fstod_val, r2, dest ) +#define sparc_fstoq( ins, r2, dest ) sparc_fop( ins, 0, sparc_fstoq_val, r2, dest ) + +#define sparc_fdtos( ins, r2, dest ) sparc_fop( ins, 0, sparc_fdtos_val, r2, dest ) +#define sparc_fdtoq( ins, r2, dest ) sparc_fop( ins, 0, sparc_fdtoq_val, r2, dest ) + +#define sparc_fqtos( ins, r2, dest ) sparc_fop( ins, 0, sparc_fqtos_val, r2, dest ) +#define sparc_fqtod( ins, r2, dest ) sparc_fop( ins, 0, sparc_fqtod_val, r2, dest ) + +#define sparc_fmovs( ins, r2, dest ) sparc_fop( ins, 0, sparc_fmovs_val, r2, dest ) +#define sparc_fnegs( ins, r2, dest ) sparc_fop( ins, 0, sparc_fnegs_val, r2, dest ) +#define sparc_fabss( ins, r2, dest ) sparc_fop( ins, 0, sparc_fabss_val, r2, dest ) + +#define sparc_fmovd( ins, r2, dest) sparc_fop (ins, 0, sparc_fmovd_val, r2, dest); +#define sparc_fnegd( ins, r2, dest) sparc_fop (ins, 0, sparc_fnegd_val, r2, dest); +#define sparc_fabsd( ins, r2, dest) sparc_fop (ins, 0, sparc_fabsd_val, r2, dest); + +#define sparc_fsqrts( ins, r2, dest ) sparc_fop( ins, 0, sparc_fsqrts_val, r2, dest ) +#define sparc_fsqrtd( ins, r2, dest ) sparc_fop( ins, 0, sparc_fsqrtd_val, r2, dest ) +#define sparc_fsqrtq( ins, r2, dest ) sparc_fop( ins, 0, sparc_fsqrtq_val, r2, dest ) + +/* format 2 fops */ + +#define sparc_fcmps( ins, r1, r2 ) sparc_fcmp( ins, r1, sparc_fcmps_val, r2 ) +#define sparc_fcmpd( ins, r1, r2 ) sparc_fcmp( ins, r1, sparc_fcmpd_val, r2 ) +#define sparc_fcmpq( ins, r1, r2 ) sparc_fcmp( ins, r1, sparc_fcmpq_val, r2 ) +#define sparc_fcmpes( ins, r1, r2 ) sparc_fcmpes( ins, r1, sparc_fcmpes_val, r2 ) +#define sparc_fcmped( ins, r1, r2 ) sparc_fcmped( ins, r1, sparc_fcmped_val, r2 ) +#define sparc_fcmpeq( ins, r1, r2 ) sparc_fcmpeq( ins, r1, sparc_fcmpeq_val, r2 ) + +/* logical */ + +/* FIXME: condense this using macros */ +/* FIXME: the setcc stuff is wrong in lots of places */ + +#define sparc_logic(ins,op,setcc,r1,r2,dest) sparc_encode_format3a((ins),2,0,(r1),(r2),((setcc) ? 0x10 : 0) | (op), (dest)) +#define sparc_logic_imm(ins,op,setcc,r1,imm,dest) sparc_encode_format3b((ins),2,(r1),(imm),((setcc) ? 0x10 : 0) | (op), (dest)) + +#define sparc_and(ins,setcc,r1,r2,dest) sparc_logic(ins,1,setcc,r1,r2,dest) +#define sparc_and_imm(ins,setcc,r1,imm,dest) sparc_logic_imm(ins,1,setcc,r1,imm,dest) + +#define sparc_andn(ins,setcc,r1,r2,dest) sparc_encode_format3a((ins),2,0,(r1),(r2),(setcc)|5,(dest)) +#define sparc_andn_imm(ins,setcc,r1,imm,dest) sparc_encode_format3b((ins),2,(r1),(imm),(setcc)|5,(dest)) + +#define sparc_or(ins,setcc,r1,r2,dest) sparc_encode_format3a((ins),2,0,(r1),(r2),(setcc)|2,(dest)) +#define sparc_or_imm(ins,setcc,r1,imm,dest) sparc_encode_format3b((ins),2,(r1),(imm),(setcc)|2,(dest)) + +#define sparc_orn(ins,setcc,r1,r2,dest) sparc_encode_format3a((ins),2,0,(r1),(r2),(setcc)|6,(dest)) +#define sparc_orn_imm(ins,setcc,r1,imm,dest) sparc_encode_format3b((ins),2,(r1),(imm),(setcc)|6,(dest)) + +#define sparc_xor(ins,setcc,r1,r2,dest) sparc_encode_format3a((ins),2,0,(r1),(r2),(setcc)|3,(dest)) +#define sparc_xor_imm(ins,setcc,r1,imm,dest) sparc_encode_format3b((ins),2,(r1),(imm), (setcc)|3,(dest)) + +#define sparc_xnor(ins,setcc,r1,r2,dest) sparc_encode_format3a((ins),2,0,(r1),(r2),(setcc)|7,(dest)) +#define sparc_xnor_imm(ins,setcc,r1,imm,dest) sparc_encode_format3b((ins),2,(r1),(imm),(setcc)|7,(dest)) + +/* shift */ +#define sparc_sll(ins,src,disp,dest) sparc_encode_format3a((ins),2,0,(src),(disp),37,(dest)) +#define sparc_sll_imm(ins,src,disp,dest) sparc_encode_format3b((ins),2,(src),(disp),37,(dest)) + +/* Sparc V9 */ +#define sparc_sllx(ins,src,disp,dest) sparc_encode_format3ax((ins),2,0,(src),(disp),37,(dest)) +#define sparc_sllx_imm(ins,src,disp,dest) sparc_encode_format3bx((ins),2,(src),(disp),37,(dest)) + +#define sparc_srl(ins,src,disp,dest) sparc_encode_format3a((ins),2,0,(src),(disp),38,(dest)) +#define sparc_srl_imm(ins,src,disp,dest) sparc_encode_format3b((ins),2,(src),(disp),38,(dest)) + +/* Sparc V9 */ +#define sparc_srlx(ins,src,disp,dest) sparc_encode_format3ax((ins),2,0,(src),(disp),38,(dest)) +#define sparc_srlx_imm(ins,src,disp,dest) sparc_encode_format3bx((ins),2,(src),(disp),38,(dest)) + +#define sparc_sra(ins,src,disp,dest) sparc_encode_format3a((ins),2,0,(src),(disp),39,(dest)) +#define sparc_sra_imm(ins,src,disp,dest) sparc_encode_format3b((ins),2,(src),(disp),39,(dest)) + +/* Sparc V9 */ +#define sparc_srax(ins,src,disp,dest) sparc_encode_format3ax((ins),2,0,(src),(disp),39,(dest)) +#define sparc_srax_imm(ins,src,disp,dest) sparc_encode_format3bx((ins),2,(src),(disp),39,(dest)) + +/* alu */ + +#define sparc_alu_reg(ins,op,setcc,r1,r2,dest) sparc_encode_format3a((ins),2,0,(r1),(r2),op|((setcc) ? 0x10 : 0),(dest)) +#define sparc_alu_imm(ins,op,setcc,r1,imm,dest) sparc_encode_format3b((ins),2,(r1),(imm),op|((setcc) ? 0x10 : 0),(dest)) + +#define sparc_add(ins,setcc,r1,r2,dest) sparc_alu_reg((ins),0,(setcc),(r1),(r2),(dest)) +#define sparc_add_imm(ins,setcc,r1,imm,dest) sparc_alu_imm((ins),0,(setcc),(r1),(imm),(dest)) + +#define sparc_addx(ins,setcc,r1,r2,dest) sparc_alu_reg((ins),0x8,(setcc),(r1),(r2),(dest)) +#define sparc_addx_imm(ins,setcc,r1,imm,dest) sparc_alu_imm((ins),0x8,(setcc),(r1),(imm),(dest)) + +#define sparc_sub(ins,setcc,r1,r2,dest) sparc_alu_reg((ins),0x4,(setcc),(r1),(r2),(dest)) +#define sparc_sub_imm(ins,setcc,r1,imm,dest) sparc_alu_imm((ins),0x4,(setcc),(r1),(imm),(dest)) + +#define sparc_subx(ins,setcc,r1,r2,dest) sparc_alu_reg((ins),0xc,(setcc),(r1),(r2),(dest)) +#define sparc_subx_imm(ins,setcc,r1,imm,dest) sparc_alu_imm((ins),0xc,(setcc),(r1),(imm),(dest)) + +#define sparc_muls(ins,r1,r2,dest) sparc_encode_format3a((ins),2,0,(r1),(r2),36,(dest)) +#define sparc_muls_imm(ins,r1,imm,dest) sparc_encode_format3b((ins),2,(r1),(imm),36,(dest)) + +#define sparc_umul(ins,setcc,r1,r2,dest) sparc_alu_reg((ins),0xa,(setcc),(r1),(r2),(dest)) +#define sparc_umul_imm(ins,setcc,r1,imm,dest) sparc_alu_imm((ins),0xa,(setcc),(r1),(imm),(dest)) + +#define sparc_smul(ins,setcc,r1,r2,dest) sparc_alu_reg((ins),0xb,(setcc),(r1),(r2),(dest)) +#define sparc_smul_imm(ins,setcc,r1,imm,dest) sparc_alu_imm((ins),0xb,(setcc),(r1),(imm),(dest)) + +#define sparc_udiv(ins,setcc,r1,r2,dest) sparc_alu_reg((ins),0xe,(setcc),(r1),(r2),(dest)) +#define sparc_udiv_imm(ins,setcc,r1,imm,dest) sparc_alu_imm((ins),0xe,(setcc),(r1),(imm),(dest)) + +#define sparc_sdiv(ins,setcc,r1,r2,dest) sparc_alu_reg((ins),0xf,(setcc),(r1),(r2),(dest)) +#define sparc_sdiv_imm(ins,setcc,r1,imm,dest) sparc_alu_imm((ins),0xf,(setcc),(r1),(imm),(dest)) + + +/* branch */ +#define sparc_branch(ins,aval,condval,displ) sparc_encode_format2b((ins),(aval),(condval),2,(displ)) +/* FIXME: float condition codes are different: unify. */ +#define sparc_fbranch(ins,aval,condval,displ) sparc_encode_format2b((ins),(aval),(condval),6,(displ)) +#define sparc_branchp(ins,aval,condval,xcc,predict,displ) sparc_encode_format2c((ins),(aval),(condval),0x1,(xcc),(predict),(displ)) + +#define sparc_brz(ins,aval,predict,rs1,disp) sparc_encode_format2d((ins), (aval),0x1,0x3,(predict),(rs1),(disp)) +#define sparc_brlez(ins,aval,predict,rs1,disp) sparc_encode_format2d((ins), (aval),0x2,0x3,(predict),(rs1),(disp)) +#define sparc_brlz(ins,aval,predict,rs1,disp) sparc_encode_format2d((ins), (aval),0x3,0x3,(predict),(rs1),(disp)) +#define sparc_brnz(ins,aval,predict,rs1,disp) sparc_encode_format2d((ins), (aval),0x5,0x3,(predict),(rs1),(disp)) +#define sparc_brgz(ins,aval,predict,rs1,disp) sparc_encode_format2d((ins), (aval),0x6,0x3,(predict),(rs1),(disp)) +#define sparc_brgez(ins,aval,predict,rs1,disp) sparc_encode_format2d((ins), (aval),0x7,0x3,(predict),(rs1),(disp)) + +/* conditional moves */ +#define sparc_movcc(ins,cc,condval,r1,dest) sparc_encode_format4c((ins), 0x2, 0x2c, cc, condval, r1, dest) + +#define sparc_movcc_imm(ins,cc,condval,imm,dest) sparc_encode_format4d((ins), 0x2, 0x2c, cc, condval, imm, dest) + +/* synthetic instructions */ +#define sparc_cmp(ins,r1,r2) sparc_sub((ins),sparc_cc,(r1),(r2),sparc_g0) +#define sparc_cmp_imm(ins,r1,imm) sparc_sub_imm((ins),sparc_cc,(r1),(imm),sparc_g0) +#define sparc_jmp(ins,base,disp) sparc_jmpl((ins),(base),(disp),sparc_g0) +#define sparc_jmp_imm(ins,base,disp) sparc_jmpl_imm((ins),(base),(disp),sparc_g0) +#define sparc_call(ins,base,disp) sparc_jmpl((ins),(base),(disp),sparc_o7) +#define sparc_call_imm(ins,base,disp) sparc_jmpl_imm((ins),(base),(disp),sparc_o7) + +#define sparc_test(ins,reg) sparc_or ((ins),sparc_cc,sparc_g0,(reg),sparc_g0) + +#define sparc_ret(ins) sparc_jmpl_imm((ins),sparc_i7,8,sparc_g0) +#define sparc_retl(ins) sparc_jmpl_imm((ins),sparc_o7,8,sparc_g0) +#define sparc_restore_simple(ins) sparc_restore((ins),sparc_g0,sparc_g0,sparc_g0) +#define sparc_rett_simple(ins) sparc_rett_imm((ins),sparc_i7,8) + +#define sparc_set32(ins,val,reg) \ + do { \ + if ((val) == 0) \ + sparc_clr_reg((ins),(reg)); \ + else if (((guint32)(val) & 0x3ff) == 0) \ + sparc_sethi((ins),(guint32)(val),(reg)); \ + else if (((gint32)(val) >= -4096) && ((gint32)(val) <= 4095)) \ + sparc_or_imm((ins),FALSE,sparc_g0,(gint32)(val),(reg)); \ + else { \ + sparc_sethi((ins),(guint32)(val),(reg)); \ + sparc_or_imm((ins),FALSE,(reg),(guint32)(val)&0x3ff,(reg)); \ + } \ + } while (0) + +#ifdef SPARCV9 +#define SPARC_SET_MAX_SIZE (6 * 4) +#else +#define SPARC_SET_MAX_SIZE (2 * 4) +#endif + +#if SPARCV9 +#define sparc_set(ins,ptr,reg) \ + do { \ + g_assert ((reg) != sparc_g1); \ + gint64 val = (gint64)ptr; \ + guint32 top_word = (val) >> 32; \ + guint32 bottom_word = (val) & 0xffffffff; \ + if (val == 0) \ + sparc_clr_reg ((ins), reg); \ + else if ((val >= -4096) && ((val) <= 4095)) \ + sparc_or_imm((ins),FALSE,sparc_g0,bottom_word,(reg)); \ + else if ((val >= 0) && (val <= 4294967295L)) { \ + sparc_sethi((ins),bottom_word,(reg)); \ + if (bottom_word & 0x3ff) \ + sparc_or_imm((ins),FALSE,(reg),bottom_word&0x3ff,(reg)); \ + } \ + else if ((val >= 0) && (val <= (1L << 44) - 1)) { \ + sparc_sethi ((ins), (val >> 12), (reg)); \ + sparc_or_imm ((ins), FALSE, (reg), (val >> 12) & 0x3ff, (reg)); \ + sparc_sllx_imm ((ins),(reg), 12, (reg)); \ + sparc_or_imm ((ins), FALSE, (reg), (val) & 0xfff, (reg)); \ + } \ + else if (top_word == 0xffffffff) { \ + sparc_xnor ((ins), FALSE, sparc_g0, sparc_g0, sparc_g1); \ + sparc_sethi((ins),bottom_word,(reg)); \ + sparc_sllx_imm((ins),sparc_g1,32,sparc_g1); \ + sparc_or_imm((ins),FALSE,(reg),bottom_word&0x3ff,(reg)); \ + sparc_or((ins),FALSE,(reg),sparc_g1,(reg)); \ + } \ + else { \ + sparc_sethi((ins),top_word,sparc_g1); \ + sparc_sethi((ins),bottom_word,(reg)); \ + sparc_or_imm((ins),FALSE,sparc_g1,top_word&0x3ff,sparc_g1); \ + sparc_or_imm((ins),FALSE,(reg),bottom_word&0x3ff,(reg)); \ + sparc_sllx_imm((ins),sparc_g1,32,sparc_g1); \ + sparc_or((ins),FALSE,(reg),sparc_g1,(reg)); \ + } \ + } while (0) +#else +#define sparc_set(ins,val,reg) \ + do { \ + if ((val) == 0) \ + sparc_clr_reg((ins),(reg)); \ + else if (((guint32)(val) & 0x3ff) == 0) \ + sparc_sethi((ins),(guint32)(val),(reg)); \ + else if (((gint32)(val) >= -4096) && ((gint32)(val) <= 4095)) \ + sparc_or_imm((ins),FALSE,sparc_g0,(gint32)(val),(reg)); \ + else { \ + sparc_sethi((ins),(guint32)(val),(reg)); \ + sparc_or_imm((ins),FALSE,(reg),(guint32)(val)&0x3ff,(reg)); \ + } \ + } while (0) +#endif + +#define sparc_set_ptr(ins,val,reg) sparc_set(ins,val,reg) + +#ifdef SPARCV9 +#define sparc_set_template(ins,reg) sparc_set (ins,0x7fffffff7fffffff, reg) +#else +#define sparc_set_template(ins,reg) sparc_set (ins,0x7fffffff, reg) +#endif + +#define sparc_not(ins,reg) sparc_xnor((ins),FALSE,(reg),sparc_g0,(reg)) +#define sparc_neg(ins,reg) sparc_sub((ins),FALSE,sparc_g0,(reg),(reg)) +#define sparc_clr_reg(ins,reg) sparc_or((ins),FALSE,sparc_g0,sparc_g0,(reg)) + +#define sparc_mov_reg_reg(ins,src,dest) sparc_or((ins),FALSE,sparc_g0,(src),(dest)) + +#ifdef SPARCV9 +#define sparc_sti_imm sparc_stx_imm +#define sparc_ldi_imm sparc_ldx_imm +#define sparc_sti sparc_stx +#define sparc_ldi sparc_ldx +#else +#define sparc_sti_imm sparc_st_imm +#define sparc_ldi_imm sparc_ld_imm +#define sparc_sti sparc_st +#define sparc_ldi sparc_ld +#endif + +#endif /* __SPARC_CODEGEN_H__ */ + diff --git a/unity-2019.4.24f1-mbe/mono/arch/sparc/test.c b/unity-2019.4.24f1-mbe/mono/arch/sparc/test.c new file mode 100644 index 000000000..0d4ad1869 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/sparc/test.c @@ -0,0 +1,123 @@ +#include +#include "sparc-codegen.h" + +/* don't run the resulting program, it will destroy your computer, + * just objdump -d it to inspect we generated the correct assembler. + */ + +int +main () +{ + guint32 *p; + guint32 code_buffer [500]; + guint32 local_size = 0, stack_size = 0, code_size = 6; + guint32 arg_pos, simpletype; + unsigned char *ins; + int i, stringp, cur_out_reg, size; + + p = code_buffer; + + printf (".text\n.align 4\n.globl main\n.type main,@function\nmain:\n"); + + /* + * Standard function prolog. + */ + sparc_save_imm (p, sparc_sp, -112-stack_size, sparc_sp); + cur_out_reg = sparc_o0; + arg_pos = 0; + + if (1) { + sparc_mov_reg_reg (p, sparc_i2, cur_out_reg); + ++cur_out_reg; + } + + sparc_ld_imm (p, sparc_i3, arg_pos, cur_out_reg); + ++cur_out_reg; + sparc_ld_imm (p, sparc_i3, arg_pos+4, cur_out_reg); + ++cur_out_reg; + /* + * Insert call to function + */ + sparc_jmpl (p, sparc_i0, 0, sparc_callsite); + sparc_nop (p); + + sparc_jmpl_imm (p, sparc_i7, 8, sparc_zero); + sparc_restore (p, sparc_zero, sparc_zero, sparc_zero); + + sparc_ldsb (p, sparc_i3, sparc_l0, sparc_o5); + sparc_ldsb_imm (p, sparc_i3, 2, sparc_o5); + + sparc_ldsh (p, sparc_i3, sparc_l0, sparc_o5); + sparc_ldsh_imm (p, sparc_i3, 2, sparc_o5); + + sparc_ldub (p, sparc_i3, sparc_l0, sparc_o5); + sparc_ldub_imm (p, sparc_i3, 2, sparc_o5); + + sparc_lduh (p, sparc_i3, sparc_l0, sparc_o5); + sparc_lduh_imm (p, sparc_i3, 2, sparc_o5); + + sparc_ldf (p, sparc_i3, sparc_l0, sparc_o5); + sparc_ldf_imm (p, sparc_i3, 2, sparc_o5); + + sparc_stb (p, sparc_i3, sparc_l0, sparc_l2); + sparc_stb_imm (p, sparc_i3, sparc_o5, 2); + + sparc_sethi (p, 0xff000000, sparc_o2); + sparc_rdy (p, sparc_l0); + sparc_wry (p, sparc_l0, sparc_l1); + sparc_wry_imm (p, sparc_l0, 16); + sparc_stbar (p); + sparc_unimp (p, 24); + sparc_flush (p, sparc_l4, 0); + + sparc_and (p, sparc_cc, sparc_l0, sparc_l1, sparc_o1); + sparc_and_imm (p, FALSE, sparc_l0, 0xff, sparc_o1); + sparc_andn (p, sparc_cc, sparc_l0, sparc_l1, sparc_o1); + sparc_or (p, sparc_cc, sparc_l0, sparc_l1, sparc_o1); + sparc_orn (p, sparc_cc, sparc_l0, sparc_l1, sparc_o1); + sparc_xor (p, sparc_cc, sparc_l0, sparc_l1, sparc_o1); + sparc_xnor (p, sparc_cc, sparc_l0, sparc_l1, sparc_o1); + + sparc_sll (p, sparc_l0, sparc_l1, sparc_o1); + sparc_sll_imm (p, sparc_l0, 2, sparc_o1); + sparc_srl (p, sparc_l0, sparc_l1, sparc_o1); + sparc_srl_imm (p, sparc_l0, 2, sparc_o1); + sparc_sra (p, sparc_l0, sparc_l1, sparc_o1); + sparc_sra_imm (p, sparc_l0, 2, sparc_o1); + + sparc_add (p, sparc_cc, sparc_l0, sparc_l1, sparc_o1); + sparc_add_imm (p, FALSE, sparc_l0, 0xff, sparc_o1); + sparc_addx (p, sparc_cc, sparc_l0, sparc_l1, sparc_o1); + sparc_sub (p, sparc_cc, sparc_l0, sparc_l1, sparc_o1); + sparc_subx (p, sparc_cc, sparc_l0, sparc_l1, sparc_o1); + + sparc_muls (p, sparc_l0, sparc_l1, sparc_o1); + sparc_umul (p, sparc_cc, sparc_l0, sparc_l1, sparc_o1); + sparc_smul (p, sparc_cc, sparc_l0, sparc_l1, sparc_o1); + sparc_udiv (p, sparc_cc, sparc_l0, sparc_l1, sparc_o1); + sparc_sdiv (p, sparc_cc, sparc_l0, sparc_l1, sparc_o1); + + sparc_branch (p, FALSE, sparc_bne, -12); + sparc_ret (p); + sparc_retl (p); + sparc_test (p, sparc_l4); + sparc_cmp (p, sparc_l4, sparc_l6); + sparc_cmp_imm (p, sparc_l4, 4); + sparc_restore_simple (p); + + sparc_set (p, 0xff000000, sparc_l7); + sparc_set (p, 1, sparc_l7); + sparc_set (p, 0xff0000ff, sparc_l7); + + sparc_not (p, sparc_g2); + sparc_neg (p, sparc_g3); + sparc_clr_reg (p, sparc_g4); + + + size = (p-code_buffer)*4; + ins = (gchar*)code_buffer; + for (i = 0; i < size; ++i) + printf (".byte %d\n", (unsigned int) ins [i]); + return 0; +} + diff --git a/unity-2019.4.24f1-mbe/mono/arch/x86/.gitignore b/unity-2019.4.24f1-mbe/mono/arch/x86/.gitignore new file mode 100644 index 000000000..341daec37 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/x86/.gitignore @@ -0,0 +1,6 @@ +/Makefile +/Makefile.in +/.libs +/.deps +/*.la +/*.lo diff --git a/unity-2019.4.24f1-mbe/mono/arch/x86/Makefile.am b/unity-2019.4.24f1-mbe/mono/arch/x86/Makefile.am new file mode 100644 index 000000000..bab0f9e54 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/x86/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = x86-codegen.h \ No newline at end of file diff --git a/unity-2019.4.24f1-mbe/mono/arch/x86/x86-codegen.h b/unity-2019.4.24f1-mbe/mono/arch/x86/x86-codegen.h new file mode 100644 index 000000000..183d5fe99 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/arch/x86/x86-codegen.h @@ -0,0 +1,2391 @@ +/* + * x86-codegen.h: Macros for generating x86 code + * + * Authors: + * Paolo Molaro (lupus@ximian.com) + * Intel Corporation (ORP Project) + * Sergey Chaban (serge@wildwestsoftware.com) + * Dietmar Maurer (dietmar@ximian.com) + * Patrik Torstensson + * + * Copyright (C) 2000 Intel Corporation. All rights reserved. + * Copyright (C) 2001, 2002 Ximian, Inc. + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#ifndef X86_H +#define X86_H +#include + +#define x86_codegen_pre(inst_ptr_ptr, inst_len) do {} while (0) +/* Two variants are needed to avoid warnings */ +#define x86_call_sequence_pre_val(inst) guint8* _code_start = (inst); +#define x86_call_sequence_post_val(inst) _code_start +#define x86_call_sequence_pre(inst) +#define x86_call_sequence_post(inst) + +/* +// x86 register numbers +*/ +typedef enum { + X86_EAX = 0, + X86_ECX = 1, + X86_EDX = 2, + X86_EBX = 3, + X86_ESP = 4, + X86_EBP = 5, + X86_ESI = 6, + X86_EDI = 7, + X86_NREG +} X86_Reg_No; + +typedef enum { + X86_XMM0, + X86_XMM1, + X86_XMM2, + X86_XMM3, + X86_XMM4, + X86_XMM5, + X86_XMM6, + X86_XMM7, + X86_XMM_NREG +} X86_XMM_Reg_No; + +/* +// opcodes for alu instructions +*/ +typedef enum { + X86_ADD = 0, + X86_OR = 1, + X86_ADC = 2, + X86_SBB = 3, + X86_AND = 4, + X86_SUB = 5, + X86_XOR = 6, + X86_CMP = 7, + X86_NALU +} X86_ALU_Opcode; +/* +// opcodes for shift instructions +*/ +typedef enum { + X86_SHLD, + X86_SHLR, + X86_ROL = 0, + X86_ROR = 1, + X86_RCL = 2, + X86_RCR = 3, + X86_SHL = 4, + X86_SHR = 5, + X86_SAR = 7, + X86_NSHIFT = 8 +} X86_Shift_Opcode; +/* +// opcodes for floating-point instructions +*/ +typedef enum { + X86_FADD = 0, + X86_FMUL = 1, + X86_FCOM = 2, + X86_FCOMP = 3, + X86_FSUB = 4, + X86_FSUBR = 5, + X86_FDIV = 6, + X86_FDIVR = 7, + X86_NFP = 8 +} X86_FP_Opcode; +/* +// integer conditions codes +*/ +typedef enum { + X86_CC_EQ = 0, X86_CC_E = 0, X86_CC_Z = 0, + X86_CC_NE = 1, X86_CC_NZ = 1, + X86_CC_LT = 2, X86_CC_B = 2, X86_CC_C = 2, X86_CC_NAE = 2, + X86_CC_LE = 3, X86_CC_BE = 3, X86_CC_NA = 3, + X86_CC_GT = 4, X86_CC_A = 4, X86_CC_NBE = 4, + X86_CC_GE = 5, X86_CC_AE = 5, X86_CC_NB = 5, X86_CC_NC = 5, + X86_CC_LZ = 6, X86_CC_S = 6, + X86_CC_GEZ = 7, X86_CC_NS = 7, + X86_CC_P = 8, X86_CC_PE = 8, + X86_CC_NP = 9, X86_CC_PO = 9, + X86_CC_O = 10, + X86_CC_NO = 11, + X86_NCC +} X86_CC; + +/* FP status */ +enum { + X86_FP_C0 = 0x100, + X86_FP_C1 = 0x200, + X86_FP_C2 = 0x400, + X86_FP_C3 = 0x4000, + X86_FP_CC_MASK = 0x4500 +}; + +/* FP control word */ +enum { + X86_FPCW_INVOPEX_MASK = 0x1, + X86_FPCW_DENOPEX_MASK = 0x2, + X86_FPCW_ZERODIV_MASK = 0x4, + X86_FPCW_OVFEX_MASK = 0x8, + X86_FPCW_UNDFEX_MASK = 0x10, + X86_FPCW_PRECEX_MASK = 0x20, + X86_FPCW_PRECC_MASK = 0x300, + X86_FPCW_ROUNDC_MASK = 0xc00, + + /* values for precision control */ + X86_FPCW_PREC_SINGLE = 0, + X86_FPCW_PREC_DOUBLE = 0x200, + X86_FPCW_PREC_EXTENDED = 0x300, + + /* values for rounding control */ + X86_FPCW_ROUND_NEAREST = 0, + X86_FPCW_ROUND_DOWN = 0x400, + X86_FPCW_ROUND_UP = 0x800, + X86_FPCW_ROUND_TOZERO = 0xc00 +}; + +/* +// prefix code +*/ +typedef enum { + X86_LOCK_PREFIX = 0xF0, + X86_REPNZ_PREFIX = 0xF2, + X86_REPZ_PREFIX = 0xF3, + X86_REP_PREFIX = 0xF3, + X86_CS_PREFIX = 0x2E, + X86_SS_PREFIX = 0x36, + X86_DS_PREFIX = 0x3E, + X86_ES_PREFIX = 0x26, + X86_FS_PREFIX = 0x64, + X86_GS_PREFIX = 0x65, + X86_UNLIKELY_PREFIX = 0x2E, + X86_LIKELY_PREFIX = 0x3E, + X86_OPERAND_PREFIX = 0x66, + X86_ADDRESS_PREFIX = 0x67 +} X86_Prefix; + +static const unsigned char +x86_cc_unsigned_map [X86_NCC] = { + 0x74, /* eq */ + 0x75, /* ne */ + 0x72, /* lt */ + 0x76, /* le */ + 0x77, /* gt */ + 0x73, /* ge */ + 0x78, /* lz */ + 0x79, /* gez */ + 0x7a, /* p */ + 0x7b, /* np */ + 0x70, /* o */ + 0x71, /* no */ +}; + +static const unsigned char +x86_cc_signed_map [X86_NCC] = { + 0x74, /* eq */ + 0x75, /* ne */ + 0x7c, /* lt */ + 0x7e, /* le */ + 0x7f, /* gt */ + 0x7d, /* ge */ + 0x78, /* lz */ + 0x79, /* gez */ + 0x7a, /* p */ + 0x7b, /* np */ + 0x70, /* o */ + 0x71, /* no */ +}; + +typedef union { + int val; + unsigned char b [4]; +} x86_imm_buf; + +#define X86_NOBASEREG (-1) + +/* +// bitvector mask for callee-saved registers +*/ +#define X86_ESI_MASK (1<> 6) +#define x86_modrm_reg(modrm) (((modrm) >> 3) & 0x7) +#define x86_modrm_rm(modrm) ((modrm) & 0x7) + +#define x86_address_byte(inst,m,o,r) do { *(inst)++ = ((((m)&0x03)<<6)|(((o)&0x07)<<3)|(((r)&0x07))); } while (0) +#define x86_imm_emit32(inst,imm) \ + do { \ + x86_imm_buf imb; imb.val = (int) (imm); \ + *(inst)++ = imb.b [0]; \ + *(inst)++ = imb.b [1]; \ + *(inst)++ = imb.b [2]; \ + *(inst)++ = imb.b [3]; \ + } while (0) +#define x86_imm_emit16(inst,imm) do { *(short*)(inst) = (imm); (inst) += 2; } while (0) +#define x86_imm_emit8(inst,imm) do { *(inst) = (unsigned char)((imm) & 0xff); ++(inst); } while (0) +#define x86_is_imm8(imm) (((int)(imm) >= -128 && (int)(imm) <= 127)) +#define x86_is_imm16(imm) (((int)(imm) >= -(1<<16) && (int)(imm) <= ((1<<16)-1))) + +#define x86_reg_emit(inst,r,regno) do { x86_address_byte ((inst), 3, (r), (regno)); } while (0) +#define x86_reg8_emit(inst,r,regno,is_rh,is_rnoh) do {x86_address_byte ((inst), 3, (is_rh)?((r)|4):(r), (is_rnoh)?((regno)|4):(regno));} while (0) +#define x86_regp_emit(inst,r,regno) do { x86_address_byte ((inst), 0, (r), (regno)); } while (0) +#define x86_mem_emit(inst,r,disp) do { x86_address_byte ((inst), 0, (r), 5); x86_imm_emit32((inst), (disp)); } while (0) + +#define kMaxMembaseEmitPadding 6 + +#define x86_membase_emit_body(inst,r,basereg,disp) do {\ + if ((basereg) == X86_ESP) { \ + if ((disp) == 0) { \ + x86_address_byte ((inst), 0, (r), X86_ESP); \ + x86_address_byte ((inst), 0, X86_ESP, X86_ESP); \ + } else if (x86_is_imm8((disp))) { \ + x86_address_byte ((inst), 1, (r), X86_ESP); \ + x86_address_byte ((inst), 0, X86_ESP, X86_ESP); \ + x86_imm_emit8 ((inst), (disp)); \ + } else { \ + x86_address_byte ((inst), 2, (r), X86_ESP); \ + x86_address_byte ((inst), 0, X86_ESP, X86_ESP); \ + x86_imm_emit32 ((inst), (disp)); \ + } \ + break; \ + } \ + if ((disp) == 0 && (basereg) != X86_EBP) { \ + x86_address_byte ((inst), 0, (r), (basereg)); \ + break; \ + } \ + if (x86_is_imm8((disp))) { \ + x86_address_byte ((inst), 1, (r), (basereg)); \ + x86_imm_emit8 ((inst), (disp)); \ + } else { \ + x86_address_byte ((inst), 2, (r), (basereg)); \ + x86_imm_emit32 ((inst), (disp)); \ + } \ + } while (0) + +#define x86_membase_emit(inst,r,basereg,disp) \ + do { \ + x86_membase_emit_body((inst),(r),(basereg),(disp)); \ + } while (0) + +#define kMaxMemindexEmitPadding 6 + +#define x86_memindex_emit(inst,r,basereg,disp,indexreg,shift) \ + do { \ + if ((basereg) == X86_NOBASEREG) { \ + x86_address_byte ((inst), 0, (r), 4); \ + x86_address_byte ((inst), (shift), (indexreg), 5); \ + x86_imm_emit32 ((inst), (disp)); \ + } else if ((disp) == 0 && (basereg) != X86_EBP) { \ + x86_address_byte ((inst), 0, (r), 4); \ + x86_address_byte ((inst), (shift), (indexreg), (basereg)); \ + } else if (x86_is_imm8((disp))) { \ + x86_address_byte ((inst), 1, (r), 4); \ + x86_address_byte ((inst), (shift), (indexreg), (basereg)); \ + x86_imm_emit8 ((inst), (disp)); \ + } else { \ + x86_address_byte ((inst), 2, (r), 4); \ + x86_address_byte ((inst), (shift), (indexreg), (basereg)); \ + x86_imm_emit32 ((inst), (disp)); \ + } \ + } while (0) + +/* + * target is the position in the code where to jump to: + * target = code; + * .. output loop code... + * x86_mov_reg_imm (code, X86_EAX, 0); + * loop = code; + * x86_loop (code, -1); + * ... finish method + * + * patch displacement + * x86_patch (loop, target); + * + * ins should point at the start of the instruction that encodes a target. + * the instruction is inspected for validity and the correct displacement + * is inserted. + */ +#define x86_do_patch(ins,target) \ + do { \ + unsigned char* pos = (ins) + 1; \ + int disp, size = 0; \ + switch (*(unsigned char*)(ins)) { \ + case 0xe8: case 0xe9: ++size; break; /* call, jump32 */ \ + case 0x0f: if (!(*pos >= 0x70 && *pos <= 0x8f)) assert (0); \ + ++size; ++pos; break; /* prefix for 32-bit disp */ \ + case 0xe0: case 0xe1: case 0xe2: /* loop */ \ + case 0xeb: /* jump8 */ \ + /* conditional jump opcodes */ \ + case 0x70: case 0x71: case 0x72: case 0x73: \ + case 0x74: case 0x75: case 0x76: case 0x77: \ + case 0x78: case 0x79: case 0x7a: case 0x7b: \ + case 0x7c: case 0x7d: case 0x7e: case 0x7f: \ + break; \ + default: assert (0); \ + } \ + disp = (target) - pos; \ + if (size) x86_imm_emit32 (pos, disp - 4); \ + else if (x86_is_imm8 (disp - 1)) x86_imm_emit8 (pos, disp - 1); \ + else assert (0); \ + } while (0) + +#define x86_patch(ins,target) do { x86_do_patch((ins), (target)); } while (0) + +#define x86_breakpoint(inst) \ + do { \ + *(inst)++ = 0xcc; \ + } while (0) + +#define x86_cld(inst) do { *(inst)++ =(unsigned char)0xfc; } while (0) +#define x86_stosb(inst) do { *(inst)++ =(unsigned char)0xaa; } while (0) +#define x86_stosl(inst) do { *(inst)++ =(unsigned char)0xab; } while (0) +#define x86_stosd(inst) x86_stosl((inst)) +#define x86_movsb(inst) do { *(inst)++ =(unsigned char)0xa4; } while (0) +#define x86_movsl(inst) do { *(inst)++ =(unsigned char)0xa5; } while (0) +#define x86_movsd(inst) x86_movsl((inst)) + +#define x86_prefix(inst,p) \ + do { \ + *(inst)++ =(unsigned char) (p); \ + } while (0) + +#define x86_mfence(inst) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = 0x0f; \ + *(inst)++ = 0xae; \ + *(inst)++ = 0xf0; \ + } while (0) + +#define x86_rdtsc(inst) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = 0x0f; \ + *(inst)++ = 0x31; \ + } while (0) + +#define x86_cmpxchg_reg_reg(inst,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xb1; \ + x86_reg_emit ((inst), (reg), (dreg)); \ + } while (0) + +#define x86_cmpxchg_mem_reg(inst,mem,reg) \ + do { \ + x86_codegen_pre(&(inst), 7); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xb1; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_cmpxchg_membase_reg(inst,basereg,disp,reg) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xb1; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_xchg_reg_reg(inst,dreg,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + if ((size) == 1) \ + *(inst)++ = (unsigned char)0x86; \ + else \ + *(inst)++ = (unsigned char)0x87; \ + x86_reg_emit ((inst), (reg), (dreg)); \ + } while (0) + +#define x86_xchg_mem_reg(inst,mem,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + if ((size) == 1) \ + *(inst)++ = (unsigned char)0x86; \ + else \ + *(inst)++ = (unsigned char)0x87; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_xchg_membase_reg(inst,basereg,disp,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + if ((size) == 1) \ + *(inst)++ = (unsigned char)0x86; \ + else \ + *(inst)++ = (unsigned char)0x87; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_xadd_reg_reg(inst,dreg,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x0F; \ + if ((size) == 1) \ + *(inst)++ = (unsigned char)0xC0; \ + else \ + *(inst)++ = (unsigned char)0xC1; \ + x86_reg_emit ((inst), (reg), (dreg)); \ + } while (0) + +#define x86_xadd_mem_reg(inst,mem,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 7); \ + *(inst)++ = (unsigned char)0x0F; \ + if ((size) == 1) \ + *(inst)++ = (unsigned char)0xC0; \ + else \ + *(inst)++ = (unsigned char)0xC1; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_xadd_membase_reg(inst,basereg,disp,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0F; \ + if ((size) == 1) \ + *(inst)++ = (unsigned char)0xC0; \ + else \ + *(inst)++ = (unsigned char)0xC1; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_inc_mem(inst,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xff; \ + x86_mem_emit ((inst), 0, (mem)); \ + } while (0) + +#define x86_inc_membase(inst,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xff; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + } while (0) + +#define x86_inc_reg(inst,reg) do { *(inst)++ = (unsigned char)0x40 + (reg); } while (0) + +#define x86_dec_mem(inst,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xff; \ + x86_mem_emit ((inst), 1, (mem)); \ + } while (0) + +#define x86_dec_membase(inst,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xff; \ + x86_membase_emit ((inst), 1, (basereg), (disp)); \ + } while (0) + +#define x86_dec_reg(inst,reg) do { *(inst)++ = (unsigned char)0x48 + (reg); } while (0) + +#define x86_not_mem(inst,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_mem_emit ((inst), 2, (mem)); \ + } while (0) + +#define x86_not_membase(inst,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_membase_emit ((inst), 2, (basereg), (disp)); \ + } while (0) + +#define x86_not_reg(inst,reg) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_reg_emit ((inst), 2, (reg)); \ + } while (0) + +#define x86_neg_mem(inst,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_mem_emit ((inst), 3, (mem)); \ + } while (0) + +#define x86_neg_membase(inst,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_membase_emit ((inst), 3, (basereg), (disp)); \ + } while (0) + +#define x86_neg_reg(inst,reg) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_reg_emit ((inst), 3, (reg)); \ + } while (0) + +#define x86_nop(inst) do { *(inst)++ = (unsigned char)0x90; } while (0) + +#define x86_alu_reg_imm(inst,opc,reg,imm) \ + do { \ + if ((reg) == X86_EAX) { \ + x86_codegen_pre(&(inst), 5); \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 5; \ + x86_imm_emit32 ((inst), (imm)); \ + break; \ + } \ + if (x86_is_imm8((imm))) { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x83; \ + x86_reg_emit ((inst), (opc), (reg)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0x81; \ + x86_reg_emit ((inst), (opc), (reg)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_alu_mem_imm(inst,opc,mem,imm) \ + do { \ + if (x86_is_imm8((imm))) { \ + x86_codegen_pre(&(inst), 7); \ + *(inst)++ = (unsigned char)0x83; \ + x86_mem_emit ((inst), (opc), (mem)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else { \ + x86_codegen_pre(&(inst), 10); \ + *(inst)++ = (unsigned char)0x81; \ + x86_mem_emit ((inst), (opc), (mem)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_alu_membase_imm(inst,opc,basereg,disp,imm) \ + do { \ + if (x86_is_imm8((imm))) { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x83; \ + x86_membase_emit ((inst), (opc), (basereg), (disp)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else { \ + x86_codegen_pre(&(inst), 5 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x81; \ + x86_membase_emit ((inst), (opc), (basereg), (disp)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_alu_membase8_imm(inst,opc,basereg,disp,imm) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x80; \ + x86_membase_emit ((inst), (opc), (basereg), (disp)); \ + x86_imm_emit8 ((inst), (imm)); \ + } while (0) + +#define x86_alu_mem_reg(inst,opc,mem,reg) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 1; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_alu_membase_reg(inst,opc,basereg,disp,reg) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 1; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_alu_reg_reg(inst,opc,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + } while (0) + +/** + * @x86_alu_reg8_reg8: + * Supports ALU operations between two 8-bit registers. + * dreg := dreg opc reg + * X86_Reg_No enum is used to specify the registers. + * Additionally is_*_h flags are used to specify what part + * of a given 32-bit register is used - high (TRUE) or low (FALSE). + * For example: dreg = X86_EAX, is_dreg_h = TRUE -> use AH + */ +#define x86_alu_reg8_reg8(inst,opc,dreg,reg,is_dreg_h,is_reg_h) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 2; \ + x86_reg8_emit ((inst), (dreg), (reg), (is_dreg_h), (is_reg_h)); \ + } while (0) + +#define x86_alu_reg_mem(inst,opc,reg,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_alu_reg_membase(inst,opc,reg,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_test_reg_imm(inst,reg,imm) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + if ((reg) == X86_EAX) { \ + *(inst)++ = (unsigned char)0xa9; \ + } else { \ + *(inst)++ = (unsigned char)0xf7; \ + x86_reg_emit ((inst), 0, (reg)); \ + } \ + x86_imm_emit32 ((inst), (imm)); \ + } while (0) + +#define x86_test_mem_imm8(inst,mem,imm) \ + do { \ + x86_codegen_pre(&(inst), 7); \ + *(inst)++ = (unsigned char)0xf6; \ + x86_mem_emit ((inst), 0, (mem)); \ + x86_imm_emit8 ((inst), (imm)); \ + } while (0) + +#define x86_test_mem_imm(inst,mem,imm) \ + do { \ + x86_codegen_pre(&(inst), 10); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_mem_emit ((inst), 0, (mem)); \ + x86_imm_emit32 ((inst), (imm)); \ + } while (0) + +#define x86_test_membase_imm(inst,basereg,disp,imm) \ + do { \ + x86_codegen_pre(&(inst), 5 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + x86_imm_emit32 ((inst), (imm)); \ + } while (0) + +#define x86_test_reg_reg(inst,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0x85; \ + x86_reg_emit ((inst), (reg), (dreg)); \ + } while (0) + +#define x86_test_mem_reg(inst,mem,reg) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0x85; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_test_membase_reg(inst,basereg,disp,reg) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x85; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_shift_reg_imm(inst,opc,reg,imm) \ + do { \ + if ((imm) == 1) { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xd1; \ + x86_reg_emit ((inst), (opc), (reg)); \ + } else { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0xc1; \ + x86_reg_emit ((inst), (opc), (reg)); \ + x86_imm_emit8 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_shift_mem_imm(inst,opc,mem,imm) \ + do { \ + if ((imm) == 1) { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xd1; \ + x86_mem_emit ((inst), (opc), (mem)); \ + } else { \ + x86_codegen_pre(&(inst), 7); \ + *(inst)++ = (unsigned char)0xc1; \ + x86_mem_emit ((inst), (opc), (mem)); \ + x86_imm_emit8 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_shift_membase_imm(inst,opc,basereg,disp,imm) \ + do { \ + if ((imm) == 1) { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xd1; \ + x86_membase_emit ((inst), (opc), (basereg), (disp)); \ + } else { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xc1; \ + x86_membase_emit ((inst), (opc), (basereg), (disp)); \ + x86_imm_emit8 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_shift_reg(inst,opc,reg) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xd3; \ + x86_reg_emit ((inst), (opc), (reg)); \ + } while (0) + +#define x86_shift_mem(inst,opc,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xd3; \ + x86_mem_emit ((inst), (opc), (mem)); \ + } while (0) + +#define x86_shift_membase(inst,opc,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xd3; \ + x86_membase_emit ((inst), (opc), (basereg), (disp)); \ + } while (0) + +/* + * Multi op shift missing. + */ + +#define x86_shrd_reg(inst,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xad; \ + x86_reg_emit ((inst), (reg), (dreg)); \ + } while (0) + +#define x86_shrd_reg_imm(inst,dreg,reg,shamt) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xac; \ + x86_reg_emit ((inst), (reg), (dreg)); \ + x86_imm_emit8 ((inst), (shamt)); \ + } while (0) + +#define x86_shld_reg(inst,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xa5; \ + x86_reg_emit ((inst), (reg), (dreg)); \ + } while (0) + +#define x86_shld_reg_imm(inst,dreg,reg,shamt) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xa4; \ + x86_reg_emit ((inst), (reg), (dreg)); \ + x86_imm_emit8 ((inst), (shamt)); \ + } while (0) + +/* + * EDX:EAX = EAX * rm + */ +#define x86_mul_reg(inst,reg,is_signed) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_reg_emit ((inst), 4 + ((is_signed) ? 1 : 0), (reg)); \ + } while (0) + +#define x86_mul_mem(inst,mem,is_signed) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_mem_emit ((inst), 4 + ((is_signed) ? 1 : 0), (mem)); \ + } while (0) + +#define x86_mul_membase(inst,basereg,disp,is_signed) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_membase_emit ((inst), 4 + ((is_signed) ? 1 : 0), (basereg), (disp)); \ + } while (0) + +/* + * r *= rm + */ +#define x86_imul_reg_reg(inst,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xaf; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + } while (0) + +#define x86_imul_reg_mem(inst,reg,mem) \ + do { \ + x86_codegen_pre(&(inst), 7); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xaf; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_imul_reg_membase(inst,reg,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xaf; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +/* + * dreg = rm * imm + */ +#define x86_imul_reg_reg_imm(inst,dreg,reg,imm) \ + do { \ + if (x86_is_imm8 ((imm))) { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x6b; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0x69; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_imul_reg_mem_imm(inst,reg,mem,imm) \ + do { \ + if (x86_is_imm8 ((imm))) { \ + x86_codegen_pre(&(inst), 7); \ + *(inst)++ = (unsigned char)0x6b; \ + x86_mem_emit ((inst), (reg), (mem)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0x69; \ + x86_mem_emit ((inst), (reg), (mem)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_imul_reg_membase_imm(inst,reg,basereg,disp,imm) \ + do { \ + if (x86_is_imm8 ((imm))) { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x6b; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else { \ + x86_codegen_pre(&(inst), 5 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x69; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +/* + * divide EDX:EAX by rm; + * eax = quotient, edx = remainder + */ + +#define x86_div_reg(inst,reg,is_signed) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_reg_emit ((inst), 6 + ((is_signed) ? 1 : 0), (reg)); \ + } while (0) + +#define x86_div_mem(inst,mem,is_signed) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_mem_emit ((inst), 6 + ((is_signed) ? 1 : 0), (mem)); \ + } while (0) + +#define x86_div_membase(inst,basereg,disp,is_signed) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_membase_emit ((inst), 6 + ((is_signed) ? 1 : 0), (basereg), (disp)); \ + } while (0) + +#define x86_mov_mem_reg(inst,mem,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 7); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x88; break; \ + case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ + case 4: *(inst)++ = (unsigned char)0x89; break; \ + default: assert (0); \ + } \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_mov_regp_reg(inst,regp,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x88; break; \ + case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ + case 4: *(inst)++ = (unsigned char)0x89; break; \ + default: assert (0); \ + } \ + x86_regp_emit ((inst), (reg), (regp)); \ + } while (0) + +#define x86_mov_membase_reg(inst,basereg,disp,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x88; break; \ + case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ + case 4: *(inst)++ = (unsigned char)0x89; break; \ + default: assert (0); \ + } \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_mov_memindex_reg(inst,basereg,disp,indexreg,shift,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMemindexEmitPadding); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x88; break; \ + case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ + case 4: *(inst)++ = (unsigned char)0x89; break; \ + default: assert (0); \ + } \ + x86_memindex_emit ((inst), (reg), (basereg), (disp), (indexreg), (shift)); \ + } while (0) + +#define x86_mov_reg_reg(inst,dreg,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x8a; break; \ + case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ + case 4: *(inst)++ = (unsigned char)0x8b; break; \ + default: assert (0); \ + } \ + x86_reg_emit ((inst), (dreg), (reg)); \ + } while (0) + +#define x86_mov_reg_mem(inst,reg,mem,size) \ + do { \ + x86_codegen_pre(&(inst), 7); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x8a; break; \ + case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ + case 4: *(inst)++ = (unsigned char)0x8b; break; \ + default: assert (0); \ + } \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define kMovRegMembasePadding (2 + kMaxMembaseEmitPadding) + +#define x86_mov_reg_membase(inst,reg,basereg,disp,size) \ + do { \ + x86_codegen_pre(&(inst), kMovRegMembasePadding); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x8a; break; \ + case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ + case 4: *(inst)++ = (unsigned char)0x8b; break; \ + default: assert (0); \ + } \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_mov_reg_memindex(inst,reg,basereg,disp,indexreg,shift,size) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMemindexEmitPadding); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x8a; break; \ + case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ + case 4: *(inst)++ = (unsigned char)0x8b; break; \ + default: assert (0); \ + } \ + x86_memindex_emit ((inst), (reg), (basereg), (disp), (indexreg), (shift)); \ + } while (0) + +/* + * Note: x86_clear_reg () chacnges the condition code! + */ +#define x86_clear_reg(inst,reg) x86_alu_reg_reg((inst), X86_XOR, (reg), (reg)) + +#define x86_mov_reg_imm(inst,reg,imm) \ + do { \ + x86_codegen_pre(&(inst), 5); \ + *(inst)++ = (unsigned char)0xb8 + (reg); \ + x86_imm_emit32 ((inst), (imm)); \ + } while (0) + +#define x86_mov_mem_imm(inst,mem,imm,size) \ + do { \ + if ((size) == 1) { \ + x86_codegen_pre(&(inst), 7); \ + *(inst)++ = (unsigned char)0xc6; \ + x86_mem_emit ((inst), 0, (mem)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else if ((size) == 2) { \ + x86_codegen_pre(&(inst), 9); \ + x86_prefix((inst), X86_OPERAND_PREFIX); \ + *(inst)++ = (unsigned char)0xc7; \ + x86_mem_emit ((inst), 0, (mem)); \ + x86_imm_emit16 ((inst), (imm)); \ + } else { \ + x86_codegen_pre(&(inst), 10); \ + *(inst)++ = (unsigned char)0xc7; \ + x86_mem_emit ((inst), 0, (mem)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_mov_membase_imm(inst,basereg,disp,imm,size) \ + do { \ + if ((size) == 1) { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xc6; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else if ((size) == 2) { \ + x86_codegen_pre(&(inst), 4 + kMaxMembaseEmitPadding); \ + x86_prefix((inst), X86_OPERAND_PREFIX); \ + *(inst)++ = (unsigned char)0xc7; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + x86_imm_emit16 ((inst), (imm)); \ + } else { \ + x86_codegen_pre(&(inst), 5 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xc7; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_mov_memindex_imm(inst,basereg,disp,indexreg,shift,imm,size) \ + do { \ + if ((size) == 1) { \ + x86_codegen_pre(&(inst), 2 + kMaxMemindexEmitPadding); \ + *(inst)++ = (unsigned char)0xc6; \ + x86_memindex_emit ((inst), 0, (basereg), (disp), (indexreg), (shift)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else if ((size) == 2) { \ + x86_codegen_pre(&(inst), 4 + kMaxMemindexEmitPadding); \ + x86_prefix((inst), X86_OPERAND_PREFIX); \ + *(inst)++ = (unsigned char)0xc7; \ + x86_memindex_emit ((inst), 0, (basereg), (disp), (indexreg), (shift)); \ + x86_imm_emit16 ((inst), (imm)); \ + } else { \ + x86_codegen_pre(&(inst), 5 + kMaxMemindexEmitPadding); \ + *(inst)++ = (unsigned char)0xc7; \ + x86_memindex_emit ((inst), 0, (basereg), (disp), (indexreg), (shift)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_lea_mem(inst,reg,mem) \ + do { \ + x86_codegen_pre(&(inst), 5); \ + *(inst)++ = (unsigned char)0x8d; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_lea_membase(inst,reg,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x8d; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_lea_memindex(inst,reg,basereg,disp,indexreg,shift) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMemindexEmitPadding); \ + *(inst)++ = (unsigned char)0x8d; \ + x86_memindex_emit ((inst), (reg), (basereg), (disp), (indexreg), (shift)); \ + } while (0) + +#define x86_widen_reg(inst,dreg,reg,is_signed,is_half) \ + do { \ + unsigned char op = 0xb6; \ + g_assert (is_half || X86_IS_BYTE_REG (reg)); \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) op += 0x08; \ + if ((is_half)) op += 0x01; \ + *(inst)++ = op; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + } while (0) + +#define x86_widen_mem(inst,dreg,mem,is_signed,is_half) \ + do { \ + unsigned char op = 0xb6; \ + x86_codegen_pre(&(inst), 7); \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) op += 0x08; \ + if ((is_half)) op += 0x01; \ + *(inst)++ = op; \ + x86_mem_emit ((inst), (dreg), (mem)); \ + } while (0) + +#define x86_widen_membase(inst,dreg,basereg,disp,is_signed,is_half) \ + do { \ + unsigned char op = 0xb6; \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) op += 0x08; \ + if ((is_half)) op += 0x01; \ + *(inst)++ = op; \ + x86_membase_emit ((inst), (dreg), (basereg), (disp)); \ + } while (0) + +#define x86_widen_memindex(inst,dreg,basereg,disp,indexreg,shift,is_signed,is_half) \ + do { \ + unsigned char op = 0xb6; \ + x86_codegen_pre(&(inst), 2 + kMaxMemindexEmitPadding); \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) op += 0x08; \ + if ((is_half)) op += 0x01; \ + *(inst)++ = op; \ + x86_memindex_emit ((inst), (dreg), (basereg), (disp), (indexreg), (shift)); \ + } while (0) + +#define x86_cdq(inst) do { *(inst)++ = (unsigned char)0x99; } while (0) +#define x86_wait(inst) do { *(inst)++ = (unsigned char)0x9b; } while (0) + +#define x86_fp_op_mem(inst,opc,mem,is_double) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (is_double) ? (unsigned char)0xdc : (unsigned char)0xd8; \ + x86_mem_emit ((inst), (opc), (mem)); \ + } while (0) + +#define x86_fp_op_membase(inst,opc,basereg,disp,is_double) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (is_double) ? (unsigned char)0xdc : (unsigned char)0xd8; \ + x86_membase_emit ((inst), (opc), (basereg), (disp)); \ + } while (0) + +#define x86_fp_op(inst,opc,index) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xd8; \ + *(inst)++ = (unsigned char)0xc0+((opc)<<3)+((index)&0x07); \ + } while (0) + +#define x86_fp_op_reg(inst,opc,index,pop_stack) \ + do { \ + static const unsigned char map[] = { 0, 1, 2, 3, 5, 4, 7, 6, 8}; \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (pop_stack) ? (unsigned char)0xde : (unsigned char)0xdc; \ + *(inst)++ = (unsigned char)0xc0+(map[(opc)]<<3)+((index)&0x07); \ + } while (0) + +/** + * @x86_fp_int_op_membase + * Supports FPU operations between ST(0) and integer operand in memory. + * Operation encoded using X86_FP_Opcode enum. + * Operand is addressed by [basereg + disp]. + * is_int specifies whether operand is int32 (TRUE) or int16 (FALSE). + */ +#define x86_fp_int_op_membase(inst,opc,basereg,disp,is_int) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (is_int) ? (unsigned char)0xda : (unsigned char)0xde; \ + x86_membase_emit ((inst), opc, (basereg), (disp)); \ + } while (0) + +#define x86_fstp(inst,index) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xdd; \ + *(inst)++ = (unsigned char)0xd8+(index); \ + } while (0) + +#define x86_fcompp(inst) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xde; \ + *(inst)++ = (unsigned char)0xd9; \ + } while (0) + +#define x86_fucompp(inst) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xda; \ + *(inst)++ = (unsigned char)0xe9; \ + } while (0) + +#define x86_fnstsw(inst) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xdf; \ + *(inst)++ = (unsigned char)0xe0; \ + } while (0) + +#define x86_fnstcw(inst,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xd9; \ + x86_mem_emit ((inst), 7, (mem)); \ + } while (0) + +#define x86_fnstcw_membase(inst,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xd9; \ + x86_membase_emit ((inst), 7, (basereg), (disp)); \ + } while (0) + +#define x86_fldcw(inst,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xd9; \ + x86_mem_emit ((inst), 5, (mem)); \ + } while (0) + +#define x86_fldcw_membase(inst,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xd9; \ + x86_membase_emit ((inst), 5, (basereg), (disp)); \ + } while (0) + +#define x86_fchs(inst) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xd9; \ + *(inst)++ = (unsigned char)0xe0; \ + } while (0) + +#define x86_frem(inst) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xd9; \ + *(inst)++ = (unsigned char)0xf8; \ + } while (0) + +#define x86_fxch(inst,index) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xd9; \ + *(inst)++ = (unsigned char)0xc8 + ((index) & 0x07); \ + } while (0) + +#define x86_fcomi(inst,index) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xdb; \ + *(inst)++ = (unsigned char)0xf0 + ((index) & 0x07); \ + } while (0) + +#define x86_fcomip(inst,index) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xdf; \ + *(inst)++ = (unsigned char)0xf0 + ((index) & 0x07); \ + } while (0) + +#define x86_fucomi(inst,index) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xdb; \ + *(inst)++ = (unsigned char)0xe8 + ((index) & 0x07); \ + } while (0) + +#define x86_fucomip(inst,index) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xdf; \ + *(inst)++ = (unsigned char)0xe8 + ((index) & 0x07); \ + } while (0) + +#define x86_fld(inst,mem,is_double) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (is_double) ? (unsigned char)0xdd : (unsigned char)0xd9; \ + x86_mem_emit ((inst), 0, (mem)); \ + } while (0) + +#define x86_fld_membase(inst,basereg,disp,is_double) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (is_double) ? (unsigned char)0xdd : (unsigned char)0xd9; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + } while (0) + +#define x86_fld80_mem(inst,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xdb; \ + x86_mem_emit ((inst), 5, (mem)); \ + } while (0) + +#define x86_fld80_membase(inst,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xdb; \ + x86_membase_emit ((inst), 5, (basereg), (disp)); \ + } while (0) + +#define x86_fild(inst,mem,is_long) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + if ((is_long)) { \ + *(inst)++ = (unsigned char)0xdf; \ + x86_mem_emit ((inst), 5, (mem)); \ + } else { \ + *(inst)++ = (unsigned char)0xdb; \ + x86_mem_emit ((inst), 0, (mem)); \ + } \ + } while (0) + +#define x86_fild_membase(inst,basereg,disp,is_long) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + if ((is_long)) { \ + *(inst)++ = (unsigned char)0xdf; \ + x86_membase_emit ((inst), 5, (basereg), (disp)); \ + } else { \ + *(inst)++ = (unsigned char)0xdb; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + } \ + } while (0) + +#define x86_fld_reg(inst,index) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xd9; \ + *(inst)++ = (unsigned char)0xc0 + ((index) & 0x07); \ + } while (0) + +#define x86_fldz(inst) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xd9; \ + *(inst)++ = (unsigned char)0xee; \ + } while (0) + +#define x86_fld1(inst) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xd9; \ + *(inst)++ = (unsigned char)0xe8; \ + } while (0) + +#define x86_fldpi(inst) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xd9; \ + *(inst)++ = (unsigned char)0xeb; \ + } while (0) + +#define x86_fst(inst,mem,is_double,pop_stack) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (is_double) ? (unsigned char)0xdd: (unsigned char)0xd9; \ + x86_mem_emit ((inst), 2 + ((pop_stack) ? 1 : 0), (mem)); \ + } while (0) + +#define x86_fst_membase(inst,basereg,disp,is_double,pop_stack) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (is_double) ? (unsigned char)0xdd: (unsigned char)0xd9; \ + x86_membase_emit ((inst), 2 + ((pop_stack) ? 1 : 0), (basereg), (disp)); \ + } while (0) + +#define x86_fst80_mem(inst,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xdb; \ + x86_mem_emit ((inst), 7, (mem)); \ + } while (0) + + +#define x86_fst80_membase(inst,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xdb; \ + x86_membase_emit ((inst), 7, (basereg), (disp)); \ + } while (0) + + +#define x86_fist_pop(inst,mem,is_long) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + if ((is_long)) { \ + *(inst)++ = (unsigned char)0xdf; \ + x86_mem_emit ((inst), 7, (mem)); \ + } else { \ + *(inst)++ = (unsigned char)0xdb; \ + x86_mem_emit ((inst), 3, (mem)); \ + } \ + } while (0) + +#define x86_fist_pop_membase(inst,basereg,disp,is_long) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + if ((is_long)) { \ + *(inst)++ = (unsigned char)0xdf; \ + x86_membase_emit ((inst), 7, (basereg), (disp)); \ + } else { \ + *(inst)++ = (unsigned char)0xdb; \ + x86_membase_emit ((inst), 3, (basereg), (disp)); \ + } \ + } while (0) + +#define x86_fstsw(inst) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x9b; \ + *(inst)++ = (unsigned char)0xdf; \ + *(inst)++ = (unsigned char)0xe0; \ + } while (0) + +/** + * @x86_fist_membase + * Converts content of ST(0) to integer and stores it at memory location + * addressed by [basereg + disp]. + * is_int specifies whether destination is int32 (TRUE) or int16 (FALSE). + */ +#define x86_fist_membase(inst,basereg,disp,is_int) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + if ((is_int)) { \ + *(inst)++ = (unsigned char)0xdb; \ + x86_membase_emit ((inst), 2, (basereg), (disp)); \ + } else { \ + *(inst)++ = (unsigned char)0xdf; \ + x86_membase_emit ((inst), 2, (basereg), (disp)); \ + } \ + } while (0) + + +#define x86_push_reg(inst,reg) \ + do { \ + *(inst)++ = (unsigned char)0x50 + (reg); \ + } while (0) + +#define x86_push_regp(inst,reg) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xff; \ + x86_regp_emit ((inst), 6, (reg)); \ + } while (0) + +#define x86_push_mem(inst,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xff; \ + x86_mem_emit ((inst), 6, (mem)); \ + } while (0) + +#define x86_push_membase(inst,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xff; \ + x86_membase_emit ((inst), 6, (basereg), (disp)); \ + } while (0) + +#define x86_push_memindex(inst,basereg,disp,indexreg,shift) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMemindexEmitPadding); \ + *(inst)++ = (unsigned char)0xff; \ + x86_memindex_emit ((inst), 6, (basereg), (disp), (indexreg), (shift)); \ + } while (0) + +#define x86_push_imm_template(inst) x86_push_imm (inst, 0xf0f0f0f0) + +#define x86_push_imm(inst,imm) \ + do { \ + int _imm = (int) (imm); \ + if (x86_is_imm8 (_imm)) { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0x6A; \ + x86_imm_emit8 ((inst), (_imm)); \ + } else { \ + x86_codegen_pre(&(inst), 5); \ + *(inst)++ = (unsigned char)0x68; \ + x86_imm_emit32 ((inst), (_imm)); \ + } \ + } while (0) + +#define x86_pop_reg(inst,reg) \ + do { \ + *(inst)++ = (unsigned char)0x58 + (reg); \ + } while (0) + +#define x86_pop_mem(inst,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0x87; \ + x86_mem_emit ((inst), 0, (mem)); \ + } while (0) + +#define x86_pop_membase(inst,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x87; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + } while (0) + +#define x86_pushad(inst) do { *(inst)++ = (unsigned char)0x60; } while (0) +#define x86_pushfd(inst) do { *(inst)++ = (unsigned char)0x9c; } while (0) +#define x86_popad(inst) do { *(inst)++ = (unsigned char)0x61; } while (0) +#define x86_popfd(inst) do { *(inst)++ = (unsigned char)0x9d; } while (0) + +#define x86_loop(inst,imm) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xe2; \ + x86_imm_emit8 ((inst), (imm)); \ + } while (0) + +#define x86_loope(inst,imm) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xe1; \ + x86_imm_emit8 ((inst), (imm)); \ + } while (0) + +#define x86_loopne(inst,imm) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xe0; \ + x86_imm_emit8 ((inst), (imm)); \ + } while (0) + +#if defined(TARGET_X86) +#define x86_jump32(inst,imm) \ + do { \ + x86_codegen_pre(&(inst), 5); \ + *(inst)++ = (unsigned char)0xe9; \ + x86_imm_emit32 ((inst), (imm)); \ + } while (0) + +#define x86_jump8(inst,imm) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xeb; \ + x86_imm_emit8 ((inst), (imm)); \ + } while (0) +#elif defined(TARGET_AMD64) +/* These macros are used directly from mini-amd64.c and other */ +/* amd64 specific files, so they need to be instrumented directly. */ +#define x86_jump32(inst,imm) \ + do { \ + amd64_codegen_pre(inst); \ + *(inst)++ = (unsigned char)0xe9; \ + x86_imm_emit32 ((inst), (imm)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define x86_jump8(inst,imm) \ + do { \ + amd64_codegen_pre(inst); \ + *(inst)++ = (unsigned char)0xeb; \ + x86_imm_emit8 ((inst), (imm)); \ + amd64_codegen_post(inst); \ + } while (0) +#endif + +#define x86_jump_reg(inst,reg) \ + do { \ + *(inst)++ = (unsigned char)0xff; \ + x86_reg_emit ((inst), 4, (reg)); \ + } while (0) + +#define x86_jump_mem(inst,mem) \ + do { \ + *(inst)++ = (unsigned char)0xff; \ + x86_mem_emit ((inst), 4, (mem)); \ + } while (0) + +#define x86_jump_membase(inst,basereg,disp) \ + do { \ + *(inst)++ = (unsigned char)0xff; \ + x86_membase_emit ((inst), 4, (basereg), (disp)); \ + } while (0) +/* + * target is a pointer in our buffer. + */ +#define x86_jump_code_body(inst,target) \ + do { \ + int t; \ + x86_codegen_pre(&(inst), 2); \ + t = (unsigned char*)(target) - (inst) - 2; \ + if (x86_is_imm8(t)) { \ + x86_jump8 ((inst), t); \ + } else { \ + x86_codegen_pre(&(inst), 5); \ + t = (unsigned char*)(target) - (inst) - 5; \ + x86_jump32 ((inst), t); \ + } \ + } while (0) + +#define x86_jump_code(inst,target) \ + do { \ + x86_jump_code_body((inst),(target)); \ + } while (0) + +#define x86_jump_disp(inst,disp) \ + do { \ + int t = (disp) - 2; \ + if (x86_is_imm8(t)) { \ + x86_jump8 ((inst), t); \ + } else { \ + t -= 3; \ + x86_jump32 ((inst), t); \ + } \ + } while (0) + +#if defined(TARGET_X86) +#define x86_branch8(inst,cond,imm,is_signed) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)]; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)]; \ + x86_imm_emit8 ((inst), (imm)); \ + } while (0) + +#define x86_branch32(inst,cond,imm,is_signed) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)] + 0x10; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x10; \ + x86_imm_emit32 ((inst), (imm)); \ + } while (0) +#elif defined(TARGET_AMD64) +/* These macros are used directly from mini-amd64.c and other */ +/* amd64 specific files, so they need to be instrumented directly. */ +#define x86_branch8(inst,cond,imm,is_signed) \ + do { \ + amd64_codegen_pre(inst); \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)]; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)]; \ + x86_imm_emit8 ((inst), (imm)); \ + amd64_codegen_post(inst); \ + } while (0) +#define x86_branch32(inst,cond,imm,is_signed) \ + do { \ + amd64_codegen_pre(inst); \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)] + 0x10; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x10; \ + x86_imm_emit32 ((inst), (imm)); \ + amd64_codegen_post(inst); \ + } while (0) +#endif + +#if defined(TARGET_X86) +#define x86_branch(inst,cond,target,is_signed) \ + do { \ + int offset; \ + guint8* branch_start; \ + x86_codegen_pre(&(inst), 2); \ + offset = (target) - (inst) - 2; \ + branch_start = (inst); \ + if (x86_is_imm8 ((offset))) \ + x86_branch8 ((inst), (cond), offset, (is_signed)); \ + else { \ + x86_codegen_pre(&(inst), 6); \ + offset = (target) - (inst) - 6; \ + x86_branch32 ((inst), (cond), offset, (is_signed)); \ + } \ + x86_patch(branch_start, (target)); \ + } while (0) +#elif defined(TARGET_AMD64) +/* This macro is used directly from mini-amd64.c and other */ +/* amd64 specific files, so it needs to be instrumented directly. */ + +#define x86_branch_body(inst,cond,target,is_signed) \ + do { \ + int offset = (target) - (inst) - 2; \ + if (x86_is_imm8 ((offset))) \ + x86_branch8 ((inst), (cond), offset, (is_signed)); \ + else { \ + offset = (target) - (inst) - 6; \ + x86_branch32 ((inst), (cond), offset, (is_signed)); \ + } \ + } while (0) + +#define x86_branch(inst,cond,target,is_signed) \ + do { \ + x86_branch_body((inst),(cond),(target),(is_signed)); \ + } while (0) + +#endif /* TARGET_AMD64 */ + +#define x86_branch_disp(inst,cond,disp,is_signed) \ + do { \ + int offset = (disp) - 2; \ + if (x86_is_imm8 ((offset))) \ + x86_branch8 ((inst), (cond), offset, (is_signed)); \ + else { \ + offset -= 4; \ + x86_branch32 ((inst), (cond), offset, (is_signed)); \ + } \ + } while (0) + +#define x86_set_reg(inst,cond,reg,is_signed) \ + do { \ + g_assert (X86_IS_BYTE_REG (reg)); \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)] + 0x20; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x20; \ + x86_reg_emit ((inst), 0, (reg)); \ + } while (0) + +#define x86_set_mem(inst,cond,mem,is_signed) \ + do { \ + x86_codegen_pre(&(inst), 7); \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)] + 0x20; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x20; \ + x86_mem_emit ((inst), 0, (mem)); \ + } while (0) + +#define x86_set_membase(inst,cond,basereg,disp,is_signed) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)] + 0x20; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x20; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + } while (0) + +#define x86_call_imm_body(inst,disp) \ + do { \ + *(inst)++ = (unsigned char)0xe8; \ + x86_imm_emit32 ((inst), (int)(disp)); \ + } while (0) + +#define x86_call_imm(inst,disp) \ + do { \ + x86_call_sequence_pre((inst)); \ + x86_call_imm_body((inst), (disp)); \ + x86_call_sequence_post((inst)); \ + } while (0) + + +#define x86_call_reg(inst,reg) \ + do { \ + *(inst)++ = (unsigned char)0xff; \ + x86_reg_emit ((inst), 2, (reg)); \ + } while (0) + +#define x86_call_mem(inst,mem) \ + do { \ + *(inst)++ = (unsigned char)0xff; \ + x86_mem_emit ((inst), 2, (mem)); \ + } while (0) + +#define x86_call_membase(inst,basereg,disp) \ + do { \ + *(inst)++ = (unsigned char)0xff; \ + x86_membase_emit ((inst), 2, (basereg), (disp)); \ + } while (0) + +#define x86_call_code(inst,target) \ + do { \ + int _x86_offset; \ + _x86_offset = (unsigned char*)(target) - (inst); \ + _x86_offset -= 5; \ + x86_call_imm_body ((inst), _x86_offset); \ + } while (0) + +#define x86_ret(inst) do { *(inst)++ = (unsigned char)0xc3; } while (0) + +#define x86_ret_imm(inst,imm) \ + do { \ + if ((imm) == 0) { \ + x86_ret ((inst)); \ + } else { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0xc2; \ + x86_imm_emit16 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_cmov_reg(inst,cond,is_signed,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char) 0x0f; \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)] - 0x30; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)] - 0x30; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + } while (0) + +#define x86_cmov_mem(inst,cond,is_signed,reg,mem) \ + do { \ + x86_codegen_pre(&(inst), 7); \ + *(inst)++ = (unsigned char) 0x0f; \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)] - 0x30; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)] - 0x30; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_cmov_membase(inst,cond,is_signed,reg,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char) 0x0f; \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)] - 0x30; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)] - 0x30; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_enter(inst,framesize) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + *(inst)++ = (unsigned char)0xc8; \ + x86_imm_emit16 ((inst), (framesize)); \ + *(inst)++ = 0; \ + } while (0) + +#define x86_leave(inst) do { *(inst)++ = (unsigned char)0xc9; } while (0) +#define x86_sahf(inst) do { *(inst)++ = (unsigned char)0x9e; } while (0) + +#define x86_fsin(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xfe; } while (0) +#define x86_fcos(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xff; } while (0) +#define x86_fabs(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xe1; } while (0) +#define x86_ftst(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xe4; } while (0) +#define x86_fxam(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xe5; } while (0) +#define x86_fpatan(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xf3; } while (0) +#define x86_fprem(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xf8; } while (0) +#define x86_fprem1(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xf5; } while (0) +#define x86_frndint(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xfc; } while (0) +#define x86_fsqrt(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xfa; } while (0) +#define x86_fptan(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xf2; } while (0) + +#define x86_padding(inst,size) \ + do { \ + switch ((size)) { \ + case 1: x86_nop ((inst)); break; \ + case 2: *(inst)++ = 0x8b; \ + *(inst)++ = 0xc0; break; \ + case 3: *(inst)++ = 0x8d; *(inst)++ = 0x6d; \ + *(inst)++ = 0x00; break; \ + case 4: *(inst)++ = 0x8d; *(inst)++ = 0x64; \ + *(inst)++ = 0x24; *(inst)++ = 0x00; \ + break; \ + case 5: *(inst)++ = 0x8d; *(inst)++ = 0x64; \ + *(inst)++ = 0x24; *(inst)++ = 0x00; \ + x86_nop ((inst)); break; \ + case 6: *(inst)++ = 0x8d; *(inst)++ = 0xad; \ + *(inst)++ = 0x00; *(inst)++ = 0x00; \ + *(inst)++ = 0x00; *(inst)++ = 0x00; \ + break; \ + case 7: *(inst)++ = 0x8d; *(inst)++ = 0xa4; \ + *(inst)++ = 0x24; *(inst)++ = 0x00; \ + *(inst)++ = 0x00; *(inst)++ = 0x00; \ + *(inst)++ = 0x00; break; \ + default: assert (0); \ + } \ + } while (0) + +#define x86_prolog(inst,frame_size,reg_mask) \ + do { \ + unsigned i, m = 1; \ + x86_enter ((inst), (frame_size)); \ + for (i = 0; i < X86_NREG; ++i, m <<= 1) { \ + if ((reg_mask) & m) \ + x86_push_reg ((inst), i); \ + } \ + } while (0) + +#define x86_epilog(inst,reg_mask) \ + do { \ + unsigned i, m = 1 << X86_EDI; \ + for (i = X86_EDI; m != 0; i--, m=m>>1) { \ + if ((reg_mask) & m) \ + x86_pop_reg ((inst), i); \ + } \ + x86_leave ((inst)); \ + x86_ret ((inst)); \ + } while (0) + + +typedef enum { + X86_SSE_SQRT = 0x51, + X86_SSE_RSQRT = 0x52, + X86_SSE_RCP = 0x53, + X86_SSE_ADD = 0x58, + X86_SSE_DIV = 0x5E, + X86_SSE_MUL = 0x59, + X86_SSE_SUB = 0x5C, + X86_SSE_MIN = 0x5D, + X86_SSE_MAX = 0x5F, + X86_SSE_COMP = 0xC2, + X86_SSE_AND = 0x54, + X86_SSE_ANDN = 0x55, + X86_SSE_OR = 0x56, + X86_SSE_XOR = 0x57, + X86_SSE_UNPCKL = 0x14, + X86_SSE_UNPCKH = 0x15, + + X86_SSE_ADDSUB = 0xD0, + X86_SSE_HADD = 0x7C, + X86_SSE_HSUB = 0x7D, + X86_SSE_MOVSHDUP = 0x16, + X86_SSE_MOVSLDUP = 0x12, + X86_SSE_MOVDDUP = 0x12, + + X86_SSE_PAND = 0xDB, + X86_SSE_POR = 0xEB, + X86_SSE_PXOR = 0xEF, + + X86_SSE_PADDB = 0xFC, + X86_SSE_PADDW = 0xFD, + X86_SSE_PADDD = 0xFE, + X86_SSE_PADDQ = 0xD4, + + X86_SSE_PSUBB = 0xF8, + X86_SSE_PSUBW = 0xF9, + X86_SSE_PSUBD = 0xFA, + X86_SSE_PSUBQ = 0xFB, + + X86_SSE_PMAXSB = 0x3C, /*sse41*/ + X86_SSE_PMAXSW = 0xEE, + X86_SSE_PMAXSD = 0x3D, /*sse41*/ + + X86_SSE_PMAXUB = 0xDE, + X86_SSE_PMAXUW = 0x3E, /*sse41*/ + X86_SSE_PMAXUD = 0x3F, /*sse41*/ + + X86_SSE_PMINSB = 0x38, /*sse41*/ + X86_SSE_PMINSW = 0xEA, + X86_SSE_PMINSD = 0x39,/*sse41*/ + + X86_SSE_PMINUB = 0xDA, + X86_SSE_PMINUW = 0x3A, /*sse41*/ + X86_SSE_PMINUD = 0x3B, /*sse41*/ + + X86_SSE_PAVGB = 0xE0, + X86_SSE_PAVGW = 0xE3, + + X86_SSE_PCMPEQB = 0x74, + X86_SSE_PCMPEQW = 0x75, + X86_SSE_PCMPEQD = 0x76, + X86_SSE_PCMPEQQ = 0x29, /*sse41*/ + + X86_SSE_PCMPGTB = 0x64, + X86_SSE_PCMPGTW = 0x65, + X86_SSE_PCMPGTD = 0x66, + X86_SSE_PCMPGTQ = 0x37, /*sse42*/ + + X86_SSE_PSADBW = 0xf6, + + X86_SSE_PSHUFD = 0x70, + + X86_SSE_PUNPCKLBW = 0x60, + X86_SSE_PUNPCKLWD = 0x61, + X86_SSE_PUNPCKLDQ = 0x62, + X86_SSE_PUNPCKLQDQ = 0x6C, + + X86_SSE_PUNPCKHBW = 0x68, + X86_SSE_PUNPCKHWD = 0x69, + X86_SSE_PUNPCKHDQ = 0x6A, + X86_SSE_PUNPCKHQDQ = 0x6D, + + X86_SSE_PACKSSWB = 0x63, + X86_SSE_PACKSSDW = 0x6B, + + X86_SSE_PACKUSWB = 0x67, + X86_SSE_PACKUSDW = 0x2B,/*sse41*/ + + X86_SSE_PADDUSB = 0xDC, + X86_SSE_PADDUSW = 0xDD, + X86_SSE_PSUBUSB = 0xD8, + X86_SSE_PSUBUSW = 0xD9, + + X86_SSE_PADDSB = 0xEC, + X86_SSE_PADDSW = 0xED, + X86_SSE_PSUBSB = 0xE8, + X86_SSE_PSUBSW = 0xE9, + + X86_SSE_PMULLW = 0xD5, + X86_SSE_PMULLD = 0x40,/*sse41*/ + X86_SSE_PMULHUW = 0xE4, + X86_SSE_PMULHW = 0xE5, + X86_SSE_PMULUDQ = 0xF4, + + X86_SSE_PMOVMSKB = 0xD7, + + X86_SSE_PSHIFTW = 0x71, + X86_SSE_PSHIFTD = 0x72, + X86_SSE_PSHIFTQ = 0x73, + X86_SSE_SHR = 2, + X86_SSE_SAR = 4, + X86_SSE_SHL = 6, + + X86_SSE_PSRLW_REG = 0xD1, + X86_SSE_PSRAW_REG = 0xE1, + X86_SSE_PSLLW_REG = 0xF1, + + X86_SSE_PSRLD_REG = 0xD2, + X86_SSE_PSRAD_REG = 0xE2, + X86_SSE_PSLLD_REG = 0xF2, + + X86_SSE_PSRLQ_REG = 0xD3, + X86_SSE_PSLLQ_REG = 0xF3, + + X86_SSE_PREFETCH = 0x18, + X86_SSE_MOVNTPS = 0x2B, + X86_SSE_MOVHPD_REG_MEMBASE = 0x16, + X86_SSE_MOVHPD_MEMBASE_REG = 0x17, + + X86_SSE_MOVSD_REG_MEMBASE = 0x10, + X86_SSE_MOVSD_MEMBASE_REG = 0x11, + + X86_SSE_PINSRB = 0x20,/*sse41*/ + X86_SSE_PINSRW = 0xC4, + X86_SSE_PINSRD = 0x22,/*sse41*/ + + X86_SSE_PEXTRB = 0x14,/*sse41*/ + X86_SSE_PEXTRW = 0xC5, + X86_SSE_PEXTRD = 0x16,/*sse41*/ + + X86_SSE_SHUFP = 0xC6, + + X86_SSE_CVTDQ2PD = 0xE6, + X86_SSE_CVTDQ2PS = 0x5B, + X86_SSE_CVTPD2DQ = 0xE6, + X86_SSE_CVTPD2PS = 0x5A, + X86_SSE_CVTPS2DQ = 0x5B, + X86_SSE_CVTPS2PD = 0x5A, + X86_SSE_CVTTPD2DQ = 0xE6, + X86_SSE_CVTTPS2DQ = 0x5B, +} X86_SSE_Opcode; + + +/* minimal SSE* support */ +#define x86_movsd_reg_membase(inst,dreg,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 3 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xf2; \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x10; \ + x86_membase_emit ((inst), (dreg), (basereg), (disp)); \ + } while (0) + +#define x86_cvttsd2si(inst,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + *(inst)++ = (unsigned char)0xf2; \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x2c; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + } while (0) + +#define x86_sse_alu_reg_reg(inst,opc,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x0F; \ + *(inst)++ = (unsigned char)(opc); \ + x86_reg_emit ((inst), (dreg), (reg)); \ + } while (0) + +#define x86_sse_alu_reg_membase(inst,opc,sreg,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)(opc); \ + x86_membase_emit ((inst), (sreg), (basereg), (disp)); \ + } while (0) + +#define x86_sse_alu_membase_reg(inst,opc,basereg,disp,reg) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0F; \ + *(inst)++ = (unsigned char)(opc); \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_sse_alu_reg_reg_imm8(inst,opc,dreg,reg, imm8) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + *(inst)++ = (unsigned char)0x0F; \ + *(inst)++ = (unsigned char)(opc); \ + x86_reg_emit ((inst), (dreg), (reg)); \ + *(inst)++ = (unsigned char)(imm8); \ + } while (0) + +#define x86_sse_alu_pd_reg_reg_imm8(inst,opc,dreg,reg, imm8) \ + do { \ + x86_codegen_pre(&(inst), 5); \ + *(inst)++ = (unsigned char)0x66; \ + x86_sse_alu_reg_reg_imm8 ((inst), (opc), (dreg), (reg), (imm8)); \ + } while (0) + +#define x86_sse_alu_pd_reg_reg(inst,opc,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + *(inst)++ = (unsigned char)0x66; \ + x86_sse_alu_reg_reg ((inst), (opc), (dreg), (reg)); \ + } while (0) + +#define x86_sse_alu_pd_membase_reg(inst,opc,basereg,disp,reg) \ + do { \ + x86_codegen_pre(&(inst), 3 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x66; \ + x86_sse_alu_membase_reg ((inst), (opc), (basereg), (disp), (reg)); \ + } while (0) + +#define x86_sse_alu_pd_reg_membase(inst,opc,dreg,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 3 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x66; \ + x86_sse_alu_reg_membase ((inst), (opc), (dreg),(basereg), (disp)); \ + } while (0) + +#define x86_sse_alu_pd_reg_reg_imm(inst,opc,dreg,reg,imm) \ + do { \ + x86_codegen_pre(&(inst), 5); \ + x86_sse_alu_pd_reg_reg ((inst), (opc), (dreg), (reg)); \ + *(inst)++ = (unsigned char)(imm); \ + } while (0) + +#define x86_sse_alu_pd_reg_membase_imm(inst,opc,dreg,basereg,disp,imm) \ + do { \ + x86_codegen_pre(&(inst), 4 + kMaxMembaseEmitPadding); \ + x86_sse_alu_pd_reg_membase ((inst), (opc), (dreg),(basereg), (disp)); \ + *(inst)++ = (unsigned char)(imm); \ + } while (0) + + +#define x86_sse_alu_ps_reg_reg(inst,opc,dreg,reg) \ + do { \ + x86_sse_alu_reg_reg ((inst), (opc), (dreg), (reg)); \ + } while (0) + +#define x86_sse_alu_ps_reg_reg_imm(inst,opc,dreg,reg, imm) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + x86_sse_alu_reg_reg ((inst), (opc), (dreg), (reg)); \ + *(inst)++ = (unsigned char)imm; \ + } while (0) + + +#define x86_sse_alu_sd_reg_reg(inst,opc,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + *(inst)++ = (unsigned char)0xF2; \ + x86_sse_alu_reg_reg ((inst), (opc), (dreg), (reg)); \ + } while (0) + +#define x86_sse_alu_sd_membase_reg(inst,opc,basereg,disp,reg) \ + do { \ + x86_codegen_pre(&(inst), 3 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xF2; \ + x86_sse_alu_membase_reg ((inst), (opc), (basereg), (disp), (reg)); \ + } while (0) + + +#define x86_sse_alu_ss_reg_reg(inst,opc,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + *(inst)++ = (unsigned char)0xF3; \ + x86_sse_alu_reg_reg ((inst), (opc), (dreg), (reg)); \ + } while (0) + +#define x86_sse_alu_ss_membase_reg(inst,opc,basereg,disp,reg) \ + do { \ + x86_codegen_pre(&(inst), 3 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xF3; \ + x86_sse_alu_membase_reg ((inst), (opc), (basereg), (disp), (reg)); \ + } while (0) + + + +#define x86_sse_alu_sse41_reg_reg(inst,opc,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 5); \ + *(inst)++ = (unsigned char)0x66; \ + *(inst)++ = (unsigned char)0x0F; \ + *(inst)++ = (unsigned char)0x38; \ + *(inst)++ = (unsigned char)(opc); \ + x86_reg_emit ((inst), (dreg), (reg)); \ + } while (0) + +#define x86_movups_reg_membase(inst,sreg,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x10; \ + x86_membase_emit ((inst), (sreg), (basereg), (disp)); \ + } while (0) + +#define x86_movups_membase_reg(inst,basereg,disp,reg) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x11; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_movaps_reg_membase(inst,sreg,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x28; \ + x86_membase_emit ((inst), (sreg), (basereg), (disp)); \ + } while (0) + +#define x86_movaps_membase_reg(inst,basereg,disp,reg) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x29; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_movaps_reg_reg(inst,dreg,sreg) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x28; \ + x86_reg_emit ((inst), (dreg), (sreg)); \ + } while (0) + + +#define x86_movd_reg_xreg(inst,dreg,sreg) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + *(inst)++ = (unsigned char)0x66; \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x7e; \ + x86_reg_emit ((inst), (sreg), (dreg)); \ + } while (0) + +#define x86_movd_xreg_reg(inst,dreg,sreg) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + *(inst)++ = (unsigned char)0x66; \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x6e; \ + x86_reg_emit ((inst), (dreg), (sreg)); \ + } while (0) + +#define x86_movd_xreg_membase(inst,sreg,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 3 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x66; \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x6e; \ + x86_membase_emit ((inst), (sreg), (basereg), (disp)); \ + } while (0) + +#define x86_pshufw_reg_reg(inst,dreg,sreg,mask,high_words) \ + do { \ + x86_codegen_pre(&(inst), 5); \ + *(inst)++ = (unsigned char)(high_words) ? 0xF3 : 0xF2; \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x70; \ + x86_reg_emit ((inst), (dreg), (sreg)); \ + *(inst)++ = (unsigned char)mask; \ + } while (0) + +#define x86_sse_shift_reg_imm(inst,opc,mode, dreg,imm) \ + do { \ + x86_codegen_pre(&(inst), 5); \ + x86_sse_alu_pd_reg_reg (inst, opc, mode, dreg); \ + x86_imm_emit8 ((inst), (imm)); \ + } while (0) + +#define x86_sse_shift_reg_reg(inst,opc,dreg,sreg) \ + do { \ + x86_sse_alu_pd_reg_reg (inst, opc, dreg, sreg); \ + } while (0) + + + +#endif // X86_H + diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/.gitattributes b/unity-2019.4.24f1-mbe/mono/benchmark/.gitattributes new file mode 100644 index 000000000..7e84f6b8d --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/.gitattributes @@ -0,0 +1,3 @@ +/bulkcpy.il -crlf +/life.cs -crlf +/zipmark.cs -crlf diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/.gitignore b/unity-2019.4.24f1-mbe/mono/benchmark/.gitignore new file mode 100644 index 000000000..68abb274b --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/.gitignore @@ -0,0 +1,5 @@ +/Makefile.in +/Makefile +/*.stdout +/.deps +/.libs diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/Makefile.am b/unity-2019.4.24f1-mbe/mono/benchmark/Makefile.am new file mode 100644 index 000000000..62354f931 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/Makefile.am @@ -0,0 +1,72 @@ +TEST_PROG=../mini/mono +RUNTIME_ARGS="-O=all" + +CSC=mcs + + +TESTSRC= \ + fib.cs \ + life.cs \ + castclass.cs \ + cmov1.cs \ + cmov2.cs \ + cmov3.cs \ + cmov4.cs \ + cmov5.cs \ + commute.cs \ + isinst.cs \ + sbperf1.cs \ + sbperf2.cs \ + iconst-byte.cs \ + inline1.cs \ + inline2.cs \ + inline3.cs \ + inline4.cs \ + inline5.cs \ + inline6.cs \ + inline-readonly.cs \ + max-min.cs \ + muldiv.cs \ + loops.cs \ + initlocals.cs \ + logic.cs \ + switch.cs \ + ctor-bench.cs \ + readonly.cs \ + readonly-byte-array.cs \ + readonly-inst.cs \ + readonly-vt.cs \ + regalloc.cs \ + regalloc-2.cs \ + bulkcpy.il \ + math.cs \ + boxtest.cs \ + valuetype-hash-equals.cs \ + vt2.cs + +TESTSI_TMP=$(TESTSRC:.cs=.exe) +TESTSI=$(TESTSI_TMP:.il=.exe) + +EXTRA_DIST=test-driver $(TESTSRC) + +%.exe: %.il + ilasm $< /OUTPUT=$@ + +%.exe: %.cs + $(CSC) $< + +test: $(TEST_PROG) $(TESTSI) + @failed=0; \ + passed=0; \ + for i in $(TESTSI); do \ + if ./test-driver $(TEST_PROG) $$i $(RUNTIME_ARGS); \ + then \ + passed=`expr $${passed} + 1`; \ + else \ + failed=`expr $${failed} + 1`; \ + fi \ + done; \ + echo "$${passed} test(s) passed. $${failed} test(s) failed." + +check: + @echo no check yet diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/boxtest.cs b/unity-2019.4.24f1-mbe/mono/benchmark/boxtest.cs new file mode 100644 index 000000000..cde46ca3c --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/boxtest.cs @@ -0,0 +1,19 @@ +using System; + + +public class Test { + + public static int Main (string[] args) { + object x = null; + + for (int i = 0 ; i < 5000000; i++) { + x = i; + } + + int j = (int)x; + + return 0; + } +} + + diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/bulkcpy.il b/unity-2019.4.24f1-mbe/mono/benchmark/bulkcpy.il new file mode 100644 index 000000000..a8a9f2cfa --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/bulkcpy.il @@ -0,0 +1,680 @@ +.assembly BulkCpy {} + +.class public auto ansi sealed beforefieldinit BulkCpy { + + .field public static unsigned int8 Data at RandomData + + .method static public void Main() il managed { + .entrypoint + + .locals ( + int32 count, + int32 start, + int32 elapsed, + unsigned int8[] buff1, + unsigned int8[] buff2, + unsigned int8& pinned dest1, + unsigned int8& pinned dest2, + int32 size + ) + + ldc.i4 2053 + stloc size + + ldloc size + newarr [mscorlib]System.Byte + dup + stloc buff1 + ldc.i4.0 + ldelema [mscorlib]System.Byte + stloc dest1 + + ldloc size + newarr [mscorlib]System.Byte + dup + stloc buff2 + ldc.i4.0 + ldelema [mscorlib]System.Byte + stloc dest2 + + + ldc.i4 1000000 + stloc count + + call int32 [mscorlib]System.Environment::get_TickCount() + stloc start + + loop: + ldloc dest1 + ldsflda unsigned int8 BulkCpy::Data + ldc.i4 2053 + cpblk + + ldloc dest1 + ldsflda unsigned int8 BulkCpy::Data + ldc.i4 0 + cpblk + + ldloc dest2 + ldsflda unsigned int8 BulkCpy::Data + ldc.i4 1 + cpblk + + ldloc dest1 + ldsflda unsigned int8 BulkCpy::Data + ldc.i4 2 + cpblk + + ldloc dest2 + ldsflda unsigned int8 BulkCpy::Data + ldc.i4 3 + cpblk + + ldloc dest1 + ldsflda unsigned int8 BulkCpy::Data + ldc.i4 4 + cpblk + + ldloc dest2 + ldsflda unsigned int8 BulkCpy::Data + ldc.i4 5 + cpblk + + ldloc dest1 + ldsflda unsigned int8 BulkCpy::Data + ldc.i4 6 + cpblk + + ldloc dest2 + ldsflda unsigned int8 BulkCpy::Data + ldc.i4 7 + cpblk + + ldloc dest1 + ldsflda unsigned int8 BulkCpy::Data + ldc.i4 8 + cpblk + + ldloc dest2 + ldsflda unsigned int8 BulkCpy::Data + ldc.i4 9 + cpblk + + ldloc dest1 + ldsflda unsigned int8 BulkCpy::Data + ldc.i4 10 + cpblk + + ldloc dest2 + ldsflda unsigned int8 BulkCpy::Data + ldc.i4 11 + cpblk + + ldloc dest1 + ldsflda unsigned int8 BulkCpy::Data + ldc.i4 12 + cpblk + + ldloc dest2 + ldsflda unsigned int8 BulkCpy::Data + ldc.i4 13 + cpblk + + ldloc dest1 + ldsflda unsigned int8 BulkCpy::Data + ldc.i4 14 + cpblk + + ldloc dest2 + ldsflda unsigned int8 BulkCpy::Data + ldc.i4 15 + cpblk + + ldloc dest1 + ldsflda unsigned int8 BulkCpy::Data + ldc.i4 16 + cpblk + + ldloc dest2 + ldsflda unsigned int8 BulkCpy::Data + ldc.i4 2053 + cpblk + + ldloc count + ldc.i4.1 + sub + dup + stloc count + brtrue loop + + + call int32 [mscorlib]System.Environment::get_TickCount() + ldloc start + sub + stloc elapsed + + ldstr "Elapsed : {0} ms." + ldloc elapsed + box [mscorlib]System.Int32 + call void [mscorlib]System.Console::WriteLine(string, object) + + ret + } + +} + + +.data RandomData = bytearray ( + 25 4e d7 23 b9 f8 fb ef + 4e c2 b6 23 a9 16 d1 2a + eb f6 89 78 3f 35 26 c0 + 21 43 82 4c 27 f0 b2 cb + 70 d7 ed b6 fb e6 80 cf + c9 f7 d5 72 9e e8 31 02 + d1 25 61 8b ff f4 eb 3e + f9 6b 69 bd 15 7a d6 cd + 08 9e bd 58 d4 32 59 25 + 43 51 76 b8 db 4c 32 61 + 68 69 ee ea e7 eb 33 a1 + ac 2d dc 9c 43 58 b9 df + 79 c1 61 71 4e d8 3a b0 + 5b 9a 8b 12 7a d9 b5 b1 + 76 ec 6b 75 5b b9 dc 92 + 18 88 43 3c 38 11 51 a3 + 4b c3 ff f0 79 c9 d2 86 + b1 89 9f 47 b0 4f fb e6 + ae be 8f 35 0d f8 0d 2f + 56 12 92 5c e3 9e c7 55 + b5 e3 c0 2f 6d f1 fd 97 + e0 c5 bb c0 25 59 1a e9 + 92 3b d8 63 16 b3 4c 3f + 45 f1 f3 84 08 b1 b6 1b + 38 01 6e 78 85 60 34 c2 + ce 5f f1 db 48 bf eb 40 + 85 37 b7 73 b9 0d 23 a0 + 76 cd 03 fc 5a 6c b3 44 + 9f 41 ca 06 e7 ea ed bb + 85 52 0b 53 1d ee 02 c7 + 57 6f 9e d4 22 49 0c b8 + b1 73 05 c2 9c 2d a6 3c + 69 b8 d0 f3 74 26 87 f0 + b7 6c d3 c3 0f a4 b5 c9 + f8 1b 58 c5 c3 04 4c f6 + 8e 19 a2 22 47 af db 27 + fa 97 b5 be a0 5d 1d e5 + 4b cc cc e2 66 f3 a6 2a + e3 cb 5a 3c 68 a1 d9 91 + d5 8a c4 3a 94 d9 cb 63 + f3 63 2c 65 c9 b6 06 e2 + 61 81 31 03 00 14 27 c7 + 36 7b 5b 84 1f 7b 2a f3 + 96 8d 9f 2d d1 43 84 1d + d8 67 43 43 4d 49 2a db + 43 4d 77 19 a0 7f 84 fd + 87 e6 a4 94 bf be 69 b7 + 5d 1f 87 0e 7e 21 23 bc + f7 9c 2b 1b 70 1a 03 2c + 60 28 53 19 c1 73 c7 4d + 8a b4 85 58 d9 a4 ce 4c + 27 08 a9 2e 0e 72 b0 6d + 1f 78 88 31 e5 39 5f d6 + ce 4d 84 e5 33 a7 7c 7f + 8e f9 68 98 0b 9e e9 5f + f0 96 62 98 11 6e 3b 11 + 4e bb a2 ea d0 dd d9 a0 + 73 ce 62 c1 8d bd 50 63 + 1d f6 73 dc b9 21 04 6b + 77 fd 7c 8e e5 58 de 46 + 32 39 48 f6 63 12 86 bd + 3f 3b 0d 0a 35 3f 43 50 + 31 d2 b4 71 60 18 42 00 + 37 d3 c5 99 70 04 7f 9d + ae a4 98 32 33 90 9b 14 + 52 fc 41 a4 9f 2f 74 07 + 36 70 19 bd 46 39 4d 80 + cf 9b 0a 11 36 67 62 c2 + bb ac 15 a0 54 7d e1 24 + de 30 c5 a2 f8 11 45 ed + af fd a5 0a 36 6d 31 15 + 6d 0a 3b e0 a6 3c 51 c1 + 6e 55 d8 62 cf cd 25 64 + 72 a8 cd 1b 51 9a 94 c8 + 75 64 41 e8 57 68 89 9c + 49 10 14 2f 39 4b de 0c + ea b5 d0 df 64 6c c1 84 + 07 55 d9 90 be 92 62 9b + f8 17 14 40 61 5c ca 4a + 88 2d a1 b2 fc 2f 4e a0 + 7d f9 32 38 31 00 01 73 + eb fa 88 18 7a 02 d4 24 + 02 c6 cc a3 4f 1f 7c b5 + bf e5 43 44 c0 1e 17 f2 + 4c 1d 0c c8 7c a6 68 87 + cb 97 9a 83 b7 5c d9 a1 + e6 a3 4e d9 96 8e e4 13 + f8 1e 57 66 fe d0 f9 5a + 6f 9b ec 78 70 dc a5 11 + 4b a5 0a 4d 65 94 b2 09 + cd f1 04 39 44 b2 dc af + de 3d 92 35 21 09 c6 f4 + c2 c2 aa 56 16 ed d9 a8 + e4 e3 c4 73 f2 54 a5 19 + 8d cc a8 c1 93 a4 99 77 + 19 b7 76 e6 9c 23 aa 5b + b7 52 c6 c9 d2 9d a8 eb + 3e e2 73 07 1f 79 b6 20 + aa 75 96 5d 3d 93 a8 f4 + f4 dd fd 92 57 5a 32 3e + d1 63 ed 94 d9 b4 6b 90 + c5 90 a2 12 93 73 da 01 + 66 f0 79 9b e3 c1 74 53 + 19 d8 6a 2a eb 25 78 3d + 7b 6c ac 2f 68 68 91 0e + 5e 73 c5 94 cf a0 93 7d + bf ea b5 b9 e7 02 cd 26 + c1 50 32 47 89 5e 77 21 + 12 94 e6 af ec 7d f9 49 + 30 93 9d 9c 37 a2 1e 1a + 20 da 1e 16 ad 66 f5 31 + f5 33 81 fe cf b4 74 48 + c0 1b 2f 4a 73 ee f8 28 + 53 47 b0 38 05 84 11 23 + cb 7d e9 66 05 ce 3b d0 + 09 f7 b3 39 75 71 54 62 + a6 3b dd ec 81 3e cf 92 + 3c 1e 49 3d 8e 1f 89 6b + 5b aa 9c 2b 04 79 b7 65 + 90 9d 71 30 86 b9 12 ac + 0d 01 50 55 f3 86 c1 6c + ca 0d 14 1b 29 92 19 9f + 3a 9e d6 ac 15 5a 6e 56 + 07 51 95 0b 5c ea e0 ee + f2 2b 13 d0 16 db 3e c3 + 0b 8e 1e 2c 8d d1 32 2e + 1e 27 ec 6e 45 0d 2f 6d + 09 f5 fa 7f 61 7f 78 48 + eb 22 4b ad 7b 62 c7 45 + 19 9f 23 a7 74 0e 79 97 + b6 33 74 49 34 ee 1b 61 + 7d be 8d d7 2f 6f 94 c1 + 49 35 06 13 c3 1b 5a 51 + 8f 52 02 9c 22 64 75 8f + 77 f3 80 ac 34 f1 d4 1c + 90 b0 69 f1 e9 88 27 01 + 4f 10 0e 5f e6 9a 80 e6 + aa 63 12 6f c6 dc 9d 71 + 47 7e 1c b0 48 cf b7 73 + e7 fb e7 dc ad 54 71 43 + 7f 7c 75 7a 22 54 93 65 + b1 90 b4 7f 89 87 f3 71 + 27 e6 b5 ea be 70 e3 8c + 5a 5e a1 cb 50 6e 76 b5 + ae bd 32 57 82 7f 9f 22 + 78 85 49 3c 60 30 c3 2e + 24 25 2e e4 df 69 f5 2e + 0f 89 a8 cf bd 14 4b e7 + 09 cb 67 38 e9 8e 0e 4a + 63 0c ce 63 f0 7c 82 8d + 9a a3 65 b9 02 c9 e1 3b + ec 8a c6 db 57 8b 1f a3 + 4a 67 22 66 01 5b c5 a9 + 25 40 80 c4 43 4b d5 55 + ef 2b 44 a7 84 f0 a3 5d + 1a fd a2 f3 9e cf c6 ca + 47 72 95 04 47 b2 08 b6 + fa a0 9a a8 ee 26 8c 4c + 35 32 1a df 7f 62 c4 5e + 6f c6 f3 94 d3 e4 1a df + 68 6b 62 bf d4 04 3d 71 + 41 dc b8 c3 27 e1 fd 8f + 6f b0 59 0f bf c2 c4 35 + 22 65 a3 7e 3c 30 b6 33 + a3 38 f8 2c 52 e7 eb 05 + b2 e7 f0 a5 16 d4 12 94 + cf b7 8a df 5e 86 78 70 + 0b 94 d5 87 dc 90 bb ab + b8 c2 ca 04 74 17 02 a3 + 7c 82 76 a3 41 c7 1e 3f + 13 be a8 e5 29 aa 9b cd + 27 ef 3e f6 83 bf e4 e5 + 67 37 a4 a8 e5 6f 90 c2 + a8 f9 39 64 60 3b d7 07 + 38 2a d3 d1 38 15 67 3b + e8 2f 49 17 12 6f af e5 + 3b d2 ab ba 65 8d b5 c7 + 57 58 a7 72 83 ad 48 01 + 8e e1 fc 61 80 eb 1d d8 + 39 54 7e 10 12 a3 6d fc + 29 67 57 69 fd 71 35 28 + 1d fe cd fa ba 34 ce 3f + 15 8f 5a 5b b9 22 78 40 + 63 15 85 5e 7a 09 ba 6d + 16 c1 8d a6 59 01 7f 5b + b0 26 96 8e fb 0b 5b bc + f2 2a b5 f3 8c 62 cf b6 + 2e 02 d4 0d 0b 63 2e 08 + 8c 67 59 23 b7 84 27 04 + 37 b9 e6 8e f9 3a a9 46 + 62 b2 d8 82 50 3d 88 50 + 37 b9 fd 85 5c d8 45 0a + 19 d5 6b 8a b0 58 c9 d9 + 9b e9 62 d8 3f 3a c7 24 + 0e 64 70 06 0b 69 fc 5a + 3e c5 7f 63 0a 44 97 a1 + c6 dd f9 4e d0 f6 8a bd + 25 5c 01 93 6b 69 eb 04 + 3f 13 05 8e 0d 04 7d e4 + f3 90 a6 56 19 d5 82 81 + 13 f6 65 9c 52 0d f4 df + a0 56 08 ad 72 94 d5 59 + ed cb 7f 74 32 54 84 df + 64 55 ca 22 69 b9 16 d9 + 9b 01 5a 3b dd d5 8a db + 31 f6 78 67 26 aa 86 be + 6d 2a d1 46 6c c5 b1 96 + 55 e2 76 a8 e1 2b 0b 60 + 01 79 8c 5e 74 39 69 bb + b8 9c 4d 54 9a 91 e0 cb + 8b 13 d6 b6 3f 2a e4 f9 + 48 02 a5 d8 60 00 1c ae + d3 fb cb 5c de 2c 53 2d + d1 2c 8d ba 3a cb 7f 8b + 2a b6 22 7b 27 0d ed b4 + 70 1b 32 1a 25 65 89 9f + 30 b9 ec 5d 4b eb 08 6c + a2 05 98 f1 ed cc ad 63 + f6 90 ba 66 d2 9b ef 32 + 2a f0 98 ec 64 60 24 e0 + a4 9a 8e 10 1a 15 82 5f + d9 cd ed bf b2 e1 0b 5c + 02 d8 51 90 c6 d5 88 39 + 6e 46 24 27 cf a7 90 bd + 36 84 12 80 a8 07 62 d4 + 12 ab c7 1b 29 7a 21 3d + 9b 03 13 f4 f1 c8 66 f4 + eb 10 0c a6 2c 86 8e 0b + 90 a8 f7 dc 87 d6 c1 69 + f9 5b 9d 8a b1 b4 a6 63 + 15 57 6f b5 cb 84 e6 a7 + 7b 68 96 97 c9 fc 48 e7 + c6 cd e8 35 46 57 87 0a + 0c ac fb d8 40 69 b5 e8 + 4a 9b f6 74 51 8e 0d 1c + 74 47 92 2c 6a 0e 3c 68 + 8a e1 2f 38 ee 01 82 71 + 48 c3 02 d7 23 a2 01 99 + 51 b2 00 00 17 0e 70 0f + aa 6d 25 48 da e2 43 5e + 9f 29 8d bd 22 75 58 ba + 3c 57 4b da df 5b b5 c6 + f9 7a 1d f8 15 a0 6b 74 + 44 c1 7a f3 7d c5 8b 19 + a5 09 f0 87 0a 3a 9a c0 + 15 77 ee 25 2f 6f ab b8 + ab d2 a1 d5 7b 3e ec 59 + ef 6e 42 26 86 94 da e2 + 5a 56 02 c7 29 80 d9 98 + ea c2 9d 72 8c 64 44 8b + 1a 1a dd dc 9f 14 0a 2f + 70 f0 9e d2 ad 74 1f a1 + a7 a3 70 13 c0 05 c2 b3 + 24 09 da 04 7c 88 17 07 + 2e e7 0d f8 f5 38 f3 73 + f7 98 2c 7b 35 4d 55 c8 + 7f a5 09 c2 99 45 fc 64 + 3a a6 30 ca 15 6e 38 29 + a5 e2 73 d7 30 b4 7a e8 + 24 07 50 38 2d d2 72 73 + e1 15 9e c9 b1 93 6e 3a + 9d 79 cf 8a e1 00 49 2a + c4 4b ea d8 7d dd c7 61 + 6b 7f 86 88 53 36 5a 39 + 52 0a 3c 26 8c 63 2c 94 + b7 7b 42 1a e7 ef 77 27 + 10 d5 5d 49 49 44 ba 4e + c4 42 ee f7 cb 76 bd 37 + c9 f7 a7 83 d9 ac f9 7b + 62 de 3d 7b 3e be 6a 2b + 31 0b 9f 18 4e b5 d2 98 + 1f 80 b4 60 21 10 f2 24 + fd 99 3d a3 46 3a 93 66 + f6 76 db 5a 5c d0 1a da + dd fe d8 3d 81 0d 10 1c + 88 57 4b f1 d6 bd 54 62 + bd 32 40 8b 1d 01 86 b5 + cd 26 aa 58 cf a8 ed 9a + c0 fd 7f 8c 86 93 ac f3 + 95 19 ca 41 ba 3a b4 88 + 29 8b 1b 5f c3 04 7a e5 + 53 55 d9 a7 b6 f5 00 37 + bc ce 37 d2 69 bc e6 8b + 12 91 d0 18 4f 12 af 23 + 98 ed c0 3d ad 59 12 a6 + 6c 9c 64 35 05 cc dd f3 + 7f 7e 46 2d 97 e4 0a 42 + 23 cc d9 c6 f0 7e 3c 47 + ae 95 41 d3 bc e2 75 7a + f3 66 cd 29 7a 0a 46 39 + 64 77 32 3a a5 d3 ec 8a + f4 ca 1d c7 56 2a b8 ad + 47 76 c1 92 47 bc ed 9f + 4a 6a 0a 10 d9 8a d8 49 + 4e 9f 38 14 0b 5d 5f b5 + da 0a 34 e2 65 c5 b7 65 + a7 94 d3 cd 22 7d c9 cf + 9f 08 b0 71 35 11 26 9c + 2f 5f f6 7c ab ac 0d 2f + 3f 1b 30 bd 48 da 11 32 + 23 db 60 14 2c 68 99 68 + 7c 73 d8 5e a3 3f 25 5b + 8d be 7e 52 e1 33 aa 63 + 29 67 29 7b 38 34 fd a1 + ae b8 a8 eb f8 fc 4c 2c + 4c 30 90 b5 f2 5e 74 0b + 7a f6 7b 65 c6 e5 54 6c + d0 0b 54 90 c3 ee df 60 + 3f 33 84 fd 70 ef 42 f5 + 24 fa b1 94 ca 17 27 f2 + 3d 97 be 8c 92 49 47 b9 + ee 00 0f 9c 5b ac f8 08 + 8e f2 25 72 6d 2a ba 4f + 0a 28 16 d2 9e bf e3 a0 + 81 0f c9 d5 9c 49 27 0b + 91 d6 e6 a1 c3 0e 48 d8 + 40 80 ad 4b e8 37 ba 2c + 74 21 15 7c 90 88 1c 90 + 99 72 8f 4b ec 64 49 2c + 7d 06 fe cb 59 f6 82 7a + fe be 8c 64 5b 82 7c b7 + 79 a0 56 37 9c 37 d0 0c + df 5c 12 a3 84 f3 8b 06 + f2 2d b4 94 c7 2f 7e 1a + 0e 84 1f 64 33 91 f8 f1 + dc 76 ae b1 7c bb bd 26 + b9 de 1d e3 d7 f7 e0 cb + 5d 24 12 7a 08 a3 76 b3 + 24 f1 e3 a1 dd ec 53 4f + 0b 56 05 97 da f6 6a 0f + c7 34 d9 97 d3 ca 3b d3 + f0 a0 8c 68 88 12 7d c1 + 5e 89 a5 00 0b 9e ba 71 + 2c 59 2b 12 a2 28 17 01 + 8c 84 20 d7 08 7e 10 fa + ac 0b 5e 8d a4 9f 46 6b + 69 d4 0c dc 75 69 cb 55 + e0 ea dc 93 75 66 cc b4 + a5 1e 2e 30 94 e2 82 5e + 7d ef 64 46 44 d4 2d cf + b8 b8 cd 23 c2 af f6 7a + da fa 97 9e c7 3e be 81 + 23 93 a9 3b da 06 d8 65 + a0 7f 9b f4 e9 85 40 57 + 76 e1 fa bf bd 3a c8 97 + e2 51 87 10 da fd ac 36 + 65 a9 08 a8 ba 3a 86 99 + 64 4f e4 d8 55 bd 55 d5 + 92 1e 58 94 ed 95 36 91 + f5 0a 33 9d 7f 9e dc 92 + 5e 6e 6a 17 0b 5a 77 0d + 02 db 21 43 3c 66 00 16 + df 82 4a 6d 37 9e d9 94 + bd 33 9d 68 a7 7a f4 c2 + ab b2 f3 78 53 16 f0 c1 + 51 bd 13 d7 14 1c b4 5e + 67 55 de 32 22 7f 82 8a + b2 fa 8c 8a ee 2a b8 db + 35 3b fe c9 cd 2b 34 c4 + 59 2b 40 91 ec 53 38 13 + f3 66 fc 18 3f 47 7d bf + d3 bd 57 4a 66 0b 6f 9e + bd 2a e5 6d 1d ed bc e1 + 30 94 cb 8b fb de 54 a0 + 8e 21 42 25 6f 9d 78 44 + be 92 4a a4 96 78 78 7c + a9 39 39 42 27 11 60 29 + 98 fe e8 4e 9a 97 af 1e + 3d 70 fb f6 91 e8 54 97 + d7 26 b8 99 37 bc fc 26 + 97 a5 f3 6d f9 58 b5 e0 + d9 86 94 c3 eb f7 b7 66 + 04 71 5d 47 90 89 a6 5c + e8 28 34 de 39 37 b7 5b + c2 aa 84 05 b3 16 df 6b + 52 0b 98 03 16 dc 9a a0 + 95 08 8b 39 6b 5e 7b 4e + a0 94 f0 94 d6 cc c2 b8 + 96 7d 03 e8 34 bb 8b 39 + 54 66 19 af 06 d1 39 71 + 44 c4 62 9c 55 f3 9e b8 + ce 68 a8 d6 d1 4c 24 21 + 01 55 df 60 11 44 bf c0 + 39 69 d2 af fe ea ef 5d + 48 04 5e 93 73 f1 f7 c8 + 8e 15 5e 9b e4 07 5b 7a + f3 ab b3 50 55 dc 8f 5f + cd 2f 4a 5c f7 96 8a b7 + 84 f8 15 72 7c af 08 8a + f3 85 37 a0 7b 57 6f 87 + dd be aa 70 f4 f9 50 73 + ba 3b 11 65 b2 05 89 6c + a0 90 c3 06 d6 c2 dd ce + 47 b4 93 82 49 56 3f 3c + 3b f8 f9 7c 79 d6 9f 19 + d8 81 22 4e c3 13 00 33 + 8f 3f 37 c8 83 d2 97 c2 + a2 2a e6 b2 03 15 97 b4 + 79 ba 35 42 13 02 a6 63 + 2c 4e d1 54 67 47 9e ca + 0e 70 f7 b2 0b 86 ac 17 + 2b 20 cb 97 c8 72 7c 98 + 11 28 55 e9 73 e8 58 c4 + 67 26 c1 7e 21 0c c5 95 + fd 8f 58 b9 f6 71 24 00 + 0d 12 8f 5d 43 63 fa a6 + 52 04 3e e4 16 c9 cf cd + f6 76 ad 6b 96 94 e2 54 + 6f b8 b3 2d bf d5 8e 09 + bf f6 82 4c 10 f9 50 2d + d4 14 36 93 69 de 20 b4 + 89 6e 71 42 3a 96 4e 9f + 4f 0b 6d fb f9 79 92 2d + c6 ea c6 e1 f8 ee 0c c4 + 38 21 06 de 4b e9 93 97 + b5 ec 8e 22 59 1d d1 3b + 13 08 76 b5 dc ac f6 94 + b9 06 0e 68 9d 95 0b 73 + e2 44 8c 8e ed c9 f3 a8 + e3 9e de 4c 18 82 5c f2 + 24 15 90 9f 41 e1 fc 4a + 89 89 7e 3d 75 9c 5b 7d + 0a 43 51 bb 9f 03 27 05 + aa 8d d2 a5 03 0a 3e f5 + 3e d9 bc 0b 94 ec 7e 3f + 2f 57 6e 70 e5 5c 0c d4 + 32 42 2d e0 b2 da f5 24 + 29 a0 59 07 4e ad 61 83 + bc ce 4e c9 cb 5b 82 4e + c9 b4 63 20 b0 2e f0 ac + fc 1e 26 90 90 bb 94 c1 + 60 2c 68 b0 5f de 12 74 + 22 5a 62 b7 63 ed d9 bf + db 46 63 0f b6 0c c8 aa + 95 2d c3 03 1d 09 c9 db + 6b 9b d8 69 b6 17 39 60 + 33 ab d7 fc 53 40 84 09 + c8 ad 7d d5 9c 32 30 a8 + f2 3c 24 e9 a0 7b 40 77 + 25 3f 24 e6 b8 d1 67 49 + 12 9e fa 87 01 83 9f 36 + 88 3f 0f a9 11 48 be 61 + 74 36 81 13 df 6e 3a b4 + 71 32 29 7d c3 00 4d 57 + 53 4c f4 be 67 2c 91 d0 + d2 69 ea d4 50 4e c7 57 + 86 95 37 bf e3 ce 70 d4 + 06 0d 23 b7 6d 30 a0 98 + 1e 23 d6 d6 a7 a1 e5 47 + 71 4f 1e 20 d7 36 6c d4 + 37 e3 91 cc d3 c8 af 08 + a2 ea e7 d4 3c 3f 0f 92 + 1a e4 20 c5 b1 7f 5e 80 + d7 0d 1f 73 e7 cd f8 18 + 70 1d ea d4 39 57 65 b9 + ea d2 7f 9c 52 c6 0f b8 + c5 83 be 87 09 dd bd 37 + 9b 09 e2 47 a2 26 8b 35 + 3e ce 7b 44 bb aa a1 cb + 67 66 d8 53 4a 80 e9 7a + 15 87 00 3e b9 0f ad 6c + c5 83 a7 90 a6 3f 22 73 + e4 e5 50 3f 42 0a 4b ab + d9 9f 16 f1 d8 49 20 b1 + 73 d6 d3 d7 ef 6f b6 f9 + 72 ab e3 b2 ee 06 dd ee + 0d 0a 1e 48 e0 b1 95 10 + fc 4e cd 10 e0 e4 f6 4a + 61 6a 0c b1 9c 3c 2c 89 + a4 a2 2e 14 42 03 1f 7c + 9e c8 83 a4 a8 fd 66 f3 + 60 44 bc f0 87 f2 42 38 + 22 79 b3 38 02 9c 67 4a + 9d 6a 49 3d a5 16 eb 09 + f6 6e 53 4e 96 81 18 80 + d1 27 d5 5f eb 0d f5 0f + 8e 1b 45 e4 f8 03 1c 7d + fd 8d b6 f5 45 1d e3 a9 + 0a 1c 8e 26 9d a2 34 e3 + 94 b4 7c a1 de 1b 42 14 + 47 8c 8b 07 21 1c 79 d0 + fe b5 ec 60 33 94 df 9a + b4 a4 c1 51 a5 1c 75 75 + 80 f0 a6 44 c2 d6 d0 07 + 3e f8 26 83 95 3a a7 8d + a7 9e e6 a6 36 82 6f bc + f7 b3 22 7e 0f b5 c6 e2 + 83 ba 5a 7a dc b3 51 b1 + b9 19 d8 3c 3c 27 e8 41 + b6 0d 26 88 1f a6 31 10 + fa 95 13 fb ee 09 dc 8f + 48 d6 cc ab c0 34 df 67 + 26 7c 98 f9 31 f2 4b d7 + 26 a1 a1 d4 1f 61 62 df + 82 61 65 99 3d 75 57 75 + 56 2f 70 08 95 35 4c 10 + e2 58 ca 36 78 73 db 46 + 4c 18 54 6d 2e e7 de 0a + 31 fa bc ec 88 52 08 6b + 74 16 d2 b5 b7 46 3f 1d + d2 81 f8 2f 51 88 27 d1 + 61 4b d1 28 60 2b 3a c2 + 9a a1 da ed b3 14 3d a7 + a1 b7 58 ac 13 e6 99 69 + ef 48 c4 76 ac 0f b9 0b + 69 e5 62 db 27 e3 a0 53 + 21 05 99 65 c2 b8 c4 6c + c7 25 6a 42 fa 7f 78 76 + da e6 87 e4 02 d0 0e 6a + 28 61 59 29 87 d6 d8 61 + 5c f9 39 4d 69 d8 39 6b + 75 72 b0 3f 30 b3 4c f9 +) diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/castclass.cs b/unity-2019.4.24f1-mbe/mono/benchmark/castclass.cs new file mode 100644 index 000000000..0864a1a35 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/castclass.cs @@ -0,0 +1,27 @@ +using System; + +public class Test { + + int tmp = 1; + + public static int Main (string[] args) { + int repeat = 1; + + + if (args.Length == 1) + repeat = Convert.ToInt32 (args [0]); + + Console.WriteLine ("Repeat = " + repeat); + + object a = new Test (); + + for (int i = 0; i < (repeat * 5000); i++) + for (int j = 0; j < 100000; j++) + if (((Test)a).tmp != 1) + return 1; + + return 0; + } +} + + diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/cmov1.cs b/unity-2019.4.24f1-mbe/mono/benchmark/cmov1.cs new file mode 100644 index 000000000..d0c0d007e --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/cmov1.cs @@ -0,0 +1,29 @@ +using System; +class T { + static void Main () { + int y = 0; + int z = 1; + for (int i = 0; i < 500000000; i ++) { + if (y == 0) + z = i; + + if (y == 4) + y = i; + + if (z == 0) + y = 1; + + if (y == 1) + z = i; + + if (y == 1) + y = i; + + if (z == 2) + y = z; + else + y = i; + } + } + +} \ No newline at end of file diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/cmov2.cs b/unity-2019.4.24f1-mbe/mono/benchmark/cmov2.cs new file mode 100644 index 000000000..b2d0706b8 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/cmov2.cs @@ -0,0 +1,23 @@ +using System; +class T { + static void Main () { + int a = 1, b = 2, c = 3, d = 4, e = 5; + for (int i = 0; i < 500000000; i ++) { + // on the stack + if (a == b) + a = i; + if (b == a) + b = i; + if (c == d) + c = i; + if (d == e) + d = i; + if (e == a) + e = i; + } + + if ((a ^ b ^ c ^ d ^ e) == 12345) + return; + } + +} \ No newline at end of file diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/cmov3.cs b/unity-2019.4.24f1-mbe/mono/benchmark/cmov3.cs new file mode 100644 index 000000000..4cfc681b4 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/cmov3.cs @@ -0,0 +1,14 @@ +using System; +class T { + // test x ? A : B where A and B are constants. + static void Main () { + int a = 0, b = 0, c = 0, d = 0, e = 0; + for (int i = 0; i < 50000000; i ++) { + a = b == 10 ? 1 : 1; + b = b > 1 ? 9 : 8; + c = b <= c ? 1 : 2; + d = d > 0 ? 1 : 0; + e = e == 0 ? -1 : 0; + } + } +} \ No newline at end of file diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/cmov4.cs b/unity-2019.4.24f1-mbe/mono/benchmark/cmov4.cs new file mode 100644 index 000000000..c00bc32b6 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/cmov4.cs @@ -0,0 +1,33 @@ +using System; +class T { + // some more advanced versions. + static void Main () { + int a = 0, b = 0, c = 0, d = 0, e = 0; + for (int i = 0; i < 50000000; i ++) { + // sgn (x) + if (a == 0) + a = 0; + else if (a < 0) + a = -1; + else + a = 1; + + // cond incr + if (a <= 0) + a ++; + + // buffer ring + if (b == 49) + b = 0; + else + b ++; + + // max + c = a > b ? a : b; + + // abs + d = a > 0 ? a : -a; + } + } + +} \ No newline at end of file diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/cmov5.cs b/unity-2019.4.24f1-mbe/mono/benchmark/cmov5.cs new file mode 100644 index 000000000..c3c6f0889 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/cmov5.cs @@ -0,0 +1,15 @@ +using System; +class T { + static void Main () { + int a = 1, b = 2, c = 3, d = 4, e = 5; + for (int i = 0; i < 500000000; i ++) { + // on the stack + a = e == 1 ? b : c; + b = a == 1 ? c : d; + c = b == 1 ? d : e; + d = c == 1 ? e : a; + e = d == 1 ? a : b; + } + } + +} \ No newline at end of file diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/commute.cs b/unity-2019.4.24f1-mbe/mono/benchmark/commute.cs new file mode 100644 index 000000000..00dff806c --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/commute.cs @@ -0,0 +1,37 @@ +using System; + +class A { + static int Main () + { + int a = 0; + + for (int i = 0; i < 50000000; i++) { + + + a = + (((((a + 1) - 2) + 3) - 4 + 5 - 6 + 7) + + (((((a + 1) - 2) + 3) - 4 + 5 - 6 + 7) + + (((((a + 1) - 2) + 3) - 4 + 5 - 6 + 7) + + (((((a + 1) - 2) + 3) - 4 + 5 - 6 + 7) + + (((((a + 1) - 2) + 3) - 4 + 5 - 6 + 7) + + (((((a + 1) - 2) + 3) - 4 + 5 - 6 + 7) + + (((((a + 1) - 2) + 3) - 4 + 5 - 6 + 7) + + (((((a + 1) - 2) + 3) - 4 + 5 - 6 + 7) + + (((((a + 1) - 2) + 3) - 4 + 5 - 6 + 7) + + (((((a + 1) - 2) + 3) - 4 + 5 - 6 + 7) + + (((((a + 1) - 2) + 3) - 4 + 5 - 6 + 7) + + (((((a + 1) - 2) + 3) - 4 + 5 - 6 + 7) + + (((((a + 1) - 2) + 3) - 4 + 5 - 6 + 7) + + (((((a + 1) - 2) + 3) - 4 + 5 - 6 + 7) + + (((((a + 1) - 2) + 3) - 4 + 5 - 6 + 7) + + (((((a + 1) - 2) + 3) - 4 + 5 - 6 + 7) + + (((((a + 1) - 2) + 3) - 4 + 5 - 6 + 7) + + (((((a + 1) - 2) + 3) - 4 + 5 - 6 + 7))))))))))))))))))); + } + + // no ssa + return a != (a + 1) ? 0 : 1; + } + + static void Foo (out int dummy) { dummy = 0; } +} \ No newline at end of file diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/contextbound-perf.cs b/unity-2019.4.24f1-mbe/mono/benchmark/contextbound-perf.cs new file mode 100644 index 000000000..848ee52e5 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/contextbound-perf.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections; +using System.Runtime.Remoting; + +public class Bar : System.ContextBoundObject { +} + +public class Driver { + public static void Main (string[] args) { + Bar b = new Bar(); + + for (int i = 0; i != 30000000; i++) + if (!b.Equals (b)) + Console.WriteLine ("error!!"); + } +} + diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/ctor-bench.cs b/unity-2019.4.24f1-mbe/mono/benchmark/ctor-bench.cs new file mode 100644 index 000000000..31099acdd --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/ctor-bench.cs @@ -0,0 +1,67 @@ +using System; +using System.Reflection; + +class T { + + public T () { + } + + const int count = 1000000; + + static void use_new () { + for (int i = 0; i < count; ++i) + new T (); + } + + object Clone () { + return MemberwiseClone (); + } + + static void use_clone () { + T t = new T (); + for (int i = 0; i < count; ++i) + t.Clone (); + } + + static void use_activator () { + for (int i = 0; i < count; ++i) + Activator.CreateInstance (typeof (T)); + } + + static void use_ctor () { + ConstructorInfo ctor = typeof (T).GetConstructor (Type.EmptyTypes); + for (int i = 0; i < count; ++i) + ctor.Invoke (null); + } + + static void Main () { + long start, end, new_val, perc; + start = Environment.TickCount; + + start = Environment.TickCount; + use_new (); + end = Environment.TickCount; + Console.WriteLine ("new took {0}", end-start); + new_val = end-start; + + start = Environment.TickCount; + use_clone (); + end = Environment.TickCount; + perc = ((end-start-new_val) * 100) / new_val; + Console.WriteLine ("clone took {0} {1} %", end-start, perc); + + start = Environment.TickCount; + use_activator (); + end = Environment.TickCount; + perc = ((end-start-new_val) * 100) / new_val; + Console.WriteLine ("activator took {0} {1} %", end-start, perc); + + start = Environment.TickCount; + use_ctor (); + end = Environment.TickCount; + perc = ((end-start-new_val) * 100) / new_val; + Console.WriteLine ("ctor took {0} {1} %", end-start, perc); + + } +} + diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/divun.cs b/unity-2019.4.24f1-mbe/mono/benchmark/divun.cs new file mode 100644 index 000000000..ba1cd1f13 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/divun.cs @@ -0,0 +1,26 @@ +using System; + +class X { + + public static int DivUn(int x) { + x *= 163859; + x = (int) ((uint)x / 5); + x = (int) ((uint)x / 25); + x = (int) ((uint)x / 10); + x = (int) ((uint)x / 128); + x = (int) ((uint)x / 43); + x = (int) ((uint)x / 2); + x = (int) ((uint)x / 4); + x = (int) ((uint)x / 1); + return x; + } + + public static int Main() { + int x = 1; + for (int i=0; i < 100000000; ++i) x += DivUn(12345); + // x |= -1; // check for overflow case + x = (int) ((uint)x / 1025); + return x; + } + +} diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/fib.cs b/unity-2019.4.24f1-mbe/mono/benchmark/fib.cs new file mode 100644 index 000000000..44c398493 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/fib.cs @@ -0,0 +1,26 @@ +using System; + +public class Fib { + + public static int fib (int n) { + if (n < 2) + return 1; + return fib(n-2)+fib(n-1); + } + public static int Main (string[] args) { + int repeat = 1; + + if (args.Length == 1) + repeat = Convert.ToInt32 (args [0]); + + Console.WriteLine ("Repeat = " + repeat); + + for (int i = 0; i < (repeat * 50); i++) + if (fib (32) != 3524578) + return 1; + + return 0; + } +} + + diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/iconst-byte.cs b/unity-2019.4.24f1-mbe/mono/benchmark/iconst-byte.cs new file mode 100644 index 000000000..05178ad80 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/iconst-byte.cs @@ -0,0 +1,41 @@ +using System; + +class A { + static int Main () + { + // prevent ssa (for now) + int dummy; + Foo (out dummy); + + for (int i = 0; i < 50000000; i++) { + byte b; + + b = 0; + b = 0; + b = 0; + b = 0; + b = 0; + b = 0; + b = 0; + b = 0; + b = 0; + b = 0; + b = 0; + b = 0; + b = 0; + b = 0; + b = 0; + b = 0; + b = 0; + b = 0; + b = 0; + b = 0; + b = 0; + b = 0; + } + + return 0; + } + + static void Foo (out int dummy) { dummy = 0; } +} \ No newline at end of file diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/incr.cs b/unity-2019.4.24f1-mbe/mono/benchmark/incr.cs new file mode 100644 index 000000000..696de5a74 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/incr.cs @@ -0,0 +1,26 @@ +using System; +class T { + static void Main () { + int i = Environment.TickCount; + new T ().X (); + Console.WriteLine (Environment.TickCount - i); + } + + int [] window = new int [9]; + + void X () { + int scan = 0, match = 0; + for (int i = 0; i < 5000000; i ++) { + if (window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match]) { + scan = match = 0; + } + } + } +} \ No newline at end of file diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/initlocals.cs b/unity-2019.4.24f1-mbe/mono/benchmark/initlocals.cs new file mode 100644 index 000000000..dc7b3737d --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/initlocals.cs @@ -0,0 +1,33 @@ +// Foo() benefits from definite assignment analysis + +using System; + +class Test { + static void Foo () { + int a, b, c, d, e, f, g; + int h, i, j, k, l, m, n; + + for (int x = 0; x < 2; ++x) { + a = 0; + b = a; + c = a; + d = b; + e = c; + f = 1; + g = 2; + h = a + b; + i = h + h; + j = 1 + b + c; + k = i + j; + l = f + g; + m = k + l; + n = l + l; + } + } + + + static void Main () { + for (int i = 0; i < 100000000; ++ i) + Foo (); + } +} diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/inline-readonly.cs b/unity-2019.4.24f1-mbe/mono/benchmark/inline-readonly.cs new file mode 100644 index 000000000..df610eed4 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/inline-readonly.cs @@ -0,0 +1,17 @@ +using System; + +public class A { + + static readonly A a = new A (); + + static int Main () + { + for (int i = 0; i < 500000000; i++) + a.Dummy (); + + return 0; + } + + public virtual void Dummy () { + } +} \ No newline at end of file diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/inline1.cs b/unity-2019.4.24f1-mbe/mono/benchmark/inline1.cs new file mode 100644 index 000000000..883a81ddc --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/inline1.cs @@ -0,0 +1,24 @@ +using System; + +public class Test { + + public static void test (int n) { + } + + public static int Main (string[] args) { + int repeat = 1; + + if (args.Length == 1) + repeat = Convert.ToInt32 (args [0]); + + Console.WriteLine ("Repeat = " + repeat); + + for (int i = 0; i < repeat; i++) + for (int j = 0; j < 500000000; j++) + test (12345); + + return 0; + } +} + + diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/inline2.cs b/unity-2019.4.24f1-mbe/mono/benchmark/inline2.cs new file mode 100644 index 000000000..548a1e011 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/inline2.cs @@ -0,0 +1,36 @@ +using System; + +public class Test { + + public static void test0 () { + test1 (0); + } + public static void test1 (int a) { + test2 (0, 1); + } + public static void test2 (int a, int b) { + test3 (0, 1, 2); + } + public static void test3 (int a, int b, int c) { + test4 (0, 1, 2, 3); + } + public static void test4 (int a, int b, int c, int d) { + } + + public static int Main (string[] args) { + int repeat = 1; + + if (args.Length == 1) + repeat = Convert.ToInt32 (args [0]); + + Console.WriteLine ("Repeat = " + repeat); + + for (int i = 0; i < repeat; i++) + for (int j = 0; j < 500000000; j++) + test0 (); + + return 0; + } +} + + diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/inline3.cs b/unity-2019.4.24f1-mbe/mono/benchmark/inline3.cs new file mode 100644 index 000000000..40e8c4445 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/inline3.cs @@ -0,0 +1,52 @@ +using System; + +public class Test { + + public static int test0 () + { + return test1 (1); + } + public static int test1 (int a) + { + return test2 (a, 2); + } + + public static int test2 (int a, int b) + { + return test3 (a, b, 3); + } + + public static int test3 (int a, int b, int c) + { + return test4 (a, b, c, 4); + } + + public static int test4 (int a, int b, int c, int d) + { + return a + b + c + d; + } + + public static int run () + { + return test0 (); + } + + public static int Main (string[] args) { + int repeat = 1; + + if (args.Length == 1) + repeat = Convert.ToInt32 (args [0]); + + Console.WriteLine ("Repeat = " + repeat); + + for (int i = 0; i < repeat; i++) + for (int j = 0; j < 500000000; j++) + if (test0 () != 10) + return 1; + + + return 0; + } +} + + diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/inline4.cs b/unity-2019.4.24f1-mbe/mono/benchmark/inline4.cs new file mode 100644 index 000000000..47cd0ee83 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/inline4.cs @@ -0,0 +1,34 @@ +using System; + +public class Tests { + + public static int test (int n) { + if ((n & 1) == 0) + return 2; + else + return 1; + } + + public static int Main (string[] args) { + int repeat = 1; + int sum = 0; + + if (args.Length == 1) + repeat = Convert.ToInt32 (args [0]); + + Console.WriteLine ("Repeat = " + repeat); + + for (int i = 0; i < repeat; i++) + for (int j = 0; j < 50000000; j++) + sum += test (j); + + + Console.WriteLine (sum); + if (sum != (75000000 * repeat)) + return 1; + + return 0; + } +} + + diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/inline5.cs b/unity-2019.4.24f1-mbe/mono/benchmark/inline5.cs new file mode 100644 index 000000000..4e8e45b9b --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/inline5.cs @@ -0,0 +1,27 @@ +using System; + +public class Test { + + static int a = 0; + + public static void test (int n) { + a+=n; + } + + public static int Main (string[] args) { + int repeat = 1; + + if (args.Length == 1) + repeat = Convert.ToInt32 (args [0]); + + Console.WriteLine ("Repeat = " + repeat); + + for (int i = 0; i < repeat; i++) + for (int j = 0; j < 500000000; j++) + test (a); + + return 0; + } +} + + diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/inline6.cs b/unity-2019.4.24f1-mbe/mono/benchmark/inline6.cs new file mode 100644 index 000000000..7874a53ea --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/inline6.cs @@ -0,0 +1,29 @@ +using System; + +public class Test { + + public static int test (int n) { + int x = n + 1; + + return x; + } + + public static int Main (string[] args) { + int repeat = 1; + + /* + if (args.Length == 1) + repeat = Convert.ToInt32 (args [0]); + + Console.WriteLine ("Repeat = " + repeat); + */ + + for (int i = 0; i < repeat; i++) + for (int j = 0; j < 500000000; j++) + test (12345); + + return 0; + } +} + + diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/isinst.cs b/unity-2019.4.24f1-mbe/mono/benchmark/isinst.cs new file mode 100644 index 000000000..7d5008147 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/isinst.cs @@ -0,0 +1,24 @@ +using System; + +public class Test { + + public static int Main (string[] args) { + int repeat = 1; + + if (args.Length == 1) + repeat = Convert.ToInt32 (args [0]); + + Console.WriteLine ("Repeat = " + repeat); + + object a = new Test (); + + for (int i = 0; i < (repeat * 5000); i++) + for (int j = 0; j < 100000; j++) + if (!(a is Test)) + return 1; + + return 0; + } +} + + diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/life.cs b/unity-2019.4.24f1-mbe/mono/benchmark/life.cs new file mode 100644 index 000000000..828f6d5a2 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/life.cs @@ -0,0 +1,74 @@ +// created on 03/03/2002 at 15:12 +using System; + +class App { + static String s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15; + static int[] offsets = {-16, -15, -14, -1, 1, 14, 15, 16}; + public static int Main(String[] args) { + int i2 = 500; + int i0; + double n2; + DateTime start, end; + start = DateTime.Now; + s0 = " "; + s1 = " "; + s2 = " "; + s3 = " *** "; + s4 = " "; + s5 = " "; + s6 = " * "; + s7 = " * "; + s8 = " * "; + s9 = " * "; + s10 =" * "; + s11 =" * "; + s12 =" "; + s13 =" "; + s14 =" "; + s15 =""; + s15 = s0+s1+s2+s3+s4+s5+s6+s7+s8+s9+s10+s11+s12+s13+s14; + dump(); + i0 =0; + while (i0++ < i2) { + generate(); + dump(); + } + end = DateTime.Now; + n2 = (end-start).TotalMilliseconds; + Console.WriteLine("{0} generations in {1} milliseconds, {2} gen/sec.", + i2, (int)n2, (int)(i2/(n2/1000))); + return 0; +} +static void generate() { + int i0, i1, i2, i3; + i0 = s15.Length; + s1 = ""; + i1 = 0; + do { + i2 = 0; + foreach (int offset in offsets) { + i3 = (offset + i0 + i1) % i0; + if (s15.Substring(i3, 1) == "*") + i2++; + } + if (s15.Substring(i1, 1) == "*") { + if (i2 < 2 || i2 > 3) { + s1 += " "; + } else { + s1 += "*"; + } + } else { + if (i2 == 3) { + s1 += "*"; + } else { + s1 += "*"; + } + } + } while (++i1 < i0); + s15 = s1; +} +static void dump() { + ; +} +} + diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/lock.cs b/unity-2019.4.24f1-mbe/mono/benchmark/lock.cs new file mode 100644 index 000000000..28e9bf1c4 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/lock.cs @@ -0,0 +1,13 @@ +using System; +class T { + static void Main () { + int i = Environment.TickCount; + new T ().X (); + Console.WriteLine (Environment.TickCount - i); + } + + void X () { + for (int i = 0; i < 10000000; i ++) + lock (this) {} + } +} \ No newline at end of file diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/logic.cs b/unity-2019.4.24f1-mbe/mono/benchmark/logic.cs new file mode 100644 index 000000000..db3660726 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/logic.cs @@ -0,0 +1,89 @@ +/* this code is part of the pnetmark benchmark - i have only done some small + * modification + */ + +/* + * LogicBenchmark.cs - Implementation of the "LogicBenchmark" class. + * + * Copyright (C) 2001 Southern Storm Software, Pty Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +using System; + +public class Tests { + + public static void logic_run () + { + int iter; + + // Initialize. + bool flag1 = true; + bool flag2 = true; + bool flag3 = true; + bool flag4 = true; + bool flag5 = true; + bool flag6 = true; + bool flag7 = true; + bool flag8 = true; + bool flag9 = true; + bool flag10 = true; + bool flag11 = true; + bool flag12 = true; + bool flag13 = true; + + // First set of tests. + for(iter = 0; iter < 2000000; ++iter) { + if((flag1 || flag2) && (flag3 || flag4) && + (flag5 || flag6 || flag7)) + { + flag8 = !flag8; + flag9 = !flag9; + flag10 = !flag10; + flag11 = !flag11; + flag12 = !flag12; + flag13 = !flag13; + flag1 = !flag1; + flag2 = !flag2; + flag3 = !flag3; + flag4 = !flag4; + flag5 = !flag5; + flag6 = !flag6; + flag1 = !flag1; + flag2 = !flag2; + flag3 = !flag3; + flag4 = !flag4; + flag5 = !flag5; + flag6 = !flag6; + } + } + } + + public static int Main (string[] args) { + int repeat = 1; + + if (args.Length == 1) + repeat = Convert.ToInt32 (args [0]); + + Console.WriteLine ("Repeat = " + repeat); + + for (int i = 0; i < (repeat * 50); i++) + logic_run (); + + return 0; + } +} + + diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/long-muldiv.cs b/unity-2019.4.24f1-mbe/mono/benchmark/long-muldiv.cs new file mode 100644 index 000000000..1cdb26c4f --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/long-muldiv.cs @@ -0,0 +1,24 @@ +using System; +class T { + static void Main () { + int i = Environment.TickCount; + new T ().X (); + Console.WriteLine (Environment.TickCount - i); + } + + void X () { + long a = 1, b = 2, c = 3, d = 4; + + for (int i = 0; i < 10000000; i ++) { + a /= (b + 1); + b /= (c + 1); + c /= (d + 1); + d /= (a + 1); + + a *= (b + 2); + b *= (c + 2); + c *= (d + 2); + d *= (a + 2); + } + } +} \ No newline at end of file diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/loops.cs b/unity-2019.4.24f1-mbe/mono/benchmark/loops.cs new file mode 100644 index 000000000..da009e14e --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/loops.cs @@ -0,0 +1,45 @@ +using System; + +public class NestedLoop { + static public int nest_test () { + int n = 16; + int x = 0; + int a = n; + while (a-- != 0) { + int b = n; + while (b-- != 0) { + int c = n; + while (c-- != 0) { + int d = n; + while (d-- != 0) { + int e = n; + while (e-- != 0) { + int f = n; + while (f-- != 0) { + x++; + } + } + } + } + } + } + return x; + } + + public static int Main (string[] args) { + int repeat = 1; + + if (args.Length == 1) + repeat = Convert.ToInt32 (args [0]); + + Console.WriteLine ("Repeat = " + repeat); + + for (int i = 0; i < repeat*10; i++) + if (nest_test () != 16777216) + return 1; + + return 0; + } +} + + diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/math.cs b/unity-2019.4.24f1-mbe/mono/benchmark/math.cs new file mode 100644 index 000000000..4ad824275 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/math.cs @@ -0,0 +1,31 @@ +using System; + +public class MulDiv { + + public static double mathtest (int n) { + double res = 0; + + for (int j = 0; j < 200000; j++) { + res += Math.Sin (j); + res += Math.Cos (j); + } + + return res; + } + + public static int Main (string[] args) { + int repeat = 1; + + if (args.Length == 1) + repeat = Convert.ToInt32 (args [0]); + + Console.WriteLine ("Repeat = " + repeat); + + for (int i = 0; i < (repeat * 50); i++) + mathtest (1000); + + return 0; + } +} + + diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/max-min.cs b/unity-2019.4.24f1-mbe/mono/benchmark/max-min.cs new file mode 100644 index 000000000..a2f946bc0 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/max-min.cs @@ -0,0 +1,26 @@ +/* +mono max-min.exe 0m1.468s +mono -O=inline max-min.exe 0m1.087s +../mini/mono max-min.exe 0m0.511s +*/ + +class T { + + static int DoIt (int a, int b) { + int x = 0; + for (int j = 0; j < 200000; j++) { + x += System.Math.Max (a, b); + x += System.Math.Max (a, b); + x += System.Math.Max (j, b); + x += System.Math.Max (j, b); + x += System.Math.Min (System.Math.Max (j, x), b); + x += System.Math.Min (System.Math.Max (j, x), b); + } + return x; + } + + static void Main () { + for (int i = 0; i < 50; i++) + DoIt (1, 5); + } +} \ No newline at end of file diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/muldiv.cs b/unity-2019.4.24f1-mbe/mono/benchmark/muldiv.cs new file mode 100644 index 000000000..1b1353e0a --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/muldiv.cs @@ -0,0 +1,34 @@ +using System; + +public class MulDiv { + + public static int muldiv (int n) { + int res = n; + + for (int j = 0; j < 1048; j++) { + for (int i = 0; i < (n * 4); i++) { + n = (n / 256); + n++; + n = n * 128; + } + } + + return res; + } + + public static int Main (string[] args) { + int repeat = 1; + + if (args.Length == 1) + repeat = Convert.ToInt32 (args [0]); + + Console.WriteLine ("Repeat = " + repeat); + + for (int i = 0; i < (repeat * 50); i++) + muldiv (1000); + + return 0; + } +} + + diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/pinvoke.cs b/unity-2019.4.24f1-mbe/mono/benchmark/pinvoke.cs new file mode 100644 index 000000000..5ec3a6b6b --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/pinvoke.cs @@ -0,0 +1,31 @@ +using System; +using System.Runtime.InteropServices; + +public class Test { + + public static int delegate_test (int a) + { + if (a == 2) + return 0; + + return 1; + } + + [DllImport ("libtest", EntryPoint="mono_test_empty_pinvoke")] + public static extern int mono_test_empty_pinvoke (int i); + + public static int Main (String[] args) { + int repeat = 1; + + if (args.Length == 1) + repeat = Convert.ToInt32 (args [0]); + + Console.WriteLine ("Repeat = " + repeat); + + for (int i = 0; i < (repeat * 5000); i++) + for (int j = 0; j < 10000; j++) + mono_test_empty_pinvoke (5); + + return 0; + } +} diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/readonly-byte-array.cs b/unity-2019.4.24f1-mbe/mono/benchmark/readonly-byte-array.cs new file mode 100644 index 000000000..6af67b375 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/readonly-byte-array.cs @@ -0,0 +1,30 @@ +class Foo { + static readonly byte [] my_table = { + 0x00, 0x03, 0x06, 0x05, 0x0C, 0x0F, 0x0A, 0x09, 0x18, 0x1B, 0x1E, 0x1D, 0x14, 0x17, 0x12, 0x11, + 0x30, 0x33, 0x36, 0x35, 0x3C, 0x3F, 0x3A, 0x39, 0x28, 0x2B, 0x2E, 0x2D, 0x24, 0x27, 0x22, 0x21, + 0x60, 0x63, 0x66, 0x65, 0x6C, 0x6F, 0x6A, 0x69, 0x78, 0x7B, 0x7E, 0x7D, 0x74, 0x77, 0x72, 0x71, + 0x50, 0x53, 0x56, 0x55, 0x5C, 0x5F, 0x5A, 0x59, 0x48, 0x4B, 0x4E, 0x4D, 0x44, 0x47, 0x42, 0x41, + 0xC0, 0xC3, 0xC6, 0xC5, 0xCC, 0xCF, 0xCA, 0xC9, 0xD8, 0xDB, 0xDE, 0xDD, 0xD4, 0xD7, 0xD2, 0xD1, + 0xF0, 0xF3, 0xF6, 0xF5, 0xFC, 0xFF, 0xFA, 0xF9, 0xE8, 0xEB, 0xEE, 0xED, 0xE4, 0xE7, 0xE2, 0xE1, + 0xA0, 0xA3, 0xA6, 0xA5, 0xAC, 0xAF, 0xAA, 0xA9, 0xB8, 0xBB, 0xBE, 0xBD, 0xB4, 0xB7, 0xB2, 0xB1, + 0x90, 0x93, 0x96, 0x95, 0x9C, 0x9F, 0x9A, 0x99, 0x88, 0x8B, 0x8E, 0x8D, 0x84, 0x87, 0x82, 0x81, + 0x9B, 0x98, 0x9D, 0x9E, 0x97, 0x94, 0x91, 0x92, 0x83, 0x80, 0x85, 0x86, 0x8F, 0x8C, 0x89, 0x8A, + 0xAB, 0xA8, 0xAD, 0xAE, 0xA7, 0xA4, 0xA1, 0xA2, 0xB3, 0xB0, 0xB5, 0xB6, 0xBF, 0xBC, 0xB9, 0xBA, + 0xFB, 0xF8, 0xFD, 0xFE, 0xF7, 0xF4, 0xF1, 0xF2, 0xE3, 0xE0, 0xE5, 0xE6, 0xEF, 0xEC, 0xE9, 0xEA, + 0xCB, 0xC8, 0xCD, 0xCE, 0xC7, 0xC4, 0xC1, 0xC2, 0xD3, 0xD0, 0xD5, 0xD6, 0xDF, 0xDC, 0xD9, 0xDA, + 0x5B, 0x58, 0x5D, 0x5E, 0x57, 0x54, 0x51, 0x52, 0x43, 0x40, 0x45, 0x46, 0x4F, 0x4C, 0x49, 0x4A, + 0x6B, 0x68, 0x6D, 0x6E, 0x67, 0x64, 0x61, 0x62, 0x73, 0x70, 0x75, 0x76, 0x7F, 0x7C, 0x79, 0x7A, + 0x3B, 0x38, 0x3D, 0x3E, 0x37, 0x34, 0x31, 0x32, 0x23, 0x20, 0x25, 0x26, 0x2F, 0x2C, 0x29, 0x2A, + 0x0B, 0x08, 0x0D, 0x0E, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16, 0x1F, 0x1C, 0x19, 0x1A + }; + + static void Main () + { + byte b = 0; + byte foo = 0; + for (int i = 0; i < 100000000; i ++, b ++) { + foo ^= my_table [b]; + foo ^= my_table [foo]; + } + } +} \ No newline at end of file diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/readonly-inst.cs b/unity-2019.4.24f1-mbe/mono/benchmark/readonly-inst.cs new file mode 100644 index 000000000..ec257d9fc --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/readonly-inst.cs @@ -0,0 +1,23 @@ +using System; + +class A { + class B { public readonly C c = new C (); } + class C { public readonly D d = new D (); } + class D { public readonly E e = new E (); } + class E { public readonly int i = 1; } + + static readonly A foo = new A (); + static readonly A bar = new A (); + + readonly B b = new B (); + static int Main () + { + + for (int i = 0; i < 50000000; i++) { + if (foo.b.c.d.e.i != bar.b.c.d.e.i) + return 1; + } + + return 0; + } +} \ No newline at end of file diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/readonly-vt.cs b/unity-2019.4.24f1-mbe/mono/benchmark/readonly-vt.cs new file mode 100644 index 000000000..127724bd8 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/readonly-vt.cs @@ -0,0 +1,22 @@ +// Since the structs are readonly, the expression here is a jit time constant. + +using System; + +struct A { + struct B { int dummy; public C c; } + struct C { int dummy; public D d; } + struct D { public int i; } + + static readonly B b, bb; + + static int Main () + { + + for (int i = 0; i < 50000000; i++) { + if (b.c.d.i != bb.c.d.i) + return 1; + } + + return 0; + } +} \ No newline at end of file diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/readonly.cs b/unity-2019.4.24f1-mbe/mono/benchmark/readonly.cs new file mode 100644 index 000000000..11ed40d77 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/readonly.cs @@ -0,0 +1,27 @@ +using System; + +public class Test { + + static readonly int a = 5; + static readonly int b = 6; + + public static int Main (string[] args) { + int repeat = 1; + + if (args.Length == 1) + repeat = Convert.ToInt32 (args [0]); + + Console.WriteLine ("Repeat = " + repeat); + + for (int i = 0; i < repeat*3; i++) { + for (int j = 0; j < 100000000; j++) { + if ((a != 5) || (b != 6)) + return 1; + } + } + + return 0; + } +} + + diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/regalloc-2.cs b/unity-2019.4.24f1-mbe/mono/benchmark/regalloc-2.cs new file mode 100644 index 000000000..69bb0c238 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/regalloc-2.cs @@ -0,0 +1,40 @@ +// +// Idealy, we would fold the operations from a...h into one long xor chain, +// we could then commute the xor operation. +// +// More realisticly, we should assign each of a...h to one reg, rather than making +// the two swap between two registers: +// (stind.i4 regvar[%edi] (xor (ldind.i4 regoffset[-0x18(%ebp)]) iconst[1])) +// (stind.i4 regvar[%esi] (xor (ldind.i4 regvar[%edi]) iconst[2])) +// (stind.i4 regvar[%edi] (xor (ldind.i4 regvar[%esi]) iconst[3])) +// (stind.i4 regvar[%esi] (xor (ldind.i4 regvar[%edi]) iconst[4])) +// + +class T { + static void Main () + { + int j = 0, k = 0, l = 0; + for (int i = 0; i < 50000000; i ++) { + int a = i ^ 1; + int b = a ^ 2; + int c = b ^ 3; + int d = c ^ 4; + int e = d ^ 5; + int f = e ^ 6; + int g = f ^ 7; + int h = g ^ 8; + + j ^= h; + k ^= h + 1; + l ^= h & 5; + + j ^= l; + k ^= k + 1; + l ^= j & 5; + + j ^= l; + k ^= k + 1; + l ^= j & 5; + } + } +} \ No newline at end of file diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/regalloc-3.cs b/unity-2019.4.24f1-mbe/mono/benchmark/regalloc-3.cs new file mode 100644 index 000000000..643a9cf0c --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/regalloc-3.cs @@ -0,0 +1,65 @@ +// +// You need a deadce to get rid of the initlocals statements, +// which make all of the variables overlap in their live ranges. +// + +using System; +class T { + static void Main () { + int i = Environment.TickCount; + new T ().X (); + Console.WriteLine (Environment.TickCount - i); + } + + void X () { + int a = 0; + for (int x = 0; x < 1000; x ++) { + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + for (int i = 0; i < 10000; i ++) a ++; + } + } +} \ No newline at end of file diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/regalloc.cs b/unity-2019.4.24f1-mbe/mono/benchmark/regalloc.cs new file mode 100644 index 000000000..d84753fa1 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/regalloc.cs @@ -0,0 +1,54 @@ +// +// To do this test well, I think you need to move the int aX = 0... +// lines down as far as possible. That way, the lifespan of the variables +// is short, and they can go into registers. +// + +class T { + static int Main () + { + for (int r = 0; r < 50; r ++) { + int a0 = 0, b0 = 0, c0 = 0, d0 = 0; + int a1 = 0, b1 = 0, c1 = 0, d1 = 0; + int a2 = 0, b2 = 0, c2 = 0, d2 = 0; + int a3 = 0, b3 = 0, c3 = 0, d3 = 0; + int a4 = 0, b4 = 0, c4 = 0, d4 = 0; + + int x = 0; + + for (int i = 0; i < 400000; i ++) a0 ++; + for (int i = 0; i < 400000; i ++) b0 ++; + for (int i = 0; i < 400000; i ++) c0 ++; + for (int i = 0; i < 400000; i ++) d0 ++; + x ^= a0 ^ b0 ^ c0 ^ d0; + + for (int i = 0; i < 400000; i ++) a1 ++; + for (int i = 0; i < 400000; i ++) b1 ++; + for (int i = 0; i < 400000; i ++) c1 ++; + for (int i = 0; i < 400000; i ++) d1 ++; + x ^= a1 ^ b1 ^ c1 ^ d1; + + for (int i = 0; i < 400000; i ++) a2 ++; + for (int i = 0; i < 400000; i ++) b2 ++; + for (int i = 0; i < 400000; i ++) c2 ++; + for (int i = 0; i < 400000; i ++) d2 ++; + x ^= a2 ^ b2 ^ c2 ^ d2; + + for (int i = 0; i < 400000; i ++) a3 ++; + for (int i = 0; i < 400000; i ++) b3 ++; + for (int i = 0; i < 400000; i ++) c3 ++; + for (int i = 0; i < 400000; i ++) d3 ++; + x ^= a3 ^ b3 ^ c3 ^ d3; + + for (int i = 0; i < 400000; i ++) a4 ++; + for (int i = 0; i < 400000; i ++) b4 ++; + for (int i = 0; i < 400000; i ++) c4 ++; + for (int i = 0; i < 400000; i ++) d4 ++; + x ^= a4 ^ b4 ^ c4 ^ d4; + + if (x != 0) + return 1; + } + return 0; + } +} \ No newline at end of file diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/regvar.cs b/unity-2019.4.24f1-mbe/mono/benchmark/regvar.cs new file mode 100644 index 000000000..4f5e0a355 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/regvar.cs @@ -0,0 +1,43 @@ +// +// We get stuff like: +// 48: 8b c3 mov eax,ebx +// 4a: 8b cf mov ecx,edi +// 4c: 0b c1 or eax,ecx +// 4e: 8b d8 mov ebx,eax +// + +using System; +class T { + static void Main () { + int i = Environment.TickCount; + new T ().X (); + Console.WriteLine (Environment.TickCount - i); + } + + void X () { + int a = 0, b = 0, c = 0, d = 0; + for (int i = 0; i < 50000000; i ++) { + + + a |= b; + b |= c; + c |= d; + b |= d; + + a ^= b; + b ^= c; + c ^= d; + b ^= d; + + a &= b; + b &= c; + c &= d; + b &= d; + + a += b; + b += c; + c += d; + b += d; + } + } +} \ No newline at end of file diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/sbperf1.cs b/unity-2019.4.24f1-mbe/mono/benchmark/sbperf1.cs new file mode 100644 index 000000000..f5f13747c --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/sbperf1.cs @@ -0,0 +1,17 @@ +using System.Text; + +namespace test { + public class Test { + public static int Main() { + for (int i = 0; i < 500000; i++) { + StringBuilder sb = new StringBuilder (); + sb.Append ("hello"); + sb.Append (" world!"); + sb.ToString (); + } + + return 0; + + } + } +} diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/sbperf2.cs b/unity-2019.4.24f1-mbe/mono/benchmark/sbperf2.cs new file mode 100644 index 000000000..8d7379e4e --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/sbperf2.cs @@ -0,0 +1,19 @@ +using System.Text; + +namespace test { + public class Test { + public static int Main() { + StringBuilder sb = new StringBuilder (); + for (int i = 0; i < 1000000; i++) { + sb.Append ("hello"); + sb.Append (" world!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + string str = sb.ToString (); + int len = str.Length; + sb.Length = 0; + } + + return 0; + + } + } +} diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/static-fields.cs b/unity-2019.4.24f1-mbe/mono/benchmark/static-fields.cs new file mode 100644 index 000000000..9ac89df33 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/static-fields.cs @@ -0,0 +1,25 @@ +using System; + +public class Tests { + + public static int si = 0; + + public static int Main (string[] args) { + int h = 0, repeat = 1; + + Console.WriteLine ("Repeat = " + repeat); + + for (int i = 0; i < (repeat * 50); i++) { + for (int j = 0; j < 10000000; j++) { + h += si; + } + } + + if (h != 0) + return 1; + + return 0; + } +} + + diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/stelemref.cs b/unity-2019.4.24f1-mbe/mono/benchmark/stelemref.cs new file mode 100644 index 000000000..7020f5a9e --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/stelemref.cs @@ -0,0 +1,15 @@ +using System; +class T { + static void Main () { + int i = Environment.TickCount; + new T ().X (); + Console.WriteLine (Environment.TickCount - i); + } + + void X () { + object [] x = new object [1]; + object o = new object (); + for (int i = 0; i < 10000000; i ++) + x [0] = o; + } +} \ No newline at end of file diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/string1.cs b/unity-2019.4.24f1-mbe/mono/benchmark/string1.cs new file mode 100644 index 000000000..a185d5ffe --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/string1.cs @@ -0,0 +1,30 @@ +using System; + +public class Tests { + + public static int Main (string[] args) { + int res, repeat = 1; + string ts1 = "abcdefghijklmnopqrstuvwxyz"; + + if (args.Length == 1) + repeat = Convert.ToInt32 (args [0]); + + Console.WriteLine ("Repeat = " + repeat); + + int len = ts1.Length; + + for (int i = 0; i < (repeat * 50); i++) { + for (int j = 0; j < 100000; j++) { + int k, h = 0; + + for (k = 0; k < len; ++k) + h += ts1 [k]; + + } + } + + return 0; + } +} + + diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/switch.cs b/unity-2019.4.24f1-mbe/mono/benchmark/switch.cs new file mode 100644 index 000000000..da3cb9d1f --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/switch.cs @@ -0,0 +1,26 @@ +public class Tests { + + public static int Main () { + int n = 2; + int b = 0; + + for (int i = 0; i < 1000000000; i++) { + switch (n) { + case 0: b = 2; break; + case 1: b = 3; break; + case 2: b = 4; break; + case -1: b = 5; break; + default: + b = 6; + break; + } + } + + if (b != 4) + return 1; + + return 0; + } +} + + diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/test-driver b/unity-2019.4.24f1-mbe/mono/benchmark/test-driver new file mode 100644 index 000000000..4f5ed6777 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/test-driver @@ -0,0 +1,39 @@ +#!/usr/bin/perl -w + +my $interpreter = shift; +my $test = shift; +my $output = $test; +my $stdout = $test.'.stdout'; +my $stderr = $test.'.stderr'; + +$output =~ s/\.exe$/.output/; + +$| = 0; +print "Testing $test... "; + +for ($c = 20 - length ($test); $c > 0; $c--) { print " "; } + +my $res = system("/usr/bin/time -o .res -f '%U' $interpreter @ARGV $test 2>/dev/null 1>$stdout"); + +if ($res) { + printf ("failed $? (%d) signal (%d).\n", $? >> 8, $? & 127); + exit (1); +} elsif (-f $output) { + print "failed output.\n" if (read_file ($output) ne read_file ($stdout)); + exit (1); +} else { + $t = `cat .res`; + $t =~ s/\n//; + print "pass. $t\n"; + #unlink ($result); +} +exit (0); + +sub read_file { + local ($/); + my $out = shift; + open (F, "<$out") || die $!; + $out = ; + close(F); + return $out; +} diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/valuetype-hash-equals.cs b/unity-2019.4.24f1-mbe/mono/benchmark/valuetype-hash-equals.cs new file mode 100644 index 000000000..2aa1a4ba3 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/valuetype-hash-equals.cs @@ -0,0 +1,75 @@ +using System; + +public class ValueType1 +{ + static int Main () + { + Blah a = new Blah ("abc", 1); + Blah b = new Blah ("ab" + 'c', 1); + long start, end; + start = Environment.TickCount; + + start = Environment.TickCount; + for (int i = 0; i < 1000000; i++) + a.GetHashCode (); + end = Environment.TickCount; + Console.WriteLine ("struct common GetHashCode(): {0}", end-start); + + start = Environment.TickCount; + for (int i = 0; i < 1000000; i++) + a.Equals (b); + end = Environment.TickCount; + Console.WriteLine ("struct common Equals(): {0}", end-start); + + Blah2 a2 = new Blah2 ("abc", 1); + Blah2 b2 = new Blah2 ("abc", 1); + start = Environment.TickCount; + for (int i = 0; i < 1000000; i++) + a2.GetHashCode (); + end = Environment.TickCount; + Console.WriteLine ("struct specific GetHashCode(): {0}", end-start); + + start = Environment.TickCount; + for (int i = 0; i < 1000000; i++) + a2.Equals (b2); + end = Environment.TickCount; + Console.WriteLine ("struct specific Equals(): {0}", end-start); + + return 0; + } + + struct Blah + { + public string s; + public int i; + + public Blah (string s, int k) + { + this.s = s; + i = k; + } + } + + struct Blah2 + { + public string s; + public int i; + + public Blah2 (string s, int k) + { + this.s = s; + i = k; + } + + public override int GetHashCode () { + return i ^ s.GetHashCode (); + } + public override bool Equals (object obj) { + if (obj == null || !(obj is Blah2)) + return false; + Blah2 b = (Blah2)obj; + return b.s == this.s && b.i == this.i; + } + } +} + diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/vt2.cs b/unity-2019.4.24f1-mbe/mono/benchmark/vt2.cs new file mode 100644 index 000000000..295e7249e --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/vt2.cs @@ -0,0 +1,26 @@ +using System; + +public struct A { + public int a; + static int Main () + { + A a = new A (); + A b = new A (); + + for (int i = 0; i < 50000000; i++) { + a.a = i; + b.a = i + 5; + + a.a = Foo (a, b); + b.a = a.a + 8; + + Foo (a, b); + } + + return 0; + } + + static int Foo (A a, A b) { + return a.a + b.a; + } +} \ No newline at end of file diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/vtype1.cs b/unity-2019.4.24f1-mbe/mono/benchmark/vtype1.cs new file mode 100644 index 000000000..a6176c0ad --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/vtype1.cs @@ -0,0 +1,26 @@ +using System; + +public struct TestResult +{ + public TestResult(int x) { + a=x; + } + + public int a; +} + +public class Test +{ + static int Main () + { + TestResult x; + + for (int i = 0; i < 500000000; i++) { + //x = new TestResult(i, 0m); + x = new TestResult(i); + } + + return 0; + } + +} diff --git a/unity-2019.4.24f1-mbe/mono/benchmark/zipmark.cs b/unity-2019.4.24f1-mbe/mono/benchmark/zipmark.cs new file mode 100644 index 000000000..7c57c75ce --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/benchmark/zipmark.cs @@ -0,0 +1,4068 @@ +// zipmark.cs +// (This file is the NZipLib sources with a crude benchmark class attached - +// 2002-15-05 Dan Lewis ) +// ------------------------------------------------------------------------- +// +// NZipLib source: +// Copyright (C) 2001 Mike Krueger +// +// This file was translated from java, it was part of the GNU Classpath +// Copyright (C) 2001 Free Software Foundation, Inc. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// As a special exception, if you link this library with other files to +// produce an executable, this library does not by itself cause the +// resulting executable to be covered by the GNU General Public License. +// This exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +using System; +using System.IO; +using NZlib.Streams; +using NZlib.Checksums; +using NZlib.Compression; + +class ZipMark { + static int Iterations = 1000; + static int BlockSize = 1024; + + public static void Main (string [] args) + { + if (args.Length == 0 || args.Length > 3) { + Console.WriteLine ("Usage: zipmark FILE [ITERATIONS] [BLOCKSIZE]"); + return; + } + + string filename = args [0]; + FileInfo file = new FileInfo (filename); + if (!file.Exists) { + Console.WriteLine ("Couldn't find file {0}", filename); + return; + } + + FileStream fs = file.OpenRead (); + + byte [] raw = new byte [file.Length]; + int count = fs.Read (raw, 0, (int)file.Length); + fs.Close (); + + if (count != file.Length) { + Console.WriteLine ("Couldn't read file {0}", filename); + return; + } + + Deflater def = new Deflater (Deflater.BEST_COMPRESSION, false); + Inflater inf = new Inflater (false); + + // 1. Count deflated size + + int cooked_size = Deflate (def, raw, null); + byte [] cooked = new byte [cooked_size]; + + // 2. Deflate & Inflate + + if (args.Length > 1) + Iterations = Int32.Parse (args [1]); + if (args.Length > 2) + BlockSize = Int32.Parse (args [2]); + + for (int i = 0; i < Iterations; ++ i) { + Deflate (def, raw, cooked); + Inflate (inf, cooked, raw); + } + + return; + } + + static int Deflate (Deflater def, byte [] src, byte [] dest) + { + bool count; + int offset, length, remain; + + if (dest == null) { + dest = new byte [BlockSize]; + count = true; + } else + count = false; + + def.Reset (); + def.SetInput (src); + + offset = 0; + while (!def.IsFinished) { + if (def.IsNeedingInput) + def.Finish (); + + remain = Math.Min (dest.Length - offset, BlockSize); + if (remain == 0) + break; + + length = def.Deflate (dest, offset, remain); + if (!count) + offset += length; + } + + return def.TotalOut; + } + + static int Inflate (Inflater inf, byte [] src, byte [] dest) + { + int offset, length, remain; + + inf.Reset (); + inf.SetInput (src); + + offset = 0; + while (!inf.IsNeedingInput) { + remain = Math.Min (dest.Length - offset, BlockSize); + if (remain == 0) + break; + + length = inf.Inflate (dest, offset, remain); + offset += length; + } + + return inf.TotalOut; + } +} + +// ---------------------- NZipLib sources from here on -------------------------- + +namespace NZlib.Compression { + + /// + /// This is the Deflater class. The deflater class compresses input + /// with the deflate algorithm described in RFC 1951. It has several + /// compression levels and three different strategies described below. + /// + /// This class is not thread safe. This is inherent in the API, due + /// to the split of deflate and setInput. + /// + /// author of the original java version : Jochen Hoenicke + /// + public class Deflater + { + /// + /// The best and slowest compression level. This tries to find very + /// long and distant string repetitions. + /// + public static int BEST_COMPRESSION = 9; + + /// + /// The worst but fastest compression level. + /// + public static int BEST_SPEED = 1; + + /// + /// The default compression level. + /// + public static int DEFAULT_COMPRESSION = -1; + + /// + /// This level won't compress at all but output uncompressed blocks. + /// + public static int NO_COMPRESSION = 0; + + /// + /// The default strategy. + /// + public static int DEFAULT_STRATEGY = 0; + + + /// + /// This strategy will only allow longer string repetitions. It is + /// useful for random data with a small character set. + /// + public static int FILTERED = 1; + + /// + /// This strategy will not look for string repetitions at all. It + /// only encodes with Huffman trees (which means, that more common + /// characters get a smaller encoding. + /// + public static int HUFFMAN_ONLY = 2; + + /// + /// The compression method. This is the only method supported so far. + /// There is no need to use this constant at all. + /// + public static int DEFLATED = 8; + + /* + * The Deflater can do the following state transitions: + * + * (1) -> INIT_STATE ----> INIT_FINISHING_STATE ---. + * / | (2) (5) | + * / v (5) | + * (3)| SETDICT_STATE ---> SETDICT_FINISHING_STATE |(3) + * \ | (3) | ,-------' + * | | | (3) / + * v v (5) v v + * (1) -> BUSY_STATE ----> FINISHING_STATE + * | (6) + * v + * FINISHED_STATE + * \_____________________________________/ + * | (7) + * v + * CLOSED_STATE + * + * (1) If we should produce a header we start in INIT_STATE, otherwise + * we start in BUSY_STATE. + * (2) A dictionary may be set only when we are in INIT_STATE, then + * we change the state as indicated. + * (3) Whether a dictionary is set or not, on the first call of deflate + * we change to BUSY_STATE. + * (4) -- intentionally left blank -- :) + * (5) FINISHING_STATE is entered, when flush() is called to indicate that + * there is no more INPUT. There are also states indicating, that + * the header wasn't written yet. + * (6) FINISHED_STATE is entered, when everything has been flushed to the + * internal pending output buffer. + * (7) At any time (7) + * + */ + + private static int IS_SETDICT = 0x01; + private static int IS_FLUSHING = 0x04; + private static int IS_FINISHING = 0x08; + + private static int INIT_STATE = 0x00; + private static int SETDICT_STATE = 0x01; +// private static int INIT_FINISHING_STATE = 0x08; +// private static int SETDICT_FINISHING_STATE = 0x09; + private static int BUSY_STATE = 0x10; + private static int FLUSHING_STATE = 0x14; + private static int FINISHING_STATE = 0x1c; + private static int FINISHED_STATE = 0x1e; + private static int CLOSED_STATE = 0x7f; + + /// + /// Compression level. + /// + private int level; + + /// + /// should we include a header. + /// + private bool noHeader; + +// /// +// /// Compression strategy. +// /// +// private int strategy; + + /// + /// The current state. + /// + private int state; + + /// + /// The total bytes of output written. + /// + private int totalOut; + + /// + /// The pending output. + /// + private DeflaterPending pending; + + /// + /// The deflater engine. + /// + private DeflaterEngine engine; + + /// + /// Creates a new deflater with default compression level. + /// + public Deflater() : this(DEFAULT_COMPRESSION, false) + { + + } + + /// + /// Creates a new deflater with given compression level. + /// + /// + /// the compression level, a value between NO_COMPRESSION + /// and BEST_COMPRESSION, or DEFAULT_COMPRESSION. + /// + /// if lvl is out of range. + public Deflater(int lvl) : this(lvl, false) + { + + } + + /// + /// Creates a new deflater with given compression level. + /// + /// + /// the compression level, a value between NO_COMPRESSION + /// and BEST_COMPRESSION. + /// + /// + /// true, if we should suppress the deflate header at the + /// beginning and the adler checksum at the end of the output. This is + /// useful for the GZIP format. + /// + /// if lvl is out of range. + public Deflater(int lvl, bool nowrap) + { + if (lvl == DEFAULT_COMPRESSION) { + lvl = 6; + } else if (lvl < NO_COMPRESSION || lvl > BEST_COMPRESSION) { + throw new ArgumentOutOfRangeException("lvl"); + } + + pending = new DeflaterPending(); + engine = new DeflaterEngine(pending); + this.noHeader = nowrap; + SetStrategy(DEFAULT_STRATEGY); + SetLevel(lvl); + Reset(); + } + + + /// + /// Resets the deflater. The deflater acts afterwards as if it was + /// just created with the same compression level and strategy as it + /// had before. + /// + public void Reset() + { + state = (noHeader ? BUSY_STATE : INIT_STATE); + totalOut = 0; + pending.Reset(); + engine.Reset(); + } + + /// + /// Gets the current adler checksum of the data that was processed so far. + /// + public int Adler { + get { + return engine.Adler; + } + } + + /// + /// Gets the number of input bytes processed so far. + /// + public int TotalIn { + get { + return engine.TotalIn; + } + } + + /// + /// Gets the number of output bytes so far. + /// + public int TotalOut { + get { + return totalOut; + } + } + + /// + /// Flushes the current input block. Further calls to deflate() will + /// produce enough output to inflate everything in the current input + /// block. This is not part of Sun's JDK so I have made it package + /// private. It is used by DeflaterOutputStream to implement + /// flush(). + /// + public void Flush() + { + state |= IS_FLUSHING; + } + + /// + /// Finishes the deflater with the current input block. It is an error + /// to give more input after this method was called. This method must + /// be called to force all bytes to be flushed. + /// + public void Finish() + { + state |= IS_FLUSHING | IS_FINISHING; + } + + /// + /// Returns true if the stream was finished and no more output bytes + /// are available. + /// + public bool IsFinished { + get { + return state == FINISHED_STATE && pending.IsFlushed; + } + } + + /// + /// Returns true, if the input buffer is empty. + /// You should then call setInput(). + /// NOTE: This method can also return true when the stream + /// was finished. + /// + public bool IsNeedingInput { + get { + return engine.NeedsInput(); + } + } + + /// + /// Sets the data which should be compressed next. This should be only + /// called when needsInput indicates that more input is needed. + /// If you call setInput when needsInput() returns false, the + /// previous input that is still pending will be thrown away. + /// The given byte array should not be changed, before needsInput() returns + /// true again. + /// This call is equivalent to setInput(input, 0, input.length). + /// + /// + /// the buffer containing the input data. + /// + /// + /// if the buffer was finished() or ended(). + /// + public void SetInput(byte[] input) + { + SetInput(input, 0, input.Length); + } + + /// + /// Sets the data which should be compressed next. This should be + /// only called when needsInput indicates that more input is needed. + /// The given byte array should not be changed, before needsInput() returns + /// true again. + /// + /// + /// the buffer containing the input data. + /// + /// + /// the start of the data. + /// + /// + /// the length of the data. + /// + /// + /// if the buffer was finished() or ended() or if previous input is still pending. + /// + public void SetInput(byte[] input, int off, int len) + { + if ((state & IS_FINISHING) != 0) { + throw new InvalidOperationException("finish()/end() already called"); + } + engine.SetInput(input, off, len); + } + + /// + /// Sets the compression level. There is no guarantee of the exact + /// position of the change, but if you call this when needsInput is + /// true the change of compression level will occur somewhere near + /// before the end of the so far given input. + /// + /// + /// the new compression level. + /// + public void SetLevel(int lvl) + { + if (lvl == DEFAULT_COMPRESSION) { + lvl = 6; + } else if (lvl < NO_COMPRESSION || lvl > BEST_COMPRESSION) { + throw new ArgumentOutOfRangeException("lvl"); + } + + + if (level != lvl) { + level = lvl; + engine.SetLevel(lvl); + } + } + + /// + /// Sets the compression strategy. Strategy is one of + /// DEFAULT_STRATEGY, HUFFMAN_ONLY and FILTERED. For the exact + /// position where the strategy is changed, the same as for + /// setLevel() applies. + /// + /// + /// the new compression strategy. + /// + public void SetStrategy(int stgy) + { + if (stgy != DEFAULT_STRATEGY && stgy != FILTERED && stgy != HUFFMAN_ONLY) { + throw new Exception(); + } + engine.Strategy = stgy; + } + + /// + /// Deflates the current input block to the given array. It returns + /// the number of bytes compressed, or 0 if either + /// needsInput() or finished() returns true or length is zero. + /// + /// + /// the buffer where to write the compressed data. + /// + public int Deflate(byte[] output) + { + return Deflate(output, 0, output.Length); + } + + /// + /// Deflates the current input block to the given array. It returns + /// the number of bytes compressed, or 0 if either + /// needsInput() or finished() returns true or length is zero. + /// + /// + /// the buffer where to write the compressed data. + /// + /// + /// the offset into the output array. + /// + /// + /// the maximum number of bytes that may be written. + /// + /// + /// if end() was called. + /// + /// + /// if offset and/or length don't match the array length. + /// + public int Deflate(byte[] output, int offset, int length) + { + int origLength = length; + + if (state == CLOSED_STATE) { + throw new InvalidOperationException("Deflater closed"); + } + + if (state < BUSY_STATE) { + /* output header */ + int header = (DEFLATED + + ((DeflaterConstants.MAX_WBITS - 8) << 4)) << 8; + int level_flags = (level - 1) >> 1; + if (level_flags < 0 || level_flags > 3) { + level_flags = 3; + } + header |= level_flags << 6; + if ((state & IS_SETDICT) != 0) { + /* Dictionary was set */ + header |= DeflaterConstants.PRESET_DICT; + } + header += 31 - (header % 31); + + + pending.WriteShortMSB(header); + if ((state & IS_SETDICT) != 0) { + int chksum = engine.Adler; + engine.ResetAdler(); + pending.WriteShortMSB(chksum >> 16); + pending.WriteShortMSB(chksum & 0xffff); + } + + state = BUSY_STATE | (state & (IS_FLUSHING | IS_FINISHING)); + } + + for (;;) { + int count = pending.Flush(output, offset, length); + offset += count; + totalOut += count; + length -= count; + + if (length == 0 || state == FINISHED_STATE) { + break; + } + + if (!engine.Deflate((state & IS_FLUSHING) != 0, (state & IS_FINISHING) != 0)) { + if (state == BUSY_STATE) { + /* We need more input now */ + return origLength - length; + } else if (state == FLUSHING_STATE) { + if (level != NO_COMPRESSION) { + /* We have to supply some lookahead. 8 bit lookahead + * are needed by the zlib inflater, and we must fill + * the next byte, so that all bits are flushed. + */ + int neededbits = 8 + ((-pending.BitCount) & 7); + while (neededbits > 0) { + /* write a static tree block consisting solely of + * an EOF: + */ + pending.WriteBits(2, 10); + neededbits -= 10; + } + } + state = BUSY_STATE; + } else if (state == FINISHING_STATE) { + pending.AlignToByte(); + /* We have completed the stream */ + if (!noHeader) { + int adler = engine.Adler; + pending.WriteShortMSB(adler >> 16); + pending.WriteShortMSB(adler & 0xffff); + } + state = FINISHED_STATE; + } + } + } + return origLength - length; + } + + /// + /// Sets the dictionary which should be used in the deflate process. + /// This call is equivalent to setDictionary(dict, 0, dict.Length). + /// + /// + /// the dictionary. + /// + /// + /// if setInput () or deflate () were already called or another dictionary was already set. + /// + public void SetDictionary(byte[] dict) + { + SetDictionary(dict, 0, dict.Length); + } + + /// + /// Sets the dictionary which should be used in the deflate process. + /// The dictionary should be a byte array containing strings that are + /// likely to occur in the data which should be compressed. The + /// dictionary is not stored in the compressed output, only a + /// checksum. To decompress the output you need to supply the same + /// dictionary again. + /// + /// + /// the dictionary. + /// + /// + /// an offset into the dictionary. + /// + /// + /// the length of the dictionary. + /// + /// + /// if setInput () or deflate () were already called or another dictionary was already set. + /// + public void SetDictionary(byte[] dict, int offset, int length) + { + if (state != INIT_STATE) { + throw new InvalidOperationException(); + } + + state = SETDICT_STATE; + engine.SetDictionary(dict, offset, length); + } + } +} + +namespace NZlib.Compression { + + /// + /// This class contains constants used for the deflater. + /// + public class DeflaterConstants + { + public const bool DEBUGGING = false; + + public const int STORED_BLOCK = 0; + public const int STATIC_TREES = 1; + public const int DYN_TREES = 2; + public const int PRESET_DICT = 0x20; + + public const int DEFAULT_MEM_LEVEL = 8; + + public const int MAX_MATCH = 258; + public const int MIN_MATCH = 3; + + public const int MAX_WBITS = 15; + public const int WSIZE = 1 << MAX_WBITS; + public const int WMASK = WSIZE - 1; + + public const int HASH_BITS = DEFAULT_MEM_LEVEL + 7; + public const int HASH_SIZE = 1 << HASH_BITS; + public const int HASH_MASK = HASH_SIZE - 1; + public const int HASH_SHIFT = (HASH_BITS + MIN_MATCH - 1) / MIN_MATCH; + + public const int MIN_LOOKAHEAD = MAX_MATCH + MIN_MATCH + 1; + public const int MAX_DIST = WSIZE - MIN_LOOKAHEAD; + + public const int PENDING_BUF_SIZE = 1 << (DEFAULT_MEM_LEVEL + 8); + public static int MAX_BLOCK_SIZE = Math.Min(65535, PENDING_BUF_SIZE-5); + + public const int DEFLATE_STORED = 0; + public const int DEFLATE_FAST = 1; + public const int DEFLATE_SLOW = 2; + + public static int[] GOOD_LENGTH = { 0, 4, 4, 4, 4, 8, 8, 8, 32, 32 }; + public static int[] MAX_LAZY = { 0, 4, 5, 6, 4,16, 16, 32, 128, 258 }; + public static int[] NICE_LENGTH = { 0, 8,16,32,16,32,128,128, 258, 258 }; + public static int[] MAX_CHAIN = { 0, 4, 8,32,16,32,128,256,1024,4096 }; + public static int[] COMPR_FUNC = { 0, 1, 1, 1, 1, 2, 2, 2, 2, 2 }; + } +} + +namespace NZlib.Compression { + + public class DeflaterEngine : DeflaterConstants + { + private static int TOO_FAR = 4096; + + private int ins_h; +// private byte[] buffer; + private short[] head; + private short[] prev; + + private int matchStart, matchLen; + private bool prevAvailable; + private int blockStart; + private int strstart, lookahead; + private byte[] window; + + private int strategy, max_chain, max_lazy, niceLength, goodLength; + + /// + /// The current compression function. + /// + private int comprFunc; + + /// + /// The input data for compression. + /// + private byte[] inputBuf; + + /// + /// The total bytes of input read. + /// + private int totalIn; + + /// + /// The offset into inputBuf, where input data starts. + /// + private int inputOff; + + /// + /// The end offset of the input data. + /// + private int inputEnd; + + private DeflaterPending pending; + private DeflaterHuffman huffman; + + /// + /// The adler checksum + /// + private Adler32 adler; + + public DeflaterEngine(DeflaterPending pending) + { + this.pending = pending; + huffman = new DeflaterHuffman(pending); + adler = new Adler32(); + + window = new byte[2*WSIZE]; + head = new short[HASH_SIZE]; + prev = new short[WSIZE]; + + /* We start at index 1, to avoid a implementation deficiency, that + * we cannot build a repeat pattern at index 0. + */ + blockStart = strstart = 1; + } + + public void Reset() + { + huffman.Reset(); + adler.Reset(); + blockStart = strstart = 1; + lookahead = 0; + totalIn = 0; + prevAvailable = false; + matchLen = MIN_MATCH - 1; + + for (int i = 0; i < HASH_SIZE; i++) { + head[i] = 0; + } + + for (int i = 0; i < WSIZE; i++) { + prev[i] = 0; + } + } + + public void ResetAdler() + { + adler.Reset(); + } + + public int Adler { + get { + return (int)adler.Value; + } + } + + public int TotalIn { + get { + return totalIn; + } + } + + public int Strategy { + get { + return strategy; + } + set { + strategy = value; + } + } + + public void SetLevel(int lvl) + { + goodLength = DeflaterConstants.GOOD_LENGTH[lvl]; + max_lazy = DeflaterConstants.MAX_LAZY[lvl]; + niceLength = DeflaterConstants.NICE_LENGTH[lvl]; + max_chain = DeflaterConstants.MAX_CHAIN[lvl]; + + if (DeflaterConstants.COMPR_FUNC[lvl] != comprFunc) { +// if (DeflaterConstants.DEBUGGING) { +// Console.WriteLine("Change from "+comprFunc +" to " +// + DeflaterConstants.COMPR_FUNC[lvl]); +// } + switch (comprFunc) { + case DEFLATE_STORED: + if (strstart > blockStart) { + huffman.FlushStoredBlock(window, blockStart, + strstart - blockStart, false); + blockStart = strstart; + } + UpdateHash(); + break; + case DEFLATE_FAST: + if (strstart > blockStart) { + huffman.FlushBlock(window, blockStart, strstart - blockStart, + false); + blockStart = strstart; + } + break; + case DEFLATE_SLOW: + if (prevAvailable) { + huffman.TallyLit(window[strstart-1] & 0xff); + } + if (strstart > blockStart) { + huffman.FlushBlock(window, blockStart, strstart - blockStart, + false); + blockStart = strstart; + } + prevAvailable = false; + matchLen = MIN_MATCH - 1; + break; + } + comprFunc = COMPR_FUNC[lvl]; + } + } + + private void UpdateHash() + { +// if (DEBUGGING) { +// Console.WriteLine("updateHash: "+strstart); +// } + ins_h = (window[strstart] << HASH_SHIFT) ^ window[strstart + 1]; + } + + private int InsertString() + { + short match; + int hash = ((ins_h << HASH_SHIFT) ^ window[strstart + (MIN_MATCH -1)]) & HASH_MASK; + +// if (DEBUGGING) { +// if (hash != (((window[strstart] << (2*HASH_SHIFT)) ^ +// (window[strstart + 1] << HASH_SHIFT) ^ +// (window[strstart + 2])) & HASH_MASK)) { +// throw new Exception("hash inconsistent: "+hash+"/" +// +window[strstart]+"," +// +window[strstart+1]+"," +// +window[strstart+2]+","+HASH_SHIFT); +// } +// } + + prev[strstart & WMASK] = match = head[hash]; + head[hash] = (short)strstart; + ins_h = hash; + return match & 0xffff; + } + + public void FillWindow() + { + while (lookahead < DeflaterConstants.MIN_LOOKAHEAD && inputOff < inputEnd) { + int more = 2*WSIZE - lookahead - strstart; + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (strstart >= WSIZE + MAX_DIST) { + System.Array.Copy(window, WSIZE, window, 0, WSIZE); + matchStart -= WSIZE; + strstart -= WSIZE; + blockStart -= WSIZE; + + /* Slide the hash table (could be avoided with 32 bit values + * at the expense of memory usage). + */ + for (int i = 0; i < HASH_SIZE; i++) { + int m = head[i]; + head[i] = m >= WSIZE ? (short) (m - WSIZE) : (short)0; + } + more += WSIZE; + } + + if (more > inputEnd - inputOff) { + more = inputEnd - inputOff; + } + + System.Array.Copy(inputBuf, inputOff, window, strstart + lookahead, more); + adler.Update(inputBuf, inputOff, more); + inputOff += more; + totalIn += more; + lookahead += more; + + if (lookahead >= MIN_MATCH) { + UpdateHash(); + } + } + } + + private bool FindLongestMatch(int curMatch) + { + int chainLength = this.max_chain; + int niceLength = this.niceLength; + short[] prev = this.prev; + int scan = this.strstart; + int match; + int best_end = this.strstart + matchLen; + int best_len = Math.Max(matchLen, MIN_MATCH - 1); + + int limit = Math.Max(strstart - MAX_DIST, 0); + + int strend = strstart + MAX_MATCH - 1; + byte scan_end1 = window[best_end - 1]; + byte scan_end = window[best_end]; + + /* Do not waste too much time if we already have a good match: */ + if (best_len >= this.goodLength) { + chainLength >>= 2; + } + + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if (niceLength > lookahead) { + niceLength = lookahead; + } + + if (DeflaterConstants.DEBUGGING && strstart > 2*WSIZE - MIN_LOOKAHEAD) { + throw new InvalidOperationException("need lookahead"); + } + + do { + if (DeflaterConstants.DEBUGGING && curMatch >= strstart) { + throw new InvalidOperationException("future match"); + } + if (window[curMatch + best_len] != scan_end || + window[curMatch + best_len - 1] != scan_end1 || + window[curMatch] != window[scan] || + window[curMatch+1] != window[scan + 1]) { + continue; + } + + match = curMatch + 2; + scan += 2; + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + while (window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && scan < strend) ; + + if (scan > best_end) { + // if (DeflaterConstants.DEBUGGING && ins_h == 0) + // System.err.println("Found match: "+curMatch+"-"+(scan-strstart)); + matchStart = curMatch; + best_end = scan; + best_len = scan - strstart; + if (best_len >= niceLength) { + break; + } + + scan_end1 = window[best_end-1]; + scan_end = window[best_end]; + } + scan = strstart; + } while ((curMatch = (prev[curMatch & WMASK] & 0xffff)) > limit && --chainLength != 0); + + matchLen = Math.Min(best_len, lookahead); + return matchLen >= MIN_MATCH; + } + + public void SetDictionary(byte[] buffer, int offset, int length) + { + if (DeflaterConstants.DEBUGGING && strstart != 1) { + throw new InvalidOperationException("strstart not 1"); + } + adler.Update(buffer, offset, length); + if (length < MIN_MATCH) { + return; + } + if (length > MAX_DIST) { + offset += length - MAX_DIST; + length = MAX_DIST; + } + + System.Array.Copy(buffer, offset, window, strstart, length); + + UpdateHash(); + --length; + while (--length > 0) { + InsertString(); + strstart++; + } + strstart += 2; + blockStart = strstart; + } + + private bool DeflateStored(bool flush, bool finish) + { + if (!flush && lookahead == 0) { + return false; + } + + strstart += lookahead; + lookahead = 0; + + int storedLen = strstart - blockStart; + + if ((storedLen >= DeflaterConstants.MAX_BLOCK_SIZE) || /* Block is full */ + (blockStart < WSIZE && storedLen >= MAX_DIST) || /* Block may move out of window */ + flush) { + bool lastBlock = finish; + if (storedLen > DeflaterConstants.MAX_BLOCK_SIZE) { + storedLen = DeflaterConstants.MAX_BLOCK_SIZE; + lastBlock = false; + } + +// if (DeflaterConstants.DEBUGGING) { +// Console.WriteLine("storedBlock["+storedLen+","+lastBlock+"]"); +// } + + huffman.FlushStoredBlock(window, blockStart, storedLen, lastBlock); + blockStart += storedLen; + return !lastBlock; + } + return true; + } + + private bool DeflateFast(bool flush, bool finish) + { + if (lookahead < MIN_LOOKAHEAD && !flush) { + return false; + } + + while (lookahead >= MIN_LOOKAHEAD || flush) { + if (lookahead == 0) { + /* We are flushing everything */ + huffman.FlushBlock(window, blockStart, strstart - blockStart, finish); + blockStart = strstart; + return false; + } + + int hashHead; + if (lookahead >= MIN_MATCH && + (hashHead = InsertString()) != 0 && + strategy != Deflater.HUFFMAN_ONLY && + strstart - hashHead <= MAX_DIST && + FindLongestMatch(hashHead)) { + /* longestMatch sets matchStart and matchLen */ +// if (DeflaterConstants.DEBUGGING) { +// for (int i = 0 ; i < matchLen; i++) { +// if (window[strstart+i] != window[matchStart + i]) { +// throw new Exception(); +// } +// } +// } + + huffman.TallyDist(strstart - matchStart, matchLen); + + lookahead -= matchLen; + if (matchLen <= max_lazy && lookahead >= MIN_MATCH) { + while (--matchLen > 0) { + ++strstart; + InsertString(); + } + ++strstart; + } else { + strstart += matchLen; + if (lookahead >= MIN_MATCH - 1) { + UpdateHash(); + } + } + matchLen = MIN_MATCH - 1; + continue; + } else { + /* No match found */ + huffman.TallyLit(window[strstart] & 0xff); + ++strstart; + --lookahead; + } + + if (huffman.IsFull()) { + bool lastBlock = finish && lookahead == 0; + huffman.FlushBlock(window, blockStart, strstart - blockStart, + lastBlock); + blockStart = strstart; + return !lastBlock; + } + } + return true; + } + + private bool DeflateSlow(bool flush, bool finish) + { + if (lookahead < MIN_LOOKAHEAD && !flush) { + return false; + } + + while (lookahead >= MIN_LOOKAHEAD || flush) { + if (lookahead == 0) { + if (prevAvailable) { + huffman.TallyLit(window[strstart-1] & 0xff); + } + prevAvailable = false; + + /* We are flushing everything */ + if (DeflaterConstants.DEBUGGING && !flush) { + throw new Exception("Not flushing, but no lookahead"); + } + huffman.FlushBlock(window, blockStart, strstart - blockStart, + finish); + blockStart = strstart; + return false; + } + + int prevMatch = matchStart; + int prevLen = matchLen; + if (lookahead >= MIN_MATCH) { + int hashHead = InsertString(); + if (strategy != Deflater.HUFFMAN_ONLY && hashHead != 0 && strstart - hashHead <= MAX_DIST && FindLongestMatch(hashHead)) + { + /* longestMatch sets matchStart and matchLen */ + + /* Discard match if too small and too far away */ + if (matchLen <= 5 && (strategy == Deflater.FILTERED || (matchLen == MIN_MATCH && strstart - matchStart > TOO_FAR))) { + matchLen = MIN_MATCH - 1; + } + } + } + + /* previous match was better */ + if (prevLen >= MIN_MATCH && matchLen <= prevLen) { +// if (DeflaterConstants.DEBUGGING) { +// for (int i = 0 ; i < matchLen; i++) { +// if (window[strstart-1+i] != window[prevMatch + i]) +// throw new Exception(); +// } +// } + huffman.TallyDist(strstart - 1 - prevMatch, prevLen); + prevLen -= 2; + do { + strstart++; + lookahead--; + if (lookahead >= MIN_MATCH) { + InsertString(); + } + } while (--prevLen > 0); + strstart ++; + lookahead--; + prevAvailable = false; + matchLen = MIN_MATCH - 1; + } else { + if (prevAvailable) { + huffman.TallyLit(window[strstart-1] & 0xff); + } + prevAvailable = true; + strstart++; + lookahead--; + } + + if (huffman.IsFull()) { + int len = strstart - blockStart; + if (prevAvailable) { + len--; + } + bool lastBlock = (finish && lookahead == 0 && !prevAvailable); + huffman.FlushBlock(window, blockStart, len, lastBlock); + blockStart += len; + return !lastBlock; + } + } + return true; + } + + public bool Deflate(bool flush, bool finish) + { + bool progress; + do { + FillWindow(); + bool canFlush = flush && inputOff == inputEnd; +// if (DeflaterConstants.DEBUGGING) { +// Console.WriteLine("window: ["+blockStart+","+strstart+"," +// +lookahead+"], "+comprFunc+","+canFlush); +// } + switch (comprFunc) { + case DEFLATE_STORED: + progress = DeflateStored(canFlush, finish); + break; + case DEFLATE_FAST: + progress = DeflateFast(canFlush, finish); + break; + case DEFLATE_SLOW: + progress = DeflateSlow(canFlush, finish); + break; + default: + throw new InvalidOperationException("unknown comprFunc"); + } + } while (pending.IsFlushed && progress); /* repeat while we have no pending output and progress was made */ + return progress; + } + + public void SetInput(byte[] buf, int off, int len) + { + if (inputOff < inputEnd) { + throw new InvalidOperationException("Old input was not completely processed"); + } + + int end = off + len; + + /* We want to throw an ArrayIndexOutOfBoundsException early. The + * check is very tricky: it also handles integer wrap around. + */ + if (0 > off || off > end || end > buf.Length) { + throw new ArgumentOutOfRangeException(); + } + + inputBuf = buf; + inputOff = off; + inputEnd = end; + } + + public bool NeedsInput() + { + return inputEnd == inputOff; + } + } +} + +namespace NZlib.Compression { + + /// + /// This is the DeflaterHuffman class. + /// + /// This class is not thread safe. This is inherent in the API, due + /// to the split of deflate and setInput. + /// + /// author of the original java version : Jochen Hoenicke + /// + public class DeflaterHuffman + { + private static int BUFSIZE = 1 << (DeflaterConstants.DEFAULT_MEM_LEVEL + 6); + private static int LITERAL_NUM = 286; + private static int DIST_NUM = 30; + private static int BITLEN_NUM = 19; + private static int REP_3_6 = 16; + private static int REP_3_10 = 17; + private static int REP_11_138 = 18; + private static int EOF_SYMBOL = 256; + private static int[] BL_ORDER = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + + private static byte[] bit4Reverse = { + 0, + 8, + 4, + 12, + 2, + 10, + 6, + 14, + 1, + 9, + 5, + 13, + 3, + 11, + 7, + 15 + }; + + + public class Tree + { + public short[] freqs; + public short[] codes; + public byte[] length; + public int[] bl_counts; + public int minNumCodes, numCodes; + public int maxLength; + DeflaterHuffman dh; + + public Tree(DeflaterHuffman dh, int elems, int minCodes, int maxLength) + { + this.dh = dh; + this.minNumCodes = minCodes; + this.maxLength = maxLength; + freqs = new short[elems]; + bl_counts = new int[maxLength]; + } + + public void Reset() + { + for (int i = 0; i < freqs.Length; i++) { + freqs[i] = 0; + } + codes = null; + length = null; + } + + public void WriteSymbol(int code) + { +// if (DeflaterConstants.DEBUGGING) { +// freqs[code]--; +// // Console.Write("writeSymbol("+freqs.length+","+code+"): "); +// } + dh.pending.WriteBits(codes[code] & 0xffff, length[code]); + } + + public void CheckEmpty() + { + bool empty = true; + for (int i = 0; i < freqs.Length; i++) { + if (freqs[i] != 0) { + Console.WriteLine("freqs["+i+"] == "+freqs[i]); + empty = false; + } + } + if (!empty) { + throw new Exception(); + } + Console.WriteLine("checkEmpty suceeded!"); + } + + public void SetStaticCodes(short[] stCodes, byte[] stLength) + { + codes = stCodes; + length = stLength; + } + + public void BuildCodes() + { + int numSymbols = freqs.Length; + int[] nextCode = new int[maxLength]; + int code = 0; + codes = new short[freqs.Length]; + +// if (DeflaterConstants.DEBUGGING) { +// Console.WriteLine("buildCodes: "+freqs.Length); +// } + + for (int bits = 0; bits < maxLength; bits++) { + nextCode[bits] = code; + code += bl_counts[bits] << (15 - bits); +// if (DeflaterConstants.DEBUGGING) { +// Console.WriteLine("bits: "+(bits+1)+" count: "+bl_counts[bits] +// +" nextCode: "+code); // HACK : Integer.toHexString( +// } + } + if (DeflaterConstants.DEBUGGING && code != 65536) { + throw new Exception("Inconsistent bl_counts!"); + } + + for (int i=0; i < numCodes; i++) { + int bits = length[i]; + if (bits > 0) { +// if (DeflaterConstants.DEBUGGING) { +// Console.WriteLine("codes["+i+"] = rev(" + nextCode[bits-1]+")," // HACK : Integer.toHexString( +// +bits); +// } + codes[i] = BitReverse(nextCode[bits-1]); + nextCode[bits-1] += 1 << (16 - bits); + } + } + } + + private void BuildLength(int[] childs) + { + this.length = new byte [freqs.Length]; + int numNodes = childs.Length / 2; + int numLeafs = (numNodes + 1) / 2; + int overflow = 0; + + for (int i = 0; i < maxLength; i++) { + bl_counts[i] = 0; + } + + /* First calculate optimal bit lengths */ + int[] lengths = new int[numNodes]; + lengths[numNodes-1] = 0; + + for (int i = numNodes - 1; i >= 0; i--) { + if (childs[2*i+1] != -1) { + int bitLength = lengths[i] + 1; + if (bitLength > maxLength) { + bitLength = maxLength; + overflow++; + } + lengths[childs[2*i]] = lengths[childs[2*i+1]] = bitLength; + } else { + /* A leaf node */ + int bitLength = lengths[i]; + bl_counts[bitLength - 1]++; + this.length[childs[2*i]] = (byte) lengths[i]; + } + } + +// if (DeflaterConstants.DEBUGGING) { +// Console.WriteLine("Tree "+freqs.Length+" lengths:"); +// for (int i=0; i < numLeafs; i++) { +// Console.WriteLine("Node "+childs[2*i]+" freq: "+freqs[childs[2*i]] +// + " len: "+length[childs[2*i]]); +// } +// } + + if (overflow == 0) { + return; + } + + int incrBitLen = maxLength - 1; + do { + /* Find the first bit length which could increase: */ + while (bl_counts[--incrBitLen] == 0) + ; + + /* Move this node one down and remove a corresponding + * amount of overflow nodes. + */ + do { + bl_counts[incrBitLen]--; + bl_counts[++incrBitLen]++; + overflow -= 1 << (maxLength - 1 - incrBitLen); + } while (overflow > 0 && incrBitLen < maxLength - 1); + } while (overflow > 0); + + /* We may have overshot above. Move some nodes from maxLength to + * maxLength-1 in that case. + */ + bl_counts[maxLength-1] += overflow; + bl_counts[maxLength-2] -= overflow; + + /* Now recompute all bit lengths, scanning in increasing + * frequency. It is simpler to reconstruct all lengths instead of + * fixing only the wrong ones. This idea is taken from 'ar' + * written by Haruhiko Okumura. + * + * The nodes were inserted with decreasing frequency into the childs + * array. + */ + int nodePtr = 2 * numLeafs; + for (int bits = maxLength; bits != 0; bits--) { + int n = bl_counts[bits-1]; + while (n > 0) { + int childPtr = 2*childs[nodePtr++]; + if (childs[childPtr + 1] == -1) { + /* We found another leaf */ + length[childs[childPtr]] = (byte) bits; + n--; + } + } + } +// if (DeflaterConstants.DEBUGGING) { +// Console.WriteLine("*** After overflow elimination. ***"); +// for (int i=0; i < numLeafs; i++) { +// Console.WriteLine("Node "+childs[2*i]+" freq: "+freqs[childs[2*i]] +// + " len: "+length[childs[2*i]]); +// } +// } + } + + public void BuildTree() + { + int numSymbols = freqs.Length; + + /* heap is a priority queue, sorted by frequency, least frequent + * nodes first. The heap is a binary tree, with the property, that + * the parent node is smaller than both child nodes. This assures + * that the smallest node is the first parent. + * + * The binary tree is encoded in an array: 0 is root node and + * the nodes 2*n+1, 2*n+2 are the child nodes of node n. + */ + int[] heap = new int[numSymbols]; + int heapLen = 0; + int maxCode = 0; + for (int n = 0; n < numSymbols; n++) { + int freq = freqs[n]; + if (freq != 0) { + /* Insert n into heap */ + int pos = heapLen++; + int ppos; + while (pos > 0 && freqs[heap[ppos = (pos - 1) / 2]] > freq) { + heap[pos] = heap[ppos]; + pos = ppos; + } + heap[pos] = n; + + maxCode = n; + } + } + + /* We could encode a single literal with 0 bits but then we + * don't see the literals. Therefore we force at least two + * literals to avoid this case. We don't care about order in + * this case, both literals get a 1 bit code. + */ + while (heapLen < 2) { + int node = maxCode < 2 ? ++maxCode : 0; + heap[heapLen++] = node; + } + + numCodes = Math.Max(maxCode + 1, minNumCodes); + + int numLeafs = heapLen; + int[] childs = new int[4*heapLen - 2]; + int[] values = new int[2*heapLen - 1]; + int numNodes = numLeafs; + for (int i = 0; i < heapLen; i++) { + int node = heap[i]; + childs[2*i] = node; + childs[2*i+1] = -1; + values[i] = freqs[node] << 8; + heap[i] = i; + } + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + do { + int first = heap[0]; + int last = heap[--heapLen]; + + /* Propagate the hole to the leafs of the heap */ + int ppos = 0; + int path = 1; + while (path < heapLen) { + if (path + 1 < heapLen && values[heap[path]] > values[heap[path+1]]) { + path++; + } + + heap[ppos] = heap[path]; + ppos = path; + path = path * 2 + 1; + } + + /* Now propagate the last element down along path. Normally + * it shouldn't go too deep. + */ + int lastVal = values[last]; + while ((path = ppos) > 0 && values[heap[ppos = (path - 1)/2]] > lastVal) { + heap[path] = heap[ppos]; + } + heap[path] = last; + + + int second = heap[0]; + + /* Create a new node father of first and second */ + last = numNodes++; + childs[2*last] = first; + childs[2*last+1] = second; + int mindepth = Math.Min(values[first] & 0xff, values[second] & 0xff); + values[last] = lastVal = values[first] + values[second] - mindepth + 1; + + /* Again, propagate the hole to the leafs */ + ppos = 0; + path = 1; + while (path < heapLen) { + if (path + 1 < heapLen && values[heap[path]] > values[heap[path+1]]) { + path++; + } + + heap[ppos] = heap[path]; + ppos = path; + path = ppos * 2 + 1; + } + + /* Now propagate the new element down along path */ + while ((path = ppos) > 0 && values[heap[ppos = (path - 1)/2]] > lastVal) { + heap[path] = heap[ppos]; + } + heap[path] = last; + } while (heapLen > 1); + + if (heap[0] != childs.Length / 2 - 1) { + throw new Exception("Weird!"); + } + BuildLength(childs); + } + + public int GetEncodedLength() + { + int len = 0; + for (int i = 0; i < freqs.Length; i++) { + len += freqs[i] * length[i]; + } + return len; + } + + public void CalcBLFreq(Tree blTree) + { + int max_count; /* max repeat count */ + int min_count; /* min repeat count */ + int count; /* repeat count of the current code */ + int curlen = -1; /* length of current code */ + + int i = 0; + while (i < numCodes) { + count = 1; + int nextlen = length[i]; + if (nextlen == 0) { + max_count = 138; + min_count = 3; + } else { + max_count = 6; + min_count = 3; + if (curlen != nextlen) { + blTree.freqs[nextlen]++; + count = 0; + } + } + curlen = nextlen; + i++; + + while (i < numCodes && curlen == length[i]) { + i++; + if (++count >= max_count) { + break; + } + } + + if (count < min_count) { + blTree.freqs[curlen] += (short)count; + } else if (curlen != 0) { + blTree.freqs[REP_3_6]++; + } else if (count <= 10) { + blTree.freqs[REP_3_10]++; + } else { + blTree.freqs[REP_11_138]++; + } + } + } + + public void WriteTree(Tree blTree) + { + int max_count; /* max repeat count */ + int min_count; /* min repeat count */ + int count; /* repeat count of the current code */ + int curlen = -1; /* length of current code */ + + int i = 0; + while (i < numCodes) { + count = 1; + int nextlen = length[i]; + if (nextlen == 0) { + max_count = 138; + min_count = 3; + } else { + max_count = 6; + min_count = 3; + if (curlen != nextlen) { + blTree.WriteSymbol(nextlen); + count = 0; + } + } + curlen = nextlen; + i++; + + while (i < numCodes && curlen == length[i]) { + i++; + if (++count >= max_count) { + break; + } + } + + if (count < min_count) { + while (count-- > 0) { + blTree.WriteSymbol(curlen); + } + } + else if (curlen != 0) { + blTree.WriteSymbol(REP_3_6); + dh.pending.WriteBits(count - 3, 2); + } else if (count <= 10) { + blTree.WriteSymbol(REP_3_10); + dh.pending.WriteBits(count - 3, 3); + } else { + blTree.WriteSymbol(REP_11_138); + dh.pending.WriteBits(count - 11, 7); + } + } + } + } + + public DeflaterPending pending; + private Tree literalTree, distTree, blTree; + + private short[] d_buf; + private byte[] l_buf; + private int last_lit; + private int extra_bits; + + private static short[] staticLCodes; + private static byte[] staticLLength; + private static short[] staticDCodes; + private static byte[] staticDLength; + + /// + /// Reverse the bits of a 16 bit value. + /// + public static short BitReverse(int value) + { + return (short) (bit4Reverse[value & 0xF] << 12 + | bit4Reverse[(value >> 4) & 0xF] << 8 + | bit4Reverse[(value >> 8) & 0xF] << 4 + | bit4Reverse[value >> 12]); + } + + + static DeflaterHuffman() + { + /* See RFC 1951 3.2.6 */ + /* Literal codes */ + staticLCodes = new short[LITERAL_NUM]; + staticLLength = new byte[LITERAL_NUM]; + int i = 0; + while (i < 144) { + staticLCodes[i] = BitReverse((0x030 + i) << 8); + staticLLength[i++] = 8; + } + while (i < 256) { + staticLCodes[i] = BitReverse((0x190 - 144 + i) << 7); + staticLLength[i++] = 9; + } + while (i < 280) { + staticLCodes[i] = BitReverse((0x000 - 256 + i) << 9); + staticLLength[i++] = 7; + } + while (i < LITERAL_NUM) { + staticLCodes[i] = BitReverse((0x0c0 - 280 + i) << 8); + staticLLength[i++] = 8; + } + + /* Distant codes */ + staticDCodes = new short[DIST_NUM]; + staticDLength = new byte[DIST_NUM]; + for (i = 0; i < DIST_NUM; i++) { + staticDCodes[i] = BitReverse(i << 11); + staticDLength[i] = 5; + } + } + + public DeflaterHuffman(DeflaterPending pending) + { + this.pending = pending; + + literalTree = new Tree(this, LITERAL_NUM, 257, 15); + distTree = new Tree(this, DIST_NUM, 1, 15); + blTree = new Tree(this, BITLEN_NUM, 4, 7); + + d_buf = new short[BUFSIZE]; + l_buf = new byte [BUFSIZE]; + } + + public void Reset() + { + last_lit = 0; + extra_bits = 0; + literalTree.Reset(); + distTree.Reset(); + blTree.Reset(); + } + + private int L_code(int len) + { + if (len == 255) { + return 285; + } + + int code = 257; + while (len >= 8) { + code += 4; + len >>= 1; + } + return code + len; + } + + private int D_code(int distance) + { + int code = 0; + while (distance >= 4) { + code += 2; + distance >>= 1; + } + return code + distance; + } + + public void SendAllTrees(int blTreeCodes) + { + blTree.BuildCodes(); + literalTree.BuildCodes(); + distTree.BuildCodes(); + pending.WriteBits(literalTree.numCodes - 257, 5); + pending.WriteBits(distTree.numCodes - 1, 5); + pending.WriteBits(blTreeCodes - 4, 4); + for (int rank = 0; rank < blTreeCodes; rank++) { + pending.WriteBits(blTree.length[BL_ORDER[rank]], 3); + } + literalTree.WriteTree(blTree); + distTree.WriteTree(blTree); +// if (DeflaterConstants.DEBUGGING) { +// blTree.CheckEmpty(); +// } + } + + public void CompressBlock() + { + for (int i = 0; i < last_lit; i++) { + int litlen = l_buf[i] & 0xff; + int dist = d_buf[i]; + if (dist-- != 0) { +// if (DeflaterConstants.DEBUGGING) { +// Console.Write("["+(dist+1)+","+(litlen+3)+"]: "); +// } + + int lc = L_code(litlen); + literalTree.WriteSymbol(lc); + + int bits = (lc - 261) / 4; + if (bits > 0 && bits <= 5) { + pending.WriteBits(litlen & ((1 << bits) - 1), bits); + } + + int dc = D_code(dist); + distTree.WriteSymbol(dc); + + bits = dc / 2 - 1; + if (bits > 0) { + pending.WriteBits(dist & ((1 << bits) - 1), bits); + } + } else { +// if (DeflaterConstants.DEBUGGING) { +// if (litlen > 32 && litlen < 127) { +// Console.Write("("+(char)litlen+"): "); +// } else { +// Console.Write("{"+litlen+"}: "); +// } +// } + literalTree.WriteSymbol(litlen); + } + } +// if (DeflaterConstants.DEBUGGING) { +// Console.Write("EOF: "); +// } + literalTree.WriteSymbol(EOF_SYMBOL); +// if (DeflaterConstants.DEBUGGING) { +// literalTree.CheckEmpty(); +// distTree.CheckEmpty(); +// } + } + + public void FlushStoredBlock(byte[] stored, int stored_offset, int stored_len, bool lastBlock) + { +// if (DeflaterConstants.DEBUGGING) { +// Console.WriteLine("Flushing stored block "+ stored_len); +// } + pending.WriteBits((DeflaterConstants.STORED_BLOCK << 1) + + (lastBlock ? 1 : 0), 3); + pending.AlignToByte(); + pending.WriteShort(stored_len); + pending.WriteShort(~stored_len); + pending.WriteBlock(stored, stored_offset, stored_len); + Reset(); + } + + public void FlushBlock(byte[] stored, int stored_offset, int stored_len, bool lastBlock) + { + literalTree.freqs[EOF_SYMBOL]++; + + /* Build trees */ + literalTree.BuildTree(); + distTree.BuildTree(); + + /* Calculate bitlen frequency */ + literalTree.CalcBLFreq(blTree); + distTree.CalcBLFreq(blTree); + + /* Build bitlen tree */ + blTree.BuildTree(); + + int blTreeCodes = 4; + for (int i = 18; i > blTreeCodes; i--) { + if (blTree.length[BL_ORDER[i]] > 0) { + blTreeCodes = i+1; + } + } + int opt_len = 14 + blTreeCodes * 3 + blTree.GetEncodedLength() + + literalTree.GetEncodedLength() + distTree.GetEncodedLength() + + extra_bits; + + int static_len = extra_bits; + for (int i = 0; i < LITERAL_NUM; i++) { + static_len += literalTree.freqs[i] * staticLLength[i]; + } + for (int i = 0; i < DIST_NUM; i++) { + static_len += distTree.freqs[i] * staticDLength[i]; + } + if (opt_len >= static_len) { + /* Force static trees */ + opt_len = static_len; + } + + if (stored_offset >= 0 && stored_len+4 < opt_len >> 3) { + /* Store Block */ +// if (DeflaterConstants.DEBUGGING) { +// Console.WriteLine("Storing, since " + stored_len + " < " + opt_len +// + " <= " + static_len); +// } + FlushStoredBlock(stored, stored_offset, stored_len, lastBlock); + } else if (opt_len == static_len) { + /* Encode with static tree */ + pending.WriteBits((DeflaterConstants.STATIC_TREES << 1) + (lastBlock ? 1 : 0), 3); + literalTree.SetStaticCodes(staticLCodes, staticLLength); + distTree.SetStaticCodes(staticDCodes, staticDLength); + CompressBlock(); + Reset(); + } else { + /* Encode with dynamic tree */ + pending.WriteBits((DeflaterConstants.DYN_TREES << 1) + (lastBlock ? 1 : 0), 3); + SendAllTrees(blTreeCodes); + CompressBlock(); + Reset(); + } + } + + public bool IsFull() + { + return last_lit + 16 >= BUFSIZE; // HACK: This was == 'last_lit == BUFSIZE', but errors occured with DeflateFast + } + + public bool TallyLit(int lit) + { +// if (DeflaterConstants.DEBUGGING) { +// if (lit > 32 && lit < 127) { +// Console.WriteLine("("+(char)lit+")"); +// } else { +// Console.WriteLine("{"+lit+"}"); +// } +// } + d_buf[last_lit] = 0; + l_buf[last_lit++] = (byte)lit; + literalTree.freqs[lit]++; + return IsFull(); + } + + public bool TallyDist(int dist, int len) + { +// if (DeflaterConstants.DEBUGGING) { +// Console.WriteLine("["+dist+","+len+"]"); +// } + + d_buf[last_lit] = (short)dist; + l_buf[last_lit++] = (byte)(len - 3); + + int lc = L_code(len - 3); + literalTree.freqs[lc]++; + if (lc >= 265 && lc < 285) { + extra_bits += (lc - 261) / 4; + } + + int dc = D_code(dist - 1); + distTree.freqs[dc]++; + if (dc >= 4) { + extra_bits += dc / 2 - 1; + } + return IsFull(); + } + } +} + +namespace NZlib.Compression { + + /// + /// This class stores the pending output of the Deflater. + /// + /// author of the original java version : Jochen Hoenicke + /// + public class DeflaterPending : PendingBuffer + { + public DeflaterPending() : base(DeflaterConstants.PENDING_BUF_SIZE) + { + } + } +} + +namespace NZlib.Compression { + + /// + /// Inflater is used to decompress data that has been compressed according + /// to the "deflate" standard described in rfc1950. + /// + /// The usage is as following. First you have to set some input with + /// setInput(), then inflate() it. If inflate doesn't + /// inflate any bytes there may be three reasons: + ///
    + ///
  • needsInput() returns true because the input buffer is empty. + /// You have to provide more input with setInput(). + /// NOTE: needsInput() also returns true when, the stream is finished. + ///
  • + ///
  • needsDictionary() returns true, you have to provide a preset + /// dictionary with setDictionary().
  • + ///
  • finished() returns true, the inflater has finished.
  • + ///
+ /// Once the first output byte is produced, a dictionary will not be + /// needed at a later stage. + /// + /// author of the original java version : John Leuner, Jochen Hoenicke + ///
+ public class Inflater + { + /// + /// Copy lengths for literal codes 257..285 + /// + private static int[] CPLENS = { + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 + }; + + /// + /// Extra bits for literal codes 257..285 + /// + private static int[] CPLEXT = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 + }; + + /// + /// Copy offsets for distance codes 0..29 + /// + private static int[] CPDIST = { + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577 + }; + + /// + /// Extra bits for distance codes + /// + private static int[] CPDEXT = { + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13 + }; + + /// + /// This are the state in which the inflater can be. + /// + private const int DECODE_HEADER = 0; + private const int DECODE_DICT = 1; + private const int DECODE_BLOCKS = 2; + private const int DECODE_STORED_LEN1 = 3; + private const int DECODE_STORED_LEN2 = 4; + private const int DECODE_STORED = 5; + private const int DECODE_DYN_HEADER = 6; + private const int DECODE_HUFFMAN = 7; + private const int DECODE_HUFFMAN_LENBITS = 8; + private const int DECODE_HUFFMAN_DIST = 9; + private const int DECODE_HUFFMAN_DISTBITS = 10; + private const int DECODE_CHKSUM = 11; + private const int FINISHED = 12; + + /// + /// This variable contains the current state. + /// + private int mode; + + /// + /// The adler checksum of the dictionary or of the decompressed + /// stream, as it is written in the header resp. footer of the + /// compressed stream. + /// Only valid if mode is DECODE_DICT or DECODE_CHKSUM. + /// + private int readAdler; + + /// + /// The number of bits needed to complete the current state. This + /// is valid, if mode is DECODE_DICT, DECODE_CHKSUM, + /// DECODE_HUFFMAN_LENBITS or DECODE_HUFFMAN_DISTBITS. + /// + private int neededBits; + private int repLength, repDist; + private int uncomprLen; + + /// + /// True, if the last block flag was set in the last block of the + /// inflated stream. This means that the stream ends after the + /// current block. + /// + private bool isLastBlock; + + /// + /// The total number of inflated bytes. + /// + private int totalOut; + + /// + /// The total number of bytes set with setInput(). This is not the + /// value returned by getTotalIn(), since this also includes the + /// unprocessed input. + /// + private int totalIn; + + /// + /// This variable stores the nowrap flag that was given to the constructor. + /// True means, that the inflated stream doesn't contain a header nor the + /// checksum in the footer. + /// + private bool nowrap; + + private StreamManipulator input; + private OutputWindow outputWindow; + private InflaterDynHeader dynHeader; + private InflaterHuffmanTree litlenTree, distTree; + private Adler32 adler; + + /// + /// Creates a new inflater. + /// + public Inflater() : this(false) + { + } + + /// + /// Creates a new inflater. + /// + /// + /// true if no header and checksum field appears in the + /// stream. This is used for GZIPed input. For compatibility with + /// Sun JDK you should provide one byte of input more than needed in + /// this case. + /// + public Inflater(bool nowrap) + { + this.nowrap = nowrap; + this.adler = new Adler32(); + input = new StreamManipulator(); + outputWindow = new OutputWindow(); + mode = nowrap ? DECODE_BLOCKS : DECODE_HEADER; + } + + /// + /// Resets the inflater so that a new stream can be decompressed. All + /// pending input and output will be discarded. + /// + public void Reset() + { + mode = nowrap ? DECODE_BLOCKS : DECODE_HEADER; + totalIn = totalOut = 0; + input.Reset(); + outputWindow.Reset(); + dynHeader = null; + litlenTree = null; + distTree = null; + isLastBlock = false; + adler.Reset(); + } + + /// + /// Decodes the deflate header. + /// + /// + /// false if more input is needed. + /// + /// + /// if header is invalid. + /// + private bool DecodeHeader() + { + int header = input.PeekBits(16); + if (header < 0) { + return false; + } + input.DropBits(16); + /* The header is written in "wrong" byte order */ + header = ((header << 8) | (header >> 8)) & 0xffff; + if (header % 31 != 0) { + throw new FormatException("Header checksum illegal"); + } + + if ((header & 0x0f00) != (Deflater.DEFLATED << 8)) { + throw new FormatException("Compression Method unknown"); + } + + /* Maximum size of the backwards window in bits. + * We currently ignore this, but we could use it to make the + * inflater window more space efficient. On the other hand the + * full window (15 bits) is needed most times, anyway. + int max_wbits = ((header & 0x7000) >> 12) + 8; + */ + + if ((header & 0x0020) == 0) { // Dictionary flag? + mode = DECODE_BLOCKS; + } else { + mode = DECODE_DICT; + neededBits = 32; + } + return true; + } + + /// + /// Decodes the dictionary checksum after the deflate header. + /// + /// + /// false if more input is needed. + /// + private bool DecodeDict() + { + while (neededBits > 0) { + int dictByte = input.PeekBits(8); + if (dictByte < 0) { + return false; + } + input.DropBits(8); + readAdler = (readAdler << 8) | dictByte; + neededBits -= 8; + } + return false; + } + + /// + /// Decodes the huffman encoded symbols in the input stream. + /// + /// + /// false if more input is needed, true if output window is + /// full or the current block ends. + /// + /// + /// if deflated stream is invalid. + /// + private bool DecodeHuffman() + { + int free = outputWindow.GetFreeSpace(); + while (free >= 258) { + int symbol; + switch (mode) { + case DECODE_HUFFMAN: + /* This is the inner loop so it is optimized a bit */ + while (((symbol = litlenTree.GetSymbol(input)) & ~0xff) == 0) { + outputWindow.Write(symbol); + if (--free < 258) { + return true; + } + } + if (symbol < 257) { + if (symbol < 0) { + return false; + } else { + /* symbol == 256: end of block */ + distTree = null; + litlenTree = null; + mode = DECODE_BLOCKS; + return true; + } + } + + try { + repLength = CPLENS[symbol - 257]; + neededBits = CPLEXT[symbol - 257]; + } catch (Exception) { + throw new FormatException("Illegal rep length code"); + } + goto case DECODE_HUFFMAN_LENBITS;/* fall through */ + case DECODE_HUFFMAN_LENBITS: + if (neededBits > 0) { + mode = DECODE_HUFFMAN_LENBITS; + int i = input.PeekBits(neededBits); + if (i < 0) { + return false; + } + input.DropBits(neededBits); + repLength += i; + } + mode = DECODE_HUFFMAN_DIST; + goto case DECODE_HUFFMAN_DIST;/* fall through */ + case DECODE_HUFFMAN_DIST: + symbol = distTree.GetSymbol(input); + if (symbol < 0) { + return false; + } + try { + repDist = CPDIST[symbol]; + neededBits = CPDEXT[symbol]; + } catch (Exception) { + throw new FormatException("Illegal rep dist code"); + } + + goto case DECODE_HUFFMAN_DISTBITS;/* fall through */ + case DECODE_HUFFMAN_DISTBITS: + if (neededBits > 0) { + mode = DECODE_HUFFMAN_DISTBITS; + int i = input.PeekBits(neededBits); + if (i < 0) { + return false; + } + input.DropBits(neededBits); + repDist += i; + } + outputWindow.Repeat(repLength, repDist); + free -= repLength; + mode = DECODE_HUFFMAN; + break; + default: + throw new FormatException(); + } + } + return true; + } + + /// + /// Decodes the adler checksum after the deflate stream. + /// + /// + /// false if more input is needed. + /// + /// + /// DataFormatException, if checksum doesn't match. + /// + private bool DecodeChksum() + { + while (neededBits > 0) { + int chkByte = input.PeekBits(8); + if (chkByte < 0) { + return false; + } + input.DropBits(8); + readAdler = (readAdler << 8) | chkByte; + neededBits -= 8; + } + if ((int) adler.Value != readAdler) { + throw new FormatException("Adler chksum doesn't match: " + + (int)adler.Value + + " vs. " + readAdler); + } + mode = FINISHED; + return false; + } + + /// + /// Decodes the deflated stream. + /// + /// + /// false if more input is needed, or if finished. + /// + /// + /// DataFormatException, if deflated stream is invalid. + /// + private bool Decode() + { + switch (mode) { + case DECODE_HEADER: + return DecodeHeader(); + case DECODE_DICT: + return DecodeDict(); + case DECODE_CHKSUM: + return DecodeChksum(); + + case DECODE_BLOCKS: + if (isLastBlock) { + if (nowrap) { + mode = FINISHED; + return false; + } else { + input.SkipToByteBoundary(); + neededBits = 32; + mode = DECODE_CHKSUM; + return true; + } + } + + int type = input.PeekBits(3); + if (type < 0) { + return false; + } + input.DropBits(3); + + if ((type & 1) != 0) { + isLastBlock = true; + } + switch (type >> 1) { + case DeflaterConstants.STORED_BLOCK: + input.SkipToByteBoundary(); + mode = DECODE_STORED_LEN1; + break; + case DeflaterConstants.STATIC_TREES: + litlenTree = InflaterHuffmanTree.defLitLenTree; + distTree = InflaterHuffmanTree.defDistTree; + mode = DECODE_HUFFMAN; + break; + case DeflaterConstants.DYN_TREES: + dynHeader = new InflaterDynHeader(); + mode = DECODE_DYN_HEADER; + break; + default: + throw new FormatException("Unknown block type "+type); + } + return true; + + case DECODE_STORED_LEN1: { + if ((uncomprLen = input.PeekBits(16)) < 0) { + return false; + } + input.DropBits(16); + mode = DECODE_STORED_LEN2; + } + goto case DECODE_STORED_LEN2; /* fall through */ + case DECODE_STORED_LEN2: { + int nlen = input.PeekBits(16); + if (nlen < 0) { + return false; + } + input.DropBits(16); + if (nlen != (uncomprLen ^ 0xffff)) { + throw new FormatException("broken uncompressed block"); + } + mode = DECODE_STORED; + } + goto case DECODE_STORED;/* fall through */ + case DECODE_STORED: { + int more = outputWindow.CopyStored(input, uncomprLen); + uncomprLen -= more; + if (uncomprLen == 0) { + mode = DECODE_BLOCKS; + return true; + } + return !input.IsNeedingInput; + } + + case DECODE_DYN_HEADER: + if (!dynHeader.Decode(input)) { + return false; + } + + litlenTree = dynHeader.BuildLitLenTree(); + distTree = dynHeader.BuildDistTree(); + mode = DECODE_HUFFMAN; + goto case DECODE_HUFFMAN; /* fall through */ + case DECODE_HUFFMAN: + case DECODE_HUFFMAN_LENBITS: + case DECODE_HUFFMAN_DIST: + case DECODE_HUFFMAN_DISTBITS: + return DecodeHuffman(); + case FINISHED: + return false; + default: + throw new FormatException(); + } + } + + /// + /// Sets the preset dictionary. This should only be called, if + /// needsDictionary() returns true and it should set the same + /// dictionary, that was used for deflating. The getAdler() + /// function returns the checksum of the dictionary needed. + /// + /// + /// the dictionary. + /// + /// + /// if no dictionary is needed. + /// + /// + /// if the dictionary checksum is wrong. + /// + public void SetDictionary(byte[] buffer) + { + SetDictionary(buffer, 0, buffer.Length); + } + + /// + /// Sets the preset dictionary. This should only be called, if + /// needsDictionary() returns true and it should set the same + /// dictionary, that was used for deflating. The getAdler() + /// function returns the checksum of the dictionary needed. + /// + /// + /// the dictionary. + /// + /// + /// the offset into buffer where the dictionary starts. + /// + /// + /// the length of the dictionary. + /// + /// + /// if no dictionary is needed. + /// + /// + /// if the dictionary checksum is wrong. + /// + /// + /// if the off and/or len are wrong. + /// + public void SetDictionary(byte[] buffer, int off, int len) + { + if (!IsNeedingDictionary) { + throw new InvalidOperationException(); + } + + adler.Update(buffer, off, len); + if ((int) adler.Value != readAdler) { + throw new ArgumentException("Wrong adler checksum"); + } + adler.Reset(); + outputWindow.CopyDict(buffer, off, len); + mode = DECODE_BLOCKS; + } + + /// + /// Sets the input. This should only be called, if needsInput() + /// returns true. + /// + /// + /// the input. + /// + /// + /// if no input is needed. + /// + public void SetInput(byte[] buf) + { + SetInput(buf, 0, buf.Length); + } + + /// + /// Sets the input. This should only be called, if needsInput() + /// returns true. + /// + /// + /// the input. + /// + /// + /// the offset into buffer where the input starts. + /// + /// + /// the length of the input. + /// + /// + /// if no input is needed. + /// + /// + /// if the off and/or len are wrong. + /// + public void SetInput(byte[] buf, int off, int len) + { + input.SetInput(buf, off, len); + totalIn += len; + } + + /// + /// Inflates the compressed stream to the output buffer. If this + /// returns 0, you should check, whether needsDictionary(), + /// needsInput() or finished() returns true, to determine why no + /// further output is produced. + /// + /// + /// the output buffer. + /// + /// + /// the number of bytes written to the buffer, 0 if no further + /// output can be produced. + /// + /// + /// if buf has length 0. + /// + /// + /// if deflated stream is invalid. + /// + public int Inflate(byte[] buf) + { + return Inflate(buf, 0, buf.Length); + } + + /// + /// Inflates the compressed stream to the output buffer. If this + /// returns 0, you should check, whether needsDictionary(), + /// needsInput() or finished() returns true, to determine why no + /// further output is produced. + /// + /// + /// the output buffer. + /// + /// + /// the offset into buffer where the output should start. + /// + /// + /// the maximum length of the output. + /// + /// + /// the number of bytes written to the buffer, 0 if no further output can be produced. + /// + /// + /// if len is <= 0. + /// + /// + /// if the off and/or len are wrong. + /// + /// + /// if deflated stream is invalid. + /// + public int Inflate(byte[] buf, int off, int len) + { + if (len <= 0) { + throw new ArgumentOutOfRangeException("len <= 0"); + } + int count = 0; + int more; + do { + if (mode != DECODE_CHKSUM) { + /* Don't give away any output, if we are waiting for the + * checksum in the input stream. + * + * With this trick we have always: + * needsInput() and not finished() + * implies more output can be produced. + */ + more = outputWindow.CopyOutput(buf, off, len); + adler.Update(buf, off, more); + off += more; + count += more; + totalOut += more; + len -= more; + if (len == 0) { + return count; + } + } + } while (Decode() || (outputWindow.GetAvailable() > 0 && + mode != DECODE_CHKSUM)); + return count; + } + + /// + /// Returns true, if the input buffer is empty. + /// You should then call setInput(). + /// NOTE: This method also returns true when the stream is finished. + /// + public bool IsNeedingInput { + get { + return input.IsNeedingInput; + } + } + + /// + /// Returns true, if a preset dictionary is needed to inflate the input. + /// + public bool IsNeedingDictionary { + get { + return mode == DECODE_DICT && neededBits == 0; + } + } + + /// + /// Returns true, if the inflater has finished. This means, that no + /// input is needed and no output can be produced. + /// + public bool IsFinished { + get { + return mode == FINISHED && outputWindow.GetAvailable() == 0; + } + } + + /// + /// Gets the adler checksum. This is either the checksum of all + /// uncompressed bytes returned by inflate(), or if needsDictionary() + /// returns true (and thus no output was yet produced) this is the + /// adler checksum of the expected dictionary. + /// + /// + /// the adler checksum. + /// + public int Adler { + get { + return IsNeedingDictionary ? readAdler : (int) adler.Value; + } + } + + /// + /// Gets the total number of output bytes returned by inflate(). + /// + /// + /// the total number of output bytes. + /// + public int TotalOut { + get { + return totalOut; + } + } + + /// + /// Gets the total number of processed compressed input bytes. + /// + /// + /// the total number of bytes of processed input bytes. + /// + public int TotalIn { + get { + return totalIn - RemainingInput; + } + } + + /// + /// Gets the number of unprocessed input. Useful, if the end of the + /// stream is reached and you want to further process the bytes after + /// the deflate stream. + /// + /// + /// the number of bytes of the input which were not processed. + /// + public int RemainingInput { + get { + return input.AvailableBytes; + } + } + } +} + +namespace NZlib.Compression { + + public class InflaterDynHeader + { + private const int LNUM = 0; + private const int DNUM = 1; + private const int BLNUM = 2; + private const int BLLENS = 3; + private const int LLENS = 4; + private const int DLENS = 5; + private const int LREPS = 6; + private const int DREPS = 7; + private const int FINISH = 8; + + private byte[] blLens; + private byte[] litlenLens; + private byte[] distLens; + + private InflaterHuffmanTree blTree; + + private int mode; + private int lnum, dnum, blnum; + private int repBits; + private byte repeatedLen; + private int ptr; + + private static int[] BL_ORDER = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + + public InflaterDynHeader() + { + } + + public bool Decode(StreamManipulator input) + { + decode_loop: + for (;;) { + switch (mode) { + case LNUM: + lnum = input.PeekBits(5); + if (lnum < 0) { + return false; + } + lnum += 257; + input.DropBits(5); + litlenLens = new byte[lnum]; + // System.err.println("LNUM: "+lnum); + mode = DNUM; + goto case DNUM;/* fall through */ + case DNUM: + dnum = input.PeekBits(5); + if (dnum < 0) { + return false; + } + dnum++; + input.DropBits(5); + distLens = new byte[dnum]; + // System.err.println("DNUM: "+dnum); + mode = BLNUM; + goto case BLNUM;/* fall through */ + case BLNUM: + blnum = input.PeekBits(4); + if (blnum < 0) { + return false; + } + blnum += 4; + input.DropBits(4); + blLens = new byte[19]; + ptr = 0; + // System.err.println("BLNUM: "+blnum); + mode = BLLENS; + goto case BLLENS;/* fall through */ + case BLLENS: + while (ptr < blnum) { + int len = input.PeekBits(3); + if (len < 0) { + return false; + } + input.DropBits(3); + // System.err.println("blLens["+BL_ORDER[ptr]+"]: "+len); + blLens[BL_ORDER[ptr]] = (byte) len; + ptr++; + } + blTree = new InflaterHuffmanTree(blLens); + blLens = null; + ptr = 0; + mode = LLENS; + goto case LLENS;/* fall through */ + case LLENS: + while (ptr < lnum) { + int symbol = blTree.GetSymbol(input); + if (symbol < 0) { + return false; + } + switch (symbol) { + default: + // System.err.println("litlenLens["+ptr+"]: "+symbol); + litlenLens[ptr++] = (byte) symbol; + break; + case 16: /* repeat last len 3-6 times */ + if (ptr == 0) { + throw new Exception("Repeating, but no prev len"); + } + + // System.err.println("litlenLens["+ptr+"]: repeat"); + repeatedLen = litlenLens[ptr-1]; + repBits = 2; + for (int i = 3; i-- > 0; ) { + if (ptr >= lnum) { + throw new Exception(); + } + litlenLens[ptr++] = repeatedLen; + } + mode = LREPS; + goto decode_loop; + case 17: /* repeat zero 3-10 times */ + // System.err.println("litlenLens["+ptr+"]: zero repeat"); + repeatedLen = 0; + repBits = 3; + for (int i = 3; i-- > 0; ) { + if (ptr >= lnum) { + throw new Exception(); + } + litlenLens[ptr++] = repeatedLen; + } + mode = LREPS; + goto decode_loop; + case 18: /* repeat zero 11-138 times */ + // System.err.println("litlenLens["+ptr+"]: zero repeat"); + repeatedLen = 0; + repBits = 7; + for (int i = 11; i-- > 0; ) { + if (ptr >= lnum) { + throw new Exception(); + } + litlenLens[ptr++] = repeatedLen; + } + mode = LREPS; + goto decode_loop; + } + } + ptr = 0; + mode = DLENS; + goto case DLENS;/* fall through */ + case DLENS: + while (ptr < dnum) { + int symbol = blTree.GetSymbol(input); + if (symbol < 0) { + return false; + } + switch (symbol) { + default: + distLens[ptr++] = (byte) symbol; + // System.err.println("distLens["+ptr+"]: "+symbol); + break; + case 16: /* repeat last len 3-6 times */ + if (ptr == 0) { + throw new Exception("Repeating, but no prev len"); + } + // System.err.println("distLens["+ptr+"]: repeat"); + repeatedLen = distLens[ptr-1]; + repBits = 2; + for (int i = 3; i-- > 0; ) { + if (ptr >= dnum) { + throw new Exception(); + } + distLens[ptr++] = repeatedLen; + } + mode = DREPS; + goto decode_loop; + case 17: /* repeat zero 3-10 times */ + // System.err.println("distLens["+ptr+"]: repeat zero"); + repeatedLen = 0; + repBits = 3; + for (int i = 3; i-- > 0; ) { + if (ptr >= dnum) { + throw new Exception(); + } + distLens[ptr++] = repeatedLen; + } + mode = DREPS; + goto decode_loop; + case 18: /* repeat zero 11-138 times */ + // System.err.println("distLens["+ptr+"]: repeat zero"); + repeatedLen = 0; + repBits = 7; + for (int i = 11; i-- > 0; ) { + if (ptr >= dnum) { + throw new Exception(); + } + distLens[ptr++] = repeatedLen; + } + mode = DREPS; + goto decode_loop; + } + } + mode = FINISH; + return true; + case LREPS: + { + int count = input.PeekBits(repBits); + if (count < 0) { + return false; + } + input.DropBits(repBits); + // System.err.println("litlenLens repeat: "+repBits); + while (count-- > 0) { + if (ptr >= lnum) { + throw new Exception(); + } + litlenLens[ptr++] = repeatedLen; + } + } + mode = LLENS; + goto decode_loop; + case DREPS: + { + int count = input.PeekBits(repBits); + if (count < 0) { + return false; + } + input.DropBits(repBits); + while (count-- > 0) { + if (ptr >= dnum) { + throw new Exception(); + } + distLens[ptr++] = repeatedLen; + } + } + mode = DLENS; + goto decode_loop; + } + } + } + + public InflaterHuffmanTree BuildLitLenTree() + { + return new InflaterHuffmanTree(litlenLens); + } + + public InflaterHuffmanTree BuildDistTree() + { + return new InflaterHuffmanTree(distLens); + } + } +} + +namespace NZlib.Compression { + + public class InflaterHuffmanTree + { + private static int MAX_BITLEN = 15; + private short[] tree; + + public static InflaterHuffmanTree defLitLenTree, defDistTree; + + static InflaterHuffmanTree() + { + try { + byte[] codeLengths = new byte[288]; + int i = 0; + while (i < 144) { + codeLengths[i++] = 8; + } + while (i < 256) { + codeLengths[i++] = 9; + } + while (i < 280) { + codeLengths[i++] = 7; + } + while (i < 288) { + codeLengths[i++] = 8; + } + defLitLenTree = new InflaterHuffmanTree(codeLengths); + + codeLengths = new byte[32]; + i = 0; + while (i < 32) { + codeLengths[i++] = 5; + } + defDistTree = new InflaterHuffmanTree(codeLengths); + } catch (Exception) { + throw new ApplicationException("InflaterHuffmanTree: static tree length illegal"); + } + } + + /// + /// Constructs a Huffman tree from the array of code lengths. + /// + /// + /// the array of code lengths + /// + public InflaterHuffmanTree(byte[] codeLengths) + { + BuildTree(codeLengths); + } + + private void BuildTree(byte[] codeLengths) + { + int[] blCount = new int[MAX_BITLEN + 1]; + int[] nextCode = new int[MAX_BITLEN + 1]; + + for (int i = 0; i < codeLengths.Length; i++) { + int bits = codeLengths[i]; + if (bits > 0) + blCount[bits]++; + } + + int code = 0; + int treeSize = 512; + for (int bits = 1; bits <= MAX_BITLEN; bits++) { + nextCode[bits] = code; + code += blCount[bits] << (16 - bits); + if (bits >= 10) { + /* We need an extra table for bit lengths >= 10. */ + int start = nextCode[bits] & 0x1ff80; + int end = code & 0x1ff80; + treeSize += (end - start) >> (16 - bits); + } + } + if (code != 65536) { + throw new Exception("Code lengths don't add up properly."); + } + /* Now create and fill the extra tables from longest to shortest + * bit len. This way the sub trees will be aligned. + */ + tree = new short[treeSize]; + int treePtr = 512; + for (int bits = MAX_BITLEN; bits >= 10; bits--) { + int end = code & 0x1ff80; + code -= blCount[bits] << (16 - bits); + int start = code & 0x1ff80; + for (int i = start; i < end; i += 1 << 7) { + tree[DeflaterHuffman.BitReverse(i)] = (short) ((-treePtr << 4) | bits); + treePtr += 1 << (bits-9); + } + } + + for (int i = 0; i < codeLengths.Length; i++) { + int bits = codeLengths[i]; + if (bits == 0) { + continue; + } + code = nextCode[bits]; + int revcode = DeflaterHuffman.BitReverse(code); + if (bits <= 9) { + do { + tree[revcode] = (short) ((i << 4) | bits); + revcode += 1 << bits; + } while (revcode < 512); + } else { + int subTree = tree[revcode & 511]; + int treeLen = 1 << (subTree & 15); + subTree = -(subTree >> 4); + do { + tree[subTree | (revcode >> 9)] = (short) ((i << 4) | bits); + revcode += 1 << bits; + } while (revcode < treeLen); + } + nextCode[bits] = code + (1 << (16 - bits)); + } + + } + + /// + /// Reads the next symbol from input. The symbol is encoded using the + /// huffman tree. + /// + /// + /// input the input source. + /// + /// + /// the next symbol, or -1 if not enough input is available. + /// + public int GetSymbol(StreamManipulator input) + { + int lookahead, symbol; + if ((lookahead = input.PeekBits(9)) >= 0) { + if ((symbol = tree[lookahead]) >= 0) { + input.DropBits(symbol & 15); + return symbol >> 4; + } + int subtree = -(symbol >> 4); + int bitlen = symbol & 15; + if ((lookahead = input.PeekBits(bitlen)) >= 0) { + symbol = tree[subtree | (lookahead >> 9)]; + input.DropBits(symbol & 15); + return symbol >> 4; + } else { + int bits = input.AvailableBits; + lookahead = input.PeekBits(bits); + symbol = tree[subtree | (lookahead >> 9)]; + if ((symbol & 15) <= bits) { + input.DropBits(symbol & 15); + return symbol >> 4; + } else { + return -1; + } + } + } else { + int bits = input.AvailableBits; + lookahead = input.PeekBits(bits); + symbol = tree[lookahead]; + if (symbol >= 0 && (symbol & 15) <= bits) { + input.DropBits(symbol & 15); + return symbol >> 4; + } else { + return -1; + } + } + } + } +} + +namespace NZlib.Compression { + + /// + /// This class is general purpose class for writing data to a buffer. + /// + /// It allows you to write bits as well as bytes + /// Based on DeflaterPending.java + /// + /// author of the original java version : Jochen Hoenicke + /// + public class PendingBuffer + { + protected byte[] buf; + int start; + int end; + + uint bits; + int bitCount; + + public PendingBuffer() : this( 4096 ) + { + + } + + public PendingBuffer(int bufsize) + { + buf = new byte[bufsize]; + } + + public void Reset() + { + start = end = bitCount = 0; + } + + public void WriteByte(int b) + { + if (DeflaterConstants.DEBUGGING && start != 0) + throw new Exception(); + buf[end++] = (byte) b; + } + + public void WriteShort(int s) + { + if (DeflaterConstants.DEBUGGING && start != 0) { + throw new Exception(); + } + buf[end++] = (byte) s; + buf[end++] = (byte) (s >> 8); + } + + public void WriteInt(int s) + { + if (DeflaterConstants.DEBUGGING && start != 0) { + throw new Exception(); + } + buf[end++] = (byte) s; + buf[end++] = (byte) (s >> 8); + buf[end++] = (byte) (s >> 16); + buf[end++] = (byte) (s >> 24); + } + + public void WriteBlock(byte[] block, int offset, int len) + { + if (DeflaterConstants.DEBUGGING && start != 0) { + throw new Exception(); + } + System.Array.Copy(block, offset, buf, end, len); + end += len; + } + + public int BitCount { + get { + return bitCount; + } + } + + public void AlignToByte() + { + if (DeflaterConstants.DEBUGGING && start != 0) { + throw new Exception(); + } + if (bitCount > 0) { + buf[end++] = (byte) bits; + if (bitCount > 8) { + buf[end++] = (byte) (bits >> 8); + } + } + bits = 0; + bitCount = 0; + } + + public void WriteBits(int b, int count) + { + if (DeflaterConstants.DEBUGGING && start != 0) { + throw new Exception(); + } +// if (DeflaterConstants.DEBUGGING) { +// Console.WriteLine("writeBits("+b+","+count+")"); +// } + bits |= (uint)(b << bitCount); + bitCount += count; + if (bitCount >= 16) { + buf[end++] = (byte) bits; + buf[end++] = (byte) (bits >> 8); + bits >>= 16; + bitCount -= 16; + } + } + + public void WriteShortMSB(int s) + { + if (DeflaterConstants.DEBUGGING && start != 0) { + throw new Exception(); + } + buf[end++] = (byte) (s >> 8); + buf[end++] = (byte) s; + } + + public bool IsFlushed { + get { + return end == 0; + } + } + + /// + /// Flushes the pending buffer into the given output array. If the + /// output array is to small, only a partial flush is done. + /// + /// + /// the output array; + /// + /// + /// the offset into output array; + /// + /// + /// length the maximum number of bytes to store; + /// + /// + /// IndexOutOfBoundsException if offset or length are invalid. + /// + public int Flush(byte[] output, int offset, int length) + { + if (bitCount >= 8) { + buf[end++] = (byte) bits; + bits >>= 8; + bitCount -= 8; + } + if (length > end - start) { + length = end - start; + System.Array.Copy(buf, start, output, offset, length); + start = 0; + end = 0; + } else { + System.Array.Copy(buf, start, output, offset, length); + start += length; + } + return length; + } + + public byte[] ToByteArray() + { + byte[] ret = new byte[ end - start ]; + System.Array.Copy(buf, start, ret, 0, ret.Length); + start = 0; + end = 0; + return ret; + } + } +} + +namespace NZlib.Checksums { + + /// + /// Computes Adler32 checksum for a stream of data. An Adler32 + /// checksum is not as reliable as a CRC32 checksum, but a lot faster to + /// compute. + /// + /// The specification for Adler32 may be found in RFC 1950. + /// ZLIB Compressed Data Format Specification version 3.3) + /// + /// + /// From that document: + /// + /// "ADLER32 (Adler-32 checksum) + /// This contains a checksum value of the uncompressed data + /// (excluding any dictionary data) computed according to Adler-32 + /// algorithm. This algorithm is a 32-bit extension and improvement + /// of the Fletcher algorithm, used in the ITU-T X.224 / ISO 8073 + /// standard. + /// + /// Adler-32 is composed of two sums accumulated per byte: s1 is + /// the sum of all bytes, s2 is the sum of all s1 values. Both sums + /// are done modulo 65521. s1 is initialized to 1, s2 to zero. The + /// Adler-32 checksum is stored as s2*65536 + s1 in most- + /// significant-byte first (network) order." + /// + /// "8.2. The Adler-32 algorithm + /// + /// The Adler-32 algorithm is much faster than the CRC32 algorithm yet + /// still provides an extremely low probability of undetected errors. + /// + /// The modulo on unsigned long accumulators can be delayed for 5552 + /// bytes, so the modulo operation time is negligible. If the bytes + /// are a, b, c, the second sum is 3a + 2b + c + 3, and so is position + /// and order sensitive, unlike the first sum, which is just a + /// checksum. That 65521 is prime is important to avoid a possible + /// large class of two-byte errors that leave the check unchanged. + /// (The Fletcher checksum uses 255, which is not prime and which also + /// makes the Fletcher check insensitive to single byte changes 0 - + /// 255.) + /// + /// The sum s1 is initialized to 1 instead of zero to make the length + /// of the sequence part of s2, so that the length does not have to be + /// checked separately. (Any sequence of zeroes has a Fletcher + /// checksum of zero.)" + /// + /// + /// + public sealed class Adler32 : IChecksum + { + /// + /// largest prime smaller than 65536 + /// + readonly static uint BASE = 65521; + + uint checksum; + + /// + /// Returns the Adler32 data checksum computed so far. + /// + public long Value { + get { + return (long) checksum & 0xFFFFFFFFL; + } + } + + /// + /// Creates a new instance of the Adler32 class. + /// The checksum starts off with a value of 1. + /// + public Adler32() + { + Reset(); + } + + /// + /// Resets the Adler32 checksum to the initial value. + /// + public void Reset() + { + checksum = 1; //Initialize to 1 + } + + /// + /// Updates the checksum with the byte b. + /// + /// + /// the data value to add. The high byte of the int is ignored. + /// + public void Update(int bval) + { + //We could make a length 1 byte array and call update again, but I + //would rather not have that overhead + uint s1 = checksum & 0xFFFF; + uint s2 = checksum >> 16; + + s1 = (s1 + ((uint)bval & 0xFF)) % BASE; + s2 = (s1 + s2) % BASE; + + checksum = (s2 << 16) + s1; + } + + /// + /// Updates the checksum with the bytes taken from the array. + /// + /// + /// buffer an array of bytes + /// + public void Update(byte[] buffer) + { + Update(buffer, 0, buffer.Length); + } + + /// + /// Updates the checksum with the bytes taken from the array. + /// + /// + /// an array of bytes + /// + /// + /// the start of the data used for this update + /// + /// + /// the number of bytes to use for this update + /// + public void Update(byte[] buf, int off, int len) + { + if (buf == null) { + throw new ArgumentNullException("buf"); + } + + if (off < 0 || len < 0 || off + len > buf.Length) { + throw new ArgumentOutOfRangeException(); + } + + //(By Per Bothner) + uint s1 = checksum & 0xFFFF; + uint s2 = checksum >> 16; + + while (len > 0) { + // We can defer the modulo operation: + // s1 maximally grows from 65521 to 65521 + 255 * 3800 + // s2 maximally grows by 3800 * median(s1) = 2090079800 < 2^31 + int n = 3800; + if (n > len) { + n = len; + } + len -= n; + while (--n >= 0) { + s1 = s1 + (uint)(buf[off++] & 0xFF); + s2 = s2 + s1; + } + s1 %= BASE; + s2 %= BASE; + } + + checksum = (s2 << 16) | s1; + } + } +} + +namespace NZlib.Checksums { + + /// + /// Generate a table for a byte-wise 32-bit CRC calculation on the polynomial: + /// x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + /// + /// Polynomials over GF(2) are represented in binary, one bit per coefficient, + /// with the lowest powers in the most significant bit. Then adding polynomials + /// is just exclusive-or, and multiplying a polynomial by x is a right shift by + /// one. If we call the above polynomial p, and represent a byte as the + /// polynomial q, also with the lowest power in the most significant bit (so the + /// byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + /// where a mod b means the remainder after dividing a by b. + /// + /// This calculation is done using the shift-register method of multiplying and + /// taking the remainder. The register is initialized to zero, and for each + /// incoming bit, x^32 is added mod p to the register if the bit is a one (where + /// x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + /// x (which is shifting right by one and adding x^32 mod p if the bit shifted + /// out is a one). We start with the highest power (least significant bit) of + /// q and repeat for all eight bits of q. + /// + /// The table is simply the CRC of all possible eight bit values. This is all + /// the information needed to generate CRC's on data a byte at a time for all + /// combinations of CRC register values and incoming bytes. + /// + public sealed class Crc32 : IChecksum + { + readonly static uint CrcSeed = 0xFFFFFFFF; + + readonly static uint[] CrcTable = new uint[] { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, + 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, + 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, + 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, + 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, + 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, + 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, + 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, + 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, + 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, + 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, + 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, + 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, + 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, + 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, + 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, + 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, + 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, + 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, + 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, + 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, + 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, + 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, + 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, + 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, + 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, + 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, + 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, + 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, + 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, + 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, + 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, + 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, + 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, + 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, + 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, + 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, + 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, + 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, + 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, + 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, + 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, + 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, + 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, + 0x2D02EF8D + }; + + /// + /// The crc data checksum so far. + /// + uint crc = 0; + + /// + /// Returns the CRC32 data checksum computed so far. + /// + public long Value { + get { + return (long)crc; + } + } + + /// + /// Resets the CRC32 data checksum as if no update was ever called. + /// + public void Reset() + { + crc = 0; + } + + /// + /// Updates the checksum with the int bval. + /// + /// + /// the byte is taken as the lower 8 bits of bval + /// + public void Update(int bval) + { + crc ^= CrcSeed; + crc = CrcTable[(crc ^ bval) & 0xFF] ^ (crc >> 8); + crc ^= CrcSeed; + } + + /// + /// Updates the checksum with the bytes taken from the array. + /// + /// + /// buffer an array of bytes + /// + public void Update(byte[] buffer) + { + Update(buffer, 0, buffer.Length); + } + + /// + /// Adds the byte array to the data checksum. + /// + /// + /// the buffer which contains the data + /// + /// + /// the offset in the buffer where the data starts + /// + /// + /// the length of the data + /// + public void Update(byte[] buf, int off, int len) + { + if (buf == null) { + throw new ArgumentNullException("buf"); + } + + if (off < 0 || len < 0 || off + len > buf.Length) { + throw new ArgumentOutOfRangeException(); + } + + crc ^= CrcSeed; + + while (--len >= 0) { + crc = CrcTable[(crc ^ buf[off++]) & 0xFF] ^ (crc >> 8); + } + + crc ^= CrcSeed; + } + } +} + +namespace NZlib.Checksums { + + /// + /// Interface to compute a data checksum used by checked input/output streams. + /// A data checksum can be updated by one byte or with a byte array. After each + /// update the value of the current checksum can be returned by calling + /// getValue. The complete checksum object can also be reset + /// so it can be used again with new data. + /// + public interface IChecksum + { + /// + /// Returns the data checksum computed so far. + /// + long Value { + get; + } + + /// + /// Resets the data checksum as if no update was ever called. + /// + void Reset(); + + /// + /// Adds one byte to the data checksum. + /// + /// + /// the data value to add. The high byte of the int is ignored. + /// + void Update(int bval); + + /// + /// Updates the data checksum with the bytes taken from the array. + /// + /// + /// buffer an array of bytes + /// + void Update(byte[] buffer); + + /// + /// Adds the byte array to the data checksum. + /// + /// + /// the buffer which contains the data + /// + /// + /// the offset in the buffer where the data starts + /// + /// + /// the length of the data + /// + void Update(byte[] buf, int off, int len); + } +} + +namespace NZlib.Streams { + + /// + /// Contains the output from the Inflation process. + /// We need to have a window so that we can refer backwards into the output stream + /// to repeat stuff. + /// + /// author of the original java version : John Leuner + /// + public class OutputWindow + { + private static int WINDOW_SIZE = 1 << 15; + private static int WINDOW_MASK = WINDOW_SIZE - 1; + + private byte[] window = new byte[WINDOW_SIZE]; //The window is 2^15 bytes + private int window_end = 0; + private int window_filled = 0; + + public void Write(int abyte) + { + if (window_filled++ == WINDOW_SIZE) { + throw new InvalidOperationException("Window full"); + } + window[window_end++] = (byte) abyte; + window_end &= WINDOW_MASK; + } + + + private void SlowRepeat(int rep_start, int len, int dist) + { + while (len-- > 0) { + window[window_end++] = window[rep_start++]; + window_end &= WINDOW_MASK; + rep_start &= WINDOW_MASK; + } + } + + public void Repeat(int len, int dist) + { + if ((window_filled += len) > WINDOW_SIZE) { + throw new InvalidOperationException("Window full"); + } + + int rep_start = (window_end - dist) & WINDOW_MASK; + int border = WINDOW_SIZE - len; + if (rep_start <= border && window_end < border) { + if (len <= dist) { + System.Array.Copy(window, rep_start, window, window_end, len); + window_end += len; + } else { + /* We have to copy manually, since the repeat pattern overlaps. + */ + while (len-- > 0) { + window[window_end++] = window[rep_start++]; + } + } + } else { + SlowRepeat(rep_start, len, dist); + } + } + + public int CopyStored(StreamManipulator input, int len) + { + len = Math.Min(Math.Min(len, WINDOW_SIZE - window_filled), input.AvailableBytes); + int copied; + + int tailLen = WINDOW_SIZE - window_end; + if (len > tailLen) { + copied = input.CopyBytes(window, window_end, tailLen); + if (copied == tailLen) { + copied += input.CopyBytes(window, 0, len - tailLen); + } + } else { + copied = input.CopyBytes(window, window_end, len); + } + + window_end = (window_end + copied) & WINDOW_MASK; + window_filled += copied; + return copied; + } + + public void CopyDict(byte[] dict, int offset, int len) + { + if (window_filled > 0) { + throw new InvalidOperationException(); + } + + if (len > WINDOW_SIZE) { + offset += len - WINDOW_SIZE; + len = WINDOW_SIZE; + } + System.Array.Copy(dict, offset, window, 0, len); + window_end = len & WINDOW_MASK; + } + + public int GetFreeSpace() + { + return WINDOW_SIZE - window_filled; + } + + public int GetAvailable() + { + return window_filled; + } + + public int CopyOutput(byte[] output, int offset, int len) + { + int copy_end = window_end; + if (len > window_filled) { + len = window_filled; + } else { + copy_end = (window_end - window_filled + len) & WINDOW_MASK; + } + + int copied = len; + int tailLen = len - copy_end; + + if (tailLen > 0) { + System.Array.Copy(window, WINDOW_SIZE - tailLen, + output, offset, tailLen); + offset += tailLen; + len = copy_end; + } + System.Array.Copy(window, copy_end - len, output, offset, len); + window_filled -= copied; + if (window_filled < 0) { + throw new InvalidOperationException(); + } + return copied; + } + + public void Reset() + { + window_filled = window_end = 0; + } + } +} + +namespace NZlib.Streams { + + /// + /// This class allows us to retrieve a specified amount of bits from + /// the input buffer, as well as copy big byte blocks. + /// + /// It uses an int buffer to store up to 31 bits for direct + /// manipulation. This guarantees that we can get at least 16 bits, + /// but we only need at most 15, so this is all safe. + /// + /// There are some optimizations in this class, for example, you must + /// never peek more then 8 bits more than needed, and you must first + /// peek bits before you may drop them. This is not a general purpose + /// class but optimized for the behaviour of the Inflater. + /// + /// authors of the original java version : John Leuner, Jochen Hoenicke + /// + public class StreamManipulator + { + private byte[] window; + private int window_start = 0; + private int window_end = 0; + + private uint buffer = 0; + private int bits_in_buffer = 0; + + /// + /// Get the next n bits but don't increase input pointer. n must be + /// less or equal 16 and if you if this call succeeds, you must drop + /// at least n-8 bits in the next call. + /// + /// + /// the value of the bits, or -1 if not enough bits available. */ + /// + public int PeekBits(int n) + { + if (bits_in_buffer < n) { + if (window_start == window_end) { + return -1; + } + buffer |= (uint)((window[window_start++] & 0xff | + (window[window_start++] & 0xff) << 8) << bits_in_buffer); + bits_in_buffer += 16; + } + return (int)(buffer & ((1 << n) - 1)); + } + + /// + /// Drops the next n bits from the input. You should have called peekBits + /// with a bigger or equal n before, to make sure that enough bits are in + /// the bit buffer. + /// + public void DropBits(int n) + { + buffer >>= n; + bits_in_buffer -= n; + } + + /// + /// Gets the next n bits and increases input pointer. This is equivalent + /// to peekBits followed by dropBits, except for correct error handling. + /// + /// + /// the value of the bits, or -1 if not enough bits available. + /// + public int GetBits(int n) + { + int bits = PeekBits(n); + if (bits >= 0) { + DropBits(n); + } + return bits; + } + + /// + /// Gets the number of bits available in the bit buffer. This must be + /// only called when a previous peekBits() returned -1. + /// + /// + /// the number of bits available. + /// + public int AvailableBits { + get { + return bits_in_buffer; + } + } + + /// + /// Gets the number of bytes available. + /// + /// + /// the number of bytes available. + /// + public int AvailableBytes { + get { + return window_end - window_start + (bits_in_buffer >> 3); + } + } + + /// + /// Skips to the next byte boundary. + /// + public void SkipToByteBoundary() + { + buffer >>= (bits_in_buffer & 7); + bits_in_buffer &= ~7; + } + + public bool IsNeedingInput { + get { + return window_start == window_end; + } + } + + /// + /// Copies length bytes from input buffer to output buffer starting + /// at output[offset]. You have to make sure, that the buffer is + /// byte aligned. If not enough bytes are available, copies fewer + /// bytes. + /// + /// + /// the buffer. + /// + /// + /// the offset in the buffer. + /// + /// + /// the length to copy, 0 is allowed. + /// + /// + /// the number of bytes copied, 0 if no byte is available. + /// + public int CopyBytes(byte[] output, int offset, int length) + { + if (length < 0) { + throw new ArgumentOutOfRangeException("length negative"); + } + if ((bits_in_buffer & 7) != 0) { + /* bits_in_buffer may only be 0 or 8 */ + throw new InvalidOperationException("Bit buffer is not aligned!"); + } + + int count = 0; + while (bits_in_buffer > 0 && length > 0) { + output[offset++] = (byte) buffer; + buffer >>= 8; + bits_in_buffer -= 8; + length--; + count++; + } + if (length == 0) { + return count; + } + + int avail = window_end - window_start; + if (length > avail) { + length = avail; + } + System.Array.Copy(window, window_start, output, offset, length); + window_start += length; + + if (((window_start - window_end) & 1) != 0) { + /* We always want an even number of bytes in input, see peekBits */ + buffer = (uint)(window[window_start++] & 0xff); + bits_in_buffer = 8; + } + return count + length; + } + + public StreamManipulator() + { + } + + public void Reset() + { + buffer = (uint)(window_start = window_end = bits_in_buffer = 0); + } + + public void SetInput(byte[] buf, int off, int len) + { + if (window_start < window_end) { + throw new InvalidOperationException("Old input was not completely processed"); + } + + int end = off + len; + + /* We want to throw an ArrayIndexOutOfBoundsException early. The + * check is very tricky: it also handles integer wrap around. + */ + if (0 > off || off > end || end > buf.Length) { + throw new ArgumentOutOfRangeException(); + } + + if ((len & 1) != 0) { + /* We always want an even number of bytes in input, see peekBits */ + buffer |= (uint)((buf[off++] & 0xff) << bits_in_buffer); + bits_in_buffer += 8; + } + + window = buf; + window_start = off; + window_end = end; + } + } +} diff --git a/unity-2019.4.24f1-mbe/mono/btls/.gitignore b/unity-2019.4.24f1-mbe/mono/btls/.gitignore new file mode 100644 index 000000000..545f630f7 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/.gitignore @@ -0,0 +1,6 @@ +Makefile +Makefile.in +build/ +martin-test +build-shared/ +build-static/ diff --git a/unity-2019.4.24f1-mbe/mono/btls/CMakeLists.txt b/unity-2019.4.24f1-mbe/mono/btls/CMakeLists.txt new file mode 100644 index 000000000..8b9e4eb0c --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/CMakeLists.txt @@ -0,0 +1,91 @@ +cmake_minimum_required (VERSION 2.8.10) + +project (mono-btls) + +if(POLICY CMP0026) +cmake_policy(SET CMP0026 NEW) +endif() +if(POLICY CMP0042) +cmake_policy(SET CMP0042 NEW) +endif() + +enable_language(C) +enable_language(CXX) + +find_program(CCACHE_PROGRAM ccache) +if(CCACHE_PROGRAM) + set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}") +endif() + +# FIXME: cmake's asm detection is broken when using xcrun. +set (CMAKE_ASM_COMPILER "${CMAKE_C_COMPILER}") +set (CMAKE_ASM_COMPILER_ARG1 "${CMAKE_C_COMPILER_ARG1}") +set (CMAKE_ASM_COMPILER_ID "${CMAKE_C_COMPILER_ID}") +enable_language(ASM) + +if (NOT "${BTLS_ARCH}" STREQUAL "") + message (WARNING "SET ARCH: ${BTLS_ARCH}") + set (CMAKE_SYSTEM_PROCESSOR "${BTLS_ARCH}") +endif () + +set (C_CXX_FLAGS "-Wall -Wsign-compare -Wmissing-field-initializers -fPIC -ggdb -fvisibility=hidden") + +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${C_CXX_FLAGS} ${BTLS_CFLAGS}") +set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${C_CXX_FLAGS} ${BTLS_CFLAGS}") +set (CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} ${BTLS_CFLAGS}") +set (CMAKE_MACOSX_RPATH 1) +set (MONO_BTLS 1) + +set(BUILD_SHARED_LIBS_SAVED "${BUILD_SHARED_LIBS}") +set(BUILD_SHARED_LIBS OFF) +add_subdirectory (${BTLS_ROOT} boringssl) +set(BUILD_SHARED_LIBS "${BUILD_SHARED_LIBS_SAVED}") + +include_directories ( + ${SRC_DIR} + ${BTLS_ROOT}/include +) + +set ( + MONO_BTLS_SOURCES + + btls-bio.c + btls-bio.h + btls-error.c + btls-error.h + btls-key.c + btls-key.h + btls-pkcs12.c + btls-pkcs12.h + btls-ssl-ctx.c + btls-ssl-ctx.h + btls-ssl.c + btls-ssl.h + btls-time64.c + btls-util.c + btls-util.h + btls-x509-chain.c + btls-x509-chain.h + btls-x509-crl.c + btls-x509-crl.h + btls-x509-lookup.c + btls-x509-lookup.h + btls-x509-lookup-mono.c + btls-x509-lookup-mono.h + btls-x509-name.c + btls-x509-name.h + btls-x509-revoked.c + btls-x509-revoked.h + btls-x509-store-ctx.c + btls-x509-store-ctx.h + btls-x509-store.c + btls-x509-store.h + btls-x509-verify-param.c + btls-x509-verify-param.h + btls-x509.c + btls-x509.h + + ${BORINGSSL_OBJECTS} +) + +add_library (mono-btls-shared SHARED ${MONO_BTLS_SOURCES}) diff --git a/unity-2019.4.24f1-mbe/mono/btls/Makefile.am b/unity-2019.4.24f1-mbe/mono/btls/Makefile.am new file mode 100644 index 000000000..0902fb41f --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/Makefile.am @@ -0,0 +1,74 @@ +MONO_BTLS_SOURCES_FILES = \ + btls-bio.c \ + btls-bio.h \ + btls-error.c \ + btls-error.h \ + btls-key.c \ + btls-key.h \ + btls-pkcs12.c \ + btls-pkcs12.h \ + btls-ssl.c \ + btls-ssl-ctx.c \ + btls-ssl-ctx.h \ + btls-ssl.h \ + btls-time64.c \ + btls-util.c \ + btls-util.h \ + btls-x509.c \ + btls-x509-chain.c \ + btls-x509-chain.h \ + btls-x509-crl.c \ + btls-x509-crl.h \ + btls-x509.h \ + btls-x509-lookup.c \ + btls-x509-lookup.h \ + btls-x509-lookup-mono.c \ + btls-x509-lookup-mono.h \ + btls-x509-name.c \ + btls-x509-name.h \ + btls-x509-revoked.c \ + btls-x509-revoked.h \ + btls-x509-store.c \ + btls-x509-store-ctx.c \ + btls-x509-store-ctx.h \ + btls-x509-store.h \ + btls-x509-verify-param.c \ + btls-x509-verify-param.h \ + CMakeLists.txt + +EXTRA_DIST = $(MONO_BTLS_SOURCES_FILES) + +CMAKE_VERBOSE=$(if $(V),VERBOSE=1,) +NINJA_VERBOSE=$(if ($V),-v,) + +if NINJA +NINJA_ARGS = -G Ninja +BUILDFILE = build.ninja +else +NINJA_ARGS = +BUILDFILE = Makefile +endif + +CMAKE_ARGS = -D CMAKE_INSTALL_PREFIX:PATH=$(prefix) -D BTLS_ROOT:PATH=$(BTLS_ROOT) \ + -D SRC_DIR:PATH=$(abs_top_srcdir)/mono/btls -D BTLS_CFLAGS:STRING="$(BTLS_CFLAGS)" $(NINJA_ARGS) + +all-local: build-shared/libmono-btls-shared$(libsuffix) + +build-shared/$(BUILDFILE): + -mkdir -p build-shared + (cd build-shared && CC="$(CC)" CXX="$(CXX)" $(CMAKE) $(CMAKE_ARGS) $(BTLS_CMAKE_ARGS) -DBUILD_SHARED_LIBS=1 $(abs_top_srcdir)/mono/btls) + +if NINJA +build-shared/libmono-btls-shared$(libsuffix): build-shared/$(BUILDFILE) $(MONO_BTLS_SOURCES_FILES) + ninja -C build-shared $(NINJA_VERBOSE) +else +build-shared/libmono-btls-shared$(libsuffix): build-shared/$(BUILDFILE) $(MONO_BTLS_SOURCES_FILES) + $(MAKE) -C build-shared $(CMAKE_VERBOSE) +endif + +clean-local: + -rm -rf build-shared + +install-exec-local: + $(mkinstalldirs) "$(DESTDIR)$(libdir)" + $(install_sh) build-shared/libmono-btls-shared.* "$(DESTDIR)$(libdir)" diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-bio.c b/unity-2019.4.24f1-mbe/mono/btls/btls-bio.c new file mode 100644 index 000000000..016175bea --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-bio.c @@ -0,0 +1,208 @@ +// +// btls-bio.c +// MonoBtls +// +// Created by Martin Baulig on 14/11/15. +// Copyright (c) 2015 Xamarin. All rights reserved. +// + +#include +#include +#include + +struct MonoBtlsBio { + const void *instance; + MonoBtlsReadFunc read_func; + MonoBtlsWriteFunc write_func; + MonoBtlsControlFunc control_func; +}; + +#if 0 +static void +mono_debug (const char *message) +{ + BIO *bio_err; + bio_err = BIO_new_fp (stderr, BIO_NOCLOSE); + fprintf (stderr, "DEBUG: %s\n", message); + ERR_print_errors (bio_err); +} +#endif + +static int +mono_read (BIO *bio, char *out, int outl) +{ + MonoBtlsBio *mono = (MonoBtlsBio *)bio->ptr; + int ret, wantMore; + + if (!mono) + return -1; + + ret = mono->read_func (mono->instance, out, outl, &wantMore); + + if (ret < 0) { + errno = EIO; + return -1; + } + if (ret > 0) + return ret; + + if (wantMore) { + errno = EAGAIN; + BIO_set_retry_read (bio); + return -1; + } + + return 0; +} + +static int +mono_write (BIO *bio, const char *in, int inl) +{ + MonoBtlsBio *mono = (MonoBtlsBio *)bio->ptr; + + if (!mono) + return -1; + + return mono->write_func (mono->instance, in, inl); +} + +static long +mono_ctrl (BIO *bio, int cmd, long num, void *ptr) +{ + MonoBtlsBio *mono = (MonoBtlsBio *)bio->ptr; + + if (!mono) + return -1; + + // fprintf (stderr, "mono_ctrl: %x - %lx - %p\n", cmd, num, ptr); + switch (cmd) { + case BIO_CTRL_FLUSH: + return mono->control_func (mono->instance, MONO_BTLS_CONTROL_COMMAND_FLUSH, 0); + default: + return -1; + } + return -1; +} + +static int +mono_new (BIO *bio) +{ + // mono_debug("mono_new!\n"); + bio->init = 0; + bio->num = -1; + bio->flags = 0; + return 1; +} + +static int +mono_free (BIO *bio) +{ + // mono_debug ("mono_free!\n"); + if (bio->ptr) { + MonoBtlsBio *mono = (MonoBtlsBio *)bio->ptr; + + bio->ptr = NULL; + mono->instance = NULL; + mono->read_func = NULL; + mono->write_func = NULL; + mono->control_func = NULL; + free (mono); + } + return 1; +} + +static const BIO_METHOD mono_method = { + BIO_TYPE_NONE, "mono", mono_write, mono_read, + NULL, NULL, mono_ctrl, mono_new, mono_free, NULL +}; + +MONO_API BIO * +mono_btls_bio_mono_new (void) +{ + BIO *bio; + MonoBtlsBio *monoBio; + + bio = BIO_new (&mono_method); + if (!bio) + return NULL; + + monoBio = calloc (1, sizeof (MonoBtlsBio)); + if (!monoBio) { + BIO_free (bio); + return NULL; + } + + bio->ptr = monoBio; + bio->init = 0; + + return bio; +} + +MONO_API void +mono_btls_bio_mono_initialize (BIO *bio, const void *instance, + MonoBtlsReadFunc read_func, MonoBtlsWriteFunc write_func, + MonoBtlsControlFunc control_func) +{ + MonoBtlsBio *monoBio = bio->ptr; + + monoBio->instance = instance; + monoBio->read_func = read_func; + monoBio->write_func = write_func; + monoBio->control_func = control_func; + + bio->init = 1; +} + +MONO_API int +mono_btls_bio_read (BIO *bio, void *data, int len) +{ + return BIO_read (bio, data, len); +} + +MONO_API int +mono_btls_bio_write (BIO *bio, const void *data, int len) +{ + return BIO_write (bio, data, len); +} + +MONO_API int +mono_btls_bio_flush (BIO *bio) +{ + return BIO_flush (bio); +} + +MONO_API int +mono_btls_bio_indent (BIO *bio, unsigned indent, unsigned max_indent) +{ + return BIO_indent (bio, indent, max_indent); +} + +MONO_API int +mono_btls_bio_hexdump (BIO *bio, const uint8_t *data, int len, unsigned indent) +{ + return BIO_hexdump (bio, data, len, indent); +} + +MONO_API void +mono_btls_bio_print_errors (BIO *bio) +{ + BIO_print_errors (bio); +} + +MONO_API void +mono_btls_bio_free (BIO *bio) +{ + BIO_free (bio); +} + +MONO_API BIO * +mono_btls_bio_mem_new (void) +{ + return BIO_new (BIO_s_mem ()); +} + +MONO_API int +mono_btls_bio_mem_get_data (BIO *bio, void **data) +{ + return (int)BIO_get_mem_data (bio, (char**)data); +} diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-bio.h b/unity-2019.4.24f1-mbe/mono/btls/btls-bio.h new file mode 100644 index 000000000..0795ef0f2 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-bio.h @@ -0,0 +1,58 @@ +// +// btls-bio.h +// MonoBtls +// +// Created by Martin Baulig on 14/11/15. +// Copyright (c) 2015 Xamarin. All rights reserved. +// + +#ifndef __btls__btls_bio__ +#define __btls__btls_bio__ + +#include +#include + +typedef enum { + MONO_BTLS_CONTROL_COMMAND_FLUSH = 1 +} MonoBtlsControlCommand; + +typedef int (* MonoBtlsReadFunc) (const void *instance, const void *buf, int size, int *wantMore); +typedef int (* MonoBtlsWriteFunc) (const void *instance, const void *buf, int size); +typedef int64_t (* MonoBtlsControlFunc) (const void *instance, MonoBtlsControlCommand command, int64_t arg); + +BIO * +mono_btls_bio_mono_new (void); + +void +mono_btls_bio_mono_initialize (BIO *bio, const void *instance, + MonoBtlsReadFunc read_func, MonoBtlsWriteFunc write_func, + MonoBtlsControlFunc control_func); + +int +mono_btls_bio_read (BIO *bio, void *data, int len); + +int +mono_btls_bio_write (BIO *bio, const void *data, int len); + +int +mono_btls_bio_flush (BIO *bio); + +int +mono_btls_bio_indent (BIO *bio, unsigned indent, unsigned max_indent); + +int +mono_btls_bio_hexdump (BIO *bio, const uint8_t *data, int len, unsigned indent); + +void +mono_btls_bio_print_errors (BIO *bio); + +void +mono_btls_bio_free (BIO *bio); + +BIO * +mono_btls_bio_mem_new (void); + +int +mono_btls_bio_mem_get_data (BIO *bio, void **data); + +#endif /* defined(__btls__btls_bio__) */ diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-error.c b/unity-2019.4.24f1-mbe/mono/btls/btls-error.c new file mode 100644 index 000000000..3d81b32f3 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-error.c @@ -0,0 +1,48 @@ +// +// btls-error.c +// MonoBtls +// +// Created by Martin Baulig on 6/19/16. +// Copyright © 2016 Xamarin. All rights reserved. +// + +#include +#include +#include + +MONO_API int +mono_btls_error_peek_error (void) +{ + return ERR_peek_error (); +} + +MONO_API int +mono_btls_error_get_error (void) +{ + return ERR_get_error (); +} + +MONO_API int +mono_btls_error_peek_error_line (const char **file, int *line) +{ + return ERR_peek_error_line (file, line); +} + +MONO_API int +mono_btls_error_get_error_line (const char **file, int *line) +{ + return ERR_get_error_line (file, line); +} + +MONO_API void +mono_btls_error_clear_error (void) +{ + ERR_clear_error (); +} + +MONO_API void +mono_btls_error_get_error_string_n (int error, char *buf, int len) +{ + ERR_error_string_n (error, buf, len); +} + diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-error.h b/unity-2019.4.24f1-mbe/mono/btls/btls-error.h new file mode 100644 index 000000000..418727e9a --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-error.h @@ -0,0 +1,35 @@ +// +// btls-util.h +// MonoBtls +// +// Created by Martin Baulig on 3/23/16. +// Copyright © 2016 Xamarin. All rights reserved. +// + +#ifndef __btls__btls_error__ +#define __btls__btls_error__ + +#include +#include +#include +#include + +int +mono_btls_error_peek_error (void); + +int +mono_btls_error_get_error (void); + +void +mono_btls_error_clear_error (void); + +int +mono_btls_error_peek_error_line (const char **file, int *line); + +int +mono_btls_error_get_error_line (const char **file, int *line); + +void +mono_btls_error_get_error_string_n (int error, char *buf, int len); + +#endif /* __btls__btls_error__ */ diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-key.c b/unity-2019.4.24f1-mbe/mono/btls/btls-key.c new file mode 100644 index 000000000..462a71419 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-key.c @@ -0,0 +1,82 @@ +// +// btls-key.c +// MonoBtls +// +// Created by Martin Baulig on 3/7/16. +// Copyright © 2016 Xamarin. All rights reserved. +// + +#include + +MONO_API EVP_PKEY * +mono_btls_key_new () +{ + return EVP_PKEY_new (); +} + +MONO_API void +mono_btls_key_free (EVP_PKEY *pkey) +{ + EVP_PKEY_free (pkey); +} + +MONO_API EVP_PKEY * +mono_btls_key_up_ref (EVP_PKEY *pkey) +{ + return EVP_PKEY_up_ref (pkey); +} + +MONO_API int +mono_btls_key_get_bits (EVP_PKEY *pkey) +{ + return EVP_PKEY_bits (pkey); +} + +MONO_API int +mono_btls_key_is_rsa (EVP_PKEY *pkey) +{ + return pkey->type == EVP_PKEY_RSA; +} + +MONO_API int +mono_btls_key_assign_rsa_private_key (EVP_PKEY *pkey, uint8_t *der_data, int der_length) +{ + RSA *rsa; + + rsa = RSA_private_key_from_bytes (der_data, der_length); + if (!rsa) + return 0; + + return EVP_PKEY_assign_RSA (pkey, rsa); +} + +MONO_API int +mono_btls_key_get_bytes (EVP_PKEY *pkey, uint8_t **buffer, int *size, int include_private_bits) +{ + size_t len; + RSA *rsa; + int ret; + + *size = 0; + *buffer = NULL; + + if (pkey->type != EVP_PKEY_RSA) + return 0; + + rsa = EVP_PKEY_get1_RSA (pkey); + if (!rsa) + return 0; + + if (include_private_bits) + ret = RSA_private_key_to_bytes (buffer, &len, rsa); + else + ret = RSA_public_key_to_bytes (buffer, &len, rsa); + + RSA_free (rsa); + + if (ret != 1) + return 0; + + *size = (int)len; + return 1; +} diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-key.h b/unity-2019.4.24f1-mbe/mono/btls/btls-key.h new file mode 100644 index 000000000..32d9e021c --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-key.h @@ -0,0 +1,38 @@ +// +// btls-key.h +// MonoBtls +// +// Created by Martin Baulig on 3/7/16. +// Copyright © 2016 Xamarin. All rights reserved. +// + +#ifndef __btls__btls_key__ +#define __btls__btls_key__ + +#include +#include +#include + +EVP_PKEY * +mono_btls_key_new (); + +void +mono_btls_key_free (EVP_PKEY *pkey); + +EVP_PKEY * +mono_btls_key_up_ref (EVP_PKEY *pkey); + +int +mono_btls_key_get_bits (EVP_PKEY *pkey); + +int +mono_btls_key_is_rsa (EVP_PKEY *pkey); + +int +mono_btls_key_assign_rsa_private_key (EVP_PKEY *pkey, uint8_t *der_data, int der_length); + +int +mono_btls_key_get_bytes (EVP_PKEY *pkey, uint8_t **buffer, int *size, int include_private_bits); + +#endif /* __btls__btls_key__ */ + diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-pkcs12.c b/unity-2019.4.24f1-mbe/mono/btls/btls-pkcs12.c new file mode 100644 index 000000000..6e1a79e60 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-pkcs12.c @@ -0,0 +1,101 @@ +// +// btls-pkcs12.c +// MonoBtls +// +// Created by Martin Baulig on 3/8/16. +// Copyright © 2016 Xamarin. All rights reserved. +// + +#include +#include + +struct MonoBtlsPkcs12 { + STACK_OF(X509) *certs; + EVP_PKEY *private_key; + CRYPTO_refcount_t references; +}; + +MONO_API MonoBtlsPkcs12 * +mono_btls_pkcs12_new (void) +{ + MonoBtlsPkcs12 *pkcs12 = (MonoBtlsPkcs12 *)OPENSSL_malloc (sizeof (MonoBtlsPkcs12)); + if (pkcs12 == NULL) + return NULL; + + memset (pkcs12, 0, sizeof(MonoBtlsPkcs12)); + pkcs12->certs = sk_X509_new_null (); + pkcs12->references = 1; + return pkcs12; +} + +MONO_API int +mono_btls_pkcs12_get_count (MonoBtlsPkcs12 *pkcs12) +{ + return (int)sk_X509_num (pkcs12->certs); +} + +MONO_API X509 * +mono_btls_pkcs12_get_cert (MonoBtlsPkcs12 *pkcs12, int index) +{ + X509 *cert; + + if ((size_t)index >= sk_X509_num (pkcs12->certs)) + return NULL; + cert = sk_X509_value (pkcs12->certs, index); + if (cert) + X509_up_ref (cert); + return cert; +} + +MONO_API STACK_OF(X509) * +mono_btls_pkcs12_get_certs (MonoBtlsPkcs12 *pkcs12) +{ + return pkcs12->certs; +} + +MONO_API int +mono_btls_pkcs12_free (MonoBtlsPkcs12 *pkcs12) +{ + if (!CRYPTO_refcount_dec_and_test_zero (&pkcs12->references)) + return 0; + + sk_X509_pop_free (pkcs12->certs, X509_free); + OPENSSL_free (pkcs12); + return 1; +} + +MONO_API MonoBtlsPkcs12 * +mono_btls_pkcs12_up_ref (MonoBtlsPkcs12 *pkcs12) +{ + CRYPTO_refcount_inc (&pkcs12->references); + return pkcs12; +} + +MONO_API void +mono_btls_pkcs12_add_cert (MonoBtlsPkcs12 *pkcs12, X509 *x509) +{ + X509_up_ref (x509); + sk_X509_push (pkcs12->certs, x509); +} + +MONO_API int +mono_btls_pkcs12_import (MonoBtlsPkcs12 *pkcs12, const void *data, int len, const void *password) +{ + CBS cbs; + CBS_init (&cbs, data, len); + return PKCS12_get_key_and_certs (&pkcs12->private_key, pkcs12->certs, &cbs, password); +} + +MONO_API int +mono_btls_pkcs12_has_private_key (MonoBtlsPkcs12 *pkcs12) +{ + return pkcs12->private_key != NULL; +} + +MONO_API EVP_PKEY * +mono_btls_pkcs12_get_private_key (MonoBtlsPkcs12 *pkcs12) +{ + if (!pkcs12->private_key) + return NULL; + return EVP_PKEY_up_ref (pkcs12->private_key); +} diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-pkcs12.h b/unity-2019.4.24f1-mbe/mono/btls/btls-pkcs12.h new file mode 100644 index 000000000..20c4fd9c4 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-pkcs12.h @@ -0,0 +1,46 @@ +// +// btls-pkcs12.h +// MonoBtls +// +// Created by Martin Baulig on 3/8/16. +// Copyright © 2016 Xamarin. All rights reserved. +// + +#ifndef __btls__btls_pkcs12__ +#define __btls__btls_pkcs12__ + +#include +#include +#include + +MonoBtlsPkcs12 * +mono_btls_pkcs12_new (void); + +int +mono_btls_pkcs12_get_count (MonoBtlsPkcs12 *pkcs12); + +X509 * +mono_btls_pkcs12_get_cert (MonoBtlsPkcs12 *pkcs12, int index); + +STACK_OF(X509) * +mono_btls_pkcs12_get_certs (MonoBtlsPkcs12 *pkcs12); + +int +mono_btls_pkcs12_free (MonoBtlsPkcs12 *pkcs12); + +MonoBtlsPkcs12 * +mono_btls_pkcs12_up_ref (MonoBtlsPkcs12 *pkcs12); + +void +mono_btls_pkcs12_add_cert (MonoBtlsPkcs12 *pkcs12, X509 *x509); + +int +mono_btls_pkcs12_import (MonoBtlsPkcs12 *pkcs12, const void *data, int len, const void *password); + +int +mono_btls_pkcs12_has_private_key (MonoBtlsPkcs12 *pkcs12); + +EVP_PKEY * +mono_btls_pkcs12_get_private_key (MonoBtlsPkcs12 *pkcs12); + +#endif /* __btls__btls_pkcs12__ */ diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-ssl-ctx.c b/unity-2019.4.24f1-mbe/mono/btls/btls-ssl-ctx.c new file mode 100644 index 000000000..fa56fbd73 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-ssl-ctx.c @@ -0,0 +1,264 @@ +// +// btls-ssl-ctx.c +// MonoBtls +// +// Created by Martin Baulig on 4/11/16. +// Copyright © 2016 Xamarin. All rights reserved. +// + +#include +#include + +struct MonoBtlsSslCtx { + CRYPTO_refcount_t references; + SSL_CTX *ctx; + BIO *bio; + BIO *debug_bio; + void *instance; + MonoBtlsVerifyFunc verify_func; + MonoBtlsSelectFunc select_func; +}; + +#define debug_print(ptr,message) \ +do { if (mono_btls_ssl_ctx_is_debug_enabled(ptr)) \ +mono_btls_ssl_ctx_debug_printf (ptr, "%s:%d:%s(): " message, __FILE__, __LINE__, \ + __func__); } while (0) + +#define debug_printf(ptr,fmt, ...) \ +do { if (mono_btls_ssl_ctx_is_debug_enabled(ptr)) \ +mono_btls_ssl_ctx_debug_printf (ptr, "%s:%d:%s(): " fmt, __FILE__, __LINE__, \ + __func__, __VA_ARGS__); } while (0) + +void ssl_cipher_preference_list_free (struct ssl_cipher_preference_list_st *cipher_list); + +MONO_API int +mono_btls_ssl_ctx_is_debug_enabled (MonoBtlsSslCtx *ctx) +{ + return ctx->debug_bio != NULL; +} + +MONO_API int +mono_btls_ssl_ctx_debug_printf (MonoBtlsSslCtx *ctx, const char *format, ...) +{ + va_list args; + int ret; + + if (!ctx->debug_bio) + return 0; + + va_start (args, format); + ret = mono_btls_debug_printf (ctx->debug_bio, format, args); + va_end (args); + return ret; +} + +MONO_API MonoBtlsSslCtx * +mono_btls_ssl_ctx_new (void) +{ + MonoBtlsSslCtx *ctx; + + ctx = OPENSSL_malloc (sizeof (MonoBtlsSslCtx)); + if (!ctx) + return NULL; + + memset (ctx, 0, sizeof (MonoBtlsSslCtx)); + ctx->references = 1; + ctx->ctx = SSL_CTX_new (TLS_method ()); + + // enable the default ciphers but disable any RC4 based ciphers + // since they're insecure: RFC 7465 "Prohibiting RC4 Cipher Suites" + SSL_CTX_set_cipher_list (ctx->ctx, "DEFAULT:!RC4"); + + // disable SSLv2 and SSLv3 by default, they are deprecated + // and should generally not be used according to the openssl docs + SSL_CTX_set_options (ctx->ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); + + return ctx; +} + +MONO_API MonoBtlsSslCtx * +mono_btls_ssl_ctx_up_ref (MonoBtlsSslCtx *ctx) +{ + CRYPTO_refcount_inc (&ctx->references); + return ctx; +} + +MONO_API int +mono_btls_ssl_ctx_free (MonoBtlsSslCtx *ctx) +{ + if (!CRYPTO_refcount_dec_and_test_zero (&ctx->references)) + return 0; + SSL_CTX_free (ctx->ctx); + ctx->instance = NULL; + OPENSSL_free (ctx); + return 1; +} + +MONO_API SSL_CTX * +mono_btls_ssl_ctx_get_ctx (MonoBtlsSslCtx *ctx) +{ + return ctx->ctx; +} + +MONO_API void +mono_btls_ssl_ctx_set_debug_bio (MonoBtlsSslCtx *ctx, BIO *debug_bio) +{ + if (debug_bio) + ctx->debug_bio = BIO_up_ref(debug_bio); + else + ctx->debug_bio = NULL; +} + +MONO_API void +mono_btls_ssl_ctx_initialize (MonoBtlsSslCtx *ctx, void *instance) +{ + ctx->instance = instance; +} + +static int +cert_verify_callback (X509_STORE_CTX *storeCtx, void *arg) +{ + MonoBtlsSslCtx *ptr = (MonoBtlsSslCtx*)arg; + int ret; + + debug_printf (ptr, "cert_verify_callback(): %p\n", ptr->verify_func); + ret = X509_verify_cert (storeCtx); + debug_printf (ptr, "cert_verify_callback() #1: %d\n", ret); + + if (ptr->verify_func) + ret = ptr->verify_func (ptr->instance, ret, storeCtx); + + return ret; +} + +MONO_API void +mono_btls_ssl_ctx_set_cert_verify_callback (MonoBtlsSslCtx *ptr, MonoBtlsVerifyFunc func, int cert_required) +{ + int mode; + + ptr->verify_func = func; + SSL_CTX_set_cert_verify_callback (ptr->ctx, cert_verify_callback, ptr); + + mode = SSL_VERIFY_PEER; + if (cert_required) + mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + + SSL_CTX_set_verify (ptr->ctx, mode, NULL); +} + +static int +cert_select_callback (SSL *ssl, void *arg) +{ + MonoBtlsSslCtx *ptr = (MonoBtlsSslCtx*)arg; + int ret = 1; + + debug_printf (ptr, "cert_select_callback(): %p\n", ptr->select_func); + if (ptr->select_func) + ret = ptr->select_func (ptr->instance); + debug_printf (ptr, "cert_select_callback() #1: %d\n", ret); + + return ret; +} + +MONO_API void +mono_btls_ssl_ctx_set_cert_select_callback (MonoBtlsSslCtx *ptr, MonoBtlsSelectFunc func) +{ + ptr->select_func = func; + SSL_CTX_set_cert_cb (ptr->ctx, cert_select_callback, ptr); +} + +MONO_API X509_STORE * +mono_btls_ssl_ctx_peek_store (MonoBtlsSslCtx *ctx) +{ + return SSL_CTX_get_cert_store (ctx->ctx); +} + +MONO_API void +mono_btls_ssl_ctx_set_min_version (MonoBtlsSslCtx *ctx, int version) +{ + SSL_CTX_set_min_version (ctx->ctx, version); +} + +MONO_API void +mono_btls_ssl_ctx_set_max_version (MonoBtlsSslCtx *ctx, int version) +{ + SSL_CTX_set_max_version (ctx->ctx, version); +} + +MONO_API int +mono_btls_ssl_ctx_is_cipher_supported (MonoBtlsSslCtx *ctx, uint16_t value) +{ + const SSL_CIPHER *cipher; + + cipher = SSL_get_cipher_by_value (value); + return cipher != NULL; +} + +MONO_API int +mono_btls_ssl_ctx_set_ciphers (MonoBtlsSslCtx *ctx, int count, const uint16_t *data, + int allow_unsupported) +{ + STACK_OF(SSL_CIPHER) *ciphers = NULL; + struct ssl_cipher_preference_list_st *pref_list = NULL; + uint8_t *in_group_flags = NULL; + int i; + + ciphers = sk_SSL_CIPHER_new_null (); + if (!ciphers) + goto err; + + for (i = 0; i < count; i++) { + const SSL_CIPHER *cipher = SSL_get_cipher_by_value (data [i]); + if (!cipher) { + debug_printf (ctx, "mono_btls_ssl_ctx_set_ciphers(): unknown cipher %02x", data [i]); + if (!allow_unsupported) + goto err; + continue; + } + if (!sk_SSL_CIPHER_push (ciphers, cipher)) + goto err; + } + + pref_list = OPENSSL_malloc (sizeof (struct ssl_cipher_preference_list_st)); + if (!pref_list) + goto err; + + memset (pref_list, 0, sizeof (struct ssl_cipher_preference_list_st)); + pref_list->ciphers = sk_SSL_CIPHER_dup (ciphers); + if (!pref_list->ciphers) + goto err; + pref_list->in_group_flags = OPENSSL_malloc (sk_SSL_CIPHER_num (ciphers)); + if (!pref_list->in_group_flags) + goto err; + + if (ctx->ctx->cipher_list) + ssl_cipher_preference_list_free (ctx->ctx->cipher_list); + if (ctx->ctx->cipher_list_by_id) + sk_SSL_CIPHER_free (ctx->ctx->cipher_list_by_id); + if (ctx->ctx->cipher_list_tls10) { + ssl_cipher_preference_list_free (ctx->ctx->cipher_list_tls10); + ctx->ctx->cipher_list_tls10 = NULL; + } + if (ctx->ctx->cipher_list_tls11) { + ssl_cipher_preference_list_free (ctx->ctx->cipher_list_tls11); + ctx->ctx->cipher_list_tls11 = NULL; + } + + ctx->ctx->cipher_list = pref_list; + ctx->ctx->cipher_list_by_id = ciphers; + + return (int)sk_SSL_CIPHER_num (ciphers); + +err: + sk_SSL_CIPHER_free (ciphers); + OPENSSL_free (pref_list); + OPENSSL_free (in_group_flags); + return 0; +} + +MONO_API int +mono_btls_ssl_ctx_set_verify_param (MonoBtlsSslCtx *ctx, const MonoBtlsX509VerifyParam *param) +{ + return SSL_CTX_set1_param (ctx->ctx, mono_btls_x509_verify_param_peek_param (param)); +} + diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-ssl-ctx.h b/unity-2019.4.24f1-mbe/mono/btls/btls-ssl-ctx.h new file mode 100644 index 000000000..095419270 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-ssl-ctx.h @@ -0,0 +1,84 @@ +// +// btls-ssl-ctx.h +// MonoBtls +// +// Created by Martin Baulig on 4/11/16. +// Copyright © 2016 Xamarin. All rights reserved. +// + +#ifndef __btls_ssl_ctx__btls_ssl_ctx__ +#define __btls_ssl_ctx__btls_ssl_ctx__ + +#include +#include +#include +#include +#include + +typedef struct MonoBtlsBio MonoBtlsBio; +typedef struct MonoBtlsX509Chain MonoBtlsX509Chain; +typedef struct MonoBtlsX509Crl MonoBtlsX509Crl; +typedef struct MonoBtlsX509Lookup MonoBtlsX509Lookup; +typedef struct MonoBtlsX509LookupMono MonoBtlsX509LookupMono; +typedef struct MonoBtlsX509Name MonoBtlsX509Name; +typedef struct MonoBtlsX509Store MonoBtlsX509Store; +typedef struct MonoBtlsX509StoreCtx MonoBtlsX509StoreCtx; +typedef struct MonoBtlsX509Revoked MonoBtlsX509Revoked; +typedef struct MonoBtlsX509VerifyParam MonoBtlsX509VerifyParam; +typedef struct MonoBtlsPkcs12 MonoBtlsPkcs12; +typedef struct MonoBtlsSsl MonoBtlsSsl; +typedef struct MonoBtlsSslCtx MonoBtlsSslCtx; + +typedef int (* MonoBtlsVerifyFunc) (void *instance, int preverify_ok, X509_STORE_CTX *ctx); +typedef int (* MonoBtlsSelectFunc) (void *instance); + +MonoBtlsSslCtx * +mono_btls_ssl_ctx_new (void); + +MonoBtlsSslCtx * +mono_btls_ssl_ctx_up_ref (MonoBtlsSslCtx *ctx); + +int +mono_btls_ssl_ctx_free (MonoBtlsSslCtx *ctx); + +void +mono_btls_ssl_ctx_initialize (MonoBtlsSslCtx *ctx, void *instance); + +SSL_CTX * +mono_btls_ssl_ctx_get_ctx (MonoBtlsSslCtx *ctx); + +int +mono_btls_ssl_ctx_debug_printf (MonoBtlsSslCtx *ctx, const char *format, ...); + +int +mono_btls_ssl_ctx_is_debug_enabled (MonoBtlsSslCtx *ctx); + +void +mono_btls_ssl_ctx_set_cert_verify_callback (MonoBtlsSslCtx *ptr, MonoBtlsVerifyFunc func, int cert_required); + +void +mono_btls_ssl_ctx_set_cert_select_callback (MonoBtlsSslCtx *ptr, MonoBtlsSelectFunc func); + +void +mono_btls_ssl_ctx_set_debug_bio (MonoBtlsSslCtx *ctx, BIO *debug_bio); + +X509_STORE * +mono_btls_ssl_ctx_peek_store (MonoBtlsSslCtx *ctx); + +void +mono_btls_ssl_ctx_set_min_version (MonoBtlsSslCtx *ctx, int version); + +void +mono_btls_ssl_ctx_set_max_version (MonoBtlsSslCtx *ctx, int version); + +int +mono_btls_ssl_ctx_is_cipher_supported (MonoBtlsSslCtx *ctx, uint16_t value); + +int +mono_btls_ssl_ctx_set_ciphers (MonoBtlsSslCtx *ctx, int count, const uint16_t *data, + int allow_unsupported); + +int +mono_btls_ssl_ctx_set_verify_param (MonoBtlsSslCtx *ctx, const MonoBtlsX509VerifyParam *param); + +#endif /* __btls_ssl_ctx__btls_ssl_ctx__ */ diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-ssl.c b/unity-2019.4.24f1-mbe/mono/btls/btls-ssl.c new file mode 100644 index 000000000..6a40f63fd --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-ssl.c @@ -0,0 +1,238 @@ +// +// btls-ssl.c +// MonoBtls +// +// Created by Martin Baulig on 14/11/15. +// Copyright (c) 2015 Xamarin. All rights reserved. +// + +#include +#include + +struct MonoBtlsSsl { + MonoBtlsSslCtx *ctx; + SSL *ssl; +}; + +#define debug_print(ptr,message) \ +do { if (mono_btls_ssl_ctx_is_debug_enabled(ptr->ctx)) \ +mono_btls_ssl_ctx_debug_printf (ptr->ctx, "%s:%d:%s(): " message, __FILE__, __LINE__, \ +__func__); } while (0) + +#define debug_printf(ptr,fmt, ...) \ +do { if (mono_btls_ssl_ctx_is_debug_enabled(ptr->ctx)) \ +mono_btls_ssl_ctx_debug_printf (ptr->ctx, "%s:%d:%s(): " fmt, __FILE__, __LINE__, \ +__func__, __VA_ARGS__); } while (0) + +STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list (SSL *s, const CBS *cbs); + +MONO_API MonoBtlsSsl * +mono_btls_ssl_new (MonoBtlsSslCtx *ctx) +{ + MonoBtlsSsl *ptr; + + ptr = calloc (1, sizeof (MonoBtlsSsl)); + + ptr->ctx = mono_btls_ssl_ctx_up_ref (ctx); + ptr->ssl = SSL_new (mono_btls_ssl_ctx_get_ctx (ptr->ctx)); + + return ptr; +} + +MONO_API void +mono_btls_ssl_destroy (MonoBtlsSsl *ptr) +{ + mono_btls_ssl_close (ptr); + if (ptr->ssl) { + SSL_free (ptr->ssl); + ptr->ssl = NULL; + } + if (ptr->ctx) { + mono_btls_ssl_ctx_free (ptr->ctx); + ptr->ctx = NULL; + } + free (ptr); +} + +MONO_API void +mono_btls_ssl_close (MonoBtlsSsl *ptr) +{ + ; +} + +MONO_API int +mono_btls_ssl_shutdown (MonoBtlsSsl *ptr) +{ + return SSL_shutdown (ptr->ssl); +} + +MONO_API void +mono_btls_ssl_set_quiet_shutdown (MonoBtlsSsl *ptr, int mode) +{ + SSL_set_quiet_shutdown (ptr->ssl, mode); +} + +MONO_API void +mono_btls_ssl_set_bio (MonoBtlsSsl *ptr, BIO *bio) +{ + BIO_up_ref (bio); + SSL_set_bio (ptr->ssl, bio, bio); +} + +MONO_API void +mono_btls_ssl_print_errors_cb (ERR_print_errors_callback_t callback, void *ctx) +{ + ERR_print_errors_cb (callback, ctx); +} + +MONO_API int +mono_btls_ssl_use_certificate (MonoBtlsSsl *ptr, X509 *x509) +{ + return SSL_use_certificate (ptr->ssl, x509); +} + +MONO_API int +mono_btls_ssl_use_private_key (MonoBtlsSsl *ptr, EVP_PKEY *key) +{ + return SSL_use_PrivateKey (ptr->ssl, key); +} + +MONO_API int +mono_btls_ssl_add_chain_certificate (MonoBtlsSsl *ptr, X509 *x509) +{ + return SSL_add1_chain_cert (ptr->ssl, x509); +} + +MONO_API int +mono_btls_ssl_accept (MonoBtlsSsl *ptr) +{ + return SSL_accept (ptr->ssl); +} + +MONO_API int +mono_btls_ssl_connect (MonoBtlsSsl *ptr) +{ + return SSL_connect (ptr->ssl); +} + +MONO_API int +mono_btls_ssl_handshake (MonoBtlsSsl *ptr) +{ + return SSL_do_handshake (ptr->ssl); +} + +MONO_API int +mono_btls_ssl_read (MonoBtlsSsl *ptr, void *buf, int count) +{ + return SSL_read (ptr->ssl, buf, count); +} + +MONO_API int +mono_btls_ssl_write (MonoBtlsSsl *ptr, void *buf, int count) +{ + return SSL_write (ptr->ssl, buf, count); +} + +MONO_API int +mono_btls_ssl_get_version (MonoBtlsSsl *ptr) +{ + return SSL_version (ptr->ssl); +} + +MONO_API void +mono_btls_ssl_set_min_version (MonoBtlsSsl *ptr, int version) +{ + SSL_set_min_version (ptr->ssl, version); +} + +MONO_API void +mono_btls_ssl_set_max_version (MonoBtlsSsl *ptr, int version) +{ + SSL_set_max_version (ptr->ssl, version); +} + +MONO_API int +mono_btls_ssl_get_cipher (MonoBtlsSsl *ptr) +{ + const SSL_CIPHER *cipher; + + cipher = SSL_get_current_cipher (ptr->ssl); + if (!cipher) + return 0; + return (uint16_t)SSL_CIPHER_get_id (cipher); +} + +MONO_API int +mono_btls_ssl_set_cipher_list (MonoBtlsSsl *ptr, const char *str) +{ + return SSL_set_cipher_list(ptr->ssl, str); +} + +MONO_API int +mono_btls_ssl_get_ciphers (MonoBtlsSsl *ptr, uint16_t **data) +{ + STACK_OF(SSL_CIPHER) *ciphers; + int count, i; + + *data = NULL; + + ciphers = SSL_get_ciphers (ptr->ssl); + if (!ciphers) + return 0; + + count = (int)sk_SSL_CIPHER_num (ciphers); + + *data = OPENSSL_malloc (2 * count); + if (!*data) + return 0; + + for (i = 0; i < count; i++) { + const SSL_CIPHER *cipher = sk_SSL_CIPHER_value (ciphers, i); + (*data) [i] = (uint16_t) SSL_CIPHER_get_id (cipher); + } + + return count; +} + +MONO_API X509 * +mono_btls_ssl_get_peer_certificate (MonoBtlsSsl *ptr) +{ + return SSL_get_peer_certificate (ptr->ssl); +} + +MONO_API int +mono_btls_ssl_get_error (MonoBtlsSsl *ptr, int ret_code) +{ + return SSL_get_error (ptr->ssl, ret_code); +} + +MONO_API int +mono_btls_ssl_set_verify_param (MonoBtlsSsl *ptr, const MonoBtlsX509VerifyParam *param) +{ + return SSL_set1_param (ptr->ssl, mono_btls_x509_verify_param_peek_param (param)); +} + +MONO_API int +mono_btls_ssl_set_server_name (MonoBtlsSsl *ptr, const char *name) +{ + return SSL_set_tlsext_host_name (ptr->ssl, name); +} + +MONO_API const char * +mono_btls_ssl_get_server_name (MonoBtlsSsl *ptr) +{ + return SSL_get_servername (ptr->ssl, TLSEXT_NAMETYPE_host_name); +} + +MONO_API void +mono_btls_ssl_set_renegotiate_mode (MonoBtlsSsl *ptr, MonoBtlsSslRenegotiateMode mode) +{ + SSL_set_renegotiate_mode (ptr->ssl, (enum ssl_renegotiate_mode_t)mode); +} + +MONO_API int +mono_btls_ssl_renegotiate_pending (MonoBtlsSsl *ptr) +{ + return SSL_renegotiate_pending (ptr->ssl); +} + diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-ssl.h b/unity-2019.4.24f1-mbe/mono/btls/btls-ssl.h new file mode 100644 index 000000000..cb4ddd77d --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-ssl.h @@ -0,0 +1,105 @@ +// +// btls-ssl.h +// MonoBtls +// +// Created by Martin Baulig on 14/11/15. +// Copyright (c) 2015 Xamarin. All rights reserved. +// + +#ifndef __btls__btls_ssl__ +#define __btls__btls_ssl__ + +#include + +MonoBtlsSsl * +mono_btls_ssl_new (MonoBtlsSslCtx *ctx); + +int +mono_btls_ssl_use_certificate (MonoBtlsSsl *ptr, X509 *x509); + +int +mono_btls_ssl_use_private_key (MonoBtlsSsl *ptr, EVP_PKEY *key); + +int +mono_btls_ssl_add_chain_certificate (MonoBtlsSsl *ptr, X509 *x509); + +int +mono_btls_ssl_accept (MonoBtlsSsl *ptr); + +int +mono_btls_ssl_connect (MonoBtlsSsl *ptr); + +int +mono_btls_ssl_handshake (MonoBtlsSsl *ptr); + +void +mono_btls_ssl_print_errors_cb (ERR_print_errors_callback_t callback, void *ctx); + +void +mono_btls_ssl_set_bio (MonoBtlsSsl *ptr, BIO *bio); + +int +mono_btls_ssl_read (MonoBtlsSsl *ptr, void *buf, int count); + +int +mono_btls_ssl_write (MonoBtlsSsl *ptr, void *buf, int count); + +int +mono_btls_ssl_get_version (MonoBtlsSsl *ptr); + +void +mono_btls_ssl_set_min_version (MonoBtlsSsl *ptr, int version); + +void +mono_btls_ssl_set_max_version (MonoBtlsSsl *ptr, int version); + +int +mono_btls_ssl_get_cipher (MonoBtlsSsl *ptr); + +int +mono_btls_ssl_set_cipher_list (MonoBtlsSsl *ptr, const char *str); + +int +mono_btls_ssl_get_ciphers (MonoBtlsSsl *ptr, uint16_t **data); + +X509 * +mono_btls_ssl_get_peer_certificate (MonoBtlsSsl *ptr); + +void +mono_btls_ssl_close (MonoBtlsSsl *ptr); + +int +mono_btls_ssl_shutdown (MonoBtlsSsl *ptr); + +MONO_API void +mono_btls_ssl_set_quiet_shutdown (MonoBtlsSsl *ptr, int mode); + +int +mono_btls_ssl_get_error (MonoBtlsSsl *ptr, int ret_code); + +int +mono_btls_ssl_set_verify_param (MonoBtlsSsl *ptr, const MonoBtlsX509VerifyParam *param); + +int +mono_btls_ssl_set_server_name (MonoBtlsSsl *ptr, const char *name); + +const char * +mono_btls_ssl_get_server_name (MonoBtlsSsl *ptr); + +typedef enum { + MONO_BTLS_SSL_RENEGOTIATE_NEVER = 0, + MONO_BTLS_SSL_RENEGOTIATE_ONCE, + MONO_BTLS_SSL_RENEGOTIATE_FREELY, + MONO_BTLS_SSL_RENEGOTIATE_IGNORE +} MonoBtlsSslRenegotiateMode; + +void +mono_btls_ssl_set_renegotiate_mode (MonoBtlsSsl *ptr, MonoBtlsSslRenegotiateMode mode); + +int +mono_btls_ssl_renegotiate_pending (MonoBtlsSsl *ptr); + +void +mono_btls_ssl_destroy (MonoBtlsSsl *ptr); + +#endif /* defined(__btls__btls_ssl__) */ diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-time64.c b/unity-2019.4.24f1-mbe/mono/btls/btls-time64.c new file mode 100644 index 000000000..d5b12aeec --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-time64.c @@ -0,0 +1,115 @@ +/* + +Copyright (c) 2007-2008 Michael G Schwern + +This software originally derived from Paul Sheer's pivotal_gmtime_r.c. + +The MIT License: + +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. + +*/ + +/* See http://code.google.com/p/y2038 for this code's origin */ + +#include +#include +#include +#include +#include +#include +#include + +static const int julian_days_by_month[2][12] = { + {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}, + {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}, +}; + +static const int length_of_year[2] = { 365, 366 }; + +/* Some numbers relating to the gregorian cycle */ +#define days_in_gregorian_cycle ((365 * 400) + 100 - 4 + 1) + +/* Year range we can trust the time funcitons with */ +#define MAX_SAFE_YEAR 2037 +#define MIN_SAFE_YEAR 1971 + +/* 28 year Julian calendar cycle */ +#define SOLAR_CYCLE_LENGTH 28 + +/* Let's assume people are going to be looking for dates in the future. + Let's provide some cheats so you can skip ahead. + This has a 4x speed boost when near 2008. +*/ +/* Number of days since epoch on Jan 1st, 2008 GMT */ +#define CHEAT_DAYS (1199145600 / 24 / 60 / 60) +#define CHEAT_YEARS 108 + +#define IS_LEAP(n) ((!(((n) + 1900) % 400) || (!(((n) + 1900) % 4) && (((n) + 1900) % 100))) != 0) +#define WRAP(a,b,m) ((a) = ((a) < 0 ) ? ((b)--, (a) + (m)) : (a)) + +/* timegm() is not in the C or POSIX spec, but it is such a useful + extension I would be remiss in leaving it out. Also I need it + for localtime64() +*/ +int64_t btls_timegm64(const struct tm *date) { + int64_t days = 0; + int64_t seconds = 0; + int64_t year; + int64_t orig_year = (int64_t)date->tm_year; + int cycles = 0; + + if( orig_year > 100 ) { + cycles = (orig_year - 100) / 400; + orig_year -= cycles * 400; + days += (int64_t)cycles * days_in_gregorian_cycle; + } + else if( orig_year < -300 ) { + cycles = (orig_year - 100) / 400; + orig_year -= cycles * 400; + days += (int64_t)cycles * days_in_gregorian_cycle; + } + + if( orig_year > 70 ) { + year = 70; + while( year < orig_year ) { + days += length_of_year[IS_LEAP(year)]; + year++; + } + } + else if ( orig_year < 70 ) { + year = 69; + do { + days -= length_of_year[IS_LEAP(year)]; + year--; + } while( year >= orig_year ); + } + + + days += julian_days_by_month[IS_LEAP(orig_year)][date->tm_mon]; + days += date->tm_mday - 1; + + seconds = days * 60 * 60 * 24; + + seconds += date->tm_hour * 60 * 60; + seconds += date->tm_min * 60; + seconds += date->tm_sec; + + return(seconds); +} diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-util.c b/unity-2019.4.24f1-mbe/mono/btls/btls-util.c new file mode 100644 index 000000000..57fa0fa78 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-util.c @@ -0,0 +1,83 @@ +// +// btls-util.c +// MonoBtls +// +// Created by Martin Baulig on 3/23/16. +// Copyright © 2016 Xamarin. All rights reserved. +// + +#include +#include +// #include + +extern int asn1_generalizedtime_to_tm (struct tm *tm, const ASN1_GENERALIZEDTIME *d); + +extern int64_t btls_timegm64 (const struct tm *date); + + +MONO_API void +mono_btls_free (void *data) +{ + OPENSSL_free (data); +} + +int64_t +mono_btls_util_asn1_time_to_ticks (ASN1_TIME *time) +{ + ASN1_GENERALIZEDTIME *gtime; + struct tm tm; + int64_t epoch; + int ret; + + memset (&tm, 0, sizeof (tm)); + + gtime = ASN1_TIME_to_generalizedtime (time, NULL); + ret = asn1_generalizedtime_to_tm (&tm, gtime); + ASN1_GENERALIZEDTIME_free (gtime); + + /* FIXME: check the return value in managed code */ + if (ret == 0) { + return 0; + } + + epoch = btls_timegm64 (&tm); + + return epoch; +} + +// Copied from crypto/bio/printf.c, takes va_list +int +mono_btls_debug_printf (BIO *bio, const char *format, va_list args) +{ + char buf[256], *out, out_malloced = 0; + int out_len, ret; + + out_len = vsnprintf (buf, sizeof(buf), format, args); + if (out_len < 0) { + return -1; + } + + if ((size_t) out_len >= sizeof(buf)) { + const int requested_len = out_len; + /* The output was truncated. Note that vsnprintf's return value + * does not include a trailing NUL, but the buffer must be sized + * for it. */ + out = OPENSSL_malloc (requested_len + 1); + out_malloced = 1; + if (out == NULL) { + OPENSSL_PUT_ERROR(BIO, ERR_R_MALLOC_FAILURE); + return -1; + } + out_len = vsnprintf (out, requested_len + 1, format, args); + assert(out_len == requested_len); + } else { + out = buf; + } + + ret = BIO_write(bio, out, out_len); + if (out_malloced) { + OPENSSL_free(out); + } + + return ret; +} diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-util.h b/unity-2019.4.24f1-mbe/mono/btls/btls-util.h new file mode 100644 index 000000000..cad092039 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-util.h @@ -0,0 +1,45 @@ +// +// btls-util.h +// MonoBtls +// +// Created by Martin Baulig on 3/23/16. +// Copyright © 2016 Xamarin. All rights reserved. +// + +#ifndef __btls__btls_util__ +#define __btls__btls_util__ + +#include +#include +#include +#include + +#ifndef MONO_API +#if defined(_MSC_VER) + +#define MONO_API __declspec(dllexport) + +#else + +#ifdef __GNUC__ +#define MONO_API __attribute__ ((__visibility__ ("default"))) +#else +#define MONO_API +#endif + +#endif +#endif + +void +mono_btls_free (void *data); + +int64_t +mono_btls_util_asn1_time_to_ticks (ASN1_TIME *time); + +int +mono_btls_debug_printf (BIO *bio, const char *format, va_list args); + +OPENSSL_EXPORT void CRYPTO_refcount_inc(CRYPTO_refcount_t *count); +OPENSSL_EXPORT int CRYPTO_refcount_dec_and_test_zero(CRYPTO_refcount_t *count); + +#endif /* __btls__btls_util__ */ diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-x509-chain.c b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-chain.c new file mode 100644 index 000000000..5b7082ddb --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-chain.c @@ -0,0 +1,96 @@ +// +// btls-x509-chain.c +// MonoBtls +// +// Created by Martin Baulig on 3/3/16. +// Copyright © 2016 Xamarin. All rights reserved. +// + +#include + +struct MonoBtlsX509Chain { + STACK_OF(X509) *certs; + CRYPTO_refcount_t references; +}; + +MONO_API MonoBtlsX509Chain * +mono_btls_x509_chain_new (void) +{ + MonoBtlsX509Chain *chain = (MonoBtlsX509Chain *)OPENSSL_malloc (sizeof (MonoBtlsX509Chain)); + if (chain == NULL) + return NULL; + + memset(chain, 0, sizeof(MonoBtlsX509Chain)); + chain->certs = sk_X509_new_null (); + chain->references = 1; + return chain; +} + +MONO_API MonoBtlsX509Chain * +mono_btls_x509_chain_from_certs (STACK_OF(X509) *certs) +{ + MonoBtlsX509Chain *chain = (MonoBtlsX509Chain *)OPENSSL_malloc (sizeof (MonoBtlsX509Chain)); + if (chain == NULL) + return NULL; + + memset(chain, 0, sizeof(MonoBtlsX509Chain)); + chain->certs = X509_chain_up_ref(certs); + chain->references = 1; + return chain; +} + +MONO_API STACK_OF(X509) * +mono_btls_x509_chain_peek_certs (MonoBtlsX509Chain *chain) +{ + return chain->certs; +} + +MONO_API int +mono_btls_x509_chain_get_count (MonoBtlsX509Chain *chain) +{ + return (int)sk_X509_num(chain->certs); +} + +MONO_API X509 * +mono_btls_x509_chain_get_cert (MonoBtlsX509Chain *chain, int index) +{ + X509 *cert; + + if ((size_t)index >= sk_X509_num(chain->certs)) + return NULL; + cert = sk_X509_value(chain->certs, index); + if (cert) + X509_up_ref(cert); + return cert; +} + +MONO_API STACK_OF(X509) * +mono_btls_x509_chain_get_certs (MonoBtlsX509Chain *chain) +{ + return chain->certs; +} + +MONO_API int +mono_btls_x509_chain_free (MonoBtlsX509Chain *chain) +{ + if (!CRYPTO_refcount_dec_and_test_zero(&chain->references)) + return 0; + + sk_X509_pop_free(chain->certs, X509_free); + OPENSSL_free (chain); + return 1; +} + +MONO_API MonoBtlsX509Chain * +mono_btls_x509_chain_up_ref (MonoBtlsX509Chain *chain) +{ + CRYPTO_refcount_inc(&chain->references); + return chain; +} + +MONO_API void +mono_btls_x509_chain_add_cert (MonoBtlsX509Chain *chain, X509 *x509) +{ + X509_up_ref(x509); + sk_X509_push(chain->certs, x509); +} diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-x509-chain.h b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-chain.h new file mode 100644 index 000000000..68ef5773b --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-chain.h @@ -0,0 +1,41 @@ +// +// btls-x509-chain.h +// MonoBtls +// +// Created by Martin Baulig on 3/3/16. +// Copyright © 2016 Xamarin. All rights reserved. +// + +#ifndef __btls__btls_x509_chain__ +#define __btls__btls_x509_chain__ + +#include +#include +#include + +MonoBtlsX509Chain * +mono_btls_x509_chain_new (void); + +MonoBtlsX509Chain * +mono_btls_x509_chain_from_certs (STACK_OF(X509) *certs); + +STACK_OF(X509) * +mono_btls_x509_chain_peek_certs (MonoBtlsX509Chain *chain); + +int +mono_btls_x509_chain_get_count (MonoBtlsX509Chain *chain); + +X509 * +mono_btls_x509_chain_get_cert (MonoBtlsX509Chain *chain, int index); + +MonoBtlsX509Chain * +mono_btls_x509_chain_up_ref (MonoBtlsX509Chain *chain); + +int +mono_btls_x509_chain_free (MonoBtlsX509Chain *chain); + +void +mono_btls_x509_chain_add_cert (MonoBtlsX509Chain *chain, X509 *x509); + +#endif /* defined(__btls__btls_x509_chain__) */ + diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-x509-crl.c b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-crl.c new file mode 100644 index 000000000..d496c072f --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-crl.c @@ -0,0 +1,150 @@ +// +// btls-x509-crl.c +// MonoBtls +// +// Created by Martin Baulig on 3/23/16. +// Copyright © 2016 Xamarin. All rights reserved. +// + +#include +#include + +struct MonoBtlsX509Crl { + X509_CRL *crl; + CRYPTO_refcount_t references; +}; + +MONO_API MonoBtlsX509Crl * +mono_btls_x509_crl_from_data (const void *buf, int len, MonoBtlsX509Format format) +{ + MonoBtlsX509Crl *crl; + BIO *bio; + + crl = OPENSSL_malloc (sizeof (MonoBtlsX509Crl)); + memset (crl, 0, sizeof(MonoBtlsX509Crl)); + crl->references = 1; + + bio = BIO_new_mem_buf ((void *)buf, len); + switch (format) { + case MONO_BTLS_X509_FORMAT_DER: + crl->crl = d2i_X509_CRL_bio (bio, NULL); + break; + case MONO_BTLS_X509_FORMAT_PEM: + crl->crl = PEM_read_bio_X509_CRL (bio, NULL, NULL, NULL); + break; + } + BIO_free (bio); + + if (!crl->crl) { + OPENSSL_free (crl); + return NULL; + } + + return crl; +} + +MONO_API MonoBtlsX509Crl * +mono_btls_x509_crl_ref (MonoBtlsX509Crl *crl) +{ + CRYPTO_refcount_inc (&crl->references); + return crl; +} + +MONO_API int +mono_btls_x509_crl_free (MonoBtlsX509Crl *crl) +{ + if (!CRYPTO_refcount_dec_and_test_zero (&crl->references)) + return 0; + + X509_CRL_free (crl->crl); + OPENSSL_free (crl); + return 1; +} + +MONO_API MonoBtlsX509Revoked * +mono_btls_x509_crl_get_by_cert (MonoBtlsX509Crl *crl, X509 *x509) +{ + X509_REVOKED *revoked; + int ret; + + revoked = NULL; + ret = X509_CRL_get0_by_cert (crl->crl, &revoked, x509); + fprintf (stderr, "mono_btls_x509_crl_get_by_cert: %d - %p\n", ret, revoked); + + if (!ret || !revoked) + return NULL; + + return mono_btls_x509_revoked_new (crl, revoked); +} + +MONO_API MonoBtlsX509Revoked * +mono_btls_x509_crl_get_by_serial (MonoBtlsX509Crl *crl, void *serial, int len) +{ + ASN1_INTEGER si; + X509_REVOKED *revoked; + int ret; + + si.type = V_ASN1_INTEGER; + si.length = len; + si.data = serial; + + revoked = NULL; + ret = X509_CRL_get0_by_serial (crl->crl, &revoked, &si); + fprintf (stderr, "mono_btls_x509_crl_get_by_serial: %d - %p\n", ret, revoked); + + if (!ret || !revoked) + return NULL; + + return mono_btls_x509_revoked_new (crl, revoked); +} + +MONO_API int +mono_btls_x509_crl_get_revoked_count (MonoBtlsX509Crl *crl) +{ + STACK_OF(X509_REVOKED) *stack; + + stack = X509_CRL_get_REVOKED (crl->crl); + return (int)sk_X509_REVOKED_num (stack); +} + +MONO_API MonoBtlsX509Revoked * +mono_btls_x509_crl_get_revoked (MonoBtlsX509Crl *crl, int index) +{ + STACK_OF(X509_REVOKED) *stack; + X509_REVOKED *revoked; + + stack = X509_CRL_get_REVOKED (crl->crl); + if ((size_t)index >= sk_X509_REVOKED_num (stack)) + return NULL; + + revoked = sk_X509_REVOKED_value (stack, index); + if (!revoked) + return NULL; + + return mono_btls_x509_revoked_new (crl, revoked); +} + +MONO_API int64_t +mono_btls_x509_crl_get_last_update (MonoBtlsX509Crl *crl) +{ + return mono_btls_util_asn1_time_to_ticks (X509_CRL_get_lastUpdate (crl->crl)); +} + +MONO_API int64_t +mono_btls_x509_crl_get_next_update (MonoBtlsX509Crl *crl) +{ + return mono_btls_util_asn1_time_to_ticks (X509_CRL_get_nextUpdate (crl->crl)); +} + +MONO_API int64_t +mono_btls_x509_crl_get_version (MonoBtlsX509Crl *crl) +{ + return X509_CRL_get_version (crl->crl); +} + +MONO_API MonoBtlsX509Name * +mono_btls_x509_crl_get_issuer (MonoBtlsX509Crl *crl) +{ + return mono_btls_x509_name_copy (X509_CRL_get_issuer (crl->crl)); +} + diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-x509-crl.h b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-crl.h new file mode 100644 index 000000000..625037d99 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-crl.h @@ -0,0 +1,49 @@ +// +// btls-x509-crl.h +// MonoBtls +// +// Created by Martin Baulig on 3/23/16. +// Copyright © 2016 Xamarin. All rights reserved. +// + +#ifndef __btls__btls_x509_crl__ +#define __btls__btls_x509_crl__ + +#include +#include +#include + +MonoBtlsX509Crl * +mono_btls_x509_crl_from_data (const void *buf, int len, MonoBtlsX509Format format); + +MonoBtlsX509Crl * +mono_btls_x509_crl_ref (MonoBtlsX509Crl *crl); + +int +mono_btls_x509_crl_free (MonoBtlsX509Crl *crl); + +MonoBtlsX509Revoked * +mono_btls_x509_crl_get_by_cert (MonoBtlsX509Crl *crl, X509 *x509); + +MonoBtlsX509Revoked * +mono_btls_x509_crl_get_by_serial (MonoBtlsX509Crl *crl, void *serial, int len); + +int +mono_btls_x509_crl_get_revoked_count (MonoBtlsX509Crl *crl); + +MonoBtlsX509Revoked * +mono_btls_x509_crl_get_revoked (MonoBtlsX509Crl *crl, int index); + +int64_t +mono_btls_x509_crl_get_last_update (MonoBtlsX509Crl *crl); + +int64_t +mono_btls_x509_crl_get_next_update (MonoBtlsX509Crl *crl); + +int64_t +mono_btls_x509_crl_get_version (MonoBtlsX509Crl *crl); + +MonoBtlsX509Name * +mono_btls_x509_crl_get_issuer (MonoBtlsX509Crl *crl); + +#endif /* __btls__btls_x509_crl__ */ diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-x509-lookup-mono.c b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-lookup-mono.c new file mode 100644 index 000000000..1d8f7d3ee --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-lookup-mono.c @@ -0,0 +1,228 @@ +// +// btls-x509-lookup-mono.c +// MonoBtls +// +// Created by Martin Baulig on 3/6/16. +// Copyright © 2016 Xamarin. All rights reserved. +// + +#include +#include +#include + +// random high number +#define MONO_BTLS_X509_L_MONO_ADD 36292 + +typedef struct MonoLookupNode MonoLookupNode; +struct MonoLookupNode { + MonoBtlsX509LookupMono *mono; + MonoLookupNode *next; +}; + +typedef struct { + MonoLookupNode *nodes; +} MonoLookup; + +struct MonoBtlsX509LookupMono { + const void *instance; + MonoBtlsX509LookupMono_BySubject by_subject_func; + MonoLookup *lookup; +}; + +MONO_API MonoBtlsX509LookupMono * +mono_btls_x509_lookup_mono_new (void) +{ + MonoBtlsX509LookupMono *mono; + + mono = OPENSSL_malloc (sizeof (MonoBtlsX509LookupMono)); + if (!mono) + return NULL; + + memset (mono, 0, sizeof (MonoBtlsX509LookupMono)); + return mono; +} + +MONO_API void +mono_btls_x509_lookup_mono_init (MonoBtlsX509LookupMono *mono, const void *instance, + MonoBtlsX509LookupMono_BySubject by_subject_func) +{ + mono->instance = instance; + mono->by_subject_func = by_subject_func; +} + +static int +mono_lookup_install (MonoLookup *lookup, MonoBtlsX509LookupMono *mono) +{ + MonoLookupNode *node; + + node = OPENSSL_malloc (sizeof (MonoLookupNode)); + if (!node) + return 0; + + memset (node, 0, sizeof (MonoLookupNode)); + mono->lookup = lookup; + node->mono = mono; + node->next = lookup->nodes; + lookup->nodes = node; + return 1; +} + +static int +mono_lookup_uninstall (MonoBtlsX509LookupMono *mono) +{ + MonoLookupNode **ptr; + + if (!mono->lookup) + return 0; + + for (ptr = &mono->lookup->nodes; *ptr; ptr = &(*ptr)->next) { + if ((*ptr)->mono == mono) { + *ptr = (*ptr)->next; + return 1; + } + } + + return 0; +} + +MONO_API int +mono_btls_x509_lookup_mono_free (MonoBtlsX509LookupMono *mono) +{ + mono->instance = NULL; + mono->by_subject_func = NULL; + + if (mono->lookup) { + if (!mono_lookup_uninstall (mono)) + return 0; + } + + mono->lookup = NULL; + + OPENSSL_free (mono); + return 1; +} + +static int +mono_lookup_ctrl (X509_LOOKUP *ctx, int cmd, const char *argp, long argl, char **ret) +{ + MonoLookup *lookup = (MonoLookup*)ctx->method_data; + MonoBtlsX509LookupMono *mono = (MonoBtlsX509LookupMono*)argp; + + if (!lookup || cmd != MONO_BTLS_X509_L_MONO_ADD) + return 0; + if (!mono || mono->lookup) + return 0; + + return mono_lookup_install (lookup, mono); +} + +static int +mono_lookup_new (X509_LOOKUP *ctx) +{ + MonoLookup *data; + + data = OPENSSL_malloc (sizeof (MonoLookup)); + if (!data) + return 0; + + memset (data, 0, sizeof (MonoLookup)); + ctx->method_data = (void *)data; + return 1; +} + +static void +mono_lookup_free (X509_LOOKUP *ctx) +{ + MonoLookup *lookup; + MonoLookupNode *ptr; + + lookup = (MonoLookup *)ctx->method_data; + ctx->method_data = NULL; + if (!lookup) + return; + + ptr = lookup->nodes; + lookup->nodes = NULL; + + while (ptr) { + MonoLookupNode *node = ptr; + ptr = ptr->next; + + if (node->mono) + node->mono->lookup = NULL; + node->mono = NULL; + node->next = NULL; + OPENSSL_free (node); + } + + OPENSSL_free (lookup); +} + +static int +mono_lookup_get_by_subject (X509_LOOKUP *ctx, int type, X509_NAME *name, X509_OBJECT *obj_ret) +{ + MonoLookup *lookup; + MonoBtlsX509Name *name_obj; + MonoLookupNode *node; + X509 *x509 = NULL; + int ret = 0; + + lookup = (MonoLookup *)ctx->method_data; + + if (!lookup || !lookup->nodes) + return 0; + if (type != X509_LU_X509) + return 0; + + name_obj = mono_btls_x509_name_from_name (name); + x509 = NULL; + + for (node = lookup->nodes; node; node = node->next) { + if (!node->mono || !node->mono->by_subject_func) + continue; + ret = (* node->mono->by_subject_func) (node->mono->instance, name_obj, &x509); + if (ret) + break; + } + + mono_btls_x509_name_free (name_obj); + + if (!ret) { + if (x509) + X509_free(x509); + return 0; + } + + obj_ret->type = X509_LU_X509; + obj_ret->data.x509 = x509; + return 1; +} + +static X509_LOOKUP_METHOD mono_lookup_method = { + "Mono lookup method", + mono_lookup_new, /* new */ + mono_lookup_free, /* free */ + NULL, /* init */ + NULL, /* shutdown */ + mono_lookup_ctrl, /* ctrl */ + mono_lookup_get_by_subject, /* get_by_subject */ + NULL, /* get_by_issuer_serial */ + NULL, /* get_by_fingerprint */ + NULL, /* get_by_alias */ +}; + +MONO_API X509_LOOKUP_METHOD * +mono_btls_x509_lookup_mono_method (void) +{ + return &mono_lookup_method; +} + +MONO_API int +mono_btls_x509_lookup_add_mono (MonoBtlsX509Lookup *lookup, MonoBtlsX509LookupMono *mono) +{ + if (mono_btls_x509_lookup_get_type (lookup) != MONO_BTLS_X509_LOOKUP_TYPE_MONO) + return 0; + return X509_LOOKUP_ctrl (mono_btls_x509_lookup_peek_lookup (lookup), + MONO_BTLS_X509_L_MONO_ADD, + (void*)mono, 0, NULL); +} diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-x509-lookup-mono.h b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-lookup-mono.h new file mode 100644 index 000000000..06df552c0 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-lookup-mono.h @@ -0,0 +1,36 @@ +// +// btls-x509-lookup-mono.h +// MonoBtls +// +// Created by Martin Baulig on 3/3/16. +// Copyright © 2016 Xamarin. All rights reserved. +// + +#ifndef __btls__btls_x509_lookup_mono__ +#define __btls__btls_x509_lookup_mono__ + +#include +#include +#include +#include + +typedef int (* MonoBtlsX509LookupMono_BySubject) (const void *instance, MonoBtlsX509Name *name, X509 **ret); + +MonoBtlsX509LookupMono * +mono_btls_x509_lookup_mono_new (void); + +int +mono_btls_x509_lookup_mono_free (MonoBtlsX509LookupMono *mono); + +void +mono_btls_x509_lookup_mono_init (MonoBtlsX509LookupMono *mono, const void *instance, + MonoBtlsX509LookupMono_BySubject by_subject_func); + +int +mono_btls_x509_lookup_add_mono (MonoBtlsX509Lookup *lookup, MonoBtlsX509LookupMono *mono); + +X509_LOOKUP_METHOD * +mono_btls_x509_lookup_mono_method (void); + +#endif /* defined(__btls__btls_x509_lookup_mono__) */ + diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-x509-lookup.c b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-lookup.c new file mode 100644 index 000000000..9e7dbee77 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-lookup.c @@ -0,0 +1,162 @@ +// +// btls-x509-lookup.c +// MonoBtls +// +// Created by Martin Baulig on 3/6/16. +// Copyright © 2016 Xamarin. All rights reserved. +// + +#include +#include + +struct MonoBtlsX509Lookup { + MonoBtlsX509LookupType type; + X509_LOOKUP *lookup; + int owns_lookup; + MonoBtlsX509Store *store; + CRYPTO_refcount_t references; +}; + +static X509_LOOKUP_METHOD * +get_lookup_method (MonoBtlsX509LookupType type) +{ + switch (type) { + case MONO_BTLS_X509_LOOKUP_TYPE_FILE: + return X509_LOOKUP_file (); + case MONO_BTLS_X509_LOOKUP_TYPE_HASH_DIR: + return X509_LOOKUP_hash_dir (); + case MONO_BTLS_X509_LOOKUP_TYPE_MONO: + return mono_btls_x509_lookup_mono_method (); + default: + return NULL; + } +} + +MONO_API MonoBtlsX509Lookup * +mono_btls_x509_lookup_new (MonoBtlsX509Store *store, MonoBtlsX509LookupType type) +{ + MonoBtlsX509Lookup *lookup; + X509_LOOKUP *store_lookup; + X509_LOOKUP_METHOD *method; + + method = get_lookup_method (type); + if (!method) + return NULL; + + lookup = OPENSSL_malloc (sizeof(MonoBtlsX509Lookup)); + if (!lookup) + return NULL; + + store_lookup = X509_STORE_add_lookup (mono_btls_x509_store_peek_store (store), method); + if (!store_lookup) { + OPENSSL_free (lookup); + return NULL; + } + + memset (lookup, 0, sizeof(MonoBtlsX509Lookup)); + // The X509_STORE owns the X509_LOOKUP. + lookup->store = mono_btls_x509_store_up_ref (store); + lookup->lookup = store_lookup; + lookup->owns_lookup = 0; + lookup->references = 1; + lookup->type = type; + return lookup; +} + +MONO_API int +mono_btls_x509_lookup_load_file (MonoBtlsX509Lookup *lookup, const char *file, MonoBtlsX509FileType type) +{ + return X509_LOOKUP_load_file (lookup->lookup, file, type); +} + +MONO_API int +mono_btls_x509_lookup_add_dir (MonoBtlsX509Lookup *lookup, const char *dir, MonoBtlsX509FileType type) +{ + return X509_LOOKUP_add_dir (lookup->lookup, dir, type); +} + +MONO_API MonoBtlsX509Lookup * +mono_btls_x509_lookup_up_ref (MonoBtlsX509Lookup *lookup) +{ + CRYPTO_refcount_inc (&lookup->references); + return lookup; +} + +MONO_API int +mono_btls_x509_lookup_free (MonoBtlsX509Lookup *lookup) +{ + if (!CRYPTO_refcount_dec_and_test_zero (&lookup->references)) + return 0; + + if (lookup->store) { + mono_btls_x509_store_free (lookup->store); + lookup->store = NULL; + } + + if (lookup->lookup) { + if (lookup->owns_lookup) + X509_LOOKUP_free (lookup->lookup); + lookup->lookup = NULL; + } + + OPENSSL_free (lookup); + return 1; +} + +MONO_API int +mono_btls_x509_lookup_init (MonoBtlsX509Lookup *lookup) +{ + return X509_LOOKUP_init (lookup->lookup); +} + +MONO_API int +mono_btls_x509_lookup_shutdown (MonoBtlsX509Lookup *lookup) +{ + return X509_LOOKUP_shutdown (lookup->lookup); +} + +MONO_API MonoBtlsX509LookupType +mono_btls_x509_lookup_get_type (MonoBtlsX509Lookup *lookup) +{ + return lookup->type; +} + +MONO_API X509_LOOKUP * +mono_btls_x509_lookup_peek_lookup (MonoBtlsX509Lookup *lookup) +{ + return lookup->lookup; +} + +MONO_API X509 * +mono_btls_x509_lookup_by_subject (MonoBtlsX509Lookup *lookup, MonoBtlsX509Name *name) +{ + X509_OBJECT obj; + X509 *x509; + int ret; + + ret = X509_LOOKUP_by_subject (lookup->lookup, X509_LU_X509, mono_btls_x509_name_peek_name (name), &obj); + if (ret != X509_LU_X509) { + X509_OBJECT_free_contents (&obj); + return NULL; + } + + x509 = X509_up_ref (obj.data.x509); + return x509; +} + +MONO_API X509 * +mono_btls_x509_lookup_by_fingerprint (MonoBtlsX509Lookup *lookup, unsigned char *bytes, int len) +{ + X509_OBJECT obj; + X509 *x509; + int ret; + + ret = X509_LOOKUP_by_fingerprint (lookup->lookup, X509_LU_X509, bytes, len, &obj); + if (ret != X509_LU_X509) { + X509_OBJECT_free_contents (&obj); + return NULL; + } + + x509 = X509_up_ref (obj.data.x509); + return x509; +} diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-x509-lookup.h b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-lookup.h new file mode 100644 index 000000000..df3d37f1c --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-lookup.h @@ -0,0 +1,58 @@ +// +// btls-x509-lookup.h +// MonoBtls +// +// Created by Martin Baulig on 3/3/16. +// Copyright © 2016 Xamarin. All rights reserved. +// + +#ifndef __btls__btls_x509_lookup__ +#define __btls__btls_x509_lookup__ + +#include +#include +#include +#include + +typedef enum { + MONO_BTLS_X509_LOOKUP_TYPE_UNKNOWN = 0, + MONO_BTLS_X509_LOOKUP_TYPE_FILE, + MONO_BTLS_X509_LOOKUP_TYPE_HASH_DIR, + MONO_BTLS_X509_LOOKUP_TYPE_MONO +} MonoBtlsX509LookupType; + +MonoBtlsX509Lookup * +mono_btls_x509_lookup_new (MonoBtlsX509Store *store, MonoBtlsX509LookupType type); + +int +mono_btls_x509_lookup_load_file (MonoBtlsX509Lookup *lookup, const char *file, MonoBtlsX509FileType type); + +int +mono_btls_x509_lookup_add_dir (MonoBtlsX509Lookup *lookup, const char *dir, MonoBtlsX509FileType type); + +MonoBtlsX509Lookup * +mono_btls_x509_lookup_up_ref (MonoBtlsX509Lookup *lookup); + +int +mono_btls_x509_lookup_free (MonoBtlsX509Lookup *lookup); + +int +mono_btls_x509_lookup_init (MonoBtlsX509Lookup *lookup); + +MonoBtlsX509LookupType +mono_btls_x509_lookup_get_type (MonoBtlsX509Lookup *lookup); + +X509_LOOKUP * +mono_btls_x509_lookup_peek_lookup (MonoBtlsX509Lookup *lookup); + +int +mono_btls_x509_lookup_shutdown (MonoBtlsX509Lookup *lookup); + +X509 * +mono_btls_x509_lookup_by_subject (MonoBtlsX509Lookup *lookup, MonoBtlsX509Name *name); + +X509 * +mono_btls_x509_lookup_by_fingerprint (MonoBtlsX509Lookup *lookup, unsigned char *bytes, int len); + +#endif /* defined(__btls__btls_x509_lookup__) */ + diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-x509-name.c b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-name.c new file mode 100644 index 000000000..7f9875880 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-name.c @@ -0,0 +1,296 @@ +// +// btls-x509-name.c +// MonoBtls +// +// Created by Martin Baulig on 3/5/16. +// Copyright © 2016 Xamarin. All rights reserved. +// + +#include + +struct MonoBtlsX509Name { + int owns; + X509_NAME *name; +}; + +MONO_API MonoBtlsX509Name * +mono_btls_x509_name_from_name (X509_NAME *xn) +{ + MonoBtlsX509Name *name; + + name = OPENSSL_malloc (sizeof (MonoBtlsX509Name)); + if (!name) + return NULL; + + memset(name, 0, sizeof(MonoBtlsX509Name)); + name->name = xn; + return name; +} + +MONO_API MonoBtlsX509Name * +mono_btls_x509_name_copy (X509_NAME *xn) +{ + MonoBtlsX509Name *name; + + name = OPENSSL_malloc (sizeof (MonoBtlsX509Name)); + if (!name) + return NULL; + + memset(name, 0, sizeof(MonoBtlsX509Name)); + name->name = X509_NAME_dup(xn); + name->owns = 1; + return name; +} + +MONO_API void +mono_btls_x509_name_free (MonoBtlsX509Name *name) +{ + if (name->owns) { + if (name->name) { + X509_NAME_free(name->name); + name->name = NULL; + } + } + OPENSSL_free(name); +} + +MONO_API X509_NAME * +mono_btls_x509_name_peek_name (MonoBtlsX509Name *name) +{ + return name->name; +} + +MONO_API int +mono_btls_x509_name_print_bio (MonoBtlsX509Name *name, BIO *bio) +{ + return X509_NAME_print_ex (bio, name->name, 0, ASN1_STRFLGS_RFC2253 | XN_FLAG_FN_SN | XN_FLAG_SEP_CPLUS_SPC | XN_FLAG_DN_REV); +} + +MONO_API int +mono_btls_x509_name_get_raw_data (MonoBtlsX509Name *name, void **buffer, int use_canon_enc) +{ + int len; + void *ptr; + + if (use_canon_enc) { + // make sure canon_enc is initialized. + i2d_X509_NAME (name->name, NULL); + + len = name->name->canon_enclen; + ptr = name->name->canon_enc; + } else { + len = (int)name->name->bytes->length; + ptr = name->name->bytes->data; + } + + *buffer = OPENSSL_malloc (len); + if (!*buffer) + return 0; + + memcpy (*buffer, ptr, len); + return len; +} + +MONO_API MonoBtlsX509Name * +mono_btls_x509_name_from_data (const void *data, int len, int use_canon_enc) +{ + MonoBtlsX509Name *name; + uint8_t *buf; + const unsigned char *ptr; + X509_NAME *ret; + + name = OPENSSL_malloc (sizeof (MonoBtlsX509Name)); + if (!name) + return NULL; + + memset (name, 0, sizeof(MonoBtlsX509Name)); + name->owns = 1; + + name->name = X509_NAME_new (); + if (!name->name) { + OPENSSL_free (name); + return NULL; + } + + if (use_canon_enc) { + CBB cbb, contents; + size_t buf_len; + + // re-add ASN1 SEQUENCE header. + CBB_init(&cbb, 0); + if (!CBB_add_asn1(&cbb, &contents, 0x30) || + !CBB_add_bytes(&contents, data, len) || + !CBB_finish(&cbb, &buf, &buf_len)) { + CBB_cleanup (&cbb); + mono_btls_x509_name_free (name); + return NULL; + } + + ptr = buf; + len = (int)buf_len; + } else { + ptr = data; + buf = NULL; + } + + ret = d2i_X509_NAME (&name->name, &ptr, len); + + if (buf) + OPENSSL_free (buf); + + if (ret != name->name) { + mono_btls_x509_name_free (name); + return NULL; + } + + return name; +} + +MONO_API int +mono_btls_x509_name_print_string (MonoBtlsX509Name *name, char *buffer, int size) +{ + *buffer = 0; + return X509_NAME_oneline (name->name, buffer, size) != NULL; +} + +MONO_API int64_t +mono_btls_x509_name_hash (MonoBtlsX509Name *name) +{ + return X509_NAME_hash (name->name); +} + +MONO_API int64_t +mono_btls_x509_name_hash_old (MonoBtlsX509Name *name) +{ + return X509_NAME_hash_old (name->name); +} + +MONO_API int +mono_btls_x509_name_get_entry_count (MonoBtlsX509Name *name) +{ + return X509_NAME_entry_count (name->name); +} + +static MonoBtlsX509NameEntryType +nid2mono (int nid) +{ + switch (nid) { + case NID_countryName: + return MONO_BTLS_X509_NAME_ENTRY_TYPE_COUNTRY_NAME; + case NID_organizationName: + return MONO_BTLS_X509_NAME_ENTRY_TYPE_ORGANIZATION_NAME; + case NID_organizationalUnitName: + return MONO_BTLS_X509_NAME_ENTRY_TYPE_ORGANIZATIONAL_UNIT_NAME; + case NID_commonName: + return MONO_BTLS_X509_NAME_ENTRY_TYPE_COMMON_NAME; + case NID_localityName: + return MONO_BTLS_X509_NAME_ENTRY_TYPE_LOCALITY_NAME; + case NID_stateOrProvinceName: + return MONO_BTLS_X509_NAME_ENTRY_TYPE_STATE_OR_PROVINCE_NAME; + case NID_streetAddress: + return MONO_BTLS_X509_NAME_ENTRY_TYPE_STREET_ADDRESS; + case NID_serialNumber: + return MONO_BTLS_X509_NAME_ENTRY_TYPE_SERIAL_NUMBER; + case NID_domainComponent: + return MONO_BTLS_X509_NAME_ENTRY_TYPE_DOMAIN_COMPONENT; + case NID_userId: + return MONO_BTLS_X509_NAME_ENTRY_TYPE_USER_ID; + case NID_dnQualifier: + return MONO_BTLS_X509_NAME_ENTRY_TYPE_DN_QUALIFIER; + case NID_title: + return MONO_BTLS_X509_NAME_ENTRY_TYPE_TITLE; + case NID_surname: + return MONO_BTLS_X509_NAME_ENTRY_TYPE_SURNAME; + case NID_givenName: + return MONO_BTLS_X509_NAME_ENTRY_TYPE_GIVEN_NAME; + case NID_initials: + return MONO_BTLS_X509_NAME_ENTRY_TYPE_INITIAL; + default: + return MONO_BTLS_X509_NAME_ENTRY_TYPE_UNKNOWN; + } +} + +MONO_API MonoBtlsX509NameEntryType +mono_btls_x509_name_get_entry_type (MonoBtlsX509Name *name, int index) +{ + X509_NAME_ENTRY *entry; + ASN1_OBJECT *obj; + + if (index >= X509_NAME_entry_count (name->name)) + return -1; + + entry = X509_NAME_get_entry (name->name, index); + if (!entry) + return -1; + + obj = X509_NAME_ENTRY_get_object (entry); + if (!obj) + return -1; + + return nid2mono (OBJ_obj2nid (obj)); +} + +MONO_API int +mono_btls_x509_name_get_entry_oid (MonoBtlsX509Name *name, int index, char *buffer, int size) +{ + X509_NAME_ENTRY *entry; + ASN1_OBJECT *obj; + + if (index >= X509_NAME_entry_count (name->name)) + return 0; + + entry = X509_NAME_get_entry (name->name, index); + if (!entry) + return 0; + + obj = X509_NAME_ENTRY_get_object (entry); + if (!obj) + return 0; + + return OBJ_obj2txt (buffer, size, obj, 1); +} + +MONO_API int +mono_btls_x509_name_get_entry_oid_data (MonoBtlsX509Name *name, int index, const void **data) +{ + X509_NAME_ENTRY *entry; + ASN1_OBJECT *obj; + + if (index >= X509_NAME_entry_count (name->name)) + return -1; + + entry = X509_NAME_get_entry (name->name, index); + if (!entry) + return -1; + + obj = X509_NAME_ENTRY_get_object (entry); + if (!obj) + return -1; + + *data = obj->data; + return obj->length; +} + +MONO_API int +mono_btls_x509_name_get_entry_value (MonoBtlsX509Name *name, int index, int *tag, unsigned char **str) +{ + X509_NAME_ENTRY *entry; + ASN1_STRING *data; + + *str = NULL; + *tag = 0; + + if (index >= X509_NAME_entry_count (name->name)) + return 0; + + entry = X509_NAME_get_entry (name->name, index); + if (!entry) + return 0; + + data = X509_NAME_ENTRY_get_data (entry); + if (!data) + return 0; + + *tag = data->type; + return ASN1_STRING_to_UTF8 (str, data); +} diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-x509-name.h b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-name.h new file mode 100644 index 000000000..9d43bc649 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-name.h @@ -0,0 +1,80 @@ +// +// btls-x509-name.h +// MonoBtls +// +// Created by Martin Baulig on 3/5/16. +// Copyright © 2016 Xamarin. All rights reserved. +// + +#ifndef __btls__btls_x509_name__ +#define __btls__btls_x509_name__ + +#include +#include + +typedef enum { + MONO_BTLS_X509_NAME_ENTRY_TYPE_UNKNOWN = 0, + MONO_BTLS_X509_NAME_ENTRY_TYPE_COUNTRY_NAME, + MONO_BTLS_X509_NAME_ENTRY_TYPE_ORGANIZATION_NAME, + MONO_BTLS_X509_NAME_ENTRY_TYPE_ORGANIZATIONAL_UNIT_NAME, + MONO_BTLS_X509_NAME_ENTRY_TYPE_COMMON_NAME, + MONO_BTLS_X509_NAME_ENTRY_TYPE_LOCALITY_NAME, + MONO_BTLS_X509_NAME_ENTRY_TYPE_STATE_OR_PROVINCE_NAME, + MONO_BTLS_X509_NAME_ENTRY_TYPE_STREET_ADDRESS, + MONO_BTLS_X509_NAME_ENTRY_TYPE_SERIAL_NUMBER, + MONO_BTLS_X509_NAME_ENTRY_TYPE_DOMAIN_COMPONENT, + MONO_BTLS_X509_NAME_ENTRY_TYPE_USER_ID, + MONO_BTLS_X509_NAME_ENTRY_TYPE_EMAIL, + MONO_BTLS_X509_NAME_ENTRY_TYPE_DN_QUALIFIER, + MONO_BTLS_X509_NAME_ENTRY_TYPE_TITLE, + MONO_BTLS_X509_NAME_ENTRY_TYPE_SURNAME, + MONO_BTLS_X509_NAME_ENTRY_TYPE_GIVEN_NAME, + MONO_BTLS_X509_NAME_ENTRY_TYPE_INITIAL +} MonoBtlsX509NameEntryType; + +MonoBtlsX509Name * +mono_btls_x509_name_from_name (X509_NAME *name); + +MonoBtlsX509Name * +mono_btls_x509_name_copy (X509_NAME *xn); + +void +mono_btls_x509_name_free (MonoBtlsX509Name *name); + +X509_NAME * +mono_btls_x509_name_peek_name (MonoBtlsX509Name *name); + +MonoBtlsX509Name * +mono_btls_x509_name_from_data (const void *data, int len, int use_canon_enc); + +int +mono_btls_x509_name_print_bio (MonoBtlsX509Name *name, BIO *bio); + +int +mono_btls_x509_name_print_string (MonoBtlsX509Name *name, char *buffer, int size); + +int +mono_btls_x509_name_get_raw_data (MonoBtlsX509Name *name, void **buffer, int use_canon_enc); + +int64_t +mono_btls_x509_name_hash (MonoBtlsX509Name *name); + +int64_t +mono_btls_x509_name_hash_old (MonoBtlsX509Name *name); + +int +mono_btls_x509_name_get_entry_count (MonoBtlsX509Name *name); + +MonoBtlsX509NameEntryType +mono_btls_x509_name_get_entry_type (MonoBtlsX509Name *name, int index); + +int +mono_btls_x509_name_get_entry_oid (MonoBtlsX509Name *name, int index, char *buffer, int size); + +int +mono_btls_x509_name_get_entry_oid_data (MonoBtlsX509Name *name, int index, const void **data); + +int +mono_btls_x509_name_get_entry_value (MonoBtlsX509Name *name, int index, int *tag, unsigned char **str); + +#endif /* __btls__btls_x509_name__ */ diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-x509-revoked.c b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-revoked.c new file mode 100644 index 000000000..bf9af79de --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-revoked.c @@ -0,0 +1,72 @@ +// +// btls-x509-revoked.c +// MonoBtls +// +// Created by Martin Baulig on 3/23/16. +// Copyright © 2016 Xamarin. All rights reserved. +// + +#include + +struct MonoBtlsX509Revoked { + MonoBtlsX509Crl *owner; + X509_REVOKED *revoked; +}; + +MONO_API MonoBtlsX509Revoked * +mono_btls_x509_revoked_new (MonoBtlsX509Crl *owner, X509_REVOKED *revoked) +{ + MonoBtlsX509Revoked *instance; + + instance = OPENSSL_malloc (sizeof (MonoBtlsX509Revoked)); + memset (instance, 0, sizeof (MonoBtlsX509Revoked)); + + instance->owner = mono_btls_x509_crl_ref (owner); + instance->revoked = revoked; + return instance; +} + +MONO_API void +mono_btls_x509_revoked_free (MonoBtlsX509Revoked *revoked) +{ + mono_btls_x509_crl_free (revoked->owner); + OPENSSL_free (revoked); +} + +MONO_API int +mono_btls_x509_revoked_get_serial_number (MonoBtlsX509Revoked *revoked, char *buffer, int size) +{ + ASN1_INTEGER *serial; + + serial = revoked->revoked->serialNumber; + if (serial->length == 0 || serial->length+1 > size) + return 0; + + memcpy (buffer, serial->data, serial->length); + return serial->length; +} + +MONO_API int64_t +mono_btls_x509_revoked_get_revocation_date (MonoBtlsX509Revoked *revoked) +{ + ASN1_TIME *date; + + date = revoked->revoked->revocationDate; + if (!date) + return 0; + + return mono_btls_util_asn1_time_to_ticks (date); +} + +MONO_API int +mono_btls_x509_revoked_get_reason (MonoBtlsX509Revoked *revoked) +{ + return revoked->revoked->reason; +} + +MONO_API int +mono_btls_x509_revoked_get_sequence (MonoBtlsX509Revoked *revoked) +{ + return revoked->revoked->sequence; +} + diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-x509-revoked.h b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-revoked.h new file mode 100644 index 000000000..592fc9316 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-revoked.h @@ -0,0 +1,34 @@ +// +// btls-x509-revoked.h +// MonoBtls +// +// Created by Martin Baulig on 3/23/16. +// Copyright © 2016 Xamarin. All rights reserved. +// + +#ifndef __btls__btls_x509_revoked__ +#define __btls__btls_x509_revoked__ + +#include +#include +#include + +MonoBtlsX509Revoked * +mono_btls_x509_revoked_new (MonoBtlsX509Crl *owner, X509_REVOKED *revoked); + +void +mono_btls_x509_revoked_free (MonoBtlsX509Revoked *revoked); + +int +mono_btls_x509_revoked_get_serial_number (MonoBtlsX509Revoked *revoked, char *buffer, int size); + +int64_t +mono_btls_x509_revoked_get_revocation_date (MonoBtlsX509Revoked *revoked); + +int +mono_btls_x509_revoked_get_reason (MonoBtlsX509Revoked *revoked); + +int +mono_btls_x509_revoked_get_sequence (MonoBtlsX509Revoked *revoked); + +#endif /* __btls__btls_x509_revoked__ */ diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-x509-store-ctx.c b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-store-ctx.c new file mode 100644 index 000000000..8bbb732ac --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-store-ctx.c @@ -0,0 +1,217 @@ +// +// btls-x509-store-ctx.c +// MonoBtls +// +// Created by Martin Baulig on 3/5/16. +// Copyright © 2016 Xamarin. All rights reserved. +// + +#include + +struct MonoBtlsX509StoreCtx { + int owns; + X509_STORE_CTX *ctx; + CRYPTO_refcount_t references; + MonoBtlsX509Store *store; + MonoBtlsX509Chain *chain; +}; + +MONO_API MonoBtlsX509StoreCtx * +mono_btls_x509_store_ctx_from_ptr (X509_STORE_CTX *ptr) +{ + MonoBtlsX509StoreCtx *ctx; + + ctx = OPENSSL_malloc (sizeof(MonoBtlsX509StoreCtx)); + if (!ctx) + return NULL; + + memset (ctx, 0, sizeof (MonoBtlsX509StoreCtx)); + ctx->ctx = ptr; + ctx->references = 1; + return ctx; +} + +MONO_API MonoBtlsX509StoreCtx * +mono_btls_x509_store_ctx_new (void) +{ + MonoBtlsX509StoreCtx *ctx; + + ctx = OPENSSL_malloc (sizeof(MonoBtlsX509StoreCtx)); + if (!ctx) + return NULL; + + memset (ctx, 0, sizeof (MonoBtlsX509StoreCtx)); + ctx->ctx = X509_STORE_CTX_new (); + ctx->references = 1; + ctx->owns = 1; + return ctx; +} + +MONO_API MonoBtlsX509StoreCtx * +mono_btls_x509_store_ctx_up_ref (MonoBtlsX509StoreCtx *ctx) +{ + CRYPTO_refcount_inc (&ctx->references); + return ctx; +} + +MONO_API int +mono_btls_x509_store_ctx_free (MonoBtlsX509StoreCtx *ctx) +{ + if (!CRYPTO_refcount_dec_and_test_zero (&ctx->references)) + return 0; + + if (ctx->owns) { + X509_STORE_CTX_cleanup (ctx->ctx); + X509_STORE_CTX_free (ctx->ctx); + ctx->owns = 0; + } + if (ctx->store) { + mono_btls_x509_store_free (ctx->store); + ctx->store = NULL; + } + if (ctx->chain) { + mono_btls_x509_chain_free (ctx->chain); + ctx->chain = NULL; + } + OPENSSL_free (ctx); + return 1; +} + +MONO_API int +mono_btls_x509_store_ctx_get_error (MonoBtlsX509StoreCtx *ctx, const char **error_string) +{ + int error; + + error = X509_STORE_CTX_get_error (ctx->ctx); + if (error_string) + *error_string = X509_verify_cert_error_string (error); + return error; +} + +MONO_API int +mono_btls_x509_store_ctx_get_error_depth (MonoBtlsX509StoreCtx *ctx) +{ + return X509_STORE_CTX_get_error_depth (ctx->ctx); +} + +MONO_API MonoBtlsX509Chain * +mono_btls_x509_store_ctx_get_chain (MonoBtlsX509StoreCtx *ctx) +{ + STACK_OF(X509) *certs; + + certs = X509_STORE_CTX_get_chain (ctx->ctx); + if (!certs) + return NULL; + + return mono_btls_x509_chain_from_certs (certs); +} + +MONO_API MonoBtlsX509Chain * +mono_btls_x509_store_ctx_get_untrusted (MonoBtlsX509StoreCtx *ctx) +{ + STACK_OF(X509) *untrusted; + + /* + * Unfortunately, there is no accessor function for this. + * + * This is the set of certificate that's passed in by + * X509_STORE_CTX_init() and X509_STORE_CTX_set_chain(). + */ + untrusted = ctx->ctx->untrusted; + if (!untrusted) + return NULL; + + return mono_btls_x509_chain_from_certs (untrusted); +} + +MONO_API int +mono_btls_x509_store_ctx_init (MonoBtlsX509StoreCtx *ctx, + MonoBtlsX509Store *store, MonoBtlsX509Chain *chain) +{ + STACK_OF(X509) *certs; + X509 *leaf; + int ret; + + if (ctx->store) + return 0; + + certs = mono_btls_x509_chain_peek_certs (chain); + if (!certs || !sk_X509_num (certs)) + return 0; + + ctx->store = mono_btls_x509_store_up_ref(store); + ctx->chain = mono_btls_x509_chain_up_ref(chain); + + leaf = sk_X509_value (certs, 0); + ret = X509_STORE_CTX_init (ctx->ctx, mono_btls_x509_store_peek_store (store), leaf, certs); + if (ret != 1) + return ret; + + X509_STORE_CTX_set_app_data (ctx->ctx, ctx); + return 1; +} + +MONO_API int +mono_btls_x509_store_ctx_set_param (MonoBtlsX509StoreCtx *ctx, MonoBtlsX509VerifyParam *param) +{ + return X509_VERIFY_PARAM_set1 (X509_STORE_CTX_get0_param (ctx->ctx), mono_btls_x509_verify_param_peek_param (param)); +} + +MONO_API int +mono_btls_x509_store_ctx_verify_cert (MonoBtlsX509StoreCtx *ctx) +{ + return X509_verify_cert (ctx->ctx); +} + +MONO_API X509 * +mono_btls_x509_store_ctx_get_by_subject (MonoBtlsX509StoreCtx *ctx, MonoBtlsX509Name *name) +{ + X509_OBJECT obj; + X509 *x509; + int ret; + + ret = X509_STORE_get_by_subject (ctx->ctx, X509_LU_X509, mono_btls_x509_name_peek_name (name), &obj); + if (ret != X509_LU_X509) { + X509_OBJECT_free_contents (&obj); + return NULL; + } + + x509 = X509_up_ref (obj.data.x509); + return x509; +} + +MONO_API X509 * +mono_btls_x509_store_ctx_get_current_cert (MonoBtlsX509StoreCtx *ctx) +{ + X509 *x509 = X509_STORE_CTX_get_current_cert (ctx->ctx); + if (!x509) + return NULL; + return X509_up_ref (x509); +} + +MONO_API X509 * +mono_btls_x509_store_ctx_get_current_issuer (MonoBtlsX509StoreCtx *ctx) +{ + X509 *x509 = X509_STORE_CTX_get0_current_issuer (ctx->ctx); + if (!x509) + return NULL; + return X509_up_ref (x509); +} + +MONO_API MonoBtlsX509VerifyParam * +mono_btls_x509_store_ctx_get_verify_param (MonoBtlsX509StoreCtx *ctx) +{ + X509_VERIFY_PARAM *param; + + param = X509_STORE_CTX_get0_param (ctx->ctx); + if (!param) + return NULL; + + return mono_btls_x509_verify_param_from_store_ctx (ctx, param); +} + +MONO_API int +mono_btls_x509_store_ctx_get_foo (MonoBtlsX509StoreCtx *ctx) +{ + return 0; +} diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-x509-store-ctx.h b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-store-ctx.h new file mode 100644 index 000000000..188092e0c --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-store-ctx.h @@ -0,0 +1,66 @@ +// +// btls-x509-store-ctx.h +// MonoBtls +// +// Created by Martin Baulig on 3/3/16. +// Copyright © 2016 Xamarin. All rights reserved. +// + +#ifndef __btls__btls_x509_store_ctx__ +#define __btls__btls_x509_store_ctx__ + +#include +#include +#include +#include +#include +#include + +MonoBtlsX509StoreCtx * +mono_btls_x509_store_ctx_from_ptr (X509_STORE_CTX *ptr); + +MonoBtlsX509StoreCtx * +mono_btls_x509_store_ctx_new (void); + +MonoBtlsX509StoreCtx * +mono_btls_x509_store_ctx_up_ref (MonoBtlsX509StoreCtx *ctx); + +int +mono_btls_x509_store_ctx_free (MonoBtlsX509StoreCtx *ctx); + +int +mono_btls_x509_store_ctx_get_error (MonoBtlsX509StoreCtx *ctx, const char **error_string); + +int +mono_btls_x509_store_ctx_get_error_depth (MonoBtlsX509StoreCtx *ctx); + +MonoBtlsX509Chain * +mono_btls_x509_store_ctx_get_chain (MonoBtlsX509StoreCtx *ctx); + +X509 * +mono_btls_x509_store_ctx_get_current_cert (MonoBtlsX509StoreCtx *ctx); + +X509 * +mono_btls_x509_store_ctx_get_current_issuer (MonoBtlsX509StoreCtx *ctx); + +int +mono_btls_x509_store_ctx_init (MonoBtlsX509StoreCtx *ctx, + MonoBtlsX509Store *store, MonoBtlsX509Chain *chain); + +int +mono_btls_x509_store_ctx_set_param (MonoBtlsX509StoreCtx *ctx, MonoBtlsX509VerifyParam *param); + +X509 * +mono_btls_x509_store_ctx_get_by_subject (MonoBtlsX509StoreCtx *ctx, MonoBtlsX509Name *name); + +int +mono_btls_x509_store_ctx_verify_cert (MonoBtlsX509StoreCtx *ctx); + +MonoBtlsX509VerifyParam * +mono_btls_x509_store_ctx_get_verify_param (MonoBtlsX509StoreCtx *ctx); + +MonoBtlsX509Chain * +mono_btls_x509_store_ctx_get_untrusted (MonoBtlsX509StoreCtx *ctx); + +#endif /* defined(__btls__btls_x509_store_ctx__) */ + diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-x509-store.c b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-store.c new file mode 100644 index 000000000..3534739a6 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-store.c @@ -0,0 +1,110 @@ +// +// btls-x509-store.c +// MonoBtls +// +// Created by Martin Baulig on 3/3/16. +// Copyright © 2016 Xamarin. All rights reserved. +// + +#include + +struct MonoBtlsX509Store { + X509_STORE *store; + CRYPTO_refcount_t references; +}; + +MONO_API MonoBtlsX509Store * +mono_btls_x509_store_from_store (X509_STORE *ctx) +{ + MonoBtlsX509Store *store; + + store = OPENSSL_malloc (sizeof(MonoBtlsX509Store)); + if (!store) + return NULL; + + memset (store, 0, sizeof(MonoBtlsX509Store)); + store->store = ctx; + CRYPTO_refcount_inc (&store->store->references); + store->references = 1; + return store; +} + +MONO_API MonoBtlsX509Store * +mono_btls_x509_store_from_ctx (X509_STORE_CTX *ctx) +{ + return mono_btls_x509_store_from_store (ctx->ctx); +} + +MONO_API MonoBtlsX509Store * +mono_btls_x509_store_new (void) +{ + MonoBtlsX509Store *store; + + store = OPENSSL_malloc (sizeof(MonoBtlsX509Store)); + if (!store) + return NULL; + + memset (store, 0, sizeof(MonoBtlsX509Store)); + store->store = X509_STORE_new (); + store->references = 1; + return store; +} + +MONO_API X509_STORE * +mono_btls_x509_store_peek_store (MonoBtlsX509Store *store) +{ + return store->store; +} + +MONO_API MonoBtlsX509Store * +mono_btls_x509_store_from_ssl_ctx (MonoBtlsSslCtx *ctx) +{ + X509_STORE *store = mono_btls_ssl_ctx_peek_store (ctx); + return mono_btls_x509_store_from_store (store); +} + +MONO_API int +mono_btls_x509_store_free (MonoBtlsX509Store *store) +{ + if (!CRYPTO_refcount_dec_and_test_zero(&store->references)) + return 0; + + if (store->store) { + X509_STORE_free (store->store); + store->store = NULL; + } + OPENSSL_free (store); + return 1; +} + +MONO_API MonoBtlsX509Store * +mono_btls_x509_store_up_ref (MonoBtlsX509Store *store) +{ + CRYPTO_refcount_inc (&store->references); + return store; +} + +MONO_API int +mono_btls_x509_store_add_cert (MonoBtlsX509Store *store, X509 *cert) +{ + return X509_STORE_add_cert (store->store, cert); +} + +MONO_API int +mono_btls_x509_store_load_locations (MonoBtlsX509Store *store, const char *file, const char *path) +{ + return X509_STORE_load_locations (store->store, file, path); +} + +MONO_API int +mono_btls_x509_store_set_default_paths (MonoBtlsX509Store *store) +{ + return X509_STORE_set_default_paths (store->store); +} + +MONO_API int +mono_btls_x509_store_get_count (MonoBtlsX509Store *store) +{ + return (int)sk_X509_OBJECT_num (store->store->objs); +} + diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-x509-store.h b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-store.h new file mode 100644 index 000000000..67ffe00cd --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-store.h @@ -0,0 +1,46 @@ +// +// btls-x509-store.h +// MonoBtls +// +// Created by Martin Baulig on 3/3/16. +// Copyright © 2016 Xamarin. All rights reserved. +// + +#ifndef __btls__btls_x509_store__ +#define __btls__btls_x509_store__ + +#include +#include + +MonoBtlsX509Store * +mono_btls_x509_store_new (void); + +MonoBtlsX509Store * +mono_btls_x509_store_from_ctx (X509_STORE_CTX *ctx); + +MonoBtlsX509Store * +mono_btls_x509_store_from_ssl_ctx (MonoBtlsSslCtx *ctx); + +MonoBtlsX509Store * +mono_btls_x509_store_up_ref (MonoBtlsX509Store *store); + +int +mono_btls_x509_store_free (MonoBtlsX509Store *store); + +X509_STORE * +mono_btls_x509_store_peek_store (MonoBtlsX509Store *store); + +int +mono_btls_x509_store_add_cert (MonoBtlsX509Store *store, X509 *cert); + +int +mono_btls_x509_store_load_locations (MonoBtlsX509Store *store, const char *file, const char *path); + +int +mono_btls_x509_store_set_default_paths (MonoBtlsX509Store *store); + +int +mono_btls_x509_store_get_count (MonoBtlsX509Store *store); + +#endif /* defined(__btls__btls_x509_store__) */ + diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-x509-verify-param.c b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-verify-param.c new file mode 100644 index 000000000..24be3da8c --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-verify-param.c @@ -0,0 +1,221 @@ +// +// btls-x509-verify-param.c +// MonoBtls +// +// Created by Martin Baulig on 3/5/16. +// Copyright © 2016 Xamarin. All rights reserved. +// + +#include +#include + +struct MonoBtlsX509VerifyParam { + int owns; + MonoBtlsX509StoreCtx *owner; + X509_VERIFY_PARAM *param; +}; + +MONO_API MonoBtlsX509VerifyParam * +mono_btls_x509_verify_param_new (void) +{ + MonoBtlsX509VerifyParam *param; + + param = OPENSSL_malloc (sizeof(MonoBtlsX509VerifyParam)); + if (!param) + return NULL; + memset (param, 0, sizeof (MonoBtlsX509VerifyParam)); + param->param = X509_VERIFY_PARAM_new(); + param->owns = 1; + return param; +} + +MONO_API MonoBtlsX509VerifyParam * +mono_btls_x509_verify_param_from_store_ctx (MonoBtlsX509StoreCtx *ctx, X509_VERIFY_PARAM *param) +{ + MonoBtlsX509VerifyParam *instance; + + instance = OPENSSL_malloc (sizeof(MonoBtlsX509VerifyParam)); + if (!instance) + return NULL; + memset (instance, 0, sizeof (MonoBtlsX509VerifyParam)); + instance->param = param; + instance->owner = mono_btls_x509_store_ctx_up_ref (ctx); + return instance; +} + +MONO_API MonoBtlsX509VerifyParam * +mono_btls_x509_verify_param_copy (const MonoBtlsX509VerifyParam *from) +{ + MonoBtlsX509VerifyParam *param; + + param = mono_btls_x509_verify_param_new (); + if (!param) + return NULL; + + X509_VERIFY_PARAM_set1 (param->param, from->param); + return param; +} + +MONO_API const X509_VERIFY_PARAM * +mono_btls_x509_verify_param_peek_param (const MonoBtlsX509VerifyParam *param) +{ + return param->param; +} + +MONO_API int +mono_btls_x509_verify_param_can_modify (MonoBtlsX509VerifyParam *param) +{ + return param->owns; +} + +MONO_API MonoBtlsX509VerifyParam * +mono_btls_x509_verify_param_lookup (const char *name) +{ + MonoBtlsX509VerifyParam *param; + const X509_VERIFY_PARAM *p; + + p = X509_VERIFY_PARAM_lookup(name); + if (!p) + return NULL; + + param = OPENSSL_malloc (sizeof(MonoBtlsX509VerifyParam)); + if (!param) + return NULL; + memset (param, 0, sizeof (MonoBtlsX509VerifyParam)); + param->param = (X509_VERIFY_PARAM *)p; + return param; +} + +MONO_API void +mono_btls_x509_verify_param_free (MonoBtlsX509VerifyParam *param) +{ + if (param->owns) { + if (param->param) { + X509_VERIFY_PARAM_free (param->param); + param->param = NULL; + } + } + if (param->owner) { + mono_btls_x509_store_ctx_free (param->owner); + param->owner = NULL; + } + OPENSSL_free (param); +} + +MONO_API int +mono_btls_x509_verify_param_set_name (MonoBtlsX509VerifyParam *param, const char *name) +{ + if (!param->owns) + return -1; + return X509_VERIFY_PARAM_set1_name (param->param, name); +} + +MONO_API int +mono_btls_x509_verify_param_set_host (MonoBtlsX509VerifyParam *param, const char *host, int namelen) +{ + if (!param->owns) + return -1; + return X509_VERIFY_PARAM_set1_host (param->param, host, namelen); +} + +MONO_API int +mono_btls_x509_verify_param_add_host (MonoBtlsX509VerifyParam *param, const char *host, int namelen) +{ + if (!param->owns) + return -1; + return X509_VERIFY_PARAM_set1_host (param->param, host, namelen); +} + +MONO_API uint64_t +mono_btls_x509_verify_param_get_flags (MonoBtlsX509VerifyParam *param) +{ + return X509_VERIFY_PARAM_get_flags (param->param); +} + +MONO_API int +mono_btls_x509_verify_param_set_flags (MonoBtlsX509VerifyParam *param, uint64_t flags) +{ + if (!param->owns) + return -1; + return X509_VERIFY_PARAM_set_flags (param->param, flags); +} + +MONO_API MonoBtlsX509VerifyFlags +mono_btls_x509_verify_param_get_mono_flags (MonoBtlsX509VerifyParam *param) +{ + MonoBtlsX509VerifyFlags current; + uint64_t flags; + + if (!param->owns) + return -1; + + current = 0; + flags = X509_VERIFY_PARAM_get_flags (param->param); + + if (flags & X509_V_FLAG_CRL_CHECK) + current |= MONO_BTLS_X509_VERIFY_FLAGS_CRL_CHECK; + if (flags & X509_V_FLAG_CRL_CHECK_ALL) + current |= MONO_BTLS_X509_VERIFY_FLAGS_CRL_CHECK_ALL; + if (flags & X509_V_FLAG_X509_STRICT) + current |= MONO_BTLS_X509_VERIFY_FLAGS_X509_STRICT; + + return current; +} + +MONO_API int +mono_btls_x509_verify_param_set_mono_flags (MonoBtlsX509VerifyParam *param, MonoBtlsX509VerifyFlags flags) +{ + uint64_t current; + + if (!param->owns) + return -1; + + current = X509_VERIFY_PARAM_get_flags (param->param); + if (flags & MONO_BTLS_X509_VERIFY_FLAGS_CRL_CHECK) + current |= X509_V_FLAG_CRL_CHECK; + if (flags & MONO_BTLS_X509_VERIFY_FLAGS_CRL_CHECK_ALL) + current |= X509_V_FLAG_CRL_CHECK_ALL; + if (flags & MONO_BTLS_X509_VERIFY_FLAGS_X509_STRICT) + current |= X509_V_FLAG_X509_STRICT; + + return X509_VERIFY_PARAM_set_flags (param->param, current); +} + +MONO_API int +mono_btls_x509_verify_param_set_purpose (MonoBtlsX509VerifyParam *param, MonoBtlsX509Purpose purpose) +{ + if (!param->owns) + return -1; + return X509_VERIFY_PARAM_set_purpose (param->param, purpose); +} + +MONO_API int +mono_btls_x509_verify_param_get_depth (MonoBtlsX509VerifyParam *param) +{ + return X509_VERIFY_PARAM_get_depth (param->param); +} + +MONO_API int +mono_btls_x509_verify_param_set_depth (MonoBtlsX509VerifyParam *param, int depth) +{ + if (!param->owns) + return -1; + X509_VERIFY_PARAM_set_depth (param->param, depth); + return 1; +} + +MONO_API int +mono_btls_x509_verify_param_set_time (MonoBtlsX509VerifyParam *param, int64_t time) +{ + if (!param->owns) + return -1; + X509_VERIFY_PARAM_set_time (param->param, time); + return 1; +} + +MONO_API char * +mono_btls_x509_verify_param_get_peername (MonoBtlsX509VerifyParam *param) +{ + char *peer = X509_VERIFY_PARAM_get0_peername (param->param); + return peer; +} diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-x509-verify-param.h b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-verify-param.h new file mode 100644 index 000000000..e85d547fb --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-x509-verify-param.h @@ -0,0 +1,81 @@ +// +// btls-x509-verify-param.h +// MonoBtls +// +// Created by Martin Baulig on 3/3/16. +// Copyright © 2016 Xamarin. All rights reserved. +// + +#ifndef __btls__btls_x509_verify_param__ +#define __btls__btls_x509_verify_param__ + +#include +#include +#include + +typedef enum { + MONO_BTLS_X509_VERIFY_FLAGS_DEFAULT = 0, + MONO_BTLS_X509_VERIFY_FLAGS_CRL_CHECK = 1, + MONO_BTLS_X509_VERIFY_FLAGS_CRL_CHECK_ALL = 2, + MONO_BTLS_X509_VERIFY_FLAGS_X509_STRICT = 4 +} MonoBtlsX509VerifyFlags; + +MonoBtlsX509VerifyParam * +mono_btls_x509_verify_param_new (void); + +MonoBtlsX509VerifyParam * +mono_btls_x509_verify_param_from_store_ctx (MonoBtlsX509StoreCtx *ctx, X509_VERIFY_PARAM *param); + +MonoBtlsX509VerifyParam * +mono_btls_x509_verify_param_copy (const MonoBtlsX509VerifyParam *from); + +void +mono_btls_x509_verify_param_free (MonoBtlsX509VerifyParam *param); + +const X509_VERIFY_PARAM * +mono_btls_x509_verify_param_peek_param (const MonoBtlsX509VerifyParam *param); + +int +mono_btls_x509_verify_param_can_modify (MonoBtlsX509VerifyParam *param); + +MonoBtlsX509VerifyParam * +mono_btls_x509_verify_param_lookup (const char *name); + +int +mono_btls_x509_verify_param_set_name (MonoBtlsX509VerifyParam *param, const char *name); + +int +mono_btls_x509_verify_param_set_host (MonoBtlsX509VerifyParam *param, const char *host, int namelen); + +int +mono_btls_x509_verify_param_add_host (MonoBtlsX509VerifyParam *param, const char *host, int namelen); + +uint64_t +mono_btls_x509_verify_param_get_flags (MonoBtlsX509VerifyParam *param); + +int +mono_btls_x509_verify_param_set_flags (MonoBtlsX509VerifyParam *param, uint64_t flags); + +MonoBtlsX509VerifyFlags +mono_btls_x509_verify_param_get_mono_flags (MonoBtlsX509VerifyParam *param); + +int +mono_btls_x509_verify_param_set_mono_flags (MonoBtlsX509VerifyParam *param, MonoBtlsX509VerifyFlags flags); + +int +mono_btls_x509_verify_param_set_purpose (MonoBtlsX509VerifyParam *param, MonoBtlsX509Purpose purpose); + +int +mono_btls_x509_verify_param_get_depth (MonoBtlsX509VerifyParam *param); + +int +mono_btls_x509_verify_param_set_depth (MonoBtlsX509VerifyParam *param, int depth); + +int +mono_btls_x509_verify_param_set_time (MonoBtlsX509VerifyParam *param, int64_t time); + +char * +mono_btls_x509_verify_param_get_peername (MonoBtlsX509VerifyParam *param); + +#endif /* defined(__btls__btls_x509_verify_param__) */ + diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-x509.c b/unity-2019.4.24f1-mbe/mono/btls/btls-x509.c new file mode 100644 index 000000000..473d94fba --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-x509.c @@ -0,0 +1,441 @@ +// +// btls-x509.c +// MonoBtls +// +// Created by Martin Baulig on 14/11/15. +// Copyright (c) 2015 Xamarin. All rights reserved. +// + +#include +#include +#include + +MONO_API X509 * +mono_btls_x509_from_data (const void *buf, int len, MonoBtlsX509Format format) +{ + BIO *bio; + X509 *cert = NULL; + + bio = BIO_new_mem_buf ((void *)buf, len); + switch (format) { + case MONO_BTLS_X509_FORMAT_DER: + cert = d2i_X509_bio (bio, NULL); + break; + case MONO_BTLS_X509_FORMAT_PEM: + cert = PEM_read_bio_X509 (bio, NULL, NULL, NULL); + break; + } + BIO_free (bio); + return cert; +} + +MONO_API X509 * +mono_btls_x509_up_ref (X509 *x509) +{ + X509_up_ref (x509); + return x509; +} + +MONO_API void +mono_btls_x509_free (X509 *x509) +{ + X509_free (x509); +} + +MONO_API X509 * +mono_btls_x509_dup (X509 *x509) +{ + return X509_dup (x509); +} + +MONO_API MonoBtlsX509Name * +mono_btls_x509_get_subject_name (X509 *x509) +{ + return mono_btls_x509_name_copy (X509_get_subject_name (x509)); +} + +MONO_API MonoBtlsX509Name * +mono_btls_x509_get_issuer_name (X509 *x509) +{ + return mono_btls_x509_name_copy (X509_get_issuer_name (x509)); +} + +MONO_API int +mono_btls_x509_get_subject_name_string (X509 *name, char *buffer, int size) +{ + *buffer = 0; + return X509_NAME_oneline (X509_get_subject_name (name), buffer, size) != NULL; +} + +MONO_API int +mono_btls_x509_get_issuer_name_string (X509 *name, char *buffer, int size) +{ + *buffer = 0; + return X509_NAME_oneline (X509_get_issuer_name (name), buffer, size) != NULL; +} + +MONO_API int +mono_btls_x509_get_raw_data (X509 *x509, BIO *bio, MonoBtlsX509Format format) +{ + switch (format) { + case MONO_BTLS_X509_FORMAT_DER: + return i2d_X509_bio (bio, x509); + case MONO_BTLS_X509_FORMAT_PEM: + return PEM_write_bio_X509 (bio, x509); + default: + return 0; + } +} + +MONO_API int +mono_btls_x509_cmp (const X509 *a, const X509 *b) +{ + return X509_cmp (a, b); +} + +MONO_API int +mono_btls_x509_get_hash (X509 *x509, const void **data) +{ + X509_check_purpose (x509, -1, 0); + *data = x509->sha1_hash; + return SHA_DIGEST_LENGTH; +} + +MONO_API int64_t +mono_btls_x509_get_not_before (X509 *x509) +{ + return mono_btls_util_asn1_time_to_ticks (X509_get_notBefore (x509)); +} + +MONO_API int64_t +mono_btls_x509_get_not_after (X509 *x509) +{ + return mono_btls_util_asn1_time_to_ticks (X509_get_notAfter (x509)); +} + +MONO_API int +mono_btls_x509_get_public_key (X509 *x509, BIO *bio) +{ + EVP_PKEY *pkey; + uint8_t *data = NULL; + int ret; + + pkey = X509_get_pubkey (x509); + if (!pkey) + return -1; + + ret = i2d_PublicKey (pkey, &data); + + if (ret > 0 && data) { + ret = BIO_write (bio, data, ret); + OPENSSL_free (data); + } + + EVP_PKEY_free (pkey); + return ret; +} + +MONO_API int +mono_btls_x509_get_serial_number (X509 *x509, char *buffer, int size, int mono_style) +{ + ASN1_INTEGER *serial; + unsigned char *temp, *p; + int len, idx; + + serial = X509_get_serialNumber (x509); + if (serial->length == 0 || serial->length+1 > size) + return 0; + + if (!mono_style) { + memcpy (buffer, serial->data, serial->length); + return serial->length; + } + + temp = OPENSSL_malloc (serial->length + 1); + if (!temp) + return 0; + + p = temp; + len = i2c_ASN1_INTEGER (serial, &p); + + if (!len) { + OPENSSL_free (temp); + return 0; + } + + for (idx = 0; idx < len; idx++) { + buffer [idx] = *(--p); + } + buffer [len] = 0; + + OPENSSL_free (temp); + return len; +} + +MONO_API int +mono_btls_x509_get_public_key_algorithm (X509 *x509, char *buffer, int size) +{ + X509_PUBKEY *pkey; + ASN1_OBJECT *ppkalg; + int ret; + + *buffer = 0; + pkey = X509_get_X509_PUBKEY (x509); + if (!pkey) + return 0; + + ret = X509_PUBKEY_get0_param (&ppkalg, NULL, NULL, NULL, pkey); + if (!ret || !ppkalg) + return ret; + + return OBJ_obj2txt (buffer, size, ppkalg, 1); +} + +MONO_API int +mono_btls_x509_get_version (X509 *x509) +{ + return (int)X509_get_version (x509) + 1; +} + +MONO_API int +mono_btls_x509_get_signature_algorithm (X509 *x509, char *buffer, int size) +{ + const ASN1_OBJECT *obj; + int nid; + + *buffer = 0; + + nid = X509_get_signature_nid (x509); + + obj = OBJ_nid2obj (nid); + if (!obj) + return 0; + + return OBJ_obj2txt (buffer, size, obj, 1); +} + +MONO_API int +mono_btls_x509_get_public_key_asn1 (X509 *x509, char *out_oid, int oid_len, uint8_t **buffer, int *size) +{ + X509_PUBKEY *pkey; + ASN1_OBJECT *ppkalg; + const unsigned char *pk; + int pk_len; + int ret; + + if (out_oid) + *out_oid = 0; + + pkey = X509_get_X509_PUBKEY (x509); + if (!pkey || !pkey->public_key) + return 0; + + ret = X509_PUBKEY_get0_param (&ppkalg, &pk, &pk_len, NULL, pkey); + if (ret != 1 || !ppkalg || !pk) + return 0; + + if (out_oid) { + OBJ_obj2txt (out_oid, oid_len, ppkalg, 1); + } + + if (buffer) { + *size = pk_len; + *buffer = OPENSSL_malloc (pk_len); + if (!*buffer) + return 0; + + memcpy (*buffer, pk, pk_len); + } + + return 1; + +} + +MONO_API int +mono_btls_x509_get_public_key_parameters (X509 *x509, char *out_oid, int oid_len, uint8_t **buffer, int *size) +{ + X509_PUBKEY *pkey; + X509_ALGOR *algor; + ASN1_OBJECT *paobj; + int ptype; + void *pval; + int ret; + + if (out_oid) + *out_oid = 0; + + pkey = X509_get_X509_PUBKEY (x509); + + ret = X509_PUBKEY_get0_param (NULL, NULL, NULL, &algor, pkey); + if (ret != 1 || !algor) + return 0; + + X509_ALGOR_get0 (&paobj, &ptype, &pval, algor); + + if (ptype != V_ASN1_NULL && ptype != V_ASN1_SEQUENCE) + return 0; + + if (ptype == V_ASN1_NULL) { + uint8_t *ptr; + + *size = 2; + *buffer = OPENSSL_malloc (2); + if (!*buffer) + return 0; + + ptr = *buffer; + *ptr++ = 0x05; + *ptr++ = 0x00; + + if (out_oid) + OBJ_obj2txt (out_oid, oid_len, paobj, 1); + + return 1; + } else if (ptype == V_ASN1_SEQUENCE) { + ASN1_STRING *pstr = pval; + + *size = pstr->length; + *buffer = OPENSSL_malloc (pstr->length); + if (!*buffer) + return 0; + + memcpy (*buffer, pstr->data, pstr->length); + + if (out_oid) + OBJ_obj2txt (out_oid, oid_len, paobj, 1); + + return 1; + } else { + return 0; + } +} + +MONO_API EVP_PKEY * +mono_btls_x509_get_pubkey (X509 *x509) +{ + return X509_get_pubkey (x509); +} + +MONO_API int +mono_btls_x509_get_subject_key_identifier (X509 *x509, uint8_t **buffer, int *size) +{ + ASN1_OCTET_STRING *skid; + + *size = 0; + *buffer = NULL; + + if (X509_get_version (x509) != 2) + return 0; + + skid = X509_get_ext_d2i (x509, NID_subject_key_identifier, NULL, NULL); + if (!skid) + return 0; + + *size = skid->length; + *buffer = OPENSSL_malloc (*size); + if (!*buffer) + return 0; + + memcpy (*buffer, skid->data, *size); + return 1; +} + +MONO_API int +mono_btls_x509_print (X509 *x509, BIO *bio) +{ + return X509_print_ex (bio, x509, XN_FLAG_COMPAT, X509_FLAG_COMPAT); +} + +static int +get_trust_nid (MonoBtlsX509Purpose purpose) +{ + switch (purpose) { + case MONO_BTLS_X509_PURPOSE_SSL_CLIENT: + return NID_client_auth; + case MONO_BTLS_X509_PURPOSE_SSL_SERVER: + return NID_server_auth; + default: + return 0; + } +} + +MONO_API int +mono_btls_x509_add_trust_object (X509 *x509, MonoBtlsX509Purpose purpose) +{ + ASN1_OBJECT *trust; + int nid; + + nid = get_trust_nid (purpose); + if (!nid) + return 0; + + trust = ASN1_OBJECT_new (); + if (!trust) + return 0; + + trust->nid = nid; + return X509_add1_trust_object (x509, trust); +} + +MONO_API int +mono_btls_x509_add_reject_object (X509 *x509, MonoBtlsX509Purpose purpose) +{ + ASN1_OBJECT *reject; + int nid; + + nid = get_trust_nid (purpose); + if (!nid) + return 0; + + reject = ASN1_OBJECT_new (); + if (!reject) + return 0; + + reject->nid = nid; + return X509_add1_reject_object (x509, reject); +} + +MONO_API int +mono_btls_x509_add_explicit_trust (X509 *x509, MonoBtlsX509TrustKind kind) +{ + int ret = 0; + + if ((kind & MONO_BTLS_X509_TRUST_KIND_REJECT_ALL) != 0) + kind |= MONO_BTLS_X509_TRUST_KIND_REJECT_CLIENT | MONO_BTLS_X509_TRUST_KIND_REJECT_SERVER; + + if ((kind & MONO_BTLS_X509_TRUST_KIND_TRUST_ALL) != 0) + kind |= MONO_BTLS_X509_TRUST_KIND_TRUST_CLIENT | MONO_BTLS_X509_TRUST_KIND_TRUST_SERVER; + + + if ((kind & MONO_BTLS_X509_TRUST_KIND_REJECT_CLIENT) != 0) { + ret = mono_btls_x509_add_reject_object (x509, MONO_BTLS_X509_PURPOSE_SSL_CLIENT); + if (!ret) + return ret; + } + + if ((kind & MONO_BTLS_X509_TRUST_KIND_REJECT_SERVER) != 0) { + ret = mono_btls_x509_add_reject_object (x509, MONO_BTLS_X509_PURPOSE_SSL_SERVER); + if (!ret) + return ret; + } + + if (ret) { + // Ignore any MONO_BTLS_X509_TRUST_KIND_TRUST_* settings if we added + // any kind of MONO_BTLS_X509_TRUST_KIND_REJECT_* before. + return ret; + } + + if ((kind & MONO_BTLS_X509_TRUST_KIND_TRUST_CLIENT) != 0) { + ret = mono_btls_x509_add_trust_object (x509, MONO_BTLS_X509_PURPOSE_SSL_CLIENT); + if (!ret) + return ret; + } + + if ((kind & MONO_BTLS_X509_TRUST_KIND_TRUST_SERVER) != 0) { + ret = mono_btls_x509_add_trust_object (x509, MONO_BTLS_X509_PURPOSE_SSL_SERVER); + if (!ret) + return ret; + } + + return ret; +} diff --git a/unity-2019.4.24f1-mbe/mono/btls/btls-x509.h b/unity-2019.4.24f1-mbe/mono/btls/btls-x509.h new file mode 100644 index 000000000..d91374313 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/btls/btls-x509.h @@ -0,0 +1,127 @@ +// +// btls-x509.h +// MonoBtls +// +// Created by Martin Baulig on 14/11/15. +// Copyright (c) 2015 Xamarin. All rights reserved. +// + +#ifndef __btls__btls_x509__ +#define __btls__btls_x509__ + +#include +#include +#include + +typedef enum { + MONO_BTLS_X509_FORMAT_DER = 1, + MONO_BTLS_X509_FORMAT_PEM = 2 +} MonoBtlsX509Format; + +typedef enum { + MONO_BTLS_x509_FILE_TYPE_PEM = 1, // X509_FILETYPE_PEM + MONO_BTLS_x509_FILE_TYPE_ASN1 = 2, // X509_FILETYPE_ASN1 + MONO_BTLS_x509_FILE_TYPE_DEFAULT = 3, // X509_FILETYPE_DEFAULT +} MonoBtlsX509FileType; + +typedef enum { + MONO_BTLS_X509_PURPOSE_SSL_CLIENT = 1, + MONO_BTLS_X509_PURPOSE_SSL_SERVER = 2, + MONO_BTLS_X509_PURPOSE_NS_SSL_SERVER = 3, + MONO_BTLS_X509_PURPOSE_SMIME_SIGN = 4, + MONO_BTLS_X509_PURPOSE_SMIME_ENCRYPT = 5, + MONO_BTLS_X509_PURPOSE_CRL_SIGN = 6, + MONO_BTLS_X509_PURPOSE_ANY = 7, + MONO_BTLS_X509_PURPOSE_OCSP_HELPER = 8, + MONO_BTLS_X509_PURPOSE_TIMESTAMP_SIGN = 9, +} MonoBtlsX509Purpose; + +typedef enum { + MONO_BTLS_X509_TRUST_KIND_DEFAULT = 0, + MONO_BTLS_X509_TRUST_KIND_TRUST_CLIENT = 1, + MONO_BTLS_X509_TRUST_KIND_TRUST_SERVER = 2, + MONO_BTLS_X509_TRUST_KIND_TRUST_ALL = 4, + MONO_BTLS_X509_TRUST_KIND_REJECT_CLIENT = 32, + MONO_BTLS_X509_TRUST_KIND_REJECT_SERVER = 64, + MONO_BTLS_X509_TRUST_KIND_REJECT_ALL = 128 +} MonoBtlsX509TrustKind; + +X509 * +mono_btls_x509_from_data (const void *buf, int len, MonoBtlsX509Format format); + +X509 * +mono_btls_x509_up_ref (X509 *x509); + +void +mono_btls_x509_free (X509 *x509); + +X509 * +mono_btls_x509_dup (X509 *x509); + +MonoBtlsX509Name * +mono_btls_x509_get_subject_name (X509 *x509); + +MonoBtlsX509Name * +mono_btls_x509_get_issuer_name (X509 *x509); + +int +mono_btls_x509_get_subject_name_string (X509 *name, char *buffer, int size); + +int +mono_btls_x509_get_issuer_name_string (X509 *name, char *buffer, int size); + +int +mono_btls_x509_get_raw_data (X509 *x509, BIO *bio, MonoBtlsX509Format format); + +int +mono_btls_x509_cmp (const X509 *a, const X509 *b); + +int +mono_btls_x509_get_hash (X509 *x509, const void **data); + +int64_t +mono_btls_x509_get_not_before (X509 *x509); + +int64_t +mono_btls_x509_get_not_after (X509 *x509); + +int +mono_btls_x509_get_public_key (X509 *x509, BIO *bio); + +int +mono_btls_x509_get_public_key_parameters (X509 *x509, char *out_oid, int oid_len, uint8_t **buffer, int *size); + +int +mono_btls_x509_get_serial_number (X509 *x509, char *buffer, int size, int mono_style); + +int +mono_btls_x509_get_public_key_algorithm (X509 *x509, char *buffer, int size); + +int +mono_btls_x509_get_version (X509 *x509); + +int +mono_btls_x509_get_signature_algorithm (X509 *x509, char *buffer, int size); + +int +mono_btls_x509_get_public_key_asn1 (X509 *x509, char *out_oid, int oid_len, uint8_t **buffer, int *size); + +EVP_PKEY * +mono_btls_x509_get_pubkey (X509 *x509); + +int +mono_btls_x509_get_subject_key_identifier (X509 *x509, uint8_t **buffer, int *size); + +int +mono_btls_x509_print (X509 *x509, BIO *bio); + +int +mono_btls_x509_add_trust_object (X509 *x509, MonoBtlsX509Purpose purpose); + +int +mono_btls_x509_add_reject_object (X509 *x509, MonoBtlsX509Purpose purpose); + +int +mono_btls_x509_add_explicit_trust (X509 *x509, MonoBtlsX509TrustKind kind); + +#endif /* defined(__btls__btls_x509__) */ diff --git a/unity-2019.4.24f1-mbe/mono/cil/.gitattributes b/unity-2019.4.24f1-mbe/mono/cil/.gitattributes new file mode 100644 index 000000000..a5a53632a --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/cil/.gitattributes @@ -0,0 +1 @@ +/make-opcode-def.xsl -crlf diff --git a/unity-2019.4.24f1-mbe/mono/cil/.gitignore b/unity-2019.4.24f1-mbe/mono/cil/.gitignore new file mode 100644 index 000000000..b336cc7ce --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/cil/.gitignore @@ -0,0 +1,2 @@ +/Makefile +/Makefile.in diff --git a/unity-2019.4.24f1-mbe/mono/cil/Makefile.am b/unity-2019.4.24f1-mbe/mono/cil/Makefile.am new file mode 100644 index 000000000..8e2ac9448 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/cil/Makefile.am @@ -0,0 +1,12 @@ +opcode.def: make-opcodes-def.pl cil-opcodes.xml + perl $(srcdir)/make-opcodes-def.pl $(srcdir)/cil-opcodes.xml $@.tmp + mv $@.tmp $@ + +defdir = $(includedir)/mono-$(API_VER)/mono/cil +def_DATA = opcode.def + +xmldir = $(datadir)/mono-$(API_VER)/mono/cil +xml_DATA = cil-opcodes.xml + +EXTRA_DIST=tables.def opcode.def make-opcodes-def.pl make-opcode-def.xsl $(xml_DATA) $(def_DATA) + diff --git a/unity-2019.4.24f1-mbe/mono/cil/TODO b/unity-2019.4.24f1-mbe/mono/cil/TODO new file mode 100644 index 000000000..6175693d3 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/cil/TODO @@ -0,0 +1,3 @@ +* Replace make-opcodes-def.pl with XLST process + + We should use an XSLT file to do the conversion there. diff --git a/unity-2019.4.24f1-mbe/mono/cil/cil-opcodes.xml b/unity-2019.4.24f1-mbe/mono/cil/cil-opcodes.xml new file mode 100644 index 000000000..886cf63af --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/cil/cil-opcodes.xml @@ -0,0 +1,325 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unity-2019.4.24f1-mbe/mono/cil/make-opcode-def.xsl b/unity-2019.4.24f1-mbe/mono/cil/make-opcode-def.xsl new file mode 100644 index 000000000..3b8efc68f --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/cil/make-opcode-def.xsl @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OPDEF(CEE_) + + + + +#ifndef OPALIAS +#define _MONO_CIL_OPALIAS_DEFINED_ +#define OPALIAS(a,s,r) +#endif + +OPALIAS(CEE_BRNULL, "brnull", CEE_BRFALSE) +OPALIAS(CEE_BRNULL_S, "brnull.s", CEE_BRFALSE_S) +OPALIAS(CEE_BRZERO, "brzero", CEE_BRFALSE) +OPALIAS(CEE_BRZERO_S, "brzero.s", CEE_BRFALSE_S) +OPALIAS(CEE_BRINST, "brinst", CEE_BRTRUE) +OPALIAS(CEE_BRINST_S, "brinst.s", CEE_BRTRUE_S) +OPALIAS(CEE_LDIND_U8, "ldind.u8", CEE_LDIND_I8) +OPALIAS(CEE_LDELEM_U8, "ldelem.u8", CEE_LDELEM_I8) +OPALIAS(CEE_LDX_I4_MIX, "ldc.i4.M1", CEE_LDC_I4_M1) +OPALIAS(CEE_ENDFAULT, "endfault", CEE_ENDFINALLY) + +#ifdef _MONO_CIL_OPALIAS_DEFINED_ +#undef OPALIAS +#undef _MONO_CIL_OPALIAS_DEFINED_ +#endif + + + + diff --git a/unity-2019.4.24f1-mbe/mono/cil/make-opcodes-def.pl b/unity-2019.4.24f1-mbe/mono/cil/make-opcodes-def.pl new file mode 100644 index 000000000..4a4c8cf55 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/cil/make-opcodes-def.pl @@ -0,0 +1,87 @@ +#!/usr/bin/perl +# +# make-opcodes-def.pl: Loads the opcodes from the CIL-opcodes.xml and +# generates a spec compliant opcodes.def file +# +# Author: +# Miguel de Icaza (miguel@ximian.com) +# +# (C) 2001 Ximian, Inc. +# +# We should really be doing this with XSLT, but I know nothing about XSLT +# ;-) +# or maybe just an XML::Parser... - lupus + +use strict; +use XML::Parser; + +my %valid_flow; +# the XML file also includes "throw" +@valid_flow{qw(next call return branch meta cond-branch)} = (); + +open OUTPUT, ">$ARGV[1]" || die "Can not create $ARGV[1] file: $!"; + +my $parser = new XML::Parser (Handlers => {Start => \&handle_opcode}); +print_header(); +$parser->parsefile($ARGV[0]); +print_trailer(); +close(OUTPUT) || die "Can not close file: $!"; + +sub handle_opcode { + my ($parser, $elem, %attrs) = @_; + my ($name, $input, $output, $args, $o1, $o2, $flow, $uname, $count, $ff); + + return if ($elem ne 'opcode'); + + ($name, $input, $output, $args, $o1, $o2, $flow) = + @attrs{qw(name input output args o1 o2 flow)}; + + $uname = uc $name; + $uname =~ tr/./_/; + if (hex($o1) == 0xff){ + $count = 1; + } else { + $count = 2; + } + + $ff = "ERROR"; + if (exists $valid_flow{$flow}) { + $ff = uc $flow; + $ff =~ tr/-/_/; + } + + print OUTPUT "OPDEF(CEE_$uname, \"$name\", $input, $output, $args, X, $count, $o1, $o2, $ff)\n"; + +} + +sub print_header { +print OUTPUT< + * + * Copyright (C) 2005 Novell, Inc (http://www.novell.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#include + +#include "mono/metadata/blob.h" +#include "mono/metadata/metadata.h" +#include "mono/metadata/mono-endian.h" +#include "mono/utils/mono-compiler.h" + +#include "declsec.h" +#include "util.h" + +static char* +declsec_20_get_classname (const char* p, const char **rptr) +{ + int apos, cpos = 0; + char *c; + char *a; + char *result; + GString *res = g_string_new (""); + int len = mono_metadata_decode_value (p, &p); + + c = (char *) p; + while ((*c++ != ',') && (cpos++ < len)); + c++; + + apos = cpos; + a = c; + while ((*a++ != ',') && (apos++ < len)); + + if (apos - cpos > 1) { + g_string_append_printf (res, "[%.*s]%.*s", apos - cpos, c, cpos, p); + } else { + /* in-assembly type aren't fully qualified (no comma) */ + g_string_append_printf (res, "%.*s", cpos - 1, p); + } + + p += len; + if (rptr) + *rptr = p; + + result = res->str; + g_string_free (res, FALSE); + return result; +} + +static gboolean +declsec_20_write_type (GString *str, char type) +{ + switch (type) { + case MONO_TYPE_BOOLEAN: + g_string_append (str, "bool"); + break; + case MONO_TYPE_SZARRAY: + g_string_append (str, "[]"); + break; + case MONO_TYPE_SYSTEM_TYPE: + g_string_append (str, "type"); + break; + case MONO_TYPE_STRING: + g_string_append (str, "string"); + break; + default: + g_warning ("TODO type %d - please fill a bug report on this!", type); + return FALSE; + } + return TRUE; +} + +static const char* +declsec_20_write_value (GString *str, char type, const char *value) +{ + switch (type) { + case MONO_TYPE_U1: + g_string_append_printf (str, "%d", (unsigned char)*value); + return value + 1; + case MONO_TYPE_I1: + g_string_append_printf (str, "%d", *value); + return value + 1; + case MONO_TYPE_BOOLEAN: + g_string_append_printf (str, "%s", *value ? "true" : "false"); + return value + 1; + case MONO_TYPE_CHAR: + g_string_append_printf (str, "0x%04X", read16 (value)); + return value + 2; + case MONO_TYPE_U2: + g_string_append_printf (str, "%d", read16 (value)); + return value + 2; + case MONO_TYPE_I2: + g_string_append_printf (str, "%d", (gint16)read16 (value)); + return value + 2; + case MONO_TYPE_U4: + g_string_append_printf (str, "%d", read32 (value)); + return value + 4; + case MONO_TYPE_I4: + g_string_append_printf (str, "%d", (gint32)read32 (value)); + return value + 4; + case MONO_TYPE_U8: + g_string_append_printf (str, "%lld", (long long)read64 (value)); + return value + 8; + case MONO_TYPE_I8: + g_string_append_printf (str, "%lld", (long long)read64 (value)); + return value + 8; + case MONO_TYPE_R4: { + float val; + int inf; + readr4 (value, &val); + inf = dis_isinf (val); + if (inf == -1) + g_string_append_printf (str, "0xFF800000"); /* negative infinity */ + else if (inf == 1) + g_string_append_printf (str, "0x7F800000"); /* positive infinity */ + else if (dis_isnan (val)) + g_string_append_printf (str, "0xFFC00000"); /* NaN */ + else + g_string_append_printf (str, "%.8g", val); + return value + 4; + } + case MONO_TYPE_R8: { + double val; + int inf; + readr8 (value, &val); + inf = dis_isinf (val); + if (inf == -1) + g_string_append_printf (str, "0xFFF00000000000000"); /* negative infinity */ + else if (inf == 1) + g_string_append_printf (str, "0x7FFF0000000000000"); /* positive infinity */ + else if (isnan (val)) + g_string_append_printf (str, "0xFFF80000000000000"); /* NaN */ + else + g_string_append_printf (str, "%.17g", val); + return value + 8; + } + case MONO_TYPE_STRING: + if (*value == (char)0xff) { + g_string_append (str, "nullref"); + return value + 1; + } else { + int len = mono_metadata_decode_value (value, &value); + g_string_append_printf (str, "'%.*s'", len, value); + return value + len; + } + case MONO_TYPE_SYSTEM_TYPE: { + char *cname = declsec_20_get_classname (value, NULL); + int len = mono_metadata_decode_value (value, &value); + g_string_append (str, cname); + g_free (cname); + return value + len; + } + } + return 0; +} + +char* +dump_declsec_entry20 (MonoImage *m, const char* p, const char *indent) +{ + int i, num; + char *result; + GString *res = g_string_new (""); + + if (*p++ != MONO_DECLSEC_FORMAT_20) + return NULL; + + g_string_append (res, "{"); + + /* number of encoded permission attributes */ + num = mono_metadata_decode_value (p, &p); + + for (i = 0; i < num; i++) { + int len, j, pos = 0, param_len; + char *param_start; + char *s = declsec_20_get_classname (p, &p); + g_string_append_printf (res, "%s = {", s); + g_free (s); + + /* optional parameters length */ + param_len = mono_metadata_decode_value (p, &p); + param_start = (char *) p; + + /* number of parameters */ + pos = mono_metadata_decode_value (p, &p); + for (j = 0; j < pos; j++) { + int k, elem; + int type = *p++; + + switch (type) { + case MONO_DECLSEC_FIELD: + /* not sure if/how we can get this in a declarative security attribute... */ + g_string_append (res, "field "); + break; + case MONO_DECLSEC_PROPERTY: + g_string_append (res, "property "); + break; + default: + g_warning ("TODO %d - please fill a bug report on this!", type); + break; + } + + type = *p++; + + if (type == MONO_DECLSEC_ENUM) { + s = declsec_20_get_classname (p, &p); + len = mono_metadata_decode_value (p, &p); + g_string_append_printf (res, "enum %s '%.*s' = ", s, len, p); + g_free (s); + p += len; + /* TODO: we must detect the size of the enum element (from the type ? length ?) + * note: ildasm v2 has some problem decoding them too and doesn't + * seems to rely on the type (as the other assembly isn't loaded) */ + g_string_append_printf (res, "int32(%d)", read32 (p)); + p += 4; + } else { + int arraytype = 0; + if (type == MONO_TYPE_SZARRAY) { + arraytype = *p++; + declsec_20_write_type (res, arraytype); + } + declsec_20_write_type (res, type); + + len = mono_metadata_decode_value (p, &p); + g_string_append_printf (res, " '%.*s' = ", len, p); + p += len; + + if (type == MONO_TYPE_SZARRAY) { + type = arraytype; + declsec_20_write_type (res, type); + elem = read32 (p); + p += 4; + g_string_append_printf (res, "[%d]", elem); + } else { + declsec_20_write_type (res, type); + elem = 1; + } + g_string_append (res, "("); + + /* write value - or each element in the array */ + for (k = 0; k < elem; k++) { + p = declsec_20_write_value (res, type, p); + /* separate array elements */ + if (k < elem - 1) + g_string_append (res, " "); + } + + if (j < pos - 1) + g_string_append_printf (res, ")\n%s", indent); + else + g_string_append (res, ")"); + } + + } + + if (i < num - 1) + g_string_append_printf (res, "},\n%s", indent); + else + g_string_append (res, "}"); + + if (param_len > 0) + p = param_start + param_len; + } + g_string_append (res, "}"); + + result = res->str; + g_string_free (res, FALSE); + return result; +} diff --git a/unity-2019.4.24f1-mbe/mono/dis/declsec.h b/unity-2019.4.24f1-mbe/mono/dis/declsec.h new file mode 100644 index 000000000..a0e621f7c --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/dis/declsec.h @@ -0,0 +1,25 @@ +/* + * declsec.h: Support for the new declarative security attribute + * metadata format (2.0) + * + * Author: + * Sebastien Pouliot + * + * Copyright (C) 2005 Novell, Inc (http://www.novell.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#ifndef __MONODIS_DECLSEC_H__ +#define __MONODIS_DECLSEC_H__ + +#define MONO_DECLSEC_FORMAT_20 0x2E + +#define MONO_DECLSEC_FIELD 0x53 +#define MONO_DECLSEC_PROPERTY 0x54 +#define MONO_DECLSEC_ENUM 0x55 + +#define MONO_TYPE_SYSTEM_TYPE 0x50 + +char* dump_declsec_entry20 (MonoImage *m, const char* p, const char *indent); + +#endif /* __MONODIS_DECLSEC_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/dis/dis-cil.c b/unity-2019.4.24f1-mbe/mono/dis/dis-cil.c new file mode 100644 index 000000000..bc40414ba --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/dis/dis-cil.c @@ -0,0 +1,322 @@ +/* + * dis-cil.c: Disassembles CIL byte codes + * + * Author: + * Miguel de Icaza (miguel@ximian.com) + * + * (C) 2001 Ximian, Inc. + */ +#include +#include +#include +#include +#ifdef HAVE_WCHAR_H +#include +#endif +#include "meta.h" +#include "get.h" +#include "dump.h" +#include "dis-cil.h" +#include "util.h" +#include "mono/metadata/opcodes.h" +#include "mono/metadata/class-internals.h" +#include "mono/utils/mono-compiler.h" + +#define CODE_INDENT g_assert (indent_level < 512); \ + indent[indent_level*2] = ' '; \ + indent[indent_level*2+1] = ' '; \ + ++indent_level; \ + indent[indent_level*2] = 0; +#define CODE_UNINDENT g_assert (indent_level); \ + --indent_level; \ + indent[indent_level*2] = 0; + +void +disassemble_cil (MonoImage *m, MonoMethodHeader *mh, MonoGenericContainer *container) +{ + const unsigned char *start = mh->code; + int size = mh->code_size; + const unsigned char *end = start + size; + const unsigned char *ptr = start; + const MonoOpcode *entry; + char indent[1024]; + int i, j, indent_level = 0; + gboolean in_fault = 0; + const char *clause_names[] = {"catch", "filter", "finally", "", "fault"}; + gboolean *trys = NULL; + indent [0] = 0; + +#ifdef DEBUG + for (i = 0; i < mh->num_clauses; ++i) { +#define clause mh->clauses [i] + g_print ("/* out clause %d: from %d len=%d, handler at %d, %d */\n", + clause.flags, clause.try_offset, clause.try_len, clause.handler_offset, clause.handler_len); +#undef clause + } +#endif + + if (mh->num_clauses) { + trys = (gboolean *)g_malloc0 (sizeof (gboolean) * mh->num_clauses); + trys [0] = 1; + for (i=1; i < mh->num_clauses; ++i) { +#define jcl mh->clauses [j] +#define cl mh->clauses [i] + trys [i] = 1; + for (j = 0; j < i; j++) { + if (cl.try_offset == jcl.try_offset && cl.try_len == jcl.try_len) { + trys [i] = 0; + break; + } + } +#undef jcl +#undef cl + } + } + + while (ptr < end){ + for (i = mh->num_clauses - 1; i >= 0 ; --i) { + if (ptr == start + mh->clauses[i].try_offset && trys [i]) { + fprintf (output, "\t%s.try { // %d\n", indent, i); + CODE_INDENT; + } + + if (ptr == start + mh->clauses[i].handler_offset) { + if (mh->clauses[i].flags == MONO_EXCEPTION_CLAUSE_FILTER) { + CODE_UNINDENT; + fprintf (output, "\t%s} { // %d\n", indent, i); + } else { + char * klass = mh->clauses[i].flags ? g_strdup ("") : + dis_stringify_object_with_class (m, mh->clauses[i].data.catch_class, + TRUE, FALSE); + fprintf (output, "\t%s%s %s { // %d\n", indent, + clause_names [mh->clauses[i].flags], klass, i); + g_free (klass); + } + CODE_INDENT; + if (mh->clauses[i].flags == MONO_EXCEPTION_CLAUSE_FAULT) + in_fault = 1; + } + if (mh->clauses[i].flags == MONO_EXCEPTION_CLAUSE_FILTER && ptr == start + mh->clauses[i].data.filter_offset) { + fprintf (output, "\t%s%s {\n", indent, clause_names[1]); + CODE_INDENT; + } + } + fprintf (output, "\t%sIL_%04x: ", indent, (int) (ptr - start)); + i = *ptr; + if (*ptr == 0xfe){ + ptr++; + i = *ptr + 256; + } + entry = &mono_opcodes [i]; + + if (in_fault && entry->opval == 0xDC) + fprintf (output, " %s", "endfault"); + else + fprintf (output, " %s ", mono_opcode_name (i)); + ptr++; + switch (entry->argument){ + case MonoInlineBrTarget: { + gint target = read32 (ptr); + fprintf (output, "IL_%04x\n", ((int) (ptr - start)) + 4 + target); + ptr += 4; + break; + } + + case MonoInlineField: { + guint32 token = read32 (ptr); + char *s; + + s = get_field (m, token, container); + fprintf (output, "%s", s); + g_free (s); + ptr += 4; + break; + } + + case MonoInlineI: { + int value = read32 (ptr); + + fprintf (output, "%d", value); + ptr += 4; + break; + } + + case MonoInlineI8: { + gint64 top = read64 (ptr); + + fprintf (output, "0x%llx", (long long) top); + ptr += 8; + break; + } + + case MonoInlineMethod: { + guint32 token = read32 (ptr); + char *s; + + s = get_method (m, token, container); + fprintf (output, "%s", s); + g_free (s); + ptr += 4; + break; + } + + case MonoInlineNone: + break; + + case MonoInlineR: { + double r; + int inf; + readr8 (ptr, &r); + inf = dis_isinf (r); + if (inf == -1) + fprintf (output, "(00 00 00 00 00 00 f0 ff)"); /* negative infinity */ + else if (inf == 1) + fprintf (output, "(00 00 00 00 00 00 f0 7f)"); /* positive infinity */ + else if (dis_isnan (r)) + fprintf (output, "(00 00 00 00 00 00 f8 ff)"); /* NaN */ + else { + char *str = stringify_double (r); + fprintf (output, "%s", str); + g_free (str); + } + ptr += 8; + break; + } + + case MonoInlineSig: { + guint32 token = read32 (ptr); + fprintf (output, "signature-0x%08x", token); + ptr += 4; + break; + } + + case MonoInlineString: { + guint32 token = read32 (ptr); + const char *us_ptr = mono_metadata_user_string (m, token & 0xffffff); + int len = mono_metadata_decode_blob_size (us_ptr, (const char**)&us_ptr); + + char *s = get_encoded_user_string_or_bytearray ((const guchar*)us_ptr, len); + + /* + * See section 23.1.4 on the encoding of the #US heap + */ + fprintf (output, "%s", s); + g_free (s); + ptr += 4; + break; + } + + case MonoInlineSwitch: { + guint32 count = read32 (ptr); + const unsigned char *endswitch; + guint32 n; + + ptr += 4; + endswitch = ptr + sizeof (guint32) * count; + fprintf (output, count > 0 ? "(\n" : "( )"); + CODE_INDENT; + for (n = 0; n < count; n++){ + fprintf (output, "\t%sIL_%04x%s", indent, + (int)(endswitch-start+read32 (ptr)), + n == count - 1 ? ")" : ",\n"); + ptr += 4; + } + CODE_UNINDENT; + break; + } + + case MonoInlineTok: { + guint32 token = read32 (ptr); + char *s; + + s = get_token (m, token, container); + fprintf (output, "%s", s); + g_free (s); + + ptr += 4; + break; + } + + case MonoInlineType: { + guint32 token = read32 (ptr); + char *s = get_token_type (m, token, container); + fprintf (output, "%s", s); + g_free (s); + ptr += 4; + break; + } + + case MonoInlineVar: { + guint16 var_idx = read16 (ptr); + + fprintf (output, "%d\n", var_idx); + ptr += 2; + break; + } + + case MonoShortInlineBrTarget: { + signed char x = *ptr; + + fprintf (output, "IL_%04x\n", (int)(ptr - start + 1 + x)); + ptr++; + break; + } + + case MonoShortInlineI: { + char x = *ptr; + + fprintf (output, "0x%02x", x); + ptr++; + break; + } + + case MonoShortInlineR: { + float f; + int inf; + + readr4 (ptr, &f); + + inf = dis_isinf (f); + if (inf == -1) + fprintf (output, "(00 00 80 ff)"); /* negative infinity */ + else if (inf == 1) + fprintf (output, "(00 00 80 7f)"); /* positive infinity */ + else if (dis_isnan (f)) + fprintf (output, "(00 00 c0 ff)"); /* NaN */ + else { + char *str = stringify_double ((double) f); + fprintf (output, "%s", str); + g_free (str); + } + ptr += 4; + break; + } + + case MonoShortInlineVar: { + unsigned char x = *ptr; + + fprintf (output, "%d", (int) x); + ptr++; + break; + } + default: + break; + } + + fprintf (output, "\n"); + for (i = 0; i < mh->num_clauses; ++i) { + if (ptr == start + mh->clauses[i].try_offset + mh->clauses[i].try_len && trys [i]) { + CODE_UNINDENT; + fprintf (output, "\t%s} // end .try %d\n", indent, i); + } + if (ptr == start + mh->clauses[i].handler_offset + mh->clauses[i].handler_len) { + CODE_UNINDENT; + fprintf (output, "\t%s} // end handler %d\n", indent, i); + if (mh->clauses[i].flags == MONO_EXCEPTION_CLAUSE_FAULT) + in_fault = 0; + } + } + } + if (trys) + g_free (trys); +} diff --git a/unity-2019.4.24f1-mbe/mono/dis/dis-cil.h b/unity-2019.4.24f1-mbe/mono/dis/dis-cil.h new file mode 100644 index 000000000..d43d63aa9 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/dis/dis-cil.h @@ -0,0 +1 @@ +void disassemble_cil (MonoImage *m, MonoMethodHeader *mh, MonoGenericContainer *container); diff --git a/unity-2019.4.24f1-mbe/mono/dis/dump.c b/unity-2019.4.24f1-mbe/mono/dis/dump.c new file mode 100644 index 000000000..c13675e82 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/dis/dump.c @@ -0,0 +1,1344 @@ +/* + * dump.c: Dumping routines for the disassembler. + * + * Author: + * Miguel de Icaza (miguel@ximian.com) + * + * (C) 2001 Ximian, Inc. + */ +#include +#include +#include +#include +#include +#include "meta.h" +#include "util.h" +#include "dump.h" +#include "get.h" +#include "declsec.h" +#include "mono/metadata/loader.h" +#include "mono/metadata/class.h" +#include "mono/metadata/class-internals.h" +#include "mono/utils/mono-compiler.h" + +void +dump_table_assembly (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_ASSEMBLY]; + guint32 cols [MONO_ASSEMBLY_SIZE]; + const char *ptr; + int len; + + fprintf (output, "Assembly Table\n"); + + if (!t->rows) + return; + + mono_metadata_decode_row (t, 0, cols, MONO_ASSEMBLY_SIZE); + + fprintf (output, "Name: %s\n", mono_metadata_string_heap (m, cols [MONO_ASSEMBLY_NAME])); + fprintf (output, "Hash Algoritm: 0x%08x\n", cols [MONO_ASSEMBLY_HASH_ALG]); + fprintf (output, "Version: %d.%d.%d.%d\n", cols [MONO_ASSEMBLY_MAJOR_VERSION], + cols [MONO_ASSEMBLY_MINOR_VERSION], + cols [MONO_ASSEMBLY_BUILD_NUMBER], + cols [MONO_ASSEMBLY_REV_NUMBER]); + fprintf (output, "Flags: 0x%08x\n", cols [MONO_ASSEMBLY_FLAGS]); + fprintf (output, "PublicKey: BlobPtr (0x%08x)\n", cols [MONO_ASSEMBLY_PUBLIC_KEY]); + + ptr = mono_metadata_blob_heap (m, cols [MONO_ASSEMBLY_PUBLIC_KEY]); + len = mono_metadata_decode_value (ptr, &ptr); + if (len > 0){ + fprintf (output, "\tDump:"); + hex_dump (ptr, 0, len); + fprintf (output, "\n"); + } else + fprintf (output, "\tZero sized public key\n"); + + fprintf (output, "Culture: %s\n", mono_metadata_string_heap (m, cols [MONO_ASSEMBLY_CULTURE])); + fprintf (output, "\n"); +} + +void +dump_table_typeref (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_TYPEREF]; + int i; + + fprintf (output, "Typeref Table\n"); + + for (i = 1; i <= t->rows; i++){ + char *s = get_typeref (m, i); + + fprintf (output, "%d: %s\n", i, s); + g_free (s); + } + fprintf (output, "\n"); +} + +void +dump_table_typedef (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_TYPEDEF]; + int i; + + fprintf (output, "Typedef Table\n"); + + for (i = 1; i <= t->rows; i++){ + char *s = get_typedef (m, i); + guint32 cols [MONO_TYPEDEF_SIZE]; + + mono_metadata_decode_row (&m->tables [MONO_TABLE_TYPEDEF], i - 1, cols, MONO_TYPEDEF_SIZE); + + fprintf (output, "%d: %s (flist=%d, mlist=%d, flags=0x%x, extends=0x%x)\n", i, s, + cols [MONO_TYPEDEF_FIELD_LIST], cols [MONO_TYPEDEF_METHOD_LIST], + cols [MONO_TYPEDEF_FLAGS], cols [MONO_TYPEDEF_EXTENDS]); + g_free (s); + } + fprintf (output, "\n"); +} + +void +dump_table_typespec (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_TYPESPEC]; + int i; + + fprintf (output, "Typespec Table\n"); + + for (i = 1; i <= t->rows; i++){ + char *typespec = get_typespec (m, i, TRUE, NULL); + + fprintf (output, "%d: %s\n", i, typespec); + g_free (typespec); + } + fprintf (output, "\n"); +} + +void +dump_table_assemblyref (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_ASSEMBLYREF]; + int i; + + fprintf (output, "AssemblyRef Table\n"); + + for (i = 0; i < t->rows; i++){ + const char *ptr; + int len; + guint32 cols [MONO_ASSEMBLYREF_SIZE]; + + mono_metadata_decode_row (t, i, cols, MONO_ASSEMBLYREF_SIZE); + fprintf (output, "%d: Version=%d.%d.%d.%d\n\tName=%s\n", i + 1, + cols [MONO_ASSEMBLYREF_MAJOR_VERSION], + cols [MONO_ASSEMBLYREF_MINOR_VERSION], + cols [MONO_ASSEMBLYREF_BUILD_NUMBER], + cols [MONO_ASSEMBLYREF_REV_NUMBER], + mono_metadata_string_heap (m, cols [MONO_ASSEMBLYREF_NAME])); + fprintf (output, "\tFlags=0x%08x\n", cols [MONO_ASSEMBLYREF_FLAGS]); + ptr = mono_metadata_blob_heap (m, cols [MONO_ASSEMBLYREF_PUBLIC_KEY]); + len = mono_metadata_decode_value (ptr, &ptr); + if (len > 0){ + fprintf (output, "\tPublic Key:"); + hex_dump (ptr, 0, len); + fprintf (output, "\n"); + } else + fprintf (output, "\tZero sized public key\n"); + + } + fprintf (output, "\n"); +} + +void +dump_table_param (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_PARAM]; + int i; + + fprintf (output, "Param Table\n"); + + for (i = 0; i < t->rows; i++){ + guint32 cols [MONO_PARAM_SIZE]; + + mono_metadata_decode_row (t, i, cols, MONO_PARAM_SIZE); + fprintf (output, "%d: 0x%04x %d %s\n", + i + 1, + cols [MONO_PARAM_FLAGS], cols [MONO_PARAM_SEQUENCE], + mono_metadata_string_heap (m, cols [MONO_PARAM_NAME])); + } + fprintf (output, "\n"); +} + +void +dump_table_field (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_FIELD]; + MonoTableInfo *td = &m->tables [MONO_TABLE_TYPEDEF]; + MonoTableInfo *fl = &m->tables [MONO_TABLE_FIELDLAYOUT]; + MonoTableInfo *rva = &m->tables [MONO_TABLE_FIELDRVA]; + int i, current_type, offset_row, rva_row; + guint32 first_m, last_m; + + fprintf (output, "Field Table (1..%d)\n", t->rows); + + rva_row = offset_row = current_type = 1; + last_m = first_m = 1; + for (i = 1; i <= t->rows; i++){ + guint32 cols [MONO_FIELD_SIZE]; + char *sig, *flags; + + /* + * Find the next type. + */ + while (current_type <= td->rows && i >= (last_m = mono_metadata_decode_row_col (td, current_type - 1, MONO_TYPEDEF_FIELD_LIST))) { + current_type++; + } + if (i == first_m) { + fprintf (output, "########## %s.%s\n", + mono_metadata_string_heap (m, mono_metadata_decode_row_col (td, current_type - 2, MONO_TYPEDEF_NAMESPACE)), + mono_metadata_string_heap (m, mono_metadata_decode_row_col (td, current_type - 2, MONO_TYPEDEF_NAME))); + first_m = last_m; + } + mono_metadata_decode_row (t, i - 1, cols, MONO_FIELD_SIZE); + sig = get_field_signature (m, cols [MONO_FIELD_SIGNATURE], NULL); + flags = field_flags (cols [MONO_FIELD_FLAGS]); + fprintf (output, "%d: %s %s: %s\n", + i, + sig, + mono_metadata_string_heap (m, cols [MONO_FIELD_NAME]), + flags); + g_free (sig); + g_free (flags); + if (offset_row <= fl->rows && (mono_metadata_decode_row_col (fl, offset_row - 1, MONO_FIELD_LAYOUT_FIELD) == i)) { + fprintf (output, "\texplicit offset: %d\n", mono_metadata_decode_row_col (fl, offset_row - 1, MONO_FIELD_LAYOUT_OFFSET)); + offset_row ++; + } + if (rva_row <= rva->rows && (mono_metadata_decode_row_col (rva, rva_row - 1, MONO_FIELD_RVA_FIELD) == i)) { + fprintf (output, "\trva: %d\n", mono_metadata_decode_row_col (rva, rva_row - 1, MONO_FIELD_RVA_RVA)); + rva_row ++; + } + } + fprintf (output, "\n"); +} + +void +dump_table_memberref (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_MEMBERREF]; + int i, kind, idx; + char *x, *xx; + char *sig; + const char *blob, *ks = NULL; + + fprintf (output, "MemberRef Table (1..%d)\n", t->rows); + + for (i = 0; i < t->rows; i++){ + guint32 cols [MONO_MEMBERREF_SIZE]; + + mono_metadata_decode_row (t, i, cols, MONO_MEMBERREF_SIZE); + + kind = cols [MONO_MEMBERREF_CLASS] & 7; + idx = cols [MONO_MEMBERREF_CLASS] >> 3; + + x = g_strdup ("UNHANDLED CASE"); + + switch (kind){ + case 0: + ks = "TypeDef"; + xx = get_typedef (m, idx); + x = g_strconcat (xx, ".", mono_metadata_string_heap (m, cols [MONO_MEMBERREF_NAME]), NULL); + g_free (xx); + break; + case 1: + ks = "TypeRef"; + xx = get_typeref (m, idx); + x = g_strconcat (xx, ".", mono_metadata_string_heap (m, cols [MONO_MEMBERREF_NAME]), NULL); + g_free (xx); + break; + case 2: + ks = "ModuleRef"; break; + case 3: + ks = "MethodDef"; + x = get_methoddef (m, idx); + break; + case 4: + ks = "TypeSpec"; + xx = get_typespec (m, idx, FALSE, NULL); + x = g_strconcat (xx, ".", mono_metadata_string_heap (m, cols [MONO_MEMBERREF_NAME]), NULL); + g_free (xx); + break; + default: + g_error ("Unknown tag: %d\n", kind); + } + blob = mono_metadata_blob_heap (m, cols [MONO_MEMBERREF_SIGNATURE]); + mono_metadata_decode_blob_size (blob, &blob); + if (*blob == 0x6) { /* it's a field */ + sig = get_field_signature (m, cols [MONO_MEMBERREF_SIGNATURE], NULL); + } else { + sig = get_methodref_signature (m, cols [MONO_MEMBERREF_SIGNATURE], NULL); + } + fprintf (output, "%d: %s[%d] %s\n\tResolved: %s\n\tSignature: %s\n\t\n", + i + 1, + ks, idx, + mono_metadata_string_heap (m, cols [MONO_MEMBERREF_NAME]), + x ? x : "", + sig); + + if (x) + g_free (x); + g_free (sig); + } +} + +void +dump_table_class_layout (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_CLASSLAYOUT]; + int i; + fprintf (output, "ClassLayout Table (1..%d)\n", t->rows); + + for (i = 0; i < t->rows; i++){ + guint32 cols [MONO_CLASS_LAYOUT_SIZE]; + + mono_metadata_decode_row (t, i, cols, MONO_CLASS_LAYOUT_SIZE); + + fprintf (output, "%d: PackingSize=%d ClassSize=%d Parent=%s\n", + i + 1, cols [MONO_CLASS_LAYOUT_PACKING_SIZE], + cols [MONO_CLASS_LAYOUT_CLASS_SIZE], + get_typedef (m, cols [MONO_CLASS_LAYOUT_PARENT])); + } +} + +void +dump_table_constant (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_CONSTANT]; + int i; + const char *desc [] = { + "Field", + "Param", + "Property", + "" + }; + fprintf (output, "Constant Table (1..%d)\n", t->rows); + + for (i = 0; i < t->rows; i++){ + guint32 cols [MONO_CONSTANT_SIZE]; + const char *parent; + mono_metadata_decode_row (t, i, cols, MONO_CONSTANT_SIZE); + parent = desc [cols [MONO_CONSTANT_PARENT] & MONO_HASCONSTANT_MASK]; + + fprintf (output, "%d: Parent= %s: %d %s\n", + i + 1, parent, cols [MONO_CONSTANT_PARENT] >> MONO_HASCONSTANT_BITS, + get_constant (m, (MonoTypeEnum) cols [MONO_CONSTANT_TYPE], cols [MONO_CONSTANT_VALUE])); + } + +} + +void +dump_table_property_map (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_PROPERTYMAP]; + int i; + char *s; + + fprintf (output, "Property Map Table (1..%d)\n", t->rows); + + for (i = 0; i < t->rows; i++){ + guint32 cols [MONO_PROPERTY_MAP_SIZE]; + + mono_metadata_decode_row (t, i, cols, MONO_PROPERTY_MAP_SIZE); + s = get_typedef (m, cols [MONO_PROPERTY_MAP_PARENT]); + fprintf (output, "%d: %s (%d) %d\n", i + 1, s, cols [MONO_PROPERTY_MAP_PARENT], cols [MONO_PROPERTY_MAP_PROPERTY_LIST]); + g_free (s); + } +} + +void +dump_table_property (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_PROPERTY]; + int i, j, pcount; + const char *ptr; + char flags[128]; + + fprintf (output, "Property Table (1..%d)\n", t->rows); + + for (i = 0; i < t->rows; i++){ + guint32 cols [MONO_PROPERTY_SIZE]; + char *type; + int prop_flags; + + mono_metadata_decode_row (t, i, cols, MONO_PROPERTY_SIZE); + flags [0] = 0; + prop_flags = cols [MONO_PROPERTY_FLAGS]; + if (prop_flags & 0x0200) + strcat (flags, "special "); + if (prop_flags & 0x0400) + strcat (flags, "runtime "); + if (prop_flags & 0x1000) + strcat (flags, "hasdefault "); + + ptr = mono_metadata_blob_heap (m, cols [MONO_PROPERTY_TYPE]); + /* bsize = */ mono_metadata_decode_blob_size (ptr, &ptr); + /* ECMA claims 0x08 ... */ + if (*ptr != 0x28 && *ptr != 0x08) + g_warning("incorrect signature in propert blob: 0x%x", *ptr); + ptr++; + pcount = mono_metadata_decode_value (ptr, &ptr); + ptr = get_type (m, ptr, &type, FALSE, NULL); + fprintf (output, "%d: %s %s (", + i + 1, type, mono_metadata_string_heap (m, cols [MONO_PROPERTY_NAME])); + g_free (type); + + for (j = 0; j < pcount; j++){ + ptr = get_param (m, ptr, &type, NULL); + fprintf (output, "%s%s", j > 0? ", " : "",type); + g_free (type); + } + fprintf (output, ") %s\n", flags); + } +} + +void +dump_table_event (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_EVENT]; + int i; + fprintf (output, "Event Table (1..%d)\n", t->rows); + + for (i = 0; i < t->rows; i++){ + guint32 cols [MONO_EVENT_SIZE]; + const char *name; + char *type; + + mono_metadata_decode_row (t, i, cols, MONO_EVENT_SIZE); + + name = mono_metadata_string_heap (m, cols [MONO_EVENT_NAME]); + type = get_typedef_or_ref (m, cols [MONO_EVENT_TYPE], NULL); + fprintf (output, "%d: %s %s %s\n", i + 1, type, name, + cols [MONO_EVENT_FLAGS] & 0x200 ? "specialname " : ""); + g_free (type); + } + +} + +void +dump_table_file (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_FILE]; + int i, j, len; + fprintf (output, "File Table (1..%d)\n", t->rows); + + for (i = 0; i < t->rows; i++){ + guint32 cols [MONO_FILE_SIZE]; + const char *name, *hash; + + mono_metadata_decode_row (t, i, cols, MONO_FILE_SIZE); + + name = mono_metadata_string_heap (m, cols [MONO_FILE_NAME]); + fprintf (output, "%d: %s %s [", i + 1, name, + cols [MONO_FILE_FLAGS] & 0x1 ? "nometadata" : "containsmetadata"); + hash = mono_metadata_blob_heap (m, cols [MONO_FILE_HASH_VALUE]); + len = mono_metadata_decode_blob_size (hash, &hash); + for (j = 0; j < len; ++j) + fprintf (output, "%s%02X", j? " ": "", hash [j] & 0xff); + fprintf (output, "]\n"); + } + +} + +static char* +get_manifest_implementation (MonoImage *m, guint32 idx) +{ + guint32 row; + const char* table = ""; + if (!idx) + return g_strdup ("current module"); + row = idx >> MONO_IMPLEMENTATION_BITS; + switch (idx & MONO_IMPLEMENTATION_MASK) { + case MONO_IMPLEMENTATION_FILE: + table = "file"; + break; + case MONO_IMPLEMENTATION_ASSEMBLYREF: + table = "assemblyref"; + break; + case MONO_IMPLEMENTATION_EXP_TYPE: + table = "exportedtype"; + break; + default: + g_assert_not_reached (); + } + return g_strdup_printf ("%s %d", table, row); +} + +static const char* +get_manifest_flags (guint32 mf) +{ + mf &= 3; + switch (mf) { + case 1: return "public"; + case 2: return "private"; + default: + return ""; + } +} + +void +dump_table_manifest (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_MANIFESTRESOURCE]; + int i; + fprintf (output, "Manifestresource Table (1..%d)\n", t->rows); + + for (i = 0; i < t->rows; i++){ + guint32 cols [MONO_MANIFEST_SIZE]; + const char *name, *mf; + char *impl; + + mono_metadata_decode_row (t, i, cols, MONO_MANIFEST_SIZE); + + name = mono_metadata_string_heap (m, cols [MONO_MANIFEST_NAME]); + mf = get_manifest_flags (cols [MONO_MANIFEST_FLAGS]); + impl = get_manifest_implementation (m, cols [MONO_MANIFEST_IMPLEMENTATION]); + fprintf (output, "%d: %s '%s' at offset %u in %s\n", i + 1, mf, name, cols [MONO_MANIFEST_OFFSET], impl); + g_free (impl); + } + +} + +void +dump_table_moduleref (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_MODULEREF]; + int i; + fprintf (output, "ModuleRef Table (1..%d)\n", t->rows); + + for (i = 0; i < t->rows; i++){ + guint32 cols [MONO_MODULEREF_SIZE]; + const char *name; + + mono_metadata_decode_row (t, i, cols, MONO_MODULEREF_SIZE); + + name = mono_metadata_string_heap (m, cols [MONO_MODULEREF_NAME]); + fprintf (output, "%d: %s\n", i + 1, name); + } + +} + +void +dump_table_module (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_MODULE]; + int i; + fprintf (output, "Module Table (1..%d)\n", t->rows); + + for (i = 0; i < t->rows; i++){ + guint32 cols [MONO_MODULE_SIZE]; + const char *name; + char *guid; + + mono_metadata_decode_row (t, i, cols, MONO_MODULE_SIZE); + + name = mono_metadata_string_heap (m, cols [MONO_MODULE_NAME]); + guid = get_guid (m, cols [MONO_MODULE_MVID]); + fprintf (output, "%d: %s %d %s\n", i + 1, name, cols [MONO_MODULE_MVID], guid); + } +} + +void +dump_table_method (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_METHOD]; + MonoTableInfo *td = &m->tables [MONO_TABLE_TYPEDEF]; + int i, current_type; + guint32 first_m, last_m; + /* Generic container for Type & method */ + MonoGenericContainer *type_container = NULL, *method_container = NULL; + + fprintf (output, "Method Table (1..%d)\n", t->rows); + + current_type = 1; + last_m = first_m = 1; + for (i = 1; i <= t->rows; i++){ + MonoError error; + guint32 cols [MONO_METHOD_SIZE]; + char *sig, *impl_flags; + const char *sigblob; + MonoMethodSignature *method; + + /* + * Find the next type. + */ + while (current_type <= td->rows && i >= (last_m = mono_metadata_decode_row_col (td, current_type - 1, MONO_TYPEDEF_METHOD_LIST))) { + current_type++; + } + if (i == first_m) { + fprintf (output, "########## %s.%s\n", + mono_metadata_string_heap (m, mono_metadata_decode_row_col (td, current_type - 2, MONO_TYPEDEF_NAMESPACE)), + mono_metadata_string_heap (m, mono_metadata_decode_row_col (td, current_type - 2, MONO_TYPEDEF_NAME))); + first_m = last_m; + type_container = mono_metadata_load_generic_params (m, MONO_TOKEN_TYPE_DEF | (current_type - 1), NULL); + if (type_container) { + mono_metadata_load_generic_param_constraints_checked (m, MONO_TOKEN_TYPE_DEF | (current_type - 1), type_container, &error); + g_assert (mono_error_ok (&error)); /*FIXME don't swallow the error message*/ + } + } + + method_container = mono_metadata_load_generic_params (m, MONO_TOKEN_METHOD_DEF | i, type_container); + if (method_container) { + mono_metadata_load_generic_param_constraints_checked (m, MONO_TOKEN_METHOD_DEF | i, method_container, &error); + g_assert (mono_error_ok (&error)); /*FIXME don't swallow the error message*/ + } + mono_metadata_decode_table_row (m, MONO_TABLE_METHOD, i - 1, cols, MONO_METHOD_SIZE); + sigblob = mono_metadata_blob_heap (m, cols [MONO_METHOD_SIGNATURE]); + mono_metadata_decode_blob_size (sigblob, &sigblob); + method = mono_metadata_parse_method_signature_full (m, method_container ? method_container : type_container, i, sigblob, &sigblob, &error); + if (!mono_error_ok (&error)) { + fprintf (output,"%d: failed to parse due to %s\n", i, mono_error_get_message (&error)); + mono_error_cleanup (&error); + continue; + } + + g_assert (mono_error_ok (&error)); /*FIXME don't swallow the error message*/ + sig = dis_stringify_method_signature (m, method, i, method_container ? method_container : type_container, FALSE); + impl_flags = get_method_impl_flags (cols [MONO_METHOD_IMPLFLAGS]); + fprintf (output, "%d: %s (param: %d impl_flags: %s)\n", i, sig, cols [MONO_METHOD_PARAMLIST], impl_flags); + g_free (sig); + g_free (impl_flags); + mono_metadata_free_method_signature (method); + } + +} + +void +dump_table_implmap (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_IMPLMAP]; + MonoTableInfo *td = &m->tables [MONO_TABLE_MODULEREF]; + int i; + + fprintf (output, "ImplMap Table (1..%d)\n", t->rows); + + for (i = 1; i <= t->rows; i++){ + guint32 cols [MONO_IMPLMAP_SIZE]; + char *method; + + mono_metadata_decode_row (t, i - 1, cols, MONO_IMPLMAP_SIZE); + + method = get_method (m, MONO_TOKEN_METHOD_DEF | (cols [MONO_IMPLMAP_MEMBER] >> MONO_MEMBERFORWD_BITS), NULL); + + fprintf (output, "%d: %s %d (%s %s)\n", i, + method, + cols [MONO_IMPLMAP_FLAGS], + mono_metadata_string_heap (m, cols [MONO_IMPLMAP_NAME]), + mono_metadata_string_heap (m, mono_metadata_decode_row_col (td, cols [MONO_IMPLMAP_SCOPE] - 1, MONO_MODULEREF_NAME))); + } +} + +void +dump_table_fieldrva (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_FIELDRVA]; + int i; + + fprintf (output, "FieldRVA Table (1..%d)\n", t->rows); + + for (i = 1; i <= t->rows; i++){ + guint32 cols [MONO_FIELD_RVA_SIZE]; + + mono_metadata_decode_row (t, i - 1, cols, MONO_FIELD_RVA_SIZE); + fprintf (output, "%d: Field %d: %x\n", i, cols [MONO_FIELD_RVA_FIELD], cols [MONO_FIELD_RVA_RVA]); + } +} + +void +dump_table_methodimpl (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_METHODIMPL]; + /*MonoTableInfo *td = &m->tables [MONO_TABLE_TYPEDEF];*/ + int i; + + fprintf (output, "MethodImpl Table (1..%d)\n", t->rows); + + for (i = 1; i <= t->rows; i++){ + guint32 cols [MONO_METHODIMPL_SIZE]; + char *klass, *impl, *decl; + + mono_metadata_decode_row (t, i - 1, cols, MONO_METHODIMPL_SIZE); + klass = get_typedef (m, cols [MONO_METHODIMPL_CLASS]); + impl = get_method (m, method_dor_to_token (cols [MONO_METHODIMPL_BODY]), NULL); + decl = get_method (m, method_dor_to_token (cols [MONO_METHODIMPL_DECLARATION]), NULL); + fprintf (output, "%d: %s\n\tdecl: %s\n\timpl: %s\n", i, klass, decl, impl); + g_free (klass); + g_free (impl); + g_free (decl); + } + +} + +static dis_map_t semantics_map [] = { + {1, "setter"}, + {2, "getter"}, + {4, "other"}, + {8, "add-on"}, + {0x10, "remove-on"}, + {0x20, "fire"}, + {0, NULL}, +}; + +void +dump_table_methodsem (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_METHODSEMANTICS]; + int i, is_property, index; + const char *semantics; + + fprintf (output, "Method Semantics Table (1..%d)\n", t->rows); + for (i = 1; i <= t->rows; i++){ + guint32 cols [MONO_METHOD_SEMA_SIZE]; + + mono_metadata_decode_row (t, i - 1, cols, MONO_METHOD_SEMA_SIZE); + semantics = flags (cols [MONO_METHOD_SEMA_SEMANTICS], semantics_map); + is_property = cols [MONO_METHOD_SEMA_ASSOCIATION] & MONO_HAS_SEMANTICS_MASK; + index = cols [MONO_METHOD_SEMA_ASSOCIATION] >> MONO_HAS_SEMANTICS_BITS; + fprintf (output, "%d: [%d] %s method: %d %s %d\n", i, cols [MONO_METHOD_SEMA_ASSOCIATION], semantics, + cols [MONO_METHOD_SEMA_METHOD] - 1, + is_property? "property" : "event", + index); + } +} + +void +dump_table_interfaceimpl (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_INTERFACEIMPL]; + int i; + + fprintf (output, "Interface Implementation Table (1..%d)\n", t->rows); + for (i = 1; i <= t->rows; i++) { + guint32 cols [MONO_INTERFACEIMPL_SIZE]; + + mono_metadata_decode_row (t, i - 1, cols, MONO_INTERFACEIMPL_SIZE); + fprintf (output, "%d: %s implements %s\n", i, + get_typedef (m, cols [MONO_INTERFACEIMPL_CLASS]), + get_typedef_or_ref (m, cols [MONO_INTERFACEIMPL_INTERFACE], NULL)); + } +} + +static char* +has_cattr_get_table (MonoImage *m, guint32 val) +{ + guint32 t = val & MONO_CUSTOM_ATTR_MASK; + guint32 index = val >> MONO_CUSTOM_ATTR_BITS; + const char *table; + + switch (t) { + case MONO_CUSTOM_ATTR_METHODDEF: + table = "MethodDef"; + break; + case MONO_CUSTOM_ATTR_FIELDDEF: + table = "FieldDef"; + break; + case MONO_CUSTOM_ATTR_TYPEREF: + table = "TypeRef"; + break; + case MONO_CUSTOM_ATTR_TYPEDEF: + table = "TypeDef"; + break; + case MONO_CUSTOM_ATTR_PARAMDEF: + table = "Param"; + break; + case MONO_CUSTOM_ATTR_INTERFACE: + table = "InterfaceImpl"; + break; + case MONO_CUSTOM_ATTR_MEMBERREF: + table = "MemberRef"; + break; + case MONO_CUSTOM_ATTR_MODULE: + table = "Module"; + break; + case MONO_CUSTOM_ATTR_PERMISSION: + table = "DeclSecurity?"; + break; + case MONO_CUSTOM_ATTR_PROPERTY: + table = "Property"; + break; + case MONO_CUSTOM_ATTR_EVENT: + table = "Event"; + break; + case MONO_CUSTOM_ATTR_SIGNATURE: + table = "StandAloneSignature"; + break; + case MONO_CUSTOM_ATTR_MODULEREF: + table = "ModuleRef"; + break; + case MONO_CUSTOM_ATTR_TYPESPEC: + table = "TypeSpec"; + break; + case MONO_CUSTOM_ATTR_ASSEMBLY: + table = "Assembly"; + break; + case MONO_CUSTOM_ATTR_ASSEMBLYREF: + table = "AssemblyRef"; + break; + case MONO_CUSTOM_ATTR_FILE: + table = "File"; + break; + case MONO_CUSTOM_ATTR_EXP_TYPE: + table = "ExportedType"; + break; + case MONO_CUSTOM_ATTR_MANIFEST: + table = "Manifest"; + break; + case MONO_CUSTOM_ATTR_GENERICPAR: + table = "GenericParam"; + break; + default: + table = "Unknown"; + break; + } + /* + * FIXME: we should decode the index into something more uman-friendly. + */ + return g_strdup_printf ("%s: %d", table, index); +} + +static char* +custom_attr_params (MonoImage *m, MonoMethodSignature* sig, const char* value) +{ + int len, i, slen, type; + GString *res; + char *s; + const char *p = value; + + len = mono_metadata_decode_value (p, &p); + if (len < 2 || read16 (p) != 0x0001) /* Prolog */ + return g_strdup (""); + + /* skip prolog */ + p += 2; + res = g_string_new (""); + for (i = 0; i < sig->param_count; ++i) { + if (i != 0) + g_string_append (res, ", "); + type = sig->params [i]->type; +handle_enum: + switch (type) { + case MONO_TYPE_U1: + g_string_append_printf (res, "%d", (unsigned int)*p); + ++p; + break; + case MONO_TYPE_I1: + g_string_append_printf (res, "%d", *p); + ++p; + break; + case MONO_TYPE_BOOLEAN: + g_string_append_printf (res, "%s", *p?"true":"false"); + ++p; + break; + case MONO_TYPE_CHAR: + g_string_append_printf (res, "'%c'", read16 (p)); + p += 2; + break; + case MONO_TYPE_U2: + g_string_append_printf (res, "%d", read16 (p)); + p += 2; + break; + case MONO_TYPE_I2: + g_string_append_printf (res, "%d", (gint16)read16 (p)); + p += 2; + break; + case MONO_TYPE_U4: + g_string_append_printf (res, "%d", read32 (p)); + p += 4; + break; + case MONO_TYPE_I4: + g_string_append_printf (res, "%d", (gint32)read32 (p)); + p += 4; + break; + case MONO_TYPE_U8: + g_string_append_printf (res, "%lld", (long long)read64 (p)); + p += 8; + break; + case MONO_TYPE_I8: + g_string_append_printf (res, "%lld", (long long)read64 (p)); + p += 8; + break; + case MONO_TYPE_R4: { + float val; + int inf; + readr4 (p, &val); + inf = dis_isinf (val); + if (inf == -1) + g_string_append_printf (res, "(00 00 80 ff)"); /* negative infinity */ + else if (inf == 1) + g_string_append_printf (res, "(00 00 80 7f)"); /* positive infinity */ + else if (dis_isnan (val)) + g_string_append_printf (res, "(00 00 c0 ff)"); /* NaN */ + else + g_string_append_printf (res, "%g", val); + p += 4; + break; + } + case MONO_TYPE_R8: { + double val; + int inf; + + readr8 (p, &val); + inf = dis_isinf (val); + if (inf == -1) + g_string_append_printf (res, "(00 00 00 00 00 00 f0 ff)"); /* negative infinity */ + else if (inf == 1) + g_string_append_printf (res, "(00 00 00 00 00 00 f0 7f)"); /* positive infinity */ + else if (isnan (val)) + g_string_append_printf (res, "(00 00 00 00 00 00 f8 ff)"); /* NaN */ + else + g_string_append_printf (res, "%g", val); + p += 8; + break; + } + case MONO_TYPE_VALUETYPE: + if (mono_class_is_enum (sig->params [i]->data.klass)) { + type = mono_class_enum_basetype (sig->params [i]->data.klass)->type; + goto handle_enum; + } else { + g_warning ("generic valutype not handled in custom attr value decoding"); + } + break; + case MONO_TYPE_CLASS: /* It must be a Type: check? */ + case MONO_TYPE_STRING: + if (*p == (char)0xff) { + g_string_append (res, "null"); + p++; + break; + } + slen = mono_metadata_decode_value (p, &p); + g_string_append_c (res, '"'); + g_string_append (res, p); + g_string_append_c (res, '"'); + p += slen; + break; + default: + g_warning ("Type %02x not handled in custom attr value decoding", sig->params [i]->type); + break; + } + } + slen = read16 (p); + if (slen) { + g_string_append_printf (res, " %d named args: (", slen); + slen = len - (p - value) + 1; + for (i = 0; i < slen; ++i) { + g_string_append_printf (res, " %02X", (p [i] & 0xff)); + } + g_string_append_c (res, ')'); + } + s = res->str; + g_string_free (res, FALSE); + return s; +} + +void +dump_table_customattr (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_CUSTOMATTRIBUTE]; + int i; + + fprintf (output, "Custom Attributes Table (1..%d)\n", t->rows); + for (i = 1; i <= t->rows; i++) { + MonoError error; + guint32 cols [MONO_CUSTOM_ATTR_SIZE]; + guint32 mtoken; + char * desc; + char *method; + char *params; + MonoMethod *meth; + + mono_metadata_decode_row (t, i - 1, cols, MONO_CUSTOM_ATTR_SIZE); + desc = has_cattr_get_table (m, cols [MONO_CUSTOM_ATTR_PARENT]); + mtoken = cols [MONO_CUSTOM_ATTR_TYPE] >> MONO_CUSTOM_ATTR_TYPE_BITS; + switch (cols [MONO_CUSTOM_ATTR_TYPE] & MONO_CUSTOM_ATTR_TYPE_MASK) { + case MONO_CUSTOM_ATTR_TYPE_METHODDEF: + mtoken |= MONO_TOKEN_METHOD_DEF; + break; + case MONO_CUSTOM_ATTR_TYPE_MEMBERREF: + mtoken |= MONO_TOKEN_MEMBER_REF; + break; + default: + g_warning ("Unknown table for custom attr type %08x", cols [MONO_CUSTOM_ATTR_TYPE]); + break; + } + method = get_method (m, mtoken, NULL); + meth = mono_get_method_checked (m, mtoken, NULL, NULL, &error); + if (meth) { + params = custom_attr_params (m, mono_method_signature (meth), mono_metadata_blob_heap (m, cols [MONO_CUSTOM_ATTR_VALUE])); + fprintf (output, "%d: %s: %s [%s]\n", i, desc, method, params); + g_free (params); + } else { + fprintf (output, "Could not decode method due to %s", mono_error_get_message (&error)); + mono_error_cleanup (&error); + } + + g_free (desc); + g_free (method); + } +} + +void +dump_table_nestedclass (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_NESTEDCLASS]; + guint32 cols [MONO_NESTED_CLASS_SIZE]; + int i; + char *nested, *nesting; + fprintf (output, "NestedClass Table (1..%d)\n", t->rows); + + for (i = 1; i <= t->rows; i++){ + mono_metadata_decode_row (t, i - 1, cols, MONO_NESTED_CLASS_SIZE); + nested = get_typedef (m, cols [MONO_NESTED_CLASS_NESTED]); + nesting = get_typedef (m, cols [MONO_NESTED_CLASS_ENCLOSING]); + fprintf (output, "%d: %d %d: %s in %s\n", i, + cols [MONO_NESTED_CLASS_NESTED], + cols [MONO_NESTED_CLASS_ENCLOSING], nested, nesting); + g_free (nested); + g_free (nesting); + } + +} + +void +dump_table_exported (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_EXPORTEDTYPE]; + guint32 cols [MONO_EXP_TYPE_SIZE]; + int i; + const char *name, *nspace; + char *impl; + guint32 index, flags; + fprintf (output, "ExportedType Table (1..%d)\n", t->rows); + + for (i = 1; i <= t->rows; i++) { + mono_metadata_decode_row (t, i - 1, cols, MONO_EXP_TYPE_SIZE); + name = mono_metadata_string_heap (m, cols [MONO_EXP_TYPE_NAME]); + nspace = mono_metadata_string_heap (m, cols [MONO_EXP_TYPE_NAMESPACE]); + impl = get_manifest_implementation (m, cols [MONO_EXP_TYPE_IMPLEMENTATION]); + index = cols [MONO_EXP_TYPE_TYPEDEF]; + flags = cols [MONO_EXP_TYPE_FLAGS]; + fprintf (output, "%d: %s%s%s is in %s, index=%x, flags=0x%x\n", i, nspace, *nspace ? "." : "", name, impl, index, flags); + g_free (impl); + } + +} + +static void +dump_blob (MonoImage *m, const char* blob) +{ + int j, bsize; + + bsize = mono_metadata_decode_blob_size (blob, &blob); + + for (j = 0; j < bsize; j++) { + fprintf (output, "%02x ", blob [j] & 0xff); + } +} + +void +dump_table_field_marshal (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_FIELDMARSHAL]; + guint32 cols [MONO_FIELD_MARSHAL_SIZE]; + int i, is_field, idx; + const char *blob; + char *native; + + fprintf (output, "FieldMarshal Table (1..%d)\n", t->rows); + + for (i = 1; i <= t->rows; i++) { + mono_metadata_decode_row (t, i - 1, cols, MONO_FIELD_MARSHAL_SIZE); + blob = mono_metadata_blob_heap (m, cols [MONO_FIELD_MARSHAL_NATIVE_TYPE]); + native = get_marshal_info (m, blob); + is_field = (cols [MONO_FIELD_MARSHAL_PARENT] & MONO_HAS_FIELD_MARSHAL_MASK) == MONO_HAS_FIELD_MARSHAL_FIELDSREF; + idx = cols [MONO_FIELD_MARSHAL_PARENT] >> MONO_HAS_FIELD_MARSHAL_BITS; + fprintf (output, "%d: (0x%04x) %s %d: %s\n", i, cols [MONO_FIELD_MARSHAL_PARENT], is_field? "Field" : "Param", idx, native); + fprintf (output, "\tblob encoding: "); + dump_blob (m, blob); + fprintf (output, "\n"); + g_free (native); + } + +} + +static const char* +get_security_action (int val) { + static char buf [32]; + + switch (val) { + case SECURITY_ACTION_DEMAND: + return "Demand"; + case SECURITY_ACTION_ASSERT: + return "Assert"; + case SECURITY_ACTION_DENY: + return "Deny"; + case SECURITY_ACTION_PERMITONLY: + return "PermitOnly"; + case SECURITY_ACTION_LINKDEMAND: + return "LinkDemand"; + case SECURITY_ACTION_INHERITDEMAND: + return "InheritanceDemand"; + case SECURITY_ACTION_REQMIN: + return "RequestMinimum"; + case SECURITY_ACTION_REQOPT: + return "RequestOptional"; + case SECURITY_ACTION_REQREFUSE: + return "RequestRefuse"; + /* Special actions (for non CAS permissions) */ + case SECURITY_ACTION_NONCASDEMAND: + return "NonCasDemand"; + case SECURITY_ACTION_NONCASLINKDEMAND: + return "NonCasLinkDemand"; + case SECURITY_ACTION_NONCASINHERITANCE: + return "NonCasInheritance"; + /* Fx 2.0 actions (for both CAS and non-CAS permissions) */ + case SECURITY_ACTION_LINKDEMANDCHOICE: + return "LinkDemandChoice"; + case SECURITY_ACTION_INHERITDEMANDCHOICE: + return "InheritanceDemandChoice"; + case SECURITY_ACTION_DEMANDCHOICE: + return "DemandChoice"; + default: + g_snprintf (buf, sizeof (buf), "0x%04X", val); + return buf; + } +} + +void +dump_table_declsec (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_DECLSECURITY]; + guint32 cols [MONO_DECL_SECURITY_SIZE]; + int i, len; + guint32 idx; + const char *blob, *action; + const char* parent[] = { + "TypeDef", "MethodDef", "Assembly", "" + }; + + fprintf (output, "DeclSecurity Table (1..%d)\n", t->rows); + + for (i = 1; i <= t->rows; i++) { + mono_metadata_decode_row (t, i - 1, cols, MONO_DECL_SECURITY_SIZE); + blob = mono_metadata_blob_heap (m, cols [MONO_DECL_SECURITY_PERMISSIONSET]); + len = mono_metadata_decode_blob_size (blob, &blob); + action = get_security_action (cols [MONO_DECL_SECURITY_ACTION]); + idx = cols [MONO_DECL_SECURITY_PARENT]; + fprintf (output, "%d: %s on %s %d%s", i, action, parent [idx & MONO_HAS_DECL_SECURITY_MASK], idx >> MONO_HAS_DECL_SECURITY_BITS, len? ":\n\t":"\n"); + if (!len) + continue; + if (blob [0] == MONO_DECLSEC_FORMAT_20) { + /* 2.0 declarative security format */ + char *declsec = dump_declsec_entry20 (m, blob, "\t"); + fprintf (output, "%s", declsec); + g_free (declsec); + } else { + /* 1.0 declarative security format - Unicode XML */ + for (idx = 0; idx < len; ++idx) + fprintf (output, "%c", blob [idx]); + } + fprintf (output, "\n"); + } +} + +void +dump_table_genericpar (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_GENERICPARAM]; + guint32 cols [MONO_GENERICPARAM_SIZE]; + int i; + + fprintf (output, "GenericParameters (1..%d)\n", t->rows); + + for (i = 1; i <= t->rows; i++) { + char *sig; + mono_metadata_decode_row (t, i - 1, cols, MONO_GENERICPARAM_SIZE); + + // sig = get_type_or_methdef (m, cols [MONO_GENERICPARAM_OWNER]); + sig = g_strdup_printf ("%x", cols [MONO_GENERICPARAM_OWNER]); + fprintf (output, "%d: %d, flags=%d, owner=%s %s\n", i, + cols [MONO_GENERICPARAM_NUMBER], + cols [MONO_GENERICPARAM_FLAGS], sig, + mono_metadata_string_heap (m, cols [MONO_GENERICPARAM_NAME])); + g_free (sig); + } +} + +void +dump_table_methodspec (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_METHODSPEC]; + guint32 cols [MONO_METHODSPEC_SIZE]; + int i; + + fprintf (output, "MethodSpec (1..%d)\n", t->rows); + + for (i = 1; i <= t->rows; i++) { + char *sig; + char *method; + guint32 token; + + mono_metadata_decode_row (t, i - 1, cols, MONO_METHODSPEC_SIZE); + + /* build a methodspec token to get the method */ + token = MONO_TOKEN_METHOD_SPEC | i; + method = get_method (m, token, NULL); + + sig = get_method_type_param (m, cols [MONO_METHODSPEC_SIGNATURE], NULL); + fprintf (output, "%d: %s, %s\n", i, method, sig); + g_free (sig); + g_free (method); + } +} + +void +dump_table_parconstraint (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_GENERICPARAMCONSTRAINT]; + guint32 cols [MONO_GENPARCONSTRAINT_SIZE]; + int i; + + fprintf (output, "Generic Param Constraint (1..%d)\n", t->rows); + + for (i = 1; i <= t->rows; i++) { + char *sig; + mono_metadata_decode_row (t, i - 1, cols, MONO_GENPARCONSTRAINT_SIZE); + + // sig = get_typedef_or_ref (m, cols [MONO_GENPARCONSTRAINT_CONSTRAINT], NULL); + sig = g_strdup_printf ("%x", cols [MONO_GENPARCONSTRAINT_CONSTRAINT]); + fprintf (output, "%d: gen-par=%d, Constraint=%s\n", i, + cols [MONO_GENPARCONSTRAINT_GENERICPAR], sig); + g_free (sig); + } +} + +void +dump_stream_blob (MonoImage *m) +{ + int i; + + fprintf (output, "Blob heap contents\n"); + + for (i = 0; i < m->heap_blob.size; i++) { + if (i > 0) { + if ((i % 16) == 0) + fprintf (output, "\n"); + else if ((i % 8) == 0) + fprintf (output, "- "); + } + fprintf (output, "%02x ", m->heap_blob.data [i] & 0xff); + } + + fprintf (output, "\n"); +} + +void +dump_stream_strings (MonoImage *m) +{ + guint32 i; + + fprintf (output, "Strings heap contents\n"); + + for (i = 0; i < m->heap_strings.size; ) { + const char *str = mono_metadata_string_heap (m, i); + fprintf (output, "%02x: \"%s\"\n", i, str); + i += strlen (str) + 1; + } +} + +void +dump_stream_us (MonoImage *m) +{ + guint32 i; + + fprintf (output, "User Strings heap contents\n"); + + for (i = 0; i < m->heap_us.size; ) { + const char *us_ptr = mono_metadata_user_string (m, i); + int len = mono_metadata_decode_blob_size (us_ptr, (const char**)&us_ptr); + + char *str = get_encoded_user_string_or_bytearray ((const guchar*)us_ptr, len); + fprintf (output, "%02x: %s\n", i, str); + g_free (str); + i += len + 1; + } +} + +void +dump_table_standalonesig (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_STANDALONESIG]; + guint32 cols [MONO_STAND_ALONE_SIGNATURE_SIZE]; + int i; + + fprintf (output, "Stand alone signature (1..%d)\n", t->rows); + + for (i = 1; i <= t->rows; i++) { + const char *locals_ptr; + int j, bsize; + + mono_metadata_decode_row (t, i - 1, cols, MONO_STAND_ALONE_SIGNATURE_SIZE); + + locals_ptr = mono_metadata_blob_heap (m, cols [MONO_STAND_ALONE_SIGNATURE]); + bsize = mono_metadata_decode_blob_size (locals_ptr, &locals_ptr); + + fprintf (output, "%d: blob[0x%x] = ", i, cols [MONO_STAND_ALONE_SIGNATURE]); + + for (j = 0; j < bsize; j++) { + fprintf (output, "%02x ", locals_ptr [j] & 0xff); + } + fprintf (output, "\n"); + } +} + +static void +dump_table_ptr (MonoImage *m, int table, const char *name) +{ + MonoTableInfo *t = &m->tables [table]; + guint32 cols [1]; + int i; + + fprintf (output, "%s (1..%d)\n", name, t->rows); + + for (i = 1; i <= t->rows; i++) { + mono_metadata_decode_row (t, i - 1, cols, 1); + + fprintf (output, "%d: %d\n", i, cols [0]); + } +} + +void +dump_table_methodptr (MonoImage *m) +{ + dump_table_ptr (m, MONO_TABLE_METHOD_POINTER, "Method Ptr"); +} + +void +dump_table_fieldptr (MonoImage *m) +{ + dump_table_ptr (m, MONO_TABLE_FIELD_POINTER, "Field Ptr"); +} + +void +dump_table_paramptr (MonoImage *m) +{ + dump_table_ptr (m, MONO_TABLE_PARAM_POINTER, "Param Ptr"); +} + +void +dump_table_eventptr (MonoImage *m) +{ + dump_table_ptr (m, MONO_TABLE_EVENT_POINTER, "Event Ptr"); +} + +void +dump_table_propertyptr (MonoImage *m) +{ + dump_table_ptr (m, MONO_TABLE_PROPERTY_POINTER, "Property Ptr"); +} diff --git a/unity-2019.4.24f1-mbe/mono/dis/dump.h b/unity-2019.4.24f1-mbe/mono/dis/dump.h new file mode 100644 index 000000000..71502499a --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/dis/dump.h @@ -0,0 +1,42 @@ +extern FILE *output; + +void dump_table_assembly (MonoImage *m); +void dump_table_assemblyref (MonoImage *m); +void dump_table_class_layout (MonoImage *m); +void dump_table_constant (MonoImage *m); +void dump_table_customattr (MonoImage *m); +void dump_table_declsec (MonoImage *m); +void dump_table_property (MonoImage *m); +void dump_table_property_map (MonoImage *m); +void dump_table_event (MonoImage *m); +void dump_table_file (MonoImage *m); +void dump_table_moduleref (MonoImage *m); +void dump_table_module (MonoImage *m); +void dump_table_method (MonoImage *m); +void dump_table_methodimpl (MonoImage *m); +void dump_table_methodsem (MonoImage *m); +void dump_table_field (MonoImage *m); +void dump_table_manifest (MonoImage *m); +void dump_table_memberref (MonoImage *m); +void dump_table_param (MonoImage *m); +void dump_table_typedef (MonoImage *m); +void dump_table_typeref (MonoImage *m); +void dump_table_typespec (MonoImage *m); +void dump_table_exported (MonoImage *m); +void dump_table_nestedclass (MonoImage *m); +void dump_table_interfaceimpl (MonoImage *m); +void dump_table_field_marshal (MonoImage *m); +void dump_table_genericpar (MonoImage *m); +void dump_table_methodspec (MonoImage *m); +void dump_table_parconstraint(MonoImage *m); +void dump_table_implmap (MonoImage *m); +void dump_table_fieldrva (MonoImage *m); +void dump_table_standalonesig (MonoImage *m); +void dump_table_methodptr (MonoImage *m); +void dump_table_fieldptr (MonoImage *m); +void dump_table_paramptr (MonoImage *m); +void dump_table_eventptr (MonoImage *m); +void dump_table_propertyptr (MonoImage *m); +void dump_stream_blob (MonoImage *m); +void dump_stream_strings (MonoImage *m); +void dump_stream_us (MonoImage *m); diff --git a/unity-2019.4.24f1-mbe/mono/dis/get.c b/unity-2019.4.24f1-mbe/mono/dis/get.c new file mode 100644 index 000000000..f5b64acdd --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/dis/get.c @@ -0,0 +1,3262 @@ +/* + * get.c: Functions to get stringified values from the metadata tables. + * + * Author: + * Miguel de Icaza (miguel@ximian.com) + * + * (C) 2001 Ximian, Inc. + * Copyright 2012 Xamarin Inc + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#include +#include +#include +#include +#include +#include +#include +#include "meta.h" +#include "util.h" +#include "get.h" +#include +#include +#include +#include + +extern gboolean substitute_with_mscorlib_p; + +static char * +get_token_comment (const char *prefix, guint32 token); + +static MonoGenericContainer * +get_memberref_container (MonoImage *m, guint32 mrp_token, MonoGenericContainer *container); + +static char * +get_memberref_parent (MonoImage *m, guint32 mrp_token, MonoGenericContainer *container); + +static gboolean +cant_print_generic_param_name (MonoGenericParam *gparam); + +GHashTable *key_table = NULL; +GHashTable *mono_generic_params_with_ambiguous_names = NULL; +GHashTable *generic_containers = NULL; +gboolean show_method_tokens = FALSE; +gboolean show_tokens = FALSE; + +char * +get_typedef (MonoImage *m, int idx) +{ + guint32 cols [MONO_TYPEDEF_SIZE]; + const char *ns; + char *tstring, *result; + guint32 token; + + if (idx == 1) + /* */ + return NULL; + + mono_metadata_decode_row (&m->tables [MONO_TABLE_TYPEDEF], idx - 1, cols, MONO_TYPEDEF_SIZE); + + ns = mono_metadata_string_heap (m, cols [MONO_TYPEDEF_NAMESPACE]); + + /* Check if this is a nested type */ + token = MONO_TOKEN_TYPE_DEF | (idx); + token = mono_metadata_nested_in_typedef (m, token); + tstring = get_token_comment (NULL, token); + if (token) { + char *outer; + + outer = get_typedef (m, mono_metadata_token_index (token)); + result = g_strdup_printf ( + "%s/%s%s", outer, + mono_metadata_string_heap (m, cols [MONO_TYPEDEF_NAME]), + tstring ? tstring : ""); + g_free (outer); + g_free (tstring); + return result; + } + + + result = g_strdup_printf ( + "%s%s%s%s", ns, *ns?".":"", + mono_metadata_string_heap (m, cols [MONO_TYPEDEF_NAME]), + tstring ? tstring : ""); + g_free (tstring); + + return result; +} + +char * +get_module (MonoImage *m, int idx) +{ + guint32 cols [MONO_MODULE_SIZE]; + + /* + * There MUST BE only one module in the Module table + */ + g_assert (idx == 1); + + mono_metadata_decode_row (&m->tables [MONO_TABLE_MODULE], idx - 1, cols, MONO_MODULE_SIZE); + + return get_escaped_name (mono_metadata_string_heap (m, cols [MONO_MODULE_NAME])); +} + +char * +get_moduleref (MonoImage *m, int idx) +{ + guint32 cols [MONO_MODULEREF_SIZE]; + + mono_metadata_decode_row (&m->tables [MONO_TABLE_MODULEREF], idx - 1, cols, MONO_MODULEREF_SIZE); + + return get_escaped_name (mono_metadata_string_heap (m, cols [MONO_MODULEREF_NAME])); +} + +char * +get_assemblyref (MonoImage *m, int idx) +{ + guint32 cols [MONO_ASSEMBLYREF_SIZE]; + + mono_metadata_decode_row (&m->tables [MONO_TABLE_ASSEMBLYREF], idx - 1, cols, MONO_ASSEMBLYREF_SIZE); + + return get_escaped_name (mono_metadata_string_heap (m, cols [MONO_ASSEMBLYREF_NAME])); +} + +static char * +stringify_array (guint32 rank, guint32 num_sizes, guint32 num_lo_bounds, gint32 *sizes, gint32 *lo_bounds) +{ + GString *res = g_string_new ("["); + int i; + + for (i = 0; i < rank; i++) { + if (i) + g_string_append_c (res, ','); + if (i < num_lo_bounds) + g_string_append_printf (res, "%d...", lo_bounds [i]); + if (i < num_sizes) { + if (i < num_lo_bounds) + g_string_append_printf (res, "%d", lo_bounds [i] + sizes [i] - 1); + else + g_string_append_printf (res, "%d", sizes [i]); + } + + } + g_string_append (res, "]"); + + return g_string_free (res, FALSE); +} + +/* + * + * Returns a string representing the ArrayShape (22.2.16). + */ +static const char * +get_array_shape (MonoImage *m, const char *ptr, char **result) +{ + guint32 rank, num_sizes, num_lo_bounds; + gint32 *sizes = NULL, *lo_bounds = NULL; + int i; + + rank = mono_metadata_decode_value (ptr, &ptr); + num_sizes = mono_metadata_decode_value (ptr, &ptr); + + if (num_sizes > 0) + sizes = g_new (gint32, num_sizes); + + for (i = 0; i < num_sizes; i++) + sizes [i] = mono_metadata_decode_value (ptr, &ptr); + + num_lo_bounds = mono_metadata_decode_value (ptr, &ptr); + if (num_lo_bounds > 0) + lo_bounds = g_new (gint32, num_lo_bounds); + + for (i = 0; i < num_lo_bounds; i++) + lo_bounds [i] = mono_metadata_decode_signed_value (ptr, &ptr); + + *result = stringify_array (rank, num_sizes, num_lo_bounds, sizes, lo_bounds); + + g_free (sizes); + g_free (lo_bounds); + + return ptr; +} + +/** + * get_typespec: + * @m: metadata context + * @blob_idx: index into the blob heap + * + * Returns the stringified representation of a TypeSpec signature (22.2.17) + */ +char * +get_typespec (MonoImage *m, guint32 idx, gboolean is_def, MonoGenericContainer *container) +{ + guint32 cols [MONO_TYPESPEC_SIZE]; + const char *ptr; + char *s, *result; + GString *res = g_string_new (""); + MonoMethodSignature *sig; + + mono_metadata_decode_row (&m->tables [MONO_TABLE_TYPESPEC], idx-1, cols, MONO_TYPESPEC_SIZE); + ptr = mono_metadata_blob_heap (m, cols [MONO_TYPESPEC_SIGNATURE]); + /* len = */ mono_metadata_decode_value (ptr, &ptr); + + switch (*ptr++){ + case MONO_TYPE_PTR: + ptr = get_custom_mod (m, ptr, &s); + if (s){ + g_string_append (res, s); + g_string_append_c (res, ' '); + g_free (s); + } + + if (*ptr == MONO_TYPE_VOID) + g_string_append (res, "void"); + else { + ptr = get_type (m, ptr, &s, is_def, container); + if (s) + g_string_append (res, s); + } + g_string_append (res, "*"); + break; + + case MONO_TYPE_FNPTR: { + MonoError error; + sig = mono_metadata_parse_method_signature_full (m, container, 0, ptr, &ptr, &error); + g_assert (mono_error_ok (&error)); /*FIXME don't swallow the error message*/ + s = dis_stringify_function_ptr (m, sig); + g_string_append (res, "method "); + g_string_append (res, s); + g_free (s); + break; + } + case MONO_TYPE_ARRAY: + ptr = get_type (m, ptr, &s, is_def, container); + g_string_append (res, s); + g_free (s); + g_string_append_c (res, ' '); + ptr = get_array_shape (m, ptr, &s); + g_string_append (res, s); + g_free (s); + break; + + case MONO_TYPE_SZARRAY: + ptr = get_custom_mod (m, ptr, &s); + if (s){ + g_string_append (res, s); + g_string_append_c (res, ' '); + g_free (s); + } + ptr = get_type (m, ptr, &s, is_def, container); + g_string_append (res, s); + g_string_append (res, "[]"); + g_free (s); + break; + + default: + ptr = get_type (m, ptr - 1, &s, is_def, container); + g_string_append (res, s); + g_free (s); + break; + } + + if (show_tokens) { + int token = mono_metadata_make_token (MONO_TABLE_TYPESPEC, idx); + result = get_token_comment (res->str, token); + g_string_free (res, TRUE); + } else { + result = res->str; + g_string_free (res, FALSE); + } + + return result; +} + +char * +get_typeref (MonoImage *m, int idx) +{ + guint32 cols [MONO_TYPEREF_SIZE]; + const char *s, *t; + char *x, *ret; + guint32 rs_idx, table; + + mono_metadata_decode_row (&m->tables [MONO_TABLE_TYPEREF], idx - 1, cols, MONO_TYPEREF_SIZE); + + t = mono_metadata_string_heap (m, cols [MONO_TYPEREF_NAME]); + s = mono_metadata_string_heap (m, cols [MONO_TYPEREF_NAMESPACE]); + + rs_idx = cols [MONO_TYPEREF_SCOPE] >> MONO_RESOLTION_SCOPE_BITS; + table = cols [MONO_TYPEREF_SCOPE] & MONO_RESOLTION_SCOPE_MASK; + + switch (table){ + case MONO_RESOLTION_SCOPE_MODULE: /* Module */ + x = get_module (m, rs_idx); + ret = g_strdup_printf ("[%s] %s%s%s", x, s, *s?".":"", t); + g_free (x); + break; + + case MONO_RESOLTION_SCOPE_MODULEREF: /* ModuleRef */ + x = get_moduleref (m, rs_idx); + ret = g_strdup_printf ("[.module %s]%s%s%s", x, s, *s ? "." : "", t); + break; + + case MONO_RESOLTION_SCOPE_ASSEMBLYREF: /* + * AssemblyRef (ECMA docs claim it is 3, but it looks to + * me like it is 2 (tokens are prefixed with 0x23) + */ + x = get_assemblyref (m, rs_idx); + ret = g_strdup_printf ("[%s]%s%s%s", x, s, *s?".":"", t); + g_free (x); + break; + + case MONO_RESOLTION_SCOPE_TYPEREF: /* TypeRef */ + x = get_typeref (m, rs_idx); + ret = g_strdup_printf ("%s/%s", x, t); + g_free (x); + break; + + default: + ret = g_strdup_printf ("Unknown table in TypeRef %d", table); + } + + if (show_tokens) { + int token = mono_metadata_make_token (MONO_TABLE_TYPEREF, idx); + char *temp = get_token_comment (ret, token); + g_free (ret); + ret = temp; + } + + return ret; +} + +/** + * get_typedef_or_ref: + * @m: metadata context + * @dor_token: def or ref encoded index + * + * Low two bits contain table to lookup from + * high bits contain the index into the def or ref table + * + * Returns: a stringified version of the MethodDef or MethodRef + * at (dor_token >> 2) + */ +char * +get_typedef_or_ref (MonoImage *m, guint32 dor_token, MonoGenericContainer *container) +{ + char *temp = NULL, *s = NULL; + int table, idx; + + /* + * low 2 bits contain encoding + */ + table = dor_token & MONO_TYPEDEFORREF_MASK; + idx = dor_token >> MONO_TYPEDEFORREF_BITS; + + switch (table){ + case 0: /* TypeDef */ + temp = get_typedef (m, idx); + s = g_strdup_printf ("%s", temp); + break; + + case 1: /* TypeRef */ + temp = get_typeref (m, idx); + s = g_strdup_printf ("%s", temp); + break; + + case 2: /* TypeSpec */ + s = get_typespec (m, idx, FALSE, container); + break; + + default: + g_error ("Unhandled encoding for typedef-or-ref coded index 0x%08x", dor_token); + + } + + if (temp) + g_free (temp); + + return s; +} + +/** + * get_type_or_methdef + * @m: metadata context + * @dor_token: type or method def encoded index + * + * Low bit contains the table to lookup from + * high bits contain the index into the type def or method def table + * + * Returns: a stringified version of the TypeOrMethodDef token + */ +char * +get_type_or_methdef (MonoImage *m, guint32 dor_token) +{ + if (dor_token & MONO_TYPEORMETHOD_METHOD) /* MethodDef */ + return get_methoddef (m, dor_token >> MONO_TYPEORMETHOD_BITS); + else /* TypeDef */ + return get_typedef (m, dor_token >> MONO_TYPEORMETHOD_BITS); +} + +/** + * get_encoded_typedef_or_ref: + * @m: metadata context + * @ptr: location to decode from. + * @result: pointer to string where resulting decoded string is stored + * + * result will point to a g_malloc()ed string. + * + * Returns: the new ptr to continue decoding + */ +const char * +get_encoded_typedef_or_ref (MonoImage *m, const char *ptr, char **result) +{ + guint32 token; + + token = mono_metadata_decode_value (ptr, &ptr); + + *result = get_typedef_or_ref (m, token, NULL); + + return ptr; +} + +/** + * get_custom_mod: + * + * Decodes a CustomMod (22.2.7) + * + * Returns: updated pointer location + */ +const char * +get_custom_mod (MonoImage *m, const char *ptr, char **return_value) +{ + char *s; + const char *mod; + + *return_value = NULL; + while ((*ptr == MONO_TYPE_CMOD_OPT) || + (*ptr == MONO_TYPE_CMOD_REQD)) { + mod = (*ptr == MONO_TYPE_CMOD_REQD) ? "modreq" : "modopt"; + ptr++; + ptr = get_encoded_typedef_or_ref (m, ptr, &s); + + if (*return_value == NULL) + *return_value = g_strconcat (" ", mod, " (", s, ")", NULL); + else + *return_value = g_strconcat (*return_value, " ", mod, " (", s, ")", NULL); + g_free (s); + } + return ptr; +} + + +static dis_map_t element_type_map [] = { + { MONO_TYPE_END , "end" }, + { MONO_TYPE_VOID , "void" }, + { MONO_TYPE_BOOLEAN , "bool" }, + { MONO_TYPE_CHAR , "char" }, + { MONO_TYPE_I1 , "int8" }, + { MONO_TYPE_U1 , "unsigned int8" }, + { MONO_TYPE_I2 , "int16" }, + { MONO_TYPE_U2 , "unsigned int16" }, + { MONO_TYPE_I4 , "int32" }, + { MONO_TYPE_U4 , "unsigned int32" }, + { MONO_TYPE_I8 , "int64" }, + { MONO_TYPE_U8 , "unsigned int64" }, + { MONO_TYPE_R4 , "float32" }, + { MONO_TYPE_R8 , "float64" }, + { MONO_TYPE_STRING , "string" }, + { MONO_TYPE_TYPEDBYREF , "typedref" }, + { MONO_TYPE_I , "native int" }, + { MONO_TYPE_U , "native unsigned int" }, + { MONO_TYPE_OBJECT , "object" }, + { 0, NULL } +}; + +static dis_map_t call_conv_type_map [] = { + { MONO_CALL_DEFAULT , "default" }, + { MONO_CALL_C , "unmanaged cdecl" }, + { MONO_CALL_STDCALL , "unmanaged stdcall" }, + { MONO_CALL_THISCALL , "unmanaged thiscall" }, + { MONO_CALL_FASTCALL , "unmanaged fastcall" }, + { MONO_CALL_VARARG , "vararg" }, + { 0, NULL } +}; + +char* +dis_stringify_token (MonoImage *m, guint32 token) +{ + guint idx = token & 0xffffff; + switch (token >> 24) { + case MONO_TABLE_TYPEDEF: return get_typedef (m, idx); + case MONO_TABLE_TYPEREF: return get_typeref (m, idx); + case MONO_TABLE_TYPESPEC: return get_typespec (m, idx, FALSE, NULL); + default: + break; + } + return g_strdup_printf("0x%08x", token); +} + +char* +dis_stringify_array (MonoImage *m, MonoArrayType *array, gboolean is_def) +{ + char *type, *arr_str, *ret; + + type = dis_stringify_type (m, &array->eklass->byval_arg, is_def); + arr_str = stringify_array (array->rank, array->numsizes, array->numlobounds, array->sizes, array->lobounds); + + ret = g_strconcat (type, arr_str, NULL); + + g_free (arr_str); + g_free (type); + return ret; +} + +char* +dis_stringify_modifiers (MonoImage *m, int n, MonoCustomMod *mod) +{ + GString *s = g_string_new(""); + char *result; + int i; + for (i = 0; i < n; ++i) { + char *tok = dis_stringify_token (m, mod[i].token); + if (i > 0) + g_string_append_printf (s, " "); + g_string_append_printf (s, " %s (%s)", mod[i].required ? "modreq": "modopt", tok); + g_free (tok); + } + g_string_append_c (s, ' '); + result = s->str; + g_string_free (s, FALSE); + return result; +} + +char* +dis_stringify_param (MonoImage *m, MonoType *param) +{ + char *t; + char *result; + char *attribs; + const char *in = param->attrs & PARAM_ATTRIBUTE_IN ? "[in]" : ""; + const char *out = param->attrs & PARAM_ATTRIBUTE_OUT ? "[out]": ""; + const char *opt = param->attrs & PARAM_ATTRIBUTE_OPTIONAL ? "[opt]": ""; + attribs = g_strconcat(in, out, opt, NULL); + t = dis_stringify_type (m, param, TRUE); + result = g_strjoin(attribs[0] ? " ":"", attribs, t, NULL); + g_free (t); + g_free (attribs); + return result; +} + +static char* +dis_stringify_variant_type (MonoMarshalVariant variant) +{ + switch (variant) { + case MONO_VARIANT_EMPTY: + return g_strdup (""); + case MONO_VARIANT_NULL: + return g_strdup ("null"); + case MONO_VARIANT_I2: + return g_strdup ("int16"); + case MONO_VARIANT_I4: + return g_strdup ("int32"); + case MONO_VARIANT_R4: + return g_strdup ("float32"); + case MONO_VARIANT_R8: + return g_strdup ("float64"); + case MONO_VARIANT_CY: + return g_strdup ("currency"); + case MONO_VARIANT_DATE: + return g_strdup ("date"); + case MONO_VARIANT_BSTR: + return g_strdup ("bstr"); + case MONO_VARIANT_DISPATCH: + return g_strdup ("idispatch"); + case MONO_VARIANT_ERROR: + return g_strdup ("error"); + case MONO_VARIANT_BOOL: + return g_strdup ("bool"); + case MONO_VARIANT_VARIANT: + return g_strdup ("variant"); + case MONO_VARIANT_UNKNOWN: + return g_strdup ("iunknown"); + case MONO_VARIANT_DECIMAL: + return g_strdup ("decimal"); + case MONO_VARIANT_I1: + return g_strdup ("int8"); + case MONO_VARIANT_UI1: + return g_strdup ("unsigned int8"); + case MONO_VARIANT_UI2: + return g_strdup ("unsigned int16"); + case MONO_VARIANT_UI4: + return g_strdup ("unsigned int32"); + case MONO_VARIANT_I8: + return g_strdup ("int64"); + case MONO_VARIANT_UI8: + return g_strdup ("unsigned int64"); + case MONO_VARIANT_INT: + return g_strdup ("int"); + case MONO_VARIANT_UINT: + return g_strdup ("unsigned int"); + case MONO_VARIANT_VOID: + return g_strdup ("void"); + case MONO_VARIANT_HRESULT: + return g_strdup ("hresult"); + case MONO_VARIANT_PTR: + return g_strdup ("*"); + case MONO_VARIANT_SAFEARRAY: + return g_strdup ("safearray"); + case MONO_VARIANT_CARRAY: + return g_strdup ("carray"); + case MONO_VARIANT_USERDEFINED: + return g_strdup ("userdefined"); + case MONO_VARIANT_LPSTR: + return g_strdup ("lpstr"); + case MONO_VARIANT_LPWSTR: + return g_strdup ("lpwstr"); + case MONO_VARIANT_RECORD: + return g_strdup ("record"); + case MONO_VARIANT_FILETIME: + return g_strdup ("filetime"); + case MONO_VARIANT_BLOB: + return g_strdup ("blob"); + case MONO_VARIANT_STREAM: + return g_strdup ("stream"); + case MONO_VARIANT_STORAGE: + return g_strdup ("storage"); + case MONO_VARIANT_STREAMED_OBJECT: + return g_strdup ("streamed_object"); + case MONO_VARIANT_STORED_OBJECT: + return g_strdup ("stored_object"); + case MONO_VARIANT_BLOB_OBJECT: + return g_strdup ("blob_object"); + case MONO_VARIANT_CF: + return g_strdup ("cf"); + case MONO_VARIANT_CLSID: + return g_strdup ("clsid"); + case MONO_VARIANT_VECTOR: + /* FIXME: output: vector */ + return g_strdup ("vector"); + case MONO_VARIANT_ARRAY: + /* FIXME: output: [ ] */ + return g_strdup ("[]"); + case MONO_VARIANT_BYREF: + /* FIXME: output: & */ + return g_strdup ("&"); + default: + return g_strdup ("unknown"); + } +} + +static char* +dis_stringify_native_type (MonoMarshalNative native) +{ + switch (native) { + case MONO_NATIVE_BOOLEAN: + return g_strdup ("bool"); + case MONO_NATIVE_I1: + return g_strdup ("int8"); + case MONO_NATIVE_U1: + return g_strdup ("unsigned int8"); + case MONO_NATIVE_I2: + return g_strdup ("int16"); + case MONO_NATIVE_U2: + return g_strdup ("unsigned int16"); + case MONO_NATIVE_I4: + return g_strdup ("int32"); + case MONO_NATIVE_U4: + return g_strdup ("unsigned int32"); + case MONO_NATIVE_I8: + return g_strdup ("int64"); + case MONO_NATIVE_U8: + return g_strdup ("unsigned int64"); + case MONO_NATIVE_R4: + return g_strdup ("float32"); + case MONO_NATIVE_R8: + return g_strdup ("float64"); + case MONO_NATIVE_CURRENCY: + return g_strdup ("currency"); + case MONO_NATIVE_BSTR: + return g_strdup ("bstr"); + case MONO_NATIVE_LPSTR: + return g_strdup ("lpstr"); + case MONO_NATIVE_LPWSTR: + return g_strdup ("lpwstr"); + case MONO_NATIVE_LPTSTR: + return g_strdup ("lptstr"); + case MONO_NATIVE_IUNKNOWN: + return g_strdup ("iunknown"); + case MONO_NATIVE_IDISPATCH: + return g_strdup ("idispatch"); + case MONO_NATIVE_STRUCT: + return g_strdup ("struct"); + case MONO_NATIVE_INTERFACE: + return g_strdup ("interface"); + case MONO_NATIVE_SAFEARRAY: + return g_strdup ("safearray"); + case MONO_NATIVE_INT: + return g_strdup ("int"); + case MONO_NATIVE_UINT: + return g_strdup ("unsigned int"); + case MONO_NATIVE_VBBYREFSTR: + return g_strdup ("vbbyrefstr"); + case MONO_NATIVE_ANSIBSTR: + return g_strdup ("ansi bstr"); + case MONO_NATIVE_TBSTR: + return g_strdup ("tbstr"); + case MONO_NATIVE_VARIANTBOOL: + return g_strdup ("variant bool"); + case MONO_NATIVE_FUNC: + return g_strdup ("method"); + case MONO_NATIVE_ASANY: + return g_strdup ("as any"); + case MONO_NATIVE_LPSTRUCT: + return g_strdup ("lpstruct"); + case MONO_NATIVE_CUSTOM: + return g_strdup ("custom"); + case MONO_NATIVE_ERROR: + return g_strdup ("error"); + case MONO_NATIVE_MAX: + return g_strdup (""); + default: + return g_strdup ("unknown"); + } +} + +char* +dis_stringify_marshal_spec (MonoMarshalSpec *spec) +{ + switch (spec->native) { + case MONO_NATIVE_BYVALTSTR: + return g_strdup_printf (" marshal (fixed sysstring [%d])", spec->data.array_data.num_elem); + case MONO_NATIVE_BYVALARRAY: + return g_strdup_printf (" marshal (fixed array [%d])", spec->data.array_data.num_elem); + case MONO_NATIVE_LPARRAY: { + char *elem_type, *elems, *ret; + guint32 num_elem = spec->data.array_data.num_elem; + guint32 param_num = spec->data.array_data.param_num; + + elem_type = dis_stringify_native_type (spec->data.array_data.elem_type); + if (num_elem == -1 && param_num == -1) + elems = g_strdup (""); + else if ((param_num == -1) || (spec->data.array_data.elem_mult == 0)) + elems = g_strdup_printf ("%d", num_elem); + else if ((num_elem == -1) || (num_elem == 0)) + elems = g_strdup_printf ("+ %d", param_num); + else + elems = g_strdup_printf ("%d + %d", num_elem, param_num); + + ret = g_strdup_printf (" marshal (%s[%s])", elem_type, elems); + g_free (elem_type); + g_free (elems); + return ret; + } + case MONO_NATIVE_SAFEARRAY: { + char *elem_type = NULL, *ret; + + if (spec->data.safearray_data.elem_type != 0) + elem_type = dis_stringify_variant_type (spec->data.safearray_data.elem_type); + ret = g_strdup_printf (" marshal (safearray %s)", elem_type ? elem_type : ""); + + g_free (elem_type); + return ret; + } + case MONO_NATIVE_CUSTOM: + return g_strdup_printf (" marshal (custom (\"%s\", \"%s\"))", + spec->data.custom_data.custom_name ? spec->data.custom_data.custom_name : "", + spec->data.custom_data.cookie ? spec->data.custom_data.cookie : ""); + default: { + char *native_type, *ret; + native_type = dis_stringify_native_type (spec->native); + ret = g_strdup_printf (" marshal (%s)", native_type); + g_free (native_type); + return ret; + } + } +} + +/** + * get_generic_param + * @m: metadata context + * @table_type: The type of table we are getting generics for (0 for typedef, 1 for method) + * @row: The row in the table + * + * Returns: Allocated stringified generic parameters + */ +char* +get_generic_param (MonoImage *m, MonoGenericContainer *container) +{ + GString *result; + char *retval; + int i; + + if (!container) + return NULL; + + result = g_string_new (""); + + g_string_append_c (result, '<'); + for (i = 0; i < container->type_argc; i++) { + MonoGenericParam *param = mono_generic_container_get_param (container, i); + MonoGenericParamInfo *param_info = mono_generic_param_info (param); + MonoClass **constr; + int first = 1; + guint16 flags; + char *esname; + + if (i > 0) + g_string_append (result, ","); + + flags = param_info->flags & GENERIC_PARAMETER_ATTRIBUTE_VARIANCE_MASK; + if ((flags & GENERIC_PARAMETER_ATTRIBUTE_COVARIANT) == GENERIC_PARAMETER_ATTRIBUTE_COVARIANT) + g_string_append (result, "+ "); + if ((flags & GENERIC_PARAMETER_ATTRIBUTE_CONTRAVARIANT) == GENERIC_PARAMETER_ATTRIBUTE_CONTRAVARIANT) + g_string_append (result, "- "); + + flags = param_info->flags & GENERIC_PARAMETER_ATTRIBUTE_SPECIAL_CONSTRAINTS_MASK; + if ((flags & GENERIC_PARAMETER_ATTRIBUTE_REFERENCE_TYPE_CONSTRAINT) == GENERIC_PARAMETER_ATTRIBUTE_REFERENCE_TYPE_CONSTRAINT) + g_string_append (result, "class "); + if ((flags & GENERIC_PARAMETER_ATTRIBUTE_VALUE_TYPE_CONSTRAINT) == GENERIC_PARAMETER_ATTRIBUTE_VALUE_TYPE_CONSTRAINT) + g_string_append (result, "valuetype "); + if ((flags & GENERIC_PARAMETER_ATTRIBUTE_CONSTRUCTOR_CONSTRAINT) == GENERIC_PARAMETER_ATTRIBUTE_CONSTRUCTOR_CONSTRAINT) + g_string_append (result, ".ctor "); + + for (constr = param_info->constraints; constr && *constr; constr++) { + char *sig; + + if (first) { + g_string_append_c (result, '('); + first = 0; + } else + g_string_append (result, ", "); + sig = dis_stringify_type (m, &((*constr)->byval_arg), TRUE); + g_string_append (result, sig); + g_free (sig); + } + + if (!first) + g_string_append (result, ") "); + + esname = get_escaped_name (mono_generic_param_info (param)->name); + g_string_append (result, esname); + g_free (esname); + } + + g_string_append_c (result, '>'); + + retval = result->str; + g_string_free (result, FALSE); + return retval; +} + +char* +dis_stringify_method_signature (MonoImage *m, MonoMethodSignature *method, int methoddef_row, + MonoGenericContainer *container, gboolean fully_qualified) +{ + return dis_stringify_method_signature_full (m, method, methoddef_row, container, fully_qualified, TRUE); +} + +/* + * @m: metadata context + * @method: MonoMethodSignature to dis-stringify + * @methoddef_row: row index in the Method table + * @context: generic context, generic method's context in case of a Generic method + * or a generic type's context. if !@context, treats it as a non-generic method + * @fully_qualified: TRUE to print type name also. + * + * Returns: Allocated stringified method signature + */ +char* +dis_stringify_method_signature_full (MonoImage *m, MonoMethodSignature *method, int methoddef_row, + MonoGenericContainer *container, gboolean fully_qualified, gboolean with_marshal_info) +{ + guint32 cols [MONO_METHOD_SIZE]; + guint32 pcols [MONO_PARAM_SIZE]; + guint32 param_index = 0, next_param_index = 0; + gboolean has_param_row; + const char *method_name = ""; + int free_method = 0; + char *retval, *esname; + char *type = NULL; + char *marshal_info = NULL, *ret_marshal_info = NULL; + char *gen_param = NULL; + GString *result = g_string_new (""); + GString *result_ret = g_string_new (""); + int i, start; + + g_assert (method || methoddef_row); + + if (methoddef_row) { + mono_metadata_decode_row (&m->tables [MONO_TABLE_METHOD], methoddef_row -1, cols, MONO_METHOD_SIZE); + if (fully_qualified) { + guint32 type_idx = mono_metadata_typedef_from_method (m, methoddef_row); + if (type_idx) + type = get_typedef (m, type_idx); + else + type = g_strdup (""); + } + method_name = mono_metadata_string_heap (m, cols [MONO_METHOD_NAME]); + param_index = cols [MONO_METHOD_PARAMLIST]; + if (!method) { + MonoError error; + const char *sig = mono_metadata_blob_heap (m, cols [MONO_METHOD_SIGNATURE]); + + container = mono_metadata_load_generic_params (m, MONO_TOKEN_METHOD_DEF | methoddef_row, container); + if (container) { + mono_metadata_load_generic_param_constraints_checked (m, MONO_TOKEN_METHOD_DEF | methoddef_row, container, &error); + g_assert (mono_error_ok (&error)); /*FIXME don't swallow the error message*/ + } + + mono_metadata_decode_blob_size (sig, &sig); + method = mono_metadata_parse_method_signature_full (m, container, methoddef_row, sig, &sig, &error); + g_assert (mono_error_ok (&error)); /*FIXME don't swallow the error message*/ + free_method = 1; + } + + if (container && container->is_method) + gen_param = get_generic_param (m, container); + + if (methoddef_row < m->tables [MONO_TABLE_METHOD].rows) { + mono_metadata_decode_row (&m->tables [MONO_TABLE_METHOD], methoddef_row, cols, MONO_METHOD_SIZE); + next_param_index = cols [MONO_METHOD_PARAMLIST]; + } else { + next_param_index = m->tables [MONO_TABLE_PARAM].rows + 1; + } + } + + start = method->hasthis ? 0 : 1; + for (i = 0; i < method->param_count + 1; ++i) { + marshal_info = NULL; + has_param_row = param_index && param_index < next_param_index; + esname = NULL; + + if (method->param_count == 0 && !has_param_row) + /* method has zero parameters, and no row for return val in the PARAM table */ + continue; + + if (has_param_row) + mono_metadata_decode_row (&m->tables [MONO_TABLE_PARAM], param_index - 1, pcols, MONO_PARAM_SIZE); + + if (has_param_row && i == pcols [MONO_PARAM_SEQUENCE]) { + if (i) + esname = get_escaped_name ( + mono_metadata_string_heap (m, pcols [MONO_PARAM_NAME])); + + if (with_marshal_info && (pcols [MONO_PARAM_FLAGS] & PARAM_ATTRIBUTE_HAS_FIELD_MARSHAL)) { + const char *tp; + MonoMarshalSpec *spec; + tp = mono_metadata_get_marshal_info (m, param_index - 1, FALSE); + if (tp) { + spec = mono_metadata_parse_marshal_spec (m, tp); + + if (i) + marshal_info = dis_stringify_marshal_spec (spec); + else + ret_marshal_info = dis_stringify_marshal_spec (spec); + } else { + if (i) + marshal_info = g_strdup ("(missing)"); + else + ret_marshal_info = g_strdup ("(missing)"); + } + } + param_index ++; + } else { + if (i) + /* A_[0-9]* does not require escaping */ + esname = g_strdup_printf ("A_%i", i - start); + } + + if (!i) + continue; + + if (i > 1) + g_string_append (result, ", "); + + retval = dis_stringify_param (m, method->params [i - 1]); + + g_string_append_printf (result, "%s%s %s", retval, marshal_info ? marshal_info : "", esname); + g_free (retval); + g_free (esname); + g_free (marshal_info); + } + g_string_append (result, ") "); + + retval = dis_stringify_param (m, method->ret); + + if (method->hasthis) + g_string_append (result_ret, "instance "); + g_string_append (result_ret, map (method->call_convention, call_conv_type_map)); + g_string_append_printf (result_ret, " %s%s ", retval, ret_marshal_info ? ret_marshal_info :""); + g_free (ret_marshal_info); + if (type) { + char *estype = get_escaped_name (type); + g_string_append_printf (result_ret, "%s::", estype); + g_free (estype); + g_free (type); + } + esname = get_escaped_name (method_name); + g_string_append (result_ret, esname); + g_free (esname); + if (gen_param) { + g_string_append (result_ret, gen_param); + g_free (gen_param); + } + g_string_append (result_ret, " ("); + g_free (retval); + + g_string_prepend (result, result_ret->str); + g_string_free (result_ret, FALSE); + + if (show_method_tokens && methoddef_row) + g_string_append_printf (result, " /* 0x%X */ ", + (methoddef_row >> MONO_TYPEORMETHOD_BITS) | MONO_TOKEN_METHOD_DEF); + + if (free_method) + mono_metadata_free_method_signature (method); + retval = result->str; + g_string_free (result, FALSE); + + return retval; +} + +char* +dis_stringify_function_ptr (MonoImage *m, MonoMethodSignature *method) +{ + char *retval; + GString *result = g_string_new (""); + int i; + + g_assert (method); + + g_string_append (result, map (method->call_convention, call_conv_type_map)); + + retval = dis_stringify_param (m, method->ret); + g_string_append_printf (result, " %s ", retval); + g_free (retval); + + g_string_append (result, " *("); + for (i = 0; i < method->param_count; ++i) { + if (i) + g_string_append (result, ", "); + retval = dis_stringify_param (m, method->params [i]); + g_string_append (result, retval); + g_free (retval); + } + g_string_append (result, ") "); + + retval = result->str; + g_string_free (result, FALSE); + + return retval; +} + +static char * +get_escaped_class_name (MonoClass *c) +{ + char *result, *esname; + + if (c->type_token == mono_metadata_make_token (MONO_TABLE_TYPEDEF, 1)) + /* */ + return NULL; + + if (c->rank || c->byval_arg.type == MONO_TYPE_PTR) + g_assert (0); + + esname = get_escaped_name (c->name); + + if (c->nested_in){ + char *part_a = get_escaped_class_name (c->nested_in); + + result = g_strdup_printf ("%s/%s", part_a, esname); + g_free (part_a); + } else if (*c->name_space) + result = g_strdup_printf ("%s.%s", c->name_space, esname); + else + result = g_strdup (esname); + + g_free (esname); + return result; +} + +char * +dis_stringify_object_with_class (MonoImage *m, MonoClass *c, gboolean prefix, gboolean is_def) +{ + /* FIXME: handle MONO_TYPE_OBJECT ... */ + MonoType *type = &c->byval_arg; + const char *otype = type->type == MONO_TYPE_VALUETYPE ? "valuetype " : "class " ; + char *assemblyref = NULL, *result, *esname, *generic = NULL; + + if (c->type_token == mono_metadata_make_token (MONO_TABLE_TYPEDEF, 1)) + /* */ + return NULL; + + if (m != c->image) { + if (c->image->assembly_name) { + /* we cheat */ + if (substitute_with_mscorlib_p && !strcmp ("corlib", c->image->assembly_name)) + assemblyref = g_strdup_printf ("[%s]", "mscorlib"); + else { + char *esc = get_escaped_name (c->image->assembly->aname.name); + + assemblyref = g_strdup_printf ("[%s]", esc); + g_free (esc); + } + } else { + assemblyref = g_strdup_printf ("[.module %s]", c->image->module_name); + } + } + + esname = get_escaped_class_name (c); + + if (mono_class_is_ginst (c)) { + MonoGenericClass *gclass = mono_class_get_generic_class (c); + MonoGenericInst *inst = gclass->context.class_inst; + GString *str = g_string_new (""); + int i; + + for (i = 0; i < inst->type_argc; i++){ + char *t = dis_stringify_type (m, inst->type_argv [i], is_def); + + g_string_append (str, t); + if (i+1 != inst->type_argc) + g_string_append (str, ", "); + g_free (t); + } + generic = g_strdup_printf ("<%s>", str->str); + g_string_free (str, TRUE); + } + + + result = g_strdup_printf ("%s%s%s%s", prefix ? otype : "", assemblyref?assemblyref:"", + esname, generic?generic:""); + + g_free (generic); + g_free (assemblyref); + g_free (esname); + + return result; +} + +static char * +dis_stringify_object (MonoImage *m, MonoType *type, gboolean is_def) +{ + MonoClass *c = mono_class_from_mono_type (type); + return dis_stringify_object_with_class (m, c, TRUE, is_def); +} + +char* +dis_stringify_type (MonoImage *m, MonoType *type, gboolean is_def) +{ + const char *pinned = "", *byref = ""; + char *bare = NULL, *mods = NULL; + char *result; + + if (type->num_mods) + mods = dis_stringify_modifiers (m, type->num_mods, type->modifiers); + + switch (type->type){ + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_CHAR: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_STRING: + case MONO_TYPE_OBJECT: + case MONO_TYPE_TYPEDBYREF: + bare = g_strdup (map (type->type, element_type_map)); + break; + + case MONO_TYPE_VALUETYPE: + case MONO_TYPE_CLASS: + bare = dis_stringify_object (m, type, is_def); + break; + case MONO_TYPE_FNPTR: { + char *child_type; + child_type = dis_stringify_function_ptr (m, type->data.method); + bare = g_strdup_printf ("method %s", child_type); + g_free (child_type); + break; + } + case MONO_TYPE_PTR: { + char *child_type; + child_type = dis_stringify_type (m, type->data.type, is_def); + + bare = g_strdup_printf ("%s*", child_type); + g_free (child_type); + break; + } + case MONO_TYPE_SZARRAY: { + char *child_type; + child_type = dis_stringify_type (m, &type->data.klass->byval_arg, is_def); + + bare = g_strdup_printf ("%s[]", child_type); + g_free (child_type); + break; + } + case MONO_TYPE_ARRAY: + bare = dis_stringify_array (m, type->data.array, is_def); + break; + case MONO_TYPE_VOID: + bare = g_strdup ("void"); + break; + case MONO_TYPE_MVAR: + if (is_def && !cant_print_generic_param_name (type->data.generic_param)) + bare = g_strdup_printf ("!!%s", get_escaped_name (mono_generic_param_info (type->data.generic_param)->name)); + else + bare = g_strdup_printf ("!!%d", mono_type_get_generic_param_num (type)); + break; + case MONO_TYPE_VAR: + if (is_def && !cant_print_generic_param_name (type->data.generic_param)) + bare = g_strdup_printf ("!%s", get_escaped_name (mono_generic_param_info (type->data.generic_param)->name)); + else + bare = g_strdup_printf ("!%d", mono_type_get_generic_param_num (type)); + break; + case MONO_TYPE_GENERICINST: { + GString *str = g_string_new (""); + MonoGenericInst *inst; + int i; + char *generic_type = dis_stringify_type ( + m, &type->data.generic_class->container_class->byval_arg, is_def); + inst = type->data.generic_class->context.class_inst; + for (i = 0; i < inst->type_argc; i++){ + char *t = dis_stringify_type (m, inst->type_argv [i], is_def); + + g_string_append (str, t); + if (i+1 != inst->type_argc) + g_string_append (str, ", "); + g_free (t); + } + bare = g_strdup_printf ("%s<%s>", generic_type, str->str); + g_string_free (str, TRUE); + break; + } + + default: + g_error ("Do not know how to stringify type 0x%x", type->type); + } + + if (type->pinned) + pinned = " pinned"; + + if (type->byref) + byref = "&"; + + if (!bare) + /* bare is NULL, for */ + return bare; + + result = g_strconcat (bare, byref, pinned, mods ? mods : "", NULL); + + g_free (bare); + + return result; +} + +/** + * get_type: + * @m: metadata context + * @ptr: location to decode from. + * @result: pointer to string where resulting decoded string is stored + * + * This routine returs in @result the stringified type pointed by @ptr. + * (22.2.12) + * + * Returns: the new ptr to continue decoding + */ +const char * +get_type (MonoImage *m, const char *ptr, char **result, gboolean is_def, MonoGenericContainer *container) +{ + MonoError error; + const char *start = ptr; + guint32 type; + MonoType *t; + + if (*ptr == MONO_TYPE_BYREF) + ++ptr; + + type = mono_metadata_decode_value (ptr, &ptr); + + switch (type){ + case MONO_TYPE_VALUETYPE: + case MONO_TYPE_CLASS: { + guint32 token = mono_metadata_parse_typedef_or_ref (m, ptr, &ptr); + MonoClass *klass = mono_class_get_checked (m, token, &error); + char *temp; + if (klass) { + temp = dis_stringify_object_with_class (m, klass, TRUE, FALSE); + } else { + temp = g_strdup_printf ("", token, mono_error_get_message (&error)); + mono_error_cleanup (&error); + } + + if (show_tokens) { + *result = get_token_comment (temp, token); + g_free (temp); + } else + *result = temp; + break; + } + + case MONO_TYPE_GENERICINST: { + GString *str = g_string_new (""); + int count, i; + char *temp; + + ptr = get_type (m, ptr, &temp, is_def, container); + g_string_append (str, temp); + g_free (temp); + + count = mono_metadata_decode_value (ptr, &ptr); + g_string_append (str, "<"); + + for (i = 0; i < count; i++) { + if (i) + g_string_append (str, ","); + ptr = get_type (m, ptr, &temp, is_def, container); + g_string_append (str, temp); + } + + g_string_append (str, ">"); + *result = str->str; + g_string_free (str, FALSE); + break; + } + + default: + t = mono_metadata_parse_type_checked (m, container, 0, FALSE, start, &ptr, &error); + if (t) { + *result = dis_stringify_type (m, t, is_def); + } else { + *result = g_strdup_printf ("Invalid type due to %s", mono_error_get_message (&error)); + mono_error_cleanup (&error); + } + + break; + } + + return ptr; +} + +/** + * + * Returns a stringified representation of a FieldSig (22.2.4) + */ +char * +get_field_signature (MonoImage *m, guint32 blob_signature, MonoGenericContainer *container) +{ + char *allocated_modifier_string, *allocated_type_string; + const char *ptr = mono_metadata_blob_heap (m, blob_signature); + char *res; + int len; + + len = mono_metadata_decode_value (ptr, &ptr); + /* FIELD is 0x06 */ + g_assert (*ptr == 0x06); +/* hex_dump (ptr, 0, len); */ + ptr++; len--; + + ptr = get_custom_mod (m, ptr, &allocated_modifier_string); + ptr = get_type (m, ptr, &allocated_type_string, FALSE, container); + + res = g_strdup_printf ( + "%s%s", + allocated_type_string, + allocated_modifier_string ? allocated_modifier_string : ""); + + if (allocated_modifier_string) + g_free (allocated_modifier_string); + if (allocated_type_string) + g_free (allocated_type_string); + + return res; +} + +MonoTypeEnum +get_field_literal_type (MonoImage *m, guint32 blob_signature) +{ + const char *ptr = mono_metadata_blob_heap (m, blob_signature); + int len; + char *allocated_modifier_string; + + len = mono_metadata_decode_value (ptr, &ptr); + + /* FIELD is 0x06 */ + g_assert (*ptr == 0x06); + ptr++; len--; + + ptr = get_custom_mod (m, ptr, &allocated_modifier_string); + if (allocated_modifier_string) + g_free (allocated_modifier_string); + + return (MonoTypeEnum) *ptr; + +} + +/** + * decode_literal: + * @m: metadata context + * @token: token to decode + * + * decodes the literal indexed by @token. + */ +char * +decode_literal (MonoImage *m, guint32 token) +{ + return g_strdup ("LITERAL_VALUE"); +} + +/** + * get_ret_type: + * @m: metadata context + * @ptr: location to decode from. + * @result: pointer to string where resulting decoded string is stored + * + * This routine returns in @result the stringified RetType (22.2.11) + * + * Returns: the new ptr to continue decoding. + */ +const char * +get_ret_type (MonoImage *m, const char *ptr, char **ret_type, MonoGenericContainer *container) +{ + GString *str = g_string_new (""); + char *mod = NULL; + char *allocated_type_string; + int has_byref = 0; + + ptr = get_custom_mod (m, ptr, &mod); + + if (*ptr == MONO_TYPE_TYPEDBYREF){ + g_string_append (str, "typedref"); + ptr++; + } else if (*ptr == MONO_TYPE_VOID){ + g_string_append (str, "void"); + ptr++; + } else { + if (*ptr == MONO_TYPE_BYREF){ + has_byref = 1; + ptr++; + } + + ptr = get_type (m, ptr, &allocated_type_string, FALSE, container); + g_string_append (str, allocated_type_string); + if (has_byref) + g_string_append (str, "& "); + g_free (allocated_type_string); + } + + if (mod){ + g_string_append (str, mod); + g_string_append_c (str, ' '); + g_free (mod); + } + + *ret_type = str->str; + g_string_free (str, FALSE); + + return ptr; +} + +/** + * get_param: + * @m: metadata context + * @ptr: location to decode from. + * @result: pointer to string where resulting decoded string is stored + * + * This routine returns in @result the stringified Param (22.2.10) + * + * Returns: the new ptr to continue decoding. + */ +const char * +get_param (MonoImage *m, const char *ptr, char **retval, MonoGenericContainer *container) +{ + GString *str = g_string_new (""); + char *allocated_mod_string, *allocated_type_string; + + ptr = get_custom_mod (m, ptr, &allocated_mod_string); + + if (*ptr == MONO_TYPE_TYPEDBYREF){ + g_string_append (str, " typedref "); + ptr++; + } else { + gboolean by_ref = 0; + if (*ptr == MONO_TYPE_BYREF){ + g_string_append (str, "[out] "); + ptr++; + by_ref = 1; + } + ptr = get_type (m, ptr, &allocated_type_string, FALSE, container); + g_string_append (str, allocated_type_string); + if (by_ref) + g_string_append_c (str, '&'); + g_free (allocated_type_string); + } + + if (allocated_mod_string){ + g_string_append (str, allocated_mod_string); + g_string_append_c (str, ' '); + g_free (allocated_mod_string); + } + + *retval = str->str; + g_string_free (str, FALSE); + return ptr; +} + +/** + * str_escape + * + * @str: string to process + * @list: list of chars to escape + * + * Returns: an allocated escaped string. + */ +static char* +str_escape (const char *str, const char *list) +{ + const char *p = str; + GString *res; + + res = g_string_sized_new (strlen (str)); + + for (;;) { + while (*p && !strchr (list, *p)) + ++p; + g_string_append_len (res, str, p - str); + if (!*p) + break; + g_string_append_c (res, '\\'); + str = p; + ++p; + } + + return g_string_free (res, FALSE); +} + +/** + * get_escaped_name + * + * Returns: An allocated escaped name. A name needs to be escaped + * because it might be an ilasm keyword. + */ +char* +get_escaped_name (const char *name) +{ + const char *s; + char *ret, *esc; + + if (!name) + return NULL; + + g_assert (key_table); + + if (strlen (name) == 0) + return g_strdup (name); + + for (s = name; *s; s++) { + char *first, *result; + + if (*s != '/') + continue; + + first = g_strndup (name, s-name); + result = g_strdup_printf ("%s/%s", get_escaped_name (first), get_escaped_name (s+1)); + g_free (first); + + return result; + } + + for (s = name; *s; s++) { + if (isalnum (*s) || *s == '_' || *s == '$' || *s == '@' || + *s == '?' || (*s == '.' && s != name) || *s == 0 || *s == '!' || *s == '`') + continue; + + esc = str_escape (name, "'\\"); + ret = g_strdup_printf ("'%s'", esc); + g_free (esc); + return ret; + } + + if (g_hash_table_lookup (key_table, name)) + return g_strdup_printf ("'%s'", name); + + return str_escape (name, "'\\"); +} + +static dis_map_t param_map [] = { + { PARAM_ATTRIBUTE_IN, "[in] " }, + { PARAM_ATTRIBUTE_OUT, "[out] " }, + { PARAM_ATTRIBUTE_OPTIONAL, "optional " }, + { PARAM_ATTRIBUTE_HAS_DEFAULT, "hasdefault " }, + { PARAM_ATTRIBUTE_HAS_FIELD_MARSHAL, "fieldmarshal " }, + { 0, NULL } +}; + +char * +param_flags (guint32 f) +{ + return g_strdup (flags (f, param_map)); +} + +static dis_map_t field_access_map [] = { + { FIELD_ATTRIBUTE_COMPILER_CONTROLLED, "privatescope " }, + { FIELD_ATTRIBUTE_PRIVATE, "private " }, + { FIELD_ATTRIBUTE_FAM_AND_ASSEM, "famandassem " }, + { FIELD_ATTRIBUTE_ASSEMBLY, "assembly " }, + { FIELD_ATTRIBUTE_FAMILY, "family " }, + { FIELD_ATTRIBUTE_FAM_OR_ASSEM, "famorassem " }, + { FIELD_ATTRIBUTE_PUBLIC, "public " }, + { 0, NULL } +}; + +static dis_map_t field_flags_map [] = { + { FIELD_ATTRIBUTE_STATIC, "static " }, + { FIELD_ATTRIBUTE_INIT_ONLY, "initonly " }, + { FIELD_ATTRIBUTE_LITERAL, "literal " }, + { FIELD_ATTRIBUTE_NOT_SERIALIZED, "notserialized " }, + { FIELD_ATTRIBUTE_SPECIAL_NAME, "specialname " }, + { FIELD_ATTRIBUTE_PINVOKE_IMPL, "FIXME:pinvokeimpl " }, + { FIELD_ATTRIBUTE_RT_SPECIAL_NAME, "rtspecialname " }, + + /* This is set when a MarshalAs attribute is seen. FIXME: round-trip? */ + { FIELD_ATTRIBUTE_HAS_FIELD_MARSHAL, "" }, + + /* This seems to be set if LITERAL is set. FIXME: round-trip? */ + { FIELD_ATTRIBUTE_HAS_DEFAULT, "" }, + + /* This seems to be set on compiler-generated array initializer fields. FIXME: round-trip? */ + { FIELD_ATTRIBUTE_HAS_FIELD_RVA, "" }, + { 0, NULL } +}; + +/** + * field_flags: + * + * Returns a stringified version of a Field's flags + */ +char * +field_flags (guint32 f) +{ + char buffer [1024]; + int access = f & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK; + int rest = f & ~access; + + buffer [0] = 0; + + strcat (buffer, map (access, field_access_map)); + strcat (buffer, flags (rest, field_flags_map)); + return g_strdup (buffer); +} + +/** + * Returns a stringifed representation of a MethodRefSig (22.2.2) + */ +char * +get_methodref_signature (MonoImage *m, guint32 blob_signature, const char *fancy_name) +{ + GString *res = g_string_new (""); + const char *ptr = mono_metadata_blob_heap (m, blob_signature); + char *allocated_ret_type, *s; + const char *cconv_str; + gboolean seen_vararg = 0; + int param_count; + int i, gen_count = 0; + int cconv; + + /* signature_len = */ mono_metadata_decode_value (ptr, &ptr); + + if (*ptr & 0x20){ + if (*ptr & 0x40) + g_string_append (res, "explicit-this "); + else + g_string_append (res, "instance "); /* has-this */ + } + + if (*ptr & 0x10) + gen_count = 1; + cconv = *ptr & 0x0f; + cconv_str = map (cconv, call_conv_type_map); + if (strcmp (cconv_str, "default") != 0) { + g_string_append (res, cconv_str); + g_string_append (res, " "); + } + + ptr++; + if (gen_count) + gen_count = mono_metadata_decode_value (ptr, &ptr); + param_count = mono_metadata_decode_value (ptr, &ptr); + if (cconv != 0xa) { + ptr = get_ret_type (m, ptr, &allocated_ret_type, NULL); + g_string_append (res, allocated_ret_type); + g_free (allocated_ret_type); + } + + if (fancy_name){ + g_string_append_c (res, ' '); + g_string_append (res, fancy_name); + } + + g_string_append (res, "("); + + /* + * param_count describes parameters *before* and *after* + * the vararg sentinel + */ + for (i = 0; i < param_count; i++){ + char *param = NULL; + + /* + * If ptr is a SENTINEL + */ + if (*ptr == 0x41){ + if (!seen_vararg){ + g_string_append (res, "..., "); + seen_vararg = 1; + } + + ptr++; + } + + ptr = get_param (m, ptr, ¶m, NULL); + g_string_append (res, param); + if (i+1 != param_count) + g_string_append (res, ", "); + g_free (param); + } + g_string_append (res, ")"); + + /* + * cleanup and return + */ + s = res->str; + g_string_free (res, FALSE); + return s; +} + +/** + * Returns a stringifed representation of a field ref + */ +char * +get_fieldref_signature (MonoImage *m, int idx, MonoGenericContainer *container) +{ + guint32 cols [MONO_MEMBERREF_SIZE]; + MonoGenericContainer *new_container; + char *type, *esname; + char *sig; + char *full_sig; + + mono_metadata_decode_row (&m->tables [MONO_TABLE_MEMBERREF], + idx - 1, cols, MONO_MEMBERREF_SIZE); + + new_container = get_memberref_container (m, cols [MONO_MEMBERREF_CLASS], container); + sig = get_field_signature (m, cols [MONO_MEMBERREF_SIGNATURE], new_container); + + type = get_memberref_parent (m, cols [MONO_MEMBERREF_CLASS], container); + esname = get_escaped_name (mono_metadata_string_heap (m, cols [MONO_MEMBERREF_NAME])); + + full_sig = g_strdup_printf ("%s %s%s%s", + sig, + type ? type : "", + type ? "::" : "", + esname); + g_free (sig); + g_free (type); + g_free (esname); + + return full_sig; +} + +/** + * get_token_comment: + * + * If show_tokens is TRUE, return "prefix""token(table)". + * If show_tokens is FALSE, return "prefix" or NULL if prefix is NULL. + * Caller is responsible for freeing. + */ +char * +get_token_comment (const char *prefix, guint32 token) +{ + if (!show_tokens) + return prefix ? g_strdup_printf ("%s", prefix) : NULL; + gint32 tableidx = mono_metadata_token_table (token); + if ((tableidx < 0) || (tableidx > MONO_TABLE_LAST)) + return g_strdup_printf ("%s/*%08x*/", prefix ? prefix : "", token); + else + return g_strdup_printf ("%s/*%08x(%s)*/", prefix ? prefix : "", token, mono_meta_table_name (tableidx)); +} + +/** + * get_field: + * @m: metadata context + * @token: a FIELD_DEF token + * + * This routine has to locate the TypeDef that "owns" this Field. + * Since there is no backpointer in the Field table, we have to scan + * the TypeDef table and locate the actual "owner" of the field + */ +char * +get_field (MonoImage *m, guint32 token, MonoGenericContainer *container) +{ + int idx = mono_metadata_token_index (token); + guint32 cols [MONO_FIELD_SIZE]; + char *sig, *res, *type, *estype, *esname, *token_comment; + guint32 type_idx; + + /* + * We can get here also with a MenberRef token (for a field + * defined in another module/assembly, just like in get_method () + */ + if (mono_metadata_token_code (token) == MONO_TOKEN_MEMBER_REF) { + return get_fieldref_signature (m, idx, container); + } + g_assert (mono_metadata_token_code (token) == MONO_TOKEN_FIELD_DEF); + + mono_metadata_decode_row (&m->tables [MONO_TABLE_FIELD], idx - 1, cols, MONO_FIELD_SIZE); + sig = get_field_signature (m, cols [MONO_FIELD_SIGNATURE], container); + + /* + * To locate the actual "container" for this field, we have to scan + * the TypeDef table. LAME! + */ + type_idx = mono_metadata_typedef_from_field (m, idx); + if (!type_idx) { + res = g_strdup_printf (" %s", sig); + g_free (sig); + return res; + } + + type = get_typedef (m, type_idx); + estype = get_escaped_name (type); + esname = get_escaped_name (mono_metadata_string_heap (m, cols [MONO_FIELD_NAME])); + token_comment = get_token_comment (NULL, token); + res = g_strdup_printf ("%s %s%s%s%s", + sig, + estype ? estype : "", + estype ? "::" : "", + esname, + token_comment ? token_comment : "" + ); + + g_free (type); + g_free (sig); + g_free (estype); + g_free (esname); + g_free (token_comment); + + return res; +} + +static MonoGenericContainer * +get_memberref_container (MonoImage *m, guint32 mrp_token, MonoGenericContainer *container) +{ + MonoClass *klass; + + /* + * mrp_index is a MemberRefParent coded index + */ + guint32 table = mrp_token & 7; + guint32 idx = mrp_token >> 3; + + switch (table){ + case 0: /* TypeDef */ + return mono_metadata_load_generic_params (m, MONO_TOKEN_TYPE_DEF | idx, NULL); + + case 1: /* TypeRef */ + return NULL; + + case 4: /* TypeSpec */ + klass = mono_class_get_full (m, MONO_TOKEN_TYPE_SPEC | idx, (MonoGenericContext *) container); + g_assert (klass); + return mono_class_is_ginst (klass) ? mono_class_get_generic_container (mono_class_get_generic_class (klass)->container_class) : NULL; + } + g_assert_not_reached (); + return NULL; +} + +static char * +get_memberref_parent (MonoImage *m, guint32 mrp_token, MonoGenericContainer *container) +{ + /* + * mrp_index is a MemberRefParent coded index + */ + guint32 table = mrp_token & 7; + guint32 idx = mrp_token >> 3; + + switch (table){ + case 0: /* TypeDef */ + return get_typedef (m, idx); + + case 1: /* TypeRef */ + return get_typeref (m, idx); + + case 2: /* ModuleRef */ + return g_strdup_printf ("TODO:MemberRefParent-ModuleRef"); + + case 3: /* MethodDef */ + return g_strdup ("TODO:MethodDef"); + + case 4: /* TypeSpec */ + return get_typespec (m, idx, FALSE, container); + } + g_assert_not_reached (); + return NULL; +} + +/** + * get_method: + * @m: metadata context + * @token: a METHOD_DEF or MEMBER_REF token + * + * This routine has to locate the TypeDef that "owns" this Field. + * Since there is no backpointer in the Field table, we have to scan + * the TypeDef table and locate the actual "owner" of the field + */ +static char * +get_method_core (MonoImage *m, guint32 token, gboolean fullsig, MonoGenericContainer *container) +{ + MonoError error; + int idx = mono_metadata_token_index (token); + guint32 member_cols [MONO_MEMBERREF_SIZE], method_cols [MONO_METHOD_SIZE]; + char *sig = NULL, *esname; + char *name; + + MonoMethod *mh; + MonoGenericContainer *type_container = container; + + mh = mono_get_method_checked (m, token, NULL, (MonoGenericContext *) container, &error); + if (mh) { + if (mono_method_signature (mh)->is_inflated) + container = mono_method_get_generic_container (((MonoMethodInflated *) mh)->declaring); + esname = get_escaped_name (mh->name); + sig = dis_stringify_type (m, &mh->klass->byval_arg, TRUE); + char *token_comment = get_token_comment (NULL, token); + name = g_strdup_printf ("%s%s%s%s", sig ? sig : "", token_comment ? token_comment : "", sig ? "::" : "", esname); + g_free (sig); + g_free (esname); + g_free (token_comment); + } else { + name = NULL; + mono_error_cleanup (&error); + } + + switch (mono_metadata_token_code (token)){ + case MONO_TOKEN_METHOD_DEF: + mono_metadata_decode_row (&m->tables [MONO_TABLE_METHOD], + idx - 1, method_cols, MONO_METHOD_SIZE); + + sig = get_methodref_signature (m, method_cols [MONO_METHOD_SIGNATURE], name); + break; + + case MONO_TOKEN_MEMBER_REF: { + mono_metadata_decode_row (&m->tables [MONO_TABLE_MEMBERREF], + idx - 1, member_cols, MONO_MEMBERREF_SIZE); + if (!name) { + char *parent = get_memberref_parent (m, member_cols [MONO_MEMBERREF_CLASS], container); + name = g_strdup_printf ("%s%s%s", + parent ? parent : "", + parent ? "::" : "", + mono_metadata_string_heap (m, member_cols [MONO_MEMBERREF_NAME])); + g_free (parent); + } + if (mh) { + int arity = 0; + + if (mh->is_generic) + arity = mono_method_get_generic_container (mh)->type_argc; + else + if (mh->is_inflated && ((MonoMethodInflated *)mh)->declaring->is_generic) + arity = mono_method_get_generic_container (((MonoMethodInflated*) mh)->declaring)->type_argc; + + if (arity > 0) { + char *str = g_strdup_printf ("%s <[%d]>", name, arity); + g_free (name); + name = str; + } + } + + sig = get_methodref_signature ( + m, member_cols [MONO_MEMBERREF_SIGNATURE], name); + break; + } + case MONO_TOKEN_METHOD_SPEC: { + mono_metadata_decode_row (&m->tables [MONO_TABLE_METHODSPEC], + idx - 1, member_cols, MONO_METHODSPEC_SIZE); + token = member_cols [MONO_METHODSPEC_METHOD]; + sig = get_methodspec (m, idx, token, name, type_container); + break; + } + + default: + g_assert_not_reached (); + } + + if (fullsig) + g_free (name); + else { + g_free (sig); + return name; + } + + if (show_tokens) { + char *retval = get_token_comment (sig, token); + g_free (sig); + return retval; + } else + return sig; +} + +char * +get_method (MonoImage *m, guint32 token, MonoGenericContainer *container) +{ + return get_method_core (m, token, TRUE, container); +} + +/** + * get_methoddef + * @m: metadata context + * @idx: index into the method table + * + * Returns: A stringified version of the method signature. + */ +char * +get_methoddef (MonoImage *m, guint32 idx) +{ + MonoError error; + guint32 cols [MONO_METHOD_SIZE]; + char *sig; + const char *name; + + MonoMethod *mh; + + mh = mono_get_method_checked (m, MONO_TOKEN_METHOD_DEF | idx, NULL, NULL, &error); + if (mh) { + sig = dis_stringify_type (m, &mh->klass->byval_arg, FALSE); + name = g_strdup_printf ("%s%s%s", + sig ? sig : "", + sig ? "::" : "", + mh->name); + g_free (sig); + } else { + name = g_strdup_printf ("!bad-method-name!"); + mono_error_cleanup (&error); + } + mono_metadata_decode_row (&m->tables [MONO_TABLE_METHOD], + idx - 1, cols, MONO_METHOD_SIZE); + sig = get_methodref_signature (m, cols [MONO_METHOD_SIGNATURE], name); + + return sig; +} + +char * +get_method_type_param (MonoImage *m, guint32 blob_signature, MonoGenericContainer *container) +{ + GString *res = g_string_new (""); + const char *ptr = mono_metadata_blob_heap (m, blob_signature); + int param_count; + int i = 0; + char *s; + + mono_metadata_decode_value (ptr, &ptr); + ptr++; + param_count = mono_metadata_decode_value (ptr, &ptr); + + g_string_append_c (res, '<'); + + for (i = 0; i < param_count; i++){ + char *param = NULL; + + ptr = get_param (m, ptr, ¶m, container); + g_string_append (res, param); + if (i+1 != param_count) + g_string_append (res, ", "); + g_free (param); + } + g_string_append_c (res, '>'); + + s = res->str; + g_string_free (res, FALSE); + return s; +} + +/** + * get_methodspec + * + * Returns: An allocated stringified version of the methodspec signature. + */ + +char * +get_methodspec (MonoImage *m, int idx, guint32 token, const char *fancy_name, MonoGenericContainer *type_container) +{ + MonoError error; + GString *res = g_string_new (""); + guint32 member_cols [MONO_MEMBERREF_SIZE], method_cols [MONO_METHOD_SIZE]; + char *s, *type_param; + const char *ptr; + guint32 sig = 0; + int param_count, cconv, i, gen_count = 0; + MonoGenericContainer *container; + MonoMethod *mh = NULL; + + switch (token & MONO_METHODDEFORREF_MASK) { + case MONO_METHODDEFORREF_METHODDEF: + mono_metadata_decode_row (&m->tables [MONO_TABLE_METHOD], + (token >> MONO_METHODDEFORREF_BITS) - 1, + method_cols, MONO_METHOD_SIZE); + sig = method_cols [MONO_METHOD_SIGNATURE]; + break; + case MONO_METHODDEFORREF_METHODREF: + mono_metadata_decode_row (&m->tables [MONO_TABLE_MEMBERREF], + (token >> MONO_METHODDEFORREF_BITS) - 1, + member_cols, MONO_MEMBERREF_SIZE); + sig = member_cols [MONO_MEMBERREF_SIGNATURE]; + break; + default: + g_assert_not_reached (); + } + + ptr = mono_metadata_blob_heap (m, sig); + mono_metadata_decode_value (ptr, &ptr); + + mh = mono_get_method_checked (m, method_dor_to_token (token), NULL, (MonoGenericContext *) type_container, &error); + if (!mh) { + g_string_append_printf (res, "Could not decode method token 0x%x due to %s", token, mono_error_get_message (&error)); + mono_error_cleanup (&error); + return g_string_free (res, FALSE); + } + + container = mono_method_get_generic_container (mh); + if (!container) + container = type_container; + + if (*ptr & 0x20){ + if (*ptr & 0x40) + g_string_append (res, "explicit-this "); + else + g_string_append (res, "instance "); /* has-this */ + } + + if (*ptr & 0x10) + gen_count = 1; + cconv = *ptr & 0x0f; + ptr++; + if (gen_count) + gen_count = mono_metadata_decode_value (ptr, &ptr); + param_count = mono_metadata_decode_value (ptr, &ptr); + if (cconv != 0xa) { + char *allocated_ret_type; + ptr = get_ret_type (m, ptr, &allocated_ret_type, container); + g_string_append (res, allocated_ret_type); + g_free (allocated_ret_type); + } + + if (fancy_name){ + g_string_append_c (res, ' '); + g_string_append (res, fancy_name); + } + + mono_metadata_decode_row (&m->tables [MONO_TABLE_METHODSPEC], + idx - 1, member_cols, MONO_METHODSPEC_SIZE); + token = member_cols [MONO_METHODSPEC_SIGNATURE]; + type_param = get_method_type_param (m, token, type_container); + g_string_append (res, type_param); + g_string_append (res, " ("); + + /* + * methodspecs can not be varargs so we don't need to worry about that here + */ + + for (i = 0; i < param_count; i++){ + char *param = NULL; + + ptr = get_param (m, ptr, ¶m, container); + g_string_append (res, param); + if (i+1 != param_count) + g_string_append (res, ", "); + g_free (param); + } + g_string_append (res, ")"); + + /* + * cleanup and return + */ + s = res->str; + g_string_free (res, FALSE); + return s; +} + +/* + * get_encoded_user_string_bytearray: + * @ptr: pointer into the US heap + * @len: length of string in the heap. + * + * Strings on the US heap are encoded using UTF-16. Print a bytearray. + */ +static char* +get_encoded_user_string_bytearray (const unsigned char* ptr, int len) +{ + gchar *str; + int i, j, tspaces = (len % 16); + GString *res; + + if (len == 0) + return g_strdup_printf ("\"\""); + + res = g_string_new ("bytearray (\n\t"); + + for (i = 1; i <= len; ++i) { + g_string_append_printf (res, "%02x ", ptr [i-1]); + + if (i % 16 == 0) { + if (i == len) + g_string_append (res, ")// "); + else + g_string_append (res, " // "); + + for(j = i - 16; j < i; ++j) + g_string_append_printf (res, "%c", isprint (ptr [j]) ? ptr [j] : '.'); + g_string_append (res, "\n\t"); + } + } + + if (tspaces) { + g_string_append (res, ") "); + for (i = tspaces + 1; i < 16; ++i) + g_string_append_printf (res, " "); + + g_string_append (res, " // "); + for(i = len - tspaces; i < len; ++i) + g_string_append_printf (res, "%c", isprint (ptr [i]) ? ptr [i] : '.'); + g_string_append (res, "\n\t"); + } + + str = res->str; + g_string_free (res, FALSE); + return str; +} + +/* + * get_encoded_user_string_or_bytearray: + * @ptr: pointer into the US heap + * + * Strings on the US heap are encoded using UTF-16. Print as string + * if possible, else emit a bytearray. + */ +char* +get_encoded_user_string_or_bytearray (const unsigned char *ptr, int len) +{ + char *res, *eres, *result; + int i; + + res = (char *)g_malloc ((len >> 1) + 1); + + /* + * I should really use some kind of libunicode here + */ + for (i = 0; i + 1 < len; i += 2) { + if (ptr [i + 1] || + (!isprint (ptr [i]) && ptr [i] != '\\' && ptr [i] != '"' && + ptr [i] != '\r' && ptr [i] != '\n' && ptr [i] != '\t')) { + g_free (res); + return get_encoded_user_string_bytearray (ptr, len); + } + + res [i >> 1] = ptr [i]; + } + + res [len >> 1] = 0; + + eres = g_strescape (res, NULL); + result = g_strdup_printf ("\"%s\"", eres); + g_free (res); + g_free (eres); + + return result; +} + +char * +stringify_double (double r) +{ + char *ret, *ptr; + + ret = g_strdup_printf ("%.17g.", r); + ptr = ret + strlen (ret) - 1; + if (strpbrk (ret, ".eE") != ptr) + *ptr = '\0'; + + return ret; +} + +/** + * get_constant: + * @m: metadata context + * @blob_index: index into the blob where the constant is stored + * + * Returns: An allocated value representing a stringified version of the + * constant. + */ +char * +get_constant (MonoImage *m, MonoTypeEnum t, guint32 blob_index) +{ + const char *ptr = mono_metadata_blob_heap (m, blob_index); + int len; + + len = mono_metadata_decode_value (ptr, &ptr); + + switch (t){ + case MONO_TYPE_BOOLEAN: + return g_strdup_printf ("%s", *ptr ? "bool(true)" : "bool(false)"); + + case MONO_TYPE_CHAR: + return g_strdup_printf ("char(0x%04x)", read16(ptr)); + + case MONO_TYPE_U1: + case MONO_TYPE_I1: + return g_strdup_printf ("int8(0x%02x)", (int) ((*ptr) & 0xFF)); + break; + + case MONO_TYPE_U2: + case MONO_TYPE_I2: + return g_strdup_printf ("int16(0x%08x)", (int) read16 (ptr)); + + case MONO_TYPE_U4: + case MONO_TYPE_I4: + return g_strdup_printf ("int32(0x%08x)", read32 (ptr)); + + case MONO_TYPE_U8: + case MONO_TYPE_I8: { + guint32 low, high; + low = read32 (ptr); + high = read32 (ptr + 4); + return g_strdup_printf ("int64(0x%08x%08x)", high, low); + } + case MONO_TYPE_R4: { + gboolean normal; + float r; + readr4 (ptr, &r); + + /* Crazy solaris systems doesn't have isnormal */ +#ifdef HAVE_ISFINITE + normal = isfinite (r); +#else + normal = !dis_isinf (r) && !dis_isnan (r); +#endif + if (!normal) { + return g_strdup_printf ("float32(0x%08x)", read32 (ptr)); + } else { + char *str = stringify_double ((double) r); + char *ret = g_strdup_printf ("float32(%s)", str); + g_free (str); + return ret; + } + } + case MONO_TYPE_R8: { + gboolean normal; + double r; + readr8 (ptr, &r); + + /* Crazy solaris systems doesn't have isnormal */ +#ifdef HAVE_ISFINITE + normal = isfinite (r); +#else + normal = isnormal (r); +#endif + if (!normal) { + guint32 low, high; + low = read32 (ptr); + high = read32 (ptr + 4); + return g_strdup_printf ("float64(0x%08x%08x)", high, low); + } else { + char *str = stringify_double (r); + char *ret = g_strdup_printf ("float64(%s)", str); + g_free (str); + return ret; + } + } + case MONO_TYPE_STRING: + return get_encoded_user_string_or_bytearray ((const guchar*)ptr, len); + + case MONO_TYPE_CLASS: + return g_strdup ("nullref"); + + default: + g_error ("Unknown MONO_TYPE (%d) on constant at Blob index (0x%08x)\n", + (int) *ptr, blob_index); + return g_strdup_printf ("Unknown"); + } + +} + +/** + * get_token: + * @m: metadata context + * @token: token that we want to decode. + * + * Returns: An allocated value representing a stringified version of the + * constant. + */ +char * +get_token (MonoImage *m, guint32 token, MonoGenericContainer *container) +{ + char *temp, *result; + guint32 idx = mono_metadata_token_index (token); + + switch (mono_metadata_token_code (token)){ + case MONO_TOKEN_FIELD_DEF: + temp = get_field (m, token, container); + result = g_strdup_printf ("field %s", temp); + g_free (temp); + return result; + case MONO_TOKEN_METHOD_DEF: + case MONO_TOKEN_METHOD_SPEC: + temp = get_method (m, token, container); + result = g_strdup_printf ("method %s", temp); + g_free (temp); + return result; + case MONO_TOKEN_TYPE_DEF: + temp = get_typedef (m, idx); + result = get_escaped_name (temp); + g_free (temp); + return result; + case MONO_TOKEN_TYPE_REF: + return get_typeref (m, idx); + case MONO_TOKEN_TYPE_SPEC: + return get_typespec (m, idx, TRUE, container); + case MONO_TOKEN_MEMBER_REF: { + guint32 cols [MONO_MEMBERREF_SIZE]; + const char *sig; + mono_metadata_decode_row (&m->tables [MONO_TABLE_MEMBERREF], mono_metadata_token_index (token) - 1, cols, MONO_MEMBERREF_SIZE); + sig = mono_metadata_blob_heap (m, cols [MONO_MEMBERREF_SIGNATURE]); + mono_metadata_decode_blob_size (sig, &sig); + if (*sig == 0x6) { /* it's a field */ + temp = get_field (m, token, container); + result = g_strdup_printf ("field %s", temp); + g_free (temp); + return result; + } else { + temp = get_method (m, token, container); + result = g_strdup_printf ("method %s", temp); + g_free (temp); + return result; + } + break; + } + default: + g_error ("Do not know how to decode tokens of type 0x%08x", token); + } + + g_assert_not_reached (); + return g_strdup ("ERROR"); +} + +/** + * get_token_type: + * @m: metadata context + * @token: the token can belong to any of the following tables: + * MONO_TOKEN_TYPE_REF, MONO_TOKEN_TYPE_DEF, MONO_TOKEN_TYPE_SPEC + * + * Returns: a stringified version of the MethodDef or MethodRef or TypeSpecn + * at (token & 0xffffff) + */ +char * +get_token_type (MonoImage *m, guint32 token, MonoGenericContainer *container) +{ + char *temp = NULL, *s = NULL; + int idx; + + idx = mono_metadata_token_index (token); + + switch (mono_metadata_token_code (token)){ + case MONO_TOKEN_TYPE_DEF: + temp = get_typedef (m, idx); + s = g_strdup_printf ("%s", temp); + break; + + case MONO_TOKEN_TYPE_REF: + temp = get_typeref (m, idx); + s = g_strdup_printf ("%s", temp); + break; + + case MONO_TOKEN_TYPE_SPEC: + s = get_typespec (m, idx, FALSE, container); + break; + + default: + g_error ("Unhandled encoding for token 0x%08x", token); + + } + + if (temp) + g_free (temp); + + return s; +} + +char * +get_guid (MonoImage *m, guint32 guid_index) +{ + const unsigned char *guid; + char *result; + + guid = (const guchar*)mono_metadata_guid_heap (m, guid_index); + + result = g_strdup_printf ("{%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X}", + guid [3], guid [2], guid [1], guid [0], guid [5], guid [4], guid [7], guid [6], + guid [8], guid [9], guid [10], guid [11], guid [12], guid [13], guid [14], guid [15]); + return result; +} + +GList * +dis_get_custom_attrs (MonoImage *m, guint32 token) +{ + GList *list = NULL; + guint32 idx, i, len, mtoken; + guint32 cols [MONO_CUSTOM_ATTR_SIZE]; + MonoTableInfo *ca; + char *method; + GString *attr; + const char *val; + + idx = mono_metadata_token_index (token); + idx <<= MONO_CUSTOM_ATTR_BITS; + + switch (mono_metadata_token_table (token)) { + case MONO_TABLE_TYPEDEF: + idx |= MONO_CUSTOM_ATTR_TYPEDEF; + break; + case MONO_TABLE_ASSEMBLY: + idx |= MONO_CUSTOM_ATTR_ASSEMBLY; + break; + case MONO_TABLE_ASSEMBLYREF: + idx |= MONO_CUSTOM_ATTR_ASSEMBLYREF; + break; + case MONO_TABLE_MODULE: + idx |= MONO_CUSTOM_ATTR_MODULE; + break; + case MONO_TABLE_PROPERTY: + idx |= MONO_CUSTOM_ATTR_PROPERTY; + break; + case MONO_TABLE_EVENT: + idx |= MONO_CUSTOM_ATTR_EVENT; + break; + case MONO_TABLE_FIELD: + idx |= MONO_CUSTOM_ATTR_FIELDDEF; + break; + case MONO_TABLE_METHOD: + idx |= MONO_CUSTOM_ATTR_METHODDEF; + break; + case MONO_TABLE_PARAM: + idx |= MONO_CUSTOM_ATTR_PARAMDEF; + break; + case MONO_TABLE_GENERICPARAM: + idx |= MONO_CUSTOM_ATTR_GENERICPAR; + break; + default: + g_print ("Missing custom attr get support for token 0x%08x\n", token); + return NULL; + } + + ca = &m->tables [MONO_TABLE_CUSTOMATTRIBUTE]; + /* the table is not sorted */ + for (i = 0; i < ca->rows; ++i) { + char *dump; + mono_metadata_decode_row (ca, i, cols, MONO_CUSTOM_ATTR_SIZE); + if (cols [MONO_CUSTOM_ATTR_PARENT] != idx) + continue; + mtoken = cols [MONO_CUSTOM_ATTR_TYPE] >> MONO_CUSTOM_ATTR_TYPE_BITS; + switch (cols [MONO_CUSTOM_ATTR_TYPE] & MONO_CUSTOM_ATTR_TYPE_MASK) { + case MONO_CUSTOM_ATTR_TYPE_METHODDEF: + mtoken |= MONO_TOKEN_METHOD_DEF; + break; + case MONO_CUSTOM_ATTR_TYPE_MEMBERREF: + mtoken |= MONO_TOKEN_MEMBER_REF; + break; + default: + g_error ("Unknown table for custom attr type %08x", cols [MONO_CUSTOM_ATTR_TYPE]); + break; + } + method = get_method (m, mtoken, NULL); + val = mono_metadata_blob_heap (m, cols [MONO_CUSTOM_ATTR_VALUE]); + len = mono_metadata_decode_value (val, &val); + attr = g_string_new (".custom "); + dump = data_dump (val, len, "\t\t"); + g_string_append_printf (attr, "%s = %s", method, dump); + g_free (dump); + list = g_list_append (list, attr->str); + g_string_free (attr, FALSE); + g_free (method); + } + return list; +} + +char* +get_marshal_info (MonoImage *m, const char *blob) { + int size = 0; + + /* len = */ mono_metadata_decode_blob_size (blob, &blob); + + switch (*blob) { + case MONO_NATIVE_BOOLEAN: + return g_strdup ("bool"); + case MONO_NATIVE_I1: + return g_strdup ("int8"); + case MONO_NATIVE_U1: + return g_strdup ("unsigned int8"); + case MONO_NATIVE_I2: + return g_strdup ("int16"); + case MONO_NATIVE_U2: + return g_strdup ("unsigned int16"); + case MONO_NATIVE_I4: + return g_strdup ("int32"); + case MONO_NATIVE_U4: + return g_strdup ("unsigned int32"); + case MONO_NATIVE_I8: + return g_strdup ("int64"); + case MONO_NATIVE_U8: + return g_strdup ("unsigned int64"); + case MONO_NATIVE_R4: + return g_strdup ("float32"); + case MONO_NATIVE_R8: + return g_strdup ("float64"); + case MONO_NATIVE_CURRENCY: + return g_strdup ("currency"); + case MONO_NATIVE_BSTR: + return g_strdup ("bstr"); + case MONO_NATIVE_LPSTR: + return g_strdup ("lpstr"); + case MONO_NATIVE_LPWSTR: + return g_strdup ("lpwstr"); + case MONO_NATIVE_LPTSTR: + return g_strdup ("lptstr"); + case MONO_NATIVE_BYVALTSTR: + size = mono_metadata_decode_value (blob + 1, &blob); + return g_strdup_printf ("fixed sysstring [%d]", size); + case MONO_NATIVE_IUNKNOWN: + return g_strdup ("iunknown"); + case MONO_NATIVE_IDISPATCH: + return g_strdup ("idispatch"); + case MONO_NATIVE_STRUCT: + return g_strdup ("struct"); + case MONO_NATIVE_INTERFACE: + return g_strdup ("interface"); + case MONO_NATIVE_SAFEARRAY: + return g_strdup ("safearray"); + case MONO_NATIVE_BYVALARRAY: + size = mono_metadata_decode_value (blob + 1, &blob); + return g_strdup_printf ("fixed array [%d]", size); + case MONO_NATIVE_INT: + return g_strdup ("int"); + case MONO_NATIVE_UINT: + return g_strdup ("unsigned int"); + case MONO_NATIVE_VBBYREFSTR: + return g_strdup ("vbbyrefstr"); + case MONO_NATIVE_ANSIBSTR: + return g_strdup ("ansi bstr"); + case MONO_NATIVE_TBSTR: + return g_strdup ("tbstr"); + case MONO_NATIVE_VARIANTBOOL: + return g_strdup ("variant bool"); + case MONO_NATIVE_FUNC: + return g_strdup ("method"); + case MONO_NATIVE_ASANY: + return g_strdup ("as any"); + case MONO_NATIVE_LPARRAY: + return g_strdup ("[]"); + case MONO_NATIVE_LPSTRUCT: + return g_strdup ("lpstruct"); + case MONO_NATIVE_CUSTOM: + return g_strdup ("custom"); + case MONO_NATIVE_ERROR: + return g_strdup ("error"); + default: + return g_strdup ("unknown"); + } +} + +void +init_key_table (void) +{ + key_table = g_hash_table_new (g_str_hash, g_str_equal); + + g_hash_table_insert (key_table, (char *) "9", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "abstract", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "add", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "add.ovf", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "add.ovf.un", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "algorithm", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "alignment", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "and", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ansi", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "any", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "arglist", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "array", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "as", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "assembly", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "assert", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "at", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "autochar", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "auto", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "beforefieldinit", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "bestfit", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "beq", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "beq.s", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "bge", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "bge.s", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "bge.un", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "bge.un.s", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "bgt", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "bgt.s", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "bgt.un", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "bgt.un.s", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ble", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ble.s", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ble.un", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ble.un.s", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "blob", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "blob_object", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "blt", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "blt.s", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "blt.un", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "blt.un.s", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "bne.un", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "bne.un.s", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "bool", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "box", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "break", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "brfalse", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "brfalse.s", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "br", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "brinst", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "brinst.s", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "brnull", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "brnull.s", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "br.s", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "brtrue", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "brtrue.s", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "brzero", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "brzero.s", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "bstr", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "bytearray", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "byvalstr", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "call", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "callconv", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "calli", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "callmostderived", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "callvirt", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "carray", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "castclass", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "catch", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "cdecl", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ceq", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "cf", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "cgt", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "cgt.un", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "char", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "charmaperror", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "cil", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ckfinite", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "class", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "clsid", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "clt", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "clt.un", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "Compilercontrolled", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "const", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.i1", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.i2", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.i4", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.i8", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.i", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.ovf.i1", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.ovf.i1.un", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.ovf.i2", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.ovf.i2.un", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.ovf.i4", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.ovf.i4.un", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.ovf.i8", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.ovf.i8.un", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.ovf.i", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.ovf.i.un", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.ovf.u1", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.ovf.u1.un", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.ovf.u2", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.ovf.u2.un", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.ovf.u4", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.ovf.u4.un", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.ovf.u8", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.ovf.u8.un", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.ovf.u", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.ovf.u.un", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.r4", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.r8", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.r.un", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.u1", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.u2", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.u4", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.u8", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "conv.u", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "cpblk", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "cpobj", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "currency", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "custom", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "date", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "decimal", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "default", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "demand", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "deny", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "div", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "div.un", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "dup", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "endfault", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "endfilter", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "endfinally", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "endmac", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "enum", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "error", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "explicit", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "extends", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "extern", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "false", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "famandassem", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "family", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "famorassem", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "fastcall", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "fault", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "field", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "filetime", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "filter", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "final", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "finally", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "fixed", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "flags", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "float32", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "float64", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "float", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "forwardref", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "fromunmanaged", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "handler", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "hidebysig", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "hresult", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "idispatch", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "il", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "illegal", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "implements", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "implicitcom", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "implicitres", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "import", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "in", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "inheritcheck", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "initblk", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "init", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "initobj", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "initonly", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "instance", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "int16", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "int32", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "int64", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "int8", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "interface", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "internalcall", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "int", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "isinst", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "iunknown", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "jmp", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "lasterr", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "lcid", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldarg.0", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldarg.1", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldarg.2", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldarg.3", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldarga", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldarga.s", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldarg", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldarg.s", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldc.i4.0", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldc.i4.1", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldc.i4.2", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldc.i4.3", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldc.i4.4", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldc.i4.5", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldc.i4.6", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldc.i4.7", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldc.i4.8", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldc.i4", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldc.i4.m1", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldc.i4.M1", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldc.i4.s", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldc.i8", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldc.r4", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldc.r8", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldelem", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldelema", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldelem.i1", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldelem.i2", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldelem.i4", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldelem.i8", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldelem.i", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldelem.r4", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldelem.r8", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldelem.ref", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldelem.u1", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldelem.u2", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldelem.u4", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldelem.u8", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldflda", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldfld", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldftn", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldind.i1", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldind.i2", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldind.i4", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldind.i8", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldind.i", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldind.r4", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldind.r8", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldind.ref", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldind.u1", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldind.u2", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldind.u4", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldind.u8", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldlen", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldloc.0", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldloc.1", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldloc.2", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldloc.3", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldloca", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldloca.s", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldloc", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldloc.s", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldnull", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldobj", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldsflda", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldsfld", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldstr", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldtoken", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ldvirtftn", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "leave", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "leave.s", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "legacy", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "linkcheck", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "literal", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "localloc", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "lpstr", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "lpstruct", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "lptstr", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "lpvoid", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "lpwstr", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "managed", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "marshal", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "method", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "mkrefany", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "modopt", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "modreq", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "mul", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "mul.ovf", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "mul.ovf.un", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "native", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "neg", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "nested", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "newarr", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "newobj", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "newslot", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "noappdomain", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "noinlining", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "nomachine", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "nomangle", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "nometadata", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "noncasdemand", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "noncasinheritance", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "noncaslinkdemand", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "nop", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "noprocess", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "not", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "not_in_gc_heap", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "notremotable", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "notserialized", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "null", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "nullref", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "object", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "objectref", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "off", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "on", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "opt", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "optil", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "or", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "out", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "permitonly", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "pinned", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "pinvokeimpl", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "pop", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "prefix1", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "prefix2", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "prefix3", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "prefix4", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "prefix5", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "prefix6", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "prefix7", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "prefixref", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "prejitdeny", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "prejitgrant", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "preservesig", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "private", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "privatescope", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "property", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "protected", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "public", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "readonly", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "record", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "refany", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "refanytype", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "refanyval", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "rem", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "rem.un", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "reqmin", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "reqopt", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "reqrefuse", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "reqsecobj", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "request", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "ret", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "rethrow", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "retval", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "rtspecialname", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "runtime", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "safearray", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "sealed", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "sequential", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "serializable", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "shl", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "shr", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "shr.un", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "sizeof", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "special", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "specialname", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "starg", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "starg.s", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "static", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "stdcall", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "stelem", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "stelem.i1", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "stelem.i2", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "stelem.i4", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "stelem.i8", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "stelem.i", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "stelem.r4", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "stelem.r8", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "stelem.ref", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "stfld", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "stind.i1", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "stind.i2", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "stind.i4", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "stind.i8", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "stind.i", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "stind.r4", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "stind.r8", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "stloc", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "stobj", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "storage", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "stored_object", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "streamed_object", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "stream", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "strict", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "string", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "struct", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "stsfld", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "sub", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "sub.ovf", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "sub.ovf.un", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "switch", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "synchronized", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "syschar", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "sysstring", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "tbstr", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "thiscall", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "tls", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "to", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "true", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "type", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "typedref", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "uint", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "uint8", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "uint16", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "uint32", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "uint64", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "unbox", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "unicode", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "unmanagedexp", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "unmanaged", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "unsigned", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "userdefined", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "value", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "valuetype", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "vararg", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "variant", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "vector", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "virtual", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "void", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "wchar", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "winapi", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "with", GINT_TO_POINTER (TRUE)); + g_hash_table_insert (key_table, (char *) "xor", GINT_TO_POINTER (TRUE)); +} + +guint32 +method_dor_to_token (guint32 idx) { + switch (idx & MONO_METHODDEFORREF_MASK) { + case MONO_METHODDEFORREF_METHODDEF: + return MONO_TOKEN_METHOD_DEF | (idx >> MONO_METHODDEFORREF_BITS); + case MONO_METHODDEFORREF_METHODREF: + return MONO_TOKEN_MEMBER_REF | (idx >> MONO_METHODDEFORREF_BITS); + } + return -1; +} + +char * +get_method_override (MonoImage *m, guint32 token, MonoGenericContainer *container) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_METHODIMPL]; + int i; + + for (i = 1; i <= t->rows; i++){ + guint32 cols [MONO_METHODIMPL_SIZE]; + guint32 decl, impl; + + mono_metadata_decode_row (t, i - 1, cols, MONO_METHODIMPL_SIZE); + + impl = method_dor_to_token (cols [MONO_METHODIMPL_BODY]); + decl = method_dor_to_token (cols [MONO_METHODIMPL_DECLARATION]); + + if (token == impl) { + MonoError error; + MonoMethod *mh = NULL; + mh = mono_get_method_checked (m, decl, NULL, (MonoGenericContext *) container, &error); + + if (mh && (mh->klass && (mono_class_is_ginst (mh->klass) || mono_class_is_gtd (mh->klass)))) { + char *meth_str; + char *ret; + + meth_str = get_method_core (m, decl, TRUE, container); + ret = g_strdup_printf ("method %s", meth_str); + g_free (meth_str); + return ret; + } else { + if (!mono_error_ok (&error)) { + char *meth_str = get_method_core (m, decl, FALSE, container); + char *ret = g_strdup_printf ("Could not decode method override %s due to %s", meth_str, mono_error_get_message (&error)); + + mono_error_cleanup (&error); + g_free (meth_str); + return ret; + } else { + return get_method_core (m, decl, FALSE, container); + } + } + } + } + + return NULL; +} + +static void +check_ambiguous_genparams (MonoGenericContainer *container) +{ + GSList *dup_list = NULL, *l; + GHashTable *table = NULL; + gpointer *p; + int i; + + if (!container) + return; + + if (generic_containers && g_hash_table_lookup (generic_containers, container)) + /* Already been checked for ambiguous gen params */ + return; + + table = g_hash_table_new (g_str_hash, g_str_equal); + for (i = 0; i < container->type_argc; i++) { + MonoGenericParam *param = mono_generic_container_get_param (container, i); + + if ((p = (gpointer *)g_hash_table_lookup (table, mono_generic_param_info (param)->name))) + dup_list = g_slist_prepend (g_slist_prepend (dup_list, GUINT_TO_POINTER (i + 1)), p); + else + g_hash_table_insert (table, (char*)mono_generic_param_info (param)->name, GUINT_TO_POINTER (i + 1)); + } + + if (dup_list) { + if (!mono_generic_params_with_ambiguous_names) + mono_generic_params_with_ambiguous_names = g_hash_table_new (NULL, NULL); + for (l = dup_list; l; l = l->next) { + int param = GPOINTER_TO_UINT (l->data); + g_hash_table_insert (mono_generic_params_with_ambiguous_names, + mono_generic_container_get_param (container, param-1), + mono_generic_container_get_param (container, param-1)); + } + g_slist_free (dup_list); + } + + if (!generic_containers) + generic_containers = g_hash_table_new (NULL, NULL); + + g_hash_table_insert (generic_containers, container, container); + g_hash_table_destroy (table); +} + +static gboolean +cant_print_generic_param_name (MonoGenericParam *gparam) +{ + MonoGenericContainer *container; + g_assert (gparam); + + container = mono_generic_param_owner (gparam); + check_ambiguous_genparams (container); + return (!container || (mono_generic_params_with_ambiguous_names && + g_hash_table_lookup (mono_generic_params_with_ambiguous_names, gparam)) || !mono_generic_param_info (gparam)); +} + + +static dis_map_t method_impl_map [] = { + { METHOD_IMPL_ATTRIBUTE_IL, "cil " }, + { METHOD_IMPL_ATTRIBUTE_NATIVE, "native " }, + { METHOD_IMPL_ATTRIBUTE_OPTIL, "optil " }, + { METHOD_IMPL_ATTRIBUTE_RUNTIME, "runtime " }, + { 0, NULL } +}; + +static dis_map_t managed_type_map [] = { + { METHOD_IMPL_ATTRIBUTE_UNMANAGED, "unmanaged " }, + { METHOD_IMPL_ATTRIBUTE_MANAGED, "managed " }, + { 0, NULL } +}; + +static dis_map_t managed_impl_flags [] = { + { METHOD_IMPL_ATTRIBUTE_FORWARD_REF, "fwdref " }, + { METHOD_IMPL_ATTRIBUTE_PRESERVE_SIG, "preservesig " }, + { METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL, "internalcall " }, + { METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED, "synchronized " }, + { METHOD_IMPL_ATTRIBUTE_NOINLINING, "noinlining " }, + { METHOD_IMPL_ATTRIBUTE_NOOPTIMIZATION, "nooptimization " }, + { METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING, "agressive-inlining" }, + { 0, NULL } +}; + +char * +get_method_impl_flags (guint32 f) +{ + GString *str = g_string_new (""); + char *s; + int code_type = f & METHOD_IMPL_ATTRIBUTE_CODE_TYPE_MASK; + int managed_type = f & METHOD_IMPL_ATTRIBUTE_MANAGED_MASK; + int rest = f & ~(code_type | managed_type); + + g_string_append (str, map (code_type, method_impl_map)); + g_string_append (str, map (managed_type, managed_type_map)); + g_string_append (str, flags (rest, managed_impl_flags)); + + s = str->str; + g_string_free (str, FALSE); + return s; +} + diff --git a/unity-2019.4.24f1-mbe/mono/dis/get.h b/unity-2019.4.24f1-mbe/mono/dis/get.h new file mode 100644 index 000000000..738500c5d --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/dis/get.h @@ -0,0 +1,87 @@ + +/* + * These return allocated strings + */ +char *get_typedef (MonoImage *m, int idx); +char *get_module (MonoImage *m, int idx); +char *get_moduleref (MonoImage *m, int idx); +char *get_assemblyref (MonoImage *m, int idx); +char *get_typeref (MonoImage *m, int idx); +char *get_typedef_or_ref (MonoImage *m, guint32 dor_token, MonoGenericContainer *container); +char *dis_stringify_object_with_class (MonoImage *m, MonoClass *c, gboolean p, gboolean d); +char *get_type_or_methdef (MonoImage *m, guint32 dor_token); +char *get_field_signature (MonoImage *m, guint32 blob_signature, MonoGenericContainer *container); +char *get_fieldref_signature (MonoImage *m, int idx, MonoGenericContainer *container); +char *decode_literal (MonoImage *m, guint32 token); +char *get_field (MonoImage *m, guint32 token, MonoGenericContainer *container); +char *param_flags (guint32 f); +char *field_flags (guint32 f); +char *get_methodref_signature (MonoImage *m, guint32 blob_signature, const char *fancy); +char *get_methodspec (MonoImage *m, int idx, guint32 token, const char *fancy, + MonoGenericContainer *container); +char *get_constant (MonoImage *m, MonoTypeEnum t, guint32 blob_index); +char *get_encoded_user_string_or_bytearray (const unsigned char *ptr, int len); +char *get_token (MonoImage *m, guint32 token, MonoGenericContainer *container); +char *get_token_type (MonoImage *m, guint32 token, MonoGenericContainer *container); +char *get_typespec (MonoImage *m, guint32 blob_idx, gboolean is_def, MonoGenericContainer *container); +char *get_methoddef (MonoImage *m, guint32 idx); +char *get_method (MonoImage *m, guint32 token, MonoGenericContainer *container); +char *get_method_type_param (MonoImage *m, guint32 blob_signature, MonoGenericContainer *container); +char *get_guid (MonoImage *m, guint32 guid_index); +char *get_marshal_info (MonoImage *m, const char *blob); +char *get_generic_param (MonoImage *m, MonoGenericContainer *container); +char *get_escaped_name (const char *name); +char *get_method_override (MonoImage *m, guint32 token, MonoGenericContainer *container); + +GList *dis_get_custom_attrs (MonoImage *m, guint32 token); + +char *dis_stringify_type (MonoImage *m, MonoType *type, gboolean is_def); +char *dis_stringify_token (MonoImage *m, guint32 token); +char *dis_stringify_array (MonoImage *m, MonoArrayType *array, gboolean is_def); +char *dis_stringify_modifiers (MonoImage *m, int n, MonoCustomMod *mod); +char *dis_stringify_param (MonoImage *m, MonoType *param); +char *dis_stringify_method_signature_full (MonoImage *m, MonoMethodSignature *method, int methoddef_row, + MonoGenericContainer *container, gboolean fully_qualified, gboolean with_marshal_info); +char *dis_stringify_method_signature (MonoImage *m, MonoMethodSignature *method, int methoddef_row, + MonoGenericContainer *container, gboolean fully_qualified); +char *dis_stringify_function_ptr (MonoImage *m, MonoMethodSignature *method); +char *dis_stringify_marshal_spec (MonoMarshalSpec *spec); + +guint32 method_dor_to_token (guint32 idx); + +char *get_method_impl_flags (guint32 f); + +/* + * These functions are used during the decoding of streams in the + * metadata heaps (a simple parsing). + * + * They return the `next' location to continue parsing from (ptr is + * the starting location). + * + * Results are returning in the pointer argument. + */ +const char *get_encoded_typedef_or_ref (MonoImage *m, const char *ptr, + char **result); +const char *get_encoded_value (const char *_ptr, + guint32 *len); +const char *get_custom_mod (MonoImage *m, const char *ptr, + char **return_value); +const char *get_type (MonoImage *m, const char *ptr, + char **result, gboolean is_def, MonoGenericContainer *container); +const char *get_ret_type (MonoImage *m, const char *ptr, + char **ret_type, MonoGenericContainer *container); +const char *get_param (MonoImage *m, const char *ptr, + char **retval, MonoGenericContainer *container); +const char *get_blob_encoded_size (const char *ptr, int *size); + +MonoTypeEnum get_field_literal_type (MonoImage *m, guint32 blob_signature); + +char *stringify_double (double r); + +/** + * This is called to initialize the table containing keyword names + */ +void init_key_table (void); + +extern gboolean show_method_tokens; +extern gboolean show_tokens; diff --git a/unity-2019.4.24f1-mbe/mono/dis/main.c b/unity-2019.4.24f1-mbe/mono/dis/main.c new file mode 100644 index 000000000..dbbb12a62 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/dis/main.c @@ -0,0 +1,2067 @@ +/* + * main.c: Sample disassembler + * + * Author: + * Miguel de Icaza (miguel@ximian.com) + * + * (C) 2001 Ximian, Inc. + * + * TODO: + * Investigate how interface inheritance works and how it should be dumped. + * Structs are not being labeled as `valuetype' classes + * + * How are fields with literals mapped to constants? + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#include +#include +#include +#include +#include +#include +#include +#include "meta.h" +#include "util.h" +#include "dump.h" +#include "get.h" +#include "dis-cil.h" +#include "declsec.h" +#include +#include +#include +#include +#include +#include +#include +#include + +static void setup_filter (MonoImage *image); +static gboolean should_include_type (int idx); +static gboolean should_include_method (int idx); +static gboolean should_include_field (int idx); + +FILE *output; + +/* True if you want to get a dump of the header data */ +gboolean dump_header_data_p = FALSE; + +/* True if you want to get forward declarations */ +gboolean dump_forward_decls = FALSE; + +/* True if you want to dump managed resources as files */ +gboolean dump_managed_resources = FALSE; + +gboolean substitute_with_mscorlib_p = FALSE; + +int dump_table = -1; + +static void +dump_header_data (MonoImage *img) +{ + if (!dump_header_data_p) + return; + + fprintf (output, + "// Ximian's CIL disassembler, version 1.0\n" + "// Copyright (C) 2001 Ximian, Inc.\n\n"); +} + +static void +dump_cattrs_list (GList *list, const char *indent) +{ + GList *tmp; + + for (tmp = list; tmp; tmp = tmp->next) { + fprintf (output, "%s%s\n", indent, (char*)tmp->data); + g_free (tmp->data); + } + g_list_free (list); +} + +static void +dump_cattrs (MonoImage *m, guint32 token, const char *indent) +{ + GList *list; + + list = dis_get_custom_attrs (m, token); + dump_cattrs_list (list, indent); +} + +static const char* +get_il_security_action (int val) +{ + static char buf [32]; + + switch (val) { + case SECURITY_ACTION_DEMAND: + return "demand"; + case SECURITY_ACTION_ASSERT: + return "assert"; + case SECURITY_ACTION_DENY: + return "deny"; + case SECURITY_ACTION_PERMITONLY: + return "permitonly"; + case SECURITY_ACTION_LINKDEMAND: + return "linkcheck"; + case SECURITY_ACTION_INHERITDEMAND: + return "inheritcheck"; + case SECURITY_ACTION_REQMIN: + return "reqmin"; + case SECURITY_ACTION_REQOPT: + return "reqopt"; + case SECURITY_ACTION_REQREFUSE: + return "reqrefuse"; + /* Special actions (for non CAS permissions) */ + case SECURITY_ACTION_NONCASDEMAND: + return "noncasdemand"; + case SECURITY_ACTION_NONCASLINKDEMAND: + return "noncaslinkdemand"; + case SECURITY_ACTION_NONCASINHERITANCE: + return "noncasinheritance"; + /* Fx 2.0 actions (for both CAS and non-CAS permissions) */ + case SECURITY_ACTION_LINKDEMANDCHOICE: + return "linkdemandor"; + case SECURITY_ACTION_INHERITDEMANDCHOICE: + return "inheritancedemandor"; + case SECURITY_ACTION_DEMANDCHOICE: + return "demandor"; + default: + g_snprintf (buf, sizeof (buf), "0x%04X", val); + return buf; + } +} + +#define OBJECT_TYPE_TYPEDEF 0 +#define OBJECT_TYPE_METHODDEF 1 +#define OBJECT_TYPE_ASSEMBLYDEF 2 + +static void +dump_declarative_security (MonoImage *m, guint32 objectType, guint32 token, const char *indent) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_DECLSECURITY]; + guint32 cols [MONO_DECL_SECURITY_SIZE]; + int i, len; + guint32 idx; + const char *blob, *action; + + for (i = 1; i <= t->rows; i++) { + mono_metadata_decode_row (t, i - 1, cols, MONO_DECL_SECURITY_SIZE); + blob = mono_metadata_blob_heap (m, cols [MONO_DECL_SECURITY_PERMISSIONSET]); + len = mono_metadata_decode_blob_size (blob, &blob); + action = get_il_security_action (cols [MONO_DECL_SECURITY_ACTION]); + idx = cols [MONO_DECL_SECURITY_PARENT]; + if (((idx & MONO_HAS_DECL_SECURITY_MASK) == objectType) && ((idx >> MONO_HAS_DECL_SECURITY_BITS) == token)) { + char *dump; + if (blob [0] == MONO_DECLSEC_FORMAT_20) { + /* 2.0 declarative security format */ + dump = dump_declsec_entry20 (m, blob, indent); + fprintf (output, "%s.permissionset %s = %s\n", indent, action, dump); + } else { + /* 1.x declarative security metadata format */ + dump = data_dump (blob, len, indent); + fprintf (output, "%s.permissionset %s = %s", indent, action, dump); + } + g_free (dump); + } + } +} + +static char * +assembly_flags (guint32 f) +{ + if (f & ASSEMBLYREF_RETARGETABLE_FLAG) + return g_strdup ("retargetable "); + return g_strdup (""); +} + +static void +dis_directive_assembly (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_ASSEMBLY]; + guint32 cols [MONO_ASSEMBLY_SIZE]; + char *flags; + + if (t->base == NULL) + return; + + mono_metadata_decode_row (t, 0, cols, MONO_ASSEMBLY_SIZE); + flags = assembly_flags (cols [MONO_ASSEMBLY_FLAGS]); + + fprintf (output, ".assembly %s'%s'\n{\n", + flags, mono_metadata_string_heap (m, cols [MONO_ASSEMBLY_NAME])); + dump_cattrs (m, MONO_TOKEN_ASSEMBLY | 1, " "); + dump_declarative_security (m, OBJECT_TYPE_ASSEMBLYDEF, 1, " "); + fprintf (output, + " .hash algorithm 0x%08x\n" + " .ver %d:%d:%d:%d\n", + cols [MONO_ASSEMBLY_HASH_ALG], + cols [MONO_ASSEMBLY_MAJOR_VERSION], cols [MONO_ASSEMBLY_MINOR_VERSION], + cols [MONO_ASSEMBLY_BUILD_NUMBER], cols [MONO_ASSEMBLY_REV_NUMBER]); + if (cols [MONO_ASSEMBLY_CULTURE]){ + const char *locale = mono_metadata_string_heap (m, cols [MONO_ASSEMBLY_CULTURE]); + glong items_read, items_written; + gunichar2 *render = g_utf8_to_utf16 (locale, strlen (locale), &items_read, &items_written, NULL); + char *dump = data_dump ((const char *) render, items_written * sizeof (gunichar2), "\t\t"); + fprintf (output, " .locale %s\n", dump); + g_free (dump); + g_free (render); + + } if (cols [MONO_ASSEMBLY_PUBLIC_KEY]) { + const char* b = mono_metadata_blob_heap (m, cols [MONO_ASSEMBLY_PUBLIC_KEY]); + int len = mono_metadata_decode_blob_size (b, &b); + char *dump = data_dump (b, len, "\t\t"); + fprintf (output, " .publickey =%s", dump); + g_free (dump); + } + fprintf (output, "}\n"); + + g_free (flags); +} + +static void +dis_directive_assemblyref (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_ASSEMBLYREF]; + guint32 cols [MONO_ASSEMBLYREF_SIZE]; + int i; + + if (t->base == NULL) + return; + + for (i = 0; i < t->rows; i++){ + char *esc, *flags; + + mono_metadata_decode_row (t, i, cols, MONO_ASSEMBLYREF_SIZE); + + esc = get_escaped_name (mono_metadata_string_heap (m, cols [MONO_ASSEMBLYREF_NAME])); + flags = assembly_flags (cols [MONO_ASSEMBLYREF_FLAGS]); + + fprintf (output, + ".assembly extern %s%s\n" + "{\n" + " .ver %d:%d:%d:%d\n", + flags, + esc, + cols [MONO_ASSEMBLYREF_MAJOR_VERSION], cols [MONO_ASSEMBLYREF_MINOR_VERSION], + cols [MONO_ASSEMBLYREF_BUILD_NUMBER], cols [MONO_ASSEMBLYREF_REV_NUMBER] + ); + dump_cattrs (m, MONO_TOKEN_ASSEMBLY_REF | (i + 1), " "); + if (cols [MONO_ASSEMBLYREF_CULTURE]){ + fprintf (output, " .locale %s\n", mono_metadata_string_heap (m, cols [MONO_ASSEMBLYREF_CULTURE])); + } + if (cols [MONO_ASSEMBLYREF_PUBLIC_KEY]){ + const char* b = mono_metadata_blob_heap (m, cols [MONO_ASSEMBLYREF_PUBLIC_KEY]); + int len = mono_metadata_decode_blob_size (b, &b); + char *dump = data_dump (b, len, "\t\t"); + fprintf (output, " .publickeytoken =%s", dump); + g_free (dump); + } + fprintf (output, "}\n"); + g_free (flags); + g_free (esc); + } +} + +static void +dis_directive_module (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_MODULE]; + int i; + + for (i = 0; i < t->rows; i++){ + guint32 cols [MONO_MODULE_SIZE]; + const char *name; + char *guid, *ename; + + mono_metadata_decode_row (t, i, cols, MONO_MODULE_SIZE); + + name = mono_metadata_string_heap (m, cols [MONO_MODULE_NAME]); + ename = get_escaped_name (name); + guid = get_guid (m, cols [MONO_MODULE_MVID]); + fprintf (output, ".module %s // GUID = %s\n\n", ename, guid); + g_free (ename); + + dump_cattrs (m, MONO_TOKEN_MODULE | (i + 1), ""); + } +} + +static void +dis_directive_moduleref (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_MODULEREF]; + int i; + + for (i = 0; i < t->rows; i++){ + guint32 cols [MONO_MODULEREF_SIZE]; + + mono_metadata_decode_row (t, i, cols, MONO_MODULEREF_SIZE); + + fprintf (output, ".module extern '%s'\n", mono_metadata_string_heap (m, cols [MONO_MODULEREF_NAME])); + } + +} + +static void +dis_nt_header (MonoImage *m) +{ + MonoCLIImageInfo *image_info = (MonoCLIImageInfo *)m->image_info; + if (image_info && image_info->cli_header.nt.pe_stack_reserve != 0x100000) + fprintf (output, ".stackreserve 0x%x\n", image_info->cli_header.nt.pe_stack_reserve); +} + +static void +dis_directive_file (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_FILE]; + int i, j, len; + guint32 entry_point; + + entry_point = mono_image_get_entry_point (m); + + for (i = 0; i < t->rows; i++){ + guint32 cols [MONO_FILE_SIZE]; + const char *name, *hash; + guint32 token; + + mono_metadata_decode_row (t, i, cols, MONO_FILE_SIZE); + + name = mono_metadata_string_heap (m, cols [MONO_FILE_NAME]); + + hash = mono_metadata_blob_heap (m, cols [MONO_FILE_HASH_VALUE]); + len = mono_metadata_decode_blob_size (hash, &hash); + + fprintf (output, ".file %s%s .hash = (", + cols [MONO_FILE_FLAGS] & FILE_CONTAINS_NO_METADATA ? "nometadata " : "", name); + + for (j = 0; j < len; ++j) + fprintf (output, " %02X", hash [j] & 0xff); + + token = mono_metadata_make_token (MONO_TABLE_FILE, i + 1); + fprintf (output, " )%s\n", (token == entry_point) ? " .entrypoint" : ""); + } + +} + +static void +dis_directive_mresource (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_MANIFESTRESOURCE]; + int i; + + for (i = 0; i < t->rows; i++){ + guint32 cols [MONO_MANIFEST_SIZE]; + const char *name; + guint32 impl, idx, name_token; + + mono_metadata_decode_row (t, i, cols, MONO_MANIFEST_SIZE); + + name = mono_metadata_string_heap (m, cols [MONO_MANIFEST_NAME]); + + fprintf (output, ".mresource %s '%s'\n", (cols [MONO_MANIFEST_FLAGS] & MANIFEST_RESOURCE_VISIBILITY_MASK) == (MANIFEST_RESOURCE_PUBLIC) ? "public" : "private", name); + fprintf (output, "{\n"); + impl = cols [MONO_MANIFEST_IMPLEMENTATION]; + if (impl) { + idx = impl >> MONO_IMPLEMENTATION_BITS; + if ((impl & MONO_IMPLEMENTATION_MASK) == MONO_IMPLEMENTATION_FILE) { + name_token = mono_metadata_decode_row_col (&m->tables [MONO_TABLE_FILE], idx - 1, MONO_FILE_NAME); + + fprintf (output, " .file '%s' at 0x0\n", mono_metadata_string_heap (m, name_token)); + } + if ((impl & MONO_IMPLEMENTATION_MASK) == MONO_IMPLEMENTATION_ASSEMBLYREF) { + name_token = mono_metadata_decode_row_col (&m->tables [MONO_TABLE_ASSEMBLYREF], idx - 1, MONO_ASSEMBLYREF_NAME); + fprintf (output, " .assembly extern '%s'\n", mono_metadata_string_heap (m, name_token)); + } + } + fprintf (output, "}\n"); + } + +} + +static dis_map_t visibility_map [] = { + { TYPE_ATTRIBUTE_NOT_PUBLIC, "private " }, + { TYPE_ATTRIBUTE_PUBLIC, "public " }, + { TYPE_ATTRIBUTE_NESTED_PUBLIC, "nested public " }, + { TYPE_ATTRIBUTE_NESTED_PRIVATE, "nested private " }, + { TYPE_ATTRIBUTE_NESTED_FAMILY, "nested family " }, + { TYPE_ATTRIBUTE_NESTED_ASSEMBLY, "nested assembly " }, + { TYPE_ATTRIBUTE_NESTED_FAM_AND_ASSEM, "nested famandassem " }, + { TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM, "nested famorassem " }, + { 0, NULL } +}; + +static dis_map_t layout_map [] = { + { TYPE_ATTRIBUTE_AUTO_LAYOUT, "auto " }, + { TYPE_ATTRIBUTE_SEQUENTIAL_LAYOUT, "sequential " }, + { TYPE_ATTRIBUTE_EXPLICIT_LAYOUT, "explicit " }, + { 0, NULL } +}; + +static dis_map_t format_map [] = { + { TYPE_ATTRIBUTE_ANSI_CLASS, "ansi " }, + { TYPE_ATTRIBUTE_UNICODE_CLASS, "unicode " }, + { TYPE_ATTRIBUTE_AUTO_CLASS, "auto " }, + { 0, NULL } +}; + +static char * +typedef_flags (guint32 flags) +{ + static char buffer [1024]; + int visibility = flags & TYPE_ATTRIBUTE_VISIBILITY_MASK; + int layout = flags & TYPE_ATTRIBUTE_LAYOUT_MASK; + int format = flags & TYPE_ATTRIBUTE_STRING_FORMAT_MASK; + + buffer [0] = 0; + + strcat (buffer, map (visibility, visibility_map)); + strcat (buffer, map (layout, layout_map)); + strcat (buffer, map (format, format_map)); + + if (flags & TYPE_ATTRIBUTE_ABSTRACT) + strcat (buffer, "abstract "); + if (flags & TYPE_ATTRIBUTE_SEALED) + strcat (buffer, "sealed "); + if (flags & TYPE_ATTRIBUTE_SPECIAL_NAME) + strcat (buffer, "specialname "); + if (flags & TYPE_ATTRIBUTE_IMPORT) + strcat (buffer, "import "); + if (flags & TYPE_ATTRIBUTE_SERIALIZABLE) + strcat (buffer, "serializable "); + if (flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT) + strcat (buffer, "beforefieldinit "); + if (flags & TYPE_ATTRIBUTE_FORWARDER) + strcat (buffer, "forwarder "); + + return buffer; +} + +/** + * dis_field_list: + * @m: metadata context + * @start: starting index into the Field Table. + * @end: ending index into Field table. + * + * This routine displays all the decoded fields from @start to @end + */ +static void +dis_field_list (MonoImage *m, guint32 start, guint32 end, MonoGenericContainer *container) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_FIELD]; + guint32 cols [MONO_FIELD_SIZE]; + char *esname; + char rva_desc [32]; + guint32 rva; + int i; + + if (end > t->rows + 1) { + g_warning ("ERROR index out of range in fields"); + end = t->rows; + } + + for (i = start; i < end; i++){ + char *sig, *flags, *attrs = NULL; + char *marshal_str = NULL; + guint32 field_offset = -1; + + if (!should_include_field (i + 1)) + continue; + mono_metadata_decode_row (t, i, cols, MONO_FIELD_SIZE); + sig = get_field_signature (m, cols [MONO_FIELD_SIGNATURE], container); + flags = field_flags (cols [MONO_FIELD_FLAGS]); + + if (cols [MONO_FIELD_FLAGS] & FIELD_ATTRIBUTE_HAS_FIELD_MARSHAL) { + const char *tp; + MonoMarshalSpec *spec; + + tp = mono_metadata_get_marshal_info (m, i, TRUE); + spec = mono_metadata_parse_marshal_spec (m, tp); + marshal_str = dis_stringify_marshal_spec (spec); + } + + if (cols [MONO_FIELD_FLAGS] & FIELD_ATTRIBUTE_HAS_FIELD_RVA) { + mono_metadata_field_info (m, i, NULL, &rva, NULL); + g_snprintf (rva_desc, sizeof (rva_desc), " at D_%08x", rva); + } else { + rva_desc [0] = 0; + } + + mono_metadata_field_info (m, i, &field_offset, NULL, NULL); + if (field_offset != -1) + attrs = g_strdup_printf ("[%d]", field_offset); + esname = get_escaped_name (mono_metadata_string_heap (m, cols [MONO_FIELD_NAME])); + if (cols [MONO_FIELD_FLAGS] & FIELD_ATTRIBUTE_HAS_DEFAULT){ + char *lit; + guint32 const_cols [MONO_CONSTANT_SIZE]; + guint32 crow; + + if ((crow = mono_metadata_get_constant_index (m, MONO_TOKEN_FIELD_DEF | (i+1), 0))) { + mono_metadata_decode_row (&m->tables [MONO_TABLE_CONSTANT], crow-1, const_cols, MONO_CONSTANT_SIZE); + lit = get_constant (m, (MonoTypeEnum)const_cols [MONO_CONSTANT_TYPE], const_cols [MONO_CONSTANT_VALUE]); + } else { + lit = g_strdup ("not found"); + } + + fprintf (output, " .field %s%s%s %s = ", + flags, marshal_str ? marshal_str : " ", sig, esname); + fprintf (output, "%s\n", lit); + g_free (lit); + } else + fprintf (output, " .field %s %s%s%s %s%s\n", + attrs? attrs: "", flags, marshal_str ? marshal_str : " ", sig, esname, rva_desc); + g_free (attrs); + g_free (flags); + g_free (marshal_str); + g_free (sig); + g_free (esname); + dump_cattrs (m, MONO_TOKEN_FIELD_DEF | (i + 1), " "); + } +} + +static dis_map_t method_access_map [] = { + { METHOD_ATTRIBUTE_COMPILER_CONTROLLED, "privatescope " }, + { METHOD_ATTRIBUTE_PRIVATE, "private " }, + { METHOD_ATTRIBUTE_FAM_AND_ASSEM, "famandassem " }, + { METHOD_ATTRIBUTE_ASSEM, "assembly " }, + { METHOD_ATTRIBUTE_FAMILY, "family " }, + { METHOD_ATTRIBUTE_FAM_OR_ASSEM, "famorassem " }, + { METHOD_ATTRIBUTE_PUBLIC, "public " }, + { 0, NULL } +}; + +static dis_map_t method_flags_map [] = { + { METHOD_ATTRIBUTE_STATIC, "static " }, + { METHOD_ATTRIBUTE_FINAL, "final " }, + { METHOD_ATTRIBUTE_VIRTUAL, "virtual " }, + { METHOD_ATTRIBUTE_HIDE_BY_SIG, "hidebysig " }, + { METHOD_ATTRIBUTE_VTABLE_LAYOUT_MASK, "newslot " }, + { METHOD_ATTRIBUTE_ABSTRACT, "abstract " }, + { METHOD_ATTRIBUTE_SPECIAL_NAME, "specialname " }, + { METHOD_ATTRIBUTE_RT_SPECIAL_NAME, "rtspecialname " }, + { METHOD_ATTRIBUTE_UNMANAGED_EXPORT, "export " }, +/* MS ilasm doesn't compile this statement - is must be added automagically when permissionset are present */ + { METHOD_ATTRIBUTE_HAS_SECURITY, "" /*"hassecurity"*/ }, + { METHOD_ATTRIBUTE_REQUIRE_SEC_OBJECT, "requiresecobj " }, + { METHOD_ATTRIBUTE_PINVOKE_IMPL, "pinvokeimpl " }, + { METHOD_ATTRIBUTE_STRICT, "strict " }, + { 0, NULL } +}; + +/** + * method_flags: + * + * Returns a stringified version of the Method's flags + */ +static char * +method_flags (guint32 f) +{ + GString *str = g_string_new (""); + int access = f & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK; + int rest = f & ~access; + char *s; + + g_string_append (str, map (access, method_access_map)); + g_string_append (str, flags (rest, method_flags_map)); + + s = str->str; + g_string_free (str, FALSE); + + return s; +} + +static dis_map_t pinvoke_flags_map [] = { + { PINVOKE_ATTRIBUTE_NO_MANGLE , "nomangle " }, + { PINVOKE_ATTRIBUTE_SUPPORTS_LAST_ERROR, "lasterr " }, + { PINVOKE_ATTRIBUTE_BEST_FIT_ENABLED, "bestfit:on " }, + { PINVOKE_ATTRIBUTE_BEST_FIT_DISABLED, "bestfit:off " }, + { PINVOKE_ATTRIBUTE_THROW_ON_UNMAPPABLE_ENABLED, "charmaperror:on " }, + { PINVOKE_ATTRIBUTE_THROW_ON_UNMAPPABLE_DISABLED, "charmaperror:off " }, + { 0, NULL } +}; + +static dis_map_t pinvoke_call_conv_map [] = { + { PINVOKE_ATTRIBUTE_CALL_CONV_WINAPI, "winapi " }, + { PINVOKE_ATTRIBUTE_CALL_CONV_CDECL, "cdecl " }, + { PINVOKE_ATTRIBUTE_CALL_CONV_STDCALL, "stdcall " }, + { PINVOKE_ATTRIBUTE_CALL_CONV_THISCALL, "thiscall " }, + { PINVOKE_ATTRIBUTE_CALL_CONV_FASTCALL, "fastcall " }, + { 0, "" }, + { -1, NULL } +}; + +static dis_map_t pinvoke_char_set_map [] = { + { PINVOKE_ATTRIBUTE_CHAR_SET_NOT_SPEC, "" }, + { PINVOKE_ATTRIBUTE_CHAR_SET_ANSI, "ansi " }, + { PINVOKE_ATTRIBUTE_CHAR_SET_UNICODE , "unicode " }, + { PINVOKE_ATTRIBUTE_CHAR_SET_AUTO, "autochar " }, + { 0, NULL } +}; + +/** + * pinvoke_flags: + * + * Returns a stringified version of the Method's pinvoke flags + */ +static char * +pinvoke_flags (guint32 f) +{ + GString *str = g_string_new (""); + int cset = f & PINVOKE_ATTRIBUTE_CHAR_SET_MASK; + int cconv = f & PINVOKE_ATTRIBUTE_CALL_CONV_MASK; + int rest = f & ~(cset | cconv); + char *s; + + g_string_append (str, map (cset, pinvoke_char_set_map)); + g_string_append (str, map (cconv, pinvoke_call_conv_map)); + g_string_append (str, flags (rest, pinvoke_flags_map)); + + s = g_strdup(str->str); + g_string_free (str, FALSE); + + return s; +} + +static void +dis_locals (MonoImage *m, MonoMethodHeader *mh, const char *ptr) +{ + int i; + + if (show_tokens) { + unsigned char flags = *(const unsigned char *) ptr; + unsigned char format = flags & METHOD_HEADER_FORMAT_MASK; + guint16 fat_flags; + guint32 local_var_sig_tok, init_locals; + + g_assert (format == METHOD_HEADER_FAT_FORMAT); + fat_flags = read16 (ptr); + ptr += 2; + /* max_stack = read16 (ptr); */ + ptr += 2; + /* code_size = read32 (ptr); */ + ptr += 4; + local_var_sig_tok = read32 (ptr); + ptr += 4; + + if (fat_flags & METHOD_HEADER_INIT_LOCALS) + init_locals = 1; + else + init_locals = 0; + + fprintf(output, "\t.locals /*%08x*/ %s(\n", + local_var_sig_tok, init_locals ? "init " : ""); + } else + fprintf(output, "\t.locals %s(\n", mh->init_locals ? "init " : ""); + + for (i=0; i < mh->num_locals; ++i) { + char * desc; + if (i) + fprintf(output, ",\n"); + /* print also byref and pinned attributes */ + desc = dis_stringify_type (m, mh->locals[i], TRUE); + fprintf(output, "\t\t%s\tV_%d", desc, i); + g_free(desc); + } + fprintf(output, ")\n"); +} + +static void +dis_code (MonoImage *m, guint32 token, guint32 rva, MonoGenericContainer *container) +{ + MonoError error; + MonoMethodHeader *mh; + const char *ptr = mono_image_rva_map (m, rva); + const char *loc; + gchar *override; + guint32 entry_point; + + if (rva == 0) + return; + + override = get_method_override (m, token, container); + if (override) { + fprintf (output, "\t.override %s\n", override); + g_free (override); + } + + mh = mono_metadata_parse_mh_full (m, container, ptr, &error); + entry_point = mono_image_get_entry_point (m); + if (entry_point && mono_metadata_token_index (entry_point) && mono_metadata_token_table (entry_point) == MONO_TABLE_METHOD) { + loc = mono_metadata_locate_token (m, entry_point); + if (rva == read32 (loc)) + fprintf (output, "\t.entrypoint\n"); + } + + if (mh) { + fprintf (output, "\t// Code size %d (0x%x)\n", mh->code_size, mh->code_size); + fprintf (output, "\t.maxstack %d\n", mh->max_stack); + if (mh->num_locals) + dis_locals (m, mh, ptr); + disassemble_cil (m, mh, container); +/* + hex_dump (mh->code, 0, mh->code_size); + printf ("\nAfter the code\n"); + hex_dump (mh->code + mh->code_size, 0, 64); +*/ + mono_metadata_free_mh (mh); + } else { + mono_error_cleanup (&error); + } +} + +static char * +pinvoke_info (MonoImage *m, guint32 mindex) +{ + MonoTableInfo *im = &m->tables [MONO_TABLE_IMPLMAP]; + MonoTableInfo *mr = &m->tables [MONO_TABLE_MODULEREF]; + guint32 im_cols [MONO_IMPLMAP_SIZE]; + guint32 mr_cols [MONO_MODULEREF_SIZE]; + const char *import, *scope; + char *flags; + int i; + + for (i = 0; i < im->rows; i++) { + + mono_metadata_decode_row (im, i, im_cols, MONO_IMPLMAP_SIZE); + + if ((im_cols [MONO_IMPLMAP_MEMBER] >> 1) == mindex + 1) { + + flags = pinvoke_flags (im_cols [MONO_IMPLMAP_FLAGS]); + + import = mono_metadata_string_heap (m, im_cols [MONO_IMPLMAP_NAME]); + + mono_metadata_decode_row (mr, im_cols [MONO_IMPLMAP_SCOPE] - 1, + mr_cols, MONO_MODULEREF_SIZE); + + scope = mono_metadata_string_heap (m, mr_cols [MONO_MODULEREF_NAME]); + + return g_strdup_printf ("(\"%s\" as \"%s\" %s)", scope, import, + flags); + g_free (flags); + } + } + + return NULL; +} + +/* + * dump_cattrs_for_type_params + * + * @m: + * @token: TypeOrMethodDef token, owner for GenericParam + * + * Dumps the custom attributes for @token's type parameters + */ +static void +dump_cattrs_for_type_params (MonoImage *m, guint32 token, const char *indent) +{ + MonoTableInfo *tdef = &m->tables [MONO_TABLE_GENERICPARAM]; + guint32 cols [MONO_GENERICPARAM_SIZE]; + guint32 owner = 0, i; + GList *list = NULL; + + if (! (i = mono_metadata_get_generic_param_row (m, token, &owner))) + return; + + mono_metadata_decode_row (tdef, i - 1, cols, MONO_GENERICPARAM_SIZE); + do { + list = dis_get_custom_attrs (m, mono_metadata_make_token (MONO_TABLE_GENERICPARAM, i)); + if (list) { + fprintf (output, "%s.param type %s\n", indent, mono_metadata_string_heap (m, cols [MONO_GENERICPARAM_NAME])); + dump_cattrs_list (list, indent); + } + + if (++i > tdef->rows) + break; + mono_metadata_decode_row (tdef, i - 1, cols, MONO_GENERICPARAM_SIZE); + } while (cols [MONO_GENERICPARAM_OWNER] == owner); +} + +static void +dump_cattrs_for_method_params (MonoImage *m, guint32 midx, MonoMethodSignature *sig) { + MonoTableInfo *methodt; + MonoTableInfo *paramt; + guint param_index, lastp, i; + + methodt = &m->tables [MONO_TABLE_METHOD]; + paramt = &m->tables [MONO_TABLE_PARAM]; + param_index = mono_metadata_decode_row_col (methodt, midx, MONO_METHOD_PARAMLIST); + if (midx + 1 < methodt->rows) + lastp = mono_metadata_decode_row_col (methodt, midx + 1, MONO_METHOD_PARAMLIST); + else + lastp = paramt->rows + 1; + for (i = param_index; i < lastp; ++i) { + char *lit; + int crow; + guint32 param_cols [MONO_PARAM_SIZE]; + GList *list; + + list = dis_get_custom_attrs (m, MONO_TOKEN_PARAM_DEF | i); + + mono_metadata_decode_row (paramt, i-1, param_cols, MONO_PARAM_SIZE); + if (!(param_cols[MONO_PARAM_FLAGS] & PARAM_ATTRIBUTE_HAS_DEFAULT)) { + if(list != NULL) + fprintf (output, "\t.param [%d]\n", param_cols[MONO_PARAM_SEQUENCE]); + } else { + fprintf (output, "\t.param [%d] = ", param_cols[MONO_PARAM_SEQUENCE]); + + if ((crow = mono_metadata_get_constant_index(m, MONO_TOKEN_PARAM_DEF | i, 0))) { + guint32 const_cols [MONO_CONSTANT_SIZE]; + mono_metadata_decode_row( &m->tables[MONO_TABLE_CONSTANT], crow-1, const_cols, MONO_CONSTANT_SIZE); + lit = get_constant (m, (MonoTypeEnum)const_cols [MONO_CONSTANT_TYPE], const_cols [MONO_CONSTANT_VALUE]); + } + else { + lit = g_strdup ("not found"); + } + fprintf(output, "%s\n", lit); + g_free(lit); + } + dump_cattrs_list (list, "\t"); + } +} + +/** + * dis_method_list: + * @m: metadata context + * @start: starting index into the Method Table. + * @end: ending index into Method table. + * + * This routine displays the methods in the Method Table from @start to @end + */ +static void +dis_method_list (const char *klass_name, MonoImage *m, guint32 start, guint32 end, MonoGenericContainer *type_container) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_METHOD]; + guint32 cols [MONO_METHOD_SIZE]; + int i; + + if (end > t->rows){ + fprintf (output, "ERROR index out of range in methods"); + /*exit (1);*/ + end = t->rows; + } + + for (i = start; i < end; i++){ + MonoError error; + MonoMethodSignature *ms; + MonoGenericContainer *container; + char *flags, *impl_flags; + const char *sig, *method_name; + char *sig_str; + guint32 token; + + if (!should_include_method (i + 1)) + continue; + mono_metadata_decode_row (t, i, cols, MONO_METHOD_SIZE); + + flags = method_flags (cols [MONO_METHOD_FLAGS]); + impl_flags = get_method_impl_flags (cols [MONO_METHOD_IMPLFLAGS]); + + sig = mono_metadata_blob_heap (m, cols [MONO_METHOD_SIGNATURE]); + mono_metadata_decode_blob_size (sig, &sig); + + container = mono_metadata_load_generic_params (m, MONO_TOKEN_METHOD_DEF | (i + 1), type_container); + if (container) { + MonoError error; + mono_metadata_load_generic_param_constraints_checked (m, MONO_TOKEN_METHOD_DEF | (i + 1), container, &error); + g_assert (mono_error_ok (&error)); /*FIXME don't swallow the error message*/ + } else { + container = type_container; + } + + ms = mono_metadata_parse_method_signature_full (m, container, i + 1, sig, &sig, &error); + if (ms != NULL){ + sig_str = dis_stringify_method_signature (m, ms, i + 1, container, FALSE); + method_name = mono_metadata_string_heap (m, cols [MONO_METHOD_NAME]); + } else { + sig_str = NULL; + method_name = g_strdup (""); + mono_error_cleanup (&error); + } + + fprintf (output, " // method line %d\n", i + 1); + fprintf (output, " .method %s", flags); + + if ((cols [MONO_METHOD_FLAGS] & METHOD_ATTRIBUTE_PINVOKE_IMPL) && (cols [MONO_METHOD_RVA] == 0)) { + gchar *pi = pinvoke_info (m, i); + if (pi) { + fprintf (output, "%s", pi); + g_free (pi); + } + } + + fprintf (output, "\n %s", sig_str); + fprintf (output, " %s\n", impl_flags); + g_free (flags); + g_free (impl_flags); + + token = MONO_TOKEN_METHOD_DEF | (i + 1); + + fprintf (output, " {\n"); + dump_cattrs (m, token, " "); + dump_cattrs_for_type_params (m, MONO_TOKEN_METHOD_DEF | (i + 1), " "); + dump_cattrs_for_method_params (m, i, ms); + + fprintf (output, " // Method begins at RVA 0x%x\n", cols [MONO_METHOD_RVA]); + dump_declarative_security (m, OBJECT_TYPE_METHODDEF, i + 1, " "); + if (cols [MONO_METHOD_IMPLFLAGS] & METHOD_IMPL_ATTRIBUTE_NATIVE) + fprintf (output, " // Disassembly of native methods is not supported\n"); + else + dis_code (m, token, cols [MONO_METHOD_RVA], container); + if (klass_name) + fprintf (output, " } // end of method %s::%s\n\n", klass_name, method_name); + else + fprintf (output, " } // end of global method %s\n\n", method_name); + mono_metadata_free_method_signature (ms); + g_free (sig_str); + } +} + +typedef struct { + MonoTableInfo *t; + guint32 col_idx; + guint32 idx; + guint32 result; +} plocator_t; + +static int +table_locator (const void *a, const void *b) +{ + plocator_t *loc = (plocator_t *) a; + const char *bb = (const char *) b; + guint32 table_index = (bb - loc->t->base) / loc->t->row_size; + guint32 col; + + col = mono_metadata_decode_row_col (loc->t, table_index, loc->col_idx); + + if (loc->idx == col) { + loc->result = table_index; + return 0; + } + if (loc->idx < col) + return -1; + else + return 1; +} + +static void +dis_property_methods (MonoImage *m, guint32 prop, MonoGenericContainer *container) +{ + guint start, end; + MonoTableInfo *msemt = &m->tables [MONO_TABLE_METHODSEMANTICS]; + guint32 cols [MONO_METHOD_SEMA_SIZE]; + char *sig; + const char *type[] = {NULL, ".set", ".get", NULL, ".other"}; + + start = mono_metadata_methods_from_property (m, prop, &end); + for (; start < end; ++start) { + mono_metadata_decode_row (msemt, start, cols, MONO_METHOD_SEMA_SIZE); + if (!should_include_method (cols [MONO_METHOD_SEMA_METHOD])) + continue; + sig = dis_stringify_method_signature_full (m, NULL, cols [MONO_METHOD_SEMA_METHOD], container, TRUE, FALSE); + fprintf (output, "\t\t%s %s\n", type [cols [MONO_METHOD_SEMA_SEMANTICS]], sig); + g_free (sig); + } +} +static char* +dis_property_signature (MonoImage *m, guint32 prop_idx, MonoGenericContainer *container) +{ + MonoError error; + MonoTableInfo *propt = &m->tables [MONO_TABLE_PROPERTY]; + const char *ptr; + guint32 pcount, i; + guint32 cols [MONO_PROPERTY_SIZE]; + MonoType *type; + MonoType *param; + char *blurb, *qk; + const char *name; + int prop_flags; + GString *res = g_string_new (""); + + mono_metadata_decode_row (propt, prop_idx, cols, MONO_PROPERTY_SIZE); + name = mono_metadata_string_heap (m, cols [MONO_PROPERTY_NAME]); + prop_flags = cols [MONO_PROPERTY_FLAGS]; + ptr = mono_metadata_blob_heap (m, cols [MONO_PROPERTY_TYPE]); + mono_metadata_decode_blob_size (ptr, &ptr); + if (!(*ptr & 0x08)) + g_warning("incorrect signature in property blob: 0x%x", *ptr); + if (*ptr & 0x20) + g_string_append (res, "instance "); + ptr++; + pcount = mono_metadata_decode_value (ptr, &ptr); + type = mono_metadata_parse_type_checked (m, container, 0, FALSE, ptr, &ptr, &error); + if (type) { + blurb = dis_stringify_type (m, type, TRUE); + } else { + blurb = g_strdup_printf ("Invalid type due to %s", mono_error_get_message (&error)); + mono_error_cleanup (&error); + } + if (prop_flags & 0x0200) + g_string_append (res, "specialname "); + if (prop_flags & 0x0400) + g_string_append (res, "rtspecialname "); + qk = get_escaped_name (name); + g_string_append_printf (res, "%s %s (", blurb, qk); + g_free (qk); + g_free (blurb); + for (i = 0; i < pcount; i++) { + if (i) + g_string_append (res, ", "); + param = mono_metadata_parse_type_checked (m, container, 0, FALSE, ptr, &ptr, &error); + if (type) { + blurb = dis_stringify_param (m, param); + } else { + blurb = g_strdup_printf ("Invalid type due to %s", mono_error_get_message (&error)); + mono_error_cleanup (&error); + } + + g_string_append (res, blurb); + g_free (blurb); + } + g_string_append_c (res, ')'); + blurb = res->str; + g_string_free (res, FALSE); + return blurb; + +} + +static void +dis_property_list (MonoImage *m, guint32 typedef_row, MonoGenericContainer *container) +{ + guint start, end, i; + start = mono_metadata_properties_from_typedef (m, typedef_row, &end); + + for (i = start; i < end; ++i) { + char *sig = dis_property_signature (m, i, container); + fprintf (output, "\t.property %s\n\t{\n", sig); + dump_cattrs (m, MONO_TOKEN_PROPERTY | (i + 1), "\t\t"); + dis_property_methods (m, i, container); + fprintf (output, "\t}\n"); + g_free (sig); + } +} + +static char* +dis_event_signature (MonoImage *m, guint32 event_idx, MonoGenericContainer *container) +{ + MonoTableInfo *et = &m->tables [MONO_TABLE_EVENT]; + char *type, *result, *esname; + guint32 cols [MONO_EVENT_SIZE]; + int event_flags; + GString *res = g_string_new (""); + + mono_metadata_decode_row (et, event_idx, cols, MONO_EVENT_SIZE); + esname = get_escaped_name (mono_metadata_string_heap (m, cols [MONO_EVENT_NAME])); + type = get_typedef_or_ref (m, cols [MONO_EVENT_TYPE], container); + event_flags = cols [MONO_EVENT_FLAGS]; + + if (event_flags & 0x0200) + g_string_append (res, "specialname "); + if (event_flags & 0x0400) + g_string_append (res, "rtspecialname "); + g_string_append_printf (res, "%s %s", type, esname); + + g_free (type); + g_free (esname); + result = res->str; + g_string_free (res, FALSE); + return result; +} + +static void +dis_event_methods (MonoImage *m, guint32 event, MonoGenericContainer *container) +{ + guint start, end; + MonoTableInfo *msemt = &m->tables [MONO_TABLE_METHODSEMANTICS]; + guint32 cols [MONO_METHOD_SEMA_SIZE]; + char *sig; + const char *type = ""; + + start = mono_metadata_methods_from_event (m, event, &end); + for (; start < end; ++start) { + mono_metadata_decode_row (msemt, start, cols, MONO_METHOD_SEMA_SIZE); + if (!should_include_method (cols [MONO_METHOD_SEMA_METHOD])) + continue; + sig = dis_stringify_method_signature_full (m, NULL, cols [MONO_METHOD_SEMA_METHOD], container, TRUE, FALSE); + switch (cols [MONO_METHOD_SEMA_SEMANTICS]) { + case METHOD_SEMANTIC_OTHER: + type = ".other"; break; + case METHOD_SEMANTIC_ADD_ON: + type = ".addon"; break; + case METHOD_SEMANTIC_REMOVE_ON: + type = ".removeon"; break; + case METHOD_SEMANTIC_FIRE: + type = ".fire"; break; + default: + break; + } + fprintf (output, "\t\t%s %s\n", type, sig); + g_free (sig); + } +} + +static void +dis_event_list (MonoImage *m, guint32 typedef_row, MonoGenericContainer *container) +{ + guint start, end, i; + start = mono_metadata_events_from_typedef (m, typedef_row, &end); + + for (i = start; i < end; ++i) { + char *sig = dis_event_signature (m, i, container); + fprintf (output, "\t.event %s\n\t{\n", sig); + dump_cattrs (m, MONO_TOKEN_EVENT | (i + 1), "\t\t"); + dis_event_methods (m, i, container); + fprintf (output, "\t}\n"); + g_free (sig); + } +} + +static void +dis_interfaces (MonoImage *m, guint32 typedef_row, MonoGenericContainer *container) +{ + plocator_t loc; + guint start; + gboolean first_interface = 1; + guint32 cols [MONO_INTERFACEIMPL_SIZE]; + char *intf; + MonoTableInfo *table = &m->tables [MONO_TABLE_INTERFACEIMPL]; + + if (!table->base) + return; + + loc.t = table; + loc.col_idx = MONO_INTERFACEIMPL_CLASS; + loc.idx = typedef_row; + + if (!mono_binary_search (&loc, table->base, table->rows, table->row_size, table_locator)) + return; + + start = loc.result; + /* + * We may end up in the middle of the rows... + */ + while (start > 0) { + if (loc.idx == mono_metadata_decode_row_col (table, start - 1, MONO_INTERFACEIMPL_CLASS)) + start--; + else + break; + } + while (start < table->rows) { + mono_metadata_decode_row (table, start, cols, MONO_INTERFACEIMPL_SIZE); + if (cols [MONO_INTERFACEIMPL_CLASS] != loc.idx) + break; + intf = get_typedef_or_ref (m, cols [MONO_INTERFACEIMPL_INTERFACE], container); + if (first_interface) { + fprintf (output, " \timplements %s", intf); + first_interface = 0; + } else { + fprintf (output, ", %s", intf); + } + g_free (intf); + ++start; + } +} + +/** + * dis_type: + * @m: metadata context + * @n: index of type to disassemble + * @is_nested: nested type ? + * @forward: forward declarations? + * + * Disassembles the type whose index in the TypeDef table is @n. + */ +static void +dis_type (MonoImage *m, int n, int is_nested, int forward) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_TYPEDEF]; + guint32 cols [MONO_TYPEDEF_SIZE]; + guint32 cols_next [MONO_TYPEDEF_SIZE]; + const char *name, *nspace; + char *esname, *param; + MonoGenericContainer *container; + guint32 packing_size, class_size; + gboolean next_is_valid, last; + guint32 nested; + + if (!should_include_type (n + 1)) + return; + mono_metadata_decode_row (t, n, cols, MONO_TYPEDEF_SIZE); + + if (t->rows > n + 1) { + mono_metadata_decode_row (t, n + 1, cols_next, MONO_TYPEDEF_SIZE); + next_is_valid = 1; + } else + next_is_valid = 0; + + name = mono_metadata_string_heap (m, cols [MONO_TYPEDEF_NAME]); + nspace = mono_metadata_string_heap (m, cols [MONO_TYPEDEF_NAMESPACE]); + if (*nspace && !is_nested) { + char *esnspace; + esnspace = get_escaped_name (nspace); + fprintf (output, ".namespace %s\n{\n", esnspace); + g_free (esnspace); + } + + container = mono_metadata_load_generic_params (m, MONO_TOKEN_TYPE_DEF | (n + 1), NULL); + if (container) { + MonoError error; + mono_metadata_load_generic_param_constraints_checked (m, MONO_TOKEN_TYPE_DEF | (n + 1), container, &error); + g_assert (mono_error_ok (&error)); /*FIXME don't swallow the error message*/ + } + + esname = get_escaped_name (name); + if ((cols [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_CLASS_SEMANTIC_MASK) == TYPE_ATTRIBUTE_CLASS){ + fprintf (output, " .class %s%s", typedef_flags (cols [MONO_TYPEDEF_FLAGS]), esname); + + param = get_generic_param (m, container); + if (param) { + fprintf (output, "%s", param); + g_free (param); + } + fprintf (output, "\n"); + if (cols [MONO_TYPEDEF_EXTENDS]) { + char *base = get_typedef_or_ref (m, cols [MONO_TYPEDEF_EXTENDS], container); + fprintf (output, " \textends %s\n", base); + g_free (base); + } + } else { + fprintf (output, " .class interface %s%s", typedef_flags (cols [MONO_TYPEDEF_FLAGS]), esname); + + param = get_generic_param (m, container); + if (param) { + fprintf (output, "%s", param); + g_free (param); + } + fprintf (output, "\n"); + } + + g_free (esname); + dis_interfaces (m, n + 1, container); + fprintf (output, " {\n"); + if (!forward) { + dump_cattrs (m, MONO_TOKEN_TYPE_DEF | (n + 1), " "); + dump_cattrs_for_type_params (m, MONO_TOKEN_TYPE_DEF | (n + 1), " "); + dump_declarative_security (m, OBJECT_TYPE_TYPEDEF, (n + 1), " "); + + if (mono_metadata_packing_from_typedef (m, n + 1, &packing_size, &class_size)) { + fprintf (output, " .pack %d\n", packing_size); + fprintf (output, " .size %d\n", class_size); + } + /* + * The value in the table is always valid, we know we have fields + * if the value stored is different than the next record. + */ + + if (next_is_valid) + last = cols_next [MONO_TYPEDEF_FIELD_LIST] - 1; + else + last = m->tables [MONO_TABLE_FIELD].rows; + + if (cols [MONO_TYPEDEF_FIELD_LIST] && cols [MONO_TYPEDEF_FIELD_LIST] <= m->tables [MONO_TABLE_FIELD].rows) + dis_field_list (m, cols [MONO_TYPEDEF_FIELD_LIST] - 1, last, container); + fprintf (output, "\n"); + + if (next_is_valid) + last = cols_next [MONO_TYPEDEF_METHOD_LIST] - 1; + else + last = m->tables [MONO_TABLE_METHOD].rows; + + if (cols [MONO_TYPEDEF_METHOD_LIST] && cols [MONO_TYPEDEF_METHOD_LIST] <= m->tables [MONO_TABLE_METHOD].rows) + dis_method_list (name, m, cols [MONO_TYPEDEF_METHOD_LIST] - 1, last, container); + + dis_property_list (m, n, container); + dis_event_list (m, n, container); + } + + t = &m->tables [MONO_TABLE_NESTEDCLASS]; + nested = mono_metadata_nesting_typedef (m, n + 1, 1); + while (nested) { + dis_type (m, mono_metadata_decode_row_col (t, nested - 1, MONO_NESTED_CLASS_NESTED) - 1, 1, forward); + nested = mono_metadata_nesting_typedef (m, n + 1, nested + 1); + } + + fprintf (output, " } // end of class %s%s%s\n", nspace, *nspace? ".": "", name); + if (*nspace && !is_nested) + fprintf (output, "}\n"); + fprintf (output, "\n"); +} + + +/** + * dis_globals + * @m: metadata context + * + * disassembles all the global fields and methods + */ +static void +dis_globals (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_TYPEDEF]; + guint32 cols [MONO_TYPEDEF_SIZE]; + guint32 cols_next [MONO_TYPEDEF_SIZE]; + gboolean next_is_valid, last; + + mono_metadata_decode_row (t, 0, cols, MONO_TYPEDEF_SIZE); + + if (t->rows > 1) { + mono_metadata_decode_row (t, 1, cols_next, MONO_TYPEDEF_SIZE); + next_is_valid = 1; + } else + next_is_valid = 0; + + /* + * The value in the table is always valid, we know we have fields + * if the value stored is different than the next record. + */ + + if (next_is_valid) + last = cols_next [MONO_TYPEDEF_FIELD_LIST] - 1; + else + last = m->tables [MONO_TABLE_FIELD].rows; + + if (cols [MONO_TYPEDEF_FIELD_LIST] && cols [MONO_TYPEDEF_FIELD_LIST] <= m->tables [MONO_TABLE_FIELD].rows) + dis_field_list (m, cols [MONO_TYPEDEF_FIELD_LIST] - 1, last, NULL); + fprintf (output, "\n"); + + if (next_is_valid) + last = cols_next [MONO_TYPEDEF_METHOD_LIST] - 1; + else + last = m->tables [MONO_TABLE_METHOD].rows; + + if (cols [MONO_TYPEDEF_METHOD_LIST] && cols [MONO_TYPEDEF_METHOD_LIST] <= m->tables [MONO_TABLE_METHOD].rows) + dis_method_list (NULL, m, cols [MONO_TYPEDEF_METHOD_LIST] - 1, last, NULL); + +} + +static void dis_resources_worker (MonoImage *m, gboolean just_print) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_MANIFESTRESOURCE]; + int i; + + for (i = 0; i < t->rows; i++){ + guint32 cols [MONO_MANIFEST_SIZE]; + const char *name, *res; + guint32 size; + FILE* fp; + + mono_metadata_decode_row (t, i, cols, MONO_MANIFEST_SIZE); + name = mono_metadata_string_heap (m, cols [MONO_MANIFEST_NAME]); + + if (just_print) + fprintf (output, "%8x: %s", cols [MONO_MANIFEST_OFFSET], name); + + if (! (res = mono_image_get_resource (m, cols [MONO_MANIFEST_OFFSET], &size))) { + if (just_print) + fprintf (output, " (absent from image)\n"); + continue; + } + + if (just_print) { + fprintf (output, " (size %u)\n", size); + continue; + } + + if ( (fp = fopen (name, "ab")) ) { + if (ftell (fp) == 0) + fwrite (res, size, 1, fp); + else + g_warning ("Error creating managed resource - %s : File already exists.", name); + + fclose (fp); + } else + g_warning ("Error creating managed resource - %s : %s", name, g_strerror (errno)); + } +} + +static void +dis_mresource (MonoImage *m) +{ + dis_resources_worker (m, FALSE); +} + +static void +dis_presource (MonoImage *m) +{ + dis_resources_worker (m, TRUE); +} + +static char * +exported_type_flags (guint32 flags) +{ + static char buffer [1024]; + int visibility = flags & TYPE_ATTRIBUTE_VISIBILITY_MASK; + + buffer [0] = 0; + + if (flags & TYPE_ATTRIBUTE_FORWARDER) { + strcat (buffer, "forwarder "); + return buffer; + } + + strcat (buffer, map (visibility, visibility_map)); + return buffer; +} + +static char * +get_escaped_fullname (MonoImage *m, guint32 nspace_idx, guint32 name_idx) +{ + const char *name, *nspace; + char *fullname, *esfullname; + + nspace = mono_metadata_string_heap (m, nspace_idx); + name = mono_metadata_string_heap (m, name_idx); + + fullname = g_strdup_printf ("%s%s%s", nspace, *nspace ? "." : "", name); + esfullname = get_escaped_name (fullname); + + g_free (fullname); + + return esfullname; +} + +static void +dis_exported_types (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_EXPORTEDTYPE]; + int i; + + for (i = 1; i <= t->rows; i++) { + char *fullname; + guint32 impl, idx, type_token; + guint32 cols [MONO_EXP_TYPE_SIZE]; + + mono_metadata_decode_row (t, i - 1, cols, MONO_EXP_TYPE_SIZE); + + fullname = get_escaped_fullname (m, cols [MONO_EXP_TYPE_NAMESPACE], cols [MONO_EXP_TYPE_NAME]); + + fprintf (output, "\n"); + fprintf (output, ".class extern %s%s\n", exported_type_flags (cols [MONO_EXP_TYPE_FLAGS]), fullname); + fprintf (output, "{\n"); + + g_free (fullname); + + impl = cols [MONO_EXP_TYPE_IMPLEMENTATION]; + if (impl) { + idx = impl >> MONO_IMPLEMENTATION_BITS; + switch (impl & MONO_IMPLEMENTATION_MASK) { + case MONO_IMPLEMENTATION_FILE: + fprintf (output, " .file '%s'\n", + mono_metadata_string_heap (m, mono_metadata_decode_row_col (&m->tables [MONO_TABLE_FILE], idx - 1, MONO_FILE_NAME))); + break; + case MONO_IMPLEMENTATION_ASSEMBLYREF: + fprintf (output, " .assembly extern '%s'\n", + mono_metadata_string_heap (m, mono_metadata_decode_row_col (&m->tables [MONO_TABLE_ASSEMBLYREF], idx - 1, MONO_ASSEMBLYREF_NAME))); + break; + case MONO_IMPLEMENTATION_EXP_TYPE: + fullname = get_escaped_fullname ( + m, + mono_metadata_decode_row_col (&m->tables [MONO_TABLE_EXPORTEDTYPE], idx - 1, MONO_EXP_TYPE_NAMESPACE), + mono_metadata_decode_row_col (&m->tables [MONO_TABLE_EXPORTEDTYPE], idx - 1, MONO_EXP_TYPE_NAME)); + fprintf (output, " .class extern %s\n", fullname); + g_free (fullname); + break; + default: + g_assert_not_reached (); + break; + } + } + + type_token = cols [MONO_EXP_TYPE_TYPEDEF]; + if (type_token) + fprintf (output, " .class 0x%08x\n", type_token | MONO_TOKEN_TYPE_DEF); + + fprintf (output, "}\n"); + } +} + +/** + * dis_types: + * @m: metadata context + * + * disassembles all types in the @m context + */ +static void +dis_types (MonoImage *m, int forward) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_TYPEDEF]; + int i; + guint32 flags; + + dis_globals (m); + + for (i = 1; i < t->rows; i++) { + flags = mono_metadata_decode_row_col (t, i, MONO_TYPEDEF_FLAGS); + flags &= TYPE_ATTRIBUTE_VISIBILITY_MASK; + if (flags == TYPE_ATTRIBUTE_PUBLIC || flags == TYPE_ATTRIBUTE_NOT_PUBLIC) + dis_type (m, i, 0, forward); + } +} + +static const char * +get_uninitialized_data_type (guint32 size) +{ + switch (size) { + case 1: + return "int8"; + case 2: + return "int16"; + case 4: + return "int32"; + case 8: + return "int64"; + default: + g_error ("get_uninitialized_data_type for size: %d\n", size); + } + return NULL; +} + +/** + * dis_data: + * @m: metadata context + * + * disassembles all data blobs references in the FieldRVA table in the @m context + */ +static void +dis_data (MonoImage *m) +{ + MonoTableInfo *t = &m->tables [MONO_TABLE_FIELDRVA]; + MonoTableInfo *ft = &m->tables [MONO_TABLE_FIELD]; + int i, b; + const char *rva, *sig; + guint32 size; + gint align; + guint32 cols [MONO_FIELD_RVA_SIZE]; + MonoType *type; + + for (i = 0; i < t->rows; i++) { + MonoError error; + mono_metadata_decode_row (t, i, cols, MONO_FIELD_RVA_SIZE); + rva = mono_image_rva_map (m, cols [MONO_FIELD_RVA_RVA]); + sig = mono_metadata_blob_heap (m, mono_metadata_decode_row_col (ft, cols [MONO_FIELD_RVA_FIELD] -1, MONO_FIELD_SIGNATURE)); + mono_metadata_decode_value (sig, &sig); + /* FIELD signature == 0x06 */ + g_assert (*sig == 0x06); + type = mono_metadata_parse_type_checked (m, NULL, 0, FALSE, sig + 1, &sig, &error); + if (!type) { + fprintf (output, "// invalid field %d due to %s\n", i, mono_error_get_message (&error)); + mono_error_cleanup (&error); + continue; + } + mono_class_init (mono_class_from_mono_type (type)); + size = mono_type_size (type, &align); + + if (rva) { + fprintf (output, ".data D_%08x = bytearray (", cols [MONO_FIELD_RVA_RVA]); + for (b = 0; b < size; ++b) { + if (!(b % 16)) + fprintf (output, "\n\t"); + fprintf (output, " %02X", rva [b] & 0xff); + } + fprintf (output, ") // size: %d\n", size); + } else + fprintf (output, ".data D_%08x = %s [%d]\n", + cols [MONO_FIELD_RVA_RVA], get_uninitialized_data_type (size), size); + } +} + +struct { + const char *name; + int table; + void (*dumper) (MonoImage *m); +} table_list [] = { + { "--assembly", MONO_TABLE_ASSEMBLY, dump_table_assembly }, + { "--assemblyref", MONO_TABLE_ASSEMBLYREF, dump_table_assemblyref }, + { "--classlayout", MONO_TABLE_CLASSLAYOUT, dump_table_class_layout }, + { "--constant", MONO_TABLE_CONSTANT, dump_table_constant }, + { "--customattr", MONO_TABLE_CUSTOMATTRIBUTE, dump_table_customattr }, + { "--declsec", MONO_TABLE_DECLSECURITY, dump_table_declsec }, + { "--event", MONO_TABLE_EVENT, dump_table_event }, + { "--exported", MONO_TABLE_EXPORTEDTYPE, dump_table_exported }, + { "--fields", MONO_TABLE_FIELD, dump_table_field }, + { "--file", MONO_TABLE_FILE, dump_table_file }, + { "--genericpar", MONO_TABLE_GENERICPARAM, dump_table_genericpar }, + { "--interface", MONO_TABLE_INTERFACEIMPL, dump_table_interfaceimpl }, + { "--manifest", MONO_TABLE_MANIFESTRESOURCE, dump_table_manifest }, + { "--marshal", MONO_TABLE_FIELDMARSHAL, dump_table_field_marshal }, + { "--memberref", MONO_TABLE_MEMBERREF, dump_table_memberref }, + { "--method", MONO_TABLE_METHOD, dump_table_method }, + { "--methodimpl", MONO_TABLE_METHODIMPL, dump_table_methodimpl }, + { "--methodsem", MONO_TABLE_METHODSEMANTICS, dump_table_methodsem }, + { "--methodspec", MONO_TABLE_METHODSPEC, dump_table_methodspec }, + { "--moduleref", MONO_TABLE_MODULEREF, dump_table_moduleref }, + { "--module", MONO_TABLE_MODULE, dump_table_module }, + { "--mresources", 0, dis_mresource }, + { "--presources", 0, dis_presource }, + { "--nested", MONO_TABLE_NESTEDCLASS, dump_table_nestedclass }, + { "--param", MONO_TABLE_PARAM, dump_table_param }, + { "--parconst", MONO_TABLE_GENERICPARAMCONSTRAINT, dump_table_parconstraint }, + { "--property", MONO_TABLE_PROPERTY, dump_table_property }, + { "--propertymap", MONO_TABLE_PROPERTYMAP, dump_table_property_map }, + { "--typedef", MONO_TABLE_TYPEDEF, dump_table_typedef }, + { "--typeref", MONO_TABLE_TYPEREF, dump_table_typeref }, + { "--typespec", MONO_TABLE_TYPESPEC, dump_table_typespec }, + { "--implmap", MONO_TABLE_IMPLMAP, dump_table_implmap }, + { "--fieldrva", MONO_TABLE_FIELDRVA, dump_table_fieldrva }, + { "--standalonesig", MONO_TABLE_STANDALONESIG, dump_table_standalonesig }, + { "--methodptr", MONO_TABLE_METHOD_POINTER, dump_table_methodptr }, + { "--fieldptr", MONO_TABLE_FIELD_POINTER, dump_table_fieldptr }, + { "--paramptr", MONO_TABLE_PARAM_POINTER, dump_table_paramptr }, + { "--eventptr", MONO_TABLE_EVENT_POINTER, dump_table_eventptr }, + { "--propertyptr", MONO_TABLE_PROPERTY_POINTER, dump_table_propertyptr }, + { "--blob", 0, dump_stream_blob }, + { "--strings", 0, dump_stream_strings }, + { "--userstrings", 0, dump_stream_us }, + { NULL, -1, } +}; + +/** + * disassemble_file: + * @file: file containing CIL code. + * + * Disassembles the @file file. + */ +static int +disassemble_file (const char *file) +{ + MonoImageOpenStatus status; + MonoImage *img; + + img = mono_image_open (file, &status); + if (!img) { + fprintf (stderr, "Error while trying to process %s\n", file); + return 1; + } else { + /* FIXME: is this call necessary? */ + mono_assembly_load_from_full (img, file, &status, FALSE); + } + + setup_filter (img); + + if (dump_table != -1){ + (*table_list [dump_table].dumper) (img); + } else { + dump_header_data (img); + + dis_directive_assemblyref (img); + dis_directive_assembly (img); + dis_directive_file (img); + dis_directive_mresource (img); + dis_directive_module (img); + dis_directive_moduleref (img); + dis_exported_types (img); + dis_nt_header (img); + if (dump_managed_resources) + dis_mresource (img); + if (dump_forward_decls) { + fprintf (output, "// *************** Forward Declarations for Classes ***************\n\n"); + dis_types (img, 1); + fprintf (output, "// *************** End-Of Forward Declarations for Classes ***************\n\n"); + } + dis_types (img, 0); + dis_data (img); + } + + mono_image_close (img); + return 0; +} + +typedef struct { + int size; + int count; + int *elems; +} TableFilter; + +typedef struct { + char *name; + char *guid; + TableFilter types; + TableFilter fields; + TableFilter methods; +} ImageFilter; + +static GList *filter_list = NULL; +static ImageFilter *cur_filter = NULL; + +static void +setup_filter (MonoImage *image) +{ + ImageFilter *ifilter; + GList *item; + const char *name = mono_image_get_name (image); + + for (item = filter_list; item; item = item->next) { + ifilter = (ImageFilter *)item->data; + if (strcmp (ifilter->name, name) == 0) { + cur_filter = ifilter; + return; + } + } + cur_filter = NULL; +} + +static int +int_cmp (const void *e1, const void *e2) +{ + const int *i1 = (const int *)e1; + const int *i2 = (const int *)e2; + return *i1 - *i2; +} + +static gboolean +table_includes (TableFilter *tf, int idx) +{ + if (!tf->count) + return FALSE; + return mono_binary_search (&idx, tf->elems, tf->count, sizeof (int), int_cmp) != NULL; +} + +static gboolean +should_include_type (int idx) +{ + if (!cur_filter) + return TRUE; + return table_includes (&cur_filter->types, idx); +} + +static gboolean +should_include_method (int idx) +{ + if (!cur_filter) + return TRUE; + return table_includes (&cur_filter->methods, idx); +} + +static gboolean +should_include_field (int idx) +{ + if (!cur_filter) + return TRUE; + return table_includes (&cur_filter->fields, idx); +} + +static ImageFilter* +add_filter (const char *name) +{ + ImageFilter *ifilter; + GList *item; + + for (item = filter_list; item; item = item->next) { + ifilter = (ImageFilter *)item->data; + if (strcmp (ifilter->name, name) == 0) + return ifilter; + } + ifilter = g_new0 (ImageFilter, 1); + ifilter->name = g_strdup (name); + filter_list = g_list_prepend (filter_list, ifilter); + return ifilter; +} + +static void +add_item (TableFilter *tf, int val) +{ + if (tf->count >= tf->size) { + if (!tf->size) { + tf->size = 8; + tf->elems = (int *)g_malloc (sizeof (int) * tf->size); + } else { + tf->size *= 2; + tf->elems = (int *)g_realloc (tf->elems, sizeof (int) * tf->size); + } + } + tf->elems [tf->count++] = val; +} + +static void +sort_filter_elems (void) +{ + ImageFilter *ifilter; + GList *item; + + for (item = filter_list; item; item = item->next) { + ifilter = (ImageFilter *)item->data; + qsort (ifilter->types.elems, ifilter->types.count, sizeof (int), int_cmp); + qsort (ifilter->fields.elems, ifilter->fields.count, sizeof (int), int_cmp); + qsort (ifilter->methods.elems, ifilter->methods.count, sizeof (int), int_cmp); + } +} + +static void +load_filter (const char* filename) +{ + FILE *file; + char buf [1024]; + char *p, *s, *endptr; + int line = 0; + ImageFilter *ifilter = NULL; + int value = 0; + + if (!(file = fopen (filename, "r"))) { + g_print ("Cannot open filter file '%s'\n", filename); + exit (1); + } + while (fgets (buf, sizeof (buf), file) != NULL) { + ++line; + s = buf; + while (*s && g_ascii_isspace (*s)) ++s; + switch (*s) { + case 0: + case '#': + break; + case '[': + p = strchr (s, ']'); + if (!p) + g_error ("No matching ']' in filter at line %d\n", line); + *p = 0; + ifilter = add_filter (s + 1); + break; + case 'T': + if (!ifilter) + g_error ("Invalid format in filter at line %d\n", line); + if ((s [1] != ':') || !(value = strtol (s + 2, &endptr, 0)) || (endptr == s + 2)) + g_error ("Invalid type number in filter at line %d\n", line); + add_item (&ifilter->types, value); + break; + case 'M': + if (!ifilter) + g_error ("Invalid format in filter at line %d\n", line); + if ((s [1] != ':') || !(value = strtol (s + 2, &endptr, 0)) || (endptr == s + 2)) + g_error ("Invalid method number in filter at line %d\n", line); + add_item (&ifilter->methods, value); + break; + case 'F': + if (!ifilter) + g_error ("Invalid format in filter at line %d\n", line); + if ((s [1] != ':') || !(value = strtol (s + 2, &endptr, 0)) || (endptr == s + 2)) + g_error ("Invalid field number in filter at line %d\n", line); + add_item (&ifilter->fields, value); + break; + default: + g_error ("Invalid format in filter at line %d\n", line); + } + } + fclose (file); + sort_filter_elems (); +} + + +static gboolean +try_load_from (MonoAssembly **assembly, + const gchar *path1, const gchar *path2, + const gchar *path3, const gchar *path4, gboolean refonly, + MonoAssemblyCandidatePredicate predicate, gpointer user_data) +{ + gchar *fullpath; + + *assembly = NULL; + fullpath = g_build_filename (path1, path2, path3, path4, NULL); + if (g_file_test (fullpath, G_FILE_TEST_IS_REGULAR)) + *assembly = mono_assembly_open_predicate (fullpath, refonly, FALSE, predicate, user_data, NULL); + + g_free (fullpath); + return (*assembly != NULL); +} + +static MonoAssembly * +real_load (gchar **search_path, const gchar *culture, const gchar *name, gboolean refonly, + MonoAssemblyCandidatePredicate predicate, gpointer user_data) +{ + MonoAssembly *result = NULL; + gchar **path; + gchar *filename; + const gchar *local_culture; + gint len; + + if (!culture || *culture == '\0') { + local_culture = ""; + } else { + local_culture = culture; + } + + filename = g_strconcat (name, ".dll", NULL); + len = strlen (filename); + + for (path = search_path; *path; path++) { + if (**path == '\0') + continue; /* Ignore empty ApplicationBase */ + + /* See test cases in bug #58992 and bug #57710 */ + /* 1st try: [culture]/[name].dll (culture may be empty) */ + strcpy (filename + len - 4, ".dll"); + if (try_load_from (&result, *path, local_culture, "", filename, refonly, predicate, user_data)) + break; + + /* 2nd try: [culture]/[name].exe (culture may be empty) */ + strcpy (filename + len - 4, ".exe"); + if (try_load_from (&result, *path, local_culture, "", filename, refonly, predicate, user_data)) + break; + + /* 3rd try: [culture]/[name]/[name].dll (culture may be empty) */ + strcpy (filename + len - 4, ".dll"); + if (try_load_from (&result, *path, local_culture, name, filename, refonly, predicate, user_data)) + break; + + /* 4th try: [culture]/[name]/[name].exe (culture may be empty) */ + strcpy (filename + len - 4, ".exe"); + if (try_load_from (&result, *path, local_culture, name, filename, refonly, predicate, user_data)) + break; + } + + g_free (filename); + return result; +} + +/* + * Try to load referenced assemblies from assemblies_path. + */ +static MonoAssembly * +monodis_preload (MonoAssemblyName *aname, + gchar **assemblies_path, + gpointer user_data) +{ + MonoAssembly *result = NULL; + gboolean refonly = GPOINTER_TO_UINT (user_data); + + if (assemblies_path && assemblies_path [0] != NULL) { + result = real_load (assemblies_path, aname->culture, aname->name, refonly, NULL, NULL); + } + + return result; +} + +static GList *loaded_assemblies = NULL; + +static void +monodis_assembly_load_hook (MonoAssembly *assembly, gpointer user_data) +{ + loaded_assemblies = g_list_prepend (loaded_assemblies, assembly); +} + +static MonoAssembly * +monodis_assembly_search_hook (MonoAssemblyName *aname, gpointer user_data) +{ + GList *tmp; + + for (tmp = loaded_assemblies; tmp; tmp = tmp->next) { + MonoAssembly *ass = (MonoAssembly *)tmp->data; + if (mono_assembly_names_equal (aname, &ass->aname)) + return ass; + } + return NULL; +} + +static void +usage (void) +{ + GString *args = g_string_new ("[--output=filename] [--filter=filename]\n"); + int i; + + g_string_append (args, "[--help] [--mscorlib] [--show-tokens] [--show-method-tokens]\n"); + + for (i = 0; table_list [i].name != NULL; i++){ + g_string_append (args, "["); + g_string_append (args, table_list [i].name); + g_string_append (args, "] "); + if (((i-2) % 5) == 0) + g_string_append_c (args, '\n'); + } + g_string_append (args, "[--forward-decls]"); + fprintf (stderr, + "monodis -- Mono Common Intermediate Language Disassembler\n" + "Usage is: monodis %s file ..\n", args->str); + exit (1); +} + +static void +thread_state_init (MonoThreadUnwindState *ctx) +{ +} + +int +main (int argc, char *argv []) +{ + MonoThreadInfoRuntimeCallbacks ticallbacks; + + GList *input_files = NULL, *l; + int i, j; + + output = stdout; + init_key_table (); + for (i = 1; i < argc; i++){ + if (argv [i][0] == '-'){ + if (argv [i][1] == 'h') + usage (); + else if (argv [i][1] == 'd') + dump_header_data_p = TRUE; + else if (strcmp (argv [i], "--mscorlib") == 0) { + substitute_with_mscorlib_p = TRUE; + continue; + } else if (strcmp (argv [i], "--show-method-tokens") == 0) { + show_method_tokens = TRUE; + continue; + } else if (strcmp (argv [i], "--show-tokens") == 0) { + show_tokens = TRUE; + continue; + } else if (strncmp (argv [i], "--output=", 9) == 0) { + output = fopen (argv [i]+9, "w"); + if (output == NULL) { + fprintf (stderr, "Can't open output file `%s': %s\n", + argv [i]+9, strerror (errno)); + exit (1); + } + dump_managed_resources = TRUE; + continue; + } else if (strncmp (argv [i], "--filter=", 9) == 0) { + load_filter (argv [i]+9); + continue; + } else if (strcmp (argv [i], "--forward-decls") == 0) { + dump_forward_decls = TRUE; + continue; + } else if (strcmp (argv [i], "--help") == 0) + usage (); + for (j = 0; table_list [j].name != NULL; j++) { + if (strcmp (argv [i], table_list [j].name) == 0) + dump_table = j; + } + if (dump_table < 0) + usage (); + } else + input_files = g_list_append (input_files, argv [i]); + } + + if (input_files == NULL) + usage (); + + CHECKED_MONO_INIT (); + mono_counters_init (); + mono_tls_init_runtime_keys (); + memset (&ticallbacks, 0, sizeof (ticallbacks)); + ticallbacks.thread_state_init = thread_state_init; +#ifndef HOST_WIN32 + mono_w32handle_init (); +#endif + mono_thread_info_runtime_init (&ticallbacks); + + mono_install_assembly_load_hook (monodis_assembly_load_hook, NULL); + mono_install_assembly_search_hook (monodis_assembly_search_hook, NULL); + + /* + * If we just have one file, use the corlib version it requires. + */ + if (!input_files->next) { + char *filename = (char *)input_files->data; + + mono_init_from_assembly (argv [0], filename); + + mono_install_assembly_preload_hook (monodis_preload, GUINT_TO_POINTER (FALSE)); + + return disassemble_file (filename); + } else { + mono_init (argv [0]); + + i = 0; + for (l = input_files; l; l = l->next) + if (disassemble_file ((const char *)l->data) == 1) i = 1; + return i; + } + + return 0; +} diff --git a/unity-2019.4.24f1-mbe/mono/dis/meta.h b/unity-2019.4.24f1-mbe/mono/dis/meta.h new file mode 100644 index 000000000..869b07590 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/dis/meta.h @@ -0,0 +1,8 @@ +#include +#include +#include +#include +#include +#include +#include + diff --git a/unity-2019.4.24f1-mbe/mono/dis/monodis.1 b/unity-2019.4.24f1-mbe/mono/dis/monodis.1 new file mode 100644 index 000000000..5783a12fd --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/dis/monodis.1 @@ -0,0 +1,213 @@ +.\" +.\" monodis manual page. +.\" (C) Ximian, Inc. +.\" Author: +.\" Miguel de Icaza (miguel@gnu.org) +.\" +.TH Mono "Mono 1.1.x" +.SH NAME +monodis \- CIL image content dumper and disassembler. +.SH SYNOPSIS +.PP +.B monodis +[\-h] [\-\-help] +[\-\-output=FILENAME] +[\-\-mscorlib] +[\-\-assembly] +[\-\-assemblyref] +[\-\-classlayout] +[\-\-constant] +[\-\-customattr] +[\-\-declsec] +[\-\-event] +[\-\-exported] +[\-\-fields] +[\-\-file] +[\-\-forward-decls] +[\-\-genericpar] +[\-\-implmap] +[\-\-interface] +[\-\-manifest] +[\-\-marshal] +[\-\-memberref] +[\-\-method] +[\-\-methodimpl] +[\-\-methodsem] +[\-\-methodspec] +[\-\-module] +[\-\-moduleref] +[\-\-mresources] +[\-\-presources] +[\-\-nested] +[\-\-param] +[\-\-parconst] +[\-\-property] +[\-\-propertymap] +[\-\-standalonesig] +[\-\-typedef] +[\-\-typeref] +[\-\-typespec] +[\-\-blob] +[\-\-strings] +[\-\-userstrings] +[FILES...] +.SH DESCRIPTION +The \fImonodis\fP program is used to dump the contents an ECMA/ISO CIL +image (contained in .EXE files that contain extended PE/COFF CIL +code). +.PP +To roundtrip assemblies using ilasm, it is best to use the --output +argument, as that will make monodis save the embedded resources in +files that can later be properly embedded back by ilasm. +.PP +Additionally, the tool can be used to dump the contents of the various +ECMA CIL metadata tables. +.SH OPTIONS +The following Generic options are supported: +.TP +.I "--help", "-h" +Displays usage instructions. +.TP +.I "--output=FILENAME" +Write output into \fIFILENAME\fP and dump any embedded managed resources. +.TP +.I "--mscorlib" +For non-corlib assemblies, use "mscorlib" as the assembly name. This +is useful for round-tripping the IL with ilasm. +.TP +.I "--show-method-tokens" +Display tokens for disassembled methods. +.TP +.I "--show-tokens" +Display tokens for strings, types, methods, fields, etc. +.SH OPTIONS TO DISPLAY METADATA TABLES +The following options are used to display metadata tables instead of +disassembling the CIL image. +.TP +.I "--assembly" +Dumps the contents of the Assembly table. +.TP +.I "--assemblyref" +Dumps the contents of the AssemblyRef table. +.TP +.I "--classlayout" +Dumps the contents of the ClassLayout table. +.TP +.I "--constant" +Dumps the contents of the Constant table. +.TP +.I "--customattr" +Dumps the contents of the CustomAttribute table. +.TP +.I "--declsec" +Dumps the contents of the DeclSec table. +.TP +.I "--event" +Dumps the contents of the Event table. +.TP +.I "--exported" +Dumps the contents of the ExportedType table. +.TP +.I "--fields" +Dumps the contents of the Field table. +.TP +.I "--file" +Dumps the contents of the File table. +.TP +.I "--forward-decls" +Dumps forward declarations for classes. +.TP +.I "--genericpar" +Dumps the contents of the GenericParam table. +.TP +.I "--implmap" +Dumps the contents of the ImplMap table. +.TP +.I "--interface" +Dumps the contents of the InterfaceImpl table. +.TP +.I "--manifest" +Dumps the contents of the ManifestResource table. +.TP +.I "--marshal" +Dumps the contents of the FieldMarshal table. +.TP +.I "--memberref" +Dumps the contents of the MemberRef table. +.TP +.I "--method" +Dumps the contents of the MethodDef table. +.TP +.I "--methodimpl" +Dumps the contents of the MethodImpl table. +.TP +.I "--methodspec" +Dumps the contents of the MethodSpec table. +.TP +.I "--methodsem" +Dumps the contents of the MethodSemantics table. +.TP +.I "--module" +Dumps the contents of the Module table. +.TP +.I "--moduleref" +Dumps the contents of the ModuleRef table. +.TP +.I "--mresources" +Saves all the managed resources embedded in the assembly into the +current directory. To get a list of the embedded resources use the +--manifest option. +.TP +.I "--presources" +Prints offsets and names of manifest resources embedded in the assembly. +.TP +.I "--nested" +Dumps the contents of the NestedClass table. +.TP +.I "--param" +Dumps the contents of the Param table. +.TP +.I "--parconst" +Dumps the contents of the GenericParameterConstraint table. +.TP +.I "--property" +Dumps the contents of the Property table. +.TP +.I "--propertymap" +Dumps the contents of the PropertyMap table. +.TP +.I "--standalonesig" +Dumps the contents of the StandAloneSig table. +.TP +.I "--typedef" +Dumps the contents of the TypeDef table. +.TP +.I "--typespec" +Dumps the contents of the TypeSpec table. +.TP +.I "--typeref" +Dumps the contents of the TypeRef table. +.TP +.I "--blob" +Dumps the entire contents of the blob stream as hex. +.TP +.I "--strings" +Dumps the contents of the Strings heap. +.TP +.I "--userstrings" +Dumps the contents of the User-Strings heap +.PP +If no flags are specified the program dumps the content of the image +in a format that can be used to rountrip the code. +.PP +.SH ENVIRONMENT VARIABLES +.TP +.I "MONO_PATH" +Provides a search path to mono and mint where to look for library files. +Directories are separated by the platform path separator (colons on unix). Example: +.B /home/username/lib:/usr/local/mono/lib +.PP +.SH AUTHOR +monodis was written by Miguel de Icaza, Paolo Molaro and Dietmar Maurer. +.SH SEE ALSO +.BR pedump(1) diff --git a/unity-2019.4.24f1-mbe/mono/dis/push-pop.h b/unity-2019.4.24f1-mbe/mono/dis/push-pop.h new file mode 100644 index 000000000..3ededc256 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/dis/push-pop.h @@ -0,0 +1,265 @@ +/* Poping */ +/* 1 bit */ +#define Pop0 1 +/* 2 bits */ +#define Pop1 2 +/* 3 bits */ +#define PopI 8 +/* 1 bit */ +#define PopI8 64 +/* 1 bit */ +#define Pop8 128 +/* 1 bit */ +#define PopR4 256 +/* 1 bit */ +#define PopR8 512 +/* 1 bit */ +#define PopRef 1024 +/* 1 bit */ +#define VarPop 2048 + +/* Pushing */ +#define Push0 1 +#define PushI 2 +#define PushI8 4 +#define PushR4 8 +#define PushR8 16 +#define PushRef 32 +#define VarPush 64 +#define Push1 128 + +/* + * dis-cil.c: Disassembles CIL byte codes + * + * Author: + * Miguel de Icaza (miguel@ximian.com) + * + * (C) 2001 Ximian, Inc. + */ +#include +#include +#include +#include "meta.h" +#include "dump.h" +#include "dis-cil.h" + +/* Poping */ +/* 1 bit */ +#define Pop0 1 +/* 2 bits */ +#define Pop1 2 +/* 3 bits */ +#define PopI 8 +/* 1 bit */ +#define PopI8 64 +/* 1 bit */ +#define Pop8 128 +/* 1 bit */ +#define PopR4 256 +/* 1 bit */ +#define PopR8 512 +/* 1 bit */ +#define PopRef 1024 +/* 1 bit */ +#define VarPop 2048 + +/* Pushing */ +#define Push0 1 +#define PushI 2 +#define PushI8 4 +#define PushR4 8 +#define PushR8 16 +#define PushRef 32 +#define VarPush 64 +#define Push1 128 + +enum { + InlineBrTarget, + InlineField, + InlineI, + InlineI8, + InlineMethod, + InlineNone, + InlineR, + InlineSig, + InlineString, + InlineSwitch, + InlineTok, + InlineType, + InlineVar, + ShortInlineBrTarget, + ShortInlineI, + ShortInlineR, + ShortInlineVar +}; + +#define OPDEF(a,b,c,d,e,f,g,h,i,j) \ + { b, c, d, e, g, h, i }, + +typedef struct { + char *name; + int pop, push; + int argument; + int bytes; + unsigned char o1, o2; +} opcode_t; + +static opcode_t opcodes [300] = { +#include "mono/cil/opcode.def" +}; + +void +disassemble_cil (MonoMetadata *m, const unsigned char *start, int size) +{ + const unsigned char *end = start + size; + const unsigned char *ptr = start; + opcode_t *entry; + + while (ptr < end){ + if (*ptr == 0xfe){ + ptr++; + entry = &opcodes [*ptr + 256]; + } else + entry = &opcodes [*ptr]; + + ptr++; + + fprintf (output, "\tIL_%04x: %s ", ptr - start, entry->name); + switch (entry->argument){ + case InlineBrTarget: { + gint target = *(gint32 *) ptr; + fprintf (output, "IL_%04x", ptr + 4 + target); + ptr += 4; + break; + } + + case InlineField: { + token = *(guint32 *) ptr; + fprintf (output, "fieldref-0x%08x", token); + ptr += 4; + break; + } + + case InlineI: { + int value = *(int *) ptr; + + fprintf (output, "%d", value); + ptr += 4; + break; + } + + case InlineI8: { + gint64 top = *(guint64 *) value; + + fprintf (output, "%ld", top); + ptr += 8; + break; + } + + case InlineMethod: { + token = *(guint32 *) ptr; + fprintf (output, "method-0x%08x", token); + ptr += 4; + break; + } + + case InlineNone: + break; + + case InlineR: { + double r = *(double *) ptr; + fprintf (output, "%g", r); + ptr += 8; + break; + } + + case InlineSig: { + guint32 token = *(guint32 *) ptr; + fprintf (output, "signature-0x%08x", token); + ptr += 4; + break; + } + + case InlineString: { + guint32 token = *(guint32 *) ptr; + + fprintf (output, "string-%0x08x", token); + ptr += 4; + break; + } + + case InlineSwitch: { + guint32 count = *(guint32 *) ptr; + guint32 i; + + ptr += 4; + fprintf (output, "(\n\t\t\t"); + for (i = 0; i < count; i++){ + fprintf (output, "IL_%x", *(guint32 *) ptr); + ptr += 4; + } + fprintf (output, "\t\t\t)"); + break; + } + + case InlineTok: { + guint32 token = *(guint32 *) ptr; + + fprintf (output, "TOKEN_%08x", token); + ptr += 4; + break; + } + + case InlineType: { + guint32 token = *(guint32 *) ptr; + + fprintf (output, "Type-%08x", token); + ptr += 4; + break; + } + + case InlineVar: { + gint16 var_idx = *(gint16 *) ptr; + + fprintf (output, "variable-%d\n", var_idx); + ptr += 2; + break; + } + + case ShortInlineBrTarget: { + signed char x = *ptr; + + fprintf (output, "IL_%04x", ptr - start + 1 + x); + ptr++: + break; + } + + case ShortInlineI: { + char x = *ptr; + + fprintf (output, "0x%02x", x); + ptr++; + break; + } + + case ShortInlineR: { + float f = *(float *) ptr; + + fprintf (output, "%g", (double) f); + ptr += 4; + break; + } + + case ShortInlineVar: { + signed char x = *ptr; + + fprintf (output, "Varidx-%d", (int) x); + ptr++; + break; + }m + + } + + fprintf (output, "\n"); + } +} diff --git a/unity-2019.4.24f1-mbe/mono/dis/tests/ambiguous-gen-params.il b/unity-2019.4.24f1-mbe/mono/dis/tests/ambiguous-gen-params.il new file mode 100644 index 000000000..808fac60b --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/dis/tests/ambiguous-gen-params.il @@ -0,0 +1,53 @@ +// Test for type/method having type parameters with identical names +// +// Roundtrip with ilasm2/monodis should yield same +// meth (!!0 _a1, !!1 _a2, !1 _t, !!B _b) + +.assembly extern mscorlib +{ + .ver 2:0:0:0 +} +.assembly 'ambiguous-gen-params' +{ + .custom instance void class [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( + 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx + 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. + + .ver 0:0:0:0 +} +.module 'ambiguous-gen-params.dll' + + + .class private auto ansi beforefieldinit g`1 + extends [mscorlib]System.Object + { + + .method public hidebysig specialname rtspecialname + instance default void .ctor () cil managed + { + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void object::.ctor() + IL_0006: ret + } + + .method public static hidebysig + default void meth (!!0 _a1, !!1 _a2, !1 _t, !!B _b) cil managed + { + ret + } + + .method public static hidebysig + default !!A foo (!!B _b, !1 _t, !!A _a) cil managed + { + .maxstack 1 + .locals init ( + !!A V_0) + IL_0000: ldloca.s 0 + IL_0002: initobj !!0 + IL_0008: ldloc.0 + IL_0009: ret + } + + } + diff --git a/unity-2019.4.24f1-mbe/mono/dis/tests/gen-cattr.cs b/unity-2019.4.24f1-mbe/mono/dis/tests/gen-cattr.cs new file mode 100644 index 000000000..adc7ef5e1 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/dis/tests/gen-cattr.cs @@ -0,0 +1,17 @@ +/* Custom attributes for type parameters */ +using System; + +[AttributeUsage(AttributeTargets.GenericParameter)] +class GenParAttribute : Attribute { +} + +class cons <[GenPar] A> { + public void abc <[GenPar] M> () { + } +} + +class Test { + public static void Main () + { + } +} diff --git a/unity-2019.4.24f1-mbe/mono/dis/tests/gen-dump-method.cs b/unity-2019.4.24f1-mbe/mono/dis/tests/gen-dump-method.cs new file mode 100644 index 000000000..d49e323c9 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/dis/tests/gen-dump-method.cs @@ -0,0 +1,21 @@ +//Test for dumping method table for generic types and generic methods +//monodis --method +class a +{ + void a_foo (U u) + { + } +} + +class g +{ + T foo2 (int i, T t) + { + return default (T); + } + + T foo (U u, int i) + { + return default (T); + } +} diff --git a/unity-2019.4.24f1-mbe/mono/dis/tests/gen-prop.cs b/unity-2019.4.24f1-mbe/mono/dis/tests/gen-prop.cs new file mode 100644 index 000000000..b6f9b7024 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/dis/tests/gen-prop.cs @@ -0,0 +1,7 @@ +//Property using a generic param +class g +{ + public T abc { + get { return default (T); } + } +} diff --git a/unity-2019.4.24f1-mbe/mono/dis/tests/gen-type.cs b/unity-2019.4.24f1-mbe/mono/dis/tests/gen-type.cs new file mode 100644 index 000000000..b3fdbbef0 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/dis/tests/gen-type.cs @@ -0,0 +1,16 @@ +// Test for a MemberRef with a TypeSpec parent + +class g +{ + public void foo (A _a) + { + } +} + +class test { + public static void Main () + { + g _g = new g (); + _g.foo ("abc"); + } +} diff --git a/unity-2019.4.24f1-mbe/mono/dis/tests/test1.cs b/unity-2019.4.24f1-mbe/mono/dis/tests/test1.cs new file mode 100644 index 000000000..b28fe8b1e --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/dis/tests/test1.cs @@ -0,0 +1,23 @@ +/* Bug #76671 + Note: gmcs currently emits duplicate TypeSpecs, so this + case doesn't get exposed, so use csc compiled + assemblies till gmcs is fixed. +*/ + +class X { + public static void Xfoo () { + X.Xfoo(); + } +} + +class Y { + public static void Yfoo () { + X.Xfoo(); + } +} + +class Test { + static void Main () + { + } +} diff --git a/unity-2019.4.24f1-mbe/mono/dis/tests/test2.cs b/unity-2019.4.24f1-mbe/mono/dis/tests/test2.cs new file mode 100644 index 000000000..0d19757ad --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/dis/tests/test2.cs @@ -0,0 +1,50 @@ +/* Bug #76671 + Note: gmcs currently emits duplicate TypeSpecs, so this + case doesn't get exposed, so use csc compiled + assemblies till gmcs is fixed. + + Array of type params +*/ +using System; + +class list { + public static void bar () + { + gen.foo (); + gen.foo (); + gen.foo (); + gen.foo (); + } +} + +class list_two { + public static void bar () + { + gen.foo (); + gen.foo (); + gen.foo (); + gen.foo (); + } +} + +class list_three { + public static void bar () + { + gen.foo (); + gen.foo (); + gen.foo (); + gen.foo (); + } +} + +class gen { + public static void foo () + { + } +} + +class Test { + public static void Main () + { + } +} diff --git a/unity-2019.4.24f1-mbe/mono/dis/tests/test3.cs b/unity-2019.4.24f1-mbe/mono/dis/tests/test3.cs new file mode 100644 index 000000000..3a6a07e09 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/dis/tests/test3.cs @@ -0,0 +1,25 @@ +/* Bug #76671 + Note: gmcs currently emits duplicate TypeSpecs, so this + case doesn't get exposed, so use csc compiled + assemblies till gmcs is fixed. +*/ + +using System; + +class X { + public static void Xfoo () { + Console.WriteLine (typeof (T1).ToString ()); + } +} + +class Y { + public static void Yfoo () { + Console.WriteLine (typeof (T2).ToString ()); + } +} + +class Test { + static void Main () + { + } +} diff --git a/unity-2019.4.24f1-mbe/mono/dis/util.c b/unity-2019.4.24f1-mbe/mono/dis/util.c new file mode 100644 index 000000000..80b6ad444 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/dis/util.c @@ -0,0 +1,162 @@ +/** + * \file + * Assorted utilities for the disassembler + * + * Author: + * Miguel de Icaza (miguel@ximian.com) + * + * (C) 2001 Ximian, Inc (http://www.ximian.com) + */ +#include +#include +#include +#include +#include +#include "util.h" +#include "mono/utils/mono-compiler.h" + +#ifdef HAVE_IEEEFP_H +#include +#endif + +/** + * \param code code to lookup in table + * \param table table to decode code + * + * Warning: returns static buffer. + */ +const char * +map (guint32 code, dis_map_t *table) +{ + int i; + + for (i = 0; table [i].str != NULL; i++) + if (table [i].code == code) + return table [i].str; + return "invalid-flags"; +} + +/** + * \param code bitfield + * \param table table to decode bitfield + * + * Warning: returns static buffer. + */ +const char * +flags (guint32 code, dis_map_t *table) +{ + static char buffer [1024]; + int i; + + buffer [0] = 0; + + for (i = 0; code && table [i].str != NULL; i++) + if (table [i].code & code) { + code &= ~table [i].code; + strcat (buffer, table [i].str); + } + + if (code) + sprintf (buffer + strlen (buffer), "unknown-flag-%2x ", code); + + return buffer; +} + +/** + * hex_dump: + * @buffer: pointer to buffer to dump + * @base: numbering base to use + * @count: number of bytes to dump + */ +void +hex_dump (const char *buffer, int base, int count) +{ + int show_header = 1; + int i; + + if (count < 0){ + count = -count; + show_header = 0; + } + + for (i = 0; i < count; i++){ + if (show_header) + if ((i % 16) == 0) + printf ("\n0x%08X: ", (unsigned char) base + i); + + printf ("%02X ", (unsigned char) (buffer [i])); + } + fflush (stdout); +} + +char* +data_dump (const char *data, int len, const char* prefix) { + int i, j; + GString *str; + if (!len) + return g_strdup (" ()\n"); + str = g_string_new (" ("); + for (i = 0; i + 15 < len; i += 16) { + if (i == 0) + g_string_append_printf (str, "\n"); + g_string_append_printf (str, "%s", prefix); + for (j = 0; j < 16; ++j) + g_string_append_printf (str, "%02X ", (unsigned char) (data [i + j])); + g_string_append_printf (str, i == len - 16? ") // ": " // "); + for (j = 0; j < 16; ++j) + g_string_append_printf (str, "%c", data [i + j] >= 32 && data [i + j] <= 126? data [i + j]: '.'); + g_string_append_printf (str, "\n"); + } + if (i == len) + return g_string_free (str, FALSE); + if (len > 16) + g_string_append_printf (str, "%s", prefix); + j = i; + for (; i < len; ++i) + g_string_append_printf (str, "%02X ", (unsigned char) (data [i])); + if (len > 16) { + /* align */ + int count = 16 - (len % 16); + for (i = 0; i < count; ++i) + g_string_append_printf (str, " "); + } + g_string_append_printf (str, ") // "); + for (i = j; i < len; ++i) + g_string_append_printf (str, "%c", data [i] >= 32 && data [i] <= 126? data [i]: '.'); + g_string_append_printf (str, "\n"); + return g_string_free (str, FALSE); +} + +int +dis_isinf (double num) +{ +#ifdef HAVE_ISINF + return isinf (num); +#elif defined(HAVE_IEEEFP_H) + fpclass_t klass; + + klass = fpclass (num); + if (klass == FP_NINF) + return -1; + + if (klass == FP_PINF) + return 1; + + return 0; +#elif defined(HAVE__FINITE) + return _finite (num) ? 0 : 1; +#else +#error "Don't know how to implement isinf for this platform." +#endif +} + +int +dis_isnan (double num) +{ +#ifdef __MINGW32_VERSION +return _isnan (num); +#else +return isnan (num); +#endif +} + diff --git a/unity-2019.4.24f1-mbe/mono/dis/util.h b/unity-2019.4.24f1-mbe/mono/dis/util.h new file mode 100644 index 000000000..d8ff7bfb1 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/dis/util.h @@ -0,0 +1,12 @@ +typedef struct { + int code; + const char *str; +} dis_map_t; + +const char *map (guint32 code, dis_map_t *table); +const char *flags (guint32 code, dis_map_t *table); +void hex_dump (const char *buffer, int base, int count); +char* data_dump (const char *data, int len, const char* prefix); + +extern int dis_isinf (double num); +extern int dis_isnan (double num); diff --git a/unity-2019.4.24f1-mbe/mono/eglib/.gitignore b/unity-2019.4.24f1-mbe/mono/eglib/.gitignore new file mode 100644 index 000000000..6635df1a9 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/.gitignore @@ -0,0 +1,11 @@ +/Makefile +/Makefile.in +/.libs +/.deps +/*.lo +/*.la +/*.o +/semantic.cache +/.project +/.cproject +/eglib-config.h diff --git a/unity-2019.4.24f1-mbe/mono/eglib/Makefile.am b/unity-2019.4.24f1-mbe/mono/eglib/Makefile.am new file mode 100644 index 000000000..87051a6be --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/Makefile.am @@ -0,0 +1,65 @@ +noinst_LTLIBRARIES = libeglib.la + +AM_CFLAGS = $(WERROR_CFLAGS) + +win_files = \ + eglib-config.hw \ + gdate-win32.c gdir-win32.c gfile-win32.c gmisc-win32.c \ + gmodule-win32.c gmodule-win32-internals.h gtimer-win32.c gunicode-win32.c + +unix_files = \ + gdate-unix.c gdir-unix.c gfile-unix.c gmisc-unix.c \ + gmodule-unix.c gtimer-unix.c + +if HOST_WIN32 +os_files = $(win_files) +else +os_files = $(unix_files) +endif + +libeglib_la_SOURCES = \ + eglib-remap.h \ + sort.frag.h \ + glib.h \ + garray.c \ + gbytearray.c \ + gerror.c \ + ghashtable.c \ + giconv.c \ + gmem.c \ + gmodule.h \ + goutput.c \ + gqsort.c \ + gstr.c \ + gslist.c \ + gstring.c \ + gptrarray.c \ + glist.c \ + gqueue.c \ + gpath.c \ + gshell.c \ + gspawn.c \ + gfile.c \ + gfile-posix.c \ + gpattern.c \ + gmarkup.c \ + gutf8.c \ + gunicode.c \ + unicode-data.h \ + $(os_files) + +libeglib_la_CFLAGS = -g -Wall -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE + +AM_CPPFLAGS = -I$(srcdir) + +if HOST_WIN32 +libeglib_la_LIBADD = -lm $(LIBICONV) -lpsapi +else +if HOST_ANDROID +libeglib_la_LIBADD = -llog +endif +endif + +MAINTAINERCLEANFILES = Makefile.in + +EXTRA_DIST = eglib-config.h.in $(win_files) $(unix_files) diff --git a/unity-2019.4.24f1-mbe/mono/eglib/eglib-config.h.in b/unity-2019.4.24f1-mbe/mono/eglib/eglib-config.h.in new file mode 100644 index 000000000..26e48fd34 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/eglib-config.h.in @@ -0,0 +1,47 @@ +#ifndef __EGLIB_CONFIG_H +#define __EGLIB_CONFIG_H + +/* + * System-dependent settings + */ +#define G_GNUC_PRETTY_FUNCTION @GNUC_PRETTY@ +#define G_GNUC_UNUSED @GNUC_UNUSED@ +#define G_BYTE_ORDER @ORDER@ +#define G_GNUC_NORETURN @GNUC_NORETURN@ +#define G_SEARCHPATH_SEPARATOR_S "@SEARCHSEP@" +#define G_SEARCHPATH_SEPARATOR '@SEARCHSEP@' +#define G_DIR_SEPARATOR '@PATHSEP@' +#define G_DIR_SEPARATOR_S "@PATHSEP@" +#define G_BREAKPOINT() @BREAKPOINT@ +#define G_OS_@OS@ +#define GPOINTER_TO_INT(ptr) @GPOINTER_TO_INT@ +#define GPOINTER_TO_UINT(ptr) @GPOINTER_TO_UINT@ +#define GINT_TO_POINTER(v) @GINT_TO_POINTER@ +#define GUINT_TO_POINTER(v) @GUINT_TO_POINTER@ + +#if @HAVE_ALLOCA_H@ == 1 +#define G_HAVE_ALLOCA_H +#endif + +typedef unsigned @GSIZE@ gsize; +typedef signed @GSIZE@ gssize; + +#define G_GSIZE_FORMAT @GSIZE_FORMAT@ + +#if @G_HAVE_ISO_VARARGS@ == 1 +#define G_HAVE_ISO_VARARGS +#endif + +#if defined (HOST_WATCHOS) +#undef G_BREAKPOINT +#define G_BREAKPOINT() +#endif + +#if defined (HOST_WASM) +#undef G_BREAKPOINT +#define G_BREAKPOINT() do { printf ("MONO: BREAKPOINT\n"); abort (); } while (0) +#endif + +typedef @PIDTYPE@ GPid; + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/eglib/eglib-config.hw b/unity-2019.4.24f1-mbe/mono/eglib/eglib-config.hw new file mode 100644 index 000000000..cc7bd44a8 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/eglib-config.hw @@ -0,0 +1,63 @@ +#ifndef __EGLIB_CONFIG_H +#define __EGLIB_CONFIG_H + +/* + * System-dependent settings + */ +#define G_OS_WIN32 1 + +#ifdef _MSC_VER + +#include + +#define G_GNUC_PRETTY_FUNCTION __FUNCTION__ +#define G_GNUC_UNUSED +#define G_BYTE_ORDER 1234 +#define G_GNUC_NORETURN +#define G_BREAKPOINT() __debugbreak() +#define MAXPATHLEN 242 + +typedef uintptr_t gsize; +typedef intptr_t gssize; +typedef int pid_t; + +#define G_DIR_SEPARATOR '\\' +#define G_DIR_SEPARATOR_S "\\" +#define G_SEARCHPATH_SEPARATOR_S ";" +#define G_SEARCHPATH_SEPARATOR ';' +#define G_GSIZE_FORMAT "d" +#define GPOINTER_TO_INT(ptr) ((gint)(intptr_t) (ptr)) +#define GPOINTER_TO_UINT(ptr) ((guint)(intptr_t) (ptr)) +#define GINT_TO_POINTER(v) ((gpointer)(intptr_t) (v)) +#define GUINT_TO_POINTER(v) ((gpointer)(intptr_t) (v)) + +#define STDOUT_FILENO (int)(intptr_t)stdout +#define STDERR_FILENO (int)(intptr_t)stderr + +/* FIXME: what should this be ?*/ +#define X_OK 4 /* This is really read */ +#define WNOHANG 1 +#define F_SETFD 1 +#define FD_CLOEXEC 1 + +#undef inline +#define inline __inline + +#define strtok_r strtok_s + +#undef G_HAVE_UNISTD_H +#undef G_HAVE_SYS_TIME_H +#undef G_HAVE_SYS_WAIT_H +#undef G_HAVE_PWD_H +#undef G_HAVE_STRNDUP +#define G_HAVE_GETOPT_H 1 + +/* disable the following warnings + * C4100: The formal parameter is not referenced in the body of the function. The unreferenced parameter is ignored. + * C4127: conditional expression is constant +*/ +#pragma warning(disable:4100 4127) +#endif + +typedef void * GPid; +#endif diff --git a/unity-2019.4.24f1-mbe/mono/eglib/eglib-remap.h b/unity-2019.4.24f1-mbe/mono/eglib/eglib-remap.h new file mode 100644 index 000000000..15dc4bb0b --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/eglib-remap.h @@ -0,0 +1,289 @@ +#define g_array_append monoeg_g_array_append +#define g_array_append_vals monoeg_g_array_append_vals +#define g_array_free monoeg_g_array_free +#define g_array_insert_vals monoeg_g_array_insert_vals +#define g_array_new monoeg_g_array_new +#define g_array_remove_index monoeg_g_array_remove_index +#define g_array_remove_index_fast monoeg_g_array_remove_index_fast +#define g_array_set_size monoeg_g_array_set_size +#define g_array_sized_new monoeg_g_array_sized_new +#define g_ascii_strdown monoeg_g_ascii_strdown +#define g_ascii_strncasecmp monoeg_g_ascii_strncasecmp +#define g_ascii_tolower monoeg_g_ascii_tolower +#define g_ascii_xdigit_value monoeg_g_ascii_xdigit_value +#define g_build_path monoeg_g_build_path +#define g_byte_array_append monoeg_g_byte_array_append +#define g_byte_array_free monoeg_g_byte_array_free +#define g_byte_array_new monoeg_g_byte_array_new +#define g_byte_array_set_size monoeg_g_byte_array_set_size +#define g_calloc monoeg_g_calloc +#define g_clear_error monoeg_g_clear_error +#define g_convert monoeg_g_convert +#define g_convert_error_quark monoeg_g_convert_error_quark +#define g_dir_close monoeg_g_dir_close +#define g_dir_open monoeg_g_dir_open +#define g_dir_read_name monoeg_g_dir_read_name +#define g_dir_rewind monoeg_g_dir_rewind +#define g_mkdir_with_parents monoeg_g_mkdir_with_parents +#define g_direct_equal monoeg_g_direct_equal +#define g_direct_hash monoeg_g_direct_hash +#define g_ensure_directory_exists monoeg_g_ensure_directory_exists +#define g_error_free monoeg_g_error_free +#define g_error_new monoeg_g_error_new +#define g_error_vnew monoeg_g_error_vnew +#define g_file_error_quark monoeg_g_file_error_quark +#define g_file_error_from_errno monoeg_g_file_error_from_errno +#define g_file_get_contents monoeg_g_file_get_contents +#define g_file_set_contents monoeg_g_file_set_contents +#define g_file_open_tmp monoeg_g_file_open_tmp +#define g_file_test monoeg_g_file_test +#define g_filename_from_uri monoeg_g_filename_from_uri +#define g_filename_from_utf8 monoeg_g_filename_from_utf8 +#define g_filename_to_uri monoeg_g_filename_to_uri +#define g_find_program_in_path monoeg_g_find_program_in_path +#define g_fprintf monoeg_g_fprintf +#define g_free monoeg_g_free +#define g_get_charset monoeg_g_get_charset +#define g_get_current_dir monoeg_g_get_current_dir +#define g_get_current_time monoeg_g_get_current_time +#define g_get_home_dir monoeg_g_get_home_dir +#define g_get_prgname monoeg_g_get_prgname +#define g_get_tmp_dir monoeg_g_get_tmp_dir +#define g_get_user_name monoeg_g_get_user_name +#define g_getenv monoeg_g_getenv +#define g_hasenv monoeg_g_hasenv +#define g_hash_table_destroy monoeg_g_hash_table_destroy +#define g_hash_table_find monoeg_g_hash_table_find +#define g_hash_table_foreach monoeg_g_hash_table_foreach +#define g_hash_table_foreach_remove monoeg_g_hash_table_foreach_remove +#define g_hash_table_foreach_steal monoeg_g_hash_table_foreach_steal +#define g_hash_table_get_keys monoeg_g_hash_table_get_keys +#define g_hash_table_get_values monoeg_g_hash_table_get_values +#define g_hash_table_insert_replace monoeg_g_hash_table_insert_replace +#define g_hash_table_lookup monoeg_g_hash_table_lookup +#define g_hash_table_lookup_extended monoeg_g_hash_table_lookup_extended +#define g_hash_table_new monoeg_g_hash_table_new +#define g_hash_table_new_full monoeg_g_hash_table_new_full +#define g_hash_table_remove monoeg_g_hash_table_remove +#define g_hash_table_steal monoeg_g_hash_table_steal +#define g_hash_table_size monoeg_g_hash_table_size +#define g_hash_table_print_stats monoeg_g_hash_table_print_stats +#define g_hash_table_remove_all monoeg_g_hash_table_remove_all +#define g_hash_table_iter_init monoeg_g_hash_table_iter_init +#define g_hash_table_iter_next monoeg_g_hash_table_iter_next +#define g_iconv monoeg_g_iconv +#define g_iconv_close monoeg_g_iconv_close +#define g_iconv_open monoeg_g_iconv_open +#define g_int_equal monoeg_g_int_equal +#define g_int_hash monoeg_g_int_hash +#define g_list_alloc monoeg_g_list_alloc +#define g_list_append monoeg_g_list_append +#define g_list_concat monoeg_g_list_concat +#define g_list_copy monoeg_g_list_copy +#define g_list_delete_link monoeg_g_list_delete_link +#define g_list_find monoeg_g_list_find +#define g_list_find_custom monoeg_g_list_find_custom +#define g_list_first monoeg_g_list_first +#define g_list_foreach monoeg_g_list_foreach +#define g_list_free monoeg_g_list_free +#define g_list_free_1 monoeg_g_list_free_1 +#define g_list_index monoeg_g_list_index +#define g_list_insert_before monoeg_g_list_insert_before +#define g_list_insert_sorted monoeg_g_list_insert_sorted +#define g_list_last monoeg_g_list_last +#define g_list_length monoeg_g_list_length +#define g_list_nth monoeg_g_list_nth +#define g_list_nth_data monoeg_g_list_nth_data +#define g_list_prepend monoeg_g_list_prepend +#define g_list_remove monoeg_g_list_remove +#define g_list_remove_all monoeg_g_list_remove_all +#define g_list_remove_link monoeg_g_list_remove_link +#define g_list_reverse monoeg_g_list_reverse +#define g_list_sort monoeg_g_list_sort +#define g_locale_from_utf8 monoeg_g_locale_from_utf8 +#define g_locale_to_utf8 monoeg_g_locale_to_utf8 +#define g_log monoeg_g_log +#define g_log_set_always_fatal monoeg_g_log_set_always_fatal +#define g_log_set_fatal_mask monoeg_g_log_set_fatal_mask +#define g_logv monoeg_g_logv +#define g_markup_parse_context_end_parse monoeg_g_markup_parse_context_end_parse +#define g_markup_parse_context_free monoeg_g_markup_parse_context_free +#define g_markup_parse_context_new monoeg_g_markup_parse_context_new +#define g_markup_parse_context_parse monoeg_g_markup_parse_context_parse +#define g_memdup monoeg_g_memdup +#define g_mem_set_vtable monoeg_g_mem_set_vtable +#define g_mkdtemp monoeg_g_mkdtemp +#define g_module_build_path monoeg_g_module_build_path +#define g_module_close monoeg_g_module_close +#define g_module_error monoeg_g_module_error +#define g_module_open monoeg_g_module_open +#define g_module_symbol monoeg_g_module_symbol +#define g_path_get_basename monoeg_g_path_get_basename +#define g_path_get_dirname monoeg_g_path_get_dirname +#define g_path_is_absolute monoeg_g_path_is_absolute +#define g_pattern_match_string monoeg_g_pattern_match_string +#define g_pattern_spec_free monoeg_g_pattern_spec_free +#define g_pattern_spec_new monoeg_g_pattern_spec_new +#define g_print monoeg_g_print +#define g_printf monoeg_g_printf +#define g_printerr monoeg_g_printerr +#define g_propagate_error monoeg_g_propagate_error +#define g_ptr_array_add monoeg_g_ptr_array_add +#define g_ptr_array_capacity monoeg_g_ptr_array_capacity +#define g_ptr_array_foreach monoeg_g_ptr_array_foreach +#define g_ptr_array_free monoeg_g_ptr_array_free +#define g_ptr_array_new monoeg_g_ptr_array_new +#define g_ptr_array_remove monoeg_g_ptr_array_remove +#define g_ptr_array_remove_fast monoeg_g_ptr_array_remove_fast +#define g_ptr_array_remove_index monoeg_g_ptr_array_remove_index +#define g_ptr_array_remove_index_fast monoeg_g_ptr_array_remove_index_fast +#define g_ptr_array_set_size monoeg_g_ptr_array_set_size +#define g_ptr_array_sized_new monoeg_g_ptr_array_sized_new +#define g_ptr_array_sort monoeg_g_ptr_array_sort +#define g_ptr_array_sort_with_data monoeg_g_ptr_array_sort_with_data +#define g_qsort_with_data monoeg_g_qsort_with_data +#define g_queue_free monoeg_g_queue_free +#define g_queue_is_empty monoeg_g_queue_is_empty +#define g_queue_foreach monoeg_g_queue_foreach +#define g_queue_new monoeg_g_queue_new +#define g_queue_pop_head monoeg_g_queue_pop_head +#define g_queue_push_head monoeg_g_queue_push_head +#define g_queue_push_tail monoeg_g_queue_push_tail +#define g_set_error monoeg_g_set_error +#define g_set_prgname monoeg_g_set_prgname +#define g_setenv monoeg_g_setenv +#define g_shell_parse_argv monoeg_g_shell_parse_argv +#define g_shell_quote monoeg_g_shell_quote +#define g_shell_unquote monoeg_g_shell_unquote +#define g_slist_alloc monoeg_g_slist_alloc +#define g_slist_append monoeg_g_slist_append +#define g_slist_concat monoeg_g_slist_concat +#define g_slist_copy monoeg_g_slist_copy +#define g_slist_delete_link monoeg_g_slist_delete_link +#define g_slist_find monoeg_g_slist_find +#define g_slist_find_custom monoeg_g_slist_find_custom +#define g_slist_foreach monoeg_g_slist_foreach +#define g_slist_free monoeg_g_slist_free +#define g_slist_free_1 monoeg_g_slist_free_1 +#define g_slist_index monoeg_g_slist_index +#define g_slist_insert_before monoeg_g_slist_insert_before +#define g_slist_insert_sorted monoeg_g_slist_insert_sorted +#define g_slist_last monoeg_g_slist_last +#define g_slist_length monoeg_g_slist_length +#define g_slist_nth monoeg_g_slist_nth +#define g_slist_nth_data monoeg_g_slist_nth_data +#define g_slist_prepend monoeg_g_slist_prepend +#define g_slist_remove monoeg_g_slist_remove +#define g_slist_remove_all monoeg_g_slist_remove_all +#define g_slist_remove_link monoeg_g_slist_remove_link +#define g_slist_reverse monoeg_g_slist_reverse +#define g_slist_sort monoeg_g_slist_sort +#define g_snprintf monoeg_g_snprintf +#define g_spaced_primes_closest monoeg_g_spaced_primes_closest +#define g_spawn_async_with_pipes monoeg_g_spawn_async_with_pipes +#define g_spawn_command_line_sync monoeg_g_spawn_command_line_sync +#define g_sprintf monoeg_g_sprintf +#define g_stpcpy monoeg_g_stpcpy +#define g_str_equal monoeg_g_str_equal +#define g_str_has_prefix monoeg_g_str_has_prefix +#define g_str_has_suffix monoeg_g_str_has_suffix +#define g_str_hash monoeg_g_str_hash +#define g_strchomp monoeg_g_strchomp +#define g_strchug monoeg_g_strchug +#define g_strconcat monoeg_g_strconcat +#define g_strdelimit monoeg_g_strdelimit +#define g_strdown monoeg_g_strdown +#define g_strdup_printf monoeg_g_strdup_printf +#define g_strdup_vprintf monoeg_g_strdup_vprintf +#define g_strerror monoeg_g_strerror +#define g_strescape monoeg_g_strescape +#define g_strfreev monoeg_g_strfreev +#define g_strdupv monoeg_g_strdupv +#define g_string_append monoeg_g_string_append +#define g_string_append_c monoeg_g_string_append_c +#define g_string_append_len monoeg_g_string_append_len +#define g_string_append_unichar monoeg_g_string_append_unichar +#define g_string_append_printf monoeg_g_string_append_printf +#define g_string_append_vprintf monoeg_g_string_append_vprintf +#define g_string_erase monoeg_g_string_erase +#define g_string_free monoeg_g_string_free +#define g_string_insert monoeg_g_string_insert +#define g_string_new monoeg_g_string_new +#define g_string_new_len monoeg_g_string_new_len +#define g_string_prepend monoeg_g_string_prepend +#define g_string_printf monoeg_g_string_printf +#define g_string_set_size monoeg_g_string_set_size +#define g_string_sized_new monoeg_g_string_sized_new +#define g_string_truncate monoeg_g_string_truncate +#define g_strjoin monoeg_g_strjoin +#define g_strjoinv monoeg_g_strjoinv +#define g_strlcpy monoeg_g_strlcpy +#define g_strndup monoeg_g_strndup +#define g_strnfill monoeg_g_strnfill +#define g_strreverse monoeg_g_strreverse +#define g_strsplit monoeg_g_strsplit +#define g_strsplit_set monoeg_g_strsplit_set +#define g_strv_length monoeg_g_strv_length +#define g_timer_destroy monoeg_g_timer_destroy +#define g_timer_elapsed monoeg_g_timer_elapsed +#define g_timer_new monoeg_g_timer_new +#define g_timer_start monoeg_g_timer_start +#define g_timer_stop monoeg_g_timer_stop +#define g_trailingBytesForUTF8 monoeg_g_trailingBytesForUTF8 +#define g_ucs4_to_utf8 monoeg_g_ucs4_to_utf8 +#define g_ucs4_to_utf16 monoeg_g_ucs4_to_utf16 +#define g_unichar_case monoeg_g_unichar_case +#define g_unichar_isxdigit monoeg_g_unichar_isxdigit +#define g_unichar_tolower monoeg_g_unichar_tolower +#define g_unichar_totitle monoeg_g_unichar_totitle +#define g_unichar_toupper monoeg_g_unichar_toupper +#define g_unichar_type monoeg_g_unichar_type +#define g_unichar_xdigit_value monoeg_g_unichar_xdigit_value +#define g_unsetenv monoeg_g_unsetenv +#define g_usleep monoeg_g_usleep +#define g_utf16_to_ucs4 monoeg_g_utf16_to_ucs4 +#define g_utf16_to_utf8 monoeg_g_utf16_to_utf8 +#define g_utf8_get_char monoeg_g_utf8_get_char +#define g_utf8_offset_to_pointer monoeg_g_utf8_offset_to_pointer +#define g_utf8_pointer_to_offset monoeg_g_utf8_pointer_to_offset +#define g_utf8_strdown monoeg_g_utf8_strdown +#define g_utf8_strlen monoeg_g_utf8_strlen +#define g_utf8_strup monoeg_g_utf8_strup +#define g_utf8_to_ucs4_fast monoeg_g_utf8_to_ucs4_fast +#define g_utf8_to_utf16 monoeg_g_utf8_to_utf16 +#define g_utf8_validate monoeg_g_utf8_validate +#define g_unichar_to_utf8 monoeg_g_unichar_to_utf8 +#define g_unichar_is_space monoeg_g_unichar_is_space +#define g_unicode_break_type monoeg_g_unicode_break_type +#define g_utf8_offset_to_pointer monoeg_g_utf8_offset_to_pointer +#define g_utf8_pointer_to_offset monoeg_g_utf8_pointer_to_offset +#define g_utf8_to_ucs4_fast monoeg_g_utf8_to_ucs4_fast +#define g_vasprintf monoeg_g_vasprintf +#define g_win32_getlocale monoeg_g_win32_getlocale +#define g_assertion_message monoeg_assertion_message +#define g_malloc monoeg_malloc +#define g_malloc0 monoeg_malloc0 +#define g_ptr_array_grow monoeg_ptr_array_grow +#define g_realloc monoeg_realloc +#define g_try_malloc monoeg_try_malloc +#define g_try_realloc monoeg_try_realloc +#define g_strdup monoeg_strdup +#define g_ucs4_to_utf16_len monoeg_ucs4_to_utf16_len +#define g_utf16_to_ucs4_len monoeg_utf16_to_ucs4_len + +#define g_ascii_strcasecmp monoeg_ascii_strcasecmp +#define g_ascii_strup monoeg_ascii_strup +#define g_ascii_toupper monoeg_ascii_toupper +#define g_unichar_break_type monoeg_unichar_break_type +#define g_unichar_isspace monoeg_unichar_isspace +#define g_unichar_to_utf16 monoeg_unichar_to_utf16 +#define g_utf8_find_prev_char monoeg_utf8_find_prev_char +#define g_utf8_get_char_validated monoeg_utf8_get_char_validated +#define g_utf8_prev_char monoeg_utf8_prev_char +#define g_utf8_to_ucs4 monoeg_utf8_to_ucs4 + + +#define g_log_default_handler monoeg_log_default_handler +#define g_log_set_default_handler monoeg_log_set_default_handler +#define g_set_print_handler monoeg_set_print_handler +#define g_set_printerr_handler monoeg_set_printerr_handler diff --git a/unity-2019.4.24f1-mbe/mono/eglib/garray.c b/unity-2019.4.24f1-mbe/mono/eglib/garray.c new file mode 100644 index 000000000..b9844f88a --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/garray.c @@ -0,0 +1,238 @@ +/* + * Arrays + * + * Author: + * Chris Toshok (toshok@novell.com) + * + * (C) 2006 Novell, Inc. + * + * 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. + */ + +#include +#include + +#define INITIAL_CAPACITY 16 + +#define element_offset(p,i) ((p)->array.data + (i) * (p)->element_size) +#define element_length(p,i) ((i) * (p)->element_size) + +typedef struct { + GArray array; + gboolean clear_; + guint element_size; + gboolean zero_terminated; + guint capacity; +} GArrayPriv; + +static void +ensure_capacity (GArrayPriv *priv, guint capacity) +{ + guint new_capacity; + + if (capacity <= priv->capacity) + return; + + new_capacity = (capacity + 63) & ~63; + + priv->array.data = g_realloc (priv->array.data, element_length (priv, new_capacity)); + + if (priv->clear_) { + memset (element_offset (priv, priv->capacity), + 0, + element_length (priv, new_capacity - priv->capacity)); + } + + priv->capacity = new_capacity; +} + +GArray * +g_array_new (gboolean zero_terminated, + gboolean clear_, + guint element_size) +{ + GArrayPriv *rv = g_new0 (GArrayPriv, 1); + rv->zero_terminated = zero_terminated; + rv->clear_ = clear_; + rv->element_size = element_size; + + ensure_capacity (rv, INITIAL_CAPACITY); + + return (GArray*)rv; +} + +GArray * +g_array_sized_new (gboolean zero_terminated, + gboolean clear_, + guint element_size, + guint reserved_size) +{ + GArrayPriv *rv = g_new0 (GArrayPriv, 1); + rv->zero_terminated = zero_terminated; + rv->clear_ = clear_; + rv->element_size = element_size; + + ensure_capacity (rv, reserved_size); + + return (GArray*)rv; +} + +gchar* +g_array_free (GArray *array, + gboolean free_segment) +{ + gchar* rv = NULL; + + g_return_val_if_fail (array != NULL, NULL); + + if (free_segment) + g_free (array->data); + else + rv = array->data; + + g_free (array); + + return rv; +} + +GArray * +g_array_append_vals (GArray *array, + gconstpointer data, + guint len) +{ + GArrayPriv *priv = (GArrayPriv*)array; + + g_return_val_if_fail (array != NULL, NULL); + + ensure_capacity (priv, priv->array.len + len + (priv->zero_terminated ? 1 : 0)); + + memmove (element_offset (priv, priv->array.len), + data, + element_length (priv, len)); + + priv->array.len += len; + + if (priv->zero_terminated) { + memset (element_offset (priv, priv->array.len), + 0, + priv->element_size); + } + + return array; +} + +GArray* +g_array_insert_vals (GArray *array, + guint index_, + gconstpointer data, + guint len) +{ + GArrayPriv *priv = (GArrayPriv*)array; + guint extra = (priv->zero_terminated ? 1 : 0); + + g_return_val_if_fail (array != NULL, NULL); + + ensure_capacity (priv, array->len + len + extra); + + /* first move the existing elements out of the way */ + memmove (element_offset (priv, index_ + len), + element_offset (priv, index_), + element_length (priv, array->len - index_)); + + /* then copy the new elements into the array */ + memmove (element_offset (priv, index_), + data, + element_length (priv, len)); + + array->len += len; + + if (priv->zero_terminated) { + memset (element_offset (priv, priv->array.len), + 0, + priv->element_size); + } + + return array; +} + +GArray* +g_array_remove_index (GArray *array, + guint index_) +{ + GArrayPriv *priv = (GArrayPriv*)array; + + g_return_val_if_fail (array != NULL, NULL); + + memmove (element_offset (priv, index_), + element_offset (priv, index_ + 1), + element_length (priv, array->len - index_)); + + array->len --; + + if (priv->zero_terminated) { + memset (element_offset (priv, priv->array.len), + 0, + priv->element_size); + } + + return array; +} + +GArray* +g_array_remove_index_fast (GArray *array, + guint index_) +{ + GArrayPriv *priv = (GArrayPriv*)array; + + g_return_val_if_fail (array != NULL, NULL); + + memmove (element_offset (priv, index_), + element_offset (priv, array->len - 1), + element_length (priv, 1)); + + array->len --; + + if (priv->zero_terminated) { + memset (element_offset (priv, priv->array.len), + 0, + priv->element_size); + } + + return array; +} + +void +g_array_set_size (GArray *array, gint length) +{ + GArrayPriv *priv = (GArrayPriv*)array; + + g_return_if_fail (array != NULL); + g_return_if_fail (length >= 0); + + if (length == priv->capacity) + return; // nothing to be done + + if (length > priv->capacity) { + // grow the array + ensure_capacity (priv, length); + } + + array->len = length; +} diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gbytearray.c b/unity-2019.4.24f1-mbe/mono/eglib/gbytearray.c new file mode 100644 index 000000000..f8231b483 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gbytearray.c @@ -0,0 +1,58 @@ +/* + * Arrays + * + * Author: + * Geoff Norton (gnorton@novell.com) + * + * (C) 2010 Novell, Inc. + * + * 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. + */ + +#include +#include + +GByteArray * +g_byte_array_new () +{ + return (GByteArray *) g_array_new (FALSE, TRUE, 1); +} + +guint8* +g_byte_array_free (GByteArray *array, + gboolean free_segment) +{ + return (guint8*) g_array_free ((GArray *)array, free_segment); +} + +GByteArray * +g_byte_array_append (GByteArray *array, + const guint8 *data, + guint len) +{ + return (GByteArray *)g_array_append_vals ((GArray *)array, data, len); +} + +void +g_byte_array_set_size (GByteArray *array, gint length) +{ + g_array_set_size ((GArray *)array, length); +} + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gdate-unity.c b/unity-2019.4.24f1-mbe/mono/eglib/gdate-unity.c new file mode 100644 index 000000000..c25eda909 --- /dev/null +++ b/unity-2019.4.24f1-mbe/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/unity-2019.4.24f1-mbe/mono/eglib/gdate-unix.c b/unity-2019.4.24f1-mbe/mono/eglib/gdate-unix.c new file mode 100644 index 000000000..5573e0d35 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gdate-unix.c @@ -0,0 +1,55 @@ +/* + * gdate-unix.c: Date and time utility functions. + * + * Author: + * Gonzalo Paniagua Javier (gonzalo@novell.com + * + * (C) 2006 Novell, Inc. + * + * 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. + */ +#include +#include +#include +#include +#include + +void +g_get_current_time (GTimeVal *result) +{ + struct timeval tv; + + g_return_if_fail (result != NULL); + gettimeofday (&tv, NULL); + result->tv_sec = tv.tv_sec; + result->tv_usec = tv.tv_usec; +} + +void +g_usleep (gulong microseconds) +{ + struct timespec req, rem; + + req.tv_sec = microseconds / 1000000; + req.tv_nsec = (microseconds % 1000000) * 1000; + + while (nanosleep (&req, &rem) == -1 && errno == EINTR) + req = rem; +} diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gdate-win32.c b/unity-2019.4.24f1-mbe/mono/eglib/gdate-win32.c new file mode 100644 index 000000000..b00e9b7bd --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gdate-win32.c @@ -0,0 +1,49 @@ +/* + * gdate-win32.c: Date and time utility functions. + * + * Author: + * Gonzalo Paniagua Javier (gonzalo@novell.com + * + * (C) 2006 Novell, Inc. + * + * 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. + */ +#include +#include + +#include + +void +g_get_current_time (GTimeVal *result) +{ + long int l; + + g_return_if_fail (result != NULL); + l = GetTickCount(); + + result->tv_sec = l / 1000; + result->tv_usec = (l % 1000) * 1000; +} + +void +g_usleep (gulong microseconds) +{ + Sleep (microseconds/1000); +} diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gdir-unity.c b/unity-2019.4.24f1-mbe/mono/eglib/gdir-unity.c new file mode 100644 index 000000000..a3d8eec30 --- /dev/null +++ b/unity-2019.4.24f1-mbe/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/unity-2019.4.24f1-mbe/mono/eglib/gdir-unix.c b/unity-2019.4.24f1-mbe/mono/eglib/gdir-unix.c new file mode 100644 index 000000000..abca22f9e --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gdir-unix.c @@ -0,0 +1,146 @@ +/* + * Directory utility functions. + * + * Author: + * Gonzalo Paniagua Javier (gonzalo@novell.com) + * + * (C) 2006 Novell, Inc. + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +struct _GDir { + DIR *dir; +#ifndef HAVE_REWINDDIR + char *path; +#endif +}; + +GDir * +g_dir_open (const gchar *path, guint flags, GError **error) +{ + GDir *dir; + + g_return_val_if_fail (path != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + (void) flags; /* this is not used */ + dir = g_new (GDir, 1); + dir->dir = opendir (path); + if (dir->dir == NULL) { + if (error) { + gint err = errno; + *error = g_error_new (G_LOG_DOMAIN, g_file_error_from_errno (err), strerror (err)); + } + g_free (dir); + return NULL; + } +#ifndef HAVE_REWINDDIR + dir->path = g_strdup (path); +#endif + return dir; +} + +const gchar * +g_dir_read_name (GDir *dir) +{ + struct dirent *entry; + + g_return_val_if_fail (dir != NULL && dir->dir != NULL, NULL); + do { + entry = readdir (dir->dir); + if (entry == NULL) + return NULL; + } while ((strcmp (entry->d_name, ".") == 0) || (strcmp (entry->d_name, "..") == 0)); + + return entry->d_name; +} + +void +g_dir_rewind (GDir *dir) +{ + g_return_if_fail (dir != NULL && dir->dir != NULL); +#ifndef HAVE_REWINDDIR + closedir (dir->dir); + dir->dir = opendir (dir->path); +#else + rewinddir (dir->dir); +#endif +} + +void +g_dir_close (GDir *dir) +{ + g_return_if_fail (dir != NULL && dir->dir != 0); + closedir (dir->dir); +#ifndef HAVE_REWINDDIR + g_free (dir->path); +#endif + dir->dir = NULL; + g_free (dir); +} + +int +g_mkdir_with_parents (const gchar *pathname, int mode) +{ + char *path, *d; + int rv; + + if (!pathname || *pathname == '\0') { + errno = EINVAL; + return -1; + } + + d = path = g_strdup (pathname); + if (*d == '/') + d++; + + while (TRUE) { + if (*d == '/' || *d == '\0') { + char orig = *d; + *d = '\0'; + rv = mkdir (path, mode); + if (rv == -1 && errno != EEXIST) { + g_free (path); + return -1; + } + + *d++ = orig; + while (orig == '/' && *d == '/') + d++; + if (orig == '\0') + break; + } else { + d++; + } + } + + g_free (path); + + return 0; +} diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gdir-win32.c b/unity-2019.4.24f1-mbe/mono/eglib/gdir-win32.c new file mode 100644 index 000000000..0ae3fd41c --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gdir-win32.c @@ -0,0 +1,141 @@ +/* + * Directory utility functions. + * + * Author: + * Gonzalo Paniagua Javier (gonzalo@novell.com) + * + * (C) 2006 Novell, Inc. + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include + +#include + +struct _GDir { + HANDLE handle; + gchar* current; + gchar* next; +}; + +GDir * +g_dir_open (const gchar *path, guint flags, GError **error) +{ + GDir *dir; + gunichar2* path_utf16; + gunichar2* path_utf16_search; + WIN32_FIND_DATAW find_data; + + g_return_val_if_fail (path != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + dir = g_new0 (GDir, 1); + path_utf16 = u8to16 (path); + path_utf16_search = g_malloc ((wcslen((wchar_t *) path_utf16) + 3)*sizeof(gunichar2)); + wcscpy (path_utf16_search, path_utf16); + wcscat (path_utf16_search, L"\\*"); + + dir->handle = FindFirstFileW (path_utf16_search, &find_data); + if (dir->handle == INVALID_HANDLE_VALUE) { + if (error) { + gint err = errno; + *error = g_error_new (G_LOG_DOMAIN, g_file_error_from_errno (err), strerror (err)); + } + g_free (path_utf16_search); + g_free (path_utf16); + g_free (dir); + return NULL; + } + g_free (path_utf16_search); + g_free (path_utf16); + + while ((wcscmp ((wchar_t *) find_data.cFileName, L".") == 0) || (wcscmp ((wchar_t *) find_data.cFileName, L"..") == 0)) { + if (!FindNextFileW (dir->handle, &find_data)) { + if (error) { + gint err = errno; + *error = g_error_new (G_LOG_DOMAIN, g_file_error_from_errno (err), strerror (err)); + } + g_free (dir); + return NULL; + } + } + + dir->current = NULL; + dir->next = u16to8 (find_data.cFileName); + return dir; +} + +const gchar * +g_dir_read_name (GDir *dir) +{ + WIN32_FIND_DATAW find_data; + + 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 { + if (!FindNextFileW (dir->handle, &find_data)) { + dir->next = NULL; + return dir->current; + } + } while ((wcscmp ((wchar_t *) find_data.cFileName, L".") == 0) || (wcscmp ((wchar_t *) find_data.cFileName, L"..") == 0)); + + dir->next = u16to8 (find_data.cFileName); + return dir->current; +} + +void +g_dir_rewind (GDir *dir) +{ +} + +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; + FindClose (dir->handle); + dir->handle = 0; + g_free (dir); +} + + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gerror.c b/unity-2019.4.24f1-mbe/mono/eglib/gerror.c new file mode 100644 index 000000000..43fef97cc --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gerror.c @@ -0,0 +1,103 @@ +/* + * gerror.c: Error support. + * + * Author: + * Miguel de Icaza (miguel@novell.com) + * + * (C) 2006 Novell, Inc. + * + * 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. + */ +#include +#include +#include +#include +#include +GError * +g_error_new (gpointer domain, gint code, const char *format, ...) +{ + va_list args; + GError *err = g_new (GError, 1); + + err->domain = domain; + err->code = code; + + va_start (args, format); + if (g_vasprintf (&err->message, format, args) == -1) + err->message = g_strdup_printf ("internal: invalid format string %s", format); + va_end (args); + + return err; +} + +static GError * +g_error_vnew (gpointer domain, gint code, const char *format, va_list ap) +{ + GError *err = g_new (GError, 1); + + err->domain = domain; + err->code = code; + + if (g_vasprintf (&err->message, format, ap) == -1) + err->message = g_strdup_printf ("internal: invalid format string %s", format); + + return err; +} + +void +g_clear_error (GError **error) +{ + if (error && *error) { + g_error_free (*error); + *error = NULL; + } +} + +void +g_error_free (GError *error) +{ + g_return_if_fail (error != NULL); + + g_free (error->message); + g_free (error); +} + +void +g_set_error (GError **err, gpointer domain, gint code, const gchar *format, ...) +{ + va_list args; + + if (err) { + va_start (args, format); + *err = g_error_vnew (domain, code, format, args); + va_end (args); + } +} + +void +g_propagate_error (GError **dest, GError *src) +{ + if (dest == NULL){ + if (src) + g_error_free (src); + } else { + *dest = src; + } +} diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gfile-posix.c b/unity-2019.4.24f1-mbe/mono/eglib/gfile-posix.c new file mode 100644 index 000000000..48a9192ab --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gfile-posix.c @@ -0,0 +1,175 @@ +/* + * File utility functions. + * + * Author: + * Gonzalo Paniagua Javier (gonzalo@novell.com) + * + * (C) 2006 Novell, Inc. + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include + +#ifdef _MSC_VER +#include +#endif +#ifdef G_OS_WIN32 +int mkstemp (char *tmp_template); +#endif + +#ifndef O_LARGEFILE +#define OPEN_FLAGS (O_RDONLY) +#else +#define OPEN_FLAGS (O_RDONLY | O_LARGEFILE) +#endif +gboolean +g_file_get_contents (const gchar *filename, gchar **contents, gsize *length, GError **error) +{ + gchar *str; + int fd; + struct stat st; + long offset; + int nread; + + g_return_val_if_fail (filename != NULL, FALSE); + g_return_val_if_fail (contents != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + *contents = NULL; + if (length) + *length = 0; + + fd = open (filename, OPEN_FLAGS); + if (fd == -1) { + if (error != NULL) { + int err = errno; + *error = g_error_new (G_LOG_DOMAIN, g_file_error_from_errno (err), "Error opening file"); + } + return FALSE; + } + + if (fstat (fd, &st) != 0) { + if (error != NULL) { + int err = errno; + *error = g_error_new (G_LOG_DOMAIN, g_file_error_from_errno (err), "Error in fstat()"); + } + close (fd); + return FALSE; + } + + str = g_malloc (st.st_size + 1); + offset = 0; + do { + nread = read (fd, str + offset, st.st_size - offset); + if (nread > 0) { + offset += nread; + } + } while ((nread > 0 && offset < st.st_size) || (nread == -1 && errno == EINTR)); + + close (fd); + str [st.st_size] = '\0'; + if (length) { + *length = st.st_size; + } + *contents = str; + return TRUE; +} + +gint +g_file_open_tmp (const gchar *tmpl, gchar **name_used, GError **error) +{ + const static gchar *default_tmpl = ".XXXXXX"; + gchar *t; + gint fd; + size_t len; + + g_return_val_if_fail (error == NULL || *error == NULL, -1); + + if (tmpl == NULL) + tmpl = default_tmpl; + + if (strchr (tmpl, G_DIR_SEPARATOR) != NULL) { + if (error) { + *error = g_error_new (G_LOG_DOMAIN, 24, "Template should not have any " G_DIR_SEPARATOR_S); + } + return -1; + } + + len = strlen (tmpl); + if (len < 6 || strcmp (tmpl + len - 6, "XXXXXX")) { + if (error) { + *error = g_error_new (G_LOG_DOMAIN, 24, "Template should end with XXXXXX"); + } + return -1; + } + + t = g_build_filename (g_get_tmp_dir (), tmpl, NULL); + + fd = mkstemp (t); + + if (fd == -1) { + if (error) { + int err = errno; + *error = g_error_new (G_LOG_DOMAIN, g_file_error_from_errno (err), "Error in mkstemp()"); + } + g_free (t); + return -1; + } + + if (name_used) { + *name_used = t; + } else { + g_free (t); + } + return fd; +} + +gchar * +g_get_current_dir (void) +{ + int s = 32; + char *buffer = NULL, *r; + gboolean fail; + + do { + buffer = g_realloc (buffer, s); + r = getcwd (buffer, s); + fail = (r == NULL && errno == ERANGE); + if (fail) { + s <<= 1; + } + } while (fail); + + /* On amd64 sometimes the bottom 32-bits of r == the bottom 32-bits of buffer + * but the top 32-bits of r have overflown to 0xffffffff (seriously wtf getcwd + * so we return the buffer here since it has a pointer to the valid string + */ + return buffer; +} diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gfile-unity.c b/unity-2019.4.24f1-mbe/mono/eglib/gfile-unity.c new file mode 100644 index 000000000..c7c6edf23 --- /dev/null +++ b/unity-2019.4.24f1-mbe/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/unity-2019.4.24f1-mbe/mono/eglib/gfile-unix.c b/unity-2019.4.24f1-mbe/mono/eglib/gfile-unix.c new file mode 100644 index 000000000..c213c8823 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gfile-unix.c @@ -0,0 +1,92 @@ +/* + * File utility functions. + * + * Author: + * Gonzalo Paniagua Javier (gonzalo@novell.com) + * + * (C) 2006 Novell, Inc. + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +gboolean +g_file_test (const gchar *filename, GFileTest test) +{ + struct stat st; + gboolean have_stat; + + if (filename == NULL || test == 0) + return FALSE; + + have_stat = FALSE; + + if ((test & G_FILE_TEST_EXISTS) != 0) { + if (access (filename, F_OK) == 0) + return TRUE; + } + + if ((test & G_FILE_TEST_IS_EXECUTABLE) != 0) { + if (access (filename, X_OK) == 0) + return TRUE; + } + if ((test & G_FILE_TEST_IS_SYMLINK) != 0) { + have_stat = (lstat (filename, &st) == 0); + if (have_stat && S_ISLNK (st.st_mode)) + return TRUE; + } + + if ((test & G_FILE_TEST_IS_REGULAR) != 0) { + if (!have_stat) + have_stat = (stat (filename, &st) == 0); + if (have_stat && S_ISREG (st.st_mode)) + return TRUE; + } + if ((test & G_FILE_TEST_IS_DIR) != 0) { + if (!have_stat) + have_stat = (stat (filename, &st) == 0); + if (have_stat && S_ISDIR (st.st_mode)) + return TRUE; + } + return FALSE; +} + +gchar * +g_mkdtemp (char *tmp_template) +{ +#ifdef HAVE_MKDTEMP + char *template_copy = g_strdup (tmp_template); + + return mkdtemp (template_copy); +#else + g_error("Function mkdtemp not supported"); +#endif +} diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gfile-win32.c b/unity-2019.4.24f1-mbe/mono/eglib/gfile-win32.c new file mode 100644 index 000000000..61e23cbe4 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gfile-win32.c @@ -0,0 +1,141 @@ +/* + * File utility functions. + * + * Author: + * Gonzalo Paniagua Javier (gonzalo@novell.com) + * + * (C) 2006 Novell, Inc. + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef G_OS_WIN32 +#include +#define open _open +#ifndef S_ISREG +#define S_ISREG(x) ((x & _S_IFMT) == _S_IFREG) +#endif +#ifndef S_ISDIR +#define S_ISDIR(x) ((x & _S_IFMT) == _S_IFDIR) +#endif +#endif + +int mkstemp (char *tmp_template) +{ + int fd; + gunichar2* utf16_template; + + utf16_template = u8to16 (tmp_template); + + fd = -1; + utf16_template = _wmktemp( utf16_template); + if (utf16_template && *utf16_template) { + /* FIXME: _O_TEMPORARY causes file to disappear on close causing a test to fail */ + fd = _wopen( utf16_template, _O_BINARY | _O_CREAT /*| _O_TEMPORARY*/ | _O_RDWR | _O_EXCL, _S_IREAD | _S_IWRITE); + } + + /* FIXME: this will crash if utf16_template == NULL */ + sprintf (tmp_template + strlen (tmp_template) - 6, "%S", utf16_template + wcslen (utf16_template) - 6); + + g_free (utf16_template); + return fd; +} + +gchar * +g_mkdtemp (char *tmp_template) +{ + gunichar2* utf16_template; + + utf16_template = u8to16 (tmp_template); + + utf16_template = _wmktemp(utf16_template); + if (utf16_template && *utf16_template) { + if (_wmkdir (utf16_template) == 0){ + char *ret = u16to8 (utf16_template); + g_free (utf16_template); + return ret; + } + } + + g_free (utf16_template); + return NULL; +} + +#ifdef _MSC_VER +#pragma warning(disable:4701) +#endif + +gboolean +g_file_test (const gchar *filename, GFileTest test) +{ + gunichar2* utf16_filename = NULL; + DWORD attr; + + if (filename == NULL || test == 0) + return FALSE; + + utf16_filename = u8to16 (filename); + attr = GetFileAttributesW (utf16_filename); + g_free (utf16_filename); + + if (attr == INVALID_FILE_ATTRIBUTES) + return FALSE; + + if ((test & G_FILE_TEST_EXISTS) != 0) { + return TRUE; + } + + if ((test & G_FILE_TEST_IS_EXECUTABLE) != 0) { + size_t len = strlen (filename); + if (len > 4 && strcmp (filename + len-3, "exe")) + return TRUE; + + return FALSE; + } + + if ((test & G_FILE_TEST_IS_REGULAR) != 0) { + if (attr & (FILE_ATTRIBUTE_DEVICE|FILE_ATTRIBUTE_DIRECTORY)) + return FALSE; + return TRUE; + } + + if ((test & G_FILE_TEST_IS_DIR) != 0) { + if (attr & FILE_ATTRIBUTE_DIRECTORY) + 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/unity-2019.4.24f1-mbe/mono/eglib/gfile.c b/unity-2019.4.24f1-mbe/mono/eglib/gfile.c new file mode 100644 index 000000000..01cfa251b --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gfile.c @@ -0,0 +1,157 @@ +/* + * File utility functions. + * + * Author: + * Gonzalo Paniagua Javier (gonzalo@novell.com) + * + * (C) 2006 Novell, Inc. + * + * 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. + */ + +#include + +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include + +static gpointer error_quark = "FileError"; + +gpointer +g_file_error_quark (void) +{ + return error_quark; +} + +GFileError +g_file_error_from_errno (gint err_no) +{ + switch (err_no) { + case EEXIST: + return G_FILE_ERROR_EXIST; + case EISDIR: + return G_FILE_ERROR_ISDIR; + case EACCES: + return G_FILE_ERROR_ACCES; + case ENAMETOOLONG: + return G_FILE_ERROR_NAMETOOLONG; + case ENOENT: + return G_FILE_ERROR_NOENT; + case ENOTDIR: + return G_FILE_ERROR_NOTDIR; + case ENXIO: + return G_FILE_ERROR_NXIO; + case ENODEV: + return G_FILE_ERROR_NODEV; + case EROFS: + return G_FILE_ERROR_ROFS; +#ifdef ETXTBSY + case ETXTBSY: + return G_FILE_ERROR_TXTBSY; +#endif + case EFAULT: + return G_FILE_ERROR_FAULT; +#ifdef ELOOP + case ELOOP: + return G_FILE_ERROR_LOOP; +#endif + case ENOSPC: + return G_FILE_ERROR_NOSPC; + case ENOMEM: + return G_FILE_ERROR_NOMEM; + case EMFILE: + return G_FILE_ERROR_MFILE; + case ENFILE: + return G_FILE_ERROR_NFILE; + case EBADF: + return G_FILE_ERROR_BADF; + case EINVAL: + return G_FILE_ERROR_INVAL; + case EPIPE: + return G_FILE_ERROR_PIPE; + case EAGAIN: + return G_FILE_ERROR_AGAIN; + case EINTR: + return G_FILE_ERROR_INTR; + case EIO: + return G_FILE_ERROR_IO; + case EPERM: + return G_FILE_ERROR_PERM; + case ENOSYS: + return G_FILE_ERROR_NOSYS; + default: + return G_FILE_ERROR_FAILED; + } +} + +#ifdef G_OS_WIN32 +#define TMP_FILE_FORMAT "%.*s%s.tmp" +#else +#define TMP_FILE_FORMAT "%.*s.%s~" +#endif + +gboolean +g_file_set_contents (const gchar *filename, const gchar *contents, gssize length, GError **err) +{ + const char *name; + char *path; + FILE *fp; + + if (!(name = strrchr (filename, G_DIR_SEPARATOR))) + name = filename; + else + name++; + + path = g_strdup_printf (TMP_FILE_FORMAT, name - filename, filename, name); + if (!(fp = fopen (path, "wb"))) { + g_set_error (err, G_FILE_ERROR, g_file_error_from_errno (errno), "%s", g_strerror (errno)); + g_free (path); + return FALSE; + } + + if (length < 0) + length = strlen (contents); + + if (fwrite (contents, 1, length, fp) < length) { + g_set_error (err, G_FILE_ERROR, g_file_error_from_errno (ferror (fp)), "%s", g_strerror (ferror (fp))); + g_unlink (path); + g_free (path); + fclose (fp); + + return FALSE; + } + + fclose (fp); + + if (g_rename (path, filename) != 0) { + g_set_error (err, G_FILE_ERROR, g_file_error_from_errno (errno), "%s", g_strerror (errno)); + g_unlink (path); + g_free (path); + return FALSE; + } + + g_free (path); + + return TRUE; +} diff --git a/unity-2019.4.24f1-mbe/mono/eglib/ghashtable.c b/unity-2019.4.24f1-mbe/mono/eglib/ghashtable.c new file mode 100644 index 000000000..5fac0eb0f --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/ghashtable.c @@ -0,0 +1,671 @@ +/* + * ghashtable.c: Hashtable implementation + * + * Author: + * Miguel de Icaza (miguel@novell.com) + * + * (C) 2006 Novell, Inc. + * + * 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. + */ +#include +#include +#include + +typedef struct _Slot Slot; + +struct _Slot { + gpointer key; + gpointer value; + Slot *next; +}; + +static gpointer KEYMARKER_REMOVED = &KEYMARKER_REMOVED; + +struct _GHashTable { + GHashFunc hash_func; + GEqualFunc key_equal_func; + + Slot **table; + int table_size; + int in_use; + int threshold; + int last_rehash; + GDestroyNotify value_destroy_func, key_destroy_func; +}; + +typedef struct { + GHashTable *ht; + int slot_index; + Slot *slot; +} Iter; + +static const guint prime_tbl[] = { + 11, 19, 37, 73, 109, 163, 251, 367, 557, 823, 1237, + 1861, 2777, 4177, 6247, 9371, 14057, 21089, 31627, + 47431, 71143, 106721, 160073, 240101, 360163, + 540217, 810343, 1215497, 1823231, 2734867, 4102283, + 6153409, 9230113, 13845163 +}; + +static gboolean +test_prime (int x) +{ + if ((x & 1) != 0) { + int n; + for (n = 3; n< (int)sqrt (x); n += 2) { + if ((x % n) == 0) + return FALSE; + } + return TRUE; + } + // There is only one even prime - 2. + return (x == 2); +} + +static int +calc_prime (int x) +{ + int i; + + for (i = (x & (~1))-1; i< G_MAXINT32; i += 2) { + if (test_prime (i)) + return i; + } + return x; +} + +guint +g_spaced_primes_closest (guint x) +{ + int i; + + for (i = 0; i < G_N_ELEMENTS (prime_tbl); i++) { + if (x <= prime_tbl [i]) + return prime_tbl [i]; + } + return calc_prime (x); +} + +GHashTable * +g_hash_table_new (GHashFunc hash_func, GEqualFunc key_equal_func) +{ + GHashTable *hash; + + if (hash_func == NULL) + hash_func = g_direct_hash; + if (key_equal_func == NULL) + key_equal_func = g_direct_equal; + hash = g_new0 (GHashTable, 1); + + hash->hash_func = hash_func; + hash->key_equal_func = key_equal_func; + + hash->table_size = g_spaced_primes_closest (1); + hash->table = g_new0 (Slot *, hash->table_size); + hash->last_rehash = hash->table_size; + + return hash; +} + +GHashTable * +g_hash_table_new_full (GHashFunc hash_func, GEqualFunc key_equal_func, + GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func) +{ + GHashTable *hash = g_hash_table_new (hash_func, key_equal_func); + if (hash == NULL) + return NULL; + + hash->key_destroy_func = key_destroy_func; + hash->value_destroy_func = value_destroy_func; + + return hash; +} + +#if 0 +static void +dump_hash_table (GHashTable *hash) +{ + int i; + + for (i = 0; i < hash->table_size; i++) { + Slot *s; + + for (s = hash->table [i]; s != NULL; s = s->next){ + guint hashcode = (*hash->hash_func) (s->key); + guint slot = (hashcode) % hash->table_size; + printf ("key %p hash %x on slot %d correct slot %d tb size %d\n", s->key, hashcode, i, slot, hash->table_size); + } + } +} +#endif + +#ifdef SANITY_CHECK +static void +sanity_check (GHashTable *hash) +{ + int i; + + for (i = 0; i < hash->table_size; i++) { + Slot *s; + + for (s = hash->table [i]; s != NULL; s = s->next){ + guint hashcode = (*hash->hash_func) (s->key); + guint slot = (hashcode) % hash->table_size; + if (slot != i) { + dump_hashcode_func = 1; + hashcode = (*hash->hash_func) (s->key); + dump_hashcode_func = 0; + g_error ("Key %p (bucket %d) on invalid bucket %d (hashcode %x) (tb size %d)", s->key, slot, i, hashcode, hash->table_size); + } + } + } +} +#else + +#define sanity_check(HASH) do {}while(0) + +#endif + +static void +do_rehash (GHashTable *hash) +{ + int current_size, i; + Slot **table; + + /* printf ("Resizing diff=%d slots=%d\n", hash->in_use - hash->last_rehash, hash->table_size); */ + hash->last_rehash = hash->table_size; + current_size = hash->table_size; + hash->table_size = g_spaced_primes_closest (hash->in_use); + /* printf ("New size: %d\n", hash->table_size); */ + table = hash->table; + hash->table = g_new0 (Slot *, hash->table_size); + + for (i = 0; i < current_size; i++){ + Slot *s, *next; + + for (s = table [i]; s != NULL; s = next){ + guint hashcode = ((*hash->hash_func) (s->key)) % hash->table_size; + next = s->next; + + s->next = hash->table [hashcode]; + hash->table [hashcode] = s; + } + } + g_free (table); +} + +static void +rehash (GHashTable *hash) +{ + int diff = ABS (hash->last_rehash - hash->in_use); + + /* These are the factors to play with to change the rehashing strategy */ + /* I played with them with a large range, and could not really get */ + /* something that was too good, maybe the tests are not that great */ + if (!(diff * 0.75 > hash->table_size * 2)) + return; + do_rehash (hash); + sanity_check (hash); +} + +void +g_hash_table_insert_replace (GHashTable *hash, gpointer key, gpointer value, gboolean replace) +{ + guint hashcode; + Slot *s; + GEqualFunc equal; + + g_return_if_fail (hash != NULL); + sanity_check (hash); + + equal = hash->key_equal_func; + if (hash->in_use >= hash->threshold) + rehash (hash); + + hashcode = ((*hash->hash_func) (key)) % hash->table_size; + for (s = hash->table [hashcode]; s != NULL; s = s->next){ + if ((*equal) (s->key, key)){ + if (replace){ + if (hash->key_destroy_func != NULL) + (*hash->key_destroy_func)(s->key); + s->key = key; + } + if (hash->value_destroy_func != NULL) + (*hash->value_destroy_func) (s->value); + s->value = value; + sanity_check (hash); + return; + } + } + s = g_new (Slot, 1); + s->key = key; + s->value = value; + s->next = hash->table [hashcode]; + hash->table [hashcode] = s; + hash->in_use++; + sanity_check (hash); +} + +GList* +g_hash_table_get_keys (GHashTable *hash) +{ + GHashTableIter iter; + GList *rv = NULL; + gpointer key; + + g_hash_table_iter_init (&iter, hash); + + while (g_hash_table_iter_next (&iter, &key, NULL)) + rv = g_list_prepend (rv, key); + + return g_list_reverse (rv); +} + +GList* +g_hash_table_get_values (GHashTable *hash) +{ + GHashTableIter iter; + GList *rv = NULL; + gpointer value; + + g_hash_table_iter_init (&iter, hash); + + while (g_hash_table_iter_next (&iter, NULL, &value)) + rv = g_list_prepend (rv, value); + + return g_list_reverse (rv); +} + + +guint +g_hash_table_size (GHashTable *hash) +{ + g_return_val_if_fail (hash != NULL, 0); + + return hash->in_use; +} + +gpointer +g_hash_table_lookup (GHashTable *hash, gconstpointer key) +{ + gpointer orig_key, value; + + if (g_hash_table_lookup_extended (hash, key, &orig_key, &value)) + return value; + else + return NULL; +} + +gboolean +g_hash_table_lookup_extended (GHashTable *hash, gconstpointer key, gpointer *orig_key, gpointer *value) +{ + GEqualFunc equal; + Slot *s; + guint hashcode; + + g_return_val_if_fail (hash != NULL, FALSE); + sanity_check (hash); + equal = hash->key_equal_func; + + hashcode = ((*hash->hash_func) (key)) % hash->table_size; + + for (s = hash->table [hashcode]; s != NULL; s = s->next){ + if ((*equal)(s->key, key)){ + if (orig_key) + *orig_key = s->key; + if (value) + *value = s->value; + return TRUE; + } + } + return FALSE; +} + +void +g_hash_table_foreach (GHashTable *hash, GHFunc func, gpointer user_data) +{ + int i; + + g_return_if_fail (hash != NULL); + g_return_if_fail (func != NULL); + + for (i = 0; i < hash->table_size; i++){ + Slot *s; + + for (s = hash->table [i]; s != NULL; s = s->next) + (*func)(s->key, s->value, user_data); + } +} + +gpointer +g_hash_table_find (GHashTable *hash, GHRFunc predicate, gpointer user_data) +{ + int i; + + g_return_val_if_fail (hash != NULL, NULL); + g_return_val_if_fail (predicate != NULL, NULL); + + for (i = 0; i < hash->table_size; i++){ + Slot *s; + + for (s = hash->table [i]; s != NULL; s = s->next) + if ((*predicate)(s->key, s->value, user_data)) + return s->value; + } + return NULL; +} + +void +g_hash_table_remove_all (GHashTable *hash) +{ + int i; + + g_return_if_fail (hash != NULL); + + for (i = 0; i < hash->table_size; i++){ + Slot *s; + + while (hash->table [i]) { + s = hash->table [i]; + g_hash_table_remove (hash, s->key); + } + } +} + +gboolean +g_hash_table_remove (GHashTable *hash, gconstpointer key) +{ + GEqualFunc equal; + Slot *s, *last; + guint hashcode; + + g_return_val_if_fail (hash != NULL, FALSE); + sanity_check (hash); + equal = hash->key_equal_func; + + hashcode = ((*hash->hash_func)(key)) % hash->table_size; + last = NULL; + for (s = hash->table [hashcode]; s != NULL; s = s->next){ + if ((*equal)(s->key, key)){ + if (hash->key_destroy_func != NULL) + (*hash->key_destroy_func)(s->key); + if (hash->value_destroy_func != NULL) + (*hash->value_destroy_func)(s->value); + if (last == NULL) + hash->table [hashcode] = s->next; + else + last->next = s->next; + g_free (s); + hash->in_use--; + sanity_check (hash); + return TRUE; + } + last = s; + } + sanity_check (hash); + return FALSE; +} + +guint +g_hash_table_foreach_remove (GHashTable *hash, GHRFunc func, gpointer user_data) +{ + int i; + int count = 0; + + g_return_val_if_fail (hash != NULL, 0); + g_return_val_if_fail (func != NULL, 0); + + sanity_check (hash); + for (i = 0; i < hash->table_size; i++){ + Slot *s, *last; + + last = NULL; + for (s = hash->table [i]; s != NULL; ){ + if ((*func)(s->key, s->value, user_data)){ + Slot *n; + + if (hash->key_destroy_func != NULL) + (*hash->key_destroy_func)(s->key); + if (hash->value_destroy_func != NULL) + (*hash->value_destroy_func)(s->value); + if (last == NULL){ + hash->table [i] = s->next; + n = s->next; + } else { + last->next = s->next; + n = last->next; + } + g_free (s); + hash->in_use--; + count++; + s = n; + } else { + last = s; + s = s->next; + } + } + } + sanity_check (hash); + if (count > 0) + rehash (hash); + return count; +} + +gboolean +g_hash_table_steal (GHashTable *hash, gconstpointer key) +{ + GEqualFunc equal; + Slot *s, *last; + guint hashcode; + + g_return_val_if_fail (hash != NULL, FALSE); + sanity_check (hash); + equal = hash->key_equal_func; + + hashcode = ((*hash->hash_func)(key)) % hash->table_size; + last = NULL; + for (s = hash->table [hashcode]; s != NULL; s = s->next){ + if ((*equal)(s->key, key)) { + if (last == NULL) + hash->table [hashcode] = s->next; + else + last->next = s->next; + g_free (s); + hash->in_use--; + sanity_check (hash); + return TRUE; + } + last = s; + } + sanity_check (hash); + return FALSE; + +} + +guint +g_hash_table_foreach_steal (GHashTable *hash, GHRFunc func, gpointer user_data) +{ + int i; + int count = 0; + + g_return_val_if_fail (hash != NULL, 0); + g_return_val_if_fail (func != NULL, 0); + + sanity_check (hash); + for (i = 0; i < hash->table_size; i++){ + Slot *s, *last; + + last = NULL; + for (s = hash->table [i]; s != NULL; ){ + if ((*func)(s->key, s->value, user_data)){ + Slot *n; + + if (last == NULL){ + hash->table [i] = s->next; + n = s->next; + } else { + last->next = s->next; + n = last->next; + } + g_free (s); + hash->in_use--; + count++; + s = n; + } else { + last = s; + s = s->next; + } + } + } + sanity_check (hash); + if (count > 0) + rehash (hash); + return count; +} + +void +g_hash_table_destroy (GHashTable *hash) +{ + int i; + + g_return_if_fail (hash != NULL); + + for (i = 0; i < hash->table_size; i++){ + Slot *s, *next; + + for (s = hash->table [i]; s != NULL; s = next){ + next = s->next; + + if (hash->key_destroy_func != NULL) + (*hash->key_destroy_func)(s->key); + if (hash->value_destroy_func != NULL) + (*hash->value_destroy_func)(s->value); + g_free (s); + } + } + g_free (hash->table); + + g_free (hash); +} + +void +g_hash_table_print_stats (GHashTable *table) +{ + int i, max_chain_index, chain_size, max_chain_size; + Slot *node; + + max_chain_size = 0; + max_chain_index = -1; + for (i = 0; i < table->table_size; i++) { + chain_size = 0; + for (node = table->table [i]; node; node = node->next) + chain_size ++; + if (chain_size > max_chain_size) { + max_chain_size = chain_size; + max_chain_index = i; + } + } + + printf ("Size: %d Table Size: %d Max Chain Length: %d at %d\n", table->in_use, table->table_size, max_chain_size, max_chain_index); +} + +void +g_hash_table_iter_init (GHashTableIter *it, GHashTable *hash_table) +{ + Iter *iter = (Iter*)it; + + memset (iter, 0, sizeof (Iter)); + iter->ht = hash_table; + iter->slot_index = -1; +} + +gboolean g_hash_table_iter_next (GHashTableIter *it, gpointer *key, gpointer *value) +{ + Iter *iter = (Iter*)it; + + GHashTable *hash = iter->ht; + + g_assert (iter->slot_index != -2); + g_assert (sizeof (Iter) <= sizeof (GHashTableIter)); + + if (!iter->slot) { + while (TRUE) { + iter->slot_index ++; + if (iter->slot_index >= hash->table_size) { + iter->slot_index = -2; + return FALSE; + } + if (hash->table [iter->slot_index]) + break; + } + iter->slot = hash->table [iter->slot_index]; + } + + if (key) + *key = iter->slot->key; + if (value) + *value = iter->slot->value; + iter->slot = iter->slot->next; + + return TRUE; +} + +gboolean +g_direct_equal (gconstpointer v1, gconstpointer v2) +{ + return v1 == v2; +} + +guint +g_direct_hash (gconstpointer v1) +{ + return GPOINTER_TO_UINT (v1); +} + +gboolean +g_int_equal (gconstpointer v1, gconstpointer v2) +{ + return *(gint *)v1 == *(gint *)v2; +} + +guint +g_int_hash (gconstpointer v1) +{ + return *(guint *)v1; +} + +gboolean +g_str_equal (gconstpointer v1, gconstpointer v2) +{ + return strcmp (v1, v2) == 0; +} + +guint +g_str_hash (gconstpointer v1) +{ + guint hash = 0; + char *p = (char *) v1; + + while (*p++) + hash = (hash << 5) - (hash + *p); + + return hash; +} diff --git a/unity-2019.4.24f1-mbe/mono/eglib/giconv.c b/unity-2019.4.24f1-mbe/mono/eglib/giconv.c new file mode 100644 index 000000000..c7723fef5 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/giconv.c @@ -0,0 +1,1340 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2011 Jeffrey Stedfast + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#ifdef HAVE_ICONV_H +#include +#endif +#include + +#ifdef _MSC_VER +#define FORCE_INLINE(RET_TYPE) __forceinline RET_TYPE +#else +#define FORCE_INLINE(RET_TYPE) inline RET_TYPE __attribute__((always_inline)) +#endif + + +#define UNROLL_DECODE_UTF8 0 +#define UNROLL_ENCODE_UTF8 0 + +typedef int (* Decoder) (char *inbuf, size_t inleft, gunichar *outchar); +typedef int (* Encoder) (gunichar c, char *outbuf, size_t outleft); + +struct _GIConv { + Decoder decode; + Encoder encode; + gunichar c; +#ifdef HAVE_ICONV + iconv_t cd; +#endif +}; + +static int decode_utf32be (char *inbuf, size_t inleft, gunichar *outchar); +static int encode_utf32be (gunichar c, char *outbuf, size_t outleft); + +static int decode_utf32le (char *inbuf, size_t inleft, gunichar *outchar); +static int encode_utf32le (gunichar c, char *outbuf, size_t outleft); + +static int decode_utf16be (char *inbuf, size_t inleft, gunichar *outchar); +static int encode_utf16be (gunichar c, char *outbuf, size_t outleft); + +static int decode_utf16le (char *inbuf, size_t inleft, gunichar *outchar); +static int encode_utf16le (gunichar c, char *outbuf, size_t outleft); + +static FORCE_INLINE (int) decode_utf8 (char *inbuf, size_t inleft, gunichar *outchar); +static int encode_utf8 (gunichar c, char *outbuf, size_t outleft); + +static int decode_latin1 (char *inbuf, size_t inleft, gunichar *outchar); +static int encode_latin1 (gunichar c, char *outbuf, size_t outleft); + +#if G_BYTE_ORDER == G_LITTLE_ENDIAN +#define decode_utf32 decode_utf32le +#define encode_utf32 encode_utf32le +#define decode_utf16 decode_utf16le +#define encode_utf16 encode_utf16le +#else +#define decode_utf32 decode_utf32be +#define encode_utf32 encode_utf32be +#define decode_utf16 decode_utf16be +#define encode_utf16 encode_utf16be +#endif + +static struct { + const char *name; + Decoder decoder; + Encoder encoder; +} charsets[] = { + { "ISO-8859-1", decode_latin1, encode_latin1 }, + { "ISO8859-1", decode_latin1, encode_latin1 }, + { "UTF-32BE", decode_utf32be, encode_utf32be }, + { "UTF-32LE", decode_utf32le, encode_utf32le }, + { "UTF-16BE", decode_utf16be, encode_utf16be }, + { "UTF-16LE", decode_utf16le, encode_utf16le }, + { "UTF-32", decode_utf32, encode_utf32 }, + { "UTF-16", decode_utf16, encode_utf16 }, + { "UTF-8", decode_utf8, encode_utf8 }, + { "US-ASCII", decode_latin1, encode_latin1 }, + { "Latin1", decode_latin1, encode_latin1 }, + { "ASCII", decode_latin1, encode_latin1 }, + { "UTF32", decode_utf32, encode_utf32 }, + { "UTF16", decode_utf16, encode_utf16 }, + { "UTF8", decode_utf8, encode_utf8 }, +}; + + +GIConv +g_iconv_open (const char *to_charset, const char *from_charset) +{ +#ifdef HAVE_ICONV + iconv_t icd = (iconv_t) -1; +#endif + Decoder decoder = NULL; + Encoder encoder = NULL; + GIConv cd; + guint i; + + if (!to_charset || !from_charset || !to_charset[0] || !from_charset[0]) { + errno = EINVAL; + + return (GIConv) -1; + } + + for (i = 0; i < G_N_ELEMENTS (charsets); i++) { + if (!g_ascii_strcasecmp (charsets[i].name, from_charset)) + decoder = charsets[i].decoder; + + if (!g_ascii_strcasecmp (charsets[i].name, to_charset)) + encoder = charsets[i].encoder; + } + + if (!encoder || !decoder) { +#ifdef HAVE_ICONV + if ((icd = iconv_open (to_charset, from_charset)) == (iconv_t) -1) + return (GIConv) -1; +#else + errno = EINVAL; + + return (GIConv) -1; +#endif + } + + cd = (GIConv) g_malloc (sizeof (struct _GIConv)); + cd->decode = decoder; + cd->encode = encoder; + cd->c = -1; + +#ifdef HAVE_ICONV + cd->cd = icd; +#endif + + return cd; +} + +int +g_iconv_close (GIConv cd) +{ +#ifdef HAVE_ICONV + if (cd->cd != (iconv_t) -1) + iconv_close (cd->cd); +#endif + + g_free (cd); + + return 0; +} + +gsize +g_iconv (GIConv cd, gchar **inbytes, gsize *inbytesleft, + gchar **outbytes, gsize *outbytesleft) +{ + gsize inleft, outleft; + char *inptr, *outptr; + gunichar c; + int rc = 0; + +#ifdef HAVE_ICONV + if (cd->cd != (iconv_t) -1) { + /* Note: gsize may have a different size than size_t, so we need to + remap inbytesleft and outbytesleft to size_t's. */ + size_t *outleftptr, *inleftptr; + size_t n_outleft, n_inleft; + + if (inbytesleft) { + n_inleft = *inbytesleft; + inleftptr = &n_inleft; + } else { + inleftptr = NULL; + } + + if (outbytesleft) { + n_outleft = *outbytesleft; + outleftptr = &n_outleft; + } else { + outleftptr = NULL; + } +#if defined(__NetBSD__) + return iconv (cd->cd, (const gchar **)inbytes, inleftptr, outbytes, outleftptr); +#else + return iconv (cd->cd, inbytes, inleftptr, outbytes, outleftptr); +#endif + } +#endif + + if (outbytes == NULL || outbytesleft == NULL) { + /* reset converter */ + cd->c = -1; + return 0; + } + + inleft = inbytesleft ? *inbytesleft : 0; + inptr = inbytes ? *inbytes : NULL; + outleft = *outbytesleft; + outptr = *outbytes; + + if ((c = cd->c) != (gunichar) -1) + goto encode; + + while (inleft > 0) { + if ((rc = cd->decode (inptr, inleft, &c)) < 0) + break; + + inleft -= rc; + inptr += rc; + + encode: + if ((rc = cd->encode (c, outptr, outleft)) < 0) + break; + + c = (gunichar) -1; + outleft -= rc; + outptr += rc; + } + + if (inbytesleft) + *inbytesleft = inleft; + + if (inbytes) + *inbytes = inptr; + + *outbytesleft = outleft; + *outbytes = outptr; + cd->c = c; + + return rc < 0 ? -1 : 0; +} + +/* + * Unicode encoders and decoders + */ + +static int +decode_utf32be (char *inbuf, size_t inleft, gunichar *outchar) +{ + unsigned char *inptr = (unsigned char *) inbuf; + gunichar c; + + if (inleft < 4) { + errno = EINVAL; + return -1; + } + + c = (inptr[0] << 24) | (inptr[1] << 16) | (inptr[2] << 8) | inptr[3]; + + if (c >= 0xd800 && c < 0xe000) { + errno = EILSEQ; + return -1; + } else if (c >= 0x110000) { + errno = EILSEQ; + return -1; + } + + *outchar = c; + + return 4; +} + +static int +decode_utf32le (char *inbuf, size_t inleft, gunichar *outchar) +{ + unsigned char *inptr = (unsigned char *) inbuf; + gunichar c; + + if (inleft < 4) { + errno = EINVAL; + return -1; + } + + c = (inptr[3] << 24) | (inptr[2] << 16) | (inptr[1] << 8) | inptr[0]; + + if (c >= 0xd800 && c < 0xe000) { + errno = EILSEQ; + return -1; + } else if (c >= 0x110000) { + errno = EILSEQ; + return -1; + } + + *outchar = c; + + return 4; +} + +static int +encode_utf32be (gunichar c, char *outbuf, size_t outleft) +{ + unsigned char *outptr = (unsigned char *) outbuf; + + if (outleft < 4) { + errno = E2BIG; + return -1; + } + + outptr[0] = (c >> 24) & 0xff; + outptr[1] = (c >> 16) & 0xff; + outptr[2] = (c >> 8) & 0xff; + outptr[3] = c & 0xff; + + return 4; +} + +static int +encode_utf32le (gunichar c, char *outbuf, size_t outleft) +{ + unsigned char *outptr = (unsigned char *) outbuf; + + if (outleft < 4) { + errno = E2BIG; + return -1; + } + + outptr[0] = c & 0xff; + outptr[1] = (c >> 8) & 0xff; + outptr[2] = (c >> 16) & 0xff; + outptr[3] = (c >> 24) & 0xff; + + return 4; +} + +static int +decode_utf16be (char *inbuf, size_t inleft, gunichar *outchar) +{ + unsigned char *inptr = (unsigned char *) inbuf; + gunichar2 c; + gunichar u; + + if (inleft < 2) { + errno = EINVAL; + return -1; + } + + u = (inptr[0] << 8) | inptr[1]; + + if (u < 0xd800) { + /* 0x0000 -> 0xd7ff */ + *outchar = u; + return 2; + } else if (u < 0xdc00) { + /* 0xd800 -> 0xdbff */ + if (inleft < 4) { + errno = EINVAL; + return -2; + } + + c = (inptr[2] << 8) | inptr[3]; + + if (c < 0xdc00 || c > 0xdfff) { + errno = EILSEQ; + return -2; + } + + u = ((u - 0xd800) << 10) + (c - 0xdc00) + 0x0010000UL; + *outchar = u; + + return 4; + } else if (u < 0xe000) { + /* 0xdc00 -> 0xdfff */ + errno = EILSEQ; + return -1; + } else { + /* 0xe000 -> 0xffff */ + *outchar = u; + return 2; + } +} + +static int +decode_utf16le (char *inbuf, size_t inleft, gunichar *outchar) +{ + unsigned char *inptr = (unsigned char *) inbuf; + gunichar2 c; + gunichar u; + + if (inleft < 2) { + errno = EINVAL; + return -1; + } + + u = (inptr[1] << 8) | inptr[0]; + + if (u < 0xd800) { + /* 0x0000 -> 0xd7ff */ + *outchar = u; + return 2; + } else if (u < 0xdc00) { + /* 0xd800 -> 0xdbff */ + if (inleft < 4) { + errno = EINVAL; + return -2; + } + + c = (inptr[3] << 8) | inptr[2]; + + if (c < 0xdc00 || c > 0xdfff) { + errno = EILSEQ; + return -2; + } + + u = ((u - 0xd800) << 10) + (c - 0xdc00) + 0x0010000UL; + *outchar = u; + + return 4; + } else if (u < 0xe000) { + /* 0xdc00 -> 0xdfff */ + errno = EILSEQ; + return -1; + } else { + /* 0xe000 -> 0xffff */ + *outchar = u; + return 2; + } +} + +static int +encode_utf16be (gunichar c, char *outbuf, size_t outleft) +{ + unsigned char *outptr = (unsigned char *) outbuf; + gunichar2 ch; + gunichar c2; + + if (c < 0x10000) { + if (outleft < 2) { + errno = E2BIG; + return -1; + } + + outptr[0] = (c >> 8) & 0xff; + outptr[1] = c & 0xff; + + return 2; + } else { + if (outleft < 4) { + errno = E2BIG; + return -1; + } + + c2 = c - 0x10000; + + ch = (gunichar2) ((c2 >> 10) + 0xd800); + outptr[0] = (ch >> 8) & 0xff; + outptr[1] = ch & 0xff; + + ch = (gunichar2) ((c2 & 0x3ff) + 0xdc00); + outptr[2] = (ch >> 8) & 0xff; + outptr[3] = ch & 0xff; + + return 4; + } +} + +static int +encode_utf16le (gunichar c, char *outbuf, size_t outleft) +{ + unsigned char *outptr = (unsigned char *) outbuf; + gunichar2 ch; + gunichar c2; + + if (c < 0x10000) { + if (outleft < 2) { + errno = E2BIG; + return -1; + } + + outptr[0] = c & 0xff; + outptr[1] = (c >> 8) & 0xff; + + return 2; + } else { + if (outleft < 4) { + errno = E2BIG; + return -1; + } + + c2 = c - 0x10000; + + ch = (gunichar2) ((c2 >> 10) + 0xd800); + outptr[0] = ch & 0xff; + outptr[1] = (ch >> 8) & 0xff; + + ch = (gunichar2) ((c2 & 0x3ff) + 0xdc00); + outptr[2] = ch & 0xff; + outptr[3] = (ch >> 8) & 0xff; + + return 4; + } +} + +static FORCE_INLINE (int) +decode_utf8 (char *inbuf, size_t inleft, gunichar *outchar) +{ + unsigned char *inptr = (unsigned char *) inbuf; + gunichar u; + int n, i; + + u = *inptr; + + if (u < 0x80) { + /* simple ascii case */ + *outchar = u; + return 1; + } else if (u < 0xc2) { + errno = EILSEQ; + return -1; + } else if (u < 0xe0) { + u &= 0x1f; + n = 2; + } else if (u < 0xf0) { + u &= 0x0f; + n = 3; + } else if (u < 0xf8) { + u &= 0x07; + n = 4; + } else if (u < 0xfc) { + u &= 0x03; + n = 5; + } else if (u < 0xfe) { + u &= 0x01; + n = 6; + } else { + errno = EILSEQ; + return -1; + } + + if (n > inleft) { + errno = EINVAL; + return -1; + } + +#if UNROLL_DECODE_UTF8 + switch (n) { + case 6: u = (u << 6) | (*++inptr ^ 0x80); + case 5: u = (u << 6) | (*++inptr ^ 0x80); + case 4: u = (u << 6) | (*++inptr ^ 0x80); + case 3: u = (u << 6) | (*++inptr ^ 0x80); + case 2: u = (u << 6) | (*++inptr ^ 0x80); + } +#else + for (i = 1; i < n; i++) + u = (u << 6) | (*++inptr ^ 0x80); +#endif + + *outchar = u; + + return n; +} + +static int +encode_utf8 (gunichar c, char *outbuf, size_t outleft) +{ + unsigned char *outptr = (unsigned char *) outbuf; + int base, n, i; + + if (c < 0x80) { + outptr[0] = c; + return 1; + } else if (c < 0x800) { + base = 192; + n = 2; + } else if (c < 0x10000) { + base = 224; + n = 3; + } else if (c < 0x200000) { + base = 240; + n = 4; + } else if (c < 0x4000000) { + base = 248; + n = 5; + } else { + base = 252; + n = 6; + } + + if (outleft < n) { + errno = E2BIG; + return -1; + } + +#if UNROLL_ENCODE_UTF8 + switch (n) { + case 6: outptr[5] = (c & 0x3f) | 0x80; c >>= 6; + case 5: outptr[4] = (c & 0x3f) | 0x80; c >>= 6; + case 4: outptr[3] = (c & 0x3f) | 0x80; c >>= 6; + case 3: outptr[2] = (c & 0x3f) | 0x80; c >>= 6; + case 2: outptr[1] = (c & 0x3f) | 0x80; c >>= 6; + case 1: outptr[0] = c | base; + } +#else + for (i = n - 1; i > 0; i--) { + outptr[i] = (c & 0x3f) | 0x80; + c >>= 6; + } + + outptr[0] = c | base; +#endif + + return n; +} + +static int +decode_latin1 (char *inbuf, size_t inleft, gunichar *outchar) +{ + *outchar = (unsigned char) *inbuf; + return 1; +} + +static int +encode_latin1 (gunichar c, char *outbuf, size_t outleft) +{ + if (outleft < 1) { + errno = E2BIG; + return -1; + } + + if (c > 0xff) { + errno = EILSEQ; + return -1; + } + + *outbuf = (char) c; + + return 1; +} + + +/* + * Simple conversion API + */ + +static gpointer error_quark = "ConvertError"; + +gpointer +g_convert_error_quark (void) +{ + return error_quark; +} + +gchar * +g_convert (const gchar *str, gssize len, const gchar *to_charset, const gchar *from_charset, + gsize *bytes_read, gsize *bytes_written, GError **err) +{ + gsize outsize, outused, outleft, inleft, grow, rc; + char *result, *outbuf, *inbuf; + gboolean flush = FALSE; + gboolean done = FALSE; + GIConv cd; + + g_return_val_if_fail (str != NULL, NULL); + g_return_val_if_fail (to_charset != NULL, NULL); + g_return_val_if_fail (from_charset != NULL, NULL); + + if ((cd = g_iconv_open (to_charset, from_charset)) == (GIConv) -1) { + g_set_error (err, G_CONVERT_ERROR, G_CONVERT_ERROR_NO_CONVERSION, + "Conversion from %s to %s not supported.", + from_charset, to_charset); + + if (bytes_written) + *bytes_written = 0; + + if (bytes_read) + *bytes_read = 0; + + return NULL; + } + + inleft = len < 0 ? strlen (str) : len; + inbuf = (char *) str; + + outleft = outsize = MAX (inleft, 8); + outbuf = result = g_malloc (outsize + 4); + + do { + if (!flush) + rc = g_iconv (cd, &inbuf, &inleft, &outbuf, &outleft); + else + rc = g_iconv (cd, NULL, NULL, &outbuf, &outleft); + + if (rc == (gsize) -1) { + switch (errno) { + case E2BIG: + /* grow our result buffer */ + grow = MAX (inleft, 8) << 1; + outused = outbuf - result; + outsize += grow; + outleft += grow; + result = g_realloc (result, outsize + 4); + outbuf = result + outused; + break; + case EINVAL: + /* incomplete input, stop converting and terminate here */ + if (flush) + done = TRUE; + else + flush = TRUE; + break; + case EILSEQ: + /* illegal sequence in the input */ + g_set_error (err, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE, "%s", g_strerror (errno)); + + if (bytes_read) { + /* save offset of the illegal input sequence */ + *bytes_read = (inbuf - str); + } + + if (bytes_written) + *bytes_written = 0; + + g_iconv_close (cd); + g_free (result); + return NULL; + default: + /* unknown errno */ + g_set_error (err, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED, "%s", g_strerror (errno)); + + if (bytes_written) + *bytes_written = 0; + + if (bytes_read) + *bytes_read = 0; + + g_iconv_close (cd); + g_free (result); + return NULL; + } + } else if (flush) { + /* input has been converted and output has been flushed */ + break; + } else { + /* input has been converted, need to flush the output */ + flush = TRUE; + } + } while (!done); + + g_iconv_close (cd); + + /* Note: not all charsets can be null-terminated with a single + null byte. UCS2, for example, needs 2 null bytes and UCS4 + needs 4. I hope that 4 null bytes is enough to terminate all + multibyte charsets? */ + + /* null-terminate the result */ + memset (outbuf, 0, 4); + + if (bytes_written) + *bytes_written = outbuf - result; + + if (bytes_read) + *bytes_read = inbuf - str; + + return result; +} + + +/* + * Unicode conversion + */ + +/** + * An explanation of the conversion can be found at: + * http://home.tiscali.nl/t876506/utf8tbl.html + * + **/ +gint +g_unichar_to_utf8 (gunichar c, gchar *outbuf) +{ + int base, n, i; + + if (c < 0x80) { + base = 0; + n = 1; + } else if (c < 0x800) { + base = 192; + n = 2; + } else if (c < 0x10000) { + base = 224; + n = 3; + } else if (c < 0x200000) { + base = 240; + n = 4; + } else if (c < 0x4000000) { + base = 248; + n = 5; + } else if (c < 0x80000000) { + base = 252; + n = 6; + } else { + return -1; + } + + if (outbuf != NULL) { + for (i = n - 1; i > 0; i--) { + /* mask off 6 bits worth and add 128 */ + outbuf[i] = (c & 0x3f) | 0x80; + c >>= 6; + } + + /* first character has a different base */ + outbuf[0] = c | base; + } + + return n; +} + +static FORCE_INLINE (int) +g_unichar_to_utf16 (gunichar c, gunichar2 *outbuf) +{ + gunichar c2; + + if (c < 0xd800) { + if (outbuf) + *outbuf = (gunichar2) c; + + return 1; + } else if (c < 0xe000) { + return -1; + } else if (c < 0x10000) { + if (outbuf) + *outbuf = (gunichar2) c; + + return 1; + } else if (c < 0x110000) { + if (outbuf) { + c2 = c - 0x10000; + + outbuf[0] = (gunichar2) ((c2 >> 10) + 0xd800); + outbuf[1] = (gunichar2) ((c2 & 0x3ff) + 0xdc00); + } + + return 2; + } else { + return -1; + } +} + +gunichar * +g_utf8_to_ucs4_fast (const gchar *str, glong len, glong *items_written) +{ + gunichar *outbuf, *outptr; + char *inptr; + glong n, i; + + g_return_val_if_fail (str != NULL, NULL); + + n = g_utf8_strlen (str, len); + + if (items_written) + *items_written = n; + + outptr = outbuf = g_malloc ((n + 1) * sizeof (gunichar)); + inptr = (char *) str; + + for (i = 0; i < n; i++) { + *outptr++ = g_utf8_get_char (inptr); + inptr = g_utf8_next_char (inptr); + } + + *outptr = 0; + + return outbuf; +} + +static gunichar2 * +eg_utf8_to_utf16_general (const gchar *str, glong len, glong *items_read, glong *items_written, gboolean include_nuls, GError **err) +{ + gunichar2 *outbuf, *outptr; + size_t outlen = 0; + size_t inleft; + char *inptr; + gunichar c; + int u, n; + + g_return_val_if_fail (str != NULL, NULL); + + if (len < 0) { + if (include_nuls) { + g_set_error (err, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED, "Conversions with embedded nulls must pass the string length"); + return NULL; + } + + len = strlen (str); + } + + inptr = (char *) str; + inleft = len; + + while (inleft > 0) { + if ((n = decode_utf8 (inptr, inleft, &c)) < 0) + goto error; + + if (c == 0 && !include_nuls) + break; + + if ((u = g_unichar_to_utf16 (c, NULL)) < 0) { + errno = EILSEQ; + goto error; + } + + outlen += u; + inleft -= n; + inptr += n; + } + + if (items_read) + *items_read = inptr - str; + + if (items_written) + *items_written = outlen; + + outptr = outbuf = g_malloc ((outlen + 1) * sizeof (gunichar2)); + inptr = (char *) str; + inleft = len; + + while (inleft > 0) { + if ((n = decode_utf8 (inptr, inleft, &c)) < 0) + break; + + if (c == 0 && !include_nuls) + break; + + outptr += g_unichar_to_utf16 (c, outptr); + inleft -= n; + inptr += n; + } + + *outptr = '\0'; + + return outbuf; + + error: + if (errno == EILSEQ) { + g_set_error (err, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE, + "Illegal byte sequence encounted in the input."); + } else if (items_read) { + /* partial input is ok if we can let our caller know... */ + } else { + g_set_error (err, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT, + "Partial byte sequence encountered in the input."); + } + + if (items_read) + *items_read = inptr - str; + + if (items_written) + *items_written = 0; + + return NULL; +} + +gunichar2 * +g_utf8_to_utf16 (const gchar *str, glong len, glong *items_read, glong *items_written, GError **err) +{ + return eg_utf8_to_utf16_general (str, len, items_read, items_written, FALSE, err); +} + +gunichar2 * +eg_utf8_to_utf16_with_nuls (const gchar *str, glong len, glong *items_read, glong *items_written, GError **err) +{ + return eg_utf8_to_utf16_general (str, len, items_read, items_written, TRUE, err); +} + +gunichar * +g_utf8_to_ucs4 (const gchar *str, glong len, glong *items_read, glong *items_written, GError **err) +{ + gunichar *outbuf, *outptr; + size_t outlen = 0; + size_t inleft; + char *inptr; + gunichar c; + int n; + + g_return_val_if_fail (str != NULL, NULL); + + if (len < 0) + len = strlen (str); + + inptr = (char *) str; + inleft = len; + + while (inleft > 0) { + if ((n = decode_utf8 (inptr, inleft, &c)) < 0) { + if (errno == EILSEQ) { + g_set_error (err, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE, + "Illegal byte sequence encounted in the input."); + } else if (items_read) { + /* partial input is ok if we can let our caller know... */ + break; + } else { + g_set_error (err, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT, + "Partial byte sequence encountered in the input."); + } + + if (items_read) + *items_read = inptr - str; + + if (items_written) + *items_written = 0; + + return NULL; + } else if (c == 0) + break; + + outlen += 4; + inleft -= n; + inptr += n; + } + + if (items_written) + *items_written = outlen / 4; + + if (items_read) + *items_read = inptr - str; + + outptr = outbuf = g_malloc (outlen + 4); + inptr = (char *) str; + inleft = len; + + while (inleft > 0) { + if ((n = decode_utf8 (inptr, inleft, &c)) < 0) + break; + else if (c == 0) + break; + + *outptr++ = c; + inleft -= n; + inptr += n; + } + + *outptr = 0; + + return outbuf; +} + +gchar * +g_utf16_to_utf8 (const gunichar2 *str, glong len, glong *items_read, glong *items_written, GError **err) +{ + char *inptr, *outbuf, *outptr; + size_t outlen = 0; + size_t inleft; + gunichar c; + int n; + + g_return_val_if_fail (str != NULL, NULL); + + if (len < 0) { + len = 0; + while (str[len]) + len++; + } + + inptr = (char *) str; + inleft = len * 2; + + while (inleft > 0) { + if ((n = decode_utf16 (inptr, inleft, &c)) < 0) { + if (n == -2 && inleft > 2) { + /* This means that the first UTF-16 char was read, but second failed */ + inleft -= 2; + inptr += 2; + } + + if (errno == EILSEQ) { + g_set_error (err, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE, + "Illegal byte sequence encounted in the input."); + } else if (items_read) { + /* partial input is ok if we can let our caller know... */ + break; + } else { + g_set_error (err, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT, + "Partial byte sequence encountered in the input."); + } + + if (items_read) + *items_read = (inptr - (char *) str) / 2; + + if (items_written) + *items_written = 0; + + return NULL; + } else if (c == 0) + break; + + outlen += g_unichar_to_utf8 (c, NULL); + inleft -= n; + inptr += n; + } + + if (items_read) + *items_read = (inptr - (char *) str) / 2; + + if (items_written) + *items_written = outlen; + + outptr = outbuf = g_malloc (outlen + 1); + inptr = (char *) str; + inleft = len * 2; + + while (inleft > 0) { + if ((n = decode_utf16 (inptr, inleft, &c)) < 0) + break; + else if (c == 0) + break; + + outptr += g_unichar_to_utf8 (c, outptr); + inleft -= n; + inptr += n; + } + + *outptr = '\0'; + + return outbuf; +} + +gunichar * +g_utf16_to_ucs4 (const gunichar2 *str, glong len, glong *items_read, glong *items_written, GError **err) +{ + gunichar *outbuf, *outptr; + size_t outlen = 0; + size_t inleft; + char *inptr; + gunichar c; + int n; + + g_return_val_if_fail (str != NULL, NULL); + + if (len < 0) { + len = 0; + while (str[len]) + len++; + } + + inptr = (char *) str; + inleft = len * 2; + + while (inleft > 0) { + if ((n = decode_utf16 (inptr, inleft, &c)) < 0) { + if (n == -2 && inleft > 2) { + /* This means that the first UTF-16 char was read, but second failed */ + inleft -= 2; + inptr += 2; + } + + if (errno == EILSEQ) { + g_set_error (err, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE, + "Illegal byte sequence encounted in the input."); + } else if (items_read) { + /* partial input is ok if we can let our caller know... */ + break; + } else { + g_set_error (err, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT, + "Partial byte sequence encountered in the input."); + } + + if (items_read) + *items_read = (inptr - (char *) str) / 2; + + if (items_written) + *items_written = 0; + + return NULL; + } else if (c == 0) + break; + + outlen += 4; + inleft -= n; + inptr += n; + } + + if (items_read) + *items_read = (inptr - (char *) str) / 2; + + if (items_written) + *items_written = outlen / 4; + + outptr = outbuf = g_malloc (outlen + 4); + inptr = (char *) str; + inleft = len * 2; + + while (inleft > 0) { + if ((n = decode_utf16 (inptr, inleft, &c)) < 0) + break; + else if (c == 0) + break; + + *outptr++ = c; + inleft -= n; + inptr += n; + } + + *outptr = 0; + + return outbuf; +} + +gchar * +g_ucs4_to_utf8 (const gunichar *str, glong len, glong *items_read, glong *items_written, GError **err) +{ + char *outbuf, *outptr; + size_t outlen = 0; + glong i; + int n; + + g_return_val_if_fail (str != NULL, NULL); + + if (len < 0) { + for (i = 0; str[i] != 0; i++) { + if ((n = g_unichar_to_utf8 (str[i], NULL)) < 0) { + g_set_error (err, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE, + "Illegal byte sequence encounted in the input."); + + if (items_written) + *items_written = 0; + + if (items_read) + *items_read = i; + + return NULL; + } + + outlen += n; + } + } else { + for (i = 0; i < len && str[i] != 0; i++) { + if ((n = g_unichar_to_utf8 (str[i], NULL)) < 0) { + g_set_error (err, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE, + "Illegal byte sequence encounted in the input."); + + if (items_written) + *items_written = 0; + + if (items_read) + *items_read = i; + + return NULL; + } + + outlen += n; + } + } + + len = i; + + outptr = outbuf = g_malloc (outlen + 1); + for (i = 0; i < len; i++) + outptr += g_unichar_to_utf8 (str[i], outptr); + *outptr = 0; + + if (items_written) + *items_written = outlen; + + if (items_read) + *items_read = i; + + return outbuf; +} + +gunichar2 * +g_ucs4_to_utf16 (const gunichar *str, glong len, glong *items_read, glong *items_written, GError **err) +{ + gunichar2 *outbuf, *outptr; + size_t outlen = 0; + glong i; + int n; + + g_return_val_if_fail (str != NULL, NULL); + + if (len < 0) { + for (i = 0; str[i] != 0; i++) { + if ((n = g_unichar_to_utf16 (str[i], NULL)) < 0) { + g_set_error (err, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE, + "Illegal byte sequence encounted in the input."); + + if (items_written) + *items_written = 0; + + if (items_read) + *items_read = i; + + return NULL; + } + + outlen += n; + } + } else { + for (i = 0; i < len && str[i] != 0; i++) { + if ((n = g_unichar_to_utf16 (str[i], NULL)) < 0) { + g_set_error (err, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE, + "Illegal byte sequence encounted in the input."); + + if (items_written) + *items_written = 0; + + if (items_read) + *items_read = i; + + return NULL; + } + + outlen += n; + } + } + + len = i; + + outptr = outbuf = g_malloc ((outlen + 1) * sizeof (gunichar2)); + for (i = 0; i < len; i++) + outptr += g_unichar_to_utf16 (str[i], outptr); + *outptr = 0; + + if (items_written) + *items_written = outlen; + + if (items_read) + *items_read = i; + + return outbuf; +} diff --git a/unity-2019.4.24f1-mbe/mono/eglib/glib.h b/unity-2019.4.24f1-mbe/mono/eglib/glib.h new file mode 100644 index 000000000..4babfcede --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/glib.h @@ -0,0 +1,1095 @@ +#ifndef __GLIB_H +#define __GLIB_H +#include +#include +#include +#include +#include +#include +#include + + +#ifdef _MSC_VER +#pragma include_alias(, ) +#endif + +#include + +/* inttypes.h is only available from VS2013 */ +#if !defined(_MSC_VER) || (_MSC_VER >= 1800) +#include +#endif + +#include +#ifndef EGLIB_NO_REMAP +#include +#endif + +#ifdef G_HAVE_ALLOCA_H +#include +#endif + +#ifdef WIN32 +/* For alloca */ +#include +#endif + +#ifndef offsetof +# define offsetof(s_name,n_name) (size_t)(char *)&(((s_name*)0)->m_name) +#endif + +#define __EGLIB_X11 1 + +#ifdef __cplusplus +#define G_BEGIN_DECLS extern "C" { +#define G_END_DECLS } +#else +#define G_BEGIN_DECLS +#define G_END_DECLS +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1900 +#define inline __inline +#endif + +G_BEGIN_DECLS + +/* + * Basic data types + */ +typedef int gint; +typedef unsigned int guint; +typedef short gshort; +typedef unsigned short gushort; +typedef long glong; +typedef unsigned long gulong; +typedef void * gpointer; +typedef const void * gconstpointer; +typedef char gchar; +typedef unsigned char guchar; + +/* Types defined in terms of the stdint.h */ +typedef int8_t gint8; +typedef uint8_t guint8; +typedef int16_t gint16; +typedef uint16_t guint16; +typedef int32_t gint32; +typedef uint32_t guint32; +typedef int64_t gint64; +typedef uint64_t guint64; +typedef float gfloat; +typedef double gdouble; +typedef int32_t gboolean; + +typedef guint16 gunichar2; +typedef guint32 gunichar; + +/* + * Macros + */ +#define G_N_ELEMENTS(s) (sizeof(s) / sizeof ((s) [0])) + +#define FALSE 0 +#define TRUE 1 + +#define G_MINSHORT SHRT_MIN +#define G_MAXSHORT SHRT_MAX +#define G_MAXUSHORT USHRT_MAX +#define G_MAXINT INT_MAX +#define G_MININT INT_MIN +#define G_MAXINT32 INT32_MAX +#define G_MAXUINT32 UINT32_MAX +#define G_MININT32 INT32_MIN +#define G_MININT64 INT64_MIN +#define G_MAXINT64 INT64_MAX +#define G_MAXUINT64 UINT64_MAX + +#define G_LITTLE_ENDIAN 1234 +#define G_BIG_ENDIAN 4321 +#define G_STMT_START do +#define G_STMT_END while (0) + +#define G_USEC_PER_SEC 1000000 + +#ifndef ABS +#define ABS(a) ((a) > 0 ? (a) : -(a)) +#endif + +#define G_STRUCT_OFFSET(p_type,field) offsetof(p_type,field) + +#define EGLIB_STRINGIFY(x) #x +#define EGLIB_TOSTRING(x) EGLIB_STRINGIFY(x) +#define G_STRLOC __FILE__ ":" EGLIB_TOSTRING(__LINE__) ":" + +#define G_CONST_RETURN const + +#define G_GUINT64_FORMAT PRIu64 +#define G_GINT64_FORMAT PRIi64 +#define G_GUINT32_FORMAT PRIu32 +#define G_GINT32_FORMAT PRIi32 + +/* + * Allocation + */ +void g_free (void *ptr); +gpointer g_realloc (gpointer obj, gsize size); +gpointer g_malloc (gsize x); +gpointer g_malloc0 (gsize x); +gpointer g_calloc (gsize n, gsize x); +gpointer g_try_malloc (gsize x); +gpointer g_try_realloc (gpointer obj, gsize size); + +#define g_new(type,size) ((type *) g_malloc (sizeof (type) * (size))) +#define g_new0(type,size) ((type *) g_malloc0 (sizeof (type)* (size))) +#define g_newa(type,size) ((type *) alloca (sizeof (type) * (size))) + +#define g_memmove(dest,src,len) memmove (dest, src, len) +#define g_renew(struct_type, mem, n_structs) g_realloc (mem, sizeof (struct_type) * n_structs) +#define g_alloca(size) alloca (size) + +gpointer g_memdup (gconstpointer mem, guint byte_size); +static inline gchar *g_strdup (const gchar *str) { if (str) { return (gchar*) g_memdup (str, (guint)strlen (str) + 1); } return NULL; } +gchar **g_strdupv (gchar **str_array); + +typedef struct { + gpointer (*malloc) (gsize n_bytes); + gpointer (*realloc) (gpointer mem, gsize n_bytes); + void (*free) (gpointer mem); + gpointer (*calloc) (gsize n_blocks, gsize n_block_bytes); +} GMemVTable; + +void g_mem_set_vtable (GMemVTable* vtable); + +struct _GMemChunk { + guint alloc_size; +}; + +typedef struct _GMemChunk GMemChunk; +/* + * Misc. + */ + +gboolean g_hasenv(const gchar *variable); +gchar * g_getenv(const gchar *variable); +gboolean g_setenv(const gchar *variable, const gchar *value, gboolean overwrite); +void g_unsetenv(const gchar *variable); + +gchar* g_win32_getlocale(void); + +/* + * Precondition macros + */ +#define g_warn_if_fail(x) G_STMT_START { if (!(x)) { g_warning ("%s:%d: assertion '%s' failed", __FILE__, __LINE__, #x); } } G_STMT_END +#define g_return_if_fail(x) G_STMT_START { if (!(x)) { g_critical ("%s:%d: assertion '%s' failed", __FILE__, __LINE__, #x); return; } } G_STMT_END +#define g_return_val_if_fail(x,e) G_STMT_START { if (!(x)) { g_critical ("%s:%d: assertion '%s' failed", __FILE__, __LINE__, #x); return (e); } } G_STMT_END + +/* + * Errors + */ +typedef struct { + /* In the real glib, this is a GQuark, but we dont use/need that */ + gpointer domain; + gint code; + gchar *message; +} GError; + +void g_clear_error (GError **error); +void g_error_free (GError *error); +GError *g_error_new (gpointer domain, gint code, const char *format, ...); +void g_set_error (GError **err, gpointer domain, gint code, const gchar *format, ...); +void g_propagate_error (GError **dest, GError *src); + +/* + * Strings utility + */ +gchar *g_strdup_printf (const gchar *format, ...); +gchar *g_strdup_vprintf (const gchar *format, va_list args); +gchar *g_strndup (const gchar *str, gsize n); +const gchar *g_strerror (gint errnum); +gchar *g_strndup (const gchar *str, gsize n); +void g_strfreev (gchar **str_array); +gchar *g_strconcat (const gchar *first, ...); +gchar **g_strsplit (const gchar *string, const gchar *delimiter, gint max_tokens); +gchar **g_strsplit_set (const gchar *string, const gchar *delimiter, gint max_tokens); +gchar *g_strreverse (gchar *str); +gboolean g_str_has_prefix (const gchar *str, const gchar *prefix); +gboolean g_str_has_suffix (const gchar *str, const gchar *suffix); +guint g_strv_length (gchar **str_array); +gchar *g_strjoin (const gchar *separator, ...); +gchar *g_strjoinv (const gchar *separator, gchar **str_array); +gchar *g_strchug (gchar *str); +gchar *g_strchomp (gchar *str); +void g_strdown (gchar *string); +gchar *g_strnfill (gsize length, gchar fill_char); + +gchar *g_strdelimit (gchar *string, const gchar *delimiters, gchar new_delimiter); +gchar *g_strescape (const gchar *source, const gchar *exceptions); + +gchar *g_filename_to_uri (const gchar *filename, const gchar *hostname, GError **error); +gchar *g_filename_from_uri (const gchar *uri, gchar **hostname, GError **error); + +gint g_printf (gchar const *format, ...); +gint g_fprintf (FILE *file, gchar const *format, ...); +gint g_sprintf (gchar *string, gchar const *format, ...); +gint g_snprintf (gchar *string, gulong n, gchar const *format, ...); +gint g_vasprintf (gchar **ret, const gchar *fmt, va_list ap); +#define g_vprintf vprintf +#define g_vfprintf vfprintf +#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); + + +gchar g_ascii_tolower (gchar c); +gchar g_ascii_toupper (gchar c); +gchar *g_ascii_strdown (const gchar *str, gssize len); +gchar *g_ascii_strup (const gchar *str, gssize len); +gint g_ascii_strncasecmp (const gchar *s1, const gchar *s2, gsize n); +gint g_ascii_strcasecmp (const gchar *s1, const gchar *s2); +gint g_ascii_xdigit_value (gchar c); +#define g_ascii_isspace(c) (isspace (c) != 0) +#define g_ascii_isalpha(c) (isalpha (c) != 0) +#define g_ascii_isprint(c) (isprint (c) != 0) +#define g_ascii_isxdigit(c) (isxdigit (c) != 0) + +/* FIXME: g_strcasecmp supports utf8 unicode stuff */ +#ifdef _MSC_VER +#define g_strcasecmp stricmp +#define g_strncasecmp strnicmp +#define g_strstrip(a) g_strchug (g_strchomp (a)) +#else +#define g_strcasecmp strcasecmp +#define g_ascii_strtoull strtoull +#define g_strncasecmp strncasecmp +#define g_strstrip(a) g_strchug (g_strchomp (a)) +#endif +#define g_ascii_strdup strdup + + +#define G_STR_DELIMITERS "_-|> <." + +/* + * String type + */ +typedef struct { + char *str; + gsize len; + gsize allocated_len; +} GString; + +GString *g_string_new (const gchar *init); +GString *g_string_new_len (const gchar *init, gssize len); +GString *g_string_sized_new (gsize default_size); +gchar *g_string_free (GString *string, gboolean free_segment); +GString *g_string_append (GString *string, const gchar *val); +void g_string_printf (GString *string, const gchar *format, ...); +void g_string_append_printf (GString *string, const gchar *format, ...); +void g_string_append_vprintf (GString *string, const gchar *format, va_list args); +GString *g_string_append_unichar (GString *string, gunichar c); +GString *g_string_append_c (GString *string, gchar c); +GString *g_string_append (GString *string, const gchar *val); +GString *g_string_append_len (GString *string, const gchar *val, gssize len); +GString *g_string_truncate (GString *string, gsize len); +GString *g_string_prepend (GString *string, const gchar *val); +GString *g_string_insert (GString *string, gssize pos, const gchar *val); +GString *g_string_set_size (GString *string, gsize len); +GString *g_string_erase (GString *string, gssize pos, gssize len); + +#define g_string_sprintfa g_string_append_printf + +typedef void (*GFunc) (gpointer data, gpointer user_data); +typedef gint (*GCompareFunc) (gconstpointer a, gconstpointer b); +typedef gint (*GCompareDataFunc) (gconstpointer a, gconstpointer b, gpointer user_data); +typedef void (*GHFunc) (gpointer key, gpointer value, gpointer user_data); +typedef gboolean (*GHRFunc) (gpointer key, gpointer value, gpointer user_data); +typedef void (*GDestroyNotify) (gpointer data); +typedef guint (*GHashFunc) (gconstpointer key); +typedef gboolean (*GEqualFunc) (gconstpointer a, gconstpointer b); +typedef void (*GFreeFunc) (gpointer data); + +/* + * Lists + */ +typedef struct _GSList GSList; +struct _GSList { + gpointer data; + GSList *next; +}; + +GSList *g_slist_alloc (void); +GSList *g_slist_append (GSList *list, + gpointer data); +GSList *g_slist_prepend (GSList *list, + gpointer data); +void g_slist_free (GSList *list); +void g_slist_free_1 (GSList *list); +GSList *g_slist_copy (GSList *list); +GSList *g_slist_concat (GSList *list1, + GSList *list2); +void g_slist_foreach (GSList *list, + GFunc func, + gpointer user_data); +GSList *g_slist_last (GSList *list); +GSList *g_slist_find (GSList *list, + gconstpointer data); +GSList *g_slist_find_custom (GSList *list, + gconstpointer data, + GCompareFunc func); +GSList *g_slist_remove (GSList *list, + gconstpointer data); +GSList *g_slist_remove_all (GSList *list, + gconstpointer data); +GSList *g_slist_reverse (GSList *list); +guint g_slist_length (GSList *list); +GSList *g_slist_remove_link (GSList *list, + GSList *link); +GSList *g_slist_delete_link (GSList *list, + GSList *link); +GSList *g_slist_insert_sorted (GSList *list, + gpointer data, + GCompareFunc func); +GSList *g_slist_insert_before (GSList *list, + GSList *sibling, + gpointer data); +GSList *g_slist_sort (GSList *list, + GCompareFunc func); +gint g_slist_index (GSList *list, + gconstpointer data); +GSList *g_slist_nth (GSList *list, + guint n); +gpointer g_slist_nth_data (GSList *list, + guint n); + +#define g_slist_next(slist) ((slist) ? (((GSList *) (slist))->next) : NULL) + + +typedef struct _GList GList; +struct _GList { + gpointer data; + GList *next; + GList *prev; +}; + +#define g_list_next(list) ((list) ? (((GList *) (list))->next) : NULL) +#define g_list_previous(list) ((list) ? (((GList *) (list))->prev) : NULL) + +GList *g_list_alloc (void); +GList *g_list_append (GList *list, + gpointer data); +GList *g_list_prepend (GList *list, + gpointer data); +void g_list_free (GList *list); +void g_list_free_1 (GList *list); +GList *g_list_copy (GList *list); +guint g_list_length (GList *list); +gint g_list_index (GList *list, + gconstpointer data); +GList *g_list_nth (GList *list, + guint n); +gpointer g_list_nth_data (GList *list, + guint n); +GList *g_list_last (GList *list); +GList *g_list_concat (GList *list1, + GList *list2); +void g_list_foreach (GList *list, + GFunc func, + gpointer user_data); +GList *g_list_first (GList *list); +GList *g_list_find (GList *list, + gconstpointer data); +GList *g_list_find_custom (GList *list, + gconstpointer data, + GCompareFunc func); +GList *g_list_remove (GList *list, + gconstpointer data); +GList *g_list_remove_all (GList *list, + gconstpointer data); +GList *g_list_reverse (GList *list); +GList *g_list_remove_link (GList *list, + GList *link); +GList *g_list_delete_link (GList *list, + GList *link); +GList *g_list_insert_sorted (GList *list, + gpointer data, + GCompareFunc func); +GList *g_list_insert_before (GList *list, + GList *sibling, + gpointer data); +GList *g_list_sort (GList *sort, + GCompareFunc func); + +/* + * Hashtables + */ +typedef struct _GHashTable GHashTable; +typedef struct _GHashTableIter GHashTableIter; + +/* Private, but needed for stack allocation */ +struct _GHashTableIter +{ + gpointer dummy [8]; +}; + +GHashTable *g_hash_table_new (GHashFunc hash_func, GEqualFunc key_equal_func); +GHashTable *g_hash_table_new_full (GHashFunc hash_func, GEqualFunc key_equal_func, + GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func); +void g_hash_table_insert_replace (GHashTable *hash, gpointer key, gpointer value, gboolean replace); +guint g_hash_table_size (GHashTable *hash); +GList *g_hash_table_get_keys (GHashTable *hash); +GList *g_hash_table_get_values (GHashTable *hash); +gpointer g_hash_table_lookup (GHashTable *hash, gconstpointer key); +gboolean g_hash_table_lookup_extended (GHashTable *hash, gconstpointer key, gpointer *orig_key, gpointer *value); +void g_hash_table_foreach (GHashTable *hash, GHFunc func, gpointer user_data); +gpointer g_hash_table_find (GHashTable *hash, GHRFunc predicate, gpointer user_data); +gboolean g_hash_table_remove (GHashTable *hash, gconstpointer key); +gboolean g_hash_table_steal (GHashTable *hash, gconstpointer key); +void g_hash_table_remove_all (GHashTable *hash); +guint g_hash_table_foreach_remove (GHashTable *hash, GHRFunc func, gpointer user_data); +guint g_hash_table_foreach_steal (GHashTable *hash, GHRFunc func, gpointer user_data); +void g_hash_table_destroy (GHashTable *hash); +void g_hash_table_print_stats (GHashTable *table); + +void g_hash_table_iter_init (GHashTableIter *iter, GHashTable *hash_table); +gboolean g_hash_table_iter_next (GHashTableIter *iter, gpointer *key, gpointer *value); + +guint g_spaced_primes_closest (guint x); + +#define g_hash_table_insert(h,k,v) g_hash_table_insert_replace ((h),(k),(v),FALSE) +#define g_hash_table_replace(h,k,v) g_hash_table_insert_replace ((h),(k),(v),TRUE) + +gboolean g_direct_equal (gconstpointer v1, gconstpointer v2); +guint g_direct_hash (gconstpointer v1); +gboolean g_int_equal (gconstpointer v1, gconstpointer v2); +guint g_int_hash (gconstpointer v1); +gboolean g_str_equal (gconstpointer v1, gconstpointer v2); +guint g_str_hash (gconstpointer v1); + +/* + * ByteArray + */ + +typedef struct _GByteArray GByteArray; +struct _GByteArray { + guint8 *data; + gint len; +}; + +GByteArray *g_byte_array_new (void); +GByteArray* g_byte_array_append (GByteArray *array, const guint8 *data, guint len); +guint8* g_byte_array_free (GByteArray *array, gboolean free_segment); +void g_byte_array_set_size (GByteArray *array, gint length); + +/* + * Array + */ + +typedef struct _GArray GArray; +struct _GArray { + gchar *data; + gint len; +}; + +GArray *g_array_new (gboolean zero_terminated, gboolean clear_, guint element_size); +GArray *g_array_sized_new (gboolean zero_terminated, gboolean clear_, guint element_size, guint reserved_size); +gchar* g_array_free (GArray *array, gboolean free_segment); +GArray *g_array_append_vals (GArray *array, gconstpointer data, guint len); +GArray* g_array_insert_vals (GArray *array, guint index_, gconstpointer data, guint len); +GArray* g_array_remove_index (GArray *array, guint index_); +GArray* g_array_remove_index_fast (GArray *array, guint index_); +void g_array_set_size (GArray *array, gint length); + +#define g_array_append_val(a,v) (g_array_append_vals((a),&(v),1)) +#define g_array_insert_val(a,i,v) (g_array_insert_vals((a),(i),&(v),1)) +#define g_array_index(a,t,i) *(t*)(((a)->data) + sizeof(t) * (i)) + +/* + * QSort +*/ + +void g_qsort_with_data (gpointer base, size_t nmemb, size_t size, GCompareDataFunc compare, gpointer user_data); + +/* + * Pointer Array + */ + +typedef struct _GPtrArray GPtrArray; +struct _GPtrArray { + gpointer *pdata; + guint len; +}; + +GPtrArray *g_ptr_array_new (void); +GPtrArray *g_ptr_array_sized_new (guint reserved_size); +void g_ptr_array_add (GPtrArray *array, gpointer data); +gboolean g_ptr_array_remove (GPtrArray *array, gpointer data); +gpointer g_ptr_array_remove_index (GPtrArray *array, guint index); +gboolean g_ptr_array_remove_fast (GPtrArray *array, gpointer data); +gpointer g_ptr_array_remove_index_fast (GPtrArray *array, guint index); +void g_ptr_array_sort (GPtrArray *array, GCompareFunc compare_func); +void g_ptr_array_sort_with_data (GPtrArray *array, GCompareDataFunc compare_func, gpointer user_data); +void g_ptr_array_set_size (GPtrArray *array, gint length); +gpointer *g_ptr_array_free (GPtrArray *array, gboolean free_seg); +void g_ptr_array_foreach (GPtrArray *array, GFunc func, gpointer user_data); +guint g_ptr_array_capacity (GPtrArray *array); +#define g_ptr_array_index(array,index) (array)->pdata[(index)] + +/* + * Queues + */ +typedef struct { + GList *head; + GList *tail; + guint length; +} GQueue; + +gpointer g_queue_pop_head (GQueue *queue); +void g_queue_push_head (GQueue *queue, + gpointer data); +void g_queue_push_tail (GQueue *queue, + gpointer data); +gboolean g_queue_is_empty (GQueue *queue); +GQueue *g_queue_new (void); +void g_queue_free (GQueue *queue); +void g_queue_foreach (GQueue *queue, GFunc func, gpointer user_data); + +/* + * Messages + */ +#ifndef G_LOG_DOMAIN +#define G_LOG_DOMAIN ((gchar*) 0) +#endif + +typedef enum { + G_LOG_FLAG_RECURSION = 1 << 0, + G_LOG_FLAG_FATAL = 1 << 1, + + G_LOG_LEVEL_ERROR = 1 << 2, + G_LOG_LEVEL_CRITICAL = 1 << 3, + G_LOG_LEVEL_WARNING = 1 << 4, + G_LOG_LEVEL_MESSAGE = 1 << 5, + G_LOG_LEVEL_INFO = 1 << 6, + G_LOG_LEVEL_DEBUG = 1 << 7, + + G_LOG_LEVEL_MASK = ~(G_LOG_FLAG_RECURSION | G_LOG_FLAG_FATAL) +} GLogLevelFlags; + +void g_print (const gchar *format, ...); +void g_printerr (const gchar *format, ...); +GLogLevelFlags g_log_set_always_fatal (GLogLevelFlags fatal_mask); +GLogLevelFlags g_log_set_fatal_mask (const gchar *log_domain, GLogLevelFlags fatal_mask); +void g_logv (const gchar *log_domain, GLogLevelFlags log_level, const gchar *format, va_list args); +void g_log (const gchar *log_domain, GLogLevelFlags log_level, const gchar *format, ...); +void g_assertion_message (const gchar *format, ...) G_GNUC_NORETURN; + +#ifdef HAVE_C99_SUPPORT +/* The for (;;) tells gc thats g_error () doesn't return, avoiding warnings */ +#define g_error(format, ...) do { g_log (G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, format, __VA_ARGS__); for (;;); } while (0) +#define g_critical(format, ...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, format, __VA_ARGS__) +#define g_warning(format, ...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, format, __VA_ARGS__) +#define g_message(format, ...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, format, __VA_ARGS__) +#define g_debug(format, ...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, format, __VA_ARGS__) +#else /* HAVE_C99_SUPPORT */ +#define g_error(...) do { g_log (G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, __VA_ARGS__); for (;;); } while (0) +#define g_critical(...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, __VA_ARGS__) +#define g_warning(...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, __VA_ARGS__) +#define g_message(...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, __VA_ARGS__) +#define g_debug(...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, __VA_ARGS__) +#endif /* ndef HAVE_C99_SUPPORT */ + +typedef void (*GLogFunc) (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data); +typedef void (*GPrintFunc) (const gchar *string); + +void g_log_default_handler (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer unused_data); +GLogFunc g_log_set_default_handler (GLogFunc log_func, gpointer user_data); +GPrintFunc g_set_print_handler (GPrintFunc func); +GPrintFunc g_set_printerr_handler (GPrintFunc func); +/* + * Conversions + */ + +gpointer g_convert_error_quark(void); + + +/* + * Unicode Manipulation: most of this is not used by Mono by default, it is + * only used if the old collation code is activated, so this is only the + * bare minimum to build. + */ + +typedef enum { + G_UNICODE_CONTROL, + G_UNICODE_FORMAT, + G_UNICODE_UNASSIGNED, + G_UNICODE_PRIVATE_USE, + G_UNICODE_SURROGATE, + G_UNICODE_LOWERCASE_LETTER, + G_UNICODE_MODIFIER_LETTER, + G_UNICODE_OTHER_LETTER, + G_UNICODE_TITLECASE_LETTER, + G_UNICODE_UPPERCASE_LETTER, + G_UNICODE_COMBINING_MARK, + G_UNICODE_ENCLOSING_MARK, + G_UNICODE_NON_SPACING_MARK, + G_UNICODE_DECIMAL_NUMBER, + G_UNICODE_LETTER_NUMBER, + G_UNICODE_OTHER_NUMBER, + G_UNICODE_CONNECT_PUNCTUATION, + G_UNICODE_DASH_PUNCTUATION, + G_UNICODE_CLOSE_PUNCTUATION, + G_UNICODE_FINAL_PUNCTUATION, + G_UNICODE_INITIAL_PUNCTUATION, + G_UNICODE_OTHER_PUNCTUATION, + G_UNICODE_OPEN_PUNCTUATION, + G_UNICODE_CURRENCY_SYMBOL, + G_UNICODE_MODIFIER_SYMBOL, + G_UNICODE_MATH_SYMBOL, + G_UNICODE_OTHER_SYMBOL, + G_UNICODE_LINE_SEPARATOR, + G_UNICODE_PARAGRAPH_SEPARATOR, + G_UNICODE_SPACE_SEPARATOR +} GUnicodeType; + +typedef enum { + G_UNICODE_BREAK_MANDATORY, + G_UNICODE_BREAK_CARRIAGE_RETURN, + G_UNICODE_BREAK_LINE_FEED, + G_UNICODE_BREAK_COMBINING_MARK, + G_UNICODE_BREAK_SURROGATE, + G_UNICODE_BREAK_ZERO_WIDTH_SPACE, + G_UNICODE_BREAK_INSEPARABLE, + G_UNICODE_BREAK_NON_BREAKING_GLUE, + G_UNICODE_BREAK_CONTINGENT, + G_UNICODE_BREAK_SPACE, + G_UNICODE_BREAK_AFTER, + G_UNICODE_BREAK_BEFORE, + G_UNICODE_BREAK_BEFORE_AND_AFTER, + G_UNICODE_BREAK_HYPHEN, + G_UNICODE_BREAK_NON_STARTER, + G_UNICODE_BREAK_OPEN_PUNCTUATION, + G_UNICODE_BREAK_CLOSE_PUNCTUATION, + G_UNICODE_BREAK_QUOTATION, + G_UNICODE_BREAK_EXCLAMATION, + G_UNICODE_BREAK_IDEOGRAPHIC, + G_UNICODE_BREAK_NUMERIC, + G_UNICODE_BREAK_INFIX_SEPARATOR, + G_UNICODE_BREAK_SYMBOL, + G_UNICODE_BREAK_ALPHABETIC, + G_UNICODE_BREAK_PREFIX, + G_UNICODE_BREAK_POSTFIX, + G_UNICODE_BREAK_COMPLEX_CONTEXT, + G_UNICODE_BREAK_AMBIGUOUS, + G_UNICODE_BREAK_UNKNOWN, + G_UNICODE_BREAK_NEXT_LINE, + G_UNICODE_BREAK_WORD_JOINER, + G_UNICODE_BREAK_HANGUL_L_JAMO, + G_UNICODE_BREAK_HANGUL_V_JAMO, + G_UNICODE_BREAK_HANGUL_T_JAMO, + G_UNICODE_BREAK_HANGUL_LV_SYLLABLE, + G_UNICODE_BREAK_HANGUL_LVT_SYLLABLE +} GUnicodeBreakType; + +gunichar g_unichar_toupper (gunichar c); +gunichar g_unichar_tolower (gunichar c); +gunichar g_unichar_totitle (gunichar c); +GUnicodeType g_unichar_type (gunichar c); +gboolean g_unichar_isspace (gunichar c); +gboolean g_unichar_isxdigit (gunichar c); +gint g_unichar_xdigit_value (gunichar c); +GUnicodeBreakType g_unichar_break_type (gunichar c); + +#ifndef MAX +#define MAX(a,b) (((a)>(b)) ? (a) : (b)) +#endif + +#ifndef MIN +#define MIN(a,b) (((a)<(b)) ? (a) : (b)) +#endif + +#ifndef CLAMP +#define CLAMP(a,low,high) (((a) < (low)) ? (low) : (((a) > (high)) ? (high) : (a))) +#endif + +#if defined(__GNUC__) && (__GNUC__ > 2) +#define G_LIKELY(expr) (__builtin_expect ((expr) != 0, 1)) +#define G_UNLIKELY(expr) (__builtin_expect ((expr) != 0, 0)) +#else +#define G_LIKELY(x) (x) +#define G_UNLIKELY(x) (x) +#endif + +#if defined(_MSC_VER) +#define eg_unreachable() __assume(0) +#elif defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && (__GNUC_MINOR__ >= 5))) +#define eg_unreachable() __builtin_unreachable() +#else +#define eg_unreachable() +#endif + +#define g_assert(x) G_STMT_START { if (G_UNLIKELY (!(x))) g_assertion_message ("* Assertion at %s:%d, condition `%s' not met\n", __FILE__, __LINE__, #x); } G_STMT_END +#define g_assert_not_reached() G_STMT_START { g_assertion_message ("* Assertion: should not be reached at %s:%d\n", __FILE__, __LINE__); eg_unreachable(); } G_STMT_END + +/* + * Unicode conversion + */ + +#define G_CONVERT_ERROR g_convert_error_quark() + +typedef enum { + G_CONVERT_ERROR_NO_CONVERSION, + G_CONVERT_ERROR_ILLEGAL_SEQUENCE, + G_CONVERT_ERROR_FAILED, + G_CONVERT_ERROR_PARTIAL_INPUT, + G_CONVERT_ERROR_BAD_URI, + G_CONVERT_ERROR_NOT_ABSOLUTE_PATH +} GConvertError; + +gchar *g_utf8_strup (const gchar *str, gssize len); +gchar *g_utf8_strdown (const gchar *str, gssize len); +gint g_unichar_to_utf8 (gunichar c, gchar *outbuf); +gunichar *g_utf8_to_ucs4_fast (const gchar *str, glong len, glong *items_written); +gunichar *g_utf8_to_ucs4 (const gchar *str, glong len, glong *items_read, glong *items_written, GError **err); +gunichar2 *g_utf8_to_utf16 (const gchar *str, glong len, glong *items_read, glong *items_written, GError **err); +gunichar2 *eg_utf8_to_utf16_with_nuls (const gchar *str, glong len, glong *items_read, glong *items_written, GError **err); +gchar *g_utf16_to_utf8 (const gunichar2 *str, glong len, glong *items_read, glong *items_written, GError **err); +gunichar *g_utf16_to_ucs4 (const gunichar2 *str, glong len, glong *items_read, glong *items_written, GError **err); +gchar *g_ucs4_to_utf8 (const gunichar *str, glong len, glong *items_read, glong *items_written, GError **err); +gunichar2 *g_ucs4_to_utf16 (const gunichar *str, glong len, glong *items_read, glong *items_written, GError **err); + +#define u8to16(str) g_utf8_to_utf16(str, (glong)strlen(str), NULL, NULL, NULL) + +#ifdef G_OS_WIN32 +#define u16to8(str) g_utf16_to_utf8((gunichar2 *) (str), (glong)wcslen((wchar_t *) (str)), NULL, NULL, NULL) +#else +#define u16to8(str) g_utf16_to_utf8(str, (glong)strlen(str), NULL, NULL, NULL) +#endif + +/* + * Path + */ +gchar *g_build_path (const gchar *separator, const gchar *first_element, ...); +#define g_build_filename(x, ...) g_build_path(G_DIR_SEPARATOR_S, x, __VA_ARGS__) +gchar *g_path_get_dirname (const gchar *filename); +gchar *g_path_get_basename (const char *filename); +gchar *g_find_program_in_path (const gchar *program); +gchar *g_get_current_dir (void); +gboolean g_path_is_absolute (const char *filename); + +const gchar *g_get_home_dir (void); +const gchar *g_get_tmp_dir (void); +const gchar *g_get_user_name (void); +gchar *g_get_prgname (void); +void g_set_prgname (const gchar *prgname); + +gboolean g_ensure_directory_exists (const gchar *filename); + +/* + * Shell + */ + +gboolean g_shell_parse_argv (const gchar *command_line, gint *argcp, gchar ***argvp, GError **error); +gchar *g_shell_unquote (const gchar *quoted_string, GError **error); +gchar *g_shell_quote (const gchar *unquoted_string); + +/* + * Spawn + */ +typedef enum { + G_SPAWN_LEAVE_DESCRIPTORS_OPEN = 1, + G_SPAWN_DO_NOT_REAP_CHILD = 1 << 1, + G_SPAWN_SEARCH_PATH = 1 << 2, + G_SPAWN_STDOUT_TO_DEV_NULL = 1 << 3, + G_SPAWN_STDERR_TO_DEV_NULL = 1 << 4, + G_SPAWN_CHILD_INHERITS_STDIN = 1 << 5, + G_SPAWN_FILE_AND_ARGV_ZERO = 1 << 6 +} GSpawnFlags; + +typedef void (*GSpawnChildSetupFunc) (gpointer user_data); + +gboolean g_spawn_command_line_sync (const gchar *command_line, gchar **standard_output, gchar **standard_error, gint *exit_status, GError **error); +gboolean g_spawn_async_with_pipes (const gchar *working_directory, gchar **argv, gchar **envp, GSpawnFlags flags, GSpawnChildSetupFunc child_setup, + gpointer user_data, GPid *child_pid, gint *standard_input, gint *standard_output, gint *standard_error, GError **error); + +int eg_getdtablesize (void); + +/* + * Timer + */ +typedef struct _GTimer GTimer; + +GTimer *g_timer_new (void); +void g_timer_destroy (GTimer *timer); +gdouble g_timer_elapsed (GTimer *timer, gulong *microseconds); +void g_timer_stop (GTimer *timer); +void g_timer_start (GTimer *timer); + +/* + * Date and time + */ +typedef struct { + glong tv_sec; + glong tv_usec; +} GTimeVal; + +void g_get_current_time (GTimeVal *result); +void g_usleep (gulong microseconds); + +/* + * File + */ + +gpointer g_file_error_quark (void); + +#define G_FILE_ERROR g_file_error_quark () + +typedef enum { + G_FILE_ERROR_EXIST, + G_FILE_ERROR_ISDIR, + G_FILE_ERROR_ACCES, + G_FILE_ERROR_NAMETOOLONG, + G_FILE_ERROR_NOENT, + G_FILE_ERROR_NOTDIR, + G_FILE_ERROR_NXIO, + G_FILE_ERROR_NODEV, + G_FILE_ERROR_ROFS, + G_FILE_ERROR_TXTBSY, + G_FILE_ERROR_FAULT, + G_FILE_ERROR_LOOP, + G_FILE_ERROR_NOSPC, + G_FILE_ERROR_NOMEM, + G_FILE_ERROR_MFILE, + G_FILE_ERROR_NFILE, + G_FILE_ERROR_BADF, + G_FILE_ERROR_INVAL, + G_FILE_ERROR_PIPE, + G_FILE_ERROR_AGAIN, + G_FILE_ERROR_INTR, + G_FILE_ERROR_IO, + G_FILE_ERROR_PERM, + G_FILE_ERROR_NOSYS, + G_FILE_ERROR_FAILED +} GFileError; + +typedef enum { + G_FILE_TEST_IS_REGULAR = 1 << 0, + G_FILE_TEST_IS_SYMLINK = 1 << 1, + G_FILE_TEST_IS_DIR = 1 << 2, + G_FILE_TEST_IS_EXECUTABLE = 1 << 3, + G_FILE_TEST_EXISTS = 1 << 4 +} GFileTest; + + +gboolean g_file_set_contents (const gchar *filename, const gchar *contents, gssize length, GError **error); +gboolean g_file_get_contents (const gchar *filename, gchar **contents, gsize *length, GError **error); +GFileError g_file_error_from_errno (gint err_no); +gint g_file_open_tmp (const gchar *tmpl, gchar **name_used, GError **error); +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 +#define g_mkstemp mkstemp +#define g_ascii_isdigit isdigit +#define g_ascii_strtod strtod +#define g_ascii_isalnum isalnum + +gchar *g_mkdtemp (gchar *tmpl); + +/* + * Pattern matching + */ +typedef struct _GPatternSpec GPatternSpec; +GPatternSpec * g_pattern_spec_new (const gchar *pattern); +void g_pattern_spec_free (GPatternSpec *pspec); +gboolean g_pattern_match_string (GPatternSpec *pspec, const gchar *string); + +/* + * Directory + */ +typedef struct _GDir GDir; +GDir *g_dir_open (const gchar *path, guint flags, GError **error); +const gchar *g_dir_read_name (GDir *dir); +void g_dir_rewind (GDir *dir); +void g_dir_close (GDir *dir); + +int g_mkdir_with_parents (const gchar *pathname, int mode); +#define g_mkdir mkdir + +/* + * GMarkup + */ +typedef struct _GMarkupParseContext GMarkupParseContext; + +typedef enum +{ + G_MARKUP_DO_NOT_USE_THIS_UNSUPPORTED_FLAG = 1 << 0, + G_MARKUP_TREAT_CDATA_AS_TEXT = 1 << 1 +} GMarkupParseFlags; + +typedef struct { + void (*start_element) (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error); + + void (*end_element) (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error); + + void (*text) (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error); + + void (*passthrough) (GMarkupParseContext *context, + const gchar *passthrough_text, + gsize text_len, + gpointer user_data, + GError **error); + void (*error) (GMarkupParseContext *context, + GError *error, + gpointer user_data); +} GMarkupParser; + +GMarkupParseContext *g_markup_parse_context_new (const GMarkupParser *parser, + GMarkupParseFlags flags, + gpointer user_data, + GDestroyNotify user_data_dnotify); +void g_markup_parse_context_free (GMarkupParseContext *context); +gboolean g_markup_parse_context_parse (GMarkupParseContext *context, + const gchar *text, gssize text_len, + GError **error); +gboolean g_markup_parse_context_end_parse (GMarkupParseContext *context, + GError **error); + +/* + * Character set conversion + */ +typedef struct _GIConv *GIConv; + +gsize g_iconv (GIConv cd, gchar **inbytes, gsize *inbytesleft, gchar **outbytes, gsize *outbytesleft); +GIConv g_iconv_open (const gchar *to_charset, const gchar *from_charset); +int g_iconv_close (GIConv cd); + +gboolean g_get_charset (G_CONST_RETURN char **charset); +gchar *g_locale_to_utf8 (const gchar *opsysstring, gssize len, + gsize *bytes_read, gsize *bytes_written, + GError **error); +gchar *g_locale_from_utf8 (const gchar *utf8string, gssize len, gsize *bytes_read, + gsize *bytes_written, GError **error); +gchar *g_filename_from_utf8 (const gchar *utf8string, gssize len, gsize *bytes_read, + gsize *bytes_written, GError **error); +gchar *g_convert (const gchar *str, gssize len, + const gchar *to_codeset, const gchar *from_codeset, + gsize *bytes_read, gsize *bytes_written, GError **error); + +/* + * Unicode manipulation + */ +extern const guchar g_utf8_jump_table[256]; + +gboolean g_utf8_validate (const gchar *str, gssize max_len, const gchar **end); +gunichar g_utf8_get_char_validated (const gchar *str, gssize max_len); +gchar *g_utf8_find_prev_char (const char *str, const char *p); +gchar *g_utf8_prev_char (const char *str); +#define g_utf8_next_char(p) ((p) + g_utf8_jump_table[(guchar)(*p)]) +gunichar g_utf8_get_char (const gchar *src); +glong g_utf8_strlen (const gchar *str, gssize max); +gchar *g_utf8_offset_to_pointer (const gchar *str, glong offset); +glong g_utf8_pointer_to_offset (const gchar *str, const gchar *pos); + +/* + * priorities + */ +#define G_PRIORITY_DEFAULT 0 +#define G_PRIORITY_DEFAULT_IDLE 200 + +#define GUINT16_SWAP_LE_BE_CONSTANT(x) ((((guint16) x) >> 8) | ((((guint16) x) << 8))) + +#define GUINT16_SWAP_LE_BE(x) ((guint16) (((guint16) x) >> 8) | ((((guint16)(x)) & 0xff) << 8)) +#define GUINT32_SWAP_LE_BE(x) ((guint32) \ + ( (((guint32) (x)) << 24)| \ + ((((guint32) (x)) & 0xff0000) >> 8) | \ + ((((guint32) (x)) & 0xff00) << 8) | \ + (((guint32) (x)) >> 24)) ) + +#define GUINT64_SWAP_LE_BE(x) ((guint64) (((guint64)(GUINT32_SWAP_LE_BE(((guint64)x) & 0xffffffff))) << 32) | \ + GUINT32_SWAP_LE_BE(((guint64)x) >> 32)) + + + +#if G_BYTE_ORDER == G_LITTLE_ENDIAN +# define GUINT64_FROM_BE(x) GUINT64_SWAP_LE_BE(x) +# define GUINT32_FROM_BE(x) GUINT32_SWAP_LE_BE(x) +# define GUINT16_FROM_BE(x) GUINT16_SWAP_LE_BE(x) +# define GUINT_FROM_BE(x) GUINT32_SWAP_LE_BE(x) +# define GUINT64_FROM_LE(x) (x) +# define GUINT32_FROM_LE(x) (x) +# define GUINT16_FROM_LE(x) (x) +# define GUINT_FROM_LE(x) (x) +# define GUINT64_TO_BE(x) GUINT64_SWAP_LE_BE(x) +# define GUINT32_TO_BE(x) GUINT32_SWAP_LE_BE(x) +# define GUINT16_TO_BE(x) GUINT16_SWAP_LE_BE(x) +# define GUINT_TO_BE(x) GUINT32_SWAP_LE_BE(x) +# define GUINT64_TO_LE(x) (x) +# define GUINT32_TO_LE(x) (x) +# define GUINT16_TO_LE(x) (x) +# define GUINT_TO_LE(x) (x) +#else +# define GUINT64_FROM_BE(x) (x) +# define GUINT32_FROM_BE(x) (x) +# define GUINT16_FROM_BE(x) (x) +# define GUINT_FROM_BE(x) (x) +# define GUINT64_FROM_LE(x) GUINT64_SWAP_LE_BE(x) +# define GUINT32_FROM_LE(x) GUINT32_SWAP_LE_BE(x) +# define GUINT16_FROM_LE(x) GUINT16_SWAP_LE_BE(x) +# define GUINT_FROM_LE(x) GUINT32_SWAP_LE_BE(x) +# define GUINT64_TO_BE(x) (x) +# define GUINT32_TO_BE(x) (x) +# define GUINT16_TO_BE(x) (x) +# define GUINT_TO_BE(x) (x) +# define GUINT64_TO_LE(x) GUINT64_SWAP_LE_BE(x) +# define GUINT32_TO_LE(x) GUINT32_SWAP_LE_BE(x) +# define GUINT16_TO_LE(x) GUINT16_SWAP_LE_BE(x) +# define GUINT_TO_LE(x) GUINT32_SWAP_LE_BE(x) +#endif + +#define GINT64_FROM_BE(x) (GUINT64_TO_BE (x)) +#define GINT32_FROM_BE(x) (GUINT32_TO_BE (x)) +#define GINT16_FROM_BE(x) (GUINT16_TO_BE (x)) +#define GINT64_FROM_LE(x) (GUINT64_TO_LE (x)) +#define GINT32_FROM_LE(x) (GUINT32_TO_LE (x)) +#define GINT16_FROM_LE(x) (GUINT16_TO_LE (x)) + +#define _EGLIB_MAJOR 2 +#define _EGLIB_MIDDLE 4 +#define _EGLIB_MINOR 0 + +#define GLIB_CHECK_VERSION(a,b,c) ((a < _EGLIB_MAJOR) || (a == _EGLIB_MAJOR && (b < _EGLIB_MIDDLE || (b == _EGLIB_MIDDLE && c <= _EGLIB_MINOR)))) + +#define G_HAVE_API_SUPPORT(x) (x) +#define G_UNSUPPORTED_API "%s:%d: '%s' not supported.", __FILE__, __LINE__ +#define g_unsupported_api(name) G_STMT_START { g_warning (G_UNSUPPORTED_API, name); } G_STMT_END + +G_END_DECLS + +#endif + + + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/glist.c b/unity-2019.4.24f1-mbe/mono/eglib/glist.c new file mode 100644 index 000000000..882fda48e --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/glist.c @@ -0,0 +1,345 @@ +/* + * glist.c: Doubly-linked list implementation + * + * Authors: + * Duncan Mak (duncan@novell.com) + * Raja R Harinath (rharinath@novell.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. + * + * (C) 2006 Novell, Inc. + */ +#include +#include + +GList* +g_list_alloc () +{ + return g_new0 (GList, 1); +} + +static inline GList* +new_node (GList *prev, gpointer data, GList *next) +{ + GList *node = g_list_alloc (); + node->data = data; + node->prev = prev; + node->next = next; + if (prev) + prev->next = node; + if (next) + next->prev = node; + return node; +} + +static inline GList* +disconnect_node (GList *node) +{ + if (node->next) + node->next->prev = node->prev; + if (node->prev) + node->prev->next = node->next; + return node; +} + +GList * +g_list_prepend (GList *list, gpointer data) +{ + return new_node (list ? list->prev : NULL, data, list); +} + +void +g_list_free_1 (GList *list) +{ + g_free (list); +} + +void +g_list_free (GList *list) +{ + while (list){ + GList *next = list->next; + g_list_free_1 (list); + list = next; + } +} + +GList* +g_list_append (GList *list, gpointer data) +{ + GList *node = new_node (g_list_last (list), data, NULL); + return list ? list : node; +} + +GList * +g_list_concat (GList *list1, GList *list2) +{ + if (list1 && list2) { + list2->prev = g_list_last (list1); + list2->prev->next = list2; + } + return list1 ? list1 : list2; +} + +guint +g_list_length (GList *list) +{ + guint length = 0; + + while (list) { + length ++; + list = list->next; + } + + return length; +} + +GList* +g_list_remove (GList *list, gconstpointer data) +{ + GList *current = g_list_find (list, data); + if (!current) + return list; + + if (current == list) + list = list->next; + g_list_free_1 (disconnect_node (current)); + + return list; +} + +GList* +g_list_remove_all (GList *list, gconstpointer data) +{ + GList *current = g_list_find (list, data); + + if (!current) + return list; + + while (current) { + if (current == list) + list = list->next; + g_list_free_1 (disconnect_node (current)); + + current = g_list_find (list, data); + } + + return list; +} + +GList* +g_list_remove_link (GList *list, GList *link) +{ + if (list == link) + list = list->next; + + disconnect_node (link); + link->next = NULL; + link->prev = NULL; + + return list; +} + +GList* +g_list_delete_link (GList *list, GList *link) +{ + list = g_list_remove_link (list, link); + g_list_free_1 (link); + + return list; +} + +GList* +g_list_find (GList *list, gconstpointer data) +{ + while (list){ + if (list->data == data) + return list; + + list = list->next; + } + + return NULL; +} + +GList* +g_list_find_custom (GList *list, gconstpointer data, GCompareFunc func) +{ + if (!func) + return NULL; + + while (list) { + if (func (list->data, data) == 0) + return list; + + list = list->next; + } + + return NULL; +} + +GList* +g_list_reverse (GList *list) +{ + GList *reverse = NULL; + + while (list) { + reverse = list; + list = reverse->next; + + reverse->next = reverse->prev; + reverse->prev = list; + } + + return reverse; +} + +GList* +g_list_first (GList *list) +{ + if (!list) + return NULL; + + while (list->prev) + list = list->prev; + + return list; +} + +GList* +g_list_last (GList *list) +{ + if (!list) + return NULL; + + while (list->next) + list = list->next; + + return list; +} + +GList* +g_list_insert_sorted (GList *list, gpointer data, GCompareFunc func) +{ + GList *prev = NULL; + GList *current; + GList *node; + + if (!func) + return list; + + /* Invariant: !prev || func (prev->data, data) <= 0) */ + for (current = list; current; current = current->next) { + if (func (current->data, data) > 0) + break; + prev = current; + } + + node = new_node (prev, data, current); + return list == current ? node : list; +} + +GList* +g_list_insert_before (GList *list, GList *sibling, gpointer data) +{ + if (sibling) { + GList *node = new_node (sibling->prev, data, sibling); + return list == sibling ? node : list; + } + return g_list_append (list, data); +} + +void +g_list_foreach (GList *list, GFunc func, gpointer user_data) +{ + while (list){ + (*func) (list->data, user_data); + list = list->next; + } +} + +gint +g_list_index (GList *list, gconstpointer data) +{ + gint index = 0; + + while (list){ + if (list->data == data) + return index; + + index ++; + list = list->next; + } + + return -1; +} + +GList* +g_list_nth (GList *list, guint n) +{ + for (; list; list = list->next) { + if (n == 0) + break; + n--; + } + return list; +} + +gpointer +g_list_nth_data (GList *list, guint n) +{ + GList *node = g_list_nth (list, n); + return node ? node->data : NULL; +} + +GList* +g_list_copy (GList *list) +{ + GList *copy = NULL; + + if (list) { + GList *tmp = new_node (NULL, list->data, NULL); + copy = tmp; + + for (list = list->next; list; list = list->next) + tmp = new_node (tmp, list->data, NULL); + } + + return copy; +} + +typedef GList list_node; +#include "sort.frag.h" + +GList* +g_list_sort (GList *list, GCompareFunc func) +{ + GList *current; + if (!list || !list->next) + return list; + list = do_sort (list, func); + + /* Fixup: do_sort doesn't update 'prev' pointers */ + list->prev = NULL; + for (current = list; current->next; current = current->next) + current->next->prev = current; + + return list; +} diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gmarkup.c b/unity-2019.4.24f1-mbe/mono/eglib/gmarkup.c new file mode 100644 index 000000000..4e6c6641f --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gmarkup.c @@ -0,0 +1,474 @@ +/* + * gmakrup.c: Minimal XML markup reader. + * + * Unlike the GLib one, this can not be restarted with more text + * as the Mono use does not require it. + * + * Actually, with further thought, I think that this could be made + * to restart very easily. The pos == end condition would mean + * "return to caller" and only at end parse this would be a fatal + * error. + * + * Not that it matters to Mono, but it is very simple to change, there + * is a tricky situation: there are a few places where we check p+n + * in the source, and that would have to change to be progressive, instead + * of depending on the string to be complete at that point, so we would + * have to introduce extra states to cope with that. + * + * Author: + * Miguel de Icaza (miguel@novell.com) + * + * (C) 2006 Novell, Inc. + * + * 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. + */ +#include +#include +#include + +#define set_error(msg, ...) do { if (error != NULL) *error = g_error_new (GINT_TO_POINTER (1), 1, msg, __VA_ARGS__); } while (0); + +typedef enum { + START, + START_ELEMENT, + TEXT, + FLUSH_TEXT, + CLOSING_ELEMENT, + COMMENT, + SKIP_XML_DECLARATION +} ParseState; + +struct _GMarkupParseContext { + GMarkupParser parser; + gpointer user_data; + GDestroyNotify user_data_dnotify; + ParseState state; + + /* Stores the name of the current element, so we can issue the end_element */ + GSList *level; + + GString *text; +}; + +GMarkupParseContext * +g_markup_parse_context_new (const GMarkupParser *parser, + GMarkupParseFlags flags, + gpointer user_data, + GDestroyNotify user_data_dnotify) +{ + GMarkupParseContext *context = g_new0 (GMarkupParseContext, 1); + + context->parser = *parser; + context->user_data = user_data; + context->user_data_dnotify = user_data_dnotify; + + return context; +} + +void +g_markup_parse_context_free (GMarkupParseContext *context) +{ + GSList *l; + + g_return_if_fail (context != NULL); + + if (context->user_data_dnotify != NULL) + (context->user_data_dnotify) (context->user_data); + + if (context->text != NULL) + g_string_free (context->text, TRUE); + for (l = context->level; l; l = l->next) + g_free (l->data); + g_slist_free (context->level); + g_free (context); +} + +static gboolean +my_isspace (char c) +{ + if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\v') + return TRUE; + return FALSE; +} + +static gboolean +my_isalnum (char c) +{ + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) + return TRUE; + if (c >= '0' && c <= '9') + return TRUE; + + return FALSE; +} + +static gboolean +my_isalpha (char c) +{ + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) + return TRUE; + return FALSE; +} + +static const char * +skip_space (const char *p, const char *end) +{ + for (; p < end && my_isspace (*p); p++) + ; + return p; +} + +static const char * +parse_value (const char *p, const char *end, char **value, GError **error) +{ + const char *start; + int l; + + if (*p != '"'){ + set_error ("%s", "Expected the attribute value to start with a quote"); + return end; + } + start = ++p; + for (; p < end && *p != '"'; p++) + ; + if (p == end) + return end; + l = (int)(p - start); + p++; + *value = g_malloc (l + 1); + if (*value == NULL) + return end; + strncpy (*value, start, l); + (*value) [l] = 0; + return p; +} + +static const char * +parse_name (const char *p, const char *end, char **value) +{ + const char *start = p; + int l; + + for (; p < end && my_isalnum (*p); p++) + ; + if (p == end) + return end; + + l = (int)(p - start); + *value = g_malloc (l + 1); + if (*value == NULL) + return end; + strncpy (*value, start, l); + (*value) [l] = 0; + return p; +} + +static const char * +parse_attributes (const char *p, const char *end, char ***names, char ***values, GError **error, int *full_stop, int state) +{ + int nnames = 0; + + while (TRUE){ + p = skip_space (p, end); + if (p == end) + return end; + + if (*p == '>'){ + *full_stop = 0; + return p; + } + if (state == SKIP_XML_DECLARATION && *p == '?' && ((p+1) < end) && *(p+1) == '>'){ + *full_stop = 0; + return p+1; + } + + if (*p == '/' && ((p+1) < end && *(p+1) == '>')){ + *full_stop = 1; + return p+1; + } else { + char *name, *value; + + p = parse_name (p, end, &name); + if (p == end) + return p; + + p = skip_space (p, end); + if (p == end){ + g_free (name); + return p; + } + if (*p != '='){ + set_error ("Expected an = after the attribute name `%s'", name); + g_free (name); + return end; + } + p++; + p = skip_space (p, end); + if (p == end){ + g_free (name); + return end; + } + + p = parse_value (p, end, &value, error); + if (p == end){ + g_free (name); + return p; + } + + ++nnames; + *names = g_realloc (*names, sizeof (char **) * (nnames+1)); + *values = g_realloc (*values, sizeof (char **) * (nnames+1)); + (*names) [nnames-1] = name; + (*values) [nnames-1] = value; + (*names) [nnames] = NULL; + (*values) [nnames] = NULL; + } + } +} + +static void +destroy_parse_state (GMarkupParseContext *context) +{ + GSList *p; + + for (p = context->level; p != NULL; p = p->next) + g_free (p->data); + + g_slist_free (context->level); + if (context->text != NULL) + g_string_free (context->text, TRUE); + context->text = NULL; + context->level = NULL; +} + +gboolean +g_markup_parse_context_parse (GMarkupParseContext *context, + const gchar *text, gssize text_len, + GError **error) +{ + const char *p, *end; + + g_return_val_if_fail (context != NULL, FALSE); + g_return_val_if_fail (text != NULL, FALSE); + g_return_val_if_fail (text_len >= 0, FALSE); + + end = text + text_len; + + for (p = text; p < end; p++){ + char c = *p; + + switch (context->state){ + case START: + if (c == ' ' || c == '\t' || c == '\f' || c == '\n' || (c & 0x80)) + continue; + if (c == '<'){ + if (p+1 < end && p [1] == '?'){ + context->state = SKIP_XML_DECLARATION; + p++; + } else + context->state = START_ELEMENT; + continue; + } + set_error ("%s", "Expected < to start the document"); + goto fail; + + case SKIP_XML_DECLARATION: + case START_ELEMENT: { + const char *element_start = p, *element_end; + char *ename = NULL; + int full_stop = 0, l; + gchar **names = NULL, **values = NULL; + + for (; p < end && my_isspace (*p); p++) + ; + if (p == end){ + set_error ("%s", "Unfinished element"); + goto fail; + } + + if (*p == '!' && (p+2 < end) && (p [1] == '-') && (p [2] == '-')){ + context->state = COMMENT; + p += 2; + break; + } + + if (!my_isalpha (*p)){ + set_error ("%s", "Expected an element name"); + goto fail; + } + + for (++p; p < end && (my_isalnum (*p) || (*p == '.')); p++) + ; + if (p == end){ + set_error ("%s", "Expected an element"); + goto fail; + } + element_end = p; + + for (; p < end && my_isspace (*p); p++) + ; + if (p == end){ + set_error ("%s", "Unfinished element"); + goto fail; + } + p = parse_attributes (p, end, &names, &values, error, &full_stop, context->state); + if (p == end){ + if (names != NULL) { + g_strfreev (names); + g_strfreev (values); + } + /* Only set the error if parse_attributes did not */ + if (error != NULL && *error == NULL) + set_error ("%s", "Unfinished sequence"); + goto fail; + } + l = (int)(element_end - element_start); + ename = g_malloc (l + 1); + if (ename == NULL) + goto fail; + strncpy (ename, element_start, l); + ename [l] = 0; + + if (context->state == START_ELEMENT) + if (context->parser.start_element != NULL) + context->parser.start_element (context, ename, + (const gchar **) names, + (const gchar **) values, + context->user_data, error); + + if (names != NULL){ + g_strfreev (names); + g_strfreev (values); + } + + if (error != NULL && *error != NULL){ + g_free (ename); + goto fail; + } + + if (full_stop){ + if (context->parser.end_element != NULL && context->state == START_ELEMENT){ + context->parser.end_element (context, ename, context->user_data, error); + if (error != NULL && *error != NULL){ + g_free (ename); + goto fail; + } + } + g_free (ename); + } else { + context->level = g_slist_prepend (context->level, ename); + } + + context->state = TEXT; + break; + } /* case START_ELEMENT */ + + case TEXT: { + if (c == '<'){ + context->state = FLUSH_TEXT; + break; + } + if (context->parser.text != NULL){ + if (context->text == NULL) + context->text = g_string_new (""); + g_string_append_c (context->text, c); + } + break; + } + + case COMMENT: + if (*p != '-') + break; + if (p+2 < end && (p [1] == '-') && (p [2] == '>')){ + context->state = TEXT; + p += 2; + break; + } + break; + + case FLUSH_TEXT: + if (context->parser.text != NULL && context->text != NULL){ + context->parser.text (context, context->text->str, context->text->len, + context->user_data, error); + if (error != NULL && *error != NULL) + goto fail; + } + + if (c == '/') + context->state = CLOSING_ELEMENT; + else { + p--; + context->state = START_ELEMENT; + } + break; + + case CLOSING_ELEMENT: { + GSList *current = context->level; + char *text; + + if (context->level == NULL){ + set_error ("%s", "Too many closing tags, not enough open tags"); + goto fail; + } + + text = current->data; + if (context->parser.end_element != NULL){ + context->parser.end_element (context, text, context->user_data, error); + if (error != NULL && *error != NULL){ + g_free (text); + goto fail; + } + } + g_free (text); + + while (p < end && *p != '>') + p++; + + context->level = context->level->next; + g_slist_free_1 (current); + context->state = TEXT; + break; + } /* case CLOSING_ELEMENT */ + + } /* switch */ + } + + + return TRUE; + fail: + if (context->parser.error && error != NULL && *error) + context->parser.error (context, *error, context->user_data); + + destroy_parse_state (context); + return FALSE; +} + +gboolean +g_markup_parse_context_end_parse (GMarkupParseContext *context, GError **error) +{ + g_return_val_if_fail (context != NULL, FALSE); + + /* + * In our case, we always signal errors during parse, not at the end + * see the notes at the top of this file for details on how this + * could be moved here + */ + return TRUE; +} diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gmem.c b/unity-2019.4.24f1-mbe/mono/eglib/gmem.c new file mode 100644 index 000000000..eff478dcb --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gmem.c @@ -0,0 +1,139 @@ +/* + * gmem.c: memory utility functions + * + * Author: + * Gonzalo Paniagua Javier (gonzalo@novell.com) + * + * (C) 2006 Novell, Inc. + * + * 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. + */ +#include +#include +#include +#include + +#if defined (ENABLE_OVERRIDABLE_ALLOCATORS) + +static GMemVTable sGMemVTable = { malloc, realloc, free, calloc }; + +void +g_mem_set_vtable (GMemVTable* vtable) +{ + sGMemVTable.calloc = vtable->calloc ? vtable->calloc : calloc; + sGMemVTable.realloc = vtable->realloc ? vtable->realloc : realloc; + sGMemVTable.malloc = vtable->malloc ? vtable->malloc : malloc; + sGMemVTable.free = vtable->free ? vtable->free : free; +} + +#define G_FREE_INTERNAL sGMemVTable.free +#define G_REALLOC_INTERNAL sGMemVTable.realloc +#define G_CALLOC_INTERNAL sGMemVTable.calloc +#define G_MALLOC_INTERNAL sGMemVTable.malloc +#else + +void +g_mem_set_vtable (GMemVTable* vtable) +{ +} + +#define G_FREE_INTERNAL free +#define G_REALLOC_INTERNAL realloc +#define G_CALLOC_INTERNAL calloc +#define G_MALLOC_INTERNAL malloc +#endif +void +g_free (void *ptr) +{ + if (ptr != NULL) + G_FREE_INTERNAL (ptr); +} + +gpointer +g_memdup (gconstpointer mem, guint byte_size) +{ + gpointer ptr; + + if (mem == NULL) + return NULL; + + ptr = g_malloc (byte_size); + if (ptr != NULL) + memcpy (ptr, mem, byte_size); + + return ptr; +} + +gpointer g_realloc (gpointer obj, gsize size) +{ + gpointer ptr; + if (!size) { + g_free (obj); + return 0; + } + ptr = G_REALLOC_INTERNAL (obj, size); + if (ptr) + return ptr; + g_error ("Could not allocate %i bytes", size); +} + +gpointer +g_malloc (gsize x) +{ + gpointer ptr; + if (!x) + return 0; + ptr = G_MALLOC_INTERNAL (x); + if (ptr) + return ptr; + g_error ("Could not allocate %i bytes", x); +} + +gpointer g_calloc (gsize n, gsize x) +{ + gpointer ptr; + if (!x || !n) + return 0; + ptr = G_CALLOC_INTERNAL (n, x); + if (ptr) + return ptr; + g_error ("Could not allocate %i (%i * %i) bytes", x*n, n, x); +} +gpointer g_malloc0 (gsize x) +{ + return g_calloc (1,x); +} + +gpointer g_try_malloc (gsize x) +{ + if (x) + return G_MALLOC_INTERNAL (x); + return 0; +} + + +gpointer g_try_realloc (gpointer obj, gsize size) +{ + if (!size) { + G_FREE_INTERNAL (obj); + return 0; + } + return G_REALLOC_INTERNAL (obj, size); +} diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gmisc-unity.c b/unity-2019.4.24f1-mbe/mono/eglib/gmisc-unity.c new file mode 100644 index 000000000..b1d32812e --- /dev/null +++ b/unity-2019.4.24f1-mbe/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/unity-2019.4.24f1-mbe/mono/eglib/gmisc-unix.c b/unity-2019.4.24f1-mbe/mono/eglib/gmisc-unix.c new file mode 100644 index 000000000..8fabbd2dd --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gmisc-unix.c @@ -0,0 +1,203 @@ +/* + * gmisc.c: Misc functions with no place to go (right now) + * + * Author: + * Aaron Bockover (abockover@novell.com) + * + * (C) 2006 Novell, Inc. + * + * 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. + */ + +#include +#include +#include +#include + +#ifdef HAVE_PWD_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +static pthread_mutex_t env_lock = PTHREAD_MUTEX_INITIALIZER; + +/* MONO Comment + * + * As per the UNIX spec, + * "The return value from getenv() may point to static data which may be overwritten by subsequent calls to getenv(), setenv(), or unsetenv()." + * Source: Unix Manual Pages for getenv, IEEE Std 1003.1 + * + * This means that using pointers returned from getenv may (and does) lead to many + * pointers which refer to the same piece of memory. When one is freed, all will be freed. + * + * This is unsafe and an ergonomics risk to fix in the callers. While the caller could lock, + * this introduces the risk for looping or exiting while inside of a lock. For this reason, + * g_getenv does not mimic the behavior of POSIX getenv anymore. + * + * The memory address returned will be unique to the invocaton, and must be freed. + * */ +gchar * +g_getenv (const gchar *variable) +{ + gchar *ret = NULL; + pthread_mutex_lock (&env_lock); + gchar *res = getenv(variable); + if (res) + ret = g_strdup(res); + pthread_mutex_unlock (&env_lock); + + return ret; +} + +/* + * This function checks if the given variable is non-NULL + * in the environment. It's useful because it removes memory + * freeing requirements. + * + */ +gboolean +g_hasenv (const gchar *variable) +{ + pthread_mutex_lock (&env_lock); + gchar *res = getenv(variable); + gboolean not_null = (res != NULL); + pthread_mutex_unlock (&env_lock); + + return not_null; +} + +gboolean +g_setenv(const gchar *variable, const gchar *value, gboolean overwrite) +{ + gboolean res; + pthread_mutex_lock (&env_lock); + res = (setenv(variable, value, overwrite) == 0); + pthread_mutex_unlock (&env_lock); + return res; +} + +void +g_unsetenv(const gchar *variable) +{ + pthread_mutex_lock (&env_lock); + unsetenv(variable); + pthread_mutex_unlock (&env_lock); +} + +gchar* +g_win32_getlocale(void) +{ + return NULL; +} + +gboolean +g_path_is_absolute (const char *filename) +{ + g_return_val_if_fail (filename != NULL, FALSE); + + return (*filename == '/'); +} + +static pthread_mutex_t pw_lock = PTHREAD_MUTEX_INITIALIZER; +static const gchar *home_dir; +static const gchar *user_name; + +static void +get_pw_data (void) +{ +#ifdef HAVE_GETPWUID_R + struct passwd pw; + struct passwd *result = NULL; + char buf [4096]; +#endif + + if (user_name != NULL) + return; + + pthread_mutex_lock (&pw_lock); + if (user_name != NULL) { + pthread_mutex_unlock (&pw_lock); + return; + } + + home_dir = g_getenv ("HOME"); + user_name = g_getenv ("USER"); + +#ifdef HAVE_GETPWUID_R + if (home_dir == NULL || user_name == NULL) { + if (getpwuid_r (getuid (), &pw, buf, 4096, &result) == 0 && result) { + if (home_dir == NULL) + home_dir = g_strdup (pw.pw_dir); + if (user_name == NULL) + user_name = g_strdup (pw.pw_name); + } + } +#endif + + if (user_name == NULL) + user_name = "somebody"; + if (home_dir == NULL) + home_dir = "/"; + + pthread_mutex_unlock (&pw_lock); +} + +const gchar * +g_get_home_dir (void) +{ + get_pw_data (); + return home_dir; +} + +const char * +g_get_user_name (void) +{ + get_pw_data (); + return user_name; +} + +static const char *tmp_dir; + +static pthread_mutex_t tmp_lock = PTHREAD_MUTEX_INITIALIZER; + +const gchar * +g_get_tmp_dir (void) +{ + if (tmp_dir == NULL){ + pthread_mutex_lock (&tmp_lock); + if (tmp_dir == NULL){ + tmp_dir = g_getenv ("TMPDIR"); + if (tmp_dir == NULL){ + tmp_dir = g_getenv ("TMP"); + if (tmp_dir == NULL){ + tmp_dir = g_getenv ("TEMP"); + if (tmp_dir == NULL) + tmp_dir = "/tmp"; + } + } + } + pthread_mutex_unlock (&tmp_lock); + } + return tmp_dir; +} + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gmisc-win32-uwp.c b/unity-2019.4.24f1-mbe/mono/eglib/gmisc-win32-uwp.c new file mode 100644 index 000000000..cdf5896de --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gmisc-win32-uwp.c @@ -0,0 +1,39 @@ +/* + * gmisc-win32-uwp.c: UWP misc support. + * + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. +*/ +#include +#include + +#if G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) +#include +#include + +gchar* +g_win32_getlocale(void) +{ + gunichar2 buf[19]; + gint ccBuf = GetLocaleInfoEx (LOCALE_NAME_USER_DEFAULT, LOCALE_SISO639LANGNAME, buf, 9); + assert (ccBuf <= 9); + if (ccBuf != 0) { + buf[ccBuf - 1] = L'-'; + ccBuf = GetLocaleInfoEx (LOCALE_NAME_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, buf + ccBuf, 9); + assert (ccBuf <= 9); + } + + // Check for GetLocaleInfoEx failure. + if (ccBuf == 0) + buf[0] = L'\0'; + + return u16to8 (buf); +} + +#else /* G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) */ + +#ifdef _MSC_VER +// Quiet Visual Studio linker warning, LNK4221, in cases when this source file intentional ends up empty. +void __mono_win32_gmisc_win32_uwp_quiet_lnk4221(void) {} +#endif +#endif /* G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) */ diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gmisc-win32.c b/unity-2019.4.24f1-mbe/mono/eglib/gmisc-win32.c new file mode 100644 index 000000000..70d99bfb9 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gmisc-win32.c @@ -0,0 +1,208 @@ +/* + * gmisc.c: Misc functions with no place to go (right now) + * + * Author: + * Aaron Bockover (abockover@novell.com) + * + * (C) 2006 Novell, Inc. + * + * 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. + */ + +#include + +#include +#include + +#include +#if _MSC_VER && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +#include +#endif +#include +#include +#include + +gboolean +g_hasenv (const gchar *variable) +{ + return g_getenv (variable) != NULL; +} + +gchar * +g_getenv(const gchar *variable) +{ + gunichar2 *var, *buffer; + gchar* val = NULL; + gint32 buffer_size = 1024; + gint32 retval; + var = u8to16(variable); + buffer = g_malloc(buffer_size*sizeof(gunichar2)); + retval = GetEnvironmentVariableW (var, buffer, buffer_size); + if (retval != 0) { + if (retval > buffer_size) { + g_free (buffer); + buffer_size = retval; + buffer = g_malloc(buffer_size*sizeof(gunichar2)); + retval = GetEnvironmentVariableW (var, buffer, buffer_size); + } + val = u16to8 (buffer); + } else { + if (GetLastError () != ERROR_ENVVAR_NOT_FOUND){ + val = g_malloc (1); + *val = 0; + } + } + g_free(var); + g_free(buffer); + return val; +} + +gboolean +g_setenv(const gchar *variable, const gchar *value, gboolean overwrite) +{ + gunichar2 *var, *val; + gboolean result; + var = u8to16(variable); + val = u8to16(value); + result = (SetEnvironmentVariableW(var, val) != 0) ? TRUE : FALSE; + g_free(var); + g_free(val); + return result; +} + +void +g_unsetenv(const gchar *variable) +{ + gunichar2 *var; + var = u8to16(variable); + SetEnvironmentVariableW(var, L""); + g_free(var); +} + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +gchar* +g_win32_getlocale(void) +{ + LCID lcid = GetThreadLocale(); + gchar buf[19]; + gint ccBuf = GetLocaleInfoA(lcid, LOCALE_SISO639LANGNAME, buf, 9); + buf[ccBuf - 1] = '-'; + ccBuf += GetLocaleInfoA(lcid, LOCALE_SISO3166CTRYNAME, buf + ccBuf, 9); + return g_strdup (buf); +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ + +gboolean +g_path_is_absolute (const char *filename) +{ + g_return_val_if_fail (filename != NULL, FALSE); + + if (filename[0] != '\0' && filename[1] != '\0') { + if (filename[1] == ':' && filename[2] != '\0' && + (filename[2] == '\\' || filename[2] == '/')) + return TRUE; + /* UNC paths */ + else if (filename[0] == '\\' && filename[1] == '\\' && + filename[2] != '\0') + return TRUE; + } + + return FALSE; +} + +#if _MSC_VER && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +static gchar* +g_get_known_folder_path (void) +{ + gchar *folder_path = NULL; + PWSTR profile_path = NULL; + HRESULT hr = SHGetKnownFolderPath (&FOLDERID_Profile, KF_FLAG_DEFAULT, NULL, &profile_path); + if (SUCCEEDED(hr)) { + folder_path = u16to8 (profile_path); + CoTaskMemFree (profile_path); + } + + return folder_path; +} + +#else + +static inline gchar * +g_get_known_folder_path (void) +{ + return NULL; +} +#endif + +const gchar * +g_get_home_dir (void) +{ + gchar *home_dir = g_get_known_folder_path (); + + if (!home_dir) { + home_dir = (gchar *) g_getenv ("USERPROFILE"); + } + + if (!home_dir) { + const gchar *drive = g_getenv ("HOMEDRIVE"); + const gchar *path = g_getenv ("HOMEPATH"); + + if (drive && path) { + home_dir = g_malloc (strlen (drive) + strlen (path) + 1); + if (home_dir) { + sprintf (home_dir, "%s%s", drive, path); + } + } + g_free (drive); + g_free (path); + } + + return home_dir; +} + +const char * +g_get_user_name (void) +{ + const char * retName = g_getenv ("USER"); + if (!retName) + retName = g_getenv ("USERNAME"); + return retName; +} + +static const char *tmp_dir; + +const gchar * +g_get_tmp_dir (void) +{ + if (tmp_dir == NULL){ + if (tmp_dir == NULL){ + tmp_dir = g_getenv ("TMPDIR"); + if (tmp_dir == NULL){ + tmp_dir = g_getenv ("TMP"); + if (tmp_dir == NULL){ + tmp_dir = g_getenv ("TEMP"); + if (tmp_dir == NULL) + tmp_dir = "C:\\temp"; + } + } + } + } + return tmp_dir; +} diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gmodule-unix.c b/unity-2019.4.24f1-mbe/mono/eglib/gmodule-unix.c new file mode 100644 index 000000000..77f8b8dd5 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gmodule-unix.c @@ -0,0 +1,288 @@ +/* + * gmodule.c: dl* functions, glib style + * + * Author: + * Gonzalo Paniagua Javier (gonzalo@novell.com) + * Jonathan Chambers (joncham@gmail.com) + * Robert Jordan (robertj@gmx.net) + * + * (C) 2006 Novell, Inc. + * (C) 2006 Jonathan Chambers + * + * 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. + */ +#include + +#include +#include + +#if defined(G_OS_UNIX) && defined(HAVE_DLFCN_H) +#include + +/* For Linux and Solaris, need to add others as we port this */ +#define LIBPREFIX "lib" +#define LIBSUFFIX ".so" + +struct _GModule { + void *handle; +}; + +GModule * +g_module_open (const gchar *file, GModuleFlags flags) +{ + int f = 0; + GModule *module; + void *handle; + + flags &= G_MODULE_BIND_MASK; + if ((flags & G_MODULE_BIND_LAZY) != 0) + f |= RTLD_LAZY; + if ((flags & G_MODULE_BIND_LOCAL) != 0) + f |= RTLD_LOCAL; + + handle = dlopen (file, f); + if (handle == NULL) + return NULL; + + module = g_new (GModule,1); + module->handle = handle; + + return module; +} + +gboolean +g_module_symbol (GModule *module, const gchar *symbol_name, gpointer *symbol) +{ + if (symbol_name == NULL || symbol == NULL) + return FALSE; + + if (module == NULL || module->handle == NULL) + return FALSE; + + *symbol = dlsym (module->handle, symbol_name); + return (*symbol != NULL); +} + +const gchar * +g_module_error (void) +{ + return dlerror (); +} + +gboolean +g_module_close (GModule *module) +{ + void *handle; + if (module == NULL || module->handle == NULL) + return FALSE; + + handle = module->handle; + module->handle = NULL; + g_free (module); + return (0 == dlclose (handle)); +} + +#elif defined (G_OS_WIN32) +#include +#include + +#define LIBSUFFIX ".dll" +#define LIBPREFIX "" + +struct _GModule { + HMODULE handle; + int main_module; +}; + +GModule * +g_module_open (const gchar *file, GModuleFlags flags) +{ + GModule *module; + module = g_malloc (sizeof (GModule)); + if (module == NULL) + return NULL; + + if (file != NULL) { + gunichar2 *file16; + file16 = u8to16(file); + module->main_module = FALSE; + module->handle = LoadLibrary (file16); + g_free(file16); + if (!module->handle) { + g_free (module); + return NULL; + } + + } else { + module->main_module = TRUE; + module->handle = GetModuleHandle (NULL); + } + + return module; +} + +static gpointer +w32_find_symbol (const gchar *symbol_name) +{ + HMODULE *modules; + DWORD buffer_size = sizeof (HMODULE) * 1024; + DWORD needed, i; + + modules = (HMODULE *) g_malloc (buffer_size); + + if (modules == NULL) + return NULL; + + if (!EnumProcessModules (GetCurrentProcess (), modules, + buffer_size, &needed)) { + g_free (modules); + return NULL; + } + + /* check whether the supplied buffer was too small, realloc, retry */ + if (needed > buffer_size) { + g_free (modules); + + buffer_size = needed; + modules = (HMODULE *) g_malloc (buffer_size); + + if (modules == NULL) + return NULL; + + if (!EnumProcessModules (GetCurrentProcess (), modules, + buffer_size, &needed)) { + g_free (modules); + return NULL; + } + } + + for (i = 0; i < needed / sizeof (HANDLE); i++) { + gpointer proc = (gpointer)(intptr_t)GetProcAddress (modules [i], symbol_name); + if (proc != NULL) { + g_free (modules); + return proc; + } + } + + g_free (modules); + return NULL; +} + +gboolean +g_module_symbol (GModule *module, const gchar *symbol_name, gpointer *symbol) +{ + if (module == NULL || symbol_name == NULL || symbol == NULL) + return FALSE; + + if (module->main_module) { + *symbol = (gpointer)(intptr_t)GetProcAddress (module->handle, symbol_name); + if (*symbol != NULL) + return TRUE; + + *symbol = w32_find_symbol (symbol_name); + return *symbol != NULL; + } else { + *symbol = (gpointer)(intptr_t)GetProcAddress (module->handle, symbol_name); + return *symbol != NULL; + } +} + +const gchar * +g_module_error (void) +{ + gchar* ret = NULL; + TCHAR* buf = NULL; + DWORD code = GetLastError (); + + FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, + code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 0, NULL); + + ret = u16to8 (buf); + LocalFree(buf); + + return ret; +} + +gboolean +g_module_close (GModule *module) +{ + HMODULE handle; + int main_module; + + if (module == NULL || module->handle == NULL) + return FALSE; + + handle = module->handle; + main_module = module->main_module; + module->handle = NULL; + g_free (module); + return (main_module ? 1 : (0 == FreeLibrary (handle))); +} + +#else + +#define LIBSUFFIX "" +#define LIBPREFIX "" + +GModule * +g_module_open (const gchar *file, GModuleFlags flags) +{ + g_error ("%s", "g_module_open not implemented on this platform"); + return NULL; +} + +gboolean +g_module_symbol (GModule *module, const gchar *symbol_name, gpointer *symbol) +{ + g_error ("%s", "g_module_open not implemented on this platform"); + return FALSE; +} + +const gchar * +g_module_error (void) +{ + g_error ("%s", "g_module_open not implemented on this platform"); + return NULL; +} + +gboolean +g_module_close (GModule *module) +{ + g_error ("%s", "g_module_open not implemented on this platform"); + return FALSE; +} +#endif + +gchar * +g_module_build_path (const gchar *directory, const gchar *module_name) +{ + const char *lib_prefix = ""; + + if (module_name == NULL) + return NULL; + + if (strncmp (module_name, "lib", 3) != 0) + lib_prefix = LIBPREFIX; + + if (directory && *directory) + return g_strdup_printf ("%s/%s%s" LIBSUFFIX, directory, lib_prefix, module_name); + return g_strdup_printf ("%s%s" LIBSUFFIX, lib_prefix, module_name); +} + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gmodule-win32-internals.h b/unity-2019.4.24f1-mbe/mono/eglib/gmodule-win32-internals.h new file mode 100644 index 000000000..d18e27f48 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gmodule-win32-internals.h @@ -0,0 +1,13 @@ +#ifndef __G_MODULE_WINDOWS_INTERNALS_H__ +#define __G_MODULE_WINDOWS_INTERNALS_H__ + +#include +#include + +#ifdef G_OS_WIN32 +#include + +gpointer +w32_find_symbol (const gchar *symbol_name); +#endif /* G_OS_WIN32 */ +#endif /* __G_MODULE_WINDOWS_INTERNALS_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gmodule-win32-uwp.c b/unity-2019.4.24f1-mbe/mono/eglib/gmodule-win32-uwp.c new file mode 100644 index 000000000..5117e3b72 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gmodule-win32-uwp.c @@ -0,0 +1,43 @@ +/* + * gmodule-win32-uwp.c: UWP gmodule support. + * + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. +*/ +#include +#include + +#if G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) +#include +#include + +gpointer +w32_find_symbol (const gchar *symbol_name) +{ + g_unsupported_api ("EnumProcessModules"); + SetLastError (ERROR_NOT_SUPPORTED); + return NULL; +} + +const gchar * +g_module_error (void) +{ + gchar *ret = NULL; + TCHAR buf [1024]; + DWORD code = GetLastError (); + + if (!FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, + code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, G_N_ELEMENTS (buf) - 1, NULL) ) + buf[0] = TEXT('\0'); + + ret = u16to8 (buf); + return ret; +} + +#else /* G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) */ + +#ifdef _MSC_VER +// Quiet Visual Studio linker warning, LNK4221, in cases when this source file intentional ends up empty. +void __mono_win32_gmodule_win32_uwp_quiet_lnk4221(void) {} +#endif +#endif /* G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) */ diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gmodule-win32.c b/unity-2019.4.24f1-mbe/mono/eglib/gmodule-win32.c new file mode 100644 index 000000000..72eaca32f --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gmodule-win32.c @@ -0,0 +1,191 @@ +/* + * gmodule.c: dl* functions, glib style + * + * Author: + * Gonzalo Paniagua Javier (gonzalo@novell.com) + * Jonathan Chambers (joncham@gmail.com) + * Robert Jordan (robertj@gmx.net) + * + * (C) 2006 Novell, Inc. + * (C) 2006 Jonathan Chambers + * + * 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. + */ +#include +#include +#include +#include +#include + +#define LIBSUFFIX ".dll" +#define LIBPREFIX "" + +struct _GModule { + HMODULE handle; + int main_module; +}; + +GModule * +g_module_open (const gchar *file, GModuleFlags flags) +{ + GModule *module; + module = g_malloc (sizeof (GModule)); + if (module == NULL) + return NULL; + + if (file != NULL) { + gunichar2 *file16; + file16 = u8to16(file); + module->main_module = FALSE; + module->handle = LoadLibraryW (file16); + g_free(file16); + if (!module->handle) { + g_free (module); + return NULL; + } + + } else { + module->main_module = TRUE; + module->handle = GetModuleHandle (NULL); + } + + return module; +} + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +gpointer +w32_find_symbol (const gchar *symbol_name) +{ + HMODULE *modules; + DWORD buffer_size = sizeof (HMODULE) * 1024; + DWORD needed, i; + + modules = (HMODULE *) g_malloc (buffer_size); + + if (modules == NULL) + return NULL; + + if (!EnumProcessModules (GetCurrentProcess (), modules, + buffer_size, &needed)) { + g_free (modules); + return NULL; + } + + /* check whether the supplied buffer was too small, realloc, retry */ + if (needed > buffer_size) { + g_free (modules); + + buffer_size = needed; + modules = (HMODULE *) g_malloc (buffer_size); + + if (modules == NULL) + return NULL; + + if (!EnumProcessModules (GetCurrentProcess (), modules, + buffer_size, &needed)) { + g_free (modules); + return NULL; + } + } + + for (i = 0; i < needed / sizeof (HANDLE); i++) { + gpointer proc = (gpointer)(intptr_t)GetProcAddress (modules [i], symbol_name); + if (proc != NULL) { + g_free (modules); + return proc; + } + } + + g_free (modules); + return NULL; +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ + +gboolean +g_module_symbol (GModule *module, const gchar *symbol_name, gpointer *symbol) +{ + if (module == NULL || symbol_name == NULL || symbol == NULL) + return FALSE; + + if (module->main_module) { + *symbol = (gpointer)(intptr_t)GetProcAddress (module->handle, symbol_name); + if (*symbol != NULL) + return TRUE; + + *symbol = w32_find_symbol (symbol_name); + return *symbol != NULL; + } else { + *symbol = (gpointer)(intptr_t)GetProcAddress (module->handle, symbol_name); + return *symbol != NULL; + } +} + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +const gchar * +g_module_error (void) +{ + gchar* ret = NULL; + TCHAR* buf = NULL; + DWORD code = GetLastError (); + + /* FIXME: buf must not be NULL! */ + FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, + code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 0, NULL); + + ret = u16to8 (buf); + LocalFree(buf); + + return ret; +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ + +gboolean +g_module_close (GModule *module) +{ + HMODULE handle; + int main_module; + + if (module == NULL || module->handle == NULL) + return FALSE; + + handle = module->handle; + main_module = module->main_module; + module->handle = NULL; + g_free (module); + return (main_module ? 1 : (0 == FreeLibrary (handle))); +} + +gchar * +g_module_build_path (const gchar *directory, const gchar *module_name) +{ + char *lib_prefix = ""; + + if (module_name == NULL) + return NULL; + + if (strncmp (module_name, "lib", 3) != 0) + lib_prefix = LIBPREFIX; + + if (directory && *directory){ + + return g_strdup_printf ("%s/%s%s" LIBSUFFIX, directory, lib_prefix, module_name); + } + return g_strdup_printf ("%s%s" LIBSUFFIX, lib_prefix, module_name); +} diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gmodule.h b/unity-2019.4.24f1-mbe/mono/eglib/gmodule.h new file mode 100644 index 000000000..ee89683f5 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gmodule.h @@ -0,0 +1,37 @@ +#ifndef __GLIB_GMODULE_H +#define __GLIB_GMODULE_H + +#include + +#define G_MODULE_IMPORT extern +#ifdef G_OS_WIN32 +#define G_MODULE_EXPORT __declspec(dllexport) +#else +#define G_MODULE_EXPORT +#endif + +G_BEGIN_DECLS + +/* + * Modules + */ +typedef enum { + G_MODULE_BIND_LAZY = 0x01, + G_MODULE_BIND_LOCAL = 0x02, + G_MODULE_BIND_MASK = 0x03 +} GModuleFlags; +typedef struct _GModule GModule; + +GModule *g_module_open (const gchar *file, GModuleFlags flags); +gboolean g_module_symbol (GModule *module, const gchar *symbol_name, + gpointer *symbol); +const gchar *g_module_error (void); +gboolean g_module_close (GModule *module); +gchar * g_module_build_path (const gchar *directory, const gchar *module_name); + +extern char *gmodule_libprefix; +extern char *gmodule_libsuffix; + +G_END_DECLS + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/eglib/goutput.c b/unity-2019.4.24f1-mbe/mono/eglib/goutput.c new file mode 100644 index 000000000..1e2933625 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/goutput.c @@ -0,0 +1,306 @@ +/* + * Output and debugging functions + * + * Author: + * Miguel de Icaza (miguel@novell.com) + * + * (C) 2006 Novell, Inc. + * Copyright 2011 Xamarin Inc. + * + * 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. + */ +#include +#include +#include +#include + +/* 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); + +void +g_print (const gchar *format, ...) +{ + char *msg; + va_list args; + + va_start (args, format); + if (g_vasprintf (&msg, format, args) < 0) { + va_end (args); + return; + } + va_end (args); + + if (!stdout_handler) + stdout_handler = default_stdout_handler; + + stdout_handler (msg); + g_free (msg); +} + +void +g_printerr (const gchar *format, ...) +{ + char *msg; + va_list args; + + va_start (args, format); + if (g_vasprintf (&msg, format, args) < 0) { + va_end (args); + return; + } + va_end (args); + + if (!stderr_handler) + stderr_handler = default_stderr_handler; + + stderr_handler (msg); + g_free (msg); +} + +GLogLevelFlags +g_log_set_always_fatal (GLogLevelFlags fatal_mask) +{ + GLogLevelFlags old_fatal = fatal; + + fatal |= fatal_mask; + + return old_fatal; +} + +GLogLevelFlags +g_log_set_fatal_mask (const gchar *log_domain, GLogLevelFlags fatal_mask) +{ + /* + * Mono does not use a G_LOG_DOMAIN currently, so we just assume things are fatal + * if we decide to set G_LOG_DOMAIN (we probably should) we should implement + * this. + */ + return fatal_mask; +} + +void +g_logv (const gchar *log_domain, GLogLevelFlags log_level, const gchar *format, va_list args) +{ + char *msg; + + if (!default_log_func) + default_log_func = g_log_default_handler; + + if (g_vasprintf (&msg, format, args) < 0) + return; + + default_log_func (log_domain, log_level, msg, default_log_func_user_data); + g_free (msg); +} + +void +g_log (const gchar *log_domain, GLogLevelFlags log_level, const gchar *format, ...) +{ + va_list args; + + va_start (args, format); + g_logv (log_domain, log_level, format, args); + va_end (args); +} + +void +g_assertion_message (const gchar *format, ...) +{ + va_list args; + + va_start (args, format); + g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, format, args); + va_end (args); + exit (0); +} + +#if HOST_ANDROID +#include + +static android_LogPriority +to_android_priority (GLogLevelFlags log_level) +{ + switch (log_level & G_LOG_LEVEL_MASK) + { + case G_LOG_LEVEL_ERROR: return ANDROID_LOG_FATAL; + case G_LOG_LEVEL_CRITICAL: return ANDROID_LOG_ERROR; + case G_LOG_LEVEL_WARNING: return ANDROID_LOG_WARN; + case G_LOG_LEVEL_MESSAGE: return ANDROID_LOG_INFO; + case G_LOG_LEVEL_INFO: return ANDROID_LOG_DEBUG; + case G_LOG_LEVEL_DEBUG: return ANDROID_LOG_VERBOSE; + } + return ANDROID_LOG_UNKNOWN; +} + +void +g_log_default_handler (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer unused_data) +{ + __android_log_write (to_android_priority (log_level), log_domain, message); + if (log_level & fatal) + abort (); +} + +static void +default_stdout_handler (const gchar *message) +{ + /* TODO: provide a proper app name */ + __android_log_write (ANDROID_LOG_ERROR, "mono", message); +} + +static void +default_stderr_handler (const gchar *message) +{ + /* TODO: provide a proper app name */ + __android_log_write (ANDROID_LOG_ERROR, "mono", message); +} + + +#elif defined(HOST_IOS) +#include + +static int +to_asl_priority (GLogLevelFlags log_level) +{ + switch (log_level & G_LOG_LEVEL_MASK) + { + case G_LOG_LEVEL_ERROR: return ASL_LEVEL_CRIT; + case G_LOG_LEVEL_CRITICAL: return ASL_LEVEL_ERR; + case G_LOG_LEVEL_WARNING: return ASL_LEVEL_WARNING; + case G_LOG_LEVEL_MESSAGE: return ASL_LEVEL_NOTICE; + case G_LOG_LEVEL_INFO: return ASL_LEVEL_INFO; + case G_LOG_LEVEL_DEBUG: return ASL_LEVEL_DEBUG; + } + return ASL_LEVEL_ERR; +} + +void +g_log_default_handler (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer unused_data) +{ + asl_log (NULL, NULL, to_asl_priority (log_level), "%s", message); + if (log_level & fatal) + abort (); +} + +static void +default_stdout_handler (const gchar *message) +{ + asl_log (NULL, NULL, ASL_LEVEL_WARNING, "%s", message); +} + +static void +default_stderr_handler (const gchar *message) +{ + asl_log (NULL, NULL, ASL_LEVEL_WARNING, "%s", message); +} + +#else + +void +g_log_default_handler (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer unused_data) +{ + FILE *target = stdout; + + fprintf (target, "%s%s%s\n", + log_domain != NULL ? log_domain : "", + log_domain != NULL ? ": " : "", + message); + + if (log_level & fatal) { + fflush (stdout); + fflush (stderr); + abort (); + } +} + +static void +default_stdout_handler (const gchar *string) +{ + fprintf (stdout, "%s", string); +} + +static void +default_stderr_handler (const gchar *string) +{ + fprintf (stderr, "%s", string); +} + +#endif + +GLogFunc +g_log_set_default_handler (GLogFunc log_func, gpointer user_data) +{ + GLogFunc old = default_log_func; + default_log_func = log_func; + default_log_func_user_data = user_data; + return old; +} + +GPrintFunc +g_set_print_handler (GPrintFunc func) +{ + GPrintFunc old = stdout_handler; + stdout_handler = func; + return old; +} + +GPrintFunc +g_set_printerr_handler (GPrintFunc func) +{ + GPrintFunc old = stderr_handler; + stderr_handler = 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/unity-2019.4.24f1-mbe/mono/eglib/gpath.c b/unity-2019.4.24f1-mbe/mono/eglib/gpath.c new file mode 100644 index 000000000..01113ee0c --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gpath.c @@ -0,0 +1,398 @@ +/* + * Portable Utility Functions + * + * Author: + * Miguel de Icaza (miguel@novell.com) + * + * (C) 2006 Novell, Inc. + * + * 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. + */ +#include +#include +#include +#include +#include + +#ifdef G_OS_WIN32 +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +gchar * +g_build_path (const gchar *separator, const gchar *first_element, ...) +{ + const char *elem, *next, *endptr; + gboolean trimmed; + GString *path; + va_list args; + size_t slen; + + g_return_val_if_fail (separator != NULL, NULL); + + path = g_string_sized_new (48); + slen = strlen (separator); + + va_start (args, first_element); + for (elem = first_element; elem != NULL; elem = next) { + /* trim any trailing separators from @elem */ + endptr = elem + strlen (elem); + trimmed = FALSE; + + while (endptr >= elem + slen) { + if (strncmp (endptr - slen, separator, slen) != 0) + break; + + endptr -= slen; + trimmed = TRUE; + } + + /* append elem, not including any trailing separators */ + if (endptr > elem) + g_string_append_len (path, elem, endptr - elem); + + /* get the next element */ + do { + if (!(next = va_arg (args, char *))) + break; + + /* remove leading separators */ + while (!strncmp (next, separator, slen)) + next += slen; + } while (*next == '\0'); + + if (next || trimmed) + g_string_append_len (path, separator, slen); + } + va_end (args); + + return g_string_free (path, FALSE); +} + +static gchar* +strrchr_seperator (const gchar* filename) +{ +#ifdef G_OS_WIN32 + char *p2; +#endif + char *p; + + p = strrchr (filename, G_DIR_SEPARATOR); +#ifdef G_OS_WIN32 + p2 = strrchr (filename, '/'); + if (p2 > p) + p = p2; +#endif + + return p; +} + +gchar * +g_path_get_dirname (const gchar *filename) +{ + char *p, *r; + size_t count; + g_return_val_if_fail (filename != NULL, NULL); + + p = strrchr_seperator (filename); + if (p == NULL) + return g_strdup ("."); + if (p == filename) + return g_strdup ("/"); + count = p - filename; + r = g_malloc (count + 1); + strncpy (r, filename, count); + r [count] = 0; + + return r; +} + +gchar * +g_path_get_basename (const char *filename) +{ + char *r; + g_return_val_if_fail (filename != NULL, NULL); + + /* Empty filename -> . */ + if (!*filename) + return g_strdup ("."); + + /* No separator -> filename */ + r = strrchr_seperator (filename); + if (r == NULL) + return g_strdup (filename); + + /* Trailing slash, remove component */ + if (r [1] == 0){ + char *copy = g_strdup (filename); + copy [r-filename] = 0; + r = strrchr_seperator (copy); + + if (r == NULL){ + g_free (copy); + return g_strdup ("/"); + } + r = g_strdup (&r[1]); + g_free (copy); + return r; + } + + return g_strdup (&r[1]); +} + +//wasm does have strtok_r even though autoconf fails to find +#if !defined (HAVE_STRTOK_R) && !defined (HOST_WASM) +// This is from BSD's strtok_r + +char * +strtok_r(char *s, const char *delim, char **last) +{ + char *spanp; + int c, sc; + char *tok; + + if (s == NULL && (s = *last) == NULL) + return NULL; + + /* + * Skip (span) leading delimiters (s += strspn(s, delim), sort of). + */ +cont: + c = *s++; + for (spanp = (char *)delim; (sc = *spanp++) != 0; ){ + if (c == sc) + goto cont; + } + + if (c == 0){ /* no non-delimiter characters */ + *last = NULL; + return NULL; + } + tok = s - 1; + + /* + * Scan token (scan for delimiters: s += strcspn(s, delim), sort of). + * Note that delim must have one NUL; we stop if we see that, too. + */ + for (;;){ + c = *s++; + spanp = (char *)delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else { + char *w = s - 1; + *w = '\0'; + } + *last = s; + return tok; + } + } + while (sc != 0); + } + /* NOTREACHED */ +} +#endif + +gchar * +g_find_program_in_path (const gchar *program) +{ + char *p; + char *x, *l; + gchar *curdir = NULL; + char *save = NULL; +#ifdef G_OS_WIN32 + char *program_exe; + char *suffix_list[5] = {".exe",".cmd",".bat",".com",NULL}; + int listx; + gboolean hasSuffix; +#endif + + g_return_val_if_fail (program != NULL, NULL); + x = p = g_strdup (g_getenv ("PATH")); + + if (x == NULL || *x == '\0') { + curdir = g_get_current_dir (); + x = curdir; + } + +#ifdef G_OS_WIN32 + /* see if program already has a suffix */ + listx = 0; + hasSuffix = FALSE; + while (!hasSuffix && suffix_list[listx]) { + hasSuffix = g_str_has_suffix(program,suffix_list[listx++]); + } +#endif + + while ((l = strtok_r (x, G_SEARCHPATH_SEPARATOR_S, &save)) != NULL){ + char *probe_path; + + x = NULL; + probe_path = g_build_path (G_DIR_SEPARATOR_S, l, program, NULL); +#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 + /* check for program with a suffix attached */ + if (!hasSuffix) { + listx = 0; + 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 !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); + } + } +#endif + } + g_free (curdir); + g_free (p); + return NULL; +} + +static char *name; + +void +g_set_prgname (const gchar *prgname) +{ + name = g_strdup (prgname); +} + +gchar * +g_get_prgname (void) +{ + return name; +} + +gboolean +g_ensure_directory_exists (const gchar *filename) +{ +#ifdef G_OS_WIN32 + gchar *dir_utf8 = g_path_get_dirname (filename); + gunichar2 *p; + gunichar2 *dir_utf16 = NULL; + int retval; + + if (!dir_utf8 || !dir_utf8 [0]) + return FALSE; + + dir_utf16 = g_utf8_to_utf16 (dir_utf8, strlen (dir_utf8), NULL, NULL, NULL); + g_free (dir_utf8); + + if (!dir_utf16) + return FALSE; + + p = dir_utf16; + + /* make life easy and only use one directory seperator */ + while (*p != '\0') + { + if (*p == '/') + *p = '\\'; + p++; + } + + p = dir_utf16; + + /* get past C:\ )*/ + while (*p++ != '\\') + { + } + + while (1) { + gboolean bRet = FALSE; + p = wcschr (p, '\\'); + if (p) + *p = '\0'; + retval = _wmkdir (dir_utf16); + if (retval != 0 && errno != EEXIST) { + g_free (dir_utf16); + return FALSE; + } + if (!p) + break; + *p++ = '\\'; + } + + g_free (dir_utf16); + return TRUE; +#else + char *p; + gchar *dir = g_path_get_dirname (filename); + int retval; + struct stat sbuf; + + if (!dir || !dir [0]) { + g_free (dir); + return FALSE; + } + + if (stat (dir, &sbuf) == 0 && S_ISDIR (sbuf.st_mode)) { + g_free (dir); + return TRUE; + } + + p = dir; + while (*p == '/') + p++; + + while (1) { + p = strchr (p, '/'); + if (p) + *p = '\0'; + retval = mkdir (dir, 0777); + if (retval != 0 && errno != EEXIST) { + g_free (dir); + return FALSE; + } + if (!p) + break; + *p++ = '/'; + } + + g_free (dir); + return TRUE; +#endif +} + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gpattern.c b/unity-2019.4.24f1-mbe/mono/eglib/gpattern.c new file mode 100644 index 000000000..84861412e --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gpattern.c @@ -0,0 +1,213 @@ +/* + * Simple pattern matching + * + * Author: + * Gonzalo Paniagua Javier (gonzalo@novell.com + * + * (C) 2006 Novell, Inc. + * + * 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. + */ +#include +#include +#include +#include +#ifndef _MSC_VER +#include +#endif + +typedef enum { + MATCH_LITERAL, + MATCH_ANYCHAR, + MATCH_ANYTHING, + MATCH_ANYTHING_END, + MATCH_INVALID = -1 +} MatchType; + +typedef struct { + MatchType type; + gchar *str; +} PData; + +struct _GPatternSpec { + GSList *pattern; +}; + +static GSList * +compile_pattern (const gchar *pattern) +{ + GSList *list; + size_t i, len; + PData *data; + gchar c; + MatchType last = MATCH_INVALID; + GString *str; + gboolean free_str; + + if (pattern == NULL) + return NULL; + + data = NULL; + list = NULL; + free_str = TRUE; + str = g_string_new (""); + for (i = 0, len = strlen (pattern); i < len; i++) { + c = pattern [i]; + if (c == '*' || c == '?') { + if (str->len > 0) { + data = g_new0 (PData, 1); + data->type = MATCH_LITERAL; + data->str = g_string_free (str, FALSE); + list = g_slist_append (list, data); + str = g_string_new (""); + } + + if (last == MATCH_ANYTHING && c == '*') + continue; + + data = g_new0 (PData, 1); + data->type = (c == '*') ? MATCH_ANYTHING : MATCH_ANYCHAR; + list = g_slist_append (list, data); + last = data->type; + } else { + g_string_append_c (str, c); + last = MATCH_LITERAL; + } + } + + if (last == MATCH_ANYTHING && str->len == 0) { + data->type = MATCH_ANYTHING_END; + free_str = TRUE; + } else if (str->len > 0) { + data = g_new0 (PData, 1); + data->type = MATCH_LITERAL; + data->str = str->str; + free_str = FALSE; + list = g_slist_append (list, data); + } + g_string_free (str, free_str); + return list; +} + +#ifdef DEBUG_PATTERN +static void +print_pattern (gpointer data, gpointer user_data) +{ + PData *d = (PData *) data; + + printf ("Type: %s", d->type == MATCH_LITERAL ? "literal" : d->type == MATCH_ANYCHAR ? "any char" : "anything"); + if (d->type == MATCH_LITERAL) + printf (" String: %s", d->str); + printf ("\n"); +} +#endif + +GPatternSpec * +g_pattern_spec_new (const gchar *pattern) +{ + GPatternSpec *spec; + + g_return_val_if_fail (pattern != NULL, NULL); + spec = g_new0 (GPatternSpec, 1); + if (pattern) { + spec->pattern = compile_pattern (pattern); +#ifdef DEBUG_PATTERN + g_slist_foreach (spec->pattern, print_pattern, NULL); + printf ("\n"); +#endif + } + return spec; +} + +static void +free_pdata (gpointer data, gpointer user_data) +{ + PData *d = (PData *) data; + + if (d->str) + g_free (d->str); + g_free (d); +} + +void +g_pattern_spec_free (GPatternSpec *pspec) +{ + if (pspec) { + g_slist_foreach (pspec->pattern, free_pdata, NULL); + g_slist_free (pspec->pattern); + pspec->pattern = NULL; + } + g_free (pspec); +} + +static gboolean +match_string (GSList *list, const gchar *str, size_t idx, size_t max) +{ + size_t len; + + while (list && idx < max) { + PData *data = (PData *) list->data; + + if (data->type == MATCH_ANYTHING_END) + return TRUE; + + if (data->type == MATCH_LITERAL) { + len = strlen (data->str); + if (strncmp (&str [idx], data->str, len) != 0) + return FALSE; + idx += len; + list = list->next; + if (list) { + /* + * When recursing, we need this to avoid returning FALSE + * because 'list' will not be NULL + */ + data = (PData *) list->data; + if (data->type == MATCH_ANYTHING_END) + return TRUE; + } + } else if (data->type == MATCH_ANYCHAR) { + idx++; + list = list->next; + } else if (data->type == MATCH_ANYTHING) { + while (idx < max) { + if (match_string (list->next, str, idx++, max)) + return TRUE; + } + return FALSE; + } else { + g_assert_not_reached (); + } + } + + return (list == NULL && idx >= max); +} +gboolean +g_pattern_match_string (GPatternSpec *pspec, const gchar *string) +{ + g_return_val_if_fail (pspec != NULL, FALSE); + g_return_val_if_fail (string != NULL, FALSE); + + if (pspec->pattern == NULL) + return FALSE; + return match_string (pspec->pattern, string, 0, strlen (string)); +} + + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gptrarray.c b/unity-2019.4.24f1-mbe/mono/eglib/gptrarray.c new file mode 100644 index 000000000..f77b5f0fc --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gptrarray.c @@ -0,0 +1,232 @@ +/* + * Pointer Array + * + * Author: + * Aaron Bockover (abockover@novell.com) + * Gonzalo Paniagua Javier (gonzalo@novell.com) + * Jeffrey Stedfast (fejj@novell.com) + * + * (C) 2006,2011 Novell, Inc. + * + * 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. + */ + +#include +#include + +typedef struct _GPtrArrayPriv { + gpointer *pdata; + guint len; + guint size; +} GPtrArrayPriv; + +static void +g_ptr_array_grow(GPtrArrayPriv *array, guint length) +{ + guint new_length = array->len + length; + + g_return_if_fail(array != NULL); + + if(new_length <= array->size) { + return; + } + + array->size = 1; + + while(array->size < new_length) { + array->size <<= 1; + } + + array->size = MAX(array->size, 16); + array->pdata = g_realloc(array->pdata, array->size * sizeof(gpointer)); +} + +GPtrArray * +g_ptr_array_new(void) +{ + return g_ptr_array_sized_new(0); +} + +GPtrArray * +g_ptr_array_sized_new(guint reserved_size) +{ + GPtrArrayPriv *array = g_new0(GPtrArrayPriv, 1); + + array->pdata = NULL; + array->len = 0; + array->size = 0; + + if(reserved_size > 0) { + g_ptr_array_grow(array, reserved_size); + } + + return (GPtrArray *)array; +} + +gpointer * +g_ptr_array_free(GPtrArray *array, gboolean free_seg) +{ + gpointer *data = NULL; + + g_return_val_if_fail(array != NULL, NULL); + + if(free_seg) { + g_free(array->pdata); + } else { + data = array->pdata; + } + + g_free(array); + + return data; +} + +void +g_ptr_array_set_size(GPtrArray *array, gint length) +{ + g_return_if_fail(array != NULL); + + if((size_t)length > array->len) { + g_ptr_array_grow((GPtrArrayPriv *)array, length); + memset(array->pdata + array->len, 0, (length - array->len) + * sizeof(gpointer)); + } + + array->len = length; +} + +void +g_ptr_array_add(GPtrArray *array, gpointer data) +{ + g_return_if_fail(array != NULL); + g_ptr_array_grow((GPtrArrayPriv *)array, 1); + array->pdata[array->len++] = data; +} + +gpointer +g_ptr_array_remove_index(GPtrArray *array, guint index) +{ + gpointer removed_node; + + g_return_val_if_fail(array != NULL, NULL); + g_return_val_if_fail(index < array->len, NULL); + + removed_node = array->pdata[index]; + + if(index != array->len - 1) { + g_memmove(array->pdata + index, array->pdata + index + 1, + (array->len - index - 1) * sizeof(gpointer)); + } + + array->len--; + array->pdata[array->len] = NULL; + + return removed_node; +} + +gpointer +g_ptr_array_remove_index_fast(GPtrArray *array, guint index) +{ + gpointer removed_node; + + g_return_val_if_fail(array != NULL, NULL); + g_return_val_if_fail(index < array->len, NULL); + + removed_node = array->pdata[index]; + + if(index != array->len - 1) { + g_memmove(array->pdata + index, array->pdata + array->len - 1, + sizeof(gpointer)); + } + + array->len--; + array->pdata[array->len] = NULL; + + return removed_node; +} + +gboolean +g_ptr_array_remove(GPtrArray *array, gpointer data) +{ + guint i; + + g_return_val_if_fail(array != NULL, FALSE); + + for(i = 0; i < array->len; i++) { + if(array->pdata[i] == data) { + g_ptr_array_remove_index(array, i); + return TRUE; + } + } + + return FALSE; +} + +gboolean +g_ptr_array_remove_fast(GPtrArray *array, gpointer data) +{ + guint i; + + g_return_val_if_fail(array != NULL, FALSE); + + for(i = 0; i < array->len; i++) { + if(array->pdata[i] == data) { + array->len--; + if (array->len > 0) + array->pdata [i] = array->pdata [array->len]; + else + array->pdata [i] = NULL; + return TRUE; + } + } + + return FALSE; +} + +void +g_ptr_array_foreach(GPtrArray *array, GFunc func, gpointer user_data) +{ + guint i; + + for(i = 0; i < array->len; i++) { + func(g_ptr_array_index(array, i), user_data); + } +} + +void +g_ptr_array_sort(GPtrArray *array, GCompareFunc compare) +{ + g_return_if_fail(array != NULL); + qsort(array->pdata, array->len, sizeof(gpointer), compare); +} + +void +g_ptr_array_sort_with_data (GPtrArray *array, GCompareDataFunc compare, gpointer user_data) +{ + g_return_if_fail (array != NULL); + + g_qsort_with_data (array->pdata, array->len, sizeof (gpointer), compare, user_data); +} + +guint +g_ptr_array_capacity (GPtrArray *array) +{ + return ((GPtrArrayPriv *)array)->size; +} diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gqsort.c b/unity-2019.4.24f1-mbe/mono/eglib/gqsort.c new file mode 100644 index 000000000..59bd45371 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gqsort.c @@ -0,0 +1,165 @@ +/* + * QuickSort + * + * Author: Jeffrey Stedfast + * + * (C) 2011 Novell, Inc. + * + * 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. + */ + +#include +#include + +/* Any segment <= this threshold will be sorted using insertion + * sort. OpenBSD seems to use a value of 7 so we'll go with that for + * now... */ +#define MAX_THRESHOLD 7 + +#define STACK_SIZE (8 * sizeof (size_t)) + +typedef struct _QSortStack { + char *array; + size_t count; +} QSortStack; + +#define QSORT_PUSH(sp, a, c) (sp->array = a, sp->count = c, sp++) +#define QSORT_POP(sp, a, c) (sp--, a = sp->array, c = sp->count) + +#define SWAPTYPE(TYPE, a, b) { \ + long __n = size / sizeof (TYPE); \ + register TYPE *__a = (TYPE *) (a); \ + register TYPE *__b = (TYPE *) (b); \ + register TYPE t; \ + \ + do { \ + t = *__a; \ + *__a++ = *__b; \ + *__b++ = t; \ + } while (--__n > 0); \ +} + +#define SWAPBYTE(a, b) SWAPTYPE(char, (a), (b)) +#define SWAPLONG(a, b) SWAPTYPE(long, (a), (b)) +#define SWAP(a, b) if (swaplong) SWAPLONG((a), (b)) else SWAPBYTE((a), (b)) + +/* check if we can swap by longs rather than bytes by making sure that + * memory is properly aligned and that the element size is a multiple + * of sizeof (long) */ +#define SWAP_INIT() swaplong = (((char *) base) - ((char *) 0)) % sizeof (long) == 0 && (size % sizeof (long)) == 0 + +void +g_qsort_with_data (gpointer base, size_t nmemb, size_t size, GCompareDataFunc compare, gpointer user_data) +{ + QSortStack stack[STACK_SIZE], *sp; + register char *i, *k, *mid; + size_t n, n1, n2; + char *lo, *hi; + int swaplong; + + if (nmemb <= 1) + return; + + SWAP_INIT (); + + /* initialize our stack */ + sp = stack; + QSORT_PUSH (sp, base, nmemb); + + do { + QSORT_POP (sp, lo, n); + + hi = lo + (n - 1) * size; + + if (n < MAX_THRESHOLD) { + /* switch to insertion sort */ + for (i = lo + size; i <= hi; i += size) + for (k = i; k > lo && compare (k - size, k, user_data) > 0; k -= size) + SWAP (k - size, k); + + continue; + } + + /* calculate the middle element */ + mid = lo + (n / 2) * size; + + /* once we re-order the lo, mid, and hi elements to be in + * ascending order, we'll use mid as our pivot. */ + if (compare (mid, lo, user_data) < 0) { + SWAP (mid, lo); + } + + if (compare (hi, mid, user_data) < 0) { + SWAP (mid, hi); + if (compare (mid, lo, user_data) < 0) { + SWAP (mid, lo); + } + } + + /* since we've already guaranteed that lo <= mid and mid <= hi, + * we can skip comparing them again */ + i = lo + size; + k = hi - size; + + do { + /* find the first element with a value > pivot value */ + while (i < k && compare (i, mid, user_data) <= 0) + i += size; + + /* find the last element with a value <= pivot value */ + while (k >= i && compare (mid, k, user_data) < 0) + k -= size; + + if (k <= i) + break; + + SWAP (i, k); + + /* make sure we keep track of our pivot element */ + if (mid == i) { + mid = k; + } else if (mid == k) { + mid = i; + } + + i += size; + k -= size; + } while (1); + + if (k != mid) { + /* swap the pivot with the last element in the first partition */ + SWAP (mid, k); + } + + /* calculate segment sizes */ + n2 = (hi - k) / size; + n1 = (k - lo) / size; + + /* push our partitions onto the stack, largest first + * (to make sure we don't run out of stack space) */ + if (n2 > n1) { + if (n2 > 1) QSORT_PUSH (sp, k + size, n2); + if (n1 > 1) QSORT_PUSH (sp, lo, n1); + } else { + if (n1 > 1) QSORT_PUSH (sp, lo, n1); + if (n2 > 1) QSORT_PUSH (sp, k + size, n2); + } + } while (sp > stack); +} diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gqueue.c b/unity-2019.4.24f1-mbe/mono/eglib/gqueue.c new file mode 100644 index 000000000..cd94e1f36 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gqueue.c @@ -0,0 +1,113 @@ +/* + * gqueue.c: Queue + * + * Author: + * Duncan Mak (duncan@novell.com) + * Gonzalo Paniagua Javier (gonzalo@novell.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. + * + * Copyright (c) 2006-2009 Novell, Inc. + * + */ + +#include +#include + +gpointer +g_queue_pop_head (GQueue *queue) +{ + gpointer result; + GList *old_head; + + if (!queue || queue->length == 0) + return NULL; + + result = queue->head->data; + old_head = queue->head; + queue->head = old_head->next; + g_list_free_1 (old_head); + + if (--queue->length) + queue->head->prev = NULL; + else + queue->tail = NULL; + + return result; +} + +gboolean +g_queue_is_empty (GQueue *queue) +{ + if (!queue) + return TRUE; + + return queue->length == 0; +} + +void +g_queue_push_head (GQueue *queue, gpointer head) +{ + if (!queue) + return; + + queue->head = g_list_prepend (queue->head, head); + + if (!queue->tail) + queue->tail = queue->head; + + queue->length ++; +} + +void +g_queue_push_tail (GQueue *queue, gpointer data) +{ + if (!queue) + return; + + queue->tail = g_list_append (queue->tail, data); + if (queue->head == NULL) + queue->head = queue->tail; + else + queue->tail = queue->tail->next; + queue->length++; +} + +GQueue * +g_queue_new (void) +{ + return g_new0 (GQueue, 1); +} + +void +g_queue_free (GQueue *queue) +{ + if (!queue) + return; + + g_list_free (queue->head); + g_free (queue); +} + +void +g_queue_foreach (GQueue *queue, GFunc func, gpointer user_data) +{ + g_list_foreach (queue->head, func, user_data); +} diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gshell.c b/unity-2019.4.24f1-mbe/mono/eglib/gshell.c new file mode 100644 index 000000000..43b4c1787 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gshell.c @@ -0,0 +1,291 @@ +/* + * Shell utility functions. + * + * Author: + * Gonzalo Paniagua Javier (gonzalo@novell.com + * + * (C) 2006 Novell, Inc. + * + * 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. + */ +#include +#include + +static int +split_cmdline (const gchar *cmdline, GPtrArray *array, GError **error) +{ + gchar *ptr; + gchar c; + gboolean escaped = FALSE, fresh = TRUE; + gchar quote_char = '\0'; + GString *str; + + str = g_string_new (""); + ptr = (gchar *) cmdline; + while ((c = *ptr++) != '\0') { + if (escaped) { + /* + * \CHAR is only special inside a double quote if CHAR is + * one of: $`"\ and newline + */ + if (quote_char == '\"'){ + if (!(c == '$' || c == '`' || c == '"' || c == '\\')) + g_string_append_c (str, '\\'); + g_string_append_c (str, c); + } else { + if (!g_ascii_isspace (c)) + g_string_append_c (str, c); + } + escaped = FALSE; + } else if (quote_char) { + if (c == quote_char) { + quote_char = '\0'; + if (fresh && (g_ascii_isspace (*ptr) || *ptr == '\0')){ + g_ptr_array_add (array, g_string_free (str, FALSE)); + str = g_string_new (""); + } + } else if (c == '\\'){ + escaped = TRUE; + } else + g_string_append_c (str, c); + } else if (g_ascii_isspace (c)) { + if (str->len > 0) { + g_ptr_array_add (array, g_string_free (str, FALSE)); + str = g_string_new (""); + } + } else if (c == '\\') { + escaped = TRUE; + } else if (c == '\'' || c == '"') { + fresh = str->len == 0; + quote_char = c; + } else { + g_string_append_c (str, c); + } + } + + if (escaped) { + if (error) + *error = g_error_new (G_LOG_DOMAIN, 0, "Unfinished escape."); + g_string_free (str, TRUE); + return -1; + } + + if (quote_char) { + if (error) + *error = g_error_new (G_LOG_DOMAIN, 0, "Unfinished quote."); + g_string_free (str, TRUE); + return -1; + } + + if (str->len > 0) { + g_ptr_array_add (array, g_string_free (str, FALSE)); + } else { + g_string_free (str, TRUE); + } + g_ptr_array_add (array, NULL); + return 0; +} + +gboolean +g_shell_parse_argv (const gchar *command_line, gint *argcp, gchar ***argvp, GError **error) +{ + GPtrArray *array; + gint argc; + gchar **argv; + + g_return_val_if_fail (command_line, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + array = g_ptr_array_new(); + if (split_cmdline (command_line, array, error)) { + g_ptr_array_add (array, NULL); + g_strfreev ((gchar **) array->pdata); + g_ptr_array_free (array, FALSE); + return FALSE; + } + + argc = array->len; + argv = (gchar **) array->pdata; + + if (argc == 1) { + g_strfreev (argv); + g_ptr_array_free (array, FALSE); + return FALSE; + } + + if (argcp) { + *argcp = array->len - 1; + } + + if (argvp) { + *argvp = argv; + } else { + g_strfreev (argv); + } + + g_ptr_array_free (array, FALSE); + return TRUE; +} + +gchar * +g_shell_quote (const gchar *unquoted_string) +{ + GString *result = g_string_new ("'"); + const gchar *p; + + for (p = unquoted_string; *p; p++){ + if (*p == '\'') + g_string_append (result, "'\\'"); + g_string_append_c (result, *p); + } + g_string_append_c (result, '\''); + return g_string_free (result, FALSE); +} + +gchar * +g_shell_unquote (const gchar *quoted_string, GError **error) +{ + GString *result; + const char *p; + int do_unquote = 0; + + if (quoted_string == NULL) + return NULL; + + /* Quickly try to determine if we need to unquote or not */ + for (p = quoted_string; *p; p++){ + if (*p == '\'' || *p == '"' || *p == '\\'){ + do_unquote = 1; + break; + } + } + + if (!do_unquote) + return g_strdup (quoted_string); + + /* We do need to unquote */ + result = g_string_new (""); + for (p = quoted_string; *p; p++){ + + if (*p == '\''){ + /* Process single quote, not even \ is processed by glib's version */ + for (p++; *p; p++){ + if (*p == '\'') + break; + g_string_append_c (result, *p); + } + if (!*p){ + g_set_error (error, 0, 0, "Open quote"); + return NULL; + } + } else if (*p == '"'){ + /* Process double quote, allows some escaping */ + for (p++; *p; p++){ + if (*p == '"') + break; + if (*p == '\\'){ + p++; + if (*p == 0){ + g_set_error (error, 0, 0, "Open quote"); + return NULL; + } + switch (*p){ + case '$': + case '"': + case '\\': + case '`': + break; + default: + g_string_append_c (result, '\\'); + break; + } + } + g_string_append_c (result, *p); + } + if (!*p){ + g_set_error (error, 0, 0, "Open quote"); + return NULL; + } + } else if (*p == '\\'){ + char c = *(++p); + if (!(c == '$' || c == '"' || c == '\\' || c == '`' || c == '\'' || c == 0 )) + g_string_append_c (result, '\\'); + if (c == 0) + break; + else + g_string_append_c (result, c); + } else + g_string_append_c (result, *p); + } + return g_string_free (result, FALSE); +} + +#if JOINT_TEST +/* + * This test is designed to be built with the 2 glib/eglib to compare + */ + +char *args [] = { + "\\", + "\"Foo'bar\"", + "'foo'", + "'fo\'b'", + "'foo\"bar'", + "'foo' dingus bar", + "'foo' 'bar' 'baz'", + "\"foo\" 'bar' \"baz\"", + "\"f\\$\\\'", + "\"\\", + "\\\\", + "'\\\\'", + "\"f\\$\"\\\"\\\\", // /\\\"\\\\" + "'f\\$'\\\"\\\\", + "'f\\$\\\\'", + NULL +}; + + +int +main () +{ + char **s = args; + int i; + + while (*s){ + char *r1 = g_shell_unquote (*s, NULL); + char *r2 = g2_shell_unquote (*s, NULL); + char *ok = r1 == r2 ? "ok" : (r1 != NULL && r2 != NULL && strcmp (r1, r2) == 0) ? "ok" : "fail"; + + printf ("%s [%s] -> [%s] - [%s]\n", ok, *s, r1, r2); + s++; + } + return; + char buffer [10]; + buffer [0] = '\"'; + buffer [1] = '\\'; + buffer [3] = '\"'; + buffer [4] = 0; + + for (i = 32; i < 255; i++){ + buffer [2] = i; + printf ("%d [%s] -> [%s]\n", i, buffer, g_shell_unquote (buffer, NULL)); + } +} +#endif diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gslist.c b/unity-2019.4.24f1-mbe/mono/eglib/gslist.c new file mode 100644 index 000000000..5baa297f7 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gslist.c @@ -0,0 +1,362 @@ +/* + * gslist.c: Singly-linked list implementation + * + * Authors: + * Duncan Mak (duncan@novell.com) + * Raja R Harinath (rharinath@novell.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. + * + * (C) 2006 Novell, Inc. + */ + +#include +#include + +GSList* +g_slist_alloc (void) +{ + return g_new0 (GSList, 1); +} + +void +g_slist_free_1 (GSList *list) +{ + g_free (list); +} + +GSList* +g_slist_append (GSList *list, gpointer data) +{ + return g_slist_concat (list, g_slist_prepend (NULL, data)); +} + +/* This is also a list node constructor. */ +GSList* +g_slist_prepend (GSList *list, gpointer data) +{ + GSList *head = g_slist_alloc (); + head->data = data; + head->next = list; + + return head; +} + +/* + * Insert the given data in a new node after the current node. + * Return new node. + */ +static inline GSList * +insert_after (GSList *list, gpointer data) +{ + list->next = g_slist_prepend (list->next, data); + return list->next; +} + +/* + * Return the node prior to the node containing 'data'. + * If the list is empty, or the first node contains 'data', return NULL. + * If no node contains 'data', return the last node. + */ +static inline GSList* +find_prev (GSList *list, gconstpointer data) +{ + GSList *prev = NULL; + while (list) { + if (list->data == data) + break; + prev = list; + list = list->next; + } + return prev; +} + +/* like 'find_prev', but searches for node 'link' */ +static inline GSList* +find_prev_link (GSList *list, GSList *link) +{ + GSList *prev = NULL; + while (list) { + if (list == link) + break; + prev = list; + list = list->next; + } + return prev; +} + +GSList* +g_slist_insert_before (GSList *list, GSList *sibling, gpointer data) +{ + GSList *prev = find_prev_link (list, sibling); + + if (!prev) + return g_slist_prepend (list, data); + + insert_after (prev, data); + return list; +} + +void +g_slist_free (GSList *list) +{ + while (list) { + GSList *next = list->next; + g_slist_free_1 (list); + list = next; + } +} + +GSList* +g_slist_copy (GSList *list) +{ + GSList *copy, *tmp; + + if (!list) + return NULL; + + copy = g_slist_prepend (NULL, list->data); + tmp = copy; + + for (list = list->next; list; list = list->next) + tmp = insert_after (tmp, list->data); + + return copy; +} + +GSList* +g_slist_concat (GSList *list1, GSList *list2) +{ + if (!list1) + return list2; + + g_slist_last (list1)->next = list2; + return list1; +} + +void +g_slist_foreach (GSList *list, GFunc func, gpointer user_data) +{ + while (list) { + (*func) (list->data, user_data); + list = list->next; + } +} + +GSList* +g_slist_last (GSList *list) +{ + if (!list) + return NULL; + + while (list->next) + list = list->next; + + return list; +} + +GSList* +g_slist_find (GSList *list, gconstpointer data) +{ + for (; list; list = list->next) + if (list->data == data) + break; + return list; +} + +GSList * +g_slist_find_custom (GSList *list, gconstpointer data, GCompareFunc func) +{ + if (!func) + return NULL; + + while (list) { + if (func (list->data, data) == 0) + return list; + + list = list->next; + } + + return NULL; +} + +guint +g_slist_length (GSList *list) +{ + guint length = 0; + + while (list) { + length ++; + list = list->next; + } + + return length; +} + +GSList* +g_slist_remove (GSList *list, gconstpointer data) +{ + GSList *prev = find_prev (list, data); + GSList *current = prev ? prev->next : list; + + if (current) { + if (prev) + prev->next = current->next; + else + list = current->next; + g_slist_free_1 (current); + } + + return list; +} + +GSList* +g_slist_remove_all (GSList *list, gconstpointer data) +{ + GSList *next = list; + GSList *prev = NULL; + GSList *current; + + while (next) { + GSList *tmp_prev = find_prev (next, data); + if (tmp_prev) + prev = tmp_prev; + current = prev ? prev->next : list; + + if (!current) + break; + + next = current->next; + + if (prev) + prev->next = next; + else + list = next; + g_slist_free_1 (current); + } + + return list; +} + +GSList* +g_slist_remove_link (GSList *list, GSList *link) +{ + GSList *prev = find_prev_link (list, link); + GSList *current = prev ? prev->next : list; + + if (current) { + if (prev) + prev->next = current->next; + else + list = current->next; + current->next = NULL; + } + + return list; +} + +GSList* +g_slist_delete_link (GSList *list, GSList *link) +{ + list = g_slist_remove_link (list, link); + g_slist_free_1 (link); + + return list; +} + +GSList* +g_slist_reverse (GSList *list) +{ + GSList *prev = NULL; + while (list){ + GSList *next = list->next; + list->next = prev; + prev = list; + list = next; + } + + return prev; +} + +GSList* +g_slist_insert_sorted (GSList *list, gpointer data, GCompareFunc func) +{ + GSList *prev = NULL; + + if (!func) + return list; + + if (!list || func (list->data, data) > 0) + return g_slist_prepend (list, data); + + /* Invariant: func (prev->data, data) <= 0) */ + for (prev = list; prev->next; prev = prev->next) + if (func (prev->next->data, data) > 0) + break; + + /* ... && (prev->next == 0 || func (prev->next->data, data) > 0)) */ + insert_after (prev, data); + return list; +} + +gint +g_slist_index (GSList *list, gconstpointer data) +{ + gint index = 0; + + while (list) { + if (list->data == data) + return index; + + index++; + list = list->next; + } + + return -1; +} + +GSList* +g_slist_nth (GSList *list, guint n) +{ + for (; list; list = list->next) { + if (n == 0) + break; + n--; + } + return list; +} + +gpointer +g_slist_nth_data (GSList *list, guint n) +{ + GSList *node = g_slist_nth (list, n); + return node ? node->data : NULL; +} + +typedef GSList list_node; +#include "sort.frag.h" + +GSList* +g_slist_sort (GSList *list, GCompareFunc func) +{ + if (!list || !list->next) + return list; + return do_sort (list, func); +} diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gspawn.c b/unity-2019.4.24f1-mbe/mono/eglib/gspawn.c new file mode 100644 index 000000000..01639be10 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gspawn.c @@ -0,0 +1,530 @@ +/* + * Spawning processes. + * + * Author: + * Gonzalo Paniagua Javier (gonzalo@novell.com + * + * (C) 2006 Novell, Inc. + * + * 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. + */ +#include +#include +#include +#include +#include +#include + +#include + +#ifdef HAVE_UNISTD_H +#ifndef __USE_GNU +#define __USE_GNU +#endif +#include +#endif + +#ifdef HAVE_SYS_SELECT_H +#include +#endif + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#ifdef HAVE_SYS_WAIT_H +#include +#endif + +#ifdef HAVE_SYS_RESOURCE_H +# include +#endif + +#ifdef G_OS_WIN32 +#include +#include +#define open _open +#define close _close +#define read _read +#define write _write +/* windows pipe api details: http://msdn2.microsoft.com/en-us/library/edze9h7e(VS.80).aspx */ +#define pipe(x) _pipe(x, 256, 0) +#endif + +#define set_error(msg, ...) do { if (error != NULL) *error = g_error_new (G_LOG_DOMAIN, 1, msg, __VA_ARGS__); } while (0) +#define set_error_cond(cond,msg, ...) do { if ((cond) && error != NULL) *error = g_error_new (G_LOG_DOMAIN, 1, msg, __VA_ARGS__); } while (0) +#define set_error_status(status,msg, ...) do { if (error != NULL) *error = g_error_new (G_LOG_DOMAIN, status, msg, __VA_ARGS__); } while (0) +#define NO_INTR(var,cmd) do { (var) = (cmd); } while ((var) == -1 && errno == EINTR) +#define CLOSE_PIPE(p) do { close (p [0]); close (p [1]); } while (0) + +#if defined(__APPLE__) +#if defined (TARGET_OSX) +/* Apple defines this in crt_externs.h but doesn't provide that header for + * arm-apple-darwin9. We'll manually define the symbol on Apple as it does + * in fact exist on all implementations (so far) + */ +gchar ***_NSGetEnviron(void); +#define environ (*_NSGetEnviron()) +#else +static char *mono_environ[1] = { NULL }; +#define environ mono_environ +#endif /* defined (TARGET_OSX) */ +#elif defined(_MSC_VER) +/* MS defines this in stdlib.h */ +#else +extern char **environ; +#endif + +#ifndef G_OS_WIN32 +static int +safe_read (int fd, gchar *buffer, gint count, GError **error) +{ + int res; + + NO_INTR (res, read (fd, buffer, count)); + set_error_cond (res == -1, "%s", "Error reading from pipe."); + return res; +} + +static int +read_pipes (int outfd, gchar **out_str, int errfd, gchar **err_str, GError **error) +{ + fd_set rfds; + int res; + gboolean out_closed; + gboolean err_closed; + GString *out = NULL; + GString *err = NULL; + gchar *buffer = NULL; + gint nread; + + out_closed = (outfd < 0); + err_closed = (errfd < 0); + if (out_str) { + *out_str = NULL; + out = g_string_new (""); + } + + if (err_str) { + *err_str = NULL; + err = g_string_new (""); + } + + do { + if (out_closed && err_closed) + break; + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4389) +#endif + + FD_ZERO (&rfds); + if (!out_closed && outfd >= 0) + FD_SET (outfd, &rfds); + if (!err_closed && errfd >= 0) + FD_SET (errfd, &rfds); + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + res = select (MAX (outfd, errfd) + 1, &rfds, NULL, NULL, NULL); + if (res > 0) { + if (buffer == NULL) + buffer = g_malloc (1024); + if (!out_closed && FD_ISSET (outfd, &rfds)) { + nread = safe_read (outfd, buffer, 1024, error); + if (nread < 0) { + close (errfd); + close (outfd); + return -1; + } + g_string_append_len (out, buffer, nread); + if (nread <= 0) { + out_closed = TRUE; + close (outfd); + } + } + + if (!err_closed && FD_ISSET (errfd, &rfds)) { + nread = safe_read (errfd, buffer, 1024, error); + if (nread < 0) { + close (errfd); + close (outfd); + return -1; + } + g_string_append_len (err, buffer, nread); + if (nread <= 0) { + err_closed = TRUE; + close (errfd); + } + } + } + } while (res > 0 || (res == -1 && errno == EINTR)); + + g_free (buffer); + if (out_str) + *out_str = g_string_free (out, FALSE); + + if (err_str) + *err_str = g_string_free (err, FALSE); + + return 0; +} + +static gboolean +create_pipe (int *fds, GError **error) +{ + if (pipe (fds) == -1) { + set_error ("%s", "Error creating pipe."); + return FALSE; + } + return TRUE; +} +#endif /* G_OS_WIN32 */ + +static int +write_all (int fd, const void *vbuf, size_t n) +{ + const char *buf = (const char *) vbuf; + size_t nwritten = 0; + int w; + + do { + do { + w = write (fd, buf + nwritten, n - nwritten); + } while (w == -1 && errno == EINTR); + + if (w == -1) + return -1; + + nwritten += w; + } while (nwritten < n); + + return nwritten; +} + +#ifndef G_OS_WIN32 +int +eg_getdtablesize (void) +{ +#ifdef HAVE_GETRLIMIT + struct rlimit limit; + int res; + + res = getrlimit (RLIMIT_NOFILE, &limit); + g_assert (res == 0); + return limit.rlim_cur; +#else + return getdtablesize (); +#endif +} +#else +int +eg_getdtablesize (void) +{ + g_error ("Should not be called"); +} +#endif + +gboolean +g_spawn_command_line_sync (const gchar *command_line, + gchar **standard_output, + gchar **standard_error, + gint *exit_status, + GError **error) +{ +#ifdef G_OS_WIN32 +#elif !defined (HAVE_FORK) || !defined (HAVE_EXECV) + fprintf (stderr, "g_spawn_command_line_sync not supported on this platform\n"); + return FALSE; +#else + pid_t pid; + gchar **argv; + gint argc; + int stdout_pipe [2] = { -1, -1 }; + int stderr_pipe [2] = { -1, -1 }; + int status; + int res; + + if (!g_shell_parse_argv (command_line, &argc, &argv, error)) + return FALSE; + + if (standard_output && !create_pipe (stdout_pipe, error)) + return FALSE; + + if (standard_error && !create_pipe (stderr_pipe, error)) { + if (standard_output) { + CLOSE_PIPE (stdout_pipe); + } + return FALSE; + } + + pid = fork (); + if (pid == 0) { + gint i; + + if (standard_output) { + close (stdout_pipe [0]); + dup2 (stdout_pipe [1], STDOUT_FILENO); + } + + if (standard_error) { + close (stderr_pipe [0]); + dup2 (stderr_pipe [1], STDERR_FILENO); + } + for (i = eg_getdtablesize () - 1; i >= 3; i--) + close (i); + + /* G_SPAWN_SEARCH_PATH is always enabled for g_spawn_command_line_sync */ + if (!g_path_is_absolute (argv [0])) { + gchar *arg0; + + arg0 = g_find_program_in_path (argv [0]); + if (arg0 == NULL) { + exit (1); + } + //g_free (argv [0]); + argv [0] = arg0; + } + execv (argv [0], argv); + exit (1); /* TODO: What now? */ + } + + g_strfreev (argv); + if (standard_output) + close (stdout_pipe [1]); + + if (standard_error) + close (stderr_pipe [1]); + + if (standard_output || standard_error) { + res = read_pipes (stdout_pipe [0], standard_output, stderr_pipe [0], standard_error, error); + if (res) { + waitpid (pid, &status, WNOHANG); /* avoid zombie */ + return FALSE; + } + } + + NO_INTR (res, waitpid (pid, &status, 0)); + + /* TODO: What if error? */ + if (WIFEXITED (status) && exit_status) { + *exit_status = WEXITSTATUS (status); + } +#endif + return TRUE; +} + +/* + * This is the only use we have in mono/metadata +!g_spawn_async_with_pipes (NULL, (char**)addr_argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, &child_pid, &ch_in, &ch_out, NULL, NULL) +*/ +gboolean +g_spawn_async_with_pipes (const gchar *working_directory, + gchar **argv, + gchar **envp, + GSpawnFlags flags, + GSpawnChildSetupFunc child_setup, + gpointer user_data, + GPid *child_pid, + gint *standard_input, + gint *standard_output, + gint *standard_error, + GError **error) +{ +#ifdef G_OS_WIN32 +#elif !defined (HAVE_FORK) || !defined (HAVE_EXECVE) + fprintf (stderr, "g_spawn_async_with_pipes is not supported on this platform\n"); + return FALSE; +#else + pid_t pid; + int info_pipe [2]; + int in_pipe [2] = { -1, -1 }; + int out_pipe [2] = { -1, -1 }; + int err_pipe [2] = { -1, -1 }; + int status; + + g_return_val_if_fail (argv != NULL, FALSE); /* Only mandatory arg */ + + if (!create_pipe (info_pipe, error)) + return FALSE; + + if (standard_output && !create_pipe (out_pipe, error)) { + CLOSE_PIPE (info_pipe); + return FALSE; + } + + if (standard_error && !create_pipe (err_pipe, error)) { + CLOSE_PIPE (info_pipe); + CLOSE_PIPE (out_pipe); + return FALSE; + } + + if (standard_input && !create_pipe (in_pipe, error)) { + CLOSE_PIPE (info_pipe); + CLOSE_PIPE (out_pipe); + CLOSE_PIPE (err_pipe); + return FALSE; + } + + pid = fork (); + if (pid == -1) { + CLOSE_PIPE (info_pipe); + CLOSE_PIPE (out_pipe); + CLOSE_PIPE (err_pipe); + CLOSE_PIPE (in_pipe); + set_error ("%s", "Error in fork ()"); + return FALSE; + } + + if (pid == 0) { + /* No zombie left behind */ + if ((flags & G_SPAWN_DO_NOT_REAP_CHILD) == 0) { + pid = fork (); + } + + if (pid != 0) { + exit (pid == -1 ? 1 : 0); + } else { + gint i; + int fd; + gchar *arg0; + gchar **actual_args; + gint unused; + + close (info_pipe [0]); + close (in_pipe [1]); + close (out_pipe [0]); + close (err_pipe [0]); + + /* when exec* succeeds, we want to close this fd, which will return + * a 0 read on the parent. We're not supposed to keep it open forever. + * If exec fails, we still can write the error to it before closing. + */ + fcntl (info_pipe [1], F_SETFD, FD_CLOEXEC); + + if ((flags & G_SPAWN_DO_NOT_REAP_CHILD) == 0) { + pid = getpid (); + NO_INTR (unused, write_all (info_pipe [1], &pid, sizeof (pid_t))); + } + + if (working_directory && chdir (working_directory) == -1) { + int err = errno; + NO_INTR (unused, write_all (info_pipe [1], &err, sizeof (int))); + exit (0); + } + + if (standard_output) { + dup2 (out_pipe [1], STDOUT_FILENO); + } else if ((flags & G_SPAWN_STDOUT_TO_DEV_NULL) != 0) { + fd = open ("/dev/null", O_WRONLY); + dup2 (fd, STDOUT_FILENO); + } + + if (standard_error) { + dup2 (err_pipe [1], STDERR_FILENO); + } else if ((flags & G_SPAWN_STDERR_TO_DEV_NULL) != 0) { + fd = open ("/dev/null", O_WRONLY); + dup2 (fd, STDERR_FILENO); + } + + if (standard_input) { + dup2 (in_pipe [0], STDIN_FILENO); + } else if ((flags & G_SPAWN_CHILD_INHERITS_STDIN) == 0) { + fd = open ("/dev/null", O_RDONLY); + dup2 (fd, STDIN_FILENO); + } + + if ((flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN) != 0) { + for (i = eg_getdtablesize () - 1; i >= 3; i--) + close (i); + } + + actual_args = ((flags & G_SPAWN_FILE_AND_ARGV_ZERO) == 0) ? argv : argv + 1; + if (envp == NULL) + envp = environ; + + if (child_setup) + child_setup (user_data); + + arg0 = argv [0]; + if (!g_path_is_absolute (arg0) || (flags & G_SPAWN_SEARCH_PATH) != 0) { + arg0 = g_find_program_in_path (argv [0]); + if (arg0 == NULL) { + int err = ENOENT; + write_all (info_pipe [1], &err, sizeof (int)); + exit (0); + } + } + + execve (arg0, actual_args, envp); + write_all (info_pipe [1], &errno, sizeof (int)); + exit (0); + } + } else if ((flags & G_SPAWN_DO_NOT_REAP_CHILD) == 0) { + int w; + /* Wait for the first child if two are created */ + NO_INTR (w, waitpid (pid, &status, 0)); + if (status == 1 || w == -1) { + CLOSE_PIPE (info_pipe); + CLOSE_PIPE (out_pipe); + CLOSE_PIPE (err_pipe); + CLOSE_PIPE (in_pipe); + set_error ("Error in fork (): %d", status); + return FALSE; + } + } + close (info_pipe [1]); + close (in_pipe [0]); + close (out_pipe [1]); + close (err_pipe [1]); + + if ((flags & G_SPAWN_DO_NOT_REAP_CHILD) == 0) { + int x; + NO_INTR (x, read (info_pipe [0], &pid, sizeof (pid_t))); /* if we read < sizeof (pid_t)... */ + } + + if (child_pid) { + *child_pid = pid; + } + + if (read (info_pipe [0], &status, sizeof (int)) != 0) { + close (info_pipe [0]); + close (in_pipe [0]); + close (out_pipe [1]); + close (err_pipe [1]); + set_error_status (status, "Error in exec (%d -> %s)", status, strerror (status)); + return FALSE; + } + + close (info_pipe [0]); + if (standard_input) + *standard_input = in_pipe [1]; + if (standard_output) + *standard_output = out_pipe [0]; + if (standard_error) + *standard_error = err_pipe [0]; +#endif + return TRUE; +} + + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gstr.c b/unity-2019.4.24f1-mbe/mono/eglib/gstr.c new file mode 100644 index 000000000..e8a76e429 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gstr.c @@ -0,0 +1,1035 @@ +/* + * gstr.c: String Utility Functions. + * + * Author: + * Miguel de Icaza (miguel@novell.com) + * Aaron Bockover (abockover@novell.com) + * + * (C) 2006 Novell, Inc. + * + * 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. + */ +#include +#include +#include +#include +#include + +#ifndef G_OS_WIN32 +#include +#endif + +#include + +/* + * g_strndup and g_vasprintf need to allocate memory with g_malloc if + * ENABLE_OVERRIDABLE_ALLOCATORS is defined so that it can be safely freed with g_free + * rather than free. + */ + +/* This is not a macro, because I dont want to put _GNU_SOURCE in the glib.h header */ +gchar * +g_strndup (const gchar *str, gsize n) +{ +#if defined (HAVE_STRNDUP) && !defined (ENABLE_OVERRIDABLE_ALLOCATORS) + return strndup (str, n); +#else + if (str) { + char *retval = g_malloc(n+1); + if (retval) { + strncpy(retval, str, n)[n] = 0; + } + return retval; + } + return NULL; +#endif +} + +gint g_vasprintf (gchar **ret, const gchar *fmt, va_list ap) +{ +#if defined (HAVE_VASPRINTF) && !defined (ENABLE_OVERRIDABLE_ALLOCATORS) + return vasprintf (ret, fmt, ap); +#else + char *buf; + int len; + size_t buflen; + va_list ap2; + +#if defined(_MSC_VER) || defined(__MINGW64_VERSION_MAJOR) + ap2 = ap; + len = _vscprintf(fmt, ap2); // NOTE MS specific extension ( :-( ) +#else + va_copy(ap2, ap); + len = vsnprintf(NULL, 0, fmt, ap2); +#endif + + if (len >= 0 && (buf = g_malloc ((buflen = (size_t) (len + 1)))) != NULL) { + len = vsnprintf(buf, buflen, fmt, ap); + *ret = buf; + } else { + *ret = NULL; + len = -1; + } + + va_end(ap2); + return len; +#endif +} + +void +g_strfreev (gchar **str_array) +{ + gchar **orig = str_array; + if (str_array == NULL) + return; + while (*str_array != NULL){ + g_free (*str_array); + str_array++; + } + g_free (orig); +} + +gchar ** +g_strdupv (gchar **str_array) +{ + guint length; + gchar **ret; + guint i; + + if (!str_array) + return NULL; + + length = g_strv_length(str_array); + ret = g_new0(gchar *, length + 1); + for (i = 0; str_array[i]; i++) { + ret[i] = g_strdup(str_array[i]); + } + ret[length] = NULL; + return ret; +} + +guint +g_strv_length(gchar **str_array) +{ + gint length = 0; + g_return_val_if_fail(str_array != NULL, 0); + for(length = 0; str_array[length] != NULL; length++); + return length; +} + +gboolean +g_str_has_suffix(const gchar *str, const gchar *suffix) +{ + size_t str_length; + size_t suffix_length; + + g_return_val_if_fail(str != NULL, FALSE); + g_return_val_if_fail(suffix != NULL, FALSE); + + str_length = strlen(str); + suffix_length = strlen(suffix); + + return suffix_length <= str_length ? + strncmp(str + str_length - suffix_length, suffix, suffix_length) == 0 : + FALSE; +} + +gboolean +g_str_has_prefix(const gchar *str, const gchar *prefix) +{ + size_t str_length; + size_t prefix_length; + + g_return_val_if_fail(str != NULL, FALSE); + g_return_val_if_fail(prefix != NULL, FALSE); + + str_length = strlen(str); + prefix_length = strlen(prefix); + + return prefix_length <= str_length ? + strncmp(str, prefix, prefix_length) == 0 : + FALSE; +} + +gchar * +g_strdup_vprintf (const gchar *format, va_list args) +{ + int n; + char *ret; + + n = g_vasprintf (&ret, format, args); + if (n == -1) + return NULL; + + return ret; +} + +gchar * +g_strdup_printf (const gchar *format, ...) +{ + gchar *ret; + va_list args; + int n; + + va_start (args, format); + n = g_vasprintf (&ret, format, args); + va_end (args); + if (n == -1) + return NULL; + + return ret; +} + + +/* +Max error number we support. It's empirically found by looking at our target OS. + +Last this was checked was June-2017. + +Apple is at 106. +Android is at 133. +*/ +#define MONO_ERRNO_MAX 200 +#define str(s) #s + +#ifndef G_OS_WIN32 +static pthread_mutex_t strerror_lock = PTHREAD_MUTEX_INITIALIZER; +#endif + +static char *error_messages [MONO_ERRNO_MAX]; + +const gchar * +g_strerror (gint errnum) +{ + if (errnum < 0) + errnum = -errnum; + if (errnum >= MONO_ERRNO_MAX) + return ("Error number higher than " str (MONO_ERRNO_MAX)); + + if (!error_messages [errnum]) { +#ifndef G_OS_WIN32 + pthread_mutex_lock (&strerror_lock); +#endif + +#ifdef HAVE_STRERROR_R + char tmp_buff [128]; //Quite arbitrary, should be large enough + char *buff = tmp_buff; + size_t buff_len = sizeof (tmp_buff); + buff [0] = 0; + +#ifndef STRERROR_R_CHAR_P + int r; + while ((r = strerror_r (errnum, buff, buff_len - 1))) { + if (r != ERANGE) { + buff = g_strdup_printf ("Invalid Error code '%d'", errnum); + break; + } + if (buff == tmp_buff) + buff = g_malloc (buff_len * 2); + else + buff = g_realloc (buff, buff_len * 2); + buff_len *= 2; + //Spec is not clean on whether size argument includes space for null terminator or not + } + if (!error_messages [errnum]) + error_messages [errnum] = g_strdup (buff); + if (buff != tmp_buff) + g_free (buff); +#else /* STRERROR_R_CHAR_P */ + buff = strerror_r (errnum, buff, buff_len); + if (!error_messages [errnum]) + error_messages [errnum] = g_strdup (buff); +#endif /* STRERROR_R_CHAR_P */ + +#else /* HAVE_STRERROR_R */ + if (!error_messages [errnum]) + error_messages [errnum] = g_strdup_printf ("Error code '%d'", errnum); +#endif /* HAVE_STRERROR_R */ + + +#ifndef G_OS_WIN32 + pthread_mutex_unlock (&strerror_lock); +#endif + + } + return error_messages [errnum]; +} + +gchar * +g_strconcat (const gchar *first, ...) +{ + va_list args; + size_t total = 0; + char *s, *ret; + g_return_val_if_fail (first != NULL, NULL); + + total += strlen (first); + va_start (args, first); + for (s = va_arg (args, char *); s != NULL; s = va_arg(args, char *)){ + total += strlen (s); + } + va_end (args); + + ret = g_malloc (total + 1); + if (ret == NULL) + return NULL; + + ret [total] = 0; + strcpy (ret, first); + va_start (args, first); + for (s = va_arg (args, char *); s != NULL; s = va_arg(args, char *)){ + strcat (ret, s); + } + va_end (args); + + return ret; +} + +static void +add_to_vector (gchar ***vector, int size, gchar *token) +{ + *vector = *vector == NULL ? + (gchar **)g_malloc(2 * sizeof(*vector)) : + (gchar **)g_realloc(*vector, (size + 1) * sizeof(*vector)); + + (*vector)[size - 1] = token; +} + +gchar ** +g_strsplit (const gchar *string, const gchar *delimiter, gint max_tokens) +{ + const gchar *c; + gchar *token, **vector; + gint size = 1; + + g_return_val_if_fail (string != NULL, NULL); + g_return_val_if_fail (delimiter != NULL, NULL); + g_return_val_if_fail (delimiter[0] != 0, NULL); + + if (strncmp (string, delimiter, strlen (delimiter)) == 0) { + vector = (gchar **)g_malloc (2 * sizeof(vector)); + vector[0] = g_strdup (""); + size++; + string += strlen (delimiter); + } else { + vector = NULL; + } + + while (*string && !(max_tokens > 0 && size >= max_tokens)) { + c = string; + if (strncmp (string, delimiter, strlen (delimiter)) == 0) { + token = g_strdup (""); + string += strlen (delimiter); + } else { + while (*string && strncmp (string, delimiter, strlen (delimiter)) != 0) { + string++; + } + + if (*string) { + gsize toklen = (string - c); + token = g_strndup (c, toklen); + + /* Need to leave a trailing empty + * token if the delimiter is the last + * part of the string + */ + if (strcmp (string, delimiter) != 0) { + string += strlen (delimiter); + } + } else { + token = g_strdup (c); + } + } + + add_to_vector (&vector, size, token); + size++; + } + + if (*string) { + if (strcmp (string, delimiter) == 0) + add_to_vector (&vector, size, g_strdup ("")); + else { + /* Add the rest of the string as the last element */ + add_to_vector (&vector, size, g_strdup (string)); + } + size++; + } + + if (vector == NULL) { + vector = (gchar **) g_malloc (2 * sizeof (vector)); + vector [0] = NULL; + } else if (size > 0) { + vector[size - 1] = NULL; + } + + return vector; +} + +static gboolean +charcmp (gchar testchar, const gchar *compare) +{ + while(*compare) { + if (*compare == testchar) { + return TRUE; + } + compare++; + } + + return FALSE; +} + +gchar ** +g_strsplit_set (const gchar *string, const gchar *delimiter, gint max_tokens) +{ + const gchar *c; + gchar *token, **vector; + gint size = 1; + + g_return_val_if_fail (string != NULL, NULL); + g_return_val_if_fail (delimiter != NULL, NULL); + g_return_val_if_fail (delimiter[0] != 0, NULL); + + if (charcmp (*string, delimiter)) { + vector = (gchar **)g_malloc (2 * sizeof(vector)); + vector[0] = g_strdup (""); + size++; + string++; + } else { + vector = NULL; + } + + c = string; + while (*string && !(max_tokens > 0 && size >= max_tokens)) { + if (charcmp (*string, delimiter)) { + gsize toklen = (string - c); + if (toklen == 0) { + token = g_strdup (""); + } else { + token = g_strndup (c, toklen); + } + + c = string + 1; + + add_to_vector (&vector, size, token); + size++; + } + + string++; + } + + if (max_tokens > 0 && size >= max_tokens) { + if (*string) { + /* Add the rest of the string as the last element */ + add_to_vector (&vector, size, g_strdup (string)); + size++; + } + } else { + if (*c) { + /* Fill in the trailing last token */ + add_to_vector (&vector, size, g_strdup (c)); + size++; + } else { + /* Need to leave a trailing empty token if the + * delimiter is the last part of the string + */ + add_to_vector (&vector, size, g_strdup ("")); + size++; + } + } + + if (vector == NULL) { + vector = (gchar **) g_malloc (2 * sizeof (vector)); + vector [0] = NULL; + } else if (size > 0) { + vector[size - 1] = NULL; + } + + return vector; +} + +gchar * +g_strreverse (gchar *str) +{ + size_t i, j; + gchar c; + + if (str == NULL) + return NULL; + + if (*str == 0) + return str; + + for (i = 0, j = strlen (str) - 1; i < j; i++, j--) { + c = str [i]; + str [i] = str [j]; + str [j] = c; + } + + return str; +} + +gchar * +g_strjoin (const gchar *separator, ...) +{ + va_list args; + char *res, *s, *r; + size_t len, slen; + + if (separator != NULL) + slen = strlen (separator); + else + slen = 0; + + len = 0; + va_start (args, separator); + for (s = va_arg (args, char *); s != NULL; s = va_arg (args, char *)){ + len += strlen (s); + len += slen; + } + va_end (args); + + if (len == 0) + return g_strdup (""); + + /* Remove the last separator */ + if (slen > 0 && len > 0) + len -= slen; + + res = g_malloc (len + 1); + va_start (args, separator); + s = va_arg (args, char *); + r = g_stpcpy (res, s); + for (s = va_arg (args, char *); s != NULL; s = va_arg (args, char *)){ + if (separator != NULL) + r = g_stpcpy (r, separator); + r = g_stpcpy (r, s); + } + va_end (args); + + return res; +} + +gchar * +g_strjoinv (const gchar *separator, gchar **str_array) +{ + char *res, *r; + size_t slen, len, i; + + if (separator != NULL) + slen = strlen (separator); + else + slen = 0; + + len = 0; + for (i = 0; str_array [i] != NULL; i++){ + len += strlen (str_array [i]); + len += slen; + } + + if (len == 0) + return g_strdup (""); + + if (slen > 0 && len > 0) + len -= slen; + + res = g_malloc (len + 1); + r = g_stpcpy (res, str_array [0]); + for (i = 1; str_array [i] != NULL; i++){ + if (separator != NULL) + r = g_stpcpy (r, separator); + r = g_stpcpy (r, str_array [i]); + } + + return res; +} + +gchar * +g_strchug (gchar *str) +{ + size_t len; + gchar *tmp; + + if (str == NULL) + return NULL; + + tmp = str; + while (*tmp && isspace (*tmp)) tmp++; + if (str != tmp) { + len = strlen (str) - (tmp - str - 1); + memmove (str, tmp, len); + } + return str; +} + +gchar * +g_strchomp (gchar *str) +{ + gchar *tmp; + + if (str == NULL) + return NULL; + + tmp = str + strlen (str) - 1; + while (*tmp && isspace (*tmp)) tmp--; + *(tmp + 1) = '\0'; + return str; +} + +gint +g_printf(gchar const *format, ...) +{ + va_list args; + gint ret; + + va_start(args, format); + ret = vprintf(format, args); + va_end(args); + + return ret; +} + +gint +g_fprintf(FILE *file, gchar const *format, ...) +{ + va_list args; + gint ret; + + va_start(args, format); + ret = vfprintf(file, format, args); + va_end(args); + + return ret; +} + +gint +g_sprintf(gchar *string, gchar const *format, ...) +{ + va_list args; + gint ret; + + va_start(args, format); + ret = vsprintf(string, format, args); + va_end(args); + + return ret; +} + +gint +g_snprintf(gchar *string, gulong n, gchar const *format, ...) +{ + va_list args; + gint ret; + + va_start(args, format); + ret = vsnprintf(string, n, format, args); + va_end(args); + + return ret; +} + +static const char hx [] = { '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + +static gboolean +char_needs_encoding (char c) +{ + if (((unsigned char)c) >= 0x80) + return TRUE; + + if ((c >= '@' && c <= 'Z') || + (c >= 'a' && c <= 'z') || + (c >= '&' && c < 0x3b) || + (c == '!') || (c == '$') || (c == '_') || (c == '=') || (c == '~')) + return FALSE; + return TRUE; +} + +gchar * +g_filename_to_uri (const gchar *filename, const gchar *hostname, GError **error) +{ + size_t n; + char *ret, *rp; + const char *p; +#ifdef G_OS_WIN32 + const char *uriPrefix = "file:///"; +#else + const char *uriPrefix = "file://"; +#endif + + g_return_val_if_fail (filename != NULL, NULL); + + if (hostname != NULL) + g_warning ("%s", "eglib: g_filename_to_uri: hostname not handled"); + + if (!g_path_is_absolute (filename)){ + if (error != NULL) + *error = g_error_new (NULL, 2, "Not an absolute filename"); + + return NULL; + } + + n = strlen (uriPrefix) + 1; + for (p = filename; *p; p++){ +#ifdef G_OS_WIN32 + if (*p == '\\') { + n++; + continue; + } +#endif + if (char_needs_encoding (*p)) + n += 3; + else + n++; + } + ret = g_malloc (n); + strcpy (ret, uriPrefix); + for (p = filename, rp = ret + strlen (ret); *p; p++){ +#ifdef G_OS_WIN32 + if (*p == '\\') { + *rp++ = '/'; + continue; + } +#endif + if (char_needs_encoding (*p)){ + *rp++ = '%'; + *rp++ = hx [((unsigned char)(*p)) >> 4]; + *rp++ = hx [((unsigned char)(*p)) & 0xf]; + } else + *rp++ = *p; + } + *rp = 0; + return ret; +} + +static int +decode (char p) +{ + if (p >= '0' && p <= '9') + return p - '0'; + if (p >= 'A' && p <= 'F') + return p - 'A'; + if (p >= 'a' && p <= 'f') + return p - 'a'; + g_assert_not_reached (); + return 0; +} + +gchar * +g_filename_from_uri (const gchar *uri, gchar **hostname, GError **error) +{ + const char *p; + char *r, *result; + int flen = 0; + + g_return_val_if_fail (uri != NULL, NULL); + + if (hostname != NULL) + g_warning ("%s", "eglib: g_filename_from_uri: hostname not handled"); + + if (strncmp (uri, "file:///", 8) != 0){ + if (error != NULL) + *error = g_error_new (NULL, 2, "URI does not start with the file: scheme"); + return NULL; + } + + for (p = uri + 8; *p; p++){ + if (*p == '%'){ + if (p [1] && p [2] && isxdigit (p [1]) && isxdigit (p [2])){ + p += 2; + } else { + if (error != NULL) + *error = g_error_new (NULL, 2, "URI contains an invalid escape sequence"); + return NULL; + } + } + flen++; + } +#ifndef G_OS_WIN32 + flen++; +#endif + + result = g_malloc (flen + 1); + result [flen] = 0; + +#ifndef G_OS_WIN32 + *result = '/'; + r = result + 1; +#else + r = result; +#endif + + for (p = uri + 8; *p; p++){ + if (*p == '%'){ + *r++ = (char)((decode (p [1]) << 4) | decode (p [2])); + p += 2; + } else + *r++ = *p; + flen++; + } + return result; +} + +void +g_strdown (gchar *string) +{ + g_return_if_fail (string != NULL); + + while (*string){ + *string = (gchar)tolower (*string); + string++; + } +} + +gchar +g_ascii_tolower (gchar c) +{ + return c >= 'A' && c <= 'Z' ? c + ('a' - 'A') : c; +} + +gchar * +g_ascii_strdown (const gchar *str, gssize len) +{ + char *ret; + int i; + + g_return_val_if_fail (str != NULL, NULL); + + if (len == -1) + len = strlen (str); + + ret = g_malloc (len + 1); + for (i = 0; i < len; i++) + ret [i] = (guchar) g_ascii_tolower (str [i]); + ret [i] = 0; + + return ret; +} + +gchar +g_ascii_toupper (gchar c) +{ + return c >= 'a' && c <= 'z' ? c + ('A' - 'a') : c; +} + +gchar * +g_ascii_strup (const gchar *str, gssize len) +{ + char *ret; + int i; + + g_return_val_if_fail (str != NULL, NULL); + + if (len == -1) + len = strlen (str); + + ret = g_malloc (len + 1); + for (i = 0; i < len; i++) + ret [i] = (guchar) g_ascii_toupper (str [i]); + ret [i] = 0; + + return ret; +} + +gint +g_ascii_strncasecmp (const gchar *s1, const gchar *s2, gsize n) +{ + gsize i; + + g_return_val_if_fail (s1 != NULL, 0); + g_return_val_if_fail (s2 != NULL, 0); + + for (i = 0; i < n; i++) { + gchar c1 = g_ascii_tolower (*s1++); + gchar c2 = g_ascii_tolower (*s2++); + + if (c1 != c2) + return c1 - c2; + } + + return 0; +} + +gint +g_ascii_strcasecmp (const gchar *s1, const gchar *s2) +{ + const char *sp1 = s1; + const char *sp2 = s2; + + g_return_val_if_fail (s1 != NULL, 0); + g_return_val_if_fail (s2 != NULL, 0); + + while (*sp1 != '\0') { + char c1 = g_ascii_tolower (*sp1++); + char c2 = g_ascii_tolower (*sp2++); + + if (c1 != c2) + return c1 - c2; + } + + return (*sp1) - (*sp2); +} + +gchar * +g_strdelimit (gchar *string, const gchar *delimiters, gchar new_delimiter) +{ + gchar *ptr; + + g_return_val_if_fail (string != NULL, NULL); + + if (delimiters == NULL) + delimiters = G_STR_DELIMITERS; + + for (ptr = string; *ptr; ptr++) { + if (strchr (delimiters, *ptr)) + *ptr = new_delimiter; + } + + return string; +} + +gsize +g_strlcpy (gchar *dest, const gchar *src, gsize dest_size) +{ +#ifdef HAVE_STRLCPY + return strlcpy (dest, src, dest_size); +#else + gchar *d; + const gchar *s; + gchar c; + gsize len; + + g_return_val_if_fail (src != NULL, 0); + g_return_val_if_fail (dest != NULL, 0); + + len = dest_size; + if (len == 0) + return 0; + + s = src; + d = dest; + while (--len) { + c = *s++; + *d++ = c; + if (c == '\0') + return (dest_size - len - 1); + } + + /* len is 0 i we get here */ + *d = '\0'; + /* we need to return the length of src here */ + while (*s++) ; /* instead of a plain strlen, we use 's' */ + return s - src - 1; +#endif +} + +gchar * +g_stpcpy (gchar *dest, const char *src) +{ + g_return_val_if_fail (dest != NULL, dest); + g_return_val_if_fail (src != NULL, dest); + +#if HAVE_STPCPY + return stpcpy (dest, src); +#else + while (*src) + *dest++ = *src++; + + *dest = '\0'; + + return dest; +#endif +} + +static const gchar escaped_dflt [256] = { + 1, 1, 1, 1, 1, 1, 1, 1, 'b', 't', 'n', 1, 'f', 'r', 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\\', 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 +}; + +gchar * +g_strescape (const gchar *source, const gchar *exceptions) +{ + gchar escaped [256]; + const gchar *ptr; + gchar c; + gchar op; + gchar *result; + gchar *res_ptr; + + g_return_val_if_fail (source != NULL, NULL); + + memcpy (escaped, escaped_dflt, 256); + if (exceptions != NULL) { + for (ptr = exceptions; *ptr; ptr++) + escaped [(int) *ptr] = 0; + } + result = g_malloc (strlen (source) * 4 + 1); /* Worst case: everything octal. */ + res_ptr = result; + for (ptr = source; *ptr; ptr++) { + c = *ptr; + op = escaped [(int) c]; + if (op == 0) { + *res_ptr++ = c; + } else { + *res_ptr++ = '\\'; + if (op != 1) { + *res_ptr++ = op; + } else { + *res_ptr++ = '0' + ((c >> 6) & 3); + *res_ptr++ = '0' + ((c >> 3) & 7); + *res_ptr++ = '0' + (c & 7); + } + } + } + *res_ptr = '\0'; + return result; +} + +gint +g_ascii_xdigit_value (gchar c) +{ + return ((isxdigit (c) == 0) ? -1 : + ((c >= '0' && c <= '9') ? (c - '0') : + ((c >= 'a' && c <= 'f') ? (c - 'a' + 10) : + (c - 'A' + 10)))); +} + +gchar * +g_strnfill (gsize length, gchar fill_char) +{ + gchar *ret = g_new (gchar, length + 1); + + memset (ret, fill_char, length); + ret [length] = 0; + return ret; +} diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gstring.c b/unity-2019.4.24f1-mbe/mono/eglib/gstring.c new file mode 100644 index 000000000..ba75789bc --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gstring.c @@ -0,0 +1,276 @@ +/* + * String functions + * + * Author: + * Miguel de Icaza (miguel@novell.com) + * Aaron Bockover (abockover@novell.com) + * + * (C) 2006 Novell, Inc. + * + * 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. + */ +#include +#include + +#define GROW_IF_NECESSARY(s,l) { \ + if(s->len + l >= s->allocated_len) { \ + s->allocated_len = (s->allocated_len + l + 16) * 2; \ + s->str = g_realloc(s->str, s->allocated_len); \ + } \ +} + +GString * +g_string_new_len (const gchar *init, gssize len) +{ + GString *ret = g_new (GString, 1); + + if (init == NULL) + ret->len = 0; + else + ret->len = len < 0 ? strlen(init) : len; + ret->allocated_len = MAX(ret->len + 1, 16); + ret->str = g_malloc(ret->allocated_len); + if (init) + memcpy(ret->str, init, ret->len); + ret->str[ret->len] = 0; + + return ret; +} + +GString * +g_string_new (const gchar *init) +{ + return g_string_new_len(init, -1); +} + +GString * +g_string_sized_new (gsize default_size) +{ + GString *ret = g_new (GString, 1); + + ret->str = g_malloc (default_size); + ret->str [0] = 0; + ret->len = 0; + ret->allocated_len = default_size; + + return ret; +} + +gchar * +g_string_free (GString *string, gboolean free_segment) +{ + gchar *data; + + g_return_val_if_fail (string != NULL, NULL); + + data = string->str; + g_free(string); + + if(!free_segment) { + return data; + } + + g_free(data); + return NULL; +} + +GString * +g_string_append_len (GString *string, const gchar *val, gssize len) +{ + g_return_val_if_fail(string != NULL, NULL); + g_return_val_if_fail(val != NULL, string); + + if(len < 0) { + len = strlen(val); + } + + GROW_IF_NECESSARY(string, len); + memcpy(string->str + string->len, val, len); + string->len += len; + string->str[string->len] = 0; + + return string; +} + +GString * +g_string_append (GString *string, const gchar *val) +{ + g_return_val_if_fail(string != NULL, NULL); + g_return_val_if_fail(val != NULL, string); + + return g_string_append_len(string, val, -1); +} + +GString * +g_string_append_c (GString *string, gchar c) +{ + g_return_val_if_fail(string != NULL, NULL); + + GROW_IF_NECESSARY(string, 1); + + string->str[string->len] = c; + string->str[string->len + 1] = 0; + string->len++; + + return string; +} + +GString * +g_string_append_unichar (GString *string, gunichar c) +{ + gchar utf8[6]; + gint len; + + g_return_val_if_fail (string != NULL, NULL); + + if ((len = g_unichar_to_utf8 (c, utf8)) <= 0) + return string; + + return g_string_append_len (string, utf8, len); +} + +GString * +g_string_prepend (GString *string, const gchar *val) +{ + gssize len; + + g_return_val_if_fail (string != NULL, string); + g_return_val_if_fail (val != NULL, string); + + len = strlen (val); + + GROW_IF_NECESSARY(string, len); + memmove(string->str + len, string->str, string->len + 1); + memcpy(string->str, val, len); + + return string; +} + +GString * +g_string_insert (GString *string, gssize pos, const gchar *val) +{ + gssize len; + + g_return_val_if_fail (string != NULL, string); + g_return_val_if_fail (val != NULL, string); + g_return_val_if_fail (pos <= string->len, string); + + len = strlen (val); + + GROW_IF_NECESSARY(string, len); + memmove(string->str + pos + len, string->str + pos, string->len - pos - len + 1); + memcpy(string->str + pos, val, len); + + return string; +} + +void +g_string_append_printf (GString *string, const gchar *format, ...) +{ + char *ret; + va_list args; + + g_return_if_fail (string != NULL); + g_return_if_fail (format != NULL); + + va_start (args, format); + ret = g_strdup_vprintf (format, args); + va_end (args); + g_string_append (string, ret); + + g_free (ret); +} + +void +g_string_append_vprintf (GString *string, const gchar *format, va_list args) +{ + char *ret; + + g_return_if_fail (string != NULL); + g_return_if_fail (format != NULL); + + ret = g_strdup_vprintf (format, args); + g_string_append (string, ret); + g_free (ret); +} + +void +g_string_printf (GString *string, const gchar *format, ...) +{ + va_list args; + + g_return_if_fail (string != NULL); + g_return_if_fail (format != NULL); + + g_free (string->str); + + va_start (args, format); + string->str = g_strdup_vprintf (format, args); + va_end (args); + + string->len = strlen (string->str); + string->allocated_len = string->len+1; +} + +GString * +g_string_truncate (GString *string, gsize len) +{ + g_return_val_if_fail (string != NULL, string); + + /* Silent return */ + if (len >= string->len) + return string; + + string->len = len; + string->str[len] = 0; + return string; +} + +GString * +g_string_set_size (GString *string, gsize len) +{ + g_return_val_if_fail (string != NULL, string); + + GROW_IF_NECESSARY(string, len); + + string->len = len; + string->str[len] = 0; + return string; +} + +GString * +g_string_erase (GString *string, gssize pos, gssize len) +{ + g_return_val_if_fail (string != NULL, string); + + /* Silent return */ + if (pos >= string->len) + return string; + + if (len == -1 || (pos + len) >= string->len) { + string->str[pos] = 0; + } + else { + memmove (string->str + pos, string->str + pos + len, string->len - (pos + len) + 1); + string->len -= len; + } + + return string; +} diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gtimer-unix.c b/unity-2019.4.24f1-mbe/mono/eglib/gtimer-unix.c new file mode 100644 index 000000000..085e2c17c --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gtimer-unix.c @@ -0,0 +1,96 @@ +/* + * Timer + * + * Author: + * Gonzalo Paniagua Javier (gonzalo@novell.com + * + * (C) 2006 Novell, Inc. + * + * 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. + */ +#include +#include + +struct _GTimer { + struct timeval start; + struct timeval stop; +}; + +GTimer *g_timer_new (void) +{ + GTimer *timer; + + timer = g_new0 (GTimer, 1); + g_timer_start (timer); + return timer; +} + +void +g_timer_destroy (GTimer *timer) +{ + g_return_if_fail (timer != NULL); + g_free (timer); +} + +void +g_timer_start (GTimer *timer) +{ + g_return_if_fail (timer != NULL); + gettimeofday (&timer->start, NULL); + memset (&timer->stop, 0, sizeof (struct timeval)); +} + +void +g_timer_stop (GTimer *timer) +{ + g_return_if_fail (timer != NULL); + gettimeofday (&timer->stop, NULL); +} + +gdouble +g_timer_elapsed (GTimer *timer, gulong *microseconds) +{ + struct timeval tv; + gulong seconds; + long usec; + gdouble result; + + g_return_val_if_fail (timer != NULL, 0.0); + + if (timer->stop.tv_sec == 0 && timer->stop.tv_usec == 0) { + gettimeofday (&tv, NULL); + } else { + tv = timer->stop; + } + + usec = (tv.tv_usec) - (timer->start.tv_usec); + seconds = tv.tv_sec - timer->start.tv_sec; + if (microseconds) { + if (usec < 0) { + usec += 1000000; + seconds--; + } + *microseconds = usec; + } + result = seconds * 1000000 + usec; + return (result / 1000000); +} + + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gtimer-win32.c b/unity-2019.4.24f1-mbe/mono/eglib/gtimer-win32.c new file mode 100644 index 000000000..07e802bda --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gtimer-win32.c @@ -0,0 +1,94 @@ +/* + * Timer + * + * Author: + * Gonzalo Paniagua Javier (gonzalo@novell.com + * + * (C) 2006 Novell, Inc. + * + * 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. + */ +#include +#include + +struct _GTimer { + guint64 start; + guint64 stop; +}; + +GTimer *g_timer_new (void) +{ + GTimer *timer; + + timer = g_new0 (GTimer, 1); + g_timer_start (timer); + return timer; +} + +void +g_timer_destroy (GTimer *timer) +{ + g_return_if_fail (timer != NULL); + g_free (timer); +} + +void +g_timer_start (GTimer *timer) +{ + g_return_if_fail (timer != NULL); + + QueryPerformanceCounter ((LARGE_INTEGER*)&timer->start); +} + +void +g_timer_stop (GTimer *timer) +{ + g_return_if_fail (timer != NULL); + + QueryPerformanceCounter ((LARGE_INTEGER*)&timer->stop); +} + +gdouble +g_timer_elapsed (GTimer *timer, gulong *microseconds) +{ + static guint64 freq = 0; + guint64 delta, stop; + + if (freq == 0) { + if (!QueryPerformanceFrequency ((LARGE_INTEGER *)&freq)) + freq = 1; + } + + if (timer->stop == 0) { + QueryPerformanceCounter ((LARGE_INTEGER*)&stop); + } + else { + stop = timer->stop; + } + + delta = stop - timer->start; + + if (microseconds) + *microseconds = (gulong) (delta * (1000000.0 / freq)); + + return (gdouble) delta / (gdouble) freq; +} + + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gunicode-win32-uwp.c b/unity-2019.4.24f1-mbe/mono/eglib/gunicode-win32-uwp.c new file mode 100644 index 000000000..359ed2458 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gunicode-win32-uwp.c @@ -0,0 +1,42 @@ +/* + * gunicode-win32-uwp.c: UWP unicode support. + * + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. +*/ +#include +#include + +#if G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) +#define CODESET 1 +#include + +extern const char *my_charset; +static gboolean is_utf8; + +gboolean +g_get_charset (G_CONST_RETURN char **charset) +{ + if (my_charset == NULL) { + static char buf [14]; + CPINFOEXW cp_info; + + GetCPInfoExW (CP_ACP, 0, &cp_info); + sprintf (buf, "CP%u", cp_info.CodePage); + my_charset = buf; + is_utf8 = FALSE; + } + + if (charset != NULL) + *charset = my_charset; + + return is_utf8; +} + +#else /* G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) */ + +#ifdef _MSC_VER +// Quiet Visual Studio linker warning, LNK4221, in cases when this source file intentional ends up empty. +void __mono_win32_gunicode_win32_uwp_quiet_lnk4221(void) {} +#endif +#endif /* G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) */ diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gunicode-win32.c b/unity-2019.4.24f1-mbe/mono/eglib/gunicode-win32.c new file mode 100644 index 000000000..993583629 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gunicode-win32.c @@ -0,0 +1,39 @@ +/* + * gunicode-win32.c: Windows unicode support. + * + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. +*/ +#include +#include + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_XBOXONE_WINAPI_SUPPORT) +#define CODESET 1 +#include + +extern const char *my_charset; +static gboolean is_utf8; + +gboolean +g_get_charset (G_CONST_RETURN char **charset) +{ + if (my_charset == NULL) { + static char buf [14]; + sprintf (buf, "CP%u", GetACP ()); + my_charset = buf; + is_utf8 = FALSE; + } + + if (charset != NULL) + *charset = my_charset; + + return is_utf8; +} + +#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. +void __mono_win32_mono_gunicode_win32_quiet_lnk4221(void) {} +#endif +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gunicode.c b/unity-2019.4.24f1-mbe/mono/eglib/gunicode.c new file mode 100644 index 000000000..c1280f9b8 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gunicode.c @@ -0,0 +1,240 @@ +/* + * gunicode.c: Some Unicode routines + * + * Author: + * Miguel de Icaza (miguel@novell.com) + * + * (C) 2006 Novell, Inc. + * + * utf8 validation code came from: + * libxml2-2.6.26 licensed under the MIT X11 license + * + * Authors credit in libxml's string.c: + * William Brack + * daniel@veillard.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. + * + */ +#include +#include +#include +#include +#include + +#ifndef G_OS_WIN32 +# ifdef HAVE_LOCALCHARSET_H +# include +# endif +#endif + +const char *my_charset; + +/* + * Character set conversion + */ + +GUnicodeType +g_unichar_type (gunichar c) +{ + int i; + + guint16 cp = (guint16) c; + for (i = 0; i < unicode_category_ranges_count; i++) { + if (cp < unicode_category_ranges [i].start) + continue; + if (unicode_category_ranges [i].end <= cp) + continue; + return unicode_category [i] [cp - unicode_category_ranges [i].start]; + } + + /* + // 3400-4DB5: OtherLetter + // 4E00-9FC3: OtherLetter + // AC00-D7A3: OtherLetter + // D800-DFFF: OtherSurrogate + // E000-F8FF: OtherPrivateUse + // 20000-2A6D6 OtherLetter + // F0000-FFFFD OtherPrivateUse + // 100000-10FFFD OtherPrivateUse + */ + if (0x3400 <= cp && cp < 0x4DB5) + return G_UNICODE_OTHER_LETTER; + if (0x4E00 <= cp && cp < 0x9FC3) + return G_UNICODE_OTHER_LETTER; + if (0xAC00<= cp && cp < 0xD7A3) + return G_UNICODE_OTHER_LETTER; + if (0xD800 <= cp && cp < 0xDFFF) + return G_UNICODE_SURROGATE; + if (0xE000 <= cp && cp < 0xF8FF) + return G_UNICODE_PRIVATE_USE; + /* since the argument is UTF-16, we cannot check beyond FFFF */ + + /* It should match any of above */ + return 0; +} + +GUnicodeBreakType +g_unichar_break_type (gunichar c) +{ + // MOONLIGHT_FIXME + return G_UNICODE_BREAK_UNKNOWN; +} + +gunichar +g_unichar_case (gunichar c, gboolean upper) +{ + gint8 i, i2; + guint32 cp = (guint32) c, v; + + for (i = 0; i < simple_case_map_ranges_count; i++) { + if (cp < simple_case_map_ranges [i].start) + return c; + if (simple_case_map_ranges [i].end <= cp) + continue; + if (c < 0x10000) { + const guint16 *tab = upper ? simple_upper_case_mapping_lowarea [i] : simple_lower_case_mapping_lowarea [i]; + v = tab [cp - simple_case_map_ranges [i].start]; + } else { + const guint32 *tab; + i2 = (gint8)(i - (upper ? simple_upper_case_mapping_lowarea_table_count : simple_lower_case_mapping_lowarea_table_count)); + tab = upper ? simple_upper_case_mapping_higharea [i2] : simple_lower_case_mapping_higharea [i2]; + v = tab [cp - simple_case_map_ranges [i].start]; + } + return v != 0 ? (gunichar) v : c; + } + return c; +} + +gunichar +g_unichar_toupper (gunichar c) +{ + return g_unichar_case (c, TRUE); +} + +gunichar +g_unichar_tolower (gunichar c) +{ + return g_unichar_case (c, FALSE); +} + +gunichar +g_unichar_totitle (gunichar c) +{ + guint8 i; + guint32 cp; + + cp = (guint32) c; + for (i = 0; i < simple_titlecase_mapping_count; i++) { + if (simple_titlecase_mapping [i].codepoint == cp) + return simple_titlecase_mapping [i].title; + if (simple_titlecase_mapping [i].codepoint > cp) + /* it is ordered, hence no more match */ + break; + } + return g_unichar_toupper (c); +} + +gboolean +g_unichar_isxdigit (gunichar c) +{ + return (g_unichar_xdigit_value (c) != -1); + +} + +gint +g_unichar_xdigit_value (gunichar c) +{ + if (c >= 0x30 && c <= 0x39) /*0-9*/ + return (c - 0x30); + if (c >= 0x41 && c <= 0x46) /*A-F*/ + return (c - 0x37); + if (c >= 0x61 && c <= 0x66) /*a-f*/ + return (c - 0x57); + return -1; +} + +gboolean +g_unichar_isspace (gunichar c) +{ + GUnicodeType type = g_unichar_type (c); + if (type == G_UNICODE_LINE_SEPARATOR || + type == G_UNICODE_PARAGRAPH_SEPARATOR || + type == G_UNICODE_SPACE_SEPARATOR) + return TRUE; + + return FALSE; +} + + +/* + * This is broken, and assumes an UTF8 system, but will do for eglib's first user + */ +gchar * +g_filename_from_utf8 (const gchar *utf8string, gssize len, gsize *bytes_read, gsize *bytes_written, GError **error) +{ + char *res; + + if (len == -1) + len = strlen (utf8string); + + res = g_malloc (len + 1); + g_strlcpy (res, utf8string, len + 1); + return res; +} + +#ifndef G_OS_WIN32 +static gboolean is_utf8; + +gboolean +g_get_charset (G_CONST_RETURN char **charset) +{ + if (my_charset == NULL) { + /* These shouldn't be heap allocated */ +#if defined(HAVE_LOCALCHARSET_H) + my_charset = locale_charset (); +#else + my_charset = "UTF-8"; +#endif + is_utf8 = strcmp (my_charset, "UTF-8") == 0; + } + + if (charset != NULL) + *charset = my_charset; + + return is_utf8; +} +#endif /* G_OS_WIN32 */ + +gchar * +g_locale_to_utf8 (const gchar *opsysstring, gssize len, gsize *bytes_read, gsize *bytes_written, GError **error) +{ + g_get_charset (NULL); + + return g_convert (opsysstring, len, "UTF-8", my_charset, bytes_read, bytes_written, error); +} + +gchar * +g_locale_from_utf8 (const gchar *utf8string, gssize len, gsize *bytes_read, gsize *bytes_written, GError **error) +{ + g_get_charset (NULL); + + return g_convert (utf8string, len, my_charset, "UTF-8", bytes_read, bytes_written, error); +} diff --git a/unity-2019.4.24f1-mbe/mono/eglib/gutf8.c b/unity-2019.4.24f1-mbe/mono/eglib/gutf8.c new file mode 100644 index 000000000..c4c9b912e --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/gutf8.c @@ -0,0 +1,373 @@ +/* + * gutf8.c: UTF-8 conversion + * + * Author: + * Atsushi Enomoto + * + * (C) 2006 Novell, Inc. + * Copyright 2012 Xamarin Inc + */ + +#include +#include + +/* + * Index into the table below with the first byte of a UTF-8 sequence to get + * the number of bytes that are supposed to follow it to complete the sequence. + * + * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is left + * as-is for anyone who may want to do such conversion, which was allowed in + * earlier algorithms. +*/ +const guchar g_utf8_jump_table[256] = { + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, 4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1 +}; + +static gchar * +utf8_case_conv (const gchar *str, gssize len, gboolean upper) +{ + gunichar *ustr; + glong i, ulen; + gchar *utf8; + + ustr = g_utf8_to_ucs4_fast (str, (glong) len, &ulen); + for (i = 0; i < ulen; i++) + ustr[i] = upper ? g_unichar_toupper (ustr[i]) : g_unichar_tolower (ustr[i]); + utf8 = g_ucs4_to_utf8 (ustr, ulen, NULL, NULL, NULL); + g_free (ustr); + + return utf8; +} + +gchar * +g_utf8_strup (const gchar *str, gssize len) +{ + return utf8_case_conv (str, len, TRUE); +} + +gchar * +g_utf8_strdown (const gchar *str, gssize len) +{ + return utf8_case_conv (str, len, FALSE); +} + +static gboolean +utf8_validate (const unsigned char *inptr, size_t len) +{ + const unsigned char *ptr = inptr + len; + unsigned char c; + + /* Everything falls through when TRUE... */ + switch (len) { + default: + return FALSE; + case 4: + if ((c = (*--ptr)) < 0x80 || c > 0xBF) + return FALSE; + + if ((c == 0xBF || c == 0xBE) && ptr[-1] == 0xBF) { + if (ptr[-2] == 0x8F || ptr[-2] == 0x9F || + ptr[-2] == 0xAF || ptr[-2] == 0xBF) + return FALSE; + } + case 3: + if ((c = (*--ptr)) < 0x80 || c > 0xBF) + return FALSE; + case 2: + if ((c = (*--ptr)) < 0x80 || c > 0xBF) + return FALSE; + + /* no fall-through in this inner switch */ + switch (*inptr) { + case 0xE0: if (c < 0xA0) return FALSE; break; + case 0xED: if (c > 0x9F) return FALSE; break; + case 0xEF: if (c == 0xB7 && (ptr[1] > 0x8F && ptr[1] < 0xB0)) return FALSE; + if (c == 0xBF && (ptr[1] == 0xBE || ptr[1] == 0xBF)) return FALSE; + break; + case 0xF0: if (c < 0x90) return FALSE; break; + case 0xF4: if (c > 0x8F) return FALSE; break; + default: if (c < 0x80) return FALSE; break; + } + case 1: if (*inptr >= 0x80 && *inptr < 0xC2) return FALSE; + } + + if (*inptr > 0xF4) + return FALSE; + + return TRUE; +} + +/** + * g_utf8_validate: + * @str: a utf-8 encoded string + * @max_len: max number of bytes to validate (or -1 to validate the entire null-terminated string) + * @end: output parameter to mark the end of the valid input + * + * Checks @utf for being valid UTF-8. @str is assumed to be + * null-terminated. This function is not super-strict, as it will + * allow longer UTF-8 sequences than necessary. Note that Java is + * capable of producing these sequences if provoked. Also note, this + * routine checks for the 4-byte maximum size, but does not check for + * 0x10ffff maximum value. + * + * Return value: %TRUE if @str is valid or %FALSE otherwise. + **/ +gboolean +g_utf8_validate (const gchar *str, gssize max_len, const gchar **end) +{ + guchar *inptr = (guchar *) str; + gboolean valid = TRUE; + guint length, min; + gssize n = 0; + + if (max_len == 0) + return FALSE; + + if (max_len < 0) { + while (*inptr != 0) { + length = g_utf8_jump_table[*inptr]; + if (!utf8_validate (inptr, length)) { + valid = FALSE; + break; + } + + inptr += length; + } + } else { + while (n < max_len) { + if (*inptr == 0) { + /* Note: return FALSE if we encounter nul-byte + * before max_len is reached. */ + valid = FALSE; + break; + } + + length = g_utf8_jump_table[*inptr]; + min = MIN (length, max_len - n); + + if (!utf8_validate (inptr, min)) { + valid = FALSE; + break; + } + + if (min < length) { + valid = FALSE; + break; + } + + inptr += length; + n += length; + } + } + + if (end != NULL) + *end = (gchar *) inptr; + + return valid; +} + +gunichar +g_utf8_get_char_validated (const gchar *str, gssize max_len) +{ + unsigned char *inptr = (unsigned char *) str; + gunichar u = *inptr; + int n, i; + + if (max_len == 0) + return -2; + + if (u < 0x80) { + /* simple ascii case */ + return u; + } else if (u < 0xc2) { + return -1; + } else if (u < 0xe0) { + u &= 0x1f; + n = 2; + } else if (u < 0xf0) { + u &= 0x0f; + n = 3; + } else if (u < 0xf8) { + u &= 0x07; + n = 4; + } else if (u < 0xfc) { + u &= 0x03; + n = 5; + } else if (u < 0xfe) { + u &= 0x01; + n = 6; + } else { + return -1; + } + + if (max_len > 0) { + if (!utf8_validate (inptr, MIN (max_len, n))) + return -1; + + if (max_len < n) + return -2; + } else { + if (!utf8_validate (inptr, n)) + return -1; + } + + for (i = 1; i < n; i++) + u = (u << 6) | (*++inptr ^ 0x80); + + return u; +} + +glong +g_utf8_strlen (const gchar *str, gssize max_len) +{ + const guchar *inptr = (const guchar *) str; + glong clen = 0, len = 0, n; + + if (max_len == 0) + return 0; + + if (max_len < 0) { + while (*inptr) { + inptr += g_utf8_jump_table[*inptr]; + len++; + } + } else { + while (len < max_len && *inptr) { + n = g_utf8_jump_table[*inptr]; + if ((clen + n) > max_len) + break; + + inptr += n; + clen += n; + len++; + } + } + + return len; +} + +gunichar +g_utf8_get_char (const gchar *src) +{ + unsigned char *inptr = (unsigned char *) src; + gunichar u = *inptr; + int n, i; + + if (u < 0x80) { + /* simple ascii case */ + return u; + } else if (u < 0xe0) { + u &= 0x1f; + n = 2; + } else if (u < 0xf0) { + u &= 0x0f; + n = 3; + } else if (u < 0xf8) { + u &= 0x07; + n = 4; + } else if (u < 0xfc) { + u &= 0x03; + n = 5; + } else { + u &= 0x01; + n = 6; + } + + for (i = 1; i < n; i++) + u = (u << 6) | (*++inptr ^ 0x80); + + return u; +} + +gchar * +g_utf8_find_prev_char (const gchar *str, const gchar *p) +{ + while (p > str) { + p--; + if ((*p & 0xc0) != 0xb0) + return (gchar *)p; + } + return NULL; +} + +gchar * +g_utf8_prev_char (const gchar *str) +{ + const gchar *p = str; + do { + p--; + } while ((*p & 0xc0) == 0xb0); + + return (gchar *)p; +} + +gchar * +g_utf8_offset_to_pointer (const gchar *str, glong offset) +{ + const gchar *p = str; + + if (offset > 0) { + do { + p = g_utf8_next_char (p); + offset --; + } while (offset > 0); + } + else if (offset < 0) { + const gchar *jump = str; + do { + // since the minimum size of a character is 1 + // we know we can step back at least offset bytes + jump = jump + offset; + + // if we land in the middle of a character + // walk to the beginning + while ((*jump & 0xc0) == 0xb0) + jump --; + + // count how many characters we've actually walked + // by going forward + p = jump; + do { + p = g_utf8_next_char (p); + offset ++; + } while (p < jump); + + } while (offset < 0); + } + + return (gchar *)p; +} + +glong +g_utf8_pointer_to_offset (const gchar *str, const gchar *pos) +{ + const gchar *inptr, *inend; + glong offset = 0; + glong sign = 1; + + if (pos == str) + return 0; + + if (str < pos) { + inptr = str; + inend = pos; + } else { + inptr = pos; + inend = str; + sign = -1; + } + + do { + inptr = g_utf8_next_char (inptr); + offset++; + } while (inptr < inend); + + return offset * sign; +} diff --git a/unity-2019.4.24f1-mbe/mono/eglib/sort.frag.h b/unity-2019.4.24f1-mbe/mono/eglib/sort.frag.h new file mode 100644 index 000000000..6dc1950ae --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/sort.frag.h @@ -0,0 +1,208 @@ +/* + * sort.frag.h: Common implementation of linked-list sorting + * + * Author: + * Raja R Harinath (rharinath@novell.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. + * + * (C) 2006 Novell, Inc. + */ + +/* + * This code requires a typedef named 'list_node' for the list node. It + * is assumed that the list type is the type of a pointer to a list + * node, and that the node has a field named 'next' that implements to + * the linked list. No additional invariant is maintained (e.g. the + * 'prev' pointer of a doubly-linked list node is _not_ updated). Any + * invariant would require a post-processing pass to fix matters if + * necessary. + */ +typedef list_node *digit; + +/* + * The maximum possible depth of the merge tree + * = ceiling (log2 (maximum number of list nodes)) + * = ceiling (log2 (maximum possible memory size/size of each list node)) + * = number of bits in 'size_t' - floor (log2 (sizeof digit)) + * Also, each list in sort_info is at least 2 nodes long: we can reduce the depth by 1 + */ +#define FLOOR_LOG2(x) (((x)>=2) + ((x)>=4) + ((x)>=8) + ((x)>=16) + ((x)>=32) + ((x)>=64) + ((x)>=128)) +#define MAX_RANKS ((sizeof (size_t) * 8) - FLOOR_LOG2(sizeof (list_node)) - 1) + +struct sort_info +{ + int min_rank, n_ranks; + GCompareFunc func; + + /* Invariant: ranks[i] == NULL || length(ranks[i]) >= 2**(i+1) */ + list_node *ranks [MAX_RANKS]; /* ~ 128 bytes on 32bit, ~ 512 bytes on 64bit */ +}; + +static inline void +init_sort_info (struct sort_info *si, GCompareFunc func) +{ + si->min_rank = si->n_ranks = 0; + si->func = func; + /* we don't need to initialize si->ranks, since we never lookup past si->n_ranks. */ +} + +static inline list_node * +merge_lists (list_node *first, list_node *second, GCompareFunc func) +{ + /* merge the two lists */ + list_node *list = NULL; + list_node **pos = &list; + while (first && second) { + if (func (first->data, second->data) > 0) { + *pos = second; + second = second->next; + } else { + *pos = first; + first = first->next; + } + pos = &((*pos)->next); + } + *pos = first ? first : second; + return list; +} + +/* Pre-condition: upto <= si->n_ranks, list == NULL || length(list) == 1 */ +static inline list_node * +sweep_up (struct sort_info *si, list_node *list, int upto) +{ +#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 406) + /* + * GCC incorrectly thinks we're writing below si->ranks array bounds. + */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" +#endif + + int i; + for (i = si->min_rank; i < upto; ++i) { + list = merge_lists (si->ranks [i], list, si->func); + si->ranks [i] = NULL; + } + return list; + +#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 406) +#pragma GCC diagnostic pop +#endif +} + +/* + * The 'ranks' array essentially captures the recursion stack of a mergesort. + * The merge tree is built in a bottom-up manner. The control loop for + * updating the 'ranks' array is analogous to incrementing a binary integer, + * and the O(n) time for counting upto n translates to O(n) merges when + * inserting rank-0 lists. When we plug in the sizes of the lists involved in + * those merges, we get the O(n log n) time for the sort. + * + * Inserting higher-ranked lists reduce the height of the merge tree, and also + * eliminate a lot of redundant comparisons when merging two lists that would've + * been part of the same run. Adding a rank-i list is analogous to incrementing + * a binary integer by 2**i in one operation, thus sharing a similar speedup. + * + * When inserting higher-ranked lists, we choose to clear out the lower ranks + * in the interests of keeping the sort stable, but this makes analysis harder. + * Note that clearing the lower-ranked lists is O(length(list))-- thus it + * shouldn't affect the O(n log n) behaviour. IOW, inserting one rank-i list + * is equivalent to inserting 2**i rank-0 lists, thus even if we do i additional + * merges in the clearing-out (taking at most 2**i time) we are still fine. + */ + +#define stringify2(x) #x +#define stringify(x) stringify2(x) + +/* Pre-condition: 2**(rank+1) <= length(list) < 2**(rank+2) (therefore: length(list) >= 2) */ +static inline void +insert_list (struct sort_info *si, list_node* list, int rank) +{ +#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 406) + /* + * GCC incorrectly thinks we're writing below si->ranks array bounds. + */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" +#endif + + int i; + + if (rank > si->n_ranks) { + if (rank > MAX_RANKS) { + g_warning ("Rank '%d' should not exceed " stringify (MAX_RANKS), rank); + rank = MAX_RANKS; + } + list = merge_lists (sweep_up (si, NULL, si->n_ranks), list, si->func); + for (i = si->n_ranks; i < rank; ++i) + si->ranks [i] = NULL; + } else { + if (rank) + list = merge_lists (sweep_up (si, NULL, rank), list, si->func); + for (i = rank; i < si->n_ranks && si->ranks [i]; ++i) { + list = merge_lists (si->ranks [i], list, si->func); + si->ranks [i] = NULL; + } + } + + if (i == MAX_RANKS) /* Will _never_ happen: so we can just devolve into quadratic ;-) */ + --i; + if (i >= si->n_ranks) + si->n_ranks = i + 1; + si->min_rank = i; + si->ranks [i] = list; + +#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 406) +#pragma GCC diagnostic pop +#endif +} + +#undef stringify2 +#undef stringify +#undef MAX_RANKS +#undef FLOOR_LOG2 + +/* A non-recursive mergesort */ +static inline digit +do_sort (list_node* list, GCompareFunc func) +{ + struct sort_info si; + + init_sort_info (&si, func); + + while (list && list->next) { + list_node* next = list->next; + list_node* tail = next->next; + + if (func (list->data, next->data) > 0) { + next->next = list; + next = list; + list = list->next; + } + next->next = NULL; + + insert_list (&si, list, 0); + + list = tail; + } + + return sweep_up (&si, list, si.n_ranks); +} diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/.gitignore b/unity-2019.4.24f1-mbe/mono/eglib/test/.gitignore new file mode 100644 index 000000000..2d669e106 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/.gitignore @@ -0,0 +1,12 @@ +/Makefile +/Makefile.in +/.libs +/.deps +/*.lo +/*.la +/*.o +/semantic.cache +/.project +/.cproject +/test-eglib +/test-glib diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/Makefile.am b/unity-2019.4.24f1-mbe/mono/eglib/test/Makefile.am new file mode 100644 index 000000000..c669d844e --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/Makefile.am @@ -0,0 +1,44 @@ +EXTRA_DIST = UTF-8.txt UTF-16BE.txt UTF-16LE.txt UTF-32BE.txt UTF-32LE.txt + +SOURCES = \ + test.c \ + test.h \ + tests.h \ + driver.c \ + hashtable.c \ + string-util.c \ + string.c \ + slist.c \ + sizes.c \ + ptrarray.c \ + list.c \ + array.c \ + fake.c \ + path.c \ + queue.c \ + shell.c \ + spawn.c \ + timer.c \ + file.c \ + pattern.c \ + dir.c \ + markup.c \ + unicode.c \ + utf8.c \ + endian.c \ + module.c \ + memory.c + +test_eglib_SOURCES = $(SOURCES) + +test_eglib_CFLAGS = -Wall -DEGLIB_TESTS=1 -D_FORTIFY_SOURCE=2 -I$(srcdir)/../src -I../src -DDRIVER_NAME=\"EGlib\" +test_eglib_LDADD = ../src/libeglib.la $(LIBICONV) + +run-eglib: all + srcdir=`readlink -f $(srcdir)` ./test-eglib + +noinst_PROGRAMS = test-eglib + +run-both: run-eglib + +MAINTAINERCLEANFILES = Makefile.in diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/README b/unity-2019.4.24f1-mbe/mono/eglib/test/README new file mode 100644 index 000000000..7c28d03e9 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/README @@ -0,0 +1,113 @@ +EGlib Unit Testing +=============================================================================== + + 1. Writing new tests + 2. Using the test driver + +=============================================================================== +1. Writing new tests +=============================================================================== + +Tests are easy to write, but must be grouped in to logical cases. For instance, +the GPtrArray group has a number of tests that cover the entire GPtrArray +implementation. + +These logical case groups should be in a single C file, and must have +three elements: + + #include + #include "test.h" + + ... + + ... + + static Test groupname_tests [] = { + {"groupname_test1", groupname_test1}, + {"groupname_test1", groupname_test2}, + {NULL, NULL} + }; + + DEFINE_TEST_GROUP_INIT(groupname_tests_init, groupname_tests) + +A test implementation should look like: + + RESULT groupname_test1() + { + + + if(test_failed) { + return FAILED("reason: %s", "this works like printf"); + } + + return OK; /* just NULL, but OK is cute */ + } + +Once a test group is written, it needs to be added to the groups table +in tests.h: + + DEFINE_TEST_GROUP_INIT_H(groupname_tests_init) // same as in impl + + static Group test_groups [] = { + ... + {"groupname", groupname_tests_init} + ... + }; + +=============================================================================== +2. Using the test driver +=============================================================================== + +When tests are written, they are rebuilt with make. Two programs will be +built: + + test-eglib: the test driver and tests linked against eglib + test-glib: the test driver and tests linked against system glib-2.0 + +Each driver program works exactly the same. Running test-eglib will run +the tests against eglib, and test-glib against glib-2.0. + +The test driver supports a few options to allow for performance measuring: + + --help show all options and available test groups + --time time the overall run and report it, even if --quiet is set + --quiet do not print test results, useful for timing + --iterations N run all or specified test groups N times + +Run "test-eglib --help" for more details. + +Example: run the ptrarray test group 100000 times and only print the time + it took to perform all iterations + + ./test-eglib -tqi 100000 ptrarray + +Example: show single iteration of test output for two groups + + ./test-eglib ptrarray hashtable + +Example: show test output of all available groups + + ./test-eglib + +The 'test-both' script can be used to run both test-eglib and test-glib +with the same options back to back: + + $ ./test-both -tqi 100000 ptrarray + EGlib Total Time: 1.1961s + GLib Total Time: 0.955957s + +test-both also has a nice --speed-compare mode that shows comparison +information about EGlib vs GLib. It can run all tests or specific tests +with a configurable number of iterations. --speed-compare mode always runs +the drivers with -qtni + +The syntax for --speed-compare is: + + ./test-both --speed-compare [ITERATIONS] [GROUPS...] + + $ ./test-both --speed-compare Runs all tests with default iterations + $ ./test-both --speed-compare 500 Runs all tests with 500 iterations + $ ./test-both --speed-compare ptrarray Runs ptrarray test with default + iterations + + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/UTF-16BE.txt b/unity-2019.4.24f1-mbe/mono/eglib/test/UTF-16BE.txt new file mode 100644 index 000000000..b7e494cb4 Binary files /dev/null and b/unity-2019.4.24f1-mbe/mono/eglib/test/UTF-16BE.txt differ diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/UTF-16LE.txt b/unity-2019.4.24f1-mbe/mono/eglib/test/UTF-16LE.txt new file mode 100644 index 000000000..30fe7b069 Binary files /dev/null and b/unity-2019.4.24f1-mbe/mono/eglib/test/UTF-16LE.txt differ diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/UTF-32BE.txt b/unity-2019.4.24f1-mbe/mono/eglib/test/UTF-32BE.txt new file mode 100644 index 000000000..a1fd5437b Binary files /dev/null and b/unity-2019.4.24f1-mbe/mono/eglib/test/UTF-32BE.txt differ diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/UTF-32LE.txt b/unity-2019.4.24f1-mbe/mono/eglib/test/UTF-32LE.txt new file mode 100644 index 000000000..043afc992 Binary files /dev/null and b/unity-2019.4.24f1-mbe/mono/eglib/test/UTF-32LE.txt differ diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/UTF-8.txt b/unity-2019.4.24f1-mbe/mono/eglib/test/UTF-8.txt new file mode 100644 index 000000000..4363f27bd --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/UTF-8.txt @@ -0,0 +1,212 @@ + +UTF-8 encoded sample plain-text file +‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ + +Markus Kuhn [ˈmaʳkʊs kuːn] — 2002-07-25 + + +The ASCII compatible UTF-8 encoding used in this plain-text file +is defined in Unicode, ISO 10646-1, and RFC 2279. + + +Using Unicode/UTF-8, you can write in emails and source code things such as + +Mathematics and sciences: + + ∮ E⋅da = Q, n → ∞, ∑ f(i) = ∏ g(i), ⎧⎡⎛┌─────┐⎞⎤⎫ + ⎪⎢⎜│a²+b³ ⎟⎥⎪ + ∀x∈ℝ: ⌈x⌉ = −⌊−x⌋, α ∧ ¬β = ¬(¬α ∨ β), ⎪⎢⎜│───── ⎟⎥⎪ + ⎪⎢⎜⎷ c₈ ⎟⎥⎪ + ℕ ⊆ ℕ₀ ⊂ ℤ ⊂ ℚ ⊂ ℝ ⊂ ℂ, ⎨⎢⎜ ⎟⎥⎬ + ⎪⎢⎜ ∞ ⎟⎥⎪ + ⊥ < a ≠ b ≡ c ≤ d ≪ ⊤ ⇒ (⟦A⟧ ⇔ ⟪B⟫), ⎪⎢⎜ ⎲ ⎟⎥⎪ + ⎪⎢⎜ ⎳aⁱ-bⁱ⎟⎥⎪ + 2H₂ + O₂ ⇌ 2H₂O, R = 4.7 kΩ, ⌀ 200 mm ⎩⎣⎝i=1 ⎠⎦⎭ + +Linguistics and dictionaries: + + ði ıntəˈnæʃənəl fəˈnɛtık əsoʊsiˈeıʃn + Y [ˈʏpsilɔn], Yen [jɛn], Yoga [ˈjoːgɑ] + +APL: + + ((V⍳V)=⍳⍴V)/V←,V ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈ + +Nicer typography in plain text files: + + ╔══════════════════════════════════════════╗ + ║ ║ + ║ • ‘single’ and “double” quotes ║ + ║ ║ + ║ • Curly apostrophes: “We’ve been here” ║ + ║ ║ + ║ • Latin-1 apostrophe and accents: '´` ║ + ║ ║ + ║ • ‚deutsche‘ „Anführungszeichen“ ║ + ║ ║ + ║ • †, ‡, ‰, •, 3–4, —, −5/+5, ™, … ║ + ║ ║ + ║ • ASCII safety test: 1lI|, 0OD, 8B ║ + ║ ╭─────────╮ ║ + ║ • the euro symbol: │ 14.95 € │ ║ + ║ ╰─────────╯ ║ + ╚══════════════════════════════════════════╝ + +Combining characters: + + STARGΛ̊TE SG-1, a = v̇ = r̈, a⃑ ⊥ b⃑ + +Greek (in Polytonic): + + The Greek anthem: + + Σὲ γνωρίζω ἀπὸ τὴν κόψη + τοῦ σπαθιοῦ τὴν τρομερή, + σὲ γνωρίζω ἀπὸ τὴν ὄψη + ποὺ μὲ βία μετράει τὴ γῆ. + + ᾿Απ᾿ τὰ κόκκαλα βγαλμένη + τῶν ῾Ελλήνων τὰ ἱερά + καὶ σὰν πρῶτα ἀνδρειωμένη + χαῖρε, ὦ χαῖρε, ᾿Ελευθεριά! + + From a speech of Demosthenes in the 4th century BC: + + Οὐχὶ ταὐτὰ παρίσταταί μοι γιγνώσκειν, ὦ ἄνδρες ᾿Αθηναῖοι, + ὅταν τ᾿ εἰς τὰ πράγματα ἀποβλέψω καὶ ὅταν πρὸς τοὺς + λόγους οὓς ἀκούω· τοὺς μὲν γὰρ λόγους περὶ τοῦ + τιμωρήσασθαι Φίλιππον ὁρῶ γιγνομένους, τὰ δὲ πράγματ᾿ + εἰς τοῦτο προήκοντα, ὥσθ᾿ ὅπως μὴ πεισόμεθ᾿ αὐτοὶ + πρότερον κακῶς σκέψασθαι δέον. οὐδέν οὖν ἄλλο μοι δοκοῦσιν + οἱ τὰ τοιαῦτα λέγοντες ἢ τὴν ὑπόθεσιν, περὶ ἧς βουλεύεσθαι, + οὐχὶ τὴν οὖσαν παριστάντες ὑμῖν ἁμαρτάνειν. ἐγὼ δέ, ὅτι μέν + ποτ᾿ ἐξῆν τῇ πόλει καὶ τὰ αὑτῆς ἔχειν ἀσφαλῶς καὶ Φίλιππον + τιμωρήσασθαι, καὶ μάλ᾿ ἀκριβῶς οἶδα· ἐπ᾿ ἐμοῦ γάρ, οὐ πάλαι + γέγονεν ταῦτ᾿ ἀμφότερα· νῦν μέντοι πέπεισμαι τοῦθ᾿ ἱκανὸν + προλαβεῖν ἡμῖν εἶναι τὴν πρώτην, ὅπως τοὺς συμμάχους + σώσομεν. ἐὰν γὰρ τοῦτο βεβαίως ὑπάρξῃ, τότε καὶ περὶ τοῦ + τίνα τιμωρήσεταί τις καὶ ὃν τρόπον ἐξέσται σκοπεῖν· πρὶν δὲ + τὴν ἀρχὴν ὀρθῶς ὑποθέσθαι, μάταιον ἡγοῦμαι περὶ τῆς + τελευτῆς ὁντινοῦν ποιεῖσθαι λόγον. + + Δημοσθένους, Γ´ ᾿Ολυνθιακὸς + +Georgian: + + From a Unicode conference invitation: + + გთხოვთ ახლავე გაიაროთ რეგისტრაცია Unicode-ის მეათე საერთაშორისო + კონფერენციაზე დასასწრებად, რომელიც გაიმართება 10-12 მარტს, + ქ. მაინცში, გერმანიაში. კონფერენცია შეჰკრებს ერთად მსოფლიოს + ექსპერტებს ისეთ დარგებში როგორიცაა ინტერნეტი და Unicode-ი, + ინტერნაციონალიზაცია და ლოკალიზაცია, Unicode-ის გამოყენება + ოპერაციულ სისტემებსა, და გამოყენებით პროგრამებში, შრიფტებში, + ტექსტების დამუშავებასა და მრავალენოვან კომპიუტერულ სისტემებში. + +Russian: + + From a Unicode conference invitation: + + Зарегистрируйтесь сейчас на Десятую Международную Конференцию по + Unicode, которая состоится 10-12 марта 1997 года в Майнце в Германии. + Конференция соберет широкий круг экспертов по вопросам глобального + Интернета и Unicode, локализации и интернационализации, воплощению и + применению Unicode в различных операционных системах и программных + приложениях, шрифтах, верстке и многоязычных компьютерных системах. + +Thai (UCS Level 2): + + Excerpt from a poetry on The Romance of The Three Kingdoms (a Chinese + classic 'San Gua'): + + [----------------------------|------------------------] + ๏ แผ่นดินฮั่นเสื่อมโทรมแสนสังเวช พระปกเกศกองบู๊กู้ขึ้นใหม่ + สิบสองกษัตริย์ก่อนหน้าแลถัดไป สององค์ไซร้โง่เขลาเบาปัญญา + ทรงนับถือขันทีเป็นที่พึ่ง บ้านเมืองจึงวิปริตเป็นนักหนา + โฮจิ๋นเรียกทัพทั่วหัวเมืองมา หมายจะฆ่ามดชั่วตัวสำคัญ + เหมือนขับไสไล่เสือจากเคหา รับหมาป่าเข้ามาเลยอาสัญ + ฝ่ายอ้องอุ้นยุแยกให้แตกกัน ใช้สาวนั้นเป็นชนวนชื่นชวนใจ + พลันลิฉุยกุยกีกลับก่อเหตุ ช่างอาเพศจริงหนาฟ้าร้องไห้ + ต้องรบราฆ่าฟันจนบรรลัย ฤๅหาใครค้ำชูกู้บรรลังก์ ฯ + + (The above is a two-column text. If combining characters are handled + correctly, the lines of the second column should be aligned with the + | character above.) + +Ethiopian: + + Proverbs in the Amharic language: + + ሰማይ አይታረስ ንጉሥ አይከሰስ። + ብላ ካለኝ እንደአባቴ በቆመጠኝ። + ጌጥ ያለቤቱ ቁምጥና ነው። + ደሀ በሕልሙ ቅቤ ባይጠጣ ንጣት በገደለው። + የአፍ ወለምታ በቅቤ አይታሽም። + አይጥ በበላ ዳዋ ተመታ። + ሲተረጉሙ ይደረግሙ። + ቀስ በቀስ፥ ዕንቁላል በእግሩ ይሄዳል። + ድር ቢያብር አንበሳ ያስር። + ሰው እንደቤቱ እንጅ እንደ ጉረቤቱ አይተዳደርም። + እግዜር የከፈተውን ጉሮሮ ሳይዘጋው አይድርም። + የጎረቤት ሌባ፥ ቢያዩት ይስቅ ባያዩት ያጠልቅ። + ሥራ ከመፍታት ልጄን ላፋታት። + ዓባይ ማደሪያ የለው፥ ግንድ ይዞ ይዞራል። + የእስላም አገሩ መካ የአሞራ አገሩ ዋርካ። + ተንጋሎ ቢተፉ ተመልሶ ባፉ። + ወዳጅህ ማር ቢሆን ጨርስህ አትላሰው። + እግርህን በፍራሽህ ልክ ዘርጋ። + +Runes: + + ᚻᛖ ᚳᚹᚫᚦ ᚦᚫᛏ ᚻᛖ ᛒᚢᛞᛖ ᚩᚾ ᚦᚫᛗ ᛚᚪᚾᛞᛖ ᚾᚩᚱᚦᚹᛖᚪᚱᛞᚢᛗ ᚹᛁᚦ ᚦᚪ ᚹᛖᛥᚫ + + (Old English, which transcribed into Latin reads 'He cwaeth that he + bude thaem lande northweardum with tha Westsae.' and means 'He said + that he lived in the northern land near the Western Sea.') + +Braille: + + ⡌⠁⠧⠑ ⠼⠁⠒ ⡍⠜⠇⠑⠹⠰⠎ ⡣⠕⠌ + + ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠙⠑⠁⠙⠒ ⠞⠕ ⠃⠑⠛⠔ ⠺⠊⠹⠲ ⡹⠻⠑ ⠊⠎ ⠝⠕ ⠙⠳⠃⠞ + ⠱⠁⠞⠑⠧⠻ ⠁⠃⠳⠞ ⠹⠁⠞⠲ ⡹⠑ ⠗⠑⠛⠊⠌⠻ ⠕⠋ ⠙⠊⠎ ⠃⠥⠗⠊⠁⠇ ⠺⠁⠎ + ⠎⠊⠛⠝⠫ ⠃⠹ ⠹⠑ ⠊⠇⠻⠛⠹⠍⠁⠝⠂ ⠹⠑ ⠊⠇⠻⠅⠂ ⠹⠑ ⠥⠝⠙⠻⠞⠁⠅⠻⠂ + ⠁⠝⠙ ⠹⠑ ⠡⠊⠑⠋ ⠍⠳⠗⠝⠻⠲ ⡎⠊⠗⠕⠕⠛⠑ ⠎⠊⠛⠝⠫ ⠊⠞⠲ ⡁⠝⠙ + ⡎⠊⠗⠕⠕⠛⠑⠰⠎ ⠝⠁⠍⠑ ⠺⠁⠎ ⠛⠕⠕⠙ ⠥⠏⠕⠝ ⠰⡡⠁⠝⠛⠑⠂ ⠋⠕⠗ ⠁⠝⠹⠹⠔⠛ ⠙⠑ + ⠡⠕⠎⠑ ⠞⠕ ⠏⠥⠞ ⠙⠊⠎ ⠙⠁⠝⠙ ⠞⠕⠲ + + ⡕⠇⠙ ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ + + ⡍⠔⠙⠖ ⡊ ⠙⠕⠝⠰⠞ ⠍⠑⠁⠝ ⠞⠕ ⠎⠁⠹ ⠹⠁⠞ ⡊ ⠅⠝⠪⠂ ⠕⠋ ⠍⠹ + ⠪⠝ ⠅⠝⠪⠇⠫⠛⠑⠂ ⠱⠁⠞ ⠹⠻⠑ ⠊⠎ ⠏⠜⠞⠊⠊⠥⠇⠜⠇⠹ ⠙⠑⠁⠙ ⠁⠃⠳⠞ + ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ ⡊ ⠍⠊⠣⠞ ⠙⠁⠧⠑ ⠃⠑⠲ ⠔⠊⠇⠔⠫⠂ ⠍⠹⠎⠑⠇⠋⠂ ⠞⠕ + ⠗⠑⠛⠜⠙ ⠁ ⠊⠕⠋⠋⠔⠤⠝⠁⠊⠇ ⠁⠎ ⠹⠑ ⠙⠑⠁⠙⠑⠌ ⠏⠊⠑⠊⠑ ⠕⠋ ⠊⠗⠕⠝⠍⠕⠝⠛⠻⠹ + ⠔ ⠹⠑ ⠞⠗⠁⠙⠑⠲ ⡃⠥⠞ ⠹⠑ ⠺⠊⠎⠙⠕⠍ ⠕⠋ ⠳⠗ ⠁⠝⠊⠑⠌⠕⠗⠎ + ⠊⠎ ⠔ ⠹⠑ ⠎⠊⠍⠊⠇⠑⠆ ⠁⠝⠙ ⠍⠹ ⠥⠝⠙⠁⠇⠇⠪⠫ ⠙⠁⠝⠙⠎ + ⠩⠁⠇⠇ ⠝⠕⠞ ⠙⠊⠌⠥⠗⠃ ⠊⠞⠂ ⠕⠗ ⠹⠑ ⡊⠳⠝⠞⠗⠹⠰⠎ ⠙⠕⠝⠑ ⠋⠕⠗⠲ ⡹⠳ + ⠺⠊⠇⠇ ⠹⠻⠑⠋⠕⠗⠑ ⠏⠻⠍⠊⠞ ⠍⠑ ⠞⠕ ⠗⠑⠏⠑⠁⠞⠂ ⠑⠍⠏⠙⠁⠞⠊⠊⠁⠇⠇⠹⠂ ⠹⠁⠞ + ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ + + (The first couple of paragraphs of "A Christmas Carol" by Dickens) + +Compact font selection example text: + + ABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789 + abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ + –—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвгд + ∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა + +Greetings in various languages: + + Hello world, Καλημέρα κόσμε, コンニチハ + +Box drawing alignment tests: █ + ▉ + ╔══╦══╗ ┌──┬──┐ ╭──┬──╮ ╭──┬──╮ ┏━━┳━━┓ ┎┒┏┑ ╷ ╻ ┏┯┓ ┌┰┐ ▊ ╱╲╱╲╳╳╳ + ║┌─╨─┐║ │╔═╧═╗│ │╒═╪═╕│ │╓─╁─╖│ ┃┌─╂─┐┃ ┗╃╄┙ ╶┼╴╺╋╸┠┼┨ ┝╋┥ ▋ ╲╱╲╱╳╳╳ + ║│╲ ╱│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╿ │┃ ┍╅╆┓ ╵ ╹ ┗┷┛ └┸┘ ▌ ╱╲╱╲╳╳╳ + ╠╡ ╳ ╞╣ ├╢ ╟┤ ├┼─┼─┼┤ ├╫─╂─╫┤ ┣┿╾┼╼┿┫ ┕┛┖┚ ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳ + ║│╱ ╲│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╽ │┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▎ + ║└─╥─┘║ │╚═╤═╝│ │╘═╪═╛│ │╙─╀─╜│ ┃└─╂─┘┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▏ + ╚══╩══╝ └──┴──┘ ╰──┴──╯ ╰──┴──╯ ┗━━┻━━┛ ▗▄▖▛▀▜ └╌╌┘ ╎ ┗╍╍┛ ┋ ▁▂▃▄▅▆▇█ + ▝▀▘▙▄▟ diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/array.c b/unity-2019.4.24f1-mbe/mono/eglib/test/array.c new file mode 100644 index 000000000..37d5486ac --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/array.c @@ -0,0 +1,163 @@ +#include +#include +#include +#include "test.h" + +/* example from glib documentation */ +RESULT +test_array_big () +{ + GArray *garray; + gint i; + + /* We create a new array to store gint values. + We don't want it zero-terminated or cleared to 0's. */ + garray = g_array_new (FALSE, FALSE, sizeof (gint)); + for (i = 0; i < 10000; i++) + g_array_append_val (garray, i); + + for (i = 0; i < 10000; i++) + if (g_array_index (garray, gint, i) != i) + return FAILED ("array value didn't match"); + + g_array_free (garray, TRUE); + + return NULL; +} + +RESULT +test_array_index () +{ + GArray *array = g_array_new (FALSE, FALSE, sizeof (int)); + int v; + + v = 27; + g_array_append_val (array, v); + + if (27 != g_array_index (array, int, 0)) + return FAILED (""); + + g_array_free (array, TRUE); + + return NULL; +} + +RESULT +test_array_append_zero_terminated () +{ + GArray *array = g_array_new (TRUE, FALSE, sizeof (int)); + int v; + + v = 27; + g_array_append_val (array, v); + + if (27 != g_array_index (array, int, 0)) + return FAILED ("g_array_append_val failed"); + + if (0 != g_array_index (array, int, 1)) + return FAILED ("zero_terminated didn't append a zero element"); + + g_array_free (array, TRUE); + + return NULL; +} + +RESULT +test_array_append () +{ + GArray *array = g_array_new (FALSE, FALSE, sizeof (int)); + int v; + + if (0 != array->len) + return FAILED ("initial array length not zero"); + + v = 27; + + g_array_append_val (array, v); + + if (1 != array->len) + return FAILED ("array append failed"); + + g_array_free (array, TRUE); + + return NULL; +} + +RESULT +test_array_insert_val () +{ + GArray *array = g_array_new (FALSE, FALSE, sizeof (gpointer)); + gpointer ptr0, ptr1, ptr2, ptr3; + + g_array_insert_val (array, 0, array); + + if (array != g_array_index (array, gpointer, 0)) + return FAILED ("1 The value in the array is incorrect"); + + g_array_insert_val (array, 1, array); + if (array != g_array_index (array, gpointer, 1)) + return FAILED ("2 The value in the array is incorrect"); + + g_array_insert_val (array, 2, array); + if (array != g_array_index (array, gpointer, 2)) + return FAILED ("3 The value in the array is incorrect"); + + g_array_free (array, TRUE); + array = g_array_new (FALSE, FALSE, sizeof (gpointer)); + ptr0 = array; + ptr1 = array + 1; + ptr2 = array + 2; + ptr3 = array + 3; + + g_array_insert_val (array, 0, ptr0); + g_array_insert_val (array, 1, ptr1); + g_array_insert_val (array, 2, ptr2); + g_array_insert_val (array, 1, ptr3); + if (ptr0 != g_array_index (array, gpointer, 0)) + return FAILED ("4 The value in the array is incorrect"); + if (ptr3 != g_array_index (array, gpointer, 1)) + return FAILED ("5 The value in the array is incorrect"); + if (ptr1 != g_array_index (array, gpointer, 2)) + return FAILED ("6 The value in the array is incorrect"); + if (ptr2 != g_array_index (array, gpointer, 3)) + return FAILED ("7 The value in the array is incorrect"); + + g_array_free (array, TRUE); + return NULL; +} + +RESULT +test_array_remove () +{ + GArray *array = g_array_new (FALSE, FALSE, sizeof (int)); + int v[] = {30, 29, 28, 27, 26, 25}; + + g_array_append_vals (array, v, 6); + + if (6 != array->len) + return FAILED ("append_vals fail"); + + g_array_remove_index (array, 3); + + if (5 != array->len) + return FAILED ("remove_index failed to update length"); + + if (26 != g_array_index (array, int, 3)) + return FAILED ("remove_index failed to update the array"); + + g_array_free (array, TRUE); + + return NULL; +} + +static Test array_tests [] = { + {"big", test_array_big}, + {"append", test_array_append}, + {"insert_val", test_array_insert_val}, + {"index", test_array_index}, + {"remove", test_array_remove}, + {"append_zero_term", test_array_append_zero_terminated}, + {NULL, NULL} +}; + +DEFINE_TEST_GROUP_INIT(array_tests_init, array_tests) diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/dir.c b/unity-2019.4.24f1-mbe/mono/eglib/test/dir.c new file mode 100644 index 000000000..f87112172 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/dir.c @@ -0,0 +1,66 @@ +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef G_OS_UNIX +#include +#endif +#include "test.h" + +/* This test is just to be used with valgrind */ +RESULT +test_dir () +{ + GDir *dir; + GError *error; + const gchar *name; + + /* + dir = g_dir_open (NULL, 0, NULL); + */ + dir = g_dir_open ("", 0, NULL); + if (dir != NULL) + return FAILED ("1 Should be an error"); + + dir = g_dir_open ("", 9, NULL); + if (dir != NULL) + return FAILED ("2 Should be an error"); + + error = NULL; + dir = g_dir_open (".ljasdslakjd", 9, &error); + if (dir != NULL) + return FAILED ("3 opendir should fail"); + if (error == NULL) + return FAILED ("4 got no error"); + g_error_free (error); + error = NULL; + dir = g_dir_open (g_get_tmp_dir (), 9, &error); + if (dir == NULL) + return FAILED ("5 opendir should succeed"); + if (error != NULL) + return FAILED ("6 got an error"); + name = NULL; + name = g_dir_read_name (dir); + if (name == NULL) + return FAILED ("7 didn't read a file name"); + while ((name = g_dir_read_name (dir)) != NULL) { + if (strcmp (name, ".") == 0) + return FAILED (". directory found"); + if (strcmp (name, "..") == 0) + return FAILED (".. directory found"); + } + g_dir_close (dir); + return OK; +} + +static Test dir_tests [] = { + {"g_dir_*", test_dir}, + {NULL, NULL} +}; + +DEFINE_TEST_GROUP_INIT(dir_tests_init, dir_tests) + + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/driver.c b/unity-2019.4.24f1-mbe/mono/eglib/test/driver.c new file mode 100644 index 000000000..806e8b66d --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/driver.c @@ -0,0 +1,255 @@ +/* + * EGLib Unit Test Driver + * + * Author: + * Aaron Bockover (abockover@novell.com) + * + * (C) 2006 Novell, Inc. + * + * 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. + */ + +#include +#include "test.h" + +#ifndef DRIVER_EXTERNAL_TESTS +#include "tests.h" +#endif + +#include +#ifdef HAVE_GETOPT_H +#include +#endif + +#if defined(HAVE_UNISTD_H) +#include +#endif + +#ifndef DRIVER_NAME +#define DRIVER_NAME "EGlib" +#endif + +typedef struct _StringArray { + gchar **strings; + gint length; +} StringArray; + +static StringArray * +string_array_append(StringArray *array, gchar *string) +{ + if(array == NULL) { + array = g_new0(StringArray, 1); + array->length = 1; + array->strings = g_malloc(sizeof(gchar *) * 2); + } else { + array->length++; + array->strings = g_realloc(array->strings, sizeof(gchar *) + * (array->length + 1)); + } + + array->strings[array->length - 1] = string; + array->strings[array->length] = NULL; + + return array; +} + +gint global_passed = 0, global_tests = 0; + +static void +string_array_free(StringArray *array) +{ + g_free(array->strings); + g_free(array); +} + +static void print_help(char *s) +{ + gint i; + + printf("Usage: %s [OPTION]... [TESTGROUP]...\n\n", s); + printf("OPTIONS are:\n"); + printf(" -h, --help show this help\n"); + printf(" -t, --time time the tests\n"); + printf(" -i, --iterations number of times to run tests\n"); + printf(" -q, --quiet do not print test results; " + "final time always prints\n"); + printf(" -n, --no-labels print final time without labels, " + "nice for scripts\n"); + printf(" -d, --debug do not run tests, " + "debug the driver itself for valgrind\n\n"); + printf("TESTGROUPS available:\n"); + + for(i = 0; test_groups[i].name != NULL; i++) { + if(test_groups[i].handler != fake_tests_init) { + printf(" %s\n", test_groups[i].name); + } + } + + printf("\n"); +} + +#ifdef DRIVER_EXTERNAL_MAIN +gint run_tests_main(gint argc, gchar **argv) +#else +gint main(gint argc, gchar **argv) +#endif +{ + gint i, j, c, iterations = 1; + StringArray *tests_to_run = NULL; + gdouble time_start; + gboolean report_time = FALSE; + gboolean quiet = FALSE; + gboolean global_failure = FALSE; + gboolean no_final_time_labels = FALSE; + gboolean debug = FALSE; + +#if HAVE_GETOPT_H + static struct option long_options [] = { + {"help", no_argument, 0, 'h'}, + {"time", no_argument, 0, 't'}, + {"quiet", no_argument, 0, 'q'}, + {"iterations", required_argument, 0, 'i'}, + {"debug", no_argument, 0, 'd'}, + {"no-labels", no_argument, 0, 'n'}, + {0, 0, 0, 0} + }; + + while((c = getopt_long(argc, argv, "dhtqni:", long_options, NULL)) != -1) { switch(c) { + case 'h': + print_help(argv[0]); + return 1; + case 't': + report_time = TRUE; + break; + case 'i': + iterations = atoi(optarg); + break; + case 'q': + quiet = TRUE; + break; + case 'n': + no_final_time_labels = TRUE; + break; + case 'd': + debug = TRUE; + break; + } + } + + for(i = optind; i < argc; i++) { + if(argv[i][0] == '-') { + continue; + } + + tests_to_run = string_array_append(tests_to_run, argv[i]); + } +#endif + + time_start = get_timestamp(); + + for(j = 0; test_groups[j].name != NULL; j++) { + gboolean run = TRUE; + gchar *tests = NULL; + gchar *group = NULL; + + if(tests_to_run != NULL) { + gint k; + run = FALSE; + + for(k = 0; k < tests_to_run->length; k++) { + gchar *user = tests_to_run->strings[k]; + const gchar *table = test_groups[j].name; + size_t user_len = strlen(user); + size_t table_len = strlen(table); + + if(strncmp(user, table, table_len) == 0) { + if(user_len > table_len && user[table_len] != ':') { + break; + } + + run = TRUE; + group = tests_to_run->strings[k]; + break; + } + } + } + + if(run) { + gboolean passed; + gchar **split = NULL; + + if(debug && test_groups[j].handler != fake_tests_init) { + printf("Skipping %s, in driver debug mode\n", + test_groups[j].name); + continue; + } else if(!debug && test_groups[j].handler == fake_tests_init) { + continue; + } + + if(group != NULL) { + split = eg_strsplit(group, ":", -1); + if(split != NULL) { + gint m; + for(m = 0; split[m] != NULL; m++) { + if(m == 1) { + tests = strdup(split[m]); + break; + } + } + eg_strfreev(split); + } + } + + passed = run_group(&(test_groups[j]), + iterations, quiet, report_time, tests); + + if(tests != NULL) { + g_free(tests); + } + + if(!passed && !global_failure) { + global_failure = TRUE; + } + } + } + + if(!quiet) { + gdouble pass_percentage = ((gdouble)global_passed / (gdouble)global_tests) * 100.0; + printf("=============================\n"); + printf("Overall result: %s : %d / %d (%g%%)\n", global_failure ? "FAILED" : "OK", global_passed, global_tests, pass_percentage); + } + + if(report_time) { + gdouble duration = get_timestamp() - time_start; + if(no_final_time_labels) { + printf("%g\n", duration); + } else { + printf("%s Total Time: %g\n", DRIVER_NAME, duration); + } + } + + if(tests_to_run != NULL) { + string_array_free(tests_to_run); + } + + return global_tests - global_passed; +} + + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/endian.c b/unity-2019.4.24f1-mbe/mono/eglib/test/endian.c new file mode 100644 index 000000000..3347d765f --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/endian.c @@ -0,0 +1,38 @@ +#include "test.h" + +RESULT +test_swap () +{ + guint32 a = 0xabcdef01, res32; + guint64 b = (((guint64)a) << 32) | a, res64; + guint64 b_expect = (((guint64)0x1efcdab) << 32) | 0x01efcdab; + guint16 c = 0xabcd, res16; + + res32 = GUINT32_SWAP_LE_BE (a); + if (res32 != 0x01efcdab) + return FAILED ("GUINT32_SWAP_LE_BE returned 0x%x", res32); + res32 = GUINT32_SWAP_LE_BE (1); + if (res32 != 0x1000000) + return FAILED ("GUINT32_SWAP_LE_BE returned 0x%x", res32); + + res64 = GUINT64_SWAP_LE_BE(b); + if (res64 != b_expect) + return FAILED ("GUINT64_SWAP_LE_BE returned 0x%llx (had=0x%llx)", res64, b); + res16 = GUINT16_SWAP_LE_BE(c); + if (res16 != 0xcdab) + return FAILED ("GUINT16_SWAP_LE_BE returned 0x%x", (guint32) res16); + + return OK; +} + +/* + * test initialization + */ + +static Test endian_tests [] = { + {"swap", test_swap}, + {NULL, NULL} +}; + +DEFINE_TEST_GROUP_INIT(endian_tests_init, endian_tests) + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/fake.c b/unity-2019.4.24f1-mbe/mono/eglib/test/fake.c new file mode 100644 index 000000000..c8d9af611 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/fake.c @@ -0,0 +1,19 @@ +/* + * Fake test allows debugging of the driver itself + */ + +#include "test.h" + +RESULT +test_fake() +{ + return OK; +} + +static Test fake_tests [] = { + {"fake", test_fake}, + {NULL, NULL} +}; + +DEFINE_TEST_GROUP_INIT(fake_tests_init, fake_tests) + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/file.c b/unity-2019.4.24f1-mbe/mono/eglib/test/file.c new file mode 100644 index 000000000..19276c990 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/file.c @@ -0,0 +1,228 @@ +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include "test.h" + +#ifdef G_OS_WIN32 +#include +#define close _close +#endif + +RESULT +test_file_get_contents () +{ + GError *error; + gchar *content; + gboolean ret; + gsize length; +#ifdef G_OS_WIN32 + const gchar *filename = "c:\\Windows\\system.ini"; +#else + const gchar *filename = "/etc/hosts"; +#endif + + /* + filename != NULL + ret = g_file_get_contents (NULL, NULL, NULL, NULL); + contents != NULL + ret = g_file_get_contents ("", NULL, NULL, NULL); + error no such file and fails for 'error' not being null too + ret = g_file_get_contents ("", &content, NULL, &error); + */ + + error = NULL; + ret = g_file_get_contents ("", &content, NULL, &error); + if (ret) + return FAILED ("HAH!"); + if (error == NULL) + return FAILED ("Got nothing as error."); + if (content != NULL) + return FAILED ("Content is uninitialized"); + + g_error_free (error); + error = NULL; + ret = g_file_get_contents (filename, &content, &length, &error); + if (!ret) + return FAILED ("The error is %d %s\n", error->code, error->message); + if (error != NULL) + return FAILED ("Got an error returning TRUE"); + if (content == NULL) + return FAILED ("Content is NULL"); + if (strlen (content) != length) + return FAILED ("length is %d but the string is %d", length, strlen (content)); + g_free (content); + + return OK; +} + +RESULT +test_open_tmp () +{ + GError *error; + gint fd; + gchar *name = GINT_TO_POINTER (-1); + + /* + * Okay, this works, but creates a .xxx file in /tmp on every run. Disabled. + * fd = g_file_open_tmp (NULL, NULL, NULL); + * if (fd < 0) + * return FAILED ("Default failed."); + * close (fd); + */ + error = NULL; + fd = g_file_open_tmp ("invalidtemplate", NULL, &error); + if (fd != -1) + return FAILED ("The template was invalid and accepted"); + if (error == NULL) + return FAILED ("No error returned."); + g_error_free (error); + + error = NULL; + fd = g_file_open_tmp ("i/nvalidtemplate", &name, &error); + if (fd != -1) + return FAILED ("The template was invalid and accepted"); + if (error == NULL) + return FAILED ("No error returned."); + if (name == NULL) + return FAILED ("'name' is not reset"); + g_error_free (error); + + error = NULL; + fd = g_file_open_tmp ("valid-XXXXXX", &name, &error); + if (fd == -1) + return FAILED ("This should be valid"); + if (error != NULL) + return FAILED ("No error returned."); + if (name == NULL) + return FAILED ("No name returned."); + close (fd); + unlink (name); + g_free (name); + return OK; +} + +RESULT +test_file () +{ + gboolean res; + const gchar *tmp; + gchar *path; + +#ifndef G_OS_WIN32 /* FIXME */ + gchar *sympath; + gint ignored G_GNUC_UNUSED; +#endif + + res = g_file_test (NULL, 0); + if (res) + return FAILED ("Should return FALSE HERE"); + + res = g_file_test ("file.c", 0); + if (res) + return FAILED ("Should return FALSE HERE"); + + tmp = g_get_tmp_dir (); + res = g_file_test (tmp, G_FILE_TEST_EXISTS); + if (!res) + return FAILED ("tmp does not exist."); + res = g_file_test (tmp, G_FILE_TEST_IS_REGULAR); + if (res) + return FAILED ("tmp is regular"); + + res = g_file_test (tmp, G_FILE_TEST_IS_DIR); + if (!res) + return FAILED ("tmp is not a directory"); + res = g_file_test (tmp, G_FILE_TEST_IS_EXECUTABLE); + if (!res) + return FAILED ("tmp is not a executable"); + + res = g_file_test (tmp, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_SYMLINK); + if (!res) + return FAILED ("2 tmp does not exist."); + res = g_file_test (tmp, G_FILE_TEST_IS_REGULAR | G_FILE_TEST_IS_SYMLINK); + if (res) + return FAILED ("2 tmp is regular"); + + res = g_file_test (tmp, G_FILE_TEST_IS_DIR | G_FILE_TEST_IS_SYMLINK); + if (!res) + return FAILED ("2 tmp is not a directory"); + res = g_file_test (tmp, G_FILE_TEST_IS_EXECUTABLE | G_FILE_TEST_IS_SYMLINK); + if (!res) + return FAILED ("2 tmp is not a executable"); + + close (g_file_open_tmp (NULL, &path, NULL)); /* create an empty file */ + res = g_file_test (path, G_FILE_TEST_EXISTS); + if (!res) + return FAILED ("3 %s should exist", path); + res = g_file_test (path, G_FILE_TEST_IS_REGULAR); + /* This is strange. Empty file is reported as not existing! */ + if (!res) + return FAILED ("3 %s IS_REGULAR", path); + res = g_file_test (path, G_FILE_TEST_IS_DIR); + if (res) + return FAILED ("3 %s should not be a directory", path); + res = g_file_test (path, G_FILE_TEST_IS_EXECUTABLE); + if (res) + return FAILED ("3 %s should not be executable", path); + res = g_file_test (path, G_FILE_TEST_IS_SYMLINK); + if (res) + return FAILED ("3 %s should not be a symlink", path); + +#ifndef G_OS_WIN32 /* FIXME */ + sympath = g_strconcat (path, "-link", NULL); + ignored = symlink (path, sympath); + res = g_file_test (sympath, G_FILE_TEST_EXISTS); + if (!res) + return FAILED ("4 %s should not exist", sympath); + res = g_file_test (sympath, G_FILE_TEST_IS_REGULAR); + if (!res) + return FAILED ("4 %s should not be a regular file", sympath); + res = g_file_test (sympath, G_FILE_TEST_IS_DIR); + if (res) + return FAILED ("4 %s should not be a directory", sympath); + res = g_file_test (sympath, G_FILE_TEST_IS_EXECUTABLE); + if (res) + return FAILED ("4 %s should not be executable", sympath); + res = g_file_test (sympath, G_FILE_TEST_IS_SYMLINK); + if (!res) + return FAILED ("4 %s should be a symlink", sympath); + + unlink (path); + + res = g_file_test (sympath, G_FILE_TEST_EXISTS); + if (res) + return FAILED ("5 %s should exist", sympath); + res = g_file_test (sympath, G_FILE_TEST_IS_REGULAR); + if (res) + return FAILED ("5 %s should be a regular file", sympath); + res = g_file_test (sympath, G_FILE_TEST_IS_DIR); + if (res) + return FAILED ("5 %s should not be a directory", sympath); + res = g_file_test (sympath, G_FILE_TEST_IS_EXECUTABLE); + if (res) + return FAILED ("5 %s should not be executable", sympath); + res = g_file_test (sympath, G_FILE_TEST_IS_SYMLINK); + if (!res) + return FAILED ("5 %s should be a symlink", sympath); + unlink (sympath); + g_free (sympath); +#endif + g_free (path); + return OK; +} + +static Test file_tests [] = { + {"g_file_get_contents", test_file_get_contents}, + {"g_file_open_tmp", test_open_tmp}, + {"g_file_test", test_file}, + {NULL, NULL} +}; + +DEFINE_TEST_GROUP_INIT(file_tests_init, file_tests) + + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/hashtable.c b/unity-2019.4.24f1-mbe/mono/eglib/test/hashtable.c new file mode 100644 index 000000000..8eb98852f --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/hashtable.c @@ -0,0 +1,177 @@ +#include +#include +#include +#include "test.h" + +int foreach_count = 0; +int foreach_fail = 0; + +void foreach (gpointer key, gpointer value, gpointer user_data) +{ + foreach_count++; + if (GPOINTER_TO_INT (user_data) != 'a') + foreach_fail = 1; +} + +RESULT hash_t1 (void) +{ + GHashTable *t = g_hash_table_new (g_str_hash, g_str_equal); + + foreach_count = 0; + foreach_fail = 0; + g_hash_table_insert (t, "hello", "world"); + g_hash_table_insert (t, "my", "god"); + + g_hash_table_foreach (t, foreach, GINT_TO_POINTER('a')); + if (foreach_count != 2) + return FAILED ("did not find all keys, got %d expected 2", foreach_count); + if (foreach_fail) + return FAILED("failed to pass the user-data to foreach"); + + if (!g_hash_table_remove (t, "my")) + return FAILED ("did not find known key"); + if (g_hash_table_size (t) != 1) + return FAILED ("unexpected size"); + g_hash_table_insert(t, "hello", "moon"); + if (strcmp (g_hash_table_lookup (t, "hello"), "moon") != 0) + return FAILED ("did not replace world with moon"); + + if (!g_hash_table_remove (t, "hello")) + return FAILED ("did not find known key"); + if (g_hash_table_size (t) != 0) + return FAILED ("unexpected size"); + g_hash_table_destroy (t); + + return OK; +} + +RESULT hash_t2 (void) +{ + return OK; +} + +RESULT hash_default (void) +{ + GHashTable *hash = g_hash_table_new (NULL, NULL); + + if (hash == NULL) + return FAILED ("g_hash_table_new should return a valid hash"); + + g_hash_table_destroy (hash); + return NULL; +} + +RESULT +hash_null_lookup (void) +{ + GHashTable *hash = g_hash_table_new (NULL, NULL); + gpointer ok, ov; + + g_hash_table_insert (hash, NULL, GINT_TO_POINTER (1)); + g_hash_table_insert (hash, GINT_TO_POINTER(1), GINT_TO_POINTER(2)); + + if (!g_hash_table_lookup_extended (hash, NULL, &ok, &ov)) + return FAILED ("Did not find the NULL"); + if (ok != NULL) + return FAILED ("Incorrect key found"); + if (ov != GINT_TO_POINTER (1)) + return FAILED ("Got wrong value %p\n", ov); + + if (!g_hash_table_lookup_extended (hash, GINT_TO_POINTER(1), &ok, &ov)) + return FAILED ("Did not find the 1"); + if (ok != GINT_TO_POINTER(1)) + return FAILED ("Incorrect key found"); + if (ov != GINT_TO_POINTER (2)) + return FAILED ("Got wrong value %p\n", ov); + + g_hash_table_destroy (hash); + + return NULL; +} + +static void +counter (gpointer key, gpointer value, gpointer user_data) +{ + int *counter = (int *) user_data; + + (*counter)++; +} + +RESULT hash_grow (void) +{ + GHashTable *hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + int i, count = 0; + + for (i = 0; i < 1000; i++) + g_hash_table_insert (hash, g_strdup_printf ("%d", i), g_strdup_printf ("x-%d", i)); + + for (i = 0; i < 1000; i++){ + char buffer [30]; + gpointer value; + + sprintf (buffer, "%d", i); + + value = g_hash_table_lookup (hash, buffer); + sprintf (buffer, "x-%d", i); + if (strcmp (value, buffer) != 0){ + return FAILED ("Failed to lookup the key %d, the value was %s\n", i, value); + } + } + + if (g_hash_table_size (hash) != 1000) + return FAILED ("Did not find 1000 elements on the hash, found %d\n", g_hash_table_size (hash)); + + /* Now do the manual count, lets not trust the internals */ + g_hash_table_foreach (hash, counter, &count); + if (count != 1000){ + return FAILED ("Foreach count is not 1000"); + } + + g_hash_table_destroy (hash); + return NULL; +} + +RESULT hash_iter (void) +{ +#if !defined(GLIB_MAJOR_VERSION) || GLIB_CHECK_VERSION(2, 16, 0) + GHashTable *hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL); + GHashTableIter iter; + int i, sum, keys_sum, values_sum; + gpointer key, value; + + sum = 0; + for (i = 0; i < 1000; i++) { + sum += i; + g_hash_table_insert (hash, GUINT_TO_POINTER (i), GUINT_TO_POINTER (i)); + } + + keys_sum = values_sum = 0; + g_hash_table_iter_init (&iter, hash); + while (g_hash_table_iter_next (&iter, &key, &value)) { + if (key != value) + return FAILED ("key != value"); + keys_sum += GPOINTER_TO_UINT (key); + values_sum += GPOINTER_TO_UINT (value); + } + if (keys_sum != sum || values_sum != sum) + return FAILED ("Did not find all key-value pairs"); + g_hash_table_destroy (hash); + return NULL; +#else + /* GHashTableIter was added in glib 2.16 */ + return NULL; +#endif +} + +static Test hashtable_tests [] = { + {"t1", hash_t1}, + {"t2", hash_t2}, + {"grow", hash_grow}, + {"default", hash_default}, + {"null_lookup", hash_null_lookup}, + {"iter", hash_iter}, + {NULL, NULL} +}; + +DEFINE_TEST_GROUP_INIT(hashtable_tests_init, hashtable_tests) + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/list.c b/unity-2019.4.24f1-mbe/mono/eglib/test/list.c new file mode 100644 index 000000000..68dadee61 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/list.c @@ -0,0 +1,438 @@ +#include +#include +#include +#include "test.h" + +RESULT +test_list_length () +{ + GList *list = g_list_prepend (NULL, "foo"); + + if (g_list_length (list) != 1) + return FAILED ("length failed. #1"); + + list = g_list_prepend (list, "bar"); + if (g_list_length (list) != 2) + return FAILED ("length failed. #2"); + + list = g_list_append (list, "bar"); + if (g_list_length (list) != 3) + return FAILED ("length failed. #3"); + + g_list_free (list); + return NULL; +} + +RESULT +test_list_nth () +{ + char *foo = "foo"; + char *bar = "bar"; + char *baz = "baz"; + GList *nth, *list; + list = g_list_prepend (NULL, baz); + list = g_list_prepend (list, bar); + list = g_list_prepend (list, foo); + + nth = g_list_nth (list, 0); + if (nth->data != foo) + return FAILED ("nth failed. #0"); + + nth = g_list_nth (list, 1); + if (nth->data != bar) + return FAILED ("nth failed. #1"); + + nth = g_list_nth (list, 2); + if (nth->data != baz) + return FAILED ("nth failed. #2"); + + nth = g_list_nth (list, 3); + if (nth) + return FAILED ("nth failed. #3: %s", nth->data); + + g_list_free (list); + return OK; +} + +RESULT +test_list_index () +{ + int i; + char *foo = "foo"; + char *bar = "bar"; + char *baz = "baz"; + GList *list; + list = g_list_prepend (NULL, baz); + list = g_list_prepend (list, bar); + list = g_list_prepend (list, foo); + + i = g_list_index (list, foo); + if (i != 0) + return FAILED ("index failed. #0: %d", i); + + i = g_list_index (list, bar); + if (i != 1) + return FAILED ("index failed. #1: %d", i); + + i = g_list_index (list, baz); + if (i != 2) + return FAILED ("index failed. #2: %d", i); + + g_list_free (list); + return OK; +} + +RESULT +test_list_append () +{ + GList *list = g_list_prepend (NULL, "first"); + if (g_list_length (list) != 1) + return FAILED ("Prepend failed"); + + list = g_list_append (list, "second"); + + if (g_list_length (list) != 2) + return FAILED ("Append failed"); + + g_list_free (list); + return OK; +} + +RESULT +test_list_last () +{ + GList *foo = g_list_prepend (NULL, "foo"); + GList *bar = g_list_prepend (NULL, "bar"); + GList *last; + + foo = g_list_concat (foo, bar); + last = g_list_last (foo); + + if (last != bar) + return FAILED ("last failed. #1"); + + foo = g_list_concat (foo, g_list_prepend (NULL, "baz")); + foo = g_list_concat (foo, g_list_prepend (NULL, "quux")); + + last = g_list_last (foo); + if (strcmp ("quux", last->data)) + return FAILED ("last failed. #2"); + + g_list_free (foo); + + return OK; +} + +RESULT +test_list_concat () +{ + GList *foo = g_list_prepend (NULL, "foo"); + GList *bar = g_list_prepend (NULL, "bar"); + GList *list = g_list_concat (foo, bar); + + if (g_list_length (list) != 2) + return FAILED ("Concat failed. #1"); + + if (strcmp (list->data, "foo")) + return FAILED ("Concat failed. #2"); + + if (strcmp (list->next->data, "bar")) + return FAILED ("Concat failed. #3"); + + if (g_list_first (list) != foo) + return FAILED ("Concat failed. #4"); + + if (g_list_last (list) != bar) + return FAILED ("Concat failed. #5"); + + g_list_free (list); + + return OK; +} + + +static gint +compare (gconstpointer a, gconstpointer b) +{ + char *foo = (char *) a; + char *bar = (char *) b; + + if (strlen (foo) < strlen (bar)) + return -1; + + return 1; +} + +RESULT +test_list_insert_sorted () +{ + GList *list = g_list_prepend (NULL, "a"); + list = g_list_append (list, "aaa"); + + /* insert at the middle */ + list = g_list_insert_sorted (list, "aa", compare); + if (strcmp ("aa", list->next->data)) + return FAILED ("insert_sorted failed. #1"); + + /* insert at the beginning */ + list = g_list_insert_sorted (list, "", compare); + if (strcmp ("", list->data)) + return FAILED ("insert_sorted failed. #2"); + + /* insert at the end */ + list = g_list_insert_sorted (list, "aaaa", compare); + if (strcmp ("aaaa", g_list_last (list)->data)) + return FAILED ("insert_sorted failed. #3"); + + g_list_free (list); + return OK; +} + +RESULT +test_list_copy () +{ + int i, length; + GList *list, *copy; + list = g_list_prepend (NULL, "a"); + list = g_list_append (list, "aa"); + list = g_list_append (list, "aaa"); + list = g_list_append (list, "aaaa"); + + length = g_list_length (list); + copy = g_list_copy (list); + + for (i = 0; i < length; i++) + if (strcmp (g_list_nth (list, i)->data, + g_list_nth (copy, i)->data)) + return FAILED ("copy failed."); + + g_list_free (list); + g_list_free (copy); + return OK; +} + +RESULT +test_list_reverse () +{ + guint i, length; + GList *list, *reverse; + list = g_list_prepend (NULL, "a"); + list = g_list_append (list, "aa"); + list = g_list_append (list, "aaa"); + list = g_list_append (list, "aaaa"); + + length = g_list_length (list); + reverse = g_list_reverse (g_list_copy (list)); + + if (g_list_length (reverse) != length) + return FAILED ("reverse failed #1"); + + for (i = 0; i < length; i++){ + guint j = length - i - 1; + if (strcmp (g_list_nth (list, i)->data, + g_list_nth (reverse, j)->data)) + return FAILED ("reverse failed. #2"); + } + + g_list_free (list); + g_list_free (reverse); + return OK; +} + +RESULT +test_list_remove () +{ + GList *list = g_list_prepend (NULL, "three"); + char *one = "one"; + list = g_list_prepend (list, "two"); + list = g_list_prepend (list, one); + + list = g_list_remove (list, one); + + if (g_list_length (list) != 2) + return FAILED ("Remove failed"); + + if (strcmp ("two", list->data) != 0) + return FAILED ("Remove failed"); + + g_list_free (list); + return OK; +} + +RESULT +test_list_remove_link () +{ + GList *foo = g_list_prepend (NULL, "a"); + GList *bar = g_list_prepend (NULL, "b"); + GList *baz = g_list_prepend (NULL, "c"); + GList *list = foo; + + foo = g_list_concat (foo, bar); + foo = g_list_concat (foo, baz); + + list = g_list_remove_link (list, bar); + + if (g_list_length (list) != 2) + return FAILED ("remove_link failed #1"); + + if (bar->next != NULL) + return FAILED ("remove_link failed #2"); + + g_list_free (list); + g_list_free (bar); + return OK; +} + +RESULT +test_list_insert_before () +{ + GList *foo, *bar, *baz; + + foo = g_list_prepend (NULL, "foo"); + foo = g_list_insert_before (foo, NULL, "bar"); + bar = g_list_last (foo); + + if (strcmp (bar->data, "bar")) + return FAILED ("1"); + + baz = g_list_insert_before (foo, bar, "baz"); + if (foo != baz) + return FAILED ("2"); + + if (strcmp (g_list_nth_data (foo, 1), "baz")) + return FAILED ("3: %s", g_list_nth_data (foo, 1)); + + g_list_free (foo); + return OK; +} + +#define N_ELEMS 101 + +static int intcompare (gconstpointer p1, gconstpointer p2) +{ + return GPOINTER_TO_INT (p1) - GPOINTER_TO_INT (p2); +} + +static gboolean verify_sort (GList *list, int len) +{ + int prev; + + if (list->prev) + return FALSE; + + prev = GPOINTER_TO_INT (list->data); + len--; + for (list = list->next; list; list = list->next) { + int curr = GPOINTER_TO_INT (list->data); + if (prev > curr) + return FALSE; + prev = curr; + + if (!list->prev || list->prev->next != list) + return FALSE; + + if (len == 0) + return FALSE; + len--; + } + return len == 0; +} + +RESULT +test_list_sort () +{ + int i, j, mul; + GList *list = NULL; + + for (i = 0; i < N_ELEMS; ++i) + list = g_list_prepend (list, GINT_TO_POINTER (i)); + list = g_list_sort (list, intcompare); + if (!verify_sort (list, N_ELEMS)) + return FAILED ("decreasing list"); + + g_list_free (list); + + list = NULL; + for (i = 0; i < N_ELEMS; ++i) + list = g_list_prepend (list, GINT_TO_POINTER (-i)); + list = g_list_sort (list, intcompare); + if (!verify_sort (list, N_ELEMS)) + return FAILED ("increasing list"); + + g_list_free (list); + + list = g_list_prepend (NULL, GINT_TO_POINTER (0)); + for (i = 1; i < N_ELEMS; ++i) { + list = g_list_prepend (list, GINT_TO_POINTER (i)); + list = g_list_prepend (list, GINT_TO_POINTER (-i)); + } + list = g_list_sort (list, intcompare); + if (!verify_sort (list, 2*N_ELEMS-1)) + return FAILED ("alternating list"); + + g_list_free (list); + + list = NULL; + mul = 1; + for (i = 1; i < N_ELEMS; ++i) { + mul = -mul; + for (j = 0; j < i; ++j) + list = g_list_prepend (list, GINT_TO_POINTER (mul * j)); + } + list = g_list_sort (list, intcompare); + if (!verify_sort (list, (N_ELEMS*N_ELEMS - N_ELEMS)/2)) + return FAILED ("wavering list"); + + g_list_free (list); + + return OK; +} + +static gint +find_custom (gconstpointer a, gconstpointer b) +{ + return(strcmp (a, b)); +} + +RESULT +test_list_find_custom () +{ + GList *list = NULL, *found; + char *foo = "foo"; + char *bar = "bar"; + char *baz = "baz"; + + list = g_list_prepend (list, baz); + list = g_list_prepend (list, bar); + list = g_list_prepend (list, foo); + + found = g_list_find_custom (list, baz, find_custom); + + if (found == NULL) + return FAILED ("Find failed"); + + g_list_free (list); + + return OK; +} + +static Test list_tests [] = { + { "length", test_list_length}, + { "nth", test_list_nth}, + { "index", test_list_index}, + { "last", test_list_last}, + { "append", test_list_append}, + { "concat", test_list_concat}, + {"insert_sorted", test_list_insert_sorted}, + {"insert_before", test_list_insert_before}, + { "copy", test_list_copy}, + { "reverse", test_list_reverse}, + { "remove", test_list_remove}, + { "remove_link", test_list_remove_link}, + { "remove_link", test_list_remove_link}, + { "sort", test_list_sort}, + { "find_custom", test_list_find_custom}, + {NULL, NULL} +}; + +DEFINE_TEST_GROUP_INIT(list_tests_init, list_tests) diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/markup.c b/unity-2019.4.24f1-mbe/mono/eglib/test/markup.c new file mode 100644 index 000000000..cf8d3f2d5 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/markup.c @@ -0,0 +1,234 @@ +#include +#include +#include +#include "test.h" + +#define do_bad_test(s) do { char *r = markup_test (s); if (r == NULL) return FAILED ("Failed on test " # s); else g_free (r); } while (0) +#define do_ok_test(s) do { char *r = markup_test (s); if (r != NULL) return FAILED ("Could not parse valid " # s); } while (0) + +static char * +markup_test (const char *s) +{ + GMarkupParser *parser = g_new0 (GMarkupParser, 1); + GMarkupParseContext *context; + GError *error = NULL; + + context = g_markup_parse_context_new (parser, 0, 0, 0); + + g_markup_parse_context_parse (context, s, strlen (s), &error); + g_markup_parse_context_free (context); + + if (error != NULL){ + char *msg = g_strdup (error->message); + g_error_free (error); + + g_free (parser); + return msg; + } + g_free (parser); + return NULL; +} + +RESULT +invalid_documents (void) +{ + /* These should fail */ + do_bad_test ("<1>"); + do_bad_test (""); + do_bad_test (""); + do_bad_test (""); + do_bad_test (""); + + return OK; +} + +RESULT +valid_documents (void) +{ + /* These should fail */ + do_ok_test (""); + do_ok_test (""); + + return OK; +} + +/* + * This is a test for the kind of files that the code in mono/domain.c + * parses; This code comes from Mono + */ +typedef struct { + GSList *supported_runtimes; + char *required_runtime; + int configuration_count; + int startup_count; +} AppConfigInfo; + +static char * +get_attribute_value (const gchar **attribute_names, + const gchar **attribute_values, + const char *att_name) +{ + int n; + for (n=0; attribute_names[n] != NULL; n++) { + if (strcmp (attribute_names[n], att_name) == 0) + return g_strdup (attribute_values[n]); + } + return NULL; +} + +static void +start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error) +{ + AppConfigInfo* app_config = (AppConfigInfo*) user_data; + + if (strcmp (element_name, "configuration") == 0) { + app_config->configuration_count++; + return; + } + if (strcmp (element_name, "startup") == 0) { + app_config->startup_count++; + return; + } + + if (app_config->configuration_count != 1 || app_config->startup_count != 1) + return; + + if (strcmp (element_name, "requiredRuntime") == 0) { + app_config->required_runtime = get_attribute_value (attribute_names, attribute_values, "version"); + } else if (strcmp (element_name, "supportedRuntime") == 0) { + char *version = get_attribute_value (attribute_names, attribute_values, "version"); + app_config->supported_runtimes = g_slist_append (app_config->supported_runtimes, version); + } +} + +static void +end_element (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error) +{ + AppConfigInfo* app_config = (AppConfigInfo*) user_data; + + if (strcmp (element_name, "configuration") == 0) { + app_config->configuration_count--; + } else if (strcmp (element_name, "startup") == 0) { + app_config->startup_count--; + } +} + +static const GMarkupParser +mono_parser = { + start_element, + end_element, + NULL, + NULL, + NULL +}; + +AppConfigInfo * +domain_test (char *text) +{ + AppConfigInfo *app_config = g_new0 (AppConfigInfo, 1); + GMarkupParseContext *context; + + context = g_markup_parse_context_new (&mono_parser, 0, app_config, NULL); + if (g_markup_parse_context_parse (context, text, strlen (text), NULL)) { + g_markup_parse_context_end_parse (context, NULL); + } + g_markup_parse_context_free (context); + + return app_config; +} + +void +domain_free (AppConfigInfo *info) +{ + GSList *l; + if (info->required_runtime) + g_free (info->required_runtime); + for (l = info->supported_runtimes; l != NULL; l = l->next){ + g_free (l->data); + } + g_slist_free (info->supported_runtimes); + g_free (info); +} + +RESULT +mono_domain (void) +{ + AppConfigInfo *info; + + info = domain_test (""); + if (info->required_runtime == NULL) + return FAILED ("No required runtime section"); + if (strcmp (info->required_runtime, "v1") != 0) + return FAILED ("Got a runtime version %s, expected v1", info->required_runtime); + domain_free (info); + + info = domain_test (""); + if (info->required_runtime == NULL) + return FAILED ("No required runtime section on auto-close section"); + if (strcmp (info->required_runtime, "v1") != 0) + return FAILED ("Got a runtime version %s, expected v1", info->required_runtime); + domain_free (info); + + info = domain_test (""); + if ((strcmp ((char*)info->supported_runtimes->data, "v1") == 0)){ + if (info->supported_runtimes->next == NULL) + return FAILED ("Expected 2 supported runtimes"); + + if ((strcmp ((char*)info->supported_runtimes->next->data, "v2") != 0)) + return FAILED ("Expected v1, v2, got %s", info->supported_runtimes->next->data); + if (info->supported_runtimes->next->next != NULL) + return FAILED ("Expected v1, v2, got more"); + } else + return FAILED ("Expected `v1', got %s", info->supported_runtimes->data); + domain_free (info); + + return NULL; +} + +RESULT +mcs_config (void) +{ + return markup_test ("\r\n \r\n \r\n \r\n "); + +} + +RESULT +xml_parse (void) +{ + return markup_test (""); +} + +RESULT +machine_config (void) +{ + char *data; + gsize size; + + if (g_file_get_contents ("../../data/net_1_1/machine.config", &data, &size, NULL)){ + return markup_test (data); + } + printf ("Ignoring this test\n"); + return NULL; +} + +static Test markup_tests [] = { + {"invalid_documents", invalid_documents}, + {"good_documents", valid_documents}, + {"mono_domain", mono_domain}, + {"mcs_config", mcs_config}, + {"xml_parse", xml_parse}, + {"machine_config", machine_config}, + {NULL, NULL} +}; + +DEFINE_TEST_GROUP_INIT(markup_tests_init, markup_tests) + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/memory.c b/unity-2019.4.24f1-mbe/mono/eglib/test/memory.c new file mode 100644 index 000000000..db7db0809 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/memory.c @@ -0,0 +1,40 @@ + +#include +#include "test.h" + +RESULT +test_memory_zero_size_allocations () +{ + gpointer p; + + p = g_malloc (0); + if (p) + return FAILED ("Calling g_malloc with size zero should return NULL."); + + p = g_malloc0 (0); + if (p) + return FAILED ("Calling g_malloc0 with size zero should return NULL."); + + p = g_realloc (NULL, 0); + if (p) + return FAILED ("Calling g_realloc with size zero should return NULL."); + + p = g_new (int, 0); + if (p) + return FAILED ("Calling g_new with size zero should return NULL."); + + p = g_new0 (int, 0); + if (p) + return FAILED ("Calling g_new0 with size zero should return NULL."); + + return OK; +} + + +static Test memory_tests [] = { + { "zero_size_allocations", test_memory_zero_size_allocations}, + {NULL, NULL} +}; + +DEFINE_TEST_GROUP_INIT(memory_tests_init, memory_tests) + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/module.c b/unity-2019.4.24f1-mbe/mono/eglib/test/module.c new file mode 100644 index 000000000..a28363785 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/module.c @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include "test.h" + +#if defined (G_OS_WIN32) +#define EXTERNAL_SYMBOL "GetProcAddress" +#else +#define EXTERNAL_SYMBOL "system" +#endif + +void G_MODULE_EXPORT +dummy_test_export () +{ +} + +/* test for g_module_open (NULL, ...) */ +RESULT +test_module_symbol_null () +{ + gpointer proc = GINT_TO_POINTER (42); + + GModule *m = g_module_open (NULL, G_MODULE_BIND_LAZY); + + if (m == NULL) + return FAILED ("bind to main module failed. #0"); + + if (g_module_symbol (m, "__unlikely_\nexistent__", &proc)) + return FAILED ("non-existent symbol lookup failed. #1"); + + if (proc) + return FAILED ("non-existent symbol lookup failed. #2"); + + if (!g_module_symbol (m, EXTERNAL_SYMBOL, &proc)) + return FAILED ("external lookup failed. #3"); + + if (!proc) + return FAILED ("external lookup failed. #4"); + + if (!g_module_symbol (m, "dummy_test_export", &proc)) + return FAILED ("in-proc lookup failed. #5"); + + if (!proc) + return FAILED ("in-proc lookup failed. #6"); + + if (!g_module_close (m)) + return FAILED ("close failed. #7"); + + return OK; +} + +static Test module_tests [] = { + {"g_module_symbol_null", test_module_symbol_null}, + {NULL, NULL} +}; + +DEFINE_TEST_GROUP_INIT(module_tests_init, module_tests) + + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/path.c b/unity-2019.4.24f1-mbe/mono/eglib/test/path.c new file mode 100644 index 000000000..e3832a04b --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/path.c @@ -0,0 +1,357 @@ +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef G_OS_UNIX +#include +#endif +#include "test.h" + +#ifdef G_OS_WIN32 +#include +#define chdir _chdir +#endif + +/* This test is just to be used with valgrind */ +RESULT +test_buildpath () +{ + char *s; + char *buffer = "var/private"; + char *dir = "/"; + + s = g_build_path ("/", "hola///", "//mundo", NULL); + if (strcmp (s, "hola/mundo") != 0) + return FAILED ("1 Got wrong result, got: %s", s); + g_free (s); + + s = g_build_path ("/", "hola/", "/mundo", NULL); + if (strcmp (s, "hola/mundo") != 0) + return FAILED ("2 Got wrong result, got: %s", s); + g_free (s); + + s = g_build_path ("/", "hola/", "mundo", NULL); + if (strcmp (s, "hola/mundo") != 0) + return FAILED ("3 Got wrong result, got: %s", s); + g_free (s); + + s = g_build_path ("/", "hola", "/mundo", NULL); + if (strcmp (s, "hola/mundo") != 0) + return FAILED ("4 Got wrong result, got: %s", s); + g_free (s); + + s = g_build_path ("/", "/hello", "world/", NULL); + if (strcmp (s, "/hello/world/") != 0) + return FAILED ("5 Got wrong result, got: %s", s); + g_free (s); + + /* Now test multi-char-separators */ + s = g_build_path ("**", "hello", "world", NULL); + if (strcmp (s, "hello**world") != 0) + return FAILED ("6 Got wrong result, got: %s", s); + g_free (s); + + s = g_build_path ("**", "hello**", "world", NULL); + if (strcmp (s, "hello**world") != 0) + return FAILED ("7 Got wrong result, got: %s", s); + g_free (s); + + s = g_build_path ("**", "hello**", "**world", NULL); + if (strcmp (s, "hello**world") != 0) + return FAILED ("8 Got wrong result, got: %s", s); + g_free (s); + + s = g_build_path ("**", "hello**", "**world", NULL); + if (strcmp (s, "hello**world") != 0) + return FAILED ("9 Got wrong result, got: %s", s); + g_free (s); + + s = g_build_path ("1234567890", "hello", "world", NULL); + if (strcmp (s, "hello1234567890world") != 0) + return FAILED ("10 Got wrong result, got: %s", s); + g_free (s); + + s = g_build_path ("1234567890", "hello1234567890", "1234567890world", NULL); + if (strcmp (s, "hello1234567890world") != 0) + return FAILED ("11 Got wrong result, got: %s", s); + g_free (s); + + s = g_build_path ("1234567890", "hello12345678901234567890", "1234567890world", NULL); + if (strcmp (s, "hello1234567890world") != 0) + return FAILED ("12 Got wrong result, got: %s", s); + g_free (s); + + /* Multiple */ + s = g_build_path ("/", "a", "b", "c", "d", NULL); + if (strcmp (s, "a/b/c/d") != 0) + return FAILED ("13 Got wrong result, got: %s", s); + g_free (s); + + s = g_build_path ("/", "/a", "", "/c/", NULL); + if (strcmp (s, "/a/c/") != 0) + return FAILED ("14 Got wrong result, got: %s", s); + g_free (s); + + /* Null */ + s = g_build_path ("/", NULL, NULL); + if (s == NULL) + return FAILED ("must get a non-NULL return"); + if (s [0] != 0) + return FAILED ("must get an empty string"); + + // This is to test the regression introduced by Levi for the Windows support + // that code errouneously read below the allowed area (in this case dir [-1]). + // and caused all kinds of random errors. + dir = "//"; + dir++; + s = g_build_filename (dir, buffer, NULL); + if (s [0] != '/') + return FAILED ("Must have a '/' at the start"); + + g_free (s); + return OK; +} + +RESULT +test_buildfname () +{ + char *s; + + s = g_build_filename ("a", "b", "c", "d", NULL); +#ifdef G_OS_WIN32 + if (strcmp (s, "a\\b\\c\\d") != 0) +#else + if (strcmp (s, "a/b/c/d") != 0) +#endif + return FAILED ("1 Got wrong result, got: %s", s); + g_free (s); + +#ifdef G_OS_WIN32 + s = g_build_filename ("C:\\", "a", NULL); + if (strcmp (s, "C:\\a") != 0) +#else + s = g_build_filename ("/", "a", NULL); + if (strcmp (s, "/a") != 0) +#endif + return FAILED ("1 Got wrong result, got: %s", s); + +#ifndef G_OS_WIN32 + s = g_build_filename ("/", "foo", "/bar", "tolo/", "/meo/", NULL); + if (strcmp (s, "/foo/bar/tolo/meo/") != 0) + return FAILED ("1 Got wrong result, got: %s", s); +#endif + + return OK; +} + +char * +test_dirname () +{ + char *s; + +#ifdef G_OS_WIN32 + s = g_path_get_dirname ("c:\\home\\miguel"); + if (strcmp (s, "c:\\home") != 0) + return FAILED ("Expected c:\\home, got %s", s); + g_free (s); + + s = g_path_get_dirname ("c:/home/miguel"); + if (strcmp (s, "c:/home") != 0) + return FAILED ("Expected c:/home, got %s", s); + g_free (s); + + s = g_path_get_dirname ("c:\\home\\dingus\\"); + if (strcmp (s, "c:\\home\\dingus") != 0) + return FAILED ("Expected c:\\home\\dingus, got %s", s); + g_free (s); + + s = g_path_get_dirname ("dir.c"); + if (strcmp (s, ".") != 0) + return FAILED ("Expected `.', got %s", s); + g_free (s); + + s = g_path_get_dirname ("c:\\index.html"); + if (strcmp (s, "c:") != 0) + return FAILED ("Expected [c:], got [%s]", s); +#else + s = g_path_get_dirname ("/home/miguel"); + if (strcmp (s, "/home") != 0) + return FAILED ("Expected /home, got %s", s); + g_free (s); + + s = g_path_get_dirname ("/home/dingus/"); + if (strcmp (s, "/home/dingus") != 0) + return FAILED ("Expected /home/dingus, got %s", s); + g_free (s); + + s = g_path_get_dirname ("dir.c"); + if (strcmp (s, ".") != 0) + return FAILED ("Expected `.', got %s", s); + g_free (s); + + s = g_path_get_dirname ("/index.html"); + if (strcmp (s, "/") != 0) + return FAILED ("Expected [/], got [%s]", s); +#endif + return OK; +} + +char * +test_basename () +{ + char *s; + +#ifdef G_OS_WIN32 + s = g_path_get_basename (""); + if (strcmp (s, ".") != 0) + return FAILED ("Expected `.', got %s", s); + g_free (s); + + s = g_path_get_basename ("c:\\home\\dingus\\"); + if (strcmp (s, "dingus") != 0) + return FAILED ("1 Expected dingus, got %s", s); + g_free (s); + + s = g_path_get_basename ("c:/home/dingus/"); + if (strcmp (s, "dingus") != 0) + return FAILED ("1 Expected dingus, got %s", s); + g_free (s); + + s = g_path_get_basename ("c:\\home\\dingus"); + if (strcmp (s, "dingus") != 0) + return FAILED ("2 Expected dingus, got %s", s); + g_free (s); + + s = g_path_get_basename ("c:/home/dingus"); + if (strcmp (s, "dingus") != 0) + return FAILED ("2 Expected dingus, got %s", s); + g_free (s); +#else + s = g_path_get_basename (""); + if (strcmp (s, ".") != 0) + return FAILED ("Expected `.', got %s", s); + g_free (s); + + s = g_path_get_basename ("/home/dingus/"); + if (strcmp (s, "dingus") != 0) + return FAILED ("1 Expected dingus, got %s", s); + g_free (s); + + s = g_path_get_basename ("/home/dingus"); + if (strcmp (s, "dingus") != 0) + return FAILED ("2 Expected dingus, got %s", s); + g_free (s); +#endif + return OK; +} + +gchar * +test_ppath () +{ + char *s; +#ifdef G_OS_WIN32 + const gchar *searchfor = "explorer.exe"; +#else + const gchar *searchfor = "ls"; +#endif + s = g_find_program_in_path (searchfor); + if (s == NULL) + return FAILED ("No %s on this system?", searchfor); + g_free (s); + return OK; +} + +gchar * +test_ppath2 () +{ + char *s; + const char *path = g_getenv ("PATH"); +#ifdef G_OS_WIN32 + const gchar *searchfor = "test_eglib.exe"; +#else + const gchar *searchfor = "test-glib"; +#endif + + g_setenv ("PATH", "", TRUE); + s = g_find_program_in_path ("ls"); + if (s != NULL) { + g_setenv ("PATH", path, TRUE); + return FAILED ("Found something interesting here: %s", s); + } + g_free (s); + s = g_find_program_in_path (searchfor); + if (s == NULL) { + g_setenv ("PATH", path, TRUE); + return FAILED ("It should find '%s' in the current directory.", searchfor); + } + g_free (s); + g_setenv ("PATH", path, TRUE); + return OK; +} + +#ifndef DISABLE_FILESYSTEM_TESTS +gchar * +test_cwd () +{ + char *dir = g_get_current_dir (); +#ifdef G_OS_WIN32 + const gchar *newdir = "C:\\Windows"; +#else + const gchar *newdir = "/bin"; +#endif + + if (dir == NULL) + return FAILED ("No current directory?"); + g_free (dir); + + if (chdir (newdir) == -1) + return FAILED ("No %s?", newdir); + + dir = g_get_current_dir (); + if (strcmp (dir, newdir) != 0) + return FAILED("Did not go to %s?", newdir); + g_free (dir); + + return OK; +} +#else +gchar * +test_cwd () +{ + return OK; +} +#endif + +gchar * +test_misc () +{ + const char *home = g_get_home_dir (); + const char *tmp = g_get_tmp_dir (); + + if (home == NULL) + return FAILED ("Where did my home go?"); + + if (tmp == NULL) + return FAILED ("Where did my /tmp go?"); + + return OK; +} + +static Test path_tests [] = { + {"g_build_filename", test_buildfname}, + {"g_buildpath", test_buildpath}, + {"g_path_get_dirname", test_dirname}, + {"g_path_get_basename", test_basename}, + {"g_find_program_in_path", test_ppath}, + {"g_find_program_in_path2", test_ppath2}, + {"test_cwd", test_cwd }, + {"test_misc", test_misc }, + {NULL, NULL} +}; + +DEFINE_TEST_GROUP_INIT(path_tests_init, path_tests) + + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/pattern.c b/unity-2019.4.24f1-mbe/mono/eglib/test/pattern.c new file mode 100644 index 000000000..7db5a7be3 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/pattern.c @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include "test.h" + +#define MATCH(pat,string,error_if,msg) \ + spec = g_pattern_spec_new (pat); \ + res = g_pattern_match_string (spec, string); \ + if (res == error_if) \ + return FAILED (msg " returned %s", res ? "TRUE" : "FALSE"); \ + g_pattern_spec_free (spec); + +#define TEST_MATCH(pat,string,n) MATCH (pat, string, FALSE, "MATCH " #n) +#define TEST_NO_MATCH(pat,string,n) MATCH (pat, string,TRUE, "NO_MATCH " #n) + +RESULT +test_pattern_spec () +{ + GPatternSpec *spec; + gboolean res; + + /* spec = g_pattern_spec_new (NULL); */ + TEST_MATCH ("*", "hola", 1); + TEST_MATCH ("hola", "hola", 2); + TEST_MATCH ("????", "hola", 3); + TEST_MATCH ("???a", "hola", 4); + TEST_MATCH ("h??a", "hola", 5); + TEST_MATCH ("h??*", "hola", 6); + TEST_MATCH ("h*", "hola", 7); + TEST_MATCH ("*hola", "hola", 8); + TEST_MATCH ("*l*", "hola", 9); + TEST_MATCH ("h*??", "hola", 10); + TEST_MATCH ("h*???", "hola", 11); + TEST_MATCH ("?o??", "hola", 12); + TEST_MATCH ("*h*o*l*a*", "hola", 13); + TEST_MATCH ("h*o*l*a", "hola", 14); + TEST_MATCH ("h?*?", "hola", 15); + + TEST_NO_MATCH ("", "hola", 1); + TEST_NO_MATCH ("?????", "hola", 2); + TEST_NO_MATCH ("???", "hola", 3); + TEST_NO_MATCH ("*o", "hola", 4); + TEST_NO_MATCH ("h", "hola", 5); + TEST_NO_MATCH ("h*????", "hola", 6); + + return OK; +} + +static Test pattern_tests [] = { + {"g_pattern_spec*", test_pattern_spec}, + {NULL, NULL} +}; + +DEFINE_TEST_GROUP_INIT(pattern_tests_init, pattern_tests) + + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/ptrarray.c b/unity-2019.4.24f1-mbe/mono/eglib/test/ptrarray.c new file mode 100644 index 000000000..34d78b023 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/ptrarray.c @@ -0,0 +1,349 @@ +#include +#include +#include "test.h" + +/* Redefine the private structure only to verify proper allocations */ +typedef struct _GPtrArrayPriv { + gpointer *pdata; + guint len; + guint size; +} GPtrArrayPriv; + +/* Don't add more than 32 items to this please */ +static const char *items [] = { + "Apples", "Oranges", "Plumbs", "Goats", "Snorps", "Grapes", + "Tickle", "Place", "Coffee", "Cookies", "Cake", "Cheese", + "Tseng", "Holiday", "Avenue", "Smashing", "Water", "Toilet", + NULL +}; + +static GPtrArray *ptrarray_alloc_and_fill(guint *item_count) +{ + GPtrArray *array = g_ptr_array_new(); + gint i; + + for(i = 0; items[i] != NULL; i++) { + g_ptr_array_add(array, (gpointer)items[i]); + } + + if(item_count != NULL) { + *item_count = i; + } + + return array; +} + +static guint guess_size(guint length) +{ + guint size = 1; + + while(size < length) { + size <<= 1; + } + + return size; +} + +RESULT ptrarray_alloc() +{ + GPtrArrayPriv *array; + guint i; + + array = (GPtrArrayPriv *)ptrarray_alloc_and_fill(&i); + + if(array->size != guess_size(array->len)) { + return FAILED("Size should be %d, but it is %d", + guess_size(array->len), array->size); + } + + if(array->len != i) { + return FAILED("Expected %d node(s) in the array", i); + } + + g_ptr_array_free((GPtrArray *)array, TRUE); + + return OK; +} + +RESULT ptrarray_for_iterate() +{ + GPtrArray *array = ptrarray_alloc_and_fill(NULL); + guint i; + + for(i = 0; i < array->len; i++) { + char *item = (char *)g_ptr_array_index(array, i); + if(item != items[i]) { + return FAILED( + "Expected item at %d to be %s, but it was %s", + i, items[i], item); + } + } + + g_ptr_array_free(array, TRUE); + + return OK; +} + +static gint foreach_iterate_index = 0; +static char *foreach_iterate_error = NULL; + +void foreach_callback(gpointer data, gpointer user_data) +{ + char *item = (char *)data; + const char *item_cmp = items[foreach_iterate_index++]; + + if(foreach_iterate_error != NULL) { + return; + } + + if(item != item_cmp) { + foreach_iterate_error = FAILED( + "Expected item at %d to be %s, but it was %s", + foreach_iterate_index - 1, item_cmp, item); + } +} + +RESULT ptrarray_foreach_iterate() +{ + GPtrArray *array = ptrarray_alloc_and_fill(NULL); + + foreach_iterate_index = 0; + foreach_iterate_error = NULL; + + g_ptr_array_foreach(array, foreach_callback, array); + + g_ptr_array_free(array, TRUE); + + return foreach_iterate_error; +} + +RESULT ptrarray_set_size() +{ + GPtrArray *array = g_ptr_array_new(); + guint i, grow_length = 50; + + g_ptr_array_add(array, (gpointer)items[0]); + g_ptr_array_add(array, (gpointer)items[1]); + g_ptr_array_set_size(array, grow_length); + + if(array->len != grow_length) { + return FAILED("Array length should be 50, it is %d", array->len); + } else if(array->pdata[0] != items[0]) { + return FAILED("Item 0 was overwritten, should be %s", items[0]); + } else if(array->pdata[1] != items[1]) { + return FAILED("Item 1 was overwritten, should be %s", items[1]); + } + + for(i = 2; i < array->len; i++) { + if(array->pdata[i] != NULL) { + return FAILED("Item %d is not NULL, it is %p", i, array->pdata[i]); + } + } + + g_ptr_array_free(array, TRUE); + + return OK; +} + +RESULT ptrarray_remove_index() +{ + GPtrArray *array; + guint i; + + array = ptrarray_alloc_and_fill(&i); + + g_ptr_array_remove_index(array, 0); + if(array->pdata[0] != items[1]) { + return FAILED("First item is not %s, it is %s", items[1], + array->pdata[0]); + } + + g_ptr_array_remove_index(array, array->len - 1); + + if(array->pdata[array->len - 1] != items[array->len]) { + return FAILED("Last item is not %s, it is %s", + items[array->len - 2], array->pdata[array->len - 1]); + } + + g_ptr_array_free(array, TRUE); + + return OK; +} + +RESULT ptrarray_remove_index_fast() +{ + GPtrArray *array; + guint i; + + array = ptrarray_alloc_and_fill(&i); + + g_ptr_array_remove_index_fast(array, 0); + if(array->pdata[0] != items[array->len]) { + return FAILED("First item is not %s, it is %s", items[array->len], + array->pdata[0]); + } + + g_ptr_array_remove_index_fast(array, array->len - 1); + if(array->pdata[array->len - 1] != items[array->len - 1]) { + return FAILED("Last item is not %s, it is %s", + items[array->len - 1], array->pdata[array->len - 1]); + } + + g_ptr_array_free(array, TRUE); + + return OK; +} + +RESULT ptrarray_remove() +{ + GPtrArray *array; + guint i; + + array = ptrarray_alloc_and_fill(&i); + + g_ptr_array_remove(array, (gpointer)items[7]); + + if(!g_ptr_array_remove(array, (gpointer)items[4])) { + return FAILED("Item %s not removed", items[4]); + } + + if(g_ptr_array_remove(array, (gpointer)items[4])) { + return FAILED("Item %s still in array after removal", items[4]); + } + + if(array->pdata[array->len - 1] != items[array->len + 1]) { + return FAILED("Last item in GPtrArray not correct"); + } + + g_ptr_array_free(array, TRUE); + + return OK; +} + +static gint ptrarray_sort_compare(gconstpointer a, gconstpointer b) +{ + gchar *stra = *(gchar **) a; + gchar *strb = *(gchar **) b; + return strcmp(stra, strb); +} + +RESULT ptrarray_sort() +{ + GPtrArray *array = g_ptr_array_new(); + guint i; + gchar *letters [] = { "A", "B", "C", "D", "E" }; + + g_ptr_array_add(array, letters[0]); + g_ptr_array_add(array, letters[1]); + g_ptr_array_add(array, letters[2]); + g_ptr_array_add(array, letters[3]); + g_ptr_array_add(array, letters[4]); + + g_ptr_array_sort(array, ptrarray_sort_compare); + + for(i = 0; i < array->len; i++) { + if(array->pdata[i] != letters[i]) { + return FAILED("Array out of order, expected %s got %s at position %d", + letters [i], (gchar *) array->pdata [i], i); + } + } + + g_ptr_array_free(array, TRUE); + + return OK; +} + +static gint ptrarray_sort_compare_with_data (gconstpointer a, gconstpointer b, gpointer user_data) +{ + gchar *stra = *(gchar **) a; + gchar *strb = *(gchar **) b; + + if (strcmp (user_data, "this is the data for qsort") != 0) + fprintf (stderr, "oops at compare with_data\n"); + + return strcmp(stra, strb); +} + +RESULT ptrarray_sort_with_data () +{ + GPtrArray *array = g_ptr_array_new(); + guint i; + gchar *letters [] = { "A", "B", "C", "D", "E" }; + + g_ptr_array_add(array, letters[4]); + g_ptr_array_add(array, letters[1]); + g_ptr_array_add(array, letters[2]); + g_ptr_array_add(array, letters[0]); + g_ptr_array_add(array, letters[3]); + + g_ptr_array_sort_with_data(array, ptrarray_sort_compare_with_data, "this is the data for qsort"); + + for(i = 0; i < array->len; i++) { + if(array->pdata[i] != letters[i]) { + return FAILED("Array out of order, expected %s got %s at position %d", + letters [i], (gchar *) array->pdata [i], i); + } + } + + g_ptr_array_free(array, TRUE); + + return OK; +} + +RESULT ptrarray_remove_fast() +{ + GPtrArray *array = g_ptr_array_new(); + gchar *letters [] = { "A", "B", "C", "D", "E" }; + + if (g_ptr_array_remove_fast (array, NULL)) + return FAILED ("Removing NULL succeeded"); + + g_ptr_array_add(array, letters[0]); + if (!g_ptr_array_remove_fast (array, letters[0]) || array->len != 0) + return FAILED ("Removing last element failed"); + + g_ptr_array_add(array, letters[0]); + g_ptr_array_add(array, letters[1]); + g_ptr_array_add(array, letters[2]); + g_ptr_array_add(array, letters[3]); + g_ptr_array_add(array, letters[4]); + + if (!g_ptr_array_remove_fast (array, letters[0]) || array->len != 4) + return FAILED ("Removing first element failed"); + + if (array->pdata [0] != letters [4]) + return FAILED ("First element wasn't replaced with last upon removal"); + + if (g_ptr_array_remove_fast (array, letters[0])) + return FAILED ("Succedeed removing a non-existing element"); + + if (!g_ptr_array_remove_fast (array, letters[3]) || array->len != 3) + return FAILED ("Failed removing \"D\""); + + if (!g_ptr_array_remove_fast (array, letters[1]) || array->len != 2) + return FAILED ("Failed removing \"B\""); + + if (array->pdata [0] != letters [4] || array->pdata [1] != letters [2]) + return FAILED ("Last two elements are wrong"); + g_ptr_array_free(array, TRUE); + + return OK; +} + +static Test ptrarray_tests [] = { + {"alloc", ptrarray_alloc}, + {"for_iterate", ptrarray_for_iterate}, + {"foreach_iterate", ptrarray_foreach_iterate}, + {"set_size", ptrarray_set_size}, + {"remove_index", ptrarray_remove_index}, + {"remove_index_fast", ptrarray_remove_index_fast}, + {"remove", ptrarray_remove}, + {"sort", ptrarray_sort}, + {"remove_fast", ptrarray_remove_fast}, + {"sort_with_data", ptrarray_sort_with_data}, + {NULL, NULL} +}; + +DEFINE_TEST_GROUP_INIT(ptrarray_tests_init, ptrarray_tests) + + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/queue.c b/unity-2019.4.24f1-mbe/mono/eglib/test/queue.c new file mode 100644 index 000000000..b12ddec13 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/queue.c @@ -0,0 +1,184 @@ +#include +#include +#include +#include "test.h" + +RESULT +test_queue_push () +{ + GQueue *queue = g_queue_new (); + + g_queue_push_head (queue, "foo"); + g_queue_push_head (queue, "bar"); + g_queue_push_head (queue, "baz"); + + if (queue->length != 3) + return FAILED ("push failed"); + + if (NULL != queue->head->prev) + return FAILED ("HEAD: prev is wrong"); + if (strcmp ("baz", queue->head->data)) + return FAILED ("HEAD: First element is wrong"); + if (strcmp ("bar", queue->head->next->data)) + return FAILED ("HEAD: Second element is wrong"); + if (strcmp ("foo", queue->head->next->next->data)) + return FAILED ("HEAD: Third element is wrong"); + if (NULL != queue->head->next->next->next) + return FAILED ("HEAD: End is wrong"); + + if (NULL != queue->tail->next) + return FAILED ("TAIL: next is wrong"); + if (strcmp ("foo", queue->tail->data)) + return FAILED ("TAIL: Third element is wrong"); + if (strcmp ("bar", queue->tail->prev->data)) + return FAILED ("TAIL: Second element is wrong"); + if (strcmp ("baz", queue->tail->prev->prev->data)) + return FAILED ("TAIL: First element is wrong"); + if (NULL != queue->tail->prev->prev->prev) + return FAILED ("TAIL: End is wrong"); + + g_queue_free (queue); + return OK; +} + +RESULT +test_queue_push_tail () +{ + GQueue *queue = g_queue_new (); + + g_queue_push_tail (queue, "baz"); + g_queue_push_tail (queue, "bar"); + g_queue_push_tail (queue, "foo"); + + if (queue->length != 3) + return FAILED ("push failed"); + + if (NULL != queue->head->prev) + return FAILED ("HEAD: prev is wrong"); + if (strcmp ("baz", queue->head->data)) + return FAILED ("HEAD: First element is wrong"); + if (strcmp ("bar", queue->head->next->data)) + return FAILED ("HEAD: Second element is wrong"); + if (strcmp ("foo", queue->head->next->next->data)) + return FAILED ("HEAD: Third element is wrong"); + if (NULL != queue->head->next->next->next) + return FAILED ("HEAD: End is wrong"); + + if (NULL != queue->tail->next) + return FAILED ("TAIL: next is wrong"); + if (strcmp ("foo", queue->tail->data)) + return FAILED ("TAIL: Third element is wrong"); + if (strcmp ("bar", queue->tail->prev->data)) + return FAILED ("TAIL: Second element is wrong"); + if (strcmp ("baz", queue->tail->prev->prev->data)) + return FAILED ("TAIL: First element is wrong"); + if (NULL != queue->tail->prev->prev->prev) + return FAILED ("TAIL: End is wrong"); + + g_queue_free (queue); + return OK; +} + +RESULT +test_queue_pop () +{ + GQueue *queue = g_queue_new (); + gpointer data; + + g_queue_push_head (queue, "foo"); + g_queue_push_head (queue, "bar"); + g_queue_push_head (queue, "baz"); + + data = g_queue_pop_head (queue); + if (strcmp ("baz", data)) + return FAILED ("expect baz."); + + data = g_queue_pop_head (queue); + if (strcmp ("bar", data)) + return FAILED ("expect bar."); + + data = g_queue_pop_head (queue); + if (strcmp ("foo", data)) + return FAILED ("expect foo."); + + if (g_queue_is_empty (queue) == FALSE) + return FAILED ("expect is_empty."); + + if (queue->length != 0) + return FAILED ("expect 0 length ."); + + g_queue_push_head (queue, "foo"); + g_queue_push_head (queue, "bar"); + g_queue_push_head (queue, "baz"); + + g_queue_pop_head (queue); + + if (NULL != queue->head->prev) + return FAILED ("HEAD: prev is wrong"); + if (strcmp ("bar", queue->head->data)) + return FAILED ("HEAD: Second element is wrong"); + if (strcmp ("foo", queue->head->next->data)) + return FAILED ("HEAD: Third element is wrong"); + if (NULL != queue->head->next->next) + return FAILED ("HEAD: End is wrong"); + + if (NULL != queue->tail->next) + return FAILED ("TAIL: next is wrong"); + if (strcmp ("foo", queue->tail->data)) + return FAILED ("TAIL: Second element is wrong"); + if (strcmp ("bar", queue->tail->prev->data)) + return FAILED ("TAIL: First element is wrong"); + if (NULL != queue->tail->prev->prev) + return FAILED ("TAIL: End is wrong"); + + g_queue_free (queue); + return OK; +} + +RESULT +test_queue_new () +{ + GQueue *queue = g_queue_new (); + + if (queue->length != 0) + return FAILED ("expect length == 0"); + + if (queue->head != NULL) + return FAILED ("expect head == NULL"); + + if (queue->tail != NULL) + return FAILED ("expect tail == NULL"); + + g_queue_free (queue); + return OK; +} + +RESULT +test_queue_is_empty () +{ + GQueue *queue = g_queue_new (); + + if (g_queue_is_empty (queue) == FALSE) + return FAILED ("new queue should be empty"); + + g_queue_push_head (queue, "foo"); + + if (g_queue_is_empty (queue) == TRUE) + return FAILED ("expected TRUE"); + + g_queue_free (queue); + + return OK; +} + +static Test queue_tests [] = { + { "push", test_queue_push}, + {"push_tail", test_queue_push_tail}, + { "pop", test_queue_pop}, + { "new", test_queue_new}, + {"is_empty", test_queue_is_empty}, + {NULL, NULL} +}; + +DEFINE_TEST_GROUP_INIT(queue_tests_init, queue_tests) + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/shell.c b/unity-2019.4.24f1-mbe/mono/eglib/test/shell.c new file mode 100644 index 000000000..4715f1ca7 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/shell.c @@ -0,0 +1,331 @@ +#include +#include +#include +#include "test.h" + +RESULT +test_shell_argv1 () +{ + GError *error; + gint argc; + gchar **argv; + gboolean ret; + + /* The next line prints a critical error and returns FALSE + ret = g_shell_parse_argv (NULL, NULL, NULL, NULL); + */ + ret = g_shell_parse_argv ("", NULL, NULL, NULL); + if (ret) + return FAILED ("1. It should return FALSE"); + + ret = g_shell_parse_argv ("hola", NULL, NULL, NULL); + if (!ret) + return FAILED ("2. It should return TRUE"); + + argc = 0; + ret = g_shell_parse_argv ("hola", &argc, NULL, NULL); + if (!ret) + return FAILED ("3. It should return TRUE"); + if (argc != 1) + return FAILED ("4. argc was %d", argc); + + argc = 0; + ret = g_shell_parse_argv ("hola bola", &argc, NULL, NULL); + if (!ret) + return FAILED ("5. It should return TRUE"); + if (argc != 2) + return FAILED ("6. argc was %d", argc); + + argc = 0; + ret = g_shell_parse_argv ("hola bola", &argc, &argv, NULL); + if (!ret) + return FAILED ("7. It should return TRUE"); + if (argc != 2) + return FAILED ("8. argc was %d", argc); + if (strcmp (argv [0], "hola")) + return FAILED ("9. argv[0] was %s", argv [0]); + if (strcmp (argv [1], "bola")) + return FAILED ("10. argv[1] was %s", argv [1]); + + g_strfreev (argv); + argv = NULL; + argc = 0; + error = NULL; + ret = g_shell_parse_argv ("hola 'bola'", &argc, &argv, &error); + if (!ret) + return FAILED ("11. It should return TRUE"); + if (argc != 2) + return FAILED ("12. argc was %d expected 2", argc); + if (strcmp (argv [0], "hola")) + return FAILED ("13. argv[0] was %s", argv [0]); + if (strcmp (argv [1], "bola")) + return FAILED ("14. argv[1] was %s", argv [1]); + if (error != NULL) + return FAILED ("15. error is not null"); + + + g_strfreev (argv); + argv = NULL; + argc = 0; + error = NULL; + ret = g_shell_parse_argv ("hola '' 'bola'", &argc, &argv, &error); + if (!ret) + return FAILED ("16. It should return TRUE"); + if (argc != 3) + return FAILED ("17. argc was %d expected 3", argc); + if (strcmp (argv [0], "hola")) + return FAILED ("18. argv[0] was %s", argv [0]); + if (strcmp (argv [1], "")) + return FAILED ("19. argv[2] was %s", argv [1]); + if (strcmp (argv [2], "bola")) + return FAILED ("19. argv[2] was %s", argv [2]); + if (error != NULL) + return FAILED ("20. error is not null"); + + g_strfreev (argv); + argv = NULL; + argc = 0; + error = NULL; + ret = g_shell_parse_argv ("hola'' bola", &argc, &argv, &error); + if (!ret) + return FAILED ("21. It should return TRUE"); + if (argc != 2) + return FAILED ("22. argc was %d expected 2", argc); + if (strcmp (argv [0], "hola")) + return FAILED ("23. argv[0] was %s", argv [0]); + if (strcmp (argv [1], "bola")) + return FAILED ("24. argv[2] was %s", argv [1]); + if (error != NULL) + return FAILED ("25. error is not null"); + + return OK; +} + +RESULT +test_shell_argv2 () +{ + GError *error; + gint argc; + gchar **argv; + gboolean ret; + + argv = NULL; + argc = 0; + error = NULL; + ret = g_shell_parse_argv ("hola \"bola\"", &argc, &argv, &error); + if (!ret) + return FAILED ("1. It should return TRUE"); + if (argc != 2) + return FAILED ("2. argc was %d expected 2", argc); + if (strcmp (argv [0], "hola")) + return FAILED ("3. argv[0] was %s", argv [0]); + if (strcmp (argv [1], "bola")) + return FAILED ("4. argv[1] was %s", argv [1]); + if (error != NULL) + return FAILED ("5. error is not null"); + + g_strfreev (argv); + argv = NULL; + argc = 0; + error = NULL; + ret = g_shell_parse_argv ("hola \"\" \"bola \"", &argc, &argv, &error); + if (!ret) + return FAILED ("6. It should return TRUE"); + if (argc != 3) + return FAILED ("7. argc was %d expected 3", argc); + if (strcmp (argv [0], "hola")) + return FAILED ("8. argv[0] was %s", argv [0]); + if (strcmp (argv [1], "")) + return FAILED ("9. argv[2] was %s", argv [1]); + if (strcmp (argv [2], "bola ")) + return FAILED ("10. argv[2] was %s", argv [2]); + if (error != NULL) + return FAILED ("11. error is not null"); + + g_strfreev (argv); + argv = NULL; + argc = 0; + error = NULL; + ret = g_shell_parse_argv ("hola\n\t \"\t\" \"bola \"", &argc, &argv, &error); + if (!ret) + return FAILED ("10. It should return TRUE"); + if (argc != 3) + return FAILED ("11. argc was %d expected 3", argc); + if (strcmp (argv [0], "hola")) + return FAILED ("12. argv[0] was %s", argv [0]); + if (strcmp (argv [1], "\t")) + return FAILED ("13. argv[2] was %s", argv [1]); + if (strcmp (argv [2], "bola ")) + return FAILED ("14. argv[2] was %s", argv [2]); + if (error != NULL) + return FAILED ("15. error is not null"); + + g_strfreev (argv); + argv = NULL; + argc = 0; + error = NULL; + ret = g_shell_parse_argv ("hola\n\t \\\n \"\t\" \"bola \"", &argc, &argv, &error); + if (!ret) + return FAILED ("16. It should return TRUE"); + if (argc != 3) + return FAILED ("17. argc was %d expected 3", argc); + if (strcmp (argv [0], "hola")) + return FAILED ("18. argv[0] was %s", argv [0]); + if (strcmp (argv [1], "\t")) + return FAILED ("19. argv[2] was %s", argv [1]); + if (strcmp (argv [2], "bola ")) + return FAILED ("20. argv[2] was %s", argv [2]); + if (error != NULL) + return FAILED ("21. error is not null"); + + g_strfreev (argv); + return OK; +} + +RESULT +test_shell_argv3 () +{ + GError *error; + gint argc; + gchar **argv; + gboolean ret; + + argv = NULL; + argc = 0; + error = NULL; + ret = g_shell_parse_argv ("hola \"bola", &argc, &argv, &error); + if (ret) + return FAILED ("1. It should return FALSE"); + if (argc != 0) + return FAILED ("2. argc was %d expected 0", argc); + if (argv != NULL) + return FAILED ("3. argv[0] was %s", argv [0]); + if (error == NULL) + return FAILED ("4. error is null"); + + /* Text ended before matching quote was found for ". (The text was 'hola "bola') */ + g_error_free (error); + error = NULL; + ret = g_shell_parse_argv ("hola \\\"bola", &argc, &argv, &error); + if (!ret) + return FAILED ("5. It should return TRUE"); + if (argc != 2) + return FAILED ("6. argc was %d expected 2", argc); + if (strcmp (argv [0], "hola")) + return FAILED ("18. argv[0] was %s", argv [0]); + if (strcmp (argv [1], "\"bola")) + return FAILED ("18. argv[1] was %s", argv [1]); + if (error != NULL) + return FAILED ("8. error is not null"); + + g_strfreev (argv); + argv = NULL; + argc = 0; + ret = g_shell_parse_argv ("hola \"\n\\'bola\"", &argc, &argv, &error); + if (!ret) + return FAILED ("9. It should return TRUE. %s", error->message); + if (argc != 2) + return FAILED ("10. argc was %d expected 2", argc); + if (strcmp (argv [0], "hola")) + return FAILED ("11. argv[0] was %s", argv [0]); + if (strcmp (argv [1], "\n\\'bola")) + return FAILED ("12. argv[1] was %s", argv [1]); + if (error != NULL) + return FAILED ("13. error is not null"); + + g_strfreev (argv); + argv = NULL; + argc = 0; + return OK; +} + +// This was the 2.8 showstopper error +RESULT +test_shell_argv4 () +{ + GError *error; + gint argc; + gchar **argv; + gboolean ret; + char *str = "'/usr/bin/gnome-terminal' -e \"bash -c 'read -p \\\"Press any key to continue...\\\" -n1;'\""; + + argv = NULL; + argc = 0; + error = NULL; + ret = g_shell_parse_argv (str, &argc, &argv, &error); + if (!ret) + return FAILED ("1. It should return TRUE"); + if (argc != 3) + return FAILED ("2. argc was %d expected 3", argc); + if (argv == NULL) + return FAILED ("3. argv[0] was NULL"); + if (error != NULL) + return FAILED ("4. error was set"); + + if (strcmp (argv [0], "/usr/bin/gnome-terminal")) + return FAILED ("5. Expected /usr/bin/gnome-terminal got %s", argv [0]); + if (strcmp (argv [1], "-e")) + return FAILED ("6. Expected -e, got: %s", argv [1]); + if (strcmp (argv [2], "bash -c 'read -p \"Press any key to continue...\" -n1;'")) + return FAILED ("7. Got unexpected result: %s\n", argv [2]); + + return OK; +} + +// This is https://bugzilla.novell.com/show_bug.cgi?id=655896 +RESULT +test_shell_argv5 () +{ + GError *error; + gint argc; + gchar **argv; + gboolean ret; + char *str = "echo \"foo\",\"bar\""; + + argv = NULL; + argc = 0; + error = NULL; + ret = g_shell_parse_argv (str, &argc, &argv, &error); + if (!ret) + return FAILED ("1. It should return TRUE"); + if (argc != 2) + return FAILED ("2. argc was %d expected 2", argc); + if (argv == NULL) + return FAILED ("3. argv[0] was NULL"); + if (error != NULL) + return FAILED ("4. error was set"); + + if (strcmp (argv [0], "echo")) + return FAILED ("5. Expected echo got %s", argv [0]); + if (strcmp (argv [1], "foo,bar")) + return FAILED ("6. Expected foo,bar, got: %s", argv [1]); + + return OK; +} + +RESULT +test_quote () +{ + if (strcmp (g_shell_quote ("foo"), "'foo'")) + return FAILED ("Should return 'foo'"); + + if (strcmp (g_shell_quote ("foo'bar"), "'foo'\\''bar'")) + return FAILED ("Should return 'foo'\\''bar'"); + + if (strcmp (g_shell_quote ("foo bar"), "'foo bar'")) + return FAILED ("Should return 'foo bar'"); + return OK; +} + +static Test shell_tests [] = { + {"test_shell_argv1", test_shell_argv1}, + {"test_shell_argv2", test_shell_argv2}, + {"test_shell_argv3", test_shell_argv3}, + {"test_shell_argv4", test_shell_argv4}, + {"test_shell_argv5", test_shell_argv5}, + {"g_shell_quote", test_quote}, + {NULL, NULL} +}; + +DEFINE_TEST_GROUP_INIT(shell_tests_init, shell_tests) + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/sizes.c b/unity-2019.4.24f1-mbe/mono/eglib/test/sizes.c new file mode 100644 index 000000000..06ed8a5af --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/sizes.c @@ -0,0 +1,108 @@ +/* + * Tests to ensure that our type definitions are correct + * + * These depend on -Werror, -Wall being set to catch the build error. + */ +#include +#ifndef _MSC_VER +#include +#endif +#include +#include +#include "test.h" + +RESULT +test_formats () +{ + char buffer [1024]; + gsize a = 1; + + sprintf (buffer, "%" G_GSIZE_FORMAT, a); + + return NULL; +} + +RESULT +test_ptrconv () +{ + int iv, iv2; + unsigned int uv, uv2; + gpointer ptr; + + iv = G_MAXINT32; + ptr = GINT_TO_POINTER (iv); + iv2 = GPOINTER_TO_INT (ptr); + if (iv != iv2) + return FAILED ("int to pointer and back conversions fail %d != %d", iv, iv2); + + iv = G_MININT32; + ptr = GINT_TO_POINTER (iv); + iv2 = GPOINTER_TO_INT (ptr); + if (iv != iv2) + return FAILED ("int to pointer and back conversions fail %d != %d", iv, iv2); + + iv = 1; + ptr = GINT_TO_POINTER (iv); + iv2 = GPOINTER_TO_INT (ptr); + if (iv != iv2) + return FAILED ("int to pointer and back conversions fail %d != %d", iv, iv2); + + iv = -1; + ptr = GINT_TO_POINTER (iv); + iv2 = GPOINTER_TO_INT (ptr); + if (iv != iv2) + return FAILED ("int to pointer and back conversions fail %d != %d", iv, iv2); + + iv = 0; + ptr = GINT_TO_POINTER (iv); + iv2 = GPOINTER_TO_INT (ptr); + if (iv != iv2) + return FAILED ("int to pointer and back conversions fail %d != %d", iv, iv2); + + uv = 0; + ptr = GUINT_TO_POINTER (uv); + uv2 = GPOINTER_TO_UINT (ptr); + if (uv != uv2) + return FAILED ("uint to pointer and back conversions fail %u != %d", uv, uv2); + + uv = 1; + ptr = GUINT_TO_POINTER (uv); + uv2 = GPOINTER_TO_UINT (ptr); + if (uv != uv2) + return FAILED ("uint to pointer and back conversions fail %u != %d", uv, uv2); + + uv = UINT32_MAX; + ptr = GUINT_TO_POINTER (uv); + uv2 = GPOINTER_TO_UINT (ptr); + if (uv != uv2) + return FAILED ("uint to pointer and back conversions fail %u != %d", uv, uv2); + + return NULL; + +} + +typedef struct { + int a; + int b; +} my_struct; + +RESULT +test_offset () +{ + if (G_STRUCT_OFFSET (my_struct, a) != 0) + return FAILED ("offset of a is not zero"); + + if (G_STRUCT_OFFSET (my_struct, b) != 4 && G_STRUCT_OFFSET (my_struct, b) != 8) + return FAILED ("offset of b is 4 or 8, macro might be busted"); + + return OK; +} + +static Test size_tests [] = { + {"formats", test_formats}, + {"ptrconv", test_ptrconv}, + {"g_struct_offset", test_offset}, + {NULL, NULL} +}; + +DEFINE_TEST_GROUP_INIT(size_tests_init, size_tests) diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/slist.c b/unity-2019.4.24f1-mbe/mono/eglib/test/slist.c new file mode 100644 index 000000000..3f8360eca --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/slist.c @@ -0,0 +1,345 @@ +#include +#include +#include +#include "test.h" + + +RESULT +test_slist_nth () +{ + char *foo = "foo"; + char *bar = "bar"; + char *baz = "baz"; + GSList *nth, *list; + list = g_slist_prepend (NULL, baz); + list = g_slist_prepend (list, bar); + list = g_slist_prepend (list, foo); + + nth = g_slist_nth (list, 0); + if (nth->data != foo) + return FAILED ("nth failed. #0"); + + nth = g_slist_nth (list, 1); + if (nth->data != bar) + return FAILED ("nth failed. #1"); + + nth = g_slist_nth (list, 2); + if (nth->data != baz) + return FAILED ("nth failed. #2"); + + nth = g_slist_nth (list, 3); + if (nth) + return FAILED ("nth failed. #3: %s", nth->data); + + g_slist_free (list); + return OK; +} + +RESULT +test_slist_index () +{ + int i; + char *foo = "foo"; + char *bar = "bar"; + char *baz = "baz"; + GSList *list; + list = g_slist_prepend (NULL, baz); + list = g_slist_prepend (list, bar); + list = g_slist_prepend (list, foo); + + i = g_slist_index (list, foo); + if (i != 0) + return FAILED ("index failed. #0: %d", i); + + i = g_slist_index (list, bar); + if (i != 1) + return FAILED ("index failed. #1: %d", i); + + i = g_slist_index (list, baz); + if (i != 2) + return FAILED ("index failed. #2: %d", i); + + g_slist_free (list); + return OK; +} + +RESULT +test_slist_append () +{ + GSList *foo; + GSList *list = g_slist_append (NULL, "first"); + if (g_slist_length (list) != 1) + return FAILED ("append(null,...) failed"); + + foo = g_slist_append (list, "second"); + if (foo != list) + return FAILED ("changed list head on non-empty"); + + if (g_slist_length (list) != 2) + return FAILED ("Append failed"); + + g_slist_free (list); + return OK; +} + +RESULT +test_slist_concat () +{ + GSList *foo = g_slist_prepend (NULL, "foo"); + GSList *bar = g_slist_prepend (NULL, "bar"); + + GSList *list = g_slist_concat (foo, bar); + + if (g_slist_length (list) != 2) + return FAILED ("Concat failed."); + + g_slist_free (list); + return OK; +} + +RESULT +test_slist_find () +{ + GSList *list = g_slist_prepend (NULL, "three"); + GSList *found; + char *data; + + list = g_slist_prepend (list, "two"); + list = g_slist_prepend (list, "one"); + + data = "four"; + list = g_slist_append (list, data); + + found = g_slist_find (list, data); + + if (found->data != data) + return FAILED ("Find failed"); + + g_slist_free (list); + return OK; +} + +static gint +find_custom (gconstpointer a, gconstpointer b) +{ + return(strcmp (a, b)); +} + +RESULT +test_slist_find_custom () +{ + GSList *list = NULL, *found; + char *foo = "foo"; + char *bar = "bar"; + char *baz = "baz"; + + list = g_slist_prepend (list, baz); + list = g_slist_prepend (list, bar); + list = g_slist_prepend (list, foo); + + found = g_slist_find_custom (list, baz, find_custom); + + if (found == NULL) + return FAILED ("Find failed"); + + g_slist_free (list); + + return OK; +} + +RESULT +test_slist_remove () +{ + GSList *list = g_slist_prepend (NULL, "three"); + char *one = "one"; + list = g_slist_prepend (list, "two"); + list = g_slist_prepend (list, one); + + list = g_slist_remove (list, one); + + if (g_slist_length (list) != 2) + return FAILED ("Remove failed"); + + if (strcmp ("two", list->data) != 0) + return FAILED ("Remove failed"); + + g_slist_free (list); + return OK; +} + +RESULT +test_slist_remove_link () +{ + GSList *foo = g_slist_prepend (NULL, "a"); + GSList *bar = g_slist_prepend (NULL, "b"); + GSList *baz = g_slist_prepend (NULL, "c"); + GSList *list = foo; + + foo = g_slist_concat (foo, bar); + foo = g_slist_concat (foo, baz); + + list = g_slist_remove_link (list, bar); + + if (g_slist_length (list) != 2) + return FAILED ("remove_link failed #1"); + + if (bar->next != NULL) + return FAILED ("remove_link failed #2"); + + g_slist_free (list); + g_slist_free (bar); + + return OK; +} + +static gint +compare (gconstpointer a, gconstpointer b) +{ + char *foo = (char *) a; + char *bar = (char *) b; + + if (strlen (foo) < strlen (bar)) + return -1; + + return 1; +} + +RESULT +test_slist_insert_sorted () +{ + GSList *list = g_slist_prepend (NULL, "a"); + list = g_slist_append (list, "aaa"); + + /* insert at the middle */ + list = g_slist_insert_sorted (list, "aa", compare); + if (strcmp ("aa", list->next->data)) + return FAILED("insert_sorted failed #1"); + + /* insert at the beginning */ + list = g_slist_insert_sorted (list, "", compare); + if (strcmp ("", list->data)) + return FAILED ("insert_sorted failed #2"); + + /* insert at the end */ + list = g_slist_insert_sorted (list, "aaaa", compare); + if (strcmp ("aaaa", g_slist_last (list)->data)) + return FAILED ("insert_sorted failed #3"); + + g_slist_free (list); + return OK; +} + +RESULT +test_slist_insert_before () +{ + GSList *foo, *bar, *baz; + + foo = g_slist_prepend (NULL, "foo"); + foo = g_slist_insert_before (foo, NULL, "bar"); + bar = g_slist_last (foo); + + if (strcmp (bar->data, "bar")) + return FAILED ("1"); + + baz = g_slist_insert_before (foo, bar, "baz"); + if (foo != baz) + return FAILED ("2"); + + if (strcmp (foo->next->data, "baz")) + return FAILED ("3: %s", foo->next->data); + + g_slist_free (foo); + return OK; +} + +#define N_ELEMS 100 + +static int intcompare (gconstpointer p1, gconstpointer p2) +{ + return GPOINTER_TO_INT (p1) - GPOINTER_TO_INT (p2); +} + +static gboolean verify_sort (GSList *list, int len) +{ + int prev = GPOINTER_TO_INT (list->data); + len--; + for (list = list->next; list; list = list->next) { + int curr = GPOINTER_TO_INT (list->data); + if (prev > curr) + return FALSE; + prev = curr; + + if (len == 0) + return FALSE; + len--; + } + return len == 0; +} + +RESULT +test_slist_sort () +{ + int i, j, mul; + GSList *list = NULL; + + for (i = 0; i < N_ELEMS; ++i) + list = g_slist_prepend (list, GINT_TO_POINTER (i)); + list = g_slist_sort (list, intcompare); + if (!verify_sort (list, N_ELEMS)) + return FAILED ("decreasing list"); + + g_slist_free (list); + + list = NULL; + for (i = 0; i < N_ELEMS; ++i) + list = g_slist_prepend (list, GINT_TO_POINTER (-i)); + list = g_slist_sort (list, intcompare); + if (!verify_sort (list, N_ELEMS)) + return FAILED ("increasing list"); + + g_slist_free (list); + + list = g_slist_prepend (NULL, GINT_TO_POINTER (0)); + for (i = 1; i < N_ELEMS; ++i) { + list = g_slist_prepend (list, GINT_TO_POINTER (-i)); + list = g_slist_prepend (list, GINT_TO_POINTER (i)); + } + list = g_slist_sort (list, intcompare); + if (!verify_sort (list, 2*N_ELEMS-1)) + return FAILED ("alternating list"); + + g_slist_free (list); + + list = NULL; + mul = 1; + for (i = 1; i < N_ELEMS; ++i) { + mul = -mul; + for (j = 0; j < i; ++j) + list = g_slist_prepend (list, GINT_TO_POINTER (mul * j)); + } + list = g_slist_sort (list, intcompare); + if (!verify_sort (list, (N_ELEMS*N_ELEMS - N_ELEMS)/2)) + return FAILED ("wavering list"); + + g_slist_free (list); + + return OK; +} + +static Test slist_tests [] = { + {"nth", test_slist_nth}, + {"index", test_slist_index}, + {"append", test_slist_append}, + {"concat", test_slist_concat}, + {"find", test_slist_find}, + {"find_custom", test_slist_find_custom}, + {"remove", test_slist_remove}, + {"remove_link", test_slist_remove_link}, + {"insert_sorted", test_slist_insert_sorted}, + {"insert_before", test_slist_insert_before}, + {"sort", test_slist_sort}, + {NULL, NULL} +}; + +DEFINE_TEST_GROUP_INIT(slist_tests_init, slist_tests) + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/spawn.c b/unity-2019.4.24f1-mbe/mono/eglib/test/spawn.c new file mode 100644 index 000000000..ec30fc864 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/spawn.c @@ -0,0 +1,82 @@ +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include "test.h" + +#ifdef G_OS_WIN32 +#include +#define read _read +#define close _close +#endif + +RESULT +test_spawn_sync () +{ + gchar *out; + gchar *err; + gint status = -1; + GError *error = NULL; + + if (!g_spawn_command_line_sync ("ls", &out, &err, &status, &error)) + return FAILED ("Error executing 'ls'"); + + if (status != 0) + return FAILED ("Status is %d", status); + + if (out == NULL || strlen (out) == 0) + return FAILED ("Didn't get any output from ls!?"); + + g_free (out); + g_free (err); + return OK; +} + +RESULT +test_spawn_async () +{ + /* +gboolean +g_spawn_async_with_pipes (const gchar *working_directory, + gchar **argv, + gchar **envp, + GSpawnFlags flags, + GSpawnChildSetupFunc child_setup, + gpointer user_data, + GPid *child_pid, + gint *standard_input, + gint *standard_output, + gint *standard_error, + GError **error) */ + char *argv [15]; + int stdout_fd = -1; + char buffer [512]; + pid_t child_pid = 0; + + memset (argv, 0, 15 * sizeof (char *)); + argv [0] = "ls"; + if (!g_spawn_async_with_pipes (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, &child_pid, NULL, &stdout_fd, NULL, NULL)) + return FAILED ("1 Failed to run ls"); + if (child_pid == 0) + return FAILED ("2 child pid not returned"); + if (stdout_fd == -1) + return FAILED ("3 out fd is -1"); + + while (read (stdout_fd, buffer, 512) > 0); + close (stdout_fd); + + return OK; +} + +static Test spawn_tests [] = { + {"g_shell_spawn_sync", test_spawn_sync}, + {"g_shell_spawn_async_with_pipes", test_spawn_async}, + {NULL, NULL} +}; + +DEFINE_TEST_GROUP_INIT(spawn_tests_init, spawn_tests) + + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/string-util.c b/unity-2019.4.24f1-mbe/mono/eglib/test/string-util.c new file mode 100644 index 000000000..73efd13f4 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/string-util.c @@ -0,0 +1,698 @@ +#include +#include +#include +#include "test.h" + +/* This test is just to be used with valgrind */ +RESULT +test_strfreev () +{ + gchar **array = g_new (gchar *, 4); + array [0] = g_strdup ("one"); + array [1] = g_strdup ("two"); + array [2] = g_strdup ("three"); + array [3] = NULL; + + g_strfreev (array); + g_strfreev (NULL); + + return OK; +} + +RESULT +test_concat () +{ + gchar *x = g_strconcat ("Hello", ", ", "world", NULL); + if (strcmp (x, "Hello, world") != 0) + return FAILED("concat failed, got: %s", x); + g_free (x); + return OK; +} + +RESULT +test_split () +{ + const gchar *to_split = "Hello world, how are we doing today?"; + gint i; + gchar **v; + + v= g_strsplit(to_split, " ", 0); + + if(v == NULL) { + return FAILED("split failed, got NULL vector (1)"); + } + + for(i = 0; v[i] != NULL; i++); + if(i != 7) { + return FAILED("split failed, expected 7 tokens, got %d", i); + } + + g_strfreev(v); + + v = g_strsplit(to_split, ":", -1); + if(v == NULL) { + return FAILED("split failed, got NULL vector (2)"); + } + + for(i = 0; v[i] != NULL; i++); + if(i != 1) { + return FAILED("split failed, expected 1 token, got %d", i); + } + + if(strcmp(v[0], to_split) != 0) { + return FAILED("expected vector[0] to be '%s' but it was '%s'", + to_split, v[0]); + } + g_strfreev(v); + + v = g_strsplit ("", ":", 0); + if (v == NULL) + return FAILED ("g_strsplit returned NULL"); + g_strfreev (v); + + v = g_strsplit ("/home/miguel/dingus", "/", 0); + if (v [0][0] != 0) + return FAILED ("Got a non-empty first element"); + g_strfreev (v); + + v = g_strsplit ("appdomain1, Version=0.0.0.0, Culture=neutral", ",", 4); + if (strcmp (v [0], "appdomain1") != 0) + return FAILED ("Invalid value"); + + if (strcmp (v [1], " Version=0.0.0.0") != 0) + return FAILED ("Invalid value"); + + if (strcmp (v [2], " Culture=neutral") != 0) + return FAILED ("Invalid value"); + + if (v [3] != NULL) + return FAILED ("Expected only 3 elements"); + + g_strfreev (v); + + v = g_strsplit ("abcXYdefXghiXYjklYmno", "XY", 4); + if (strcmp (v [0], "abc") != 0) + return FAILED ("Invalid value 0"); + + if (strcmp (v [1], "defXghi") != 0) + return FAILED ("Invalid value 1"); + + if (strcmp (v [2], "jklYmno") != 0) + return FAILED ("Invalid value 2"); + + if (v [3] != NULL) + return FAILED ("Expected only 3 elements (1)"); + + g_strfreev (v); + + v = g_strsplit ("abcXYdefXghiXYjklYmno", "XY", 2); + if (strcmp (v [0], "abc") != 0) + return FAILED ("Invalid value 3"); + + if (strcmp (v [1], "defXghiXYjklYmno") != 0) + return FAILED ("Invalid value 4"); + + if (v [2] != NULL) + return FAILED ("Expected only 2 elements (2)"); + + g_strfreev (v); + + v = g_strsplit ("abcXYdefXghiXYjklYmnoXY", "XY", 3); + if (strcmp (v [0], "abc") != 0) + return FAILED ("Invalid value 5"); + + if (strcmp (v [1], "defXghi") != 0) + return FAILED ("Invalid value 6"); + + if (strcmp (v [2], "jklYmnoXY") != 0) + return FAILED ("Invalid value 7"); + + if (v [3] != NULL) + return FAILED ("Expected only 3 elements (3)"); + + g_strfreev (v); + + v = g_strsplit ("abcXYXYXYdefXY", "XY", -1); + if (strcmp (v [0], "abc") != 0) + return FAILED ("Invalid value 8"); + + if (strcmp (v [1], "") != 0) + return FAILED ("Invalid value 9"); + + if (strcmp (v [2], "") != 0) + return FAILED ("Invalid value 10"); + + if (strcmp (v [3], "def") != 0) + return FAILED ("Invalid value 11"); + + if (strcmp (v [4], "") != 0) + return FAILED ("Invalid value 12"); + + if (v [5] != NULL) + return FAILED ("Expected only 5 elements (4)"); + + g_strfreev (v); + + v = g_strsplit ("XYXYXYabcXYdef", "XY", -1); + if (strcmp (v [0], "") != 0) + return FAILED ("Invalid value 13"); + + if (strcmp (v [1], "") != 0) + return FAILED ("Invalid value 14"); + + if (strcmp (v [2], "") != 0) + return FAILED ("Invalid value 15"); + + if (strcmp (v [3], "abc") != 0) + return FAILED ("Invalid value 16"); + + if (strcmp (v [4], "def") != 0) + return FAILED ("Invalid value 17"); + + if (v [5] != NULL) + return FAILED ("Expected only 5 elements (5)"); + + g_strfreev (v); + + v = g_strsplit ("value=", "=", 2); + if (strcmp (v [0], "value") != 0) + return FAILED ("Invalid value 18; expected 'value', got '%s'", v [0]); + if (strcmp (v [1], "") != 0) + return FAILED ("Invalid value 19; expected '', got '%s'", v [1]); + if (v [2] != NULL) + return FAILED ("Expected only 2 elements (6)"); + + g_strfreev (v); + + return OK; +} + +RESULT +test_split_set () +{ + gchar **v; + + v = g_strsplit_set ("abcXYdefXghiXYjklYmno", "XY", 6); + if (strcmp (v [0], "abc") != 0) + return FAILED ("Invalid value 0"); + + if (strcmp (v [1], "") != 0) + return FAILED ("Invalid value 1"); + + if (strcmp (v [2], "def") != 0) + return FAILED ("Invalid value 2"); + + if (strcmp (v [3], "ghi") != 0) + return FAILED ("Invalid value 3"); + + if (strcmp (v [4], "") != 0) + return FAILED ("Invalid value 4"); + + if (strcmp (v [5], "jklYmno") != 0) + return FAILED ("Invalid value 5"); + + if (v [6] != NULL) + return FAILED ("Expected only 6 elements (1)"); + + g_strfreev (v); + + v = g_strsplit_set ("abcXYdefXghiXYjklYmno", "XY", 3); + if (strcmp (v [0], "abc") != 0) + return FAILED ("Invalid value 6"); + + if (strcmp (v [1], "") != 0) + return FAILED ("Invalid value 7"); + + if (strcmp (v [2], "defXghiXYjklYmno") != 0) + return FAILED ("Invalid value 8"); + + if (v [3] != NULL) + return FAILED ("Expected only 3 elements (2)"); + + g_strfreev (v); + + v = g_strsplit_set ("abcXdefYghiXjklYmnoX", "XY", 5); + if (strcmp (v [0], "abc") != 0) + return FAILED ("Invalid value 9"); + + if (strcmp (v [1], "def") != 0) + return FAILED ("Invalid value 10"); + + if (strcmp (v [2], "ghi") != 0) + return FAILED ("Invalid value 11"); + + if (strcmp (v [3], "jkl") != 0) + return FAILED ("Invalid value 12"); + + if (strcmp (v [4], "mnoX") != 0) + return FAILED ("Invalid value 13"); + + if (v [5] != NULL) + return FAILED ("Expected only 5 elements (5)"); + + g_strfreev (v); + + v = g_strsplit_set ("abcXYXdefXY", "XY", -1); + if (strcmp (v [0], "abc") != 0) + return FAILED ("Invalid value 14"); + + if (strcmp (v [1], "") != 0) + return FAILED ("Invalid value 15"); + + if (strcmp (v [2], "") != 0) + return FAILED ("Invalid value 16"); + + if (strcmp (v [3], "def") != 0) + return FAILED ("Invalid value 17"); + + if (strcmp (v [4], "") != 0) + return FAILED ("Invalid value 18"); + + if (strcmp (v [5], "") != 0) + return FAILED ("Invalid value 19"); + + if (v [6] != NULL) + return FAILED ("Expected only 6 elements (4)"); + + g_strfreev (v); + + v = g_strsplit_set ("XYXabcXYdef", "XY", -1); + if (strcmp (v [0], "") != 0) + return FAILED ("Invalid value 20"); + + if (strcmp (v [1], "") != 0) + return FAILED ("Invalid value 21"); + + if (strcmp (v [2], "") != 0) + return FAILED ("Invalid value 22"); + + if (strcmp (v [3], "abc") != 0) + return FAILED ("Invalid value 23"); + + if (strcmp (v [4], "") != 0) + return FAILED ("Invalid value 24"); + + if (strcmp (v [5], "def") != 0) + return FAILED ("Invalid value 25"); + + if (v [6] != NULL) + return FAILED ("Expected only 6 elements (5)"); + + g_strfreev (v); + + return OK; +} + +RESULT +test_strreverse () +{ + RESULT res = OK; + gchar *a = g_strdup ("onetwothree"); + gchar *a_target = "eerhtowteno"; + gchar *b = g_strdup ("onetwothre"); + gchar *b_target = "erhtowteno"; + gchar *c = g_strdup (""); + gchar *c_target = ""; + + g_strreverse (a); + if (strcmp (a, a_target)) { + res = FAILED("strreverse failed. Expecting: '%s' and got '%s'\n", a, a_target); + goto cleanup; + } + + g_strreverse (b); + if (strcmp (b, b_target)) { + res = FAILED("strreverse failed. Expecting: '%s' and got '%s'\n", b, b_target); + goto cleanup; + } + + g_strreverse (c); + if (strcmp (c, c_target)) { + res = FAILED("strreverse failed. Expecting: '%s' and got '%s'\n", b, b_target); + goto cleanup; + } + +cleanup: + g_free (c); + g_free (b); + g_free (a); + return res; +} + +RESULT +test_strjoin () +{ + char *s; + + s = g_strjoin (NULL, "a", "b", NULL); + if (strcmp (s, "ab") != 0) + return FAILED ("Join of two strings with no separator fails"); + g_free (s); + + s = g_strjoin ("", "a", "b", NULL); + if (strcmp (s, "ab") != 0) + return FAILED ("Join of two strings with empty separator fails"); + g_free (s); + + s = g_strjoin ("-", "a", "b", NULL); + if (strcmp (s, "a-b") != 0) + return FAILED ("Join of two strings with separator fails"); + g_free (s); + + s = g_strjoin ("-", "aaaa", "bbbb", "cccc", "dddd", NULL); + if (strcmp (s, "aaaa-bbbb-cccc-dddd") != 0) + return FAILED ("Join of multiple strings fails"); + g_free (s); + + s = g_strjoin ("-", NULL); + if (s == NULL || (strcmp (s, "") != 0)) + return FAILED ("Failed to join empty arguments"); + g_free (s); + + return OK; +} + +RESULT +test_strchug () +{ + char *str = g_strdup (" \t\n hola"); + + g_strchug (str); + if (strcmp ("hola", str)) { + fprintf (stderr, "%s\n", str); + g_free (str); + return FAILED ("Failed."); + } + g_free (str); + return OK; +} + +RESULT +test_strchomp () +{ + char *str = g_strdup ("hola \t"); + + g_strchomp (str); + if (strcmp ("hola", str)) { + fprintf (stderr, "%s\n", str); + g_free (str); + return FAILED ("Failed."); + } + g_free (str); + return OK; +} + +RESULT +test_strstrip () +{ + char *str = g_strdup (" \t hola "); + + g_strstrip (str); + if (strcmp ("hola", str)) { + fprintf (stderr, "%s\n", str); + g_free (str); + return FAILED ("Failed."); + } + g_free (str); + return OK; +} + +#define urit(so,j) do { s = g_filename_to_uri (so, NULL, NULL); if (strcmp (s, j) != 0) return FAILED("Got %s expected %s", s, j); g_free (s); } while (0); + +#define errit(so) do { s = g_filename_to_uri (so, NULL, NULL); if (s != NULL) return FAILED ("got %s, expected NULL", s); } while (0); + +RESULT +test_filename_to_uri () +{ +#ifdef G_OS_WIN32 +#else + char *s; + + urit ("/a", "file:///a"); + urit ("/home/miguel", "file:///home/miguel"); + urit ("/home/mig uel", "file:///home/mig%20uel"); + urit ("/\303\241", "file:///%C3%A1"); + urit ("/\303\241/octal", "file:///%C3%A1/octal"); + urit ("/%", "file:///%25"); + urit ("/\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037\040", "file:///%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%20"); + urit ("/!$&'()*+,-./", "file:///!$&'()*+,-./"); + urit ("/\042\043\045", "file:///%22%23%25"); + urit ("/0123456789:=", "file:///0123456789:="); + urit ("/\073\074\076\077", "file:///%3B%3C%3E%3F"); + urit ("/\133\134\135\136_\140\173\174\175", "file:///%5B%5C%5D%5E_%60%7B%7C%7D"); + urit ("/\173\174\175\176\177\200", "file:///%7B%7C%7D~%7F%80"); + urit ("/@ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", "file:///@ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); + errit ("a"); + errit ("./hola"); +#endif + + return OK; +} + +#define fileit(so,j) do { s = g_filename_from_uri (so, NULL, NULL); if (strcmp (s, j) != 0) return FAILED("Got %s expected %s", s, j); g_free (s); } while (0); + +#define ferrit(so) do { s = g_filename_from_uri (so, NULL, NULL); if (s != NULL) return FAILED ("got %s, expected NULL", s); } while (0); + +RESULT +test_filename_from_uri () +{ +#ifdef G_OS_WIN32 +#else + char *s; + + fileit ("file:///a", "/a"); + fileit ("file:///%41", "/A"); + fileit ("file:///home/miguel", "/home/miguel"); + fileit ("file:///home/mig%20uel", "/home/mig uel"); + ferrit ("/a"); + ferrit ("a"); + ferrit ("file://a"); + ferrit ("file:a"); + ferrit ("file:///%"); + ferrit ("file:///%0"); + ferrit ("file:///%jj"); +#endif + + return OK; +} + +RESULT +test_ascii_xdigit_value () +{ + int i; + gchar j; + + i = g_ascii_xdigit_value ('9' + 1); + if (i != -1) + return FAILED ("'9' + 1"); + i = g_ascii_xdigit_value ('0' - 1); + if (i != -1) + return FAILED ("'0' - 1"); + i = g_ascii_xdigit_value ('a' - 1); + if (i != -1) + return FAILED ("'a' - 1"); + i = g_ascii_xdigit_value ('f' + 1); + if (i != -1) + return FAILED ("'f' + 1"); + i = g_ascii_xdigit_value ('A' - 1); + if (i != -1) + return FAILED ("'A' - 1"); + i = g_ascii_xdigit_value ('F' + 1); + if (i != -1) + return FAILED ("'F' + 1"); + + for (j = '0'; j < '9'; j++) { + int c = g_ascii_xdigit_value (j); + if (c != (j - '0')) + return FAILED ("Digits %c -> %d", j, c); + } + for (j = 'a'; j < 'f'; j++) { + int c = g_ascii_xdigit_value (j); + if (c != (j - 'a' + 10)) + return FAILED ("Lower %c -> %d", j, c); + } + for (j = 'A'; j < 'F'; j++) { + int c = g_ascii_xdigit_value (j); + if (c != (j - 'A' + 10)) + return FAILED ("Upper %c -> %d", j, c); + } + return OK; +} + +RESULT +test_strdelimit () +{ + gchar *str; + + str = g_strdup (G_STR_DELIMITERS); + str = g_strdelimit (str, NULL, 'a'); + if (0 != strcmp ("aaaaaaa", str)) + return FAILED ("All delimiters: '%s'", str); + g_free (str); + str = g_strdup ("hola"); + str = g_strdelimit (str, "ha", '+'); + if (0 != strcmp ("+ol+", str)) + return FAILED ("2 delimiters: '%s'", str); + g_free (str); + return OK; +} + +#define NUMBERS "0123456789" + +RESULT +test_strlcpy () +{ + const gchar *src = "onetwothree"; + gchar *dest; + gsize i; + + dest = g_malloc (strlen (src) + 1); + memset (dest, 0, strlen (src) + 1); + i = g_strlcpy (dest, src, (gsize)-1); + if (i != strlen (src)) + return FAILED ("Test1 got %d", i); + + if (0 != strcmp (dest, src)) + return FAILED ("Src and dest not equal"); + + i = g_strlcpy (dest, src, 3); + if (i != strlen (src)) + return FAILED ("Test1 got %d", i); + if (0 != strcmp (dest, "on")) + return FAILED ("Test2"); + + i = g_strlcpy (dest, src, 1); + if (i != strlen (src)) + return FAILED ("Test3 got %d", i); + if (*dest != '\0') + return FAILED ("Test4"); + + i = g_strlcpy (dest, src, 12345); + if (i != strlen (src)) + return FAILED ("Test4 got %d", i); + if (0 != strcmp (dest, src)) + return FAILED ("Src and dest not equal 2"); + g_free (dest); + + /* This is a test for g_filename_from_utf8, even if it does not look like it */ + dest = g_filename_from_utf8 (NUMBERS, strlen (NUMBERS), NULL, NULL, NULL); + if (0 != strcmp (dest, NUMBERS)) + return FAILED ("problem [%s] and [%s]", dest, NUMBERS); + g_free (dest); + + return OK; +} + +RESULT +test_strescape () +{ + gchar *str; + + str = g_strescape ("abc", NULL); + if (strcmp ("abc", str)) + return FAILED ("#1"); + str = g_strescape ("\t\b\f\n\r\\\"abc", NULL); + if (strcmp ("\\t\\b\\f\\n\\r\\\\\\\"abc", str)) + return FAILED ("#2 %s", str); + str = g_strescape ("\001abc", NULL); + if (strcmp ("\\001abc", str)) + return FAILED ("#3 %s", str); + str = g_strescape ("\001abc", "\001"); + if (strcmp ("\001abc", str)) + return FAILED ("#3 %s", str); + return OK; +} + +RESULT +test_ascii_strncasecmp () +{ + int n; + + n = g_ascii_strncasecmp ("123", "123", 1); + if (n != 0) + return FAILED ("Should have been 0"); + + n = g_ascii_strncasecmp ("423", "123", 1); + if (n != 3) + return FAILED ("Should have been 3, got %d", n); + + n = g_ascii_strncasecmp ("123", "423", 1); + if (n != -3) + return FAILED ("Should have been -3, got %d", n); + + n = g_ascii_strncasecmp ("1", "1", 10); + if (n != 0) + return FAILED ("Should have been 0, got %d", n); + return OK; +} + +RESULT +test_ascii_strdown () +{ + const gchar *a = "~09+AaBcDeFzZ$0909EmPAbCdEEEEEZZZZAAA"; + const gchar *b = "~09+aabcdefzz$0909empabcdeeeeezzzzaaa"; + gchar *c; + gint n, l; + + l = (gint)strlen (b); + c = g_ascii_strdown (a, l); + n = g_ascii_strncasecmp (b, c, l); + + if (n != 0) { + g_free (c); + return FAILED ("Should have been 0, got %d", n); + } + + g_free (c); + return OK; +} + +RESULT +test_strdupv () +{ + gchar **one; + gchar **two; + gint len; + + one = g_strdupv (NULL); + if (one) + return FAILED ("Should have been NULL"); + + one = g_malloc (sizeof (gchar *)); + *one = NULL; + two = g_strdupv (one); + if (!two) + FAILED ("Should have been not NULL"); + len = g_strv_length (two); + if (len) + FAILED ("Should have been 0"); + g_strfreev (two); + g_strfreev (one); + return NULL; +} + +static Test strutil_tests [] = { + {"g_strfreev", test_strfreev}, + {"g_strconcat", test_concat}, + {"g_strsplit", test_split}, + {"g_strsplit_set", test_split_set}, + {"g_strreverse", test_strreverse}, + {"g_strjoin", test_strjoin}, + {"g_strchug", test_strchug}, + {"g_strchomp", test_strchomp}, + {"g_strstrip", test_strstrip}, + {"g_filename_to_uri", test_filename_to_uri}, + {"g_filename_from_uri", test_filename_from_uri}, + {"g_ascii_xdigit_value", test_ascii_xdigit_value}, + {"g_strdelimit", test_strdelimit}, + {"g_strlcpy", test_strlcpy}, + {"g_strescape", test_strescape}, + {"g_ascii_strncasecmp", test_ascii_strncasecmp }, + {"g_ascii_strdown", test_ascii_strdown }, + {"g_strdupv", test_strdupv }, + {NULL, NULL} +}; + +DEFINE_TEST_GROUP_INIT(strutil_tests_init, strutil_tests) + + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/string.c b/unity-2019.4.24f1-mbe/mono/eglib/test/string.c new file mode 100644 index 000000000..02ad0ad3e --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/string.c @@ -0,0 +1,237 @@ +#include +#include +#include +#include "test.h" + +#define sfail(k,p) if (s->str [p] != k) { g_string_free (s,TRUE); return FAILED("Got %s, Failed at %d, expected '%c'", s->str, p, k);} + +RESULT +test_append_speed() +{ + GString *s = g_string_new(""); + gint i; + + for(i = 0; i < 1024; i++) { + g_string_append(s, "x"); + } + + if(strlen (s->str) != 1024) { + return FAILED("Incorrect string size, got: %s %d", + s->str, strlen(s->str)); + } + + g_string_free (s, TRUE); + + return OK; +} + +RESULT +test_append_c_speed() +{ + GString *s = g_string_new(""); + gint i; + + for(i = 0; i < 1024; i++) { + g_string_append_c(s, 'x'); + } + + if(strlen(s->str) != 1024) { + return FAILED("Incorrect string size, got: %s %d", s->str, + strlen(s->str)); + } + + g_string_free(s, TRUE); + + return OK; +} + +RESULT +test_gstring () +{ + GString *s = g_string_new_len ("My stuff", 2); + char *ret; + int i; + + if (strcmp (s->str, "My") != 0) + return "Expected only 'My' on the string"; + g_string_free (s, TRUE); + + s = g_string_new_len ("My\0\0Rest", 6); + if (s->str [2] != 0) + return "Null was not copied"; + if (strcmp (s->str+4, "Re") != 0){ + return "Did not find the 'Re' part"; + } + + g_string_append (s, "lalalalalalalalalalalalalalalalalalalalalalal"); + if (s->str [2] != 0) + return "Null as not copied"; + if (strncmp (s->str+4, "Relala", 6) != 0){ + return FAILED("Did not copy correctly, got: %s", s->str+4); + } + + g_string_free (s, TRUE); + + s = g_string_new (""); + for (i = 0; i < 1024; i++){ + g_string_append_c (s, 'x'); + } + if (strlen (s->str) != 1024){ + return FAILED("Incorrect string size, got: %s %d\n", s->str, strlen (s->str)); + } + g_string_free (s, TRUE); + + s = g_string_new ("hola"); + g_string_sprintfa (s, "%s%d", ", bola", 5); + if (strcmp (s->str, "hola, bola5") != 0){ + return FAILED("Incorrect data, got: %s\n", s->str); + } + g_string_free (s, TRUE); + + s = g_string_new ("Hola"); + g_string_printf (s, "Dingus"); + + /* Test that it does not release it */ + ret = g_string_free (s, FALSE); + g_free (ret); + + s = g_string_new_len ("H" "\000" "H", 3); + g_string_append_len (s, "1" "\000" "2", 3); + sfail ('H', 0); + sfail ( 0, 1); + sfail ('H', 2); + sfail ('1', 3); + sfail ( 0, 4); + sfail ('2', 5); + g_string_free (s, TRUE); + + return OK; +} + +RESULT +test_sized () +{ + GString *s = g_string_sized_new (20); + + if (s->str [0] != 0) + return FAILED ("Expected an empty string"); + if (s->len != 0) + return FAILED ("Expected an empty len"); + + g_string_free (s, TRUE); + + return NULL; +} + +RESULT +test_truncate () +{ + GString *s = g_string_new ("0123456789"); + g_string_truncate (s, 3); + + if (strlen (s->str) != 3) + return FAILED ("size of string should have been 3, instead it is [%s]\n", s->str); + g_string_free (s, TRUE); + + s = g_string_new ("a"); + s = g_string_truncate (s, 10); + if (strlen (s->str) != 1) + return FAILED ("The size is not 1"); + g_string_truncate (s, (gsize)-1); + if (strlen (s->str) != 1) + return FAILED ("The size is not 1"); + g_string_truncate (s, 0); + if (strlen (s->str) != 0) + return FAILED ("The size is not 0"); + + g_string_free (s, TRUE); + + return NULL; +} + +RESULT +test_prepend () +{ + GString *s = g_string_new ("dingus"); + g_string_prepend (s, "one"); + + if (strcmp (s->str, "onedingus") != 0) + return FAILED ("Failed, expected onedingus, got [%s]", s->str); + + g_string_free (s, TRUE); + + /* This is to force the code that where stuff does not fit in the allocated block */ + s = g_string_sized_new (1); + g_string_prepend (s, "one"); + if (strcmp (s->str, "one") != 0) + return FAILED ("Got erroneous result, expected [one] got [%s]", s->str); + g_string_free (s, TRUE); + + /* This is to force the path where things fit */ + s = g_string_new ("123123123123123123123123"); + g_string_truncate (s, 1); + if (strcmp (s->str, "1") != 0) + return FAILED ("Expected [1] string, got [%s]", s->str); + + g_string_prepend (s, "pre"); + if (strcmp (s->str, "pre1") != 0) + return FAILED ("Expected [pre1], got [%s]", s->str); + g_string_free (s, TRUE); + + return NULL; +} + +RESULT +test_appendlen () +{ + GString *s = g_string_new (""); + + g_string_append_len (s, "boo\000x", 0); + if (s->len != 0) + return FAILED ("The length is not zero %d", s->len); + g_string_append_len (s, "boo\000x", 5); + if (s->len != 5) + return FAILED ("The length is not five %d", s->len); + g_string_append_len (s, "ha", -1); + if (s->len != 7) + return FAILED ("The length is not seven %d", s->len); + + g_string_free (s, TRUE); + + return NULL; +} + +RESULT +test_macros () +{ + char *s = g_strdup (G_STRLOC); + char *p = strchr (s + 2, ':'); + int n; + + if (p == NULL) + return FAILED ("Did not find a separator"); + n = atoi (p+1); + if (n <= 0) + return FAILED ("did not find a valid line number"); + + *p = 0; + if (strcmp (s + strlen(s) - 8 , "string.c") != 0) + return FAILED ("This did not store the filename on G_STRLOC"); + + g_free (s); + return NULL; +} + +static Test string_tests [] = { + {"append-speed", test_append_speed}, + {"append_c-speed", test_append_c_speed}, + {"ctor+append", test_gstring }, + {"ctor+sized", test_sized }, + {"truncate", test_truncate }, + {"prepend", test_prepend }, + {"append_len", test_appendlen }, + {"macros", test_macros }, + {NULL, NULL} +}; + +DEFINE_TEST_GROUP_INIT(string_tests_init, string_tests) diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/test-both b/unity-2019.4.24f1-mbe/mono/eglib/test/test-both new file mode 100644 index 000000000..038c927eb --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/test-both @@ -0,0 +1,74 @@ +#!/bin/sh + +for arg in $@; do + if [ "x$arg" = "x--help" ]; then + echo "Usage: $0 [OPTION]... [ITERATIONS] [TESTGROUP]..." + echo "" + echo "Works the same as test-eglib or test-glib with the following" + echo "exception. Run test-eglib --help for details on normal testing" + echo "" + echo "If the first OPTION is --speed-compare, the following is" + echo "applicable to this program:" + echo "" + echo " --speed-compare run drivers in -qtni mode and report" + echo " speed comparison information" + echo "" + echo "After --speed-compare, the number of iterations " + echo "(optional, default is 100000) can be specified, followed " + echo "by specific tests to run (or none to run all)" + echo "" + echo "If --speed-compare is not the first argument, all arguments are" + echo "passed on directly to each driver" + echo "" + exit 1 + fi +done + +if [ ! -x "./test-glib" -o ! -x "./test-eglib" ]; then + make +fi + +if [ "x$1" = "x--speed-compare" ]; then + ITERATIONS=100000 + if [ ! -z "$2" ]; then + case $2 in + *[0-9]*) ITERATIONS=$2; break; + esac + fi + + OPTIONS="-qnti $ITERATIONS" + + for arg in $@; do + if [ "x$arg" = "x--speed-compare" ]; then + continue; + elif [ "$arg" = "$ITERATIONS" ]; then + continue; + fi + + OPTIONS="$OPTIONS $arg" + done + + echo "Running tests with $OPTIONS..." + + GLIB=`./test-glib $OPTIONS` + EGLIB=`./test-eglib $OPTIONS` + + # this blows + FASTER_NAME=`echo "$GLIB GLib $EGLIB EGlib" | awk '{ if($1 < $3) print $2; else print $4 }'` + FASTER_SPEED=`echo "$GLIB $EGLIB" | awk '{ if($1 < $2) print $1; else print $2 }'` + SLOWER_NAME=`echo "$GLIB GLib $EGLIB EGlib" | awk '{ if($1 > $3) print $2; else print $4 }'` + SLOWER_SPEED=`echo "$GLIB $EGLIB" | awk '{ if($1 > $2) print $1; else print $2 }'` + + FASTER_PERCENTAGE=`echo "$SLOWER_SPEED $FASTER_SPEED" | awk '{ print ($1 / $2) * 100 }'` + + echo "$FASTER_NAME $FASTER_SPEED" + echo "$SLOWER_NAME $SLOWER_SPEED" + echo "------------------------------------------------" + echo "$FASTER_NAME is $FASTER_PERCENTAGE% faster than $SLOWER_NAME" + + exit 0; +fi + +./test-eglib $@ +./test-glib $@ + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/test.c b/unity-2019.4.24f1-mbe/mono/eglib/test/test.c new file mode 100644 index 000000000..7c870e9c7 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/test.c @@ -0,0 +1,275 @@ +/* + * EGLib Unit Group/Test Runners + * + * Author: + * Aaron Bockover (abockover@novell.com) + * + * (C) 2006 Novell, Inc. + * + * 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. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef G_OS_WIN32 +#include +#endif + +#include "test.h" + +extern gint global_passed, global_tests; +static gchar *last_result = NULL; + +gboolean +run_test(Test *test, gchar **result_out) +{ + gchar *result; + + if((result = test->handler()) == NULL) { + *result_out = NULL; + return TRUE; + } else { + *result_out = result; + return FALSE; + } +} + +gboolean +run_group(Group *group, gint iterations, gboolean quiet, + gboolean time, gchar *tests_to_run_s) +{ + Test *tests = group->handler(); + gint i, j, passed = 0, total = 0; + gdouble start_time_group, start_time_test; + gchar **tests_to_run = NULL; + + if(!quiet) { + if(iterations > 1) { + printf("[%s] (%dx)\n", group->name, iterations); + } else { + printf("[%s]\n", group->name); + } + } + + if(tests_to_run_s != NULL) { + tests_to_run = eg_strsplit(tests_to_run_s, ",", -1); + } + + start_time_group = get_timestamp(); + + for(i = 0; tests[i].name != NULL; i++) { + gchar *result = ""; + gboolean iter_pass, run; + + iter_pass = FALSE; + if(tests_to_run != NULL) { + gint j; + run = FALSE; + for(j = 0; tests_to_run[j] != NULL; j++) { + if(strcmp(tests_to_run[j], tests[i].name) == 0) { + run = TRUE; + break; + } + } + } else { + run = TRUE; + } + + if(!run) { + continue; + } + + total++; + + if(!quiet) { + printf(" %s: ", tests[i].name); + } + + start_time_test = get_timestamp(); + + for(j = 0; j < iterations; j++) { + iter_pass = run_test(&(tests[i]), &result); + if(!iter_pass) { + break; + } + } + + if(iter_pass) { + passed++; + if(!quiet) { + if(time) { + printf("OK (%g)\n", get_timestamp() - start_time_test); + } else { + printf("OK\n"); + } + } + } else { + if(!quiet) { + printf("FAILED (%s)\n", result); + } + + if(last_result == result) { + last_result = NULL; + g_free(result); + } + } + } + + global_passed += passed; + global_tests += total; + + if(!quiet) { + gdouble pass_percentage = ((gdouble)passed / (gdouble)total) * 100.0; + if(time) { + printf(" %d / %d (%g%%, %g)\n", passed, total, + pass_percentage, get_timestamp() - start_time_group); + } else { + printf(" %d / %d (%g%%)\n", passed, total, pass_percentage); + } + } + + if(tests_to_run != NULL) { + eg_strfreev(tests_to_run); + } + + return passed == total; +} + +RESULT +FAILED(const gchar *format, ...) +{ + gchar *ret; + va_list args; + gint n; + +#if !defined(HAVE_VASPRINTF) && !defined(_EGLIB_MAJOR) + /* We are linked against the real glib, no vasprintf */ + g_assert_not_reached (); + return NULL; +#else + va_start(args, format); + n = g_vasprintf(&ret, format, args); + va_end(args); + + if(n == -1) { + last_result = NULL; + return NULL; + } + + last_result = ret; + return ret; +#endif +} + +gdouble +get_timestamp() +{ + /* FIXME: We should use g_get_current_time here */ + GTimeVal res; + g_get_current_time (&res); + return res.tv_sec + (1.e-6) * res.tv_usec; +} + +/* + * Duplicating code here from EGlib to avoid g_strsplit skew between + * EGLib and GLib + */ + +gchar ** +eg_strsplit (const gchar *string, const gchar *delimiter, gint max_tokens) +{ + gchar *string_c; + gchar *strtok_save, **vector; + gchar *token, *token_c; + gint size = 1; + size_t token_length; + + g_return_val_if_fail(string != NULL, NULL); + g_return_val_if_fail(delimiter != NULL, NULL); + g_return_val_if_fail(delimiter[0] != 0, NULL); + + token_length = strlen(string); + string_c = (gchar *)g_malloc(token_length + 1); + memcpy(string_c, string, token_length); + string_c[token_length] = 0; + + vector = NULL; + token = (gchar *)strtok_r(string_c, delimiter, &strtok_save); + + while(token != NULL) { + token_length = strlen(token); + token_c = (gchar *)g_malloc(token_length + 1); + memcpy(token_c, token, token_length); + token_c[token_length] = 0; + + vector = vector == NULL ? + (gchar **)g_malloc(2 * sizeof(vector)) : + (gchar **)g_realloc(vector, (size + 1) * sizeof(vector)); + + vector[size - 1] = token_c; + size++; + + if(max_tokens > 0 && size >= max_tokens) { + if(size > max_tokens) { + break; + } + + token = strtok_save; + } else { + token = (gchar *)strtok_r(NULL, delimiter, &strtok_save); + } + } + + if(vector != NULL && size > 0) { + vector[size - 1] = NULL; + } + + g_free(string_c); + string_c = NULL; + + return vector; +} + +void +eg_strfreev (gchar **str_array) +{ + gchar **orig = str_array; + if (str_array == NULL) + return; + while (*str_array != NULL){ + g_free (*str_array); + str_array++; + } + g_free (orig); +} + + + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/test.h b/unity-2019.4.24f1-mbe/mono/eglib/test/test.h new file mode 100644 index 000000000..5c7275e00 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/test.h @@ -0,0 +1,82 @@ +/* + * EGLib Unit Group/Test Runners + * + * Author: + * Aaron Bockover (abockover@novell.com) + * + * (C) 2006 Novell, Inc. + * + * 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. + */ + +#ifndef _TEST_H +#define _TEST_H + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +/* disable the following warnings + * C4100: The formal parameter is not referenced in the body of the function. The unreferenced parameter is ignored. + * C4127: conditional expression is constant (test macros produce a lot of these) +*/ +#pragma warning(disable:4100 4127) +#endif + +typedef gchar * RESULT; + +typedef struct _Test Test; +typedef struct _Group Group; + +typedef gchar * (* RunTestHandler)(); +typedef Test * (* LoadGroupHandler)(); + +struct _Test { + const gchar *name; + RunTestHandler handler; +}; + +struct _Group { + const gchar *name; + LoadGroupHandler handler; +}; + +gboolean run_group(Group *group, gint iterations, gboolean quiet, + gboolean time, gchar *tests); +#undef FAILED +RESULT FAILED(const gchar *format, ...); +gdouble get_timestamp(); +gchar ** eg_strsplit (const gchar *string, const gchar *delimiter, gint max_tokens); +void eg_strfreev (gchar **str_array); + +#define OK NULL + +#define DEFINE_TEST_GROUP_INIT(name, table) \ + Test * (name)() { return table; } + +#define DEFINE_TEST_GROUP_INIT_H(name) \ + Test * (name)(); + +#endif /* _TEST_H */ + + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/tests.h b/unity-2019.4.24f1-mbe/mono/eglib/test/tests.h new file mode 100644 index 000000000..fdca39572 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/tests.h @@ -0,0 +1,57 @@ +#include "test.h" + +DEFINE_TEST_GROUP_INIT_H(string_tests_init); +DEFINE_TEST_GROUP_INIT_H(strutil_tests_init); +DEFINE_TEST_GROUP_INIT_H(slist_tests_init); +DEFINE_TEST_GROUP_INIT_H(list_tests_init); +DEFINE_TEST_GROUP_INIT_H(hashtable_tests_init); +DEFINE_TEST_GROUP_INIT_H(ptrarray_tests_init); +DEFINE_TEST_GROUP_INIT_H(size_tests_init); +DEFINE_TEST_GROUP_INIT_H(fake_tests_init); +DEFINE_TEST_GROUP_INIT_H(array_tests_init); +DEFINE_TEST_GROUP_INIT_H(queue_tests_init); +DEFINE_TEST_GROUP_INIT_H(path_tests_init); +DEFINE_TEST_GROUP_INIT_H(shell_tests_init); +DEFINE_TEST_GROUP_INIT_H(spawn_tests_init); +DEFINE_TEST_GROUP_INIT_H(timer_tests_init); +DEFINE_TEST_GROUP_INIT_H(file_tests_init); +DEFINE_TEST_GROUP_INIT_H(pattern_tests_init); +DEFINE_TEST_GROUP_INIT_H(dir_tests_init); +DEFINE_TEST_GROUP_INIT_H(markup_tests_init); +DEFINE_TEST_GROUP_INIT_H(unicode_tests_init); +DEFINE_TEST_GROUP_INIT_H(utf8_tests_init); +DEFINE_TEST_GROUP_INIT_H(endian_tests_init); +DEFINE_TEST_GROUP_INIT_H(module_tests_init); +DEFINE_TEST_GROUP_INIT_H(memory_tests_init); + +static Group test_groups [] = { + {"string", string_tests_init}, + {"strutil", strutil_tests_init}, + {"ptrarray", ptrarray_tests_init}, + {"slist", slist_tests_init}, + {"list", list_tests_init}, + {"hashtable", hashtable_tests_init}, + {"sizes", size_tests_init}, + {"fake", fake_tests_init}, + {"array", array_tests_init}, + {"queue", queue_tests_init}, + {"path", path_tests_init}, + {"shell", shell_tests_init}, + {"markup", markup_tests_init}, +#if !DISABLE_PROCESS_TESTS + {"spawn", spawn_tests_init}, + {"module", module_tests_init}, +#endif +#if !DISABLE_FILESYSTEM_TESTS + {"file", file_tests_init}, +#endif + {"timer", timer_tests_init}, + {"pattern", pattern_tests_init}, + {"dir", dir_tests_init}, + {"unicode", unicode_tests_init}, + {"utf8", utf8_tests_init}, + {"endian", endian_tests_init}, + {"memory", memory_tests_init}, + {NULL, NULL} +}; + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/timer.c b/unity-2019.4.24f1-mbe/mono/eglib/test/timer.c new file mode 100644 index 000000000..7b41f8004 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/timer.c @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include + +#ifdef G_OS_WIN32 +#include +#define sleep(t) Sleep((t) * 1000) +#endif + +#include "test.h" + +RESULT +test_timer () +{ + GTimer *timer; + gdouble elapsed1, elapsed2; + gulong usec = 0; + + timer = g_timer_new (); + sleep (1); + elapsed1 = g_timer_elapsed (timer, NULL); + if ((elapsed1 + 0.1) < 1.0) + return FAILED ("Elapsed time should be around 1s and was %f", elapsed1); + + g_timer_stop (timer); + elapsed1 = g_timer_elapsed (timer, NULL); + elapsed2 = g_timer_elapsed (timer, &usec); + if (fabs (elapsed1 - elapsed2) > 0.000001) + return FAILED ("The elapsed times are not equal %f - %f.", elapsed1, elapsed2); + + elapsed2 *= 1000000; + while (elapsed2 > 1000000) + elapsed2 -= 1000000; + + if (fabs (usec - elapsed2) > 100.0) + return FAILED ("usecs are wrong."); + + g_timer_destroy (timer); + return OK; +} + +static Test timer_tests [] = { + {"g_timer", test_timer}, + {NULL, NULL} +}; + +DEFINE_TEST_GROUP_INIT(timer_tests_init, timer_tests) + + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/unicode.c b/unity-2019.4.24f1-mbe/mono/eglib/test/unicode.c new file mode 100644 index 000000000..c1c3402ba --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/unicode.c @@ -0,0 +1,99 @@ +#include "test.h" + +/* + * g_unichar_type + */ +RESULT +test_g_unichar_type () +{ + if (g_unichar_type ('A') != G_UNICODE_UPPERCASE_LETTER) + return FAILED ("#1"); + if (g_unichar_type ('a') != G_UNICODE_LOWERCASE_LETTER) + return FAILED ("#2"); + if (g_unichar_type ('1') != G_UNICODE_DECIMAL_NUMBER) + return FAILED ("#3"); + if (g_unichar_type (0xA3) != G_UNICODE_CURRENCY_SYMBOL) + return FAILED ("#4"); + return NULL; +} + +/* + * g_unichar_toupper + */ +RESULT +test_g_unichar_toupper () +{ + if (g_unichar_toupper (0) != 0) + return FAILED ("#0"); + if (g_unichar_toupper ('a') != 'A') + return FAILED ("#1"); + if (g_unichar_toupper ('1') != '1') + return FAILED ("#2"); + if (g_unichar_toupper (0x1C4) != 0x1C4) + return FAILED ("#3"); + if (g_unichar_toupper (0x1F2) != 0x1F1) + return FAILED ("#4"); + if (g_unichar_toupper (0x1F3) != 0x1F1) + return FAILED ("#5"); + if (g_unichar_toupper (0xFFFF) != 0xFFFF) + return FAILED ("#6"); + if (g_unichar_toupper (0x10428) != 0x10400) + return FAILED ("#7"); + return NULL; +} + +/* + * g_unichar_tolower + */ +RESULT +test_g_unichar_tolower () +{ + if (g_unichar_tolower (0) != 0) + return FAILED ("#0"); + if (g_unichar_tolower ('A') != 'a') + return FAILED ("#1"); + if (g_unichar_tolower ('1') != '1') + return FAILED ("#2"); + if (g_unichar_tolower (0x1C5) != 0x1C6) + return FAILED ("#3"); + if (g_unichar_tolower (0x1F1) != 0x1F3) + return FAILED ("#4"); + if (g_unichar_tolower (0x1F2) != 0x1F3) + return FAILED ("#5"); + if (g_unichar_tolower (0xFFFF) != 0xFFFF) + return FAILED ("#6"); + return NULL; +} + +/* + * g_unichar_totitle + */ +RESULT +test_g_unichar_totitle () +{ + if (g_unichar_toupper (0) != 0) + return FAILED ("#0"); + if (g_unichar_totitle ('a') != 'A') + return FAILED ("#1"); + if (g_unichar_totitle ('1') != '1') + return FAILED ("#2"); + if (g_unichar_totitle (0x1C4) != 0x1C5) + return FAILED ("#3"); + if (g_unichar_totitle (0x1F2) != 0x1F2) + return FAILED ("#4"); + if (g_unichar_totitle (0x1F3) != 0x1F2) + return FAILED ("#5"); + if (g_unichar_toupper (0xFFFF) != 0xFFFF) + return FAILED ("#6"); + return NULL; +} + +static Test unicode_tests [] = { + {"g_unichar_type", test_g_unichar_type}, + {"g_unichar_toupper", test_g_unichar_toupper}, + {"g_unichar_tolower", test_g_unichar_tolower}, + {"g_unichar_totitle", test_g_unichar_totitle}, + {NULL, NULL} +}; + +DEFINE_TEST_GROUP_INIT(unicode_tests_init, unicode_tests) diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/utf8.c b/unity-2019.4.24f1-mbe/mono/eglib/test/utf8.c new file mode 100644 index 000000000..a924902c9 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/utf8.c @@ -0,0 +1,935 @@ +#include + +#include "test.h" + +/* + * g_utf16_to_utf8 + */ + +glong +compare_strings_utf8_pos (const gchar *expected, const gchar *actual, glong size) +{ + int i; + for (i = 0; i < size; i++) + if (expected [i] != actual [i]) + return i; + return -1; +} + +RESULT +compare_strings_utf8_RESULT (const gchar *expected, const gchar *actual, glong size) +{ + glong ret; + + ret = compare_strings_utf8_pos (expected, actual, size); + if (ret < 0) + return OK; + return FAILED ("Incorrect output: expected '%s' but was '%s', differ at %d\n", expected, actual, ret); +} + +void +gchar_to_gunichar2 (gunichar2 ret[], const gchar *src) +{ + int i; + + for (i = 0; src [i]; i++) + ret [i] = src [i]; + ret [i] = 0; +} + +RESULT +compare_utf16_to_utf8_explicit (const gchar *expected, const gunichar2 *utf16, glong len_in, glong len_out, glong size_spec) +{ + GError *error; + gchar* ret; + RESULT result; + glong in_read, out_read; + + result = NULL; + + error = NULL; + ret = g_utf16_to_utf8 (utf16, size_spec, &in_read, &out_read, &error); + if (error) { + result = FAILED ("The error is %d %s\n", (error)->code, (error)->message); + g_error_free (error); + if (ret) + g_free (ret); + return result; + } + if (in_read != len_in) + result = FAILED ("Read size is incorrect: expected %d but was %d\n", len_in, in_read); + else if (out_read != len_out) + result = FAILED ("Converted size is incorrect: expected %d but was %d\n", len_out, out_read); + else + result = compare_strings_utf8_RESULT (expected, ret, len_out); + + g_free (ret); + if (result) + return result; + + return OK; +} + +RESULT +compare_utf16_to_utf8 (const gchar *expected, const gunichar2 *utf16, glong len_in, glong len_out) +{ + RESULT result; + + result = compare_utf16_to_utf8_explicit (expected, utf16, len_in, len_out, -1); + if (result != OK) + return result; + return compare_utf16_to_utf8_explicit (expected, utf16, len_in, len_out, len_in); +} + +RESULT +test_utf16_to_utf8 () +{ + const gchar *src0 = "", *src1 = "ABCDE", *src2 = "\xE5\xB9\xB4\x27", *src3 = "\xEF\xBC\xA1", *src4 = "\xEF\xBD\x81", *src5 = "\xF0\x90\x90\x80"; + gunichar2 str0 [] = {0}, str1 [6], str2 [] = {0x5E74, 39, 0}, str3 [] = {0xFF21, 0}, str4 [] = {0xFF41, 0}, str5 [] = {0xD801, 0xDC00, 0}; + RESULT result; + + gchar_to_gunichar2 (str1, src1); + + /* empty string */ + result = compare_utf16_to_utf8 (src0, str0, 0, 0); + if (result != OK) + return result; + + result = compare_utf16_to_utf8 (src1, str1, 5, 5); + if (result != OK) + return result; + result = compare_utf16_to_utf8 (src2, str2, 2, 4); + if (result != OK) + return result; + result = compare_utf16_to_utf8 (src3, str3, 1, 3); + if (result != OK) + return result; + result = compare_utf16_to_utf8 (src4, str4, 1, 3); + if (result != OK) + return result; + result = compare_utf16_to_utf8 (src5, str5, 2, 4); + if (result != OK) + return result; + + return OK; +} + +/* + * g_utf8_to_utf16 + */ + +glong +compare_strings_utf16_pos (const gunichar2 *expected, const gunichar2 *actual, glong size) +{ + int i; + for (i = 0; i < size; i++) + if (expected [i] != actual [i]) + return i; + return -1; +} + +RESULT +compare_strings_utf16_RESULT (const gunichar2 *expected, const gunichar2 *actual, glong size) +{ + glong ret; + + ret = compare_strings_utf16_pos (expected, actual, size); + if (ret < 0) + return OK; + return FAILED ("Incorrect output: expected '%s' but was '%s', differ at %d ('%c' x '%c')\n", expected, actual, ret, expected [ret], actual [ret]); +} + +#if !defined(EGLIB_TESTS) +#define eg_utf8_to_utf16_with_nuls g_utf8_to_utf16 +#endif + +RESULT +compare_utf8_to_utf16_explicit (const gunichar2 *expected, const gchar *utf8, glong len_in, glong len_out, glong size_spec, gboolean include_nuls) +{ + GError *error; + gunichar2* ret; + RESULT result; + glong in_read, out_read; + + result = NULL; + + error = NULL; + if (include_nuls) + ret = eg_utf8_to_utf16_with_nuls (utf8, size_spec, &in_read, &out_read, &error); + else + ret = g_utf8_to_utf16 (utf8, size_spec, &in_read, &out_read, &error); + + if (error) { + result = FAILED ("The error is %d %s\n", (error)->code, (error)->message); + g_error_free (error); + if (ret) + g_free (ret); + return result; + } + if (in_read != len_in) + result = FAILED ("Read size is incorrect: expected %d but was %d\n", len_in, in_read); + else if (out_read != len_out) + result = FAILED ("Converted size is incorrect: expected %d but was %d\n", len_out, out_read); + else + result = compare_strings_utf16_RESULT (expected, ret, len_out); + + g_free (ret); + if (result) + return result; + + return OK; +} + +RESULT +compare_utf8_to_utf16_general (const gunichar2 *expected, const gchar *utf8, glong len_in, glong len_out, gboolean include_nuls) +{ + RESULT result; + + result = compare_utf8_to_utf16_explicit (expected, utf8, len_in, len_out, -1, include_nuls); + if (result != OK) + return result; + return compare_utf8_to_utf16_explicit (expected, utf8, len_in, len_out, len_in, include_nuls); +} + +RESULT +compare_utf8_to_utf16 (const gunichar2 *expected, const gchar *utf8, glong len_in, glong len_out) +{ + return compare_utf8_to_utf16_general (expected, utf8, len_in, len_out, FALSE); +} + +RESULT +compare_utf8_to_utf16_with_nuls (const gunichar2 *expected, const gchar *utf8, glong len_in, glong len_out) +{ + return compare_utf8_to_utf16_explicit (expected, utf8, len_in, len_out, len_in, TRUE); +} + + +RESULT +test_utf8_seq () +{ + const gchar *src = "\xE5\xB9\xB4\x27"; + glong in_read, out_read; + //gunichar2 expected [6]; + GError *error = NULL; + gunichar2 *dst; + + //printf ("got: %s\n", src); + dst = g_utf8_to_utf16 (src, (glong)strlen (src), &in_read, &out_read, &error); + if (error != NULL){ + return error->message; + } + + if (in_read != 4) { + return FAILED ("in_read is expected to be 4 but was %d\n", in_read); + } + if (out_read != 2) { + return FAILED ("out_read is expected to be 2 but was %d\n", out_read); + } + g_free (dst); + + return OK; +} + +RESULT +test_utf8_to_utf16 () +{ + const gchar *src0 = "", *src1 = "ABCDE", *src2 = "\xE5\xB9\xB4\x27", *src3 = "\xEF\xBC\xA1", *src4 = "\xEF\xBD\x81"; + gunichar2 str0 [] = {0}, str1 [6], str2 [] = {0x5E74, 39, 0}, str3 [] = {0xFF21, 0}, str4 [] = {0xFF41, 0}; + RESULT result; + + gchar_to_gunichar2 (str1, src1); + + /* empty string */ + result = compare_utf8_to_utf16 (str0, src0, 0, 0); + if (result != OK) + return result; + + result = compare_utf8_to_utf16 (str1, src1, 5, 5); + if (result != OK) + return result; + result = compare_utf8_to_utf16 (str2, src2, 4, 2); + if (result != OK) + return result; + result = compare_utf8_to_utf16 (str3, src3, 3, 1); + if (result != OK) + return result; + result = compare_utf8_to_utf16 (str4, src4, 3, 1); + if (result != OK) + return result; + + return OK; +} + +RESULT +test_utf8_to_utf16_with_nuls () +{ + const gchar *src0 = "", *src1 = "AB\0DE", *src2 = "\xE5\xB9\xB4\x27", *src3 = "\xEF\xBC\xA1", *src4 = "\xEF\xBD\x81"; + gunichar2 str0 [] = {0}, str1 [] = {'A', 'B', 0, 'D', 'E', 0}, str2 [] = {0x5E74, 39, 0}, str3 [] = {0xFF21, 0}, str4 [] = {0xFF41, 0}; + RESULT result; + +#if !defined(EGLIB_TESTS) + return OK; +#endif + + /* implicit length is forbidden */ + if (eg_utf8_to_utf16_with_nuls (src1, -1, NULL, NULL, NULL) != NULL) + return FAILED ("explicit nulls must fail with -1 length\n"); + + /* empty string */ + result = compare_utf8_to_utf16_with_nuls (str0, src0, 0, 0); + if (result != OK) + return result; + + result = compare_utf8_to_utf16_with_nuls (str1, src1, 5, 5); + if (result != OK) + return result; + result = compare_utf8_to_utf16_with_nuls (str2, src2, 4, 2); + if (result != OK) + return result; + result = compare_utf8_to_utf16_with_nuls (str3, src3, 3, 1); + if (result != OK) + return result; + result = compare_utf8_to_utf16_with_nuls (str4, src4, 3, 1); + if (result != OK) + return result; + + return OK; +} + +typedef struct { + char *content; + size_t length; +} convert_result_t; + +RESULT +test_convert () +{ + static const char *charsets[] = { "UTF-8", "UTF-16LE", "UTF-16BE", "UTF-32LE", "UTF-32BE" }; + gsize length, converted_length, n; + char *content, *converted, *path; + convert_result_t **expected; + GError *err = NULL; + const char *srcdir; + gboolean loaded; + guint i, j, k; + char c; + + if (!(srcdir = getenv ("srcdir")) && !(srcdir = getenv ("PWD"))) + return FAILED ("srcdir not defined!"); + + expected = g_malloc (sizeof (convert_result_t *) * G_N_ELEMENTS (charsets)); + + /* first load all our test samples... */ + for (i = 0; i < G_N_ELEMENTS (charsets); i++) { + path = g_strdup_printf ("%s%c%s.txt", srcdir, G_DIR_SEPARATOR, charsets[i]); + loaded = g_file_get_contents (path, &content, &length, &err); + g_free (path); + + if (!loaded) { + for (j = 0; j < i; j++) { + g_free (expected[j]->content); + g_free (expected[j]); + } + + g_free (expected); + + return FAILED ("Failed to load content for %s: %s", charsets[i], err->message); + } + + expected[i] = g_malloc (sizeof (convert_result_t)); + expected[i]->content = content; + expected[i]->length = length; + } + + /* test conversion from every charset to every other charset */ + for (i = 0; i < G_N_ELEMENTS (charsets); i++) { + for (j = 0; j < G_N_ELEMENTS (charsets); j++) { + converted = g_convert (expected[i]->content, expected[i]->length, charsets[j], + charsets[i], NULL, &converted_length, NULL); + + if (converted == NULL) { + for (k = 0; k < G_N_ELEMENTS (charsets); k++) { + g_free (expected[k]->content); + g_free (expected[k]); + } + + g_free (expected); + + return FAILED ("Failed to convert from %s to %s: NULL", charsets[i], charsets[j]); + } + + if (converted_length != expected[j]->length) { + length = expected[j]->length; + + for (k = 0; k < G_N_ELEMENTS (charsets); k++) { + g_free (expected[k]->content); + g_free (expected[k]); + } + + g_free (converted); + g_free (expected); + + return FAILED ("Failed to convert from %s to %s: expected %u bytes, got %u", + charsets[i], charsets[j], length, converted_length); + } + + for (n = 0; n < converted_length; n++) { + if (converted[n] != expected[j]->content[n]) { + c = expected[j]->content[n]; + + for (k = 0; k < G_N_ELEMENTS (charsets); k++) { + g_free (expected[k]->content); + g_free (expected[k]); + } + + g_free (converted); + g_free (expected); + + return FAILED ("Failed to convert from %s to %s: expected 0x%x at offset %u, got 0x%x", + charsets[i], charsets[j], c, n, converted[n]); + } + } + + g_free (converted); + } + } + + for (k = 0; k < G_N_ELEMENTS (charsets); k++) { + g_free (expected[k]->content); + g_free (expected[k]); + } + + g_free (expected); + + return OK; +} + + +RESULT +test_xdigit () +{ + static char test_chars[] = { + '0', '1', '2', '3', '4', + '5', '6', '7', '8', '9', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'A', 'B', 'C', 'D', 'E', 'F', 'G'}; + static gint32 test_values[] = { + 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, -1, + 10, 11, 12, 13, 14, 15, -1}; + + int i =0; + + for (i = 0; i < sizeof(test_chars); i++) + if (g_unichar_xdigit_value ((gunichar)test_chars[i]) != test_values[i]) + return FAILED("Incorrect value %d at index %d", test_values[i], i); + + return OK; +} + +static RESULT +ucs4_to_utf16_check_result (const gunichar2 *result_str, const gunichar2 *expected_str, + glong result_items_read, glong expected_items_read, + glong result_items_written, glong expected_items_written, + GError* result_error, gboolean expect_error) +{ + glong i; + if (result_items_read != expected_items_read) + return FAILED("Incorrect number of items read; expected %d, got %d", expected_items_read, result_items_read); + if (result_items_written != expected_items_written) + return FAILED("Incorrect number of items written; expected %d, got %d", expected_items_written, result_items_written); + if (result_error && !expect_error) + return FAILED("There should not be an error code."); + if (!result_error && expect_error) + return FAILED("Unexpected error object."); + if (expect_error && result_str) + return FAILED("NULL should be returned when an error occurs."); + if (!expect_error && !result_str) + return FAILED("When no error occurs NULL should not be returned."); + for (i=0; i 4) + return FAILED ("Word1 has gone past its expected length"); + if (*ptr != word1ExpectedValues[count]) + return FAILED ("Word1 has an incorrect next_char at index %i", count); + ptr = g_utf8_next_char (ptr); + count++; + } + + //Test word2 + count = 0; + ptr = word2; + while (*ptr != 0) { + if (count > 4) + return FAILED ("Word2 has gone past its expected length"); + if (*ptr != word2ExpectedValues[count]) + return FAILED ("Word2 has an incorrect next_char at index %i", count); + ptr = g_utf8_next_char (ptr); + count++; + } + + return OK; +} + +RESULT +test_utf8_validate() +{ + gchar invalidWord1 [] = {0xC3, 0x82, 0xC1,0x90,'\0'}; //Invalid, 1nd oct Can't be 0xC0 or 0xC1 + gchar invalidWord2 [] = {0xC1, 0x89, 0x60, '\0'}; //Invalid, 1st oct can not be 0xC1 + gchar invalidWord3 [] = {0xC2, 0x45,0xE1, 0x81, 0x83,0x58,'\0'}; //Invalid, oct after 0xC2 must be > 0x80 + + gchar validWord1 [] = {0xC2, 0x82, 0xC3,0xA0,'\0'}; //Valid + gchar validWord2 [] = {0xC2, 0x82,0x45,0xE1, 0x81, 0x83,0x58,0xF1, 0x82, 0x82, 0x82,'\0'}; //Valid + + const gchar* end; + gboolean retVal = g_utf8_validate (invalidWord1, -1, &end); + if (retVal != FALSE) + return FAILED ("Expected invalidWord1 to be invalid"); + if (end != &invalidWord1 [2]) + return FAILED ("Expected end parameter to be pointing to invalidWord1[2]"); + + end = NULL; + retVal = g_utf8_validate (invalidWord2, -1, &end); + if (retVal != FALSE) + return FAILED ("Expected invalidWord2 to be invalid"); + if (end != &invalidWord2 [0]) + return FAILED ("Expected end parameter to be pointing to invalidWord2[0]"); + + end = NULL; + retVal = g_utf8_validate (invalidWord3, -1, &end); + if (retVal != FALSE) + return FAILED ("Expected invalidWord3 to be invalid"); + if (end != &invalidWord3 [0]) + return FAILED ("Expected end parameter to be pointing to invalidWord3[1]"); + + end = NULL; + retVal = g_utf8_validate (validWord1, -1, &end); + if (retVal != TRUE) + return FAILED ("Expected validWord1 to be valid"); + if (end != &validWord1 [4]) + return FAILED ("Expected end parameter to be pointing to validWord1[4]"); + + end = NULL; + retVal = g_utf8_validate (validWord2, -1, &end); + if (retVal != TRUE) + return FAILED ("Expected validWord2 to be valid"); + if (end != &validWord2 [11]) + return FAILED ("Expected end parameter to be pointing to validWord2[11]"); + return OK; +} + +glong +utf8_byteslen (const gchar *src) +{ + int i = 0; + do { + if (src [i] == '\0') + return i; + i++; + } while (TRUE); +} + +RESULT +test_utf8_strcase_each (const gchar *src, const gchar *expected, gboolean strup) +{ + gchar *tmp; + glong len, len2; + RESULT r; + + len = utf8_byteslen (src); + tmp = strup ? g_utf8_strup (src, len) : g_utf8_strdown (src, len); + len2 = utf8_byteslen (tmp); + r = compare_strings_utf8_RESULT (expected, tmp, len < len2 ? len2 : len); + g_free (tmp); + return r; +} + +RESULT +test_utf8_strup_each (const gchar *src, const gchar *expected) +{ + return test_utf8_strcase_each (src, expected, TRUE); +} + +RESULT +test_utf8_strdown_each (const gchar *src, const gchar *expected) +{ + return test_utf8_strcase_each (src, expected, FALSE); +} + +/* + * g_utf8_strup + */ +RESULT +test_utf8_strup () +{ + RESULT r; + + if ((r = test_utf8_strup_each ("aBc", "ABC")) != OK) + return r; + if ((r = test_utf8_strup_each ("x86-64", "X86-64")) != OK) + return r; + // U+3B1 U+392 -> U+391 U+392 + if ((r = test_utf8_strup_each ("\xCE\xB1\xCE\x92", "\xCE\x91\xCE\x92")) != OK) + return r; + // U+FF21 -> U+FF21 + if ((r = test_utf8_strup_each ("\xEF\xBC\xA1", "\xEF\xBC\xA1")) != OK) + return r; + // U+FF41 -> U+FF21 + if ((r = test_utf8_strup_each ("\xEF\xBD\x81", "\xEF\xBC\xA1")) != OK) + return r; + // U+10428 -> U+10400 + if ((r = test_utf8_strup_each ("\xF0\x90\x90\xA8", "\xF0\x90\x90\x80")) != OK) + return r; + + return OK; +} + +/* + * g_utf8_strdown + */ +RESULT +test_utf8_strdown () +{ + RESULT r; + + if ((r = test_utf8_strdown_each ("aBc", "abc")) != OK) + return r; + if ((r = test_utf8_strdown_each ("X86-64", "x86-64")) != OK) + return r; + // U+391 U+3B2 -> U+3B1 U+3B2 + if ((r = test_utf8_strdown_each ("\xCE\x91\xCE\xB2", "\xCE\xB1\xCE\xB2")) != OK) + return r; +/* + // U+FF41 -> U+FF41 + if ((r = test_utf8_strdown_each ("\xEF\xBC\x81", "\xEF\xBC\x81")) != OK) + return r; + // U+FF21 -> U+FF41 + if ((r = test_utf8_strdown_each ("\xEF\xBC\xA1", "\xEF\xBD\x81")) != OK) + return r; + // U+10400 -> U+10428 + if ((r = test_utf8_strdown_each ("\xF0\x90\x90\x80", "\xF0\x90\x90\xA8")) != OK) + return r; +*/ + return OK; +} + +/* + * test initialization + */ + +static Test utf8_tests [] = { + {"g_utf16_to_utf8", test_utf16_to_utf8}, + {"g_utf8_to_utf16", test_utf8_to_utf16}, + {"g_utf8_to_utf16_with_nuls", test_utf8_to_utf16_with_nuls}, + {"g_utf8_seq", test_utf8_seq}, + {"g_convert", test_convert }, + {"g_unichar_xdigit_value", test_xdigit }, + {"g_ucs4_to_utf16", test_ucs4_to_utf16 }, + {"g_utf16_to_ucs4", test_utf16_to_ucs4 }, + {"g_utf8_strlen", test_utf8_strlen }, + {"g_utf8_get_char", test_utf8_get_char }, + {"g_utf8_next_char", test_utf8_next_char }, + {"g_utf8_validate", test_utf8_validate }, + {"g_utf8_strup", test_utf8_strup}, + {"g_utf8_strdown", test_utf8_strdown}, + {NULL, NULL} +}; + +DEFINE_TEST_GROUP_INIT(utf8_tests_init, utf8_tests) + + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/test/whats-implemented b/unity-2019.4.24f1-mbe/mono/eglib/test/whats-implemented new file mode 100644 index 000000000..8a1135783 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/test/whats-implemented @@ -0,0 +1,83 @@ +#!/bin/bash + +# Author: Aaron Bockover +# Licensed under MIT/X11 +# (C) 2006 Novell + +if [ "x$1" = "x--help" ]; then + echo "Usage: $0 [--show-only-mono]" + echo "" + echo "This script prints a sorted list of GLib functions used in Mono" + echo "that have not yet been implemented in EGlib." + echo "" + echo "If --show-only-mono is passed, then the script will print all" + echo "GLib functions used in Mono, whether or not they have been" + echo "implemented in EGlib yet." + echo "" + echo "This script relies on the MONO_CHECKOUT environment variable." + echo "MONO_CHECKOUT should be set to the location of a mono checkout." + echo "" + exit 1 +fi + +IGNORE_FUNCTIONS="g_hash_table_lookup_node g_hash_table_foreach_remove_or_steal g_hash_table_resize" + +if [ -z $MONO_CHECKOUT ]; then + if [ -e ../../mono.pc.in ]; then + MONO_CHECKOUT=../.. + else + MONO_CHECKOUT=~/cvs/mono/mono + fi +fi + +if [ ! -d $MONO_CHECKOUT ]; then + echo "Cannot find mono checkout; set MONO_CHECKOUT" + exit 1 +fi + +MONO_CHECKOUT="$MONO_CHECKOUT/mono" +RESULTS_FILE=.results + +(for i in `find $MONO_CHECKOUT -iregex \.*.c$`; do + grep -oP "[ \t\(\)]+g_[a-z_]+[ ]{0,1}\([A-Za-z_\&\*\,\(\) ]+\)" $i | + awk 'BEGIN { FS="(" } { print $1 }' | + sed -e 's/[^A-Za-z_]//g' + done +) > $RESULTS_FILE + +if [ ! "x$1" = "x--show-only-mono" ]; then + IMPLEMENTED_FUNCTIONS=`grep -oP "g_[a-z_]+[ ]{0,1}" ../src/glib.h | awk 'BEGIN { FS="(" } { print $1 }'` + + rm -f $RESULTS_FILE.tmp + + for mono_function in `cat $RESULTS_FILE`; do + matched="no" + for implemented_function in $IMPLEMENTED_FUNCTIONS; do + if [ "x$mono_function" = "x$implemented_function" ]; then + matched="yes" + break + fi + done + + for ignore_function in $IGNORE_FUNCTIONS; do + if [ "x$ignore_function" = "x$mono_function" ]; then + matched="yes" + break + fi + done + + if [ "x$matched" = "xno" ]; then + echo $mono_function >> $RESULTS_FILE.tmp + fi + done + + mv $RESULTS_FILE.tmp $RESULTS_FILE +fi + +(for i in `cat $RESULTS_FILE | sort -u`; do + echo "`grep -c $i $RESULTS_FILE` $i"; + done; +) | sort -nr + +rm $RESULTS_FILE + diff --git a/unity-2019.4.24f1-mbe/mono/eglib/unicode-data.h b/unity-2019.4.24f1-mbe/mono/eglib/unicode-data.h new file mode 100644 index 000000000..1cd75f3a9 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/eglib/unicode-data.h @@ -0,0 +1,2152 @@ +/* +This file is automatically generated by ucd.exe. +The source for this generator should be in Mono repository +(mcs/class/corlib/Mono.Globalization.Unicode directory). +*/ + +#ifndef __UNICODE_DATA_H +#define __UNICODE_DATA_H + +#include + + +/* ======== Structures ======== */ +typedef struct { + guint32 codepoint; + guint32 upper; + guint32 title; +} SimpleTitlecaseMapping; +typedef struct { + guint32 start; + guint32 end; +} CodePointRange; +typedef struct { + guint32 upper; + guint32 lower; +} SimpleCaseMapping; + +/* ======== Unicode Categories ======== */ +static const guint8 unicode_category_ranges_count = 11; +static const CodePointRange unicode_category_ranges [] = { +{0x000000, 0x003400}, +{0x004DC0, 0x004E00}, +{0x00A000, 0x00AA80}, +{0x00F900, 0x010000}, +{0x010000, 0x0104C0}, +{0x010800, 0x010A80}, +{0x012000, 0x012480}, +{0x01D000, 0x01D800}, +{0x01F000, 0x01F0C0}, +{0x02F800, 0x02FA40}, +{0x0E0000, 0x0E0200}, +{0, 0}}; +static const guint8 unicode_category_table0 [] = { + /* ==== 0-3400 ==== */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 29,21,21,21,23,21,21,21,22,18,21,25,21,17,21,21, + 13,13,13,13,13,13,13,13,13,13,21,21,25,25,25,21, + 21,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,22,21,18,24,16, + 24,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,22,25,18,25,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 29,21,23,23,23,23,26,26,24,26,5,20,25,1,26,24, + 26,25,15,15,24,5,26,21,24,15,5,19,15,15,15,21, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,25,9,9,9,9,9,9,9,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,25,5,5,5,5,5,5,5,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,5,9,5,9,5,9,5,9, + 5,9,5,9,5,9,5,9,5,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,9,5,9,5,9,5,5, + 5,9,9,5,9,5,9,9,5,9,9,9,5,5,9,9, + 9,9,5,9,9,5,9,9,9,5,5,5,9,9,5,9, + 9,5,9,5,9,5,9,9,5,9,5,5,9,5,9,9, + 5,9,9,9,5,9,5,9,9,5,5,7,9,5,5,5, + 7,7,7,7,9,8,5,9,8,5,9,8,5,9,5,9, + 5,9,5,9,5,9,5,9,5,9,5,9,5,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 5,9,8,5,9,5,9,9,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,5,5,5,5,5,5,9,9,5,9,9,5, + 5,9,5,9,9,9,9,5,9,5,9,5,9,5,9,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,7,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,24,24,24,24,6,6,6,6,6,6,6,6,6,6, + 6,6,24,24,24,24,24,24,24,24,24,24,24,24,24,24, + 6,6,6,6,6,24,24,24,24,24,24,24,6,24,6,24, + 24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 9,5,9,5,6,24,9,5,0,0,6,5,5,5,21,0, + 0,0,0,0,24,24,9,21,9,9,9,0,9,0,9,9, + 5,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,0,9,9,9,9,9,9,9,9,9,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,9, + 5,5,9,9,9,5,5,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 5,5,5,5,9,5,25,9,5,9,9,5,5,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,26,12,12,12,12,12,11,11,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,9,5,9,5,9,5,9,5,9,5,9,5,9,5,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,0,0,0,0,0,0,0,0,0,0,0,0, + 0,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,0,0,6,21,21,21,21,21,21, + 0,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,0,21,17,0,0,0,0,0, + 0,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,17,12, + 21,12,12,21,12,12,21,12,0,0,0,0,0,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0, + 7,7,7,21,21,0,0,0,0,0,0,0,0,0,0,0, + 1,1,1,1,0,0,25,25,25,21,21,23,21,21,26,26, + 12,12,12,12,12,12,12,12,12,12,12,21,0,0,21,21, + 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 6,7,7,7,7,7,7,7,7,7,7,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,0, + 13,13,13,13,13,13,13,13,13,13,21,21,21,21,7,7, + 12,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,21,7,12,12,12,12,12,12,12,1,11,12, + 12,12,12,12,12,6,6,12,12,26,12,12,12,12,7,7, + 13,13,13,13,13,13,13,13,13,13,7,7,7,26,26,7, + 21,21,21,21,21,21,21,21,21,21,21,21,21,21,0,1, + 7,12,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,0,0,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,12,12,12,12,12,12,12,12,12,12, + 12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 13,13,13,13,13,13,13,13,13,13,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,12,12,12,12,12, + 12,12,12,12,6,6,26,21,21,21,6,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,12,12,10,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,0,0,12,7,10,10, + 10,12,12,12,12,12,12,12,12,10,10,10,10,12,0,0, + 7,12,12,12,12,0,0,0,7,7,7,7,7,7,7,7, + 7,7,12,12,21,21,13,13,13,13,13,13,13,13,13,13, + 21,6,7,0,0,0,0,0,0,0,0,7,7,7,7,7, + 0,12,10,10,0,7,7,7,7,7,7,7,7,0,0,7, + 7,0,0,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,0,7,7,7,7,7,7, + 7,0,7,0,0,0,7,7,7,7,0,0,12,7,10,10, + 10,12,12,12,12,0,0,10,10,0,0,10,10,12,7,0, + 0,0,0,0,0,0,0,10,0,0,0,0,7,7,0,7, + 7,7,12,12,0,0,13,13,13,13,13,13,13,13,13,13, + 7,7,23,23,15,15,15,15,15,15,26,0,0,0,0,0, + 0,12,12,10,0,7,7,7,7,7,7,0,0,0,0,7, + 7,0,0,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,0,7,7,7,7,7,7, + 7,0,7,7,0,7,7,0,7,7,0,0,12,0,10,10, + 10,12,12,0,0,0,0,12,12,0,0,12,12,12,0,0, + 0,12,0,0,0,0,0,0,0,7,7,7,7,0,7,0, + 0,0,0,0,0,0,13,13,13,13,13,13,13,13,13,13, + 12,12,7,7,7,12,0,0,0,0,0,0,0,0,0,0, + 0,12,12,10,0,7,7,7,7,7,7,7,7,7,0,7, + 7,7,0,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,0,7,7,7,7,7,7, + 7,0,7,7,0,7,7,7,7,7,0,0,12,7,10,10, + 10,12,12,12,12,12,0,12,12,10,0,10,10,12,0,0, + 7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 7,7,12,12,0,0,13,13,13,13,13,13,13,13,13,13, + 0,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,12,10,10,0,7,7,7,7,7,7,7,7,0,0,7, + 7,0,0,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,0,7,7,7,7,7,7, + 7,0,7,7,0,7,7,7,7,7,0,0,12,7,10,12, + 10,12,12,12,12,0,0,10,10,0,0,10,10,12,0,0, + 0,0,0,0,0,0,12,10,0,0,0,0,7,7,0,7, + 7,7,12,12,0,0,13,13,13,13,13,13,13,13,13,13, + 26,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,12,7,0,7,7,7,7,7,7,0,0,0,7,7, + 7,0,7,7,7,7,0,0,0,7,7,0,7,0,7,7, + 0,0,0,7,7,0,0,0,7,7,7,0,0,0,7,7, + 7,7,7,7,7,7,7,7,7,7,0,0,0,0,10,10, + 12,10,10,0,0,0,10,10,10,0,10,10,10,12,0,0, + 7,0,0,0,0,0,0,10,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,13,13,13,13,13,13,13,13,13,13, + 15,15,15,26,26,26,26,26,26,23,26,0,0,0,0,0, + 0,10,10,10,0,7,7,7,7,7,7,7,7,0,7,7, + 7,0,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,0,7,7,7,7,7,7, + 7,7,7,7,0,7,7,7,7,7,0,0,0,7,12,12, + 12,10,10,10,10,0,12,12,12,0,12,12,12,12,0,0, + 0,0,0,0,0,12,12,0,7,7,0,0,0,0,0,0, + 7,7,12,12,0,0,13,13,13,13,13,13,13,13,13,13, + 0,0,0,0,0,0,0,0,15,15,15,15,15,15,15,26, + 0,0,10,10,0,7,7,7,7,7,7,7,7,0,7,7, + 7,0,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,0,7,7,7,7,7,7, + 7,7,7,7,0,7,7,7,7,7,0,0,12,7,10,12, + 10,10,10,10,10,0,12,10,10,0,10,10,12,12,0,0, + 0,0,0,0,0,10,10,0,0,0,0,0,0,0,7,0, + 7,7,12,12,0,0,13,13,13,13,13,13,13,13,13,13, + 0,26,26,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,10,10,0,7,7,7,7,7,7,7,7,0,7,7, + 7,0,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,0,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,0,0,0,7,10,10, + 10,12,12,12,12,0,10,10,10,0,10,10,10,12,0,0, + 0,0,0,0,0,0,0,10,0,0,0,0,0,0,0,0, + 7,7,12,12,0,0,13,13,13,13,13,13,13,13,13,13, + 15,15,15,15,15,15,0,0,0,26,7,7,7,7,7,7, + 0,0,10,10,0,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,0,0,0,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,0,7,7,7,7,7,7,7,7,7,0,7,0,0, + 7,7,7,7,7,7,7,0,0,0,12,0,0,0,0,10, + 10,10,12,12,12,0,12,0,10,10,10,10,10,10,10,10, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,10,10,21,0,0,0,0,0,0,0,0,0,0,0, + 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,12,7,7,12,12,12,12,12,12,12,0,0,0,0,23, + 7,7,7,7,7,7,6,12,12,12,12,12,12,12,12,21, + 13,13,13,13,13,13,13,13,13,13,21,21,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,7,7,0,7,0,0,7,7,0,7,0,0,7,0,0, + 0,0,0,0,7,7,7,7,0,7,7,7,7,7,7,7, + 0,7,7,7,0,7,0,7,0,0,7,7,0,7,7,7, + 7,12,7,7,12,12,12,12,12,12,0,12,12,7,0,0, + 7,7,7,7,7,0,6,0,12,12,12,12,12,12,0,0, + 13,13,13,13,13,13,13,13,13,13,0,0,7,7,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 7,26,26,26,21,21,21,21,21,21,21,21,21,21,21,21, + 21,21,21,26,26,26,26,26,12,12,26,26,26,26,26,26, + 13,13,13,13,13,13,13,13,13,13,15,15,15,15,15,15, + 15,15,15,15,26,12,26,12,26,12,22,18,22,18,10,10, + 7,7,7,7,7,7,7,7,0,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,0,0,0, + 0,12,12,12,12,12,12,12,12,12,12,12,12,12,12,10, + 12,12,12,12,12,21,12,12,7,7,7,7,0,0,0,0, + 12,12,12,12,12,12,12,12,0,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,0,26,26, + 26,26,26,26,26,26,12,26,26,26,26,26,26,0,26,26, + 21,21,21,21,21,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,10,10,12,12,12, + 12,10,12,12,12,12,12,12,10,12,12,10,10,12,12,7, + 13,13,13,13,13,13,13,13,13,13,21,21,21,21,21,21, + 7,7,7,7,7,7,10,10,12,12,7,7,7,7,12,12, + 12,7,10,10,10,7,7,10,10,10,10,10,10,10,7,7, + 7,12,12,12,12,7,7,7,7,7,7,7,7,7,7,7, + 7,7,12,10,10,12,12,10,10,10,10,10,10,12,7,10, + 13,13,13,13,13,13,13,13,13,13,0,0,0,0,26,26, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,0,0,0,0,0,0,0,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,21,6,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,0,0,0,0,0,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,0,7,7,7,7,0,0, + 7,7,7,7,7,7,7,0,7,0,7,7,7,7,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,0,7,7,7,7,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,0,7,7,7,7,0,0,7,7,7,7,7,7,7,0, + 7,0,7,7,7,7,0,0,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,0,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,0,7,7,7,7,0,0,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,12, + 26,21,21,21,21,21,21,21,21,15,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15,15,15,15,15,15,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 26,26,26,26,26,26,26,26,26,26,0,0,0,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,0,0,0,0,0,0,0,0,0,0,0, + 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,21,21,7, + 7,7,7,7,7,7,7,0,0,0,0,0,0,0,0,0, + 29,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,22,18,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,21,21,21,14,14, + 14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,0,7,7, + 7,7,12,12,12,0,0,0,0,0,0,0,0,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,12,12,12,21,21,0,0,0,0,0,0,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,12,12,0,0,0,0,0,0,0,0,0,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,0,7,7, + 7,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,1,1,10,12,12,12,12,12,12,12,10,10, + 10,10,10,10,10,10,12,10,10,12,12,12,12,12,12,12, + 12,12,12,12,21,21,21,6,21,21,21,23,7,12,0,0, + 13,13,13,13,13,13,13,13,13,13,0,0,0,0,0,0, + 15,15,15,15,15,15,15,15,15,15,0,0,0,0,0,0, + 21,21,21,21,21,21,17,21,21,21,21,12,12,12,29,0, + 13,13,13,13,13,13,13,13,13,13,0,0,0,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,6,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,0,0,0,0,0,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,12,7,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,0,0,0, + 12,12,12,10,10,10,10,12,12,10,10,10,0,0,0,0, + 10,10,12,10,10,10,10,10,10,12,12,12,0,0,0,0, + 26,0,0,0,21,21,13,13,13,13,13,13,13,13,13,13, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,0,0, + 7,7,7,7,7,0,0,0,0,0,0,0,0,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,7,7,7,7,7,7,7,10,10,0,0,0,0,0,0, + 13,13,13,13,13,13,13,13,13,13,0,0,0,0,21,21, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,12,12,10,10,10,0,0,21,21, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 12,12,12,12,10,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,12,10,12,12,12,12,12,10,12,10,10,10, + 10,10,12,10,10,7,7,7,7,7,7,7,0,0,0,0, + 13,13,13,13,13,13,13,13,13,13,21,21,21,21,21,21, + 21,26,26,26,26,26,26,26,26,26,26,12,12,12,12,12, + 12,12,12,12,26,26,26,26,26,26,26,26,26,0,0,0, + 12,12,10,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,10,12,12,12,12,10,10,12,12,10,0,0,0,7,7, + 13,13,13,13,13,13,13,13,13,13,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,10,10,10,10,10,10,10,10,12,12,12,12, + 12,12,12,12,10,10,12,12,0,0,0,21,21,21,21,21, + 13,13,13,13,13,13,13,13,13,13,0,0,0,7,7,7, + 13,13,13,13,13,13,13,13,13,13,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,6,6,6,6,6,6,21,21, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,6,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,5,5,5,5,5,5,5,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 5,5,5,5,5,5,5,5,9,9,9,9,9,9,9,9, + 5,5,5,5,5,5,0,0,9,9,9,9,9,9,0,0, + 5,5,5,5,5,5,5,5,9,9,9,9,9,9,9,9, + 5,5,5,5,5,5,5,5,9,9,9,9,9,9,9,9, + 5,5,5,5,5,5,0,0,9,9,9,9,9,9,0,0, + 5,5,5,5,5,5,5,5,0,9,0,9,0,9,0,9, + 5,5,5,5,5,5,5,5,9,9,9,9,9,9,9,9, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,0,0, + 5,5,5,5,5,5,5,5,8,8,8,8,8,8,8,8, + 5,5,5,5,5,5,5,5,8,8,8,8,8,8,8,8, + 5,5,5,5,5,5,5,5,8,8,8,8,8,8,8,8, + 5,5,5,5,5,0,5,5,9,9,9,9,8,24,5,24, + 24,24,5,5,5,0,5,5,9,9,9,9,8,24,24,24, + 5,5,5,5,0,0,5,5,9,9,9,9,0,24,24,24, + 5,5,5,5,5,5,5,5,9,9,9,9,9,24,24,24, + 0,0,5,5,5,0,5,5,9,9,9,9,8,24,24,0, + 29,29,29,29,29,29,29,29,29,29,29,1,1,1,1,1, + 17,17,17,17,17,17,21,21,20,19,22,20,20,19,22,20, + 21,21,21,21,21,21,21,21,27,28,1,1,1,1,1,29, + 21,21,21,21,21,21,21,21,21,20,19,21,21,21,21,16, + 16,21,21,21,25,22,18,21,21,21,21,21,21,21,21,21, + 21,21,25,21,16,21,21,21,21,21,21,21,21,21,21,29, + 1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1, + 15,5,0,0,15,15,15,15,15,15,25,25,25,22,18,5, + 15,15,15,15,15,15,15,15,15,15,25,25,25,22,18,0, + 6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0, + 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, + 23,23,23,23,23,23,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 12,12,12,12,12,12,12,12,12,12,12,12,12,11,11,11, + 11,12,11,11,11,12,12,12,12,12,12,12,12,12,12,12, + 12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 26,26,9,26,26,26,26,9,26,26,5,9,9,9,5,5, + 9,9,9,5,26,9,26,26,26,9,9,9,9,9,26,26, + 26,26,26,26,9,26,9,26,9,26,9,9,9,9,26,5, + 9,9,9,9,5,7,7,7,7,5,26,26,5,5,9,9, + 25,25,25,25,25,9,5,5,5,5,26,25,26,26,5,26, + 0,0,0,15,15,15,15,15,15,15,15,15,15,15,15,15, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,9,5,14,14,14,14,0,0,0,0,0,0,0, + 25,25,25,25,25,26,26,26,26,26,25,25,26,26,26,26, + 25,26,26,25,26,26,25,26,26,26,26,26,26,26,25,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,25,25, + 26,26,25,26,25,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 26,26,26,26,26,26,26,26,25,25,25,25,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 25,25,26,26,26,26,26,26,26,22,18,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,25,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,25,25,25,25, + 25,25,26,26,26,26,26,26,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 26,26,26,26,26,26,26,26,26,26,26,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15,15,15,15,15,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,25,26,26,26,26,26,26,26,26, + 26,25,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,25,25,25,25,25,25,25,25, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,25, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,0,0, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,0,0,0, + 26,26,26,26,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,26,26,26,26,0,26,26,26,26,0,0,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,0,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,0,26,0,26, + 26,26,26,0,0,0,26,0,26,26,26,26,26,26,26,0, + 0,26,26,26,26,26,26,26,22,18,22,18,22,18,22,18, + 22,18,22,18,22,18,15,15,15,15,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, + 15,15,15,15,26,0,0,0,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 0,26,26,26,26,26,26,26,26,26,26,26,26,26,26,0, + 25,25,25,25,25,22,18,25,25,25,25,0,25,0,0,0, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,22,18,22,18,22,18,22,18,22,18, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,22,18,22,18,22,18,22,18,22,18,22,18,22, + 18,22,18,22,18,22,18,22,18,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,22,18,22,18,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,22,18,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, + 25,25,25,25,25,26,26,25,25,25,25,25,25,0,0,0, + 26,26,26,26,26,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,0, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0, + 9,5,9,9,9,5,5,9,5,9,5,9,5,9,9,9, + 0,5,9,5,5,9,5,5,5,5,5,5,5,6,0,0, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,5,26,26,26,26,26,26,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,21,21,21,21,15,21,21, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,0,0,0,0,0,0,0,0,0,6, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,0,0,0,0,0,0,0,0,0, + 7,7,7,7,7,7,7,0,7,7,7,7,7,7,7,0, + 7,7,7,7,7,7,7,0,7,7,7,7,7,7,7,0, + 7,7,7,7,7,7,7,0,7,7,7,7,7,7,7,0, + 7,7,7,7,7,7,7,0,7,7,7,7,7,7,7,0, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 21,21,20,19,20,19,21,21,21,20,19,21,20,19,21,21, + 21,21,21,21,21,21,21,17,21,21,17,21,20,19,21,21, + 20,19,22,18,22,18,22,18,22,18,21,21,21,21,21,6, + 21,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,0,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,0,0,0,0,0,0,0,0,0,0,0,0, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 26,26,26,26,26,26,26,26,26,26,26,26,0,0,0,0, + 29,21,21,21,26,6,7,14,22,18,22,18,22,18,22,18, + 22,18,26,26,22,18,22,18,22,18,22,18,17,22,18,18, + 26,14,14,14,14,14,14,14,14,14,12,12,12,12,12,12, + 17,6,6,6,6,6,26,26,14,14,14,6,7,21,26,26, + 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,0,0,12,12,24,24,6,6,7, + 17,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,21,6,6,6,7, + 0,0,0,0,0,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,0,0, + 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,0, + 26,26,15,15,15,15,26,26,26,26,26,26,26,26,26,26, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,0,0,0,0,0,0,0,0, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,0,0,0,0,0,0,0,0,0,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,0, + 15,15,15,15,15,15,15,15,15,15,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,0,0,0,0,0,0,0,0,0,0,0,0, + 26,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 15,15,15,15,15,15,15,15,15,15,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,0, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 0}; +static const guint8 unicode_category_table1 [] = { + /* ==== 4DC0-4E00 ==== */ + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 0}; +static const guint8 unicode_category_table2 [] = { + /* ==== A000-AA80 ==== */ + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,6,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,0,0,0, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,6,21,21,21, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 13,13,13,13,13,13,13,13,13,13,7,7,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 0,0,9,5,9,5,9,5,9,5,9,5,9,5,7,12, + 11,11,11,21,0,0,0,0,0,0,0,0,12,12,21,6, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24, + 24,24,24,24,24,24,24,6,6,6,6,6,6,6,6,6, + 24,24,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 5,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 9,5,9,5,9,5,9,5,9,5,9,5,9,5,9,5, + 6,5,5,5,5,5,5,5,5,9,5,9,5,9,9,5, + 9,5,9,5,9,5,9,5,6,24,24,9,5,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,7,7,7,7,7, + 7,7,12,7,7,7,12,7,7,7,7,12,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,10,10,12,12,10,26,26,26,26,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,21,21,21,21,0,0,0,0,0,0,0,0, + 10,10,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,12,0,0,0,0,0,0,0,0,0,21,21, + 13,13,13,13,13,13,13,13,13,13,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 13,13,13,13,13,13,13,13,13,13,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,12,12,12,12,12,12,12,12,21,21, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,12,12,12,12,12,12,12,12,12, + 12,12,10,10,0,0,0,0,0,0,0,0,0,0,0,21, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,12,12,12,12,12,12,10, + 10,12,12,10,10,12,12,0,0,0,0,0,0,0,0,0, + 7,7,7,12,7,7,7,7,7,7,7,7,12,10,0,0, + 13,13,13,13,13,13,13,13,13,13,0,0,21,21,21,21, + 0}; +static const guint8 unicode_category_table3 [] = { + /* ==== F900-10000 ==== */ + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 5,5,5,5,5,5,5,0,0,0,0,0,0,0,0,0, + 0,0,0,5,5,5,5,5,0,0,0,0,0,7,12,7, + 7,7,7,7,7,7,7,7,7,25,7,7,7,7,7,7, + 7,7,7,7,7,7,7,0,7,7,7,7,7,0,7,0, + 7,7,0,7,7,0,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,22,18, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 0,0,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,23,26,0,0, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 21,21,21,21,21,21,21,22,18,21,0,0,0,0,0,0, + 12,12,12,12,12,12,12,0,0,0,0,0,0,0,0,0, + 21,17,17,16,16,22,18,22,18,22,18,22,18,22,18,22, + 18,22,18,22,18,21,21,22,18,21,21,21,21,16,16,16, + 21,21,21,0,21,21,21,21,17,22,18,22,18,22,18,21, + 21,21,25,17,25,25,25,0,21,23,21,21,0,0,0,0, + 7,7,7,7,7,0,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,0,0,1, + 0,21,21,21,23,21,21,21,22,18,21,25,21,17,21,21, + 13,13,13,13,13,13,13,13,13,13,21,21,25,25,25,21, + 21,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,22,21,18,24,16, + 24,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,22,25,18,25,22, + 18,21,22,18,21,21,7,7,7,7,7,7,7,7,7,7, + 6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,0, + 0,0,7,7,7,7,7,7,0,0,7,7,7,7,7,7, + 0,0,7,7,7,7,7,7,0,0,7,7,7,0,0,0, + 23,23,25,24,26,23,23,0,26,25,25,25,25,26,26,0, + 0,0,0,0,0,0,0,0,0,1,1,1,26,26,0}; +static const guint8 unicode_category_table4 [] = { + /* ==== 10000-104C0 ==== */ + 7,7,7,7,7,7,7,7,7,7,7,7,0,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,0,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,0,7,7,0,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0, + 21,21,26,0,0,0,0,15,15,15,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, + 15,15,15,15,0,0,0,26,26,26,26,26,26,26,26,26, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,15,15,15,15,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,15,0,0,0,0,0, + 26,26,26,26,26,26,26,26,26,26,26,26,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,12,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,0, + 15,15,15,15,0,0,0,0,0,0,0,0,0,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,14,7,7,7,7,7,7,7,7,14,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,0,21, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,0,0,0,0,7,7,7,7,7,7,7,7, + 21,14,14,14,14,14,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,0,0, + 13,13,13,13,13,13,13,13,13,13,0}; +static const guint8 unicode_category_table5 [] = { + /* ==== 10800-10A80 ==== */ + 7,7,7,7,7,7,0,0,7,0,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,0,7,7,0,0,0,7,0,0,7, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,15,15,15,15,0,0,0,0,0,21, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,21, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 7,12,12,12,0,12,12,0,0,0,0,0,12,12,12,12, + 7,7,7,7,0,7,7,7,0,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,0,0,0,0,12,12,12,0,0,0,0,12, + 15,15,15,15,15,15,15,15,0,0,0,0,0,0,0,0, + 21,21,21,21,21,21,21,21,21,0}; +static const guint8 unicode_category_table6 [] = { + /* ==== 12000-12480 ==== */ + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,0,0,0,0,0,0,0,0,0,0,0,0,0, + 21,21,21,21,0}; +static const guint8 unicode_category_table7 [] = { + /* ==== 1D000-1D800 ==== */ + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,0,0,0,0,0,0,0,0,0,0, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,0,0,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,10,10,12,12,12,26,26,26,10,10,10, + 10,10,10,1,1,1,1,1,1,1,1,12,12,12,12,12, + 12,12,12,26,26,12,12,12,12,12,12,12,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,12,12,12,12,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,12,12,12,26,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,0,0,0,0,0,0,0,0,0, + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, + 15,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,5,5, + 5,5,5,5,5,0,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,9,0,9,9, + 0,0,9,0,0,9,9,0,0,9,9,9,9,0,9,9, + 9,9,9,9,9,9,5,5,5,5,0,5,0,5,5,5, + 5,5,5,5,0,5,5,5,5,5,5,5,5,5,5,5, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,9,9,0,9,9,9,9,0,0,9,9,9, + 9,9,9,9,9,0,9,9,9,9,9,9,9,0,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,9,9,0,9,9,9,9,0, + 9,9,9,9,9,0,9,0,0,0,9,9,9,9,9,9, + 9,0,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,0,0,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,25,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,25,5,5,5,5, + 5,5,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,25,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,25,5,5,5,5,5,5,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,25,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,25, + 5,5,5,5,5,5,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,25, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,25,5,5,5,5,5,5, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,25,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,25,5,5,5,5,5,5,9,5,0,0,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 0}; +static const guint8 unicode_category_table8 [] = { + /* ==== 1F000-1F0C0 ==== */ + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,0,0,0,0, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,0}; +static const guint8 unicode_category_table9 [] = { + /* ==== 2F800-2FA40 ==== */ + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,0}; +static const guint8 unicode_category_table10 [] = { + /* ==== E0000-E0200 ==== */ + 0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 0}; +static const guint8 *unicode_category [11] = { + unicode_category_table0, + unicode_category_table1, + unicode_category_table2, + unicode_category_table3, + unicode_category_table4, + unicode_category_table5, + unicode_category_table6, + unicode_category_table7, + unicode_category_table8, + unicode_category_table9, + unicode_category_table10 +}; + +static const guint8 simple_case_map_ranges_count = 9; +static const CodePointRange simple_case_map_ranges [] = { +{0x000040, 0x000600}, +{0x001000, 0x0010D0}, +{0x001D00, 0x002000}, +{0x002100, 0x0021C0}, +{0x002480, 0x002500}, +{0x002C00, 0x002D80}, +{0x00A640, 0x00A7C0}, +{0x00FF20, 0x00FF80}, +{0x010400, 0x010480}, +{0, 0}}; +static const guint16 simple_upper_case_mapping_lowarea_table0 [] = { + /* ==== 40-600 ==== */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, + 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0x39C,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x178, + 0,0x100,0,0x102,0,0x104,0,0x106,0,0x108,0,0x10A,0,0x10C,0,0x10E, + 0,0x110,0,0x112,0,0x114,0,0x116,0,0x118,0,0x11A,0,0x11C,0,0x11E, + 0,0x120,0,0x122,0,0x124,0,0x126,0,0x128,0,0x12A,0,0x12C,0,0x12E, + 0,0x49,0,0x132,0,0x134,0,0x136,0,0,0x139,0,0x13B,0,0x13D,0, + 0x13F,0,0x141,0,0x143,0,0x145,0,0x147,0,0,0x14A,0,0x14C,0,0x14E, + 0,0x150,0,0x152,0,0x154,0,0x156,0,0x158,0,0x15A,0,0x15C,0,0x15E, + 0,0x160,0,0x162,0,0x164,0,0x166,0,0x168,0,0x16A,0,0x16C,0,0x16E, + 0,0x170,0,0x172,0,0x174,0,0x176,0,0,0x179,0,0x17B,0,0x17D,0x53, + 0x243,0,0,0x182,0,0x184,0,0,0x187,0,0,0,0x18B,0,0,0, + 0,0,0x191,0,0,0x1F6,0,0,0,0x198,0x23D,0,0,0,0x220,0, + 0,0x1A0,0,0x1A2,0,0x1A4,0,0,0x1A7,0,0,0,0,0x1AC,0,0, + 0x1AF,0,0,0,0x1B3,0,0x1B5,0,0,0x1B8,0,0,0,0x1BC,0,0x1F7, + 0,0,0,0,0,0x1C4,0x1C4,0,0x1C7,0x1C7,0,0x1CA,0x1CA,0,0x1CD,0, + 0x1CF,0,0x1D1,0,0x1D3,0,0x1D5,0,0x1D7,0,0x1D9,0,0x1DB,0x18E,0,0x1DE, + 0,0x1E0,0,0x1E2,0,0x1E4,0,0x1E6,0,0x1E8,0,0x1EA,0,0x1EC,0,0x1EE, + 0,0,0x1F1,0x1F1,0,0x1F4,0,0,0,0x1F8,0,0x1FA,0,0x1FC,0,0x1FE, + 0,0x200,0,0x202,0,0x204,0,0x206,0,0x208,0,0x20A,0,0x20C,0,0x20E, + 0,0x210,0,0x212,0,0x214,0,0x216,0,0x218,0,0x21A,0,0x21C,0,0x21E, + 0,0,0,0x222,0,0x224,0,0x226,0,0x228,0,0x22A,0,0x22C,0,0x22E, + 0,0x230,0,0x232,0,0,0,0,0,0,0,0,0x23B,0,0,0, + 0,0,0x241,0,0,0,0,0x246,0,0x248,0,0x24A,0,0x24C,0,0x24E, + 0x2C6F,0x2C6D,0,0x181,0x186,0,0x189,0x18A,0,0x18F,0,0x190,0,0,0,0, + 0x193,0,0,0x194,0,0,0,0,0x197,0x196,0,0x2C62,0,0,0,0x19C, + 0,0x2C6E,0x19D,0,0,0x19F,0,0,0,0,0,0,0,0x2C64,0,0, + 0x1A6,0,0,0x1A9,0,0,0,0,0x1AE,0x244,0x1B1,0x1B2,0x245,0,0,0, + 0,0,0x1B7,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0x399,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0x370,0,0x372,0,0,0,0x376,0,0,0,0x3FD,0x3FE,0x3FF,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0x386,0x388,0x389,0x38A, + 0,0x391,0x392,0x393,0x394,0x395,0x396,0x397,0x398,0x399,0x39A,0x39B,0x39C,0x39D,0x39E,0x39F, + 0x3A0,0x3A1,0x3A3,0x3A3,0x3A4,0x3A5,0x3A6,0x3A7,0x3A8,0x3A9,0x3AA,0x3AB,0x38C,0x38E,0x38F,0, + 0x392,0x398,0,0,0,0x3A6,0x3A0,0x3CF,0,0x3D8,0,0x3DA,0,0x3DC,0,0x3DE, + 0,0x3E0,0,0x3E2,0,0x3E4,0,0x3E6,0,0x3E8,0,0x3EA,0,0x3EC,0,0x3EE, + 0x39A,0x3A1,0x3F9,0,0,0x395,0,0,0x3F7,0,0,0x3FA,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0x410,0x411,0x412,0x413,0x414,0x415,0x416,0x417,0x418,0x419,0x41A,0x41B,0x41C,0x41D,0x41E,0x41F, + 0x420,0x421,0x422,0x423,0x424,0x425,0x426,0x427,0x428,0x429,0x42A,0x42B,0x42C,0x42D,0x42E,0x42F, + 0x400,0x401,0x402,0x403,0x404,0x405,0x406,0x407,0x408,0x409,0x40A,0x40B,0x40C,0x40D,0x40E,0x40F, + 0,0x460,0,0x462,0,0x464,0,0x466,0,0x468,0,0x46A,0,0x46C,0,0x46E, + 0,0x470,0,0x472,0,0x474,0,0x476,0,0x478,0,0x47A,0,0x47C,0,0x47E, + 0,0x480,0,0,0,0,0,0,0,0,0,0x48A,0,0x48C,0,0x48E, + 0,0x490,0,0x492,0,0x494,0,0x496,0,0x498,0,0x49A,0,0x49C,0,0x49E, + 0,0x4A0,0,0x4A2,0,0x4A4,0,0x4A6,0,0x4A8,0,0x4AA,0,0x4AC,0,0x4AE, + 0,0x4B0,0,0x4B2,0,0x4B4,0,0x4B6,0,0x4B8,0,0x4BA,0,0x4BC,0,0x4BE, + 0,0,0x4C1,0,0x4C3,0,0x4C5,0,0x4C7,0,0x4C9,0,0x4CB,0,0x4CD,0x4C0, + 0,0x4D0,0,0x4D2,0,0x4D4,0,0x4D6,0,0x4D8,0,0x4DA,0,0x4DC,0,0x4DE, + 0,0x4E0,0,0x4E2,0,0x4E4,0,0x4E6,0,0x4E8,0,0x4EA,0,0x4EC,0,0x4EE, + 0,0x4F0,0,0x4F2,0,0x4F4,0,0x4F6,0,0x4F8,0,0x4FA,0,0x4FC,0,0x4FE, + 0,0x500,0,0x502,0,0x504,0,0x506,0,0x508,0,0x50A,0,0x50C,0,0x50E, + 0,0x510,0,0x512,0,0x514,0,0x516,0,0x518,0,0x51A,0,0x51C,0,0x51E, + 0,0x520,0,0x522,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0x531,0x532,0x533,0x534,0x535,0x536,0x537,0x538,0x539,0x53A,0x53B,0x53C,0x53D,0x53E,0x53F, + 0x540,0x541,0x542,0x543,0x544,0x545,0x546,0x547,0x548,0x549,0x54A,0x54B,0x54C,0x54D,0x54E,0x54F, + 0x550,0x551,0x552,0x553,0x554,0x555,0x556,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0}; +static const guint16 simple_upper_case_mapping_lowarea_table1 [] = { + /* ==== 1000-10D0 ==== */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0}; +static const guint16 simple_upper_case_mapping_lowarea_table2 [] = { + /* ==== 1D00-2000 ==== */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0xA77D,0,0,0,0x2C63,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0x1E00,0,0x1E02,0,0x1E04,0,0x1E06,0,0x1E08,0,0x1E0A,0,0x1E0C,0,0x1E0E, + 0,0x1E10,0,0x1E12,0,0x1E14,0,0x1E16,0,0x1E18,0,0x1E1A,0,0x1E1C,0,0x1E1E, + 0,0x1E20,0,0x1E22,0,0x1E24,0,0x1E26,0,0x1E28,0,0x1E2A,0,0x1E2C,0,0x1E2E, + 0,0x1E30,0,0x1E32,0,0x1E34,0,0x1E36,0,0x1E38,0,0x1E3A,0,0x1E3C,0,0x1E3E, + 0,0x1E40,0,0x1E42,0,0x1E44,0,0x1E46,0,0x1E48,0,0x1E4A,0,0x1E4C,0,0x1E4E, + 0,0x1E50,0,0x1E52,0,0x1E54,0,0x1E56,0,0x1E58,0,0x1E5A,0,0x1E5C,0,0x1E5E, + 0,0x1E60,0,0x1E62,0,0x1E64,0,0x1E66,0,0x1E68,0,0x1E6A,0,0x1E6C,0,0x1E6E, + 0,0x1E70,0,0x1E72,0,0x1E74,0,0x1E76,0,0x1E78,0,0x1E7A,0,0x1E7C,0,0x1E7E, + 0,0x1E80,0,0x1E82,0,0x1E84,0,0x1E86,0,0x1E88,0,0x1E8A,0,0x1E8C,0,0x1E8E, + 0,0x1E90,0,0x1E92,0,0x1E94,0,0,0,0,0,0x1E60,0,0,0,0, + 0,0x1EA0,0,0x1EA2,0,0x1EA4,0,0x1EA6,0,0x1EA8,0,0x1EAA,0,0x1EAC,0,0x1EAE, + 0,0x1EB0,0,0x1EB2,0,0x1EB4,0,0x1EB6,0,0x1EB8,0,0x1EBA,0,0x1EBC,0,0x1EBE, + 0,0x1EC0,0,0x1EC2,0,0x1EC4,0,0x1EC6,0,0x1EC8,0,0x1ECA,0,0x1ECC,0,0x1ECE, + 0,0x1ED0,0,0x1ED2,0,0x1ED4,0,0x1ED6,0,0x1ED8,0,0x1EDA,0,0x1EDC,0,0x1EDE, + 0,0x1EE0,0,0x1EE2,0,0x1EE4,0,0x1EE6,0,0x1EE8,0,0x1EEA,0,0x1EEC,0,0x1EEE, + 0,0x1EF0,0,0x1EF2,0,0x1EF4,0,0x1EF6,0,0x1EF8,0,0x1EFA,0,0x1EFC,0,0x1EFE, + 0x1F08,0x1F09,0x1F0A,0x1F0B,0x1F0C,0x1F0D,0x1F0E,0x1F0F,0,0,0,0,0,0,0,0, + 0x1F18,0x1F19,0x1F1A,0x1F1B,0x1F1C,0x1F1D,0,0,0,0,0,0,0,0,0,0, +0x1F28,0x1F29,0x1F2A,0x1F2B,0x1F2C,0x1F2D,0x1F2E,0x1F2F,0,0,0,0,0,0,0,0, + 0x1F38,0x1F39,0x1F3A,0x1F3B,0x1F3C,0x1F3D,0x1F3E,0x1F3F,0,0,0,0,0,0,0,0, + 0x1F48,0x1F49,0x1F4A,0x1F4B,0x1F4C,0x1F4D,0,0,0,0,0,0,0,0,0,0, +0,0x1F59,0,0x1F5B,0,0x1F5D,0,0x1F5F,0,0,0,0,0,0,0,0, + 0x1F68,0x1F69,0x1F6A,0x1F6B,0x1F6C,0x1F6D,0x1F6E,0x1F6F,0,0,0,0,0,0,0,0, + 0x1FBA,0x1FBB,0x1FC8,0x1FC9,0x1FCA,0x1FCB,0x1FDA,0x1FDB,0x1FF8,0x1FF9,0x1FEA,0x1FEB,0x1FFA,0x1FFB,0,0, +0x1F88,0x1F89,0x1F8A,0x1F8B,0x1F8C,0x1F8D,0x1F8E,0x1F8F,0,0,0,0,0,0,0,0, + 0x1F98,0x1F99,0x1F9A,0x1F9B,0x1F9C,0x1F9D,0x1F9E,0x1F9F,0,0,0,0,0,0,0,0, + 0x1FA8,0x1FA9,0x1FAA,0x1FAB,0x1FAC,0x1FAD,0x1FAE,0x1FAF,0,0,0,0,0,0,0,0, + 0x1FB8,0x1FB9,0,0x1FBC,0,0,0,0,0,0,0,0,0,0,0x399,0, + 0,0,0,0x1FCC,0,0,0,0,0,0,0,0,0,0,0,0, + 0x1FD8,0x1FD9,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0x1FE8,0x1FE9,0,0,0,0x1FEC,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0x1FFC,0,0,0,0,0,0,0,0,0,0,0,0}; +static const guint16 simple_upper_case_mapping_lowarea_table3 [] = { + /* ==== 2100-21C0 ==== */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x2132,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0x2160,0x2161,0x2162,0x2163,0x2164,0x2165,0x2166,0x2167,0x2168,0x2169,0x216A,0x216B,0x216C,0x216D,0x216E,0x216F, + 0,0,0,0,0x2183,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0}; +static const guint16 simple_upper_case_mapping_lowarea_table4 [] = { + /* ==== 2480-2500 ==== */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0x24B6,0x24B7,0x24B8,0x24B9,0x24BA,0x24BB,0x24BC,0x24BD,0x24BE,0x24BF,0x24C0,0x24C1,0x24C2,0x24C3,0x24C4,0x24C5, + 0x24C6,0x24C7,0x24C8,0x24C9,0x24CA,0x24CB,0x24CC,0x24CD,0x24CE,0x24CF,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0}; +static const guint16 simple_upper_case_mapping_lowarea_table5 [] = { + /* ==== 2C00-2D80 ==== */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0x2C00,0x2C01,0x2C02,0x2C03,0x2C04,0x2C05,0x2C06,0x2C07,0x2C08,0x2C09,0x2C0A,0x2C0B,0x2C0C,0x2C0D,0x2C0E,0x2C0F, + 0x2C10,0x2C11,0x2C12,0x2C13,0x2C14,0x2C15,0x2C16,0x2C17,0x2C18,0x2C19,0x2C1A,0x2C1B,0x2C1C,0x2C1D,0x2C1E,0x2C1F, + 0x2C20,0x2C21,0x2C22,0x2C23,0x2C24,0x2C25,0x2C26,0x2C27,0x2C28,0x2C29,0x2C2A,0x2C2B,0x2C2C,0x2C2D,0x2C2E,0, +0,0x2C60,0,0,0,0x23A,0x23E,0,0x2C67,0,0x2C69,0,0x2C6B,0,0,0, + 0,0,0,0x2C72,0,0,0x2C75,0,0,0,0,0,0,0,0,0, +0,0x2C80,0,0x2C82,0,0x2C84,0,0x2C86,0,0x2C88,0,0x2C8A,0,0x2C8C,0,0x2C8E, + 0,0x2C90,0,0x2C92,0,0x2C94,0,0x2C96,0,0x2C98,0,0x2C9A,0,0x2C9C,0,0x2C9E, + 0,0x2CA0,0,0x2CA2,0,0x2CA4,0,0x2CA6,0,0x2CA8,0,0x2CAA,0,0x2CAC,0,0x2CAE, + 0,0x2CB0,0,0x2CB2,0,0x2CB4,0,0x2CB6,0,0x2CB8,0,0x2CBA,0,0x2CBC,0,0x2CBE, + 0,0x2CC0,0,0x2CC2,0,0x2CC4,0,0x2CC6,0,0x2CC8,0,0x2CCA,0,0x2CCC,0,0x2CCE, + 0,0x2CD0,0,0x2CD2,0,0x2CD4,0,0x2CD6,0,0x2CD8,0,0x2CDA,0,0x2CDC,0,0x2CDE, + 0,0x2CE0,0,0x2CE2,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0x10A0,0x10A1,0x10A2,0x10A3,0x10A4,0x10A5,0x10A6,0x10A7,0x10A8,0x10A9,0x10AA,0x10AB,0x10AC,0x10AD,0x10AE,0x10AF, + 0x10B0,0x10B1,0x10B2,0x10B3,0x10B4,0x10B5,0x10B6,0x10B7,0x10B8,0x10B9,0x10BA,0x10BB,0x10BC,0x10BD,0x10BE,0x10BF, + 0x10C0,0x10C1,0x10C2,0x10C3,0x10C4,0x10C5,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0}; +static const guint16 simple_upper_case_mapping_lowarea_table6 [] = { + /* ==== A640-A7C0 ==== */ + 0,0xA640,0,0xA642,0,0xA644,0,0xA646,0,0xA648,0,0xA64A,0,0xA64C,0,0xA64E, + 0,0xA650,0,0xA652,0,0xA654,0,0xA656,0,0xA658,0,0xA65A,0,0xA65C,0,0xA65E, + 0,0,0,0xA662,0,0xA664,0,0xA666,0,0xA668,0,0xA66A,0,0xA66C,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0xA680,0,0xA682,0,0xA684,0,0xA686,0,0xA688,0,0xA68A,0,0xA68C,0,0xA68E, + 0,0xA690,0,0xA692,0,0xA694,0,0xA696,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0xA722,0,0xA724,0,0xA726,0,0xA728,0,0xA72A,0,0xA72C,0,0xA72E, + 0,0,0,0xA732,0,0xA734,0,0xA736,0,0xA738,0,0xA73A,0,0xA73C,0,0xA73E, + 0,0xA740,0,0xA742,0,0xA744,0,0xA746,0,0xA748,0,0xA74A,0,0xA74C,0,0xA74E, + 0,0xA750,0,0xA752,0,0xA754,0,0xA756,0,0xA758,0,0xA75A,0,0xA75C,0,0xA75E, + 0,0xA760,0,0xA762,0,0xA764,0,0xA766,0,0xA768,0,0xA76A,0,0xA76C,0,0xA76E, + 0,0,0,0,0,0,0,0,0,0,0xA779,0,0xA77B,0,0,0xA77E, + 0,0xA780,0,0xA782,0,0xA784,0,0xA786,0,0,0,0,0xA78B,0}; +static const guint16 simple_upper_case_mapping_lowarea_table7 [] = { + /* ==== FF20-FF80 ==== */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0xFF21,0xFF22,0xFF23,0xFF24,0xFF25,0xFF26,0xFF27,0xFF28,0xFF29,0xFF2A,0xFF2B,0xFF2C,0xFF2D,0xFF2E,0xFF2F, + 0xFF30,0xFF31,0xFF32,0xFF33,0xFF34,0xFF35,0xFF36,0xFF37,0xFF38,0xFF39,0xFF3A,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0}; +static const guint16 *simple_upper_case_mapping_lowarea [] = { + simple_upper_case_mapping_lowarea_table0, + simple_upper_case_mapping_lowarea_table1, + simple_upper_case_mapping_lowarea_table2, + simple_upper_case_mapping_lowarea_table3, + simple_upper_case_mapping_lowarea_table4, + simple_upper_case_mapping_lowarea_table5, + simple_upper_case_mapping_lowarea_table6, + simple_upper_case_mapping_lowarea_table7}; +static const int simple_upper_case_mapping_lowarea_table_count = 8; + +static const guint32 simple_upper_case_mapping_higharea_table0 [] = { + /* ==== 10400-10480 ==== */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0x10400,0x10401,0x10402,0x10403,0x10404,0x10405,0x10406,0x10407, + 0x10408,0x10409,0x1040A,0x1040B,0x1040C,0x1040D,0x1040E,0x1040F,0x10410,0x10411,0x10412,0x10413,0x10414,0x10415,0x10416,0x10417, + 0x10418,0x10419,0x1041A,0x1041B,0x1041C,0x1041D,0x1041E,0x1041F,0x10420,0x10421,0x10422,0x10423,0x10424,0x10425,0x10426,0x10427, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0}; +static const guint32 *simple_upper_case_mapping_higharea [] = { + simple_upper_case_mapping_higharea_table0}; + +static const guint16 simple_lower_case_mapping_lowarea_table0 [] = { + /* ==== 40-600 ==== */ + 0,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, + 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0x101,0,0x103,0,0x105,0,0x107,0,0x109,0,0x10B,0,0x10D,0,0x10F,0, + 0x111,0,0x113,0,0x115,0,0x117,0,0x119,0,0x11B,0,0x11D,0,0x11F,0, + 0x121,0,0x123,0,0x125,0,0x127,0,0x129,0,0x12B,0,0x12D,0,0x12F,0, + 0x69,0,0x133,0,0x135,0,0x137,0,0,0x13A,0,0x13C,0,0x13E,0,0x140, + 0,0x142,0,0x144,0,0x146,0,0x148,0,0,0x14B,0,0x14D,0,0x14F,0, + 0x151,0,0x153,0,0x155,0,0x157,0,0x159,0,0x15B,0,0x15D,0,0x15F,0, + 0x161,0,0x163,0,0x165,0,0x167,0,0x169,0,0x16B,0,0x16D,0,0x16F,0, + 0x171,0,0x173,0,0x175,0,0x177,0,0xFF,0x17A,0,0x17C,0,0x17E,0,0, + 0,0x253,0x183,0,0x185,0,0x254,0x188,0,0x256,0x257,0x18C,0,0,0x1DD,0x259, + 0x25B,0x192,0,0x260,0x263,0,0x269,0x268,0x199,0,0,0,0x26F,0x272,0,0x275, + 0x1A1,0,0x1A3,0,0x1A5,0,0x280,0x1A8,0,0x283,0,0,0x1AD,0,0x288,0x1B0, + 0,0x28A,0x28B,0x1B4,0,0x1B6,0,0x292,0x1B9,0,0,0,0x1BD,0,0,0, + 0,0,0,0,0x1C6,0x1C6,0,0x1C9,0x1C9,0,0x1CC,0x1CC,0,0x1CE,0,0x1D0, + 0,0x1D2,0,0x1D4,0,0x1D6,0,0x1D8,0,0x1DA,0,0x1DC,0,0,0x1DF,0, + 0x1E1,0,0x1E3,0,0x1E5,0,0x1E7,0,0x1E9,0,0x1EB,0,0x1ED,0,0x1EF,0, + 0,0x1F3,0x1F3,0,0x1F5,0,0x195,0x1BF,0x1F9,0,0x1FB,0,0x1FD,0,0x1FF,0, + 0x201,0,0x203,0,0x205,0,0x207,0,0x209,0,0x20B,0,0x20D,0,0x20F,0, + 0x211,0,0x213,0,0x215,0,0x217,0,0x219,0,0x21B,0,0x21D,0,0x21F,0, + 0x19E,0,0x223,0,0x225,0,0x227,0,0x229,0,0x22B,0,0x22D,0,0x22F,0, + 0x231,0,0x233,0,0,0,0,0,0,0,0x2C65,0x23C,0,0x19A,0x2C66,0, + 0,0x242,0,0x180,0x289,0x28C,0x247,0,0x249,0,0x24B,0,0x24D,0,0x24F,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0x371,0,0x373,0,0,0,0x377,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0x3AC,0,0x3AD,0x3AE,0x3AF,0,0x3CC,0,0x3CD,0x3CE, + 0,0x3B1,0x3B2,0x3B3,0x3B4,0x3B5,0x3B6,0x3B7,0x3B8,0x3B9,0x3BA,0x3BB,0x3BC,0x3BD,0x3BE,0x3BF, + 0x3C0,0x3C1,0,0x3C3,0x3C4,0x3C5,0x3C6,0x3C7,0x3C8,0x3C9,0x3CA,0x3CB,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x3D7, + 0,0,0,0,0,0,0,0,0x3D9,0,0x3DB,0,0x3DD,0,0x3DF,0, + 0x3E1,0,0x3E3,0,0x3E5,0,0x3E7,0,0x3E9,0,0x3EB,0,0x3ED,0,0x3EF,0, + 0,0,0,0,0x3B8,0,0,0x3F8,0,0x3F2,0x3FB,0,0,0x37B,0x37C,0x37D, + 0x450,0x451,0x452,0x453,0x454,0x455,0x456,0x457,0x458,0x459,0x45A,0x45B,0x45C,0x45D,0x45E,0x45F, + 0x430,0x431,0x432,0x433,0x434,0x435,0x436,0x437,0x438,0x439,0x43A,0x43B,0x43C,0x43D,0x43E,0x43F, + 0x440,0x441,0x442,0x443,0x444,0x445,0x446,0x447,0x448,0x449,0x44A,0x44B,0x44C,0x44D,0x44E,0x44F, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0x461,0,0x463,0,0x465,0,0x467,0,0x469,0,0x46B,0,0x46D,0,0x46F,0, + 0x471,0,0x473,0,0x475,0,0x477,0,0x479,0,0x47B,0,0x47D,0,0x47F,0, + 0x481,0,0,0,0,0,0,0,0,0,0x48B,0,0x48D,0,0x48F,0, + 0x491,0,0x493,0,0x495,0,0x497,0,0x499,0,0x49B,0,0x49D,0,0x49F,0, + 0x4A1,0,0x4A3,0,0x4A5,0,0x4A7,0,0x4A9,0,0x4AB,0,0x4AD,0,0x4AF,0, + 0x4B1,0,0x4B3,0,0x4B5,0,0x4B7,0,0x4B9,0,0x4BB,0,0x4BD,0,0x4BF,0, + 0x4CF,0x4C2,0,0x4C4,0,0x4C6,0,0x4C8,0,0x4CA,0,0x4CC,0,0x4CE,0,0, + 0x4D1,0,0x4D3,0,0x4D5,0,0x4D7,0,0x4D9,0,0x4DB,0,0x4DD,0,0x4DF,0, + 0x4E1,0,0x4E3,0,0x4E5,0,0x4E7,0,0x4E9,0,0x4EB,0,0x4ED,0,0x4EF,0, + 0x4F1,0,0x4F3,0,0x4F5,0,0x4F7,0,0x4F9,0,0x4FB,0,0x4FD,0,0x4FF,0, + 0x501,0,0x503,0,0x505,0,0x507,0,0x509,0,0x50B,0,0x50D,0,0x50F,0, + 0x511,0,0x513,0,0x515,0,0x517,0,0x519,0,0x51B,0,0x51D,0,0x51F,0, + 0x521,0,0x523,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0x561,0x562,0x563,0x564,0x565,0x566,0x567,0x568,0x569,0x56A,0x56B,0x56C,0x56D,0x56E,0x56F, + 0x570,0x571,0x572,0x573,0x574,0x575,0x576,0x577,0x578,0x579,0x57A,0x57B,0x57C,0x57D,0x57E,0x57F, + 0x580,0x581,0x582,0x583,0x584,0x585,0x586,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0}; +static const guint16 simple_lower_case_mapping_lowarea_table1 [] = { + /* ==== 1000-10D0 ==== */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0x2D00,0x2D01,0x2D02,0x2D03,0x2D04,0x2D05,0x2D06,0x2D07,0x2D08,0x2D09,0x2D0A,0x2D0B,0x2D0C,0x2D0D,0x2D0E,0x2D0F, + 0x2D10,0x2D11,0x2D12,0x2D13,0x2D14,0x2D15,0x2D16,0x2D17,0x2D18,0x2D19,0x2D1A,0x2D1B,0x2D1C,0x2D1D,0x2D1E,0x2D1F, + 0x2D20,0x2D21,0x2D22,0x2D23,0x2D24,0x2D25,0}; +static const guint16 simple_lower_case_mapping_lowarea_table2 [] = { + /* ==== 1D00-2000 ==== */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0x1E01,0,0x1E03,0,0x1E05,0,0x1E07,0,0x1E09,0,0x1E0B,0,0x1E0D,0,0x1E0F,0, + 0x1E11,0,0x1E13,0,0x1E15,0,0x1E17,0,0x1E19,0,0x1E1B,0,0x1E1D,0,0x1E1F,0, + 0x1E21,0,0x1E23,0,0x1E25,0,0x1E27,0,0x1E29,0,0x1E2B,0,0x1E2D,0,0x1E2F,0, + 0x1E31,0,0x1E33,0,0x1E35,0,0x1E37,0,0x1E39,0,0x1E3B,0,0x1E3D,0,0x1E3F,0, + 0x1E41,0,0x1E43,0,0x1E45,0,0x1E47,0,0x1E49,0,0x1E4B,0,0x1E4D,0,0x1E4F,0, + 0x1E51,0,0x1E53,0,0x1E55,0,0x1E57,0,0x1E59,0,0x1E5B,0,0x1E5D,0,0x1E5F,0, + 0x1E61,0,0x1E63,0,0x1E65,0,0x1E67,0,0x1E69,0,0x1E6B,0,0x1E6D,0,0x1E6F,0, + 0x1E71,0,0x1E73,0,0x1E75,0,0x1E77,0,0x1E79,0,0x1E7B,0,0x1E7D,0,0x1E7F,0, + 0x1E81,0,0x1E83,0,0x1E85,0,0x1E87,0,0x1E89,0,0x1E8B,0,0x1E8D,0,0x1E8F,0, + 0x1E91,0,0x1E93,0,0x1E95,0,0,0,0,0,0,0,0,0,0xDF,0, + 0x1EA1,0,0x1EA3,0,0x1EA5,0,0x1EA7,0,0x1EA9,0,0x1EAB,0,0x1EAD,0,0x1EAF,0, + 0x1EB1,0,0x1EB3,0,0x1EB5,0,0x1EB7,0,0x1EB9,0,0x1EBB,0,0x1EBD,0,0x1EBF,0, + 0x1EC1,0,0x1EC3,0,0x1EC5,0,0x1EC7,0,0x1EC9,0,0x1ECB,0,0x1ECD,0,0x1ECF,0, + 0x1ED1,0,0x1ED3,0,0x1ED5,0,0x1ED7,0,0x1ED9,0,0x1EDB,0,0x1EDD,0,0x1EDF,0, + 0x1EE1,0,0x1EE3,0,0x1EE5,0,0x1EE7,0,0x1EE9,0,0x1EEB,0,0x1EED,0,0x1EEF,0, + 0x1EF1,0,0x1EF3,0,0x1EF5,0,0x1EF7,0,0x1EF9,0,0x1EFB,0,0x1EFD,0,0x1EFF,0, + 0,0,0,0,0,0,0,0,0x1F00,0x1F01,0x1F02,0x1F03,0x1F04,0x1F05,0x1F06,0x1F07, + 0,0,0,0,0,0,0,0,0x1F10,0x1F11,0x1F12,0x1F13,0x1F14,0x1F15,0,0, +0,0,0,0,0,0,0,0,0x1F20,0x1F21,0x1F22,0x1F23,0x1F24,0x1F25,0x1F26,0x1F27, + 0,0,0,0,0,0,0,0,0x1F30,0x1F31,0x1F32,0x1F33,0x1F34,0x1F35,0x1F36,0x1F37, + 0,0,0,0,0,0,0,0,0x1F40,0x1F41,0x1F42,0x1F43,0x1F44,0x1F45,0,0, +0,0,0,0,0,0,0,0,0,0x1F51,0,0x1F53,0,0x1F55,0,0x1F57, + 0,0,0,0,0,0,0,0,0x1F60,0x1F61,0x1F62,0x1F63,0x1F64,0x1F65,0x1F66,0x1F67, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0x1F80,0x1F81,0x1F82,0x1F83,0x1F84,0x1F85,0x1F86,0x1F87, + 0,0,0,0,0,0,0,0,0x1F90,0x1F91,0x1F92,0x1F93,0x1F94,0x1F95,0x1F96,0x1F97, + 0,0,0,0,0,0,0,0,0x1FA0,0x1FA1,0x1FA2,0x1FA3,0x1FA4,0x1FA5,0x1FA6,0x1FA7, + 0,0,0,0,0,0,0,0,0x1FB0,0x1FB1,0x1F70,0x1F71,0x1FB3,0,0,0, + 0,0,0,0,0,0,0,0,0x1F72,0x1F73,0x1F74,0x1F75,0x1FC3,0,0,0, + 0,0,0,0,0,0,0,0,0x1FD0,0x1FD1,0x1F76,0x1F77,0,0,0,0, + 0,0,0,0,0,0,0,0,0x1FE0,0x1FE1,0x1F7A,0x1F7B,0x1FE5,0,0,0, + 0,0,0,0,0,0,0,0,0x1F78,0x1F79,0x1F7C,0x1F7D,0x1FF3,0,0,0}; +static const guint16 simple_lower_case_mapping_lowarea_table3 [] = { + /* ==== 2100-21C0 ==== */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0x3C9,0,0,0,0x6B,0xE5,0,0,0,0, + 0,0,0x214E,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0x2170,0x2171,0x2172,0x2173,0x2174,0x2175,0x2176,0x2177,0x2178,0x2179,0x217A,0x217B,0x217C,0x217D,0x217E,0x217F, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0x2184,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0}; +static const guint16 simple_lower_case_mapping_lowarea_table4 [] = { + /* ==== 2480-2500 ==== */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0x24D0,0x24D1,0x24D2,0x24D3,0x24D4,0x24D5,0x24D6,0x24D7,0x24D8,0x24D9, + 0x24DA,0x24DB,0x24DC,0x24DD,0x24DE,0x24DF,0x24E0,0x24E1,0x24E2,0x24E3,0x24E4,0x24E5,0x24E6,0x24E7,0x24E8,0x24E9, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0}; +static const guint16 simple_lower_case_mapping_lowarea_table5 [] = { + /* ==== 2C00-2D80 ==== */ + 0x2C30,0x2C31,0x2C32,0x2C33,0x2C34,0x2C35,0x2C36,0x2C37,0x2C38,0x2C39,0x2C3A,0x2C3B,0x2C3C,0x2C3D,0x2C3E,0x2C3F, + 0x2C40,0x2C41,0x2C42,0x2C43,0x2C44,0x2C45,0x2C46,0x2C47,0x2C48,0x2C49,0x2C4A,0x2C4B,0x2C4C,0x2C4D,0x2C4E,0x2C4F, + 0x2C50,0x2C51,0x2C52,0x2C53,0x2C54,0x2C55,0x2C56,0x2C57,0x2C58,0x2C59,0x2C5A,0x2C5B,0x2C5C,0x2C5D,0x2C5E,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0x2C61,0,0x26B,0x1D7D,0x27D,0,0,0x2C68,0,0x2C6A,0,0x2C6C,0,0x251,0x271,0x250, + 0,0,0x2C73,0,0,0x2C76,0,0,0,0,0,0,0,0,0,0, +0x2C81,0,0x2C83,0,0x2C85,0,0x2C87,0,0x2C89,0,0x2C8B,0,0x2C8D,0,0x2C8F,0, + 0x2C91,0,0x2C93,0,0x2C95,0,0x2C97,0,0x2C99,0,0x2C9B,0,0x2C9D,0,0x2C9F,0, + 0x2CA1,0,0x2CA3,0,0x2CA5,0,0x2CA7,0,0x2CA9,0,0x2CAB,0,0x2CAD,0,0x2CAF,0, + 0x2CB1,0,0x2CB3,0,0x2CB5,0,0x2CB7,0,0x2CB9,0,0x2CBB,0,0x2CBD,0,0x2CBF,0, + 0x2CC1,0,0x2CC3,0,0x2CC5,0,0x2CC7,0,0x2CC9,0,0x2CCB,0,0x2CCD,0,0x2CCF,0, + 0x2CD1,0,0x2CD3,0,0x2CD5,0,0x2CD7,0,0x2CD9,0,0x2CDB,0,0x2CDD,0,0x2CDF,0, + 0x2CE1,0,0x2CE3,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0}; +static const guint16 simple_lower_case_mapping_lowarea_table6 [] = { + /* ==== A640-A7C0 ==== */ + 0xA641,0,0xA643,0,0xA645,0,0xA647,0,0xA649,0,0xA64B,0,0xA64D,0,0xA64F,0, + 0xA651,0,0xA653,0,0xA655,0,0xA657,0,0xA659,0,0xA65B,0,0xA65D,0,0xA65F,0, + 0,0,0xA663,0,0xA665,0,0xA667,0,0xA669,0,0xA66B,0,0xA66D,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0xA681,0,0xA683,0,0xA685,0,0xA687,0,0xA689,0,0xA68B,0,0xA68D,0,0xA68F,0, + 0xA691,0,0xA693,0,0xA695,0,0xA697,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0xA723,0,0xA725,0,0xA727,0,0xA729,0,0xA72B,0,0xA72D,0,0xA72F,0, + 0,0,0xA733,0,0xA735,0,0xA737,0,0xA739,0,0xA73B,0,0xA73D,0,0xA73F,0, + 0xA741,0,0xA743,0,0xA745,0,0xA747,0,0xA749,0,0xA74B,0,0xA74D,0,0xA74F,0, + 0xA751,0,0xA753,0,0xA755,0,0xA757,0,0xA759,0,0xA75B,0,0xA75D,0,0xA75F,0, + 0xA761,0,0xA763,0,0xA765,0,0xA767,0,0xA769,0,0xA76B,0,0xA76D,0,0xA76F,0, + 0,0,0,0,0,0,0,0,0,0xA77A,0,0xA77C,0,0x1D79,0xA77F,0, + 0xA781,0,0xA783,0,0xA785,0,0xA787,0,0,0,0,0xA78C,0,0}; +static const guint16 simple_lower_case_mapping_lowarea_table7 [] = { + /* ==== FF20-FF80 ==== */ + 0,0xFF41,0xFF42,0xFF43,0xFF44,0xFF45,0xFF46,0xFF47,0xFF48,0xFF49,0xFF4A,0xFF4B,0xFF4C,0xFF4D,0xFF4E,0xFF4F, + 0xFF50,0xFF51,0xFF52,0xFF53,0xFF54,0xFF55,0xFF56,0xFF57,0xFF58,0xFF59,0xFF5A,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0}; +static const guint16 *simple_lower_case_mapping_lowarea [] = { + simple_lower_case_mapping_lowarea_table0, + simple_lower_case_mapping_lowarea_table1, + simple_lower_case_mapping_lowarea_table2, + simple_lower_case_mapping_lowarea_table3, + simple_lower_case_mapping_lowarea_table4, + simple_lower_case_mapping_lowarea_table5, + simple_lower_case_mapping_lowarea_table6, + simple_lower_case_mapping_lowarea_table7}; +static const int simple_lower_case_mapping_lowarea_table_count = 8; + +static const guint32 simple_lower_case_mapping_higharea_table0 [] = { + /* ==== 10400-10480 ==== */ + 0x10428,0x10429,0x1042A,0x1042B,0x1042C,0x1042D,0x1042E,0x1042F,0x10430,0x10431,0x10432,0x10433,0x10434,0x10435,0x10436,0x10437, + 0x10438,0x10439,0x1043A,0x1043B,0x1043C,0x1043D,0x1043E,0x1043F,0x10440,0x10441,0x10442,0x10443,0x10444,0x10445,0x10446,0x10447, + 0x10448,0x10449,0x1044A,0x1044B,0x1044C,0x1044D,0x1044E,0x1044F,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0}; +static const guint32 *simple_lower_case_mapping_higharea [] = { + simple_lower_case_mapping_higharea_table0}; + + +static const SimpleTitlecaseMapping simple_titlecase_mapping [] = { + {0x0001C4, 0x000000, 0x0001C5}, + {0x0001C5, 0x0001C4, 0x0001C5}, + {0x0001C6, 0x0001C4, 0x0001C5}, + {0x0001C7, 0x000000, 0x0001C8}, + {0x0001C8, 0x0001C7, 0x0001C8}, + {0x0001C9, 0x0001C7, 0x0001C8}, + {0x0001CA, 0x000000, 0x0001CB}, + {0x0001CB, 0x0001CA, 0x0001CB}, + {0x0001CC, 0x0001CA, 0x0001CB}, + {0x0001F1, 0x000000, 0x0001F2}, + {0x0001F2, 0x0001F1, 0x0001F2}, + {0x0001F3, 0x0001F1, 0x0001F2} +}; +static const guint8 simple_titlecase_mapping_count = 12; + +#endif + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/.gitattributes b/unity-2019.4.24f1-mbe/mono/metadata/.gitattributes new file mode 100644 index 000000000..ba7442dcb --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/.gitattributes @@ -0,0 +1 @@ +/normalization-tables.h -crlf diff --git a/unity-2019.4.24f1-mbe/mono/metadata/.gitignore b/unity-2019.4.24f1-mbe/mono/metadata/.gitignore new file mode 100644 index 000000000..e4419f293 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/.gitignore @@ -0,0 +1,15 @@ +/Makefile +/Makefile.in +/Makefile.am +/monosn +/monodiet +/.libs +/.deps +/*.lo +/*.la +/*.o +/*.a +/semantic.cache +/.project +/.cproject +/TAGS diff --git a/unity-2019.4.24f1-mbe/mono/metadata/abi-details.h b/unity-2019.4.24f1-mbe/mono/metadata/abi-details.h new file mode 100644 index 000000000..b0cdd5217 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/abi-details.h @@ -0,0 +1,64 @@ +/** + * \file + * Copyright 2014 Xamarin Inc + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#ifndef __MONO_METADATA_ABI_DETAILS_H__ +#define __MONO_METADATA_ABI_DETAILS_H__ + +#include +#include + +/* + * This file defines macros to compute sizes/alignments/field offsets which depend on + * the ABI. It is needed during cross compiling since the generated code needs to + * contain offsets which correspond to the ABI of the target, not the host. + * It defines the following macros: + * - MONO_ABI_SIZEOF(type) for every basic type + * - MONO_ABI_ALIGNOF(type) for every basic type + * - MONO_STRUCT_OFFSET(struct, field) for various runtime structures + * When not cross compiling, these correspond to the host ABI (i.e. sizeof/offsetof). + * When cross compiling, these are defined in a generated header file which is + * generated by the offsets tool in tools/offsets-tool. The name of the file + * is given by the --with-cross-offsets= configure argument. + */ + +#define MONO_ABI_ALIGNOF(type) MONO_ALIGN_ ## type +#define MONO_CURRENT_ABI_ALIGNOF_TYPEDEF(type) typedef struct { char c; type x; } Mono_Align_Struct_ ##type; +#define MONO_CURRENT_ABI_ALIGNOF(type) ((int)G_STRUCT_OFFSET(Mono_Align_Struct_ ##type, x)) +#define MONO_ABI_SIZEOF(type) MONO_SIZEOF_ ## type +#define MONO_CURRENT_ABI_SIZEOF(type) ((int)sizeof(type)) + +#undef DECL_OFFSET +#undef DECL_OFFSET2 +#define DECL_OFFSET(struct,field) MONO_OFFSET_ ## struct ## _ ## field = -1, +#define DECL_OFFSET2(struct,field,offset) MONO_OFFSET_ ## struct ## _ ## field = offset, +#define DECL_ALIGN(type) MONO_ALIGN_ ##type = MONO_CURRENT_ABI_ALIGNOF (type), +#define DECL_ALIGN2(type,size) MONO_ALIGN_ ##type = size, +#define DECL_SIZE(type) MONO_SIZEOF_ ##type = MONO_CURRENT_ABI_SIZEOF (type), +#define DECL_SIZE2(type,size) MONO_SIZEOF_ ##type = size, + +/* Needed by MONO_CURRENT_ABI_ALIGNOF */ +MONO_CURRENT_ABI_ALIGNOF_TYPEDEF(gint8) +MONO_CURRENT_ABI_ALIGNOF_TYPEDEF(gint16) +MONO_CURRENT_ABI_ALIGNOF_TYPEDEF(gint32) +MONO_CURRENT_ABI_ALIGNOF_TYPEDEF(gint64) +MONO_CURRENT_ABI_ALIGNOF_TYPEDEF(float) +MONO_CURRENT_ABI_ALIGNOF_TYPEDEF(double) +MONO_CURRENT_ABI_ALIGNOF_TYPEDEF(gpointer) + +enum { +#include "object-offsets.h" +}; + +#ifdef USED_CROSS_COMPILER_OFFSETS +#define MONO_STRUCT_OFFSET(struct,field) MONO_OFFSET_ ## struct ## _ ## field +#else +#if defined(HAS_CROSS_COMPILER_OFFSETS) || defined(MONO_CROSS_COMPILE) +#define MONO_STRUCT_OFFSET(struct,field) (MONO_OFFSET_ ## struct ## _ ## field == -1, G_STRUCT_OFFSET (struct,field)) +#else +#define MONO_STRUCT_OFFSET(struct,field) G_STRUCT_OFFSET (struct,field) +#endif +#endif + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/appdomain-icalls.h b/unity-2019.4.24f1-mbe/mono/metadata/appdomain-icalls.h new file mode 100644 index 000000000..d0e3345ad --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/appdomain-icalls.h @@ -0,0 +1,120 @@ +/** + * \file + * Appdomain-related icalls. + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#ifndef __MONO_METADATA_APPDOMAIN_ICALLS_H__ +#define __MONO_METADATA_APPDOMAIN_ICALLS_H__ + +#include +#include +#include + +MonoAppDomainHandle +ves_icall_System_AppDomain_getCurDomain (MonoError *error); + +MonoAppDomainHandle +ves_icall_System_AppDomain_getRootDomain (MonoError *error); + +MonoAppDomainHandle +ves_icall_System_AppDomain_createDomain (MonoStringHandle friendly_name, + MonoAppDomainSetupHandle setup, + MonoError *error); + +MonoObjectHandle +ves_icall_System_AppDomain_GetData (MonoAppDomainHandle ad, + MonoStringHandle name, + MonoError* error); + +MonoReflectionAssemblyHandle +ves_icall_System_AppDomain_LoadAssemblyRaw (MonoAppDomainHandle ad, + MonoArrayHandle raw_assembly, + MonoArrayHandle raw_symbol_store, + MonoObjectHandle evidence, + MonoBoolean refonly, + MonoError *error); + +void +ves_icall_System_AppDomain_SetData (MonoAppDomainHandle ad, + MonoStringHandle name, + MonoObjectHandle data, + MonoError *error); + +MonoAppDomainSetupHandle +ves_icall_System_AppDomain_getSetup (MonoAppDomainHandle ad, + MonoError *error); + +MonoStringHandle +ves_icall_System_AppDomain_getFriendlyName (MonoAppDomainHandle ad, + MonoError *error); + +MonoArrayHandle +ves_icall_System_AppDomain_GetAssemblies (MonoAppDomainHandle ad, + MonoBoolean refonly, + MonoError *error); + +MonoReflectionAssemblyHandle +ves_icall_System_Reflection_Assembly_LoadFrom (MonoStringHandle fname, + MonoBoolean refonly, + MonoError *error); + +MonoReflectionAssemblyHandle +ves_icall_System_AppDomain_LoadAssembly (MonoAppDomainHandle ad, + MonoStringHandle assRef, + MonoObjectHandle evidence, + MonoBoolean refonly, + MonoError *error); + +gboolean +ves_icall_System_AppDomain_InternalIsFinalizingForUnload (gint32 domain_id, MonoError *error); + +void +ves_icall_System_AppDomain_InternalUnload (gint32 domain_id, + MonoError *error); + +void +ves_icall_System_AppDomain_DoUnhandledException (MonoExceptionHandle exc, MonoError *error); + +gint32 +ves_icall_System_AppDomain_ExecuteAssembly (MonoAppDomainHandle ad, + MonoReflectionAssemblyHandle refass, + MonoArrayHandle args, + MonoError *error); + +MonoAppDomainHandle +ves_icall_System_AppDomain_InternalSetDomain (MonoAppDomainHandle ad, MonoError *error); + +MonoAppDomainHandle +ves_icall_System_AppDomain_InternalSetDomainByID (gint32 domainid, MonoError *error); + +void +ves_icall_System_AppDomain_InternalPushDomainRef (MonoAppDomainHandle ad, MonoError *error); + +void +ves_icall_System_AppDomain_InternalPushDomainRefByID (gint32 domain_id, MonoError *error); + +void +ves_icall_System_AppDomain_InternalPopDomainRef (MonoError *error); + +MonoAppContextHandle +ves_icall_System_AppDomain_InternalGetContext (MonoError *error); + +MonoAppContextHandle +ves_icall_System_AppDomain_InternalGetDefaultContext (MonoError *error); + +MonoAppContextHandle +ves_icall_System_AppDomain_InternalSetContext (MonoAppContextHandle mc, MonoError *error); + +gint32 +ves_icall_System_AppDomain_GetIDFromDomain (MonoAppDomain * ad); + +MonoStringHandle +ves_icall_System_AppDomain_InternalGetProcessGuid (MonoStringHandle newguid, MonoError *error); + +MonoBoolean +ves_icall_System_CLRConfig_CheckThrowUnobservedTaskExceptions (void); + + +#endif /*__MONO_METADATA_APPDOMAIN_ICALLS_H__*/ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/appdomain.c b/unity-2019.4.24f1-mbe/mono/metadata/appdomain.c new file mode 100644 index 000000000..6f05a7d78 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/appdomain.c @@ -0,0 +1,2800 @@ +/** + * \file + * AppDomain functions + * + * Authors: + * Dietmar Maurer (dietmar@ximian.com) + * Patrik Torstensson + * Gonzalo Paniagua Javier (gonzalo@ximian.com) + * + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Copyright 2012 Xamarin Inc + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#undef ASSEMBLY_LOAD_DEBUG +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_UTIME_H +#include +#else +#ifdef HAVE_SYS_UTIME_H +#include +#endif +#endif + +#include +#include +#include +#include +#include "mono/metadata/metadata-internals.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HOST_WIN32 +#include +#endif + +typedef struct +{ + int runtime_count; + int assemblybinding_count; + MonoDomain *domain; + gchar *filename; +} RuntimeConfig; + +static gunichar2 process_guid [36]; +static gboolean process_guid_set = FALSE; + +static gboolean no_exec = FALSE; + +static MonoAssembly * +mono_domain_assembly_preload (MonoAssemblyName *aname, + gchar **assemblies_path, + gpointer user_data); + +static MonoAssembly * +mono_domain_assembly_search (MonoAssemblyName *aname, + gpointer user_data); + + + +static gboolean ignore_version_and_key_when_finding_assemblies_already_loaded = FALSE; + +void +mono_set_ignore_version_and_key_when_finding_assemblies_already_loaded(gboolean value) +{ + ignore_version_and_key_when_finding_assemblies_already_loaded = value; +} + +static void +mono_domain_fire_assembly_load (MonoAssembly *assembly, gpointer user_data); + +static void +add_assemblies_to_domain (MonoDomain *domain, MonoAssembly *ass, GHashTable *hash); + +static MonoAppDomainHandle +mono_domain_create_appdomain_internal (char *friendly_name, MonoAppDomainSetupHandle setup, MonoError *error); + +static MonoDomain * +mono_domain_create_appdomain_checked (char *friendly_name, char *configuration_file, MonoError *error); + + +static void +mono_context_set_default_context (MonoDomain *domain); + +static char * +get_shadow_assembly_location_base (MonoDomain *domain, MonoError *error); + +static MonoLoadFunc load_function = NULL; + +/* Lazy class loading functions */ +static GENERATE_GET_CLASS_WITH_CACHE (assembly, "System.Reflection", "Assembly"); + +static GENERATE_GET_CLASS_WITH_CACHE (appdomain, "System", "AppDomain"); + +static MonoDomain * +mono_domain_from_appdomain_handle (MonoAppDomainHandle appdomain); + +static void +mono_error_set_appdomain_unloaded (MonoError *error) +{ + mono_error_set_generic_error (error, "System", "AppDomainUnloadedException", ""); +} + +void +mono_install_runtime_load (MonoLoadFunc func) +{ + load_function = func; +} + +MonoDomain* +mono_runtime_load (const char *filename, const char *runtime_version) +{ + g_assert (load_function); + return load_function (filename, runtime_version); +} + +/** + * mono_runtime_set_no_exec: + * + * Instructs the runtime to operate in static mode, i.e. avoid/do not + * allow managed code execution. This is useful for running the AOT + * compiler on platforms which allow full-aot execution only. This + * should be called before mono_runtime_init (). + */ +void +mono_runtime_set_no_exec (gboolean val) +{ + no_exec = val; +} + +/** + * mono_runtime_get_no_exec: + * + * If true, then the runtime will not allow managed code execution. + */ +gboolean +mono_runtime_get_no_exec (void) +{ + return no_exec; +} + +static void +create_domain_objects (MonoDomain *domain) +{ + MonoError error; + MonoDomain *old_domain = mono_domain_get (); + MonoString *arg; + MonoVTable *string_vt; + MonoClassField *string_empty_fld; + + if (domain != old_domain) { + mono_thread_push_appdomain_ref (domain); + mono_domain_set_internal_with_options (domain, FALSE); + } + + /* + * Initialize String.Empty. This enables the removal of + * the static cctor of the String class. + */ + string_vt = mono_class_vtable (domain, mono_defaults.string_class); + string_empty_fld = mono_class_get_field_from_name (mono_defaults.string_class, "Empty"); + g_assert (string_empty_fld); + MonoString *empty_str = mono_string_new_checked (domain, "", &error); + mono_error_assert_ok (&error); + empty_str = mono_string_intern_checked (empty_str, &error); + 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. + */ + 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); + + /* + * These two are needed because the signal handlers might be executing on + * an alternate stack, and Boehm GC can't handle that. + */ + 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) { + mono_thread_pop_appdomain_ref (); + mono_domain_set_internal_with_options (old_domain, FALSE); + } + + /* + * This class is used during exception handling, so initialize it here, to prevent + * stack overflows while handling stack overflows. + */ + mono_class_init (mono_array_class_get (mono_defaults.int_class, 1)); +} + +/** + * mono_runtime_init: + * \param domain domain returned by \c mono_init + * + * Initialize the core AppDomain: this function will run also some + * IL initialization code, so it needs the execution engine to be fully + * operational. + * + * \c AppDomain.SetupInformation is set up in \c mono_runtime_exec_main, where + * we know the \c entry_assembly. + * + */ +void +mono_runtime_init (MonoDomain *domain, MonoThreadStartCB start_cb, MonoThreadAttachCB attach_cb) +{ + MonoError error; + mono_runtime_init_checked (domain, start_cb, attach_cb, &error); + mono_error_cleanup (&error); +} + +void +mono_runtime_init_checked (MonoDomain *domain, MonoThreadStartCB start_cb, MonoThreadAttachCB attach_cb, MonoError *error) +{ + MonoAppDomainSetup *setup; + MonoAppDomain *ad; + MonoClass *klass; + + error_init (error); + + mono_portability_helpers_init (); + + mono_gc_base_init (); + mono_monitor_init (); + mono_marshal_init (); + + mono_install_assembly_preload_hook (mono_domain_assembly_preload, GUINT_TO_POINTER (FALSE)); + mono_install_assembly_refonly_preload_hook (mono_domain_assembly_preload, GUINT_TO_POINTER (TRUE)); + mono_install_assembly_search_hook (mono_domain_assembly_search, GUINT_TO_POINTER (FALSE)); + mono_install_assembly_refonly_search_hook (mono_domain_assembly_search, GUINT_TO_POINTER (TRUE)); + mono_install_assembly_postload_search_hook ((MonoAssemblySearchFunc)mono_domain_assembly_postload_search, GUINT_TO_POINTER (FALSE)); + mono_install_assembly_postload_refonly_search_hook ((MonoAssemblySearchFunc)mono_domain_assembly_postload_search, GUINT_TO_POINTER (TRUE)); + mono_install_assembly_load_hook (mono_domain_fire_assembly_load, NULL); + + mono_thread_init (start_cb, attach_cb); + + klass = mono_class_load_from_name (mono_defaults.corlib, "System", "AppDomainSetup"); + setup = (MonoAppDomainSetup *) mono_object_new_pinned (domain, klass, error); + return_if_nok (error); + + klass = mono_class_load_from_name (mono_defaults.corlib, "System", "AppDomain"); + + ad = (MonoAppDomain *) mono_object_new_pinned (domain, klass, error); + return_if_nok (error); + + 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); + + mono_type_initialization_init (); + + if (!mono_runtime_get_no_exec ()) + create_domain_objects (domain); + + /* GC init has to happen after thread init */ + mono_gc_init (); + + /* contexts use GC handles, so they must be initialized after the GC */ + mono_context_init_checked (domain, error); + return_if_nok (error); + mono_context_set_default_context (domain); + +#ifndef DISABLE_SOCKETS + mono_network_init (); +#endif + + mono_console_init (); + mono_attach_init (); + + mono_locks_tracer_init (); + + /* mscorlib is loaded before we install the load hook */ + mono_domain_fire_assembly_load (mono_defaults.corlib->assembly, NULL); + + return; +} + +static void +mono_context_set_default_context (MonoDomain *domain) +{ + HANDLE_FUNCTION_ENTER (); + mono_context_set_handle (MONO_HANDLE_NEW (MonoAppContext, domain->default_context)); + HANDLE_FUNCTION_RETURN (); +} + + +static int +mono_get_corlib_version (void) +{ + MonoError error; + MonoClass *klass; + MonoClassField *field; + MonoObject *value; + + klass = mono_class_load_from_name (mono_defaults.corlib, "System", "Environment"); + mono_class_init (klass); + field = mono_class_get_field_from_name (klass, "mono_corlib_version"); + if (!field) + return -1; + if (! (field->type->attrs & FIELD_ATTRIBUTE_STATIC)) + return -1; + value = mono_field_get_value_object_checked (mono_domain_get (), field, NULL, &error); + mono_error_assert_ok (&error); + return *(gint32*)((gchar*)value + sizeof (MonoObject)); +} + +/** + * mono_check_corlib_version: + * Checks that the corlib that is loaded matches the version of this runtime. + * \returns NULL if the runtime will work with the corlib, or a \c g_malloc + * allocated string with the error otherwise. + */ +const char* +mono_check_corlib_version (void) +{ + int version = mono_get_corlib_version (); + if (version != MONO_CORLIB_VERSION) + return g_strdup_printf ("expected corlib version %d, found %d.", MONO_CORLIB_VERSION, version); + + /* Check that the managed and unmanaged layout of MonoInternalThread matches */ + guint32 native_offset = (guint32) MONO_STRUCT_OFFSET (MonoInternalThread, last); + guint32 managed_offset = mono_field_get_offset (mono_class_get_field_from_name (mono_defaults.internal_thread_class, "last")); + if (native_offset != managed_offset) + return g_strdup_printf ("expected InternalThread.last field offset %u, found %u. See InternalThread.last comment", native_offset, managed_offset); + + return NULL; +} + +/** + * mono_context_init: + * \param domain The domain where the \c System.Runtime.Remoting.Context.Context is initialized + * Initializes the \p domain's default \c System.Runtime.Remoting 's Context. + */ +void +mono_context_init (MonoDomain *domain) +{ + MonoError error; + mono_context_init_checked (domain, &error); + mono_error_cleanup (&error); +} + +void +mono_context_init_checked (MonoDomain *domain, MonoError *error) +{ + MonoClass *klass; + MonoAppContext *context; + + error_init (error); + + klass = mono_class_load_from_name (mono_defaults.corlib, "System.Runtime.Remoting.Contexts", "Context"); + context = (MonoAppContext *) mono_object_new_pinned (domain, klass, error); + return_if_nok (error); + + context->domain_id = domain->domain_id; + context->context_id = 0; + mono_threads_register_app_context (context, error); + mono_error_assert_ok (error); + domain->default_context = context; +} + +/** + * mono_runtime_cleanup: + * \param domain unused. + * + * Internal routine. + * + * This must not be called while there are still running threads executing + * managed code. + */ +void +mono_runtime_cleanup (MonoDomain *domain) +{ + mono_attach_cleanup (); + + /* This ends up calling any pending pending (for at most 2 seconds) */ + mono_gc_cleanup (); + + mono_thread_cleanup (); + +#ifndef DISABLE_SOCKETS + mono_network_cleanup (); +#endif + mono_marshal_cleanup (); + + mono_type_initialization_cleanup (); + + mono_monitor_cleanup (); +} + +static MonoDomainFunc quit_function = NULL; + +/** + * mono_install_runtime_cleanup: + */ +void +mono_install_runtime_cleanup (MonoDomainFunc func) +{ + quit_function = func; +} + +/** + * mono_runtime_quit: + */ +void +mono_runtime_quit () +{ + if (quit_function != NULL) + quit_function (mono_get_root_domain (), NULL); +} + +/** + * mono_domain_create_appdomain: + * \param friendly_name The friendly name of the appdomain to create + * \param configuration_file The configuration file to initialize the appdomain with + * \returns a \c MonoDomain initialized with the appdomain + */ +MonoDomain * +mono_domain_create_appdomain (char *friendly_name, char *configuration_file) +{ + HANDLE_FUNCTION_ENTER (); + MonoError error; + MonoDomain *domain = mono_domain_create_appdomain_checked (friendly_name, configuration_file, &error); + mono_error_cleanup (&error); + HANDLE_FUNCTION_RETURN_VAL (domain); +} + +/** + * mono_domain_create_appdomain_checked: + * \param friendly_name The friendly name of the appdomain to create + * \param configuration_file The configuration file to initialize the appdomain with + * \param error Set on error. + * + * \returns a MonoDomain initialized with the appdomain. On failure sets \p error and returns NULL. + */ +MonoDomain * +mono_domain_create_appdomain_checked (char *friendly_name, char *configuration_file, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + error_init (error); + MonoDomain *result = NULL; + + MonoClass *klass = mono_class_load_from_name (mono_defaults.corlib, "System", "AppDomainSetup"); + MonoAppDomainSetupHandle setup = MONO_HANDLE_NEW (MonoAppDomainSetup, mono_object_new_checked (mono_domain_get (), klass, error)); + goto_if_nok (error, leave); + MonoStringHandle config_file; + if (configuration_file != NULL) { + config_file = mono_string_new_handle (mono_domain_get (), configuration_file, error); + goto_if_nok (error, leave); + } else { + config_file = MONO_HANDLE_NEW (MonoString, NULL); + } + MONO_HANDLE_SET (setup, configuration_file, config_file); + + MonoAppDomainHandle ad = mono_domain_create_appdomain_internal (friendly_name, setup, error); + goto_if_nok (error, leave); + + result = mono_domain_from_appdomain_handle (ad); +leave: + HANDLE_FUNCTION_RETURN_VAL (result); +} + +/** + * mono_domain_set_config: + * \param domain \c MonoDomain initialized with the appdomain we want to change + * \param base_dir new base directory for the appdomain + * \param config_file_name path to the new configuration for the app domain + * + * Used to set the system configuration for an appdomain + * + * Without using this, embedded builds will get 'System.Configuration.ConfigurationErrorsException: + * Error Initializing the configuration system. ---> System.ArgumentException: + * The 'ExeConfigFilename' argument cannot be null.' for some managed calls. + */ +void +mono_domain_set_config (MonoDomain *domain, const char *base_dir, const char *config_file_name) +{ + HANDLE_FUNCTION_ENTER (); + MonoError error; + mono_domain_set_config_checked (domain, base_dir, config_file_name, &error); + mono_error_cleanup (&error); + HANDLE_FUNCTION_RETURN (); +} + +gboolean +mono_domain_set_config_checked (MonoDomain *domain, const char *base_dir, const char *config_file_name, MonoError *error) +{ + error_init (error); + MonoAppDomainSetupHandle setup = MONO_HANDLE_NEW (MonoAppDomainSetup, domain->setup); + MonoStringHandle base_dir_str = mono_string_new_handle (domain, base_dir, error); + goto_if_nok (error, leave); + MONO_HANDLE_SET (setup, application_base, base_dir_str); + MonoStringHandle config_file_name_str = mono_string_new_handle (domain, config_file_name, error); + goto_if_nok (error, leave); + MONO_HANDLE_SET (setup, configuration_file, config_file_name_str); +leave: + return is_ok (error); +} + +static MonoAppDomainSetupHandle +copy_app_domain_setup (MonoDomain *domain, MonoAppDomainSetupHandle setup, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + MonoDomain *caller_domain; + MonoClass *ads_class; + MonoAppDomainSetupHandle result = MONO_HANDLE_NEW (MonoAppDomainSetup, NULL); + + error_init (error); + + caller_domain = mono_domain_get (); + ads_class = mono_class_load_from_name (mono_defaults.corlib, "System", "AppDomainSetup"); + + MonoAppDomainSetupHandle copy = MONO_HANDLE_NEW (MonoAppDomainSetup, mono_object_new_checked (domain, ads_class, error)); + goto_if_nok (error, leave); + + mono_domain_set_internal (domain); + +#define XCOPY_FIELD(dst,field,src,error) \ + do { \ + MonoObjectHandle src_val = MONO_HANDLE_NEW_GET (MonoObject, (src), field); \ + MonoObjectHandle copied_val = mono_marshal_xdomain_copy_value_handle (src_val, error); \ + goto_if_nok (error, leave); \ + MONO_HANDLE_SET ((dst),field,copied_val); \ + } while (0) + +#define COPY_VAL(dst,field,type,src) \ + do { \ + MONO_HANDLE_SETVAL ((dst), field, type, MONO_HANDLE_GETVAL ((src),field)); \ + } while (0) + + XCOPY_FIELD (copy, application_base, setup, error); + XCOPY_FIELD (copy, application_name, setup, error); + XCOPY_FIELD (copy, cache_path, setup, error); + XCOPY_FIELD (copy, configuration_file, setup, error); + XCOPY_FIELD (copy, dynamic_base, setup, error); + XCOPY_FIELD (copy, license_file, setup, error); + XCOPY_FIELD (copy, private_bin_path, setup, error); + XCOPY_FIELD (copy, private_bin_path_probe, setup, error); + XCOPY_FIELD (copy, shadow_copy_directories, setup, error); + XCOPY_FIELD (copy, shadow_copy_files, setup, error); + COPY_VAL (copy, publisher_policy, MonoBoolean, setup); + COPY_VAL (copy, path_changed, MonoBoolean, setup); + COPY_VAL (copy, loader_optimization, int, setup); + COPY_VAL (copy, disallow_binding_redirects, MonoBoolean, setup); + COPY_VAL (copy, disallow_code_downloads, MonoBoolean, setup); + XCOPY_FIELD (copy, domain_initializer_args, setup, error); + COPY_VAL (copy, disallow_appbase_probe, MonoBoolean, setup); + XCOPY_FIELD (copy, application_trust, setup, error); + XCOPY_FIELD (copy, configuration_bytes, setup, error); + XCOPY_FIELD (copy, serialized_non_primitives, setup, error); + +#undef XCOPY_FIELD +#undef COPY_VAL + + mono_domain_set_internal (caller_domain); + + MONO_HANDLE_ASSIGN (result, copy); +leave: + HANDLE_FUNCTION_RETURN_REF (MonoAppDomainSetup, result); +} + +static MonoAppDomainHandle +mono_domain_create_appdomain_internal (char *friendly_name, MonoAppDomainSetupHandle setup, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + MonoAppDomainHandle result = MONO_HANDLE_NEW (MonoAppDomain, NULL); + MonoClass *adclass; + MonoDomain *data; + + error_init (error); + + adclass = mono_class_get_appdomain_class (); + + /* FIXME: pin all those objects */ + data = mono_domain_create(); + + MonoAppDomainHandle ad = MONO_HANDLE_NEW (MonoAppDomain, mono_object_new_checked (data, adclass, error)); + 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)); + + MonoStringHandle app_base = MONO_HANDLE_NEW_GET (MonoString, setup, application_base); + if (MONO_HANDLE_IS_NULL (app_base)) { + /* Inherit from the root domain since MS.NET does this */ + MonoDomain *root = mono_get_root_domain (); + MonoAppDomainSetupHandle root_setup = MONO_HANDLE_NEW (MonoAppDomainSetup, root->setup); + MonoStringHandle root_app_base = MONO_HANDLE_NEW_GET (MonoString, root_setup, application_base); + if (!MONO_HANDLE_IS_NULL (root_app_base)) { + /* N.B. new string is in the new domain */ + uint32_t gchandle = mono_gchandle_from_handle (MONO_HANDLE_CAST (MonoObject, root_app_base), TRUE); + MonoStringHandle s = mono_string_new_utf16_handle (data, mono_string_chars (MONO_HANDLE_RAW (root_app_base)), mono_string_handle_length (root_app_base), error); + mono_gchandle_free (gchandle); + if (!is_ok (error)) { + g_free (data->friendly_name); + goto leave; + } + MONO_HANDLE_SET (setup, application_base, s); + } + } + + mono_context_init_checked (data, error); + 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; + } + + mono_domain_set_options_from_config (data); + add_assemblies_to_domain (data, mono_defaults.corlib->assembly, NULL); + +#ifndef DISABLE_SHADOW_COPY + /*FIXME, guard this for when the debugger is not running */ + char *shadow_location = get_shadow_assembly_location_base (data, error); + if (!mono_error_ok (error)) { + g_free (data->friendly_name); + goto leave; + } + + g_free (shadow_location); +#endif + + create_domain_objects (data); + + MONO_HANDLE_ASSIGN (result, ad); +leave: + HANDLE_FUNCTION_RETURN_REF (MonoAppDomain, result); +} + +/** + * mono_domain_has_type_resolve: + * \param domain application domain being looked up + * + * \returns TRUE if the \c AppDomain.TypeResolve field has been set. + */ +gboolean +mono_domain_has_type_resolve (MonoDomain *domain) +{ + static MonoClassField *field = NULL; + MonoObject *o; + + if (field == NULL) { + field = mono_class_get_field_from_name (mono_defaults.appdomain_class, "TypeResolve"); + g_assert (field); + } + + /*pedump doesn't create an appdomin, so the domain object doesn't exist.*/ + if (!domain->domain) + return FALSE; + + mono_field_get_value ((MonoObject*)(domain->domain), field, &o); + return o != NULL; +} + +/** + * mono_domain_try_type_resolve: + * \param domain application domainwhere the name where the type is going to be resolved + * \param name the name of the type to resolve or NULL. + * \param tb A \c System.Reflection.Emit.TypeBuilder, used if name is NULL. + * + * This routine invokes the internal \c System.AppDomain.DoTypeResolve and returns + * the assembly that matches name. + * + * If \p name is null, the value of \c ((TypeBuilder)tb).FullName is used instead + * + * \returns A \c MonoReflectionAssembly or NULL if not found + */ +MonoReflectionAssembly * +mono_domain_try_type_resolve (MonoDomain *domain, char *name, MonoObject *tb) +{ + MonoError error; + MonoReflectionAssembly *ret = mono_domain_try_type_resolve_checked (domain, name, tb, &error); + mono_error_cleanup (&error); + + return ret; +} + +MonoReflectionAssembly * +mono_domain_try_type_resolve_checked (MonoDomain *domain, char *name, MonoObject *tb, MonoError *error) +{ + static MonoMethod *method = NULL; + MonoReflectionAssembly *ret; + void *params [1]; + + error_init (error); + + g_assert (domain != NULL && ((name != NULL) || (tb != NULL))); + + if (method == NULL) { + method = mono_class_get_method_from_name (mono_class_get_appdomain_class (), "DoTypeResolve", -1); + if (method == NULL) { + g_warning ("Method AppDomain.DoTypeResolve not found.\n"); + return NULL; + } + } + + if (name) { + *params = (MonoObject*)mono_string_new_checked (mono_domain_get (), name, error); + return_val_if_nok (error, NULL); + } else + *params = tb; + + ret = (MonoReflectionAssembly *) mono_runtime_invoke_checked (method, domain->domain, params, error); + return_val_if_nok (error, NULL); + + return ret; +} + +/** + * mono_domain_owns_vtable_slot: + * \returns Whether \p vtable_slot is inside a vtable which belongs to \p domain. + */ +gboolean +mono_domain_owns_vtable_slot (MonoDomain *domain, gpointer vtable_slot) +{ + gboolean res; + + mono_domain_lock (domain); + res = mono_mempool_contains_addr (domain->mp, vtable_slot); + mono_domain_unlock (domain); + return res; +} + +/** + * mono_domain_set: + * \param domain domain + * \param force force setting. + * + * Set the current appdomain to \p domain. If \p force is set, set it even + * if it is being unloaded. + * + * \returns TRUE on success; FALSE if the domain is unloaded + */ +gboolean +mono_domain_set (MonoDomain *domain, gboolean force) +{ + if (!force && domain->state == MONO_APPDOMAIN_UNLOADED) + return FALSE; + + mono_domain_set_internal (domain); + + return TRUE; +} + +MonoObjectHandle +ves_icall_System_AppDomain_GetData (MonoAppDomainHandle ad, MonoStringHandle name, MonoError *error) +{ + error_init (error); + + if (MONO_HANDLE_IS_NULL (name)) { + mono_error_set_argument_null (error, "name", ""); + return NULL_HANDLE; + } + + g_assert (!MONO_HANDLE_IS_NULL (ad)); + MonoDomain *add = MONO_HANDLE_GETVAL (ad, data); + g_assert (add); + + char *str = mono_string_handle_to_utf8 (name, error); + return_val_if_nok (error, NULL_HANDLE); + + mono_domain_lock (add); + + MonoAppDomainSetupHandle ad_setup = MONO_HANDLE_NEW (MonoAppDomainSetup, add->setup); + MonoStringHandle o; + if (!strcmp (str, "APPBASE")) + o = MONO_HANDLE_NEW_GET (MonoString, ad_setup, application_base); + else if (!strcmp (str, "APP_CONFIG_FILE")) + o = MONO_HANDLE_NEW_GET (MonoString, ad_setup, configuration_file); + else if (!strcmp (str, "DYNAMIC_BASE")) + o = MONO_HANDLE_NEW_GET (MonoString, ad_setup, dynamic_base); + else if (!strcmp (str, "APP_NAME")) + o = MONO_HANDLE_NEW_GET (MonoString, ad_setup, application_name); + else if (!strcmp (str, "CACHE_BASE")) + o = MONO_HANDLE_NEW_GET (MonoString, ad_setup, cache_path); + else if (!strcmp (str, "PRIVATE_BINPATH")) + o = MONO_HANDLE_NEW_GET (MonoString, ad_setup, private_bin_path); + else if (!strcmp (str, "BINPATH_PROBE_ONLY")) + o = MONO_HANDLE_NEW_GET (MonoString, ad_setup, private_bin_path_probe); + else if (!strcmp (str, "SHADOW_COPY_DIRS")) + o = MONO_HANDLE_NEW_GET (MonoString, ad_setup, shadow_copy_directories); + else if (!strcmp (str, "FORCE_CACHE_INSTALL")) + o = MONO_HANDLE_NEW_GET (MonoString, ad_setup, shadow_copy_files); + else + o = MONO_HANDLE_NEW (MonoString, mono_g_hash_table_lookup (add->env, MONO_HANDLE_RAW (name))); + + mono_domain_unlock (add); + g_free (str); + + return MONO_HANDLE_CAST (MonoObject, o); +} + +void +ves_icall_System_AppDomain_SetData (MonoAppDomainHandle ad, MonoStringHandle name, MonoObjectHandle data, MonoError *error) +{ + error_init (error); + + if (MONO_HANDLE_IS_NULL (name)) { + mono_error_set_argument_null (error, "name", ""); + return; + } + + g_assert (!MONO_HANDLE_IS_NULL (ad)); + MonoDomain *add = MONO_HANDLE_GETVAL (ad, data); + g_assert (add); + + mono_domain_lock (add); + + mono_g_hash_table_insert (add->env, MONO_HANDLE_RAW (name), MONO_HANDLE_RAW (data)); + + mono_domain_unlock (add); +} + +MonoAppDomainSetupHandle +ves_icall_System_AppDomain_getSetup (MonoAppDomainHandle ad, MonoError *error) +{ + error_init (error); + g_assert (!MONO_HANDLE_IS_NULL (ad)); + MonoDomain *domain = MONO_HANDLE_GETVAL (ad, data); + g_assert (domain); + + return MONO_HANDLE_NEW (MonoAppDomainSetup, domain->setup); +} + +MonoStringHandle +ves_icall_System_AppDomain_getFriendlyName (MonoAppDomainHandle ad, MonoError *error) +{ + error_init (error); + g_assert (!MONO_HANDLE_IS_NULL (ad)); + MonoDomain *domain = MONO_HANDLE_GETVAL (ad, data); + g_assert (domain); + + return mono_string_new_handle (domain, domain->friendly_name, error); +} + +MonoAppDomainHandle +ves_icall_System_AppDomain_getCurDomain (MonoError *error) +{ + error_init (error); + MonoDomain *add = mono_domain_get (); + + return MONO_HANDLE_NEW (MonoAppDomain, add->domain); +} + +MonoAppDomainHandle +ves_icall_System_AppDomain_getRootDomain (MonoError *error) +{ + error_init (error); + MonoDomain *root = mono_get_root_domain (); + + return MONO_HANDLE_NEW (MonoAppDomain, root->domain); +} + +MonoBoolean +ves_icall_System_CLRConfig_CheckThrowUnobservedTaskExceptions () +{ + MonoDomain *domain = mono_domain_get (); + + return domain->throw_unobserved_task_exceptions; +} + +static char* +get_attribute_value (const gchar **attribute_names, + const gchar **attribute_values, + const char *att_name) +{ + int n; + for (n = 0; attribute_names [n] != NULL; n++) { + if (strcmp (attribute_names [n], att_name) == 0) + return g_strdup (attribute_values [n]); + } + return NULL; +} + +static void +start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error) +{ + RuntimeConfig *runtime_config = (RuntimeConfig *)user_data; + + if (strcmp (element_name, "runtime") == 0) { + runtime_config->runtime_count++; + return; + } + + if (strcmp (element_name, "assemblyBinding") == 0) { + runtime_config->assemblybinding_count++; + return; + } + + if (runtime_config->runtime_count != 1) + return; + + if (strcmp (element_name, "ThrowUnobservedTaskExceptions") == 0) { + const char *value = get_attribute_value (attribute_names, attribute_values, "enabled"); + + if (value && g_ascii_strcasecmp (value, "true") == 0) + runtime_config->domain->throw_unobserved_task_exceptions = TRUE; + } + + if (runtime_config->assemblybinding_count != 1) + return; + + if (strcmp (element_name, "probing") != 0) + return; + + g_free (runtime_config->domain->private_bin_path); + runtime_config->domain->private_bin_path = get_attribute_value (attribute_names, attribute_values, "privatePath"); + if (runtime_config->domain->private_bin_path && !runtime_config->domain->private_bin_path [0]) { + g_free (runtime_config->domain->private_bin_path); + runtime_config->domain->private_bin_path = NULL; + return; + } +} + +static void +end_element (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error) +{ + RuntimeConfig *runtime_config = (RuntimeConfig *)user_data; + if (strcmp (element_name, "runtime") == 0) + runtime_config->runtime_count--; + else if (strcmp (element_name, "assemblyBinding") == 0) + runtime_config->assemblybinding_count--; +} + +static void +parse_error (GMarkupParseContext *context, GError *error, gpointer user_data) +{ + RuntimeConfig *state = (RuntimeConfig *)user_data; + const gchar *msg; + const gchar *filename; + + filename = state && state->filename ? (gchar *) state->filename : ""; + msg = error && error->message ? error->message : ""; + g_warning ("Error parsing %s: %s", filename, msg); +} + +static const GMarkupParser +mono_parser = { + start_element, + end_element, + NULL, + NULL, + parse_error +}; + +void +mono_domain_set_options_from_config (MonoDomain *domain) +{ + MonoError error; + gchar *config_file_name = NULL, *text = NULL, *config_file_path = NULL; + gsize len; + GMarkupParseContext *context; + RuntimeConfig runtime_config; + gint offset; + + if (!domain || !domain->setup || !domain->setup->configuration_file) + return; + + config_file_name = mono_string_to_utf8_checked (domain->setup->configuration_file, &error); + if (!mono_error_ok (&error)) { + mono_error_cleanup (&error); + goto free_and_out; + } + + config_file_path = mono_portability_find_file (config_file_name, TRUE); + if (!config_file_path) + config_file_path = config_file_name; + + if (!g_file_get_contents (config_file_path, &text, &len, NULL)) + goto free_and_out; + + runtime_config.runtime_count = 0; + runtime_config.assemblybinding_count = 0; + runtime_config.domain = domain; + runtime_config.filename = config_file_path; + + offset = 0; + if (len > 3 && text [0] == '\xef' && text [1] == (gchar) '\xbb' && text [2] == '\xbf') + offset = 3; /* Skip UTF-8 BOM */ + + context = g_markup_parse_context_new (&mono_parser, (GMarkupParseFlags)0, &runtime_config, NULL); + if (g_markup_parse_context_parse (context, text + offset, len - offset, NULL)) + g_markup_parse_context_end_parse (context, NULL); + g_markup_parse_context_free (context); + + free_and_out: + g_free (text); + if (config_file_name != config_file_path) + g_free (config_file_name); + g_free (config_file_path); +} + +MonoAppDomainHandle +ves_icall_System_AppDomain_createDomain (MonoStringHandle friendly_name, MonoAppDomainSetupHandle setup, MonoError *error) +{ + error_init (error); + MonoAppDomainHandle ad = MONO_HANDLE_NEW (MonoAppDomain, NULL); + +#ifdef DISABLE_APPDOMAINS + mono_error_set_not_supported (error, "AppDomain creation is not supported on this runtime."); +#else + char *fname; + + fname = mono_string_handle_to_utf8 (friendly_name, error); + return_val_if_nok (error, ad); + ad = mono_domain_create_appdomain_internal (fname, setup, error); + g_free (fname); +#endif + return ad; +} + +static gboolean +add_assembly_to_array (MonoDomain *domain, MonoArrayHandle dest, int dest_idx, MonoAssembly* assm, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + error_init (error); + MonoReflectionAssemblyHandle assm_obj = mono_assembly_get_object_handle (domain, assm, error); + goto_if_nok (error, leave); + MONO_HANDLE_ARRAY_SETREF (dest, dest_idx, assm_obj); +leave: + HANDLE_FUNCTION_RETURN_VAL (is_ok (error)); +} + +MonoArrayHandle +ves_icall_System_AppDomain_GetAssemblies (MonoAppDomainHandle ad, MonoBoolean refonly, MonoError *error) +{ + error_init (error); + MonoDomain *domain = MONO_HANDLE_GETVAL (ad, data); + MonoAssembly* ass; + GSList *tmp; + int i; + GPtrArray *assemblies; + + /* + * Make a copy of the list of assemblies because we can't hold the assemblies + * lock while creating objects etc. + */ + assemblies = g_ptr_array_new (); + /* Need to skip internal assembly builders created by remoting */ + mono_domain_assemblies_lock (domain); + for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) { + ass = (MonoAssembly *)tmp->data; + if (refonly != ass->ref_only) + continue; + if (ass->corlib_internal) + continue; + g_ptr_array_add (assemblies, ass); + } + mono_domain_assemblies_unlock (domain); + + MonoArrayHandle res = mono_array_new_handle (domain, mono_class_get_assembly_class (), assemblies->len, error); + goto_if_nok (error, leave); + for (i = 0; i < assemblies->len; ++i) { + if (!add_assembly_to_array (domain, res, i, (MonoAssembly *)g_ptr_array_index (assemblies, i), error)) + goto leave; + } + +leave: + g_ptr_array_free (assemblies, TRUE); + return res; +} + +MonoAssembly* +mono_try_assembly_resolve (MonoDomain *domain, const char *fname_raw, MonoAssembly *requesting, gboolean refonly, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + error_init (error); + MonoAssembly *result = NULL; + MonoStringHandle fname = mono_string_new_handle (domain, fname_raw, error); + goto_if_nok (error, leave); + result = mono_try_assembly_resolve_handle (domain, fname, requesting, refonly, error); +leave: + HANDLE_FUNCTION_RETURN_VAL (result); +} + +MonoAssembly* +mono_try_assembly_resolve_handle (MonoDomain *domain, MonoStringHandle fname, MonoAssembly *requesting, gboolean refonly, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + MonoAssembly *ret = NULL; + MonoMethod *method; + MonoBoolean isrefonly; + gpointer params [3]; + + error_init (error); + + if (mono_runtime_get_no_exec ()) + goto leave; + + g_assert (domain != NULL && !MONO_HANDLE_IS_NULL (fname)); + + method = mono_class_get_method_from_name (mono_class_get_appdomain_class (), "DoAssemblyResolve", -1); + g_assert (method != NULL); + + isrefonly = refonly ? 1 : 0; + MonoReflectionAssemblyHandle requesting_handle; + if (requesting) { + requesting_handle = mono_assembly_get_object_handle (domain, requesting, error); + goto_if_nok (error, leave); + } + params [0] = MONO_HANDLE_RAW (fname); + params[1] = requesting ? MONO_HANDLE_RAW (requesting_handle) : NULL; + params [2] = &isrefonly; + MonoObject *exc = NULL; + MonoReflectionAssemblyHandle result = MONO_HANDLE_NEW (MonoReflectionAssembly, mono_runtime_try_invoke (method, domain->domain, params, &exc, error)); + if (!is_ok (error) || exc != NULL) { + if (is_ok (error)) + mono_error_set_exception_instance (error, (MonoException*)exc); + goto leave; + } + ret = !MONO_HANDLE_IS_NULL (result) ? MONO_HANDLE_GETVAL (result, assembly) : NULL; + + if (ret && !refonly && ret->ref_only) { + /* .NET Framework throws System.IO.FileNotFoundException in this case */ + mono_error_set_file_not_found (error, "AssemblyResolveEvent handlers cannot return Assemblies loaded for reflection only"); + ret = NULL; + goto leave; + } +leave: + HANDLE_FUNCTION_RETURN_VAL (ret); +} + +MonoAssembly * +mono_domain_assembly_postload_search (MonoAssemblyName *aname, MonoAssembly *requesting, + gboolean refonly) +{ + MonoError error; + MonoAssembly *assembly; + MonoDomain *domain = mono_domain_get (); + char *aname_str; + + aname_str = mono_stringify_assembly_name (aname); + + /* FIXME: We invoke managed code here, so there is a potential for deadlocks */ + + assembly = mono_try_assembly_resolve (domain, aname_str, requesting, refonly, &error); + g_free (aname_str); + mono_error_cleanup (&error); + + return assembly; +} + +/* + * LOCKING: assumes assemblies_lock in the domain is already locked. + */ +static void +add_assemblies_to_domain (MonoDomain *domain, MonoAssembly *ass, GHashTable *ht) +{ + gint i; + GSList *tmp; + gboolean destroy_ht = FALSE; + + if (!ass->aname.name) + return; + + if (!ht) { + ht = g_hash_table_new (mono_aligned_addr_hash, NULL); + destroy_ht = TRUE; + for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) { + g_hash_table_insert (ht, tmp->data, tmp->data); + } + } + + /* FIXME: handle lazy loaded assemblies */ + + if (!g_hash_table_lookup (ht, ass)) { + mono_assembly_addref (ass); + g_hash_table_insert (ht, ass, ass); + domain->domain_assemblies = g_slist_append (domain->domain_assemblies, ass); + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Assembly %s[%p] added to domain %s, ref_count=%d", ass->aname.name, ass, domain->friendly_name, ass->ref_count); + } + + if (ass->image->references) { + for (i = 0; i < ass->image->nreferences; i++) { + if (ass->image->references[i] && ass->image->references [i] != REFERENCE_MISSING) { + if (!g_hash_table_lookup (ht, ass->image->references [i])) { + add_assemblies_to_domain (domain, ass->image->references [i], ht); + } + } + } + } + if (destroy_ht) + g_hash_table_destroy (ht); +} + +static void +mono_domain_fire_assembly_load (MonoAssembly *assembly, gpointer user_data) +{ + HANDLE_FUNCTION_ENTER (); + static MonoClassField *assembly_load_field; + static MonoMethod *assembly_load_method; + MonoError error; + MonoDomain *domain = mono_domain_get (); + MonoClass *klass; + gpointer load_value; + void *params [1]; + + if (!domain->domain) + /* This can happen during startup */ + goto leave; +#ifdef ASSEMBLY_LOAD_DEBUG + fprintf (stderr, "Loading %s into domain %s\n", assembly->aname.name, domain->friendly_name); +#endif + klass = domain->domain->mbr.obj.vtable->klass; + + mono_domain_assemblies_lock (domain); + add_assemblies_to_domain (domain, assembly, NULL); + mono_domain_assemblies_unlock (domain); + + if (assembly_load_field == NULL) { + assembly_load_field = mono_class_get_field_from_name (klass, "AssemblyLoad"); + g_assert (assembly_load_field); + } + + mono_field_get_value ((MonoObject*) domain->domain, assembly_load_field, &load_value); + if (load_value == NULL) { + /* No events waiting to be triggered */ + goto leave; + } + + MonoReflectionAssemblyHandle ref_assembly = mono_assembly_get_object_handle (domain, assembly, &error); + mono_error_assert_ok (&error); + + if (assembly_load_method == NULL) { + assembly_load_method = mono_class_get_method_from_name (klass, "DoAssemblyLoad", -1); + g_assert (assembly_load_method); + } + + *params = MONO_HANDLE_RAW(ref_assembly); + + mono_runtime_invoke_checked (assembly_load_method, domain->domain, params, &error); + mono_error_cleanup (&error); +leave: + HANDLE_FUNCTION_RETURN (); +} + +/* + * LOCKING: Acquires the domain assemblies lock. + */ +static void +set_domain_search_path (MonoDomain *domain) +{ + MonoError error; + MonoAppDomainSetup *setup; + gchar **tmp; + gchar *search_path = NULL; + gint i; + gint npaths = 0; + gchar **pvt_split = NULL; + GError *gerror = NULL; + gint appbaselen = -1; + + /* + * We use the low-level domain assemblies lock, since this is called from + * assembly loads hooks, which means this thread might hold the loader lock. + */ + mono_domain_assemblies_lock (domain); + + if (!domain->setup) { + mono_domain_assemblies_unlock (domain); + return; + } + + if ((domain->search_path != NULL) && !domain->setup->path_changed) { + mono_domain_assemblies_unlock (domain); + return; + } + setup = domain->setup; + if (!setup->application_base) { + mono_domain_assemblies_unlock (domain); + return; /* Must set application base to get private path working */ + } + + npaths++; + + if (setup->private_bin_path) { + search_path = mono_string_to_utf8_checked (setup->private_bin_path, &error); + if (!mono_error_ok (&error)) { /*FIXME maybe we should bubble up the error.*/ + g_warning ("Could not decode AppDomain search path since it contains invalid characters"); + mono_error_cleanup (&error); + mono_domain_assemblies_unlock (domain); + return; + } + } + + if (domain->private_bin_path) { + if (search_path == NULL) + search_path = domain->private_bin_path; + else { + gchar *tmp2 = search_path; + search_path = g_strjoin (";", search_path, domain->private_bin_path, NULL); + g_free (tmp2); + } + } + + if (search_path) { + /* + * As per MSDN documentation, AppDomainSetup.PrivateBinPath contains a list of + * directories relative to ApplicationBase separated by semicolons (see + * http://msdn2.microsoft.com/en-us/library/system.appdomainsetup.privatebinpath.aspx) + * The loop below copes with the fact that some Unix applications may use ':' (or + * System.IO.Path.PathSeparator) as the path search separator. We replace it with + * ';' for the subsequent split. + * + * The issue was reported in bug #81446 + */ + +#ifndef TARGET_WIN32 + gint slen; + + slen = strlen (search_path); + for (i = 0; i < slen; i++) + if (search_path [i] == ':') + search_path [i] = ';'; +#endif + + pvt_split = g_strsplit (search_path, ";", 1000); + g_free (search_path); + for (tmp = pvt_split; *tmp; tmp++, npaths++); + } + + if (!npaths) { + if (pvt_split) + g_strfreev (pvt_split); + /* + * Don't do this because the first time is called, the domain + * setup is not finished. + * + * domain->search_path = g_malloc (sizeof (char *)); + * domain->search_path [0] = NULL; + */ + mono_domain_assemblies_unlock (domain); + return; + } + + if (domain->search_path) + g_strfreev (domain->search_path); + + tmp = (gchar **)g_malloc ((npaths + 1) * sizeof (gchar *)); + tmp [npaths] = NULL; + + *tmp = mono_string_to_utf8_checked (setup->application_base, &error); + if (!mono_error_ok (&error)) { + mono_error_cleanup (&error); + g_strfreev (pvt_split); + g_free (tmp); + + mono_domain_assemblies_unlock (domain); + return; + } + + domain->search_path = tmp; + + /* FIXME: is this needed? */ + if (strncmp (*tmp, "file://", 7) == 0) { + gchar *file = *tmp; + gchar *uri = *tmp; + gchar *tmpuri; + + if (uri [7] != '/') + uri = g_strdup_printf ("file:///%s", uri + 7); + + tmpuri = uri; + uri = mono_escape_uri_string (tmpuri); + *tmp = g_filename_from_uri (uri, NULL, &gerror); + g_free (uri); + + if (tmpuri != file) + g_free (tmpuri); + + if (gerror != NULL) { + g_warning ("%s\n", gerror->message); + g_error_free (gerror); + *tmp = file; + } else { + g_free (file); + } + } + + for (i = 1; pvt_split && i < npaths; i++) { + if (g_path_is_absolute (pvt_split [i - 1])) { + tmp [i] = g_strdup (pvt_split [i - 1]); + } else { + tmp [i] = g_build_filename (tmp [0], pvt_split [i - 1], NULL); + } + + if (strchr (tmp [i], '.')) { + gchar *reduced; + gchar *freeme; + + reduced = mono_path_canonicalize (tmp [i]); + if (appbaselen == -1) + appbaselen = strlen (tmp [0]); + + if (strncmp (tmp [0], reduced, appbaselen)) { + g_free (reduced); + g_free (tmp [i]); + tmp [i] = g_strdup (""); + continue; + } + + freeme = tmp [i]; + tmp [i] = reduced; + g_free (freeme); + } + } + + if (setup->private_bin_path_probe != NULL) { + g_free (tmp [0]); + tmp [0] = g_strdup (""); + } + + domain->setup->path_changed = FALSE; + + g_strfreev (pvt_split); + + mono_domain_assemblies_unlock (domain); +} + +#ifdef DISABLE_SHADOW_COPY +gboolean +mono_is_shadow_copy_enabled (MonoDomain *domain, const gchar *dir_name) +{ + return FALSE; +} + +char * +mono_make_shadow_copy (const char *filename, MonoError *error) +{ + error_init (error); + return (char *) filename; +} +#else +static gboolean +shadow_copy_sibling (gchar *src, gint srclen, const char *extension, gchar *target, gint targetlen, gint tail_len) +{ + guint16 *orig, *dest; + gboolean copy_result; + gint32 copy_error; + + strcpy (src + srclen - tail_len, extension); + + if (IS_PORTABILITY_CASE) { + gchar *file = mono_portability_find_file (src, TRUE); + + if (file == NULL) + return TRUE; + + g_free (file); + } else if (!g_file_test (src, G_FILE_TEST_IS_REGULAR)) { + return TRUE; + } + + orig = g_utf8_to_utf16 (src, strlen (src), NULL, NULL, NULL); + + strcpy (target + targetlen - tail_len, extension); + dest = g_utf8_to_utf16 (target, strlen (target), NULL, NULL, NULL); + + mono_w32file_delete (dest); + + copy_result = mono_w32file_copy (orig, dest, TRUE, ©_error); + + /* Fix for bug #556884 - make sure the files have the correct mode so that they can be + * overwritten when updated in their original locations. */ + if (copy_result) + copy_result = mono_w32file_set_attributes (dest, FILE_ATTRIBUTE_NORMAL); + + g_free (orig); + g_free (dest); + + return copy_result; +} + +static gint32 +get_cstring_hash (const char *str) +{ + int len, i; + const char *p; + gint32 h = 0; + + if (!str || !str [0]) + return 0; + + len = strlen (str); + p = str; + for (i = 0; i < len; i++) { + h = (h << 5) - h + *p; + p++; + } + + return h; +} + +/* + * Returned memory is malloc'd. Called must free it + */ +static char * +get_shadow_assembly_location_base (MonoDomain *domain, MonoError *error) +{ + MonoAppDomainSetup *setup; + char *cache_path, *appname; + char *userdir; + char *location; + + error_init (error); + + setup = domain->setup; + if (setup->cache_path != NULL && setup->application_name != NULL) { + cache_path = mono_string_to_utf8_checked (setup->cache_path, error); + return_val_if_nok (error, NULL); + +#ifndef TARGET_WIN32 + { + gint i; + for (i = strlen (cache_path) - 1; i >= 0; i--) + if (cache_path [i] == '\\') + cache_path [i] = '/'; + } +#endif + + appname = mono_string_to_utf8_checked (setup->application_name, error); + if (!mono_error_ok (error)) { + g_free (cache_path); + return NULL; + } + + location = g_build_filename (cache_path, appname, "assembly", "shadow", NULL); + g_free (appname); + g_free (cache_path); + } else { + userdir = g_strdup_printf ("%s-mono-cachepath", g_get_user_name ()); + location = g_build_filename (g_get_tmp_dir (), userdir, "assembly", "shadow", NULL); + g_free (userdir); + } + return location; +} + +static char * +get_shadow_assembly_location (const char *filename, MonoError *error) +{ + gint32 hash = 0, hash2 = 0; + char name_hash [9]; + char path_hash [30]; + char *bname = g_path_get_basename (filename); + char *dirname = g_path_get_dirname (filename); + char *location, *tmploc; + MonoDomain *domain = mono_domain_get (); + + error_init (error); + + hash = get_cstring_hash (bname); + hash2 = get_cstring_hash (dirname); + g_snprintf (name_hash, sizeof (name_hash), "%08x", hash); + g_snprintf (path_hash, sizeof (path_hash), "%08x_%08x_%08x", hash ^ hash2, hash2, domain->shadow_serial); + tmploc = get_shadow_assembly_location_base (domain, error); + if (!mono_error_ok (error)) { + g_free (bname); + g_free (dirname); + return NULL; + } + + location = g_build_filename (tmploc, name_hash, path_hash, bname, NULL); + g_free (tmploc); + g_free (bname); + g_free (dirname); + return location; +} + +static gboolean +private_file_needs_copying (const char *src, struct stat *sbuf_src, char *dest) +{ + struct stat sbuf_dest; + gchar *stat_src; + gchar *real_src = mono_portability_find_file (src, TRUE); + + if (!real_src) + stat_src = (gchar*)src; + else + stat_src = real_src; + + if (stat (stat_src, sbuf_src) == -1) { + time_t tnow = time (NULL); + + if (real_src) + g_free (real_src); + + memset (sbuf_src, 0, sizeof (*sbuf_src)); + sbuf_src->st_mtime = tnow; + sbuf_src->st_atime = tnow; + return TRUE; + } + + if (real_src) + g_free (real_src); + + if (stat (dest, &sbuf_dest) == -1) + return TRUE; + + if (sbuf_src->st_size == sbuf_dest.st_size && + sbuf_src->st_mtime == sbuf_dest.st_mtime) + return FALSE; + + return TRUE; +} + +static gboolean +shadow_copy_create_ini (const char *shadow, const char *filename) +{ + char *dir_name; + char *ini_file; + guint16 *u16_ini; + gboolean result; + guint32 n; + HANDLE *handle; + gchar *full_path; + + dir_name = g_path_get_dirname (shadow); + ini_file = g_build_filename (dir_name, "__AssemblyInfo__.ini", NULL); + g_free (dir_name); + if (g_file_test (ini_file, G_FILE_TEST_IS_REGULAR)) { + g_free (ini_file); + return TRUE; + } + + u16_ini = g_utf8_to_utf16 (ini_file, strlen (ini_file), NULL, NULL, NULL); + g_free (ini_file); + if (!u16_ini) { + return FALSE; + } + handle = (void **)mono_w32file_create (u16_ini, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, CREATE_NEW, FileAttributes_Normal); + g_free (u16_ini); + if (handle == INVALID_HANDLE_VALUE) { + return FALSE; + } + + full_path = mono_path_resolve_symlinks (filename); + result = mono_w32file_write (handle, full_path, strlen (full_path), &n); + g_free (full_path); + mono_w32file_close (handle); + return result; +} + +gboolean +mono_is_shadow_copy_enabled (MonoDomain *domain, const gchar *dir_name) +{ + MonoError error; + MonoAppDomainSetup *setup; + gchar *all_dirs; + gchar **dir_ptr; + gchar **directories; + gchar *shadow_status_string; + gchar *base_dir; + gboolean shadow_enabled; + gboolean found = FALSE; + + if (domain == NULL) + return FALSE; + + setup = domain->setup; + if (setup == NULL || setup->shadow_copy_files == NULL) + return FALSE; + + shadow_status_string = mono_string_to_utf8_checked (setup->shadow_copy_files, &error); + if (!mono_error_ok (&error)) { + mono_error_cleanup (&error); + return FALSE; + } + shadow_enabled = !g_ascii_strncasecmp (shadow_status_string, "true", 4); + g_free (shadow_status_string); + + if (!shadow_enabled) + return FALSE; + + if (setup->shadow_copy_directories == NULL) + return TRUE; + + /* Is dir_name a shadow_copy destination already? */ + base_dir = get_shadow_assembly_location_base (domain, &error); + if (!mono_error_ok (&error)) { + mono_error_cleanup (&error); + return FALSE; + } + + if (strstr (dir_name, base_dir)) { + g_free (base_dir); + return TRUE; + } + g_free (base_dir); + + all_dirs = mono_string_to_utf8_checked (setup->shadow_copy_directories, &error); + if (!mono_error_ok (&error)) { + mono_error_cleanup (&error); + return FALSE; + } + + directories = g_strsplit (all_dirs, G_SEARCHPATH_SEPARATOR_S, 1000); + dir_ptr = directories; + while (*dir_ptr) { + if (**dir_ptr != '\0' && !strcmp (*dir_ptr, dir_name)) { + found = TRUE; + break; + } + dir_ptr++; + } + g_strfreev (directories); + g_free (all_dirs); + return found; +} + +/* +This function raises exceptions so it can cause as sorts of nasty stuff if called +while holding a lock. +Returns old file name if shadow copy is disabled, new shadow copy file name if successful +or NULL if source file not found. +FIXME bubble up the error instead of raising it here +*/ +char * +mono_make_shadow_copy (const char *filename, MonoError *oerror) +{ + MonoError error; + gchar *sibling_source, *sibling_target; + gint sibling_source_len, sibling_target_len; + guint16 *orig, *dest; + guint32 attrs; + char *shadow; + gboolean copy_result; + struct stat src_sbuf; + struct utimbuf utbuf; + char *dir_name = g_path_get_dirname (filename); + MonoDomain *domain = mono_domain_get (); + char *shadow_dir; + gint32 copy_error; + + error_init (oerror); + + set_domain_search_path (domain); + + if (!mono_is_shadow_copy_enabled (domain, dir_name)) { + g_free (dir_name); + return (char *) filename; + } + + /* Is dir_name a shadow_copy destination already? */ + shadow_dir = get_shadow_assembly_location_base (domain, &error); + if (!mono_error_ok (&error)) { + mono_error_cleanup (&error); + g_free (dir_name); + mono_error_set_execution_engine (oerror, "Failed to create shadow copy (invalid characters in shadow directory name)."); + return NULL; + } + + if (strstr (dir_name, shadow_dir)) { + g_free (shadow_dir); + g_free (dir_name); + return (char *) filename; + } + g_free (shadow_dir); + g_free (dir_name); + + shadow = get_shadow_assembly_location (filename, &error); + if (!mono_error_ok (&error)) { + mono_error_cleanup (&error); + mono_error_set_execution_engine (oerror, "Failed to create shadow copy (invalid characters in file name)."); + return NULL; + } + + if (g_ensure_directory_exists (shadow) == FALSE) { + g_free (shadow); + mono_error_set_execution_engine (oerror, "Failed to create shadow copy (ensure directory exists)."); + return NULL; + } + + if (!private_file_needs_copying (filename, &src_sbuf, shadow)) + return (char*) shadow; + + orig = g_utf8_to_utf16 (filename, strlen (filename), NULL, NULL, NULL); + dest = g_utf8_to_utf16 (shadow, strlen (shadow), NULL, NULL, NULL); + mono_w32file_delete (dest); + + /* Fix for bug #17066 - make sure we can read the file. if not then don't error but rather + * let the assembly fail to load. This ensures you can do Type.GetType("NS.T, NonExistantAssembly) + * and not have it runtime error" */ + attrs = mono_w32file_get_attributes (orig); + if (attrs == INVALID_FILE_ATTRIBUTES) { + g_free (shadow); + return (char *)filename; + } + + copy_result = mono_w32file_copy (orig, dest, TRUE, ©_error); + + /* Fix for bug #556884 - make sure the files have the correct mode so that they can be + * overwritten when updated in their original locations. */ + if (copy_result) + copy_result = mono_w32file_set_attributes (dest, FILE_ATTRIBUTE_NORMAL); + + g_free (dest); + g_free (orig); + + if (copy_result == FALSE) { + g_free (shadow); + + /* Fix for bug #17251 - if file not found try finding assembly by other means (it is not fatal error) */ + if (mono_w32error_get_last() == ERROR_FILE_NOT_FOUND || mono_w32error_get_last() == ERROR_PATH_NOT_FOUND) + return NULL; /* file not found, shadow copy failed */ + + mono_error_set_execution_engine (oerror, "Failed to create shadow copy (mono_w32file_copy)."); + return NULL; + } + + /* attempt to copy .mdb, .config if they exist */ + sibling_source = g_strconcat (filename, ".config", NULL); + sibling_source_len = strlen (sibling_source); + sibling_target = g_strconcat (shadow, ".config", NULL); + sibling_target_len = strlen (sibling_target); + + copy_result = shadow_copy_sibling (sibling_source, sibling_source_len, ".mdb", sibling_target, sibling_target_len, 7); + if (copy_result) + copy_result = shadow_copy_sibling (sibling_source, sibling_source_len, ".pdb", sibling_target, sibling_target_len, 11); + if (copy_result) + copy_result = shadow_copy_sibling (sibling_source, sibling_source_len, ".config", sibling_target, sibling_target_len, 7); + + g_free (sibling_source); + g_free (sibling_target); + + if (!copy_result) { + g_free (shadow); + mono_error_set_execution_engine (oerror, "Failed to create shadow copy of sibling data (mono_w32file_copy)."); + return NULL; + } + + /* Create a .ini file containing the original assembly location */ + if (!shadow_copy_create_ini (shadow, filename)) { + g_free (shadow); + mono_error_set_execution_engine (oerror, "Failed to create shadow copy .ini file."); + return NULL; + } + + utbuf.actime = src_sbuf.st_atime; + utbuf.modtime = src_sbuf.st_mtime; + utime (shadow, &utbuf); + + return shadow; +} +#endif /* DISABLE_SHADOW_COPY */ + +/** + * mono_domain_from_appdomain: + */ +MonoDomain * +mono_domain_from_appdomain (MonoAppDomain *appdomain_raw) +{ + HANDLE_FUNCTION_ENTER (); + MONO_HANDLE_DCL (MonoAppDomain, appdomain); + MonoDomain *result = mono_domain_from_appdomain_handle (appdomain); + HANDLE_FUNCTION_RETURN_VAL (result); +} + +MonoDomain * +mono_domain_from_appdomain_handle (MonoAppDomainHandle appdomain) +{ + HANDLE_FUNCTION_ENTER (); + MonoDomain *dom = NULL; + if (MONO_HANDLE_IS_NULL (appdomain)) + goto leave; + + if (mono_class_is_transparent_proxy (mono_handle_class (appdomain))) { + MonoTransparentProxyHandle tp = MONO_HANDLE_CAST (MonoTransparentProxy, appdomain); + MonoRealProxyHandle rp = MONO_HANDLE_NEW_GET (MonoRealProxy, tp, rp); + + dom = mono_domain_get_by_id (MONO_HANDLE_GETVAL (rp, target_domain_id)); + } else + dom = MONO_HANDLE_GETVAL (appdomain, data); + +leave: + HANDLE_FUNCTION_RETURN_VAL (dom); +} + + +static gboolean +try_load_from (MonoAssembly **assembly, + const gchar *path1, const gchar *path2, + const gchar *path3, const gchar *path4, + gboolean refonly, gboolean is_private, + MonoAssemblyCandidatePredicate predicate, gpointer user_data) +{ + gchar *fullpath; + gboolean found = FALSE; + + *assembly = NULL; + fullpath = g_build_filename (path1, path2, path3, path4, NULL); + + if (IS_PORTABILITY_SET) { + gchar *new_fullpath = mono_portability_find_file (fullpath, TRUE); + if (new_fullpath) { + g_free (fullpath); + fullpath = new_fullpath; + found = TRUE; + } + } else + found = g_file_test (fullpath, G_FILE_TEST_IS_REGULAR); + + if (found) + *assembly = mono_assembly_open_predicate (fullpath, refonly, FALSE, predicate, user_data, NULL); + + g_free (fullpath); + return (*assembly != NULL); +} + +static MonoAssembly * +real_load (gchar **search_path, const gchar *culture, const gchar *name, gboolean refonly, MonoAssemblyCandidatePredicate predicate, gpointer user_data) +{ + MonoAssembly *result = NULL; + gchar **path; + gchar *filename; + const gchar *local_culture; + gint len; + gboolean is_private = FALSE; + + if (!culture || *culture == '\0') { + local_culture = ""; + } else { + local_culture = culture; + } + + filename = g_strconcat (name, ".dll", NULL); + len = strlen (filename); + + for (path = search_path; *path; path++) { + if (**path == '\0') { + is_private = TRUE; + continue; /* Ignore empty ApplicationBase */ + } + + /* See test cases in bug #58992 and bug #57710 */ + /* 1st try: [culture]/[name].dll (culture may be empty) */ + strcpy (filename + len - 4, ".dll"); + if (try_load_from (&result, *path, local_culture, "", filename, refonly, is_private, predicate, user_data)) + break; + + /* 2nd try: [culture]/[name].exe (culture may be empty) */ + strcpy (filename + len - 4, ".exe"); + if (try_load_from (&result, *path, local_culture, "", filename, refonly, is_private, predicate, user_data)) + break; + + /* 3rd try: [culture]/[name]/[name].dll (culture may be empty) */ + strcpy (filename + len - 4, ".dll"); + if (try_load_from (&result, *path, local_culture, name, filename, refonly, is_private, predicate, user_data)) + break; + + /* 4th try: [culture]/[name]/[name].exe (culture may be empty) */ + strcpy (filename + len - 4, ".exe"); + if (try_load_from (&result, *path, local_culture, name, filename, refonly, is_private, predicate, user_data)) + break; + } + + g_free (filename); + return result; +} + +/* + * Try loading the assembly from ApplicationBase and PrivateBinPath + * and then from assemblies_path if any. + * LOCKING: This is called from the assembly loading code, which means the caller + * might hold the loader lock. Thus, this function must not acquire the domain lock. + */ +static MonoAssembly * +mono_domain_assembly_preload (MonoAssemblyName *aname, + gchar **assemblies_path, + gpointer user_data) +{ + MonoDomain *domain = mono_domain_get (); + MonoAssembly *result = NULL; + gboolean refonly = GPOINTER_TO_UINT (user_data); + + set_domain_search_path (domain); + + MonoAssemblyCandidatePredicate predicate = NULL; + void* predicate_ud = NULL; +#if !defined(DISABLE_DESKTOP_LOADER) + if (G_LIKELY (mono_loader_get_strict_strong_names ())) { + predicate = &mono_assembly_candidate_predicate_sn_same_name; + predicate_ud = aname; + } +#endif + if (domain->search_path && domain->search_path [0] != NULL) { + if (mono_trace_is_traced (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, "Domain %s search path is:", domain->friendly_name); + for (int i = 0; domain->search_path [i]; i++) { + const char *p = domain->search_path[i]; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, "\tpath[%d] = '%s'", i, p); + } + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, "End of domain %s search path.", domain->friendly_name); + } + result = real_load (domain->search_path, aname->culture, aname->name, refonly, predicate, predicate_ud); + } + + if (result == NULL && assemblies_path && assemblies_path [0] != NULL) { + result = real_load (assemblies_path, aname->culture, aname->name, refonly, predicate, predicate_ud); + } + + return result; +} + +/* + * Check whenever a given assembly was already loaded in the current appdomain. + */ +static MonoAssembly * +mono_domain_assembly_search (MonoAssemblyName *aname, + gpointer user_data) +{ + MonoDomain *domain = mono_domain_get (); + 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 && !ignore_version_and_key_when_finding_assemblies_already_loaded) ? 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_flags (aname, &ass->aname, eq_flags)) + continue; + + mono_domain_assemblies_unlock (domain); + return ass; + } + mono_domain_assemblies_unlock (domain); + + return NULL; +} + +MonoReflectionAssemblyHandle +ves_icall_System_Reflection_Assembly_LoadFrom (MonoStringHandle fname, MonoBoolean refOnly, MonoError *error) +{ + error_init (error); + MonoDomain *domain = mono_domain_get (); + char *name, *filename; + MonoImageOpenStatus status = MONO_IMAGE_OK; + MonoReflectionAssemblyHandle result = MONO_HANDLE_CAST (MonoReflectionAssembly, NULL_HANDLE); + + name = NULL; + result = NULL; + + if (fname == NULL) { + mono_error_set_argument_null (error, "assemblyFile", ""); + goto leave; + } + + name = filename = mono_string_handle_to_utf8 (fname, error); + goto_if_nok (error, leave); + + MonoAssembly *ass = mono_assembly_open_predicate (filename, refOnly, TRUE, NULL, NULL, &status); + + if (!ass) { + if (status == MONO_IMAGE_IMAGE_INVALID) + mono_error_set_bad_image_name (error, g_strdup (name), ""); + else + mono_error_set_assembly_load (error, g_strdup (name), "%s", ""); + goto leave; + } + + result = mono_assembly_get_object_handle (domain, ass, error); + +leave: + g_free (name); + return result; +} + +MonoReflectionAssemblyHandle +ves_icall_System_AppDomain_LoadAssemblyRaw (MonoAppDomainHandle ad, + MonoArrayHandle raw_assembly, + MonoArrayHandle raw_symbol_store, MonoObjectHandle evidence, + MonoBoolean refonly, + MonoError *error) +{ + error_init (error); + MonoAssembly *ass; + MonoReflectionAssemblyHandle refass = MONO_HANDLE_CAST (MonoReflectionAssembly, NULL_HANDLE); + MonoDomain *domain = MONO_HANDLE_GETVAL(ad, data); + MonoImageOpenStatus status; + guint32 raw_assembly_len = mono_array_handle_length (raw_assembly); + + /* Copy the data ourselves to unpin the raw assembly byte array as soon as possible */ + char *assembly_data = (char*) g_try_malloc (raw_assembly_len); + if (!assembly_data) { + mono_error_set_out_of_memory (error, "Could not allocate %ud bytes to copy raw assembly data", raw_assembly_len); + return refass; + } + uint32_t gchandle; + mono_byte *raw_data = (mono_byte*) MONO_ARRAY_HANDLE_PIN (raw_assembly, gchar, 0, &gchandle); + memcpy (assembly_data, raw_data, raw_assembly_len); + mono_gchandle_free (gchandle); /* unpin */ + MONO_HANDLE_ASSIGN (raw_assembly, NULL_HANDLE); /* don't reference the data anymore */ + + MonoImage *image = mono_image_open_from_data_full (assembly_data, raw_assembly_len, FALSE, NULL, refonly); + + if (!image) { + mono_error_set_bad_image_name (error, g_strdup (""), "%s", ""); + return refass; + } + + if (!MONO_HANDLE_IS_NULL(raw_symbol_store)) { + guint32 symbol_len = mono_array_handle_length (raw_symbol_store); + uint32_t symbol_gchandle; + mono_byte *raw_symbol_data = (mono_byte*) MONO_ARRAY_HANDLE_PIN (raw_symbol_store, mono_byte, 0, &symbol_gchandle); + mono_debug_open_image_from_memory (image, raw_symbol_data, symbol_len); + mono_gchandle_free (symbol_gchandle); + } + + ass = mono_assembly_load_from_full (image, "", &status, refonly); + + + if (!ass) { + mono_image_close (image); + mono_error_set_bad_image_name (error, g_strdup (""), "%s", ""); + 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); + return refass; +} + +MonoReflectionAssemblyHandle +ves_icall_System_AppDomain_LoadAssembly (MonoAppDomainHandle ad, MonoStringHandle assRef, MonoObjectHandle evidence, MonoBoolean refOnly, MonoError *error) +{ + error_init (error); + MonoDomain *domain = MONO_HANDLE_GETVAL (ad, data); + MonoImageOpenStatus status = MONO_IMAGE_OK; + MonoAssembly *ass; + MonoAssemblyName aname; + gchar *name = NULL; + gboolean parsed; + + g_assert (assRef); + + name = mono_string_handle_to_utf8 (assRef, error); + goto_if_nok (error, fail); + parsed = mono_assembly_name_parse (name, &aname); + g_free (name); + + if (!parsed) { + MonoReflectionAssemblyHandle refass = MONO_HANDLE_CAST (MonoReflectionAssembly, NULL_HANDLE); + /* This is a parse error... */ + if (!refOnly) { + MonoAssembly *assm = mono_try_assembly_resolve_handle (domain, assRef, NULL, refOnly, error); + goto_if_nok (error, fail); + if (assm) { + refass = mono_assembly_get_object_handle (domain, assm, error); + goto_if_nok (error, fail); + } + } + return refass; + } + + ass = mono_assembly_load_full_nosearch (&aname, NULL, &status, refOnly); + mono_assembly_name_free (&aname); + + if (!ass) { + /* MS.NET doesn't seem to call the assembly resolve handler for refonly assemblies */ + if (!refOnly) { + ass = mono_try_assembly_resolve_handle (domain, assRef, NULL, refOnly, error); + goto_if_nok (error, fail); + } + if (!ass) + goto fail; + } + + g_assert (ass); + MonoReflectionAssemblyHandle refass = mono_assembly_get_object_handle (domain, ass, error); + goto_if_nok (error, fail); + + MONO_HANDLE_SET (refass, evidence, evidence); + + return refass; +fail: + return MONO_HANDLE_CAST (MonoReflectionAssembly, NULL_HANDLE); +} + +void +ves_icall_System_AppDomain_InternalUnload (gint32 domain_id, MonoError *error) +{ + error_init (error); + MonoDomain * domain = mono_domain_get_by_id (domain_id); + + if (NULL == domain) { + mono_error_set_execution_engine (error, "Failed to unload domain, domain id not found"); + return; + } + + if (domain == mono_get_root_domain ()) { + mono_error_set_generic_error (error, "System", "CannotUnloadAppDomainException", "The default appdomain can not be unloaded."); + return; + } + + /* + * Unloading seems to cause problems when running NUnit/NAnt, hence + * this workaround. + */ + if (g_hasenv ("MONO_NO_UNLOAD")) + return; + + MonoException *exc = NULL; + mono_domain_try_unload (domain, (MonoObject**)&exc, NULL); + if (exc) + mono_error_set_exception_instance (error, exc); +} + +gboolean +ves_icall_System_AppDomain_InternalIsFinalizingForUnload (gint32 domain_id, MonoError *error) +{ + error_init (error); + MonoDomain *domain = mono_domain_get_by_id (domain_id); + + if (!domain) + return TRUE; + + return mono_domain_is_unloading (domain); +} + +void +ves_icall_System_AppDomain_DoUnhandledException (MonoExceptionHandle exc, MonoError *error) +{ + error_init (error); + mono_unhandled_exception_checked (MONO_HANDLE_CAST (MonoObject, exc), error); + mono_error_assert_ok (error); +} + +gint32 +ves_icall_System_AppDomain_ExecuteAssembly (MonoAppDomainHandle ad, + MonoReflectionAssemblyHandle refass, MonoArrayHandle args, + MonoError *error) +{ + error_init (error); + MonoImage *image; + MonoMethod *method; + + g_assert (!MONO_HANDLE_IS_NULL (refass)); + MonoAssembly *assembly = MONO_HANDLE_GETVAL (refass, assembly); + image = assembly->image; + g_assert (image); + + method = mono_get_method_checked (image, mono_image_get_entry_point (image), NULL, NULL, error); + + if (!method) + g_error ("No entry point method found in %s due to %s", image->name, mono_error_get_message (error)); + + if (MONO_HANDLE_IS_NULL (args)) { + MonoDomain *domain = MONO_HANDLE_GETVAL (ad, data); + MONO_HANDLE_ASSIGN (args , mono_array_new_handle (domain, mono_defaults.string_class, 0, error)); + mono_error_assert_ok (error); + } + + int res = mono_runtime_exec_main_checked (method, MONO_HANDLE_RAW (args), error); + return res; +} + +gint32 +ves_icall_System_AppDomain_GetIDFromDomain (MonoAppDomain * ad) +{ + return ad->data->domain_id; +} + +MonoAppDomainHandle +ves_icall_System_AppDomain_InternalSetDomain (MonoAppDomainHandle ad, MonoError* error) +{ + error_init (error); + MonoDomain *old_domain = mono_domain_get (); + + if (!mono_domain_set (MONO_HANDLE_GETVAL (ad, data), FALSE)) { + mono_error_set_appdomain_unloaded (error); + return MONO_HANDLE_CAST (MonoAppDomain, NULL_HANDLE); + } + + return MONO_HANDLE_NEW (MonoAppDomain, old_domain->domain); +} + +MonoAppDomainHandle +ves_icall_System_AppDomain_InternalSetDomainByID (gint32 domainid, MonoError *error) +{ + MonoDomain *current_domain = mono_domain_get (); + MonoDomain *domain = mono_domain_get_by_id (domainid); + + if (!domain || !mono_domain_set (domain, FALSE)) { + mono_error_set_appdomain_unloaded (error); + return MONO_HANDLE_CAST (MonoAppDomain, NULL_HANDLE); + } + + return MONO_HANDLE_NEW (MonoAppDomain, current_domain->domain); +} + +void +ves_icall_System_AppDomain_InternalPushDomainRef (MonoAppDomainHandle ad, MonoError *error) +{ + error_init (error); + mono_thread_push_appdomain_ref (MONO_HANDLE_GETVAL (ad, data)); +} + +void +ves_icall_System_AppDomain_InternalPushDomainRefByID (gint32 domain_id, MonoError *error) +{ + error_init (error); + MonoDomain *domain = mono_domain_get_by_id (domain_id); + + if (!domain) { + /* + * Raise an exception to prevent the managed code from executing a pop + * later. + */ + mono_error_set_appdomain_unloaded (error); + return; + } + + mono_thread_push_appdomain_ref (domain); +} + +void +ves_icall_System_AppDomain_InternalPopDomainRef (MonoError *error) +{ + error_init (error); + mono_thread_pop_appdomain_ref (); +} + +MonoAppContextHandle +ves_icall_System_AppDomain_InternalGetContext (MonoError *error) +{ + error_init (error); + return mono_context_get_handle (); +} + +MonoAppContextHandle +ves_icall_System_AppDomain_InternalGetDefaultContext (MonoError *error) +{ + error_init (error); + return MONO_HANDLE_NEW (MonoAppContext, mono_domain_get ()->default_context); +} + +MonoAppContextHandle +ves_icall_System_AppDomain_InternalSetContext (MonoAppContextHandle mc, MonoError *error) +{ + error_init (error); + MonoAppContextHandle old_context = mono_context_get_handle (); + + mono_context_set_handle (mc); + + return old_context; +} + +MonoStringHandle +ves_icall_System_AppDomain_InternalGetProcessGuid (MonoStringHandle newguid, MonoError *error) +{ + error_init (error); + MonoDomain* mono_root_domain = mono_get_root_domain (); + mono_domain_lock (mono_root_domain); + if (process_guid_set) { + mono_domain_unlock (mono_root_domain); + return mono_string_new_utf16_handle (mono_domain_get (), process_guid, sizeof(process_guid)/2, error); + } + uint32_t gchandle = mono_gchandle_from_handle (MONO_HANDLE_CAST (MonoObject, newguid), TRUE); + memcpy (process_guid, mono_string_chars(MONO_HANDLE_RAW (newguid)), sizeof(process_guid)); + mono_gchandle_free (gchandle); + process_guid_set = TRUE; + mono_domain_unlock (mono_root_domain); + return newguid; +} + +/** + * mono_domain_is_unloading: + */ +gboolean +mono_domain_is_unloading (MonoDomain *domain) +{ + if (domain->state == MONO_APPDOMAIN_UNLOADING || domain->state == MONO_APPDOMAIN_UNLOADED) + return TRUE; + else + return FALSE; +} + +static void +clear_cached_vtable (MonoVTable *vtable) +{ + MonoClass *klass = vtable->klass; + MonoDomain *domain = vtable->domain; + MonoClassRuntimeInfo *runtime_info; + void *data; + + runtime_info = klass->runtime_info; + if (runtime_info && runtime_info->max_domain >= domain->domain_id) + runtime_info->domain_vtables [domain->domain_id] = NULL; + if (klass->has_static_refs && (data = mono_vtable_get_static_field_data (vtable))) + mono_gc_free_fixed (data); +} + +static G_GNUC_UNUSED void +zero_static_data (MonoVTable *vtable) +{ + MonoClass *klass = vtable->klass; + void *data; + + if (klass->has_static_refs && (data = mono_vtable_get_static_field_data (vtable))) + mono_gc_bzero_aligned (data, mono_class_data_size (klass)); +} + +typedef struct unload_data { + gboolean done; + MonoDomain *domain; + char *failure_reason; + gint32 refcount; +} unload_data; + +static void +unload_data_unref (unload_data *data) +{ + gint32 count; + do { + mono_atomic_load_acquire (count, gint32, &data->refcount); + g_assert (count >= 1 && count <= 2); + if (count == 1) { + g_free (data); + return; + } + } while (mono_atomic_cas_i32 (&data->refcount, count - 1, count) != count); +} + +static void +deregister_reflection_info_roots_from_list (MonoImage *image) +{ + GSList *list = image->reflection_info_unregister_classes; + + while (list) { + MonoClass *klass = (MonoClass *)list->data; + + mono_class_free_ref_info (klass); + + list = list->next; + } + + image->reflection_info_unregister_classes = NULL; +} + +static void +deregister_reflection_info_roots (MonoDomain *domain) +{ + GSList *list; + + mono_domain_assemblies_lock (domain); + for (list = domain->domain_assemblies; list; list = list->next) { + MonoAssembly *assembly = (MonoAssembly *)list->data; + MonoImage *image = assembly->image; + int i; + + /* + * No need to take the image lock here since dynamic images are appdomain bound and + * at this point the mutator is gone. Taking the image lock here would mean + * promoting it from a simple lock to a complex lock, which we better avoid if + * possible. + */ + if (image_is_dynamic (image)) + deregister_reflection_info_roots_from_list (image); + + for (i = 0; i < image->module_count; ++i) { + MonoImage *module = image->modules [i]; + if (module && image_is_dynamic (module)) + deregister_reflection_info_roots_from_list (module); + } + } + mono_domain_assemblies_unlock (domain); +} + +static gsize WINAPI +unload_thread_main (void *arg) +{ + MonoError error; + unload_data *data = (unload_data*)arg; + MonoDomain *domain = data->domain; + MonoInternalThread *internal; + int i; + + internal = mono_thread_internal_current (); + + MonoString *thread_name_str = mono_string_new_checked (mono_domain_get (), "Domain unloader", &error); + if (is_ok (&error)) + mono_thread_set_name_internal (internal, thread_name_str, TRUE, FALSE, &error); + if (!is_ok (&error)) { + data->failure_reason = g_strdup (mono_error_get_message (&error)); + mono_error_cleanup (&error); + goto failure; + } + + /* + * FIXME: Abort our parent thread last, so we can return a failure + * indication if aborting times out. + */ + if (!mono_threads_abort_appdomain_threads (domain, -1)) { + data->failure_reason = g_strdup_printf ("Aborting of threads in domain %s timed out.", domain->friendly_name); + goto failure; + } + + if (!mono_threadpool_remove_domain_jobs (domain, -1)) { + data->failure_reason = g_strdup_printf ("Cleanup of threadpool jobs of domain %s timed out.", domain->friendly_name); + goto failure; + } + + /* Finalize all finalizable objects in the doomed appdomain */ + if (!mono_domain_finalize (domain, -1)) { + data->failure_reason = g_strdup_printf ("Finalization of domain %s timed out.", domain->friendly_name); + goto failure; + } + + /* Clear references to our vtables in class->runtime_info. + * We also hold the loader lock because we're going to change + * class->runtime_info. + */ + + mono_loader_lock (); //FIXME why do we need the loader lock here? + mono_domain_lock (domain); + /* + * We need to make sure that we don't have any remsets + * pointing into static data of the to-be-freed domain because + * at the next collections they would be invalid. So what we + * do is we first zero all static data and then do a minor + * collection. Because all references in the static data will + * now be null we won't do any unnecessary copies and after + * the collection there won't be any more remsets. + */ + for (i = 0; i < domain->class_vtable_array->len; ++i) + zero_static_data ((MonoVTable *)g_ptr_array_index (domain->class_vtable_array, i)); + mono_gc_collect (0); + 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); + + mono_assembly_cleanup_domain_bindings (domain->domain_id); + + mono_domain_unlock (domain); + mono_loader_unlock (); + + domain->state = MONO_APPDOMAIN_UNLOADED; + + /* printf ("UNLOADED %s.\n", domain->friendly_name); */ + + /* remove from the handle table the items related to this domain */ + mono_gchandle_free_domain (domain); + + 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; + +failure: + mono_atomic_store_release (&data->done, TRUE); + unload_data_unref (data); + return 1; +} + +/** + * mono_domain_unload: + * \param domain The domain to unload + * + * Unloads an appdomain. Follows the process outlined in the comment + * for \c mono_domain_try_unload. + */ +void +mono_domain_unload (MonoDomain *domain) +{ + MonoObject *exc = NULL; + mono_domain_try_unload (domain, &exc, NULL); +} + +static MonoThreadInfoWaitRet +guarded_wait (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertable) +{ + MonoThreadInfoWaitRet result; + + MONO_ENTER_GC_SAFE; + result = mono_thread_info_wait_one_handle (thread_handle, timeout, alertable); + MONO_EXIT_GC_SAFE; + + return result; +} + +/** + * 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 + * + * If doing things the 'right' way is too hard or complex, we do it the + * 'simple' way, which means do everything needed to avoid crashes and + * memory leaks, but not much else. + * + * It is required to pass a valid reference to the exc argument, upon return + * from this function *exc will be set to the exception thrown, if any. + * + * If this method is not called from an icall (embedded scenario for instance), + * it must not be called with any managed frames on the stack, since the unload + * process could end up trying to abort the current thread. + */ +void +mono_domain_try_unload (MonoDomain *domain, MonoObject **exc, MonoUnityExceptionFunc callback) +{ + MonoError error; + MonoThreadHandle *thread_handle; + MonoAppDomainState prev_state; + MonoMethod *method; + unload_data *thread_data; + MonoInternalThread *internal; + MonoDomain *caller_domain = mono_domain_get (); + + /* printf ("UNLOAD STARTING FOR %s (%p) IN THREAD 0x%x.\n", domain->friendly_name, domain, mono_native_thread_id_get ()); */ + + /* Atomically change our state to UNLOADING */ + prev_state = (MonoAppDomainState)mono_atomic_cas_i32 ((gint32*)&domain->state, + MONO_APPDOMAIN_UNLOADING_START, + MONO_APPDOMAIN_CREATED); + if (prev_state != MONO_APPDOMAIN_CREATED) { + switch (prev_state) { + case MONO_APPDOMAIN_UNLOADING_START: + case MONO_APPDOMAIN_UNLOADING: + *exc = (MonoObject *) mono_get_exception_cannot_unload_appdomain ("Appdomain is already being unloaded."); + return; + case MONO_APPDOMAIN_UNLOADED: + *exc = (MonoObject *) mono_get_exception_cannot_unload_appdomain ("Appdomain is already unloaded."); + return; + default: + g_warning ("Invalid appdomain state %d", prev_state); + g_assert_not_reached (); + } + } + + mono_domain_set (domain, FALSE); + /* Notify OnDomainUnload listeners */ + method = mono_class_get_method_from_name (domain->domain->mbr.obj.vtable->klass, "DoDomainUnload", -1); + g_assert (method); + + mono_runtime_try_invoke (method, domain->domain, NULL, exc, &error); + + if (!mono_error_ok (&error)) { + if (*exc) + mono_error_cleanup (&error); + else + *exc = (MonoObject*)mono_error_convert_to_exception (&error); + } + + if (*exc) { + 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); + + thread_data = g_new0 (unload_data, 1); + thread_data->domain = domain; + thread_data->failure_reason = NULL; + thread_data->done = FALSE; + thread_data->refcount = 2; /*Must be 2: unload thread + initiator */ + + /*The managed callback finished successfully, now we start tearing down the appdomain*/ + domain->state = MONO_APPDOMAIN_UNLOADING; + /* + * First we create a separate thread for unloading, since + * we might have to abort some threads, including the current one. + * + * Have to attach to the runtime so shutdown can wait for this thread. + * + * Force it to be attached to avoid racing during shutdown. + */ + internal = mono_thread_create_internal (mono_get_root_domain (), unload_thread_main, thread_data, MONO_THREAD_CREATE_FLAGS_FORCE_CREATE, &error); + mono_error_assert_ok (&error); + + thread_handle = mono_threads_open_thread_handle (internal->handle); + + /* Wait for the thread */ + while (!thread_data->done && guarded_wait (thread_handle, MONO_INFINITE_WAIT, TRUE) == MONO_THREAD_INFO_WAIT_RET_ALERTED) { + if (mono_thread_internal_has_appdomain_ref (mono_thread_internal_current (), domain) && (mono_thread_interruption_requested ())) { + /* The unload thread tries to abort us */ + /* The icall wrapper will execute the abort */ + mono_threads_close_thread_handle (thread_handle); + unload_data_unref (thread_data); + return; + } + } + + mono_threads_close_thread_handle (thread_handle); + + if (thread_data->failure_reason) { + /* Roll back the state change */ + domain->state = MONO_APPDOMAIN_CREATED; + + g_warning ("%s", thread_data->failure_reason); + + *exc = (MonoObject *) mono_get_exception_cannot_unload_appdomain (thread_data->failure_reason); + + g_free (thread_data->failure_reason); + thread_data->failure_reason = NULL; + } + + unload_data_unref (thread_data); +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/appdomain.h b/unity-2019.4.24f1-mbe/mono/metadata/appdomain.h new file mode 100644 index 000000000..0cafe36c3 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/appdomain.h @@ -0,0 +1,254 @@ +/** + * \file + * AppDomain functions + * + * Author: + * Dietmar Maurer (dietmar@ximian.com) + * + * (C) 2001 Ximian, Inc. + */ + +#ifndef _MONO_METADATA_APPDOMAIN_H_ +#define _MONO_METADATA_APPDOMAIN_H_ + +#include + +#include +#include + +MONO_BEGIN_DECLS + +typedef void (*MonoThreadStartCB) (intptr_t tid, void* stack_start, + void* func); +typedef void (*MonoThreadAttachCB) (intptr_t tid, void* stack_start); + +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 (*MonoJitInfoFunc)(MonoDomain *domain, MonoMethod* method, MonoJitInfo* jinfo, void* user_data); +typedef void (*MonoUnityExceptionFunc) (MonoObject* exc); + +MONO_API MonoDomain* +mono_init (const char *filename); + +MONO_API MonoDomain * +mono_init_from_assembly (const char *domain_name, const char *filename); + +MONO_API MonoDomain * +mono_init_version (const char *domain_name, const char *version); + +MONO_API MonoDomain* +mono_get_root_domain (void); + +MONO_RT_EXTERNAL_ONLY +MONO_API void +mono_runtime_init (MonoDomain *domain, MonoThreadStartCB start_cb, + MonoThreadAttachCB attach_cb); + +MONO_API void +mono_runtime_cleanup (MonoDomain *domain); + +MONO_API void +mono_install_runtime_cleanup (MonoDomainFunc func); + +MONO_API void +mono_runtime_quit (void); + +MONO_API void +mono_runtime_set_shutting_down (void); + +MONO_API mono_bool +mono_runtime_is_shutting_down (void); + +MONO_API const char* +mono_check_corlib_version (void); + +MONO_API MonoDomain * +mono_domain_create (void); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoDomain * +mono_domain_create_appdomain (char *friendly_name, char *configuration_file); + +MONO_RT_EXTERNAL_ONLY +MONO_API void +mono_domain_set_config (MonoDomain *domain, const char *base_dir, const char *config_file_name); + +MONO_API MonoDomain * +mono_domain_get (void); + +MONO_API MonoDomain * +mono_domain_get_by_id (int32_t domainid); + +MONO_API int32_t +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); + +MONO_API void +mono_domain_set_internal (MonoDomain *domain); + +MONO_RT_EXTERNAL_ONLY +MONO_API void +mono_domain_unload (MonoDomain *domain); + +MONO_API void +mono_domain_try_unload (MonoDomain *domain, MonoObject **exc, MonoUnityExceptionFunc callback); + +MONO_API mono_bool +mono_domain_is_unloading (MonoDomain *domain); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoDomain * +mono_domain_from_appdomain (MonoAppDomain *appdomain); + +MONO_API void +mono_domain_jit_foreach (MonoDomain *domain, MonoJitInfoFunc func, void *user_data); + +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); + +MONO_API void +mono_set_ignore_version_and_key_when_finding_assemblies_already_loaded(mono_bool value); + +MONO_API mono_bool +mono_domain_finalize (MonoDomain *domain, uint32_t timeout); + +MONO_API void +mono_domain_free (MonoDomain *domain, mono_bool force); + +MONO_API mono_bool +mono_domain_has_type_resolve (MonoDomain *domain); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoReflectionAssembly * +mono_domain_try_type_resolve (MonoDomain *domain, char *name, MonoObject *tb); + +MONO_API mono_bool +mono_domain_owns_vtable_slot (MonoDomain *domain, void* vtable_slot); + +MONO_RT_EXTERNAL_ONLY +MONO_API void +mono_context_init (MonoDomain *domain); + +MONO_RT_EXTERNAL_ONLY +MONO_API void +mono_context_set (MonoAppContext *new_context); + +MONO_API MonoAppContext * +mono_context_get (void); + +MONO_API int32_t +mono_context_get_id (MonoAppContext *context); + +MONO_API int32_t +mono_context_get_domain_id (MonoAppContext *context); + +MONO_API MonoJitInfo * +mono_jit_info_table_find (MonoDomain *domain, char *addr); + +/* MonoJitInfo accessors */ + +MONO_API void* +mono_jit_info_get_code_start (MonoJitInfo* ji); + +MONO_API int +mono_jit_info_get_code_size (MonoJitInfo* ji); + +MONO_API MonoMethod* +mono_jit_info_get_method (MonoJitInfo* ji); + + +MONO_API MonoImage* +mono_get_corlib (void); + +MONO_API MonoClass* +mono_get_object_class (void); + +MONO_API MonoClass* +mono_get_byte_class (void); + +MONO_API MonoClass* +mono_get_void_class (void); + +MONO_API MonoClass* +mono_get_boolean_class (void); + +MONO_API MonoClass* +mono_get_sbyte_class (void); + +MONO_API MonoClass* +mono_get_int16_class (void); + +MONO_API MonoClass* +mono_get_uint16_class (void); + +MONO_API MonoClass* +mono_get_int32_class (void); + +MONO_API MonoClass* +mono_get_uint32_class (void); + +MONO_API MonoClass* +mono_get_intptr_class (void); + +MONO_API MonoClass* +mono_get_uintptr_class (void); + +MONO_API MonoClass* +mono_get_int64_class (void); + +MONO_API MonoClass* +mono_get_uint64_class (void); + +MONO_API MonoClass* +mono_get_single_class (void); + +MONO_API MonoClass* +mono_get_double_class (void); + +MONO_API MonoClass* +mono_get_char_class (void); + +MONO_API MonoClass* +mono_get_string_class (void); + +MONO_API MonoClass* +mono_get_enum_class (void); + +MONO_API MonoClass* +mono_get_array_class (void); + +MONO_API MonoClass* +mono_get_thread_class (void); + +MONO_API MonoClass* +mono_get_exception_class (void); + +MONO_API void +mono_security_enable_core_clr (void); + +typedef mono_bool (*MonoCoreClrPlatformCB) (const char *image_name); + +MONO_API void +mono_security_set_core_clr_platform_callback (MonoCoreClrPlatformCB callback); + +MONO_END_DECLS + +#endif /* _MONO_METADATA_APPDOMAIN_H_ */ + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/assembly-internals.h b/unity-2019.4.24f1-mbe/mono/metadata/assembly-internals.h new file mode 100644 index 000000000..3116ffae1 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/assembly-internals.h @@ -0,0 +1,59 @@ +/** + * \file + * Copyright 2015 Xamarin Inc + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#ifndef __MONO_METADATA_ASSEMBLY_INTERNALS_H__ +#define __MONO_METADATA_ASSEMBLY_INTERNALS_H__ + +#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); + +MonoAssembly * mono_assembly_open_a_lot (const char *filename, MonoImageOpenStatus *status, gboolean refonly, gboolean load_from_context); + +/* If predicate returns true assembly should be loaded, if false ignore it. */ +typedef gboolean (*MonoAssemblyCandidatePredicate)(MonoAssembly *, gpointer); + +MonoAssembly* mono_assembly_open_predicate (const char *filename, + gboolean refonly, + gboolean load_from_context, + MonoAssemblyCandidatePredicate pred, + gpointer user_data, + MonoImageOpenStatus *status); + +MonoAssembly* mono_assembly_load_from_predicate (MonoImage *image, const char *fname, + gboolean refonly, + MonoAssemblyCandidatePredicate pred, + gpointer user_data, + MonoImageOpenStatus *status); + +/* MonoAssemblyCandidatePredicate that compares the assembly name (name, version, + * culture, public key token) of the candidate with the wanted name, if the + * wanted name has a public key token (if not present, always return true). + * Pass the wanted MonoAssemblyName* as the user_data. + */ +gboolean +mono_assembly_candidate_predicate_sn_same_name (MonoAssembly *candidate, gpointer wanted_name); + +#endif /* __MONO_METADATA_ASSEMBLY_INTERNALS_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/assembly.c b/unity-2019.4.24f1-mbe/mono/metadata/assembly.c new file mode 100644 index 000000000..3b5c81d75 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/assembly.c @@ -0,0 +1,4202 @@ +/** + * \file + * Routines for loading assemblies. + * + * Author: + * Miguel de Icaza (miguel@ximian.com) + * + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Copyright 2011 Xamarin, Inc (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#include +#include +#include +#include +#include +#include +#include "assembly.h" +#include "assembly-internals.h" +#include "image.h" +#include "image-internals.h" +#include "object-internals.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef HOST_WIN32 +#include +#include +#include +#endif + +#ifdef HOST_DARWIN +#include +#endif + +/* AssemblyVersionMap: an assembly name, the assembly version set on which it is based, the assembly name it is replaced with and whether only versions lower than the current runtime version should be remapped */ +typedef struct { + const char* assembly_name; + guint8 version_set_index; + const char* new_assembly_name; + gboolean only_lower_versions; + gboolean framework_facade_assembly; +} AssemblyVersionMap; + +/* the default search path is empty, the first slot is replaced with the computed value */ +static const char* +default_path [] = { + NULL, + NULL, + NULL +}; + +/* Contains the list of directories to be searched for assemblies (MONO_PATH) */ +static char **assemblies_path = NULL; + +/* Contains the list of directories that point to auxiliary GACs */ +static char **extra_gac_paths = NULL; + +#ifndef DISABLE_DESKTOP_LOADER + +#define FACADE_ASSEMBLY(str) {str, 0, NULL, FALSE, TRUE} + +static GHashTable* assembly_remapping_table; +/* The list of system assemblies what will be remapped to the running + * runtime version. + * This list is stored in @assembly_remapping_table during initialization. + * Keep it sorted just to make maintenance easier. + * + * The integer number is an index in the MonoRuntimeInfo structure, whose + * values can be found in domain.c - supported_runtimes. Look there + * to understand what remapping will be made. + * + * .NET version can be found at https://github.com/dotnet/coreclr/blob/master/src/inc/fxretarget.h#L99 + * + */ +static const AssemblyVersionMap framework_assemblies [] = { + {"Accessibility", 0}, + {"Commons.Xml.Relaxng", 0}, + {"I18N", 0}, + {"I18N.CJK", 0}, + {"I18N.MidEast", 0}, + {"I18N.Other", 0}, + {"I18N.Rare", 0}, + {"I18N.West", 0}, + {"Microsoft.Build.Engine", 2, NULL, TRUE}, + {"Microsoft.Build.Framework", 2, NULL, TRUE}, + {"Microsoft.Build.Tasks", 2, "Microsoft.Build.Tasks.v4.0"}, + {"Microsoft.Build.Tasks.v3.5", 2, "Microsoft.Build.Tasks.v4.0"}, + {"Microsoft.Build.Utilities", 2, "Microsoft.Build.Utilities.v4.0"}, + {"Microsoft.Build.Utilities.v3.5", 2, "Microsoft.Build.Utilities.v4.0"}, + {"Microsoft.VisualBasic", 1}, + {"Microsoft.VisualC", 1}, + FACADE_ASSEMBLY ("Microsoft.Win32.Primitives"), + FACADE_ASSEMBLY ("Microsoft.Win32.Registry"), + FACADE_ASSEMBLY ("Microsoft.Win32.Registry.AccessControl"), + {"Mono.Cairo", 0}, + {"Mono.CompilerServices.SymbolWriter", 0}, + {"Mono.Data", 0}, + {"Mono.Data.SybaseClient", 0}, + {"Mono.Data.Tds", 0}, + {"Mono.Data.TdsClient", 0}, + {"Mono.GetOptions", 0}, + {"Mono.Http", 0}, + {"Mono.Posix", 0}, + {"Mono.Security", 0}, + {"Mono.Security.Win32", 0}, + {"Mono.Xml.Ext", 0}, + {"Novell.Directory.Ldap", 0}, + {"PEAPI", 0}, + {"System", 0}, + FACADE_ASSEMBLY ("System.AppContext"), + FACADE_ASSEMBLY ("System.Collections"), + FACADE_ASSEMBLY ("System.Collections.Concurrent"), + FACADE_ASSEMBLY ("System.Collections.NonGeneric"), + FACADE_ASSEMBLY ("System.Collections.Specialized"), + FACADE_ASSEMBLY ("System.ComponentModel"), + FACADE_ASSEMBLY ("System.ComponentModel.Annotations"), + {"System.ComponentModel.Composition", 2}, + {"System.ComponentModel.DataAnnotations", 2}, + FACADE_ASSEMBLY ("System.ComponentModel.EventBasedAsync"), + FACADE_ASSEMBLY ("System.ComponentModel.Primitives"), + FACADE_ASSEMBLY ("System.ComponentModel.TypeConverter"), + {"System.Configuration", 0}, + {"System.Configuration.Install", 0}, + FACADE_ASSEMBLY ("System.Console"), + {"System.Core", 2}, + {"System.Data", 0}, + FACADE_ASSEMBLY ("System.Data.Common"), + {"System.Data.Linq", 2}, + {"System.Data.OracleClient", 0}, + {"System.Data.Services", 2}, + {"System.Data.Services.Client", 2}, + FACADE_ASSEMBLY ("System.Data.SqlClient"), + {"System.Data.SqlXml", 0}, + {"System.Design", 0}, + FACADE_ASSEMBLY ("System.Diagnostics.Contracts"), + FACADE_ASSEMBLY ("System.Diagnostics.Debug"), + FACADE_ASSEMBLY ("System.Diagnostics.FileVersionInfo"), + FACADE_ASSEMBLY ("System.Diagnostics.Process"), + FACADE_ASSEMBLY ("System.Diagnostics.StackTrace"), + FACADE_ASSEMBLY ("System.Diagnostics.TextWriterTraceListener"), + FACADE_ASSEMBLY ("System.Diagnostics.Tools"), + FACADE_ASSEMBLY ("System.Diagnostics.TraceEvent"), + FACADE_ASSEMBLY ("System.Diagnostics.TraceSource"), + FACADE_ASSEMBLY ("System.Diagnostics.Tracing"), + {"System.DirectoryServices", 0}, + {"System.Drawing", 0}, + {"System.Drawing.Design", 0}, + FACADE_ASSEMBLY ("System.Drawing.Primitives"), + FACADE_ASSEMBLY ("System.Dynamic.Runtime"), + {"System.EnterpriseServices", 0}, + FACADE_ASSEMBLY ("System.Globalization"), + FACADE_ASSEMBLY ("System.Globalization.Calendars"), + FACADE_ASSEMBLY ("System.Globalization.Extensions"), + {"System.IdentityModel", 3}, + {"System.IdentityModel.Selectors", 3}, + FACADE_ASSEMBLY ("System.IO"), + {"System.IO.Compression", 2}, + FACADE_ASSEMBLY ("System.IO.Compression.ZipFile"), + FACADE_ASSEMBLY ("System.IO.FileSystem"), + FACADE_ASSEMBLY ("System.IO.FileSystem.AccessControl"), + FACADE_ASSEMBLY ("System.IO.FileSystem.DriveInfo"), + FACADE_ASSEMBLY ("System.IO.FileSystem.Primitives"), + FACADE_ASSEMBLY ("System.IO.FileSystem.Watcher"), + FACADE_ASSEMBLY ("System.IO.IsolatedStorage"), + FACADE_ASSEMBLY ("System.IO.MemoryMappedFiles"), + FACADE_ASSEMBLY ("System.IO.Packaging"), + FACADE_ASSEMBLY ("System.IO.Pipes"), + FACADE_ASSEMBLY ("System.IO.UnmanagedMemoryStream"), + FACADE_ASSEMBLY ("System.Linq"), + FACADE_ASSEMBLY ("System.Linq.Expressions"), + FACADE_ASSEMBLY ("System.Linq.Parallel"), + FACADE_ASSEMBLY ("System.Linq.Queryable"), + {"System.Management", 0}, + {"System.Messaging", 0}, + {"System.Net", 2}, + FACADE_ASSEMBLY ("System.Net.AuthenticationManager"), + FACADE_ASSEMBLY ("System.Net.Cache"), + {"System.Net.Http", 4}, + {"System.Net.Http.Rtc", 0}, + FACADE_ASSEMBLY ("System.Net.HttpListener"), + FACADE_ASSEMBLY ("System.Net.Mail"), + FACADE_ASSEMBLY ("System.Net.NameResolution"), + FACADE_ASSEMBLY ("System.Net.NetworkInformation"), + FACADE_ASSEMBLY ("System.Net.Ping"), + FACADE_ASSEMBLY ("System.Net.Primitives"), + FACADE_ASSEMBLY ("System.Net.Requests"), + FACADE_ASSEMBLY ("System.Net.Security"), + FACADE_ASSEMBLY ("System.Net.ServicePoint"), + FACADE_ASSEMBLY ("System.Net.Sockets"), + FACADE_ASSEMBLY ("System.Net.Utilities"), + FACADE_ASSEMBLY ("System.Net.WebHeaderCollection"), + FACADE_ASSEMBLY ("System.Net.WebSockets"), + FACADE_ASSEMBLY ("System.Net.WebSockets.Client"), + {"System.Numerics.Vectors", 3}, + FACADE_ASSEMBLY ("System.ObjectModel"), + FACADE_ASSEMBLY ("System.Reflection"), + FACADE_ASSEMBLY ("System.Reflection.DispatchProxy"), + FACADE_ASSEMBLY ("System.Reflection.Emit"), + FACADE_ASSEMBLY ("System.Reflection.Emit.ILGeneration"), + FACADE_ASSEMBLY ("System.Reflection.Emit.Lightweight"), + FACADE_ASSEMBLY ("System.Reflection.Extensions"), + FACADE_ASSEMBLY ("System.Reflection.Primitives"), + FACADE_ASSEMBLY ("System.Reflection.TypeExtensions"), + FACADE_ASSEMBLY ("System.Resources.ReaderWriter"), + FACADE_ASSEMBLY ("System.Resources.ResourceManager"), + FACADE_ASSEMBLY ("System.Runtime"), + FACADE_ASSEMBLY ("System.Runtime.CompilerServices.VisualC"), + FACADE_ASSEMBLY ("System.Runtime.Extensions"), + FACADE_ASSEMBLY ("System.Runtime.Handles"), + FACADE_ASSEMBLY ("System.Runtime.InteropServices"), + FACADE_ASSEMBLY ("System.Runtime.InteropServices.RuntimeInformation"), + FACADE_ASSEMBLY ("System.Runtime.InteropServices.WindowsRuntime"), + FACADE_ASSEMBLY ("System.Runtime.Loader"), + FACADE_ASSEMBLY ("System.Runtime.Numerics"), + {"System.Runtime.Remoting", 0}, + {"System.Runtime.Serialization", 3}, + FACADE_ASSEMBLY ("System.Runtime.Serialization.Formatters"), + {"System.Runtime.Serialization.Formatters.Soap", 0}, + FACADE_ASSEMBLY ("System.Runtime.Serialization.Json"), + FACADE_ASSEMBLY ("System.Runtime.Serialization.Primitives"), + FACADE_ASSEMBLY ("System.Runtime.Serialization.Xml"), + {"System.Security", 0}, + FACADE_ASSEMBLY ("System.Security.AccessControl"), + FACADE_ASSEMBLY ("System.Security.Claims"), + FACADE_ASSEMBLY ("System.Security.Cryptography.Algorithms"), + FACADE_ASSEMBLY ("System.Security.Cryptography.Cng"), + FACADE_ASSEMBLY ("System.Security.Cryptography.Csp"), + FACADE_ASSEMBLY ("System.Security.Cryptography.DeriveBytes"), + FACADE_ASSEMBLY ("System.Security.Cryptography.Encoding"), + FACADE_ASSEMBLY ("System.Security.Cryptography.Encryption"), + FACADE_ASSEMBLY ("System.Security.Cryptography.Encryption.Aes"), + FACADE_ASSEMBLY ("System.Security.Cryptography.Encryption.ECDiffieHellman"), + FACADE_ASSEMBLY ("System.Security.Cryptography.Encryption.ECDsa"), + FACADE_ASSEMBLY ("System.Security.Cryptography.Encryption.Hashing"), + FACADE_ASSEMBLY ("System.Security.Cryptography.Encryption.Hashing.Algorithms"), + FACADE_ASSEMBLY ("System.Security.Cryptography.OpenSsl"), + FACADE_ASSEMBLY ("System.Security.Cryptography.Pkcs"), + FACADE_ASSEMBLY ("System.Security.Cryptography.Primitives"), + FACADE_ASSEMBLY ("System.Security.Cryptography.ProtectedData"), + FACADE_ASSEMBLY ("System.Security.Cryptography.RSA"), + FACADE_ASSEMBLY ("System.Security.Cryptography.RandomNumberGenerator"), + FACADE_ASSEMBLY ("System.Security.Cryptography.X509Certificates"), + FACADE_ASSEMBLY ("System.Security.Principal"), + FACADE_ASSEMBLY ("System.Security.Principal.Windows"), + FACADE_ASSEMBLY ("System.Security.SecureString"), + {"System.ServiceModel", 3}, + FACADE_ASSEMBLY ("System.ServiceModel.Duplex"), + FACADE_ASSEMBLY ("System.ServiceModel.Http"), + FACADE_ASSEMBLY ("System.ServiceModel.NetTcp"), + FACADE_ASSEMBLY ("System.ServiceModel.Primitives"), + FACADE_ASSEMBLY ("System.ServiceModel.Security"), + {"System.ServiceModel.Web", 2}, + {"System.ServiceProcess", 0}, + FACADE_ASSEMBLY ("System.ServiceProcess.ServiceController"), + FACADE_ASSEMBLY ("System.Text.Encoding"), + FACADE_ASSEMBLY ("System.Text.Encoding.CodePages"), + FACADE_ASSEMBLY ("System.Text.Encoding.Extensions"), + FACADE_ASSEMBLY ("System.Text.RegularExpressions"), + FACADE_ASSEMBLY ("System.Threading"), + FACADE_ASSEMBLY ("System.Threading.AccessControl"), + FACADE_ASSEMBLY ("System.Threading.Overlapped"), + FACADE_ASSEMBLY ("System.Threading.Tasks"), + FACADE_ASSEMBLY ("System.Threading.Tasks.Parallel"), + FACADE_ASSEMBLY ("System.Threading.Thread"), + FACADE_ASSEMBLY ("System.Threading.ThreadPool"), + FACADE_ASSEMBLY ("System.Threading.Timer"), + {"System.Transactions", 0}, + FACADE_ASSEMBLY ("System.ValueTuple"), + {"System.Web", 0}, + {"System.Web.Abstractions", 2}, + {"System.Web.DynamicData", 2}, + {"System.Web.Extensions", 2}, + {"System.Web.Mobile", 0}, + {"System.Web.Routing", 2}, + {"System.Web.Services", 0}, + {"System.Windows", 0}, + {"System.Windows.Forms", 0}, + {"System.Xml", 0}, + {"System.Xml.Linq", 2}, + FACADE_ASSEMBLY ("System.Xml.ReaderWriter"), + {"System.Xml.Serialization", 0}, + FACADE_ASSEMBLY ("System.Xml.XDocument"), + FACADE_ASSEMBLY ("System.Xml.XPath"), + FACADE_ASSEMBLY ("System.Xml.XPath.XmlDocument"), + FACADE_ASSEMBLY ("System.Xml.XPath.XDocument"), + FACADE_ASSEMBLY ("System.Xml.XmlDocument"), + FACADE_ASSEMBLY ("System.Xml.XmlSerializer"), + FACADE_ASSEMBLY ("System.Xml.Xsl.Primitives"), + {"WindowsBase", 3}, + {"mscorlib", 0}, + FACADE_ASSEMBLY ("netstandard"), +}; +#endif + +/* + * keeps track of loaded assemblies + */ +static GList *loaded_assemblies = NULL; +static MonoAssembly *corlib; + +static char* unquote (const char *str); + +/* This protects loaded_assemblies and image->references */ +#define mono_assemblies_lock() mono_os_mutex_lock (&assemblies_mutex) +#define mono_assemblies_unlock() mono_os_mutex_unlock (&assemblies_mutex) +static mono_mutex_t assemblies_mutex; + +/* If defined, points to the bundled assembly information */ +const MonoBundledAssembly **bundles; + +static mono_mutex_t assembly_binding_mutex; + +/* Loaded assembly binding info */ +static GSList *loaded_assembly_bindings = NULL; + +/* Class lazy loading functions */ +static GENERATE_TRY_GET_CLASS_WITH_CACHE (internals_visible, "System.Runtime.CompilerServices", "InternalsVisibleToAttribute") +static MonoAssembly* +mono_assembly_invoke_search_hook_internal (MonoAssemblyName *aname, MonoAssembly *requesting, gboolean refonly, gboolean postload); +static MonoAssembly* +mono_assembly_load_full_internal (MonoAssemblyName *aname, MonoAssembly *requesting, const char *basedir, MonoImageOpenStatus *status, gboolean refonly); +static MonoBoolean +mono_assembly_is_in_gac (const gchar *filanem); + +static MonoAssembly* +prevent_reference_assembly_from_running (MonoAssembly* candidate, gboolean refonly); + +/* Assembly name matching */ +static gboolean +exact_sn_match (MonoAssemblyName *wanted_name, MonoAssemblyName *candidate_name); +static gboolean +framework_assembly_sn_match (MonoAssemblyName *wanted_name, MonoAssemblyName *candidate_name); + +static gchar* +encode_public_tok (const guchar *token, gint32 len) +{ + const static gchar allowed [] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + gchar *res; + int i; + + res = (gchar *)g_malloc (len * 2 + 1); + for (i = 0; i < len; i++) { + res [i * 2] = allowed [token [i] >> 4]; + res [i * 2 + 1] = allowed [token [i] & 0xF]; + } + res [len * 2] = 0; + return res; +} + +/** + * mono_public_tokens_are_equal: + * \param pubt1 first public key token + * \param pubt2 second public key token + * + * Compare two public key tokens and return TRUE is they are equal and FALSE + * otherwise. + */ +gboolean +mono_public_tokens_are_equal (const unsigned char *pubt1, const unsigned char *pubt2) +{ + return memcmp (pubt1, pubt2, 16) == 0; +} + +/** + * mono_set_assemblies_path: + * \param 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 \c MONO_PATH variable. + * + * Notice that \c 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 (const char* path) +{ + char **splitted, **dest; + + splitted = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, 1000); + if (assemblies_path) + g_strfreev (assemblies_path); + assemblies_path = dest = splitted; + while (*splitted) { + char *tmp = *splitted; + if (*tmp) + *dest++ = mono_path_canonicalize (tmp); + g_free (tmp); + splitted++; + } + *dest = *splitted; + + if (g_hasenv ("MONO_DEBUG")) + return; + + splitted = assemblies_path; + while (*splitted) { + if (**splitted && !g_file_test (*splitted, G_FILE_TEST_IS_DIR)) + g_warning ("'%s' in MONO_PATH doesn't exist or has wrong permissions.", *splitted); + + splitted++; + } +} + +/** +* 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) +{ + if (assemblies_path != NULL) + return; + + char* path = g_getenv ("MONO_PATH"); + if (!path) + return; + + mono_set_assemblies_path(path); + g_free (path); +} + +static void +check_extra_gac_path_env (void) +{ + char *path; + char **splitted, **dest; + + path = g_getenv ("MONO_GAC_PREFIX"); + if (!path) + return; + + splitted = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, 1000); + g_free (path); + + if (extra_gac_paths) + g_strfreev (extra_gac_paths); + extra_gac_paths = dest = splitted; + while (*splitted){ + if (**splitted) + *dest++ = *splitted; + splitted++; + } + *dest = *splitted; + + if (!g_hasenv ("MONO_DEBUG")) + return; + + while (*splitted) { + if (**splitted && !g_file_test (*splitted, G_FILE_TEST_IS_DIR)) + g_warning ("'%s' in MONO_GAC_PREFIX doesn't exist or has wrong permissions.", *splitted); + + splitted++; + } +} + +static gboolean +assembly_binding_maps_name (MonoAssemblyBindingInfo *info, MonoAssemblyName *aname) +{ + if (!info || !info->name) + return FALSE; + + if (strcmp (info->name, aname->name)) + return FALSE; + + if (info->major != aname->major || info->minor != aname->minor) + return FALSE; + + if ((info->culture != NULL && info->culture [0]) != (aname->culture != NULL && aname->culture [0])) + return FALSE; + + if (info->culture && aname->culture && strcmp (info->culture, aname->culture)) + return FALSE; + + if (!mono_public_tokens_are_equal (info->public_key_token, aname->public_key_token)) + return FALSE; + + return TRUE; +} + +static void +mono_assembly_binding_info_free (MonoAssemblyBindingInfo *info) +{ + if (!info) + return; + + g_free (info->name); + g_free (info->culture); +} + +static void +get_publisher_policy_info (MonoImage *image, MonoAssemblyName *aname, MonoAssemblyBindingInfo *binding_info) +{ + MonoTableInfo *t; + guint32 cols [MONO_MANIFEST_SIZE]; + const gchar *filename; + gchar *subpath, *fullpath; + + t = &image->tables [MONO_TABLE_MANIFESTRESOURCE]; + /* MS Impl. accepts policy assemblies with more than + * one manifest resource, and only takes the first one */ + if (t->rows < 1) { + binding_info->is_valid = FALSE; + return; + } + + mono_metadata_decode_row (t, 0, cols, MONO_MANIFEST_SIZE); + if ((cols [MONO_MANIFEST_IMPLEMENTATION] & MONO_IMPLEMENTATION_MASK) != MONO_IMPLEMENTATION_FILE) { + binding_info->is_valid = FALSE; + return; + } + + filename = mono_metadata_string_heap (image, cols [MONO_MANIFEST_NAME]); + g_assert (filename != NULL); + + subpath = g_path_get_dirname (image->name); + fullpath = g_build_path (G_DIR_SEPARATOR_S, subpath, filename, NULL); + mono_config_parse_publisher_policy (fullpath, binding_info); + g_free (subpath); + g_free (fullpath); + + /* Define the optional elements/attributes before checking */ + if (!binding_info->culture) + binding_info->culture = g_strdup (""); + + /* Check that the most important elements/attributes exist */ + if (!binding_info->name || !binding_info->public_key_token [0] || !binding_info->has_old_version_bottom || + !binding_info->has_new_version || !assembly_binding_maps_name (binding_info, aname)) { + mono_assembly_binding_info_free (binding_info); + binding_info->is_valid = FALSE; + return; + } + + binding_info->is_valid = TRUE; +} + +static int +compare_versions (AssemblyVersionSet *v, MonoAssemblyName *aname) +{ + if (v->major > aname->major) + return 1; + else if (v->major < aname->major) + return -1; + + if (v->minor > aname->minor) + return 1; + else if (v->minor < aname->minor) + return -1; + + if (v->build > aname->build) + return 1; + else if (v->build < aname->build) + return -1; + + if (v->revision > aname->revision) + return 1; + else if (v->revision < aname->revision) + return -1; + + return 0; +} + +static gboolean +check_policy_versions (MonoAssemblyBindingInfo *info, MonoAssemblyName *name) +{ + if (!info->is_valid) + return FALSE; + + /* If has_old_version_top doesn't exist, we don't have an interval */ + if (!info->has_old_version_top) { + if (compare_versions (&info->old_version_bottom, name) == 0) + return TRUE; + + return FALSE; + } + + /* Check that the version defined by name is valid for the interval */ + if (compare_versions (&info->old_version_top, name) < 0) + return FALSE; + + /* We should be greater or equal than the small version */ + if (compare_versions (&info->old_version_bottom, name) > 0) + return FALSE; + + return TRUE; +} + +/** + * mono_assembly_names_equal: + * \param l first assembly + * \param r second assembly. + * + * Compares two \c MonoAssemblyName instances and returns whether they are equal. + * + * This compares the names, the cultures, the release version and their + * public tokens. + * + * \returns TRUE if both assembly names are equal. + */ +gboolean +mono_assembly_names_equal (MonoAssemblyName *l, MonoAssemblyName *r) +{ + 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 +mono_assembly_names_equal_flags (MonoAssemblyName *l, MonoAssemblyName *r, MonoAssemblyNameEqFlags flags) +{ + if (!l->name || !r->name) + return FALSE; + + 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)) + return FALSE; + + if ((l->major != r->major || l->minor != r->minor || + l->build != r->build || l->revision != r->revision) && + (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 & MONO_ANAME_EQ_IGNORE_PUBKEY) != 0) + return TRUE; + + if (!mono_public_tokens_are_equal (l->public_key_token, r->public_key_token)) + return FALSE; + + return TRUE; +} + +static MonoAssembly * +load_in_path (const char *basename, const char** search_path, MonoImageOpenStatus *status, MonoBoolean refonly, MonoAssemblyCandidatePredicate predicate, gpointer user_data) +{ + int i; + char *fullpath; + MonoAssembly *result; + + for (i = 0; search_path [i]; ++i) { + fullpath = g_build_filename (search_path [i], basename, NULL); + result = mono_assembly_open_predicate (fullpath, refonly, FALSE, predicate, user_data, status); + g_free (fullpath); + if (result) + return result; + } + return NULL; +} + +/** + * mono_assembly_setrootdir: + * \param root_dir The pathname of the root directory where we will locate assemblies + * + * This routine sets the internal default root directory for looking up + * assemblies. + * + * This is used by Windows installations to compute dynamically the + * place where the Mono assemblies are located. + * + */ +void +mono_assembly_setrootdir (const char *root_dir) +{ + /* + * Override the MONO_ASSEMBLIES directory configured at compile time. + */ + /* Leak if called more than once */ + default_path [0] = g_strdup (root_dir); +} + +/** + * mono_assembly_getrootdir: + * + * Obtains the root directory used for looking up assemblies. + * + * Returns: a string with the directory, this string should not be freed. + */ +G_CONST_RETURN gchar * +mono_assembly_getrootdir (void) +{ + return default_path [0]; +} + +/** + * mono_native_getrootdir: + * + * Obtains the root directory used for looking up native libs (.so, .dylib). + * + * Returns: a string with the directory, this string should be freed by + * the caller. + */ +gchar * +mono_native_getrootdir (void) +{ + gchar* fullpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (), mono_config_get_reloc_lib_dir(), NULL); + return fullpath; +} + +/** + * mono_set_dirs: + * \param assembly_dir the base directory for assemblies + * \param config_dir the base directory for configuration files + * + * This routine is used internally and by developers embedding + * the runtime into their own applications. + * + * There are a number of cases to consider: Mono as a system-installed + * package that is available on the location preconfigured or Mono in + * a relocated location. + * + * If you are using a system-installed Mono, you can pass NULL + * to both parameters. If you are not, you should compute both + * directory values and call this routine. + * + * The values for a given PREFIX are: + * + * assembly_dir: PREFIX/lib + * config_dir: PREFIX/etc + * + * Notice that embedders that use Mono in a relocated way must + * compute the location at runtime, as they will be in control + * of where Mono is installed. + */ +void +mono_set_dirs (const char *assembly_dir, const char *config_dir) +{ + if (assembly_dir == NULL) + assembly_dir = mono_config_get_assemblies_dir (); + if (config_dir == NULL) + config_dir = mono_config_get_cfg_dir (); + mono_assembly_setrootdir (assembly_dir); + mono_set_config_dir (config_dir); +} + +#ifndef HOST_WIN32 + +static char * +compute_base (char *path) +{ + char *p = strrchr (path, '/'); + if (p == NULL) + 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-bdwgc") && strcmp (p, "/mono-sgen") && strcmp (p, "/pedump") && strcmp (p, "/monodis")) + return NULL; + + *p = 0; + p = strrchr (path, '/'); + if (p == NULL) + return NULL; + + if (strstr (p, "/bin") == 0) + return NULL; + *p = 0; + return path; +} + +static void +fallback (void) +{ + mono_set_dirs (mono_config_get_assemblies_dir (), mono_config_get_cfg_dir ()); +} + +static G_GNUC_UNUSED void +set_dirs (char *exe) +{ + char *base; + char *config, *lib, *mono; + struct stat buf; + const char *bindir; + + /* + * Only /usr prefix is treated specially + */ + bindir = mono_config_get_bin_dir (); + g_assert (bindir); + if (strncmp (exe, bindir, strlen (bindir)) == 0 || (base = compute_base (exe)) == NULL){ + fallback (); + return; + } + + config = g_build_filename (base, "etc", NULL); + lib = g_build_filename (base, "lib", NULL); + mono = g_build_filename (lib, "mono/4.5", NULL); // FIXME: stop hardcoding 4.5 here + if (stat (mono, &buf) == -1) + fallback (); + else { + mono_set_dirs (lib, config); + } + + g_free (config); + g_free (lib); + g_free (mono); +} + +#endif /* HOST_WIN32 */ + +/** + * mono_set_rootdir: + * + * Registers the root directory for the Mono runtime, for Linux and Solaris 10, + * this auto-detects the prefix where Mono was installed. + */ +void +mono_set_rootdir (void) +{ +#if defined(HOST_WIN32) || (defined(HOST_DARWIN) && !defined(TARGET_ARM)) + gchar *bindir, *installdir, *root, *name, *resolvedname, *config; + +#ifdef HOST_WIN32 + name = mono_get_module_file_name ((HMODULE) &__ImageBase); +#else + { + /* + * _NSGetExecutablePath may return -1 to indicate buf is not large + * enough, but we ignore that case to avoid having to do extra dynamic + * allocation for the path and hope that 4096 is enough - this is + * ok in the Linux/Solaris case below at least... + */ + + gchar buf[4096]; + guint buf_size = sizeof (buf); + + name = NULL; + if (_NSGetExecutablePath (buf, &buf_size) == 0) + name = g_strdup (buf); + + if (name == NULL) { + fallback (); + return; + } + } +#endif + + resolvedname = mono_path_resolve_symlinks (name); + + bindir = g_path_get_dirname (resolvedname); + installdir = g_path_get_dirname (bindir); + root = g_build_path (G_DIR_SEPARATOR_S, installdir, "lib", NULL); + + config = g_build_filename (root, "..", "etc", NULL); +#ifdef HOST_WIN32 + mono_set_dirs (root, config); +#else + if (g_file_test (root, G_FILE_TEST_EXISTS) && g_file_test (config, G_FILE_TEST_EXISTS)) + mono_set_dirs (root, config); + else + fallback (); +#endif + + g_free (config); + g_free (root); + g_free (installdir); + g_free (bindir); + g_free (name); + g_free (resolvedname); +#elif defined(DISABLE_MONO_AUTODETECTION) + fallback (); +#else + char buf [4096]; + int s; + char *str; + + /* Linux style */ + s = readlink ("/proc/self/exe", buf, sizeof (buf)-1); + + if (s != -1){ + buf [s] = 0; + set_dirs (buf); + return; + } + + /* Solaris 10 style */ + str = g_strdup_printf ("/proc/%d/path/a.out", getpid ()); + s = readlink (str, buf, sizeof (buf)-1); + g_free (str); + if (s != -1){ + buf [s] = 0; + set_dirs (buf); + return; + } + fallback (); +#endif +} + +/** + * mono_assemblies_init: + * + * Initialize global variables used by this module. + */ +void +mono_assemblies_init (void) +{ + /* + * Initialize our internal paths if we have not been initialized yet. + * This happens when embedders use Mono. + */ + if (mono_assembly_getrootdir () == NULL) + mono_set_rootdir (); + + check_path_env (); + check_extra_gac_path_env (); + + mono_os_mutex_init_recursive (&assemblies_mutex); + mono_os_mutex_init (&assembly_binding_mutex); + +#ifndef DISABLE_DESKTOP_LOADER + assembly_remapping_table = g_hash_table_new (g_str_hash, g_str_equal); + + int i; + for (i = 0; i < G_N_ELEMENTS (framework_assemblies) - 1; ++i) + g_hash_table_insert (assembly_remapping_table, (void*)framework_assemblies [i].assembly_name, (void*)&framework_assemblies [i]); + +#endif +} + +static void +mono_assembly_binding_lock (void) +{ + mono_locks_os_acquire (&assembly_binding_mutex, AssemblyBindingLock); +} + +static void +mono_assembly_binding_unlock (void) +{ + mono_locks_os_release (&assembly_binding_mutex, AssemblyBindingLock); +} + +gboolean +mono_assembly_fill_assembly_name_full (MonoImage *image, MonoAssemblyName *aname, gboolean copyBlobs) +{ + MonoTableInfo *t = &image->tables [MONO_TABLE_ASSEMBLY]; + guint32 cols [MONO_ASSEMBLY_SIZE]; + gint32 machine, flags; + + if (!t->rows) + return FALSE; + + mono_metadata_decode_row (t, 0, cols, MONO_ASSEMBLY_SIZE); + + aname->hash_len = 0; + aname->hash_value = NULL; + aname->name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_NAME]); + if (copyBlobs) + aname->name = g_strdup (aname->name); + aname->culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_CULTURE]); + if (copyBlobs) + aname->culture = g_strdup (aname->culture); + aname->flags = cols [MONO_ASSEMBLY_FLAGS]; + aname->major = cols [MONO_ASSEMBLY_MAJOR_VERSION]; + aname->minor = cols [MONO_ASSEMBLY_MINOR_VERSION]; + aname->build = cols [MONO_ASSEMBLY_BUILD_NUMBER]; + aname->revision = cols [MONO_ASSEMBLY_REV_NUMBER]; + aname->hash_alg = cols [MONO_ASSEMBLY_HASH_ALG]; + if (cols [MONO_ASSEMBLY_PUBLIC_KEY]) { + guchar* token = (guchar *)g_malloc (8); + gchar* encoded; + const gchar* pkey; + int len; + + pkey = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLY_PUBLIC_KEY]); + len = mono_metadata_decode_blob_size (pkey, &pkey); + aname->public_key = (guchar*)pkey; + + mono_digest_get_public_token (token, aname->public_key, len); + encoded = encode_public_tok (token, 8); + g_strlcpy ((char*)aname->public_key_token, encoded, MONO_PUBLIC_KEY_TOKEN_LENGTH); + + g_free (encoded); + g_free (token); + } + else { + aname->public_key = NULL; + memset (aname->public_key_token, 0, MONO_PUBLIC_KEY_TOKEN_LENGTH); + } + + if (cols [MONO_ASSEMBLY_PUBLIC_KEY]) { + aname->public_key = (guchar*)mono_metadata_blob_heap (image, cols [MONO_ASSEMBLY_PUBLIC_KEY]); + if (copyBlobs) { + const gchar *pkey_end; + int len = mono_metadata_decode_blob_size ((const gchar*) aname->public_key, &pkey_end); + pkey_end += len; /* move to end */ + size_t size = pkey_end - (const gchar*)aname->public_key; + guchar *tmp = g_new (guchar, size); + memcpy (tmp, aname->public_key, size); + aname->public_key = tmp; + } + + } + else + aname->public_key = 0; + + machine = ((MonoCLIImageInfo*)(image->image_info))->cli_header.coff.coff_machine; + flags = ((MonoCLIImageInfo*)(image->image_info))->cli_cli_header.ch_flags; + switch (machine) { + case COFF_MACHINE_I386: + /* https://bugzilla.xamarin.com/show_bug.cgi?id=17632 */ + if (flags & (CLI_FLAGS_32BITREQUIRED|CLI_FLAGS_PREFERRED32BIT)) + aname->arch = MONO_PROCESSOR_ARCHITECTURE_X86; + else if ((flags & 0x70) == 0x70) + aname->arch = MONO_PROCESSOR_ARCHITECTURE_NONE; + else + aname->arch = MONO_PROCESSOR_ARCHITECTURE_MSIL; + break; + case COFF_MACHINE_IA64: + aname->arch = MONO_PROCESSOR_ARCHITECTURE_IA64; + break; + case COFF_MACHINE_AMD64: + aname->arch = MONO_PROCESSOR_ARCHITECTURE_AMD64; + break; + case COFF_MACHINE_ARM: + aname->arch = MONO_PROCESSOR_ARCHITECTURE_ARM; + break; + default: + break; + } + + return TRUE; +} + +/** + * mono_assembly_fill_assembly_name: + * \param image Image + * \param aname Name + * \returns TRUE if successful + */ +gboolean +mono_assembly_fill_assembly_name (MonoImage *image, MonoAssemblyName *aname) +{ + return mono_assembly_fill_assembly_name_full (image, aname, FALSE); +} + +/** + * mono_stringify_assembly_name: + * \param aname the assembly name. + * + * Convert \p aname into its string format. The returned string is dynamically + * allocated and should be freed by the caller. + * + * \returns a newly allocated string with a string representation of + * the assembly name. + */ +char* +mono_stringify_assembly_name (MonoAssemblyName *aname) +{ + const char *quote = (aname->name && g_ascii_isspace (aname->name [0])) ? "\"" : ""; + + return g_strdup_printf ( + "%s%s%s, Version=%d.%d.%d.%d, Culture=%s, PublicKeyToken=%s%s", + quote, aname->name, quote, + aname->major, aname->minor, aname->build, aname->revision, + aname->culture && *aname->culture? aname->culture: "neutral", + aname->public_key_token [0] ? (char *)aname->public_key_token : "null", + (aname->flags & ASSEMBLYREF_RETARGETABLE_FLAG) ? ", Retargetable=Yes" : ""); +} + +static gchar* +assemblyref_public_tok (MonoImage *image, guint32 key_index, guint32 flags) +{ + const gchar *public_tok; + int len; + + public_tok = mono_metadata_blob_heap (image, key_index); + len = mono_metadata_decode_blob_size (public_tok, &public_tok); + + if (flags & ASSEMBLYREF_FULL_PUBLIC_KEY_FLAG) { + guchar token [8]; + mono_digest_get_public_token (token, (guchar*)public_tok, len); + return encode_public_tok (token, 8); + } + + return encode_public_tok ((guchar*)public_tok, len); +} + +/** + * mono_assembly_addref: + * \param assembly the assembly to reference + * + * This routine increments the reference count on a MonoAssembly. + * The reference count is reduced every time the method mono_assembly_close() is + * invoked. + */ +void +mono_assembly_addref (MonoAssembly *assembly) +{ + mono_atomic_inc_i32 (&assembly->ref_count); +} + +/* + * CAUTION: This table must be kept in sync with + * ivkm/reflect/Fusion.cs + */ + +#define SILVERLIGHT_KEY "7cec85d7bea7798e" +#define WINFX_KEY "31bf3856ad364e35" +#define ECMA_KEY "b77a5c561934e089" +#define MSFINAL_KEY "b03f5f7f11d50a3a" +#define COMPACTFRAMEWORK_KEY "969db8053d3322ac" + +typedef struct { + const char *name; + const char *from; + const char *to; +} KeyRemapEntry; + +static KeyRemapEntry key_remap_table[] = { + { "CustomMarshalers", COMPACTFRAMEWORK_KEY, MSFINAL_KEY }, + { "Microsoft.CSharp", WINFX_KEY, MSFINAL_KEY }, + { "Microsoft.VisualBasic", COMPACTFRAMEWORK_KEY, MSFINAL_KEY }, + { "System", SILVERLIGHT_KEY, ECMA_KEY }, + { "System", COMPACTFRAMEWORK_KEY, ECMA_KEY }, + { "System.ComponentModel.Composition", WINFX_KEY, ECMA_KEY }, + { "System.ComponentModel.DataAnnotations", "ddd0da4d3e678217", WINFX_KEY }, + { "System.Core", SILVERLIGHT_KEY, ECMA_KEY }, + { "System.Core", COMPACTFRAMEWORK_KEY, ECMA_KEY }, + { "System.Data", COMPACTFRAMEWORK_KEY, ECMA_KEY }, + { "System.Data.DataSetExtensions", COMPACTFRAMEWORK_KEY, ECMA_KEY }, + { "System.Drawing", COMPACTFRAMEWORK_KEY, MSFINAL_KEY }, + { "System.Messaging", COMPACTFRAMEWORK_KEY, MSFINAL_KEY }, + // FIXME: MS uses MSFINAL_KEY for .NET 4.5 + { "System.Net", SILVERLIGHT_KEY, MSFINAL_KEY }, + { "System.Numerics", WINFX_KEY, ECMA_KEY }, + { "System.Runtime.Serialization", SILVERLIGHT_KEY, ECMA_KEY }, + { "System.Runtime.Serialization", COMPACTFRAMEWORK_KEY, ECMA_KEY }, + { "System.ServiceModel", WINFX_KEY, ECMA_KEY }, + { "System.ServiceModel", COMPACTFRAMEWORK_KEY, ECMA_KEY }, + { "System.ServiceModel.Web", SILVERLIGHT_KEY, WINFX_KEY }, + { "System.Web.Services", COMPACTFRAMEWORK_KEY, MSFINAL_KEY }, + { "System.Windows", SILVERLIGHT_KEY, MSFINAL_KEY }, + { "System.Windows.Forms", COMPACTFRAMEWORK_KEY, ECMA_KEY }, + { "System.Xml", SILVERLIGHT_KEY, ECMA_KEY }, + { "System.Xml", COMPACTFRAMEWORK_KEY, ECMA_KEY }, + { "System.Xml.Linq", WINFX_KEY, ECMA_KEY }, + { "System.Xml.Linq", COMPACTFRAMEWORK_KEY, ECMA_KEY }, + { "System.Xml.Serialization", WINFX_KEY, ECMA_KEY } +}; + +static void +remap_keys (MonoAssemblyName *aname) +{ + int i; + for (i = 0; i < G_N_ELEMENTS (key_remap_table); i++) { + const KeyRemapEntry *entry = &key_remap_table [i]; + + if (strcmp (aname->name, entry->name) || + !mono_public_tokens_are_equal (aname->public_key_token, (const unsigned char*) entry->from)) + continue; + + memcpy (aname->public_key_token, entry->to, MONO_PUBLIC_KEY_TOKEN_LENGTH); + + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, + "Remapped public key token of retargetable assembly %s from %s to %s", + aname->name, entry->from, entry->to); + return; + } +} + +static MonoAssemblyName * +mono_assembly_remap_version (MonoAssemblyName *aname, MonoAssemblyName *dest_aname) +{ + const MonoRuntimeInfo *current_runtime; + + if (aname->name == NULL) return aname; + + current_runtime = mono_get_runtime_info (); + + if (aname->flags & ASSEMBLYREF_RETARGETABLE_FLAG) { + const AssemblyVersionSet* vset; + + /* Remap to current runtime */ + vset = ¤t_runtime->version_sets [0]; + + memcpy (dest_aname, aname, sizeof(MonoAssemblyName)); + dest_aname->major = vset->major; + dest_aname->minor = vset->minor; + dest_aname->build = vset->build; + dest_aname->revision = vset->revision; + dest_aname->flags &= ~ASSEMBLYREF_RETARGETABLE_FLAG; + + /* Remap assembly name */ + if (!strcmp (aname->name, "System.Net")) + dest_aname->name = g_strdup ("System"); + + remap_keys (dest_aname); + + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, + "The request to load the retargetable assembly %s v%d.%d.%d.%d was remapped to %s v%d.%d.%d.%d", + aname->name, + aname->major, aname->minor, aname->build, aname->revision, + dest_aname->name, + vset->major, vset->minor, vset->build, vset->revision + ); + + return dest_aname; + } + +#ifndef DISABLE_DESKTOP_LOADER + const AssemblyVersionMap *vmap = (AssemblyVersionMap *)g_hash_table_lookup (assembly_remapping_table, aname->name); + if (vmap) { + const AssemblyVersionSet* vset; + int index = vmap->version_set_index; + g_assert (index < G_N_ELEMENTS (current_runtime->version_sets)); + vset = ¤t_runtime->version_sets [index]; + + if (vmap->framework_facade_assembly) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, "Assembly %s is a framework Facade asseembly", + aname->name); + return aname; + } + + if (aname->major == vset->major && aname->minor == vset->minor && + aname->build == vset->build && aname->revision == vset->revision) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, "Found assembly remapping for %s and was for the same version %d.%d.%d.%d", + aname->name, + aname->major, aname->minor, aname->build, aname->revision); + return aname; + } + + if (vmap->only_lower_versions && compare_versions ((AssemblyVersionSet*)vset, aname) < 0) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, + "Found lower-versions-only assembly remaping to load %s %d.%d.%d.%d but mapping has %d.%d.%d.%d", + aname->name, + aname->major, aname->minor, aname->build, aname->revision, + vset->major, vset->minor, vset->build, vset->revision + ); + return aname; + } + + if ((aname->major | aname->minor | aname->build | aname->revision) != 0) + mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_ASSEMBLY, + "The request to load the assembly %s v%d.%d.%d.%d was remapped to v%d.%d.%d.%d", + aname->name, + aname->major, aname->minor, aname->build, aname->revision, + vset->major, vset->minor, vset->build, vset->revision + ); + + memcpy (dest_aname, aname, sizeof(MonoAssemblyName)); + dest_aname->major = vset->major; + dest_aname->minor = vset->minor; + dest_aname->build = vset->build; + dest_aname->revision = vset->revision; + if (vmap->new_assembly_name != NULL) { + dest_aname->name = vmap->new_assembly_name; + mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_ASSEMBLY, + "The assembly name %s was remapped to %s", + aname->name, + dest_aname->name); + } + return dest_aname; + } +#endif + + return aname; +} + +/** + * mono_assembly_get_assemblyref: + * \param image pointer to the \c MonoImage to extract the information from. + * \param index index to the assembly reference in the image. + * \param aname pointer to a \c MonoAssemblyName that will hold the returned value. + * + * Fills out the \p aname with the assembly name of the \p index assembly reference in \p image. + */ +void +mono_assembly_get_assemblyref (MonoImage *image, int index, MonoAssemblyName *aname) +{ + MonoTableInfo *t; + guint32 cols [MONO_ASSEMBLYREF_SIZE]; + const char *hash; + + t = &image->tables [MONO_TABLE_ASSEMBLYREF]; + + mono_metadata_decode_row (t, index, cols, MONO_ASSEMBLYREF_SIZE); + + hash = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLYREF_HASH_VALUE]); + aname->hash_len = mono_metadata_decode_blob_size (hash, &hash); + aname->hash_value = hash; + aname->name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_NAME]); + aname->culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_CULTURE]); + aname->flags = cols [MONO_ASSEMBLYREF_FLAGS]; + aname->major = cols [MONO_ASSEMBLYREF_MAJOR_VERSION]; + aname->minor = cols [MONO_ASSEMBLYREF_MINOR_VERSION]; + aname->build = cols [MONO_ASSEMBLYREF_BUILD_NUMBER]; + aname->revision = cols [MONO_ASSEMBLYREF_REV_NUMBER]; + + if (cols [MONO_ASSEMBLYREF_PUBLIC_KEY]) { + gchar *token = assemblyref_public_tok (image, cols [MONO_ASSEMBLYREF_PUBLIC_KEY], aname->flags); + g_strlcpy ((char*)aname->public_key_token, token, MONO_PUBLIC_KEY_TOKEN_LENGTH); + g_free (token); + } else { + memset (aname->public_key_token, 0, MONO_PUBLIC_KEY_TOKEN_LENGTH); + } +} + +/** + * mono_assembly_load_reference: + */ +void +mono_assembly_load_reference (MonoImage *image, int index) +{ + MonoAssembly *reference; + MonoAssemblyName aname; + MonoImageOpenStatus status; + + /* + * image->references is shared between threads, so we need to access + * it inside a critical section. + */ + mono_assemblies_lock (); + if (!image->references) { + MonoTableInfo *t = &image->tables [MONO_TABLE_ASSEMBLYREF]; + + image->references = g_new0 (MonoAssembly *, t->rows + 1); + image->nreferences = t->rows; + } + reference = image->references [index]; + mono_assemblies_unlock (); + if (reference) + return; + + mono_assembly_get_assemblyref (image, index, &aname); + + if (image->assembly && image->assembly->ref_only) { + /* We use the loaded corlib */ + if (!strcmp (aname.name, "mscorlib")) + reference = mono_assembly_load_full_internal (&aname, image->assembly, image->assembly->basedir, &status, FALSE); + else { + reference = mono_assembly_loaded_full (&aname, TRUE); + if (!reference) + /* Try a postload search hook */ + reference = mono_assembly_invoke_search_hook_internal (&aname, image->assembly, TRUE, TRUE); + } + + /* + * Here we must advice that the error was due to + * a non loaded reference using the ReflectionOnly api + */ + if (!reference) + reference = (MonoAssembly *)REFERENCE_MISSING; + } else { + /* we first try without setting the basedir: this can eventually result in a ResolveAssembly + * event which is the MS .net compatible behaviour (the assemblyresolve_event3.cs test has been fixed + * accordingly, it would fail on the MS runtime before). + * The second load attempt has the basedir set to keep compatibility with the old mono behavior, for + * example bug-349190.2.cs and who knows how much more code in the wild. + */ + reference = mono_assembly_load_full_internal (&aname, image->assembly, NULL, &status, FALSE); + if (!reference && image->assembly) + reference = mono_assembly_load_full_internal (&aname, image->assembly, image->assembly->basedir, &status, FALSE); + } + + if (reference == NULL){ + char *extra_msg; + + if (status == MONO_IMAGE_ERROR_ERRNO && errno == ENOENT) { + extra_msg = g_strdup_printf ("The assembly was not found in the Global Assembly Cache, a path listed in the MONO_PATH environment variable, or in the location of the executing assembly (%s).\n", image->assembly != NULL ? image->assembly->basedir : "" ); + } else if (status == MONO_IMAGE_ERROR_ERRNO) { + extra_msg = g_strdup_printf ("System error: %s\n", strerror (errno)); + } else if (status == MONO_IMAGE_MISSING_ASSEMBLYREF) { + extra_msg = g_strdup ("Cannot find an assembly referenced from this one.\n"); + } else if (status == MONO_IMAGE_IMAGE_INVALID) { + extra_msg = g_strdup ("The file exists but is not a valid assembly.\n"); + } else { + extra_msg = g_strdup (""); + } + + mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_ASSEMBLY, "The following assembly referenced from %s could not be loaded:\n" + " Assembly: %s (assemblyref_index=%d)\n" + " Version: %d.%d.%d.%d\n" + " Public Key: %s\n%s", + image->name, aname.name, index, + aname.major, aname.minor, aname.build, aname.revision, + strlen ((char*)aname.public_key_token) == 0 ? "(none)" : (char*)aname.public_key_token, extra_msg); + g_free (extra_msg); + + } + + mono_assemblies_lock (); + if (reference == NULL) { + /* Flag as not found */ + reference = (MonoAssembly *)REFERENCE_MISSING; + } + + if (!image->references [index]) { + if (reference != REFERENCE_MISSING){ + mono_assembly_addref (reference); + if (image->assembly) + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Assembly Ref addref %s[%p] -> %s[%p]: %d", + image->assembly->aname.name, image->assembly, reference->aname.name, reference, reference->ref_count); + } else { + if (image->assembly) + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Failed to load assembly %s[%p].", + image->assembly->aname.name, image->assembly); + } + + image->references [index] = reference; + } + mono_assemblies_unlock (); + + if (image->references [index] != reference) { + /* Somebody loaded it before us */ + mono_assembly_close (reference); + } +} + +/** + * mono_assembly_load_references: + * \param image + * \param status + * \deprecated There is no reason to use this method anymore, it does nothing + * + * This method is now a no-op, it does nothing other than setting the \p status to \c MONO_IMAGE_OK + */ +void +mono_assembly_load_references (MonoImage *image, MonoImageOpenStatus *status) +{ + /* This is a no-op now but it is part of the embedding API so we can't remove it */ + *status = MONO_IMAGE_OK; +} + +typedef struct AssemblyLoadHook AssemblyLoadHook; +struct AssemblyLoadHook { + AssemblyLoadHook *next; + MonoAssemblyLoadFunc func; + gpointer user_data; +}; + +AssemblyLoadHook *assembly_load_hook = NULL; + +/** + * mono_assembly_invoke_load_hook: + */ +void +mono_assembly_invoke_load_hook (MonoAssembly *ass) +{ + AssemblyLoadHook *hook; + + for (hook = assembly_load_hook; hook; hook = hook->next) { + hook->func (ass, hook->user_data); + } +} + +/** + * mono_install_assembly_load_hook: + */ +void +mono_install_assembly_load_hook (MonoAssemblyLoadFunc func, gpointer user_data) +{ + AssemblyLoadHook *hook; + + g_return_if_fail (func != NULL); + + hook = g_new0 (AssemblyLoadHook, 1); + hook->func = func; + hook->user_data = user_data; + hook->next = assembly_load_hook; + assembly_load_hook = hook; +} + +static void +free_assembly_load_hooks (void) +{ + AssemblyLoadHook *hook, *next; + + for (hook = assembly_load_hook; hook; hook = next) { + next = hook->next; + g_free (hook); + } +} + +typedef struct AssemblySearchHook AssemblySearchHook; +struct AssemblySearchHook { + AssemblySearchHook *next; + MonoAssemblySearchFunc func; + gboolean refonly; + gboolean postload; + gpointer user_data; +}; + +AssemblySearchHook *assembly_search_hook = NULL; + +static MonoAssembly* +mono_assembly_invoke_search_hook_internal (MonoAssemblyName *aname, MonoAssembly *requesting, gboolean refonly, gboolean postload) +{ + AssemblySearchHook *hook; + + for (hook = assembly_search_hook; hook; hook = hook->next) { + if ((hook->refonly == refonly) && (hook->postload == postload)) { + MonoAssembly *ass; + /** + * A little explanation is in order here. + * + * The default postload search hook needs to know the requesting assembly to report it to managed code. + * The embedding API exposes a search hook that doesn't take such argument. + * + * The original fix would call the default search hook before all the registered ones and pass + * the requesting assembly to it. It works but broke a very suddle embedding API aspect that some users + * rely on. Which is the ordering between user hooks and the default runtime hook. + * + * Registering the hook after mono_jit_init would let your hook run before the default one and + * when using it to handle non standard app layouts this could save your app from a massive amount + * of syscalls that the default hook does when probing all sorts of places. Slow targets with horrible IO + * are all using this trick and if we broke this assumption they would be very disapointed at us. + * + * So what's the fix? We register the default hook using regular means and special case it when iterating + * over the registered hooks. This preserves ordering and enables managed resolve hooks to get the requesting + * assembly. + */ + if (hook->func == (void*)mono_domain_assembly_postload_search) + ass = mono_domain_assembly_postload_search (aname, requesting, refonly); + else + ass = hook->func (aname, hook->user_data); + if (ass) + return ass; + } + } + + return NULL; +} + +/** + * mono_assembly_invoke_search_hook: + */ +MonoAssembly* +mono_assembly_invoke_search_hook (MonoAssemblyName *aname) +{ + return mono_assembly_invoke_search_hook_internal (aname, NULL, FALSE, FALSE); +} + +static void +mono_install_assembly_search_hook_internal (MonoAssemblySearchFunc func, gpointer user_data, gboolean refonly, gboolean postload) +{ + AssemblySearchHook *hook; + + g_return_if_fail (func != NULL); + + hook = g_new0 (AssemblySearchHook, 1); + hook->func = func; + hook->user_data = user_data; + hook->refonly = refonly; + hook->postload = postload; + hook->next = assembly_search_hook; + assembly_search_hook = hook; +} + +/** + * mono_install_assembly_search_hook: + */ +void +mono_install_assembly_search_hook (MonoAssemblySearchFunc func, gpointer user_data) +{ + mono_install_assembly_search_hook_internal (func, user_data, FALSE, FALSE); +} + +static void +free_assembly_search_hooks (void) +{ + AssemblySearchHook *hook, *next; + + for (hook = assembly_search_hook; hook; hook = next) { + next = hook->next; + g_free (hook); + } +} + +/** + * mono_install_assembly_refonly_search_hook: + */ +void +mono_install_assembly_refonly_search_hook (MonoAssemblySearchFunc func, gpointer user_data) +{ + mono_install_assembly_search_hook_internal (func, user_data, TRUE, FALSE); +} + +/** + * mono_install_assembly_postload_search_hook: + */ +void +mono_install_assembly_postload_search_hook (MonoAssemblySearchFunc func, gpointer user_data) +{ + mono_install_assembly_search_hook_internal (func, user_data, FALSE, TRUE); +} + +void +mono_install_assembly_postload_refonly_search_hook (MonoAssemblySearchFunc func, gpointer user_data) +{ + mono_install_assembly_search_hook_internal (func, user_data, TRUE, TRUE); +} + +typedef struct AssemblyPreLoadHook AssemblyPreLoadHook; +struct AssemblyPreLoadHook { + AssemblyPreLoadHook *next; + MonoAssemblyPreLoadFunc func; + gpointer user_data; +}; + +static AssemblyPreLoadHook *assembly_preload_hook = NULL; +static AssemblyPreLoadHook *assembly_refonly_preload_hook = NULL; + +static MonoAssembly * +invoke_assembly_preload_hook (MonoAssemblyName *aname, gchar **assemblies_path) +{ + AssemblyPreLoadHook *hook; + MonoAssembly *assembly; + + for (hook = assembly_preload_hook; hook; hook = hook->next) { + assembly = hook->func (aname, assemblies_path, hook->user_data); + if (assembly != NULL) + return assembly; + } + + return NULL; +} + +static MonoAssembly * +invoke_assembly_refonly_preload_hook (MonoAssemblyName *aname, gchar **assemblies_path) +{ + AssemblyPreLoadHook *hook; + MonoAssembly *assembly; + + for (hook = assembly_refonly_preload_hook; hook; hook = hook->next) { + assembly = hook->func (aname, assemblies_path, hook->user_data); + if (assembly != NULL) + return assembly; + } + + return NULL; +} + +/** + * mono_install_assembly_preload_hook: + */ +void +mono_install_assembly_preload_hook (MonoAssemblyPreLoadFunc func, gpointer user_data) +{ + AssemblyPreLoadHook *hook; + + g_return_if_fail (func != NULL); + + hook = g_new0 (AssemblyPreLoadHook, 1); + hook->func = func; + hook->user_data = user_data; + hook->next = assembly_preload_hook; + assembly_preload_hook = hook; +} + +/** + * mono_install_assembly_refonly_preload_hook: + */ +void +mono_install_assembly_refonly_preload_hook (MonoAssemblyPreLoadFunc func, gpointer user_data) +{ + AssemblyPreLoadHook *hook; + + g_return_if_fail (func != NULL); + + hook = g_new0 (AssemblyPreLoadHook, 1); + hook->func = func; + hook->user_data = user_data; + hook->next = assembly_refonly_preload_hook; + assembly_refonly_preload_hook = hook; +} + +static void +free_assembly_preload_hooks (void) +{ + AssemblyPreLoadHook *hook, *next; + + for (hook = assembly_preload_hook; hook; hook = next) { + next = hook->next; + g_free (hook); + } + + for (hook = assembly_refonly_preload_hook; hook; hook = next) { + next = hook->next; + g_free (hook); + } +} + +static gchar * +absolute_dir (const gchar *filename) +{ + gchar *cwd; + gchar *mixed; + gchar **parts; + gchar *part; + GList *list, *tmp; + GString *result; + gchar *res; + gint i; + + if (g_path_is_absolute (filename)) { + part = g_path_get_dirname (filename); + res = g_strconcat (part, G_DIR_SEPARATOR_S, NULL); + g_free (part); + return res; + } + + cwd = g_get_current_dir (); + mixed = g_build_filename (cwd, filename, NULL); + parts = g_strsplit (mixed, G_DIR_SEPARATOR_S, 0); + g_free (mixed); + g_free (cwd); + + list = NULL; + for (i = 0; (part = parts [i]) != NULL; i++) { + if (!strcmp (part, ".")) + continue; + + if (!strcmp (part, "..")) { + if (list && list->next) /* Don't remove root */ + list = g_list_delete_link (list, list); + } else { + list = g_list_prepend (list, part); + } + } + + result = g_string_new (""); + list = g_list_reverse (list); + + /* Ignores last data pointer, which should be the filename */ + for (tmp = list; tmp && tmp->next != NULL; tmp = tmp->next){ + if (tmp->data) + g_string_append_printf (result, "%s%c", (char *) tmp->data, + G_DIR_SEPARATOR); + } + + res = result->str; + g_string_free (result, FALSE); + g_list_free (list); + g_strfreev (parts); + if (*res == '\0') { + g_free (res); + return g_strdup ("."); + } + + return res; +} + +/** + * mono_assembly_open_from_bundle: + * \param filename Filename requested + * \param status return status code + * + * This routine tries to open the assembly specified by \p filename from the + * defined bundles, if found, returns the MonoImage for it, if not found + * returns NULL + */ +MonoImage * +mono_assembly_open_from_bundle (const char *filename, MonoImageOpenStatus *status, gboolean refonly) +{ + int i; + char *name; + gchar *lowercase_filename; + MonoImage *image = NULL; + gboolean is_satellite = FALSE; + /* + * we do a very simple search for bundled assemblies: it's not a general + * purpose assembly loading mechanism. + */ + + if (!bundles) + return NULL; + + lowercase_filename = g_utf8_strdown (filename, -1); + is_satellite = g_str_has_suffix (lowercase_filename, ".resources.dll"); + g_free (lowercase_filename); + name = g_path_get_basename (filename); + mono_assemblies_lock (); + for (i = 0; !image && bundles [i]; ++i) { + if (strcmp (bundles [i]->name, is_satellite ? filename : name) == 0) { + image = mono_image_open_from_data_with_name ((char*)bundles [i]->data, bundles [i]->size, FALSE, status, refonly, name); + break; + } + } + mono_assemblies_unlock (); + if (image) { + mono_image_addref (image); + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Assembly Loader loaded assembly from bundle: '%s'.", is_satellite ? filename : name); + g_free (name); + return image; + } + g_free (name); + return NULL; +} + +/** + * mono_assembly_open_full: + * \param filename the file to load + * \param status return status code + * \param refonly Whether this assembly is being opened in "reflection-only" mode. + * + * This loads an assembly from the specified \p filename. The \p filename allows + * a local URL (starting with a \c file:// prefix). If a file prefix is used, the + * filename is interpreted as a URL, and the filename is URL-decoded. Otherwise the file + * is treated as a local path. + * + * First, an attempt is made to load the assembly from the bundled executable (for those + * deployments that have been done with the \c mkbundle tool or for scenarios where the + * assembly has been registered as an embedded assembly). If this is not the case, then + * the assembly is loaded from disk using `api:mono_image_open_full`. + * + * If the pointed assembly does not live in the Global Assembly Cache, a shadow copy of + * the assembly is made. + * + * If \p refonly is set to true, then the assembly is loaded purely for inspection with + * the \c System.Reflection API. + * + * \returns NULL on error, with the \p status set to an error code, or a pointer + * to the assembly. + */ +MonoAssembly * +mono_assembly_open_full (const char *filename, MonoImageOpenStatus *status, gboolean refonly) +{ + return mono_assembly_open_a_lot (filename, status, refonly, FALSE); +} + +MonoAssembly * +mono_assembly_open_a_lot (const char *filename, MonoImageOpenStatus *status, gboolean refonly, gboolean load_from_context) +{ + return mono_assembly_open_predicate (filename, refonly, load_from_context, NULL, NULL, status); +} + +MonoAssembly * +mono_assembly_open_predicate (const char *filename, gboolean refonly, + gboolean load_from_context, + MonoAssemblyCandidatePredicate predicate, + gpointer user_data, + MonoImageOpenStatus *status) +{ + MonoImage *image; + MonoAssembly *ass; + MonoImageOpenStatus def_status; + gchar *fname; + gchar *new_fname; + gboolean loaded_from_bundle; + + g_return_val_if_fail (filename != NULL, NULL); + + if (!status) + status = &def_status; + *status = MONO_IMAGE_OK; + + if (strncmp (filename, "file://", 7) == 0) { + GError *error = NULL; + gchar *uri = (gchar *) filename; + gchar *tmpuri; + + /* + * MS allows file://c:/... and fails on file://localhost/c:/... + * They also throw an IndexOutOfRangeException if "file://" + */ + if (uri [7] != '/') + uri = g_strdup_printf ("file:///%s", uri + 7); + + tmpuri = uri; + uri = mono_escape_uri_string (tmpuri); + fname = g_filename_from_uri (uri, NULL, &error); + g_free (uri); + + if (tmpuri != filename) + g_free (tmpuri); + + if (error != NULL) { + g_warning ("%s\n", error->message); + g_error_free (error); + fname = g_strdup (filename); + } + } else { + fname = g_strdup (filename); + } + + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, + "Assembly Loader probing location: '%s'.", fname); + + new_fname = NULL; + if (!mono_assembly_is_in_gac (fname)) { + MonoError error; + new_fname = mono_make_shadow_copy (fname, &error); + if (!is_ok (&error)) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, + "Assembly Loader shadow copy error: %s.", mono_error_get_message (&error)); + mono_error_cleanup (&error); + *status = MONO_IMAGE_IMAGE_INVALID; + g_free (fname); + return NULL; + } + } + if (new_fname && new_fname != fname) { + g_free (fname); + fname = new_fname; + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, + "Assembly Loader shadow-copied assembly to: '%s'.", fname); + } + + image = NULL; + + // If VM built with mkbundle + loaded_from_bundle = FALSE; + if (bundles != NULL) { + image = mono_assembly_open_from_bundle (fname, status, refonly); + loaded_from_bundle = image != NULL; + } + + if (!image) + image = mono_image_open_a_lot (fname, status, refonly, load_from_context); + + if (!image){ + if (*status == MONO_IMAGE_OK) + *status = MONO_IMAGE_ERROR_ERRNO; + g_free (fname); + return NULL; + } + + if (image->assembly) { + /* We want to return the MonoAssembly that's already loaded, + * but if we're using the strict assembly loader, we also need + * to check that the previously loaded assembly matches the + * predicate. It could be that we previously loaded a + * different version that happens to have the filename that + * we're currently probing. */ + if (mono_loader_get_strict_strong_names () && + predicate && !predicate (image->assembly, user_data)) { + mono_image_close (image); + g_free (fname); + return NULL; + } else { + /* Already loaded by another appdomain */ + mono_assembly_invoke_load_hook (image->assembly); + mono_image_close (image); + g_free (fname); + return image->assembly; + } + } + + ass = mono_assembly_load_from_predicate (image, fname, refonly, predicate, user_data, status); + + if (ass) { + if (!loaded_from_bundle) + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, + "Assembly Loader loaded assembly from location: '%s'.", filename); + if (!refonly) + mono_config_for_assembly (ass->image); + } + + /* Clear the reference added by mono_image_open */ + mono_image_close (image); + + g_free (fname); + + return ass; +} + +static void +free_item (gpointer val, gpointer user_data) +{ + g_free (val); +} + +/** + * mono_assembly_load_friends: + * \param ass an assembly + * + * Load the list of friend assemblies that are allowed to access + * the assembly's internal types and members. They are stored as assembly + * names in custom attributes. + * + * This is an internal method, we need this because when we load mscorlib + * we do not have the internals visible cattr loaded yet, + * so we need to load these after we initialize the runtime. + * + * LOCKING: Acquires the assemblies lock plus the loader lock. + */ +void +mono_assembly_load_friends (MonoAssembly* ass) +{ + MonoError error; + int i; + MonoCustomAttrInfo* attrs; + GSList *list; + + if (ass->friend_assembly_names_inited) + return; + + attrs = mono_custom_attrs_from_assembly_checked (ass, FALSE, &error); + mono_error_assert_ok (&error); + if (!attrs) { + mono_assemblies_lock (); + ass->friend_assembly_names_inited = TRUE; + mono_assemblies_unlock (); + return; + } + + mono_assemblies_lock (); + if (ass->friend_assembly_names_inited) { + mono_assemblies_unlock (); + return; + } + mono_assemblies_unlock (); + + list = NULL; + /* + * We build the list outside the assemblies lock, the worse that can happen + * is that we'll need to free the allocated list. + */ + for (i = 0; i < attrs->num_attrs; ++i) { + MonoCustomAttrEntry *attr = &attrs->attrs [i]; + MonoAssemblyName *aname; + const gchar *data; + uint32_t data_length; + gchar *data_with_terminator; + /* Do some sanity checking */ + if (!attr->ctor || attr->ctor->klass != mono_class_try_get_internals_visible_class ()) + continue; + if (attr->data_size < 4) + continue; + data = (const char*)attr->data; + /* 0xFF means null string, see custom attr format */ + if (data [0] != 1 || data [1] != 0 || (data [2] & 0xFF) == 0xFF) + continue; + data_length = mono_metadata_decode_value (data + 2, &data); + data_with_terminator = (char *)g_memdup (data, data_length + 1); + data_with_terminator[data_length] = 0; + aname = g_new0 (MonoAssemblyName, 1); + /*g_print ("friend ass: %s\n", data);*/ + if (mono_assembly_name_parse_full (data_with_terminator, aname, TRUE, NULL, NULL)) { + list = g_slist_prepend (list, aname); + } else { + g_free (aname); + } + g_free (data_with_terminator); + } + mono_custom_attrs_free (attrs); + + mono_assemblies_lock (); + if (ass->friend_assembly_names_inited) { + mono_assemblies_unlock (); + g_slist_foreach (list, free_item, NULL); + g_slist_free (list); + return; + } + ass->friend_assembly_names = list; + + /* Because of the double checked locking pattern above */ + mono_memory_barrier (); + ass->friend_assembly_names_inited = TRUE; + mono_assemblies_unlock (); +} + +struct HasReferenceAssemblyAttributeIterData { + gboolean has_attr; +}; + +static gboolean +has_reference_assembly_attribute_iterator (MonoImage *image, guint32 typeref_scope_token, const char *nspace, const char *name, guint32 method_token, gpointer user_data) +{ + gboolean stop_scanning = FALSE; + struct HasReferenceAssemblyAttributeIterData *iter_data = (struct HasReferenceAssemblyAttributeIterData*)user_data; + + if (!strcmp (name, "ReferenceAssemblyAttribute") && !strcmp (nspace, "System.Runtime.CompilerServices")) { + /* Note we don't check the assembly name, same as coreCLR. */ + iter_data->has_attr = TRUE; + stop_scanning = TRUE; + } + + return stop_scanning; +} + +/** + * mono_assembly_has_reference_assembly_attribute: + * \param assembly a MonoAssembly + * \param error set on error. + * + * \returns TRUE if \p assembly has the \c System.Runtime.CompilerServices.ReferenceAssemblyAttribute set. + * On error returns FALSE and sets \p error. + */ +gboolean +mono_assembly_has_reference_assembly_attribute (MonoAssembly *assembly, MonoError *error) +{ + g_assert (assembly && assembly->image); + /* .NET Framework appears to ignore the attribute on dynamic + * assemblies, so don't call this function for dynamic assemblies. */ + g_assert (!image_is_dynamic (assembly->image)); + error_init (error); + + /* + * This might be called during assembly loading, so do everything using the low-level + * metadata APIs. + */ + + struct HasReferenceAssemblyAttributeIterData iter_data = { FALSE }; + + mono_assembly_metadata_foreach_custom_attr (assembly, &has_reference_assembly_attribute_iterator, &iter_data); + + return iter_data.has_attr; +} + +/** + * mono_assembly_open: + * \param filename Opens the assembly pointed out by this name + * \param status return status code + * + * This loads an assembly from the specified \p filename. The \p filename allows + * a local URL (starting with a \c file:// prefix). If a file prefix is used, the + * filename is interpreted as a URL, and the filename is URL-decoded. Otherwise the file + * is treated as a local path. + * + * First, an attempt is made to load the assembly from the bundled executable (for those + * deployments that have been done with the \c mkbundle tool or for scenarios where the + * assembly has been registered as an embedded assembly). If this is not the case, then + * the assembly is loaded from disk using `api:mono_image_open_full`. + * + * If the pointed assembly does not live in the Global Assembly Cache, a shadow copy of + * the assembly is made. + * + * \returns a pointer to the \c MonoAssembly if \p filename contains a valid + * assembly or NULL on error. Details about the error are stored in the + * \p status variable. + */ +MonoAssembly * +mono_assembly_open (const char *filename, MonoImageOpenStatus *status) +{ + return mono_assembly_open_predicate (filename, FALSE, FALSE, NULL, NULL, status); +} + +/** + * mono_assembly_load_from_full: + * \param image Image to load the assembly from + * \param fname assembly name to associate with the assembly + * \param status returns the status condition + * \param refonly Whether this assembly is being opened in "reflection-only" mode. + * + * If the provided \p image has an assembly reference, it will process the given + * image as an assembly with the given name. + * + * Most likely you want to use the `api:mono_assembly_load_full` method instead. + * + * Returns: A valid pointer to a \c MonoAssembly* on success and the \p status will be + * set to \c MONO_IMAGE_OK; or NULL on error. + * + * If there is an error loading the assembly the \p status will indicate the + * reason with \p status being set to \c MONO_IMAGE_INVALID if the + * image did not contain an assembly reference table. + */ +MonoAssembly * +mono_assembly_load_from_full (MonoImage *image, const char*fname, + MonoImageOpenStatus *status, gboolean refonly) +{ + return mono_assembly_load_from_predicate (image, fname, refonly, NULL, NULL, status); +} + +MonoAssembly * +mono_assembly_load_from_predicate (MonoImage *image, const char *fname, + gboolean refonly, + MonoAssemblyCandidatePredicate predicate, + gpointer user_data, + MonoImageOpenStatus *status) +{ + MonoAssembly *ass, *ass2; + char *base_dir; + + if (!image->tables [MONO_TABLE_ASSEMBLY].rows) { + /* 'image' doesn't have a manifest -- maybe someone is trying to Assembly.Load a .netmodule */ + *status = MONO_IMAGE_IMAGE_INVALID; + return NULL; + } + +#if defined (HOST_WIN32) + { + gchar *tmp_fn; + int i; + + tmp_fn = g_strdup (fname); + for (i = strlen (tmp_fn) - 1; i >= 0; i--) { + if (tmp_fn [i] == '/') + tmp_fn [i] = '\\'; + } + + base_dir = absolute_dir (tmp_fn); + g_free (tmp_fn); + } +#else + base_dir = absolute_dir (fname); +#endif + + /* + * Create assembly struct, and enter it into the assembly cache + */ + ass = g_new0 (MonoAssembly, 1); + ass->basedir = base_dir; + ass->ref_only = refonly; + ass->image = image; + + MONO_PROFILER_RAISE (assembly_loading, (ass)); + + mono_assembly_fill_assembly_name (image, &ass->aname); + + if (mono_defaults.corlib && strcmp (ass->aname.name, "mscorlib") == 0) { + // MS.NET doesn't support loading other mscorlibs + g_free (ass); + g_free (base_dir); + mono_image_addref (mono_defaults.corlib); + *status = MONO_IMAGE_OK; + return mono_defaults.corlib->assembly; + } + + /* Add a non-temporary reference because of ass->image */ + mono_image_addref (image); + + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Image addref %s[%p] -> %s[%p]: %d", ass->aname.name, ass, image->name, image, image->ref_count); + + /* + * The load hooks might take locks so we can't call them while holding the + * assemblies lock. + */ + if (ass->aname.name) { + ass2 = mono_assembly_invoke_search_hook_internal (&ass->aname, NULL, refonly, FALSE); + if (ass2) { + g_free (ass); + g_free (base_dir); + mono_image_close (image); + *status = MONO_IMAGE_OK; + return ass2; + } + } + + /* We need to check for ReferenceAssmeblyAttribute before we + * mark the assembly as loaded and before we fire the load + * hook. Otherwise mono_domain_fire_assembly_load () in + * appdomain.c will cache a mapping from the assembly name to + * this image and we won't be able to look for a different + * candidate. */ + + if (!refonly) { + MonoError refasm_error; + if (mono_assembly_has_reference_assembly_attribute (ass, &refasm_error)) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Image for assembly '%s' (%s) has ReferenceAssemblyAttribute, skipping", ass->aname.name, image->name); + g_free (ass); + g_free (base_dir); + mono_image_close (image); + *status = MONO_IMAGE_IMAGE_INVALID; + return NULL; + } + mono_error_cleanup (&refasm_error); + } + + if (predicate && !predicate (ass, user_data)) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate returned FALSE, skipping '%s' (%s)\n", ass->aname.name, image->name); + g_free (ass); + g_free (base_dir); + mono_image_close (image); + *status = MONO_IMAGE_IMAGE_INVALID; + return NULL; + } + + mono_assemblies_lock (); + + if (image->assembly) { + /* + * This means another thread has already loaded the assembly, but not yet + * called the load hooks so the search hook can't find the assembly. + */ + mono_assemblies_unlock (); + ass2 = image->assembly; + g_free (ass); + g_free (base_dir); + mono_image_close (image); + *status = MONO_IMAGE_OK; + return ass2; + } + + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Prepared to set up assembly '%s' (%s)", ass->aname.name, image->name); + + image->assembly = ass; + + loaded_assemblies = g_list_prepend (loaded_assemblies, ass); + mono_assemblies_unlock (); + +#ifdef HOST_WIN32 + if (image->is_module_handle) + mono_image_fixup_vtable (image); +#endif + + mono_assembly_invoke_load_hook (ass); + + MONO_PROFILER_RAISE (assembly_loaded, (ass)); + + return ass; +} + +/** + * mono_assembly_load_from: + * \param image Image to load the assembly from + * \param fname assembly name to associate with the assembly + * \param status return status code + * + * If the provided \p image has an assembly reference, it will process the given + * image as an assembly with the given name. + * + * Most likely you want to use the `api:mono_assembly_load_full` method instead. + * + * This is equivalent to calling `api:mono_assembly_load_from_full` with the + * \p refonly parameter set to FALSE. + * \returns A valid pointer to a \c MonoAssembly* on success and then \p status will be + * set to \c MONO_IMAGE_OK; or NULL on error. + * + * If there is an error loading the assembly the \p status will indicate the + * reason with \p status being set to \c MONO_IMAGE_INVALID if the + * image did not contain an assembly reference table. + + */ +MonoAssembly * +mono_assembly_load_from (MonoImage *image, const char *fname, + MonoImageOpenStatus *status) +{ + return mono_assembly_load_from_full (image, fname, status, FALSE); +} + +/** + * mono_assembly_name_free: + * \param aname assembly name to free + * + * Frees the provided assembly name object. + * (it does not frees the object itself, only the name members). + */ +void +mono_assembly_name_free (MonoAssemblyName *aname) +{ + if (aname == NULL) + return; + + g_free ((void *) aname->name); + g_free ((void *) aname->culture); + g_free ((void *) aname->hash_value); + g_free ((guint8*) aname->public_key); +} + +static gboolean +parse_public_key (const gchar *key, gchar** pubkey, gboolean *is_ecma) +{ + const gchar *pkey; + gchar header [16], val, *arr, *endp; + gint i, j, offset, bitlen, keylen, pkeylen; + + //both pubkey and is_ecma are required arguments + g_assert (pubkey && is_ecma); + + keylen = strlen (key) >> 1; + if (keylen < 1) + return FALSE; + + /* allow the ECMA standard key */ + if (strcmp (key, "00000000000000000400000000000000") == 0) { + *pubkey = NULL; + *is_ecma = TRUE; + return TRUE; + } + *is_ecma = FALSE; + val = g_ascii_xdigit_value (key [0]) << 4; + val |= g_ascii_xdigit_value (key [1]); + switch (val) { + case 0x00: + if (keylen < 13) + return FALSE; + val = g_ascii_xdigit_value (key [24]); + val |= g_ascii_xdigit_value (key [25]); + if (val != 0x06) + return FALSE; + pkey = key + 24; + break; + case 0x06: + pkey = key; + break; + default: + return FALSE; + } + + /* We need the first 16 bytes + * to check whether this key is valid or not */ + pkeylen = strlen (pkey) >> 1; + if (pkeylen < 16) + return FALSE; + + for (i = 0, j = 0; i < 16; i++) { + header [i] = g_ascii_xdigit_value (pkey [j++]) << 4; + header [i] |= g_ascii_xdigit_value (pkey [j++]); + } + + if (header [0] != 0x06 || /* PUBLICKEYBLOB (0x06) */ + header [1] != 0x02 || /* Version (0x02) */ + header [2] != 0x00 || /* Reserved (word) */ + header [3] != 0x00 || + (guint)(read32 (header + 8)) != 0x31415352) /* DWORD magic = RSA1 */ + return FALSE; + + /* Based on this length, we _should_ be able to know if the length is right */ + bitlen = read32 (header + 12) >> 3; + if ((bitlen + 16 + 4) != pkeylen) + return FALSE; + + arr = (gchar *)g_malloc (keylen + 4); + /* Encode the size of the blob */ + mono_metadata_encode_value (keylen, &arr[0], &endp); + offset = (gint)(endp-arr); + + for (i = offset, j = 0; i < keylen + offset; i++) { + arr [i] = g_ascii_xdigit_value (key [j++]) << 4; + arr [i] |= g_ascii_xdigit_value (key [j++]); + } + + *pubkey = arr; + + return TRUE; +} + +static gboolean +build_assembly_name (const char *name, const char *version, const char *culture, const char *token, const char *key, guint32 flags, guint32 arch, MonoAssemblyName *aname, gboolean save_public_key) +{ + gint major, minor, build, revision; + gint len; + gint version_parts; + gchar *pkeyptr, *encoded, tok [8]; + + memset (aname, 0, sizeof (MonoAssemblyName)); + + if (version) { + version_parts = sscanf (version, "%u.%u.%u.%u", &major, &minor, &build, &revision); + if (version_parts < 2 || version_parts > 4) + return FALSE; + + /* FIXME: we should set build & revision to -1 (instead of 0) + if these are not set in the version string. That way, later on, + we can still determine if these were specified. */ + aname->major = major; + aname->minor = minor; + if (version_parts >= 3) + aname->build = build; + else + aname->build = 0; + if (version_parts == 4) + aname->revision = revision; + else + aname->revision = 0; + } + + aname->flags = flags; + aname->arch = arch; + aname->name = g_strdup (name); + + if (culture) { + if (g_ascii_strcasecmp (culture, "neutral") == 0) + aname->culture = g_strdup (""); + else + aname->culture = g_strdup (culture); + } + + if (token && strncmp (token, "null", 4) != 0) { + char *lower; + + /* the constant includes the ending NULL, hence the -1 */ + if (strlen (token) != (MONO_PUBLIC_KEY_TOKEN_LENGTH - 1)) { + mono_assembly_name_free (aname); + return FALSE; + } + lower = g_ascii_strdown (token, MONO_PUBLIC_KEY_TOKEN_LENGTH); + g_strlcpy ((char*)aname->public_key_token, lower, MONO_PUBLIC_KEY_TOKEN_LENGTH); + g_free (lower); + } + + if (key) { + gboolean is_ecma = FALSE; + gchar *pkey = NULL; + if (strcmp (key, "null") == 0 || !parse_public_key (key, &pkey, &is_ecma)) { + mono_assembly_name_free (aname); + return FALSE; + } + + if (is_ecma) { + g_assert (pkey == NULL); + aname->public_key = NULL; + g_strlcpy ((gchar*)aname->public_key_token, "b77a5c561934e089", MONO_PUBLIC_KEY_TOKEN_LENGTH); + return TRUE; + } + + len = mono_metadata_decode_blob_size ((const gchar *) pkey, (const gchar **) &pkeyptr); + // We also need to generate the key token + mono_digest_get_public_token ((guchar*) tok, (guint8*) pkeyptr, len); + encoded = encode_public_tok ((guchar*) tok, 8); + g_strlcpy ((gchar*)aname->public_key_token, encoded, MONO_PUBLIC_KEY_TOKEN_LENGTH); + g_free (encoded); + + if (save_public_key) + aname->public_key = (guint8*) pkey; + else + g_free (pkey); + } + + return TRUE; +} + +static gboolean +parse_assembly_directory_name (const char *name, const char *dirname, MonoAssemblyName *aname) +{ + gchar **parts; + gboolean res; + + parts = g_strsplit (dirname, "_", 3); + if (!parts || !parts[0] || !parts[1] || !parts[2]) { + g_strfreev (parts); + return FALSE; + } + + res = build_assembly_name (name, parts[0], parts[1], parts[2], NULL, 0, 0, aname, FALSE); + g_strfreev (parts); + return res; +} + +static gboolean +split_key_value (const gchar *pair, gchar **key, guint32 *keylen, gchar **value) +{ + char *eqsign = strchr (pair, '='); + if (!eqsign) { + *key = NULL; + *keylen = 0; + *value = NULL; + return FALSE; + } + + *key = (gchar*)pair; + *keylen = eqsign - *key; + while (*keylen > 0 && g_ascii_isspace ((*key) [*keylen - 1])) + (*keylen)--; + *value = g_strstrip (eqsign + 1); + return TRUE; +} + +gboolean +mono_assembly_name_parse_full (const char *name, MonoAssemblyName *aname, gboolean save_public_key, gboolean *is_version_defined, gboolean *is_token_defined) +{ + gchar *dllname; + gchar *dllname_uq; + gchar *version = NULL; + gchar *version_uq; + gchar *culture = NULL; + gchar *culture_uq; + gchar *token = NULL; + gchar *token_uq; + gchar *key = NULL; + gchar *key_uq; + gchar *retargetable = NULL; + gchar *retargetable_uq; + gchar *procarch; + gchar *procarch_uq; + gboolean res; + gchar *value, *part_name; + guint32 part_name_len; + gchar **parts; + gchar **tmp; + gboolean version_defined; + gboolean token_defined; + guint32 flags = 0; + guint32 arch = MONO_PROCESSOR_ARCHITECTURE_NONE; + + if (!is_version_defined) + is_version_defined = &version_defined; + *is_version_defined = FALSE; + if (!is_token_defined) + is_token_defined = &token_defined; + *is_token_defined = FALSE; + + parts = tmp = g_strsplit (name, ",", 6); + if (!tmp || !*tmp) { + g_strfreev (tmp); + return FALSE; + } + + dllname = g_strstrip (*tmp); + + tmp++; + + while (*tmp) { + if (!split_key_value (g_strstrip (*tmp), &part_name, &part_name_len, &value)) + goto cleanup_and_fail; + + if (part_name_len == 7 && !g_ascii_strncasecmp (part_name, "Version", part_name_len)) { + *is_version_defined = TRUE; + version = value; + if (strlen (version) == 0) { + goto cleanup_and_fail; + } + tmp++; + continue; + } + + if (part_name_len == 7 && !g_ascii_strncasecmp (part_name, "Culture", part_name_len)) { + culture = value; + if (strlen (culture) == 0) { + goto cleanup_and_fail; + } + tmp++; + continue; + } + + if (part_name_len == 14 && !g_ascii_strncasecmp (part_name, "PublicKeyToken", part_name_len)) { + *is_token_defined = TRUE; + token = value; + if (strlen (token) == 0) { + goto cleanup_and_fail; + } + tmp++; + continue; + } + + if (part_name_len == 9 && !g_ascii_strncasecmp (part_name, "PublicKey", part_name_len)) { + key = value; + if (strlen (key) == 0) { + goto cleanup_and_fail; + } + tmp++; + continue; + } + + if (part_name_len == 12 && !g_ascii_strncasecmp (part_name, "Retargetable", part_name_len)) { + retargetable = value; + retargetable_uq = unquote (retargetable); + if (retargetable_uq != NULL) + retargetable = retargetable_uq; + + if (!g_ascii_strcasecmp (retargetable, "yes")) { + flags |= ASSEMBLYREF_RETARGETABLE_FLAG; + } else if (g_ascii_strcasecmp (retargetable, "no")) { + g_free (retargetable_uq); + goto cleanup_and_fail; + } + + g_free (retargetable_uq); + tmp++; + continue; + } + + if (part_name_len == 21 && !g_ascii_strncasecmp (part_name, "ProcessorArchitecture", part_name_len)) { + procarch = value; + procarch_uq = unquote (procarch); + if (procarch_uq != NULL) + procarch = procarch_uq; + + if (!g_ascii_strcasecmp (procarch, "MSIL")) + arch = MONO_PROCESSOR_ARCHITECTURE_MSIL; + else if (!g_ascii_strcasecmp (procarch, "X86")) + arch = MONO_PROCESSOR_ARCHITECTURE_X86; + else if (!g_ascii_strcasecmp (procarch, "IA64")) + arch = MONO_PROCESSOR_ARCHITECTURE_IA64; + else if (!g_ascii_strcasecmp (procarch, "AMD64")) + arch = MONO_PROCESSOR_ARCHITECTURE_AMD64; + else { + g_free (procarch_uq); + goto cleanup_and_fail; + } + + g_free (procarch_uq); + tmp++; + continue; + } + + g_strfreev (parts); + return FALSE; + } + + /* if retargetable flag is set, then we must have a fully qualified name */ + if (retargetable != NULL && (version == NULL || culture == NULL || (key == NULL && token == NULL))) { + goto cleanup_and_fail; + } + + dllname_uq = unquote (dllname); + version_uq = unquote (version); + culture_uq = unquote (culture); + token_uq = unquote (token); + key_uq = unquote (key); + + res = build_assembly_name ( + dllname_uq == NULL ? dllname : dllname_uq, + version_uq == NULL ? version : version_uq, + culture_uq == NULL ? culture : culture_uq, + token_uq == NULL ? token : token_uq, + key_uq == NULL ? key : key_uq, + flags, arch, aname, save_public_key); + + g_free (dllname_uq); + g_free (version_uq); + g_free (culture_uq); + g_free (token_uq); + g_free (key_uq); + + g_strfreev (parts); + return res; + +cleanup_and_fail: + g_strfreev (parts); + return FALSE; +} + +static char* +unquote (const char *str) +{ + gint slen; + const char *end; + + if (str == NULL) + return NULL; + + slen = strlen (str); + if (slen < 2) + return NULL; + + if (*str != '\'' && *str != '\"') + return NULL; + + end = str + slen - 1; + if (*str != *end) + return NULL; + + return g_strndup (str + 1, slen - 2); +} + +/** + * mono_assembly_name_parse: + * \param name name to parse + * \param aname the destination assembly name + * + * Parses an assembly qualified type name and assigns the name, + * version, culture and token to the provided assembly name object. + * + * \returns TRUE if the name could be parsed. + */ +gboolean +mono_assembly_name_parse (const char *name, MonoAssemblyName *aname) +{ + return mono_assembly_name_parse_full (name, aname, FALSE, NULL, NULL); +} + +/** + * mono_assembly_name_new: + * \param name name to parse + * + * Allocate a new \c MonoAssemblyName and fill its values from the + * passed \p name. + * + * \returns a newly allocated structure or NULL if there was any failure. + */ +MonoAssemblyName* +mono_assembly_name_new (const char *name) +{ + MonoAssemblyName *aname = g_new0 (MonoAssemblyName, 1); + if (mono_assembly_name_parse (name, aname)) + return aname; + g_free (aname); + return NULL; +} + +/** + * mono_assembly_name_get_name: + */ +const char* +mono_assembly_name_get_name (MonoAssemblyName *aname) +{ + return aname->name; +} + +/** + * mono_assembly_name_get_culture: + */ +const char* +mono_assembly_name_get_culture (MonoAssemblyName *aname) +{ + return aname->culture; +} + +/** + * mono_assembly_name_get_pubkeytoken: + */ +mono_byte* +mono_assembly_name_get_pubkeytoken (MonoAssemblyName *aname) +{ + if (aname->public_key_token [0]) + return aname->public_key_token; + return NULL; +} + +/** + * mono_assembly_name_get_version: + */ +uint16_t +mono_assembly_name_get_version (MonoAssemblyName *aname, uint16_t *minor, uint16_t *build, uint16_t *revision) +{ + if (minor) + *minor = aname->minor; + if (build) + *build = aname->build; + if (revision) + *revision = aname->revision; + return aname->major; +} + +static MonoAssembly* +probe_for_partial_name (const char *basepath, const char *fullname, MonoAssemblyName *aname, MonoImageOpenStatus *status) +{ + gchar *fullpath = NULL; + GDir *dirhandle; + const char* direntry; + MonoAssemblyName gac_aname; + gint major=-1, minor=0, build=0, revision=0; + gboolean exact_version; + + dirhandle = g_dir_open (basepath, 0, NULL); + if (!dirhandle) + return NULL; + + exact_version = (aname->major | aname->minor | aname->build | aname->revision) != 0; + + while ((direntry = g_dir_read_name (dirhandle))) { + gboolean match = TRUE; + + if(!parse_assembly_directory_name (aname->name, direntry, &gac_aname)) + continue; + + if (aname->culture != NULL && strcmp (aname->culture, gac_aname.culture) != 0) + match = FALSE; + + if (match && strlen ((char*)aname->public_key_token) > 0 && + !mono_public_tokens_are_equal (aname->public_key_token, gac_aname.public_key_token)) + match = FALSE; + + if (match) { + if (exact_version) { + match = (aname->major == gac_aname.major && aname->minor == gac_aname.minor && + aname->build == gac_aname.build && aname->revision == gac_aname.revision); + } + else if (gac_aname.major < major) + match = FALSE; + else if (gac_aname.major == major) { + if (gac_aname.minor < minor) + match = FALSE; + else if (gac_aname.minor == minor) { + if (gac_aname.build < build) + match = FALSE; + else if (gac_aname.build == build && gac_aname.revision <= revision) + match = FALSE; + } + } + } + + if (match) { + major = gac_aname.major; + minor = gac_aname.minor; + build = gac_aname.build; + revision = gac_aname.revision; + g_free (fullpath); + fullpath = g_build_path (G_DIR_SEPARATOR_S, basepath, direntry, fullname, NULL); + } + + mono_assembly_name_free (&gac_aname); + } + + g_dir_close (dirhandle); + + if (fullpath == NULL) + return NULL; + else { + MonoAssembly *res = mono_assembly_open_predicate (fullpath, FALSE, FALSE, NULL, NULL, status); + g_free (fullpath); + return res; + } +} + +/** + * mono_assembly_load_with_partial_name: + * \param name an assembly name that is then parsed by `api:mono_assembly_name_parse`. + * \param status return status code + * + * Loads a \c MonoAssembly from a name. The name is parsed using `api:mono_assembly_name_parse`, + * so it might contain a qualified type name, version, culture and token. + * + * This will load the assembly from the file whose name is derived from the assembly name + * by appending the \c .dll extension. + * + * The assembly is loaded from either one of the extra Global Assembly Caches specified + * by the extra GAC paths (specified by the \c MONO_GAC_PREFIX environment variable) or + * if that fails from the GAC. + * + * \returns NULL on failure, or a pointer to a \c MonoAssembly on success. + */ +MonoAssembly* +mono_assembly_load_with_partial_name (const char *name, MonoImageOpenStatus *status) +{ + MonoError error; + MonoAssembly *res; + MonoAssemblyName *aname, base_name; + MonoAssemblyName mapped_aname; + gchar *fullname, *gacpath; + gchar **paths; + + memset (&base_name, 0, sizeof (MonoAssemblyName)); + aname = &base_name; + + if (!mono_assembly_name_parse (name, aname)) + return NULL; + + /* + * If no specific version has been requested, make sure we load the + * correct version for system assemblies. + */ + if ((aname->major | aname->minor | aname->build | aname->revision) == 0) + aname = mono_assembly_remap_version (aname, &mapped_aname); + + res = mono_assembly_loaded (aname); + if (res) { + mono_assembly_name_free (aname); + return res; + } + + res = invoke_assembly_preload_hook (aname, assemblies_path); + if (res) { + res->in_gac = FALSE; + mono_assembly_name_free (aname); + return res; + } + + fullname = g_strdup_printf ("%s.dll", aname->name); + + if (extra_gac_paths) { + paths = extra_gac_paths; + while (!res && *paths) { + gacpath = g_build_path (G_DIR_SEPARATOR_S, *paths, "lib", "mono", "gac", aname->name, NULL); + res = probe_for_partial_name (gacpath, fullname, aname, status); + g_free (gacpath); + paths++; + } + } + + if (res) { + res->in_gac = TRUE; + g_free (fullname); + mono_assembly_name_free (aname); + return res; + } + + gacpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (), "mono", "gac", aname->name, NULL); + res = probe_for_partial_name (gacpath, fullname, aname, status); + g_free (gacpath); + + g_free (fullname); + mono_assembly_name_free (aname); + + if (res) + res->in_gac = TRUE; + else { + MonoDomain *domain = mono_domain_get (); + + res = mono_try_assembly_resolve (domain, name, NULL, FALSE, &error); + if (!is_ok (&error)) { + mono_error_cleanup (&error); + if (*status == MONO_IMAGE_OK) + *status = MONO_IMAGE_IMAGE_INVALID; + } + } + + return res; +} + +static MonoBoolean +mono_assembly_is_in_gac (const gchar *filename) +{ + const gchar *rootdir; + gchar *gp; + gchar **paths; + + if (filename == NULL) + return FALSE; + + for (paths = extra_gac_paths; paths && *paths; paths++) { + if (strstr (*paths, filename) != *paths) + continue; + + gp = (gchar *) (filename + strlen (*paths)); + if (*gp != G_DIR_SEPARATOR) + continue; + gp++; + if (strncmp (gp, "lib", 3)) + continue; + gp += 3; + if (*gp != G_DIR_SEPARATOR) + continue; + gp++; + if (strncmp (gp, "mono", 4)) + continue; + gp += 4; + if (*gp != G_DIR_SEPARATOR) + continue; + gp++; + if (strncmp (gp, "gac", 3)) + continue; + gp += 3; + if (*gp != G_DIR_SEPARATOR) + continue; + + return TRUE; + } + + rootdir = mono_assembly_getrootdir (); + if (strstr (filename, rootdir) != filename) + return FALSE; + + gp = (gchar *) (filename + strlen (rootdir)); + if (*gp != G_DIR_SEPARATOR) + return FALSE; + gp++; + if (strncmp (gp, "mono", 4)) + return FALSE; + gp += 4; + if (*gp != G_DIR_SEPARATOR) + return FALSE; + gp++; + if (strncmp (gp, "gac", 3)) + return FALSE; + gp += 3; + if (*gp != G_DIR_SEPARATOR) + return FALSE; + return TRUE; +} + +static MonoImage* +mono_assembly_load_publisher_policy (MonoAssemblyName *aname) +{ + MonoImage *image; + gchar *filename, *pname, *name, *culture, *version, *fullpath, *subpath; + gchar **paths; + gint32 len; + + if (strstr (aname->name, ".dll")) { + len = strlen (aname->name) - 4; + name = (gchar *)g_malloc (len + 1); + memcpy (name, aname->name, len); + name[len] = 0; + } else + name = g_strdup (aname->name); + + if (aname->culture) + culture = g_utf8_strdown (aname->culture, -1); + else + culture = g_strdup (""); + + pname = g_strdup_printf ("policy.%d.%d.%s", aname->major, aname->minor, name); + version = g_strdup_printf ("0.0.0.0_%s_%s", culture, aname->public_key_token); + g_free (name); + g_free (culture); + + filename = g_strconcat (pname, ".dll", NULL); + subpath = g_build_path (G_DIR_SEPARATOR_S, pname, version, filename, NULL); + g_free (pname); + g_free (version); + g_free (filename); + + image = NULL; + if (extra_gac_paths) { + paths = extra_gac_paths; + while (!image && *paths) { + fullpath = g_build_path (G_DIR_SEPARATOR_S, *paths, + "lib", "mono", "gac", subpath, NULL); + image = mono_image_open (fullpath, NULL); + g_free (fullpath); + paths++; + } + } + + if (image) { + g_free (subpath); + return image; + } + + fullpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (), + "mono", "gac", subpath, NULL); + image = mono_image_open (fullpath, NULL); + g_free (subpath); + g_free (fullpath); + + return image; +} + +static MonoAssemblyName* +mono_assembly_bind_version (MonoAssemblyBindingInfo *info, MonoAssemblyName *aname, MonoAssemblyName *dest_name) +{ + memcpy (dest_name, aname, sizeof (MonoAssemblyName)); + dest_name->major = info->new_version.major; + dest_name->minor = info->new_version.minor; + dest_name->build = info->new_version.build; + dest_name->revision = info->new_version.revision; + + return dest_name; +} + +/* LOCKING: assembly_binding lock must be held */ +static MonoAssemblyBindingInfo* +search_binding_loaded (MonoAssemblyName *aname) +{ + GSList *tmp; + + for (tmp = loaded_assembly_bindings; tmp; tmp = tmp->next) { + MonoAssemblyBindingInfo *info = (MonoAssemblyBindingInfo *)tmp->data; + if (assembly_binding_maps_name (info, aname)) + return info; + } + + return NULL; +} + +static inline gboolean +info_compare_versions (AssemblyVersionSet *left, AssemblyVersionSet *right) +{ + if (left->major != right->major || left->minor != right->minor || + left->build != right->build || left->revision != right->revision) + return FALSE; + + return TRUE; +} + +static inline gboolean +info_versions_equal (MonoAssemblyBindingInfo *left, MonoAssemblyBindingInfo *right) +{ + if (left->has_old_version_bottom != right->has_old_version_bottom) + return FALSE; + + if (left->has_old_version_top != right->has_old_version_top) + return FALSE; + + if (left->has_new_version != right->has_new_version) + return FALSE; + + if (left->has_old_version_bottom && !info_compare_versions (&left->old_version_bottom, &right->old_version_bottom)) + return FALSE; + + if (left->has_old_version_top && !info_compare_versions (&left->old_version_top, &right->old_version_top)) + return FALSE; + + if (left->has_new_version && !info_compare_versions (&left->new_version, &right->new_version)) + return FALSE; + + return TRUE; +} + +/* LOCKING: assumes all the necessary locks are held */ +static void +assembly_binding_info_parsed (MonoAssemblyBindingInfo *info, void *user_data) +{ + MonoAssemblyBindingInfo *info_copy; + GSList *tmp; + MonoAssemblyBindingInfo *info_tmp; + MonoDomain *domain = (MonoDomain*)user_data; + + if (!domain) + return; + + if (info->has_new_version && mono_assembly_is_problematic_version (info->name, info->new_version.major, info->new_version.minor, info->new_version.build, info->new_version.revision)) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Discarding assembly binding to problematic version %s v%d.%d.%d.%d", + info->name, info->new_version.major, info->new_version.minor, info->new_version.build, info->new_version.revision); + return; + } + + for (tmp = domain->assembly_bindings; tmp; tmp = tmp->next) { + info_tmp = (MonoAssemblyBindingInfo *)tmp->data; + if (strcmp (info->name, info_tmp->name) == 0 && info_versions_equal (info, info_tmp)) + return; + } + + info_copy = (MonoAssemblyBindingInfo *)mono_mempool_alloc0 (domain->mp, sizeof (MonoAssemblyBindingInfo)); + memcpy (info_copy, info, sizeof (MonoAssemblyBindingInfo)); + if (info->name) + info_copy->name = mono_mempool_strdup (domain->mp, info->name); + if (info->culture) + info_copy->culture = mono_mempool_strdup (domain->mp, info->culture); + + domain->assembly_bindings = g_slist_append_mempool (domain->mp, domain->assembly_bindings, info_copy); +} + +static int +get_version_number (int major, int minor) +{ + return major * 256 + minor; +} + +static inline gboolean +info_major_minor_in_range (MonoAssemblyBindingInfo *info, MonoAssemblyName *aname) +{ + int aname_version_number = get_version_number (aname->major, aname->minor); + if (!info->has_old_version_bottom) + return FALSE; + + if (get_version_number (info->old_version_bottom.major, info->old_version_bottom.minor) > aname_version_number) + return FALSE; + + if (info->has_old_version_top && get_version_number (info->old_version_top.major, info->old_version_top.minor) < aname_version_number) + return FALSE; + + /* This is not the nicest way to do it, but it's a by-product of the way parsing is done */ + info->major = aname->major; + info->minor = aname->minor; + + return TRUE; +} + +/* LOCKING: Assumes that we are already locked - both loader and domain locks */ +static MonoAssemblyBindingInfo* +get_per_domain_assembly_binding_info (MonoDomain *domain, MonoAssemblyName *aname) +{ + MonoAssemblyBindingInfo *info; + GSList *list; + + if (!domain->assembly_bindings) + return NULL; + + info = NULL; + for (list = domain->assembly_bindings; list; list = list->next) { + info = (MonoAssemblyBindingInfo *)list->data; + if (info && !strcmp (aname->name, info->name) && info_major_minor_in_range (info, aname)) + break; + info = NULL; + } + + if (info) { + if (info->name && info->public_key_token [0] && info->has_old_version_bottom && + info->has_new_version && assembly_binding_maps_name (info, aname)) + info->is_valid = TRUE; + else + info->is_valid = FALSE; + } + + return info; +} + +void +mono_domain_parse_assembly_bindings (MonoDomain *domain, int amajor, int aminor, gchar *domain_config_file_name) +{ + if (domain->assembly_bindings_parsed) + return; + mono_domain_lock (domain); + if (!domain->assembly_bindings_parsed) { + + gchar *domain_config_file_path = mono_portability_find_file (domain_config_file_name, TRUE); + + if (!domain_config_file_path) + domain_config_file_path = domain_config_file_name; + + mono_config_parse_assembly_bindings (domain_config_file_path, amajor, aminor, domain, assembly_binding_info_parsed); + domain->assembly_bindings_parsed = TRUE; + if (domain_config_file_name != domain_config_file_path) + g_free (domain_config_file_path); + } + + mono_domain_unlock (domain); +} + +static MonoAssemblyName* +mono_assembly_apply_binding (MonoAssemblyName *aname, MonoAssemblyName *dest_name) +{ + MonoError error; + MonoAssemblyBindingInfo *info, *info2; + MonoImage *ppimage; + MonoDomain *domain; + + if (aname->public_key_token [0] == 0) + return aname; + + domain = mono_domain_get (); + + mono_assembly_binding_lock (); + info = search_binding_loaded (aname); + mono_assembly_binding_unlock (); + + if (!info) { + mono_domain_lock (domain); + info = get_per_domain_assembly_binding_info (domain, aname); + mono_domain_unlock (domain); + } + + if (info) { + if (!check_policy_versions (info, aname)) + return aname; + + mono_assembly_bind_version (info, aname, dest_name); + return dest_name; + } + + if (domain && domain->setup && domain->setup->configuration_file) { + gchar *domain_config_file_name = mono_string_to_utf8_checked (domain->setup->configuration_file, &error); + /* expect this to succeed because mono_domain_set_options_from_config () did + * the same thing when the domain was created. */ + mono_error_assert_ok (&error); + mono_domain_parse_assembly_bindings (domain, aname->major, aname->minor, domain_config_file_name); + g_free (domain_config_file_name); + + mono_domain_lock (domain); + info2 = get_per_domain_assembly_binding_info (domain, aname); + + if (info2) { + info = (MonoAssemblyBindingInfo *)g_memdup (info2, sizeof (MonoAssemblyBindingInfo)); + info->name = g_strdup (info2->name); + info->culture = g_strdup (info2->culture); + info->domain_id = domain->domain_id; + } + + mono_domain_unlock (domain); + + } + + if (!info) { + info = g_new0 (MonoAssemblyBindingInfo, 1); + info->major = aname->major; + info->minor = aname->minor; + } + + if (!info->is_valid) { + ppimage = mono_assembly_load_publisher_policy (aname); + if (ppimage) { + get_publisher_policy_info (ppimage, aname, info); + mono_image_close (ppimage); + } + } + + /* Define default error value if needed */ + if (!info->is_valid) { + info->name = g_strdup (aname->name); + info->culture = g_strdup (aname->culture); + g_strlcpy ((char *)info->public_key_token, (const char *)aname->public_key_token, MONO_PUBLIC_KEY_TOKEN_LENGTH); + } + + mono_assembly_binding_lock (); + info2 = search_binding_loaded (aname); + if (info2) { + /* This binding was added by another thread + * before us */ + mono_assembly_binding_info_free (info); + g_free (info); + + info = info2; + } else + loaded_assembly_bindings = g_slist_prepend (loaded_assembly_bindings, info); + + mono_assembly_binding_unlock (); + + if (!info->is_valid || !check_policy_versions (info, aname)) + return aname; + + mono_assembly_bind_version (info, aname, dest_name); + return dest_name; +} + +/** + * mono_assembly_load_from_gac + * + * \param aname The assembly name object + */ +static MonoAssembly* +mono_assembly_load_from_gac (MonoAssemblyName *aname, gchar *filename, MonoImageOpenStatus *status, MonoBoolean refonly) +{ + MonoAssembly *result = NULL; + gchar *name, *version, *culture, *fullpath, *subpath; + gint32 len; + gchar **paths; + char *pubtok; + + if (aname->public_key_token [0] == 0) { + return NULL; + } + + if (strstr (aname->name, ".dll")) { + len = strlen (filename) - 4; + name = (gchar *)g_malloc (len + 1); + memcpy (name, aname->name, len); + name[len] = 0; + } else { + name = g_strdup (aname->name); + } + + if (aname->culture) { + culture = g_utf8_strdown (aname->culture, -1); + } else { + culture = g_strdup (""); + } + + pubtok = g_ascii_strdown ((char*)aname->public_key_token, MONO_PUBLIC_KEY_TOKEN_LENGTH); + version = g_strdup_printf ("%d.%d.%d.%d_%s_%s", aname->major, + aname->minor, aname->build, aname->revision, + culture, pubtok); + g_free (pubtok); + + subpath = g_build_path (G_DIR_SEPARATOR_S, name, version, filename, NULL); + g_free (name); + g_free (version); + g_free (culture); + + if (extra_gac_paths) { + paths = extra_gac_paths; + while (!result && *paths) { + fullpath = g_build_path (G_DIR_SEPARATOR_S, *paths, "lib", "mono", "gac", subpath, NULL); + result = mono_assembly_open_predicate (fullpath, refonly, FALSE, NULL, NULL, status); + g_free (fullpath); + paths++; + } + } + + if (result) { + result->in_gac = TRUE; + g_free (subpath); + return result; + } + + fullpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (), + "mono", "gac", subpath, NULL); + result = mono_assembly_open_predicate (fullpath, refonly, FALSE, NULL, NULL, status); + g_free (fullpath); + + if (result) + result->in_gac = TRUE; + + g_free (subpath); + + return result; +} + +MonoAssembly* +mono_assembly_load_corlib (const MonoRuntimeInfo *runtime, MonoImageOpenStatus *status) +{ + char *corlib_file; + MonoAssemblyName *aname; + + if (corlib) { + /* g_print ("corlib already loaded\n"); */ + return corlib; + } + + // A nonstandard preload hook may provide a special mscorlib assembly + aname = mono_assembly_name_new ("mscorlib.dll"); + corlib = invoke_assembly_preload_hook (aname, assemblies_path); + mono_assembly_name_free (aname); + g_free (aname); + if (corlib != NULL) + goto return_corlib_and_facades; + + // This unusual directory layout can occur if mono is being built and run out of its own source repo + if (assemblies_path) { // Custom assemblies path set via MONO_PATH or mono_set_assemblies_path + corlib = load_in_path ("mscorlib.dll", (const char**)assemblies_path, status, FALSE, NULL, NULL); + if (corlib) + goto return_corlib_and_facades; + } + + /* Normal case: Load corlib from mono/ */ + corlib_file = g_build_filename ("mono", runtime->framework_version, "mscorlib.dll", NULL); + if (assemblies_path) { // Custom assemblies path + corlib = load_in_path (corlib_file, (const char**)assemblies_path, status, FALSE, NULL, NULL); + if (corlib) { + g_free (corlib_file); + goto return_corlib_and_facades; + } + } + corlib = load_in_path (corlib_file, default_path, status, FALSE, NULL, NULL); + g_free (corlib_file); + +return_corlib_and_facades: + if (corlib && !strcmp (runtime->framework_version, "4.5")) // FIXME: stop hardcoding 4.5 here + default_path [1] = g_strdup_printf ("%s/Facades", corlib->basedir); + + return corlib; +} + +static MonoAssembly* +prevent_reference_assembly_from_running (MonoAssembly* candidate, gboolean refonly) +{ + MonoError refasm_error; + error_init (&refasm_error); + if (candidate && !refonly) { + /* .NET Framework seems to not check for ReferenceAssemblyAttribute on dynamic assemblies */ + if (!image_is_dynamic (candidate->image) && + mono_assembly_has_reference_assembly_attribute (candidate, &refasm_error)) + candidate = NULL; + } + mono_error_cleanup (&refasm_error); + return candidate; +} + +gboolean +mono_assembly_candidate_predicate_sn_same_name (MonoAssembly *candidate, gpointer ud) +{ + MonoAssemblyName *wanted_name = (MonoAssemblyName*)ud; + MonoAssemblyName *candidate_name = &candidate->aname; + + g_assert (wanted_name != NULL); + g_assert (candidate_name != NULL); + + if (mono_trace_is_traced (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY)) { + char * s = mono_stringify_assembly_name (wanted_name); + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate: wanted = %s\n", s); + g_free (s); + s = mono_stringify_assembly_name (candidate_name); + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate: candidate = %s\n", s); + g_free (s); + } + + + /* Wanted name has no token, not strongly named: always matches. */ + if (0 == wanted_name->public_key_token [0]) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate: wanted has no token, returning TRUE\n"); + return TRUE; + } + + /* Candidate name has no token, not strongly named: never matches */ + if (0 == candidate_name->public_key_token [0]) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate: candidate has no token, returning FALSE\n"); + return FALSE; + } + + return exact_sn_match (wanted_name, candidate_name) || + framework_assembly_sn_match (wanted_name, candidate_name); +} + +gboolean +exact_sn_match (MonoAssemblyName *wanted_name, MonoAssemblyName *candidate_name) +{ + gboolean result = mono_assembly_names_equal (wanted_name, candidate_name); + + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate: candidate and wanted names %s\n", + result ? "match, returning TRUE" : "don't match, returning FALSE"); + return result; + +} + +gboolean +framework_assembly_sn_match (MonoAssemblyName *wanted_name, MonoAssemblyName *candidate_name) +{ +#ifndef DISABLE_DESKTOP_LOADER + const AssemblyVersionMap *vmap = (AssemblyVersionMap *)g_hash_table_lookup (assembly_remapping_table, wanted_name->name); + 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 = 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 = 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; + } + } +#endif + return FALSE; +} + +MonoAssembly* +mono_assembly_load_full_nosearch (MonoAssemblyName *aname, + const char *basedir, + MonoImageOpenStatus *status, + gboolean refonly) +{ + MonoAssembly *result; + char *fullpath, *filename; + MonoAssemblyName maped_aname; + MonoAssemblyName maped_name_pp; + int ext_index; + const char *ext; + int len; + + aname = mono_assembly_remap_version (aname, &maped_aname); + + /* Reflection only assemblies don't get assembly binding */ + if (!refonly) + aname = mono_assembly_apply_binding (aname, &maped_name_pp); + + result = mono_assembly_loaded_full (aname, refonly); + if (result) + return result; + + result = refonly ? invoke_assembly_refonly_preload_hook (aname, assemblies_path) : invoke_assembly_preload_hook (aname, assemblies_path); + if (result) { + result->in_gac = FALSE; + return result; + } + + /* Currently we retrieve the loaded corlib for reflection + * only requests, like a common reflection only assembly + */ + if (strcmp (aname->name, "mscorlib") == 0 || strcmp (aname->name, "mscorlib.dll") == 0) { + return mono_assembly_load_corlib (mono_get_runtime_info (), status); + } + + MonoAssemblyCandidatePredicate predicate = NULL; + void* predicate_ud = NULL; +#if !defined(DISABLE_DESKTOP_LOADER) + if (G_LIKELY (mono_loader_get_strict_strong_names ())) { + predicate = &mono_assembly_candidate_predicate_sn_same_name; + predicate_ud = aname; + } +#endif + + len = strlen (aname->name); + for (ext_index = 0; ext_index < 2; ext_index ++) { + ext = ext_index == 0 ? ".dll" : ".exe"; + if (len > 4 && (!strcmp (aname->name + len - 4, ".dll") || !strcmp (aname->name + len - 4, ".exe"))) { + filename = g_strdup (aname->name); + /* Don't try appending .dll/.exe if it already has one of those extensions */ + ext_index++; + } else { + filename = g_strconcat (aname->name, ext, NULL); + } + + result = mono_assembly_load_from_gac (aname, filename, status, refonly); + if (result) { + g_free (filename); + return result; + } + + if (basedir) { + fullpath = g_build_filename (basedir, filename, NULL); + result = mono_assembly_open_predicate (fullpath, refonly, FALSE, predicate, predicate_ud, status); + g_free (fullpath); + if (result) { + result->in_gac = FALSE; + g_free (filename); + return result; + } + } + + result = load_in_path (filename, default_path, status, refonly, predicate, predicate_ud); + if (result) + result->in_gac = FALSE; + g_free (filename); + if (result) + return result; + } + + return result; +} + +MonoAssembly* +mono_assembly_load_full_internal (MonoAssemblyName *aname, MonoAssembly *requesting, const char *basedir, MonoImageOpenStatus *status, gboolean refonly) +{ + MonoAssembly *result = mono_assembly_load_full_nosearch (aname, basedir, status, refonly); + + if (!result) { + /* Try a postload search hook */ + result = mono_assembly_invoke_search_hook_internal (aname, requesting, refonly, TRUE); + result = prevent_reference_assembly_from_running (result, refonly); + } + return result; +} + +/** + * mono_assembly_load_full: + * \param aname A MonoAssemblyName with the assembly name to load. + * \param basedir A directory to look up the assembly at. + * \param status a pointer to a MonoImageOpenStatus to return the status of the load operation + * \param refonly Whether this assembly is being opened in "reflection-only" mode. + * + * Loads the assembly referenced by \p aname, if the value of \p basedir is not NULL, it + * attempts to load the assembly from that directory before probing the standard locations. + * + * If the assembly is being opened in reflection-only mode (\p refonly set to TRUE) then no + * assembly binding takes place. + * + * \returns the assembly referenced by \p aname loaded or NULL on error. On error the + * value pointed by \p status is updated with an error code. + */ +MonoAssembly* +mono_assembly_load_full (MonoAssemblyName *aname, const char *basedir, MonoImageOpenStatus *status, gboolean refonly) +{ + return mono_assembly_load_full_internal (aname, NULL, basedir, status, refonly); +} + +/** + * mono_assembly_load: + * \param aname A MonoAssemblyName with the assembly name to load. + * \param basedir A directory to look up the assembly at. + * \param status a pointer to a MonoImageOpenStatus to return the status of the load operation + * + * Loads the assembly referenced by \p aname, if the value of \p basedir is not NULL, it + * attempts to load the assembly from that directory before probing the standard locations. + * + * \returns the assembly referenced by \p aname loaded or NULL on error. On error the + * value pointed by \p status is updated with an error code. + */ +MonoAssembly* +mono_assembly_load (MonoAssemblyName *aname, const char *basedir, MonoImageOpenStatus *status) +{ + return mono_assembly_load_full_internal (aname, NULL, basedir, status, FALSE); +} + +/** + * mono_assembly_loaded_full: + * \param aname an assembly to look for. + * \param refonly Whether this assembly is being opened in "reflection-only" mode. + * + * This is used to determine if the specified assembly has been loaded + * \returns NULL If the given \p aname assembly has not been loaded, or a pointer to + * a \c MonoAssembly that matches the \c MonoAssemblyName specified. + */ +MonoAssembly* +mono_assembly_loaded_full (MonoAssemblyName *aname, gboolean refonly) +{ + MonoAssembly *res; + MonoAssemblyName maped_aname; + + aname = mono_assembly_remap_version (aname, &maped_aname); + + res = mono_assembly_invoke_search_hook_internal (aname, NULL, refonly, FALSE); + + return res; +} + +/** + * mono_assembly_loaded: + * \param aname an assembly to look for. + * + * This is used to determine if the specified assembly has been loaded + + * \returns NULL If the given \p aname assembly has not been loaded, or a pointer to + * a \c MonoAssembly that matches the \c MonoAssemblyName specified. + */ +MonoAssembly* +mono_assembly_loaded (MonoAssemblyName *aname) +{ + return mono_assembly_loaded_full (aname, FALSE); +} + +void +mono_assembly_release_gc_roots (MonoAssembly *assembly) +{ + if (assembly == NULL || assembly == REFERENCE_MISSING) + return; + + if (assembly_is_dynamic (assembly)) { + int i; + MonoDynamicImage *dynimg = (MonoDynamicImage *)assembly->image; + for (i = 0; i < dynimg->image.module_count; ++i) + mono_dynamic_image_release_gc_roots ((MonoDynamicImage *)dynimg->image.modules [i]); + mono_dynamic_image_release_gc_roots (dynimg); + } +} + +/* + * Returns whether mono_assembly_close_finish() must be called as + * well. See comment for mono_image_close_except_pools() for why we + * unload in two steps. + */ +gboolean +mono_assembly_close_except_image_pools (MonoAssembly *assembly) +{ + GSList *tmp; + g_return_val_if_fail (assembly != NULL, FALSE); + + if (assembly == REFERENCE_MISSING) + return FALSE; + + /* Might be 0 already */ + if (mono_atomic_dec_i32 (&assembly->ref_count) > 0) + return FALSE; + + MONO_PROFILER_RAISE (assembly_unloading, (assembly)); + + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Unloading assembly %s [%p].", assembly->aname.name, assembly); + + mono_debug_close_image (assembly->image); + + mono_assemblies_lock (); + loaded_assemblies = g_list_remove (loaded_assemblies, assembly); + mono_assemblies_unlock (); + + assembly->image->assembly = NULL; + + if (!mono_image_close_except_pools (assembly->image)) + assembly->image = NULL; + + for (tmp = assembly->friend_assembly_names; tmp; tmp = tmp->next) { + MonoAssemblyName *fname = (MonoAssemblyName *)tmp->data; + mono_assembly_name_free (fname); + g_free (fname); + } + g_slist_free (assembly->friend_assembly_names); + g_free (assembly->basedir); + + MONO_PROFILER_RAISE (assembly_unloaded, (assembly)); + + return TRUE; +} + +void +mono_assembly_close_finish (MonoAssembly *assembly) +{ + g_assert (assembly && assembly != REFERENCE_MISSING); + + if (assembly->image) + mono_image_close_finish (assembly->image); + + if (assembly_is_dynamic (assembly)) { + g_free ((char*)assembly->aname.culture); + } else { + g_free (assembly); + } +} + +/** + * mono_assembly_close: + * \param assembly the assembly to release. + * + * This method releases a reference to the \p assembly. The assembly is + * only released when all the outstanding references to it are released. + */ +void +mono_assembly_close (MonoAssembly *assembly) +{ + if (mono_assembly_close_except_image_pools (assembly)) + mono_assembly_close_finish (assembly); +} + +/** + * mono_assembly_load_module: + */ +MonoImage* +mono_assembly_load_module (MonoAssembly *assembly, guint32 idx) +{ + MonoError error; + MonoImage *result = mono_assembly_load_module_checked (assembly, idx, &error); + mono_error_assert_ok (&error); + return result; +} + +MONO_API MonoImage* +mono_assembly_load_module_checked (MonoAssembly *assembly, uint32_t idx, MonoError *error) +{ + return mono_image_load_file_for_image_checked (assembly->image, idx, error); +} + + +/** + * mono_assembly_foreach: + * \param func function to invoke for each assembly loaded + * \param user_data data passed to the callback + * + * Invokes the provided \p func callback for each assembly loaded into + * the runtime. The first parameter passed to the callback is the + * \c MonoAssembly*, and the second parameter is the \p user_data. + * + * This is done for all assemblies loaded in the runtime, not just + * those loaded in the current application domain. + */ +void +mono_assembly_foreach (GFunc func, gpointer user_data) +{ + GList *copy; + + /* + * We make a copy of the list to avoid calling the callback inside the + * lock, which could lead to deadlocks. + */ + mono_assemblies_lock (); + copy = g_list_copy (loaded_assemblies); + mono_assemblies_unlock (); + + g_list_foreach (loaded_assemblies, func, user_data); + + g_list_free (copy); +} + +/** + * mono_assemblies_cleanup: + * + * Free all resources used by this module. + */ +void +mono_assemblies_cleanup (void) +{ + GSList *l; + + mono_os_mutex_destroy (&assemblies_mutex); + mono_os_mutex_destroy (&assembly_binding_mutex); + + for (l = loaded_assembly_bindings; l; l = l->next) { + MonoAssemblyBindingInfo *info = (MonoAssemblyBindingInfo *)l->data; + + mono_assembly_binding_info_free (info); + g_free (info); + } + g_slist_free (loaded_assembly_bindings); + + free_assembly_load_hooks (); + free_assembly_search_hooks (); + free_assembly_preload_hooks (); +} + +/*LOCKING takes the assembly_binding lock*/ +void +mono_assembly_cleanup_domain_bindings (guint32 domain_id) +{ + GSList **iter; + + mono_assembly_binding_lock (); + iter = &loaded_assembly_bindings; + while (*iter) { + GSList *l = *iter; + MonoAssemblyBindingInfo *info = (MonoAssemblyBindingInfo *)l->data; + + if (info->domain_id == domain_id) { + *iter = l->next; + mono_assembly_binding_info_free (info); + g_free (info); + g_slist_free_1 (l); + } else { + iter = &l->next; + } + } + mono_assembly_binding_unlock (); +} + +/* + * Holds the assembly of the application, for + * System.Diagnostics.Process::MainModule + */ +static MonoAssembly *main_assembly=NULL; + +/** + * mono_assembly_set_main: + */ +void +mono_assembly_set_main (MonoAssembly *assembly) +{ + main_assembly = assembly; +} + +/** + * mono_assembly_get_main: + * + * Returns: the assembly for the application, the first assembly that is loaded by the VM + */ +MonoAssembly * +mono_assembly_get_main (void) +{ + return (main_assembly); +} + +/** + * mono_assembly_get_image: + * \param assembly The assembly to retrieve the image from + * + * \returns the \c MonoImage associated with this assembly. + */ +MonoImage* +mono_assembly_get_image (MonoAssembly *assembly) +{ + return assembly->image; +} + +/** + * mono_assembly_get_name: + * \param assembly The assembly to retrieve the name from + * + * The returned name's lifetime is the same as \p assembly's. + * + * \returns the \c MonoAssemblyName associated with this assembly. + */ +MonoAssemblyName * +mono_assembly_get_name (MonoAssembly *assembly) +{ + return &assembly->aname; +} + +/** + * mono_register_bundled_assemblies: + */ +void +mono_register_bundled_assemblies (const MonoBundledAssembly **assemblies) +{ + bundles = assemblies; +} + +#define MONO_DECLSEC_FORMAT_10 0x3C +#define MONO_DECLSEC_FORMAT_20 0x2E +#define MONO_DECLSEC_FIELD 0x53 +#define MONO_DECLSEC_PROPERTY 0x54 + +#define SKIP_VISIBILITY_XML_ATTRIBUTE ("\"SkipVerification\"") +#define SKIP_VISIBILITY_ATTRIBUTE_NAME ("System.Security.Permissions.SecurityPermissionAttribute") +#define SKIP_VISIBILITY_ATTRIBUTE_SIZE (sizeof (SKIP_VISIBILITY_ATTRIBUTE_NAME) - 1) +#define SKIP_VISIBILITY_PROPERTY_NAME ("SkipVerification") +#define SKIP_VISIBILITY_PROPERTY_SIZE (sizeof (SKIP_VISIBILITY_PROPERTY_NAME) - 1) + +static gboolean +mono_assembly_try_decode_skip_verification_param (const char *p, const char **resp, gboolean *abort_decoding) +{ + int len; + switch (*p++) { + case MONO_DECLSEC_PROPERTY: + break; + case MONO_DECLSEC_FIELD: + default: + *abort_decoding = TRUE; + return FALSE; + break; + } + + if (*p++ != MONO_TYPE_BOOLEAN) { + *abort_decoding = TRUE; + return FALSE; + } + + /* property name length */ + len = mono_metadata_decode_value (p, &p); + + if (len >= SKIP_VISIBILITY_PROPERTY_SIZE && !memcmp (p, SKIP_VISIBILITY_PROPERTY_NAME, SKIP_VISIBILITY_PROPERTY_SIZE)) { + p += len; + return *p; + } + p += len + 1; + + *resp = p; + return FALSE; +} + +static gboolean +mono_assembly_try_decode_skip_verification (const char *p, const char *endn) +{ + int i, j, num, len, params_len; + + if (*p == MONO_DECLSEC_FORMAT_10) { + gsize read, written; + char *res = g_convert (p, endn - p, "UTF-8", "UTF-16LE", &read, &written, NULL); + if (res) { + gboolean found = strstr (res, SKIP_VISIBILITY_XML_ATTRIBUTE) != NULL; + g_free (res); + return found; + } + return FALSE; + } + if (*p++ != MONO_DECLSEC_FORMAT_20) + return FALSE; + + /* number of encoded permission attributes */ + num = mono_metadata_decode_value (p, &p); + for (i = 0; i < num; ++i) { + gboolean is_valid = FALSE; + gboolean abort_decoding = FALSE; + + /* attribute name length */ + len = mono_metadata_decode_value (p, &p); + + /* We don't really need to fully decode the type. Comparing the name is enough */ + is_valid = len >= SKIP_VISIBILITY_ATTRIBUTE_SIZE && !memcmp (p, SKIP_VISIBILITY_ATTRIBUTE_NAME, SKIP_VISIBILITY_ATTRIBUTE_SIZE); + + p += len; + + /*size of the params table*/ + params_len = mono_metadata_decode_value (p, &p); + if (is_valid) { + const char *params_end = p + params_len; + + /* number of parameters */ + len = mono_metadata_decode_value (p, &p); + + for (j = 0; j < len; ++j) { + if (mono_assembly_try_decode_skip_verification_param (p, &p, &abort_decoding)) + return TRUE; + if (abort_decoding) + break; + } + p = params_end; + } else { + p += params_len; + } + } + + return FALSE; +} + + +gboolean +mono_assembly_has_skip_verification (MonoAssembly *assembly) +{ + MonoTableInfo *t; + guint32 cols [MONO_DECL_SECURITY_SIZE]; + const char *blob; + int i, len; + + if (MONO_SECMAN_FLAG_INIT (assembly->skipverification)) + return MONO_SECMAN_FLAG_GET_VALUE (assembly->skipverification); + + t = &assembly->image->tables [MONO_TABLE_DECLSECURITY]; + + for (i = 0; i < t->rows; ++i) { + mono_metadata_decode_row (t, i, cols, MONO_DECL_SECURITY_SIZE); + if ((cols [MONO_DECL_SECURITY_PARENT] & MONO_HAS_DECL_SECURITY_MASK) != MONO_HAS_DECL_SECURITY_ASSEMBLY) + continue; + if (cols [MONO_DECL_SECURITY_ACTION] != SECURITY_ACTION_REQMIN) + continue; + + blob = mono_metadata_blob_heap (assembly->image, cols [MONO_DECL_SECURITY_PERMISSIONSET]); + len = mono_metadata_decode_blob_size (blob, &blob); + if (!len) + continue; + + if (mono_assembly_try_decode_skip_verification (blob, blob + len)) { + MONO_SECMAN_FLAG_SET_VALUE (assembly->skipverification, TRUE); + return TRUE; + } + } + + MONO_SECMAN_FLAG_SET_VALUE (assembly->skipverification, FALSE); + return FALSE; +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/assembly.h b/unity-2019.4.24f1-mbe/mono/metadata/assembly.h new file mode 100644 index 000000000..a34b5ed6c --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/assembly.h @@ -0,0 +1,121 @@ +/** + * \file + */ + +#ifndef _MONONET_METADATA_ASSEMBLY_H_ +#define _MONONET_METADATA_ASSEMBLY_H_ + +#include +#include + +MONO_BEGIN_DECLS + +MONO_API void mono_assemblies_init (void); +MONO_API void mono_assemblies_cleanup (void); +MONO_RT_EXTERNAL_ONLY +MONO_API MonoAssembly *mono_assembly_open (const char *filename, + MonoImageOpenStatus *status); +MONO_RT_EXTERNAL_ONLY +MONO_API MonoAssembly *mono_assembly_open_full (const char *filename, + MonoImageOpenStatus *status, + mono_bool refonly); +MONO_API MonoAssembly* mono_assembly_load (MonoAssemblyName *aname, + const char *basedir, + MonoImageOpenStatus *status); +MONO_API MonoAssembly* mono_assembly_load_full (MonoAssemblyName *aname, + const char *basedir, + MonoImageOpenStatus *status, + mono_bool refonly); +MONO_API MonoAssembly* mono_assembly_load_from (MonoImage *image, const char *fname, + MonoImageOpenStatus *status); +MONO_API MonoAssembly* mono_assembly_load_from_full (MonoImage *image, const char *fname, + MonoImageOpenStatus *status, + mono_bool refonly); + +MONO_API MonoAssembly* mono_assembly_load_with_partial_name (const char *name, MonoImageOpenStatus *status); + +MONO_API MonoAssembly* mono_assembly_loaded (MonoAssemblyName *aname); +MONO_API MonoAssembly* mono_assembly_loaded_full (MonoAssemblyName *aname, mono_bool refonly); +MONO_API void mono_assembly_get_assemblyref (MonoImage *image, int index, MonoAssemblyName *aname); +MONO_API void mono_assembly_load_reference (MonoImage *image, int index); +MONO_API void mono_assembly_load_references (MonoImage *image, MonoImageOpenStatus *status); +MONO_RT_EXTERNAL_ONLY MONO_API MonoImage* mono_assembly_load_module (MonoAssembly *assembly, uint32_t idx); +MONO_API void mono_assembly_close (MonoAssembly *assembly); +MONO_API void mono_assembly_setrootdir (const char *root_dir); +MONO_API MONO_CONST_RETURN char *mono_assembly_getrootdir (void); +MONO_API char *mono_native_getrootdir (void); +MONO_API void mono_assembly_foreach (MonoFunc func, void* user_data); +MONO_API void mono_assembly_set_main (MonoAssembly *assembly); +MONO_API MonoAssembly *mono_assembly_get_main (void); +MONO_API MonoImage *mono_assembly_get_image (MonoAssembly *assembly); +MONO_API MonoAssemblyName *mono_assembly_get_name (MonoAssembly *assembly); +MONO_API mono_bool mono_assembly_fill_assembly_name (MonoImage *image, MonoAssemblyName *aname); +MONO_API mono_bool mono_assembly_names_equal (MonoAssemblyName *l, MonoAssemblyName *r); +MONO_API char* mono_stringify_assembly_name (MonoAssemblyName *aname); + +/* Installs a function which is called each time a new assembly is loaded. */ +typedef void (*MonoAssemblyLoadFunc) (MonoAssembly *assembly, void* user_data); +MONO_API void mono_install_assembly_load_hook (MonoAssemblyLoadFunc func, void* user_data); + +/* + * Installs a new function which is used to search the list of loaded + * assemblies for a given assembly name. + */ +typedef MonoAssembly *(*MonoAssemblySearchFunc) (MonoAssemblyName *aname, void* user_data); +MONO_API void mono_install_assembly_search_hook (MonoAssemblySearchFunc func, void* user_data); +MONO_API void mono_install_assembly_refonly_search_hook (MonoAssemblySearchFunc func, void* user_data); + +MONO_API MonoAssembly* mono_assembly_invoke_search_hook (MonoAssemblyName *aname); + +/* + * Installs a new search function which is used as a last resort when loading + * an assembly fails. This could invoke AssemblyResolve events. + */ +MONO_API void +mono_install_assembly_postload_search_hook (MonoAssemblySearchFunc func, void* user_data); + +MONO_API void +mono_install_assembly_postload_refonly_search_hook (MonoAssemblySearchFunc func, void* user_data); + + +/* Installs a function which is called before a new assembly is loaded + * The hook are invoked from last hooked to first. If any of them returns + * a non-null value, that will be the value returned in mono_assembly_load */ +typedef MonoAssembly * (*MonoAssemblyPreLoadFunc) (MonoAssemblyName *aname, + char **assemblies_path, + void* user_data); + +MONO_API void mono_install_assembly_preload_hook (MonoAssemblyPreLoadFunc func, + void* user_data); +MONO_API void mono_install_assembly_refonly_preload_hook (MonoAssemblyPreLoadFunc func, + void* user_data); + +MONO_API void mono_assembly_invoke_load_hook (MonoAssembly *ass); + +MONO_API MonoAssemblyName* mono_assembly_name_new (const char *name); +MONO_API const char* mono_assembly_name_get_name (MonoAssemblyName *aname); +MONO_API const char* mono_assembly_name_get_culture (MonoAssemblyName *aname); +MONO_API uint16_t mono_assembly_name_get_version (MonoAssemblyName *aname, + uint16_t *minor, uint16_t *build, uint16_t *revision); +MONO_API mono_byte* mono_assembly_name_get_pubkeytoken (MonoAssemblyName *aname); +MONO_API void mono_assembly_name_free (MonoAssemblyName *aname); + +typedef struct { + const char *name; + const unsigned char *data; + const unsigned int size; +} MonoBundledAssembly; + +MONO_API void mono_register_bundled_assemblies (const MonoBundledAssembly **assemblies); +MONO_API void mono_register_config_for_assembly (const char* assembly_name, const char* config_xml); +MONO_API void mono_register_symfile_for_assembly (const char* assembly_name, const mono_byte *raw_contents, int size); +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/unity-2019.4.24f1-mbe/mono/metadata/attach.c b/unity-2019.4.24f1-mbe/mono/metadata/attach.c new file mode 100644 index 000000000..942209fb1 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/attach.c @@ -0,0 +1,625 @@ +/** + * \file + * Support for attaching to the runtime from other processes. + * + * Author: + * Zoltan Varga (vargaz@gmail.com) + * + * Copyright 2007-2009 Novell, Inc (http://www.novell.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#include + +#ifdef HOST_WIN32 +#define DISABLE_ATTACH +#endif +#ifndef DISABLE_ATTACH + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "attach.h" + +#include + +/* + * This module enables other processes to attach to a running mono process and + * load agent assemblies. + * Communication is done through a UNIX Domain Socket located at + * /tmp/mono-/.mono-. + * We use a simplified version of the .net remoting protocol. + * To increase security, and to avoid spinning up a listener thread on startup, + * we follow the java implementation, and only start up the attach mechanism + * when we receive a QUIT signal and there is a file named + * '.mono_attach_pid' in /tmp. + * + * SECURITY: + * - This module allows loading of arbitrary code into a running mono runtime, so + * it is security critical. + * - Security is based on controlling access to the unix file to which the unix + * domain socket is bound. Permissions/ownership are set such that only the owner + * of the process can access the socket. + * - As an additional measure, the socket is only created when the process receives + * a SIGQUIT signal, which only its owner/root can send. + * - The socket is kept in a directory whose ownership is checked before creating + * the socket. This could allow an attacker a kind of DOS attack by creating the + * directory with the wrong permissions/ownership. However, the only thing such + * an attacker could prevent is the attaching of agents to the mono runtime. + */ + +typedef struct { + gboolean enabled; +} AgentConfig; + +typedef struct { + int bytes_sent; +} AgentStats; + +/*******************************************************************/ +/* Remoting Protocol type definitions from [MS-NRBF] and [MS-NRTP] */ +/*******************************************************************/ + +typedef enum { + PRIM_TYPE_INT32 = 8, + PRIM_TYPE_INT64 = 9, + PRIM_TYPE_NULL = 17, + PRIM_TYPE_STRING = 18 +} PrimitiveType; + +static AgentConfig config; + +static int listen_fd, conn_fd; + +static char *ipc_filename; + +static char *server_uri; + +static MonoThreadHandle *receiver_thread_handle; + +static gboolean stop_receiver_thread; + +static gboolean needs_to_start, started; + +static void transport_connect (void); + +static gsize WINAPI receiver_thread (void *arg); + +static void transport_start_receive (void); + +/* + * Functions to decode protocol data + */ +static inline int +decode_byte (guint8 *buf, guint8 **endbuf, guint8 *limit) +{ + *endbuf = buf + 1; + g_assert (*endbuf <= limit); + return buf [0]; +} + +static inline int +decode_int (guint8 *buf, guint8 **endbuf, guint8 *limit) +{ + *endbuf = buf + 4; + g_assert (*endbuf <= limit); + + return (((int)buf [0]) << 0) | (((int)buf [1]) << 8) | (((int)buf [2]) << 16) | (((int)buf [3]) << 24); +} + +static char* +decode_string_value (guint8 *buf, guint8 **endbuf, guint8 *limit) +{ + int type; + gint32 length; + guint8 *p = buf; + char *s; + + type = decode_byte (p, &p, limit); + if (type == PRIM_TYPE_NULL) { + *endbuf = p; + return NULL; + } + g_assert (type == PRIM_TYPE_STRING); + + length = 0; + while (TRUE) { + guint8 b = decode_byte (p, &p, limit); + + length <<= 8; + length += b; + if (b <= 0x7f) + break; + } + + g_assert (length < (1 << 16)); + + s = (char *)g_malloc (length + 1); + + g_assert (p + length <= limit); + memcpy (s, p, length); + s [length] = '\0'; + p += length; + + *endbuf = p; + + return s; +} + +/********************************/ +/* AGENT IMPLEMENTATION */ +/********************************/ + +void +mono_attach_parse_options (char *options) +{ + if (!options) + return; + if (!strcmp (options, "disable")) + config.enabled = FALSE; +} + +void +mono_attach_init (void) +{ + config.enabled = TRUE; +} + +/** + * mono_attach_start: + * + * Start the attach mechanism if needed. This is called from a signal handler so it must be signal safe. + * + * Returns: whenever it was started. + */ +gboolean +mono_attach_start (void) +{ + char path [256]; + int fd; + + if (started) + return FALSE; + + /* Check for the existence of the trigger file */ + + /* + * We don't do anything with this file, and the only thing an attacker can do + * by creating it is to enable the attach mechanism if the process receives a + * SIGQUIT signal, which can only be sent by the owner/root. + */ + snprintf (path, sizeof (path), "/tmp/.mono_attach_pid%"PRIdMAX"", (intmax_t) getpid ()); + fd = open (path, O_RDONLY); + if (fd == -1) + return FALSE; + close (fd); + + if (!config.enabled) + /* Act like we started */ + return TRUE; + + if (started) + return FALSE; + + /* + * Our startup includes non signal-safe code, so ask the finalizer thread to + * do the actual startup. + */ + needs_to_start = TRUE; + mono_gc_finalize_notify (); + + return TRUE; +} + +/* Called by the finalizer thread when it is woken up */ +void +mono_attach_maybe_start (void) +{ + if (!needs_to_start) + return; + + needs_to_start = FALSE; + if (!started) { + transport_start_receive (); + + started = TRUE; + } +} + +void +mono_attach_cleanup (void) +{ + if (listen_fd) + close (listen_fd); + if (ipc_filename) + unlink (ipc_filename); + + stop_receiver_thread = TRUE; + if (conn_fd) + /* This will cause receiver_thread () to break out of the read () call */ + close (conn_fd); + + /* Wait for the receiver thread to exit */ + if (receiver_thread_handle) + mono_thread_info_wait_one_handle (receiver_thread_handle, 0, FALSE); +} + +static int +mono_attach_load_agent (MonoDomain *domain, char *agent, char *args, MonoObject **exc) +{ + MonoError error; + MonoAssembly *agent_assembly; + MonoImage *image; + MonoMethod *method; + guint32 entry; + MonoArray *main_args; + gpointer pa [1]; + MonoImageOpenStatus open_status; + + agent_assembly = mono_assembly_open_predicate (agent, FALSE, FALSE, NULL, NULL, &open_status); + if (!agent_assembly) { + fprintf (stderr, "Cannot open agent assembly '%s': %s.\n", agent, mono_image_strerror (open_status)); + g_free (agent); + return 2; + } + + /* + * Can't use mono_jit_exec (), as it sets things which might confuse the + * real Main method. + */ + image = mono_assembly_get_image (agent_assembly); + entry = mono_image_get_entry_point (image); + if (!entry) { + g_print ("Assembly '%s' doesn't have an entry point.\n", mono_image_get_filename (image)); + g_free (agent); + return 1; + } + + method = mono_get_method_checked (image, entry, NULL, NULL, &error); + if (method == NULL){ + g_print ("The entry point method of assembly '%s' could not be loaded due to %s\n", agent, mono_error_get_message (&error)); + mono_error_cleanup (&error); + g_free (agent); + return 1; + } + + + main_args = (MonoArray*)mono_array_new_checked (domain, mono_defaults.string_class, (args == NULL) ? 0 : 1, &error); + if (main_args == NULL) { + g_print ("Could not allocate main method args due to %s\n", mono_error_get_message (&error)); + mono_error_cleanup (&error); + g_free (agent); + return 1; + } + + if (args) { + MonoString *args_str = mono_string_new_checked (domain, args, &error); + if (!is_ok (&error)) { + g_print ("Could not allocate main method arg string due to %s\n", mono_error_get_message (&error)); + mono_error_cleanup (&error); + g_free (agent); + return 1; + } + mono_array_set (main_args, MonoString*, 0, args_str); + } + + + pa [0] = main_args; + mono_runtime_try_invoke (method, NULL, pa, exc, &error); + if (!is_ok (&error)) { + g_print ("The entry point method of assembly '%s' could not be executed due to %s\n", agent, mono_error_get_message (&error)); + mono_error_cleanup (&error); + g_free (agent); + return 1; + } + + g_free (agent); + + return 0; +} + +/* + * ipc_connect: + * + * Create a UNIX domain socket and bind it to a file in /tmp. + * + * SECURITY: This routine is _very_ security critical since we depend on the UNIX + * permissions system to prevent attackers from connecting to the socket. + */ +static void +ipc_connect (void) +{ + struct sockaddr_un name; + int sock, res; + size_t size; + char *filename, *directory; + struct stat stat; + struct passwd pwbuf; + char buf [1024]; + struct passwd *pw; + + if (getuid () != geteuid ()) { + fprintf (stderr, "attach: disabled listening on an IPC socket when running in setuid mode.\n"); + return; + } + + /* Create the socket. */ + sock = socket (PF_UNIX, SOCK_STREAM, 0); + if (sock < 0) { + perror ("attach: failed to create IPC socket"); + return; + } + + /* + * For security reasons, create a directory to hold the listening socket, + * since there is a race between bind () and chmod () below. + */ + /* FIXME: Use TMP ? */ + pw = NULL; +#ifdef HAVE_GETPWUID_R + res = getpwuid_r (getuid (), &pwbuf, buf, sizeof (buf), &pw); +#else + pw = getpwuid(getuid ()); + res = pw != NULL ? 0 : 1; +#endif + if (res != 0) { + fprintf (stderr, "attach: getpwuid_r () failed.\n"); + return; + } + g_assert (pw); + directory = g_strdup_printf ("/tmp/mono-%s", pw->pw_name); + res = mkdir (directory, S_IRUSR | S_IWUSR | S_IXUSR); + if (res != 0) { + if (errno == EEXIST) { + /* Check type and permissions */ + res = lstat (directory, &stat); + if (res != 0) { + perror ("attach: lstat () failed"); + return; + } + if (!S_ISDIR (stat.st_mode)) { + fprintf (stderr, "attach: path '%s' is not a directory.\n", directory); + return; + } + if (stat.st_uid != getuid ()) { + fprintf (stderr, "attach: directory '%s' is not owned by the current user.\n", directory); + return; + } + if ((stat.st_mode & S_IRWXG) != 0 || (stat.st_mode & S_IRWXO) || ((stat.st_mode & S_IRWXU) != (S_IRUSR | S_IWUSR | S_IXUSR))) { + fprintf (stderr, "attach: directory '%s' should have protection 0700.\n", directory); + return; + } + } else { + perror ("attach: mkdir () failed"); + return; + } + } + + filename = g_strdup_printf ("%s/.mono-%"PRIdMAX"", directory, (intmax_t) getpid ()); + unlink (filename); + + /* Bind a name to the socket. */ + name.sun_family = AF_UNIX; + strcpy (name.sun_path, filename); + + size = (offsetof (struct sockaddr_un, sun_path) + + strlen (name.sun_path) + 1); + + if (bind (sock, (struct sockaddr *) &name, size) < 0) { + fprintf (stderr, "attach: failed to bind IPC socket '%s': %s\n", filename, strerror (errno)); + close (sock); + return; + } + + /* Set permissions */ + res = chmod (filename, S_IRUSR | S_IWUSR); + if (res != 0) { + perror ("attach: failed to set permissions on IPC socket"); + close (sock); + unlink (filename); + return; + } + + res = listen (sock, 16); + if (res != 0) { + fprintf (stderr, "attach: listen () failed: %s\n", strerror (errno)); + exit (1); + } + + listen_fd = sock; + + ipc_filename = g_strdup (filename); + + server_uri = g_strdup_printf ("unix://%s/.mono-%"PRIdMAX"?/vm", directory, (intmax_t) getpid ()); + + g_free (filename); + g_free (directory); +} + +static void +transport_connect (void) +{ + ipc_connect (); +} + +#if 0 + +static void +transport_send (int fd, guint8 *data, int len) +{ + int res; + + stats.bytes_sent += len; + //printf ("X: %d\n", stats.bytes_sent); + + res = write (fd, data, len); + if (res != len) { + /* FIXME: What to do here ? */ + } +} + +#endif + +static void +transport_start_receive (void) +{ + MonoError error; + MonoInternalThread *internal; + + transport_connect (); + + if (!listen_fd) + return; + + internal = mono_thread_create_internal (mono_get_root_domain (), receiver_thread, NULL, MONO_THREAD_CREATE_FLAGS_NONE, &error); + mono_error_assert_ok (&error); + + receiver_thread_handle = mono_threads_open_thread_handle (internal->handle); + g_assert (receiver_thread_handle); +} + +static gsize WINAPI +receiver_thread (void *arg) +{ + MonoError error; + int res, content_len; + guint8 buffer [256]; + guint8 *p, *p_end; + MonoObject *exc; + MonoInternalThread *internal; + + internal = mono_thread_internal_current (); + MonoString *attach_str = mono_string_new_checked (mono_domain_get (), "Attach receiver", &error); + mono_error_assert_ok (&error); + mono_thread_set_name_internal (internal, attach_str, TRUE, FALSE, &error); + mono_error_assert_ok (&error); + /* Ask the runtime to not abort this thread */ + //internal->flags |= MONO_THREAD_FLAG_DONT_MANAGE; + /* Ask the runtime to not wait for this thread */ + internal->state |= ThreadState_Background; + + printf ("attach: Listening on '%s'...\n", server_uri); + + while (TRUE) { + conn_fd = accept (listen_fd, NULL, NULL); + if (conn_fd == -1) + /* Probably closed by mono_attach_cleanup () */ + return 0; + + printf ("attach: Connected.\n"); + + while (TRUE) { + char *cmd, *agent_name, *agent_args; + guint8 *body; + + /* Read Header */ + res = read (conn_fd, buffer, 6); + + if (res == -1 && errno == EINTR) + continue; + + if (res == -1 || stop_receiver_thread) + break; + + if (res != 6) + break; + + if ((strncmp ((char*)buffer, "MONO", 4) != 0) || buffer [4] != 1 || buffer [5] != 0) { + fprintf (stderr, "attach: message from server has unknown header.\n"); + break; + } + + /* Read content length */ + res = read (conn_fd, buffer, 4); + if (res != 4) + break; + + p = buffer; + p_end = p + 8; + + content_len = decode_int (p, &p, p_end); + + /* Read message body */ + body = (guint8 *)g_malloc (content_len); + res = read (conn_fd, body, content_len); + + p = body; + p_end = body + content_len; + + cmd = decode_string_value (p, &p, p_end); + if (cmd == NULL) + break; + g_assert (!strcmp (cmd, "attach")); + + agent_name = decode_string_value (p, &p, p_end); + agent_args = decode_string_value (p, &p, p_end); + + printf ("attach: Loading agent '%s'.\n", agent_name); + mono_attach_load_agent (mono_domain_get (), agent_name, agent_args, &exc); + + g_free (body); + + // FIXME: Send back a result + } + + close (conn_fd); + conn_fd = 0; + + printf ("attach: Disconnected.\n"); + + if (stop_receiver_thread) + break; + } + + return 0; +} + +#else /* DISABLE_ATTACH */ + +void +mono_attach_parse_options (char *options) +{ +} + +void +mono_attach_init (void) +{ +} + +gboolean +mono_attach_start (void) +{ + return FALSE; +} + +void +mono_attach_maybe_start (void) +{ +} + +void +mono_attach_cleanup (void) +{ +} + +#endif /* DISABLE_ATTACH */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/attach.h b/unity-2019.4.24f1-mbe/mono/metadata/attach.h new file mode 100644 index 000000000..ff33b83fa --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/attach.h @@ -0,0 +1,30 @@ +/** + * \file + */ + +#ifndef __MONO_ATTACH_H__ +#define __MONO_ATTACH_H__ + +#include +#include + +G_BEGIN_DECLS + +void +mono_attach_parse_options (char *options); + +void +mono_attach_init (void); + +gboolean +mono_attach_start (void); + +void +mono_attach_maybe_start (void); + +void +mono_attach_cleanup (void); + +G_END_DECLS + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/attrdefs.h b/unity-2019.4.24f1-mbe/mono/metadata/attrdefs.h new file mode 100644 index 000000000..504c6c65f --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/attrdefs.h @@ -0,0 +1,274 @@ +/** + * \file + * This file contains the various definitions for constants + * found on the metadata tables + * + * Author: + * Miguel de Icaza (miguel@ximian.com) + * Paolo Molaro (lupus@ximian.com) + * + * (C) 2001 Ximian, Inc. + * (C) 2006 Novell, Inc. + * + * From the ECMA documentation + */ + +#ifndef _MONO_METADATA_ATTRDEFS_H_ +#define _MONO_METADATA_ATTRDEFS_H_ + +/* + * 23.1.1 Values for AssemblyHashAlgorithm + */ +enum { + MONO_ASSEMBLY_HASH_NONE, + MONO_ASSEMBLY_HASH_MD5 = 0x8003, + MONO_ASSEMBLY_HASH_SHA1 = 0x8004 +}; + +/* + * 23.1.2 AssemblyRefs + */ +enum { + MONO_ASSEMBLYREF_FULL_PUBLIC_KEY = 0x0001, + MONO_ASSEMBLYREF_RETARGETABLE = 0x0100, + MONO_ASSEMBLYREF_JIT_TRACKING = 0x8000, + MONO_ASSEMBLYREF_NO_JIT_OPT = 0x4000 +}; + +/* + * 23.1.4 Flags for Event.EventAttributes + */ +enum { + MONO_EVENT_SPECIALNAME = 0x0200, + MONO_EVENT_RTSPECIALNAME = 0x0400 +}; + +/* + * Field Attributes (23.1.5). + */ +enum { + MONO_FIELD_ATTR_FIELD_ACCESS_MASK = 0x0007, + MONO_FIELD_ATTR_COMPILER_CONTROLLED = 0x0000, + MONO_FIELD_ATTR_PRIVATE = 0x0001, + MONO_FIELD_ATTR_FAM_AND_ASSEM = 0x0002, + MONO_FIELD_ATTR_ASSEMBLY = 0x0003, + MONO_FIELD_ATTR_FAMILY = 0x0004, + MONO_FIELD_ATTR_FAM_OR_ASSEM = 0x0005, + MONO_FIELD_ATTR_PUBLIC = 0x0006, + + MONO_FIELD_ATTR_STATIC = 0x0010, + MONO_FIELD_ATTR_INIT_ONLY = 0x0020, + MONO_FIELD_ATTR_LITERAL = 0x0040, + MONO_FIELD_ATTR_NOT_SERIALIZED = 0x0080, + MONO_FIELD_ATTR_SPECIAL_NAME = 0x0200, + MONO_FIELD_ATTR_PINVOKE_IMPL = 0x2000, + +/* For runtime use only */ + MONO_FIELD_ATTR_RESERVED_MASK = 0x9500, + MONO_FIELD_ATTR_RT_SPECIAL_NAME = 0x0400, + MONO_FIELD_ATTR_HAS_MARSHAL = 0x1000, + MONO_FIELD_ATTR_HAS_DEFAULT = 0x8000, + MONO_FIELD_ATTR_HAS_RVA = 0x0100 +}; + +/* + * 23.1.6 Flags for FileAttributes + */ +enum { + MONO_FILE_HAS_METADATA = 0, + MONO_FILE_HAS_NO_METADATA = 1 +}; + +/* + * 23.1.7 Flags for generic parameters + */ +enum { + MONO_GEN_PARAM_VARIANCE_MASK = 0x0003, + MONO_GEN_PARAM_NON_VARIANT = 0x0000, + MONO_GEN_PARAM_VARIANT = 0x0001, + MONO_GEN_PARAM_COVARIANT = 0x0002, + MONO_GEN_PARAM_CONSTRAINT_MASK = 0x001c, + MONO_GEN_PARAM_CONSTRAINT_CLASS = 0x0004, + MONO_GEN_PARAM_CONSTRAINT_VTYPE = 0x0008, + MONO_GEN_PARAM_CONSTRAINT_DCTOR = 0x0010 +}; + +/* + * 23.1.8 Flags for ImplMap [PInvokeAttributes] + */ +enum { + MONO_PINVOKE_NO_MANGLE = 0x0001, + MONO_PINVOKE_CHAR_SET_MASK = 0x0006, + MONO_PINVOKE_CHAR_SET_NOT_SPEC = 0x0000, + MONO_PINVOKE_CHAR_SET_ANSI = 0x0002, + MONO_PINVOKE_CHAR_SET_UNICODE = 0x0004, + MONO_PINVOKE_CHAR_SET_AUTO = 0x0006, + MONO_PINVOKE_BEST_FIT_ENABLED = 0x0010, + MONO_PINVOKE_BEST_FIT_DISABLED = 0x0020, + MONO_PINVOKE_BEST_FIT_MASK = 0x0030, + MONO_PINVOKE_SUPPORTS_LAST_ERROR = 0x0040, + MONO_PINVOKE_CALL_CONV_MASK = 0x0700, + MONO_PINVOKE_CALL_CONV_WINAPI = 0x0100, + MONO_PINVOKE_CALL_CONV_CDECL = 0x0200, + MONO_PINVOKE_CALL_CONV_STDCALL = 0x0300, + MONO_PINVOKE_CALL_CONV_THISCALL = 0x0400, + MONO_PINVOKE_CALL_CONV_FASTCALL = 0x0500, + MONO_PINVOKE_THROW_ON_UNMAPPABLE_ENABLED = 0x1000, + MONO_PINVOKE_THROW_ON_UNMAPPABLE_DISABLED = 0x2000, + MONO_PINVOKE_THROW_ON_UNMAPPABLE_MASK = 0x3000, + MONO_PINVOKE_CALL_CONV_GENERIC = 0x0010, + MONO_PINVOKE_CALL_CONV_GENERICINST = 0x000a +}; + +/* + * 23.1.9 Flags for ManifestResource + */ +enum { + MONO_MANIFEST_RESOURCE_VISIBILITY_MASK = 0x00000007, + MONO_MANIFEST_RESOURCE_PUBLIC = 0x00000001, + MONO_MANIFEST_RESOURCE_PRIVATE = 0x00000002 +}; + +/* + * Method Attributes (23.1.10) + */ +enum { + MONO_METHOD_ATTR_ACCESS_MASK = 0x0007, + MONO_METHOD_ATTR_COMPILER_CONTROLLED = 0x0000, + MONO_METHOD_ATTR_PRIVATE = 0x0001, + MONO_METHOD_ATTR_FAM_AND_ASSEM = 0x0002, + MONO_METHOD_ATTR_ASSEM = 0x0003, + MONO_METHOD_ATTR_FAMILY = 0x0004, + MONO_METHOD_ATTR_FAM_OR_ASSEM = 0x0005, + MONO_METHOD_ATTR_PUBLIC = 0x0006, + + MONO_METHOD_ATTR_STATIC = 0x0010, + MONO_METHOD_ATTR_FINAL = 0x0020, + MONO_METHOD_ATTR_VIRTUAL = 0x0040, + MONO_METHOD_ATTR_HIDE_BY_SIG = 0x0080, + + MONO_METHOD_ATTR_VTABLE_LAYOUT_MASK = 0x0100, + MONO_METHOD_ATTR_REUSE_SLOT = 0x0000, + MONO_METHOD_ATTR_NEW_SLOT = 0x0100, + MONO_METHOD_ATTR_STRICT = 0x0200, + MONO_METHOD_ATTR_ABSTRACT = 0x0400, + + MONO_METHOD_ATTR_SPECIAL_NAME = 0x0800, + + MONO_METHOD_ATTR_PINVOKE_IMPL = 0x2000, + MONO_METHOD_ATTR_UNMANAGED_EXPORT = 0x0008, + +/* + * For runtime use only + */ + MONO_METHOD_ATTR_RESERVED_MASK = 0xd000, + MONO_METHOD_ATTR_RT_SPECIAL_NAME = 0x1000, + MONO_METHOD_ATTR_HAS_SECURITY = 0x4000, + MONO_METHOD_ATTR_REQUIRE_SEC_OBJECT = 0x8000 +}; + +/* + * Method Impl Attributes (23.1.11) + */ +enum { + MONO_METHOD_IMPL_ATTR_CODE_TYPE_MASK = 0x0003, + MONO_METHOD_IMPL_ATTR_IL = 0x0000, + MONO_METHOD_IMPL_ATTR_NATIVE = 0x0001, + MONO_METHOD_IMPL_ATTR_OPTIL = 0x0002, + MONO_METHOD_IMPL_ATTR_RUNTIME = 0x0003, + + MONO_METHOD_IMPL_ATTR_MANAGED_MASK = 0x0004, + MONO_METHOD_IMPL_ATTR_UNMANAGED = 0x0004, + MONO_METHOD_IMPL_ATTR_MANAGED = 0x0000, + + MONO_METHOD_IMPL_ATTR_FORWARD_REF = 0x0010, + MONO_METHOD_IMPL_ATTR_PRESERVE_SIG = 0x0080, + MONO_METHOD_IMPL_ATTR_INTERNAL_CALL = 0x1000, + MONO_METHOD_IMPL_ATTR_SYNCHRONIZED = 0x0020, + MONO_METHOD_IMPL_ATTR_NOINLINING = 0x0008, + MONO_METHOD_IMPL_ATTR_NOOPTIMIZATION = 0x0040, + MONO_METHOD_IMPL_ATTR_MAX_METHOD_IMPL_VAL = 0xffff +}; + +/* + * Method Semantics ([MethodSemanticAttributes]) 23.1.12, + */ +enum { + MONO_METHOD_SEMANTIC_SETTER = 0x0001, + MONO_METHOD_SEMANTIC_GETTER = 0x0002, + MONO_METHOD_SEMANTIC_OTHER = 0x0004, + MONO_METHOD_SEMANTIC_ADD_ON = 0x0008, + MONO_METHOD_SEMANTIC_REMOVE_ON = 0x0010, + MONO_METHOD_SEMANTIC_FIRE = 0x0020 +}; + +/* + * Flags for Params (23.1.13) + */ +enum { + MONO_PARAM_ATTR_IN = 0x0001, + MONO_PARAM_ATTR_OUT = 0x0002, + MONO_PARAM_ATTR_OPTIONAL = 0x0010, + MONO_PARAM_ATTR_RESERVED_MASK = 0xf000, + MONO_PARAM_ATTR_HAS_DEFAULT = 0x1000, + MONO_PARAM_ATTR_HAS_MARSHAL = 0x2000, + MONO_PARAM_ATTR_UNUSED = 0xcfe0 +}; + +/* + * 23.1.14 PropertyAttributes + */ +enum { + MONO_PROPERTY_ATTR_SPECIAL_NAME = 0x0200, + MONO_PROPERTY_ATTR_RESERVED_MASK = 0xf400, + MONO_PROPERTY_ATTR_RT_SPECIAL_NAME = 0x0400, + MONO_PROPERTY_ATTR_HAS_DEFAULT = 0x1000, + MONO_PROPERTY_ATTR_UNUSED = 0xe9ff +}; + +/* + * Type Attributes (23.1.15). + */ +enum { + MONO_TYPE_ATTR_VISIBILITY_MASK = 0x00000007, + MONO_TYPE_ATTR_NOT_PUBLIC = 0x00000000, + MONO_TYPE_ATTR_PUBLIC = 0x00000001, + MONO_TYPE_ATTR_NESTED_PUBLIC = 0x00000002, + MONO_TYPE_ATTR_NESTED_PRIVATE = 0x00000003, + MONO_TYPE_ATTR_NESTED_FAMILY = 0x00000004, + MONO_TYPE_ATTR_NESTED_ASSEMBLY = 0x00000005, + MONO_TYPE_ATTR_NESTED_FAM_AND_ASSEM = 0x00000006, + MONO_TYPE_ATTR_NESTED_FAM_OR_ASSEM = 0x00000007, + + MONO_TYPE_ATTR_LAYOUT_MASK = 0x00000018, + MONO_TYPE_ATTR_AUTO_LAYOUT = 0x00000000, + MONO_TYPE_ATTR_SEQUENTIAL_LAYOUT = 0x00000008, + MONO_TYPE_ATTR_EXPLICIT_LAYOUT = 0x00000010, + + MONO_TYPE_ATTR_CLASS_SEMANTIC_MASK = 0x00000020, + MONO_TYPE_ATTR_CLASS = 0x00000000, + MONO_TYPE_ATTR_INTERFACE = 0x00000020, + + MONO_TYPE_ATTR_ABSTRACT = 0x00000080, + MONO_TYPE_ATTR_SEALED = 0x00000100, + MONO_TYPE_ATTR_SPECIAL_NAME = 0x00000400, + + MONO_TYPE_ATTR_IMPORT = 0x00001000, + MONO_TYPE_ATTR_SERIALIZABLE = 0x00002000, + + MONO_TYPE_ATTR_STRING_FORMAT_MASK = 0x00030000, + MONO_TYPE_ATTR_ANSI_CLASS = 0x00000000, + MONO_TYPE_ATTR_UNICODE_CLASS = 0x00010000, + MONO_TYPE_ATTR_AUTO_CLASS = 0x00020000, + MONO_TYPE_ATTR_CUSTOM_CLASS = 0x00030000, + MONO_TYPE_ATTR_CUSTOM_MASK = 0x00c00000, + + MONO_TYPE_ATTR_BEFORE_FIELD_INIT = 0x00100000, + MONO_TYPE_ATTR_FORWARDER = 0x00200000, + + MONO_TYPE_ATTR_RESERVED_MASK = 0x00040800, + MONO_TYPE_ATTR_RT_SPECIAL_NAME = 0x00000800, + MONO_TYPE_ATTR_HAS_SECURITY = 0x00040000 +}; + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/blob.h b/unity-2019.4.24f1-mbe/mono/metadata/blob.h new file mode 100644 index 000000000..df2685821 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/blob.h @@ -0,0 +1,118 @@ +/** + * \file + * Definitions used to pull information out of the Blob + * + */ +#ifndef _MONO_METADATA_BLOB_H_ +#define _MONO_METADATA_BLOB_H_ + +/* + * Encoding for type signatures used in the Metadata + */ +typedef enum { + MONO_TYPE_END = 0x00, /* End of List */ + MONO_TYPE_VOID = 0x01, + MONO_TYPE_BOOLEAN = 0x02, + MONO_TYPE_CHAR = 0x03, + MONO_TYPE_I1 = 0x04, + MONO_TYPE_U1 = 0x05, + MONO_TYPE_I2 = 0x06, + MONO_TYPE_U2 = 0x07, + MONO_TYPE_I4 = 0x08, + MONO_TYPE_U4 = 0x09, + MONO_TYPE_I8 = 0x0a, + MONO_TYPE_U8 = 0x0b, + MONO_TYPE_R4 = 0x0c, + MONO_TYPE_R8 = 0x0d, + MONO_TYPE_STRING = 0x0e, + MONO_TYPE_PTR = 0x0f, /* arg: token */ + MONO_TYPE_BYREF = 0x10, /* arg: token */ + MONO_TYPE_VALUETYPE = 0x11, /* arg: token */ + MONO_TYPE_CLASS = 0x12, /* arg: token */ + MONO_TYPE_VAR = 0x13, /* number */ + MONO_TYPE_ARRAY = 0x14, /* type, rank, boundsCount, bound1, loCount, lo1 */ + MONO_TYPE_GENERICINST= 0x15, /* \x{2026} */ + MONO_TYPE_TYPEDBYREF = 0x16, + MONO_TYPE_I = 0x18, + MONO_TYPE_U = 0x19, + MONO_TYPE_FNPTR = 0x1b, /* arg: full method signature */ + MONO_TYPE_OBJECT = 0x1c, + MONO_TYPE_SZARRAY = 0x1d, /* 0-based one-dim-array */ + MONO_TYPE_MVAR = 0x1e, /* number */ + MONO_TYPE_CMOD_REQD = 0x1f, /* arg: typedef or typeref token */ + MONO_TYPE_CMOD_OPT = 0x20, /* optional arg: typedef or typref token */ + MONO_TYPE_INTERNAL = 0x21, /* CLR internal type */ + + MONO_TYPE_MODIFIER = 0x40, /* Or with the following types */ + MONO_TYPE_SENTINEL = 0x41, /* Sentinel for varargs method signature */ + MONO_TYPE_PINNED = 0x45, /* Local var that points to pinned object */ + + MONO_TYPE_ENUM = 0x55 /* an enumeration */ +} MonoTypeEnum; + +typedef enum { + MONO_TABLE_MODULE, + MONO_TABLE_TYPEREF, + MONO_TABLE_TYPEDEF, + MONO_TABLE_FIELD_POINTER, + MONO_TABLE_FIELD, + MONO_TABLE_METHOD_POINTER, + MONO_TABLE_METHOD, + MONO_TABLE_PARAM_POINTER, + MONO_TABLE_PARAM, + MONO_TABLE_INTERFACEIMPL, + MONO_TABLE_MEMBERREF, /* 0xa */ + MONO_TABLE_CONSTANT, + MONO_TABLE_CUSTOMATTRIBUTE, + MONO_TABLE_FIELDMARSHAL, + MONO_TABLE_DECLSECURITY, + MONO_TABLE_CLASSLAYOUT, + MONO_TABLE_FIELDLAYOUT, /* 0x10 */ + MONO_TABLE_STANDALONESIG, + MONO_TABLE_EVENTMAP, + MONO_TABLE_EVENT_POINTER, + MONO_TABLE_EVENT, + MONO_TABLE_PROPERTYMAP, + MONO_TABLE_PROPERTY_POINTER, + MONO_TABLE_PROPERTY, + MONO_TABLE_METHODSEMANTICS, + MONO_TABLE_METHODIMPL, + MONO_TABLE_MODULEREF, /* 0x1a */ + MONO_TABLE_TYPESPEC, + MONO_TABLE_IMPLMAP, + MONO_TABLE_FIELDRVA, + MONO_TABLE_UNUSED6, + MONO_TABLE_UNUSED7, + MONO_TABLE_ASSEMBLY, /* 0x20 */ + MONO_TABLE_ASSEMBLYPROCESSOR, + MONO_TABLE_ASSEMBLYOS, + MONO_TABLE_ASSEMBLYREF, + MONO_TABLE_ASSEMBLYREFPROCESSOR, + MONO_TABLE_ASSEMBLYREFOS, + MONO_TABLE_FILE, + MONO_TABLE_EXPORTEDTYPE, + MONO_TABLE_MANIFESTRESOURCE, + MONO_TABLE_NESTEDCLASS, + MONO_TABLE_GENERICPARAM, /* 0x2a */ + MONO_TABLE_METHODSPEC, + MONO_TABLE_GENERICPARAMCONSTRAINT, + MONO_TABLE_UNUSED8, + MONO_TABLE_UNUSED9, + MONO_TABLE_UNUSED10, + /* Portable PDB tables */ + MONO_TABLE_DOCUMENT, /* 0x30 */ + MONO_TABLE_METHODBODY, + MONO_TABLE_LOCALSCOPE, + MONO_TABLE_LOCALVARIABLE, + MONO_TABLE_LOCALCONSTANT, + MONO_TABLE_IMPORTSCOPE, + MONO_TABLE_STATEMACHINEMETHOD, + MONO_TABLE_CUSTOMDEBUGINFORMATION + +#define MONO_TABLE_LAST MONO_TABLE_CUSTOMDEBUGINFORMATION +#define MONO_TABLE_NUM (MONO_TABLE_LAST + 1) + +} MonoMetaTableEnum; + +#endif + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/boehm-gc.c b/unity-2019.4.24f1-mbe/mono/metadata/boehm-gc.c new file mode 100644 index 000000000..5f5f5974f --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/boehm-gc.c @@ -0,0 +1,2271 @@ +/** + * \file + * GC implementation using either the installed or included Boehm GC. + * + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2011 Novell, Inc (http://www.novell.com) + * Copyright 2011-2012 Xamarin, Inc (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include "config.h" + +#include + +#define GC_I_HIDE_POINTERS +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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" + +#if defined(HOST_DARWIN) && defined(HAVE_PTHREAD_GET_STACKADDR_NP) +void *pthread_get_stackaddr_np(pthread_t); +#endif + +#define GC_NO_DESCRIPTOR ((gpointer)(0 | GC_DS_LENGTH)) +/*Boehm max heap cannot be smaller than 16MB*/ +#define MIN_BOEHM_MAX_HEAP_SIZE_IN_MB 16 +#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); + +static GC_push_other_roots_proc default_push_other_roots; +static GHashTable *roots; + +static void +mono_push_other_roots(void); + +static void +register_test_toggleref_callback (void); + +#define BOEHM_GC_BIT_FINALIZER_AWARE 1 +static MonoGCFinalizerCallbacks fin_callbacks; + +/* GC Handles */ + +static mono_mutex_t handle_section; +#define lock_handles(handles) mono_os_mutex_lock (&handle_section) +#define unlock_handles(handles) mono_os_mutex_unlock (&handle_section) + +typedef struct { + guint32 *bitmap; + gpointer *entries; + guint32 size; + guint8 type; + guint slot_hint : 24; /* starting slot for search in bitmap */ + /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */ + /* we alloc this only for weak refs, since we can get the domain directly in the other cases */ + guint16 *domain_ids; +} HandleData; + +#define EMPTY_HANDLE_DATA(type) {NULL, NULL, 0, (type), 0, NULL} + +/* weak and weak-track arrays will be allocated in malloc memory + */ +static HandleData gc_handles [] = { + EMPTY_HANDLE_DATA (HANDLE_WEAK), + EMPTY_HANDLE_DATA (HANDLE_WEAK_TRACK), + EMPTY_HANDLE_DATA (HANDLE_NORMAL), + EMPTY_HANDLE_DATA (HANDLE_PINNED) +}; + +static void +mono_gc_warning (char *msg, GC_word arg) +{ + mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_GC, msg, (unsigned long)arg); +} + +static void on_gc_notification (GC_EventType event); +static void on_gc_heap_resize (size_t new_size); + +void +mono_gc_base_init (void) +{ + char *env; + char *params_opts = NULL; + char *debug_opts = NULL; + + if (gc_initialized) + return; + + mono_counters_init (); + +#ifndef HOST_WIN32 + mono_w32handle_init (); +#endif + + /* + * Handle the case when we are called from a thread different from the main thread, + * confusing libgc. + * FIXME: Move this to libgc where it belongs. + * + * we used to do this only when running on valgrind, + * but it happens also in other setups. + */ +#if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) + { + size_t size; + void *sstart; + pthread_attr_t attr; + pthread_getattr_np (pthread_self (), &attr); + pthread_attr_getstack (&attr, &sstart, &size); + pthread_attr_destroy (&attr); + /*g_print ("stackbottom pth is: %p\n", (char*)sstart + size);*/ + /* apparently with some linuxthreads implementations sstart can be NULL, + * fallback to the more imprecise method (bug# 78096). + */ + if (sstart) { + GC_stackbottom = (char*)sstart + size; + } else { + int dummy; + gsize stack_bottom = (gsize)&dummy; + stack_bottom += 4095; + stack_bottom &= ~4095; + GC_stackbottom = (char*)stack_bottom; + } + } +#elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP) + GC_stackbottom = (char*)pthread_get_stackaddr_np (pthread_self ()); +#elif defined(__OpenBSD__) +# include + { + stack_t ss; + int rslt; + + rslt = pthread_stackseg_np(pthread_self(), &ss); + g_assert (rslt == 0); + + GC_stackbottom = (char*)ss.ss_sp; + } +#else + { + int dummy; + gsize stack_bottom = (gsize)&dummy; + stack_bottom += 4095; + stack_bottom &= ~4095; + /*g_print ("stackbottom is: %p\n", (char*)stack_bottom);*/ + GC_stackbottom = (char*)stack_bottom; + } +#endif + + roots = g_hash_table_new (NULL, NULL); + default_push_other_roots = GC_push_other_roots; + GC_push_other_roots = mono_push_other_roots; + +#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) + { + 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_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); + GC_finalize_on_demand = 1; + GC_finalizer_notifier = mono_gc_finalize_notify; + + GC_init_gcj_malloc (5, NULL); + GC_allow_register_threads (); + + 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; + + opt = strchr (opt, '=') + 1; + if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) { + if (max_heap < MIN_BOEHM_MAX_HEAP_SIZE) { + fprintf (stderr, "max-heap-size must be at least %dMb.\n", MIN_BOEHM_MAX_HEAP_SIZE_IN_MB); + exit (1); + } + GC_set_max_heap_size (max_heap); + } else { + fprintf (stderr, "max-heap-size must be an integer.\n"); + exit (1); + } + continue; + } else if (g_str_has_prefix (opt, "toggleref-test")) { + register_test_toggleref_callback (); + continue; + } 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"); + fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n"); + exit (1); + */ + } + } + g_strfreev (opts); + g_free (params_opts); + } + + mono_thread_callbacks_init (); + mono_thread_info_init (sizeof (MonoThreadInfo)); + mono_os_mutex_init (&mono_gc_lock); + mono_os_mutex_init_recursive (&handle_section); + + mono_thread_info_attach (); + + GC_set_on_collection_event (on_gc_notification); + GC_on_heap_resize = on_gc_heap_resize; + + 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) +{ + GC_finalizer_notifier = NULL; +} + +/** + * mono_gc_collect: + * \param generation GC generation identifier + * + * Perform a garbage collection for the given generation, higher numbers + * mean usually older objects. Collecting a high-numbered generation + * implies collecting also the lower-numbered generations. + * The maximum value for \p generation can be retrieved with a call to + * \c mono_gc_max_generation, so this function is usually called as: + * + * mono_gc_collect (mono_gc_max_generation ()); + */ +void +mono_gc_collect (int generation) +{ +#ifndef DISABLE_PERFCOUNTERS + mono_atomic_inc_i32 (&mono_perfcounters->gc_induced); +#endif + GC_gcollect (); +} + + +int +mono_gc_collect_a_little() +{ + return GC_collect_a_little(); +} + +/** + * mono_gc_max_generation: + * + * Get the maximum generation number used by the current garbage + * collector. The value will be 0 for the Boehm collector, 1 or more + * for the generational collectors. + * + * Returns: the maximum generation number. + */ +int +mono_gc_max_generation (void) +{ + return 0; +} + +/** + * mono_gc_get_generation: + * \param object a managed object + * + * Get the garbage collector's generation that \p object belongs to. + * Use this has a hint only. + * + * \returns a garbage collector generation number + */ +int +mono_gc_get_generation (MonoObject *object) +{ + return 0; +} + +/** + * mono_gc_collection_count: + * \param generation a GC generation number + * + * Get how many times a garbage collection has been performed + * for the given \p generation number. + * + * \returns the number of garbage collections + */ +int +mono_gc_collection_count (int generation) +{ + return GC_gc_no; +} + +/** + * mono_gc_add_memory_pressure: + * \param value amount of bytes + * + * Adjust the garbage collector's view of how many bytes of memory + * are indirectly referenced by managed objects (for example unmanaged + * memory holding image or other binary data). + * This is a hint only to the garbage collector algorithm. + * Note that negative amounts of p value will decrease the memory + * pressure. + */ +void +mono_gc_add_memory_pressure (gint64 value) +{ +} + +/** + * mono_gc_get_used_size: + * + * Get the approximate amount of memory used by managed objects. + * + * Returns: the amount of memory used in bytes + */ +int64_t +mono_gc_get_used_size (void) +{ + return GC_get_heap_size () - GC_get_free_bytes (); +} + +/** + * mono_gc_get_heap_size: + * + * Get the amount of memory used by the garbage collector. + * + * Returns: the size of the heap in bytes + */ +int64_t +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) +{ + return GC_thread_is_registered (); +} + +gpointer +mono_gc_thread_attach (MonoThreadInfo* info) +{ + struct GC_stack_base sb; + int res; + + /* TODO: use GC_get_stack_base instead of baseptr. */ + sb.mem_base = info->stack_end; + res = GC_register_my_thread (&sb); + if (res == GC_UNIMPLEMENTED) + return NULL; /* Cannot happen with GC v7+. */ + + info->handle_stack = mono_handle_stack_alloc (); + + 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) +{ + MonoNativeThreadId tid; + + tid = mono_thread_info_get_tid (p); + + mono_threads_add_joinable_runtime_thread(p); + + mono_handle_stack_free (p->handle_stack); + p->handle_stack = NULL; +} + +gboolean +mono_gc_thread_in_critical_region (MonoThreadInfo *info) +{ + return FALSE; +} + +gboolean +mono_object_is_alive (MonoObject* o) +{ + return GC_is_marked ((ptr_t)o); +} + +int +mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data) +{ + return 1; +} + +static gint64 gc_start_time; + +static void +on_gc_notification (GC_EventType event) +{ + MonoProfilerGCEvent e; + + switch (event) { + case GC_EVENT_PRE_STOP_WORLD: + e = MONO_GC_EVENT_PRE_STOP_WORLD; + MONO_GC_WORLD_STOP_BEGIN (); + break; + + case GC_EVENT_POST_STOP_WORLD: + e = MONO_GC_EVENT_POST_STOP_WORLD; + MONO_GC_WORLD_STOP_END (); + break; + + case GC_EVENT_PRE_START_WORLD: + e = MONO_GC_EVENT_PRE_START_WORLD; + MONO_GC_WORLD_RESTART_BEGIN (1); + break; + + case GC_EVENT_POST_START_WORLD: + e = MONO_GC_EVENT_POST_START_WORLD; + MONO_GC_WORLD_RESTART_END (1); + break; + + case GC_EVENT_START: + e = MONO_GC_EVENT_START; + MONO_GC_BEGIN (1); +#ifndef DISABLE_PERFCOUNTERS + if (mono_perfcounters) + mono_atomic_inc_i32 (&mono_perfcounters->gc_collections0); +#endif + mono_atomic_inc_i32 (&gc_stats.major_gc_count); + gc_start_time = mono_100ns_ticks (); + break; + + case GC_EVENT_END: + e = MONO_GC_EVENT_END; + MONO_GC_END (1); +#if defined(ENABLE_DTRACE) && defined(__sun__) + /* This works around a dtrace -G problem on Solaris. + Limit its actual use to when the probe is enabled. */ + if (MONO_GC_END_ENABLED ()) + sleep(0); +#endif + +#ifndef DISABLE_PERFCOUNTERS + if (mono_perfcounters) { + guint64 heap_size = GC_get_heap_size (); + guint64 used_size = heap_size - GC_get_free_bytes (); + /* FIXME: change these to mono_atomic_store_i64 () */ + UnlockedWrite64 (&mono_perfcounters->gc_total_bytes, used_size); + UnlockedWrite64 (&mono_perfcounters->gc_committed_bytes, heap_size); + UnlockedWrite64 (&mono_perfcounters->gc_reserved_bytes, heap_size); + UnlockedWrite64 (&mono_perfcounters->gc_gen0size, heap_size); + } +#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); + break; + default: + break; + } + + switch (event) { + case GC_EVENT_MARK_START: + case GC_EVENT_MARK_END: + case GC_EVENT_RECLAIM_START: + case GC_EVENT_RECLAIM_END: + break; + default: + MONO_PROFILER_RAISE (gc_event, (e, 0)); + break; + } + + switch (event) { + case GC_EVENT_PRE_STOP_WORLD: + mono_thread_info_suspend_lock (); + MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED, 0)); + break; + case GC_EVENT_POST_START_WORLD: + mono_thread_info_suspend_unlock (); + MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_POST_START_WORLD_UNLOCKED, 0)); + break; + default: + break; + } +} + + +static void +on_gc_heap_resize (size_t new_size) +{ + guint64 heap_size = GC_get_heap_size (); +#ifndef DISABLE_PERFCOUNTERS + if (mono_perfcounters) { + /* FIXME: change these to mono_atomic_store_i64 () */ + UnlockedWrite64 (&mono_perfcounters->gc_committed_bytes, heap_size); + UnlockedWrite64 (&mono_perfcounters->gc_reserved_bytes, heap_size); + UnlockedWrite64 (&mono_perfcounters->gc_gen0size, heap_size); + } +#endif + + MONO_PROFILER_RAISE (gc_resize, (new_size)); +} + +typedef struct { + char *start; + char *end; +} RootData; + +static gpointer +register_root (gpointer arg) +{ + RootData* root_data = arg; + g_hash_table_insert (roots, root_data->start, root_data->end); + return NULL; +} + +int +mono_gc_register_root (char *start, size_t size, void *descr, MonoGCRootSource source, void *key, const char *msg) +{ + RootData root_data; + root_data.start = start; + /* Boehm root processing requires one byte past end of region to be scanned */ + root_data.end = start + size + 1; + GC_call_with_alloc_lock (register_root, &root_data); + MONO_PROFILER_RAISE (gc_root_register, ((const mono_byte *) start, size, source, key, msg)); + return TRUE; +} + +int +mono_gc_register_root_wbarrier (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, void *key, const char *msg) +{ + return mono_gc_register_root (start, size, descr, source, key, msg); +} + +static gpointer +deregister_root (gpointer arg) +{ + gboolean removed = g_hash_table_remove (roots, arg); + g_assert (removed); + return NULL; +} + +void +mono_gc_deregister_root (char* addr) +{ + GC_call_with_alloc_lock (deregister_root, 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) +{ + HandleChunk *cur = stack->bottom; + HandleChunk *last = stack->top; + + if (!cur) + return; + + while (cur) { + if (cur->size > 0) + GC_push_all (cur->elems, (char*)(cur->elems + cur->size) + 1); + if (cur == last) + break; + cur = cur->next; + } +} + +static void +mono_push_other_roots (void) +{ + g_hash_table_foreach (roots, push_root, NULL); + FOREACH_THREAD (info) { + HandleStack* stack = (HandleStack*)info->handle_stack; + if (stack) + push_handle_stack (stack); + } FOREACH_THREAD_END + if (default_push_other_roots) + default_push_other_roots (); +} + +static void +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 + GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr, obj); +} + +static void +mono_gc_weak_link_remove (void **link_addr, gboolean track) +{ + if (track) + GC_unregister_long_link (link_addr); + else + GC_unregister_disappearing_link (link_addr); + *link_addr = NULL; +} + +static gpointer +reveal_link (gpointer link_addr) +{ + void **link_a = (void **)link_addr; + return REVEAL_POINTER (*link_a); +} + +static MonoObject * +mono_gc_weak_link_get (void **link_addr) +{ + MonoObject *obj = (MonoObject *)GC_call_with_alloc_lock (reveal_link, link_addr); + if (obj == (MonoObject *) -1) + return NULL; + return obj; +} + +void* +mono_gc_make_descr_for_string (gsize *bitmap, int numbits) +{ + return mono_gc_make_descr_from_bitmap (bitmap, numbits); +} + +void* +mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size) +{ + return mono_gc_make_descr_from_bitmap (bitmap, numbits); +} + +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... */ + return GC_NO_DESCRIPTOR; +} + +void* +mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits) +{ + /* It seems there are issues when the bitmap doesn't fit: play it safe */ + if (numbits >= 30) + return GC_NO_DESCRIPTOR; + else + return (gpointer)GC_make_descriptor ((GC_bitmap)bitmap, numbits); +} + +void* +mono_gc_make_vector_descr (void) +{ + return NULL; +} + +void* +mono_gc_make_root_descr_all_refs (int numbits) +{ + return NULL; +} + +void* +mono_gc_alloc_fixed (size_t size, void *descr, MonoGCRootSource source, void *key, const char *msg) +{ + void *start = GC_MALLOC_UNCOLLECTABLE (size); + MONO_PROFILER_RAISE (gc_root_register, ((const mono_byte *) start, size, source, key, msg)); + return start; +} + +void +mono_gc_free_fixed (void* addr) +{ + MONO_PROFILER_RAISE (gc_root_unregister, ((const mono_byte *) addr)); + GC_FREE (addr); +} + +void * +mono_gc_alloc_obj (MonoVTable *vtable, size_t size) +{ + MonoObject *obj; + + if (!vtable->klass->has_references) { + obj = (MonoObject *)GC_MALLOC_ATOMIC (size); + if (G_UNLIKELY (!obj)) + return NULL; + + obj->vtable = vtable; + obj->synchronisation = NULL; + + memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject)); + } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) { + obj = (MonoObject *)GC_GCJ_MALLOC (size, vtable); + if (G_UNLIKELY (!obj)) + return NULL; + } else { + obj = (MonoObject *)GC_MALLOC (size); + if (G_UNLIKELY (!obj)) + return NULL; + + obj->vtable = vtable; + } + + if (G_UNLIKELY (mono_profiler_allocations_enabled ())) + MONO_PROFILER_RAISE (gc_allocation, (obj)); + + return obj; +} + +void * +mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length) +{ + MonoArray *obj; + + if (!vtable->klass->has_references) { + obj = (MonoArray *)GC_MALLOC_ATOMIC (size); + if (G_UNLIKELY (!obj)) + return NULL; + + obj->obj.vtable = vtable; + 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 (G_UNLIKELY (!obj)) + return NULL; + } else { + obj = (MonoArray *)GC_MALLOC (size); + if (G_UNLIKELY (!obj)) + return NULL; + + obj->obj.vtable = vtable; + } + + obj->max_length = max_length; + + if (G_UNLIKELY (mono_profiler_allocations_enabled ())) + MONO_PROFILER_RAISE (gc_allocation, (&obj->obj)); + + return obj; +} + +void * +mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size) +{ + MonoArray *obj; + + if (!vtable->klass->has_references) { + obj = (MonoArray *)GC_MALLOC_ATOMIC (size); + if (G_UNLIKELY (!obj)) + return NULL; + + obj->obj.vtable = vtable; + 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 (G_UNLIKELY (!obj)) + return NULL; + } else { + obj = (MonoArray *)GC_MALLOC (size); + if (G_UNLIKELY (!obj)) + return NULL; + + obj->obj.vtable = vtable; + } + + obj->max_length = max_length; + + if (bounds_size) + obj->bounds = (MonoArrayBounds *) ((char *) obj + size - bounds_size); + + if (G_UNLIKELY (mono_profiler_allocations_enabled ())) + MONO_PROFILER_RAISE (gc_allocation, (&obj->obj)); + + return obj; +} + +void * +mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len) +{ + MonoString *obj = (MonoString *)GC_MALLOC_ATOMIC (size); + if (G_UNLIKELY (!obj)) + return NULL; + + obj->object.vtable = vtable; + obj->object.synchronisation = NULL; + obj->length = len; + obj->chars [len] = 0; + + if (G_UNLIKELY (mono_profiler_allocations_enabled ())) + MONO_PROFILER_RAISE (gc_allocation, (&obj->object)); + + return obj; +} + +void* +mono_gc_alloc_mature (MonoVTable *vtable, size_t size) +{ + return mono_gc_alloc_obj (vtable, size); +} + +void* +mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size) +{ + return mono_gc_alloc_obj (vtable, size); +} + +int +mono_gc_invoke_finalizers (void) +{ + /* There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4: + * the 'mem_freed' variable is not initialized when there are no + * objects to finalize, which leads to strange behavior later on. + * The check is necessary to work around that bug. + */ + if (GC_should_invoke_finalizers ()) + return GC_invoke_finalizers (); + return 0; +} + +MonoBoolean +mono_gc_pending_finalizers (void) +{ + return GC_should_invoke_finalizers (); +} + +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) +{ + 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 */ + 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 +mono_gc_clear_domain (MonoDomain *domain) +{ +} + +void +mono_gc_suspend_finalizers (void) +{ +} + +int +mono_gc_get_suspend_signal (void) +{ + return GC_get_suspend_signal (); +} + +int +mono_gc_get_restart_signal (void) +{ + return GC_get_thr_restart_signal (); +} + +#if defined(USE_COMPILER_TLS) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__)) +extern __thread void* GC_thread_tls; +#include "metadata-internals.h" + +static int +shift_amount (int v) +{ + int i = 0; + while (!(v & (1 << i))) + i++; + return i; +} + +enum { + ATYPE_FREEPTR, + ATYPE_FREEPTR_FOR_BOX, + ATYPE_NORMAL, + ATYPE_GCJ, + ATYPE_STRING, + ATYPE_NUM +}; + +static MonoMethod* +create_allocator (int atype, int tls_key, gboolean slowpath) +{ + int index_var, bytes_var, my_fl_var, my_entry_var; + guint32 no_freelist_branch, not_small_enough_branch = 0; + guint32 size_overflow_branch = 0; + MonoMethodBuilder *mb; + MonoMethod *res; + MonoMethodSignature *csig; + const char *name = NULL; + WrapperInfo *info; + + 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) { + name = slowpath ? "SlowAllocPtrfreeBox" : "AllocPtrfreeBox"; + } else if (atype == ATYPE_NORMAL) { + name = slowpath ? "SlowAlloc" : "Alloc"; + } else if (atype == ATYPE_GCJ) { + name = slowpath ? "SlowAllocGcj" : "AllocGcj"; + } else if (atype == ATYPE_STRING) { + name = slowpath ? "SlowAllocString" : "AllocString"; + } else { + g_assert_not_reached (); + } + + csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2); + + if (atype == ATYPE_STRING) { + csig->ret = &mono_defaults.string_class->byval_arg; + csig->params [0] = &mono_defaults.int_class->byval_arg; + csig->params [1] = &mono_defaults.int32_class->byval_arg; + } else { + csig->ret = &mono_defaults.object_class->byval_arg; + csig->params [0] = &mono_defaults.int_class->byval_arg; + csig->params [1] = &mono_defaults.int32_class->byval_arg; + } + + mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC); + + if (slowpath) + goto always_slowpath; + + bytes_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg); + if (atype == ATYPE_STRING) { + /* a string alloator method takes the args: (vtable, len) */ + /* bytes = (offsetof (MonoString, chars) + ((len + 1) * 2)); */ + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_icon (mb, 1); + mono_mb_emit_byte (mb, MONO_CEE_ADD); + mono_mb_emit_icon (mb, 1); + mono_mb_emit_byte (mb, MONO_CEE_SHL); + // sizeof (MonoString) might include padding + mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, chars)); + mono_mb_emit_byte (mb, MONO_CEE_ADD); + mono_mb_emit_stloc (mb, bytes_var); + } else { + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_stloc (mb, bytes_var); + } + + /* this is needed for strings/arrays only as the other big types are never allocated with this method */ + if (atype == ATYPE_STRING) { + /* check for size */ + /* if (!SMALL_ENOUGH (bytes)) jump slow_path;*/ + mono_mb_emit_ldloc (mb, bytes_var); + mono_mb_emit_icon (mb, (NFREELISTS-1) * GRANULARITY); + not_small_enough_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S); + /* check for overflow */ + mono_mb_emit_ldloc (mb, bytes_var); + mono_mb_emit_icon (mb, sizeof (MonoString)); + size_overflow_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S); + } + + /* int index = INDEX_FROM_BYTES(bytes); */ + index_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg); + + mono_mb_emit_ldloc (mb, bytes_var); + mono_mb_emit_icon (mb, GRANULARITY - 1); + mono_mb_emit_byte (mb, MONO_CEE_ADD); + mono_mb_emit_icon (mb, shift_amount (GRANULARITY)); + mono_mb_emit_byte (mb, MONO_CEE_SHR_UN); + mono_mb_emit_icon (mb, shift_amount (sizeof (gpointer))); + mono_mb_emit_byte (mb, MONO_CEE_SHL); + /* index var is already adjusted into bytes */ + mono_mb_emit_stloc (mb, index_var); + + my_fl_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + my_entry_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + /* my_fl = ((GC_thread)tsd) -> ptrfree_freelists + index; */ + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, 0x0D); /* CEE_MONO_TLS */ + mono_mb_emit_i4 (mb, tls_key); + if (atype == ATYPE_FREEPTR || atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING) + mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs) + + G_STRUCT_OFFSET (struct thread_local_freelists, + ptrfree_freelists)); + else if (atype == ATYPE_NORMAL) + mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs) + + G_STRUCT_OFFSET (struct thread_local_freelists, + normal_freelists)); + else if (atype == ATYPE_GCJ) + mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs) + + G_STRUCT_OFFSET (struct thread_local_freelists, + gcj_freelists)); + else + g_assert_not_reached (); + mono_mb_emit_byte (mb, MONO_CEE_ADD); + mono_mb_emit_ldloc (mb, index_var); + mono_mb_emit_byte (mb, MONO_CEE_ADD); + mono_mb_emit_stloc (mb, my_fl_var); + + /* my_entry = *my_fl; */ + mono_mb_emit_ldloc (mb, my_fl_var); + mono_mb_emit_byte (mb, MONO_CEE_LDIND_I); + mono_mb_emit_stloc (mb, my_entry_var); + + /* if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { */ + mono_mb_emit_ldloc (mb, my_entry_var); + mono_mb_emit_icon (mb, HBLKSIZE); + no_freelist_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S); + + /* ptr_t next = obj_link(my_entry); *my_fl = next; */ + mono_mb_emit_ldloc (mb, my_fl_var); + mono_mb_emit_ldloc (mb, my_entry_var); + mono_mb_emit_byte (mb, MONO_CEE_LDIND_I); + mono_mb_emit_byte (mb, MONO_CEE_STIND_I); + + /* set the vtable and clear the words in the object */ + mono_mb_emit_ldloc (mb, my_entry_var); + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_byte (mb, MONO_CEE_STIND_I); + + if (atype == ATYPE_FREEPTR) { + int start_var, end_var, start_loop; + /* end = my_entry + bytes; start = my_entry + sizeof (gpointer); + */ + start_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + end_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + mono_mb_emit_ldloc (mb, my_entry_var); + mono_mb_emit_ldloc (mb, bytes_var); + mono_mb_emit_byte (mb, MONO_CEE_ADD); + mono_mb_emit_stloc (mb, end_var); + mono_mb_emit_ldloc (mb, my_entry_var); + mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation)); + mono_mb_emit_byte (mb, MONO_CEE_ADD); + mono_mb_emit_stloc (mb, start_var); + /* + * do { + * *start++ = NULL; + * } while (start < end); + */ + start_loop = mono_mb_get_label (mb); + mono_mb_emit_ldloc (mb, start_var); + mono_mb_emit_icon (mb, 0); + mono_mb_emit_byte (mb, MONO_CEE_STIND_I); + mono_mb_emit_ldloc (mb, start_var); + mono_mb_emit_icon (mb, sizeof (gpointer)); + mono_mb_emit_byte (mb, MONO_CEE_ADD); + mono_mb_emit_stloc (mb, start_var); + + mono_mb_emit_ldloc (mb, start_var); + mono_mb_emit_ldloc (mb, end_var); + mono_mb_emit_byte (mb, MONO_CEE_BLT_UN_S); + mono_mb_emit_byte (mb, start_loop - (mono_mb_get_label (mb) + 1)); + } else if (atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING) { + /* need to clear just the sync pointer */ + mono_mb_emit_ldloc (mb, my_entry_var); + mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation)); + mono_mb_emit_byte (mb, MONO_CEE_ADD); + mono_mb_emit_icon (mb, 0); + mono_mb_emit_byte (mb, MONO_CEE_STIND_I); + } + + if (atype == ATYPE_STRING) { + /* need to set length and clear the last char */ + /* s->length = len; */ + mono_mb_emit_ldloc (mb, my_entry_var); + mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, length)); + mono_mb_emit_byte (mb, MONO_CEE_ADD); + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_byte (mb, MONO_CEE_STIND_I4); + /* s->chars [len] = 0; */ + mono_mb_emit_ldloc (mb, my_entry_var); + mono_mb_emit_ldloc (mb, bytes_var); + mono_mb_emit_icon (mb, 2); + mono_mb_emit_byte (mb, MONO_CEE_SUB); + mono_mb_emit_byte (mb, MONO_CEE_ADD); + mono_mb_emit_icon (mb, 0); + mono_mb_emit_byte (mb, MONO_CEE_STIND_I2); + } + + /* return my_entry; */ + mono_mb_emit_ldloc (mb, my_entry_var); + mono_mb_emit_byte (mb, MONO_CEE_RET); + + mono_mb_patch_short_branch (mb, no_freelist_branch); + if (not_small_enough_branch > 0) + mono_mb_patch_short_branch (mb, not_small_enough_branch); + if (size_overflow_branch > 0) + mono_mb_patch_short_branch (mb, size_overflow_branch); + + /* the slow path: we just call back into the runtime */ + always_slowpath: + if (atype == ATYPE_STRING) { + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_icall (mb, ves_icall_string_alloc); + } else { + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_icall (mb, ves_icall_object_new_specific); + } + + mono_mb_emit_byte (mb, MONO_CEE_RET); + + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); + info->d.alloc.gc_name = "boehm"; + info->d.alloc.alloc_type = atype; + mb->init_locals = FALSE; + + res = mono_mb_create (mb, csig, 8, info); + mono_mb_free (mb); + + return res; +#endif +} + +static MonoMethod* alloc_method_cache [ATYPE_NUM]; +static MonoMethod* slowpath_alloc_method_cache [ATYPE_NUM]; + +gboolean +mono_gc_is_critical_method (MonoMethod *method) +{ + int i; + + for (i = 0; i < ATYPE_NUM; ++i) + if (method == alloc_method_cache [i] || method == slowpath_alloc_method_cache [i]) + return TRUE; + + return FALSE; +} + +/* + * If possible, generate a managed method that can quickly allocate objects in class + * @klass. The method will typically have an thread-local inline allocation sequence. + * The signature of the called method is: + * object allocate (MonoVTable *vtable) + * The thread local alloc logic is taken from libgc/pthread_support.c. + */ +MonoMethod* +mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size) +{ + int atype; + + /* + * Tls implementation changed, we jump to tls native getters/setters. + * Is boehm managed allocator ok with this ? Do we even care ? + */ + return NULL; + +#if 0 + if (!SMALL_ENOUGH (klass->instance_size)) + return NULL; + if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass)) + return NULL; + if (G_UNLIKELY (mono_profiler_allocations_enabled ())) + return NULL; + if (klass->rank) + return NULL; + if (mono_class_is_open_constructed_type (&klass->byval_arg)) + return NULL; + if (klass->byval_arg.type == MONO_TYPE_STRING) { + atype = ATYPE_STRING; + } else if (!known_instance_size) { + return NULL; + } else if (!klass->has_references) { + if (for_box) + atype = ATYPE_FREEPTR_FOR_BOX; + else + atype = ATYPE_FREEPTR; + } else { + return NULL; + /* + * disabled because we currently do a runtime choice anyway, to + * deal with multiple appdomains. + if (vtable->gc_descr != GC_NO_DESCRIPTOR) + atype = ATYPE_GCJ; + else + atype = ATYPE_NORMAL; + */ + } + return mono_gc_get_managed_allocator_by_type (atype, MANAGED_ALLOCATOR_REGULAR); +#endif +} + +MonoMethod* +mono_gc_get_managed_array_allocator (MonoClass *klass) +{ + return NULL; +} + +/** + * mono_gc_get_managed_allocator_by_type: + * + * Return a managed allocator method corresponding to allocator type ATYPE. + */ +MonoMethod* +mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant) +{ + MonoMethod *res; + gboolean slowpath = variant != MANAGED_ALLOCATOR_REGULAR; + MonoMethod **cache = slowpath ? slowpath_alloc_method_cache : alloc_method_cache; + + return NULL; + + res = cache [atype]; + if (res) + return res; + + res = create_allocator (atype, -1, slowpath); + mono_os_mutex_lock (&mono_gc_lock); + if (cache [atype]) { + mono_free_method (res); + res = cache [atype]; + } else { + mono_memory_barrier (); + cache [atype] = res; + } + mono_os_mutex_unlock (&mono_gc_lock); + return res; +} + +guint32 +mono_gc_get_managed_allocator_types (void) +{ + return 0; +} + +static MonoMethod *write_barrier_conc_method; +MonoMethod* +mono_gc_get_write_barrier (void) +{ + 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 + +gboolean +mono_gc_is_critical_method (MonoMethod *method) +{ + return FALSE; +} + +MonoMethod* +mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size) +{ + return NULL; +} + +MonoMethod* +mono_gc_get_managed_array_allocator (MonoClass *klass) +{ + return NULL; +} + +MonoMethod* +mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant) +{ + return NULL; +} + +guint32 +mono_gc_get_managed_allocator_types (void) +{ + return 0; +} + +static MonoMethod *write_barrier_conc_method; +MonoMethod* +mono_gc_get_write_barrier (void) +{ + 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 + +MonoMethod* +mono_gc_get_specific_write_barrier (gboolean is_concurrent) +{ + g_assert_not_reached (); + return NULL; +} + +int +mono_gc_get_aligned_size_for_allocator (int size) +{ + return size; +} + +const char * +mono_gc_get_gc_name (void) +{ + return "boehm"; +} + +void* +mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data) +{ + return GC_call_with_alloc_lock (func, data); +} + +char* +mono_gc_get_description (void) +{ + return g_strdup ("BDWGC"); +} + +void +mono_gc_set_desktop_mode (void) +{ + GC_dont_expand = 1; +} + +gboolean +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 || gc_dont_gc_env) + return TRUE; + else + return FALSE; +} + +void +mono_gc_wbarrier_range_copy (gpointer _dest, gpointer _src, int size) +{ + memcpy (_dest, _src, size); + mono_gc_dirty_range (_dest, size); +} + +void* +mono_gc_get_range_copy_func (void) +{ + return &mono_gc_wbarrier_range_copy; +} + +guint8* +mono_gc_get_card_table (int *shift_bits, gpointer *card_mask) +{ + return NULL; +} + +gboolean +mono_gc_card_table_nursery_check (void) +{ + g_assert_not_reached (); + return TRUE; +} + +void* +mono_gc_get_nursery (int *shift_bits, size_t *size) +{ + return NULL; +} + +gboolean +mono_gc_precise_stack_mark_enabled (void) +{ + return FALSE; +} + +FILE * +mono_gc_get_logfile (void) +{ + return NULL; +} + +void +mono_gc_conservatively_scan_area (void *start, void *end) +{ + g_assert_not_reached (); +} + +void * +mono_gc_scan_object (void *obj, void *gc_data) +{ + g_assert_not_reached (); + return NULL; +} + +gsize* +mono_gc_get_bitmap_for_descr (void *descr, int *numbits) +{ + g_assert_not_reached (); + return NULL; +} + +void +mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks) +{ +} + +void +mono_gc_set_stack_end (void *stack_end) +{ +} + +void mono_gc_set_skip_thread (gboolean value) +{ +} + +void +mono_gc_register_for_finalization (MonoObject *obj, void *user_data) +{ + guint offset = 0; + +#ifndef GC_DEBUG + /* This assertion is not valid when GC_DEBUG is defined */ + g_assert (GC_base (obj) == (char*)obj - offset); +#endif + + GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, (GC_finalization_proc)user_data, GUINT_TO_POINTER (offset), NULL, NULL); +} + +#ifndef HOST_WIN32 +int +mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) +{ + /* it is being replaced by GC_pthread_create on some + * platforms, see libgc/include/gc_pthread_redirects.h */ + return pthread_create (new_thread, attr, start_routine, arg); +} +#endif + +#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 + +MonoVTable * +mono_gc_get_vtable (MonoObject *obj) +{ + // No pointer tagging. + return obj->vtable; +} + +guint +mono_gc_get_vtable_bits (MonoClass *klass) +{ + if (fin_callbacks.is_class_finalization_aware) { + if (fin_callbacks.is_class_finalization_aware (klass)) + return BOEHM_GC_BIT_FINALIZER_AWARE; + } + return 0; +} + +/* + * mono_gc_register_altstack: + * + * Register the dimensions of the normal stack and altstack with the collector. + * Currently, STACK/STACK_SIZE is only used when the thread is suspended while it is on an altstack. + */ +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 +mono_gc_get_los_limit (void) +{ + return G_MAXINT; +} + +void +mono_gc_set_string_length (MonoString *str, gint32 new_length) +{ + mono_unichar2 *new_end = str->chars + new_length; + + /* zero the discarded string. This null-delimits the string and allows + * the space to be reclaimed by SGen. */ + + memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2)); + str->length = new_length; +} + +gboolean +mono_gc_user_markers_supported (void) +{ + return FALSE; +} + +void * +mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker) +{ + g_assert_not_reached (); + return NULL; +} + +/* Toggleref support */ + +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 */ + +static MonoToggleRefStatus +test_toggleref_callback (MonoObject *obj) +{ + static MonoClassField *mono_toggleref_test_field; + MonoToggleRefStatus status = MONO_TOGGLE_REF_DROP; + + if (!mono_toggleref_test_field) { + mono_toggleref_test_field = mono_class_get_field_from_name (mono_object_get_class (obj), "__test"); + g_assert (mono_toggleref_test_field); + } + + mono_field_get_value (obj, mono_toggleref_test_field, &status); + printf ("toggleref-cb obj %d\n", status); + return status; +} + +static void +register_test_toggleref_callback (void) +{ + mono_gc_toggleref_register_callback (test_toggleref_callback); +} + +static gboolean +is_finalization_aware (MonoObject *obj) +{ + MonoVTable *vt = obj->vtable; + return (vt->gc_bits & BOEHM_GC_BIT_FINALIZER_AWARE) == BOEHM_GC_BIT_FINALIZER_AWARE; +} + +static void +fin_notifier (MonoObject *obj) +{ + if (is_finalization_aware (obj)) + fin_callbacks.object_queued_for_finalization (obj); +} + +void +mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks) +{ + if (callbacks->version != MONO_GC_FINALIZER_EXTENSION_VERSION) + g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION, callbacks->version); + + 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) + +static inline gboolean +slot_occupied (HandleData *handles, guint slot) { + return handles->bitmap [slot / BITMAP_SIZE] & (1 << (slot % BITMAP_SIZE)); +} + +static inline void +vacate_slot (HandleData *handles, guint slot) { + handles->bitmap [slot / BITMAP_SIZE] &= ~(1 << (slot % BITMAP_SIZE)); +} + +static inline void +occupy_slot (HandleData *handles, guint slot) { + handles->bitmap [slot / BITMAP_SIZE] |= 1 << (slot % BITMAP_SIZE); +} + +static int +find_first_unset (guint32 bitmap) +{ + int i; + for (i = 0; i < 32; ++i) { + if (!(bitmap & (1 << i))) + return i; + } + return -1; +} + +static void +handle_data_alloc_entries (HandleData *handles) +{ + handles->size = 32; + if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) { + handles->entries = (void **)g_malloc0 (sizeof (*handles->entries) * handles->size); + handles->domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * handles->size); + } else { + handles->entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * handles->size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, NULL, "GC Handle Table (Boehm)"); + } + handles->bitmap = (guint32 *)g_malloc0 (handles->size / CHAR_BIT); +} + +static gint +handle_data_next_unset (HandleData *handles) +{ + gint slot; + for (slot = handles->slot_hint; slot < handles->size / BITMAP_SIZE; ++slot) { + if (handles->bitmap [slot] == 0xffffffff) + continue; + handles->slot_hint = slot; + return find_first_unset (handles->bitmap [slot]); + } + return -1; +} + +static gint +handle_data_first_unset (HandleData *handles) +{ + gint slot; + for (slot = 0; slot < handles->slot_hint; ++slot) { + if (handles->bitmap [slot] == 0xffffffff) + continue; + handles->slot_hint = slot; + return find_first_unset (handles->bitmap [slot]); + } + return -1; +} + +/* Returns the index of the current slot in the bitmap. */ +static void +handle_data_grow (HandleData *handles, gboolean track) +{ + guint32 *new_bitmap; + guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */ + + /* resize and copy the bitmap */ + new_bitmap = (guint32 *)g_malloc0 (new_size / CHAR_BIT); + memcpy (new_bitmap, handles->bitmap, handles->size / CHAR_BIT); + g_free (handles->bitmap); + handles->bitmap = new_bitmap; + + /* resize and copy the entries */ + if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) { + gpointer *entries; + guint16 *domain_ids; + gint i; + domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * new_size); + entries = (void **)g_malloc0 (sizeof (*handles->entries) * new_size); + memcpy (domain_ids, handles->domain_ids, sizeof (*handles->domain_ids) * handles->size); + for (i = 0; i < handles->size; ++i) { + MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i])); + if (obj) { + mono_gc_weak_link_add (&(entries [i]), obj, track); + mono_gc_weak_link_remove (&(handles->entries [i]), track); + } else { + g_assert (!handles->entries [i]); + } + } + g_free (handles->entries); + g_free (handles->domain_ids); + handles->entries = entries; + handles->domain_ids = domain_ids; + } else { + 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; + } + handles->slot_hint = handles->size / BITMAP_SIZE; + handles->size = new_size; +} + +static guint32 +alloc_handle (HandleData *handles, MonoObject *obj, gboolean track) +{ + gint slot, i; + guint32 res; + lock_handles (handles); + if (!handles->size) + handle_data_alloc_entries (handles); + i = handle_data_next_unset (handles); + if (i == -1 && handles->slot_hint != 0) + i = handle_data_first_unset (handles); + if (i == -1) { + handle_data_grow (handles, track); + i = 0; + } + slot = handles->slot_hint * BITMAP_SIZE + i; + occupy_slot (handles, slot); + handles->entries [slot] = NULL; + if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) { + /*FIXME, what to use when obj == null?*/ + handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id; + if (obj) + mono_gc_weak_link_add (&(handles->entries [slot]), obj, track); + } else { + handles->entries [slot] = obj; + mono_gc_dirty (handles->entries + slot); + } + +#ifndef DISABLE_PERFCOUNTERS + mono_atomic_inc_i32 (&mono_perfcounters->gc_num_handles); +#endif + unlock_handles (handles); + res = MONO_GC_HANDLE (slot, handles->type); + MONO_PROFILER_RAISE (gc_handle_created, (res, handles->type, obj)); + return res; +} + +/** + * mono_gchandle_new: + * \param obj managed object to get a handle for + * \param pinned whether the object should be pinned + * + * This returns a handle that wraps the object, this is used to keep a + * reference to a managed object from the unmanaged world and preventing the + * object from being disposed. + * + * If \p pinned is false the address of the object can not be obtained, if it is + * true the address of the object can be obtained. This will also pin the + * object so it will not be possible by a moving garbage collector to move the + * object. + * + * \returns a handle that can be used to access the object from + * unmanaged code. + */ +guint32 +mono_gchandle_new (MonoObject *obj, gboolean pinned) +{ + return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE); +} + +/** + * mono_gchandle_new_weakref: + * \param obj managed object to get a handle for + * \param track_resurrection Determines how long to track the object, if this is set to TRUE, the object is tracked after finalization, if FALSE, the object is only tracked up until the point of finalization. + * + * This returns a weak handle that wraps the object, this is used to + * keep a reference to a managed object from the unmanaged world. + * Unlike the \c mono_gchandle_new the object can be reclaimed by the + * garbage collector. In this case the value of the GCHandle will be + * set to zero. + * + * If \p track_resurrection is TRUE the object will be tracked through + * finalization and if the object is resurrected during the execution + * of the finalizer, then the returned weakref will continue to hold + * a reference to the object. If \p track_resurrection is FALSE, then + * the weak reference's target will become NULL as soon as the object + * is passed on to the finalizer. + * + * \returns a handle that can be used to access the object from + * unmanaged code. + */ +guint32 +mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection) +{ + return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection); +} + +/** + * mono_gchandle_get_target: + * \param gchandle a GCHandle's handle. + * + * The handle was previously created by calling \c mono_gchandle_new or + * \c mono_gchandle_new_weakref. + * + * \returns A pointer to the \c MonoObject* represented by the handle or + * NULL for a collected object if using a weakref handle. + */ +MonoObject* +mono_gchandle_get_target (guint32 gchandle) +{ + guint slot = MONO_GC_HANDLE_SLOT (gchandle); + guint type = MONO_GC_HANDLE_TYPE (gchandle); + HandleData *handles = &gc_handles [type]; + MonoObject *obj = NULL; + if (type >= HANDLE_TYPE_MAX) + return NULL; + + lock_handles (handles); + if (slot < handles->size && slot_occupied (handles, slot)) { + if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) { + obj = mono_gc_weak_link_get (&handles->entries [slot]); + } else { + obj = (MonoObject *)handles->entries [slot]; + } + } else { + /* print a warning? */ + } + unlock_handles (handles); + /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/ + return obj; +} + +void +mono_gchandle_set_target (guint32 gchandle, MonoObject *obj) +{ + guint slot = MONO_GC_HANDLE_SLOT (gchandle); + guint type = MONO_GC_HANDLE_TYPE (gchandle); + HandleData *handles = &gc_handles [type]; + MonoObject *old_obj = NULL; + + g_assert (type < HANDLE_TYPE_MAX); + lock_handles (handles); + if (slot < handles->size && slot_occupied (handles, slot)) { + if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) { + old_obj = (MonoObject *)handles->entries [slot]; + if (handles->entries [slot]) + mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK); + if (obj) + mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK); + /*FIXME, what to use when obj == null?*/ + 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? */ + } + /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/ + unlock_handles (handles); +} + +gboolean +mono_gc_is_null (void) +{ + return FALSE; +} + +/** + * mono_gchandle_is_in_domain: + * \param gchandle a GCHandle's handle. + * \param domain An application domain. + * + * Use this function to determine if the \p gchandle points to an + * object allocated in the specified \p domain. + * + * \returns TRUE if the object wrapped by the \p gchandle belongs to the specific \p domain. + */ +gboolean +mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain) +{ + guint slot = MONO_GC_HANDLE_SLOT (gchandle); + guint type = MONO_GC_HANDLE_TYPE (gchandle); + HandleData *handles = &gc_handles [type]; + gboolean result = FALSE; + + if (type >= HANDLE_TYPE_MAX) + return FALSE; + + lock_handles (handles); + if (slot < handles->size && slot_occupied (handles, slot)) { + if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) { + result = domain->domain_id == handles->domain_ids [slot]; + } else { + MonoObject *obj; + obj = (MonoObject *)handles->entries [slot]; + if (obj == NULL) + result = TRUE; + else + result = domain == mono_object_domain (obj); + } + } else { + /* print a warning? */ + } + unlock_handles (handles); + return result; +} + +/** + * mono_gchandle_free: + * \param gchandle a GCHandle's handle. + * + * Frees the \p gchandle handle. If there are no outstanding + * references, the garbage collector can reclaim the memory of the + * object wrapped. + */ +void +mono_gchandle_free (guint32 gchandle) +{ + guint slot = MONO_GC_HANDLE_SLOT (gchandle); + guint type = MONO_GC_HANDLE_TYPE (gchandle); + HandleData *handles = &gc_handles [type]; + if (type >= HANDLE_TYPE_MAX) + return; + + lock_handles (handles); + if (slot < handles->size && slot_occupied (handles, slot)) { + if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) { + if (handles->entries [slot]) + 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 { + /* print a warning? */ + } +#ifndef DISABLE_PERFCOUNTERS + mono_atomic_dec_i32 (&mono_perfcounters->gc_num_handles); +#endif + /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/ + unlock_handles (handles); + MONO_PROFILER_RAISE (gc_handle_deleted, (gchandle, handles->type)); +} + +/** + * mono_gchandle_free_domain: + * \param domain domain that is unloading + * + * Function used internally to cleanup any GC handle for objects belonging + * to the specified domain during appdomain unload. + */ +void +mono_gchandle_free_domain (MonoDomain *domain) +{ + guint type; + + for (type = HANDLE_TYPE_MIN; type <= HANDLE_PINNED; ++type) { + guint slot; + HandleData *handles = &gc_handles [type]; + lock_handles (handles); + for (slot = 0; slot < handles->size; ++slot) { + if (!slot_occupied (handles, slot)) + continue; + if (MONO_GC_HANDLE_TYPE_IS_WEAK (type)) { + if (domain->domain_id == handles->domain_ids [slot]) { + vacate_slot (handles, slot); + if (handles->entries [slot]) + mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK); + } + } else { + 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); + } + } + } + unlock_handles (handles); + } + +} + +void +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); +#endif /* no Boehm GC */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/callspec.c b/unity-2019.4.24f1-mbe/mono/metadata/callspec.c new file mode 100644 index 000000000..f2301e2c3 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/callspec.c @@ -0,0 +1,355 @@ +/** + * \file + * Call specification facilities for the Mono Runtime. + * + * Author: + * Paolo Molaro (lupus@ximian.com) + * Dietmar Maurer (dietmar@ximian.com) + * + * (C) 2002 Ximian, Inc. + * Copyright 2011 Xamarin, Inc (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full + * license information. + */ +#include +#include "metadata.h" +#include "callspec.h" +#include "assembly.h" +#include "class-internals.h" +#include "debug-helpers.h" + +static MonoAssembly *prog_assembly; + +gboolean +mono_callspec_eval_exception (MonoClass *klass, MonoCallSpec *spec) +{ + int include = 0; + int i; + + if (!klass) + return FALSE; + + for (i = 0; i < spec->len; i++) { + MonoTraceOperation *op = &spec->ops [i]; + int inc = 0; + + switch (op->op) { + case MONO_TRACEOP_EXCEPTION: + if (strcmp ("", op->data) == 0 && + strcmp ("all", op->data2) == 0) + inc = 1; + else if (strcmp ("", op->data) == 0 || + strcmp (klass->name_space, op->data) == 0) + if (strcmp (klass->name, op->data2) == 0) + inc = 1; + break; + default: + break; + } + if (op->exclude) { + if (inc) + include = 0; + } else if (inc) + include = 1; + } + + return include; +} + +gboolean mono_callspec_eval (MonoMethod *method, const MonoCallSpec *spec) +{ + int include = 0; + int i; + + for (i = 0; i < spec->len; i++) { + MonoTraceOperation *op = &spec->ops[i]; + MonoMethodDesc *mdesc; + gboolean is_full; + int inc = 0; + + switch (op->op) { + case MONO_TRACEOP_ALL: + inc = 1; + break; + case MONO_TRACEOP_PROGRAM: + if (prog_assembly && + (method->klass->image == + mono_assembly_get_image (prog_assembly))) + inc = 1; + break; + case MONO_TRACEOP_WRAPPER: + if ((method->wrapper_type == + MONO_WRAPPER_NATIVE_TO_MANAGED) || + (method->wrapper_type == + MONO_WRAPPER_MANAGED_TO_NATIVE)) + inc = 1; + break; + case MONO_TRACEOP_METHOD: + mdesc = op->data; + is_full = mono_method_desc_is_full (mdesc); + if (is_full && + mono_method_desc_full_match (mdesc, method)) + inc = 1; + if (!is_full && mono_method_desc_match (mdesc, method)) + inc = 1; + break; + case MONO_TRACEOP_CLASS: + if (strcmp (method->klass->name_space, op->data) == 0) + if (strcmp (method->klass->name, op->data2) == + 0) + inc = 1; + break; + case MONO_TRACEOP_ASSEMBLY: + if (strcmp (mono_image_get_name (method->klass->image), + op->data) == 0) + inc = 1; + break; + case MONO_TRACEOP_NAMESPACE: + if (strcmp (method->klass->name_space, op->data) == 0) + inc = 1; + break; + case MONO_TRACEOP_EXCEPTION: + break; + } + if (op->exclude) { + if (inc) + include = 0; + } else if (inc) { + include = 1; + } + } + return include; +} + +static int is_filenamechar (char p) +{ + if (p >= 'A' && p <= 'Z') + return TRUE; + if (p >= 'a' && p <= 'z') + return TRUE; + if (p >= '0' && p <= '9') + return TRUE; + if (p == '.' || p == ':' || p == '_' || p == '-' || p == '`') + return TRUE; + return FALSE; +} + +static char *get_string (char **in) +{ + char *start = *in; + char *p = *in; + while (is_filenamechar (*p)) { + p++; + } + size_t len = p - start; + char *ret = (char *)g_malloc (len + 1); + memcpy (ret, start, len); + ret [len] = 0; + *in = p; + return ret; +} + +enum Token { + TOKEN_METHOD, + TOKEN_CLASS, + TOKEN_ALL, + TOKEN_PROGRAM, + TOKEN_EXCEPTION, + TOKEN_NAMESPACE, + TOKEN_WRAPPER, + TOKEN_STRING, + TOKEN_EXCLUDE, + TOKEN_DISABLED, + TOKEN_SEPARATOR, + TOKEN_END, + TOKEN_ERROR +}; + +static int get_token (char **in, char **extra, char **errstr) +{ + char *p = *in; + while (p[0] == '+') + p++; + + *extra = NULL; + + if (p[0] == '\0') { + *in = p; + return TOKEN_END; + } + if (p[0] == 'M' && p[1] == ':') { + p += 2; + *extra = get_string (&p); + *in = p; + return TOKEN_METHOD; + } + if (p[0] == 'N' && p[1] == ':') { + p += 2; + *extra = get_string (&p); + *in = p; + return TOKEN_NAMESPACE; + } + if (p[0] == 'T' && p[1] == ':') { + p += 2; + *extra = get_string (&p); + *in = p; + return TOKEN_CLASS; + } + if (p[0] == 'E' && p[1] == ':') { + p += 2; + *extra = get_string (&p); + *in = p; + return TOKEN_EXCEPTION; + } + if (*p == '-') { + p++; + *in = p; + return TOKEN_EXCLUDE; + } + if (is_filenamechar (*p)) { + *extra = get_string (&p); + *in = p; + if (strcmp (*extra, "all") == 0) + return TOKEN_ALL; + if (strcmp (*extra, "program") == 0) + return TOKEN_PROGRAM; + if (strcmp (*extra, "wrapper") == 0) + return TOKEN_WRAPPER; + if (strcmp (*extra, "disabled") == 0) + return TOKEN_DISABLED; + return TOKEN_STRING; + } + if (*p == ',') { + p++; + *in = p; + return TOKEN_SEPARATOR; + } + + *errstr = g_strdup_printf ("Syntax error at or around '%s'", p); + return TOKEN_ERROR; +} + +static int get_spec (char **in, MonoCallSpec *spec, char **errstr) +{ + int n = spec->len; + char *extra = NULL; + + int token = get_token (in, &extra, errstr); + gboolean exclude = FALSE; + if (token == TOKEN_EXCLUDE) { + exclude = TRUE; + token = get_token (in, &extra, errstr); + if (token == TOKEN_EXCLUDE || token == TOKEN_DISABLED) { + *errstr = g_strdup_printf ("Expecting an expression"); + token = TOKEN_ERROR; + goto out; + } + } + if (token == TOKEN_END || token == TOKEN_SEPARATOR || + token == TOKEN_ERROR) + goto out; + + if (token == TOKEN_DISABLED) { + spec->enabled = FALSE; + goto out; + } + + if (token == TOKEN_METHOD) { + MonoMethodDesc *desc = mono_method_desc_new (extra, TRUE); + if (desc == NULL) { + *errstr = + g_strdup_printf ("Invalid method name: %s", extra); + token = TOKEN_ERROR; + goto out; + } + spec->ops[n].op = MONO_TRACEOP_METHOD; + spec->ops[n].data = desc; + } else if (token == TOKEN_ALL) + spec->ops[n].op = MONO_TRACEOP_ALL; + else if (token == TOKEN_PROGRAM) + spec->ops[n].op = MONO_TRACEOP_PROGRAM; + else if (token == TOKEN_WRAPPER) + spec->ops[n].op = MONO_TRACEOP_WRAPPER; + else if (token == TOKEN_NAMESPACE) { + spec->ops[n].op = MONO_TRACEOP_NAMESPACE; + spec->ops[n].data = g_strdup (extra); + } else if (token == TOKEN_CLASS || token == TOKEN_EXCEPTION) { + char *p = strrchr (extra, '.'); + if (p) { + *p++ = 0; + spec->ops[n].data = g_strdup (extra); + spec->ops[n].data2 = g_strdup (p); + } else { + spec->ops[n].data = g_strdup (""); + spec->ops[n].data2 = g_strdup (extra); + } + spec->ops[n].op = token == TOKEN_CLASS ? MONO_TRACEOP_CLASS + : MONO_TRACEOP_EXCEPTION; + } else if (token == TOKEN_STRING) { + spec->ops[n].op = MONO_TRACEOP_ASSEMBLY; + spec->ops[n].data = g_strdup (extra); + } else { + *errstr = + g_strdup_printf ("Syntax error in method specification"); + token = TOKEN_ERROR; + goto out; + } + + if (exclude) + spec->ops[n].exclude = 1; + + spec->len = n + 1; + token = TOKEN_SEPARATOR; +out: + if (extra != NULL) { + g_free (extra); + } + return token; +} + +gboolean +mono_callspec_parse (const char *options, MonoCallSpec *spec, char **errstr) +{ + char *p = (char *)options; + int size = 1; + int token; + + memset (spec, 0, sizeof (*spec)); + *errstr = NULL; + + spec->enabled = TRUE; + if (*p == 0) { + spec->len = 1; + spec->ops = g_new0 (MonoTraceOperation, 1); + spec->ops[0].op = MONO_TRACEOP_ALL; + return TRUE; + } + + for (p = (char *)options; *p != 0; p++) + if (*p == ',') + size++; + + spec->ops = g_new0 (MonoTraceOperation, size); + + p = (char *)options; + + while ((token = (get_spec (&p, spec, errstr))) != TOKEN_END) { + if (token == TOKEN_ERROR) + return FALSE; + } + return TRUE; +} + +void mono_callspec_cleanup (MonoCallSpec *spec) +{ + if (spec->ops != NULL) { + g_free (spec->ops); + } + memset (spec, 0, sizeof (*spec)); +} + +void +mono_callspec_set_assembly (MonoAssembly *assembly) +{ + prog_assembly = assembly; +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/callspec.h b/unity-2019.4.24f1-mbe/mono/metadata/callspec.h new file mode 100644 index 000000000..c9f699819 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/callspec.h @@ -0,0 +1,47 @@ +/** + * \file + */ + +#ifndef __MONO_CALLSPEC_H__ +#define __MONO_CALLSPEC_H__ +#include +#include + +typedef enum { + MONO_TRACEOP_ALL, + MONO_TRACEOP_PROGRAM, + MONO_TRACEOP_METHOD, + MONO_TRACEOP_ASSEMBLY, + MONO_TRACEOP_CLASS, + MONO_TRACEOP_NAMESPACE, + MONO_TRACEOP_EXCEPTION, + MONO_TRACEOP_WRAPPER, +} MonoTraceOpcode; + +typedef struct { + MonoTraceOpcode op; + int exclude; + void *data, *data2; +} MonoTraceOperation; + +typedef struct { + int len; + gboolean enabled; + MonoTraceOperation *ops; +} MonoCallSpec; + +G_BEGIN_DECLS + +MONO_PROFILER_API gboolean mono_callspec_parse (const char *options, + MonoCallSpec *spec, + char **errstr); +MONO_PROFILER_API void mono_callspec_cleanup (MonoCallSpec *spec); +MONO_PROFILER_API gboolean mono_callspec_eval_exception (MonoClass *klass, + MonoCallSpec *spec); +MONO_PROFILER_API gboolean mono_callspec_eval (MonoMethod *method, + const MonoCallSpec *spec); +void mono_callspec_set_assembly (MonoAssembly *assembly); + +G_END_DECLS + +#endif /* __MONO_CALLSPEC_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/cil-coff.h b/unity-2019.4.24f1-mbe/mono/metadata/cil-coff.h new file mode 100644 index 000000000..d6e966ee8 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/cil-coff.h @@ -0,0 +1,335 @@ +/** + * \file + */ + +#ifndef __MONO_CIL_COFF_H__ +#define __MONO_CIL_COFF_H__ + +#include +#include + +/* + * 25.2.1: Method header type values + */ +#define METHOD_HEADER_FORMAT_MASK 3 +#define METHOD_HEADER_TINY_FORMAT 2 +#define METHOD_HEADER_FAT_FORMAT 3 + +/* + * 25.2.3.1: Flags for method headers + */ +#define METHOD_HEADER_INIT_LOCALS 0x10 +#define METHOD_HEADER_MORE_SECTS 0x08 + +/* + * For section data (25.3) + */ +#define METHOD_HEADER_SECTION_RESERVED 0 +#define METHOD_HEADER_SECTION_EHTABLE 1 +#define METHOD_HEADER_SECTION_OPTIL_TABLE 2 +#define METHOD_HEADER_SECTION_FAT_FORMAT 0x40 +#define METHOD_HEADER_SECTION_MORE_SECTS 0x80 + +/* 128 bytes */ +typedef struct { + char msdos_sig [2]; + guint16 nlast_page; + guint16 npages; + char msdos_header [54]; + guint32 pe_offset; + char msdos_header2 [64]; +} MonoMSDOSHeader; + +/* Possible values for coff_machine */ +#define COFF_MACHINE_I386 332 +#define COFF_MACHINE_IA64 512 +#define COFF_MACHINE_AMD64 34404 +#define COFF_MACHINE_ARM 452 + +/* 20 bytes */ +typedef struct { + guint16 coff_machine; + guint16 coff_sections; + guint32 coff_time; + guint32 coff_symptr; + guint32 coff_symcount; + guint16 coff_opt_header_size; + guint16 coff_attributes; +} MonoCOFFHeader; + +#define COFF_ATTRIBUTE_EXECUTABLE_IMAGE 0x0002 +#define COFF_ATTRIBUTE_LIBRARY_IMAGE 0x2000 + +/* 28 bytes */ +typedef struct { + guint16 pe_magic; + guchar pe_major; + guchar pe_minor; + guint32 pe_code_size; + guint32 pe_data_size; + guint32 pe_uninit_data_size; + guint32 pe_rva_entry_point; + guint32 pe_rva_code_base; + guint32 pe_rva_data_base; +} MonoPEHeader; + +/* 24 bytes */ +typedef struct { + guint16 pe_magic; + guchar pe_major; + guchar pe_minor; + guint32 pe_code_size; + guint32 pe_data_size; + guint32 pe_uninit_data_size; + guint32 pe_rva_entry_point; + guint32 pe_rva_code_base; +} MonoPEHeader64; + +/* 68 bytes */ +typedef struct { + guint32 pe_image_base; /* must be 0x400000 */ + guint32 pe_section_align; /* must be 8192 */ + guint32 pe_file_alignment; /* must be 512 or 4096 */ + guint16 pe_os_major; /* must be 4 */ + guint16 pe_os_minor; /* must be 0 */ + guint16 pe_user_major; + guint16 pe_user_minor; + guint16 pe_subsys_major; + guint16 pe_subsys_minor; + guint32 pe_reserved_1; + guint32 pe_image_size; + guint32 pe_header_size; + guint32 pe_checksum; + guint16 pe_subsys_required; + guint16 pe_dll_flags; + guint32 pe_stack_reserve; + guint32 pe_stack_commit; + guint32 pe_heap_reserve; + guint32 pe_heap_commit; + guint32 pe_loader_flags; + guint32 pe_data_dir_count; +} MonoPEHeaderNT; + +/* 88 bytes */ +typedef struct { + guint64 pe_image_base; + guint32 pe_section_align; /* must be 8192 */ + guint32 pe_file_alignment; /* must be 512 or 4096 */ + guint16 pe_os_major; /* must be 4 */ + guint16 pe_os_minor; /* must be 0 */ + guint16 pe_user_major; + guint16 pe_user_minor; + guint16 pe_subsys_major; + guint16 pe_subsys_minor; + guint32 pe_reserved_1; + guint32 pe_image_size; + guint32 pe_header_size; + guint32 pe_checksum; + guint16 pe_subsys_required; + guint16 pe_dll_flags; + guint64 pe_stack_reserve; + guint64 pe_stack_commit; + guint64 pe_heap_reserve; + guint64 pe_heap_commit; + guint32 pe_loader_flags; + guint32 pe_data_dir_count; +} MonoPEHeaderNT64; + +typedef struct { + guint32 rde_data_offset; + guint32 rde_size; + guint32 rde_codepage; + guint32 rde_reserved; +} MonoPEResourceDataEntry; + +#define MONO_PE_RESOURCE_ID_CURSOR 0x01 +#define MONO_PE_RESOURCE_ID_BITMAP 0x02 +#define MONO_PE_RESOURCE_ID_ICON 0x03 +#define MONO_PE_RESOURCE_ID_MENU 0x04 +#define MONO_PE_RESOURCE_ID_DIALOG 0x05 +#define MONO_PE_RESOURCE_ID_STRING 0x06 +#define MONO_PE_RESOURCE_ID_FONTDIR 0x07 +#define MONO_PE_RESOURCE_ID_FONT 0x08 +#define MONO_PE_RESOURCE_ID_ACCEL 0x09 +#define MONO_PE_RESOURCE_ID_RCDATA 0x0a +#define MONO_PE_RESOURCE_ID_MESSAGETABLE 0x0b +#define MONO_PE_RESOURCE_ID_GROUP_CURSOR 0x0c +#define MONO_PE_RESOURCE_ID_GROUP_ICON 0x0d +#define MONO_PE_RESOURCE_ID_VERSION 0x10 +#define MONO_PE_RESOURCE_ID_DLGINCLUDE 0x11 +#define MONO_PE_RESOURCE_ID_PLUGPLAY 0x13 +#define MONO_PE_RESOURCE_ID_VXD 0x14 +#define MONO_PE_RESOURCE_ID_ANICURSOR 0x15 +#define MONO_PE_RESOURCE_ID_ANIICON 0x16 +#define MONO_PE_RESOURCE_ID_HTML 0x17 +#define MONO_PE_RESOURCE_ID_ASPNET_STRING 0x65 + +typedef struct { + /* If the MSB is set, then the other 31 bits store the RVA of + * the unicode string containing the name. Otherwise, the + * other 31 bits contain the ID of this entry. + */ + guint32 name; + + /* If the MSB is set, then the other 31 bits store the RVA of + * another subdirectory. Otherwise, the other 31 bits store + * the RVA of the resource data entry leaf node. + */ + guint32 dir; +} MonoPEResourceDirEntry; + +#define MONO_PE_RES_DIR_ENTRY_NAME_IS_STRING(d) (GUINT32_FROM_LE((d).name) >> 31) +#define MONO_PE_RES_DIR_ENTRY_NAME_OFFSET(d) (GUINT32_FROM_LE((d).name) & 0x7fffffff) +#define MONO_PE_RES_DIR_ENTRY_SET_NAME(d,i,o) ((d).name = GUINT32_TO_LE(((guint32)((i)?1:0) << 31) | ((o) & 0x7fffffff))) + +#define MONO_PE_RES_DIR_ENTRY_IS_DIR(d) (GUINT32_FROM_LE((d).dir) >> 31) +#define MONO_PE_RES_DIR_ENTRY_DIR_OFFSET(d) (GUINT32_FROM_LE((d).dir) & 0x7fffffff) +#define MONO_PE_RES_DIR_ENTRY_SET_DIR(d,i,o) ((d).dir = GUINT32_TO_LE(((guint32)((i)?1:0) << 31) | ((o) & 0x7fffffff))) + +typedef struct +{ + guint32 res_characteristics; + guint32 res_date_stamp; + guint16 res_major; + guint16 res_minor; + guint16 res_named_entries; + guint16 res_id_entries; + /* Directory entries follow on here. The array is + * res_named_entries + res_id_entries long, containing all + * named entries first. + */ +} MonoPEResourceDir; + +typedef struct { + guint32 rva; + guint32 size; +} MonoPEDirEntry; + +/* 128 bytes */ +typedef struct { + MonoPEDirEntry pe_export_table; + MonoPEDirEntry pe_import_table; + MonoPEDirEntry pe_resource_table; + MonoPEDirEntry pe_exception_table; + MonoPEDirEntry pe_certificate_table; + MonoPEDirEntry pe_reloc_table; + MonoPEDirEntry pe_debug; + MonoPEDirEntry pe_copyright; + MonoPEDirEntry pe_global_ptr; + MonoPEDirEntry pe_tls_table; + MonoPEDirEntry pe_load_config_table; + MonoPEDirEntry pe_bound_import; + MonoPEDirEntry pe_iat; + MonoPEDirEntry pe_delay_import_desc; + MonoPEDirEntry pe_cli_header; + MonoPEDirEntry pe_reserved; +} MonoPEDatadir; + +/* 248 bytes */ +typedef struct { + char pesig [4]; + MonoCOFFHeader coff; + MonoPEHeader pe; + MonoPEHeaderNT nt; + MonoPEDatadir datadir; +} MonoDotNetHeader32; + +/* 248 bytes */ +typedef struct { + char pesig [4]; + MonoCOFFHeader coff; + MonoPEHeader pe; + MonoPEHeaderNT nt; + MonoPEDatadir datadir; +} MonoDotNetHeader; + +/* XX248 bytes */ +typedef struct { + char pesig [4]; + MonoCOFFHeader coff; + MonoPEHeader64 pe; + MonoPEHeaderNT64 nt; + MonoPEDatadir datadir; +} MonoDotNetHeader64; + +#define VTFIXUP_TYPE_32BIT 0x01 +#define VTFIXUP_TYPE_64BIT 0x02 +#define VTFIXUP_TYPE_FROM_UNMANAGED 0x04 +#define VTFIXUP_TYPE_FROM_UNMANAGED_RETAIN_APPDOMAIN 0x08 +#define VTFIXUP_TYPE_CALL_MOST_DERIVED 0x10 + +typedef struct { + guint32 rva; + guint16 count; + guint16 type; +} MonoVTableFixup; + +typedef struct { + char st_name [8]; + guint32 st_virtual_size; + guint32 st_virtual_address; + guint32 st_raw_data_size; + guint32 st_raw_data_ptr; + guint32 st_reloc_ptr; + guint32 st_lineno_ptr; + guint16 st_reloc_count; + guint16 st_line_count; + +#define SECT_FLAGS_HAS_CODE 0x20 +#define SECT_FLAGS_HAS_INITIALIZED_DATA 0x40 +#define SECT_FLAGS_HAS_UNINITIALIZED_DATA 0x80 +#define SECT_FLAGS_MEM_DISCARDABLE 0x02000000 +#define SECT_FLAGS_MEM_NOT_CACHED 0x04000000 +#define SECT_FLAGS_MEM_NOT_PAGED 0x08000000 +#define SECT_FLAGS_MEM_SHARED 0x10000000 +#define SECT_FLAGS_MEM_EXECUTE 0x20000000 +#define SECT_FLAGS_MEM_READ 0x40000000 +#define SECT_FLAGS_MEM_WRITE 0x80000000 + guint32 st_flags; + +} MonoSectionTable; + +typedef struct { + guint32 ch_size; + guint16 ch_runtime_major; + guint16 ch_runtime_minor; + MonoPEDirEntry ch_metadata; + +#define CLI_FLAGS_ILONLY 0x01 +#define CLI_FLAGS_32BITREQUIRED 0x02 +#define CLI_FLAGS_STRONGNAMESIGNED 0x8 +#define CLI_FLAGS_TRACKDEBUGDATA 0x00010000 +#define CLI_FLAGS_PREFERRED32BIT 0x00020000 + guint32 ch_flags; + + guint32 ch_entry_point; + MonoPEDirEntry ch_resources; + MonoPEDirEntry ch_strong_name; + MonoPEDirEntry ch_code_manager_table; + MonoPEDirEntry ch_vtable_fixups; + MonoPEDirEntry ch_export_address_table_jumps; + + /* The following are zero in the current docs */ + MonoPEDirEntry ch_eeinfo_table; + MonoPEDirEntry ch_helper_table; + MonoPEDirEntry ch_dynamic_info; + MonoPEDirEntry ch_delay_load_info; + MonoPEDirEntry ch_module_image; + MonoPEDirEntry ch_external_fixups; + MonoPEDirEntry ch_ridmap; + MonoPEDirEntry ch_debug_map; + MonoPEDirEntry ch_ip_map; +} MonoCLIHeader; + +/* This is not an on-disk structure */ +typedef struct { + MonoDotNetHeader cli_header; + int cli_section_count; + MonoSectionTable *cli_section_tables; + void **cli_sections; + MonoCLIHeader cli_cli_header; +} MonoCLIImageInfo; + +MONO_API guint32 mono_cli_rva_image_map (MonoImage *image, guint32 rva); + +#endif /* __MONO_CIL_COFF_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/class-accessors.c b/unity-2019.4.24f1-mbe/mono/metadata/class-accessors.c new file mode 100644 index 000000000..189cfbba3 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/class-accessors.c @@ -0,0 +1,421 @@ +/** + * \file + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#include +#include +#include + + +typedef enum { + PROP_MARSHAL_INFO = 1, /* MonoMarshalType */ + PROP_REF_INFO_HANDLE = 2, /* gchandle */ + PROP_EXCEPTION_DATA = 3, /* MonoErrorBoxed* */ + PROP_NESTED_CLASSES = 4, /* GList* */ + PROP_PROPERTY_INFO = 5, /* MonoClassPropertyInfo* */ + PROP_EVENT_INFO = 6, /* MonoClassEventInfo* */ + PROP_FIELD_DEF_VALUES = 7, /* MonoFieldDefaultValue* */ + PROP_DECLSEC_FLAGS = 8, /* guint32 */ + PROP_WEAK_BITMAP = 9 +} InfrequentDataKind; + +/* Accessors based on class kind*/ + +/* +* mono_class_get_generic_class: +* +* Return the MonoGenericClass of @klass, which MUST be a generic instance. +*/ +MonoGenericClass* +mono_class_get_generic_class (MonoClass *klass) +{ + g_assert (mono_class_is_ginst (klass)); + return ((MonoClassGenericInst*)klass)->generic_class; +} + +/* +* mono_class_try_get_generic_class: +* +* Return the MonoGenericClass if @klass is a ginst, NULL otherwise +*/ +MonoGenericClass* +mono_class_try_get_generic_class (MonoClass *klass) +{ + if (mono_class_is_ginst (klass)) + return ((MonoClassGenericInst*)klass)->generic_class; + return NULL; +} + +/** + * mono_class_get_flags: + * \param klass the MonoClass to act on + * \returns the \c TypeAttributes flags of \p klass. + * See the \c TYPE_ATTRIBUTE_* definitions in \c tabledefs.h for the different values. + */ +guint32 +mono_class_get_flags (MonoClass *klass) +{ + switch (klass->class_kind) { + case MONO_CLASS_DEF: + case MONO_CLASS_GTD: + return ((MonoClassDef*)klass)->flags; + case MONO_CLASS_GINST: + return mono_class_get_flags (((MonoClassGenericInst*)klass)->generic_class->container_class); + case MONO_CLASS_GPARAM: + return TYPE_ATTRIBUTE_PUBLIC; + case MONO_CLASS_ARRAY: + /* all arrays are marked serializable and sealed, bug #42779 */ + return TYPE_ATTRIBUTE_CLASS | TYPE_ATTRIBUTE_SERIALIZABLE | TYPE_ATTRIBUTE_SEALED | TYPE_ATTRIBUTE_PUBLIC; + case MONO_CLASS_POINTER: + return TYPE_ATTRIBUTE_CLASS | (mono_class_get_flags (klass->element_class) & TYPE_ATTRIBUTE_VISIBILITY_MASK); + } + g_assert_not_reached (); +} + +void +mono_class_set_flags (MonoClass *klass, guint32 flags) +{ + g_assert (klass->class_kind == MONO_CLASS_DEF || klass->class_kind == MONO_CLASS_GTD); + ((MonoClassDef*)klass)->flags = flags; +} + +/* + * mono_class_get_generic_container: + * + * Return the generic container of KLASS which should be a generic type definition. + */ +MonoGenericContainer* +mono_class_get_generic_container (MonoClass *klass) +{ + g_assert (mono_class_is_gtd (klass)); + + return ((MonoClassGtd*)klass)->generic_container; +} + +MonoGenericContainer* +mono_class_try_get_generic_container (MonoClass *klass) +{ + if (mono_class_is_gtd (klass)) + return ((MonoClassGtd*)klass)->generic_container; + return NULL; +} + + +void +mono_class_set_generic_container (MonoClass *klass, MonoGenericContainer *container) +{ + g_assert (mono_class_is_gtd (klass)); + + ((MonoClassGtd*)klass)->generic_container = container; +} + +/* + * mono_class_get_first_method_idx: + * + * Return the table index of the first method for metadata classes. + */ +guint32 +mono_class_get_first_method_idx (MonoClass *klass) +{ + g_assert (mono_class_has_static_metadata (klass)); + + return ((MonoClassDef*)klass)->first_method_idx; +} + +void +mono_class_set_first_method_idx (MonoClass *klass, guint32 idx) +{ + g_assert (mono_class_has_static_metadata (klass)); + + ((MonoClassDef*)klass)->first_method_idx = idx; +} + +guint32 +mono_class_get_first_field_idx (MonoClass *klass) +{ + if (mono_class_is_ginst (klass)) + return mono_class_get_first_field_idx (mono_class_get_generic_class (klass)->container_class); + + g_assert (mono_class_has_static_metadata (klass)); + + return ((MonoClassDef*)klass)->first_field_idx; +} + +void +mono_class_set_first_field_idx (MonoClass *klass, guint32 idx) +{ + g_assert (mono_class_has_static_metadata (klass)); + + ((MonoClassDef*)klass)->first_field_idx = idx; +} + +guint32 +mono_class_get_method_count (MonoClass *klass) +{ + switch (klass->class_kind) { + case MONO_CLASS_DEF: + case MONO_CLASS_GTD: + return ((MonoClassDef*)klass)->method_count; + case MONO_CLASS_GINST: + return mono_class_get_method_count (((MonoClassGenericInst*)klass)->generic_class->container_class); + case MONO_CLASS_GPARAM: + return 0; + case MONO_CLASS_ARRAY: + return ((MonoClassArray*)klass)->method_count; + case MONO_CLASS_POINTER: + return 0; + default: + g_assert_not_reached (); + return 0; + } +} + +void +mono_class_set_method_count (MonoClass *klass, guint32 count) +{ + switch (klass->class_kind) { + case MONO_CLASS_DEF: + case MONO_CLASS_GTD: + ((MonoClassDef*)klass)->method_count = count; + break; + case MONO_CLASS_GINST: + break; + case MONO_CLASS_GPARAM: + case MONO_CLASS_POINTER: + g_assert (count == 0); + break; + case MONO_CLASS_ARRAY: + ((MonoClassArray*)klass)->method_count = count; + break; + default: + g_assert_not_reached (); + break; + } +} + +guint32 +mono_class_get_field_count (MonoClass *klass) +{ + switch (klass->class_kind) { + case MONO_CLASS_DEF: + case MONO_CLASS_GTD: + return ((MonoClassDef*)klass)->field_count; + case MONO_CLASS_GINST: + return mono_class_get_field_count (((MonoClassGenericInst*)klass)->generic_class->container_class); + case MONO_CLASS_GPARAM: + case MONO_CLASS_ARRAY: + case MONO_CLASS_POINTER: + return 0; + default: + g_assert_not_reached (); + return 0; + } +} + +void +mono_class_set_field_count (MonoClass *klass, guint32 count) +{ + switch (klass->class_kind) { + case MONO_CLASS_DEF: + case MONO_CLASS_GTD: + ((MonoClassDef*)klass)->field_count = count; + break; + case MONO_CLASS_GINST: + break; + case MONO_CLASS_GPARAM: + case MONO_CLASS_ARRAY: + case MONO_CLASS_POINTER: + g_assert (count == 0); + break; + default: + g_assert_not_reached (); + break; + } +} + +MonoMarshalType* +mono_class_get_marshal_info (MonoClass *klass) +{ + return mono_property_bag_get (&klass->infrequent_data, PROP_MARSHAL_INFO); +} + +void +mono_class_set_marshal_info (MonoClass *klass, MonoMarshalType *marshal_info) +{ + marshal_info->head.tag = PROP_MARSHAL_INFO; + mono_property_bag_add (&klass->infrequent_data, marshal_info); +} + +typedef struct { + MonoPropertyBagItem head; + guint32 value; +} Uint32Property; + +guint32 +mono_class_get_ref_info_handle (MonoClass *klass) +{ + Uint32Property *prop = mono_property_bag_get (&klass->infrequent_data, PROP_REF_INFO_HANDLE); + return prop ? prop->value : 0; +} + +guint32 +mono_class_set_ref_info_handle (MonoClass *klass, guint32 value) +{ + if (!value) { + Uint32Property *prop = mono_property_bag_get (&klass->infrequent_data, PROP_REF_INFO_HANDLE); + if (prop) + prop->value = 0; + return 0; + } + + Uint32Property *prop = mono_class_alloc (klass, sizeof (Uint32Property)); + prop->head.tag = PROP_REF_INFO_HANDLE; + prop->value = value; + prop = mono_property_bag_add (&klass->infrequent_data, prop); + return prop->value; +} + +typedef struct { + MonoPropertyBagItem head; + gpointer value; +} PointerProperty; + +static void +set_pointer_property (MonoClass *klass, InfrequentDataKind property, gpointer value) +{ + PointerProperty *prop = mono_class_alloc (klass, sizeof (PointerProperty)); + prop->head.tag = property; + prop->value = value; + mono_property_bag_add (&klass->infrequent_data, prop); +} + +static gpointer +get_pointer_property (MonoClass *klass, InfrequentDataKind property) +{ + PointerProperty *prop = (PointerProperty*)mono_property_bag_get (&klass->infrequent_data, property); + return prop ? prop->value : NULL; +} + +MonoErrorBoxed* +mono_class_get_exception_data (MonoClass *klass) +{ + return (MonoErrorBoxed*)get_pointer_property (klass, PROP_EXCEPTION_DATA); +} + +void +mono_class_set_exception_data (MonoClass *klass, MonoErrorBoxed *value) +{ + set_pointer_property (klass, PROP_EXCEPTION_DATA, value); +} + +GList* +mono_class_get_nested_classes_property (MonoClass *klass) +{ + return (GList*)get_pointer_property (klass, PROP_NESTED_CLASSES); +} + +void +mono_class_set_nested_classes_property (MonoClass *klass, GList *value) +{ + set_pointer_property (klass, PROP_NESTED_CLASSES, value); +} + +MonoClassPropertyInfo* +mono_class_get_property_info (MonoClass *klass) +{ + return mono_property_bag_get (&klass->infrequent_data, PROP_PROPERTY_INFO); +} + +void +mono_class_set_property_info (MonoClass *klass, MonoClassPropertyInfo *info) +{ + info->head.tag = PROP_PROPERTY_INFO; + mono_property_bag_add (&klass->infrequent_data, info); +} + +MonoClassEventInfo* +mono_class_get_event_info (MonoClass *klass) +{ + return mono_property_bag_get (&klass->infrequent_data, PROP_EVENT_INFO); +} + +void +mono_class_set_event_info (MonoClass *klass, MonoClassEventInfo *info) +{ + info->head.tag = PROP_EVENT_INFO; + mono_property_bag_add (&klass->infrequent_data, info); +} + +MonoFieldDefaultValue* +mono_class_get_field_def_values (MonoClass *klass) +{ + return (MonoFieldDefaultValue*)get_pointer_property (klass, PROP_FIELD_DEF_VALUES); +} + +void +mono_class_set_field_def_values (MonoClass *klass, MonoFieldDefaultValue *values) +{ + set_pointer_property (klass, PROP_FIELD_DEF_VALUES, values); +} + +guint32 +mono_class_get_declsec_flags (MonoClass *klass) +{ + Uint32Property *prop = mono_property_bag_get (&klass->infrequent_data, PROP_DECLSEC_FLAGS); + return prop ? prop->value : 0; +} + +void +mono_class_set_declsec_flags (MonoClass *klass, guint32 value) +{ + Uint32Property *prop = mono_class_alloc (klass, sizeof (Uint32Property)); + prop->head.tag = PROP_DECLSEC_FLAGS; + prop->value = value; + mono_property_bag_add (&klass->infrequent_data, prop); +} + +void +mono_class_set_is_com_object (MonoClass *klass) +{ +#ifndef DISABLE_COM + mono_loader_lock (); + klass->is_com_object = 1; + mono_loader_unlock (); +#endif +} + +MonoType* +mono_class_gtd_get_canonical_inst (MonoClass *klass) +{ + g_assert (mono_class_is_gtd (klass)); + return &((MonoClassGtd*)klass)->canonical_inst; +} + +typedef struct { + MonoPropertyBagItem head; + + int nbits; + gsize *bits; +} WeakBitmapData; + +void +mono_class_set_weak_bitmap (MonoClass *klass, int nbits, gsize *bits) +{ + WeakBitmapData *info = mono_class_alloc (klass, sizeof (WeakBitmapData)); + info->nbits = nbits; + info->bits = bits; + + info->head.tag = PROP_WEAK_BITMAP; + mono_property_bag_add (&klass->infrequent_data, info); +} + +gsize* +mono_class_get_weak_bitmap (MonoClass *klass, int *nbits) +{ + WeakBitmapData *prop = mono_property_bag_get (&klass->infrequent_data, PROP_WEAK_BITMAP); + + g_assert (prop); + *nbits = prop->nbits; + return prop->bits; +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/class-inlines.h b/unity-2019.4.24f1-mbe/mono/metadata/class-inlines.h new file mode 100644 index 000000000..4b4fb2eb5 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/class-inlines.h @@ -0,0 +1,102 @@ +/** + * \file + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#ifndef __MONO_METADATA_CLASS_INLINES_H__ +#define __MONO_METADATA_CLASS_INLINES_H__ + +#include +#include + +static inline gboolean +mono_class_is_def (MonoClass *klass) +{ + return klass->class_kind == MONO_CLASS_DEF; +} + +static inline gboolean +mono_class_is_gtd (MonoClass *klass) +{ + return klass->class_kind == MONO_CLASS_GTD; +} + +static inline gboolean +mono_class_is_ginst (MonoClass *klass) +{ + return klass->class_kind == MONO_CLASS_GINST; +} + +static inline gboolean +mono_class_is_gparam (MonoClass *klass) +{ + return klass->class_kind == MONO_CLASS_GPARAM; +} + +static inline gboolean +mono_class_is_array (MonoClass *klass) +{ + return klass->class_kind == MONO_CLASS_ARRAY; +} + +static inline gboolean +mono_class_is_pointer (MonoClass *klass) +{ + return klass->class_kind == MONO_CLASS_POINTER; +} + +static inline gboolean +mono_class_is_abstract (MonoClass *klass) +{ + return mono_class_get_flags (klass) & TYPE_ATTRIBUTE_ABSTRACT; +} + +static inline gboolean +mono_class_is_interface (MonoClass *klass) +{ + return mono_class_get_flags (klass) & TYPE_ATTRIBUTE_INTERFACE; +} + +static inline gboolean +mono_class_is_sealed (MonoClass *klass) +{ + return mono_class_get_flags (klass) & TYPE_ATTRIBUTE_SEALED; +} + +static inline gboolean +mono_class_is_before_field_init (MonoClass *klass) +{ + return mono_class_get_flags (klass) & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT; +} + +static inline gboolean +mono_class_is_auto_layout (MonoClass *klass) +{ + return (mono_class_get_flags (klass) & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_AUTO_LAYOUT; +} + +static inline gboolean +mono_class_is_explicit_layout (MonoClass *klass) +{ + return (mono_class_get_flags (klass) & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_EXPLICIT_LAYOUT; +} + +static inline gboolean +mono_class_is_public (MonoClass *klass) +{ + return mono_class_get_flags (klass) & TYPE_ATTRIBUTE_PUBLIC; +} + +static inline gboolean +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/unity-2019.4.24f1-mbe/mono/metadata/class-internals.h b/unity-2019.4.24f1-mbe/mono/metadata/class-internals.h new file mode 100644 index 000000000..20c887c90 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/class-internals.h @@ -0,0 +1,1552 @@ +/** + * \file + * Copyright 2012 Xamarin Inc + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#ifndef __MONO_METADATA_CLASS_INTERNALS_H__ +#define __MONO_METADATA_CLASS_INTERNALS_H__ + +#include +#include +#include +#include +#include +#include "mono/utils/mono-compiler.h" +#include "mono/utils/mono-error.h" +#include "mono/sgen/gc-internal-agnostic.h" + +#define MONO_CLASS_IS_ARRAY(c) ((c)->rank) + +#define MONO_CLASS_HAS_STATIC_METADATA(klass) ((klass)->type_token && !(klass)->image->dynamic && !mono_class_is_ginst (klass)) + +#define MONO_DEFAULT_SUPERTABLE_SIZE 6 + +extern gboolean mono_print_vtable; +extern gboolean mono_align_small_structs; + +typedef struct _MonoMethodWrapper MonoMethodWrapper; +typedef struct _MonoMethodInflated MonoMethodInflated; +typedef struct _MonoMethodPInvoke MonoMethodPInvoke; +typedef struct _MonoDynamicMethod MonoDynamicMethod; + +/* Properties that applies to a group of structs should better use a higher number + * to avoid colision with type specific properties. + * + * This prop applies to class, method, property, event, assembly and image. + */ +#define MONO_PROP_DYNAMIC_CATTR 0x1000 + +#ifdef ENABLE_ICALL_EXPORT +#pragma GCC diagnostic ignored "-Wmissing-prototypes" +#define ICALL_DECL_EXPORT MONO_API +#define ICALL_EXPORT MONO_API +#else +#define ICALL_DECL_EXPORT +#define ICALL_EXPORT static +#endif + +typedef enum { +#define WRAPPER(e,n) MONO_WRAPPER_ ## e, +#include "wrapper-types.h" +#undef WRAPPER + MONO_WRAPPER_NUM +} MonoWrapperType; + +typedef enum { + MONO_TYPE_NAME_FORMAT_IL, + MONO_TYPE_NAME_FORMAT_REFLECTION, + MONO_TYPE_NAME_FORMAT_FULL_NAME, + MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED +} MonoTypeNameFormat; + +typedef enum { + MONO_REMOTING_TARGET_UNKNOWN, + MONO_REMOTING_TARGET_APPDOMAIN, + MONO_REMOTING_TARGET_COMINTEROP +} MonoRemotingTarget; + +#define MONO_METHOD_PROP_GENERIC_CONTAINER 0 + +struct _MonoMethod { + guint16 flags; /* method flags */ + guint16 iflags; /* method implementation flags */ + guint32 token; + MonoClass *klass; /* To what class does this method belong */ + 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; + unsigned int wrapper_type:5; + unsigned int string_ctor:1; + unsigned int save_lmf:1; + unsigned int dynamic:1; /* created & destroyed during runtime */ + unsigned int sre_method:1; /* created at runtime using Reflection.Emit */ + unsigned int is_generic:1; /* whenever this is a generic method definition */ + unsigned int is_inflated:1; /* whether we're a MonoMethodInflated */ + unsigned int skip_visibility:1; /* whenever to skip JIT visibility checks */ + unsigned int verification_success:1; /* whether this method has been verified successfully.*/ + signed int slot : 16; + + /* + * If is_generic is TRUE, the generic_container is stored in image->property_hash, + * using the key MONO_METHOD_PROP_GENERIC_CONTAINER. + */ +}; + +struct _MonoMethodWrapper { + MonoMethod method; + MonoMethodHeader *header; + void *method_data; +}; + +struct _MonoDynamicMethod { + MonoMethodWrapper method; + MonoAssembly *assembly; +}; + +struct _MonoMethodPInvoke { + MonoMethod method; + gpointer addr; + /* add marshal info */ + guint16 piflags; /* pinvoke flags */ + guint32 implmap_idx; /* index into IMPLMAP */ +}; + +/* + * Stores the default value / RVA of fields. + * This information is rarely needed, so it is stored separately from + * MonoClassField. + */ +typedef struct MonoFieldDefaultValue { + /* + * If the field is constant, pointer to the metadata constant + * value. + * If the field has an RVA flag, pointer to the data. + * Else, invalid. + */ + const char *data; + + /* If the field is constant, the type of the constant. */ + MonoTypeEnum def_type; +} MonoFieldDefaultValue; + +/* + * MonoClassField is just a runtime representation of the metadata for + * field, it doesn't contain the data directly. Static fields are + * stored in MonoVTable->data. Instance fields are allocated in the + * objects after the object header. + */ +struct _MonoClassField { + /* Type of the field */ + MonoType *type; + + const char *name; + + /* Type where the field was defined */ + MonoClass *parent; + + /* + * Offset where this field is stored; if it is an instance + * field, it's the offset from the start of the object, if + * it's static, it's from the start of the memory chunk + * allocated for statics for the class. + * For special static fields, this is set to -1 during vtable construction. + */ + int offset; +}; + +/* a field is ignored if it's named "_Deleted" and it has the specialname and rtspecialname flags set */ +#define mono_field_is_deleted(field) (((field)->type->attrs & (FIELD_ATTRIBUTE_SPECIAL_NAME | FIELD_ATTRIBUTE_RT_SPECIAL_NAME)) \ + && (strcmp (mono_field_get_name (field), "_Deleted") == 0)) + +/* a field is ignored if it's named "_Deleted" and it has the specialname and rtspecialname flags set */ +/* Try to avoid loading the field's type */ +#define mono_field_is_deleted_with_flags(field, flags) (((flags) & (FIELD_ATTRIBUTE_SPECIAL_NAME | FIELD_ATTRIBUTE_RT_SPECIAL_NAME)) \ + && (strcmp (mono_field_get_name (field), "_Deleted") == 0)) + +typedef struct { + MonoClassField *field; + guint32 offset; + MonoMarshalSpec *mspec; +} MonoMarshalField; + +typedef struct { + MonoPropertyBagItem head; + + guint32 native_size, min_align; + guint32 num_fields; + MonoMethod *ptr_to_str; + MonoMethod *str_to_ptr; + MonoMarshalField fields [MONO_ZERO_LEN_ARRAY]; +} MonoMarshalType; + +#define MONO_SIZEOF_MARSHAL_TYPE (offsetof (MonoMarshalType, fields)) + +struct _MonoProperty { + MonoClass *parent; + const char *name; + MonoMethod *get; + MonoMethod *set; + guint32 attrs; +}; + +struct _MonoEvent { + MonoClass *parent; + const char *name; + MonoMethod *add; + MonoMethod *remove; + MonoMethod *raise; +#ifndef MONO_SMALL_CONFIG + MonoMethod **other; +#endif + guint32 attrs; +}; + +/* type of exception being "on hold" for later processing (see exception_type) */ +typedef enum { + MONO_EXCEPTION_NONE = 0, + MONO_EXCEPTION_INVALID_PROGRAM = 3, + MONO_EXCEPTION_UNVERIFIABLE_IL = 4, + MONO_EXCEPTION_MISSING_METHOD = 5, + MONO_EXCEPTION_MISSING_FIELD = 6, + MONO_EXCEPTION_TYPE_LOAD = 7, + MONO_EXCEPTION_FILE_NOT_FOUND = 8, + MONO_EXCEPTION_METHOD_ACCESS = 9, + MONO_EXCEPTION_FIELD_ACCESS = 10, + MONO_EXCEPTION_GENERIC_SHARING_FAILED = 11, + MONO_EXCEPTION_BAD_IMAGE = 12, + MONO_EXCEPTION_OBJECT_SUPPLIED = 13, /*The exception object is already created.*/ + MONO_EXCEPTION_OUT_OF_MEMORY = 14, + MONO_EXCEPTION_INLINE_FAILED = 15, + MONO_EXCEPTION_MONO_ERROR = 16, + /* add other exception type */ +} MonoExceptionType; + +/* This struct collects the info needed for the runtime use of a class, + * like the vtables for a domain, the GC descriptor, etc. + */ +typedef struct { + guint16 max_domain; + /* domain_vtables is indexed by the domain id and the size is max_domain + 1 */ + MonoVTable *domain_vtables [MONO_ZERO_LEN_ARRAY]; +} MonoClassRuntimeInfo; + +#define MONO_SIZEOF_CLASS_RUNTIME_INFO (sizeof (MonoClassRuntimeInfo) - MONO_ZERO_LEN_ARRAY * SIZEOF_VOID_P) + +typedef struct { + MonoPropertyBagItem head; + + MonoProperty *properties; + guint32 first, count; + MonoFieldDefaultValue *def_values; +} MonoClassPropertyInfo; + +typedef struct { + MonoPropertyBagItem head; + + /* Initialized by a call to mono_class_setup_events () */ + MonoEvent *events; + guint32 first, count; +} MonoClassEventInfo; + +typedef enum { + MONO_CLASS_DEF = 1, /* non-generic type */ + MONO_CLASS_GTD, /* generic type definition */ + MONO_CLASS_GINST, /* generic instantiation */ + MONO_CLASS_GPARAM, /* generic parameter */ + MONO_CLASS_ARRAY, /* vector or array, bounded or not */ + MONO_CLASS_POINTER, /* pointer of function pointer*/ +} MonoTypeKind; + +struct _MonoClass { + /* element class for arrays and enum basetype for enums */ + MonoClass *element_class; + /* used for subtype checks */ + MonoClass *cast_class; + + /* for fast subtype checks */ + MonoClass **supertypes; + guint16 idepth; + + /* array dimension */ + guint8 rank; + + int instance_size; /* object instance size */ + + guint inited : 1; + + /* A class contains static and non static data. Static data can be + * of the same type as the class itselfs, but it does not influence + * the instance size of the class. To avoid cyclic calls to + * mono_class_init (from mono_class_instance_size ()) we first + * initialise all non static fields. After that we set size_inited + * to 1, because we know the instance size now. After that we + * initialise all static fields. + */ + + /* ALL BITFIELDS SHOULD BE WRITTEN WHILE HOLDING THE LOADER LOCK */ + guint size_inited : 1; + guint valuetype : 1; /* derives from System.ValueType */ + guint enumtype : 1; /* derives from System.Enum */ + guint blittable : 1; /* class is blittable */ + guint unicode : 1; /* class uses unicode char when marshalled */ + guint wastypebuilder : 1; /* class was created at runtime from a TypeBuilder */ + guint is_array_special_interface : 1; /* gtd or ginst of once of the magic interfaces that arrays implement */ + + /* next byte */ + guint8 min_align; + + /* next byte */ + guint packing_size : 4; + guint ghcimpl : 1; /* class has its own GetHashCode impl */ + guint has_finalize : 1; /* class has its own Finalize impl */ +#ifndef DISABLE_REMOTING + guint marshalbyref : 1; /* class is a MarshalByRefObject */ + guint contextbound : 1; /* class is a ContextBoundObject */ +#endif + /* next byte */ + guint delegate : 1; /* class is a Delegate */ + guint gc_descr_inited : 1; /* gc_descr is initialized */ + guint has_cctor : 1; /* class has a cctor */ + guint has_references : 1; /* it has GC-tracked references in the instance */ + guint has_static_refs : 1; /* it has static fields that are GC-tracked */ + guint no_special_static_fields : 1; /* has no thread/context static fields */ + /* directly or indirectly derives from ComImport attributed class. + * this means we need to create a proxy for instances of this class + * for COM Interop. set this flag on loading so all we need is a quick check + * during object creation rather than having to traverse supertypes + */ + guint is_com_object : 1; + guint nested_classes_inited : 1; /* Whenever nested_class is initialized */ + + /* next byte*/ + guint class_kind : 3; /* One of the values from MonoTypeKind */ + guint interfaces_inited : 1; /* interfaces is initialized */ + guint simd_type : 1; /* class is a simd intrinsic type */ + guint has_finalize_inited : 1; /* has_finalize is initialized */ + guint fields_inited : 1; /* setup_fields () has finished */ + guint has_failure : 1; /* See mono_class_get_exception_data () for a MonoErrorBoxed with the details */ + guint has_weak_fields : 1; /* class has weak reference fields */ + + MonoClass *parent; + MonoClass *nested_in; + + MonoImage *image; + const char *name; + const char *name_space; + + guint32 type_token; + int vtable_size; /* number of slots */ + + guint16 interface_count; + guint32 interface_id; /* unique inderface id (for interfaces) */ + guint32 max_interface_id; + + guint16 interface_offsets_count; + MonoClass **interfaces_packed; + guint16 *interface_offsets_packed; +/* enabled only with small config for now: we might want to do it unconditionally */ +#ifdef MONO_SMALL_CONFIG +#define COMPRESSED_INTERFACE_BITMAP 1 +#endif + guint8 *interface_bitmap; + + MonoClass **interfaces; + + union { + int class_size; /* size of area for static fields */ + int element_size; /* for array types */ + int generic_param_token; /* for generic param types, both var and mvar */ + } sizes; + + /* + * Field information: Type and location from object base + */ + MonoClassField *fields; + + MonoMethod **methods; + + /* used as the type of the this argument and when passing the arg by value */ + MonoType this_arg; + MonoType byval_arg; + + MonoGCDescriptor gc_descr; + + MonoClassRuntimeInfo *runtime_info; + + /* Generic vtable. Initialized by a call to mono_class_setup_vtable () */ + MonoMethod **vtable; + + /* Infrequently used items. See class-accessors.c: InfrequentDataKind for what goes into here. */ + MonoPropertyBag infrequent_data; + + void *unity_user_data; +}; + +typedef struct { + MonoClass klass; + guint32 flags; + /* + * From the TypeDef table + */ + guint32 first_method_idx; + guint32 first_field_idx; + guint32 method_count, field_count; + /* next element in the class_cache hash list (in MonoImage) */ + MonoClass *next_class_cache; +} MonoClassDef; + +typedef struct { + MonoClassDef klass; + MonoGenericContainer *generic_container; + /* The canonical GENERICINST where we instantiate a generic type definition with its own generic parameters.*/ + /* Suppose we have class T`2 {...}. canonical_inst is the GTD T`2 applied to A and B. */ + MonoType canonical_inst; +} MonoClassGtd; + +typedef struct { + MonoClass klass; + MonoGenericClass *generic_class; +} MonoClassGenericInst; + +typedef struct { + MonoClass klass; +} MonoClassGenericParam; + +typedef struct { + MonoClass klass; + guint32 method_count; +} MonoClassArray; + +typedef struct { + MonoClass klass; +} MonoClassPointer; + +#ifdef COMPRESSED_INTERFACE_BITMAP +int mono_compress_bitmap (uint8_t *dest, const uint8_t *bitmap, int size); +int mono_class_interface_match (const uint8_t *bitmap, int id); +#else +#define mono_class_interface_match(bmap,uiid) ((bmap) [(uiid) >> 3] & (1 << ((uiid)&7))) +#endif + +#define MONO_CLASS_IMPLEMENTS_INTERFACE(k,uiid) (((uiid) <= (k)->max_interface_id) && mono_class_interface_match ((k)->interface_bitmap, (uiid))) + +#define MONO_VTABLE_AVAILABLE_GC_BITS 4 + +#ifdef DISABLE_REMOTING +#define mono_class_is_marshalbyref(klass) (FALSE) +#define mono_class_is_contextbound(klass) (FALSE) +#define mono_vtable_is_remote(vtable) (FALSE) +#define mono_vtable_set_is_remote(vtable,enable) do {} while (0) +#else +#define mono_class_is_marshalbyref(klass) ((klass)->marshalbyref) +#define mono_class_is_contextbound(klass) ((klass)->contextbound) +#define mono_vtable_is_remote(vtable) ((vtable)->remote) +#define mono_vtable_set_is_remote(vtable,enable) do { (vtable)->remote = enable ? 1 : 0; } while (0) +#endif + +#ifdef DISABLE_COM +#define mono_class_is_com_object(klass) (FALSE) +#else +#define mono_class_is_com_object(klass) ((klass)->is_com_object) +#endif + + +MONO_API int mono_class_interface_offset (MonoClass *klass, MonoClass *itf); +int mono_class_interface_offset_with_variance (MonoClass *klass, MonoClass *itf, gboolean *non_exact_match); + +typedef gpointer MonoRuntimeGenericContext; + +/* the interface_offsets array is stored in memory before this struct */ +struct MonoVTable { + MonoClass *klass; + /* + * According to comments in gc_gcj.h, this should be the second word in + * the vtable. + */ + MonoGCDescriptor gc_descr; + MonoDomain *domain; /* each object/vtable belongs to exactly one domain */ + gpointer type; /* System.Type type for klass */ + guint8 *interface_bitmap; + guint32 max_interface_id; + guint8 rank; + /* Keep this a guint8, the jit depends on it */ + guint8 initialized; /* cctor has been run */ + guint remote : 1; /* class is remotely activated */ + guint init_failed : 1; /* cctor execution failed */ + guint has_static_fields : 1; /* pointer to the data stored at the end of the vtable array */ + guint gc_bits : MONO_VTABLE_AVAILABLE_GC_BITS; /* Those bits are reserved for the usaged of the GC */ + + guint32 imt_collisions_bitmap; + MonoRuntimeGenericContext *runtime_generic_context; + /* do not add any fields after vtable, the structure is dynamically extended */ + /* vtable contains function pointers to methods or their trampolines, at the + end there may be a slot containing the pointer to the static fields */ + gpointer vtable [MONO_ZERO_LEN_ARRAY]; +}; + +#define MONO_SIZEOF_VTABLE (sizeof (MonoVTable) - MONO_ZERO_LEN_ARRAY * SIZEOF_VOID_P) + +#define MONO_VTABLE_IMPLEMENTS_INTERFACE(vt,uiid) (((uiid) <= (vt)->max_interface_id) && mono_class_interface_match ((vt)->interface_bitmap, (uiid))) + +/* + * Generic instantiation data type encoding. + */ + +/* + * A particular generic instantiation: + * + * All instantiations are cached and we don't distinguish between class and method + * instantiations here. + */ +struct _MonoGenericInst { +#ifndef MONO_SMALL_CONFIG + gint32 id; /* unique ID for debugging */ +#endif + guint type_argc : 22; /* number of type arguments */ + guint is_open : 1; /* if this is an open type */ + MonoType *type_argv [MONO_ZERO_LEN_ARRAY]; +}; + +#define MONO_SIZEOF_GENERIC_INST (sizeof (MonoGenericInst) - MONO_ZERO_LEN_ARRAY * SIZEOF_VOID_P) +/* + * The generic context: an instantiation of a set of class and method generic parameters. + * + * NOTE: Never allocate this directly on the heap. It have to be either allocated on the stack, + * or embedded within other objects. Don't store pointers to this, because it may be on the stack. + * If you really have to, ensure you store a pointer to the embedding object along with it. + */ +struct _MonoGenericContext { + /* The instantiation corresponding to the class generic parameters */ + MonoGenericInst *class_inst; + /* The instantiation corresponding to the method generic parameters */ + MonoGenericInst *method_inst; +}; + +/* + * Inflated generic method. + */ +struct _MonoMethodInflated { + union { + MonoMethod method; + MonoMethodPInvoke pinvoke; + } method; + MonoMethod *declaring; /* the generic method definition. */ + MonoGenericContext context; /* The current instantiation */ + MonoImageSet *owner; /* The image set that the inflated method belongs to. */ +}; + +/* + * A particular instantiation of a generic type. + */ +struct _MonoGenericClass { + MonoClass *container_class; /* the generic type definition */ + MonoGenericContext context; /* a context that contains the type instantiation doesn't contain any method instantiation */ /* FIXME: Only the class_inst member of "context" is ever used, so this field could be replaced with just a monogenericinst */ + guint is_dynamic : 1; /* Contains dynamic types */ + guint is_tb_open : 1; /* This is the fully open instantiation for a type_builder. Quite ugly, but it's temporary.*/ + guint need_sync : 1; /* Only if dynamic. Need to be synchronized with its container class after its finished. */ + MonoClass *cached_class; /* if present, the MonoClass corresponding to the instantiation. */ + + /* + * The image set which owns this generic class. Memory owned by the generic class + * including cached_class should be allocated from the mempool of the image set, + * so it is easy to free. + */ + MonoImageSet *owner; +}; + +/* + * A type parameter. + */ +struct _MonoGenericParam { + /* + * Type or method this parameter was defined in. + */ + MonoGenericContainer *owner; + guint16 num; + /* + * If != NULL, this is a generated generic param used by the JIT to implement generic + * sharing. + */ + MonoType *gshared_constraint; +}; + +/* Additional details about a MonoGenericParam */ +/* Keep in sync with managed Mono.RuntimeStructs.GenericParamInfo */ +typedef struct { + MonoClass *pklass; /* The corresponding `MonoClass'. */ + const char *name; + + // See GenericParameterAttributes + guint16 flags; + + guint32 token; + + // Constraints on type parameters + MonoClass** constraints; /* NULL means end of list */ +} MonoGenericParamInfo; + +typedef struct { + MonoGenericParam param; + MonoGenericParamInfo info; +} MonoGenericParamFull; + +/* + * The generic container. + * + * Stores the type parameters of a generic type definition or a generic method definition. + */ +struct _MonoGenericContainer { + MonoGenericContext context; + /* If we're a generic method definition in a generic type definition, + the generic container of the containing class. */ + MonoGenericContainer *parent; + /* the generic type definition or the generic method definition corresponding to this container */ + /* Union rules: If is_anonymous, image field is valid; else if is_method, method field is valid; else klass is valid. */ + union { + MonoClass *klass; + MonoMethod *method; + MonoImage *image; + } owner; + int type_argc : 29; // Per the ECMA spec, this value is capped at 16 bits + /* If true, we're a generic method, otherwise a generic type definition. */ + /* Invariant: parent != NULL => is_method */ + int is_method : 1; + /* If true, this container has no associated class/method and only the image is known. This can happen: + 1. For the special anonymous containers kept by MonoImage. + 2. During container creation via the mono_metadata_load_generic_params path-- in this case the caller + sets the owner, so temporarily while load_generic_params is completing the container is anonymous. + 3. When user code creates a generic parameter via SRE, but has not yet set an owner. */ + int is_anonymous : 1; + /* If false, all params in this container are full-size. If true, all params are just param structs. */ + /* This field is always == to the is_anonymous field, except in "temporary" cases (2) and (3) above. */ + /* TODO: Merge GenericParam and GenericParamFull, remove this field. Benefit is marginal. */ + int is_small_param : 1; + /* Our type parameters. */ + MonoGenericParamFull *type_params; +}; + +static inline MonoGenericParam * +mono_generic_container_get_param (MonoGenericContainer *gc, int i) +{ + return (MonoGenericParam *) &gc->type_params [i]; +} + +static inline MonoGenericParamInfo * +mono_generic_container_get_param_info (MonoGenericContainer *gc, int i) +{ + return &gc->type_params [i].info; +} + +static inline MonoGenericContainer * +mono_generic_param_owner (MonoGenericParam *p) +{ + return p->owner; +} + +static inline int +mono_generic_param_num (MonoGenericParam *p) +{ + return p->num; +} + +static inline gboolean +mono_generic_param_is_fullsize (MonoGenericParam *p) +{ + return !mono_generic_param_owner (p)->is_small_param; +} + +static inline MonoGenericParamInfo * +mono_generic_param_info (MonoGenericParam *p) +{ + if (mono_generic_param_is_fullsize (p)) + return &((MonoGenericParamFull *) p)->info; + return NULL; +} + +static inline const char * +mono_generic_param_name (MonoGenericParam *p) +{ + if (mono_generic_param_is_fullsize (p)) + return ((MonoGenericParamFull *) p)->info.name; + return NULL; +} + +static inline MonoGenericContainer * +mono_type_get_generic_param_owner (MonoType *t) +{ + return mono_generic_param_owner (t->data.generic_param); +} + +static inline int +mono_type_get_generic_param_num (MonoType *t) +{ + return mono_generic_param_num (t->data.generic_param); +} + +/* + * Class information which might be cached by the runtime in the AOT file for + * example. Caching this allows us to avoid computing a generic vtable + * (class->vtable) in most cases, saving time and avoiding creation of lots of + * MonoMethod structures. + */ +typedef struct MonoCachedClassInfo { + guint32 vtable_size; + guint has_finalize : 1; + guint ghcimpl : 1; + guint has_cctor : 1; + guint has_nested_classes : 1; + guint blittable : 1; + guint has_references : 1; + guint has_static_refs : 1; + guint no_special_static_fields : 1; + guint is_generic_container : 1; + guint has_weak_fields : 1; + guint32 cctor_token; + MonoImage *finalize_image; + guint32 finalize_token; + guint32 instance_size; + guint32 class_size; + guint32 packing_size; + guint32 min_align; +} MonoCachedClassInfo; + +typedef struct { + const char *name; + gconstpointer func; + gconstpointer wrapper; + gconstpointer trampoline; + MonoMethodSignature *sig; + const char *c_symbol; + MonoMethod *wrapper_method; +} MonoJitICallInfo; + +void +mono_class_setup_supertypes (MonoClass *klass); + +void +mono_class_setup_fields (MonoClass *klass); + +/* WARNING + * Only call this function if you can ensure both @klass and @parent + * have supertype information initialized. + * This can be accomplished by mono_class_setup_supertypes or mono_class_init. + * If unsure, use mono_class_has_parent. + */ +static inline gboolean +mono_class_has_parent_fast (MonoClass *klass, MonoClass *parent) +{ + return (klass->idepth >= parent->idepth) && (klass->supertypes [parent->idepth - 1] == parent); +} + +static inline gboolean +mono_class_has_parent (MonoClass *klass, MonoClass *parent) +{ + if (G_UNLIKELY (!klass->supertypes)) + mono_class_setup_supertypes (klass); + + if (G_UNLIKELY (!parent->supertypes)) + mono_class_setup_supertypes (parent); + + return mono_class_has_parent_fast (klass, parent); +} + +typedef struct { + MonoVTable *default_vtable; + MonoVTable *xdomain_vtable; + MonoClass *proxy_class; + char* proxy_class_name; + uint32_t interface_count; + MonoClass *interfaces [MONO_ZERO_LEN_ARRAY]; +} MonoRemoteClass; + +#define MONO_SIZEOF_REMOTE_CLASS (sizeof (MonoRemoteClass) - MONO_ZERO_LEN_ARRAY * SIZEOF_VOID_P) + +typedef struct { + gint32 initialized_class_count; + gint32 generic_vtable_count; + gint32 used_class_count; + gint32 method_count; + gint32 class_vtable_size; + gint32 class_static_data_size; + gint32 generic_class_count; + gint32 inflated_method_count; + gint32 inflated_type_count; + gint32 delegate_creations; + gint32 imt_tables_size; + gint32 imt_number_of_tables; + gint32 imt_number_of_methods; + gint32 imt_used_slots; + gint32 imt_slots_with_collisions; + gint32 imt_max_collisions_in_slot; + gint32 imt_method_count_when_max_collisions; + gint32 imt_trampolines_size; + gint32 jit_info_table_insert_count; + gint32 jit_info_table_remove_count; + gint32 jit_info_table_lookup_count; + gint32 generics_sharable_methods; + gint32 generics_unsharable_methods; + gint32 generics_shared_methods; + gint32 gsharedvt_methods; + gboolean enabled; +} MonoStats; + +/* + * new structure to hold performace counters values that are exported + * to managed code. + * Note: never remove fields from this structure and only add them to the end. + * Size of fields and type should not be changed as well. + */ +typedef struct { + /* JIT category */ + gint32 jit_methods; + gint32 jit_bytes; + gint32 jit_time; + gint32 jit_failures; + /* Exceptions category */ + gint32 exceptions_thrown; + gint32 exceptions_filters; + gint32 exceptions_finallys; + gint32 exceptions_depth; + gint32 aspnet_requests_queued; + gint32 aspnet_requests; + /* Memory category */ + gint32 gc_collections0; + gint32 gc_collections1; + gint32 gc_collections2; + gint32 gc_promotions0; + gint32 gc_promotions1; + gint32 gc_promotion_finalizers; + gint64 gc_gen0size; + gint64 gc_gen1size; + gint64 gc_gen2size; + gint32 gc_lossize; + gint32 gc_fin_survivors; + gint32 gc_num_handles; + gint32 gc_allocated; + gint32 gc_induced; + gint32 gc_time; + gint64 gc_total_bytes; + gint64 gc_committed_bytes; + gint64 gc_reserved_bytes; + gint32 gc_num_pinned; + gint32 gc_sync_blocks; + /* Remoting category */ + gint32 remoting_calls; + gint32 remoting_channels; + gint32 remoting_proxies; + gint32 remoting_classes; + gint32 remoting_objects; + gint32 remoting_contexts; + /* Loader category */ + gint32 loader_classes; + gint32 loader_total_classes; + gint32 loader_appdomains; + gint32 loader_total_appdomains; + gint32 loader_assemblies; + gint32 loader_total_assemblies; + gint32 loader_failures; + gint32 loader_bytes; + gint32 loader_appdomains_uloaded; + /* Threads and Locks category */ + gint32 thread_contentions; + gint32 thread_queue_len; + gint32 thread_queue_max; + gint32 thread_num_logical; + gint32 thread_num_physical; + gint32 thread_cur_recognized; + gint32 thread_num_recognized; + /* Interop category */ + gint32 interop_num_ccw; + gint32 interop_num_stubs; + gint32 interop_num_marshals; + /* Security category */ + gint32 security_num_checks; + gint32 security_num_link_checks; + gint32 security_time; + gint32 security_depth; + gint32 unused; + /* Threadpool */ + gint64 threadpool_workitems; + gint64 threadpool_ioworkitems; + gint32 threadpool_threads; + gint32 threadpool_iothreads; +} MonoPerfCounters; + +extern MonoPerfCounters *mono_perfcounters; + +MONO_API void mono_perfcounters_init (void); + +/* + * The definition of the first field in SafeHandle, + * Keep in sync with SafeHandle.cs, this is only used + * to access the `handle' parameter. + */ +typedef struct { + MonoObject base; + void *handle; +} MonoSafeHandle; + +/* + * Keep in sync with HandleRef.cs + */ +typedef struct { + MonoObject *wrapper; + void *handle; +} MonoHandleRef; + +extern MonoStats mono_stats; + +typedef gboolean (*MonoGetCachedClassInfo) (MonoClass *klass, MonoCachedClassInfo *res); + +typedef gboolean (*MonoGetClassFromName) (MonoImage *image, const char *name_space, const char *name, MonoClass **res); + +static inline gboolean +method_is_dynamic (MonoMethod *method) +{ +#ifdef DISABLE_REFLECTION_EMIT + return FALSE; +#else + return method->dynamic; +#endif +} + +void +mono_classes_init (void); + +void +mono_classes_cleanup (void); + +void +mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_size, gboolean sre); + +void +mono_class_setup_interface_offsets (MonoClass *klass); + +void +mono_class_setup_vtable_general (MonoClass *klass, MonoMethod **overrides, int onum, GList *in_setup); + +void +mono_class_setup_vtable (MonoClass *klass); + +void +mono_class_setup_methods (MonoClass *klass); + +void +mono_class_setup_mono_type (MonoClass *klass); + +void +mono_class_setup_parent (MonoClass *klass, MonoClass *parent); + +MonoMethod* +mono_class_get_method_by_index (MonoClass *klass, int index); + +MonoMethod* +mono_class_get_inflated_method (MonoClass *klass, MonoMethod *method); + +MonoMethod* +mono_class_get_vtable_entry (MonoClass *klass, int offset); + +GPtrArray* +mono_class_get_implemented_interfaces (MonoClass *klass, MonoError *error); + +int +mono_class_get_vtable_size (MonoClass *klass); + +gboolean +mono_class_is_open_constructed_type (MonoType *t); + +void +mono_class_get_overrides_full (MonoImage *image, guint32 type_token, MonoMethod ***overrides, gint32 *num_overrides, MonoGenericContext *generic_context, MonoError *error); + +MonoMethod* +mono_class_get_cctor (MonoClass *klass) MONO_LLVM_INTERNAL; + +MonoMethod* +mono_class_get_finalizer (MonoClass *klass); + +gboolean +mono_class_needs_cctor_run (MonoClass *klass, MonoMethod *caller); + +gboolean +mono_class_field_is_special_static (MonoClassField *field); + +guint32 +mono_class_field_get_special_static_type (MonoClassField *field); + +gboolean +mono_class_has_special_static_fields (MonoClass *klass); + +const char* +mono_class_get_field_default_value (MonoClassField *field, MonoTypeEnum *def_type); + +const char* +mono_class_get_property_default_value (MonoProperty *property, MonoTypeEnum *def_type); + +gpointer +mono_lookup_dynamic_token (MonoImage *image, guint32 token, MonoGenericContext *context, MonoError *error); + +gpointer +mono_lookup_dynamic_token_class (MonoImage *image, guint32 token, gboolean check_token, MonoClass **handle_class, MonoGenericContext *context, MonoError *error); + +gpointer +mono_runtime_create_jump_trampoline (MonoDomain *domain, MonoMethod *method, gboolean add_sync_wrapper, MonoError *error); + +gpointer +mono_runtime_create_delegate_trampoline (MonoClass *klass); + +void +mono_install_get_cached_class_info (MonoGetCachedClassInfo func); + +void +mono_install_get_class_from_name (MonoGetClassFromName func); + +MONO_PROFILER_API MonoGenericContext* +mono_class_get_context (MonoClass *klass); + +MONO_PROFILER_API MonoMethodSignature* +mono_method_signature_checked (MonoMethod *m, MonoError *err); + +MonoGenericContext* +mono_method_get_context_general (MonoMethod *method, gboolean uninflated); + +MONO_PROFILER_API MonoGenericContext* +mono_method_get_context (MonoMethod *method); + +/* Used by monodis, thus cannot be MONO_INTERNAL */ +MONO_API MonoGenericContainer* +mono_method_get_generic_container (MonoMethod *method); + +MonoGenericContext* +mono_generic_class_get_context (MonoGenericClass *gclass); + +MonoClass* +mono_generic_class_get_class (MonoGenericClass *gclass); + +void +mono_method_set_generic_container (MonoMethod *method, MonoGenericContainer* container); + +MonoMethod* +mono_class_inflate_generic_method_full (MonoMethod *method, MonoClass *klass_hint, MonoGenericContext *context); + +MonoMethod* +mono_class_inflate_generic_method_full_checked (MonoMethod *method, MonoClass *klass_hint, MonoGenericContext *context, MonoError *error); + +MonoMethod * +mono_class_inflate_generic_method_checked (MonoMethod *method, MonoGenericContext *context, MonoError *error); + +MonoImageSet * +mono_metadata_get_image_set_for_class (MonoClass *klass); + +MonoImageSet * +mono_metadata_get_image_set_for_method (MonoMethodInflated *method); + +MONO_API MonoMethodSignature * +mono_metadata_get_inflated_signature (MonoMethodSignature *sig, MonoGenericContext *context); + +MonoType* +mono_class_inflate_generic_type_with_mempool (MonoImage *image, MonoType *type, MonoGenericContext *context, MonoError *error); + +MonoType* +mono_class_inflate_generic_type_checked (MonoType *type, MonoGenericContext *context, MonoError *error); + +MONO_API void +mono_metadata_free_inflated_signature (MonoMethodSignature *sig); + +MonoMethodSignature* +mono_inflate_generic_signature (MonoMethodSignature *sig, MonoGenericContext *context, MonoError *error); + +typedef struct { + MonoImage *corlib; + MonoClass *object_class; + MonoClass *byte_class; + MonoClass *void_class; + MonoClass *boolean_class; + MonoClass *sbyte_class; + MonoClass *int16_class; + MonoClass *uint16_class; + MonoClass *int32_class; + MonoClass *uint32_class; + MonoClass *int_class; + MonoClass *uint_class; + MonoClass *int64_class; + MonoClass *uint64_class; + MonoClass *single_class; + MonoClass *double_class; + MonoClass *char_class; + MonoClass *string_class; + MonoClass *enum_class; + MonoClass *array_class; + MonoClass *delegate_class; + MonoClass *multicastdelegate_class; + MonoClass *asyncresult_class; + MonoClass *manualresetevent_class; + MonoClass *typehandle_class; + MonoClass *fieldhandle_class; + MonoClass *methodhandle_class; + MonoClass *systemtype_class; + MonoClass *runtimetype_class; + MonoClass *exception_class; + MonoClass *threadabortexception_class; + MonoClass *thread_class; + MonoClass *internal_thread_class; +#ifndef DISABLE_REMOTING + MonoClass *transparent_proxy_class; + MonoClass *real_proxy_class; + MonoClass *marshalbyrefobject_class; + MonoClass *iremotingtypeinfo_class; +#endif + MonoClass *mono_method_message_class; + MonoClass *appdomain_class; + MonoClass *field_info_class; + MonoClass *method_info_class; + MonoClass *stringbuilder_class; + MonoClass *math_class; + MonoClass *stack_frame_class; + MonoClass *stack_trace_class; + MonoClass *marshal_class; + MonoClass *typed_reference_class; + MonoClass *argumenthandle_class; + MonoClass *monitor_class; + MonoClass *generic_ilist_class; + MonoClass *generic_nullable_class; + MonoClass *handleref_class; + MonoClass *attribute_class; + MonoClass *customattribute_data_class; + MonoClass *critical_finalizer_object; /* MAYBE NULL */ + MonoClass *generic_ireadonlylist_class; + MonoClass *threadpool_wait_callback_class; + MonoMethod *threadpool_perform_wait_callback_method; +} MonoDefaults; + +#ifdef DISABLE_REMOTING +#define mono_class_is_transparent_proxy(klass) (FALSE) +#define mono_class_is_real_proxy(klass) (FALSE) +#else +#define mono_class_is_transparent_proxy(klass) ((klass) == mono_defaults.transparent_proxy_class) +#define mono_class_is_real_proxy(klass) ((klass) == mono_defaults.real_proxy_class) +#endif + +#define mono_object_is_transparent_proxy(object) (mono_class_is_transparent_proxy (mono_object_class (object))) + + +#define GENERATE_GET_CLASS_WITH_CACHE_DECL(shortname) \ +MonoClass* mono_class_get_##shortname##_class (void); + +#define GENERATE_TRY_GET_CLASS_WITH_CACHE_DECL(shortname) \ +MonoClass* mono_class_try_get_##shortname##_class (void); + +#define GENERATE_GET_CLASS_WITH_CACHE(shortname,name_space,name) \ +MonoClass* \ +mono_class_get_##shortname##_class (void) \ +{ \ + static MonoClass *tmp_class; \ + MonoClass *klass = tmp_class; \ + if (!klass) { \ + klass = mono_class_load_from_name (mono_defaults.corlib, name_space, name); \ + mono_memory_barrier (); \ + tmp_class = klass; \ + } \ + return klass; \ +} + +#define GENERATE_TRY_GET_CLASS_WITH_CACHE(shortname,name_space,name) \ +MonoClass* \ +mono_class_try_get_##shortname##_class (void) \ +{ \ + static volatile MonoClass *tmp_class; \ + static volatile gboolean inited; \ + MonoClass *klass = (MonoClass *)tmp_class; \ + mono_memory_barrier (); \ + if (!inited) { \ + klass = mono_class_try_load_from_name (mono_defaults.corlib, name_space, name); \ + tmp_class = klass; \ + mono_memory_barrier (); \ + inited = TRUE; \ + } \ + return klass; \ +} + +GENERATE_TRY_GET_CLASS_WITH_CACHE_DECL (safehandle) + +#ifndef DISABLE_COM + +GENERATE_GET_CLASS_WITH_CACHE_DECL (interop_proxy) +GENERATE_GET_CLASS_WITH_CACHE_DECL (idispatch) +GENERATE_GET_CLASS_WITH_CACHE_DECL (iunknown) +GENERATE_GET_CLASS_WITH_CACHE_DECL (com_object) +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; + +void +mono_loader_init (void); + +void +mono_loader_cleanup (void); + +void +mono_loader_lock (void) MONO_LLVM_INTERNAL; + +void +mono_loader_unlock (void) MONO_LLVM_INTERNAL; + +void +mono_loader_lock_track_ownership (gboolean track); + +gboolean +mono_loader_lock_is_owned_by_self (void); + +void +mono_loader_lock_if_inited (void); + +void +mono_loader_unlock_if_inited (void); + +void +mono_reflection_init (void); + +void +mono_icall_init (void); + +void +mono_icall_cleanup (void); + +gpointer +mono_method_get_wrapper_data (MonoMethod *method, guint32 id); + +gboolean +mono_metadata_has_generic_params (MonoImage *image, guint32 token); + +MONO_API MonoGenericContainer * +mono_metadata_load_generic_params (MonoImage *image, guint32 token, + MonoGenericContainer *parent_container); + +MONO_API gboolean +mono_metadata_load_generic_param_constraints_checked (MonoImage *image, guint32 token, + MonoGenericContainer *container, MonoError *error); + +MonoMethodSignature* +mono_create_icall_signature (const char *sigstr); + +MonoJitICallInfo * +mono_register_jit_icall (gconstpointer func, const char *name, MonoMethodSignature *sig, gboolean is_save); + +MonoJitICallInfo * +mono_register_jit_icall_full (gconstpointer func, const char *name, MonoMethodSignature *sig, gboolean no_wrapper, const char *c_symbol); + +void +mono_register_jit_icall_wrapper (MonoJitICallInfo *info, gconstpointer wrapper); + +MonoJitICallInfo * +mono_find_jit_icall_by_name (const char *name) MONO_LLVM_INTERNAL; + +MonoJitICallInfo * +mono_find_jit_icall_by_addr (gconstpointer addr) MONO_LLVM_INTERNAL; + +GHashTable* +mono_get_jit_icall_info (void); + +const char* +mono_lookup_jit_icall_symbol (const char *name); + +gboolean +mono_class_set_type_load_failure (MonoClass *klass, const char * fmt, ...) MONO_ATTR_FORMAT_PRINTF(2,3); + +MonoException* +mono_class_get_exception_for_failure (MonoClass *klass); + +UNITY_MONO_API char* +mono_type_get_name_full (MonoType *type, MonoTypeNameFormat format); + +char* +mono_type_get_full_name (MonoClass *klass); + +char * +mono_method_get_name_full (MonoMethod *method, gboolean signature, gboolean ret, MonoTypeNameFormat format); + +char * +mono_method_get_full_name (MonoMethod *method); + +MonoArrayType *mono_dup_array_type (MonoImage *image, MonoArrayType *a); +MonoMethodSignature *mono_metadata_signature_deep_dup (MonoImage *image, MonoMethodSignature *sig); + +MONO_API void +mono_image_init_name_cache (MonoImage *image); + +gboolean mono_class_is_nullable (MonoClass *klass); +MonoClass *mono_class_get_nullable_param (MonoClass *klass); + +/* object debugging functions, for use inside gdb */ +MONO_API void mono_object_describe (MonoObject *obj); +MONO_API void mono_object_describe_fields (MonoObject *obj); +MONO_API void mono_value_describe_fields (MonoClass* klass, const char* addr); +MONO_API void mono_class_describe_statics (MonoClass* klass); + +/* method debugging functions, for use inside gdb */ +MONO_API void mono_method_print_code (MonoMethod *method); + +MONO_PROFILER_API char *mono_signature_full_name (MonoMethodSignature *sig); + +/*Enum validation related functions*/ +MONO_API gboolean +mono_type_is_valid_enum_basetype (MonoType * type); + +MONO_API gboolean +mono_class_is_valid_enum (MonoClass *klass); + +MONO_PROFILER_API gboolean +mono_type_is_primitive (MonoType *type); + +MonoType * +mono_type_get_checked (MonoImage *image, guint32 type_token, MonoGenericContext *context, MonoError *error); + +gboolean +mono_generic_class_is_generic_type_definition (MonoGenericClass *gklass); + +MonoMethod* +mono_class_get_method_generic (MonoClass *klass, MonoMethod *method); + +MonoType* +mono_type_get_basic_type_from_generic (MonoType *type); + +gboolean +mono_method_can_access_method_full (MonoMethod *method, MonoMethod *called, MonoClass *context_klass); + +gboolean +mono_method_can_access_field_full (MonoMethod *method, MonoClassField *field, MonoClass *context_klass); + +gboolean +mono_class_can_access_class (MonoClass *access_class, MonoClass *target_class); + +MonoClass * +mono_class_get_generic_type_definition (MonoClass *klass); + +gboolean +mono_class_has_parent_and_ignore_generics (MonoClass *klass, MonoClass *parent); + +int +mono_method_get_vtable_slot (MonoMethod *method); + +int +mono_method_get_vtable_index (MonoMethod *method); + +MonoMethod* +mono_method_get_base_method (MonoMethod *method, gboolean definition, MonoError *error); + +MonoMethod* +mono_method_search_in_array_class (MonoClass *klass, const char *name, MonoMethodSignature *sig); + +void +mono_class_setup_interface_id (MonoClass *klass); + +MonoGenericContainer* +mono_class_get_generic_container (MonoClass *klass); + +gpointer +mono_class_alloc (MonoClass *klass, int size); + +gpointer +mono_class_alloc0 (MonoClass *klass, int size); + +void +mono_class_setup_interfaces (MonoClass *klass, MonoError *error); + +MonoClassField* +mono_class_get_field_from_name_full (MonoClass *klass, const char *name, MonoType *type); + +MonoVTable* +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); + +gboolean +mono_class_is_variant_compatible (MonoClass *klass, MonoClass *oklass, gboolean check_for_reference_conv); + +gboolean mono_is_corlib_image (MonoImage *image); + +MonoType* +mono_field_get_type_checked (MonoClassField *field, MonoError *error); + +MonoClassField* +mono_class_get_fields_lazy (MonoClass* klass, gpointer *iter); + +gboolean +mono_class_check_vtable_constraints (MonoClass *klass, GList *in_setup); + +gboolean +mono_class_has_finalizer (MonoClass *klass); + +void +mono_unload_interface_id (MonoClass *klass); + +GPtrArray* +mono_class_get_methods_by_name (MonoClass *klass, const char *name, guint32 bflags, gboolean ignore_case, gboolean allow_ctors, MonoError *error); + +char* +mono_class_full_name (MonoClass *klass); + +MonoClass* +mono_class_inflate_generic_class_checked (MonoClass *gklass, MonoGenericContext *context, MonoError *error); + +MonoClass * +mono_class_get_checked (MonoImage *image, guint32 type_token, MonoError *error); + +MonoClass * +mono_class_get_and_inflate_typespec_checked (MonoImage *image, guint32 type_token, MonoGenericContext *context, MonoError *error); + +MonoClass * +mono_class_from_name_checked (MonoImage *image, const char* name_space, const char *name, MonoError *error); + +MonoClass * +mono_class_from_name_case_checked (MonoImage *image, const char* name_space, const char *name, MonoError *error); + +MonoClassField* +mono_field_from_token_checked (MonoImage *image, uint32_t token, MonoClass **retklass, MonoGenericContext *context, MonoError *error); + +gpointer +mono_ldtoken_checked (MonoImage *image, guint32 token, MonoClass **handle_class, MonoGenericContext *context, MonoError *error); + +MonoClass * +mono_class_from_generic_parameter_internal (MonoGenericParam *param); + +MonoImage * +get_image_for_generic_param (MonoGenericParam *param); + +char * +make_generic_name_string (MonoImage *image, int num); + +MonoClass * +mono_class_load_from_name (MonoImage *image, const char* name_space, const char *name) MONO_LLVM_INTERNAL; + +MonoClass* +mono_class_try_load_from_name (MonoImage *image, const char* name_space, const char *name); + +void +mono_error_set_for_class_failure (MonoError *orerror, const MonoClass *klass); + +gboolean +mono_class_has_failure (const MonoClass *klass); + +/* Kind specific accessors */ +MonoGenericClass* +mono_class_get_generic_class (MonoClass *klass); + +MonoGenericClass* +mono_class_try_get_generic_class (MonoClass *klass); + +void +mono_class_set_flags (MonoClass *klass, guint32 flags); + +MonoGenericContainer* +mono_class_try_get_generic_container (MonoClass *klass); + +void +mono_class_set_generic_container (MonoClass *klass, MonoGenericContainer *container); + +MonoType* +mono_class_gtd_get_canonical_inst (MonoClass *klass); + +guint32 +mono_class_get_first_method_idx (MonoClass *klass); + +void +mono_class_set_first_method_idx (MonoClass *klass, guint32 idx); + +guint32 +mono_class_get_first_field_idx (MonoClass *klass); + +void +mono_class_set_first_field_idx (MonoClass *klass, guint32 idx); + +guint32 +mono_class_get_method_count (MonoClass *klass); + +void +mono_class_set_method_count (MonoClass *klass, guint32 count); + +guint32 +mono_class_get_field_count (MonoClass *klass); + +void +mono_class_set_field_count (MonoClass *klass, guint32 count); + +MonoMarshalType* +mono_class_get_marshal_info (MonoClass *klass); + +void +mono_class_set_marshal_info (MonoClass *klass, MonoMarshalType *marshal_info); + +guint32 +mono_class_get_ref_info_handle (MonoClass *klass); + +guint32 +mono_class_set_ref_info_handle (MonoClass *klass, guint32 value); + +MonoErrorBoxed* +mono_class_get_exception_data (MonoClass *klass); + +void +mono_class_set_exception_data (MonoClass *klass, MonoErrorBoxed *value); + +GList* +mono_class_get_nested_classes_property (MonoClass *klass); + +void +mono_class_set_nested_classes_property (MonoClass *klass, GList *value); + +MonoClassPropertyInfo* +mono_class_get_property_info (MonoClass *klass); + +void +mono_class_set_property_info (MonoClass *klass, MonoClassPropertyInfo *info); + +MonoClassEventInfo* +mono_class_get_event_info (MonoClass *klass); + +void +mono_class_set_event_info (MonoClass *klass, MonoClassEventInfo *info); + +MonoFieldDefaultValue* +mono_class_get_field_def_values (MonoClass *klass); + +void +mono_class_set_field_def_values (MonoClass *klass, MonoFieldDefaultValue *values); + +guint32 +mono_class_get_declsec_flags (MonoClass *klass); + +void +mono_class_set_declsec_flags (MonoClass *klass, guint32 value); + +void +mono_class_set_is_com_object (MonoClass *klass); + +void +mono_class_set_weak_bitmap (MonoClass *klass, int nbits, gsize *bits); + +gsize* +mono_class_get_weak_bitmap (MonoClass *klass, int *nbits); + +/*Now that everything has been defined, let's include the inline functions */ +#include + +#endif /* __MONO_METADATA_CLASS_INTERNALS_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/class.c b/unity-2019.4.24f1-mbe/mono/metadata/class.c new file mode 100644 index 000000000..9a1c80208 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/class.c @@ -0,0 +1,11149 @@ +/** + * \file + * Class management for the Mono runtime + * + * Author: + * Miguel de Icaza (miguel@ximian.com) + * + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Copyright 2012 Xamarin Inc (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#include +#ifdef HAVE_ALLOCA_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +gint32 classes_size; +gint32 class_def_count, class_gtd_count, class_ginst_count, class_gparam_count, class_array_count, class_pointer_count; + +/* Low level lock which protects data structures in this module */ +static mono_mutex_t classes_mutex; + +/* Function supplied by the runtime to find classes by name using information from the AOT file */ +static MonoGetClassFromName get_class_from_name = NULL; + +static MonoClass * mono_class_create_from_typedef (MonoImage *image, guint32 type_token, MonoError *error); +static gboolean mono_class_get_cached_class_info (MonoClass *klass, MonoCachedClassInfo *res); +static gboolean can_access_type (MonoClass *access_klass, MonoClass *member_klass); +static MonoMethod* find_method_in_metadata (MonoClass *klass, const char *name, int param_count, int flags); +static int generic_array_methods (MonoClass *klass); +static void setup_generic_array_ifaces (MonoClass *klass, MonoClass *iface, MonoMethod **methods, int pos, GHashTable *cache); + +static MonoMethod* mono_class_get_virtual_methods (MonoClass* klass, gpointer *iter); +static char* mono_assembly_name_from_token (MonoImage *image, guint32 type_token); +static void mono_field_resolve_type (MonoClassField *field, MonoError *error); +static guint32 mono_field_resolve_flags (MonoClassField *field); +static void mono_class_setup_vtable_full (MonoClass *klass, GList *in_setup); +static void mono_generic_class_setup_parent (MonoClass *klass, MonoClass *gklass); + +static gboolean mono_class_set_failure (MonoClass *klass, MonoErrorBoxed *boxed_error); + +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. + +Given the following type hierarchy: + +class TextBox : TextBoxBase {} +class TextBoxBase : TextInput where T : TextBoxBase {} +class TextInput : Input where T: TextInput {} +class Input {} + +The runtime tries to load TextBoxBase<>. +To load TextBoxBase<> to do so it must resolve the parent which is TextInput. +To instantiate TextInput it must resolve TextInput<> and TextBox. +To load TextBox it must resolve the parent which is TextBoxBase. + +At this point the runtime must instantiate TextBoxBase. Both types are partially loaded +at this point, iow, both are registered in the type map and both and a NULL parent. This means +that the resulting generic instance will have a NULL parent, which is wrong and will cause breakage. + +To fix that what we do is to record all generic instantes created while resolving the parent of +any generic type definition and, after resolved, correct the parent field if needed. + +*/ +static int record_gclass_instantiation; +static GSList *gclass_recorded_list; +typedef gboolean (*gclass_record_func) (MonoClass*, void*); + +/* This TLS variable points to a GSList of classes which have setup_fields () executing */ +static MonoNativeTlsKey setup_fields_tls_id; + +static MonoNativeTlsKey init_pending_tls_id; + +static inline void +classes_lock (void) +{ + mono_locks_os_acquire (&classes_mutex, ClassesLock); +} + +static inline void +classes_unlock (void) +{ + mono_locks_os_release (&classes_mutex, ClassesLock); +} + +/* + * LOCKING: loader lock must be held until pairing disable_gclass_recording is called. +*/ +static void +enable_gclass_recording (void) +{ + ++record_gclass_instantiation; +} + +/* + * LOCKING: loader lock must be held since pairing enable_gclass_recording was called. +*/ +static void +disable_gclass_recording (gclass_record_func func, void *user_data) +{ + GSList **head = &gclass_recorded_list; + + g_assert (record_gclass_instantiation > 0); + --record_gclass_instantiation; + + while (*head) { + GSList *node = *head; + if (func ((MonoClass*)node->data, user_data)) { + *head = node->next; + g_slist_free_1 (node); + } else { + head = &node->next; + } + } + + /* We automatically discard all recorded gclasses when disabled. */ + if (!record_gclass_instantiation && gclass_recorded_list) { + g_slist_free (gclass_recorded_list); + gclass_recorded_list = NULL; + } +} + +/** + * mono_class_from_typeref: + * \param image a MonoImage + * \param type_token a TypeRef token + * + * Creates the \c MonoClass* structure representing the type defined by + * the typeref token valid inside \p image. + * \returns The \c MonoClass* representing the typeref token, or NULL if it could + * not be loaded. + */ +MonoClass * +mono_class_from_typeref (MonoImage *image, guint32 type_token) +{ + MonoError error; + MonoClass *klass = mono_class_from_typeref_checked (image, type_token, &error); + g_assert (mono_error_ok (&error)); /*FIXME proper error handling*/ + return klass; +} + +/** + * mono_class_from_typeref_checked: + * \param image a MonoImage + * \param type_token a TypeRef token + * \param error error return code, if any. + * + * Creates the \c MonoClass* structure representing the type defined by + * the typeref token valid inside \p image. + * + * \returns The \c MonoClass* representing the typeref token, NULL if it could + * not be loaded with the \p error value filled with the information about the + * error. + */ +MonoClass * +mono_class_from_typeref_checked (MonoImage *image, guint32 type_token, MonoError *error) +{ + guint32 cols [MONO_TYPEREF_SIZE]; + MonoTableInfo *t = &image->tables [MONO_TABLE_TYPEREF]; + guint32 idx; + const char *name, *nspace; + MonoClass *res = NULL; + MonoImage *module; + + error_init (error); + + if (!mono_verifier_verify_typeref_row (image, (type_token & 0xffffff) - 1, error)) + return NULL; + + mono_metadata_decode_row (t, (type_token&0xffffff)-1, cols, MONO_TYPEREF_SIZE); + + name = mono_metadata_string_heap (image, cols [MONO_TYPEREF_NAME]); + nspace = mono_metadata_string_heap (image, cols [MONO_TYPEREF_NAMESPACE]); + + idx = cols [MONO_TYPEREF_SCOPE] >> MONO_RESOLUTION_SCOPE_BITS; + switch (cols [MONO_TYPEREF_SCOPE] & MONO_RESOLUTION_SCOPE_MASK) { + case MONO_RESOLUTION_SCOPE_MODULE: + /* + LAMESPEC The spec says that a null module resolution scope should go through the exported type table. + This is not the observed behavior of existing implementations. + The defacto behavior is that it's just a typedef in disguise. + */ + /* a typedef in disguise */ + res = mono_class_from_name_checked (image, nspace, name, error); + goto done; + + case MONO_RESOLUTION_SCOPE_MODULEREF: + module = mono_image_load_module_checked (image, idx, error); + if (module) + res = mono_class_from_name_checked (module, nspace, name, error); + goto done; + + case MONO_RESOLUTION_SCOPE_TYPEREF: { + MonoClass *enclosing; + GList *tmp; + + if (idx == mono_metadata_token_index (type_token)) { + mono_error_set_bad_image (error, image, "Image with self-referencing typeref token %08x.", type_token); + return NULL; + } + + enclosing = mono_class_from_typeref_checked (image, MONO_TOKEN_TYPE_REF | idx, error); + return_val_if_nok (error, NULL); + + GList *nested_classes = mono_class_get_nested_classes_property (enclosing); + if (enclosing->nested_classes_inited && nested_classes) { + /* Micro-optimization: don't scan the metadata tables if enclosing is already inited */ + for (tmp = nested_classes; tmp; tmp = tmp->next) { + res = (MonoClass *)tmp->data; + if (strcmp (res->name, name) == 0) + return res; + } + } else { + /* Don't call mono_class_init as we might've been called by it recursively */ + int i = mono_metadata_nesting_typedef (enclosing->image, enclosing->type_token, 1); + while (i) { + guint32 class_nested = mono_metadata_decode_row_col (&enclosing->image->tables [MONO_TABLE_NESTEDCLASS], i - 1, MONO_NESTED_CLASS_NESTED); + guint32 string_offset = mono_metadata_decode_row_col (&enclosing->image->tables [MONO_TABLE_TYPEDEF], class_nested - 1, MONO_TYPEDEF_NAME); + const char *nname = mono_metadata_string_heap (enclosing->image, string_offset); + + if (strcmp (nname, name) == 0) + return mono_class_create_from_typedef (enclosing->image, MONO_TOKEN_TYPE_DEF | class_nested, error); + + i = mono_metadata_nesting_typedef (enclosing->image, enclosing->type_token, i + 1); + } + } + g_warning ("TypeRef ResolutionScope not yet handled (%d) for %s.%s in image %s", idx, nspace, name, image->name); + goto done; + } + case MONO_RESOLUTION_SCOPE_ASSEMBLYREF: + break; + } + + if (idx > image->tables [MONO_TABLE_ASSEMBLYREF].rows) { + mono_error_set_bad_image (error, image, "Image with invalid assemblyref token %08x.", idx); + return NULL; + } + + if (!image->references || !image->references [idx - 1]) + mono_assembly_load_reference (image, idx - 1); + g_assert (image->references [idx - 1]); + + /* If the assembly did not load, register this as a type load exception */ + if (image->references [idx - 1] == REFERENCE_MISSING){ + MonoAssemblyName aname; + char *human_name; + + mono_assembly_get_assemblyref (image, idx - 1, &aname); + human_name = mono_stringify_assembly_name (&aname); + mono_error_set_assembly_load_simple (error, human_name, image->assembly ? image->assembly->ref_only : FALSE); + return NULL; + } + + res = mono_class_from_name_checked (image->references [idx - 1]->image, nspace, name, error); + +done: + /* Generic case, should be avoided for when a better error is possible. */ + if (!res && mono_error_ok (error)) { + char *name = mono_class_name_from_token (image, type_token); + char *assembly = mono_assembly_name_from_token (image, type_token); + mono_error_set_type_load_name (error, name, assembly, "Could not resolve type with token %08x (from typeref, class/assembly %s, %s)", type_token, name, assembly); + } + return res; +} + + +static void * +mono_image_memdup (MonoImage *image, void *data, guint size) +{ + void *res = mono_image_alloc (image, size); + memcpy (res, data, size); + return res; +} + +/* Copy everything mono_metadata_free_array free. */ +MonoArrayType * +mono_dup_array_type (MonoImage *image, MonoArrayType *a) +{ + if (image) { + a = (MonoArrayType *)mono_image_memdup (image, a, sizeof (MonoArrayType)); + if (a->sizes) + a->sizes = (int *)mono_image_memdup (image, a->sizes, a->numsizes * sizeof (int)); + if (a->lobounds) + a->lobounds = (int *)mono_image_memdup (image, a->lobounds, a->numlobounds * sizeof (int)); + } else { + a = (MonoArrayType *)g_memdup (a, sizeof (MonoArrayType)); + if (a->sizes) + a->sizes = (int *)g_memdup (a->sizes, a->numsizes * sizeof (int)); + if (a->lobounds) + a->lobounds = (int *)g_memdup (a->lobounds, a->numlobounds * sizeof (int)); + } + return a; +} + +/* Copy everything mono_metadata_free_method_signature free. */ +MonoMethodSignature* +mono_metadata_signature_deep_dup (MonoImage *image, MonoMethodSignature *sig) +{ + int i; + + sig = mono_metadata_signature_dup_full (image, sig); + + sig->ret = mono_metadata_type_dup (image, sig->ret); + for (i = 0; i < sig->param_count; ++i) + sig->params [i] = mono_metadata_type_dup (image, sig->params [i]); + + return sig; +} + +static void +_mono_type_get_assembly_name (MonoClass *klass, GString *str) +{ + MonoAssembly *ta = klass->image->assembly; + char *name; + + name = mono_stringify_assembly_name (&ta->aname); + g_string_append_printf (str, ", %s", name); + g_free (name); +} + +static inline void +mono_type_name_check_byref (MonoType *type, GString *str) +{ + if (type->byref) + g_string_append_c (str, '&'); +} + +/** + * mono_identifier_escape_type_name_chars: + * \param str a destination string + * \param identifier an IDENTIFIER in internal form + * + * \returns \p str + * + * The displayed form of the identifier is appended to str. + * + * The displayed form of an identifier has the characters ,+&*[]\ + * that have special meaning in type names escaped with a preceeding + * backslash (\) character. + */ +static GString* +mono_identifier_escape_type_name_chars (GString* str, const char* identifier) +{ + if (!identifier) + return str; + + size_t n = str->len; + // reserve space for common case: there will be no escaped characters. + g_string_set_size(str, n + strlen(identifier)); + g_string_set_size(str, n); + + for (const char* s = identifier; *s != 0 ; s++) { + switch (*s) { + case ',': + case '+': + case '&': + case '*': + case '[': + case ']': + case '\\': + g_string_append_c (str, '\\'); + g_string_append_c (str, *s); + break; + default: + g_string_append_c (str, *s); + break; + } + } + return str; +} + +static void +mono_type_get_name_recurse (MonoType *type, GString *str, gboolean is_recursed, + MonoTypeNameFormat format) +{ + MonoClass *klass; + + 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_type_get_name_recurse ( + &type->data.array->eklass->byval_arg, str, FALSE, nested_format); + g_string_append_c (str, '['); + if (rank == 1) + g_string_append_c (str, '*'); + for (i = 1; i < rank; i++) + g_string_append_c (str, ','); + g_string_append_c (str, ']'); + + mono_type_name_check_byref (type, str); + + if (format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) + _mono_type_get_assembly_name (type->data.array->eklass, str); + 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_type_get_name_recurse ( + &type->data.klass->byval_arg, str, FALSE, nested_format); + g_string_append (str, "[]"); + + mono_type_name_check_byref (type, str); + + if (format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) + _mono_type_get_assembly_name (type->data.klass, str); + 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_type_get_name_recurse ( + type->data.type, str, FALSE, nested_format); + g_string_append_c (str, '*'); + + mono_type_name_check_byref (type, str); + + if (format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) + _mono_type_get_assembly_name (mono_class_from_mono_type (type->data.type), str); + break; + } + case MONO_TYPE_VAR: + case MONO_TYPE_MVAR: + if (!mono_generic_param_info (type->data.generic_param)) + g_string_append_printf (str, "%s%d", type->type == MONO_TYPE_VAR ? "!" : "!!", type->data.generic_param->num); + else + g_string_append (str, mono_generic_param_info (type->data.generic_param)->name); + + mono_type_name_check_byref (type, str); + + break; + default: + klass = mono_class_from_mono_type (type); + if (klass->nested_in) { + mono_type_get_name_recurse ( + &klass->nested_in->byval_arg, str, TRUE, format); + if (format == MONO_TYPE_NAME_FORMAT_IL) + g_string_append_c (str, '.'); + else + g_string_append_c (str, '+'); + } else if (*klass->name_space) { + if (format == MONO_TYPE_NAME_FORMAT_IL) + g_string_append (str, klass->name_space); + else + mono_identifier_escape_type_name_chars (str, klass->name_space); + g_string_append_c (str, '.'); + } + if (format == MONO_TYPE_NAME_FORMAT_IL) { + char *s = strchr (klass->name, '`'); + int len = s ? s - klass->name : strlen (klass->name); + g_string_append_len (str, klass->name, len); + } else { + mono_identifier_escape_type_name_chars (str, klass->name); + } + if (is_recursed) + break; + 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) + g_string_append_c (str, '<'); + else + g_string_append_c (str, '['); + for (i = 0; i < inst->type_argc; i++) { + MonoType *t = inst->type_argv [i]; + + if (i) + g_string_append_c (str, ','); + if ((nested_format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) && + (t->type != MONO_TYPE_VAR) && (type->type != MONO_TYPE_MVAR)) + g_string_append_c (str, '['); + mono_type_get_name_recurse (inst->type_argv [i], str, FALSE, nested_format); + if ((nested_format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) && + (t->type != MONO_TYPE_VAR) && (type->type != MONO_TYPE_MVAR)) + g_string_append_c (str, ']'); + } + if (format == MONO_TYPE_NAME_FORMAT_IL) + g_string_append_c (str, '>'); + else + g_string_append_c (str, ']'); + } 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) + g_string_append_c (str, '<'); + else + g_string_append_c (str, '['); + for (i = 0; i < mono_class_get_generic_container (klass)->type_argc; i++) { + if (i) + g_string_append_c (str, ','); + g_string_append (str, mono_generic_container_get_param_info (mono_class_get_generic_container (klass), i)->name); + } + if (format == MONO_TYPE_NAME_FORMAT_IL) + g_string_append_c (str, '>'); + else + g_string_append_c (str, ']'); + } + + mono_type_name_check_byref (type, str); + + if ((format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) && + (type->type != MONO_TYPE_VAR) && (type->type != MONO_TYPE_MVAR)) + _mono_type_get_assembly_name (klass, str); + break; + } +} + +/** + * mono_type_get_name_full: + * \param type a type + * \param format the format for the return string. + * + * + * \returns The string representation in a number of formats: + * + * if \p format is \c MONO_TYPE_NAME_FORMAT_REFLECTION, the return string is + * returned in the format required by \c System.Reflection, this is the + * inverse of mono_reflection_parse_type(). + * + * if \p format is \c MONO_TYPE_NAME_FORMAT_IL, it returns a syntax that can + * be used by the IL assembler. + * + * if \p format is \c MONO_TYPE_NAME_FORMAT_FULL_NAME + * + * if \p format is \c MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED + */ +char* +mono_type_get_name_full (MonoType *type, MonoTypeNameFormat format) +{ + GString* result; + + result = g_string_new (""); + + mono_type_get_name_recurse (type, result, FALSE, format); + + return g_string_free (result, FALSE); +} + +/** + * mono_type_get_full_name: + * \param class a class + * + * \returns The string representation for type as required by System.Reflection. + * The inverse of mono_reflection_parse_type(). + */ +char * +mono_type_get_full_name (MonoClass *klass) +{ + return mono_type_get_name_full (mono_class_get_type (klass), MONO_TYPE_NAME_FORMAT_REFLECTION); +} + +/** + * mono_type_get_name: + * \param type a type + * \returns The string representation for type as it would be represented in IL code. + */ +char* +mono_type_get_name (MonoType *type) +{ + return mono_type_get_name_full (type, MONO_TYPE_NAME_FORMAT_IL); +} + +/** + * mono_type_get_underlying_type: + * \param type a type + * \returns The \c MonoType for the underlying integer type if \p type + * is an enum and byref is false, otherwise the type itself. + */ +MonoType* +mono_type_get_underlying_type (MonoType *type) +{ + if (type->type == MONO_TYPE_VALUETYPE && type->data.klass->enumtype && !type->byref) + return mono_class_enum_basetype (type->data.klass); + if (type->type == MONO_TYPE_GENERICINST && type->data.generic_class->container_class->enumtype && !type->byref) + return mono_class_enum_basetype (type->data.generic_class->container_class); + return type; +} + +/** + * mono_class_is_open_constructed_type: + * \param type a type + * + * \returns TRUE if type represents a generics open constructed type. + * IOW, not all type parameters required for the instantiation have + * been provided or it's a generic type definition. + * + * An open constructed type means it's a non realizable type. Not to + * be mixed up with an abstract type - we can't cast or dispatch to + * an open type, for example. + */ +gboolean +mono_class_is_open_constructed_type (MonoType *t) +{ + switch (t->type) { + case MONO_TYPE_VAR: + case MONO_TYPE_MVAR: + return TRUE; + case MONO_TYPE_SZARRAY: + return mono_class_is_open_constructed_type (&t->data.klass->byval_arg); + case MONO_TYPE_ARRAY: + return mono_class_is_open_constructed_type (&t->data.array->eklass->byval_arg); + case MONO_TYPE_PTR: + return mono_class_is_open_constructed_type (t->data.type); + case MONO_TYPE_GENERICINST: + return t->data.generic_class->context.class_inst->is_open; + case MONO_TYPE_CLASS: + case MONO_TYPE_VALUETYPE: + return mono_class_is_gtd (t->data.klass); + default: + return FALSE; + } +} + +/* +This is a simple function to catch the most common bad instances of generic types. +Specially those that might lead to further failures in the runtime. +*/ +static gboolean +is_valid_generic_argument (MonoType *type) +{ + switch (type->type) { + case MONO_TYPE_VOID: + //case MONO_TYPE_TYPEDBYREF: + return FALSE; + default: + return TRUE; + } +} + +static MonoType* +inflate_generic_type (MonoImage *image, MonoType *type, MonoGenericContext *context, MonoError *error) +{ + error_init (error); + + switch (type->type) { + case MONO_TYPE_MVAR: { + MonoType *nt; + int num = mono_type_get_generic_param_num (type); + MonoGenericInst *inst = context->method_inst; + if (!inst) + return NULL; + if (num >= inst->type_argc) { + MonoGenericParamInfo *info = mono_generic_param_info (type->data.generic_param); + mono_error_set_bad_image (error, image, "MVAR %d (%s) cannot be expanded in this context with %d instantiations", + num, info ? info->name : "", inst->type_argc); + return NULL; + } + + if (!is_valid_generic_argument (inst->type_argv [num])) { + MonoGenericParamInfo *info = mono_generic_param_info (type->data.generic_param); + mono_error_set_bad_image (error, image, "MVAR %d (%s) cannot be expanded with type 0x%x", + num, info ? info->name : "", inst->type_argv [num]->type); + return NULL; + } + /* + * Note that the VAR/MVAR cases are different from the rest. The other cases duplicate @type, + * while the VAR/MVAR duplicates a type from the context. So, we need to ensure that the + * ->byref and ->attrs from @type are propagated to the returned type. + */ + nt = mono_metadata_type_dup (image, inst->type_argv [num]); + nt->byref = type->byref; + nt->attrs = type->attrs; + return nt; + } + case MONO_TYPE_VAR: { + MonoType *nt; + int num = mono_type_get_generic_param_num (type); + MonoGenericInst *inst = context->class_inst; + if (!inst) + return NULL; + if (num >= inst->type_argc) { + MonoGenericParamInfo *info = mono_generic_param_info (type->data.generic_param); + mono_error_set_bad_image (error, image, "VAR %d (%s) cannot be expanded in this context with %d instantiations", + num, info ? info->name : "", inst->type_argc); + return NULL; + } + if (!is_valid_generic_argument (inst->type_argv [num])) { + MonoGenericParamInfo *info = mono_generic_param_info (type->data.generic_param); + mono_error_set_bad_image (error, image, "VAR %d (%s) cannot be expanded with type 0x%x", + num, info ? info->name : "", inst->type_argv [num]->type); + return NULL; + } + nt = mono_metadata_type_dup (image, inst->type_argv [num]); + nt->byref = type->byref; + nt->attrs = type->attrs; + return nt; + } + case MONO_TYPE_SZARRAY: { + MonoClass *eclass = type->data.klass; + MonoType *nt, *inflated = inflate_generic_type (NULL, &eclass->byval_arg, context, error); + if (!inflated || !mono_error_ok (error)) + return NULL; + nt = mono_metadata_type_dup (image, type); + nt->data.klass = mono_class_from_mono_type (inflated); + mono_metadata_free_type (inflated); + return nt; + } + case MONO_TYPE_ARRAY: { + MonoClass *eclass = type->data.array->eklass; + MonoType *nt, *inflated = inflate_generic_type (NULL, &eclass->byval_arg, context, error); + if (!inflated || !mono_error_ok (error)) + return NULL; + nt = mono_metadata_type_dup (image, type); + nt->data.array->eklass = mono_class_from_mono_type (inflated); + mono_metadata_free_type (inflated); + return nt; + } + case MONO_TYPE_GENERICINST: { + MonoGenericClass *gclass = type->data.generic_class; + MonoGenericInst *inst; + MonoType *nt; + if (!gclass->context.class_inst->is_open) + return NULL; + + inst = mono_metadata_inflate_generic_inst (gclass->context.class_inst, context, error); + return_val_if_nok (error, NULL); + + if (inst != gclass->context.class_inst) + gclass = mono_metadata_lookup_generic_class (gclass->container_class, inst, gclass->is_dynamic); + + if (gclass == type->data.generic_class) + return NULL; + + nt = mono_metadata_type_dup (image, type); + nt->data.generic_class = gclass; + return nt; + } + case MONO_TYPE_CLASS: + case MONO_TYPE_VALUETYPE: { + MonoClass *klass = type->data.klass; + MonoGenericContainer *container = mono_class_try_get_generic_container (klass); + MonoGenericInst *inst; + MonoGenericClass *gclass = NULL; + MonoType *nt; + + if (!container) + return NULL; + + /* We can't use context->class_inst directly, since it can have more elements */ + inst = mono_metadata_inflate_generic_inst (container->context.class_inst, context, error); + return_val_if_nok (error, NULL); + + if (inst == container->context.class_inst) + return NULL; + + gclass = mono_metadata_lookup_generic_class (klass, inst, image_is_dynamic (klass->image)); + + nt = mono_metadata_type_dup (image, type); + nt->type = MONO_TYPE_GENERICINST; + 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; + } + return NULL; +} + +MonoGenericContext * +mono_generic_class_get_context (MonoGenericClass *gclass) +{ + return &gclass->context; +} + +MonoGenericContext * +mono_class_get_context (MonoClass *klass) +{ + MonoGenericClass *gklass = mono_class_try_get_generic_class (klass); + return gklass ? mono_generic_class_get_context (gklass) : NULL; +} + +/* + * mono_class_inflate_generic_type_with_mempool: + * @mempool: a mempool + * @type: a type + * @context: a generics context + * @error: error context + * + * The same as mono_class_inflate_generic_type, but allocates the MonoType + * from mempool if it is non-NULL. If it is NULL, the MonoType is + * allocated on the heap and is owned by the caller. + * The returned type can potentially be the same as TYPE, so it should not be + * modified by the caller, and it should be freed using mono_metadata_free_type (). + */ +MonoType* +mono_class_inflate_generic_type_with_mempool (MonoImage *image, MonoType *type, MonoGenericContext *context, MonoError *error) +{ + MonoType *inflated = NULL; + error_init (error); + + if (context) + inflated = inflate_generic_type (image, type, context, error); + return_val_if_nok (error, NULL); + + if (!inflated) { + MonoType *shared = mono_metadata_get_shared_type (type); + + if (shared) { + return shared; + } else { + return mono_metadata_type_dup (image, type); + } + } + + UnlockedIncrement (&mono_stats.inflated_type_count); + return inflated; +} + +/** + * mono_class_inflate_generic_type: + * \param type a type + * \param context a generics context + * \deprecated Please use \c mono_class_inflate_generic_type_checked instead + * + * If \p type is a generic type and \p context is not NULL, instantiate it using the + * generics context \p context. + * + * \returns The instantiated type or a copy of \p type. The returned \c MonoType is allocated + * on the heap and is owned by the caller. Returns NULL on error. + */ +MonoType* +mono_class_inflate_generic_type (MonoType *type, MonoGenericContext *context) +{ + MonoError error; + MonoType *result; + result = mono_class_inflate_generic_type_checked (type, context, &error); + mono_error_cleanup (&error); + return result; +} + +/* + * mono_class_inflate_generic_type: + * @type: a type + * @context: a generics context + * @error: error context to use + * + * If @type is a generic type and @context is not NULL, instantiate it using the + * generics context @context. + * + * Returns: The instantiated type or a copy of @type. The returned MonoType is allocated + * on the heap and is owned by the caller. + */ +MonoType* +mono_class_inflate_generic_type_checked (MonoType *type, MonoGenericContext *context, MonoError *error) +{ + return mono_class_inflate_generic_type_with_mempool (NULL, type, context, error); +} + +/* + * mono_class_inflate_generic_type_no_copy: + * + * Same as inflate_generic_type_with_mempool, but return TYPE if no inflation + * was done. + */ +static MonoType* +mono_class_inflate_generic_type_no_copy (MonoImage *image, MonoType *type, MonoGenericContext *context, MonoError *error) +{ + MonoType *inflated = NULL; + + error_init (error); + if (context) { + inflated = inflate_generic_type (image, type, context, error); + return_val_if_nok (error, NULL); + } + + if (!inflated) + return type; + + UnlockedIncrement (&mono_stats.inflated_type_count); + return inflated; +} + +/* + * mono_class_inflate_generic_class: + * + * Inflate the class @gklass with @context. Set @error on failure. + */ +MonoClass* +mono_class_inflate_generic_class_checked (MonoClass *gklass, MonoGenericContext *context, MonoError *error) +{ + MonoClass *res; + MonoType *inflated; + + inflated = mono_class_inflate_generic_type_checked (&gklass->byval_arg, context, error); + return_val_if_nok (error, NULL); + + res = mono_class_from_mono_type (inflated); + mono_metadata_free_type (inflated); + + return res; +} + +static MonoGenericContext +inflate_generic_context (MonoGenericContext *context, MonoGenericContext *inflate_with, MonoError *error) +{ + MonoGenericInst *class_inst = NULL; + MonoGenericInst *method_inst = NULL; + MonoGenericContext res = { NULL, NULL }; + + error_init (error); + + if (context->class_inst) { + class_inst = mono_metadata_inflate_generic_inst (context->class_inst, inflate_with, error); + if (!mono_error_ok (error)) + goto fail; + } + + if (context->method_inst) { + method_inst = mono_metadata_inflate_generic_inst (context->method_inst, inflate_with, error); + if (!mono_error_ok (error)) + goto fail; + } + + res.class_inst = class_inst; + res.method_inst = method_inst; +fail: + return res; +} + +/** + * mono_class_inflate_generic_method: + * \param method a generic method + * \param context a generics context + * + * Instantiate the generic method \p method using the generics context \p context. + * + * \returns The new instantiated method + */ +MonoMethod * +mono_class_inflate_generic_method (MonoMethod *method, MonoGenericContext *context) +{ + return mono_class_inflate_generic_method_full (method, NULL, context); +} + +MonoMethod * +mono_class_inflate_generic_method_checked (MonoMethod *method, MonoGenericContext *context, MonoError *error) +{ + return mono_class_inflate_generic_method_full_checked (method, NULL, context, error); +} + +/** + * mono_class_inflate_generic_method_full: + * + * Instantiate method \p method with the generic context \p context. + * BEWARE: All non-trivial fields are invalid, including klass, signature, and header. + * Use mono_method_signature() and mono_method_get_header() to get the correct values. + */ +MonoMethod* +mono_class_inflate_generic_method_full (MonoMethod *method, MonoClass *klass_hint, MonoGenericContext *context) +{ + MonoError error; + MonoMethod *res = mono_class_inflate_generic_method_full_checked (method, klass_hint, context, &error); + if (!mono_error_ok (&error)) + /*FIXME do proper error handling - on this case, kill this function. */ + g_error ("Could not inflate generic method due to %s", mono_error_get_message (&error)); + + return res; +} + +/** + * mono_class_inflate_generic_method_full_checked: + * Same as mono_class_inflate_generic_method_full but return failure using \p error. + */ +MonoMethod* +mono_class_inflate_generic_method_full_checked (MonoMethod *method, MonoClass *klass_hint, MonoGenericContext *context, MonoError *error) +{ + MonoMethod *result; + MonoMethodInflated *iresult, *cached; + MonoMethodSignature *sig; + MonoGenericContext tmp_context; + + error_init (error); + + /* The `method' has already been instantiated before => we need to peel out the instantiation and create a new context */ + while (method->is_inflated) { + MonoGenericContext *method_context = mono_method_get_context (method); + MonoMethodInflated *imethod = (MonoMethodInflated *) method; + + tmp_context = inflate_generic_context (method_context, context, error); + return_val_if_nok (error, NULL); + + context = &tmp_context; + + if (mono_metadata_generic_context_equal (method_context, context)) + return method; + + method = imethod->declaring; + } + + /* + * A method only needs to be inflated if the context has argument for which it is + * parametric. Eg: + * + * class Foo { void Bar(); } - doesn't need to be inflated if only mvars' are supplied + * class Foo { void Bar (); } - doesn't need to be if only vars' are supplied + * + */ + if (!((method->is_generic && context->method_inst) || + (mono_class_is_gtd (method->klass) && context->class_inst))) + return method; + + iresult = g_new0 (MonoMethodInflated, 1); + iresult->context = *context; + iresult->declaring = method; + + if (!context->method_inst && method->is_generic) + iresult->context.method_inst = mono_method_get_generic_container (method)->context.method_inst; + + if (!context->class_inst) { + g_assert (!mono_class_is_ginst (iresult->declaring->klass)); + if (mono_class_is_gtd (iresult->declaring->klass)) + iresult->context.class_inst = mono_class_get_generic_container (iresult->declaring->klass)->context.class_inst; + } + /* This can happen with some callers like mono_object_get_virtual_method () */ + if (!mono_class_is_gtd (iresult->declaring->klass) && !mono_class_is_ginst (iresult->declaring->klass)) + iresult->context.class_inst = NULL; + + MonoImageSet *set = mono_metadata_get_image_set_for_method (iresult); + + // check cache + mono_image_set_lock (set); + cached = (MonoMethodInflated *)g_hash_table_lookup (set->gmethod_cache, iresult); + mono_image_set_unlock (set); + + if (cached) { + g_free (iresult); + return (MonoMethod*)cached; + } + + UnlockedIncrement (&mono_stats.inflated_method_count); + + UnlockedAdd (&inflated_methods_size, sizeof (MonoMethodInflated)); + + sig = mono_method_signature (method); + if (!sig) { + char *name = mono_type_get_full_name (method->klass); + mono_error_set_bad_image (error, method->klass->image, "Could not resolve signature of method %s:%s", name, method->name); + g_free (name); + goto fail; + } + + if (sig->pinvoke) { + memcpy (&iresult->method.pinvoke, method, sizeof (MonoMethodPInvoke)); + } else { + memcpy (&iresult->method.method, method, sizeof (MonoMethod)); + } + + result = (MonoMethod *) iresult; + result->is_inflated = TRUE; + result->is_generic = FALSE; + result->sre_method = FALSE; + result->signature = NULL; + + if (method->wrapper_type) { + MonoMethodWrapper *mw = (MonoMethodWrapper*)method; + MonoMethodWrapper *resw = (MonoMethodWrapper*)result; + int len = GPOINTER_TO_INT (((void**)mw->method_data) [0]); + + resw->method_data = (void **)g_malloc (sizeof (gpointer) * (len + 1)); + memcpy (resw->method_data, mw->method_data, sizeof (gpointer) * (len + 1)); + } + + if (iresult->context.method_inst) { + /* Set the generic_container of the result to the generic_container of method */ + MonoGenericContainer *generic_container = mono_method_get_generic_container (method); + + if (generic_container && iresult->context.method_inst == generic_container->context.method_inst) { + result->is_generic = 1; + mono_method_set_generic_container (result, generic_container); + } + } + + if (klass_hint) { + MonoGenericClass *gklass_hint = mono_class_try_get_generic_class (klass_hint); + if (gklass_hint && (gklass_hint->container_class != method->klass || gklass_hint->context.class_inst != context->class_inst)) + klass_hint = NULL; + } + + if (mono_class_is_gtd (method->klass)) + result->klass = klass_hint; + + if (!result->klass) { + MonoType *inflated = inflate_generic_type (NULL, &method->klass->byval_arg, context, error); + if (!mono_error_ok (error)) + goto fail; + + result->klass = inflated ? mono_class_from_mono_type (inflated) : method->klass; + if (inflated) + mono_metadata_free_type (inflated); + } + + /* + * FIXME: This should hold, but it doesn't: + * + * if (result->is_inflated && mono_method_get_context (result)->method_inst && + * mono_method_get_context (result)->method_inst == mono_method_get_generic_container (((MonoMethodInflated*)result)->declaring)->context.method_inst) { + * g_assert (result->is_generic); + * } + * + * Fixing this here causes other things to break, hence a very + * ugly hack in mini-trampolines.c - see + * is_generic_method_definition(). + */ + + // check cache + mono_image_set_lock (set); + cached = (MonoMethodInflated *)g_hash_table_lookup (set->gmethod_cache, iresult); + if (!cached) { + g_hash_table_insert (set->gmethod_cache, iresult, iresult); + iresult->owner = set; + cached = iresult; + } + mono_image_set_unlock (set); + + return (MonoMethod*)cached; + +fail: + g_free (iresult); + return NULL; +} + +/** + * mono_get_inflated_method: + * + * Obsolete. We keep it around since it's mentioned in the public API. + */ +MonoMethod* +mono_get_inflated_method (MonoMethod *method) +{ + return method; +} + +/* + * mono_method_get_context_general: + * @method: a method + * @uninflated: handle uninflated methods? + * + * Returns the generic context of a method or NULL if it doesn't have + * one. For an inflated method that's the context stored in the + * method. Otherwise it's in the method's generic container or in the + * generic container of the method's class. + */ +MonoGenericContext* +mono_method_get_context_general (MonoMethod *method, gboolean uninflated) +{ + if (method->is_inflated) { + MonoMethodInflated *imethod = (MonoMethodInflated *) method; + return &imethod->context; + } + if (!uninflated) + return NULL; + if (method->is_generic) + return &(mono_method_get_generic_container (method)->context); + if (mono_class_is_gtd (method->klass)) + return &mono_class_get_generic_container (method->klass)->context; + return NULL; +} + +/* + * mono_method_get_context: + * @method: a method + * + * Returns the generic context for method if it's inflated, otherwise + * NULL. + */ +MonoGenericContext* +mono_method_get_context (MonoMethod *method) +{ + return mono_method_get_context_general (method, FALSE); +} + +/* + * mono_method_get_generic_container: + * + * Returns the generic container of METHOD, which should be a generic method definition. + * Returns NULL if METHOD is not a generic method definition. + * LOCKING: Acquires the loader lock. + */ +MonoGenericContainer* +mono_method_get_generic_container (MonoMethod *method) +{ + MonoGenericContainer *container; + + if (!method->is_generic) + return NULL; + + container = (MonoGenericContainer *)mono_image_property_lookup (method->klass->image, method, MONO_METHOD_PROP_GENERIC_CONTAINER); + g_assert (container); + + return container; +} + +/* + * mono_method_set_generic_container: + * + * Sets the generic container of METHOD to CONTAINER. + * LOCKING: Acquires the image lock. + */ +void +mono_method_set_generic_container (MonoMethod *method, MonoGenericContainer* container) +{ + g_assert (method->is_generic); + + mono_image_property_insert (method->klass->image, method, MONO_METHOD_PROP_GENERIC_CONTAINER, container); +} + +/** + * mono_class_find_enum_basetype: + * \param class The enum class + * + * Determine the basetype of an enum by iterating through its fields. We do this + * in a separate function since it is cheaper than calling mono_class_setup_fields. + */ +static MonoType* +mono_class_find_enum_basetype (MonoClass *klass, MonoError *error) +{ + MonoGenericContainer *container = NULL; + MonoImage *m = klass->image; + const int top = mono_class_get_field_count (klass); + int i, first_field_idx; + + g_assert (klass->enumtype); + + error_init (error); + + container = mono_class_try_get_generic_container (klass); + if (mono_class_is_ginst (klass)) { + MonoClass *gklass = mono_class_get_generic_class (klass)->container_class; + + container = mono_class_get_generic_container (gklass); + g_assert (container); + } + + /* + * Fetch all the field information. + */ + first_field_idx = mono_class_get_first_field_idx (klass); + for (i = 0; i < top; i++){ + const char *sig; + guint32 cols [MONO_FIELD_SIZE]; + int idx = first_field_idx + i; + MonoType *ftype; + + /* first_field_idx and idx points into the fieldptr table */ + mono_metadata_decode_table_row (m, MONO_TABLE_FIELD, idx, cols, MONO_FIELD_SIZE); + + if (cols [MONO_FIELD_FLAGS] & FIELD_ATTRIBUTE_STATIC) //no need to decode static fields + continue; + + if (!mono_verifier_verify_field_signature (klass->image, cols [MONO_FIELD_SIGNATURE], NULL)) { + mono_error_set_bad_image (error, klass->image, "Invalid field signature %x", cols [MONO_FIELD_SIGNATURE]); + goto fail; + } + + sig = mono_metadata_blob_heap (m, cols [MONO_FIELD_SIGNATURE]); + mono_metadata_decode_value (sig, &sig); + /* FIELD signature == 0x06 */ + if (*sig != 0x06) { + mono_error_set_bad_image (error, klass->image, "Invalid field signature %x, expected 0x6 but got %x", cols [MONO_FIELD_SIGNATURE], *sig); + goto fail; + } + + ftype = mono_metadata_parse_type_checked (m, container, cols [MONO_FIELD_FLAGS], FALSE, sig + 1, &sig, error); + if (!ftype) + goto fail; + + if (mono_class_is_ginst (klass)) { + //FIXME do we leak here? + ftype = mono_class_inflate_generic_type_checked (ftype, mono_class_get_context (klass), error); + if (!mono_error_ok (error)) + goto fail; + ftype->attrs = cols [MONO_FIELD_FLAGS]; + } + + return ftype; + } + mono_error_set_type_load_class (error, klass, "Could not find base type"); + +fail: + return NULL; +} + +/* + * Checks for MonoClass::has_failure without resolving all MonoType's into MonoClass'es + */ +static gboolean +mono_type_has_exceptions (MonoType *type) +{ + switch (type->type) { + case MONO_TYPE_CLASS: + case MONO_TYPE_VALUETYPE: + case MONO_TYPE_SZARRAY: + return mono_class_has_failure (type->data.klass); + case MONO_TYPE_ARRAY: + return mono_class_has_failure (type->data.array->eklass); + case MONO_TYPE_GENERICINST: + return mono_class_has_failure (mono_generic_class_get_class (type->data.generic_class)); + default: + return FALSE; + } +} + +void +mono_error_set_for_class_failure (MonoError *oerror, const MonoClass *klass) +{ + g_assert (mono_class_has_failure (klass)); + MonoErrorBoxed *box = mono_class_get_exception_data ((MonoClass*)klass); + mono_error_set_from_boxed (oerror, box); +} + +/* + * mono_class_alloc: + * + * Allocate memory for some data belonging to CLASS, either from its image's mempool, + * or from the heap. + */ +gpointer +mono_class_alloc (MonoClass *klass, int size) +{ + MonoGenericClass *gklass = mono_class_try_get_generic_class (klass); + if (gklass) + return mono_image_set_alloc (gklass->owner, size); + else + return mono_image_alloc (klass->image, size); +} + +gpointer +mono_class_alloc0 (MonoClass *klass, int size) +{ + gpointer res; + + res = mono_class_alloc (klass, size); + memset (res, 0, size); + return res; +} + +#define mono_class_new0(klass,struct_type, n_structs) \ + ((struct_type *) mono_class_alloc0 ((klass), ((gsize) sizeof (struct_type)) * ((gsize) (n_structs)))) + +/** + * mono_class_setup_basic_field_info: + * \param class The class to initialize + * + * Initializes the following fields in MonoClass: + * * klass->fields (only field->parent and field->name) + * * klass->field.count + * * klass->first_field_idx + * LOCKING: Acquires the loader lock + */ +static void +mono_class_setup_basic_field_info (MonoClass *klass) +{ + MonoGenericClass *gklass; + MonoClassField *field; + MonoClassField *fields; + MonoClass *gtd; + MonoImage *image; + int i, top; + + if (klass->fields) + return; + + gklass = mono_class_try_get_generic_class (klass); + gtd = gklass ? mono_class_get_generic_type_definition (klass) : NULL; + image = klass->image; + + + if (gklass && image_is_dynamic (gklass->container_class->image) && !gklass->container_class->wastypebuilder) { + /* + * This happens when a generic instance of an unfinished generic typebuilder + * is used as an element type for creating an array type. We can't initialize + * the fields of this class using the fields of gklass, since gklass is not + * finished yet, fields could be added to it later. + */ + return; + } + + if (gtd) { + mono_class_setup_basic_field_info (gtd); + + mono_loader_lock (); + mono_class_set_field_count (klass, mono_class_get_field_count (gtd)); + mono_loader_unlock (); + } + + top = mono_class_get_field_count (klass); + + fields = (MonoClassField *)mono_class_alloc0 (klass, sizeof (MonoClassField) * top); + + /* + * Fetch all the field information. + */ + int first_field_idx = mono_class_has_static_metadata (klass) ? mono_class_get_first_field_idx (klass) : 0; + for (i = 0; i < top; i++) { + field = &fields [i]; + field->parent = klass; + + if (gtd) { + field->name = mono_field_get_name (>d->fields [i]); + } else { + int idx = first_field_idx + i; + /* first_field_idx and idx points into the fieldptr table */ + guint32 name_idx = mono_metadata_decode_table_row_col (image, MONO_TABLE_FIELD, idx, MONO_FIELD_NAME); + /* The name is needed for fieldrefs */ + field->name = mono_metadata_string_heap (image, name_idx); + } + } + + mono_memory_barrier (); + + mono_loader_lock (); + if (!klass->fields) + klass->fields = fields; + mono_loader_unlock (); +} + +/** + * mono_class_set_failure_causedby_class: + * \param klass the class that is failing + * \param caused_by the class that caused the failure + * \param msg Why \p klass is failing. + * + * If \p caused_by has a failure, sets a TypeLoadException failure on + * \p klass with message "\p msg, due to: {\p caused_by message}". + * + * \returns TRUE if a failiure was set, or FALSE if \p caused_by doesn't have a failure. + */ +static gboolean +mono_class_set_type_load_failure_causedby_class (MonoClass *klass, const MonoClass *caused_by, const gchar* msg) +{ + if (mono_class_has_failure (caused_by)) { + MonoError cause_error; + error_init (&cause_error); + mono_error_set_for_class_failure (&cause_error, caused_by); + mono_class_set_type_load_failure (klass, "%s, due to: %s", msg, mono_error_get_message (&cause_error)); + mono_error_cleanup (&cause_error); + return TRUE; + } else { + return FALSE; + } +} + + +/** + * mono_class_setup_fields: + * \p klass The class to initialize + * + * Initializes klass->fields, computes class layout and sizes. + * typebuilder_setup_fields () is the corresponding function for dynamic classes. + * Sets the following fields in \p klass: + * - all the fields initialized by mono_class_init_sizes () + * - element_class/cast_class (for enums) + * - sizes:element_size (for arrays) + * - field->type/offset for all fields + * - fields_inited + * + * LOCKING: Acquires the loader lock. + */ +void +mono_class_setup_fields (MonoClass *klass) +{ + MonoError error; + MonoImage *m = klass->image; + int top; + guint32 layout = mono_class_get_flags (klass) & TYPE_ATTRIBUTE_LAYOUT_MASK; + int i; + guint32 real_size = 0; + guint32 packing_size = 0; + int instance_size; + gboolean explicit_size; + MonoClassField *field; + MonoGenericClass *gklass = mono_class_try_get_generic_class (klass); + MonoClass *gtd = gklass ? mono_class_get_generic_type_definition (klass) : NULL; + + if (klass->fields_inited) + return; + + if (gklass && image_is_dynamic (gklass->container_class->image) && !gklass->container_class->wastypebuilder) { + /* + * This happens when a generic instance of an unfinished generic typebuilder + * is used as an element type for creating an array type. We can't initialize + * the fields of this class using the fields of gklass, since gklass is not + * finished yet, fields could be added to it later. + */ + return; + } + + mono_class_setup_basic_field_info (klass); + top = mono_class_get_field_count (klass); + + if (gtd) { + mono_class_setup_fields (gtd); + if (mono_class_set_type_load_failure_causedby_class (klass, gtd, "Generic type definition failed")) + return; + } + + instance_size = 0; + if (klass->parent) { + /* For generic instances, klass->parent might not have been initialized */ + mono_class_init (klass->parent); + mono_class_setup_fields (klass->parent); + if (mono_class_set_type_load_failure_causedby_class (klass, klass->parent, "Could not set up parent class")) + return; + instance_size = klass->parent->instance_size; + } else { + instance_size = sizeof (MonoObject); + } + + /* Get the real size */ + explicit_size = mono_metadata_packing_from_typedef (klass->image, klass->type_token, &packing_size, &real_size); + if (explicit_size) + instance_size += real_size; + + /* + * This function can recursively call itself. + * Prevent infinite recursion by using a list in TLS. + */ + GSList *init_list = (GSList *)mono_native_tls_get_value (setup_fields_tls_id); + if (g_slist_find (init_list, klass)) + return; + init_list = g_slist_prepend (init_list, klass); + mono_native_tls_set_value (setup_fields_tls_id, init_list); + + /* + * Fetch all the field information. + */ + int first_field_idx = mono_class_has_static_metadata (klass) ? mono_class_get_first_field_idx (klass) : 0; + for (i = 0; i < top; i++) { + int idx = first_field_idx + i; + field = &klass->fields [i]; + + if (!field->type) { + mono_field_resolve_type (field, &error); + if (!mono_error_ok (&error)) { + /*mono_field_resolve_type already failed class*/ + mono_error_cleanup (&error); + break; + } + if (!field->type) + g_error ("could not resolve %s:%s\n", mono_type_get_full_name(klass), field->name); + g_assert (field->type); + } + + if (!mono_type_get_underlying_type (field->type)) { + mono_class_set_type_load_failure (klass, "Field '%s' is an enum type with a bad underlying type", field->name); + break; + } + + if (mono_field_is_deleted (field)) + continue; + if (layout == TYPE_ATTRIBUTE_EXPLICIT_LAYOUT) { + guint32 uoffset; + mono_metadata_field_info (m, idx, &uoffset, NULL, NULL); + int offset = uoffset; + + if (offset == (guint32)-1 && !(field->type->attrs & FIELD_ATTRIBUTE_STATIC)) { + mono_class_set_type_load_failure (klass, "Missing field layout info for %s", field->name); + break; + } + if (offset < -1) { /*-1 is used to encode special static fields */ + mono_class_set_type_load_failure (klass, "Field '%s' has a negative offset %d", field->name, offset); + break; + } + if (mono_class_is_gtd (klass)) { + mono_class_set_type_load_failure (klass, "Generic class cannot have explicit layout."); + break; + } + } + if (mono_type_has_exceptions (field->type)) { + char *class_name = mono_type_get_full_name (klass); + char *type_name = mono_type_full_name (field->type); + + mono_class_set_type_load_failure (klass, "Invalid type %s for instance field %s:%s", type_name, class_name, field->name); + g_free (class_name); + g_free (type_name); + break; + } + /* The def_value of fields is compute lazily during vtable creation */ + } + + if (!mono_class_has_failure (klass)) { + mono_loader_lock (); + mono_class_layout_fields (klass, instance_size, packing_size, FALSE); + mono_loader_unlock (); + } + + init_list = g_slist_remove (init_list, klass); + mono_native_tls_set_value (setup_fields_tls_id, init_list); +} + +static void +init_sizes_with_info (MonoClass *klass, MonoCachedClassInfo *cached_info) +{ + if (cached_info) { + mono_loader_lock (); + klass->instance_size = cached_info->instance_size; + klass->sizes.class_size = cached_info->class_size; + klass->packing_size = cached_info->packing_size; + klass->min_align = cached_info->min_align; + klass->blittable = cached_info->blittable; + klass->has_references = cached_info->has_references; + klass->has_static_refs = cached_info->has_static_refs; + klass->no_special_static_fields = cached_info->no_special_static_fields; + klass->has_weak_fields = cached_info->has_weak_fields; + mono_loader_unlock (); + } + else { + if (!klass->size_inited) + mono_class_setup_fields (klass); + } +} +/* + + * mono_class_init_sizes: + * + * Initializes the size related fields of @klass without loading all field data if possible. + * Sets the following fields in @klass: + * - instance_size + * - sizes.class_size + * - packing_size + * - min_align + * - blittable + * - has_references + * - has_static_refs + * - size_inited + * Can fail the class. + * + * LOCKING: Acquires the loader lock. + */ +static void +mono_class_init_sizes (MonoClass *klass) +{ + MonoCachedClassInfo cached_info; + gboolean has_cached_info; + + if (klass->size_inited) + return; + + has_cached_info = mono_class_get_cached_class_info (klass, &cached_info); + + init_sizes_with_info (klass, has_cached_info ? &cached_info : NULL); +} + +/* + * mono_type_get_basic_type_from_generic: + * @type: a type + * + * Returns a closed type corresponding to the possibly open type + * passed to it. + */ +MonoType* +mono_type_get_basic_type_from_generic (MonoType *type) +{ + /* When we do generic sharing we let type variables stand for reference/primitive types. */ + if (!type->byref && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) && + (!type->data.generic_param->gshared_constraint || type->data.generic_param->gshared_constraint->type == MONO_TYPE_OBJECT)) + return &mono_defaults.object_class->byval_arg; + return type; +} + +static gboolean +class_has_references (MonoClass *klass) +{ + mono_class_init_sizes (klass); + + /* + * has_references is not set if this is called recursively, but this is not a problem since this is only used + * during field layout, and instance fields are initialized before static fields, and instance fields can't + * embed themselves. + */ + return klass->has_references; +} + +static gboolean +type_has_references (MonoClass *klass, MonoType *ftype) +{ + if (MONO_TYPE_IS_REFERENCE (ftype) || IS_GC_REFERENCE (klass, ftype) || ((MONO_TYPE_ISSTRUCT (ftype) && class_has_references (mono_class_from_mono_type (ftype))))) + return TRUE; + if (!ftype->byref && (ftype->type == MONO_TYPE_VAR || ftype->type == MONO_TYPE_MVAR)) { + MonoGenericParam *gparam = ftype->data.generic_param; + + if (gparam->gshared_constraint) + return class_has_references (mono_class_from_mono_type (gparam->gshared_constraint)); + } + 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 + * @base_instance_size: base instance size + * @packing_size: + * + * This contains the common code for computing the layout of classes and sizes. + * This should only be called from mono_class_setup_fields () and + * typebuilder_setup_fields (). + * + * LOCKING: Acquires the loader lock + */ +void +mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_size, gboolean sre) +{ + int i; + const int top = mono_class_get_field_count (klass); + guint32 layout = mono_class_get_flags (klass) & TYPE_ATTRIBUTE_LAYOUT_MASK; + guint32 pass, passes, real_size; + gboolean gc_aware_layout = FALSE; + gboolean has_static_fields = FALSE; + gboolean has_references = FALSE; + gboolean has_static_refs = FALSE; + MonoClassField *field; + gboolean blittable; + int instance_size = base_instance_size; + int element_size = -1; + int class_size, min_align; + int *field_offsets; + gboolean *fields_has_references; + + /* + * We want to avoid doing complicated work inside locks, so we compute all the required + * information and write it to @klass inside a lock. + */ + if (klass->fields_inited) + return; + + if ((packing_size & 0xffffff00) != 0) { + mono_class_set_type_load_failure (klass, "Could not load struct '%s' with packing size %d >= 256", klass->name, packing_size); + return; + } + + if (klass->parent) { + min_align = klass->parent->min_align; + /* we use | since it may have been set already */ + has_references = klass->has_references | klass->parent->has_references; + } else { + min_align = 1; + } + /* We can't really enable 16 bytes alignment until the GC supports it. + The whole layout/instance size code must be reviewed because we do alignment calculation in terms of the + boxed instance, which leads to unexplainable holes at the beginning of an object embedding a simd type. + Bug #506144 is an example of this issue. + + if (klass->simd_type) + min_align = 16; + */ + + /* + * When we do generic sharing we need to have layout + * information for open generic classes (either with a generic + * context containing type variables or with a generic + * container), so we don't return in that case anymore. + */ + + if (klass->enumtype) { + for (i = 0; i < top; i++) { + field = &klass->fields [i]; + if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC)) { + klass->cast_class = klass->element_class = mono_class_from_mono_type (field->type); + break; + } + } + + if (!mono_class_enum_basetype (klass)) { + mono_class_set_type_load_failure (klass, "The enumeration's base type is invalid."); + return; + } + } + + /* + * Enable GC aware auto layout: in this mode, reference + * fields are grouped together inside objects, increasing collector + * performance. + * Requires that all classes whose layout is known to native code be annotated + * with [StructLayout (LayoutKind.Sequential)] + * Value types have gc_aware_layout disabled by default, as per + * what the default is for other runtimes. + */ + /* corlib is missing [StructLayout] directives in many places */ + 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 */ + blittable = TRUE; + if (klass->parent) + blittable = klass->parent->blittable; + if (layout == TYPE_ATTRIBUTE_AUTO_LAYOUT && !(mono_is_corlib_image (klass->image) && !strcmp (klass->name_space, "System") && !strcmp (klass->name, "ValueType")) && top) + blittable = FALSE; + for (i = 0; i < top; i++) { + field = &klass->fields [i]; + + if (mono_field_is_deleted (field)) + continue; + if (field->type->attrs & FIELD_ATTRIBUTE_STATIC) + continue; + 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) { + mono_class_setup_fields (field_class); + if (mono_class_has_failure (field_class)) { + MonoError field_error; + error_init (&field_error); + mono_error_set_for_class_failure (&field_error, field_class); + mono_class_set_type_load_failure (klass, "Could not set up field '%s' due to: %s", field->name, mono_error_get_message (&field_error)); + mono_error_cleanup (&field_error); + break; + } + } + if (!field_class || !field_class->blittable) + blittable = FALSE; + } + } + if (klass->enumtype) + blittable = klass->element_class->blittable; + } + if (mono_class_has_failure (klass)) + return; + if (klass == mono_defaults.string_class) + blittable = FALSE; + + /* Compute klass->has_references */ + /* + * Process non-static fields first, since static fields might recursively + * refer to the class itself. + */ + for (i = 0; i < top; i++) { + MonoType *ftype; + + field = &klass->fields [i]; + + if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC)) { + ftype = mono_type_get_underlying_type (field->type); + ftype = mono_type_get_basic_type_from_generic (ftype); + if (type_has_references (klass, ftype)) + has_references = TRUE; + } + } + + /* + * Compute field layout and total size (not considering static fields) + */ + field_offsets = g_new0 (int, top); + fields_has_references = g_new0 (gboolean, top); + int first_field_idx = mono_class_has_static_metadata (klass) ? mono_class_get_first_field_idx (klass) : 0; + switch (layout) { + case TYPE_ATTRIBUTE_AUTO_LAYOUT: + case TYPE_ATTRIBUTE_SEQUENTIAL_LAYOUT: + if (gc_aware_layout) + passes = 2; + else + passes = 1; + + if (layout != TYPE_ATTRIBUTE_AUTO_LAYOUT) + passes = 1; + + if (klass->parent) { + mono_class_setup_fields (klass->parent); + if (mono_class_set_type_load_failure_causedby_class (klass, klass->parent, "Cannot initialize parent class")) + return; + real_size = klass->parent->instance_size; + } else { + real_size = sizeof (MonoObject); + } + + for (pass = 0; pass < passes; ++pass) { + for (i = 0; i < top; i++){ + gint32 align; + guint32 size; + MonoType *ftype; + + field = &klass->fields [i]; + + if (mono_field_is_deleted (field)) + continue; + if (field->type->attrs & FIELD_ATTRIBUTE_STATIC) + continue; + + ftype = mono_type_get_underlying_type (field->type); + ftype = mono_type_get_basic_type_from_generic (ftype); + if (gc_aware_layout) { + fields_has_references [i] = type_has_references (klass, ftype); + if (fields_has_references [i]) { + if (pass == 1) + continue; + } else { + if (pass == 0) + continue; + } + } + + if ((top == 1) && (instance_size == sizeof (MonoObject)) && + (strcmp (mono_field_get_name (field), "$PRIVATE$") == 0)) { + /* This field is a hack inserted by MCS to empty structures */ + continue; + } + + size = mono_type_size (field->type, &align); + + /* FIXME (LAMESPEC): should we also change the min alignment according to pack? */ + align = packing_size ? MIN (packing_size, align): align; + /* if the field has managed references, we need to force-align it + * see bug #77788 + */ + if (type_has_references (klass, ftype)) + align = MAX (align, sizeof (gpointer)); + + min_align = MAX (align, min_align); + field_offsets [i] = real_size; + if (align) { + field_offsets [i] += align - 1; + field_offsets [i] &= ~(align - 1); + } + /*TypeBuilders produce all sort of weird things*/ + g_assert (image_is_dynamic (klass->image) || field_offsets [i] > 0); + real_size = field_offsets [i] + size; + } + + instance_size = MAX (real_size, instance_size); + + if (instance_size & (min_align - 1)) { + instance_size += min_align - 1; + instance_size &= ~(min_align - 1); + } + } + break; + case TYPE_ATTRIBUTE_EXPLICIT_LAYOUT: { + guint8 *ref_bitmap; + + real_size = 0; + for (i = 0; i < top; i++) { + gint32 align; + guint32 size; + MonoType *ftype; + + field = &klass->fields [i]; + + /* + * There must be info about all the fields in a type if it + * uses explicit layout. + */ + if (mono_field_is_deleted (field)) + continue; + if (field->type->attrs & FIELD_ATTRIBUTE_STATIC) + continue; + + size = mono_type_size (field->type, &align); + align = packing_size ? MIN (packing_size, align): align; + min_align = MAX (align, min_align); + + if (sre) { + /* Already set by typebuilder_setup_fields () */ + field_offsets [i] = field->offset + sizeof (MonoObject); + } else { + int idx = first_field_idx + i; + guint32 offset; + mono_metadata_field_info (klass->image, idx, &offset, NULL, NULL); + field_offsets [i] = offset + sizeof (MonoObject); + } + ftype = mono_type_get_underlying_type (field->type); + ftype = mono_type_get_basic_type_from_generic (ftype); + if (type_has_references (klass, ftype)) { + if (field_offsets [i] % sizeof (gpointer)) { + mono_class_set_type_load_failure (klass, "Reference typed field '%s' has explicit offset that is not pointer-size aligned.", field->name); + } + } + + /* + * Calc max size. + */ + real_size = MAX (real_size, size + field_offsets [i]); + } + + if (klass->has_references) { + ref_bitmap = g_new0 (guint8, real_size / sizeof (gpointer)); + + /* Check for overlapping reference and non-reference fields */ + for (i = 0; i < top; i++) { + MonoType *ftype; + + field = &klass->fields [i]; + + if (mono_field_is_deleted (field)) + continue; + if (field->type->attrs & FIELD_ATTRIBUTE_STATIC) + continue; + ftype = mono_type_get_underlying_type (field->type); + if (MONO_TYPE_IS_REFERENCE (ftype)) + ref_bitmap [field_offsets [i] / sizeof (gpointer)] = 1; + } + for (i = 0; i < top; i++) { + field = &klass->fields [i]; + + if (mono_field_is_deleted (field)) + continue; + if (field->type->attrs & FIELD_ATTRIBUTE_STATIC) + continue; + + // FIXME: Too much code does this +#if 0 + if (!MONO_TYPE_IS_REFERENCE (field->type) && ref_bitmap [field_offsets [i] / sizeof (gpointer)]) { + mono_class_set_type_load_failure (klass, "Could not load type '%s' because it contains an object field at offset %d that is incorrectly aligned or overlapped by a non-object field.", klass->name, field_offsets [i]); + } +#endif + } + g_free (ref_bitmap); + } + + instance_size = MAX (real_size, instance_size); + if (instance_size & (min_align - 1)) { + instance_size += min_align - 1; + instance_size &= ~(min_align - 1); + } + break; + } + } + + if (layout != TYPE_ATTRIBUTE_EXPLICIT_LAYOUT) { + /* + * This leads to all kinds of problems with nested structs, so only + * enable it when a MONO_DEBUG property is set. + * + * For small structs, set min_align to at least the struct size to improve + * performance, and since the JIT memset/memcpy code assumes this and generates + * unaligned accesses otherwise. See #78990 for a testcase. + */ + if (mono_align_small_structs && top) { + if (instance_size <= sizeof (MonoObject) + sizeof (gpointer)) + min_align = MAX (min_align, instance_size - sizeof (MonoObject)); + } + } + + if (klass->byval_arg.type == MONO_TYPE_VAR || klass->byval_arg.type == MONO_TYPE_MVAR) + instance_size = sizeof (MonoObject) + mono_type_stack_size_internal (&klass->byval_arg, NULL, TRUE); + else if (klass->byval_arg.type == MONO_TYPE_PTR) + instance_size = sizeof (MonoObject) + sizeof (gpointer); + + if (klass->byval_arg.type == MONO_TYPE_SZARRAY || klass->byval_arg.type == MONO_TYPE_ARRAY) + element_size = mono_class_array_element_size (klass->element_class); + + /* Publish the data */ + mono_loader_lock (); + if (klass->instance_size && !klass->image->dynamic) { + /* Might be already set using cached info */ + if (klass->instance_size != instance_size) { + /* Emit info to help debugging */ + g_print ("%s\n", mono_class_full_name (klass)); + g_print ("%d %d %d %d\n", klass->instance_size, instance_size, klass->blittable, blittable); + g_print ("%d %d %d %d\n", klass->has_references, has_references, klass->packing_size, packing_size); + g_print ("%d %d\n", klass->min_align, min_align); + for (i = 0; i < top; ++i) { + field = &klass->fields [i]; + if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC)) + printf (" %s %d %d %d\n", klass->fields [i].name, klass->fields [i].offset, field_offsets [i], fields_has_references [i]); + } + } + g_assert (klass->instance_size == instance_size); + } else { + klass->instance_size = instance_size; + } + klass->blittable = blittable; + klass->has_references = has_references; + klass->packing_size = packing_size; + klass->min_align = min_align; + for (i = 0; i < top; ++i) { + field = &klass->fields [i]; + if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC)) + klass->fields [i].offset = field_offsets [i]; + } + + if (klass->byval_arg.type == MONO_TYPE_SZARRAY || klass->byval_arg.type == MONO_TYPE_ARRAY) + klass->sizes.element_size = element_size; + + mono_memory_barrier (); + klass->size_inited = 1; + mono_loader_unlock (); + + /* + * Compute static field layout and size + * Static fields can reference the class itself, so this has to be + * done after instance_size etc. are initialized. + */ + class_size = 0; + for (i = 0; i < top; i++) { + gint32 align; + guint32 size; + + field = &klass->fields [i]; + + if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC) || field->type->attrs & FIELD_ATTRIBUTE_LITERAL) + continue; + if (mono_field_is_deleted (field)) + continue; + + if (mono_type_has_exceptions (field->type)) { + mono_class_set_type_load_failure (klass, "Field '%s' has an invalid type.", field->name); + break; + } + + has_static_fields = TRUE; + + size = mono_type_size (field->type, &align); + field_offsets [i] = class_size; + /*align is always non-zero here*/ + field_offsets [i] += align - 1; + field_offsets [i] &= ~(align - 1); + class_size = field_offsets [i] + size; + } + + if (has_static_fields && class_size == 0) + /* Simplify code which depends on class_size != 0 if the class has static fields */ + class_size = 8; + + /* Compute klass->has_static_refs */ + has_static_refs = FALSE; + for (i = 0; i < top; i++) { + MonoType *ftype; + + field = &klass->fields [i]; + + if (field->type->attrs & FIELD_ATTRIBUTE_STATIC) { + ftype = mono_type_get_underlying_type (field->type); + ftype = mono_type_get_basic_type_from_generic (ftype); + if (type_has_references (klass, ftype)) + has_static_refs = TRUE; + } + } + + /*valuetypes can't be neither bigger than 1Mb or empty. */ + if (klass->valuetype && (klass->instance_size <= 0 || klass->instance_size > (0x100000 + sizeof (MonoObject)))) { + /* Special case compiler generated types */ + /* Hard to check for [CompilerGenerated] here */ + if (!strstr (klass->name, "StaticArrayInitTypeSize") && !strstr (klass->name, "$ArrayType")) + mono_class_set_type_load_failure (klass, "Value type instance size (%d) cannot be zero, negative, or bigger than 1Mb", klass->instance_size); + } + + // Weak field support + // + // FIXME: + // - generic instances + // - Disallow on structs/static fields/nonref fields + gboolean has_weak_fields = FALSE; + + if (mono_class_has_static_metadata (klass)) { + for (MonoClass *p = klass; p != NULL; p = p->parent) { + gpointer iter = NULL; + guint32 first_field_idx = mono_class_get_first_field_idx (p); + + while ((field = mono_class_get_fields (p, &iter))) { + guint32 field_idx = first_field_idx + (field - p->fields); + if (MONO_TYPE_IS_REFERENCE (field->type) && mono_assembly_is_weak_field (p->image, field_idx + 1)) { + has_weak_fields = TRUE; + mono_trace_message (MONO_TRACE_TYPE, "Field %s:%s at offset %x is weak.", field->parent->name, field->name, field->offset); + } + } + } + } + + /* Publish the data */ + mono_loader_lock (); + if (!klass->rank) + klass->sizes.class_size = class_size; + klass->has_static_refs = has_static_refs; + klass->has_weak_fields = has_weak_fields; + for (i = 0; i < top; ++i) { + field = &klass->fields [i]; + + if (field->type->attrs & FIELD_ATTRIBUTE_STATIC) + field->offset = field_offsets [i]; + } + mono_memory_barrier (); + klass->fields_inited = 1; + mono_loader_unlock (); + + g_free (field_offsets); + g_free (fields_has_references); +} + +static MonoMethod* +create_array_method (MonoClass *klass, const char *name, MonoMethodSignature *sig) +{ + MonoMethod *method; + + method = (MonoMethod *) mono_image_alloc0 (klass->image, sizeof (MonoMethodPInvoke)); + method->klass = klass; + method->flags = METHOD_ATTRIBUTE_PUBLIC; + method->iflags = METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL; + method->signature = sig; + method->name = name; + method->slot = -1; + /* .ctor */ + if (name [0] == '.') { + method->flags |= METHOD_ATTRIBUTE_RT_SPECIAL_NAME | METHOD_ATTRIBUTE_SPECIAL_NAME; + } else { + method->iflags |= METHOD_IMPL_ATTRIBUTE_RUNTIME; + } + return method; +} + +/* + * mono_class_setup_methods: + * @class: a class + * + * Initializes the 'methods' array in CLASS. + * Calling this method should be avoided if possible since it allocates a lot + * of long-living MonoMethod structures. + * Methods belonging to an interface are assigned a sequential slot starting + * from 0. + * + * On failure this function sets klass->has_failure and stores a MonoErrorBoxed with details + */ +void +mono_class_setup_methods (MonoClass *klass) +{ + int i, count; + MonoMethod **methods; + + if (klass->methods) + return; + + if (mono_class_is_ginst (klass)) { + MonoError error; + MonoClass *gklass = mono_class_get_generic_class (klass)->container_class; + + mono_class_init (gklass); + if (!mono_class_has_failure (gklass)) + mono_class_setup_methods (gklass); + if (mono_class_set_type_load_failure_causedby_class (klass, gklass, "Generic type definition failed to load")) + return; + + /* The + 1 makes this always non-NULL to pass the check in mono_class_setup_methods () */ + count = mono_class_get_method_count (gklass); + methods = (MonoMethod **)mono_class_alloc0 (klass, sizeof (MonoMethod*) * (count + 1)); + + for (i = 0; i < count; i++) { + methods [i] = mono_class_inflate_generic_method_full_checked ( + gklass->methods [i], klass, mono_class_get_context (klass), &error); + if (!mono_error_ok (&error)) { + char *method = mono_method_full_name (gklass->methods [i], TRUE); + mono_class_set_type_load_failure (klass, "Could not inflate method %s due to %s", method, mono_error_get_message (&error)); + + g_free (method); + mono_error_cleanup (&error); + return; + } + } + } else if (klass->rank) { + MonoError error; + MonoMethod *amethod; + MonoMethodSignature *sig; + int count_generic = 0, first_generic = 0; + int method_num = 0; + gboolean jagged_ctor = FALSE; + + count = 3 + (klass->rank > 1? 2: 1); + + mono_class_setup_interfaces (klass, &error); + g_assert (mono_error_ok (&error)); /*FIXME can this fail for array types?*/ + + if (klass->rank == 1 && klass->element_class->rank) { + jagged_ctor = TRUE; + count ++; + } + + if (klass->interface_count) { + count_generic = generic_array_methods (klass); + first_generic = count; + count += klass->interface_count * count_generic; + } + + methods = (MonoMethod **)mono_class_alloc0 (klass, sizeof (MonoMethod*) * count); + + sig = mono_metadata_signature_alloc (klass->image, klass->rank); + sig->ret = &mono_defaults.void_class->byval_arg; + sig->pinvoke = TRUE; + sig->hasthis = TRUE; + for (i = 0; i < klass->rank; ++i) + sig->params [i] = &mono_defaults.int32_class->byval_arg; + + amethod = create_array_method (klass, ".ctor", sig); + methods [method_num++] = amethod; + if (klass->rank > 1) { + sig = mono_metadata_signature_alloc (klass->image, klass->rank * 2); + sig->ret = &mono_defaults.void_class->byval_arg; + sig->pinvoke = TRUE; + sig->hasthis = TRUE; + for (i = 0; i < klass->rank * 2; ++i) + sig->params [i] = &mono_defaults.int32_class->byval_arg; + + amethod = create_array_method (klass, ".ctor", sig); + methods [method_num++] = amethod; + } + + if (jagged_ctor) { + /* Jagged arrays have an extra ctor in .net which creates an array of arrays */ + sig = mono_metadata_signature_alloc (klass->image, klass->rank + 1); + sig->ret = &mono_defaults.void_class->byval_arg; + sig->pinvoke = TRUE; + sig->hasthis = TRUE; + for (i = 0; i < klass->rank + 1; ++i) + sig->params [i] = &mono_defaults.int32_class->byval_arg; + amethod = create_array_method (klass, ".ctor", sig); + methods [method_num++] = amethod; + } + + /* element Get (idx11, [idx2, ...]) */ + sig = mono_metadata_signature_alloc (klass->image, klass->rank); + sig->ret = &klass->element_class->byval_arg; + sig->pinvoke = TRUE; + sig->hasthis = TRUE; + for (i = 0; i < klass->rank; ++i) + sig->params [i] = &mono_defaults.int32_class->byval_arg; + amethod = create_array_method (klass, "Get", sig); + methods [method_num++] = amethod; + /* element& Address (idx11, [idx2, ...]) */ + sig = mono_metadata_signature_alloc (klass->image, klass->rank); + sig->ret = &klass->element_class->this_arg; + sig->pinvoke = TRUE; + sig->hasthis = TRUE; + for (i = 0; i < klass->rank; ++i) + sig->params [i] = &mono_defaults.int32_class->byval_arg; + amethod = create_array_method (klass, "Address", sig); + methods [method_num++] = amethod; + /* void Set (idx11, [idx2, ...], element) */ + sig = mono_metadata_signature_alloc (klass->image, klass->rank + 1); + sig->ret = &mono_defaults.void_class->byval_arg; + sig->pinvoke = TRUE; + sig->hasthis = TRUE; + for (i = 0; i < klass->rank; ++i) + sig->params [i] = &mono_defaults.int32_class->byval_arg; + sig->params [i] = &klass->element_class->byval_arg; + amethod = create_array_method (klass, "Set", sig); + methods [method_num++] = amethod; + + GHashTable *cache = g_hash_table_new (NULL, NULL); + for (i = 0; i < klass->interface_count; i++) + setup_generic_array_ifaces (klass, klass->interfaces [i], methods, first_generic + i * count_generic, cache); + g_hash_table_destroy (cache); + } else if (mono_class_has_static_metadata (klass)) { + MonoError error; + int first_idx = mono_class_get_first_method_idx (klass); + + count = mono_class_get_method_count (klass); + methods = (MonoMethod **)mono_class_alloc (klass, sizeof (MonoMethod*) * count); + for (i = 0; i < count; ++i) { + int idx = mono_metadata_translate_token_index (klass->image, MONO_TABLE_METHOD, first_idx + i + 1); + methods [i] = mono_get_method_checked (klass->image, MONO_TOKEN_METHOD_DEF | idx, klass, NULL, &error); + if (!methods [i]) { + mono_class_set_type_load_failure (klass, "Could not load method %d due to %s", i, mono_error_get_message (&error)); + mono_error_cleanup (&error); + } + } + } else { + methods = (MonoMethod **)mono_class_alloc (klass, sizeof (MonoMethod*) * 1); + count = 0; + } + + if (MONO_CLASS_IS_INTERFACE (klass)) { + int slot = 0; + /*Only assign slots to virtual methods as interfaces are allowed to have static methods.*/ + for (i = 0; i < count; ++i) { + if (methods [i]->flags & METHOD_ATTRIBUTE_VIRTUAL) + methods [i]->slot = slot++; + } + } + + mono_image_lock (klass->image); + + if (!klass->methods) { + mono_class_set_method_count (klass, count); + + /* Needed because of the double-checking locking pattern */ + mono_memory_barrier (); + + klass->methods = methods; + } + + mono_image_unlock (klass->image); +} + +/* + * mono_class_get_method_by_index: + * + * Returns klass->methods [index], initializing klass->methods if neccesary. + * + * LOCKING: Acquires the loader lock. + */ +MonoMethod* +mono_class_get_method_by_index (MonoClass *klass, int index) +{ + MonoError error; + + MonoGenericClass *gklass = mono_class_try_get_generic_class (klass); + /* Avoid calling setup_methods () if possible */ + if (gklass && !klass->methods) { + MonoMethod *m; + + m = mono_class_inflate_generic_method_full_checked ( + gklass->container_class->methods [index], klass, mono_class_get_context (klass), &error); + g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */ + /* + * If setup_methods () is called later for this class, no duplicates are created, + * since inflate_generic_method guarantees that only one instance of a method + * is created for each context. + */ + /* + mono_class_setup_methods (klass); + g_assert (m == klass->methods [index]); + */ + return m; + } else { + mono_class_setup_methods (klass); + if (mono_class_has_failure (klass)) /*FIXME do proper error handling*/ + return NULL; + g_assert (index >= 0 && index < mono_class_get_method_count (klass)); + return klass->methods [index]; + } +} + +/* + * mono_class_get_inflated_method: + * + * Given an inflated class CLASS and a method METHOD which should be a method of + * CLASS's generic definition, return the inflated method corresponding to METHOD. + */ +MonoMethod* +mono_class_get_inflated_method (MonoClass *klass, MonoMethod *method) +{ + MonoClass *gklass = mono_class_get_generic_class (klass)->container_class; + int i, mcount; + + g_assert (method->klass == gklass); + + mono_class_setup_methods (gklass); + g_assert (!mono_class_has_failure (gklass)); /*FIXME do proper error handling*/ + + mcount = mono_class_get_method_count (gklass); + for (i = 0; i < mcount; ++i) { + if (gklass->methods [i] == method) { + if (klass->methods) { + return klass->methods [i]; + } else { + MonoError error; + MonoMethod *result = mono_class_inflate_generic_method_full_checked (gklass->methods [i], klass, mono_class_get_context (klass), &error); + g_assert (mono_error_ok (&error)); /* FIXME don't swallow this error */ + return result; + } + } + } + + return NULL; +} + +/* + * mono_class_get_vtable_entry: + * + * Returns klass->vtable [offset], computing it if neccesary. Returns NULL on failure. + * LOCKING: Acquires the loader lock. + */ +MonoMethod* +mono_class_get_vtable_entry (MonoClass *klass, int offset) +{ + MonoMethod *m; + + if (klass->rank == 1) { + /* + * szarrays do not overwrite any methods of Array, so we can avoid + * initializing their vtables in some cases. + */ + mono_class_setup_vtable (klass->parent); + if (offset < klass->parent->vtable_size) + return klass->parent->vtable [offset]; + } + + if (mono_class_is_ginst (klass)) { + MonoError error; + MonoClass *gklass = mono_class_get_generic_class (klass)->container_class; + mono_class_setup_vtable (gklass); + m = gklass->vtable [offset]; + + m = mono_class_inflate_generic_method_full_checked (m, klass, mono_class_get_context (klass), &error); + g_assert (mono_error_ok (&error)); /* FIXME don't swallow this error */ + } else { + mono_class_setup_vtable (klass); + if (mono_class_has_failure (klass)) + return NULL; + m = klass->vtable [offset]; + } + + return m; +} + +/* + * mono_class_get_vtable_size: + * + * Return the vtable size for KLASS. + */ +int +mono_class_get_vtable_size (MonoClass *klass) +{ + mono_class_setup_vtable (klass); + + return klass->vtable_size; +} + +/* + * mono_class_setup_properties: + * + * Initialize klass->ext.property and klass->ext.properties. + * + * This method can fail the class. + */ +static void +mono_class_setup_properties (MonoClass *klass) +{ + guint startm, endm, i, j; + guint32 cols [MONO_PROPERTY_SIZE]; + MonoTableInfo *msemt = &klass->image->tables [MONO_TABLE_METHODSEMANTICS]; + MonoProperty *properties; + guint32 last; + int first, count; + MonoClassPropertyInfo *info; + + info = mono_class_get_property_info (klass); + if (info) + return; + + if (mono_class_is_ginst (klass)) { + MonoClass *gklass = mono_class_get_generic_class (klass)->container_class; + + mono_class_init (gklass); + mono_class_setup_properties (gklass); + if (mono_class_set_type_load_failure_causedby_class (klass, gklass, "Generic type definition failed to load")) + return; + + MonoClassPropertyInfo *ginfo = mono_class_get_property_info (gklass); + properties = mono_class_new0 (klass, MonoProperty, ginfo->count + 1); + + for (i = 0; i < ginfo->count; i++) { + MonoError error; + MonoProperty *prop = &properties [i]; + + *prop = ginfo->properties [i]; + + if (prop->get) + prop->get = mono_class_inflate_generic_method_full_checked ( + prop->get, klass, mono_class_get_context (klass), &error); + if (prop->set) + prop->set = mono_class_inflate_generic_method_full_checked ( + prop->set, klass, mono_class_get_context (klass), &error); + + g_assert (mono_error_ok (&error)); /*FIXME proper error handling*/ + prop->parent = klass; + } + + first = ginfo->first; + count = ginfo->count; + } else { + first = mono_metadata_properties_from_typedef (klass->image, mono_metadata_token_index (klass->type_token) - 1, &last); + count = last - first; + + if (count) { + mono_class_setup_methods (klass); + if (mono_class_has_failure (klass)) + return; + } + + properties = (MonoProperty *)mono_class_alloc0 (klass, sizeof (MonoProperty) * count); + for (i = first; i < last; ++i) { + mono_metadata_decode_table_row (klass->image, MONO_TABLE_PROPERTY, i, cols, MONO_PROPERTY_SIZE); + properties [i - first].parent = klass; + properties [i - first].attrs = cols [MONO_PROPERTY_FLAGS]; + properties [i - first].name = mono_metadata_string_heap (klass->image, cols [MONO_PROPERTY_NAME]); + + startm = mono_metadata_methods_from_property (klass->image, i, &endm); + int first_idx = mono_class_get_first_method_idx (klass); + for (j = startm; j < endm; ++j) { + MonoMethod *method; + + mono_metadata_decode_row (msemt, j, cols, MONO_METHOD_SEMA_SIZE); + + if (klass->image->uncompressed_metadata) { + MonoError error; + /* It seems like the MONO_METHOD_SEMA_METHOD column needs no remapping */ + method = mono_get_method_checked (klass->image, MONO_TOKEN_METHOD_DEF | cols [MONO_METHOD_SEMA_METHOD], klass, NULL, &error); + mono_error_cleanup (&error); /* FIXME don't swallow this error */ + } else { + method = klass->methods [cols [MONO_METHOD_SEMA_METHOD] - 1 - first_idx]; + } + + switch (cols [MONO_METHOD_SEMA_SEMANTICS]) { + case METHOD_SEMANTIC_SETTER: + properties [i - first].set = method; + break; + case METHOD_SEMANTIC_GETTER: + properties [i - first].get = method; + break; + default: + break; + } + } + } + } + + info = mono_class_alloc0 (klass, sizeof (MonoClassPropertyInfo)); + info->first = first; + info->count = count; + info->properties = properties; + mono_memory_barrier (); + + /* This might leak 'info' which was allocated from the image mempool */ + mono_class_set_property_info (klass, info); +} + +static MonoMethod** +inflate_method_listz (MonoMethod **methods, MonoClass *klass, MonoGenericContext *context) +{ + MonoMethod **om, **retval; + int count; + + for (om = methods, count = 0; *om; ++om, ++count) + ; + + retval = g_new0 (MonoMethod*, count + 1); + count = 0; + for (om = methods, count = 0; *om; ++om, ++count) { + MonoError error; + retval [count] = mono_class_inflate_generic_method_full_checked (*om, klass, context, &error); + g_assert (mono_error_ok (&error)); /*FIXME proper error handling*/ + } + + return retval; +} + +/*This method can fail the class.*/ +static void +mono_class_setup_events (MonoClass *klass) +{ + int first, count; + guint startm, endm, i, j; + guint32 cols [MONO_EVENT_SIZE]; + MonoTableInfo *msemt = &klass->image->tables [MONO_TABLE_METHODSEMANTICS]; + guint32 last; + MonoEvent *events; + + MonoClassEventInfo *info = mono_class_get_event_info (klass); + if (info) + return; + + if (mono_class_is_ginst (klass)) { + MonoClass *gklass = mono_class_get_generic_class (klass)->container_class; + MonoGenericContext *context = NULL; + + mono_class_setup_events (gklass); + if (mono_class_set_type_load_failure_causedby_class (klass, gklass, "Generic type definition failed to load")) + return; + + MonoClassEventInfo *ginfo = mono_class_get_event_info (gklass); + first = ginfo->first; + count = ginfo->count; + + events = mono_class_new0 (klass, MonoEvent, count); + + if (count) + context = mono_class_get_context (klass); + + for (i = 0; i < count; i++) { + MonoError error; + MonoEvent *event = &events [i]; + MonoEvent *gevent = &ginfo->events [i]; + + error_init (&error); //since we do conditional calls, we must ensure the default value is ok + + event->parent = klass; + event->name = gevent->name; + event->add = gevent->add ? mono_class_inflate_generic_method_full_checked (gevent->add, klass, context, &error) : NULL; + g_assert (mono_error_ok (&error)); /*FIXME proper error handling*/ + event->remove = gevent->remove ? mono_class_inflate_generic_method_full_checked (gevent->remove, klass, context, &error) : NULL; + g_assert (mono_error_ok (&error)); /*FIXME proper error handling*/ + event->raise = gevent->raise ? mono_class_inflate_generic_method_full_checked (gevent->raise, klass, context, &error) : NULL; + g_assert (mono_error_ok (&error)); /*FIXME proper error handling*/ + +#ifndef MONO_SMALL_CONFIG + event->other = gevent->other ? inflate_method_listz (gevent->other, klass, context) : NULL; +#endif + event->attrs = gevent->attrs; + } + } else { + first = mono_metadata_events_from_typedef (klass->image, mono_metadata_token_index (klass->type_token) - 1, &last); + count = last - first; + + if (count) { + mono_class_setup_methods (klass); + if (mono_class_has_failure (klass)) { + return; + } + } + + events = (MonoEvent *)mono_class_alloc0 (klass, sizeof (MonoEvent) * count); + for (i = first; i < last; ++i) { + MonoEvent *event = &events [i - first]; + + mono_metadata_decode_table_row (klass->image, MONO_TABLE_EVENT, i, cols, MONO_EVENT_SIZE); + event->parent = klass; + event->attrs = cols [MONO_EVENT_FLAGS]; + event->name = mono_metadata_string_heap (klass->image, cols [MONO_EVENT_NAME]); + + startm = mono_metadata_methods_from_event (klass->image, i, &endm); + int first_idx = mono_class_get_first_method_idx (klass); + for (j = startm; j < endm; ++j) { + MonoMethod *method; + + mono_metadata_decode_row (msemt, j, cols, MONO_METHOD_SEMA_SIZE); + + if (klass->image->uncompressed_metadata) { + MonoError error; + /* It seems like the MONO_METHOD_SEMA_METHOD column needs no remapping */ + method = mono_get_method_checked (klass->image, MONO_TOKEN_METHOD_DEF | cols [MONO_METHOD_SEMA_METHOD], klass, NULL, &error); + mono_error_cleanup (&error); /* FIXME don't swallow this error */ + } else { + method = klass->methods [cols [MONO_METHOD_SEMA_METHOD] - 1 - first_idx]; + } + + switch (cols [MONO_METHOD_SEMA_SEMANTICS]) { + case METHOD_SEMANTIC_ADD_ON: + event->add = method; + break; + case METHOD_SEMANTIC_REMOVE_ON: + event->remove = method; + break; + case METHOD_SEMANTIC_FIRE: + event->raise = method; + break; + case METHOD_SEMANTIC_OTHER: { +#ifndef MONO_SMALL_CONFIG + int n = 0; + + if (event->other == NULL) { + event->other = g_new0 (MonoMethod*, 2); + } else { + while (event->other [n]) + n++; + event->other = (MonoMethod **)g_realloc (event->other, (n + 2) * sizeof (MonoMethod*)); + } + event->other [n] = method; + /* NULL terminated */ + event->other [n + 1] = NULL; +#endif + break; + } + default: + break; + } + } + } + } + + info = mono_class_alloc0 (klass, sizeof (MonoClassEventInfo)); + info->events = events; + info->first = first; + info->count = count; + + mono_memory_barrier (); + + mono_class_set_event_info (klass, info); +} + +/* + * Global pool of interface IDs, represented as a bitset. + * LOCKING: Protected by the classes lock. + */ +static MonoBitSet *global_interface_bitset = NULL; + +/* + * mono_unload_interface_ids: + * @bitset: bit set of interface IDs + * + * When an image is unloaded, the interface IDs associated with + * the image are put back in the global pool of IDs so the numbers + * can be reused. + */ +void +mono_unload_interface_ids (MonoBitSet *bitset) +{ + classes_lock (); + mono_bitset_sub (global_interface_bitset, bitset); + classes_unlock (); +} + +void +mono_unload_interface_id (MonoClass *klass) +{ + if (global_interface_bitset && klass->interface_id) { + classes_lock (); + mono_bitset_clear (global_interface_bitset, klass->interface_id); + classes_unlock (); + } +} + +/** + * mono_get_unique_iid: + * \param klass interface + * + * Assign a unique integer ID to the interface represented by \p klass. + * The ID will positive and as small as possible. + * LOCKING: Acquires the classes lock. + * \returns The new ID. + */ +static guint32 +mono_get_unique_iid (MonoClass *klass) +{ + int iid; + + g_assert (MONO_CLASS_IS_INTERFACE (klass)); + + classes_lock (); + + if (!global_interface_bitset) { + global_interface_bitset = mono_bitset_new (128, 0); + } + + iid = mono_bitset_find_first_unset (global_interface_bitset, -1); + if (iid < 0) { + int old_size = mono_bitset_size (global_interface_bitset); + MonoBitSet *new_set = mono_bitset_clone (global_interface_bitset, old_size * 2); + mono_bitset_free (global_interface_bitset); + global_interface_bitset = new_set; + iid = old_size; + } + mono_bitset_set (global_interface_bitset, iid); + /* set the bit also in the per-image set */ + if (!mono_class_is_ginst (klass)) { + if (klass->image->interface_bitset) { + if (iid >= mono_bitset_size (klass->image->interface_bitset)) { + MonoBitSet *new_set = mono_bitset_clone (klass->image->interface_bitset, iid + 1); + mono_bitset_free (klass->image->interface_bitset); + klass->image->interface_bitset = new_set; + } + } else { + klass->image->interface_bitset = mono_bitset_new (iid + 1, 0); + } + mono_bitset_set (klass->image->interface_bitset, iid); + } + + classes_unlock (); + +#ifndef MONO_SMALL_CONFIG + if (mono_print_vtable) { + int generic_id; + char *type_name = mono_type_full_name (&klass->byval_arg); + MonoGenericClass *gklass = mono_class_try_get_generic_class (klass); + if (gklass && !gklass->context.class_inst->is_open) { + generic_id = gklass->context.class_inst->id; + g_assert (generic_id != 0); + } else { + generic_id = 0; + } + printf ("Interface: assigned id %d to %s|%s|%d\n", iid, klass->image->name, type_name, generic_id); + g_free (type_name); + } +#endif + + /* I've confirmed iids safe past 16 bits, however bitset code uses a signed int while testing. + * Once this changes, it should be safe for us to allow 2^32-1 interfaces, until then 2^31-2 is the max. */ + g_assert (iid < INT_MAX); + return iid; +} + +static void +collect_implemented_interfaces_aux (MonoClass *klass, GPtrArray **res, GHashTable **ifaces, MonoError *error) +{ + int i; + MonoClass *ic; + + mono_class_setup_interfaces (klass, error); + return_if_nok (error); + + for (i = 0; i < klass->interface_count; i++) { + ic = klass->interfaces [i]; + + if (*res == NULL) + *res = g_ptr_array_new (); + if (*ifaces == NULL) + *ifaces = g_hash_table_new (NULL, NULL); + if (g_hash_table_lookup (*ifaces, ic)) + continue; + /* A gparam is not an implemented interface for the purposes of + * mono_class_get_implemented_interfaces */ + if (mono_class_is_gparam (ic)) + continue; + g_ptr_array_add (*res, ic); + g_hash_table_insert (*ifaces, ic, ic); + mono_class_init (ic); + if (mono_class_has_failure (ic)) { + mono_error_set_type_load_class (error, ic, "Error Loading class"); + return; + } + + collect_implemented_interfaces_aux (ic, res, ifaces, error); + return_if_nok (error); + } +} + +GPtrArray* +mono_class_get_implemented_interfaces (MonoClass *klass, MonoError *error) +{ + GPtrArray *res = NULL; + GHashTable *ifaces = NULL; + + collect_implemented_interfaces_aux (klass, &res, &ifaces, error); + if (ifaces) + g_hash_table_destroy (ifaces); + if (!mono_error_ok (error)) { + if (res) + g_ptr_array_free (res, TRUE); + return NULL; + } + return res; +} + +static int +compare_interface_ids (const void *p_key, const void *p_element) +{ + const MonoClass *key = (const MonoClass *)p_key; + const MonoClass *element = *(const MonoClass **)p_element; + + return (key->interface_id - element->interface_id); +} + +/*FIXME verify all callers if they should switch to mono_class_interface_offset_with_variance*/ +int +mono_class_interface_offset (MonoClass *klass, MonoClass *itf) +{ + MonoClass **result = (MonoClass **)mono_binary_search ( + itf, + klass->interfaces_packed, + klass->interface_offsets_count, + sizeof (MonoClass *), + compare_interface_ids); + if (result) { + return klass->interface_offsets_packed [result - (klass->interfaces_packed)]; + } else { + return -1; + } +} + +/** + * mono_class_interface_offset_with_variance: + * + * Return the interface offset of \p itf in \p klass. Sets \p non_exact_match to TRUE if the match required variance check + * If \p itf is an interface with generic variant arguments, try to find the compatible one. + * + * Note that this function is responsible for resolving ambiguities. Right now we use whatever ordering interfaces_packed gives us. + * + * FIXME figure out MS disambiguation rules and fix this function. + */ +int +mono_class_interface_offset_with_variance (MonoClass *klass, MonoClass *itf, gboolean *non_exact_match) +{ + int i = mono_class_interface_offset (klass, itf); + *non_exact_match = FALSE; + if (i >= 0) + return i; + + if (itf->is_array_special_interface && klass->rank < 2) { + MonoClass *gtd = mono_class_get_generic_type_definition (itf); + + for (i = 0; i < klass->interface_offsets_count; i++) { + // printf ("\t%s\n", mono_type_get_full_name (klass->interfaces_packed [i])); + if (mono_class_get_generic_type_definition (klass->interfaces_packed [i]) == gtd) { + *non_exact_match = TRUE; + return klass->interface_offsets_packed [i]; + } + } + } + + if (!mono_class_has_variant_generic_params (itf)) + return -1; + + for (i = 0; i < klass->interface_offsets_count; i++) { + if (mono_class_is_variant_compatible (itf, klass->interfaces_packed [i], FALSE)) { + *non_exact_match = TRUE; + return klass->interface_offsets_packed [i]; + } + } + + return -1; +} + +static void +print_implemented_interfaces (MonoClass *klass) +{ + char *name; + MonoError error; + GPtrArray *ifaces = NULL; + int i; + int ancestor_level = 0; + + name = mono_type_get_full_name (klass); + printf ("Packed interface table for class %s has size %d\n", name, klass->interface_offsets_count); + g_free (name); + + for (i = 0; i < klass->interface_offsets_count; i++) + printf (" [%03d][UUID %03d][SLOT %03d][SIZE %03d] interface %s.%s\n", i, + klass->interfaces_packed [i]->interface_id, + klass->interface_offsets_packed [i], + mono_class_get_method_count (klass->interfaces_packed [i]), + klass->interfaces_packed [i]->name_space, + klass->interfaces_packed [i]->name ); + printf ("Interface flags: "); + for (i = 0; i <= klass->max_interface_id; i++) + if (MONO_CLASS_IMPLEMENTS_INTERFACE (klass, i)) + printf ("(%d,T)", i); + else + printf ("(%d,F)", i); + printf ("\n"); + printf ("Dump interface flags:"); +#ifdef COMPRESSED_INTERFACE_BITMAP + { + const uint8_t* p = klass->interface_bitmap; + i = klass->max_interface_id; + while (i > 0) { + printf (" %d x 00 %02X", p [0], p [1]); + i -= p [0] * 8; + i -= 8; + } + } +#else + for (i = 0; i < ((((klass->max_interface_id + 1) >> 3)) + (((klass->max_interface_id + 1) & 7)? 1 :0)); i++) + printf (" %02X", klass->interface_bitmap [i]); +#endif + printf ("\n"); + while (klass != NULL) { + printf ("[LEVEL %d] Implemented interfaces by class %s:\n", ancestor_level, klass->name); + ifaces = mono_class_get_implemented_interfaces (klass, &error); + if (!mono_error_ok (&error)) { + printf (" Type failed due to %s\n", mono_error_get_message (&error)); + mono_error_cleanup (&error); + } else if (ifaces) { + for (i = 0; i < ifaces->len; i++) { + MonoClass *ic = (MonoClass *)g_ptr_array_index (ifaces, i); + printf (" [UIID %d] interface %s\n", ic->interface_id, ic->name); + printf (" [%03d][UUID %03d][SLOT %03d][SIZE %03d] interface %s.%s\n", i, + ic->interface_id, + mono_class_interface_offset (klass, ic), + mono_class_get_method_count (ic), + ic->name_space, + ic->name ); + } + g_ptr_array_free (ifaces, TRUE); + } + ancestor_level ++; + klass = klass->parent; + } +} + +/* + * Return the number of virtual methods. + * Even for interfaces we can't simply return the number of methods as all CLR types are allowed to have static methods. + * Return -1 on failure. + * FIXME It would be nice if this information could be cached somewhere. + */ +static int +count_virtual_methods (MonoClass *klass) +{ + int i, mcount, vcount = 0; + guint32 flags; + klass = mono_class_get_generic_type_definition (klass); /*We can find this information by looking at the GTD*/ + + if (klass->methods || !MONO_CLASS_HAS_STATIC_METADATA (klass)) { + mono_class_setup_methods (klass); + if (mono_class_has_failure (klass)) + return -1; + + mcount = mono_class_get_method_count (klass); + for (i = 0; i < mcount; ++i) { + flags = klass->methods [i]->flags; + if (flags & METHOD_ATTRIBUTE_VIRTUAL) + ++vcount; + } + } else { + int first_idx = mono_class_get_first_method_idx (klass); + mcount = mono_class_get_method_count (klass); + for (i = 0; i < mcount; ++i) { + flags = mono_metadata_decode_table_row_col (klass->image, MONO_TABLE_METHOD, first_idx + i, MONO_METHOD_FLAGS); + + if (flags & METHOD_ATTRIBUTE_VIRTUAL) + ++vcount; + } + } + return vcount; +} + +static int +find_interface (int num_ifaces, MonoClass **interfaces_full, MonoClass *ic) +{ + int m, l = 0; + if (!num_ifaces) + return -1; + while (1) { + if (l > num_ifaces) + return -1; + m = (l + num_ifaces) / 2; + if (interfaces_full [m] == ic) + return m; + if (l == num_ifaces) + return -1; + if (!interfaces_full [m] || interfaces_full [m]->interface_id > ic->interface_id) { + num_ifaces = m - 1; + } else { + l = m + 1; + } + } +} + +static mono_bool +set_interface_and_offset (int num_ifaces, MonoClass **interfaces_full, int *interface_offsets_full, MonoClass *ic, int offset, mono_bool force_set) +{ + int i = find_interface (num_ifaces, interfaces_full, ic); + if (i >= 0) { + if (!force_set) + return TRUE; + interface_offsets_full [i] = offset; + return FALSE; + } + for (i = 0; i < num_ifaces; ++i) { + if (interfaces_full [i]) { + int end; + if (interfaces_full [i]->interface_id < ic->interface_id) + continue; + end = i + 1; + while (end < num_ifaces && interfaces_full [end]) end++; + memmove (interfaces_full + i + 1, interfaces_full + i, sizeof (MonoClass*) * (end - i)); + memmove (interface_offsets_full + i + 1, interface_offsets_full + i, sizeof (int) * (end - i)); + } + interfaces_full [i] = ic; + interface_offsets_full [i] = offset; + break; + } + return FALSE; +} + +#ifdef COMPRESSED_INTERFACE_BITMAP + +/* + * Compressed interface bitmap design. + * + * Interface bitmaps take a large amount of memory, because their size is + * linear with the maximum interface id assigned in the process (each interface + * is assigned a unique id as it is loaded). The number of interface classes + * is high because of the many implicit interfaces implemented by arrays (we'll + * need to lazy-load them in the future). + * Most classes implement a very small number of interfaces, so the bitmap is + * sparse. This bitmap needs to be checked by interface casts, so access to the + * needed bit must be fast and doable with few jit instructions. + * + * The current compression format is as follows: + * *) it is a sequence of one or more two-byte elements + * *) the first byte in the element is the count of empty bitmap bytes + * at the current bitmap position + * *) the second byte in the element is an actual bitmap byte at the current + * bitmap position + * + * As an example, the following compressed bitmap bytes: + * 0x07 0x01 0x00 0x7 + * correspond to the following bitmap: + * 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x01 0x07 + * + * Each two-byte element can represent up to 2048 bitmap bits, but as few as a single + * bitmap byte for non-sparse sequences. In practice the interface bitmaps created + * during a gmcs bootstrap are reduced to less tha 5% of the original size. + */ + +/** + * mono_compress_bitmap: + * \param dest destination buffer + * \param bitmap bitmap buffer + * \param size size of \p bitmap in bytes + * + * This is a mono internal function. + * The \p bitmap data is compressed into a format that is small but + * still searchable in few instructions by the JIT and runtime. + * The compressed data is stored in the buffer pointed to by the + * \p dest array. Passing a NULL value for \p dest allows to just compute + * the size of the buffer. + * This compression algorithm assumes the bits set in the bitmap are + * few and far between, like in interface bitmaps. + * \returns The size of the compressed bitmap in bytes. + */ +int +mono_compress_bitmap (uint8_t *dest, const uint8_t *bitmap, int size) +{ + int numz = 0; + int res = 0; + const uint8_t *end = bitmap + size; + while (bitmap < end) { + if (*bitmap || numz == 255) { + if (dest) { + *dest++ = numz; + *dest++ = *bitmap; + } + res += 2; + numz = 0; + bitmap++; + continue; + } + bitmap++; + numz++; + } + if (numz) { + res += 2; + if (dest) { + *dest++ = numz; + *dest++ = 0; + } + } + return res; +} + +/** + * mono_class_interface_match: + * \param bitmap a compressed bitmap buffer + * \param id the index to check in the bitmap + * + * This is a mono internal function. + * Checks if a bit is set in a compressed interface bitmap. \p id must + * be already checked for being smaller than the maximum id encoded in the + * bitmap. + * + * \returns A non-zero value if bit \p id is set in the bitmap \p bitmap, + * FALSE otherwise. + */ +int +mono_class_interface_match (const uint8_t *bitmap, int id) +{ + while (TRUE) { + id -= bitmap [0] * 8; + if (id < 8) { + if (id < 0) + return 0; + return bitmap [1] & (1 << id); + } + bitmap += 2; + id -= 8; + } +} +#endif + +/* + * Return -1 on failure and set klass->has_failure and store a MonoErrorBoxed with the details. + * LOCKING: Acquires the loader lock. + */ +static int +setup_interface_offsets (MonoClass *klass, int cur_slot, gboolean overwrite) +{ + MonoError error; + MonoClass *k, *ic; + int i, j, num_ifaces; + guint32 max_iid; + MonoClass **interfaces_full = NULL; + int *interface_offsets_full = NULL; + GPtrArray *ifaces; + GPtrArray **ifaces_array = NULL; + int interface_offsets_count; + + mono_loader_lock (); + + mono_class_setup_supertypes (klass); + + /* compute maximum number of slots and maximum interface id */ + max_iid = 0; + num_ifaces = 0; /* this can include duplicated ones */ + ifaces_array = g_new0 (GPtrArray *, klass->idepth); + for (j = 0; j < klass->idepth; j++) { + k = klass->supertypes [j]; + g_assert (k); + num_ifaces += k->interface_count; + for (i = 0; i < k->interface_count; i++) { + ic = k->interfaces [i]; + + /* A gparam does not have any interface_id set. */ + if (! mono_class_is_gparam (ic)) + mono_class_init (ic); + + if (max_iid < ic->interface_id) + max_iid = ic->interface_id; + } + ifaces = mono_class_get_implemented_interfaces (k, &error); + if (!mono_error_ok (&error)) { + char *name = mono_type_get_full_name (k); + mono_class_set_type_load_failure (klass, "Error getting the interfaces of %s due to %s", name, mono_error_get_message (&error)); + g_free (name); + mono_error_cleanup (&error); + cur_slot = -1; + goto end; + } + if (ifaces) { + num_ifaces += ifaces->len; + for (i = 0; i < ifaces->len; ++i) { + ic = (MonoClass *)g_ptr_array_index (ifaces, i); + if (max_iid < ic->interface_id) + max_iid = ic->interface_id; + } + ifaces_array [j] = ifaces; + } + } + + if (MONO_CLASS_IS_INTERFACE (klass)) { + num_ifaces++; + if (max_iid < klass->interface_id) + max_iid = klass->interface_id; + } + + /* compute vtable offset for interfaces */ + interfaces_full = (MonoClass **)g_malloc0 (sizeof (MonoClass*) * num_ifaces); + interface_offsets_full = (int *)g_malloc (sizeof (int) * num_ifaces); + + for (i = 0; i < num_ifaces; i++) + interface_offsets_full [i] = -1; + + /* skip the current class */ + for (j = 0; j < klass->idepth - 1; j++) { + k = klass->supertypes [j]; + ifaces = ifaces_array [j]; + + if (ifaces) { + for (i = 0; i < ifaces->len; ++i) { + int io; + ic = (MonoClass *)g_ptr_array_index (ifaces, i); + + /*Force the sharing of interface offsets between parent and subtypes.*/ + io = mono_class_interface_offset (k, ic); + g_assert (io >= 0); + set_interface_and_offset (num_ifaces, interfaces_full, interface_offsets_full, ic, io, TRUE); + } + } + } + + g_assert (klass == klass->supertypes [klass->idepth - 1]); + ifaces = ifaces_array [klass->idepth - 1]; + if (ifaces) { + for (i = 0; i < ifaces->len; ++i) { + int count; + ic = (MonoClass *)g_ptr_array_index (ifaces, i); + if (set_interface_and_offset (num_ifaces, interfaces_full, interface_offsets_full, ic, cur_slot, FALSE)) + continue; + count = count_virtual_methods (ic); + if (count == -1) { + char *name = mono_type_get_full_name (ic); + mono_class_set_type_load_failure (klass, "Error calculating interface offset of %s", name); + g_free (name); + cur_slot = -1; + goto end; + } + cur_slot += count; + } + } + + if (MONO_CLASS_IS_INTERFACE (klass)) + set_interface_and_offset (num_ifaces, interfaces_full, interface_offsets_full, klass, cur_slot, TRUE); + + for (interface_offsets_count = 0, i = 0; i < num_ifaces; i++) { + if (interface_offsets_full [i] != -1) + interface_offsets_count ++; + } + + /* Publish the data */ + klass->max_interface_id = max_iid; + /* + * We might get called multiple times: + * - mono_class_init () + * - mono_class_setup_vtable (). + * - mono_class_setup_interface_offsets (). + * mono_class_setup_interface_offsets () passes 0 as CUR_SLOT, so the computed interface offsets will be invalid. This + * means we have to overwrite those when called from other places (#4440). + */ + if (klass->interfaces_packed) { + if (!overwrite) + g_assert (klass->interface_offsets_count == interface_offsets_count); + } else { + uint8_t *bitmap; + int bsize; + klass->interface_offsets_count = interface_offsets_count; + klass->interfaces_packed = (MonoClass **)mono_class_alloc (klass, sizeof (MonoClass*) * interface_offsets_count); + klass->interface_offsets_packed = (guint16 *)mono_class_alloc (klass, sizeof (guint16) * interface_offsets_count); + bsize = (sizeof (guint8) * ((max_iid + 1) >> 3)) + (((max_iid + 1) & 7)? 1 :0); +#ifdef COMPRESSED_INTERFACE_BITMAP + bitmap = g_malloc0 (bsize); +#else + bitmap = (uint8_t *)mono_class_alloc0 (klass, bsize); +#endif + for (i = 0; i < interface_offsets_count; i++) { + guint32 id = interfaces_full [i]->interface_id; + bitmap [id >> 3] |= (1 << (id & 7)); + klass->interfaces_packed [i] = interfaces_full [i]; + klass->interface_offsets_packed [i] = interface_offsets_full [i]; + } +#ifdef COMPRESSED_INTERFACE_BITMAP + i = mono_compress_bitmap (NULL, bitmap, bsize); + klass->interface_bitmap = mono_class_alloc0 (klass, i); + mono_compress_bitmap (klass->interface_bitmap, bitmap, bsize); + g_free (bitmap); +#else + klass->interface_bitmap = bitmap; +#endif + } +end: + mono_loader_unlock (); + + g_free (interfaces_full); + g_free (interface_offsets_full); + for (i = 0; i < klass->idepth; i++) { + ifaces = ifaces_array [i]; + if (ifaces) + g_ptr_array_free (ifaces, TRUE); + } + g_free (ifaces_array); + + //printf ("JUST DONE: "); + //print_implemented_interfaces (klass); + + return cur_slot; +} + +/* + * Setup interface offsets for interfaces. + * Initializes: + * - klass->max_interface_id + * - klass->interface_offsets_count + * - klass->interfaces_packed + * - klass->interface_offsets_packed + * - klass->interface_bitmap + * + * This function can fail @class. + * + */ +void +mono_class_setup_interface_offsets (MonoClass *klass) +{ + /* NOTE: This function is only correct for interfaces. + * + * It assumes that klass's interfaces can be assigned offsets starting + * from 0. That assumption is incorrect for classes and valuetypes. + */ + g_assert (MONO_CLASS_IS_INTERFACE (klass) && !mono_class_is_ginst (klass)); + setup_interface_offsets (klass, 0, FALSE); +} + +/*Checks if @klass has @parent as one of it's parents type gtd + * + * For example: + * Foo + * Bar : Foo>> + * + */ +static gboolean +mono_class_has_gtd_parent (MonoClass *klass, MonoClass *parent) +{ + klass = mono_class_get_generic_type_definition (klass); + parent = mono_class_get_generic_type_definition (parent); + mono_class_setup_supertypes (klass); + mono_class_setup_supertypes (parent); + + return klass->idepth >= parent->idepth && + mono_class_get_generic_type_definition (klass->supertypes [parent->idepth - 1]) == parent; +} + +gboolean +mono_class_check_vtable_constraints (MonoClass *klass, GList *in_setup) +{ + MonoGenericInst *ginst; + int i; + + if (!mono_class_is_ginst (klass)) { + mono_class_setup_vtable_full (klass, in_setup); + return !mono_class_has_failure (klass); + } + + mono_class_setup_vtable_full (mono_class_get_generic_type_definition (klass), in_setup); + if (mono_class_set_type_load_failure_causedby_class (klass, mono_class_get_generic_class (klass)->container_class, "Failed to load generic definition vtable")) + return FALSE; + + ginst = mono_class_get_generic_class (klass)->context.class_inst; + for (i = 0; i < ginst->type_argc; ++i) { + MonoClass *arg; + if (ginst->type_argv [i]->type != MONO_TYPE_GENERICINST) + continue; + arg = mono_class_from_mono_type (ginst->type_argv [i]); + /*Those 2 will be checked by mono_class_setup_vtable itself*/ + if (mono_class_has_gtd_parent (klass, arg) || mono_class_has_gtd_parent (arg, klass)) + continue; + if (!mono_class_check_vtable_constraints (arg, in_setup)) { + mono_class_set_type_load_failure (klass, "Failed to load generic parameter %d", i); + return FALSE; + } + } + return TRUE; +} + +/* + * mono_class_setup_vtable: + * + * Creates the generic vtable of CLASS. + * Initializes the following fields in MonoClass: + * - vtable + * - vtable_size + * Plus all the fields initialized by setup_interface_offsets (). + * If there is an error during vtable construction, klass->has_failure + * is set and details are stored in a MonoErrorBoxed. + * + * LOCKING: Acquires the loader lock. + */ +void +mono_class_setup_vtable (MonoClass *klass) +{ + mono_class_setup_vtable_full (klass, NULL); +} + +static void +mono_class_setup_vtable_full (MonoClass *klass, GList *in_setup) +{ + MonoError error; + MonoMethod **overrides = NULL; + MonoGenericContext *context; + guint32 type_token; + int onum = 0; + + if (klass->vtable) + return; + + if (MONO_CLASS_IS_INTERFACE (klass)) { + /* This sets method->slot for all methods if this is an interface */ + mono_class_setup_methods (klass); + return; + } + + if (mono_class_has_failure (klass)) + return; + + if (g_list_find (in_setup, klass)) + return; + + mono_loader_lock (); + + if (klass->vtable) { + mono_loader_unlock (); + return; + } + + UnlockedIncrement (&mono_stats.generic_vtable_count); + in_setup = g_list_prepend (in_setup, klass); + + if (mono_class_is_ginst (klass)) { + if (!mono_class_check_vtable_constraints (klass, in_setup)) { + mono_loader_unlock (); + g_list_remove (in_setup, klass); + return; + } + + context = mono_class_get_context (klass); + type_token = mono_class_get_generic_class (klass)->container_class->type_token; + } else { + context = (MonoGenericContext *) mono_class_try_get_generic_container (klass); //FIXME is this a case of a try? + type_token = klass->type_token; + } + + if (image_is_dynamic (klass->image)) { + /* Generic instances can have zero method overrides without causing any harm. + * This is true since we don't do layout all over again for them, we simply inflate + * the layout of the parent. + */ + mono_reflection_get_dynamic_overrides (klass, &overrides, &onum, &error); + if (!is_ok (&error)) { + mono_class_set_type_load_failure (klass, "Could not load list of method overrides due to %s", mono_error_get_message (&error)); + goto done; + } + } else { + /* The following call fails if there are missing methods in the type */ + /* FIXME it's probably a good idea to avoid this for generic instances. */ + mono_class_get_overrides_full (klass->image, type_token, &overrides, &onum, context, &error); + if (!is_ok (&error)) { + mono_class_set_type_load_failure (klass, "Could not load list of method overrides due to %s", mono_error_get_message (&error)); + goto done; + } + } + + mono_class_setup_vtable_general (klass, overrides, onum, in_setup); + +done: + g_free (overrides); + mono_error_cleanup (&error); + + mono_loader_unlock (); + g_list_remove (in_setup, klass); + + return; +} + +#define DEBUG_INTERFACE_VTABLE_CODE 0 +#define TRACE_INTERFACE_VTABLE_CODE 0 +#define VERIFY_INTERFACE_VTABLE_CODE 0 +#define VTABLE_SELECTOR (1) + +#if (TRACE_INTERFACE_VTABLE_CODE|DEBUG_INTERFACE_VTABLE_CODE) +#define DEBUG_INTERFACE_VTABLE(stmt) do {\ + if (!(VTABLE_SELECTOR)) break; \ + stmt;\ +} while (0) +#else +#define DEBUG_INTERFACE_VTABLE(stmt) +#endif + +#if TRACE_INTERFACE_VTABLE_CODE +#define TRACE_INTERFACE_VTABLE(stmt) do {\ + if (!(VTABLE_SELECTOR)) break; \ + stmt;\ +} while (0) +#else +#define TRACE_INTERFACE_VTABLE(stmt) +#endif + +#if VERIFY_INTERFACE_VTABLE_CODE +#define VERIFY_INTERFACE_VTABLE(stmt) do {\ + if (!(VTABLE_SELECTOR)) break; \ + stmt;\ +} while (0) +#else +#define VERIFY_INTERFACE_VTABLE(stmt) +#endif + + +#if (TRACE_INTERFACE_VTABLE_CODE|DEBUG_INTERFACE_VTABLE_CODE) +static char* +mono_signature_get_full_desc (MonoMethodSignature *sig, gboolean include_namespace) +{ + int i; + char *result; + GString *res = g_string_new (""); + + g_string_append_c (res, '('); + for (i = 0; i < sig->param_count; ++i) { + if (i > 0) + g_string_append_c (res, ','); + mono_type_get_desc (res, sig->params [i], include_namespace); + } + g_string_append (res, ")=>"); + if (sig->ret != NULL) { + mono_type_get_desc (res, sig->ret, include_namespace); + } else { + g_string_append (res, "NULL"); + } + result = res->str; + g_string_free (res, FALSE); + return result; +} +static void +print_method_signatures (MonoMethod *im, MonoMethod *cm) { + char *im_sig = mono_signature_get_full_desc (mono_method_signature (im), TRUE); + char *cm_sig = mono_signature_get_full_desc (mono_method_signature (cm), TRUE); + printf ("(IM \"%s\", CM \"%s\")", im_sig, cm_sig); + g_free (im_sig); + g_free (cm_sig); + +} + +#endif +static gboolean +is_wcf_hack_disabled (void) +{ + static gboolean disabled; + static gboolean inited = FALSE; + if (!inited) { + disabled = g_hasenv ("MONO_DISABLE_WCF_HACK"); + inited = TRUE; + } + return disabled; +} + +static gboolean +check_interface_method_override (MonoClass *klass, MonoMethod *im, MonoMethod *cm, gboolean require_newslot, gboolean interface_is_explicitly_implemented_by_class, gboolean slot_is_empty) +{ + MonoMethodSignature *cmsig, *imsig; + if (strcmp (im->name, cm->name) == 0) { + if (! (cm->flags & METHOD_ATTRIBUTE_PUBLIC)) { + TRACE_INTERFACE_VTABLE (printf ("[PUBLIC CHECK FAILED]")); + return FALSE; + } + if (! slot_is_empty) { + if (require_newslot) { + if (! interface_is_explicitly_implemented_by_class) { + TRACE_INTERFACE_VTABLE (printf ("[NOT EXPLICIT IMPLEMENTATION IN FULL SLOT REFUSED]")); + return FALSE; + } + if (! (cm->flags & METHOD_ATTRIBUTE_NEW_SLOT)) { + TRACE_INTERFACE_VTABLE (printf ("[NEWSLOT CHECK FAILED]")); + return FALSE; + } + } else { + TRACE_INTERFACE_VTABLE (printf ("[FULL SLOT REFUSED]")); + } + } + cmsig = mono_method_signature (cm); + imsig = mono_method_signature (im); + if (!cmsig || !imsig) { + mono_class_set_type_load_failure (klass, "Could not resolve the signature of a virtual method"); + return FALSE; + } + + if (! mono_metadata_signature_equal (cmsig, imsig)) { + TRACE_INTERFACE_VTABLE (printf ("[SIGNATURE CHECK FAILED ")); + TRACE_INTERFACE_VTABLE (print_method_signatures (im, cm)); + TRACE_INTERFACE_VTABLE (printf ("]")); + return FALSE; + } + TRACE_INTERFACE_VTABLE (printf ("[SECURITY CHECKS]")); + if (mono_security_core_clr_enabled ()) + mono_security_core_clr_check_override (klass, cm, im); + + TRACE_INTERFACE_VTABLE (printf ("[NAME CHECK OK]")); + if (is_wcf_hack_disabled () && !mono_method_can_access_method_full (cm, im, NULL)) { + char *body_name = mono_method_full_name (cm, TRUE); + char *decl_name = mono_method_full_name (im, TRUE); + mono_class_set_type_load_failure (klass, "Method %s overrides method '%s' which is not accessible", body_name, decl_name); + g_free (body_name); + g_free (decl_name); + return FALSE; + } + + return TRUE; + } else { + MonoClass *ic = im->klass; + const char *ic_name_space = ic->name_space; + const char *ic_name = ic->name; + char *subname; + + if (! require_newslot) { + TRACE_INTERFACE_VTABLE (printf ("[INJECTED METHOD REFUSED]")); + return FALSE; + } + if (cm->klass->rank == 0) { + TRACE_INTERFACE_VTABLE (printf ("[RANK CHECK FAILED]")); + return FALSE; + } + cmsig = mono_method_signature (cm); + imsig = mono_method_signature (im); + if (!cmsig || !imsig) { + mono_class_set_type_load_failure (klass, "Could not resolve the signature of a virtual method"); + return FALSE; + } + + if (! mono_metadata_signature_equal (cmsig, imsig)) { + TRACE_INTERFACE_VTABLE (printf ("[(INJECTED) SIGNATURE CHECK FAILED ")); + TRACE_INTERFACE_VTABLE (print_method_signatures (im, cm)); + TRACE_INTERFACE_VTABLE (printf ("]")); + return FALSE; + } + if (mono_class_get_image (ic) != mono_defaults.corlib) { + TRACE_INTERFACE_VTABLE (printf ("[INTERFACE CORLIB CHECK FAILED]")); + return FALSE; + } + if ((ic_name_space == NULL) || (strcmp (ic_name_space, "System.Collections.Generic") != 0)) { + TRACE_INTERFACE_VTABLE (printf ("[INTERFACE NAMESPACE CHECK FAILED]")); + return FALSE; + } + if ((ic_name == NULL) || ((strcmp (ic_name, "IEnumerable`1") != 0) && (strcmp (ic_name, "ICollection`1") != 0) && (strcmp (ic_name, "IList`1") != 0) && (strcmp (ic_name, "IReadOnlyList`1") != 0) && (strcmp (ic_name, "IReadOnlyCollection`1") != 0))) { + TRACE_INTERFACE_VTABLE (printf ("[INTERFACE NAME CHECK FAILED]")); + return FALSE; + } + + subname = strstr (cm->name, ic_name_space); + if (subname != cm->name) { + TRACE_INTERFACE_VTABLE (printf ("[ACTUAL NAMESPACE CHECK FAILED]")); + return FALSE; + } + subname += strlen (ic_name_space); + if (subname [0] != '.') { + TRACE_INTERFACE_VTABLE (printf ("[FIRST DOT CHECK FAILED]")); + return FALSE; + } + subname ++; + if (strstr (subname, ic_name) != subname) { + TRACE_INTERFACE_VTABLE (printf ("[ACTUAL CLASS NAME CHECK FAILED]")); + return FALSE; + } + subname += strlen (ic_name); + if (subname [0] != '.') { + TRACE_INTERFACE_VTABLE (printf ("[SECOND DOT CHECK FAILED]")); + return FALSE; + } + subname ++; + if (strcmp (subname, im->name) != 0) { + TRACE_INTERFACE_VTABLE (printf ("[METHOD NAME CHECK FAILED]")); + return FALSE; + } + + TRACE_INTERFACE_VTABLE (printf ("[SECURITY CHECKS (INJECTED CASE)]")); + if (mono_security_core_clr_enabled ()) + mono_security_core_clr_check_override (klass, cm, im); + + TRACE_INTERFACE_VTABLE (printf ("[INJECTED INTERFACE CHECK OK]")); + if (is_wcf_hack_disabled () && !mono_method_can_access_method_full (cm, im, NULL)) { + char *body_name = mono_method_full_name (cm, TRUE); + char *decl_name = mono_method_full_name (im, TRUE); + mono_class_set_type_load_failure (klass, "Method %s overrides method '%s' which is not accessible", body_name, decl_name); + g_free (body_name); + g_free (decl_name); + return FALSE; + } + + return TRUE; + } +} + +#if (TRACE_INTERFACE_VTABLE_CODE|DEBUG_INTERFACE_VTABLE_CODE) +static void +foreach_override (gpointer key, gpointer value, gpointer user_data) { + MonoMethod *method = key; + MonoMethod *override = value; + MonoClass *method_class = mono_method_get_class (method); + MonoClass *override_class = mono_method_get_class (override); + + printf (" Method '%s.%s:%s' has override '%s.%s:%s'\n", + mono_class_get_namespace (method_class), mono_class_get_name (method_class), mono_method_get_name (method), + mono_class_get_namespace (override_class), mono_class_get_name (override_class), mono_method_get_name (override)); +} +static void +print_overrides (GHashTable *override_map, const char *message) { + if (override_map) { + printf ("Override map \"%s\" START:\n", message); + g_hash_table_foreach (override_map, foreach_override, NULL); + printf ("Override map \"%s\" END.\n", message); + } else { + printf ("Override map \"%s\" EMPTY.\n", message); + } +} +static void +print_vtable_full (MonoClass *klass, MonoMethod** vtable, int size, int first_non_interface_slot, const char *message, gboolean print_interfaces) { + char *full_name = mono_type_full_name (&klass->byval_arg); + int i; + int parent_size; + + printf ("*** Vtable for class '%s' at \"%s\" (size %d)\n", full_name, message, size); + + if (print_interfaces) { + print_implemented_interfaces (klass); + printf ("* Interfaces for class '%s' done.\nStarting vtable (size %d):\n", full_name, size); + } + + if (klass->parent) { + parent_size = klass->parent->vtable_size; + } else { + parent_size = 0; + } + for (i = 0; i < size; ++i) { + MonoMethod *cm = vtable [i]; + char *cm_name = cm ? mono_method_full_name (cm, TRUE) : g_strdup ("nil"); + char newness = (i < parent_size) ? 'O' : ((i < first_non_interface_slot) ? 'I' : 'N'); + + printf (" [%c][%03d][INDEX %03d] %s [%p]\n", newness, i, cm ? cm->slot : - 1, cm_name, cm); + g_free (cm_name); + } + + g_free (full_name); +} +#endif + +#if VERIFY_INTERFACE_VTABLE_CODE +static int +mono_method_try_get_vtable_index (MonoMethod *method) +{ + if (method->is_inflated && (method->flags & METHOD_ATTRIBUTE_VIRTUAL)) { + MonoMethodInflated *imethod = (MonoMethodInflated*)method; + if (imethod->declaring->is_generic) + return imethod->declaring->slot; + } + return method->slot; +} + +static void +mono_class_verify_vtable (MonoClass *klass) +{ + int i, count; + char *full_name = mono_type_full_name (&klass->byval_arg); + + printf ("*** Verifying VTable of class '%s' \n", full_name); + g_free (full_name); + full_name = NULL; + + if (!klass->methods) + return; + + count = mono_class_method_count (klass); + for (i = 0; i < count; ++i) { + MonoMethod *cm = klass->methods [i]; + int slot; + + if (!(cm->flags & METHOD_ATTRIBUTE_VIRTUAL)) + continue; + + g_free (full_name); + full_name = mono_method_full_name (cm, TRUE); + + slot = mono_method_try_get_vtable_index (cm); + if (slot >= 0) { + if (slot >= klass->vtable_size) { + printf ("\tInvalid method %s at index %d with vtable of length %d\n", full_name, slot, klass->vtable_size); + continue; + } + + if (slot >= 0 && klass->vtable [slot] != cm && (klass->vtable [slot])) { + char *other_name = klass->vtable [slot] ? mono_method_full_name (klass->vtable [slot], TRUE) : g_strdup ("[null value]"); + printf ("\tMethod %s has slot %d but vtable has %s on it\n", full_name, slot, other_name); + g_free (other_name); + } + } else + printf ("\tVirtual method %s does n't have an assigned slot\n", full_name); + } + g_free (full_name); +} +#endif + +static void +print_unimplemented_interface_method_info (MonoClass *klass, MonoClass *ic, MonoMethod *im, int im_slot, MonoMethod **overrides, int onum) +{ + int index, mcount; + char *method_signature; + char *type_name; + + for (index = 0; index < onum; ++index) { + mono_trace_warning (MONO_TRACE_TYPE, " at slot %d: %s (%d) overrides %s (%d)", im_slot, overrides [index*2+1]->name, + overrides [index*2+1]->slot, overrides [index*2]->name, overrides [index*2]->slot); + } + method_signature = mono_signature_get_desc (mono_method_signature (im), FALSE); + type_name = mono_type_full_name (&klass->byval_arg); + mono_trace_warning (MONO_TRACE_TYPE, "no implementation for interface method %s::%s(%s) in class %s", + mono_type_get_name (&ic->byval_arg), im->name, method_signature, type_name); + g_free (method_signature); + g_free (type_name); + mono_class_setup_methods (klass); + if (mono_class_has_failure (klass)) { + char *name = mono_type_get_full_name (klass); + mono_trace_warning (MONO_TRACE_TYPE, "CLASS %s failed to resolve methods", name); + g_free (name); + return; + } + mcount = mono_class_get_method_count (klass); + for (index = 0; index < mcount; ++index) { + MonoMethod *cm = klass->methods [index]; + method_signature = mono_signature_get_desc (mono_method_signature (cm), TRUE); + + mono_trace_warning (MONO_TRACE_TYPE, "METHOD %s(%s)", cm->name, method_signature); + g_free (method_signature); + } +} + +static MonoMethod* +mono_method_get_method_definition (MonoMethod *method) +{ + while (method->is_inflated) + method = ((MonoMethodInflated*)method)->declaring; + return method; +} + +static gboolean +verify_class_overrides (MonoClass *klass, MonoMethod **overrides, int onum) +{ + int i; + + for (i = 0; i < onum; ++i) { + MonoMethod *decl = overrides [i * 2]; + MonoMethod *body = overrides [i * 2 + 1]; + + if (mono_class_get_generic_type_definition (body->klass) != mono_class_get_generic_type_definition (klass)) { + mono_class_set_type_load_failure (klass, "Method belongs to a different class than the declared one"); + return FALSE; + } + + if (!(body->flags & METHOD_ATTRIBUTE_VIRTUAL) || (body->flags & METHOD_ATTRIBUTE_STATIC)) { + if (body->flags & METHOD_ATTRIBUTE_STATIC) + mono_class_set_type_load_failure (klass, "Method must not be static to override a base type"); + else + mono_class_set_type_load_failure (klass, "Method must be virtual to override a base type"); + return FALSE; + } + + if (!(decl->flags & METHOD_ATTRIBUTE_VIRTUAL) || (decl->flags & METHOD_ATTRIBUTE_STATIC)) { + if (body->flags & METHOD_ATTRIBUTE_STATIC) + mono_class_set_type_load_failure (klass, "Cannot override a static method in a base type"); + else + mono_class_set_type_load_failure (klass, "Cannot override a non virtual method in a base type"); + return FALSE; + } + + if (!mono_class_is_assignable_from_slow (decl->klass, klass)) { + mono_class_set_type_load_failure (klass, "Method overrides a class or interface that is not extended or implemented by this type"); + return FALSE; + } + + body = mono_method_get_method_definition (body); + decl = mono_method_get_method_definition (decl); + + if (is_wcf_hack_disabled () && !mono_method_can_access_method_full (body, decl, NULL)) { + char *body_name = mono_method_full_name (body, TRUE); + char *decl_name = mono_method_full_name (decl, TRUE); + mono_class_set_type_load_failure (klass, "Method %s overrides method '%s' which is not accessible", body_name, decl_name); + g_free (body_name); + g_free (decl_name); + return FALSE; + } + } + return TRUE; +} + +static gboolean +mono_class_need_stelemref_method (MonoClass *klass) +{ + return klass->rank == 1 && MONO_TYPE_IS_REFERENCE (&klass->element_class->byval_arg); +} + +static int +apply_override (MonoClass *klass, MonoMethod **vtable, MonoMethod *decl, MonoMethod *override) +{ + int dslot; + dslot = mono_method_get_vtable_slot (decl); + if (dslot == -1) { + mono_class_set_type_load_failure (klass, ""); + return FALSE; + } + + dslot += mono_class_interface_offset (klass, decl->klass); + vtable [dslot] = override; + if (!MONO_CLASS_IS_INTERFACE (override->klass)) { + /* + * If override from an interface, then it is an override of a default interface method, + * don't override its slot. + */ + vtable [dslot]->slot = dslot; + } + + if (mono_security_core_clr_enabled ()) + mono_security_core_clr_check_override (klass, vtable [dslot], decl); + + return TRUE; +} + +/* + * LOCKING: this is supposed to be called with the loader lock held. + */ +void +mono_class_setup_vtable_general (MonoClass *klass, MonoMethod **overrides, int onum, GList *in_setup) +{ + MonoError error; + MonoClass *k, *ic; + MonoMethod **vtable = NULL; + int i, max_vtsize = 0, cur_slot = 0; + guint32 max_iid; + GPtrArray *ifaces = NULL; + GHashTable *override_map = NULL; + MonoMethod *cm; +#if (DEBUG_INTERFACE_VTABLE_CODE|TRACE_INTERFACE_VTABLE_CODE) + int first_non_interface_slot; +#endif + GSList *virt_methods = NULL, *l; + int stelemref_slot = 0; + + error_init (&error); + + if (klass->vtable) + return; + + if (overrides && !verify_class_overrides (klass, overrides, onum)) + return; + + ifaces = mono_class_get_implemented_interfaces (klass, &error); + if (!mono_error_ok (&error)) { + char *name = mono_type_get_full_name (klass); + mono_class_set_type_load_failure (klass, "Could not resolve %s interfaces due to %s", name, mono_error_get_message (&error)); + g_free (name); + mono_error_cleanup (&error); + return; + } else if (ifaces) { + for (i = 0; i < ifaces->len; i++) { + MonoClass *ic = (MonoClass *)g_ptr_array_index (ifaces, i); + max_vtsize += mono_class_get_method_count (ic); + } + g_ptr_array_free (ifaces, TRUE); + ifaces = NULL; + } + + if (klass->parent) { + mono_class_init (klass->parent); + mono_class_setup_vtable_full (klass->parent, in_setup); + + if (mono_class_set_type_load_failure_causedby_class (klass, klass->parent, "Parent class failed to load")) + return; + + max_vtsize += klass->parent->vtable_size; + cur_slot = klass->parent->vtable_size; + } + + max_vtsize += mono_class_get_method_count (klass); + + /*Array have a slot for stelemref*/ + if (mono_class_need_stelemref_method (klass)) { + stelemref_slot = cur_slot; + ++max_vtsize; + ++cur_slot; + } + + /* printf ("METAINIT %s.%s\n", klass->name_space, klass->name); */ + + cur_slot = setup_interface_offsets (klass, cur_slot, TRUE); + if (cur_slot == -1) /*setup_interface_offsets fails the type.*/ + return; + + max_iid = klass->max_interface_id; + DEBUG_INTERFACE_VTABLE (first_non_interface_slot = cur_slot); + + /* Optimized version for generic instances */ + if (mono_class_is_ginst (klass)) { + MonoError error; + MonoClass *gklass = mono_class_get_generic_class (klass)->container_class; + MonoMethod **tmp; + + mono_class_setup_vtable_full (gklass, in_setup); + if (mono_class_set_type_load_failure_causedby_class (klass, gklass, "Could not load generic definition")) + return; + + tmp = (MonoMethod **)mono_class_alloc0 (klass, sizeof (gpointer) * gklass->vtable_size); + klass->vtable_size = gklass->vtable_size; + for (i = 0; i < gklass->vtable_size; ++i) + if (gklass->vtable [i]) { + MonoMethod *inflated = mono_class_inflate_generic_method_full_checked (gklass->vtable [i], klass, mono_class_get_context (klass), &error); + goto_if_nok (&error, fail); + tmp [i] = inflated; + tmp [i]->slot = gklass->vtable [i]->slot; + } + mono_memory_barrier (); + klass->vtable = tmp; + + /* Have to set method->slot for abstract virtual methods */ + if (klass->methods && gklass->methods) { + int mcount = mono_class_get_method_count (klass); + for (i = 0; i < mcount; ++i) + if (klass->methods [i]->slot == -1) + klass->methods [i]->slot = gklass->methods [i]->slot; + } + + return; + } + + vtable = (MonoMethod **)g_malloc0 (sizeof (gpointer) * max_vtsize); + + if (klass->parent && klass->parent->vtable_size) { + MonoClass *parent = klass->parent; + int i; + + memcpy (vtable, parent->vtable, sizeof (gpointer) * parent->vtable_size); + + // Also inherit parent interface vtables, just as a starting point. + // This is needed otherwise bug-77127.exe fails when the property methods + // have different names in the iterface and the class, because for child + // classes the ".override" information is not used anymore. + for (i = 0; i < parent->interface_offsets_count; i++) { + MonoClass *parent_interface = parent->interfaces_packed [i]; + int interface_offset = mono_class_interface_offset (klass, parent_interface); + /*FIXME this is now dead code as this condition will never hold true. + Since interface offsets are inherited then the offset of an interface implemented + by a parent will never be the out of it's vtable boundary. + */ + if (interface_offset >= parent->vtable_size) { + int parent_interface_offset = mono_class_interface_offset (parent, parent_interface); + int j; + + mono_class_setup_methods (parent_interface); /*FIXME Just kill this whole chunk of dead code*/ + TRACE_INTERFACE_VTABLE (printf (" +++ Inheriting interface %s.%s\n", parent_interface->name_space, parent_interface->name)); + int mcount = mono_class_get_method_count (parent_interface); + for (j = 0; j < mcount && !mono_class_has_failure (klass); j++) { + vtable [interface_offset + j] = parent->vtable [parent_interface_offset + j]; + TRACE_INTERFACE_VTABLE (printf (" --- Inheriting: [%03d][(%03d)+(%03d)] => [%03d][(%03d)+(%03d)]\n", + parent_interface_offset + j, parent_interface_offset, j, + interface_offset + j, interface_offset, j)); + } + } + + } + } + + /*Array have a slot for stelemref*/ + if (mono_class_need_stelemref_method (klass)) { + MonoMethod *method = mono_marshal_get_virtual_stelemref (klass); + if (!method->slot) + method->slot = stelemref_slot; + else + g_assert (method->slot == stelemref_slot); + + vtable [stelemref_slot] = method; + } + + TRACE_INTERFACE_VTABLE (print_vtable_full (klass, vtable, cur_slot, first_non_interface_slot, "AFTER INHERITING PARENT VTABLE", TRUE)); + + /* Process overrides from interface default methods */ + // FIXME: Ordering between interfaces + for (int ifindex = 0; ifindex < klass->interface_offsets_count; ifindex++) { + ic = klass->interfaces_packed [ifindex]; + + mono_class_setup_methods (ic); + if (mono_class_has_failure (ic)) + goto fail; + + MonoMethod **iface_overrides; + int iface_onum; + mono_class_get_overrides_full (ic->image, ic->type_token, &iface_overrides, &iface_onum, mono_class_get_context (ic), &error); + goto_if_nok (&error, fail); + for (int i = 0; i < iface_onum; i++) { + MonoMethod *decl = iface_overrides [i*2]; + MonoMethod *override = iface_overrides [i*2 + 1]; + if (!apply_override (klass, vtable, decl, override)) + goto fail; + + if (!override_map) + override_map = g_hash_table_new (mono_aligned_addr_hash, NULL); + g_hash_table_insert (override_map, decl, override); + } + g_free (iface_overrides); + } + + /* override interface methods */ + for (i = 0; i < onum; i++) { + MonoMethod *decl = overrides [i*2]; + MonoMethod *override = overrides [i*2 + 1]; + if (MONO_CLASS_IS_INTERFACE (decl->klass)) { + if (!apply_override (klass, vtable, decl, override)) + goto fail; + + if (!override_map) + override_map = g_hash_table_new (mono_aligned_addr_hash, NULL); + g_hash_table_insert (override_map, decl, override); + } + } + + TRACE_INTERFACE_VTABLE (print_overrides (override_map, "AFTER OVERRIDING INTERFACE METHODS")); + TRACE_INTERFACE_VTABLE (print_vtable_full (klass, vtable, cur_slot, first_non_interface_slot, "AFTER OVERRIDING INTERFACE METHODS", FALSE)); + + /* + * Create a list of virtual methods to avoid calling + * mono_class_get_virtual_methods () which is slow because of the metadata + * optimization. + */ + { + gpointer iter = NULL; + MonoMethod *cm; + + virt_methods = NULL; + while ((cm = mono_class_get_virtual_methods (klass, &iter))) { + virt_methods = g_slist_prepend (virt_methods, cm); + } + if (mono_class_has_failure (klass)) + goto fail; + } + + // Loop on all implemented interfaces... + for (i = 0; i < klass->interface_offsets_count; i++) { + MonoClass *parent = klass->parent; + int ic_offset; + gboolean interface_is_explicitly_implemented_by_class; + int im_index; + + ic = klass->interfaces_packed [i]; + ic_offset = mono_class_interface_offset (klass, ic); + + mono_class_setup_methods (ic); + if (mono_class_has_failure (ic)) + goto fail; + + // Check if this interface is explicitly implemented (instead of just inherited) + if (parent != NULL) { + int implemented_interfaces_index; + interface_is_explicitly_implemented_by_class = FALSE; + for (implemented_interfaces_index = 0; implemented_interfaces_index < klass->interface_count; implemented_interfaces_index++) { + if (ic == klass->interfaces [implemented_interfaces_index]) { + interface_is_explicitly_implemented_by_class = TRUE; + break; + } + } + } else { + interface_is_explicitly_implemented_by_class = TRUE; + } + + // Loop on all interface methods... + int mcount = mono_class_get_method_count (ic); + for (im_index = 0; im_index < mcount; im_index++) { + MonoMethod *im = ic->methods [im_index]; + int im_slot = ic_offset + im->slot; + MonoMethod *override_im = (override_map != NULL) ? (MonoMethod *)g_hash_table_lookup (override_map, im) : NULL; + + if (im->flags & METHOD_ATTRIBUTE_STATIC) + continue; + + TRACE_INTERFACE_VTABLE (printf ("\tchecking iface method %s\n", mono_method_full_name (im,1))); + + // If there is an explicit implementation, just use it right away, + // otherwise look for a matching method + if (override_im == NULL) { + int cm_index; + MonoMethod *cm; + + // First look for a suitable method among the class methods + for (l = virt_methods; l; l = l->next) { + cm = (MonoMethod *)l->data; + TRACE_INTERFACE_VTABLE (printf (" For slot %d ('%s'.'%s':'%s'), trying method '%s'.'%s':'%s'... [EXPLICIT IMPLEMENTATION = %d][SLOT IS NULL = %d]", im_slot, ic->name_space, ic->name, im->name, cm->klass->name_space, cm->klass->name, cm->name, interface_is_explicitly_implemented_by_class, (vtable [im_slot] == NULL))); + if (check_interface_method_override (klass, im, cm, TRUE, interface_is_explicitly_implemented_by_class, (vtable [im_slot] == NULL))) { + TRACE_INTERFACE_VTABLE (printf ("[check ok]: ASSIGNING")); + vtable [im_slot] = cm; + /* Why do we need this? */ + if (cm->slot < 0) { + cm->slot = im_slot; + } + } + TRACE_INTERFACE_VTABLE (printf ("\n")); + if (mono_class_has_failure (klass)) /*Might be set by check_interface_method_override*/ + goto fail; + } + + // If the slot is still empty, look in all the inherited virtual methods... + if ((vtable [im_slot] == NULL) && klass->parent != NULL) { + MonoClass *parent = klass->parent; + // Reverse order, so that last added methods are preferred + for (cm_index = parent->vtable_size - 1; cm_index >= 0; cm_index--) { + MonoMethod *cm = parent->vtable [cm_index]; + + TRACE_INTERFACE_VTABLE ((cm != NULL) && printf (" For slot %d ('%s'.'%s':'%s'), trying (ancestor) method '%s'.'%s':'%s'... ", im_slot, ic->name_space, ic->name, im->name, cm->klass->name_space, cm->klass->name, cm->name)); + if ((cm != NULL) && check_interface_method_override (klass, im, cm, FALSE, FALSE, TRUE)) { + TRACE_INTERFACE_VTABLE (printf ("[everything ok]: ASSIGNING")); + vtable [im_slot] = cm; + /* Why do we need this? */ + if (cm->slot < 0) { + cm->slot = im_slot; + } + break; + } + if (mono_class_has_failure (klass)) /*Might be set by check_interface_method_override*/ + goto fail; + TRACE_INTERFACE_VTABLE ((cm != NULL) && printf ("\n")); + } + } + + if (vtable [im_slot] == NULL) { + if (!(im->flags & METHOD_ATTRIBUTE_ABSTRACT)) { + TRACE_INTERFACE_VTABLE (printf (" Using default iface method %s.\n", mono_method_full_name (im, 1))); + vtable [im_slot] = im; + } + } + } else { + g_assert (vtable [im_slot] == override_im); + } + } + } + + // If the class is not abstract, check that all its interface slots are full. + // The check is done here and not directly at the end of the loop above because + // it can happen (for injected generic array interfaces) that the same slot is + // processed multiple times (those interfaces have overlapping slots), and it + // will not always be the first pass the one that fills the slot. + if (!mono_class_is_abstract (klass)) { + for (i = 0; i < klass->interface_offsets_count; i++) { + int ic_offset; + int im_index; + + ic = klass->interfaces_packed [i]; + ic_offset = mono_class_interface_offset (klass, ic); + + int mcount = mono_class_get_method_count (ic); + for (im_index = 0; im_index < mcount; im_index++) { + MonoMethod *im = ic->methods [im_index]; + int im_slot = ic_offset + im->slot; + + if (im->flags & METHOD_ATTRIBUTE_STATIC) + continue; + + TRACE_INTERFACE_VTABLE (printf (" [class is not abstract, checking slot %d for interface '%s'.'%s', method %s, slot check is %d]\n", + im_slot, ic->name_space, ic->name, im->name, (vtable [im_slot] == NULL))); + if (vtable [im_slot] == NULL) { + print_unimplemented_interface_method_info (klass, ic, im, im_slot, overrides, onum); + goto fail; + } + } + } + } + + TRACE_INTERFACE_VTABLE (print_vtable_full (klass, vtable, cur_slot, first_non_interface_slot, "AFTER SETTING UP INTERFACE METHODS", FALSE)); + for (l = virt_methods; l; l = l->next) { + cm = (MonoMethod *)l->data; + /* + * If the method is REUSE_SLOT, we must check in the + * base class for a method to override. + */ + if (!(cm->flags & METHOD_ATTRIBUTE_NEW_SLOT)) { + int slot = -1; + for (k = klass->parent; k ; k = k->parent) { + gpointer k_iter; + MonoMethod *m1; + + k_iter = NULL; + while ((m1 = mono_class_get_virtual_methods (k, &k_iter))) { + MonoMethodSignature *cmsig, *m1sig; + + cmsig = mono_method_signature (cm); + m1sig = mono_method_signature (m1); + + if (!cmsig || !m1sig) /* FIXME proper error message, use signature_checked? */ + goto fail; + + if (!strcmp(cm->name, m1->name) && + mono_metadata_signature_equal (cmsig, m1sig)) { + + if (mono_security_core_clr_enabled ()) + mono_security_core_clr_check_override (klass, cm, m1); + + slot = mono_method_get_vtable_slot (m1); + if (slot == -1) + goto fail; + + if (is_wcf_hack_disabled () && !mono_method_can_access_method_full (cm, m1, NULL)) { + char *body_name = mono_method_full_name (cm, TRUE); + char *decl_name = mono_method_full_name (m1, TRUE); + mono_class_set_type_load_failure (klass, "Method %s overrides method '%s' which is not accessible", body_name, decl_name); + g_free (body_name); + g_free (decl_name); + goto fail; + } + + g_assert (cm->slot < max_vtsize); + if (!override_map) + override_map = g_hash_table_new (mono_aligned_addr_hash, NULL); + TRACE_INTERFACE_VTABLE (printf ("adding iface override from %s [%p] to %s [%p]\n", + mono_method_full_name (m1, 1), m1, + mono_method_full_name (cm, 1), cm)); + g_hash_table_insert (override_map, m1, cm); + break; + } + } + if (mono_class_has_failure (k)) + goto fail; + + if (slot >= 0) + break; + } + if (slot >= 0) + cm->slot = slot; + } + + /*Non final newslot methods must be given a non-interface vtable slot*/ + if ((cm->flags & METHOD_ATTRIBUTE_NEW_SLOT) && !(cm->flags & METHOD_ATTRIBUTE_FINAL) && cm->slot >= 0) + cm->slot = -1; + + if (cm->slot < 0) + cm->slot = cur_slot++; + + if (!(cm->flags & METHOD_ATTRIBUTE_ABSTRACT)) + vtable [cm->slot] = cm; + } + + /* override non interface methods */ + for (i = 0; i < onum; i++) { + MonoMethod *decl = overrides [i*2]; + if (!MONO_CLASS_IS_INTERFACE (decl->klass)) { + g_assert (decl->slot != -1); + vtable [decl->slot] = overrides [i*2 + 1]; + overrides [i * 2 + 1]->slot = decl->slot; + if (!override_map) + override_map = g_hash_table_new (mono_aligned_addr_hash, NULL); + TRACE_INTERFACE_VTABLE (printf ("adding explicit override from %s [%p] to %s [%p]\n", + mono_method_full_name (decl, 1), decl, + mono_method_full_name (overrides [i * 2 + 1], 1), overrides [i * 2 + 1])); + g_hash_table_insert (override_map, decl, overrides [i * 2 + 1]); + + if (mono_security_core_clr_enabled ()) + mono_security_core_clr_check_override (klass, vtable [decl->slot], decl); + } + } + + /* + * If a method occupies more than one place in the vtable, and it is + * overriden, then change the other occurances too. + */ + if (override_map) { + MonoMethod *cm; + + for (i = 0; i < max_vtsize; ++i) + if (vtable [i]) { + TRACE_INTERFACE_VTABLE (printf ("checking slot %d method %s[%p] for overrides\n", i, mono_method_full_name (vtable [i], 1), vtable [i])); + + cm = (MonoMethod *)g_hash_table_lookup (override_map, vtable [i]); + if (cm) + vtable [i] = cm; + } + + g_hash_table_destroy (override_map); + override_map = NULL; + } + + g_slist_free (virt_methods); + virt_methods = NULL; + + g_assert (cur_slot <= max_vtsize); + + /* Ensure that all vtable slots are filled with concrete instance methods */ + if (!mono_class_is_abstract (klass)) { + for (i = 0; i < cur_slot; ++i) { + if (vtable [i] == NULL || (vtable [i]->flags & (METHOD_ATTRIBUTE_ABSTRACT | METHOD_ATTRIBUTE_STATIC))) { + char *type_name = mono_type_get_full_name (klass); + char *method_name = vtable [i] ? mono_method_full_name (vtable [i], TRUE) : g_strdup ("none"); + mono_class_set_type_load_failure (klass, "Type %s has invalid vtable method slot %d with method %s", type_name, i, method_name); + g_free (type_name); + g_free (method_name); + g_free (vtable); + return; + } + } + } + + if (mono_class_is_ginst (klass)) { + MonoClass *gklass = mono_class_get_generic_class (klass)->container_class; + + mono_class_init (gklass); + + klass->vtable_size = MAX (gklass->vtable_size, cur_slot); + } else { + /* Check that the vtable_size value computed in mono_class_init () is correct */ + if (klass->vtable_size) + g_assert (cur_slot == klass->vtable_size); + klass->vtable_size = cur_slot; + } + + /* Try to share the vtable with our parent. */ + if (klass->parent && (klass->parent->vtable_size == klass->vtable_size) && (memcmp (klass->parent->vtable, vtable, sizeof (gpointer) * klass->vtable_size) == 0)) { + mono_memory_barrier (); + klass->vtable = klass->parent->vtable; + } else { + MonoMethod **tmp = (MonoMethod **)mono_class_alloc0 (klass, sizeof (gpointer) * klass->vtable_size); + memcpy (tmp, vtable, sizeof (gpointer) * klass->vtable_size); + mono_memory_barrier (); + klass->vtable = tmp; + } + + DEBUG_INTERFACE_VTABLE (print_vtable_full (klass, klass->vtable, klass->vtable_size, first_non_interface_slot, "FINALLY", FALSE)); + if (mono_print_vtable) { + int icount = 0; + + print_implemented_interfaces (klass); + + for (i = 0; i <= max_iid; i++) + if (MONO_CLASS_IMPLEMENTS_INTERFACE (klass, i)) + icount++; + + printf ("VTable %s (vtable entries = %d, interfaces = %d)\n", mono_type_full_name (&klass->byval_arg), + klass->vtable_size, icount); + + for (i = 0; i < cur_slot; ++i) { + MonoMethod *cm; + + cm = vtable [i]; + if (cm) { + printf (" slot assigned: %03d, slot index: %03d %s\n", i, cm->slot, + mono_method_full_name (cm, TRUE)); + } + } + + + if (icount) { + printf ("Interfaces %s.%s (max_iid = %d)\n", klass->name_space, + klass->name, max_iid); + + for (i = 0; i < klass->interface_count; i++) { + ic = klass->interfaces [i]; + printf (" slot offset: %03d, method count: %03d, iid: %03d %s\n", + mono_class_interface_offset (klass, ic), + count_virtual_methods (ic), ic->interface_id, mono_type_full_name (&ic->byval_arg)); + } + + for (k = klass->parent; k ; k = k->parent) { + for (i = 0; i < k->interface_count; i++) { + ic = k->interfaces [i]; + printf (" parent slot offset: %03d, method count: %03d, iid: %03d %s\n", + mono_class_interface_offset (klass, ic), + count_virtual_methods (ic), ic->interface_id, mono_type_full_name (&ic->byval_arg)); + } + } + } + } + + g_free (vtable); + + VERIFY_INTERFACE_VTABLE (mono_class_verify_vtable (klass)); + return; + +fail: + { + char *name = mono_type_get_full_name (klass); + if (!is_ok (&error)) + mono_class_set_type_load_failure (klass, "VTable setup of type %s failed due to: %s", name, mono_error_get_message (&error)); + else + mono_class_set_type_load_failure (klass, "VTable setup of type %s failed", name); + mono_error_cleanup (&error); + g_free (name); + g_free (vtable); + if (override_map) + g_hash_table_destroy (override_map); + if (virt_methods) + g_slist_free (virt_methods); + } +} + +/* + * mono_method_get_vtable_slot: + * + * Returns method->slot, computing it if neccesary. Return -1 on failure. + * LOCKING: Acquires the loader lock. + * + * FIXME Use proper MonoError machinery here. + */ +int +mono_method_get_vtable_slot (MonoMethod *method) +{ + if (method->slot == -1) { + mono_class_setup_vtable (method->klass); + if (mono_class_has_failure (method->klass)) + return -1; + if (method->slot == -1) { + MonoClass *gklass; + int i, mcount; + + if (!mono_class_is_ginst (method->klass)) { + g_assert (method->is_inflated); + return mono_method_get_vtable_slot (((MonoMethodInflated*)method)->declaring); + } + + /* This can happen for abstract methods of generic instances due to the shortcut code in mono_class_setup_vtable_general (). */ + g_assert (mono_class_is_ginst (method->klass)); + gklass = mono_class_get_generic_class (method->klass)->container_class; + mono_class_setup_methods (method->klass); + g_assert (method->klass->methods); + mcount = mono_class_get_method_count (method->klass); + for (i = 0; i < mcount; ++i) { + if (method->klass->methods [i] == method) + break; + } + g_assert (i < mcount); + g_assert (gklass->methods); + method->slot = gklass->methods [i]->slot; + } + g_assert (method->slot != -1); + } + return method->slot; +} + +/** + * mono_method_get_vtable_index: + * \param method a method + * + * Returns the index into the runtime vtable to access the method or, + * in the case of a virtual generic method, the virtual generic method + * thunk. Returns -1 on failure. + * + * FIXME Use proper MonoError machinery here. + */ +int +mono_method_get_vtable_index (MonoMethod *method) +{ + if (method->is_inflated && (method->flags & METHOD_ATTRIBUTE_VIRTUAL)) { + MonoMethodInflated *imethod = (MonoMethodInflated*)method; + if (imethod->declaring->is_generic) + return mono_method_get_vtable_slot (imethod->declaring); + } + return mono_method_get_vtable_slot (method); +} + +static MonoMethod *default_ghc = NULL; +static MonoMethod *default_finalize = NULL; +static int finalize_slot = -1; +static int ghc_slot = -1; + +static void +initialize_object_slots (MonoClass *klass) +{ + int i; + if (default_ghc) + return; + if (klass == mono_defaults.object_class) { + mono_class_setup_vtable (klass); + for (i = 0; i < klass->vtable_size; ++i) { + MonoMethod *cm = klass->vtable [i]; + + if (!strcmp (cm->name, "GetHashCode")) + ghc_slot = i; + else if (!strcmp (cm->name, "Finalize")) + finalize_slot = i; + } + + g_assert (ghc_slot > 0); + default_ghc = klass->vtable [ghc_slot]; + + g_assert (finalize_slot > 0); + default_finalize = klass->vtable [finalize_slot]; + } +} + +typedef struct { + MonoMethod *array_method; + char *name; +} GenericArrayMethodInfo; + +static int generic_array_method_num = 0; +static GenericArrayMethodInfo *generic_array_method_info = NULL; + +static int +generic_array_methods (MonoClass *klass) +{ + int i, count_generic = 0, mcount; + GList *list = NULL, *tmp; + if (generic_array_method_num) + return generic_array_method_num; + mono_class_setup_methods (klass->parent); /*This is setting up System.Array*/ + g_assert (!mono_class_has_failure (klass->parent)); /*So hitting this assert is a huge problem*/ + mcount = mono_class_get_method_count (klass->parent); + for (i = 0; i < mcount; i++) { + MonoMethod *m = klass->parent->methods [i]; + if (!strncmp (m->name, "InternalArray__", 15)) { + count_generic++; + list = g_list_prepend (list, m); + } + } + list = g_list_reverse (list); + generic_array_method_info = (GenericArrayMethodInfo *)mono_image_alloc (mono_defaults.corlib, sizeof (GenericArrayMethodInfo) * count_generic); + i = 0; + for (tmp = list; tmp; tmp = tmp->next) { + const char *mname, *iname; + gchar *name; + MonoMethod *m = (MonoMethod *)tmp->data; + const char *ireadonlylist_prefix = "InternalArray__IReadOnlyList_"; + const char *ireadonlycollection_prefix = "InternalArray__IReadOnlyCollection_"; + + generic_array_method_info [i].array_method = m; + if (!strncmp (m->name, "InternalArray__ICollection_", 27)) { + iname = "System.Collections.Generic.ICollection`1."; + mname = m->name + 27; + } else if (!strncmp (m->name, "InternalArray__IEnumerable_", 27)) { + iname = "System.Collections.Generic.IEnumerable`1."; + mname = m->name + 27; + } else if (!strncmp (m->name, ireadonlylist_prefix, strlen (ireadonlylist_prefix))) { + iname = "System.Collections.Generic.IReadOnlyList`1."; + mname = m->name + strlen (ireadonlylist_prefix); + } else if (!strncmp (m->name, ireadonlycollection_prefix, strlen (ireadonlycollection_prefix))) { + iname = "System.Collections.Generic.IReadOnlyCollection`1."; + mname = m->name + strlen (ireadonlycollection_prefix); + } else if (!strncmp (m->name, "InternalArray__", 15)) { + iname = "System.Collections.Generic.IList`1."; + mname = m->name + 15; + } else { + g_assert_not_reached (); + } + + name = (gchar *)mono_image_alloc (mono_defaults.corlib, strlen (iname) + strlen (mname) + 1); + strcpy (name, iname); + strcpy (name + strlen (iname), mname); + generic_array_method_info [i].name = name; + i++; + } + /*g_print ("array generic methods: %d\n", count_generic);*/ + + generic_array_method_num = count_generic; + g_list_free (list); + return generic_array_method_num; +} + +static void +setup_generic_array_ifaces (MonoClass *klass, MonoClass *iface, MonoMethod **methods, int pos, GHashTable *cache) +{ + MonoGenericContext tmp_context; + int i; + + tmp_context.class_inst = NULL; + tmp_context.method_inst = mono_class_get_generic_class (iface)->context.class_inst; + //g_print ("setting up array interface: %s\n", mono_type_get_name_full (&iface->byval_arg, 0)); + + for (i = 0; i < generic_array_method_num; i++) { + MonoError error; + MonoMethod *m = generic_array_method_info [i].array_method; + MonoMethod *inflated, *helper; + + inflated = mono_class_inflate_generic_method_checked (m, &tmp_context, &error); + mono_error_assert_ok (&error); + helper = g_hash_table_lookup (cache, inflated); + if (!helper) { + helper = mono_marshal_get_generic_array_helper (klass, generic_array_method_info [i].name, inflated); + g_hash_table_insert (cache, inflated, helper); + } + methods [pos ++] = helper; + } +} + +static char* +concat_two_strings_with_zero (MonoImage *image, const char *s1, const char *s2) +{ + int null_length = strlen ("(null)"); + int len = (s1 ? strlen (s1) : null_length) + (s2 ? strlen (s2) : null_length) + 2; + char *s = (char *)mono_image_alloc (image, len); + int result; + + result = g_snprintf (s, len, "%s%c%s", s1 ? s1 : "(null)", '\0', s2 ? s2 : "(null)"); + g_assert (result == len - 1); + + return s; +} + +/** + * mono_class_init: + * \param klass the class to initialize + * + * Compute the \c instance_size, \c class_size and other infos that cannot be + * computed at \c mono_class_get time. Also compute vtable_size if possible. + * Initializes the following fields in \p klass: + * - all the fields initialized by \c mono_class_init_sizes + * - has_cctor + * - ghcimpl + * - inited + * + * LOCKING: Acquires the loader lock. + * + * \returns TRUE on success or FALSE if there was a problem in loading + * the type (incorrect assemblies, missing assemblies, methods, etc). + */ +gboolean +mono_class_init (MonoClass *klass) +{ + int i, vtable_size = 0, array_method_count = 0; + MonoCachedClassInfo cached_info; + gboolean has_cached_info; + gboolean locked = FALSE; + gboolean ghcimpl = FALSE; + gboolean has_cctor = FALSE; + int first_iface_slot = 0; + + g_assert (klass); + + /* Double-checking locking pattern */ + if (klass->inited || mono_class_has_failure (klass)) + return !mono_class_has_failure (klass); + + /*g_print ("Init class %s\n", mono_type_get_full_name (klass));*/ + + /* + * This function can recursively call itself. + */ + GSList *init_list = (GSList *)mono_native_tls_get_value (init_pending_tls_id); + if (g_slist_find (init_list, klass)) { + mono_class_set_type_load_failure (klass, "Recursive type definition detected"); + goto leave_no_init_pending; + } + init_list = g_slist_prepend (init_list, klass); + mono_native_tls_set_value (init_pending_tls_id, init_list); + + /* + * We want to avoid doing complicated work inside locks, so we compute all the required + * information and write it to @klass inside a lock. + */ + + if (mono_verifier_is_enabled_for_class (klass) && !mono_verifier_verify_class (klass)) { + mono_class_set_type_load_failure (klass, "%s", concat_two_strings_with_zero (klass->image, klass->name, klass->image->assembly_name)); + goto leave; + } + + if (klass->byval_arg.type == MONO_TYPE_ARRAY || klass->byval_arg.type == MONO_TYPE_SZARRAY) { + MonoClass *element_class = klass->element_class; + if (!element_class->inited) + mono_class_init (element_class); + if (mono_class_set_type_load_failure_causedby_class (klass, element_class, "Could not load array element class")) + goto leave; + } + + UnlockedIncrement (&mono_stats.initialized_class_count); + + if (mono_class_is_ginst (klass) && !mono_class_get_generic_class (klass)->is_dynamic) { + MonoClass *gklass = mono_class_get_generic_class (klass)->container_class; + + mono_class_init (gklass); + if (mono_class_set_type_load_failure_causedby_class (klass, gklass, "Generic Type Definition failed to init")) + goto leave; + + if (MONO_CLASS_IS_INTERFACE (klass)) + mono_class_setup_interface_id (klass); + } + + if (klass->parent && !klass->parent->inited) + mono_class_init (klass->parent); + + has_cached_info = mono_class_get_cached_class_info (klass, &cached_info); + + /* Compute instance size etc. */ + init_sizes_with_info (klass, has_cached_info ? &cached_info : NULL); + if (mono_class_has_failure (klass)) + goto leave; + + mono_class_setup_supertypes (klass); + + if (!default_ghc) + initialize_object_slots (klass); + + /* + * Initialize the rest of the data without creating a generic vtable if possible. + * If possible, also compute vtable_size, so mono_class_create_runtime_vtable () can + * also avoid computing a generic vtable. + */ + if (has_cached_info) { + /* AOT case */ + vtable_size = cached_info.vtable_size; + ghcimpl = cached_info.ghcimpl; + has_cctor = cached_info.has_cctor; + } else if (klass->rank == 1 && klass->byval_arg.type == MONO_TYPE_SZARRAY) { + /* SZARRAY can have 3 vtable layouts, with and without the stelemref method and enum element type + * The first slot if for array with. + */ + static int szarray_vtable_size[3] = { 0 }; + + int slot; + + if (MONO_TYPE_IS_REFERENCE (&klass->element_class->byval_arg)) + slot = 0; + else if (klass->element_class->enumtype) + slot = 1; + else + slot = 2; + + /* SZARRAY case */ + if (!szarray_vtable_size [slot]) { + mono_class_setup_vtable (klass); + szarray_vtable_size [slot] = klass->vtable_size; + vtable_size = klass->vtable_size; + } else { + vtable_size = szarray_vtable_size[slot]; + } + } else if (mono_class_is_ginst (klass) && !MONO_CLASS_IS_INTERFACE (klass)) { + MonoClass *gklass = mono_class_get_generic_class (klass)->container_class; + + /* Generic instance case */ + ghcimpl = gklass->ghcimpl; + has_cctor = gklass->has_cctor; + + mono_class_setup_vtable (gklass); + if (mono_class_set_type_load_failure_causedby_class (klass, gklass, "Generic type definition failed to init")) + goto leave; + + vtable_size = gklass->vtable_size; + } else { + /* General case */ + + /* ghcimpl is not currently used + klass->ghcimpl = 1; + if (klass->parent) { + MonoMethod *cmethod = klass->vtable [ghc_slot]; + if (cmethod->is_inflated) + cmethod = ((MonoMethodInflated*)cmethod)->declaring; + if (cmethod == default_ghc) { + klass->ghcimpl = 0; + } + } + */ + + /* C# doesn't allow interfaces to have cctors */ + if (!MONO_CLASS_IS_INTERFACE (klass) || klass->image != mono_defaults.corlib) { + MonoMethod *cmethod = NULL; + + if (mono_class_is_ginst (klass)) { + MonoClass *gklass = mono_class_get_generic_class (klass)->container_class; + + /* Generic instance case */ + ghcimpl = gklass->ghcimpl; + has_cctor = gklass->has_cctor; + } else if (klass->type_token && !image_is_dynamic(klass->image)) { + cmethod = find_method_in_metadata (klass, ".cctor", 0, METHOD_ATTRIBUTE_SPECIAL_NAME); + /* The find_method function ignores the 'flags' argument */ + if (cmethod && (cmethod->flags & METHOD_ATTRIBUTE_SPECIAL_NAME)) + has_cctor = 1; + } else { + mono_class_setup_methods (klass); + if (mono_class_has_failure (klass)) + goto leave; + + int mcount = mono_class_get_method_count (klass); + for (i = 0; i < mcount; ++i) { + MonoMethod *method = klass->methods [i]; + if ((method->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) && + (strcmp (".cctor", method->name) == 0)) { + has_cctor = 1; + break; + } + } + } + } + } + + if (klass->rank) { + array_method_count = 3 + (klass->rank > 1? 2: 1); + + if (klass->interface_count) { + int count_generic = generic_array_methods (klass); + array_method_count += klass->interface_count * count_generic; + } + } + + if (klass->parent) { + if (!klass->parent->vtable_size) + mono_class_setup_vtable (klass->parent); + if (mono_class_set_type_load_failure_causedby_class (klass, klass->parent, "Parent class vtable failed to initialize")) + goto leave; + g_assert (klass->parent->vtable_size); + first_iface_slot = klass->parent->vtable_size; + if (mono_class_need_stelemref_method (klass)) + ++first_iface_slot; + } + + /* + * Do the actual changes to @klass inside the loader lock + */ + mono_loader_lock (); + locked = TRUE; + + if (klass->inited || mono_class_has_failure (klass)) { + /* Somebody might have gotten in before us */ + goto leave; + } + + UnlockedIncrement (&mono_stats.initialized_class_count); + + if (mono_class_is_ginst (klass) && !mono_class_get_generic_class (klass)->is_dynamic) + UnlockedIncrement (&mono_stats.generic_class_count); + + if (mono_class_is_ginst (klass) || image_is_dynamic (klass->image) || !klass->type_token || (has_cached_info && !cached_info.has_nested_classes)) + klass->nested_classes_inited = TRUE; + klass->ghcimpl = ghcimpl; + klass->has_cctor = has_cctor; + if (vtable_size) + klass->vtable_size = vtable_size; + if (has_cached_info) { + klass->has_finalize = cached_info.has_finalize; + klass->has_finalize_inited = TRUE; + } + if (klass->rank) + mono_class_set_method_count (klass, array_method_count); + + mono_loader_unlock (); + locked = FALSE; + + setup_interface_offsets (klass, first_iface_slot, TRUE); + + if (mono_security_core_clr_enabled ()) + mono_security_core_clr_check_inheritance (klass); + + if (mono_class_is_ginst (klass) && !mono_verifier_class_is_valid_generic_instantiation (klass)) + mono_class_set_type_load_failure (klass, "Invalid generic instantiation"); + + goto leave; + +leave: + init_list = mono_native_tls_get_value (init_pending_tls_id); + init_list = g_slist_remove (init_list, klass); + mono_native_tls_set_value (init_pending_tls_id, init_list); + +leave_no_init_pending: + if (locked) + mono_loader_unlock (); + + /* Leave this for last */ + mono_loader_lock (); + klass->inited = 1; + mono_loader_unlock (); + + return !mono_class_has_failure (klass); +} + +/* + * mono_class_has_finalizer: + * + * Return whenever KLASS has a finalizer, initializing klass->has_finalizer in the + * process. + */ +gboolean +mono_class_has_finalizer (MonoClass *klass) +{ + gboolean has_finalize = FALSE; + + if (klass->has_finalize_inited) + return klass->has_finalize; + + /* Interfaces and valuetypes are not supposed to have finalizers */ + if (!(MONO_CLASS_IS_INTERFACE (klass) || klass->valuetype)) { + MonoMethod *cmethod = NULL; + + if (klass->rank == 1 && klass->byval_arg.type == MONO_TYPE_SZARRAY) { + } else if (mono_class_is_ginst (klass)) { + MonoClass *gklass = mono_class_get_generic_class (klass)->container_class; + + has_finalize = mono_class_has_finalizer (gklass); + } else if (klass->parent && klass->parent->has_finalize) { + has_finalize = TRUE; + } else { + if (klass->parent) { + /* + * Can't search in metadata for a method named Finalize, because that + * ignores overrides. + */ + mono_class_setup_vtable (klass); + if (mono_class_has_failure (klass)) + cmethod = NULL; + else + cmethod = klass->vtable [finalize_slot]; + } + + if (cmethod) { + g_assert (klass->vtable_size > finalize_slot); + + if (klass->parent) { + if (cmethod->is_inflated) + cmethod = ((MonoMethodInflated*)cmethod)->declaring; + if (cmethod != default_finalize) + has_finalize = TRUE; + } + } + } + } + + mono_loader_lock (); + if (!klass->has_finalize_inited) { + klass->has_finalize = has_finalize ? 1 : 0; + + mono_memory_barrier (); + klass->has_finalize_inited = TRUE; + } + mono_loader_unlock (); + + return klass->has_finalize; +} + +gboolean +mono_is_corlib_image (MonoImage *image) +{ + return image == mono_defaults.corlib; +} + +/* + * LOCKING: this assumes the loader lock is held + */ +void +mono_class_setup_mono_type (MonoClass *klass) +{ + const char *name = klass->name; + const char *nspace = klass->name_space; + gboolean is_corlib = mono_is_corlib_image (klass->image); + + klass->this_arg.byref = 1; + klass->this_arg.data.klass = klass; + klass->this_arg.type = MONO_TYPE_CLASS; + klass->byval_arg.data.klass = klass; + klass->byval_arg.type = MONO_TYPE_CLASS; + + if (is_corlib && !strcmp (nspace, "System")) { + if (!strcmp (name, "ValueType")) { + /* + * do not set the valuetype bit for System.ValueType. + * klass->valuetype = 1; + */ + klass->blittable = TRUE; + } else if (!strcmp (name, "Enum")) { + /* + * do not set the valuetype bit for System.Enum. + * klass->valuetype = 1; + */ + klass->valuetype = 0; + klass->enumtype = 0; + } else if (!strcmp (name, "Object")) { + klass->byval_arg.type = MONO_TYPE_OBJECT; + klass->this_arg.type = MONO_TYPE_OBJECT; + } else if (!strcmp (name, "String")) { + klass->byval_arg.type = MONO_TYPE_STRING; + klass->this_arg.type = MONO_TYPE_STRING; + } else if (!strcmp (name, "TypedReference")) { + klass->byval_arg.type = MONO_TYPE_TYPEDBYREF; + klass->this_arg.type = MONO_TYPE_TYPEDBYREF; + } + } + + if (klass->valuetype) { + int t = MONO_TYPE_VALUETYPE; + + if (is_corlib && !strcmp (nspace, "System")) { + switch (*name) { + case 'B': + if (!strcmp (name, "Boolean")) { + t = MONO_TYPE_BOOLEAN; + } else if (!strcmp(name, "Byte")) { + t = MONO_TYPE_U1; + klass->blittable = TRUE; + } + break; + case 'C': + if (!strcmp (name, "Char")) { + t = MONO_TYPE_CHAR; + } + break; + case 'D': + if (!strcmp (name, "Double")) { + t = MONO_TYPE_R8; + klass->blittable = TRUE; + } + break; + case 'I': + if (!strcmp (name, "Int32")) { + t = MONO_TYPE_I4; + klass->blittable = TRUE; + } else if (!strcmp(name, "Int16")) { + t = MONO_TYPE_I2; + klass->blittable = TRUE; + } else if (!strcmp(name, "Int64")) { + t = MONO_TYPE_I8; + klass->blittable = TRUE; + } else if (!strcmp(name, "IntPtr")) { + t = MONO_TYPE_I; + klass->blittable = TRUE; + } + break; + case 'S': + if (!strcmp (name, "Single")) { + t = MONO_TYPE_R4; + klass->blittable = TRUE; + } else if (!strcmp(name, "SByte")) { + t = MONO_TYPE_I1; + klass->blittable = TRUE; + } + break; + case 'U': + if (!strcmp (name, "UInt32")) { + t = MONO_TYPE_U4; + klass->blittable = TRUE; + } else if (!strcmp(name, "UInt16")) { + t = MONO_TYPE_U2; + klass->blittable = TRUE; + } else if (!strcmp(name, "UInt64")) { + t = MONO_TYPE_U8; + klass->blittable = TRUE; + } else if (!strcmp(name, "UIntPtr")) { + t = MONO_TYPE_U; + klass->blittable = TRUE; + } + break; + case 'T': + if (!strcmp (name, "TypedReference")) { + t = MONO_TYPE_TYPEDBYREF; + klass->blittable = TRUE; + } + break; + case 'V': + if (!strcmp (name, "Void")) { + t = MONO_TYPE_VOID; + } + break; + default: + break; + } + } + klass->byval_arg.type = (MonoTypeEnum)t; + klass->this_arg.type = (MonoTypeEnum)t; + } + + if (MONO_CLASS_IS_INTERFACE (klass)) { + klass->interface_id = mono_get_unique_iid (klass); + + if (is_corlib && !strcmp (nspace, "System.Collections.Generic")) { + //FIXME IEnumerator needs to be special because GetEnumerator uses magic under the hood + /* FIXME: System.Array/InternalEnumerator don't need all this interface fabrication machinery. + * MS returns diferrent types based on which instance is called. For example: + * object obj = new byte[10][]; + * Type a = ((IEnumerable)obj).GetEnumerator ().GetType (); + * Type b = ((IEnumerable>)obj).GetEnumerator ().GetType (); + * a != b ==> true + */ + if (!strcmp (name, "IList`1") || !strcmp (name, "ICollection`1") || !strcmp (name, "IEnumerable`1") || !strcmp (name, "IEnumerator`1")) + klass->is_array_special_interface = 1; + } + } +} + +#ifndef DISABLE_COM +/* + * COM initialization is delayed until needed. + * However when a [ComImport] attribute is present on a type it will trigger + * the initialization. This is not a problem unless the BCL being executed + * lacks the types that COM depends on (e.g. Variant on Silverlight). + */ +static void +init_com_from_comimport (MonoClass *klass) +{ + /* we don't always allow COM initialization under the CoreCLR (e.g. Moonlight does not require it) */ + if (mono_security_core_clr_enabled ()) { + /* but some other CoreCLR user could requires it for their platform (i.e. trusted) code */ + if (!mono_security_core_clr_determine_platform_image (klass->image)) { + /* but it can not be made available for application (i.e. user code) since all COM calls + * are considered native calls. In this case we fail with a TypeLoadException (just like + * Silverlight 2 does */ + mono_class_set_type_load_failure (klass, ""); + return; + } + } + + /* FIXME : we should add an extra checks to ensure COM can be initialized properly before continuing */ +} +#endif /*DISABLE_COM*/ + +/* + * LOCKING: this assumes the loader lock is held + */ +void +mono_class_setup_parent (MonoClass *klass, MonoClass *parent) +{ + gboolean system_namespace; + gboolean is_corlib = mono_is_corlib_image (klass->image); + + system_namespace = !strcmp (klass->name_space, "System") && is_corlib; + + /* if root of the hierarchy */ + if (system_namespace && !strcmp (klass->name, "Object")) { + klass->parent = NULL; + klass->instance_size = sizeof (MonoObject); + return; + } + if (!strcmp (klass->name, "")) { + klass->parent = NULL; + klass->instance_size = 0; + return; + } + + if (!MONO_CLASS_IS_INTERFACE (klass)) { + /* Imported COM Objects always derive from __ComObject. */ +#ifndef DISABLE_COM + if (MONO_CLASS_IS_IMPORT (klass)) { + init_com_from_comimport (klass); + if (parent == mono_defaults.object_class) + parent = mono_class_get_com_object_class (); + } +#endif + if (!parent) { + /* set the parent to something useful and safe, but mark the type as broken */ + parent = mono_defaults.object_class; + mono_class_set_type_load_failure (klass, ""); + g_assert (parent); + } + + klass->parent = parent; + + if (mono_class_is_ginst (parent) && !parent->name) { + /* + * If the parent is a generic instance, we may get + * called before it is fully initialized, especially + * before it has its name. + */ + return; + } + +#ifndef DISABLE_REMOTING + klass->marshalbyref = parent->marshalbyref; + klass->contextbound = parent->contextbound; +#endif + + klass->delegate = parent->delegate; + + if (MONO_CLASS_IS_IMPORT (klass) || mono_class_is_com_object (parent)) + mono_class_set_is_com_object (klass); + + if (system_namespace) { +#ifndef DISABLE_REMOTING + if (klass->name [0] == 'M' && !strcmp (klass->name, "MarshalByRefObject")) + klass->marshalbyref = 1; + + if (klass->name [0] == 'C' && !strcmp (klass->name, "ContextBoundObject")) + klass->contextbound = 1; +#endif + if (klass->name [0] == 'D' && !strcmp (klass->name, "Delegate")) + klass->delegate = 1; + } + + if (klass->parent->enumtype || (mono_is_corlib_image (klass->parent->image) && (strcmp (klass->parent->name, "ValueType") == 0) && + (strcmp (klass->parent->name_space, "System") == 0))) + klass->valuetype = 1; + if (mono_is_corlib_image (klass->parent->image) && ((strcmp (klass->parent->name, "Enum") == 0) && (strcmp (klass->parent->name_space, "System") == 0))) { + klass->valuetype = klass->enumtype = 1; + } + /*klass->enumtype = klass->parent->enumtype; */ + } else { + /* initialize com types if COM interfaces are present */ +#ifndef DISABLE_COM + if (MONO_CLASS_IS_IMPORT (klass)) + init_com_from_comimport (klass); +#endif + klass->parent = NULL; + } + +} + +/* + * mono_class_setup_supertypes: + * @class: a class + * + * Build the data structure needed to make fast type checks work. + * This currently sets two fields in @class: + * - idepth: distance between @class and System.Object in the type + * hierarchy + 1 + * - supertypes: array of classes: each element has a class in the hierarchy + * starting from @class up to System.Object + * + * LOCKING: Acquires the loader lock. + */ +void +mono_class_setup_supertypes (MonoClass *klass) +{ + int ms, idepth; + MonoClass **supertypes; + + mono_atomic_load_acquire (supertypes, MonoClass **, &klass->supertypes); + if (supertypes) + return; + + if (klass->parent && !klass->parent->supertypes) + mono_class_setup_supertypes (klass->parent); + if (klass->parent) + idepth = klass->parent->idepth + 1; + else + idepth = 1; + + ms = MAX (MONO_DEFAULT_SUPERTABLE_SIZE, idepth); + supertypes = (MonoClass **)mono_class_alloc0 (klass, sizeof (MonoClass *) * ms); + + if (klass->parent) { + CHECKED_METADATA_WRITE_PTR ( supertypes [idepth - 1] , klass ); + + int supertype_idx; + for (supertype_idx = 0; supertype_idx < klass->parent->idepth; supertype_idx++) + CHECKED_METADATA_WRITE_PTR ( supertypes [supertype_idx] , klass->parent->supertypes [supertype_idx] ); + } else { + CHECKED_METADATA_WRITE_PTR ( supertypes [0] , klass ); + } + + mono_memory_barrier (); + + mono_loader_lock (); + klass->idepth = idepth; + /* Needed so idepth is visible before supertypes is set */ + mono_memory_barrier (); + klass->supertypes = supertypes; + mono_loader_unlock (); +} + +static gboolean +discard_gclass_due_to_failure (MonoClass *gclass, void *user_data) +{ + return mono_class_get_generic_class (gclass)->container_class == user_data; +} + +static gboolean +fix_gclass_incomplete_instantiation (MonoClass *gclass, void *user_data) +{ + MonoClass *gtd = (MonoClass*)user_data; + /* Only try to fix generic instances of @gtd */ + if (mono_class_get_generic_class (gclass)->container_class != gtd) + return FALSE; + + /* Check if the generic instance has no parent. */ + if (gtd->parent && !gclass->parent) + mono_generic_class_setup_parent (gclass, gtd); + + return TRUE; +} + +static void +mono_class_set_failure_and_error (MonoClass *klass, MonoError *error, const char *msg) +{ + mono_class_set_type_load_failure (klass, "%s", msg); + mono_error_set_type_load_class (error, klass, "%s", msg); +} + +/** + * mono_class_create_from_typedef: + * \param image: image where the token is valid + * \param type_token: typedef token + * \param error: used to return any error found while creating the type + * + * Create the MonoClass* representing the specified type token. + * \p type_token must be a TypeDef token. + * + * FIXME: don't return NULL on failure, just let the caller figure it out. + */ +static MonoClass * +mono_class_create_from_typedef (MonoImage *image, guint32 type_token, MonoError *error) +{ + MonoTableInfo *tt = &image->tables [MONO_TABLE_TYPEDEF]; + MonoClass *klass, *parent = NULL; + guint32 cols [MONO_TYPEDEF_SIZE]; + guint32 cols_next [MONO_TYPEDEF_SIZE]; + guint tidx = mono_metadata_token_index (type_token); + MonoGenericContext *context = NULL; + const char *name, *nspace; + guint icount = 0; + MonoClass **interfaces; + guint32 field_last, method_last; + guint32 nesting_tokeen; + + error_init (error); + + if (mono_metadata_token_table (type_token) != MONO_TABLE_TYPEDEF || tidx > tt->rows) { + mono_error_set_bad_image (error, image, "Invalid typedef token %x", type_token); + return NULL; + } + + mono_loader_lock (); + + if ((klass = (MonoClass *)mono_internal_hash_table_lookup (&image->class_cache, GUINT_TO_POINTER (type_token)))) { + mono_loader_unlock (); + return klass; + } + + mono_metadata_decode_row (tt, tidx - 1, cols, MONO_TYPEDEF_SIZE); + + name = mono_metadata_string_heap (image, cols [MONO_TYPEDEF_NAME]); + nspace = mono_metadata_string_heap (image, cols [MONO_TYPEDEF_NAMESPACE]); + + if (mono_metadata_has_generic_params (image, type_token)) { + klass = mono_image_alloc0 (image, sizeof (MonoClassGtd)); + klass->class_kind = MONO_CLASS_GTD; + UnlockedAdd (&classes_size, sizeof (MonoClassGtd)); + ++class_gtd_count; + } else { + klass = mono_image_alloc0 (image, sizeof (MonoClassDef)); + klass->class_kind = MONO_CLASS_DEF; + UnlockedAdd (&classes_size, sizeof (MonoClassDef)); + ++class_def_count; + } + + klass->name = name; + klass->name_space = nspace; + + MONO_PROFILER_RAISE (class_loading, (klass)); + + klass->image = image; + klass->type_token = type_token; + mono_class_set_flags (klass, cols [MONO_TYPEDEF_FLAGS]); + + mono_internal_hash_table_insert (&image->class_cache, GUINT_TO_POINTER (type_token), klass); + + /* + * Check whether we're a generic type definition. + */ + if (mono_class_is_gtd (klass)) { + MonoGenericContainer *generic_container = mono_metadata_load_generic_params (image, klass->type_token, NULL); + generic_container->owner.klass = klass; + generic_container->is_anonymous = FALSE; // Owner class is now known, container is no longer anonymous + context = &generic_container->context; + mono_class_set_generic_container (klass, generic_container); + MonoType *canonical_inst = &((MonoClassGtd*)klass)->canonical_inst; + canonical_inst->type = MONO_TYPE_GENERICINST; + canonical_inst->data.generic_class = mono_metadata_lookup_generic_class (klass, context->class_inst, FALSE); + enable_gclass_recording (); + } + + if (cols [MONO_TYPEDEF_EXTENDS]) { + MonoClass *tmp; + guint32 parent_token = mono_metadata_token_from_dor (cols [MONO_TYPEDEF_EXTENDS]); + + if (mono_metadata_token_table (parent_token) == MONO_TABLE_TYPESPEC) { + /*WARNING: this must satisfy mono_metadata_type_hash*/ + klass->this_arg.byref = 1; + klass->this_arg.data.klass = klass; + klass->this_arg.type = MONO_TYPE_CLASS; + klass->byval_arg.data.klass = klass; + klass->byval_arg.type = MONO_TYPE_CLASS; + } + parent = mono_class_get_checked (image, parent_token, error); + if (parent && context) /* Always inflate */ + parent = mono_class_inflate_generic_class_checked (parent, context, error); + + if (parent == NULL) { + mono_class_set_type_load_failure (klass, "%s", mono_error_get_message (error)); + goto parent_failure; + } + + for (tmp = parent; tmp; tmp = tmp->parent) { + if (tmp == klass) { + mono_class_set_failure_and_error (klass, error, "Cycle found while resolving parent"); + goto parent_failure; + } + if (mono_class_is_gtd (klass) && mono_class_is_ginst (tmp) && mono_class_get_generic_class (tmp)->container_class == klass) { + mono_class_set_failure_and_error (klass, error, "Parent extends generic instance of this type"); + goto parent_failure; + } + } + } + + mono_class_setup_parent (klass, parent); + + /* uses ->valuetype, which is initialized by mono_class_setup_parent above */ + mono_class_setup_mono_type (klass); + + if (mono_class_is_gtd (klass)) + disable_gclass_recording (fix_gclass_incomplete_instantiation, klass); + + /* + * This might access klass->byval_arg for recursion generated by generic constraints, + * so it has to come after setup_mono_type (). + */ + if ((nesting_tokeen = mono_metadata_nested_in_typedef (image, type_token))) { + klass->nested_in = mono_class_create_from_typedef (image, nesting_tokeen, error); + if (!mono_error_ok (error)) { + /*FIXME implement a mono_class_set_failure_from_mono_error */ + mono_class_set_type_load_failure (klass, "%s", mono_error_get_message (error)); + mono_loader_unlock (); + MONO_PROFILER_RAISE (class_failed, (klass)); + return NULL; + } + } + + if ((mono_class_get_flags (klass) & TYPE_ATTRIBUTE_STRING_FORMAT_MASK) == TYPE_ATTRIBUTE_UNICODE_CLASS) + klass->unicode = 1; + +#ifdef HOST_WIN32 + if ((mono_class_get_flags (klass) & TYPE_ATTRIBUTE_STRING_FORMAT_MASK) == TYPE_ATTRIBUTE_AUTO_CLASS) + klass->unicode = 1; +#endif + + klass->cast_class = klass->element_class = klass; + if (mono_is_corlib_image (klass->image)) { + switch (klass->byval_arg.type) { + case MONO_TYPE_I1: + if (mono_defaults.byte_class) + klass->cast_class = mono_defaults.byte_class; + break; + case MONO_TYPE_U1: + if (mono_defaults.sbyte_class) + mono_defaults.sbyte_class = klass; + break; + case MONO_TYPE_I2: + if (mono_defaults.uint16_class) + mono_defaults.uint16_class = klass; + break; + case MONO_TYPE_U2: + if (mono_defaults.int16_class) + klass->cast_class = mono_defaults.int16_class; + break; + case MONO_TYPE_I4: + if (mono_defaults.uint32_class) + mono_defaults.uint32_class = klass; + break; + case MONO_TYPE_U4: + if (mono_defaults.int32_class) + klass->cast_class = mono_defaults.int32_class; + break; + case MONO_TYPE_I8: + if (mono_defaults.uint64_class) + mono_defaults.uint64_class = klass; + break; + case MONO_TYPE_U8: + if (mono_defaults.int64_class) + klass->cast_class = mono_defaults.int64_class; + break; + } + } + + if (!klass->enumtype) { + if (!mono_metadata_interfaces_from_typedef_full ( + image, type_token, &interfaces, &icount, FALSE, context, error)){ + + mono_class_set_type_load_failure (klass, "%s", mono_error_get_message (error)); + mono_loader_unlock (); + MONO_PROFILER_RAISE (class_failed, (klass)); + return NULL; + } + + /* This is required now that it is possible for more than 2^16 interfaces to exist. */ + g_assert(icount <= 65535); + + klass->interfaces = interfaces; + klass->interface_count = icount; + klass->interfaces_inited = 1; + } + + /*g_print ("Load class %s\n", name);*/ + + /* + * Compute the field and method lists + */ + int first_field_idx = cols [MONO_TYPEDEF_FIELD_LIST] - 1; + mono_class_set_first_field_idx (klass, first_field_idx); + int first_method_idx = cols [MONO_TYPEDEF_METHOD_LIST] - 1; + mono_class_set_first_method_idx (klass, first_method_idx); + + if (tt->rows > tidx){ + mono_metadata_decode_row (tt, tidx, cols_next, MONO_TYPEDEF_SIZE); + field_last = cols_next [MONO_TYPEDEF_FIELD_LIST] - 1; + method_last = cols_next [MONO_TYPEDEF_METHOD_LIST] - 1; + } else { + field_last = image->tables [MONO_TABLE_FIELD].rows; + method_last = image->tables [MONO_TABLE_METHOD].rows; + } + + if (cols [MONO_TYPEDEF_FIELD_LIST] && + cols [MONO_TYPEDEF_FIELD_LIST] <= image->tables [MONO_TABLE_FIELD].rows) + mono_class_set_field_count (klass, field_last - first_field_idx); + if (cols [MONO_TYPEDEF_METHOD_LIST] <= image->tables [MONO_TABLE_METHOD].rows) + mono_class_set_method_count (klass, method_last - first_method_idx); + + /* reserve space to store vector pointer in arrays */ + if (mono_is_corlib_image (image) && !strcmp (nspace, "System") && !strcmp (name, "Array")) { + klass->instance_size += 2 * sizeof (gpointer); + g_assert (mono_class_get_field_count (klass) == 0); + } + + if (klass->enumtype) { + MonoType *enum_basetype = mono_class_find_enum_basetype (klass, error); + if (!enum_basetype) { + /*set it to a default value as the whole runtime can't handle this to be null*/ + klass->cast_class = klass->element_class = mono_defaults.int32_class; + mono_class_set_type_load_failure (klass, "%s", mono_error_get_message (error)); + mono_loader_unlock (); + MONO_PROFILER_RAISE (class_failed, (klass)); + return NULL; + } + klass->cast_class = klass->element_class = mono_class_from_mono_type (enum_basetype); + } + + /* + * If we're a generic type definition, load the constraints. + * We must do this after the class has been constructed to make certain recursive scenarios + * work. + */ + if (mono_class_is_gtd (klass) && !mono_metadata_load_generic_param_constraints_checked (image, type_token, mono_class_get_generic_container (klass), error)) { + mono_class_set_type_load_failure (klass, "Could not load generic parameter constrains due to %s", mono_error_get_message (error)); + mono_loader_unlock (); + MONO_PROFILER_RAISE (class_failed, (klass)); + return NULL; + } + + if (klass->image->assembly_name && !strcmp (klass->image->assembly_name, "Mono.Simd") && !strcmp (nspace, "Mono.Simd")) { + 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")) { + /* 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; + } + + mono_loader_unlock (); + + MONO_PROFILER_RAISE (class_loaded, (klass)); + + return klass; + +parent_failure: + if (mono_class_is_gtd (klass)) + disable_gclass_recording (discard_gclass_due_to_failure, klass); + + mono_class_setup_mono_type (klass); + mono_loader_unlock (); + MONO_PROFILER_RAISE (class_failed, (klass)); + return NULL; +} + +/** Is klass a Nullable ginst? */ +gboolean +mono_class_is_nullable (MonoClass *klass) +{ + MonoGenericClass *gklass = mono_class_try_get_generic_class (klass); + return gklass && gklass->container_class == mono_defaults.generic_nullable_class; +} + + +/** if klass is T? return T */ +MonoClass* +mono_class_get_nullable_param (MonoClass *klass) +{ + g_assert (mono_class_is_nullable (klass)); + return mono_class_from_mono_type (mono_class_get_generic_class (klass)->context.class_inst->type_argv [0]); +} + +static void +mono_generic_class_setup_parent (MonoClass *klass, MonoClass *gtd) +{ + if (gtd->parent) { + MonoError error; + MonoGenericClass *gclass = mono_class_get_generic_class (klass); + + klass->parent = mono_class_inflate_generic_class_checked (gtd->parent, mono_generic_class_get_context (gclass), &error); + if (!mono_error_ok (&error)) { + /*Set parent to something safe as the runtime doesn't handle well this kind of failure.*/ + klass->parent = mono_defaults.object_class; + mono_class_set_type_load_failure (klass, "Parent is a generic type instantiation that failed due to: %s", mono_error_get_message (&error)); + mono_error_cleanup (&error); + } + } + mono_loader_lock (); + if (klass->parent) + mono_class_setup_parent (klass, klass->parent); + + if (klass->enumtype) { + klass->cast_class = gtd->cast_class; + klass->element_class = gtd->element_class; + } + mono_loader_unlock (); +} + +gboolean +mono_type_is_primitive (MonoType *type) +{ + return (type->type >= MONO_TYPE_BOOLEAN && type->type <= MONO_TYPE_R8) || + type-> type == MONO_TYPE_I || type->type == MONO_TYPE_U; +} + +/* + * Create the `MonoClass' for an instantiation of a generic type. + * We only do this if we actually need it. + */ +MonoClass* +mono_generic_class_get_class (MonoGenericClass *gclass) +{ + MonoClass *klass, *gklass; + + if (gclass->cached_class) + return gclass->cached_class; + + klass = (MonoClass *)mono_image_set_alloc0 (gclass->owner, sizeof (MonoClassGenericInst)); + + gklass = gclass->container_class; + + if (gklass->nested_in) { + /* The nested_in type should not be inflated since it's possible to produce a nested type with less generic arguments*/ + klass->nested_in = gklass->nested_in; + } + + klass->name = gklass->name; + klass->name_space = gklass->name_space; + + klass->image = gklass->image; + klass->type_token = gklass->type_token; + + klass->class_kind = MONO_CLASS_GINST; + //FIXME add setter + ((MonoClassGenericInst*)klass)->generic_class = gclass; + + klass->byval_arg.type = MONO_TYPE_GENERICINST; + klass->this_arg.type = klass->byval_arg.type; + klass->this_arg.data.generic_class = klass->byval_arg.data.generic_class = gclass; + klass->this_arg.byref = TRUE; + klass->enumtype = gklass->enumtype; + klass->valuetype = gklass->valuetype; + + + if (gklass->image->assembly_name && !strcmp (gklass->image->assembly_name, "System.Numerics.Vectors") && !strcmp (gklass->name_space, "System.Numerics") && !strcmp (gklass->name, "Vector`1")) { + g_assert (gclass->context.class_inst); + g_assert (gclass->context.class_inst->type_argc > 0); + if (mono_type_is_primitive (gclass->context.class_inst->type_argv [0])) + klass->simd_type = 1; + } + klass->is_array_special_interface = gklass->is_array_special_interface; + + klass->cast_class = klass->element_class = klass; + + if (gclass->is_dynamic) { + /* + * We don't need to do any init workf with unbaked typebuilders. Generic instances created at this point will be later unregistered and/or fixed. + * This is to avoid work that would probably give wrong results as fields change as we build the TypeBuilder. + * See remove_instantiations_of_and_ensure_contents in reflection.c and its usage in reflection.c to understand the fixup stage of SRE banking. + */ + if (!gklass->wastypebuilder) + klass->inited = 1; + + if (klass->enumtype) { + /* + * For enums, gklass->fields might not been set, but instance_size etc. is + * already set in mono_reflection_create_internal_class (). For non-enums, + * these will be computed normally in mono_class_layout_fields (). + */ + klass->instance_size = gklass->instance_size; + klass->sizes.class_size = gklass->sizes.class_size; + klass->size_inited = 1; + } + } + + mono_loader_lock (); + + if (gclass->cached_class) { + mono_loader_unlock (); + return gclass->cached_class; + } + + if (record_gclass_instantiation > 0) + gclass_recorded_list = g_slist_append (gclass_recorded_list, klass); + + if (mono_class_is_nullable (klass)) + klass->cast_class = klass->element_class = mono_class_get_nullable_param (klass); + + MONO_PROFILER_RAISE (class_loading, (klass)); + + mono_generic_class_setup_parent (klass, gklass); + + if (gclass->is_dynamic) + mono_class_setup_supertypes (klass); + + mono_memory_barrier (); + gclass->cached_class = klass; + + MONO_PROFILER_RAISE (class_loaded, (klass)); + + ++class_ginst_count; + inflated_classes_size += sizeof (MonoClassGenericInst); + + mono_loader_unlock (); + + return klass; +} + +static MonoImage * +get_image_for_container (MonoGenericContainer *container) +{ + MonoImage *result; + if (container->is_anonymous) { + result = container->owner.image; + } else { + MonoClass *klass; + if (container->is_method) { + MonoMethod *method = container->owner.method; + g_assert_checked (method); + klass = method->klass; + } else { + klass = container->owner.klass; + } + g_assert_checked (klass); + result = klass->image; + } + g_assert (result); + return result; +} + +MonoImage * +get_image_for_generic_param (MonoGenericParam *param) +{ + MonoGenericContainer *container = mono_generic_param_owner (param); + g_assert_checked (container); + return get_image_for_container (container); +} + +// Make a string in the designated image consisting of a single integer. +#define INT_STRING_SIZE 16 +char * +make_generic_name_string (MonoImage *image, int num) +{ + char *name = (char *)mono_image_alloc0 (image, INT_STRING_SIZE); + g_snprintf (name, INT_STRING_SIZE, "%d", num); + return name; +} + +// This is called by mono_class_from_generic_parameter_internal when a new class must be created. +// pinfo is derived from param by the caller for us. +static MonoClass* +make_generic_param_class (MonoGenericParam *param, MonoGenericParamInfo *pinfo) +{ + MonoClass *klass, **ptr; + int count, pos, i; + MonoGenericContainer *container = mono_generic_param_owner (param); + g_assert_checked (container); + + MonoImage *image = get_image_for_container (container); + gboolean is_mvar = container->is_method; + gboolean is_anonymous = container->is_anonymous; + + klass = (MonoClass *)mono_image_alloc0 (image, sizeof (MonoClassGenericParam)); + klass->class_kind = MONO_CLASS_GPARAM; + UnlockedAdd (&classes_size, sizeof (MonoClassGenericParam)); + UnlockedIncrement (&class_gparam_count); + + if (pinfo) { + CHECKED_METADATA_WRITE_PTR_EXEMPT ( klass->name , pinfo->name ); + } else { + int n = mono_generic_param_num (param); + CHECKED_METADATA_WRITE_PTR_LOCAL ( klass->name , make_generic_name_string (image, n) ); + } + + if (is_anonymous) { + CHECKED_METADATA_WRITE_PTR_EXEMPT ( klass->name_space , "" ); + } else if (is_mvar) { + MonoMethod *omethod = container->owner.method; + CHECKED_METADATA_WRITE_PTR_EXEMPT ( klass->name_space , (omethod && omethod->klass) ? omethod->klass->name_space : "" ); + } else { + MonoClass *oklass = container->owner.klass; + CHECKED_METADATA_WRITE_PTR_EXEMPT ( klass->name_space , oklass ? oklass->name_space : "" ); + } + + MONO_PROFILER_RAISE (class_loading, (klass)); + + // Count non-NULL items in pinfo->constraints + count = 0; + if (pinfo) + for (ptr = pinfo->constraints; ptr && *ptr; ptr++, count++) + ; + + pos = 0; + if ((count > 0) && !MONO_CLASS_IS_INTERFACE (pinfo->constraints [0])) { + CHECKED_METADATA_WRITE_PTR ( klass->parent , pinfo->constraints [0] ); + pos++; + } else if (pinfo && pinfo->flags & GENERIC_PARAMETER_ATTRIBUTE_VALUE_TYPE_CONSTRAINT) { + CHECKED_METADATA_WRITE_PTR ( klass->parent , mono_class_load_from_name (mono_defaults.corlib, "System", "ValueType") ); + } else { + CHECKED_METADATA_WRITE_PTR ( klass->parent , mono_defaults.object_class ); + } + + if (count - pos > 0) { + klass->interface_count = count - pos; + CHECKED_METADATA_WRITE_PTR_LOCAL ( klass->interfaces , (MonoClass **)mono_image_alloc0 (image, sizeof (MonoClass *) * (count - pos)) ); + klass->interfaces_inited = TRUE; + for (i = pos; i < count; i++) + CHECKED_METADATA_WRITE_PTR ( klass->interfaces [i - pos] , pinfo->constraints [i] ); + } + + CHECKED_METADATA_WRITE_PTR_EXEMPT ( klass->image , image ); + + klass->inited = TRUE; + CHECKED_METADATA_WRITE_PTR_LOCAL ( klass->cast_class , klass ); + CHECKED_METADATA_WRITE_PTR_LOCAL ( klass->element_class , klass ); + + klass->byval_arg.type = is_mvar ? MONO_TYPE_MVAR : MONO_TYPE_VAR; + klass->this_arg.type = klass->byval_arg.type; + CHECKED_METADATA_WRITE_PTR ( klass->this_arg.data.generic_param , param ); + CHECKED_METADATA_WRITE_PTR ( klass->byval_arg.data.generic_param , param ); + klass->this_arg.byref = TRUE; + + /* We don't use type_token for VAR since only classes can use it (not arrays, pointer, VARs, etc) */ + klass->sizes.generic_param_token = pinfo ? pinfo->token : 0; + + /*Init these fields to sane values*/ + klass->min_align = 1; + /* + * This makes sure the the value size of this class is equal to the size of the types the gparam is + * constrained to, the JIT depends on this. + */ + klass->instance_size = sizeof (MonoObject) + mono_type_stack_size_internal (&klass->byval_arg, NULL, TRUE); + mono_memory_barrier (); + klass->size_inited = 1; + + mono_class_setup_supertypes (klass); + + if (count - pos > 0) { + mono_class_setup_vtable (klass->parent); + if (mono_class_has_failure (klass->parent)) + mono_class_set_type_load_failure (klass, "Failed to setup parent interfaces"); + else + setup_interface_offsets (klass, klass->parent->vtable_size, TRUE); + } + + return klass; +} + +#define FAST_CACHE_SIZE 16 + +/* + * get_anon_gparam_class and set_anon_gparam_class are helpers for mono_class_from_generic_parameter_internal. + * The latter will sometimes create MonoClasses for anonymous generic params. To prevent this being wasteful, + * we cache the MonoClasses. + * FIXME: It would be better to instead cache anonymous MonoGenericParams, and allow anonymous params to point directly to classes using the pklass field. + * LOCKING: Takes the image lock depending on @take_lock. + */ +static MonoClass * +get_anon_gparam_class (MonoGenericParam *param, gboolean take_lock) +{ + int n = mono_generic_param_num (param); + MonoImage *image = get_image_for_generic_param (param); + gboolean is_mvar = mono_generic_param_owner (param)->is_method; + MonoClass *klass = NULL; + GHashTable *ht; + + g_assert (image); + + // For params with a small num and no constraints, we use a "fast" cache which does simple num lookup in an array. + // For high numbers or constraints we have to use pointer hashes. + if (param->gshared_constraint) { + ht = is_mvar ? image->mvar_cache_constrained : image->var_cache_constrained; + if (ht) { + if (take_lock) + mono_image_lock (image); + klass = (MonoClass *)g_hash_table_lookup (ht, param); + if (take_lock) + mono_image_unlock (image); + } + return klass; + } + + if (n < FAST_CACHE_SIZE) { + if (is_mvar) + return image->mvar_cache_fast ? image->mvar_cache_fast [n] : NULL; + else + return image->var_cache_fast ? image->var_cache_fast [n] : NULL; + } else { + ht = is_mvar ? image->mvar_cache_slow : image->var_cache_slow; + if (ht) { + if (take_lock) + mono_image_lock (image); + klass = (MonoClass *)g_hash_table_lookup (ht, GINT_TO_POINTER (n)); + if (take_lock) + mono_image_unlock (image); + } + return klass; + } +} + +/* + * LOCKING: Image lock (param->image) must be held + */ +static void +set_anon_gparam_class (MonoGenericParam *param, MonoClass *klass) +{ + int n = mono_generic_param_num (param); + MonoImage *image = get_image_for_generic_param (param); + gboolean is_mvar = mono_generic_param_owner (param)->is_method; + + g_assert (image); + + if (param->gshared_constraint) { + GHashTable *ht = is_mvar ? image->mvar_cache_constrained : image->var_cache_constrained; + if (!ht) { + ht = g_hash_table_new ((GHashFunc)mono_metadata_generic_param_hash, (GEqualFunc)mono_metadata_generic_param_equal); + mono_memory_barrier (); + if (is_mvar) + image->mvar_cache_constrained = ht; + else + image->var_cache_constrained = ht; + } + g_hash_table_insert (ht, param, klass); + } else if (n < FAST_CACHE_SIZE) { + if (is_mvar) { + /* Requires locking to avoid droping an already published class */ + if (!image->mvar_cache_fast) + image->mvar_cache_fast = (MonoClass **)mono_image_alloc0 (image, sizeof (MonoClass*) * FAST_CACHE_SIZE); + image->mvar_cache_fast [n] = klass; + } else { + if (!image->var_cache_fast) + image->var_cache_fast = (MonoClass **)mono_image_alloc0 (image, sizeof (MonoClass*) * FAST_CACHE_SIZE); + image->var_cache_fast [n] = klass; + } + } else { + GHashTable *ht = is_mvar ? image->mvar_cache_slow : image->var_cache_slow; + if (!ht) { + ht = is_mvar ? image->mvar_cache_slow : image->var_cache_slow; + if (!ht) { + ht = g_hash_table_new (NULL, NULL); + mono_memory_barrier (); + if (is_mvar) + image->mvar_cache_slow = ht; + else + image->var_cache_slow = ht; + } + } + g_hash_table_insert (ht, GINT_TO_POINTER (n), klass); + } +} + +/* + * LOCKING: Acquires the image lock (@image). + */ +MonoClass * +mono_class_from_generic_parameter_internal (MonoGenericParam *param) +{ + MonoImage *image = get_image_for_generic_param (param); + MonoGenericParamInfo *pinfo = mono_generic_param_info (param); + MonoClass *klass, *klass2; + + // If a klass already exists for this object and is cached, return it. + if (pinfo) // Non-anonymous + klass = pinfo->pklass; + else // Anonymous + klass = get_anon_gparam_class (param, TRUE); + + if (klass) + return klass; + + // Create a new klass + klass = make_generic_param_class (param, pinfo); + + // Now we need to cache the klass we created. + // But since we wait to grab the lock until after creating the klass, we need to check to make sure + // another thread did not get in and cache a klass ahead of us. In that case, return their klass + // and allow our newly-created klass object to just leak. + mono_memory_barrier (); + + mono_image_lock (image); + + // Here "klass2" refers to the klass potentially created by the other thread. + if (pinfo) // Repeat check from above + klass2 = pinfo->pklass; + else + klass2 = get_anon_gparam_class (param, FALSE); + + if (klass2) { + klass = klass2; + } else { + // Cache here + if (pinfo) + pinfo->pklass = klass; + else + set_anon_gparam_class (param, klass); + } + mono_image_unlock (image); + + /* FIXME: Should this go inside 'make_generic_param_klass'? */ + if (klass2) + MONO_PROFILER_RAISE (class_failed, (klass2)); + else + MONO_PROFILER_RAISE (class_loaded, (klass)); + + return klass; +} + +/** + * mono_class_from_generic_parameter: + * \param param Parameter to find/construct a class for. + * \param arg2 Is ignored. + * \param arg3 Is ignored. + */ +MonoClass * +mono_class_from_generic_parameter (MonoGenericParam *param, MonoImage *arg2 G_GNUC_UNUSED, gboolean arg3 G_GNUC_UNUSED) +{ + return mono_class_from_generic_parameter_internal (param); +} + +/** + * mono_ptr_class_get: + */ +MonoClass * +mono_ptr_class_get (MonoType *type) +{ + MonoClass *result; + MonoClass *el_class; + MonoImage *image; + char *name; + MonoImageSet* image_set; + + el_class = mono_class_from_mono_type (type); + image = el_class->image; + image_set = class_kind_may_contain_generic_instances (el_class->class_kind) ? mono_metadata_get_image_set_for_class (el_class) : NULL; + + if (image_set) { + mono_image_set_lock (image_set); + if (image_set->ptr_cache) { + if ((result = (MonoClass *)g_hash_table_lookup (image_set->ptr_cache, el_class))) { + mono_image_set_unlock (image_set); + return result; + } + } + mono_image_set_unlock (image_set); + } else { + mono_image_lock (image); + if (image->ptr_cache) { + if ((result = (MonoClass *)g_hash_table_lookup (image->ptr_cache, el_class))) { + mono_image_unlock (image); + return result; + } + } + mono_image_unlock (image); + } + + result = image_set ? (MonoClass *)mono_image_set_alloc0 (image_set, sizeof (MonoClassPointer)) : (MonoClass *)mono_image_alloc0 (image, sizeof (MonoClassPointer)); + + UnlockedAdd (&classes_size, sizeof (MonoClassPointer)); + ++class_pointer_count; + + result->parent = NULL; /* no parent for PTR types */ + result->name_space = el_class->name_space; + name = g_strdup_printf ("%s*", el_class->name); + result->name = image_set ? mono_image_set_strdup (image_set, name) : mono_image_strdup (image, name); + result->class_kind = MONO_CLASS_POINTER; + g_free (name); + + MONO_PROFILER_RAISE (class_loading, (result)); + + result->image = el_class->image; + result->inited = TRUE; + result->instance_size = sizeof (MonoObject) + sizeof (gpointer); + result->min_align = sizeof (gpointer); + result->cast_class = result->element_class = el_class; + result->blittable = TRUE; + + result->byval_arg.type = MONO_TYPE_PTR; + result->this_arg.type = result->byval_arg.type; + result->this_arg.data.type = result->byval_arg.data.type = &result->element_class->byval_arg; + result->this_arg.byref = TRUE; + + mono_class_setup_supertypes (result); + + if (image_set) { + mono_image_set_lock (image_set); + if (image_set->ptr_cache) { + MonoClass *result2; + if ((result2 = (MonoClass *)g_hash_table_lookup (image_set->ptr_cache, el_class))) { + mono_image_set_unlock (image_set); + MONO_PROFILER_RAISE (class_failed, (result)); + return result2; + } + } + else { + image_set->ptr_cache = g_hash_table_new (mono_aligned_addr_hash, NULL); + } + g_hash_table_insert (image_set->ptr_cache, el_class, result); + mono_image_set_unlock (image_set); + } else { + mono_image_lock (image); + if (image->ptr_cache) { + MonoClass *result2; + if ((result2 = (MonoClass *)g_hash_table_lookup (image->ptr_cache, el_class))) { + mono_image_unlock (image); + MONO_PROFILER_RAISE (class_failed, (result)); + return result2; + } + } else { + image->ptr_cache = g_hash_table_new (mono_aligned_addr_hash, NULL); + } + g_hash_table_insert (image->ptr_cache, el_class, result); + mono_image_unlock (image); + } + + MONO_PROFILER_RAISE (class_loaded, (result)); + + return result; +} + +static MonoClass * +mono_fnptr_class_get (MonoMethodSignature *sig) +{ + MonoClass *result, *cached; + static GHashTable *ptr_hash = NULL; + + /* FIXME: These should be allocate from a mempool as well, but which one ? */ + + mono_loader_lock (); + if (!ptr_hash) + ptr_hash = g_hash_table_new (mono_aligned_addr_hash, NULL); + cached = (MonoClass *)g_hash_table_lookup (ptr_hash, sig); + mono_loader_unlock (); + if (cached) + return cached; + + result = g_new0 (MonoClass, 1); + + result->parent = NULL; /* no parent for PTR types */ + result->name_space = "System"; + result->name = "MonoFNPtrFakeClass"; + result->class_kind = MONO_CLASS_POINTER; + + result->image = mono_defaults.corlib; /* need to fix... */ + result->instance_size = sizeof (MonoObject) + sizeof (gpointer); + result->min_align = sizeof (gpointer); + result->cast_class = result->element_class = result; + result->byval_arg.type = MONO_TYPE_FNPTR; + result->this_arg.type = result->byval_arg.type; + result->this_arg.data.method = result->byval_arg.data.method = sig; + result->this_arg.byref = TRUE; + result->blittable = TRUE; + result->inited = TRUE; + + mono_class_setup_supertypes (result); + + mono_loader_lock (); + + cached = (MonoClass *)g_hash_table_lookup (ptr_hash, sig); + if (cached) { + g_free (result); + mono_loader_unlock (); + return cached; + } + + MONO_PROFILER_RAISE (class_loading, (result)); + + UnlockedAdd (&classes_size, sizeof (MonoClassPointer)); + ++class_pointer_count; + + g_hash_table_insert (ptr_hash, sig, result); + + mono_loader_unlock (); + + MONO_PROFILER_RAISE (class_loaded, (result)); + + return result; +} + +/** + * mono_class_from_mono_type: + * \param type describes the type to return + * \returns a \c MonoClass for the specified \c MonoType, the value is never NULL. + */ +MonoClass * +mono_class_from_mono_type (MonoType *type) +{ + switch (type->type) { + case MONO_TYPE_OBJECT: + return type->data.klass? type->data.klass: mono_defaults.object_class; + case MONO_TYPE_VOID: + return type->data.klass? type->data.klass: mono_defaults.void_class; + case MONO_TYPE_BOOLEAN: + return type->data.klass? type->data.klass: mono_defaults.boolean_class; + case MONO_TYPE_CHAR: + return type->data.klass? type->data.klass: mono_defaults.char_class; + case MONO_TYPE_I1: + return type->data.klass? type->data.klass: mono_defaults.sbyte_class; + case MONO_TYPE_U1: + return type->data.klass? type->data.klass: mono_defaults.byte_class; + case MONO_TYPE_I2: + return type->data.klass? type->data.klass: mono_defaults.int16_class; + case MONO_TYPE_U2: + return type->data.klass? type->data.klass: mono_defaults.uint16_class; + case MONO_TYPE_I4: + return type->data.klass? type->data.klass: mono_defaults.int32_class; + case MONO_TYPE_U4: + return type->data.klass? type->data.klass: mono_defaults.uint32_class; + case MONO_TYPE_I: + return type->data.klass? type->data.klass: mono_defaults.int_class; + case MONO_TYPE_U: + return type->data.klass? type->data.klass: mono_defaults.uint_class; + case MONO_TYPE_I8: + return type->data.klass? type->data.klass: mono_defaults.int64_class; + case MONO_TYPE_U8: + return type->data.klass? type->data.klass: mono_defaults.uint64_class; + case MONO_TYPE_R4: + return type->data.klass? type->data.klass: mono_defaults.single_class; + case MONO_TYPE_R8: + return type->data.klass? type->data.klass: mono_defaults.double_class; + case MONO_TYPE_STRING: + return type->data.klass? type->data.klass: mono_defaults.string_class; + case MONO_TYPE_TYPEDBYREF: + return type->data.klass? type->data.klass: mono_defaults.typed_reference_class; + case MONO_TYPE_ARRAY: + return mono_bounded_array_class_get (type->data.array->eklass, type->data.array->rank, TRUE); + case MONO_TYPE_PTR: + return mono_ptr_class_get (type->data.type); + case MONO_TYPE_FNPTR: + return mono_fnptr_class_get (type->data.method); + case MONO_TYPE_SZARRAY: + return mono_array_class_get (type->data.klass, 1); + case MONO_TYPE_CLASS: + case MONO_TYPE_VALUETYPE: + return type->data.klass; + case MONO_TYPE_GENERICINST: + return mono_generic_class_get_class (type->data.generic_class); + case MONO_TYPE_MVAR: + case MONO_TYPE_VAR: + return mono_class_from_generic_parameter_internal (type->data.generic_param); + default: + g_warning ("mono_class_from_mono_type: implement me 0x%02x\n", type->type); + g_assert_not_reached (); + } + + // Yes, this returns NULL, even if it is documented as not doing so, but there + // is no way for the code to make it this far, due to the assert above. + return NULL; +} + +/** + * mono_type_retrieve_from_typespec + * \param image context where the image is created + * \param type_spec typespec token + * \param context the generic context used to evaluate generic instantiations in + */ +static MonoType * +mono_type_retrieve_from_typespec (MonoImage *image, guint32 type_spec, MonoGenericContext *context, gboolean *did_inflate, MonoError *error) +{ + MonoType *t = mono_type_create_from_typespec_checked (image, type_spec, error); + + *did_inflate = FALSE; + + if (!t) + return NULL; + + if (context && (context->class_inst || context->method_inst)) { + MonoType *inflated = inflate_generic_type (NULL, t, context, error); + + if (!mono_error_ok (error)) { + return NULL; + } + + if (inflated) { + t = inflated; + *did_inflate = TRUE; + } + } + return t; +} + +/** + * mono_class_create_from_typespec + * \param image context where the image is created + * \param type_spec typespec token + * \param context the generic context used to evaluate generic instantiations in + */ +static MonoClass * +mono_class_create_from_typespec (MonoImage *image, guint32 type_spec, MonoGenericContext *context, MonoError *error) +{ + MonoClass *ret; + gboolean inflated = FALSE; + MonoType *t = mono_type_retrieve_from_typespec (image, type_spec, context, &inflated, error); + return_val_if_nok (error, NULL); + ret = mono_class_from_mono_type (t); + if (inflated) + mono_metadata_free_type (t); + return ret; +} + +/** + * mono_bounded_array_class_get: + * \param element_class element class + * \param rank the dimension of the array class + * \param bounded whenever the array has non-zero bounds + * \returns A class object describing the array with element type \p element_type and + * dimension \p rank. + */ +MonoClass * +mono_bounded_array_class_get (MonoClass *eclass, guint32 rank, gboolean bounded) +{ + MonoImage *image; + MonoClass *klass, *cached, *k; + MonoClass *parent = NULL; + GSList *list, *rootlist = NULL; + int nsize; + char *name; + MonoImageSet* image_set; + + g_assert (rank <= 255); + + if (rank > 1) + /* bounded only matters for one-dimensional arrays */ + bounded = FALSE; + + image = eclass->image; + image_set = class_kind_may_contain_generic_instances (eclass->class_kind) ? mono_metadata_get_image_set_for_class (eclass) : NULL; + + /* Check cache */ + cached = NULL; + if (rank == 1 && !bounded) { + if (image_set) { + mono_image_set_lock (image_set); + cached = (MonoClass *)g_hash_table_lookup (image_set->szarray_cache, eclass); + mono_image_set_unlock (image_set); + } else { + /* + * This case is very frequent not just during compilation because of calls + * from mono_class_from_mono_type (), mono_array_new (), + * Array:CreateInstance (), etc, so use a separate cache + a separate lock. + */ + mono_os_mutex_lock (&image->szarray_cache_lock); + if (!image->szarray_cache) + image->szarray_cache = g_hash_table_new (mono_aligned_addr_hash, NULL); + cached = (MonoClass *)g_hash_table_lookup (image->szarray_cache, eclass); + mono_os_mutex_unlock (&image->szarray_cache_lock); + } + } else { + if (image_set) { + mono_image_set_lock (image_set); + rootlist = (GSList *)g_hash_table_lookup (image_set->array_cache, eclass); + for (list = rootlist; list; list = list->next) { + k = (MonoClass *)list->data; + if ((k->rank == rank) && (k->byval_arg.type == (((rank > 1) || bounded) ? MONO_TYPE_ARRAY : MONO_TYPE_SZARRAY))) { + cached = k; + break; + } + } + mono_image_set_unlock (image_set); + } else { + mono_loader_lock (); + if (!image->array_cache) + image->array_cache = g_hash_table_new (mono_aligned_addr_hash, NULL); + rootlist = (GSList *)g_hash_table_lookup (image->array_cache, eclass); + for (list = rootlist; list; list = list->next) { + k = (MonoClass *)list->data; + if ((k->rank == rank) && (k->byval_arg.type == (((rank > 1) || bounded) ? MONO_TYPE_ARRAY : MONO_TYPE_SZARRAY))) { + cached = k; + break; + } + } + mono_loader_unlock (); + } + } + if (cached) + return cached; + + parent = mono_defaults.array_class; + if (!parent->inited) + mono_class_init (parent); + + klass = image_set ? (MonoClass *)mono_image_set_alloc0 (image_set, sizeof (MonoClassArray)) : (MonoClass *)mono_image_alloc0 (image, sizeof (MonoClassArray)); + + klass->image = image; + klass->name_space = eclass->name_space; + klass->class_kind = MONO_CLASS_ARRAY; + + nsize = strlen (eclass->name); + name = (char *)g_malloc (nsize + 2 + rank + 1); + memcpy (name, eclass->name, nsize); + name [nsize] = '['; + if (rank > 1) + memset (name + nsize + 1, ',', rank - 1); + if (bounded) + name [nsize + rank] = '*'; + name [nsize + rank + bounded] = ']'; + name [nsize + rank + bounded + 1] = 0; + klass->name = image_set ? mono_image_set_strdup (image_set, name) : mono_image_strdup (image, name); + g_free (name); + + klass->type_token = 0; + klass->parent = parent; + klass->instance_size = mono_class_instance_size (klass->parent); + + if (eclass->byval_arg.type == MONO_TYPE_TYPEDBYREF) { + /*Arrays of those two types are invalid.*/ + MonoError prepared_error; + error_init (&prepared_error); + mono_error_set_invalid_program (&prepared_error, "Arrays of System.TypedReference types are invalid."); + mono_class_set_failure (klass, mono_error_box (&prepared_error, klass->image)); + mono_error_cleanup (&prepared_error); + } else if (eclass->enumtype && !mono_class_enum_basetype (eclass)) { + guint32 ref_info_handle = mono_class_get_ref_info_handle (eclass); + if (!ref_info_handle || eclass->wastypebuilder) { + g_warning ("Only incomplete TypeBuilder objects are allowed to be an enum without base_type"); + g_assert (ref_info_handle && !eclass->wastypebuilder); + } + /* element_size -1 is ok as this is not an instantitable type*/ + klass->sizes.element_size = -1; + } else + klass->sizes.element_size = -1; + + mono_class_setup_supertypes (klass); + + if (mono_class_is_ginst (eclass)) + mono_class_init (eclass); + if (!eclass->size_inited) + mono_class_setup_fields (eclass); + mono_class_set_type_load_failure_causedby_class (klass, eclass, "Could not load array element type"); + /*FIXME we fail the array type, but we have to let other fields be set.*/ + + klass->has_references = MONO_TYPE_IS_REFERENCE (&eclass->byval_arg) || eclass->has_references? TRUE: FALSE; + + klass->rank = rank; + + if (eclass->enumtype) + klass->cast_class = eclass->element_class; + else + klass->cast_class = eclass; + + switch (klass->cast_class->byval_arg.type) { + case MONO_TYPE_I1: + klass->cast_class = mono_defaults.byte_class; + break; + case MONO_TYPE_U2: + klass->cast_class = mono_defaults.int16_class; + break; + case MONO_TYPE_U4: +#if SIZEOF_VOID_P == 4 + case MONO_TYPE_I: + case MONO_TYPE_U: +#endif + klass->cast_class = mono_defaults.int32_class; + break; + case MONO_TYPE_U8: +#if SIZEOF_VOID_P == 8 + case MONO_TYPE_I: + case MONO_TYPE_U: +#endif + klass->cast_class = mono_defaults.int64_class; + break; + default: + break; + } + + klass->element_class = eclass; + + if ((rank > 1) || bounded) { + MonoArrayType *at = image_set ? (MonoArrayType *)mono_image_set_alloc0 (image_set, sizeof (MonoArrayType)) : (MonoArrayType *)mono_image_alloc0 (image, sizeof (MonoArrayType)); + klass->byval_arg.type = MONO_TYPE_ARRAY; + klass->byval_arg.data.array = at; + at->eklass = eclass; + at->rank = rank; + /* FIXME: complete.... */ + } else { + klass->byval_arg.type = MONO_TYPE_SZARRAY; + klass->byval_arg.data.klass = eclass; + } + klass->this_arg = klass->byval_arg; + klass->this_arg.byref = 1; + + if (rank > 32) { + MonoError prepared_error; + error_init (&prepared_error); + name = mono_type_get_full_name (klass); + mono_error_set_type_load_class (&prepared_error, klass, "%s has too many dimensions.", name); + mono_class_set_failure (klass, mono_error_box (&prepared_error, klass->image)); + mono_error_cleanup (&prepared_error); + g_free (name); + } + + mono_loader_lock (); + + /* Check cache again */ + cached = NULL; + if (rank == 1 && !bounded) { + if (image_set) { + mono_image_set_lock (image_set); + cached = (MonoClass *)g_hash_table_lookup (image_set->szarray_cache, eclass); + mono_image_set_unlock (image_set); + } else { + mono_os_mutex_lock (&image->szarray_cache_lock); + cached = (MonoClass *)g_hash_table_lookup (image->szarray_cache, eclass); + mono_os_mutex_unlock (&image->szarray_cache_lock); + } + } else { + if (image_set) { + mono_image_set_lock (image_set); + rootlist = (GSList *)g_hash_table_lookup (image_set->array_cache, eclass); + for (list = rootlist; list; list = list->next) { + k = (MonoClass *)list->data; + if ((k->rank == rank) && (k->byval_arg.type == (((rank > 1) || bounded) ? MONO_TYPE_ARRAY : MONO_TYPE_SZARRAY))) { + cached = k; + break; + } + } + mono_image_set_unlock (image_set); + } else { + rootlist = (GSList *)g_hash_table_lookup (image->array_cache, eclass); + for (list = rootlist; list; list = list->next) { + k = (MonoClass *)list->data; + if ((k->rank == rank) && (k->byval_arg.type == (((rank > 1) || bounded) ? MONO_TYPE_ARRAY : MONO_TYPE_SZARRAY))) { + cached = k; + break; + } + } + } + } + if (cached) { + mono_loader_unlock (); + return cached; + } + + MONO_PROFILER_RAISE (class_loading, (klass)); + + UnlockedAdd (&classes_size, sizeof (MonoClassArray)); + ++class_array_count; + + if (rank == 1 && !bounded) { + if (image_set) { + mono_image_set_lock (image_set); + g_hash_table_insert (image_set->szarray_cache, eclass, klass); + mono_image_set_unlock (image_set); + } else { + mono_os_mutex_lock (&image->szarray_cache_lock); + g_hash_table_insert (image->szarray_cache, eclass, klass); + mono_os_mutex_unlock (&image->szarray_cache_lock); + } + } else { + if (image_set) { + mono_image_set_lock (image_set); + list = g_slist_append (rootlist, klass); + g_hash_table_insert (image_set->array_cache, eclass, list); + mono_image_set_unlock (image_set); + } else { + list = g_slist_append (rootlist, klass); + g_hash_table_insert (image->array_cache, eclass, list); + } + } + + mono_loader_unlock (); + + MONO_PROFILER_RAISE (class_loaded, (klass)); + + return klass; +} + +/** + * mono_array_class_get: + * \param element_class element class + * \param rank the dimension of the array class + * \returns A class object describing the array with element type \p element_type and + * dimension \p rank. + */ +MonoClass * +mono_array_class_get (MonoClass *eclass, guint32 rank) +{ + return mono_bounded_array_class_get (eclass, rank, FALSE); +} + +/** + * mono_class_instance_size: + * \param klass a class + * + * Use to get the size of a class in bytes. + * + * \returns The size of an object instance + */ +gint32 +mono_class_instance_size (MonoClass *klass) +{ + if (!klass->size_inited) + mono_class_init (klass); + + return klass->instance_size; +} + +/** + * mono_class_min_align: + * \param klass a class + * + * Use to get the computed minimum alignment requirements for the specified class. + * + * Returns: minimum alignment requirements + */ +gint32 +mono_class_min_align (MonoClass *klass) +{ + if (!klass->size_inited) + mono_class_init (klass); + + return klass->min_align; +} + +/** + * mono_class_value_size: + * \param klass a class + * + * This function is used for value types, and return the + * space and the alignment to store that kind of value object. + * + * \returns the size of a value of kind \p klass + */ +gint32 +mono_class_value_size (MonoClass *klass, guint32 *align) +{ + gint32 size; + + /* fixme: check disable, because we still have external revereces to + * mscorlib and Dummy Objects + */ + /*g_assert (klass->valuetype);*/ + + size = mono_class_instance_size (klass) - sizeof (MonoObject); + + if (align) + *align = klass->min_align; + + return size; +} + +/** + * mono_class_data_size: + * \param klass a class + * + * \returns The size of the static class data + */ +gint32 +mono_class_data_size (MonoClass *klass) +{ + if (!klass->inited) + mono_class_init (klass); + /* This can happen with dynamically created types */ + if (!klass->fields_inited) + mono_class_setup_fields (klass); + + /* in arrays, sizes.class_size is unioned with element_size + * and arrays have no static fields + */ + if (klass->rank) + return 0; + return klass->sizes.class_size; +} + +/* + * Auxiliary routine to mono_class_get_field + * + * Takes a field index instead of a field token. + */ +static MonoClassField * +mono_class_get_field_idx (MonoClass *klass, int idx) +{ + mono_class_setup_fields (klass); + if (mono_class_has_failure (klass)) + return NULL; + + while (klass) { + int first_field_idx = mono_class_get_first_field_idx (klass); + int fcount = mono_class_get_field_count (klass); + if (klass->image->uncompressed_metadata) { + /* + * first_field_idx points to the FieldPtr table, while idx points into the + * Field table, so we have to do a search. + */ + /*FIXME this is broken for types with multiple fields with the same name.*/ + const char *name = mono_metadata_string_heap (klass->image, mono_metadata_decode_row_col (&klass->image->tables [MONO_TABLE_FIELD], idx, MONO_FIELD_NAME)); + int i; + + for (i = 0; i < fcount; ++i) + if (mono_field_get_name (&klass->fields [i]) == name) + return &klass->fields [i]; + g_assert_not_reached (); + } else { + if (fcount) { + if ((idx >= first_field_idx) && (idx < first_field_idx + fcount)){ + return &klass->fields [idx - first_field_idx]; + } + } + } + klass = klass->parent; + } + return NULL; +} + +/** + * mono_class_get_field: + * \param class the class to lookup the field. + * \param field_token the field token + * + * \returns A \c MonoClassField representing the type and offset of + * the field, or a NULL value if the field does not belong to this + * class. + */ +MonoClassField * +mono_class_get_field (MonoClass *klass, guint32 field_token) +{ + int idx = mono_metadata_token_index (field_token); + + g_assert (mono_metadata_token_code (field_token) == MONO_TOKEN_FIELD_DEF); + + return mono_class_get_field_idx (klass, idx - 1); +} + +/** + * mono_class_get_field_from_name: + * \param klass the class to lookup the field. + * \param name the field name + * + * Search the class \p klass and its parents for a field with the name \p name. + * + * \returns The \c MonoClassField pointer of the named field or NULL + */ +MonoClassField * +mono_class_get_field_from_name (MonoClass *klass, const char *name) +{ + return mono_class_get_field_from_name_full (klass, name, NULL); +} + +/** + * mono_class_get_field_from_name_full: + * \param klass the class to lookup the field. + * \param name the field name + * \param type the type of the fields. This optional. + * + * Search the class \p klass and it's parents for a field with the name \p name and type \p type. + * + * If \p klass is an inflated generic type, the type comparison is done with the equivalent field + * of its generic type definition. + * + * \returns The MonoClassField pointer of the named field or NULL + */ +MonoClassField * +mono_class_get_field_from_name_full (MonoClass *klass, const char *name, MonoType *type) +{ + int i; + + mono_class_setup_fields (klass); + if (mono_class_has_failure (klass)) + return NULL; + + while (klass) { + int fcount = mono_class_get_field_count (klass); + for (i = 0; i < fcount; ++i) { + MonoClassField *field = &klass->fields [i]; + + if (strcmp (name, mono_field_get_name (field)) != 0) + continue; + + if (type) { + MonoType *field_type = mono_metadata_get_corresponding_field_from_generic_type_definition (field)->type; + if (!mono_metadata_type_equal_full (type, field_type, TRUE)) + continue; + } + return field; + } + klass = klass->parent; + } + return NULL; +} + +/** + * mono_class_get_field_token: + * \param field the field we need the token of + * + * Get the token of a field. Note that the tokesn is only valid for the image + * the field was loaded from. Don't use this function for fields in dynamic types. + * + * \returns The token representing the field in the image it was loaded from. + */ +guint32 +mono_class_get_field_token (MonoClassField *field) +{ + MonoClass *klass = field->parent; + int i; + + mono_class_setup_fields (klass); + + while (klass) { + if (!klass->fields) + return 0; + int first_field_idx = mono_class_get_first_field_idx (klass); + int fcount = mono_class_get_field_count (klass); + for (i = 0; i < fcount; ++i) { + if (&klass->fields [i] == field) { + int idx = first_field_idx + i + 1; + + if (klass->image->uncompressed_metadata) + idx = mono_metadata_translate_token_index (klass->image, MONO_TABLE_FIELD, idx); + return mono_metadata_make_token (MONO_TABLE_FIELD, idx); + } + } + klass = klass->parent; + } + + g_assert_not_reached (); + return 0; +} + +static int +mono_field_get_index (MonoClassField *field) +{ + int index = field - field->parent->fields; + g_assert (index >= 0 && index < mono_class_get_field_count (field->parent)); + + return index; +} + +/* + * mono_class_get_field_default_value: + * + * Return the default value of the field as a pointer into the metadata blob. + */ +const char* +mono_class_get_field_default_value (MonoClassField *field, MonoTypeEnum *def_type) +{ + guint32 cindex; + guint32 constant_cols [MONO_CONSTANT_SIZE]; + int field_index; + MonoClass *klass = field->parent; + MonoFieldDefaultValue *def_values; + + g_assert (field->type->attrs & FIELD_ATTRIBUTE_HAS_DEFAULT); + + def_values = mono_class_get_field_def_values (klass); + if (!def_values) { + def_values = (MonoFieldDefaultValue *)mono_class_alloc0 (klass, sizeof (MonoFieldDefaultValue) * mono_class_get_field_count (klass)); + + mono_class_set_field_def_values (klass, def_values); + } + + field_index = mono_field_get_index (field); + + if (!def_values [field_index].data) { + cindex = mono_metadata_get_constant_index (field->parent->image, mono_class_get_field_token (field), 0); + if (!cindex) + return NULL; + + g_assert (!(field->type->attrs & FIELD_ATTRIBUTE_HAS_FIELD_RVA)); + + mono_metadata_decode_row (&field->parent->image->tables [MONO_TABLE_CONSTANT], cindex - 1, constant_cols, MONO_CONSTANT_SIZE); + def_values [field_index].def_type = (MonoTypeEnum)constant_cols [MONO_CONSTANT_TYPE]; + mono_memory_barrier (); + def_values [field_index].data = (const char *)mono_metadata_blob_heap (field->parent->image, constant_cols [MONO_CONSTANT_VALUE]); + } + + *def_type = def_values [field_index].def_type; + return def_values [field_index].data; +} + +static int +mono_property_get_index (MonoProperty *prop) +{ + MonoClassPropertyInfo *info = mono_class_get_property_info (prop->parent); + int index = prop - info->properties; + + g_assert (index >= 0 && index < info->count); + + return index; +} + +/* + * mono_class_get_property_default_value: + * + * Return the default value of the field as a pointer into the metadata blob. + */ +const char* +mono_class_get_property_default_value (MonoProperty *property, MonoTypeEnum *def_type) +{ + guint32 cindex; + guint32 constant_cols [MONO_CONSTANT_SIZE]; + MonoClass *klass = property->parent; + + g_assert (property->attrs & PROPERTY_ATTRIBUTE_HAS_DEFAULT); + /* + * We don't cache here because it is not used by C# so it's quite rare, but + * we still do the lookup in klass->ext because that is where the data + * is stored for dynamic assemblies. + */ + + if (image_is_dynamic (klass->image)) { + MonoClassPropertyInfo *info = mono_class_get_property_info (klass); + int prop_index = mono_property_get_index (property); + if (info->def_values && info->def_values [prop_index].data) { + *def_type = info->def_values [prop_index].def_type; + return info->def_values [prop_index].data; + } + return NULL; + } + cindex = mono_metadata_get_constant_index (klass->image, mono_class_get_property_token (property), 0); + if (!cindex) + return NULL; + + mono_metadata_decode_row (&klass->image->tables [MONO_TABLE_CONSTANT], cindex - 1, constant_cols, MONO_CONSTANT_SIZE); + *def_type = (MonoTypeEnum)constant_cols [MONO_CONSTANT_TYPE]; + return (const char *)mono_metadata_blob_heap (klass->image, constant_cols [MONO_CONSTANT_VALUE]); +} + +/** + * mono_class_get_event_token: + */ +guint32 +mono_class_get_event_token (MonoEvent *event) +{ + MonoClass *klass = event->parent; + int i; + + while (klass) { + MonoClassEventInfo *info = mono_class_get_event_info (klass); + if (info) { + for (i = 0; i < info->count; ++i) { + if (&info->events [i] == event) + return mono_metadata_make_token (MONO_TABLE_EVENT, info->first + i + 1); + } + } + klass = klass->parent; + } + + g_assert_not_reached (); + return 0; +} + +/** + * mono_class_get_property_from_name: + * \param klass a class + * \param name name of the property to lookup in the specified class + * + * Use this method to lookup a property in a class + * \returns the \c MonoProperty with the given name, or NULL if the property + * does not exist on the \p klass. + */ +MonoProperty* +mono_class_get_property_from_name (MonoClass *klass, const char *name) +{ + while (klass) { + MonoProperty* p; + gpointer iter = NULL; + while ((p = mono_class_get_properties (klass, &iter))) { + if (! strcmp (name, p->name)) + return p; + } + klass = klass->parent; + } + return NULL; +} + +/** + * mono_class_get_property_token: + * \param prop MonoProperty to query + * + * \returns The ECMA token for the specified property. + */ +guint32 +mono_class_get_property_token (MonoProperty *prop) +{ + MonoClass *klass = prop->parent; + while (klass) { + MonoProperty* p; + int i = 0; + gpointer iter = NULL; + MonoClassPropertyInfo *info = mono_class_get_property_info (klass); + while ((p = mono_class_get_properties (klass, &iter))) { + if (&info->properties [i] == prop) + return mono_metadata_make_token (MONO_TABLE_PROPERTY, info->first + i + 1); + + i ++; + } + klass = klass->parent; + } + + g_assert_not_reached (); + return 0; +} + +/** + * mono_class_name_from_token: + */ +char * +mono_class_name_from_token (MonoImage *image, guint32 type_token) +{ + const char *name, *nspace; + if (image_is_dynamic (image)) + return g_strdup_printf ("DynamicType 0x%08x", type_token); + + switch (type_token & 0xff000000){ + case MONO_TOKEN_TYPE_DEF: { + guint32 cols [MONO_TYPEDEF_SIZE]; + MonoTableInfo *tt = &image->tables [MONO_TABLE_TYPEDEF]; + guint tidx = mono_metadata_token_index (type_token); + + if (tidx > tt->rows) + return g_strdup_printf ("Invalid type token 0x%08x", type_token); + + mono_metadata_decode_row (tt, tidx - 1, cols, MONO_TYPEDEF_SIZE); + name = mono_metadata_string_heap (image, cols [MONO_TYPEDEF_NAME]); + nspace = mono_metadata_string_heap (image, cols [MONO_TYPEDEF_NAMESPACE]); + if (strlen (nspace) == 0) + return g_strdup_printf ("%s", name); + else + return g_strdup_printf ("%s.%s", nspace, name); + } + + case MONO_TOKEN_TYPE_REF: { + MonoError error; + guint32 cols [MONO_TYPEREF_SIZE]; + MonoTableInfo *t = &image->tables [MONO_TABLE_TYPEREF]; + guint tidx = mono_metadata_token_index (type_token); + + if (tidx > t->rows) + return g_strdup_printf ("Invalid type token 0x%08x", type_token); + + if (!mono_verifier_verify_typeref_row (image, tidx - 1, &error)) { + char *msg = g_strdup_printf ("Invalid type token 0x%08x due to '%s'", type_token, mono_error_get_message (&error)); + mono_error_cleanup (&error); + return msg; + } + + mono_metadata_decode_row (t, tidx-1, cols, MONO_TYPEREF_SIZE); + name = mono_metadata_string_heap (image, cols [MONO_TYPEREF_NAME]); + nspace = mono_metadata_string_heap (image, cols [MONO_TYPEREF_NAMESPACE]); + if (strlen (nspace) == 0) + return g_strdup_printf ("%s", name); + else + return g_strdup_printf ("%s.%s", nspace, name); + } + + case MONO_TOKEN_TYPE_SPEC: + return g_strdup_printf ("Typespec 0x%08x", type_token); + default: + return g_strdup_printf ("Invalid type token 0x%08x", type_token); + } +} + +static char * +mono_assembly_name_from_token (MonoImage *image, guint32 type_token) +{ + if (image_is_dynamic (image)) + return g_strdup_printf ("DynamicAssembly %s", image->name); + + switch (type_token & 0xff000000){ + case MONO_TOKEN_TYPE_DEF: + if (image->assembly) + return mono_stringify_assembly_name (&image->assembly->aname); + else if (image->assembly_name) + return g_strdup (image->assembly_name); + return g_strdup_printf ("%s", image->name ? image->name : "[Could not resolve assembly name"); + case MONO_TOKEN_TYPE_REF: { + MonoError error; + MonoAssemblyName aname; + guint32 cols [MONO_TYPEREF_SIZE]; + MonoTableInfo *t = &image->tables [MONO_TABLE_TYPEREF]; + guint32 idx = mono_metadata_token_index (type_token); + + if (idx > t->rows) + return g_strdup_printf ("Invalid type token 0x%08x", type_token); + + if (!mono_verifier_verify_typeref_row (image, idx - 1, &error)) { + char *msg = g_strdup_printf ("Invalid type token 0x%08x due to '%s'", type_token, mono_error_get_message (&error)); + mono_error_cleanup (&error); + return msg; + } + mono_metadata_decode_row (t, idx-1, cols, MONO_TYPEREF_SIZE); + + idx = cols [MONO_TYPEREF_SCOPE] >> MONO_RESOLUTION_SCOPE_BITS; + switch (cols [MONO_TYPEREF_SCOPE] & MONO_RESOLUTION_SCOPE_MASK) { + case MONO_RESOLUTION_SCOPE_MODULE: + /* FIXME: */ + return g_strdup (""); + case MONO_RESOLUTION_SCOPE_MODULEREF: + /* FIXME: */ + return g_strdup (""); + case MONO_RESOLUTION_SCOPE_TYPEREF: + /* FIXME: */ + return g_strdup (""); + case MONO_RESOLUTION_SCOPE_ASSEMBLYREF: + mono_assembly_get_assemblyref (image, idx - 1, &aname); + return mono_stringify_assembly_name (&aname); + default: + g_assert_not_reached (); + } + break; + } + case MONO_TOKEN_TYPE_SPEC: + /* FIXME: */ + return g_strdup (""); + default: + g_assert_not_reached (); + } + + return NULL; +} + +/** + * mono_class_get_full: + * \param image the image where the class resides + * \param type_token the token for the class + * \param context the generic context used to evaluate generic instantiations in + * \deprecated Functions that expose \c MonoGenericContext are going away in mono 4.0 + * \returns The \c MonoClass that represents \p type_token in \p image + */ +MonoClass * +mono_class_get_full (MonoImage *image, guint32 type_token, MonoGenericContext *context) +{ + MonoError error; + MonoClass *klass; + klass = mono_class_get_checked (image, type_token, &error); + + if (klass && context && mono_metadata_token_table (type_token) == MONO_TABLE_TYPESPEC) + klass = mono_class_inflate_generic_class_checked (klass, context, &error); + + g_assert (mono_error_ok (&error)); /* FIXME deprecate this function and forbit the runtime from using it. */ + return klass; +} + + +MonoClass * +mono_class_get_and_inflate_typespec_checked (MonoImage *image, guint32 type_token, MonoGenericContext *context, MonoError *error) +{ + MonoClass *klass; + + error_init (error); + klass = mono_class_get_checked (image, type_token, error); + + if (klass && context && mono_metadata_token_table (type_token) == MONO_TABLE_TYPESPEC) + klass = mono_class_inflate_generic_class_checked (klass, context, error); + + return klass; +} +/** + * mono_class_get_checked: + * \param image the image where the class resides + * \param type_token the token for the class + * \param error error object to return any error + * + * \returns The MonoClass that represents \p type_token in \p image, or NULL on error. + */ +MonoClass * +mono_class_get_checked (MonoImage *image, guint32 type_token, MonoError *error) +{ + MonoClass *klass = NULL; + + error_init (error); + + if (image_is_dynamic (image)) { + int table = mono_metadata_token_table (type_token); + + if (table != MONO_TABLE_TYPEDEF && table != MONO_TABLE_TYPEREF && table != MONO_TABLE_TYPESPEC) { + mono_error_set_bad_image (error, image,"Bad token table for dynamic image: %x", table); + return NULL; + } + klass = (MonoClass *)mono_lookup_dynamic_token (image, type_token, NULL, error); + goto done; + } + + switch (type_token & 0xff000000){ + case MONO_TOKEN_TYPE_DEF: + klass = mono_class_create_from_typedef (image, type_token, error); + break; + case MONO_TOKEN_TYPE_REF: + klass = mono_class_from_typeref_checked (image, type_token, error); + break; + case MONO_TOKEN_TYPE_SPEC: + klass = mono_class_create_from_typespec (image, type_token, NULL, error); + break; + default: + mono_error_set_bad_image (error, image, "Unknown type token %x", type_token & 0xff000000); + } + +done: + /* Generic case, should be avoided for when a better error is possible. */ + if (!klass && mono_error_ok (error)) { + char *name = mono_class_name_from_token (image, type_token); + char *assembly = mono_assembly_name_from_token (image, type_token); + mono_error_set_type_load_name (error, name, assembly, "Could not resolve type with token %08x (class/assembly %s, %s)", type_token, name, assembly); + } + + return klass; +} + + +/** + * mono_type_get_checked: + * \param image the image where the type resides + * \param type_token the token for the type + * \param context the generic context used to evaluate generic instantiations in + * \param error Error handling context + * + * This functions exists to fullfill the fact that sometimes it's desirable to have access to the + * + * \returns The MonoType that represents \p type_token in \p image + */ +MonoType * +mono_type_get_checked (MonoImage *image, guint32 type_token, MonoGenericContext *context, MonoError *error) +{ + MonoType *type = NULL; + gboolean inflated = FALSE; + + error_init (error); + + //FIXME: this will not fix the very issue for which mono_type_get_full exists -but how to do it then? + if (image_is_dynamic (image)) { + MonoClass *klass = (MonoClass *)mono_lookup_dynamic_token (image, type_token, context, error); + return_val_if_nok (error, NULL); + return mono_class_get_type (klass); + } + + if ((type_token & 0xff000000) != MONO_TOKEN_TYPE_SPEC) { + MonoClass *klass = mono_class_get_checked (image, type_token, error); + + if (!klass) { + return NULL; + } + + g_assert (klass); + return mono_class_get_type (klass); + } + + type = mono_type_retrieve_from_typespec (image, type_token, context, &inflated, error); + + if (!type) { + return NULL; + } + + if (inflated) { + MonoType *tmp = type; + type = mono_class_get_type (mono_class_from_mono_type (type)); + /* FIXME: This is a workaround fo the fact that a typespec token sometimes reference to the generic type definition. + * A MonoClass::byval_arg of a generic type definion has type CLASS. + * Some parts of mono create a GENERICINST to reference a generic type definition and this generates confict with byval_arg. + * + * The long term solution is to chaise this places and make then set MonoType::type correctly. + * */ + if (type->type != tmp->type) + type = tmp; + else + mono_metadata_free_type (tmp); + } + return type; +} + +/** + * mono_class_get: + * \param image image where the class token will be looked up. + * \param type_token a type token from the image + * \returns the \c MonoClass with the given \p type_token on the \p image + */ +MonoClass * +mono_class_get (MonoImage *image, guint32 type_token) +{ + return mono_class_get_full (image, type_token, NULL); +} + +/** + * mono_image_init_name_cache: + * + * Initializes the class name cache stored in image->name_cache. + * + * LOCKING: Acquires the corresponding image lock. + */ +void +mono_image_init_name_cache (MonoImage *image) +{ + MonoTableInfo *t = &image->tables [MONO_TABLE_TYPEDEF]; + guint32 cols [MONO_TYPEDEF_SIZE]; + const char *name; + const char *nspace; + guint32 i, visib, nspace_index; + GHashTable *name_cache2, *nspace_table, *the_name_cache; + + if (image->name_cache) + return; + + the_name_cache = g_hash_table_new (g_str_hash, g_str_equal); + + if (image_is_dynamic (image)) { + mono_image_lock (image); + if (image->name_cache) { + /* Somebody initialized it before us */ + g_hash_table_destroy (the_name_cache); + } else { + mono_atomic_store_release (&image->name_cache, the_name_cache); + } + mono_image_unlock (image); + return; + } + + /* Temporary hash table to avoid lookups in the nspace_table */ + name_cache2 = g_hash_table_new (NULL, NULL); + + for (i = 1; i <= t->rows; ++i) { + mono_metadata_decode_row (t, i - 1, cols, MONO_TYPEDEF_SIZE); + visib = cols [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_VISIBILITY_MASK; + /* + * Nested types are accessed from the nesting name. We use the fact that nested types use different visibility flags + * than toplevel types, thus avoiding the need to grovel through the NESTED_TYPE table + */ + if (visib >= TYPE_ATTRIBUTE_NESTED_PUBLIC && visib <= TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM) + continue; + name = mono_metadata_string_heap (image, cols [MONO_TYPEDEF_NAME]); + nspace = mono_metadata_string_heap (image, cols [MONO_TYPEDEF_NAMESPACE]); + + nspace_index = cols [MONO_TYPEDEF_NAMESPACE]; + nspace_table = (GHashTable *)g_hash_table_lookup (name_cache2, GUINT_TO_POINTER (nspace_index)); + if (!nspace_table) { + nspace_table = g_hash_table_new (g_str_hash, g_str_equal); + g_hash_table_insert (the_name_cache, (char*)nspace, nspace_table); + g_hash_table_insert (name_cache2, GUINT_TO_POINTER (nspace_index), + nspace_table); + } + g_hash_table_insert (nspace_table, (char *) name, GUINT_TO_POINTER (i)); + } + + /* Load type names from EXPORTEDTYPES table */ + { + MonoTableInfo *t = &image->tables [MONO_TABLE_EXPORTEDTYPE]; + guint32 cols [MONO_EXP_TYPE_SIZE]; + int i; + + for (i = 0; i < t->rows; ++i) { + mono_metadata_decode_row (t, i, cols, MONO_EXP_TYPE_SIZE); + + guint32 impl = cols [MONO_EXP_TYPE_IMPLEMENTATION]; + if ((impl & MONO_IMPLEMENTATION_MASK) == MONO_IMPLEMENTATION_EXP_TYPE) + /* Nested type */ + continue; + + name = mono_metadata_string_heap (image, cols [MONO_EXP_TYPE_NAME]); + nspace = mono_metadata_string_heap (image, cols [MONO_EXP_TYPE_NAMESPACE]); + + nspace_index = cols [MONO_EXP_TYPE_NAMESPACE]; + nspace_table = (GHashTable *)g_hash_table_lookup (name_cache2, GUINT_TO_POINTER (nspace_index)); + if (!nspace_table) { + nspace_table = g_hash_table_new (g_str_hash, g_str_equal); + g_hash_table_insert (the_name_cache, (char*)nspace, nspace_table); + g_hash_table_insert (name_cache2, GUINT_TO_POINTER (nspace_index), + nspace_table); + } + g_hash_table_insert (nspace_table, (char *) name, GUINT_TO_POINTER (mono_metadata_make_token (MONO_TABLE_EXPORTEDTYPE, i + 1))); + } + } + + g_hash_table_destroy (name_cache2); + + mono_image_lock (image); + if (image->name_cache) { + /* Somebody initialized it before us */ + g_hash_table_destroy (the_name_cache); + } else { + mono_atomic_store_release (&image->name_cache, the_name_cache); + } + mono_image_unlock (image); +} + +/*FIXME Only dynamic assemblies should allow this operation.*/ +/** + * mono_image_add_to_name_cache: + */ +void +mono_image_add_to_name_cache (MonoImage *image, const char *nspace, + const char *name, guint32 index) +{ + GHashTable *nspace_table; + GHashTable *name_cache; + guint32 old_index; + + mono_image_init_name_cache (image); + mono_image_lock (image); + + name_cache = image->name_cache; + if (!(nspace_table = (GHashTable *)g_hash_table_lookup (name_cache, nspace))) { + nspace_table = g_hash_table_new (g_str_hash, g_str_equal); + g_hash_table_insert (name_cache, (char *)nspace, (char *)nspace_table); + } + + if ((old_index = GPOINTER_TO_UINT (g_hash_table_lookup (nspace_table, (char*) name)))) + g_error ("overrwritting old token %x on image %s for type %s::%s", old_index, image->name, nspace, name); + + g_hash_table_insert (nspace_table, (char *) name, GUINT_TO_POINTER (index)); + + mono_image_unlock (image); +} + +typedef struct { + gconstpointer key; + gpointer value; +} FindUserData; + +static void +find_nocase (gpointer key, gpointer value, gpointer user_data) +{ + char *name = (char*)key; + FindUserData *data = (FindUserData*)user_data; + + if (!data->value && (NULL == data->key || (mono_utf8_strcasecmp (name, (char*)data->key) == 0))) + data->value = value; +} + +/** + * mono_class_from_name_case: + * \param image The MonoImage where the type is looked up in + * \param name_space the type namespace + * \param name the type short name. + * \deprecated use the mono_class_from_name_case_checked variant instead. + * + * Obtains a \c MonoClass with a given namespace and a given name which + * is located in the given \c MonoImage. The namespace and name + * lookups are case insensitive. + */ +MonoClass * +mono_class_from_name_case (MonoImage *image, const char* name_space, const char *name) +{ + MonoError error; + MonoClass *res = mono_class_from_name_case_checked (image, name_space, name, &error); + mono_error_cleanup (&error); + + return res; +} + +/** + * mono_class_from_name_case_checked: + * \param image The MonoImage where the type is looked up in + * \param name_space the type namespace + * \param name the type short name. + * \param error if + * + * Obtains a MonoClass with a given namespace and a given name which + * is located in the given MonoImage. The namespace and name + * lookups are case insensitive. + * + * \returns The MonoClass if the given namespace and name were found, or NULL if it + * was not found. The \p error object will contain information about the problem + * in that case. + */ +MonoClass * +mono_class_from_name_case_checked (MonoImage *image, const char *name_space, const char *name, MonoError *error) +{ + MonoTableInfo *t = &image->tables [MONO_TABLE_TYPEDEF]; + guint32 cols [MONO_TYPEDEF_SIZE]; + const char *n; + const char *nspace; + guint32 i, visib; + + error_init (error); + + if (image_is_dynamic (image)) { + guint32 token = 0; + FindUserData user_data; + + mono_image_init_name_cache (image); + mono_image_lock (image); + + user_data.key = name_space; + user_data.value = NULL; + g_hash_table_foreach (image->name_cache, find_nocase, &user_data); + + if (user_data.value) { + GHashTable *nspace_table = (GHashTable*)user_data.value; + + user_data.key = name; + user_data.value = NULL; + + g_hash_table_foreach (nspace_table, find_nocase, &user_data); + + if (user_data.value) + token = GPOINTER_TO_UINT (user_data.value); + } + + mono_image_unlock (image); + + if (token) + return mono_class_get_checked (image, MONO_TOKEN_TYPE_DEF | token, error); + else + return NULL; + + } + + /* add a cache if needed */ + for (i = 1; i <= t->rows; ++i) { + mono_metadata_decode_row (t, i - 1, cols, MONO_TYPEDEF_SIZE); + visib = cols [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_VISIBILITY_MASK; + /* + * Nested types are accessed from the nesting name. We use the fact that nested types use different visibility flags + * than toplevel types, thus avoiding the need to grovel through the NESTED_TYPE table + */ + if (visib >= TYPE_ATTRIBUTE_NESTED_PUBLIC && visib <= TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM) + 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 && (NULL == name_space || (mono_utf8_strcasecmp (nspace, name_space) == 0))) + return mono_class_get_checked (image, MONO_TOKEN_TYPE_DEF | i, error); + } + return NULL; +} + +static MonoClass* +return_nested_in (MonoClass *klass, char *nested) +{ + MonoClass *found; + char *s = strchr (nested, '/'); + gpointer iter = NULL; + + if (s) { + *s = 0; + s++; + } + + while ((found = mono_class_get_nested_types (klass, &iter))) { + if (strcmp (found->name, nested) == 0) { + if (s) + return return_nested_in (found, s); + return found; + } + } + return NULL; +} + +static MonoClass* +search_modules (MonoImage *image, const char *name_space, const char *name, MonoError *error) +{ + MonoTableInfo *file_table = &image->tables [MONO_TABLE_FILE]; + MonoImage *file_image; + MonoClass *klass; + int i; + + error_init (error); + + /* + * The EXPORTEDTYPES table only contains public types, so have to search the + * modules as well. + * Note: image->modules contains the contents of the MODULEREF table, while + * the real module list is in the FILE table. + */ + for (i = 0; i < file_table->rows; i++) { + guint32 cols [MONO_FILE_SIZE]; + mono_metadata_decode_row (file_table, i, cols, MONO_FILE_SIZE); + if (cols [MONO_FILE_FLAGS] == FILE_CONTAINS_NO_METADATA) + continue; + + file_image = mono_image_load_file_for_image_checked (image, i + 1, error); + if (file_image) { + klass = mono_class_from_name_checked (file_image, name_space, name, error); + if (klass || !is_ok (error)) + return klass; + } + } + + return NULL; +} + +static MonoClass * +mono_class_from_name_checked_aux (MonoImage *image, const char* name_space, const char *name, GHashTable* visited_images, MonoError *error) +{ + GHashTable *nspace_table; + MonoImage *loaded_image; + guint32 token = 0; + int i; + MonoClass *klass; + char *nested; + char buf [1024]; + + error_init (error); + + // Checking visited images avoids stack overflows when cyclic references exist. + if (g_hash_table_lookup (visited_images, image)) + return NULL; + + g_hash_table_insert (visited_images, image, GUINT_TO_POINTER(1)); + + if ((nested = strchr (name, '/'))) { + int pos = nested - name; + int len = strlen (name); + if (len > 1023) + return NULL; + memcpy (buf, name, len + 1); + buf [pos] = 0; + nested = buf + pos + 1; + name = buf; + } + + /* FIXME: get_class_from_name () can't handle types in the EXPORTEDTYPE table */ + if (get_class_from_name && image->tables [MONO_TABLE_EXPORTEDTYPE].rows == 0) { + gboolean res = get_class_from_name (image, name_space, name, &klass); + if (res) { + if (!klass) { + klass = search_modules (image, name_space, name, error); + if (!is_ok (error)) + return NULL; + } + if (nested) + return klass ? return_nested_in (klass, nested) : NULL; + else + return klass; + } + } + + mono_image_init_name_cache (image); + mono_image_lock (image); + + nspace_table = (GHashTable *)g_hash_table_lookup (image->name_cache, name_space); + + if (nspace_table) + token = GPOINTER_TO_UINT (g_hash_table_lookup (nspace_table, name)); + + mono_image_unlock (image); + + if (!token && image_is_dynamic (image) && image->modules) { + /* Search modules as well */ + for (i = 0; i < image->module_count; ++i) { + MonoImage *module = image->modules [i]; + + klass = mono_class_from_name_checked (module, name_space, name, error); + if (klass || !is_ok (error)) + return klass; + } + } + + if (!token) { + klass = search_modules (image, name_space, name, error); + if (klass || !is_ok (error)) + return klass; + return NULL; + } + + if (mono_metadata_token_table (token) == MONO_TABLE_EXPORTEDTYPE) { + MonoTableInfo *t = &image->tables [MONO_TABLE_EXPORTEDTYPE]; + guint32 cols [MONO_EXP_TYPE_SIZE]; + guint32 idx, impl; + + idx = mono_metadata_token_index (token); + + mono_metadata_decode_row (t, idx - 1, cols, MONO_EXP_TYPE_SIZE); + + impl = cols [MONO_EXP_TYPE_IMPLEMENTATION]; + if ((impl & MONO_IMPLEMENTATION_MASK) == MONO_IMPLEMENTATION_FILE) { + loaded_image = mono_assembly_load_module_checked (image->assembly, impl >> MONO_IMPLEMENTATION_BITS, error); + if (!loaded_image) + return NULL; + klass = mono_class_from_name_checked_aux (loaded_image, name_space, name, visited_images, error); + if (nested) + return klass ? return_nested_in (klass, nested) : NULL; + return klass; + } else if ((impl & MONO_IMPLEMENTATION_MASK) == MONO_IMPLEMENTATION_ASSEMBLYREF) { + guint32 assembly_idx; + + assembly_idx = impl >> MONO_IMPLEMENTATION_BITS; + + mono_assembly_load_reference (image, assembly_idx - 1); + g_assert (image->references [assembly_idx - 1]); + if (image->references [assembly_idx - 1] == (gpointer)-1) + return NULL; + klass = mono_class_from_name_checked_aux (image->references [assembly_idx - 1]->image, name_space, name, visited_images, error); + if (nested) + return klass ? return_nested_in (klass, nested) : NULL; + return klass; + } else { + g_assert_not_reached (); + } + } + + token = MONO_TOKEN_TYPE_DEF | token; + + klass = mono_class_get_checked (image, token, error); + if (nested) + return return_nested_in (klass, nested); + return klass; +} + +/** + * mono_class_from_name_checked: + * \param image The MonoImage where the type is looked up in + * \param name_space the type namespace + * \param name the type short name. + * + * Obtains a MonoClass with a given namespace and a given name which + * is located in the given MonoImage. + * + * Works like mono_class_from_name, but error handling is tricky. It can return NULL and have no error + * set if the class was not found or it will return NULL and set the error if there was a loading error. + */ +MonoClass * +mono_class_from_name_checked (MonoImage *image, const char* name_space, const char *name, MonoError *error) +{ + MonoClass *klass; + GHashTable *visited_images; + + visited_images = g_hash_table_new (g_direct_hash, g_direct_equal); + + klass = mono_class_from_name_checked_aux (image, name_space, name, visited_images, error); + + g_hash_table_destroy (visited_images); + + return klass; +} + +/** + * mono_class_from_name: + * \param image The \c MonoImage where the type is looked up in + * \param name_space the type namespace + * \param name the type short name. + * + * Obtains a \c MonoClass with a given namespace and a given name which + * is located in the given \c MonoImage. + * + * To reference nested classes, use the "/" character as a separator. + * For example use \c "Foo/Bar" to reference the class \c Bar that is nested + * inside \c Foo, like this: "class Foo { class Bar {} }". + */ +MonoClass * +mono_class_from_name (MonoImage *image, const char* name_space, const char *name) +{ + MonoError error; + MonoClass *klass; + + klass = mono_class_from_name_checked (image, name_space, name, &error); + mono_error_cleanup (&error); /* FIXME Don't swallow the error */ + + return klass; +} + +/** + * mono_class_load_from_name: + * \param image The MonoImage where the type is looked up in + * \param name_space the type namespace + * \param name the type short name. + * + * This function works exactly like mono_class_from_name but it will abort if the class is not found. + * This function should be used by the runtime for critical types to which there's no way to recover but crash + * If they are missing. Thing of System.Object or System.String. + */ +MonoClass * +mono_class_load_from_name (MonoImage *image, const char* name_space, const char *name) +{ + MonoError error; + MonoClass *klass; + + klass = mono_class_from_name_checked (image, name_space, name, &error); + if (!klass) + g_error ("Runtime critical type %s.%s not found", name_space, name); + if (!mono_error_ok (&error)) + g_error ("Could not load runtime critical type %s.%s due to %s", name_space, name, mono_error_get_message (&error)); + return klass; +} + +/** + * mono_class_try_load_from_name: + * \param image The MonoImage where the type is looked up in + * \param name_space the type namespace + * \param name the type short name. + * + * This function tries to load a type, returning the class was found or NULL otherwise. + * This function should be used by the runtime when probing for optional types, those that could have being linked out. + * + * Big design consideration. This function aborts if there was an error loading the type. This prevents us from missing + * a type that we would otherwise assume to be available but was not due some error. + * + */ +MonoClass* +mono_class_try_load_from_name (MonoImage *image, const char* name_space, const char *name) +{ + MonoError error; + MonoClass *klass; + + klass = mono_class_from_name_checked (image, name_space, name, &error); + if (!mono_error_ok (&error)) + g_error ("Could not load runtime critical type %s.%s due to %s", name_space, name, mono_error_get_message (&error)); + return klass; +} + + +/** + * mono_class_is_subclass_of: + * \param klass class to probe if it is a subclass of another one + * \param klassc the class we suspect is the base class + * \param check_interfaces whether we should perform interface checks + * + * This method determines whether \p klass is a subclass of \p klassc. + * + * If the \p check_interfaces flag is set, then if \p klassc is an interface + * this method return TRUE if the \p klass implements the interface or + * if \p klass is an interface, if one of its base classes is \p klass. + * + * If \p check_interfaces is false, then if \p klass is not an interface, + * it returns TRUE if the \p klass is a subclass of \p klassc. + * + * if \p klass is an interface and \p klassc is \c System.Object, then this function + * returns TRUE. + * + */ +gboolean +mono_class_is_subclass_of (MonoClass *klass, MonoClass *klassc, + gboolean check_interfaces) +{ + /* FIXME test for interfaces with variant generic arguments */ + mono_class_init (klass); + mono_class_init (klassc); + + if (check_interfaces && MONO_CLASS_IS_INTERFACE (klassc) && !MONO_CLASS_IS_INTERFACE (klass)) { + if (MONO_CLASS_IMPLEMENTS_INTERFACE (klass, klassc->interface_id)) + return TRUE; + } else if (check_interfaces && MONO_CLASS_IS_INTERFACE (klassc) && MONO_CLASS_IS_INTERFACE (klass)) { + int i; + + for (i = 0; i < klass->interface_count; i ++) { + MonoClass *ic = klass->interfaces [i]; + if (ic == klassc) + return TRUE; + } + } else { + if (!MONO_CLASS_IS_INTERFACE (klass) && mono_class_has_parent (klass, klassc)) + return TRUE; + } + + /* + * MS.NET thinks interfaces are a subclass of Object, so we think it as + * well. + */ + if (klassc == mono_defaults.object_class) + return TRUE; + + return FALSE; +} + +static gboolean +mono_type_is_generic_argument (MonoType *type) +{ + return type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR; +} + +gboolean +mono_class_has_variant_generic_params (MonoClass *klass) +{ + int i; + MonoGenericContainer *container; + + if (!mono_class_is_ginst (klass)) + return FALSE; + + container = mono_class_get_generic_container (mono_class_get_generic_class (klass)->container_class); + + for (i = 0; i < container->type_argc; ++i) + if (mono_generic_container_get_param_info (container, i)->flags & (MONO_GEN_PARAM_VARIANT|MONO_GEN_PARAM_COVARIANT)) + return TRUE; + + return FALSE; +} + +static gboolean +mono_gparam_is_reference_conversible (MonoClass *target, MonoClass *candidate, gboolean check_for_reference_conv) +{ + if (target == candidate) + return TRUE; + + if (check_for_reference_conv && + mono_type_is_generic_argument (&target->byval_arg) && + mono_type_is_generic_argument (&candidate->byval_arg)) { + MonoGenericParam *gparam = candidate->byval_arg.data.generic_param; + MonoGenericParamInfo *pinfo = mono_generic_param_info (gparam); + + if (!pinfo || (pinfo->flags & GENERIC_PARAMETER_ATTRIBUTE_REFERENCE_TYPE_CONSTRAINT) == 0) + return FALSE; + } + if (!mono_class_is_assignable_from (target, candidate)) + return FALSE; + return TRUE; +} + +/** + * @container the generic container from the GTD + * @klass: the class to be assigned to + * @oklass: the source class + * + * Both @klass and @oklass must be instances of the same generic interface. + * + * Returns: TRUE if @klass can be assigned to a @klass variable + */ +gboolean +mono_class_is_variant_compatible (MonoClass *klass, MonoClass *oklass, gboolean check_for_reference_conv) +{ + int j; + MonoType **klass_argv, **oklass_argv; + MonoClass *klass_gtd = mono_class_get_generic_type_definition (klass); + MonoGenericContainer *container = mono_class_get_generic_container (klass_gtd); + + if (klass == oklass) + return TRUE; + + /*Viable candidates are instances of the same generic interface*/ + if (mono_class_get_generic_type_definition (oklass) != klass_gtd || oklass == klass_gtd) + return FALSE; + + klass_argv = &mono_class_get_generic_class (klass)->context.class_inst->type_argv [0]; + oklass_argv = &mono_class_get_generic_class (oklass)->context.class_inst->type_argv [0]; + + for (j = 0; j < container->type_argc; ++j) { + MonoClass *param1_class = mono_class_from_mono_type (klass_argv [j]); + MonoClass *param2_class = mono_class_from_mono_type (oklass_argv [j]); + + if (param1_class->valuetype != param2_class->valuetype || (param1_class->valuetype && param1_class != param2_class)) + return FALSE; + + /* + * The _VARIANT and _COVARIANT constants should read _COVARIANT and + * _CONTRAVARIANT, but they are in a public header so we can't fix it. + */ + if (param1_class != param2_class) { + if (mono_generic_container_get_param_info (container, j)->flags & MONO_GEN_PARAM_VARIANT) { + if (!mono_gparam_is_reference_conversible (param1_class, param2_class, check_for_reference_conv)) + return FALSE; + } else if (mono_generic_container_get_param_info (container, j)->flags & MONO_GEN_PARAM_COVARIANT) { + if (!mono_gparam_is_reference_conversible (param2_class, param1_class, check_for_reference_conv)) + return FALSE; + } else + return FALSE; + } + } + return TRUE; +} + +static gboolean +mono_gparam_is_assignable_from (MonoClass *target, MonoClass *candidate) +{ + MonoGenericParam *gparam, *ogparam; + MonoGenericParamInfo *tinfo, *cinfo; + MonoClass **candidate_class; + gboolean class_constraint_satisfied, valuetype_constraint_satisfied; + int tmask, cmask; + + if (target == candidate) + return TRUE; + if (target->byval_arg.type != candidate->byval_arg.type) + return FALSE; + + gparam = target->byval_arg.data.generic_param; + ogparam = candidate->byval_arg.data.generic_param; + tinfo = mono_generic_param_info (gparam); + cinfo = mono_generic_param_info (ogparam); + + class_constraint_satisfied = FALSE; + valuetype_constraint_satisfied = FALSE; + + /*candidate must have a super set of target's special constraints*/ + tmask = tinfo->flags & GENERIC_PARAMETER_ATTRIBUTE_SPECIAL_CONSTRAINTS_MASK; + cmask = cinfo->flags & GENERIC_PARAMETER_ATTRIBUTE_SPECIAL_CONSTRAINTS_MASK; + + if (cinfo->constraints) { + for (candidate_class = cinfo->constraints; *candidate_class; ++candidate_class) { + MonoClass *cc = *candidate_class; + + if (mono_type_is_reference (&cc->byval_arg) && !MONO_CLASS_IS_INTERFACE (cc)) + class_constraint_satisfied = TRUE; + else if (!mono_type_is_reference (&cc->byval_arg) && !MONO_CLASS_IS_INTERFACE (cc)) + valuetype_constraint_satisfied = TRUE; + } + } + class_constraint_satisfied |= (cmask & GENERIC_PARAMETER_ATTRIBUTE_REFERENCE_TYPE_CONSTRAINT) != 0; + valuetype_constraint_satisfied |= (cmask & GENERIC_PARAMETER_ATTRIBUTE_VALUE_TYPE_CONSTRAINT) != 0; + + if ((tmask & GENERIC_PARAMETER_ATTRIBUTE_REFERENCE_TYPE_CONSTRAINT) && !class_constraint_satisfied) + return FALSE; + if ((tmask & GENERIC_PARAMETER_ATTRIBUTE_VALUE_TYPE_CONSTRAINT) && !valuetype_constraint_satisfied) + return FALSE; + if ((tmask & GENERIC_PARAMETER_ATTRIBUTE_CONSTRUCTOR_CONSTRAINT) && !((cmask & GENERIC_PARAMETER_ATTRIBUTE_CONSTRUCTOR_CONSTRAINT) || + valuetype_constraint_satisfied)) { + return FALSE; + } + + + /*candidate type constraints must be a superset of target's*/ + if (tinfo->constraints) { + MonoClass **target_class; + for (target_class = tinfo->constraints; *target_class; ++target_class) { + MonoClass *tc = *target_class; + + /* + * A constraint from @target might inflate into @candidate itself and in that case we don't need + * check it's constraints since it satisfy the constraint by itself. + */ + if (mono_metadata_type_equal (&tc->byval_arg, &candidate->byval_arg)) + continue; + + if (!cinfo->constraints) + return FALSE; + + for (candidate_class = cinfo->constraints; *candidate_class; ++candidate_class) { + MonoClass *cc = *candidate_class; + + if (mono_class_is_assignable_from (tc, cc)) + break; + + /* + * This happens when we have the following: + * + * Bar where K : IFace + * Foo where T : U where U : IFace + * ... + * Bar <- T here satisfy K constraint transitively through to U's constraint + * + */ + if (mono_type_is_generic_argument (&cc->byval_arg)) { + if (mono_gparam_is_assignable_from (target, cc)) + break; + } + } + if (!*candidate_class) + return FALSE; + } + } + + /*candidate itself must have a constraint that satisfy target*/ + if (cinfo->constraints) { + for (candidate_class = cinfo->constraints; *candidate_class; ++candidate_class) { + MonoClass *cc = *candidate_class; + if (mono_class_is_assignable_from (target, cc)) + return TRUE; + } + } + return FALSE; +} + +/** + * mono_class_is_assignable_from: + * \param klass the class to be assigned to + * \param oklass the source class + * + * \returns TRUE if an instance of class \p oklass can be assigned to an + * instance of class \p klass + */ +gboolean +mono_class_is_assignable_from (MonoClass *klass, MonoClass *oklass) +{ + MonoError error; + /*FIXME this will cause a lot of irrelevant stuff to be loaded.*/ + if (!klass->inited) + mono_class_init (klass); + + if (!oklass->inited) + mono_class_init (oklass); + + if (mono_class_has_failure (klass) || mono_class_has_failure (oklass)) + return FALSE; + + if (mono_type_is_generic_argument (&klass->byval_arg)) { + if (!mono_type_is_generic_argument (&oklass->byval_arg)) + return FALSE; + return mono_gparam_is_assignable_from (klass, oklass); + } + + /* This can happen if oklass is a tyvar that has a constraint which is another tyvar which in turn + * has a constraint which is a class type: + * + * class Foo { } + * class G where T1 : T2 where T2 : Foo { } + * + * In this case, Foo is assignable from T1. + */ + if ((oklass->byval_arg.type == MONO_TYPE_VAR) || (oklass->byval_arg.type == MONO_TYPE_MVAR)) { + MonoGenericParam *gparam = oklass->byval_arg.data.generic_param; + MonoClass **constraints = mono_generic_container_get_param_info (gparam->owner, gparam->num)->constraints; + int i; + + if (constraints) { + for (i = 0; constraints [i]; ++i) { + if (mono_class_is_assignable_from (klass, constraints [i])) + return TRUE; + } + } + + return mono_class_has_parent (oklass, klass); + } + + if (MONO_CLASS_IS_INTERFACE (klass)) { + + /* interface_offsets might not be set for dynamic classes */ + if (mono_class_get_ref_info_handle (oklass) && !oklass->interface_bitmap) { + /* + * oklass might be a generic type parameter but they have + * interface_offsets set. + */ + gboolean result = mono_reflection_call_is_assignable_to (oklass, klass, &error); + if (!is_ok (&error)) { + mono_error_cleanup (&error); + return FALSE; + } + return result; + } + if (!oklass->interface_bitmap) + /* Happens with generic instances of not-yet created dynamic types */ + return FALSE; + if (MONO_CLASS_IMPLEMENTS_INTERFACE (oklass, klass->interface_id)) + return TRUE; + + if (klass->is_array_special_interface && oklass->rank == 1) { + //XXX we could offset this by having the cast target computed at JIT time + //XXX we could go even further and emit a wrapper that would do the extra type check + MonoClass *iface_klass = mono_class_from_mono_type (mono_class_get_generic_class (klass)->context.class_inst->type_argv [0]); + MonoClass *obj_klass = oklass->cast_class; //This gets us the cast class of element type of the array + + // If the target we're trying to cast to is a valuetype, we must account of weird valuetype equivalences such as IntEnum <> int or uint <> int + // We can't apply it for ref types as this would go wrong with arrays - IList would have byte tested + if (iface_klass->valuetype) + iface_klass = iface_klass->cast_class; + + //array covariant casts only operates on scalar to scalar + //This is so int[] can't be casted to IComparable[] + if (!(obj_klass->valuetype && !iface_klass->valuetype) && mono_class_is_assignable_from (iface_klass, obj_klass)) + return TRUE; + } + + if (mono_class_has_variant_generic_params (klass)) { + int i; + mono_class_setup_interfaces (oklass, &error); + if (!mono_error_ok (&error)) { + mono_error_cleanup (&error); + return FALSE; + } + + /*klass is a generic variant interface, We need to extract from oklass a list of ifaces which are viable candidates.*/ + for (i = 0; i < oklass->interface_offsets_count; ++i) { + MonoClass *iface = oklass->interfaces_packed [i]; + + if (mono_class_is_variant_compatible (klass, iface, FALSE)) + return TRUE; + } + } + return FALSE; + } else if (klass->delegate) { + if (mono_class_has_variant_generic_params (klass) && mono_class_is_variant_compatible (klass, oklass, FALSE)) + return TRUE; + }else if (klass->rank) { + MonoClass *eclass, *eoclass; + + if (oklass->rank != klass->rank) + return FALSE; + + /* vectors vs. one dimensional arrays */ + if (oklass->byval_arg.type != klass->byval_arg.type) + return FALSE; + + eclass = klass->cast_class; + eoclass = oklass->cast_class; + + /* + * a is b does not imply a[] is b[] when a is a valuetype, and + * b is a reference type. + */ + + if (eoclass->valuetype) { + if ((eclass == mono_defaults.enum_class) || + (eclass == mono_defaults.enum_class->parent) || + (eclass == mono_defaults.object_class)) + return FALSE; + } + + return mono_class_is_assignable_from (klass->cast_class, oklass->cast_class); + } else if (mono_class_is_nullable (klass)) { + if (mono_class_is_nullable (oklass)) + return mono_class_is_assignable_from (klass->cast_class, oklass->cast_class); + else + return mono_class_is_assignable_from (klass->cast_class, oklass); + } else if (klass == mono_defaults.object_class) + return TRUE; + + return mono_class_has_parent (oklass, klass); +} + +/*Check if @oklass is variant compatible with @klass.*/ +static gboolean +mono_class_is_variant_compatible_slow (MonoClass *klass, MonoClass *oklass) +{ + int j; + MonoType **klass_argv, **oklass_argv; + MonoClass *klass_gtd = mono_class_get_generic_type_definition (klass); + MonoGenericContainer *container = mono_class_get_generic_container (klass_gtd); + + /*Viable candidates are instances of the same generic interface*/ + if (mono_class_get_generic_type_definition (oklass) != klass_gtd || oklass == klass_gtd) + return FALSE; + + klass_argv = &mono_class_get_generic_class (klass)->context.class_inst->type_argv [0]; + oklass_argv = &mono_class_get_generic_class (oklass)->context.class_inst->type_argv [0]; + + for (j = 0; j < container->type_argc; ++j) { + MonoClass *param1_class = mono_class_from_mono_type (klass_argv [j]); + MonoClass *param2_class = mono_class_from_mono_type (oklass_argv [j]); + + if (param1_class->valuetype != param2_class->valuetype) + return FALSE; + + /* + * The _VARIANT and _COVARIANT constants should read _COVARIANT and + * _CONTRAVARIANT, but they are in a public header so we can't fix it. + */ + if (param1_class != param2_class) { + if (mono_generic_container_get_param_info (container, j)->flags & MONO_GEN_PARAM_VARIANT) { + if (!mono_class_is_assignable_from_slow (param1_class, param2_class)) + return FALSE; + } else if (mono_generic_container_get_param_info (container, j)->flags & MONO_GEN_PARAM_COVARIANT) { + if (!mono_class_is_assignable_from_slow (param2_class, param1_class)) + return FALSE; + } else + return FALSE; + } + } + return TRUE; +} +/*Check if @candidate implements the interface @target*/ +static gboolean +mono_class_implement_interface_slow (MonoClass *target, MonoClass *candidate) +{ + MonoError error; + int i; + gboolean is_variant = mono_class_has_variant_generic_params (target); + + if (is_variant && MONO_CLASS_IS_INTERFACE (candidate)) { + if (mono_class_is_variant_compatible_slow (target, candidate)) + return TRUE; + } + + do { + if (candidate == target) + return TRUE; + + /*A TypeBuilder can have more interfaces on tb->interfaces than on candidate->interfaces*/ + if (image_is_dynamic (candidate->image) && !candidate->wastypebuilder) { + MonoReflectionTypeBuilder *tb = (MonoReflectionTypeBuilder *)mono_class_get_ref_info_raw (candidate); /* FIXME use handles */ + int j; + if (tb && tb->interfaces) { + for (j = mono_array_length (tb->interfaces) - 1; j >= 0; --j) { + MonoReflectionType *iface = mono_array_get (tb->interfaces, MonoReflectionType*, j); + MonoClass *iface_class; + + /* we can't realize the type here since it can do pretty much anything. */ + if (!iface->type) + continue; + iface_class = mono_class_from_mono_type (iface->type); + if (iface_class == target) + return TRUE; + if (is_variant && mono_class_is_variant_compatible_slow (target, iface_class)) + return TRUE; + if (mono_class_implement_interface_slow (target, iface_class)) + return TRUE; + } + } + } else { + /*setup_interfaces don't mono_class_init anything*/ + /*FIXME this doesn't handle primitive type arrays. + ICollection x byte [] won't work because candidate->interfaces, for byte[], won't have IList. + A possible way to fix this would be to move that to setup_interfaces from setup_interface_offsets. + */ + mono_class_setup_interfaces (candidate, &error); + if (!mono_error_ok (&error)) { + mono_error_cleanup (&error); + return FALSE; + } + + for (i = 0; i < candidate->interface_count; ++i) { + if (candidate->interfaces [i] == target) + return TRUE; + + if (is_variant && mono_class_is_variant_compatible_slow (target, candidate->interfaces [i])) + return TRUE; + + if (mono_class_implement_interface_slow (target, candidate->interfaces [i])) + return TRUE; + } + } + candidate = candidate->parent; + } while (candidate); + + return FALSE; +} + +/* + * Check if @oklass can be assigned to @klass. + * This function does the same as mono_class_is_assignable_from but is safe to be used from mono_class_init context. + */ +gboolean +mono_class_is_assignable_from_slow (MonoClass *target, MonoClass *candidate) +{ + if (candidate == target) + return TRUE; + if (target == mono_defaults.object_class) + return TRUE; + + if (mono_class_has_parent (candidate, target)) + return TRUE; + + /*If target is not an interface there is no need to check them.*/ + if (MONO_CLASS_IS_INTERFACE (target)) + return mono_class_implement_interface_slow (target, candidate); + + if (target->delegate && mono_class_has_variant_generic_params (target)) + return mono_class_is_variant_compatible (target, candidate, FALSE); + + if (target->rank) { + MonoClass *eclass, *eoclass; + + if (target->rank != candidate->rank) + return FALSE; + + /* vectors vs. one dimensional arrays */ + if (target->byval_arg.type != candidate->byval_arg.type) + return FALSE; + + eclass = target->cast_class; + eoclass = candidate->cast_class; + + /* + * a is b does not imply a[] is b[] when a is a valuetype, and + * b is a reference type. + */ + + if (eoclass->valuetype) { + if ((eclass == mono_defaults.enum_class) || + (eclass == mono_defaults.enum_class->parent) || + (eclass == mono_defaults.object_class)) + return FALSE; + } + + return mono_class_is_assignable_from_slow (target->cast_class, candidate->cast_class); + } + /*FIXME properly handle nullables */ + /*FIXME properly handle (M)VAR */ + 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 + * + * \returns The static constructor of \p klass if it exists, NULL otherwise. + */ +MonoMethod* +mono_class_get_cctor (MonoClass *klass) +{ + MonoCachedClassInfo cached_info; + + if (image_is_dynamic (klass->image)) { + /* + * has_cctor is not set for these classes because mono_class_init () is + * not run for them. + */ + return mono_class_get_method_from_name_flags (klass, ".cctor", -1, METHOD_ATTRIBUTE_SPECIAL_NAME); + } + + mono_class_init (klass); + + if (!klass->has_cctor) + return NULL; + + if (mono_class_is_ginst (klass) && !klass->methods) + return mono_class_get_inflated_method (klass, mono_class_get_cctor (mono_class_get_generic_class (klass)->container_class)); + + if (mono_class_get_cached_class_info (klass, &cached_info)) { + MonoError error; + MonoMethod *result = mono_get_method_checked (klass->image, cached_info.cctor_token, klass, NULL, &error); + if (!mono_error_ok (&error)) + g_error ("Could not lookup class cctor from cached metadata due to %s", mono_error_get_message (&error)); + return result; + } + + return mono_class_get_method_from_name_flags (klass, ".cctor", -1, METHOD_ATTRIBUTE_SPECIAL_NAME); +} + +/** + * mono_class_get_finalizer: + * \param klass: The MonoClass pointer + * + * \returns The finalizer method of \p klass if it exists, NULL otherwise. + */ +MonoMethod* +mono_class_get_finalizer (MonoClass *klass) +{ + MonoCachedClassInfo cached_info; + + if (!klass->inited) + mono_class_init (klass); + if (!mono_class_has_finalizer (klass)) + return NULL; + + if (mono_class_get_cached_class_info (klass, &cached_info)) { + MonoError error; + MonoMethod *result = mono_get_method_checked (cached_info.finalize_image, cached_info.finalize_token, NULL, NULL, &error); + if (!mono_error_ok (&error)) + g_error ("Could not lookup finalizer from cached metadata due to %s", mono_error_get_message (&error)); + return result; + }else { + mono_class_setup_vtable (klass); + return klass->vtable [finalize_slot]; + } +} + +/** + * mono_class_needs_cctor_run: + * \param klass the MonoClass pointer + * \param caller a MonoMethod describing the caller + * + * Determines whenever the class has a static constructor and whenever it + * needs to be called when executing CALLER. + */ +gboolean +mono_class_needs_cctor_run (MonoClass *klass, MonoMethod *caller) +{ + MonoMethod *method; + + method = mono_class_get_cctor (klass); + if (method) + return (method == caller) ? FALSE : TRUE; + else + return FALSE; +} + +/** + * mono_class_array_element_size: + * \param klass + * + * \returns The number of bytes an element of type \p klass uses when stored into an array. + */ +gint32 +mono_class_array_element_size (MonoClass *klass) +{ + MonoType *type = &klass->byval_arg; + +handle_enum: + switch (type->type) { + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_BOOLEAN: + return 1; + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_CHAR: + return 2; + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_R4: + return 4; + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_PTR: + case MONO_TYPE_CLASS: + case MONO_TYPE_STRING: + case MONO_TYPE_OBJECT: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_ARRAY: + return sizeof (gpointer); + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_R8: + return 8; + case MONO_TYPE_VALUETYPE: + if (type->data.klass->enumtype) { + type = mono_class_enum_basetype (type->data.klass); + klass = klass->element_class; + goto handle_enum; + } + return mono_class_instance_size (klass) - sizeof (MonoObject); + case MONO_TYPE_GENERICINST: + type = &type->data.generic_class->container_class->byval_arg; + goto handle_enum; + case MONO_TYPE_VAR: + case MONO_TYPE_MVAR: { + int align; + + return mono_type_size (type, &align); + } + case MONO_TYPE_VOID: + return 0; + + default: + g_error ("unknown type 0x%02x in mono_class_array_element_size", type->type); + } + return -1; +} + +/** + * mono_array_element_size: + * \param ac pointer to a \c MonoArrayClass + * + * \returns The size of single array element. + * + * LOCKING: Acquires the loader lock. + */ +gint32 +mono_array_element_size (MonoClass *ac) +{ + g_assert (ac->rank); + if (G_UNLIKELY (!ac->size_inited)) { + mono_class_setup_fields (ac); + } + return ac->sizes.element_size; +} + +/** + * mono_ldtoken: + */ +gpointer +mono_ldtoken (MonoImage *image, guint32 token, MonoClass **handle_class, + MonoGenericContext *context) +{ + MonoError error; + gpointer res = mono_ldtoken_checked (image, token, handle_class, context, &error); + g_assert (mono_error_ok (&error)); + return res; +} + +gpointer +mono_ldtoken_checked (MonoImage *image, guint32 token, MonoClass **handle_class, + MonoGenericContext *context, MonoError *error) +{ + error_init (error); + + if (image_is_dynamic (image)) { + MonoClass *tmp_handle_class; + gpointer obj = mono_lookup_dynamic_token_class (image, token, TRUE, &tmp_handle_class, context, error); + + mono_error_assert_ok (error); + g_assert (tmp_handle_class); + if (handle_class) + *handle_class = tmp_handle_class; + + if (tmp_handle_class == mono_defaults.typehandle_class) + return &((MonoClass*)obj)->byval_arg; + else + return obj; + } + + switch (token & 0xff000000) { + case MONO_TOKEN_TYPE_DEF: + case MONO_TOKEN_TYPE_REF: + case MONO_TOKEN_TYPE_SPEC: { + MonoType *type; + if (handle_class) + *handle_class = mono_defaults.typehandle_class; + type = mono_type_get_checked (image, token, context, error); + if (!type) + return NULL; + + mono_class_init (mono_class_from_mono_type (type)); + /* We return a MonoType* as handle */ + return type; + } + case MONO_TOKEN_FIELD_DEF: { + MonoClass *klass; + guint32 type = mono_metadata_typedef_from_field (image, mono_metadata_token_index (token)); + if (!type) { + mono_error_set_bad_image (error, image, "Bad ldtoken %x", token); + return NULL; + } + if (handle_class) + *handle_class = mono_defaults.fieldhandle_class; + klass = mono_class_get_and_inflate_typespec_checked (image, MONO_TOKEN_TYPE_DEF | type, context, error); + if (!klass) + return NULL; + + mono_class_init (klass); + return mono_class_get_field (klass, token); + } + case MONO_TOKEN_METHOD_DEF: + case MONO_TOKEN_METHOD_SPEC: { + MonoMethod *meth; + meth = mono_get_method_checked (image, token, NULL, context, error); + if (handle_class) + *handle_class = mono_defaults.methodhandle_class; + if (!meth) + return NULL; + + return meth; + } + case MONO_TOKEN_MEMBER_REF: { + guint32 cols [MONO_MEMBERREF_SIZE]; + const char *sig; + mono_metadata_decode_row (&image->tables [MONO_TABLE_MEMBERREF], mono_metadata_token_index (token) - 1, cols, MONO_MEMBERREF_SIZE); + sig = mono_metadata_blob_heap (image, cols [MONO_MEMBERREF_SIGNATURE]); + mono_metadata_decode_blob_size (sig, &sig); + if (*sig == 0x6) { /* it's a field */ + MonoClass *klass; + MonoClassField *field; + field = mono_field_from_token_checked (image, token, &klass, context, error); + if (handle_class) + *handle_class = mono_defaults.fieldhandle_class; + return field; + } else { + MonoMethod *meth; + meth = mono_get_method_checked (image, token, NULL, context, error); + if (handle_class) + *handle_class = mono_defaults.methodhandle_class; + return meth; + } + } + default: + mono_error_set_bad_image (error, image, "Bad ldtoken %x", token); + } + return NULL; +} + +gpointer +mono_lookup_dynamic_token (MonoImage *image, guint32 token, MonoGenericContext *context, MonoError *error) +{ + MonoClass *handle_class; + error_init (error); + return mono_reflection_lookup_dynamic_token (image, token, TRUE, &handle_class, context, error); +} + +gpointer +mono_lookup_dynamic_token_class (MonoImage *image, guint32 token, gboolean valid_token, MonoClass **handle_class, MonoGenericContext *context, MonoError *error) +{ + return mono_reflection_lookup_dynamic_token (image, token, valid_token, handle_class, context, error); +} + +static MonoGetCachedClassInfo get_cached_class_info = NULL; + +void +mono_install_get_cached_class_info (MonoGetCachedClassInfo func) +{ + get_cached_class_info = func; +} + +static gboolean +mono_class_get_cached_class_info (MonoClass *klass, MonoCachedClassInfo *res) +{ + if (!get_cached_class_info) + return FALSE; + else + return get_cached_class_info (klass, res); +} + +void +mono_install_get_class_from_name (MonoGetClassFromName func) +{ + get_class_from_name = func; +} + +/** + * mono_class_get_image: + * + * Use this method to get the \c MonoImage* where this class came from. + * + * \returns The image where this class is defined. + */ +MonoImage* +mono_class_get_image (MonoClass *klass) +{ + return klass->image; +} + +/** + * mono_class_get_element_class: + * \param klass the \c MonoClass to act on + * + * Use this function to get the element class of an array. + * + * \returns The element class of an array. + */ +MonoClass* +mono_class_get_element_class (MonoClass *klass) +{ + return klass->element_class; +} + +/** + * mono_class_is_valuetype: + * \param klass the \c MonoClass to act on + * + * Use this method to determine if the provided \c MonoClass* represents a value type, + * or a reference type. + * + * \returns TRUE if the \c MonoClass represents a \c ValueType, FALSE if it represents a reference type. + */ +gboolean +mono_class_is_valuetype (MonoClass *klass) +{ + return klass->valuetype; +} + +/** + * mono_class_is_enum: + * \param klass the \c MonoClass to act on + * + * Use this function to determine if the provided \c MonoClass* represents an enumeration. + * + * \returns TRUE if the \c MonoClass represents an enumeration. + */ +gboolean +mono_class_is_enum (MonoClass *klass) +{ + return klass->enumtype; +} + +/** + * mono_class_enum_basetype: + * \param klass the \c MonoClass to act on + * + * Use this function to get the underlying type for an enumeration value. + * + * \returns The underlying type representation for an enumeration. + */ +MonoType* +mono_class_enum_basetype (MonoClass *klass) +{ + if (klass->element_class == klass) + /* SRE or broken types */ + return NULL; + else + return &klass->element_class->byval_arg; +} + +/** + * mono_class_get_parent + * \param klass the \c MonoClass to act on + * + * \returns The parent class for this class. + */ +MonoClass* +mono_class_get_parent (MonoClass *klass) +{ + return klass->parent; +} + +/** + * mono_class_get_nesting_type: + * \param klass the \c MonoClass to act on + * + * Use this function to obtain the class that the provided \c MonoClass* is nested on. + * + * If the return is NULL, this indicates that this class is not nested. + * + * \returns The container type where this type is nested or NULL if this type is not a nested type. + */ +MonoClass* +mono_class_get_nesting_type (MonoClass *klass) +{ + return klass->nested_in; +} + +/** + * mono_class_get_rank: + * \param klass the MonoClass to act on + * + * \returns The rank for the array (the number of dimensions). + */ +int +mono_class_get_rank (MonoClass *klass) +{ + return klass->rank; +} + +/** + * mono_class_get_name + * \param klass the \c MonoClass to act on + * + * \returns The name of the class. + */ +const char* +mono_class_get_name (MonoClass *klass) +{ + return klass->name; +} + +/** + * mono_class_get_namespace: + * \param klass the \c MonoClass to act on + * + * \returns The namespace of the class. + */ +const char* +mono_class_get_namespace (MonoClass *klass) +{ + return klass->name_space; +} + +/** + * mono_class_get_type: + * \param klass the \c MonoClass to act on + * + * This method returns the internal \c MonoType representation for the class. + * + * \returns The \c MonoType from the class. + */ +MonoType* +mono_class_get_type (MonoClass *klass) +{ + return &klass->byval_arg; +} + +/** + * mono_class_get_type_token: + * \param klass the \c MonoClass to act on + * + * This method returns type token for the class. + * + * \returns The type token for the class. + */ +guint32 +mono_class_get_type_token (MonoClass *klass) +{ + return klass->type_token; +} + +/** + * mono_class_get_byref_type: + * \param klass the \c MonoClass to act on + * + * + */ +MonoType* +mono_class_get_byref_type (MonoClass *klass) +{ + return &klass->this_arg; +} + +/** + * mono_class_num_fields: + * \param klass the \c MonoClass to act on + * + * \returns The number of static and instance fields in the class. + */ +int +mono_class_num_fields (MonoClass *klass) +{ + return mono_class_get_field_count (klass); +} + +/** + * mono_class_num_methods: + * \param klass the \c MonoClass to act on + * + * \returns The number of methods in the class. + */ +int +mono_class_num_methods (MonoClass *klass) +{ + return mono_class_get_method_count (klass); +} + +/** + * mono_class_num_properties + * \param klass the \c MonoClass to act on + * + * \returns The number of properties in the class. + */ +int +mono_class_num_properties (MonoClass *klass) +{ + mono_class_setup_properties (klass); + + return mono_class_get_property_info (klass)->count; +} + +/** + * mono_class_num_events: + * \param klass the \c MonoClass to act on + * + * \returns The number of events in the class. + */ +int +mono_class_num_events (MonoClass *klass) +{ + mono_class_setup_events (klass); + + return mono_class_get_event_info (klass)->count; +} + +/** + * mono_class_get_fields: + * \param klass the \c MonoClass to act on + * + * This routine is an iterator routine for retrieving the fields in a class. + * + * You must pass a \c gpointer that points to zero and is treated as an opaque handle to + * iterate over all of the elements. When no more values are + * available, the return value is NULL. + * + * \returns a \c MonoClassField* on each iteration, or NULL when no more fields are available. + */ +MonoClassField* +mono_class_get_fields (MonoClass* klass, gpointer *iter) +{ + MonoClassField* field; + if (!iter) + return NULL; + if (!*iter) { + mono_class_setup_fields (klass); + if (mono_class_has_failure (klass)) + return NULL; + /* start from the first */ + if (mono_class_get_field_count (klass)) { + *iter = &klass->fields [0]; + return &klass->fields [0]; + } else { + /* no fields */ + return NULL; + } + } + field = (MonoClassField *)*iter; + field++; + if (field < &klass->fields [mono_class_get_field_count (klass)]) { + *iter = field; + return field; + } + return NULL; +} + +/** + * mono_class_get_methods: + * \param klass the \c MonoClass to act on + * + * This routine is an iterator routine for retrieving the fields in a class. + * + * You must pass a \c gpointer that points to zero and is treated as an opaque handle to + * iterate over all of the elements. When no more values are + * available, the return value is NULL. + * + * \returns a \c MonoMethod on each iteration or NULL when no more methods are available. + */ +MonoMethod* +mono_class_get_methods (MonoClass* klass, gpointer *iter) +{ + MonoMethod** method; + if (!iter) + return NULL; + if (!*iter) { + mono_class_setup_methods (klass); + + /* + * We can't fail lookup of methods otherwise the runtime will burst in flames on all sort of places. + * FIXME we should better report this error to the caller + */ + if (!klass->methods) + return NULL; + /* start from the first */ + if (mono_class_get_method_count (klass)) { + *iter = &klass->methods [0]; + return klass->methods [0]; + } else { + /* no method */ + return NULL; + } + } + method = (MonoMethod **)*iter; + method++; + if (method < &klass->methods [mono_class_get_method_count (klass)]) { + *iter = method; + return *method; + } + return NULL; +} + +/* + * mono_class_get_virtual_methods: + * + * Iterate over the virtual methods of KLASS. + * + * LOCKING: Assumes the loader lock is held (because of the klass->methods check). + */ +static MonoMethod* +mono_class_get_virtual_methods (MonoClass* klass, gpointer *iter) +{ + gboolean static_iter = FALSE; + + if (!iter) + return NULL; + + /* + * If the lowest bit of the iterator is 1, this is an iterator for static metadata, + * and the upper bits contain an index. Otherwise, the iterator is a pointer into + * klass->methods. + */ + if ((gsize)(*iter) & 1) + static_iter = TRUE; + /* Use the static metadata only if klass->methods is not yet initialized */ + if (!static_iter && !(klass->methods || !MONO_CLASS_HAS_STATIC_METADATA (klass))) + static_iter = TRUE; + + if (!static_iter) { + MonoMethod** methodptr; + + if (!*iter) { + mono_class_setup_methods (klass); + /* + * We can't fail lookup of methods otherwise the runtime will burst in flames on all sort of places. + * FIXME we should better report this error to the caller + */ + if (!klass->methods) + return NULL; + /* start from the first */ + methodptr = &klass->methods [0]; + } else { + methodptr = (MonoMethod **)*iter; + methodptr++; + } + if (*iter) + g_assert ((guint64)(*iter) > 0x100); + int mcount = mono_class_get_method_count (klass); + while (methodptr < &klass->methods [mcount]) { + if (*methodptr && ((*methodptr)->flags & METHOD_ATTRIBUTE_VIRTUAL)) + break; + methodptr ++; + } + if (methodptr < &klass->methods [mcount]) { + *iter = methodptr; + return *methodptr; + } else { + return NULL; + } + } else { + /* Search directly in metadata to avoid calling setup_methods () */ + MonoMethod *res = NULL; + int i, start_index; + + if (!*iter) { + start_index = 0; + } else { + start_index = GPOINTER_TO_UINT (*iter) >> 1; + } + + int first_idx = mono_class_get_first_method_idx (klass); + int mcount = mono_class_get_method_count (klass); + for (i = start_index; i < mcount; ++i) { + guint32 flags; + + /* first_idx points into the methodptr table */ + flags = mono_metadata_decode_table_row_col (klass->image, MONO_TABLE_METHOD, first_idx + i, MONO_METHOD_FLAGS); + + if (flags & METHOD_ATTRIBUTE_VIRTUAL) + break; + } + + if (i < mcount) { + MonoError error; + res = mono_get_method_checked (klass->image, MONO_TOKEN_METHOD_DEF | (first_idx + i + 1), klass, NULL, &error); + mono_error_cleanup (&error); /* FIXME don't swallow the error */ + + /* Add 1 here so the if (*iter) check fails */ + *iter = GUINT_TO_POINTER (((i + 1) << 1) | 1); + return res; + } else { + return NULL; + } + } +} + +/** + * mono_class_get_properties: + * \param klass the \c MonoClass to act on + * + * This routine is an iterator routine for retrieving the properties in a class. + * + * You must pass a gpointer that points to zero and is treated as an opaque handle to + * iterate over all of the elements. When no more values are + * available, the return value is NULL. + * + * Returns: a \c MonoProperty* on each invocation, or NULL when no more are available. + */ +MonoProperty* +mono_class_get_properties (MonoClass* klass, gpointer *iter) +{ + MonoProperty* property; + if (!iter) + return NULL; + if (!*iter) { + mono_class_setup_properties (klass); + MonoClassPropertyInfo *info = mono_class_get_property_info (klass); + /* start from the first */ + if (info->count) { + *iter = &info->properties [0]; + return (MonoProperty *)*iter; + } else { + /* no fields */ + return NULL; + } + } + property = (MonoProperty *)*iter; + property++; + MonoClassPropertyInfo *info = mono_class_get_property_info (klass); + if (property < &info->properties [info->count]) { + *iter = property; + return (MonoProperty *)*iter; + } + return NULL; +} + +/** + * mono_class_get_events: + * \param klass the \c MonoClass to act on + * + * This routine is an iterator routine for retrieving the properties in a class. + * + * You must pass a \c gpointer that points to zero and is treated as an opaque handle to + * iterate over all of the elements. When no more values are + * available, the return value is NULL. + * + * \returns a \c MonoEvent* on each invocation, or NULL when no more are available. + */ +MonoEvent* +mono_class_get_events (MonoClass* klass, gpointer *iter) +{ + MonoEvent* event; + if (!iter) + return NULL; + if (!*iter) { + mono_class_setup_events (klass); + MonoClassEventInfo *info = mono_class_get_event_info (klass); + /* start from the first */ + if (info->count) { + *iter = &info->events [0]; + return (MonoEvent *)*iter; + } else { + /* no fields */ + return NULL; + } + } + event = (MonoEvent *)*iter; + event++; + MonoClassEventInfo *info = mono_class_get_event_info (klass); + if (event < &info->events [info->count]) { + *iter = event; + return (MonoEvent *)*iter; + } + return NULL; +} + +/** + * mono_class_get_interfaces + * \param klass the \c MonoClass to act on + * + * This routine is an iterator routine for retrieving the interfaces implemented by this class. + * + * You must pass a \c gpointer that points to zero and is treated as an opaque handle to + * iterate over all of the elements. When no more values are + * available, the return value is NULL. + * + * \returns a \c MonoClass* on each invocation, or NULL when no more are available. + */ +MonoClass* +mono_class_get_interfaces (MonoClass* klass, gpointer *iter) +{ + MonoError error; + MonoClass** iface; + if (!iter) + return NULL; + if (!*iter) { + if (!klass->inited) + mono_class_init (klass); + if (!klass->interfaces_inited) { + mono_class_setup_interfaces (klass, &error); + if (!mono_error_ok (&error)) { + mono_error_cleanup (&error); + return NULL; + } + } + /* start from the first */ + if (klass->interface_count) { + *iter = &klass->interfaces [0]; + return klass->interfaces [0]; + } else { + /* no interface */ + return NULL; + } + } + iface = (MonoClass **)*iter; + iface++; + if (iface < &klass->interfaces [klass->interface_count]) { + *iter = iface; + return *iface; + } + return NULL; +} + +static void +setup_nested_types (MonoClass *klass) +{ + MonoError error; + GList *classes, *nested_classes, *l; + int i; + + if (klass->nested_classes_inited) + return; + + if (!klass->type_token) { + mono_loader_lock (); + klass->nested_classes_inited = TRUE; + mono_loader_unlock (); + return; + } + + i = mono_metadata_nesting_typedef (klass->image, klass->type_token, 1); + classes = NULL; + while (i) { + MonoClass* nclass; + guint32 cols [MONO_NESTED_CLASS_SIZE]; + mono_metadata_decode_row (&klass->image->tables [MONO_TABLE_NESTEDCLASS], i - 1, cols, MONO_NESTED_CLASS_SIZE); + nclass = mono_class_create_from_typedef (klass->image, MONO_TOKEN_TYPE_DEF | cols [MONO_NESTED_CLASS_NESTED], &error); + if (!mono_error_ok (&error)) { + /*FIXME don't swallow the error message*/ + mono_error_cleanup (&error); + + i = mono_metadata_nesting_typedef (klass->image, klass->type_token, i + 1); + continue; + } + + classes = g_list_prepend (classes, nclass); + + i = mono_metadata_nesting_typedef (klass->image, klass->type_token, i + 1); + } + + nested_classes = NULL; + for (l = classes; l; l = l->next) + nested_classes = g_list_prepend_image (klass->image, nested_classes, l->data); + g_list_free (classes); + + mono_loader_lock (); + if (!klass->nested_classes_inited) { + mono_class_set_nested_classes_property (klass, nested_classes); + mono_memory_barrier (); + klass->nested_classes_inited = TRUE; + } + mono_loader_unlock (); +} + +/** + * mono_class_get_nested_types + * \param klass the \c MonoClass to act on + * + * This routine is an iterator routine for retrieving the nested types of a class. + * This works only if \p klass is non-generic, or a generic type definition. + * + * You must pass a \c gpointer that points to zero and is treated as an opaque handle to + * iterate over all of the elements. When no more values are + * available, the return value is NULL. + * + * \returns a \c Monoclass* on each invocation, or NULL when no more are available. + */ +MonoClass* +mono_class_get_nested_types (MonoClass* klass, gpointer *iter) +{ + GList *item; + + if (!iter) + return NULL; + if (!klass->nested_classes_inited) + setup_nested_types (klass); + + if (!*iter) { + GList *nested_classes = mono_class_get_nested_classes_property (klass); + /* start from the first */ + if (nested_classes) { + *iter = nested_classes; + return (MonoClass *)nested_classes->data; + } else { + /* no nested types */ + return NULL; + } + } + item = (GList *)*iter; + item = item->next; + if (item) { + *iter = item; + return (MonoClass *)item->data; + } + return NULL; +} + + +/** + * mono_class_is_delegate + * \param klass the \c MonoClass to act on + * + * \returns TRUE if the \c MonoClass represents a \c System.Delegate. + */ +mono_bool +mono_class_is_delegate (MonoClass *klass) +{ + return klass->delegate; +} + +/** + * mono_class_implements_interface + * \param klass The MonoClass to act on + * \param interface The interface to check if \p klass implements. + * + * \returns TRUE if \p klass implements \p interface. + */ +mono_bool +mono_class_implements_interface (MonoClass* klass, MonoClass* iface) +{ + return mono_class_is_assignable_from (iface, klass); +} + +/** + * mono_field_get_name: + * \param field the \c MonoClassField to act on + * + * \returns The name of the field. + */ +const char* +mono_field_get_name (MonoClassField *field) +{ + return field->name; +} + +/** + * mono_field_get_type: + * \param field the \c MonoClassField to act on + * \returns \c MonoType of the field. + */ +MonoType* +mono_field_get_type (MonoClassField *field) +{ + MonoError error; + MonoType *type = mono_field_get_type_checked (field, &error); + if (!mono_error_ok (&error)) { + mono_trace_warning (MONO_TRACE_TYPE, "Could not load field's type due to %s", mono_error_get_message (&error)); + mono_error_cleanup (&error); + } + return type; +} + + +/** + * mono_field_get_type_checked: + * \param field the \c MonoClassField to act on + * \param error used to return any error found while retrieving \p field type + * + * \returns \c MonoType of the field. + */ +MonoType* +mono_field_get_type_checked (MonoClassField *field, MonoError *error) +{ + error_init (error); + if (!field->type) + mono_field_resolve_type (field, error); + return field->type; +} + +/** + * mono_field_get_parent: + * \param field the \c MonoClassField to act on + * + * \returns \c MonoClass where the field was defined. + */ +MonoClass* +mono_field_get_parent (MonoClassField *field) +{ + return field->parent; +} + +/** + * mono_field_get_flags; + * \param field the \c MonoClassField to act on + * + * The metadata flags for a field are encoded using the + * \c FIELD_ATTRIBUTE_* constants. See the \c tabledefs.h file for details. + * + * \returns The flags for the field. + */ +guint32 +mono_field_get_flags (MonoClassField *field) +{ + if (!field->type) + return mono_field_resolve_flags (field); + return field->type->attrs; +} + +/** + * mono_field_get_offset: + * \param field the \c MonoClassField to act on + * + * \returns The field offset. + */ +guint32 +mono_field_get_offset (MonoClassField *field) +{ + mono_class_setup_fields(field->parent); + return field->offset; +} + +static const char * +mono_field_get_rva (MonoClassField *field) +{ + guint32 rva; + int field_index; + MonoClass *klass = field->parent; + MonoFieldDefaultValue *def_values; + + g_assert (field->type->attrs & FIELD_ATTRIBUTE_HAS_FIELD_RVA); + + def_values = mono_class_get_field_def_values (klass); + if (!def_values) { + def_values = (MonoFieldDefaultValue *)mono_class_alloc0 (klass, sizeof (MonoFieldDefaultValue) * mono_class_get_field_count (klass)); + + mono_class_set_field_def_values (klass, def_values); + } + + field_index = mono_field_get_index (field); + + if (!def_values [field_index].data && !image_is_dynamic (klass->image)) { + int first_field_idx = mono_class_get_first_field_idx (klass); + mono_metadata_field_info (field->parent->image, first_field_idx + field_index, NULL, &rva, NULL); + if (!rva) + g_warning ("field %s in %s should have RVA data, but hasn't", mono_field_get_name (field), field->parent->name); + def_values [field_index].data = mono_image_rva_map (field->parent->image, rva); + } + + return def_values [field_index].data; +} + +/** + * mono_field_get_data: + * \param field the \c MonoClassField to act on + * + * \returns A pointer to the metadata constant value or to the field + * data if it has an RVA flag. + */ +const char * +mono_field_get_data (MonoClassField *field) +{ + if (field->type->attrs & FIELD_ATTRIBUTE_HAS_DEFAULT) { + MonoTypeEnum def_type; + + return mono_class_get_field_default_value (field, &def_type); + } else if (field->type->attrs & FIELD_ATTRIBUTE_HAS_FIELD_RVA) { + return mono_field_get_rva (field); + } else { + return NULL; + } +} + +/** + * mono_property_get_name: + * \param prop the \c MonoProperty to act on + * \returns The name of the property + */ +const char* +mono_property_get_name (MonoProperty *prop) +{ + return prop->name; +} + +/** + * mono_property_get_set_method + * \param prop the \c MonoProperty to act on. + * \returns The setter method of the property, a \c MonoMethod. + */ +MonoMethod* +mono_property_get_set_method (MonoProperty *prop) +{ + return prop->set; +} + +/** + * mono_property_get_get_method + * \param prop the MonoProperty to act on. + * \returns The getter method of the property (A \c MonoMethod) + */ +MonoMethod* +mono_property_get_get_method (MonoProperty *prop) +{ + return prop->get; +} + +/** + * mono_property_get_parent: + * \param prop the \c MonoProperty to act on. + * \returns The \c MonoClass where the property was defined. + */ +MonoClass* +mono_property_get_parent (MonoProperty *prop) +{ + return prop->parent; +} + +/** + * mono_property_get_flags: + * \param prop the \c MonoProperty to act on. + * + * The metadata flags for a property are encoded using the + * \c PROPERTY_ATTRIBUTE_* constants. See the \c tabledefs.h file for details. + * + * \returns The flags for the property. + */ +guint32 +mono_property_get_flags (MonoProperty *prop) +{ + return prop->attrs; +} + +/** + * mono_event_get_name: + * \param event the MonoEvent to act on + * \returns The name of the event. + */ +const char* +mono_event_get_name (MonoEvent *event) +{ + return event->name; +} + +/** + * mono_event_get_add_method: + * \param event The \c MonoEvent to act on. + * \returns The \c add method for the event, a \c MonoMethod. + */ +MonoMethod* +mono_event_get_add_method (MonoEvent *event) +{ + return event->add; +} + +/** + * mono_event_get_remove_method: + * \param event The \c MonoEvent to act on. + * \returns The \c remove method for the event, a \c MonoMethod. + */ +MonoMethod* +mono_event_get_remove_method (MonoEvent *event) +{ + return event->remove; +} + +/** + * mono_event_get_raise_method: + * \param event The \c MonoEvent to act on. + * \returns The \c raise method for the event, a \c MonoMethod. + */ +MonoMethod* +mono_event_get_raise_method (MonoEvent *event) +{ + return event->raise; +} + +/** + * mono_event_get_parent: + * \param event the MonoEvent to act on. + * \returns The \c MonoClass where the event is defined. + */ +MonoClass* +mono_event_get_parent (MonoEvent *event) +{ + return event->parent; +} + +/** + * mono_event_get_flags + * \param event the \c MonoEvent to act on. + * + * The metadata flags for an event are encoded using the + * \c EVENT_* constants. See the \c tabledefs.h file for details. + * + * \returns The flags for the event. + */ +guint32 +mono_event_get_flags (MonoEvent *event) +{ + return event->attrs; +} + +/** + * mono_class_get_method_from_name: + * \param klass where to look for the method + * \param name name of the method + * \param param_count number of parameters. -1 for any number. + * + * Obtains a \c MonoMethod with a given name and number of parameters. + * It only works if there are no multiple signatures for any given method name. + */ +MonoMethod * +mono_class_get_method_from_name (MonoClass *klass, const char *name, int param_count) +{ + return mono_class_get_method_from_name_flags (klass, name, param_count, 0); +} + +static MonoMethod* +find_method_in_metadata (MonoClass *klass, const char *name, int param_count, int flags) +{ + MonoMethod *res = NULL; + int i; + + /* Search directly in the metadata to avoid calling setup_methods () */ + int first_idx = mono_class_get_first_method_idx (klass); + int mcount = mono_class_get_method_count (klass); + for (i = 0; i < mcount; ++i) { + MonoError error; + guint32 cols [MONO_METHOD_SIZE]; + MonoMethod *method; + MonoMethodSignature *sig; + + /* first_idx points into the methodptr table */ + mono_metadata_decode_table_row (klass->image, MONO_TABLE_METHOD, first_idx + i, cols, MONO_METHOD_SIZE); + + if (!strcmp (mono_metadata_string_heap (klass->image, cols [MONO_METHOD_NAME]), name)) { + method = mono_get_method_checked (klass->image, MONO_TOKEN_METHOD_DEF | (first_idx + i + 1), klass, NULL, &error); + if (!method) { + mono_error_cleanup (&error); /* FIXME don't swallow the error */ + continue; + } + if (param_count == -1) { + res = method; + break; + } + sig = mono_method_signature_checked (method, &error); + if (!sig) { + mono_error_cleanup (&error); /* FIXME don't swallow the error */ + continue; + } + if (sig->param_count == param_count) { + res = method; + break; + } + } + } + + return res; +} + +/** + * mono_class_get_method_from_name_flags: + * \param klass where to look for the method + * \param name_space name of the method + * \param param_count number of parameters. -1 for any number. + * \param flags flags which must be set in the method + * + * Obtains a \c MonoMethod with a given name and number of parameters. + * It only works if there are no multiple signatures for any given method name. + */ +MonoMethod * +mono_class_get_method_from_name_flags (MonoClass *klass, const char *name, int param_count, int flags) +{ + MonoMethod *res = NULL; + int i; + + mono_class_init (klass); + + if (mono_class_is_ginst (klass) && !klass->methods) { + res = mono_class_get_method_from_name_flags (mono_class_get_generic_class (klass)->container_class, name, param_count, flags); + if (res) { + MonoError error; + res = mono_class_inflate_generic_method_full_checked (res, klass, mono_class_get_context (klass), &error); + if (!mono_error_ok (&error)) + mono_error_cleanup (&error); /*FIXME don't swallow the error */ + } + return res; + } + + if (klass->methods || !MONO_CLASS_HAS_STATIC_METADATA (klass)) { + mono_class_setup_methods (klass); + /* + We can't fail lookup of methods otherwise the runtime will burst in flames on all sort of places. + See mono/tests/array_load_exception.il + FIXME we should better report this error to the caller + */ + if (!klass->methods) + return NULL; + int mcount = mono_class_get_method_count (klass); + for (i = 0; i < mcount; ++i) { + MonoMethod *method = klass->methods [i]; + + if (method->name[0] == name [0] && + !strcmp (name, method->name) && + (param_count == -1 || mono_method_signature (method)->param_count == param_count) && + ((method->flags & flags) == flags)) { + res = method; + break; + } + } + } + else { + res = find_method_in_metadata (klass, name, param_count, flags); + } + + return res; +} + +/** + * mono_class_set_failure: + * \param klass class in which the failure was detected + * \param ex_type the kind of exception/error to be thrown (later) + * \param ex_data exception data (specific to each type of exception/error) + * + * Keep a detected failure informations in the class for later processing. + * Note that only the first failure is kept. + * + * LOCKING: Acquires the loader lock. + */ +static gboolean +mono_class_set_failure (MonoClass *klass, MonoErrorBoxed *boxed_error) +{ + g_assert (boxed_error != NULL); + + if (mono_class_has_failure (klass)) + return FALSE; + + mono_loader_lock (); + klass->has_failure = 1; + mono_class_set_exception_data (klass, boxed_error); + mono_loader_unlock (); + + return TRUE; +} + +gboolean +mono_class_has_failure (const MonoClass *klass) +{ + g_assert (klass != NULL); + return klass->has_failure != 0; +} + + +/** + * mono_class_set_type_load_failure: + * \param klass class in which the failure was detected + * \param fmt \c printf -style error message string. + * + * Collect detected failure informaion in the class for later processing. + * The error is stored as a MonoErrorBoxed as with mono_error_set_type_load_class() + * Note that only the first failure is kept. + * + * LOCKING: Acquires the loader lock. + * + * \returns FALSE if a failure was already set on the class, or TRUE otherwise. + */ +gboolean +mono_class_set_type_load_failure (MonoClass *klass, const char * fmt, ...) +{ + MonoError prepare_error; + va_list args; + + if (mono_class_has_failure (klass)) + return FALSE; + + error_init (&prepare_error); + + va_start (args, fmt); + mono_error_vset_type_load_class (&prepare_error, klass, fmt, args); + va_end (args); + + MonoErrorBoxed *box = mono_error_box (&prepare_error, klass->image); + mono_error_cleanup (&prepare_error); + return mono_class_set_failure (klass, box); +} + +/** + * mono_classes_init: + * + * Initialize the resources used by this module. + * Known racy counters: `class_gparam_count`, `classes_size` and `inflated_methods_size` + */ +MONO_NO_SANITIZE_THREAD +void +mono_classes_init (void) +{ + mono_os_mutex_init (&classes_mutex); + + mono_native_tls_alloc (&setup_fields_tls_id, NULL); + mono_native_tls_alloc (&init_pending_tls_id, NULL); + + mono_counters_register ("MonoClassDef count", + MONO_COUNTER_METADATA | MONO_COUNTER_INT, &class_def_count); + mono_counters_register ("MonoClassGtd count", + MONO_COUNTER_METADATA | MONO_COUNTER_INT, &class_gtd_count); + mono_counters_register ("MonoClassGenericInst count", + MONO_COUNTER_METADATA | MONO_COUNTER_INT, &class_ginst_count); + mono_counters_register ("MonoClassGenericParam count", + MONO_COUNTER_METADATA | MONO_COUNTER_INT, &class_gparam_count); + mono_counters_register ("MonoClassArray count", + MONO_COUNTER_METADATA | MONO_COUNTER_INT, &class_array_count); + mono_counters_register ("MonoClassPointer count", + MONO_COUNTER_METADATA | MONO_COUNTER_INT, &class_pointer_count); + mono_counters_register ("Inflated methods size", + MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &inflated_methods_size); + mono_counters_register ("Inflated classes size", + MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &inflated_classes_size); + mono_counters_register ("MonoClass size", + MONO_COUNTER_METADATA | MONO_COUNTER_INT, &classes_size); +} + +/** + * mono_classes_cleanup: + * + * Free the resources used by this module. + */ +void +mono_classes_cleanup (void) +{ + mono_native_tls_free (setup_fields_tls_id); + mono_native_tls_free (init_pending_tls_id); + + if (global_interface_bitset) + mono_bitset_free (global_interface_bitset); + global_interface_bitset = NULL; + mono_os_mutex_destroy (&classes_mutex); +} + +/** + * mono_class_get_exception_for_failure: + * \param klass class in which the failure was detected + * + * \returns a constructed MonoException than the caller can then throw + * using mono_raise_exception - or NULL if no failure is present (or + * doesn't result in an exception). + */ +MonoException* +mono_class_get_exception_for_failure (MonoClass *klass) +{ + if (!mono_class_has_failure (klass)) + return NULL; + MonoError unboxed_error; + error_init (&unboxed_error); + mono_error_set_for_class_failure (&unboxed_error, klass); + return mono_error_convert_to_exception (&unboxed_error); +} + +static gboolean +is_nesting_type (MonoClass *outer_klass, MonoClass *inner_klass) + { + outer_klass = mono_class_get_generic_type_definition (outer_klass); + inner_klass = mono_class_get_generic_type_definition (inner_klass); + do { + if (outer_klass == inner_klass) + return TRUE; + inner_klass = inner_klass->nested_in; + } while (inner_klass); + return FALSE; +} + +MonoClass * +mono_class_get_generic_type_definition (MonoClass *klass) +{ + MonoGenericClass *gklass = mono_class_try_get_generic_class (klass); + return gklass ? gklass->container_class : klass; +} + +/* + * Check if @klass is a subtype of @parent ignoring generic instantiations. + * + * Generic instantiations are ignored for all super types of @klass. + * + * Visibility checks ignoring generic instantiations. + */ +gboolean +mono_class_has_parent_and_ignore_generics (MonoClass *klass, MonoClass *parent) +{ + int i; + klass = mono_class_get_generic_type_definition (klass); + parent = mono_class_get_generic_type_definition (parent); + mono_class_setup_supertypes (klass); + + for (i = 0; i < klass->idepth; ++i) { + if (parent == mono_class_get_generic_type_definition (klass->supertypes [i])) + return TRUE; + } + return FALSE; +} +/* + * Subtype can only access parent members with family protection if the site object + * is subclass of Subtype. For example: + * class A { protected int x; } + * class B : A { + * void valid_access () { + * B b; + * b.x = 0; + * } + * void invalid_access () { + * A a; + * a.x = 0; + * } + * } + * */ +static gboolean +is_valid_family_access (MonoClass *access_klass, MonoClass *member_klass, MonoClass *context_klass) +{ + if (!mono_class_has_parent_and_ignore_generics (access_klass, member_klass)) + return FALSE; + + if (context_klass == NULL) + return TRUE; + /*if access_klass is not member_klass context_klass must be type compat*/ + if (access_klass != member_klass && !mono_class_has_parent_and_ignore_generics (context_klass, access_klass)) + return FALSE; + return TRUE; +} + +static gboolean +can_access_internals (MonoAssembly *accessing, MonoAssembly* accessed) +{ + GSList *tmp; + if (accessing == accessed) + return TRUE; + if (!accessed || !accessing) + return FALSE; + + /* extra safety under CoreCLR - the runtime does not verify the strongname signatures + * anywhere so untrusted friends are not safe to access platform's code internals */ + if (mono_security_core_clr_enabled ()) { + if (!mono_security_core_clr_can_access_internals (accessing->image, accessed->image)) + return FALSE; + } + + mono_assembly_load_friends (accessed); + for (tmp = accessed->friend_assembly_names; tmp; tmp = tmp->next) { + MonoAssemblyName *friend_ = (MonoAssemblyName *)tmp->data; + /* Be conservative with checks */ + if (!friend_->name) + continue; + if (g_ascii_strcasecmp (accessing->aname.name, friend_->name)) + continue; + if (friend_->public_key_token [0]) { + if (!accessing->aname.public_key_token [0]) + continue; + if (!mono_public_tokens_are_equal (friend_->public_key_token, accessing->aname.public_key_token)) + continue; + } + return TRUE; + } + return FALSE; +} + +/* + * If klass is a generic type or if it is derived from a generic type, return the + * MonoClass of the generic definition + * Returns NULL if not found + */ +static MonoClass* +get_generic_definition_class (MonoClass *klass) +{ + while (klass) { + MonoGenericClass *gklass = mono_class_try_get_generic_class (klass); + if (gklass && gklass->container_class) + return gklass->container_class; + klass = klass->parent; + } + return NULL; +} + +static gboolean +can_access_instantiation (MonoClass *access_klass, MonoGenericInst *ginst) +{ + int i; + for (i = 0; i < ginst->type_argc; ++i) { + MonoType *type = ginst->type_argv[i]; + switch (type->type) { + case MONO_TYPE_SZARRAY: + if (!can_access_type (access_klass, type->data.klass)) + return FALSE; + break; + case MONO_TYPE_ARRAY: + if (!can_access_type (access_klass, type->data.array->eklass)) + return FALSE; + break; + case MONO_TYPE_PTR: + if (!can_access_type (access_klass, mono_class_from_mono_type (type->data.type))) + return FALSE; + break; + case MONO_TYPE_CLASS: + case MONO_TYPE_VALUETYPE: + case MONO_TYPE_GENERICINST: + if (!can_access_type (access_klass, mono_class_from_mono_type (type))) + return FALSE; + default: + break; + } + } + return TRUE; +} + +static gboolean +can_access_type (MonoClass *access_klass, MonoClass *member_klass) +{ + int access_level; + + if (access_klass == member_klass) + return TRUE; + + if (access_klass->image->assembly && access_klass->image->assembly->corlib_internal) + return TRUE; + + if (access_klass->element_class && !access_klass->enumtype) + access_klass = access_klass->element_class; + + if (member_klass->element_class && !member_klass->enumtype) + member_klass = member_klass->element_class; + + access_level = mono_class_get_flags (member_klass) & TYPE_ATTRIBUTE_VISIBILITY_MASK; + + if (member_klass->byval_arg.type == MONO_TYPE_VAR || member_klass->byval_arg.type == MONO_TYPE_MVAR) + return TRUE; + + if (mono_class_is_ginst (member_klass) && !can_access_instantiation (access_klass, mono_class_get_generic_class (member_klass)->context.class_inst)) + return FALSE; + + if (is_nesting_type (access_klass, member_klass) || (access_klass->nested_in && is_nesting_type (access_klass->nested_in, member_klass))) + return TRUE; + + if (member_klass->nested_in && !can_access_type (access_klass, member_klass->nested_in)) + return FALSE; + + /*Non nested type with nested visibility. We just fail it.*/ + if (access_level >= TYPE_ATTRIBUTE_NESTED_PRIVATE && access_level <= TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM && member_klass->nested_in == NULL) + return FALSE; + + switch (access_level) { + case TYPE_ATTRIBUTE_NOT_PUBLIC: + return can_access_internals (access_klass->image->assembly, member_klass->image->assembly); + + case TYPE_ATTRIBUTE_PUBLIC: + return TRUE; + + case TYPE_ATTRIBUTE_NESTED_PUBLIC: + return TRUE; + + case TYPE_ATTRIBUTE_NESTED_PRIVATE: + return is_nesting_type (member_klass, access_klass); + + case TYPE_ATTRIBUTE_NESTED_FAMILY: + return mono_class_has_parent_and_ignore_generics (access_klass, member_klass->nested_in); + + case TYPE_ATTRIBUTE_NESTED_ASSEMBLY: + return can_access_internals (access_klass->image->assembly, member_klass->image->assembly); + + case TYPE_ATTRIBUTE_NESTED_FAM_AND_ASSEM: + return can_access_internals (access_klass->image->assembly, member_klass->nested_in->image->assembly) && + mono_class_has_parent_and_ignore_generics (access_klass, member_klass->nested_in); + + case TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM: + return can_access_internals (access_klass->image->assembly, member_klass->nested_in->image->assembly) || + mono_class_has_parent_and_ignore_generics (access_klass, member_klass->nested_in); + } + return FALSE; +} + +/* FIXME: check visibility of type, too */ +static gboolean +can_access_member (MonoClass *access_klass, MonoClass *member_klass, MonoClass* context_klass, int access_level) +{ + MonoClass *member_generic_def; + if (access_klass->image->assembly && access_klass->image->assembly->corlib_internal) + return TRUE; + + MonoGenericClass *access_gklass = mono_class_try_get_generic_class (access_klass); + if (((access_gklass && access_gklass->container_class) || + mono_class_is_gtd (access_klass)) && + (member_generic_def = get_generic_definition_class (member_klass))) { + MonoClass *access_container; + + if (mono_class_is_gtd (access_klass)) + access_container = access_klass; + else + access_container = access_gklass->container_class; + + if (can_access_member (access_container, member_generic_def, context_klass, access_level)) + return TRUE; + } + + /* Partition I 8.5.3.2 */ + /* the access level values are the same for fields and methods */ + switch (access_level) { + case FIELD_ATTRIBUTE_COMPILER_CONTROLLED: + /* same compilation unit */ + return access_klass->image == member_klass->image; + case FIELD_ATTRIBUTE_PRIVATE: + return access_klass == member_klass; + case FIELD_ATTRIBUTE_FAM_AND_ASSEM: + if (is_valid_family_access (access_klass, member_klass, context_klass) && + can_access_internals (access_klass->image->assembly, member_klass->image->assembly)) + return TRUE; + return FALSE; + case FIELD_ATTRIBUTE_ASSEMBLY: + return can_access_internals (access_klass->image->assembly, member_klass->image->assembly); + case FIELD_ATTRIBUTE_FAMILY: + if (is_valid_family_access (access_klass, member_klass, context_klass)) + return TRUE; + return FALSE; + case FIELD_ATTRIBUTE_FAM_OR_ASSEM: + if (is_valid_family_access (access_klass, member_klass, context_klass)) + return TRUE; + return can_access_internals (access_klass->image->assembly, member_klass->image->assembly); + case FIELD_ATTRIBUTE_PUBLIC: + return TRUE; + } + return FALSE; +} + +/** + * mono_method_can_access_field: + * \param method Method that will attempt to access the field + * \param field the field to access + * + * Used to determine if a method is allowed to access the specified field. + * + * \returns TRUE if the given \p method is allowed to access the \p field while following + * the accessibility rules of the CLI. + */ +gboolean +mono_method_can_access_field (MonoMethod *method, MonoClassField *field) +{ + /* FIXME: check all overlapping fields */ + int can = can_access_member (method->klass, field->parent, NULL, mono_field_get_type (field)->attrs & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK); + if (!can) { + MonoClass *nested = method->klass->nested_in; + while (nested) { + can = can_access_member (nested, field->parent, NULL, mono_field_get_type (field)->attrs & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK); + if (can) + return TRUE; + nested = nested->nested_in; + } + } + return can; +} + +/** + * mono_method_can_access_method: + * \param method Method that will attempt to access the other method + * \param called the method that we want to probe for accessibility. + * + * Used to determine if the \p method is allowed to access the specified \p called method. + * + * \returns TRUE if the given \p method is allowed to invoke the \p called while following + * the accessibility rules of the CLI. + */ +gboolean +mono_method_can_access_method (MonoMethod *method, MonoMethod *called) +{ + method = mono_method_get_method_definition (method); + called = mono_method_get_method_definition (called); + return mono_method_can_access_method_full (method, called, NULL); +} + +/* + * mono_method_can_access_method_full: + * @method: The caller method + * @called: The called method + * @context_klass: The static type on stack of the owner @called object used + * + * This function must be used with instance calls, as they have more strict family accessibility. + * It can be used with static methods, but context_klass should be NULL. + * + * Returns: TRUE if caller have proper visibility and acessibility to @called + */ +gboolean +mono_method_can_access_method_full (MonoMethod *method, MonoMethod *called, MonoClass *context_klass) +{ + /* Wrappers are except from access checks */ + if (method->wrapper_type != MONO_WRAPPER_NONE || called->wrapper_type != MONO_WRAPPER_NONE) + return TRUE; + + MonoClass *access_class = method->klass; + MonoClass *member_class = called->klass; + int can = can_access_member (access_class, member_class, context_klass, called->flags & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK); + if (!can) { + MonoClass *nested = access_class->nested_in; + while (nested) { + can = can_access_member (nested, member_class, context_klass, called->flags & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK); + if (can) + break; + nested = nested->nested_in; + } + } + + if (!can) + return FALSE; + + can = can_access_type (access_class, member_class); + if (!can) { + MonoClass *nested = access_class->nested_in; + while (nested) { + can = can_access_type (nested, member_class); + if (can) + break; + nested = nested->nested_in; + } + } + + if (!can) + return FALSE; + + if (called->is_inflated) { + MonoMethodInflated * infl = (MonoMethodInflated*)called; + if (infl->context.method_inst && !can_access_instantiation (access_class, infl->context.method_inst)) + return FALSE; + } + + return TRUE; +} + + +/* + * mono_method_can_access_field_full: + * @method: The caller method + * @field: The accessed field + * @context_klass: The static type on stack of the owner @field object used + * + * This function must be used with instance fields, as they have more strict family accessibility. + * It can be used with static fields, but context_klass should be NULL. + * + * Returns: TRUE if caller have proper visibility and acessibility to @field + */ +gboolean +mono_method_can_access_field_full (MonoMethod *method, MonoClassField *field, MonoClass *context_klass) +{ + MonoClass *access_class = method->klass; + MonoClass *member_class = field->parent; + /* FIXME: check all overlapping fields */ + int can = can_access_member (access_class, member_class, context_klass, field->type->attrs & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK); + if (!can) { + MonoClass *nested = access_class->nested_in; + while (nested) { + can = can_access_member (nested, member_class, context_klass, field->type->attrs & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK); + if (can) + break; + nested = nested->nested_in; + } + } + + if (!can) + return FALSE; + + can = can_access_type (access_class, member_class); + if (!can) { + MonoClass *nested = access_class->nested_in; + while (nested) { + can = can_access_type (nested, member_class); + if (can) + break; + nested = nested->nested_in; + } + } + + if (!can) + return FALSE; + return TRUE; +} + +/* + * mono_class_can_access_class: + * @source_class: The source class + * @target_class: The accessed class + * + * This function returns is @target_class is visible to @source_class + * + * Returns: TRUE if source have proper visibility and acessibility to target + */ +gboolean +mono_class_can_access_class (MonoClass *source_class, MonoClass *target_class) +{ + return can_access_type (source_class, target_class); +} + +/** + * mono_type_is_valid_enum_basetype: + * \param type The MonoType to check + * \returns TRUE if the type can be used as the basetype of an enum + */ +gboolean mono_type_is_valid_enum_basetype (MonoType * type) { + switch (type->type) { + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_CHAR: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_I: + case MONO_TYPE_U: + return TRUE; + default: + return FALSE; + } +} + +/** + * mono_class_is_valid_enum: + * \param klass An enum class to be validated + * + * This method verify the required properties an enum should have. + * + * FIXME: TypeBuilder enums are allowed to implement interfaces, but since they cannot have methods, only empty interfaces are possible + * FIXME: enum types are not allowed to have a cctor, but mono_reflection_create_runtime_class sets has_cctor to 1 for all types + * FIXME: TypeBuilder enums can have any kind of static fields, but the spec is very explicit about that (P II 14.3) + * + * \returns TRUE if the informed enum class is valid + */ +gboolean +mono_class_is_valid_enum (MonoClass *klass) +{ + MonoClassField * field; + gpointer iter = NULL; + gboolean found_base_field = FALSE; + + g_assert (klass->enumtype); + /* we cannot test against mono_defaults.enum_class, or mcs won't be able to compile the System namespace*/ + if (!klass->parent || strcmp (klass->parent->name, "Enum") || strcmp (klass->parent->name_space, "System") ) { + return FALSE; + } + + if (!mono_class_is_auto_layout (klass)) + return FALSE; + + while ((field = mono_class_get_fields (klass, &iter))) { + if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC)) { + if (found_base_field) + return FALSE; + found_base_field = TRUE; + if (!mono_type_is_valid_enum_basetype (field->type)) + return FALSE; + } + } + + if (!found_base_field) + return FALSE; + + if (mono_class_get_method_count (klass) > 0) + return FALSE; + + return TRUE; +} + +gboolean +mono_generic_class_is_generic_type_definition (MonoGenericClass *gklass) +{ + return gklass->context.class_inst == mono_class_get_generic_container (gklass->container_class)->context.class_inst; +} + +/* + * mono_class_setup_interface_id: + * + * Initializes MonoClass::interface_id if required. + * + * LOCKING: Acquires the loader lock. + */ +void +mono_class_setup_interface_id (MonoClass *klass) +{ + g_assert (MONO_CLASS_IS_INTERFACE (klass)); + mono_loader_lock (); + if (!klass->interface_id) + klass->interface_id = mono_get_unique_iid (klass); + mono_loader_unlock (); +} + +/* + * mono_class_setup_interfaces: + * + * Initialize klass->interfaces/interfaces_count. + * LOCKING: Acquires the loader lock. + * This function can fail the type. + */ +void +mono_class_setup_interfaces (MonoClass *klass, MonoError *error) +{ + int i, interface_count; + MonoClass **interfaces; + + error_init (error); + + if (klass->interfaces_inited) + return; + + if (klass->rank == 1 && klass->byval_arg.type != MONO_TYPE_ARRAY) { + MonoType *args [1]; + + /* IList and IReadOnlyList -> 2x if enum*/ + interface_count = klass->element_class->enumtype ? 4 : 2; + interfaces = (MonoClass **)mono_image_alloc0 (klass->image, sizeof (MonoClass*) * interface_count); + + args [0] = &klass->element_class->byval_arg; + interfaces [0] = mono_class_bind_generic_parameters ( + mono_defaults.generic_ilist_class, 1, args, FALSE); + interfaces [1] = mono_class_bind_generic_parameters ( + mono_defaults.generic_ireadonlylist_class, 1, args, FALSE); + if (klass->element_class->enumtype) { + args [0] = mono_class_enum_basetype (klass->element_class); + interfaces [2] = mono_class_bind_generic_parameters ( + mono_defaults.generic_ilist_class, 1, args, FALSE); + interfaces [3] = mono_class_bind_generic_parameters ( + mono_defaults.generic_ireadonlylist_class, 1, args, FALSE); + } + } else if (mono_class_is_ginst (klass)) { + MonoClass *gklass = mono_class_get_generic_class (klass)->container_class; + + mono_class_setup_interfaces (gklass, error); + if (!mono_error_ok (error)) { + mono_class_set_type_load_failure (klass, "Could not setup the interfaces"); + return; + } + + interface_count = gklass->interface_count; + interfaces = mono_class_new0 (klass, MonoClass *, interface_count); + for (i = 0; i < interface_count; i++) { + interfaces [i] = mono_class_inflate_generic_class_checked (gklass->interfaces [i], mono_generic_class_get_context (mono_class_get_generic_class (klass)), error); + if (!mono_error_ok (error)) { + mono_class_set_type_load_failure (klass, "Could not setup the interfaces"); + return; + } + } + } else { + interface_count = 0; + interfaces = NULL; + } + + mono_loader_lock (); + if (!klass->interfaces_inited) { + klass->interface_count = interface_count; + klass->interfaces = interfaces; + + mono_memory_barrier (); + + klass->interfaces_inited = TRUE; + } + mono_loader_unlock (); +} + +static void +mono_field_resolve_type (MonoClassField *field, MonoError *error) +{ + MonoClass *klass = field->parent; + MonoImage *image = klass->image; + MonoClass *gtd = mono_class_is_ginst (klass) ? mono_class_get_generic_type_definition (klass) : NULL; + MonoType *ftype; + int field_idx = field - klass->fields; + + error_init (error); + + if (gtd) { + MonoClassField *gfield = >d->fields [field_idx]; + MonoType *gtype = mono_field_get_type_checked (gfield, error); + if (!mono_error_ok (error)) { + char *full_name = mono_type_get_full_name (gtd); + mono_class_set_type_load_failure (klass, "Could not load generic type of field '%s:%s' (%d) due to: %s", full_name, gfield->name, field_idx, mono_error_get_message (error)); + g_free (full_name); + } + + ftype = mono_class_inflate_generic_type_no_copy (image, gtype, mono_class_get_context (klass), error); + if (!mono_error_ok (error)) { + char *full_name = mono_type_get_full_name (klass); + mono_class_set_type_load_failure (klass, "Could not load instantiated type of field '%s:%s' (%d) due to: %s", full_name, field->name, field_idx, mono_error_get_message (error)); + g_free (full_name); + } + } else { + const char *sig; + guint32 cols [MONO_FIELD_SIZE]; + MonoGenericContainer *container = NULL; + int idx = mono_class_get_first_field_idx (klass) + field_idx; + + /*FIXME, in theory we do not lazy load SRE fields*/ + g_assert (!image_is_dynamic (image)); + + if (mono_class_is_gtd (klass)) { + container = mono_class_get_generic_container (klass); + } else if (gtd) { + container = mono_class_get_generic_container (gtd); + g_assert (container); + } + + /* first_field_idx and idx points into the fieldptr table */ + mono_metadata_decode_table_row (image, MONO_TABLE_FIELD, idx, cols, MONO_FIELD_SIZE); + + if (!mono_verifier_verify_field_signature (image, cols [MONO_FIELD_SIGNATURE], NULL)) { + char *full_name = mono_type_get_full_name (klass); + mono_error_set_type_load_class (error, klass, "Could not verify field '%s:%s' signature", full_name, field->name);; + mono_class_set_type_load_failure (klass, "%s", mono_error_get_message (error)); + g_free (full_name); + return; + } + + sig = mono_metadata_blob_heap (image, cols [MONO_FIELD_SIGNATURE]); + + mono_metadata_decode_value (sig, &sig); + /* FIELD signature == 0x06 */ + g_assert (*sig == 0x06); + + ftype = mono_metadata_parse_type_checked (image, container, cols [MONO_FIELD_FLAGS], FALSE, sig + 1, &sig, error); + if (!ftype) { + char *full_name = mono_type_get_full_name (klass); + mono_class_set_type_load_failure (klass, "Could not load type of field '%s:%s' (%d) due to: %s", full_name, field->name, field_idx, mono_error_get_message (error)); + g_free (full_name); + } + } + mono_memory_barrier (); + field->type = ftype; +} + +static guint32 +mono_field_resolve_flags (MonoClassField *field) +{ + MonoClass *klass = field->parent; + MonoImage *image = klass->image; + MonoClass *gtd = mono_class_is_ginst (klass) ? mono_class_get_generic_type_definition (klass) : NULL; + int field_idx = field - klass->fields; + + if (gtd) { + MonoClassField *gfield = >d->fields [field_idx]; + return mono_field_get_flags (gfield); + } else { + int idx = mono_class_get_first_field_idx (klass) + field_idx; + + /*FIXME, in theory we do not lazy load SRE fields*/ + g_assert (!image_is_dynamic (image)); + + return mono_metadata_decode_table_row_col (image, MONO_TABLE_FIELD, idx, MONO_FIELD_FLAGS); + } +} + +/** + * mono_class_get_fields_lazy: + * \param klass the MonoClass to act on + * + * This routine is an iterator routine for retrieving the fields in a class. + * Only minimal information about fields are loaded. Accessors must be used + * for all MonoClassField returned. + * + * You must pass a gpointer that points to zero and is treated as an opaque handle to + * iterate over all of the elements. When no more values are + * available, the return value is NULL. + * + * \returns a \c MonoClassField* on each iteration, or NULL when no more fields are available. + */ +MonoClassField* +mono_class_get_fields_lazy (MonoClass* klass, gpointer *iter) +{ + MonoClassField* field; + if (!iter) + return NULL; + if (!*iter) { + mono_class_setup_basic_field_info (klass); + if (!klass->fields) + return NULL; + /* start from the first */ + if (mono_class_get_field_count (klass)) { + *iter = &klass->fields [0]; + return (MonoClassField *)*iter; + } else { + /* no fields */ + return NULL; + } + } + field = (MonoClassField *)*iter; + field++; + if (field < &klass->fields [mono_class_get_field_count (klass)]) { + *iter = field; + return (MonoClassField *)*iter; + } + return NULL; +} + +char* +mono_class_full_name (MonoClass *klass) +{ + return mono_type_full_name (&klass->byval_arg); +} + +/* Declare all shared lazy type lookup functions */ +GENERATE_TRY_GET_CLASS_WITH_CACHE (safehandle, "System.Runtime.InteropServices", "SafeHandle") + +/** + * mono_method_get_base_method: + * \param method a method + * \param definition if true, get the definition + * \param error set on failure + * + * Given a virtual method associated with a subclass, return the corresponding + * method from an ancestor. If \p definition is FALSE, returns the method in the + * superclass of the given method. If \p definition is TRUE, return the method + * in the ancestor class where it was first declared. The type arguments will + * be inflated in the ancestor classes. If the method is not associated with a + * class, or isn't virtual, returns the method itself. On failure returns NULL + * and sets \p error. + */ +MonoMethod* +mono_method_get_base_method (MonoMethod *method, gboolean definition, MonoError *error) +{ + MonoClass *klass, *parent; + MonoGenericContext *generic_inst = NULL; + MonoMethod *result = NULL; + int slot; + + if (method->klass == NULL) + return method; + + if (!(method->flags & METHOD_ATTRIBUTE_VIRTUAL) || + MONO_CLASS_IS_INTERFACE (method->klass) || + method->flags & METHOD_ATTRIBUTE_NEW_SLOT) + return method; + + slot = mono_method_get_vtable_slot (method); + if (slot == -1) + return method; + + klass = method->klass; + if (mono_class_is_ginst (klass)) { + generic_inst = mono_class_get_context (klass); + klass = mono_class_get_generic_class (klass)->container_class; + } + +retry: + if (definition) { + /* At the end of the loop, klass points to the eldest class that has this virtual function slot. */ + for (parent = klass->parent; parent != NULL; parent = parent->parent) { + /* on entry, klass is either a plain old non-generic class and generic_inst == NULL + or klass is the generic container class and generic_inst is the instantiation. + + when we go to the parent, if the parent is an open constructed type, we need to + replace the type parameters by the definitions from the generic_inst, and then take it + apart again into the klass and the generic_inst. + + For cases like this: + class C : B { + public override void Foo () { ... } + } + class B : A> { + public override void Foo () { ... } + } + class A { + public virtual void Foo () { ... } + } + + if at each iteration the parent isn't open, we can skip inflating it. if at some + iteration the parent isn't generic (after possible inflation), we set generic_inst to + NULL; + */ + MonoGenericContext *parent_inst = NULL; + if (mono_class_is_open_constructed_type (mono_class_get_type (parent))) { + parent = mono_class_inflate_generic_class_checked (parent, generic_inst, error); + return_val_if_nok (error, NULL); + } + if (mono_class_is_ginst (parent)) { + parent_inst = mono_class_get_context (parent); + parent = mono_class_get_generic_class (parent)->container_class; + } + + mono_class_setup_vtable (parent); + if (parent->vtable_size <= slot) + break; + klass = parent; + generic_inst = parent_inst; + } + } else { + klass = klass->parent; + if (!klass) + return method; + if (mono_class_is_open_constructed_type (mono_class_get_type (klass))) { + klass = mono_class_inflate_generic_class_checked (klass, generic_inst, error); + return_val_if_nok (error, NULL); + + generic_inst = NULL; + } + if (mono_class_is_ginst (klass)) { + generic_inst = mono_class_get_context (klass); + klass = mono_class_get_generic_class (klass)->container_class; + } + + } + + if (generic_inst) { + klass = mono_class_inflate_generic_class_checked (klass, generic_inst, error); + return_val_if_nok (error, NULL); + } + + if (klass == method->klass) + return method; + + /*This is possible if definition == FALSE. + * Do it here to be really sure we don't read invalid memory. + */ + if (slot >= klass->vtable_size) + return method; + + mono_class_setup_vtable (klass); + + result = klass->vtable [slot]; + if (result == NULL) { + /* It is an abstract method */ + gboolean found = FALSE; + gpointer iter = NULL; + while ((result = mono_class_get_methods (klass, &iter))) { + if (result->slot == slot) { + found = TRUE; + break; + } + } + /* found might be FALSE if we looked in an abstract class + * that doesn't override an abstract method of its + * parent: + * abstract class Base { + * public abstract void Foo (); + * } + * abstract class Derived : Base { } + * class Child : Derived { + * public override void Foo () { } + * } + * + * if m was Child.Foo and we ask for the base method, + * then we get here with klass == Derived and found == FALSE + */ + /* but it shouldn't be the case that if we're looking + * for the definition and didn't find a result; the + * loop above should've taken us as far as we could + * go! */ + g_assert (!(definition && !found)); + if (!found) + goto retry; + } + + g_assert (result != NULL); + return result; +} + + +static gboolean +class_kind_may_contain_generic_instances (MonoTypeKind kind) +{ + /* classes of type generic inst may contain generic arguments from other images, + * as well as arrays and pointers whose element types (recursively) may be a generic inst */ + return (kind == MONO_CLASS_GINST || kind == MONO_CLASS_ARRAY || kind == MONO_CLASS_POINTER); +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/class.h b/unity-2019.4.24f1-mbe/mono/metadata/class.h new file mode 100644 index 000000000..565dac59e --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/class.h @@ -0,0 +1,278 @@ +/** + * \file + */ + +#ifndef _MONO_CLI_CLASS_H_ +#define _MONO_CLI_CLASS_H_ + +#include +#include +#include +#include + +MONO_BEGIN_DECLS + +typedef struct MonoVTable MonoVTable; + +typedef struct _MonoClassField MonoClassField; +typedef struct _MonoProperty MonoProperty; +typedef struct _MonoEvent MonoEvent; + +MONO_API MonoClass * +mono_class_get (MonoImage *image, uint32_t type_token); + +MONO_API MonoClass * +mono_class_get_full (MonoImage *image, uint32_t type_token, MonoGenericContext *context); + +MONO_API mono_bool +mono_class_init (MonoClass *klass); + +MONO_API MonoVTable * +mono_class_vtable (MonoDomain *domain, MonoClass *klass); + +MONO_RT_EXTERNAL_ONLY MONO_API MonoClass * +mono_class_from_name (MonoImage *image, const char* name_space, const char *name); + +MONO_RT_EXTERNAL_ONLY MONO_API MonoClass * +mono_class_from_name_case (MonoImage *image, const char* name_space, const char *name); + +MONO_API MonoMethod * +mono_class_get_method_from_name_flags (MonoClass *klass, const char *name, int param_count, int flags); + +MONO_API MonoClass * +mono_class_from_typeref (MonoImage *image, uint32_t type_token); + +MONO_API MonoClass * +mono_class_from_typeref_checked (MonoImage *image, uint32_t type_token, MonoError *error); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoClass * +mono_class_from_generic_parameter (MonoGenericParam *param, MonoImage *image, mono_bool is_mvar); + +MONO_RT_EXTERNAL_ONLY MONO_API MonoType* +mono_class_inflate_generic_type (MonoType *type, MonoGenericContext *context) /* MONO_DEPRECATED */; + +MONO_API MonoMethod* +mono_class_inflate_generic_method (MonoMethod *method, MonoGenericContext *context); + +MONO_API MonoMethod * +mono_get_inflated_method (MonoMethod *method); + +MONO_API MonoClassField* +mono_field_from_token (MonoImage *image, uint32_t token, MonoClass **retklass, MonoGenericContext *context); + +MONO_API MonoClass * +mono_bounded_array_class_get (MonoClass *element_class, uint32_t rank, mono_bool bounded); + +MONO_API MonoClass * +mono_array_class_get (MonoClass *element_class, uint32_t rank); + +MONO_API MonoClass * +mono_ptr_class_get (MonoType *type); + +MONO_API MonoClassField * +mono_class_get_field (MonoClass *klass, uint32_t field_token); + +MONO_API MonoClassField * +mono_class_get_field_from_name (MonoClass *klass, const char *name); + +MONO_API uint32_t +mono_class_get_field_token (MonoClassField *field); + +MONO_API uint32_t +mono_class_get_event_token (MonoEvent *event); + +MONO_API MonoProperty* +mono_class_get_property_from_name (MonoClass *klass, const char *name); + +MONO_API uint32_t +mono_class_get_property_token (MonoProperty *prop); + +MONO_API int32_t +mono_array_element_size (MonoClass *ac); + +MONO_API int32_t +mono_class_instance_size (MonoClass *klass); + +MONO_API int32_t +mono_class_array_element_size (MonoClass *klass); + +MONO_API int32_t +mono_class_data_size (MonoClass *klass); + +MONO_API int32_t +mono_class_value_size (MonoClass *klass, uint32_t *align); + +MONO_API int32_t +mono_class_min_align (MonoClass *klass); + +MONO_API MonoClass * +mono_class_from_mono_type (MonoType *type); + +MONO_API mono_bool +mono_class_is_subclass_of (MonoClass *klass, MonoClass *klassc, + mono_bool check_interfaces); + +MONO_API mono_bool +mono_class_is_assignable_from (MonoClass *klass, MonoClass *oklass); + +MONO_API void* +mono_ldtoken (MonoImage *image, uint32_t token, MonoClass **retclass, MonoGenericContext *context); + +MONO_API char* +mono_type_get_name (MonoType *type); + +MONO_API MonoType* +mono_type_get_underlying_type (MonoType *type); + +/* MonoClass accessors */ +MONO_API MonoImage* +mono_class_get_image (MonoClass *klass); + +MONO_API MonoClass* +mono_class_get_element_class (MonoClass *klass); + +MONO_API mono_bool +mono_class_is_valuetype (MonoClass *klass); + +MONO_API mono_bool +mono_class_is_enum (MonoClass *klass); + +MONO_API MonoType* +mono_class_enum_basetype (MonoClass *klass); + +MONO_API MonoClass* +mono_class_get_parent (MonoClass *klass); + +MONO_API MonoClass* +mono_class_get_nesting_type (MonoClass *klass); + +MONO_API int +mono_class_get_rank (MonoClass *klass); + +MONO_API uint32_t +mono_class_get_flags (MonoClass *klass); + +MONO_API const char* +mono_class_get_name (MonoClass *klass); + +MONO_API const char* +mono_class_get_namespace (MonoClass *klass); + +MONO_API MonoType* +mono_class_get_type (MonoClass *klass); + +MONO_API uint32_t +mono_class_get_type_token (MonoClass *klass); + +MONO_API MonoType* +mono_class_get_byref_type (MonoClass *klass); + +MONO_API int +mono_class_num_fields (MonoClass *klass); + +MONO_API int +mono_class_num_methods (MonoClass *klass); + +MONO_API int +mono_class_num_properties (MonoClass *klass); + +MONO_API int +mono_class_num_events (MonoClass *klass); + +MONO_API MonoClassField* +mono_class_get_fields (MonoClass* klass, void **iter); + +MONO_API MonoMethod* +mono_class_get_methods (MonoClass* klass, void **iter); + +MONO_API MonoProperty* +mono_class_get_properties (MonoClass* klass, void **iter); + +MONO_API MonoEvent* +mono_class_get_events (MonoClass* klass, void **iter); + +MONO_API MonoClass* +mono_class_get_interfaces (MonoClass* klass, void **iter); + +MONO_API MonoClass* +mono_class_get_nested_types (MonoClass* klass, void **iter); + +MONO_API mono_bool +mono_class_is_delegate (MonoClass* klass); + +MONO_API mono_bool +mono_class_implements_interface (MonoClass* klass, MonoClass* iface); + +/* MonoClassField accessors */ +MONO_API const char* +mono_field_get_name (MonoClassField *field); + +MONO_API MonoType* +mono_field_get_type (MonoClassField *field); + +MONO_API MonoClass* +mono_field_get_parent (MonoClassField *field); + +MONO_API uint32_t +mono_field_get_flags (MonoClassField *field); + +MONO_API uint32_t +mono_field_get_offset (MonoClassField *field); + +MONO_API const char * +mono_field_get_data (MonoClassField *field); + +/* MonoProperty acessors */ +MONO_API const char* +mono_property_get_name (MonoProperty *prop); + +MONO_API MonoMethod* +mono_property_get_set_method (MonoProperty *prop); + +MONO_API MonoMethod* +mono_property_get_get_method (MonoProperty *prop); + +MONO_API MonoClass* +mono_property_get_parent (MonoProperty *prop); + +MONO_API uint32_t +mono_property_get_flags (MonoProperty *prop); + +/* MonoEvent accessors */ +MONO_API const char* +mono_event_get_name (MonoEvent *event); + +MONO_API MonoMethod* +mono_event_get_add_method (MonoEvent *event); + +MONO_API MonoMethod* +mono_event_get_remove_method (MonoEvent *event); + +MONO_API MonoMethod* +mono_event_get_remove_method (MonoEvent *event); + +MONO_API MonoMethod* +mono_event_get_raise_method (MonoEvent *event); + +MONO_API MonoClass* +mono_event_get_parent (MonoEvent *event); + +MONO_API uint32_t +mono_event_get_flags (MonoEvent *event); + +MONO_API MonoMethod * +mono_class_get_method_from_name (MonoClass *klass, const char *name, int param_count); + +MONO_API char * +mono_class_name_from_token (MonoImage *image, uint32_t type_token); + +MONO_API mono_bool +mono_method_can_access_field (MonoMethod *method, MonoClassField *field); + +MONO_API mono_bool +mono_method_can_access_method (MonoMethod *method, MonoMethod *called); + +MONO_END_DECLS + +#endif /* _MONO_CLI_CLASS_H_ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/cominterop-win32-internals.h b/unity-2019.4.24f1-mbe/mono/metadata/cominterop-win32-internals.h new file mode 100644 index 000000000..795ee2e5e --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/cominterop-win32-internals.h @@ -0,0 +1,41 @@ +/** + * \file + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#ifndef __MONO_METADATA_COMINTEROP_WIN32_INTERNALS_H__ +#define __MONO_METADATA_COMINTEROP_WIN32_INTERNALS_H__ + +#include +#include + +// On some Windows platforms the implementation of below methods are hosted +// in separate source files like cominterop-win32-*.c. On other platforms, +// the implementation is kept in cominterop.c and declared as static and in some +// cases even inline. +#if defined(HOST_WIN32) && !G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) + +guint32 +mono_marshal_win_safearray_get_dim (gpointer safearray); + +int +mono_marshal_win_safe_array_get_lbound (gpointer psa, guint nDim, glong* plLbound); + +int +mono_marshal_win_safe_array_get_ubound (gpointer psa, guint nDim, glong* plUbound); + +int +mono_marshal_win_safearray_get_value (gpointer safearray, gpointer indices, gpointer *result); + +void +mono_marshal_win_safearray_end (gpointer safearray, gpointer indices); + +gboolean +mono_marshal_win_safearray_create_internal (UINT cDims, SAFEARRAYBOUND *rgsabound, gpointer *newsafearray); + +int +mono_marshal_win_safearray_set_value (gpointer safearray, gpointer indices, gpointer value); + +#endif /* HOST_WIN32 && !G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */ + +#endif /* __MONO_METADATA_COMINTEROP_WIN32_INTERNALS_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/cominterop.c b/unity-2019.4.24f1-mbe/mono/metadata/cominterop.c new file mode 100644 index 000000000..ed3964dbc --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/cominterop.c @@ -0,0 +1,3764 @@ +/** + * \file + * COM Interop Support + * + * + * (C) 2002 Ximian, Inc. http://www.ximian.com + * + */ + +#include "config.h" +#include +#ifdef HAVE_ALLOCA_H +#include +#endif + +#include "object.h" +#include "loader.h" +#include "cil-coff.h" +#include "metadata/abi-details.h" +#include "metadata/cominterop.h" +#include "metadata/marshal.h" +#include "metadata/method-builder.h" +#include "metadata/tabledefs.h" +#include "metadata/exception.h" +#include "metadata/appdomain.h" +#include "metadata/reflection-internals.h" +#include "mono/metadata/debug-helpers.h" +#include "mono/metadata/threads.h" +#include "mono/metadata/monitor.h" +#include "mono/metadata/metadata-internals.h" +#include "mono/metadata/domain-internals.h" +#include "mono/metadata/gc-internals.h" +#include "mono/metadata/threads-types.h" +#include "mono/metadata/string-icalls.h" +#include "mono/metadata/attrdefs.h" +#include "mono/utils/mono-counters.h" +#include "mono/utils/strenc.h" +#include "mono/utils/atomic.h" +#include "mono/utils/mono-error.h" +#include "mono/utils/mono-error-internals.h" +#include +#include +#include + +#if defined(HOST_WIN32) +#include +#include "mono/metadata/cominterop-win32-internals.h" +#endif + +/* +Code shared between the DISABLE_COM and !DISABLE_COM +*/ +static void +register_icall (gpointer func, const char *name, const char *sigstr, gboolean save) +{ + MonoMethodSignature *sig = mono_create_icall_signature (sigstr); + + mono_register_jit_icall (func, name, sig, save); +} + +gpointer +mono_string_to_bstr(MonoString* ptr) +{ + if (!ptr) + return NULL; + + return mono_ptr_to_bstr(mono_string_chars(ptr), mono_string_length(ptr)); +} + +#ifndef DISABLE_COM + +#define OPDEF(a,b,c,d,e,f,g,h,i,j) \ + a = i, + +typedef enum { + MONO_MARSHAL_NONE, /* No marshalling needed */ + MONO_MARSHAL_COPY, /* Can be copied by value to the new domain */ + MONO_MARSHAL_COPY_OUT, /* out parameter that needs to be copied back to the original instance */ + MONO_MARSHAL_SERIALIZE /* Value needs to be serialized into the new domain */ +} MonoXDomainMarshalType; + +typedef enum { + MONO_COM_DEFAULT, + MONO_COM_MS +} MonoCOMProvider; + +static MonoCOMProvider com_provider = MONO_COM_DEFAULT; + +enum { +#include "mono/cil/opcode.def" + LAST = 0xff +}; +#undef OPDEF + +/* This mutex protects the various cominterop related caches in MonoImage */ +#define mono_cominterop_lock() mono_os_mutex_lock (&cominterop_mutex) +#define mono_cominterop_unlock() mono_os_mutex_unlock (&cominterop_mutex) +static mono_mutex_t cominterop_mutex; + +/* STDCALL on windows, CDECL everywhere else to work with XPCOM and MainWin COM */ +#ifdef HOST_WIN32 +#define STDCALL __stdcall +#else +#define STDCALL +#endif + +GENERATE_GET_CLASS_WITH_CACHE (interop_proxy, "Mono.Interop", "ComInteropProxy") +GENERATE_GET_CLASS_WITH_CACHE (idispatch, "Mono.Interop", "IDispatch") +GENERATE_GET_CLASS_WITH_CACHE (iunknown, "Mono.Interop", "IUnknown") + +GENERATE_GET_CLASS_WITH_CACHE (com_object, "System", "__ComObject") +GENERATE_GET_CLASS_WITH_CACHE (variant, "System", "Variant") + +static GENERATE_GET_CLASS_WITH_CACHE (interface_type_attribute, "System.Runtime.InteropServices", "InterfaceTypeAttribute") +static GENERATE_GET_CLASS_WITH_CACHE (guid_attribute, "System.Runtime.InteropServices", "GuidAttribute") + +/* Upon creation of a CCW, only allocate a weak handle and set the + * reference count to 0. If the unmanaged client code decides to addref and + * hold onto the CCW, I then allocate a strong handle. Once the reference count + * goes back to 0, convert back to a weak handle. + */ +typedef struct { + guint32 ref_count; + guint32 gc_handle; + GHashTable* vtable_hash; +#ifdef HOST_WIN32 + gpointer free_marshaler; +#endif +} MonoCCW; + +/* This type is the actual pointer passed to unmanaged code + * to represent a COM interface. + */ +typedef struct { + gpointer vtable; + MonoCCW* ccw; +} MonoCCWInterface; + +/* IUnknown */ +static int STDCALL cominterop_ccw_addref (MonoCCWInterface* ccwe); + +static int STDCALL cominterop_ccw_release (MonoCCWInterface* ccwe); + +static int STDCALL cominterop_ccw_queryinterface (MonoCCWInterface* ccwe, guint8* riid, gpointer* ppv); + +/* IDispatch */ +static int STDCALL cominterop_ccw_get_type_info_count (MonoCCWInterface* ccwe, guint32 *pctinfo); + +static int STDCALL cominterop_ccw_get_type_info (MonoCCWInterface* ccwe, guint32 iTInfo, guint32 lcid, gpointer *ppTInfo); + +static int STDCALL cominterop_ccw_get_ids_of_names (MonoCCWInterface* ccwe, gpointer riid, + gunichar2** rgszNames, guint32 cNames, + guint32 lcid, gint32 *rgDispId); + +static int STDCALL cominterop_ccw_invoke (MonoCCWInterface* ccwe, guint32 dispIdMember, + gpointer riid, guint32 lcid, + guint16 wFlags, gpointer pDispParams, + gpointer pVarResult, gpointer pExcepInfo, + guint32 *puArgErr); + +static MonoMethod * +cominterop_get_managed_wrapper_adjusted (MonoMethod *method); + +static gpointer +cominterop_get_ccw (MonoObject* object, MonoClass* itf); + +static gpointer +cominterop_get_ccw_checked (MonoObject *object, MonoClass *itf, MonoError *error); + + +static MonoObject* +cominterop_get_ccw_object (MonoCCWInterface* ccw_entry, gboolean verify); + +/* SAFEARRAY marshalling */ +static gboolean +mono_marshal_safearray_begin (gpointer safearray, MonoArray **result, gpointer *indices, gpointer empty, gpointer parameter, gboolean allocateNewArray); + +static gpointer +mono_marshal_safearray_get_value (gpointer safearray, gpointer indices); + +static gboolean +mono_marshal_safearray_next (gpointer safearray, gpointer indices); + +static void +mono_marshal_safearray_end (gpointer safearray, gpointer indices); + +static gboolean +mono_marshal_safearray_create (MonoArray *input, gpointer *newsafearray, gpointer *indices, gpointer empty); + +static void +mono_marshal_safearray_set_value (gpointer safearray, gpointer indices, gpointer value); + +static void +mono_marshal_safearray_free_indices (gpointer indices); + +MonoClass* +mono_class_try_get_com_object_class (void) +{ + static MonoClass *tmp_class; + static gboolean inited; + MonoClass *klass; + if (!inited) { + klass = mono_class_load_from_name (mono_defaults.corlib, "System", "__ComObject"); + mono_memory_barrier (); + tmp_class = klass; + mono_memory_barrier (); + inited = TRUE; + } + return tmp_class; +} + +/** + * cominterop_method_signature: + * @method: a method + * + * Returns: the corresponding unmanaged method signature for a managed COM + * method. + */ +static MonoMethodSignature* +cominterop_method_signature (MonoMethod* method) +{ + MonoMethodSignature *res; + MonoImage *image = method->klass->image; + MonoMethodSignature *sig = mono_method_signature (method); + gboolean preserve_sig = method->iflags & METHOD_IMPL_ATTRIBUTE_PRESERVE_SIG; + int sigsize; + int i; + int param_count = sig->param_count + 1; // convert this arg into IntPtr arg + + if (!preserve_sig &&!MONO_TYPE_IS_VOID (sig->ret)) + param_count++; + + res = mono_metadata_signature_alloc (image, param_count); + sigsize = MONO_SIZEOF_METHOD_SIGNATURE + sig->param_count * sizeof (MonoType *); + memcpy (res, sig, sigsize); + + // now move args forward one + for (i = sig->param_count-1; i >= 0; i--) + res->params[i+1] = sig->params[i]; + + // first arg is interface pointer + res->params[0] = &mono_defaults.int_class->byval_arg; + + if (preserve_sig) { + res->ret = sig->ret; + } + else { + // last arg is return type + if (!MONO_TYPE_IS_VOID (sig->ret)) { + res->params[param_count-1] = mono_metadata_type_dup (image, sig->ret); + res->params[param_count-1]->byref = 1; + res->params[param_count-1]->attrs = PARAM_ATTRIBUTE_OUT; + } + + // return type is always int32 (HRESULT) + res->ret = &mono_defaults.int32_class->byval_arg; + } + + // no pinvoke + res->pinvoke = FALSE; + + // no hasthis + res->hasthis = 0; + + // set param_count + res->param_count = param_count; + + // STDCALL on windows, CDECL everywhere else to work with XPCOM and MainWin COM +#ifdef HOST_WIN32 + res->call_convention = MONO_CALL_STDCALL; +#else + res->call_convention = MONO_CALL_C; +#endif + + return res; +} + +/** + * cominterop_get_function_pointer: + * @itf: a pointer to the COM interface + * @slot: the vtable slot of the method pointer to return + * + * Returns: the unmanaged vtable function pointer from the interface + */ +static gpointer +cominterop_get_function_pointer (gpointer itf, int slot) +{ + gpointer func; + func = *((*(gpointer**)itf)+slot); + return func; +} + +/** + * cominterop_object_is_com_object: + * @obj: a pointer to the object + * + * Returns: a value indicating if the object is a + * Runtime Callable Wrapper (RCW) for a COM object + */ +static gboolean +cominterop_object_is_rcw (MonoObject *obj) +{ + MonoClass *klass = NULL; + MonoRealProxy* real_proxy = NULL; + if (!obj) + return FALSE; + klass = mono_object_class (obj); + if (!mono_class_is_transparent_proxy (klass)) + return FALSE; + + real_proxy = ((MonoTransparentProxy*)obj)->rp; + if (!real_proxy) + return FALSE; + + klass = mono_object_class (real_proxy); + return (klass && klass == mono_class_get_interop_proxy_class ()); +} + +static int +cominterop_get_com_slot_begin (MonoClass* klass) +{ + MonoError error; + MonoCustomAttrInfo *cinfo = NULL; + MonoInterfaceTypeAttribute* itf_attr = NULL; + + cinfo = mono_custom_attrs_from_class_checked (klass, &error); + mono_error_assert_ok (&error); + if (cinfo) { + itf_attr = (MonoInterfaceTypeAttribute*)mono_custom_attrs_get_attr_checked (cinfo, mono_class_get_interface_type_attribute_class (), &error); + g_assert (mono_error_ok (&error)); /*FIXME proper error handling*/ + if (!cinfo->cached) + mono_custom_attrs_free (cinfo); + } + + if (itf_attr && itf_attr->intType == 1) + return 3; /* 3 methods in IUnknown*/ + else + return 7; /* 7 methods in IDispatch*/ +} + +/** + * cominterop_get_method_interface: + * @method: method being called + * + * Returns: the MonoClass* representing the interface on which + * the method is defined. + */ +static MonoClass* +cominterop_get_method_interface (MonoMethod* method) +{ + MonoError error; + MonoClass *ic = method->klass; + + /* if method is on a class, we need to look up interface method exists on */ + if (!MONO_CLASS_IS_INTERFACE(method->klass)) { + GPtrArray *ifaces = mono_class_get_implemented_interfaces (method->klass, &error); + g_assert (mono_error_ok (&error)); + if (ifaces) { + int i; + mono_class_setup_vtable (method->klass); + for (i = 0; i < ifaces->len; ++i) { + int j, offset; + gboolean found = FALSE; + ic = (MonoClass *)g_ptr_array_index (ifaces, i); + offset = mono_class_interface_offset (method->klass, ic); + int mcount = mono_class_get_method_count (ic); + for (j = 0; j < mcount; ++j) { + if (method->klass->vtable [j + offset] == method) { + found = TRUE; + break; + } + } + if (found) + break; + ic = NULL; + } + g_ptr_array_free (ifaces, TRUE); + } + } + + return ic; +} + +static void +mono_cominterop_get_interface_missing_error (MonoError* error, MonoMethod* method) +{ + mono_error_set_invalid_operation (error, "Method '%s' in ComImport class '%s' must implement an interface method.", method->name, method->klass->name); +} + +/** + * cominterop_get_com_slot_for_method: + * @method: a method + * @error: set on error + * + * Returns: the method's slot in the COM interface vtable + */ +static int +cominterop_get_com_slot_for_method (MonoMethod* method, MonoError* error) +{ + guint32 slot = method->slot; + MonoClass *ic = method->klass; + + error_init (error); + + /* if method is on a class, we need to look up interface method exists on */ + if (!MONO_CLASS_IS_INTERFACE(ic)) { + int offset = 0; + int i = 0; + ic = cominterop_get_method_interface (method); + if (!ic || !MONO_CLASS_IS_INTERFACE (ic)) { + mono_cominterop_get_interface_missing_error (error, method); + return -1; + } + offset = mono_class_interface_offset (method->klass, ic); + g_assert(offset >= 0); + int mcount = mono_class_get_method_count (ic); + for(i = 0; i < mcount; ++i) { + if (method->klass->vtable [i + offset] == method) + { + slot = ic->methods[i]->slot; + break; + } + } + } + + g_assert (ic); + g_assert (MONO_CLASS_IS_INTERFACE (ic)); + + return slot + cominterop_get_com_slot_begin (ic); +} + + +static void +cominterop_mono_string_to_guid (MonoString* string, guint8 *guid); + +static gboolean +cominterop_class_guid (MonoClass* klass, guint8* guid) +{ + MonoError error; + MonoCustomAttrInfo *cinfo; + + cinfo = mono_custom_attrs_from_class_checked (klass, &error); + mono_error_assert_ok (&error); + if (cinfo) { + MonoReflectionGuidAttribute *attr = (MonoReflectionGuidAttribute*)mono_custom_attrs_get_attr_checked (cinfo, mono_class_get_guid_attribute_class (), &error); + g_assert (mono_error_ok (&error)); /*FIXME proper error handling*/ + + if (!attr) + return FALSE; + if (!cinfo->cached) + mono_custom_attrs_free (cinfo); + + cominterop_mono_string_to_guid (attr->guid, guid); + return TRUE; + } + return FALSE; +} + +static gboolean +cominterop_com_visible (MonoClass* klass) +{ + MonoError error; + MonoCustomAttrInfo *cinfo; + GPtrArray *ifaces; + MonoBoolean visible = 1; + + cinfo = mono_custom_attrs_from_class_checked (klass, &error); + mono_error_assert_ok (&error); + if (cinfo) { + MonoReflectionComVisibleAttribute *attr = (MonoReflectionComVisibleAttribute*)mono_custom_attrs_get_attr_checked (cinfo, mono_class_get_guid_attribute_class (), &error); + g_assert (mono_error_ok (&error)); /*FIXME proper error handling*/ + + if (attr) + visible = attr->visible; + if (!cinfo->cached) + mono_custom_attrs_free (cinfo); + if (visible) + return TRUE; + } + + ifaces = mono_class_get_implemented_interfaces (klass, &error); + g_assert (mono_error_ok (&error)); + if (ifaces) { + int i; + for (i = 0; i < ifaces->len; ++i) { + MonoClass *ic = NULL; + ic = (MonoClass *)g_ptr_array_index (ifaces, i); + if (MONO_CLASS_IS_IMPORT (ic)) + visible = TRUE; + + } + g_ptr_array_free (ifaces, TRUE); + } + return visible; + +} + +static void cominterop_set_hr_error (MonoError *oerror, int hr) +{ + static MonoMethod* throw_exception_for_hr = NULL; + MonoError error; + MonoException* ex; + void* params[1] = {&hr}; + + if (!throw_exception_for_hr) + throw_exception_for_hr = mono_class_get_method_from_name (mono_defaults.marshal_class, "GetExceptionForHR", 1); + + ex = (MonoException*)mono_runtime_invoke_checked (throw_exception_for_hr, NULL, params, &error); + mono_error_assert_ok (&error); + + mono_error_set_exception_instance (oerror, ex); +} + +/** + * cominterop_get_interface_checked: + * @obj: managed wrapper object containing COM object + * @ic: interface type to retrieve for COM object + * @error: set on error + * + * Returns: the COM interface requested. On failure returns NULL and sets @error + */ +static gpointer +cominterop_get_interface_checked (MonoComObject* obj, MonoClass* ic, MonoError *error) +{ + gpointer itf = NULL; + + g_assert (ic); + g_assert (MONO_CLASS_IS_INTERFACE (ic)); + + error_init (error); + + mono_cominterop_lock (); + if (obj->itf_hash) + itf = g_hash_table_lookup (obj->itf_hash, GUINT_TO_POINTER ((guint)ic->interface_id)); + mono_cominterop_unlock (); + + if (!itf) { + guint8 iid [16]; + int found = cominterop_class_guid (ic, iid); + int hr; + g_assert(found); + hr = ves_icall_System_Runtime_InteropServices_Marshal_QueryInterfaceInternal (obj->iunknown, iid, &itf); + if (hr < 0) { + cominterop_set_hr_error (error, hr); + } + + if (hr >= 0 && itf) { + mono_cominterop_lock (); + if (!obj->itf_hash) + obj->itf_hash = g_hash_table_new (mono_aligned_addr_hash, NULL); + g_hash_table_insert (obj->itf_hash, GUINT_TO_POINTER ((guint)ic->interface_id), itf); + mono_cominterop_unlock (); + } + + } + return itf; +} + +/** + * cominterop_get_interface: + * @obj: managed wrapper object containing COM object + * @ic: interface type to retrieve for COM object + * + * Returns: the COM interface requested + */ +static gpointer +cominterop_get_interface (MonoComObject *obj, MonoClass *ic, gboolean throw_exception) +{ + MonoError error; + gpointer itf = cominterop_get_interface_checked (obj, ic, &error); + if (!is_ok (&error)) { + if (throw_exception) { + mono_error_set_pending_exception (&error); + return NULL; + } else { + mono_error_cleanup (&error); + } + } + + if (throw_exception) + g_assert (itf); + + return itf; +} + +static int +cominterop_get_hresult_for_exception (MonoException* exc) +{ + int hr = 0; + return hr; +} + +static MonoReflectionType * +cominterop_type_from_handle (MonoType *handle) +{ + MonoError error; + MonoReflectionType *ret; + MonoDomain *domain = mono_domain_get (); + MonoClass *klass = mono_class_from_mono_type (handle); + + mono_class_init (klass); + + ret = mono_type_get_object_checked (domain, handle, &error); + mono_error_set_pending_exception (&error); + + return ret; +} + +void +mono_cominterop_init (void) +{ + char* com_provider_env; + + mono_os_mutex_init_recursive (&cominterop_mutex); + + com_provider_env = g_getenv ("MONO_COM"); + if (com_provider_env && !strcmp(com_provider_env, "MS")) + com_provider = MONO_COM_MS; + if (com_provider_env) + g_free (com_provider_env); + + register_icall (cominterop_get_method_interface, "cominterop_get_method_interface", "ptr ptr", FALSE); + register_icall (cominterop_get_function_pointer, "cominterop_get_function_pointer", "ptr ptr int32", FALSE); + register_icall (cominterop_object_is_rcw, "cominterop_object_is_rcw", "int32 object", FALSE); + register_icall (cominterop_get_ccw, "cominterop_get_ccw", "ptr object ptr", FALSE); + register_icall (cominterop_get_ccw_object, "cominterop_get_ccw_object", "object ptr int32", FALSE); + register_icall (cominterop_get_hresult_for_exception, "cominterop_get_hresult_for_exception", "int32 object", FALSE); + register_icall (cominterop_get_interface, "cominterop_get_interface", "ptr object ptr int32", FALSE); + + register_icall (mono_string_to_bstr, "mono_string_to_bstr", "ptr obj", FALSE); + register_icall (mono_string_from_bstr_icall, "mono_string_from_bstr_icall", "obj ptr", FALSE); + register_icall (mono_free_bstr, "mono_free_bstr", "void ptr", FALSE); + register_icall (cominterop_type_from_handle, "cominterop_type_from_handle", "object ptr", FALSE); + + /* SAFEARRAY marshalling */ + register_icall (mono_marshal_safearray_begin, "mono_marshal_safearray_begin", "int32 ptr ptr ptr ptr ptr int32", FALSE); + register_icall (mono_marshal_safearray_get_value, "mono_marshal_safearray_get_value", "ptr ptr ptr", FALSE); + register_icall (mono_marshal_safearray_next, "mono_marshal_safearray_next", "int32 ptr ptr", FALSE); + register_icall (mono_marshal_safearray_end, "mono_marshal_safearray_end", "void ptr ptr", FALSE); + register_icall (mono_marshal_safearray_create, "mono_marshal_safearray_create", "int32 object ptr ptr ptr", FALSE); + register_icall (mono_marshal_safearray_set_value, "mono_marshal_safearray_set_value", "void ptr ptr ptr", FALSE); + register_icall (mono_marshal_safearray_free_indices, "mono_marshal_safearray_free_indices", "void ptr", FALSE); +} + +void +mono_cominterop_cleanup (void) +{ + mono_os_mutex_destroy (&cominterop_mutex); +} + +void +mono_mb_emit_cominterop_get_function_pointer (MonoMethodBuilder *mb, MonoMethod *method) +{ +#ifndef DISABLE_JIT + int slot; + MonoError error; + // get function pointer from 1st arg, the COM interface pointer + mono_mb_emit_ldarg (mb, 0); + slot = cominterop_get_com_slot_for_method (method, &error); + if (is_ok (&error)) { + mono_mb_emit_icon (mb, slot); + mono_mb_emit_icall (mb, cominterop_get_function_pointer); + /* Leaves the function pointer on top of the stack */ + } + else { + mono_mb_emit_exception_for_error (mb, &error); + } + mono_error_cleanup (&error); +#endif +} + +void +mono_mb_emit_cominterop_call_function_pointer (MonoMethodBuilder *mb, MonoMethodSignature *sig) +{ +#ifndef DISABLE_JIT + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_SAVE_LMF); + mono_mb_emit_calli (mb, sig); + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_RESTORE_LMF); +#endif /* DISABLE_JIT */ +} + +void +mono_mb_emit_cominterop_call (MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethod* method) +{ +#ifndef DISABLE_JIT + mono_mb_emit_cominterop_get_function_pointer (mb, method); + + mono_mb_emit_cominterop_call_function_pointer (mb, sig); +#endif /* DISABLE_JIT */ +} + +void +mono_cominterop_emit_ptr_to_object_conv (MonoMethodBuilder *mb, MonoType *type, MonoMarshalConv conv, MonoMarshalSpec *mspec) +{ +#ifndef DISABLE_JIT + switch (conv) { + case MONO_MARSHAL_CONV_OBJECT_INTERFACE: + case MONO_MARSHAL_CONV_OBJECT_IUNKNOWN: + case MONO_MARSHAL_CONV_OBJECT_IDISPATCH: { + static MonoMethod* com_interop_proxy_get_proxy = NULL; + static MonoMethod* get_transparent_proxy = NULL; + guint32 pos_null = 0, pos_ccw = 0, pos_end = 0; + MonoClass *klass = NULL; + + klass = mono_class_from_mono_type (type); + + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_byte (mb, CEE_LDNULL); + mono_mb_emit_byte (mb, CEE_STIND_REF); + + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte (mb, CEE_LDIND_I); + pos_null = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S); + + /* load dst to store later */ + mono_mb_emit_ldloc (mb, 1); + + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_icon (mb, TRUE); + mono_mb_emit_icall (mb, cominterop_get_ccw_object); + pos_ccw = mono_mb_emit_short_branch (mb, CEE_BRTRUE_S); + + if (!com_interop_proxy_get_proxy) + com_interop_proxy_get_proxy = mono_class_get_method_from_name_flags (mono_class_get_interop_proxy_class (), "GetProxy", 2, METHOD_ATTRIBUTE_PRIVATE); +#ifndef DISABLE_REMOTING + if (!get_transparent_proxy) + get_transparent_proxy = mono_class_get_method_from_name (mono_defaults.real_proxy_class, "GetTransparentProxy", 0); +#endif + + mono_mb_add_local (mb, &mono_class_get_interop_proxy_class ()->byval_arg); + + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_ptr (mb, &mono_class_get_com_object_class ()->byval_arg); + mono_mb_emit_icall (mb, cominterop_type_from_handle); + mono_mb_emit_managed_call (mb, com_interop_proxy_get_proxy, NULL); + mono_mb_emit_managed_call (mb, get_transparent_proxy, NULL); + if (conv == MONO_MARSHAL_CONV_OBJECT_INTERFACE) { + g_assert (klass); + mono_mb_emit_op (mb, CEE_CASTCLASS, klass); + } + mono_mb_emit_byte (mb, CEE_STIND_REF); + pos_end = mono_mb_emit_short_branch (mb, CEE_BR_S); + + /* is already managed object */ + mono_mb_patch_short_branch (mb, pos_ccw); + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_icon (mb, TRUE); + mono_mb_emit_icall (mb, cominterop_get_ccw_object); + + if (conv == MONO_MARSHAL_CONV_OBJECT_INTERFACE) { + g_assert (klass); + mono_mb_emit_op (mb, CEE_CASTCLASS, klass); + } + mono_mb_emit_byte (mb, CEE_STIND_REF); + + mono_mb_patch_short_branch (mb, pos_end); + /* case if null */ + mono_mb_patch_short_branch (mb, pos_null); + break; + } + default: + g_assert_not_reached (); + } +#endif /* DISABLE_JIT */ +} + +void +mono_cominterop_emit_object_to_ptr_conv (MonoMethodBuilder *mb, MonoType *type, MonoMarshalConv conv, MonoMarshalSpec *mspec) +{ +#ifndef DISABLE_JIT + switch (conv) { + case MONO_MARSHAL_CONV_OBJECT_INTERFACE: + case MONO_MARSHAL_CONV_OBJECT_IDISPATCH: + case MONO_MARSHAL_CONV_OBJECT_IUNKNOWN: { + guint32 pos_null = 0, pos_rcw = 0, pos_end = 0; + + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_icon (mb, 0); + mono_mb_emit_byte (mb, CEE_CONV_U); + mono_mb_emit_byte (mb, CEE_STIND_I); + + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + + // if null just break, dst was already inited to 0 + pos_null = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S); + + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_icall (mb, cominterop_object_is_rcw); + pos_rcw = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S); + + // load dst to store later + mono_mb_emit_ldloc (mb, 1); + + // load src + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoTransparentProxy, rp)); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + + /* load the RCW from the ComInteropProxy*/ + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoComInteropProxy, com_object)); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + + if (conv == MONO_MARSHAL_CONV_OBJECT_INTERFACE) { + mono_mb_emit_ptr (mb, mono_type_get_class (type)); + mono_mb_emit_icon (mb, TRUE); + mono_mb_emit_icall (mb, cominterop_get_interface); + + } + else if (conv == MONO_MARSHAL_CONV_OBJECT_IUNKNOWN) { + static MonoProperty* iunknown = NULL; + + if (!iunknown) + iunknown = mono_class_get_property_from_name (mono_class_get_com_object_class (), "IUnknown"); + mono_mb_emit_managed_call (mb, iunknown->get, NULL); + } + else if (conv == MONO_MARSHAL_CONV_OBJECT_IDISPATCH) { + static MonoProperty* idispatch = NULL; + + if (!idispatch) + idispatch = mono_class_get_property_from_name (mono_class_get_com_object_class (), "IDispatch"); + mono_mb_emit_managed_call (mb, idispatch->get, NULL); + } + else { + g_assert_not_reached (); + } + mono_mb_emit_byte (mb, CEE_STIND_I); + pos_end = mono_mb_emit_short_branch (mb, CEE_BR_S); + + // if not rcw + mono_mb_patch_short_branch (mb, pos_rcw); + /* load dst to store later */ + mono_mb_emit_ldloc (mb, 1); + /* load src */ + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + + if (conv == MONO_MARSHAL_CONV_OBJECT_INTERFACE) + mono_mb_emit_ptr (mb, mono_type_get_class (type)); + else if (conv == MONO_MARSHAL_CONV_OBJECT_IUNKNOWN) + mono_mb_emit_ptr (mb, mono_class_get_iunknown_class ()); + else if (conv == MONO_MARSHAL_CONV_OBJECT_IDISPATCH) + mono_mb_emit_ptr (mb, mono_class_get_idispatch_class ()); + else + g_assert_not_reached (); + mono_mb_emit_icall (mb, cominterop_get_ccw); + mono_mb_emit_byte (mb, CEE_STIND_I); + + mono_mb_patch_short_branch (mb, pos_end); + mono_mb_patch_short_branch (mb, pos_null); + break; + } + default: + g_assert_not_reached (); + } +#endif /* DISABLE_JIT */ +} + +/** + * cominterop_get_native_wrapper_adjusted: + * @method: managed COM Interop method + * + * Returns: the generated method to call with signature matching + * the unmanaged COM Method signature + */ +static MonoMethod * +cominterop_get_native_wrapper_adjusted (MonoMethod *method) +{ + MonoMethod *res; + MonoMethodBuilder *mb_native; + MonoMarshalSpec **mspecs; + MonoMethodSignature *sig, *sig_native; + MonoMethodPInvoke *piinfo = (MonoMethodPInvoke *) method; + int i; + + sig = mono_method_signature (method); + + // create unmanaged wrapper + mb_native = mono_mb_new (method->klass, method->name, MONO_WRAPPER_MANAGED_TO_NATIVE); + sig_native = cominterop_method_signature (method); + + mspecs = g_new (MonoMarshalSpec*, sig_native->param_count+1); + memset (mspecs, 0, sizeof(MonoMarshalSpec*)*(sig_native->param_count+1)); + + mono_method_get_marshal_info (method, mspecs); + + // move managed args up one + for (i = sig->param_count; i >= 1; i--) + mspecs[i+1] = mspecs[i]; + + // first arg is IntPtr for interface + mspecs[1] = NULL; + + if (!(method->iflags & METHOD_IMPL_ATTRIBUTE_PRESERVE_SIG)) { + // move return spec to last param + if (!MONO_TYPE_IS_VOID (sig->ret)) + mspecs[sig_native->param_count] = mspecs[0]; + + mspecs[0] = NULL; + } + + for (i = 1; i < sig_native->param_count; i++) { + int mspec_index = i + 1; + if (mspecs[mspec_index] == NULL) { + // default object to VARIANT + if (sig_native->params[i]->type == MONO_TYPE_OBJECT) { + mspecs[mspec_index] = g_new0 (MonoMarshalSpec, 1); + mspecs[mspec_index]->native = MONO_NATIVE_STRUCT; + } + else if (sig_native->params[i]->type == MONO_TYPE_STRING) { + mspecs[mspec_index] = g_new0 (MonoMarshalSpec, 1); + mspecs[mspec_index]->native = MONO_NATIVE_BSTR; + } + else if (sig_native->params[i]->type == MONO_TYPE_CLASS) { + mspecs[mspec_index] = g_new0 (MonoMarshalSpec, 1); + mspecs[mspec_index]->native = MONO_NATIVE_INTERFACE; + } + else if (sig_native->params[i]->type == MONO_TYPE_BOOLEAN) { + mspecs[mspec_index] = g_new0 (MonoMarshalSpec, 1); + mspecs[mspec_index]->native = MONO_NATIVE_VARIANTBOOL; + } + } + } + + if (method->iflags & METHOD_IMPL_ATTRIBUTE_PRESERVE_SIG) { + // move return spec to last param + if (!MONO_TYPE_IS_VOID (sig->ret) && mspecs[0] == NULL) { + // default object to VARIANT + if (sig->ret->type == MONO_TYPE_OBJECT) { + mspecs[0] = g_new0 (MonoMarshalSpec, 1); + mspecs[0]->native = MONO_NATIVE_STRUCT; + } + else if (sig->ret->type == MONO_TYPE_STRING) { + mspecs[0] = g_new0 (MonoMarshalSpec, 1); + mspecs[0]->native = MONO_NATIVE_BSTR; + } + else if (sig->ret->type == MONO_TYPE_CLASS) { + mspecs[0] = g_new0 (MonoMarshalSpec, 1); + mspecs[0]->native = MONO_NATIVE_INTERFACE; + } + else if (sig->ret->type == MONO_TYPE_BOOLEAN) { + mspecs[0] = g_new0 (MonoMarshalSpec, 1); + mspecs[0]->native = MONO_NATIVE_VARIANTBOOL; + } + } + } + + mono_marshal_emit_native_wrapper (method->klass->image, mb_native, sig_native, piinfo, mspecs, piinfo->addr, FALSE, TRUE, FALSE); + + res = mono_mb_create_method (mb_native, sig_native, sig_native->param_count + 16); + + mono_mb_free (mb_native); + + for (i = sig_native->param_count; i >= 0; i--) + if (mspecs [i]) + mono_metadata_free_marshal_spec (mspecs [i]); + g_free (mspecs); + + return res; +} + +/** + * mono_cominterop_get_native_wrapper: + * \param method managed method + * \returns the generated method to call + */ +MonoMethod * +mono_cominterop_get_native_wrapper (MonoMethod *method) +{ + MonoMethod *res; + GHashTable *cache; + MonoMethodBuilder *mb; + MonoMethodSignature *sig, *csig; + + g_assert (method); + + cache = mono_marshal_get_cache (&mono_method_get_wrapper_cache (method)->cominterop_wrapper_cache, mono_aligned_addr_hash, NULL); + + if ((res = mono_marshal_find_in_cache (cache, method))) + return res; + + if (!method->klass->vtable) + mono_class_setup_vtable (method->klass); + + if (!method->klass->methods) + mono_class_setup_methods (method->klass); + g_assert (!mono_class_has_failure (method->klass)); /*FIXME do proper error handling*/ + + sig = mono_method_signature (method); + mb = mono_mb_new (method->klass, method->name, MONO_WRAPPER_COMINTEROP); + +#ifndef DISABLE_JIT + /* if method klass is import, that means method + * is really a com call. let interop system emit it. + */ + if (MONO_CLASS_IS_IMPORT(method->klass)) { + /* FIXME: we have to call actual class .ctor + * instead of just __ComObject .ctor. + */ + if (!strcmp(method->name, ".ctor")) { + static MonoMethod *ctor = NULL; + + if (!ctor) + ctor = mono_class_get_method_from_name (mono_class_get_com_object_class (), ".ctor", 0); + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_managed_call (mb, ctor, NULL); + mono_mb_emit_byte (mb, CEE_RET); + } + else if (method->flags & METHOD_ATTRIBUTE_STATIC) { + /* + * The method's class must implement an interface. + * However, no interfaces are allowed to have static methods. + * Thus, calling it should invariably lead to an exception. + */ + MonoError error; + error_init (&error); + mono_cominterop_get_interface_missing_error (&error, method); + mono_mb_emit_exception_for_error (mb, &error); + mono_error_cleanup (&error); + } + else { + static MonoMethod * ThrowExceptionForHR = NULL; + MonoMethod *adjusted_method; + int retval = 0; + int ptr_this; + int i; + gboolean preserve_sig = method->iflags & METHOD_IMPL_ATTRIBUTE_PRESERVE_SIG; + + // add local variables + ptr_this = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + if (!MONO_TYPE_IS_VOID (sig->ret)) + retval = mono_mb_add_local (mb, sig->ret); + + // get the type for the interface the method is defined on + // and then get the underlying COM interface for that type + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ptr (mb, method); + mono_mb_emit_icall (mb, cominterop_get_method_interface); + mono_mb_emit_icon (mb, TRUE); + mono_mb_emit_icall (mb, cominterop_get_interface); + mono_mb_emit_stloc (mb, ptr_this); + + // arg 1 is unmanaged this pointer + mono_mb_emit_ldloc (mb, ptr_this); + + // load args + for (i = 1; i <= sig->param_count; i++) + mono_mb_emit_ldarg (mb, i); + + // push managed return value as byref last argument + if (!MONO_TYPE_IS_VOID (sig->ret) && !preserve_sig) + mono_mb_emit_ldloc_addr (mb, retval); + + adjusted_method = cominterop_get_native_wrapper_adjusted (method); + mono_mb_emit_managed_call (mb, adjusted_method, NULL); + + if (!preserve_sig) { + if (!ThrowExceptionForHR) + ThrowExceptionForHR = mono_class_get_method_from_name (mono_defaults.marshal_class, "ThrowExceptionForHR", 1); + mono_mb_emit_managed_call (mb, ThrowExceptionForHR, NULL); + + // load return value managed is expecting + if (!MONO_TYPE_IS_VOID (sig->ret)) + mono_mb_emit_ldloc (mb, retval); + } + + mono_mb_emit_byte (mb, CEE_RET); + } + + + } + /* Does this case ever get hit? */ + else { + char *msg = g_strdup ("non imported interfaces on \ + imported classes is not yet implemented."); + mono_mb_emit_exception (mb, "NotSupportedException", msg); + } +#endif /* DISABLE_JIT */ + + csig = mono_metadata_signature_dup_full (method->klass->image, sig); + csig->pinvoke = 0; + res = mono_mb_create_and_cache (cache, method, + mb, csig, csig->param_count + 16); + mono_mb_free (mb); + return res; +} + +/** + * mono_cominterop_get_invoke: + * \param method managed method + * \returns the generated method that calls the underlying \c __ComObject + * rather than the proxy object. + */ +MonoMethod * +mono_cominterop_get_invoke (MonoMethod *method) +{ + MonoMethodSignature *sig; + MonoMethodBuilder *mb; + MonoMethod *res; + int i; + GHashTable* cache; + + cache = mono_marshal_get_cache (&mono_method_get_wrapper_cache (method)->cominterop_invoke_cache, mono_aligned_addr_hash, NULL); + + g_assert (method); + + if ((res = mono_marshal_find_in_cache (cache, method))) + return res; + + sig = mono_signature_no_pinvoke (method); + + /* we cant remote methods without this pointer */ + if (!sig->hasthis) + return method; + + mb = mono_mb_new (method->klass, method->name, MONO_WRAPPER_COMINTEROP_INVOKE); + +#ifndef DISABLE_JIT + /* get real proxy object, which is a ComInteropProxy in this case*/ + mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoTransparentProxy, rp)); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + + /* load the RCW from the ComInteropProxy*/ + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoComInteropProxy, com_object)); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + + /* load args and make the call on the RCW */ + for (i = 1; i <= sig->param_count; i++) + mono_mb_emit_ldarg (mb, i); + + if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) || mono_class_is_interface (method->klass)) { + MonoMethod * native_wrapper = mono_cominterop_get_native_wrapper(method); + mono_mb_emit_managed_call (mb, native_wrapper, NULL); + } + else { + if (method->flags & METHOD_ATTRIBUTE_VIRTUAL) + mono_mb_emit_op (mb, CEE_CALLVIRT, method); + else + mono_mb_emit_op (mb, CEE_CALL, method); + } + + if (!strcmp(method->name, ".ctor")) { + static MonoMethod *cache_proxy = NULL; + + if (!cache_proxy) + cache_proxy = mono_class_get_method_from_name (mono_class_get_interop_proxy_class (), "CacheProxy", 0); + + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoTransparentProxy, rp)); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_managed_call (mb, cache_proxy, NULL); + } + + mono_marshal_emit_thread_interrupt_checkpoint (mb); + + mono_mb_emit_byte (mb, CEE_RET); +#endif /* DISABLE_JIT */ + + res = mono_mb_create_and_cache (cache, method, mb, sig, sig->param_count + 16); + mono_mb_free (mb); + + return res; +} + +/* Maps a managed object to its unmanaged representation + * i.e. it's COM Callable Wrapper (CCW). + * Key: MonoObject* + * Value: MonoCCW* + */ +static GHashTable* ccw_hash = NULL; + +/* Maps a CCW interface to it's containing CCW. + * Note that a CCW support many interfaces. + * Key: MonoCCW* + * Value: MonoCCWInterface* + */ +static GHashTable* ccw_interface_hash = NULL; + +/* Maps the IUnknown value of a RCW to + * it's MonoComInteropProxy*. + * Key: void* + * Value: gchandle + */ +static GHashTable* rcw_hash = NULL; + +int +mono_cominterop_emit_marshal_com_interface (EmitMarshalContext *m, int argnum, + MonoType *t, + MonoMarshalSpec *spec, + int conv_arg, MonoType **conv_arg_type, + MarshalAction action) +{ + MonoMethodBuilder *mb = m->mb; + MonoClass *klass = t->data.klass; + static MonoMethod* get_object_for_iunknown = NULL; + static MonoMethod* get_iunknown_for_object_internal = NULL; + static MonoMethod* get_com_interface_for_object_internal = NULL; + static MonoMethod* get_idispatch_for_object_internal = NULL; + static MonoMethod* marshal_release = NULL; + static MonoMethod* AddRef = NULL; + if (!get_object_for_iunknown) + get_object_for_iunknown = mono_class_get_method_from_name (mono_defaults.marshal_class, "GetObjectForIUnknown", 1); + if (!get_iunknown_for_object_internal) + get_iunknown_for_object_internal = mono_class_get_method_from_name (mono_defaults.marshal_class, "GetIUnknownForObjectInternal", 1); + if (!get_idispatch_for_object_internal) + get_idispatch_for_object_internal = mono_class_get_method_from_name (mono_defaults.marshal_class, "GetIDispatchForObjectInternal", 1); + if (!get_com_interface_for_object_internal) + get_com_interface_for_object_internal = mono_class_get_method_from_name (mono_defaults.marshal_class, "GetComInterfaceForObjectInternal", 2); + if (!marshal_release) + marshal_release = mono_class_get_method_from_name (mono_defaults.marshal_class, "Release", 1); + +#ifdef DISABLE_JIT + switch (action) { + case MARSHAL_ACTION_CONV_IN: + *conv_arg_type = &mono_defaults.int_class->byval_arg; + break; + case MARSHAL_ACTION_MANAGED_CONV_IN: + *conv_arg_type = &mono_defaults.int_class->byval_arg; + break; + default: + break; + } +#else + switch (action) { + case MARSHAL_ACTION_CONV_IN: { + guint32 pos_null = 0; + + *conv_arg_type = &mono_defaults.int_class->byval_arg; + conv_arg = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + + mono_mb_emit_ptr (mb, NULL); + mono_mb_emit_stloc (mb, conv_arg); + + /* we dont need any conversions for out parameters */ + if (t->byref && t->attrs & PARAM_ATTRIBUTE_OUT) + break; + + mono_mb_emit_ldarg (mb, argnum); + if (t->byref) + mono_mb_emit_byte (mb, CEE_LDIND_REF); + /* if null just break, conv arg was already inited to 0 */ + pos_null = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S); + + mono_mb_emit_ldarg (mb, argnum); + if (t->byref) + mono_mb_emit_byte (mb, CEE_LDIND_REF); + + if (klass && klass != mono_defaults.object_class) { + mono_mb_emit_ptr (mb, t); + mono_mb_emit_icall (mb, cominterop_type_from_handle); + mono_mb_emit_managed_call (mb, get_com_interface_for_object_internal, NULL); + } + else if (spec->native == MONO_NATIVE_IUNKNOWN) + mono_mb_emit_managed_call (mb, get_iunknown_for_object_internal, NULL); + else if (spec->native == MONO_NATIVE_IDISPATCH) + mono_mb_emit_managed_call (mb, get_idispatch_for_object_internal, NULL); + else if (!klass && spec->native == MONO_NATIVE_INTERFACE) + mono_mb_emit_managed_call (mb, get_iunknown_for_object_internal, NULL); + else + g_assert_not_reached (); + mono_mb_emit_stloc (mb, conv_arg); + mono_mb_patch_short_branch (mb, pos_null); + break; + } + + case MARSHAL_ACTION_CONV_OUT: { + if (t->byref && (t->attrs & PARAM_ATTRIBUTE_OUT)) { + int ccw_obj; + guint32 pos_null = 0, pos_ccw = 0, pos_end = 0; + ccw_obj = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_byte (mb, CEE_LDNULL); + mono_mb_emit_byte (mb, CEE_STIND_REF); + + mono_mb_emit_ldloc (mb, conv_arg); + pos_null = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S); + + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_icon (mb, TRUE); + mono_mb_emit_icall (mb, cominterop_get_ccw_object); + mono_mb_emit_stloc (mb, ccw_obj); + mono_mb_emit_ldloc (mb, ccw_obj); + pos_ccw = mono_mb_emit_short_branch (mb, CEE_BRTRUE_S); + + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_managed_call (mb, get_object_for_iunknown, NULL); + + if (klass && klass != mono_defaults.object_class) + mono_mb_emit_op (mb, CEE_CASTCLASS, klass); + mono_mb_emit_byte (mb, CEE_STIND_REF); + + pos_end = mono_mb_emit_short_branch (mb, CEE_BR_S); + + /* is already managed object */ + mono_mb_patch_short_branch (mb, pos_ccw); + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_ldloc (mb, ccw_obj); + + if (klass && klass != mono_defaults.object_class) + mono_mb_emit_op (mb, CEE_CASTCLASS, klass); + mono_mb_emit_byte (mb, CEE_STIND_REF); + + mono_mb_patch_short_branch (mb, pos_end); + + /* need to call Release to follow COM rules of ownership */ + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_managed_call (mb, marshal_release, NULL); + mono_mb_emit_byte (mb, CEE_POP); + + /* case if null */ + mono_mb_patch_short_branch (mb, pos_null); + } + break; + } + case MARSHAL_ACTION_PUSH: + if (t->byref) + mono_mb_emit_ldloc_addr (mb, conv_arg); + else + mono_mb_emit_ldloc (mb, conv_arg); + break; + + case MARSHAL_ACTION_CONV_RESULT: { + int ccw_obj, ret_ptr; + guint32 pos_null = 0, pos_ccw = 0, pos_end = 0; + ccw_obj = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + ret_ptr = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + + /* store return value */ + mono_mb_emit_stloc (mb, ret_ptr); + + mono_mb_emit_ldloc (mb, ret_ptr); + pos_null = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S); + + mono_mb_emit_ldloc (mb, ret_ptr); + mono_mb_emit_icon (mb, TRUE); + mono_mb_emit_icall (mb, cominterop_get_ccw_object); + mono_mb_emit_stloc (mb, ccw_obj); + mono_mb_emit_ldloc (mb, ccw_obj); + pos_ccw = mono_mb_emit_short_branch (mb, CEE_BRTRUE_S); + + mono_mb_emit_ldloc (mb, ret_ptr); + mono_mb_emit_managed_call (mb, get_object_for_iunknown, NULL); + + if (klass && klass != mono_defaults.object_class) + mono_mb_emit_op (mb, CEE_CASTCLASS, klass); + mono_mb_emit_stloc (mb, 3); + + pos_end = mono_mb_emit_short_branch (mb, CEE_BR_S); + + /* is already managed object */ + mono_mb_patch_short_branch (mb, pos_ccw); + mono_mb_emit_ldloc (mb, ccw_obj); + + if (klass && klass != mono_defaults.object_class) + mono_mb_emit_op (mb, CEE_CASTCLASS, klass); + mono_mb_emit_stloc (mb, 3); + + mono_mb_patch_short_branch (mb, pos_end); + + /* need to call Release to follow COM rules of ownership */ + mono_mb_emit_ldloc (mb, ret_ptr); + mono_mb_emit_managed_call (mb, marshal_release, NULL); + mono_mb_emit_byte (mb, CEE_POP); + + /* case if null */ + mono_mb_patch_short_branch (mb, pos_null); + break; + } + + case MARSHAL_ACTION_MANAGED_CONV_IN: { + int ccw_obj; + guint32 pos_null = 0, pos_ccw = 0, pos_end = 0; + ccw_obj = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + + klass = mono_class_from_mono_type (t); + conv_arg = mono_mb_add_local (mb, &klass->byval_arg); + *conv_arg_type = &mono_defaults.int_class->byval_arg; + + mono_mb_emit_byte (mb, CEE_LDNULL); + mono_mb_emit_stloc (mb, conv_arg); + if (t->attrs & PARAM_ATTRIBUTE_OUT) + break; + + mono_mb_emit_ldarg (mb, argnum); + if (t->byref) + mono_mb_emit_byte (mb, CEE_LDIND_REF); + pos_null = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S); + + mono_mb_emit_ldarg (mb, argnum); + if (t->byref) + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_icon (mb, TRUE); + mono_mb_emit_icall (mb, cominterop_get_ccw_object); + mono_mb_emit_stloc (mb, ccw_obj); + mono_mb_emit_ldloc (mb, ccw_obj); + pos_ccw = mono_mb_emit_short_branch (mb, CEE_BRTRUE_S); + + + mono_mb_emit_ldarg (mb, argnum); + if (t->byref) + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_managed_call (mb, get_object_for_iunknown, NULL); + + if (klass && klass != mono_defaults.object_class) + mono_mb_emit_op (mb, CEE_CASTCLASS, klass); + mono_mb_emit_stloc (mb, conv_arg); + pos_end = mono_mb_emit_short_branch (mb, CEE_BR_S); + + /* is already managed object */ + mono_mb_patch_short_branch (mb, pos_ccw); + mono_mb_emit_ldloc (mb, ccw_obj); + if (klass && klass != mono_defaults.object_class) + mono_mb_emit_op (mb, CEE_CASTCLASS, klass); + mono_mb_emit_stloc (mb, conv_arg); + + mono_mb_patch_short_branch (mb, pos_end); + /* case if null */ + mono_mb_patch_short_branch (mb, pos_null); + break; + } + + case MARSHAL_ACTION_MANAGED_CONV_OUT: { + if (t->byref && t->attrs & PARAM_ATTRIBUTE_OUT) { + guint32 pos_null = 0; + + if (!AddRef) + AddRef = mono_class_get_method_from_name (mono_defaults.marshal_class, "AddRef", 1); + + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_byte (mb, CEE_LDC_I4_0); + mono_mb_emit_byte (mb, CEE_STIND_I); + + mono_mb_emit_ldloc (mb, conv_arg); + pos_null = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S); + + /* to store later */ + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_ldloc (mb, conv_arg); + if (klass && klass != mono_defaults.object_class) { + mono_mb_emit_ptr (mb, t); + mono_mb_emit_icall (mb, cominterop_type_from_handle); + mono_mb_emit_managed_call (mb, get_com_interface_for_object_internal, NULL); + } + else if (spec->native == MONO_NATIVE_IUNKNOWN) + mono_mb_emit_managed_call (mb, get_iunknown_for_object_internal, NULL); + else if (spec->native == MONO_NATIVE_IDISPATCH) + mono_mb_emit_managed_call (mb, get_idispatch_for_object_internal, NULL); + else if (!klass && spec->native == MONO_NATIVE_INTERFACE) + mono_mb_emit_managed_call (mb, get_iunknown_for_object_internal, NULL); + else + g_assert_not_reached (); + mono_mb_emit_byte (mb, CEE_STIND_I); + + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_managed_call (mb, AddRef, NULL); + mono_mb_emit_byte (mb, CEE_POP); + + mono_mb_patch_short_branch (mb, pos_null); + } + break; + } + + case MARSHAL_ACTION_MANAGED_CONV_RESULT: { + guint32 pos_null = 0; + int ccw_obj; + ccw_obj = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + + if (!AddRef) + AddRef = mono_class_get_method_from_name (mono_defaults.marshal_class, "AddRef", 1); + + /* store return value */ + mono_mb_emit_stloc (mb, ccw_obj); + + mono_mb_emit_ldloc (mb, ccw_obj); + + /* if null just break, conv arg was already inited to 0 */ + pos_null = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S); + + /* to store later */ + mono_mb_emit_ldloc (mb, ccw_obj); + if (klass && klass != mono_defaults.object_class) { + mono_mb_emit_ptr (mb, t); + mono_mb_emit_icall (mb, cominterop_type_from_handle); + mono_mb_emit_managed_call (mb, get_com_interface_for_object_internal, NULL); + } + else if (spec->native == MONO_NATIVE_IUNKNOWN) + mono_mb_emit_managed_call (mb, get_iunknown_for_object_internal, NULL); + else if (spec->native == MONO_NATIVE_IDISPATCH) + mono_mb_emit_managed_call (mb, get_idispatch_for_object_internal, NULL); + else if (!klass && spec->native == MONO_NATIVE_INTERFACE) + mono_mb_emit_managed_call (mb, get_iunknown_for_object_internal, NULL); + else + g_assert_not_reached (); + mono_mb_emit_stloc (mb, 3); + mono_mb_emit_ldloc (mb, 3); + + mono_mb_emit_managed_call (mb, AddRef, NULL); + mono_mb_emit_byte (mb, CEE_POP); + + mono_mb_patch_short_branch (mb, pos_null); + break; + } + + default: + g_assert_not_reached (); + } +#endif /* DISABLE_JIT */ + + return conv_arg; +} + +typedef struct +{ + int (STDCALL *QueryInterface)(gpointer pUnk, gpointer riid, gpointer* ppv); + int (STDCALL *AddRef)(gpointer pUnk); + int (STDCALL *Release)(gpointer pUnk); +} MonoIUnknown; + +#define MONO_S_OK 0x00000000L +#define MONO_E_NOINTERFACE 0x80004002L +#define MONO_E_NOTIMPL 0x80004001L +#define MONO_E_INVALIDARG 0x80070057L +#define MONO_E_DISP_E_UNKNOWNNAME 0x80020006L +#define MONO_E_DISPID_UNKNOWN (gint32)-1 + +int +ves_icall_System_Runtime_InteropServices_Marshal_AddRefInternal (gpointer pUnk) +{ + g_assert (pUnk); + return (*(MonoIUnknown**)pUnk)->AddRef(pUnk); +} + +int +ves_icall_System_Runtime_InteropServices_Marshal_QueryInterfaceInternal (gpointer pUnk, gpointer riid, gpointer* ppv) +{ + g_assert (pUnk); + return (*(MonoIUnknown**)pUnk)->QueryInterface(pUnk, riid, ppv); +} + +int +ves_icall_System_Runtime_InteropServices_Marshal_ReleaseInternal (gpointer pUnk) +{ + g_assert (pUnk); + return (*(MonoIUnknown**)pUnk)->Release(pUnk); +} + +static gboolean cominterop_can_support_dispatch (MonoClass* klass) +{ + if (!mono_class_is_public (klass)) + return FALSE; + + if (!cominterop_com_visible (klass)) + return FALSE; + + return TRUE; +} + +static void* +cominterop_get_idispatch_for_object (MonoObject* object, MonoError *error) +{ + error_init (error); + if (!object) + return NULL; + + if (cominterop_object_is_rcw (object)) { + return cominterop_get_interface_checked (((MonoComInteropProxy*)((MonoTransparentProxy*)object)->rp)->com_object, + mono_class_get_idispatch_class (), error); + } + else { + MonoClass* klass = mono_object_class (object); + if (!cominterop_can_support_dispatch (klass) ) { + cominterop_set_hr_error (error, MONO_E_NOINTERFACE); + return NULL; + } + return cominterop_get_ccw_checked (object, mono_class_get_idispatch_class (), error); + } +} + +void* +ves_icall_System_Runtime_InteropServices_Marshal_GetIUnknownForObjectInternal (MonoObject* object) +{ +#ifndef DISABLE_COM + MonoError error; + + if (!object) + return NULL; + + if (cominterop_object_is_rcw (object)) { + MonoClass *klass = NULL; + MonoRealProxy* real_proxy = NULL; + if (!object) + return NULL; + klass = mono_object_class (object); + if (!mono_class_is_transparent_proxy (klass)) { + g_assert_not_reached (); + return NULL; + } + + real_proxy = ((MonoTransparentProxy*)object)->rp; + if (!real_proxy) { + g_assert_not_reached (); + return NULL; + } + + klass = mono_object_class (real_proxy); + if (klass != mono_class_get_interop_proxy_class ()) { + g_assert_not_reached (); + return NULL; + } + + if (!((MonoComInteropProxy*)real_proxy)->com_object) { + g_assert_not_reached (); + return NULL; + } + + return ((MonoComInteropProxy*)real_proxy)->com_object->iunknown; + } + else { + void* ccw_entry = cominterop_get_ccw_checked (object, mono_class_get_iunknown_class (), &error); + mono_error_set_pending_exception (&error); + return ccw_entry; + } +#else + g_assert_not_reached (); +#endif +} + +MonoObject* +ves_icall_System_Runtime_InteropServices_Marshal_GetObjectForCCW (void* pUnk) +{ +#ifndef DISABLE_COM + MonoObject* object = NULL; + + if (!pUnk) + return NULL; + + /* see if it is a CCW */ + object = cominterop_get_ccw_object ((MonoCCWInterface*)pUnk, TRUE); + + return object; +#else + g_assert_not_reached (); +#endif +} + +void* +ves_icall_System_Runtime_InteropServices_Marshal_GetIDispatchForObjectInternal (MonoObject* object) +{ +#ifndef DISABLE_COM + MonoError error; + void* idisp = cominterop_get_idispatch_for_object (object, &error); + mono_error_set_pending_exception (&error); + return idisp; +#else + g_assert_not_reached (); +#endif +} + +void* +ves_icall_System_Runtime_InteropServices_Marshal_GetCCW (MonoObject* object, MonoReflectionType* type) +{ +#ifndef DISABLE_COM + MonoError error; + MonoClass* klass = NULL; + void* itf = NULL; + g_assert (type); + g_assert (type->type); + klass = mono_type_get_class (type->type); + g_assert (klass); + if (!mono_class_init (klass)) { + mono_set_pending_exception (mono_class_get_exception_for_failure (klass)); + return NULL; + } + + itf = cominterop_get_ccw_checked (object, klass, &error); + mono_error_set_pending_exception (&error); + return itf; +#else + g_assert_not_reached (); +#endif +} + +gint32 +ves_icall_System_Runtime_InteropServices_Marshal_ReleaseComObjectInternal (MonoObject* object) +{ +#ifndef DISABLE_COM + MonoComInteropProxy* proxy = NULL; + gint32 ref_count = 0; + + g_assert (object); + g_assert (cominterop_object_is_rcw (object)); + + proxy = (MonoComInteropProxy*)((MonoTransparentProxy*)object)->rp; + g_assert (proxy); + + if (proxy->ref_count == 0) + return -1; + + ref_count = mono_atomic_dec_i32 (&proxy->ref_count); + + g_assert (ref_count >= 0); + + if (ref_count == 0) + ves_icall_System_ComObject_ReleaseInterfaces (proxy->com_object); + + return ref_count; +#else + g_assert_not_reached (); +#endif +} + +guint32 +ves_icall_System_Runtime_InteropServices_Marshal_GetComSlotForMethodInfoInternal (MonoReflectionMethod *m) +{ +#ifndef DISABLE_COM + MonoError error; + int slot = cominterop_get_com_slot_for_method (m->method, &error); + mono_error_assert_ok (&error); + return slot; +#else + g_assert_not_reached (); +#endif +} + +/* Only used for COM RCWs */ +MonoObject * +ves_icall_System_ComObject_CreateRCW (MonoReflectionType *type) +{ + MonoError error; + MonoClass *klass; + MonoDomain *domain; + MonoObject *obj; + + domain = mono_object_domain (type); + klass = mono_class_from_mono_type (type->type); + + /* call mono_object_new_alloc_specific_checked instead of mono_object_new + * because we want to actually create object. mono_object_new checks + * to see if type is import and creates transparent proxy. this method + * is called by the corresponding real proxy to create the real RCW. + * Constructor does not need to be called. Will be called later. + */ + MonoVTable *vtable = mono_class_vtable_full (domain, klass, &error); + if (mono_error_set_pending_exception (&error)) + return NULL; + obj = mono_object_new_alloc_specific_checked (vtable, &error); + if (mono_error_set_pending_exception (&error)) + return NULL; + + return obj; +} + +static gboolean +cominterop_rcw_interface_finalizer (gpointer key, gpointer value, gpointer user_data) +{ + ves_icall_System_Runtime_InteropServices_Marshal_ReleaseInternal (value); + return TRUE; +} + +void +ves_icall_System_ComObject_ReleaseInterfaces (MonoComObject* obj) +{ + g_assert(obj); + if (obj->itf_hash) { + guint32 gchandle = 0; + mono_cominterop_lock (); + gchandle = GPOINTER_TO_UINT (g_hash_table_lookup (rcw_hash, obj->iunknown)); + if (gchandle) { + mono_gchandle_free (gchandle); + g_hash_table_remove (rcw_hash, obj->iunknown); + } + + g_hash_table_foreach_remove (obj->itf_hash, cominterop_rcw_interface_finalizer, NULL); + g_hash_table_destroy (obj->itf_hash); + ves_icall_System_Runtime_InteropServices_Marshal_ReleaseInternal (obj->iunknown); + obj->iunknown = NULL; + obj->itf_hash = NULL; + mono_cominterop_unlock (); + } +} + +static gboolean +cominterop_rcw_finalizer (gpointer key, gpointer value, gpointer user_data) +{ + guint32 gchandle = 0; + + gchandle = GPOINTER_TO_UINT (value); + if (gchandle) { + MonoComInteropProxy* proxy = (MonoComInteropProxy*)mono_gchandle_get_target (gchandle); + + if (proxy) { + if (proxy->com_object->itf_hash) { + g_hash_table_foreach_remove (proxy->com_object->itf_hash, cominterop_rcw_interface_finalizer, NULL); + g_hash_table_destroy (proxy->com_object->itf_hash); + } + if (proxy->com_object->iunknown) + ves_icall_System_Runtime_InteropServices_Marshal_ReleaseInternal (proxy->com_object->iunknown); + proxy->com_object->iunknown = NULL; + proxy->com_object->itf_hash = NULL; + } + + mono_gchandle_free (gchandle); + } + + return TRUE; +} + +void +cominterop_release_all_rcws (void) +{ + if (!rcw_hash) + return; + + mono_cominterop_lock (); + + g_hash_table_foreach_remove (rcw_hash, cominterop_rcw_finalizer, NULL); + g_hash_table_destroy (rcw_hash); + rcw_hash = NULL; + + mono_cominterop_unlock (); +} + +gpointer +ves_icall_System_ComObject_GetInterfaceInternal (MonoComObject* obj, MonoReflectionType* type, MonoBoolean throw_exception) +{ +#ifndef DISABLE_COM + MonoError error; + MonoClass *klass = mono_type_get_class (type->type); + if (!mono_class_init (klass)) { + mono_set_pending_exception (mono_class_get_exception_for_failure (klass)); + return NULL; + } + + gpointer itf = cominterop_get_interface_checked (obj, klass, &error); + if (throw_exception) + mono_error_set_pending_exception (&error); + else + mono_error_cleanup (&error); + return itf; +#else + g_assert_not_reached (); +#endif +} + +void +ves_icall_Mono_Interop_ComInteropProxy_AddProxy (gpointer pUnk, MonoComInteropProxy* proxy) +{ +#ifndef DISABLE_COM + guint32 gchandle = 0; + if (!rcw_hash) { + mono_cominterop_lock (); + rcw_hash = g_hash_table_new (mono_aligned_addr_hash, NULL); + mono_cominterop_unlock (); + } + + gchandle = mono_gchandle_new_weakref ((MonoObject*)proxy, FALSE); + + mono_cominterop_lock (); + g_hash_table_insert (rcw_hash, pUnk, GUINT_TO_POINTER (gchandle)); + mono_cominterop_unlock (); +#else + g_assert_not_reached (); +#endif +} + +MonoComInteropProxy* +ves_icall_Mono_Interop_ComInteropProxy_FindProxy (gpointer pUnk) +{ +#ifndef DISABLE_COM + MonoComInteropProxy* proxy = NULL; + guint32 gchandle = 0; + + mono_cominterop_lock (); + if (rcw_hash) + gchandle = GPOINTER_TO_UINT (g_hash_table_lookup (rcw_hash, pUnk)); + mono_cominterop_unlock (); + if (gchandle) { + proxy = (MonoComInteropProxy*)mono_gchandle_get_target (gchandle); + /* proxy is null means we need to free up old RCW */ + if (!proxy) { + mono_gchandle_free (gchandle); + g_hash_table_remove (rcw_hash, pUnk); + } + } + return proxy; +#else + g_assert_not_reached (); +#endif +} + +/** + * cominterop_get_ccw_object: + * @ccw_entry: a pointer to the CCWEntry + * @verify: verify ccw_entry is in fact a ccw + * + * Returns: the corresponding object for the CCW + */ +static MonoObject* +cominterop_get_ccw_object (MonoCCWInterface* ccw_entry, gboolean verify) +{ + MonoCCW *ccw = NULL; + + /* no CCW's exist yet */ + if (!ccw_interface_hash) + return NULL; + + if (verify) { + ccw = (MonoCCW *)g_hash_table_lookup (ccw_interface_hash, ccw_entry); + } + else { + ccw = ccw_entry->ccw; + g_assert (ccw); + } + if (ccw) + return mono_gchandle_get_target (ccw->gc_handle); + else + return NULL; +} + +static void +cominterop_setup_marshal_context (EmitMarshalContext *m, MonoMethod *method) +{ + MonoMethodSignature *sig, *csig; + sig = mono_method_signature (method); + /* we copy the signature, so that we can modify it */ + /* FIXME: which to use? */ + csig = mono_metadata_signature_dup_full (method->klass->image, sig); + /* csig = mono_metadata_signature_dup (sig); */ + + /* STDCALL on windows, CDECL everywhere else to work with XPCOM and MainWin COM */ +#ifdef HOST_WIN32 + csig->call_convention = MONO_CALL_STDCALL; +#else + csig->call_convention = MONO_CALL_C; +#endif + csig->hasthis = 0; + csig->pinvoke = 1; + + m->image = method->klass->image; + m->piinfo = NULL; + m->retobj_var = 0; + m->sig = sig; + m->csig = csig; +} + +/** + * cominterop_get_ccw_checked: + * @object: a pointer to the object + * @itf: interface type needed + * @error: set on error + * + * Returns: a value indicating if the object is a + * Runtime Callable Wrapper (RCW) for a COM object. + * On failure returns NULL and sets @error. + */ +static gpointer +cominterop_get_ccw_checked (MonoObject* object, MonoClass* itf, MonoError *error) +{ + int i; + MonoCCW *ccw = NULL; + MonoCCWInterface* ccw_entry = NULL; + gpointer *vtable = NULL; + static gpointer iunknown[3] = {NULL, NULL, NULL}; + static gpointer idispatch[4] = {NULL, NULL, NULL, NULL}; + MonoClass* iface = NULL; + MonoClass* klass = NULL; + EmitMarshalContext m; + int start_slot = 3; + int method_count = 0; + GList *ccw_list, *ccw_list_item; + MonoCustomAttrInfo *cinfo = NULL; + + error_init (error); + + if (!object) + return NULL; + + klass = mono_object_get_class (object); + + mono_cominterop_lock (); + if (!ccw_hash) + ccw_hash = g_hash_table_new (mono_aligned_addr_hash, NULL); + if (!ccw_interface_hash) + ccw_interface_hash = g_hash_table_new (mono_aligned_addr_hash, NULL); + + ccw_list = (GList *)g_hash_table_lookup (ccw_hash, GINT_TO_POINTER (mono_object_hash (object))); + mono_cominterop_unlock (); + + ccw_list_item = ccw_list; + while (ccw_list_item) { + MonoCCW* ccw_iter = (MonoCCW *)ccw_list_item->data; + if (mono_gchandle_get_target (ccw_iter->gc_handle) == object) { + ccw = ccw_iter; + break; + } + ccw_list_item = g_list_next(ccw_list_item); + } + + if (!iunknown [0]) { + iunknown [0] = cominterop_ccw_queryinterface; + iunknown [1] = cominterop_ccw_addref; + iunknown [2] = cominterop_ccw_release; + } + + if (!idispatch [0]) { + idispatch [0] = cominterop_ccw_get_type_info_count; + idispatch [1] = cominterop_ccw_get_type_info; + idispatch [2] = cominterop_ccw_get_ids_of_names; + idispatch [3] = cominterop_ccw_invoke; + } + + if (!ccw) { + ccw = g_new0 (MonoCCW, 1); +#ifdef HOST_WIN32 + ccw->free_marshaler = 0; +#endif + ccw->vtable_hash = g_hash_table_new (mono_aligned_addr_hash, NULL); + ccw->ref_count = 0; + /* just alloc a weak handle until we are addref'd*/ + ccw->gc_handle = mono_gchandle_new_weakref (object, FALSE); + + if (!ccw_list) { + ccw_list = g_list_alloc (); + ccw_list->data = ccw; + } + else + ccw_list = g_list_append (ccw_list, ccw); + mono_cominterop_lock (); + g_hash_table_insert (ccw_hash, GINT_TO_POINTER (mono_object_hash (object)), ccw_list); + mono_cominterop_unlock (); + /* register for finalization to clean up ccw */ + mono_object_register_finalizer (object); + } + + cinfo = mono_custom_attrs_from_class_checked (itf, error); + mono_error_assert_ok (error); + if (cinfo) { + static MonoClass* coclass_attribute = NULL; + if (!coclass_attribute) + coclass_attribute = mono_class_load_from_name (mono_defaults.corlib, "System.Runtime.InteropServices", "CoClassAttribute"); + if (mono_custom_attrs_has_attr (cinfo, coclass_attribute)) { + g_assert(itf->interface_count && itf->interfaces[0]); + itf = itf->interfaces[0]; + } + if (!cinfo->cached) + mono_custom_attrs_free (cinfo); + } + + iface = itf; + if (iface == mono_class_get_iunknown_class ()) { + start_slot = 3; + } + else if (iface == mono_class_get_idispatch_class ()) { + start_slot = 7; + } + else { + method_count += mono_class_get_method_count (iface); + start_slot = cominterop_get_com_slot_begin (iface); + iface = NULL; + } + + ccw_entry = (MonoCCWInterface *)g_hash_table_lookup (ccw->vtable_hash, itf); + + if (!ccw_entry) { + int vtable_index = method_count-1+start_slot; + vtable = (void **)mono_image_alloc0 (klass->image, sizeof (gpointer)*(method_count+start_slot)); + memcpy (vtable, iunknown, sizeof (iunknown)); + if (start_slot == 7) + memcpy (vtable+3, idispatch, sizeof (idispatch)); + + iface = itf; + for (i = mono_class_get_method_count (iface) - 1; i >= 0; i--) { + int param_index = 0; + MonoMethodBuilder *mb; + MonoMarshalSpec ** mspecs; + MonoMethod *wrapper_method, *adjust_method; + MonoMethod *method = iface->methods [i]; + MonoMethodSignature* sig_adjusted; + MonoMethodSignature* sig = mono_method_signature (method); + gboolean preserve_sig = method->iflags & METHOD_IMPL_ATTRIBUTE_PRESERVE_SIG; + + + mb = mono_mb_new (iface, method->name, MONO_WRAPPER_NATIVE_TO_MANAGED); + adjust_method = cominterop_get_managed_wrapper_adjusted (method); + sig_adjusted = mono_method_signature (adjust_method); + + mspecs = g_new (MonoMarshalSpec*, sig_adjusted->param_count + 1); + mono_method_get_marshal_info (method, mspecs); + + + /* move managed args up one */ + for (param_index = sig->param_count; param_index >= 1; param_index--) { + int mspec_index = param_index+1; + mspecs [mspec_index] = mspecs [param_index]; + + if (mspecs[mspec_index] == NULL) { + if (sig_adjusted->params[param_index]->type == MONO_TYPE_OBJECT) { + mspecs[mspec_index] = g_new0 (MonoMarshalSpec, 1); + mspecs[mspec_index]->native = MONO_NATIVE_STRUCT; + } + else if (sig_adjusted->params[param_index]->type == MONO_TYPE_STRING) { + mspecs[mspec_index] = g_new0 (MonoMarshalSpec, 1); + mspecs[mspec_index]->native = MONO_NATIVE_BSTR; + } + else if (sig_adjusted->params[param_index]->type == MONO_TYPE_CLASS) { + mspecs[mspec_index] = g_new0 (MonoMarshalSpec, 1); + mspecs[mspec_index]->native = MONO_NATIVE_INTERFACE; + } + else if (sig_adjusted->params[param_index]->type == MONO_TYPE_BOOLEAN) { + mspecs[mspec_index] = g_new0 (MonoMarshalSpec, 1); + mspecs[mspec_index]->native = MONO_NATIVE_VARIANTBOOL; + } + } else { + /* increase SizeParamIndex since we've added a param */ + if (sig_adjusted->params[param_index]->type == MONO_TYPE_ARRAY || + sig_adjusted->params[param_index]->type == MONO_TYPE_SZARRAY) + if (mspecs[mspec_index]->data.array_data.param_num != -1) + mspecs[mspec_index]->data.array_data.param_num++; + } + } + + /* first arg is IntPtr for interface */ + mspecs [1] = NULL; + + /* move return spec to last param */ + if (!preserve_sig && !MONO_TYPE_IS_VOID (sig->ret)) { + if (mspecs [0] == NULL) { + if (sig_adjusted->params[sig_adjusted->param_count-1]->type == MONO_TYPE_OBJECT) { + mspecs[0] = g_new0 (MonoMarshalSpec, 1); + mspecs[0]->native = MONO_NATIVE_STRUCT; + } + else if (sig_adjusted->params[sig_adjusted->param_count-1]->type == MONO_TYPE_STRING) { + mspecs[0] = g_new0 (MonoMarshalSpec, 1); + mspecs[0]->native = MONO_NATIVE_BSTR; + } + else if (sig_adjusted->params[sig_adjusted->param_count-1]->type == MONO_TYPE_CLASS) { + mspecs[0] = g_new0 (MonoMarshalSpec, 1); + mspecs[0]->native = MONO_NATIVE_INTERFACE; + } + else if (sig_adjusted->params[sig_adjusted->param_count-1]->type == MONO_TYPE_BOOLEAN) { + mspecs[0] = g_new0 (MonoMarshalSpec, 1); + mspecs[0]->native = MONO_NATIVE_VARIANTBOOL; + } + } + + mspecs [sig_adjusted->param_count] = mspecs [0]; + mspecs [0] = NULL; + } + +#ifndef DISABLE_JIT + /* skip visiblity since we call internal methods */ + mb->skip_visibility = TRUE; +#endif + + cominterop_setup_marshal_context (&m, adjust_method); + m.mb = mb; + mono_marshal_emit_managed_wrapper (mb, sig_adjusted, mspecs, &m, adjust_method, 0); + mono_cominterop_lock (); + wrapper_method = mono_mb_create_method (mb, m.csig, m.csig->param_count + 16); + mono_cominterop_unlock (); + + vtable [vtable_index--] = mono_compile_method_checked (wrapper_method, error); + + // cleanup, then error out if compile_method failed + for (param_index = sig_adjusted->param_count; param_index >= 0; param_index--) + if (mspecs [param_index]) + mono_metadata_free_marshal_spec (mspecs [param_index]); + g_free (mspecs); + return_val_if_nok (error, NULL); + } + + ccw_entry = g_new0 (MonoCCWInterface, 1); + ccw_entry->ccw = ccw; + ccw_entry->vtable = vtable; + g_hash_table_insert (ccw->vtable_hash, itf, ccw_entry); + g_hash_table_insert (ccw_interface_hash, ccw_entry, ccw); + } + + return ccw_entry; +} + +/** + * cominterop_get_ccw: + * @object: a pointer to the object + * @itf: interface type needed + * + * Returns: a value indicating if the object is a + * Runtime Callable Wrapper (RCW) for a COM object + */ +static gpointer +cominterop_get_ccw (MonoObject* object, MonoClass* itf) +{ + MonoError error; + gpointer ccw_entry = cominterop_get_ccw_checked (object, itf, &error); + mono_error_set_pending_exception (&error); + return ccw_entry; +} + +static gboolean +mono_marshal_free_ccw_entry (gpointer key, gpointer value, gpointer user_data) +{ + g_hash_table_remove (ccw_interface_hash, value); + g_assert (value); + g_free (value); + return TRUE; +} + +/** + * mono_marshal_free_ccw: + * \param object the mono object + * \returns whether the object had a CCW + */ +gboolean +mono_marshal_free_ccw (MonoObject* object) +{ + GList *ccw_list, *ccw_list_orig, *ccw_list_item; + /* no ccw's were created */ + if (!ccw_hash || g_hash_table_size (ccw_hash) == 0) + return FALSE; + + /* need to cache orig list address to remove from hash_table if empty */ + mono_cominterop_lock (); + ccw_list = ccw_list_orig = (GList *)g_hash_table_lookup (ccw_hash, GINT_TO_POINTER (mono_object_hash (object))); + mono_cominterop_unlock (); + + if (!ccw_list) + return FALSE; + + ccw_list_item = ccw_list; + while (ccw_list_item) { + MonoCCW* ccw_iter = (MonoCCW *)ccw_list_item->data; + MonoObject* handle_target = mono_gchandle_get_target (ccw_iter->gc_handle); + + /* Looks like the GC NULLs the weakref handle target before running the + * finalizer. So if we get a NULL target, destroy the CCW as well. + * Unless looking up the object from the CCW shows it not the right object. + */ + gboolean destroy_ccw = !handle_target || handle_target == object; + if (!handle_target) { + MonoCCWInterface* ccw_entry = (MonoCCWInterface *)g_hash_table_lookup (ccw_iter->vtable_hash, mono_class_get_iunknown_class ()); + if (!(ccw_entry && object == cominterop_get_ccw_object (ccw_entry, FALSE))) + destroy_ccw = FALSE; + } + + if (destroy_ccw) { + /* remove all interfaces */ + g_hash_table_foreach_remove (ccw_iter->vtable_hash, mono_marshal_free_ccw_entry, NULL); + g_hash_table_destroy (ccw_iter->vtable_hash); + + /* get next before we delete */ + ccw_list_item = g_list_next(ccw_list_item); + + /* remove ccw from list */ + ccw_list = g_list_remove (ccw_list, ccw_iter); + +#ifdef HOST_WIN32 + if (ccw_iter->free_marshaler) + ves_icall_System_Runtime_InteropServices_Marshal_ReleaseInternal (ccw_iter->free_marshaler); +#endif + + g_free (ccw_iter); + } + else + ccw_list_item = g_list_next (ccw_list_item); + } + + /* if list is empty remove original address from hash */ + if (g_list_length (ccw_list) == 0) + g_hash_table_remove (ccw_hash, GINT_TO_POINTER (mono_object_hash (object))); + else if (ccw_list != ccw_list_orig) + g_hash_table_insert (ccw_hash, GINT_TO_POINTER (mono_object_hash (object)), ccw_list); + + return TRUE; +} + +/** + * cominterop_get_managed_wrapper_adjusted: + * @method: managed COM Interop method + * + * Returns: the generated method to call with signature matching + * the unmanaged COM Method signature + */ +static MonoMethod * +cominterop_get_managed_wrapper_adjusted (MonoMethod *method) +{ + static MonoMethod *get_hr_for_exception = NULL; + MonoMethod *res = NULL; + MonoMethodBuilder *mb; + MonoMarshalSpec **mspecs; + MonoMethodSignature *sig, *sig_native; + MonoExceptionClause *main_clause = NULL; + int pos_leave; + int hr = 0; + int i; + gboolean preserve_sig = method->iflags & METHOD_IMPL_ATTRIBUTE_PRESERVE_SIG; + + if (!get_hr_for_exception) + get_hr_for_exception = mono_class_get_method_from_name (mono_defaults.marshal_class, "GetHRForException", -1); + + sig = mono_method_signature (method); + + /* create unmanaged wrapper */ + mb = mono_mb_new (method->klass, method->name, MONO_WRAPPER_COMINTEROP); + + sig_native = cominterop_method_signature (method); + + mspecs = g_new0 (MonoMarshalSpec*, sig_native->param_count+1); + + mono_method_get_marshal_info (method, mspecs); + + /* move managed args up one */ + for (i = sig->param_count; i >= 1; i--) + mspecs [i+1] = mspecs [i]; + + /* first arg is IntPtr for interface */ + mspecs [1] = NULL; + + /* move return spec to last param */ + if (!preserve_sig && !MONO_TYPE_IS_VOID (sig->ret)) + mspecs [sig_native->param_count] = mspecs [0]; + + mspecs [0] = NULL; + +#ifndef DISABLE_JIT + if (!preserve_sig) + hr = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg); + else if (!MONO_TYPE_IS_VOID (sig->ret)) + hr = mono_mb_add_local (mb, sig->ret); + + /* try */ + main_clause = g_new0 (MonoExceptionClause, 1); + main_clause->try_offset = mono_mb_get_label (mb); + + /* load last param to store result if not preserve_sig and not void */ + if (!preserve_sig && !MONO_TYPE_IS_VOID (sig->ret)) + mono_mb_emit_ldarg (mb, sig_native->param_count-1); + + /* the CCW -> object conversion */ + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_icon (mb, FALSE); + mono_mb_emit_icall (mb, cominterop_get_ccw_object); + + for (i = 0; i < sig->param_count; i++) + mono_mb_emit_ldarg (mb, i+1); + + mono_mb_emit_managed_call (mb, method, NULL); + + if (!MONO_TYPE_IS_VOID (sig->ret)) { + if (!preserve_sig) { + MonoClass *rclass = mono_class_from_mono_type (sig->ret); + if (rclass->valuetype) { + mono_mb_emit_op (mb, CEE_STOBJ, rclass); + } else { + mono_mb_emit_byte (mb, mono_type_to_stind (sig->ret)); + } + } else + mono_mb_emit_stloc (mb, hr); + } + + pos_leave = mono_mb_emit_branch (mb, CEE_LEAVE); + + /* Main exception catch */ + main_clause->flags = MONO_EXCEPTION_CLAUSE_NONE; + main_clause->try_len = mono_mb_get_pos (mb) - main_clause->try_offset; + main_clause->data.catch_class = mono_defaults.object_class; + + /* handler code */ + main_clause->handler_offset = mono_mb_get_label (mb); + + if (!preserve_sig || (sig->ret && !sig->ret->byref && (sig->ret->type == MONO_TYPE_U4 || sig->ret->type == MONO_TYPE_I4))) { + mono_mb_emit_managed_call (mb, get_hr_for_exception, NULL); + mono_mb_emit_stloc (mb, hr); + } + else { + mono_mb_emit_byte (mb, CEE_POP); + } + + mono_mb_emit_branch (mb, CEE_LEAVE); + main_clause->handler_len = mono_mb_get_pos (mb) - main_clause->handler_offset; + /* end catch */ + + mono_mb_set_clauses (mb, 1, main_clause); + + mono_mb_patch_branch (mb, pos_leave); + + if (!preserve_sig || !MONO_TYPE_IS_VOID (sig->ret)) + mono_mb_emit_ldloc (mb, hr); + + mono_mb_emit_byte (mb, CEE_RET); +#endif /* DISABLE_JIT */ + + mono_cominterop_lock (); + res = mono_mb_create_method (mb, sig_native, sig_native->param_count + 16); + mono_cominterop_unlock (); + + mono_mb_free (mb); + + for (i = sig_native->param_count; i >= 0; i--) + if (mspecs [i]) + mono_metadata_free_marshal_spec (mspecs [i]); + g_free (mspecs); + + return res; +} + +/** + * cominterop_mono_string_to_guid: + * + * Converts the standard string representation of a GUID + * to a 16 byte Microsoft GUID. + */ +static void +cominterop_mono_string_to_guid (MonoString* string, guint8 *guid) { + gunichar2 * chars = mono_string_chars (string); + int i = 0; + static guint8 indexes[16] = {7, 5, 3, 1, 12, 10, 17, 15, 20, 22, 25, 27, 29, 31, 33, 35}; + + for (i = 0; i < sizeof(indexes); i++) + guid [i] = g_unichar_xdigit_value (chars [indexes [i]]) + (g_unichar_xdigit_value (chars [indexes [i] - 1]) << 4); +} + +static gboolean +cominterop_class_guid_equal (guint8* guid, MonoClass* klass) +{ + guint8 klass_guid [16]; + if (cominterop_class_guid (klass, klass_guid)) + return !memcmp (guid, klass_guid, sizeof (klass_guid)); + return FALSE; +} + +static int STDCALL +cominterop_ccw_addref (MonoCCWInterface* ccwe) +{ + gint32 ref_count = 0; + MonoCCW* ccw = ccwe->ccw; + g_assert (ccw); + g_assert (ccw->gc_handle); + ref_count = mono_atomic_inc_i32 ((gint32*)&ccw->ref_count); + if (ref_count == 1) { + guint32 oldhandle = ccw->gc_handle; + g_assert (oldhandle); + /* since we now have a ref count, alloc a strong handle*/ + ccw->gc_handle = mono_gchandle_new (mono_gchandle_get_target (oldhandle), FALSE); + mono_gchandle_free (oldhandle); + } + return ref_count; +} + +static int STDCALL +cominterop_ccw_release (MonoCCWInterface* ccwe) +{ + gint32 ref_count = 0; + MonoCCW* ccw = ccwe->ccw; + g_assert (ccw); + g_assert (ccw->ref_count > 0); + ref_count = mono_atomic_dec_i32 ((gint32*)&ccw->ref_count); + if (ref_count == 0) { + /* allow gc of object */ + guint32 oldhandle = ccw->gc_handle; + g_assert (oldhandle); + ccw->gc_handle = mono_gchandle_new_weakref (mono_gchandle_get_target (oldhandle), FALSE); + mono_gchandle_free (oldhandle); + } + return ref_count; +} + +#ifdef HOST_WIN32 +static const IID MONO_IID_IMarshal = {0x3, 0x0, 0x0, {0xC0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46}}; +#endif + +#ifdef HOST_WIN32 +/* All ccw objects are free threaded */ +static int +cominterop_ccw_getfreethreadedmarshaler (MonoCCW* ccw, MonoObject* object, gpointer* ppv, MonoError *error) +{ + error_init (error); +#ifdef HOST_WIN32 + if (!ccw->free_marshaler) { + int ret = 0; + gpointer tunk; + tunk = cominterop_get_ccw_checked (object, mono_class_get_iunknown_class (), error); + return_val_if_nok (error, MONO_E_NOINTERFACE); + ret = CoCreateFreeThreadedMarshaler (tunk, (LPUNKNOWN*)&ccw->free_marshaler); + } + + if (!ccw->free_marshaler) + return MONO_E_NOINTERFACE; + + return ves_icall_System_Runtime_InteropServices_Marshal_QueryInterfaceInternal (ccw->free_marshaler, (IID*)&MONO_IID_IMarshal, ppv); +#else + return MONO_E_NOINTERFACE; +#endif +} +#endif + +static int STDCALL +cominterop_ccw_queryinterface (MonoCCWInterface* ccwe, guint8* riid, gpointer* ppv) +{ + MonoError error; + GPtrArray *ifaces; + MonoClass *itf = NULL; + int i; + MonoCCW* ccw = ccwe->ccw; + MonoClass* klass = NULL; + MonoClass* klass_iter = NULL; + MonoObject* object = mono_gchandle_get_target (ccw->gc_handle); + + g_assert (object); + klass = mono_object_class (object); + + if (ppv) + *ppv = NULL; + + if (!mono_domain_get ()) + mono_thread_attach (mono_get_root_domain ()); + + /* handle IUnknown special */ + if (cominterop_class_guid_equal (riid, mono_class_get_iunknown_class ())) { + *ppv = cominterop_get_ccw_checked (object, mono_class_get_iunknown_class (), &error); + mono_error_assert_ok (&error); + /* remember to addref on QI */ + cominterop_ccw_addref ((MonoCCWInterface *)*ppv); + return MONO_S_OK; + } + + /* handle IDispatch special */ + if (cominterop_class_guid_equal (riid, mono_class_get_idispatch_class ())) { + if (!cominterop_can_support_dispatch (klass)) + return MONO_E_NOINTERFACE; + + *ppv = cominterop_get_ccw_checked (object, mono_class_get_idispatch_class (), &error); + mono_error_assert_ok (&error); + /* remember to addref on QI */ + cominterop_ccw_addref ((MonoCCWInterface *)*ppv); + return MONO_S_OK; + } + +#ifdef HOST_WIN32 + /* handle IMarshal special */ + if (0 == memcmp (riid, &MONO_IID_IMarshal, sizeof (IID))) { + int res = cominterop_ccw_getfreethreadedmarshaler (ccw, object, ppv, &error); + mono_error_assert_ok (&error); + return res; + } +#endif + klass_iter = klass; + while (klass_iter && klass_iter != mono_defaults.object_class) { + ifaces = mono_class_get_implemented_interfaces (klass_iter, &error); + g_assert (mono_error_ok (&error)); + if (ifaces) { + for (i = 0; i < ifaces->len; ++i) { + MonoClass *ic = NULL; + ic = (MonoClass *)g_ptr_array_index (ifaces, i); + if (cominterop_class_guid_equal (riid, ic)) { + itf = ic; + break; + } + } + g_ptr_array_free (ifaces, TRUE); + } + + if (itf) + break; + + klass_iter = klass_iter->parent; + } + if (itf) { + *ppv = cominterop_get_ccw_checked (object, itf, &error); + if (!is_ok (&error)) { + mono_error_cleanup (&error); /* FIXME don't swallow the error */ + return MONO_E_NOINTERFACE; + } + /* remember to addref on QI */ + cominterop_ccw_addref ((MonoCCWInterface *)*ppv); + return MONO_S_OK; + } + + return MONO_E_NOINTERFACE; +} + +static int STDCALL +cominterop_ccw_get_type_info_count (MonoCCWInterface* ccwe, guint32 *pctinfo) +{ + if(!pctinfo) + return MONO_E_INVALIDARG; + + *pctinfo = 1; + + return MONO_S_OK; +} + +static int STDCALL +cominterop_ccw_get_type_info (MonoCCWInterface* ccwe, guint32 iTInfo, guint32 lcid, gpointer *ppTInfo) +{ + return MONO_E_NOTIMPL; +} + +static int STDCALL +cominterop_ccw_get_ids_of_names (MonoCCWInterface* ccwe, gpointer riid, + gunichar2** rgszNames, guint32 cNames, + guint32 lcid, gint32 *rgDispId) +{ + static MonoClass *ComDispIdAttribute = NULL; + MonoError error; + MonoCustomAttrInfo *cinfo = NULL; + int i,ret = MONO_S_OK; + MonoMethod* method; + gchar* methodname; + MonoClass *klass = NULL; + MonoCCW* ccw = ccwe->ccw; + MonoObject* object = mono_gchandle_get_target (ccw->gc_handle); + + /* Handle DispIdAttribute */ + if (!ComDispIdAttribute) + ComDispIdAttribute = mono_class_load_from_name (mono_defaults.corlib, "System.Runtime.InteropServices", "DispIdAttribute"); + + g_assert (object); + klass = mono_object_class (object); + + if (!mono_domain_get ()) + mono_thread_attach (mono_get_root_domain ()); + + for (i=0; i < cNames; i++) { + methodname = mono_unicode_to_external (rgszNames[i]); + + method = mono_class_get_method_from_name(klass, methodname, -1); + if (method) { + cinfo = mono_custom_attrs_from_method_checked (method, &error); + mono_error_assert_ok (&error); /* FIXME what's reasonable to do here */ + if (cinfo) { + MonoObject *result = mono_custom_attrs_get_attr_checked (cinfo, ComDispIdAttribute, &error); + g_assert (mono_error_ok (&error)); /*FIXME proper error handling*/; + + if (result) + rgDispId[i] = *(gint32*)mono_object_unbox (result); + else + rgDispId[i] = (gint32)method->token; + + if (!cinfo->cached) + mono_custom_attrs_free (cinfo); + } + else + rgDispId[i] = (gint32)method->token; + } else { + rgDispId[i] = MONO_E_DISPID_UNKNOWN; + ret = MONO_E_DISP_E_UNKNOWNNAME; + } + } + + return ret; +} + +static int STDCALL +cominterop_ccw_invoke (MonoCCWInterface* ccwe, guint32 dispIdMember, + gpointer riid, guint32 lcid, + guint16 wFlags, gpointer pDispParams, + gpointer pVarResult, gpointer pExcepInfo, + guint32 *puArgErr) +{ + return MONO_E_NOTIMPL; +} + +typedef gpointer (STDCALL *SysAllocStringLenFunc)(gunichar* str, guint32 len); +typedef guint32 (STDCALL *SysStringLenFunc)(gpointer bstr); +typedef void (STDCALL *SysFreeStringFunc)(gunichar* str); + +static SysAllocStringLenFunc sys_alloc_string_len_ms = NULL; +static SysStringLenFunc sys_string_len_ms = NULL; +static SysFreeStringFunc sys_free_string_ms = NULL; + +#ifndef HOST_WIN32 + +typedef struct tagSAFEARRAYBOUND { + ULONG cElements; + LONG lLbound; +}SAFEARRAYBOUND,*LPSAFEARRAYBOUND; +#define VT_VARIANT 12 + +#endif + +typedef guint32 (STDCALL *SafeArrayGetDimFunc)(gpointer psa); +typedef int (STDCALL *SafeArrayGetLBoundFunc)(gpointer psa, guint32 nDim, glong* plLbound); +typedef int (STDCALL *SafeArrayGetUBoundFunc)(gpointer psa, guint32 nDim, glong* plUbound); +typedef int (STDCALL *SafeArrayPtrOfIndexFunc)(gpointer psa, glong* rgIndices, gpointer* ppvData); +typedef int (STDCALL *SafeArrayDestroyFunc)(gpointer psa); +typedef int (STDCALL *SafeArrayPutElementFunc)(gpointer psa, glong* rgIndices, gpointer* ppvData); +typedef gpointer (STDCALL *SafeArrayCreateFunc)(int vt, guint32 cDims, SAFEARRAYBOUND* rgsabound); + +static SafeArrayGetDimFunc safe_array_get_dim_ms = NULL; +static SafeArrayGetLBoundFunc safe_array_get_lbound_ms = NULL; +static SafeArrayGetUBoundFunc safe_array_get_ubound_ms = NULL; +static SafeArrayPtrOfIndexFunc safe_array_ptr_of_index_ms = NULL; +static SafeArrayDestroyFunc safe_array_destroy_ms = NULL; +static SafeArrayPutElementFunc safe_array_put_element_ms = NULL; +static SafeArrayCreateFunc safe_array_create_ms = NULL; + +static gboolean +init_com_provider_ms (void) +{ + static gboolean initialized = FALSE; + char *error_msg; + MonoDl *module = NULL; + const char* scope = "liboleaut32.so"; + + if (initialized) + return TRUE; + + module = mono_dl_open(scope, MONO_DL_LAZY, &error_msg); + if (error_msg) { + g_warning ("Error loading COM support library '%s': %s", scope, error_msg); + g_assert_not_reached (); + return FALSE; + } + error_msg = mono_dl_symbol (module, "SysAllocStringLen", (gpointer*)&sys_alloc_string_len_ms); + if (error_msg) { + g_warning ("Error loading entry point '%s' in COM support library '%s': %s", "SysAllocStringLen", scope, error_msg); + g_assert_not_reached (); + return FALSE; + } + + error_msg = mono_dl_symbol (module, "SysStringLen", (gpointer*)&sys_string_len_ms); + if (error_msg) { + g_warning ("Error loading entry point '%s' in COM support library '%s': %s", "SysStringLen", scope, error_msg); + g_assert_not_reached (); + return FALSE; + } + + error_msg = mono_dl_symbol (module, "SysFreeString", (gpointer*)&sys_free_string_ms); + if (error_msg) { + g_warning ("Error loading entry point '%s' in COM support library '%s': %s", "SysFreeString", scope, error_msg); + g_assert_not_reached (); + return FALSE; + } + + error_msg = mono_dl_symbol (module, "SafeArrayGetDim", (gpointer*)&safe_array_get_dim_ms); + if (error_msg) { + g_warning ("Error loading entry point '%s' in COM support library '%s': %s", "SafeArrayGetDim", scope, error_msg); + g_assert_not_reached (); + return FALSE; + } + + error_msg = mono_dl_symbol (module, "SafeArrayGetLBound", (gpointer*)&safe_array_get_lbound_ms); + if (error_msg) { + g_warning ("Error loading entry point '%s' in COM support library '%s': %s", "SafeArrayGetLBound", scope, error_msg); + g_assert_not_reached (); + return FALSE; + } + + error_msg = mono_dl_symbol (module, "SafeArrayGetUBound", (gpointer*)&safe_array_get_ubound_ms); + if (error_msg) { + g_warning ("Error loading entry point '%s' in COM support library '%s': %s", "SafeArrayGetUBound", scope, error_msg); + g_assert_not_reached (); + return FALSE; + } + + error_msg = mono_dl_symbol (module, "SafeArrayPtrOfIndex", (gpointer*)&safe_array_ptr_of_index_ms); + if (error_msg) { + g_warning ("Error loading entry point '%s' in COM support library '%s': %s", "SafeArrayPtrOfIndex", scope, error_msg); + g_assert_not_reached (); + return FALSE; + } + + error_msg = mono_dl_symbol (module, "SafeArrayDestroy", (gpointer*)&safe_array_destroy_ms); + if (error_msg) { + g_warning ("Error loading entry point '%s' in COM support library '%s': %s", "SafeArrayDestroy", scope, error_msg); + g_assert_not_reached (); + return FALSE; + } + + error_msg = mono_dl_symbol (module, "SafeArrayPutElement", (gpointer*)&safe_array_put_element_ms); + if (error_msg) { + g_warning ("Error loading entry point '%s' in COM support library '%s': %s", "SafeArrayPutElement", scope, error_msg); + g_assert_not_reached (); + return FALSE; + } + + error_msg = mono_dl_symbol (module, "SafeArrayCreate", (gpointer*)&safe_array_create_ms); + if (error_msg) { + g_warning ("Error loading entry point '%s' in COM support library '%s': %s", "SafeArrayCreate", scope, error_msg); + g_assert_not_reached (); + return FALSE; + } + + initialized = TRUE; + return TRUE; +} + +gpointer +mono_ptr_to_bstr(gpointer ptr, int slen) +{ + if (!ptr) + return NULL; +#ifdef HOST_WIN32 + return SysAllocStringLen (ptr, slen); +#else + if (com_provider == MONO_COM_DEFAULT) { + /* allocate len + 1 utf16 characters plus 4 byte integer for length*/ + char *ret = (char *)g_malloc((slen + 1) * sizeof(gunichar2) + sizeof(guint32)); + if (ret == NULL) + return NULL; + memcpy(ret + sizeof(guint32), ptr, slen * sizeof(gunichar2)); + *((guint32 *)ret) = slen * sizeof(gunichar2); + ret[4 + slen * sizeof(gunichar2)] = 0; + ret[5 + slen * sizeof(gunichar2)] = 0; + + return ret + 4; + } + else if (com_provider == MONO_COM_MS && init_com_provider_ms()) { + gpointer ret = NULL; + gunichar* str = NULL; + guint32 len = slen; + str = g_utf16_to_ucs4(ptr, len, + NULL, NULL, NULL); + ret = sys_alloc_string_len_ms(str, len); + g_free(str); + return ret; + } + else { + g_assert_not_reached(); + } +#endif +} + +MonoString * +mono_string_from_bstr (gpointer bstr) +{ + MonoError error; + MonoString *result = mono_string_from_bstr_checked (bstr, &error); + mono_error_cleanup (&error); + return result; +} + +MonoString * +mono_string_from_bstr_icall (gpointer bstr) +{ + MonoError error; + MonoString *result = mono_string_from_bstr_checked (bstr, &error); + mono_error_set_pending_exception (&error); + return result; +} + +MonoString * +mono_string_from_bstr_checked (gpointer bstr, MonoError *error) +{ + MonoString * res = NULL; + + error_init (error); + + if (!bstr) + return NULL; +#ifdef HOST_WIN32 + res = mono_string_new_utf16_checked (mono_domain_get (), bstr, SysStringLen (bstr), error); +#else + if (com_provider == MONO_COM_DEFAULT) { + res = mono_string_new_utf16_checked (mono_domain_get (), (const mono_unichar2 *)bstr, *((guint32 *)bstr - 1) / sizeof(gunichar2), error); + } else if (com_provider == MONO_COM_MS && init_com_provider_ms ()) { + MonoString* str = NULL; + glong written = 0; + gunichar2* utf16 = NULL; + + utf16 = g_ucs4_to_utf16 ((const gunichar *)bstr, sys_string_len_ms (bstr), NULL, &written, NULL); + str = mono_string_new_utf16_checked (mono_domain_get (), utf16, written, error); + g_free (utf16); + res = str; + } else { + g_assert_not_reached (); + } + +#endif + return res; +} + +void +mono_free_bstr (gpointer bstr) +{ + if (!bstr) + return; +#ifdef HOST_WIN32 + SysFreeString ((BSTR)bstr); +#else + if (com_provider == MONO_COM_DEFAULT) { + g_free (((char *)bstr) - 4); + } else if (com_provider == MONO_COM_MS && init_com_provider_ms ()) { + sys_free_string_ms ((gunichar *)bstr); + } else { + g_assert_not_reached (); + } + +#endif +} + + +/* SAFEARRAY marshalling */ +int +mono_cominterop_emit_marshal_safearray (EmitMarshalContext *m, int argnum, MonoType *t, + MonoMarshalSpec *spec, + int conv_arg, MonoType **conv_arg_type, + MarshalAction action) +{ + MonoMethodBuilder *mb = m->mb; + +#ifndef DISABLE_JIT + switch (action) { + case MARSHAL_ACTION_CONV_IN: { + if (t->attrs & PARAM_ATTRIBUTE_IN) { + + /* Generates IL code for the following algorithm: + + SafeArray safearray; // safearray_var + IntPtr indices; // indices_var + int empty; // empty_var + if (mono_marshal_safearray_create (array, out safearray, out indices, out empty)) { + if (!empty) { + int index=0; // index_var + do { // label3 + variant elem = Marshal.GetNativeVariantForObject (array.GetValueImpl(index)); + mono_marshal_safearray_set_value (safearray, indices, elem); + ++index; + } + while (mono_marshal_safearray_next (safearray, indices)); + } // label2 + mono_marshal_safearray_free_indices (indices); + } // label1 + */ + + int safearray_var, indices_var, empty_var, elem_var, index_var; + guint32 label1 = 0, label2 = 0, label3 = 0; + static MonoMethod *get_native_variant_for_object = NULL; + static MonoMethod *get_value_impl = NULL; + static MonoMethod *variant_clear = NULL; + + conv_arg = safearray_var = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + indices_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + empty_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + + if (t->byref) { + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + } else + mono_mb_emit_ldarg (mb, argnum); + + mono_mb_emit_ldloc_addr (mb, safearray_var); + mono_mb_emit_ldloc_addr (mb, indices_var); + mono_mb_emit_ldloc_addr (mb, empty_var); + mono_mb_emit_icall (mb, mono_marshal_safearray_create); + + label1 = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S); + + mono_mb_emit_ldloc (mb, empty_var); + + label2 = mono_mb_emit_short_branch (mb, CEE_BRTRUE_S); + + index_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg); + mono_mb_emit_byte (mb, CEE_LDC_I4_0); + mono_mb_emit_stloc (mb, index_var); + + label3 = mono_mb_get_label (mb); + + if (!get_value_impl) + get_value_impl = mono_class_get_method_from_name (mono_defaults.array_class, "GetValueImpl", 1); + g_assert (get_value_impl); + + if (t->byref) { + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + } else + mono_mb_emit_ldarg (mb, argnum); + + mono_mb_emit_ldloc (mb, index_var); + + mono_mb_emit_managed_call (mb, get_value_impl, NULL); + + if (!get_native_variant_for_object) + get_native_variant_for_object = mono_class_get_method_from_name (mono_defaults.marshal_class, "GetNativeVariantForObject", 2); + g_assert (get_native_variant_for_object); + + elem_var = mono_mb_add_local (mb, &mono_class_get_variant_class ()->byval_arg); + mono_mb_emit_ldloc_addr (mb, elem_var); + + mono_mb_emit_managed_call (mb, get_native_variant_for_object, NULL); + + mono_mb_emit_ldloc (mb, safearray_var); + mono_mb_emit_ldloc (mb, indices_var); + mono_mb_emit_ldloc_addr (mb, elem_var); + mono_mb_emit_icall (mb, mono_marshal_safearray_set_value); + + if (!variant_clear) + variant_clear = mono_class_get_method_from_name (mono_class_get_variant_class (), "Clear", 0); + + mono_mb_emit_ldloc_addr (mb, elem_var); + mono_mb_emit_managed_call (mb, variant_clear, NULL); + + mono_mb_emit_add_to_local (mb, index_var, 1); + + mono_mb_emit_ldloc (mb, safearray_var); + mono_mb_emit_ldloc (mb, indices_var); + mono_mb_emit_icall (mb, mono_marshal_safearray_next); + mono_mb_emit_branch_label (mb, CEE_BRTRUE, label3); + + mono_mb_patch_short_branch (mb, label2); + + mono_mb_emit_ldloc (mb, indices_var); + mono_mb_emit_icall (mb, mono_marshal_safearray_free_indices); + + mono_mb_patch_short_branch (mb, label1); + } + break; + } + + case MARSHAL_ACTION_PUSH: + if (t->byref) + mono_mb_emit_ldloc_addr (mb, conv_arg); + else + mono_mb_emit_ldloc (mb, conv_arg); + break; + + case MARSHAL_ACTION_CONV_OUT: { + if (t->attrs & PARAM_ATTRIBUTE_OUT) { + /* Generates IL code for the following algorithm: + + Array result; // result_var + IntPtr indices; // indices_var + int empty; // empty_var + bool byValue = !t->byref && (t->attrs & PARAM_ATTRIBUTE_IN); + if (mono_marshal_safearray_begin(safearray, out result, out indices, out empty, parameter, byValue)) { + if (!empty) { + int index=0; // index_var + do { // label3 + if (!byValue || (index < parameter.Length)) { + object elem = Variant.GetObjectForNativeVariant(mono_marshal_safearray_get_value(safearray, indices)); + result.SetValueImpl(elem, index); + } + ++index; + } + while (mono_marshal_safearray_next(safearray, indices)); + } // label2 + mono_marshal_safearray_end(safearray, indices); + } // label1 + if (!byValue) + return result; + */ + + int result_var, indices_var, empty_var, elem_var, index_var; + guint32 label1 = 0, label2 = 0, label3 = 0, label4 = 0; + static MonoMethod *get_object_for_native_variant = NULL; + static MonoMethod *set_value_impl = NULL; + gboolean byValue = !t->byref && (t->attrs & PARAM_ATTRIBUTE_IN); + + result_var = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + indices_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + empty_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_ldloc_addr (mb, result_var); + mono_mb_emit_ldloc_addr (mb, indices_var); + mono_mb_emit_ldloc_addr (mb, empty_var); + mono_mb_emit_ldarg (mb, argnum); + if (byValue) + mono_mb_emit_byte (mb, CEE_LDC_I4_0); + else + mono_mb_emit_byte (mb, CEE_LDC_I4_1); + mono_mb_emit_icall (mb, mono_marshal_safearray_begin); + + label1 = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S); + + mono_mb_emit_ldloc (mb, empty_var); + + label2 = mono_mb_emit_short_branch (mb, CEE_BRTRUE_S); + + index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + mono_mb_emit_byte (mb, CEE_LDC_I4_0); + mono_mb_emit_stloc (mb, index_var); + + label3 = mono_mb_get_label (mb); + + if (byValue) { + mono_mb_emit_ldloc (mb, index_var); + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_byte (mb, CEE_LDLEN); + label4 = mono_mb_emit_branch (mb, CEE_BGE); + } + + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_ldloc (mb, indices_var); + mono_mb_emit_icall (mb, mono_marshal_safearray_get_value); + + if (!get_object_for_native_variant) + get_object_for_native_variant = mono_class_get_method_from_name (mono_defaults.marshal_class, "GetObjectForNativeVariant", 1); + g_assert (get_object_for_native_variant); + + if (!set_value_impl) + set_value_impl = mono_class_get_method_from_name (mono_defaults.array_class, "SetValueImpl", 2); + g_assert (set_value_impl); + + elem_var = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + + mono_mb_emit_managed_call (mb, get_object_for_native_variant, NULL); + mono_mb_emit_stloc (mb, elem_var); + + mono_mb_emit_ldloc (mb, result_var); + mono_mb_emit_ldloc (mb, elem_var); + mono_mb_emit_ldloc (mb, index_var); + mono_mb_emit_managed_call (mb, set_value_impl, NULL); + + if (byValue) + mono_mb_patch_short_branch (mb, label4); + + mono_mb_emit_add_to_local (mb, index_var, 1); + + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_ldloc (mb, indices_var); + mono_mb_emit_icall (mb, mono_marshal_safearray_next); + mono_mb_emit_branch_label (mb, CEE_BRTRUE, label3); + + mono_mb_patch_short_branch (mb, label2); + + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_ldloc (mb, indices_var); + mono_mb_emit_icall (mb, mono_marshal_safearray_end); + + mono_mb_patch_short_branch (mb, label1); + + if (!byValue) { + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_ldloc (mb, result_var); + mono_mb_emit_byte (mb, CEE_STIND_REF); + } + } + break; + } + + default: + g_assert_not_reached (); + } +#endif /* DISABLE_JIT */ + + return conv_arg; +} + +#ifdef HOST_WIN32 +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) +static inline guint32 +mono_marshal_win_safearray_get_dim (gpointer safearray) +{ + return SafeArrayGetDim (safearray); +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */ + +static guint32 +mono_marshal_safearray_get_dim (gpointer safearray) +{ + return mono_marshal_win_safearray_get_dim (safearray); +} + +#else /* HOST_WIN32 */ + +static guint32 +mono_marshal_safearray_get_dim (gpointer safearray) +{ + guint32 result=0; + if (com_provider == MONO_COM_MS && init_com_provider_ms ()) { + result = safe_array_get_dim_ms (safearray); + } else { + g_assert_not_reached (); + } + return result; +} +#endif /* HOST_WIN32 */ + +#ifdef HOST_WIN32 +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) +static inline int +mono_marshal_win_safe_array_get_lbound (gpointer psa, guint nDim, glong* plLbound) +{ + return SafeArrayGetLBound (psa, nDim, plLbound); +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */ + +static int +mono_marshal_safe_array_get_lbound (gpointer psa, guint nDim, glong* plLbound) +{ + return mono_marshal_win_safe_array_get_lbound (psa, nDim, plLbound); +} + +#else /* HOST_WIN32 */ + +static int +mono_marshal_safe_array_get_lbound (gpointer psa, guint nDim, glong* plLbound) +{ + int result=MONO_S_OK; + if (com_provider == MONO_COM_MS && init_com_provider_ms ()) { + result = safe_array_get_lbound_ms (psa, nDim, plLbound); + } else { + g_assert_not_reached (); + } + return result; +} +#endif /* HOST_WIN32 */ + +#ifdef HOST_WIN32 +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) +inline static int +mono_marshal_win_safe_array_get_ubound (gpointer psa, guint nDim, glong* plUbound) +{ + return SafeArrayGetUBound (psa, nDim, plUbound); +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */ + +static int +mono_marshal_safe_array_get_ubound (gpointer psa, guint nDim, glong* plUbound) +{ + return mono_marshal_win_safe_array_get_ubound (psa, nDim, plUbound); +} + +#else /* HOST_WIN32 */ + +static int +mono_marshal_safe_array_get_ubound (gpointer psa, guint nDim, glong* plUbound) +{ + int result=MONO_S_OK; + if (com_provider == MONO_COM_MS && init_com_provider_ms ()) { + result = safe_array_get_ubound_ms (psa, nDim, plUbound); + } else { + g_assert_not_reached (); + } + return result; +} +#endif /* HOST_WIN32 */ + +/* This is an icall */ +static gboolean +mono_marshal_safearray_begin (gpointer safearray, MonoArray **result, gpointer *indices, gpointer empty, gpointer parameter, gboolean allocateNewArray) +{ + MonoError error; + int dim; + uintptr_t *sizes; + intptr_t *bounds; + MonoClass *aklass; + int i; + gboolean bounded = FALSE; + +#ifndef HOST_WIN32 + // If not on windows, check that the MS provider is used as it is + // required for SAFEARRAY support. + // If SAFEARRAYs are not supported, returning FALSE from this + // function will prevent the other mono_marshal_safearray_xxx functions + // from being called. + if ((com_provider != MONO_COM_MS) || !init_com_provider_ms ()) { + return FALSE; + } +#endif + + (*(int*)empty) = TRUE; + + if (safearray != NULL) { + + dim = mono_marshal_safearray_get_dim (safearray); + + if (dim > 0) { + + *indices = g_malloc (dim * sizeof(int)); + + sizes = (uintptr_t *)alloca (dim * sizeof(uintptr_t)); + bounds = (intptr_t *)alloca (dim * sizeof(intptr_t)); + + for (i=0; i=0; --i) + { + glong lbound, ubound; + + hr = mono_marshal_safe_array_get_ubound (safearray, i+1, &ubound); + if (hr < 0) { + cominterop_set_hr_error (&error, hr); + mono_error_set_pending_exception (&error); + return FALSE; + } + + if (++pIndices[i] <= ubound) { + break; + } + + hr = mono_marshal_safe_array_get_lbound (safearray, i+1, &lbound); + if (hr < 0) { + cominterop_set_hr_error (&error, hr); + mono_error_set_pending_exception (&error); + return FALSE; + } + + pIndices[i] = lbound; + + if (i == 0) + ret = FALSE; + } + return ret; +} + +#ifdef HOST_WIN32 +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) +static inline void +mono_marshal_win_safearray_end (gpointer safearray, gpointer indices) +{ + g_free(indices); + SafeArrayDestroy (safearray); +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */ + +static void +mono_marshal_safearray_end (gpointer safearray, gpointer indices) +{ + mono_marshal_win_safearray_end (safearray, indices); +} + +#else /* HOST_WIN32 */ + +static void +mono_marshal_safearray_end (gpointer safearray, gpointer indices) +{ + g_free(indices); + if (com_provider == MONO_COM_MS && init_com_provider_ms ()) { + safe_array_destroy_ms (safearray); + } else { + g_assert_not_reached (); + } +} +#endif /* HOST_WIN32 */ + +#ifdef HOST_WIN32 +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) +static inline gboolean +mono_marshal_win_safearray_create_internal (UINT cDims, SAFEARRAYBOUND *rgsabound, gpointer *newsafearray) +{ + *newsafearray = SafeArrayCreate (VT_VARIANT, cDims, rgsabound); + return TRUE; +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */ + +static gboolean +mono_marshal_safearray_create_internal (UINT cDims, SAFEARRAYBOUND *rgsabound, gpointer *newsafearray) +{ + return mono_marshal_win_safearray_create_internal (cDims, rgsabound, newsafearray); +} + +#else /* HOST_WIN32 */ + +static inline gboolean +mono_marshal_safearray_create_internal (UINT cDims, SAFEARRAYBOUND *rgsabound, gpointer *newsafearray) +{ + *newsafearray = safe_array_create_ms (VT_VARIANT, cDims, rgsabound); + return TRUE; +} + +#endif /* HOST_WIN32 */ + +static gboolean +mono_marshal_safearray_create (MonoArray *input, gpointer *newsafearray, gpointer *indices, gpointer empty) +{ + int dim; + SAFEARRAYBOUND *bounds; + int i; + int max_array_length; + +#ifndef HOST_WIN32 + // If not on windows, check that the MS provider is used as it is + // required for SAFEARRAY support. + // If SAFEARRAYs are not supported, returning FALSE from this + // function will prevent the other mono_marshal_safearray_xxx functions + // from being called. + if ((com_provider != MONO_COM_MS) || !init_com_provider_ms ()) { + return FALSE; + } +#endif + + max_array_length = mono_array_length (input); + dim = ((MonoObject *)input)->vtable->klass->rank; + + *indices = g_malloc (dim * sizeof (int)); + bounds = (SAFEARRAYBOUND *)alloca (dim * sizeof (SAFEARRAYBOUND)); + (*(int*)empty) = (max_array_length == 0); + + if (dim > 1) { + for (i=0; ibounds [i].lower_bound; + bounds [i].cElements = input->bounds [i].length; + } + } else { + ((int*)*indices) [0] = 0; + bounds [0].cElements = max_array_length; + bounds [0].lLbound = 0; + } + + return mono_marshal_safearray_create_internal (dim, bounds, newsafearray); +} + +/* This is an icall */ +#ifdef HOST_WIN32 +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) +static inline int +mono_marshal_win_safearray_set_value (gpointer safearray, gpointer indices, gpointer value) +{ + return SafeArrayPutElement (safearray, indices, value); +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */ + +static void +mono_marshal_safearray_set_value (gpointer safearray, gpointer indices, gpointer value) +{ + MonoError error; + int hr = mono_marshal_win_safearray_set_value (safearray, indices, value); + if (hr < 0) { + cominterop_set_hr_error (&error, hr); + mono_error_set_pending_exception (&error); + return; + } +} + +#else /* HOST_WIN32 */ + +static void +mono_marshal_safearray_set_value (gpointer safearray, gpointer indices, gpointer value) +{ + MonoError error; + if (com_provider == MONO_COM_MS && init_com_provider_ms ()) { + int hr = safe_array_put_element_ms (safearray, (glong *)indices, (void **)value); + if (hr < 0) { + cominterop_set_hr_error (&error, hr); + mono_error_set_pending_exception (&error); + return; + } + } else + g_assert_not_reached (); +} +#endif /* HOST_WIN32 */ + +static +void mono_marshal_safearray_free_indices (gpointer indices) +{ + g_free (indices); +} + +#else /* DISABLE_COM */ + +void +mono_cominterop_init (void) +{ + /*FIXME + + This icalls are used by the marshal code when doing PtrToStructure and StructureToPtr and pinvoke. + + If we leave them out and the FullAOT compiler finds the need to emit one of the above 3 wrappers it will + g_assert. + + The proper fix would be to emit warning, remove them from marshal.c when DISABLE_COM is used and + emit an exception in the generated IL. + */ + register_icall (mono_string_to_bstr, "mono_string_to_bstr", "ptr obj", FALSE); + register_icall (mono_string_from_bstr_icall, "mono_string_from_bstr_icall", "obj ptr", FALSE); + register_icall (mono_free_bstr, "mono_free_bstr", "void ptr", FALSE); +} + +void +mono_cominterop_cleanup (void) +{ +} + +void +cominterop_release_all_rcws (void) +{ +} + +gpointer +mono_ptr_to_bstr (gpointer ptr, int slen) +{ + if (!ptr) + return NULL; +#ifdef HOST_WIN32 + return SysAllocStringLen (ptr, slen); +#else + { + /* allocate len + 1 utf16 characters plus 4 byte integer for length*/ + char *ret = g_malloc ((slen + 1) * sizeof(gunichar2) + sizeof(guint32)); + if (ret == NULL) + return NULL; + memcpy (ret + sizeof(guint32), ptr, slen * sizeof(gunichar2)); + * ((guint32 *) ret) = slen * sizeof(gunichar2); + ret [4 + slen * sizeof(gunichar2)] = 0; + ret [5 + slen * sizeof(gunichar2)] = 0; + + return ret + 4; + } +#endif +} + + +MonoString * +mono_string_from_bstr (gpointer bstr) +{ + MonoError error; + MonoString *result = mono_string_from_bstr_checked (bstr, &error); + mono_error_cleanup (&error); + return result; +} + +MonoString * +mono_string_from_bstr_icall (gpointer bstr) +{ + MonoError error; + MonoString *result = mono_string_from_bstr_checked (bstr, &error); + mono_error_set_pending_exception (&error); + return result; +} + +MonoString * +mono_string_from_bstr_checked (gpointer bstr, MonoError *error) +{ + MonoString *res = NULL; + error_init (error); + if (!bstr) + return NULL; +#ifdef HOST_WIN32 + res = mono_string_new_utf16_checked (mono_domain_get (), bstr, SysStringLen (bstr), error); +#else + res = mono_string_new_utf16_checked (mono_domain_get (), bstr, *((guint32 *)bstr - 1) / sizeof(gunichar2), error); +#endif + return res; +} + +void +mono_free_bstr (gpointer bstr) +{ + if (!bstr) + return; +#ifdef HOST_WIN32 + SysFreeString ((BSTR)bstr); +#else + g_free (((char *)bstr) - 4); +#endif +} + +gboolean +mono_marshal_free_ccw (MonoObject* object) +{ + return FALSE; +} + +int +ves_icall_System_Runtime_InteropServices_Marshal_AddRefInternal (gpointer pUnk) +{ + g_assert_not_reached (); + return 0; +} + +int +ves_icall_System_Runtime_InteropServices_Marshal_ReleaseInternal (gpointer pUnk) +{ + g_assert_not_reached (); + return 0; +} + +int +ves_icall_System_Runtime_InteropServices_Marshal_QueryInterfaceInternal (gpointer pUnk, gpointer riid, gpointer* ppv) +{ + g_assert_not_reached (); + return 0; +} + +#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) +{ + MonoError error; + MonoString *result = mono_string_from_bstr_checked (ptr, &error); + mono_error_set_pending_exception (&error); + return result; +} + +gpointer +ves_icall_System_Runtime_InteropServices_Marshal_StringToBSTR (MonoString* ptr) +{ + return mono_string_to_bstr(ptr); +} + +gpointer +ves_icall_System_Runtime_InteropServices_Marshal_BufferToBSTR (MonoArray* ptr, int len) +{ + return mono_ptr_to_bstr (ptr->vector, len); +} + +void +ves_icall_System_Runtime_InteropServices_Marshal_FreeBSTR (gpointer ptr) +{ + mono_free_bstr (ptr); +} + +void* +mono_cominterop_get_com_interface (MonoObject *object, MonoClass *ic, MonoError *error) +{ + error_init (error); + +#ifndef DISABLE_COM + if (!object) + return NULL; + + if (cominterop_object_is_rcw (object)) { + MonoClass *klass = NULL; + MonoRealProxy* real_proxy = NULL; + if (!object) + return NULL; + klass = mono_object_class (object); + if (!mono_class_is_transparent_proxy (klass)) { + mono_error_set_invalid_operation (error, "Class is not transparent"); + return NULL; + } + + real_proxy = ((MonoTransparentProxy*)object)->rp; + if (!real_proxy) { + mono_error_set_invalid_operation (error, "RealProxy is null"); + return NULL; + } + + klass = mono_object_class (real_proxy); + if (klass != mono_class_get_interop_proxy_class ()) { + mono_error_set_invalid_operation (error, "Object is not a proxy"); + return NULL; + } + + if (!((MonoComInteropProxy*)real_proxy)->com_object) { + mono_error_set_invalid_operation (error, "Proxy points to null COM object"); + return NULL; + } + + void* com_itf = cominterop_get_interface_checked (((MonoComInteropProxy*)real_proxy)->com_object, ic, error); + return com_itf; + } + else { + void* ccw_entry = cominterop_get_ccw_checked (object, ic, error); + return ccw_entry; + } +#else + g_assert_not_reached (); +#endif +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/cominterop.h b/unity-2019.4.24f1-mbe/mono/metadata/cominterop.h new file mode 100644 index 000000000..6f93ec191 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/cominterop.h @@ -0,0 +1,76 @@ +/** + * \file + * COM Interop Support + * + * + * (C) 2002 Ximian, Inc. http://www.ximian.com + * + */ + +#ifndef __MONO_COMINTEROP_H__ +#define __MONO_COMINTEROP_H__ + +#include +#include + +void +mono_cominterop_init (void); + +void +mono_cominterop_cleanup (void); + +void +mono_mb_emit_cominterop_get_function_pointer (MonoMethodBuilder *mb, MonoMethod* method); + +void +mono_mb_emit_cominterop_call_function_pointer (MonoMethodBuilder *mb, MonoMethodSignature *sig); + +void +mono_mb_emit_cominterop_call (MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethod* method); + +void +mono_cominterop_emit_ptr_to_object_conv (MonoMethodBuilder *mb, MonoType *type, MonoMarshalConv conv, MonoMarshalSpec *mspec); + +void +mono_cominterop_emit_object_to_ptr_conv (MonoMethodBuilder *mb, MonoType *type, MonoMarshalConv conv, MonoMarshalSpec *mspec); + +MonoMethod * +mono_cominterop_get_native_wrapper (MonoMethod *method); + +MonoMethod * +mono_cominterop_get_invoke (MonoMethod *method); + +int +mono_cominterop_emit_marshal_com_interface (EmitMarshalContext *m, int argnum, + MonoType *t, + MonoMarshalSpec *spec, + int conv_arg, MonoType **conv_arg_type, + MarshalAction action); + +int +mono_cominterop_emit_marshal_safearray (EmitMarshalContext *m, int argnum, + MonoType *t, + MonoMarshalSpec *spec, + int conv_arg, MonoType **conv_arg_type, + MarshalAction action); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoString * +mono_string_from_bstr (gpointer bstr); + +MonoString * +mono_string_from_bstr_icall (gpointer bstr); + +MonoString * +mono_string_from_bstr_checked (gpointer bstr, MonoError *error); + +MONO_API void +mono_free_bstr (gpointer bstr); + +MonoClass* +mono_class_try_get_com_object_class (void); + +void* +mono_cominterop_get_com_interface (MonoObject* object, MonoClass* ic, MonoError *error); + +#endif /* __MONO_COMINTEROP_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/console-io.h b/unity-2019.4.24f1-mbe/mono/metadata/console-io.h new file mode 100644 index 000000000..c5c572e58 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/console-io.h @@ -0,0 +1,35 @@ +/** + * \file + * Console IO internal calls + * + * Author: + * Gonzalo Paniagua Javier (gonzalo@ximian.com) + * + * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#ifndef _MONO_METADATA_CONSOLEIO_H +#define _MONO_METADATA_CONSOLEIO_H + +#include +#include + +#include +#include + +G_BEGIN_DECLS + +void mono_console_init (void); +void mono_console_handle_async_ops (void); +MonoBoolean ves_icall_System_ConsoleDriver_Isatty (gpointer handle); +gint32 ves_icall_System_ConsoleDriver_InternalKeyAvailable (gint32 timeout); +MonoBoolean ves_icall_System_ConsoleDriver_SetEcho (MonoBoolean echo); +MonoBoolean ves_icall_System_ConsoleDriver_SetBreak (MonoBoolean want_break); +MonoBoolean ves_icall_System_ConsoleDriver_TtySetup (MonoString *keypad, MonoString *teardown, MonoArray **control_characters, int **size); +void ves_icall_System_ConsoleDriver_Suspend (void); + +G_END_DECLS + +#endif /* _MONO_METADATA_CONSOLEIO_H */ + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/console-null.c b/unity-2019.4.24f1-mbe/mono/metadata/console-null.c new file mode 100644 index 000000000..749a43452 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/console-null.c @@ -0,0 +1,61 @@ +/** + * \file + * Null driver, does nothing. + * + * Author: + * Gonzalo Paniagua Javier (gonzalo@ximian.com) + * + * Copyright (C) 2005-2009 Novell, Inc. (http://www.novell.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +void +mono_console_init (void) +{ +} + +void +mono_console_handle_async_ops (void) +{ +} + +MonoBoolean +ves_icall_System_ConsoleDriver_Isatty (HANDLE handle) +{ + return mono_w32file_get_type (handle) == FILE_TYPE_CHAR; +} + +MonoBoolean +ves_icall_System_ConsoleDriver_SetEcho (MonoBoolean want_echo) +{ + return FALSE; +} + +MonoBoolean +ves_icall_System_ConsoleDriver_SetBreak (MonoBoolean want_break) +{ + return FALSE; +} + +gint32 +ves_icall_System_ConsoleDriver_InternalKeyAvailable (gint32 timeout) +{ + return FALSE; +} + +MonoBoolean +ves_icall_System_ConsoleDriver_TtySetup (MonoString *keypad, MonoString *teardown, MonoArray **control_chars, int **size) +{ + return FALSE; +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/console-unity.c b/unity-2019.4.24f1-mbe/mono/metadata/console-unity.c new file mode 100644 index 000000000..97376f4c9 --- /dev/null +++ b/unity-2019.4.24f1-mbe/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/unity-2019.4.24f1-mbe/mono/metadata/console-unix.c b/unity-2019.4.24f1-mbe/mono/metadata/console-unix.c new file mode 100644 index 000000000..fe71923db --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/console-unix.c @@ -0,0 +1,523 @@ +/** + * \file + * ConsoleDriver internal calls for Unix systems. + * + * Author: + * Gonzalo Paniagua Javier (gonzalo@ximian.com) + * + * Copyright (C) 2005-2009 Novell, Inc. (http://www.novell.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_SELECT_H +# include +#endif +#ifdef HAVE_SYS_TIME_H +# include +#endif +#include +#ifdef HAVE_UNISTD_H +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* On solaris, curses.h must come before both termios.h and term.h */ +#ifdef HAVE_CURSES_H +# include +#endif +#ifdef HAVE_TERMIOS_H +# include +#endif +#ifdef HAVE_TERM_H +# include +#endif + +/* Needed for FIONREAD under solaris */ +#ifdef HAVE_SYS_FILIO_H +# include +#endif +#ifdef HAVE_SYS_IOCTL_H +# include +#endif + +#include +#include + +static gboolean setup_finished; +static gboolean atexit_called; + +/* The string used to return the terminal to its previous state */ +static gchar *teardown_str; + +/* The string used to set the terminal into keypad xmit mode after SIGCONT is received */ +static gchar *keypad_xmit_str; + +#ifdef HAVE_TERMIOS_H +/* This is the last state used by Mono, used after a CONT signal is received */ +static struct termios mono_attr; +#endif + +/* static void console_restore_signal_handlers (void); */ +static void console_set_signal_handlers (void); + +void +mono_console_init (void) +{ + int fd; + + /* Make sure the standard file descriptors are opened */ + fd = open ("/dev/null", O_RDWR); + while (fd >= 0 && fd < 3) { + fd = open ("/dev/null", O_RDWR); + } + close (fd); +} + +static struct termios initial_attr; + +MonoBoolean +ves_icall_System_ConsoleDriver_Isatty (HANDLE handle) +{ + return isatty (GPOINTER_TO_INT (handle)); +} + +static MonoBoolean +set_property (gint property, gboolean value) +{ + struct termios attr; + gboolean callset = FALSE; + gboolean check; + + if (tcgetattr (STDIN_FILENO, &attr) == -1) + return FALSE; + + check = (attr.c_lflag & property) != 0; + if ((value || check) && !(value && check)) { + callset = TRUE; + if (value) + attr.c_lflag |= property; + else + attr.c_lflag &= ~property; + } + + if (!callset) + return TRUE; + + if (tcsetattr (STDIN_FILENO, TCSANOW, &attr) == -1) + return FALSE; + + mono_attr = attr; + return TRUE; +} + +MonoBoolean +ves_icall_System_ConsoleDriver_SetEcho (MonoBoolean want_echo) +{ + + return set_property (ECHO, want_echo); +} + +MonoBoolean +ves_icall_System_ConsoleDriver_SetBreak (MonoBoolean want_break) +{ + return set_property (IGNBRK, !want_break); +} + +gint32 +ves_icall_System_ConsoleDriver_InternalKeyAvailable (gint32 timeout) +{ + fd_set rfds; + struct timeval tv; + struct timeval *tvptr; + div_t divvy; + int ret, nbytes; + + do { + FD_ZERO (&rfds); + FD_SET (STDIN_FILENO, &rfds); + if (timeout >= 0) { + divvy = div (timeout, 1000); + tv.tv_sec = divvy.quot; + tv.tv_usec = divvy.rem; + tvptr = &tv; + } else { + tvptr = NULL; + } + ret = select (STDIN_FILENO + 1, &rfds, NULL, NULL, tvptr); + } while (ret == -1 && errno == EINTR); + + if (ret > 0) { + nbytes = 0; + ret = ioctl (STDIN_FILENO, FIONREAD, &nbytes); + if (ret >= 0) + ret = nbytes; + } + + return (ret > 0) ? ret : 0; +} + +static gint32 cols_and_lines; + +#ifdef TIOCGWINSZ +static int +terminal_get_dimensions (void) +{ + struct winsize ws; + int ret; + int save_errno = errno; + + if (ioctl (STDIN_FILENO, TIOCGWINSZ, &ws) == 0){ + ret = (ws.ws_col << 16) | ws.ws_row; + errno = save_errno; + return ret; + } + return -1; +} +#else +static int +terminal_get_dimensions (void) +{ + return -1; +} +#endif + +static void +tty_teardown (void) +{ + int unused G_GNUC_UNUSED; + + if (!setup_finished) + return; + + if (teardown_str != NULL) { + unused = write (STDOUT_FILENO, teardown_str, strlen (teardown_str)); + g_free (teardown_str); + teardown_str = NULL; + } + + tcflush (STDIN_FILENO, TCIFLUSH); + tcsetattr (STDIN_FILENO, TCSANOW, &initial_attr); + set_property (ECHO, TRUE); + setup_finished = FALSE; +} + +static void +do_console_cancel_event (void) +{ + static MonoClassField *cancel_handler_field; + MonoError error; + MonoDomain *domain = mono_domain_get (); + MonoClass *klass; + MonoDelegate *load_value; + MonoMethod *method; + MonoVTable *vtable; + + /* FIXME: this should likely iterate all the domains, instead */ + if (!domain->domain) + return; + + klass = mono_class_try_load_from_name (mono_defaults.corlib, "System", "Console"); + if (klass == NULL) + return; + + if (cancel_handler_field == NULL) { + cancel_handler_field = mono_class_get_field_from_name (klass, "cancel_handler"); + g_assert (cancel_handler_field); + } + + vtable = mono_class_vtable_full (domain, klass, &error); + if (vtable == NULL || !is_ok (&error)) { + mono_error_cleanup (&error); + return; + } + mono_field_static_get_value_checked (vtable, cancel_handler_field, &load_value, &error); + if (load_value == NULL || !is_ok (&error)) { + mono_error_cleanup (&error); + return; + } + + klass = load_value->object.vtable->klass; + method = mono_class_get_method_from_name (klass, "BeginInvoke", -1); + g_assert (method != NULL); + + mono_threadpool_begin_invoke (domain, (MonoObject*) load_value, method, NULL, &error); + if (!is_ok (&error)) { + g_warning ("Couldn't invoke System.Console cancel handler due to %s", mono_error_get_message (&error)); + mono_error_cleanup (&error); + } +} + +static int need_cancel = FALSE; +/* this is executed from the finalizer thread */ +void +mono_console_handle_async_ops (void) +{ + if (need_cancel) { + need_cancel = FALSE; + do_console_cancel_event (); + } +} + +static gboolean in_sigint; + +MONO_SIG_HANDLER_FUNC (static, sigint_handler) +{ + int save_errno; + + if (in_sigint) + return; + + in_sigint = TRUE; + save_errno = errno; + need_cancel = TRUE; + mono_gc_finalize_notify (); + errno = save_errno; + in_sigint = FALSE; +} + +static struct sigaction save_sigcont, save_sigint, save_sigwinch; + +MONO_SIG_HANDLER_FUNC (static, sigcont_handler) +{ + int unused G_GNUC_UNUSED; + // Ignore error, there is not much we can do in the sigcont handler. + tcsetattr (STDIN_FILENO, TCSANOW, &mono_attr); + + if (keypad_xmit_str != NULL) + unused = write (STDOUT_FILENO, keypad_xmit_str, strlen (keypad_xmit_str)); + + // Call previous handler + if (save_sigcont.sa_sigaction != NULL && + save_sigcont.sa_sigaction != (void *)SIG_DFL && + save_sigcont.sa_sigaction != (void *)SIG_IGN) + (*save_sigcont.sa_sigaction) (MONO_SIG_HANDLER_PARAMS); +} + +MONO_SIG_HANDLER_FUNC (static, sigwinch_handler) +{ + int dims = terminal_get_dimensions (); + if (dims != -1) + cols_and_lines = dims; + + // Call previous handler + if (save_sigwinch.sa_sigaction != NULL && + save_sigwinch.sa_sigaction != (void *)SIG_DFL && + save_sigwinch.sa_sigaction != (void *)SIG_IGN) + (*save_sigwinch.sa_sigaction) (MONO_SIG_HANDLER_PARAMS); +} + +/* + * console_set_signal_handlers: + * + * Installs various signals handlers for the use of the console, as + * follows: + * + * SIGCONT: this is received after the application has resumed execution + * if it was suspended with Control-Z before. This signal handler needs + * to resend the terminal sequence to send keyboard in keypad mode (this + * is the difference between getting a cuu1 code or a kcuu1 code for up-arrow + * for example + * + * SIGINT: invokes the System.Console.DoConsoleCancelEvent method using + * a thread from the thread pool which notifies all registered cancel_event + * listeners. + * + * SIGWINCH: is used to track changes to the console window when a GUI + * terminal is resized. It sets an internal variable that is checked + * by System.Console when the Terminfo driver has been activated. + */ +static void +console_set_signal_handlers () +{ +#if defined(HAVE_SIGACTION) + struct sigaction sigcont, sigint, sigwinch; + + memset (&sigcont, 0, sizeof (struct sigaction)); + memset (&sigint, 0, sizeof (struct sigaction)); + memset (&sigwinch, 0, sizeof (struct sigaction)); + + // Continuing + sigcont.sa_handler = (void (*)(int)) sigcont_handler; + sigcont.sa_flags = SA_RESTART; + sigemptyset (&sigcont.sa_mask); + sigaction (SIGCONT, &sigcont, &save_sigcont); + + // Interrupt handler + sigint.sa_handler = (void (*)(int)) sigint_handler; + sigint.sa_flags = SA_RESTART; + sigemptyset (&sigint.sa_mask); + sigaction (SIGINT, &sigint, &save_sigint); + + // Window size changed + sigwinch.sa_handler = (void (*)(int)) sigwinch_handler; + sigwinch.sa_flags = SA_RESTART; + sigemptyset (&sigwinch.sa_mask); + sigaction (SIGWINCH, &sigwinch, &save_sigwinch); +#endif +} + +#if currently_unuused +// +// Currently unused, should we ever call the restore handler? +// Perhaps before calling into Process.Start? +// +void +console_restore_signal_handlers () +{ + sigaction (SIGCONT, &save_sigcont, NULL); + sigaction (SIGINT, &save_sigint, NULL); + sigaction (SIGWINCH, &save_sigwinch, NULL); +} +#endif + +static void +set_control_chars (MonoArray *control_chars, const guchar *cc) +{ + /* The index into the array comes from corlib/System/ControlCharacters.cs */ +#ifdef VINTR + mono_array_set (control_chars, gchar, 0, cc [VINTR]); +#endif +#ifdef VQUIT + mono_array_set (control_chars, gchar, 1, cc [VQUIT]); +#endif +#ifdef VERASE + mono_array_set (control_chars, gchar, 2, cc [VERASE]); +#endif +#ifdef VKILL + mono_array_set (control_chars, gchar, 3, cc [VKILL]); +#endif +#ifdef VEOF + mono_array_set (control_chars, gchar, 4, cc [VEOF]); +#endif +#ifdef VTIME + mono_array_set (control_chars, gchar, 5, cc [VTIME]); +#endif +#ifdef VMIN + mono_array_set (control_chars, gchar, 6, cc [VMIN]); +#endif +#ifdef VSWTC + mono_array_set (control_chars, gchar, 7, cc [VSWTC]); +#endif +#ifdef VSTART + mono_array_set (control_chars, gchar, 8, cc [VSTART]); +#endif +#ifdef VSTOP + mono_array_set (control_chars, gchar, 9, cc [VSTOP]); +#endif +#ifdef VSUSP + mono_array_set (control_chars, gchar, 10, cc [VSUSP]); +#endif +#ifdef VEOL + mono_array_set (control_chars, gchar, 11, cc [VEOL]); +#endif +#ifdef VREPRINT + mono_array_set (control_chars, gchar, 12, cc [VREPRINT]); +#endif +#ifdef VDISCARD + mono_array_set (control_chars, gchar, 13, cc [VDISCARD]); +#endif +#ifdef VWERASE + mono_array_set (control_chars, gchar, 14, cc [VWERASE]); +#endif +#ifdef VLNEXT + mono_array_set (control_chars, gchar, 15, cc [VLNEXT]); +#endif +#ifdef VEOL2 + mono_array_set (control_chars, gchar, 16, cc [VEOL2]); +#endif +} + +MonoBoolean +ves_icall_System_ConsoleDriver_TtySetup (MonoString *keypad, MonoString *teardown, MonoArray **control_chars, int **size) +{ + MonoError error; + + int dims; + + dims = terminal_get_dimensions (); + if (dims == -1){ + int cols = 0, rows = 0; + + char *str = g_getenv ("COLUMNS"); + if (str != NULL) { + cols = atoi (str); + g_free (str); + } + str = g_getenv ("LINES"); + if (str != NULL) { + rows = atoi (str); + g_free (str); + } + + if (cols != 0 && rows != 0) + cols_and_lines = (cols << 16) | rows; + else + cols_and_lines = -1; + } else { + cols_and_lines = dims; + } + + *size = &cols_and_lines; + + /* 17 is the number of entries set in set_control_chars() above. + * NCCS is the total size, but, by now, we only care about those 17 values*/ + MonoArray *control_chars_arr = mono_array_new_checked (mono_domain_get (), mono_defaults.byte_class, 17, &error); + if (mono_error_set_pending_exception (&error)) + return FALSE; + mono_gc_wbarrier_generic_store (control_chars, (MonoObject*) control_chars_arr); + if (tcgetattr (STDIN_FILENO, &initial_attr) == -1) + return FALSE; + + mono_attr = initial_attr; + mono_attr.c_lflag &= ~(ICANON); + mono_attr.c_iflag &= ~(IXON|IXOFF); + mono_attr.c_cc [VMIN] = 1; + mono_attr.c_cc [VTIME] = 0; +#ifdef VDSUSP + /* Disable C-y being used as a suspend character on OSX */ + mono_attr.c_cc [VDSUSP] = 255; +#endif + if (tcsetattr (STDIN_FILENO, TCSANOW, &mono_attr) == -1) + return FALSE; + + set_control_chars (*control_chars, mono_attr.c_cc); + /* If initialized from another appdomain... */ + if (setup_finished) + return TRUE; + + keypad_xmit_str = NULL; + if (keypad != NULL) { + keypad_xmit_str = mono_string_to_utf8_checked (keypad, &error); + if (mono_error_set_pending_exception (&error)) + return FALSE; + } + + console_set_signal_handlers (); + setup_finished = TRUE; + if (!atexit_called) { + if (teardown != NULL) { + teardown_str = mono_string_to_utf8_checked (teardown, &error); + if (mono_error_set_pending_exception (&error)) + return FALSE; + } + + mono_atexit (tty_teardown); + } + + return TRUE; +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/console-win32-internals.h b/unity-2019.4.24f1-mbe/mono/metadata/console-win32-internals.h new file mode 100644 index 000000000..0022457bb --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/console-win32-internals.h @@ -0,0 +1,19 @@ +/** + * \file + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#ifndef __MONO_CONSOLE_WIN32_INTERNALS_H__ +#define __MONO_CONSOLE_WIN32_INTERNALS_H__ + +#include +#include + +#include "mono/metadata/object.h" +#include "mono/metadata/object-internals.h" +#include "mono/utils/mono-error.h" +#include "mono/utils/mono-error-internals.h" +#include + +#endif /* __MONO_CONSOLE_WIN32_INTERNALS_H__ */ + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/console-win32-uwp.c b/unity-2019.4.24f1-mbe/mono/metadata/console-win32-uwp.c new file mode 100644 index 000000000..5770f0beb --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/console-win32-uwp.c @@ -0,0 +1,99 @@ +/** + * \file + * UWP console support for Mono. + * + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. +*/ +#include +#include +#include "mono/utils/mono-compiler.h" + +#if G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) +#include +#include "mono/metadata/console-win32-internals.h" + +MonoBoolean +ves_icall_System_ConsoleDriver_Isatty (HANDLE handle) +{ + MonoError mono_error; + error_init (&mono_error); + + g_unsupported_api ("Console"); + + mono_error_set_not_supported (&mono_error, G_UNSUPPORTED_API, "Console"); + mono_error_set_pending_exception (&mono_error); + + SetLastError (ERROR_NOT_SUPPORTED); + + return FALSE; +} + +MonoBoolean +ves_icall_System_ConsoleDriver_SetEcho (MonoBoolean want_echo) +{ + MonoError mono_error; + error_init (&mono_error); + + g_unsupported_api ("Console"); + + mono_error_set_not_supported (&mono_error, G_UNSUPPORTED_API, "Console"); + mono_error_set_pending_exception (&mono_error); + + SetLastError (ERROR_NOT_SUPPORTED); + + return FALSE; +} + +MonoBoolean +ves_icall_System_ConsoleDriver_SetBreak (MonoBoolean want_break) +{ + MonoError mono_error; + error_init (&mono_error); + + g_unsupported_api ("Console"); + + mono_error_set_not_supported (&mono_error, G_UNSUPPORTED_API, "Console"); + mono_error_set_pending_exception (&mono_error); + + SetLastError (ERROR_NOT_SUPPORTED); + + return FALSE; +} + +gint32 +ves_icall_System_ConsoleDriver_InternalKeyAvailable (gint32 timeout) +{ + MonoError mono_error; + error_init (&mono_error); + + g_unsupported_api ("Console"); + + mono_error_set_not_supported (&mono_error, G_UNSUPPORTED_API, "Console"); + mono_error_set_pending_exception (&mono_error); + + SetLastError (ERROR_NOT_SUPPORTED); + + return FALSE; +} + +MonoBoolean +ves_icall_System_ConsoleDriver_TtySetup (MonoString *keypad, MonoString *teardown, MonoArray **control_chars, int **size) +{ + MonoError mono_error; + error_init (&mono_error); + + g_unsupported_api ("Console"); + + mono_error_set_not_supported (&mono_error, G_UNSUPPORTED_API, "Console"); + mono_error_set_pending_exception (&mono_error); + + SetLastError (ERROR_NOT_SUPPORTED); + + return FALSE; +} + +#else /* G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) */ + +MONO_EMPTY_SOURCE_FILE (console_win32_uwp); +#endif /* G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/console-win32.c b/unity-2019.4.24f1-mbe/mono/metadata/console-win32.c new file mode 100644 index 000000000..ab856f644 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/console-win32.c @@ -0,0 +1,76 @@ +/** + * \file + * ConsoleDriver internal calls for Win32 + * + * Author: + * Gonzalo Paniagua Javier (gonzalo@ximian.com) + * + * Copyright (C) 2005-2009 Novell, Inc. (http://www.novell.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include + +void +mono_console_init (void) +{ +} + +void +mono_console_handle_async_ops (void) +{ +} + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +MonoBoolean +ves_icall_System_ConsoleDriver_Isatty (HANDLE handle) +{ + DWORD mode; + return GetConsoleMode (handle, &mode) != 0; +} + +MonoBoolean +ves_icall_System_ConsoleDriver_SetEcho (MonoBoolean want_echo) +{ + return FALSE; +} + +MonoBoolean +ves_icall_System_ConsoleDriver_SetBreak (MonoBoolean want_break) +{ + return FALSE; +} + +gint32 +ves_icall_System_ConsoleDriver_InternalKeyAvailable (gint32 timeout) +{ + return FALSE; +} + +MonoBoolean +ves_icall_System_ConsoleDriver_TtySetup (MonoString *keypad, MonoString *teardown, MonoArray **control_chars, int **size) +{ + return FALSE; +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/coree-internals.h b/unity-2019.4.24f1-mbe/mono/metadata/coree-internals.h new file mode 100644 index 000000000..38dc3e130 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/coree-internals.h @@ -0,0 +1,45 @@ +/** + * \file + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#ifndef __MONO_COREE_INTERNALS_H__ +#define __MONO_COREE_INTERNALS_H__ + +#include +#include + +#ifdef HOST_WIN32 +#include + +BOOL STDMETHODCALLTYPE +_CorDllMain (HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved); + +__int32 STDMETHODCALLTYPE +_CorExeMain (void); + +void STDMETHODCALLTYPE +CorExitProcess (int exitCode); + +STDAPI +_CorValidateImage (PVOID *ImageBase, LPCWSTR FileName); + +STDAPI_(VOID) +_CorImageUnloading (PVOID ImageBase); + +STDAPI +CorBindToRuntimeEx (LPCWSTR pwszVersion, LPCWSTR pwszBuildFlavor, + DWORD startupFlags, REFCLSID rclsid, REFIID riid, LPVOID FAR *ppv); + +STDAPI +CorBindToRuntime (LPCWSTR pwszVersion, LPCWSTR pwszBuildFlavor, + REFCLSID rclsid, REFIID riid, LPVOID FAR *ppv); + +HMODULE WINAPI +MonoLoadImage (LPCWSTR FileName); + +void mono_coree_set_act_ctx (const char *file_name); +#endif /* HOST_WIN32 */ + +#endif /* __MONO_COREE_INTERNALS_H__ */ + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/coree-windows-uwp.c b/unity-2019.4.24f1-mbe/mono/metadata/coree-windows-uwp.c new file mode 100644 index 000000000..ef4c2b773 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/coree-windows-uwp.c @@ -0,0 +1,56 @@ +/** + * \file + * UWP coree support for Mono. + * + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. +*/ +#include +#include +#include "mono/utils/mono-compiler.h" + +#if G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) +#include +#include "mono/metadata/coree-internals.h" + +BOOL STDMETHODCALLTYPE +_CorDllMain(HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved) +{ + g_unsupported_api ("_CorDllMain"); + return FALSE; +} + +__int32 STDMETHODCALLTYPE +_CorExeMain(void) +{ + g_unsupported_api ("_CorExeMain"); + ExitProcess (EXIT_FAILURE); +} + +STDAPI +_CorValidateImage(PVOID *ImageBase, LPCWSTR FileName) +{ + g_unsupported_api ("_CorValidateImage"); + return E_UNEXPECTED; +} + +HMODULE WINAPI +MonoLoadImage(LPCWSTR FileName) +{ + g_unsupported_api ("MonoLoadImage"); + return NULL; +} + +void +mono_coree_set_act_ctx (const char *file_name) +{ + g_unsupported_api ("CreateActCtx, ActivateActCtx"); + SetLastError (ERROR_NOT_SUPPORTED); + + return; +} + +#else /* G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) */ + +MONO_EMPTY_SOURCE_FILE (coree_windows_uwp); +#endif /* G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/coree.c b/unity-2019.4.24f1-mbe/mono/metadata/coree.c new file mode 100644 index 000000000..d3a0589cb --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/coree.c @@ -0,0 +1,955 @@ +/** + * \file + * mscoree.dll functions + * + * Author: + * Kornel Pal + * + * Copyright (C) 2008 Kornel Pal + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include + +#ifdef HOST_WIN32 + +#include +#include +#include +#include "utils/w32api.h" +#include "cil-coff.h" +#include "metadata-internals.h" +#include "image.h" +#include "assembly-internals.h" +#include "domain-internals.h" +#include "appdomain.h" +#include "object.h" +#include "object-internals.h" +#include "loader.h" +#include "threads.h" +#include "environment.h" +#include "coree.h" +#include "coree-internals.h" + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +#include +#endif + +HMODULE coree_module_handle = NULL; + +static gboolean init_from_coree = FALSE; + +gchar* +mono_get_module_file_name (HMODULE module_handle) +{ + gunichar2* file_name; + gchar* file_name_utf8; + DWORD buffer_size; + DWORD size; + + buffer_size = 1024; + file_name = g_new (gunichar2, buffer_size); + + for (;;) { + size = GetModuleFileName (module_handle, file_name, buffer_size); + if (!size) { + g_free (file_name); + return NULL; + } + + g_assert (size <= buffer_size); + if (size != buffer_size) + break; + + buffer_size += 1024; + file_name = g_realloc (file_name, buffer_size * sizeof (gunichar2)); + } + + file_name_utf8 = g_utf16_to_utf8 (file_name, size, NULL, NULL, NULL); + g_free (file_name); + + return file_name_utf8; +} + +/* Entry point called by LdrLoadDll of ntdll.dll after _CorValidateImage. */ +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +BOOL STDMETHODCALLTYPE _CorDllMain(HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved) +{ + MonoAssembly* assembly; + MonoImage* image; + gchar* file_name; + gchar* error; + + switch (dwReason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls (hInst); + + file_name = mono_get_module_file_name (hInst); + + if (mono_get_root_domain ()) { + image = mono_image_open_from_module_handle (hInst, mono_path_resolve_symlinks (file_name), TRUE, NULL); + } else { + init_from_coree = TRUE; + mono_runtime_load (file_name, NULL); + error = (gchar*) mono_check_corlib_version (); + if (error) { + g_free (error); + g_free (file_name); + mono_runtime_quit (); + return FALSE; + } + + image = mono_image_open (file_name, NULL); + if (image) { + image->has_entry_point = TRUE; + mono_close_exe_image (); + /* Decrement reference count to zero. (Image will not be closed.) */ + mono_image_close (image); + } + } + + if (!image) { + g_free (file_name); + return FALSE; + } + + /* + * FIXME: Find a better way to call mono_image_fixup_vtable. Only + * loader trampolines should be used and assembly loading should + * probably be delayed until the first call to an exported function. + */ + if (image->tables [MONO_TABLE_ASSEMBLY].rows && ((MonoCLIImageInfo*) image->image_info)->cli_cli_header.ch_vtable_fixups.rva) + assembly = mono_assembly_open_predicate (file_name, FALSE, FALSE, NULL, NULL, NULL); + + g_free (file_name); + break; + case DLL_PROCESS_DETACH: + if (lpReserved != NULL) + /* The process is terminating. */ + return TRUE; + file_name = mono_get_module_file_name (hInst); + image = mono_image_loaded (file_name); + if (image) + mono_image_close (image); + + g_free (file_name); + break; + } + + return TRUE; +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ + +/* Called by ntdll.dll reagardless of entry point after _CorValidateImage. */ +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +__int32 STDMETHODCALLTYPE _CorExeMain(void) +{ + MonoError error; + MonoDomain* domain; + MonoAssembly* assembly; + MonoImage* image; + MonoMethod* method; + guint32 entry; + gchar* file_name; + gchar* corlib_version_error; + int argc; + gunichar2** argvw; + gchar** argv; + int i; + + file_name = mono_get_module_file_name (NULL); + init_from_coree = TRUE; + domain = mono_runtime_load (file_name, NULL); + + corlib_version_error = (gchar*) mono_check_corlib_version (); + if (corlib_version_error) { + g_free (corlib_version_error); + g_free (file_name); + MessageBox (NULL, L"Corlib not in sync with this runtime.", NULL, MB_ICONERROR); + mono_runtime_quit (); + ExitProcess (1); + } + + assembly = mono_assembly_open_predicate (file_name, FALSE, FALSE, NULL, NULL, NULL); + mono_close_exe_image (); + if (!assembly) { + g_free (file_name); + MessageBox (NULL, L"Cannot open assembly.", NULL, MB_ICONERROR); + mono_runtime_quit (); + ExitProcess (1); + } + + image = assembly->image; + entry = mono_image_get_entry_point (image); + if (!entry) { + g_free (file_name); + MessageBox (NULL, L"Assembly doesn't have an entry point.", NULL, MB_ICONERROR); + mono_runtime_quit (); + ExitProcess (1); + } + + method = mono_get_method_checked (image, entry, NULL, NULL, &error); + if (method == NULL) { + g_free (file_name); + mono_error_cleanup (&error); /* FIXME don't swallow the error */ + MessageBox (NULL, L"The entry point method could not be loaded.", NULL, MB_ICONERROR); + mono_runtime_quit (); + ExitProcess (1); + } + + argvw = CommandLineToArgvW (GetCommandLine (), &argc); + argv = g_new0 (gchar*, argc); + argv [0] = file_name; + for (i = 1; i < argc; ++i) + argv [i] = g_utf16_to_utf8 (argvw [i], -1, NULL, NULL, NULL); + LocalFree (argvw); + + mono_runtime_run_main_checked (method, argc, argv, &error); + mono_error_raise_exception_deprecated (&error); /* OK, triggers unhandled exn handler */ + mono_thread_manage (); + + mono_runtime_quit (); + + /* return does not terminate the process. */ + ExitProcess (mono_environment_exitcode_get ()); +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ + +/* Called by msvcrt.dll when shutting down. */ +void STDMETHODCALLTYPE CorExitProcess(int exitCode) +{ + /* FIXME: This is not currently supported by the runtime. */ +#if 0 + if (mono_get_root_domain () && !mono_runtime_is_shutting_down ()) { + mono_runtime_set_shutting_down (); + mono_thread_suspend_all_other_threads (); + mono_runtime_quit (); + } +#endif + ExitProcess (exitCode); +} + +/* Called by ntdll.dll before _CorDllMain and _CorExeMain. */ +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +STDAPI _CorValidateImage(PVOID *ImageBase, LPCWSTR FileName) +{ + IMAGE_DOS_HEADER* DosHeader; + IMAGE_NT_HEADERS32* NtHeaders32; + IMAGE_DATA_DIRECTORY* CliHeaderDir; +#ifdef _WIN64 + IMAGE_NT_HEADERS64* NtHeaders64; + MonoCLIHeader* CliHeader; + DWORD SizeOfHeaders; +#endif + DWORD* Address; + DWORD OldProtect; + + DosHeader = (IMAGE_DOS_HEADER*)*ImageBase; + if (DosHeader->e_magic != IMAGE_DOS_SIGNATURE) + return STATUS_INVALID_IMAGE_FORMAT; + + NtHeaders32 = (IMAGE_NT_HEADERS32*)((DWORD_PTR)DosHeader + DosHeader->e_lfanew); + if (NtHeaders32->Signature != IMAGE_NT_SIGNATURE) + return STATUS_INVALID_IMAGE_FORMAT; + +#ifdef _WIN64 + NtHeaders64 = (IMAGE_NT_HEADERS64*)NtHeaders32; + if (NtHeaders64->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) + { + if (NtHeaders64->OptionalHeader.NumberOfRvaAndSizes <= IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR) + return STATUS_INVALID_IMAGE_FORMAT; + + CliHeaderDir = &NtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR]; + if (!CliHeaderDir->VirtualAddress) + return STATUS_INVALID_IMAGE_FORMAT; + + CliHeader = (MonoCLIHeader*)((DWORD_PTR)DosHeader + CliHeaderDir->VirtualAddress); + if (CliHeader->ch_flags & CLI_FLAGS_32BITREQUIRED) + return STATUS_INVALID_IMAGE_FORMAT; + + if (CliHeader->ch_flags & CLI_FLAGS_ILONLY) + { + /* Avoid calling _CorDllMain because imports are not resolved for IL only images. */ + if (NtHeaders64->OptionalHeader.AddressOfEntryPoint != 0) + { + Address = &NtHeaders64->OptionalHeader.AddressOfEntryPoint; + if (!VirtualProtect(Address, sizeof(DWORD), PAGE_READWRITE, &OldProtect)) + return E_UNEXPECTED; + *Address = (DWORD)0; + if (!VirtualProtect(Address, sizeof(DWORD), OldProtect, &OldProtect)) + return E_UNEXPECTED; + } + } + + return STATUS_SUCCESS; + } + + if (NtHeaders32->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) + return STATUS_INVALID_IMAGE_FORMAT; + + if (NtHeaders32->OptionalHeader.NumberOfRvaAndSizes <= IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR) + return STATUS_INVALID_IMAGE_FORMAT; + + CliHeaderDir = &NtHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR]; + if (!CliHeaderDir->VirtualAddress) + return STATUS_INVALID_IMAGE_FORMAT; + + CliHeader = (MonoCLIHeader*)((DWORD_PTR)DosHeader + CliHeaderDir->VirtualAddress); + if (!(CliHeader->ch_flags & CLI_FLAGS_ILONLY) || (CliHeader->ch_flags & CLI_FLAGS_32BITREQUIRED)) + return STATUS_INVALID_IMAGE_FORMAT; + + /* Fixup IMAGE_NT_HEADERS32 to IMAGE_NT_HEADERS64. */ + SizeOfHeaders = NtHeaders32->OptionalHeader.SizeOfHeaders; + if (SizeOfHeaders < DosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS64) + (sizeof(IMAGE_SECTION_HEADER) * NtHeaders32->FileHeader.NumberOfSections)) + return STATUS_INVALID_IMAGE_FORMAT; + + if (!VirtualProtect(DosHeader, SizeOfHeaders, PAGE_READWRITE, &OldProtect)) + return E_UNEXPECTED; + + memmove(NtHeaders64 + 1, IMAGE_FIRST_SECTION(NtHeaders32), sizeof(IMAGE_SECTION_HEADER) * NtHeaders32->FileHeader.NumberOfSections); + + NtHeaders64->OptionalHeader.DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES - 1].Size = 0; + NtHeaders64->OptionalHeader.DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES - 1].VirtualAddress = 0; + NtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].Size = NtHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].Size; + NtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress = NtHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress; + NtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].Size = 0; + NtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].VirtualAddress = 0; + NtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size = 0; + NtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = 0; + NtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0; + NtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0; + NtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].Size = 0; + NtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].VirtualAddress = 0; + NtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].Size = 0; + NtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress = 0; + NtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_GLOBALPTR].Size = 0; + NtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_GLOBALPTR].VirtualAddress = 0; + NtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_ARCHITECTURE].Size = 0; + NtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_ARCHITECTURE].VirtualAddress = 0; + NtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size = NtHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size; + NtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress = NtHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress; + NtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = NtHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size; + NtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = NtHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress; + NtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].Size = NtHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].Size; + NtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress = NtHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress; + NtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size = 0; + NtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress = 0; + NtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = NtHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size; + NtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = NtHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress; + NtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = 0; + NtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = 0; + NtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size = 0; + NtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress = 0; + + NtHeaders64->OptionalHeader.NumberOfRvaAndSizes = IMAGE_NUMBEROF_DIRECTORY_ENTRIES; + NtHeaders64->OptionalHeader.LoaderFlags = NtHeaders32->OptionalHeader.LoaderFlags; + NtHeaders64->OptionalHeader.SizeOfHeapCommit = (ULONGLONG)NtHeaders32->OptionalHeader.SizeOfHeapCommit; + NtHeaders64->OptionalHeader.SizeOfHeapReserve = (ULONGLONG)NtHeaders32->OptionalHeader.SizeOfHeapReserve; + NtHeaders64->OptionalHeader.SizeOfStackCommit = (ULONGLONG)NtHeaders32->OptionalHeader.SizeOfStackCommit; + NtHeaders64->OptionalHeader.SizeOfStackReserve = (ULONGLONG)NtHeaders32->OptionalHeader.SizeOfStackReserve; + NtHeaders64->OptionalHeader.DllCharacteristics = NtHeaders32->OptionalHeader.DllCharacteristics; + NtHeaders64->OptionalHeader.Subsystem = NtHeaders32->OptionalHeader.Subsystem; + NtHeaders64->OptionalHeader.CheckSum = NtHeaders32->OptionalHeader.CheckSum; + NtHeaders64->OptionalHeader.SizeOfHeaders = NtHeaders32->OptionalHeader.SizeOfHeaders; + NtHeaders64->OptionalHeader.SizeOfImage = NtHeaders32->OptionalHeader.SizeOfImage; + NtHeaders64->OptionalHeader.Win32VersionValue = NtHeaders32->OptionalHeader.Win32VersionValue; + NtHeaders64->OptionalHeader.MinorSubsystemVersion = NtHeaders32->OptionalHeader.MinorSubsystemVersion; + NtHeaders64->OptionalHeader.MajorSubsystemVersion = NtHeaders32->OptionalHeader.MajorSubsystemVersion; + NtHeaders64->OptionalHeader.MinorImageVersion = NtHeaders32->OptionalHeader.MinorImageVersion; + NtHeaders64->OptionalHeader.MajorImageVersion = NtHeaders32->OptionalHeader.MajorImageVersion; + NtHeaders64->OptionalHeader.MinorOperatingSystemVersion = NtHeaders32->OptionalHeader.MinorOperatingSystemVersion; + NtHeaders64->OptionalHeader.MajorOperatingSystemVersion = NtHeaders32->OptionalHeader.MajorOperatingSystemVersion; + NtHeaders64->OptionalHeader.FileAlignment = NtHeaders32->OptionalHeader.FileAlignment; + NtHeaders64->OptionalHeader.SectionAlignment = NtHeaders32->OptionalHeader.SectionAlignment; + NtHeaders64->OptionalHeader.ImageBase = (ULONGLONG)NtHeaders32->OptionalHeader.ImageBase; + /* BaseOfCode is at the same offset. */ + NtHeaders64->OptionalHeader.AddressOfEntryPoint = 0; + NtHeaders64->OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR64_MAGIC; + NtHeaders64->FileHeader.SizeOfOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER64); + + if (!VirtualProtect(DosHeader, SizeOfHeaders, OldProtect, &OldProtect)) + return E_UNEXPECTED; +#else + if (NtHeaders32->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) + return STATUS_INVALID_IMAGE_FORMAT; + + if (NtHeaders32->OptionalHeader.NumberOfRvaAndSizes <= IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR) + return STATUS_INVALID_IMAGE_FORMAT; + + CliHeaderDir = &NtHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR]; + if (!CliHeaderDir->VirtualAddress) + return STATUS_INVALID_IMAGE_FORMAT; + + Address = &NtHeaders32->OptionalHeader.AddressOfEntryPoint; + if (!VirtualProtect(Address, sizeof(DWORD), PAGE_READWRITE, &OldProtect)) + return E_UNEXPECTED; + if (NtHeaders32->FileHeader.Characteristics & IMAGE_FILE_DLL) + *Address = (DWORD)((DWORD_PTR)&_CorDllMain - (DWORD_PTR)DosHeader); + else + *Address = (DWORD)((DWORD_PTR)&_CorExeMain - (DWORD_PTR)DosHeader); + if (!VirtualProtect(Address, sizeof(DWORD), OldProtect, &OldProtect)) + return E_UNEXPECTED; +#endif + + return STATUS_SUCCESS; +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ + +/* Called by ntdll.dll. */ +STDAPI_(VOID) _CorImageUnloading(PVOID ImageBase) +{ + /* Nothing to do. */ +} + +STDAPI CorBindToRuntimeEx(LPCWSTR pwszVersion, LPCWSTR pwszBuildFlavor, DWORD startupFlags, REFCLSID rclsid, REFIID riid, LPVOID FAR *ppv) +{ + if (ppv == NULL) + return E_POINTER; + + *ppv = NULL; + return E_NOTIMPL; +} + +STDAPI CorBindToRuntime(LPCWSTR pwszVersion, LPCWSTR pwszBuildFlavor, REFCLSID rclsid, REFIID riid, LPVOID FAR *ppv) +{ + return CorBindToRuntimeEx (pwszVersion, pwszBuildFlavor, 0, rclsid, riid, ppv); +} + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +HMODULE WINAPI MonoLoadImage(LPCWSTR FileName) +{ + HANDLE FileHandle; + DWORD FileSize; + HANDLE MapHandle; + IMAGE_DOS_HEADER* DosHeader; + IMAGE_NT_HEADERS32* NtHeaders32; +#ifdef _WIN64 + IMAGE_NT_HEADERS64* NtHeaders64; +#endif + HMODULE ModuleHandle; + + FileHandle = CreateFile(FileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (FileHandle == INVALID_HANDLE_VALUE) + return NULL; + + FileSize = GetFileSize(FileHandle, NULL); + if (FileSize == INVALID_FILE_SIZE) + goto CloseFile; + + MapHandle = CreateFileMapping(FileHandle, NULL, PAGE_READONLY, 0, 0, NULL); + if (MapHandle == NULL) + goto CloseFile; + + DosHeader = (IMAGE_DOS_HEADER*)MapViewOfFile(MapHandle, FILE_MAP_READ, 0, 0, 0); + if (DosHeader == NULL) + goto CloseMap; + + if (FileSize < sizeof(IMAGE_DOS_HEADER) || DosHeader->e_magic != IMAGE_DOS_SIGNATURE || FileSize < DosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS32)) + goto InvalidImageFormat; + + NtHeaders32 = (IMAGE_NT_HEADERS32*)((DWORD_PTR)DosHeader + DosHeader->e_lfanew); + if (NtHeaders32->Signature != IMAGE_NT_SIGNATURE) + goto InvalidImageFormat; + +#ifdef _WIN64 + NtHeaders64 = (IMAGE_NT_HEADERS64*)NtHeaders32; + if (NtHeaders64->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) + { + if (FileSize < DosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS64) || + NtHeaders64->OptionalHeader.NumberOfRvaAndSizes <= IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR || + !NtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress) + goto InvalidImageFormat; + + goto ValidImage; + } +#endif + + if (NtHeaders32->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC || + NtHeaders32->OptionalHeader.NumberOfRvaAndSizes <= IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR || + !NtHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress) + { +InvalidImageFormat: + SetLastError(STATUS_INVALID_IMAGE_FORMAT); + goto UnmapView; + } + +#ifdef _WIN64 +ValidImage: +#endif + UnmapViewOfFile(DosHeader); + CloseHandle(MapHandle); + + ModuleHandle = LoadLibrary(FileName); + + CloseHandle(FileHandle); + return ModuleHandle; + +UnmapView: + UnmapViewOfFile(DosHeader); +CloseMap: + CloseHandle(MapHandle); +CloseFile: + CloseHandle(FileHandle); + return NULL; +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ + +typedef struct _EXPORT_FIXUP +{ + LPCSTR Name; + union + { + PVOID Pointer; + DWORD_PTR DWordPtr; + BYTE Bytes[sizeof(PVOID)]; +#ifdef _M_IA64 + PLABEL_DESCRIPTOR* PLabel; +#endif + } ProcAddress; +} EXPORT_FIXUP; + +/* Has to be binary ordered. */ +static const EXPORT_FIXUP ExportFixups[] = { + {"CorBindToRuntime", {&CorBindToRuntime}}, + {"CorBindToRuntimeEx", {&CorBindToRuntimeEx}}, + {"CorExitProcess", {&CorExitProcess}}, + {"_CorDllMain", {&_CorDllMain}}, + {"_CorExeMain", {&_CorExeMain}}, + {"_CorImageUnloading", {&_CorImageUnloading}}, + {"_CorValidateImage", {&_CorValidateImage}}, + {NULL, {NULL}} +}; + +#define EXPORT_FIXUP_COUNT (sizeof(ExportFixups) / sizeof(EXPORT_FIXUP) - 1) + +static HMODULE ExportFixupModuleHandle = NULL; +static DWORD ExportFixupRvas[EXPORT_FIXUP_COUNT]; + +/* Fixup exported functions of mscoree.dll to our implementations. */ +STDAPI MonoFixupCorEE(HMODULE ModuleHandle) +{ + IMAGE_DOS_HEADER* DosHeader; + IMAGE_NT_HEADERS* NtHeaders; + IMAGE_DATA_DIRECTORY* ExportDataDir; + IMAGE_EXPORT_DIRECTORY* ExportDir; + DWORD* Functions; + DWORD* Names; + WORD* NameOrdinals; + EXPORT_FIXUP* ExportFixup; + DWORD* ExportFixupRva; + DWORD* Address; + DWORD OldProtect; + DWORD ProcRva; + DWORD i; + int cmp; +#ifdef _WIN64 + MEMORY_BASIC_INFORMATION MemoryInfo; + PVOID Region; + PVOID RegionBase; + PVOID MaxRegionBase; +#ifdef _M_IA64 + PLABEL_DESCRIPTOR* PLabel; + +#define ELEMENT_SIZE sizeof(PLABEL_DESCRIPTOR) +#define REGION_WRITE_PROTECT PAGE_READWRITE +#define REGION_PROTECT PAGE_READ +#else + BYTE* Trampoline; + +#define ELEMENT_SIZE 13 +#define REGION_WRITE_PROTECT PAGE_EXECUTE_READWRITE +#define REGION_PROTECT PAGE_EXECUTE_READ +#endif +#endif + + if (ExportFixupModuleHandle != NULL) + return ModuleHandle == ExportFixupModuleHandle ? S_OK : E_FAIL; + + DosHeader = (IMAGE_DOS_HEADER*)ModuleHandle; + if (DosHeader == NULL) + return E_POINTER; + + if (DosHeader->e_magic != IMAGE_DOS_SIGNATURE) + return E_INVALIDARG; + + NtHeaders = (IMAGE_NT_HEADERS*)((DWORD_PTR)DosHeader + DosHeader->e_lfanew); + if (NtHeaders->Signature != IMAGE_NT_SIGNATURE) + return E_INVALIDARG; + + if (NtHeaders->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC) + return E_INVALIDARG; + + if (NtHeaders->OptionalHeader.NumberOfRvaAndSizes <= IMAGE_DIRECTORY_ENTRY_EXPORT) + return E_FAIL; + + ExportDataDir = &NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; + if (!ExportDataDir->VirtualAddress) + return E_FAIL; + +#ifdef _WIN64 + /* Allocate memory after base address because RVAs are 32-bit unsigned integers. */ + RegionBase = DosHeader; + MaxRegionBase = (PVOID)((DWORD_PTR)RegionBase + (DWORD_PTR)(0x100000000L - (ELEMENT_SIZE * (EXPORT_FIXUP_COUNT - 1)))); + for (;;) + { + if (!VirtualQuery(RegionBase, &MemoryInfo, sizeof(MEMORY_BASIC_INFORMATION))) + return E_UNEXPECTED; + if (MemoryInfo.State == MEM_FREE && MemoryInfo.RegionSize >= ELEMENT_SIZE * EXPORT_FIXUP_COUNT) + { + Region = VirtualAlloc(RegionBase, ELEMENT_SIZE * EXPORT_FIXUP_COUNT, MEM_COMMIT | MEM_RESERVE, REGION_WRITE_PROTECT); + if (Region != NULL) + break; + } + RegionBase = (PVOID)((DWORD_PTR)MemoryInfo.BaseAddress + (DWORD_PTR)MemoryInfo.RegionSize); + if (RegionBase > MaxRegionBase) + return E_OUTOFMEMORY; + } + +#ifdef _M_IA64 + PLabel = (PLABEL_DESCRIPTOR*)Region; +#else + Trampoline = (BYTE*)Region; +#endif +#endif + + ExportDir = (IMAGE_EXPORT_DIRECTORY*)((DWORD_PTR)DosHeader + ExportDataDir->VirtualAddress); + Functions = (DWORD*)((DWORD_PTR)DosHeader + ExportDir->AddressOfFunctions); + Names = (DWORD*)((DWORD_PTR)DosHeader + ExportDir->AddressOfNames); + NameOrdinals = (WORD*)((DWORD_PTR)DosHeader + ExportDir->AddressOfNameOrdinals); + ExportFixup = (EXPORT_FIXUP*)&ExportFixups; + ExportFixupRva = (DWORD*)&ExportFixupRvas; + + for (i = 0; i < ExportDir->NumberOfNames; i++) + { + cmp = strcmp((LPCSTR)((DWORD_PTR)DosHeader + Names[i]), ExportFixup->Name); + if (cmp > 0) + return E_FAIL; + + if (cmp == 0) + { +#ifdef _WIN64 +#if defined(_M_IA64) + ProcRva = (DWORD)((DWORD_PTR)PLabel - (DWORD_PTR)DosHeader); + *(PLabel)++ = *ExportFixup->ProcAddress.PLabel; +#elif defined(_M_AMD64) + ProcRva = (DWORD)((DWORD_PTR)Trampoline - (DWORD_PTR)DosHeader); + /* mov r11, ExportFixup->ProcAddress */ + *(Trampoline)++ = 0x49; + *(Trampoline)++ = 0xBB; + *(Trampoline)++ = ExportFixup->ProcAddress.Bytes[0]; + *(Trampoline)++ = ExportFixup->ProcAddress.Bytes[1]; + *(Trampoline)++ = ExportFixup->ProcAddress.Bytes[2]; + *(Trampoline)++ = ExportFixup->ProcAddress.Bytes[3]; + *(Trampoline)++ = ExportFixup->ProcAddress.Bytes[4]; + *(Trampoline)++ = ExportFixup->ProcAddress.Bytes[5]; + *(Trampoline)++ = ExportFixup->ProcAddress.Bytes[6]; + *(Trampoline)++ = ExportFixup->ProcAddress.Bytes[7]; + /* jmp r11 */ + *(Trampoline)++ = 0x41; + *(Trampoline)++ = 0xFF; + *(Trampoline)++ = 0xE3; +#else +#error Unsupported architecture. +#endif +#else + ProcRva = (DWORD)(ExportFixup->ProcAddress.DWordPtr - (DWORD_PTR)DosHeader); +#endif + Address = &Functions[NameOrdinals[i]]; + if (!VirtualProtect(Address, sizeof(DWORD), PAGE_READWRITE, &OldProtect)) + return E_UNEXPECTED; + *ExportFixupRva = *Address; + *Address = ProcRva; + if (!VirtualProtect(Address, sizeof(DWORD), OldProtect, &OldProtect)) + return E_UNEXPECTED; + ExportFixup++; + if (ExportFixup->Name == NULL) { +#ifdef _WIN64 + if (!VirtualProtect(Region, ELEMENT_SIZE * EXPORT_FIXUP_COUNT, REGION_PROTECT, &OldProtect)) + return E_UNEXPECTED; +#endif + + ExportFixupModuleHandle = ModuleHandle; + return S_OK; + } + ExportFixupRva++; + } + } + return E_FAIL; +} + +/* Executable images are only mapped by the OS loader. We need to do fixups for native code support. */ +STDAPI MonoFixupExe(HMODULE ModuleHandle) +{ + IMAGE_DOS_HEADER* DosHeader; + IMAGE_NT_HEADERS* NtHeaders; + DWORD_PTR* Address; + DWORD OldProtect; + DWORD_PTR BaseDiff; + + DosHeader = (IMAGE_DOS_HEADER*)ModuleHandle; + if (DosHeader == NULL) + return E_POINTER; + + if (DosHeader->e_magic != IMAGE_DOS_SIGNATURE) + return E_INVALIDARG; + + NtHeaders = (IMAGE_NT_HEADERS*)((DWORD_PTR)DosHeader + DosHeader->e_lfanew); + if (NtHeaders->Signature != IMAGE_NT_SIGNATURE) + return E_INVALIDARG; + + if (NtHeaders->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC) + return E_INVALIDARG; + + if (NtHeaders->FileHeader.Characteristics & IMAGE_FILE_DLL) + return S_OK; + + if (NtHeaders->OptionalHeader.NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR) + { + IMAGE_DATA_DIRECTORY* CliHeaderDir; + MonoCLIHeader* CliHeader; + + CliHeaderDir = &NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR]; + if (CliHeaderDir->VirtualAddress) + { + CliHeader = (MonoCLIHeader*)((DWORD_PTR)DosHeader + CliHeaderDir->VirtualAddress); + if (CliHeader->ch_flags & CLI_FLAGS_ILONLY) + return S_OK; + } + } + + BaseDiff = (DWORD_PTR)DosHeader - NtHeaders->OptionalHeader.ImageBase; + if (BaseDiff != 0) + { + if (NtHeaders->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED) + return E_FAIL; + + Address = &NtHeaders->OptionalHeader.ImageBase; + if (!VirtualProtect(Address, sizeof(DWORD_PTR), PAGE_READWRITE, &OldProtect)) + return E_UNEXPECTED; + *Address = (DWORD_PTR)DosHeader; + if (!VirtualProtect(Address, sizeof(DWORD_PTR), OldProtect, &OldProtect)) + return E_UNEXPECTED; + + if (NtHeaders->OptionalHeader.NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_BASERELOC) + { + IMAGE_DATA_DIRECTORY* BaseRelocDir; + IMAGE_BASE_RELOCATION* BaseReloc; + USHORT* RelocBlock; + ULONG BaseRelocSize; + ULONG RelocBlockSize; + USHORT RelocOffset; + DWORD_PTR UNALIGNED *RelocFixup; + + BaseRelocDir = &NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; + if (BaseRelocDir->VirtualAddress) + { + BaseReloc = (IMAGE_BASE_RELOCATION*)((DWORD_PTR)DosHeader + BaseRelocDir->VirtualAddress); + BaseRelocSize = BaseRelocDir->Size; + + while (BaseRelocSize) + { + RelocBlockSize = BaseReloc->SizeOfBlock; + + if (!RelocBlockSize || BaseRelocSize < RelocBlockSize) + return E_FAIL; + + BaseRelocSize -= RelocBlockSize; + RelocBlock = (USHORT*)((DWORD_PTR)BaseReloc + sizeof(IMAGE_BASE_RELOCATION)); + RelocBlockSize -= sizeof(IMAGE_BASE_RELOCATION); + RelocBlockSize /= sizeof(USHORT); + + while (RelocBlockSize-- != 0) + { + RelocOffset = *RelocBlock & (USHORT)0x0fff; + RelocFixup = (DWORD_PTR*)((DWORD_PTR)DosHeader + BaseReloc->VirtualAddress + RelocOffset); + + switch (*RelocBlock >> 12) + { + case IMAGE_REL_BASED_ABSOLUTE: + break; + +#ifdef _WIN64 + case IMAGE_REL_BASED_DIR64: +#else + case IMAGE_REL_BASED_HIGHLOW: +#endif + if (!VirtualProtect(RelocFixup, sizeof(DWORD_PTR), PAGE_EXECUTE_READWRITE, &OldProtect)) + return E_UNEXPECTED; + *RelocFixup += BaseDiff; + if (!VirtualProtect(RelocFixup, sizeof(DWORD_PTR), OldProtect, &OldProtect)) + return E_UNEXPECTED; + break; + + default: + return E_FAIL; + } + + RelocBlock++; + } + BaseReloc = (IMAGE_BASE_RELOCATION*)RelocBlock; + } + } + } + } + + if (NtHeaders->OptionalHeader.NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_IMPORT) + { + IMAGE_DATA_DIRECTORY* ImportDir; + IMAGE_IMPORT_DESCRIPTOR* ImportDesc; + HMODULE ImportModuleHandle; + IMAGE_THUNK_DATA* ImportThunkData; + DWORD_PTR ProcAddress; + + ImportDir = &NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; + if (ImportDir->VirtualAddress != 0) + { + ImportDesc = (IMAGE_IMPORT_DESCRIPTOR*)((DWORD_PTR)DosHeader + ImportDir->VirtualAddress); + while (ImportDesc->Name && ImportDesc->OriginalFirstThunk) + { + gchar *file_utf8 = (gchar *)((DWORD_PTR)DosHeader + ImportDesc->Name); + + gunichar2 *file_utf16 = g_utf8_to_utf16 (file_utf8, (glong)strlen (file_utf8), NULL, NULL, NULL); + ImportModuleHandle = NULL; + if (file_utf16 != NULL) { + ImportModuleHandle = LoadLibraryW(file_utf16); + g_free (file_utf16); + } + + if (ImportModuleHandle == NULL) + return E_FAIL; + + ImportThunkData = (IMAGE_THUNK_DATA*)((DWORD_PTR)DosHeader + ImportDesc->OriginalFirstThunk); + while (ImportThunkData->u1.Ordinal != 0) + { + if (IMAGE_SNAP_BY_ORDINAL(ImportThunkData->u1.Ordinal)) + ProcAddress = (DWORD_PTR)GetProcAddress(ImportModuleHandle, (LPCSTR)IMAGE_ORDINAL(ImportThunkData->u1.Ordinal)); + else + { + IMAGE_IMPORT_BY_NAME* ImportByName = (IMAGE_IMPORT_BY_NAME*)((DWORD_PTR)DosHeader + ImportThunkData->u1.AddressOfData); + ProcAddress = (DWORD_PTR)GetProcAddress(ImportModuleHandle, ImportByName->Name); + } + if (ProcAddress == 0) + return E_FAIL; + Address = (DWORD_PTR*)((DWORD_PTR)ImportThunkData - ImportDesc->OriginalFirstThunk + ImportDesc->FirstThunk); + if (!VirtualProtect(Address, sizeof(DWORD_PTR), PAGE_READWRITE, &OldProtect)) + return E_UNEXPECTED; + *Address = ProcAddress; + if (!VirtualProtect(Address, sizeof(DWORD_PTR), OldProtect, &OldProtect)) + return E_UNEXPECTED; + ImportThunkData++; + } + + ImportDesc++; + } + } + } + + return S_OK; +} + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +void +mono_coree_set_act_ctx (const char* file_name) +{ + typedef HANDLE (WINAPI* CREATEACTCTXW_PROC) (PCACTCTXW pActCtx); + typedef BOOL (WINAPI* ACTIVATEACTCTX_PROC) (HANDLE hActCtx, ULONG_PTR* lpCookie); + + HMODULE kernel32_handle; + CREATEACTCTXW_PROC CreateActCtx_proc; + ACTIVATEACTCTX_PROC ActivateActCtx_proc; + gchar* full_path; + gunichar2* full_path_utf16; + gchar* dir_name; + gunichar2* dir_name_utf16; + gchar* base_name; + gunichar2* base_name_utf16; + ACTCTX act_ctx; + HANDLE handle; + ULONG_PTR cookie; + + kernel32_handle = GetModuleHandle (L"kernel32.dll"); + if (!kernel32_handle) + return; + CreateActCtx_proc = (CREATEACTCTXW_PROC) GetProcAddress (kernel32_handle, "CreateActCtxW"); + if (!CreateActCtx_proc) + return; + ActivateActCtx_proc = (ACTIVATEACTCTX_PROC) GetProcAddress (kernel32_handle, "ActivateActCtx"); + if (!ActivateActCtx_proc) + return; + + full_path = mono_path_canonicalize (file_name); + full_path_utf16 = g_utf8_to_utf16 (full_path, -1, NULL, NULL, NULL); + dir_name = g_path_get_dirname (full_path); + dir_name_utf16 = g_utf8_to_utf16 (dir_name, -1, NULL, NULL, NULL); + base_name = g_path_get_basename (full_path); + base_name_utf16 = g_utf8_to_utf16 (base_name, -1, NULL, NULL, NULL); + g_free (base_name); + g_free (dir_name); + g_free (full_path); + + memset (&act_ctx, 0, sizeof (ACTCTX)); + act_ctx.cbSize = sizeof (ACTCTX); + act_ctx.dwFlags = ACTCTX_FLAG_SET_PROCESS_DEFAULT | ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID | ACTCTX_FLAG_RESOURCE_NAME_VALID | ACTCTX_FLAG_APPLICATION_NAME_VALID; + act_ctx.lpSource = full_path_utf16; + act_ctx.lpAssemblyDirectory = dir_name_utf16; + act_ctx.lpResourceName = MAKEINTRESOURCE (CREATEPROCESS_MANIFEST_RESOURCE_ID); + act_ctx.lpApplicationName = base_name_utf16; + + handle = CreateActCtx_proc (&act_ctx); + if (handle == INVALID_HANDLE_VALUE && GetLastError () == ERROR_SXS_PROCESS_DEFAULT_ALREADY_SET) { + act_ctx.dwFlags &= ~ACTCTX_FLAG_SET_PROCESS_DEFAULT; + handle = CreateActCtx_proc (&act_ctx); + } + + g_free (base_name_utf16); + g_free (dir_name_utf16); + g_free (full_path_utf16); + + if (handle != INVALID_HANDLE_VALUE) + ActivateActCtx_proc (handle, &cookie); +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ + +void +mono_load_coree (const char* exe_file_name) +{ + HMODULE module_handle; + gunichar2* file_name; + UINT required_size; + UINT size; + + if (coree_module_handle) + return; + + if (!init_from_coree && exe_file_name) + mono_coree_set_act_ctx (exe_file_name); + + /* ntdll.dll loads mscoree.dll from the system32 directory. */ + required_size = GetSystemDirectory (NULL, 0); + file_name = g_new (gunichar2, required_size + 12); + size = GetSystemDirectory (file_name, required_size); + g_assert (size < required_size); + if (file_name [size - 1] != L'\\') + file_name [size++] = L'\\'; + memcpy (&file_name [size], L"mscoree.dll", 12 * sizeof (gunichar2)); + + module_handle = LoadLibrary (file_name); + g_free (file_name); + + if (module_handle && !SUCCEEDED (MonoFixupCorEE (module_handle))) { + FreeLibrary (module_handle); + module_handle = NULL; + } + + coree_module_handle = module_handle; +} + +void +mono_fixup_exe_image (MonoImage* image) +{ + if (!init_from_coree && image && image->is_module_handle) + MonoFixupExe ((HMODULE) image->raw_data); +} + +#endif /* HOST_WIN32 */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/coree.h b/unity-2019.4.24f1-mbe/mono/metadata/coree.h new file mode 100644 index 000000000..5057ab1cf --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/coree.h @@ -0,0 +1,53 @@ +/** + * \file + * mscoree.dll functions + * + * Author: + * Kornel Pal + * + * Copyright (C) 2008 Kornel Pal + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#ifndef __MONO_COREE_H__ +#define __MONO_COREE_H__ + +#include +#include + +#ifdef HOST_WIN32 + +#include +#include +#include "image.h" + +#define STATUS_SUCCESS 0x00000000L +#define STATUS_INVALID_IMAGE_FORMAT 0xC000007BL + +MONO_API STDAPI MonoFixupCorEE(HMODULE ModuleHandle); + +/* Defined by the linker. */ +#ifndef _MSC_VER +#ifdef __MINGW64_VERSION_MAJOR +#define __ImageBase __MINGW_LSYMBOL(_image_base__) +#else +#define __ImageBase _image_base__ +#endif +#endif +extern IMAGE_DOS_HEADER __ImageBase; + +extern HMODULE coree_module_handle; + +HMODULE WINAPI MonoLoadImage(LPCWSTR FileName); +STDAPI MonoFixupExe(HMODULE ModuleHandle); + +gchar* mono_get_module_file_name (HMODULE module_handle); +void mono_load_coree (const char* file_name); +void mono_fixup_exe_image (MonoImage* image); + +/* Declared in image.c. */ +MonoImage* mono_image_open_from_module_handle (HMODULE module_handle, char* fname, gboolean has_entry_point, MonoImageOpenStatus* status); + +#endif /* HOST_WIN32 */ + +#endif /* __MONO_COREE_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/culture-info-tables.h b/unity-2019.4.24f1-mbe/mono/metadata/culture-info-tables.h new file mode 100644 index 000000000..15ee88d5c --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/culture-info-tables.h @@ -0,0 +1,8479 @@ + +/* This is a generated file. Do not edit. See tools/locale-builder. */ +#ifndef MONO_METADATA_CULTURE_INFO_TABLES +#define MONO_METADATA_CULTURE_INFO_TABLES 1 + + +#define NUM_CULTURE_ENTRIES 339 +#define NUM_REGION_ENTRIES 136 + + +static const DateTimeFormatEntry datetime_format_entries [] = { + {1, 0, 0, {0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0}, {1, 10, 17, 37, 57, 81, 105, 112, 123, 134, 143, 161, 0}, {1, 10, 17, 37, 57, 81, 105, 112, 123, 134, 143, 161, 0}, {1, 10, 17, 37, 57, 81, 105, 112, 123, 134, 143, 161, 0}, {1, 10, 17, 37, 57, 81, 105, 112, 123, 134, 143, 161, 0}, 0, 6, 1, 3, {9,18,0,0,0,0,0,0,0,0,0,0,0,0},{29,42,0,0,0,0,0,0,0,0},{62,71,0,0,0,0,0,0,0,0,0,0},{77,89,0,0,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {109, 5, 8, {177, 190, 211, 226, 237, 256, 267}, {280, 285, 290, 295, 300, 305, 310}, {315, 318, 321, 324, 327, 318, 324}, {330, 343, 360, 369, 380, 387, 394, 401, 414, 433, 450, 465, 0}, {330, 343, 360, 369, 380, 387, 394, 401, 414, 433, 450, 465, 0}, {482, 489, 360, 496, 380, 387, 394, 503, 510, 517, 524, 531, 0}, {482, 489, 360, 496, 380, 387, 394, 503, 510, 517, 524, 531, 0}, 2, 1, 11, 3, {116,131,147,163,0,0,0,0,0,0,0,0,0,0},{180,199,217,242,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{279,0,0,0,0,0,0,0}}, + {109, 13, 19, {538, 547, 555, 563, 572, 579, 589}, {598, 602, 606, 610, 614, 618, 622}, {626, 629, 632, 635, 638, 641, 644}, {647, 653, 660, 666, 672, 677, 682, 689, 695, 704, 712, 721, 0}, {730, 739, 749, 758, 768, 776, 784, 794, 804, 816, 828, 840, 0}, {852, 857, 660, 863, 672, 677, 868, 873, 877, 882, 887, 892, 0}, {852, 857, 660, 863, 672, 677, 868, 873, 877, 882, 887, 892, 0}, 2, 1, 1, 3, {295,304,0,0,0,0,0,0,0,0,0,0,0,0},{315,338,0,0,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{355,0,0,0,0,0,0,0}}, + {370, 25, 32, {897, 907, 917, 927, 937, 947, 957}, {967, 974, 981, 988, 995, 1002, 1009}, {1016, 1020, 1024, 1028, 1032, 1036, 1040}, {1044, 1051, 1058, 1065, 1072, 1079, 1086, 1093, 1100, 1107, 1114, 1124, 0}, {1044, 1051, 1058, 1065, 1072, 1079, 1086, 1093, 1100, 1107, 1114, 1124, 0}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, 0, 0, 1, 3, {379,388,397,406,417,428,439,446,453,460,0,0,0,0},{469,491,519,547,562,0,0,0,0,0},{266,71,583,591,0,0,0,0,0,0,0,0},{271,89,600,611,0,0,0,0,0},{623,639,652,666,0,0,0,0}}, + {370, 25, 32, {897, 907, 917, 927, 937, 947, 957}, {967, 974, 981, 988, 995, 1002, 1009}, {1016, 1020, 1024, 1028, 1032, 1036, 1040}, {1044, 1051, 1058, 1065, 1072, 1079, 1086, 1093, 1100, 1107, 1114, 1124, 0}, {1044, 1051, 1058, 1065, 1072, 1079, 1086, 1093, 1100, 1107, 1114, 1124, 0}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, 0, 0, 1, 3, {379,388,397,406,417,428,439,446,453,460,0,0,0,0},{469,491,519,547,562,0,0,0,0,0},{266,71,583,591,0,0,0,0,0,0,0,0},{271,89,600,611,0,0,0,0,0},{623,639,652,666,0,0,0,0}}, + {673, 39, 44, {1197, 1205, 1215, 1223, 1231, 1240, 1247}, {1254, 1257, 1260, 1264, 1267, 1271, 1275}, {1278, 1280, 1282, 1285, 1287, 1280, 1285}, {1290, 1296, 1302, 1310, 1316, 1324, 1332, 1342, 1348, 1356, 1364, 1373, 0}, {1382, 1388, 1395, 1403, 1409, 1417, 1425, 1435, 1348, 1441, 1449, 1459, 0}, {1468, 1472, 1477, 1482, 1486, 1491, 1496, 1501, 1505, 1511, 1517, 1521, 0}, {1468, 1472, 1477, 1482, 1486, 1491, 1496, 1501, 1505, 1511, 1517, 1521, 0}, 2, 1, 11, 3, {681,692,0,0,0,0,0,0,0,0,0,0,0,0},{703,721,0,0,0,0,0,0,0,0},{266,0,0,0,0,0,0,0,0,0,0,0},{271,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {673, 49, 52, {1525, 1533, 1540, 1548, 1555, 1563, 1570}, {1578, 1583, 1587, 1591, 1595, 1599, 1603}, {1285, 1608, 1610, 1612, 1610, 1614, 1616}, {1618, 1625, 1633, 1639, 1645, 1649, 1654, 1659, 1666, 1676, 1684, 1693, 0}, {1618, 1625, 1633, 1639, 1645, 1649, 1654, 1659, 1666, 1676, 1684, 1693, 0}, {1702, 1707, 1712, 1717, 1645, 1722, 868, 1727, 1732, 1737, 887, 1742, 0}, {1702, 1707, 1712, 1717, 1645, 1722, 868, 1727, 1732, 1737, 887, 1742, 0}, 2, 1, 55, 3, {744,755,417,764,0,0,0,0,0,0,0,0,0,0},{721,0,0,0,0,0,0,0,0,0},{71,266,0,0,0,0,0,0,0,0,0,0},{89,271,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {673, 57, 63, {1747, 1755, 1762, 1771, 1780, 1791, 1799}, {1807, 1810, 1813, 1816, 1819, 1822, 1825}, {1285, 1608, 1828, 1608, 1828, 1614, 1285}, {1830, 1837, 1845, 1851, 1857, 1861, 1866, 1871, 1878, 1888, 1896, 1905, 0}, {1830, 1837, 1845, 1851, 1857, 1861, 1866, 1871, 1878, 1888, 1896, 1905, 0}, {1914, 1918, 1922, 1927, 1857, 1931, 1935, 1939, 1943, 1947, 1951, 1955, 0}, {1914, 1918, 1922, 1927, 1857, 1931, 1935, 1939, 1943, 1947, 1951, 1955, 0}, 2, 1, 11, 3, {681,775,417,784,0,0,0,0,0,0,0,0,0,0},{798,721,817,0,0,0,0,0,0,0},{71,830,0,0,0,0,0,0,0,0,0,0},{89,842,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 70, 77, {1959, 1974, 1989, 2000, 2015, 2028, 2047}, {2062, 2069, 2076, 2083, 2090, 2097, 2104}, {2111, 2114, 2117, 2117, 2120, 2120, 2123}, {2126, 2147, 2170, 2185, 2202, 2213, 2228, 2243, 2262, 2285, 2304, 2323, 0}, {2344, 2365, 2388, 2403, 2420, 2431, 2446, 2461, 2480, 2503, 2522, 2541, 0}, {2562, 2569, 2576, 2583, 2590, 2597, 2606, 2615, 2622, 2629, 2636, 2643, 0}, {2562, 2569, 2576, 2583, 2590, 2597, 2606, 2615, 2622, 2629, 2636, 2643, 0}, 2, 1, 1, 3, {295,18,857,9,864,417,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{906,62,266,71,0,0,0,0,0,0,0,0},{914,77,271,89,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {925, 49, 52, {2650, 2657, 2664, 2672, 2682, 2691, 2698}, {2707, 2711, 2715, 2719, 2723, 2727, 2731}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {1914, 1918, 2791, 1927, 2760, 1931, 1935, 1939, 1943, 2795, 1951, 2799, 0}, {1914, 1918, 2791, 1927, 2760, 1931, 1935, 1939, 1943, 2795, 1951, 2799, 0}, 0, 0, 1, 3, {932,941,948,957,460,417,968,0,0,0,0,0,0,0},{978,997,1010,1029,0,0,0,0,0,0},{906,62,266,71,0,0,0,0,0,0,0,0},{914,77,271,89,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {1042, 13, 19, {2803, 2811, 2817, 2824, 2835, 2842, 2850}, {2858, 2863, 1712, 2868, 2874, 2879, 2884}, {1828, 1616, 1608, 2890, 2892, 2894, 1285}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 2986, 882, 887, 2992, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 2986, 882, 887, 2992, 0}, 0, 1, 1, 3, {18,9,1054,857,1062,755,775,417,0,0,0,0,0,0},{1069,1097,1124,0,0,0,0,0,0,0},{266,71,1146,1151,1157,0,0,0,0,0,0,0},{271,89,1165,1173,1182,0,0,0,0},{1193,0,0,0,0,0,0,0}}, + {673, 84, 88, {2997, 3009, 3021, 3031, 3045, 3055, 3067}, {3078, 3081, 3084, 3087, 3090, 3093, 3096}, {1285, 1608, 1610, 3099, 1610, 1280, 1616}, {3101, 3110, 3119, 3129, 3138, 3147, 3156, 3166, 3173, 3181, 3189, 3199, 0}, {3208, 3219, 3230, 3242, 3253, 3264, 3275, 3287, 3296, 3306, 3316, 3328, 0}, {3339, 3345, 3351, 3358, 3364, 3370, 3376, 3383, 3387, 3392, 3397, 3404, 0}, {3339, 3345, 3351, 3358, 3364, 3370, 3376, 3383, 3387, 3392, 3397, 3404, 0}, 2, 1, 11, 11, {1208,0,0,0,0,0,0,0,0,0,0,0,0,0},{703,721,0,0,0,0,0,0,0,0},{1146,0,0,0,0,0,0,0,0,0,0,0},{1165,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 49, 52, {3410, 3419, 3425, 3431, 3440, 3446, 3455}, {3462, 2863, 1712, 3467, 3472, 3477, 3482}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {3487, 3495, 3504, 3509, 3515, 3519, 3524, 3532, 3538, 3548, 712, 3556, 0}, {3487, 3495, 3504, 3509, 3515, 3519, 3524, 3532, 3538, 3548, 712, 3556, 0}, {3566, 3572, 3504, 3579, 3515, 3519, 3584, 3532, 2986, 882, 887, 3590, 0}, {3566, 3572, 3504, 3579, 3515, 3519, 3584, 3532, 2986, 882, 887, 3590, 0}, 0, 1, 1, 3, {18,9,775,755,417,0,0,0,0,0,0,0,0,0},{1217,894,1234,0,0,0,0,0,0,0},{71,266,1151,1243,1253,0,0,0,0,0,0,0},{89,271,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {1, 92, 105, {3596, 3614, 3628, 3646, 3664, 3682, 3698}, {3712, 3724, 3736, 3748, 3760, 3772, 3784}, {3791, 3796, 3801, 3806, 3811, 3816, 3821}, {3826, 3837, 3850, 3857, 3868, 3875, 3884, 3893, 3906, 3919, 3934, 3947, 0}, {3826, 3837, 3850, 3857, 3868, 3875, 3884, 3893, 3906, 3919, 3934, 3947, 0}, {3958, 3967, 3850, 3976, 3868, 3875, 3884, 3985, 3994, 4003, 4012, 4021, 0}, {3958, 3967, 3850, 3976, 3868, 3875, 3884, 3985, 3994, 4003, 4012, 4021, 0}, 0, 0, 1, 3, {18,1261,9,29,755,744,1274,417,1287,1304,0,0,0,0},{1314,1261,1332,1354,1287,0,0,0,0,0},{71,62,0,0,0,0,0,0,0,0,0,0},{89,77,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {1375, 116, 120, {4030, 4040, 4048, 4053, 4060, 4073, 4081}, {2894, 4089, 3099, 4091, 4095, 1280, 4098}, {2894, 4089, 3099, 4102, 4095, 1280, 4102}, {4105, 4113, 4122, 4131, 4140, 4147, 4155, 4163, 4173, 4184, 1684, 1693, 0}, {4105, 4113, 4122, 4131, 4140, 4147, 4155, 4163, 4173, 4184, 1684, 1693, 0}, {1702, 857, 4193, 4200, 4206, 4212, 4218, 1727, 4224, 1737, 887, 1742, 0}, {1702, 857, 4193, 4200, 4206, 4212, 4218, 1727, 4224, 1737, 887, 1742, 0}, 2, 1, 124, 3, {1383,1397,0,0,0,0,0,0,0,0,0,0,0,0},{1410,1430,0,0,0,0,0,0,0,0},{266,0,0,0,0,0,0,0,0,0,0,0},{271,0,0,0,0,0,0,0,0},{1444,0,0,0,0,0,0,0}}, + {673, 127, 132, {4231, 4242, 4253, 4267, 4281, 4293, 4305}, {4317, 4322, 4328, 4334, 4340, 4345, 4351}, {1285, 1608, 4356, 1608, 1614, 1614, 1616}, {4359, 4367, 3504, 4376, 4383, 4388, 4395, 4402, 1666, 4184, 4410, 4420, 0}, {4359, 4367, 3504, 4376, 4383, 4388, 4395, 4402, 1666, 4184, 4410, 4420, 0}, {1702, 1707, 1712, 1717, 4383, 4212, 4218, 4429, 1732, 1737, 4436, 892, 0}, {1702, 1707, 1712, 1717, 4383, 4212, 4218, 4429, 1732, 1737, 4436, 892, 0}, 2, 1, 11, 3, {1208,1455,0,0,0,0,0,0,0,0,0,0,0,0},{798,721,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 49, 52, {4442, 4451, 4459, 4468, 4479, 4488, 4497}, {4504, 4508, 4512, 4516, 4520, 4524, 4528}, {1828, 1616, 1608, 1608, 4532, 2894, 1285}, {4534, 4542, 2910, 4551, 4558, 4565, 4572, 2933, 4579, 4589, 712, 4597, 0}, {4534, 4542, 2910, 4551, 4558, 4565, 4572, 2933, 4579, 4589, 712, 4597, 0}, {4606, 4610, 4512, 4614, 4618, 4622, 4626, 4630, 4634, 4638, 4642, 4646, 0}, {4606, 4610, 4512, 4614, 4618, 4622, 4626, 4630, 4634, 4638, 4642, 4646, 0}, 2, 1, 1, 3, {18,864,9,1467,857,0,0,0,0,0,0,0,0,0},{1217,1475,894,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {370, 137, 144, {4650, 4660, 4670, 4680, 4690, 4700, 4710}, {1016, 4720, 4724, 4728, 4732, 4736, 4740}, {1016, 4720, 4724, 4728, 4732, 4736, 4740}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, {4744, 4746, 4748, 4750, 4752, 4754, 4756, 4758, 4760, 4762, 4765, 4768, 0}, {4744, 4746, 4748, 4750, 4752, 4754, 4756, 4758, 4760, 4762, 4765, 4768, 0}, 0, 0, 1, 3, {406,460,439,379,417,0,0,0,0,0,0,0,0,0},{469,1484,1508,1535,1564,1588,1617,1637,0,0},{266,71,583,591,0,0,0,0,0,0,0,0},{271,89,600,611,0,0,0,0,0},{623,1662,652,0,0,0,0,0}}, + {1680, 49, 52, {4771, 4781, 4791, 4801, 4811, 4821, 4831}, {4841, 4845, 4849, 4853, 4857, 4861, 4865}, {4841, 4845, 4849, 4853, 4857, 4861, 4865}, {4869, 4874, 4879, 4884, 4889, 4894, 4899, 4904, 4909, 4914, 4920, 4926, 0}, {4869, 4874, 4879, 4884, 4889, 4894, 4899, 4904, 4909, 4914, 4920, 4926, 0}, {4869, 4874, 4879, 4884, 4889, 4894, 4899, 4904, 4909, 4914, 4920, 4926, 0}, {4869, 4874, 4879, 4884, 4889, 4894, 4899, 4904, 4909, 4914, 4920, 4926, 0}, 0, 0, 55, 3, {417,1690,446,388,0,0,0,0,0,0,0,0,0,0},{1699,1728,1752,1779,1801,1832,1858,1889,1915,1942},{583,591,266,71,0,0,0,0,0,0,0,0},{600,611,271,89,0,0,0,0,0},{1964,1981,2000,0,0,0,0,0}}, + {109, 151, 156, {4932, 4939, 4947, 4955, 4964, 4974, 4982}, {4991, 3081, 4994, 4997, 5000, 5003, 5006}, {5009, 1608, 1828, 2735, 1828, 2894, 5009}, {5011, 5019, 5028, 1639, 5034, 1649, 1654, 5038, 1666, 1676, 1684, 1693, 0}, {5011, 5019, 5028, 1639, 5034, 1649, 1654, 5038, 1666, 1676, 1684, 1693, 0}, {1702, 1707, 5047, 1717, 5034, 1722, 868, 1727, 1732, 1737, 887, 1742, 0}, {1702, 1707, 5047, 1717, 5034, 1722, 868, 1727, 1732, 1737, 887, 1742, 0}, 2, 1, 55, 3, {2015,1062,755,9,775,2024,417,0,0,0,0,0,0,0},{1217,1475,894,1234,0,0,0,0,0,0},{71,266,1146,2036,2048,0,0,0,0,0,0,0},{89,271,2060,2075,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {2090, 151, 156, {1525, 1533, 1540, 1548, 1555, 1563, 1570}, {5052, 5058, 5063, 5068, 5073, 5078, 5083}, {1285, 1608, 1610, 1612, 1610, 1614, 1616}, {1618, 1625, 3504, 1639, 3515, 1649, 1654, 1659, 1666, 1676, 1684, 4420, 0}, {1618, 1625, 3504, 1639, 3515, 1649, 1654, 1659, 1666, 1676, 1684, 4420, 0}, {5089, 4610, 4512, 4614, 3515, 5093, 5097, 5101, 5105, 5109, 4642, 5113, 0}, {5089, 4610, 4512, 4614, 3515, 5093, 5097, 5101, 5105, 5109, 4642, 5113, 0}, 0, 0, 11, 11, {681,1455,0,0,0,0,0,0,0,0,0,0,0,0},{703,721,0,0,0,0,0,0,0,0},{1151,0,0,0,0,0,0,0,0,0,0,0},{1173,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 49, 52, {5117, 5127, 5141, 5148, 5155, 5164, 1247}, {5172, 5179, 5184, 5188, 5193, 5198, 5202}, {1278, 1280, 2735, 5207, 5210, 1280, 1285}, {5212, 5221, 5226, 5233, 1645, 5243, 5252, 5259, 5269, 5279, 1364, 5292, 0}, {5302, 5311, 5318, 5324, 5333, 5338, 5346, 5352, 5361, 5371, 5385, 5395, 0}, {5403, 5407, 4512, 5411, 1645, 5415, 5419, 5423, 5427, 5431, 1517, 5436, 0}, {5403, 5407, 4512, 5411, 1645, 5415, 5419, 5423, 5427, 5431, 1517, 5436, 0}, 2, 1, 11, 3, {681,0,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {1042, 49, 52, {2803, 5440, 5454, 5467, 5480, 5493, 2850}, {4504, 5505, 5509, 5513, 5517, 5521, 5525}, {1828, 1285, 1610, 5530, 5530, 1285, 1285}, {5532, 5540, 5550, 666, 5557, 5562, 5568, 2933, 5574, 5583, 5591, 5600, 0}, {5532, 5540, 5550, 666, 5557, 5562, 5568, 2933, 5574, 5583, 5591, 5600, 0}, {5089, 5609, 4512, 5613, 3515, 5093, 5097, 4630, 4634, 5617, 4642, 5621, 0}, {5089, 5609, 4512, 5613, 3515, 5093, 5097, 4630, 4634, 5617, 4642, 5621, 0}, 0, 0, 1, 3, {18,9,295,857,755,744,1062,2015,775,681,2024,2098,1208,417},{1069,1124,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{1193,0,0,0,0,0,0,0}}, + {673, 49, 52, {5625, 5634, 3425, 5644, 5652, 5660, 5669}, {5675, 5678, 3081, 5682, 5685, 5689, 1275}, {1828, 4532, 1608, 1608, 4532, 2894, 1285}, {5692, 5700, 3504, 5707, 5714, 5719, 5729, 5737, 5743, 5753, 1684, 1693, 0}, {5692, 5700, 3504, 5707, 5714, 5719, 5729, 5737, 5743, 5753, 1684, 1693, 0}, {5761, 5768, 3504, 3579, 5714, 5774, 5781, 5737, 5786, 882, 887, 1742, 0}, {5761, 5768, 3504, 3579, 5714, 5774, 5781, 5737, 5786, 882, 887, 1742, 0}, 2, 1, 55, 3, {744,0,0,0,0,0,0,0,0,0,0,0,0,0},{2105,2134,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 151, 156, {5792, 5802, 5807, 5814, 5823, 5827, 5834}, {5845, 2863, 1712, 5850, 5823, 5855, 5860}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {5866, 5875, 5885, 5892, 3515, 5900, 5906, 1659, 5912, 5923, 5933, 5943, 0}, {5866, 5875, 5885, 5892, 3515, 5900, 5906, 1659, 5912, 5923, 5933, 5943, 0}, {5953, 1707, 1712, 1717, 3515, 5958, 5963, 1727, 2986, 882, 887, 1742, 0}, {5953, 1707, 1712, 1717, 3515, 5958, 5963, 1727, 2986, 882, 887, 1742, 0}, 0, 1, 11, 3, {681,304,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 161, 166, {5968, 5991, 211, 6014, 6025, 6040, 6055}, {6070, 285, 290, 295, 300, 305, 310}, {6075, 6078, 6075, 6081, 6084, 6078, 6081}, {6087, 6100, 360, 6115, 380, 6128, 6137, 401, 6146, 6163, 6178, 6191, 0}, {6206, 6219, 6234, 6245, 6258, 6265, 6274, 6283, 6298, 6315, 6330, 6343, 0}, {6358, 6366, 360, 6376, 380, 6128, 6137, 6384, 6392, 6402, 6410, 6420, 0}, {6358, 6366, 360, 6376, 380, 6128, 6137, 6384, 6392, 6402, 6410, 6420, 0}, 0, 1, 11, 3, {681,775,2098,744,9,0,0,0,0,0,0,0,0,0},{199,180,0,0,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {673, 49, 52, {6428, 6437, 6449, 6456, 6464, 6474, 6480}, {6487, 6491, 6495, 6499, 6503, 6508, 6512}, {6516, 6518, 6520, 6522, 6524, 6518, 6522}, {6527, 6537, 6546, 6554, 6562, 6570, 6577, 6584, 6592, 1364, 6598, 6606, 0}, {6615, 6625, 6634, 6642, 6650, 6658, 6665, 6672, 6681, 5385, 6687, 6697, 0}, {6706, 6710, 6715, 6720, 6724, 5419, 1501, 6728, 6732, 1517, 6736, 1521, 0}, {6706, 6710, 6715, 6720, 6724, 5419, 1501, 6728, 6732, 1517, 6736, 1521, 0}, 0, 1, 11, 3, {2151,2161,2169,2181,2193,2203,2213,417,0,0,0,0,0,0},{2225,2239,2254,0,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {673, 49, 52, {6740, 6748, 6757, 6764, 6771, 6780, 1247}, {1254, 1257, 6787, 1264, 6790, 6794, 1275}, {6516, 6518, 6520, 6522, 6797, 6518, 6522}, {4105, 4113, 6800, 4376, 6806, 6811, 6816, 1659, 1666, 4184, 1684, 1693, 0}, {6821, 6830, 5318, 6840, 6848, 6854, 6860, 6866, 6874, 6884, 6893, 6902, 0}, {5089, 4610, 4512, 4614, 6806, 6811, 6816, 5101, 5105, 5109, 4642, 6911, 0}, {5089, 4610, 4512, 4614, 6806, 6811, 6816, 5101, 5105, 5109, 4642, 6911, 0}, 2, 1, 11, 3, {681,692,0,0,0,0,0,0,0,0,0,0,0,0},{798,721,0,0,0,0,0,0,0,0},{266,0,0,0,0,0,0,0,0,0,0,0},{271,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 171, 183, {6915, 6922, 6931, 6940, 6952, 6960, 6969}, {6979, 6983, 2791, 6988, 6993, 6997, 7001}, {1828, 4089, 1608, 1608, 7005, 1280, 1285}, {7007, 7013, 7020, 7025, 7031, 7035, 7043, 7050, 7056, 7064, 7070, 7078, 0}, {7086, 7092, 3504, 7099, 1645, 7105, 7113, 7120, 7126, 7134, 7140, 7148, 0}, {1914, 7156, 2791, 7160, 7031, 7164, 7168, 7172, 7001, 7176, 7180, 7185, 0}, {1914, 7156, 2791, 7160, 7031, 7164, 7168, 7172, 7001, 7176, 7180, 7185, 0}, 0, 1, 11, 3, {1208,304,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {2274, 194, 197, {7189, 7197, 7205, 1548, 1555, 1563, 7212}, {7220, 7225, 7230, 1591, 7234, 1599, 7239}, {1285, 1608, 1610, 1612, 1610, 1614, 1616}, {5011, 5019, 3504, 1639, 1645, 1649, 1654, 7244, 1666, 1676, 1684, 1693, 0}, {5011, 5019, 3504, 1639, 1645, 1649, 1654, 7244, 1666, 1676, 1684, 1693, 0}, {1702, 1707, 3504, 1717, 1645, 1649, 1654, 1727, 1732, 1737, 887, 1742, 0}, {1702, 1707, 3504, 1717, 1645, 1649, 1654, 1727, 1732, 1737, 887, 1742, 0}, 2, 1, 55, 3, {417,1690,0,0,0,0,0,0,0,0,0,0,0,0},{2287,2305,0,0,0,0,0,0,0,0},{71,266,2328,0,0,0,0,0,0,0,0,0},{89,271,2338,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 0, 0, {0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {4744, 4746, 4748, 4750, 4752, 4754, 4756, 4758, 4760, 4762, 4765, 4768, 0}, {4744, 4746, 4748, 4750, 4752, 4754, 4756, 4758, 4760, 4762, 4765, 4768, 0}, 0, 0, 1, 3, {295,857,9,18,304,2351,417,0,0,0,0,0,0,0},{894,2363,2379,0,0,0,0,0,0,0},{266,71,906,62,0,0,0,0,0,0,0,0},{271,89,914,77,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {1, 200, 205, {7252, 7258, 7268, 7274, 7285, 7295, 7300}, {7310, 7314, 7318, 7322, 7327, 7331, 7335}, {1280, 1280, 1285, 7339, 1280, 5210, 5210}, {7342, 7347, 7354, 7359, 7365, 7372, 7380, 7387, 7396, 7403, 7408, 7415, 0}, {7342, 7347, 7354, 7359, 7365, 7372, 7380, 7387, 7396, 7403, 7408, 7415, 0}, {7423, 7427, 2791, 7432, 2760, 7436, 7440, 7444, 7449, 7453, 7457, 7461, 0}, {7423, 7427, 2791, 7432, 2760, 7436, 7440, 7444, 7449, 7453, 7457, 7461, 0}, 0, 1, 11, 3, {2421,304,0,0,0,0,0,0,0,0,0,0,0,0},{2431,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {1, 49, 52, {7465, 7476, 7489, 7498, 7505, 7518, 7527}, {7465, 7476, 7489, 7498, 7505, 7518, 7527}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {7536, 7547, 7558, 7567, 7578, 7585, 7592, 7605, 7614, 7625, 7638, 7649, 0}, {7536, 7547, 7558, 7567, 7578, 7585, 7592, 7605, 7614, 7625, 7638, 7649, 0}, {7536, 7547, 7558, 7567, 7578, 7585, 7592, 7605, 7614, 7625, 7638, 7649, 0}, {7536, 7547, 7558, 7567, 7578, 7585, 7592, 7605, 7614, 7625, 7638, 7649, 0}, 0, 0, 1, 3, {18,9,417,0,0,0,0,0,0,0,0,0,0,0},{2448,42,0,0,0,0,0,0,0,0},{906,62,266,71,0,0,0,0,0,0,0,0},{914,77,271,89,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {109, 49, 52, {7660, 7667, 7673, 7680, 7685, 7691, 7697}, {7703, 7707, 7711, 7715, 7719, 7723, 7727}, {1608, 1285, 1285, 7731, 3099, 2892, 1285}, {7733, 7741, 7750, 1851, 7756, 1861, 1866, 7760, 1878, 1888, 1896, 7768, 0}, {7733, 7741, 7750, 1851, 7756, 1861, 1866, 7760, 1878, 1888, 1896, 7768, 0}, {1914, 1918, 2791, 1927, 7756, 1931, 1935, 7777, 1943, 1947, 1951, 7781, 0}, {1914, 1918, 2791, 1927, 7756, 1931, 1935, 7777, 1943, 1947, 1951, 7781, 0}, 0, 0, 1, 11, {18,304,0,0,0,0,0,0,0,0,0,0,0,0},{2462,894,0,0,0,0,0,0,0,0},{1151,0,0,0,0,0,0,0,0,0,0,0},{1173,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 209, 214, {7785, 7798, 7817, 7834, 7847, 7860, 7877}, {280, 285, 290, 295, 300, 305, 310}, {7890, 6078, 6075, 6081, 6084, 6078, 6081}, {7893, 7906, 7917, 7934, 7949, 7964, 7979, 7992, 8007, 8024, 8039, 8056, 0}, {8071, 8082, 8095, 8110, 8123, 8136, 8149, 8160, 8173, 8188, 8201, 8220, 0}, {8233, 8240, 8247, 8254, 8261, 8268, 8275, 8282, 8289, 8296, 8303, 8310, 0}, {8233, 8240, 8247, 8254, 8261, 8268, 8275, 8282, 8289, 8296, 8303, 8310, 0}, 0, 1, 11, 3, {681,775,417,0,0,0,0,0,0,0,0,0,0,0},{2481,0,0,0,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{2499,0,0,0,0,0,0,0}}, + {109, 49, 52, {8317, 8332, 8353, 8368, 8381, 8394, 7877}, {280, 285, 8409, 295, 8414, 305, 310}, {315, 318, 8419, 324, 327, 318, 324}, {8422, 8439, 8448, 8463, 380, 8480, 8495, 8508, 8523, 8540, 8561, 8578, 0}, {8593, 8610, 8623, 8640, 6258, 8659, 8674, 8687, 8700, 8715, 8738, 8757, 0}, {8770, 8240, 8777, 8784, 380, 8791, 8798, 8805, 8289, 8812, 8819, 8826, 0}, {8770, 8240, 8777, 8784, 380, 8791, 8798, 8805, 8289, 8812, 8819, 8826, 0}, 0, 1, 11, 3, {775,0,0,0,0,0,0,0,0,0,0,0,0,0},{894,0,0,0,0,0,0,0,0,0},{71,266,0,0,0,0,0,0,0,0,0,0},{89,271,0,0,0,0,0,0,0},{2515,0,0,0,0,0,0,0}}, + {673, 39, 219, {8833, 8841, 8852, 8858, 8864, 8873, 1247}, {8879, 5179, 5073, 8884, 8889, 8895, 5202}, {6516, 6518, 8900, 6522, 6524, 6518, 6522}, {1618, 1625, 6800, 1639, 1645, 8902, 8908, 8914, 1666, 1676, 1684, 1693, 0}, {1618, 1625, 6800, 1639, 1645, 8902, 8908, 8914, 1666, 1676, 1684, 1693, 0}, {1702, 1707, 1712, 1717, 1645, 1722, 868, 8921, 1732, 1737, 887, 1742, 0}, {1702, 1707, 1712, 1717, 1645, 1722, 868, 8921, 1732, 1737, 887, 1742, 0}, 0, 1, 124, 3, {2529,1455,0,0,0,0,0,0,0,0,0,0,0,0},{2541,2561,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {673, 49, 52, {8926, 8937, 8948, 8959, 8970, 8981, 8987}, {1280, 7005, 1610, 3099, 1278, 7731, 1616}, {1280, 7005, 1610, 3099, 1278, 7731, 1616}, {8996, 9004, 9013, 9020, 3515, 9027, 9033, 1659, 1666, 9039, 1684, 9048, 0}, {8996, 9004, 9013, 9020, 3515, 9027, 9033, 1659, 1666, 9039, 1684, 9048, 0}, {9058, 9063, 9013, 4614, 3515, 9027, 9033, 5101, 9069, 5109, 4642, 9074, 0}, {9058, 9063, 9013, 4614, 3515, 9027, 9033, 5101, 9069, 5109, 4642, 9074, 0}, 2, 1, 11, 3, {681,1455,0,0,0,0,0,0,0,0,0,0,0,0},{798,721,0,0,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{2575,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {673, 224, 234, {9079, 9090, 9100, 9109, 9120, 9132, 9143}, {9153, 9161, 9168, 9174, 9182, 9191, 9199}, {1285, 1280, 1612, 1610, 5210, 1280, 1285}, {9206, 9216, 1633, 9227, 9236, 9242, 9250, 9258, 9266, 9277, 9286, 9296, 0}, {9206, 9216, 1633, 9227, 9236, 9242, 9250, 9258, 9266, 9277, 9286, 9296, 0}, {3566, 857, 1633, 1717, 9236, 9306, 9312, 1727, 2986, 1737, 887, 1742, 0}, {3566, 857, 1633, 1717, 9236, 9306, 9312, 1727, 2986, 1737, 887, 1742, 0}, 0, 1, 11, 3, {681,2583,0,0,0,0,0,0,0,0,0,0,0,0},{2603,2630,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2651,0,0,0,0,0,0,0}}, + {925, 241, 252, {9318, 9330, 9342, 9354, 9368, 9383, 9396}, {9410, 9413, 9416, 9419, 9422, 9425, 6790}, {1285, 1280, 9428, 1610, 3099, 1280, 9430}, {9433, 9440, 9448, 9454, 9463, 9472, 9482, 9488, 9499, 9509, 9516, 9526, 0}, {9534, 9541, 9549, 9554, 9565, 9575, 9585, 9592, 9604, 9613, 9620, 9631, 0}, {9641, 9647, 9652, 9657, 9662, 9667, 9674, 9680, 9686, 9692, 9698, 9705, 0}, {9641, 9647, 9652, 9657, 9662, 9667, 9674, 9680, 9686, 9692, 9698, 9705, 0}, 2, 1, 55, 3, {417,0,0,0,0,0,0,0,0,0,0,0,0,0},{2667,2695,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2717,0,0,0,0,0,0,0}}, + {1, 259, 271, {9712, 9727, 9742, 9757, 9774, 9793, 9804}, {9815, 9822, 9829, 9836, 9843, 9850, 9857}, {0, 0, 0, 0, 0, 0, 0}, {9864, 9875, 9888, 9897, 9908, 9915, 9922, 9929, 9942, 9957, 9970, 9981, 0}, {9864, 9875, 9888, 9897, 9908, 9915, 9922, 9929, 9942, 9957, 9970, 9981, 0}, {9994, 10001, 10008, 10015, 9908, 9915, 9922, 10022, 10029, 10036, 10043, 10050, 0}, {9994, 10001, 10008, 10015, 9908, 9915, 9922, 10022, 10029, 10036, 10043, 10050, 0}, 0, 0, 11, 3, {681,775,2098,744,9,0,0,0,0,0,0,0,0,0},{2727,2745,0,0,0,0,0,0,0,0},{71,266,0,0,0,0,0,0,0,0,0,0},{89,271,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 283, 290, {10057, 10070, 10083, 10099, 10116, 10131, 10140}, {10057, 10070, 10083, 10099, 10116, 10131, 10140}, {10149, 10152, 10155, 10158, 10161, 10164, 10167}, {10170, 10183, 10194, 10203, 10214, 10219, 10228, 10239, 10246, 10261, 10272, 10285, 0}, {10298, 10313, 10194, 10203, 10326, 10219, 10333, 10239, 10246, 10261, 10272, 10285, 0}, {10170, 10183, 10194, 10203, 10214, 10219, 10228, 10239, 10246, 10261, 10272, 10285, 0}, {10170, 10183, 10194, 10203, 10214, 10219, 10228, 10239, 10246, 10261, 10272, 10285, 0}, 0, 6, 1, 3, {18,9,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{62,71,0,0,0,0,0,0,0,0,0,0},{77,89,0,0,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {1, 297, 300, {10346, 10359, 10369, 10378, 10388, 10399, 10410}, {10422, 10425, 10430, 10435, 10440, 10445, 10450}, {10422, 10455, 10458, 10461, 10464, 10467, 10470}, {10473, 10482, 10491, 10500, 10509, 10518, 10527, 10536, 10545, 10554, 10564, 10574, 0}, {10584, 10593, 10602, 10611, 10620, 10629, 10638, 10647, 10656, 10665, 10675, 10685, 0}, {10695, 10701, 10707, 10713, 10719, 10725, 10731, 10737, 10743, 10749, 10756, 10763, 0}, {10695, 10701, 10707, 10713, 10719, 10725, 10731, 10737, 10743, 10749, 10756, 10763, 0}, 0, 1, 1, 3, {18,9,755,744,417,0,0,0,0,0,0,0,0,0},{1261,0,0,0,0,0,0,0,0,0},{906,62,266,71,0,0,0,0,0,0,0,0},{914,77,271,89,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 303, 308, {10770, 10783, 10804, 10823, 10844, 10863, 10876}, {10887, 10894, 10901, 10908, 10915, 10922, 10929}, {10936, 10939, 10939, 10942, 10945, 10948, 10951}, {10954, 10969, 10984, 10993, 11004, 11015, 11028, 11041, 11056, 11075, 11094, 11111, 0}, {11130, 11147, 11164, 11175, 11188, 11201, 11216, 11231, 11248, 11269, 11290, 11309, 0}, {11330, 11337, 11344, 11351, 11358, 11365, 11372, 11379, 11386, 11393, 11400, 11407, 0}, {11330, 11337, 11344, 11351, 11358, 11365, 11372, 11379, 11386, 11393, 11400, 11407, 0}, 0, 1, 11, 3, {681,775,2764,18,2774,864,417,0,0,0,0,0,0,0},{1029,876,2462,1261,2785,2796,2808,2825,0,0},{71,266,0,0,0,0,0,0,0,0,0,0},{89,271,0,0,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {109, 49, 52, {11414, 11420, 11434, 11457, 11471, 11487, 11494}, {11503, 11506, 11511, 11517, 11521, 11526, 11529}, {4756, 4744, 4746, 4748, 4750, 4752, 4754}, {11533, 11540, 7354, 11547, 2760, 11553, 11559, 11565, 11572, 11581, 11589, 11596, 0}, {11603, 11610, 11617, 11622, 11628, 11632, 11637, 11642, 11649, 11658, 11666, 11673, 0}, {11680, 5609, 4512, 4614, 11628, 11684, 11688, 11692, 11696, 5109, 11700, 11704, 0}, {11680, 5609, 4512, 4614, 11628, 11684, 11688, 11692, 11696, 5109, 11700, 11704, 0}, 0, 0, 11, 3, {681,304,0,0,0,0,0,0,0,0,0,0,0,0},{2843,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {925, 49, 52, {11708, 11716, 11727, 11737, 11748, 11757, 11766}, {11776, 11780, 11784, 11788, 11792, 11796, 11800}, {11804, 9428, 9428, 9428, 1612, 1612, 1616}, {11806, 11816, 11824, 11832, 11840, 11848, 11855, 11863, 11871, 11878, 11884, 11891, 0}, {11806, 11899, 11907, 11915, 11923, 11931, 11938, 11946, 11954, 11961, 11967, 11974, 0}, {11982, 11987, 1712, 11992, 11997, 12002, 12007, 12012, 12017, 12022, 12027, 12032, 0}, {11982, 11987, 1712, 11992, 11997, 12002, 12007, 12012, 12017, 12022, 12027, 12032, 0}, 2, 1, 1, 3, {406,2861,0,0,0,0,0,0,0,0,0,0,0,0},{2872,2899,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2920,0,0,0,0,0,0,0}}, + {673, 313, 324, {12037, 12047, 12058, 12065, 12072, 12082, 1247}, {12088, 12092, 12097, 12101, 12105, 12110, 12114}, {6516, 6518, 12118, 6522, 6797, 6518, 6522}, {1618, 1625, 12120, 12126, 12132, 8902, 8908, 12137, 1666, 1676, 12144, 1693, 0}, {12153, 12161, 12170, 12177, 12184, 12189, 12196, 12203, 6874, 12211, 12219, 6902, 0}, {5089, 4610, 12228, 4614, 12233, 5093, 5097, 12237, 5105, 5109, 12241, 6911, 0}, {5089, 4610, 12228, 4614, 12233, 5093, 5097, 12237, 5105, 5109, 12241, 6911, 0}, 2, 1, 11, 3, {1208,0,0,0,0,0,0,0,0,0,0,0,0,0},{798,721,0,0,0,0,0,0,0,0},{2939,0,0,0,0,0,0,0,0,0,0,0},{271,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 335, 349, {12245, 190, 211, 6014, 12258, 12275, 12286}, {12299, 12307, 12315, 12323, 12331, 12339, 12347}, {315, 318, 321, 324, 327, 318, 324}, {12355, 343, 360, 369, 12370, 12377, 12386, 401, 414, 433, 450, 465, 0}, {12355, 343, 360, 369, 12370, 12377, 12386, 401, 414, 433, 450, 465, 0}, {12395, 12403, 12411, 6376, 12370, 12419, 12427, 6384, 12435, 6402, 12445, 6420, 0}, {12395, 12403, 12411, 6376, 12370, 12419, 12427, 6384, 12435, 6402, 12445, 6420, 0}, 0, 1, 11, 3, {2953,0,0,0,0,0,0,0,0,0,0,0,0,0},{2462,1261,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2963,0,0,0,0,0,0,0}}, + {925, 0, 0, {12455, 12463, 12472, 12481, 12490, 12497, 12507}, {12516, 12520, 12524, 12528, 12532, 12535, 12539}, {0, 0, 0, 0, 0, 0, 0}, {12543, 12554, 12562, 12572, 12578, 12590, 12599, 12605, 12611, 12619, 12628, 12640, 0}, {12543, 12554, 12562, 12572, 12578, 12590, 12599, 12605, 12611, 12619, 12628, 12640, 0}, {12648, 12652, 12656, 12660, 12664, 1914, 12668, 12672, 12676, 12680, 12684, 12688, 0}, {12648, 12652, 12656, 12660, 12664, 1914, 12668, 12672, 12676, 12680, 12684, 12688, 0}, 0, 0, 55, 3, {417,2861,0,0,0,0,0,0,0,0,0,0,0,0},{2979,2997,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2717,0,0,0,0,0,0,0}}, + {925, 0, 0, {12692, 12698, 12711, 12722, 12733, 12742, 12754}, {12516, 12764, 12768, 12772, 12532, 12776, 12780}, {0, 0, 0, 0, 0, 0, 0}, {12784, 12792, 12804, 12816, 12828, 12838, 12850, 12859, 12867, 12875, 12885, 12892, 0}, {12784, 12792, 12804, 12816, 12828, 12838, 12850, 12859, 12867, 12875, 12885, 12892, 0}, {2707, 12906, 12910, 12914, 12918, 12922, 12926, 12930, 12934, 12938, 12942, 12946, 0}, {2707, 12906, 12910, 12914, 12918, 12922, 12926, 12930, 12934, 12938, 12942, 12946, 0}, 0, 0, 55, 3, {417,2861,0,0,0,0,0,0,0,0,0,0,0,0},{2979,2997,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2717,0,0,0,0,0,0,0}}, + {925, 0, 0, {12950, 12957, 12472, 12968, 12490, 12977, 12988}, {12688, 12998, 12524, 12528, 12532, 13002, 13006}, {0, 0, 0, 0, 0, 0, 0}, {13010, 13020, 13029, 13037, 13046, 13059, 13071, 13078, 13085, 13092, 13102, 13114, 0}, {13010, 13020, 13029, 13037, 13046, 13059, 13071, 13078, 13085, 13092, 13102, 13114, 0}, {13127, 12776, 13131, 13135, 12664, 13139, 13143, 12672, 13147, 13151, 13155, 13159, 0}, {13127, 12776, 13131, 13135, 12664, 13139, 13143, 12672, 13147, 13151, 13155, 13159, 0}, 0, 0, 55, 3, {417,2861,0,0,0,0,0,0,0,0,0,0,0,0},{2979,2997,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2717,0,0,0,0,0,0,0}}, + {925, 0, 0, {13163, 13168, 13174, 13184, 13196, 13204, 13215}, {13224, 13228, 13232, 13236, 13240, 12535, 13244}, {0, 0, 0, 0, 0, 0, 0}, {13248, 13258, 13268, 13275, 13282, 1861, 13287, 13294, 13301, 13310, 13318, 13326, 0}, {13248, 13258, 13268, 13275, 13282, 1861, 13287, 13294, 13301, 13310, 13318, 13326, 0}, {1914, 1918, 13006, 13334, 13338, 1931, 1935, 13342, 1943, 1947, 1951, 13346, 0}, {1914, 1918, 13006, 13334, 13338, 1931, 1935, 13342, 1943, 1947, 1951, 13346, 0}, 0, 0, 55, 3, {417,2861,0,0,0,0,0,0,0,0,0,0,0,0},{2979,2997,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2717,0,0,0,0,0,0,0}}, + {925, 49, 52, {13350, 13357, 13369, 13380, 13393, 13402, 13414}, {12516, 13424, 13428, 13236, 13240, 12535, 13244}, {1285, 1608, 13432, 1610, 1285, 4089, 1608}, {13434, 13258, 13443, 13449, 13282, 1861, 13287, 13294, 13457, 13310, 13318, 13326, 0}, {13467, 13258, 13443, 13449, 13282, 1861, 13287, 13294, 13457, 13310, 13318, 13326, 0}, {1914, 1918, 13478, 13482, 13338, 1931, 1935, 13342, 1943, 1947, 1951, 13346, 0}, {1914, 1918, 13478, 13482, 13338, 1931, 1935, 13342, 1943, 1947, 1951, 13346, 0}, 0, 0, 1, 3, {932,3009,0,0,0,0,0,0,0,0,0,0,0,0},{978,997,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 359, 363, {13486, 13493, 13501, 13509, 13518, 13528, 13535}, {13544, 13548, 13552, 13556, 13560, 13564, 13568}, {1285, 1608, 1828, 2735, 1828, 2894, 1285}, {13572, 13581, 13591, 1851, 7756, 13597, 13603, 13609, 1878, 1888, 1896, 7768, 0}, {13572, 13581, 13591, 1851, 7756, 13597, 13603, 13609, 1878, 1888, 1896, 7768, 0}, {13618, 13623, 13628, 13633, 7756, 13638, 13643, 13648, 13653, 13658, 13663, 13668, 0}, {13618, 13623, 13628, 13633, 7756, 13638, 13643, 13648, 13653, 13658, 13663, 13668, 0}, 0, 0, 55, 3, {417,2351,0,0,0,0,0,0,0,0,0,0,0,0},{2462,1261,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 49, 52, {13673, 13689, 13714, 13742, 13770, 13798, 13826}, {13845, 13855, 13865, 13875, 13885, 13895, 13905}, {13915, 13919, 13923, 13919, 13927, 13931, 13935}, {13939, 13961, 13989, 14005, 14024, 14040, 14059, 14078, 14100, 14131, 14159, 14184, 0}, {13939, 13961, 13989, 14005, 14024, 14040, 14059, 14078, 14100, 14131, 14159, 14184, 0}, {14212, 14222, 14232, 14242, 14252, 14262, 14272, 14282, 14292, 14302, 14312, 14322, 0}, {14212, 14222, 14232, 14242, 14252, 14262, 14272, 14282, 14292, 14302, 14312, 14322, 0}, 0, 1, 11, 3, {681,3021,0,0,0,0,0,0,0,0,0,0,0,0},{42,1029,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {925, 49, 52, {4231, 14332, 14343, 14353, 14363, 14373, 14387}, {14399, 14403, 14408, 14413, 14417, 14422, 14427}, {1285, 1608, 1610, 1608, 4089, 1614, 1616}, {1618, 1625, 3504, 4376, 3515, 1649, 1654, 1659, 1666, 1676, 1684, 4420, 0}, {1618, 1625, 3504, 4376, 3515, 1649, 1654, 1659, 1666, 1676, 1684, 4420, 0}, {5089, 4610, 4512, 4614, 3515, 5093, 5097, 5101, 5105, 5109, 4642, 5113, 0}, {5089, 4610, 4512, 4614, 3515, 5093, 5097, 5101, 5105, 5109, 4642, 5113, 0}, 2, 1, 55, 3, {744,0,0,0,0,0,0,0,0,0,0,0,0,0},{1314,1455,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2717,0,0,0,0,0,0,0}}, + {1, 367, 395, {14431, 14450, 14469, 14491, 14510, 14532, 14557}, {14576, 14586, 14596, 14609, 14619, 14632, 14648}, {14658, 14662, 14669, 14676, 14683, 14690, 14697}, {14701, 14717, 14736, 14752, 14771, 14778, 14788, 14804, 14820, 14839, 14861, 14877, 0}, {14701, 14717, 14736, 14752, 14771, 14778, 14788, 14804, 14820, 14839, 14861, 14877, 0}, {14896, 14906, 14736, 14752, 14771, 14778, 14919, 14932, 14942, 14955, 14974, 14984, 0}, {14896, 14906, 14736, 14752, 14771, 14778, 14919, 14932, 14942, 14955, 14974, 14984, 0}, 0, 0, 55, 3, {744,755,1062,2098,417,0,0,0,0,0,0,0,0,0},{1261,894,0,0,0,0,0,0,0,0},{71,266,591,583,0,0,0,0,0,0,0,0},{89,271,611,600,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {3033, 49, 52, {14997, 15006, 15015, 15025, 15035, 15045, 15057}, {15065, 15070, 15074, 15078, 15082, 15087, 15092}, {15096, 15100, 15103, 15106, 15109, 15113, 15117}, {15120, 15127, 15132, 1851, 15138, 15144, 15151, 15157, 15165, 15175, 15183, 15192, 0}, {15120, 15127, 15132, 1851, 15138, 15144, 15151, 15157, 15165, 15175, 15183, 15192, 0}, {1914, 15202, 2791, 1927, 15206, 15210, 15215, 15219, 15223, 15227, 1951, 15231, 0}, {1914, 15202, 2791, 1927, 15206, 15210, 15215, 15219, 15223, 15227, 1951, 15231, 0}, 0, 0, 1, 3, {18,2351,0,0,0,0,0,0,0,0,0,0,0,0},{3048,3074,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{3094,0,0,0,0,0,0,0}}, + {925, 417, 422, {15236, 15248, 15259, 15273, 15285, 15295, 15305}, {15316, 15321, 15326, 15331, 15336, 15341, 15346}, {1285, 2894, 1608, 4532, 1828, 13432, 1616}, {15351, 15368, 15381, 15395, 15408, 15421, 15434, 15448, 15460, 15474, 15488, 15502, 0}, {15351, 15368, 15381, 15395, 15408, 15421, 15434, 15448, 15460, 15474, 15488, 15502, 0}, {15515, 15522, 15527, 15532, 15536, 15541, 15546, 15551, 15556, 15563, 15568, 15574, 0}, {15515, 15522, 15527, 15532, 15536, 15541, 15546, 15551, 15556, 15563, 15568, 15574, 0}, 2, 1, 55, 3, {417,2861,0,0,0,0,0,0,0,0,0,0,0,0},{2979,2997,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2717,0,0,0,0,0,0,0}}, + {109, 151, 156, {15579, 15593, 15603, 15614, 15628, 15639, 15650}, {15663, 15668, 15673, 15680, 15686, 15692, 15698}, {1828, 1616, 1608, 5210, 1828, 9428, 1285}, {15703, 15711, 15719, 15726, 15735, 15745, 15755, 15761, 15769, 15784, 15802, 15810, 0}, {15703, 15711, 15719, 15726, 15735, 15745, 15755, 15761, 15769, 15784, 15802, 15810, 0}, {15818, 15822, 15719, 15828, 15832, 15837, 15755, 15843, 15848, 15855, 15862, 15867, 0}, {15818, 15822, 15719, 15828, 15832, 15837, 15755, 15843, 15848, 15855, 15862, 15867, 0}, 2, 0, 1, 3, {18,304,0,0,0,0,0,0,0,0,0,0,0,0},{1217,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 427, 430, {15872, 15877, 7673, 7680, 15883, 15890, 7697}, {15897, 15901, 7711, 7715, 15905, 7723, 7727}, {9428, 11804, 1285, 7731, 3099, 2892, 1285}, {7733, 7741, 15909, 1851, 7756, 1931, 15913, 15919, 1878, 1888, 1896, 15924, 0}, {7733, 7741, 15909, 1851, 7756, 1931, 15913, 15919, 1878, 1888, 1896, 15924, 0}, {1914, 1918, 15909, 1927, 7756, 1931, 1935, 15933, 1943, 1947, 1951, 13346, 0}, {1914, 1918, 15909, 1927, 7756, 1931, 1935, 15933, 1943, 1947, 1951, 13346, 0}, 0, 1, 1, 3, {2764,304,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 49, 52, {15937, 15954, 15971, 15988, 16005, 16022, 16031}, {16042, 16047, 16052, 16057, 16062, 16067, 16072}, {16077, 16080, 6081, 6081, 16083, 16077, 6081}, {16086, 16099, 16110, 16123, 16134, 16145, 16158, 16169, 16180, 16197, 16208, 16221, 0}, {16240, 16253, 16264, 16277, 16288, 16299, 16312, 16323, 16334, 16351, 16362, 16375, 0}, {16394, 16402, 16410, 16418, 16426, 16434, 16442, 16450, 16458, 16466, 16474, 16482, 0}, {16394, 16402, 16410, 16418, 16426, 16434, 16442, 16450, 16458, 16466, 16474, 16482, 0}, 0, 1, 1, 3, {18,3112,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {3125, 434, 439, {16490, 16507, 16524, 16541, 16558, 16575, 16584}, {16597, 16605, 16613, 16623, 16633, 16575, 16643}, {16077, 16080, 16651, 16651, 16083, 16077, 16654}, {16657, 16670, 9888, 16685, 9908, 16698, 16707, 9929, 16716, 16733, 16748, 16761, 0}, {6087, 6100, 360, 6115, 380, 6128, 6137, 401, 6146, 6163, 6178, 6191, 0}, {9994, 10001, 10008, 10015, 9908, 9915, 9922, 10022, 10029, 10036, 10043, 10050, 0}, {9994, 10001, 10008, 10015, 9908, 9915, 9922, 10022, 10029, 10036, 10043, 10050, 0}, 0, 1, 55, 3, {3132,0,0,0,0,0,0,0,0,0,0,0,0,0},{3141,0,0,0,0,0,0,0,0,0},{71,266,0,0,0,0,0,0,0,0,0,0},{89,271,0,0,0,0,0,0,0},{3160,0,0,0,0,0,0,0}}, + {109, 49, 52, {16776, 16785, 16794, 16802, 16811, 16820, 16827}, {16776, 16785, 16794, 16802, 16811, 16820, 16827}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {7733, 7741, 16836, 16842, 7756, 1861, 15913, 16849, 13301, 16856, 13318, 16863, 0}, {7733, 7741, 16836, 16842, 7756, 1861, 15913, 16849, 13301, 16856, 13318, 16863, 0}, {1914, 1918, 15909, 1927, 7756, 1931, 1935, 16871, 1943, 1947, 1951, 7781, 0}, {1914, 1918, 15909, 1927, 7756, 1931, 1935, 16871, 1943, 1947, 1951, 7781, 0}, 0, 0, 1, 3, {18,304,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 0, 0, {16875, 16886, 16895, 16904, 16915, 16925, 16930}, {16937, 16941, 16944, 16947, 16951, 9416, 16954}, {16958, 1828, 1285, 7339, 1280, 9428, 16961}, {16964, 16972, 11617, 11622, 16979, 16984, 16990, 12137, 16996, 17006, 17015, 11673, 0}, {16964, 16972, 11617, 11622, 16979, 16984, 16990, 12137, 16996, 17006, 17015, 11673, 0}, {17023, 17028, 11617, 4614, 16979, 16984, 16990, 12237, 11696, 5109, 17032, 11704, 0}, {17023, 17028, 11617, 4614, 16979, 16984, 16990, 12237, 11696, 5109, 17032, 11704, 0}, 0, 1, 11, 3, {3176,681,0,0,0,0,0,0,0,0,0,0,0,0},{3191,0,0,0,0,0,0,0,0,0},{71,266,0,0,0,0,0,0,0,0,0,0},{89,271,0,0,0,0,0,0,0},{3223,0,0,0,0,0,0,0}}, + {925, 444, 447, {17037, 17047, 17056, 17065, 17076, 17086, 17091}, {17098, 17102, 17107, 17112, 17117, 7723, 17121}, {17126, 1828, 1285, 5210, 1280, 2892, 1285}, {11533, 11540, 7354, 11547, 2760, 17128, 17133, 17138, 17145, 17153, 11589, 11596, 0}, {11603, 11610, 11617, 11622, 11628, 11632, 11637, 8914, 17160, 17168, 11666, 11673, 0}, {12906, 17175, 2791, 1927, 2760, 17179, 17183, 17187, 7707, 1947, 17191, 17195, 0}, {12906, 17175, 2791, 1927, 2760, 17179, 17183, 17187, 7707, 1947, 17191, 17195, 0}, 0, 0, 1, 3, {406,2861,0,0,0,0,0,0,0,0,0,0,0,0},{3239,2997,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2717,0,0,0,0,0,0,0}}, + {109, 49, 52, {17199, 17218, 17237, 17262, 17281, 17315, 17340}, {17359, 17369, 17379, 17395, 17405, 17430, 17446}, {17456, 17460, 17467, 17471, 17478, 17485, 17492}, {17496, 17524, 17558, 17574, 17593, 17600, 17610, 17626, 17642, 17673, 17695, 17717, 0}, {17496, 17524, 17558, 17574, 17593, 17600, 17610, 17626, 17642, 17673, 17695, 17717, 0}, {17496, 17524, 17558, 17574, 17593, 17600, 17610, 17626, 17642, 17673, 17695, 17717, 0}, {17496, 17524, 17558, 17574, 17593, 17600, 17610, 17626, 17642, 17673, 17695, 17717, 0}, 0, 1, 55, 11, {755,1062,744,755,744,0,0,0,0,0,0,0,0,0},{1261,894,0,0,0,0,0,0,0,0},{1151,1146,3258,3267,0,0,0,0,0,0,0,0},{1173,1165,3275,3287,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {1, 450, 465, {17742, 17758, 17777, 17799, 17821, 17840, 17868}, {17899, 17906, 17916, 17929, 17942, 17952, 17971}, {17993, 17997, 18004, 18011, 18021, 18028, 18041}, {18048, 18064, 18083, 18096, 18115, 18122, 18132, 18148, 18161, 18177, 18196, 18212, 0}, {18048, 18064, 18083, 18096, 18115, 18122, 18132, 18148, 18161, 18177, 18196, 18212, 0}, {18228, 18235, 18083, 18245, 18115, 18122, 18261, 18274, 18281, 18291, 18304, 18314, 0}, {18228, 18235, 18083, 18245, 18115, 18122, 18261, 18274, 18281, 18291, 18304, 18314, 0}, 0, 0, 55, 3, {755,1062,2098,744,417,0,0,0,0,0,0,0,0,0},{3298,894,0,0,0,0,0,0,0,0},{591,583,266,71,0,0,0,0,0,0,0,0},{611,600,271,89,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {1, 49, 52, {18324, 18343, 18362, 18384, 18403, 18425, 18450}, {18469, 18479, 18489, 18502, 18512, 18525, 18541}, {18551, 18555, 18562, 18569, 18576, 18583, 18590}, {18594, 18622, 18650, 18666, 18685, 18692, 18702, 18718, 18734, 18762, 18784, 18806, 0}, {18594, 18622, 18650, 18666, 18685, 18692, 18702, 18718, 18734, 18762, 18784, 18806, 0}, {18831, 18850, 18650, 18666, 18685, 18692, 18702, 18718, 18869, 18885, 18901, 18911, 0}, {18831, 18850, 18650, 18666, 18685, 18692, 18702, 18718, 18869, 18885, 18901, 18911, 0}, 0, 0, 55, 3, {755,1062,2098,744,417,0,0,0,0,0,0,0,0,0},{1261,894,0,0,0,0,0,0,0,0},{71,266,591,583,0,0,0,0,0,0,0,0},{89,271,611,600,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {1, 5, 8, {18924, 18943, 18962, 18987, 19006, 19028, 19053}, {19072, 19082, 19092, 19108, 19118, 19131, 19147}, {19157, 19161, 19168, 19172, 19179, 19186, 19193}, {19197, 19219, 19241, 19263, 19282, 19289, 19299, 19315, 19331, 19362, 19384, 19406, 0}, {19197, 19219, 19241, 19263, 19282, 19289, 19299, 19315, 19331, 19362, 19384, 19406, 0}, {19197, 19219, 19241, 19263, 19282, 19289, 19299, 19315, 19331, 19362, 19384, 19406, 0}, {19197, 19219, 19241, 19263, 19282, 19289, 19299, 19315, 19331, 19362, 19384, 19406, 0}, 0, 0, 55, 3, {755,1062,2098,744,417,0,0,0,0,0,0,0,0,0},{1261,894,0,0,0,0,0,0,0,0},{71,266,591,583,0,0,0,0,0,0,0,0},{89,271,611,600,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {109, 480, 505, {19431, 19450, 19472, 19497, 19513, 19535, 19554}, {19564, 19578, 19592, 19606, 19617, 19631, 19554}, {19645, 19652, 19659, 19666, 19673, 19680, 19687}, {19691, 19707, 19732, 19751, 19770, 19777, 19790, 19803, 19822, 19853, 19878, 19900, 0}, {19691, 19707, 19732, 19751, 19770, 19777, 19790, 19803, 19822, 19853, 19878, 19900, 0}, {19925, 19933, 19947, 19961, 19770, 19777, 19790, 19972, 19980, 19994, 20005, 20013, 0}, {19925, 19933, 19947, 19961, 19770, 19777, 19790, 19972, 19980, 19994, 20005, 20013, 0}, 0, 1, 55, 3, {744,755,1062,2098,417,0,0,0,0,0,0,0,0,0},{1261,894,0,0,0,0,0,0,0,0},{71,266,591,583,0,0,0,0,0,0,0,0},{89,271,611,600,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {925, 49, 52, {20024, 20046, 20068, 20093, 20115, 20140, 20168}, {20190, 20200, 20210, 20223, 20233, 20246, 20262}, {20272, 20276, 20283, 20287, 20294, 20301, 20308}, {20312, 20328, 20353, 20372, 20394, 20401, 20414, 20427, 20446, 20477, 20502, 20521, 0}, {20312, 20328, 20353, 20372, 20394, 20401, 20414, 20427, 20446, 20477, 20502, 20521, 0}, {20546, 20553, 20353, 20569, 20394, 20401, 20414, 20427, 20585, 20607, 20623, 20633, 0}, {20546, 20553, 20353, 20569, 20394, 20401, 20414, 20427, 20585, 20607, 20623, 20633, 0}, 0, 0, 55, 3, {755,1062,2098,744,417,0,0,0,0,0,0,0,0,0},{1261,894,0,0,0,0,0,0,0,0},{71,266,591,583,0,0,0,0,0,0,0,0},{89,271,611,600,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {925, 530, 558, {20649, 20671, 20690, 20712, 20731, 20753, 20778}, {20797, 20810, 20820, 20833, 20843, 20856, 20872}, {20882, 20889, 20896, 20903, 20910, 20917, 20924}, {20928, 20944, 20969, 20988, 21010, 21017, 21030, 21043, 21062, 21093, 21118, 21140, 0}, {20928, 20944, 20969, 20988, 21010, 21017, 21030, 21043, 21062, 21093, 21118, 21140, 0}, {21165, 21172, 20969, 21188, 21010, 21017, 21030, 21204, 21211, 21233, 21249, 21262, 0}, {21165, 21172, 20969, 21188, 21010, 21017, 21030, 21204, 21211, 21233, 21249, 21262, 0}, 0, 0, 55, 3, {755,1062,2098,744,417,0,0,0,0,0,0,0,0,0},{1261,894,0,0,0,0,0,0,0,0},{71,266,591,583,0,0,0,0,0,0,0,0},{89,271,611,600,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {3316, 49, 52, {21278, 21306, 21340, 21368, 21396, 21427, 21464}, {21492, 21505, 21524, 21540, 21553, 21572, 21591}, {21601, 21608, 21615, 21622, 21629, 21642, 21649}, {21653, 21672, 21700, 21722, 21741, 21754, 21764, 21777, 21802, 21833, 21858, 21874, 0}, {21653, 21672, 21700, 21722, 21741, 21754, 21764, 21777, 21802, 21833, 21858, 21874, 0}, {21893, 21903, 21922, 21932, 21741, 21754, 21764, 21948, 21955, 21980, 21996, 22006, 0}, {21893, 21903, 21922, 21932, 21741, 21754, 21764, 21948, 21955, 21980, 21996, 22006, 0}, 0, 0, 55, 11, {755,1062,775,2098,0,0,0,0,0,0,0,0,0,0},{1261,894,0,0,0,0,0,0,0,0},{1151,1146,3258,3267,0,0,0,0,0,0,0,0},{1173,1165,3275,3287,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {109, 580, 608, {22019, 22038, 22057, 22082, 22101, 22135, 22160}, {22179, 17369, 17379, 17395, 22189, 22214, 17446}, {0, 0, 0, 0, 0, 0, 0}, {22230, 22255, 22286, 22302, 17593, 17600, 17610, 22321, 22337, 22368, 22390, 22412, 0}, {22230, 22255, 22286, 22302, 17593, 17600, 17610, 22321, 22337, 22368, 22390, 22412, 0}, {22437, 22450, 22286, 22302, 17593, 17600, 17610, 22469, 22476, 22492, 22508, 22518, 0}, {22437, 22450, 22286, 22302, 17593, 17600, 17610, 22469, 22476, 22492, 22508, 22518, 0}, 0, 0, 55, 3, {744,0,0,0,0,0,0,0,0,0,0,0,0,0},{3324,0,0,0,0,0,0,0,0,0},{583,591,266,0,0,0,0,0,0,0,0,0},{600,611,271,0,0,0,0,0,0},{3343,3351,0,0,0,0,0,0}}, + {1, 630, 642, {14431, 14450, 22531, 14491, 14510, 14532, 14557}, {14576, 14586, 22553, 14609, 14619, 14632, 14648}, {14658, 14662, 14669, 14676, 14683, 14690, 14697}, {22566, 22591, 14736, 22622, 22641, 14778, 22648, 22661, 22677, 22702, 22724, 22752, 0}, {22566, 22591, 14736, 22622, 22641, 14778, 22648, 22661, 22677, 22702, 22724, 22752, 0}, {22774, 22787, 14736, 22806, 22641, 14778, 22648, 22822, 22829, 22848, 22864, 22886, 0}, {22774, 22787, 14736, 22806, 22641, 14778, 22648, 22822, 22829, 22848, 22864, 22886, 0}, 0, 0, 55, 3, {744,755,1062,2098,417,0,0,0,0,0,0,0,0,0},{1261,894,0,0,0,0,0,0,0,0},{71,266,591,583,0,0,0,0,0,0,0,0},{89,271,611,600,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {925, 651, 656, {22902, 22909, 22920, 22933, 22946, 22957, 22970}, {22981, 22986, 22991, 22996, 23001, 23006, 23011}, {22981, 22986, 22991, 22996, 23001, 23006, 23011}, {23016, 23042, 23070, 23100, 23130, 23156, 23186, 23212, 23240, 23264, 23292, 23329, 0}, {23016, 23042, 23070, 23100, 23130, 23156, 23186, 23212, 23240, 23264, 23292, 23329, 0}, {23368, 23380, 23392, 23404, 23416, 23428, 23440, 23452, 23464, 23476, 23489, 23502, 0}, {23368, 23380, 23392, 23404, 23416, 23428, 23440, 23452, 23464, 23476, 23489, 23502, 0}, 0, 0, 55, 3, {417,2861,0,0,0,0,0,0,0,0,0,0,0,0},{3361,3399,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2717,0,0,0,0,0,0,0}}, + {3431, 661, 683, {23515, 23543, 23571, 23608, 23639, 23673, 23704}, {23738, 23754, 23770, 23795, 23814, 23836, 23855}, {23877, 23884, 23891, 23901, 23911, 23921, 23931}, {23944, 23978, 24015, 24052, 24086, 24117, 24154, 24191, 24231, 24265, 24299, 24348, 0}, {24397, 24428, 24462, 24496, 24527, 24555, 24589, 24623, 24660, 24691, 24722, 24768, 0}, {24814, 24827, 24840, 24853, 24866, 24879, 24892, 24905, 24918, 24931, 24947, 24963, 0}, {24814, 24827, 24840, 24853, 24866, 24879, 24892, 24905, 24918, 24931, 24947, 24963, 0}, 0, 0, 1, 3, {379,388,397,428,417,406,446,439,453,0,0,0,0,0},{3455,3498,3546,3578,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{3615,666,0,0,0,0,0,0}}, + {109, 708, 711, {24979, 24988, 24998, 25010, 25023, 25032, 25044}, {25056, 25060, 12926, 25065, 25069, 25073, 25077}, {1285, 25081, 1608, 1608, 11804, 4532, 1285}, {25084, 25091, 25100, 25107, 1857, 25114, 25122, 25133, 25138, 25143, 25150, 25159, 0}, {25084, 25091, 25100, 25107, 1857, 25114, 25122, 25133, 25138, 25143, 25150, 25159, 0}, {25167, 25171, 12926, 25175, 1857, 25179, 25183, 25133, 25138, 25187, 25191, 25196, 0}, {25167, 25171, 12926, 25175, 1857, 25179, 25183, 25133, 25138, 25187, 25191, 25196, 0}, 2, 1, 1, 3, {18,304,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 49, 52, {25201, 25223, 25239, 25258, 25268, 25299, 25315}, {25201, 25223, 25239, 25258, 25268, 25299, 25315}, {25328, 25332, 25328, 25336, 25336, 25340, 25340}, {25344, 25357, 25376, 25389, 25402, 25415, 25434, 25453, 25466, 25482, 25495, 25520, 0}, {25344, 25357, 25376, 25389, 25402, 25415, 25434, 25453, 25466, 25482, 25495, 25520, 0}, {25344, 25357, 25376, 25389, 25402, 25415, 25434, 25453, 25466, 25482, 25495, 25520, 0}, {25344, 25357, 25376, 25389, 25402, 25415, 25434, 25453, 25466, 25482, 25495, 25520, 0}, 0, 0, 1, 3, {9,417,0,0,0,0,0,0,0,0,0,0,0,0},{894,2363,0,0,0,0,0,0,0,0},{266,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{3648,0,0,0,0,0,0,0}}, + {109, 714, 739, {25533, 25558, 25577, 25605, 25624, 25649, 25668}, {25690, 25706, 25716, 25735, 25745, 25761, 25771}, {25784, 25791, 25795, 25799, 25803, 25810, 25817}, {25821, 25840, 25856, 25869, 25882, 25904, 25923, 25945, 25961, 25977, 25990, 26006, 0}, {25821, 25840, 25856, 25869, 25882, 25904, 25923, 25945, 25961, 25977, 25990, 26006, 0}, {26022, 26031, 26040, 26049, 26058, 26067, 26079, 26088, 26097, 26106, 26115, 26124, 0}, {26022, 26031, 26040, 26049, 26058, 26067, 26079, 26088, 26097, 26106, 26115, 26124, 0}, 0, 0, 1, 3, {295,304,0,0,0,0,0,0,0,0,0,0,0,0},{3683,894,0,0,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 764, 780, {26133, 26161, 26183, 26202, 26227, 26252, 26271}, {26133, 26161, 26183, 26202, 26227, 26252, 26271}, {26281, 26281, 26285, 26289, 26293, 26297, 26301}, {26305, 26330, 26361, 26371, 26384, 26391, 26404, 26426, 26442, 26467, 26498, 26523, 0}, {26305, 26330, 26361, 26371, 26384, 26391, 26404, 26426, 26442, 26467, 26498, 26523, 0}, {26545, 26555, 26361, 26562, 26384, 26391, 26566, 26573, 26577, 26587, 26603, 26613, 0}, {26545, 26555, 26361, 26562, 26384, 26391, 26566, 26573, 26577, 26587, 26603, 26613, 0}, 0, 0, 55, 3, {744,304,0,0,0,0,0,0,0,0,0,0,0,0},{3710,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 151, 156, {2803, 26620, 2817, 26625, 26635, 26641, 2850}, {26648, 26653, 26658, 26663, 26669, 26674, 26679}, {1828, 1616, 1608, 1608, 2890, 2894, 1285}, {26685, 26693, 26702, 26708, 26714, 26719, 26725, 26731, 26738, 26747, 26755, 26764, 0}, {26773, 26781, 2910, 666, 5557, 26790, 26796, 2933, 5574, 5583, 5591, 26802, 0}, {26811, 13623, 26658, 26816, 26714, 26719, 26821, 26826, 26831, 26836, 13663, 26841, 0}, {26811, 13623, 26658, 26816, 26714, 26719, 26821, 26826, 26831, 26836, 13663, 26841, 0}, 2, 1, 1, 3, {18,3021,0,0,0,0,0,0,0,0,0,0,0,0},{1314,1261,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {1, 630, 790, {26846, 14450, 26874, 14491, 14510, 14532, 14557}, {14576, 14586, 22553, 14609, 14619, 14632, 14648}, {0, 0, 0, 0, 0, 0, 0}, {22566, 22591, 14736, 22622, 22641, 14778, 22648, 26893, 26909, 26937, 22724, 22752, 0}, {22566, 22591, 14736, 22622, 22641, 14778, 22648, 26893, 26909, 26937, 22724, 22752, 0}, {4744, 4746, 4748, 4750, 4752, 4754, 4756, 4758, 4760, 4762, 4765, 4768, 0}, {4744, 4746, 4748, 4750, 4752, 4754, 4756, 4758, 4760, 4762, 4765, 4768, 0}, 0, 0, 55, 3, {744,755,1062,2098,417,0,0,0,0,0,0,0,0,0},{1261,894,0,0,0,0,0,0,0,0},{71,266,591,583,0,0,0,0,0,0,0,0},{89,271,611,600,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {925, 802, 814, {26959, 26975, 26991, 27019, 27035, 27078, 27103}, {26959, 26975, 27131, 27019, 27141, 27163, 27176}, {27186, 27190, 27194, 27198, 27202, 27215, 27222}, {27229, 27248, 27273, 27292, 27317, 27330, 27343, 27356, 27378, 27412, 27437, 27465, 0}, {27229, 27248, 27273, 27292, 27317, 27330, 27343, 27356, 27378, 27412, 27437, 27465, 0}, {27493, 27500, 27510, 27292, 27317, 27330, 27343, 27523, 27533, 27546, 27556, 27569, 0}, {27493, 27500, 27510, 27292, 27317, 27330, 27343, 27523, 27533, 27546, 27556, 27569, 0}, 0, 1, 55, 11, {417,2861,0,0,0,0,0,0,0,0,0,0,0,0},{2979,2997,0,0,0,0,0,0,0,0},{3267,71,0,0,0,0,0,0,0,0,0,0},{3287,89,0,0,0,0,0,0,0},{2717,0,0,0,0,0,0,0}}, + {925, 823, 833, {27582, 27604, 27626, 27642, 27658, 27674, 27693}, {27715, 27725, 27735, 27745, 27755, 27765, 27775}, {27785, 27789, 27793, 27797, 27801, 27805, 27809}, {27813, 27829, 27839, 27849, 27859, 27875, 27888, 27901, 27911, 27924, 27937, 27950, 0}, {27813, 27829, 27839, 27849, 27859, 27875, 27888, 27901, 27911, 27924, 27937, 27950, 0}, {27963, 27970, 27977, 27984, 27991, 27998, 28005, 28012, 28019, 28026, 28033, 28040, 0}, {27963, 27970, 27977, 27984, 27991, 27998, 28005, 28012, 28019, 28026, 28033, 28040, 0}, 0, 0, 1, 3, {932,941,948,957,460,417,968,0,0,0,0,0,0,0},{3731,3750,42,2448,0,0,0,0,0,0},{906,62,266,71,0,0,0,0,0,0,0,0},{914,77,271,89,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {925, 852, 862, {28047, 28057, 28064, 28077, 28087, 28097, 28107}, {28047, 28057, 28117, 28077, 28087, 28097, 28107}, {28127, 28131, 28135, 28139, 28143, 28147, 28151}, {28155, 28171, 28187, 28197, 28210, 28217, 28224, 28234, 28247, 28266, 28282, 28298, 0}, {28155, 28171, 28187, 28197, 28210, 28217, 28224, 28234, 28247, 28266, 28282, 28298, 0}, {28314, 28324, 28187, 28334, 28210, 28217, 28224, 28344, 28354, 28364, 28374, 28384, 0}, {28314, 28324, 28187, 28334, 28210, 28217, 28224, 28344, 28354, 28364, 28374, 28384, 0}, 0, 0, 1, 3, {18,304,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 875, 885, {28394, 28401, 28407, 28414, 28420, 28426, 28434}, {28444, 28448, 28452, 28456, 28460, 28464, 28468}, {9428, 9428, 9428, 9428, 9428, 9428, 9428}, {28474, 28483, 7020, 28492, 28498, 28504, 28510, 28517, 28523, 28532, 28541, 28549, 0}, {28474, 28483, 7020, 28492, 28498, 28504, 28510, 28517, 28523, 28532, 28541, 28549, 0}, {28558, 28562, 2791, 28566, 2760, 28570, 28574, 28578, 28583, 28587, 28593, 28597, 0}, {28558, 28562, 2791, 28566, 2760, 28570, 28574, 28578, 28583, 28587, 28593, 28597, 0}, 0, 0, 55, 3, {744,755,417,0,0,0,0,0,0,0,0,0,0,0},{2448,42,0,0,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {1, 367, 395, {28601, 28620, 28639, 28664, 28683, 28705, 28730}, {28749, 14586, 28759, 14609, 28775, 14632, 14648}, {28788, 14662, 28792, 14676, 28796, 14690, 14697}, {14701, 28803, 14736, 28831, 22641, 28850, 14788, 28860, 28876, 28907, 28929, 28954, 0}, {14701, 28803, 14736, 28831, 14771, 28850, 14788, 28860, 28876, 28907, 28929, 28954, 0}, {14701, 28803, 14736, 28831, 22641, 28850, 14788, 28860, 28876, 28907, 28929, 28954, 0}, {14701, 28803, 14736, 28831, 22641, 28850, 14788, 28860, 28876, 28907, 28929, 28954, 0}, 0, 0, 1, 3, {932,941,948,957,460,417,968,0,0,0,0,0,0,0},{3763,3783,42,2448,0,0,0,0,0,0},{906,62,266,71,0,0,0,0,0,0,0,0},{914,77,271,89,0,0,0,0,0},{3351,0,0,0,0,0,0,0}}, + {109, 49, 52, {28979, 28985, 28993, 29001, 29010, 29021, 29027}, {29033, 29036, 3084, 4997, 3090, 29039, 1275}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {29042, 29053, 13591, 1851, 29064, 29070, 2769, 13609, 29075, 1888, 29085, 29094, 0}, {29042, 29053, 13591, 1851, 29064, 29070, 2769, 13609, 29075, 1888, 29085, 29094, 0}, {1914, 1918, 29103, 1927, 1857, 1931, 1935, 1939, 1943, 1947, 1951, 7781, 0}, {1914, 1918, 29103, 1927, 1857, 1931, 1935, 1939, 1943, 1947, 1951, 7781, 0}, 2, 1, 55, 3, {744,304,0,0,0,0,0,0,0,0,0,0,0,0},{1217,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 0, 0, {0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0}, {29107, 29114, 29123, 29138, 29149, 29158, 29165, 29172, 29179, 29190, 29203, 29216, 0}, {29107, 29114, 29123, 29138, 29149, 29158, 29165, 29172, 29179, 29190, 29203, 29216, 0}, {29107, 29114, 29123, 29138, 29149, 29158, 29165, 29172, 29179, 29190, 29203, 29216, 0}, {29107, 29114, 29123, 29138, 29149, 29158, 29165, 29172, 29179, 29190, 29203, 29216, 0}, 0, 6, 1, 3, {379,417,0,0,0,0,0,0,0,0,0,0,0,0},{894,1217,0,0,0,0,0,0,0,0},{266,0,0,0,0,0,0,0,0,0,0,0},{271,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {925, 49, 52, {29221, 29228, 29234, 29241, 29252, 29260, 29269}, {29276, 29280, 2791, 29284, 29288, 29292, 7727}, {29276, 29280, 2791, 29284, 29288, 29292, 7727}, {29296, 29302, 29310, 26708, 29316, 29321, 29327, 26731, 29333, 29343, 29351, 29361, 0}, {29296, 29302, 29310, 26708, 29316, 29321, 29327, 26731, 29333, 29343, 29351, 29361, 0}, {29371, 29375, 2791, 29379, 2760, 29383, 29387, 16871, 15223, 1947, 29391, 13346, 0}, {29371, 29375, 2791, 29379, 2760, 29383, 29387, 16871, 15223, 1947, 29391, 13346, 0}, 0, 0, 1, 3, {932,3009,0,0,0,0,0,0,0,0,0,0,0,0},{978,997,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {1, 898, 905, {29395, 29400, 29408, 29417, 29427, 29437, 29444}, {29455, 29459, 29464, 12088, 29468, 29472, 29476}, {29480, 29482, 29484, 6516, 6516, 29484, 29486}, {29488, 29494, 29500, 29506, 29514, 29521, 29527, 29533, 29538, 29545, 29554, 29560, 0}, {29488, 29494, 29500, 29506, 29514, 29521, 29527, 29533, 29538, 29545, 29554, 29560, 0}, {29566, 29570, 29574, 29578, 29582, 29586, 29590, 29594, 29598, 29602, 29606, 29610, 0}, {29566, 29570, 29574, 29578, 29582, 29586, 29590, 29594, 29598, 29602, 29606, 29610, 0}, 0, 0, 1, 3, {18,9,775,755,417,0,0,0,0,0,0,0,0,0},{1217,1234,894,0,0,0,0,0,0,0},{71,266,1151,1243,0,0,0,0,0,0,0,0},{89,271,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {925, 49, 52, {29614, 29621, 29629, 29636, 29643, 29651, 29660}, {29667, 29671, 29675, 29679, 29683, 7723, 28444}, {1616, 1616, 1610, 1616, 9428, 2892, 9428}, {29687, 29695, 29705, 29711, 29719, 29724, 29729, 29734, 29741, 16856, 29749, 29757, 0}, {29687, 29695, 29705, 29711, 29719, 29724, 29729, 29734, 29741, 16856, 29749, 29757, 0}, {1914, 29765, 2791, 29769, 2760, 28570, 28574, 29773, 2731, 1947, 29777, 13346, 0}, {1914, 29765, 2791, 29769, 2760, 28570, 28574, 29773, 2731, 1947, 29777, 13346, 0}, 0, 0, 1, 3, {295,3021,0,0,0,0,0,0,0,0,0,0,0,0},{1010,1029,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {925, 914, 925, {29781, 29799, 29814, 29836, 29849, 29863, 29880}, {29904, 29912, 29917, 29836, 29849, 29929, 29936}, {0, 0, 0, 0, 0, 0, 0}, {29950, 29972, 29988, 30008, 30022, 30039, 30054, 30071, 30085, 30098, 30117, 30131, 0}, {29950, 29972, 29988, 30008, 30022, 30039, 30054, 30071, 30085, 30098, 30117, 30131, 0}, {30150, 30165, 30174, 30187, 30194, 30204, 30212, 30222, 30229, 30235, 30247, 30254, 0}, {30150, 30165, 30174, 30187, 30194, 30204, 30212, 30222, 30229, 30235, 30247, 30254, 0}, 0, 0, 1, 3, {18,304,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {925, 0, 0, {30266, 30274, 12472, 12968, 12490, 30285, 30295}, {12516, 12998, 12524, 12528, 12532, 12535, 30304}, {0, 0, 0, 0, 0, 0, 0}, {30308, 30317, 30327, 30335, 7756, 2764, 30343, 30349, 30358, 30367, 30376, 30385, 0}, {30308, 30317, 30327, 30335, 7756, 2764, 30343, 30349, 30358, 30367, 30376, 30385, 0}, {1914, 1918, 13006, 30394, 7756, 1931, 1935, 16871, 15223, 1947, 30398, 13346, 0}, {1914, 1918, 13006, 30394, 7756, 1931, 1935, 16871, 15223, 1947, 30398, 13346, 0}, 0, 0, 55, 3, {417,2861,0,0,0,0,0,0,0,0,0,0,0,0},{2979,2997,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2717,0,0,0,0,0,0,0}}, + {3797, 935, 941, {30402, 30410, 30419, 30430, 30440, 30452, 30460}, {12516, 30470, 30475, 30480, 30485, 30489, 30493}, {1285, 1608, 1828, 1608, 1828, 1614, 1285}, {1830, 1837, 30497, 30504, 30512, 1861, 1866, 1871, 1878, 1888, 1896, 1905, 0}, {1830, 1837, 30497, 30504, 30512, 1861, 1866, 1871, 1878, 1888, 1896, 1905, 0}, {1914, 1918, 30516, 29379, 30512, 1931, 1935, 1939, 1943, 1947, 1951, 1955, 0}, {1914, 1918, 30516, 29379, 30512, 1931, 1935, 1939, 1943, 1947, 1951, 1955, 0}, 2, 1, 11, 3, {775,9,755,0,0,0,0,0,0,0,0,0,0,0},{721,3806,798,2541,3819,3844,0,0,0,0},{71,266,1146,3870,0,0,0,0,0,0,0,0},{89,3882,3897,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {3913, 951, 956, {30521, 30528, 30543, 30557, 30573, 30588, 30604}, {4528, 30619, 4512, 30623, 30627, 30631, 30635}, {1285, 9428, 1608, 1280, 1285, 1610, 9428}, {5011, 5019, 30639, 30646, 30653, 1649, 1654, 30658, 30668, 30679, 30688, 30698, 0}, {5011, 5019, 30639, 30646, 30653, 1649, 1654, 30658, 30668, 30679, 30688, 30698, 0}, {5089, 4610, 4512, 4614, 1645, 5093, 5097, 5101, 5105, 5109, 4642, 6911, 0}, {5089, 4610, 4512, 4614, 1645, 5093, 5097, 5101, 5105, 5109, 4642, 6911, 0}, 0, 0, 55, 3, {744,755,417,764,0,0,0,0,0,0,0,0,0,0},{3926,721,2561,0,0,0,0,0,0,0},{71,266,0,0,0,0,0,0,0,0,0,0},{89,271,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {925, 961, 966, {30708, 30724, 30732, 30740, 30749, 30761, 30771}, {30781, 30787, 30793, 30797, 30801, 30809, 2731}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {30816, 30829, 30843, 30852, 30512, 30858, 30863, 30871, 13301, 30884, 13318, 13326, 0}, {30816, 30829, 30843, 30852, 30512, 30858, 30863, 30871, 13301, 30884, 13318, 13326, 0}, {30893, 1918, 30897, 13334, 30512, 30901, 1935, 30905, 1943, 30913, 1951, 13346, 0}, {30893, 1918, 30897, 13334, 30512, 30901, 1935, 30905, 1943, 30913, 1951, 13346, 0}, 0, 0, 1, 3, {18,304,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {1, 971, 974, {30919, 30927, 30935, 30943, 30950, 30958, 30966}, {30974, 30978, 30982, 30986, 7719, 30990, 30994}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {30998, 31006, 31018, 31030, 31035, 31042, 31053, 31064, 31072, 31081, 31094, 31102, 0}, {30998, 31006, 31018, 31030, 31035, 31042, 31053, 31064, 31072, 31081, 31094, 31102, 0}, {31109, 31113, 31117, 31121, 31125, 31129, 31133, 31137, 31141, 31145, 25077, 12918, 0}, {31109, 31113, 31117, 31121, 31125, 31129, 31133, 31137, 31141, 31145, 25077, 12918, 0}, 0, 0, 1, 3, {18,2796,0,0,0,0,0,0,0,0,0,0,0,0},{978,1261,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {1, 977, 997, {31149, 31162, 31172, 28077, 31182, 31192, 31202}, {31212, 31219, 31226, 31233, 31240, 31247, 31254}, {28131, 28131, 31261, 28139, 31265, 28147, 31269}, {31273, 31280, 31293, 31306, 31319, 31332, 31339, 31349, 31359, 31375, 31388, 31398, 0}, {31273, 31280, 31293, 31306, 31319, 31332, 31339, 31349, 31359, 31375, 31388, 31398, 0}, {31273, 31411, 31418, 31425, 31432, 31332, 31439, 31446, 31453, 31460, 31467, 31474, 0}, {31273, 31411, 31418, 31425, 31432, 31332, 31439, 31446, 31453, 31460, 31467, 31474, 0}, 0, 0, 1, 3, {18,2796,0,0,0,0,0,0,0,0,0,0,0,0},{3945,1261,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 49, 52, {31481, 31489, 31499, 31508, 31518, 31527, 31537}, {31546, 31549, 31552, 31555, 31558, 31561, 31564}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {31567, 31575, 31585, 31592, 7756, 31602, 31607, 31613, 31622, 31633, 31643, 31652, 0}, {31567, 31575, 31585, 31592, 7756, 31602, 31607, 31613, 31622, 31633, 31643, 31652, 0}, {31661, 31666, 31671, 31676, 7756, 31682, 31687, 31692, 31698, 31703, 31709, 31714, 0}, {31661, 31666, 31671, 31676, 7756, 31682, 31687, 31692, 31698, 31703, 31709, 31714, 0}, 0, 0, 1, 3, {295,304,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {925, 1017, 1021, {31719, 31724, 31731, 31739, 31746, 31754, 31760}, {31766, 15901, 29675, 31770, 15905, 30990, 7727}, {9428, 11804, 1610, 9428, 31774, 2892, 1285}, {31777, 31791, 31804, 31820, 31833, 31847, 31860, 31875, 31891, 31907, 31921, 31943, 0}, {31777, 31791, 31804, 31820, 31833, 31847, 31860, 31875, 31891, 31907, 31921, 31943, 0}, {31966, 31970, 25077, 31974, 31978, 31982, 31986, 31990, 31994, 31998, 32002, 32006, 0}, {31966, 31970, 25077, 31974, 31978, 31982, 31986, 31990, 31994, 31998, 32002, 32006, 0}, 0, 0, 1, 3, {18,2796,0,0,0,0,0,0,0,0,0,0,0,0},{3763,1261,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {3982, 1025, 1032, {32010, 32020, 32030, 32040, 32050, 32060, 32070}, {32080, 32087, 32094, 32101, 32108, 32115, 32122}, {32129, 32133, 32137, 32141, 32145, 32149, 32153}, {32157, 32164, 32171, 32178, 32185, 32192, 32199, 32206, 32213, 32220, 32227, 32237, 0}, {32157, 32164, 32171, 32178, 32185, 32192, 32199, 32206, 32213, 32220, 32227, 32237, 0}, {32157, 32164, 32171, 32178, 32185, 32192, 32199, 32206, 32213, 32220, 32227, 32237, 0}, {32157, 32164, 32171, 32178, 32185, 32192, 32199, 32206, 32213, 32220, 32227, 32237, 0}, 0, 0, 1, 3, {379,388,397,428,417,406,0,0,0,0,0,0,0,0},{4005,4029,4059,4089,4106,0,0,0,0,0},{583,266,71,0,0,0,0,0,0,0,0,0},{600,271,89,0,0,0,0,0,0},{4129,666,0,0,0,0,0,0}}, + {109, 961, 1039, {25056, 29280, 32247, 32254, 32264, 32269, 32276}, {25056, 29280, 32283, 32288, 32264, 32293, 32298}, {32303, 1616, 32306, 32309, 17126, 4532, 1825}, {32312, 32319, 32247, 32330, 32336, 32340, 32349, 32356, 32361, 32370, 32375, 32378, 0}, {32312, 32319, 32247, 32330, 32336, 32340, 32349, 32356, 32361, 32370, 32375, 32378, 0}, {32384, 32389, 32397, 32403, 32336, 32408, 32414, 32356, 32420, 32370, 32375, 32426, 0}, {32384, 32389, 32397, 32403, 32336, 32408, 32414, 32356, 32420, 32370, 32375, 32426, 0}, 2, 1, 55, 3, {417,2861,0,0,0,0,0,0,0,0,0,0,0,0},{2979,2997,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {3125, 1044, 1050, {32431, 32448, 32463, 32480, 32497, 32514, 32523}, {32534, 32539, 32544, 32549, 32554, 32559, 32564}, {32569, 10152, 10155, 10158, 10161, 10164, 10167}, {32572, 32585, 32598, 32607, 32620, 32627, 32638, 32649, 32664, 32681, 32698, 32713, 0}, {32572, 32585, 32598, 32607, 32620, 32627, 32638, 32649, 32664, 32681, 32698, 32713, 0}, {32572, 32585, 32598, 32607, 32620, 32627, 32638, 32649, 32664, 32681, 32698, 32713, 0}, {32572, 32585, 32598, 32607, 32620, 32627, 32638, 32649, 32664, 32681, 32698, 32713, 0}, 0, 0, 55, 3, {388,397,417,428,0,0,0,0,0,0,0,0,0,0},{4146,4167,4193,4234,4280,0,0,0,0,0},{266,71,583,591,0,0,0,0,0,0,0,0},{271,89,600,611,0,0,0,0,0},{4294,2717,666,0,0,0,0,0}}, + {673, 1056, 1061, {32728, 32736, 32746, 32756, 32765, 32775, 32783}, {32793, 32797, 32802, 32806, 32810, 32814, 13568}, {1285, 1608, 1828, 1608, 1828, 1614, 1285}, {1830, 1837, 1845, 1851, 1857, 1861, 1866, 32818, 32827, 32838, 32847, 32857, 0}, {1830, 1837, 1845, 1851, 1857, 1861, 1866, 32818, 32827, 32838, 32847, 32857, 0}, {1914, 1918, 1922, 1927, 1857, 1931, 1935, 1939, 1943, 1947, 1951, 1955, 0}, {1914, 1918, 1922, 1927, 1857, 1931, 1935, 1939, 1943, 1947, 1951, 1955, 0}, 2, 1, 1, 3, {18,9,775,755,417,0,0,0,0,0,0,0,0,0},{1217,1234,894,0,0,0,0,0,0,0},{71,266,1151,1243,1253,0,0,0,0,0,0,0},{89,271,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {4315, 1066, 1071, {32867, 32892, 32917, 32942, 32955, 32970, 32987}, {33002, 33007, 33012, 33017, 33022, 33027, 310}, {16083, 16083, 33032, 6081, 6084, 16083, 6081}, {33035, 33054, 33069, 33091, 33111, 33125, 33139, 33151, 33175, 33197, 33214, 33231, 0}, {33248, 33267, 33282, 33304, 33324, 33340, 33356, 33370, 33396, 33420, 33437, 33231, 0}, {33454, 33463, 33472, 33479, 33486, 33493, 33500, 33507, 33514, 33521, 33528, 33535, 0}, {33454, 33463, 33472, 33479, 33486, 33493, 33500, 33507, 33514, 33521, 33528, 33535, 0}, 0, 1, 11, 3, {681,1208,417,4331,4341,0,0,0,0,0,0,0,0,0},{4354,4389,4418,0,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{4453,0,0,0,0,0,0,0}}, + {925, 49, 52, {33542, 33554, 33565, 33577, 33589, 33599, 33611}, {33626, 33631, 33636, 33641, 33646, 33651, 33656}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {33661, 33670, 33682, 33690, 33695, 33705, 33712, 33721, 33728, 33734, 33743, 33754, 0}, {33661, 33670, 33682, 33690, 33695, 33705, 33712, 33721, 33728, 33734, 33743, 33754, 0}, {33762, 33767, 33772, 33777, 33782, 33787, 33792, 33646, 33797, 33802, 33807, 33812, 0}, {33762, 33767, 33772, 33777, 33782, 33787, 33792, 33646, 33797, 33802, 33807, 33812, 0}, 0, 0, 1, 3, {406,2861,0,0,0,0,0,0,0,0,0,0,0,0},{3239,2997,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2717,0,0,0,0,0,0,0}}, + {4469, 1076, 1078, {33817, 33830, 33838, 33847, 33857, 33867, 33876}, {33888, 33892, 33896, 33900, 33904, 33908, 33912}, {1828, 1616, 1608, 5210, 9428, 4089, 1285}, {33916, 33930, 33941, 33950, 33961, 33973, 33987, 33999, 34012, 34025, 34037, 34050, 0}, {34064, 34081, 34095, 34107, 34121, 34136, 34150, 34162, 34177, 34192, 34206, 34221, 0}, {34237, 34242, 34248, 34254, 34259, 34265, 34271, 34276, 34282, 34287, 15862, 34293, 0}, {34237, 34242, 34248, 34254, 34259, 34265, 34271, 34276, 34282, 34287, 15862, 34293, 0}, 2, 1, 1, 3, {18,304,0,0,0,0,0,0,0,0,0,0,0,0},{4480,4502,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {1, 0, 0, {0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0}, {1, 10, 17, 37, 57, 81, 105, 112, 123, 134, 143, 161, 0}, {1, 10, 17, 37, 57, 81, 105, 112, 123, 134, 143, 161, 0}, {1, 10, 17, 37, 57, 81, 105, 112, 123, 134, 143, 161, 0}, {1, 10, 17, 37, 57, 81, 105, 112, 123, 134, 143, 161, 0}, 0, 0, 1, 3, {9,18,0,0,0,0,0,0,0,0,0,0,0,0},{29,42,0,0,0,0,0,0,0,0},{62,71,0,0,0,0,0,0,0,0,0,0},{77,89,0,0,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {109, 5, 8, {177, 190, 211, 226, 237, 256, 267}, {280, 285, 290, 295, 300, 305, 310}, {315, 318, 321, 324, 327, 318, 324}, {330, 343, 360, 369, 380, 387, 394, 401, 414, 433, 450, 465, 0}, {330, 343, 360, 369, 380, 387, 394, 401, 414, 433, 450, 465, 0}, {482, 489, 360, 496, 380, 387, 394, 503, 510, 517, 524, 531, 0}, {482, 489, 360, 496, 380, 387, 394, 503, 510, 517, 524, 531, 0}, 2, 1, 11, 3, {116,131,147,163,0,0,0,0,0,0,0,0,0,0},{180,199,217,242,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{279,0,0,0,0,0,0,0}}, + {109, 13, 19, {538, 547, 555, 563, 572, 579, 589}, {598, 602, 606, 610, 614, 618, 622}, {626, 629, 632, 635, 638, 641, 644}, {647, 653, 660, 666, 672, 677, 682, 689, 695, 704, 712, 721, 0}, {730, 739, 749, 758, 768, 776, 784, 794, 804, 816, 828, 840, 0}, {852, 857, 660, 863, 672, 677, 868, 873, 877, 882, 887, 892, 0}, {852, 857, 660, 863, 672, 677, 868, 873, 877, 882, 887, 892, 0}, 2, 1, 1, 3, {295,304,0,0,0,0,0,0,0,0,0,0,0,0},{315,338,0,0,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{355,0,0,0,0,0,0,0}}, + {370, 25, 32, {897, 907, 917, 927, 937, 947, 957}, {34299, 34306, 34313, 34320, 34327, 34334, 34341}, {1016, 1020, 1024, 1028, 1032, 1036, 1040}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, 0, 0, 1, 3, {379,406,417,0,0,0,0,0,0,0,0,0,0,0},{469,1484,547,4518,0,0,0,0,0,0},{591,583,71,266,0,0,0,0,0,0,0,0},{611,600,89,271,0,0,0,0,0},{623,639,652,0,0,0,0,0}}, + {673, 39, 44, {1197, 1205, 1215, 1223, 1231, 1240, 1247}, {1254, 1257, 1260, 1264, 1267, 1271, 1275}, {1278, 1280, 1282, 1285, 1287, 1280, 1285}, {1290, 1296, 1302, 1310, 1316, 1324, 1332, 1342, 1348, 1356, 1364, 1373, 0}, {1382, 1388, 1395, 1403, 1409, 1417, 1425, 1435, 1348, 1441, 1449, 1459, 0}, {1468, 1472, 1477, 1482, 1486, 1491, 1496, 1501, 1505, 1511, 1517, 1521, 0}, {1468, 1472, 1477, 1482, 1486, 1491, 1496, 1501, 1505, 1511, 1517, 1521, 0}, 2, 1, 11, 3, {681,692,0,0,0,0,0,0,0,0,0,0,0,0},{703,721,0,0,0,0,0,0,0,0},{266,0,0,0,0,0,0,0,0,0,0,0},{271,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {673, 49, 52, {1525, 1533, 1540, 1548, 1555, 1563, 1570}, {1578, 1583, 1587, 1591, 1595, 1599, 1603}, {1285, 1608, 1610, 1612, 1610, 1614, 1616}, {1618, 1625, 1633, 1639, 1645, 1649, 1654, 1659, 1666, 1676, 1684, 1693, 0}, {1618, 1625, 1633, 1639, 1645, 1649, 1654, 1659, 1666, 1676, 1684, 1693, 0}, {1702, 1707, 1712, 1717, 1645, 1722, 868, 1727, 1732, 1737, 887, 1742, 0}, {1702, 1707, 1712, 1717, 1645, 1722, 868, 1727, 1732, 1737, 887, 1742, 0}, 2, 1, 55, 3, {744,755,417,764,0,0,0,0,0,0,0,0,0,0},{721,0,0,0,0,0,0,0,0,0},{71,266,0,0,0,0,0,0,0,0,0,0},{89,271,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {673, 57, 63, {1747, 1755, 1762, 1771, 1780, 1791, 1799}, {1807, 1810, 1813, 1816, 1819, 1822, 1825}, {1285, 1608, 1828, 1608, 1828, 1614, 1285}, {1830, 1837, 1845, 1851, 1857, 1861, 1866, 1871, 1878, 1888, 1896, 1905, 0}, {1830, 1837, 1845, 1851, 1857, 1861, 1866, 1871, 1878, 1888, 1896, 1905, 0}, {1914, 1918, 1922, 1927, 1857, 1931, 1935, 1939, 1943, 1947, 1951, 1955, 0}, {1914, 1918, 1922, 1927, 1857, 1931, 1935, 1939, 1943, 1947, 1951, 1955, 0}, 2, 1, 11, 3, {681,775,417,784,0,0,0,0,0,0,0,0,0,0},{798,721,817,0,0,0,0,0,0,0},{71,830,0,0,0,0,0,0,0,0,0,0},{89,842,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 70, 77, {1959, 1974, 1989, 2000, 2015, 2028, 2047}, {2062, 2069, 2076, 2083, 2090, 2097, 2104}, {2111, 2114, 2117, 2117, 2120, 2120, 2123}, {2126, 2147, 2170, 2185, 2202, 2213, 2228, 2243, 2262, 2285, 2304, 2323, 0}, {2344, 2365, 2388, 2403, 2420, 2431, 2446, 2461, 2480, 2503, 2522, 2541, 0}, {2562, 2569, 2576, 2583, 2590, 2597, 2606, 2615, 2622, 2629, 2636, 2643, 0}, {2562, 2569, 2576, 2583, 2590, 2597, 2606, 2615, 2622, 2629, 2636, 2643, 0}, 2, 1, 1, 3, {295,18,857,9,864,417,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{906,62,266,71,0,0,0,0,0,0,0,0},{914,77,271,89,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {925, 49, 52, {2650, 2657, 2664, 2672, 2682, 2691, 2698}, {2707, 2711, 2715, 2719, 2723, 2727, 2731}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {1914, 1918, 2791, 1927, 2760, 1931, 1935, 1939, 1943, 2795, 1951, 2799, 0}, {1914, 1918, 2791, 1927, 2760, 1931, 1935, 1939, 1943, 2795, 1951, 2799, 0}, 0, 0, 1, 3, {932,941,948,957,460,417,968,0,0,0,0,0,0,0},{978,997,1010,1029,0,0,0,0,0,0},{906,62,266,71,0,0,0,0,0,0,0,0},{914,77,271,89,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {673, 84, 88, {2997, 3009, 3021, 3031, 3045, 3055, 3067}, {3078, 3081, 3084, 3087, 3090, 3093, 3096}, {1285, 1608, 1610, 3099, 1610, 1280, 1616}, {3101, 3110, 3119, 3129, 3138, 3147, 3156, 3166, 3173, 3181, 3189, 3199, 0}, {3208, 3219, 3230, 3242, 3253, 3264, 3275, 3287, 3296, 3306, 3316, 3328, 0}, {3339, 3345, 3351, 3358, 3364, 3370, 3376, 3383, 3387, 3392, 3397, 3404, 0}, {3339, 3345, 3351, 3358, 3364, 3370, 3376, 3383, 3387, 3392, 3397, 3404, 0}, 2, 1, 11, 11, {1208,0,0,0,0,0,0,0,0,0,0,0,0,0},{703,721,0,0,0,0,0,0,0,0},{1146,0,0,0,0,0,0,0,0,0,0,0},{1165,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 49, 52, {3410, 3419, 3425, 3431, 3440, 3446, 3455}, {3462, 2863, 1712, 3467, 3472, 3477, 3482}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {3487, 3495, 3504, 3509, 3515, 3519, 3524, 3532, 3538, 3548, 712, 3556, 0}, {3487, 3495, 3504, 3509, 3515, 3519, 3524, 3532, 3538, 3548, 712, 3556, 0}, {3566, 3572, 3504, 3579, 3515, 3519, 3584, 3532, 2986, 882, 887, 3590, 0}, {3566, 3572, 3504, 3579, 3515, 3519, 3584, 3532, 2986, 882, 887, 3590, 0}, 2, 1, 1, 3, {18,9,775,755,417,0,0,0,0,0,0,0,0,0},{1217,894,1234,0,0,0,0,0,0,0},{71,266,1151,1243,1253,0,0,0,0,0,0,0},{89,271,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {1, 92, 105, {3596, 3614, 3628, 3646, 3664, 3682, 3698}, {3712, 3724, 3736, 3748, 3760, 3772, 3784}, {3791, 3796, 3801, 3806, 3811, 3816, 3821}, {3826, 3837, 3850, 3857, 3868, 3875, 3884, 3893, 3906, 3919, 3934, 3947, 0}, {3826, 3837, 3850, 3857, 3868, 3875, 3884, 3893, 3906, 3919, 3934, 3947, 0}, {3958, 3967, 3850, 3976, 3868, 3875, 3884, 3985, 3994, 4003, 4012, 4021, 0}, {3958, 3967, 3850, 3976, 3868, 3875, 3884, 3985, 3994, 4003, 4012, 4021, 0}, 0, 0, 1, 3, {18,1261,9,29,755,744,1274,417,1287,1304,0,0,0,0},{1314,1261,1332,1354,1287,0,0,0,0,0},{71,62,0,0,0,0,0,0,0,0,0,0},{89,77,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {1375, 116, 120, {4030, 4040, 4048, 4053, 4060, 4073, 4081}, {2894, 4089, 3099, 4091, 4095, 1280, 4098}, {2894, 4089, 3099, 4102, 4095, 1280, 4102}, {4105, 4113, 4122, 4131, 4140, 4147, 4155, 4163, 4173, 4184, 1684, 1693, 0}, {4105, 4113, 4122, 4131, 4140, 4147, 4155, 4163, 4173, 4184, 1684, 1693, 0}, {1702, 857, 4193, 4200, 4206, 4212, 4218, 1727, 4224, 1737, 887, 1742, 0}, {1702, 857, 4193, 4200, 4206, 4212, 4218, 1727, 4224, 1737, 887, 1742, 0}, 2, 1, 124, 3, {1383,1397,0,0,0,0,0,0,0,0,0,0,0,0},{1410,1430,0,0,0,0,0,0,0,0},{266,0,0,0,0,0,0,0,0,0,0,0},{271,0,0,0,0,0,0,0,0},{1444,0,0,0,0,0,0,0}}, + {673, 127, 132, {4231, 4242, 4253, 4267, 4281, 4293, 4305}, {4317, 4322, 4328, 4334, 4340, 4345, 4351}, {1285, 1608, 4356, 1608, 1614, 1614, 1616}, {4359, 4367, 3504, 4376, 4383, 4388, 4395, 4402, 1666, 4184, 4410, 4420, 0}, {4359, 4367, 3504, 4376, 4383, 4388, 4395, 4402, 1666, 4184, 4410, 4420, 0}, {1702, 1707, 1712, 1717, 4383, 4212, 4218, 4429, 1732, 1737, 4436, 892, 0}, {1702, 1707, 1712, 1717, 4383, 4212, 4218, 4429, 1732, 1737, 4436, 892, 0}, 2, 1, 11, 3, {1208,1455,0,0,0,0,0,0,0,0,0,0,0,0},{798,721,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 49, 52, {4442, 4451, 4459, 4468, 4479, 4488, 4497}, {4504, 4508, 4512, 4516, 4520, 4524, 4528}, {1828, 1616, 1608, 1608, 4532, 2894, 1285}, {4534, 4542, 2910, 4551, 4558, 4565, 4572, 2933, 4579, 4589, 712, 4597, 0}, {4534, 4542, 2910, 4551, 4558, 4565, 4572, 2933, 4579, 4589, 712, 4597, 0}, {4606, 4610, 4512, 4614, 4618, 4622, 4626, 4630, 4634, 4638, 4642, 4646, 0}, {4606, 4610, 4512, 4614, 4618, 4622, 4626, 4630, 4634, 4638, 4642, 4646, 0}, 2, 1, 1, 3, {18,864,9,1467,857,0,0,0,0,0,0,0,0,0},{1217,1475,894,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {370, 137, 144, {4650, 4660, 4670, 4680, 4690, 4700, 4710}, {1016, 4720, 4724, 4728, 4732, 4736, 4740}, {1016, 4720, 4724, 4728, 4732, 4736, 4740}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, {4744, 4746, 4748, 4750, 4752, 4754, 4756, 4758, 4760, 4762, 4765, 4768, 0}, {4744, 4746, 4748, 4750, 4752, 4754, 4756, 4758, 4760, 4762, 4765, 4768, 0}, 0, 0, 1, 3, {406,460,439,379,417,0,0,0,0,0,0,0,0,0},{469,1484,1508,1535,1564,1588,1617,1637,0,0},{266,71,583,591,0,0,0,0,0,0,0,0},{271,89,600,611,0,0,0,0,0},{623,1662,652,0,0,0,0,0}}, + {1680, 49, 52, {4771, 4781, 4791, 4801, 4811, 4821, 4831}, {4841, 4845, 4849, 4853, 4857, 4861, 4865}, {4841, 4845, 4849, 4853, 4857, 4861, 4865}, {4869, 4874, 4879, 4884, 4889, 4894, 4899, 4904, 4909, 4914, 4920, 4926, 0}, {4869, 4874, 4879, 4884, 4889, 4894, 4899, 4904, 4909, 4914, 4920, 4926, 0}, {4869, 4874, 4879, 4884, 4889, 4894, 4899, 4904, 4909, 4914, 4920, 4926, 0}, {4869, 4874, 4879, 4884, 4889, 4894, 4899, 4904, 4909, 4914, 4920, 4926, 0}, 0, 0, 55, 3, {417,1690,446,388,0,0,0,0,0,0,0,0,0,0},{1699,1728,1752,1779,1801,1832,1858,1889,1915,1942},{583,591,266,71,0,0,0,0,0,0,0,0},{600,611,271,89,0,0,0,0,0},{1964,1981,2000,0,0,0,0,0}}, + {109, 151, 156, {4932, 4939, 4947, 4955, 4964, 4974, 4982}, {4991, 3081, 4994, 4997, 5000, 5003, 5006}, {5009, 1608, 1828, 2735, 1828, 2894, 5009}, {5011, 5019, 5028, 1639, 5034, 1649, 1654, 5038, 1666, 1676, 1684, 1693, 0}, {5011, 5019, 5028, 1639, 5034, 1649, 1654, 5038, 1666, 1676, 1684, 1693, 0}, {1702, 1707, 5047, 1717, 5034, 1722, 868, 1727, 1732, 1737, 887, 1742, 0}, {1702, 1707, 5047, 1717, 5034, 1722, 868, 1727, 1732, 1737, 887, 1742, 0}, 2, 1, 55, 3, {2015,1062,755,9,775,2024,417,0,0,0,0,0,0,0},{1217,1475,894,1234,0,0,0,0,0,0},{71,266,1146,2036,2048,0,0,0,0,0,0,0},{89,271,2060,2075,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {2090, 151, 156, {1525, 1533, 1540, 1548, 1555, 1563, 1570}, {5052, 5058, 5063, 5068, 5073, 5078, 5083}, {1285, 1608, 1610, 1612, 1610, 1614, 1616}, {1618, 1625, 3504, 1639, 3515, 1649, 1654, 1659, 1666, 1676, 1684, 4420, 0}, {1618, 1625, 3504, 1639, 3515, 1649, 1654, 1659, 1666, 1676, 1684, 4420, 0}, {5089, 4610, 4512, 4614, 3515, 5093, 5097, 5101, 5105, 5109, 4642, 5113, 0}, {5089, 4610, 4512, 4614, 3515, 5093, 5097, 5101, 5105, 5109, 4642, 5113, 0}, 2, 1, 11, 11, {681,1455,0,0,0,0,0,0,0,0,0,0,0,0},{703,721,0,0,0,0,0,0,0,0},{1151,0,0,0,0,0,0,0,0,0,0,0},{1173,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 49, 52, {5117, 5127, 5141, 5148, 5155, 5164, 1247}, {5172, 5179, 5184, 5188, 5193, 5198, 5202}, {1278, 1280, 2735, 5207, 5210, 1280, 1285}, {5212, 5221, 5226, 5233, 1645, 5243, 5252, 5259, 5269, 5279, 1364, 5292, 0}, {5302, 5311, 5318, 5324, 5333, 5338, 5346, 5352, 5361, 5371, 5385, 5395, 0}, {5403, 5407, 4512, 5411, 1645, 5415, 5419, 5423, 5427, 5431, 1517, 5436, 0}, {5403, 5407, 4512, 5411, 1645, 5415, 5419, 5423, 5427, 5431, 1517, 5436, 0}, 2, 1, 11, 3, {681,0,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {1042, 49, 52, {2803, 5440, 5454, 5467, 5480, 5493, 2850}, {4504, 5505, 5509, 5513, 5517, 5521, 5525}, {1828, 1285, 1610, 5530, 5530, 1285, 1285}, {5532, 5540, 5550, 666, 5557, 5562, 5568, 2933, 5574, 5583, 5591, 5600, 0}, {5532, 5540, 5550, 666, 5557, 5562, 5568, 2933, 5574, 5583, 5591, 5600, 0}, {5089, 5609, 4512, 5613, 3515, 5093, 5097, 4630, 4634, 5617, 4642, 5621, 0}, {5089, 5609, 4512, 5613, 3515, 5093, 5097, 4630, 4634, 5617, 4642, 5621, 0}, 0, 0, 1, 3, {18,9,295,857,755,744,1062,2015,775,681,2024,2098,1208,417},{1069,1124,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{1193,0,0,0,0,0,0,0}}, + {673, 49, 52, {5625, 5634, 3425, 5644, 5652, 5660, 5669}, {5675, 5678, 3081, 5682, 5685, 5689, 1275}, {1828, 4532, 1608, 1608, 4532, 2894, 1285}, {5692, 5700, 3504, 5707, 5714, 5719, 5729, 5737, 5743, 5753, 1684, 1693, 0}, {5692, 5700, 3504, 5707, 5714, 5719, 5729, 5737, 5743, 5753, 1684, 1693, 0}, {5761, 5768, 3504, 3579, 5714, 5774, 5781, 5737, 5786, 882, 887, 1742, 0}, {5761, 5768, 3504, 3579, 5714, 5774, 5781, 5737, 5786, 882, 887, 1742, 0}, 2, 1, 55, 3, {744,0,0,0,0,0,0,0,0,0,0,0,0,0},{2105,2134,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 151, 156, {5792, 5802, 5807, 5814, 5823, 5827, 5834}, {5845, 2863, 1712, 5850, 5823, 5855, 5860}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {5866, 5875, 5885, 5892, 3515, 5900, 5906, 1659, 5912, 5923, 5933, 5943, 0}, {5866, 5875, 5885, 5892, 3515, 5900, 5906, 1659, 5912, 5923, 5933, 5943, 0}, {5953, 1707, 1712, 1717, 3515, 5958, 5963, 1727, 2986, 882, 887, 1742, 0}, {5953, 1707, 1712, 1717, 3515, 5958, 5963, 1727, 2986, 882, 887, 1742, 0}, 0, 1, 11, 3, {681,304,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 161, 166, {5968, 5991, 211, 6014, 6025, 6040, 6055}, {6070, 285, 290, 295, 300, 305, 310}, {6075, 6078, 6075, 6081, 6084, 6078, 6081}, {6087, 6100, 360, 6115, 380, 6128, 6137, 401, 6146, 6163, 6178, 6191, 0}, {6206, 6219, 6234, 6245, 6258, 6265, 6274, 6283, 6298, 6315, 6330, 6343, 0}, {6358, 6366, 360, 6376, 380, 6128, 6137, 6384, 6392, 6402, 6410, 6420, 0}, {6358, 6366, 360, 6376, 380, 6128, 6137, 6384, 6392, 6402, 6410, 6420, 0}, 0, 1, 11, 3, {681,775,2098,744,9,0,0,0,0,0,0,0,0,0},{199,180,0,0,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {673, 49, 52, {6428, 6437, 6449, 6456, 6464, 6474, 6480}, {6487, 6491, 6495, 6499, 6503, 6508, 6512}, {6516, 6518, 6520, 6522, 6524, 6518, 6522}, {6527, 6537, 6546, 6554, 6562, 6570, 6577, 6584, 6592, 1364, 6598, 6606, 0}, {6615, 6625, 6634, 6642, 6650, 6658, 6665, 6672, 6681, 5385, 6687, 6697, 0}, {6706, 6710, 6715, 6720, 6724, 5419, 1501, 6728, 6732, 1517, 6736, 1521, 0}, {6706, 6710, 6715, 6720, 6724, 5419, 1501, 6728, 6732, 1517, 6736, 1521, 0}, 0, 1, 11, 3, {2151,2161,2169,2181,2193,2203,2213,417,0,0,0,0,0,0},{2225,2239,2254,0,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {673, 49, 52, {6740, 6748, 6757, 6764, 6771, 6780, 1247}, {1254, 1257, 6787, 1264, 6790, 6794, 1275}, {6516, 6518, 6520, 6522, 6797, 6518, 6522}, {4105, 4113, 6800, 4376, 6806, 6811, 6816, 1659, 1666, 4184, 1684, 1693, 0}, {6821, 6830, 5318, 6840, 6848, 6854, 6860, 6866, 6874, 6884, 6893, 6902, 0}, {5089, 4610, 4512, 4614, 6806, 6811, 6816, 5101, 5105, 5109, 4642, 6911, 0}, {5089, 4610, 4512, 4614, 6806, 6811, 6816, 5101, 5105, 5109, 4642, 6911, 0}, 2, 1, 11, 3, {681,692,0,0,0,0,0,0,0,0,0,0,0,0},{798,721,0,0,0,0,0,0,0,0},{266,0,0,0,0,0,0,0,0,0,0,0},{271,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 171, 183, {6915, 6922, 6931, 6940, 6952, 6960, 6969}, {6979, 6983, 2791, 6988, 6993, 6997, 7001}, {1828, 4089, 1608, 1608, 7005, 1280, 1285}, {7007, 7013, 7020, 7025, 7031, 7035, 7043, 7050, 7056, 7064, 7070, 7078, 0}, {7086, 7092, 3504, 7099, 1645, 7105, 7113, 7120, 7126, 7134, 7140, 7148, 0}, {1914, 7156, 2791, 7160, 7031, 7164, 7168, 7172, 7001, 7176, 7180, 7185, 0}, {1914, 7156, 2791, 7160, 7031, 7164, 7168, 7172, 7001, 7176, 7180, 7185, 0}, 0, 1, 11, 3, {1208,304,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {2274, 194, 197, {7189, 7197, 7205, 1548, 1555, 1563, 7212}, {7220, 7225, 7230, 1591, 7234, 1599, 7239}, {1285, 1608, 1610, 1612, 1610, 1614, 1616}, {5011, 5019, 3504, 1639, 1645, 1649, 1654, 7244, 1666, 1676, 1684, 1693, 0}, {5011, 5019, 3504, 1639, 1645, 1649, 1654, 7244, 1666, 1676, 1684, 1693, 0}, {1702, 1707, 3504, 1717, 1645, 1649, 1654, 1727, 1732, 1737, 887, 1742, 0}, {1702, 1707, 3504, 1717, 1645, 1649, 1654, 1727, 1732, 1737, 887, 1742, 0}, 2, 1, 55, 3, {417,1690,0,0,0,0,0,0,0,0,0,0,0,0},{2287,2305,0,0,0,0,0,0,0,0},{71,266,2328,0,0,0,0,0,0,0,0,0},{89,271,2338,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 0, 0, {0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {4744, 4746, 4748, 4750, 4752, 4754, 4756, 4758, 4760, 4762, 4765, 4768, 0}, {4744, 4746, 4748, 4750, 4752, 4754, 4756, 4758, 4760, 4762, 4765, 4768, 0}, 0, 0, 1, 3, {295,857,9,18,304,2351,417,0,0,0,0,0,0,0},{894,2363,2379,0,0,0,0,0,0,0},{266,71,906,62,0,0,0,0,0,0,0,0},{271,89,914,77,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {1, 200, 205, {7252, 7258, 7268, 7274, 7285, 7295, 7300}, {7310, 7314, 7318, 7322, 7327, 7331, 7335}, {1280, 1280, 1285, 7339, 1280, 5210, 5210}, {7342, 7347, 7354, 7359, 7365, 7372, 7380, 7387, 7396, 7403, 7408, 7415, 0}, {7342, 7347, 7354, 7359, 7365, 7372, 7380, 7387, 7396, 7403, 7408, 7415, 0}, {7423, 7427, 2791, 7432, 2760, 7436, 7440, 7444, 7449, 7453, 7457, 7461, 0}, {7423, 7427, 2791, 7432, 2760, 7436, 7440, 7444, 7449, 7453, 7457, 7461, 0}, 0, 1, 11, 3, {2421,304,0,0,0,0,0,0,0,0,0,0,0,0},{2431,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {1, 49, 52, {7465, 7476, 7489, 7498, 7505, 7518, 7527}, {7465, 7476, 7489, 7498, 7505, 7518, 7527}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {7536, 7547, 7558, 7567, 7578, 7585, 7592, 7605, 7614, 7625, 7638, 7649, 0}, {7536, 7547, 7558, 7567, 7578, 7585, 7592, 7605, 7614, 7625, 7638, 7649, 0}, {7536, 7547, 7558, 7567, 7578, 7585, 7592, 7605, 7614, 7625, 7638, 7649, 0}, {7536, 7547, 7558, 7567, 7578, 7585, 7592, 7605, 7614, 7625, 7638, 7649, 0}, 0, 0, 1, 3, {18,9,417,0,0,0,0,0,0,0,0,0,0,0},{2448,42,0,0,0,0,0,0,0,0},{906,62,266,71,0,0,0,0,0,0,0,0},{914,77,271,89,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {109, 49, 52, {7660, 7667, 7673, 7680, 7685, 7691, 7697}, {7703, 7707, 7711, 7715, 7719, 7723, 7727}, {1608, 1285, 1285, 7731, 3099, 2892, 1285}, {7733, 7741, 7750, 1851, 7756, 1861, 1866, 7760, 1878, 1888, 1896, 7768, 0}, {7733, 7741, 7750, 1851, 7756, 1861, 1866, 7760, 1878, 1888, 1896, 7768, 0}, {1914, 1918, 2791, 1927, 7756, 1931, 1935, 7777, 1943, 1947, 1951, 7781, 0}, {1914, 1918, 2791, 1927, 7756, 1931, 1935, 7777, 1943, 1947, 1951, 7781, 0}, 0, 0, 1, 11, {18,304,0,0,0,0,0,0,0,0,0,0,0,0},{2462,894,0,0,0,0,0,0,0,0},{1151,0,0,0,0,0,0,0,0,0,0,0},{1173,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 209, 214, {7785, 7798, 7817, 7834, 7847, 7860, 7877}, {280, 285, 290, 295, 300, 305, 310}, {7890, 6078, 6075, 6081, 6084, 6078, 6081}, {7893, 7906, 7917, 7934, 7949, 7964, 7979, 7992, 8007, 8024, 8039, 8056, 0}, {8071, 8082, 8095, 8110, 8123, 8136, 8149, 8160, 8173, 8188, 8201, 8220, 0}, {8233, 8240, 8247, 8254, 8261, 8268, 8275, 8282, 8289, 8296, 8303, 8310, 0}, {8233, 8240, 8247, 8254, 8261, 8268, 8275, 8282, 8289, 8296, 8303, 8310, 0}, 0, 1, 11, 3, {681,775,417,0,0,0,0,0,0,0,0,0,0,0},{2481,0,0,0,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{2499,0,0,0,0,0,0,0}}, + {109, 49, 52, {8317, 8332, 8353, 8368, 8381, 8394, 7877}, {280, 285, 8409, 295, 8414, 305, 310}, {315, 318, 8419, 324, 327, 318, 324}, {8422, 8439, 8448, 8463, 380, 8480, 8495, 8508, 8523, 8540, 8561, 8578, 0}, {8593, 8610, 8623, 8640, 6258, 8659, 8674, 8687, 8700, 8715, 8738, 8757, 0}, {8770, 8240, 8777, 8784, 380, 8791, 8798, 8805, 8289, 8812, 8819, 8826, 0}, {8770, 8240, 8777, 8784, 380, 8791, 8798, 8805, 8289, 8812, 8819, 8826, 0}, 0, 1, 11, 3, {775,0,0,0,0,0,0,0,0,0,0,0,0,0},{894,0,0,0,0,0,0,0,0,0},{71,266,0,0,0,0,0,0,0,0,0,0},{89,271,0,0,0,0,0,0,0},{2515,0,0,0,0,0,0,0}}, + {673, 39, 219, {8833, 8841, 8852, 8858, 8864, 8873, 1247}, {8879, 5179, 5073, 8884, 8889, 8895, 5202}, {6516, 6518, 8900, 6522, 6524, 6518, 6522}, {1618, 1625, 6800, 1639, 1645, 8902, 8908, 8914, 1666, 1676, 1684, 1693, 0}, {1618, 1625, 6800, 1639, 1645, 8902, 8908, 8914, 1666, 1676, 1684, 1693, 0}, {1702, 1707, 1712, 1717, 1645, 1722, 868, 8921, 1732, 1737, 887, 1742, 0}, {1702, 1707, 1712, 1717, 1645, 1722, 868, 8921, 1732, 1737, 887, 1742, 0}, 0, 1, 124, 3, {2529,1455,0,0,0,0,0,0,0,0,0,0,0,0},{2541,2561,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {673, 49, 52, {8926, 8937, 8948, 8959, 8970, 8981, 8987}, {1280, 7005, 1610, 3099, 1278, 7731, 1616}, {1280, 7005, 1610, 3099, 1278, 7731, 1616}, {8996, 9004, 9013, 9020, 3515, 9027, 9033, 1659, 1666, 9039, 1684, 9048, 0}, {8996, 9004, 9013, 9020, 3515, 9027, 9033, 1659, 1666, 9039, 1684, 9048, 0}, {9058, 9063, 9013, 4614, 3515, 9027, 9033, 5101, 9069, 5109, 4642, 9074, 0}, {9058, 9063, 9013, 4614, 3515, 9027, 9033, 5101, 9069, 5109, 4642, 9074, 0}, 2, 1, 11, 3, {681,1455,0,0,0,0,0,0,0,0,0,0,0,0},{798,721,0,0,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{2575,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {673, 224, 234, {9079, 9090, 9100, 9109, 9120, 9132, 9143}, {9153, 9161, 9168, 9174, 9182, 9191, 9199}, {1285, 1280, 1612, 1610, 5210, 1280, 1285}, {9206, 9216, 1633, 9227, 9236, 9242, 9250, 9258, 9266, 9277, 9286, 9296, 0}, {9206, 9216, 1633, 9227, 9236, 9242, 9250, 9258, 9266, 9277, 9286, 9296, 0}, {3566, 857, 1633, 1717, 9236, 9306, 9312, 1727, 2986, 1737, 887, 1742, 0}, {3566, 857, 1633, 1717, 9236, 9306, 9312, 1727, 2986, 1737, 887, 1742, 0}, 0, 1, 11, 3, {681,2583,0,0,0,0,0,0,0,0,0,0,0,0},{2603,2630,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2651,0,0,0,0,0,0,0}}, + {925, 241, 252, {9318, 9330, 9342, 9354, 9368, 9383, 9396}, {9410, 9413, 9416, 9419, 9422, 9425, 6790}, {1285, 1280, 9428, 1610, 3099, 1280, 9430}, {9433, 9440, 9448, 9454, 9463, 9472, 9482, 9488, 9499, 9509, 9516, 9526, 0}, {9534, 9541, 9549, 9554, 9565, 9575, 9585, 9592, 9604, 9613, 9620, 9631, 0}, {9641, 9647, 9652, 9657, 9662, 9667, 9674, 9680, 9686, 9692, 9698, 9705, 0}, {9641, 9647, 9652, 9657, 9662, 9667, 9674, 9680, 9686, 9692, 9698, 9705, 0}, 2, 1, 55, 3, {417,0,0,0,0,0,0,0,0,0,0,0,0,0},{2667,2695,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2717,0,0,0,0,0,0,0}}, + {1, 259, 271, {9712, 9727, 9742, 9757, 9774, 9793, 9804}, {9815, 9822, 9829, 9836, 9843, 9850, 9857}, {0, 0, 0, 0, 0, 0, 0}, {9864, 9875, 9888, 9897, 9908, 9915, 9922, 9929, 9942, 9957, 9970, 9981, 0}, {9864, 9875, 9888, 9897, 9908, 9915, 9922, 9929, 9942, 9957, 9970, 9981, 0}, {9994, 10001, 10008, 10015, 9908, 9915, 9922, 10022, 10029, 10036, 10043, 10050, 0}, {9994, 10001, 10008, 10015, 9908, 9915, 9922, 10022, 10029, 10036, 10043, 10050, 0}, 0, 1, 11, 3, {681,775,2098,744,9,0,0,0,0,0,0,0,0,0},{2727,2745,0,0,0,0,0,0,0,0},{71,266,0,0,0,0,0,0,0,0,0,0},{89,271,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 283, 290, {10057, 10070, 10083, 10099, 10116, 10131, 10140}, {10057, 10070, 10083, 10099, 10116, 10131, 10140}, {10149, 10152, 10155, 10158, 10161, 10164, 10167}, {10170, 10183, 10194, 10203, 10214, 10219, 10228, 10239, 10246, 10261, 10272, 10285, 0}, {10298, 10313, 10194, 10203, 10326, 10219, 10333, 10239, 10246, 10261, 10272, 10285, 0}, {10170, 10183, 10194, 10203, 10214, 10219, 10228, 10239, 10246, 10261, 10272, 10285, 0}, {10170, 10183, 10194, 10203, 10214, 10219, 10228, 10239, 10246, 10261, 10272, 10285, 0}, 0, 6, 1, 3, {18,9,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{62,71,0,0,0,0,0,0,0,0,0,0},{77,89,0,0,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {1, 297, 300, {10346, 10359, 10369, 10378, 10388, 10399, 10410}, {10422, 10425, 10430, 10435, 10440, 10445, 10450}, {10422, 10455, 10458, 10461, 10464, 10467, 10470}, {10473, 10482, 10491, 10500, 10509, 10518, 10527, 10536, 10545, 10554, 10564, 10574, 0}, {10584, 10593, 10602, 10611, 10620, 10629, 10638, 10647, 10656, 10665, 10675, 10685, 0}, {10695, 10701, 10707, 10713, 10719, 10725, 10731, 10737, 10743, 10749, 10756, 10763, 0}, {10695, 10701, 10707, 10713, 10719, 10725, 10731, 10737, 10743, 10749, 10756, 10763, 0}, 0, 1, 1, 3, {18,9,755,744,417,0,0,0,0,0,0,0,0,0},{1261,0,0,0,0,0,0,0,0,0},{906,62,266,71,0,0,0,0,0,0,0,0},{914,77,271,89,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 303, 308, {10770, 10783, 10804, 10823, 10844, 10863, 10876}, {10887, 10894, 10901, 10908, 10915, 10922, 10929}, {10936, 10939, 10939, 10942, 10945, 10948, 10951}, {10954, 10969, 10984, 10993, 11004, 11015, 11028, 11041, 11056, 11075, 11094, 11111, 0}, {11130, 11147, 11164, 11175, 11188, 11201, 11216, 11231, 11248, 11269, 11290, 11309, 0}, {11330, 11337, 11344, 11351, 11358, 11365, 11372, 11379, 11386, 11393, 11400, 11407, 0}, {11330, 11337, 11344, 11351, 11358, 11365, 11372, 11379, 11386, 11393, 11400, 11407, 0}, 0, 1, 11, 3, {681,775,2764,18,2774,864,417,0,0,0,0,0,0,0},{1029,876,2462,1261,2785,2796,2808,2825,0,0},{71,266,0,0,0,0,0,0,0,0,0,0},{89,271,0,0,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {109, 49, 52, {11414, 11420, 11434, 11457, 11471, 11487, 11494}, {11503, 11506, 11511, 11517, 11521, 11526, 11529}, {4756, 4744, 4746, 4748, 4750, 4752, 4754}, {11533, 11540, 7354, 11547, 2760, 11553, 11559, 11565, 11572, 11581, 11589, 11596, 0}, {11603, 11610, 11617, 11622, 11628, 11632, 11637, 11642, 11649, 11658, 11666, 11673, 0}, {11680, 5609, 4512, 4614, 11628, 11684, 11688, 11692, 11696, 5109, 11700, 11704, 0}, {11680, 5609, 4512, 4614, 11628, 11684, 11688, 11692, 11696, 5109, 11700, 11704, 0}, 0, 1, 11, 3, {681,304,0,0,0,0,0,0,0,0,0,0,0,0},{2843,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {925, 49, 52, {11708, 11716, 11727, 11737, 11748, 11757, 11766}, {11776, 11780, 11784, 11788, 11792, 11796, 11800}, {11804, 9428, 9428, 9428, 1612, 1612, 1616}, {11806, 11816, 11824, 11832, 11840, 11848, 11855, 11863, 11871, 11878, 11884, 11891, 0}, {11806, 11899, 11907, 11915, 11923, 11931, 11938, 11946, 11954, 11961, 11967, 11974, 0}, {11982, 11987, 1712, 11992, 11997, 12002, 12007, 12012, 12017, 12022, 12027, 12032, 0}, {11982, 11987, 1712, 11992, 11997, 12002, 12007, 12012, 12017, 12022, 12027, 12032, 0}, 2, 1, 1, 3, {406,2861,0,0,0,0,0,0,0,0,0,0,0,0},{2872,2899,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2920,0,0,0,0,0,0,0}}, + {673, 313, 324, {12037, 12047, 12058, 12065, 12072, 12082, 1247}, {12088, 12092, 12097, 12101, 12105, 12110, 12114}, {6516, 6518, 12118, 6522, 6797, 6518, 6522}, {1618, 1625, 12120, 12126, 12132, 8902, 8908, 12137, 1666, 1676, 12144, 1693, 0}, {12153, 12161, 12170, 12177, 12184, 12189, 12196, 12203, 6874, 12211, 12219, 6902, 0}, {5089, 4610, 12228, 4614, 12233, 5093, 5097, 12237, 5105, 5109, 12241, 6911, 0}, {5089, 4610, 12228, 4614, 12233, 5093, 5097, 12237, 5105, 5109, 12241, 6911, 0}, 2, 1, 11, 3, {1208,0,0,0,0,0,0,0,0,0,0,0,0,0},{798,721,0,0,0,0,0,0,0,0},{2939,0,0,0,0,0,0,0,0,0,0,0},{271,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 335, 349, {12245, 190, 211, 6014, 12258, 12275, 12286}, {12299, 12307, 12315, 12323, 12331, 12339, 12347}, {315, 318, 321, 324, 327, 318, 324}, {12355, 343, 360, 369, 12370, 12377, 12386, 401, 414, 433, 450, 465, 0}, {12355, 343, 360, 369, 12370, 12377, 12386, 401, 414, 433, 450, 465, 0}, {12395, 12403, 12411, 6376, 12370, 12419, 12427, 6384, 12435, 6402, 12445, 6420, 0}, {12395, 12403, 12411, 6376, 12370, 12419, 12427, 6384, 12435, 6402, 12445, 6420, 0}, 0, 1, 11, 3, {2953,0,0,0,0,0,0,0,0,0,0,0,0,0},{2462,1261,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2963,0,0,0,0,0,0,0}}, + {925, 0, 0, {12455, 12463, 12472, 12481, 12490, 12497, 12507}, {12516, 12520, 12524, 12528, 12532, 12535, 12539}, {0, 0, 0, 0, 0, 0, 0}, {12543, 12554, 12562, 12572, 12578, 12590, 12599, 12605, 12611, 12619, 12628, 12640, 0}, {12543, 12554, 12562, 12572, 12578, 12590, 12599, 12605, 12611, 12619, 12628, 12640, 0}, {12648, 12652, 12656, 12660, 12664, 1914, 12668, 12672, 12676, 12680, 12684, 12688, 0}, {12648, 12652, 12656, 12660, 12664, 1914, 12668, 12672, 12676, 12680, 12684, 12688, 0}, 0, 0, 55, 3, {417,2861,0,0,0,0,0,0,0,0,0,0,0,0},{2979,2997,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2717,0,0,0,0,0,0,0}}, + {925, 0, 0, {12692, 12698, 12711, 12722, 12733, 12742, 12754}, {12516, 12764, 12768, 12772, 12532, 12776, 12780}, {0, 0, 0, 0, 0, 0, 0}, {12784, 12792, 12804, 12816, 12828, 12838, 12850, 12859, 12867, 12875, 12885, 12892, 0}, {12784, 12792, 12804, 12816, 12828, 12838, 12850, 12859, 12867, 12875, 12885, 12892, 0}, {2707, 12906, 12910, 12914, 12918, 12922, 12926, 12930, 12934, 12938, 12942, 12946, 0}, {2707, 12906, 12910, 12914, 12918, 12922, 12926, 12930, 12934, 12938, 12942, 12946, 0}, 0, 0, 55, 3, {417,2861,0,0,0,0,0,0,0,0,0,0,0,0},{2979,2997,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2717,0,0,0,0,0,0,0}}, + {925, 0, 0, {12950, 12957, 12472, 12968, 12490, 12977, 12988}, {12688, 12998, 12524, 12528, 12532, 13002, 13006}, {0, 0, 0, 0, 0, 0, 0}, {13010, 13020, 13029, 13037, 13046, 13059, 13071, 13078, 13085, 13092, 13102, 13114, 0}, {13010, 13020, 13029, 13037, 13046, 13059, 13071, 13078, 13085, 13092, 13102, 13114, 0}, {13127, 12776, 13131, 13135, 12664, 13139, 13143, 12672, 13147, 13151, 13155, 13159, 0}, {13127, 12776, 13131, 13135, 12664, 13139, 13143, 12672, 13147, 13151, 13155, 13159, 0}, 0, 0, 55, 3, {417,2861,0,0,0,0,0,0,0,0,0,0,0,0},{2979,2997,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2717,0,0,0,0,0,0,0}}, + {925, 0, 0, {13163, 13168, 13174, 13184, 13196, 13204, 13215}, {13224, 13228, 13232, 13236, 13240, 12535, 13244}, {0, 0, 0, 0, 0, 0, 0}, {13248, 13258, 13268, 13275, 13282, 1861, 13287, 13294, 13301, 13310, 13318, 13326, 0}, {13248, 13258, 13268, 13275, 13282, 1861, 13287, 13294, 13301, 13310, 13318, 13326, 0}, {1914, 1918, 13006, 13334, 13338, 1931, 1935, 13342, 1943, 1947, 1951, 13346, 0}, {1914, 1918, 13006, 13334, 13338, 1931, 1935, 13342, 1943, 1947, 1951, 13346, 0}, 0, 0, 55, 3, {417,2861,0,0,0,0,0,0,0,0,0,0,0,0},{2979,2997,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2717,0,0,0,0,0,0,0}}, + {925, 49, 52, {13350, 13357, 13369, 13380, 13393, 13402, 13414}, {12516, 13424, 13428, 13236, 13240, 12535, 13244}, {1285, 1608, 13432, 1610, 1285, 4089, 1608}, {13434, 13258, 13443, 13449, 13282, 1861, 13287, 13294, 13457, 13310, 13318, 13326, 0}, {13467, 13258, 13443, 13449, 13282, 1861, 13287, 13294, 13457, 13310, 13318, 13326, 0}, {1914, 1918, 13478, 13482, 13338, 1931, 1935, 13342, 1943, 1947, 1951, 13346, 0}, {1914, 1918, 13478, 13482, 13338, 1931, 1935, 13342, 1943, 1947, 1951, 13346, 0}, 0, 0, 1, 3, {932,3009,0,0,0,0,0,0,0,0,0,0,0,0},{978,997,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 359, 363, {13486, 13493, 13501, 13509, 13518, 13528, 13535}, {13544, 13548, 13552, 13556, 13560, 13564, 13568}, {1285, 1608, 1828, 2735, 1828, 2894, 1285}, {13572, 13581, 13591, 1851, 7756, 13597, 13603, 13609, 1878, 1888, 1896, 7768, 0}, {13572, 13581, 13591, 1851, 7756, 13597, 13603, 13609, 1878, 1888, 1896, 7768, 0}, {13618, 13623, 13628, 13633, 7756, 13638, 13643, 13648, 13653, 13658, 13663, 13668, 0}, {13618, 13623, 13628, 13633, 7756, 13638, 13643, 13648, 13653, 13658, 13663, 13668, 0}, 0, 0, 55, 3, {417,2351,0,0,0,0,0,0,0,0,0,0,0,0},{2462,1261,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 49, 52, {13673, 13689, 13714, 13742, 13770, 13798, 13826}, {13845, 13855, 13865, 13875, 13885, 13895, 13905}, {13915, 13919, 13923, 13919, 13927, 13931, 13935}, {13939, 13961, 13989, 14005, 14024, 14040, 14059, 14078, 14100, 14131, 14159, 14184, 0}, {13939, 13961, 13989, 14005, 14024, 14040, 14059, 14078, 14100, 14131, 14159, 14184, 0}, {14212, 14222, 14232, 14242, 14252, 14262, 14272, 14282, 14292, 14302, 14312, 14322, 0}, {14212, 14222, 14232, 14242, 14252, 14262, 14272, 14282, 14292, 14302, 14312, 14322, 0}, 0, 1, 11, 3, {681,3021,0,0,0,0,0,0,0,0,0,0,0,0},{42,1029,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {925, 49, 52, {4231, 14332, 14343, 14353, 14363, 14373, 14387}, {14399, 14403, 14408, 14413, 14417, 14422, 14427}, {1285, 1608, 1610, 1608, 4089, 1614, 1616}, {1618, 1625, 3504, 4376, 3515, 1649, 1654, 1659, 1666, 1676, 1684, 4420, 0}, {1618, 1625, 3504, 4376, 3515, 1649, 1654, 1659, 1666, 1676, 1684, 4420, 0}, {5089, 4610, 4512, 4614, 3515, 5093, 5097, 5101, 5105, 5109, 4642, 5113, 0}, {5089, 4610, 4512, 4614, 3515, 5093, 5097, 5101, 5105, 5109, 4642, 5113, 0}, 2, 1, 55, 3, {744,0,0,0,0,0,0,0,0,0,0,0,0,0},{1314,1455,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2717,0,0,0,0,0,0,0}}, + {1, 367, 395, {14431, 14450, 14469, 14491, 14510, 14532, 14557}, {14576, 14586, 14596, 14609, 14619, 14632, 14648}, {14658, 14662, 14669, 14676, 14683, 14690, 14697}, {14701, 14717, 14736, 14752, 14771, 14778, 14788, 14804, 14820, 14839, 14861, 14877, 0}, {14701, 14717, 14736, 14752, 14771, 14778, 14788, 14804, 14820, 14839, 14861, 14877, 0}, {14896, 14906, 14736, 14752, 14771, 14778, 14919, 14932, 14942, 14955, 14974, 14984, 0}, {14896, 14906, 14736, 14752, 14771, 14778, 14919, 14932, 14942, 14955, 14974, 14984, 0}, 0, 0, 55, 3, {744,755,1062,2098,417,0,0,0,0,0,0,0,0,0},{1261,894,0,0,0,0,0,0,0,0},{71,266,591,583,0,0,0,0,0,0,0,0},{89,271,611,600,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {3033, 49, 52, {14997, 15006, 15015, 15025, 15035, 15045, 15057}, {15065, 15070, 15074, 15078, 15082, 15087, 15092}, {15096, 15100, 15103, 15106, 15109, 15113, 15117}, {15120, 15127, 15132, 1851, 15138, 15144, 15151, 15157, 15165, 15175, 15183, 15192, 0}, {15120, 15127, 15132, 1851, 15138, 15144, 15151, 15157, 15165, 15175, 15183, 15192, 0}, {1914, 15202, 2791, 1927, 15206, 15210, 15215, 15219, 15223, 15227, 1951, 15231, 0}, {1914, 15202, 2791, 1927, 15206, 15210, 15215, 15219, 15223, 15227, 1951, 15231, 0}, 0, 0, 1, 3, {18,2351,0,0,0,0,0,0,0,0,0,0,0,0},{3048,3074,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{3094,0,0,0,0,0,0,0}}, + {925, 417, 422, {15236, 15248, 15259, 15273, 15285, 15295, 15305}, {15316, 15321, 15326, 15331, 15336, 15341, 15346}, {1285, 2894, 1608, 4532, 1828, 13432, 1616}, {15351, 15368, 15381, 15395, 15408, 15421, 15434, 15448, 15460, 15474, 15488, 15502, 0}, {15351, 15368, 15381, 15395, 15408, 15421, 15434, 15448, 15460, 15474, 15488, 15502, 0}, {15515, 15522, 15527, 15532, 15536, 15541, 15546, 15551, 15556, 15563, 15568, 15574, 0}, {15515, 15522, 15527, 15532, 15536, 15541, 15546, 15551, 15556, 15563, 15568, 15574, 0}, 2, 1, 55, 3, {417,2861,0,0,0,0,0,0,0,0,0,0,0,0},{2979,2997,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2717,0,0,0,0,0,0,0}}, + {109, 427, 430, {15872, 15877, 7673, 7680, 15883, 15890, 7697}, {15897, 15901, 7711, 7715, 15905, 7723, 7727}, {9428, 11804, 1285, 7731, 3099, 2892, 1285}, {7733, 7741, 15909, 1851, 7756, 1931, 15913, 15919, 1878, 1888, 1896, 15924, 0}, {7733, 7741, 15909, 1851, 7756, 1931, 15913, 15919, 1878, 1888, 1896, 15924, 0}, {1914, 1918, 15909, 1927, 7756, 1931, 1935, 15933, 1943, 1947, 1951, 13346, 0}, {1914, 1918, 15909, 1927, 7756, 1931, 1935, 15933, 1943, 1947, 1951, 13346, 0}, 0, 1, 1, 3, {2764,304,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 49, 52, {15937, 15954, 15971, 15988, 16005, 16022, 16031}, {16042, 16047, 16052, 16057, 16062, 16067, 16072}, {16077, 16080, 6081, 6081, 16083, 16077, 6081}, {16086, 16099, 16110, 16123, 16134, 16145, 16158, 16169, 16180, 16197, 16208, 16221, 0}, {16240, 16253, 16264, 16277, 16288, 16299, 16312, 16323, 16334, 16351, 16362, 16375, 0}, {16394, 16402, 16410, 16418, 16426, 16434, 16442, 16450, 16458, 16466, 16474, 16482, 0}, {16394, 16402, 16410, 16418, 16426, 16434, 16442, 16450, 16458, 16466, 16474, 16482, 0}, 0, 1, 1, 3, {18,3112,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {3125, 434, 439, {16490, 16507, 16524, 16541, 16558, 16575, 16584}, {16597, 16605, 16613, 16623, 16633, 16575, 16643}, {16077, 16080, 16651, 16651, 16083, 16077, 16654}, {16657, 16670, 9888, 16685, 9908, 16698, 16707, 9929, 16716, 16733, 16748, 16761, 0}, {6087, 6100, 360, 6115, 380, 6128, 6137, 401, 6146, 6163, 6178, 6191, 0}, {9994, 10001, 10008, 10015, 9908, 9915, 9922, 10022, 10029, 10036, 10043, 10050, 0}, {9994, 10001, 10008, 10015, 9908, 9915, 9922, 10022, 10029, 10036, 10043, 10050, 0}, 0, 1, 55, 3, {3132,0,0,0,0,0,0,0,0,0,0,0,0,0},{3141,0,0,0,0,0,0,0,0,0},{71,266,0,0,0,0,0,0,0,0,0,0},{89,271,0,0,0,0,0,0,0},{3160,0,0,0,0,0,0,0}}, + {109, 49, 52, {16776, 16785, 16794, 16802, 16811, 16820, 16827}, {16776, 16785, 16794, 16802, 16811, 16820, 16827}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {7733, 7741, 16836, 16842, 7756, 1861, 15913, 16849, 13301, 16856, 13318, 16863, 0}, {7733, 7741, 16836, 16842, 7756, 1861, 15913, 16849, 13301, 16856, 13318, 16863, 0}, {1914, 1918, 15909, 1927, 7756, 1931, 1935, 16871, 1943, 1947, 1951, 7781, 0}, {1914, 1918, 15909, 1927, 7756, 1931, 1935, 16871, 1943, 1947, 1951, 7781, 0}, 0, 0, 1, 3, {18,304,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 0, 0, {16875, 16886, 16895, 16904, 16915, 16925, 16930}, {16937, 16941, 16944, 16947, 16951, 9416, 16954}, {16958, 1828, 1285, 7339, 1280, 9428, 16961}, {16964, 16972, 11617, 11622, 16979, 16984, 16990, 12137, 16996, 17006, 17015, 11673, 0}, {16964, 16972, 11617, 11622, 16979, 16984, 16990, 12137, 16996, 17006, 17015, 11673, 0}, {17023, 17028, 11617, 4614, 16979, 16984, 16990, 12237, 11696, 5109, 17032, 11704, 0}, {17023, 17028, 11617, 4614, 16979, 16984, 16990, 12237, 11696, 5109, 17032, 11704, 0}, 0, 1, 11, 3, {3176,681,0,0,0,0,0,0,0,0,0,0,0,0},{3191,0,0,0,0,0,0,0,0,0},{71,266,0,0,0,0,0,0,0,0,0,0},{89,271,0,0,0,0,0,0,0},{3223,0,0,0,0,0,0,0}}, + {925, 444, 447, {17037, 17047, 17056, 17065, 17076, 17086, 17091}, {17098, 17102, 17107, 17112, 17117, 7723, 17121}, {17126, 1828, 1285, 5210, 1280, 2892, 1285}, {11533, 11540, 7354, 11547, 2760, 17128, 17133, 17138, 17145, 17153, 11589, 11596, 0}, {11603, 11610, 11617, 11622, 11628, 11632, 11637, 8914, 17160, 17168, 11666, 11673, 0}, {12906, 17175, 2791, 1927, 2760, 17179, 17183, 17187, 7707, 1947, 17191, 17195, 0}, {12906, 17175, 2791, 1927, 2760, 17179, 17183, 17187, 7707, 1947, 17191, 17195, 0}, 0, 1, 1, 3, {406,2861,0,0,0,0,0,0,0,0,0,0,0,0},{3239,2997,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2717,0,0,0,0,0,0,0}}, + {109, 49, 52, {17199, 17218, 17237, 17262, 17281, 17315, 17340}, {17359, 17369, 17379, 17395, 17405, 17430, 17446}, {17456, 17460, 17467, 17471, 17478, 17485, 17492}, {17496, 17524, 17558, 17574, 17593, 17600, 17610, 17626, 17642, 17673, 17695, 17717, 0}, {17496, 17524, 17558, 17574, 17593, 17600, 17610, 17626, 17642, 17673, 17695, 17717, 0}, {17496, 17524, 17558, 17574, 17593, 17600, 17610, 17626, 17642, 17673, 17695, 17717, 0}, {17496, 17524, 17558, 17574, 17593, 17600, 17610, 17626, 17642, 17673, 17695, 17717, 0}, 0, 0, 55, 11, {755,1062,744,755,744,0,0,0,0,0,0,0,0,0},{1261,894,0,0,0,0,0,0,0,0},{1151,1146,3258,3267,0,0,0,0,0,0,0,0},{1173,1165,3275,3287,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {1, 49, 52, {18324, 18343, 18362, 18384, 18403, 18425, 18450}, {18469, 18479, 18489, 18502, 18512, 18525, 18541}, {18551, 18555, 18562, 18569, 18576, 18583, 18590}, {18594, 18622, 18650, 18666, 18685, 18692, 18702, 18718, 18734, 18762, 18784, 18806, 0}, {18594, 18622, 18650, 18666, 18685, 18692, 18702, 18718, 18734, 18762, 18784, 18806, 0}, {18831, 18850, 18650, 18666, 18685, 18692, 18702, 18718, 18869, 18885, 18901, 18911, 0}, {18831, 18850, 18650, 18666, 18685, 18692, 18702, 18718, 18869, 18885, 18901, 18911, 0}, 0, 0, 55, 3, {755,1062,2098,744,417,0,0,0,0,0,0,0,0,0},{1261,894,0,0,0,0,0,0,0,0},{71,266,591,583,0,0,0,0,0,0,0,0},{89,271,611,600,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {1, 5, 8, {18924, 18943, 18962, 18987, 19006, 19028, 19053}, {19072, 19082, 19092, 19108, 19118, 19131, 19147}, {19157, 19161, 19168, 19172, 19179, 19186, 19193}, {19197, 19219, 19241, 19263, 19282, 19289, 19299, 19315, 19331, 19362, 19384, 19406, 0}, {19197, 19219, 19241, 19263, 19282, 19289, 19299, 19315, 19331, 19362, 19384, 19406, 0}, {19197, 19219, 19241, 19263, 19282, 19289, 19299, 19315, 19331, 19362, 19384, 19406, 0}, {19197, 19219, 19241, 19263, 19282, 19289, 19299, 19315, 19331, 19362, 19384, 19406, 0}, 0, 0, 55, 3, {755,1062,2098,744,417,0,0,0,0,0,0,0,0,0},{1261,894,0,0,0,0,0,0,0,0},{71,266,591,583,0,0,0,0,0,0,0,0},{89,271,611,600,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {109, 480, 505, {19431, 19450, 19472, 19497, 19513, 19535, 19554}, {19564, 19578, 19592, 19606, 19617, 19631, 19554}, {19645, 19652, 19659, 19666, 19673, 19680, 19687}, {19691, 19707, 19732, 19751, 19770, 19777, 19790, 19803, 19822, 19853, 19878, 19900, 0}, {19691, 19707, 19732, 19751, 19770, 19777, 19790, 19803, 19822, 19853, 19878, 19900, 0}, {19925, 19933, 19947, 19961, 19770, 19777, 19790, 19972, 19980, 19994, 20005, 20013, 0}, {19925, 19933, 19947, 19961, 19770, 19777, 19790, 19972, 19980, 19994, 20005, 20013, 0}, 0, 0, 55, 3, {744,755,1062,2098,417,0,0,0,0,0,0,0,0,0},{1261,894,0,0,0,0,0,0,0,0},{71,266,591,583,0,0,0,0,0,0,0,0},{89,271,611,600,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {925, 49, 52, {20024, 20046, 20068, 20093, 20115, 20140, 20168}, {20190, 20200, 20210, 20223, 20233, 20246, 20262}, {20272, 20276, 20283, 20287, 20294, 20301, 20308}, {20312, 20328, 20353, 20372, 20394, 20401, 20414, 20427, 20446, 20477, 20502, 20521, 0}, {20312, 20328, 20353, 20372, 20394, 20401, 20414, 20427, 20446, 20477, 20502, 20521, 0}, {20546, 20553, 20353, 20569, 20394, 20401, 20414, 20427, 20585, 20607, 20623, 20633, 0}, {20546, 20553, 20353, 20569, 20394, 20401, 20414, 20427, 20585, 20607, 20623, 20633, 0}, 0, 0, 55, 3, {755,1062,2098,744,417,0,0,0,0,0,0,0,0,0},{1261,894,0,0,0,0,0,0,0,0},{71,266,591,583,0,0,0,0,0,0,0,0},{89,271,611,600,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {925, 530, 558, {20649, 20671, 20690, 20712, 20731, 20753, 20778}, {20797, 20810, 20820, 20833, 20843, 20856, 20872}, {20882, 20889, 20896, 20903, 20910, 20917, 20924}, {20928, 20944, 20969, 20988, 21010, 21017, 21030, 21043, 21062, 21093, 21118, 21140, 0}, {20928, 20944, 20969, 20988, 21010, 21017, 21030, 21043, 21062, 21093, 21118, 21140, 0}, {21165, 21172, 20969, 21188, 21010, 21017, 21030, 21204, 21211, 21233, 21249, 21262, 0}, {21165, 21172, 20969, 21188, 21010, 21017, 21030, 21204, 21211, 21233, 21249, 21262, 0}, 0, 0, 55, 3, {755,1062,2098,744,417,0,0,0,0,0,0,0,0,0},{1261,894,0,0,0,0,0,0,0,0},{71,266,591,583,0,0,0,0,0,0,0,0},{89,271,611,600,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {3316, 49, 52, {21278, 21306, 21340, 21368, 21396, 21427, 21464}, {21492, 21505, 21524, 21540, 21553, 21572, 21591}, {21601, 21608, 21615, 21622, 21629, 21642, 21649}, {21653, 21672, 21700, 21722, 21741, 21754, 21764, 21777, 21802, 21833, 21858, 21874, 0}, {21653, 21672, 21700, 21722, 21741, 21754, 21764, 21777, 21802, 21833, 21858, 21874, 0}, {21893, 21903, 21922, 21932, 21741, 21754, 21764, 21948, 21955, 21980, 21996, 22006, 0}, {21893, 21903, 21922, 21932, 21741, 21754, 21764, 21948, 21955, 21980, 21996, 22006, 0}, 0, 0, 55, 11, {755,1062,775,2098,0,0,0,0,0,0,0,0,0,0},{1261,894,0,0,0,0,0,0,0,0},{1151,1146,3258,3267,0,0,0,0,0,0,0,0},{1173,1165,3275,3287,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {109, 580, 608, {22019, 22038, 22057, 22082, 22101, 22135, 22160}, {22179, 17369, 17379, 17395, 22189, 22214, 17446}, {0, 0, 0, 0, 0, 0, 0}, {22230, 22255, 22286, 22302, 17593, 17600, 17610, 22321, 22337, 22368, 22390, 22412, 0}, {22230, 22255, 22286, 22302, 17593, 17600, 17610, 22321, 22337, 22368, 22390, 22412, 0}, {22437, 22450, 22286, 22302, 17593, 17600, 17610, 22469, 22476, 22492, 22508, 22518, 0}, {22437, 22450, 22286, 22302, 17593, 17600, 17610, 22469, 22476, 22492, 22508, 22518, 0}, 0, 0, 55, 3, {744,0,0,0,0,0,0,0,0,0,0,0,0,0},{3324,0,0,0,0,0,0,0,0,0},{583,591,266,0,0,0,0,0,0,0,0,0},{600,611,271,0,0,0,0,0,0},{3343,3351,0,0,0,0,0,0}}, + {1, 630, 642, {14431, 14450, 22531, 14491, 14510, 14532, 14557}, {14576, 14586, 22553, 14609, 14619, 14632, 14648}, {14658, 14662, 14669, 14676, 14683, 14690, 14697}, {22566, 22591, 14736, 22622, 22641, 14778, 22648, 22661, 22677, 22702, 22724, 22752, 0}, {22566, 22591, 14736, 22622, 22641, 14778, 22648, 22661, 22677, 22702, 22724, 22752, 0}, {22774, 22787, 14736, 22806, 22641, 14778, 22648, 22822, 22829, 22848, 22864, 22886, 0}, {22774, 22787, 14736, 22806, 22641, 14778, 22648, 22822, 22829, 22848, 22864, 22886, 0}, 0, 0, 55, 3, {744,755,1062,2098,417,0,0,0,0,0,0,0,0,0},{1261,894,0,0,0,0,0,0,0,0},{71,266,591,583,0,0,0,0,0,0,0,0},{89,271,611,600,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {925, 651, 656, {22902, 22909, 22920, 22933, 22946, 22957, 22970}, {22981, 22986, 22991, 22996, 23001, 23006, 23011}, {22981, 22986, 22991, 22996, 23001, 23006, 23011}, {23016, 23042, 23070, 23100, 23130, 23156, 23186, 23212, 23240, 23264, 23292, 23329, 0}, {23016, 23042, 23070, 23100, 23130, 23156, 23186, 23212, 23240, 23264, 23292, 23329, 0}, {23368, 23380, 23392, 23404, 23416, 23428, 23440, 23452, 23464, 23476, 23489, 23502, 0}, {23368, 23380, 23392, 23404, 23416, 23428, 23440, 23452, 23464, 23476, 23489, 23502, 0}, 0, 1, 55, 3, {417,2861,0,0,0,0,0,0,0,0,0,0,0,0},{3361,3399,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2717,0,0,0,0,0,0,0}}, + {3431, 661, 683, {23515, 23543, 23571, 23608, 23639, 23673, 23704}, {23738, 23754, 23770, 23795, 23814, 23836, 23855}, {23877, 23884, 23891, 23901, 23911, 23921, 23931}, {23944, 23978, 24015, 24052, 24086, 24117, 24154, 24191, 24231, 24265, 24299, 24348, 0}, {24397, 24428, 24462, 24496, 24527, 24555, 24589, 24623, 24660, 24691, 24722, 24768, 0}, {24814, 24827, 24840, 24853, 24866, 24879, 24892, 24905, 24918, 24931, 24947, 24963, 0}, {24814, 24827, 24840, 24853, 24866, 24879, 24892, 24905, 24918, 24931, 24947, 24963, 0}, 0, 0, 1, 3, {379,388,397,428,417,406,446,439,453,0,0,0,0,0},{3455,3498,3546,3578,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{3615,666,0,0,0,0,0,0}}, + {109, 708, 711, {24979, 24988, 24998, 25010, 25023, 25032, 25044}, {25056, 25060, 12926, 25065, 25069, 25073, 25077}, {1285, 25081, 1608, 1608, 11804, 4532, 1285}, {25084, 25091, 25100, 25107, 1857, 25114, 25122, 25133, 25138, 25143, 25150, 25159, 0}, {25084, 25091, 25100, 25107, 1857, 25114, 25122, 25133, 25138, 25143, 25150, 25159, 0}, {25167, 25171, 12926, 25175, 1857, 25179, 25183, 25133, 25138, 25187, 25191, 25196, 0}, {25167, 25171, 12926, 25175, 1857, 25179, 25183, 25133, 25138, 25187, 25191, 25196, 0}, 2, 1, 1, 3, {18,304,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 49, 52, {25201, 25223, 25239, 25258, 25268, 25299, 25315}, {25201, 25223, 25239, 25258, 25268, 25299, 25315}, {25328, 25332, 25328, 25336, 25336, 25340, 25340}, {25344, 25357, 25376, 25389, 25402, 25415, 25434, 25453, 25466, 25482, 25495, 25520, 0}, {25344, 25357, 25376, 25389, 25402, 25415, 25434, 25453, 25466, 25482, 25495, 25520, 0}, {25344, 25357, 25376, 25389, 25402, 25415, 25434, 25453, 25466, 25482, 25495, 25520, 0}, {25344, 25357, 25376, 25389, 25402, 25415, 25434, 25453, 25466, 25482, 25495, 25520, 0}, 0, 0, 1, 3, {9,417,0,0,0,0,0,0,0,0,0,0,0,0},{894,2363,0,0,0,0,0,0,0,0},{266,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{3648,0,0,0,0,0,0,0}}, + {109, 714, 739, {25533, 25558, 25577, 25605, 25624, 25649, 25668}, {25690, 25706, 25716, 25735, 25745, 25761, 25771}, {25784, 25791, 25795, 25799, 25803, 25810, 25817}, {25821, 25840, 25856, 25869, 25882, 25904, 25923, 25945, 25961, 25977, 25990, 26006, 0}, {25821, 25840, 25856, 25869, 25882, 25904, 25923, 25945, 25961, 25977, 25990, 26006, 0}, {26022, 26031, 26040, 26049, 26058, 26067, 26079, 26088, 26097, 26106, 26115, 26124, 0}, {26022, 26031, 26040, 26049, 26058, 26067, 26079, 26088, 26097, 26106, 26115, 26124, 0}, 0, 0, 1, 3, {295,304,0,0,0,0,0,0,0,0,0,0,0,0},{3683,894,0,0,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 764, 780, {26133, 26161, 26183, 26202, 26227, 26252, 26271}, {26133, 26161, 26183, 26202, 26227, 26252, 26271}, {26281, 26281, 26285, 26289, 26293, 26297, 26301}, {26305, 26330, 26361, 26371, 26384, 26391, 26404, 26426, 26442, 26467, 26498, 26523, 0}, {26305, 26330, 26361, 26371, 26384, 26391, 26404, 26426, 26442, 26467, 26498, 26523, 0}, {26545, 26555, 26361, 26562, 26384, 26391, 26566, 26573, 26577, 26587, 26603, 26613, 0}, {26545, 26555, 26361, 26562, 26384, 26391, 26566, 26573, 26577, 26587, 26603, 26613, 0}, 0, 0, 55, 3, {744,304,0,0,0,0,0,0,0,0,0,0,0,0},{3710,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 151, 156, {2803, 26620, 2817, 26625, 26635, 26641, 2850}, {26648, 26653, 26658, 26663, 26669, 26674, 26679}, {1828, 1616, 1608, 1608, 2890, 2894, 1285}, {26685, 26693, 26702, 26708, 26714, 26719, 26725, 26731, 26738, 26747, 26755, 26764, 0}, {26773, 26781, 2910, 666, 5557, 26790, 26796, 2933, 5574, 5583, 5591, 26802, 0}, {26811, 13623, 26658, 26816, 26714, 26719, 26821, 26826, 26831, 26836, 13663, 26841, 0}, {26811, 13623, 26658, 26816, 26714, 26719, 26821, 26826, 26831, 26836, 13663, 26841, 0}, 2, 1, 1, 3, {18,3021,0,0,0,0,0,0,0,0,0,0,0,0},{1314,1261,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {1, 630, 790, {26846, 14450, 26874, 14491, 14510, 14532, 14557}, {14576, 14586, 22553, 14609, 14619, 14632, 14648}, {0, 0, 0, 0, 0, 0, 0}, {22566, 22591, 14736, 22622, 22641, 14778, 22648, 26893, 26909, 26937, 22724, 22752, 0}, {22566, 22591, 14736, 22622, 22641, 14778, 22648, 26893, 26909, 26937, 22724, 22752, 0}, {4744, 4746, 4748, 4750, 4752, 4754, 4756, 4758, 4760, 4762, 4765, 4768, 0}, {4744, 4746, 4748, 4750, 4752, 4754, 4756, 4758, 4760, 4762, 4765, 4768, 0}, 0, 0, 55, 3, {744,755,1062,2098,417,0,0,0,0,0,0,0,0,0},{1261,894,0,0,0,0,0,0,0,0},{71,266,591,583,0,0,0,0,0,0,0,0},{89,271,611,600,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {925, 802, 814, {26959, 26975, 26991, 27019, 27035, 27078, 27103}, {26959, 26975, 27131, 27019, 27141, 27163, 27176}, {27186, 27190, 27194, 27198, 27202, 27215, 27222}, {27229, 27248, 27273, 27292, 27317, 27330, 27343, 27356, 27378, 27412, 27437, 27465, 0}, {27229, 27248, 27273, 27292, 27317, 27330, 27343, 27356, 27378, 27412, 27437, 27465, 0}, {27493, 27500, 27510, 27292, 27317, 27330, 27343, 27523, 27533, 27546, 27556, 27569, 0}, {27493, 27500, 27510, 27292, 27317, 27330, 27343, 27523, 27533, 27546, 27556, 27569, 0}, 0, 1, 55, 11, {417,2861,0,0,0,0,0,0,0,0,0,0,0,0},{2979,2997,0,0,0,0,0,0,0,0},{3267,71,0,0,0,0,0,0,0,0,0,0},{3287,89,0,0,0,0,0,0,0},{2717,0,0,0,0,0,0,0}}, + {925, 852, 862, {28047, 28057, 28064, 28077, 28087, 28097, 28107}, {28047, 28057, 28117, 28077, 28087, 28097, 28107}, {28127, 28131, 28135, 28139, 28143, 28147, 28151}, {28155, 28171, 28187, 28197, 28210, 28217, 28224, 28234, 28247, 28266, 28282, 28298, 0}, {28155, 28171, 28187, 28197, 28210, 28217, 28224, 28234, 28247, 28266, 28282, 28298, 0}, {28314, 28324, 28187, 28334, 28210, 28217, 28224, 28344, 28354, 28364, 28374, 28384, 0}, {28314, 28324, 28187, 28334, 28210, 28217, 28224, 28344, 28354, 28364, 28374, 28384, 0}, 0, 0, 1, 3, {18,304,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {1, 367, 395, {28601, 28620, 28639, 28664, 28683, 28705, 28730}, {28749, 14586, 28759, 14609, 28775, 14632, 14648}, {28788, 14662, 28792, 14676, 28796, 14690, 14697}, {14701, 28803, 14736, 28831, 22641, 28850, 14788, 28860, 28876, 28907, 28929, 28954, 0}, {14701, 28803, 14736, 28831, 14771, 28850, 14788, 28860, 28876, 28907, 28929, 28954, 0}, {14701, 28803, 14736, 28831, 22641, 28850, 14788, 28860, 28876, 28907, 28929, 28954, 0}, {14701, 28803, 14736, 28831, 22641, 28850, 14788, 28860, 28876, 28907, 28929, 28954, 0}, 0, 0, 1, 3, {932,941,948,957,460,417,968,0,0,0,0,0,0,0},{3763,3783,42,2448,0,0,0,0,0,0},{906,62,266,71,0,0,0,0,0,0,0,0},{914,77,271,89,0,0,0,0,0},{3351,0,0,0,0,0,0,0}}, + {109, 49, 52, {28979, 28985, 28993, 29001, 29010, 29021, 29027}, {29033, 29036, 3084, 4997, 3090, 29039, 1275}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {29042, 29053, 13591, 1851, 29064, 29070, 2769, 13609, 29075, 1888, 29085, 29094, 0}, {29042, 29053, 13591, 1851, 29064, 29070, 2769, 13609, 29075, 1888, 29085, 29094, 0}, {1914, 1918, 29103, 1927, 1857, 1931, 1935, 1939, 1943, 1947, 1951, 7781, 0}, {1914, 1918, 29103, 1927, 1857, 1931, 1935, 1939, 1943, 1947, 1951, 7781, 0}, 2, 1, 55, 3, {744,304,0,0,0,0,0,0,0,0,0,0,0,0},{1217,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 0, 0, {0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0}, {29107, 29114, 29123, 29138, 29149, 29158, 29165, 29172, 29179, 29190, 29203, 29216, 0}, {29107, 29114, 29123, 29138, 29149, 29158, 29165, 29172, 29179, 29190, 29203, 29216, 0}, {29107, 29114, 29123, 29138, 29149, 29158, 29165, 29172, 29179, 29190, 29203, 29216, 0}, {29107, 29114, 29123, 29138, 29149, 29158, 29165, 29172, 29179, 29190, 29203, 29216, 0}, 0, 6, 1, 3, {379,417,0,0,0,0,0,0,0,0,0,0,0,0},{894,1217,0,0,0,0,0,0,0,0},{266,0,0,0,0,0,0,0,0,0,0,0},{271,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {925, 49, 52, {29221, 29228, 29234, 29241, 29252, 29260, 29269}, {29276, 29280, 2791, 29284, 29288, 29292, 7727}, {29276, 29280, 2791, 29284, 29288, 29292, 7727}, {29296, 29302, 29310, 26708, 29316, 29321, 29327, 26731, 29333, 29343, 29351, 29361, 0}, {29296, 29302, 29310, 26708, 29316, 29321, 29327, 26731, 29333, 29343, 29351, 29361, 0}, {29371, 29375, 2791, 29379, 2760, 29383, 29387, 16871, 15223, 1947, 29391, 13346, 0}, {29371, 29375, 2791, 29379, 2760, 29383, 29387, 16871, 15223, 1947, 29391, 13346, 0}, 0, 0, 1, 3, {932,3009,0,0,0,0,0,0,0,0,0,0,0,0},{978,997,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {925, 49, 52, {29614, 29621, 29629, 29636, 29643, 29651, 29660}, {29667, 29671, 29675, 29679, 29683, 7723, 28444}, {1616, 1616, 1610, 1616, 9428, 2892, 9428}, {29687, 29695, 29705, 29711, 29719, 29724, 29729, 29734, 29741, 16856, 29749, 29757, 0}, {29687, 29695, 29705, 29711, 29719, 29724, 29729, 29734, 29741, 16856, 29749, 29757, 0}, {1914, 29765, 2791, 29769, 2760, 28570, 28574, 29773, 2731, 1947, 29777, 13346, 0}, {1914, 29765, 2791, 29769, 2760, 28570, 28574, 29773, 2731, 1947, 29777, 13346, 0}, 0, 0, 1, 3, {295,3021,0,0,0,0,0,0,0,0,0,0,0,0},{1010,1029,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {925, 914, 925, {29781, 29799, 29814, 29836, 29849, 29863, 29880}, {29904, 29912, 29917, 29836, 29849, 29929, 29936}, {0, 0, 0, 0, 0, 0, 0}, {29950, 29972, 29988, 30008, 30022, 30039, 30054, 30071, 30085, 30098, 30117, 30131, 0}, {29950, 29972, 29988, 30008, 30022, 30039, 30054, 30071, 30085, 30098, 30117, 30131, 0}, {30150, 30165, 30174, 30187, 30194, 30204, 30212, 30222, 30229, 30235, 30247, 30254, 0}, {30150, 30165, 30174, 30187, 30194, 30204, 30212, 30222, 30229, 30235, 30247, 30254, 0}, 0, 0, 1, 3, {18,304,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {925, 0, 0, {30266, 30274, 12472, 12968, 12490, 30285, 30295}, {12516, 12998, 12524, 12528, 12532, 12535, 30304}, {0, 0, 0, 0, 0, 0, 0}, {30308, 30317, 30327, 30335, 7756, 2764, 30343, 30349, 30358, 30367, 30376, 30385, 0}, {30308, 30317, 30327, 30335, 7756, 2764, 30343, 30349, 30358, 30367, 30376, 30385, 0}, {1914, 1918, 13006, 30394, 7756, 1931, 1935, 16871, 15223, 1947, 30398, 13346, 0}, {1914, 1918, 13006, 30394, 7756, 1931, 1935, 16871, 15223, 1947, 30398, 13346, 0}, 0, 0, 55, 3, {417,2861,0,0,0,0,0,0,0,0,0,0,0,0},{2979,2997,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2717,0,0,0,0,0,0,0}}, + {3797, 935, 941, {30402, 30410, 30419, 30430, 30440, 30452, 30460}, {12516, 30470, 30475, 30480, 30485, 30489, 30493}, {1285, 1608, 1828, 1608, 1828, 1614, 1285}, {1830, 1837, 30497, 30504, 30512, 1861, 1866, 1871, 1878, 1888, 1896, 1905, 0}, {1830, 1837, 30497, 30504, 30512, 1861, 1866, 1871, 1878, 1888, 1896, 1905, 0}, {1914, 1918, 30516, 29379, 30512, 1931, 1935, 1939, 1943, 1947, 1951, 1955, 0}, {1914, 1918, 30516, 29379, 30512, 1931, 1935, 1939, 1943, 1947, 1951, 1955, 0}, 2, 1, 11, 3, {775,9,755,0,0,0,0,0,0,0,0,0,0,0},{721,3806,798,2541,3819,3844,0,0,0,0},{71,266,1146,3870,0,0,0,0,0,0,0,0},{89,3882,3897,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {3913, 951, 956, {30521, 30528, 30543, 30557, 30573, 30588, 30604}, {4528, 30619, 4512, 30623, 30627, 30631, 30635}, {1285, 9428, 1608, 1280, 1285, 1610, 9428}, {5011, 5019, 30639, 30646, 30653, 1649, 1654, 30658, 30668, 30679, 30688, 30698, 0}, {5011, 5019, 30639, 30646, 30653, 1649, 1654, 30658, 30668, 30679, 30688, 30698, 0}, {5089, 4610, 4512, 4614, 1645, 5093, 5097, 5101, 5105, 5109, 4642, 6911, 0}, {5089, 4610, 4512, 4614, 1645, 5093, 5097, 5101, 5105, 5109, 4642, 6911, 0}, 0, 0, 55, 3, {744,755,417,764,0,0,0,0,0,0,0,0,0,0},{3926,721,2561,0,0,0,0,0,0,0},{71,266,0,0,0,0,0,0,0,0,0,0},{89,271,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {925, 961, 966, {30708, 30724, 30732, 30740, 30749, 30761, 30771}, {30781, 30787, 30793, 30797, 30801, 30809, 2731}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {30816, 30829, 30843, 30852, 30512, 30858, 30863, 30871, 13301, 30884, 13318, 13326, 0}, {30816, 30829, 30843, 30852, 30512, 30858, 30863, 30871, 13301, 30884, 13318, 13326, 0}, {30893, 1918, 30897, 13334, 30512, 30901, 1935, 30905, 1943, 30913, 1951, 13346, 0}, {30893, 1918, 30897, 13334, 30512, 30901, 1935, 30905, 1943, 30913, 1951, 13346, 0}, 0, 0, 1, 3, {18,304,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {1, 971, 974, {30919, 30927, 30935, 30943, 30950, 30958, 30966}, {30974, 30978, 30982, 30986, 7719, 30990, 30994}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {30998, 31006, 31018, 31030, 31035, 31042, 31053, 31064, 31072, 31081, 31094, 31102, 0}, {30998, 31006, 31018, 31030, 31035, 31042, 31053, 31064, 31072, 31081, 31094, 31102, 0}, {31109, 31113, 31117, 31121, 31125, 31129, 31133, 31137, 31141, 31145, 25077, 12918, 0}, {31109, 31113, 31117, 31121, 31125, 31129, 31133, 31137, 31141, 31145, 25077, 12918, 0}, 0, 0, 1, 3, {18,2796,0,0,0,0,0,0,0,0,0,0,0,0},{978,1261,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {1, 977, 997, {31149, 31162, 31172, 28077, 31182, 31192, 31202}, {31212, 31219, 31226, 31233, 31240, 31247, 31254}, {28131, 28131, 31261, 28139, 31265, 28147, 31269}, {31273, 31280, 31293, 31306, 31319, 31332, 31339, 31349, 31359, 31375, 31388, 31398, 0}, {31273, 31280, 31293, 31306, 31319, 31332, 31339, 31349, 31359, 31375, 31388, 31398, 0}, {31273, 31411, 31418, 31425, 31432, 31332, 31439, 31446, 31453, 31460, 31467, 31474, 0}, {31273, 31411, 31418, 31425, 31432, 31332, 31439, 31446, 31453, 31460, 31467, 31474, 0}, 0, 0, 1, 3, {18,2796,0,0,0,0,0,0,0,0,0,0,0,0},{4538,1261,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 49, 52, {31481, 31489, 31499, 31508, 31518, 31527, 31537}, {31546, 31549, 31552, 31555, 31558, 31561, 31564}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {31567, 31575, 31585, 31592, 7756, 31602, 31607, 31613, 31622, 31633, 31643, 31652, 0}, {31567, 31575, 31585, 31592, 7756, 31602, 31607, 31613, 31622, 31633, 31643, 31652, 0}, {31661, 31666, 31671, 31676, 7756, 31682, 31687, 31692, 31698, 31703, 31709, 31714, 0}, {31661, 31666, 31671, 31676, 7756, 31682, 31687, 31692, 31698, 31703, 31709, 31714, 0}, 0, 0, 1, 3, {295,304,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {925, 1017, 1021, {31719, 31724, 31731, 31739, 31746, 31754, 31760}, {31766, 15901, 29675, 31770, 15905, 30990, 7727}, {9428, 11804, 1610, 9428, 31774, 2892, 1285}, {31777, 31791, 31804, 31820, 31833, 31847, 31860, 31875, 31891, 31907, 31921, 31943, 0}, {31777, 31791, 31804, 31820, 31833, 31847, 31860, 31875, 31891, 31907, 31921, 31943, 0}, {31966, 31970, 25077, 31974, 31978, 31982, 31986, 31990, 31994, 31998, 32002, 32006, 0}, {31966, 31970, 25077, 31974, 31978, 31982, 31986, 31990, 31994, 31998, 32002, 32006, 0}, 0, 0, 1, 3, {18,2796,0,0,0,0,0,0,0,0,0,0,0,0},{3763,1261,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {3982, 1025, 1032, {32010, 32020, 32030, 32040, 32050, 32060, 32070}, {32080, 32087, 32094, 32101, 32108, 32115, 32122}, {32129, 32133, 32137, 32141, 32145, 32149, 32153}, {32157, 32164, 32171, 32178, 32185, 32192, 32199, 32206, 32213, 32220, 32227, 32237, 0}, {32157, 32164, 32171, 32178, 32185, 32192, 32199, 32206, 32213, 32220, 32227, 32237, 0}, {32157, 32164, 32171, 32178, 32185, 32192, 32199, 32206, 32213, 32220, 32227, 32237, 0}, {32157, 32164, 32171, 32178, 32185, 32192, 32199, 32206, 32213, 32220, 32227, 32237, 0}, 0, 0, 1, 3, {379,388,397,428,417,406,0,0,0,0,0,0,0,0},{4005,4029,4059,4089,4106,0,0,0,0,0},{583,266,71,0,0,0,0,0,0,0,0,0},{600,271,89,0,0,0,0,0,0},{4129,666,0,0,0,0,0,0}}, + {109, 961, 1039, {25056, 29280, 32247, 32254, 32264, 32269, 32276}, {25056, 29280, 32283, 32288, 32264, 32293, 32298}, {32303, 1616, 32306, 32309, 17126, 4532, 1825}, {32312, 32319, 32247, 32330, 32336, 32340, 32349, 32356, 32361, 32370, 32375, 32378, 0}, {32312, 32319, 32247, 32330, 32336, 32340, 32349, 32356, 32361, 32370, 32375, 32378, 0}, {32384, 32389, 32397, 32403, 32336, 32408, 32414, 32356, 32420, 32370, 32375, 32426, 0}, {32384, 32389, 32397, 32403, 32336, 32408, 32414, 32356, 32420, 32370, 32375, 32426, 0}, 2, 1, 55, 3, {417,2861,0,0,0,0,0,0,0,0,0,0,0,0},{2979,2997,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {3125, 1044, 1050, {32431, 32448, 32463, 32480, 32497, 32514, 32523}, {32534, 32539, 32544, 32549, 32554, 32559, 32564}, {32569, 10152, 10155, 10158, 10161, 10164, 10167}, {32572, 32585, 32598, 32607, 32620, 32627, 32638, 32649, 32664, 32681, 32698, 32713, 0}, {32572, 32585, 32598, 32607, 32620, 32627, 32638, 32649, 32664, 32681, 32698, 32713, 0}, {32572, 32585, 32598, 32607, 32620, 32627, 32638, 32649, 32664, 32681, 32698, 32713, 0}, {32572, 32585, 32598, 32607, 32620, 32627, 32638, 32649, 32664, 32681, 32698, 32713, 0}, 0, 0, 55, 3, {388,397,417,428,0,0,0,0,0,0,0,0,0,0},{4146,4167,4193,4234,4280,0,0,0,0,0},{266,71,583,591,0,0,0,0,0,0,0,0},{271,89,600,611,0,0,0,0,0},{4294,2717,666,0,0,0,0,0}}, + {673, 1056, 1061, {32728, 32736, 32746, 32756, 32765, 32775, 32783}, {32793, 32797, 32802, 32806, 32810, 32814, 13568}, {1285, 1608, 1828, 1608, 1828, 1614, 1285}, {1830, 1837, 1845, 1851, 1857, 1861, 1866, 32818, 32827, 32838, 32847, 32857, 0}, {1830, 1837, 1845, 1851, 1857, 1861, 1866, 32818, 32827, 32838, 32847, 32857, 0}, {1914, 1918, 1922, 1927, 1857, 1931, 1935, 1939, 1943, 1947, 1951, 1955, 0}, {1914, 1918, 1922, 1927, 1857, 1931, 1935, 1939, 1943, 1947, 1951, 1955, 0}, 2, 1, 1, 3, {18,9,775,755,417,0,0,0,0,0,0,0,0,0},{1217,1234,894,0,0,0,0,0,0,0},{71,266,1151,1243,1253,0,0,0,0,0,0,0},{89,271,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {4315, 1066, 1071, {32867, 32892, 32917, 32942, 32955, 32970, 32987}, {33002, 33007, 33012, 33017, 33022, 33027, 310}, {16083, 16083, 33032, 6081, 6084, 16083, 6081}, {33035, 33054, 33069, 33091, 33111, 33125, 33139, 33151, 33175, 33197, 33214, 33231, 0}, {33248, 33267, 33282, 33304, 33324, 33340, 33356, 33370, 33396, 33420, 33437, 33231, 0}, {33454, 33463, 33472, 33479, 33486, 33493, 33500, 33507, 33514, 33521, 33528, 33535, 0}, {33454, 33463, 33472, 33479, 33486, 33493, 33500, 33507, 33514, 33521, 33528, 33535, 0}, 0, 1, 11, 3, {681,1208,417,4331,4341,0,0,0,0,0,0,0,0,0},{4354,4389,4418,0,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{4453,0,0,0,0,0,0,0}}, + {925, 49, 52, {33542, 33554, 33565, 33577, 33589, 33599, 33611}, {33626, 33631, 33636, 33641, 33646, 33651, 33656}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {33661, 33670, 33682, 33690, 33695, 33705, 33712, 33721, 33728, 33734, 33743, 33754, 0}, {33661, 33670, 33682, 33690, 33695, 33705, 33712, 33721, 33728, 33734, 33743, 33754, 0}, {33762, 33767, 33772, 33777, 33782, 33787, 33792, 33646, 33797, 33802, 33807, 33812, 0}, {33762, 33767, 33772, 33777, 33782, 33787, 33792, 33646, 33797, 33802, 33807, 33812, 0}, 0, 0, 1, 3, {406,2861,0,0,0,0,0,0,0,0,0,0,0,0},{3239,2997,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2717,0,0,0,0,0,0,0}}, + {4469, 1076, 1078, {33817, 33830, 33838, 33847, 33857, 33867, 33876}, {33888, 33892, 33896, 33900, 33904, 33908, 33912}, {1828, 1616, 1608, 5210, 9428, 4089, 1285}, {33916, 33930, 33941, 33950, 33961, 33973, 33987, 33999, 34012, 34025, 34037, 34050, 0}, {34064, 34081, 34095, 34107, 34121, 34136, 34150, 34162, 34177, 34192, 34206, 34221, 0}, {34237, 34242, 34248, 34254, 34259, 34265, 34271, 34276, 34282, 34287, 15862, 34293, 0}, {34237, 34242, 34248, 34254, 34259, 34265, 34271, 34276, 34282, 34287, 15862, 34293, 0}, 2, 1, 1, 3, {18,304,0,0,0,0,0,0,0,0,0,0,0,0},{4480,4502,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {1, 1080, 1083, {34348, 34359, 34374, 34391, 34408, 34421, 34434}, {34348, 34359, 34374, 34391, 34408, 34421, 34434}, {34445, 34448, 34451, 34454, 34457, 10164, 10155}, {34460, 34484, 34493, 34502, 34513, 34522, 34535, 34544, 34549, 34560, 34582, 34606, 0}, {34460, 34484, 34493, 34502, 34513, 34522, 34535, 34544, 34549, 34560, 34582, 34606, 0}, {34460, 34484, 34493, 34502, 34513, 34522, 34535, 34544, 34549, 34560, 34582, 34606, 0}, {34460, 34484, 34493, 34502, 34513, 34522, 34535, 34544, 34549, 34560, 34582, 34606, 0}, 0, 6, 1, 3, {18,9,417,0,0,0,0,0,0,0,0,0,0,0},{2448,42,0,0,0,0,0,0,0,0},{62,71,0,0,0,0,0,0,0,0,0,0},{77,89,0,0,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {109, 13, 19, {538, 547, 555, 563, 572, 579, 589}, {598, 602, 606, 610, 614, 618, 622}, {626, 629, 632, 635, 638, 641, 644}, {647, 653, 660, 666, 672, 677, 682, 689, 695, 704, 712, 721, 0}, {730, 739, 749, 758, 768, 776, 784, 794, 804, 816, 828, 840, 0}, {852, 857, 660, 863, 672, 677, 868, 873, 877, 882, 887, 892, 0}, {852, 857, 660, 863, 672, 677, 868, 873, 877, 882, 887, 892, 0}, 2, 1, 1, 3, {295,304,0,0,0,0,0,0,0,0,0,0,0,0},{315,338,0,0,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{355,0,0,0,0,0,0,0}}, + {370, 25, 32, {897, 907, 917, 927, 937, 947, 957}, {967, 974, 981, 988, 995, 1002, 1009}, {1016, 1020, 1024, 1028, 1032, 1036, 1040}, {1044, 1051, 1058, 1065, 1072, 1079, 1086, 1093, 1100, 1107, 1114, 1124, 0}, {1044, 1051, 1058, 1065, 1072, 1079, 1086, 1093, 1100, 1107, 1114, 1124, 0}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, 0, 0, 1, 3, {379,388,397,406,417,428,439,446,453,460,0,0,0,0},{469,491,519,547,562,0,0,0,0,0},{266,71,583,591,0,0,0,0,0,0,0,0},{271,89,600,611,0,0,0,0,0},{623,639,652,666,0,0,0,0}}, + {4575, 57, 63, {1747, 1755, 1762, 1771, 1780, 1791, 1799}, {1807, 1810, 1813, 1816, 1819, 1822, 1825}, {1285, 1608, 1828, 1608, 1828, 1614, 1285}, {1830, 1837, 1845, 1851, 1857, 1861, 1866, 1871, 1878, 1888, 1896, 1905, 0}, {1830, 1837, 1845, 1851, 1857, 1861, 1866, 1871, 1878, 1888, 1896, 1905, 0}, {1914, 1918, 1922, 1927, 1857, 1931, 1935, 1939, 1943, 1947, 1951, 1955, 0}, {1914, 1918, 1922, 1927, 1857, 1931, 1935, 1939, 1943, 1947, 1951, 1955, 0}, 2, 1, 11, 3, {681,0,0,0,0,0,0,0,0,0,0,0,0,0},{798,721,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 5, 8, {2650, 2657, 2664, 2672, 2682, 2691, 2698}, {2707, 2711, 2715, 2719, 2723, 2727, 2731}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {1914, 1918, 2791, 1927, 2760, 1931, 1935, 1939, 1943, 2795, 1951, 2799, 0}, {1914, 1918, 2791, 1927, 2760, 1931, 1935, 1939, 1943, 2795, 1951, 2799, 0}, 2, 1, 1, 3, {18,9,857,2098,417,0,0,0,0,0,0,0,0,0},{1261,894,876,2462,0,0,0,0,0,0},{71,266,62,906,0,0,0,0,0,0,0,0},{89,271,77,914,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {1042, 13, 19, {2803, 2811, 2817, 2824, 2835, 2842, 2850}, {2858, 2863, 1712, 2868, 2874, 2879, 2884}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {34628, 4610, 4512, 5613, 11628, 5093, 5097, 4630, 5105, 34632, 4642, 4646, 0}, {34628, 4610, 4512, 5613, 11628, 5093, 5097, 4630, 5105, 34632, 4642, 4646, 0}, 0, 0, 1, 3, {18,864,9,1054,857,755,417,0,0,0,0,0,0,0},{1069,1124,0,0,0,0,0,0,0,0},{62,906,266,71,0,0,0,0,0,0,0,0},{77,914,271,89,0,0,0,0,0},{1193,0,0,0,0,0,0,0}}, + {109, 49, 52, {3410, 3419, 3425, 3431, 3440, 3446, 3455}, {3462, 2863, 1712, 3467, 3472, 3477, 3482}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {3487, 3495, 3504, 3509, 3515, 3519, 3524, 3532, 3538, 3548, 712, 3556, 0}, {3487, 3495, 3504, 3509, 3515, 3519, 3524, 3532, 3538, 3548, 712, 3556, 0}, {3566, 3572, 3504, 3579, 3515, 3519, 3584, 3532, 2986, 882, 887, 3590, 0}, {3566, 3572, 3504, 3579, 3515, 3519, 3584, 3532, 2986, 882, 887, 3590, 0}, 2, 1, 55, 3, {755,18,9,744,0,0,0,0,0,0,0,0,0,0},{1217,894,0,0,0,0,0,0,0,0},{71,266,1151,1243,1253,0,0,0,0,0,0,0},{89,271,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {4584, 49, 52, {4442, 4451, 4459, 4468, 4479, 4488, 4497}, {4504, 4508, 4512, 4516, 4520, 4524, 4528}, {1828, 1616, 1608, 1608, 4532, 2894, 1285}, {4534, 4542, 2910, 4551, 4558, 4565, 4572, 2933, 4579, 4589, 712, 4597, 0}, {4534, 4542, 2910, 4551, 4558, 4565, 4572, 2933, 4579, 4589, 712, 4597, 0}, {4606, 4610, 4512, 4614, 4618, 4622, 4626, 4630, 4634, 4638, 4642, 4646, 0}, {4606, 4610, 4512, 4614, 4618, 4622, 4626, 4630, 4634, 4638, 4642, 4646, 0}, 2, 1, 11, 3, {681,2785,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{4592,0,0,0,0,0,0,0}}, + {109, 151, 156, {4932, 4939, 4947, 4955, 4964, 4974, 4982}, {4991, 3081, 4994, 4997, 5000, 5003, 5006}, {5009, 1608, 1828, 2735, 1828, 2894, 5009}, {5011, 5019, 5028, 1639, 5034, 1649, 1654, 5038, 1666, 1676, 1684, 1693, 0}, {5011, 5019, 5028, 1639, 5034, 1649, 1654, 5038, 1666, 1676, 1684, 1693, 0}, {1702, 1707, 5047, 1717, 5034, 1722, 868, 1727, 1732, 1737, 887, 1742, 0}, {1702, 1707, 5047, 1717, 5034, 1722, 868, 1727, 1732, 1737, 887, 1742, 0}, 2, 1, 1, 3, {2764,1054,755,775,2024,417,0,0,0,0,0,0,0,0},{1217,968,894,1304,0,0,0,0,0,0},{266,71,4602,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {673, 1086, 1091, {1525, 7197, 34636, 1548, 1555, 1563, 34643}, {1578, 7225, 34651, 1591, 1595, 1599, 34655}, {1285, 1608, 1610, 1612, 1610, 1614, 1616}, {1618, 1625, 3504, 1639, 3515, 1649, 1654, 1659, 1666, 1676, 1684, 4420, 0}, {1618, 1625, 3504, 1639, 3515, 1649, 1654, 1659, 1666, 1676, 1684, 4420, 0}, {5089, 4610, 4512, 4614, 3515, 5093, 5097, 5101, 5105, 5109, 4642, 5113, 0}, {5089, 4610, 4512, 4614, 3515, 5093, 5097, 5101, 5105, 5109, 4642, 5113, 0}, 2, 1, 11, 3, {681,1455,0,0,0,0,0,0,0,0,0,0,0,0},{703,721,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {1042, 151, 156, {2803, 5440, 5454, 5467, 5480, 5493, 2850}, {2803, 34659, 34667, 34674, 34681, 34688, 2850}, {1828, 1285, 1610, 5530, 5530, 1285, 1285}, {5532, 5540, 5550, 666, 5557, 5562, 5568, 2933, 5574, 5583, 5591, 5600, 0}, {5532, 5540, 5550, 666, 5557, 5562, 5568, 2933, 5574, 5583, 5591, 5600, 0}, {5089, 5609, 4512, 5613, 3515, 5093, 5097, 4630, 4634, 5617, 4642, 5621, 0}, {5089, 5609, 4512, 5613, 3515, 5093, 5097, 4630, 4634, 5617, 4642, 5621, 0}, 2, 1, 1, 3, {18,681,744,9,775,755,406,428,417,460,4612,1690,0,0},{1124,1069,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{1193,0,0,0,0,0,0,0}}, + {109, 151, 156, {5792, 5802, 5807, 5814, 5823, 5827, 5834}, {34694, 29280, 2791, 34698, 34702, 34706, 34710}, {1828, 1616, 34715, 1816, 2892, 2894, 1285}, {5866, 5875, 5885, 5892, 3515, 5900, 5906, 1659, 5912, 5923, 5933, 5943, 0}, {5866, 5875, 5885, 5892, 3515, 5900, 5906, 1659, 5912, 5923, 5933, 5943, 0}, {5953, 1707, 1712, 1717, 3515, 5958, 5963, 1727, 2986, 882, 887, 1742, 0}, {5953, 1707, 1712, 1717, 3515, 5958, 5963, 1727, 2986, 882, 887, 1742, 0}, 0, 1, 11, 3, {681,304,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {4584, 161, 166, {5968, 5991, 211, 6014, 6025, 6040, 6055}, {6070, 285, 290, 295, 300, 305, 310}, {6075, 6078, 6075, 6081, 6084, 6078, 6081}, {6087, 6100, 360, 6115, 380, 6128, 6137, 401, 6146, 6163, 6178, 6191, 0}, {6206, 6219, 6234, 6245, 6258, 6265, 6274, 6283, 6298, 6315, 6330, 6343, 0}, {6358, 6366, 360, 6376, 380, 6128, 6137, 6384, 6392, 6402, 6410, 6420, 0}, {6358, 6366, 360, 6376, 380, 6128, 6137, 6384, 6392, 6402, 6410, 6420, 0}, 0, 1, 11, 3, {681,4621,0,0,0,0,0,0,0,0,0,0,0,0},{4638,4662,0,0,0,0,0,0,0,0},{266,0,0,0,0,0,0,0,0,0,0,0},{271,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 194, 197, {7189, 7197, 7205, 1548, 1555, 1563, 7212}, {7220, 7225, 7230, 1591, 7234, 1599, 7239}, {1285, 1608, 1610, 1612, 1610, 1614, 1616}, {5011, 5019, 3504, 1639, 1645, 1649, 1654, 7244, 1666, 1676, 1684, 1693, 0}, {5011, 5019, 3504, 1639, 1645, 1649, 1654, 7244, 1666, 1676, 1684, 1693, 0}, {1702, 1707, 3504, 1717, 1645, 1649, 1654, 1727, 1732, 1737, 887, 1742, 0}, {1702, 1707, 3504, 1717, 1645, 1649, 1654, 1727, 1732, 1737, 887, 1742, 0}, 2, 1, 55, 3, {744,304,0,0,0,0,0,0,0,0,0,0,0,0},{1217,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 49, 52, {7465, 34718, 7489, 7498, 7505, 7518, 7527}, {7465, 34718, 7489, 7498, 7505, 7518, 7527}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {7536, 7547, 7558, 7567, 7578, 7585, 7592, 7605, 7614, 7625, 7638, 7649, 0}, {7536, 7547, 7558, 7567, 7578, 7585, 7592, 7605, 7614, 7625, 7638, 7649, 0}, {7536, 7547, 7558, 7567, 7578, 7585, 7592, 7605, 7614, 7625, 7638, 7649, 0}, {7536, 7547, 7558, 7567, 7578, 7585, 7592, 7605, 7614, 7625, 7638, 7649, 0}, 0, 0, 1, 3, {857,3021,0,0,0,0,0,0,0,0,0,0,0,0},{1010,1029,0,0,0,0,0,0,0,0},{906,266,0,0,0,0,0,0,0,0,0,0},{914,271,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 1096, 1101, {34725, 34736, 34760, 34790, 34807, 34829, 34838}, {34849, 34853, 34860, 34867, 34871, 34878, 34882}, {4756, 4744, 4746, 4748, 4750, 4752, 4754}, {34886, 9875, 9888, 9897, 9908, 34899, 34908, 9929, 34917, 34934, 34949, 9981, 0}, {34962, 34975, 360, 34988, 380, 34999, 35008, 401, 35017, 35034, 35049, 35062, 0}, {35075, 489, 35082, 496, 380, 35089, 35096, 503, 35103, 517, 35110, 531, 0}, {35075, 489, 35082, 496, 380, 35089, 35096, 503, 35103, 517, 35110, 531, 0}, 0, 1, 11, 3, {681,775,2098,9,417,0,0,0,0,0,0,0,0,0},{894,1261,0,0,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {673, 313, 1106, {35117, 35126, 35138, 35147, 35154, 35163, 1247}, {12088, 12092, 35169, 12101, 35174, 35178, 12114}, {6516, 6518, 12118, 6522, 6522, 6518, 6522}, {1618, 1625, 12120, 12126, 1645, 8902, 8908, 12137, 1666, 1676, 12144, 1693, 0}, {12153, 12161, 12170, 12177, 5333, 12189, 12196, 12203, 6874, 12211, 12219, 6902, 0}, {5089, 4610, 12228, 4614, 1645, 5093, 5097, 12237, 5105, 5109, 12241, 6911, 0}, {5089, 4610, 12228, 4614, 1645, 5093, 5097, 12237, 5105, 5109, 12241, 6911, 0}, 2, 1, 124, 3, {692,4680,681,775,417,0,0,0,0,0,0,0,0,0},{798,721,692,0,0,0,0,0,0,0},{71,266,4689,4703,0,0,0,0,0,0,0,0},{89,4716,4733,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {925, 0, 0, {12950, 12957, 12472, 12968, 12490, 12977, 12988}, {12688, 12998, 12524, 12528, 12532, 13002, 13006}, {0, 0, 0, 0, 0, 0, 0}, {13010, 13020, 13029, 13037, 13046, 13059, 13071, 13078, 13085, 13092, 13102, 13114, 0}, {13010, 13020, 13029, 13037, 13046, 13059, 13071, 13078, 13085, 13092, 13102, 13114, 0}, {13127, 12776, 13131, 13135, 12664, 13139, 13143, 12672, 13147, 13151, 13155, 13159, 0}, {13127, 12776, 13131, 13135, 12664, 13139, 13143, 12672, 13147, 13151, 13155, 13159, 0}, 0, 0, 55, 3, {417,2861,0,0,0,0,0,0,0,0,0,0,0,0},{2979,2997,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2717,0,0,0,0,0,0,0}}, + {4749, 417, 422, {15236, 15248, 15259, 15273, 15285, 15295, 15305}, {15316, 15321, 15326, 15331, 15336, 15341, 15346}, {1285, 2894, 1608, 4532, 1828, 13432, 1616}, {15351, 15368, 15381, 15395, 15408, 15421, 15434, 15448, 15460, 15474, 15488, 15502, 0}, {15351, 15368, 15381, 15395, 15408, 15421, 15434, 15448, 15460, 15474, 15488, 15502, 0}, {15515, 15522, 15527, 15532, 15536, 15541, 15546, 15551, 15556, 15563, 15568, 15574, 0}, {15515, 15522, 15527, 15532, 15536, 15541, 15546, 15551, 15556, 15563, 15568, 15574, 0}, 2, 1, 55, 3, {417,1690,0,0,0,0,0,0,0,0,0,0,0,0},{4762,4786,0,0,0,0,0,0,0,0},{71,266,0,0,0,0,0,0,0,0,0,0},{89,271,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 151, 156, {15579, 15593, 15603, 15614, 15628, 15639, 15650}, {15663, 15668, 15673, 15680, 15686, 15692, 15698}, {1828, 1616, 1608, 5210, 1828, 9428, 1285}, {15703, 15711, 15719, 15726, 15735, 15745, 15755, 15761, 15769, 15784, 15802, 15810, 0}, {15703, 15711, 15719, 15726, 15735, 15745, 15755, 15761, 15769, 15784, 15802, 15810, 0}, {15818, 15822, 15719, 15828, 15832, 15837, 15755, 15843, 15848, 15855, 15862, 15867, 0}, {15818, 15822, 15719, 15828, 15832, 15837, 15755, 15843, 15848, 15855, 15862, 15867, 0}, 2, 0, 1, 3, {18,304,0,0,0,0,0,0,0,0,0,0,0,0},{1217,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 427, 430, {15872, 15877, 7673, 7680, 15883, 15890, 7697}, {15897, 15901, 7711, 7715, 15905, 7723, 7727}, {9428, 11804, 1285, 7731, 3099, 2892, 1285}, {7733, 7741, 15909, 1851, 7756, 1931, 15913, 15919, 1878, 1888, 1896, 15924, 0}, {7733, 7741, 15909, 1851, 7756, 1931, 15913, 15919, 1878, 1888, 1896, 15924, 0}, {1914, 1918, 15909, 1927, 7756, 1931, 1935, 15933, 1943, 1947, 1951, 13346, 0}, {1914, 1918, 15909, 1927, 7756, 1931, 1935, 15933, 1943, 1947, 1951, 13346, 0}, 0, 1, 1, 3, {2764,304,0,0,0,0,0,0,0,0,0,0,0,0},{1261,894,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {925, 1119, 1124, {35183, 35198, 35213, 35228, 35245, 16575, 35262}, {35273, 35280, 35287, 35294, 35301, 35308, 35315}, {35322, 16080, 6081, 6084, 6078, 16077, 16651}, {9864, 9875, 9888, 9897, 9908, 9915, 9922, 9929, 9942, 9957, 9970, 9981, 0}, {35325, 34975, 360, 34988, 380, 35336, 35343, 401, 35350, 35365, 35378, 35062, 0}, {9994, 10001, 10008, 10015, 9908, 9915, 9922, 10022, 10029, 10036, 10043, 10050, 0}, {9994, 10001, 10008, 10015, 9908, 9915, 9922, 10022, 10029, 10036, 10043, 10050, 0}, 0, 1, 1, 3, {406,2861,0,0,0,0,0,0,0,0,0,0,0,0},{3239,2997,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2717,0,0,0,0,0,0,0}}, + {1, 49, 52, {17199, 17218, 17237, 17262, 17281, 17315, 17340}, {17359, 17369, 17379, 17395, 17405, 17430, 17446}, {17456, 17460, 17467, 17471, 17478, 17485, 17492}, {17496, 17524, 17558, 17574, 17593, 17600, 17610, 17626, 17642, 17673, 17695, 17717, 0}, {17496, 17524, 17558, 17574, 17593, 17600, 17610, 17626, 17642, 17673, 17695, 17717, 0}, {17496, 17524, 17558, 17574, 17593, 17600, 17610, 17626, 17642, 17673, 17695, 17717, 0}, {17496, 17524, 17558, 17574, 17593, 17600, 17610, 17626, 17642, 17673, 17695, 17717, 0}, 0, 5, 55, 11, {755,1062,2098,744,417,0,0,0,0,0,0,0,0,0},{1261,894,0,0,0,0,0,0,0,0},{1151,1146,3258,3267,0,0,0,0,0,0,0,0},{1173,1165,3275,3287,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {1, 450, 465, {7465, 34718, 7489, 35389, 7505, 7518, 7527}, {17899, 17906, 17916, 17929, 17942, 17952, 17971}, {17993, 17997, 18004, 18011, 18021, 18028, 18041}, {7536, 7547, 7558, 7567, 35398, 7585, 7592, 7605, 7614, 7625, 7638, 7649, 0}, {7536, 7547, 7558, 7567, 35398, 7585, 7592, 7605, 7614, 7625, 7638, 7649, 0}, {18228, 18235, 18083, 18245, 18115, 18122, 18261, 18274, 18281, 18291, 18304, 18314, 0}, {18228, 18235, 18083, 18245, 18115, 18122, 18261, 18274, 18281, 18291, 18304, 18314, 0}, 0, 0, 55, 11, {755,1062,2098,744,417,0,0,0,0,0,0,0,0,0},{3298,894,0,0,0,0,0,0,0,0},{4804,62,266,71,0,0,0,0,0,0,0,0},{4812,77,271,89,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {109, 480, 505, {19431, 19450, 19472, 19497, 19513, 19535, 19554}, {19564, 19578, 19592, 19606, 19617, 19631, 19554}, {19645, 19652, 19659, 19666, 19673, 19680, 19687}, {19691, 19707, 19732, 19751, 19770, 19777, 19790, 19803, 19822, 19853, 19878, 19900, 0}, {19691, 19707, 19732, 19751, 19770, 19777, 19790, 19803, 19822, 19853, 19878, 19900, 0}, {19925, 19933, 19947, 19961, 19770, 19777, 19790, 19972, 19980, 19994, 20005, 20013, 0}, {19925, 19933, 19947, 19961, 19770, 19777, 19790, 19972, 19980, 19994, 20005, 20013, 0}, 0, 1, 55, 3, {2015,3021,0,0,0,0,0,0,0,0,0,0,0,0},{1010,1029,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {925, 367, 395, {28601, 28620, 28639, 28664, 28683, 28705, 28730}, {28749, 14586, 28759, 14609, 28775, 14632, 14648}, {28788, 14662, 28792, 14676, 28796, 14690, 14697}, {14701, 28803, 14736, 28831, 22641, 28850, 14788, 28860, 28876, 28907, 28929, 28954, 0}, {14701, 28803, 14736, 28831, 14771, 28850, 14788, 28860, 28876, 28907, 28929, 28954, 0}, {14701, 28803, 14736, 28831, 22641, 28850, 14788, 28860, 28876, 28907, 28929, 28954, 0}, {14701, 28803, 14736, 28831, 22641, 28850, 14788, 28860, 28876, 28907, 28929, 28954, 0}, 0, 0, 55, 3, {417,2861,0,0,0,0,0,0,0,0,0,0,0,0},{2979,2997,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2717,0,0,0,0,0,0,0}}, + {1, 977, 997, {31149, 31162, 31172, 28077, 31182, 31192, 31202}, {31212, 31219, 31226, 31233, 31240, 31247, 31254}, {28131, 28131, 28131, 28139, 31265, 28147, 31269}, {31273, 31280, 31293, 31306, 31319, 31332, 31339, 31349, 31359, 31375, 31388, 31398, 0}, {31273, 31280, 31293, 31306, 31319, 31332, 31339, 31349, 31359, 31375, 31388, 31398, 0}, {31273, 31411, 31418, 31425, 31432, 31332, 31439, 31446, 31453, 31460, 31467, 31474, 0}, {31273, 31411, 31418, 31425, 31432, 31332, 31439, 31446, 31453, 31460, 31467, 31474, 0}, 0, 0, 1, 3, {18,2796,0,0,0,0,0,0,0,0,0,0,0,0},{3945,1261,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {1, 1080, 1083, {34348, 34359, 34374, 34391, 34408, 34421, 34434}, {34348, 34359, 34374, 34391, 34408, 34421, 34434}, {34445, 34448, 34451, 34454, 34457, 10164, 10155}, {35403, 35414, 10194, 35427, 35438, 35447, 35458, 35469, 35480, 35493, 35506, 35519, 0}, {35403, 35414, 10194, 35427, 35438, 35447, 35458, 35469, 35480, 35493, 35506, 35519, 0}, {35403, 35414, 10194, 35427, 35438, 35447, 35458, 35469, 35480, 35493, 35506, 35519, 0}, {35403, 35414, 10194, 35427, 35438, 35447, 35458, 35469, 35480, 35493, 35506, 35519, 0}, 0, 6, 1, 3, {18,9,417,0,0,0,0,0,0,0,0,0,0,0},{2448,42,0,0,0,0,0,0,0,0},{62,71,0,0,0,0,0,0,0,0,0,0},{77,89,0,0,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {370, 25, 32, {897, 907, 917, 927, 937, 947, 957}, {34299, 34306, 34313, 34320, 34327, 34334, 34341}, {1016, 1020, 1024, 1028, 1032, 1036, 1040}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, 0, 0, 1, 3, {295,857,9,439,460,379,406,417,0,0,0,0,0,0},{469,1484,547,562,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{623,639,652,666,0,0,0,0}}, + {4575, 57, 63, {1747, 1755, 1762, 1771, 1780, 1791, 1799}, {1807, 1810, 1813, 1816, 1819, 1822, 1825}, {1285, 1608, 1828, 1608, 1828, 1614, 1285}, {35532, 1837, 1845, 1851, 1857, 1861, 1866, 1871, 1878, 1888, 1896, 1905, 0}, {35532, 1837, 1845, 1851, 1857, 1861, 1866, 1871, 1878, 1888, 1896, 1905, 0}, {35540, 1918, 1922, 1927, 1857, 1931, 1935, 1939, 1943, 1947, 1951, 1955, 0}, {35540, 1918, 1922, 1927, 1857, 1931, 1935, 1939, 1943, 1947, 1951, 1955, 0}, 2, 1, 11, 3, {681,0,0,0,0,0,0,0,0,0,0,0,0,0},{798,721,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 49, 52, {2650, 2657, 2664, 2672, 2682, 2691, 2698}, {35545, 35550, 35555, 35560, 35565, 35570, 35575}, {32793, 35580, 35583, 35587, 35590, 35594, 13568}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {13618, 13623, 26658, 13633, 2760, 13638, 13643, 13648, 13653, 35597, 13663, 26841, 0}, {13618, 13623, 26658, 13633, 2760, 13638, 13643, 13648, 13653, 35597, 13663, 26841, 0}, 0, 0, 1, 3, {2764,1054,857,295,9,18,968,1274,417,460,406,0,0,0},{876,894,0,0,0,0,0,0,0,0},{906,266,71,0,0,0,0,0,0,0,0,0},{914,271,89,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {1042, 13, 19, {2803, 2811, 2817, 2824, 2835, 2842, 2850}, {2858, 2863, 1712, 2868, 2874, 2879, 2884}, {1828, 1616, 1608, 2890, 2892, 2894, 1285}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 2986, 882, 887, 2992, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 2986, 882, 887, 2992, 0}, 2, 1, 1, 3, {18,9,1054,857,1062,755,775,417,0,0,0,0,0,0},{1069,1097,1124,0,0,0,0,0,0,0},{266,71,1146,1151,1157,0,0,0,0,0,0,0},{271,89,1165,1173,1182,0,0,0,0},{1193,0,0,0,0,0,0,0}}, + {109, 151, 156, {3410, 3419, 3425, 3431, 3440, 3446, 3455}, {3462, 2863, 1712, 3467, 3472, 3477, 3482}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {3487, 3495, 3504, 3509, 3515, 3519, 3524, 3532, 3538, 3548, 712, 3556, 0}, {3487, 3495, 3504, 3509, 3515, 3519, 3524, 3532, 3538, 3548, 712, 3556, 0}, {3566, 3572, 3504, 3579, 3515, 3519, 35602, 3532, 2986, 882, 887, 3590, 0}, {3566, 3572, 3504, 3579, 3515, 3519, 35602, 3532, 2986, 882, 887, 3590, 0}, 0, 0, 55, 3, {417,1690,755,4823,9,864,0,0,0,0,0,0,0,0},{894,304,0,0,0,0,0,0,0,0},{71,266,1151,1243,1253,0,0,0,0,0,0,0},{89,271,0,0,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {4749, 417, 422, {15236, 35609, 35621, 35636, 35647, 35658, 35669}, {15316, 15321, 15326, 15331, 15336, 15341, 15346}, {1285, 1608, 1828, 4532, 1828, 13432, 1616}, {15351, 15368, 15381, 15395, 15408, 15421, 15434, 15448, 15460, 15474, 15488, 15502, 0}, {15351, 15368, 15381, 15395, 15408, 15421, 15434, 15448, 15460, 15474, 15488, 15502, 0}, {15515, 15522, 15527, 15532, 15536, 15541, 15546, 15551, 15556, 15563, 15568, 15574, 0}, {15515, 15522, 15527, 15532, 15536, 15541, 15546, 15551, 15556, 15563, 15568, 15574, 0}, 2, 1, 11, 3, {1208,681,2098,417,0,0,0,0,0,0,0,0,0,0},{4832,4786,0,0,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {1, 1080, 1083, {34348, 34359, 34374, 34391, 34408, 34421, 34434}, {34348, 34359, 34374, 34391, 34408, 34421, 34434}, {34445, 34448, 34451, 34454, 34457, 10164, 10155}, {35403, 35414, 10194, 35427, 35438, 35447, 35458, 35469, 35480, 35493, 35506, 35519, 0}, {35403, 35414, 10194, 35427, 35438, 35447, 35458, 35469, 35480, 35493, 35506, 35519, 0}, {35403, 35414, 10194, 35427, 35438, 35447, 35458, 35469, 35480, 35493, 35506, 35519, 0}, {35403, 35414, 10194, 35427, 35438, 35447, 35458, 35469, 35480, 35493, 35506, 35519, 0}, 0, 6, 1, 3, {18,9,417,0,0,0,0,0,0,0,0,0,0,0},{2448,42,0,0,0,0,0,0,0,0},{62,71,0,0,0,0,0,0,0,0,0,0},{77,89,0,0,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {370, 25, 32, {897, 907, 917, 927, 937, 947, 957}, {967, 974, 981, 988, 995, 1002, 1009}, {1016, 1020, 1024, 1028, 1032, 1036, 1040}, {1044, 1051, 1058, 1065, 1072, 1079, 1086, 1093, 1100, 1107, 1114, 1124, 0}, {1044, 1051, 1058, 1065, 1072, 1079, 1086, 1093, 1100, 1107, 1114, 1124, 0}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, 0, 0, 1, 3, {295,857,9,439,460,379,406,417,0,0,0,0,0,0},{469,1484,547,4518,0,0,0,0,0,0},{583,591,266,71,0,0,0,0,0,0,0,0},{600,611,271,89,0,0,0,0,0},{623,639,652,0,0,0,0,0}}, + {4575, 57, 63, {1747, 1755, 1762, 1771, 1780, 1791, 1799}, {1807, 1810, 1813, 1816, 1819, 1822, 1825}, {1285, 1608, 1828, 1608, 1828, 1614, 1285}, {1830, 1837, 1845, 1851, 1857, 1861, 1866, 1871, 1878, 1888, 1896, 1905, 0}, {1830, 1837, 1845, 1851, 1857, 1861, 1866, 1871, 1878, 1888, 1896, 1905, 0}, {1914, 1918, 1922, 1927, 1857, 1931, 1935, 1939, 1943, 1947, 1951, 1955, 0}, {1914, 1918, 1922, 1927, 1857, 1931, 1935, 1939, 1943, 1947, 1951, 1955, 0}, 2, 1, 11, 3, {681,0,0,0,0,0,0,0,0,0,0,0,0,0},{798,721,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 49, 52, {2650, 2657, 2664, 2672, 2682, 2691, 2698}, {2707, 2711, 2715, 2719, 2723, 2727, 2731}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {1914, 1918, 2791, 1927, 2760, 1931, 1935, 1939, 1943, 2795, 1951, 2799, 0}, {1914, 1918, 2791, 1927, 2760, 1931, 1935, 1939, 1943, 2795, 1951, 2799, 0}, 0, 0, 55, 3, {417,18,9,857,1690,4858,968,2796,417,0,0,0,0,0},{997,3763,4866,1475,0,0,0,0,0,0},{906,62,71,266,0,0,0,0,0,0,0,0},{914,77,89,271,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {4877, 13, 19, {2803, 2811, 2817, 2824, 2835, 2842, 2850}, {2858, 2863, 1712, 2868, 2874, 2879, 2884}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 1732, 882, 887, 2992, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 1732, 882, 887, 2992, 0}, 0, 0, 1, 3, {2764,0,0,0,0,0,0,0,0,0,0,0,0,0},{4885,4913,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{355,0,0,0,0,0,0,0}}, + {4584, 49, 52, {3410, 3419, 3425, 3431, 3440, 3446, 3455}, {3462, 2863, 1712, 3467, 3472, 3477, 3482}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {3487, 3495, 3504, 3509, 3515, 3519, 3524, 3532, 3538, 3548, 712, 3556, 0}, {3487, 3495, 3504, 3509, 3515, 3519, 3524, 3532, 3538, 3548, 712, 3556, 0}, {3566, 3572, 3504, 3579, 3515, 3519, 3584, 3532, 2986, 882, 887, 3590, 0}, {3566, 3572, 3504, 3579, 3515, 3519, 3584, 3532, 2986, 882, 887, 3590, 0}, 2, 1, 11, 3, {681,304,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{4592,0,0,0,0,0,0,0}}, + {673, 49, 52, {6428, 6437, 6449, 6456, 6464, 6474, 6480}, {6487, 6491, 6495, 6499, 6503, 6508, 6512}, {1278, 1280, 35681, 1285, 1287, 1280, 1285}, {6527, 6537, 6546, 6554, 6562, 6570, 6577, 6584, 6592, 1364, 6598, 6606, 0}, {6615, 6625, 6634, 6642, 6650, 6658, 6665, 6672, 6681, 5385, 6687, 6697, 0}, {6706, 6710, 6715, 6720, 6724, 5419, 1501, 6728, 6732, 1517, 6736, 1521, 0}, {6706, 6710, 6715, 6720, 6724, 5419, 1501, 6728, 6732, 1517, 6736, 1521, 0}, 0, 1, 11, 3, {2181,4935,0,0,0,0,0,0,0,0,0,0,0,0},{2254,2225,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{4948,0,0,0,0,0,0,0}}, + {1, 1080, 1083, {34348, 34359, 34374, 34391, 34408, 34421, 34434}, {34348, 34359, 34374, 34391, 34408, 34421, 34434}, {34445, 34448, 34451, 34454, 34457, 10164, 10155}, {35683, 35694, 10194, 35705, 32620, 35716, 35725, 35738, 35480, 35493, 35506, 35519, 0}, {35683, 35694, 10194, 35705, 32620, 35716, 35725, 35738, 35480, 35493, 35506, 35519, 0}, {35683, 35694, 10194, 35705, 32620, 35716, 35725, 35738, 35480, 35493, 35506, 35519, 0}, {35683, 35694, 10194, 35705, 32620, 35716, 35725, 35738, 35480, 35493, 35506, 35519, 0}, 0, 6, 55, 3, {744,755,417,0,0,0,0,0,0,0,0,0,0,0},{2448,42,0,0,0,0,0,0,0,0},{266,71,62,0,0,0,0,0,0,0,0,0},{271,89,77,0,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {370, 25, 32, {897, 907, 917, 927, 937, 947, 957}, {34299, 34306, 34313, 34320, 34327, 34334, 34341}, {1016, 1020, 1024, 1028, 1032, 1036, 1040}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, 0, 0, 1, 3, {295,857,9,439,460,379,406,417,0,0,0,0,0,0},{469,4959,4986,764,547,4518,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{623,639,652,666,0,0,0,0}}, + {4575, 57, 63, {1747, 1755, 1762, 1771, 1780, 1791, 1799}, {1807, 1810, 1813, 1816, 1819, 1822, 1825}, {1285, 1608, 1828, 1608, 1828, 1614, 1285}, {1830, 1837, 1845, 1851, 1857, 1861, 1866, 1871, 1878, 1888, 1896, 1905, 0}, {1830, 1837, 1845, 1851, 1857, 1861, 1866, 1871, 1878, 1888, 1896, 1905, 0}, {1914, 1918, 1922, 1927, 1857, 1931, 1935, 1939, 1943, 1947, 1951, 1955, 0}, {1914, 1918, 1922, 1927, 1857, 1931, 1935, 1939, 1943, 1947, 1951, 1955, 0}, 2, 1, 11, 3, {681,0,0,0,0,0,0,0,0,0,0,0,0,0},{798,721,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {5002, 151, 156, {2650, 2657, 2664, 2672, 2682, 2691, 2698}, {2707, 2711, 2715, 2719, 2723, 2727, 2731}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {1914, 1918, 2791, 1927, 2760, 1931, 1935, 1939, 1943, 2795, 1951, 2799, 0}, {1914, 1918, 2791, 1927, 2760, 1931, 1935, 1939, 1943, 2795, 1951, 2799, 0}, 0, 0, 1, 3, {2764,0,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{5009,0,0,0,0,0,0,0}}, + {4877, 13, 19, {2803, 2811, 2817, 2824, 2835, 2842, 2850}, {2858, 2863, 1712, 2868, 2874, 2879, 2884}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 1732, 882, 887, 2992, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 1732, 882, 887, 2992, 0}, 0, 1, 1, 3, {295,304,0,0,0,0,0,0,0,0,0,0,0,0},{4885,4913,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{355,0,0,0,0,0,0,0}}, + {109, 49, 52, {3410, 3419, 3425, 3431, 3440, 3446, 3455}, {3462, 2863, 1712, 3467, 3472, 3477, 3482}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {3487, 3495, 3504, 3509, 3515, 3519, 3524, 3532, 3538, 3548, 712, 3556, 0}, {3487, 3495, 3504, 3509, 3515, 3519, 3524, 3532, 3538, 3548, 712, 3556, 0}, {3566, 3572, 3504, 3579, 3515, 3519, 3584, 3532, 2986, 882, 887, 3590, 0}, {3566, 3572, 3504, 3579, 3515, 3519, 3584, 3532, 2986, 882, 887, 3590, 0}, 2, 1, 1, 3, {18,304,0,0,0,0,0,0,0,0,0,0,0,0},{1217,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {673, 1129, 1140, {6428, 6437, 6449, 6456, 6464, 6474, 6480}, {6487, 6491, 6495, 6499, 6503, 6508, 6512}, {6516, 6518, 6520, 6522, 6524, 6518, 6522}, {1618, 1625, 11617, 1639, 1645, 1649, 1654, 8914, 35745, 35755, 35763, 35772, 0}, {1618, 1625, 11617, 1639, 1645, 1649, 1654, 8914, 35745, 35755, 35763, 35772, 0}, {5089, 4610, 4512, 4614, 1645, 5093, 5097, 35781, 5105, 5109, 4642, 6911, 0}, {5089, 4610, 4512, 4614, 1645, 5093, 5097, 35781, 5105, 5109, 4642, 6911, 0}, 0, 1, 11, 3, {2181,5019,0,0,0,0,0,0,0,0,0,0,0,0},{5034,2239,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{4948,0,0,0,0,0,0,0}}, + {1, 1080, 1083, {34348, 34359, 34374, 34391, 34408, 34421, 34434}, {34348, 34359, 34374, 34391, 34408, 34421, 34434}, {34445, 34448, 34451, 34454, 34457, 10164, 10155}, {35403, 35414, 10194, 35427, 32620, 35447, 35785, 35798, 35805, 35493, 35816, 35827, 0}, {35403, 35414, 10194, 35427, 32620, 35447, 35785, 35798, 35805, 35493, 35816, 35827, 0}, {35403, 35414, 10194, 35427, 32620, 35447, 35785, 35798, 35805, 35493, 35816, 35827, 0}, {35403, 35414, 10194, 35427, 32620, 35447, 35785, 35798, 35805, 35493, 35816, 35827, 0}, 0, 6, 55, 3, {744,755,417,0,0,0,0,0,0,0,0,0,0,0},{2448,42,0,0,0,0,0,0,0,0},{266,71,62,0,0,0,0,0,0,0,0,0},{271,89,77,0,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {5002, 151, 156, {2650, 2657, 2664, 2672, 2682, 2691, 2698}, {2707, 2711, 2715, 2719, 2723, 2727, 2731}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {1914, 1918, 2791, 1927, 2760, 1931, 1935, 1939, 1943, 2795, 1951, 2799, 0}, {1914, 1918, 2791, 1927, 2760, 1931, 1935, 1939, 1943, 2795, 1951, 2799, 0}, 2, 0, 1, 3, {18,304,0,0,0,0,0,0,0,0,0,0,0,0},{1217,894,0,0,0,0,0,0,0,0},{71,906,0,0,0,0,0,0,0,0,0,0},{89,914,0,0,0,0,0,0,0},{5009,0,0,0,0,0,0,0}}, + {5055, 13, 19, {2803, 2811, 2817, 2824, 2835, 2842, 2850}, {2858, 2863, 1712, 2868, 2874, 2879, 2884}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 1732, 882, 887, 2992, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 1732, 882, 887, 2992, 0}, 0, 0, 1, 3, {957,0,0,0,0,0,0,0,0,0,0,0,0,0},{4885,4913,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{5009,0,0,0,0,0,0,0}}, + {109, 49, 52, {3410, 3419, 3425, 3431, 3440, 3446, 3455}, {3462, 2863, 1712, 3467, 3472, 3477, 3482}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {3487, 3495, 3504, 3509, 3515, 3519, 3524, 3532, 3538, 3548, 712, 3556, 0}, {3487, 3495, 3504, 3509, 3515, 3519, 3524, 3532, 3538, 3548, 712, 3556, 0}, {3566, 3572, 3504, 3579, 3515, 3519, 3584, 3532, 2986, 882, 887, 3590, 0}, {3566, 3572, 3504, 3579, 3515, 3519, 3584, 3532, 2986, 882, 887, 3590, 0}, 2, 1, 1, 3, {18,304,0,0,0,0,0,0,0,0,0,0,0,0},{1217,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {673, 1148, 1160, {6428, 35838, 6449, 6456, 6464, 6474, 6480}, {8879, 5179, 35849, 35853, 8889, 8895, 35857}, {6516, 6518, 6520, 6522, 6524, 6518, 6522}, {1618, 1625, 11617, 1639, 1645, 5093, 5097, 8914, 35745, 35755, 35763, 35772, 0}, {1618, 1625, 11617, 1639, 1645, 5093, 5097, 8914, 35745, 35755, 35763, 35772, 0}, {1702, 1707, 11617, 1717, 1645, 5093, 5097, 8921, 2986, 1737, 887, 1742, 0}, {1702, 1707, 11617, 1717, 1645, 5093, 5097, 8921, 2986, 1737, 887, 1742, 0}, 0, 1, 11, 3, {2151,2181,0,0,0,0,0,0,0,0,0,0,0,0},{5034,2239,0,0,0,0,0,0,0,0},{71,1151,0,0,0,0,0,0,0,0,0,0},{89,1173,0,0,0,0,0,0,0},{4948,0,0,0,0,0,0,0}}, + {1, 1080, 1083, {34348, 34359, 34374, 34391, 34408, 34421, 34434}, {34348, 34359, 34374, 34391, 34408, 34421, 34434}, {34445, 34448, 34451, 34454, 34457, 10164, 10155}, {35683, 35694, 10194, 35705, 32620, 35716, 35725, 35738, 35480, 35493, 35506, 35519, 0}, {35683, 35694, 10194, 35705, 32620, 35716, 35725, 35738, 35480, 35493, 35506, 35519, 0}, {35683, 35694, 10194, 35705, 32620, 35716, 35725, 35738, 35480, 35493, 35506, 35519, 0}, {35683, 35694, 10194, 35705, 32620, 35716, 35725, 35738, 35480, 35493, 35506, 35519, 0}, 0, 0, 55, 3, {744,755,417,0,0,0,0,0,0,0,0,0,0,0},{2448,42,0,0,0,0,0,0,0,0},{266,71,62,0,0,0,0,0,0,0,0,0},{271,89,77,0,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {1, 49, 52, {2650, 2657, 2664, 2672, 2682, 2691, 2698}, {2707, 2711, 2715, 2719, 2723, 2727, 2731}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {1914, 1918, 2791, 1927, 2760, 1931, 1935, 1939, 1943, 2795, 1951, 2799, 0}, {1914, 1918, 2791, 1927, 2760, 1931, 1935, 1939, 1943, 2795, 1951, 2799, 0}, 0, 0, 1, 3, {406,2351,0,0,0,0,0,0,0,0,0,0,0,0},{2462,1261,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{5009,0,0,0,0,0,0,0}}, + {4877, 13, 19, {2803, 2811, 2817, 2824, 2835, 2842, 2850}, {2858, 2863, 1712, 2868, 2874, 2879, 2884}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 1732, 882, 887, 2992, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 1732, 882, 887, 2992, 0}, 0, 0, 1, 3, {295,304,0,0,0,0,0,0,0,0,0,0,0,0},{4885,4913,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{355,0,0,0,0,0,0,0}}, + {673, 1169, 1191, {35862, 35877, 35896, 35909, 35924, 35941, 7877}, {12299, 12307, 35952, 35958, 12331, 12339, 35964}, {315, 318, 35972, 324, 327, 318, 324}, {35975, 35988, 360, 369, 12370, 36003, 36010, 401, 36017, 36036, 36051, 36068, 0}, {35975, 35988, 360, 369, 12370, 36003, 36010, 401, 36017, 36036, 36051, 36068, 0}, {12395, 36085, 360, 6376, 12370, 36003, 36010, 6384, 12435, 6402, 36093, 36101, 0}, {12395, 36085, 360, 6376, 12370, 36003, 36010, 6384, 12435, 6402, 36093, 36101, 0}, 0, 1, 11, 3, {2151,2181,0,0,0,0,0,0,0,0,0,0,0,0},{721,2561,798,0,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {1, 1080, 1083, {34348, 34359, 34374, 34391, 34408, 34421, 34434}, {34348, 34359, 34374, 34391, 34408, 34421, 34434}, {34445, 34448, 34451, 34454, 34457, 10164, 10155}, {35403, 35414, 10194, 35427, 35438, 35447, 35458, 35469, 35480, 35493, 35506, 35519, 0}, {35403, 35414, 10194, 35427, 35438, 35447, 35458, 35469, 35480, 35493, 35506, 35519, 0}, {35403, 35414, 10194, 35427, 35438, 35447, 35458, 35469, 35480, 35493, 35506, 35519, 0}, {35403, 35414, 10194, 35427, 35438, 35447, 35458, 35469, 35480, 35493, 35506, 35519, 0}, 0, 6, 1, 3, {18,9,417,0,0,0,0,0,0,0,0,0,0,0},{2448,42,0,0,0,0,0,0,0,0},{62,71,266,0,0,0,0,0,0,0,0,0},{77,89,271,0,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {5002, 49, 52, {2650, 2657, 2664, 2672, 2682, 2691, 2698}, {2707, 2711, 2715, 2719, 2723, 2727, 2731}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {1914, 1918, 2791, 1927, 2760, 1931, 1935, 1939, 1943, 2795, 1951, 2799, 0}, {1914, 1918, 2791, 1927, 2760, 1931, 1935, 1939, 1943, 2795, 1951, 2799, 0}, 0, 0, 1, 3, {295,304,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{5009,0,0,0,0,0,0,0}}, + {4877, 13, 19, {2803, 2811, 2817, 2824, 2835, 2842, 2850}, {2858, 2863, 1712, 2868, 2874, 2879, 2884}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 2986, 882, 887, 2992, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 2986, 882, 887, 2992, 0}, 0, 0, 1, 3, {295,304,0,0,0,0,0,0,0,0,0,0,0,0},{4885,4913,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{355,0,0,0,0,0,0,0}}, + {109, 49, 52, {3410, 3419, 3425, 3431, 3440, 3446, 3455}, {3462, 2863, 1712, 3467, 3472, 3477, 3482}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {3487, 3495, 3504, 3509, 3515, 3519, 3524, 3532, 3538, 3548, 712, 3556, 0}, {3487, 3495, 3504, 3509, 3515, 3519, 3524, 3532, 3538, 3548, 712, 3556, 0}, {3566, 3572, 3504, 3579, 3515, 3519, 3584, 3532, 2986, 882, 887, 3590, 0}, {3566, 3572, 3504, 3579, 3515, 3519, 3584, 3532, 2986, 882, 887, 3590, 0}, 2, 1, 1, 3, {18,304,0,0,0,0,0,0,0,0,0,0,0,0},{1217,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {3316, 1207, 1225, {36109, 35877, 35896, 35909, 35924, 35941, 7877}, {36122, 36129, 36136, 36143, 36150, 36157, 36164}, {315, 318, 35972, 324, 327, 318, 324}, {35975, 35988, 360, 369, 12370, 12377, 12386, 401, 36017, 36036, 36051, 36068, 0}, {35975, 35988, 360, 369, 12370, 12377, 12386, 401, 36017, 36036, 36051, 36068, 0}, {35075, 36171, 35082, 496, 12370, 36003, 36010, 503, 510, 517, 36178, 36185, 0}, {35075, 36171, 35082, 496, 12370, 36003, 36010, 503, 510, 517, 36178, 36185, 0}, 0, 1, 11, 3, {1208,2098,692,681,4680,775,5063,417,0,0,0,0,0,0},{721,2561,798,0,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {1, 1080, 1083, {34348, 34359, 34374, 34391, 34408, 34421, 34434}, {34348, 34359, 34374, 34391, 34408, 34421, 34434}, {34445, 34448, 34451, 34454, 34457, 10164, 10155}, {35403, 35414, 10194, 35427, 35438, 35447, 35458, 35469, 35480, 35493, 35506, 35519, 0}, {35403, 35414, 10194, 35427, 35438, 35447, 35458, 35469, 35480, 35493, 35506, 35519, 0}, {35403, 35414, 10194, 35427, 35438, 35447, 35458, 35469, 35480, 35493, 35506, 35519, 0}, {35403, 35414, 10194, 35427, 35438, 35447, 35458, 35469, 35480, 35493, 35506, 35519, 0}, 0, 0, 1, 3, {18,9,417,0,0,0,0,0,0,0,0,0,0,0},{2448,42,0,0,0,0,0,0,0,0},{62,71,266,0,0,0,0,0,0,0,0,0},{77,89,271,0,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {4877, 13, 19, {2803, 2811, 2817, 2824, 2835, 2842, 2850}, {2858, 2863, 1712, 2868, 2874, 2879, 2884}, {29480, 36192, 29484, 29484, 36194, 36196, 6522}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 2986, 882, 887, 2992, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 2986, 882, 887, 2992, 0}, 0, 0, 1, 3, {2764,0,0,0,0,0,0,0,0,0,0,0,0,0},{4885,4913,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{355,0,0,0,0,0,0,0}}, + {109, 49, 52, {3410, 3419, 3425, 3431, 3440, 3446, 3455}, {3462, 2863, 1712, 3467, 3472, 3477, 3482}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {3487, 3495, 3504, 3509, 3515, 3519, 3524, 3532, 3538, 3548, 712, 3556, 0}, {3487, 3495, 3504, 3509, 3515, 3519, 3524, 3532, 3538, 3548, 712, 3556, 0}, {3566, 3572, 3504, 3579, 3515, 3519, 3584, 3532, 2986, 882, 887, 3590, 0}, {3566, 3572, 3504, 3579, 3515, 3519, 3584, 3532, 2986, 882, 887, 3590, 0}, 0, 0, 1, 3, {18,304,0,0,0,0,0,0,0,0,0,0,0,0},{1217,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {673, 1240, 1160, {8833, 35838, 6449, 8858, 6464, 6474, 6480}, {6487, 6491, 6495, 36198, 6503, 6508, 6512}, {6516, 6518, 6520, 6522, 6524, 6518, 6522}, {1618, 1625, 11617, 1639, 1645, 5093, 5097, 8914, 35745, 35755, 35763, 35772, 0}, {1618, 1625, 11617, 1639, 1645, 5093, 5097, 8914, 35745, 35755, 35763, 35772, 0}, {5089, 4610, 4512, 4614, 1645, 5093, 5097, 35781, 5105, 5109, 4642, 6911, 0}, {5089, 4610, 4512, 4614, 1645, 5093, 5097, 35781, 5105, 5109, 4642, 6911, 0}, 0, 1, 11, 11, {2151,2181,0,0,0,0,0,0,0,0,0,0,0,0},{5034,2239,0,0,0,0,0,0,0,0},{1151,0,0,0,0,0,0,0,0,0,0,0},{1173,0,0,0,0,0,0,0,0},{4948,0,0,0,0,0,0,0}}, + {5074, 88, 1250, {36202, 36213, 36225, 36237, 36245, 36256, 36270}, {36280, 36284, 1645, 36288, 36292, 36296, 15346}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {36301, 36319, 36333, 36349, 36365, 36378, 36390, 36404, 36417, 36433, 36449, 36464, 0}, {36301, 36319, 36333, 36349, 36365, 36378, 36390, 36404, 36417, 36433, 36449, 36464, 0}, {36479, 36485, 36492, 36501, 36510, 36516, 36521, 36528, 36534, 36543, 36552, 36560, 0}, {36479, 36485, 36492, 36501, 36510, 36516, 36521, 36528, 36534, 36543, 36552, 36560, 0}, 2, 1, 11, 3, {1208,681,2098,417,0,0,0,0,0,0,0,0,0,0},{5088,0,0,0,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {1, 1080, 1083, {34348, 34359, 34374, 34391, 34408, 34421, 34434}, {34348, 34359, 34374, 34391, 34408, 34421, 34434}, {34445, 34448, 34451, 34454, 34457, 10164, 10155}, {34460, 34484, 34493, 34502, 34513, 34522, 34535, 34544, 34549, 34560, 34582, 34606, 0}, {34460, 34484, 34493, 34502, 34513, 34522, 34535, 34544, 34549, 34560, 34582, 34606, 0}, {34460, 34484, 34493, 34502, 34513, 34522, 34535, 34544, 34549, 34560, 34582, 34606, 0}, {34460, 34484, 34493, 34502, 34513, 34522, 34535, 34544, 34549, 34560, 34582, 34606, 0}, 0, 6, 1, 3, {18,9,417,0,0,0,0,0,0,0,0,0,0,0},{2448,42,0,0,0,0,0,0,0,0},{62,71,266,0,0,0,0,0,0,0,0,0},{77,89,271,0,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {1, 49, 52, {2650, 2657, 2664, 2672, 2682, 2691, 2698}, {2707, 2711, 2715, 2719, 2723, 2727, 2731}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {1914, 1918, 2791, 1927, 2760, 1931, 1935, 1939, 1943, 2795, 1951, 2799, 0}, {1914, 1918, 2791, 1927, 2760, 1931, 1935, 1939, 1943, 2795, 1951, 2799, 0}, 0, 0, 1, 3, {18,2796,0,0,0,0,0,0,0,0,0,0,0,0},{2462,1261,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{5009,0,0,0,0,0,0,0}}, + {4877, 13, 19, {2803, 2811, 2817, 2824, 2835, 2842, 2850}, {2858, 2863, 1712, 2868, 2874, 2879, 2884}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {29296, 36568, 26702, 26708, 29316, 36576, 36582, 26731, 36588, 36598, 36606, 36616, 0}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 36626, 704, 2951, 2961, 0}, {36636, 13623, 26658, 26816, 36641, 13638, 13643, 26826, 26831, 35597, 13663, 36646, 0}, {36636, 13623, 26658, 26816, 36641, 13638, 13643, 26826, 26831, 35597, 13663, 36646, 0}, 0, 0, 1, 3, {2764,304,0,0,0,0,0,0,0,0,0,0,0,0},{4885,4913,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{355,0,0,0,0,0,0,0}}, + {109, 49, 52, {3410, 3419, 3425, 3431, 3440, 3446, 3455}, {3462, 2863, 1712, 3467, 3472, 3477, 3482}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {3487, 3495, 3504, 3509, 3515, 3519, 3524, 3532, 3538, 3548, 712, 3556, 0}, {3487, 3495, 3504, 3509, 3515, 3519, 3524, 3532, 3538, 3548, 712, 3556, 0}, {3566, 3572, 3504, 3579, 3515, 3519, 3584, 3532, 2986, 882, 887, 3590, 0}, {3566, 3572, 3504, 3579, 3515, 3519, 3584, 3532, 2986, 882, 887, 3590, 0}, 0, 0, 1, 3, {18,304,0,0,0,0,0,0,0,0,0,0,0,0},{1217,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {673, 1207, 1191, {36109, 35877, 35896, 6014, 35924, 35941, 7877}, {36122, 36129, 36136, 36651, 36150, 36157, 36164}, {315, 318, 35972, 324, 327, 318, 324}, {35975, 35988, 360, 369, 12370, 36003, 36010, 401, 36017, 36036, 36051, 36068, 0}, {35975, 35988, 360, 369, 12370, 36003, 36010, 401, 36017, 36036, 36051, 36068, 0}, {35075, 36171, 35082, 496, 12370, 36003, 36010, 503, 510, 517, 36178, 36185, 0}, {35075, 36171, 35082, 496, 12370, 36003, 36010, 503, 510, 517, 36178, 36185, 0}, 0, 1, 11, 3, {2181,0,0,0,0,0,0,0,0,0,0,0,0,0},{2225,2239,2254,0,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{4948,0,0,0,0,0,0,0}}, + {1, 1080, 1083, {34348, 34359, 34374, 34391, 34408, 34421, 34434}, {34348, 34359, 34374, 34391, 34408, 34421, 34434}, {34445, 34448, 34451, 34454, 34457, 10164, 10155}, {34460, 34484, 34493, 34502, 34513, 34522, 34535, 34544, 34549, 34560, 34582, 34606, 0}, {34460, 34484, 34493, 34502, 34513, 34522, 34535, 34544, 34549, 34560, 34582, 34606, 0}, {34460, 34484, 34493, 34502, 34513, 34522, 34535, 34544, 34549, 34560, 34582, 34606, 0}, {34460, 34484, 34493, 34502, 34513, 34522, 34535, 34544, 34549, 34560, 34582, 34606, 0}, 0, 6, 1, 3, {18,9,417,0,0,0,0,0,0,0,0,0,0,0},{2448,42,0,0,0,0,0,0,0,0},{62,71,266,0,0,0,0,0,0,0,0,0},{77,89,271,0,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {109, 49, 52, {2650, 2657, 2664, 2672, 2682, 2691, 2698}, {2707, 2711, 2715, 2719, 2723, 2727, 2731}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {1914, 1918, 2791, 1927, 2760, 1931, 1935, 1939, 1943, 2795, 1951, 2799, 0}, {1914, 1918, 2791, 1927, 2760, 1931, 1935, 1939, 1943, 2795, 1951, 2799, 0}, 0, 0, 1, 3, {18,304,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{5009,0,0,0,0,0,0,0}}, + {4877, 13, 19, {2803, 2811, 2817, 2824, 2835, 2842, 2850}, {2858, 2863, 1712, 2868, 2874, 2879, 2884}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 1732, 882, 887, 2992, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 1732, 882, 887, 2992, 0}, 0, 0, 1, 3, {295,304,0,0,0,0,0,0,0,0,0,0,0,0},{4885,4913,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{5106,0,0,0,0,0,0,0}}, + {109, 1254, 1259, {3410, 3419, 3425, 3431, 3440, 3446, 3455}, {3462, 2863, 1712, 3467, 3472, 3477, 3482}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {3487, 3495, 3504, 3509, 3515, 3519, 3524, 3532, 3538, 3548, 712, 3556, 0}, {3487, 3495, 3504, 3509, 3515, 3519, 3524, 3532, 3538, 3548, 712, 3556, 0}, {3566, 3572, 3504, 3579, 3515, 3519, 3584, 3532, 2986, 882, 887, 3590, 0}, {3566, 3572, 3504, 3579, 3515, 3519, 3584, 3532, 2986, 882, 887, 3590, 0}, 0, 1, 1, 3, {18,304,0,0,0,0,0,0,0,0,0,0,0,0},{1217,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {673, 1148, 1160, {6428, 35838, 6449, 6456, 6464, 6474, 6480}, {8879, 5179, 35849, 35853, 8889, 8895, 35857}, {6516, 6518, 6520, 6522, 6524, 6518, 6522}, {1618, 1625, 11617, 1639, 1645, 5093, 5097, 8914, 35745, 35755, 35763, 35772, 0}, {1618, 1625, 11617, 1639, 1645, 5093, 5097, 8914, 35745, 35755, 35763, 35772, 0}, {1702, 1707, 11617, 1717, 1645, 5093, 5097, 8921, 2986, 1737, 887, 1742, 0}, {1702, 1707, 11617, 1717, 1645, 5093, 5097, 8921, 2986, 1737, 887, 1742, 0}, 0, 1, 11, 11, {2151,2181,0,0,0,0,0,0,0,0,0,0,0,0},{5034,2239,0,0,0,0,0,0,0,0},{1151,0,0,0,0,0,0,0,0,0,0,0},{1173,0,0,0,0,0,0,0,0},{4948,0,0,0,0,0,0,0}}, + {1, 1080, 1083, {34348, 34359, 34374, 34391, 34408, 34421, 34434}, {34348, 34359, 34374, 34391, 34408, 34421, 34434}, {34445, 34448, 34451, 34454, 34457, 10164, 10155}, {34460, 34484, 34493, 34502, 34513, 34522, 34535, 34544, 34549, 34560, 34582, 34606, 0}, {34460, 34484, 34493, 34502, 34513, 34522, 34535, 34544, 34549, 34560, 34582, 34606, 0}, {34460, 34484, 34493, 34502, 34513, 34522, 34535, 34544, 34549, 34560, 34582, 34606, 0}, {34460, 34484, 34493, 34502, 34513, 34522, 34535, 34544, 34549, 34560, 34582, 34606, 0}, 0, 1, 1, 3, {18,9,417,0,0,0,0,0,0,0,0,0,0,0},{2448,42,0,0,0,0,0,0,0,0},{62,71,266,0,0,0,0,0,0,0,0,0},{77,89,271,0,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {1, 49, 52, {2650, 2657, 2664, 2672, 2682, 2691, 2698}, {2707, 2711, 2715, 2719, 2723, 2727, 2731}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {1914, 1918, 2791, 1927, 2760, 1931, 1935, 1939, 1943, 2795, 1951, 2799, 0}, {1914, 1918, 2791, 1927, 2760, 1931, 1935, 1939, 1943, 2795, 1951, 2799, 0}, 0, 0, 1, 3, {295,5116,0,0,0,0,0,0,0,0,0,0,0,0},{2462,1261,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{5009,0,0,0,0,0,0,0}}, + {4877, 13, 19, {2803, 2811, 2817, 2824, 2835, 2842, 2850}, {2858, 2863, 1712, 2868, 2874, 2879, 2884}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 1732, 882, 887, 2992, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 1732, 882, 887, 2992, 0}, 0, 1, 1, 3, {295,304,0,0,0,0,0,0,0,0,0,0,0,0},{4885,4913,0,0,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{355,0,0,0,0,0,0,0}}, + {109, 49, 52, {3410, 3419, 3425, 3431, 3440, 3446, 3455}, {3462, 2863, 1712, 3467, 3472, 3477, 3482}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {3487, 3495, 3504, 3509, 3515, 3519, 3524, 3532, 3538, 3548, 712, 3556, 0}, {3487, 3495, 3504, 3509, 3515, 3519, 3524, 3532, 3538, 3548, 712, 3556, 0}, {3566, 3572, 3504, 3579, 3515, 3519, 3584, 3532, 2986, 882, 887, 3590, 0}, {3566, 3572, 3504, 3579, 3515, 3519, 3584, 3532, 2986, 882, 887, 3590, 0}, 0, 0, 1, 3, {18,304,0,0,0,0,0,0,0,0,0,0,0,0},{1217,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {673, 1169, 1191, {35862, 35877, 35896, 35909, 35924, 35941, 7877}, {12299, 12307, 35952, 35958, 12331, 12339, 35964}, {315, 318, 35972, 324, 327, 318, 324}, {35975, 35988, 360, 369, 12370, 36003, 36010, 401, 36017, 36036, 36051, 36068, 0}, {35975, 35988, 360, 369, 12370, 36003, 36010, 401, 36017, 36036, 36051, 36068, 0}, {12395, 36085, 360, 6376, 12370, 36003, 36010, 6384, 12435, 6402, 36093, 36101, 0}, {12395, 36085, 360, 6376, 12370, 36003, 36010, 6384, 12435, 6402, 36093, 36101, 0}, 0, 1, 11, 3, {2151,2161,2169,2181,2193,2203,2213,5128,0,0,0,0,0,0},{2225,2239,2254,0,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{4948,0,0,0,0,0,0,0}}, + {1, 1080, 1083, {34348, 34359, 34374, 34391, 34408, 34421, 34434}, {34348, 34359, 34374, 34391, 34408, 34421, 34434}, {34445, 34448, 34451, 34454, 34457, 10164, 10155}, {35403, 35414, 10194, 35427, 35438, 35447, 35458, 35469, 35480, 35493, 35506, 35519, 0}, {35403, 35414, 10194, 35427, 35438, 35447, 35458, 35469, 35480, 35493, 35506, 35519, 0}, {35403, 35414, 10194, 35427, 35438, 35447, 35458, 35469, 35480, 35493, 35506, 35519, 0}, {35403, 35414, 10194, 35427, 35438, 35447, 35458, 35469, 35480, 35493, 35506, 35519, 0}, 0, 6, 1, 3, {18,9,417,0,0,0,0,0,0,0,0,0,0,0},{2448,42,0,0,0,0,0,0,0,0},{62,71,266,0,0,0,0,0,0,0,0,0},{77,89,271,0,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {109, 49, 52, {2650, 2657, 2664, 2672, 2682, 2691, 2698}, {2707, 2711, 2715, 2719, 2723, 2727, 2731}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {1914, 1918, 2791, 1927, 2760, 1931, 1935, 1939, 1943, 2795, 1951, 2799, 0}, {1914, 1918, 2791, 1927, 2760, 1931, 1935, 1939, 1943, 2795, 1951, 2799, 0}, 0, 0, 1, 3, {18,304,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{5009,0,0,0,0,0,0,0}}, + {4877, 13, 19, {2803, 2811, 2817, 2824, 2835, 2842, 2850}, {2858, 2863, 1712, 2868, 2874, 2879, 2884}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 2986, 882, 887, 2992, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 2986, 882, 887, 2992, 0}, 0, 1, 55, 3, {744,0,0,0,0,0,0,0,0,0,0,0,0,0},{4885,4913,0,0,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{5106,0,0,0,0,0,0,0}}, + {109, 49, 52, {3410, 3419, 3425, 3431, 3440, 3446, 3455}, {3462, 2863, 1712, 3467, 3472, 3477, 3482}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {3487, 3495, 3504, 3509, 3515, 3519, 3524, 3532, 3538, 3548, 712, 3556, 0}, {3487, 3495, 3504, 3509, 3515, 3519, 3524, 3532, 3538, 3548, 712, 3556, 0}, {3566, 3572, 3504, 3579, 3515, 3519, 3584, 3532, 2986, 882, 887, 3590, 0}, {3566, 3572, 3504, 3579, 3515, 3519, 3584, 3532, 2986, 882, 887, 3590, 0}, 0, 0, 1, 3, {18,304,0,0,0,0,0,0,0,0,0,0,0,0},{1217,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {1, 1080, 1083, {34348, 34359, 34374, 34391, 34408, 34421, 34434}, {34348, 34359, 34374, 34391, 34408, 34421, 34434}, {34445, 34448, 34451, 34454, 34457, 10164, 10155}, {35403, 35414, 10194, 35427, 35438, 35447, 35458, 35469, 35480, 35493, 35506, 35519, 0}, {35403, 35414, 10194, 35427, 35438, 35447, 35458, 35469, 35480, 35493, 35506, 35519, 0}, {35403, 35414, 10194, 35427, 35438, 35447, 35458, 35469, 35480, 35493, 35506, 35519, 0}, {35403, 35414, 10194, 35427, 35438, 35447, 35458, 35469, 35480, 35493, 35506, 35519, 0}, 0, 6, 1, 3, {18,9,417,0,0,0,0,0,0,0,0,0,0,0},{2448,42,0,0,0,0,0,0,0,0},{62,71,266,0,0,0,0,0,0,0,0,0},{77,89,271,0,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {4877, 13, 19, {2803, 2811, 2817, 2824, 2835, 2842, 2850}, {2858, 2863, 1712, 2868, 2874, 2879, 2884}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {29296, 36568, 26702, 26708, 29316, 36576, 36582, 26731, 36588, 36598, 36606, 36616, 0}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 36626, 704, 2951, 2961, 0}, {36636, 13623, 26658, 26816, 36641, 13638, 13643, 26826, 26831, 35597, 13663, 36646, 0}, {36636, 13623, 26658, 26816, 36641, 13638, 13643, 26826, 26831, 35597, 13663, 36646, 0}, 0, 1, 1, 3, {295,304,0,0,0,0,0,0,0,0,0,0,0,0},{4885,4913,0,0,0,0,0,0,0,0},{266,71,62,906,0,0,0,0,0,0,0,0},{271,89,77,914,0,0,0,0,0},{355,0,0,0,0,0,0,0}}, + {109, 151, 156, {3410, 3419, 3425, 3431, 3440, 3446, 3455}, {3462, 2863, 1712, 3467, 3472, 3477, 3482}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {3487, 3495, 3504, 3509, 3515, 3519, 3524, 3532, 3538, 3548, 712, 3556, 0}, {3487, 3495, 3504, 3509, 3515, 3519, 3524, 3532, 3538, 3548, 712, 3556, 0}, {1702, 36658, 1712, 3579, 3515, 36664, 3584, 3532, 2986, 882, 887, 3590, 0}, {1702, 36658, 1712, 3579, 3515, 36664, 3584, 3532, 2986, 882, 887, 3590, 0}, 0, 6, 1, 3, {18,304,0,0,0,0,0,0,0,0,0,0,0,0},{1217,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {1, 1080, 1083, {34348, 34359, 34374, 34391, 34408, 34421, 34434}, {34348, 34359, 34374, 34391, 34408, 34421, 34434}, {34445, 34448, 34451, 34454, 34457, 10164, 10155}, {35403, 35414, 10194, 35427, 35438, 35447, 35458, 35469, 35480, 35493, 35506, 35519, 0}, {35403, 35414, 10194, 35427, 35438, 35447, 35458, 35469, 35480, 35493, 35506, 35519, 0}, {35403, 35414, 10194, 35427, 35438, 35447, 35458, 35469, 35480, 35493, 35506, 35519, 0}, {35403, 35414, 10194, 35427, 35438, 35447, 35458, 35469, 35480, 35493, 35506, 35519, 0}, 0, 6, 1, 3, {18,9,417,0,0,0,0,0,0,0,0,0,0,0},{2448,42,0,0,0,0,0,0,0,0},{62,71,266,0,0,0,0,0,0,0,0,0},{77,89,271,0,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {5002, 49, 52, {2650, 2657, 2664, 2672, 2682, 2691, 2698}, {2707, 2711, 2715, 2719, 2723, 2727, 2731}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {1914, 1918, 2791, 1927, 2760, 1931, 1935, 1939, 1943, 2795, 1951, 2799, 0}, {1914, 1918, 2791, 1927, 2760, 1931, 1935, 1939, 1943, 2795, 1951, 2799, 0}, 0, 0, 1, 3, {295,304,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{5009,0,0,0,0,0,0,0}}, + {4877, 13, 19, {2803, 2811, 2817, 2824, 2835, 2842, 2850}, {2858, 2863, 1712, 2868, 2874, 2879, 2884}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 2986, 882, 887, 2992, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 2986, 882, 887, 2992, 0}, 0, 0, 1, 3, {295,304,0,0,0,0,0,0,0,0,0,0,0,0},{4885,4913,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{355,0,0,0,0,0,0,0}}, + {109, 49, 52, {3410, 3419, 3425, 3431, 3440, 3446, 3455}, {3462, 2863, 1712, 3467, 3472, 3477, 3482}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {3487, 3495, 3504, 3509, 3515, 3519, 3524, 3532, 3538, 3548, 712, 3556, 0}, {3487, 3495, 3504, 3509, 3515, 3519, 3524, 3532, 3538, 3548, 712, 3556, 0}, {3566, 3572, 3504, 3579, 3515, 3519, 3584, 3532, 2986, 882, 887, 3590, 0}, {3566, 3572, 3504, 3579, 3515, 3519, 3584, 3532, 2986, 882, 887, 3590, 0}, 0, 0, 1, 3, {18,304,0,0,0,0,0,0,0,0,0,0,0,0},{1217,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {1, 1080, 1083, {34348, 34359, 34374, 34391, 34408, 34421, 34434}, {34348, 34359, 34374, 34391, 34408, 34421, 34434}, {34445, 34448, 34451, 34454, 34457, 10164, 10155}, {35403, 35414, 10194, 35427, 35438, 35447, 35458, 35469, 35480, 35493, 35506, 35519, 0}, {35403, 35414, 10194, 35427, 35438, 35447, 35458, 35469, 35480, 35493, 35506, 35519, 0}, {35403, 35414, 10194, 35427, 35438, 35447, 35458, 35469, 35480, 35493, 35506, 35519, 0}, {35403, 35414, 10194, 35427, 35438, 35447, 35458, 35469, 35480, 35493, 35506, 35519, 0}, 0, 6, 1, 3, {18,9,417,0,0,0,0,0,0,0,0,0,0,0},{2448,42,0,0,0,0,0,0,0,0},{62,71,266,0,0,0,0,0,0,0,0,0},{77,89,271,0,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {109, 49, 52, {2650, 2657, 2664, 2672, 2682, 2691, 2698}, {2707, 2711, 2715, 2719, 2723, 2727, 2731}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {1914, 1918, 2791, 1927, 2760, 1931, 1935, 1939, 1943, 2795, 1951, 2799, 0}, {1914, 1918, 2791, 1927, 2760, 1931, 1935, 1939, 1943, 2795, 1951, 2799, 0}, 0, 0, 55, 3, {744,755,1062,2098,417,0,0,0,0,0,0,0,0,0},{1261,894,0,0,0,0,0,0,0,0},{71,266,62,0,0,0,0,0,0,0,0,0},{89,271,4812,77,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {4877, 13, 19, {2803, 2811, 2817, 2824, 2835, 2842, 2850}, {2858, 2863, 1712, 2868, 2874, 2879, 2884}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 1732, 882, 887, 2992, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 1732, 882, 887, 2992, 0}, 0, 0, 1, 3, {295,304,0,0,0,0,0,0,0,0,0,0,0,0},{4885,4913,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{355,0,0,0,0,0,0,0}}, + {109, 49, 52, {2650, 2657, 2664, 2672, 2682, 2691, 2698}, {2707, 2711, 2715, 2719, 2723, 2727, 2731}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {1914, 1918, 2791, 1927, 2760, 1931, 1935, 1939, 1943, 2795, 1951, 2799, 0}, {1914, 1918, 2791, 1927, 2760, 1931, 1935, 1939, 1943, 2795, 1951, 2799, 0}, 0, 1, 1, 3, {295,857,18,9,417,0,0,0,0,0,0,0,0,0},{1010,1029,0,0,0,0,0,0,0,0},{906,62,266,71,0,0,0,0,0,0,0,0},{914,77,271,89,0,0,0,0,0},{98,734,0,0,0,0,0,0}}, + {4877, 13, 19, {2803, 2811, 2817, 2824, 2835, 2842, 2850}, {2858, 2863, 1712, 2868, 2874, 2879, 2884}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 1732, 882, 887, 2992, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 1732, 882, 887, 2992, 0}, 0, 0, 1, 3, {295,304,0,0,0,0,0,0,0,0,0,0,0,0},{4885,4913,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{355,0,0,0,0,0,0,0}}, + {109, 49, 52, {2650, 2657, 2664, 2672, 2682, 2691, 2698}, {2707, 2711, 2715, 2719, 2723, 2727, 2731}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {2737, 2745, 2754, 1851, 2760, 2764, 2769, 1871, 1878, 2774, 1896, 2782, 0}, {1914, 1918, 2791, 1927, 2760, 1931, 1935, 1939, 1943, 2795, 1951, 2799, 0}, {1914, 1918, 2791, 1927, 2760, 1931, 1935, 1939, 1943, 2795, 1951, 2799, 0}, 0, 0, 1, 3, {295,304,0,0,0,0,0,0,0,0,0,0,0,0},{876,894,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{5009,0,0,0,0,0,0,0}}, + {4877, 13, 19, {2803, 2811, 2817, 2824, 2835, 2842, 2850}, {2858, 2863, 1712, 2868, 2874, 2879, 2884}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 1732, 882, 887, 2992, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 1732, 882, 887, 2992, 0}, 0, 0, 1, 3, {295,304,0,0,0,0,0,0,0,0,0,0,0,0},{5140,5168,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{355,0,0,0,0,0,0,0}}, + {4877, 13, 19, {2803, 2811, 2817, 2824, 2835, 2842, 2850}, {2858, 2863, 1712, 2868, 2874, 2879, 2884}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 1732, 882, 887, 2992, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 1732, 882, 887, 2992, 0}, 0, 0, 1, 3, {295,304,0,0,0,0,0,0,0,0,0,0,0,0},{4885,4913,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{355,0,0,0,0,0,0,0}}, + {5055, 13, 19, {2803, 2811, 2817, 2824, 2835, 2842, 2850}, {2858, 2863, 1712, 2868, 2874, 2879, 2884}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 1732, 882, 887, 2992, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 1732, 882, 887, 2992, 0}, 0, 0, 1, 3, {957,0,0,0,0,0,0,0,0,0,0,0,0,0},{4885,4913,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{5009,0,0,0,0,0,0,0}}, + {925, 13, 19, {2803, 2811, 2817, 2824, 2835, 2842, 2850}, {2858, 2863, 1712, 2868, 2874, 2879, 2884}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 1732, 882, 887, 2992, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 1732, 882, 887, 2992, 0}, 0, 0, 1, 3, {932,941,948,957,460,417,968,0,0,0,0,0,0,0},{3763,3783,42,2448,0,0,0,0,0,0},{906,62,266,71,0,0,0,0,0,0,0,0},{914,77,271,89,0,0,0,0,0},{1193,0,0,0,0,0,0,0}}, + {4877, 151, 156, {2803, 2811, 2817, 2824, 2835, 2842, 2850}, {2858, 2863, 1712, 2868, 2874, 2879, 2884}, {1828, 1616, 1608, 1608, 2892, 2894, 1285}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2896, 2902, 2910, 666, 2916, 2921, 2927, 2933, 2940, 704, 2951, 2961, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 1732, 882, 887, 2992, 0}, {2971, 1707, 1712, 863, 2976, 1722, 868, 2981, 1732, 882, 887, 2992, 0}, 0, 0, 1, 3, {295,304,0,0,0,0,0,0,0,0,0,0,0,0},{4885,4913,0,0,0,0,0,0,0,0},{906,71,0,0,0,0,0,0,0,0,0,0},{914,89,0,0,0,0,0,0,0},{355,0,0,0,0,0,0,0}}, + {3316, 1207, 1225, {36109, 35877, 35896, 35909, 35924, 35941, 7877}, {36122, 36129, 36136, 36143, 36150, 36157, 36164}, {315, 318, 35972, 324, 327, 318, 324}, {35975, 35988, 360, 369, 12370, 12377, 12386, 401, 36017, 36036, 36051, 36068, 0}, {35975, 35988, 360, 369, 12370, 12377, 12386, 401, 36017, 36036, 36051, 36068, 0}, {35075, 36171, 35082, 496, 12370, 36003, 36010, 503, 510, 517, 36178, 36185, 0}, {35075, 36171, 35082, 496, 12370, 36003, 36010, 503, 510, 517, 36178, 36185, 0}, 0, 1, 11, 3, {1208,2098,692,681,4680,775,5063,417,0,0,0,0,0,0},{721,2561,798,0,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {673, 1129, 1140, {6428, 6437, 6449, 6456, 6464, 6474, 6480}, {6487, 6491, 6495, 6499, 6503, 6508, 6512}, {6516, 6518, 6520, 6522, 6524, 6518, 6522}, {1618, 1625, 11617, 1639, 1645, 1649, 1654, 8914, 35745, 35755, 35763, 35772, 0}, {1618, 1625, 11617, 1639, 1645, 1649, 1654, 8914, 35745, 35755, 35763, 35772, 0}, {5089, 4610, 4512, 4614, 1645, 5093, 5097, 35781, 5105, 5109, 4642, 6911, 0}, {5089, 4610, 4512, 4614, 1645, 5093, 5097, 35781, 5105, 5109, 4642, 6911, 0}, 0, 1, 11, 3, {2181,5019,0,0,0,0,0,0,0,0,0,0,0,0},{5034,2239,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{4948,0,0,0,0,0,0,0}}, + {673, 1207, 1191, {36109, 35877, 35896, 6014, 35924, 35941, 7877}, {36122, 36129, 36136, 36651, 36150, 36157, 36164}, {315, 318, 35972, 324, 327, 318, 324}, {35975, 35988, 360, 369, 12370, 36003, 36010, 401, 36017, 36036, 36051, 36068, 0}, {35975, 35988, 360, 369, 12370, 36003, 36010, 401, 36017, 36036, 36051, 36068, 0}, {35075, 36171, 35082, 496, 12370, 36003, 36010, 503, 510, 517, 36178, 36185, 0}, {35075, 36171, 35082, 496, 12370, 36003, 36010, 503, 510, 517, 36178, 36185, 0}, 0, 1, 11, 3, {2181,0,0,0,0,0,0,0,0,0,0,0,0,0},{2225,2239,2254,0,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{4948,0,0,0,0,0,0,0}}, + {673, 1240, 1160, {8833, 35838, 6449, 8858, 6464, 6474, 6480}, {6487, 6491, 6495, 36198, 6503, 6508, 6512}, {6516, 6518, 6520, 6522, 6524, 6518, 6522}, {1618, 1625, 11617, 1639, 1645, 5093, 5097, 8914, 35745, 35755, 35763, 35772, 0}, {1618, 1625, 11617, 1639, 1645, 5093, 5097, 8914, 35745, 35755, 35763, 35772, 0}, {5089, 4610, 4512, 4614, 1645, 5093, 5097, 35781, 5105, 5109, 4642, 6911, 0}, {5089, 4610, 4512, 4614, 1645, 5093, 5097, 35781, 5105, 5109, 4642, 6911, 0}, 0, 1, 11, 11, {2151,2181,0,0,0,0,0,0,0,0,0,0,0,0},{5034,2239,0,0,0,0,0,0,0,0},{1151,0,0,0,0,0,0,0,0,0,0,0},{1173,0,0,0,0,0,0,0,0},{4948,0,0,0,0,0,0,0}}, + {5074, 88, 1250, {36202, 36213, 36225, 36237, 36245, 36256, 36270}, {36280, 36284, 1645, 36288, 36292, 36296, 15346}, {1285, 1608, 1610, 2735, 1610, 1614, 1285}, {36301, 36319, 36333, 36349, 36365, 36378, 36390, 36404, 36417, 36433, 36449, 36464, 0}, {36301, 36319, 36333, 36349, 36365, 36378, 36390, 36404, 36417, 36433, 36449, 36464, 0}, {36479, 36485, 36492, 36501, 36510, 36516, 36521, 36528, 36534, 36543, 36552, 36560, 0}, {36479, 36485, 36492, 36501, 36510, 36516, 36521, 36528, 36534, 36543, 36552, 36560, 0}, 2, 1, 11, 3, {1208,681,2098,417,0,0,0,0,0,0,0,0,0,0},{5088,0,0,0,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {109, 1096, 1101, {34725, 34736, 34760, 34790, 34807, 34829, 34838}, {34849, 34853, 34860, 34867, 34871, 34878, 34882}, {4756, 4744, 4746, 4748, 4750, 4752, 4754}, {34886, 9875, 9888, 9897, 9908, 34899, 34908, 9929, 34917, 34934, 34949, 9981, 0}, {34962, 34975, 360, 34988, 380, 34999, 35008, 401, 35017, 35034, 35049, 35062, 0}, {35075, 489, 35082, 496, 380, 35089, 35096, 503, 35103, 517, 35110, 531, 0}, {35075, 489, 35082, 496, 380, 35089, 35096, 503, 35103, 517, 35110, 531, 0}, 0, 1, 11, 3, {681,775,2098,9,417,0,0,0,0,0,0,0,0,0},{894,1261,0,0,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {370, 25, 32, {897, 907, 917, 927, 937, 947, 957}, {967, 974, 981, 988, 995, 1002, 1009}, {1016, 1020, 1024, 1028, 1032, 1036, 1040}, {1044, 1051, 1058, 1065, 1072, 1079, 1086, 1093, 1100, 1107, 1114, 1124, 0}, {1044, 1051, 1058, 1065, 1072, 1079, 1086, 1093, 1100, 1107, 1114, 1124, 0}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, 0, 0, 1, 3, {379,388,397,406,417,428,439,446,453,460,0,0,0,0},{469,491,519,547,562,0,0,0,0,0},{266,71,583,591,0,0,0,0,0,0,0,0},{271,89,600,611,0,0,0,0,0},{623,639,652,666,0,0,0,0}}, + {673, 1086, 1091, {1525, 7197, 34636, 1548, 1555, 1563, 34643}, {1578, 7225, 34651, 1591, 1595, 1599, 34655}, {1285, 1608, 1610, 1612, 1610, 1614, 1616}, {1618, 1625, 3504, 1639, 3515, 1649, 1654, 1659, 1666, 1676, 1684, 4420, 0}, {1618, 1625, 3504, 1639, 3515, 1649, 1654, 1659, 1666, 1676, 1684, 4420, 0}, {5089, 4610, 4512, 4614, 3515, 5093, 5097, 5101, 5105, 5109, 4642, 5113, 0}, {5089, 4610, 4512, 4614, 3515, 5093, 5097, 5101, 5105, 5109, 4642, 5113, 0}, 2, 1, 11, 3, {681,1455,0,0,0,0,0,0,0,0,0,0,0,0},{703,721,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {673, 1129, 1140, {6428, 6437, 6449, 6456, 6464, 6474, 6480}, {6487, 6491, 6495, 6499, 6503, 6508, 6512}, {6516, 6518, 6520, 6522, 6524, 6518, 6522}, {1618, 1625, 11617, 1639, 1645, 1649, 1654, 8914, 35745, 35755, 35763, 35772, 0}, {1618, 1625, 11617, 1639, 1645, 1649, 1654, 8914, 35745, 35755, 35763, 35772, 0}, {5089, 4610, 4512, 4614, 1645, 5093, 5097, 35781, 5105, 5109, 4642, 6911, 0}, {5089, 4610, 4512, 4614, 1645, 5093, 5097, 35781, 5105, 5109, 4642, 6911, 0}, 0, 1, 11, 3, {2181,5019,0,0,0,0,0,0,0,0,0,0,0,0},{5034,2239,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{4948,0,0,0,0,0,0,0}}, + {109, 49, 52, {11414, 11420, 11434, 11457, 11471, 11487, 11494}, {11503, 11506, 11511, 11517, 11521, 11526, 11529}, {4756, 4744, 4746, 4748, 4750, 4752, 4754}, {11533, 11540, 7354, 11547, 2760, 11553, 11559, 11565, 11572, 11581, 11589, 11596, 0}, {11603, 11610, 11617, 11622, 11628, 11632, 11637, 11642, 11649, 11658, 11666, 11673, 0}, {11680, 5609, 4512, 4614, 11628, 11684, 11688, 11692, 11696, 5109, 11700, 11704, 0}, {11680, 5609, 4512, 4614, 11628, 11684, 11688, 11692, 11696, 5109, 11700, 11704, 0}, 0, 1, 11, 3, {681,304,0,0,0,0,0,0,0,0,0,0,0,0},{2843,894,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {925, 1119, 1124, {35183, 35198, 35213, 35228, 35245, 16575, 35262}, {35273, 35280, 35287, 35294, 35301, 35308, 35315}, {35322, 16080, 6081, 6084, 6078, 16077, 16651}, {9864, 9875, 9888, 9897, 9908, 9915, 9922, 9929, 9942, 9957, 9970, 9981, 0}, {35325, 34975, 360, 34988, 380, 35336, 35343, 401, 35350, 35365, 35378, 35062, 0}, {9994, 10001, 10008, 10015, 9908, 9915, 9922, 10022, 10029, 10036, 10043, 10050, 0}, {9994, 10001, 10008, 10015, 9908, 9915, 9922, 10022, 10029, 10036, 10043, 10050, 0}, 0, 1, 1, 3, {406,2861,0,0,0,0,0,0,0,0,0,0,0,0},{3239,2997,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2717,0,0,0,0,0,0,0}}, + {925, 651, 656, {22902, 22909, 22920, 22933, 22946, 22957, 22970}, {22981, 22986, 22991, 22996, 23001, 23006, 23011}, {22981, 22986, 22991, 22996, 23001, 23006, 23011}, {23016, 23042, 23070, 23100, 23130, 23156, 23186, 23212, 23240, 23264, 23292, 23329, 0}, {23016, 23042, 23070, 23100, 23130, 23156, 23186, 23212, 23240, 23264, 23292, 23329, 0}, {23368, 23380, 23392, 23404, 23416, 23428, 23440, 23452, 23464, 23476, 23489, 23502, 0}, {23368, 23380, 23392, 23404, 23416, 23428, 23440, 23452, 23464, 23476, 23489, 23502, 0}, 0, 1, 55, 3, {417,2861,0,0,0,0,0,0,0,0,0,0,0,0},{3361,3399,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2717,0,0,0,0,0,0,0}}, + {370, 25, 32, {897, 907, 917, 927, 937, 947, 957}, {34299, 34306, 34313, 34320, 34327, 34334, 34341}, {1016, 1020, 1024, 1028, 1032, 1036, 1040}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, 0, 0, 1, 3, {295,857,9,439,460,379,406,417,0,0,0,0,0,0},{469,1484,547,562,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{623,639,652,666,0,0,0,0}}, + {370, 25, 32, {897, 907, 917, 927, 937, 947, 957}, {34299, 34306, 34313, 34320, 34327, 34334, 34341}, {1016, 1020, 1024, 1028, 1032, 1036, 1040}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, {1134, 1139, 1144, 1149, 1154, 1159, 1164, 1169, 1174, 1179, 1185, 1191, 0}, 0, 0, 1, 3, {295,857,9,439,460,379,406,417,0,0,0,0,0,0},{469,1484,547,562,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{623,639,652,666,0,0,0,0}}, + {2090, 151, 156, {1525, 1533, 1540, 1548, 1555, 1563, 1570}, {5052, 5058, 5063, 5068, 5073, 5078, 5083}, {1285, 1608, 1610, 1612, 1610, 1614, 1616}, {1618, 1625, 3504, 1639, 3515, 1649, 1654, 1659, 1666, 1676, 1684, 4420, 0}, {1618, 1625, 3504, 1639, 3515, 1649, 1654, 1659, 1666, 1676, 1684, 4420, 0}, {5089, 4610, 4512, 4614, 3515, 5093, 5097, 5101, 5105, 5109, 4642, 5113, 0}, {5089, 4610, 4512, 4614, 3515, 5093, 5097, 5101, 5105, 5109, 4642, 5113, 0}, 2, 1, 11, 11, {681,1455,0,0,0,0,0,0,0,0,0,0,0,0},{703,721,0,0,0,0,0,0,0,0},{1151,0,0,0,0,0,0,0,0,0,0,0},{1173,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {673, 1207, 1191, {36109, 35877, 35896, 6014, 35924, 35941, 7877}, {36122, 36129, 36136, 36651, 36150, 36157, 36164}, {315, 318, 35972, 324, 327, 318, 324}, {35975, 35988, 360, 369, 12370, 36003, 36010, 401, 36017, 36036, 36051, 36068, 0}, {35975, 35988, 360, 369, 12370, 36003, 36010, 401, 36017, 36036, 36051, 36068, 0}, {35075, 36171, 35082, 496, 12370, 36003, 36010, 503, 510, 517, 36178, 36185, 0}, {35075, 36171, 35082, 496, 12370, 36003, 36010, 503, 510, 517, 36178, 36185, 0}, 0, 1, 11, 11, {2151,2181,0,0,0,0,0,0,0,0,0,0,0,0},{5034,2239,0,0,0,0,0,0,0,0},{1151,0,0,0,0,0,0,0,0,0,0,0},{1173,0,0,0,0,0,0,0,0},{4948,0,0,0,0,0,0,0}}, + {1, 259, 271, {9712, 9727, 9742, 9757, 9774, 9793, 9804}, {9815, 9822, 9829, 9836, 9843, 9850, 9857}, {0, 0, 0, 0, 0, 0, 0}, {9864, 9875, 9888, 9897, 9908, 9915, 9922, 9929, 9942, 9957, 9970, 9981, 0}, {9864, 9875, 9888, 9897, 9908, 9915, 9922, 9929, 9942, 9957, 9970, 9981, 0}, {9994, 10001, 10008, 10015, 9908, 9915, 9922, 10022, 10029, 10036, 10043, 10050, 0}, {9994, 10001, 10008, 10015, 9908, 9915, 9922, 10022, 10029, 10036, 10043, 10050, 0}, 0, 1, 11, 3, {681,775,2098,744,9,0,0,0,0,0,0,0,0,0},{2727,2745,0,0,0,0,0,0,0,0},{71,266,0,0,0,0,0,0,0,0,0,0},{89,271,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {673, 313, 1106, {35117, 35126, 35138, 35147, 35154, 35163, 1247}, {12088, 12092, 35169, 12101, 35174, 35178, 12114}, {6516, 6518, 12118, 6522, 6522, 6518, 6522}, {1618, 1625, 12120, 12126, 1645, 8902, 8908, 12137, 1666, 1676, 12144, 1693, 0}, {12153, 12161, 12170, 12177, 5333, 12189, 12196, 12203, 6874, 12211, 12219, 6902, 0}, {5089, 4610, 12228, 4614, 1645, 5093, 5097, 12237, 5105, 5109, 12241, 6911, 0}, {5089, 4610, 12228, 4614, 1645, 5093, 5097, 12237, 5105, 5109, 12241, 6911, 0}, 2, 1, 124, 3, {692,4680,681,775,417,0,0,0,0,0,0,0,0,0},{798,721,692,0,0,0,0,0,0,0},{71,266,4689,4703,0,0,0,0,0,0,0,0},{89,4716,4733,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}}, + {925, 444, 447, {17037, 17047, 17056, 17065, 17076, 17086, 17091}, {17098, 17102, 17107, 17112, 17117, 7723, 17121}, {17126, 1828, 1285, 5210, 1280, 2892, 1285}, {11533, 11540, 7354, 11547, 2760, 17128, 17133, 17138, 17145, 17153, 11589, 11596, 0}, {11603, 11610, 11617, 11622, 11628, 11632, 11637, 8914, 17160, 17168, 11666, 11673, 0}, {12906, 17175, 2791, 1927, 2760, 17179, 17183, 17187, 7707, 1947, 17191, 17195, 0}, {12906, 17175, 2791, 1927, 2760, 17179, 17183, 17187, 7707, 1947, 17191, 17195, 0}, 0, 1, 1, 3, {406,2861,0,0,0,0,0,0,0,0,0,0,0,0},{3239,2997,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{2717,0,0,0,0,0,0,0}}, + {1, 450, 465, {7465, 34718, 7489, 35389, 7505, 7518, 7527}, {17899, 17906, 17916, 17929, 17942, 17952, 17971}, {17993, 17997, 18004, 18011, 18021, 18028, 18041}, {7536, 7547, 7558, 7567, 35398, 7585, 7592, 7605, 7614, 7625, 7638, 7649, 0}, {7536, 7547, 7558, 7567, 35398, 7585, 7592, 7605, 7614, 7625, 7638, 7649, 0}, {18228, 18235, 18083, 18245, 18115, 18122, 18261, 18274, 18281, 18291, 18304, 18314, 0}, {18228, 18235, 18083, 18245, 18115, 18122, 18261, 18274, 18281, 18291, 18304, 18314, 0}, 0, 0, 55, 11, {755,1062,2098,744,417,0,0,0,0,0,0,0,0,0},{3298,894,0,0,0,0,0,0,0,0},{4804,62,266,71,0,0,0,0,0,0,0,0},{4812,77,271,89,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {109, 875, 885, {28394, 28401, 28407, 28414, 28420, 28426, 28434}, {28444, 28448, 28452, 28456, 28460, 28464, 28468}, {9428, 9428, 9428, 9428, 9428, 9428, 9428}, {28474, 28483, 7020, 28492, 28498, 28504, 28510, 28517, 28523, 28532, 28541, 28549, 0}, {28474, 28483, 7020, 28492, 28498, 28504, 28510, 28517, 28523, 28532, 28541, 28549, 0}, {28558, 28562, 2791, 28566, 2760, 28570, 28574, 28578, 28583, 28587, 28593, 28597, 0}, {28558, 28562, 2791, 28566, 2760, 28570, 28574, 28578, 28583, 28587, 28593, 28597, 0}, 0, 0, 55, 3, {744,755,417,0,0,0,0,0,0,0,0,0,0,0},{2448,42,0,0,0,0,0,0,0,0},{266,71,0,0,0,0,0,0,0,0,0,0},{271,89,0,0,0,0,0,0,0},{98,0,0,0,0,0,0,0}}, + {925, 49, 52, {29614, 29621, 29629, 29636, 29643, 29651, 29660}, {29667, 29671, 29675, 29679, 29683, 7723, 28444}, {1616, 1616, 1610, 1616, 9428, 2892, 9428}, {29687, 29695, 29705, 29711, 29719, 29724, 29729, 29734, 29741, 16856, 29749, 29757, 0}, {29687, 29695, 29705, 29711, 29719, 29724, 29729, 29734, 29741, 16856, 29749, 29757, 0}, {1914, 29765, 2791, 29769, 2760, 28570, 28574, 29773, 2731, 1947, 29777, 13346, 0}, {1914, 29765, 2791, 29769, 2760, 28570, 28574, 29773, 2731, 1947, 29777, 13346, 0}, 0, 0, 1, 3, {295,3021,0,0,0,0,0,0,0,0,0,0,0,0},{1010,1029,0,0,0,0,0,0,0,0},{71,0,0,0,0,0,0,0,0,0,0,0},{89,0,0,0,0,0,0,0,0},{734,0,0,0,0,0,0,0}} +}; + + +static const NumberFormatEntry number_format_entries [] = { + {11, 1264, 11, 1264, 1266, 1276, 1281, 1296, 1299, 1309, 55, 1318, 3, 2, 0, 0, 3, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1325, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1343, 1331, 1333, 1337, 1347, 1356, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1364, 1331, 1333, 1296, 1299, 1309, 55, 1341, 2, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1364, 1331, 1333, 1296, 1299, 1309, 55, 1341, 2, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1367, 1331, 0, 1337, 1371, 1383, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1395, 1331, 1333, 1337, 1299, 1309, 55, 1341, 12, 2, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1343, 1331, 1333, 1337, 1399, 1410, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1343, 1331, 1333, 1337, 1421, 1435, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1448, 1331, 1333, 1337, 1299, 1309, 55, 1341, 0, 0, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1343, 1331, 1333, 1337, 1450, 1460, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1343, 1331, 1469, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1343, 1478, 1333, 1296, 1481, 1489, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1497, 1331, 1333, 1337, 1299, 1309, 55, 1501, 2, 2, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1506, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1509, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 1, 0, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1343, 1331, 1333, 1337, 1450, 1513, 55, 1341, 9, 2, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1523, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 0, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1527, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 0, 0, 1, 0, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1343, 1331, 1333, 1296, 1531, 1541, 55, 1341, 12, 2, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 0, 1276, 1333, 1296, 1299, 1309, 55, 1318, 9, 2, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1550, 1331, 1333, 1337, 1554, 1573, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1592, 1331, 1333, 1337, 1450, 1513, 55, 1341, 9, 2, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1595, 11, 1595, 1599, 1331, 1333, 1337, 1603, 1612, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1621, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1625, 1331, 1629, 1337, 1646, 1674, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1701, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 2, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1343, 1331, 1333, 1337, 1371, 1383, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1704, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 1, 0, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 1322, 1710, 1331, 1713, 1720, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1726, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1730, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 2, 2, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1734, 1331, 1333, 1337, 1299, 1309, 55, 1737, 3, 0, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1745, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 0, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1748, 1331, 1333, 1337, 1299, 1309, 55, 1341, 5, 1, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1752, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1343, 1331, 1333, 1337, 1755, 1769, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1343, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1343, 1331, 1782, 1337, 1785, 1797, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {2, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1343, 1331, 1333, 1337, 1808, 1819, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 0, 1331, 0, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1, 1264, 1, 1264, 1829, 1478, 0, 1296, 1299, 1309, 55, 1341, 3, 0, 0, 0, 3, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1838, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1842, 1331, 1845, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 0, 1331, 1333, 1337, 1299, 1309, 55, 1341, 9, 2, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1343, 1331, 1333, 1337, 1852, 1862, 55, 1341, 8, 3, 7, 3, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1343, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1871, 1331, 1333, 1337, 1299, 1309, 55, 1341, 9, 2, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1878, 1331, 0, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1878, 1331, 0, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1322, 11, 1322, 1878, 1331, 0, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1878, 1331, 0, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1878, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1878, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1880, 1331, 1884, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1710, 1331, 1333, 1337, 1299, 1309, 55, 1341, 2, 0, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1925, 1331, 0, 1337, 1299, 1309, 55, 1341, 12, 2, 0, 0, 1, 2, 2, {3, 2}, {3, 2}}, + {11, 1264, 11, 1264, 1343, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1343, 1331, 1713, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1343, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1929, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1932, 1331, 1936, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1953, 1331, 1960, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1977, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1981, 1331, 1985, 1337, 1299, 1309, 55, 1341, 5, 1, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 0, 1478, 1995, 1296, 1299, 1309, 55, 1341, 9, 2, 1, 1, 1, 0, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2014, 1331, 1333, 1337, 1299, 1309, 55, 1341, 12, 2, 1, 0, 1, 2, 2, {3, 2}, {3, 2}}, + {11, 1264, 11, 1264, 0, 1478, 1333, 1296, 1299, 1309, 55, 1341, 12, 2, 0, 0, 1, 2, 2, {3, 2}, {3, 2}}, + {11, 1264, 11, 1264, 1925, 1331, 0, 1337, 1299, 1309, 55, 1341, 12, 2, 0, 0, 1, 2, 2, {3, 2}, {3, 2}}, + {11, 1264, 11, 1264, 1925, 1331, 0, 1337, 1299, 1309, 55, 1341, 12, 2, 0, 0, 1, 2, 2, {3, 2}, {3, 2}}, + {11, 1264, 11, 1264, 1925, 1331, 1333, 1337, 1299, 1309, 55, 1341, 12, 2, 0, 0, 1, 2, 2, {3, 2}, {3, 2}}, + {11, 1264, 11, 1264, 1925, 1331, 1333, 1337, 1299, 1309, 55, 1341, 12, 2, 0, 0, 1, 2, 2, {3, 2}, {3, 2}}, + {11, 1264, 11, 1264, 1925, 1331, 1333, 1337, 1299, 1309, 55, 1341, 12, 2, 0, 0, 1, 2, 2, {3, 2}, {3, 2}}, + {11, 1264, 11, 1264, 1925, 1331, 1333, 1337, 1299, 1309, 55, 1341, 12, 2, 2, 2, 1, 2, 2, {3, -1}, {3, 2}}, + {11, 1264, 11, 1264, 1925, 1331, 0, 1337, 1299, 1309, 55, 1341, 12, 2, 1, 1, 1, 2, 2, {3, 2}, {3, 2}}, + {2021, 1264, 11, 1264, 1925, 1331, 1333, 1337, 1299, 1309, 55, 1341, 12, 2, 0, 0, 1, 2, 2, {3, 2}, {3, 2}}, + {11, 1264, 11, 1264, 0, 1331, 1333, 1337, 1299, 1309, 55, 1341, 9, 2, 1, 1, 1, 0, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1364, 1331, 1333, 1337, 1299, 1309, 55, 1341, 2, 0, 1, 1, 1, 2, 2, {3, 0}, {3, -1}}, + {11, 1264, 11, 1264, 2023, 1331, 0, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2026, 1331, 1333, 1337, 1299, 1309, 55, 1341, 5, 1, 1, 1, 2, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 2030, 1331, 2034, 1337, 1299, 1309, 55, 1341, 2, 0, 1, 1, 1, 0, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2080, 1331, 2082, 1337, 1299, 1309, 55, 1341, 9, 2, 1, 1, 1, 0, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1343, 1331, 1333, 1337, 1450, 1460, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1925, 1331, 0, 1337, 1299, 1309, 55, 1341, 12, 2, 0, 0, 1, 2, 2, {3, 2}, {3, 2}}, + {11, 1264, 11, 1264, 2122, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 0, 1331, 1333, 1337, 1299, 1309, 55, 1341, 0, 0, 1, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2130, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 1, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 0, 1331, 0, 1337, 1481, 1489, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2137, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, 2}}, + {1264, 11, 1264, 11, 1343, 1331, 1333, 1337, 1299, 1309, 55, 1341, 11, 2, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 2144, 1478, 1333, 1296, 1299, 1309, 55, 1737, 8, 3, 1, 1, 1, 0, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2147, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 0, 1331, 0, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 0, 1331, 0, 1337, 1299, 1309, 55, 1341, 9, 2, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2151, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1322, 11, 1322, 1878, 1331, 0, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1343, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1395, 1331, 1713, 1337, 1299, 1309, 55, 1341, 12, 2, 0, 0, 1, 2, 2, {3, 0}, {3, -1}}, + {11, 1264, 11, 1264, 2151, 1331, 1333, 1296, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1752, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2155, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1448, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2159, 1331, 0, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 0, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1364, 1331, 0, 1337, 1299, 1309, 55, 1341, 2, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1343, 1331, 0, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1523, 1331, 1333, 1337, 1299, 1309, 55, 1341, 2, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1343, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1625, 1331, 2161, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 2190, 1331, 1333, 1337, 1299, 1309, 55, 1341, 9, 2, 1, 1, 1, 0, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2023, 1478, 1333, 1296, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1266, 1478, 1281, 1296, 1299, 1309, 55, 1318, 3, 2, 0, 0, 3, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1325, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1343, 1331, 1333, 1337, 1347, 1356, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1448, 1331, 2193, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1367, 1331, 0, 1337, 1371, 1383, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1395, 1331, 1333, 1337, 1299, 1309, 55, 1341, 12, 2, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1343, 1331, 1333, 1337, 1399, 1410, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1343, 1331, 1333, 1337, 1421, 1435, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1448, 1331, 1333, 1337, 1299, 1309, 55, 1341, 0, 0, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1343, 1331, 1469, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1343, 1478, 1333, 1296, 1481, 1489, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1497, 1331, 1333, 1337, 1299, 1309, 55, 1501, 2, 2, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1506, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1509, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 1, 0, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1343, 1331, 1333, 1337, 1450, 1513, 55, 1341, 9, 2, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1523, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 0, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1527, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 0, 0, 1, 0, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1343, 1331, 1333, 1296, 1531, 1541, 55, 1341, 12, 2, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1710, 1276, 1333, 1296, 1299, 1309, 55, 1318, 9, 2, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1550, 1331, 1333, 1337, 1554, 1573, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1592, 1331, 1333, 1337, 1450, 1513, 55, 1341, 9, 2, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1595, 11, 1595, 1599, 1331, 1333, 1337, 1603, 1612, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1621, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1625, 1331, 1629, 1337, 1646, 1674, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 2203, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 2, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1343, 1331, 1333, 1337, 1371, 1383, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1704, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 1, 0, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 1322, 1710, 1331, 1713, 1720, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1726, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1730, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 2, 2, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1734, 1331, 1333, 1337, 1299, 1309, 55, 1737, 3, 0, 0, 0, 1, 0, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1745, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 0, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1748, 1331, 1333, 1337, 1299, 1309, 55, 1341, 5, 1, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1752, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1343, 1331, 1333, 1337, 1755, 1769, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1343, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1343, 1331, 1782, 1337, 1785, 1797, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {2, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1343, 1331, 1333, 1337, 1808, 1819, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1953, 1331, 0, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1, 1264, 1, 1264, 1829, 1478, 0, 1296, 1299, 1309, 55, 1341, 3, 0, 0, 0, 3, 0, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1838, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 0, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1842, 1331, 1845, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 0, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 2207, 1331, 1333, 1337, 1299, 1309, 55, 1341, 9, 2, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1343, 1331, 1333, 1337, 1852, 1862, 55, 1341, 8, 3, 7, 3, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1343, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1871, 1331, 1333, 1337, 1299, 1309, 55, 1341, 9, 2, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1878, 1331, 0, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1878, 1331, 0, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1322, 11, 1322, 1878, 1331, 0, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1878, 1331, 0, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1878, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1878, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1880, 1331, 1884, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1710, 1331, 1333, 1337, 1299, 1309, 55, 1341, 2, 0, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1925, 1331, 0, 1337, 1299, 1309, 55, 1341, 12, 2, 0, 0, 1, 2, 2, {3, 2}, {3, 2}}, + {11, 1264, 11, 1264, 1343, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1710, 1331, 1713, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1929, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1932, 1331, 1936, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1953, 1331, 1960, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1977, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1981, 1331, 1985, 1337, 1299, 1309, 55, 1341, 5, 1, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 2211, 1478, 1995, 1296, 1299, 1309, 55, 1341, 9, 2, 1, 1, 1, 0, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1925, 1331, 1333, 1337, 1299, 1309, 55, 1341, 12, 2, 1, 0, 1, 2, 2, {3, 2}, {3, 2}}, + {11, 1264, 11, 1264, 1925, 1331, 0, 1337, 1299, 1309, 55, 1341, 12, 2, 0, 0, 1, 2, 2, {3, 2}, {3, 2}}, + {11, 1264, 11, 1264, 1925, 1331, 0, 1337, 1299, 1309, 55, 1341, 12, 2, 0, 0, 1, 2, 2, {3, 2}, {3, 2}}, + {11, 1264, 11, 1264, 1925, 1331, 1333, 1337, 1299, 1309, 55, 1341, 12, 2, 0, 0, 1, 2, 2, {3, 2}, {3, 2}}, + {11, 1264, 11, 1264, 1925, 1331, 1333, 1337, 1299, 1309, 55, 1341, 12, 2, 0, 0, 1, 2, 2, {3, 2}, {3, 2}}, + {11, 1264, 11, 1264, 1925, 1331, 1333, 1337, 1299, 1309, 55, 1341, 12, 2, 0, 0, 1, 2, 2, {3, 2}, {3, 2}}, + {11, 1264, 11, 1264, 1925, 1331, 1333, 1337, 1299, 1309, 55, 1341, 12, 2, 2, 2, 1, 2, 2, {3, -1}, {3, 2}}, + {11, 1264, 11, 1264, 1925, 1331, 0, 1337, 1299, 1309, 55, 1341, 12, 2, 1, 1, 1, 2, 2, {3, 2}, {3, 2}}, + {2021, 1264, 11, 1264, 1925, 1331, 1333, 1337, 1299, 1309, 55, 1341, 12, 2, 0, 0, 1, 2, 2, {3, 2}, {3, 2}}, + {11, 1264, 11, 1264, 2217, 1331, 1333, 1337, 1299, 1309, 55, 1341, 9, 2, 1, 1, 1, 0, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1364, 1331, 1333, 1337, 1299, 1309, 55, 1341, 2, 0, 1, 1, 1, 2, 2, {3, 0}, {3, -1}}, + {11, 1264, 11, 1264, 2023, 1331, 0, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2026, 1331, 1333, 1337, 1299, 1309, 55, 1341, 5, 1, 1, 1, 2, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 2030, 1331, 2034, 1337, 1299, 1309, 55, 1341, 2, 0, 1, 1, 1, 0, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2080, 1331, 2082, 1337, 1299, 1309, 55, 1341, 9, 2, 1, 1, 1, 0, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1343, 1331, 1333, 1337, 1450, 1460, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1925, 1331, 0, 1337, 1299, 1309, 55, 1341, 12, 2, 0, 0, 1, 2, 2, {3, 2}, {3, 2}}, + {11, 1264, 11, 1264, 2122, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2130, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 1, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2221, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, 2}}, + {1264, 11, 1264, 11, 1343, 1331, 1333, 1337, 1299, 1309, 55, 1341, 11, 2, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 2144, 1478, 1333, 1296, 1299, 1309, 55, 1737, 8, 3, 1, 1, 1, 0, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2147, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2151, 1331, 0, 1337, 1299, 1309, 55, 1341, 9, 2, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2151, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1322, 11, 1322, 1878, 1331, 0, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1343, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1395, 1331, 1713, 1337, 1299, 1309, 55, 1341, 12, 2, 0, 0, 1, 2, 2, {3, 0}, {3, -1}}, + {11, 1264, 11, 1264, 2151, 1331, 1333, 1296, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1752, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1752, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1448, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2159, 1331, 0, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 0, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1364, 1331, 0, 1337, 1299, 1309, 55, 1341, 2, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1343, 1331, 0, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1523, 1331, 1333, 1337, 1299, 1309, 55, 1341, 2, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1343, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1625, 1331, 2161, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 2190, 1331, 1333, 1337, 1299, 1309, 55, 1341, 9, 2, 1, 1, 1, 0, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2023, 1478, 1333, 1296, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2234, 1276, 1281, 1296, 1299, 1309, 55, 1318, 3, 2, 0, 0, 3, 0, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1343, 1331, 1333, 1337, 1347, 1356, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1523, 1331, 1333, 1296, 1299, 1309, 55, 1341, 2, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 2244, 11, 2244, 1599, 1331, 1333, 1337, 1399, 1410, 55, 1341, 2, 2, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2023, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1448, 1331, 1333, 1337, 1450, 1460, 55, 1341, 1, 0, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1343, 1478, 1333, 1296, 1481, 1489, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 2244, 11, 2244, 1599, 1331, 1333, 1337, 1450, 1513, 55, 1341, 2, 2, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 1322, 1343, 1331, 1333, 1296, 1531, 1541, 55, 1341, 12, 2, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1710, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1343, 1331, 1333, 1337, 1450, 1513, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 2246, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 2246, 1331, 1629, 1337, 1646, 1674, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1343, 1331, 1713, 1720, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1925, 1331, 1333, 1337, 1299, 1309, 55, 1737, 1, 0, 1, 2, 1, 2, 2, {3, 2}, {3, -1}}, + {1264, 1322, 1264, 1322, 2207, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1343, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1322, 11, 1322, 2248, 1331, 0, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 1322, 1710, 1331, 1713, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1343, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1448, 1331, 1333, 1337, 1299, 1309, 55, 1341, 9, 2, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 2250, 1478, 2257, 1296, 1299, 1309, 55, 1341, 9, 2, 1, 1, 1, 0, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2290, 1331, 1333, 1337, 1299, 1309, 55, 1341, 12, 2, 1, 0, 1, 2, 2, {3, 2}, {3, 2}}, + {11, 1264, 11, 1264, 2294, 1478, 1333, 1296, 1299, 1309, 55, 1501, 9, 2, 9, 3, 2, 0, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2297, 1331, 1333, 1337, 1299, 1309, 55, 1341, 9, 2, 1, 1, 1, 2, 2, {3, 2}, {3, 2}}, + {11, 1264, 11, 1264, 1925, 1331, 1333, 1337, 1299, 1309, 55, 1341, 9, 2, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2155, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2301, 1276, 1281, 1296, 1299, 1309, 55, 1318, 3, 2, 0, 0, 3, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2311, 1331, 2193, 1337, 1299, 1309, 55, 1341, 0, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1343, 1331, 1333, 1337, 1399, 1410, 55, 1341, 9, 2, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1448, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1343, 1331, 1333, 1337, 1450, 1460, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1448, 1478, 1333, 1296, 1481, 1489, 55, 1341, 15, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1343, 1331, 1713, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2315, 1276, 1281, 1296, 1299, 1309, 55, 1318, 3, 0, 0, 0, 3, 3, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1448, 1331, 1333, 1296, 1299, 1309, 55, 1341, 0, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1343, 1331, 1333, 1337, 1399, 1410, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1448, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2325, 1331, 1333, 1337, 1450, 1460, 55, 1341, 1, 0, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1322, 11, 1322, 1599, 1478, 1333, 1296, 1481, 1489, 55, 1341, 2, 2, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 2327, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2330, 1276, 1281, 1296, 1299, 1309, 55, 1318, 3, 2, 0, 0, 3, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2340, 1331, 2193, 1337, 1299, 1309, 55, 1341, 0, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 2244, 11, 2244, 1599, 1331, 1333, 1337, 1399, 1410, 55, 1341, 9, 2, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1448, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 2345, 1331, 1333, 1337, 1450, 1460, 55, 1341, 1, 0, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1343, 1478, 1333, 1296, 1481, 1489, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 2327, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2349, 1276, 1281, 1296, 1299, 1309, 55, 1318, 3, 2, 0, 0, 3, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1343, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2359, 1331, 1333, 1337, 1450, 1460, 55, 1341, 1, 0, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1343, 1478, 1333, 1296, 1481, 1489, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 2327, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2363, 1276, 1281, 1296, 1299, 1309, 55, 1318, 3, 2, 0, 0, 3, 3, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1878, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2373, 1331, 1333, 1337, 1450, 1460, 55, 1341, 1, 0, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 2377, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2382, 1276, 1281, 1296, 1299, 1309, 55, 1318, 3, 2, 0, 0, 3, 3, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1448, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 2392, 1331, 1333, 1337, 1450, 1460, 55, 1341, 2, 0, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1343, 1478, 1333, 1296, 1481, 1489, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 2377, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2396, 1276, 1281, 1296, 1299, 1309, 55, 1318, 3, 2, 0, 0, 3, 0, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1448, 1331, 1333, 1337, 1450, 1460, 55, 1341, 1, 0, 0, 0, 1, 0, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 2406, 1478, 1333, 1296, 1481, 1489, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 2409, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 1, 0, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1343, 1331, 2413, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2421, 1276, 1281, 1296, 1299, 1309, 55, 1318, 3, 2, 0, 0, 3, 0, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1448, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2431, 1331, 1333, 1337, 1450, 1460, 55, 1341, 1, 0, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 2434, 1478, 1333, 1296, 1481, 1489, 55, 1341, 8, 3, 0, 0, 1, 0, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 2409, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 1, 0, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2438, 1276, 1281, 1296, 1299, 1309, 55, 1318, 3, 2, 0, 0, 3, 3, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1448, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1448, 1331, 1333, 1337, 1450, 1460, 55, 1341, 1, 0, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 2448, 1478, 1333, 1296, 1481, 1489, 55, 1341, 8, 3, 0, 0, 1, 0, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1343, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2453, 1276, 1281, 1296, 1299, 1309, 55, 1318, 3, 2, 0, 0, 3, 0, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1448, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1448, 1331, 1333, 1337, 1450, 1460, 55, 1341, 2, 0, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 2434, 1478, 1333, 1296, 1481, 1489, 55, 1341, 8, 3, 0, 0, 1, 0, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1343, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2463, 1276, 1281, 1296, 1299, 1309, 55, 1318, 3, 2, 0, 0, 3, 3, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2147, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1448, 1331, 1333, 1337, 1450, 1460, 55, 1341, 2, 0, 0, 0, 1, 0, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 2434, 1478, 1333, 1296, 1481, 1489, 55, 1341, 8, 3, 0, 0, 1, 0, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2473, 1276, 1281, 1296, 1299, 1309, 55, 1318, 3, 2, 0, 0, 3, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1448, 1331, 1333, 1337, 1450, 1460, 55, 1341, 9, 2, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 2483, 1478, 1333, 1296, 1481, 1489, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2487, 1276, 1281, 1296, 1299, 1309, 55, 1318, 3, 2, 0, 0, 3, 3, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2311, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 2497, 1331, 1333, 1337, 1450, 1460, 55, 1341, 12, 2, 0, 0, 1, 0, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 2501, 1478, 1333, 1296, 1481, 1489, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2503, 1276, 1281, 1296, 1299, 1309, 55, 1318, 3, 2, 0, 0, 3, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1925, 1331, 1333, 1337, 1299, 1309, 55, 1341, 12, 2, 1, 1, 1, 2, 2, {3, 2}, {3, 2}}, + {1264, 11, 1264, 11, 2513, 1331, 1333, 1337, 1450, 1460, 55, 1341, 1, 0, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1929, 1331, 1333, 1337, 1299, 1309, 55, 1341, 0, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1448, 1331, 1333, 1337, 1450, 1460, 55, 1341, 1, 0, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1448, 1331, 1333, 1337, 1299, 1309, 55, 1341, 1, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2246, 1331, 1333, 1337, 1450, 1460, 55, 1341, 1, 0, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2516, 1331, 1333, 1337, 1450, 1460, 55, 1341, 1, 0, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1448, 1331, 1333, 1337, 1450, 1460, 55, 1341, 1, 0, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1448, 1331, 1333, 1337, 1450, 1460, 55, 1341, 0, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1448, 1331, 1333, 1337, 1450, 1460, 55, 1341, 1, 0, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 2377, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 2327, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 2519, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 2527, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 1, 0, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1343, 1331, 2413, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 2207, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 1364, 1331, 1333, 1296, 1299, 1309, 55, 1341, 2, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1710, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 2327, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 2207, 1331, 1333, 1337, 1299, 1309, 55, 1341, 9, 2, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 2250, 1478, 2257, 1296, 1299, 1309, 55, 1341, 9, 2, 1, 1, 1, 0, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2217, 1331, 1333, 1337, 1299, 1309, 55, 1341, 9, 2, 1, 1, 1, 0, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2311, 1331, 2193, 1337, 1299, 1309, 55, 1341, 0, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2311, 1331, 2193, 1337, 1299, 1309, 55, 1341, 0, 0, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1710, 1276, 1333, 1296, 1299, 1309, 55, 1318, 9, 2, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 2527, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 1, 0, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 1953, 1331, 0, 1337, 1299, 1309, 55, 1341, 8, 3, 1, 1, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 11, 1264, 11, 1343, 1331, 1333, 1337, 1299, 1309, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 2211, 1478, 1995, 1296, 1299, 1309, 55, 1341, 9, 2, 1, 1, 1, 0, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2294, 1478, 1333, 1296, 1299, 1309, 55, 1501, 9, 2, 9, 3, 2, 2, 2, {3, -1}, {3, -1}}, + {1264, 1322, 1264, 1322, 0, 1331, 0, 1337, 1481, 1489, 55, 1341, 8, 3, 0, 0, 1, 2, 2, {3, -1}, {3, -1}}, + {11, 1264, 11, 1264, 2151, 1331, 0, 1337, 1299, 1309, 55, 1341, 9, 2, 1, 1, 1, 2, 2, {3, -1}, {3, -1}} +}; + + +static const CultureInfoEntry culture_entries [] = { + {0x0001, 0x007F, 768, -1, 2532, 2535, 2542, 2557, 2561, 2532, 0, {0, 0, 36669, 0}, 0, 0, { 1256, 20420, 10004, 720, 1, ';' }}, + {0x0002, 0x007F, 257, -1, 2565, 2568, 2578, 2597, 2601, 2565, 0, {36697, 0, 0, 0}, 1, 1, { 1251, 21025, 10007, 866, 0, ';' }}, + {0x0003, 0x007F, 257, -1, 2605, 2608, 2616, 2624, 2628, 2605, 0, {36739, 0, 0, 0}, 2, 2, { 1252, 500, 10000, 850, 0, ';' }}, + {0x0004, 0x7804, 257, -1, 2632, 2640, 2661, 2668, 2672, 2676, 0, {36759, 0, 0, 0}, 3, 3, { 936, 500, 10008, 936, 0, ',' }}, + {0x0004, 0x0004, 257, -1, 2679, 2686, 2661, 2668, 2672, 2676, 0, {36759, 0, 0, 0}, 4, 4, { 936, 500, 10008, 936, 0, ',' }}, + {0x0005, 0x007F, 257, -1, 2714, 2717, 2723, 2733, 2737, 2714, 0, {36766, 0, 0, 0}, 5, 5, { 1250, 500, 10029, 852, 0, ';' }}, + {0x0006, 0x007F, 257, -1, 2741, 2744, 2751, 2757, 2761, 2741, 0, {36792, 0, 0, 0}, 6, 6, { 1252, 20277, 10000, 850, 0, ';' }}, + {0x0007, 0x007F, 257, -1, 2765, 2768, 2775, 2783, 2787, 2765, 0, {36813, 0, 0, 0}, 7, 7, { 1252, 20273, 10000, 850, 0, ';' }}, + {0x0008, 0x007F, 257, -1, 2791, 2794, 2800, 2817, 2821, 2791, 0, {36838, 0, 0, 0}, 8, 8, { 1253, 20273, 10006, 737, 0, ';' }}, + {0x0009, 0x007F, 257, -1, 2825, 2828, 2828, 2836, 2840, 2825, 0, {36880, 0, 0, 0}, 9, 9, { 1252, 37, 10000, 437, 0, ',' }}, + {0x000A, 0x007F, 257, -1, 2844, 2847, 2855, 2864, 2868, 2844, 0, {36899, 0, 0, 0}, 10, 10, { 1252, 20284, 10000, 850, 0, ';' }}, + {0x000B, 0x007F, 257, -1, 2872, 2875, 2883, 2889, 2893, 2872, 0, {36921, 0, 0, 0}, 11, 11, { 1252, 20278, 10000, 850, 0, ';' }}, + {0x000C, 0x007F, 257, -1, 2897, 2900, 2907, 2917, 2921, 2897, 0, {36946, 0, 0, 0}, 12, 12, { 1252, 20297, 10000, 850, 0, ';' }}, + {0x000D, 0x007F, 257, -1, 2925, 2928, 2935, 2946, 2950, 2925, 0, {36968, 0, 0, 0}, 13, 13, { 1255, 500, 10005, 862, 1, ',' }}, + {0x000E, 0x007F, 257, -1, 2954, 2957, 2967, 2974, 2978, 2954, 0, {37005, 0, 0, 0}, 14, 14, { 1250, 500, 10029, 852, 0, ';' }}, + {0x000F, 0x007F, 257, -1, 2982, 2985, 2995, 3005, 3009, 2982, 0, {37021, 0, 0, 0}, 15, 15, { 1252, 20871, 10079, 850, 0, ';' }}, + {0x0010, 0x007F, 257, -1, 3013, 3016, 3024, 3033, 3037, 3013, 0, {37041, 0, 0, 0}, 16, 16, { 1252, 20280, 10000, 850, 0, ';' }}, + {0x0011, 0x007F, 257, -1, 3041, 3044, 3053, 3063, 3067, 3041, 0, {37063, 0, 0, 0}, 17, 17, { 932, 20290, 10001, 932, 0, ',' }}, + {0x0012, 0x007F, 257, -1, 3071, 3074, 3081, 3091, 3095, 3071, 0, {37090, 0, 0, 0}, 18, 18, { 949, 20833, 10003, 949, 0, ',' }}, + {0x0013, 0x007F, 257, -1, 3099, 3102, 3108, 3119, 3123, 3099, 0, {37097, 0, 0, 0}, 19, 19, { 1252, 500, 10000, 850, 0, ';' }}, + {0x0014, 0x007F, 257, -1, 3127, 3130, 3140, 3146, 3150, 3154, 0, {36792, 0, 0, 0}, 20, 20, { 1252, 20277, 10000, 850, 0, ';' }}, + {0x0015, 0x007F, 257, -1, 3157, 3160, 3167, 3174, 3178, 3157, 0, {37119, 0, 0, 0}, 21, 21, { 1250, 20880, 10029, 852, 0, ';' }}, + {0x0016, 0x007F, 257, -1, 3182, 3185, 3196, 3207, 3211, 3182, 0, {37143, 0, 0, 0}, 22, 22, { 1252, 500, 10000, 850, 0, ';' }}, + {0x0017, 0x007F, 257, -1, 3215, 3218, 3226, 3236, 3240, 3215, 0, {37166, 0, 0, 0}, 23, 23, { 1252, 20273, 10000, 850, 0, ';' }}, + {0x0018, 0x007F, 257, -1, 3244, 3247, 3256, 3265, 3269, 3244, 0, {37186, 0, 0, 0}, 24, 24, { 1250, 20880, 10029, 852, 0, ';' }}, + {0x0019, 0x007F, 257, -1, 3273, 3276, 3284, 3299, 3303, 3273, 0, {37205, 0, 0, 0}, 25, 25, { 1251, 20880, 10007, 866, 0, ';' }}, + {0x001A, 0x007F, 257, -1, 3307, 3310, 3319, 3328, 3332, 3307, 0, {37251, 0, 0, 0}, 26, 26, { 1250, 500, 10082, 852, 0, ';' }}, + {0x001B, 0x007F, 257, -1, 3336, 3339, 3346, 3358, 3362, 3336, 0, {37274, 0, 0, 0}, 27, 27, { 1250, 20880, 10029, 852, 0, ';' }}, + {0x001C, 0x007F, 257, -1, 3366, 3369, 3378, 3384, 3388, 3366, 0, {37298, 0, 0, 0}, 28, 28, { 1250, 20880, 10029, 852, 0, ';' }}, + {0x001D, 0x007F, 257, -1, 3392, 3395, 3403, 3411, 3415, 3392, 0, {36792, 0, 0, 0}, 29, 29, { 1252, 20278, 10000, 850, 0, ';' }}, + {0x001E, 0x007F, 512, -1, 3419, 3422, 3427, 3437, 3441, 3419, 0, {0, 37317, 0, 0}, 30, 30, { 874, 20838, 10021, 874, 0, ',' }}, + {0x001F, 0x007F, 257, -1, 3445, 3448, 3456, 3465, 3469, 3445, 0, {37348, 0, 0, 0}, 31, 31, { 1254, 20905, 10081, 857, 0, ';' }}, + {0x0020, 0x007F, 257, -1, 3473, 3476, 3481, 3490, 3494, 3473, 0, {37362, 0, 0, 0}, 32, 32, { 1256, 20420, 10004, 720, 1, ';' }}, + {0x0021, 0x007F, 257, -1, 3498, 3501, 3512, 3522, 3526, 3498, 0, {37392, 0, 0, 0}, 33, 33, { 1252, 500, 10000, 850, 0, ';' }}, + {0x0022, 0x007F, 257, -1, 3530, 3533, 3543, 3564, 3568, 3530, 0, {37411, 0, 0, 0}, 34, 34, { 1251, 500, 10017, 866, 0, ';' }}, + {0x0023, 0x007F, 257, -1, 3572, 3575, 3586, 3607, 3611, 3572, 0, {37457, 0, 0, 0}, 35, 35, { 1251, 500, 10007, 866, 0, ';' }}, + {0x0024, 0x007F, 257, -1, 3615, 3618, 3628, 3642, 3646, 3615, 0, {37499, 0, 0, 0}, 36, 36, { 1250, 20880, 10029, 852, 0, ';' }}, + {0x0025, 0x007F, 257, -1, 3650, 3653, 3662, 3668, 3672, 3650, 0, {37521, 0, 0, 0}, 37, 37, { 1257, 500, 10029, 775, 0, ';' }}, + {0x0026, 0x007F, 257, -1, 3676, 3679, 3687, 3697, 3701, 3676, 0, {37541, 0, 0, 0}, 38, 38, { 1257, 500, 10029, 775, 0, ';' }}, + {0x0027, 0x007F, 257, -1, 3705, 3708, 3719, 3729, 3733, 3705, 0, {37560, 0, 0, 0}, 39, 39, { 1257, 500, 10029, 775, 0, ';' }}, + {0x0028, 0x007F, 257, -1, 3737, 3740, 3746, 3759, 3763, 3737, 0, {0, 0, 0, 0}, 40, 40, { 1251, 20880, 10007, 866, 0, ';' }}, + {0x0029, 0x007F, 257, -1, 3767, 3770, 3778, 3789, 3793, 3767, 0, {37583, 0, 0, 0}, 41, 41, { 1256, 20420, 10004, 720, 1, ';' }}, + {0x002A, 0x007F, 257, -1, 3797, 3800, 3811, 3826, 3830, 3797, 0, {37607, 0, 0, 0}, 42, 42, { 1258, 500, 10000, 1258, 0, ',' }}, + {0x002B, 0x007F, 257, -1, 3834, 3837, 3846, 3861, 3865, 3834, 0, {37622, 0, 0, 0}, 43, 43, { 0, 500, 2, 1, 0, ',' }}, + {0x002C, 0x007F, 257, -1, 3869, 3872, 3884, 3896, 3900, 3869, 0, {37652, 0, 0, 0}, 44, 44, { 1254, 20905, 10081, 857, 0, ';' }}, + {0x002D, 0x007F, 257, -1, 3904, 3907, 3914, 3922, 3926, 3904, 0, {37671, 0, 0, 0}, 45, 45, { 1252, 500, 2, 850, 0, ';' }}, + {0x002E, 0x007F, 257, -1, 3930, 3934, 3948, 3966, 3930, 3930, 0, {37691, 0, 0, 0}, 46, 46, { 1252, 870, 10000, 850, 0, ';' }}, + {0x002F, 0x007F, 257, -1, 3970, 3973, 3984, 4005, 4009, 3970, 0, {37712, 0, 0, 0}, 47, 47, { 1251, 500, 10007, 866, 0, ';' }}, + {0x0030, 0x007F, 257, -1, 4013, 4016, 4031, 4039, 4043, 4013, 0, {0, 0, 0, 0}, 48, 48, { 0, 500, 2, 1, 0, ';' }}, + {0x0031, 0x007F, 257, -1, 4047, 4050, 4057, 4066, 4070, 4047, 0, {0, 0, 0, 0}, 49, 49, { 0, 500, 2, 1, 0, ';' }}, + {0x0032, 0x007F, 257, -1, 4074, 4077, 4084, 4093, 4097, 4074, 0, {0, 0, 0, 0}, 50, 50, { 1252, 500, 10000, 850, 0, ';' }}, + {0x0034, 0x007F, 257, -1, 4101, 4104, 4110, 4119, 4123, 4101, 0, {0, 0, 0, 0}, 51, 51, { 1252, 500, 10000, 850, 0, ';' }}, + {0x0035, 0x007F, 257, -1, 4127, 4130, 4135, 4143, 4147, 4127, 0, {37756, 0, 0, 0}, 52, 52, { 1252, 500, 10000, 850, 0, ';' }}, + {0x0036, 0x007F, 257, -1, 4151, 4154, 4154, 4164, 4168, 4151, 0, {37781, 0, 0, 0}, 53, 53, { 1252, 500, 10000, 850, 0, ';' }}, + {0x0037, 0x007F, 257, -1, 4172, 4175, 4184, 4206, 4210, 4172, 0, {37801, 0, 0, 0}, 54, 54, { 0, 500, 2, 1, 0, ';' }}, + {0x0038, 0x007F, 257, -1, 4214, 4217, 4225, 4235, 4239, 4214, 0, {37866, 0, 0, 0}, 55, 55, { 1252, 20277, 10079, 850, 0, ';' }}, + {0x0039, 0x007F, 257, -1, 4243, 4246, 4252, 4271, 4275, 4243, 0, {37890, 0, 0, 0}, 56, 56, { 0, 500, 2, 1, 0, ',' }}, + {0x003A, 0x007F, 257, -1, 4279, 4282, 4290, 4296, 4300, 4279, 0, {37943, 0, 0, 0}, 57, 57, { 0, 500, 2, 1, 0, ';' }}, + {0x003B, 0x007F, 257, -1, 4304, 4307, 4321, 4338, 4342, 4304, 0, {37964, 0, 0, 0}, 58, 58, { 1252, 20277, 10000, 850, 0, ';' }}, + {0x003C, 0x007F, 257, -1, 4346, 4349, 4355, 4363, 4367, 4346, 0, {37983, 0, 0, 0}, 59, 59, { 1252, 500, 10000, 850, 0, ';' }}, + {0x003E, 0x007F, 257, -1, 4371, 4374, 4380, 4394, 4398, 4371, 0, {38004, 0, 0, 0}, 60, 60, { 1252, 500, 10000, 850, 0, ';' }}, + {0x003F, 0x007F, 257, -1, 4402, 4405, 4412, 4432, 4436, 4402, 0, {38021, 0, 0, 0}, 61, 61, { 0, 500, 2, 1, 0, ';' }}, + {0x0040, 0x007F, 257, -1, 4440, 4443, 4450, 4467, 4471, 4440, 0, {38063, 0, 0, 0}, 62, 62, { 1251, 20880, 10007, 866, 0, ';' }}, + {0x0041, 0x007F, 257, -1, 4475, 4478, 4486, 4496, 4500, 4475, 0, {38103, 0, 0, 0}, 63, 63, { 1252, 500, 10000, 437, 0, ';' }}, + {0x0042, 0x007F, 257, -1, 4504, 4507, 4515, 4527, 4531, 4504, 0, {38124, 0, 0, 0}, 64, 64, { 1250, 20880, 10029, 852, 0, ';' }}, + {0x0043, 0x007F, 257, -1, 4535, 4538, 4544, 4553, 4557, 4535, 0, {38146, 0, 0, 0}, 65, 65, { 1254, 500, 10029, 857, 0, ';' }}, + {0x0045, 0x007F, 257, -1, 4561, 4564, 4571, 4587, 4591, 4561, 0, {38164, 0, 0, 0}, 66, 66, { 0, 500, 2, 1, 0, ',' }}, + {0x0046, 0x007F, 257, -1, 4595, 4598, 4606, 4625, 4629, 4595, 0, {38235, 0, 0, 0}, 67, 67, { 0, 500, 2, 1, 0, ',' }}, + {0x0047, 0x007F, 257, -1, 4633, 4636, 4645, 4667, 4671, 4633, 0, {38282, 0, 0, 0}, 68, 68, { 0, 500, 2, 1, 0, ',' }}, + {0x0048, 0x007F, 257, -1, 4675, 4678, 4683, 4699, 4703, 4675, 0, {0, 0, 0, 0}, 69, 69, { 0, 500, 2, 1, 0, ',' }}, + {0x0049, 0x007F, 257, -1, 4707, 4710, 4716, 4732, 4736, 4707, 0, {38338, 0, 0, 0}, 70, 70, { 0, 500, 2, 1, 0, ',' }}, + {0x004A, 0x007F, 257, -1, 4740, 4743, 4750, 4769, 4773, 4740, 0, {38403, 0, 0, 0}, 71, 71, { 0, 500, 2, 1, 0, ',' }}, + {0x004B, 0x007F, 257, -1, 1701, 4777, 4785, 4801, 4805, 1701, 0, {38468, 0, 0, 0}, 72, 72, { 0, 500, 2, 1, 0, ',' }}, + {0x004C, 0x007F, 257, -1, 4809, 4812, 4822, 4841, 4845, 4809, 0, {38533, 0, 0, 0}, 73, 73, { 0, 500, 2, 1, 0, ',' }}, + {0x004D, 0x007F, 257, -1, 4849, 4852, 4861, 4883, 4887, 4849, 0, {38577, 0, 0, 0}, 74, 74, { 0, 500, 2, 1, 0, ',' }}, + {0x004E, 0x007F, 257, -1, 4891, 4894, 4902, 4918, 4922, 4891, 0, {38630, 0, 0, 0}, 75, 75, { 0, 500, 2, 1, 0, ',' }}, + {0x0050, 0x007F, 257, -1, 4926, 4929, 4939, 4952, 4956, 4926, 0, {38692, 0, 0, 0}, 76, 76, { 1251, 20880, 10007, 866, 0, ';' }}, + {0x0051, 0x007F, 257, -1, 4960, 4963, 4971, 4996, 5000, 4960, 0, {0, 0, 0, 0}, 77, 77, { 0, 500, 2, 1, 0, ',' }}, + {0x0052, 0x007F, 257, -1, 5004, 5007, 5013, 5021, 5025, 5004, 0, {38724, 0, 0, 0}, 78, 78, { 1252, 20285, 10000, 850, 0, ';' }}, + {0x0053, 0x007F, 257, -1, 5029, 5032, 5038, 5054, 5058, 5029, 0, {38740, 0, 0, 0}, 79, 79, { 0, 500, 2, 1, 0, ',' }}, + {0x0054, 0x007F, 257, -1, 5062, 5065, 5069, 5079, 5083, 5062, 0, {38795, 0, 0, 0}, 80, 80, { 0, 500, 2, 1, 0, ';' }}, + {0x0055, 0x007F, 257, -1, 5087, 5090, 5098, 5117, 5121, 5087, 0, {38841, 0, 0, 0}, 81, 81, { 0, 500, 2, 1, 0, ';' }}, + {0x0056, 0x007F, 257, -1, 5125, 5128, 5137, 5144, 5148, 5125, 0, {36899, 0, 0, 0}, 82, 82, { 1252, 500, 10000, 850, 0, ';' }}, + {0x0057, 0x007F, 257, -1, 5152, 5156, 5164, 5183, 5152, 5152, 0, {0, 0, 0, 0}, 83, 83, { 0, 500, 2, 1, 0, ',' }}, + {0x005B, 0x007F, 257, -1, 5187, 5190, 5198, 5214, 5218, 5187, 0, {38912, 0, 0, 0}, 84, 84, { 0, 500, 2, 1, 0, ';' }}, + {0x005C, 0x007F, 257, -1, 5222, 5226, 5235, 5245, 5222, 5222, 0, {38978, 0, 0, 0}, 85, 85, { 0, 500, 2, 1, 0, ',' }}, + {0x005E, 0x007F, 257, -1, 5, 5249, 5257, 5270, 5274, 5, 0, {39017, 0, 0, 0}, 86, 86, { 0, 500, 2, 1, 0, ';' }}, + {0x005F, 0x007F, 257, -1, 5278, 5282, 5306, 5329, 5278, 5278, 0, {0, 0, 0, 0}, 87, 87, { 1252, 20297, 10000, 850, 0, ';' }}, + {0x0061, 0x007F, 257, -1, 5333, 5336, 5343, 5362, 5366, 5333, 0, {39065, 0, 0, 0}, 88, 88, { 0, 500, 2, 1, 0, ',' }}, + {0x0062, 0x007F, 257, -1, 5370, 5373, 5389, 5400, 5404, 5370, 0, {39115, 0, 0, 0}, 89, 89, { 1252, 500, 10000, 850, 0, ';' }}, + {0x0063, 0x007F, 1024, -1, 5408, 5411, 5418, 5427, 5431, 5408, 0, {0, 0, 0, 0}, 90, 90, { 0, 500, 2, 1, 1, ';' }}, + {0x0064, 0x007F, 257, -1, 5435, 5439, 5439, 5448, 5435, 5435, 0, {39138, 0, 0, 0}, 91, 91, { 1252, 500, 10000, 437, 0, ';' }}, + {0x0067, 0x007F, 257, -1, 5452, 5455, 5461, 5468, 5472, 5452, 0, {0, 0, 0, 0}, 92, 92, { 1252, 20297, 10000, 850, 0, ';' }}, + {0x0068, 0x007F, 257, -1, 5476, 5479, 5479, 5485, 5489, 5476, 0, {0, 0, 0, 0}, 93, 93, { 1252, 37, 10000, 437, 0, ';' }}, + {0x006A, 0x007F, 257, -1, 5493, 5496, 5503, 5518, 5522, 5493, 0, {0, 0, 0, 0}, 94, 94, { 1252, 37, 10000, 437, 0, ';' }}, + {0x006C, 0x007F, 257, -1, 5526, 5530, 5545, 5562, 5526, 5526, 0, {0, 0, 0, 0}, 95, 95, { 1252, 500, 10000, 850, 0, ';' }}, + {0x006E, 0x007F, 257, -1, 5566, 5569, 5583, 5599, 5603, 5566, 0, {39162, 0, 0, 0}, 96, 96, { 1252, 20297, 10000, 850, 0, ';' }}, + {0x006F, 0x007F, 257, -1, 5607, 5610, 5622, 5634, 5638, 5607, 0, {39186, 0, 0, 0}, 97, 97, { 1252, 20277, 10000, 850, 0, ';' }}, + {0x0070, 0x007F, 257, -1, 5642, 5645, 5645, 5650, 5654, 5642, 0, {0, 0, 0, 0}, 98, 98, { 1252, 37, 10000, 437, 0, ';' }}, + {0x0072, 0x007F, 257, -1, 5658, 5661, 5667, 5674, 5678, 5658, 0, {0, 0, 0, 0}, 99, 99, { 0, 500, 2, 1, 0, ';' }}, + {0x0073, 0x007F, 257, -1, 5682, 5685, 5694, 5707, 5711, 5682, 0, {0, 0, 0, 0}, 100, 100, { 0, 500, 2, 1, 0, ';' }}, + {0x0075, 0x007F, 257, -1, 5715, 5719, 5728, 5746, 5715, 5715, 0, {0, 0, 0, 0}, 101, 101, { 1252, 37, 10000, 437, 0, ';' }}, + {0x0077, 0x007F, 257, -1, 5750, 5753, 5760, 5769, 5773, 5750, 0, {0, 0, 0, 0}, 102, 102, { 0, 500, 2, 1, 0, ';' }}, + {0x0078, 0x007F, 257, -1, 5777, 5780, 5791, 5801, 5805, 5777, 0, {39213, 0, 0, 0}, 103, 103, { 0, 500, 2, 1, 0, ';' }}, + {0x007E, 0x007F, 257, -1, 5809, 5812, 5819, 5829, 5833, 5809, 0, {39226, 0, 0, 0}, 104, 104, { 1252, 20297, 10000, 850, 0, ';' }}, + {0x0080, 0x007F, 257, -1, 5837, 5840, 5847, 5864, 5868, 5837, 0, {39246, 0, 0, 0}, 105, 105, { 1256, 20420, 10004, 720, 1, ',' }}, + {0x0084, 0x007F, 257, -1, 5872, 5876, 5889, 5908, 5872, 5872, 0, {39282, 0, 0, 0}, 106, 106, { 1252, 20297, 10000, 850, 0, ';' }}, + {0x0085, 0x007F, 257, -1, 5912, 5916, 5922, 5940, 5912, 5912, 0, {0, 0, 0, 0}, 107, 107, { 1251, 20880, 10007, 866, 0, ';' }}, + {0x0087, 0x007F, 257, -1, 5944, 5947, 5947, 5959, 5963, 5944, 0, {0, 0, 0, 0}, 108, 108, { 1252, 37, 10000, 437, 0, ';' }}, + {0x0091, 0x007F, 257, -1, 5967, 5970, 5986, 5996, 6000, 5967, 0, {39307, 0, 0, 0}, 109, 109, { 1252, 20285, 10000, 850, 0, ';' }}, + {0x0401, 0x0001, 768, 111, 6004, 6010, 6032, 2557, 2561, 2532, 297, {0, 0, 36669, 0}, 110, 110, { 1256, 20420, 10004, 720, 1, ';' }}, + {0x0402, 0x0002, 257, 11, 6096, 6102, 6123, 2597, 2601, 2565, 6161, {36697, 0, 0, 0}, 111, 111, { 1251, 21025, 10007, 866, 0, ';' }}, + {0x0403, 0x0003, 257, 38, 6164, 6170, 6186, 2624, 2628, 2605, 6204, {36739, 0, 0, 0}, 112, 112, { 1252, 500, 10000, 850, 0, ';' }}, + {0x0404, 0x7C04, 257, 126, 6207, 6213, 6235, 6251, 2672, 2676, 6255, {39332, 0, 0, 0}, 113, 113, { 950, 500, 10002, 950, 0, ',' }}, + {0x0405, 0x0005, 257, 29, 6258, 6264, 6287, 2733, 2737, 2714, 6317, {36766, 0, 0, 0}, 114, 114, { 1250, 500, 10029, 852, 0, ';' }}, + {0x0406, 0x0006, 257, 31, 6320, 6326, 6343, 2757, 2761, 2741, 6359, {36792, 0, 0, 0}, 115, 115, { 1252, 20277, 10000, 850, 0, ';' }}, + {0x0407, 0x0007, 257, 30, 6362, 6368, 6385, 2783, 2787, 2765, 6407, {36813, 0, 0, 0}, 116, 116, { 1252, 20273, 10000, 850, 0, ';' }}, + {0x0408, 0x0008, 257, 46, 6410, 6416, 6431, 2817, 2821, 2791, 6463, {36838, 0, 0, 0}, 117, 117, { 1253, 20273, 10006, 737, 0, ';' }}, + {0x0409, 0x0009, 257, 128, 6466, 6472, 6472, 2836, 2840, 2825, 6496, {36880, 0, 0, 0}, 118, 118, { 1252, 37, 10000, 437, 0, ',' }}, + {0x040B, 0x000B, 257, 40, 6499, 6505, 6523, 2889, 2893, 2872, 6537, {36921, 0, 0, 0}, 119, 119, { 1252, 20278, 10000, 850, 0, ';' }}, + {0x040C, 0x000C, 257, 42, 6540, 6546, 6562, 2917, 2921, 2897, 6581, {36946, 0, 0, 0}, 120, 120, { 1252, 20297, 10000, 850, 0, ';' }}, + {0x040D, 0x000D, 257, 55, 6584, 6590, 6606, 2946, 2950, 2925, 6630, {36968, 0, 0, 0}, 121, 121, { 1255, 500, 10005, 862, 1, ',' }}, + {0x040E, 0x000E, 257, 52, 6633, 6639, 6659, 2974, 2978, 2954, 6682, {37005, 0, 0, 0}, 122, 122, { 1250, 500, 10029, 852, 0, ';' }}, + {0x040F, 0x000F, 257, 59, 6685, 6691, 6711, 3005, 3009, 2982, 6731, {37021, 0, 0, 0}, 123, 123, { 1252, 20871, 10079, 850, 0, ';' }}, + {0x0410, 0x0010, 257, 60, 6734, 6740, 6756, 3033, 3037, 3013, 6774, {37041, 0, 0, 0}, 124, 124, { 1252, 20280, 10000, 850, 0, ';' }}, + {0x0411, 0x0011, 257, 63, 6777, 6783, 6800, 3063, 3067, 3041, 6819, {37063, 0, 0, 0}, 125, 125, { 932, 20290, 10001, 932, 0, ',' }}, + {0x0412, 0x0012, 257, 67, 6822, 6828, 6849, 3091, 3095, 3071, 6874, {37090, 0, 0, 0}, 126, 126, { 949, 20833, 10003, 949, 0, ',' }}, + {0x0413, 0x0013, 257, 92, 6877, 6883, 6903, 3119, 3123, 3099, 6926, {37097, 0, 0, 0}, 127, 127, { 1252, 500, 10000, 850, 0, ';' }}, + {0x0414, 0x7C14, 257, 93, 6929, 6935, 6962, 3146, 3150, 3154, 6984, {36792, 0, 0, 0}, 128, 128, { 1252, 20277, 10000, 850, 0, ';' }}, + {0x0415, 0x0015, 257, 101, 6987, 6993, 7009, 3174, 3178, 3157, 7025, {37119, 0, 0, 0}, 129, 129, { 1250, 20880, 10029, 852, 0, ';' }}, + {0x0416, 0x0016, 257, 15, 7028, 7034, 7054, 3207, 3211, 3182, 7074, {37143, 0, 0, 0}, 130, 130, { 1252, 500, 10000, 850, 0, ';' }}, + {0x0417, 0x0017, 257, 21, 7077, 7083, 7105, 3236, 3240, 3215, 300, {37166, 0, 0, 0}, 131, 131, { 1252, 20273, 10000, 850, 0, ';' }}, + {0x0418, 0x0018, 257, 107, 7124, 7130, 7149, 3265, 3269, 3244, 7169, {37186, 0, 0, 0}, 132, 132, { 1250, 20880, 10029, 852, 0, ';' }}, + {0x0419, 0x0019, 257, 109, 7172, 7178, 7195, 3299, 3303, 3273, 7225, {37205, 0, 0, 0}, 133, 133, { 1251, 20880, 10007, 866, 0, ';' }}, + {0x041A, 0x001A, 257, 50, 7228, 7234, 7253, 3328, 3332, 3307, 7273, {37251, 0, 0, 0}, 134, 134, { 1250, 500, 10082, 852, 0, ';' }}, + {0x041B, 0x001B, 257, 115, 7276, 7282, 7300, 3358, 3362, 3336, 7324, {37274, 0, 0, 0}, 135, 135, { 1250, 20880, 10029, 852, 0, ';' }}, + {0x041C, 0x001C, 257, 2, 7327, 7333, 7352, 3384, 3388, 3366, 7370, {37298, 0, 0, 0}, 136, 136, { 1250, 20880, 10029, 852, 0, ';' }}, + {0x041D, 0x001D, 257, 112, 7373, 7379, 7396, 3411, 3415, 3392, 7414, {36792, 0, 0, 0}, 137, 137, { 1252, 20278, 10000, 850, 0, ';' }}, + {0x041E, 0x001E, 512, 120, 7417, 7423, 7439, 3437, 3441, 3419, 7461, {0, 37317, 0, 0}, 138, 138, { 874, 20838, 10021, 874, 0, ',' }}, + {0x041F, 0x001F, 257, 124, 7464, 7470, 7487, 3465, 3469, 3445, 7507, {37348, 0, 0, 0}, 139, 139, { 1254, 20905, 10081, 857, 0, ';' }}, + {0x0420, 0x0020, 257, 100, 7510, 7516, 7532, 3490, 3494, 3473, 7558, {37362, 0, 0, 0}, 140, 140, { 1256, 20420, 10004, 720, 1, ';' }}, + {0x0421, 0x0021, 257, 53, 7561, 7567, 7590, 3522, 3526, 3498, 7612, {37392, 0, 0, 0}, 141, 141, { 1252, 500, 10000, 850, 0, ';' }}, + {0x0422, 0x0022, 257, 127, 7615, 7621, 7641, 3564, 3568, 3530, 7679, {37411, 0, 0, 0}, 142, 142, { 1251, 500, 10017, 866, 0, ';' }}, + {0x0423, 0x0023, 257, 17, 7682, 7688, 7709, 3607, 3611, 3572, 7749, {37457, 0, 0, 0}, 143, 143, { 1251, 500, 10007, 866, 0, ';' }}, + {0x0424, 0x0024, 257, 114, 7752, 7758, 7779, 3642, 3646, 3615, 7805, {37499, 0, 0, 0}, 144, 144, { 1250, 20880, 10029, 852, 0, ';' }}, + {0x0425, 0x0025, 257, 35, 7808, 7814, 7833, 3668, 3672, 3650, 7847, {37521, 0, 0, 0}, 145, 145, { 1257, 500, 10029, 775, 0, ';' }}, + {0x0426, 0x0026, 257, 76, 7850, 7856, 7873, 3697, 3701, 3676, 7893, {37541, 0, 0, 0}, 146, 146, { 1257, 500, 10029, 775, 0, ';' }}, + {0x0427, 0x0027, 257, 74, 7896, 7902, 7925, 3729, 3733, 3705, 7945, {37560, 0, 0, 0}, 147, 147, { 1257, 500, 10029, 775, 0, ';' }}, + {0x0428, 0x7C28, 257, 121, 7948, 7959, 7988, 3759, 3763, 3737, 8024, {0, 0, 0, 0}, 148, 148, { 1251, 20880, 10007, 866, 0, ';' }}, + {0x0429, 0x0029, 257, 58, 8027, 8033, 8048, 3789, 3793, 3767, 8072, {37583, 0, 0, 0}, 149, 149, { 1256, 20420, 10004, 720, 1, ';' }}, + {0x042A, 0x002A, 257, 132, 8075, 8081, 8102, 3826, 3830, 3797, 8130, {37607, 0, 0, 0}, 150, 150, { 1258, 500, 10000, 1258, 0, ',' }}, + {0x042B, 0x002B, 257, 3, 8133, 8139, 8158, 3861, 3865, 3834, 49, {37622, 0, 0, 0}, 151, 151, { 0, 500, 2, 1, 0, ',' }}, + {0x042C, 0x782C, 257, 7, 8192, 8203, 8235, 3896, 3900, 3869, 8261, {37652, 0, 0, 0}, 152, 152, { 1254, 20905, 10081, 857, 0, ';' }}, + {0x042D, 0x002D, 257, 38, 8264, 8270, 8285, 3922, 3926, 3904, 6204, {37671, 0, 0, 0}, 153, 153, { 1252, 500, 2, 850, 0, ';' }}, + {0x042E, 0x002E, 257, 30, 8304, 8311, 8335, 3966, 3930, 3930, 6407, {37691, 0, 0, 0}, 154, 154, { 1252, 870, 10000, 850, 0, ';' }}, + {0x042F, 0x002F, 257, 82, 8363, 8369, 8392, 4005, 4009, 3970, 8436, {37712, 0, 0, 0}, 155, 155, { 1251, 500, 10007, 866, 0, ';' }}, + {0x0430, 0x0030, 257, 134, 8439, 8445, 4031, 4039, 4043, 4013, 8475, {0, 0, 0, 0}, 156, 156, { 0, 500, 2, 1, 0, ';' }}, + {0x0431, 0x0031, 257, 134, 8478, 8484, 4057, 4066, 4070, 4047, 8475, {0, 0, 0, 0}, 157, 157, { 0, 500, 2, 1, 0, ';' }}, + {0x0432, 0x0032, 257, 134, 8506, 8512, 4084, 4093, 4097, 4074, 8475, {0, 0, 0, 0}, 158, 158, { 1252, 500, 10000, 850, 0, ';' }}, + {0x0434, 0x0034, 257, 134, 8534, 8540, 4110, 4119, 4123, 4101, 8475, {0, 0, 0, 0}, 159, 159, { 1252, 500, 10000, 850, 0, ';' }}, + {0x0435, 0x0035, 257, 134, 8561, 8567, 8587, 4143, 4147, 4127, 8475, {37756, 0, 0, 0}, 160, 160, { 1252, 500, 10000, 850, 0, ';' }}, + {0x0436, 0x0036, 257, 134, 8612, 8618, 8643, 4164, 4168, 4151, 8475, {37781, 0, 0, 0}, 161, 161, { 1252, 500, 10000, 850, 0, ';' }}, + {0x0437, 0x0037, 257, 44, 8667, 8673, 8692, 4206, 4210, 4172, 8747, {37801, 0, 0, 0}, 162, 162, { 0, 500, 2, 1, 0, ';' }}, + {0x0438, 0x0038, 257, 41, 8750, 8756, 8780, 4235, 4239, 4214, 8801, {37866, 0, 0, 0}, 163, 163, { 1252, 20277, 10079, 850, 0, ';' }}, + {0x0439, 0x0039, 257, 56, 8804, 8810, 8824, 4271, 4275, 4243, 8858, {37890, 0, 0, 0}, 164, 164, { 0, 500, 2, 1, 0, ',' }}, + {0x043A, 0x003A, 257, 87, 8861, 8867, 8883, 4296, 4300, 4279, 8897, {37943, 0, 0, 0}, 165, 165, { 0, 500, 2, 1, 0, ';' }}, + {0x043B, 0x003B, 257, 93, 8900, 8906, 8929, 4338, 4342, 4304, 6984, {37964, 0, 0, 0}, 166, 166, { 1252, 20277, 10000, 850, 0, ';' }}, + {0x043E, 0x003E, 257, 89, 8954, 8960, 8977, 4394, 4398, 4371, 9002, {38004, 0, 0, 0}, 167, 167, { 1252, 500, 10000, 850, 0, ';' }}, + {0x043F, 0x003F, 257, 69, 9005, 9011, 9031, 4432, 4436, 4402, 9072, {38021, 0, 0, 0}, 168, 168, { 0, 500, 2, 1, 0, ';' }}, + {0x0440, 0x0040, 257, 65, 9075, 9081, 9101, 4467, 4471, 4440, 9141, {38063, 0, 0, 0}, 169, 169, { 1251, 20880, 10007, 866, 0, ';' }}, + {0x0441, 0x0041, 257, 64, 9144, 9150, 9166, 4496, 4500, 4475, 9184, {38103, 0, 0, 0}, 170, 170, { 1252, 500, 10000, 437, 0, ';' }}, + {0x0442, 0x0042, 257, 122, 9187, 9193, 9216, 4527, 4531, 4504, 9244, {38124, 0, 0, 0}, 171, 171, { 1250, 20880, 10029, 852, 0, ';' }}, + {0x0443, 0x7C43, 257, 130, 9247, 9258, 9284, 4553, 4557, 4535, 9308, {38146, 0, 0, 0}, 172, 172, { 1254, 500, 10029, 857, 0, ';' }}, + {0x0445, 0x0045, 257, 56, 9311, 9317, 9332, 4587, 4591, 4561, 8858, {38164, 0, 0, 0}, 173, 173, { 0, 500, 2, 1, 0, ',' }}, + {0x0447, 0x0047, 257, 56, 9363, 9369, 9386, 4667, 4671, 4633, 8858, {38282, 0, 0, 0}, 174, 174, { 0, 500, 2, 1, 0, ',' }}, + {0x0448, 0x0048, 257, 56, 9423, 9429, 9442, 4699, 4703, 4675, 8858, {0, 0, 0, 0}, 175, 175, { 0, 500, 2, 1, 0, ',' }}, + {0x0449, 0x0049, 257, 56, 9473, 9479, 9493, 4732, 4736, 4707, 8858, {38338, 0, 0, 0}, 176, 176, { 0, 500, 2, 1, 0, ',' }}, + {0x044A, 0x004A, 257, 56, 9533, 9539, 9554, 4769, 4773, 4740, 8858, {38403, 0, 0, 0}, 177, 177, { 0, 500, 2, 1, 0, ',' }}, + {0x044B, 0x004B, 257, 56, 9601, 9607, 9623, 4801, 4805, 1701, 8858, {38468, 0, 0, 0}, 178, 178, { 0, 500, 2, 1, 0, ',' }}, + {0x044C, 0x004C, 257, 56, 9654, 9660, 9678, 4841, 4845, 4809, 8858, {38533, 0, 0, 0}, 179, 179, { 0, 500, 2, 1, 0, ',' }}, + {0x044D, 0x004D, 257, 56, 9718, 9724, 9741, 4883, 4887, 4849, 8858, {38577, 0, 0, 0}, 180, 180, { 0, 500, 2, 1, 0, ',' }}, + {0x044E, 0x004E, 257, 56, 9778, 9784, 9800, 4918, 4922, 4891, 8858, {38630, 0, 0, 0}, 181, 181, { 0, 500, 2, 1, 0, ',' }}, + {0x0450, 0x7850, 257, 85, 9831, 9837, 9858, 9886, 4956, 4926, 9890, {38692, 0, 0, 0}, 182, 182, { 1251, 20880, 10007, 866, 0, ';' }}, + {0x0451, 0x0051, 257, 25, 9893, 9899, 9915, 4996, 5000, 4960, 9961, {0, 0, 0, 0}, 183, 183, { 0, 500, 2, 1, 0, ',' }}, + {0x0452, 0x0052, 257, 43, 9964, 9970, 9993, 5021, 5025, 5004, 10020, {38724, 0, 0, 0}, 184, 184, { 1252, 20285, 10000, 850, 0, ';' }}, + {0x0453, 0x0053, 257, 66, 10023, 10029, 10046, 5054, 5058, 5029, 10086, {38740, 0, 0, 0}, 185, 185, { 0, 500, 2, 1, 0, ',' }}, + {0x0454, 0x0054, 257, 70, 10089, 10095, 10106, 5079, 5083, 5062, 10128, {38795, 0, 0, 0}, 186, 186, { 0, 500, 2, 1, 0, ';' }}, + {0x0455, 0x0055, 257, 84, 10131, 10137, 10163, 5117, 5121, 5087, 10203, {38841, 0, 0, 0}, 187, 187, { 0, 500, 2, 1, 0, ';' }}, + {0x0456, 0x0056, 257, 38, 10206, 10212, 10229, 5144, 5148, 5125, 6204, {36899, 0, 0, 0}, 188, 188, { 1252, 500, 10000, 850, 0, ';' }}, + {0x0457, 0x0057, 257, 56, 10246, 10253, 10269, 5183, 5152, 5152, 8858, {0, 0, 0, 0}, 189, 189, { 0, 500, 2, 1, 0, ',' }}, + {0x045B, 0x005B, 257, 73, 10303, 10309, 10329, 5214, 5218, 5187, 10379, {38912, 0, 0, 0}, 190, 190, { 0, 500, 2, 1, 0, ';' }}, + {0x045E, 0x005E, 257, 39, 10382, 10388, 10407, 5270, 5274, 5, 10438, {39017, 0, 0, 0}, 191, 191, { 0, 500, 2, 1, 0, ';' }}, + {0x0461, 0x0061, 257, 94, 10441, 10447, 10462, 5362, 5366, 5333, 10499, {39065, 0, 0, 0}, 192, 192, { 0, 500, 2, 1, 0, ',' }}, + {0x0462, 0x0062, 257, 92, 10502, 10508, 10538, 5400, 5404, 5370, 6926, {39115, 0, 0, 0}, 193, 193, { 1252, 500, 10000, 850, 0, ';' }}, + {0x0463, 0x0063, 1024, 1, 10561, 10567, 10588, 5427, 5431, 5408, 10618, {0, 0, 0, 0}, 194, 194, { 0, 500, 2, 1, 1, ';' }}, + {0x0464, 0x0064, 257, 99, 10621, 10628, 10651, 5448, 5435, 5435, 10672, {39138, 0, 0, 0}, 195, 195, { 1252, 500, 10000, 437, 0, ';' }}, + {0x0468, 0x7C68, 257, 90, 10675, 10686, 10709, 5485, 5489, 5476, 10726, {0, 0, 0, 0}, 196, 196, { 1252, 37, 10000, 437, 0, ';' }}, + {0x046A, 0x006A, 257, 90, 10729, 10735, 10752, 5518, 5522, 5493, 10726, {0, 0, 0, 0}, 197, 197, { 1252, 37, 10000, 437, 0, ';' }}, + {0x046C, 0x006C, 257, 134, 10798, 10805, 5545, 5562, 5526, 5526, 8475, {0, 0, 0, 0}, 198, 198, { 1252, 500, 10000, 850, 0, ';' }}, + {0x046E, 0x006E, 257, 75, 10835, 10841, 10868, 5599, 5603, 5566, 10898, {39162, 0, 0, 0}, 199, 199, { 1252, 20297, 10000, 850, 0, ';' }}, + {0x046F, 0x006F, 257, 45, 10901, 10907, 10931, 5634, 5638, 5607, 10962, {39186, 0, 0, 0}, 200, 200, { 1252, 20277, 10000, 850, 0, ';' }}, + {0x0470, 0x0070, 257, 90, 10965, 10971, 10971, 5650, 5654, 5642, 10726, {0, 0, 0, 0}, 201, 201, { 1252, 37, 10000, 437, 0, ';' }}, + {0x0472, 0x0072, 257, 39, 10986, 10992, 11009, 5674, 5678, 5658, 10438, {0, 0, 0, 0}, 202, 202, { 0, 500, 2, 1, 0, ';' }}, + {0x0473, 0x0073, 257, 39, 11029, 11035, 11055, 11086, 5711, 5682, 10438, {0, 0, 0, 0}, 203, 203, { 0, 500, 2, 1, 0, ';' }}, + {0x0475, 0x0075, 257, 128, 11090, 11097, 11122, 5746, 5715, 5715, 6496, {0, 0, 0, 0}, 204, 204, { 1252, 37, 10000, 437, 0, ';' }}, + {0x0477, 0x0077, 257, 117, 11165, 11171, 11188, 5769, 5773, 5750, 11210, {0, 0, 0, 0}, 205, 205, { 0, 500, 2, 1, 0, ';' }}, + {0x0478, 0x0078, 257, 25, 11213, 11219, 11238, 5801, 5805, 5777, 9961, {39213, 0, 0, 0}, 206, 206, { 0, 500, 2, 1, 0, ';' }}, + {0x047E, 0x007E, 257, 42, 11257, 11263, 11279, 5829, 5833, 5809, 6581, {39226, 0, 0, 0}, 207, 207, { 1252, 20297, 10000, 850, 0, ';' }}, + {0x0480, 0x0080, 257, 25, 11298, 11304, 11319, 5864, 5868, 5837, 9961, {39246, 0, 0, 0}, 208, 208, { 1256, 20420, 10004, 720, 1, ',' }}, + {0x0484, 0x0084, 257, 42, 11349, 11356, 11378, 5908, 5872, 5872, 6581, {39282, 0, 0, 0}, 209, 209, { 1252, 20297, 10000, 850, 0, ';' }}, + {0x0485, 0x0085, 257, 109, 11410, 11417, 11432, 5940, 5912, 5912, 7225, {0, 0, 0, 0}, 210, 210, { 1251, 20880, 10007, 866, 0, ';' }}, + {0x0487, 0x0087, 257, 110, 11471, 11477, 11477, 5959, 5963, 5944, 11498, {0, 0, 0, 0}, 211, 211, { 1252, 37, 10000, 437, 0, ';' }}, + {0x0491, 0x0091, 257, 43, 11501, 11507, 11540, 5996, 6000, 5967, 10020, {39307, 0, 0, 0}, 212, 212, { 1252, 20285, 10000, 850, 0, ';' }}, + {0x0801, 0x0001, 257, 57, 11576, 11582, 11596, 11626, 2561, 2532, 11630, {39339, 0, 0, 0}, 213, 213, { 1256, 20420, 10004, 720, 1, ';' }}, + {0x0803, 0x0403, 257, 38, 11633, 6170, 6186, 11648, 2628, 2605, 6204, {36739, 0, 0, 0}, 214, 214, { 1252, 500, 10000, 850, 0, ';' }}, + {0x0804, 0x0004, 257, 25, 11652, 2640, 11658, 2668, 2672, 2676, 9961, {36759, 0, 0, 0}, 215, 215, { 936, 500, 10008, 936, 0, ',' }}, + {0x0807, 0x0007, 257, 21, 11674, 11680, 11701, 11719, 2787, 2765, 300, {36813, 0, 0, 0}, 216, 216, { 1252, 20273, 10000, 850, 0, ';' }}, + {0x0809, 0x0009, 257, 43, 11723, 11729, 11729, 11754, 2840, 2825, 10020, {36880, 0, 0, 0}, 217, 217, { 1252, 20285, 10000, 850, 0, ',' }}, + {0x080A, 0x000A, 257, 88, 11758, 11764, 11781, 11800, 2868, 2844, 11804, {37041, 0, 0, 0}, 218, 218, { 1252, 20284, 10000, 850, 0, ',' }}, + {0x080C, 0x000C, 257, 10, 11807, 11813, 11830, 11851, 2921, 2897, 11855, {36946, 0, 0, 0}, 219, 219, { 1252, 20297, 10000, 850, 0, ';' }}, + {0x0810, 0x0010, 257, 21, 11858, 11864, 11886, 11906, 3037, 3013, 300, {37041, 0, 0, 0}, 220, 220, { 1252, 500, 10000, 850, 0, ';' }}, + {0x0813, 0x0013, 257, 10, 11910, 11916, 11932, 11953, 3123, 3099, 11855, {37097, 0, 0, 0}, 221, 221, { 1252, 500, 10000, 850, 0, ';' }}, + {0x0814, 0x7814, 257, 93, 11957, 11963, 11990, 12006, 12010, 12014, 6984, {36792, 0, 0, 0}, 222, 222, { 1252, 20277, 10000, 850, 0, ';' }}, + {0x0816, 0x0016, 257, 103, 12017, 12023, 12045, 430, 3211, 3182, 12067, {39371, 0, 0, 0}, 223, 223, { 1252, 500, 10000, 850, 0, ';' }}, + {0x0818, 0x0018, 257, 80, 12070, 12076, 12095, 12124, 3269, 3244, 12128, {37186, 0, 0, 0}, 224, 224, { 1250, 500, 2, 852, 0, ';' }}, + {0x0819, 0x0019, 257, 80, 12131, 12137, 12155, 12187, 3303, 3273, 12128, {37205, 0, 0, 0}, 225, 225, { 1251, 500, 2, 866, 0, ';' }}, + {0x081D, 0x001D, 257, 40, 12191, 12197, 12215, 12233, 3415, 3392, 6537, {36792, 0, 0, 0}, 226, 226, { 1252, 20278, 10000, 850, 0, ';' }}, + {0x0820, 0x0020, 257, 56, 12237, 12243, 12256, 12278, 3494, 3473, 8858, {37362, 0, 0, 0}, 227, 227, { 1256, 500, 2, 720, 1, ';' }}, + {0x082C, 0x742C, 257, 7, 12282, 12293, 8235, 12328, 3900, 3869, 8261, {37652, 0, 0, 0}, 228, 228, { 1251, 20880, 10007, 866, 0, ';' }}, + {0x082E, 0x7C2E, 257, 30, 12332, 12339, 12363, 12389, 12393, 12393, 6407, {39394, 0, 0, 0}, 229, 229, { 1252, 870, 10000, 850, 0, ';' }}, + {0x0832, 0x0032, 257, 16, 12397, 12403, 4084, 12421, 4097, 4074, 12425, {0, 0, 0, 0}, 230, 230, { 1252, 500, 10000, 850, 0, ';' }}, + {0x083B, 0x003B, 257, 112, 12428, 12434, 12457, 12485, 4342, 4304, 7414, {37964, 0, 0, 0}, 231, 231, { 1252, 20278, 10000, 850, 0, ';' }}, + {0x083C, 0x003C, 257, 54, 12489, 12495, 12511, 4363, 4367, 4346, 12527, {37983, 0, 0, 0}, 232, 232, { 1252, 500, 10000, 850, 0, ';' }}, + {0x083E, 0x003E, 257, 13, 12530, 12536, 12551, 12574, 4398, 4371, 12578, {38004, 0, 0, 0}, 233, 233, { 1252, 500, 10000, 850, 0, ';' }}, + {0x0843, 0x7843, 257, 130, 12581, 12592, 9284, 12621, 4557, 4535, 9308, {39417, 0, 0, 0}, 234, 234, { 1251, 20880, 10007, 866, 0, ';' }}, + {0x0845, 0x0045, 257, 9, 12625, 12631, 12651, 12694, 4591, 4561, 12698, {38164, 0, 0, 0}, 235, 235, { 0, 500, 2, 1, 0, ',' }}, + {0x0846, 0x7C46, 257, 100, 12701, 12712, 12739, 12785, 4629, 4595, 7558, {38235, 0, 0, 0}, 236, 236, { 1256, 20420, 10004, 720, 1, ';' }}, + {0x0849, 0x0049, 257, 73, 12789, 12795, 12813, 12850, 4736, 4707, 10379, {38338, 0, 0, 0}, 237, 237, { 0, 500, 2, 1, 0, ';' }}, + {0x0861, 0x0061, 257, 56, 12854, 12860, 12875, 12909, 5366, 5333, 8858, {39065, 0, 0, 0}, 238, 238, { 0, 500, 2, 1, 0, ';' }}, + {0x0873, 0x0073, 257, 37, 12913, 12919, 12938, 5707, 5711, 5682, 12966, {0, 0, 0, 0}, 239, 239, { 0, 500, 2, 1, 0, ';' }}, + {0x0C01, 0x0001, 257, 36, 12969, 12975, 12990, 13014, 2561, 2532, 13018, {39339, 0, 0, 0}, 240, 240, { 1256, 20420, 10004, 720, 1, ';' }}, + {0x0C04, 0x7C04, 257, 48, 13021, 13027, 13070, 13107, 2672, 2676, 13111, {39332, 0, 0, 0}, 241, 241, { 950, 500, 10002, 950, 0, ',' }}, + {0x0C07, 0x0007, 257, 5, 13114, 13120, 13137, 13159, 2787, 2765, 13163, {36813, 0, 0, 0}, 242, 242, { 1252, 20273, 10000, 850, 0, ';' }}, + {0x0C09, 0x0009, 257, 6, 13166, 13172, 13172, 13192, 2840, 2825, 13196, {36880, 0, 0, 0}, 243, 243, { 1252, 500, 10000, 850, 0, ',' }}, + {0x0C0A, 0x000A, 257, 38, 13199, 13205, 13221, 13240, 2868, 2844, 6204, {36899, 0, 0, 0}, 244, 244, { 1252, 20284, 10000, 850, 0, ';' }}, + {0x0C0C, 0x000C, 257, 19, 13244, 13250, 13266, 13285, 2921, 2897, 13289, {36946, 0, 0, 0}, 245, 245, { 1252, 20297, 10000, 850, 0, ';' }}, + {0x0C3B, 0x003B, 257, 40, 13292, 13298, 13322, 13348, 4342, 4304, 6537, {39455, 0, 0, 0}, 246, 246, { 1252, 20278, 10000, 850, 0, ';' }}, + {0x1001, 0x0001, 257, 77, 13352, 13358, 13373, 13401, 2561, 2532, 13405, {39339, 0, 0, 0}, 247, 247, { 1256, 20420, 10004, 720, 1, ';' }}, + {0x1004, 0x0004, 257, 113, 13408, 13414, 13446, 13465, 2672, 2676, 13469, {36759, 0, 0, 0}, 248, 248, { 936, 500, 10008, 936, 0, ',' }}, + {0x1007, 0x0007, 257, 75, 13472, 13478, 13498, 13518, 2787, 2765, 10898, {36813, 0, 0, 0}, 249, 249, { 1252, 20273, 10000, 850, 0, ';' }}, + {0x1009, 0x0009, 257, 19, 13522, 13528, 13528, 13545, 2840, 2825, 13289, {36880, 0, 0, 0}, 250, 250, { 1252, 37, 10000, 850, 0, ',' }}, + {0x100A, 0x000A, 257, 47, 13549, 13555, 13575, 13596, 2868, 2844, 13600, {36899, 0, 0, 0}, 251, 251, { 1252, 20284, 10000, 850, 0, ';' }}, + {0x100C, 0x000C, 257, 21, 13603, 13609, 13630, 13649, 2921, 2897, 300, {36946, 0, 0, 0}, 252, 252, { 1252, 20297, 10000, 850, 0, ';' }}, + {0x101A, 0x001A, 257, 8, 13653, 13659, 13691, 13722, 3332, 3307, 13726, {37251, 0, 0, 0}, 253, 253, { 1250, 870, 10082, 852, 0, ';' }}, + {0x1401, 0x0001, 257, 33, 13729, 13735, 13752, 13784, 2561, 2532, 13788, {39339, 0, 0, 0}, 254, 254, { 1256, 20420, 10004, 720, 1, ';' }}, + {0x1404, 0x7C04, 257, 86, 13791, 13797, 13836, 13873, 2672, 2676, 13877, {39332, 0, 0, 0}, 255, 255, { 950, 500, 10002, 950, 0, ',' }}, + {0x1407, 0x0007, 257, 72, 13880, 13886, 13909, 13933, 2787, 2765, 13937, {36813, 0, 0, 0}, 256, 256, { 1252, 20273, 10000, 850, 0, ';' }}, + {0x1409, 0x0009, 257, 95, 13940, 13946, 13946, 13968, 2840, 2825, 13972, {36880, 0, 0, 0}, 257, 257, { 1252, 500, 10000, 850, 0, ',' }}, + {0x140A, 0x000A, 257, 27, 13975, 13981, 14002, 14024, 2868, 2844, 14028, {36899, 0, 0, 0}, 258, 258, { 1252, 20284, 10000, 850, 0, ';' }}, + {0x140C, 0x000C, 257, 75, 14031, 14037, 14057, 14080, 2921, 2897, 10898, {36946, 0, 0, 0}, 259, 259, { 1252, 20297, 10000, 850, 0, ';' }}, + {0x141A, 0x681A, 257, 8, 14084, 14095, 14133, 14164, 14168, 14172, 13726, {37251, 0, 0, 0}, 260, 260, { 1250, 870, 10082, 852, 0, ';' }}, + {0x1801, 0x0001, 257, 78, 14175, 14181, 14198, 14228, 2561, 2532, 14232, {39339, 0, 0, 0}, 261, 261, { 1256, 20420, 10004, 720, 1, ';' }}, + {0x1809, 0x0009, 257, 54, 14235, 14241, 14241, 14259, 2840, 2825, 12527, {36880, 0, 0, 0}, 262, 262, { 1252, 500, 10000, 850, 0, ',' }}, + {0x180A, 0x000A, 257, 97, 14263, 14269, 14286, 14305, 2868, 2844, 14309, {36899, 0, 0, 0}, 263, 263, { 1252, 20284, 10000, 850, 0, ';' }}, + {0x180C, 0x000C, 257, 79, 14312, 14318, 14334, 14353, 2921, 2897, 14357, {36946, 0, 0, 0}, 264, 264, { 1252, 20297, 10000, 850, 0, ';' }}, + {0x181A, 0x701A, 257, 8, 14360, 14371, 14409, 14461, 14465, 14469, 13726, {37251, 0, 0, 0}, 265, 265, { 1250, 870, 10082, 852, 0, ';' }}, + {0x1C01, 0x0001, 257, 123, 14472, 14478, 14495, 14521, 2561, 2532, 14525, {39339, 0, 0, 0}, 266, 266, { 1256, 20420, 10004, 720, 1, ';' }}, + {0x1C09, 0x0009, 257, 134, 14528, 14534, 14534, 14557, 2840, 2825, 8475, {36880, 0, 0, 0}, 267, 267, { 1252, 500, 10000, 437, 0, ',' }}, + {0x1C0A, 0x000A, 257, 32, 14561, 14567, 14596, 14629, 2868, 2844, 14633, {36899, 0, 0, 0}, 268, 268, { 1252, 20284, 10000, 850, 0, ';' }}, + {0x1C1A, 0x6C1A, 257, 8, 14636, 14647, 14409, 14688, 14465, 14469, 13726, {39477, 0, 0, 0}, 269, 269, { 1251, 21025, 10007, 855, 0, ';' }}, + {0x2001, 0x0001, 257, 96, 14692, 14698, 14712, 14740, 2561, 2532, 14744, {39339, 0, 0, 0}, 270, 270, { 1256, 20420, 10004, 720, 1, ';' }}, + {0x2009, 0x0009, 257, 61, 14747, 14753, 14753, 14771, 2840, 2825, 14775, {36880, 0, 0, 0}, 271, 271, { 1252, 500, 10000, 850, 0, ',' }}, + {0x200A, 0x000A, 257, 131, 14778, 14784, 14804, 14825, 2868, 2844, 14829, {36899, 0, 0, 0}, 272, 272, { 1252, 20284, 10000, 850, 0, ';' }}, + {0x200C, 0x000C, 257, 106, 14832, 14838, 14856, 14880, 2921, 2897, 14884, {36946, 0, 0, 0}, 273, 273, { 1252, 20297, 10000, 850, 0, ';' }}, + {0x201A, 0x641A, 257, 8, 14887, 14898, 14133, 14939, 14168, 14172, 13726, {37712, 0, 0, 0}, 274, 274, { 1251, 870, 10082, 855, 0, ';' }}, + {0x2401, 0x0001, 257, 133, 14943, 14949, 14964, 14992, 2561, 2532, 14996, {39339, 0, 0, 0}, 275, 275, { 1256, 20420, 10004, 720, 1, ';' }}, + {0x240A, 0x000A, 257, 26, 14999, 15005, 15024, 15044, 2868, 2844, 15048, {36899, 0, 0, 0}, 276, 276, { 1252, 20284, 10000, 850, 0, ';' }}, + {0x240C, 0x000C, 257, 20, 15051, 15057, 15083, 15110, 2921, 2897, 15114, {36946, 0, 0, 0}, 277, 277, { 1252, 20297, 10000, 850, 0, ';' }}, + {0x241A, 0x701A, 257, 108, 15117, 15128, 15152, 15180, 14465, 14469, 15184, {37251, 0, 0, 0}, 278, 278, { 1250, 500, 10029, 852, 0, ';' }}, + {0x243B, 0x703B, 257, 40, 15187, 15194, 15215, 15239, 15243, 15243, 6537, {0, 0, 0, 0}, 279, 279, { 1252, 20278, 10000, 850, 0, ';' }}, + {0x2801, 0x0001, 257, 119, 15247, 15253, 15268, 15296, 2561, 2532, 15300, {39339, 0, 0, 0}, 280, 280, { 1256, 20420, 10004, 720, 1, ';' }}, + {0x2809, 0x0009, 257, 18, 15303, 15309, 15309, 15326, 2840, 2825, 15330, {36880, 0, 0, 0}, 281, 281, { 1252, 500, 10000, 850, 0, ',' }}, + {0x280A, 0x000A, 257, 98, 15333, 15339, 15354, 15371, 2868, 2844, 15375, {36899, 0, 0, 0}, 282, 282, { 1252, 20284, 10000, 850, 0, ';' }}, + {0x280C, 0x000C, 257, 116, 15378, 15384, 15401, 15423, 2921, 2897, 15427, {36946, 0, 0, 0}, 283, 283, { 1252, 20297, 10000, 850, 0, ';' }}, + {0x281A, 0x6C1A, 257, 108, 15430, 15441, 15152, 15468, 14465, 14469, 15184, {39477, 0, 0, 0}, 284, 284, { 1251, 21025, 10007, 855, 0, ';' }}, + {0x2C01, 0x0001, 257, 62, 15472, 15478, 15494, 15524, 2561, 2532, 15528, {39339, 0, 0, 0}, 285, 285, { 1256, 20420, 10004, 720, 1, ';' }}, + {0x2C09, 0x0009, 257, 125, 15531, 15537, 15537, 15565, 2840, 2825, 15569, {36880, 0, 0, 0}, 286, 286, { 1252, 500, 10000, 850, 0, ',' }}, + {0x2C0A, 0x000A, 257, 4, 15572, 15578, 15598, 15619, 2868, 2844, 15623, {36899, 0, 0, 0}, 287, 287, { 1252, 20284, 10000, 850, 0, ';' }}, + {0x2C0C, 0x000C, 257, 24, 15626, 15632, 15650, 15671, 2921, 2897, 15675, {36946, 0, 0, 0}, 288, 288, { 1252, 20297, 10000, 850, 0, ';' }}, + {0x2C1A, 0x701A, 257, 81, 15678, 15689, 15717, 15750, 14465, 14469, 15754, {37251, 0, 0, 0}, 289, 289, { 1250, 500, 10029, 852, 0, ';' }}, + {0x3001, 0x0001, 257, 71, 15757, 15763, 15780, 15808, 2561, 2532, 15812, {39339, 0, 0, 0}, 290, 290, { 1256, 20420, 10004, 720, 1, ';' }}, + {0x3009, 0x0009, 257, 135, 15815, 15821, 15821, 15840, 2840, 2825, 15844, {36880, 0, 0, 0}, 291, 291, { 1252, 500, 10000, 437, 0, ',' }}, + {0x300A, 0x000A, 257, 34, 15847, 15853, 15871, 15890, 2868, 2844, 15894, {36899, 0, 0, 0}, 292, 292, { 1252, 20284, 10000, 850, 0, ';' }}, + {0x300C, 0x000C, 257, 22, 15897, 15903, 15929, 15958, 2921, 2897, 15962, {36946, 0, 0, 0}, 293, 293, { 1252, 20297, 10000, 850, 0, ';' }}, + {0x301A, 0x6C1A, 257, 81, 15965, 15976, 15717, 16007, 14465, 14469, 15754, {39477, 0, 0, 0}, 294, 294, { 1251, 21025, 10007, 855, 0, ';' }}, + {0x3401, 0x0001, 257, 68, 16011, 16017, 16033, 16063, 2561, 2532, 16067, {39339, 0, 0, 0}, 295, 295, { 1256, 20420, 10004, 720, 1, ';' }}, + {0x3409, 0x0009, 257, 99, 16070, 16076, 16076, 16098, 2840, 2825, 10672, {36880, 0, 0, 0}, 296, 296, { 1252, 500, 10000, 437, 0, ',' }}, + {0x340A, 0x000A, 257, 23, 16102, 16108, 16124, 16141, 2868, 2844, 16145, {36899, 0, 0, 0}, 297, 297, { 1252, 20284, 10000, 850, 0, ';' }}, + {0x340C, 0x000C, 257, 83, 16148, 16154, 16168, 16185, 2921, 2897, 16189, {36946, 0, 0, 0}, 298, 298, { 1252, 20297, 10000, 850, 0, ';' }}, + {0x3801, 0x0001, 257, 0, 16192, 16198, 16228, 16292, 2561, 2532, 16296, {39339, 0, 0, 0}, 299, 299, { 1256, 20420, 10004, 720, 1, ';' }}, + {0x380A, 0x000A, 257, 129, 16299, 16305, 16323, 16342, 2868, 2844, 16346, {36899, 0, 0, 0}, 300, 300, { 1252, 20284, 10000, 850, 0, ';' }}, + {0x380C, 0x000C, 257, 78, 16349, 16355, 16372, 16390, 2921, 2897, 14232, {36946, 0, 0, 0}, 301, 301, { 1252, 20297, 10000, 850, 0, ';' }}, + {0x3C01, 0x0001, 257, 12, 16394, 16400, 16417, 16449, 2561, 2532, 16453, {39339, 0, 0, 0}, 302, 302, { 1256, 20420, 10004, 720, 1, ';' }}, + {0x3C09, 0x0009, 257, 48, 16456, 16462, 16462, 16492, 2840, 2825, 13111, {36880, 0, 0, 0}, 303, 303, { 1252, 500, 10000, 850, 0, ',' }}, + {0x3C0A, 0x000A, 257, 104, 16496, 16502, 16521, 16541, 2868, 2844, 16545, {36899, 0, 0, 0}, 304, 304, { 1252, 20284, 10000, 850, 0, ';' }}, + {0x3C0C, 0x000C, 257, 51, 16548, 16554, 16569, 16588, 2921, 2897, 16592, {36946, 0, 0, 0}, 305, 305, { 1252, 20297, 10000, 850, 0, ';' }}, + {0x4001, 0x0001, 257, 105, 16595, 16601, 16616, 16640, 2561, 2532, 16644, {39339, 0, 0, 0}, 306, 306, { 1256, 20420, 10004, 720, 1, ';' }}, + {0x4009, 0x0009, 257, 56, 16647, 16653, 16653, 16669, 2840, 2825, 8858, {36880, 0, 0, 0}, 307, 307, { 1252, 37, 10000, 437, 0, ',' }}, + {0x400A, 0x000A, 257, 14, 16673, 16679, 16697, 16716, 2868, 2844, 16720, {36899, 0, 0, 0}, 308, 308, { 1252, 20284, 10000, 850, 0, ';' }}, + {0x4409, 0x0009, 257, 89, 16723, 16729, 16729, 16748, 2840, 2825, 9002, {36880, 0, 0, 0}, 309, 309, { 1252, 37, 10000, 437, 0, ',' }}, + {0x440A, 0x000A, 257, 118, 16752, 16758, 16780, 16803, 2868, 2844, 16807, {36899, 0, 0, 0}, 310, 310, { 1252, 20284, 10000, 850, 0, ';' }}, + {0x4809, 0x0009, 257, 113, 16810, 16816, 16816, 16836, 2840, 2825, 13469, {36880, 0, 0, 0}, 311, 311, { 1252, 37, 10000, 437, 0, ',' }}, + {0x480A, 0x000A, 257, 49, 16840, 16846, 16865, 16885, 2868, 2844, 16889, {36899, 0, 0, 0}, 312, 312, { 1252, 20284, 10000, 850, 0, ';' }}, + {0x4C0A, 0x000A, 257, 91, 16892, 16898, 16918, 16939, 2868, 2844, 16943, {36899, 0, 0, 0}, 313, 313, { 1252, 20284, 10000, 850, 0, ';' }}, + {0x500A, 0x000A, 257, 102, 16946, 16952, 16974, 16997, 2868, 2844, 17001, {36899, 0, 0, 0}, 314, 314, { 1252, 20284, 10000, 850, 0, ';' }}, + {0x540A, 0x000A, 257, 128, 17004, 17010, 17034, 17060, 2868, 2844, 6496, {36899, 0, 0, 0}, 315, 315, { 1252, 20284, 10000, 850, 0, ',' }}, + {0x5C0A, 0x000A, 257, 28, 17064, 17070, 17085, 17101, 2868, 2844, 17105, {36899, 0, 0, 0}, 316, 316, { 1252, 20284, 10000, 850, 0, ';' }}, + {0x641A, 0x781A, 257, -1, 17108, 17116, 17135, 14939, 14168, 14172, 0, {37712, 0, 0, 0}, 317, 317, { 1251, 870, 10082, 855, 0, ';' }}, + {0x681A, 0x781A, 257, -1, 17144, 17152, 17135, 14164, 14168, 14172, 0, {37251, 0, 0, 0}, 318, 318, { 1250, 870, 10082, 852, 0, ';' }}, + {0x6C1A, 0x7C1A, 257, -1, 17168, 17176, 17195, 15468, 14465, 14469, 0, {39477, 0, 0, 0}, 319, 319, { 1251, 21025, 10007, 855, 0, ';' }}, + {0x701A, 0x7C1A, 257, -1, 17208, 17216, 17195, 15180, 14465, 14469, 0, {37251, 0, 0, 0}, 320, 320, { 1250, 500, 10029, 852, 0, ';' }}, + {0x703B, 0x003B, 257, -1, 15243, 17232, 17243, 15239, 15243, 15243, 0, {0, 0, 0, 0}, 321, 321, { 1252, 20278, 10000, 850, 0, ';' }}, + {0x742C, 0x002C, 257, -1, 17258, 17266, 3884, 12328, 3900, 3869, 0, {37652, 0, 0, 0}, 322, 322, { 1251, 20880, 10007, 866, 0, ';' }}, + {0x7804, 0x007F, 257, -1, 2676, 2640, 2661, 2668, 2672, 2676, 0, {36759, 0, 0, 0}, 323, 323, { 936, 500, 10008, 936, 0, ',' }}, + {0x7814, 0x0014, 257, -1, 12014, 17289, 17307, 12006, 12010, 12014, 0, {36792, 0, 0, 0}, 324, 324, { 1252, 20277, 10000, 850, 0, ';' }}, + {0x781A, 0x007F, 257, -1, 14172, 17315, 17135, 14164, 14168, 14172, 0, {37251, 0, 0, 0}, 325, 325, { 1250, 870, 10082, 852, 0, ';' }}, + {0x782C, 0x002C, 257, -1, 17323, 17331, 3884, 3896, 3900, 3869, 0, {37652, 0, 0, 0}, 326, 326, { 1254, 20905, 10081, 857, 0, ';' }}, + {0x7843, 0x0043, 257, -1, 17351, 17359, 4544, 12621, 4557, 4535, 0, {39417, 0, 0, 0}, 327, 327, { 1251, 20880, 10007, 866, 0, ';' }}, + {0x7850, 0x0050, 257, -1, 17376, 17384, 4939, 9886, 4956, 4926, 0, {38692, 0, 0, 0}, 328, 328, { 1251, 20880, 10007, 866, 0, ';' }}, + {0x7C04, 0x7804, 257, -1, 17405, 6213, 2661, 6251, 2672, 2676, 0, {39332, 0, 0, 0}, 329, 329, { 950, 500, 10002, 950, 0, ',' }}, + {0x7C04, 0x7C04, 257, -1, 17413, 17420, 2661, 6251, 2672, 2676, 0, {39332, 0, 0, 0}, 330, 330, { 950, 500, 10002, 950, 0, ',' }}, + {0x7C14, 0x0014, 257, -1, 3154, 17449, 17467, 3146, 3150, 3154, 0, {36792, 0, 0, 0}, 331, 331, { 1252, 20277, 10000, 850, 0, ';' }}, + {0x7C1A, 0x007F, 257, -1, 14469, 17481, 17195, 17489, 14465, 14469, 0, {39477, 0, 0, 0}, 332, 332, { 1250, 500, 10029, 852, 0, ';' }}, + {0x7C28, 0x0028, 257, -1, 17493, 17501, 3746, 3759, 3763, 3737, 0, {0, 0, 0, 0}, 333, 333, { 1251, 20880, 10007, 866, 0, ';' }}, + {0x7C2E, 0x002E, 257, -1, 12393, 17518, 17532, 12389, 12393, 12393, 0, {39394, 0, 0, 0}, 334, 334, { 1252, 870, 10000, 850, 0, ';' }}, + {0x7C43, 0x0043, 257, -1, 17549, 17557, 4544, 4553, 4557, 4535, 0, {38146, 0, 0, 0}, 335, 335, { 1254, 500, 10029, 857, 0, ';' }}, + {0x7C46, 0x0046, 257, -1, 17571, 17579, 4606, 12785, 4629, 4595, 0, {38235, 0, 0, 0}, 336, 336, { 1256, 20420, 10004, 720, 1, ';' }}, + {0x7C5F, 0x005F, 257, -1, 17596, 17605, 5306, 5329, 5278, 5278, 0, {0, 0, 0, 0}, 337, 337, { 1252, 20297, 10000, 850, 0, ';' }}, + {0x7C68, 0x0068, 257, -1, 17637, 17645, 5479, 5485, 5489, 5476, 0, {0, 0, 0, 0}, 338, 338, { 1252, 37, 10000, 437, 0, ';' }} +}; + + +static const CultureInfoNameEntry culture_name_entries [] = { + {4151, 53}, /* af */ + {17659, 161}, /* af-za */ + {5, 86}, /* am */ + {17665, 191}, /* am-et */ + {2532, 0}, /* ar */ + {17671, 299}, /* ar-ae */ + {17677, 302}, /* ar-bh */ + {17683, 254}, /* ar-dz */ + {17689, 240}, /* ar-eg */ + {17695, 213}, /* ar-iq */ + {17701, 285}, /* ar-jo */ + {17707, 295}, /* ar-kw */ + {17713, 290}, /* ar-lb */ + {17719, 247}, /* ar-ly */ + {17725, 261}, /* ar-ma */ + {17731, 270}, /* ar-om */ + {17737, 306}, /* ar-qa */ + {17743, 110}, /* ar-sa */ + {17749, 280}, /* ar-sy */ + {17755, 266}, /* ar-tn */ + {17761, 275}, /* ar-ye */ + {4849, 74}, /* as */ + {17767, 180}, /* as-in */ + {3869, 44}, /* az */ + {17773, 322}, /* az-cyrl */ + {17781, 228}, /* az-cyrl-az */ + {17792, 326}, /* az-latn */ + {17800, 152}, /* az-latn-az */ + {3572, 35}, /* be */ + {17811, 143}, /* be-by */ + {2565, 1}, /* bg */ + {17817, 111}, /* bg-bg */ + {4561, 66}, /* bn */ + {17823, 235}, /* bn-bd */ + {17829, 173}, /* bn-in */ + {4960, 77}, /* bo */ + {17835, 183}, /* bo-cn */ + {5809, 104}, /* br */ + {17841, 207}, /* br-fr */ + {14172, 325}, /* bs */ + {17847, 317}, /* bs-cyrl */ + {17855, 274}, /* bs-cyrl-ba */ + {17866, 318}, /* bs-latn */ + {17874, 260}, /* bs-latn-ba */ + {2605, 2}, /* ca */ + {17885, 112}, /* ca-es */ + {17891, 214}, /* ca-es-valencia */ + {5222, 85}, /* chr */ + {2714, 5}, /* cs */ + {17906, 114}, /* cs-cz */ + {5004, 78}, /* cy */ + {17912, 184}, /* cy-gb */ + {2741, 6}, /* da */ + {17918, 115}, /* da-dk */ + {2765, 7}, /* de */ + {17924, 242}, /* de-at */ + {17930, 216}, /* de-ch */ + {17936, 116}, /* de-de */ + {17942, 256}, /* de-li */ + {17948, 249}, /* de-lu */ + {12393, 334}, /* dsb */ + {17954, 229}, /* dsb-de */ + {2791, 8}, /* el */ + {17961, 117}, /* el-gr */ + {2825, 9}, /* en */ + {17967, 243}, /* en-au */ + {17973, 281}, /* en-bz */ + {17979, 250}, /* en-ca */ + {17985, 217}, /* en-gb */ + {17991, 303}, /* en-hk */ + {17997, 262}, /* en-ie */ + {18003, 307}, /* en-in */ + {18009, 271}, /* en-jm */ + {18015, 309}, /* en-my */ + {18021, 257}, /* en-nz */ + {18027, 296}, /* en-ph */ + {18033, 311}, /* en-sg */ + {18039, 286}, /* en-tt */ + {18045, 118}, /* en-us */ + {18051, 267}, /* en-za */ + {18057, 291}, /* en-zw */ + {2844, 10}, /* es */ + {18063, 287}, /* es-ar */ + {18069, 308}, /* es-bo */ + {18075, 297}, /* es-cl */ + {18081, 276}, /* es-co */ + {18087, 258}, /* es-cr */ + {18093, 316}, /* es-cu */ + {18099, 268}, /* es-do */ + {18105, 292}, /* es-ec */ + {18111, 244}, /* es-es */ + {18117, 251}, /* es-gt */ + {18123, 312}, /* es-hn */ + {18129, 218}, /* es-mx */ + {18135, 313}, /* es-ni */ + {18141, 263}, /* es-pa */ + {18147, 282}, /* es-pe */ + {18153, 314}, /* es-pr */ + {18159, 304}, /* es-py */ + {18165, 310}, /* es-sv */ + {18171, 315}, /* es-us */ + {18177, 300}, /* es-uy */ + {18183, 272}, /* es-ve */ + {3650, 37}, /* et */ + {18189, 145}, /* et-ee */ + {3904, 45}, /* eu */ + {18195, 153}, /* eu-es */ + {3767, 41}, /* fa */ + {18201, 149}, /* fa-ir */ + {5452, 92}, /* ff */ + {2872, 11}, /* fi */ + {18207, 119}, /* fi-fi */ + {5435, 91}, /* fil */ + {18213, 195}, /* fil-ph */ + {4214, 55}, /* fo */ + {18220, 163}, /* fo-fo */ + {2897, 12}, /* fr */ + {18226, 219}, /* fr-be */ + {18232, 245}, /* fr-ca */ + {18238, 277}, /* fr-cd */ + {18244, 252}, /* fr-ch */ + {18250, 293}, /* fr-ci */ + {18256, 288}, /* fr-cm */ + {18262, 120}, /* fr-fr */ + {18268, 305}, /* fr-ht */ + {18274, 259}, /* fr-lu */ + {18280, 301}, /* fr-ma */ + {18286, 264}, /* fr-mc */ + {18292, 298}, /* fr-ml */ + {18298, 273}, /* fr-re */ + {18304, 283}, /* fr-sn */ + {5370, 89}, /* fy */ + {18310, 193}, /* fy-nl */ + {4346, 59}, /* ga */ + {18316, 232}, /* ga-ie */ + {5967, 109}, /* gd */ + {18322, 212}, /* gd-gb */ + {5125, 82}, /* gl */ + {18328, 188}, /* gl-es */ + {5872, 106}, /* gsw */ + {18334, 209}, /* gsw-fr */ + {4633, 68}, /* gu */ + {18341, 174}, /* gu-in */ + {5476, 93}, /* ha */ + {18347, 338}, /* ha-latn */ + {18355, 196}, /* ha-latn-ng */ + {5715, 101}, /* haw */ + {18366, 204}, /* haw-us */ + {2925, 13}, /* he */ + {18373, 121}, /* he-il */ + {4243, 56}, /* hi */ + {18379, 164}, /* hi-in */ + {3307, 26}, /* hr */ + {18385, 253}, /* hr-ba */ + {18391, 134}, /* hr-hr */ + {3930, 46}, /* hsb */ + {18397, 154}, /* hsb-de */ + {2954, 14}, /* hu */ + {18404, 122}, /* hu-hu */ + {3834, 43}, /* hy */ + {18410, 151}, /* hy-am */ + {3498, 33}, /* id */ + {18416, 141}, /* id-id */ + {5642, 98}, /* ig */ + {18422, 201}, /* ig-ng */ + {5777, 103}, /* ii */ + {18428, 206}, /* ii-cn */ + {2982, 15}, /* is */ + {18434, 123}, /* is-is */ + {3013, 16}, /* it */ + {18440, 220}, /* it-ch */ + {18446, 124}, /* it-it */ + {3041, 17}, /* ja */ + {18452, 125}, /* ja-jp */ + {4172, 54}, /* ka */ + {18458, 162}, /* ka-ge */ + {4402, 61}, /* kk */ + {18464, 168}, /* kk-kz */ + {5607, 97}, /* kl */ + {18470, 200}, /* kl-gl */ + {5029, 79}, /* km */ + {18476, 185}, /* km-kh */ + {1701, 72}, /* kn */ + {18482, 178}, /* kn-in */ + {3071, 18}, /* ko */ + {18488, 126}, /* ko-kr */ + {5152, 83}, /* kok */ + {18494, 189}, /* kok-in */ + {4440, 62}, /* ky */ + {18501, 169}, /* ky-kg */ + {5566, 96}, /* lb */ + {18507, 199}, /* lb-lu */ + {5062, 80}, /* lo */ + {18513, 186}, /* lo-la */ + {3705, 39}, /* lt */ + {18519, 147}, /* lt-lt */ + {3676, 38}, /* lv */ + {18525, 146}, /* lv-lv */ + {3970, 47}, /* mk */ + {18531, 155}, /* mk-mk */ + {4809, 73}, /* ml */ + {18537, 179}, /* ml-in */ + {4926, 76}, /* mn */ + {18543, 328}, /* mn-cyrl */ + {18551, 182}, /* mn-mn */ + {4891, 75}, /* mr */ + {18557, 181}, /* mr-in */ + {4371, 60}, /* ms */ + {18563, 233}, /* ms-bn */ + {18569, 167}, /* ms-my */ + {4279, 57}, /* mt */ + {18575, 165}, /* mt-mt */ + {5087, 81}, /* my */ + {18581, 187}, /* my-mm */ + {3154, 331}, /* nb */ + {18587, 128}, /* nb-no */ + {5333, 88}, /* ne */ + {18593, 238}, /* ne-in */ + {18599, 192}, /* ne-np */ + {3099, 19}, /* nl */ + {18605, 221}, /* nl-be */ + {18611, 127}, /* nl-nl */ + {12014, 324}, /* nn */ + {18617, 222}, /* nn-no */ + {3127, 20}, /* no */ + {5526, 95}, /* nso */ + {18623, 198}, /* nso-za */ + {5658, 99}, /* om */ + {18630, 202}, /* om-et */ + {4675, 69}, /* or */ + {18636, 175}, /* or-in */ + {4595, 67}, /* pa */ + {18642, 336}, /* pa-arab */ + {18650, 236}, /* pa-arab-pk */ + {3157, 21}, /* pl */ + {18661, 129}, /* pl-pl */ + {5408, 90}, /* ps */ + {18667, 194}, /* ps-af */ + {3182, 22}, /* pt */ + {18673, 130}, /* pt-br */ + {18679, 223}, /* pt-pt */ + {3215, 23}, /* rm */ + {18685, 131}, /* rm-ch */ + {3244, 24}, /* ro */ + {18691, 224}, /* ro-md */ + {18697, 132}, /* ro-ro */ + {3273, 25}, /* ru */ + {18703, 225}, /* ru-md */ + {18709, 133}, /* ru-ru */ + {5944, 108}, /* rw */ + {18715, 211}, /* rw-rw */ + {5912, 107}, /* sah */ + {18721, 210}, /* sah-ru */ + {4304, 58}, /* se */ + {18728, 246}, /* se-fi */ + {18734, 166}, /* se-no */ + {18740, 231}, /* se-se */ + {5187, 84}, /* si */ + {18746, 190}, /* si-lk */ + {3336, 27}, /* sk */ + {18752, 135}, /* sk-sk */ + {3615, 36}, /* sl */ + {18758, 144}, /* sl-si */ + {15243, 321}, /* smn */ + {18764, 279}, /* smn-fi */ + {5750, 102}, /* so */ + {18771, 205}, /* so-so */ + {3366, 28}, /* sq */ + {18777, 136}, /* sq-al */ + {14469, 332}, /* sr */ + {18783, 319}, /* sr-cyrl */ + {18791, 269}, /* sr-cyrl-ba */ + {18802, 294}, /* sr-cyrl-me */ + {18813, 284}, /* sr-cyrl-rs */ + {18824, 320}, /* sr-latn */ + {18832, 265}, /* sr-latn-ba */ + {18843, 289}, /* sr-latn-me */ + {18854, 278}, /* sr-latn-rs */ + {4013, 48}, /* st */ + {18865, 156}, /* st-za */ + {3392, 29}, /* sv */ + {18871, 226}, /* sv-fi */ + {18877, 137}, /* sv-se */ + {4475, 63}, /* sw */ + {18883, 170}, /* sw-ke */ + {4707, 70}, /* ta */ + {18889, 176}, /* ta-in */ + {18895, 237}, /* ta-lk */ + {4740, 71}, /* te */ + {18901, 177}, /* te-in */ + {3737, 40}, /* tg */ + {18907, 333}, /* tg-cyrl */ + {18915, 148}, /* tg-cyrl-tj */ + {3419, 30}, /* th */ + {18926, 138}, /* th-th */ + {5682, 100}, /* ti */ + {18932, 239}, /* ti-er */ + {18938, 203}, /* ti-et */ + {4504, 64}, /* tk */ + {18944, 171}, /* tk-tm */ + {4074, 50}, /* tn */ + {18950, 230}, /* tn-bw */ + {18956, 158}, /* tn-za */ + {3445, 31}, /* tr */ + {18962, 139}, /* tr-tr */ + {4047, 49}, /* ts */ + {18968, 157}, /* ts-za */ + {5278, 87}, /* tzm */ + {18974, 337}, /* tzm-latn */ + {5837, 105}, /* ug */ + {18983, 208}, /* ug-cn */ + {3530, 34}, /* uk */ + {18989, 142}, /* uk-ua */ + {3473, 32}, /* ur */ + {18995, 227}, /* ur-in */ + {19001, 140}, /* ur-pk */ + {4535, 65}, /* uz */ + {19007, 327}, /* uz-cyrl */ + {19015, 234}, /* uz-cyrl-uz */ + {19026, 335}, /* uz-latn */ + {19034, 172}, /* uz-latn-uz */ + {3797, 42}, /* vi */ + {19045, 150}, /* vi-vn */ + {4101, 51}, /* xh */ + {19051, 159}, /* xh-za */ + {5493, 94}, /* yo */ + {19057, 197}, /* yo-ng */ + {2676, 323}, /* zh */ + {19063, 4}, /* zh-chs */ + {19070, 330}, /* zh-cht */ + {19077, 215}, /* zh-cn */ + {19083, 3}, /* zh-hans */ + {19091, 329}, /* zh-hant */ + {19099, 241}, /* zh-hk */ + {19105, 255}, /* zh-mo */ + {19111, 248}, /* zh-sg */ + {19117, 113}, /* zh-tw */ + {4127, 52}, /* zu */ + {19123, 160} /* zu-za */ +}; + + +static const RegionInfoEntry region_entries [] = { + { 224,16296,13014,13014,19129,19150,2473,19197,19201,19229}, + { 3,10618,19253,19253,19257,19269,2144,19288,19292,19307}, + { 6,7370,19320,19320,19324,19332,1704,19342,19346,19359}, + { 7,49,14228,14228,19373,19381,1842,19398,19402,19416}, + { 11,15623,13784,13784,19442,19442,1448,15296,19452,19467}, + { 14,13163,19482,19482,19486,19494,1343,19506,19510,19510}, + { 12,13196,19515,19515,19519,19519,1448,19529,19533,19533}, + { 5,8261,3896,3896,19551,19562,2207,19574,19578,19596}, + { 25,13726,19616,19616,19620,19641,2327,19661,19665,19701}, + { 23,12698,19721,19721,19725,19736,2290,19761,19765,19782}, + { 21,11855,3607,3607,19823,19831,1343,19506,19510,19840}, + { 35,6161,2597,2597,19845,19854,1325,19871,19875,19889}, + { 17,16453,19915,19915,19919,19927,2487,19942,19946,19961}, + { 37,12578,19985,19985,19989,19989,1448,19996,20000,20014}, + { 26,16720,20027,20027,20031,20031,2513,4996,20039,20058}, + { 32,7074,20068,20068,20072,20079,1592,20086,20090,20105}, + { 19,12425,20121,20121,20125,0,2248,20134,20138,0}, + { 29,7749,20153,20153,20157,20165,1752,20182,20186,20203}, + { 24,15330,20235,20235,20239,20239,1448,20246,20250,20250}, + { 39,13289,20264,20264,20268,20268,1448,20275,20279,20295}, + { 44,15114,20311,20311,20315,20332,2406,20347,20351,20367}, + { 223,300,20383,20383,20387,20399,1599,1599,20406,20418}, + { 119,15962,20432,20432,20436,20436,2434,20453,20457,20480}, + { 46,16145,20498,20498,20502,20502,1448,20508,20512,20525}, + { 49,15675,20538,20538,20542,20551,2448,20560,20564,20590}, + { 45,9961,20607,20607,20611,20617,1364,20636,20640,20653}, + { 51,15048,20672,20672,20676,20676,1448,20685,20689,20704}, + { 54,14028,20720,20720,20724,20724,2345,20735,20739,20758}, + { 56,17105,20779,20779,20783,20783,1448,20788,20792,20803}, + { 75,6317,20815,20815,20819,20834,1367,20852,20856,20878}, + { 94,6407,2783,2783,20893,20901,1343,19506,19510,19510}, + { 61,6359,20913,20913,20917,20925,1395,20933,20937,20950}, + { 65,14633,20962,20962,20966,20985,2373,21007,21011,21026}, + { 4,13788,21042,21042,21046,21054,2330,21069,21073,21088}, + { 66,15894,21112,21112,21116,21116,1448,21124,21128,21138}, + { 70,7847,17060,17060,21160,21168,1343,19506,19510,19840}, + { 67,13018,21174,21174,21178,21184,2301,21191,21195,21210}, + { 71,12966,21228,21228,21232,21240,2155,21253,21257,0}, + { 217,6204,2864,2864,21272,21278,1343,19506,19510,19840}, + { 73,10438,21286,21286,21290,21299,2130,21315,21319,21334}, + { 77,6537,2889,2889,21360,21368,1343,19506,19510,19840}, + { 81,8801,16390,16390,21374,21388,1710,20933,20937,21397}, + { 84,6581,2917,2917,21410,21410,1343,19506,19510,19840}, + { 242,10020,21417,21417,21421,21436,2023,21453,21457,21471}, + { 88,8747,21484,21484,21488,21496,1880,21527,21531,21545}, + { 93,10962,21580,21580,21584,21594,1395,20933,20937,21611}, + { 98,6463,21631,21631,21635,21642,1343,19506,19510,21655}, + { 99,13600,21664,21664,21668,21668,2325,21678,21682,21701}, + { 104,13111,21709,21709,21713,21733,2311,21761,21765,21782}, + { 106,16889,21789,21789,21793,21793,2246,21802,21806,21823}, + { 108,7273,3328,3328,21842,21850,2203,2203,21859,21873}, + { 103,16592,21887,21887,21891,21897,2501,21904,21908,21923}, + { 109,6682,2974,2974,21941,21949,1506,21963,21967,21984}, + { 111,7612,21998,21998,3512,3512,1745,22002,22006,22024}, + { 68,12527,22041,22041,22045,22053,1343,19506,19510,19510}, + { 117,6630,22059,22059,22063,22070,1497,22081,22085,22104}, + { 113,8858,3522,3522,22118,22124,1925,22137,22141,22154}, + { 121,11630,22189,22189,22193,22198,2234,22211,22215,22227}, + { 116,8072,22249,22249,22253,22258,1829,22269,22273,22286}, + { 110,6731,3005,3005,22306,22314,1509,1509,22322,22339}, + { 118,6774,3033,3033,22355,22361,1343,19506,19510,19840}, + { 124,14775,22368,22368,22372,22372,1448,22380,22384,22384}, + { 126,15528,22400,22400,22404,22411,2438,22424,22428,22444}, + { 122,6819,3063,3063,22466,22472,1523,22479,22483,22496}, + { 129,9184,22506,22506,22510,22510,1977,22516,22520,22536}, + { 130,9141,22554,22554,22558,22569,1953,22590,22594,22609}, + { 40,10086,5054,5054,22639,22648,2026,22670,22674,22689}, + { 134,6874,3091,3091,22723,22735,1527,22748,22752,22769}, + { 136,16067,22786,22786,22790,22797,2463,22810,22814,22828}, + { 137,9072,22850,22850,22854,22865,1932,22884,22888,22906}, + { 138,10128,5079,5079,22940,5069,2030,22945,22949,22961}, + { 139,15812,22981,22981,22985,22993,2453,23004,23008,23023}, + { 145,13937,23045,23045,23049,23049,1599,1599,20406,23063}, + { 42,10379,23081,23081,23085,23095,2122,23127,23131,23148}, + { 141,7945,23196,23196,23200,23210,1343,19506,19510,23218}, + { 147,10898,23224,23224,23228,23239,1343,19506,19510,19510}, + { 140,7893,23251,23251,23255,23262,1343,19506,19510,23270}, + { 148,13405,23275,23275,23279,23285,2315,23296,23300,23313}, + { 159,14232,4918,4918,23333,23341,2349,2483,23354,23370}, + { 158,14357,23390,23390,23394,23394,1343,19506,19510,19840}, + { 152,12128,23401,23401,23405,23413,2246,23431,23435,23448}, + { 270,15754,23464,23464,23468,23479,1343,19506,19510,23497}, + { 19618,8436,23502,23502,23506,23516,1871,23502,23537,23554}, + { 157,16189,23586,23586,23590,23590,2434,20453,20457,20480}, + { 27,10203,23595,23595,23599,5098,2080,23615,23619,23632}, + { 154,9890,23663,23663,23667,23676,2217,23689,23693,23710}, + { 151,13877,23723,23723,23727,23743,2340,23771,23775,23791}, + { 163,8897,4296,4296,23801,23801,1343,19506,19510,23807}, + { 166,11804,23812,23812,23816,23823,1448,23831,23835,23848}, + { 167,9002,23862,23862,23866,23866,1929,23875,23879,23897}, + { 175,10726,23914,23914,23918,23926,2151,23935,23939,23954}, + { 182,16943,23960,23960,23964,23964,2516,23974,23978,23998}, + { 176,6926,3119,3119,24021,24033,1343,19506,19510,19510}, + { 177,6984,3146,3146,24043,24050,1710,24056,24060,24076}, + { 178,10499,24090,24090,24094,24100,2221,24116,24120,24135}, + { 183,13972,24176,24176,24180,24180,1448,24192,24196,24196}, + { 164,14744,24215,24215,24219,24224,2382,24235,24239,24250}, + { 192,14309,4625,4625,24270,24277,2359,24285,24289,24307}, + { 187,15375,24324,24324,24328,24333,2431,24339,24343,24356}, + { 201,10672,24374,24374,24378,24390,2147,24400,24404,24420}, + { 190,7558,24438,24438,24442,24451,1734,24466,24470,24486}, + { 191,7025,24514,24514,24518,24525,1550,24532,24536,24549}, + { 202,17001,24563,24563,24567,24567,1448,21124,21128,21138}, + { 193,12067,24579,24579,24583,24583,1343,19506,19510,19510}, + { 185,16545,24592,24592,24596,24596,2497,24605,24609,24628}, + { 197,16644,24647,24647,24651,24657,2503,24664,24668,24680}, + { 198,14884,24698,24698,24702,24711,1343,19506,19510,19840}, + { 200,7169,24723,24723,24727,24735,1621,1621,24744,24757}, + { 271,15184,17489,17489,24771,24778,2409,2409,24791,24805}, + { 203,7225,3299,3299,24818,24825,1625,24838,24842,24856}, + { 204,11498,24888,24888,24892,24892,2190,24899,24903,0}, + { 205,297,24917,24917,24921,24934,1266,24981,24985,24997}, + { 221,7414,25017,25017,25021,25028,1710,25036,25040,25054}, + { 215,13469,25067,25067,25071,25081,1448,25091,25095,25112}, + { 212,7805,25125,25125,25129,25138,1343,19506,19510,25148}, + { 143,7324,25153,25153,25157,25166,1343,19506,19510,19840}, + { 210,15427,25176,25176,25180,25188,2434,20453,20457,20480}, + { 216,11210,5769,5769,25198,25206,2159,25217,25221,25237}, + { 72,16807,3642,3642,25253,25253,1448,21124,21128,21138}, + { 222,15300,25265,25265,25269,25275,2421,25286,25290,25303}, + { 227,7461,3437,3437,25323,3427,1726,1726,25332,25342}, + { 228,8024,25361,25361,25365,25376,1953,25397,25401,25420}, + { 238,9244,25433,25433,25437,25450,1981,1981,25464,25484}, + { 234,14525,25500,25500,25504,25512,2363,25521,25525,25540}, + { 235,7507,25562,25562,25566,25573,1730,25582,25586,25599}, + { 225,15569,25613,25613,25617,25617,1448,25635,25639,25639}, + { 237,6255,25664,25664,25668,25675,1448,25682,25686,25704}, + { 241,7679,3564,3564,25714,25722,1748,25737,25741,25759}, + { 244,6496,25793,25793,25797,25797,1448,21124,21128,21128}, + { 246,16346,25811,25811,25815,25815,1448,25823,25827,25842}, + { 247,9308,4553,4553,25856,25867,2211,25880,25884,25900}, + { 249,14829,25922,25922,25926,25926,2392,25936,25940,25960}, + { 251,8130,25980,25980,25984,25992,1838,26003,26007,26023}, + { 261,14996,26042,26042,26046,26052,2396,26063,26067,26079}, + { 209,8475,26097,26097,26101,0,1878,26114,26118,0}, + { 264,15844,26137,26137,26141,26141,1448,21124,21128,21128} +}; + + +static const RegionInfoNameEntry region_name_entries [] = { + {16296, 0}, /* AE */ + {10618, 1}, /* AF */ + {7370, 2}, /* AL */ + {49, 3}, /* AM */ + {15623, 4}, /* AR */ + {13163, 5}, /* AT */ + {13196, 6}, /* AU */ + {8261, 7}, /* AZ */ + {13726, 8}, /* BA */ + {12698, 9}, /* BD */ + {11855, 10}, /* BE */ + {6161, 11}, /* BG */ + {16453, 12}, /* BH */ + {12578, 13}, /* BN */ + {16720, 14}, /* BO */ + {7074, 15}, /* BR */ + {12425, 16}, /* BW */ + {7749, 17}, /* BY */ + {15330, 18}, /* BZ */ + {13289, 19}, /* CA */ + {15114, 20}, /* CD */ + {300, 21}, /* CH */ + {15962, 22}, /* CI */ + {16145, 23}, /* CL */ + {15675, 24}, /* CM */ + {9961, 25}, /* CN */ + {15048, 26}, /* CO */ + {14028, 27}, /* CR */ + {17105, 28}, /* CU */ + {6317, 29}, /* CZ */ + {6407, 30}, /* DE */ + {6359, 31}, /* DK */ + {14633, 32}, /* DO */ + {13788, 33}, /* DZ */ + {15894, 34}, /* EC */ + {7847, 35}, /* EE */ + {13018, 36}, /* EG */ + {12966, 37}, /* ER */ + {6204, 38}, /* ES */ + {10438, 39}, /* ET */ + {6537, 40}, /* FI */ + {8801, 41}, /* FO */ + {6581, 42}, /* FR */ + {10020, 43}, /* GB */ + {8747, 44}, /* GE */ + {10962, 45}, /* GL */ + {6463, 46}, /* GR */ + {13600, 47}, /* GT */ + {13111, 48}, /* HK */ + {16889, 49}, /* HN */ + {7273, 50}, /* HR */ + {16592, 51}, /* HT */ + {6682, 52}, /* HU */ + {7612, 53}, /* ID */ + {12527, 54}, /* IE */ + {6630, 55}, /* IL */ + {8858, 56}, /* IN */ + {11630, 57}, /* IQ */ + {8072, 58}, /* IR */ + {6731, 59}, /* IS */ + {6774, 60}, /* IT */ + {14775, 61}, /* JM */ + {15528, 62}, /* JO */ + {6819, 63}, /* JP */ + {9184, 64}, /* KE */ + {9141, 65}, /* KG */ + {10086, 66}, /* KH */ + {6874, 67}, /* KR */ + {16067, 68}, /* KW */ + {9072, 69}, /* KZ */ + {10128, 70}, /* LA */ + {15812, 71}, /* LB */ + {13937, 72}, /* LI */ + {10379, 73}, /* LK */ + {7945, 74}, /* LT */ + {10898, 75}, /* LU */ + {7893, 76}, /* LV */ + {13405, 77}, /* LY */ + {14232, 78}, /* MA */ + {14357, 79}, /* MC */ + {12128, 80}, /* MD */ + {15754, 81}, /* ME */ + {8436, 82}, /* MK */ + {16189, 83}, /* ML */ + {10203, 84}, /* MM */ + {9890, 85}, /* MN */ + {13877, 86}, /* MO */ + {8897, 87}, /* MT */ + {11804, 88}, /* MX */ + {9002, 89}, /* MY */ + {10726, 90}, /* NG */ + {16943, 91}, /* NI */ + {6926, 92}, /* NL */ + {6984, 93}, /* NO */ + {10499, 94}, /* NP */ + {13972, 95}, /* NZ */ + {14744, 96}, /* OM */ + {14309, 97}, /* PA */ + {15375, 98}, /* PE */ + {10672, 99}, /* PH */ + {7558, 100}, /* PK */ + {7025, 101}, /* PL */ + {17001, 102}, /* PR */ + {12067, 103}, /* PT */ + {16545, 104}, /* PY */ + {16644, 105}, /* QA */ + {14884, 106}, /* RE */ + {7169, 107}, /* RO */ + {15184, 108}, /* RS */ + {7225, 109}, /* RU */ + {11498, 110}, /* RW */ + {297, 111}, /* SA */ + {7414, 112}, /* SE */ + {13469, 113}, /* SG */ + {7805, 114}, /* SI */ + {7324, 115}, /* SK */ + {15427, 116}, /* SN */ + {11210, 117}, /* SO */ + {16807, 118}, /* SV */ + {15300, 119}, /* SY */ + {7461, 120}, /* TH */ + {8024, 121}, /* TJ */ + {9244, 122}, /* TM */ + {14525, 123}, /* TN */ + {7507, 124}, /* TR */ + {15569, 125}, /* TT */ + {6255, 126}, /* TW */ + {7679, 127}, /* UA */ + {6496, 128}, /* US */ + {16346, 129}, /* UY */ + {9308, 130}, /* UZ */ + {14829, 131}, /* VE */ + {8130, 132}, /* VN */ + {14996, 133}, /* YE */ + {8475, 134}, /* ZA */ + {15844, 135} /* ZW */ +}; + + +static const char locale_strings [] = { + "\0" + "/\0" + ":\0" + "am\0" + "pm\0" + ".\0" + "a. m.\0" + "p. m.\0" + "\xe4\xb8\x8a\xe5\x8d\x88\0" + "\xe4\xb8\x8b\xe5\x8d\x88\0" + "dop.\0" + "odp.\0" + "AM\0" + "PM\0" + "-\0" + "vorm.\0" + "nachm.\0" + "\xcf\x80.\xce\xbc.\0" + "\xce\xbc.\xce\xbc.\0" + "ap.\0" + "ip.\0" + "\xd7\x9c\xd7\xa4\xd7\xa0\xd7\x94\xd7\xb4\xd7\xa6\0" + "\xd7\x90\xd7\x97\xd7\x94\xd7\xb4\xd7\xa6\0" + "de.\0" + "du.\0" + ". \0" + "f.h.\0" + "e.h.\0" + "\xe5\x8d\x88\xe5\x89\x8d\0" + "\xe5\x8d\x88\xe5\xbe\x8c\0" + "a.m.\0" + "p.m.\0" + "\xd0\x94\xd0\x9f\0" + "\xd0\x9f\xd0\x9f\0" + "e paradites\0" + "e pasdites\0" + "fm\0" + "em\0" + "\xc3\x96\xc3\x96\0" + "\xc3\x96S\0" + "\xd0\xb4\xd0\xbf\0" + "\xd0\xbf\xd0\xbf\0" + "pop.\0" + "priek\xc5\xa1p.\0" + "p\xc4\x93\x63p.\0" + "prie\xc5\xa1piet\0" + "popiet\0" + "\xd0\xbf\xd0\xb5. \xd1\x87\xd0\xbe.\0" + "\xd0\xbf\xd0\xb0. \xd1\x87\xd0\xbe.\0" + "\xd9\x82.\xd8\xb8.\0" + "\xd8\xa8.\xd8\xb8.\0" + "SA\0" + "CH\0" + "\xd4\xbf\xd4\xb1\0" + "\xd4\xbf\xd5\x80\0" + "dopo\xc5\x82\x64nja\0" + "popo\xc5\x82\x64nju\0" + "\xd0\xbf\xd1\x80\xd0\xb5\xd1\x82\xd0\xbf\xd0\xbb.\0" + "\xd0\xbf\xd0\xbe\xd0\xbf\xd0\xbb.\0" + "vm.\0" + "nm.\0" + "\xe0\xa4\xaa\xe0\xa5\x82\xe0\xa4\xb0\xe0\xa5\x8d\xe0\xa4\xb5\xe0\xa4\xbe\xe0\xa4\xb9\xe0\xa5\x8d\xe0\xa4\xa8\0" + "\xe0\xa4\x85\xe0\xa4\xaa\xe0\xa4\xb0\xe0\xa4\xbe\xe0\xa4\xb9\xe0\xa5\x8d\xe0\xa4\xa8\0" + "i.b.\0" + "e.b.\0" + "PG\0" + "PTG\0" + "\xd1\x82\xd2\xa3\0" + "\xd1\x82\xd0\xba\0" + "TO\0" + "TK\0" + "\xe0\xa8\xaa\xe0\xa9\x82.\xe0\xa8\xa6\xe0\xa9\x81.\0" + "\xe0\xa8\xac\xe0\xa8\xbe.\xe0\xa8\xa6\xe0\xa9\x81.\0" + "\xe0\xae\xae\xe0\xaf\x81\xe0\xae\xb1\xe0\xaf\x8d\xe0\xae\xaa\xe0\xae\x95\xe0\xae\xb2\xe0\xaf\x8d\0" + "\xe0\xae\xaa\xe0\xae\xbf\xe0\xae\xb1\xe0\xaf\x8d\xe0\xae\xaa\xe0\xae\x95\xe0\xae\xb2\xe0\xaf\x8d\0" + "\xe0\xb2\xaa\xe0\xb3\x82\xe0\xb2\xb0\xe0\xb3\x8d\xe0\xb2\xb5\xe0\xb2\xbe\xe0\xb2\xb9\xe0\xb3\x8d\xe0\xb2\xa8\0" + "\xe0\xb2\x85\xe0\xb2\xaa\xe0\xb2\xb0\xe0\xb2\xbe\xe0\xb2\xb9\xe0\xb3\x8d\xe0\xb2\xa8\0" + "\xe0\xa6\xaa\xe0\xa7\x82\xe0\xa7\xb0\xe0\xa7\x8d\xe0\xa6\xac\xe0\xa6\xbe\xe0\xa6\xb9\xe0\xa7\x8d\xe0\xa6\xa3\0" + "\xe0\xa6\x85\xe0\xa6\xaa\xe0\xa7\xb0\xe0\xa6\xbe\xe0\xa6\xb9\xe0\xa7\x8d\xe0\xa6\xa3\0" + "\xe0\xa4\xae.\xe0\xa4\xaa\xe0\xa5\x82.\0" + "\xe0\xa4\xae.\xe0\xa4\x89.\0" + "\xd2\xae\xd3\xa8\0" + "\xd2\xae\xd0\xa5\0" + "\xe0\xbd\xa6\xe0\xbe\x94\xe0\xbc\x8b\xe0\xbd\x91\xe0\xbe\xb2\xe0\xbd\xbc\xe0\xbc\x8b\0" + "\xe0\xbd\x95\xe0\xbe\xb1\xe0\xbd\xb2\xe0\xbc\x8b\xe0\xbd\x91\xe0\xbe\xb2\xe0\xbd\xbc\xe0\xbc\x8b\0" + "yb\0" + "yh\0" + "\xe0\xba\x81\xe0\xbb\x88\xe0\xba\xad\xe0\xba\x99\xe0\xba\x97\xe0\xbb\x88\xe0\xba\xbd\xe0\xba\x87\0" + "\xe0\xba\xab\xe0\xba\xbc\xe0\xba\xb1\xe0\xba\x87\xe0\xba\x97\xe0\xbb\x88\xe0\xba\xbd\xe0\xba\x87\0" + "\xe1\x80\x94\xe1\x80\xb6\xe1\x80\x94\xe1\x80\x80\xe1\x80\xba\0" + "\xe1\x80\x8a\xe1\x80\x94\xe1\x80\xb1\0" + "\xe0\xa4\xae.\xe0\xa4\xa8\xe0\xa4\x82.\0" + "\xe0\xb6\xb4\xe0\xb7\x99.\xe0\xb7\x80.\0" + "\xe0\xb6\xb4.\xe0\xb7\x80.\0" + "\xe1\x8f\x8c\xe1\x8e\xbe\xe1\x8e\xb4\0" + "\xe1\x8f\x92\xe1\x8e\xaf\xe1\x8f\xb1\xe1\x8e\xa2\xe1\x8f\x97\xe1\x8f\xa2\0" + "\xe1\x8c\xa5\xe1\x8b\x8b\xe1\x89\xb5\0" + "\xe1\x8a\xa8\xe1\x88\xb0\xe1\x8b\x93\xe1\x89\xb5\0" + "Zdat azal\0" + "\xe1\xb8\x8c\x65\x66\x66ir aza\0" + "subaka\0" + "kikii\xc9\x97\x65\0" + "\xc3\x80\xc3\xa1r\xe1\xbb\x8d\xcc\x80\0" + "\xe1\xbb\x8c\xcc\x80s\xc3\xa1n\0" + "moies\0" + "nom\xc3\xabttes\0" + "u.t.\0" + "u.k.\0" + "A.M.\0" + "P.M.\0" + "WD\0" + "WB\0" + "\xe1\x8a\x95\xe1\x8c\x89\xe1\x88\x86 \xe1\x88\xb0\xe1\x8b\x93\xe1\x89\xb0\0" + "\xe1\x8b\xb5\xe1\x88\x95\xe1\x88\xad \xe1\x88\xb0\xe1\x8b\x93\xe1\x89\xb5\0" + "sn.\0" + "gn.\0" + "\xea\x8e\xb8\xea\x84\x91\0" + "\xea\x81\xaf\xea\x8b\x92\0" + "G.M.\0" + "\xda\x86.\xd8\xa8\0" + "\xda\x86.\xd9\x83\0" + "v.m.\0" + "n.m.\0" + "\xd0\xad\xd0\x98\0" + "\xd0\xad\xd0\x9a\0" + "m\0" + "f\0" + "\xd8\xb5\0" + "\xd9\x85\0" + "f.m.\0" + "e.m.\0" + "\xd0\x90\xd0\x9c\0" + "\xd0\x9f\xd0\x9c\0" + "w\xc3\xb3tpo\xc5\x82\x64nja\0" + "\xd0\xa2\xd0\x9e\0" + "\xd0\xa2\xd0\x9a\0" + "prijepodne\0" + "popodne\0" + "prije podne\0" + "po podne\0" + "\xd0\xbf\xd1\x80\xd0\xb8\xd1\x98\xd0\xb5 \xd0\xbf\xd0\xbe\xd0\xb4\xd0\xbd\xd0\xb5\0" + "\xd0\xbf\xd0\xbe \xd0\xbf\xd0\xbe\xd0\xb4\xd0\xbd\xd0\xb5\0" + "\xd0\xbf\xd1\x80\xd0\xb5 \xd0\xbf\xd0\xbe\xd0\xb4\xd0\xbd\xd0\xb5\0" + "\xd0\xbf\xd0\xbe\xd0\xbf\xd0\xbe\xd0\xb4\xd0\xbd\xd0\xb5\0" + "pre podne\0" + "ep.\0" + "mat.\0" + "soir\0" + ",\0" + "\xd8\xb1.\xd8\xb3.\xe2\x80\x8f\0" + "\xd9\xaa\xd8\x9c\0" + "\xd9\x84\xd9\x8a\xd8\xb3\xc2\xa0\xd8\xb1\xd9\x82\xd9\x85\0" + "\xd8\x89\0" + "-Infinity\0" + "Infinity\0" + "\xd8\x9c+\0" + "\xc2\xa0\0" + "\xd0\xbb\xd0\xb2.\0" + "%\0" + "NaN\0" + "\xe2\x80\xb0\0" + "+\0" + "\xe2\x82\xac\0" + "-Infinit\0" + "Infinit\0" + "\xc2\xa5\0" + "K\xc4\x8d\0" + "-nekone\xc4\x8dno\0" + "+nekone\xc4\x8dno\0" + "kr.\0" + "-unendlich\0" + "+unendlich\0" + "-\xce\x86\xcf\x80\xce\xb5\xce\xb9\xcf\x81\xce\xbf\0" + "\xce\x86\xcf\x80\xce\xb5\xce\xb9\xcf\x81\xce\xbf\0" + "$\0" + "-Infinito\0" + "Infinito\0" + "ep\xc3\xa4luku\0" + "\xd9\xaa\0" + "-Infini\0" + "+Infini\0" + "\xe2\x82\xaa\0" + "\xe2\x80\x8e+\0" + "Ft\0" + "ISK\0" + "+Infinito\0" + "\xef\xbf\xa5\0" + "\xe2\x82\xa9\0" + "-oneindig\0" + "oneindig\0" + "z\xc5\x82\0" + "-niesko\xc5\x84\x63zono\xc5\x9b\xc4\x87\0" + "+niesko\xc5\x84\x63zono\xc5\x9b\xc4\x87\0" + "R$\0" + "\xe2\x80\x99\0" + "CHF\0" + "-infinit\0" + "+infinit\0" + "RON\0" + "\xe2\x82\xbd\0" + "\xd0\xbd\xd0\xb5\xc2\xa0\xd1\x87\xd0\xb8\xd1\x81\xd0\xbb\xd0\xbe\0" + "-\xd0\xb1\xd0\xb5\xd1\x81\xd0\xba\xd0\xbe\xd0\xbd\xd0\xb5\xd1\x87\xd0\xbd\xd0\xbe\xd1\x81\xd1\x82\xd1\x8c\0" + "\xd0\xb1\xd0\xb5\xd1\x81\xd0\xba\xd0\xbe\xd0\xbd\xd0\xb5\xd1\x87\xd0\xbd\xd0\xbe\xd1\x81\xd1\x82\xd1\x8c\0" + "kn\0" + "Lek\xc3\xab\0" + "kr\0" + "\xc2\xa4\xc2\xa4\xc2\xa4\0" + "\xd8\x89\xe2\x80\x8f\0" + "THB\0" + "\xe2\x82\xba\0" + "Rs\0" + "\xe2\x80\x8e+\xe2\x80\x8e\0" + "Rp\0" + "\xe2\x82\xb4\0" + "Br\0" + "-neskon\xc4\x8dnost\0" + "neskon\xc4\x8dnost\0" + "NS\0" + "-bezgal\xc4\xab\x62\x61\0" + "bezgal\xc4\xab\x62\x61\0" + "-begalyb\xc4\x97\0" + "begalyb\xc4\x97\0" + "\xd8\xb1\xdb\x8c\xd8\xa7\xd9\x84\0" + "\xe2\x82\xab\0" + "\xd6\x8f\0" + "\xd5\x88\xd5\xb9\xd4\xb9\0" + "-Infinitu\0" + "Infinitu\0" + "\xd0\xb4\xd0\xb5\xd0\xbd\0" + "R\0" + "\xe2\x82\xbe\0" + "\xe1\x83\x90\xe1\x83\xa0\xc2\xa0\xe1\x83\x90\xe1\x83\xa0\xe1\x83\x98\xe1\x83\xa1\xc2\xa0\xe1\x83\xa0\xe1\x83\x98\xe1\x83\xaa\xe1\x83\xae\xe1\x83\x95\xe1\x83\x98\0" + "\xe2\x82\xb9\0" + "RM\0" + "\xe2\x82\xb8\0" + "\xd1\x81\xd0\xb0\xd0\xbd\xc2\xa0\xd0\xb5\xd0\xbc\xd0\xb5\xd1\x81\0" + "\xd1\x81\xd0\xbe\xd0\xbc\0" + "\xd1\x81\xd0\xb0\xd0\xbd\xc2\xa0\xd1\x8d\xd0\xbc\xd0\xb5\xd1\x81\0" + "Ksh\0" + "TMT\0" + "san\xc2\xa0\x64\xc3\xa4l\0" + "haqiqiy\xc2\xa0son\xc2\xa0\x65mas\0" + "\xe0\xa6\x9f\xe0\xa6\xbe\0" + "`\0" + "\xc2\xa3\0" + "\xe1\x9f\x9b\0" + "\xe2\x82\xad\0" + "\xe0\xba\x9a\xe0\xbb\x8d\xe0\xbb\x88\xe2\x80\x8b\xe0\xbb\x81\xe0\xba\xa1\xe0\xbb\x88\xe0\xba\x99\xe2\x80\x8b\xe0\xbb\x82\xe0\xba\x95\xe2\x80\x8b\xe0\xbb\x80\xe0\xba\xa5\xe0\xba\x81\0" + "K\0" + "\xe1\x80\x82\xe1\x80\x8f\xe1\x80\x94\xe1\x80\xba\xe1\x80\xb8\xe1\x80\x99\xe1\x80\x9f\xe1\x80\xaf\xe1\x80\x90\xe1\x80\xba\xe1\x80\x9e\xe1\x80\xb1\xe1\x80\xac\0" + "\xe0\xb6\xbb\xe0\xb7\x94.\0" + "\xe1\x89\xa5\xe1\x88\xad\0" + "\xe0\xa4\xb0\xe0\xa5\x81\0" + "\xd8\x8b\0" + "\xe2\x82\xb1\0" + "\xe2\x82\xa6\0" + "Nfk\0" + "S\0" + "\xd1\x87\xd1\x8b\xd1\x8b\xd2\xbb\xd1\x8b\xd0\xbb\xd0\xb0\xc2\xa0\xd0\xb1\xd1\x83\xd0\xbe\xd1\x82\xd0\xb0\xd1\x85\0" + "RF\0" + "\xe9\x9d\x9e\xe6\x95\xb8\xe5\x80\xbc\0" + "HRK\0" + "\xe2\x82\xbc\0" + "so\xca\xbbm\0" + "\xe2\x82\xae\0" + "\xe0\xa4\xa8\xe0\xa5\x87\xe0\xa4\xb0\xe0\xa5\x82\0" + "\xd8\xaf.\xd8\xb9.\xe2\x80\x8f\0" + "'\0" + "L\0" + "P\0" + "\xd1\x81\xd1\x9e\xd0\xbc\0" + "\xd2\xb3\xd0\xb0\xd2\x9b\xd0\xb8\xd2\x9b\xd0\xb8\xd0\xb9\xc2\xa0\xd1\x81\xd0\xbe\xd0\xbd\xc2\xa0\xd1\x8d\xd0\xbc\xd0\xb0\xd1\x81\0" + "\xe0\xa7\xb3\0" + "\xd8\xb1\0" + "Rs.\0" + "\xd8\xac.\xd9\x85.\xe2\x80\x8f\0" + "HK$\0" + "\xd8\xaf.\xd9\x84.\xe2\x80\x8f\0" + "Q\0" + "KM\0" + "\xd8\xaf.\xd8\xac.\xe2\x80\x8f\0" + "MOP$\0" + "\xe2\x82\xa1\0" + "\xd8\xaf.\xd9\x85.\xe2\x80\x8f\0" + "B/.\0" + "\xd8\xaf.\xd8\xaa.\xe2\x80\x8f\0" + "RD$\0" + "\xd0\x9a\xd0\x9c\0" + "\xd8\xb1.\xd8\xb9.\xe2\x80\x8f\0" + "Bs.\0" + "\xd8\xb1.\xd9\x8a.\xe2\x80\x8f\0" + "FC\0" + "RSD\0" + "epiloho\0" + "\xd9\x84.\xd8\xb3.\xe2\x80\x8f\0" + "S/\0" + "CFA\0" + "\xd8\xaf.\xd8\xa3.\xe2\x80\x8f\0" + "FCFA\0" + "\xd9\x84.\xd9\x84.\xe2\x80\x8f\0" + "\xd8\xaf.\xd9\x83.\xe2\x80\x8f\0" + "\xd8\xaf.\xd8\xa5.\xe2\x80\x8f\0" + "MAD\0" + "\xd8\xaf.\xd8\xa8.\xe2\x80\x8f\0" + "Gs.\0" + "G\0" + "\xd8\xb1.\xd9\x82.\xe2\x80\x8f\0" + "Bs\0" + "C$\0" + "\xd0\x94\xd0\xb8\xd0\xbd.\0" + "Din.\0" + "ar\0" + "Arabic\0" + "\xd8\xa7\xd9\x84\xd8\xb9\xd8\xb1\xd8\xa8\xd9\x8a\xd8\xa9\0" + "ARA\0" + "ara\0" + "bg\0" + "Bulgarian\0" + "\xd0\xb1\xd1\x8a\xd0\xbb\xd0\xb3\xd0\xb0\xd1\x80\xd1\x81\xd0\xba\xd0\xb8\0" + "BGR\0" + "bul\0" + "ca\0" + "Catalan\0" + "catal\xc3\xa0\0" + "CAT\0" + "cat\0" + "zh-Hans\0" + "Chinese (Simplified)\0" + "\xe4\xb8\xad\xe6\x96\x87\0" + "CHS\0" + "zho\0" + "zh\0" + "zh-CHS\0" + "Chinese (Simplified) Legacy\0" + "cs\0" + "Czech\0" + "\xc4\x8d\x65\xc5\xa1tina\0" + "CSY\0" + "ces\0" + "da\0" + "Danish\0" + "dansk\0" + "DAN\0" + "dan\0" + "de\0" + "German\0" + "Deutsch\0" + "DEU\0" + "deu\0" + "el\0" + "Greek\0" + "\xce\x95\xce\xbb\xce\xbb\xce\xb7\xce\xbd\xce\xb9\xce\xba\xce\xac\0" + "ELL\0" + "ell\0" + "en\0" + "English\0" + "ENU\0" + "eng\0" + "es\0" + "Spanish\0" + "espa\xc3\xb1ol\0" + "ESP\0" + "spa\0" + "fi\0" + "Finnish\0" + "suomi\0" + "FIN\0" + "fin\0" + "fr\0" + "French\0" + "fran\xc3\xa7\x61is\0" + "FRA\0" + "fra\0" + "he\0" + "Hebrew\0" + "\xd7\xa2\xd7\x91\xd7\xa8\xd7\x99\xd7\xaa\0" + "HEB\0" + "heb\0" + "hu\0" + "Hungarian\0" + "magyar\0" + "HUN\0" + "hun\0" + "is\0" + "Icelandic\0" + "\xc3\xadslenska\0" + "ISL\0" + "isl\0" + "it\0" + "Italian\0" + "italiano\0" + "ITA\0" + "ita\0" + "ja\0" + "Japanese\0" + "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e\0" + "JPN\0" + "jpn\0" + "ko\0" + "Korean\0" + "\xed\x95\x9c\xea\xb5\xad\xec\x96\xb4\0" + "KOR\0" + "kor\0" + "nl\0" + "Dutch\0" + "Nederlands\0" + "NLD\0" + "nld\0" + "no\0" + "Norwegian\0" + "norsk\0" + "NOR\0" + "nob\0" + "nb\0" + "pl\0" + "Polish\0" + "polski\0" + "PLK\0" + "pol\0" + "pt\0" + "Portuguese\0" + "portugu\xc3\xaas\0" + "PTB\0" + "por\0" + "rm\0" + "Romansh\0" + "rumantsch\0" + "RMC\0" + "roh\0" + "ro\0" + "Romanian\0" + "rom\xc3\xa2n\xc4\x83\0" + "ROM\0" + "ron\0" + "ru\0" + "Russian\0" + "\xd1\x80\xd1\x83\xd1\x81\xd1\x81\xd0\xba\xd0\xb8\xd0\xb9\0" + "RUS\0" + "rus\0" + "hr\0" + "Croatian\0" + "hrvatski\0" + "HRV\0" + "hrv\0" + "sk\0" + "Slovak\0" + "sloven\xc4\x8dina\0" + "SKY\0" + "slk\0" + "sq\0" + "Albanian\0" + "shqip\0" + "SQI\0" + "sqi\0" + "sv\0" + "Swedish\0" + "svenska\0" + "SVE\0" + "swe\0" + "th\0" + "Thai\0" + "\xe0\xb9\x84\xe0\xb8\x97\xe0\xb8\xa2\0" + "THA\0" + "tha\0" + "tr\0" + "Turkish\0" + "T\xc3\xbcrk\xc3\xa7\x65\0" + "TRK\0" + "tur\0" + "ur\0" + "Urdu\0" + "\xd8\xa7\xd8\xb1\xd8\xaf\xd9\x88\0" + "URD\0" + "urd\0" + "id\0" + "Indonesian\0" + "Indonesia\0" + "IND\0" + "ind\0" + "uk\0" + "Ukrainian\0" + "\xd1\x83\xd0\xba\xd1\x80\xd0\xb0\xd1\x97\xd0\xbd\xd1\x81\xd1\x8c\xd0\xba\xd0\xb0\0" + "UKR\0" + "ukr\0" + "be\0" + "Belarusian\0" + "\xd0\xb1\xd0\xb5\xd0\xbb\xd0\xb0\xd1\x80\xd1\x83\xd1\x81\xd0\xba\xd0\xb0\xd1\x8f\0" + "BEL\0" + "bel\0" + "sl\0" + "Slovenian\0" + "sloven\xc5\xa1\xc4\x8dina\0" + "SLV\0" + "slv\0" + "et\0" + "Estonian\0" + "eesti\0" + "ETI\0" + "est\0" + "lv\0" + "Latvian\0" + "latvie\xc5\xa1u\0" + "LVI\0" + "lav\0" + "lt\0" + "Lithuanian\0" + "lietuvi\xc5\xb3\0" + "LTH\0" + "lit\0" + "tg\0" + "Tajik\0" + "\xd0\xa2\xd0\xbe\xd2\xb7\xd0\xb8\xd0\xba\xd3\xa3\0" + "TAJ\0" + "tgk\0" + "fa\0" + "Persian\0" + "\xd9\x81\xd8\xa7\xd8\xb1\xd8\xb3\xdb\x8c\0" + "FAR\0" + "fas\0" + "vi\0" + "Vietnamese\0" + "Ti\xe1\xba\xbfng Vi\xe1\xbb\x87t\0" + "VIT\0" + "vie\0" + "hy\0" + "Armenian\0" + "\xd5\xb0\xd5\xa1\xd5\xb5\xd5\xa5\xd6\x80\xd5\xa5\xd5\xb6\0" + "HYE\0" + "hye\0" + "az\0" + "Azerbaijani\0" + "az\xc9\x99rbaycan\0" + "AZE\0" + "aze\0" + "eu\0" + "Basque\0" + "euskara\0" + "EUQ\0" + "eus\0" + "hsb\0" + "Upper Sorbian\0" + "hornjoserb\xc5\xa1\xc4\x87ina\0" + "HSB\0" + "mk\0" + "Macedonian\0" + "\xd0\xbc\xd0\xb0\xd0\xba\xd0\xb5\xd0\xb4\xd0\xbe\xd0\xbd\xd1\x81\xd0\xba\xd0\xb8\0" + "MKI\0" + "mkd\0" + "st\0" + "Southern Sotho\0" + "Sesotho\0" + "SOT\0" + "sot\0" + "ts\0" + "Tsonga\0" + "Xitsonga\0" + "TSO\0" + "tso\0" + "tn\0" + "Tswana\0" + "Setswana\0" + "TSN\0" + "tsn\0" + "xh\0" + "Xhosa\0" + "isiXhosa\0" + "XHO\0" + "xho\0" + "zu\0" + "Zulu\0" + "isiZulu\0" + "ZUL\0" + "zul\0" + "af\0" + "Afrikaans\0" + "AFK\0" + "afr\0" + "ka\0" + "Georgian\0" + "\xe1\x83\xa5\xe1\x83\x90\xe1\x83\xa0\xe1\x83\x97\xe1\x83\xa3\xe1\x83\x9a\xe1\x83\x98\0" + "KAT\0" + "kat\0" + "fo\0" + "Faroese\0" + "f\xc3\xb8royskt\0" + "FOS\0" + "fao\0" + "hi\0" + "Hindi\0" + "\xe0\xa4\xb9\xe0\xa4\xbf\xe0\xa4\xa8\xe0\xa5\x8d\xe0\xa4\xa6\xe0\xa5\x80\0" + "HIN\0" + "hin\0" + "mt\0" + "Maltese\0" + "Malti\0" + "MLT\0" + "mlt\0" + "se\0" + "Northern Sami\0" + "davvis\xc3\xa1megiella\0" + "SME\0" + "sme\0" + "ga\0" + "Irish\0" + "Gaeilge\0" + "IRE\0" + "gle\0" + "ms\0" + "Malay\0" + "Bahasa Melayu\0" + "MSL\0" + "msa\0" + "kk\0" + "Kazakh\0" + "\xd2\x9b\xd0\xb0\xd0\xb7\xd0\xb0\xd2\x9b \xd1\x82\xd1\x96\xd0\xbb\xd1\x96\0" + "KKZ\0" + "kaz\0" + "ky\0" + "Kyrgyz\0" + "\xd0\xba\xd1\x8b\xd1\x80\xd0\xb3\xd1\x8b\xd0\xb7\xd1\x87\xd0\xb0\0" + "KYR\0" + "kir\0" + "sw\0" + "Swahili\0" + "Kiswahili\0" + "SWK\0" + "swa\0" + "tk\0" + "Turkmen\0" + "t\xc3\xbcrkmen\xc3\xa7\x65\0" + "TUK\0" + "tuk\0" + "uz\0" + "Uzbek\0" + "o\xe2\x80\x98zbek\0" + "UZB\0" + "uzb\0" + "bn\0" + "Bangla\0" + "\xe0\xa6\xac\xe0\xa6\xbe\xe0\xa6\x82\xe0\xa6\xb2\xe0\xa6\xbe\0" + "BNG\0" + "ben\0" + "pa\0" + "Punjabi\0" + "\xe0\xa8\xaa\xe0\xa9\xb0\xe0\xa8\x9c\xe0\xa8\xbe\xe0\xa8\xac\xe0\xa9\x80\0" + "PAN\0" + "pan\0" + "gu\0" + "Gujarati\0" + "\xe0\xaa\x97\xe0\xab\x81\xe0\xaa\x9c\xe0\xaa\xb0\xe0\xaa\xbe\xe0\xaa\xa4\xe0\xab\x80\0" + "GUJ\0" + "guj\0" + "or\0" + "Odia\0" + "\xe0\xac\x93\xe0\xac\xa1\xe0\xac\xbc\xe0\xac\xbf\xe0\xac\x86\0" + "ORI\0" + "ori\0" + "ta\0" + "Tamil\0" + "\xe0\xae\xa4\xe0\xae\xae\xe0\xae\xbf\xe0\xae\xb4\xe0\xaf\x8d\0" + "TAI\0" + "tam\0" + "te\0" + "Telugu\0" + "\xe0\xb0\xa4\xe0\xb1\x86\xe0\xb0\xb2\xe0\xb1\x81\xe0\xb0\x97\xe0\xb1\x81\0" + "TEL\0" + "tel\0" + "Kannada\0" + "\xe0\xb2\x95\xe0\xb2\xa8\xe0\xb3\x8d\xe0\xb2\xa8\xe0\xb2\xa1\0" + "KDI\0" + "kan\0" + "ml\0" + "Malayalam\0" + "\xe0\xb4\xae\xe0\xb4\xb2\xe0\xb4\xaf\xe0\xb4\xbe\xe0\xb4\xb3\xe0\xb4\x82\0" + "MYM\0" + "mal\0" + "as\0" + "Assamese\0" + "\xe0\xa6\x85\xe0\xa6\xb8\xe0\xa6\xae\xe0\xa7\x80\xe0\xa6\xaf\xe0\xa6\xbc\xe0\xa6\xbe\0" + "ASM\0" + "asm\0" + "mr\0" + "Marathi\0" + "\xe0\xa4\xae\xe0\xa4\xb0\xe0\xa4\xbe\xe0\xa4\xa0\xe0\xa5\x80\0" + "MAR\0" + "mar\0" + "mn\0" + "Mongolian\0" + "\xd0\xbc\xd0\xbe\xd0\xbd\xd0\xb3\xd0\xbe\xd0\xbb\0" + "MON\0" + "mon\0" + "bo\0" + "Tibetan\0" + "\xe0\xbd\x96\xe0\xbd\xbc\xe0\xbd\x91\xe0\xbc\x8b\xe0\xbd\xa6\xe0\xbe\x90\xe0\xbd\x91\xe0\xbc\x8b\0" + "BOB\0" + "bod\0" + "cy\0" + "Welsh\0" + "Cymraeg\0" + "CYM\0" + "cym\0" + "km\0" + "Khmer\0" + "\xe1\x9e\x81\xe1\x9f\x92\xe1\x9e\x98\xe1\x9f\x82\xe1\x9e\x9a\0" + "KHM\0" + "khm\0" + "lo\0" + "Lao\0" + "\xe0\xba\xa5\xe0\xba\xb2\xe0\xba\xa7\0" + "LAO\0" + "lao\0" + "my\0" + "Burmese\0" + "\xe1\x80\x99\xe1\x80\xbc\xe1\x80\x94\xe1\x80\xba\xe1\x80\x99\xe1\x80\xac\0" + "MYA\0" + "mya\0" + "gl\0" + "Galician\0" + "galego\0" + "GLC\0" + "glg\0" + "kok\0" + "Konkani\0" + "\xe0\xa4\x95\xe0\xa5\x8b\xe0\xa4\x82\xe0\xa4\x95\xe0\xa4\xa3\xe0\xa5\x80\0" + "KNK\0" + "si\0" + "Sinhala\0" + "\xe0\xb7\x83\xe0\xb7\x92\xe0\xb6\x82\xe0\xb7\x84\xe0\xb6\xbd\0" + "SIN\0" + "sin\0" + "chr\0" + "Cherokee\0" + "\xe1\x8f\xa3\xe1\x8e\xb3\xe1\x8e\xa9\0" + "CRE\0" + "Amharic\0" + "\xe1\x8a\xa0\xe1\x88\x9b\xe1\x88\xad\xe1\x8a\x9b\0" + "AMH\0" + "amh\0" + "tzm\0" + "Central Atlas Tamazight\0" + "Tamazi\xc9\xa3t n la\xe1\xb9\xadla\xe1\xb9\xa3\0" + "TZA\0" + "ne\0" + "Nepali\0" + "\xe0\xa4\xa8\xe0\xa5\x87\xe0\xa4\xaa\xe0\xa4\xbe\xe0\xa4\xb2\xe0\xa5\x80\0" + "NEP\0" + "nep\0" + "fy\0" + "Western Frisian\0" + "West-Frysk\0" + "FYN\0" + "fry\0" + "ps\0" + "Pashto\0" + "\xd9\xbe\xda\x9a\xd8\xaa\xd9\x88\0" + "PAS\0" + "pus\0" + "fil\0" + "Filipino\0" + "FPO\0" + "ff\0" + "Fulah\0" + "Pulaar\0" + "FUL\0" + "ful\0" + "ha\0" + "Hausa\0" + "HAU\0" + "hau\0" + "yo\0" + "Yoruba\0" + "\xc3\x88\x64\xc3\xa8 Yor\xc3\xb9\x62\xc3\xa1\0" + "YOR\0" + "yor\0" + "nso\0" + "Northern Sotho\0" + "Sesotho sa Leboa\0" + "NSO\0" + "lb\0" + "Luxembourgish\0" + "L\xc3\xabtzebuergesch\0" + "LBX\0" + "ltz\0" + "kl\0" + "Kalaallisut\0" + "kalaallisut\0" + "KAL\0" + "kal\0" + "ig\0" + "Igbo\0" + "IBO\0" + "ibo\0" + "om\0" + "Oromo\0" + "Oromoo\0" + "ORM\0" + "orm\0" + "ti\0" + "Tigrinya\0" + "\xe1\x89\xb5\xe1\x8c\x8d\xe1\x88\xad\xe1\x8a\x9b\0" + "TIR\0" + "tir\0" + "haw\0" + "Hawaiian\0" + "\xca\xbb\xc5\x8clelo Hawai\xca\xbbi\0" + "HAW\0" + "so\0" + "Somali\0" + "Soomaali\0" + "SOM\0" + "som\0" + "ii\0" + "Sichuan Yi\0" + "\xea\x86\x88\xea\x8c\xa0\xea\x89\x99\0" + "III\0" + "iii\0" + "br\0" + "Breton\0" + "brezhoneg\0" + "BRE\0" + "bre\0" + "ug\0" + "Uyghur\0" + "\xd8\xa6\xdb\x87\xd9\x8a\xd8\xba\xdb\x87\xd8\xb1\xda\x86\xdb\x95\0" + "UIG\0" + "uig\0" + "gsw\0" + "Swiss German\0" + "Schwiizert\xc3\xbc\xc3\xbctsch\0" + "GSW\0" + "sah\0" + "Sakha\0" + "\xd1\x81\xd0\xb0\xd1\x85\xd0\xb0 \xd1\x82\xd1\x8b\xd0\xbb\xd0\xb0\0" + "SAH\0" + "rw\0" + "Kinyarwanda\0" + "KIN\0" + "kin\0" + "gd\0" + "Scottish Gaelic\0" + "G\xc3\xa0idhlig\0" + "GLA\0" + "gla\0" + "ar-SA\0" + "Arabic (Saudi Arabia)\0" + "\xd8\xa7\xd9\x84\xd8\xb9\xd8\xb1\xd8\xa8\xd9\x8a\xd8\xa9 (\xd8\xa7\xd9\x84\xd9\x85\xd9\x85\xd9\x84\xd9\x83\xd8\xa9 \xd8\xa7\xd9\x84\xd8\xb9\xd8\xb1\xd8\xa8\xd9\x8a\xd8\xa9 \xd8\xa7\xd9\x84\xd8\xb3\xd8\xb9\xd9\x88\xd8\xaf\xd9\x8a\xd8\xa9)\0" + "bg-BG\0" + "Bulgarian (Bulgaria)\0" + "\xd0\xb1\xd1\x8a\xd0\xbb\xd0\xb3\xd0\xb0\xd1\x80\xd1\x81\xd0\xba\xd0\xb8 (\xd0\x91\xd1\x8a\xd0\xbb\xd0\xb3\xd0\xb0\xd1\x80\xd0\xb8\xd1\x8f)\0" + "BG\0" + "ca-ES\0" + "Catalan (Spain)\0" + "catal\xc3\xa0 (Espanya)\0" + "ES\0" + "zh-TW\0" + "Chinese (Traditional)\0" + "\xe4\xb8\xad\xe6\x96\x87 (\xe5\x8f\xb0\xe6\xb9\xbe)\0" + "CHT\0" + "TW\0" + "cs-CZ\0" + "Czech (Czech Republic)\0" + "\xc4\x8d\x65\xc5\xa1tina (\xc4\x8c\x65sk\xc3\xa1 republika)\0" + "CZ\0" + "da-DK\0" + "Danish (Denmark)\0" + "dansk (Danmark)\0" + "DK\0" + "de-DE\0" + "German (Germany)\0" + "Deutsch (Deutschland)\0" + "DE\0" + "el-GR\0" + "Greek (Greece)\0" + "\xce\x95\xce\xbb\xce\xbb\xce\xb7\xce\xbd\xce\xb9\xce\xba\xce\xac (\xce\x95\xce\xbb\xce\xbb\xce\xac\xce\xb4\xce\xb1)\0" + "GR\0" + "en-US\0" + "English (United States)\0" + "US\0" + "fi-FI\0" + "Finnish (Finland)\0" + "suomi (Suomi)\0" + "FI\0" + "fr-FR\0" + "French (France)\0" + "fran\xc3\xa7\x61is (France)\0" + "FR\0" + "he-IL\0" + "Hebrew (Israel)\0" + "\xd7\xa2\xd7\x91\xd7\xa8\xd7\x99\xd7\xaa (\xd7\x99\xd7\xa9\xd7\xa8\xd7\x90\xd7\x9c)\0" + "IL\0" + "hu-HU\0" + "Hungarian (Hungary)\0" + "magyar (Magyarorsz\xc3\xa1g)\0" + "HU\0" + "is-IS\0" + "Icelandic (Iceland)\0" + "\xc3\xadslenska (\xc3\x8dsland)\0" + "IS\0" + "it-IT\0" + "Italian (Italy)\0" + "italiano (Italia)\0" + "IT\0" + "ja-JP\0" + "Japanese (Japan)\0" + "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e (\xe6\x97\xa5\xe6\x9c\xac)\0" + "JP\0" + "ko-KR\0" + "Korean (South Korea)\0" + "\xed\x95\x9c\xea\xb5\xad\xec\x96\xb4 (\xeb\x8c\x80\xed\x95\x9c\xeb\xaf\xbc\xea\xb5\xad)\0" + "KR\0" + "nl-NL\0" + "Dutch (Netherlands)\0" + "Nederlands (Nederland)\0" + "NL\0" + "nb-NO\0" + "Norwegian Bokm\xc3\xa5l (Norway)\0" + "norsk bokm\xc3\xa5l (Norge)\0" + "NO\0" + "pl-PL\0" + "Polish (Poland)\0" + "polski (Polska)\0" + "PL\0" + "pt-BR\0" + "Portuguese (Brazil)\0" + "portugu\xc3\xaas (Brasil)\0" + "BR\0" + "rm-CH\0" + "Romansh (Switzerland)\0" + "rumantsch (Svizra)\0" + "ro-RO\0" + "Romanian (Romania)\0" + "rom\xc3\xa2n\xc4\x83 (Rom\xc3\xa2nia)\0" + "RO\0" + "ru-RU\0" + "Russian (Russia)\0" + "\xd1\x80\xd1\x83\xd1\x81\xd1\x81\xd0\xba\xd0\xb8\xd0\xb9 (\xd0\xa0\xd0\xbe\xd1\x81\xd1\x81\xd0\xb8\xd1\x8f)\0" + "RU\0" + "hr-HR\0" + "Croatian (Croatia)\0" + "hrvatski (Hrvatska)\0" + "HR\0" + "sk-SK\0" + "Slovak (Slovakia)\0" + "sloven\xc4\x8dina (Slovensko)\0" + "SK\0" + "sq-AL\0" + "Albanian (Albania)\0" + "shqip (Shqip\xc3\xabri)\0" + "AL\0" + "sv-SE\0" + "Swedish (Sweden)\0" + "svenska (Sverige)\0" + "SE\0" + "th-TH\0" + "Thai (Thailand)\0" + "\xe0\xb9\x84\xe0\xb8\x97\xe0\xb8\xa2 (\xe0\xb9\x84\xe0\xb8\x97\xe0\xb8\xa2)\0" + "TH\0" + "tr-TR\0" + "Turkish (Turkey)\0" + "T\xc3\xbcrk\xc3\xa7\x65 (T\xc3\xbcrkiye)\0" + "TR\0" + "ur-PK\0" + "Urdu (Pakistan)\0" + "\xd8\xa7\xd8\xb1\xd8\xaf\xd9\x88 (\xd9\xbe\xd8\xa7\xda\xa9\xd8\xb3\xd8\xaa\xd8\xa7\xd9\x86)\0" + "PK\0" + "id-ID\0" + "Indonesian (Indonesia)\0" + "Indonesia (Indonesia)\0" + "ID\0" + "uk-UA\0" + "Ukrainian (Ukraine)\0" + "\xd1\x83\xd0\xba\xd1\x80\xd0\xb0\xd1\x97\xd0\xbd\xd1\x81\xd1\x8c\xd0\xba\xd0\xb0 (\xd0\xa3\xd0\xba\xd1\x80\xd0\xb0\xd1\x97\xd0\xbd\xd0\xb0)\0" + "UA\0" + "be-BY\0" + "Belarusian (Belarus)\0" + "\xd0\xb1\xd0\xb5\xd0\xbb\xd0\xb0\xd1\x80\xd1\x83\xd1\x81\xd0\xba\xd0\xb0\xd1\x8f (\xd0\x91\xd0\xb5\xd0\xbb\xd0\xb0\xd1\x80\xd1\x83\xd1\x81\xd1\x8c)\0" + "BY\0" + "sl-SI\0" + "Slovenian (Slovenia)\0" + "sloven\xc5\xa1\xc4\x8dina (Slovenija)\0" + "SI\0" + "et-EE\0" + "Estonian (Estonia)\0" + "eesti (Eesti)\0" + "EE\0" + "lv-LV\0" + "Latvian (Latvia)\0" + "latvie\xc5\xa1u (Latvija)\0" + "LV\0" + "lt-LT\0" + "Lithuanian (Lithuania)\0" + "lietuvi\xc5\xb3 (Lietuva)\0" + "LT\0" + "tg-Cyrl-TJ\0" + "Tajik (Cyrillic, Tajikistan)\0" + "\xd0\xa2\xd0\xbe\xd2\xb7\xd0\xb8\xd0\xba\xd3\xa3 (\xd0\xa2\xd0\xbe\xd2\xb7\xd0\xb8\xd0\xba\xd0\xb8\xd1\x81\xd1\x82\xd0\xbe\xd0\xbd)\0" + "TJ\0" + "fa-IR\0" + "Persian (Iran)\0" + "\xd9\x81\xd8\xa7\xd8\xb1\xd8\xb3\xdb\x8c (\xd8\xa7\xdb\x8c\xd8\xb1\xd8\xa7\xd9\x86)\0" + "IR\0" + "vi-VN\0" + "Vietnamese (Vietnam)\0" + "Ti\xe1\xba\xbfng Vi\xe1\xbb\x87t (Vi\xe1\xbb\x87t Nam)\0" + "VN\0" + "hy-AM\0" + "Armenian (Armenia)\0" + "\xd5\xb0\xd5\xa1\xd5\xb5\xd5\xa5\xd6\x80\xd5\xa5\xd5\xb6 (\xd5\x80\xd5\xa1\xd5\xb5\xd5\xa1\xd5\xbd\xd5\xbf\xd5\xa1\xd5\xb6)\0" + "az-Latn-AZ\0" + "Azerbaijani (Latin, Azerbaijan)\0" + "az\xc9\x99rbaycan (Az\xc9\x99rbaycan)\0" + "AZ\0" + "eu-ES\0" + "Basque (Spain)\0" + "euskara (Espainia)\0" + "hsb-DE\0" + "Upper Sorbian (Germany)\0" + "hornjoserb\xc5\xa1\xc4\x87ina (N\xc4\x9bmska)\0" + "mk-MK\0" + "Macedonian (Macedonia)\0" + "\xd0\xbc\xd0\xb0\xd0\xba\xd0\xb5\xd0\xb4\xd0\xbe\xd0\xbd\xd1\x81\xd0\xba\xd0\xb8 (\xd0\x9c\xd0\xb0\xd0\xba\xd0\xb5\xd0\xb4\xd0\xbe\xd0\xbd\xd0\xb8\xd1\x98\xd0\xb0)\0" + "MK\0" + "st-ZA\0" + "Southern Sotho (South Africa)\0" + "ZA\0" + "ts-ZA\0" + "Tsonga (South Africa)\0" + "tn-ZA\0" + "Tswana (South Africa)\0" + "xh-ZA\0" + "Xhosa (South Africa)\0" + "zu-ZA\0" + "Zulu (South Africa)\0" + "isiZulu (i-South Africa)\0" + "af-ZA\0" + "Afrikaans (South Africa)\0" + "Afrikaans (Suid-Afrika)\0" + "ka-GE\0" + "Georgian (Georgia)\0" + "\xe1\x83\xa5\xe1\x83\x90\xe1\x83\xa0\xe1\x83\x97\xe1\x83\xa3\xe1\x83\x9a\xe1\x83\x98 (\xe1\x83\xa1\xe1\x83\x90\xe1\x83\xa5\xe1\x83\x90\xe1\x83\xa0\xe1\x83\x97\xe1\x83\x95\xe1\x83\x94\xe1\x83\x9a\xe1\x83\x9d)\0" + "GE\0" + "fo-FO\0" + "Faroese (Faroe Islands)\0" + "f\xc3\xb8royskt (F\xc3\xb8royar)\0" + "FO\0" + "hi-IN\0" + "Hindi (India)\0" + "\xe0\xa4\xb9\xe0\xa4\xbf\xe0\xa4\xa8\xe0\xa5\x8d\xe0\xa4\xa6\xe0\xa5\x80 (\xe0\xa4\xad\xe0\xa4\xbe\xe0\xa4\xb0\xe0\xa4\xa4)\0" + "IN\0" + "mt-MT\0" + "Maltese (Malta)\0" + "Malti (Malta)\0" + "MT\0" + "se-NO\0" + "Northern Sami (Norway)\0" + "davvis\xc3\xa1megiella (Norga)\0" + "ms-MY\0" + "Malay (Malaysia)\0" + "Bahasa Melayu (Malaysia)\0" + "MY\0" + "kk-KZ\0" + "Kazakh (Kazakhstan)\0" + "\xd2\x9b\xd0\xb0\xd0\xb7\xd0\xb0\xd2\x9b \xd1\x82\xd1\x96\xd0\xbb\xd1\x96 (\xd2\x9a\xd0\xb0\xd0\xb7\xd0\xb0\xd2\x9b\xd1\x81\xd1\x82\xd0\xb0\xd0\xbd)\0" + "KZ\0" + "ky-KG\0" + "Kyrgyz (Kyrgyzstan)\0" + "\xd0\xba\xd1\x8b\xd1\x80\xd0\xb3\xd1\x8b\xd0\xb7\xd1\x87\xd0\xb0 (\xd0\x9a\xd1\x8b\xd1\x80\xd0\xb3\xd1\x8b\xd0\xb7\xd1\x81\xd1\x82\xd0\xb0\xd0\xbd)\0" + "KG\0" + "sw-KE\0" + "Swahili (Kenya)\0" + "Kiswahili (Kenya)\0" + "KE\0" + "tk-TM\0" + "Turkmen (Turkmenistan)\0" + "t\xc3\xbcrkmen\xc3\xa7\x65 (T\xc3\xbcrkmenistan)\0" + "TM\0" + "uz-Latn-UZ\0" + "Uzbek (Latin, Uzbekistan)\0" + "o\xe2\x80\x98zbek (O\xca\xbbzbekiston)\0" + "UZ\0" + "bn-IN\0" + "Bangla (India)\0" + "\xe0\xa6\xac\xe0\xa6\xbe\xe0\xa6\x82\xe0\xa6\xb2\xe0\xa6\xbe (\xe0\xa6\xad\xe0\xa6\xbe\xe0\xa6\xb0\xe0\xa6\xa4)\0" + "gu-IN\0" + "Gujarati (India)\0" + "\xe0\xaa\x97\xe0\xab\x81\xe0\xaa\x9c\xe0\xaa\xb0\xe0\xaa\xbe\xe0\xaa\xa4\xe0\xab\x80 (\xe0\xaa\xad\xe0\xaa\xbe\xe0\xaa\xb0\xe0\xaa\xa4)\0" + "or-IN\0" + "Odia (India)\0" + "\xe0\xac\x93\xe0\xac\xa1\xe0\xac\xbc\xe0\xac\xbf\xe0\xac\x86 (\xe0\xac\xad\xe0\xac\xbe\xe0\xac\xb0\xe0\xac\xa4)\0" + "ta-IN\0" + "Tamil (India)\0" + "\xe0\xae\xa4\xe0\xae\xae\xe0\xae\xbf\xe0\xae\xb4\xe0\xaf\x8d (\xe0\xae\x87\xe0\xae\xa8\xe0\xaf\x8d\xe0\xae\xa4\xe0\xae\xbf\xe0\xae\xaf\xe0\xae\xbe)\0" + "te-IN\0" + "Telugu (India)\0" + "\xe0\xb0\xa4\xe0\xb1\x86\xe0\xb0\xb2\xe0\xb1\x81\xe0\xb0\x97\xe0\xb1\x81 (\xe0\xb0\xad\xe0\xb0\xbe\xe0\xb0\xb0\xe0\xb0\xa4 \xe0\xb0\xa6\xe0\xb1\x87\xe0\xb0\xb6\xe0\xb0\x82)\0" + "kn-IN\0" + "Kannada (India)\0" + "\xe0\xb2\x95\xe0\xb2\xa8\xe0\xb3\x8d\xe0\xb2\xa8\xe0\xb2\xa1 (\xe0\xb2\xad\xe0\xb2\xbe\xe0\xb2\xb0\xe0\xb2\xa4)\0" + "ml-IN\0" + "Malayalam (India)\0" + "\xe0\xb4\xae\xe0\xb4\xb2\xe0\xb4\xaf\xe0\xb4\xbe\xe0\xb4\xb3\xe0\xb4\x82 (\xe0\xb4\x87\xe0\xb4\xa8\xe0\xb5\x8d\xe0\xb4\xa4\xe0\xb5\x8d\xe0\xb4\xaf)\0" + "as-IN\0" + "Assamese (India)\0" + "\xe0\xa6\x85\xe0\xa6\xb8\xe0\xa6\xae\xe0\xa7\x80\xe0\xa6\xaf\xe0\xa6\xbc\xe0\xa6\xbe (\xe0\xa6\xad\xe0\xa6\xbe\xe0\xa7\xb0\xe0\xa6\xa4)\0" + "mr-IN\0" + "Marathi (India)\0" + "\xe0\xa4\xae\xe0\xa4\xb0\xe0\xa4\xbe\xe0\xa4\xa0\xe0\xa5\x80 (\xe0\xa4\xad\xe0\xa4\xbe\xe0\xa4\xb0\xe0\xa4\xa4)\0" + "mn-MN\0" + "Mongolian (Mongolia)\0" + "\xd0\xbc\xd0\xbe\xd0\xbd\xd0\xb3\xd0\xbe\xd0\xbb (\xd0\x9c\xd0\xbe\xd0\xbd\xd0\xb3\xd0\xbe\xd0\xbb)\0" + "MNN\0" + "MN\0" + "bo-CN\0" + "Tibetan (China)\0" + "\xe0\xbd\x96\xe0\xbd\xbc\xe0\xbd\x91\xe0\xbc\x8b\xe0\xbd\xa6\xe0\xbe\x90\xe0\xbd\x91\xe0\xbc\x8b (\xe0\xbd\xa2\xe0\xbe\x92\xe0\xbe\xb1\xe0\xbc\x8b\xe0\xbd\x93\xe0\xbd\x82)\0" + "CN\0" + "cy-GB\0" + "Welsh (United Kingdom)\0" + "Cymraeg (Y Deyrnas Unedig)\0" + "GB\0" + "km-KH\0" + "Khmer (Cambodia)\0" + "\xe1\x9e\x81\xe1\x9f\x92\xe1\x9e\x98\xe1\x9f\x82\xe1\x9e\x9a (\xe1\x9e\x80\xe1\x9e\x98\xe1\x9f\x92\xe1\x9e\x96\xe1\x9e\xbb\xe1\x9e\x87\xe1\x9e\xb6)\0" + "KH\0" + "lo-LA\0" + "Lao (Laos)\0" + "\xe0\xba\xa5\xe0\xba\xb2\xe0\xba\xa7 (\xe0\xba\xa5\xe0\xba\xb2\xe0\xba\xa7)\0" + "LA\0" + "my-MM\0" + "Burmese (Myanmar (Burma))\0" + "\xe1\x80\x99\xe1\x80\xbc\xe1\x80\x94\xe1\x80\xba\xe1\x80\x99\xe1\x80\xac (\xe1\x80\x99\xe1\x80\xbc\xe1\x80\x94\xe1\x80\xba\xe1\x80\x99\xe1\x80\xac)\0" + "MM\0" + "gl-ES\0" + "Galician (Spain)\0" + "galego (Espa\xc3\xb1\x61)\0" + "kok-IN\0" + "Konkani (India)\0" + "\xe0\xa4\x95\xe0\xa5\x8b\xe0\xa4\x82\xe0\xa4\x95\xe0\xa4\xa3\xe0\xa5\x80 (\xe0\xa4\xad\xe0\xa4\xbe\xe0\xa4\xb0\xe0\xa4\xa4)\0" + "si-LK\0" + "Sinhala (Sri Lanka)\0" + "\xe0\xb7\x83\xe0\xb7\x92\xe0\xb6\x82\xe0\xb7\x84\xe0\xb6\xbd (\xe0\xb7\x81\xe0\xb7\x8a\xe2\x80\x8d\xe0\xb6\xbb\xe0\xb7\x93 \xe0\xb6\xbd\xe0\xb6\x82\xe0\xb6\x9a\xe0\xb7\x8f\xe0\xb7\x80)\0" + "LK\0" + "am-ET\0" + "Amharic (Ethiopia)\0" + "\xe1\x8a\xa0\xe1\x88\x9b\xe1\x88\xad\xe1\x8a\x9b (\xe1\x8a\xa2\xe1\x89\xb5\xe1\x8b\xae\xe1\x8c\xb5\xe1\x8b\xab)\0" + "ET\0" + "ne-NP\0" + "Nepali (Nepal)\0" + "\xe0\xa4\xa8\xe0\xa5\x87\xe0\xa4\xaa\xe0\xa4\xbe\xe0\xa4\xb2\xe0\xa5\x80 (\xe0\xa4\xa8\xe0\xa5\x87\xe0\xa4\xaa\xe0\xa4\xbe\xe0\xa4\xb2)\0" + "NP\0" + "fy-NL\0" + "Western Frisian (Netherlands)\0" + "West-Frysk (Nederl\xc3\xa2n)\0" + "ps-AF\0" + "Pashto (Afghanistan)\0" + "\xd9\xbe\xda\x9a\xd8\xaa\xd9\x88 (\xd8\xa7\xd9\x81\xd8\xba\xd8\xa7\xd9\x86\xd8\xb3\xd8\xaa\xd8\xa7\xd9\x86)\0" + "AF\0" + "fil-PH\0" + "Filipino (Philippines)\0" + "Filipino (Pilipinas)\0" + "PH\0" + "ha-Latn-NG\0" + "Hausa (Latin, Nigeria)\0" + "Hausa (Najeriya)\0" + "NG\0" + "yo-NG\0" + "Yoruba (Nigeria)\0" + "\xc3\x88\x64\xc3\xa8 Yor\xc3\xb9\x62\xc3\xa1 (Or\xc3\xadl\xe1\xba\xb9\xcc\x81\xc3\xa8\x64\x65 N\xc3\xa0\xc3\xacj\xc3\xadr\xc3\xad\xc3\xa0)\0" + "nso-ZA\0" + "Northern Sotho (South Africa)\0" + "lb-LU\0" + "Luxembourgish (Luxembourg)\0" + "L\xc3\xabtzebuergesch (L\xc3\xabtzebuerg)\0" + "LU\0" + "kl-GL\0" + "Kalaallisut (Greenland)\0" + "kalaallisut (Kalaallit Nunaat)\0" + "GL\0" + "ig-NG\0" + "Igbo (Nigeria)\0" + "om-ET\0" + "Oromo (Ethiopia)\0" + "Oromoo (Itoophiyaa)\0" + "ti-ET\0" + "Tigrinya (Ethiopia)\0" + "\xe1\x89\xb5\xe1\x8c\x8d\xe1\x88\xad\xe1\x8a\x9b (\xe1\x8a\xa2\xe1\x89\xb5\xe1\x8b\xae\xe1\x8c\xb5\xe1\x8b\xab)\0" + "TIE\0" + "haw-US\0" + "Hawaiian (United States)\0" + "\xca\xbb\xc5\x8clelo Hawai\xca\xbbi (\xca\xbb\x41melika Hui P\xc5\xab \xca\xbbIa)\0" + "so-SO\0" + "Somali (Somalia)\0" + "Soomaali (Soomaaliya)\0" + "SO\0" + "ii-CN\0" + "Sichuan Yi (China)\0" + "\xea\x86\x88\xea\x8c\xa0\xea\x89\x99 (\xea\x8d\x8f\xea\x87\xa9)\0" + "br-FR\0" + "Breton (France)\0" + "brezhoneg (Fra\xc3\xb1s)\0" + "ug-CN\0" + "Uyghur (China)\0" + "\xd8\xa6\xdb\x87\xd9\x8a\xd8\xba\xdb\x87\xd8\xb1\xda\x86\xdb\x95 (\xd8\xac\xdb\x87\xda\xad\xda\xaf\xd9\x88)\0" + "gsw-FR\0" + "Swiss German (France)\0" + "Schwiizert\xc3\xbc\xc3\xbctsch (Frankriich)\0" + "sah-RU\0" + "Sakha (Russia)\0" + "\xd1\x81\xd0\xb0\xd1\x85\xd0\xb0 \xd1\x82\xd1\x8b\xd0\xbb\xd0\xb0 (\xd0\x90\xd1\x80\xd0\xb0\xd1\x81\xd1\x81\xd1\x8b\xd1\x8b\xd0\xb9\xd0\xb0)\0" + "rw-RW\0" + "Kinyarwanda (Rwanda)\0" + "RW\0" + "gd-GB\0" + "Scottish Gaelic (United Kingdom)\0" + "G\xc3\xa0idhlig (An R\xc3\xacoghachd Aonaichte)\0" + "ar-IQ\0" + "Arabic (Iraq)\0" + "\xd8\xa7\xd9\x84\xd8\xb9\xd8\xb1\xd8\xa8\xd9\x8a\xd8\xa9 (\xd8\xa7\xd9\x84\xd8\xb9\xd8\xb1\xd8\xa7\xd9\x82)\0" + "ARI\0" + "IQ\0" + "ca-ES-valencia\0" + "VAL\0" + "zh-CN\0" + "\xe4\xb8\xad\xe6\x96\x87 (\xe4\xb8\xad\xe5\x9b\xbd)\0" + "de-CH\0" + "German (Switzerland)\0" + "Deutsch (Schweiz)\0" + "DES\0" + "en-GB\0" + "English (United Kingdom)\0" + "ENG\0" + "es-MX\0" + "Spanish (Mexico)\0" + "espa\xc3\xb1ol (M\xc3\xa9xico)\0" + "ESM\0" + "MX\0" + "fr-BE\0" + "French (Belgium)\0" + "fran\xc3\xa7\x61is (Belgique)\0" + "FRB\0" + "BE\0" + "it-CH\0" + "Italian (Switzerland)\0" + "italiano (Svizzera)\0" + "ITS\0" + "nl-BE\0" + "Dutch (Belgium)\0" + "Nederlands (Belgi\xc3\xab)\0" + "NLB\0" + "nn-NO\0" + "Norwegian Nynorsk (Norway)\0" + "nynorsk (Noreg)\0" + "NON\0" + "nno\0" + "nn\0" + "pt-PT\0" + "Portuguese (Portugal)\0" + "portugu\xc3\xaas (Portugal)\0" + "PT\0" + "ro-MD\0" + "Romanian (Moldova)\0" + "rom\xc3\xa2n\xc4\x83 (Republica Moldova)\0" + "ROD\0" + "MD\0" + "ru-MD\0" + "Russian (Moldova)\0" + "\xd1\x80\xd1\x83\xd1\x81\xd1\x81\xd0\xba\xd0\xb8\xd0\xb9 (\xd0\x9c\xd0\xbe\xd0\xbb\xd0\xb4\xd0\xbe\xd0\xb2\xd0\xb0)\0" + "RUM\0" + "sv-FI\0" + "Swedish (Finland)\0" + "svenska (Finland)\0" + "SVF\0" + "ur-IN\0" + "Urdu (India)\0" + "\xd8\xa7\xd8\xb1\xd8\xaf\xd9\x88 (\xd8\xa8\xda\xbe\xd8\xa7\xd8\xb1\xd8\xaa)\0" + "URI\0" + "az-Cyrl-AZ\0" + "Azerbaijani (Cyrillic, Azerbaijan)\0" + "AZC\0" + "dsb-DE\0" + "Lower Sorbian (Germany)\0" + "dolnoserb\xc5\xa1\xc4\x87ina (Nimska)\0" + "DSB\0" + "dsb\0" + "tn-BW\0" + "Tswana (Botswana)\0" + "TSB\0" + "BW\0" + "se-SE\0" + "Northern Sami (Sweden)\0" + "davvis\xc3\xa1megiella (Ruo\xc5\xa7\xc5\xa7\x61)\0" + "SMF\0" + "ga-IE\0" + "Irish (Ireland)\0" + "Gaeilge (\xc3\x89ire)\0" + "IE\0" + "ms-BN\0" + "Malay (Brunei)\0" + "Bahasa Melayu (Brunei)\0" + "MSB\0" + "BN\0" + "uz-Cyrl-UZ\0" + "Uzbek (Cyrillic, Uzbekistan)\0" + "UZC\0" + "bn-BD\0" + "Bangla (Bangladesh)\0" + "\xe0\xa6\xac\xe0\xa6\xbe\xe0\xa6\x82\xe0\xa6\xb2\xe0\xa6\xbe (\xe0\xa6\xac\xe0\xa6\xbe\xe0\xa6\x82\xe0\xa6\xb2\xe0\xa6\xbe\xe0\xa6\xa6\xe0\xa7\x87\xe0\xa6\xb6)\0" + "BNB\0" + "BD\0" + "pa-Arab-PK\0" + "Punjabi (Arabic, Pakistan)\0" + "\xe0\xa8\xaa\xe0\xa9\xb0\xe0\xa8\x9c\xe0\xa8\xbe\xe0\xa8\xac\xe0\xa9\x80 (\xe0\xa8\xaa\xe0\xa8\xbe\xe0\xa8\x95\xe0\xa8\xbf\xe0\xa8\xb8\xe0\xa8\xa4\xe0\xa8\xbe\xe0\xa8\xa8)\0" + "PAP\0" + "ta-LK\0" + "Tamil (Sri Lanka)\0" + "\xe0\xae\xa4\xe0\xae\xae\xe0\xae\xbf\xe0\xae\xb4\xe0\xaf\x8d (\xe0\xae\x87\xe0\xae\xb2\xe0\xae\x99\xe0\xaf\x8d\xe0\xae\x95\xe0\xaf\x88)\0" + "TAM\0" + "ne-IN\0" + "Nepali (India)\0" + "\xe0\xa4\xa8\xe0\xa5\x87\xe0\xa4\xaa\xe0\xa4\xbe\xe0\xa4\xb2\xe0\xa5\x80 (\xe0\xa4\xad\xe0\xa4\xbe\xe0\xa4\xb0\xe0\xa4\xa4)\0" + "NEI\0" + "ti-ER\0" + "Tigrinya (Eritrea)\0" + "\xe1\x89\xb5\xe1\x8c\x8d\xe1\x88\xad\xe1\x8a\x9b (\xe1\x8a\xa4\xe1\x88\xad\xe1\x89\xb5\xe1\x88\xab)\0" + "ER\0" + "ar-EG\0" + "Arabic (Egypt)\0" + "\xd8\xa7\xd9\x84\xd8\xb9\xd8\xb1\xd8\xa8\xd9\x8a\xd8\xa9 (\xd9\x85\xd8\xb5\xd8\xb1)\0" + "ARE\0" + "EG\0" + "zh-HK\0" + "Chinese (Traditional, Hong Kong SAR China)\0" + "\xe4\xb8\xad\xe6\x96\x87 (\xe4\xb8\xad\xe5\x9b\xbd\xe9\xa6\x99\xe6\xb8\xaf\xe7\x89\xb9\xe5\x88\xab\xe8\xa1\x8c\xe6\x94\xbf\xe5\x8c\xba)\0" + "ZHH\0" + "HK\0" + "de-AT\0" + "German (Austria)\0" + "Deutsch (\xc3\x96sterreich)\0" + "DEA\0" + "AT\0" + "en-AU\0" + "English (Australia)\0" + "ENA\0" + "AU\0" + "es-ES\0" + "Spanish (Spain)\0" + "espa\xc3\xb1ol (Espa\xc3\xb1\x61)\0" + "ESN\0" + "fr-CA\0" + "French (Canada)\0" + "fran\xc3\xa7\x61is (Canada)\0" + "FRC\0" + "CA\0" + "se-FI\0" + "Northern Sami (Finland)\0" + "davvis\xc3\xa1megiella (Suopma)\0" + "SMG\0" + "ar-LY\0" + "Arabic (Libya)\0" + "\xd8\xa7\xd9\x84\xd8\xb9\xd8\xb1\xd8\xa8\xd9\x8a\xd8\xa9 (\xd9\x84\xd9\x8a\xd8\xa8\xd9\x8a\xd8\xa7)\0" + "ARL\0" + "LY\0" + "zh-SG\0" + "Chinese (Simplified, Singapore)\0" + "\xe4\xb8\xad\xe6\x96\x87 (\xe6\x96\xb0\xe5\x8a\xa0\xe5\x9d\xa1)\0" + "ZHI\0" + "SG\0" + "de-LU\0" + "German (Luxembourg)\0" + "Deutsch (Luxemburg)\0" + "DEL\0" + "en-CA\0" + "English (Canada)\0" + "ENC\0" + "es-GT\0" + "Spanish (Guatemala)\0" + "espa\xc3\xb1ol (Guatemala)\0" + "ESG\0" + "GT\0" + "fr-CH\0" + "French (Switzerland)\0" + "fran\xc3\xa7\x61is (Suisse)\0" + "FRS\0" + "hr-BA\0" + "Croatian (Bosnia & Herzegovina)\0" + "hrvatski (Bosna i Hercegovina)\0" + "HRB\0" + "BA\0" + "ar-DZ\0" + "Arabic (Algeria)\0" + "\xd8\xa7\xd9\x84\xd8\xb9\xd8\xb1\xd8\xa8\xd9\x8a\xd8\xa9 (\xd8\xa7\xd9\x84\xd8\xac\xd8\xb2\xd8\xa7\xd8\xa6\xd8\xb1)\0" + "ARG\0" + "DZ\0" + "zh-MO\0" + "Chinese (Traditional, Macau SAR China)\0" + "\xe4\xb8\xad\xe6\x96\x87 (\xe4\xb8\xad\xe5\x9b\xbd\xe6\xbe\xb3\xe9\x97\xa8\xe7\x89\xb9\xe5\x88\xab\xe8\xa1\x8c\xe6\x94\xbf\xe5\x8c\xba)\0" + "ZHM\0" + "MO\0" + "de-LI\0" + "German (Liechtenstein)\0" + "Deutsch (Liechtenstein)\0" + "DEC\0" + "LI\0" + "en-NZ\0" + "English (New Zealand)\0" + "ENZ\0" + "NZ\0" + "es-CR\0" + "Spanish (Costa Rica)\0" + "espa\xc3\xb1ol (Costa Rica)\0" + "ESC\0" + "CR\0" + "fr-LU\0" + "French (Luxembourg)\0" + "fran\xc3\xa7\x61is (Luxembourg)\0" + "FRL\0" + "bs-Latn-BA\0" + "Bosnian (Latin, Bosnia & Herzegovina)\0" + "bosanski (Bosna i Hercegovina)\0" + "BSB\0" + "bos\0" + "bs\0" + "ar-MA\0" + "Arabic (Morocco)\0" + "\xd8\xa7\xd9\x84\xd8\xb9\xd8\xb1\xd8\xa8\xd9\x8a\xd8\xa9 (\xd8\xa7\xd9\x84\xd9\x85\xd8\xba\xd8\xb1\xd8\xa8)\0" + "ARM\0" + "MA\0" + "en-IE\0" + "English (Ireland)\0" + "ENI\0" + "es-PA\0" + "Spanish (Panama)\0" + "espa\xc3\xb1ol (Panam\xc3\xa1)\0" + "ESA\0" + "PA\0" + "fr-MC\0" + "French (Monaco)\0" + "fran\xc3\xa7\x61is (Monaco)\0" + "FRM\0" + "MC\0" + "sr-Latn-BA\0" + "Serbian (Latin, Bosnia & Herzegovina)\0" + "\xd1\x81\xd1\x80\xd0\xbf\xd1\x81\xd0\xba\xd0\xb8 (\xd0\x91\xd0\xbe\xd1\x81\xd0\xbd\xd0\xb0 \xd0\xb8 \xd0\xa5\xd0\xb5\xd1\x80\xd1\x86\xd0\xb5\xd0\xb3\xd0\xbe\xd0\xb2\xd0\xb8\xd0\xbd\xd0\xb0)\0" + "SRS\0" + "srp\0" + "sr\0" + "ar-TN\0" + "Arabic (Tunisia)\0" + "\xd8\xa7\xd9\x84\xd8\xb9\xd8\xb1\xd8\xa8\xd9\x8a\xd8\xa9 (\xd8\xaa\xd9\x88\xd9\x86\xd8\xb3)\0" + "ART\0" + "TN\0" + "en-ZA\0" + "English (South Africa)\0" + "ENS\0" + "es-DO\0" + "Spanish (Dominican Republic)\0" + "espa\xc3\xb1ol (Rep\xc3\xba\x62lica Dominicana)\0" + "ESD\0" + "DO\0" + "sr-Cyrl-BA\0" + "Serbian (Cyrillic, Bosnia & Herzegovina)\0" + "SRN\0" + "ar-OM\0" + "Arabic (Oman)\0" + "\xd8\xa7\xd9\x84\xd8\xb9\xd8\xb1\xd8\xa8\xd9\x8a\xd8\xa9 (\xd8\xb9\xd9\x8f\xd9\x85\xd8\xa7\xd9\x86)\0" + "ARO\0" + "OM\0" + "en-JM\0" + "English (Jamaica)\0" + "ENJ\0" + "JM\0" + "es-VE\0" + "Spanish (Venezuela)\0" + "espa\xc3\xb1ol (Venezuela)\0" + "ESV\0" + "VE\0" + "fr-RE\0" + "French (R\xc3\xa9union)\0" + "fran\xc3\xa7\x61is (La R\xc3\xa9union)\0" + "FRR\0" + "RE\0" + "bs-Cyrl-BA\0" + "Bosnian (Cyrillic, Bosnia & Herzegovina)\0" + "BSC\0" + "ar-YE\0" + "Arabic (Yemen)\0" + "\xd8\xa7\xd9\x84\xd8\xb9\xd8\xb1\xd8\xa8\xd9\x8a\xd8\xa9 (\xd8\xa7\xd9\x84\xd9\x8a\xd9\x85\xd9\x86)\0" + "ARY\0" + "YE\0" + "es-CO\0" + "Spanish (Colombia)\0" + "espa\xc3\xb1ol (Colombia)\0" + "ESO\0" + "CO\0" + "fr-CD\0" + "French (Congo - Kinshasa)\0" + "fran\xc3\xa7\x61is (Congo-Kinshasa)\0" + "FRD\0" + "CD\0" + "sr-Latn-RS\0" + "Serbian (Latin, Serbia)\0" + "\xd1\x81\xd1\x80\xd0\xbf\xd1\x81\xd0\xba\xd0\xb8 (\xd0\xa1\xd1\x80\xd0\xb1\xd0\xb8\xd1\x98\xd0\xb0)\0" + "SRM\0" + "RS\0" + "smn-FI\0" + "Inari Sami (Finland)\0" + "anar\xc3\xa2\xc5\xa1kiel\xc3\xa2 (Suom\xc3\xa2)\0" + "SMN\0" + "smn\0" + "ar-SY\0" + "Arabic (Syria)\0" + "\xd8\xa7\xd9\x84\xd8\xb9\xd8\xb1\xd8\xa8\xd9\x8a\xd8\xa9 (\xd8\xb3\xd9\x88\xd8\xb1\xd9\x8a\xd8\xa7)\0" + "ARS\0" + "SY\0" + "en-BZ\0" + "English (Belize)\0" + "ENL\0" + "BZ\0" + "es-PE\0" + "Spanish (Peru)\0" + "espa\xc3\xb1ol (Per\xc3\xba)\0" + "ESR\0" + "PE\0" + "fr-SN\0" + "French (Senegal)\0" + "fran\xc3\xa7\x61is (S\xc3\xa9n\xc3\xa9gal)\0" + "FRN\0" + "SN\0" + "sr-Cyrl-RS\0" + "Serbian (Cyrillic, Serbia)\0" + "SRO\0" + "ar-JO\0" + "Arabic (Jordan)\0" + "\xd8\xa7\xd9\x84\xd8\xb9\xd8\xb1\xd8\xa8\xd9\x8a\xd8\xa9 (\xd8\xa7\xd9\x84\xd8\xa3\xd8\xb1\xd8\xaf\xd9\x86)\0" + "ARJ\0" + "JO\0" + "en-TT\0" + "English (Trinidad & Tobago)\0" + "ENT\0" + "TT\0" + "es-AR\0" + "Spanish (Argentina)\0" + "espa\xc3\xb1ol (Argentina)\0" + "ESS\0" + "AR\0" + "fr-CM\0" + "French (Cameroon)\0" + "fran\xc3\xa7\x61is (Cameroun)\0" + "FRE\0" + "CM\0" + "sr-Latn-ME\0" + "Serbian (Latin, Montenegro)\0" + "\xd1\x81\xd1\x80\xd0\xbf\xd1\x81\xd0\xba\xd0\xb8 (\xd0\xa6\xd1\x80\xd0\xbd\xd0\xb0 \xd0\x93\xd0\xbe\xd1\x80\xd0\xb0)\0" + "SRP\0" + "ME\0" + "ar-LB\0" + "Arabic (Lebanon)\0" + "\xd8\xa7\xd9\x84\xd8\xb9\xd8\xb1\xd8\xa8\xd9\x8a\xd8\xa9 (\xd9\x84\xd8\xa8\xd9\x86\xd8\xa7\xd9\x86)\0" + "ARB\0" + "LB\0" + "en-ZW\0" + "English (Zimbabwe)\0" + "ENW\0" + "ZW\0" + "es-EC\0" + "Spanish (Ecuador)\0" + "espa\xc3\xb1ol (Ecuador)\0" + "ESF\0" + "EC\0" + "fr-CI\0" + "French (C\xc3\xb4te d\xe2\x80\x99Ivoire)\0" + "fran\xc3\xa7\x61is (C\xc3\xb4te d\xe2\x80\x99Ivoire)\0" + "FRI\0" + "CI\0" + "sr-Cyrl-ME\0" + "Serbian (Cyrillic, Montenegro)\0" + "SRQ\0" + "ar-KW\0" + "Arabic (Kuwait)\0" + "\xd8\xa7\xd9\x84\xd8\xb9\xd8\xb1\xd8\xa8\xd9\x8a\xd8\xa9 (\xd8\xa7\xd9\x84\xd9\x83\xd9\x88\xd9\x8a\xd8\xaa)\0" + "ARK\0" + "KW\0" + "en-PH\0" + "English (Philippines)\0" + "ENP\0" + "es-CL\0" + "Spanish (Chile)\0" + "espa\xc3\xb1ol (Chile)\0" + "ESL\0" + "CL\0" + "fr-ML\0" + "French (Mali)\0" + "fran\xc3\xa7\x61is (Mali)\0" + "FRF\0" + "ML\0" + "ar-AE\0" + "Arabic (United Arab Emirates)\0" + "\xd8\xa7\xd9\x84\xd8\xb9\xd8\xb1\xd8\xa8\xd9\x8a\xd8\xa9 (\xd8\xa7\xd9\x84\xd8\xa5\xd9\x85\xd8\xa7\xd8\xb1\xd8\xa7\xd8\xaa \xd8\xa7\xd9\x84\xd8\xb9\xd8\xb1\xd8\xa8\xd9\x8a\xd8\xa9 \xd8\xa7\xd9\x84\xd9\x85\xd8\xaa\xd8\xad\xd8\xaf\xd8\xa9)\0" + "ARU\0" + "AE\0" + "es-UY\0" + "Spanish (Uruguay)\0" + "espa\xc3\xb1ol (Uruguay)\0" + "ESY\0" + "UY\0" + "fr-MA\0" + "French (Morocco)\0" + "fran\xc3\xa7\x61is (Maroc)\0" + "FRO\0" + "ar-BH\0" + "Arabic (Bahrain)\0" + "\xd8\xa7\xd9\x84\xd8\xb9\xd8\xb1\xd8\xa8\xd9\x8a\xd8\xa9 (\xd8\xa7\xd9\x84\xd8\xa8\xd8\xad\xd8\xb1\xd9\x8a\xd9\x86)\0" + "ARH\0" + "BH\0" + "en-HK\0" + "English (Hong Kong SAR China)\0" + "ENH\0" + "es-PY\0" + "Spanish (Paraguay)\0" + "espa\xc3\xb1ol (Paraguay)\0" + "ESZ\0" + "PY\0" + "fr-HT\0" + "French (Haiti)\0" + "fran\xc3\xa7\x61is (Ha\xc3\xafti)\0" + "FRH\0" + "HT\0" + "ar-QA\0" + "Arabic (Qatar)\0" + "\xd8\xa7\xd9\x84\xd8\xb9\xd8\xb1\xd8\xa8\xd9\x8a\xd8\xa9 (\xd9\x82\xd8\xb7\xd8\xb1)\0" + "ARQ\0" + "QA\0" + "en-IN\0" + "English (India)\0" + "ENN\0" + "es-BO\0" + "Spanish (Bolivia)\0" + "espa\xc3\xb1ol (Bolivia)\0" + "ESB\0" + "BO\0" + "en-MY\0" + "English (Malaysia)\0" + "ENM\0" + "es-SV\0" + "Spanish (El Salvador)\0" + "espa\xc3\xb1ol (El Salvador)\0" + "ESE\0" + "SV\0" + "en-SG\0" + "English (Singapore)\0" + "ENE\0" + "es-HN\0" + "Spanish (Honduras)\0" + "espa\xc3\xb1ol (Honduras)\0" + "ESH\0" + "HN\0" + "es-NI\0" + "Spanish (Nicaragua)\0" + "espa\xc3\xb1ol (Nicaragua)\0" + "ESI\0" + "NI\0" + "es-PR\0" + "Spanish (Puerto Rico)\0" + "espa\xc3\xb1ol (Puerto Rico)\0" + "ESU\0" + "PR\0" + "es-US\0" + "Spanish (United States)\0" + "espa\xc3\xb1ol (Estados Unidos)\0" + "EST\0" + "es-CU\0" + "Spanish (Cuba)\0" + "espa\xc3\xb1ol (Cuba)\0" + "ESK\0" + "CU\0" + "bs-Cyrl\0" + "Bosnian (Cyrillic)\0" + "bosanski\0" + "bs-Latn\0" + "Bosnian (Latin)\0" + "sr-Cyrl\0" + "Serbian (Cyrillic)\0" + "\xd1\x81\xd1\x80\xd0\xbf\xd1\x81\xd0\xba\xd0\xb8\0" + "sr-Latn\0" + "Serbian (Latin)\0" + "Inari Sami\0" + "anar\xc3\xa2\xc5\xa1kiel\xc3\xa2\0" + "az-Cyrl\0" + "Azerbaijani (Cyrillic)\0" + "Norwegian Nynorsk\0" + "nynorsk\0" + "Bosnian\0" + "az-Latn\0" + "Azerbaijani (Latin)\0" + "uz-Cyrl\0" + "Uzbek (Cyrillic)\0" + "mn-Cyrl\0" + "Mongolian (Cyrillic)\0" + "zh-Hant\0" + "zh-CHT\0" + "Chinese (Traditional) Legacy\0" + "Norwegian Bokm\xc3\xa5l\0" + "norsk bokm\xc3\xa5l\0" + "Serbian\0" + "SRB\0" + "tg-Cyrl\0" + "Tajik (Cyrillic)\0" + "Lower Sorbian\0" + "dolnoserb\xc5\xa1\xc4\x87ina\0" + "uz-Latn\0" + "Uzbek (Latin)\0" + "pa-Arab\0" + "Punjabi (Arabic)\0" + "tzm-Latn\0" + "Central Atlas Tamazight (Latin)\0" + "ha-Latn\0" + "Hausa (Latin)\0" + "af-za\0" + "am-et\0" + "ar-ae\0" + "ar-bh\0" + "ar-dz\0" + "ar-eg\0" + "ar-iq\0" + "ar-jo\0" + "ar-kw\0" + "ar-lb\0" + "ar-ly\0" + "ar-ma\0" + "ar-om\0" + "ar-qa\0" + "ar-sa\0" + "ar-sy\0" + "ar-tn\0" + "ar-ye\0" + "as-in\0" + "az-cyrl\0" + "az-cyrl-az\0" + "az-latn\0" + "az-latn-az\0" + "be-by\0" + "bg-bg\0" + "bn-bd\0" + "bn-in\0" + "bo-cn\0" + "br-fr\0" + "bs-cyrl\0" + "bs-cyrl-ba\0" + "bs-latn\0" + "bs-latn-ba\0" + "ca-es\0" + "ca-es-valencia\0" + "cs-cz\0" + "cy-gb\0" + "da-dk\0" + "de-at\0" + "de-ch\0" + "de-de\0" + "de-li\0" + "de-lu\0" + "dsb-de\0" + "el-gr\0" + "en-au\0" + "en-bz\0" + "en-ca\0" + "en-gb\0" + "en-hk\0" + "en-ie\0" + "en-in\0" + "en-jm\0" + "en-my\0" + "en-nz\0" + "en-ph\0" + "en-sg\0" + "en-tt\0" + "en-us\0" + "en-za\0" + "en-zw\0" + "es-ar\0" + "es-bo\0" + "es-cl\0" + "es-co\0" + "es-cr\0" + "es-cu\0" + "es-do\0" + "es-ec\0" + "es-es\0" + "es-gt\0" + "es-hn\0" + "es-mx\0" + "es-ni\0" + "es-pa\0" + "es-pe\0" + "es-pr\0" + "es-py\0" + "es-sv\0" + "es-us\0" + "es-uy\0" + "es-ve\0" + "et-ee\0" + "eu-es\0" + "fa-ir\0" + "fi-fi\0" + "fil-ph\0" + "fo-fo\0" + "fr-be\0" + "fr-ca\0" + "fr-cd\0" + "fr-ch\0" + "fr-ci\0" + "fr-cm\0" + "fr-fr\0" + "fr-ht\0" + "fr-lu\0" + "fr-ma\0" + "fr-mc\0" + "fr-ml\0" + "fr-re\0" + "fr-sn\0" + "fy-nl\0" + "ga-ie\0" + "gd-gb\0" + "gl-es\0" + "gsw-fr\0" + "gu-in\0" + "ha-latn\0" + "ha-latn-ng\0" + "haw-us\0" + "he-il\0" + "hi-in\0" + "hr-ba\0" + "hr-hr\0" + "hsb-de\0" + "hu-hu\0" + "hy-am\0" + "id-id\0" + "ig-ng\0" + "ii-cn\0" + "is-is\0" + "it-ch\0" + "it-it\0" + "ja-jp\0" + "ka-ge\0" + "kk-kz\0" + "kl-gl\0" + "km-kh\0" + "kn-in\0" + "ko-kr\0" + "kok-in\0" + "ky-kg\0" + "lb-lu\0" + "lo-la\0" + "lt-lt\0" + "lv-lv\0" + "mk-mk\0" + "ml-in\0" + "mn-cyrl\0" + "mn-mn\0" + "mr-in\0" + "ms-bn\0" + "ms-my\0" + "mt-mt\0" + "my-mm\0" + "nb-no\0" + "ne-in\0" + "ne-np\0" + "nl-be\0" + "nl-nl\0" + "nn-no\0" + "nso-za\0" + "om-et\0" + "or-in\0" + "pa-arab\0" + "pa-arab-pk\0" + "pl-pl\0" + "ps-af\0" + "pt-br\0" + "pt-pt\0" + "rm-ch\0" + "ro-md\0" + "ro-ro\0" + "ru-md\0" + "ru-ru\0" + "rw-rw\0" + "sah-ru\0" + "se-fi\0" + "se-no\0" + "se-se\0" + "si-lk\0" + "sk-sk\0" + "sl-si\0" + "smn-fi\0" + "so-so\0" + "sq-al\0" + "sr-cyrl\0" + "sr-cyrl-ba\0" + "sr-cyrl-me\0" + "sr-cyrl-rs\0" + "sr-latn\0" + "sr-latn-ba\0" + "sr-latn-me\0" + "sr-latn-rs\0" + "st-za\0" + "sv-fi\0" + "sv-se\0" + "sw-ke\0" + "ta-in\0" + "ta-lk\0" + "te-in\0" + "tg-cyrl\0" + "tg-cyrl-tj\0" + "th-th\0" + "ti-er\0" + "ti-et\0" + "tk-tm\0" + "tn-bw\0" + "tn-za\0" + "tr-tr\0" + "ts-za\0" + "tzm-latn\0" + "ug-cn\0" + "uk-ua\0" + "ur-in\0" + "ur-pk\0" + "uz-cyrl\0" + "uz-cyrl-uz\0" + "uz-latn\0" + "uz-latn-uz\0" + "vi-vn\0" + "xh-za\0" + "yo-ng\0" + "zh-chs\0" + "zh-cht\0" + "zh-cn\0" + "zh-hans\0" + "zh-hant\0" + "zh-hk\0" + "zh-mo\0" + "zh-sg\0" + "zh-tw\0" + "zu-za\0" + "United Arab Emirates\0" + "\xd8\xa7\xd9\x84\xd8\xa5\xd9\x85\xd8\xa7\xd8\xb1\xd8\xa7\xd8\xaa \xd8\xa7\xd9\x84\xd8\xb9\xd8\xb1\xd8\xa8\xd9\x8a\xd8\xa9 \xd8\xa7\xd9\x84\xd9\x85\xd8\xaa\xd8\xad\xd8\xaf\xd8\xa9\0" + "AED\0" + "United Arab Emirates Dirham\0" + "\xd8\xaf\xd8\xb1\xd9\x87\xd9\x85 \xd8\xa5\xd9\x85\xd8\xa7\xd8\xb1\xd8\xa7\xd8\xaa\xd9\x8a\0" + "AFG\0" + "Afghanistan\0" + "\xd8\xa7\xd9\x81\xd8\xba\xd8\xa7\xd9\x86\xd8\xb3\xd8\xaa\xd8\xa7\xd9\x86\0" + "AFN\0" + "Afghan Afghani\0" + "\xd8\xa7\xd9\x81\xd8\xba\xd8\xa7\xd9\x86\xdb\x8d\0" + "ALB\0" + "Albania\0" + "Shqip\xc3\xabri\0" + "ALL\0" + "Albanian Lek\0" + "Leku shqiptar\0" + "Armenia\0" + "\xd5\x80\xd5\xa1\xd5\xb5\xd5\xa1\xd5\xbd\xd5\xbf\xd5\xa1\xd5\xb6\0" + "AMD\0" + "Armenian Dram\0" + "\xd5\x80\xd5\xa1\xd5\xb5\xd5\xaf\xd5\xa1\xd5\xaf\xd5\xa1\xd5\xb6 \xd5\xa4\xd6\x80\xd5\xa1\xd5\xb4\0" + "Argentina\0" + "Argentine Peso\0" + "peso argentino\0" + "AUT\0" + "Austria\0" + "\xc3\x96sterreich\0" + "EUR\0" + "Euro\0" + "AUS\0" + "Australia\0" + "AUD\0" + "Australian Dollar\0" + "Azerbaijan\0" + "Az\xc9\x99rbaycan\0" + "AZN\0" + "Azerbaijani Manat\0" + "Az\xc9\x99rbaycan Manat\xc4\xb1\0" + "BIH\0" + "Bosnia & Herzegovina\0" + "Bosna i Hercegovina\0" + "BAM\0" + "Bosnia-Herzegovina Convertible Mark\0" + "konvertibilna marka\0" + "BGD\0" + "Bangladesh\0" + "\xe0\xa6\xac\xe0\xa6\xbe\xe0\xa6\x82\xe0\xa6\xb2\xe0\xa6\xbe\xe0\xa6\xa6\xe0\xa7\x87\xe0\xa6\xb6\0" + "BDT\0" + "Bangladeshi Taka\0" + "\xe0\xa6\xac\xe0\xa6\xbe\xe0\xa6\x82\xe0\xa6\xb2\xe0\xa6\xbe\xe0\xa6\xa6\xe0\xa7\x87\xe0\xa6\xb6\xe0\xa7\x80 \xe0\xa6\x9f\xe0\xa6\xbe\xe0\xa6\x95\xe0\xa6\xbe\0" + "Belgium\0" + "Belgique\0" + "euro\0" + "Bulgaria\0" + "\xd0\x91\xd1\x8a\xd0\xbb\xd0\xb3\xd0\xb0\xd1\x80\xd0\xb8\xd1\x8f\0" + "BGN\0" + "Bulgarian Lev\0" + "\xd0\x91\xd1\x8a\xd0\xbb\xd0\xb3\xd0\xb0\xd1\x80\xd1\x81\xd0\xba\xd0\xb8 \xd0\xbb\xd0\xb5\xd0\xb2\0" + "BHR\0" + "Bahrain\0" + "\xd8\xa7\xd9\x84\xd8\xa8\xd8\xad\xd8\xb1\xd9\x8a\xd9\x86\0" + "BHD\0" + "Bahraini Dinar\0" + "\xd8\xaf\xd9\x8a\xd9\x86\xd8\xa7\xd8\xb1 \xd8\xa8\xd8\xad\xd8\xb1\xd9\x8a\xd9\x86\xd9\x8a\0" + "BRN\0" + "Brunei\0" + "BND\0" + "Brunei Dollar\0" + "Dolar Brunei\0" + "BOL\0" + "Bolivia\0" + "Bolivian Boliviano\0" + "boliviano\0" + "BRA\0" + "Brazil\0" + "Brasil\0" + "BRL\0" + "Brazilian Real\0" + "Real brasileiro\0" + "BWA\0" + "Botswana\0" + "BWP\0" + "Botswanan Pula\0" + "BLR\0" + "Belarus\0" + "\xd0\x91\xd0\xb5\xd0\xbb\xd0\xb0\xd1\x80\xd1\x83\xd1\x81\xd1\x8c\0" + "BYN\0" + "Belarusian Ruble\0" + "\xd0\xb1\xd0\xb5\xd0\xbb\xd0\xb0\xd1\x80\xd1\x83\xd1\x81\xd0\xba\xd1\x96 \xd1\x80\xd1\x83\xd0\xb1\xd0\xb5\xd0\xbb\xd1\x8c\0" + "BLZ\0" + "Belize\0" + "BZD\0" + "Belize Dollar\0" + "CAN\0" + "Canada\0" + "CAD\0" + "Canadian Dollar\0" + "dollar canadien\0" + "COD\0" + "Congo - Kinshasa\0" + "Congo-Kinshasa\0" + "CDF\0" + "Congolese Franc\0" + "franc congolais\0" + "CHE\0" + "Switzerland\0" + "Svizra\0" + "Swiss Franc\0" + "franc svizzer\0" + "CIV\0" + "C\xc3\xb4te d\xe2\x80\x99Ivoire\0" + "XOF\0" + "West African CFA Franc\0" + "franc CFA (BCEAO)\0" + "CHL\0" + "Chile\0" + "CLP\0" + "Chilean Peso\0" + "Peso chileno\0" + "CMR\0" + "Cameroon\0" + "Cameroun\0" + "XAF\0" + "Central African CFA Franc\0" + "franc CFA (BEAC)\0" + "CHN\0" + "China\0" + "\xe0\xbd\xa2\xe0\xbe\x92\xe0\xbe\xb1\xe0\xbc\x8b\xe0\xbd\x93\xe0\xbd\x82\0" + "CNY\0" + "Chinese Yuan\0" + "\xe0\xbd\xa1\xe0\xbd\xb4\xe0\xbc\x8b\xe0\xbd\xa8\xe0\xbd\x93\xe0\xbc\x8b\0" + "COL\0" + "Colombia\0" + "COP\0" + "Colombian Peso\0" + "peso colombiano\0" + "CRI\0" + "Costa Rica\0" + "CRC\0" + "Costa Rican Col\xc3\xb3n\0" + "col\xc3\xb3n costarricense\0" + "CUB\0" + "Cuba\0" + "CUP\0" + "Cuban Peso\0" + "peso cubano\0" + "CZE\0" + "Czech Republic\0" + "\xc4\x8c\x65sk\xc3\xa1 republika\0" + "CZK\0" + "Czech Republic Koruna\0" + "\xc4\x8d\x65sk\xc3\xa1 koruna\0" + "Germany\0" + "Deutschland\0" + "DNK\0" + "Denmark\0" + "Danmark\0" + "DKK\0" + "Danish Krone\0" + "dansk krone\0" + "DOM\0" + "Dominican Republic\0" + "Rep\xc3\xba\x62lica Dominicana\0" + "DOP\0" + "Dominican Peso\0" + "peso dominicano\0" + "DZA\0" + "Algeria\0" + "\xd8\xa7\xd9\x84\xd8\xac\xd8\xb2\xd8\xa7\xd8\xa6\xd8\xb1\0" + "DZD\0" + "Algerian Dinar\0" + "\xd8\xaf\xd9\x8a\xd9\x86\xd8\xa7\xd8\xb1 \xd8\xac\xd8\xb2\xd8\xa7\xd8\xa6\xd8\xb1\xd9\x8a\0" + "ECU\0" + "Ecuador\0" + "USD\0" + "US Dollar\0" + "d\xc3\xb3lar estadounidense\0" + "Estonia\0" + "Eesti\0" + "EGY\0" + "Egypt\0" + "\xd9\x85\xd8\xb5\xd8\xb1\0" + "EGP\0" + "Egyptian Pound\0" + "\xd8\xac\xd9\x86\xd9\x8a\xd9\x87 \xd9\x85\xd8\xb5\xd8\xb1\xd9\x8a\0" + "ERI\0" + "Eritrea\0" + "\xe1\x8a\xa4\xe1\x88\xad\xe1\x89\xb5\xe1\x88\xab\0" + "ERN\0" + "Eritrean Nakfa\0" + "Spain\0" + "Espanya\0" + "ETH\0" + "Ethiopia\0" + "\xe1\x8a\xa2\xe1\x89\xb5\xe1\x8b\xae\xe1\x8c\xb5\xe1\x8b\xab\0" + "ETB\0" + "Ethiopian Birr\0" + "\xe1\x8b\xa8\xe1\x8a\xa2\xe1\x89\xb5\xe1\x8b\xae\xe1\x8c\xb5\xe1\x8b\xab \xe1\x89\xa5\xe1\x88\xad\0" + "Finland\0" + "Suomi\0" + "Faroe Islands\0" + "F\xc3\xb8royar\0" + "donsk kr\xc3\xb3na\0" + "France\0" + "GBR\0" + "United Kingdom\0" + "Y Deyrnas Unedig\0" + "GBP\0" + "British Pound\0" + "Punt Prydain\0" + "GEO\0" + "Georgia\0" + "\xe1\x83\xa1\xe1\x83\x90\xe1\x83\xa5\xe1\x83\x90\xe1\x83\xa0\xe1\x83\x97\xe1\x83\x95\xe1\x83\x94\xe1\x83\x9a\xe1\x83\x9d\0" + "GEL\0" + "Georgian Lari\0" + "\xe1\x83\xa5\xe1\x83\x90\xe1\x83\xa0\xe1\x83\x97\xe1\x83\xa3\xe1\x83\x9a\xe1\x83\x98 \xe1\x83\x9a\xe1\x83\x90\xe1\x83\xa0\xe1\x83\x98\0" + "GRL\0" + "Greenland\0" + "Kalaallit Nunaat\0" + "danmarkimut koruuni\0" + "GRC\0" + "Greece\0" + "\xce\x95\xce\xbb\xce\xbb\xce\xac\xce\xb4\xce\xb1\0" + "\xce\x95\xcf\x85\xcf\x81\xcf\x8e\0" + "GTM\0" + "Guatemala\0" + "GTQ\0" + "Guatemalan Quetzal\0" + "quetzal\0" + "HKG\0" + "Hong Kong SAR China\0" + "\xe4\xb8\xad\xe5\x9b\xbd\xe9\xa6\x99\xe6\xb8\xaf\xe7\x89\xb9\xe5\x88\xab\xe8\xa1\x8c\xe6\x94\xbf\xe5\x8c\xba\0" + "HKD\0" + "Hong Kong Dollar\0" + "\xe6\xb8\xaf\xe5\x85\x83\0" + "HND\0" + "Honduras\0" + "HNL\0" + "Honduran Lempira\0" + "lempira hondure\xc3\xb1o\0" + "Croatia\0" + "Hrvatska\0" + "Croatian Kuna\0" + "hrvatska kuna\0" + "HTI\0" + "Haiti\0" + "Ha\xc3\xafti\0" + "HTG\0" + "Haitian Gourde\0" + "gourde ha\xc3\xaftienne\0" + "Hungary\0" + "Magyarorsz\xc3\xa1g\0" + "HUF\0" + "Hungarian Forint\0" + "magyar forint\0" + "IDN\0" + "IDR\0" + "Indonesian Rupiah\0" + "Rupiah Indonesia\0" + "IRL\0" + "Ireland\0" + "\xc3\x89ire\0" + "ISR\0" + "Israel\0" + "\xd7\x99\xd7\xa9\xd7\xa8\xd7\x90\xd7\x9c\0" + "ILS\0" + "Israeli New Shekel\0" + "\xd7\xa9\xd7\xa7\xd7\x9c \xd7\x97\xd7\x93\xd7\xa9\0" + "India\0" + "\xe0\xa4\xad\xe0\xa4\xbe\xe0\xa4\xb0\xe0\xa4\xa4\0" + "INR\0" + "Indian Rupee\0" + "\xe0\xa4\xad\xe0\xa4\xbe\xe0\xa4\xb0\xe0\xa4\xa4\xe0\xa5\x80\xe0\xa4\xaf \xe0\xa4\xb0\xe0\xa5\x81\xe0\xa4\xaa\xe0\xa4\xaf\xe0\xa4\xbe\0" + "IRQ\0" + "Iraq\0" + "\xd8\xa7\xd9\x84\xd8\xb9\xd8\xb1\xd8\xa7\xd9\x82\0" + "IQD\0" + "Iraqi Dinar\0" + "\xd8\xaf\xd9\x8a\xd9\x86\xd8\xa7\xd8\xb1 \xd8\xb9\xd8\xb1\xd8\xa7\xd9\x82\xd9\x8a\0" + "IRN\0" + "Iran\0" + "\xd8\xa7\xdb\x8c\xd8\xb1\xd8\xa7\xd9\x86\0" + "IRR\0" + "Iranian Rial\0" + "\xd8\xb1\xdb\x8c\xd8\xa7\xd9\x84 \xd8\xa7\xdb\x8c\xd8\xb1\xd8\xa7\xd9\x86\0" + "Iceland\0" + "\xc3\x8dsland\0" + "Icelandic Kr\xc3\xb3na\0" + "\xc3\xadslensk kr\xc3\xb3na\0" + "Italy\0" + "Italia\0" + "JAM\0" + "Jamaica\0" + "JMD\0" + "Jamaican Dollar\0" + "JOR\0" + "Jordan\0" + "\xd8\xa7\xd9\x84\xd8\xa3\xd8\xb1\xd8\xaf\xd9\x86\0" + "JOD\0" + "Jordanian Dinar\0" + "\xd8\xaf\xd9\x8a\xd9\x86\xd8\xa7\xd8\xb1 \xd8\xa3\xd8\xb1\xd8\xaf\xd9\x86\xd9\x8a\0" + "Japan\0" + "\xe6\x97\xa5\xe6\x9c\xac\0" + "JPY\0" + "Japanese Yen\0" + "\xe6\x97\xa5\xe6\x9c\xac\xe5\x86\x86\0" + "KEN\0" + "Kenya\0" + "KES\0" + "Kenyan Shilling\0" + "Shilingi ya Kenya\0" + "KGZ\0" + "Kyrgyzstan\0" + "\xd0\x9a\xd1\x8b\xd1\x80\xd0\xb3\xd1\x8b\xd0\xb7\xd1\x81\xd1\x82\xd0\xb0\xd0\xbd\0" + "KGS\0" + "Kyrgystani Som\0" + "\xd0\x9a\xd1\x8b\xd1\x80\xd0\xb3\xd1\x8b\xd0\xb7\xd1\x81\xd1\x82\xd0\xb0\xd0\xbd \xd1\x81\xd0\xbe\xd0\xbc\xd1\x83\0" + "Cambodia\0" + "\xe1\x9e\x80\xe1\x9e\x98\xe1\x9f\x92\xe1\x9e\x96\xe1\x9e\xbb\xe1\x9e\x87\xe1\x9e\xb6\0" + "KHR\0" + "Cambodian Riel\0" + "\xe1\x9e\x9a\xe1\x9f\x80\xe1\x9e\x9b\xe2\x80\x8b\xe1\x9e\x80\xe1\x9e\x98\xe1\x9f\x92\xe1\x9e\x96\xe1\x9e\xbb\xe1\x9e\x87\xe1\x9e\xb6\0" + "South Korea\0" + "\xeb\x8c\x80\xed\x95\x9c\xeb\xaf\xbc\xea\xb5\xad\0" + "KRW\0" + "South Korean Won\0" + "\xeb\x8c\x80\xed\x95\x9c\xeb\xaf\xbc\xea\xb5\xad \xec\x9b\x90\0" + "KWT\0" + "Kuwait\0" + "\xd8\xa7\xd9\x84\xd9\x83\xd9\x88\xd9\x8a\xd8\xaa\0" + "KWD\0" + "Kuwaiti Dinar\0" + "\xd8\xaf\xd9\x8a\xd9\x86\xd8\xa7\xd8\xb1 \xd9\x83\xd9\x88\xd9\x8a\xd8\xaa\xd9\x8a\0" + "KAZ\0" + "Kazakhstan\0" + "\xd2\x9a\xd0\xb0\xd0\xb7\xd0\xb0\xd2\x9b\xd1\x81\xd1\x82\xd0\xb0\xd0\xbd\0" + "KZT\0" + "Kazakhstani Tenge\0" + "\xd2\x9a\xd0\xb0\xd0\xb7\xd0\xb0\xd2\x9b\xd1\x81\xd1\x82\xd0\xb0\xd0\xbd \xd1\x82\xd0\xb5\xd2\xa3\xd0\xb3\xd0\xb5\xd1\x81\xd1\x96\0" + "Laos\0" + "LAK\0" + "Laotian Kip\0" + "\xe0\xba\xa5\xe0\xba\xb2\xe0\xba\xa7 \xe0\xba\x81\xe0\xba\xb5\xe0\xba\x9a\0" + "LBN\0" + "Lebanon\0" + "\xd9\x84\xd8\xa8\xd9\x86\xd8\xa7\xd9\x86\0" + "LBP\0" + "Lebanese Pound\0" + "\xd8\xac\xd9\x86\xd9\x8a\xd9\x87 \xd9\x84\xd8\xa8\xd9\x86\xd8\xa7\xd9\x86\xd9\x8a\0" + "LIE\0" + "Liechtenstein\0" + "Schweizer Franken\0" + "LKA\0" + "Sri Lanka\0" + "\xe0\xb7\x81\xe0\xb7\x8a\xe2\x80\x8d\xe0\xb6\xbb\xe0\xb7\x93 \xe0\xb6\xbd\xe0\xb6\x82\xe0\xb6\x9a\xe0\xb7\x8f\xe0\xb7\x80\0" + "LKR\0" + "Sri Lankan Rupee\0" + "\xe0\xb7\x81\xe0\xb7\x8a\xe2\x80\x8d\xe0\xb6\xbb\xe0\xb7\x93 \xe0\xb6\xbd\xe0\xb6\x82\xe0\xb6\x9a\xe0\xb7\x8f \xe0\xb6\xbb\xe0\xb7\x94\xe0\xb6\xb4\xe0\xb7\x92\xe0\xb6\xba\xe0\xb6\xbd\0" + "LTU\0" + "Lithuania\0" + "Lietuva\0" + "Euras\0" + "LUX\0" + "Luxembourg\0" + "L\xc3\xabtzebuerg\0" + "LVA\0" + "Latvia\0" + "Latvija\0" + "eiro\0" + "LBY\0" + "Libya\0" + "\xd9\x84\xd9\x8a\xd8\xa8\xd9\x8a\xd8\xa7\0" + "LYD\0" + "Libyan Dinar\0" + "\xd8\xaf\xd9\x8a\xd9\x86\xd8\xa7\xd8\xb1 \xd9\x84\xd9\x8a\xd8\xa8\xd9\x8a\0" + "Morocco\0" + "\xd8\xa7\xd9\x84\xd9\x85\xd8\xba\xd8\xb1\xd8\xa8\0" + "Moroccan Dirham\0" + "\xd8\xaf\xd8\xb1\xd9\x87\xd9\x85 \xd9\x85\xd8\xba\xd8\xb1\xd8\xa8\xd9\x8a\0" + "MCO\0" + "Monaco\0" + "MDA\0" + "Moldova\0" + "Republica Moldova\0" + "MDL\0" + "Moldovan Leu\0" + "leu moldovenesc\0" + "MNE\0" + "Montenegro\0" + "\xd0\xa6\xd1\x80\xd0\xbd\xd0\xb0 \xd0\x93\xd0\xbe\xd1\x80\xd0\xb0\0" + "Evro\0" + "MKD\0" + "Macedonia\0" + "\xd0\x9c\xd0\xb0\xd0\xba\xd0\xb5\xd0\xb4\xd0\xbe\xd0\xbd\xd0\xb8\xd1\x98\xd0\xb0\0" + "Macedonian Denar\0" + "\xd0\x9c\xd0\xb0\xd0\xba\xd0\xb5\xd0\xb4\xd0\xbe\xd0\xbd\xd1\x81\xd0\xba\xd0\xb8 \xd0\xb4\xd0\xb5\xd0\xbd\xd0\xb0\xd1\x80\0" + "MLI\0" + "Mali\0" + "MMR\0" + "Myanmar (Burma)\0" + "MMK\0" + "Myanmar Kyat\0" + "\xe1\x80\x99\xe1\x80\xbc\xe1\x80\x94\xe1\x80\xba\xe1\x80\x99\xe1\x80\xac\xe1\x80\x80\xe1\x80\xbb\xe1\x80\x95\xe1\x80\xba\0" + "MNG\0" + "Mongolia\0" + "\xd0\x9c\xd0\xbe\xd0\xbd\xd0\xb3\xd0\xbe\xd0\xbb\0" + "MNT\0" + "Mongolian Tugrik\0" + "\xd1\x82\xd3\xa9\xd0\xb3\xd1\x80\xd3\xa9\xd0\xb3\0" + "MAC\0" + "Macau SAR China\0" + "\xe4\xb8\xad\xe5\x9b\xbd\xe6\xbe\xb3\xe9\x97\xa8\xe7\x89\xb9\xe5\x88\xab\xe8\xa1\x8c\xe6\x94\xbf\xe5\x8c\xba\0" + "MOP\0" + "Macanese Pataca\0" + "\xe6\xbe\xb3\xe9\x96\x80\xe5\x85\x83\0" + "Malta\0" + "ewro\0" + "MEX\0" + "Mexico\0" + "M\xc3\xa9xico\0" + "MXN\0" + "Mexican Peso\0" + "peso mexicano\0" + "MYS\0" + "Malaysia\0" + "MYR\0" + "Malaysian Ringgit\0" + "Ringgit Malaysia\0" + "NGA\0" + "Nigeria\0" + "Najeriya\0" + "NGN\0" + "Nigerian Naira\0" + "Naira\0" + "NIC\0" + "Nicaragua\0" + "NIO\0" + "Nicaraguan C\xc3\xb3rdoba\0" + "c\xc3\xb3rdoba nicarag\xc3\xbc\x65nse\0" + "Netherlands\0" + "Nederland\0" + "Norway\0" + "Norge\0" + "NOK\0" + "Norwegian Krone\0" + "norske kroner\0" + "NPL\0" + "Nepal\0" + "\xe0\xa4\xa8\xe0\xa5\x87\xe0\xa4\xaa\xe0\xa4\xbe\xe0\xa4\xb2\0" + "NPR\0" + "Nepalese Rupee\0" + "\xe0\xa4\xa8\xe0\xa5\x87\xe0\xa4\xaa\xe0\xa4\xbe\xe0\xa4\xb2\xe0\xa5\x80 \xe0\xa4\xb0\xe0\xa5\x82\xe0\xa4\xaa\xe0\xa5\x88\xe0\xa4\xaf\xe0\xa4\xbe\xe0\xa4\x81\0" + "NZL\0" + "New Zealand\0" + "NZD\0" + "New Zealand Dollar\0" + "OMN\0" + "Oman\0" + "\xd8\xb9\xd9\x8f\xd9\x85\xd8\xa7\xd9\x86\0" + "OMR\0" + "Omani Rial\0" + "\xd8\xb1\xd9\x8a\xd8\xa7\xd9\x84 \xd8\xb9\xd9\x85\xd8\xa7\xd9\x86\xd9\x8a\0" + "Panama\0" + "Panam\xc3\xa1\0" + "PAB\0" + "Panamanian Balboa\0" + "balboa paname\xc3\xb1o\0" + "PER\0" + "Peru\0" + "Per\xc3\xba\0" + "PEN\0" + "Peruvian Sol\0" + "nuevo sol peruano\0" + "PHL\0" + "Philippines\0" + "Pilipinas\0" + "PHP\0" + "Philippine Peso\0" + "Piso ng Pilipinas\0" + "PAK\0" + "Pakistan\0" + "\xd9\xbe\xd8\xa7\xda\xa9\xd8\xb3\xd8\xaa\xd8\xa7\xd9\x86\0" + "PKR\0" + "Pakistani Rupee\0" + "\xd9\xbe\xd8\xa7\xda\xa9\xd8\xb3\xd8\xaa\xd8\xa7\xd9\x86\xdb\x8c \xd8\xb1\xd9\x88\xd9\xbe\xdb\x8c\xdb\x81\0" + "POL\0" + "Poland\0" + "Polska\0" + "PLN\0" + "Polish Zloty\0" + "z\xc5\x82oty polski\0" + "PRI\0" + "Puerto Rico\0" + "PRT\0" + "Portugal\0" + "PRY\0" + "Paraguay\0" + "PYG\0" + "Paraguayan Guarani\0" + "guaran\xc3\xad paraguayo\0" + "QAT\0" + "Qatar\0" + "\xd9\x82\xd8\xb7\xd8\xb1\0" + "QAR\0" + "Qatari Rial\0" + "\xd8\xb1\xd9\x8a\xd8\xa7\xd9\x84 \xd9\x82\xd8\xb7\xd8\xb1\xd9\x8a\0" + "REU\0" + "R\xc3\xa9union\0" + "La R\xc3\xa9union\0" + "ROU\0" + "Romania\0" + "Rom\xc3\xa2nia\0" + "Romanian Leu\0" + "leu rom\xc3\xa2nesc\0" + "Serbia\0" + "\xd0\xa1\xd1\x80\xd0\xb1\xd0\xb8\xd1\x98\xd0\xb0\0" + "Serbian Dinar\0" + "Srpski dinar\0" + "Russia\0" + "\xd0\xa0\xd0\xbe\xd1\x81\xd1\x81\xd0\xb8\xd1\x8f\0" + "RUB\0" + "Russian Ruble\0" + "\xd0\xa0\xd0\xbe\xd1\x81\xd1\x81\xd0\xb8\xd0\xb9\xd1\x81\xd0\xba\xd0\xb8\xd0\xb9 \xd1\x80\xd1\x83\xd0\xb1\xd0\xbb\xd1\x8c\0" + "RWA\0" + "Rwanda\0" + "RWF\0" + "Rwandan Franc\0" + "SAU\0" + "Saudi Arabia\0" + "\xd8\xa7\xd9\x84\xd9\x85\xd9\x85\xd9\x84\xd9\x83\xd8\xa9 \xd8\xa7\xd9\x84\xd8\xb9\xd8\xb1\xd8\xa8\xd9\x8a\xd8\xa9 \xd8\xa7\xd9\x84\xd8\xb3\xd8\xb9\xd9\x88\xd8\xaf\xd9\x8a\xd8\xa9\0" + "SAR\0" + "Saudi Riyal\0" + "\xd8\xb1\xd9\x8a\xd8\xa7\xd9\x84 \xd8\xb3\xd8\xb9\xd9\x88\xd8\xaf\xd9\x8a\0" + "SWE\0" + "Sweden\0" + "Sverige\0" + "SEK\0" + "Swedish Krona\0" + "svensk krona\0" + "SGP\0" + "Singapore\0" + "\xe6\x96\xb0\xe5\x8a\xa0\xe5\x9d\xa1\0" + "SGD\0" + "Singapore Dollar\0" + "\xe6\x96\xb0\xe5\x8a\xa0\xe5\x9d\xa1\xe5\x85\x83\0" + "SVN\0" + "Slovenia\0" + "Slovenija\0" + "evro\0" + "SVK\0" + "Slovakia\0" + "Slovensko\0" + "SEN\0" + "Senegal\0" + "S\xc3\xa9n\xc3\xa9gal\0" + "Somalia\0" + "Soomaaliya\0" + "SOS\0" + "Somali Shilling\0" + "Shilin soomaali\0" + "El Salvador\0" + "SYR\0" + "Syria\0" + "\xd8\xb3\xd9\x88\xd8\xb1\xd9\x8a\xd8\xa7\0" + "SYP\0" + "Syrian Pound\0" + "\xd9\x84\xd9\x8a\xd8\xb1\xd8\xa9 \xd8\xb3\xd9\x88\xd8\xb1\xd9\x8a\xd8\xa9\0" + "Thailand\0" + "Thai Baht\0" + "\xe0\xb8\x9a\xe0\xb8\xb2\xe0\xb8\x97\xe0\xb9\x84\xe0\xb8\x97\xe0\xb8\xa2\0" + "TJK\0" + "Tajikistan\0" + "\xd0\xa2\xd0\xbe\xd2\xb7\xd0\xb8\xd0\xba\xd0\xb8\xd1\x81\xd1\x82\xd0\xbe\xd0\xbd\0" + "TJS\0" + "Tajikistani Somoni\0" + "\xd0\xa1\xd0\xbe\xd0\xbc\xd0\xbe\xd0\xbd\xd3\xa3\0" + "TKM\0" + "Turkmenistan\0" + "T\xc3\xbcrkmenistan\0" + "Turkmenistani Manat\0" + "T\xc3\xbcrkmen manaty\0" + "TUN\0" + "Tunisia\0" + "\xd8\xaa\xd9\x88\xd9\x86\xd8\xb3\0" + "TND\0" + "Tunisian Dinar\0" + "\xd8\xaf\xd9\x8a\xd9\x86\xd8\xa7\xd8\xb1 \xd8\xaa\xd9\x88\xd9\x86\xd8\xb3\xd9\x8a\0" + "TUR\0" + "Turkey\0" + "T\xc3\xbcrkiye\0" + "TRY\0" + "Turkish Lira\0" + "T\xc3\xbcrk Liras\xc4\xb1\0" + "TTO\0" + "Trinidad & Tobago\0" + "TTD\0" + "Trinidad & Tobago Dollar\0" + "TWN\0" + "Taiwan\0" + "\xe5\x8f\xb0\xe6\xb9\xbe\0" + "TWD\0" + "New Taiwan Dollar\0" + "\xe6\x96\xb0\xe5\x8f\xb0\xe5\xb9\xa3\0" + "Ukraine\0" + "\xd0\xa3\xd0\xba\xd1\x80\xd0\xb0\xd1\x97\xd0\xbd\xd0\xb0\0" + "UAH\0" + "Ukrainian Hryvnia\0" + "\xd1\x83\xd0\xba\xd1\x80\xd0\xb0\xd1\x97\xd0\xbd\xd1\x81\xd1\x8c\xd0\xba\xd0\xb0 \xd0\xb3\xd1\x80\xd0\xb8\xd0\xb2\xd0\xbd\xd1\x8f\0" + "USA\0" + "United States\0" + "URY\0" + "Uruguay\0" + "UYU\0" + "Uruguayan Peso\0" + "peso uruguayo\0" + "Uzbekistan\0" + "O\xca\xbbzbekiston\0" + "UZS\0" + "Uzbekistani Som\0" + "O\xe2\x80\x98zbekiston so\xe2\x80\x98mi\0" + "VEN\0" + "Venezuela\0" + "VEF\0" + "Venezuelan Bol\xc3\xadvar\0" + "bol\xc3\xadvar venezolano\0" + "VNM\0" + "Vietnam\0" + "Vi\xe1\xbb\x87t Nam\0" + "VND\0" + "Vietnamese Dong\0" + "\xc4\x90\xe1\xbb\x93ng Vi\xe1\xbb\x87t Nam\0" + "YEM\0" + "Yemen\0" + "\xd8\xa7\xd9\x84\xd9\x8a\xd9\x85\xd9\x86\0" + "YER\0" + "Yemeni Rial\0" + "\xd8\xb1\xd9\x8a\xd8\xa7\xd9\x84 \xd9\x8a\xd9\x85\xd9\x86\xd9\x8a\0" + "ZAF\0" + "South Africa\0" + "ZAR\0" + "South African Rand\0" + "ZWE\0" + "Zimbabwe\0" +}; + + +static const char patterns [] = { + "\0" + "dd MMMM\0" + "dd/MM/yy\0" + "dd/MM/yyyy\0" + "dd/MMMM/yyyy\0" + "dddd, dd MMMM, yyyy\0" + "hh:mm tt\0" + "HH:mm\0" + "hh:mm:ss tt\0" + "HH:mm:ss\0" + "MMMM, yyyy\0" + "d MMMM\0" + "d.M.yyyy '\xd0\xb3.'\0" + "dd.M.yyyy '\xd0\xb3.'\0" + "d.MM.yyyy '\xd0\xb3.'\0" + "dd.MM.yyyy '\xd0\xb3.'\0" + "dd MMMM yyyy '\xd0\xb3.'\0" + "d MMMM yyyy '\xd0\xb3.'\0" + "dddd, dd MMMM yyyy '\xd0\xb3.'\0" + "dddd, d MMMM yyyy '\xd0\xb3.'\0" + "H:mm\0" + "H:mm:ss\0" + "MMMM yyyy '\xd0\xb3.'\0" + "d/M/yyyy\0" + "d MMM yyyy\0" + "dddd, d MMMM 'de' yyyy\0" + "d MMMM 'de' yyyy\0" + "MMMM 'de' yyyy\0" + "M\xe6\x9c\x88\x64\xe6\x97\xa5\0" + "yyyy/M/d\0" + "yyyy-M-d\0" + "yyyy.M.d\0" + "yyyy/MM/dd\0" + "yyyy-MM-dd\0" + "yyyy.MM.dd\0" + "yy/M/d\0" + "yy-M-d\0" + "yy.M.d\0" + "yy/MM/dd\0" + "yyyy'\xe5\xb9\xb4'M'\xe6\x9c\x88'd'\xe6\x97\xa5'\0" + "yyyy'\xe5\xb9\xb4'M'\xe6\x9c\x88'd'\xe6\x97\xa5', dddd\0" + "dddd, yyyy'\xe5\xb9\xb4'M'\xe6\x9c\x88'd'\xe6\x97\xa5'\0" + "yyyy\xe5\xb9\xb4MMMd\xe6\x97\xa5\0" + "yyyy\xe5\xb9\xb4MMMd\xe6\x97\xa5, dddd\0" + "tt h:mm\0" + "tt hh:mm\0" + "tt h:mm:ss\0" + "tt hh:mm:ss\0" + "yyyy'\xe5\xb9\xb4'M'\xe6\x9c\x88'\0" + "yyyy'\xe5\xb9\xb4'MMM\0" + "yyyy'\xe5\xb9\xb4'MMMM\0" + "yyyy.M\0" + "d. MMMM\0" + "dd.MM.yyyy\0" + "d. M. yyyy\0" + "dddd d. MMMM yyyy\0" + "d. MMMM yyyy\0" + "MMMM yyyy\0" + "dd-MM-yyyy\0" + "dd-MM-yy\0" + "yyyy MM dd\0" + "dd.MM.yy\0" + "dd. MMM. yyyy\0" + "dddd, d. MMMM yyyy\0" + "d. MMM. yyyy\0" + "HH:mm' Uhr'\0" + "HH:mm:ss' Uhr'\0" + "d/M/yy\0" + "dd/MMM/yyyy\0" + "dddd, d MMMM yyyy\0" + "d MMMM yyyy\0" + "h:mm tt\0" + "h:mm:ss tt\0" + "MMMM d\0" + "M/d/yyyy\0" + "M/d/yy\0" + "MM/dd/yy\0" + "MM/dd/yyyy\0" + "dd-MMM-yy\0" + "dddd, MMMM d, yyyy\0" + "MMMM d, yyyy\0" + "dddd, d MMMM, yyyy\0" + "d MMMM, yyyy\0" + "d 'de' MMMM\0" + "d/MM/yy\0" + "d-M-yy\0" + "dddd, d' de 'MMMM' de 'yyyy\0" + "dddd d' de 'MMMM' de 'yyyy\0" + "d' de 'MMMM' de 'yyyy\0" + "H.mm\0" + "HH.mm\0" + "HH'H'mm\0" + "H.mm.ss\0" + "HH.mm.ss\0" + "HH'H'mm.ss\0" + "MMMM' de 'yyyy\0" + "d.M.yyyy\0" + "dddd d MMMM yyyy\0" + "d MMM yy\0" + "HH' h 'mm\0" + "HH'h'mm\0" + "dd MMMM yyyy\0" + "dd-MMMM-yyyy\0" + "dd '\xd7\x91'MMMM yyyy\0" + "dd MMM yy\0" + "dddd dd MMMM yyyy\0" + "dddd dd '\xd7\x91'MMMM yyyy\0" + "ddd dd '\xd7\x91'MMMM yyyy\0" + "MMMM d.\0" + "yyyy. MM. dd.\0" + "yyyy. MMM d.\0" + "yyyy. MMMM d., dddd\0" + "yyyy. MMMM d.\0" + "yyyy. MMMM\0" + "d. MMM yyyy\0" + "dd.M.yy\0" + "d-MMM-yy\0" + "yyyy'\xe5\xb9\xb4'MM'\xe6\x9c\x88'dd'\xe6\x97\xa5'\0" + "yyyy'\xe5\xb9\xb4'M'\xe6\x9c\x88'd'\xe6\x97\xa5 'dddd\0" + "yyyy'\xe5\xb9\xb4'MM'\xe6\x9c\x88'dd'\xe6\x97\xa5 'dddd\0" + "yyyy'\xe5\xb9\xb4'MMM'\xe6\x9c\x88'd'\xe6\x97\xa5'\0" + "yyyy'\xe5\xb9\xb4'MMM'\xe6\x9c\x88'd'\xe6\x97\xa5 'dddd\0" + "yyyy'\xe5\xb9\xb4'MMMMd'\xe6\x97\xa5'\0" + "yyyy'\xe5\xb9\xb4'MMMMd'\xe6\x97\xa5 'dddd\0" + "yyyy'\xe5\xb9\xb4'MMM'\xe6\x9c\x88'\0" + "M\xec\x9b\x94 d\xec\x9d\xbc\0" + "yy-MM-dd\0" + "yyyy'\xeb\x85\x84' M'\xec\x9b\x94' d'\xec\x9d\xbc' dddd\0" + "yyyy'\xeb\x85\x84' M'\xec\x9b\x94' d'\xec\x9d\xbc'\0" + "yy'\xeb\x85\x84' M'\xec\x9b\x94' d'\xec\x9d\xbc' dddd\0" + "yy'\xeb\x85\x84' M'\xec\x9b\x94' d'\xec\x9d\xbc'\0" + "yyyy'\xeb\x85\x84' MM'\xec\x9b\x94' dd'\xec\x9d\xbc' dddd\0" + "yyyy'\xeb\x85\x84' MM'\xec\x9b\x94' dd'\xec\x9d\xbc'\0" + "yyyy'\xeb\x85\x84 'MMM'\xec\x9b\x94 'd'\xec\x9d\xbc 'dddd\0" + "yyyy'\xeb\x85\x84 'MMM'\xec\x9b\x94 'd'\xec\x9d\xbc'\0" + "yyyy'\xeb\x85\x84 'MMMM d'\xec\x9d\xbc 'dddd\0" + "yyyy'\xeb\x85\x84 'MMMM d'\xec\x9d\xbc'\0" + "yyyy'\xeb\x85\x84' M'\xec\x9b\x94'\0" + "yyyy'\xeb\x85\x84' MMM'\xec\x9b\x94'\0" + "yyyy'\xeb\x85\x84' MMMM\0" + "d-M-yyyy\0" + "dd.MMM.yyyy\0" + "HH.mm' uur'\0" + "HH:mm' uur'\0" + "HH.mm.ss' uur'\0" + "HH:mm:ss' uur'\0" + "d.MMMM.\0" + "d.M.yy\0" + "dddd, 'ils' d 'da' MMMM yyyy\0" + "d 'da' MMMM yyyy\0" + "d.M.yyyy.\0" + "d.M.yy.\0" + "d. M. yyyy.\0" + "dd.MM.yyyy.\0" + "d. M. yy.\0" + "dd.MM.yy.\0" + "dd. MM. yy.\0" + "d. MMMM yyyy.\0" + "dd. MMMM yyyy.\0" + "dddd, d. MMMM yyyy.\0" + "'den 'd MMMM\0" + "'den 'd MMMM yyyy\0" + "dddd' den 'd MMMM yyyy\0" + "'kl 'H:mm\0" + "'kl 'H:mm:ss\0" + "dd MMM yyyy\0" + "ddd d MMMM yyyy\0" + "'\xe0\xb8\xa7\xe0\xb8\xb1\xe0\xb8\x99'dddd'\xe0\xb8\x97\xe0\xb8\xb5\xe0\xb9\x88' d MMMM gg yyyy\0" + "d.MM.yyyy\0" + "d MMMM yyyy dddd\0" + "dd MMMM, yyyy\0" + "dddd, dd MMMM yyyy\0" + "d MMMM yyyy' \xd1\x80.'\0" + "MMMM yyyy' \xd1\x80.'\0" + "MMMM yyyy \xd0\xb3.\0" + "d. MM. yyyy\0" + "dddd, dd. MMMM yyyy\0" + "dd. MMMM yyyy\0" + "H:mm.ss\0" + "yyyy. 'gada' d. MMM\0" + "dddd, yyyy. 'gada' d. MMMM\0" + "yyyy. 'gada' d. MMMM\0" + "yyyy. 'g'. MMMM\0" + "yyyy 'm'. MMMM d 'd'., dddd\0" + "yyyy 'm'. MMMM d 'd'.\0" + "yyyy MMMM\0" + "d MMMM yyyy' \xd1\x81.'\0" + "dd MMMM yyyy' \xd1\x81.'\0" + "d/MM/yyyy\0" + "d/MMM/yyyy\0" + "d-MMM-yyyy\0" + "dd-MMM-yyyy\0" + "ddd, d-MMMM-yyyy\0" + "ddd, dd-MMMM-yyyy\0" + "d MMMM yyyy, dddd\0" + "yyyy MMM d\0" + "yyyy('e')'ko' MMMM d, dddd\0" + "yyyy('e')'ko' MMMM d\0" + "yyyy('e')'ko' MMMM\0" + "H:mm 'hod\xc5\xba'.\0" + "dd.M.yyyy\0" + "MMMM yyyy '\xd0\xb3'.\0" + "yyyy MMMM d, dddd\0" + "yyyy MMMM d\0" + "MMM d, yyyy\0" + "d MMM, yyyy\0" + "d 'ta'\xe2\x80\x99 MMMM\0" + "dddd, d 'ta'\xe2\x80\x99 MMMM yyyy\0" + "d 'ta'\xe2\x80\x99 MMMM yyyy\0" + "MMMM 'ta'\xe2\x80\x99 yyyy\0" + "yyyy, dd-MMM\0" + "d-MMMM\0" + "d-MMM yy\0" + "dd-MMMM yyyy'-\xd0\xb6.'\0" + "MMMM yyyy'-\xd0\xb6.'\0" + "dd.MM.yy '\xc3\xbd.'\0" + "yyyy'-nji \xc3\xbdyly\xc5\x88 'd'-nji 'MMMM\0" + "yyyy '\xc3\xbd.' MMMM\0" + "dddd, yyyy MMMM dd\0" + "tt hh.mm\0" + "tt h.mm\0" + "tt hh.mm.ss\0" + "tt h.mm.ss\0" + "dd MMMM yyyy dddd\0" + "MMMM dd\0" + "yyyy,MMMM dd, dddd\0" + "MMMM,yy\0" + "MMMM,yyyy\0" + "dddd, yyyy '\xd0\xbe\xd0\xbd\xd1\x8b' MM '\xd1\x81\xd0\xb0\xd1\x80\xd1\x8b\xd0\xbd' d\0" + "yyyy '\xd0\xbe\xd0\xbd\xd1\x8b' MM '\xd1\x81\xd0\xb0\xd1\x80\xd1\x8b\xd0\xbd' d\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8bM\xe0\xbd\x9a\xe0\xbd\xba\xe0\xbd\xa6\xe0\xbc\x8b\x64\0" + "yyyy'\xe0\xbd\xa3\xe0\xbd\xbc\xe0\xbd\xa0\xe0\xbd\xb2\xe0\xbc\x8b\xe0\xbd\x9f\xe0\xbe\xb3' M'\xe0\xbd\x9a\xe0\xbd\xba\xe0\xbd\xa6' d\0" + "yyyy'\xe0\xbd\xa3\xe0\xbd\xbc\xe0\xbd\xa0\xe0\xbd\xb2\xe0\xbc\x8b\xe0\xbd\x9f\xe0\xbe\xb3' M'\xe0\xbd\x9a\xe0\xbd\xba\xe0\xbd\xa6' d dddd\0" + "yyyy\xe0\xbd\xa3\xe0\xbd\xbc\xe0\xbd\xa0\xe0\xbd\xb2\xe0\xbc\x8b\xe0\xbd\x9f\xe0\xbe\xb3 MMM d\0" + "yyyy\xe0\xbd\xa3\xe0\xbd\xbc\xe0\xbd\xa0\xe0\xbd\xb2\xe0\xbc\x8b\xe0\xbd\x9f\xe0\xbe\xb3 MMM d dddd\0" + "yyyy'\xe0\xbd\xa3\xe0\xbd\xbc\xe0\xbd\xa0\xe0\xbd\xb2\xe0\xbc\x8b\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b' M\0" + "'\xe1\x9e\x81\xe1\x9f\x82' MM '\xe1\x9e\x86\xe1\x9f\x92\xe1\x9e\x93\xe1\x9e\xb6\xe1\x9f\x86' yyyy\0" + "dddd \xe0\xba\x97\xe0\xba\xb5 d MMMM gg yyyy\0" + "dddd\xe1\x81\x8a dd MMMM yyyy\0" + "dddd, MMMM dd,yyyy\0" + "MMMM dd,yyyy\0" + "dddd, MMMM dd, yyyy\0" + "MMMM dd, yyyy\0" + "dd. MMMM\0" + "dd. MMMMyyyy\0" + "dddd,' den 'd. MMMM yyyy\0" + "dddd,' den 'dd. MMMM yyyy\0" + "H.mm' Auer'\0" + "H:mm:ss' Auer'\0" + "HH:mm:ss' Auer'\0" + "MMMM d'.-at'\0" + "MMMM d'.-at, 'yyyy\0" + "dddd\xe1\x8d\xa1 dd MMMM \xe1\x88\x98\xe1\x8b\x93\xe1\x88\x8d\xe1\x89\xb2 yyyy gg\0" + "M\xe2\x80\x99 \xea\x86\xaa\xe2\x80\x99\x64\xe2\x80\x99 \xea\x91\x8d\xe2\x80\x99\0" + "yyyy'\xea\x88\x8e' M'\xea\x86\xaa' d'\xea\x91\x8d'\0" + "dddd, yyyy'\xea\x88\x8e' M'\xea\x86\xaa' d'\xea\x91\x8d'\0" + "yyyy'\xea\x88\x8e' M'\xea\x86\xaa' d'\xea\x91\x8d', dddd\0" + "yyyy\xea\x88\x8e MMM d\xea\x91\x8d\0" + "dddd, yyyy\xea\x88\x8e MMM d\xea\x91\x8d\0" + "yyyy'\xea\x88\x8e' M'\xea\x86\xaa'\0" + "yyyy-'\xd9\x8a\xd9\x89\xd9\x84' d-MMMM\0" + "yyyy-'\xd9\x8a\xd9\x89\xd9\x84' d-MMMM dddd\0" + "yyyy-'\xd9\x8a\xd9\x89\xd9\x84\xd9\x89' MMM'\xd9\x86\xd9\x89\xda\xad' d'-\xd9\x83\xdb\x88\xd9\x86\xd9\x89'\0" + "yyyy-'\xd9\x8a\xd9\x89\xd9\x84\xd9\x89' MMM'\xd9\x86\xd9\x89\xda\xad' d'-\xd9\x83\xdb\x88\xd9\x86\xd9\x89' dddd\0" + "yyyy-M-d dddd\0" + "yyyy-'\xd9\x8a\xd9\x89\xd9\x84\xd9\x89' MMMM\0" + "MMMM d \xd0\xba\xd2\xaf\xd0\xbd\xd1\x8d\0" + "yyyy MM d\0" + "dd yyyy MM d\0" + "dddd, yyyy '\xd1\x81.' MMMM d '\xd0\xba\xd2\xaf\xd0\xbd\xd1\x8d'\0" + "yyyy '\xd1\x81.' MMMM d '\xd0\xba\xd2\xaf\xd0\xbd\xd1\x8d'\0" + "dddd, MMMM d '\xd0\xba\xd2\xaf\xd0\xbd\xd1\x8d' yyyy '\xd1\x81.'\0" + "yyyy '\xd1\x81.' MMMM\0" + "d'mh' MMMM\0" + "dddd, d'mh' MMMM yyyy\0" + "d'mh' MMMM yyyy\0" + "dddd yyyy\xe5\xb9\xb4MMMd\xe6\x97\xa5\0" + "dddd\xe1\x8d\xa3 dd MMMM \xe1\x88\x98\xe1\x8b\x93\xe1\x88\x8d\xe1\x89\xb2 yyyy gg\0" + "dd.MMMM.\0" + "dd.MMMM\0" + "MMMM.yyyy\0" + "H.mm' u.'\0" + "yy.MM.dd\0" + "d MMM yyyy '\xd0\xb3'.\0" + "dddd, d MMMM yyyy '\xd0\xb3'.\0" + "d MMMM yyyy '\xd0\xb3'.\0" + "d. M. yy\0" + "H:mm' g\xc3\xb3\xc5\xba.'\0" + "'zeger 'H:mm\0" + "H:mm:ss' g\xc3\xb3\xc5\xba.'\0" + "'zeger 'H:mm:ss\0" + "MMMM d'. b.'\0" + "dddd, MMMM d'. b. 'yyyy\0" + "MMMM d'. b. 'yyyy\0" + "h.mm tt\0" + "h.mm.ss tt\0" + "yy MM dd\0" + "dddd', 'MMMM d'. b. 'yyyy\0" + "M/dd/yy\0" + "MMMM-dd-yy\0" + "dd-MMMM\0" + "dddd, d 'de' MMMM 'de' yyyy\0" + "d 'de' MMMM 'de' yyyy\0" + "d. MMM yyyy.\0" + "MMMM yyyy.\0" + "dddd yyyy'\xe5\xb9\xb4'M'\xe6\x9c\x88'd'\xe6\x97\xa5'\0" + "dddd yyyy MM dd\0" + "d/MMMM\0" + "MMMM/yyyy\0" + "dd. MMM. yyyy.\0" + "dddd, dd. MMMM yyyy.\0" + "MMMM/dd\0" + "dd. MM. yy\0" + "MMMM d'. p. '\0" + "MMMM d'. p. 'yyyy\0" + "MMMM-yyyy\0" + "dd MMM,yyyy\0" + "yyyy-MM-dd.\0" + "dddd dd 'de' MMMM 'de' yyyy\0" + "dd 'de' MMMM 'de' yyyy\0" +}; + + +static const char datetime_strings [] = { + "\0" + "\xd9\x85\xd8\xad\xd8\xb1\xd9\x85\0" + "\xd8\xb5\xd9\x81\xd8\xb1\0" + "\xd8\xb1\xd8\xa8\xd9\x8a\xd8\xb9 \xd8\xa7\xd9\x84\xd8\xa3\xd9\x88\xd9\x84\0" + "\xd8\xb1\xd8\xa8\xd9\x8a\xd8\xb9 \xd8\xa7\xd9\x84\xd8\xa2\xd8\xae\xd8\xb1\0" + "\xd8\xac\xd9\x85\xd8\xa7\xd8\xaf\xd9\x89 \xd8\xa7\xd9\x84\xd8\xa3\xd9\x88\xd9\x84\xd9\x89\0" + "\xd8\xac\xd9\x85\xd8\xa7\xd8\xaf\xd9\x89 \xd8\xa7\xd9\x84\xd8\xa2\xd8\xae\xd8\xb1\xd8\xa9\0" + "\xd8\xb1\xd8\xac\xd8\xa8\0" + "\xd8\xb4\xd8\xb9\xd8\xa8\xd8\xa7\xd9\x86\0" + "\xd8\xb1\xd9\x85\xd8\xb6\xd8\xa7\xd9\x86\0" + "\xd8\xb4\xd9\x88\xd8\xa7\xd9\x84\0" + "\xd8\xb0\xd9\x88 \xd8\xa7\xd9\x84\xd9\x82\xd8\xb9\xd8\xaf\xd8\xa9\0" + "\xd8\xb0\xd9\x88 \xd8\xa7\xd9\x84\xd8\xad\xd8\xac\xd8\xa9\0" + "\xd0\xbd\xd0\xb5\xd0\xb4\xd0\xb5\xd0\xbb\xd1\x8f\0" + "\xd0\xbf\xd0\xbe\xd0\xbd\xd0\xb5\xd0\xb4\xd0\xb5\xd0\xbb\xd0\xbd\xd0\xb8\xd0\xba\0" + "\xd0\xb2\xd1\x82\xd0\xbe\xd1\x80\xd0\xbd\xd0\xb8\xd0\xba\0" + "\xd1\x81\xd1\x80\xd1\x8f\xd0\xb4\xd0\xb0\0" + "\xd1\x87\xd0\xb5\xd1\x82\xd0\xb2\xd1\x8a\xd1\x80\xd1\x82\xd1\x8a\xd0\xba\0" + "\xd0\xbf\xd0\xb5\xd1\x82\xd1\x8a\xd0\xba\0" + "\xd1\x81\xd1\x8a\xd0\xb1\xd0\xbe\xd1\x82\xd0\xb0\0" + "\xd0\xbd\xd0\xb4\0" + "\xd0\xbf\xd0\xbd\0" + "\xd0\xb2\xd1\x82\0" + "\xd1\x81\xd1\x80\0" + "\xd1\x87\xd1\x82\0" + "\xd0\xbf\xd1\x82\0" + "\xd1\x81\xd0\xb1\0" + "\xd0\xbd\0" + "\xd0\xbf\0" + "\xd0\xb2\0" + "\xd1\x81\0" + "\xd1\x87\0" + "\xd1\x8f\xd0\xbd\xd1\x83\xd0\xb0\xd1\x80\xd0\xb8\0" + "\xd1\x84\xd0\xb5\xd0\xb2\xd1\x80\xd1\x83\xd0\xb0\xd1\x80\xd0\xb8\0" + "\xd0\xbc\xd0\xb0\xd1\x80\xd1\x82\0" + "\xd0\xb0\xd0\xbf\xd1\x80\xd0\xb8\xd0\xbb\0" + "\xd0\xbc\xd0\xb0\xd0\xb9\0" + "\xd1\x8e\xd0\xbd\xd0\xb8\0" + "\xd1\x8e\xd0\xbb\xd0\xb8\0" + "\xd0\xb0\xd0\xb2\xd0\xb3\xd1\x83\xd1\x81\xd1\x82\0" + "\xd1\x81\xd0\xb5\xd0\xbf\xd1\x82\xd0\xb5\xd0\xbc\xd0\xb2\xd1\x80\xd0\xb8\0" + "\xd0\xbe\xd0\xba\xd1\x82\xd0\xbe\xd0\xbc\xd0\xb2\xd1\x80\xd0\xb8\0" + "\xd0\xbd\xd0\xbe\xd0\xb5\xd0\xbc\xd0\xb2\xd1\x80\xd0\xb8\0" + "\xd0\xb4\xd0\xb5\xd0\xba\xd0\xb5\xd0\xbc\xd0\xb2\xd1\x80\xd0\xb8\0" + "\xd1\x8f\xd0\xbd\xd1\x83\0" + "\xd1\x84\xd0\xb5\xd0\xb2\0" + "\xd0\xb0\xd0\xbf\xd1\x80\0" + "\xd0\xb0\xd0\xb2\xd0\xb3\0" + "\xd1\x81\xd0\xb5\xd0\xbf\0" + "\xd0\xbe\xd0\xba\xd1\x82\0" + "\xd0\xbd\xd0\xbe\xd0\xb5\0" + "\xd0\xb4\xd0\xb5\xd0\xba\0" + "diumenge\0" + "dilluns\0" + "dimarts\0" + "dimecres\0" + "dijous\0" + "divendres\0" + "dissabte\0" + "dg.\0" + "dl.\0" + "dt.\0" + "dc.\0" + "dj.\0" + "dv.\0" + "ds.\0" + "dg\0" + "dl\0" + "dt\0" + "dc\0" + "dj\0" + "dv\0" + "ds\0" + "gener\0" + "febrer\0" + "mar\xc3\xa7\0" + "abril\0" + "maig\0" + "juny\0" + "juliol\0" + "agost\0" + "setembre\0" + "octubre\0" + "novembre\0" + "desembre\0" + "de gener\0" + "de febrer\0" + "de mar\xc3\xa7\0" + "d\xe2\x80\x99\x61\x62ril\0" + "de maig\0" + "de juny\0" + "de juliol\0" + "d\xe2\x80\x99\x61gost\0" + "de setembre\0" + "d\xe2\x80\x99octubre\0" + "de novembre\0" + "de desembre\0" + "gen.\0" + "febr.\0" + "abr.\0" + "jul.\0" + "ag.\0" + "set.\0" + "oct.\0" + "nov.\0" + "des.\0" + "\xe6\x98\x9f\xe6\x9c\x9f\xe6\x97\xa5\0" + "\xe6\x98\x9f\xe6\x9c\x9f\xe4\xb8\x80\0" + "\xe6\x98\x9f\xe6\x9c\x9f\xe4\xba\x8c\0" + "\xe6\x98\x9f\xe6\x9c\x9f\xe4\xb8\x89\0" + "\xe6\x98\x9f\xe6\x9c\x9f\xe5\x9b\x9b\0" + "\xe6\x98\x9f\xe6\x9c\x9f\xe4\xba\x94\0" + "\xe6\x98\x9f\xe6\x9c\x9f\xe5\x85\xad\0" + "\xe5\x91\xa8\xe6\x97\xa5\0" + "\xe5\x91\xa8\xe4\xb8\x80\0" + "\xe5\x91\xa8\xe4\xba\x8c\0" + "\xe5\x91\xa8\xe4\xb8\x89\0" + "\xe5\x91\xa8\xe5\x9b\x9b\0" + "\xe5\x91\xa8\xe4\xba\x94\0" + "\xe5\x91\xa8\xe5\x85\xad\0" + "\xe6\x97\xa5\0" + "\xe4\xb8\x80\0" + "\xe4\xba\x8c\0" + "\xe4\xb8\x89\0" + "\xe5\x9b\x9b\0" + "\xe4\xba\x94\0" + "\xe5\x85\xad\0" + "\xe4\xb8\x80\xe6\x9c\x88\0" + "\xe4\xba\x8c\xe6\x9c\x88\0" + "\xe4\xb8\x89\xe6\x9c\x88\0" + "\xe5\x9b\x9b\xe6\x9c\x88\0" + "\xe4\xba\x94\xe6\x9c\x88\0" + "\xe5\x85\xad\xe6\x9c\x88\0" + "\xe4\xb8\x83\xe6\x9c\x88\0" + "\xe5\x85\xab\xe6\x9c\x88\0" + "\xe4\xb9\x9d\xe6\x9c\x88\0" + "\xe5\x8d\x81\xe6\x9c\x88\0" + "\xe5\x8d\x81\xe4\xb8\x80\xe6\x9c\x88\0" + "\xe5\x8d\x81\xe4\xba\x8c\xe6\x9c\x88\0" + "1\xe6\x9c\x88\0" + "2\xe6\x9c\x88\0" + "3\xe6\x9c\x88\0" + "4\xe6\x9c\x88\0" + "5\xe6\x9c\x88\0" + "6\xe6\x9c\x88\0" + "7\xe6\x9c\x88\0" + "8\xe6\x9c\x88\0" + "9\xe6\x9c\x88\0" + "10\xe6\x9c\x88\0" + "11\xe6\x9c\x88\0" + "12\xe6\x9c\x88\0" + "ned\xc4\x9ble\0" + "pond\xc4\x9bl\xc3\xad\0" + "\xc3\xbater\xc3\xbd\0" + "st\xc5\x99\x65\x64\x61\0" + "\xc4\x8dtvrtek\0" + "p\xc3\xa1tek\0" + "sobota\0" + "ne\0" + "po\0" + "\xc3\xbat\0" + "st\0" + "\xc4\x8dt\0" + "p\xc3\xa1\0" + "so\0" + "N\0" + "P\0" + "\xc3\x9a\0" + "S\0" + "\xc4\x8c\0" + "leden\0" + "\xc3\xbanor\0" + "b\xc5\x99\x65zen\0" + "duben\0" + "kv\xc4\x9bten\0" + "\xc4\x8d\x65rven\0" + "\xc4\x8d\x65rvenec\0" + "srpen\0" + "z\xc3\xa1\xc5\x99\xc3\xad\0" + "\xc5\x99\xc3\xadjen\0" + "listopad\0" + "prosinec\0" + "ledna\0" + "\xc3\xbanora\0" + "b\xc5\x99\x65zna\0" + "dubna\0" + "kv\xc4\x9btna\0" + "\xc4\x8d\x65rvna\0" + "\xc4\x8d\x65rvence\0" + "srpna\0" + "\xc5\x99\xc3\xadjna\0" + "listopadu\0" + "prosince\0" + "led\0" + "\xc3\xbano\0" + "b\xc5\x99\x65\0" + "dub\0" + "kv\xc4\x9b\0" + "\xc4\x8dvn\0" + "\xc4\x8dvc\0" + "srp\0" + "z\xc3\xa1\xc5\x99\0" + "\xc5\x99\xc3\xadj\0" + "lis\0" + "pro\0" + "s\xc3\xb8ndag\0" + "mandag\0" + "tirsdag\0" + "onsdag\0" + "torsdag\0" + "fredag\0" + "l\xc3\xb8rdag\0" + "s\xc3\xb8n\0" + "man\0" + "tir\0" + "ons\0" + "tor\0" + "fre\0" + "l\xc3\xb8r\0" + "M\0" + "T\0" + "O\0" + "F\0" + "L\0" + "januar\0" + "februar\0" + "marts\0" + "april\0" + "maj\0" + "juni\0" + "juli\0" + "august\0" + "september\0" + "oktober\0" + "november\0" + "december\0" + "jan.\0" + "feb.\0" + "mar.\0" + "apr.\0" + "jun.\0" + "aug.\0" + "sep.\0" + "okt.\0" + "dec.\0" + "Sonntag\0" + "Montag\0" + "Dienstag\0" + "Mittwoch\0" + "Donnerstag\0" + "Freitag\0" + "Samstag\0" + "So\0" + "Mo\0" + "Di\0" + "Mi\0" + "Do\0" + "Fr\0" + "Sa\0" + "D\0" + "Januar\0" + "Februar\0" + "M\xc3\xa4rz\0" + "April\0" + "Mai\0" + "Juni\0" + "Juli\0" + "August\0" + "September\0" + "Oktober\0" + "November\0" + "Dezember\0" + "Jan\0" + "Feb\0" + "M\xc3\xa4r\0" + "Apr\0" + "Jun\0" + "Jul\0" + "Aug\0" + "Sep\0" + "Okt\0" + "Nov\0" + "Dez\0" + "\xce\x9a\xcf\x85\xcf\x81\xce\xb9\xce\xb1\xce\xba\xce\xae\0" + "\xce\x94\xce\xb5\xcf\x85\xcf\x84\xce\xad\xcf\x81\xce\xb1\0" + "\xce\xa4\xcf\x81\xce\xaf\xcf\x84\xce\xb7\0" + "\xce\xa4\xce\xb5\xcf\x84\xce\xac\xcf\x81\xcf\x84\xce\xb7\0" + "\xce\xa0\xce\xad\xce\xbc\xcf\x80\xcf\x84\xce\xb7\0" + "\xce\xa0\xce\xb1\xcf\x81\xce\xb1\xcf\x83\xce\xba\xce\xb5\xcf\x85\xce\xae\0" + "\xce\xa3\xce\xac\xce\xb2\xce\xb2\xce\xb1\xcf\x84\xce\xbf\0" + "\xce\x9a\xcf\x85\xcf\x81\0" + "\xce\x94\xce\xb5\xcf\x85\0" + "\xce\xa4\xcf\x81\xce\xaf\0" + "\xce\xa4\xce\xb5\xcf\x84\0" + "\xce\xa0\xce\xad\xce\xbc\0" + "\xce\xa0\xce\xb1\xcf\x81\0" + "\xce\xa3\xce\xac\xce\xb2\0" + "\xce\x9a\0" + "\xce\x94\0" + "\xce\xa4\0" + "\xce\xa0\0" + "\xce\xa3\0" + "\xce\x99\xce\xb1\xce\xbd\xce\xbf\xcf\x85\xce\xac\xcf\x81\xce\xb9\xce\xbf\xcf\x82\0" + "\xce\xa6\xce\xb5\xce\xb2\xcf\x81\xce\xbf\xcf\x85\xce\xac\xcf\x81\xce\xb9\xce\xbf\xcf\x82\0" + "\xce\x9c\xce\xac\xcf\x81\xcf\x84\xce\xb9\xce\xbf\xcf\x82\0" + "\xce\x91\xcf\x80\xcf\x81\xce\xaf\xce\xbb\xce\xb9\xce\xbf\xcf\x82\0" + "\xce\x9c\xce\xac\xce\xb9\xce\xbf\xcf\x82\0" + "\xce\x99\xce\xbf\xcf\x8d\xce\xbd\xce\xb9\xce\xbf\xcf\x82\0" + "\xce\x99\xce\xbf\xcf\x8d\xce\xbb\xce\xb9\xce\xbf\xcf\x82\0" + "\xce\x91\xcf\x8d\xce\xb3\xce\xbf\xcf\x85\xcf\x83\xcf\x84\xce\xbf\xcf\x82\0" + "\xce\xa3\xce\xb5\xcf\x80\xcf\x84\xce\xad\xce\xbc\xce\xb2\xcf\x81\xce\xb9\xce\xbf\xcf\x82\0" + "\xce\x9f\xce\xba\xcf\x84\xcf\x8e\xce\xb2\xcf\x81\xce\xb9\xce\xbf\xcf\x82\0" + "\xce\x9d\xce\xbf\xce\xad\xce\xbc\xce\xb2\xcf\x81\xce\xb9\xce\xbf\xcf\x82\0" + "\xce\x94\xce\xb5\xce\xba\xce\xad\xce\xbc\xce\xb2\xcf\x81\xce\xb9\xce\xbf\xcf\x82\0" + "\xce\x99\xce\xb1\xce\xbd\xce\xbf\xcf\x85\xce\xb1\xcf\x81\xce\xaf\xce\xbf\xcf\x85\0" + "\xce\xa6\xce\xb5\xce\xb2\xcf\x81\xce\xbf\xcf\x85\xce\xb1\xcf\x81\xce\xaf\xce\xbf\xcf\x85\0" + "\xce\x9c\xce\xb1\xcf\x81\xcf\x84\xce\xaf\xce\xbf\xcf\x85\0" + "\xce\x91\xcf\x80\xcf\x81\xce\xb9\xce\xbb\xce\xaf\xce\xbf\xcf\x85\0" + "\xce\x9c\xce\xb1\xce\x90\xce\xbf\xcf\x85\0" + "\xce\x99\xce\xbf\xcf\x85\xce\xbd\xce\xaf\xce\xbf\xcf\x85\0" + "\xce\x99\xce\xbf\xcf\x85\xce\xbb\xce\xaf\xce\xbf\xcf\x85\0" + "\xce\x91\xcf\x85\xce\xb3\xce\xbf\xcf\x8d\xcf\x83\xcf\x84\xce\xbf\xcf\x85\0" + "\xce\xa3\xce\xb5\xcf\x80\xcf\x84\xce\xb5\xce\xbc\xce\xb2\xcf\x81\xce\xaf\xce\xbf\xcf\x85\0" + "\xce\x9f\xce\xba\xcf\x84\xcf\x89\xce\xb2\xcf\x81\xce\xaf\xce\xbf\xcf\x85\0" + "\xce\x9d\xce\xbf\xce\xb5\xce\xbc\xce\xb2\xcf\x81\xce\xaf\xce\xbf\xcf\x85\0" + "\xce\x94\xce\xb5\xce\xba\xce\xb5\xce\xbc\xce\xb2\xcf\x81\xce\xaf\xce\xbf\xcf\x85\0" + "\xce\x99\xce\xb1\xce\xbd\0" + "\xce\xa6\xce\xb5\xce\xb2\0" + "\xce\x9c\xce\xac\xcf\x81\0" + "\xce\x91\xcf\x80\xcf\x81\0" + "\xce\x9c\xce\xac\xce\xb9\0" + "\xce\x99\xce\xbf\xcf\x8d\xce\xbd\0" + "\xce\x99\xce\xbf\xcf\x8d\xce\xbb\0" + "\xce\x91\xcf\x8d\xce\xb3\0" + "\xce\xa3\xce\xb5\xcf\x80\0" + "\xce\x9f\xce\xba\xcf\x84\0" + "\xce\x9d\xce\xbf\xce\xad\0" + "\xce\x94\xce\xb5\xce\xba\0" + "Sunday\0" + "Monday\0" + "Tuesday\0" + "Wednesday\0" + "Thursday\0" + "Friday\0" + "Saturday\0" + "Sun\0" + "Mon\0" + "Tue\0" + "Wed\0" + "Thu\0" + "Fri\0" + "Sat\0" + "W\0" + "January\0" + "February\0" + "March\0" + "May\0" + "June\0" + "July\0" + "October\0" + "December\0" + "Mar\0" + "Oct\0" + "Dec\0" + "domingo\0" + "lunes\0" + "martes\0" + "mi\xc3\xa9rcoles\0" + "jueves\0" + "viernes\0" + "s\xc3\xa1\x62\x61\x64o\0" + "dom.\0" + "lun.\0" + "mi\xc3\xa9.\0" + "jue.\0" + "vie.\0" + "s\xc3\xa1\x62.\0" + "X\0" + "J\0" + "V\0" + "enero\0" + "febrero\0" + "marzo\0" + "mayo\0" + "junio\0" + "julio\0" + "agosto\0" + "septiembre\0" + "noviembre\0" + "diciembre\0" + "ene.\0" + "may.\0" + "ago.\0" + "sept.\0" + "dic.\0" + "sunnuntaina\0" + "maanantaina\0" + "tiistaina\0" + "keskiviikkona\0" + "torstaina\0" + "perjantaina\0" + "lauantaina\0" + "su\0" + "ma\0" + "ti\0" + "ke\0" + "to\0" + "pe\0" + "la\0" + "K\0" + "tammikuu\0" + "helmikuu\0" + "maaliskuu\0" + "huhtikuu\0" + "toukokuu\0" + "kes\xc3\xa4kuu\0" + "hein\xc3\xa4kuu\0" + "elokuu\0" + "syyskuu\0" + "lokakuu\0" + "marraskuu\0" + "joulukuu\0" + "tammikuuta\0" + "helmikuuta\0" + "maaliskuuta\0" + "huhtikuuta\0" + "toukokuuta\0" + "kes\xc3\xa4kuuta\0" + "hein\xc3\xa4kuuta\0" + "elokuuta\0" + "syyskuuta\0" + "lokakuuta\0" + "marraskuuta\0" + "joulukuuta\0" + "tammi\0" + "helmi\0" + "maalis\0" + "huhti\0" + "touko\0" + "kes\xc3\xa4\0" + "hein\xc3\xa4\0" + "elo\0" + "syys\0" + "loka\0" + "marras\0" + "joulu\0" + "dimanche\0" + "lundi\0" + "mardi\0" + "mercredi\0" + "jeudi\0" + "vendredi\0" + "samedi\0" + "dim.\0" + "mer.\0" + "jeu.\0" + "ven.\0" + "sam.\0" + "janvier\0" + "f\xc3\xa9vrier\0" + "mars\0" + "avril\0" + "mai\0" + "juin\0" + "juillet\0" + "ao\xc3\xbbt\0" + "septembre\0" + "octobre\0" + "d\xc3\xa9\x63\x65mbre\0" + "janv.\0" + "f\xc3\xa9vr.\0" + "avr.\0" + "juil.\0" + "d\xc3\xa9\x63.\0" + "\xd7\x99\xd7\x95\xd7\x9d \xd7\xa8\xd7\x90\xd7\xa9\xd7\x95\xd7\x9f\0" + "\xd7\x99\xd7\x95\xd7\x9d \xd7\xa9\xd7\xa0\xd7\x99\0" + "\xd7\x99\xd7\x95\xd7\x9d \xd7\xa9\xd7\x9c\xd7\x99\xd7\xa9\xd7\x99\0" + "\xd7\x99\xd7\x95\xd7\x9d \xd7\xa8\xd7\x91\xd7\x99\xd7\xa2\xd7\x99\0" + "\xd7\x99\xd7\x95\xd7\x9d \xd7\x97\xd7\x9e\xd7\x99\xd7\xa9\xd7\x99\0" + "\xd7\x99\xd7\x95\xd7\x9d \xd7\xa9\xd7\x99\xd7\xa9\xd7\x99\0" + "\xd7\x99\xd7\x95\xd7\x9d \xd7\xa9\xd7\x91\xd7\xaa\0" + "\xd7\x99\xd7\x95\xd7\x9d \xd7\x90\xd7\xb3\0" + "\xd7\x99\xd7\x95\xd7\x9d \xd7\x91\xd7\xb3\0" + "\xd7\x99\xd7\x95\xd7\x9d \xd7\x92\xd7\xb3\0" + "\xd7\x99\xd7\x95\xd7\x9d \xd7\x93\xd7\xb3\0" + "\xd7\x99\xd7\x95\xd7\x9d \xd7\x94\xd7\xb3\0" + "\xd7\x99\xd7\x95\xd7\x9d \xd7\x95\xd7\xb3\0" + "\xd7\xa9\xd7\x91\xd7\xaa\0" + "\xd7\x90\xd7\xb3\0" + "\xd7\x91\xd7\xb3\0" + "\xd7\x92\xd7\xb3\0" + "\xd7\x93\xd7\xb3\0" + "\xd7\x94\xd7\xb3\0" + "\xd7\x95\xd7\xb3\0" + "\xd7\xa9\xd7\xb3\0" + "\xd7\x99\xd7\xa0\xd7\x95\xd7\x90\xd7\xa8\0" + "\xd7\xa4\xd7\x91\xd7\xa8\xd7\x95\xd7\x90\xd7\xa8\0" + "\xd7\x9e\xd7\xa8\xd7\xa5\0" + "\xd7\x90\xd7\xa4\xd7\xa8\xd7\x99\xd7\x9c\0" + "\xd7\x9e\xd7\x90\xd7\x99\0" + "\xd7\x99\xd7\x95\xd7\xa0\xd7\x99\0" + "\xd7\x99\xd7\x95\xd7\x9c\xd7\x99\0" + "\xd7\x90\xd7\x95\xd7\x92\xd7\x95\xd7\xa1\xd7\x98\0" + "\xd7\xa1\xd7\xa4\xd7\x98\xd7\x9e\xd7\x91\xd7\xa8\0" + "\xd7\x90\xd7\x95\xd7\xa7\xd7\x98\xd7\x95\xd7\x91\xd7\xa8\0" + "\xd7\xa0\xd7\x95\xd7\x91\xd7\x9e\xd7\x91\xd7\xa8\0" + "\xd7\x93\xd7\xa6\xd7\x9e\xd7\x91\xd7\xa8\0" + "\xd7\x99\xd7\xa0\xd7\x95\xd7\xb3\0" + "\xd7\xa4\xd7\x91\xd7\xa8\xd7\xb3\0" + "\xd7\x90\xd7\xa4\xd7\xa8\xd7\xb3\0" + "\xd7\x90\xd7\x95\xd7\x92\xd7\xb3\0" + "\xd7\xa1\xd7\xa4\xd7\x98\xd7\xb3\0" + "\xd7\x90\xd7\x95\xd7\xa7\xd7\xb3\0" + "\xd7\xa0\xd7\x95\xd7\x91\xd7\xb3\0" + "\xd7\x93\xd7\xa6\xd7\x9e\xd7\xb3\0" + "vas\xc3\xa1rnap\0" + "h\xc3\xa9tf\xc5\x91\0" + "kedd\0" + "szerda\0" + "cs\xc3\xbct\xc3\xb6rt\xc3\xb6k\0" + "p\xc3\xa9ntek\0" + "szombat\0" + "H\0" + "Sze\0" + "Cs\0" + "Szo\0" + "Sz\0" + "janu\xc3\xa1r\0" + "febru\xc3\xa1r\0" + "m\xc3\xa1rcius\0" + "\xc3\xa1prilis\0" + "m\xc3\xa1jus\0" + "j\xc3\xbanius\0" + "j\xc3\xbalius\0" + "augusztus\0" + "szeptember\0" + "okt\xc3\xb3\x62\x65r\0" + "m\xc3\xa1rc.\0" + "\xc3\xa1pr.\0" + "m\xc3\xa1j.\0" + "j\xc3\xban.\0" + "j\xc3\xbal.\0" + "szept.\0" + "sunnudagur\0" + "m\xc3\xa1nudagur\0" + "\xc3\xberi\xc3\xb0judagur\0" + "mi\xc3\xb0vikudagur\0" + "fimmtudagur\0" + "f\xc3\xb6studagur\0" + "laugardagur\0" + "sun.\0" + "m\xc3\xa1n.\0" + "\xc3\xberi.\0" + "mi\xc3\xb0.\0" + "fim.\0" + "f\xc3\xb6s.\0" + "lau.\0" + "\xc3\x9e\0" + "jan\xc3\xba\x61r\0" + "febr\xc3\xba\x61r\0" + "apr\xc3\xadl\0" + "ma\xc3\xad\0" + "j\xc3\xban\xc3\xad\0" + "j\xc3\xbal\xc3\xad\0" + "\xc3\xa1g\xc3\xbast\0" + "n\xc3\xb3vember\0" + "desember\0" + "\xc3\xa1g\xc3\xba.\0" + "n\xc3\xb3v.\0" + "domenica\0" + "luned\xc3\xac\0" + "marted\xc3\xac\0" + "mercoled\xc3\xac\0" + "gioved\xc3\xac\0" + "venerd\xc3\xac\0" + "sabato\0" + "dom\0" + "lun\0" + "mar\0" + "mer\0" + "gio\0" + "ven\0" + "sab\0" + "G\0" + "gennaio\0" + "febbraio\0" + "aprile\0" + "maggio\0" + "giugno\0" + "luglio\0" + "settembre\0" + "ottobre\0" + "dicembre\0" + "gen\0" + "feb\0" + "apr\0" + "mag\0" + "giu\0" + "lug\0" + "ago\0" + "set\0" + "ott\0" + "nov\0" + "dic\0" + "\xe6\x97\xa5\xe6\x9b\x9c\xe6\x97\xa5\0" + "\xe6\x9c\x88\xe6\x9b\x9c\xe6\x97\xa5\0" + "\xe7\x81\xab\xe6\x9b\x9c\xe6\x97\xa5\0" + "\xe6\xb0\xb4\xe6\x9b\x9c\xe6\x97\xa5\0" + "\xe6\x9c\xa8\xe6\x9b\x9c\xe6\x97\xa5\0" + "\xe9\x87\x91\xe6\x9b\x9c\xe6\x97\xa5\0" + "\xe5\x9c\x9f\xe6\x9b\x9c\xe6\x97\xa5\0" + "\xe6\x9c\x88\0" + "\xe7\x81\xab\0" + "\xe6\xb0\xb4\0" + "\xe6\x9c\xa8\0" + "\xe9\x87\x91\0" + "\xe5\x9c\x9f\0" + "1\0" + "2\0" + "3\0" + "4\0" + "5\0" + "6\0" + "7\0" + "8\0" + "9\0" + "10\0" + "11\0" + "12\0" + "\xec\x9d\xbc\xec\x9a\x94\xec\x9d\xbc\0" + "\xec\x9b\x94\xec\x9a\x94\xec\x9d\xbc\0" + "\xed\x99\x94\xec\x9a\x94\xec\x9d\xbc\0" + "\xec\x88\x98\xec\x9a\x94\xec\x9d\xbc\0" + "\xeb\xaa\xa9\xec\x9a\x94\xec\x9d\xbc\0" + "\xea\xb8\x88\xec\x9a\x94\xec\x9d\xbc\0" + "\xed\x86\xa0\xec\x9a\x94\xec\x9d\xbc\0" + "\xec\x9d\xbc\0" + "\xec\x9b\x94\0" + "\xed\x99\x94\0" + "\xec\x88\x98\0" + "\xeb\xaa\xa9\0" + "\xea\xb8\x88\0" + "\xed\x86\xa0\0" + "1\xec\x9b\x94\0" + "2\xec\x9b\x94\0" + "3\xec\x9b\x94\0" + "4\xec\x9b\x94\0" + "5\xec\x9b\x94\0" + "6\xec\x9b\x94\0" + "7\xec\x9b\x94\0" + "8\xec\x9b\x94\0" + "9\xec\x9b\x94\0" + "10\xec\x9b\x94\0" + "11\xec\x9b\x94\0" + "12\xec\x9b\x94\0" + "zondag\0" + "maandag\0" + "dinsdag\0" + "woensdag\0" + "donderdag\0" + "vrijdag\0" + "zaterdag\0" + "zo\0" + "di\0" + "wo\0" + "do\0" + "vr\0" + "za\0" + "Z\0" + "januari\0" + "februari\0" + "maart\0" + "mei\0" + "augustus\0" + "mrt.\0" + "s\xc3\xb8n.\0" + "man.\0" + "tir.\0" + "ons.\0" + "tor.\0" + "fre.\0" + "l\xc3\xb8r.\0" + "jan\0" + "jun\0" + "jul\0" + "aug\0" + "sep\0" + "okt\0" + "des\0" + "niedziela\0" + "poniedzia\xc5\x82\x65k\0" + "wtorek\0" + "\xc5\x9broda\0" + "czwartek\0" + "pi\xc4\x85tek\0" + "niedz.\0" + "pon.\0" + "wt.\0" + "\xc5\x9br.\0" + "czw.\0" + "pt.\0" + "sob.\0" + "\xc5\x9a\0" + "C\0" + "stycze\xc5\x84\0" + "luty\0" + "marzec\0" + "kwiecie\xc5\x84\0" + "czerwiec\0" + "lipiec\0" + "sierpie\xc5\x84\0" + "wrzesie\xc5\x84\0" + "pa\xc5\xba\x64ziernik\0" + "grudzie\xc5\x84\0" + "stycznia\0" + "lutego\0" + "marca\0" + "kwietnia\0" + "maja\0" + "czerwca\0" + "lipca\0" + "sierpnia\0" + "wrze\xc5\x9bnia\0" + "pa\xc5\xba\x64ziernika\0" + "listopada\0" + "grudnia\0" + "sty\0" + "lut\0" + "kwi\0" + "cze\0" + "lip\0" + "sie\0" + "wrz\0" + "pa\xc5\xba\0" + "gru\0" + "segunda-feira\0" + "ter\xc3\xa7\x61-feira\0" + "quarta-feira\0" + "quinta-feira\0" + "sexta-feira\0" + "seg\0" + "ter\0" + "qua\0" + "qui\0" + "sex\0" + "s\xc3\xa1\x62\0" + "Q\0" + "janeiro\0" + "fevereiro\0" + "mar\xc3\xa7o\0" + "maio\0" + "junho\0" + "julho\0" + "setembro\0" + "outubro\0" + "novembro\0" + "dezembro\0" + "fev\0" + "abr\0" + "out\0" + "dez\0" + "dumengia\0" + "glindesdi\0" + "mesemna\0" + "gievgia\0" + "venderdi\0" + "sonda\0" + "du\0" + "gli\0" + "me\0" + "gie\0" + "ve\0" + "schaner\0" + "favrer\0" + "avrigl\0" + "matg\0" + "zercladur\0" + "fanadur\0" + "avust\0" + "settember\0" + "october\0" + "schan.\0" + "favr.\0" + "zercl.\0" + "fan.\0" + "sett.\0" + "duminic\xc4\x83\0" + "luni\0" + "mar\xc8\x9bi\0" + "miercuri\0" + "joi\0" + "vineri\0" + "s\xc3\xa2mb\xc4\x83t\xc4\x83\0" + "dum.\0" + "mie.\0" + "vin.\0" + "s\xc3\xa2m.\0" + "ianuarie\0" + "februarie\0" + "martie\0" + "aprilie\0" + "iunie\0" + "iulie\0" + "septembrie\0" + "octombrie\0" + "noiembrie\0" + "decembrie\0" + "ian.\0" + "iun.\0" + "iul.\0" + "\xd0\xb2\xd0\xbe\xd1\x81\xd0\xba\xd1\x80\xd0\xb5\xd1\x81\xd0\xb5\xd0\xbd\xd1\x8c\xd0\xb5\0" + "\xd0\xbf\xd0\xbe\xd0\xbd\xd0\xb5\xd0\xb4\xd0\xb5\xd0\xbb\xd1\x8c\xd0\xbd\xd0\xb8\xd0\xba\0" + "\xd1\x81\xd1\x80\xd0\xb5\xd0\xb4\xd0\xb0\0" + "\xd1\x87\xd0\xb5\xd1\x82\xd0\xb2\xd0\xb5\xd1\x80\xd0\xb3\0" + "\xd0\xbf\xd1\x8f\xd1\x82\xd0\xbd\xd0\xb8\xd1\x86\xd0\xb0\0" + "\xd1\x81\xd1\x83\xd0\xb1\xd0\xb1\xd0\xbe\xd1\x82\xd0\xb0\0" + "\xd0\xb2\xd1\x81\0" + "\xd0\x92\0" + "\xd0\x9f\0" + "\xd0\xa1\0" + "\xd0\xa7\0" + "\xd1\x8f\xd0\xbd\xd0\xb2\xd0\xb0\xd1\x80\xd1\x8c\0" + "\xd1\x84\xd0\xb5\xd0\xb2\xd1\x80\xd0\xb0\xd0\xbb\xd1\x8c\0" + "\xd0\xb0\xd0\xbf\xd1\x80\xd0\xb5\xd0\xbb\xd1\x8c\0" + "\xd0\xb8\xd1\x8e\xd0\xbd\xd1\x8c\0" + "\xd0\xb8\xd1\x8e\xd0\xbb\xd1\x8c\0" + "\xd1\x81\xd0\xb5\xd0\xbd\xd1\x82\xd1\x8f\xd0\xb1\xd1\x80\xd1\x8c\0" + "\xd0\xbe\xd0\xba\xd1\x82\xd1\x8f\xd0\xb1\xd1\x80\xd1\x8c\0" + "\xd0\xbd\xd0\xbe\xd1\x8f\xd0\xb1\xd1\x80\xd1\x8c\0" + "\xd0\xb4\xd0\xb5\xd0\xba\xd0\xb0\xd0\xb1\xd1\x80\xd1\x8c\0" + "\xd1\x8f\xd0\xbd\xd0\xb2\xd0\xb0\xd1\x80\xd1\x8f\0" + "\xd1\x84\xd0\xb5\xd0\xb2\xd1\x80\xd0\xb0\xd0\xbb\xd1\x8f\0" + "\xd0\xbc\xd0\xb0\xd1\x80\xd1\x82\xd0\xb0\0" + "\xd0\xb0\xd0\xbf\xd1\x80\xd0\xb5\xd0\xbb\xd1\x8f\0" + "\xd0\xbc\xd0\xb0\xd1\x8f\0" + "\xd0\xb8\xd1\x8e\xd0\xbd\xd1\x8f\0" + "\xd0\xb8\xd1\x8e\xd0\xbb\xd1\x8f\0" + "\xd0\xb0\xd0\xb2\xd0\xb3\xd1\x83\xd1\x81\xd1\x82\xd0\xb0\0" + "\xd1\x81\xd0\xb5\xd0\xbd\xd1\x82\xd1\x8f\xd0\xb1\xd1\x80\xd1\x8f\0" + "\xd0\xbe\xd0\xba\xd1\x82\xd1\x8f\xd0\xb1\xd1\x80\xd1\x8f\0" + "\xd0\xbd\xd0\xbe\xd1\x8f\xd0\xb1\xd1\x80\xd1\x8f\0" + "\xd0\xb4\xd0\xb5\xd0\xba\xd0\xb0\xd0\xb1\xd1\x80\xd1\x8f\0" + "\xd1\x8f\xd0\xbd\xd0\xb2.\0" + "\xd1\x84\xd0\xb5\xd0\xb2\xd1\x80.\0" + "\xd0\xb0\xd0\xbf\xd1\x80.\0" + "\xd0\xb0\xd0\xb2\xd0\xb3.\0" + "\xd1\x81\xd0\xb5\xd0\xbd\xd1\x82.\0" + "\xd0\xbe\xd0\xba\xd1\x82.\0" + "\xd0\xbd\xd0\xbe\xd1\x8f\xd0\xb1.\0" + "\xd0\xb4\xd0\xb5\xd0\xba.\0" + "nedjelja\0" + "ponedjeljak\0" + "utorak\0" + "srijeda\0" + "\xc4\x8d\x65tvrtak\0" + "petak\0" + "subota\0" + "ned\0" + "pon\0" + "uto\0" + "sri\0" + "\xc4\x8d\x65t\0" + "pet\0" + "sub\0" + "n\0" + "p\0" + "u\0" + "s\0" + "\xc4\x8d\0" + "sije\xc4\x8d\x61nj\0" + "velja\xc4\x8d\x61\0" + "o\xc5\xbeujak\0" + "travanj\0" + "svibanj\0" + "lipanj\0" + "srpanj\0" + "kolovoz\0" + "rujan\0" + "studeni\0" + "prosinac\0" + "sije\xc4\x8dnja\0" + "velja\xc4\x8d\x65\0" + "o\xc5\xbeujka\0" + "travnja\0" + "svibnja\0" + "lipnja\0" + "srpnja\0" + "kolovoza\0" + "rujna\0" + "studenoga\0" + "prosinca\0" + "sij\0" + "velj\0" + "o\xc5\xbeu\0" + "tra\0" + "svi\0" + "kol\0" + "ruj\0" + "stu\0" + "nede\xc4\xbe\x61\0" + "pondelok\0" + "utorok\0" + "streda\0" + "\xc5\xa1tvrtok\0" + "piatok\0" + "ut\0" + "\xc5\xa1t\0" + "pi\0" + "\xc5\xa1\0" + "marec\0" + "m\xc3\xa1j\0" + "j\xc3\xban\0" + "j\xc3\xbal\0" + "janu\xc3\xa1ra\0" + "febru\xc3\xa1ra\0" + "apr\xc3\xadla\0" + "m\xc3\xa1ja\0" + "j\xc3\xbana\0" + "j\xc3\xbala\0" + "augusta\0" + "septembra\0" + "okt\xc3\xb3\x62ra\0" + "novembra\0" + "decembra\0" + "dec\0" + "e diel\0" + "e h\xc3\xabn\xc3\xab\0" + "e mart\xc3\xab\0" + "e m\xc3\xabrkur\xc3\xab\0" + "e enjte\0" + "e premte\0" + "e shtun\xc3\xab\0" + "Die\0" + "H\xc3\xabn\0" + "M\xc3\xabr\0" + "Enj\0" + "Pre\0" + "Sht\0" + "E\0" + "Janar\0" + "Shkurt\0" + "Mars\0" + "Prill\0" + "Maj\0" + "Qershor\0" + "Korrik\0" + "Gusht\0" + "Shtator\0" + "Tetor\0" + "N\xc3\xabntor\0" + "Dhjetor\0" + "janar\0" + "shkurt\0" + "prill\0" + "qershor\0" + "korrik\0" + "gusht\0" + "shtator\0" + "tetor\0" + "n\xc3\xabntor\0" + "dhjetor\0" + "Shk\0" + "Pri\0" + "Qer\0" + "Kor\0" + "Gsh\0" + "Tet\0" + "N\xc3\xabn\0" + "Dhj\0" + "s\xc3\xb6ndag\0" + "m\xc3\xa5ndag\0" + "tisdag\0" + "l\xc3\xb6rdag\0" + "s\xc3\xb6n\0" + "m\xc3\xa5n\0" + "tis\0" + "tors\0" + "l\xc3\xb6r\0" + "augusti\0" + "Pazar\0" + "Pazartesi\0" + "Sal\xc4\xb1\0" + "\xc3\x87\x61r\xc5\x9f\x61mba\0" + "Per\xc5\x9f\x65mbe\0" + "Cuma\0" + "Cumartesi\0" + "Paz\0" + "Pzt\0" + "Sal\0" + "\xc3\x87\x61r\0" + "Per\0" + "Cum\0" + "Cmt\0" + "\xc3\x87\0" + "Ocak\0" + "\xc5\x9eubat\0" + "Mart\0" + "Nisan\0" + "May\xc4\xb1s\0" + "Haziran\0" + "Temmuz\0" + "A\xc4\x9fustos\0" + "Eyl\xc3\xbcl\0" + "Ekim\0" + "Kas\xc4\xb1m\0" + "Aral\xc4\xb1k\0" + "Oca\0" + "\xc5\x9eub\0" + "Nis\0" + "Haz\0" + "Tem\0" + "A\xc4\x9fu\0" + "Eyl\0" + "Eki\0" + "Kas\0" + "Ara\0" + "\xd8\xa7\xd8\xaa\xd9\x88\xd8\xa7\xd8\xb1\0" + "\xd8\xb3\xd9\x88\xd9\x85\xd9\x88\xd8\xa7\xd8\xb1\0" + "\xd9\x85\xd9\x86\xda\xaf\xd9\x84\0" + "\xd8\xa8\xd8\xaf\xda\xbe\0" + "\xd8\xac\xd9\x85\xd8\xb9\xd8\xb1\xd8\xa7\xd8\xaa\0" + "\xd8\xac\xd9\x85\xd8\xb9\xdb\x81\0" + "\xdb\x81\xd9\x81\xd8\xaa\xdb\x81\0" + "\xd8\xac\xd9\x86\xd9\x88\xd8\xb1\xdb\x8c\0" + "\xd9\x81\xd8\xb1\xd9\x88\xd8\xb1\xdb\x8c\0" + "\xd9\x85\xd8\xa7\xd8\xb1\xda\x86\0" + "\xd8\xa7\xd9\xbe\xd8\xb1\xdb\x8c\xd9\x84\0" + "\xd9\x85\xd8\xa6\xdb\x8c\0" + "\xd8\xac\xd9\x88\xd9\x86\0" + "\xd8\xac\xd9\x88\xd9\x84\xd8\xa7\xd8\xa6\xdb\x8c\0" + "\xd8\xa7\xda\xaf\xd8\xb3\xd8\xaa\0" + "\xd8\xb3\xd8\xaa\xd9\x85\xd8\xa8\xd8\xb1\0" + "\xd8\xa7\xda\xa9\xd8\xaa\xd9\x88\xd8\xa8\xd8\xb1\0" + "\xd9\x86\xd9\x88\xd9\x85\xd8\xa8\xd8\xb1\0" + "\xd8\xaf\xd8\xb3\xd9\x85\xd8\xa8\xd8\xb1\0" + "Minggu\0" + "Senin\0" + "Selasa\0" + "Rabu\0" + "Kamis\0" + "Jumat\0" + "Sabtu\0" + "Min\0" + "Sen\0" + "Sel\0" + "Rab\0" + "Kam\0" + "Jum\0" + "Sab\0" + "R\0" + "Januari\0" + "Februari\0" + "Maret\0" + "Mei\0" + "Agustus\0" + "Desember\0" + "Agt\0" + "Des\0" + "\xd0\xbd\xd0\xb5\xd0\xb4\xd1\x96\xd0\xbb\xd1\x8f\0" + "\xd0\xbf\xd0\xbe\xd0\xbd\xd0\xb5\xd0\xb4\xd1\x96\xd0\xbb\xd0\xbe\xd0\xba\0" + "\xd0\xb2\xd1\x96\xd0\xb2\xd1\x82\xd0\xbe\xd1\x80\xd0\xbe\xd0\xba\0" + "\xd1\x81\xd0\xb5\xd1\x80\xd0\xb5\xd0\xb4\xd0\xb0\0" + "\xd1\x87\xd0\xb5\xd1\x82\xd0\xb2\xd0\xb5\xd1\x80\0" + "\xd0\xbf\xca\xbc\xd1\x8f\xd1\x82\xd0\xbd\xd0\xb8\xd1\x86\xd1\x8f\0" + "\xd1\x81\xd1\x83\xd0\xb1\xd0\xbe\xd1\x82\xd0\xb0\0" + "\xd0\x9d\0" + "\xd1\x81\xd1\x96\xd1\x87\xd0\xb5\xd0\xbd\xd1\x8c\0" + "\xd0\xbb\xd1\x8e\xd1\x82\xd0\xb8\xd0\xb9\0" + "\xd0\xb1\xd0\xb5\xd1\x80\xd0\xb5\xd0\xb7\xd0\xb5\xd0\xbd\xd1\x8c\0" + "\xd0\xba\xd0\xb2\xd1\x96\xd1\x82\xd0\xb5\xd0\xbd\xd1\x8c\0" + "\xd1\x82\xd1\x80\xd0\xb0\xd0\xb2\xd0\xb5\xd0\xbd\xd1\x8c\0" + "\xd1\x87\xd0\xb5\xd1\x80\xd0\xb2\xd0\xb5\xd0\xbd\xd1\x8c\0" + "\xd0\xbb\xd0\xb8\xd0\xbf\xd0\xb5\xd0\xbd\xd1\x8c\0" + "\xd1\x81\xd0\xb5\xd1\x80\xd0\xbf\xd0\xb5\xd0\xbd\xd1\x8c\0" + "\xd0\xb2\xd0\xb5\xd1\x80\xd0\xb5\xd1\x81\xd0\xb5\xd0\xbd\xd1\x8c\0" + "\xd0\xb6\xd0\xbe\xd0\xb2\xd1\x82\xd0\xb5\xd0\xbd\xd1\x8c\0" + "\xd0\xbb\xd0\xb8\xd1\x81\xd1\x82\xd0\xbe\xd0\xbf\xd0\xb0\xd0\xb4\0" + "\xd0\xb3\xd1\x80\xd1\x83\xd0\xb4\xd0\xb5\xd0\xbd\xd1\x8c\0" + "\xd1\x81\xd1\x96\xd1\x87\xd0\xbd\xd1\x8f\0" + "\xd0\xbb\xd1\x8e\xd1\x82\xd0\xbe\xd0\xb3\xd0\xbe\0" + "\xd0\xb1\xd0\xb5\xd1\x80\xd0\xb5\xd0\xb7\xd0\xbd\xd1\x8f\0" + "\xd0\xba\xd0\xb2\xd1\x96\xd1\x82\xd0\xbd\xd1\x8f\0" + "\xd1\x82\xd1\x80\xd0\xb0\xd0\xb2\xd0\xbd\xd1\x8f\0" + "\xd1\x87\xd0\xb5\xd1\x80\xd0\xb2\xd0\xbd\xd1\x8f\0" + "\xd0\xbb\xd0\xb8\xd0\xbf\xd0\xbd\xd1\x8f\0" + "\xd1\x81\xd0\xb5\xd1\x80\xd0\xbf\xd0\xbd\xd1\x8f\0" + "\xd0\xb2\xd0\xb5\xd1\x80\xd0\xb5\xd1\x81\xd0\xbd\xd1\x8f\0" + "\xd0\xb6\xd0\xbe\xd0\xb2\xd1\x82\xd0\xbd\xd1\x8f\0" + "\xd0\xbb\xd0\xb8\xd1\x81\xd1\x82\xd0\xbe\xd0\xbf\xd0\xb0\xd0\xb4\xd0\xb0\0" + "\xd0\xb3\xd1\x80\xd1\x83\xd0\xb4\xd0\xbd\xd1\x8f\0" + "\xd1\x81\xd1\x96\xd1\x87\0" + "\xd0\xbb\xd1\x8e\xd1\x82\0" + "\xd0\xb1\xd0\xb5\xd1\x80\0" + "\xd0\xba\xd0\xb2\xd1\x96\0" + "\xd1\x82\xd1\x80\xd0\xb0\0" + "\xd1\x87\xd0\xb5\xd1\x80\0" + "\xd0\xbb\xd0\xb8\xd0\xbf\0" + "\xd1\x81\xd0\xb5\xd1\x80\0" + "\xd0\xb2\xd0\xb5\xd1\x80\0" + "\xd0\xb6\xd0\xbe\xd0\xb2\0" + "\xd0\xbb\xd0\xb8\xd1\x81\0" + "\xd0\xb3\xd1\x80\xd1\x83\0" + "\xd0\xbd\xd1\x8f\xd0\xb4\xd0\xb7\xd0\xb5\xd0\xbb\xd1\x8f\0" + "\xd0\xbf\xd0\xb0\xd0\xbd\xd1\x8f\xd0\xb4\xd0\xb7\xd0\xb5\xd0\xbb\xd0\xb0\xd0\xba\0" + "\xd0\xb0\xd1\x9e\xd1\x82\xd0\xbe\xd1\x80\xd0\xb0\xd0\xba\0" + "\xd1\x81\xd0\xb5\xd1\x80\xd0\xb0\xd0\xb4\xd0\xb0\0" + "\xd1\x87\xd0\xb0\xd1\x86\xd0\xb2\xd0\xb5\xd1\x80\0" + "\xd0\xbf\xd1\x8f\xd1\x82\xd0\xbd\xd1\x96\xd1\x86\xd0\xb0\0" + "\xd0\xb0\xd1\x9e\0" + "\xd1\x87\xd1\x86\0" + "\xd0\xb0\0" + "\xd1\x81\xd1\x82\xd1\x83\xd0\xb4\xd0\xb7\xd0\xb5\xd0\xbd\xd1\x8c\0" + "\xd0\xbb\xd1\x8e\xd1\x82\xd1\x8b\0" + "\xd1\x81\xd0\xb0\xd0\xba\xd0\xb0\xd0\xb2\xd1\x96\xd0\xba\0" + "\xd0\xba\xd1\x80\xd0\xb0\xd1\x81\xd0\xb0\xd0\xb2\xd1\x96\xd0\xba\0" + "\xd1\x87\xd1\x8d\xd1\x80\xd0\xb2\xd0\xb5\xd0\xbd\xd1\x8c\0" + "\xd0\xbb\xd1\x96\xd0\xbf\xd0\xb5\xd0\xbd\xd1\x8c\0" + "\xd0\xb6\xd0\xbd\xd1\x96\xd0\xb2\xd0\xb5\xd0\xbd\xd1\x8c\0" + "\xd0\xb2\xd0\xb5\xd1\x80\xd0\xb0\xd1\x81\xd0\xb5\xd0\xbd\xd1\x8c\0" + "\xd0\xba\xd0\xb0\xd1\x81\xd1\x82\xd1\x80\xd1\x8b\xd1\x87\xd0\xbd\xd1\x96\xd0\xba\0" + "\xd0\xbb\xd1\x96\xd1\x81\xd1\x82\xd0\xb0\xd0\xbf\xd0\xb0\xd0\xb4\0" + "\xd1\x81\xd0\xbd\xd0\xb5\xd0\xb6\xd0\xb0\xd0\xbd\xd1\x8c\0" + "\xd1\x81\xd1\x82\xd1\x83\xd0\xb4\xd0\xb7\xd0\xb5\xd0\xbd\xd1\x8f\0" + "\xd0\xbb\xd1\x8e\xd1\x82\xd0\xb0\xd0\xb3\xd0\xb0\0" + "\xd1\x81\xd0\xb0\xd0\xba\xd0\xb0\xd0\xb2\xd1\x96\xd0\xba\xd0\xb0\0" + "\xd0\xba\xd1\x80\xd0\xb0\xd1\x81\xd0\xb0\xd0\xb2\xd1\x96\xd0\xba\xd0\xb0\0" + "\xd1\x87\xd1\x8d\xd1\x80\xd0\xb2\xd0\xb5\xd0\xbd\xd1\x8f\0" + "\xd0\xbb\xd1\x96\xd0\xbf\xd0\xb5\xd0\xbd\xd1\x8f\0" + "\xd0\xb6\xd0\xbd\xd1\x96\xd1\x9e\xd0\xbd\xd1\x8f\0" + "\xd0\xb2\xd0\xb5\xd1\x80\xd0\xb0\xd1\x81\xd0\xbd\xd1\x8f\0" + "\xd0\xba\xd0\xb0\xd1\x81\xd1\x82\xd1\x80\xd1\x8b\xd1\x87\xd0\xbd\xd1\x96\xd0\xba\xd0\xb0\0" + "\xd0\xbb\xd1\x96\xd1\x81\xd1\x82\xd0\xb0\xd0\xbf\xd0\xb0\xd0\xb4\xd0\xb0\0" + "\xd1\x81\xd0\xbd\xd0\xb5\xd0\xb6\xd0\xbd\xd1\x8f\0" + "\xd1\x81\xd1\x82\xd1\x83\0" + "\xd1\x81\xd0\xb0\xd0\xba\0" + "\xd0\xba\xd1\x80\xd0\xb0\0" + "\xd1\x87\xd1\x8d\xd1\x80\0" + "\xd0\xbb\xd1\x96\xd0\xbf\0" + "\xd0\xb6\xd0\xbd\xd1\x96\0" + "\xd0\xba\xd0\xb0\xd1\x81\0" + "\xd0\xbb\xd1\x96\xd1\x81\0" + "\xd1\x81\xd0\xbd\xd0\xb5\0" + "nedelja\0" + "ponedeljek\0" + "torek\0" + "sreda\0" + "\xc4\x8d\x65trtek\0" + "petek\0" + "ned.\0" + "sre.\0" + "\xc4\x8d\x65t.\0" + "pet.\0" + "t\0" + "junij\0" + "julij\0" + "avgust\0" + "avg.\0" + "p\xc3\xbchap\xc3\xa4\x65v\0" + "esmasp\xc3\xa4\x65v\0" + "teisip\xc3\xa4\x65v\0" + "kolmap\xc3\xa4\x65v\0" + "neljap\xc3\xa4\x65v\0" + "reede\0" + "laup\xc3\xa4\x65v\0" + "jaanuar\0" + "veebruar\0" + "m\xc3\xa4rts\0" + "aprill\0" + "juuni\0" + "juuli\0" + "oktoober\0" + "detsember\0" + "jaan\0" + "veebr\0" + "sept\0" + "dets\0" + "sv\xc4\x93tdiena\0" + "pirmdiena\0" + "otrdiena\0" + "tre\xc5\xa1\x64iena\0" + "ceturtdiena\0" + "piektdiena\0" + "sestdiena\0" + "Sv\xc4\x93td.\0" + "Pirmd.\0" + "Otrd.\0" + "Tre\xc5\xa1\x64.\0" + "Ceturtd.\0" + "Piektd.\0" + "Sestd.\0" + "janv\xc4\x81ris\0" + "febru\xc4\x81ris\0" + "apr\xc4\xablis\0" + "maijs\0" + "j\xc5\xabnijs\0" + "j\xc5\xablijs\0" + "augusts\0" + "septembris\0" + "oktobris\0" + "novembris\0" + "decembris\0" + "j\xc5\xabn.\0" + "j\xc5\xabl.\0" + "sekmadienis\0" + "pirmadienis\0" + "antradienis\0" + "tre\xc4\x8diadienis\0" + "ketvirtadienis\0" + "penktadienis\0" + "\xc5\xa1\x65\xc5\xa1tadienis\0" + "sk\0" + "pr\0" + "an\0" + "tr\0" + "kt\0" + "pn\0" + "A\0" + "\xc5\xa0\0" + "sausis\0" + "vasaris\0" + "kovas\0" + "balandis\0" + "gegu\xc5\xbe\xc4\x97\0" + "bir\xc5\xbe\x65lis\0" + "liepa\0" + "rugpj\xc5\xabtis\0" + "rugs\xc4\x97jis\0" + "spalis\0" + "lapkritis\0" + "gruodis\0" + "sausio\0" + "vasario\0" + "kovo\0" + "baland\xc5\xbeio\0" + "gegu\xc5\xbe\xc4\x97s\0" + "bir\xc5\xbe\x65lio\0" + "liepos\0" + "rugpj\xc5\xab\xc4\x8dio\0" + "rugs\xc4\x97jo\0" + "spalio\0" + "lapkri\xc4\x8dio\0" + "gruod\xc5\xbeio\0" + "saus.\0" + "vas.\0" + "kov.\0" + "bal.\0" + "geg.\0" + "bir\xc5\xbe.\0" + "liep.\0" + "rugp.\0" + "rugs.\0" + "spal.\0" + "lapkr.\0" + "gruod.\0" + "\xd0\xaf\xd0\xba\xd1\x88\xd0\xb0\xd0\xbd\xd0\xb1\xd0\xb5\0" + "\xd0\x94\xd1\x83\xd1\x88\xd0\xb0\xd0\xbd\xd0\xb1\xd0\xb5\0" + "\xd0\xa1\xd0\xb5\xd1\x88\xd0\xb0\xd0\xbd\xd0\xb1\xd0\xb5\0" + "\xd0\xa7\xd0\xbe\xd1\x80\xd1\x88\xd0\xb0\xd0\xbd\xd0\xb1\xd0\xb5\0" + "\xd0\x9f\xd0\xb0\xd0\xbd\xd2\xb7\xd1\x88\xd0\xb0\xd0\xbd\xd0\xb1\xd0\xb5\0" + "\xd2\xb6\xd1\x83\xd0\xbc\xd1\x8a\xd0\xb0\0" + "\xd0\xa8\xd0\xb0\xd0\xbd\xd0\xb1\xd0\xb5\0" + "\xd0\xaf\xd1\x88\xd0\xb1\0" + "\xd0\x94\xd1\x88\xd0\xb1\0" + "\xd0\xa1\xd1\x88\xd0\xb1\0" + "\xd0\xa7\xd1\x88\xd0\xb1\0" + "\xd0\x9f\xd1\x88\xd0\xb1\0" + "\xd2\xb6\xd0\xbc\xd1\x8a\0" + "\xd0\xa8\xd0\xbd\xd0\xb1\0" + "\xd0\xaf\xd0\xbd\xd0\xb2\xd0\xb0\xd1\x80\0" + "\xd0\xa4\xd0\xb5\xd0\xb2\xd1\x80\xd0\xb0\xd0\xbb\0" + "\xd0\x9c\xd0\xb0\xd1\x80\xd1\x82\0" + "\xd0\x90\xd0\xbf\xd1\x80\xd0\xb5\xd0\xbb\0" + "\xd0\x9c\xd0\xb0\xd0\xb9\0" + "\xd0\x98\xd1\x8e\xd0\xbd\0" + "\xd0\x98\xd1\x8e\xd0\xbb\0" + "\xd0\x90\xd0\xb2\xd0\xb3\xd1\x83\xd1\x81\xd1\x82\0" + "\xd0\xa1\xd0\xb5\xd0\xbd\xd1\x82\xd1\x8f\xd0\xb1\xd1\x80\0" + "\xd0\x9e\xd0\xba\xd1\x82\xd1\x8f\xd0\xb1\xd1\x80\0" + "\xd0\x9d\xd0\xbe\xd1\x8f\xd0\xb1\xd1\x80\0" + "\xd0\x94\xd0\xb5\xd0\xba\xd0\xb0\xd0\xb1\xd1\x80\0" + "\xd0\xaf\xd0\xbd\xd0\xb2\0" + "\xd0\xa4\xd0\xb5\xd0\xb2\0" + "\xd0\x9c\xd0\xb0\xd1\x80\0" + "\xd0\x90\xd0\xbf\xd1\x80\0" + "\xd0\x90\xd0\xb2\xd0\xb3\0" + "\xd0\xa1\xd0\xb5\xd0\xbd\0" + "\xd0\x9e\xd0\xba\xd1\x82\0" + "\xd0\x9d\xd0\xbe\xd1\x8f\0" + "\xd0\x94\xd0\xb5\xd0\xba\0" + "\xdb\x8c\xda\xa9\xd8\xb4\xd9\x86\xd8\xa8\xd9\x87\0" + "\xd8\xaf\xd9\x88\xd8\xb4\xd9\x86\xd8\xa8\xd9\x87\0" + "\xd8\xb3\xd9\x87\xe2\x80\x8c\xd8\xb4\xd9\x86\xd8\xa8\xd9\x87\0" + "\xda\x86\xd9\x87\xd8\xa7\xd8\xb1\xd8\xb4\xd9\x86\xd8\xa8\xd9\x87\0" + "\xd9\xbe\xd9\x86\xd8\xac\xd8\xb4\xd9\x86\xd8\xa8\xd9\x87\0" + "\xd8\xac\xd9\x85\xd8\xb9\xd9\x87\0" + "\xd8\xb4\xd9\x86\xd8\xa8\xd9\x87\0" + "\xdb\x8c\0" + "\xd8\xaf\0" + "\xd8\xb3\0" + "\xda\x86\0" + "\xd9\xbe\0" + "\xd8\xac\0" + "\xd8\xb4\0" + "\xda\x98\xd8\xa7\xd9\x86\xd9\x88\xdb\x8c\xd9\x87\0" + "\xd9\x81\xd9\x88\xd8\xb1\xdb\x8c\xd9\x87\0" + "\xd9\x85\xd8\xa7\xd8\xb1\xd8\xb3\0" + "\xd8\xa2\xd9\x88\xd8\xb1\xdb\x8c\xd9\x84\0" + "\xd9\x85\xd9\x87\0" + "\xda\x98\xd9\x88\xd8\xa6\xd9\x86\0" + "\xda\x98\xd9\x88\xd8\xa6\xdb\x8c\xd9\x87\0" + "\xd8\xa7\xd9\x88\xd8\xaa\0" + "\xd8\xb3\xd9\xbe\xd8\xaa\xd8\xa7\xd9\x85\xd8\xa8\xd8\xb1\0" + "\xd8\xa7\xda\xa9\xd8\xaa\xd8\xa8\xd8\xb1\0" + "\xd9\x86\xd9\x88\xd8\xa7\xd9\x85\xd8\xa8\xd8\xb1\0" + "\xd8\xaf\xd8\xb3\xd8\xa7\xd9\x85\xd8\xa8\xd8\xb1\0" + "\xda\x98\xd8\xa7\xd9\x86\xd9\x88\xdb\x8c\xd9\x87\xd9\x94\0" + "\xd9\x81\xd9\x88\xd8\xb1\xdb\x8c\xd9\x87\xd9\x94\0" + "\xd9\x85\xd9\x87\xd9\x94\0" + "\xda\x98\xd9\x88\xd8\xa6\xdb\x8c\xd9\x87\xd9\x94\0" + "Ch\xe1\xbb\xa7 Nh\xe1\xba\xadt\0" + "Th\xe1\xbb\xa9 Hai\0" + "Th\xe1\xbb\xa9 Ba\0" + "Th\xe1\xbb\xa9 T\xc6\xb0\0" + "Th\xe1\xbb\xa9 N\xc4\x83m\0" + "Th\xe1\xbb\xa9 S\xc3\xa1u\0" + "Th\xe1\xbb\xa9 B\xe1\xba\xa3y\0" + "CN\0" + "Th 2\0" + "Th 3\0" + "Th 4\0" + "Th 5\0" + "Th 6\0" + "Th 7\0" + "T2\0" + "T3\0" + "T4\0" + "T5\0" + "T6\0" + "T7\0" + "Th\xc3\xa1ng 1\0" + "Th\xc3\xa1ng 2\0" + "Th\xc3\xa1ng 3\0" + "Th\xc3\xa1ng 4\0" + "Th\xc3\xa1ng 5\0" + "Th\xc3\xa1ng 6\0" + "Th\xc3\xa1ng 7\0" + "Th\xc3\xa1ng 8\0" + "Th\xc3\xa1ng 9\0" + "Th\xc3\xa1ng 10\0" + "Th\xc3\xa1ng 11\0" + "Th\xc3\xa1ng 12\0" + "th\xc3\xa1ng 1\0" + "th\xc3\xa1ng 2\0" + "th\xc3\xa1ng 3\0" + "th\xc3\xa1ng 4\0" + "th\xc3\xa1ng 5\0" + "th\xc3\xa1ng 6\0" + "th\xc3\xa1ng 7\0" + "th\xc3\xa1ng 8\0" + "th\xc3\xa1ng 9\0" + "th\xc3\xa1ng 10\0" + "th\xc3\xa1ng 11\0" + "th\xc3\xa1ng 12\0" + "Thg 1\0" + "Thg 2\0" + "Thg 3\0" + "Thg 4\0" + "Thg 5\0" + "Thg 6\0" + "Thg 7\0" + "Thg 8\0" + "Thg 9\0" + "Thg 10\0" + "Thg 11\0" + "Thg 12\0" + "\xd5\xaf\xd5\xab\xd6\x80\xd5\xa1\xd5\xaf\xd5\xab\0" + "\xd5\xa5\xd6\x80\xd5\xaf\xd5\xb8\xd6\x82\xd5\xb7\xd5\xa1\xd5\xa2\xd5\xa9\xd5\xab\0" + "\xd5\xa5\xd6\x80\xd5\xa5\xd6\x84\xd5\xb7\xd5\xa1\xd5\xa2\xd5\xa9\xd5\xab\0" + "\xd5\xb9\xd5\xb8\xd6\x80\xd5\xa5\xd6\x84\xd5\xb7\xd5\xa1\xd5\xa2\xd5\xa9\xd5\xab\0" + "\xd5\xb0\xd5\xab\xd5\xb6\xd5\xa3\xd5\xb7\xd5\xa1\xd5\xa2\xd5\xa9\xd5\xab\0" + "\xd5\xb8\xd6\x82\xd6\x80\xd5\xa2\xd5\xa1\xd5\xa9\0" + "\xd5\xb7\xd5\xa1\xd5\xa2\xd5\xa1\xd5\xa9\0" + "\xd5\xaf\xd5\xab\xd6\x80\0" + "\xd5\xa5\xd6\x80\xd5\xaf\0" + "\xd5\xa5\xd6\x80\xd6\x84\0" + "\xd5\xb9\xd6\x80\xd6\x84\0" + "\xd5\xb0\xd5\xb6\xd5\xa3\0" + "\xd5\xb8\xd6\x82\xd6\x80\0" + "\xd5\xb7\xd5\xa2\xd5\xa9\0" + "\xd4\xbf\0" + "\xd4\xb5\0" + "\xd5\x89\0" + "\xd5\x80\0" + "\xd5\x88\0" + "\xd5\x87\0" + "\xd5\xb0\xd5\xb8\xd6\x82\xd5\xb6\xd5\xbe\xd5\xa1\xd6\x80\0" + "\xd6\x83\xd5\xa5\xd5\xbf\xd6\x80\xd5\xbe\xd5\xa1\xd6\x80\0" + "\xd5\xb4\xd5\xa1\xd6\x80\xd5\xbf\0" + "\xd5\xa1\xd5\xba\xd6\x80\xd5\xab\xd5\xac\0" + "\xd5\xb4\xd5\xa1\xd5\xb5\xd5\xab\xd5\xbd\0" + "\xd5\xb0\xd5\xb8\xd6\x82\xd5\xb6\xd5\xab\xd5\xbd\0" + "\xd5\xb0\xd5\xb8\xd6\x82\xd5\xac\xd5\xab\xd5\xbd\0" + "\xd6\x85\xd5\xa3\xd5\xb8\xd5\xbd\xd5\xbf\xd5\xb8\xd5\xbd\0" + "\xd5\xbd\xd5\xa5\xd5\xba\xd5\xbf\xd5\xa5\xd5\xb4\xd5\xa2\xd5\xa5\xd6\x80\0" + "\xd5\xb0\xd5\xb8\xd5\xaf\xd5\xbf\xd5\xa5\xd5\xb4\xd5\xa2\xd5\xa5\xd6\x80\0" + "\xd5\xb6\xd5\xb8\xd5\xb5\xd5\xa5\xd5\xb4\xd5\xa2\xd5\xa5\xd6\x80\0" + "\xd5\xa4\xd5\xa5\xd5\xaf\xd5\xbf\xd5\xa5\xd5\xb4\xd5\xa2\xd5\xa5\xd6\x80\0" + "\xd5\xb0\xd5\xb8\xd6\x82\xd5\xb6\xd5\xbe\xd5\xa1\xd6\x80\xd5\xab\0" + "\xd6\x83\xd5\xa5\xd5\xbf\xd6\x80\xd5\xbe\xd5\xa1\xd6\x80\xd5\xab\0" + "\xd5\xb4\xd5\xa1\xd6\x80\xd5\xbf\xd5\xab\0" + "\xd5\xa1\xd5\xba\xd6\x80\xd5\xab\xd5\xac\xd5\xab\0" + "\xd5\xb4\xd5\xa1\xd5\xb5\xd5\xab\xd5\xbd\xd5\xab\0" + "\xd5\xb0\xd5\xb8\xd6\x82\xd5\xb6\xd5\xab\xd5\xbd\xd5\xab\0" + "\xd5\xb0\xd5\xb8\xd6\x82\xd5\xac\xd5\xab\xd5\xbd\xd5\xab\0" + "\xd6\x85\xd5\xa3\xd5\xb8\xd5\xbd\xd5\xbf\xd5\xb8\xd5\xbd\xd5\xab\0" + "\xd5\xbd\xd5\xa5\xd5\xba\xd5\xbf\xd5\xa5\xd5\xb4\xd5\xa2\xd5\xa5\xd6\x80\xd5\xab\0" + "\xd5\xb0\xd5\xb8\xd5\xaf\xd5\xbf\xd5\xa5\xd5\xb4\xd5\xa2\xd5\xa5\xd6\x80\xd5\xab\0" + "\xd5\xb6\xd5\xb8\xd5\xb5\xd5\xa5\xd5\xb4\xd5\xa2\xd5\xa5\xd6\x80\xd5\xab\0" + "\xd5\xa4\xd5\xa5\xd5\xaf\xd5\xbf\xd5\xa5\xd5\xb4\xd5\xa2\xd5\xa5\xd6\x80\xd5\xab\0" + "\xd5\xb0\xd5\xb6\xd5\xbe\0" + "\xd6\x83\xd5\xbf\xd5\xbe\0" + "\xd5\xb4\xd6\x80\xd5\xbf\0" + "\xd5\xa1\xd5\xba\xd6\x80\0" + "\xd5\xb4\xd5\xb5\xd5\xbd\0" + "\xd5\xb0\xd5\xb6\xd5\xbd\0" + "\xd5\xb0\xd5\xac\xd5\xbd\0" + "\xd6\x85\xd5\xa3\xd5\xbd\0" + "\xd5\xbd\xd5\xa5\xd5\xba\0" + "\xd5\xb0\xd5\xb8\xd5\xaf\0" + "\xd5\xb6\xd5\xb8\xd5\xb5\0" + "\xd5\xa4\xd5\xa5\xd5\xaf\0" + "bazar\0" + "bazar ert\xc9\x99si\0" + "\xc3\xa7\xc9\x99r\xc5\x9f\xc9\x99nb\xc9\x99 ax\xc5\x9f\x61m\xc4\xb1\0" + "\xc3\xa7\xc9\x99r\xc5\x9f\xc9\x99nb\xc9\x99\0" + "c\xc3\xbcm\xc9\x99 ax\xc5\x9f\x61m\xc4\xb1\0" + "c\xc3\xbcm\xc9\x99\0" + "\xc5\x9f\xc9\x99nb\xc9\x99\0" + "B.\0" + "B.E.\0" + "\xc3\x87.A.\0" + "\xc3\x87.\0" + "C.A.\0" + "C.\0" + "\xc5\x9e.\0" + "Yanvar\0" + "Fevral\0" + "Aprel\0" + "\xc4\xb0yun\0" + "\xc4\xb0yul\0" + "Avqust\0" + "Sentyabr\0" + "Oktyabr\0" + "Noyabr\0" + "Dekabr\0" + "yanvar\0" + "fevral\0" + "mart\0" + "aprel\0" + "may\0" + "iyun\0" + "iyul\0" + "avqust\0" + "sentyabr\0" + "oktyabr\0" + "noyabr\0" + "dekabr\0" + "yan\0" + "iyn\0" + "iyl\0" + "avq\0" + "sen\0" + "noy\0" + "dek\0" + "igandea\0" + "astelehena\0" + "asteartea\0" + "asteazkena\0" + "osteguna\0" + "ostirala\0" + "larunbata\0" + "ig.\0" + "al.\0" + "ar.\0" + "az.\0" + "og.\0" + "or.\0" + "lr.\0" + "I\0" + "urtarrila\0" + "Otsaila\0" + "Martxoa\0" + "Apirila\0" + "Maiatza\0" + "Ekaina\0" + "Uztaila\0" + "Abuztua\0" + "Iraila\0" + "Urria\0" + "Azaroa\0" + "Abendua\0" + "otsaila\0" + "martxoa\0" + "apirila\0" + "maiatza\0" + "ekaina\0" + "uztaila\0" + "abuztua\0" + "iraila\0" + "urria\0" + "azaroa\0" + "abendua\0" + "urt.\0" + "ots.\0" + "api.\0" + "mai.\0" + "eka.\0" + "uzt.\0" + "abu.\0" + "ira.\0" + "urr.\0" + "aza.\0" + "abe.\0" + "njed\xc5\xba\x65la\0" + "p\xc3\xb3nd\xc5\xba\x65la\0" + "wutora\0" + "srjeda\0" + "\xc5\xa1tw\xc3\xb3rtk\0" + "pjatk\0" + "nje\0" + "p\xc3\xb3n\0" + "wut\0" + "srj\0" + "\xc5\xa1tw\0" + "pja\0" + "sob\0" + "w\0" + "m\xc4\x9brc\0" + "apryl\0" + "meja\0" + "awgust\0" + "nowember\0" + "januara\0" + "februara\0" + "m\xc4\x9brca\0" + "apryla\0" + "meje\0" + "junija\0" + "julija\0" + "awgusta\0" + "oktobra\0" + "nowembra\0" + "m\xc4\x9br\0" + "mej\0" + "awg\0" + "now\0" + "\xd0\xbd\xd0\xb5\xd0\xb4\xd0\xb5\xd0\xbb\xd0\xb0\0" + "\xd1\x87\xd0\xb5\xd1\x82\xd0\xb2\xd1\x80\xd1\x82\xd0\xbe\xd0\xba\0" + "\xd0\xbf\xd0\xb5\xd1\x82\xd0\xbe\xd0\xba\0" + "\xd1\x81\xd0\xb0\xd0\xb1\xd0\xbe\xd1\x82\xd0\xb0\0" + "\xd0\xbd\xd0\xb5\xd0\xb4.\0" + "\xd0\xbf\xd0\xbe\xd0\xbd.\0" + "\xd0\xb2\xd1\x82\xd0\xbe.\0" + "\xd1\x81\xd1\x80\xd0\xb5.\0" + "\xd1\x87\xd0\xb5\xd1\x82.\0" + "\xd0\xbf\xd0\xb5\xd1\x82.\0" + "\xd1\x81\xd0\xb0\xd0\xb1.\0" + "\xd1\x98\xd0\xb0\xd0\xbd\xd1\x83\xd0\xb0\xd1\x80\xd0\xb8\0" + "\xd0\xbc\xd0\xb0\xd1\x98\0" + "\xd1\x98\xd1\x83\xd0\xbd\xd0\xb8\0" + "\xd1\x98\xd1\x83\xd0\xbb\xd0\xb8\0" + "\xd1\x98\xd0\xb0\xd0\xbd.\0" + "\xd1\x84\xd0\xb5\xd0\xb2.\0" + "\xd0\xbc\xd0\xb0\xd1\x80.\0" + "\xd1\x98\xd1\x83\xd0\xbd.\0" + "\xd1\x98\xd1\x83\xd0\xbb.\0" + "\xd1\x81\xd0\xb5\xd0\xbf\xd1\x82.\0" + "\xd0\xbd\xd0\xbe\xd0\xb5\xd0\xbc.\0" + "Sontaha\0" + "Mmantaha\0" + "Labobedi\0" + "Laboraru\0" + "Labone\0" + "Labohlane\0" + "Moqebelo\0" + "Son\0" + "Mma\0" + "Bed\0" + "Rar\0" + "Ne\0" + "Hla\0" + "Moq\0" + "Phesekgong\0" + "Hlakola\0" + "Hlakubele\0" + "Mmese\0" + "Motsheanong\0" + "Phupjane\0" + "Phupu\0" + "Phata\0" + "Leotshe\0" + "Mphalane\0" + "Pundungwane\0" + "Tshitwe\0" + "Phe\0" + "Kol\0" + "Ube\0" + "Mme\0" + "Mot\0" + "Upu\0" + "Pha\0" + "Leo\0" + "Mph\0" + "Pun\0" + "Tsh\0" + "Sonto\0" + "Musumbhunuku\0" + "Ravumbirhi\0" + "Ravunharhu\0" + "Ravumune\0" + "Ravuntlhanu\0" + "Mugqivela\0" + "Mus\0" + "Bir\0" + "Har\0" + "Tlh\0" + "Mug\0" + "Sunguti\0" + "Nyenyenyani\0" + "Nyenyankulu\0" + "Dzivamisoko\0" + "Mudyaxihi\0" + "Khotavuxika\0" + "Mawuwani\0" + "Mhawuri\0" + "Ndzhati\0" + "Nhlangula\0" + "Hukuri\0" + "N'wendzamhala\0" + "Yan\0" + "Kul\0" + "Dzi\0" + "Mud\0" + "Kho\0" + "Maw\0" + "Mha\0" + "Ndz\0" + "Nhl\0" + "Huk\0" + "N'w\0" + "Tshipi\0" + "Mosopulogo\0" + "Laboraro\0" + "Labotlhano\0" + "Matlhatso\0" + "Mos\0" + "Tla\0" + "Mat\0" + "Ferikgong\0" + "Tlhakole\0" + "Mopitlo\0" + "Moranang\0" + "Motsheganang\0" + "Seetebosigo\0" + "Phukwi\0" + "Phatwe\0" + "Lwetse\0" + "Diphalane\0" + "Ngwanatsele\0" + "Sedimonthole\0" + "Fer\0" + "Mop\0" + "Mor\0" + "See\0" + "Phu\0" + "Lwe\0" + "Dip\0" + "Ngw\0" + "Sed\0" + "Cawe\0" + "Mvulo\0" + "Lwesibini\0" + "Lwesithathu\0" + "Lwesine\0" + "Lwesihlanu\0" + "Mgqibelo\0" + "Caw\0" + "Mvu\0" + "Bin\0" + "Tha\0" + "Sin\0" + "Mgq\0" + "Janyuwari\0" + "Februwari\0" + "Matshi\0" + "Epreli\0" + "Meyi\0" + "Julayi\0" + "Agasti\0" + "Septemba\0" + "Okthoba\0" + "Novemba\0" + "Disemba\0" + "Epr\0" + "Mey\0" + "Aga\0" + "Dis\0" + "ISonto\0" + "UMsombuluko\0" + "ULwesibili\0" + "ULwesithathu\0" + "ULwesine\0" + "ULwesihlanu\0" + "UMgqibelo\0" + "Mso\0" + "Bil\0" + "B\0" + "Januwari\0" + "Mashi\0" + "Ephreli\0" + "Septhemba\0" + "UMasingana\0" + "Mas\0" + "Eph\0" + "Sondag\0" + "Maandag\0" + "Dinsdag\0" + "Woensdag\0" + "Donderdag\0" + "Vrydag\0" + "Saterdag\0" + "So.\0" + "Ma.\0" + "Di.\0" + "Wo.\0" + "Do.\0" + "Vr.\0" + "Sa.\0" + "Januarie\0" + "Februarie\0" + "Maart\0" + "Junie\0" + "Julie\0" + "Augustus\0" + "Jan.\0" + "Feb.\0" + "Mrt.\0" + "Apr.\0" + "Jun.\0" + "Jul.\0" + "Aug.\0" + "Sep.\0" + "Okt.\0" + "Nov.\0" + "Des.\0" + "\xe1\x83\x99\xe1\x83\x95\xe1\x83\x98\xe1\x83\xa0\xe1\x83\x90\0" + "\xe1\x83\x9d\xe1\x83\xa0\xe1\x83\xa8\xe1\x83\x90\xe1\x83\x91\xe1\x83\x90\xe1\x83\x97\xe1\x83\x98\0" + "\xe1\x83\xa1\xe1\x83\x90\xe1\x83\x9b\xe1\x83\xa8\xe1\x83\x90\xe1\x83\x91\xe1\x83\x90\xe1\x83\x97\xe1\x83\x98\0" + "\xe1\x83\x9d\xe1\x83\x97\xe1\x83\xae\xe1\x83\xa8\xe1\x83\x90\xe1\x83\x91\xe1\x83\x90\xe1\x83\x97\xe1\x83\x98\0" + "\xe1\x83\xae\xe1\x83\xa3\xe1\x83\x97\xe1\x83\xa8\xe1\x83\x90\xe1\x83\x91\xe1\x83\x90\xe1\x83\x97\xe1\x83\x98\0" + "\xe1\x83\x9e\xe1\x83\x90\xe1\x83\xa0\xe1\x83\x90\xe1\x83\xa1\xe1\x83\x99\xe1\x83\x94\xe1\x83\x95\xe1\x83\x98\0" + "\xe1\x83\xa8\xe1\x83\x90\xe1\x83\x91\xe1\x83\x90\xe1\x83\x97\xe1\x83\x98\0" + "\xe1\x83\x99\xe1\x83\x95\xe1\x83\x98\0" + "\xe1\x83\x9d\xe1\x83\xa0\xe1\x83\xa8\0" + "\xe1\x83\xa1\xe1\x83\x90\xe1\x83\x9b\0" + "\xe1\x83\x9d\xe1\x83\x97\xe1\x83\xae\0" + "\xe1\x83\xae\xe1\x83\xa3\xe1\x83\x97\0" + "\xe1\x83\x9e\xe1\x83\x90\xe1\x83\xa0\0" + "\xe1\x83\xa8\xe1\x83\x90\xe1\x83\x91\0" + "\xe1\x83\x99\0" + "\xe1\x83\x9d\0" + "\xe1\x83\xa1\0" + "\xe1\x83\xae\0" + "\xe1\x83\x9e\0" + "\xe1\x83\xa8\0" + "\xe1\x83\x98\xe1\x83\x90\xe1\x83\x9c\xe1\x83\x95\xe1\x83\x90\xe1\x83\xa0\xe1\x83\x98\0" + "\xe1\x83\x97\xe1\x83\x94\xe1\x83\x91\xe1\x83\x94\xe1\x83\xa0\xe1\x83\x95\xe1\x83\x90\xe1\x83\x9a\xe1\x83\x98\0" + "\xe1\x83\x9b\xe1\x83\x90\xe1\x83\xa0\xe1\x83\xa2\xe1\x83\x98\0" + "\xe1\x83\x90\xe1\x83\x9e\xe1\x83\xa0\xe1\x83\x98\xe1\x83\x9a\xe1\x83\x98\0" + "\xe1\x83\x9b\xe1\x83\x90\xe1\x83\x98\xe1\x83\xa1\xe1\x83\x98\0" + "\xe1\x83\x98\xe1\x83\x95\xe1\x83\x9c\xe1\x83\x98\xe1\x83\xa1\xe1\x83\x98\0" + "\xe1\x83\x98\xe1\x83\x95\xe1\x83\x9a\xe1\x83\x98\xe1\x83\xa1\xe1\x83\x98\0" + "\xe1\x83\x90\xe1\x83\x92\xe1\x83\x95\xe1\x83\x98\xe1\x83\xa1\xe1\x83\xa2\xe1\x83\x9d\0" + "\xe1\x83\xa1\xe1\x83\x94\xe1\x83\xa5\xe1\x83\xa2\xe1\x83\x94\xe1\x83\x9b\xe1\x83\x91\xe1\x83\x94\xe1\x83\xa0\xe1\x83\x98\0" + "\xe1\x83\x9d\xe1\x83\xa5\xe1\x83\xa2\xe1\x83\x9d\xe1\x83\x9b\xe1\x83\x91\xe1\x83\x94\xe1\x83\xa0\xe1\x83\x98\0" + "\xe1\x83\x9c\xe1\x83\x9d\xe1\x83\x94\xe1\x83\x9b\xe1\x83\x91\xe1\x83\x94\xe1\x83\xa0\xe1\x83\x98\0" + "\xe1\x83\x93\xe1\x83\x94\xe1\x83\x99\xe1\x83\x94\xe1\x83\x9b\xe1\x83\x91\xe1\x83\x94\xe1\x83\xa0\xe1\x83\x98\0" + "\xe1\x83\x98\xe1\x83\x90\xe1\x83\x9c\0" + "\xe1\x83\x97\xe1\x83\x94\xe1\x83\x91\0" + "\xe1\x83\x9b\xe1\x83\x90\xe1\x83\xa0\0" + "\xe1\x83\x90\xe1\x83\x9e\xe1\x83\xa0\0" + "\xe1\x83\x9b\xe1\x83\x90\xe1\x83\x98\0" + "\xe1\x83\x98\xe1\x83\x95\xe1\x83\x9c\0" + "\xe1\x83\x98\xe1\x83\x95\xe1\x83\x9a\0" + "\xe1\x83\x90\xe1\x83\x92\xe1\x83\x95\0" + "\xe1\x83\xa1\xe1\x83\x94\xe1\x83\xa5\0" + "\xe1\x83\x9d\xe1\x83\xa5\xe1\x83\xa2\0" + "\xe1\x83\x9c\xe1\x83\x9d\xe1\x83\x94\0" + "\xe1\x83\x93\xe1\x83\x94\xe1\x83\x99\0" + "m\xc3\xa1nadagur\0" + "t\xc3\xbdsdagur\0" + "mikudagur\0" + "h\xc3\xb3sdagur\0" + "fr\xc3\xadggjadagur\0" + "leygardagur\0" + "sun\0" + "m\xc3\xa1n\0" + "t\xc3\xbds\0" + "mik\0" + "h\xc3\xb3s\0" + "fr\xc3\xad\0" + "ley\0" + "\xe0\xa4\xb0\xe0\xa4\xb5\xe0\xa4\xbf\xe0\xa4\xb5\xe0\xa4\xbe\xe0\xa4\xb0\0" + "\xe0\xa4\xb8\xe0\xa5\x8b\xe0\xa4\xae\xe0\xa4\xb5\xe0\xa4\xbe\xe0\xa4\xb0\0" + "\xe0\xa4\xae\xe0\xa4\x82\xe0\xa4\x97\xe0\xa4\xb2\xe0\xa4\xb5\xe0\xa4\xbe\xe0\xa4\xb0\0" + "\xe0\xa4\xac\xe0\xa5\x81\xe0\xa4\xa7\xe0\xa4\xb5\xe0\xa4\xbe\xe0\xa4\xb0\0" + "\xe0\xa4\x97\xe0\xa5\x81\xe0\xa4\xb0\xe0\xa5\x81\xe0\xa4\xb5\xe0\xa4\xbe\xe0\xa4\xb0\0" + "\xe0\xa4\xb6\xe0\xa5\x81\xe0\xa4\x95\xe0\xa5\x8d\xe0\xa4\xb0\xe0\xa4\xb5\xe0\xa4\xbe\xe0\xa4\xb0\0" + "\xe0\xa4\xb6\xe0\xa4\xa8\xe0\xa4\xbf\xe0\xa4\xb5\xe0\xa4\xbe\xe0\xa4\xb0\0" + "\xe0\xa4\xb0\xe0\xa4\xb5\xe0\xa4\xbf\0" + "\xe0\xa4\xb8\xe0\xa5\x8b\xe0\xa4\xae\0" + "\xe0\xa4\xae\xe0\xa4\x82\xe0\xa4\x97\xe0\xa4\xb2\0" + "\xe0\xa4\xac\xe0\xa5\x81\xe0\xa4\xa7\0" + "\xe0\xa4\x97\xe0\xa5\x81\xe0\xa4\xb0\xe0\xa5\x81\0" + "\xe0\xa4\xb6\xe0\xa5\x81\xe0\xa4\x95\xe0\xa5\x8d\xe0\xa4\xb0\0" + "\xe0\xa4\xb6\xe0\xa4\xa8\xe0\xa4\xbf\0" + "\xe0\xa4\xb0\0" + "\xe0\xa4\xb8\xe0\xa5\x8b\0" + "\xe0\xa4\xae\xe0\xa4\x82\0" + "\xe0\xa4\xac\xe0\xa5\x81\0" + "\xe0\xa4\x97\xe0\xa5\x81\0" + "\xe0\xa4\xb6\xe0\xa5\x81\0" + "\xe0\xa4\xb6\0" + "\xe0\xa4\x9c\xe0\xa4\xa8\xe0\xa4\xb5\xe0\xa4\xb0\xe0\xa5\x80\0" + "\xe0\xa4\xab\xe0\xa4\xbc\xe0\xa4\xb0\xe0\xa4\xb5\xe0\xa4\xb0\xe0\xa5\x80\0" + "\xe0\xa4\xae\xe0\xa4\xbe\xe0\xa4\xb0\xe0\xa5\x8d\xe0\xa4\x9a\0" + "\xe0\xa4\x85\xe0\xa4\xaa\xe0\xa5\x8d\xe0\xa4\xb0\xe0\xa5\x88\xe0\xa4\xb2\0" + "\xe0\xa4\xae\xe0\xa4\x88\0" + "\xe0\xa4\x9c\xe0\xa5\x82\xe0\xa4\xa8\0" + "\xe0\xa4\x9c\xe0\xa5\x81\xe0\xa4\xb2\xe0\xa4\xbe\xe0\xa4\x88\0" + "\xe0\xa4\x85\xe0\xa4\x97\xe0\xa4\xb8\xe0\xa5\x8d\xe0\xa4\xa4\0" + "\xe0\xa4\xb8\xe0\xa4\xbf\xe0\xa4\xa4\xe0\xa4\x82\xe0\xa4\xac\xe0\xa4\xb0\0" + "\xe0\xa4\x85\xe0\xa4\x95\xe0\xa5\x8d\xe0\xa4\xa4\xe0\xa5\x82\xe0\xa4\xac\xe0\xa4\xb0\0" + "\xe0\xa4\xa8\xe0\xa4\xb5\xe0\xa4\x82\xe0\xa4\xac\xe0\xa4\xb0\0" + "\xe0\xa4\xa6\xe0\xa4\xbf\xe0\xa4\xb8\xe0\xa4\x82\xe0\xa4\xac\xe0\xa4\xb0\0" + "\xe0\xa4\x9c\xe0\xa4\xa8\xe0\xa5\xb0\0" + "\xe0\xa4\xab\xe0\xa4\xbc\xe0\xa4\xb0\xe0\xa5\xb0\0" + "\xe0\xa4\x9c\xe0\xa5\x81\xe0\xa4\xb2\xe0\xa5\xb0\0" + "\xe0\xa4\x85\xe0\xa4\x97\xe0\xa5\xb0\0" + "\xe0\xa4\xb8\xe0\xa4\xbf\xe0\xa4\xa4\xe0\xa5\xb0\0" + "\xe0\xa4\x85\xe0\xa4\x95\xe0\xa5\x8d\xe0\xa4\xa4\xe0\xa5\x82\xe0\xa5\xb0\0" + "\xe0\xa4\xa8\xe0\xa4\xb5\xe0\xa5\xb0\0" + "\xe0\xa4\xa6\xe0\xa4\xbf\xe0\xa4\xb8\xe0\xa5\xb0\0" + "Il-\xc4\xa6\x61\x64\x64\0" + "It-Tnejn\0" + "It-Tlieta\0" + "L-Erbg\xc4\xa7\x61\0" + "Il-\xc4\xa6\x61mis\0" + "Il-\xc4\xa0img\xc4\xa7\x61\0" + "Is-Sibt\0" + "\xc4\xa6\x61\x64\0" + "Tne\0" + "Tli\0" + "Erb\0" + "\xc4\xa6\x61m\0" + "\xc4\xa0im\0" + "Sib\0" + "\xc4\xa6\x64\0" + "Tn\0" + "Tl\0" + "Er\0" + "\xc4\xa6m\0" + "\xc4\xa0m\0" + "Sb\0" + "Jannar\0" + "Frar\0" + "Marzu\0" + "Mejju\0" + "\xc4\xa0unju\0" + "Lulju\0" + "Awwissu\0" + "Settembru\0" + "Ottubru\0" + "Novembru\0" + "Di\xc4\x8b\x65mbru\0" + "Fra\0" + "Mej\0" + "\xc4\xa0un\0" + "Lul\0" + "Aww\0" + "Set\0" + "Ott\0" + "Di\xc4\x8b\0" + "sotnabeaivi\0" + "vuoss\xc3\xa1rga\0" + "ma\xc5\x8b\xc5\x8b\x65\x62\xc3\xa1rga\0" + "gaskavahkku\0" + "duorasdat\0" + "bearjadat\0" + "l\xc3\xa1vvardat\0" + "sotn\0" + "vuos\0" + "ma\xc5\x8b\0" + "gask\0" + "duor\0" + "bear\0" + "l\xc3\xa1v\0" + "o\xc4\x91\xc4\x91\x61jagem\xc3\xa1nnu\0" + "guovvam\xc3\xa1nnu\0" + "njuk\xc4\x8d\x61m\xc3\xa1nnu\0" + "cuo\xc5\x8bom\xc3\xa1nnu\0" + "miessem\xc3\xa1nnu\0" + "geassem\xc3\xa1nnu\0" + "suoidnem\xc3\xa1nnu\0" + "borgem\xc3\xa1nnu\0" + "\xc4\x8d\x61k\xc4\x8d\x61m\xc3\xa1nnu\0" + "golggotm\xc3\xa1nnu\0" + "sk\xc3\xa1\x62mam\xc3\xa1nnu\0" + "juovlam\xc3\xa1nnu\0" + "o\xc4\x91\xc4\x91j\0" + "guov\0" + "njuk\0" + "cuo\0" + "mies\0" + "geas\0" + "suoi\0" + "borg\0" + "\xc4\x8d\x61k\xc4\x8d\0" + "golg\0" + "sk\xc3\xa1\x62\0" + "juov\0" + "D\xc3\xa9 Domhnaigh\0" + "D\xc3\xa9 Luain\0" + "D\xc3\xa9 M\xc3\xa1irt\0" + "D\xc3\xa9 C\xc3\xa9\x61\x64\x61oin\0" + "D\xc3\xa9\x61rdaoin\0" + "D\xc3\xa9 hAoine\0" + "D\xc3\xa9 Sathairn\0" + "Domh\0" + "Luan\0" + "M\xc3\xa1irt\0" + "C\xc3\xa9\x61\x64\0" + "D\xc3\xa9\x61r\0" + "Aoine\0" + "Sath\0" + "Ean\xc3\xa1ir\0" + "Feabhra\0" + "M\xc3\xa1rta\0" + "Aibre\xc3\xa1n\0" + "Bealtaine\0" + "Meitheamh\0" + "I\xc3\xbail\0" + "L\xc3\xbanasa\0" + "Me\xc3\xa1n F\xc3\xb3mhair\0" + "Deireadh F\xc3\xb3mhair\0" + "Samhain\0" + "Nollaig\0" + "Ean\0" + "Feabh\0" + "Aib\0" + "Beal\0" + "Meith\0" + "L\xc3\xban\0" + "MF\xc3\xb3mh\0" + "DF\xc3\xb3mh\0" + "Samh\0" + "Noll\0" + "Ahad\0" + "Isnin\0" + "Khamis\0" + "Jumaat\0" + "Ahd\0" + "Isn\0" + "Kha\0" + "Mac\0" + "Julai\0" + "Ogos\0" + "Disember\0" + "Ogo\0" + "\xd0\xb6\xd0\xb5\xd0\xba\xd1\x81\xd0\xb5\xd0\xbd\xd0\xb1\xd1\x96\0" + "\xd0\xb4\xd2\xaf\xd0\xb9\xd1\x81\xd0\xb5\xd0\xbd\xd0\xb1\xd1\x96\0" + "\xd1\x81\xd0\xb5\xd0\xb9\xd1\x81\xd0\xb5\xd0\xbd\xd0\xb1\xd1\x96\0" + "\xd1\x81\xd3\x99\xd1\x80\xd1\x81\xd0\xb5\xd0\xbd\xd0\xb1\xd1\x96\0" + "\xd0\xb1\xd0\xb5\xd0\xb9\xd1\x81\xd0\xb5\xd0\xbd\xd0\xb1\xd1\x96\0" + "\xd0\xb6\xd2\xb1\xd0\xbc\xd0\xb0\0" + "\xd1\x81\xd0\xb5\xd0\xbd\xd0\xb1\xd1\x96\0" + "\xd0\x96\xd1\x81\0" + "\xd0\x94\xd1\x81\0" + "\xd0\xa1\xd1\x81\0" + "\xd0\xa1\xd1\x80\0" + "\xd0\x91\xd1\x81\0" + "\xd0\x96\xd0\xbc\0" + "\xd0\xa1\xd0\xb1\0" + "\xd0\x96\0" + "\xd0\x94\0" + "\xd0\x91\0" + "\xd2\x9a\xd0\xb0\xd2\xa3\xd1\x82\xd0\xb0\xd1\x80\0" + "\xd0\x90\xd2\x9b\xd0\xbf\xd0\xb0\xd0\xbd\0" + "\xd0\x9d\xd0\xb0\xd1\x83\xd1\x80\xd1\x8b\xd0\xb7\0" + "\xd0\xa1\xd3\x99\xd1\x83\xd1\x96\xd1\x80\0" + "\xd0\x9c\xd0\xb0\xd0\xbc\xd1\x8b\xd1\x80\0" + "\xd0\x9c\xd0\xb0\xd1\x83\xd1\x81\xd1\x8b\xd0\xbc\0" + "\xd0\xa8\xd1\x96\xd0\xbb\xd0\xb4\xd0\xb5\0" + "\xd0\xa2\xd0\xb0\xd0\xbc\xd1\x8b\xd0\xb7\0" + "\xd2\x9a\xd1\x8b\xd1\x80\xd0\xba\xd2\xaf\xd0\xb9\xd0\xb5\xd0\xba\0" + "\xd2\x9a\xd0\xb0\xd0\xb7\xd0\xb0\xd0\xbd\0" + "\xd2\x9a\xd0\xb0\xd1\x80\xd0\xb0\xd1\x88\xd0\xb0\0" + "\xd0\x96\xd0\xb5\xd0\xbb\xd1\x82\xd0\xbe\xd2\x9b\xd1\x81\xd0\xb0\xd0\xbd\0" + "\xd2\x9b\xd0\xb0\xd2\xa3\xd1\x82\xd0\xb0\xd1\x80\0" + "\xd0\xb0\xd2\x9b\xd0\xbf\xd0\xb0\xd0\xbd\0" + "\xd0\xbd\xd0\xb0\xd1\x83\xd1\x80\xd1\x8b\xd0\xb7\0" + "\xd1\x81\xd3\x99\xd1\x83\xd1\x96\xd1\x80\0" + "\xd0\xbc\xd0\xb0\xd0\xbc\xd1\x8b\xd1\x80\0" + "\xd0\xbc\xd0\xb0\xd1\x83\xd1\x81\xd1\x8b\xd0\xbc\0" + "\xd1\x88\xd1\x96\xd0\xbb\xd0\xb4\xd0\xb5\0" + "\xd1\x82\xd0\xb0\xd0\xbc\xd1\x8b\xd0\xb7\0" + "\xd2\x9b\xd1\x8b\xd1\x80\xd0\xba\xd2\xaf\xd0\xb9\xd0\xb5\xd0\xba\0" + "\xd2\x9b\xd0\xb0\xd0\xb7\xd0\xb0\xd0\xbd\0" + "\xd2\x9b\xd0\xb0\xd1\x80\xd0\xb0\xd1\x88\xd0\xb0\0" + "\xd0\xb6\xd0\xb5\xd0\xbb\xd1\x82\xd0\xbe\xd2\x9b\xd1\x81\xd0\xb0\xd0\xbd\0" + "\xd2\x9a\xd0\xb0\xd2\xa3.\0" + "\xd0\x90\xd2\x9b\xd0\xbf.\0" + "\xd0\x9d\xd0\xb0\xd1\x83.\0" + "\xd0\xa1\xd3\x99\xd1\x83.\0" + "\xd0\x9c\xd0\xb0\xd0\xbc.\0" + "\xd0\x9c\xd0\xb0\xd1\x83.\0" + "\xd0\xa8\xd1\x96\xd0\xbb.\0" + "\xd0\xa2\xd0\xb0\xd0\xbc.\0" + "\xd2\x9a\xd1\x8b\xd1\x80.\0" + "\xd2\x9a\xd0\xb0\xd0\xb7.\0" + "\xd2\x9a\xd0\xb0\xd1\x80.\0" + "\xd0\x96\xd0\xb5\xd0\xbb.\0" + "\xd0\xb6\xd0\xb5\xd0\xba\xd1\x88\xd0\xb5\xd0\xbc\xd0\xb1\xd0\xb8\0" + "\xd0\xb4\xd2\xaf\xd0\xb9\xd1\x88\xd3\xa9\xd0\xbc\xd0\xb1\xd2\xaf\0" + "\xd1\x88\xd0\xb5\xd0\xb9\xd1\x88\xd0\xb5\xd0\xbc\xd0\xb1\xd0\xb8\0" + "\xd1\x88\xd0\xb0\xd1\x80\xd1\x88\xd0\xb5\xd0\xbc\xd0\xb1\xd0\xb8\0" + "\xd0\xb1\xd0\xb5\xd0\xb9\xd1\x88\xd0\xb5\xd0\xbc\xd0\xb1\xd0\xb8\0" + "\xd0\xb6\xd1\x83\xd0\xbc\xd0\xb0\0" + "\xd0\xb8\xd1\x88\xd0\xb5\xd0\xbc\xd0\xb1\xd0\xb8\0" + "\xd0\xb6\xd0\xb5\xd0\xba.\0" + "\xd0\xb4\xd2\xaf\xd0\xb9.\0" + "\xd1\x88\xd0\xb5\xd0\xb9\xd1\x88.\0" + "\xd1\x88\xd0\xb0\xd1\x80\xd1\x88.\0" + "\xd0\xb1\xd0\xb5\xd0\xb9\xd1\x88.\0" + "\xd0\xb8\xd1\x88\xd0\xbc.\0" + "\xd0\xa8\0" + "\xd0\x98\0" + "\xd0\xaf\xd0\xbd\xd0\xb2\xd0\xb0\xd1\x80\xd1\x8c\0" + "\xd0\xa4\xd0\xb5\xd0\xb2\xd1\x80\xd0\xb0\xd0\xbb\xd1\x8c\0" + "\xd0\x90\xd0\xbf\xd1\x80\xd0\xb5\xd0\xbb\xd1\x8c\0" + "\xd0\x98\xd1\x8e\xd0\xbd\xd1\x8c\0" + "\xd0\x98\xd1\x8e\xd0\xbb\xd1\x8c\0" + "\xd0\xa1\xd0\xb5\xd0\xbd\xd1\x82\xd1\x8f\xd0\xb1\xd1\x80\xd1\x8c\0" + "\xd0\x9e\xd0\xba\xd1\x82\xd1\x8f\xd0\xb1\xd1\x80\xd1\x8c\0" + "\xd0\x9d\xd0\xbe\xd1\x8f\xd0\xb1\xd1\x80\xd1\x8c\0" + "\xd0\x94\xd0\xb5\xd0\xba\xd0\xb0\xd0\xb1\xd1\x80\xd1\x8c\0" + "Jumapili\0" + "Jumatatu\0" + "Jumanne\0" + "Jumatano\0" + "Alhamisi\0" + "Ijumaa\0" + "Jumamosi\0" + "Machi\0" + "Aprili\0" + "Agosti\0" + "Oktoba\0" + "Desemba\0" + "Ago\0" + "\xc3\xbd\x65k\xc5\x9f\x65nbe\0" + "du\xc5\x9f\x65nbe\0" + "si\xc5\x9f\x65nbe\0" + "\xc3\xa7\x61r\xc5\x9f\x65nbe\0" + "pen\xc5\x9f\x65nbe\0" + "anna\0" + "\xc5\x9f\x65nbe\0" + "\xc3\xbd\x62\0" + "db\0" + "sb\0" + "\xc3\xa7\x62\0" + "pb\0" + "\xc5\x9f\x62\0" + "\xc3\x9d\0" + "\xc5\x9e\0" + "\xc3\xbd\x61nwar\0" + "fewral\0" + "ma\xc3\xbd\0" + "i\xc3\xbdun\0" + "i\xc3\xbdul\0" + "sent\xc3\xbd\x61\x62r\0" + "okt\xc3\xbd\x61\x62r\0" + "no\xc3\xbd\x61\x62r\0" + "\xc3\xbd\x61n\0" + "few\0" + "no\xc3\xbd\0" + "yakshanba\0" + "dushanba\0" + "seshanba\0" + "chorshanba\0" + "payshanba\0" + "juma\0" + "shanba\0" + "Yak\0" + "Dush\0" + "Sesh\0" + "Chor\0" + "Pay\0" + "Shan\0" + "Y\0" + "Iyun\0" + "Iyul\0" + "Avgust\0" + "Sentabr\0" + "Oktabr\0" + "sentabr\0" + "oktabr\0" + "Fev\0" + "Iyn\0" + "Iyl\0" + "Avg\0" + "Noy\0" + "Dek\0" + "\xe0\xa6\xb0\xe0\xa6\xac\xe0\xa6\xbf\xe0\xa6\xac\xe0\xa6\xbe\xe0\xa6\xb0\0" + "\xe0\xa6\xb8\xe0\xa7\x8b\xe0\xa6\xae\xe0\xa6\xac\xe0\xa6\xbe\xe0\xa6\xb0\0" + "\xe0\xa6\xae\xe0\xa6\x99\xe0\xa7\x8d\xe0\xa6\x97\xe0\xa6\xb2\xe0\xa6\xac\xe0\xa6\xbe\xe0\xa6\xb0\0" + "\xe0\xa6\xac\xe0\xa7\x81\xe0\xa6\xa7\xe0\xa6\xac\xe0\xa6\xbe\xe0\xa6\xb0\0" + "\xe0\xa6\xac\xe0\xa7\x83\xe0\xa6\xb9\xe0\xa6\xb8\xe0\xa7\x8d\xe0\xa6\xaa\xe0\xa6\xa4\xe0\xa6\xbf\xe0\xa6\xac\xe0\xa6\xbe\xe0\xa6\xb0\0" + "\xe0\xa6\xb6\xe0\xa7\x81\xe0\xa6\x95\xe0\xa7\x8d\xe0\xa6\xb0\xe0\xa6\xac\xe0\xa6\xbe\xe0\xa6\xb0\0" + "\xe0\xa6\xb6\xe0\xa6\xa8\xe0\xa6\xbf\xe0\xa6\xac\xe0\xa6\xbe\xe0\xa6\xb0\0" + "\xe0\xa6\xb0\xe0\xa6\xac\xe0\xa6\xbf\0" + "\xe0\xa6\xb8\xe0\xa7\x8b\xe0\xa6\xae\0" + "\xe0\xa6\xae\xe0\xa6\x99\xe0\xa7\x8d\xe0\xa6\x97\xe0\xa6\xb2\0" + "\xe0\xa6\xac\xe0\xa7\x81\xe0\xa6\xa7\0" + "\xe0\xa6\xac\xe0\xa7\x83\xe0\xa6\xb9\xe0\xa6\xb8\xe0\xa7\x8d\xe0\xa6\xaa\xe0\xa6\xa4\xe0\xa6\xbf\0" + "\xe0\xa6\xb6\xe0\xa7\x81\xe0\xa6\x95\xe0\xa7\x8d\xe0\xa6\xb0\0" + "\xe0\xa6\xb6\xe0\xa6\xa8\xe0\xa6\xbf\0" + "\xe0\xa6\xb0\0" + "\xe0\xa6\xb8\xe0\xa7\x8b\0" + "\xe0\xa6\xae\0" + "\xe0\xa6\xac\xe0\xa7\x81\0" + "\xe0\xa6\xac\xe0\xa7\x83\0" + "\xe0\xa6\xb6\xe0\xa7\x81\0" + "\xe0\xa6\xb6\0" + "\xe0\xa6\x9c\xe0\xa6\xbe\xe0\xa6\xa8\xe0\xa7\x81\xe0\xa6\xaf\xe0\xa6\xbc\xe0\xa6\xbe\xe0\xa6\xb0\xe0\xa7\x80\0" + "\xe0\xa6\xab\xe0\xa7\x87\xe0\xa6\xac\xe0\xa7\x8d\xe0\xa6\xb0\xe0\xa7\x81\xe0\xa6\xaf\xe0\xa6\xbc\xe0\xa6\xbe\xe0\xa6\xb0\xe0\xa7\x80\0" + "\xe0\xa6\xae\xe0\xa6\xbe\xe0\xa6\xb0\xe0\xa7\x8d\xe0\xa6\x9a\0" + "\xe0\xa6\x8f\xe0\xa6\xaa\xe0\xa7\x8d\xe0\xa6\xb0\xe0\xa6\xbf\xe0\xa6\xb2\0" + "\xe0\xa6\xae\xe0\xa7\x87\0" + "\xe0\xa6\x9c\xe0\xa7\x81\xe0\xa6\xa8\0" + "\xe0\xa6\x9c\xe0\xa7\x81\xe0\xa6\xb2\xe0\xa6\xbe\xe0\xa6\x87\0" + "\xe0\xa6\x86\xe0\xa6\x97\xe0\xa6\xb8\xe0\xa7\x8d\xe0\xa6\x9f\0" + "\xe0\xa6\xb8\xe0\xa7\x87\xe0\xa6\xaa\xe0\xa7\x8d\xe0\xa6\x9f\xe0\xa7\x87\xe0\xa6\xae\xe0\xa7\x8d\xe0\xa6\xac\xe0\xa6\xb0\0" + "\xe0\xa6\x85\xe0\xa6\x95\xe0\xa7\x8d\xe0\xa6\x9f\xe0\xa7\x8b\xe0\xa6\xac\xe0\xa6\xb0\0" + "\xe0\xa6\xa8\xe0\xa6\xad\xe0\xa7\x87\xe0\xa6\xae\xe0\xa7\x8d\xe0\xa6\xac\xe0\xa6\xb0\0" + "\xe0\xa6\xa1\xe0\xa6\xbf\xe0\xa6\xb8\xe0\xa7\x87\xe0\xa6\xae\xe0\xa7\x8d\xe0\xa6\xac\xe0\xa6\xb0\0" + "\xe0\xa8\x90\xe0\xa8\xa4\xe0\xa8\xb5\xe0\xa8\xbe\xe0\xa8\xb0\0" + "\xe0\xa8\xb8\xe0\xa9\x8b\xe0\xa8\xae\xe0\xa8\xb5\xe0\xa8\xbe\xe0\xa8\xb0\0" + "\xe0\xa8\xae\xe0\xa9\xb0\xe0\xa8\x97\xe0\xa8\xb2\xe0\xa8\xb5\xe0\xa8\xbe\xe0\xa8\xb0\0" + "\xe0\xa8\xac\xe0\xa9\x81\xe0\xa9\xb1\xe0\xa8\xa7\xe0\xa8\xb5\xe0\xa8\xbe\xe0\xa8\xb0\0" + "\xe0\xa8\xb5\xe0\xa9\x80\xe0\xa8\xb0\xe0\xa8\xb5\xe0\xa8\xbe\xe0\xa8\xb0\0" + "\xe0\xa8\xb8\xe0\xa8\xbc\xe0\xa9\x81\xe0\xa9\xb1\xe0\xa8\x95\xe0\xa8\xb0\xe0\xa8\xb5\xe0\xa8\xbe\xe0\xa8\xb0\0" + "\xe0\xa8\xb8\xe0\xa8\xbc\xe0\xa8\xa8\xe0\xa8\xbf\xe0\xa9\xb1\xe0\xa8\x9a\xe0\xa8\xb0\xe0\xa8\xb5\xe0\xa8\xbe\xe0\xa8\xb0\0" + "\xe0\xa8\x90\xe0\xa8\xa4\0" + "\xe0\xa8\xb8\xe0\xa9\x8b\xe0\xa8\xae\0" + "\xe0\xa8\xae\xe0\xa9\xb0\xe0\xa8\x97\xe0\xa8\xb2\0" + "\xe0\xa8\xac\xe0\xa9\x81\xe0\xa9\xb1\xe0\xa8\xa7\0" + "\xe0\xa8\xb5\xe0\xa9\x80\xe0\xa8\xb0\0" + "\xe0\xa8\xb8\xe0\xa8\xbc\xe0\xa9\x81\xe0\xa9\xb1\xe0\xa8\x95\xe0\xa8\xb0\0" + "\xe0\xa8\xb8\xe0\xa8\xbc\xe0\xa8\xa8\xe0\xa8\xbf\xe0\xa9\xb1\xe0\xa8\x9a\xe0\xa8\xb0\0" + "\xe0\xa8\x90\0" + "\xe0\xa8\xb8\xe0\xa9\x8b\0" + "\xe0\xa8\xae\xe0\xa9\xb0\0" + "\xe0\xa8\xac\xe0\xa9\x81\xe0\xa9\xb1\0" + "\xe0\xa8\xb5\xe0\xa9\x80\0" + "\xe0\xa8\xb8\xe0\xa8\xbc\xe0\xa9\x81\xe0\xa9\xb1\0" + "\xe0\xa8\xb8\xe0\xa8\xbc\0" + "\xe0\xa8\x9c\xe0\xa8\xa8\xe0\xa8\xb5\xe0\xa8\xb0\xe0\xa9\x80\0" + "\xe0\xa8\xab\xe0\xa8\xbc\xe0\xa8\xb0\xe0\xa8\xb5\xe0\xa8\xb0\xe0\xa9\x80\0" + "\xe0\xa8\xae\xe0\xa8\xbe\xe0\xa8\xb0\xe0\xa8\x9a\0" + "\xe0\xa8\x85\xe0\xa8\xaa\xe0\xa9\x8d\xe0\xa8\xb0\xe0\xa9\x88\xe0\xa8\xb2\0" + "\xe0\xa8\xae\xe0\xa8\x88\0" + "\xe0\xa8\x9c\xe0\xa9\x82\xe0\xa8\xa8\0" + "\xe0\xa8\x9c\xe0\xa9\x81\xe0\xa8\xb2\xe0\xa8\xbe\xe0\xa8\x88\0" + "\xe0\xa8\x85\xe0\xa8\x97\xe0\xa8\xb8\xe0\xa8\xa4\0" + "\xe0\xa8\xb8\xe0\xa8\xa4\xe0\xa9\xb0\xe0\xa8\xac\xe0\xa8\xb0\0" + "\xe0\xa8\x85\xe0\xa8\x95\xe0\xa8\xa4\xe0\xa9\x82\xe0\xa8\xac\xe0\xa8\xb0\0" + "\xe0\xa8\xa8\xe0\xa8\xb5\xe0\xa9\xb0\xe0\xa8\xac\xe0\xa8\xb0\0" + "\xe0\xa8\xa6\xe0\xa8\xb8\xe0\xa9\xb0\xe0\xa8\xac\xe0\xa8\xb0\0" + "\xe0\xa8\x9c\xe0\xa8\xa8\0" + "\xe0\xa8\xab\xe0\xa8\xbc\xe0\xa8\xb0\0" + "\xe0\xa8\x85\xe0\xa8\xaa\xe0\xa9\x8d\xe0\xa8\xb0\xe0\xa9\x88\0" + "\xe0\xa8\x9c\xe0\xa9\x81\xe0\xa8\xb2\xe0\xa8\xbe\0" + "\xe0\xa8\x85\xe0\xa8\x97\0" + "\xe0\xa8\xb8\xe0\xa8\xa4\xe0\xa9\xb0\0" + "\xe0\xa8\x85\xe0\xa8\x95\xe0\xa8\xa4\xe0\xa9\x82\0" + "\xe0\xa8\xa8\xe0\xa8\xb5\xe0\xa9\xb0\0" + "\xe0\xa8\xa6\xe0\xa8\xb8\xe0\xa9\xb0\0" + "\xe0\xaa\xb0\xe0\xaa\xb5\xe0\xaa\xbf\xe0\xaa\xb5\xe0\xaa\xbe\xe0\xaa\xb0\0" + "\xe0\xaa\xb8\xe0\xab\x8b\xe0\xaa\xae\xe0\xaa\xb5\xe0\xaa\xbe\xe0\xaa\xb0\0" + "\xe0\xaa\xae\xe0\xaa\x82\xe0\xaa\x97\xe0\xaa\xb3\xe0\xaa\xb5\xe0\xaa\xbe\xe0\xaa\xb0\0" + "\xe0\xaa\xac\xe0\xab\x81\xe0\xaa\xa7\xe0\xaa\xb5\xe0\xaa\xbe\xe0\xaa\xb0\0" + "\xe0\xaa\x97\xe0\xab\x81\xe0\xaa\xb0\xe0\xab\x81\xe0\xaa\xb5\xe0\xaa\xbe\xe0\xaa\xb0\0" + "\xe0\xaa\xb6\xe0\xab\x81\xe0\xaa\x95\xe0\xab\x8d\xe0\xaa\xb0\xe0\xaa\xb5\xe0\xaa\xbe\xe0\xaa\xb0\0" + "\xe0\xaa\xb6\xe0\xaa\xa8\xe0\xaa\xbf\xe0\xaa\xb5\xe0\xaa\xbe\xe0\xaa\xb0\0" + "\xe0\xaa\xb0\xe0\xaa\xb5\xe0\xaa\xbf\0" + "\xe0\xaa\xb8\xe0\xab\x8b\xe0\xaa\xae\0" + "\xe0\xaa\xae\xe0\xaa\x82\xe0\xaa\x97\xe0\xaa\xb3\0" + "\xe0\xaa\xac\xe0\xab\x81\xe0\xaa\xa7\0" + "\xe0\xaa\x97\xe0\xab\x81\xe0\xaa\xb0\xe0\xab\x81\0" + "\xe0\xaa\xb6\xe0\xab\x81\xe0\xaa\x95\xe0\xab\x8d\xe0\xaa\xb0\0" + "\xe0\xaa\xb6\xe0\xaa\xa8\xe0\xaa\xbf\0" + "\xe0\xaa\xb0\0" + "\xe0\xaa\xb8\xe0\xab\x8b\0" + "\xe0\xaa\xae\xe0\xaa\x82\0" + "\xe0\xaa\xac\xe0\xab\x81\0" + "\xe0\xaa\x97\xe0\xab\x81\0" + "\xe0\xaa\xb6\xe0\xab\x81\0" + "\xe0\xaa\xb6\0" + "\xe0\xaa\x9c\xe0\xaa\xbe\xe0\xaa\xa8\xe0\xab\x8d\xe0\xaa\xaf\xe0\xab\x81\xe0\xaa\x86\xe0\xaa\xb0\xe0\xab\x80\0" + "\xe0\xaa\xab\xe0\xab\x87\xe0\xaa\xac\xe0\xab\x8d\xe0\xaa\xb0\xe0\xab\x81\xe0\xaa\x86\xe0\xaa\xb0\xe0\xab\x80\0" + "\xe0\xaa\xae\xe0\xaa\xbe\xe0\xaa\xb0\xe0\xab\x8d\xe0\xaa\x9a\0" + "\xe0\xaa\x8f\xe0\xaa\xaa\xe0\xab\x8d\xe0\xaa\xb0\xe0\xaa\xbf\xe0\xaa\xb2\0" + "\xe0\xaa\xae\xe0\xab\x87\0" + "\xe0\xaa\x9c\xe0\xab\x82\xe0\xaa\xa8\0" + "\xe0\xaa\x9c\xe0\xab\x81\xe0\xaa\xb2\xe0\xaa\xbe\xe0\xaa\x88\0" + "\xe0\xaa\x91\xe0\xaa\x97\xe0\xaa\xb8\xe0\xab\x8d\xe0\xaa\x9f\0" + "\xe0\xaa\xb8\xe0\xaa\xaa\xe0\xab\x8d\xe0\xaa\x9f\xe0\xab\x87\xe0\xaa\xae\xe0\xab\x8d\xe0\xaa\xac\xe0\xaa\xb0\0" + "\xe0\xaa\x91\xe0\xaa\x95\xe0\xab\x8d\xe0\xaa\x9f\xe0\xab\x8b\xe0\xaa\xac\xe0\xaa\xb0\0" + "\xe0\xaa\xa8\xe0\xaa\xb5\xe0\xab\x87\xe0\xaa\xae\xe0\xab\x8d\xe0\xaa\xac\xe0\xaa\xb0\0" + "\xe0\xaa\xa1\xe0\xaa\xbf\xe0\xaa\xb8\xe0\xab\x87\xe0\xaa\xae\xe0\xab\x8d\xe0\xaa\xac\xe0\xaa\xb0\0" + "\xe0\xaa\x9c\xe0\xaa\xbe\xe0\xaa\xa8\xe0\xab\x8d\xe0\xaa\xaf\xe0\xab\x81\0" + "\xe0\xaa\xab\xe0\xab\x87\xe0\xaa\xac\xe0\xab\x8d\xe0\xaa\xb0\xe0\xab\x81\0" + "\xe0\xaa\xb8\xe0\xaa\xaa\xe0\xab\x8d\xe0\xaa\x9f\xe0\xab\x87\0" + "\xe0\xaa\x91\xe0\xaa\x95\xe0\xab\x8d\xe0\xaa\x9f\xe0\xab\x8b\0" + "\xe0\xaa\xa8\xe0\xaa\xb5\xe0\xab\x87\0" + "\xe0\xaa\xa1\xe0\xaa\xbf\xe0\xaa\xb8\xe0\xab\x87\0" + "\xe0\xac\xb0\xe0\xac\xac\xe0\xac\xbf\xe0\xac\xac\xe0\xac\xbe\xe0\xac\xb0\0" + "\xe0\xac\xb8\xe0\xad\x8b\xe0\xac\xae\xe0\xac\xac\xe0\xac\xbe\xe0\xac\xb0\0" + "\xe0\xac\xae\xe0\xac\x99\xe0\xad\x8d\xe0\xac\x97\xe0\xac\xb3\xe0\xac\xac\xe0\xac\xbe\xe0\xac\xb0\0" + "\xe0\xac\xac\xe0\xad\x81\xe0\xac\xa7\xe0\xac\xac\xe0\xac\xbe\xe0\xac\xb0\0" + "\xe0\xac\x97\xe0\xad\x81\xe0\xac\xb0\xe0\xad\x81\xe0\xac\xac\xe0\xac\xbe\xe0\xac\xb0\0" + "\xe0\xac\xb6\xe0\xad\x81\xe0\xac\x95\xe0\xad\x8d\xe0\xac\xb0\xe0\xac\xac\xe0\xac\xbe\xe0\xac\xb0\0" + "\xe0\xac\xb6\xe0\xac\xa8\xe0\xac\xbf\xe0\xac\xac\xe0\xac\xbe\xe0\xac\xb0\0" + "\xe0\xac\xb0\xe0\xac\xac\xe0\xac\xbf\0" + "\xe0\xac\xb8\xe0\xad\x8b\xe0\xac\xae\0" + "\xe0\xac\xae\xe0\xac\x99\xe0\xad\x8d\xe0\xac\x97\xe0\xac\xb3\0" + "\xe0\xac\xac\xe0\xad\x81\xe0\xac\xa7\0" + "\xe0\xac\x97\xe0\xad\x81\xe0\xac\xb0\xe0\xad\x81\0" + "\xe0\xac\xb6\xe0\xad\x81\xe0\xac\x95\xe0\xad\x8d\xe0\xac\xb0\0" + "\xe0\xac\xb6\xe0\xac\xa8\xe0\xac\xbf\0" + "\xe0\xac\xb0\0" + "\xe0\xac\xb8\xe0\xad\x8b\0" + "\xe0\xac\xae\0" + "\xe0\xac\xac\xe0\xad\x81\0" + "\xe0\xac\x97\xe0\xad\x81\0" + "\xe0\xac\xb6\xe0\xad\x81\0" + "\xe0\xac\xb6\0" + "\xe0\xac\x9c\xe0\xac\xbe\xe0\xac\xa8\xe0\xad\x81\xe0\xac\x86\xe0\xac\xb0\xe0\xad\x80\0" + "\xe0\xac\xab\xe0\xad\x87\xe0\xac\xac\xe0\xad\x83\xe0\xac\x86\xe0\xac\xb0\xe0\xad\x80\0" + "\xe0\xac\xae\xe0\xac\xbe\xe0\xac\xb0\xe0\xad\x8d\xe0\xac\x9a\xe0\xad\x8d\xe0\xac\x9a\0" + "\xe0\xac\x85\xe0\xac\xaa\xe0\xad\x8d\xe0\xac\xb0\xe0\xad\x87\xe0\xac\xb2\0" + "\xe0\xac\xae\xe0\xac\x87\0" + "\xe0\xac\x9c\xe0\xad\x81\xe0\xac\xa8\0" + "\xe0\xac\x9c\xe0\xad\x81\xe0\xac\xb2\xe0\xac\xbe\xe0\xac\x87\0" + "\xe0\xac\x85\xe0\xac\x97\xe0\xac\xb7\xe0\xad\x8d\xe0\xac\x9f\0" + "\xe0\xac\xb8\xe0\xad\x87\xe0\xac\xaa\xe0\xad\x8d\xe0\xac\x9f\xe0\xad\x87\xe0\xac\xae\xe0\xad\x8d\xe0\xac\xac\xe0\xac\xb0\0" + "\xe0\xac\x85\xe0\xac\x95\xe0\xad\x8d\xe0\xac\x9f\xe0\xad\x8b\xe0\xac\xac\xe0\xac\xb0\0" + "\xe0\xac\xa8\xe0\xac\xad\xe0\xad\x87\xe0\xac\xae\xe0\xad\x8d\xe0\xac\xac\xe0\xac\xb0\0" + "\xe0\xac\xa1\xe0\xac\xbf\xe0\xac\xb8\xe0\xad\x87\xe0\xac\xae\xe0\xad\x8d\xe0\xac\xac\xe0\xac\xb0\0" + "\xe0\xae\x9e\xe0\xae\xbe\xe0\xae\xaf\xe0\xae\xbf\xe0\xae\xb1\xe0\xaf\x81\0" + "\xe0\xae\xa4\xe0\xae\xbf\xe0\xae\x99\xe0\xaf\x8d\xe0\xae\x95\xe0\xae\xb3\xe0\xaf\x8d\0" + "\xe0\xae\x9a\xe0\xaf\x86\xe0\xae\xb5\xe0\xaf\x8d\xe0\xae\xb5\xe0\xae\xbe\xe0\xae\xaf\xe0\xaf\x8d\0" + "\xe0\xae\xaa\xe0\xaf\x81\xe0\xae\xa4\xe0\xae\xa9\xe0\xaf\x8d\0" + "\xe0\xae\xb5\xe0\xae\xbf\xe0\xae\xaf\xe0\xae\xbe\xe0\xae\xb4\xe0\xae\xa9\xe0\xaf\x8d\0" + "\xe0\xae\xb5\xe0\xaf\x86\xe0\xae\xb3\xe0\xaf\x8d\xe0\xae\xb3\xe0\xae\xbf\0" + "\xe0\xae\x9a\xe0\xae\xa9\xe0\xae\xbf\0" + "\xe0\xae\x9e\xe0\xae\xbe\xe0\xae\xaf\xe0\xae\xbf.\0" + "\xe0\xae\xa4\xe0\xae\xbf\xe0\xae\x99\xe0\xaf\x8d.\0" + "\xe0\xae\x9a\xe0\xaf\x86\xe0\xae\xb5\xe0\xaf\x8d.\0" + "\xe0\xae\xaa\xe0\xaf\x81\xe0\xae\xa4.\0" + "\xe0\xae\xb5\xe0\xae\xbf\xe0\xae\xaf\xe0\xae\xbe.\0" + "\xe0\xae\xb5\xe0\xaf\x86\xe0\xae\xb3\xe0\xaf\x8d.\0" + "\xe0\xae\x9e\xe0\xae\xbe\0" + "\xe0\xae\xa4\xe0\xae\xbf\0" + "\xe0\xae\x9a\xe0\xaf\x86\0" + "\xe0\xae\xaa\xe0\xaf\x81\0" + "\xe0\xae\xb5\xe0\xae\xbf\0" + "\xe0\xae\xb5\xe0\xaf\x86\0" + "\xe0\xae\x9a\0" + "\xe0\xae\x9c\xe0\xae\xa9\xe0\xae\xb5\xe0\xae\xb0\xe0\xae\xbf\0" + "\xe0\xae\xaa\xe0\xae\xbf\xe0\xae\xaa\xe0\xaf\x8d\xe0\xae\xb0\xe0\xae\xb5\xe0\xae\xb0\xe0\xae\xbf\0" + "\xe0\xae\xae\xe0\xae\xbe\xe0\xae\xb0\xe0\xaf\x8d\xe0\xae\x9a\xe0\xaf\x8d\0" + "\xe0\xae\x8f\xe0\xae\xaa\xe0\xaf\x8d\xe0\xae\xb0\xe0\xae\xb2\xe0\xaf\x8d\0" + "\xe0\xae\xae\xe0\xaf\x87\0" + "\xe0\xae\x9c\xe0\xaf\x82\xe0\xae\xa9\xe0\xaf\x8d\0" + "\xe0\xae\x9c\xe0\xaf\x82\xe0\xae\xb2\xe0\xaf\x88\0" + "\xe0\xae\x86\xe0\xae\x95\xe0\xae\xb8\xe0\xaf\x8d\xe0\xae\x9f\xe0\xaf\x8d\0" + "\xe0\xae\x9a\xe0\xaf\x86\xe0\xae\xaa\xe0\xaf\x8d\xe0\xae\x9f\xe0\xae\xae\xe0\xaf\x8d\xe0\xae\xaa\xe0\xae\xb0\xe0\xaf\x8d\0" + "\xe0\xae\x85\xe0\xae\x95\xe0\xaf\x8d\xe0\xae\x9f\xe0\xaf\x8b\xe0\xae\xaa\xe0\xae\xb0\xe0\xaf\x8d\0" + "\xe0\xae\xa8\xe0\xae\xb5\xe0\xae\xae\xe0\xaf\x8d\xe0\xae\xaa\xe0\xae\xb0\xe0\xaf\x8d\0" + "\xe0\xae\x9f\xe0\xae\xbf\xe0\xae\x9a\xe0\xae\xae\xe0\xaf\x8d\xe0\xae\xaa\xe0\xae\xb0\xe0\xaf\x8d\0" + "\xe0\xae\x9c\xe0\xae\xa9.\0" + "\xe0\xae\xaa\xe0\xae\xbf\xe0\xae\xaa\xe0\xaf\x8d.\0" + "\xe0\xae\xae\xe0\xae\xbe\xe0\xae\xb0\xe0\xaf\x8d.\0" + "\xe0\xae\x8f\xe0\xae\xaa\xe0\xaf\x8d.\0" + "\xe0\xae\x86\xe0\xae\x95.\0" + "\xe0\xae\x9a\xe0\xaf\x86\xe0\xae\xaa\xe0\xaf\x8d.\0" + "\xe0\xae\x85\xe0\xae\x95\xe0\xaf\x8d.\0" + "\xe0\xae\xa8\xe0\xae\xb5.\0" + "\xe0\xae\x9f\xe0\xae\xbf\xe0\xae\x9a.\0" + "\xe0\xb0\x86\xe0\xb0\xa6\xe0\xb0\xbf\xe0\xb0\xb5\xe0\xb0\xbe\xe0\xb0\xb0\xe0\xb0\x82\0" + "\xe0\xb0\xb8\xe0\xb1\x8b\xe0\xb0\xae\xe0\xb0\xb5\xe0\xb0\xbe\xe0\xb0\xb0\xe0\xb0\x82\0" + "\xe0\xb0\xae\xe0\xb0\x82\xe0\xb0\x97\xe0\xb0\xb3\xe0\xb0\xb5\xe0\xb0\xbe\xe0\xb0\xb0\xe0\xb0\x82\0" + "\xe0\xb0\xac\xe0\xb1\x81\xe0\xb0\xa7\xe0\xb0\xb5\xe0\xb0\xbe\xe0\xb0\xb0\xe0\xb0\x82\0" + "\xe0\xb0\x97\xe0\xb1\x81\xe0\xb0\xb0\xe0\xb1\x81\xe0\xb0\xb5\xe0\xb0\xbe\xe0\xb0\xb0\xe0\xb0\x82\0" + "\xe0\xb0\xb6\xe0\xb1\x81\xe0\xb0\x95\xe0\xb1\x8d\xe0\xb0\xb0\xe0\xb0\xb5\xe0\xb0\xbe\xe0\xb0\xb0\xe0\xb0\x82\0" + "\xe0\xb0\xb6\xe0\xb0\xa8\xe0\xb0\xbf\xe0\xb0\xb5\xe0\xb0\xbe\xe0\xb0\xb0\xe0\xb0\x82\0" + "\xe0\xb0\x86\xe0\xb0\xa6\xe0\xb0\xbf\0" + "\xe0\xb0\xb8\xe0\xb1\x8b\xe0\xb0\xae\0" + "\xe0\xb0\xae\xe0\xb0\x82\xe0\xb0\x97\xe0\xb0\xb3\0" + "\xe0\xb0\xac\xe0\xb1\x81\xe0\xb0\xa7\0" + "\xe0\xb0\x97\xe0\xb1\x81\xe0\xb0\xb0\xe0\xb1\x81\0" + "\xe0\xb0\xb6\xe0\xb1\x81\xe0\xb0\x95\xe0\xb1\x8d\xe0\xb0\xb0\0" + "\xe0\xb0\xb6\xe0\xb0\xa8\xe0\xb0\xbf\0" + "\xe0\xb0\x86\0" + "\xe0\xb0\xb8\xe0\xb1\x8b\0" + "\xe0\xb0\xae\0" + "\xe0\xb0\xac\xe0\xb1\x81\0" + "\xe0\xb0\x97\xe0\xb1\x81\0" + "\xe0\xb0\xb6\xe0\xb1\x81\0" + "\xe0\xb0\xb6\0" + "\xe0\xb0\x9c\xe0\xb0\xa8\xe0\xb0\xb5\xe0\xb0\xb0\xe0\xb0\xbf\0" + "\xe0\xb0\xab\xe0\xb0\xbf\xe0\xb0\xac\xe0\xb1\x8d\xe0\xb0\xb0\xe0\xb0\xb5\xe0\xb0\xb0\xe0\xb0\xbf\0" + "\xe0\xb0\xae\xe0\xb0\xbe\xe0\xb0\xb0\xe0\xb1\x8d\xe0\xb0\x9a\xe0\xb0\xbf\0" + "\xe0\xb0\x8f\xe0\xb0\xaa\xe0\xb1\x8d\xe0\xb0\xb0\xe0\xb0\xbf\xe0\xb0\xb2\xe0\xb1\x8d\0" + "\xe0\xb0\xae\xe0\xb1\x87\0" + "\xe0\xb0\x9c\xe0\xb1\x82\xe0\xb0\xa8\xe0\xb1\x8d\0" + "\xe0\xb0\x9c\xe0\xb1\x81\xe0\xb0\xb2\xe0\xb1\x88\0" + "\xe0\xb0\x86\xe0\xb0\x97\xe0\xb0\xb8\xe0\xb1\x8d\xe0\xb0\x9f\xe0\xb1\x81\0" + "\xe0\xb0\xb8\xe0\xb1\x86\xe0\xb0\xaa\xe0\xb1\x8d\xe0\xb0\x9f\xe0\xb1\x86\xe0\xb0\x82\xe0\xb0\xac\xe0\xb0\xb0\xe0\xb1\x8d\0" + "\xe0\xb0\x85\xe0\xb0\x95\xe0\xb1\x8d\xe0\xb0\x9f\xe0\xb1\x8b\xe0\xb0\xac\xe0\xb0\xb0\xe0\xb1\x8d\0" + "\xe0\xb0\xa8\xe0\xb0\xb5\xe0\xb0\x82\xe0\xb0\xac\xe0\xb0\xb0\xe0\xb1\x8d\0" + "\xe0\xb0\xa1\xe0\xb0\xbf\xe0\xb0\xb8\xe0\xb1\x86\xe0\xb0\x82\xe0\xb0\xac\xe0\xb0\xb0\xe0\xb1\x8d\0" + "\xe0\xb0\x9c\xe0\xb0\xa8\0" + "\xe0\xb0\xab\xe0\xb0\xbf\xe0\xb0\xac\xe0\xb1\x8d\xe0\xb0\xb0\0" + "\xe0\xb0\x8f\xe0\xb0\xaa\xe0\xb1\x8d\xe0\xb0\xb0\xe0\xb0\xbf\0" + "\xe0\xb0\xb8\xe0\xb1\x86\xe0\xb0\xaa\xe0\xb1\x8d\xe0\xb0\x9f\xe0\xb1\x86\xe0\xb0\x82\0" + "\xe0\xb0\x85\xe0\xb0\x95\xe0\xb1\x8d\xe0\xb0\x9f\xe0\xb1\x8b\0" + "\xe0\xb0\xa8\xe0\xb0\xb5\xe0\xb0\x82\0" + "\xe0\xb0\xa1\xe0\xb0\xbf\xe0\xb0\xb8\xe0\xb1\x86\xe0\xb0\x82\0" + "\xe0\xb2\xad\xe0\xb2\xbe\xe0\xb2\xa8\xe0\xb3\x81\xe0\xb2\xb5\xe0\xb2\xbe\xe0\xb2\xb0\0" + "\xe0\xb2\xb8\xe0\xb3\x8b\xe0\xb2\xae\xe0\xb2\xb5\xe0\xb2\xbe\xe0\xb2\xb0\0" + "\xe0\xb2\xae\xe0\xb2\x82\xe0\xb2\x97\xe0\xb2\xb3\xe0\xb2\xb5\xe0\xb2\xbe\xe0\xb2\xb0\0" + "\xe0\xb2\xac\xe0\xb3\x81\xe0\xb2\xa7\xe0\xb2\xb5\xe0\xb2\xbe\xe0\xb2\xb0\0" + "\xe0\xb2\x97\xe0\xb3\x81\xe0\xb2\xb0\xe0\xb3\x81\xe0\xb2\xb5\xe0\xb2\xbe\xe0\xb2\xb0\0" + "\xe0\xb2\xb6\xe0\xb3\x81\xe0\xb2\x95\xe0\xb3\x8d\xe0\xb2\xb0\xe0\xb2\xb5\xe0\xb2\xbe\xe0\xb2\xb0\0" + "\xe0\xb2\xb6\xe0\xb2\xa8\xe0\xb2\xbf\xe0\xb2\xb5\xe0\xb2\xbe\xe0\xb2\xb0\0" + "\xe0\xb2\xad\xe0\xb2\xbe\xe0\xb2\xa8\xe0\xb3\x81\0" + "\xe0\xb2\xb8\xe0\xb3\x8b\xe0\xb2\xae\0" + "\xe0\xb2\xae\xe0\xb2\x82\xe0\xb2\x97\xe0\xb2\xb3\0" + "\xe0\xb2\xac\xe0\xb3\x81\xe0\xb2\xa7\0" + "\xe0\xb2\x97\xe0\xb3\x81\xe0\xb2\xb0\xe0\xb3\x81\0" + "\xe0\xb2\xb6\xe0\xb3\x81\xe0\xb2\x95\xe0\xb3\x8d\xe0\xb2\xb0\0" + "\xe0\xb2\xb6\xe0\xb2\xa8\xe0\xb2\xbf\0" + "\xe0\xb2\xad\xe0\xb2\xbe\0" + "\xe0\xb2\xb8\xe0\xb3\x8b\0" + "\xe0\xb2\xae\xe0\xb2\x82\0" + "\xe0\xb2\xac\xe0\xb3\x81\0" + "\xe0\xb2\x97\xe0\xb3\x81\0" + "\xe0\xb2\xb6\xe0\xb3\x81\0" + "\xe0\xb2\xb6\0" + "\xe0\xb2\x9c\xe0\xb2\xa8\xe0\xb2\xb5\xe0\xb2\xb0\xe0\xb2\xbf\0" + "\xe0\xb2\xab\xe0\xb3\x86\xe0\xb2\xac\xe0\xb3\x8d\xe0\xb2\xb0\xe0\xb2\xb5\xe0\xb2\xb0\xe0\xb2\xbf\0" + "\xe0\xb2\xae\xe0\xb2\xbe\xe0\xb2\xb0\xe0\xb3\x8d\xe0\xb2\x9a\xe0\xb3\x8d\0" + "\xe0\xb2\x8f\xe0\xb2\xaa\xe0\xb3\x8d\xe0\xb2\xb0\xe0\xb2\xbf\xe0\xb2\xb2\xe0\xb3\x8d\0" + "\xe0\xb2\xae\xe0\xb3\x87\0" + "\xe0\xb2\x9c\xe0\xb3\x82\xe0\xb2\xa8\xe0\xb3\x8d\0" + "\xe0\xb2\x9c\xe0\xb3\x81\xe0\xb2\xb2\xe0\xb3\x88\0" + "\xe0\xb2\x86\xe0\xb2\x97\xe0\xb2\xb8\xe0\xb3\x8d\xe0\xb2\x9f\xe0\xb3\x8d\0" + "\xe0\xb2\xb8\xe0\xb3\x86\xe0\xb2\xaa\xe0\xb3\x8d\xe0\xb2\x9f\xe0\xb3\x86\xe0\xb2\x82\xe0\xb2\xac\xe0\xb2\xb0\xe0\xb3\x8d\0" + "\xe0\xb2\x85\xe0\xb2\x95\xe0\xb3\x8d\xe0\xb2\x9f\xe0\xb3\x8b\xe0\xb2\xac\xe0\xb2\xb0\xe0\xb3\x8d\0" + "\xe0\xb2\xa8\xe0\xb2\xb5\xe0\xb3\x86\xe0\xb2\x82\xe0\xb2\xac\xe0\xb2\xb0\xe0\xb3\x8d\0" + "\xe0\xb2\xa1\xe0\xb2\xbf\xe0\xb2\xb8\xe0\xb3\x86\xe0\xb2\x82\xe0\xb2\xac\xe0\xb2\xb0\xe0\xb3\x8d\0" + "\xe0\xb2\x9c\xe0\xb2\xa8\0" + "\xe0\xb2\xab\xe0\xb3\x86\xe0\xb2\xac\xe0\xb3\x8d\xe0\xb2\xb0\0" + "\xe0\xb2\x8f\xe0\xb2\xaa\xe0\xb3\x8d\xe0\xb2\xb0\xe0\xb2\xbf\0" + "\xe0\xb2\x86\xe0\xb2\x97\0" + "\xe0\xb2\xb8\xe0\xb3\x86\xe0\xb2\xaa\xe0\xb3\x8d\xe0\xb2\x9f\xe0\xb3\x86\xe0\xb2\x82\0" + "\xe0\xb2\x85\xe0\xb2\x95\xe0\xb3\x8d\xe0\xb2\x9f\xe0\xb3\x8b\0" + "\xe0\xb2\xa8\xe0\xb2\xb5\xe0\xb3\x86\xe0\xb2\x82\0" + "\xe0\xb2\xa1\xe0\xb2\xbf\xe0\xb2\xb8\xe0\xb3\x86\xe0\xb2\x82\0" + "\xe0\xb4\x9e\xe0\xb4\xbe\xe0\xb4\xaf\xe0\xb4\xb1\xe0\xb4\xbe\xe0\xb4\xb4\xe0\xb5\x8d\xe2\x80\x8c\xe0\xb4\x9a\0" + "\xe0\xb4\xa4\xe0\xb4\xbf\xe0\xb4\x99\xe0\xb5\x8d\xe0\xb4\x95\xe0\xb4\xb3\xe0\xb4\xbe\xe0\xb4\xb4\xe0\xb5\x8d\xe2\x80\x8c\xe0\xb4\x9a\0" + "\xe0\xb4\x9a\xe0\xb5\x8a\xe0\xb4\xb5\xe0\xb5\x8d\xe0\xb4\xb5\xe0\xb4\xbe\xe0\xb4\xb4\xe0\xb5\x8d\xe0\xb4\x9a\0" + "\xe0\xb4\xac\xe0\xb5\x81\xe0\xb4\xa7\xe0\xb4\xa8\xe0\xb4\xbe\xe0\xb4\xb4\xe0\xb5\x8d\xe2\x80\x8c\xe0\xb4\x9a\0" + "\xe0\xb4\xb5\xe0\xb5\x8d\xe0\xb4\xaf\xe0\xb4\xbe\xe0\xb4\xb4\xe0\xb4\xbe\xe0\xb4\xb4\xe0\xb5\x8d\xe2\x80\x8c\xe0\xb4\x9a\0" + "\xe0\xb4\xb5\xe0\xb5\x86\xe0\xb4\xb3\xe0\xb5\x8d\xe0\xb4\xb3\xe0\xb4\xbf\xe0\xb4\xaf\xe0\xb4\xbe\xe0\xb4\xb4\xe0\xb5\x8d\xe2\x80\x8c\xe0\xb4\x9a\0" + "\xe0\xb4\xb6\xe0\xb4\xa8\xe0\xb4\xbf\xe0\xb4\xaf\xe0\xb4\xbe\xe0\xb4\xb4\xe0\xb5\x8d\xe2\x80\x8c\xe0\xb4\x9a\0" + "\xe0\xb4\x9e\xe0\xb4\xbe\xe0\xb4\xaf\xe0\xb5\xbc\0" + "\xe0\xb4\xa4\xe0\xb4\xbf\xe0\xb4\x99\xe0\xb5\x8d\xe0\xb4\x95\xe0\xb5\xbe\0" + "\xe0\xb4\x9a\xe0\xb5\x8a\xe0\xb4\xb5\xe0\xb5\x8d\xe0\xb4\xb5\0" + "\xe0\xb4\xac\xe0\xb5\x81\xe0\xb4\xa7\xe0\xb5\xbb\0" + "\xe0\xb4\xb5\xe0\xb5\x8d\xe0\xb4\xaf\xe0\xb4\xbe\xe0\xb4\xb4\xe0\xb4\x82\0" + "\xe0\xb4\xb5\xe0\xb5\x86\xe0\xb4\xb3\xe0\xb5\x8d\xe0\xb4\xb3\xe0\xb4\xbf\0" + "\xe0\xb4\xb6\xe0\xb4\xa8\xe0\xb4\xbf\0" + "\xe0\xb4\x9e\xe0\xb4\xbe\0" + "\xe0\xb4\xa4\xe0\xb4\xbf\0" + "\xe0\xb4\x9a\xe0\xb5\x8a\0" + "\xe0\xb4\xac\xe0\xb5\x81\0" + "\xe0\xb4\xb5\xe0\xb5\x8d\xe0\xb4\xaf\xe0\xb4\xbe\0" + "\xe0\xb4\xb5\xe0\xb5\x86\0" + "\xe0\xb4\xb6\0" + "\xe0\xb4\x9c\xe0\xb4\xa8\xe0\xb5\x81\xe0\xb4\xb5\xe0\xb4\xb0\xe0\xb4\xbf\0" + "\xe0\xb4\xab\xe0\xb5\x86\xe0\xb4\xac\xe0\xb5\x8d\xe0\xb4\xb0\xe0\xb5\x81\xe0\xb4\xb5\xe0\xb4\xb0\xe0\xb4\xbf\0" + "\xe0\xb4\xae\xe0\xb4\xbe\xe0\xb5\xbc\xe0\xb4\x9a\xe0\xb5\x8d\xe0\xb4\x9a\xe0\xb5\x8d\0" + "\xe0\xb4\x8f\xe0\xb4\xaa\xe0\xb5\x8d\xe0\xb4\xb0\xe0\xb4\xbf\xe0\xb5\xbd\0" + "\xe0\xb4\xae\xe0\xb5\x87\xe0\xb4\xaf\xe0\xb5\x8d\0" + "\xe0\xb4\x9c\xe0\xb5\x82\xe0\xb5\xba\0" + "\xe0\xb4\x9c\xe0\xb5\x82\xe0\xb4\xb2\xe0\xb5\x88\0" + "\xe0\xb4\x93\xe0\xb4\x97\xe0\xb4\xb8\xe0\xb5\x8d\xe0\xb4\xb1\xe0\xb5\x8d\xe0\xb4\xb1\xe0\xb5\x8d\0" + "\xe0\xb4\xb8\xe0\xb5\x86\xe0\xb4\xaa\xe0\xb5\x8d\xe0\xb4\xb1\xe0\xb5\x8d\xe0\xb4\xb1\xe0\xb4\x82\xe0\xb4\xac\xe0\xb5\xbc\0" + "\xe0\xb4\x92\xe0\xb4\x95\xe0\xb5\x8d\xe2\x80\x8c\xe0\xb4\x9f\xe0\xb5\x8b\xe0\xb4\xac\xe0\xb5\xbc\0" + "\xe0\xb4\xa8\xe0\xb4\xb5\xe0\xb4\x82\xe0\xb4\xac\xe0\xb5\xbc\0" + "\xe0\xb4\xa1\xe0\xb4\xbf\xe0\xb4\xb8\xe0\xb4\x82\xe0\xb4\xac\xe0\xb5\xbc\0" + "\xe0\xb4\x9c\xe0\xb4\xa8\xe0\xb5\x81\0" + "\xe0\xb4\xab\xe0\xb5\x86\xe0\xb4\xac\xe0\xb5\x8d\xe0\xb4\xb0\xe0\xb5\x81\0" + "\xe0\xb4\xae\xe0\xb4\xbe\xe0\xb5\xbc\0" + "\xe0\xb4\x8f\xe0\xb4\xaa\xe0\xb5\x8d\xe0\xb4\xb0\xe0\xb4\xbf\0" + "\xe0\xb4\x93\xe0\xb4\x97\0" + "\xe0\xb4\xb8\xe0\xb5\x86\xe0\xb4\xaa\xe0\xb5\x8d\xe0\xb4\xb1\xe0\xb5\x8d\xe0\xb4\xb1\xe0\xb4\x82\0" + "\xe0\xb4\x92\xe0\xb4\x95\xe0\xb5\x8d\xe0\xb4\x9f\xe0\xb5\x8b\0" + "\xe0\xb4\xa8\xe0\xb4\xb5\xe0\xb4\x82\0" + "\xe0\xb4\xa1\xe0\xb4\xbf\xe0\xb4\xb8\xe0\xb4\x82\0" + "\xe0\xa6\xa6\xe0\xa7\x87\xe0\xa6\x93\xe0\xa6\xac\xe0\xa6\xbe\xe0\xa7\xb0\0" + "\xe0\xa6\xb8\xe0\xa7\x8b\xe0\xa6\xae\xe0\xa6\xac\xe0\xa6\xbe\xe0\xa7\xb0\0" + "\xe0\xa6\xae\xe0\xa6\x99\xe0\xa7\x8d\xe0\xa6\x97\xe0\xa6\xb2\xe0\xa6\xac\xe0\xa6\xbe\xe0\xa7\xb0\0" + "\xe0\xa6\xac\xe0\xa7\x81\xe0\xa6\xa7\xe0\xa6\xac\xe0\xa6\xbe\xe0\xa7\xb0\0" + "\xe0\xa6\xac\xe0\xa7\x83\xe0\xa6\xb9\xe0\xa6\xb7\xe0\xa7\x8d\xe0\xa6\xaa\xe0\xa6\xa4\xe0\xa6\xbf\xe0\xa6\xac\xe0\xa6\xbe\xe0\xa7\xb0\0" + "\xe0\xa6\xb6\xe0\xa7\x81\xe0\xa6\x95\xe0\xa7\x8d\xe0\xa7\xb0\xe0\xa6\xac\xe0\xa6\xbe\xe0\xa7\xb0\0" + "\xe0\xa6\xb6\xe0\xa6\xa8\xe0\xa6\xbf\xe0\xa6\xac\xe0\xa6\xbe\xe0\xa7\xb0\0" + "\xe0\xa7\xb0\xe0\xa6\xac\xe0\xa6\xbf\0" + "\xe0\xa6\xac\xe0\xa7\x83\xe0\xa6\xb9\xe0\xa6\xb7\xe0\xa7\x8d\xe0\xa6\xaa\xe0\xa6\xa4\xe0\xa6\xbf\0" + "\xe0\xa6\xb6\xe0\xa7\x81\xe0\xa6\x95\xe0\xa7\x8d\xe0\xa7\xb0\0" + "\xe0\xa6\x9c\xe0\xa6\xbe\xe0\xa6\xa8\xe0\xa7\x81\xe0\xa7\xb1\xe0\xa6\xbe\xe0\xa7\xb0\xe0\xa7\x80\0" + "\xe0\xa6\xab\xe0\xa7\x87\xe0\xa6\xac\xe0\xa7\x8d\xe0\xa7\xb0\xe0\xa7\x81\xe0\xa7\xb1\xe0\xa6\xbe\xe0\xa7\xb0\xe0\xa7\x80\0" + "\xe0\xa6\xae\xe0\xa6\xbe\xe0\xa7\xb0\xe0\xa7\x8d\xe0\xa6\x9a\0" + "\xe0\xa6\x8f\xe0\xa6\xaa\xe0\xa7\x8d\xe0\xa7\xb0\xe0\xa6\xbf\xe0\xa6\xb2\0" + "\xe0\xa6\x86\xe0\xa6\x97\xe0\xa6\xb7\xe0\xa7\x8d\xe0\xa6\x9f\0" + "\xe0\xa6\x9b\xe0\xa7\x87\xe0\xa6\xaa\xe0\xa7\x8d\xe0\xa6\xa4\xe0\xa7\x87\xe0\xa6\xae\xe0\xa7\x8d\xe0\xa6\xac\xe0\xa7\xb0\0" + "\xe0\xa6\x85\xe0\xa6\x95\xe0\xa7\x8d\xe0\xa6\x9f\xe0\xa7\x8b\xe0\xa6\xac\xe0\xa7\xb0\0" + "\xe0\xa6\xa8\xe0\xa7\xb1\xe0\xa7\x87\xe0\xa6\xae\xe0\xa7\x8d\xe0\xa6\xac\xe0\xa7\xb0\0" + "\xe0\xa6\xa1\xe0\xa6\xbf\xe0\xa6\x9a\xe0\xa7\x87\xe0\xa6\xae\xe0\xa7\x8d\xe0\xa6\xac\xe0\xa7\xb0\0" + "\xe0\xa6\x9c\xe0\xa6\xbe\xe0\xa6\xa8\xe0\xa7\x81\0" + "\xe0\xa6\xab\xe0\xa7\x87\xe0\xa6\xac\xe0\xa7\x8d\xe0\xa7\xb0\xe0\xa7\x81\0" + "\xe0\xa6\x86\xe0\xa6\x97\0" + "\xe0\xa6\xb8\xe0\xa7\x87\xe0\xa6\xaa\xe0\xa7\x8d\xe0\xa6\x9f\0" + "\xe0\xa6\x85\xe0\xa6\x95\xe0\xa7\x8d\xe0\xa6\x9f\xe0\xa7\x8b\0" + "\xe0\xa6\xa8\xe0\xa6\xad\xe0\xa7\x87\0" + "\xe0\xa6\xa1\xe0\xa6\xbf\xe0\xa6\xb8\xe0\xa7\x87\0" + "\xe0\xa4\xae\xe0\xa4\x82\xe0\xa4\x97\xe0\xa4\xb3\xe0\xa4\xb5\xe0\xa4\xbe\xe0\xa4\xb0\0" + "\xe0\xa4\xae\xe0\xa4\x82\xe0\xa4\x97\xe0\xa4\xb3\0" + "\xe0\xa4\x9c\xe0\xa4\xbe\xe0\xa4\xa8\xe0\xa5\x87\xe0\xa4\xb5\xe0\xa4\xbe\xe0\xa4\xb0\xe0\xa5\x80\0" + "\xe0\xa4\xab\xe0\xa5\x87\xe0\xa4\xac\xe0\xa5\x8d\xe0\xa4\xb0\xe0\xa5\x81\xe0\xa4\xb5\xe0\xa4\xbe\xe0\xa4\xb0\xe0\xa5\x80\0" + "\xe0\xa4\x8f\xe0\xa4\xaa\xe0\xa5\x8d\xe0\xa4\xb0\xe0\xa4\xbf\xe0\xa4\xb2\0" + "\xe0\xa4\xae\xe0\xa5\x87\0" + "\xe0\xa4\x9c\xe0\xa5\x81\xe0\xa4\xb2\xe0\xa5\x88\0" + "\xe0\xa4\x91\xe0\xa4\x97\xe0\xa4\xb8\xe0\xa5\x8d\xe0\xa4\x9f\0" + "\xe0\xa4\xb8\xe0\xa4\xaa\xe0\xa5\x8d\xe0\xa4\x9f\xe0\xa5\x87\xe0\xa4\x82\xe0\xa4\xac\xe0\xa4\xb0\0" + "\xe0\xa4\x91\xe0\xa4\x95\xe0\xa5\x8d\xe0\xa4\x9f\xe0\xa5\x8b\xe0\xa4\xac\xe0\xa4\xb0\0" + "\xe0\xa4\xa8\xe0\xa5\x8b\xe0\xa4\xb5\xe0\xa5\x8d\xe0\xa4\xb9\xe0\xa5\x87\xe0\xa4\x82\xe0\xa4\xac\xe0\xa4\xb0\0" + "\xe0\xa4\xa1\xe0\xa4\xbf\xe0\xa4\xb8\xe0\xa5\x87\xe0\xa4\x82\xe0\xa4\xac\xe0\xa4\xb0\0" + "\xe0\xa4\x9c\xe0\xa4\xbe\xe0\xa4\xa8\xe0\xa5\x87\0" + "\xe0\xa4\xab\xe0\xa5\x87\xe0\xa4\xac\xe0\xa5\x8d\xe0\xa4\xb0\xe0\xa5\x81\0" + "\xe0\xa4\x8f\xe0\xa4\xaa\xe0\xa5\x8d\xe0\xa4\xb0\xe0\xa4\xbf\0" + "\xe0\xa4\x91\xe0\xa4\x97\0" + "\xe0\xa4\xb8\xe0\xa4\xaa\xe0\xa5\x8d\xe0\xa4\x9f\xe0\xa5\x87\xe0\xa4\x82\0" + "\xe0\xa4\x91\xe0\xa4\x95\xe0\xa5\x8d\xe0\xa4\x9f\xe0\xa5\x8b\0" + "\xe0\xa4\xa8\xe0\xa5\x8b\xe0\xa4\xb5\xe0\xa5\x8d\xe0\xa4\xb9\xe0\xa5\x87\xe0\xa4\x82\0" + "\xe0\xa4\xa1\xe0\xa4\xbf\xe0\xa4\xb8\xe0\xa5\x87\xe0\xa4\x82\0" + "\xd0\xbd\xd1\x8f\xd0\xbc\0" + "\xd0\xb4\xd0\xb0\xd0\xb2\xd0\xb0\xd0\xb0\0" + "\xd0\xbc\xd1\x8f\xd0\xb3\xd0\xbc\xd0\xb0\xd1\x80\0" + "\xd0\xbb\xd1\x85\xd0\xb0\xd0\xb3\xd0\xb2\xd0\xb0\0" + "\xd0\xbf\xd2\xaf\xd1\x80\xd1\x8d\xd0\xb2\0" + "\xd0\xb1\xd0\xb0\xd0\xb0\xd1\x81\xd0\xb0\xd0\xbd\0" + "\xd0\xb1\xd1\x8f\xd0\xbc\xd0\xb1\xd0\xb0\0" + "\xd0\x9d\xd1\x8f\0" + "\xd0\x94\xd0\xb0\0" + "\xd0\x9c\xd1\x8f\0" + "\xd0\x9b\xd1\x85\0" + "\xd0\x9f\xd2\xaf\0" + "\xd0\x91\xd0\xb0\0" + "\xd0\x91\xd1\x8f\0" + "\xd0\x9d\xd1\x8d\xd0\xb3\xd0\xb4\xd2\xaf\xd0\xb3\xd1\x8d\xd1\x8d\xd1\x80 \xd1\x81\xd0\xb0\xd1\x80\0" + "\xd0\xa5\xd0\xbe\xd1\x91\xd1\x80\xd0\xb4\xd1\x83\xd0\xb3\xd0\xb0\xd0\xb0\xd1\x80 \xd1\x81\xd0\xb0\xd1\x80\0" + "\xd0\x93\xd1\x83\xd1\x80\xd0\xb0\xd0\xb2\xd0\xb4\xd1\x83\xd0\xb3\xd0\xb0\xd0\xb0\xd1\x80 \xd1\x81\xd0\xb0\xd1\x80\0" + "\xd0\x94\xd3\xa9\xd1\x80\xd3\xa9\xd0\xb2\xd0\xb4\xd2\xaf\xd0\xb3\xd1\x8d\xd1\x8d\xd1\x80 \xd1\x81\xd0\xb0\xd1\x80\0" + "\xd0\xa2\xd0\xb0\xd0\xb2\xd0\xb4\xd1\x83\xd0\xb3\xd0\xb0\xd0\xb0\xd1\x80 \xd1\x81\xd0\xb0\xd1\x80\0" + "\xd0\x97\xd1\x83\xd1\x80\xd0\xb3\xd0\xb0\xd0\xb4\xd1\x83\xd0\xb3\xd0\xb0\xd0\xb0\xd1\x80 \xd1\x81\xd0\xb0\xd1\x80\0" + "\xd0\x94\xd0\xbe\xd0\xbb\xd0\xb4\xd1\x83\xd0\xb3\xd0\xb0\xd0\xb0\xd1\x80 \xd1\x81\xd0\xb0\xd1\x80\0" + "\xd0\x9d\xd0\xb0\xd0\xb9\xd0\xbc\xd0\xb4\xd1\x83\xd0\xb3\xd0\xb0\xd0\xb0\xd1\x80 \xd1\x81\xd0\xb0\xd1\x80\0" + "\xd0\x95\xd1\x81\xd0\xb4\xd2\xaf\xd0\xb3\xd1\x8d\xd1\x8d\xd1\x80 \xd1\x81\xd0\xb0\xd1\x80\0" + "\xd0\x90\xd1\x80\xd0\xb0\xd0\xb2\xd0\xb4\xd1\x83\xd0\xb3\xd0\xb0\xd0\xb0\xd1\x80 \xd1\x81\xd0\xb0\xd1\x80\0" + "\xd0\x90\xd1\x80\xd0\xb2\xd0\xb0\xd0\xbd \xd0\xbd\xd1\x8d\xd0\xb3\xd0\xb4\xd2\xaf\xd0\xb3\xd1\x8d\xd1\x8d\xd1\x80 \xd1\x81\xd0\xb0\xd1\x80\0" + "\xd0\x90\xd1\x80\xd0\xb2\xd0\xb0\xd0\xbd \xd1\x85\xd0\xbe\xd1\x91\xd1\x80\xd0\xb4\xd1\x83\xd0\xb3\xd0\xb0\xd0\xb0\xd1\x80 \xd1\x81\xd0\xb0\xd1\x80\0" + "1-\xd1\x80 \xd1\x81\xd0\xb0\xd1\x80\0" + "2-\xd1\x80 \xd1\x81\xd0\xb0\xd1\x80\0" + "3-\xd1\x80 \xd1\x81\xd0\xb0\xd1\x80\0" + "4-\xd1\x80 \xd1\x81\xd0\xb0\xd1\x80\0" + "5-\xd1\x80 \xd1\x81\xd0\xb0\xd1\x80\0" + "6-\xd1\x80 \xd1\x81\xd0\xb0\xd1\x80\0" + "7-\xd1\x80 \xd1\x81\xd0\xb0\xd1\x80\0" + "8-\xd1\x80 \xd1\x81\xd0\xb0\xd1\x80\0" + "9-\xd1\x80 \xd1\x81\xd0\xb0\xd1\x80\0" + "10-\xd1\x80 \xd1\x81\xd0\xb0\xd1\x80\0" + "11-\xd1\x80 \xd1\x81\xd0\xb0\xd1\x80\0" + "12-\xd1\x80 \xd1\x81\xd0\xb0\xd1\x80\0" + "\xe0\xbd\x82\xe0\xbd\x9f\xe0\xbd\xa0\xe0\xbc\x8b\xe0\xbd\x89\xe0\xbd\xb2\xe0\xbc\x8b\xe0\xbd\x98\xe0\xbc\x8b\0" + "\xe0\xbd\x82\xe0\xbd\x9f\xe0\xbd\xa0\xe0\xbc\x8b\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbc\x8b\0" + "\xe0\xbd\x82\xe0\xbd\x9f\xe0\xbd\xa0\xe0\xbc\x8b\xe0\xbd\x98\xe0\xbd\xb2\xe0\xbd\x82\xe0\xbc\x8b\xe0\xbd\x91\xe0\xbd\x98\xe0\xbd\xa2\xe0\xbc\x8b\0" + "\xe0\xbd\x82\xe0\xbd\x9f\xe0\xbd\xa0\xe0\xbc\x8b\xe0\xbd\xa3\xe0\xbe\xb7\xe0\xbd\x82\xe0\xbc\x8b\xe0\xbd\x94\xe0\xbc\x8b\0" + "\xe0\xbd\x82\xe0\xbd\x9f\xe0\xbd\xa0\xe0\xbc\x8b\xe0\xbd\x95\xe0\xbd\xb4\xe0\xbd\xa2\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbd\xb4\xe0\xbc\x8b\0" + "\xe0\xbd\x82\xe0\xbd\x9f\xe0\xbd\xa0\xe0\xbc\x8b\xe0\xbd\x94\xe0\xbc\x8b\xe0\xbd\xa6\xe0\xbd\x84\xe0\xbd\xa6\xe0\xbc\x8b\0" + "\xe0\xbd\x82\xe0\xbd\x9f\xe0\xbd\xa0\xe0\xbc\x8b\xe0\xbd\xa6\xe0\xbe\xa4\xe0\xbd\xba\xe0\xbd\x93\xe0\xbc\x8b\xe0\xbd\x94\xe0\xbc\x8b\0" + "\xe0\xbd\x89\xe0\xbd\xb2\xe0\xbc\x8b\xe0\xbd\x98\xe0\xbc\x8b\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbc\x8b\0" + "\xe0\xbd\x98\xe0\xbd\xb2\xe0\xbd\x82\xe0\xbc\x8b\xe0\xbd\x91\xe0\xbd\x98\xe0\xbd\xa2\xe0\xbc\x8b\0" + "\xe0\xbd\xa3\xe0\xbe\xb7\xe0\xbd\x82\xe0\xbc\x8b\xe0\xbd\x94\xe0\xbc\x8b\0" + "\xe0\xbd\x95\xe0\xbd\xb4\xe0\xbd\xa2\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbd\xb4\xe0\xbc\x8b\0" + "\xe0\xbd\x94\xe0\xbc\x8b\xe0\xbd\xa6\xe0\xbd\x84\xe0\xbd\xa6\xe0\xbc\x8b\0" + "\xe0\xbd\xa6\xe0\xbe\xa4\xe0\xbd\xba\xe0\xbd\x93\xe0\xbc\x8b\xe0\xbd\x94\xe0\xbc\x8b\0" + "\xe0\xbd\x89\xe0\xbd\xb2\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\0" + "\xe0\xbd\x98\xe0\xbd\xb2\xe0\xbd\x82\0" + "\xe0\xbd\xa3\xe0\xbe\xb7\xe0\xbd\x82\0" + "\xe0\xbd\x95\xe0\xbd\xb4\xe0\xbd\xa2\0" + "\xe0\xbd\xa6\xe0\xbd\x84\xe0\xbd\xa6\0" + "\xe0\xbd\xa6\xe0\xbe\xa4\xe0\xbd\xba\xe0\xbd\x93\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbc\x8b\xe0\xbd\x91\xe0\xbd\x84\xe0\xbc\x8b\xe0\xbd\x94\xe0\xbd\xbc\xe0\xbc\x8b\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbc\x8b\xe0\xbd\x82\xe0\xbd\x89\xe0\xbd\xb2\xe0\xbd\xa6\xe0\xbc\x8b\xe0\xbd\x94\xe0\xbc\x8b\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbc\x8b\xe0\xbd\x82\xe0\xbd\xa6\xe0\xbd\xb4\xe0\xbd\x98\xe0\xbc\x8b\xe0\xbd\x94\xe0\xbc\x8b\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbd\x9e\xe0\xbd\xb2\xe0\xbc\x8b\xe0\xbd\x94\xe0\xbc\x8b\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbc\x8b\xe0\xbd\xa3\xe0\xbe\x94\xe0\xbc\x8b\xe0\xbd\x94\xe0\xbc\x8b\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbc\x8b\xe0\xbd\x91\xe0\xbe\xb2\xe0\xbd\xb4\xe0\xbd\x82\xe0\xbc\x8b\xe0\xbd\x94\xe0\xbc\x8b\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbd\x91\xe0\xbd\xb4\xe0\xbd\x93\xe0\xbc\x8b\xe0\xbd\x94\xe0\xbc\x8b\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbd\xa2\xe0\xbe\x92\xe0\xbe\xb1\xe0\xbd\x91\xe0\xbc\x8b\xe0\xbd\x94\xe0\xbc\x8b\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbc\x8b\xe0\xbd\x91\xe0\xbd\x82\xe0\xbd\xb4\xe0\xbc\x8b\xe0\xbd\x94\xe0\xbc\x8b\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbd\x85\xe0\xbd\xb4\xe0\xbc\x8b\xe0\xbd\x94\xe0\xbc\x8b\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbd\x85\xe0\xbd\xb4\xe0\xbc\x8b\xe0\xbd\x82\xe0\xbd\x85\xe0\xbd\xb2\xe0\xbd\x82\xe0\xbc\x8b\xe0\xbd\x94\xe0\xbc\x8b\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbd\x85\xe0\xbd\xb4\xe0\xbc\x8b\xe0\xbd\x82\xe0\xbd\x89\xe0\xbd\xb2\xe0\xbd\xa6\xe0\xbc\x8b\xe0\xbd\x94\xe0\xbc\x8b\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbc\x8b\xe0\xbd\x91\xe0\xbd\x84\xe0\xbc\x8b\xe0\xbd\x94\xe0\xbd\xbc\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbc\x8b\xe0\xbd\x82\xe0\xbd\x89\xe0\xbd\xb2\xe0\xbd\xa6\xe0\xbc\x8b\xe0\xbd\x94\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbc\x8b\xe0\xbd\x82\xe0\xbd\xa6\xe0\xbd\xb4\xe0\xbd\x98\xe0\xbc\x8b\xe0\xbd\x94\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbd\x9e\xe0\xbd\xb2\xe0\xbc\x8b\xe0\xbd\x94\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbc\x8b\xe0\xbd\xa3\xe0\xbe\x94\xe0\xbc\x8b\xe0\xbd\x94\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbc\x8b\xe0\xbd\x91\xe0\xbe\xb2\xe0\xbd\xb4\xe0\xbd\x82\xe0\xbc\x8b\xe0\xbd\x94\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbd\x91\xe0\xbd\xb4\xe0\xbd\x93\xe0\xbc\x8b\xe0\xbd\x94\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbd\xa2\xe0\xbe\x92\xe0\xbe\xb1\xe0\xbd\x91\xe0\xbc\x8b\xe0\xbd\x94\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbc\x8b\xe0\xbd\x91\xe0\xbd\x82\xe0\xbd\xb4\xe0\xbc\x8b\xe0\xbd\x94\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbd\x85\xe0\xbd\xb4\xe0\xbc\x8b\xe0\xbd\x94\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbd\x85\xe0\xbd\xb4\xe0\xbc\x8b\xe0\xbd\x82\xe0\xbd\x85\xe0\xbd\xb2\xe0\xbd\x82\xe0\xbc\x8b\xe0\xbd\x94\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbc\x8b\xe0\xbd\x96\xe0\xbd\x85\xe0\xbd\xb4\xe0\xbc\x8b\xe0\xbd\x82\xe0\xbd\x89\xe0\xbd\xb2\xe0\xbd\xa6\xe0\xbc\x8b\xe0\xbd\x94\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbc\xa1\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbc\xa2\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbc\xa3\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbc\xa4\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbc\xa5\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbc\xa6\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbc\xa7\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbc\xa8\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbc\xa9\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbc\xa1\xe0\xbc\xa0\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbc\xa1\xe0\xbc\xa1\0" + "\xe0\xbd\x9f\xe0\xbe\xb3\xe0\xbc\x8b\xe0\xbc\xa1\xe0\xbc\xa2\0" + "Dydd Sul\0" + "Dydd Llun\0" + "Dydd Mawrth\0" + "Dydd Mercher\0" + "Dydd Iau\0" + "Dydd Gwener\0" + "Dydd Sadwrn\0" + "Sul\0" + "Llun\0" + "Mer\0" + "Iau\0" + "Gwe\0" + "Sad\0" + "Ll\0" + "Ionawr\0" + "Chwefror\0" + "Mawrth\0" + "Ebrill\0" + "Mehefin\0" + "Gorffennaf\0" + "Awst\0" + "Medi\0" + "Hydref\0" + "Tachwedd\0" + "Rhagfyr\0" + "Ion\0" + "Chw\0" + "Ebr\0" + "Meh\0" + "Gor\0" + "Hyd\0" + "Tach\0" + "Rhag\0" + "\xe1\x9e\xa2\xe1\x9e\xb6\xe1\x9e\x91\xe1\x9e\xb7\xe1\x9e\x8f\xe1\x9f\x92\xe1\x9e\x99\0" + "\xe1\x9e\x85\xe1\x9f\x90\xe1\x9e\x93\xe1\x9f\x92\xe1\x9e\x91\0" + "\xe1\x9e\xa2\xe1\x9e\x84\xe1\x9f\x92\xe1\x9e\x82\xe1\x9e\xb6\xe1\x9e\x9a\0" + "\xe1\x9e\x96\xe1\x9e\xbb\xe1\x9e\x92\0" + "\xe1\x9e\x96\xe1\x9f\x92\xe1\x9e\x9a\xe1\x9e\xa0\xe1\x9e\x9f\xe1\x9f\x92\xe1\x9e\x94\xe1\x9e\x8f\xe1\x9e\xb7\xe1\x9f\x8d\0" + "\xe1\x9e\x9f\xe1\x9e\xbb\xe1\x9e\x80\xe1\x9f\x92\xe1\x9e\x9a\0" + "\xe1\x9e\x9f\xe1\x9f\x85\xe1\x9e\x9a\xe1\x9f\x8d\0" + "\xe1\x9e\xa2\0" + "\xe1\x9e\x85\0" + "\xe1\x9e\x96\0" + "\xe1\x9e\x9f\0" + "\xe1\x9e\x98\xe1\x9e\x80\xe1\x9e\x9a\xe1\x9e\xb6\0" + "\xe1\x9e\x80\xe1\x9e\xbb\xe1\x9e\x98\xe1\x9f\x92\xe1\x9e\x97\xe1\x9f\x88\0" + "\xe1\x9e\x98\xe1\x9e\xb8\xe1\x9e\x93\xe1\x9e\xb6\0" + "\xe1\x9e\x98\xe1\x9f\x81\xe1\x9e\x9f\xe1\x9e\xb6\0" + "\xe1\x9e\xa7\xe1\x9e\x9f\xe1\x9e\x97\xe1\x9e\xb6\0" + "\xe1\x9e\x98\xe1\x9e\xb7\xe1\x9e\x90\xe1\x9e\xbb\xe1\x9e\x93\xe1\x9e\xb6\0" + "\xe1\x9e\x80\xe1\x9e\x80\xe1\x9f\x92\xe1\x9e\x80\xe1\x9e\x8a\xe1\x9e\xb6\0" + "\xe1\x9e\x9f\xe1\x9e\xb8\xe1\x9e\xa0\xe1\x9e\xb6\0" + "\xe1\x9e\x80\xe1\x9e\x89\xe1\x9f\x92\xe1\x9e\x89\xe1\x9e\xb6\0" + "\xe1\x9e\x8f\xe1\x9e\xbb\xe1\x9e\x9b\xe1\x9e\xb6\0" + "\xe1\x9e\x9c\xe1\x9e\xb7\xe1\x9e\x85\xe1\x9f\x92\xe1\x9e\x86\xe1\x9e\xb7\xe1\x9e\x80\xe1\x9e\xb6\0" + "\xe1\x9e\x92\xe1\x9f\x92\xe1\x9e\x93\xe1\x9e\xbc\0" + "\xe0\xba\xa7\xe0\xba\xb1\xe0\xba\x99\xe0\xba\xad\xe0\xba\xb2\xe0\xba\x97\xe0\xba\xb4\xe0\xba\x94\0" + "\xe0\xba\xa7\xe0\xba\xb1\xe0\xba\x99\xe0\xba\x88\xe0\xba\xb1\xe0\xba\x99\0" + "\xe0\xba\xa7\xe0\xba\xb1\xe0\xba\x99\xe0\xba\xad\xe0\xba\xb1\xe0\xba\x87\xe0\xba\x84\xe0\xba\xb2\xe0\xba\x99\0" + "\xe0\xba\xa7\xe0\xba\xb1\xe0\xba\x99\xe0\xba\x9e\xe0\xba\xb8\xe0\xba\x94\0" + "\xe0\xba\xa7\xe0\xba\xb1\xe0\xba\x99\xe0\xba\x9e\xe0\xba\xb0\xe0\xba\xab\xe0\xba\xb1\xe0\xba\x94\0" + "\xe0\xba\xa7\xe0\xba\xb1\xe0\xba\x99\xe0\xba\xaa\xe0\xba\xb8\xe0\xba\x81\0" + "\xe0\xba\xa7\xe0\xba\xb1\xe0\xba\x99\xe0\xbb\x80\xe0\xba\xaa\xe0\xba\xbb\xe0\xba\xb2\0" + "\xe0\xba\xad\xe0\xba\xb2\xe0\xba\x97\xe0\xba\xb4\xe0\xba\x94\0" + "\xe0\xba\x88\xe0\xba\xb1\xe0\xba\x99\0" + "\xe0\xba\xad\xe0\xba\xb1\xe0\xba\x87\xe0\xba\x84\xe0\xba\xb2\xe0\xba\x99\0" + "\xe0\xba\x9e\xe0\xba\xb8\xe0\xba\x94\0" + "\xe0\xba\x9e\xe0\xba\xb0\xe0\xba\xab\xe0\xba\xb1\xe0\xba\x94\0" + "\xe0\xba\xaa\xe0\xba\xb8\xe0\xba\x81\0" + "\xe0\xbb\x80\xe0\xba\xaa\xe0\xba\xbb\xe0\xba\xb2\0" + "\xe0\xba\xad\xe0\xba\xb2\0" + "\xe0\xba\x88\0" + "\xe0\xba\xad\0" + "\xe0\xba\x9e\0" + "\xe0\xba\x9e\xe0\xba\xab\0" + "\xe0\xba\xaa\xe0\xba\xb8\0" + "\xe0\xba\xaa\0" + "\xe0\xba\xa1\xe0\xba\xb1\xe0\xba\x87\xe0\xba\x81\xe0\xba\xad\xe0\xba\x99\0" + "\xe0\xba\x81\xe0\xba\xb8\xe0\xba\xa1\xe0\xba\x9e\xe0\xba\xb2\0" + "\xe0\xba\xa1\xe0\xba\xb5\xe0\xba\x99\xe0\xba\xb2\0" + "\xe0\xbb\x80\xe0\xba\xa1\xe0\xba\xaa\xe0\xba\xb2\0" + "\xe0\xba\x9e\xe0\xba\xb6\xe0\xba\x94\xe0\xba\xaa\xe0\xba\xb0\xe0\xba\x9e\xe0\xba\xb2\0" + "\xe0\xba\xa1\xe0\xba\xb4\xe0\xba\x96\xe0\xba\xb8\xe0\xba\x99\xe0\xba\xb2\0" + "\xe0\xba\x81\xe0\xbb\x8d\xe0\xba\xa5\xe0\xba\xb0\xe0\xba\x81\xe0\xba\xbb\xe0\xba\x94\0" + "\xe0\xba\xaa\xe0\xba\xb4\xe0\xba\x87\xe0\xba\xab\xe0\xba\xb2\0" + "\xe0\xba\x81\xe0\xba\xb1\xe0\xba\x99\xe0\xba\x8d\xe0\xba\xb2\0" + "\xe0\xba\x95\xe0\xba\xb8\xe0\xba\xa5\xe0\xba\xb2\0" + "\xe0\xba\x9e\xe0\xba\xb0\xe0\xba\x88\xe0\xba\xb4\xe0\xba\x81\0" + "\xe0\xba\x97\xe0\xba\xb1\xe0\xba\x99\xe0\xba\xa7\xe0\xba\xb2\0" + "\xe0\xba\xa1.\xe0\xba\x81.\0" + "\xe0\xba\x81.\xe0\xba\x9e.\0" + "\xe0\xba\xa1.\xe0\xba\x99.\0" + "\xe0\xba\xa1.\xe0\xba\xaa.\0" + "\xe0\xba\x9e.\xe0\xba\x9e.\0" + "\xe0\xba\xa1\xe0\xba\xb4.\xe0\xba\x96.\0" + "\xe0\xba\x81.\xe0\xba\xa5.\0" + "\xe0\xba\xaa.\xe0\xba\xab.\0" + "\xe0\xba\x81.\xe0\xba\x8d.\0" + "\xe0\xba\x95.\xe0\xba\xa5.\0" + "\xe0\xba\x9e.\xe0\xba\x88.\0" + "\xe0\xba\x97.\xe0\xba\xa7.\0" + "\xe1\x80\x90\xe1\x80\x94\xe1\x80\x84\xe1\x80\xba\xe1\x80\xb9\xe1\x80\x82\xe1\x80\x94\xe1\x80\xbd\xe1\x80\xb1\0" + "\xe1\x80\x90\xe1\x80\x94\xe1\x80\x84\xe1\x80\xba\xe1\x80\xb9\xe1\x80\x9c\xe1\x80\xac\0" + "\xe1\x80\xa1\xe1\x80\x84\xe1\x80\xba\xe1\x80\xb9\xe1\x80\x82\xe1\x80\xab\0" + "\xe1\x80\x97\xe1\x80\xaf\xe1\x80\x92\xe1\x80\xb9\xe1\x80\x93\xe1\x80\x9f\xe1\x80\xb0\xe1\x80\xb8\0" + "\xe1\x80\x80\xe1\x80\xbc\xe1\x80\xac\xe1\x80\x9e\xe1\x80\x95\xe1\x80\x90\xe1\x80\xb1\xe1\x80\xb8\0" + "\xe1\x80\x9e\xe1\x80\xb1\xe1\x80\xac\xe1\x80\x80\xe1\x80\xbc\xe1\x80\xac\0" + "\xe1\x80\x85\xe1\x80\x94\xe1\x80\xb1\0" + "\xe1\x80\x90\0" + "\xe1\x80\xa1\0" + "\xe1\x80\x97\0" + "\xe1\x80\x80\0" + "\xe1\x80\x9e\0" + "\xe1\x80\x85\0" + "\xe1\x80\x87\xe1\x80\x94\xe1\x80\xba\xe1\x80\x94\xe1\x80\x9d\xe1\x80\xab\xe1\x80\x9b\xe1\x80\xae\0" + "\xe1\x80\x96\xe1\x80\xb1\xe1\x80\x96\xe1\x80\xb1\xe1\x80\xac\xe1\x80\xba\xe1\x80\x9d\xe1\x80\xab\xe1\x80\x9b\xe1\x80\xae\0" + "\xe1\x80\x99\xe1\x80\x90\xe1\x80\xba\0" + "\xe1\x80\xa7\xe1\x80\x95\xe1\x80\xbc\xe1\x80\xae\0" + "\xe1\x80\x99\xe1\x80\xb1\0" + "\xe1\x80\x87\xe1\x80\xbd\xe1\x80\x94\xe1\x80\xba\0" + "\xe1\x80\x87\xe1\x80\xb0\xe1\x80\x9c\xe1\x80\xad\xe1\x80\xaf\xe1\x80\x84\xe1\x80\xba\0" + "\xe1\x80\xa9\xe1\x80\x82\xe1\x80\xaf\xe1\x80\x90\xe1\x80\xba\0" + "\xe1\x80\x85\xe1\x80\x80\xe1\x80\xba\xe1\x80\x90\xe1\x80\x84\xe1\x80\xba\xe1\x80\x98\xe1\x80\xac\0" + "\xe1\x80\xa1\xe1\x80\xb1\xe1\x80\xac\xe1\x80\x80\xe1\x80\xba\xe1\x80\x90\xe1\x80\xad\xe1\x80\xaf\xe1\x80\x98\xe1\x80\xac\0" + "\xe1\x80\x94\xe1\x80\xad\xe1\x80\xaf\xe1\x80\x9d\xe1\x80\x84\xe1\x80\xba\xe1\x80\x98\xe1\x80\xac\0" + "\xe1\x80\x92\xe1\x80\xae\xe1\x80\x87\xe1\x80\x84\xe1\x80\xba\xe1\x80\x98\xe1\x80\xac\0" + "\xe1\x80\x87\xe1\x80\x94\xe1\x80\xba\0" + "\xe1\x80\x96\xe1\x80\xb1\0" + "\xe1\x80\xa7\0" + "\xe1\x80\x87\xe1\x80\xb0\0" + "\xe1\x80\xa9\0" + "\xe1\x80\x85\xe1\x80\x80\xe1\x80\xba\0" + "\xe1\x80\xa1\xe1\x80\xb1\xe1\x80\xac\xe1\x80\x80\xe1\x80\xba\0" + "\xe1\x80\x94\xe1\x80\xad\xe1\x80\xaf\0" + "\xe1\x80\x92\xe1\x80\xae\0" + "luns\0" + "m\xc3\xa9rcores\0" + "xoves\0" + "venres\0" + "Dom.\0" + "Luns\0" + "Mar.\0" + "M\xc3\xa9r.\0" + "Xov.\0" + "Ven.\0" + "S\xc3\xa1\x62.\0" + "Xaneiro\0" + "Febreiro\0" + "Marzo\0" + "Abril\0" + "Maio\0" + "Xu\xc3\xb1o\0" + "Xullo\0" + "Agosto\0" + "Setembro\0" + "Outubro\0" + "Novembro\0" + "Decembro\0" + "xaneiro\0" + "febreiro\0" + "xu\xc3\xb1o\0" + "xullo\0" + "decembro\0" + "Xan.\0" + "Abr.\0" + "Xul.\0" + "Ago.\0" + "Set.\0" + "Out.\0" + "Dec.\0" + "\xe0\xa4\x86\xe0\xa4\xa6\xe0\xa4\xbf\xe0\xa4\xa4\xe0\xa5\x8d\xe0\xa4\xaf\xe0\xa4\xb5\xe0\xa4\xbe\xe0\xa4\xb0\0" + "\xe0\xa4\xae\xe0\xa4\x82\xe0\xa4\x97\xe0\xa4\xb3\xe0\xa4\xbe\xe0\xa4\xb0\0" + "\xe0\xa4\x93\xe0\xa4\x97\xe0\xa4\xb8\xe0\xa5\x8d\xe0\xa4\x9f\0" + "\xe0\xa4\xb8\xe0\xa5\x87\xe0\xa4\xaa\xe0\xa5\x8d\xe0\xa4\x9f\xe0\xa5\x87\xe0\xa4\x82\xe0\xa4\xac\xe0\xa4\xb0\0" + "\xe0\xa4\x93\xe0\xa4\x95\xe0\xa5\x8d\xe0\xa4\x9f\xe0\xa5\x8b\xe0\xa4\xac\xe0\xa4\xb0\0" + "\xe0\xb6\x89\xe0\xb6\xbb\xe0\xb7\x92\xe0\xb6\xaf\xe0\xb7\x8f\0" + "\xe0\xb7\x83\xe0\xb6\xb3\xe0\xb7\x94\xe0\xb6\xaf\xe0\xb7\x8f\0" + "\xe0\xb6\x85\xe0\xb6\x9f\xe0\xb7\x84\xe0\xb6\xbb\xe0\xb7\x94\xe0\xb7\x80\xe0\xb7\x8f\xe0\xb6\xaf\xe0\xb7\x8f\0" + "\xe0\xb6\xb6\xe0\xb6\xaf\xe0\xb7\x8f\xe0\xb6\xaf\xe0\xb7\x8f\0" + "\xe0\xb6\xb6\xe0\xb7\x8a\xe2\x80\x8d\xe0\xb6\xbb\xe0\xb7\x84\xe0\xb7\x83\xe0\xb7\x8a\xe0\xb6\xb4\xe0\xb6\xad\xe0\xb7\x92\xe0\xb6\xb1\xe0\xb7\x8a\xe0\xb6\xaf\xe0\xb7\x8f\0" + "\xe0\xb7\x83\xe0\xb7\x92\xe0\xb6\x9a\xe0\xb7\x94\xe0\xb6\xbb\xe0\xb7\x8f\xe0\xb6\xaf\xe0\xb7\x8f\0" + "\xe0\xb7\x83\xe0\xb7\x99\xe0\xb6\xb1\xe0\xb7\x83\xe0\xb7\x94\xe0\xb6\xbb\xe0\xb7\x8f\xe0\xb6\xaf\xe0\xb7\x8f\0" + "\xe0\xb6\x85\xe0\xb6\x9f\xe0\xb7\x84\0" + "\xe0\xb6\xb6\xe0\xb7\x8a\xe2\x80\x8d\xe0\xb6\xbb\xe0\xb7\x84\xe0\xb7\x83\xe0\xb7\x8a\0" + "\xe0\xb7\x83\xe0\xb7\x92\xe0\xb6\x9a\xe0\xb7\x94\0" + "\xe0\xb7\x83\xe0\xb7\x99\xe0\xb6\xb1\0" + "\xe0\xb6\x89\0" + "\xe0\xb7\x83\0" + "\xe0\xb6\x85\0" + "\xe0\xb6\xb6\0" + "\xe0\xb6\xb6\xe0\xb7\x8a\xe2\x80\x8d\xe0\xb6\xbb\0" + "\xe0\xb7\x83\xe0\xb7\x92\0" + "\xe0\xb7\x83\xe0\xb7\x99\0" + "\xe0\xb6\xa2\xe0\xb6\xb1\xe0\xb7\x80\xe0\xb7\x8f\xe0\xb6\xbb\xe0\xb7\x92\0" + "\xe0\xb6\xb4\xe0\xb7\x99\xe0\xb6\xb6\xe0\xb6\xbb\xe0\xb7\x80\xe0\xb7\x8f\xe0\xb6\xbb\xe0\xb7\x92\0" + "\xe0\xb6\xb8\xe0\xb7\x8f\xe0\xb6\xbb\xe0\xb7\x8a\xe0\xb6\xad\xe0\xb7\x94\0" + "\xe0\xb6\x85\xe0\xb6\xb4\xe0\xb7\x8a\xe2\x80\x8d\xe0\xb6\xbb\xe0\xb7\x9a\xe0\xb6\xbd\xe0\xb7\x8a\0" + "\xe0\xb6\xb8\xe0\xb7\x90\xe0\xb6\xba\xe0\xb7\x92\0" + "\xe0\xb6\xa2\xe0\xb7\x96\xe0\xb6\xb1\xe0\xb7\x92\0" + "\xe0\xb6\xa2\xe0\xb7\x96\xe0\xb6\xbd\xe0\xb7\x92\0" + "\xe0\xb6\x85\xe0\xb6\x9c\xe0\xb7\x9d\xe0\xb7\x83\xe0\xb7\x8a\xe0\xb6\xad\xe0\xb7\x94\0" + "\xe0\xb7\x83\xe0\xb7\x90\xe0\xb6\xb4\xe0\xb7\x8a\xe0\xb6\xad\xe0\xb7\x90\xe0\xb6\xb8\xe0\xb7\x8a\xe0\xb6\xb6\xe0\xb6\xbb\xe0\xb7\x8a\0" + "\xe0\xb6\x94\xe0\xb6\x9a\xe0\xb7\x8a\xe0\xb6\xad\xe0\xb7\x9d\xe0\xb6\xb6\xe0\xb6\xbb\xe0\xb7\x8a\0" + "\xe0\xb6\xb1\xe0\xb7\x9c\xe0\xb7\x80\xe0\xb7\x90\xe0\xb6\xb8\xe0\xb7\x8a\xe0\xb6\xb6\xe0\xb6\xbb\xe0\xb7\x8a\0" + "\xe0\xb6\xaf\xe0\xb7\x99\xe0\xb7\x83\xe0\xb7\x90\xe0\xb6\xb8\xe0\xb7\x8a\xe0\xb6\xb6\xe0\xb6\xbb\xe0\xb7\x8a\0" + "\xe0\xb6\xa2\xe0\xb6\xb1\0" + "\xe0\xb6\xb4\xe0\xb7\x99\xe0\xb6\xb6\0" + "\xe0\xb6\xb8\xe0\xb7\x8f\xe0\xb6\xbb\xe0\xb7\x8a\0" + "\xe0\xb6\x85\xe0\xb6\x9c\xe0\xb7\x9d\0" + "\xe0\xb7\x83\xe0\xb7\x90\xe0\xb6\xb4\xe0\xb7\x8a\0" + "\xe0\xb6\x94\xe0\xb6\x9a\xe0\xb7\x8a\0" + "\xe0\xb6\xb1\xe0\xb7\x9c\xe0\xb7\x80\xe0\xb7\x90\0" + "\xe0\xb6\xaf\xe0\xb7\x99\xe0\xb7\x83\xe0\xb7\x90\0" + "\xe1\x8e\xa4\xe1\x8e\xbe\xe1\x8f\x99\xe1\x8f\x93\xe1\x8f\x86\xe1\x8f\x8d\xe1\x8e\xac\0" + "\xe1\x8e\xa4\xe1\x8e\xbe\xe1\x8f\x99\xe1\x8f\x93\xe1\x8f\x89\xe1\x8f\x85\xe1\x8e\xaf\0" + "\xe1\x8f\x94\xe1\x8e\xb5\xe1\x8f\x81\xe1\x8e\xa2\xe1\x8e\xa6\0" + "\xe1\x8f\xa6\xe1\x8e\xa2\xe1\x8f\x81\xe1\x8e\xa2\xe1\x8e\xa6\0" + "\xe1\x8f\x85\xe1\x8e\xa9\xe1\x8f\x81\xe1\x8e\xa2\xe1\x8e\xa6\0" + "\xe1\x8f\xa7\xe1\x8e\xbe\xe1\x8e\xa9\xe1\x8e\xb6\xe1\x8f\x8d\xe1\x8f\x97\0" + "\xe1\x8e\xa4\xe1\x8e\xbe\xe1\x8f\x99\xe1\x8f\x93\xe1\x8f\x88\xe1\x8f\x95\xe1\x8e\xbe\0" + "\xe1\x8f\x86\xe1\x8f\x8d\xe1\x8e\xac\0" + "\xe1\x8f\x89\xe1\x8f\x85\xe1\x8e\xaf\0" + "\xe1\x8f\x94\xe1\x8e\xb5\xe1\x8f\x81\0" + "\xe1\x8f\xa6\xe1\x8e\xa2\xe1\x8f\x81\0" + "\xe1\x8f\x85\xe1\x8e\xa9\xe1\x8f\x81\0" + "\xe1\x8f\xa7\xe1\x8e\xbe\xe1\x8e\xa9\0" + "\xe1\x8f\x88\xe1\x8f\x95\xe1\x8e\xbe\0" + "\xe1\x8f\x86\0" + "\xe1\x8f\x89\0" + "\xe1\x8f\x94\0" + "\xe1\x8f\xa6\0" + "\xe1\x8f\x85\0" + "\xe1\x8f\xa7\0" + "\xe1\x8e\xa4\0" + "\xe1\x8e\xa4\xe1\x8f\x83\xe1\x8e\xb8\xe1\x8f\x94\xe1\x8f\x85\0" + "\xe1\x8e\xa7\xe1\x8e\xa6\xe1\x8e\xb5\0" + "\xe1\x8e\xa0\xe1\x8f\x85\xe1\x8f\xb1\0" + "\xe1\x8e\xa7\xe1\x8f\xac\xe1\x8f\x82\0" + "\xe1\x8e\xa0\xe1\x8f\x82\xe1\x8f\x8d\xe1\x8e\xac\xe1\x8f\x98\0" + "\xe1\x8f\x95\xe1\x8e\xad\xe1\x8e\xb7\xe1\x8f\xb1\0" + "\xe1\x8e\xab\xe1\x8f\xb0\xe1\x8f\x89\xe1\x8f\x82\0" + "\xe1\x8e\xa6\xe1\x8e\xb6\xe1\x8f\x82\0" + "\xe1\x8f\x9a\xe1\x8e\xb5\xe1\x8f\x8d\xe1\x8f\x97\0" + "\xe1\x8f\x9a\xe1\x8f\x82\xe1\x8f\x85\xe1\x8f\x97\0" + "\xe1\x8f\x85\xe1\x8f\x93\xe1\x8f\x95\xe1\x8f\x86\0" + "\xe1\x8e\xa5\xe1\x8f\x8d\xe1\x8e\xa9\xe1\x8f\xb1\0" + "\xe1\x8e\xa4\xe1\x8f\x83\0" + "\xe1\x8e\xa7\xe1\x8e\xa6\0" + "\xe1\x8e\xa0\xe1\x8f\x85\0" + "\xe1\x8e\xa7\xe1\x8f\xac\0" + "\xe1\x8e\xa0\xe1\x8f\x82\0" + "\xe1\x8f\x95\xe1\x8e\xad\0" + "\xe1\x8e\xab\xe1\x8f\xb0\0" + "\xe1\x8e\xa6\xe1\x8e\xb6\0" + "\xe1\x8f\x9a\xe1\x8e\xb5\0" + "\xe1\x8f\x9a\xe1\x8f\x82\0" + "\xe1\x8f\x85\xe1\x8f\x93\0" + "\xe1\x8e\xa5\xe1\x8f\x8d\0" + "\xe1\x8a\xa5\xe1\x88\x91\xe1\x8b\xb5\0" + "\xe1\x88\xb0\xe1\x8a\x9e\0" + "\xe1\x88\x9b\xe1\x8a\xad\xe1\x88\xb0\xe1\x8a\x9e\0" + "\xe1\x88\xa8\xe1\x89\xa1\xe1\x8b\x95\0" + "\xe1\x88\x90\xe1\x88\x99\xe1\x88\xb5\0" + "\xe1\x8b\x93\xe1\x88\xad\xe1\x89\xa5\0" + "\xe1\x89\x85\xe1\x8b\xb3\xe1\x88\x9c\0" + "\xe1\x88\x9b\xe1\x8a\xad\xe1\x88\xb0\0" + "\xe1\x8a\xa5\0" + "\xe1\x88\xb0\0" + "\xe1\x88\x9b\0" + "\xe1\x88\xa8\0" + "\xe1\x88\x90\0" + "\xe1\x8b\x93\0" + "\xe1\x89\x85\0" + "\xe1\x8c\x83\xe1\x8a\x95\xe1\x8b\xa9\xe1\x8b\x88\xe1\x88\xaa\0" + "\xe1\x8d\x8c\xe1\x89\xa5\xe1\x88\xa9\xe1\x8b\x88\xe1\x88\xaa\0" + "\xe1\x88\x9b\xe1\x88\xad\xe1\x89\xbd\0" + "\xe1\x8a\xa4\xe1\x8d\x95\xe1\x88\xaa\xe1\x88\x8d\0" + "\xe1\x88\x9c\xe1\x8b\xad\0" + "\xe1\x8c\x81\xe1\x8a\x95\0" + "\xe1\x8c\x81\xe1\x88\x8b\xe1\x8b\xad\0" + "\xe1\x8a\xa6\xe1\x8c\x88\xe1\x88\xb5\xe1\x89\xb5\0" + "\xe1\x88\xb4\xe1\x8d\x95\xe1\x89\xb4\xe1\x88\x9d\xe1\x89\xa0\xe1\x88\xad\0" + "\xe1\x8a\xa6\xe1\x8a\xad\xe1\x89\xb6\xe1\x89\xa0\xe1\x88\xad\0" + "\xe1\x8a\x96\xe1\x89\xac\xe1\x88\x9d\xe1\x89\xa0\xe1\x88\xad\0" + "\xe1\x8b\xb2\xe1\x88\xb4\xe1\x88\x9d\xe1\x89\xa0\xe1\x88\xad\0" + "\xe1\x8c\x83\xe1\x8a\x95\xe1\x8b\xa9\0" + "\xe1\x8d\x8c\xe1\x89\xa5\xe1\x88\xa9\0" + "\xe1\x8a\xa4\xe1\x8d\x95\xe1\x88\xaa\0" + "\xe1\x8a\xa6\xe1\x8c\x88\xe1\x88\xb5\0" + "\xe1\x88\xb4\xe1\x8d\x95\xe1\x89\xb4\0" + "\xe1\x8a\xa6\xe1\x8a\xad\xe1\x89\xb6\0" + "\xe1\x8a\x96\xe1\x89\xac\xe1\x88\x9d\0" + "\xe1\x8b\xb2\xe1\x88\xb4\xe1\x88\x9d\0" + "Asamas\0" + "Aynas\0" + "Asinas\0" + "Akras\0" + "Akwas\0" + "Asimwas\0" + "Asi\xe1\xb8\x8dyas\0" + "Asa\0" + "Ayn\0" + "Asn\0" + "Akr\0" + "Akw\0" + "Asm\0" + "As\xe1\xb8\x8d\0" + "Yennayer\0" + "Yebrayer\0" + "Ibrir\0" + "Mayyu\0" + "Yunyu\0" + "Yulyuz\0" + "\xc6\x94uct\0" + "Cutanbir\0" + "K\xe1\xb9\xaduber\0" + "Nwanbir\0" + "Dujanbir\0" + "Yen\0" + "Yeb\0" + "Ibr\0" + "Yun\0" + "Yul\0" + "\xc6\x94uc\0" + "Cut\0" + "K\xe1\xb9\xadu\0" + "Nwa\0" + "Duj\0" + "\xe0\xa4\x86\xe0\xa4\x87\xe0\xa4\xa4\xe0\xa4\xac\xe0\xa4\xbe\xe0\xa4\xb0\0" + "\xe0\xa4\xb8\xe0\xa5\x8b\xe0\xa4\xae\xe0\xa4\xac\xe0\xa4\xbe\xe0\xa4\xb0\0" + "\xe0\xa4\xae\xe0\xa4\x99\xe0\xa5\x8d\xe0\xa4\x97\xe0\xa4\xb2\xe0\xa4\xac\xe0\xa4\xbe\xe0\xa4\xb0\0" + "\xe0\xa4\xac\xe0\xa5\x81\xe0\xa4\xa7\xe0\xa4\xac\xe0\xa4\xbe\xe0\xa4\xb0\0" + "\xe0\xa4\xac\xe0\xa4\xbf\xe0\xa4\xb9\xe0\xa4\xbf\xe0\xa4\xac\xe0\xa4\xbe\xe0\xa4\xb0\0" + "\xe0\xa4\xb6\xe0\xa5\x81\xe0\xa4\x95\xe0\xa5\x8d\xe0\xa4\xb0\xe0\xa4\xac\xe0\xa4\xbe\xe0\xa4\xb0\0" + "\xe0\xa4\xb6\xe0\xa4\xa8\xe0\xa4\xbf\xe0\xa4\xac\xe0\xa4\xbe\xe0\xa4\xb0\0" + "\xe0\xa4\x86\xe0\xa4\x87\xe0\xa4\xa4\0" + "\xe0\xa4\xae\xe0\xa4\x99\xe0\xa5\x8d\xe0\xa4\x97\xe0\xa4\xb2\0" + "\xe0\xa4\xac\xe0\xa4\xbf\xe0\xa4\xb9\xe0\xa4\xbf\0" + "\xe0\xa4\x86\0" + "\xe0\xa4\xae\0" + "\xe0\xa4\xac\xe0\xa4\xbf\0" + "\xe0\xa4\xab\xe0\xa5\x87\xe0\xa4\xac\xe0\xa5\x8d\xe0\xa4\xb0\xe0\xa5\x81\xe0\xa4\x85\xe0\xa4\xb0\xe0\xa5\x80\0" + "\xe0\xa4\x85\xe0\xa4\xaa\xe0\xa5\x8d\xe0\xa4\xb0\xe0\xa4\xbf\xe0\xa4\xb2\0" + "\xe0\xa4\x9c\xe0\xa5\x81\xe0\xa4\xa8\0" + "\xe0\xa4\x85\xe0\xa4\x97\xe0\xa4\xb8\xe0\xa5\x8d\xe0\xa4\x9f\0" + "\xe0\xa4\xb8\xe0\xa5\x87\xe0\xa4\xaa\xe0\xa5\x8d\xe0\xa4\x9f\xe0\xa5\x87\xe0\xa4\xae\xe0\xa5\x8d\xe0\xa4\xac\xe0\xa4\xb0\0" + "\xe0\xa4\x85\xe0\xa4\x95\xe0\xa5\x8d\xe0\xa4\x9f\xe0\xa5\x8b\xe0\xa4\xac\xe0\xa4\xb0\0" + "\xe0\xa4\xa8\xe0\xa5\x8b\xe0\xa4\xad\xe0\xa5\x87\xe0\xa4\xae\xe0\xa5\x8d\xe0\xa4\xac\xe0\xa4\xb0\0" + "\xe0\xa4\xa1\xe0\xa4\xbf\xe0\xa4\xb8\xe0\xa5\x87\xe0\xa4\xae\xe0\xa5\x8d\xe0\xa4\xac\xe0\xa4\xb0\0" + "snein\0" + "moandei\0" + "tiisdei\0" + "woansdei\0" + "tongersdei\0" + "freed\0" + "sneon\0" + "si\0" + "mo\0" + "fr\0" + "Jannewaris\0" + "Febrewaris\0" + "Maaie\0" + "Juny\0" + "Septimber\0" + "Novimber\0" + "Desimber\0" + "Mrt\0" + "\xd9\x88\xd8\xb1\xdb\x8c\0" + "\xd8\xba\xd9\x88\xdb\x8c\xdb\x8c\0" + "\xd8\xba\xd8\xa8\xd8\xb1\xda\xaf\xd9\x88\xd9\x84\xdb\x8c\0" + "\xda\x86\xd9\x86\xda\xaf\xd8\xa7\xda\x9a\0" + "\xd8\xb2\xd9\x85\xd8\xb1\xdb\x8c\0" + "\xd9\x88\xda\x96\xdb\x8c\0" + "\xd8\xaa\xd9\x84\xd9\x87\0" + "\xd9\x84\xda\x93\xd9\x85\0" + "\xd9\x84\xdb\x8c\xd9\x86\xd8\xaf\xdb\x8d\0" + "\xd9\x85\xd8\xb1\xd8\xba\xd9\x88\xd9\x85\xdb\x8c\0" + "\xd8\xb3\xd9\x84\xd9\x88\xd8\xa7\xd8\xba\xd9\x87\0" + "\xda\xa9\xd8\xa8\0" + "Linggo\0" + "Lunes\0" + "Martes\0" + "Miyerkules\0" + "Huwebes\0" + "Biyernes\0" + "Sabado\0" + "Lin\0" + "Lun\0" + "Miy\0" + "Huw\0" + "Biy\0" + "Enero\0" + "Pebrero\0" + "Marso\0" + "Mayo\0" + "Hunyo\0" + "Hulyo\0" + "Setyembre\0" + "Oktubre\0" + "Nobyembre\0" + "Disyembre\0" + "Ene\0" + "Peb\0" + "Abr\0" + "Hun\0" + "Hul\0" + "Nob\0" + "dewo\0" + "aa\xc9\x93nde\0" + "mawbaare\0" + "njeslaare\0" + "naasaande\0" + "mawnde\0" + "hoore-biir\0" + "dew\0" + "aa\xc9\x93\0" + "maw\0" + "naa\0" + "mwd\0" + "hbi\0" + "d\0" + "a\0" + "m\0" + "h\0" + "siilo\0" + "colte\0" + "mbooy\0" + "see\xc9\x97to\0" + "duujal\0" + "korse\0" + "morso\0" + "juko\0" + "siilto\0" + "yarkomaa\0" + "jolal\0" + "bowte\0" + "sii\0" + "col\0" + "mbo\0" + "see\0" + "duu\0" + "kor\0" + "mor\0" + "juk\0" + "slt\0" + "yar\0" + "jol\0" + "bow\0" + "Lahadi\0" + "Litinin\0" + "Talata\0" + "Laraba\0" + "Alhamis\0" + "Jumma\xca\xbc\x61\0" + "Asabar\0" + "Lah\0" + "Lit\0" + "Tal\0" + "Lar\0" + "Alh\0" + "Janairu\0" + "Faburairu\0" + "Maris\0" + "Afirilu\0" + "Mayu\0" + "Yuni\0" + "Yuli\0" + "Agusta\0" + "Satumba\0" + "Nuwamba\0" + "Disamba\0" + "Fab\0" + "Afi\0" + "Agu\0" + "Nuw\0" + "\xe1\xbb\x8cj\xe1\xbb\x8d\xcc\x81 \xc3\x80\xc3\xack\xc3\xba\0" + "\xe1\xbb\x8cj\xe1\xbb\x8d\xcc\x81 Aj\xc3\xa9\0" + "\xe1\xbb\x8cj\xe1\xbb\x8d\xcc\x81 \xc3\x8cs\xe1\xba\xb9\xcc\x81gun\0" + "\xe1\xbb\x8cj\xe1\xbb\x8d\xcc\x81r\xc3\xba\0" + "\xe1\xbb\x8cj\xe1\xbb\x8d\xcc\x81\x62\xe1\xbb\x8d\0" + "\xe1\xbb\x8cj\xe1\xbb\x8d\xcc\x81 \xe1\xba\xb8t\xc3\xac\0" + "\xe1\xbb\x8cj\xe1\xbb\x8d\xcc\x81 \xc3\x80\x62\xc3\xa1m\xe1\xba\xb9\xcc\x81ta\0" + "\xc3\x80\xc3\xack\xc3\xba\0" + "Aj\xc3\xa9\0" + "\xc3\x8cs\xe1\xba\xb9\xcc\x81gun\0" + "\xe1\xba\xb8t\xc3\xac\0" + "\xc3\x80\x62\xc3\xa1m\xe1\xba\xb9\xcc\x81ta\0" + "O\xe1\xb9\xa3\xc3\xb9 \xe1\xb9\xa2\xe1\xba\xb9\xcc\x81r\xe1\xba\xb9\xcc\x81\0" + "O\xe1\xb9\xa3\xc3\xb9 \xc3\x88r\xc3\xa8l\xc3\xa8\0" + "O\xe1\xb9\xa3\xc3\xb9 \xe1\xba\xb8r\xe1\xba\xb9\xcc\x80n\xc3\xa0\0" + "O\xe1\xb9\xa3\xc3\xb9 \xc3\x8cgb\xc3\xa9\0" + "O\xe1\xb9\xa3\xc3\xb9 \xe1\xba\xb8\xcc\x80\x62ibi\0" + "O\xe1\xb9\xa3\xc3\xb9 \xc3\x92k\xc3\xba\x64u\0" + "O\xe1\xb9\xa3\xc3\xb9 Ag\xe1\xba\xb9m\xe1\xbb\x8d\0" + "O\xe1\xb9\xa3\xc3\xb9 \xc3\x92g\xc3\xban\0" + "O\xe1\xb9\xa3\xc3\xb9 Owewe\0" + "O\xe1\xb9\xa3\xc3\xb9 \xe1\xbb\x8c\xcc\x80w\xc3\xa0r\xc3\xa0\0" + "O\xe1\xb9\xa3\xc3\xb9 B\xc3\xa9l\xc3\xba\0" + "O\xe1\xb9\xa3\xc3\xb9 \xe1\xbb\x8c\xcc\x80p\xe1\xba\xb9\xcc\x80\0" + "\xe1\xb9\xa2\xe1\xba\xb9\xcc\x81r\xe1\xba\xb9\xcc\x81\0" + "\xc3\x88r\xc3\xa8l\xc3\xa8\0" + "\xe1\xba\xb8r\xe1\xba\xb9\xcc\x80n\xc3\xa0\0" + "\xc3\x8cgb\xc3\xa9\0" + "\xe1\xba\xb8\xcc\x80\x62ibi\0" + "\xc3\x92k\xc3\xba\x64u\0" + "Ag\xe1\xba\xb9m\xe1\xbb\x8d\0" + "\xc3\x92g\xc3\xban\0" + "Owewe\0" + "\xe1\xbb\x8c\xcc\x80w\xc3\xa0r\xc3\xa0\0" + "B\xc3\xa9l\xc3\xba\0" + "\xe1\xbb\x8c\xcc\x80p\xe1\xba\xb9\xcc\x80\0" + "Sontaga\0" + "Mosupalogo\0" + "Labohlano\0" + "Mokibelo\0" + "Mok\0" + "Janaware\0" + "Feberware\0" + "Mat\xc5\xa1he\0" + "Aporele\0" + "Julae\0" + "Agostose\0" + "Setemere\0" + "Oktobore\0" + "Nofemere\0" + "Disemere\0" + "Apo\0" + "Nof\0" + "Sonndeg\0" + "M\xc3\xa9indeg\0" + "D\xc3\xabnschdeg\0" + "M\xc3\xabttwoch\0" + "Donneschdeg\0" + "Freideg\0" + "Samschdeg\0" + "M\xc3\xa9i\0" + "D\xc3\xabn\0" + "M\xc3\xabt\0" + "Don\0" + "Fre\0" + "Sam\0" + "M\xc3\xa4\x65rz\0" + "Abr\xc3\xabll\0" + "Mee\0" + "M\xc3\xa4\x65\0" + "sabaat\0" + "ataasinngorneq\0" + "marlunngorneq\0" + "pingasunngorneq\0" + "sisamanngorneq\0" + "tallimanngorneq\0" + "arfininngorneq\0" + "ata\0" + "pin\0" + "sis\0" + "tal\0" + "arf\0" + "martsi\0" + "aprili\0" + "maji\0" + "augustusi\0" + "septemberi\0" + "oktoberi\0" + "novemberi\0" + "decemberi\0" + "Mb\xe1\xbb\x8ds\xe1\xbb\x8b \xe1\xbb\xa4ka\0" + "M\xe1\xbb\x8dnde\0" + "Tiuzdee\0" + "Wenezdee\0" + "T\xe1\xbb\x8d\xe1\xbb\x8dzdee\0" + "Fra\xe1\xbb\x8b\x64\x65\x65\0" + "Sat\xe1\xbb\x8d\x64\x65\x65\0" + "\xe1\xbb\xa4ka\0" + "M\xe1\xbb\x8dn\0" + "Tiu\0" + "Wen\0" + "T\xe1\xbb\x8d\xe1\xbb\x8d\0" + "Fra\xe1\xbb\x8b\0" + "Jen\xe1\xbb\xa5war\xe1\xbb\x8b\0" + "Febr\xe1\xbb\xa5war\xe1\xbb\x8b\0" + "Maach\xe1\xbb\x8b\0" + "Eprel\0" + "Juun\0" + "Jula\xe1\xbb\x8b\0" + "\xe1\xbb\x8cg\xe1\xbb\x8d\xe1\xbb\x8dst\0" + "\xe1\xbb\x8cktoba\0" + "Jen\0" + "Maa\0" + "Juu\0" + "\xe1\xbb\x8cg\xe1\xbb\x8d\0" + "\xe1\xbb\x8ckt\0" + "Dilbata\0" + "Wiixata\0" + "Qibxata\0" + "Roobii\0" + "Kamiisa\0" + "Jimaata\0" + "Sanbata\0" + "Dil\0" + "Wix\0" + "Qib\0" + "Rob\0" + "Jim\0" + "San\0" + "Amajjii\0" + "Guraandhala\0" + "Bitooteessa\0" + "Elba\0" + "Caamsa\0" + "Waxabajjii\0" + "Adooleessa\0" + "Hagayya\0" + "Fuulbana\0" + "Onkololeessa\0" + "Sadaasa\0" + "Muddee\0" + "Ama\0" + "Gur\0" + "Bit\0" + "Elb\0" + "Cam\0" + "Wax\0" + "Ado\0" + "Hag\0" + "Ful\0" + "Onk\0" + "\xe1\x88\xb0\xe1\x8a\x95\xe1\x89\xa0\xe1\x89\xb5\0" + "\xe1\x88\xb0\xe1\x8a\x91\xe1\x8b\xad\0" + "\xe1\x88\xa0\xe1\x88\x89\xe1\x88\xb5\0" + "\xe1\x8a\x83\xe1\x88\x99\xe1\x88\xb5\0" + "\xe1\x8b\x93\xe1\x88\xad\xe1\x89\xa2\0" + "\xe1\x89\x80\xe1\x8b\xb3\xe1\x88\x9d\0" + "\xe1\x88\xb0\xe1\x8a\x95\0" + "\xe1\x88\xb0\xe1\x8a\x91\0" + "\xe1\x88\xb0\xe1\x88\x89\0" + "\xe1\x88\xa8\xe1\x89\xa1\0" + "\xe1\x88\x93\xe1\x88\x99\0" + "\xe1\x8b\x93\xe1\x88\xad\0" + "\xe1\x89\x80\xe1\x8b\xb3\0" + "\xe1\x88\xa0\0" + "\xe1\x88\x93\0" + "\xe1\x89\x80\0" + "\xe1\x8c\xa5\xe1\x88\xaa\0" + "\xe1\x88\x88\xe1\x8a\xab\xe1\x89\xb2\xe1\x89\xb5\0" + "\xe1\x88\x98\xe1\x8c\x8b\xe1\x89\xa2\xe1\x89\xb5\0" + "\xe1\x88\x9a\xe1\x8b\xab\xe1\x8b\x9d\xe1\x8b\xab\0" + "\xe1\x8c\x8d\xe1\x8a\x95\xe1\x89\xa6\xe1\x89\xb5\0" + "\xe1\x88\xb0\xe1\x8a\x90\0" + "\xe1\x88\x93\xe1\x88\x9d\xe1\x88\x88\0" + "\xe1\x8a\x90\xe1\x88\x93\xe1\x88\xb0\0" + "\xe1\x88\x98\xe1\x88\xb5\xe1\x8a\xa8\xe1\x88\xa8\xe1\x88\x9d\0" + "\xe1\x8c\xa5\xe1\x89\x85\xe1\x88\x9d\xe1\x89\xb2\0" + "\xe1\x88\x95\xe1\x8b\xb3\xe1\x88\xad\0" + "\xe1\x89\xb3\xe1\x88\x95\xe1\x88\xb3\xe1\x88\xb5\0" + "\xe1\x88\x88\xe1\x8a\xab\0" + "\xe1\x88\x98\xe1\x8c\x8b\0" + "\xe1\x88\x9a\xe1\x8b\xab\0" + "\xe1\x8c\x8d\xe1\x8a\x95\0" + "\xe1\x88\x93\xe1\x88\x9d\0" + "\xe1\x8a\x90\xe1\x88\x93\0" + "\xe1\x88\x98\xe1\x88\xb5\0" + "\xe1\x8c\xa5\xe1\x89\x85\0" + "\xe1\x88\x95\xe1\x8b\xb3\0" + "\xe1\x89\xb3\xe1\x88\x95\0" + "L\xc4\x81pule\0" + "Po\xca\xbb\x61kahi\0" + "Po\xca\xbb\x61lua\0" + "Po\xca\xbb\x61kolu\0" + "Po\xca\xbb\x61h\xc4\x81\0" + "Po\xca\xbb\x61lima\0" + "Po\xca\xbb\x61ono\0" + "LP\0" + "P1\0" + "P2\0" + "P3\0" + "P4\0" + "P5\0" + "P6\0" + "Ianuali\0" + "Pepeluali\0" + "Malaki\0" + "\xca\xbb\x41pelila\0" + "Iune\0" + "Iulai\0" + "\xca\xbb\x41ukake\0" + "Kepakemapa\0" + "\xca\xbbOkakopa\0" + "Nowemapa\0" + "Kekemapa\0" + "Ian.\0" + "Pep.\0" + "Mal.\0" + "\xca\xbb\x41p.\0" + "Iun.\0" + "Iul.\0" + "\xca\xbb\x41u.\0" + "Kep.\0" + "\xca\xbbOk.\0" + "Now.\0" + "Kek.\0" + "Axad\0" + "Isniin\0" + "Talaado\0" + "Arbaco\0" + "Khamiis\0" + "Jimco\0" + "Sabti\0" + "Axd\0" + "Arb\0" + "Kh\0" + "Bisha Koobaad\0" + "Bisha Labaad\0" + "Bisha Saddexaad\0" + "Bisha Afraad\0" + "Bisha Shanaad\0" + "Bisha Lixaad\0" + "Bisha Todobaad\0" + "Bisha Sideedaad\0" + "Bisha Sagaalaad\0" + "Bisha Tobnaad\0" + "Bisha Kow iyo Tobnaad\0" + "Bisha Laba iyo Tobnaad\0" + "Kob\0" + "Lab\0" + "Afr\0" + "Sha\0" + "Lix\0" + "Tod\0" + "Sid\0" + "Sag\0" + "Tob\0" + "KIT\0" + "LIT\0" + "\xea\x91\xad\xea\x86\x8f\xea\x91\x8d\0" + "\xea\x86\x8f\xea\x8a\x82\xea\x8b\x8d\0" + "\xea\x86\x8f\xea\x8a\x82\xea\x91\x8d\0" + "\xea\x86\x8f\xea\x8a\x82\xea\x8c\x95\0" + "\xea\x86\x8f\xea\x8a\x82\xea\x87\x96\0" + "\xea\x86\x8f\xea\x8a\x82\xea\x89\xac\0" + "\xea\x86\x8f\xea\x8a\x82\xea\x83\x98\0" + "\xea\x91\xad\xea\x86\x8f\0" + "\xea\x86\x8f\xea\x8b\x8d\0" + "\xea\x86\x8f\xea\x91\x8d\0" + "\xea\x86\x8f\xea\x8c\x95\0" + "\xea\x86\x8f\xea\x87\x96\0" + "\xea\x86\x8f\xea\x89\xac\0" + "\xea\x86\x8f\xea\x83\x98\0" + "\xea\x86\x8f\0" + "\xea\x8b\x8d\0" + "\xea\x91\x8d\0" + "\xea\x8c\x95\0" + "\xea\x87\x96\0" + "\xea\x89\xac\0" + "\xea\x83\x98\0" + "\xea\x8b\x8d\xea\x86\xaa\0" + "\xea\x91\x8d\xea\x86\xaa\0" + "\xea\x8c\x95\xea\x86\xaa\0" + "\xea\x87\x96\xea\x86\xaa\0" + "\xea\x89\xac\xea\x86\xaa\0" + "\xea\x83\x98\xea\x86\xaa\0" + "\xea\x8f\x83\xea\x86\xaa\0" + "\xea\x89\x86\xea\x86\xaa\0" + "\xea\x88\xac\xea\x86\xaa\0" + "\xea\x8a\xb0\xea\x86\xaa\0" + "\xea\x8a\xb0\xea\x8a\xaa\xea\x86\xaa\0" + "\xea\x8a\xb0\xea\x91\x8b\xea\x86\xaa\0" + "Meurzh\0" + "Merc\xca\xbcher\0" + "Yaou\0" + "Gwener\0" + "Sadorn\0" + "Meu.\0" + "Mer.\0" + "Gwe.\0" + "Sad.\0" + "Su\0" + "Mz\0" + "Mc\0" + "Genver\0" + "C\xca\xbchwevrer\0" + "Ebrel\0" + "Mae\0" + "Mezheven\0" + "Gouere\0" + "Eost\0" + "Gwengolo\0" + "Here\0" + "Du\0" + "Kerzu\0" + "Gen.\0" + "C\xca\xbchwe.\0" + "Meur.\0" + "Ebr.\0" + "Mezh.\0" + "Goue.\0" + "Gwen.\0" + "Ker.\0" + "\xd9\x8a\xdb\x95\xd9\x83\xd8\xb4\xdb\x95\xd9\x86\xd8\xa8\xdb\x95\0" + "\xd8\xaf\xdb\x88\xd8\xb4\xdb\x95\xd9\x86\xd8\xa8\xdb\x95\0" + "\xd8\xb3\xdb\x95\xd9\x8a\xd8\xb4\xdb\x95\xd9\x86\xd8\xa8\xdb\x95\0" + "\xda\x86\xd8\xa7\xd8\xb1\xd8\xb4\xdb\x95\xd9\x86\xd8\xa8\xdb\x95\0" + "\xd9\xbe\xdb\x95\xd9\x8a\xd8\xb4\xdb\x95\xd9\x86\xd8\xa8\xdb\x95\0" + "\xd8\xac\xdb\x88\xd9\x85\xdb\x95\0" + "\xd8\xb4\xdb\x95\xd9\x86\xd8\xa8\xdb\x95\0" + "\xd9\x8a\xdb\x95\0" + "\xd8\xaf\xdb\x88\0" + "\xd8\xb3\xdb\x95\0" + "\xda\x86\xd8\xa7\0" + "\xd9\xbe\xdb\x95\0" + "\xd8\xac\xdb\x88\0" + "\xd8\xb4\xdb\x95\0" + "\xd9\x8a\0" + "\xd9\x8a\xd8\xa7\xd9\x86\xdb\x8b\xd8\xa7\xd8\xb1\0" + "\xd9\x81\xdb\x90\xdb\x8b\xd8\xb1\xd8\xa7\xd9\x84\0" + "\xd9\x85\xd8\xa7\xd8\xb1\xd8\xaa\0" + "\xd8\xa6\xd8\xa7\xd9\xbe\xd8\xb1\xdb\x90\xd9\x84\0" + "\xd9\x85\xd8\xa7\xd9\x8a\0" + "\xd8\xa6\xd9\x89\xd9\x8a\xdb\x87\xd9\x86\0" + "\xd8\xa6\xd9\x89\xd9\x8a\xdb\x87\xd9\x84\0" + "\xd8\xa6\xd8\xa7\xdb\x8b\xd8\xba\xdb\x87\xd8\xb3\xd8\xaa\0" + "\xd8\xb3\xdb\x90\xd9\x86\xd8\xaa\xdb\x95\xd8\xa8\xd9\x89\xd8\xb1\0" + "\xd8\xa6\xdb\x86\xd9\x83\xd8\xaa\xdb\x95\xd8\xa8\xd9\x89\xd8\xb1\0" + "\xd9\x86\xd9\x88\xd9\x8a\xd8\xa7\xd8\xa8\xd9\x89\xd8\xb1\0" + "\xd8\xaf\xdb\x90\xd9\x83\xd8\xa7\xd8\xa8\xd9\x89\xd8\xb1\0" + "Sunntig\0" + "M\xc3\xa4\xc3\xa4ntig\0" + "Ziischtig\0" + "Mittwuch\0" + "Dunschtig\0" + "Friitig\0" + "Samschtig\0" + "Su.\0" + "M\xc3\xa4.\0" + "Zi.\0" + "Mi.\0" + "Du.\0" + "Fr.\0" + "Auguscht\0" + "Sept\xc3\xa4mber\0" + "Oktoober\0" + "Nov\xc3\xa4mber\0" + "Dez\xc3\xa4mber\0" + "\xd0\xb1\xd0\xb0\xd1\x81\xd0\xba\xd1\x8b\xd2\xbb\xd1\x8b\xd0\xb0\xd0\xbd\xd0\xbd\xd1\x8c\xd0\xb0\0" + "\xd0\xb1\xd1\x8d\xd0\xbd\xd0\xb8\xd0\xb4\xd0\xb8\xd1\x8d\xd0\xbd\xd0\xbd\xd1\x8c\xd0\xb8\xd0\xba\0" + "\xd0\xbe\xd0\xbf\xd1\x82\xd1\x83\xd0\xbe\xd1\x80\xd1\x83\xd0\xbd\xd0\xbd\xd1\x8c\xd1\x83\xd0\xba\0" + "\xd1\x81\xd1\x8d\xd1\x80\xd1\x8d\xd0\xb4\xd1\x8d\0" + "\xd1\x87\xd1\x8d\xd0\xbf\xd0\xbf\xd0\xb8\xd1\x8d\xd1\x80\0" + "\xd0\x91\xd1\x8d\xd1\x8d\xd1\x82\xd0\xb8\xd2\xa5\xd1\x81\xd1\x8d\0" + "\xd1\x81\xd1\x83\xd0\xb1\xd1\x83\xd0\xbe\xd1\x82\xd0\xb0\0" + "\xd0\xb1\xd1\x81\0" + "\xd0\xb1\xd0\xbd\0" + "\xd0\xbe\xd0\xbf\0" + "\xd1\x81\xd1\x8d\0" + "\xd1\x87\xd0\xbf\0" + "\xd0\xb1\xd1\x8d\0" + "\xd0\x9e\0" + "\xd1\x82\xd0\xbe\xd1\x85\xd1\x81\xd1\x83\xd0\xbd\xd0\xbd\xd1\x8c\xd1\x83\0" + "\xd0\xbe\xd0\xbb\xd1\x83\xd0\xbd\xd0\xbd\xd1\x8c\xd1\x83\0" + "\xd0\xba\xd1\x83\xd0\xbb\xd1\x83\xd0\xbd \xd1\x82\xd1\x83\xd1\x82\xd0\xb0\xd1\x80\0" + "\xd0\xbc\xd1\x83\xd1\x83\xd1\x81 \xd1\x83\xd1\x81\xd1\x82\xd0\xb0\xd1\x80\0" + "\xd1\x8b\xd0\xb0\xd0\xbc \xd1\x8b\xd0\xb9\xd0\xb0\0" + "\xd0\xb1\xd1\x8d\xd1\x81 \xd1\x8b\xd0\xb9\xd0\xb0\0" + "\xd0\xbe\xd1\x82 \xd1\x8b\xd0\xb9\xd0\xb0\0" + "\xd0\xb0\xd1\x82\xd1\x8b\xd1\x80\xd0\xb4\xd1\x8c\xd1\x8b\xd1\x85 \xd1\x8b\xd0\xb9\xd0\xb0\0" + "\xd0\xb1\xd0\xb0\xd0\xbb\xd0\xb0\xd2\x95\xd0\xb0\xd0\xbd \xd1\x8b\xd0\xb9\xd0\xb0\0" + "\xd0\xb0\xd0\xbb\xd1\x82\xd1\x8b\xd0\xbd\xd0\xbd\xd1\x8c\xd1\x8b\0" + "\xd1\x81\xd1\x8d\xd1\x82\xd0\xb8\xd0\xbd\xd0\xbd\xd1\x8c\xd0\xb8\0" + "\xd0\xb0\xd1\x85\xd1\x81\xd1\x8b\xd0\xbd\xd0\xbd\xd1\x8c\xd1\x8b\0" + "\xd0\xa2\xd0\xbe\xd1\x85\xd1\x81\xd1\x83\xd0\xbd\xd0\xbd\xd1\x8c\xd1\x83\0" + "\xd0\x9e\xd0\xbb\xd1\x83\xd0\xbd\xd0\xbd\xd1\x8c\xd1\x83\0" + "\xd0\x9a\xd1\x83\xd0\xbb\xd1\x83\xd0\xbd \xd1\x82\xd1\x83\xd1\x82\xd0\xb0\xd1\x80\0" + "\xd0\x9c\xd1\x83\xd1\x83\xd1\x81 \xd1\x83\xd1\x81\xd1\x82\xd0\xb0\xd1\x80\0" + "\xd0\xab\xd0\xb0\xd0\xbc \xd1\x8b\xd0\xb9\xd1\x8b\xd0\xbd\0" + "\xd0\x91\xd1\x8d\xd1\x81 \xd1\x8b\xd0\xb9\xd1\x8b\xd0\xbd\0" + "\xd0\x9e\xd1\x82 \xd1\x8b\xd0\xb9\xd1\x8b\xd0\xbd\0" + "\xd0\x90\xd1\x82\xd1\x8b\xd1\x80\xd0\xb4\xd1\x8c\xd1\x8b\xd1\x85 \xd1\x8b\xd0\xb9\xd1\x8b\xd0\xbd\0" + "\xd0\x91\xd0\xb0\xd0\xbb\xd0\xb0\xd2\x95\xd0\xb0\xd0\xbd \xd1\x8b\xd0\xb9\xd1\x8b\xd0\xbd\0" + "\xd0\x90\xd0\xbb\xd1\x82\xd1\x8b\xd0\xbd\xd0\xbd\xd1\x8c\xd1\x8b\0" + "\xd0\xa1\xd1\x8d\xd1\x82\xd0\xb8\xd0\xbd\xd0\xbd\xd1\x8c\xd0\xb8\0" + "\xd0\xa2\xd0\xbe\xd1\x85\xd1\x81\0" + "\xd0\x9e\xd0\xbb\xd1\x83\xd0\xbd\0" + "\xd0\x9a\xd0\xbb\xd0\xbd\0" + "\xd0\x9c\xd1\x81\xd1\x83\0" + "\xd0\xab\xd0\xb0\xd0\xbc\0" + "\xd0\x91\xd1\x8d\xd1\x81\0" + "\xd0\x9e\xd1\x82\xd0\xb9\0" + "\xd0\x90\xd1\x82\xd1\x80\0" + "\xd0\x91\xd0\xbb\xd2\x95\0" + "\xd0\x90\xd0\xbb\xd1\x82\0" + "\xd0\xa1\xd1\x8d\xd1\x82\0" + "\xd0\x90\xd1\x85\xd1\x81\0" + "Ku cyumweru\0" + "Kuwa mbere\0" + "Kuwa kabiri\0" + "Kuwa gatatu\0" + "Kuwa kane\0" + "Kuwa gatanu\0" + "Kuwa gatandatu\0" + "cyu.\0" + "mbe.\0" + "kab.\0" + "gtu.\0" + "kan.\0" + "gnu.\0" + "gnd.\0" + "Mutarama\0" + "Gashyantare\0" + "Werurwe\0" + "Mata\0" + "Gicuransi\0" + "Kamena\0" + "Nyakanga\0" + "Kanama\0" + "Nzeli\0" + "Ukwakira\0" + "Ugushyingo\0" + "Ukuboza\0" + "mut.\0" + "gas.\0" + "wer.\0" + "mat.\0" + "gic.\0" + "kam.\0" + "nya.\0" + "nze.\0" + "ukw.\0" + "ugu.\0" + "uku.\0" + "DiD\xc3\xb2mhnaich\0" + "DiLuain\0" + "DiM\xc3\xa0irt\0" + "DiCiadain\0" + "DiarDaoin\0" + "DihAoine\0" + "DiSathairne\0" + "DiD\0" + "DiL\0" + "DiM\0" + "DiC\0" + "Dia\0" + "Dih\0" + "DiS\0" + "Am Faoilleach\0" + "An Gearran\0" + "Am M\xc3\xa0rt\0" + "An Giblean\0" + "An C\xc3\xa8itean\0" + "An t-\xc3\x92gmhios\0" + "An t-Iuchar\0" + "An L\xc3\xb9nastal\0" + "An t-Sultain\0" + "An D\xc3\xa0mhair\0" + "An t-Samhain\0" + "An D\xc3\xb9\x62hlachd\0" + "dhen Fhaoilleach\0" + "dhen Ghearran\0" + "dhen Mh\xc3\xa0rt\0" + "dhen Ghiblean\0" + "dhen Ch\xc3\xa8itean\0" + "dhen \xc3\x92gmhios\0" + "dhen Iuchar\0" + "dhen L\xc3\xb9nastal\0" + "dhen t-Sultain\0" + "dhen D\xc3\xa0mhair\0" + "dhen t-Samhain\0" + "dhen D\xc3\xb9\x62hlachd\0" + "Faoi\0" + "Gearr\0" + "M\xc3\xa0rt\0" + "Gibl\0" + "C\xc3\xa8it\0" + "\xc3\x92gmh\0" + "Iuch\0" + "L\xc3\xb9na\0" + "Sult\0" + "D\xc3\xa0mh\0" + "D\xc3\xb9\x62h\0" + "\xe9\x80\xb1\xe6\x97\xa5\0" + "\xe9\x80\xb1\xe4\xb8\x80\0" + "\xe9\x80\xb1\xe4\xba\x8c\0" + "\xe9\x80\xb1\xe4\xb8\x89\0" + "\xe9\x80\xb1\xe5\x9b\x9b\0" + "\xe9\x80\xb1\xe4\xba\x94\0" + "\xe9\x80\xb1\xe5\x85\xad\0" + "\xd8\xa7\xd9\x84\xd8\xa3\xd8\xad\xd8\xaf\0" + "\xd8\xa7\xd9\x84\xd8\xa7\xd8\xab\xd9\x86\xd9\x8a\xd9\x86\0" + "\xd8\xa7\xd9\x84\xd8\xab\xd9\x84\xd8\xa7\xd8\xab\xd8\xa7\xd8\xa1\0" + "\xd8\xa7\xd9\x84\xd8\xa3\xd8\xb1\xd8\xa8\xd8\xb9\xd8\xa7\xd8\xa1\0" + "\xd8\xa7\xd9\x84\xd8\xae\xd9\x85\xd9\x8a\xd8\xb3\0" + "\xd8\xa7\xd9\x84\xd8\xac\xd9\x85\xd8\xb9\xd8\xa9\0" + "\xd8\xa7\xd9\x84\xd8\xb3\xd8\xa8\xd8\xaa\0" + "\xd8\xad\0" + "\xd9\x86\0" + "\xd8\xab\0" + "\xd8\xb1\0" + "\xd8\xae\0" + "\xd9\x83\xd8\xa7\xd9\x86\xd9\x88\xd9\x86 \xd8\xa7\xd9\x84\xd8\xab\xd8\xa7\xd9\x86\xd9\x8a\0" + "\xd8\xb4\xd8\xa8\xd8\xa7\xd8\xb7\0" + "\xd8\xa2\xd8\xb0\xd8\xa7\xd8\xb1\0" + "\xd9\x86\xd9\x8a\xd8\xb3\xd8\xa7\xd9\x86\0" + "\xd8\xa3\xd9\x8a\xd8\xa7\xd8\xb1\0" + "\xd8\xad\xd8\xb2\xd9\x8a\xd8\xb1\xd8\xa7\xd9\x86\0" + "\xd8\xaa\xd9\x85\xd9\x88\xd8\xb2\0" + "\xd8\xa2\xd8\xa8\0" + "\xd8\xa3\xd9\x8a\xd9\x84\xd9\x88\xd9\x84\0" + "\xd8\xaa\xd8\xb4\xd8\xb1\xd9\x8a\xd9\x86 \xd8\xa7\xd9\x84\xd8\xa3\xd9\x88\xd9\x84\0" + "\xd8\xaa\xd8\xb4\xd8\xb1\xd9\x8a\xd9\x86 \xd8\xa7\xd9\x84\xd8\xab\xd8\xa7\xd9\x86\xd9\x8a\0" + "\xd9\x83\xd8\xa7\xd9\x86\xd9\x88\xd9\x86 \xd8\xa7\xd9\x84\xd8\xa3\xd9\x88\xd9\x84\0" + "ene\0" + "oct\0" + "tysdag\0" + "laurdag\0" + "tys\0" + "lau\0" + "segunda\0" + "ter\xc3\xa7\x61\0" + "quarta\0" + "quinta\0" + "sexta\0" + "Dum\0" + "Mie\0" + "Joi\0" + "Vin\0" + "S\xc3\xa2m\0" + "Ma\0" + "\xd9\xbe\xdb\x8c\xd8\xb1\0" + "\xd0\xb1\xd0\xb0\xd0\xb7\xd0\xb0\xd1\x80\0" + "\xd0\xb1\xd0\xb0\xd0\xb7\xd0\xb0\xd1\x80 \xd0\xb5\xd1\x80\xd1\x82\xd3\x99\xd1\x81\xd0\xb8\0" + "\xd1\x87\xd3\x99\xd1\x80\xd1\x88\xd3\x99\xd0\xbd\xd0\xb1\xd3\x99 \xd0\xb0\xd1\x85\xd1\x88\xd0\xb0\xd0\xbc\xd1\x8b\0" + "\xd1\x87\xd3\x99\xd1\x80\xd1\x88\xd3\x99\xd0\xbd\xd0\xb1\xd3\x99\0" + "\xd2\xb9\xd2\xaf\xd0\xbc\xd3\x99 \xd0\xb0\xd1\x85\xd1\x88\xd0\xb0\xd0\xbc\xd1\x8b\0" + "\xd2\xb9\xd2\xaf\xd0\xbc\xd3\x99\0" + "\xd1\x88\xd3\x99\xd0\xbd\xd0\xb1\xd3\x99\0" + "\xd0\x91.\0" + "\xd0\x91.\xd0\x95.\0" + "\xd0\xa7.\xd0\x90.\0" + "\xd0\xa7.\0" + "\xd2\xb8.\xd0\x90.\0" + "\xd2\xb8.\0" + "\xd0\xa8.\0" + "\xd0\x88\xd0\xb0\xd0\xbd\xd0\xb2\xd0\xb0\xd1\x80\0" + "\xd0\x98\xd1\x98\xd1\x83\xd0\xbd\0" + "\xd0\x98\xd1\x98\xd1\x83\xd0\xbb\0" + "\xd0\xa1\xd0\xb5\xd0\xbd\xd1\x82\xd1\x98\xd0\xb0\xd0\xb1\xd1\x80\0" + "\xd0\x9e\xd0\xba\xd1\x82\xd1\x98\xd0\xb0\xd0\xb1\xd1\x80\0" + "\xd0\x9d\xd0\xbe\xd1\x98\xd0\xb0\xd0\xb1\xd1\x80\0" + "\xd1\x98\xd0\xb0\xd0\xbd\xd0\xb2\xd0\xb0\xd1\x80\0" + "\xd1\x84\xd0\xb5\xd0\xb2\xd1\x80\xd0\xb0\xd0\xbb\0" + "\xd0\xb0\xd0\xbf\xd1\x80\xd0\xb5\xd0\xbb\0" + "\xd0\xb8\xd1\x98\xd1\x83\xd0\xbd\0" + "\xd0\xb8\xd1\x98\xd1\x83\xd0\xbb\0" + "\xd1\x81\xd0\xb5\xd0\xbd\xd1\x82\xd1\x98\xd0\xb0\xd0\xb1\xd1\x80\0" + "\xd0\xbe\xd0\xba\xd1\x82\xd1\x98\xd0\xb0\xd0\xb1\xd1\x80\0" + "\xd0\xbd\xd0\xbe\xd1\x98\xd0\xb0\xd0\xb1\xd1\x80\0" + "\xd0\xb4\xd0\xb5\xd0\xba\xd0\xb0\xd0\xb1\xd1\x80\0" + "\xd1\x98\xd0\xb0\xd0\xbd\0" + "\xd0\xbc\xd0\xb0\xd1\x80\0" + "\xd0\xb8\xd1\x98\xd0\xbd\0" + "\xd0\xb8\xd1\x98\xd0\xbb\0" + "\xd1\x81\xd0\xb5\xd0\xbd\0" + "\xd0\xbd\xd0\xbe\xd1\x98\0" + "nje\xc5\xba\x65la\0" + "p\xc3\xb3nje\xc5\xba\x65le\0" + "wa\xc5\x82tora\0" + "srjoda\0" + "stw\xc3\xb3rtk\0" + "p\xc4\x9btk\0" + "wa\xc5\x82\0" + "stw\0" + "p\xc4\x9bt\0" + "\xd1\x8f\xd0\xba\xd1\x88\xd0\xb0\xd0\xbd\xd0\xb1\xd0\xb0\0" + "\xd0\xb4\xd1\x83\xd1\x88\xd0\xb0\xd0\xbd\xd0\xb1\xd0\xb0\0" + "\xd1\x81\xd0\xb5\xd1\x88\xd0\xb0\xd0\xbd\xd0\xb1\xd0\xb0\0" + "\xd1\x87\xd0\xbe\xd1\x80\xd1\x88\xd0\xb0\xd0\xbd\xd0\xb1\xd0\xb0\0" + "\xd0\xbf\xd0\xb0\xd0\xb9\xd1\x88\xd0\xb0\xd0\xbd\xd0\xb1\xd0\xb0\0" + "\xd1\x88\xd0\xb0\xd0\xbd\xd0\xb1\xd0\xb0\0" + "\xd0\xaf\xd0\xba\xd1\x88\0" + "\xd0\x94\xd1\x83\xd1\x88\0" + "\xd0\xa1\xd0\xb5\xd1\x88\0" + "\xd0\xa7\xd0\xbe\xd1\x80\0" + "\xd0\x9f\xd0\xb0\xd0\xb9\0" + "\xd0\x96\xd1\x83\xd0\xbc\0" + "\xd0\xa8\xd0\xb0\xd0\xbd\0" + "\xd0\xaf\0" + "\xd1\x8f\xd0\xbd\xd0\xb2\xd0\xb0\xd1\x80\0" + "\xd0\xb8\xd1\x8e\xd0\xbd\0" + "\xd0\xb8\xd1\x8e\xd0\xbb\0" + "\xd1\x81\xd0\xb5\xd0\xbd\xd1\x82\xd1\x8f\xd0\xb1\xd1\x80\0" + "\xd0\xbe\xd0\xba\xd1\x82\xd1\x8f\xd0\xb1\xd1\x80\0" + "\xd0\xbd\xd0\xbe\xd1\x8f\xd0\xb1\xd1\x80\0" + "\xd8\xa8\xd9\x8f\xd8\xaf\xda\xbe\0" + "\xd9\x85\xd8\xa6\0" + "\xd9\x8a\xd9\x86\xd8\xa7\xd9\x8a\xd8\xb1\0" + "\xd9\x81\xd8\xa8\xd8\xb1\xd8\xa7\xd9\x8a\xd8\xb1\0" + "\xd8\xa3\xd8\xa8\xd8\xb1\xd9\x8a\xd9\x84\0" + "\xd9\x85\xd8\xa7\xd9\x8a\xd9\x88\0" + "\xd9\x8a\xd9\x88\xd9\x86\xd9\x8a\xd9\x88\0" + "\xd9\x8a\xd9\x88\xd9\x84\xd9\x8a\xd9\x88\0" + "\xd8\xa3\xd8\xba\xd8\xb3\xd8\xb7\xd8\xb3\0" + "\xd8\xb3\xd8\xa8\xd8\xaa\xd9\x85\xd8\xa8\xd8\xb1\0" + "\xd8\xa3\xd9\x83\xd8\xaa\xd9\x88\xd8\xa8\xd8\xb1\0" + "\xd9\x86\xd9\x88\xd9\x81\xd9\x85\xd8\xa8\xd8\xb1\0" + "\xd8\xaf\xd9\x8a\xd8\xb3\xd9\x85\xd8\xa8\xd8\xb1\0" + "J\xc3\xa4nner\0" + "J\xc3\xa4n\0" + "Sun.\0" + "Mon.\0" + "Tue.\0" + "Wed.\0" + "Thu.\0" + "Fri.\0" + "Sat.\0" + "M.\0" + "Tu.\0" + "W.\0" + "Th.\0" + "F.\0" + "Oct.\0" + "juill.\0" + "vuoss\xc3\xa1rgga\0" + "ma\xc5\x8b\xc5\x8b\x65\x62\xc3\xa1rgga\0" + "gaskavahku\0" + "duorastaga\0" + "bearjadaga\0" + "l\xc3\xa1vvardaga\0" + "U\0" + "\xd8\xac\xd8\xa7\xd9\x86\xd9\x81\xd9\x8a\0" + "\xd9\x81\xd9\x8a\xd9\x81\xd8\xb1\xd9\x8a\0" + "\xd8\xa3\xd9\x81\xd8\xb1\xd9\x8a\xd9\x84\0" + "\xd8\xac\xd9\x88\xd8\xa7\xd9\x86\0" + "\xd8\xac\xd9\x88\xd9\x8a\xd9\x84\xd9\x8a\xd8\xa9\0" + "\xd8\xa3\xd9\x88\xd8\xaa\0" + "septembar\0" + "oktobar\0" + "novembar\0" + "decembar\0" + "avg\0" + "\xd9\x8a\xd9\x88\xd9\x84\xd9\x8a\xd9\x88\xd8\xb2\0" + "\xd8\xba\xd8\xb4\xd8\xaa\0" + "\xd8\xb4\xd8\xaa\xd9\x86\xd8\xa8\xd8\xb1\0" + "\xd9\x86\xd9\x88\xd9\x86\xd8\xa8\xd8\xb1\0" + "\xd8\xaf\xd8\xac\xd9\x86\xd8\xa8\xd8\xb1\0" + "ponedeljak\0" + "ut.\0" + "sr.\0" + "sub.\0" + "\xd0\xbd\xd0\xb5\xd0\xb4\xd1\x98\xd0\xb5\xd1\x99\xd0\xb0\0" + "\xd0\xbf\xd0\xbe\xd0\xbd\xd0\xb5\xd0\xb4\xd0\xb5\xd1\x99\xd0\xb0\xd0\xba\0" + "\xd1\x83\xd1\x82\xd0\xbe\xd1\x80\xd0\xb0\xd0\xba\0" + "\xd1\x81\xd1\x80\xd0\xb8\xd1\x98\xd0\xb5\xd0\xb4\xd0\xb0\0" + "\xd1\x87\xd0\xb5\xd1\x82\xd0\xb2\xd1\x80\xd1\x82\xd0\xb0\xd0\xba\0" + "\xd0\xbf\xd0\xb5\xd1\x82\xd0\xb0\xd0\xba\0" + "\xd1\x83\xd1\x82.\0" + "\xd1\x81\xd1\x80.\0" + "\xd1\x81\xd1\x83\xd0\xb1.\0" + "\xd1\x83\0" + "\xd1\x98\xd0\xb0\xd0\xbd\xd1\x83\xd0\xb0\xd1\x80\0" + "\xd1\x84\xd0\xb5\xd0\xb1\xd1\x80\xd1\x83\xd0\xb0\xd1\x80\0" + "\xd1\x98\xd1\x83\xd0\xbd\0" + "\xd1\x98\xd1\x83\xd0\xbb\0" + "\xd1\x81\xd0\xb5\xd0\xbf\xd1\x82\xd0\xb5\xd0\xbc\xd0\xb1\xd0\xb0\xd1\x80\0" + "\xd0\xbe\xd0\xba\xd1\x82\xd0\xbe\xd0\xb1\xd0\xb0\xd1\x80\0" + "\xd0\xbd\xd0\xbe\xd0\xb2\xd0\xb5\xd0\xbc\xd0\xb1\xd0\xb0\xd1\x80\0" + "\xd0\xb4\xd0\xb5\xd1\x86\xd0\xb5\xd0\xbc\xd0\xb1\xd0\xb0\xd1\x80\0" + "\xd1\x84\xd0\xb5\xd0\xb1.\0" + "\xd0\xbd\xd0\xbe\xd0\xb2.\0" + "\xd0\xb4\xd0\xb5\xd1\x86.\0" + "\xd0\xbd\xd0\xb5\xd0\xb4\xd0\xb5\xd1\x99\xd0\xb0\0" + "\xd0\xbd\xd0\xb5\xd0\xb4\0" + "\xd0\xbf\xd0\xbe\xd0\xbd\0" + "\xd1\x83\xd1\x82\xd0\xbe\0" + "\xd1\x81\xd1\x80\xd0\xb8\0" + "\xd1\x87\xd0\xb5\xd1\x82\0" + "\xd0\xbf\xd0\xb5\xd1\x82\0" + "\xd1\x81\xd1\x83\xd0\xb1\0" + "\xd1\x84\xd0\xb5\xd0\xb1\0" + "\xd0\xbd\xd0\xbe\xd0\xb2\0" + "\xd0\xb4\xd0\xb5\xd1\x86\0" + "l\0" + "j\0" + "v\0" + "sre\0" + "pasepeeivi\0" + "vuossaarg\xc3\xa2\0" + "majebaarg\xc3\xa2\0" + "koskoho\0" + "tuor\xc3\xa2stuv\0" + "v\xc3\xa1stuppeeivi\0" + "l\xc3\xa1vurduv\0" + "pas\0" + "vuo\0" + "kos\0" + "tuo\0" + "v\xc3\xa1s\0" + "u\xc4\x91\xc4\x91\xc3\xa2ivem\xc3\xa1\xc3\xa1nu\0" + "kuov\xc3\xa2m\xc3\xa1\xc3\xa1nu\0" + "njuh\xc4\x8d\xc3\xa2m\xc3\xa1\xc3\xa1nu\0" + "cu\xc3\xa1\xc5\x8buim\xc3\xa1\xc3\xa1nu\0" + "vyesim\xc3\xa1\xc3\xa1nu\0" + "kesim\xc3\xa1\xc3\xa1nu\0" + "syeinim\xc3\xa1\xc3\xa1nu\0" + "porgem\xc3\xa1\xc3\xa1nu\0" + "\xc4\x8doh\xc4\x8d\xc3\xa2m\xc3\xa1\xc3\xa1nu\0" + "roovv\xc3\xa2\x64m\xc3\xa1\xc3\xa1nu\0" + "skamm\xc3\xa2m\xc3\xa1\xc3\xa1nu\0" + "juovl\xc3\xa2m\xc3\xa1\xc3\xa1nu\0" + "u\xc4\x91iv\0" + "kuov\xc3\xa2\0" + "njuh\xc4\x8d\xc3\xa2\0" + "cu\xc3\xa1\xc5\x8bui\0" + "vyesi\0" + "kesi\0" + "syeini\0" + "porge\0" + "\xc4\x8doh\xc4\x8d\xc3\xa2\0" + "roovv\xc3\xa2\x64\0" + "skamm\xc3\xa2\0" + "juovl\xc3\xa2\0" + "Febrero\0" + "Junio\0" + "Julio\0" + "Setiembre\0" + "Octubre\0" + "Noviembre\0" + "Diciembre\0" + "setiembre\0" + "Ene.\0" + "May.\0" + "Dic.\0" + "\xd1\x81\xd1\x80\xd0\xb5\0" + "f\xc3\xa9v.\0" + "jui.\0" + "\xd8\xa7\xd9\x84\xd8\xaa\xd9\x82\xd9\x88\xd9\x8a\xd9\x85 \xd8\xa7\xd9\x84\xd9\x87\xd8\xac\xd8\xb1\xd9\x8a\0" + "\xd0\xb3\xd1\x80\xd0\xb8\xd0\xb3\xd0\xbe\xd1\x80\xd0\xb8\xd0\xb0\xd0\xbd\xd1\x81\xd0\xba\xd0\xb8 \xd0\xba\xd0\xb0\xd0\xbb\xd0\xb5\xd0\xbd\xd0\xb4\xd0\xb0\xd1\x80\0" + "calendari gregori\xc3\xa0\0" + "\xe5\x85\xac\xe5\x8e\x86\0" + "Gregori\xc3\xa1nsk\xc3\xbd kalend\xc3\xa1\xc5\x99\0" + "gregoriansk kalender\0" + "Gregorianischer Kalender\0" + "\xce\x93\xcf\x81\xce\xb7\xce\xb3\xce\xbf\xcf\x81\xce\xb9\xce\xb1\xce\xbd\xcf\x8c \xce\xb7\xce\xbc\xce\xb5\xcf\x81\xce\xbf\xce\xbb\xcf\x8c\xce\xb3\xce\xb9\xce\xbf\0" + "Gregorian Calendar\0" + "calendario gregoriano\0" + "gregoriaaninen kalenteri\0" + "calendrier gr\xc3\xa9gorien\0" + "\xd7\x9c\xd7\x95\xd7\x97 \xd7\x94\xd7\xa9\xd7\xa0\xd7\x94 \xd7\x94\xd7\x92\xd7\xa8\xd7\x92\xd7\x95\xd7\xa8\xd7\x99\xd7\x90\xd7\xa0\xd7\x99\0" + "Gergely-napt\xc3\xa1r\0" + "Gregor\xc3\xadskt dagatal\0" + "Calendario gregoriano\0" + "\xe8\xa5\xbf\xe6\x9a\xa6(\xe3\x82\xb0\xe3\x83\xac\xe3\x82\xb4\xe3\x83\xaa\xe3\x82\xaa\xe6\x9a\xa6)\0" + "\xec\x96\x91\xeb\xa0\xa5\0" + "Gregoriaanse kalender\0" + "kalendarz gregoria\xc5\x84ski\0" + "Calend\xc3\xa1rio Gregoriano\0" + "chalender gregorian\0" + "calendar gregorian\0" + "\xd0\xb3\xd1\x80\xd0\xb8\xd0\xb3\xd0\xbe\xd1\x80\xd0\xb8\xd0\xb0\xd0\xbd\xd1\x81\xd0\xba\xd0\xb8\xd0\xb9 \xd0\xba\xd0\xb0\xd0\xbb\xd0\xb5\xd0\xbd\xd0\xb4\xd0\xb0\xd1\x80\xd1\x8c\0" + "gregorijanski kalendar\0" + "gregori\xc3\xa1nsky kalend\xc3\xa1r\0" + "kalendar gregorian\0" + "\xe0\xb8\x9b\xe0\xb8\x8f\xe0\xb8\xb4\xe0\xb8\x97\xe0\xb8\xb4\xe0\xb8\x99\xe0\xb8\x9e\xe0\xb8\xb8\xe0\xb8\x97\xe0\xb8\x98\0" + "Miladi Takvim\0" + "\xd8\xac\xd8\xa7\xd8\xb1\xd8\xac\xdb\x8c\xd8\xa7\xd8\xa6\xdb\x8c \xda\xa9\xdb\x8c\xd9\x84\xd9\x86\xda\x88\xd8\xb1\0" + "Kalender Gregorian\0" + "\xd0\xb3\xd1\x80\xd0\xb8\xd0\xb3\xd0\xbe\xd1\x80\xd1\x96\xd0\xb0\xd0\xbd\xd1\x81\xd1\x8c\xd0\xba\xd0\xb8\xd0\xb9 \xd0\xba\xd0\xb0\xd0\xbb\xd0\xb5\xd0\xbd\xd0\xb4\xd0\xb0\xd1\x80\0" + "\xd0\xb3\xd1\x80\xd1\x8b\xd0\xb3\xd0\xb0\xd1\x80\xd1\x8b\xd1\x8f\xd0\xbd\xd1\x81\xd0\xba\xd1\x96 \xd0\xba\xd0\xb0\xd0\xbb\xd1\x8f\xd0\xbd\xd0\xb4\xd0\xb0\xd1\x80\0" + "gregorijanski koledar\0" + "Gregoriuse kalender\0" + "Gregora kalend\xc4\x81rs\0" + "Grigaliaus kalendorius\0" + "\xd8\xaa\xd9\x82\xd9\x88\xdb\x8c\xd9\x85 \xd9\x85\xdb\x8c\xd9\x84\xd8\xa7\xd8\xaf\xdb\x8c\0" + "L\xe1\xbb\x8b\x63h Gregory\0" + "\xd5\xa3\xd6\x80\xd5\xab\xd5\xa3\xd5\xb8\xd6\x80\xd5\xb5\xd5\xa1\xd5\xb6 \xd5\xbf\xd5\xb8\xd5\xb4\xd5\xa1\xd6\x80\0" + "Qreqorian T\xc9\x99qvimi\0" + "Egutegi gregoriarra\0" + "gregorianska protyka\0" + "\xd0\x93\xd1\x80\xd0\xb5\xd0\xb3\xd0\xbe\xd1\x80\xd0\xb8\xd1\x98\xd0\xb0\xd0\xbd\xd1\x81\xd0\xba\xd0\xb8 \xd0\xba\xd0\xb0\xd0\xbb\xd0\xb5\xd0\xbd\xd0\xb4\xd0\xb0\xd1\x80\0" + "ikhalenda lesi-Gregorian\0" + "Gregoriese kalender\0" + "\xe1\x83\x92\xe1\x83\xa0\xe1\x83\x98\xe1\x83\x92\xe1\x83\x9d\xe1\x83\xa0\xe1\x83\x98\xe1\x83\x90\xe1\x83\x9c\xe1\x83\xa3\xe1\x83\x9a\xe1\x83\x98 \xe1\x83\x99\xe1\x83\x90\xe1\x83\x9a\xe1\x83\x94\xe1\x83\x9c\xe1\x83\x93\xe1\x83\x90\xe1\x83\xa0\xe1\x83\x98\0" + "gregorianskur kalendari\0" + "\xe0\xa4\x97\xe0\xa5\x8d\xe0\xa4\xb0\xe0\xa5\x87\xe0\xa4\x97\xe0\xa5\x8b\xe0\xa4\xb0\xe0\xa4\xbf\xe0\xa4\xaf\xe0\xa4\xa8 \xe0\xa4\x95\xe0\xa5\x88\xe0\xa4\xb2\xe0\xa5\x87\xe0\xa4\x82\xe0\xa4\xa1\xe0\xa4\xb0\0" + "Kalendarju Gregorjan\0" + "gregoria kaleander\0" + "F\xc3\xa9ilire Ghr\xc3\xa9\x61g\xc3\xb3ra\0" + "Kalendar Gregory\0" + "\xd0\x93\xd1\x80\xd0\xb5\xd0\xb3\xd0\xbe\xd1\x80\xd0\xb8\xd0\xb0\xd0\xbd\xd0\xb4\xd1\x8b\xd2\x9b \xd0\xba\xd2\xaf\xd0\xbd\xd1\x82\xd1\x96\xd0\xb7\xd0\xb1\xd0\xb5\0" + "\xd0\x93\xd1\x80\xd0\xb8\xd0\xb3\xd0\xbe\xd1\x80\xd0\xb8\xd0\xb0\xd0\xbd \xd0\xb6\xd1\x8b\xd0\xbb\xd0\xbd\xd0\xb0\xd0\xb0\xd0\xbc\xd0\xb0\xd1\x81\xd1\x8b\0" + "Kalenda ya Kigregori\0" + "Gregor\xc3\xbd\x61n senenamasy\0" + "grigorian taqvimi\0" + "\xe0\xa6\x97\xe0\xa7\x8d\xe0\xa6\xb0\xe0\xa6\xbf\xe0\xa6\x97\xe0\xa7\x8b\xe0\xa6\xb0\xe0\xa6\xbf\xe0\xa6\xaf\xe0\xa6\xbc\xe0\xa6\xbe\xe0\xa6\xa8 \xe0\xa6\x95\xe0\xa7\x8d\xe0\xa6\xaf\xe0\xa6\xbe\xe0\xa6\xb2\xe0\xa7\x87\xe0\xa6\xa8\xe0\xa7\x8d\xe0\xa6\xa1\xe0\xa6\xbe\xe0\xa6\xb0\0" + "\xe0\xa8\x97\xe0\xa8\xb0\xe0\xa9\x87\xe0\xa8\x97\xe0\xa9\x8b\xe0\xa8\xb0\xe0\xa9\x80\xe0\xa8\x85\xe0\xa8\xa8 \xe0\xa8\x95\xe0\xa9\x88\xe0\xa8\xb2\xe0\xa9\xb0\xe0\xa8\xa1\xe0\xa8\xb0\0" + "\xe0\xaa\x97\xe0\xab\x8d\xe0\xaa\xb0\xe0\xab\x87\xe0\xaa\x97\xe0\xab\x8b\xe0\xaa\xb0\xe0\xaa\xbf\xe0\xaa\x85\xe0\xaa\xa8 \xe0\xaa\x95\xe0\xab\x87\xe0\xaa\xb2\xe0\xab\x87\xe0\xaa\xa8\xe0\xab\x8d\xe0\xaa\xa1\xe0\xaa\xb0\0" + "\xe0\xae\x95\xe0\xae\xbf\xe0\xae\xb0\xe0\xae\xbf\xe0\xae\x95\xe0\xaf\x8b\xe0\xae\xb0\xe0\xae\xbf\xe0\xae\xaf\xe0\xae\xa9\xe0\xaf\x8d \xe0\xae\xa8\xe0\xae\xbe\xe0\xae\xb3\xe0\xaf\x8d\xe0\xae\x95\xe0\xae\xbe\xe0\xae\x9f\xe0\xaf\x8d\xe0\xae\x9f\xe0\xae\xbf\0" + "\xe0\xb0\x97\xe0\xb1\x8d\xe0\xb0\xb0\xe0\xb1\x87\xe0\xb0\x97\xe0\xb1\x8b\xe0\xb0\xb0\xe0\xb0\xbf\xe0\xb0\xaf\xe0\xb0\xa8\xe0\xb1\x8d \xe0\xb0\x95\xe0\xb1\x8d\xe0\xb0\xaf\xe0\xb0\xbe\xe0\xb0\xb2\xe0\xb1\x86\xe0\xb0\x82\xe0\xb0\xa1\xe0\xb0\xb0\xe0\xb1\x8d\0" + "\xe0\xb2\x97\xe0\xb3\x8d\xe0\xb2\xb0\xe0\xb3\x86\xe0\xb2\x97\xe0\xb3\x8b\xe0\xb2\xb0\xe0\xb2\xbf\xe0\xb2\xaf\xe0\xb2\xa8\xe0\xb3\x8d \xe0\xb2\x95\xe0\xb3\x8d\xe0\xb2\xaf\xe0\xb2\xbe\xe0\xb2\xb2\xe0\xb3\x86\xe0\xb2\x82\xe0\xb2\xa1\xe0\xb2\xb0\xe0\xb3\x8d\0" + "\xe0\xb4\x87\xe0\xb4\x82\xe0\xb4\x97\xe0\xb5\x8d\xe0\xb4\xb2\xe0\xb5\x80\xe0\xb4\xb7\xe0\xb5\x8d \xe0\xb4\x95\xe0\xb4\xb2\xe0\xb4\xa3\xe0\xb5\x8d\xe0\xb4\x9f\xe0\xb5\xbc\0" + "\xe0\xa6\x97\xe0\xa7\x8d\xe0\xa7\xb0\xe0\xa6\xbf\xe0\xa6\x97\xe0\xa7\x8b\xe0\xa7\xb0\xe0\xa7\x80\xe0\xa6\xaf\xe0\xa6\xbc \xe0\xa6\xaa\xe0\xa6\x9e\xe0\xa7\x8d\xe0\xa6\x9c\xe0\xa6\xbf\xe0\xa6\x95\xe0\xa6\xbe\0" + "\xe0\xa4\x97\xe0\xa5\x8d\xe0\xa4\xb0\xe0\xa5\x87\xe0\xa4\x97\xe0\xa5\x8b\xe0\xa4\xb0\xe0\xa4\xbf\xe0\xa4\xaf\xe0\xa4\xa8 \xe0\xa4\xa6\xe0\xa4\xbf\xe0\xa4\xa8\xe0\xa4\xa6\xe0\xa4\xb0\xe0\xa5\x8d\xe0\xa4\xb6\xe0\xa4\xbf\xe0\xa4\x95\xe0\xa4\xbe\0" + "\xd0\xb3\xd1\x80\xd0\xb5\xd0\xb3\xd0\xbe\xd1\x80\xd0\xb8\xd0\xb9\xd0\xbd \xd1\x85\xd1\x83\xd0\xb0\xd0\xbd\xd0\xbb\xd0\xb8\0" + "Calendr Gregori\0" + "\xe1\x9e\x94\xe1\x9f\x92\xe1\x9e\x9a\xe1\x9e\x8f\xe1\x9e\xb7\xe1\x9e\x91\xe1\x9e\xb7\xe1\x9e\x93\xe2\x80\x8b\xe1\x9e\xa0\xe1\x9f\x92\xe1\x9e\x9f\xe1\x9e\x80\xe1\x9e\xa0\xe1\x9f\x92\xe1\x9e\x9f\xe1\x9f\x8a\xe1\x9e\xb8\0" + "\xe0\xba\x9b\xe0\xba\xb0\xe0\xba\x95\xe0\xba\xb4\xe0\xba\x97\xe0\xba\xb4\xe0\xba\x99\xe0\xbb\x80\xe0\xba\x81\xe0\xba\xa3\xe0\xbb\x82\xe0\xba\x81\xe0\xba\xa3\xe0\xba\xbd\xe0\xba\x99\0" + "\xe1\x80\x94\xe1\x80\xad\xe1\x80\xaf\xe1\x80\x84\xe1\x80\xba\xe1\x80\x84\xe1\x80\xb6\xe1\x80\x90\xe1\x80\x80\xe1\x80\xac\xe1\x80\x9e\xe1\x80\xaf\xe1\x80\xb6\xe1\x80\xb8 \xe1\x80\x95\xe1\x80\xbc\xe1\x80\x80\xe1\x80\xb9\xe1\x80\x81\xe1\x80\x92\xe1\x80\xad\xe1\x80\x94\xe1\x80\xba\0" + "\xe0\xb6\x9c\xe0\xb7\x8a\xe2\x80\x8d\xe0\xb6\xbb\xe0\xb7\x99\xe0\xb6\x9c\xe0\xb6\xbb\xe0\xb7\x92\xe0\xb6\xba\xe0\xb7\x8f\xe0\xb6\xb1\xe0\xb7\x94 \xe0\xb6\xaf\xe0\xb7\x92\xe0\xb6\xb1 \xe0\xb6\xaf\xe0\xb6\xbb\xe0\xb7\x8a\xe0\xb7\x81\xe0\xb6\xb1\xe0\xb6\xba\0" + "\xe1\x8e\xa9\xe1\x8e\xb4\xe1\x8e\xaa\xe1\x8e\xb5\xe1\x8e\xa0\xe1\x8f\x82 \xe1\x8f\x85\xe1\x8f\x99 \xe1\x8f\x97\xe1\x8f\x8e\xe1\x8f\x8d\xe1\x8f\x97\0" + "\xe1\x8b\xa8\xe1\x8c\x8d\xe1\x88\xaa\xe1\x8c\x8e\xe1\x88\xaa\xe1\x8b\xab\xe1\x8a\x95 \xe1\x8b\xa8\xe1\x89\x80\xe1\x8a\x95 \xe1\x8a\xa0\xe1\x89\x86\xe1\x8c\xa3\xe1\x8c\xa0\xe1\x88\xad\0" + "\xe0\xa4\x97\xe0\xa5\x8d\xe0\xa4\xb0\xe0\xa5\x87\xe0\xa4\x97\xe0\xa5\x8b\xe0\xa4\xb0\xe0\xa4\xbf\xe0\xa4\xaf\xe0\xa4\xa8 \xe0\xa4\xaa\xe0\xa4\xbe\xe0\xa4\xa4\xe0\xa5\x8d\xe0\xa4\xb0\xe0\xa5\x8b\0" + "Gregoriaanske kalinder\0" + "Gregorian na Kalendaryo\0" + "Gregorianesche Kalenner\0" + "gregorianskit ullorsiutaat\0" + "\xea\x84\x89\xea\x89\xbb\xea\x83\x85\xea\x91\x8d\0" + "deiziadur gregorian\0" + "\xd9\x85\xd9\x89\xd9\x84\xd8\xa7\xd8\xaf\xd9\x89\xd9\x8a\xdb\x95 \xd9\x8a\xd9\x89\xd9\x84\xd9\x86\xd8\xa7\xd9\x85\xdb\x95\xd8\xb3\xd9\x89\0" + "Gregoriaanisch Kal\xc3\xa4nder\0" + "Am M\xc3\xacosachan Griogarach\0" + "\xe5\x85\xac\xe6\x9b\x86\0" + "\xd8\xa7\xd9\x84\xd8\xaa\xd9\x82\xd9\x88\xd9\x8a\xd9\x85 \xd8\xa7\xd9\x84\xd9\x85\xd9\x8a\xd9\x84\xd8\xa7\xd8\xaf\xd9\x8a\0" + "Calend\xc3\xa1rio gregoriano\0" + "gregoria\xc5\x84ski kalender\0" + "\xd0\x93\xd1\x80\xd0\xb8\xd0\xb3\xd0\xbe\xd1\x80\xd0\xb8\xd0\xb0\xd0\xbd \xd0\xba\xd0\xb0\xd0\xbb\xd0\xb5\xd0\xbd\xd0\xb4\xd0\xb0\xd1\x80\xd0\xb8\0" + "gregoriala\xc5\xa1 kalendar\0" + "\xd0\xb3\xd1\x80\xd0\xb5\xd0\xb3\xd0\xbe\xd1\x80\xd0\xb8\xd1\x98\xd0\xb0\xd0\xbd\xd1\x81\xd0\xba\xd0\xb8 \xd0\xba\xd0\xb0\xd0\xbb\xd0\xb5\xd0\xbd\xd0\xb4\xd0\xb0\xd1\x80\0" +}; + + +#endif + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/culture-info.h b/unity-2019.4.24f1-mbe/mono/metadata/culture-info.h new file mode 100644 index 000000000..69e9005e1 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/culture-info.h @@ -0,0 +1,137 @@ +/** + * \file + */ + +#ifndef _MONO_METADATA_CULTURE_INFO_H_ +#define _MONO_METADATA_CULTURE_INFO_H_ 1 + +#include +#include + +#define NUM_DAYS 7 +#define NUM_MONTHS 13 +#define GROUP_SIZE 2 +#define NUM_CALENDARS 4 + +#define NUM_SHORT_DATE_PATTERNS 14 +#define NUM_LONG_DATE_PATTERNS 10 +#define NUM_SHORT_TIME_PATTERNS 12 +#define NUM_LONG_TIME_PATTERNS 9 +#define NUM_YEAR_MONTH_PATTERNS 8 + +#define idx2string(idx) (locale_strings + (idx)) +#define pattern2string(idx) (patterns + (idx)) +#define dtidx2string(idx) (datetime_strings + (idx)) + +/* need to change this if the string data ends up to not fit in a 64KB array. */ +typedef guint16 stridx_t; + +typedef struct { + const stridx_t month_day_pattern; + const stridx_t am_designator; + const stridx_t pm_designator; + + const stridx_t day_names [NUM_DAYS]; + const stridx_t abbreviated_day_names [NUM_DAYS]; + const stridx_t shortest_day_names [NUM_DAYS]; + const stridx_t month_names [NUM_MONTHS]; + const stridx_t month_genitive_names [NUM_MONTHS]; + const stridx_t abbreviated_month_names [NUM_MONTHS]; + const stridx_t abbreviated_month_genitive_names [NUM_MONTHS]; + + const gint8 calendar_week_rule; + const gint8 first_day_of_week; + + const stridx_t date_separator; + const stridx_t time_separator; + + const stridx_t short_date_patterns [NUM_SHORT_DATE_PATTERNS]; + const stridx_t long_date_patterns [NUM_LONG_DATE_PATTERNS]; + const stridx_t short_time_patterns [NUM_SHORT_TIME_PATTERNS]; + const stridx_t long_time_patterns [NUM_LONG_TIME_PATTERNS]; + const stridx_t year_month_patterns [NUM_YEAR_MONTH_PATTERNS]; +} DateTimeFormatEntry; + +typedef struct { + const stridx_t currency_decimal_separator; + const stridx_t currency_group_separator; + const stridx_t number_decimal_separator; + const stridx_t number_group_separator; + + const stridx_t currency_symbol; + const stridx_t percent_symbol; + const stridx_t nan_symbol; + const stridx_t per_mille_symbol; + const stridx_t negative_infinity_symbol; + const stridx_t positive_infinity_symbol; + + const stridx_t negative_sign; + const stridx_t positive_sign; + + const gint8 currency_negative_pattern; + const gint8 currency_positive_pattern; + const gint8 percent_negative_pattern; + const gint8 percent_positive_pattern; + const gint8 number_negative_pattern; + + const gint8 currency_decimal_digits; + const gint8 number_decimal_digits; + + const gint currency_group_sizes [GROUP_SIZE]; + const gint number_group_sizes [GROUP_SIZE]; +} NumberFormatEntry; + +typedef struct { + const gint ansi; + const gint ebcdic; + const gint mac; + const gint oem; + const MonoBoolean is_right_to_left; + const char list_sep; +} TextInfoEntry; + +typedef struct { + const gint16 lcid; + const gint16 parent_lcid; + const gint16 calendar_type; + const gint16 region_entry_index; + const stridx_t name; + const stridx_t englishname; + const stridx_t nativename; + const stridx_t win3lang; + const stridx_t iso3lang; + const stridx_t iso2lang; + const stridx_t territory; + const stridx_t native_calendar_names [NUM_CALENDARS]; + + const gint16 datetime_format_index; + const gint16 number_format_index; + + const TextInfoEntry text_info; +} CultureInfoEntry; + +typedef struct { + const stridx_t name; + const gint16 culture_entry_index; +} CultureInfoNameEntry; + +typedef struct { + const gint16 geo_id; + const stridx_t iso2name; + const stridx_t iso3name; + const stridx_t win3name; + const stridx_t english_name; + const stridx_t native_name; + const stridx_t currency_symbol; + const stridx_t iso_currency_symbol; + const stridx_t currency_english_name; + const stridx_t currency_native_name; +} RegionInfoEntry; + +typedef struct { + const stridx_t name; + const gint16 region_entry_index; +} RegionInfoNameEntry; + +#endif + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/custom-attrs-internals.h b/unity-2019.4.24f1-mbe/mono/metadata/custom-attrs-internals.h new file mode 100644 index 000000000..a848c0b0e --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/custom-attrs-internals.h @@ -0,0 +1,25 @@ +/** + * \file + */ + +#ifndef __MONO_METADATA_CUSTOM_ATTRS_INTERNALS_H__ +#define __MONO_METADATA_CUSTOM_ATTRS_INTERNALS_H__ + +#include +#include + +MonoCustomAttrInfo* +mono_custom_attrs_from_builders (MonoImage *alloc_img, MonoImage *image, MonoArray *cattrs); + +typedef gboolean (*MonoAssemblyMetadataCustomAttrIterFunc) (MonoImage *image, guint32 typeref_scope_token, const gchar* nspace, const gchar* name, guint32 method_token, gpointer user_data); + +void +mono_assembly_metadata_foreach_custom_attr (MonoAssembly *assembly, MonoAssemblyMetadataCustomAttrIterFunc func, gpointer user_data); + +gboolean +mono_assembly_is_weak_field (MonoImage *image, guint32 field_idx); + +void +mono_assembly_init_weak_fields (MonoImage *image); + +#endif /* __MONO_METADATA_REFLECTION_CUSTOM_ATTRS_INTERNALS_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/custom-attrs.c b/unity-2019.4.24f1-mbe/mono/metadata/custom-attrs.c new file mode 100644 index 000000000..057db519f --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/custom-attrs.c @@ -0,0 +1,2335 @@ +/** + * \file + * Custom attributes. + * + * Author: + * Paolo Molaro (lupus@ximian.com) + * + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Copyright 2011 Rodrigo Kumpera + * Copyright 2016 Microsoft + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#include +#include "mono/metadata/assembly.h" +#include "mono/metadata/gc-internals.h" +#include "mono/metadata/mono-endian.h" +#include "mono/metadata/object-internals.h" +#include "mono/metadata/custom-attrs-internals.h" +#include "mono/metadata/sre-internals.h" +#include "mono/metadata/reflection-internals.h" +#include "mono/metadata/tabledefs.h" +#include "mono/metadata/tokentype.h" +#include "mono/metadata/verify-internals.h" +#include "mono/utils/checked-build.h" + + +#define CHECK_ADD4_OVERFLOW_UN(a, b) ((guint32)(0xFFFFFFFFU) - (guint32)(b) < (guint32)(a)) +#define CHECK_ADD8_OVERFLOW_UN(a, b) ((guint64)(0xFFFFFFFFFFFFFFFFUL) - (guint64)(b) < (guint64)(a)) + +#if SIZEOF_VOID_P == 4 +#define CHECK_ADDP_OVERFLOW_UN(a,b) CHECK_ADD4_OVERFLOW_UN(a, b) +#else +#define CHECK_ADDP_OVERFLOW_UN(a,b) CHECK_ADD8_OVERFLOW_UN(a, b) +#endif + +#define ADDP_IS_GREATER_OR_OVF(a, b, c) (((a) + (b) > (c)) || CHECK_ADDP_OVERFLOW_UN (a, b)) +#define ADD_IS_GREATER_OR_OVF(a, b, c) (((a) + (b) > (c)) || CHECK_ADD4_OVERFLOW_UN (a, b)) + +static gboolean type_is_reference (MonoType *type); + +static GENERATE_GET_CLASS_WITH_CACHE (custom_attribute_typed_argument, "System.Reflection", "CustomAttributeTypedArgument"); +static GENERATE_GET_CLASS_WITH_CACHE (custom_attribute_named_argument, "System.Reflection", "CustomAttributeNamedArgument"); + +static MonoCustomAttrInfo* +mono_custom_attrs_from_builders_handle (MonoImage *alloc_img, MonoImage *image, MonoArrayHandle cattrs); + +static gboolean +bcheck_blob (const char *ptr, int bump, const char *endp, MonoError *error); + +static gboolean +decode_blob_value_checked (const char *ptr, const char *endp, guint32 *size_out, const char **retp, MonoError *error); + +/* + * LOCKING: Acquires the loader lock. + */ +static MonoCustomAttrInfo* +lookup_custom_attr (MonoImage *image, gpointer member) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + MonoCustomAttrInfo* res; + + res = (MonoCustomAttrInfo *)mono_image_property_lookup (image, member, MONO_PROP_DYNAMIC_CATTR); + + if (!res) + return NULL; + + res = (MonoCustomAttrInfo *)g_memdup (res, MONO_SIZEOF_CUSTOM_ATTR_INFO + sizeof (MonoCustomAttrEntry) * res->num_attrs); + res->cached = 0; + return res; +} + +static gboolean +custom_attr_visible (MonoImage *image, MonoReflectionCustomAttr *cattr) +{ + MONO_REQ_GC_UNSAFE_MODE; + + /* FIXME: Need to do more checks */ + if (cattr->ctor->method && (cattr->ctor->method->klass->image != image)) { + int visibility = mono_class_get_flags (cattr->ctor->method->klass) & TYPE_ATTRIBUTE_VISIBILITY_MASK; + + if ((visibility != TYPE_ATTRIBUTE_PUBLIC) && (visibility != TYPE_ATTRIBUTE_NESTED_PUBLIC)) + return FALSE; + } + + return TRUE; +} + +static gboolean +type_is_reference (MonoType *type) +{ + switch (type->type) { + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_CHAR: + case MONO_TYPE_U: + case MONO_TYPE_I: + case MONO_TYPE_U1: + case MONO_TYPE_I1: + case MONO_TYPE_U2: + case MONO_TYPE_I2: + case MONO_TYPE_U4: + case MONO_TYPE_I4: + case MONO_TYPE_U8: + case MONO_TYPE_I8: + case MONO_TYPE_R8: + case MONO_TYPE_R4: + case MONO_TYPE_VALUETYPE: + return FALSE; + default: + return TRUE; + } +} + +static void +free_param_data (MonoMethodSignature *sig, void **params) { + int i; + for (i = 0; i < sig->param_count; ++i) { + if (!type_is_reference (sig->params [i])) + g_free (params [i]); + } +} + +/* + * Find the field index in the metadata FieldDef table. + */ +static guint32 +find_field_index (MonoClass *klass, MonoClassField *field) { + int i; + + int fcount = mono_class_get_field_count (klass); + for (i = 0; i < fcount; ++i) { + if (field == &klass->fields [i]) + return mono_class_get_first_field_idx (klass) + 1 + i; + } + return 0; +} + +/* + * Find the property index in the metadata Property table. + */ +static guint32 +find_property_index (MonoClass *klass, MonoProperty *property) +{ + int i; + MonoClassPropertyInfo *info = mono_class_get_property_info (klass); + + for (i = 0; i < info->count; ++i) { + if (property == &info->properties [i]) + return info->first + 1 + i; + } + return 0; +} + +/* + * Find the event index in the metadata Event table. + */ +static guint32 +find_event_index (MonoClass *klass, MonoEvent *event) +{ + int i; + MonoClassEventInfo *info = mono_class_get_event_info (klass); + + for (i = 0; i < info->count; ++i) { + if (event == &info->events [i]) + return info->first + 1 + i; + } + return 0; +} + +/* + * Load the type with name @n on behalf of image @image. On failure sets @error and returns NULL. + * The @is_enum flag only affects the error message that's displayed on failure. + */ +static MonoType* +cattr_type_from_name (char *n, MonoImage *image, gboolean is_enum, MonoError *error) +{ + MonoError inner_error; + MonoType *t = mono_reflection_type_from_name_checked (n, image, &inner_error); + if (!t) { + mono_error_set_type_load_name (error, g_strdup(n), NULL, + "Could not load %s %s while decoding custom attribute: %s", + is_enum ? "enum type": "type", + n, + mono_error_get_message (&inner_error)); + mono_error_cleanup (&inner_error); + return NULL; + } + return t; +} + +static MonoClass* +load_cattr_enum_type (MonoImage *image, const char *p, const char *boundp, const char **end, MonoError *error) +{ + char *n; + MonoType *t; + guint32 slen; + error_init (error); + + if (!decode_blob_value_checked (p, boundp, &slen, &p, error)) + return NULL; + + if (boundp && slen > 0 && !bcheck_blob (p, slen - 1, boundp, error)) + return NULL; + n = (char *)g_memdup (p, slen + 1); + n [slen] = 0; + t = cattr_type_from_name (n, image, TRUE, error); + g_free (n); + return_val_if_nok (error, NULL); + p += slen; + *end = p; + return mono_class_from_mono_type (t); +} + +static void* +load_cattr_value (MonoImage *image, MonoType *t, const char *p, const char *boundp, const char **end, MonoError *error) +{ + int type = t->type; + guint32 slen; + MonoClass *tklass = t->data.klass; + + g_assert (boundp); + error_init (error); + +handle_enum: + switch (type) { + case MONO_TYPE_U1: + case MONO_TYPE_I1: + case MONO_TYPE_BOOLEAN: { + MonoBoolean *bval = (MonoBoolean *)g_malloc (sizeof (MonoBoolean)); + if (!bcheck_blob (p, 0, boundp, error)) + return NULL; + *bval = *p; + *end = p + 1; + return bval; + } + case MONO_TYPE_CHAR: + case MONO_TYPE_U2: + case MONO_TYPE_I2: { + guint16 *val = (guint16 *)g_malloc (sizeof (guint16)); + if (!bcheck_blob (p, 1, boundp, error)) + return NULL; + *val = read16 (p); + *end = p + 2; + return val; + } +#if SIZEOF_VOID_P == 4 + case MONO_TYPE_U: + case MONO_TYPE_I: +#endif + case MONO_TYPE_R4: + case MONO_TYPE_U4: + case MONO_TYPE_I4: { + guint32 *val = (guint32 *)g_malloc (sizeof (guint32)); + if (!bcheck_blob (p, 3, boundp, error)) + return NULL; + *val = read32 (p); + *end = p + 4; + return val; + } +#if SIZEOF_VOID_P == 8 + case MONO_TYPE_U: /* error out instead? this should probably not happen */ + case MONO_TYPE_I: +#endif + case MONO_TYPE_U8: + case MONO_TYPE_I8: { + guint64 *val = (guint64 *)g_malloc (sizeof (guint64)); + if (!bcheck_blob (p, 7, boundp, error)) + return NULL; + *val = read64 (p); + *end = p + 8; + return val; + } + case MONO_TYPE_R8: { + double *val = (double *)g_malloc (sizeof (double)); + if (!bcheck_blob (p, 7, boundp, error)) + return NULL; + readr8 (p, val); + *end = p + 8; + return val; + } + case MONO_TYPE_VALUETYPE: + if (t->data.klass->enumtype) { + type = mono_class_enum_basetype (t->data.klass)->type; + goto handle_enum; + } else { + MonoClass *k = t->data.klass; + + if (mono_is_corlib_image (k->image) && strcmp (k->name_space, "System") == 0 && strcmp (k->name, "DateTime") == 0){ + guint64 *val = (guint64 *)g_malloc (sizeof (guint64)); + if (!bcheck_blob (p, 7, boundp, error)) + return NULL; + *val = read64 (p); + *end = p + 8; + return val; + } + } + g_error ("generic valutype %s not handled in custom attr value decoding", t->data.klass->name); + break; + + case MONO_TYPE_STRING: + if (!bcheck_blob (p, 0, boundp, error)) + return NULL; + if (*p == (char)0xFF) { + *end = p + 1; + return NULL; + } + if (!decode_blob_value_checked (p, boundp, &slen, &p, error)) + return NULL; + if (slen > 0 && !bcheck_blob (p, slen - 1, boundp, error)) + return NULL; + *end = p + slen; + return mono_string_new_len_checked (mono_domain_get (), p, slen, error); + case MONO_TYPE_CLASS: { + MonoReflectionType *rt; + char *n; + MonoType *t; + if (!bcheck_blob (p, 0, boundp, error)) + return NULL; + if (*p == (char)0xFF) { + *end = p + 1; + return NULL; + } +handle_type: + if (!decode_blob_value_checked (p, boundp, &slen, &p, error)) + return NULL; + if (slen > 0 && !bcheck_blob (p, slen - 1, boundp, error)) + return NULL; + n = (char *)g_memdup (p, slen + 1); + n [slen] = 0; + t = cattr_type_from_name (n, image, FALSE, error); + g_free (n); + return_val_if_nok (error, NULL); + *end = p + slen; + + rt = mono_type_get_object_checked (mono_domain_get (), t, error); + if (!mono_error_ok (error)) + return NULL; + + return rt; + } + case MONO_TYPE_OBJECT: { + if (!bcheck_blob (p, 0, boundp, error)) + return NULL; + char subt = *p++; + MonoObject *obj; + MonoClass *subc = NULL; + void *val; + + if (subt == 0x50) { + goto handle_type; + } else if (subt == 0x0E) { + type = MONO_TYPE_STRING; + goto handle_enum; + } else if (subt == 0x1D) { + MonoType simple_type = {{0}}; + if (!bcheck_blob (p, 0, boundp, error)) + return NULL; + int etype = *p; + p ++; + + type = MONO_TYPE_SZARRAY; + if (etype == 0x50) { + tklass = mono_defaults.systemtype_class; + } else if (etype == 0x55) { + tklass = load_cattr_enum_type (image, p, boundp, &p, error); + if (!is_ok (error)) + return NULL; + } else { + if (etype == 0x51) + /* See Partition II, Appendix B3 */ + etype = MONO_TYPE_OBJECT; + simple_type.type = (MonoTypeEnum)etype; + tklass = mono_class_from_mono_type (&simple_type); + } + goto handle_enum; + } else if (subt == 0x55) { + char *n; + MonoType *t; + if (!decode_blob_value_checked (p, boundp, &slen, &p, error)) + return NULL; + if (slen > 0 && !bcheck_blob (p, slen - 1, boundp, error)) + return NULL; + n = (char *)g_memdup (p, slen + 1); + n [slen] = 0; + t = cattr_type_from_name (n, image, FALSE, error); + g_free (n); + return_val_if_nok (error, NULL); + p += slen; + subc = mono_class_from_mono_type (t); + } else if (subt >= MONO_TYPE_BOOLEAN && subt <= MONO_TYPE_R8) { + MonoType simple_type = {{0}}; + simple_type.type = (MonoTypeEnum)subt; + subc = mono_class_from_mono_type (&simple_type); + } else { + g_error ("Unknown type 0x%02x for object type encoding in custom attr", subt); + } + val = load_cattr_value (image, &subc->byval_arg, p, boundp, end, error); + obj = NULL; + if (is_ok (error)) { + obj = mono_object_new_checked (mono_domain_get (), subc, error); + g_assert (!subc->has_references); + if (is_ok (error)) + mono_gc_memmove_atomic ((char*)obj + sizeof (MonoObject), val, mono_class_value_size (subc, NULL)); + } + + g_free (val); + return obj; + } + case MONO_TYPE_SZARRAY: { + MonoArray *arr; + guint32 i, alen, basetype; + if (!bcheck_blob (p, 3, boundp, error)) + return NULL; + alen = read32 (p); + p += 4; + if (alen == 0xffffffff) { + *end = p; + return NULL; + } + arr = mono_array_new_checked (mono_domain_get(), tklass, alen, error); + return_val_if_nok (error, NULL); + basetype = tklass->byval_arg.type; + if (basetype == MONO_TYPE_VALUETYPE && tklass->enumtype) + basetype = mono_class_enum_basetype (tklass)->type; + switch (basetype) + { + case MONO_TYPE_U1: + case MONO_TYPE_I1: + case MONO_TYPE_BOOLEAN: + for (i = 0; i < alen; i++) { + if (!bcheck_blob (p, 0, boundp, error)) + return NULL; + MonoBoolean val = *p++; + mono_array_set (arr, MonoBoolean, i, val); + } + break; + case MONO_TYPE_CHAR: + case MONO_TYPE_U2: + case MONO_TYPE_I2: + for (i = 0; i < alen; i++) { + if (!bcheck_blob (p, 1, boundp, error)) + return NULL; + guint16 val = read16 (p); + mono_array_set (arr, guint16, i, val); + p += 2; + } + break; + case MONO_TYPE_R4: + case MONO_TYPE_U4: + case MONO_TYPE_I4: + for (i = 0; i < alen; i++) { + if (!bcheck_blob (p, 3, boundp, error)) + return NULL; + guint32 val = read32 (p); + mono_array_set (arr, guint32, i, val); + p += 4; + } + break; + case MONO_TYPE_R8: + for (i = 0; i < alen; i++) { + if (!bcheck_blob (p, 7, boundp, error)) + return NULL; + double val; + readr8 (p, &val); + mono_array_set (arr, double, i, val); + p += 8; + } + break; + case MONO_TYPE_U8: + case MONO_TYPE_I8: + for (i = 0; i < alen; i++) { + if (!bcheck_blob (p, 7, boundp, error)) + return NULL; + guint64 val = read64 (p); + mono_array_set (arr, guint64, i, val); + p += 8; + } + break; + case MONO_TYPE_CLASS: + case MONO_TYPE_OBJECT: + case MONO_TYPE_STRING: + case MONO_TYPE_SZARRAY: + for (i = 0; i < alen; i++) { + MonoObject *item = (MonoObject *)load_cattr_value (image, &tklass->byval_arg, p, boundp, &p, error); + if (!is_ok (error)) + return NULL; + mono_array_setref (arr, i, item); + } + break; + default: + g_error ("Type 0x%02x not handled in custom attr array decoding", basetype); + } + *end=p; + return arr; + } + default: + g_error ("Type 0x%02x not handled in custom attr value decoding", type); + } + return NULL; +} + +static MonoObject* +load_cattr_value_boxed (MonoDomain *domain, MonoImage *image, MonoType *t, const char* p, const char *boundp, const char** end, MonoError *error) +{ + error_init (error); + + gboolean is_ref = type_is_reference (t); + + void *val = load_cattr_value (image, t, p, boundp, end, error); + if (!is_ok (error)) { + if (is_ref) + g_free (val); + return NULL; + } + + if (is_ref) + return (MonoObject*)val; + + MonoObject *boxed = mono_value_box_checked (domain, mono_class_from_mono_type (t), val, error); + g_free (val); + return boxed; +} + +static MonoObject* +create_cattr_typed_arg (MonoType *t, MonoObject *val, MonoError *error) +{ + static MonoMethod *ctor; + MonoObject *retval; + void *params [2], *unboxed; + + error_init (error); + + if (!ctor) + ctor = mono_class_get_method_from_name (mono_class_get_custom_attribute_typed_argument_class (), ".ctor", 2); + + params [0] = mono_type_get_object_checked (mono_domain_get (), t, error); + return_val_if_nok (error, NULL); + + params [1] = val; + retval = mono_object_new_checked (mono_domain_get (), mono_class_get_custom_attribute_typed_argument_class (), error); + return_val_if_nok (error, NULL); + unboxed = mono_object_unbox (retval); + + mono_runtime_invoke_checked (ctor, unboxed, params, error); + return_val_if_nok (error, NULL); + + return retval; +} + +static MonoObject* +create_cattr_named_arg (void *minfo, MonoObject *typedarg, MonoError *error) +{ + static MonoMethod *ctor; + MonoObject *retval; + void *unboxed, *params [2]; + + error_init (error); + + if (!ctor) + ctor = mono_class_get_method_from_name (mono_class_get_custom_attribute_named_argument_class (), ".ctor", 2); + + params [0] = minfo; + params [1] = typedarg; + retval = mono_object_new_checked (mono_domain_get (), mono_class_get_custom_attribute_named_argument_class (), error); + return_val_if_nok (error, NULL); + + unboxed = mono_object_unbox (retval); + + mono_runtime_invoke_checked (ctor, unboxed, params, error); + return_val_if_nok (error, NULL); + + return retval; +} + + +static MonoCustomAttrInfo* +mono_custom_attrs_from_builders_handle (MonoImage *alloc_img, MonoImage *image, MonoArrayHandle cattrs) +{ + return mono_custom_attrs_from_builders (alloc_img, image, MONO_HANDLE_RAW (cattrs)); /* FIXME use coop handles for mono_custom_attrs_from_builders */ +} + +MonoCustomAttrInfo* +mono_custom_attrs_from_builders (MonoImage *alloc_img, MonoImage *image, MonoArray *cattrs) +{ + MONO_REQ_GC_UNSAFE_MODE; + + int i, index, count, not_visible; + MonoCustomAttrInfo *ainfo; + MonoReflectionCustomAttr *cattr; + + if (!cattrs) + return NULL; + /* FIXME: check in assembly the Run flag is set */ + + count = mono_array_length (cattrs); + + /* Skip nonpublic attributes since MS.NET seems to do the same */ + /* FIXME: This needs to be done more globally */ + not_visible = 0; + for (i = 0; i < count; ++i) { + cattr = (MonoReflectionCustomAttr*)mono_array_get (cattrs, gpointer, i); + if (!custom_attr_visible (image, cattr)) + not_visible ++; + } + + int num_attrs = count - not_visible; + ainfo = (MonoCustomAttrInfo *)mono_image_g_malloc0 (alloc_img, MONO_SIZEOF_CUSTOM_ATTR_INFO + sizeof (MonoCustomAttrEntry) * num_attrs); + + ainfo->image = image; + ainfo->num_attrs = num_attrs; + ainfo->cached = alloc_img != NULL; + index = 0; + for (i = 0; i < count; ++i) { + cattr = (MonoReflectionCustomAttr*)mono_array_get (cattrs, gpointer, i); + if (custom_attr_visible (image, cattr)) { + unsigned char *saved = (unsigned char *)mono_image_alloc (image, mono_array_length (cattr->data)); + memcpy (saved, mono_array_addr (cattr->data, char, 0), mono_array_length (cattr->data)); + ainfo->attrs [index].ctor = cattr->ctor->method; + g_assert (cattr->ctor->method); + ainfo->attrs [index].data = saved; + ainfo->attrs [index].data_size = mono_array_length (cattr->data); + index ++; + } + } + g_assert (index == num_attrs && count == num_attrs + not_visible); + + return ainfo; +} + +static void +set_custom_attr_fmt_error (MonoError *error) +{ + error_init (error); + mono_error_set_generic_error (error, "System.Reflection", "CustomAttributeFormatException", "Binary format of the specified custom attribute was invalid."); +} + +/** + * bcheck_blob: + * \param ptr a pointer into a blob + * \param bump how far we plan on reading past \p ptr. + * \param endp upper bound for \p ptr - one past the last valid value for \p ptr. + * \param error set on error + * + * Check that ptr+bump is below endp. Returns TRUE on success, or FALSE on + * failure and sets \p error. + */ +static gboolean +bcheck_blob (const char *ptr, int bump, const char *endp, MonoError *error) +{ + error_init (error); + if (ADDP_IS_GREATER_OR_OVF (ptr, bump, endp - 1)) { + set_custom_attr_fmt_error (error); + return FALSE; + } else + return TRUE; +} + +/** + * decode_blob_size_checked: + * \param ptr a pointer into a blob + * \param endp upper bound for \p ptr - one pas the last valid value for \p ptr + * \param size_out on success set to the decoded size + * \param retp on success set to the next byte after the encoded size + * \param error set on error + * + * Decode an encoded size value which takes 1, 2, or 4 bytes and set \p + * size_out to the decoded size and \p retp to the next byte after the encoded + * size. Returns TRUE on success, or FALASE on failure and sets \p error. + */ +static gboolean +decode_blob_size_checked (const char *ptr, const char *endp, guint32 *size_out, const char **retp, MonoError *error) +{ + error_init (error); + if (endp && !bcheck_blob (ptr, 0, endp, error)) + goto leave; + if ((*ptr & 0x80) != 0) { + if ((*ptr & 0x40) == 0 && !bcheck_blob (ptr, 1, endp, error)) + goto leave; + else if (!bcheck_blob (ptr, 3, endp, error)) + goto leave; + } + *size_out = mono_metadata_decode_blob_size (ptr, retp); +leave: + return is_ok (error); +} + +/** + * decode_blob_value_checked: + * \param ptr a pointer into a blob + * \param endp upper bound for \p ptr - one pas the last valid value for \p ptr + * \param value_out on success set to the decoded value + * \param retp on success set to the next byte after the encoded size + * \param error set on error + * + * Decode an encoded uint32 value which takes 1, 2, or 4 bytes and set \p + * value_out to the decoded value and \p retp to the next byte after the + * encoded value. Returns TRUE on success, or FALASE on failure and sets \p + * error. + */ +static gboolean +decode_blob_value_checked (const char *ptr, const char *endp, guint32 *value_out, const char **retp, MonoError *error) +{ + /* This similar to decode_blob_size_checked, above but delegates to + * mono_metadata_decode_value which is semantically different. */ + error_init (error); + if (!bcheck_blob (ptr, 0, endp, error)) + goto leave; + if ((*ptr & 0x80) != 0) { + if ((*ptr & 0x40) == 0 && !bcheck_blob (ptr, 1, endp, error)) + goto leave; + else if (!bcheck_blob (ptr, 3, endp, error)) + goto leave; + } + *value_out = mono_metadata_decode_value (ptr, retp); +leave: + return is_ok (error); +} + +static MonoObject* +create_custom_attr (MonoImage *image, MonoMethod *method, const guchar *data, guint32 len, MonoError *error) +{ + const char *p = (const char*)data; + const char *data_end = (const char*)data + len; + const char *named; + guint32 i, j, num_named; + MonoObject *attr; + void *params_buf [32]; + void **params = NULL; + MonoMethodSignature *sig; + + error_init (error); + + mono_class_init (method->klass); + + if (!mono_verifier_verify_cattr_content (image, method, data, len, NULL)) { + set_custom_attr_fmt_error (error); + return NULL; + } + + if (len == 0) { + attr = mono_object_new_checked (mono_domain_get (), method->klass, error); + if (!mono_error_ok (error)) return NULL; + + mono_runtime_invoke_checked (method, attr, NULL, error); + if (!mono_error_ok (error)) + return NULL; + + return attr; + } + + if (len < 2 || read16 (p) != 0x0001) /* Prolog */ + return NULL; + + /*g_print ("got attr %s\n", method->klass->name);*/ + + sig = mono_method_signature (method); + if (sig->param_count < 32) { + params = params_buf; + memset (params, 0, sizeof (void*) * sig->param_count); + } else { + /* Allocate using GC so it gets GC tracking */ + params = (void **)mono_gc_alloc_fixed (sig->param_count * sizeof (void*), MONO_GC_DESCRIPTOR_NULL, MONO_ROOT_SOURCE_REFLECTION, NULL, "Reflection Custom Attribute Parameters"); + } + + /* skip prolog */ + p += 2; + for (i = 0; i < mono_method_signature (method)->param_count; ++i) { + params [i] = load_cattr_value (image, mono_method_signature (method)->params [i], p, data_end, &p, error); + goto_if_nok (error, fail); + } + + named = p; + attr = mono_object_new_checked (mono_domain_get (), method->klass, error); + if (!mono_error_ok (error)) goto fail; + + MonoObject *exc = NULL; + mono_runtime_try_invoke (method, attr, params, &exc, error); + if (!mono_error_ok (error)) + goto fail; + if (exc) { + mono_error_set_exception_instance (error, (MonoException*)exc); + goto fail; + } + + if (named + 1 < data_end) { + num_named = read16 (named); + named += 2; + } else { + /* CoreCLR allows p == data + len */ + if (named == data_end) + num_named = 0; + else { + set_custom_attr_fmt_error (error); + goto fail; + } + } + for (j = 0; j < num_named; j++) { + guint32 name_len; + char *name, named_type, data_type; + if (!bcheck_blob (named, 1, data_end, error)) + goto fail; + named_type = *named++; + data_type = *named++; /* type of data */ + if (data_type == MONO_TYPE_SZARRAY) { + if (!bcheck_blob (named, 0, data_end, error)) + goto fail; + data_type = *named++; + } + if (data_type == MONO_TYPE_ENUM) { + guint32 type_len; + char *type_name; + if (!decode_blob_size_checked (named, data_end, &type_len, &named, error)) + goto fail; + if (type_len > 0 && !bcheck_blob (named, type_len - 1, data_end, error)) + goto fail; + type_name = (char *)g_malloc (type_len + 1); + memcpy (type_name, named, type_len); + type_name [type_len] = 0; + named += type_len; + /* FIXME: lookup the type and check type consistency */ + g_free (type_name); + } + if (!decode_blob_size_checked (named, data_end, &name_len, &named, error)) + goto fail; + if (name_len > 0 && !bcheck_blob (named, name_len - 1, data_end, error)) + goto fail; + name = (char *)g_malloc (name_len + 1); + memcpy (name, named, name_len); + name [name_len] = 0; + named += name_len; + if (named_type == 0x53) { + MonoClassField *field; + void *val; + + /* how this fail is a blackbox */ + field = mono_class_get_field_from_name (mono_object_class (attr), name); + if (!field) { + mono_error_set_generic_error (error, "System.Reflection", "CustomAttributeFormatException", "Could not find a field with name %s", name); + g_free (name); + goto fail; + } + + val = load_cattr_value (image, field->type, named, data_end, &named, error); + if (!is_ok (error)) { + g_free (name); + if (!type_is_reference (field->type)) + g_free (val); + goto fail; + } + + mono_field_set_value (attr, field, val); + if (!type_is_reference (field->type)) + g_free (val); + } else if (named_type == 0x54) { + MonoProperty *prop; + void *pparams [1]; + MonoType *prop_type; + + prop = mono_class_get_property_from_name (mono_object_class (attr), name); + + if (!prop) { + mono_error_set_generic_error (error, "System.Reflection", "CustomAttributeFormatException", "Could not find a property with name %s", name); + g_free (name); + goto fail; + } + + if (!prop->set) { + mono_error_set_generic_error (error, "System.Reflection", "CustomAttributeFormatException", "Could not find the setter for %s", name); + g_free (name); + goto fail; + } + + /* can we have more that 1 arg in a custom attr named property? */ + prop_type = prop->get? mono_method_signature (prop->get)->ret : + mono_method_signature (prop->set)->params [mono_method_signature (prop->set)->param_count - 1]; + + pparams [0] = load_cattr_value (image, prop_type, named, data_end, &named, error); + if (!is_ok (error)) { + g_free (name); + if (!type_is_reference (prop_type)) + g_free (pparams [0]); + goto fail; + } + + + mono_property_set_value_checked (prop, attr, pparams, error); + if (!type_is_reference (prop_type)) + g_free (pparams [0]); + if (!is_ok (error)) { + g_free (name); + goto fail; + } + } + g_free (name); + } + + free_param_data (method->signature, params); + if (params != params_buf) + mono_gc_free_fixed (params); + + return attr; + +fail: + free_param_data (method->signature, params); + if (params != params_buf) + mono_gc_free_fixed (params); + return NULL; +} + +/* + * mono_reflection_create_custom_attr_data_args: + * + * Create an array of typed and named arguments from the cattr blob given by DATA. + * TYPED_ARGS and NAMED_ARGS will contain the objects representing the arguments, + * NAMED_ARG_INFO will contain information about the named arguments. + */ +void +mono_reflection_create_custom_attr_data_args (MonoImage *image, MonoMethod *method, const guchar *data, guint32 len, MonoArray **typed_args, MonoArray **named_args, CattrNamedArg **named_arg_info, MonoError *error) +{ + MonoArray *typedargs, *namedargs; + MonoClass *attrklass; + MonoDomain *domain; + const char *p = (const char*)data; + const char *data_end = p + len; + const char *named; + guint32 i, j, num_named; + CattrNamedArg *arginfo = NULL; + + *typed_args = NULL; + *named_args = NULL; + *named_arg_info = NULL; + + error_init (error); + + if (!mono_verifier_verify_cattr_content (image, method, data, len, NULL)) { + mono_error_set_generic_error (error, "System.Reflection", "CustomAttributeFormatException", "Binary format of the specified custom attribute was invalid."); + return; + } + + mono_class_init (method->klass); + + domain = mono_domain_get (); + + if (len < 2 || read16 (p) != 0x0001) /* Prolog */ + return; + /* skip prolog */ + p += 2; + + /* Parse each argument corresponding to the signature's parameters from + * the blob and store in typedargs. + */ + typedargs = mono_array_new_checked (domain, mono_get_object_class (), mono_method_signature (method)->param_count, error); + return_if_nok (error); + + for (i = 0; i < mono_method_signature (method)->param_count; ++i) { + MonoObject *obj; + + obj = load_cattr_value_boxed (domain, image, mono_method_signature (method)->params [i], p, data_end, &p, error); + return_if_nok (error); + mono_array_setref (typedargs, i, obj); + } + + named = p; + + /* Parse mandatory count of named arguments (could be zero) */ + if (!bcheck_blob (named, 1, data_end, error)) + return; + num_named = read16 (named); + namedargs = mono_array_new_checked (domain, mono_get_object_class (), num_named, error); + return_if_nok (error); + named += 2; + attrklass = method->klass; + + arginfo = g_new0 (CattrNamedArg, num_named); + *named_arg_info = arginfo; + + /* Parse each named arg, and add to arginfo. Each named argument could + * be a field name or a property name followed by a value. */ + for (j = 0; j < num_named; j++) { + guint32 name_len; + char *name, named_type, data_type; + if (!bcheck_blob (named, 1, data_end, error)) + return; + named_type = *named++; /* field or property? */ + data_type = *named++; /* type of data */ + if (data_type == MONO_TYPE_SZARRAY) { + if (!bcheck_blob (named, 0, data_end, error)) + return; + data_type = *named++; + } + if (data_type == MONO_TYPE_ENUM) { + guint32 type_len; + char *type_name; + if (!decode_blob_size_checked (named, data_end, &type_len, &named, error)) + return; + if (ADDP_IS_GREATER_OR_OVF ((const guchar*)named, type_len, data + len)) + goto fail; + + type_name = (char *)g_malloc (type_len + 1); + memcpy (type_name, named, type_len); + type_name [type_len] = 0; + named += type_len; + /* FIXME: lookup the type and check type consistency */ + g_free (type_name); + } + /* named argument name: length, then name */ + if (!decode_blob_size_checked(named, data_end, &name_len, &named, error)) + return; + if (ADDP_IS_GREATER_OR_OVF ((const guchar*)named, name_len, data + len)) + goto fail; + name = (char *)g_malloc (name_len + 1); + memcpy (name, named, name_len); + name [name_len] = 0; + named += name_len; + if (named_type == 0x53) { + /* Named arg is a field. */ + MonoObject *obj; + MonoClassField *field = mono_class_get_field_from_name (attrklass, name); + + if (!field) { + g_free (name); + goto fail; + } + + arginfo [j].type = field->type; + arginfo [j].field = field; + + obj = load_cattr_value_boxed (domain, image, field->type, named, data_end, &named, error); + if (!is_ok (error)) { + g_free (name); + return; + } + mono_array_setref (namedargs, j, obj); + + } else if (named_type == 0x54) { + /* Named arg is a property */ + MonoObject *obj; + MonoType *prop_type; + MonoProperty *prop = mono_class_get_property_from_name (attrklass, name); + + if (!prop || !prop->set) { + g_free (name); + goto fail; + } + + prop_type = prop->get? mono_method_signature (prop->get)->ret : + mono_method_signature (prop->set)->params [mono_method_signature (prop->set)->param_count - 1]; + + arginfo [j].type = prop_type; + arginfo [j].prop = prop; + + obj = load_cattr_value_boxed (domain, image, prop_type, named, data_end, &named, error); + if (!is_ok (error)) { + g_free (name); + return; + } + mono_array_setref (namedargs, j, obj); + } + g_free (name); + } + + *typed_args = typedargs; + *named_args = namedargs; + return; +fail: + mono_error_set_generic_error (error, "System.Reflection", "CustomAttributeFormatException", "Binary format of the specified custom attribute was invalid."); + g_free (arginfo); + *named_arg_info = NULL; +} + +static gboolean +reflection_resolve_custom_attribute_data (MonoReflectionMethod *ref_method, MonoReflectionAssembly *assembly, gpointer data, guint32 len, MonoArray **ctor_args, MonoArray **named_args, MonoError *error) +{ + MonoDomain *domain; + MonoArray *typedargs, *namedargs; + MonoImage *image; + MonoMethod *method; + CattrNamedArg *arginfo = NULL; + int i; + + error_init (error); + + *ctor_args = NULL; + *named_args = NULL; + + if (len == 0) + return TRUE; + + image = assembly->assembly->image; + method = ref_method->method; + domain = mono_object_domain (ref_method); + + if (!mono_class_init (method->klass)) { + mono_error_set_for_class_failure (error, method->klass); + goto leave; + } + + mono_reflection_create_custom_attr_data_args (image, method, (const guchar *)data, len, &typedargs, &namedargs, &arginfo, error); + goto_if_nok (error, leave); + + if (!typedargs || !namedargs) + goto leave; + + for (i = 0; i < mono_method_signature (method)->param_count; ++i) { + MonoObject *obj = mono_array_get (typedargs, MonoObject*, i); + MonoObject *typedarg; + + typedarg = create_cattr_typed_arg (mono_method_signature (method)->params [i], obj, error); + goto_if_nok (error, leave); + mono_array_setref (typedargs, i, typedarg); + } + + for (i = 0; i < mono_array_length (namedargs); ++i) { + MonoObject *obj = mono_array_get (namedargs, MonoObject*, i); + MonoObject *typedarg, *namedarg, *minfo; + + if (arginfo [i].prop) { + minfo = (MonoObject*)mono_property_get_object_checked (domain, NULL, arginfo [i].prop, error); + if (!minfo) + goto leave; + } else { + minfo = (MonoObject*)mono_field_get_object_checked (domain, NULL, arginfo [i].field, error); + goto_if_nok (error, leave); + } + + typedarg = create_cattr_typed_arg (arginfo [i].type, obj, error); + goto_if_nok (error, leave); + namedarg = create_cattr_named_arg (minfo, typedarg, error); + goto_if_nok (error, leave); + + mono_array_setref (namedargs, i, namedarg); + } + + *ctor_args = typedargs; + *named_args = namedargs; + +leave: + g_free (arginfo); + return mono_error_ok (error); +} + +void +ves_icall_System_Reflection_CustomAttributeData_ResolveArgumentsInternal (MonoReflectionMethod *ref_method, MonoReflectionAssembly *assembly, gpointer data, guint32 len, MonoArray **ctor_args, MonoArray **named_args) +{ + MonoError error; + (void) reflection_resolve_custom_attribute_data (ref_method, assembly, data, len, ctor_args, named_args, &error); + mono_error_set_pending_exception (&error); +} + +static MonoObjectHandle +create_custom_attr_data_handle (MonoImage *image, MonoCustomAttrEntry *cattr, MonoError *error) +{ + static MonoMethod *ctor; + + MonoDomain *domain; + void *params [4]; + + error_init (error); + + g_assert (image->assembly); + + if (!ctor) + ctor = mono_class_get_method_from_name (mono_defaults.customattribute_data_class, ".ctor", 4); + + domain = mono_domain_get (); + + MonoObjectHandle attr = MONO_HANDLE_NEW (MonoObject, mono_object_new_checked (domain, mono_defaults.customattribute_data_class, error)); + goto_if_nok (error, fail); + + MonoReflectionMethod *ctor_obj = mono_method_get_object_checked (domain, cattr->ctor, NULL, error); + goto_if_nok (error, fail); + MonoReflectionAssemblyHandle assm = mono_assembly_get_object_handle (domain, image->assembly, error); + goto_if_nok (error, fail); + params [0] = ctor_obj; + params [1] = MONO_HANDLE_RAW (assm); + params [2] = (gpointer)&cattr->data; + params [3] = &cattr->data_size; + + mono_runtime_invoke_checked (ctor, MONO_HANDLE_RAW (attr), params, error); + return attr; +fail: + return MONO_HANDLE_NEW (MonoObject, NULL); +} + +static MonoObject * +create_custom_attr_data (MonoImage *image, MonoCustomAttrEntry *cattr, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + MonoObjectHandle obj = create_custom_attr_data_handle (image, cattr, error); + HANDLE_FUNCTION_RETURN_OBJ (obj); +} + +MonoArray* +mono_custom_attrs_construct_by_type (MonoCustomAttrInfo *cinfo, MonoClass *attr_klass, MonoError *error) +{ + MonoArray *result; + MonoObject *attr; + int i, n; + + error_init (error); + + for (i = 0; i < cinfo->num_attrs; ++i) { + MonoCustomAttrEntry *centry = &cinfo->attrs[i]; + if (!centry->ctor) { + /* The cattr type is not finished yet */ + /* We should include the type name but cinfo doesn't contain it */ + mono_error_set_type_load_name (error, NULL, NULL, "Custom attribute constructor is null because the custom attribute type is not finished yet."); + return NULL; + } + } + + n = 0; + if (attr_klass) { + for (i = 0; i < cinfo->num_attrs; ++i) { + MonoMethod *ctor = cinfo->attrs[i].ctor; + g_assert (ctor); + if (mono_class_is_assignable_from (attr_klass, ctor->klass)) + n++; + } + } else { + n = cinfo->num_attrs; + } + + result = mono_array_new_cached (mono_domain_get (), mono_defaults.attribute_class, n, error); + return_val_if_nok (error, NULL); + n = 0; + for (i = 0; i < cinfo->num_attrs; ++i) { + MonoCustomAttrEntry *centry = &cinfo->attrs [i]; + if (!attr_klass || mono_class_is_assignable_from (attr_klass, centry->ctor->klass)) { + attr = create_custom_attr (cinfo->image, centry->ctor, centry->data, centry->data_size, error); + if (!mono_error_ok (error)) + return result; + mono_array_setref (result, n, attr); + n ++; + } + } + return result; +} + +/** + * mono_custom_attrs_construct: + */ +MonoArray* +mono_custom_attrs_construct (MonoCustomAttrInfo *cinfo) +{ + MonoError error; + MonoArray *result = mono_custom_attrs_construct_by_type (cinfo, NULL, &error); + mono_error_assert_ok (&error); /*FIXME proper error handling*/ + + return result; +} + +static MonoArray* +mono_custom_attrs_data_construct (MonoCustomAttrInfo *cinfo, MonoError *error) +{ + MonoArray *result; + MonoObject *attr; + int i; + + error_init (error); + result = mono_array_new_checked (mono_domain_get (), mono_defaults.customattribute_data_class, cinfo->num_attrs, error); + return_val_if_nok (error, NULL); + for (i = 0; i < cinfo->num_attrs; ++i) { + attr = create_custom_attr_data (cinfo->image, &cinfo->attrs [i], error); + return_val_if_nok (error, NULL); + mono_array_setref (result, i, attr); + } + return result; +} + +/** + * mono_custom_attrs_from_index: + * + * Returns: NULL if no attributes are found or if a loading error occurs. + */ +MonoCustomAttrInfo* +mono_custom_attrs_from_index (MonoImage *image, guint32 idx) +{ + MonoError error; + MonoCustomAttrInfo *result = mono_custom_attrs_from_index_checked (image, idx, FALSE, &error); + mono_error_cleanup (&error); + return result; +} +/** + * mono_custom_attrs_from_index_checked: + * \returns NULL if no attributes are found. On error returns NULL and sets \p error. + */ +MonoCustomAttrInfo* +mono_custom_attrs_from_index_checked (MonoImage *image, guint32 idx, gboolean ignore_missing, MonoError *error) +{ + guint32 mtoken, i, len; + guint32 cols [MONO_CUSTOM_ATTR_SIZE]; + MonoTableInfo *ca; + MonoCustomAttrInfo *ainfo; + GList *tmp, *list = NULL; + const char *data; + MonoCustomAttrEntry* attr; + + error_init (error); + + ca = &image->tables [MONO_TABLE_CUSTOMATTRIBUTE]; + + i = mono_metadata_custom_attrs_from_index (image, idx); + if (!i) + return NULL; + i --; + while (i < ca->rows) { + if (mono_metadata_decode_row_col (ca, i, MONO_CUSTOM_ATTR_PARENT) != idx) + break; + list = g_list_prepend (list, GUINT_TO_POINTER (i)); + ++i; + } + len = g_list_length (list); + if (!len) + return NULL; + ainfo = (MonoCustomAttrInfo *)g_malloc0 (MONO_SIZEOF_CUSTOM_ATTR_INFO + sizeof (MonoCustomAttrEntry) * len); + ainfo->num_attrs = len; + ainfo->image = image; + for (i = len, tmp = list; i != 0; --i, tmp = tmp->next) { + mono_metadata_decode_row (ca, GPOINTER_TO_UINT (tmp->data), cols, MONO_CUSTOM_ATTR_SIZE); + mtoken = cols [MONO_CUSTOM_ATTR_TYPE] >> MONO_CUSTOM_ATTR_TYPE_BITS; + switch (cols [MONO_CUSTOM_ATTR_TYPE] & MONO_CUSTOM_ATTR_TYPE_MASK) { + case MONO_CUSTOM_ATTR_TYPE_METHODDEF: + mtoken |= MONO_TOKEN_METHOD_DEF; + break; + case MONO_CUSTOM_ATTR_TYPE_MEMBERREF: + mtoken |= MONO_TOKEN_MEMBER_REF; + break; + default: + g_error ("Unknown table for custom attr type %08x", cols [MONO_CUSTOM_ATTR_TYPE]); + break; + } + attr = &ainfo->attrs [i - 1]; + attr->ctor = mono_get_method_checked (image, mtoken, NULL, NULL, error); + if (!attr->ctor) { + g_warning ("Can't find custom attr constructor image: %s mtoken: 0x%08x due to: %s", image->name, mtoken, mono_error_get_message (error)); + if (ignore_missing) { + mono_error_cleanup (error); + error_init (error); + } else { + g_list_free (list); + g_free (ainfo); + return NULL; + } + } + + if (!mono_verifier_verify_cattr_blob (image, cols [MONO_CUSTOM_ATTR_VALUE], NULL)) { + /*FIXME raising an exception here doesn't make any sense*/ + g_warning ("Invalid custom attribute blob on image %s for index %x", image->name, idx); + g_list_free (list); + g_free (ainfo); + return NULL; + } + data = mono_metadata_blob_heap (image, cols [MONO_CUSTOM_ATTR_VALUE]); + attr->data_size = mono_metadata_decode_value (data, &data); + attr->data = (guchar*)data; + } + g_list_free (list); + + return ainfo; +} + +/** + * mono_custom_attrs_from_method: + */ +MonoCustomAttrInfo* +mono_custom_attrs_from_method (MonoMethod *method) +{ + MonoError error; + MonoCustomAttrInfo* result = mono_custom_attrs_from_method_checked (method, &error); + mono_error_cleanup (&error); /* FIXME want a better API that doesn't swallow the error */ + return result; +} + +MonoCustomAttrInfo* +mono_custom_attrs_from_method_checked (MonoMethod *method, MonoError *error) +{ + guint32 idx; + + error_init (error); + + /* + * An instantiated method has the same cattrs as the generic method definition. + * + * LAMESPEC: The .NET SRE throws an exception for instantiations of generic method builders + * Note that this stanza is not necessary for non-SRE types, but it's a micro-optimization + */ + if (method->is_inflated) + method = ((MonoMethodInflated *) method)->declaring; + + if (method_is_dynamic (method) || image_is_dynamic (method->klass->image)) + return lookup_custom_attr (method->klass->image, method); + + if (!method->token) + /* Synthetic methods */ + return NULL; + + idx = mono_method_get_index (method); + idx <<= MONO_CUSTOM_ATTR_BITS; + idx |= MONO_CUSTOM_ATTR_METHODDEF; + return mono_custom_attrs_from_index_checked (method->klass->image, idx, FALSE, error); +} + +/** + * mono_custom_attrs_from_class: + */ +MonoCustomAttrInfo* +mono_custom_attrs_from_class (MonoClass *klass) +{ + MonoError error; + MonoCustomAttrInfo *result = mono_custom_attrs_from_class_checked (klass, &error); + mono_error_cleanup (&error); + return result; +} + +MonoCustomAttrInfo* +mono_custom_attrs_from_class_checked (MonoClass *klass, MonoError *error) +{ + guint32 idx; + + error_init (error); + + if (mono_class_is_ginst (klass)) + klass = mono_class_get_generic_class (klass)->container_class; + + if (image_is_dynamic (klass->image)) + return lookup_custom_attr (klass->image, klass); + + if (klass->byval_arg.type == MONO_TYPE_VAR || klass->byval_arg.type == MONO_TYPE_MVAR) { + idx = mono_metadata_token_index (klass->sizes.generic_param_token); + idx <<= MONO_CUSTOM_ATTR_BITS; + idx |= MONO_CUSTOM_ATTR_GENERICPAR; + } else { + idx = mono_metadata_token_index (klass->type_token); + idx <<= MONO_CUSTOM_ATTR_BITS; + idx |= MONO_CUSTOM_ATTR_TYPEDEF; + } + return mono_custom_attrs_from_index_checked (klass->image, idx, FALSE, error); +} + +/** + * mono_custom_attrs_from_assembly: + */ +MonoCustomAttrInfo* +mono_custom_attrs_from_assembly (MonoAssembly *assembly) +{ + MonoError error; + MonoCustomAttrInfo *result = mono_custom_attrs_from_assembly_checked (assembly, FALSE, &error); + mono_error_cleanup (&error); + return result; +} + +MonoCustomAttrInfo* +mono_custom_attrs_from_assembly_checked (MonoAssembly *assembly, gboolean ignore_missing, MonoError *error) +{ + guint32 idx; + + error_init (error); + + if (image_is_dynamic (assembly->image)) + return lookup_custom_attr (assembly->image, assembly); + idx = 1; /* there is only one assembly */ + idx <<= MONO_CUSTOM_ATTR_BITS; + idx |= MONO_CUSTOM_ATTR_ASSEMBLY; + return mono_custom_attrs_from_index_checked (assembly->image, idx, ignore_missing, error); +} + +static MonoCustomAttrInfo* +mono_custom_attrs_from_module (MonoImage *image, MonoError *error) +{ + guint32 idx; + + error_init (error); + + if (image_is_dynamic (image)) + return lookup_custom_attr (image, image); + idx = 1; /* there is only one module */ + idx <<= MONO_CUSTOM_ATTR_BITS; + idx |= MONO_CUSTOM_ATTR_MODULE; + return mono_custom_attrs_from_index_checked (image, idx, FALSE, error); +} + +/** + * mono_custom_attrs_from_property: + */ +MonoCustomAttrInfo* +mono_custom_attrs_from_property (MonoClass *klass, MonoProperty *property) +{ + MonoError error; + MonoCustomAttrInfo * result = mono_custom_attrs_from_property_checked (klass, property, &error); + mono_error_cleanup (&error); + return result; +} + +MonoCustomAttrInfo* +mono_custom_attrs_from_property_checked (MonoClass *klass, MonoProperty *property, MonoError *error) +{ + guint32 idx; + + error_init (error); + + if (image_is_dynamic (klass->image)) { + property = mono_metadata_get_corresponding_property_from_generic_type_definition (property); + return lookup_custom_attr (klass->image, property); + } + idx = find_property_index (klass, property); + idx <<= MONO_CUSTOM_ATTR_BITS; + idx |= MONO_CUSTOM_ATTR_PROPERTY; + return mono_custom_attrs_from_index_checked (klass->image, idx, FALSE, error); +} + +/** + * mono_custom_attrs_from_event: + */ +MonoCustomAttrInfo* +mono_custom_attrs_from_event (MonoClass *klass, MonoEvent *event) +{ + MonoError error; + MonoCustomAttrInfo * result = mono_custom_attrs_from_event_checked (klass, event, &error); + mono_error_cleanup (&error); + return result; +} + +MonoCustomAttrInfo* +mono_custom_attrs_from_event_checked (MonoClass *klass, MonoEvent *event, MonoError *error) +{ + guint32 idx; + + error_init (error); + + if (image_is_dynamic (klass->image)) { + event = mono_metadata_get_corresponding_event_from_generic_type_definition (event); + return lookup_custom_attr (klass->image, event); + } + idx = find_event_index (klass, event); + idx <<= MONO_CUSTOM_ATTR_BITS; + idx |= MONO_CUSTOM_ATTR_EVENT; + return mono_custom_attrs_from_index_checked (klass->image, idx, FALSE, error); +} + +/** + * mono_custom_attrs_from_field: + */ +MonoCustomAttrInfo* +mono_custom_attrs_from_field (MonoClass *klass, MonoClassField *field) +{ + MonoError error; + MonoCustomAttrInfo * result = mono_custom_attrs_from_field_checked (klass, field, &error); + mono_error_cleanup (&error); + return result; +} + +MonoCustomAttrInfo* +mono_custom_attrs_from_field_checked (MonoClass *klass, MonoClassField *field, MonoError *error) +{ + guint32 idx; + error_init (error); + + if (image_is_dynamic (klass->image)) { + field = mono_metadata_get_corresponding_field_from_generic_type_definition (field); + return lookup_custom_attr (klass->image, field); + } + idx = find_field_index (klass, field); + idx <<= MONO_CUSTOM_ATTR_BITS; + idx |= MONO_CUSTOM_ATTR_FIELDDEF; + return mono_custom_attrs_from_index_checked (klass->image, idx, FALSE, error); +} + +/** + * mono_custom_attrs_from_param: + * \param method handle to the method that we want to retrieve custom parameter information from + * \param param parameter number, where zero represent the return value, and one is the first parameter in the method + * + * The result must be released with mono_custom_attrs_free(). + * + * \returns the custom attribute object for the specified parameter, or NULL if there are none. + */ +MonoCustomAttrInfo* +mono_custom_attrs_from_param (MonoMethod *method, guint32 param) +{ + MonoError error; + MonoCustomAttrInfo *result = mono_custom_attrs_from_param_checked (method, param, &error); + mono_error_cleanup (&error); + return result; +} + +/** + * mono_custom_attrs_from_param_checked: + * \param method handle to the method that we want to retrieve custom parameter information from + * \param param parameter number, where zero represent the return value, and one is the first parameter in the method + * \param error set on error + * + * The result must be released with mono_custom_attrs_free(). + * + * \returns the custom attribute object for the specified parameter, or NULL if there are none. On failure returns NULL and sets \p error. + */ +MonoCustomAttrInfo* +mono_custom_attrs_from_param_checked (MonoMethod *method, guint32 param, MonoError *error) +{ + MonoTableInfo *ca; + guint32 i, idx, method_index; + guint32 param_list, param_last, param_pos, found; + MonoImage *image; + MonoReflectionMethodAux *aux; + + error_init (error); + + /* + * An instantiated method has the same cattrs as the generic method definition. + * + * LAMESPEC: The .NET SRE throws an exception for instantiations of generic method builders + * Note that this stanza is not necessary for non-SRE types, but it's a micro-optimization + */ + if (method->is_inflated) + method = ((MonoMethodInflated *) method)->declaring; + + if (image_is_dynamic (method->klass->image)) { + MonoCustomAttrInfo *res, *ainfo; + int size; + + aux = (MonoReflectionMethodAux *)g_hash_table_lookup (((MonoDynamicImage*)method->klass->image)->method_aux_hash, method); + if (!aux || !aux->param_cattr) + return NULL; + + /* Need to copy since it will be freed later */ + ainfo = aux->param_cattr [param]; + if (!ainfo) + return NULL; + size = MONO_SIZEOF_CUSTOM_ATTR_INFO + sizeof (MonoCustomAttrEntry) * ainfo->num_attrs; + res = (MonoCustomAttrInfo *)g_malloc0 (size); + memcpy (res, ainfo, size); + return res; + } + + image = method->klass->image; + method_index = mono_method_get_index (method); + if (!method_index) + return NULL; + ca = &image->tables [MONO_TABLE_METHOD]; + + param_list = mono_metadata_decode_row_col (ca, method_index - 1, MONO_METHOD_PARAMLIST); + if (method_index == ca->rows) { + ca = &image->tables [MONO_TABLE_PARAM]; + param_last = ca->rows + 1; + } else { + param_last = mono_metadata_decode_row_col (ca, method_index, MONO_METHOD_PARAMLIST); + ca = &image->tables [MONO_TABLE_PARAM]; + } + found = FALSE; + for (i = param_list; i < param_last; ++i) { + param_pos = mono_metadata_decode_row_col (ca, i - 1, MONO_PARAM_SEQUENCE); + if (param_pos == param) { + found = TRUE; + break; + } + } + if (!found) + return NULL; + idx = i; + idx <<= MONO_CUSTOM_ATTR_BITS; + idx |= MONO_CUSTOM_ATTR_PARAMDEF; + return mono_custom_attrs_from_index_checked (image, idx, FALSE, error); +} + +/** + * mono_custom_attrs_has_attr: + */ +gboolean +mono_custom_attrs_has_attr (MonoCustomAttrInfo *ainfo, MonoClass *attr_klass) +{ + int i; + for (i = 0; i < ainfo->num_attrs; ++i) { + MonoCustomAttrEntry *centry = &ainfo->attrs[i]; + if (centry->ctor == NULL) + continue; + MonoClass *klass = centry->ctor->klass; + if (klass == attr_klass || mono_class_has_parent (klass, attr_klass) || (MONO_CLASS_IS_INTERFACE (attr_klass) && mono_class_is_assignable_from (attr_klass, klass))) + return TRUE; + } + return FALSE; +} + +/** + * mono_custom_attrs_get_attr: + */ +MonoObject* +mono_custom_attrs_get_attr (MonoCustomAttrInfo *ainfo, MonoClass *attr_klass) +{ + MonoError error; + MonoObject *res = mono_custom_attrs_get_attr_checked (ainfo, attr_klass, &error); + mono_error_assert_ok (&error); /*FIXME proper error handling*/ + return res; +} + +MonoObject* +mono_custom_attrs_get_attr_checked (MonoCustomAttrInfo *ainfo, MonoClass *attr_klass, MonoError *error) +{ + int i; + MonoCustomAttrEntry *centry = NULL; + + g_assert (attr_klass != NULL); + + error_init (error); + + for (i = 0; i < ainfo->num_attrs; ++i) { + centry = &ainfo->attrs[i]; + if (centry->ctor == NULL) + continue; + MonoClass *klass = centry->ctor->klass; + if (attr_klass == klass || mono_class_is_assignable_from (attr_klass, klass)) + break; + } + if (centry == NULL) + return NULL; + + return create_custom_attr (ainfo->image, centry->ctor, centry->data, centry->data_size, error); +} + +/** + * mono_reflection_get_custom_attrs_info: + * \param obj a reflection object handle + * + * \returns the custom attribute info for attributes defined for the + * reflection handle \p obj. The objects. + * + * FIXME this function leaks like a sieve for SRE objects. + */ +MonoCustomAttrInfo* +mono_reflection_get_custom_attrs_info (MonoObject *obj_raw) +{ + HANDLE_FUNCTION_ENTER (); + MonoError error; + MONO_HANDLE_DCL (MonoObject, obj); + MonoCustomAttrInfo *result = mono_reflection_get_custom_attrs_info_checked (obj, &error); + mono_error_assert_ok (&error); + HANDLE_FUNCTION_RETURN_VAL (result); +} + +/** + * mono_reflection_get_custom_attrs_info_checked: + * \param obj a reflection object handle + * \param error set on error + * + * \returns the custom attribute info for attributes defined for the + * reflection handle \p obj. The objects. On failure returns NULL and sets \p error. + * + * FIXME this function leaks like a sieve for SRE objects. + */ +MonoCustomAttrInfo* +mono_reflection_get_custom_attrs_info_checked (MonoObjectHandle obj, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + MonoClass *klass; + MonoCustomAttrInfo *cinfo = NULL; + + error_init (error); + + klass = mono_handle_class (obj); + if (klass == mono_defaults.runtimetype_class) { + MonoType *type = mono_reflection_type_handle_mono_type (MONO_HANDLE_CAST(MonoReflectionType, obj), error); + goto_if_nok (error, leave); + klass = mono_class_from_mono_type (type); + /*We cannot mono_class_init the class from which we'll load the custom attributes since this must work with broken types.*/ + cinfo = mono_custom_attrs_from_class_checked (klass, error); + goto_if_nok (error, leave); + } else if (strcmp ("Assembly", klass->name) == 0 || strcmp ("MonoAssembly", klass->name) == 0) { + MonoReflectionAssemblyHandle rassembly = MONO_HANDLE_CAST (MonoReflectionAssembly, obj); + cinfo = mono_custom_attrs_from_assembly_checked (MONO_HANDLE_GETVAL (rassembly, assembly), FALSE, error); + goto_if_nok (error, leave); + } else if (strcmp ("Module", klass->name) == 0 || strcmp ("MonoModule", klass->name) == 0) { + MonoReflectionModuleHandle module = MONO_HANDLE_CAST (MonoReflectionModule, obj); + cinfo = mono_custom_attrs_from_module (MONO_HANDLE_GETVAL (module, image), error); + goto_if_nok (error, leave); + } else if (strcmp ("MonoProperty", klass->name) == 0) { + MonoReflectionPropertyHandle rprop = MONO_HANDLE_CAST (MonoReflectionProperty, obj); + MonoProperty *property = MONO_HANDLE_GETVAL (rprop, property); + cinfo = mono_custom_attrs_from_property_checked (property->parent, property, error); + goto_if_nok (error, leave); + } else if (strcmp ("MonoEvent", klass->name) == 0) { + MonoReflectionMonoEventHandle revent = MONO_HANDLE_CAST (MonoReflectionMonoEvent, obj); + MonoEvent *event = MONO_HANDLE_GETVAL (revent, event); + cinfo = mono_custom_attrs_from_event_checked (event->parent, event, error); + goto_if_nok (error, leave); + } else if (strcmp ("MonoField", klass->name) == 0) { + MonoReflectionFieldHandle rfield = MONO_HANDLE_CAST (MonoReflectionField, obj); + MonoClassField *field = MONO_HANDLE_GETVAL (rfield, field); + cinfo = mono_custom_attrs_from_field_checked (field->parent, field, error); + goto_if_nok (error, leave); + } else if ((strcmp ("MonoMethod", klass->name) == 0) || (strcmp ("MonoCMethod", klass->name) == 0)) { + MonoReflectionMethodHandle rmethod = MONO_HANDLE_CAST (MonoReflectionMethod, obj); + cinfo = mono_custom_attrs_from_method_checked (MONO_HANDLE_GETVAL (rmethod, method), error); + goto_if_nok (error, leave); + } else if (strcmp ("ParameterInfo", klass->name) == 0 || strcmp ("MonoParameterInfo", klass->name) == 0) { + MonoReflectionParameterHandle param = MONO_HANDLE_CAST (MonoReflectionParameter, obj); + MonoObjectHandle member_impl = MONO_HANDLE_NEW_GET (MonoObject, param, MemberImpl); + MonoClass *member_class = mono_handle_class (member_impl); + if (mono_class_is_reflection_method_or_constructor (member_class)) { + MonoReflectionMethodHandle rmethod = MONO_HANDLE_CAST (MonoReflectionMethod, member_impl); + cinfo = mono_custom_attrs_from_param_checked (MONO_HANDLE_GETVAL (rmethod, method), MONO_HANDLE_GETVAL (param, PositionImpl) + 1, error); + goto_if_nok (error, leave); + } else if (mono_is_sr_mono_property (member_class)) { + MonoReflectionPropertyHandle prop = MONO_HANDLE_CAST (MonoReflectionProperty, member_impl); + MonoProperty *property = MONO_HANDLE_GETVAL (prop, property); + MonoMethod *method; + if (!(method = property->get)) + method = property->set; + g_assert (method); + + cinfo = mono_custom_attrs_from_param_checked (method, MONO_HANDLE_GETVAL (param, PositionImpl) + 1, error); + goto_if_nok (error, leave); + } +#ifndef DISABLE_REFLECTION_EMIT + else if (mono_is_sre_method_on_tb_inst (member_class)) {/*XXX This is a workaround for Compiler Context*/ + // FIXME: Is this still needed ? + g_assert_not_reached (); + } else if (mono_is_sre_ctor_on_tb_inst (member_class)) { /*XX This is a workaround for Compiler Context*/ + // FIXME: Is this still needed ? + g_assert_not_reached (); + } +#endif + else { + char *type_name = mono_type_get_full_name (member_class); + mono_error_set_not_supported (error, + "Custom attributes on a ParamInfo with member %s are not supported", + type_name); + g_free (type_name); + goto leave; + } + } else if (strcmp ("AssemblyBuilder", klass->name) == 0) { + MonoReflectionAssemblyBuilderHandle assemblyb = MONO_HANDLE_CAST (MonoReflectionAssemblyBuilder, obj); + MonoReflectionAssemblyHandle assembly = MONO_HANDLE_CAST (MonoReflectionAssembly, assemblyb); + MonoArrayHandle cattrs = MONO_HANDLE_NEW_GET (MonoArray, assemblyb, cattrs); + MonoImage * image = MONO_HANDLE_GETVAL (assembly, assembly)->image; + g_assert (image); + cinfo = mono_custom_attrs_from_builders_handle (NULL, image, cattrs); + } else if (strcmp ("TypeBuilder", klass->name) == 0) { + MonoReflectionTypeBuilderHandle tb = MONO_HANDLE_CAST (MonoReflectionTypeBuilder, obj); + MonoReflectionModuleBuilderHandle module = MONO_HANDLE_NEW_GET (MonoReflectionModuleBuilder, tb, module); + MonoDynamicImage *dynamic_image = MONO_HANDLE_GETVAL (module, dynamic_image); + MonoArrayHandle cattrs = MONO_HANDLE_NEW_GET (MonoArray, tb, cattrs); + cinfo = mono_custom_attrs_from_builders_handle (NULL, &dynamic_image->image, cattrs); + } else if (strcmp ("ModuleBuilder", klass->name) == 0) { + MonoReflectionModuleBuilderHandle mb = MONO_HANDLE_CAST (MonoReflectionModuleBuilder, obj); + MonoDynamicImage *dynamic_image = MONO_HANDLE_GETVAL (mb, dynamic_image); + MonoArrayHandle cattrs = MONO_HANDLE_NEW_GET (MonoArray, mb, cattrs); + cinfo = mono_custom_attrs_from_builders_handle (NULL, &dynamic_image->image, cattrs); + } else if (strcmp ("ConstructorBuilder", klass->name) == 0) { + MonoReflectionCtorBuilderHandle cb = MONO_HANDLE_CAST (MonoReflectionCtorBuilder, obj); + MonoMethod *mhandle = MONO_HANDLE_GETVAL (cb, mhandle); + MonoArrayHandle cattrs = MONO_HANDLE_NEW_GET (MonoArray, cb, cattrs); + cinfo = mono_custom_attrs_from_builders_handle (NULL, mhandle->klass->image, cattrs); + } else if (strcmp ("MethodBuilder", klass->name) == 0) { + MonoReflectionMethodBuilderHandle mb = MONO_HANDLE_CAST (MonoReflectionMethodBuilder, obj); + MonoMethod *mhandle = MONO_HANDLE_GETVAL (mb, mhandle); + MonoArrayHandle cattrs = MONO_HANDLE_NEW_GET (MonoArray, mb, cattrs); + cinfo = mono_custom_attrs_from_builders_handle (NULL, mhandle->klass->image, cattrs); + } else if (strcmp ("FieldBuilder", klass->name) == 0) { + MonoReflectionFieldBuilderHandle fb = MONO_HANDLE_CAST (MonoReflectionFieldBuilder, obj); + MonoReflectionTypeBuilderHandle tb = MONO_HANDLE_NEW_GET (MonoReflectionTypeBuilder, fb, typeb); + MonoReflectionModuleBuilderHandle mb = MONO_HANDLE_NEW_GET (MonoReflectionModuleBuilder, tb, module); + MonoDynamicImage *dynamic_image = MONO_HANDLE_GETVAL (mb, dynamic_image); + MonoArrayHandle cattrs = MONO_HANDLE_NEW_GET (MonoArray, fb, cattrs); + cinfo = mono_custom_attrs_from_builders_handle (NULL, &dynamic_image->image, cattrs); + } else if (strcmp ("MonoGenericClass", klass->name) == 0) { + MonoReflectionGenericClassHandle gclass = MONO_HANDLE_CAST (MonoReflectionGenericClass, obj); + MonoReflectionTypeHandle generic_type = MONO_HANDLE_NEW_GET (MonoReflectionType, gclass, generic_type); + cinfo = mono_reflection_get_custom_attrs_info_checked (MONO_HANDLE_CAST (MonoObject, generic_type), error); + goto_if_nok (error, leave); + } else { /* handle other types here... */ + g_error ("get custom attrs not yet supported for %s", klass->name); + } + +leave: + HANDLE_FUNCTION_RETURN_VAL (cinfo); +} + +/** + * mono_reflection_get_custom_attrs_by_type: + * \param obj a reflection object handle + * \returns an array with all the custom attributes defined of the + * reflection handle \p obj. If \p attr_klass is non-NULL, only custom attributes + * of that type are returned. The objects are fully build. Return NULL if a loading error + * occurs. + */ +MonoArray* +mono_reflection_get_custom_attrs_by_type (MonoObject *obj_raw, MonoClass *attr_klass, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + MONO_HANDLE_DCL (MonoObject, obj); + MonoArrayHandle result = mono_reflection_get_custom_attrs_by_type_handle (obj, attr_klass, error); + HANDLE_FUNCTION_RETURN_OBJ (result); +} + +MonoArrayHandle +mono_reflection_get_custom_attrs_by_type_handle (MonoObjectHandle obj, MonoClass *attr_klass, MonoError *error) +{ + MonoArrayHandle result = MONO_HANDLE_NEW (MonoArray, NULL); + MonoCustomAttrInfo *cinfo; + + error_init (error); + + cinfo = mono_reflection_get_custom_attrs_info_checked (obj, error); + goto_if_nok (error, leave); + if (cinfo) { + MONO_HANDLE_ASSIGN (result, MONO_HANDLE_NEW (MonoArray, mono_custom_attrs_construct_by_type (cinfo, attr_klass, error))); /* FIXME use coop handles for mono_custom_attrs_construct_by_type */ + if (!cinfo->cached) + mono_custom_attrs_free (cinfo); + if (!result) + goto leave; + } else { + MONO_HANDLE_ASSIGN (result, mono_array_new_handle (mono_domain_get (), mono_defaults.attribute_class, 0, error)); + } + +leave: + return result; +} + +/** + * mono_reflection_get_custom_attrs: + * \param obj a reflection object handle + * \return an array with all the custom attributes defined of the + * reflection handle \p obj. The objects are fully build. Return NULL if a loading error + * occurs. + */ +MonoArray* +mono_reflection_get_custom_attrs (MonoObject *obj_raw) +{ + HANDLE_FUNCTION_ENTER (); + MonoError error; + MONO_HANDLE_DCL (MonoObject, obj); + MonoArrayHandle result = mono_reflection_get_custom_attrs_by_type_handle (obj, NULL, &error); + mono_error_cleanup (&error); + HANDLE_FUNCTION_RETURN_OBJ (result); +} + +/** + * mono_reflection_get_custom_attrs_data: + * \param obj a reflection obj handle + * \returns an array of \c System.Reflection.CustomAttributeData, + * which include information about attributes reflected on + * types loaded using the Reflection Only methods + */ +MonoArray* +mono_reflection_get_custom_attrs_data (MonoObject *obj_raw) +{ + HANDLE_FUNCTION_ENTER (); + MonoError error; + MONO_HANDLE_DCL (MonoObject, obj); + MonoArrayHandle result = mono_reflection_get_custom_attrs_data_checked (obj, &error); + mono_error_cleanup (&error); + HANDLE_FUNCTION_RETURN_OBJ (result); +} + +/* + * mono_reflection_get_custom_attrs_data_checked: + * @obj: a reflection obj handle + * @error: set on error + * + * Returns an array of System.Reflection.CustomAttributeData, + * which include information about attributes reflected on + * types loaded using the Reflection Only methods + */ +MonoArrayHandle +mono_reflection_get_custom_attrs_data_checked (MonoObjectHandle obj, MonoError *error) +{ + MonoArrayHandle result = MONO_HANDLE_NEW (MonoArray, NULL); + MonoCustomAttrInfo *cinfo; + + error_init (error); + + cinfo = mono_reflection_get_custom_attrs_info_checked (obj, error); + goto_if_nok (error, leave); + if (cinfo) { + MONO_HANDLE_ASSIGN (result, MONO_HANDLE_NEW (MonoArray, mono_custom_attrs_data_construct (cinfo, error))); /* FIXME use coop handles in mono_custom_attrs_data_construct */ + if (!cinfo->cached) + mono_custom_attrs_free (cinfo); + goto_if_nok (error, leave); + } else + MONO_HANDLE_ASSIGN (result, mono_array_new_handle (mono_domain_get (), mono_defaults.customattribute_data_class, 0, error)); + +leave: + return result; +} + +static gboolean +custom_attr_class_name_from_methoddef (MonoImage *image, guint32 method_token, const gchar **nspace, const gchar **class_name) +{ + /* mono_get_method_from_token () */ + g_assert (mono_metadata_token_table (method_token) == MONO_TABLE_METHOD); + guint32 type_token = mono_metadata_typedef_from_method (image, method_token); + if (!type_token) { + /* Bad method token (could not find corresponding typedef) */ + return FALSE; + } + type_token |= MONO_TOKEN_TYPE_DEF; + { + /* mono_class_create_from_typedef () */ + MonoTableInfo *tt = &image->tables [MONO_TABLE_TYPEDEF]; + guint32 cols [MONO_TYPEDEF_SIZE]; + guint tidx = mono_metadata_token_index (type_token); + + if (mono_metadata_token_table (type_token) != MONO_TABLE_TYPEDEF || tidx > tt->rows) { + /* "Invalid typedef token %x", type_token */ + return FALSE; + } + + mono_metadata_decode_row (tt, tidx - 1, cols, MONO_TYPEDEF_SIZE); + + if (class_name) + *class_name = mono_metadata_string_heap (image, cols [MONO_TYPEDEF_NAME]); + if (nspace) + *nspace = mono_metadata_string_heap (image, cols [MONO_TYPEDEF_NAMESPACE]); + return TRUE; + } +} + + +/** + * custom_attr_class_name_from_method_token: + * @image: The MonoImage + * @method_token: a token for a custom attr constructor in @image + * @assembly_token: out argment set to the assembly ref token of the custom attr + * @nspace: out argument set to namespace (a string in the string heap of @image) of the custom attr + * @class_name: out argument set to the class name of the custom attr. + * + * Given an @image and a @method_token (which is assumed to be a + * constructor), fills in the out arguments with the assembly ref (if + * a methodref) and the namespace and class name of the custom + * attribute. + * + * Returns: TRUE on success, FALSE otherwise. + * + * LOCKING: does not take locks + */ +static gboolean +custom_attr_class_name_from_method_token (MonoImage *image, guint32 method_token, guint32 *assembly_token, const gchar **nspace, const gchar **class_name) +{ + /* This only works with method tokens constructed from a + * custom attr token, which can only be methoddef or + * memberref */ + g_assert (mono_metadata_token_table (method_token) == MONO_TABLE_METHOD + || mono_metadata_token_table (method_token) == MONO_TABLE_MEMBERREF); + + if (mono_metadata_token_table (method_token) == MONO_TABLE_MEMBERREF) { + /* method_from_memberref () */ + guint32 cols[6]; + guint32 nindex, class_index; + + int idx = mono_metadata_token_index (method_token); + + mono_metadata_decode_row (&image->tables [MONO_TABLE_MEMBERREF], idx-1, cols, 3); + nindex = cols [MONO_MEMBERREF_CLASS] >> MONO_MEMBERREF_PARENT_BITS; + class_index = cols [MONO_MEMBERREF_CLASS] & MONO_MEMBERREF_PARENT_MASK; + if (class_index == MONO_MEMBERREF_PARENT_TYPEREF) { + guint32 type_token = MONO_TOKEN_TYPE_REF | nindex; + /* mono_class_from_typeref_checked () */ + { + guint32 cols [MONO_TYPEREF_SIZE]; + MonoTableInfo *t = &image->tables [MONO_TABLE_TYPEREF]; + + mono_metadata_decode_row (t, (type_token&0xffffff)-1, cols, MONO_TYPEREF_SIZE); + + if (class_name) + *class_name = mono_metadata_string_heap (image, cols [MONO_TYPEREF_NAME]); + if (nspace) + *nspace = mono_metadata_string_heap (image, cols [MONO_TYPEREF_NAMESPACE]); + if (assembly_token) + *assembly_token = cols [MONO_TYPEREF_SCOPE]; + return TRUE; + } + } else if (class_index == MONO_MEMBERREF_PARENT_METHODDEF) { + guint32 methoddef_token = MONO_TOKEN_METHOD_DEF | nindex; + if (assembly_token) + *assembly_token = 0; + return custom_attr_class_name_from_methoddef (image, methoddef_token, nspace, class_name); + } else { + /* Attributes can't be generic, so it won't be + * a typespec, and they're always + * constructors, so it won't be a moduleref */ + g_assert_not_reached (); + } + } else { + /* must be MONO_TABLE_METHOD */ + if (assembly_token) + *assembly_token = 0; + return custom_attr_class_name_from_methoddef (image, method_token, nspace, class_name); + } +} + +/** + * mono_assembly_metadata_foreach_custom_attr: + * \param assembly the assembly to iterate over + * \param func the function to call for each custom attribute + * \param user_data passed to \p func + * Calls \p func for each custom attribute type on the given assembly until \p func returns TRUE. + * Everything is done using low-level metadata APIs, so it is safe to use during assembly loading. + */ +void +mono_assembly_metadata_foreach_custom_attr (MonoAssembly *assembly, MonoAssemblyMetadataCustomAttrIterFunc func, gpointer user_data) +{ + MonoImage *image; + guint32 mtoken, i; + guint32 cols [MONO_CUSTOM_ATTR_SIZE]; + MonoTableInfo *ca; + guint32 idx; + + /* + * This might be called during assembly loading, so do everything using the low-level + * metadata APIs. + */ + + image = assembly->image; + /* Dynamic images would need to go through the AssemblyBuilder's + * CustomAttributeBuilder array. Going through the tables below + * definitely won't work. */ + g_assert (!image_is_dynamic (image)); + idx = 1; /* there is only one assembly */ + idx <<= MONO_CUSTOM_ATTR_BITS; + idx |= MONO_CUSTOM_ATTR_ASSEMBLY; + + /* Inlined from mono_custom_attrs_from_index_checked () */ + ca = &image->tables [MONO_TABLE_CUSTOMATTRIBUTE]; + i = mono_metadata_custom_attrs_from_index (image, idx); + if (!i) + return; + i --; + gboolean stop_iterating = FALSE; + while (!stop_iterating && i < ca->rows) { + if (mono_metadata_decode_row_col (ca, i, MONO_CUSTOM_ATTR_PARENT) != idx) + break; + mono_metadata_decode_row (ca, i, cols, MONO_CUSTOM_ATTR_SIZE); + i ++; + mtoken = cols [MONO_CUSTOM_ATTR_TYPE] >> MONO_CUSTOM_ATTR_TYPE_BITS; + switch (cols [MONO_CUSTOM_ATTR_TYPE] & MONO_CUSTOM_ATTR_TYPE_MASK) { + case MONO_CUSTOM_ATTR_TYPE_METHODDEF: + mtoken |= MONO_TOKEN_METHOD_DEF; + break; + case MONO_CUSTOM_ATTR_TYPE_MEMBERREF: + mtoken |= MONO_TOKEN_MEMBER_REF; + break; + default: + g_warning ("Unknown table for custom attr type %08x", cols [MONO_CUSTOM_ATTR_TYPE]); + continue; + } + + const char *nspace = NULL; + const char *name = NULL; + guint32 assembly_token = 0; + + if (!custom_attr_class_name_from_method_token (image, mtoken, &assembly_token, &nspace, &name)) + continue; + + stop_iterating = func (image, assembly_token, nspace, name, mtoken, user_data); + } +} + +static void +init_weak_fields_inner (MonoImage *image, GHashTable *indexes) +{ + MonoTableInfo *tdef; + MonoError error; + MonoClass *klass = NULL; + guint32 memberref_index = -1; + int first_method_idx = -1; + int method_count = -1; + + if (image == mono_get_corlib ()) { + /* Typedef */ + klass = mono_class_from_name_checked (image, "System", "WeakAttribute", &error); + if (!is_ok (&error)) { + mono_error_cleanup (&error); + return; + } + if (!klass) + return; + first_method_idx = mono_class_get_first_method_idx (klass); + method_count = mono_class_get_method_count (klass); + + tdef = &image->tables [MONO_TABLE_CUSTOMATTRIBUTE]; + guint32 parent, field_idx, col, mtoken, idx; + for (int i = 0; i < tdef->rows; ++i) { + parent = mono_metadata_decode_row_col (tdef, i, MONO_CUSTOM_ATTR_PARENT); + if ((parent & MONO_CUSTOM_ATTR_MASK) != MONO_CUSTOM_ATTR_FIELDDEF) + continue; + + col = mono_metadata_decode_row_col (tdef, i, MONO_CUSTOM_ATTR_TYPE); + mtoken = col >> MONO_CUSTOM_ATTR_TYPE_BITS; + /* 1 based index */ + idx = mtoken - 1; + if ((col & MONO_CUSTOM_ATTR_TYPE_MASK) == MONO_CUSTOM_ATTR_TYPE_METHODDEF) { + field_idx = parent >> MONO_CUSTOM_ATTR_BITS; + if (idx >= first_method_idx && idx < first_method_idx + method_count) + g_hash_table_insert (indexes, GUINT_TO_POINTER (field_idx), GUINT_TO_POINTER (1)); + } + } + } else { + /* Memberref pointing to a typeref */ + tdef = &image->tables [MONO_TABLE_MEMBERREF]; + + /* Check whenever the assembly references the WeakAttribute type */ + gboolean found = FALSE; + tdef = &image->tables [MONO_TABLE_TYPEREF]; + for (int i = 0; i < tdef->rows; ++i) { + guint32 string_offset = mono_metadata_decode_row_col (tdef, i, MONO_TYPEREF_NAME); + const char *name = mono_metadata_string_heap (image, string_offset); + if (!strcmp (name, "WeakAttribute")) { + found = TRUE; + break; + } + } + + if (!found) + return; + + /* Find the memberref pointing to a typeref */ + tdef = &image->tables [MONO_TABLE_MEMBERREF]; + for (int i = 0; i < tdef->rows; ++i) { + guint32 cols [MONO_MEMBERREF_SIZE]; + const char *sig; + + mono_metadata_decode_row (tdef, i, cols, MONO_MEMBERREF_SIZE); + sig = mono_metadata_blob_heap (image, cols [MONO_MEMBERREF_SIGNATURE]); + mono_metadata_decode_blob_size (sig, &sig); + + guint32 nindex = cols [MONO_MEMBERREF_CLASS] >> MONO_MEMBERREF_PARENT_BITS; + guint32 class_index = cols [MONO_MEMBERREF_CLASS] & MONO_MEMBERREF_PARENT_MASK; + const char *fname = mono_metadata_string_heap (image, cols [MONO_MEMBERREF_NAME]); + + if (!strcmp (fname, ".ctor") && class_index == MONO_MEMBERREF_PARENT_TYPEREF) { + MonoTableInfo *typeref_table = &image->tables [MONO_TABLE_TYPEREF]; + guint32 cols [MONO_TYPEREF_SIZE]; + + mono_metadata_decode_row (typeref_table, nindex - 1, cols, MONO_TYPEREF_SIZE); + + const char *name = mono_metadata_string_heap (image, cols [MONO_TYPEREF_NAME]); + const char *nspace = mono_metadata_string_heap (image, cols [MONO_TYPEREF_NAMESPACE]); + + if (!strcmp (nspace, "System") && !strcmp (name, "WeakAttribute")) { + MonoClass *klass = mono_class_from_typeref (image, MONO_TOKEN_TYPE_REF | nindex); + g_assert (!strcmp (klass->name, "WeakAttribute")); + /* Allow a testing dll as well since some profiles don't have WeakAttribute */ + if (klass && (klass->image == mono_get_corlib () || strstr (klass->image->name, "Mono.Runtime.Testing"))) { + /* Sanity check that it only has 1 ctor */ + gpointer iter = NULL; + int count = 0; + MonoMethod *method; + while ((method = mono_class_get_methods (klass, &iter))) { + if (!strcmp (method->name, ".ctor")) + count ++; + } + count ++; + memberref_index = i; + break; + } + } + } + } + if (memberref_index == -1) + return; + + tdef = &image->tables [MONO_TABLE_CUSTOMATTRIBUTE]; + guint32 parent, field_idx, col, mtoken, idx; + for (int i = 0; i < tdef->rows; ++i) { + parent = mono_metadata_decode_row_col (tdef, i, MONO_CUSTOM_ATTR_PARENT); + if ((parent & MONO_CUSTOM_ATTR_MASK) != MONO_CUSTOM_ATTR_FIELDDEF) + continue; + + col = mono_metadata_decode_row_col (tdef, i, MONO_CUSTOM_ATTR_TYPE); + mtoken = col >> MONO_CUSTOM_ATTR_TYPE_BITS; + /* 1 based index */ + idx = mtoken - 1; + field_idx = parent >> MONO_CUSTOM_ATTR_BITS; + if ((col & MONO_CUSTOM_ATTR_TYPE_MASK) == MONO_CUSTOM_ATTR_TYPE_MEMBERREF) { + if (idx == memberref_index) + g_hash_table_insert (indexes, GUINT_TO_POINTER (field_idx), GUINT_TO_POINTER (1)); + } + } + } +} + +/* + * mono_assembly_init_weak_fields: + * + * Initialize the image->weak_field_indexes hash. + */ +void +mono_assembly_init_weak_fields (MonoImage *image) +{ + if (image->weak_fields_inited) + return; + + GHashTable *indexes = NULL; + + if (mono_get_runtime_callbacks ()->get_weak_field_indexes) + indexes = mono_get_runtime_callbacks ()->get_weak_field_indexes (image); + if (!indexes) { + indexes = g_hash_table_new (NULL, NULL); + + /* + * To avoid lookups for every field, we scan the customattr table for entries whose + * parent is a field and whose type is WeakAttribute. + */ + init_weak_fields_inner (image, indexes); + } + + mono_image_lock (image); + if (!image->weak_fields_inited) { + image->weak_field_indexes = indexes; + mono_memory_barrier (); + image->weak_fields_inited = TRUE; + } else { + g_hash_table_destroy (indexes); + } + mono_image_unlock (image); +} + +/* + * mono_assembly_is_weak_field: + * + * Return whenever the FIELD table entry with the 1-based index FIELD_IDX has + * a [Weak] attribute. + */ +gboolean +mono_assembly_is_weak_field (MonoImage *image, guint32 field_idx) +{ + if (image->dynamic) + return FALSE; + + mono_assembly_init_weak_fields (image); + + /* The hash is not mutated, no need to lock */ + return g_hash_table_lookup (image->weak_field_indexes, GINT_TO_POINTER (field_idx)) != NULL; +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/debug-helpers.c b/unity-2019.4.24f1-mbe/mono/metadata/debug-helpers.c new file mode 100644 index 000000000..1e657e43a --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/debug-helpers.c @@ -0,0 +1,1269 @@ +/** + * \file + * + * Author: + * Mono Project (http://www.mono-project.com) + * + * Copyright (C) 2005-2008 Novell, Inc. (http://www.novell.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#include +#include "mono/metadata/tokentype.h" +#include "mono/metadata/opcodes.h" +#include "mono/metadata/metadata-internals.h" +#include "mono/metadata/class-internals.h" +#include "mono/metadata/object-internals.h" +#include "mono/metadata/mono-endian.h" +#include "mono/metadata/debug-helpers.h" +#include "mono/metadata/tabledefs.h" +#include "mono/metadata/appdomain.h" + +struct MonoMethodDesc { + char *name_space; + char *klass; + char *name; + char *args; + guint num_args; + gboolean include_namespace, klass_glob, name_glob; +}; + +#ifdef HAVE_ARRAY_ELEM_INIT +#define MSGSTRFIELD(line) MSGSTRFIELD1(line) +#define MSGSTRFIELD1(line) str##line +static const struct msgstr_t { +#define WRAPPER(a,b) char MSGSTRFIELD(__LINE__) [sizeof (b)]; +#include "wrapper-types.h" +#undef WRAPPER +} opstr = { +#define WRAPPER(a,b) b, +#include "wrapper-types.h" +#undef WRAPPER +}; +static const gint16 opidx [] = { +#define WRAPPER(a,b) [MONO_WRAPPER_ ## a] = offsetof (struct msgstr_t, MSGSTRFIELD(__LINE__)), +#include "wrapper-types.h" +#undef WRAPPER +}; + +static const char* +wrapper_type_to_str (guint32 wrapper_type) +{ + g_assert (wrapper_type < MONO_WRAPPER_NUM); + + return (const char*)&opstr + opidx [wrapper_type]; +} + +#else +#define WRAPPER(a,b) b, +static const char* const +wrapper_type_names [MONO_WRAPPER_NUM + 1] = { +#include "wrapper-types.h" + NULL +}; + +static const char* +wrapper_type_to_str (guint32 wrapper_type) +{ + g_assert (wrapper_type < MONO_WRAPPER_NUM); + + return wrapper_type_names [wrapper_type]; +} + +#endif + +static void +append_class_name (GString *res, MonoClass *klass, gboolean include_namespace) +{ + if (!klass) { + g_string_append (res, "Unknown"); + return; + } + if (klass->nested_in) { + append_class_name (res, klass->nested_in, include_namespace); + g_string_append_c (res, '/'); + } + if (include_namespace && *(klass->name_space)) { + g_string_append (res, klass->name_space); + g_string_append_c (res, '.'); + } + g_string_append (res, klass->name); +} + +static MonoClass* +find_system_class (const char *name) +{ + if (!strcmp (name, "void")) + return mono_defaults.void_class; + else if (!strcmp (name, "char")) return mono_defaults.char_class; + else if (!strcmp (name, "bool")) return mono_defaults.boolean_class; + else if (!strcmp (name, "byte")) return mono_defaults.byte_class; + else if (!strcmp (name, "sbyte")) return mono_defaults.sbyte_class; + else if (!strcmp (name, "uint16")) return mono_defaults.uint16_class; + else if (!strcmp (name, "int16")) return mono_defaults.int16_class; + else if (!strcmp (name, "uint")) return mono_defaults.uint32_class; + else if (!strcmp (name, "int")) return mono_defaults.int32_class; + else if (!strcmp (name, "ulong")) return mono_defaults.uint64_class; + else if (!strcmp (name, "long")) return mono_defaults.int64_class; + else if (!strcmp (name, "uintptr")) return mono_defaults.uint_class; + else if (!strcmp (name, "intptr")) return mono_defaults.int_class; + else if (!strcmp (name, "single")) return mono_defaults.single_class; + else if (!strcmp (name, "double")) return mono_defaults.double_class; + else if (!strcmp (name, "string")) return mono_defaults.string_class; + else if (!strcmp (name, "object")) return mono_defaults.object_class; + else + return NULL; +} + +void +mono_type_get_desc (GString *res, MonoType *type, gboolean include_namespace) +{ + int i; + + switch (type->type) { + case MONO_TYPE_VOID: + g_string_append (res, "void"); break; + case MONO_TYPE_CHAR: + g_string_append (res, "char"); break; + case MONO_TYPE_BOOLEAN: + g_string_append (res, "bool"); break; + case MONO_TYPE_U1: + g_string_append (res, "byte"); break; + case MONO_TYPE_I1: + g_string_append (res, "sbyte"); break; + case MONO_TYPE_U2: + g_string_append (res, "uint16"); break; + case MONO_TYPE_I2: + g_string_append (res, "int16"); break; + case MONO_TYPE_U4: + g_string_append (res, "uint"); break; + case MONO_TYPE_I4: + g_string_append (res, "int"); break; + case MONO_TYPE_U8: + g_string_append (res, "ulong"); break; + case MONO_TYPE_I8: + g_string_append (res, "long"); break; + case MONO_TYPE_FNPTR: /* who cares for the exact signature? */ + g_string_append (res, "*()"); break; + case MONO_TYPE_U: + g_string_append (res, "uintptr"); break; + case MONO_TYPE_I: + g_string_append (res, "intptr"); break; + case MONO_TYPE_R4: + g_string_append (res, "single"); break; + case MONO_TYPE_R8: + g_string_append (res, "double"); break; + case MONO_TYPE_STRING: + g_string_append (res, "string"); break; + case MONO_TYPE_OBJECT: + g_string_append (res, "object"); break; + case MONO_TYPE_PTR: + mono_type_get_desc (res, type->data.type, include_namespace); + g_string_append_c (res, '*'); + break; + case MONO_TYPE_ARRAY: + mono_type_get_desc (res, &type->data.array->eklass->byval_arg, include_namespace); + g_string_append_c (res, '['); + for (i = 1; i < type->data.array->rank; ++i) + g_string_append_c (res, ','); + g_string_append_c (res, ']'); + break; + case MONO_TYPE_SZARRAY: + mono_type_get_desc (res, &type->data.klass->byval_arg, include_namespace); + g_string_append (res, "[]"); + break; + case MONO_TYPE_CLASS: + case MONO_TYPE_VALUETYPE: + append_class_name (res, type->data.klass, include_namespace); + break; + case MONO_TYPE_GENERICINST: { + MonoGenericContext *context; + + mono_type_get_desc (res, &type->data.generic_class->container_class->byval_arg, include_namespace); + g_string_append (res, "<"); + context = &type->data.generic_class->context; + if (context->class_inst) { + for (i = 0; i < context->class_inst->type_argc; ++i) { + if (i > 0) + g_string_append (res, ", "); + mono_type_get_desc (res, context->class_inst->type_argv [i], include_namespace); + } + } + if (context->method_inst) { + if (context->class_inst) + g_string_append (res, "; "); + for (i = 0; i < context->method_inst->type_argc; ++i) { + if (i > 0) + g_string_append (res, ", "); + mono_type_get_desc (res, context->method_inst->type_argv [i], include_namespace); + } + } + g_string_append (res, ">"); + break; + } + case MONO_TYPE_VAR: + case MONO_TYPE_MVAR: + if (type->data.generic_param) { + const char *name = mono_generic_param_name (type->data.generic_param); + if (name) + g_string_append (res, name); + else + g_string_append_printf (res, "%s%d", type->type == MONO_TYPE_VAR ? "!" : "!!", mono_generic_param_num (type->data.generic_param)); + } else { + g_string_append (res, ""); + } + break; + case MONO_TYPE_TYPEDBYREF: + g_string_append (res, "typedbyref"); + break; + default: + break; + } + if (type->byref) + g_string_append_c (res, '&'); +} + +/** + * mono_type_full_name: + */ +char* +mono_type_full_name (MonoType *type) +{ + GString *str; + + str = g_string_new (""); + mono_type_get_desc (str, type, TRUE); + return g_string_free (str, FALSE); +} + +/** + * mono_signature_get_desc: + */ +char* +mono_signature_get_desc (MonoMethodSignature *sig, gboolean include_namespace) +{ + int i; + char *result; + GString *res; + + if (!sig) + return g_strdup (""); + + res = g_string_new (""); + + for (i = 0; i < sig->param_count; ++i) { + if (i > 0) + g_string_append_c (res, ','); + mono_type_get_desc (res, sig->params [i], include_namespace); + } + result = res->str; + g_string_free (res, FALSE); + return result; +} + +char* +mono_signature_full_name (MonoMethodSignature *sig) +{ + int i; + char *result; + GString *res; + + if (!sig) + return g_strdup (""); + + res = g_string_new (""); + + mono_type_get_desc (res, sig->ret, TRUE); + g_string_append_c (res, '('); + for (i = 0; i < sig->param_count; ++i) { + if (i > 0) + g_string_append_c (res, ','); + mono_type_get_desc (res, sig->params [i], TRUE); + } + g_string_append_c (res, ')'); + result = res->str; + g_string_free (res, FALSE); + return result; +} + +/* + * Returns a string ready to be consumed by managed code when formating a string to include class + method name. + * IE, say you have void Foo:Bar(int). It will return "void {0}(int)". + * The reason for this is that managed exception constructors for missing members require a both class and member names to be provided independently of the signature. + */ +char* +mono_signature_get_managed_fmt_string (MonoMethodSignature *sig) +{ + int i; + char *result; + GString *res; + + if (!sig) + return g_strdup (""); + + res = g_string_new (""); + + mono_type_get_desc (res, sig->ret, TRUE); + + g_string_append (res, " {0}"); + + if (sig->generic_param_count) { + g_string_append_c (res, '<'); + for (i = 0; i < sig->generic_param_count; ++i) { + if (i > 0) + g_string_append (res, ","); + g_string_append_printf (res, "!%d", i); + } + g_string_append_c (res, '>'); + } + + g_string_append_c (res, '('); + for (i = 0; i < sig->param_count; ++i) { + if (i > 0) + g_string_append_c (res, ','); + mono_type_get_desc (res, sig->params [i], TRUE); + } + g_string_append_c (res, ')'); + result = res->str; + g_string_free (res, FALSE); + return result; +} + +void +mono_ginst_get_desc (GString *str, MonoGenericInst *ginst) +{ + int i; + + for (i = 0; i < ginst->type_argc; ++i) { + if (i > 0) + g_string_append (str, ", "); + mono_type_get_desc (str, ginst->type_argv [i], TRUE); + } +} + +char* +mono_context_get_desc (MonoGenericContext *context) +{ + GString *str; + char *res; + + str = g_string_new (""); + g_string_append (str, "<"); + + if (context->class_inst) + mono_ginst_get_desc (str, context->class_inst); + if (context->method_inst) { + if (context->class_inst) + g_string_append (str, "; "); + mono_ginst_get_desc (str, context->method_inst); + } + + g_string_append (str, ">"); + res = g_strdup (str->str); + g_string_free (str, TRUE); + return res; +} + +/** + * mono_method_desc_new: + * \param name the method name. + * \param include_namespace whether the name includes a namespace or not. + * + * Creates a method description for \p name, which conforms to the following + * specification: + * + * [namespace.]classname:methodname[(args...)] + * + * in all the loaded assemblies. + * + * Both classname and methodname can contain * which matches anything. + * + * \returns a parsed representation of the method description. + */ +MonoMethodDesc* +mono_method_desc_new (const char *name, gboolean include_namespace) +{ + MonoMethodDesc *result; + char *class_name, *class_nspace, *method_name, *use_args, *end; + int use_namespace; + int generic_delim_stack; + + class_nspace = g_strdup (name); + use_args = strchr (class_nspace, '('); + if (use_args) { + /* Allow a ' ' between the method name and the signature */ + if (use_args > class_nspace && use_args [-1] == ' ') + use_args [-1] = 0; + *use_args++ = 0; + end = strchr (use_args, ')'); + if (!end) { + g_free (class_nspace); + return NULL; + } + *end = 0; + } + method_name = strrchr (class_nspace, ':'); + if (!method_name) { + g_free (class_nspace); + return NULL; + } + /* allow two :: to separate the method name */ + if (method_name != class_nspace && method_name [-1] == ':') + method_name [-1] = 0; + *method_name++ = 0; + class_name = strrchr (class_nspace, '.'); + if (class_name) { + *class_name++ = 0; + use_namespace = 1; + } else { + class_name = class_nspace; + use_namespace = 0; + } + result = g_new0 (MonoMethodDesc, 1); + result->include_namespace = include_namespace; + result->name = method_name; + result->klass = class_name; + result->name_space = use_namespace? class_nspace: NULL; + result->args = use_args? use_args: NULL; + if (strstr (result->name, "*")) + result->name_glob = TRUE; + if (strstr (result->klass, "*")) + result->klass_glob = TRUE; + if (use_args) { + end = use_args; + if (*end) + result->num_args = 1; + generic_delim_stack = 0; + while (*end) { + if (*end == '<') + generic_delim_stack++; + else if (*end == '>') + generic_delim_stack--; + + if (*end == ',' && generic_delim_stack == 0) + result->num_args++; + ++end; + } + } + + return result; +} + +/** + * mono_method_desc_from_method: + */ +MonoMethodDesc* +mono_method_desc_from_method (MonoMethod *method) +{ + MonoMethodDesc *result; + + result = g_new0 (MonoMethodDesc, 1); + result->include_namespace = TRUE; + result->name = g_strdup (method->name); + result->klass = g_strdup (method->klass->name); + result->name_space = g_strdup (method->klass->name_space); + + return result; +} + +/** + * mono_method_desc_free: + * \param desc method description to be released + * Releases the \c MonoMethodDesc object \p desc. + */ +void +mono_method_desc_free (MonoMethodDesc *desc) +{ + if (desc->name_space) + g_free (desc->name_space); + else if (desc->klass) + g_free (desc->klass); + g_free (desc); +} + +/** + * mono_method_desc_match: + * \param desc \c MonoMethoDescription + * \param method \c MonoMethod to test + * + * Determines whether the specified \p method matches the provided \p desc description. + * + * namespace and class are supposed to match already if this function is used. + * \returns TRUE if the method matches the description, FALSE otherwise. + */ +gboolean +mono_method_desc_match (MonoMethodDesc *desc, MonoMethod *method) +{ + char *sig; + gboolean name_match; + + name_match = strcmp (desc->name, method->name) == 0; + if (!name_match) + return FALSE; + if (!desc->args) + return TRUE; + if (desc->num_args != mono_method_signature (method)->param_count) + return FALSE; + sig = mono_signature_get_desc (mono_method_signature (method), desc->include_namespace); + if (strcmp (sig, desc->args)) { + g_free (sig); + return FALSE; + } + g_free (sig); + return TRUE; +} + +static const char * +my_strrchr (const char *str, char ch, int *len) +{ + int pos; + + for (pos = (*len)-1; pos >= 0; pos--) { + if (str [pos] != ch) + continue; + + *len = pos; + return str + pos; + } + + return NULL; +} + +static gboolean +match_class (MonoMethodDesc *desc, int pos, MonoClass *klass) +{ + const char *p; + gboolean is_terminal = TRUE; + + if (desc->klass_glob && !strcmp (desc->klass, "*")) + return TRUE; +#ifndef _EGLIB_MAJOR + if (desc->klass_glob && g_pattern_match_simple (desc->klass, klass->name)) + return TRUE; +#endif + if (desc->klass[pos] == '/') + is_terminal = FALSE; + + p = my_strrchr (desc->klass, '/', &pos); + if (!p) { + if (is_terminal && strcmp (desc->klass, klass->name)) + return FALSE; + if (!is_terminal && strncmp (desc->klass, klass->name, pos)) + return FALSE; + if (desc->name_space && strcmp (desc->name_space, klass->name_space)) + return FALSE; + return TRUE; + } + + if (strcmp (p+1, klass->name)) + return FALSE; + if (!klass->nested_in) + return FALSE; + + return match_class (desc, pos, klass->nested_in); +} + +/** + * mono_method_desc_is_full: + */ +gboolean +mono_method_desc_is_full (MonoMethodDesc *desc) +{ + return desc->klass && desc->klass[0] != '\0'; +} + +/** + * mono_method_desc_full_match: + */ +gboolean +mono_method_desc_full_match (MonoMethodDesc *desc, MonoMethod *method) +{ + if (!desc->klass) + return FALSE; + if (!match_class (desc, strlen (desc->klass), method->klass)) + return FALSE; + + return mono_method_desc_match (desc, method); +} + +/** + * mono_method_desc_search_in_class: + */ +MonoMethod* +mono_method_desc_search_in_class (MonoMethodDesc *desc, MonoClass *klass) +{ + MonoMethod* m; + gpointer iter = NULL; + + while ((m = mono_class_get_methods (klass, &iter))) + if (mono_method_desc_match (desc, m)) + return m; + return NULL; +} + +/** + * mono_method_desc_search_in_image: + */ +MonoMethod* +mono_method_desc_search_in_image (MonoMethodDesc *desc, MonoImage *image) +{ + MonoClass *klass; + const MonoTableInfo *methods; + MonoMethod *method; + int i; + + /* Handle short names for system classes */ + if (!desc->name_space && image == mono_defaults.corlib) { + klass = find_system_class (desc->klass); + if (klass) + return mono_method_desc_search_in_class (desc, klass); + } + + if (desc->name_space && desc->klass) { + klass = mono_class_try_load_from_name (image, desc->name_space, desc->klass); + if (!klass) + return NULL; + return mono_method_desc_search_in_class (desc, klass); + } + + /* FIXME: Is this call necessary? We don't use its result. */ + mono_image_get_table_info (image, MONO_TABLE_TYPEDEF); + methods = mono_image_get_table_info (image, MONO_TABLE_METHOD); + for (i = 0; i < mono_table_info_get_rows (methods); ++i) { + MonoError error; + guint32 token = mono_metadata_decode_row_col (methods, i, MONO_METHOD_NAME); + const char *n = mono_metadata_string_heap (image, token); + + if (strcmp (n, desc->name)) + continue; + method = mono_get_method_checked (image, MONO_TOKEN_METHOD_DEF | (i + 1), NULL, NULL, &error); + if (!method) { + mono_error_cleanup (&error); + continue; + } + if (mono_method_desc_full_match (desc, method)) + return method; + } + return NULL; +} + +static const unsigned char* +dis_one (GString *str, MonoDisHelper *dh, MonoMethod *method, const unsigned char *ip, const unsigned char *end) +{ + MonoError error; + MonoMethodHeader *header = mono_method_get_header_checked (method, &error); + const MonoOpcode *opcode; + guint32 label, token; + gint32 sval; + int i; + char *tmp; + const unsigned char* il_code; + + if (!header) { + g_string_append_printf (str, "could not disassemble, bad header due to %s", mono_error_get_message (&error)); + mono_error_cleanup (&error); + return end; + } + il_code = mono_method_header_get_code (header, NULL, NULL); + + label = ip - il_code; + if (dh->indenter) { + tmp = dh->indenter (dh, method, label); + g_string_append (str, tmp); + g_free (tmp); + } + if (dh->label_format) + g_string_append_printf (str, dh->label_format, label); + + i = mono_opcode_value (&ip, end); + ip++; + opcode = &mono_opcodes [i]; + g_string_append_printf (str, "%-10s", mono_opcode_name (i)); + + switch (opcode->argument) { + case MonoInlineNone: + break; + case MonoInlineType: + case MonoInlineField: + case MonoInlineMethod: + case MonoInlineTok: + case MonoInlineSig: + token = read32 (ip); + if (dh->tokener) { + tmp = dh->tokener (dh, method, token); + g_string_append (str, tmp); + g_free (tmp); + } else { + g_string_append_printf (str, "0x%08x", token); + } + ip += 4; + break; + case MonoInlineString: { + const char *blob; + char *s; + size_t len2; + char *blob2 = NULL; + + if (!image_is_dynamic (method->klass->image) && !method_is_dynamic (method)) { + token = read32 (ip); + blob = mono_metadata_user_string (method->klass->image, mono_metadata_token_index (token)); + + len2 = mono_metadata_decode_blob_size (blob, &blob); + len2 >>= 1; + +#ifdef NO_UNALIGNED_ACCESS + /* The blob might not be 2 byte aligned */ + blob2 = g_malloc ((len2 * 2) + 1); + memcpy (blob2, blob, len2 * 2); +#else + blob2 = (char*)blob; +#endif + +#if G_BYTE_ORDER != G_LITTLE_ENDIAN + { + guint16 *buf = g_new (guint16, len2 + 1); + int i; + + for (i = 0; i < len2; ++i) + buf [i] = GUINT16_FROM_LE (((guint16*)blob2) [i]); + s = g_utf16_to_utf8 (buf, len2, NULL, NULL, NULL); + g_free (buf); + } +#else + s = g_utf16_to_utf8 ((gunichar2*)blob2, len2, NULL, NULL, NULL); +#endif + + g_string_append_printf (str, "\"%s\"", s); + g_free (s); + if (blob != blob2) + g_free (blob2); + } + ip += 4; + break; + } + case MonoInlineVar: + g_string_append_printf (str, "%d", read16 (ip)); + ip += 2; + break; + case MonoShortInlineVar: + g_string_append_printf (str, "%d", (*ip)); + ip ++; + break; + case MonoInlineBrTarget: + sval = read32 (ip); + ip += 4; + if (dh->label_target) + g_string_append_printf (str, dh->label_target, ip + sval - il_code); + else + g_string_append_printf (str, "%d", sval); + break; + case MonoShortInlineBrTarget: + sval = *(const signed char*)ip; + ip ++; + if (dh->label_target) + g_string_append_printf (str, dh->label_target, ip + sval - il_code); + else + g_string_append_printf (str, "%d", sval); + break; + case MonoInlineSwitch: { + const unsigned char *end; + sval = read32 (ip); + ip += 4; + end = ip + sval * 4; + g_string_append_c (str, '('); + for (i = 0; i < sval; ++i) { + if (i > 0) + g_string_append (str, ", "); + label = read32 (ip); + if (dh->label_target) + g_string_append_printf (str, dh->label_target, end + label - il_code); + else + g_string_append_printf (str, "%d", label); + ip += 4; + } + g_string_append_c (str, ')'); + break; + } + case MonoInlineR: { + double r; + readr8 (ip, &r); + g_string_append_printf (str, "%g", r); + ip += 8; + break; + } + case MonoShortInlineR: { + float r; + readr4 (ip, &r); + g_string_append_printf (str, "%g", r); + ip += 4; + break; + } + case MonoInlineI: + g_string_append_printf (str, "%d", (gint32)read32 (ip)); + ip += 4; + break; + case MonoShortInlineI: + g_string_append_printf (str, "%d", *(const signed char*)ip); + ip ++; + break; + case MonoInlineI8: + ip += 8; + break; + default: + g_assert_not_reached (); + } + if (dh->newline) + g_string_append (str, dh->newline); + + mono_metadata_free_mh (header); + return ip; +} + +static MonoDisHelper +default_dh = { + "\n", + "IL_%04x: ", /* label_format */ + "IL_%04x", /* label_target */ + NULL, /* indenter */ + NULL, /* tokener */ + NULL /* user data */ +}; + +/** + * mono_disasm_code_one: + */ +char* +mono_disasm_code_one (MonoDisHelper *dh, MonoMethod *method, const guchar *ip, const guchar **endp) +{ + char *result; + GString *res = g_string_new (""); + + if (!dh) + dh = &default_dh; + /* set ip + 2 as the end: this is just a debugging method */ + ip = dis_one (res, dh, method, ip, ip + 2); + if (endp) + *endp = ip; + + result = res->str; + g_string_free (res, FALSE); + return result; +} + +/** + * mono_disasm_code: + */ +char* +mono_disasm_code (MonoDisHelper *dh, MonoMethod *method, const guchar *ip, const guchar* end) +{ + char *result; + GString *res = g_string_new (""); + + if (!dh) + dh = &default_dh; + while (ip < end) { + ip = dis_one (res, dh, method, ip, end); + } + + result = res->str; + g_string_free (res, FALSE); + return result; +} + +/** + * mono_field_full_name: + * \param field field to retrieve information for + * \returns the full name for the field, made up of the namespace, type name and the field name. + */ +char * +mono_field_full_name (MonoClassField *field) +{ + char *res; + const char *nspace = field->parent->name_space; + + res = g_strdup_printf ("%s%s%s:%s", nspace, *nspace ? "." : "", + field->parent->name, mono_field_get_name (field)); + + return res; +} + +char * +mono_method_get_name_full (MonoMethod *method, gboolean signature, gboolean ret, MonoTypeNameFormat format) +{ + char *res; + char wrapper [64]; + char *klass_desc; + char *inst_desc = NULL; + MonoError error; + + const char *class_method_separator = ":"; + const char *method_sig_space = " "; + if (format == MONO_TYPE_NAME_FORMAT_REFLECTION) { + class_method_separator = "."; + method_sig_space = ""; + } + + if (format == MONO_TYPE_NAME_FORMAT_IL) + klass_desc = mono_type_full_name (&method->klass->byval_arg); + else + klass_desc = mono_type_get_name_full (&method->klass->byval_arg, format); + + if (method->is_inflated && ((MonoMethodInflated*)method)->context.method_inst) { + GString *str = g_string_new (""); + if (format == MONO_TYPE_NAME_FORMAT_IL) + g_string_append (str, "<"); + else + g_string_append (str, "["); + mono_ginst_get_desc (str, ((MonoMethodInflated*)method)->context.method_inst); + if (format == MONO_TYPE_NAME_FORMAT_IL) + g_string_append_c (str, '>'); + else + g_string_append_c (str, ']'); + + inst_desc = str->str; + g_string_free (str, FALSE); + } else if (method->is_generic) { + MonoGenericContainer *container = mono_method_get_generic_container (method); + + GString *str = g_string_new (""); + if (format == MONO_TYPE_NAME_FORMAT_IL) + g_string_append (str, "<"); + else + g_string_append (str, "["); + mono_ginst_get_desc (str, container->context.method_inst); + if (format == MONO_TYPE_NAME_FORMAT_IL) + g_string_append_c (str, '>'); + else + g_string_append_c (str, ']'); + + inst_desc = str->str; + g_string_free (str, FALSE); + } + + if (method->wrapper_type != MONO_WRAPPER_NONE) + sprintf (wrapper, "(wrapper %s) ", wrapper_type_to_str (method->wrapper_type)); + else + strcpy (wrapper, ""); + + if (signature) { + MonoMethodSignature *sig = mono_method_signature_checked (method, &error); + char *tmpsig; + + if (!is_ok (&error)) { + tmpsig = g_strdup_printf (""); + mono_error_cleanup (&error); + } else { + tmpsig = mono_signature_get_desc (sig, TRUE); + } + + if (method->wrapper_type != MONO_WRAPPER_NONE) + sprintf (wrapper, "(wrapper %s) ", wrapper_type_to_str (method->wrapper_type)); + else + strcpy (wrapper, ""); + if (ret && sig) { + char *ret_str = mono_type_full_name (sig->ret); + res = g_strdup_printf ("%s%s %s%s%s%s%s(%s)", wrapper, ret_str, klass_desc, + class_method_separator, + method->name, inst_desc ? inst_desc : "", method_sig_space, tmpsig); + g_free (ret_str); + } else { + res = g_strdup_printf ("%s%s%s%s%s%s(%s)", wrapper, klass_desc, + class_method_separator, + method->name, inst_desc ? inst_desc : "", method_sig_space, tmpsig); + } + g_free (tmpsig); + } else { + res = g_strdup_printf ("%s%s%s%s%s", wrapper, klass_desc, + class_method_separator, + method->name, inst_desc ? inst_desc : ""); + } + + g_free (klass_desc); + g_free (inst_desc); + + return res; +} + +/** + * mono_method_full_name: + */ +char * +mono_method_full_name (MonoMethod *method, gboolean signature) +{ + return mono_method_get_name_full (method, signature, FALSE, MONO_TYPE_NAME_FORMAT_IL); +} + +char * +mono_method_get_full_name (MonoMethod *method) +{ + return mono_method_get_name_full (method, TRUE, TRUE, MONO_TYPE_NAME_FORMAT_IL); +} + +/** + * mono_method_get_reflection_name: + * + * Returns the name of the method, including signature, using the same formating as reflection. + */ +char * +mono_method_get_reflection_name (MonoMethod *method) +{ + return mono_method_get_name_full (method, TRUE, FALSE, MONO_TYPE_NAME_FORMAT_REFLECTION); +} + +static const char* +print_name_space (MonoClass *klass) +{ + if (klass->nested_in) { + print_name_space (klass->nested_in); + g_print ("%s", klass->nested_in->name); + return "/"; + } + if (klass->name_space [0]) { + g_print ("%s", klass->name_space); + return "."; + } + return ""; +} + +/** + * mono_object_describe: + * + * Prints to stdout a small description of the object \p obj. + * For use in a debugger. + */ +void +mono_object_describe (MonoObject *obj) +{ + MonoError error; + MonoClass* klass; + const char* sep; + if (!obj) { + g_print ("(null)\n"); + return; + } + klass = mono_object_class (obj); + if (klass == mono_defaults.string_class) { + char *utf8 = mono_string_to_utf8_checked ((MonoString*)obj, &error); + mono_error_cleanup (&error); /* FIXME don't swallow the error */ + if (utf8 && strlen (utf8) > 60) { + utf8 [57] = '.'; + utf8 [58] = '.'; + utf8 [59] = '.'; + utf8 [60] = 0; + } + if (utf8) { + g_print ("String at %p, length: %d, '%s'\n", obj, mono_string_length ((MonoString*) obj), utf8); + } else { + g_print ("String at %p, length: %d, unable to decode UTF16\n", obj, mono_string_length ((MonoString*) obj)); + } + g_free (utf8); + } else if (klass->rank) { + MonoArray *array = (MonoArray*)obj; + sep = print_name_space (klass); + g_print ("%s%s", sep, klass->name); + g_print (" at %p, rank: %d, length: %d\n", obj, klass->rank, (int)mono_array_length (array)); + } else { + sep = print_name_space (klass); + g_print ("%s%s", sep, klass->name); + g_print (" object at %p (klass: %p)\n", obj, klass); + } + +} + +static void +print_field_value (const char *field_ptr, MonoClassField *field, int type_offset) +{ + MonoType *type; + g_print ("At %p (ofs: %2d) %s: ", field_ptr, field->offset + type_offset, mono_field_get_name (field)); + type = mono_type_get_underlying_type (field->type); + + switch (type->type) { + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_PTR: + case MONO_TYPE_FNPTR: + g_print ("%p\n", *(const void**)field_ptr); + break; + case MONO_TYPE_STRING: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_CLASS: + case MONO_TYPE_OBJECT: + case MONO_TYPE_ARRAY: + mono_object_describe (*(MonoObject**)field_ptr); + break; + case MONO_TYPE_GENERICINST: + if (!mono_type_generic_inst_is_valuetype (type)) { + mono_object_describe (*(MonoObject**)field_ptr); + break; + } else { + /* fall through */ + } + case MONO_TYPE_VALUETYPE: { + MonoClass *k = mono_class_from_mono_type (type); + g_print ("%s ValueType (type: %p) at %p\n", k->name, k, field_ptr); + break; + } + case MONO_TYPE_I1: + g_print ("%d\n", *(gint8*)field_ptr); + break; + case MONO_TYPE_U1: + g_print ("%d\n", *(guint8*)field_ptr); + break; + case MONO_TYPE_I2: + g_print ("%d\n", *(gint16*)field_ptr); + break; + case MONO_TYPE_U2: + g_print ("%d\n", *(guint16*)field_ptr); + break; + case MONO_TYPE_I4: + g_print ("%d\n", *(gint32*)field_ptr); + break; + case MONO_TYPE_U4: + g_print ("%u\n", *(guint32*)field_ptr); + break; + case MONO_TYPE_I8: + g_print ("%lld\n", (long long int)*(gint64*)field_ptr); + break; + case MONO_TYPE_U8: + g_print ("%llu\n", (long long unsigned int)*(guint64*)field_ptr); + break; + case MONO_TYPE_R4: + g_print ("%f\n", *(gfloat*)field_ptr); + break; + case MONO_TYPE_R8: + g_print ("%f\n", *(gdouble*)field_ptr); + break; + case MONO_TYPE_BOOLEAN: + g_print ("%s (%d)\n", *(guint8*)field_ptr? "True": "False", *(guint8*)field_ptr); + break; + case MONO_TYPE_CHAR: + g_print ("'%c' (%d 0x%04x)\n", *(guint16*)field_ptr, *(guint16*)field_ptr, *(guint16*)field_ptr); + break; + default: + g_assert_not_reached (); + break; + } +} + +static void +objval_describe (MonoClass *klass, const char *addr) +{ + MonoClassField *field; + MonoClass *p; + const char *field_ptr; + gssize type_offset = 0; + + if (klass->valuetype) + type_offset = -sizeof (MonoObject); + + for (p = klass; p != NULL; p = p->parent) { + gpointer iter = NULL; + int printed_header = FALSE; + while ((field = mono_class_get_fields (p, &iter))) { + if (field->type->attrs & (FIELD_ATTRIBUTE_STATIC | FIELD_ATTRIBUTE_HAS_FIELD_RVA)) + continue; + + if (p != klass && !printed_header) { + const char *sep; + g_print ("In class "); + sep = print_name_space (p); + g_print ("%s%s:\n", sep, p->name); + printed_header = TRUE; + } + field_ptr = (const char*)addr + field->offset + type_offset; + + print_field_value (field_ptr, field, type_offset); + } + } +} + +/** + * mono_object_describe_fields: + * + * Prints to stdout a small description of each field of the object \p obj. + * For use in a debugger. + */ +void +mono_object_describe_fields (MonoObject *obj) +{ + MonoClass *klass = mono_object_class (obj); + objval_describe (klass, (char*)obj); +} + +/** + * mono_value_describe_fields: + * + * Prints to stdout a small description of each field of the value type + * stored at \p addr of type \p klass. + * For use in a debugger. + */ +void +mono_value_describe_fields (MonoClass* klass, const char* addr) +{ + objval_describe (klass, addr); +} + +/** + * mono_class_describe_statics: + * + * Prints to stdout a small description of each static field of the type \p klass + * in the current application domain. + * For use in a debugger. + */ +void +mono_class_describe_statics (MonoClass* klass) +{ + MonoError error; + MonoClassField *field; + MonoClass *p; + const char *field_ptr; + MonoVTable *vtable = mono_class_vtable_full (mono_domain_get (), klass, &error); + const char *addr; + + if (!vtable || !is_ok (&error)) { + mono_error_cleanup (&error); + return; + } + + if (!(addr = (const char *)mono_vtable_get_static_field_data (vtable))) + return; + + for (p = klass; p != NULL; p = p->parent) { + gpointer iter = NULL; + while ((field = mono_class_get_fields (p, &iter))) { + if (field->type->attrs & FIELD_ATTRIBUTE_LITERAL) + continue; + if (!(field->type->attrs & (FIELD_ATTRIBUTE_STATIC | FIELD_ATTRIBUTE_HAS_FIELD_RVA))) + continue; + + field_ptr = (const char*)addr + field->offset; + + print_field_value (field_ptr, field, 0); + } + } +} + +/** + * mono_print_method_code + * \param method: a pointer to the method + * + * This method is used from a debugger to print the code of the method. + * + * This prints the IL code of the method in the standard output. + */ +void +mono_method_print_code (MonoMethod *method) +{ + MonoError error; + char *code; + MonoMethodHeader *header = mono_method_get_header_checked (method, &error); + if (!header) { + printf ("METHOD HEADER NOT FOUND DUE TO: %s\n", mono_error_get_message (&error)); + mono_error_cleanup (&error); + return; + } + code = mono_disasm_code (0, method, header->code, header->code + header->code_size); + printf ("CODE FOR %s:\n%s\n", mono_method_full_name (method, TRUE), code); + g_free (code); +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/debug-helpers.h b/unity-2019.4.24f1-mbe/mono/metadata/debug-helpers.h new file mode 100644 index 000000000..8ecdf8edc --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/debug-helpers.h @@ -0,0 +1,54 @@ +/** + * \file + */ + +#ifndef __MONO_DEBUG_HELPERS_H__ +#define __MONO_DEBUG_HELPERS_H__ + +#include + +MONO_BEGIN_DECLS + +typedef struct MonoDisHelper MonoDisHelper; + +typedef char* (*MonoDisIndenter) (MonoDisHelper *dh, MonoMethod *method, uint32_t ip_offset); +typedef char* (*MonoDisTokener) (MonoDisHelper *dh, MonoMethod *method, uint32_t token); + +struct MonoDisHelper { + const char *newline; + const char *label_format; + const char *label_target; + MonoDisIndenter indenter; + MonoDisTokener tokener; + void* user_data; +}; + +MONO_API char* mono_disasm_code_one (MonoDisHelper *dh, MonoMethod *method, const mono_byte *ip, const mono_byte** endp); +MONO_API char* mono_disasm_code (MonoDisHelper *dh, MonoMethod *method, const mono_byte *ip, const mono_byte* end); + +typedef struct MonoMethodDesc MonoMethodDesc; + +MONO_API char* mono_type_full_name (MonoType *type); + +MONO_API char* mono_signature_get_desc (MonoMethodSignature *sig, mono_bool include_namespace); + +MONO_API char* mono_context_get_desc (MonoGenericContext *context); + +MONO_API MonoMethodDesc* mono_method_desc_new (const char *name, mono_bool include_namespace); +MONO_API MonoMethodDesc* mono_method_desc_from_method (MonoMethod *method); +MONO_API void mono_method_desc_free (MonoMethodDesc *desc); +MONO_API mono_bool mono_method_desc_match (MonoMethodDesc *desc, MonoMethod *method); +MONO_API mono_bool mono_method_desc_is_full (MonoMethodDesc *desc); +MONO_API mono_bool mono_method_desc_full_match (MonoMethodDesc *desc, MonoMethod *method); +MONO_API MonoMethod* mono_method_desc_search_in_class (MonoMethodDesc *desc, MonoClass *klass); +MONO_API MonoMethod* mono_method_desc_search_in_image (MonoMethodDesc *desc, MonoImage *image); + +MONO_API char* mono_method_full_name (MonoMethod *method, mono_bool signature); +MONO_API char* mono_method_get_reflection_name (MonoMethod *method); + +MONO_API char* mono_field_full_name (MonoClassField *field); + +MONO_END_DECLS + +#endif /* __MONO_DEBUG_HELPERS_H__ */ + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/debug-internals.h b/unity-2019.4.24f1-mbe/mono/metadata/debug-internals.h new file mode 100644 index 000000000..1d5e33cf2 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/debug-internals.h @@ -0,0 +1,87 @@ +#ifndef __DEBUG_INTERNALS_H__ +#define __DEBUG_INTERNALS_H__ + +#include +#include +#include +#include + +struct _MonoDebugMethodInfo { + MonoMethod *method; + MonoDebugHandle *handle; + uint32_t index; + uint32_t data_offset; + uint32_t lnt_offset; +}; + +typedef struct { + int parent; + int type; + /* IL offsets */ + int start_offset, end_offset; +} MonoDebugCodeBlock; + +typedef struct { + char *name; + int index; + /* Might be null for the main scope */ + MonoDebugCodeBlock *block; +} MonoDebugLocalVar; + +/* + * Information about local variables retrieved from a symbol file. + */ +struct _MonoDebugLocalsInfo { + int num_locals; + MonoDebugLocalVar *locals; + int num_blocks; + MonoDebugCodeBlock *code_blocks; +}; + +/* +* Information about method await yield and resume offsets retrieved from a symbol file. +*/ +struct _MonoDebugMethodAsyncInfo { + uint32_t catch_handler_offset; + int num_awaits; + uint32_t *yield_offsets; + uint32_t *resume_offsets; + uint32_t *move_next_method_token; +}; + +struct _MonoDebugLineNumberEntry { + uint32_t il_offset; + uint32_t native_offset; +}; + +/* + * Information about a source file retrieved from a symbol file. + */ +typedef struct { + char *source_file; + /* 16 byte long */ + guint8 *guid, *hash; +} MonoDebugSourceInfo; + +typedef struct { + int il_offset; + int line, column; + int end_line, end_column; +} MonoSymSeqPoint; + +void mono_debugger_lock (void); +void mono_debugger_unlock (void); + +void +mono_debug_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrArray **source_file_list, int **source_files, MonoSymSeqPoint **seq_points, int *n_seq_points); + +MONO_API void +mono_debug_free_locals (MonoDebugLocalsInfo *info); + +void +mono_debug_free_method_async_debug_info (MonoDebugMethodAsyncInfo *info); + +gboolean +mono_debug_image_has_debug_info (MonoImage *image); + +#endif /* __DEBUG_INTERNALS_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/debug-mono-ppdb.c b/unity-2019.4.24f1-mbe/mono/metadata/debug-mono-ppdb.c new file mode 100644 index 000000000..590bb2812 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/debug-mono-ppdb.c @@ -0,0 +1,752 @@ +/** + * \file + * Support for the portable PDB symbol + * file format + * + * + * Author: + * Mono Project (http://www.mono-project.com) + * + * Copyright 2015 Xamarin Inc (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debug-mono-ppdb.h" + +struct _MonoPPDBFile { + MonoImage *image; + GHashTable *doc_hash; + GHashTable *method_hash; +}; + +/* IMAGE_DEBUG_DIRECTORY structure */ +typedef struct +{ + gint32 characteristics; + gint32 time_date_stamp; + gint16 major_version; + gint16 minor_version; + gint32 type; + gint32 size_of_data; + gint32 address; + gint32 pointer; +} ImageDebugDirectory; + +typedef struct { + gint32 signature; + guint8 guid [16]; + gint32 age; + char path []; +} CodeviewDebugDirectory; + +typedef struct { + guint8 guid [20]; + guint32 entry_point; + guint64 referenced_tables; +} PdbStreamHeader; + +static gboolean +get_pe_debug_guid (MonoImage *image, const char** out_path, guint8 *out_guid, gint32 *out_age, gint32 *out_timestamp) +{ + MonoPEDirEntry *debug_dir_entry; + ImageDebugDirectory *debug_dir; + + debug_dir_entry = &((MonoCLIImageInfo*)image->image_info)->cli_header.datadir.pe_debug; + if (!debug_dir_entry->size) + return FALSE; + + int offset = mono_cli_rva_image_map (image, debug_dir_entry->rva); + debug_dir = (ImageDebugDirectory*)(image->raw_data + offset); + if (debug_dir->type == 2 && debug_dir->major_version == 0x100 && debug_dir->minor_version == 0x504d) { + /* This is a 'CODEVIEW' debug directory */ + CodeviewDebugDirectory *dir = (CodeviewDebugDirectory*)(image->raw_data + debug_dir->pointer); + + if (dir->signature == 0x53445352) { + memcpy (out_guid, dir->guid, 16); + *out_age = dir->age; + *out_timestamp = debug_dir->time_date_stamp; + + if (out_path != NULL) { + *out_path = g_strdup (dir->path); + } + + return TRUE; + } + } + return FALSE; +} + +static void +doc_free (gpointer key) +{ + MonoDebugSourceInfo *info = (MonoDebugSourceInfo *)key; + + g_free (info->source_file); + g_free (info); +} + +static MonoPPDBFile* +create_ppdb_file (MonoImage *ppdb_image) +{ + MonoPPDBFile *ppdb; + + ppdb = g_new0 (MonoPPDBFile, 1); + ppdb->image = ppdb_image; + ppdb->doc_hash = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) doc_free); + ppdb->method_hash = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) g_free); + return ppdb; +} + +gboolean +mono_ppdb_get_signature(MonoImage *image, const char** out_path, guint8 *out_guid, gint32 *out_age, gint32 *out_timestamp) +{ + return get_pe_debug_guid (image, out_path, out_guid, out_age, out_timestamp); +} + +MonoPPDBFile* +mono_ppdb_load_file (MonoImage *image, const guint8 *raw_contents, int size) +{ + MonoImage *ppdb_image = NULL; + const char *filename; + char *s, *ppdb_filename; + MonoImageOpenStatus status; + guint8 pe_guid [16]; + gint32 pe_age; + gint32 pe_timestamp; + + if (image->tables [MONO_TABLE_DOCUMENT].rows) { + /* Embedded ppdb */ + mono_image_addref (image); + return create_ppdb_file (image); + } + + if (!get_pe_debug_guid (image, NULL, pe_guid, &pe_age, &pe_timestamp)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, "Image '%s' has no debug directory.", image->name); + return NULL; + } + + if (raw_contents) { + if (size > 4 && strncmp ((char*)raw_contents, "BSJB", 4) == 0) + ppdb_image = mono_image_open_from_data_internal ((char*)raw_contents, size, TRUE, &status, FALSE, TRUE, NULL); + } else { + /* ppdb files drop the .exe/.dll extension */ + filename = mono_image_get_filename (image); + if (strlen (filename) > 4 && (!strcmp (filename + strlen (filename) - 4, ".exe") || !strcmp (filename + strlen (filename) - 4, ".dll"))) { + s = g_strdup (filename); + s [strlen (filename) - 4] = '\0'; + ppdb_filename = g_strdup_printf ("%s.pdb", s); + g_free (s); + } else { + ppdb_filename = g_strdup_printf ("%s.pdb", filename); + } + + ppdb_image = mono_image_open_metadata_only (ppdb_filename, &status); + if (!ppdb_image) + g_free (ppdb_filename); + } + if (!ppdb_image) + return NULL; + + /* + * Check that the images match. + * The same id is stored in the Debug Directory of the PE file, and in the + * #Pdb stream in the ppdb file. + */ + PdbStreamHeader *pdb_stream = (PdbStreamHeader*)ppdb_image->heap_pdb.data; + + g_assert (pdb_stream); + + /* The pdb id is a concentation of the pe guid and the timestamp */ + if (memcmp (pe_guid, pdb_stream->guid, 16) != 0 || memcmp (&pe_timestamp, pdb_stream->guid + 16, 4) != 0) { + g_warning ("Symbol file %s doesn't match image %s", ppdb_image->name, + image->name); + mono_image_close (ppdb_image); + return NULL; + } + + return create_ppdb_file (ppdb_image); +} + +void +mono_ppdb_close (MonoDebugHandle *handle) +{ + MonoPPDBFile *ppdb = handle->ppdb; + + mono_image_close (ppdb->image); + g_hash_table_destroy (ppdb->doc_hash); + g_hash_table_destroy (ppdb->method_hash); + g_free (ppdb); +} + +MonoDebugMethodInfo * +mono_ppdb_lookup_method (MonoDebugHandle *handle, MonoMethod *method) +{ + MonoDebugMethodInfo *minfo; + MonoPPDBFile *ppdb = handle->ppdb; + + if (handle->image != mono_class_get_image (mono_method_get_class (method))) + return NULL; + + mono_debugger_lock (); + + minfo = (MonoDebugMethodInfo *)g_hash_table_lookup (ppdb->method_hash, method); + if (minfo) { + mono_debugger_unlock (); + return minfo; + } + + minfo = g_new0 (MonoDebugMethodInfo, 1); + minfo->index = 0; + minfo->method = method; + minfo->handle = handle; + + g_hash_table_insert (ppdb->method_hash, method, minfo); + + mono_debugger_unlock (); + + return minfo; +} + +static MonoDebugSourceInfo* +get_docinfo (MonoPPDBFile *ppdb, MonoImage *image, int docidx) +{ + MonoTableInfo *tables = image->tables; + guint32 cols [MONO_DOCUMENT_SIZE]; + const char *ptr; + const char *start; + const char *part_ptr; + int size, part_size, partidx, nparts; + char sep; + GString *s; + MonoDebugSourceInfo *res, *cached; + + mono_debugger_lock (); + cached = (MonoDebugSourceInfo *)g_hash_table_lookup (ppdb->doc_hash, GUINT_TO_POINTER (docidx)); + mono_debugger_unlock (); + if (cached) + return cached; + + mono_metadata_decode_row (&tables [MONO_TABLE_DOCUMENT], docidx-1, cols, MONO_DOCUMENT_SIZE); + + ptr = mono_metadata_blob_heap (image, cols [MONO_DOCUMENT_NAME]); + size = mono_metadata_decode_blob_size (ptr, &ptr); + start = ptr; + + // FIXME: UTF8 + sep = ptr [0]; + ptr ++; + + s = g_string_new (""); + + nparts = 0; + while (ptr < start + size) { + partidx = mono_metadata_decode_value (ptr, &ptr); + if (nparts) + g_string_append_c (s, sep); + if (partidx) { + part_ptr = mono_metadata_blob_heap (image, partidx); + part_size = mono_metadata_decode_blob_size (part_ptr, &part_ptr); + + // FIXME: UTF8 + g_string_append_len (s, part_ptr, part_size); + } + nparts ++; + } + + res = g_new0 (MonoDebugSourceInfo, 1); + res->source_file = g_string_free (s, FALSE); + res->guid = NULL; + res->hash = (guint8*)mono_metadata_blob_heap (image, cols [MONO_DOCUMENT_HASH]); + + mono_debugger_lock (); + cached = (MonoDebugSourceInfo *)g_hash_table_lookup (ppdb->doc_hash, GUINT_TO_POINTER (docidx)); + if (!cached) { + g_hash_table_insert (ppdb->doc_hash, GUINT_TO_POINTER (docidx), res); + } else { + doc_free (res); + res = cached; + } + mono_debugger_unlock (); + return res; +} + +static char* +get_docname (MonoPPDBFile *ppdb, MonoImage *image, int docidx) +{ + MonoDebugSourceInfo *info; + + info = get_docinfo (ppdb, image, docidx); + return g_strdup (info->source_file); +} + +/** + * mono_ppdb_lookup_location: + * \param minfo A \c MonoDebugMethodInfo which can be retrieved by mono_debug_lookup_method(). + * \param offset IL offset within the corresponding method's CIL code. + * + * This function is similar to mono_debug_lookup_location(), but we + * already looked up the method and also already did the + * native address -> IL offset mapping. + */ +MonoDebugSourceLocation * +mono_ppdb_lookup_location (MonoDebugMethodInfo *minfo, uint32_t offset) +{ + MonoPPDBFile *ppdb = minfo->handle->ppdb; + MonoImage *image = ppdb->image; + MonoMethod *method = minfo->method; + MonoTableInfo *tables = image->tables; + guint32 cols [MONO_METHODBODY_SIZE]; + const char *ptr; + const char *end; + char *docname; + int idx, size, docidx, iloffset, delta_il, delta_lines, delta_cols, start_line, start_col, adv_line, adv_col; + gboolean first = TRUE, first_non_hidden = TRUE; + MonoDebugSourceLocation *location; + + if (!method->token) + return NULL; + + idx = mono_metadata_token_index (method->token); + + mono_metadata_decode_row (&tables [MONO_TABLE_METHODBODY], idx-1, cols, MONO_METHODBODY_SIZE); + + docidx = cols [MONO_METHODBODY_DOCUMENT]; + + if (!cols [MONO_METHODBODY_SEQ_POINTS]) + return NULL; + ptr = mono_metadata_blob_heap (image, cols [MONO_METHODBODY_SEQ_POINTS]); + size = mono_metadata_decode_blob_size (ptr, &ptr); + end = ptr + size; + + /* Header */ + /* LocalSignature */ + mono_metadata_decode_value (ptr, &ptr); + if (docidx == 0) + docidx = mono_metadata_decode_value (ptr, &ptr); + docname = get_docname (ppdb, image, docidx); + + iloffset = 0; + start_line = 0; + start_col = 0; + while (ptr < end) { + delta_il = mono_metadata_decode_value (ptr, &ptr); + if (!first && delta_il == 0) { + /* document-record */ + docidx = mono_metadata_decode_value (ptr, &ptr); + docname = get_docname (ppdb, image, docidx); + continue; + } + if (!first && iloffset + delta_il > offset) + break; + iloffset += delta_il; + first = FALSE; + + delta_lines = mono_metadata_decode_value (ptr, &ptr); + if (delta_lines == 0) + delta_cols = mono_metadata_decode_value (ptr, &ptr); + else + delta_cols = mono_metadata_decode_signed_value (ptr, &ptr); + if (delta_lines == 0 && delta_cols == 0) + /* hidden-sequence-point-record */ + continue; + if (first_non_hidden) { + start_line = mono_metadata_decode_value (ptr, &ptr); + start_col = mono_metadata_decode_value (ptr, &ptr); + } else { + adv_line = mono_metadata_decode_signed_value (ptr, &ptr); + adv_col = mono_metadata_decode_signed_value (ptr, &ptr); + start_line += adv_line; + start_col += adv_col; + } + first_non_hidden = FALSE; + } + + location = g_new0 (MonoDebugSourceLocation, 1); + location->source_file = docname; + location->row = start_line; + location->column = start_col; + location->il_offset = iloffset; + + return location; +} + +void +mono_ppdb_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrArray **source_file_list, int **source_files, MonoSymSeqPoint **seq_points, int *n_seq_points) +{ + MonoPPDBFile *ppdb = minfo->handle->ppdb; + MonoImage *image = ppdb->image; + MonoMethod *method = minfo->method; + MonoTableInfo *tables = image->tables; + guint32 cols [MONO_METHODBODY_SIZE]; + const char *ptr; + const char *end; + MonoDebugSourceInfo *docinfo; + int i, method_idx, size, docidx, iloffset, delta_il, delta_lines, delta_cols, start_line, start_col, adv_line, adv_col; + gboolean first = TRUE, first_non_hidden = TRUE; + GArray *sps; + MonoSymSeqPoint sp; + GPtrArray *sfiles = NULL; + GPtrArray *sindexes = NULL; + + if (source_file) + *source_file = NULL; + if (source_file_list) + *source_file_list = NULL; + if (source_files) + *source_files = NULL; + if (seq_points) + *seq_points = NULL; + if (n_seq_points) + *n_seq_points = 0; + + if (source_file_list) + *source_file_list = sfiles = g_ptr_array_new (); + if (source_files) + sindexes = g_ptr_array_new (); + + if (!method->token) + return; + + 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]; + + if (!cols [MONO_METHODBODY_SEQ_POINTS]) + return; + + ptr = mono_metadata_blob_heap (image, cols [MONO_METHODBODY_SEQ_POINTS]); + size = mono_metadata_decode_blob_size (ptr, &ptr); + end = ptr + size; + + sps = g_array_new (FALSE, TRUE, sizeof (MonoSymSeqPoint)); + + /* Header */ + /* LocalSignature */ + mono_metadata_decode_value (ptr, &ptr); + if (docidx == 0) + docidx = mono_metadata_decode_value (ptr, &ptr); + docinfo = get_docinfo (ppdb, image, docidx); + + if (sfiles) + g_ptr_array_add (sfiles, docinfo); + + if (source_file) + *source_file = g_strdup (docinfo->source_file); + + iloffset = 0; + start_line = 0; + start_col = 0; + while (ptr < end) { + delta_il = mono_metadata_decode_value (ptr, &ptr); + if (!first && delta_il == 0) { + /* subsequent-document-record */ + docidx = mono_metadata_decode_value (ptr, &ptr); + docinfo = get_docinfo (ppdb, image, docidx); + if (sfiles) + g_ptr_array_add (sfiles, docinfo); + continue; + } + iloffset += delta_il; + first = FALSE; + + delta_lines = mono_metadata_decode_value (ptr, &ptr); + if (delta_lines == 0) + delta_cols = mono_metadata_decode_value (ptr, &ptr); + else + delta_cols = mono_metadata_decode_signed_value (ptr, &ptr); + + if (delta_lines == 0 && delta_cols == 0) { + /* Hidden sequence point */ + continue; + } + + if (first_non_hidden) { + start_line = mono_metadata_decode_value (ptr, &ptr); + start_col = mono_metadata_decode_value (ptr, &ptr); + } else { + adv_line = mono_metadata_decode_signed_value (ptr, &ptr); + adv_col = mono_metadata_decode_signed_value (ptr, &ptr); + start_line += adv_line; + start_col += adv_col; + } + first_non_hidden = FALSE; + + memset (&sp, 0, sizeof (sp)); + sp.il_offset = iloffset; + sp.line = start_line; + sp.column = start_col; + sp.end_line = start_line + delta_lines; + sp.end_column = start_col + delta_cols; + + g_array_append_val (sps, sp); + if (source_files) + g_ptr_array_add (sindexes, GUINT_TO_POINTER (sfiles->len - 1)); + } + + if (n_seq_points) { + *n_seq_points = sps->len; + g_assert (seq_points); + *seq_points = g_new (MonoSymSeqPoint, sps->len); + memcpy (*seq_points, sps->data, sps->len * sizeof (MonoSymSeqPoint)); + } + + if (source_files) { + *source_files = g_new (int, sps->len); + for (i = 0; i < sps->len; ++i) + (*source_files)[i] = GPOINTER_TO_INT (g_ptr_array_index (sindexes, i)); + g_ptr_array_free (sindexes, TRUE); + } + + g_array_free (sps, TRUE); +} + +MonoDebugLocalsInfo* +mono_ppdb_lookup_locals (MonoDebugMethodInfo *minfo) +{ + MonoPPDBFile *ppdb = minfo->handle->ppdb; + MonoImage *image = ppdb->image; + MonoTableInfo *tables = image->tables; + MonoMethod *method = minfo->method; + guint32 cols [MONO_LOCALSCOPE_SIZE]; + guint32 locals_cols [MONO_LOCALVARIABLE_SIZE]; + int i, lindex, sindex, method_idx, start_scope_idx, scope_idx, locals_idx, locals_end_idx, nscopes; + MonoDebugLocalsInfo *res; + MonoMethodSignature *sig; + + if (!method->token) + return NULL; + + sig = mono_method_signature (method); + if (!sig) + return NULL; + + method_idx = mono_metadata_token_index (method->token); + + start_scope_idx = mono_metadata_localscope_from_methoddef (image, method_idx); + + if (!start_scope_idx) + return NULL; + + /* Compute number of locals and scopes */ + scope_idx = start_scope_idx; + mono_metadata_decode_row (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1, cols, MONO_LOCALSCOPE_SIZE); + locals_idx = cols [MONO_LOCALSCOPE_VARIABLELIST]; + + // https://github.com/dotnet/roslyn/blob/2ae8d5fed96ab3f1164031f9b4ac827f53289159/docs/specs/PortablePdb-Metadata.md#LocalScopeTable + // + // The variableList attribute in the pdb metadata table is a contiguous array that starts at a + // given offset (locals_idx) above and + // + // """ + // continues to the smaller of: + // + // the last row of the LocalVariable table + // the next run of LocalVariables, found by inspecting the VariableList of the next row in this LocalScope table. + // """ + // this endpoint becomes locals_end_idx below + + // March to the last scope that is in this method + while (scope_idx <= tables [MONO_TABLE_LOCALSCOPE].rows) { + mono_metadata_decode_row (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1, cols, MONO_LOCALSCOPE_SIZE); + if (cols [MONO_LOCALSCOPE_METHOD] != method_idx) + break; + scope_idx ++; + } + // The number of scopes is the difference in the indices + // for the first and last scopes + nscopes = scope_idx - start_scope_idx; + + // Ends with "the last row of the LocalVariable table" + // this happens if the above loop marched one past the end + // of the rows + if (scope_idx > tables [MONO_TABLE_LOCALSCOPE].rows) { + locals_end_idx = tables [MONO_TABLE_LOCALVARIABLE].rows + 1; + } else { + // Ends with "the next run of LocalVariables, + // found by inspecting the VariableList of the next row in this LocalScope table." + locals_end_idx = cols [MONO_LOCALSCOPE_VARIABLELIST]; + } + + res = g_new0 (MonoDebugLocalsInfo, 1); + res->num_blocks = nscopes; + res->code_blocks = g_new0 (MonoDebugCodeBlock, res->num_blocks); + res->num_locals = locals_end_idx - locals_idx; + res->locals = g_new0 (MonoDebugLocalVar, res->num_locals); + + lindex = 0; + for (sindex = 0; sindex < nscopes; ++sindex) { + scope_idx = start_scope_idx + sindex; + mono_metadata_decode_row (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1, cols, MONO_LOCALSCOPE_SIZE); + + locals_idx = cols [MONO_LOCALSCOPE_VARIABLELIST]; + if (scope_idx == tables [MONO_TABLE_LOCALSCOPE].rows) { + locals_end_idx = tables [MONO_TABLE_LOCALVARIABLE].rows + 1; + } else { + locals_end_idx = mono_metadata_decode_row_col (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1 + 1, MONO_LOCALSCOPE_VARIABLELIST); + } + + res->code_blocks [sindex].start_offset = cols [MONO_LOCALSCOPE_STARTOFFSET]; + res->code_blocks [sindex].end_offset = cols [MONO_LOCALSCOPE_STARTOFFSET] + cols [MONO_LOCALSCOPE_LENGTH]; + + //printf ("Scope: %s %d %d %d-%d\n", mono_method_full_name (method, 1), cols [MONO_LOCALSCOPE_STARTOFFSET], cols [MONO_LOCALSCOPE_LENGTH], locals_idx, locals_end_idx); + + for (i = locals_idx; i < locals_end_idx; ++i) { + mono_metadata_decode_row (&tables [MONO_TABLE_LOCALVARIABLE], i - 1, locals_cols, MONO_LOCALVARIABLE_SIZE); + + res->locals [lindex].name = g_strdup (mono_metadata_string_heap (image, locals_cols [MONO_LOCALVARIABLE_NAME])); + res->locals [lindex].index = locals_cols [MONO_LOCALVARIABLE_INDEX]; + res->locals [lindex].block = &res->code_blocks [sindex]; + lindex ++; + + //printf ("\t %s %d\n", mono_metadata_string_heap (image, locals_cols [MONO_LOCALVARIABLE_NAME]), locals_cols [MONO_LOCALVARIABLE_INDEX]); + } + } + + return res; +} + +/* +* We use this to pass context information to the row locator +*/ +typedef struct { + int idx; /* The index that we are trying to locate */ + int col_idx; /* The index in the row where idx may be stored */ + MonoTableInfo *t; /* pointer to the table */ + guint32 result; +} locator_t; + +static int +table_locator (const void *a, const void *b) +{ + locator_t *loc = (locator_t *)a; + const char *bb = (const char *)b; + guint32 table_index = (bb - loc->t->base) / loc->t->row_size; + guint32 col; + + col = mono_metadata_decode_row_col(loc->t, table_index, loc->col_idx); + + if (loc->idx == col) { + loc->result = table_index; + return 0; + } + if (loc->idx < col) + return -1; + else + return 1; +} + +static gboolean +compare_guid (guint8* guid1, guint8* guid2) { + for (int i = 0; i < 16; i++) { + if (guid1 [i] != guid2 [i]) + return FALSE; + } + return TRUE; +} + +// for parent_type see HasCustomDebugInformation table at +// https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PortablePdb-Metadata.md +static const char* +lookup_custom_debug_information (MonoImage* image, guint32 token, uint8_t parent_type, guint8* guid) +{ + MonoTableInfo *tables = image->tables; + MonoTableInfo *table = &tables[MONO_TABLE_CUSTOMDEBUGINFORMATION]; + locator_t loc; + + if (!table->base) + return 0; + + loc.idx = (mono_metadata_token_index (token) << 5) | parent_type; + loc.col_idx = MONO_CUSTOMDEBUGINFORMATION_PARENT; + loc.t = table; + + if (!mono_binary_search (&loc, table->base, table->rows, table->row_size, table_locator)) + return NULL; + // Great we found one of possibly many CustomDebugInformations of this entity they are distinguished by KIND guid + // First try on this index found by binary search...(it's most likeley to be only one and binary search found the one we want) + if (compare_guid (guid, (guint8*)mono_metadata_guid_heap (image, mono_metadata_decode_row_col (table, loc.result, MONO_CUSTOMDEBUGINFORMATION_KIND)))) + return mono_metadata_blob_heap (image, mono_metadata_decode_row_col (table, loc.result, MONO_CUSTOMDEBUGINFORMATION_VALUE)); + + // Move forward from binary found index, until parent token differs + for (int i = loc.result + 1; i < table->rows; i++) + { + if (mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_PARENT) != loc.idx) + break; + if (compare_guid (guid, (guint8*)mono_metadata_guid_heap (image, mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_KIND)))) + return mono_metadata_blob_heap (image, mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_VALUE)); + } + + // Move backward from binary found index, until parent token differs + for (int i = loc.result - 1; i >= 0; i--) { + if (mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_PARENT) != loc.idx) + break; + if (compare_guid (guid, (guint8*)mono_metadata_guid_heap (image, mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_KIND)))) + return mono_metadata_blob_heap (image, mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_VALUE)); + } + return NULL; +} + +MonoDebugMethodAsyncInfo* +mono_ppdb_lookup_method_async_debug_info (MonoDebugMethodInfo *minfo) +{ + MonoMethod *method = minfo->method; + MonoPPDBFile *ppdb = minfo->handle->ppdb; + MonoImage *image = ppdb->image; + + // Guid is taken from Roslyn source code: + // https://github.com/dotnet/roslyn/blob/1ad4b58/src/Dependencies/CodeAnalysis.Metadata/PortableCustomDebugInfoKinds.cs#L9 + guint8 async_method_stepping_information_guid [16] = { 0xC5, 0x2A, 0xFD, 0x54, 0x25, 0xE9, 0x1A, 0x40, 0x9C, 0x2A, 0xF9, 0x4F, 0x17, 0x10, 0x72, 0xF8 }; + char const *blob = lookup_custom_debug_information (image, method->token, 0, async_method_stepping_information_guid); + if (!blob) + return NULL; + int blob_len = mono_metadata_decode_blob_size (blob, &blob); + MonoDebugMethodAsyncInfo* res = g_new0 (MonoDebugMethodAsyncInfo, 1); + char const *pointer = blob; + + // Format of this blob is taken from Roslyn source code: + // https://github.com/dotnet/roslyn/blob/1ad4b58/src/Compilers/Core/Portable/PEWriter/MetadataWriter.PortablePdb.cs#L566 + + pointer += 4;//catch_handler_offset + while (pointer - blob < blob_len) { + res->num_awaits++; + pointer += 8;//yield_offsets+resume_offsets + mono_metadata_decode_value (pointer, &pointer);//move_next_method_token + } + g_assert(pointer - blob == blob_len); //Check that we used all blob data + pointer = blob; //reset pointer after we figured num_awaits + + res->yield_offsets = g_new (uint32_t, res->num_awaits); + res->resume_offsets = g_new (uint32_t, res->num_awaits); + res->move_next_method_token = g_new (uint32_t, res->num_awaits); + + res->catch_handler_offset = read32 (pointer); pointer += 4; + for (int i = 0; i < res->num_awaits; i++) { + res->yield_offsets [i] = read32 (pointer); pointer += 4; + res->resume_offsets [i] = read32 (pointer); pointer += 4; + res->move_next_method_token [i] = mono_metadata_decode_value (pointer, &pointer); + } + return res; +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/debug-mono-ppdb.h b/unity-2019.4.24f1-mbe/mono/metadata/debug-mono-ppdb.h new file mode 100644 index 000000000..4c498d553 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/debug-mono-ppdb.h @@ -0,0 +1,44 @@ +/** + * \file + * Support for the portable PDB symbol file format + * + * + * Author: + * Mono Project (http://www.mono-project.com) + * + * Copyright 2015 Xamarin Inc (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#ifndef __MONO_METADATA_DEBUG_MONO_PPDB_H__ +#define __MONO_METADATA_DEBUG_MONO_PPDB_H__ + +#include +#include +#include + +MONO_API gboolean +mono_ppdb_get_signature (MonoImage *image, const char** out_path, guint8 *out_guid, gint32 *out_age, gint32 *out_timestamp); + +MonoPPDBFile* +mono_ppdb_load_file (MonoImage *image, const guint8 *raw_contents, int size); + +void +mono_ppdb_close (MonoDebugHandle *handle); + +MonoDebugMethodInfo * +mono_ppdb_lookup_method (MonoDebugHandle *handle, MonoMethod *method); + +MonoDebugSourceLocation * +mono_ppdb_lookup_location (MonoDebugMethodInfo *minfo, uint32_t offset); + +void +mono_ppdb_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrArray **source_file_list, int **source_files, MonoSymSeqPoint **seq_points, int *n_seq_points); + +MonoDebugLocalsInfo* +mono_ppdb_lookup_locals (MonoDebugMethodInfo *minfo); + +MonoDebugMethodAsyncInfo* +mono_ppdb_lookup_method_async_debug_info (MonoDebugMethodInfo *minfo); + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/debug-mono-symfile.c b/unity-2019.4.24f1-mbe/mono/metadata/debug-mono-symfile.c new file mode 100644 index 000000000..838707510 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/debug-mono-symfile.c @@ -0,0 +1,896 @@ +/** + * \file + * + * Support for reading debug info from .mdb files. + * + * Author: + * Mono Project (http://www.mono-project.com) + * + * Copyright (C) 2005-2008 Novell, Inc. (http://www.novell.com) + * Copyright 2012 Xamarin Inc (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_PARAM_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef DISABLE_MDB + +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#define RANGE_TABLE_CHUNK_SIZE 256 +#define CLASS_TABLE_CHUNK_SIZE 256 +#define TYPE_TABLE_PTR_CHUNK_SIZE 256 +#define TYPE_TABLE_CHUNK_SIZE 65536 + +struct _MonoSymbolFile { + const uint8_t *raw_contents; + int raw_contents_size; + void *raw_contents_handle; + int major_version; + int minor_version; + char *filename; + GHashTable *method_hash; + GHashTable *source_hash; + MonoSymbolFileOffsetTable *offset_table; + gboolean was_loaded_from_memory; +}; + +static void +free_method_info (MonoDebugMethodInfo *minfo) +{ + g_free (minfo); +} + +static void +free_source_info (MonoDebugSourceInfo *sinfo) +{ + g_free (sinfo->source_file); + g_free (sinfo->guid); + g_free (sinfo->hash); + g_free (sinfo); +} + +static int +load_symfile (MonoDebugHandle *handle, MonoSymbolFile *symfile, mono_bool in_the_debugger) +{ + const char *ptr, *start; + gchar *guid; + uint64_t magic; + int minor, major; + + ptr = start = (const char*)symfile->raw_contents; + if (!ptr) + return FALSE; + + magic = read64(ptr); + ptr += sizeof(uint64_t); + if (magic != MONO_SYMBOL_FILE_MAGIC) { + if (!in_the_debugger) + g_warning ("Symbol file %s is not a mono symbol file", symfile->filename); + return FALSE; + } + + major = read32(ptr); + ptr += sizeof(uint32_t); + minor = read32(ptr); + ptr += sizeof(uint32_t); + + /* + * 50.0 is the frozen version for Mono 2.0. + * + * Nobody except me (Martin) is allowed to check the minor version. + */ + if (major != MONO_SYMBOL_FILE_MAJOR_VERSION) { + if (!in_the_debugger) + g_warning ("Symbol file %s has incorrect version (expected %d.%d, got %d)", + symfile->filename, MONO_SYMBOL_FILE_MAJOR_VERSION, + MONO_SYMBOL_FILE_MINOR_VERSION, major); + return FALSE; + } + + guid = mono_guid_to_string ((const uint8_t *) ptr); + ptr += 16; + + if (strcmp (handle->image->guid, guid)) { + if (!in_the_debugger) + g_warning ("Symbol file %s doesn't match image %s", symfile->filename, + handle->image->name); + if (guid) + g_free (guid); + return FALSE; + } + + symfile->major_version = major; + symfile->minor_version = minor; + + symfile->offset_table = (MonoSymbolFileOffsetTable *) ptr; + + symfile->method_hash = g_hash_table_new_full ( + NULL, NULL, NULL, (GDestroyNotify) free_method_info); + + symfile->source_hash = g_hash_table_new_full ( + NULL, NULL, NULL, (GDestroyNotify) free_source_info); + + g_free (guid); + return TRUE; +} + +/** + * mono_debug_open_mono_symbols: + */ +MonoSymbolFile * +mono_debug_open_mono_symbols (MonoDebugHandle *handle, const uint8_t *raw_contents, + int size, gboolean in_the_debugger) +{ + MonoSymbolFile *symfile; + + mono_debugger_lock (); + symfile = g_new0 (MonoSymbolFile, 1); + + if (raw_contents != NULL) { + unsigned char *p; + symfile->raw_contents_size = size; + symfile->raw_contents = p = (unsigned char *)g_malloc (size); + memcpy (p, raw_contents, size); + symfile->filename = g_strdup_printf ("LoadedFromMemory"); + symfile->was_loaded_from_memory = TRUE; + } else { + MonoFileMap *f; + + symfile->filename = g_strdup_printf ("%s.mdb", mono_image_get_filename (handle->image)); + symfile->was_loaded_from_memory = FALSE; + if ((f = mono_file_map_open (symfile->filename))) { + symfile->raw_contents_size = mono_file_map_size (f); + if (symfile->raw_contents_size == 0) { + if (!in_the_debugger) + g_warning ("stat of %s failed: %s", + symfile->filename, g_strerror (errno)); + } else { + symfile->raw_contents = (const unsigned char *)mono_file_map (symfile->raw_contents_size, MONO_MMAP_READ|MONO_MMAP_PRIVATE, mono_file_map_fd (f), 0, &symfile->raw_contents_handle); + } + + mono_file_map_close (f); + } + } + + if (load_symfile (handle, symfile, in_the_debugger)) { + mono_debugger_unlock (); + return symfile; + } else if (!in_the_debugger) { + mono_debug_close_mono_symbol_file (symfile); + mono_debugger_unlock (); + return NULL; + } + + mono_debugger_unlock (); + return symfile; +} + +/** + * mono_debug_close_mono_symbol_file: + */ +void +mono_debug_close_mono_symbol_file (MonoSymbolFile *symfile) +{ + if (!symfile) + return; + + mono_debugger_lock (); + if (symfile->method_hash) + g_hash_table_destroy (symfile->method_hash); + + if (symfile->raw_contents) { + if (symfile->was_loaded_from_memory) + g_free ((gpointer)symfile->raw_contents); + else + mono_file_unmap ((gpointer) symfile->raw_contents, symfile->raw_contents_handle); + } + + if (symfile->filename) + g_free (symfile->filename); + g_free (symfile); + mono_debugger_unlock (); +} + +/** + * mono_debug_symfile_is_loaded: + */ +mono_bool +mono_debug_symfile_is_loaded (MonoSymbolFile *symfile) +{ + return symfile && symfile->offset_table; +} + +static int +read_leb128 (const uint8_t *ptr, const uint8_t **rptr) +{ + int ret = 0; + int shift = 0; + char b; + + do { + b = *ptr++; + + ret = ret | ((b & 0x7f) << shift); + shift += 7; + } while ((b & 0x80) == 0x80); + + if (rptr) + *rptr = ptr; + + return ret; +} + +static gchar * +read_string (const uint8_t *ptr, const uint8_t **endp) +{ + gchar *s; + int len = read_leb128 (ptr, &ptr); + + s = g_filename_from_utf8 ((const char *) ptr, len, NULL, NULL, NULL); + ptr += len; + if (endp) + *endp = ptr; + return s; +} + +typedef struct { + MonoSymbolFile *symfile; + int line_base, line_range, max_address_incr; + uint8_t opcode_base; + uint32_t last_line, last_file, last_offset; + uint32_t first_file; + int line, file, offset; + gboolean is_hidden; +} StatementMachine; + +static gboolean +check_line (StatementMachine *stm, int offset, MonoDebugSourceLocation **location) +{ + gchar *source_file = NULL; + + if (stm->offset <= offset) { + stm->last_offset = stm->offset; + stm->last_file = stm->file; + if (stm->line != 0xfeefee) + stm->last_line = stm->line; + return FALSE; + } + + if (stm->last_file) { + int offset = read32(&(stm->symfile->offset_table->_source_table_offset)) + + (stm->last_file - 1) * sizeof (MonoSymbolFileSourceEntry); + MonoSymbolFileSourceEntry *se = (MonoSymbolFileSourceEntry *) + (stm->symfile->raw_contents + offset); + + source_file = read_string (stm->symfile->raw_contents + read32(&(se->_data_offset)), NULL); + } + + if (stm->last_line == 0) { + /* + * The IL offset is less than the first IL offset which has a corresponding + * source line. + */ + *location = NULL; + return TRUE; + } + + *location = g_new0 (MonoDebugSourceLocation, 1); + (*location)->source_file = source_file; + (*location)->row = stm->last_line; + (*location)->il_offset = stm->last_offset; + return TRUE; +} + +/** + * mono_debug_symfile_lookup_location: + * \param minfo A \c MonoDebugMethodInfo which can be retrieved by \c mono_debug_lookup_method. + * \param offset IL offset within the corresponding method's CIL code. + * + * This function is similar to \c mono_debug_lookup_location, but we + * already looked up the method and also already did the + * native address -> IL offset mapping. + */ +MonoDebugSourceLocation * +mono_debug_symfile_lookup_location (MonoDebugMethodInfo *minfo, uint32_t offset) +{ + MonoDebugSourceLocation *location = NULL; + MonoSymbolFile *symfile; + const unsigned char *ptr; + StatementMachine stm; + +#define DW_LNS_copy 1 +#define DW_LNS_advance_pc 2 +#define DW_LNS_advance_line 3 +#define DW_LNS_set_file 4 +#define DW_LNS_const_add_pc 8 + +#define DW_LNE_end_sequence 1 +#define DW_LNE_MONO_negate_is_hidden 0x40 + +#define DW_LNE_MONO__extensions_start 0x40 +#define DW_LNE_MONO__extensions_end 0x7f + + if ((symfile = minfo->handle->symfile) == NULL) + return NULL; + + stm.line_base = read32 (&symfile->offset_table->_line_number_table_line_base); + stm.line_range = read32 (&symfile->offset_table->_line_number_table_line_range); + stm.opcode_base = (uint8_t) read32 (&symfile->offset_table->_line_number_table_opcode_base); + stm.max_address_incr = (255 - stm.opcode_base) / stm.line_range; + + mono_debugger_lock (); + + ptr = symfile->raw_contents + minfo->lnt_offset; + + stm.symfile = symfile; + stm.offset = stm.last_offset = 0; + stm.last_file = 0; + stm.last_line = 0; + stm.first_file = 0; + stm.file = 1; + stm.line = 1; + stm.is_hidden = FALSE; + + while (TRUE) { + uint8_t opcode = *ptr++; + + if (opcode == 0) { + uint8_t size = *ptr++; + const unsigned char *end_ptr = ptr + size; + + opcode = *ptr++; + + if (opcode == DW_LNE_end_sequence) { + if (check_line (&stm, -1, &location)) + goto out_success; + break; + } else if (opcode == DW_LNE_MONO_negate_is_hidden) { + stm.is_hidden = !stm.is_hidden; + } else if ((opcode >= DW_LNE_MONO__extensions_start) && + (opcode <= DW_LNE_MONO__extensions_end)) { + ; // reserved for future extensions + } else { + g_warning ("Unknown extended opcode %x in LNT", opcode); + } + + ptr = end_ptr; + continue; + } else if (opcode < stm.opcode_base) { + switch (opcode) { + case DW_LNS_copy: + if (check_line (&stm, offset, &location)) + goto out_success; + break; + case DW_LNS_advance_pc: + stm.offset += read_leb128 (ptr, &ptr); + break; + case DW_LNS_advance_line: + stm.line += read_leb128 (ptr, &ptr); + break; + case DW_LNS_set_file: + stm.file = read_leb128 (ptr, &ptr); + break; + case DW_LNS_const_add_pc: + stm.offset += stm.max_address_incr; + break; + default: + g_warning ("Unknown standard opcode %x in LNT", opcode); + goto error_out; + } + } else { + opcode -= stm.opcode_base; + + stm.offset += opcode / stm.line_range; + stm.line += stm.line_base + (opcode % stm.line_range); + + if (check_line (&stm, offset, &location)) + goto out_success; + } + } + + error_out: + mono_debugger_unlock (); + return NULL; + + out_success: + mono_debugger_unlock (); + return location; +} + +static void +add_line (StatementMachine *stm, GPtrArray *il_offset_array, GPtrArray *line_number_array, GPtrArray *source_file_array, GPtrArray *hidden_array) +{ + g_ptr_array_add (il_offset_array, GUINT_TO_POINTER (stm->offset)); + g_ptr_array_add (line_number_array, GUINT_TO_POINTER (stm->line)); + g_ptr_array_add (source_file_array, GUINT_TO_POINTER (stm->file)); + g_ptr_array_add (hidden_array, GUINT_TO_POINTER (stm->is_hidden || stm->line <= 0)); + + if (!stm->is_hidden && !stm->first_file) + stm->first_file = stm->file; +} + +/** + * mono_debug_symfile_free_location: + * + * Free a \c MonoDebugSourceLocation returned by + * \c mono_debug_symfile_lookup_location + */ +void +mono_debug_symfile_free_location (MonoDebugSourceLocation *location) +{ + g_free (location->source_file); + g_free (location); +} + +/* + * LOCKING: Assumes the debugger lock is held. + */ +static MonoDebugSourceInfo* +get_source_info (MonoSymbolFile *symfile, int index) +{ + MonoDebugSourceInfo *info; + + info = (MonoDebugSourceInfo *)g_hash_table_lookup (symfile->source_hash, GUINT_TO_POINTER (index)); + if (!info) { + int offset = read32(&(symfile->offset_table->_source_table_offset)) + + (index - 1) * sizeof (MonoSymbolFileSourceEntry); + MonoSymbolFileSourceEntry *se = (MonoSymbolFileSourceEntry *) + (symfile->raw_contents + offset); + const uint8_t *ptr = symfile->raw_contents + read32(&(se->_data_offset)); + + info = g_new0 (MonoDebugSourceInfo, 1); + info->source_file = read_string (ptr, &ptr); + info->guid = (guint8 *)g_malloc0 (16); + memcpy (info->guid, ptr, 16); + ptr += 16; + info->hash = (guint8 *)g_malloc0 (16); + memcpy (info->hash, ptr, 16); + ptr += 16; + g_hash_table_insert (symfile->source_hash, GUINT_TO_POINTER (index), info); + } + return info; +} + +typedef enum { + LNT_FLAG_HAS_COLUMN_INFO = 1 << 1, + LNT_FLAG_HAS_END_INFO = 1 << 2, +} LineNumberTableFlags; + +static LineNumberTableFlags +method_get_lnt_flags (MonoDebugMethodInfo *minfo) +{ + MonoSymbolFile *symfile; + const unsigned char *ptr; + guint32 flags; + + if ((symfile = minfo->handle->symfile) == NULL) + return (LineNumberTableFlags)0; + + ptr = symfile->raw_contents + minfo->data_offset; + + /* Has to read 'flags' which is preceeded by a bunch of other data */ + /* compile_unit_index */ + read_leb128 (ptr, &ptr); + /* local variable table offset */ + read_leb128 (ptr, &ptr); + /* namespace id */ + read_leb128 (ptr, &ptr); + /* code block table offset */ + read_leb128 (ptr, &ptr); + /* scope variable table offset */ + read_leb128 (ptr, &ptr); + /* real name offset */ + read_leb128 (ptr, &ptr); + + flags = read_leb128 (ptr, &ptr); + return (LineNumberTableFlags)flags; +} + +/* + * mono_debug_symfile_get_seq_points: + * + * On return, SOURCE_FILE_LIST will point to a GPtrArray of MonoDebugSourceFile + * structures, and SOURCE_FILES will contain indexes into this array. + * The MonoDebugSourceFile structures are owned by this module. + */ +void +mono_debug_symfile_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrArray **source_file_list, int **source_files, MonoSymSeqPoint **seq_points, int *n_seq_points) +{ + // FIXME: Unify this with mono_debug_symfile_lookup_location + MonoSymbolFile *symfile; + const unsigned char *ptr; + StatementMachine stm; + uint32_t i, j, n; + LineNumberTableFlags flags; + GPtrArray *il_offset_array, *line_number_array, *source_file_array, *hidden_array; + gboolean has_column_info, has_end_info; + MonoSymSeqPoint *sps; + + if (source_file_list) + *source_file_list = NULL; + if (seq_points) + *seq_points = NULL; + if (n_seq_points) + *n_seq_points = 0; + if (source_files) + *source_files = NULL; + if (source_file) + *source_file = NULL; + + if ((symfile = minfo->handle->symfile) == NULL) + return; + + flags = method_get_lnt_flags (minfo); + has_column_info = (flags & LNT_FLAG_HAS_COLUMN_INFO) > 0; + has_end_info = (flags & LNT_FLAG_HAS_END_INFO) > 0; + + il_offset_array = g_ptr_array_new (); + line_number_array = g_ptr_array_new (); + source_file_array = g_ptr_array_new (); + hidden_array = g_ptr_array_new(); + + stm.line_base = read32 (&symfile->offset_table->_line_number_table_line_base); + stm.line_range = read32 (&symfile->offset_table->_line_number_table_line_range); + stm.opcode_base = (uint8_t) read32 (&symfile->offset_table->_line_number_table_opcode_base); + stm.max_address_incr = (255 - stm.opcode_base) / stm.line_range; + + mono_debugger_lock (); + + ptr = symfile->raw_contents + minfo->lnt_offset; + + stm.symfile = symfile; + stm.offset = stm.last_offset = 0; + stm.last_file = 0; + stm.last_line = 0; + stm.first_file = 0; + stm.file = 1; + stm.line = 1; + stm.is_hidden = FALSE; + + while (TRUE) { + uint8_t opcode = *ptr++; + + if (opcode == 0) { + uint8_t size = *ptr++; + const unsigned char *end_ptr = ptr + size; + + opcode = *ptr++; + + if (opcode == DW_LNE_end_sequence) { + if (il_offset_array->len == 0) + /* Empty table */ + break; + break; + } else if (opcode == DW_LNE_MONO_negate_is_hidden) { + stm.is_hidden = !stm.is_hidden; + } else if ((opcode >= DW_LNE_MONO__extensions_start) && + (opcode <= DW_LNE_MONO__extensions_end)) { + ; // reserved for future extensions + } else { + g_warning ("Unknown extended opcode %x in LNT", opcode); + } + + ptr = end_ptr; + continue; + } else if (opcode < stm.opcode_base) { + switch (opcode) { + case DW_LNS_copy: + add_line (&stm, il_offset_array, line_number_array, source_file_array, hidden_array); + break; + case DW_LNS_advance_pc: + stm.offset += read_leb128 (ptr, &ptr); + break; + case DW_LNS_advance_line: + stm.line += read_leb128 (ptr, &ptr); + break; + case DW_LNS_set_file: + stm.file = read_leb128 (ptr, &ptr); + break; + case DW_LNS_const_add_pc: + stm.offset += stm.max_address_incr; + break; + default: + g_warning ("Unknown standard opcode %x in LNT", opcode); + g_assert_not_reached (); + } + } else { + opcode -= stm.opcode_base; + + stm.offset += opcode / stm.line_range; + stm.line += stm.line_base + (opcode % stm.line_range); + + add_line (&stm, il_offset_array, line_number_array, source_file_array, hidden_array); + } + } + + if (!stm.file && stm.first_file) + stm.file = stm.first_file; + + if (stm.file && source_file) { + int offset = read32(&(stm.symfile->offset_table->_source_table_offset)) + + (stm.file - 1) * sizeof (MonoSymbolFileSourceEntry); + MonoSymbolFileSourceEntry *se = (MonoSymbolFileSourceEntry *) + (stm.symfile->raw_contents + offset); + + if (source_file) + *source_file = read_string (stm.symfile->raw_contents + read32(&(se->_data_offset)), NULL); + } + + if (source_file_list) { + int file, last_file = 0; + + *source_file_list = g_ptr_array_new (); + if (source_files) + *source_files = (int *)g_malloc (il_offset_array->len * sizeof (int)); + + for (i = 0; i < il_offset_array->len; ++i) { + file = GPOINTER_TO_UINT (g_ptr_array_index (source_file_array, i)); + if (file && file != last_file) { + MonoDebugSourceInfo *info = get_source_info (symfile, file); + + g_ptr_array_add (*source_file_list, info); + } + last_file = file; + if (source_files) + (*source_files) [i] = (*source_file_list)->len - 1; + } + } + + if (n_seq_points) { + g_assert (seq_points); + + n = il_offset_array->len; + for (i = 0; i < il_offset_array->len; i++) { + if (GPOINTER_TO_UINT (g_ptr_array_index (hidden_array, i))) { + n --; + } + } + + *n_seq_points = n; + *seq_points = sps = g_new0 (MonoSymSeqPoint, n); + j = 0; + for (i = 0; i < il_offset_array->len; ++i) { + MonoSymSeqPoint *sp = &(sps [j]); + if (!GPOINTER_TO_UINT (g_ptr_array_index (hidden_array, i))) { + sp->il_offset = GPOINTER_TO_UINT (g_ptr_array_index (il_offset_array, i)); + sp->line = GPOINTER_TO_UINT (g_ptr_array_index (line_number_array, i)); + sp->column = -1; + sp->end_line = -1; + sp->end_column = -1; + j ++; + } + } + + if (has_column_info) { + j = 0; + for (i = 0; i < il_offset_array->len; ++i) { + MonoSymSeqPoint *sp = &(sps [j]); + int column = read_leb128 (ptr, &ptr); + if (!GPOINTER_TO_UINT (g_ptr_array_index (hidden_array, i))) { + sp->column = column; + j++; + } + } + } + + if (has_end_info) { + j = 0; + for (i = 0; i < il_offset_array->len; ++i) { + MonoSymSeqPoint *sp = &(sps [j]); + int end_row, end_column = -1; + + end_row = read_leb128 (ptr, &ptr); + if (end_row != 0xffffff) { + end_row += GPOINTER_TO_UINT (g_ptr_array_index (line_number_array, i)); + end_column = read_leb128 (ptr, &ptr); + if (!GPOINTER_TO_UINT (g_ptr_array_index (hidden_array, i))) { + sp->end_line = end_row; + sp->end_column = end_column; + j++; + } + } + } + } + } + + g_ptr_array_free (il_offset_array, TRUE); + g_ptr_array_free (line_number_array, TRUE); + g_ptr_array_free (hidden_array, TRUE); + + mono_debugger_unlock (); + return; +} + +static int +compare_method (const void *key, const void *object) +{ + uint32_t token = GPOINTER_TO_UINT (key); + MonoSymbolFileMethodEntry *me = (MonoSymbolFileMethodEntry*)object; + + return token - read32(&(me->_token)); +} + +/** + * mono_debug_symfile_lookup_method: + */ +MonoDebugMethodInfo * +mono_debug_symfile_lookup_method (MonoDebugHandle *handle, MonoMethod *method) +{ + MonoSymbolFileMethodEntry *first_ie, *ie; + MonoDebugMethodInfo *minfo; + MonoSymbolFile *symfile = handle->symfile; + + if (!symfile->method_hash) + return NULL; + + if (handle->image != mono_class_get_image (mono_method_get_class (method))) + return NULL; + + mono_debugger_lock (); + + minfo = (MonoDebugMethodInfo *)g_hash_table_lookup (symfile->method_hash, method); + if (minfo) { + mono_debugger_unlock (); + return minfo; + } + + first_ie = (MonoSymbolFileMethodEntry *) + (symfile->raw_contents + read32(&(symfile->offset_table->_method_table_offset))); + + ie = (MonoSymbolFileMethodEntry *)mono_binary_search (GUINT_TO_POINTER (mono_method_get_token (method)), first_ie, + read32(&(symfile->offset_table->_method_count)), + sizeof (MonoSymbolFileMethodEntry), compare_method); + + if (!ie) { + mono_debugger_unlock (); + return NULL; + } + + minfo = g_new0 (MonoDebugMethodInfo, 1); + minfo->index = (ie - first_ie) + 1; + minfo->method = method; + minfo->handle = handle; + + minfo->data_offset = read32 (&(ie->_data_offset)); + minfo->lnt_offset = read32 (&(ie->_line_number_table)); + + g_hash_table_insert (symfile->method_hash, method, minfo); + + mono_debugger_unlock (); + return minfo; +} + +/** + * mono_debug_symfile_lookup_locals: + * + * Return information about the local variables of \p minfo from the symbol file. + * Return NULL if no information can be found. + * The result should be freed using \c mono_debug_symfile_free_locals. + */ +MonoDebugLocalsInfo* +mono_debug_symfile_lookup_locals (MonoDebugMethodInfo *minfo) +{ + MonoSymbolFile *symfile = minfo->handle->symfile; + const uint8_t *p; + int i, len, locals_offset, num_locals, block_index; + int code_block_table_offset; + MonoDebugLocalsInfo *res; + + if (!symfile) + return NULL; + + p = symfile->raw_contents + minfo->data_offset; + + /* compile_unit_index = */ read_leb128 (p, &p); + locals_offset = read_leb128 (p, &p); + /* namespace_id = */ read_leb128 (p, &p); + code_block_table_offset = read_leb128 (p, &p); + + res = g_new0 (MonoDebugLocalsInfo, 1); + + p = symfile->raw_contents + code_block_table_offset; + res->num_blocks = read_leb128 (p, &p); + res->code_blocks = g_new0 (MonoDebugCodeBlock, res->num_blocks); + for (i = 0; i < res->num_blocks; ++i) { + res->code_blocks [i].type = read_leb128 (p, &p); + res->code_blocks [i].parent = read_leb128 (p, &p); + res->code_blocks [i].start_offset = read_leb128 (p, &p); + res->code_blocks [i].end_offset = read_leb128 (p, &p); + } + + p = symfile->raw_contents + locals_offset; + num_locals = read_leb128 (p, &p); + + res->num_locals = num_locals; + res->locals = g_new0 (MonoDebugLocalVar, num_locals); + + for (i = 0; i < num_locals; ++i) { + res->locals [i].index = read_leb128 (p, &p); + len = read_leb128 (p, &p); + res->locals [i].name = (char *)g_malloc (len + 1); + memcpy (res->locals [i].name, p, len); + res->locals [i].name [len] = '\0'; + p += len; + block_index = read_leb128 (p, &p); + if (block_index >= 1 && block_index <= res->num_blocks) + res->locals [i].block = &res->code_blocks [block_index - 1]; + } + + return res; +} + +#else /* DISABLE_MDB */ + +MonoSymbolFile * +mono_debug_open_mono_symbols (MonoDebugHandle *handle, const uint8_t *raw_contents, + int size, gboolean in_the_debugger) +{ + return NULL; +} + +void +mono_debug_close_mono_symbol_file (MonoSymbolFile *symfile) +{ +} + +mono_bool +mono_debug_symfile_is_loaded (MonoSymbolFile *symfile) +{ + return FALSE; +} + +MonoDebugMethodInfo * +mono_debug_symfile_lookup_method (MonoDebugHandle *handle, MonoMethod *method) +{ + return NULL; +} + +void +mono_debug_symfile_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrArray **source_file_list, int **source_files, MonoSymSeqPoint **seq_points, int *n_seq_points) +{ + g_assert_not_reached (); +} + +MonoDebugSourceLocation * +mono_debug_symfile_lookup_location (MonoDebugMethodInfo *minfo, uint32_t offset) +{ + return NULL; +} + +MonoDebugLocalsInfo* +mono_debug_symfile_lookup_locals (MonoDebugMethodInfo *minfo) +{ + return NULL; +} + +void +mono_debug_symfile_free_location (MonoDebugSourceLocation *location) +{ +} + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/debug-mono-symfile.h b/unity-2019.4.24f1-mbe/mono/metadata/debug-mono-symfile.h new file mode 100644 index 000000000..cebc943d8 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/debug-mono-symfile.h @@ -0,0 +1,114 @@ +/** + * \file + * This header is only installed for use by the debugger: + * the structures and the API declared here are not supported. + * Copyright 2012 Xamarin Inc (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#ifndef __MONO_DEBUG_MONO_SYMFILE_H__ +#define __MONO_DEBUG_MONO_SYMFILE_H__ + +#include +#include +#include +#include +#include + +typedef struct MonoSymbolFileOffsetTable MonoSymbolFileOffsetTable; +typedef struct MonoSymbolFileLineNumberEntry MonoSymbolFileLineNumberEntry; +typedef struct MonoSymbolFileMethodAddress MonoSymbolFileMethodAddress; +typedef struct MonoSymbolFileDynamicTable MonoSymbolFileDynamicTable; +typedef struct MonoSymbolFileSourceEntry MonoSymbolFileSourceEntry; +typedef struct MonoSymbolFileMethodEntry MonoSymbolFileMethodEntry; + +/* Keep in sync with OffsetTable in mcs/class/Mono.CSharp.Debugger/MonoSymbolTable.cs */ +struct MonoSymbolFileOffsetTable { + uint32_t _total_file_size; + uint32_t _data_section_offset; + uint32_t _data_section_size; + uint32_t _compile_unit_count; + uint32_t _compile_unit_table_offset; + uint32_t _compile_unit_table_size; + uint32_t _source_count; + uint32_t _source_table_offset; + uint32_t _source_table_size; + uint32_t _method_count; + uint32_t _method_table_offset; + uint32_t _method_table_size; + uint32_t _type_count; + uint32_t _anonymous_scope_count; + uint32_t _anonymous_scope_table_offset; + uint32_t _anonymous_scope_table_size; + uint32_t _line_number_table_line_base; + uint32_t _line_number_table_line_range; + uint32_t _line_number_table_opcode_base; + uint32_t _is_aspx_source; +}; + +struct MonoSymbolFileSourceEntry { + uint32_t _index; + uint32_t _data_offset; +}; + +struct MonoSymbolFileMethodEntry { + uint32_t _token; + uint32_t _data_offset; + uint32_t _line_number_table; +}; + +struct MonoSymbolFileMethodAddress { + uint32_t size; + const uint8_t *start_address; + const uint8_t *end_address; + const uint8_t *method_start_address; + const uint8_t *method_end_address; + const uint8_t *wrapper_address; + uint32_t has_this; + uint32_t num_params; + uint32_t variable_table_offset; + uint32_t type_table_offset; + uint32_t num_line_numbers; + uint32_t line_number_offset; + uint8_t data [MONO_ZERO_LEN_ARRAY]; +}; + +#define MONO_SYMBOL_FILE_MAJOR_VERSION 50 +#define MONO_SYMBOL_FILE_MINOR_VERSION 0 +#define MONO_SYMBOL_FILE_MAGIC 0x45e82623fd7fa614ULL + +MONO_BEGIN_DECLS + +MONO_API MonoSymbolFile * +mono_debug_open_mono_symbols (MonoDebugHandle *handle, + const uint8_t *raw_contents, + int size, + mono_bool in_the_debugger); + +MONO_API void +mono_debug_close_mono_symbol_file (MonoSymbolFile *symfile); + +MONO_API mono_bool +mono_debug_symfile_is_loaded (MonoSymbolFile *symfile); + +MONO_API MonoDebugSourceLocation * +mono_debug_symfile_lookup_location (MonoDebugMethodInfo *minfo, + uint32_t offset); + +MONO_API void +mono_debug_symfile_free_location (MonoDebugSourceLocation *location); + +MONO_API MonoDebugMethodInfo * +mono_debug_symfile_lookup_method (MonoDebugHandle *handle, + MonoMethod *method); + +MONO_API MonoDebugLocalsInfo* +mono_debug_symfile_lookup_locals (MonoDebugMethodInfo *minfo); + +void +mono_debug_symfile_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrArray **source_file_list, int **source_files, MonoSymSeqPoint **seq_points, int *n_seq_points); + +MONO_END_DECLS + +#endif /* __MONO_SYMFILE_H__ */ + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/decimal-ms.c b/unity-2019.4.24f1-mbe/mono/metadata/decimal-ms.c new file mode 100644 index 000000000..c87eac688 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/decimal-ms.c @@ -0,0 +1,3105 @@ +/** + * \file + * Copyright (c) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + * + * Copyright 2015 Xamarin Inc + * + * File: decimal.c + * + * Ported from C++ to C and adjusted to Mono runtime + * + * Pending: + * DoToCurrency (they look like new methods we do not have) + */ +#ifndef DISABLE_DECIMAL +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#ifdef _MSC_VER +#include +#endif +#include "decimal-ms.h" +#include "number-ms.h" + +#define min(a, b) (((a) < (b)) ? (a) : (b)) + +typedef enum { + MONO_DECIMAL_OK, + MONO_DECIMAL_OVERFLOW, + MONO_DECIMAL_INVALID_ARGUMENT, + MONO_DECIMAL_DIVBYZERO, + MONO_DECIMAL_ARGUMENT_OUT_OF_RANGE +} MonoDecimalStatus; + +#ifndef FC_GC_POLL +# define FC_GC_POLL() +#endif + +static const uint32_t ten_to_nine = 1000000000U; +static const uint32_t ten_to_ten_div_4 = 2500000000U; +#define POWER10_MAX 9 +#define DECIMAL_NEG ((uint8_t)0x80) +#define DECMAX 28 +#define DECIMAL_SCALE(dec) ((dec).u.u.scale) +#define DECIMAL_SIGN(dec) ((dec).u.u.sign) +#define DECIMAL_SIGNSCALE(dec) ((dec).u.signscale) +#define DECIMAL_LO32(dec) ((dec).v.v.Lo32) +#define DECIMAL_MID32(dec) ((dec).v.v.Mid32) +#define DECIMAL_HI32(dec) ((dec).Hi32) +#if G_BYTE_ORDER != G_LITTLE_ENDIAN +# define DECIMAL_LO64_GET(dec) (((uint64_t)((dec).v.v.Mid32) << 32) | (dec).v.v.Lo32) +# define DECIMAL_LO64_SET(dec,value) {(dec).v.v.Lo32 = (value); (dec).v.v.Mid32 = ((value) >> 32); } +#else +# define DECIMAL_LO64_GET(dec) ((dec).v.Lo64) +# define DECIMAL_LO64_SET(dec,value) {(dec).v.Lo64 = value; } +#endif + +#define DECIMAL_SETZERO(dec) {DECIMAL_LO32(dec) = 0; DECIMAL_MID32(dec) = 0; DECIMAL_HI32(dec) = 0; DECIMAL_SIGNSCALE(dec) = 0;} +#define COPYDEC(dest, src) {DECIMAL_SIGNSCALE(dest) = DECIMAL_SIGNSCALE(src); DECIMAL_HI32(dest) = DECIMAL_HI32(src); \ + DECIMAL_MID32(dest) = DECIMAL_MID32(src); DECIMAL_LO32(dest) = DECIMAL_LO32(src); } + +#define DEC_SCALE_MAX 28 +#define POWER10_MAX 9 + +#define OVFL_MAX_9_HI 4 +#define OVFL_MAX_9_MID 1266874889 +#define OVFL_MAX_9_LO 3047500985u + +#define OVFL_MAX_5_HI 42949 +#define OVFL_MAX_5_MID 2890341191 + +#define OVFL_MAX_1_HI 429496729 + +typedef union { + uint64_t int64; + struct { +#if BYTE_ORDER == G_BIG_ENDIAN + uint32_t Hi; + uint32_t Lo; +#else + uint32_t Lo; + uint32_t Hi; +#endif + } u; +} SPLIT64; + +static const SPLIT64 ten_to_eighteen = { 1000000000000000000ULL }; + +const MonoDouble_double ds2to64 = { .s = { .sign = 0, .exp = MONO_DOUBLE_BIAS + 65, .mantHi = 0, .mantLo = 0 } }; + +// +// Data tables +// + +static const uint32_t power10 [POWER10_MAX+1] = { + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 +}; + + +static const double double_power10[] = { + 1, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, + 1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29, + 1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38, 1e39, + 1e40, 1e41, 1e42, 1e43, 1e44, 1e45, 1e46, 1e47, 1e48, 1e49, + 1e50, 1e51, 1e52, 1e53, 1e54, 1e55, 1e56, 1e57, 1e58, 1e59, + 1e60, 1e61, 1e62, 1e63, 1e64, 1e65, 1e66, 1e67, 1e68, 1e69, + 1e70, 1e71, 1e72, 1e73, 1e74, 1e75, 1e76, 1e77, 1e78, 1e79, + 1e80 }; + +const SPLIT64 sdl_power10[] = { {10000000000ULL}, // 1E10 + {100000000000ULL}, // 1E11 + {1000000000000ULL}, // 1E12 + {10000000000000ULL}, // 1E13 + {100000000000000ULL} }; // 1E14 + +static const uint64_t long_power10[] = { + 1, + 10ULL, + 100ULL, + 1000ULL, + 10000ULL, + 100000ULL, + 1000000ULL, + 10000000ULL, + 100000000ULL, + 1000000000ULL, + 10000000000ULL, + 100000000000ULL, + 1000000000000ULL, + 10000000000000ULL, + 100000000000000ULL, + 1000000000000000ULL, + 10000000000000000ULL, + 100000000000000000ULL, + 1000000000000000000ULL, + 10000000000000000000ULL}; + +typedef struct { + uint32_t Hi, Mid, Lo; +} DECOVFL; + +const DECOVFL power_overflow[] = { +// This is a table of the largest values that can be in the upper two +// ULONGs of a 96-bit number that will not overflow when multiplied +// by a given power. For the upper word, this is a table of +// 2^32 / 10^n for 1 <= n <= 9. For the lower word, this is the +// remaining fraction part * 2^32. 2^32 = 4294967296. +// + { 429496729u, 2576980377u, 2576980377u }, // 10^1 remainder 0.6 + { 42949672u, 4123168604u, 687194767u }, // 10^2 remainder 0.16 + { 4294967u, 1271310319u, 2645699854u }, // 10^3 remainder 0.616 + { 429496u, 3133608139u, 694066715u }, // 10^4 remainder 0.1616 + { 42949u, 2890341191u, 2216890319u }, // 10^5 remainder 0.51616 + { 4294u, 4154504685u, 2369172679u }, // 10^6 remainder 0.551616 + { 429u, 2133437386u, 4102387834u }, // 10^7 remainder 0.9551616 + { 42u, 4078814305u, 410238783u }, // 10^8 remainder 0.09991616 + { 4u, 1266874889u, 3047500985u }, // 10^9 remainder 0.709551616 +}; + + +#define UInt32x32To64(a, b) ((uint64_t)((uint32_t)(a)) * (uint64_t)((uint32_t)(b))) +#define Div64by32(num, den) ((uint32_t)((uint64_t)(num) / (uint32_t)(den))) +#define Mod64by32(num, den) ((uint32_t)((uint64_t)(num) % (uint32_t)(den))) + +static double +fnDblPower10(int ix) +{ + const int maxIx = (sizeof(double_power10)/sizeof(double_power10[0])); + g_assert(ix >= 0); + if (ix < maxIx) + return double_power10[ix]; + return pow(10.0, ix); +} // double fnDblPower10() + + +static inline int64_t +DivMod32by32(int32_t num, int32_t den) +{ + SPLIT64 sdl; + + sdl.u.Lo = num / den; + sdl.u.Hi = num % den; + return sdl.int64; +} + +static inline int64_t +DivMod64by32(int64_t num, int32_t den) +{ + SPLIT64 sdl; + + sdl.u.Lo = Div64by32(num, den); + sdl.u.Hi = Mod64by32(num, den); + return sdl.int64; +} + +static uint64_t +UInt64x64To128(SPLIT64 op1, SPLIT64 op2, uint64_t *hi) +{ + SPLIT64 tmp1; + SPLIT64 tmp2; + SPLIT64 tmp3; + + tmp1.int64 = UInt32x32To64(op1.u.Lo, op2.u.Lo); // lo partial prod + tmp2.int64 = UInt32x32To64(op1.u.Lo, op2.u.Hi); // mid 1 partial prod + tmp1.u.Hi += tmp2.u.Lo; + if (tmp1.u.Hi < tmp2.u.Lo) // test for carry + tmp2.u.Hi++; + tmp3.int64 = UInt32x32To64(op1.u.Hi, op2.u.Hi) + (uint64_t)tmp2.u.Hi; + tmp2.int64 = UInt32x32To64(op1.u.Hi, op2.u.Lo); + tmp1.u.Hi += tmp2.u.Lo; + if (tmp1.u.Hi < tmp2.u.Lo) // test for carry + tmp2.u.Hi++; + tmp3.int64 += (uint64_t)tmp2.u.Hi; + + *hi = tmp3.int64; + return tmp1.int64; +} + +/** +* FullDiv64By32: +* +* Entry: +* pdlNum - Pointer to 64-bit dividend +* ulDen - 32-bit divisor +* +* Purpose: +* Do full divide, yielding 64-bit result and 32-bit remainder. +* +* Exit: +* Quotient overwrites dividend. +* Returns remainder. +* +* Exceptions: +* None. +*/ +// Was: FullDiv64By32 +static uint32_t +FullDiv64By32 (uint64_t *num, uint32_t den) +{ + SPLIT64 tmp; + SPLIT64 res; + + tmp.int64 = *num; + res.u.Hi = 0; + + if (tmp.u.Hi >= den) { + // DivMod64by32 returns quotient in Lo, remainder in Hi. + // + res.u.Lo = tmp.u.Hi; + res.int64 = DivMod64by32(res.int64, den); + tmp.u.Hi = res.u.Hi; + res.u.Hi = res.u.Lo; + } + + tmp.int64 = DivMod64by32(tmp.int64, den); + res.u.Lo = tmp.u.Lo; + *num = res.int64; + return tmp.u.Hi; +} + +/*** + * SearchScale + * + * Entry: + * res_hi - Top uint32_t of quotient + * res_mid - Middle uint32_t of quotient + * res_lo - Bottom uint32_t of quotient + * scale - Scale factor of quotient, range -DEC_SCALE_MAX to DEC_SCALE_MAX + * + * Purpose: + * Determine the max power of 10, <= 9, that the quotient can be scaled + * up by and still fit in 96 bits. + * + * Exit: + * Returns power of 10 to scale by, -1 if overflow error. + * + ***********************************************************************/ + +static int +SearchScale(uint32_t res_hi, uint32_t res_mid, uint32_t res_lo, int scale) +{ + int cur_scale; + + // Quick check to stop us from trying to scale any more. + // + if (res_hi > OVFL_MAX_1_HI || scale >= DEC_SCALE_MAX) { + cur_scale = 0; + goto HaveScale; + } + + if (scale > DEC_SCALE_MAX - 9) { + // We can't scale by 10^9 without exceeding the max scale factor. + // See if we can scale to the max. If not, we'll fall into + // standard search for scale factor. + // + cur_scale = DEC_SCALE_MAX - scale; + if (res_hi < power_overflow[cur_scale - 1].Hi) + goto HaveScale; + + if (res_hi == power_overflow[cur_scale - 1].Hi) { + UpperEq: + if (res_mid > power_overflow[cur_scale - 1].Mid || + (res_mid == power_overflow[cur_scale - 1].Mid && res_lo > power_overflow[cur_scale - 1].Lo)) { + cur_scale--; + } + goto HaveScale; + } + } else if (res_hi < OVFL_MAX_9_HI || (res_hi == OVFL_MAX_9_HI && res_mid < OVFL_MAX_9_MID) || (res_hi == OVFL_MAX_9_HI && res_mid == OVFL_MAX_9_MID && res_lo <= OVFL_MAX_9_LO)) + return 9; + + // Search for a power to scale by < 9. Do a binary search + // on power_overflow[]. + // + cur_scale = 5; + if (res_hi < OVFL_MAX_5_HI) + cur_scale = 7; + else if (res_hi > OVFL_MAX_5_HI) + cur_scale = 3; + else + goto UpperEq; + + // cur_scale is 3 or 7. + // + if (res_hi < power_overflow[cur_scale - 1].Hi) + cur_scale++; + else if (res_hi > power_overflow[cur_scale - 1].Hi) + cur_scale--; + else + goto UpperEq; + + // cur_scale is 2, 4, 6, or 8. + // + // In all cases, we already found we could not use the power one larger. + // So if we can use this power, it is the biggest, and we're done. If + // we can't use this power, the one below it is correct for all cases + // unless it's 10^1 -- we might have to go to 10^0 (no scaling). + // + if (res_hi > power_overflow[cur_scale - 1].Hi) + cur_scale--; + + if (res_hi == power_overflow[cur_scale - 1].Hi) + goto UpperEq; + +HaveScale: + // cur_scale = largest power of 10 we can scale by without overflow, + // cur_scale < 9. See if this is enough to make scale factor + // positive if it isn't already. + // + if (cur_scale + scale < 0) + cur_scale = -1; + + return cur_scale; +} + + +/** +* Div96By32 +* +* Entry: +* rgulNum - Pointer to 96-bit dividend as array of uint32_ts, least-sig first +* ulDen - 32-bit divisor. +* +* Purpose: +* Do full divide, yielding 96-bit result and 32-bit remainder. +* +* Exit: +* Quotient overwrites dividend. +* Returns remainder. +* +* Exceptions: +* None. +* +*/ +static uint32_t +Div96By32(uint32_t *num, uint32_t den) +{ + SPLIT64 tmp; + + tmp.u.Hi = 0; + + if (num[2] != 0) + goto Div3Word; + + if (num[1] >= den) + goto Div2Word; + + tmp.u.Hi = num[1]; + num[1] = 0; + goto Div1Word; + +Div3Word: + tmp.u.Lo = num[2]; + tmp.int64 = DivMod64by32(tmp.int64, den); + num[2] = tmp.u.Lo; +Div2Word: + tmp.u.Lo = num[1]; + tmp.int64 = DivMod64by32(tmp.int64, den); + num[1] = tmp.u.Lo; +Div1Word: + tmp.u.Lo = num[0]; + tmp.int64 = DivMod64by32(tmp.int64, den); + num[0] = tmp.u.Lo; + return tmp.u.Hi; +} + +/*** + * DecFixInt + * + * Entry: + * pdecRes - Pointer to Decimal result location + * operand - Pointer to Decimal operand + * + * Purpose: + * Chop the value to integer. Return remainder so Int() function + * can round down if non-zero. + * + * Exit: + * Returns remainder. + * + * Exceptions: + * None. + * + ***********************************************************************/ + +static uint32_t +DecFixInt(MonoDecimal * result, MonoDecimal * operand) +{ + uint32_t num[3]; + uint32_t rem; + uint32_t pwr; + int scale; + + if (operand->u.u.scale > 0) { + num[0] = operand->v.v.Lo32; + num[1] = operand->v.v.Mid32; + num[2] = operand->Hi32; + scale = operand->u.u.scale; + result->u.u.sign = operand->u.u.sign; + rem = 0; + + do { + if (scale > POWER10_MAX) + pwr = ten_to_nine; + else + pwr = power10[scale]; + + rem |= Div96By32(num, pwr); + scale -= 9; + }while (scale > 0); + + result->v.v.Lo32 = num[0]; + result->v.v.Mid32 = num[1]; + result->Hi32 = num[2]; + result->u.u.scale = 0; + + return rem; + } + + COPYDEC(*result, *operand); + // Odd, the Microsoft code does not set result->reserved to zero on this case + return 0; +} + +/** + * ScaleResult: + * + * Entry: + * res - Array of uint32_ts with value, least-significant first. + * hi_res - Index of last non-zero value in res. + * scale - Scale factor for this value, range 0 - 2 * DEC_SCALE_MAX + * + * Purpose: + * See if we need to scale the result to fit it in 96 bits. + * Perform needed scaling. Adjust scale factor accordingly. + * + * Exit: + * res updated in place, always 3 uint32_ts. + * New scale factor returned, -1 if overflow error. + * + */ +static int +ScaleResult(uint32_t *res, int hi_res, int scale) +{ + int new_scale; + int cur; + uint32_t pwr; + uint32_t tmp; + uint32_t sticky; + SPLIT64 sdlTmp; + + // See if we need to scale the result. The combined scale must + // be <= DEC_SCALE_MAX and the upper 96 bits must be zero. + // + // Start by figuring a lower bound on the scaling needed to make + // the upper 96 bits zero. hi_res is the index into res[] + // of the highest non-zero uint32_t. + // + new_scale = hi_res * 32 - 64 - 1; + if (new_scale > 0) { + + // Find the MSB. + // + tmp = res[hi_res]; + if (!(tmp & 0xFFFF0000)) { + new_scale -= 16; + tmp <<= 16; + } + if (!(tmp & 0xFF000000)) { + new_scale -= 8; + tmp <<= 8; + } + if (!(tmp & 0xF0000000)) { + new_scale -= 4; + tmp <<= 4; + } + if (!(tmp & 0xC0000000)) { + new_scale -= 2; + tmp <<= 2; + } + if (!(tmp & 0x80000000)) { + new_scale--; + tmp <<= 1; + } + + // Multiply bit position by log10(2) to figure it's power of 10. + // We scale the log by 256. log(2) = .30103, * 256 = 77. Doing this + // with a multiply saves a 96-byte lookup table. The power returned + // is <= the power of the number, so we must add one power of 10 + // to make it's integer part zero after dividing by 256. + // + // Note: the result of this multiplication by an approximation of + // log10(2) have been exhaustively checked to verify it gives the + // correct result. (There were only 95 to check...) + // + new_scale = ((new_scale * 77) >> 8) + 1; + + // new_scale = min scale factor to make high 96 bits zero, 0 - 29. + // This reduces the scale factor of the result. If it exceeds the + // current scale of the result, we'll overflow. + // + if (new_scale > scale) + return -1; + } + else + new_scale = 0; + + // Make sure we scale by enough to bring the current scale factor + // into valid range. + // + if (new_scale < scale - DEC_SCALE_MAX) + new_scale = scale - DEC_SCALE_MAX; + + if (new_scale != 0) { + // Scale by the power of 10 given by new_scale. Note that this is + // NOT guaranteed to bring the number within 96 bits -- it could + // be 1 power of 10 short. + // + scale -= new_scale; + sticky = 0; + sdlTmp.u.Hi = 0; // initialize remainder + + for (;;) { + + sticky |= sdlTmp.u.Hi; // record remainder as sticky bit + + if (new_scale > POWER10_MAX) + pwr = ten_to_nine; + else + pwr = power10[new_scale]; + + // Compute first quotient. + // DivMod64by32 returns quotient in Lo, remainder in Hi. + // + sdlTmp.int64 = DivMod64by32(res[hi_res], pwr); + res[hi_res] = sdlTmp.u.Lo; + cur = hi_res - 1; + + if (cur >= 0) { + // If first quotient was 0, update hi_res. + // + if (sdlTmp.u.Lo == 0) + hi_res--; + + // Compute subsequent quotients. + // + do { + sdlTmp.u.Lo = res[cur]; + sdlTmp.int64 = DivMod64by32(sdlTmp.int64, pwr); + res[cur] = sdlTmp.u.Lo; + cur--; + } while (cur >= 0); + + } + + new_scale -= POWER10_MAX; + if (new_scale > 0) + continue; // scale some more + + // If we scaled enough, hi_res would be 2 or less. If not, + // divide by 10 more. + // + if (hi_res > 2) { + new_scale = 1; + scale--; + continue; // scale by 10 + } + + // Round final result. See if remainder >= 1/2 of divisor. + // If remainder == 1/2 divisor, round up if odd or sticky bit set. + // + pwr >>= 1; // power of 10 always even + if ( pwr <= sdlTmp.u.Hi && (pwr < sdlTmp.u.Hi || + ((res[0] & 1) | sticky)) ) { + cur = -1; + while (++res[++cur] == 0); + + if (cur > 2) { + // The rounding caused us to carry beyond 96 bits. + // Scale by 10 more. + // + hi_res = cur; + sticky = 0; // no sticky bit + sdlTmp.u.Hi = 0; // or remainder + new_scale = 1; + scale--; + continue; // scale by 10 + } + } + + // We may have scaled it more than we planned. Make sure the scale + // factor hasn't gone negative, indicating overflow. + // + if (scale < 0) + return -1; + + return scale; + } // for(;;) + } + return scale; +} + +// Decimal multiply +// Returns: MONO_DECIMAL_OVERFLOW or MONO_DECIMAL_OK +static MonoDecimalStatus +mono_decimal_multiply_result(MonoDecimal * left, MonoDecimal * right, MonoDecimal * result) +{ + SPLIT64 tmp; + SPLIT64 tmp2; + SPLIT64 tmp3; + int scale; + int hi_prod; + uint32_t pwr; + uint32_t rem_lo; + uint32_t rem_hi; + uint32_t prod[6]; + + scale = left->u.u.scale + right->u.u.scale; + + if ((left->Hi32 | left->v.v.Mid32 | right->Hi32 | right->v.v.Mid32) == 0) { + // Upper 64 bits are zero. + // + tmp.int64 = UInt32x32To64(left->v.v.Lo32, right->v.v.Lo32); + if (scale > DEC_SCALE_MAX) + { + // Result scale is too big. Divide result by power of 10 to reduce it. + // If the amount to divide by is > 19 the result is guaranteed + // less than 1/2. [max value in 64 bits = 1.84E19] + // + scale -= DEC_SCALE_MAX; + if (scale > 19) { + ReturnZero: + DECIMAL_SETZERO(*result); + return MONO_DECIMAL_OK; + } + + if (scale > POWER10_MAX) { + // Divide by 1E10 first, to get the power down to a 32-bit quantity. + // 1E10 itself doesn't fit in 32 bits, so we'll divide by 2.5E9 now + // then multiply the next divisor by 4 (which will be a max of 4E9). + // + rem_lo = FullDiv64By32(&tmp.int64, ten_to_ten_div_4); + pwr = power10[scale - 10] << 2; + } else { + pwr = power10[scale]; + rem_lo = 0; + } + + // Power to divide by fits in 32 bits. + // + rem_hi = FullDiv64By32(&tmp.int64, pwr); + + // Round result. See if remainder >= 1/2 of divisor. + // Divisor is a power of 10, so it is always even. + // + pwr >>= 1; + if (rem_hi >= pwr && (rem_hi > pwr || (rem_lo | (tmp.u.Lo & 1)))) + tmp.int64++; + + scale = DEC_SCALE_MAX; + } + DECIMAL_LO32(*result) = tmp.u.Lo; + DECIMAL_MID32(*result) = tmp.u.Hi; + DECIMAL_HI32(*result) = 0; + } else { + // At least one operand has bits set in the upper 64 bits. + // + // Compute and accumulate the 9 partial products into a + // 192-bit (24-byte) result. + // + // [l-h][l-m][l-l] left high, middle, low + // x [r-h][r-m][r-l] right high, middle, low + // ------------------------------ + // + // [0-h][0-l] l-l * r-l + // [1ah][1al] l-l * r-m + // [1bh][1bl] l-m * r-l + // [2ah][2al] l-m * r-m + // [2bh][2bl] l-l * r-h + // [2ch][2cl] l-h * r-l + // [3ah][3al] l-m * r-h + // [3bh][3bl] l-h * r-m + // [4-h][4-l] l-h * r-h + // ------------------------------ + // [p-5][p-4][p-3][p-2][p-1][p-0] prod[] array + // + tmp.int64 = UInt32x32To64(left->v.v.Lo32, right->v.v.Lo32); + prod[0] = tmp.u.Lo; + + tmp2.int64 = UInt32x32To64(left->v.v.Lo32, right->v.v.Mid32) + tmp.u.Hi; + + tmp.int64 = UInt32x32To64(left->v.v.Mid32, right->v.v.Lo32); + tmp.int64 += tmp2.int64; // this could generate carry + prod[1] = tmp.u.Lo; + if (tmp.int64 < tmp2.int64) // detect carry + tmp2.u.Hi = 1; + else + tmp2.u.Hi = 0; + tmp2.u.Lo = tmp.u.Hi; + + tmp.int64 = UInt32x32To64(left->v.v.Mid32, right->v.v.Mid32) + tmp2.int64; + + if (left->Hi32 | right->Hi32) { + // Highest 32 bits is non-zero. Calculate 5 more partial products. + // + tmp2.int64 = UInt32x32To64(left->v.v.Lo32, right->Hi32); + tmp.int64 += tmp2.int64; // this could generate carry + if (tmp.int64 < tmp2.int64) // detect carry + tmp3.u.Hi = 1; + else + tmp3.u.Hi = 0; + + tmp2.int64 = UInt32x32To64(left->Hi32, right->v.v.Lo32); + tmp.int64 += tmp2.int64; // this could generate carry + prod[2] = tmp.u.Lo; + if (tmp.int64 < tmp2.int64) // detect carry + tmp3.u.Hi++; + tmp3.u.Lo = tmp.u.Hi; + + tmp.int64 = UInt32x32To64(left->v.v.Mid32, right->Hi32); + tmp.int64 += tmp3.int64; // this could generate carry + if (tmp.int64 < tmp3.int64) // detect carry + tmp3.u.Hi = 1; + else + tmp3.u.Hi = 0; + + tmp2.int64 = UInt32x32To64(left->Hi32, right->v.v.Mid32); + tmp.int64 += tmp2.int64; // this could generate carry + prod[3] = tmp.u.Lo; + if (tmp.int64 < tmp2.int64) // detect carry + tmp3.u.Hi++; + tmp3.u.Lo = tmp.u.Hi; + + tmp.int64 = UInt32x32To64(left->Hi32, right->Hi32) + tmp3.int64; + prod[4] = tmp.u.Lo; + prod[5] = tmp.u.Hi; + + hi_prod = 5; + } + else { + prod[2] = tmp.u.Lo; + prod[3] = tmp.u.Hi; + hi_prod = 3; + } + + // Check for leading zero uint32_ts on the product + // + while (prod[hi_prod] == 0) { + hi_prod--; + if (hi_prod < 0) + goto ReturnZero; + } + + scale = ScaleResult(prod, hi_prod, scale); + if (scale == -1) + return MONO_DECIMAL_OVERFLOW; + + result->v.v.Lo32 = prod[0]; + result->v.v.Mid32 = prod[1]; + result->Hi32 = prod[2]; + } + + result->u.u.sign = right->u.u.sign ^ left->u.u.sign; + result->u.u.scale = (char)scale; + return MONO_DECIMAL_OK; +} + +// Addition and subtraction +static MonoDecimalStatus +DecAddSub(MonoDecimal *left, MonoDecimal *right, MonoDecimal *result, int8_t sign) +{ + uint32_t num[6]; + uint32_t pwr; + int scale; + int hi_prod; + int cur; + SPLIT64 tmp; + MonoDecimal decRes; + MonoDecimal decTmp; + MonoDecimal *pdecTmp; + + sign ^= (right->u.u.sign ^ left->u.u.sign) & DECIMAL_NEG; + + if (right->u.u.scale == left->u.u.scale) { + // Scale factors are equal, no alignment necessary. + // + decRes.u.signscale = left->u.signscale; + + AlignedAdd: + if (sign) { + // Signs differ - subtract + // + DECIMAL_LO64_SET(decRes, DECIMAL_LO64_GET(*left) - DECIMAL_LO64_GET(*right)); + DECIMAL_HI32(decRes) = DECIMAL_HI32(*left) - DECIMAL_HI32(*right); + + // Propagate carry + // + if (DECIMAL_LO64_GET(decRes) > DECIMAL_LO64_GET(*left)) { + decRes.Hi32--; + if (decRes.Hi32 >= left->Hi32) + goto SignFlip; + } else if (decRes.Hi32 > left->Hi32) { + // Got negative result. Flip its sign. + // + SignFlip: + DECIMAL_LO64_SET(decRes, -(uint64_t)DECIMAL_LO64_GET(decRes)); + decRes.Hi32 = ~decRes.Hi32; + if (DECIMAL_LO64_GET(decRes) == 0) + decRes.Hi32++; + decRes.u.u.sign ^= DECIMAL_NEG; + } + + } else { + // Signs are the same - add + // + DECIMAL_LO64_SET(decRes, DECIMAL_LO64_GET(*left) + DECIMAL_LO64_GET(*right)); + decRes.Hi32 = left->Hi32 + right->Hi32; + + // Propagate carry + // + if (DECIMAL_LO64_GET(decRes) < DECIMAL_LO64_GET(*left)) { + decRes.Hi32++; + if (decRes.Hi32 <= left->Hi32) + goto AlignedScale; + } else if (decRes.Hi32 < left->Hi32) { + AlignedScale: + // The addition carried above 96 bits. Divide the result by 10, + // dropping the scale factor. + // + if (decRes.u.u.scale == 0) + return MONO_DECIMAL_OVERFLOW; + decRes.u.u.scale--; + + tmp.u.Lo = decRes.Hi32; + tmp.u.Hi = 1; + tmp.int64 = DivMod64by32(tmp.int64, 10); + decRes.Hi32 = tmp.u.Lo; + + tmp.u.Lo = decRes.v.v.Mid32; + tmp.int64 = DivMod64by32(tmp.int64, 10); + decRes.v.v.Mid32 = tmp.u.Lo; + + tmp.u.Lo = decRes.v.v.Lo32; + tmp.int64 = DivMod64by32(tmp.int64, 10); + decRes.v.v.Lo32 = tmp.u.Lo; + + // See if we need to round up. + // + if (tmp.u.Hi >= 5 && (tmp.u.Hi > 5 || (decRes.v.v.Lo32 & 1))) { + DECIMAL_LO64_SET(decRes, DECIMAL_LO64_GET(decRes)+1) + if (DECIMAL_LO64_GET(decRes) == 0) + decRes.Hi32++; + } + } + } + } + else { + // Scale factors are not equal. Assume that a larger scale + // factor (more decimal places) is likely to mean that number + // is smaller. Start by guessing that the right operand has + // the larger scale factor. The result will have the larger + // scale factor. + // + decRes.u.u.scale = right->u.u.scale; // scale factor of "smaller" + decRes.u.u.sign = left->u.u.sign; // but sign of "larger" + scale = decRes.u.u.scale - left->u.u.scale; + + if (scale < 0) { + // Guessed scale factor wrong. Swap operands. + // + scale = -scale; + decRes.u.u.scale = left->u.u.scale; + decRes.u.u.sign ^= sign; + pdecTmp = right; + right = left; + left = pdecTmp; + } + + // *left will need to be multiplied by 10^scale so + // it will have the same scale as *right. We could be + // extending it to up to 192 bits of precision. + // + if (scale <= POWER10_MAX) { + // Scaling won't make it larger than 4 uint32_ts + // + pwr = power10[scale]; + DECIMAL_LO64_SET(decTmp, UInt32x32To64(left->v.v.Lo32, pwr)); + tmp.int64 = UInt32x32To64(left->v.v.Mid32, pwr); + tmp.int64 += decTmp.v.v.Mid32; + decTmp.v.v.Mid32 = tmp.u.Lo; + decTmp.Hi32 = tmp.u.Hi; + tmp.int64 = UInt32x32To64(left->Hi32, pwr); + tmp.int64 += decTmp.Hi32; + if (tmp.u.Hi == 0) { + // Result fits in 96 bits. Use standard aligned add. + // + decTmp.Hi32 = tmp.u.Lo; + left = &decTmp; + goto AlignedAdd; + } + num[0] = decTmp.v.v.Lo32; + num[1] = decTmp.v.v.Mid32; + num[2] = tmp.u.Lo; + num[3] = tmp.u.Hi; + hi_prod = 3; + } + else { + // Have to scale by a bunch. Move the number to a buffer + // where it has room to grow as it's scaled. + // + num[0] = left->v.v.Lo32; + num[1] = left->v.v.Mid32; + num[2] = left->Hi32; + hi_prod = 2; + + // Scan for zeros in the upper words. + // + if (num[2] == 0) { + hi_prod = 1; + if (num[1] == 0) { + hi_prod = 0; + if (num[0] == 0) { + // Left arg is zero, return right. + // + DECIMAL_LO64_SET(decRes, DECIMAL_LO64_GET(*right)); + decRes.Hi32 = right->Hi32; + decRes.u.u.sign ^= sign; + goto RetDec; + } + } + } + + // Scaling loop, up to 10^9 at a time. hi_prod stays updated + // with index of highest non-zero uint32_t. + // + for (; scale > 0; scale -= POWER10_MAX) { + if (scale > POWER10_MAX) + pwr = ten_to_nine; + else + pwr = power10[scale]; + + tmp.u.Hi = 0; + for (cur = 0; cur <= hi_prod; cur++) { + tmp.int64 = UInt32x32To64(num[cur], pwr) + tmp.u.Hi; + num[cur] = tmp.u.Lo; + } + + if (tmp.u.Hi != 0) + // We're extending the result by another uint32_t. + num[++hi_prod] = tmp.u.Hi; + } + } + + // Scaling complete, do the add. Could be subtract if signs differ. + // + tmp.u.Lo = num[0]; + tmp.u.Hi = num[1]; + + if (sign) { + // Signs differ, subtract. + // + DECIMAL_LO64_SET(decRes, tmp.int64 - DECIMAL_LO64_GET(*right)); + decRes.Hi32 = num[2] - right->Hi32; + + // Propagate carry + // + if (DECIMAL_LO64_GET(decRes) > tmp.int64) { + decRes.Hi32--; + if (decRes.Hi32 >= num[2]) + goto LongSub; + } + else if (decRes.Hi32 > num[2]) { + LongSub: + // If num has more than 96 bits of precision, then we need to + // carry the subtraction into the higher bits. If it doesn't, + // then we subtracted in the wrong order and have to flip the + // sign of the result. + // + if (hi_prod <= 2) + goto SignFlip; + + cur = 3; + while(num[cur++]-- == 0); + if (num[hi_prod] == 0) + hi_prod--; + } + } + else { + // Signs the same, add. + // + DECIMAL_LO64_SET(decRes, tmp.int64 + DECIMAL_LO64_GET(*right)); + decRes.Hi32 = num[2] + right->Hi32; + + // Propagate carry + // + if (DECIMAL_LO64_GET(decRes) < tmp.int64) { + decRes.Hi32++; + if (decRes.Hi32 <= num[2]) + goto LongAdd; + } + else if (decRes.Hi32 < num[2]) { + LongAdd: + // Had a carry above 96 bits. + // + cur = 3; + do { + if (hi_prod < cur) { + num[cur] = 1; + hi_prod = cur; + break; + } + }while (++num[cur++] == 0); + } + } + + if (hi_prod > 2) { + num[0] = decRes.v.v.Lo32; + num[1] = decRes.v.v.Mid32; + num[2] = decRes.Hi32; + decRes.u.u.scale = ScaleResult(num, hi_prod, decRes.u.u.scale); + if (decRes.u.u.scale == (uint8_t) -1) + return MONO_DECIMAL_OVERFLOW; + + decRes.v.v.Lo32 = num[0]; + decRes.v.v.Mid32 = num[1]; + decRes.Hi32 = num[2]; + } + } + +RetDec: + COPYDEC(*result, decRes); + // Odd, the Microsoft code does not set result->reserved to zero on this case + return MONO_DECIMAL_OK; +} + +// Decimal addition +static MonoDecimalStatus G_GNUC_UNUSED +mono_decimal_add(MonoDecimal *left, MonoDecimal *right, MonoDecimal *result) +{ + return DecAddSub (left, right, result, 0); +} + +// Decimal subtraction +static MonoDecimalStatus G_GNUC_UNUSED +mono_decimal_sub(MonoDecimal *left, MonoDecimal *right, MonoDecimal *result) +{ + return DecAddSub (left, right, result, DECIMAL_NEG); +} + +/** + * IncreaseScale: + * + * Entry: + * num - Pointer to 96-bit number as array of uint32_ts, least-sig first + * pwr - Scale factor to multiply by + * + * Purpose: + * Multiply the two numbers. The low 96 bits of the result overwrite + * the input. The last 32 bits of the product are the return value. + * + * Exit: + * Returns highest 32 bits of product. + * + * Exceptions: + * None. + * + */ +static uint32_t +IncreaseScale(uint32_t *num, uint32_t pwr) +{ + SPLIT64 sdlTmp; + + sdlTmp.int64 = UInt32x32To64(num[0], pwr); + num[0] = sdlTmp.u.Lo; + sdlTmp.int64 = UInt32x32To64(num[1], pwr) + sdlTmp.u.Hi; + num[1] = sdlTmp.u.Lo; + sdlTmp.int64 = UInt32x32To64(num[2], pwr) + sdlTmp.u.Hi; + num[2] = sdlTmp.u.Lo; + return sdlTmp.u.Hi; +} + +/** + * Div96By64: + * + * Entry: + * rgulNum - Pointer to 96-bit dividend as array of uint32_ts, least-sig first + * sdlDen - 64-bit divisor. + * + * Purpose: + * Do partial divide, yielding 32-bit result and 64-bit remainder. + * Divisor must be larger than upper 64 bits of dividend. + * + * Exit: + * Remainder overwrites lower 64-bits of dividend. + * Returns quotient. + * + * Exceptions: + * None. + * + */ +static uint32_t +Div96By64(uint32_t *num, SPLIT64 den) +{ + SPLIT64 quo; + SPLIT64 sdlNum; + SPLIT64 prod; + + sdlNum.u.Lo = num[0]; + + if (num[2] >= den.u.Hi) { + // Divide would overflow. Assume a quotient of 2^32, and set + // up remainder accordingly. Then jump to loop which reduces + // the quotient. + // + sdlNum.u.Hi = num[1] - den.u.Lo; + quo.u.Lo = 0; + goto NegRem; + } + + // Hardware divide won't overflow + // + if (num[2] == 0 && num[1] < den.u.Hi) + // Result is zero. Entire dividend is remainder. + // + return 0; + + // DivMod64by32 returns quotient in Lo, remainder in Hi. + // + quo.u.Lo = num[1]; + quo.u.Hi = num[2]; + quo.int64 = DivMod64by32(quo.int64, den.u.Hi); + sdlNum.u.Hi = quo.u.Hi; // remainder + + // Compute full remainder, rem = dividend - (quo * divisor). + // + prod.int64 = UInt32x32To64(quo.u.Lo, den.u.Lo); // quo * lo divisor + sdlNum.int64 -= prod.int64; + + if (sdlNum.int64 > ~prod.int64) { + NegRem: + // Remainder went negative. Add divisor back in until it's positive, + // a max of 2 times. + // + do { + quo.u.Lo--; + sdlNum.int64 += den.int64; + }while (sdlNum.int64 >= den.int64); + } + + num[0] = sdlNum.u.Lo; + num[1] = sdlNum.u.Hi; + return quo.u.Lo; +} + +/*** +* Div128By96 +* +* Entry: +* rgulNum - Pointer to 128-bit dividend as array of uint32_ts, least-sig first +* den - Pointer to 96-bit divisor. +* +* Purpose: +* Do partial divide, yielding 32-bit result and 96-bit remainder. +* Top divisor uint32_t must be larger than top dividend uint32_t. This is +* assured in the initial call because the divisor is normalized +* and the dividend can't be. In subsequent calls, the remainder +* is multiplied by 10^9 (max), so it can be no more than 1/4 of +* the divisor which is effectively multiplied by 2^32 (4 * 10^9). +* +* Exit: +* Remainder overwrites lower 96-bits of dividend. +* Returns quotient. +* +* Exceptions: +* None. +* +***********************************************************************/ + +static uint32_t +Div128By96(uint32_t *num, uint32_t *den) +{ + SPLIT64 sdlQuo; + SPLIT64 sdlNum; + SPLIT64 sdlProd1; + SPLIT64 sdlProd2; + + sdlNum.u.Lo = num[0]; + sdlNum.u.Hi = num[1]; + + if (num[3] == 0 && num[2] < den[2]){ + // Result is zero. Entire dividend is remainder. + // + return 0; + } + + // DivMod64by32 returns quotient in Lo, remainder in Hi. + // + sdlQuo.u.Lo = num[2]; + sdlQuo.u.Hi = num[3]; + sdlQuo.int64 = DivMod64by32(sdlQuo.int64, den[2]); + + // Compute full remainder, rem = dividend - (quo * divisor). + // + sdlProd1.int64 = UInt32x32To64(sdlQuo.u.Lo, den[0]); // quo * lo divisor + sdlProd2.int64 = UInt32x32To64(sdlQuo.u.Lo, den[1]); // quo * mid divisor + sdlProd2.int64 += sdlProd1.u.Hi; + sdlProd1.u.Hi = sdlProd2.u.Lo; + + sdlNum.int64 -= sdlProd1.int64; + num[2] = sdlQuo.u.Hi - sdlProd2.u.Hi; // sdlQuo.Hi is remainder + + // Propagate carries + // + if (sdlNum.int64 > ~sdlProd1.int64) { + num[2]--; + if (num[2] >= ~sdlProd2.u.Hi) + goto NegRem; + } else if (num[2] > ~sdlProd2.u.Hi) { + NegRem: + // Remainder went negative. Add divisor back in until it's positive, + // a max of 2 times. + // + sdlProd1.u.Lo = den[0]; + sdlProd1.u.Hi = den[1]; + + for (;;) { + sdlQuo.u.Lo--; + sdlNum.int64 += sdlProd1.int64; + num[2] += den[2]; + + if (sdlNum.int64 < sdlProd1.int64) { + // Detected carry. Check for carry out of top + // before adding it in. + // + if (num[2]++ < den[2]) + break; + } + if (num[2] < den[2]) + break; // detected carry + } + } + + num[0] = sdlNum.u.Lo; + num[1] = sdlNum.u.Hi; + return sdlQuo.u.Lo; +} + +// Add a 32 bit unsigned long to an array of 3 unsigned longs representing a 96 integer +// Returns FALSE if there is an overflow +static gboolean +Add32To96(uint32_t *num, uint32_t value) +{ + num[0] += value; + if (num[0] < value) { + if (++num[1] == 0) { + if (++num[2] == 0) { + return FALSE; + } + } + } + return TRUE; +} + +static void +OverflowUnscale (uint32_t *quo, gboolean remainder) +{ + SPLIT64 sdlTmp; + + // We have overflown, so load the high bit with a one. + sdlTmp.u.Hi = 1u; + sdlTmp.u.Lo = quo[2]; + sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10u); + quo[2] = sdlTmp.u.Lo; + sdlTmp.u.Lo = quo[1]; + sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10u); + quo[1] = sdlTmp.u.Lo; + sdlTmp.u.Lo = quo[0]; + sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10u); + quo[0] = sdlTmp.u.Lo; + // The remainder is the last digit that does not fit, so we can use it to work out if we need to round up + if ((sdlTmp.u.Hi > 5) || ((sdlTmp.u.Hi == 5) && ( remainder || (quo[0] & 1)))) { + Add32To96(quo, 1u); + } +} + +// mono_decimal_divide - Decimal divide +static MonoDecimalStatus G_GNUC_UNUSED +mono_decimal_divide_result(MonoDecimal *left, MonoDecimal *right, MonoDecimal *result) +{ + uint32_t quo[3]; + uint32_t quoSave[3]; + uint32_t rem[4]; + uint32_t divisor[3]; + uint32_t pwr; + uint32_t utmp; + uint32_t utmp1; + SPLIT64 sdlTmp; + SPLIT64 sdlDivisor; + int scale; + int cur_scale; + + scale = left->u.u.scale - right->u.u.scale; + divisor[0] = right->v.v.Lo32; + divisor[1] = right->v.v.Mid32; + divisor[2] = right->Hi32; + + if (divisor[1] == 0 && divisor[2] == 0) { + // Divisor is only 32 bits. Easy divide. + // + if (divisor[0] == 0) + return MONO_DECIMAL_DIVBYZERO; + + quo[0] = left->v.v.Lo32; + quo[1] = left->v.v.Mid32; + quo[2] = left->Hi32; + rem[0] = Div96By32(quo, divisor[0]); + + for (;;) { + if (rem[0] == 0) { + if (scale < 0) { + cur_scale = min(9, -scale); + goto HaveScale; + } + break; + } + + // We have computed a quotient based on the natural scale + // ( - ). We have a non-zero + // remainder, so now we should increase the scale if possible to + // include more quotient bits. + // + // If it doesn't cause overflow, we'll loop scaling by 10^9 and + // computing more quotient bits as long as the remainder stays + // non-zero. If scaling by that much would cause overflow, we'll + // drop out of the loop and scale by as much as we can. + // + // Scaling by 10^9 will overflow if quo[2].quo[1] >= 2^32 / 10^9 + // = 4.294 967 296. So the upper limit is quo[2] == 4 and + // quo[1] == 0.294 967 296 * 2^32 = 1,266,874,889.7+. Since + // quotient bits in quo[0] could be all 1's, then 1,266,874,888 + // is the largest value in quo[1] (when quo[2] == 4) that is + // assured not to overflow. + // + cur_scale = SearchScale(quo[2], quo[1], quo [0], scale); + if (cur_scale == 0) { + // No more scaling to be done, but remainder is non-zero. + // Round quotient. + // + utmp = rem[0] << 1; + if (utmp < rem[0] || (utmp >= divisor[0] && + (utmp > divisor[0] || (quo[0] & 1)))) { + RoundUp: + if (++quo[0] == 0) + if (++quo[1] == 0) + quo[2]++; + } + break; + } + + if (cur_scale == -1) + return MONO_DECIMAL_OVERFLOW; + + HaveScale: + pwr = power10[cur_scale]; + scale += cur_scale; + + if (IncreaseScale(quo, pwr) != 0) + return MONO_DECIMAL_OVERFLOW; + + sdlTmp.int64 = DivMod64by32(UInt32x32To64(rem[0], pwr), divisor[0]); + rem[0] = sdlTmp.u.Hi; + + quo[0] += sdlTmp.u.Lo; + if (quo[0] < sdlTmp.u.Lo) { + if (++quo[1] == 0) + quo[2]++; + } + } // for (;;) + } + else { + // Divisor has bits set in the upper 64 bits. + // + // Divisor must be fully normalized (shifted so bit 31 of the most + // significant uint32_t is 1). Locate the MSB so we know how much to + // normalize by. The dividend will be shifted by the same amount so + // the quotient is not changed. + // + if (divisor[2] == 0) + utmp = divisor[1]; + else + utmp = divisor[2]; + + cur_scale = 0; + if (!(utmp & 0xFFFF0000)) { + cur_scale += 16; + utmp <<= 16; + } + if (!(utmp & 0xFF000000)) { + cur_scale += 8; + utmp <<= 8; + } + if (!(utmp & 0xF0000000)) { + cur_scale += 4; + utmp <<= 4; + } + if (!(utmp & 0xC0000000)) { + cur_scale += 2; + utmp <<= 2; + } + if (!(utmp & 0x80000000)) { + cur_scale++; + utmp <<= 1; + } + + // Shift both dividend and divisor left by cur_scale. + // + sdlTmp.int64 = DECIMAL_LO64_GET(*left) << cur_scale; + rem[0] = sdlTmp.u.Lo; + rem[1] = sdlTmp.u.Hi; + sdlTmp.u.Lo = left->v.v.Mid32; + sdlTmp.u.Hi = left->Hi32; + sdlTmp.int64 <<= cur_scale; + rem[2] = sdlTmp.u.Hi; + rem[3] = (left->Hi32 >> (31 - cur_scale)) >> 1; + + sdlDivisor.u.Lo = divisor[0]; + sdlDivisor.u.Hi = divisor[1]; + sdlDivisor.int64 <<= cur_scale; + + if (divisor[2] == 0) { + // Have a 64-bit divisor in sdlDivisor. The remainder + // (currently 96 bits spread over 4 uint32_ts) will be < divisor. + // + sdlTmp.u.Lo = rem[2]; + sdlTmp.u.Hi = rem[3]; + + quo[2] = 0; + quo[1] = Div96By64(&rem[1], sdlDivisor); + quo[0] = Div96By64(rem, sdlDivisor); + + for (;;) { + if ((rem[0] | rem[1]) == 0) { + if (scale < 0) { + cur_scale = min(9, -scale); + goto HaveScale64; + } + break; + } + + // Remainder is non-zero. Scale up quotient and remainder by + // powers of 10 so we can compute more significant bits. + // + cur_scale = SearchScale(quo[2], quo[1], quo [0], scale); + if (cur_scale == 0) { + // No more scaling to be done, but remainder is non-zero. + // Round quotient. + // + sdlTmp.u.Lo = rem[0]; + sdlTmp.u.Hi = rem[1]; + if (sdlTmp.u.Hi >= 0x80000000 || (sdlTmp.int64 <<= 1) > sdlDivisor.int64 || + (sdlTmp.int64 == sdlDivisor.int64 && (quo[0] & 1))) + goto RoundUp; + break; + } + + if (cur_scale == -1) + return MONO_DECIMAL_OVERFLOW; + + HaveScale64: + pwr = power10[cur_scale]; + scale += cur_scale; + + if (IncreaseScale(quo, pwr) != 0) + return MONO_DECIMAL_OVERFLOW; + + rem[2] = 0; // rem is 64 bits, IncreaseScale uses 96 + IncreaseScale(rem, pwr); + utmp = Div96By64(rem, sdlDivisor); + quo[0] += utmp; + if (quo[0] < utmp) + if (++quo[1] == 0) + quo[2]++; + + } // for (;;) + } + else { + // Have a 96-bit divisor in divisor[]. + // + // Start by finishing the shift left by cur_scale. + // + sdlTmp.u.Lo = divisor[1]; + sdlTmp.u.Hi = divisor[2]; + sdlTmp.int64 <<= cur_scale; + divisor[0] = sdlDivisor.u.Lo; + divisor[1] = sdlDivisor.u.Hi; + divisor[2] = sdlTmp.u.Hi; + + // The remainder (currently 96 bits spread over 4 uint32_ts) + // will be < divisor. + // + quo[2] = 0; + quo[1] = 0; + quo[0] = Div128By96(rem, divisor); + + for (;;) { + if ((rem[0] | rem[1] | rem[2]) == 0) { + if (scale < 0) { + cur_scale = min(9, -scale); + goto HaveScale96; + } + break; + } + + // Remainder is non-zero. Scale up quotient and remainder by + // powers of 10 so we can compute more significant bits. + // + cur_scale = SearchScale(quo[2], quo[1], quo [0], scale); + if (cur_scale == 0) { + // No more scaling to be done, but remainder is non-zero. + // Round quotient. + // + if (rem[2] >= 0x80000000) + goto RoundUp; + + utmp = rem[0] > 0x80000000; + utmp1 = rem[1] > 0x80000000; + rem[0] <<= 1; + rem[1] = (rem[1] << 1) + utmp; + rem[2] = (rem[2] << 1) + utmp1; + + if ((rem[2] > divisor[2] || rem[2] == divisor[2]) && + ((rem[1] > divisor[1] || rem[1] == divisor[1]) && + ((rem[0] > divisor[0] || rem[0] == divisor[0]) && + (quo[0] & 1)))) + goto RoundUp; + break; + } + + if (cur_scale == -1) + return MONO_DECIMAL_OVERFLOW; + + HaveScale96: + pwr = power10[cur_scale]; + scale += cur_scale; + + if (IncreaseScale(quo, pwr) != 0) + return MONO_DECIMAL_OVERFLOW; + + rem[3] = IncreaseScale(rem, pwr); + utmp = Div128By96(rem, divisor); + quo[0] += utmp; + if (quo[0] < utmp) + if (++quo[1] == 0) + quo[2]++; + + } // for (;;) + } + } + + // No more remainder. Try extracting any extra powers of 10 we may have + // added. We do this by trying to divide out 10^8, 10^4, 10^2, and 10^1. + // If a division by one of these powers returns a zero remainder, then + // we keep the quotient. If the remainder is not zero, then we restore + // the previous value. + // + // Since 10 = 2 * 5, there must be a factor of 2 for every power of 10 + // we can extract. We use this as a quick test on whether to try a + // given power. + // + while ((quo[0] & 0xFF) == 0 && scale >= 8) { + quoSave[0] = quo[0]; + quoSave[1] = quo[1]; + quoSave[2] = quo[2]; + + if (Div96By32(quoSave, 100000000) == 0) { + quo[0] = quoSave[0]; + quo[1] = quoSave[1]; + quo[2] = quoSave[2]; + scale -= 8; + } + else + break; + } + + if ((quo[0] & 0xF) == 0 && scale >= 4) { + quoSave[0] = quo[0]; + quoSave[1] = quo[1]; + quoSave[2] = quo[2]; + + if (Div96By32(quoSave, 10000) == 0) { + quo[0] = quoSave[0]; + quo[1] = quoSave[1]; + quo[2] = quoSave[2]; + scale -= 4; + } + } + + if ((quo[0] & 3) == 0 && scale >= 2) { + quoSave[0] = quo[0]; + quoSave[1] = quo[1]; + quoSave[2] = quo[2]; + + if (Div96By32(quoSave, 100) == 0) { + quo[0] = quoSave[0]; + quo[1] = quoSave[1]; + quo[2] = quoSave[2]; + scale -= 2; + } + } + + if ((quo[0] & 1) == 0 && scale >= 1) { + quoSave[0] = quo[0]; + quoSave[1] = quo[1]; + quoSave[2] = quo[2]; + + if (Div96By32(quoSave, 10) == 0) { + quo[0] = quoSave[0]; + quo[1] = quoSave[1]; + quo[2] = quoSave[2]; + scale -= 1; + } + } + + result->Hi32 = quo[2]; + result->v.v.Mid32 = quo[1]; + result->v.v.Lo32 = quo[0]; + result->u.u.scale = scale; + result->u.u.sign = left->u.u.sign ^ right->u.u.sign; + return MONO_DECIMAL_OK; +} + +// mono_decimal_absolute - Decimal Absolute Value +static void G_GNUC_UNUSED +mono_decimal_absolute (MonoDecimal *pdecOprd, MonoDecimal *result) +{ + COPYDEC(*result, *pdecOprd); + result->u.u.sign &= ~DECIMAL_NEG; + // Microsoft does not set reserved here +} + +// mono_decimal_fix - Decimal Fix (chop to integer) +static void +mono_decimal_fix (MonoDecimal *pdecOprd, MonoDecimal *result) +{ + DecFixInt(result, pdecOprd); +} + +// mono_decimal_round_to_int - Decimal Int (round down to integer) +static void +mono_decimal_round_to_int (MonoDecimal *pdecOprd, MonoDecimal *result) +{ + if (DecFixInt(result, pdecOprd) != 0 && (result->u.u.sign & DECIMAL_NEG)) { + // We have chopped off a non-zero amount from a negative value. Since + // we round toward -infinity, we must increase the integer result by + // 1 to make it more negative. This will never overflow because + // in order to have a remainder, we must have had a non-zero scale factor. + // Our scale factor is back to zero now. + // + DECIMAL_LO64_SET(*result, DECIMAL_LO64_GET(*result) + 1); + if (DECIMAL_LO64_GET(*result) == 0) + result->Hi32++; + } +} + +// mono_decimal_negate - Decimal Negate +static void G_GNUC_UNUSED +mono_decimal_negate (MonoDecimal *pdecOprd, MonoDecimal *result) +{ + COPYDEC(*result, *pdecOprd); + // Microsoft does not set result->reserved to zero on this case. + result->u.u.sign ^= DECIMAL_NEG; +} + +// +// Returns: MONO_DECIMAL_INVALID_ARGUMENT, MONO_DECIMAL_OK +// +static MonoDecimalStatus +mono_decimal_round_result(MonoDecimal *input, int cDecimals, MonoDecimal *result) +{ + uint32_t num[3]; + uint32_t rem; + uint32_t sticky; + uint32_t pwr; + int scale; + + if (cDecimals < 0) + return MONO_DECIMAL_INVALID_ARGUMENT; + + scale = input->u.u.scale - cDecimals; + if (scale > 0) { + num[0] = input->v.v.Lo32; + num[1] = input->v.v.Mid32; + num[2] = input->Hi32; + result->u.u.sign = input->u.u.sign; + rem = sticky = 0; + + do { + sticky |= rem; + if (scale > POWER10_MAX) + pwr = ten_to_nine; + else + pwr = power10[scale]; + + rem = Div96By32(num, pwr); + scale -= 9; + }while (scale > 0); + + // Now round. rem has last remainder, sticky has sticky bits. + // To do IEEE rounding, we add LSB of result to sticky bits so + // either causes round up if remainder * 2 == last divisor. + // + sticky |= num[0] & 1; + rem = (rem << 1) + (sticky != 0); + if (pwr < rem && + ++num[0] == 0 && + ++num[1] == 0 + ) + ++num[2]; + + result->v.v.Lo32 = num[0]; + result->v.v.Mid32 = num[1]; + result->Hi32 = num[2]; + result->u.u.scale = cDecimals; + return MONO_DECIMAL_OK; + } + + COPYDEC(*result, *input); + // Odd, the Microsoft source does not set the result->reserved to zero here. + return MONO_DECIMAL_OK; +} + +// +// Returns MONO_DECIMAL_OK or MONO_DECIMAL_OVERFLOW +static MonoDecimalStatus +mono_decimal_from_float (float input_f, MonoDecimal* result) +{ + int exp; // number of bits to left of binary point + int power; + uint32_t mant; + double dbl; + SPLIT64 sdlLo; + SPLIT64 sdlHi; + int lmax, cur; // temps used during scale reduction + MonoSingle_float input = { .f = input_f }; + + // The most we can scale by is 10^28, which is just slightly more + // than 2^93. So a float with an exponent of -94 could just + // barely reach 0.5, but smaller exponents will always round to zero. + // + if ((exp = input.s.exp - MONO_SINGLE_BIAS) < -94 ) { + DECIMAL_SETZERO(*result); + return MONO_DECIMAL_OK; + } + + if (exp > 96) + return MONO_DECIMAL_OVERFLOW; + + // Round the input to a 7-digit integer. The R4 format has + // only 7 digits of precision, and we want to keep garbage digits + // out of the Decimal were making. + // + // Calculate max power of 10 input value could have by multiplying + // the exponent by log10(2). Using scaled integer multiplcation, + // log10(2) * 2 ^ 16 = .30103 * 65536 = 19728.3. + // + dbl = fabs(input.f); + power = 6 - ((exp * 19728) >> 16); + + if (power >= 0) { + // We have less than 7 digits, scale input up. + // + if (power > DECMAX) + power = DECMAX; + + dbl = dbl * double_power10[power]; + } else { + if (power != -1 || dbl >= 1E7) + dbl = dbl / fnDblPower10(-power); + else + power = 0; // didn't scale it + } + + g_assert (dbl < 1E7); + if (dbl < 1E6 && power < DECMAX) { + dbl *= 10; + power++; + g_assert(dbl >= 1E6); + } + + // Round to integer + // + mant = (int32_t)dbl; + dbl -= (double)mant; // difference between input & integer + if ( dbl > 0.5 || (dbl == 0.5 && (mant & 1))) + mant++; + + if (mant == 0) { + DECIMAL_SETZERO(*result); + return MONO_DECIMAL_OK; + } + + if (power < 0) { + // Add -power factors of 10, -power <= (29 - 7) = 22. + // + power = -power; + if (power < 10) { + sdlLo.int64 = UInt32x32To64(mant, (uint32_t)long_power10[power]); + + DECIMAL_LO32(*result) = sdlLo.u.Lo; + DECIMAL_MID32(*result) = sdlLo.u.Hi; + DECIMAL_HI32(*result) = 0; + } else { + // Have a big power of 10. + // + if (power > 18) { + sdlLo.int64 = UInt32x32To64(mant, (uint32_t)long_power10[power - 18]); + sdlLo.int64 = UInt64x64To128(sdlLo, ten_to_eighteen, &sdlHi.int64); + + if (sdlHi.u.Hi != 0) + return MONO_DECIMAL_OVERFLOW; + } + else { + sdlLo.int64 = UInt32x32To64(mant, (uint32_t)long_power10[power - 9]); + sdlHi.int64 = UInt32x32To64(ten_to_nine, sdlLo.u.Hi); + sdlLo.int64 = UInt32x32To64(ten_to_nine, sdlLo.u.Lo); + sdlHi.int64 += sdlLo.u.Hi; + sdlLo.u.Hi = sdlHi.u.Lo; + sdlHi.u.Lo = sdlHi.u.Hi; + } + DECIMAL_LO32(*result) = sdlLo.u.Lo; + DECIMAL_MID32(*result) = sdlLo.u.Hi; + DECIMAL_HI32(*result) = sdlHi.u.Lo; + } + DECIMAL_SCALE(*result) = 0; + } else { + // Factor out powers of 10 to reduce the scale, if possible. + // The maximum number we could factor out would be 6. This + // comes from the fact we have a 7-digit number, and the + // MSD must be non-zero -- but the lower 6 digits could be + // zero. Note also the scale factor is never negative, so + // we can't scale by any more than the power we used to + // get the integer. + // + // DivMod32by32 returns the quotient in Lo, the remainder in Hi. + // + lmax = min(power, 6); + + // lmax is the largest power of 10 to try, lmax <= 6. + // We'll try powers 4, 2, and 1 unless they're too big. + // + for (cur = 4; cur > 0; cur >>= 1) + { + if (cur > lmax) + continue; + + sdlLo.int64 = DivMod32by32(mant, (uint32_t)long_power10[cur]); + + if (sdlLo.u.Hi == 0) { + mant = sdlLo.u.Lo; + power -= cur; + lmax -= cur; + } + } + DECIMAL_LO32(*result) = mant; + DECIMAL_MID32(*result) = 0; + DECIMAL_HI32(*result) = 0; + DECIMAL_SCALE(*result) = power; + } + + DECIMAL_SIGN(*result) = (char)input.s.sign << 7; + return MONO_DECIMAL_OK; +} + +// Returns MONO_DECIMAL_OK or MONO_DECIMAL_OVERFLOW +static MonoDecimalStatus +mono_decimal_from_double (double input_d, MonoDecimal *result) +{ + int exp; // number of bits to left of binary point + int power; // power-of-10 scale factor + SPLIT64 sdlMant; + SPLIT64 sdlLo; + double dbl; + int lmax, cur; // temps used during scale reduction + uint32_t pwr_cur; + uint32_t quo; + MonoDouble_double input = { .d = input_d }; + + // The most we can scale by is 10^28, which is just slightly more + // than 2^93. So a float with an exponent of -94 could just + // barely reach 0.5, but smaller exponents will always round to zero. + // + if ((exp = input.s.exp - MONO_DOUBLE_BIAS) < -94) { + DECIMAL_SETZERO(*result); + return MONO_DECIMAL_OK; + } + + if (exp > 96) + return MONO_DECIMAL_OVERFLOW; + + // Round the input to a 15-digit integer. The R8 format has + // only 15 digits of precision, and we want to keep garbage digits + // out of the Decimal were making. + // + // Calculate max power of 10 input value could have by multiplying + // the exponent by log10(2). Using scaled integer multiplcation, + // log10(2) * 2 ^ 16 = .30103 * 65536 = 19728.3. + // + dbl = fabs(input.d); + power = 14 - ((exp * 19728) >> 16); + + if (power >= 0) { + // We have less than 15 digits, scale input up. + // + if (power > DECMAX) + power = DECMAX; + + dbl = dbl * double_power10[power]; + } else { + if (power != -1 || dbl >= 1E15) + dbl = dbl / fnDblPower10(-power); + else + power = 0; // didn't scale it + } + + g_assert (dbl < 1E15); + if (dbl < 1E14 && power < DECMAX) { + dbl *= 10; + power++; + g_assert(dbl >= 1E14); + } + + // Round to int64 + // + sdlMant.int64 = (int64_t)dbl; + dbl -= (double)(int64_t)sdlMant.int64; // dif between input & integer + if ( dbl > 0.5 || (dbl == 0.5 && (sdlMant.u.Lo & 1))) + sdlMant.int64++; + + if (sdlMant.int64 == 0) { + DECIMAL_SETZERO(*result); + return MONO_DECIMAL_OK; + } + + if (power < 0) { + // Add -power factors of 10, -power <= (29 - 15) = 14. + // + power = -power; + if (power < 10) { + sdlLo.int64 = UInt32x32To64(sdlMant.u.Lo, (uint32_t)long_power10[power]); + sdlMant.int64 = UInt32x32To64(sdlMant.u.Hi, (uint32_t)long_power10[power]); + sdlMant.int64 += sdlLo.u.Hi; + sdlLo.u.Hi = sdlMant.u.Lo; + sdlMant.u.Lo = sdlMant.u.Hi; + } + else { + // Have a big power of 10. + // + g_assert(power <= 14); + sdlLo.int64 = UInt64x64To128(sdlMant, sdl_power10[power-10], &sdlMant.int64); + + if (sdlMant.u.Hi != 0) + return MONO_DECIMAL_OVERFLOW; + } + DECIMAL_LO32(*result) = sdlLo.u.Lo; + DECIMAL_MID32(*result) = sdlLo.u.Hi; + DECIMAL_HI32(*result) = sdlMant.u.Lo; + DECIMAL_SCALE(*result) = 0; + } + else { + // Factor out powers of 10 to reduce the scale, if possible. + // The maximum number we could factor out would be 14. This + // comes from the fact we have a 15-digit number, and the + // MSD must be non-zero -- but the lower 14 digits could be + // zero. Note also the scale factor is never negative, so + // we can't scale by any more than the power we used to + // get the integer. + // + // DivMod64by32 returns the quotient in Lo, the remainder in Hi. + // + lmax = min(power, 14); + + // lmax is the largest power of 10 to try, lmax <= 14. + // We'll try powers 8, 4, 2, and 1 unless they're too big. + // + for (cur = 8; cur > 0; cur >>= 1) + { + if (cur > lmax) + continue; + + pwr_cur = (uint32_t)long_power10[cur]; + + if (sdlMant.u.Hi >= pwr_cur) { + // Overflow if we try to divide in one step. + // + sdlLo.int64 = DivMod64by32(sdlMant.u.Hi, pwr_cur); + quo = sdlLo.u.Lo; + sdlLo.u.Lo = sdlMant.u.Lo; + sdlLo.int64 = DivMod64by32(sdlLo.int64, pwr_cur); + } + else { + quo = 0; + sdlLo.int64 = DivMod64by32(sdlMant.int64, pwr_cur); + } + + if (sdlLo.u.Hi == 0) { + sdlMant.u.Hi = quo; + sdlMant.u.Lo = sdlLo.u.Lo; + power -= cur; + lmax -= cur; + } + } + + DECIMAL_HI32(*result) = 0; + DECIMAL_SCALE(*result) = power; + DECIMAL_LO32(*result) = sdlMant.u.Lo; + DECIMAL_MID32(*result) = sdlMant.u.Hi; + } + + DECIMAL_SIGN(*result) = (char)input.s.sign << 7; + return MONO_DECIMAL_OK; +} + +// Returns: MONO_DECIMAL_OK, or MONO_DECIMAL_INVALID_ARGUMENT +static MonoDecimalStatus +mono_decimal_to_double_result(MonoDecimal *input, double *result) +{ + SPLIT64 tmp; + double dbl; + + if (DECIMAL_SCALE(*input) > DECMAX || (DECIMAL_SIGN(*input) & ~DECIMAL_NEG) != 0) + return MONO_DECIMAL_INVALID_ARGUMENT; + + tmp.u.Lo = DECIMAL_LO32(*input); + tmp.u.Hi = DECIMAL_MID32(*input); + + if ((int32_t)DECIMAL_MID32(*input) < 0) + dbl = (ds2to64.d + (double)(int64_t)tmp.int64 + + (double)DECIMAL_HI32(*input) * ds2to64.d) / fnDblPower10(DECIMAL_SCALE(*input)) ; + else + dbl = ((double)(int64_t)tmp.int64 + + (double)DECIMAL_HI32(*input) * ds2to64.d) / fnDblPower10(DECIMAL_SCALE(*input)); + + if (DECIMAL_SIGN(*input)) + dbl = -dbl; + + *result = dbl; + return MONO_DECIMAL_OK; +} + +// Returns: MONO_DECIMAL_OK, or MONO_DECIMAL_INVALID_ARGUMENT +static MonoDecimalStatus +mono_decimal_to_float_result(MonoDecimal *input, float *result) +{ + double dbl; + + if (DECIMAL_SCALE(*input) > DECMAX || (DECIMAL_SIGN(*input) & ~DECIMAL_NEG) != 0) + return MONO_DECIMAL_INVALID_ARGUMENT; + + // Can't overflow; no errors possible. + // + mono_decimal_to_double_result(input, &dbl); + *result = (float)dbl; + return MONO_DECIMAL_OK; +} + +static void +DecShiftLeft(MonoDecimal* value) +{ + unsigned int c0 = DECIMAL_LO32(*value) & 0x80000000? 1: 0; + unsigned int c1 = DECIMAL_MID32(*value) & 0x80000000? 1: 0; + g_assert(value != NULL); + + DECIMAL_LO32(*value) <<= 1; + DECIMAL_MID32(*value) = DECIMAL_MID32(*value) << 1 | c0; + DECIMAL_HI32(*value) = DECIMAL_HI32(*value) << 1 | c1; +} + +static int +D32AddCarry(uint32_t* value, uint32_t i) +{ + uint32_t v = *value; + uint32_t sum = v + i; + *value = sum; + return sum < v || sum < i? 1: 0; +} + +static void +DecAdd(MonoDecimal *value, MonoDecimal* d) +{ + g_assert(value != NULL && d != NULL); + + if (D32AddCarry(&DECIMAL_LO32(*value), DECIMAL_LO32(*d))) { + if (D32AddCarry(&DECIMAL_MID32(*value), 1)) { + D32AddCarry(&DECIMAL_HI32(*value), 1); + } + } + if (D32AddCarry(&DECIMAL_MID32(*value), DECIMAL_MID32(*d))) { + D32AddCarry(&DECIMAL_HI32(*value), 1); + } + D32AddCarry(&DECIMAL_HI32(*value), DECIMAL_HI32(*d)); +} + +static void +DecMul10(MonoDecimal* value) +{ + MonoDecimal d = *value; + g_assert (value != NULL); + + DecShiftLeft(value); + DecShiftLeft(value); + DecAdd(value, &d); + DecShiftLeft(value); +} + +static void +DecAddInt32(MonoDecimal* value, unsigned int i) +{ + g_assert(value != NULL); + + if (D32AddCarry(&DECIMAL_LO32(*value), i)) { + if (D32AddCarry(&DECIMAL_MID32(*value), 1)) { + D32AddCarry(&DECIMAL_HI32(*value), 1); + } + } +} + +MonoDecimalCompareResult +mono_decimal_compare (MonoDecimal *left, MonoDecimal *right) +{ + uint32_t left_sign; + uint32_t right_sign; + MonoDecimal result; + + result.Hi32 = 0; // Just to shut up the compiler + + // First check signs and whether either are zero. If both are + // non-zero and of the same sign, just use subtraction to compare. + // + left_sign = left->v.v.Lo32 | left->v.v.Mid32 | left->Hi32; + right_sign = right->v.v.Lo32 | right->v.v.Mid32 | right->Hi32; + if (left_sign != 0) + left_sign = (left->u.u.sign & DECIMAL_NEG) | 1; + + if (right_sign != 0) + right_sign = (right->u.u.sign & DECIMAL_NEG) | 1; + + // left_sign & right_sign have values 1, 0, or 0x81 depending on if the left/right + // operand is +, 0, or -. + // + if (left_sign == right_sign) { + if (left_sign == 0) // both are zero + return MONO_DECIMAL_CMP_EQ; // return equal + + DecAddSub(left, right, &result, DECIMAL_NEG); + if (DECIMAL_LO64_GET(result) == 0 && result.Hi32 == 0) + return MONO_DECIMAL_CMP_EQ; + if (result.u.u.sign & DECIMAL_NEG) + return MONO_DECIMAL_CMP_LT; + return MONO_DECIMAL_CMP_GT; + } + + // + // Signs are different. Use signed byte comparison + // + if ((signed char)left_sign > (signed char)right_sign) + return MONO_DECIMAL_CMP_GT; + return MONO_DECIMAL_CMP_LT; +} + +void +mono_decimal_init_single (MonoDecimal *_this, float value) +{ + if (mono_decimal_from_float (value, _this) == MONO_DECIMAL_OVERFLOW) { + mono_set_pending_exception (mono_get_exception_overflow ()); + return; + } + _this->reserved = 0; +} + +void +mono_decimal_init_double (MonoDecimal *_this, double value) +{ + if (mono_decimal_from_double (value, _this) == MONO_DECIMAL_OVERFLOW) { + mono_set_pending_exception (mono_get_exception_overflow ()); + return; + } + _this->reserved = 0; +} + +void +mono_decimal_floor (MonoDecimal *d) +{ + MonoDecimal decRes; + + mono_decimal_round_to_int(d, &decRes); + + // copy decRes into d + COPYDEC(*d, decRes); + d->reserved = 0; + FC_GC_POLL (); +} + +int32_t +mono_decimal_get_hash_code (MonoDecimal *d) +{ + double dbl; + + if (mono_decimal_to_double_result(d, &dbl) != MONO_DECIMAL_OK) + return 0; + + if (dbl == 0.0) { + // Ensure 0 and -0 have the same hash code + return 0; + } + // conversion to double is lossy and produces rounding errors so we mask off the lowest 4 bits + // + // For example these two numerically equal decimals with different internal representations produce + // slightly different results when converted to double: + // + // decimal a = new decimal(new int[] { 0x76969696, 0x2fdd49fa, 0x409783ff, 0x00160000 }); + // => (decimal)1999021.176470588235294117647000000000 => (double)1999021.176470588 + // decimal b = new decimal(new int[] { 0x3f0f0f0f, 0x1e62edcc, 0x06758d33, 0x00150000 }); + // => (decimal)1999021.176470588235294117647000000000 => (double)1999021.1764705882 + // + return ((((int *)&dbl)[0]) & 0xFFFFFFF0) ^ ((int *)&dbl)[1]; + +} + +void +mono_decimal_multiply (MonoDecimal *d1, MonoDecimal *d2) +{ + MonoDecimal decRes; + + MonoDecimalStatus status = mono_decimal_multiply_result(d1, d2, &decRes); + if (status != MONO_DECIMAL_OK) { + mono_set_pending_exception (mono_get_exception_overflow ()); + return; + } + + COPYDEC(*d1, decRes); + d1->reserved = 0; + + FC_GC_POLL (); +} + +void +mono_decimal_round (MonoDecimal *d, int32_t decimals) +{ + MonoDecimal decRes; + + // GC is only triggered for throwing, no need to protect result + if (decimals < 0 || decimals > 28) { + mono_set_pending_exception (mono_get_exception_argument_out_of_range ("d")); + return; + } + + mono_decimal_round_result(d, decimals, &decRes); + + // copy decRes into d + COPYDEC(*d, decRes); + d->reserved = 0; + + FC_GC_POLL(); +} + +void +mono_decimal_tocurrency (MonoDecimal *decimal) +{ + // TODO +} + +double +mono_decimal_to_double (MonoDecimal d) +{ + double result = 0.0; + // Note: this can fail if the input is an invalid decimal, but for compatibility we should return 0 + mono_decimal_to_double_result(&d, &result); + return result; +} + +int32_t +mono_decimal_to_int32 (MonoDecimal d) +{ + MonoDecimal result; + + // The following can not return an error, it only returns INVALID_ARG if the decimals is < 0 + mono_decimal_round_result(&d, 0, &result); + + if (DECIMAL_SCALE(result) != 0) { + d = result; + mono_decimal_fix (&d, &result); + } + + if (DECIMAL_HI32(result) == 0 && DECIMAL_MID32(result) == 0) { + int32_t i = DECIMAL_LO32(result); + if ((int16_t)DECIMAL_SIGNSCALE(result) >= 0) { + if (i >= 0) + return i; + } else { + i = -i; + if (i <= 0) + return i; + } + } + + mono_set_pending_exception (mono_get_exception_overflow ()); + return 0; +} + +float +mono_decimal_to_float (MonoDecimal d) +{ + float result = 0.0f; + // Note: this can fail if the input is an invalid decimal, but for compatibility we should return 0 + mono_decimal_to_float_result(&d, &result); + return result; +} + +void +mono_decimal_truncate (MonoDecimal *d) +{ + MonoDecimal decRes; + + mono_decimal_fix(d, &decRes); + + // copy decRes into d + COPYDEC(*d, decRes); + d->reserved = 0; + FC_GC_POLL(); +} + +void +mono_decimal_addsub (MonoDecimal *left, MonoDecimal *right, uint8_t sign) +{ + MonoDecimal result, decTmp; + MonoDecimal *pdecTmp, *leftOriginal; + uint32_t num[6], pwr; + int scale, hi_prod, cur; + SPLIT64 sdlTmp; + + g_assert(sign == 0 || sign == DECIMAL_NEG); + + leftOriginal = left; + + sign ^= (DECIMAL_SIGN(*right) ^ DECIMAL_SIGN(*left)) & DECIMAL_NEG; + + if (DECIMAL_SCALE(*right) == DECIMAL_SCALE(*left)) { + // Scale factors are equal, no alignment necessary. + // + DECIMAL_SIGNSCALE(result) = DECIMAL_SIGNSCALE(*left); + + AlignedAdd: + if (sign) { + // Signs differ - subtract + // + DECIMAL_LO64_SET(result, (DECIMAL_LO64_GET(*left) - DECIMAL_LO64_GET(*right))); + DECIMAL_HI32(result) = DECIMAL_HI32(*left) - DECIMAL_HI32(*right); + + // Propagate carry + // + if (DECIMAL_LO64_GET(result) > DECIMAL_LO64_GET(*left)) { + DECIMAL_HI32(result)--; + if (DECIMAL_HI32(result) >= DECIMAL_HI32(*left)) + goto SignFlip; + } else if (DECIMAL_HI32(result) > DECIMAL_HI32(*left)) { + // Got negative result. Flip its sign. + // + SignFlip: + DECIMAL_LO64_SET(result, -(int64_t)DECIMAL_LO64_GET(result)); + DECIMAL_HI32(result) = ~DECIMAL_HI32(result); + if (DECIMAL_LO64_GET(result) == 0) + DECIMAL_HI32(result)++; + DECIMAL_SIGN(result) ^= DECIMAL_NEG; + } + + } else { + // Signs are the same - add + // + DECIMAL_LO64_SET(result, (DECIMAL_LO64_GET(*left) + DECIMAL_LO64_GET(*right))); + DECIMAL_HI32(result) = DECIMAL_HI32(*left) + DECIMAL_HI32(*right); + + // Propagate carry + // + if (DECIMAL_LO64_GET(result) < DECIMAL_LO64_GET(*left)) { + DECIMAL_HI32(result)++; + if (DECIMAL_HI32(result) <= DECIMAL_HI32(*left)) + goto AlignedScale; + } else if (DECIMAL_HI32(result) < DECIMAL_HI32(*left)) { + AlignedScale: + // The addition carried above 96 bits. Divide the result by 10, + // dropping the scale factor. + // + if (DECIMAL_SCALE(result) == 0) { + mono_set_pending_exception (mono_get_exception_overflow ()); + return; + } + DECIMAL_SCALE(result)--; + + sdlTmp.u.Lo = DECIMAL_HI32(result); + sdlTmp.u.Hi = 1; + sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10); + DECIMAL_HI32(result) = sdlTmp.u.Lo; + + sdlTmp.u.Lo = DECIMAL_MID32(result); + sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10); + DECIMAL_MID32(result) = sdlTmp.u.Lo; + + sdlTmp.u.Lo = DECIMAL_LO32(result); + sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10); + DECIMAL_LO32(result) = sdlTmp.u.Lo; + + // See if we need to round up. + // + if (sdlTmp.u.Hi >= 5 && (sdlTmp.u.Hi > 5 || (DECIMAL_LO32(result) & 1))) { + DECIMAL_LO64_SET(result, DECIMAL_LO64_GET(result)+1); + if (DECIMAL_LO64_GET(result) == 0) + DECIMAL_HI32(result)++; + } + } + } + } else { + // Scale factors are not equal. Assume that a larger scale + // factor (more decimal places) is likely to mean that number + // is smaller. Start by guessing that the right operand has + // the larger scale factor. The result will have the larger + // scale factor. + // + DECIMAL_SCALE(result) = DECIMAL_SCALE(*right); // scale factor of "smaller" + DECIMAL_SIGN(result) = DECIMAL_SIGN(*left); // but sign of "larger" + scale = DECIMAL_SCALE(result)- DECIMAL_SCALE(*left); + + if (scale < 0) { + // Guessed scale factor wrong. Swap operands. + // + scale = -scale; + DECIMAL_SCALE(result) = DECIMAL_SCALE(*left); + DECIMAL_SIGN(result) ^= sign; + pdecTmp = right; + right = left; + left = pdecTmp; + } + + // *left will need to be multiplied by 10^scale so + // it will have the same scale as *right. We could be + // extending it to up to 192 bits of precision. + // + if (scale <= POWER10_MAX) { + // Scaling won't make it larger than 4 uint32_ts + // + pwr = power10[scale]; + DECIMAL_LO64_SET(decTmp, UInt32x32To64(DECIMAL_LO32(*left), pwr)); + sdlTmp.int64 = UInt32x32To64(DECIMAL_MID32(*left), pwr); + sdlTmp.int64 += DECIMAL_MID32(decTmp); + DECIMAL_MID32(decTmp) = sdlTmp.u.Lo; + DECIMAL_HI32(decTmp) = sdlTmp.u.Hi; + sdlTmp.int64 = UInt32x32To64(DECIMAL_HI32(*left), pwr); + sdlTmp.int64 += DECIMAL_HI32(decTmp); + if (sdlTmp.u.Hi == 0) { + // Result fits in 96 bits. Use standard aligned add. + // + DECIMAL_HI32(decTmp) = sdlTmp.u.Lo; + left = &decTmp; + goto AlignedAdd; + } + num[0] = DECIMAL_LO32(decTmp); + num[1] = DECIMAL_MID32(decTmp); + num[2] = sdlTmp.u.Lo; + num[3] = sdlTmp.u.Hi; + hi_prod = 3; + } else { + // Have to scale by a bunch. Move the number to a buffer + // where it has room to grow as it's scaled. + // + num[0] = DECIMAL_LO32(*left); + num[1] = DECIMAL_MID32(*left); + num[2] = DECIMAL_HI32(*left); + hi_prod = 2; + + // Scan for zeros in the upper words. + // + if (num[2] == 0) { + hi_prod = 1; + if (num[1] == 0) { + hi_prod = 0; + if (num[0] == 0) { + // Left arg is zero, return right. + // + DECIMAL_LO64_SET(result, DECIMAL_LO64_GET(*right)); + DECIMAL_HI32(result) = DECIMAL_HI32(*right); + DECIMAL_SIGN(result) ^= sign; + goto RetDec; + } + } + } + + // Scaling loop, up to 10^9 at a time. hi_prod stays updated + // with index of highest non-zero uint32_t. + // + for (; scale > 0; scale -= POWER10_MAX) { + if (scale > POWER10_MAX) + pwr = ten_to_nine; + else + pwr = power10[scale]; + + sdlTmp.u.Hi = 0; + for (cur = 0; cur <= hi_prod; cur++) { + sdlTmp.int64 = UInt32x32To64(num[cur], pwr) + sdlTmp.u.Hi; + num[cur] = sdlTmp.u.Lo; + } + + if (sdlTmp.u.Hi != 0) + // We're extending the result by another uint32_t. + num[++hi_prod] = sdlTmp.u.Hi; + } + } + + // Scaling complete, do the add. Could be subtract if signs differ. + // + sdlTmp.u.Lo = num[0]; + sdlTmp.u.Hi = num[1]; + + if (sign) { + // Signs differ, subtract. + // + DECIMAL_LO64_SET(result, (sdlTmp.int64 - DECIMAL_LO64_GET(*right))); + DECIMAL_HI32(result) = num[2] - DECIMAL_HI32(*right); + + // Propagate carry + // + if (DECIMAL_LO64_GET(result) > sdlTmp.int64) { + DECIMAL_HI32(result)--; + if (DECIMAL_HI32(result) >= num[2]) + goto LongSub; + } else if (DECIMAL_HI32(result) > num[2]) { + LongSub: + // If num has more than 96 bits of precision, then we need to + // carry the subtraction into the higher bits. If it doesn't, + // then we subtracted in the wrong order and have to flip the + // sign of the result. + // + if (hi_prod <= 2) + goto SignFlip; + + cur = 3; + while(num[cur++]-- == 0); + if (num[hi_prod] == 0) + hi_prod--; + } + } else { + // Signs the same, add. + // + DECIMAL_LO64_SET(result, (sdlTmp.int64 + DECIMAL_LO64_GET(*right))); + DECIMAL_HI32(result) = num[2] + DECIMAL_HI32(*right); + + // Propagate carry + // + if (DECIMAL_LO64_GET(result) < sdlTmp.int64) { + DECIMAL_HI32(result)++; + if (DECIMAL_HI32(result) <= num[2]) + goto LongAdd; + } else if (DECIMAL_HI32(result) < num[2]) { + LongAdd: + // Had a carry above 96 bits. + // + cur = 3; + do { + if (hi_prod < cur) { + num[cur] = 1; + hi_prod = cur; + break; + } + }while (++num[cur++] == 0); + } + } + + if (hi_prod > 2) { + num[0] = DECIMAL_LO32(result); + num[1] = DECIMAL_MID32(result); + num[2] = DECIMAL_HI32(result); + DECIMAL_SCALE(result) = (uint8_t)ScaleResult(num, hi_prod, DECIMAL_SCALE(result)); + if (DECIMAL_SCALE(result) == (uint8_t)-1) { + mono_set_pending_exception (mono_get_exception_overflow ()); + return; + } + + DECIMAL_LO32(result) = num[0]; + DECIMAL_MID32(result) = num[1]; + DECIMAL_HI32(result) = num[2]; + } + } + +RetDec: + left = leftOriginal; + COPYDEC(*left, result); + left->reserved = 0; +} + +void +mono_decimal_divide (MonoDecimal *left, MonoDecimal *right) +{ + uint32_t quo[3], quo_save[3],rem[4], divisor[3]; + uint32_t pwr, tmp, tmp1; + SPLIT64 sdlTmp, sdlDivisor; + int scale, cur_scale; + gboolean unscale; + + scale = DECIMAL_SCALE(*left) - DECIMAL_SCALE(*right); + unscale = FALSE; + divisor[0] = DECIMAL_LO32(*right); + divisor[1] = DECIMAL_MID32(*right); + divisor[2] = DECIMAL_HI32(*right); + + if (divisor[1] == 0 && divisor[2] == 0) { + // Divisor is only 32 bits. Easy divide. + // + if (divisor[0] == 0) { + mono_set_pending_exception (mono_get_exception_divide_by_zero ()); + return; + } + + quo[0] = DECIMAL_LO32(*left); + quo[1] = DECIMAL_MID32(*left); + quo[2] = DECIMAL_HI32(*left); + rem[0] = Div96By32(quo, divisor[0]); + + for (;;) { + if (rem[0] == 0) { + if (scale < 0) { + cur_scale = min(9, -scale); + goto HaveScale; + } + break; + } + // We need to unscale if and only if we have a non-zero remainder + unscale = TRUE; + + // We have computed a quotient based on the natural scale + // ( - ). We have a non-zero + // remainder, so now we should increase the scale if possible to + // include more quotient bits. + // + // If it doesn't cause overflow, we'll loop scaling by 10^9 and + // computing more quotient bits as long as the remainder stays + // non-zero. If scaling by that much would cause overflow, we'll + // drop out of the loop and scale by as much as we can. + // + // Scaling by 10^9 will overflow if quo[2].quo[1] >= 2^32 / 10^9 + // = 4.294 967 296. So the upper limit is quo[2] == 4 and + // quo[1] == 0.294 967 296 * 2^32 = 1,266,874,889.7+. Since + // quotient bits in quo[0] could be all 1's, then 1,266,874,888 + // is the largest value in quo[1] (when quo[2] == 4) that is + // assured not to overflow. + // + cur_scale = SearchScale(quo[2], quo[1], quo[0], scale); + if (cur_scale == 0) { + // No more scaling to be done, but remainder is non-zero. + // Round quotient. + // + tmp = rem[0] << 1; + if (tmp < rem[0] || (tmp >= divisor[0] && + (tmp > divisor[0] || (quo[0] & 1)))) { + RoundUp: + if (!Add32To96(quo, 1)) { + if (scale == 0) { + mono_set_pending_exception (mono_get_exception_overflow ()); + return; + } + scale--; + OverflowUnscale(quo, TRUE); + break; + } + } + break; + } + + if (cur_scale < 0) { + mono_set_pending_exception (mono_get_exception_overflow ()); + return; + } + + HaveScale: + pwr = power10[cur_scale]; + scale += cur_scale; + + if (IncreaseScale(quo, pwr) != 0) { + mono_set_pending_exception (mono_get_exception_overflow ()); + return; + } + + sdlTmp.int64 = DivMod64by32(UInt32x32To64(rem[0], pwr), divisor[0]); + rem[0] = sdlTmp.u.Hi; + + if (!Add32To96(quo, sdlTmp.u.Lo)) { + if (scale == 0) { + mono_set_pending_exception (mono_get_exception_overflow ()); + return; + } + scale--; + OverflowUnscale(quo, (rem[0] != 0)); + break; + } + } // for (;;) + } else { + // Divisor has bits set in the upper 64 bits. + // + // Divisor must be fully normalized (shifted so bit 31 of the most + // significant uint32_t is 1). Locate the MSB so we know how much to + // normalize by. The dividend will be shifted by the same amount so + // the quotient is not changed. + // + if (divisor[2] == 0) + tmp = divisor[1]; + else + tmp = divisor[2]; + + cur_scale = 0; + if (!(tmp & 0xFFFF0000)) { + cur_scale += 16; + tmp <<= 16; + } + if (!(tmp & 0xFF000000)) { + cur_scale += 8; + tmp <<= 8; + } + if (!(tmp & 0xF0000000)) { + cur_scale += 4; + tmp <<= 4; + } + if (!(tmp & 0xC0000000)) { + cur_scale += 2; + tmp <<= 2; + } + if (!(tmp & 0x80000000)) { + cur_scale++; + tmp <<= 1; + } + + // Shift both dividend and divisor left by cur_scale. + // + sdlTmp.int64 = DECIMAL_LO64_GET(*left) << cur_scale; + rem[0] = sdlTmp.u.Lo; + rem[1] = sdlTmp.u.Hi; + sdlTmp.u.Lo = DECIMAL_MID32(*left); + sdlTmp.u.Hi = DECIMAL_HI32(*left); + sdlTmp.int64 <<= cur_scale; + rem[2] = sdlTmp.u.Hi; + rem[3] = (DECIMAL_HI32(*left) >> (31 - cur_scale)) >> 1; + + sdlDivisor.u.Lo = divisor[0]; + sdlDivisor.u.Hi = divisor[1]; + sdlDivisor.int64 <<= cur_scale; + + if (divisor[2] == 0) { + // Have a 64-bit divisor in sdlDivisor. The remainder + // (currently 96 bits spread over 4 uint32_ts) will be < divisor. + // + sdlTmp.u.Lo = rem[2]; + sdlTmp.u.Hi = rem[3]; + + quo[2] = 0; + quo[1] = Div96By64(&rem[1], sdlDivisor); + quo[0] = Div96By64(rem, sdlDivisor); + + for (;;) { + if ((rem[0] | rem[1]) == 0) { + if (scale < 0) { + cur_scale = min(9, -scale); + goto HaveScale64; + } + break; + } + + // We need to unscale if and only if we have a non-zero remainder + unscale = TRUE; + + // Remainder is non-zero. Scale up quotient and remainder by + // powers of 10 so we can compute more significant bits. + // + cur_scale = SearchScale(quo[2], quo[1], quo[0], scale); + if (cur_scale == 0) { + // No more scaling to be done, but remainder is non-zero. + // Round quotient. + // + sdlTmp.u.Lo = rem[0]; + sdlTmp.u.Hi = rem[1]; + if (sdlTmp.u.Hi >= 0x80000000 || (sdlTmp.int64 <<= 1) > sdlDivisor.int64 || + (sdlTmp.int64 == sdlDivisor.int64 && (quo[0] & 1))) + goto RoundUp; + break; + } + + if (cur_scale < 0) { + mono_set_pending_exception (mono_get_exception_overflow ()); + return; + } + + HaveScale64: + pwr = power10[cur_scale]; + scale += cur_scale; + + if (IncreaseScale(quo, pwr) != 0) { + mono_set_pending_exception (mono_get_exception_overflow ()); + return; + } + + rem[2] = 0; // rem is 64 bits, IncreaseScale uses 96 + IncreaseScale(rem, pwr); + tmp = Div96By64(rem, sdlDivisor); + if (!Add32To96(quo, tmp)) { + if (scale == 0) { + mono_set_pending_exception (mono_get_exception_overflow ()); + return; + } + scale--; + OverflowUnscale(quo, (rem[0] != 0 || rem[1] != 0)); + break; + } + + } // for (;;) + } else { + // Have a 96-bit divisor in divisor[]. + // + // Start by finishing the shift left by cur_scale. + // + sdlTmp.u.Lo = divisor[1]; + sdlTmp.u.Hi = divisor[2]; + sdlTmp.int64 <<= cur_scale; + divisor[0] = sdlDivisor.u.Lo; + divisor[1] = sdlDivisor.u.Hi; + divisor[2] = sdlTmp.u.Hi; + + // The remainder (currently 96 bits spread over 4 uint32_ts) + // will be < divisor. + // + quo[2] = 0; + quo[1] = 0; + quo[0] = Div128By96(rem, divisor); + + for (;;) { + if ((rem[0] | rem[1] | rem[2]) == 0) { + if (scale < 0) { + cur_scale = min(9, -scale); + goto HaveScale96; + } + break; + } + + // We need to unscale if and only if we have a non-zero remainder + unscale = TRUE; + + // Remainder is non-zero. Scale up quotient and remainder by + // powers of 10 so we can compute more significant bits. + // + cur_scale = SearchScale(quo[2], quo[1], quo[0], scale); + if (cur_scale == 0) { + // No more scaling to be done, but remainder is non-zero. + // Round quotient. + // + if (rem[2] >= 0x80000000) + goto RoundUp; + + tmp = rem[0] > 0x80000000; + tmp1 = rem[1] > 0x80000000; + rem[0] <<= 1; + rem[1] = (rem[1] << 1) + tmp; + rem[2] = (rem[2] << 1) + tmp1; + + if (rem[2] > divisor[2] || (rem[2] == divisor[2] && (rem[1] > divisor[1] || rem[1] == (divisor[1] && (rem[0] > divisor[0] || (rem[0] == divisor[0] && (quo[0] & 1))))))) + goto RoundUp; + break; + } + + if (cur_scale < 0) { + mono_set_pending_exception (mono_get_exception_overflow ()); + return; + } + + HaveScale96: + pwr = power10[cur_scale]; + scale += cur_scale; + + if (IncreaseScale(quo, pwr) != 0) { + mono_set_pending_exception (mono_get_exception_overflow ()); + return; + } + + rem[3] = IncreaseScale(rem, pwr); + tmp = Div128By96(rem, divisor); + if (!Add32To96(quo, tmp)) { + if (scale == 0) { + mono_set_pending_exception (mono_get_exception_overflow ()); + return; + } + + scale--; + OverflowUnscale(quo, (rem[0] != 0 || rem[1] != 0 || rem[2] != 0 || rem[3] != 0)); + break; + } + + } // for (;;) + } + } + + // We need to unscale if and only if we have a non-zero remainder + if (unscale) { + // Try extracting any extra powers of 10 we may have + // added. We do this by trying to divide out 10^8, 10^4, 10^2, and 10^1. + // If a division by one of these powers returns a zero remainder, then + // we keep the quotient. If the remainder is not zero, then we restore + // the previous value. + // + // Since 10 = 2 * 5, there must be a factor of 2 for every power of 10 + // we can extract. We use this as a quick test on whether to try a + // given power. + // + while ((quo[0] & 0xFF) == 0 && scale >= 8) { + quo_save[0] = quo[0]; + quo_save[1] = quo[1]; + quo_save[2] = quo[2]; + + if (Div96By32(quo_save, 100000000) == 0) { + quo[0] = quo_save[0]; + quo[1] = quo_save[1]; + quo[2] = quo_save[2]; + scale -= 8; + } else + break; + } + + if ((quo[0] & 0xF) == 0 && scale >= 4) { + quo_save[0] = quo[0]; + quo_save[1] = quo[1]; + quo_save[2] = quo[2]; + + if (Div96By32(quo_save, 10000) == 0) { + quo[0] = quo_save[0]; + quo[1] = quo_save[1]; + quo[2] = quo_save[2]; + scale -= 4; + } + } + + if ((quo[0] & 3) == 0 && scale >= 2) { + quo_save[0] = quo[0]; + quo_save[1] = quo[1]; + quo_save[2] = quo[2]; + + if (Div96By32(quo_save, 100) == 0) { + quo[0] = quo_save[0]; + quo[1] = quo_save[1]; + quo[2] = quo_save[2]; + scale -= 2; + } + } + + if ((quo[0] & 1) == 0 && scale >= 1) { + quo_save[0] = quo[0]; + quo_save[1] = quo[1]; + quo_save[2] = quo[2]; + + if (Div96By32(quo_save, 10) == 0) { + quo[0] = quo_save[0]; + quo[1] = quo_save[1]; + quo[2] = quo_save[2]; + scale -= 1; + } + } + } + + DECIMAL_SIGN(*left) = DECIMAL_SIGN(*left) ^ DECIMAL_SIGN(*right); + DECIMAL_HI32(*left) = quo[2]; + DECIMAL_MID32(*left) = quo[1]; + DECIMAL_LO32(*left) = quo[0]; + DECIMAL_SCALE(*left) = (uint8_t)scale; + left->reserved = 0; + +} + +#define DECIMAL_PRECISION 29 + +int +mono_decimal_from_number (void *from, MonoDecimal *target) +{ + MonoNumber *number = (MonoNumber *) from; + uint16_t* p = number->digits; + MonoDecimal d; + int e = number->scale; + g_assert(number != NULL); + g_assert(target != NULL); + + d.reserved = 0; + DECIMAL_SIGNSCALE(d) = 0; + DECIMAL_HI32(d) = 0; + DECIMAL_LO32(d) = 0; + DECIMAL_MID32(d) = 0; + g_assert(p != NULL); + if (!*p) { + // To avoid risking an app-compat issue with pre 4.5 (where some app was illegally using Reflection to examine the internal scale bits), we'll only force + // the scale to 0 if the scale was previously positive + if (e > 0) { + e = 0; + } + } else { + if (e > DECIMAL_PRECISION) return 0; + while ((e > 0 || (*p && e > -28)) && (DECIMAL_HI32(d) < 0x19999999 || (DECIMAL_HI32(d) == 0x19999999 && (DECIMAL_MID32(d) < 0x99999999 || (DECIMAL_MID32(d) == 0x99999999 && (DECIMAL_LO32(d) < 0x99999999 || (DECIMAL_LO32(d) == 0x99999999 && *p <= '5'))))))) { + DecMul10(&d); + if (*p) + DecAddInt32(&d, *p++ - '0'); + e--; + } + if (*p++ >= '5') { + gboolean round = TRUE; + if (*(p-1) == '5' && *(p-2) % 2 == 0) { // Check if previous digit is even, only if the when we are unsure whether hows to do Banker's rounding + // For digits > 5 we will be roundinp up anyway. + int count = 20; // Look at the next 20 digits to check to round + while (*p == '0' && count != 0) { + p++; + count--; + } + if (*p == '\0' || count == 0) + round = FALSE;// Do nothing + } + + if (round) { + DecAddInt32(&d, 1); + if ((DECIMAL_HI32(d) | DECIMAL_MID32(d) | DECIMAL_LO32(d)) == 0) { + DECIMAL_HI32(d) = 0x19999999; + DECIMAL_MID32(d) = 0x99999999; + DECIMAL_LO32(d) = 0x9999999A; + e++; + } + } + } + } + if (e > 0) + return 0; + if (e <= -DECIMAL_PRECISION) { + // Parsing a large scale zero can give you more precision than fits in the decimal. + // This should only happen for actual zeros or very small numbers that round to zero. + DECIMAL_SIGNSCALE(d) = 0; + DECIMAL_HI32(d) = 0; + DECIMAL_LO32(d) = 0; + DECIMAL_MID32(d) = 0; + DECIMAL_SCALE(d) = (DECIMAL_PRECISION - 1); + } else { + DECIMAL_SCALE(d) = (uint8_t)(-e); + } + + DECIMAL_SIGN(d) = number->sign? DECIMAL_NEG: 0; + *target = d; + return 1; +} + + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/decimal-ms.h b/unity-2019.4.24f1-mbe/mono/metadata/decimal-ms.h new file mode 100644 index 000000000..38594e719 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/decimal-ms.h @@ -0,0 +1,66 @@ +/** + * \file + */ + +#ifndef __MONO_DECIMAL_MS_H__ +#define __MONO_DECIMAL_MS_H__ + +typedef struct { + // Decimal.cs treats the first two shorts as one long + // And they seriable the data so we need to little endian + // seriliazation + // The wReserved overlaps with Variant's vt member +#if G_BYTE_ORDER != G_LITTLE_ENDIAN + union { + struct { + uint8_t sign; + uint8_t scale; + } u; + uint16_t signscale; + } u; + uint16_t reserved; +#else + uint16_t reserved; + union { + struct { + uint8_t scale; + uint8_t sign; + } u; + uint16_t signscale; + } u; +#endif + uint32_t Hi32; + union { + struct { + uint32_t Lo32; + uint32_t Mid32; + } v; + uint64_t Lo64; + } v; +} MonoDecimal; + +typedef enum { + MONO_DECIMAL_CMP_LT=-1, + MONO_DECIMAL_CMP_EQ, + MONO_DECIMAL_CMP_GT +} MonoDecimalCompareResult; + +MonoDecimalCompareResult + mono_decimal_compare (MonoDecimal *left, MonoDecimal *right); + +void mono_decimal_init_single (MonoDecimal *_this, float value); +void mono_decimal_init_double (MonoDecimal *_this, double value); +void mono_decimal_floor (MonoDecimal *d); +int32_t mono_decimal_get_hash_code (MonoDecimal *d); +void mono_decimal_multiply (MonoDecimal *d1, MonoDecimal *d2); +void mono_decimal_round (MonoDecimal *d, int32_t decimals); +void mono_decimal_tocurrency (MonoDecimal *decimal); +double mono_decimal_to_double (MonoDecimal d); +int32_t mono_decimal_to_int32 (MonoDecimal d); +float mono_decimal_to_float (MonoDecimal d); +void mono_decimal_truncate (MonoDecimal *d); +void mono_decimal_addsub (MonoDecimal *left, MonoDecimal *right, uint8_t sign); +void mono_decimal_divide (MonoDecimal *left, MonoDecimal *right); +int mono_decimal_from_number (void *from, MonoDecimal *target); + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/domain-internals.h b/unity-2019.4.24f1-mbe/mono/metadata/domain-internals.h new file mode 100644 index 000000000..fbfe9d336 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/domain-internals.h @@ -0,0 +1,630 @@ +/** + * \file + * Appdomain-related internal data structures and functions. + * Copyright 2012 Xamarin Inc (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#ifndef __MONO_METADATA_DOMAIN_INTERNALS_H__ +#define __MONO_METADATA_DOMAIN_INTERNALS_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * If this is set, the memory belonging to appdomains is not freed when a domain is + * unloaded, and assemblies loaded by the appdomain are not unloaded either. This + * allows us to use typed gc in non-default appdomains too, leading to increased + * performance. + */ +extern gboolean mono_dont_free_domains; + +/* This is a copy of System.AppDomainSetup */ +typedef struct { + MonoObject object; + MonoString *application_base; + MonoString *application_name; + MonoString *cache_path; + MonoString *configuration_file; + MonoString *dynamic_base; + MonoString *license_file; + MonoString *private_bin_path; + MonoString *private_bin_path_probe; + MonoString *shadow_copy_directories; + MonoString *shadow_copy_files; + MonoBoolean publisher_policy; + MonoBoolean path_changed; + int loader_optimization; + MonoBoolean disallow_binding_redirects; + MonoBoolean disallow_code_downloads; + MonoObject *activation_arguments; /* it is System.Object in 1.x, ActivationArguments in 2.0 */ + MonoObject *domain_initializer; + MonoObject *application_trust; /* it is System.Object in 1.x, ApplicationTrust in 2.0 */ + MonoArray *domain_initializer_args; + MonoBoolean disallow_appbase_probe; + MonoArray *configuration_bytes; + MonoArray *serialized_non_primitives; +} MonoAppDomainSetup; + +typedef struct _MonoJitInfoTable MonoJitInfoTable; +typedef struct _MonoJitInfoTableChunk MonoJitInfoTableChunk; + +#define MONO_JIT_INFO_TABLE_CHUNK_SIZE 64 + +struct _MonoJitInfoTableChunk +{ + int refcount; + volatile int num_elements; + volatile gint8 *last_code_end; + MonoJitInfo *next_tombstone; + MonoJitInfo * volatile data [MONO_JIT_INFO_TABLE_CHUNK_SIZE]; +}; + +struct _MonoJitInfoTable +{ + MonoDomain *domain; + int num_chunks; + int num_valid; + MonoJitInfoTableChunk *chunks [MONO_ZERO_LEN_ARRAY]; +}; + +#define MONO_SIZEOF_JIT_INFO_TABLE (sizeof (struct _MonoJitInfoTable) - MONO_ZERO_LEN_ARRAY * SIZEOF_VOID_P) + +typedef GArray MonoAotModuleInfoTable; + +typedef struct { + guint32 flags; + gint32 exvar_offset; + gpointer try_start; + gpointer try_end; + gpointer handler_start; + /* + * For LLVM compiled code, this is the index of the il clause + * associated with this handler. + */ + int clause_index; + uint32_t try_offset; + uint32_t try_len; + uint32_t handler_offset; + uint32_t handler_len; + union { + MonoClass *catch_class; + gpointer filter; + gpointer handler_end; + } data; +} MonoJitExceptionInfo; + +/* + * Contains information about the type arguments for generic shared methods. + */ +typedef struct { + gboolean is_gsharedvt; +} MonoGenericSharingContext; + +/* Simplified DWARF location list entry */ +typedef struct { + /* Whenever the value is in a register */ + gboolean is_reg; + /* + * If is_reg is TRUE, the register which contains the value. Otherwise + * the base register. + */ + int reg; + /* + * If is_reg is FALSE, the offset of the stack location relative to 'reg'. + * Otherwise, 0. + */ + int offset; + /* + * Offsets of the PC interval where the value is in this location. + */ + int from, to; +} MonoDwarfLocListEntry; + +typedef struct +{ + MonoGenericSharingContext *generic_sharing_context; + int nlocs; + MonoDwarfLocListEntry *locations; + gint32 this_offset; + guint8 this_reg; + gboolean has_this:1; + gboolean this_in_reg:1; +} MonoGenericJitInfo; + +/* +A try block hole is used to represent a non-contiguous part of +of a segment of native code protected by a given .try block. +Usually, a try block is defined as a contiguous segment of code. +But in some cases it's needed to have some parts of it to not be protected. +For example, given "try {} finally {}", the code in the .try block to call +the finally part looks like: + +try { + ... + call finally_block + adjust stack + jump outside try block + ... +} finally { + ... +} + +The instructions between the call and the jump should not be under the try block since they happen +after the finally block executes, which means if an async exceptions happens at that point we would +execute the finally clause twice. So, to avoid this, we introduce a hole in the try block to signal +that those instructions are not protected. +*/ +typedef struct +{ + guint32 offset; + guint16 clause; + guint16 length; +} MonoTryBlockHoleJitInfo; + +typedef struct +{ + guint16 num_holes; + MonoTryBlockHoleJitInfo holes [MONO_ZERO_LEN_ARRAY]; +} MonoTryBlockHoleTableJitInfo; + +typedef struct +{ + guint32 stack_size; + guint32 epilog_size; +} MonoArchEHJitInfo; + +typedef struct { + /* Relative to code_start */ + int thunks_offset; + int thunks_size; +} MonoThunkJitInfo; + +typedef struct { + guint8 *unw_info; + int unw_info_len; +} MonoUnwindJitInfo; + +typedef enum { + JIT_INFO_NONE = 0, + JIT_INFO_HAS_GENERIC_JIT_INFO = (1 << 0), + JIT_INFO_HAS_TRY_BLOCK_HOLES = (1 << 1), + JIT_INFO_HAS_ARCH_EH_INFO = (1 << 2), + JIT_INFO_HAS_THUNK_INFO = (1 << 3), + /* + * If this is set, the unwind info is stored in the structure, instead of being pointed to by the + * 'unwind_info' field. + */ + JIT_INFO_HAS_UNWIND_INFO = (1 << 4) +} MonoJitInfoFlags; + +struct _MonoJitInfo { + /* NOTE: These first two elements (method and + next_jit_code_hash) must be in the same order and at the + same offset as in RuntimeMethod, because of the jit_code_hash + internal hash table in MonoDomain. */ + union { + MonoMethod *method; + MonoImage *image; + gpointer aot_info; + gpointer tramp_info; + } d; + union { + struct _MonoJitInfo *next_jit_code_hash; + struct _MonoJitInfo *next_tombstone; + } n; + gpointer code_start; + guint32 unwind_info; + int code_size; + guint32 num_clauses:15; + /* Whenever the code is domain neutral or 'shared' */ + gboolean domain_neutral:1; + gboolean has_generic_jit_info:1; + gboolean has_try_block_holes:1; + gboolean has_arch_eh_info:1; + gboolean has_thunk_info:1; + gboolean has_unwind_info:1; + gboolean from_aot:1; + gboolean from_llvm:1; + gboolean dbg_attrs_inited:1; + gboolean dbg_hidden:1; + /* Whenever this jit info was loaded in async context */ + gboolean async:1; + gboolean dbg_step_through:1; + gboolean dbg_non_user_code:1; + /* + * Whenever this jit info refers to a trampoline. + * d.tramp_info contains additional data in this case. + */ + gboolean is_trampoline:1; + /* 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 */ + + MonoJitExceptionInfo clauses [MONO_ZERO_LEN_ARRAY]; + /* There is an optional MonoGenericJitInfo after the clauses */ + /* There is an optional MonoTryBlockHoleTableJitInfo after MonoGenericJitInfo clauses*/ + /* There is an optional MonoArchEHJitInfo after MonoTryBlockHoleTableJitInfo */ + /* There is an optional MonoThunkJitInfo after MonoArchEHJitInfo */ +}; + +#define MONO_SIZEOF_JIT_INFO (offsetof (struct _MonoJitInfo, clauses)) + +typedef struct { + gpointer *static_data; /* Used to free the static data without going through the MonoAppContext object itself. */ + uint32_t gc_handle; +} ContextStaticData; + +struct _MonoAppContext { + MonoObject obj; + gint32 domain_id; + gint32 context_id; + gpointer *static_data; + ContextStaticData *data; +}; + +/* Lock-free allocator */ +typedef struct { + guint8 *mem; + gpointer prev; + int size, pos; +} LockFreeMempoolChunk; + +typedef struct { + LockFreeMempoolChunk *current, *chunks; +} LockFreeMempool; + +/* + * We have two unloading states because the domain + * must remain fully functional while AppDomain::DomainUnload is + * processed. + * After that unloading began and all domain facilities are teared down + * such as execution of new threadpool jobs. + */ +typedef enum { + MONO_APPDOMAIN_CREATED, + MONO_APPDOMAIN_UNLOADING_START, + MONO_APPDOMAIN_UNLOADING, + MONO_APPDOMAIN_UNLOADED +} MonoAppDomainState; + +typedef struct _MonoThunkFreeList { + guint32 size; + int length; /* only valid for the wait list */ + struct _MonoThunkFreeList *next; +} MonoThunkFreeList; + +typedef struct _MonoJitCodeHash MonoJitCodeHash; + +struct _MonoDomain { + /* + * This lock must never be taken before the loader lock, + * i.e. if both are taken by the same thread, the loader lock + * must taken first. + */ + MonoCoopMutex lock; + MonoMemPool *mp; + MonoCodeManager *code_mp; + /* + * keep all the managed objects close to each other for the precise GC + * For the Boehm GC we additionally keep close also other GC-tracked pointers. + */ +#define MONO_DOMAIN_FIRST_OBJECT setup + MonoAppDomainSetup *setup; + MonoAppDomain *domain; + MonoAppContext *default_context; + MonoException *out_of_memory_ex; + MonoException *null_reference_ex; + MonoException *stack_overflow_ex; + /* typeof (void) */ + MonoObject *typeof_void; + /* Ephemeron Tombstone*/ + MonoObject *ephemeron_tombstone; + /* new MonoType [0] */ + MonoArray *empty_types; + MonoString *empty_string; + /* + * The fields between FIRST_GC_TRACKED and LAST_GC_TRACKED are roots, but + * not object references. + */ +#define MONO_DOMAIN_FIRST_GC_TRACKED env + MonoGHashTable *env; + MonoGHashTable *ldstr_table; + /* hashtables for Reflection handles */ + MonoGHashTable *type_hash; + MonoConcGHashTable *refobject_hash; + /* maps class -> type initialization exception object */ + MonoGHashTable *type_init_exception_hash; + /* maps delegate trampoline addr -> delegate object */ + MonoGHashTable *delegate_hash_table; +#define MONO_DOMAIN_LAST_GC_TRACKED delegate_hash_table + guint32 state; + /* Needed by Thread:GetDomainID() */ + gint32 domain_id; + gint32 shadow_serial; + GSList *domain_assemblies; + MonoAssembly *entry_assembly; + char *friendly_name; + GPtrArray *class_vtable_array; + /* maps remote class key -> MonoRemoteClass */ + GHashTable *proxy_vtable_hash; + /* Protected by 'jit_code_hash_lock' */ + MonoInternalHashTable jit_code_hash; + mono_mutex_t jit_code_hash_lock; + int num_jit_info_tables; + MonoJitInfoTable * + volatile jit_info_table; + /* + * Contains information about AOT loaded code. + * Only used in the root domain. + */ + MonoJitInfoTable * + volatile aot_modules; + GSList *jit_info_free_queue; + /* Used when loading assemblies */ + gchar **search_path; + gchar *private_bin_path; + LockFreeMempool *lock_free_mp; + + /* Used by remoting proxies */ + MonoMethod *create_proxy_for_type_method; + MonoMethod *private_invoke_method; + /* Used to store offsets of thread and context static fields */ + GHashTable *special_static_fields; + /* + * This must be a GHashTable, since these objects can't be finalized + * if the hashtable contains a GC visible reference to them. + */ + GHashTable *finalizable_objects_hash; + + /* Protects the three hashes above */ + mono_mutex_t finalizable_objects_hash_lock; + /* Used when accessing 'domain_assemblies' */ + mono_mutex_t assemblies_lock; + + GHashTable *method_rgctx_hash; + + GHashTable *generic_virtual_cases; + + /* Information maintained by the JIT engine */ + gpointer runtime_info; + + /* Contains the compiled runtime invoke wrapper used by finalizers */ + gpointer finalize_runtime_invoke; + + /* Contains the compiled runtime invoke wrapper used by async resylt creation to capture thread context*/ + gpointer capture_context_runtime_invoke; + + /* Contains the compiled method used by async resylt creation to capture thread context*/ + gpointer capture_context_method; + + /* Assembly bindings, the per-domain part */ + GSList *assembly_bindings; + gboolean assembly_bindings_parsed; + + /* Used by socket-io.c */ + /* These are domain specific, since the assembly can be unloaded */ + MonoImage *socket_assembly; + MonoClass *sockaddr_class; + MonoClassField *sockaddr_data_field; + MonoClassField *sockaddr_data_length_field; + + /* Cache function pointers for architectures */ + /* that require wrappers */ + GHashTable *ftnptrs_hash; + + /* Maps MonoMethod* to weak links to DynamicMethod objects */ + GHashTable *method_to_dyn_method; + + /* support */ + gboolean throw_unobserved_task_exceptions; + + guint32 execution_context_field_offset; +}; + +typedef struct { + guint16 major, minor, build, revision; +} AssemblyVersionSet; + +/* MonoRuntimeInfo: Contains information about versions supported by this runtime */ +typedef struct { + const char runtime_version [12]; + const char framework_version [4]; + const AssemblyVersionSet version_sets [5]; +} MonoRuntimeInfo; + +#define mono_domain_assemblies_lock(domain) mono_locks_os_acquire(&(domain)->assemblies_lock, DomainAssembliesLock) +#define mono_domain_assemblies_unlock(domain) mono_locks_os_release(&(domain)->assemblies_lock, DomainAssembliesLock) +#define mono_domain_jit_code_hash_lock(domain) mono_locks_os_acquire(&(domain)->jit_code_hash_lock, DomainJitCodeHashLock) +#define mono_domain_jit_code_hash_unlock(domain) mono_locks_os_release(&(domain)->jit_code_hash_lock, DomainJitCodeHashLock) + +typedef MonoDomain* (*MonoLoadFunc) (const char *filename, const char *runtime_version); + +void mono_domain_lock (MonoDomain *domain) MONO_LLVM_INTERNAL; +void mono_domain_unlock (MonoDomain *domain) MONO_LLVM_INTERNAL; + +void +mono_install_runtime_load (MonoLoadFunc func); + +MonoDomain* +mono_runtime_load (const char *filename, const char *runtime_version); + +typedef void (*MonoCreateDomainFunc) (MonoDomain *domain); + +void +mono_install_create_domain_hook (MonoCreateDomainFunc func); + +typedef void (*MonoFreeDomainFunc) (MonoDomain *domain); + +void +mono_install_free_domain_hook (MonoFreeDomainFunc func); + +void +mono_cleanup (void); + +void +mono_close_exe_image (void); + +int +mono_jit_info_size (MonoJitInfoFlags flags, int num_clauses, int num_holes); + +void +mono_jit_info_init (MonoJitInfo *ji, MonoMethod *method, guint8 *code, int code_size, + MonoJitInfoFlags flags, int num_clauses, int num_holes); + +MonoJitInfoTable * +mono_jit_info_table_new (MonoDomain *domain); + +void +mono_jit_info_table_free (MonoJitInfoTable *table); + +void +mono_jit_info_table_add (MonoDomain *domain, MonoJitInfo *ji); + +void +mono_jit_info_table_foreach (MonoDomain *domain, MonoJitInfoFunc func, void *user_data); + +void +mono_jit_info_table_remove (MonoDomain *domain, MonoJitInfo *ji); + +void +mono_jit_info_add_aot_module (MonoImage *image, gpointer start, gpointer end); + +MonoGenericJitInfo* +mono_jit_info_get_generic_jit_info (MonoJitInfo *ji); + +MonoGenericSharingContext* +mono_jit_info_get_generic_sharing_context (MonoJitInfo *ji); + +void +mono_jit_info_set_generic_sharing_context (MonoJitInfo *ji, MonoGenericSharingContext *gsctx); + +char * +mono_make_shadow_copy (const char *filename, MonoError *error); + +gboolean +mono_is_shadow_copy_enabled (MonoDomain *domain, const gchar *dir_name); + +gpointer +mono_domain_alloc (MonoDomain *domain, guint size); + +gpointer +mono_domain_alloc0 (MonoDomain *domain, guint size); + +gpointer +mono_domain_alloc0_lock_free (MonoDomain *domain, guint size); + +void* +mono_domain_code_reserve (MonoDomain *domain, int size) MONO_LLVM_INTERNAL; + +void* +mono_domain_code_reserve_align (MonoDomain *domain, int size, int alignment); + +void +mono_domain_code_commit (MonoDomain *domain, void *data, int size, int newsize); + +void +mono_domain_code_foreach (MonoDomain *domain, MonoCodeManagerFunc func, void *user_data); + +void +mono_domain_unset (void); + +void +mono_domain_set_internal_with_options (MonoDomain *domain, gboolean migrate_exception); + +gboolean +mono_domain_set_config_checked (MonoDomain *domain, const char *base_dir, const char *config_file_name, MonoError *error); + +MonoTryBlockHoleTableJitInfo* +mono_jit_info_get_try_block_hole_table_info (MonoJitInfo *ji); + +MonoArchEHJitInfo* +mono_jit_info_get_arch_eh_info (MonoJitInfo *ji); + +MonoThunkJitInfo* +mono_jit_info_get_thunk_info (MonoJitInfo *ji); + +MonoUnwindJitInfo* +mono_jit_info_get_unwind_info (MonoJitInfo *ji); + +/* + * Installs a new function which is used to return a MonoJitInfo for a method inside + * an AOT module. + */ +typedef MonoJitInfo *(*MonoJitInfoFindInAot) (MonoDomain *domain, MonoImage *image, gpointer addr); +void mono_install_jit_info_find_in_aot (MonoJitInfoFindInAot func); + +void +mono_jit_code_hash_init (MonoInternalHashTable *jit_code_hash); + +MonoAssembly * +mono_assembly_load_corlib (const MonoRuntimeInfo *runtime, MonoImageOpenStatus *status); + +const MonoRuntimeInfo* +mono_get_runtime_info (void); + +void +mono_runtime_set_no_exec (gboolean val); + +gboolean +mono_runtime_get_no_exec (void); + +void +mono_domain_parse_assembly_bindings (MonoDomain *domain, int amajor, int aminor, gchar *domain_config_file_name); + +UNITY_MONO_API gboolean +mono_assembly_name_parse (const char *name, MonoAssemblyName *aname); + +MonoImage *mono_assembly_open_from_bundle (const char *filename, + MonoImageOpenStatus *status, + gboolean refonly); + +MonoAssembly * +mono_try_assembly_resolve (MonoDomain *domain, const char *fname, MonoAssembly *requesting, gboolean refonly, MonoError *error); + +MonoAssembly * +mono_domain_assembly_postload_search (MonoAssemblyName *aname, MonoAssembly *requesting, gboolean refonly); + +MonoAssembly* mono_assembly_load_full_nosearch (MonoAssemblyName *aname, + const char *basedir, + MonoImageOpenStatus *status, + gboolean refonly); + +void mono_domain_set_options_from_config (MonoDomain *domain); + +int mono_framework_version (void); + +void mono_reflection_cleanup_domain (MonoDomain *domain); + +void mono_assembly_cleanup_domain_bindings (guint32 domain_id); + +MonoJitInfo* mono_jit_info_table_find_internal (MonoDomain *domain, char *addr, gboolean try_aot, gboolean allow_trampolines); + +void mono_enable_debug_domain_unload (gboolean enable); + +MonoReflectionAssembly * +mono_domain_try_type_resolve_checked (MonoDomain *domain, char *name, MonoObject *tb, MonoError *error); + +void +mono_runtime_init_checked (MonoDomain *domain, MonoThreadStartCB start_cb, MonoThreadAttachCB attach_cb, MonoError *error); + +void +mono_context_init_checked (MonoDomain *domain, MonoError *error); + +gboolean +mono_assembly_has_reference_assembly_attribute (MonoAssembly *assembly, MonoError *error); + +GPtrArray* +mono_domain_get_assemblies (MonoDomain *domain, gboolean refonly); + +#endif /* __MONO_METADATA_DOMAIN_INTERNALS_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/domain.c b/unity-2019.4.24f1-mbe/mono/metadata/domain.c new file mode 100644 index 000000000..8188b9d28 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/domain.c @@ -0,0 +1,2022 @@ +/** + * \file + * MonoDomain functions + * + * Author: + * Dietmar Maurer (dietmar@ximian.com) + * Patrik Torstensson + * + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Copyright 2011-2012 Xamarin, Inc (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define DEBUG_DOMAIN_UNLOAD 1 + +#define GET_APPDOMAIN() ((MonoDomain*)mono_tls_get_domain ()) +#define SET_APPDOMAIN(x) do { \ + MonoThreadInfo *info; \ + mono_tls_set_domain (x); \ + info = mono_thread_info_current (); \ + if (info) \ + mono_thread_info_tls_set (info, TLS_KEY_DOMAIN, (x)); \ +} while (FALSE) + +#define GET_APPCONTEXT() (mono_thread_internal_current ()->current_appcontext) +#define SET_APPCONTEXT(x) MONO_OBJECT_SETREF (mono_thread_internal_current (), current_appcontext, (x)) + +static guint16 appdomain_list_size = 0; +static guint16 appdomain_next = 0; +static MonoDomain **appdomains_list = NULL; +static MonoImage *exe_image; +static gboolean debug_domain_unload; + +gboolean mono_dont_free_domains; + +#define mono_appdomains_lock() mono_coop_mutex_lock (&appdomains_mutex) +#define mono_appdomains_unlock() mono_coop_mutex_unlock (&appdomains_mutex) +static MonoCoopMutex appdomains_mutex; + +static MonoDomain *mono_root_domain = NULL; + +/* some statistics */ +static int max_domain_code_size = 0; +static int max_domain_code_alloc = 0; +static int total_domain_code_alloc = 0; + +/* AppConfigInfo: Information about runtime versions supported by an + * aplication. + */ +typedef struct { + GSList *supported_runtimes; + char *required_runtime; + int configuration_count; + int startup_count; +} AppConfigInfo; + +static const MonoRuntimeInfo *current_runtime = NULL; + +#define NOT_AVAIL {0xffffU,0xffffU,0xffffU,0xffffU} + +/* This is the list of runtime versions supported by this JIT. + */ +static const MonoRuntimeInfo supported_runtimes[] = { + {"v4.0.30319","4.5", { {4,0,0,0}, {10,0,0,0}, {4,0,0,0}, {4,0,0,0}, {4,0,0,0} } }, + {"mobile", "2.1", { {2,0,5,0}, {10,0,0,0}, {2,0,5,0}, {2,0,5,0}, {4,0,0,0} } }, + {"moonlight", "2.1", { {2,0,5,0}, { 9,0,0,0}, {3,5,0,0}, {3,0,0,0}, NOT_AVAIL } }, +}; + +#undef NOT_AVAIL + + +/* The stable runtime version */ +#define DEFAULT_RUNTIME_VERSION "v4.0.30319" + +/* Callbacks installed by the JIT */ +static MonoCreateDomainFunc create_domain_hook; +static MonoFreeDomainFunc free_domain_hook; + +/* AOT cache configuration */ +static MonoAotCacheConfig aot_cache_config; + +static void +get_runtimes_from_exe (const char *exe_file, MonoImage **exe_image, const MonoRuntimeInfo** runtimes); + +static const MonoRuntimeInfo* +get_runtime_by_version (const char *version); + +#define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1)) +#define ALIGN_PTR_TO(ptr,align) (gpointer)((((gssize)(ptr)) + (align - 1)) & (~(align - 1))) + +static LockFreeMempool* +lock_free_mempool_new (void) +{ + return g_new0 (LockFreeMempool, 1); +} + +static void +lock_free_mempool_free (LockFreeMempool *mp) +{ + LockFreeMempoolChunk *chunk, *next; + + chunk = mp->chunks; + while (chunk) { + next = (LockFreeMempoolChunk *)chunk->prev; + mono_vfree (chunk, mono_pagesize (), MONO_MEM_ACCOUNT_DOMAIN); + chunk = next; + } + g_free (mp); +} + +/* + * This is async safe + */ +static LockFreeMempoolChunk* +lock_free_mempool_chunk_new (LockFreeMempool *mp, int len) +{ + LockFreeMempoolChunk *chunk, *prev; + int size; + + size = mono_pagesize (); + while (size - sizeof (LockFreeMempoolChunk) < len) + size += mono_pagesize (); + chunk = (LockFreeMempoolChunk *)mono_valloc (0, size, MONO_MMAP_READ|MONO_MMAP_WRITE, MONO_MEM_ACCOUNT_DOMAIN); + g_assert (chunk); + chunk->mem = (guint8 *)ALIGN_PTR_TO ((char*)chunk + sizeof (LockFreeMempoolChunk), 16); + chunk->size = ((char*)chunk + size) - (char*)chunk->mem; + chunk->pos = 0; + + /* Add to list of chunks lock-free */ + while (TRUE) { + prev = mp->chunks; + if (mono_atomic_cas_ptr ((volatile gpointer*)&mp->chunks, chunk, prev) == prev) + break; + } + chunk->prev = prev; + + return chunk; +} + +/* + * This is async safe + */ +static gpointer +lock_free_mempool_alloc0 (LockFreeMempool *mp, guint size) +{ + LockFreeMempoolChunk *chunk; + gpointer res; + int oldpos; + + // FIXME: Free the allocator + + size = ALIGN_TO (size, 8); + chunk = mp->current; + if (!chunk) { + chunk = lock_free_mempool_chunk_new (mp, size); + mono_memory_barrier (); + /* Publish */ + mp->current = chunk; + } + + /* The code below is lock-free, 'chunk' is shared state */ + oldpos = mono_atomic_fetch_add_i32 (&chunk->pos, size); + if (oldpos + size > chunk->size) { + chunk = lock_free_mempool_chunk_new (mp, size); + g_assert (chunk->pos + size <= chunk->size); + res = chunk->mem; + chunk->pos += size; + mono_memory_barrier (); + mp->current = chunk; + } else { + res = (char*)chunk->mem + oldpos; + } + + return res; +} + +void +mono_install_create_domain_hook (MonoCreateDomainFunc func) +{ + create_domain_hook = func; +} + +void +mono_install_free_domain_hook (MonoFreeDomainFunc func) +{ + free_domain_hook = func; +} + +/** + * mono_string_equal: + * \param s1 First string to compare + * \param s2 Second string to compare + * + * Compares two \c MonoString* instances ordinally for equality. + * + * \returns FALSE if the strings differ. + */ +gboolean +mono_string_equal (MonoString *s1, MonoString *s2) +{ + int l1 = mono_string_length (s1); + int l2 = mono_string_length (s2); + + if (s1 == s2) + return TRUE; + if (l1 != l2) + return FALSE; + + return memcmp (mono_string_chars (s1), mono_string_chars (s2), l1 * 2) == 0; +} + +/** + * mono_string_hash: + * \param s the string to hash + * + * Compute the hash for a \c MonoString* + * \returns the hash for the string. + */ +guint +mono_string_hash (MonoString *s) +{ + const guint16 *p = mono_string_chars (s); + int i, len = mono_string_length (s); + guint h = 0; + + for (i = 0; i < len; i++) { + h = (h << 5) - h + *p; + p++; + } + + return h; +} + +static gboolean +mono_ptrarray_equal (gpointer *s1, gpointer *s2) +{ + int len = GPOINTER_TO_INT (s1 [0]); + if (len != GPOINTER_TO_INT (s2 [0])) + return FALSE; + + return memcmp (s1 + 1, s2 + 1, len * sizeof(gpointer)) == 0; +} + +static guint +mono_ptrarray_hash (gpointer *s) +{ + int i; + int len = GPOINTER_TO_INT (s [0]); + guint hash = 0; + + for (i = 1; i < len; i++) + hash += GPOINTER_TO_UINT (s [i]); + + return hash; +} + +//g_malloc on sgen and mono_gc_alloc_fixed on boehm +static void* +gc_alloc_fixed_non_heap_list (size_t size) +{ + if (mono_gc_is_moving ()) + return g_malloc0 (size); + else + 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 ()) + g_free (ptr); + else + mono_gc_free_fixed (ptr); +} +/* + * Allocate an id for domain and set domain->domain_id. + * LOCKING: must be called while holding appdomains_mutex. + * We try to assign low numbers to the domain, so it can be used + * as an index in data tables to lookup domain-specific info + * with minimal memory overhead. We also try not to reuse the + * same id too quickly (to help debugging). + */ +static int +domain_id_alloc (MonoDomain *domain) +{ + int id = -1, i; + if (!appdomains_list) { + appdomain_list_size = 2; + appdomains_list = (MonoDomain **)gc_alloc_fixed_non_heap_list (appdomain_list_size * sizeof (void*)); + + } + for (i = appdomain_next; i < appdomain_list_size; ++i) { + if (!appdomains_list [i]) { + id = i; + break; + } + } + if (id == -1) { + for (i = 0; i < appdomain_next; ++i) { + if (!appdomains_list [i]) { + id = i; + break; + } + } + } + if (id == -1) { + MonoDomain **new_list; + int new_size = appdomain_list_size * 2; + if (new_size >= (1 << 16)) + g_assert_not_reached (); + id = appdomain_list_size; + new_list = (MonoDomain **)gc_alloc_fixed_non_heap_list (new_size * sizeof (void*)); + memcpy (new_list, appdomains_list, appdomain_list_size * sizeof (void*)); + gc_free_fixed_non_heap_list (appdomains_list); + appdomains_list = new_list; + appdomain_list_size = new_size; + } + domain->domain_id = id; + appdomains_list [id] = domain; + appdomain_next++; + if (appdomain_next > appdomain_list_size) + appdomain_next = 0; + return id; +} + +static gsize domain_gc_bitmap [sizeof(MonoDomain)/4/32 + 1]; +static MonoGCDescriptor domain_gc_desc = MONO_GC_DESCRIPTOR_NULL; +static guint32 domain_shadow_serial = 0L; + +/** + * mono_domain_create: + * + * Creates a new application domain, the unmanaged representation + * of the actual domain. + * + * Application domains provide an isolation facilty for assemblies. You + * can load assemblies and execute code in them that will not be visible + * to other application domains. This is a runtime-based virtualization + * technology. + * + * It is possible to unload domains, which unloads the assemblies and + * data that was allocated in that domain. + * + * When a domain is created a mempool is allocated for domain-specific + * structures, along a dedicated code manager to hold code that is + * associated with the domain. + * + * \returns New initialized \c MonoDomain, with no configuration or assemblies + * loaded into it. + */ +MonoDomain * +mono_domain_create (void) +{ + MonoDomain *domain; + guint32 shadow_serial; + + mono_appdomains_lock (); + shadow_serial = domain_shadow_serial++; + + if (!domain_gc_desc) { + unsigned int i, bit = 0; + for (i = G_STRUCT_OFFSET (MonoDomain, MONO_DOMAIN_FIRST_OBJECT); i < G_STRUCT_OFFSET (MonoDomain, MONO_DOMAIN_FIRST_GC_TRACKED); i += sizeof (gpointer)) { + bit = i / sizeof (gpointer); + domain_gc_bitmap [bit / 32] |= (gsize) 1 << (bit % 32); + } + domain_gc_desc = mono_gc_make_descr_from_bitmap ((gsize*)domain_gc_bitmap, bit + 1); + } + mono_appdomains_unlock (); + + if (!mono_gc_is_moving ()) + domain = (MonoDomain *)mono_gc_alloc_fixed (sizeof (MonoDomain), MONO_GC_DESCRIPTOR_NULL, MONO_ROOT_SOURCE_DOMAIN, NULL, "Domain Structure"); + else + domain = (MonoDomain *)mono_gc_alloc_fixed (sizeof (MonoDomain), domain_gc_desc, MONO_ROOT_SOURCE_DOMAIN, NULL, "Domain Structure"); + + domain->shadow_serial = shadow_serial; + domain->domain = NULL; + domain->setup = NULL; + domain->friendly_name = NULL; + domain->search_path = NULL; + + MONO_PROFILER_RAISE (domain_loading, (domain)); + + domain->mp = mono_mempool_new (); + domain->code_mp = mono_code_manager_new (); + domain->lock_free_mp = lock_free_mempool_new (); + domain->env = mono_g_hash_table_new_type ((GHashFunc)mono_string_hash, (GCompareFunc)mono_string_equal, MONO_HASH_KEY_VALUE_GC, MONO_ROOT_SOURCE_DOMAIN, domain, "Domain Environment Variable Table"); + domain->domain_assemblies = NULL; + domain->assembly_bindings = NULL; + domain->assembly_bindings_parsed = FALSE; + domain->class_vtable_array = g_ptr_array_new (); + domain->proxy_vtable_hash = g_hash_table_new ((GHashFunc)mono_ptrarray_hash, (GCompareFunc)mono_ptrarray_equal); + mono_jit_code_hash_init (&domain->jit_code_hash); + domain->ldstr_table = mono_g_hash_table_new_type ((GHashFunc)mono_string_hash, (GCompareFunc)mono_string_equal, MONO_HASH_KEY_VALUE_GC, MONO_ROOT_SOURCE_DOMAIN, domain, "Domain String Pool Table"); + domain->num_jit_info_tables = 1; + domain->jit_info_table = mono_jit_info_table_new (domain); + domain->jit_info_free_queue = NULL; + domain->finalizable_objects_hash = g_hash_table_new (mono_aligned_addr_hash, NULL); + domain->ftnptrs_hash = g_hash_table_new (mono_aligned_addr_hash, NULL); + + mono_coop_mutex_init_recursive (&domain->lock); + + mono_os_mutex_init_recursive (&domain->assemblies_lock); + mono_os_mutex_init_recursive (&domain->jit_code_hash_lock); + mono_os_mutex_init_recursive (&domain->finalizable_objects_hash_lock); + + domain->method_rgctx_hash = NULL; + + mono_appdomains_lock (); + domain_id_alloc (domain); + mono_appdomains_unlock (); + +#ifndef DISABLE_PERFCOUNTERS + mono_atomic_inc_i32 (&mono_perfcounters->loader_appdomains); + mono_atomic_inc_i32 (&mono_perfcounters->loader_total_appdomains); +#endif + + mono_debug_domain_create (domain); + mono_profiler_coverage_domain_init (domain); + + if (create_domain_hook) + create_domain_hook (domain); + + MONO_PROFILER_RAISE (domain_loaded, (domain)); + + return domain; +} + +/** + * mono_init_internal: + * + * Creates the initial application domain and initializes the mono_defaults + * structure. + * This function is guaranteed to not run any IL code. + * If exe_filename is not NULL, the method will determine the required runtime + * from the exe configuration file or the version PE field. + * If runtime_version is not NULL, that runtime version will be used. + * Either exe_filename or runtime_version must be provided. + * + * Returns: the initial domain. + */ +static MonoDomain * +mono_init_internal (const char *filename, const char *exe_filename, const char *runtime_version) +{ + static MonoDomain *domain = NULL; + MonoAssembly *ass = NULL; + MonoImageOpenStatus status = MONO_IMAGE_OK; + const MonoRuntimeInfo* runtimes [G_N_ELEMENTS (supported_runtimes) + 1] = { NULL }; + int n; + +#ifdef DEBUG_DOMAIN_UNLOAD + debug_domain_unload = TRUE; +#endif + + if (domain) + g_assert_not_reached (); + +#if defined(HOST_WIN32) && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) + /* Avoid system error message boxes. */ + SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); +#endif + +#ifndef HOST_WIN32 + mono_w32handle_init (); + mono_w32handle_namespace_init (); +#endif + + mono_w32mutex_init (); + mono_w32semaphore_init (); + mono_w32event_init (); + mono_w32process_init (); + mono_w32file_init (); + +#ifndef DISABLE_PERFCOUNTERS + mono_perfcounters_init (); +#endif + mono_counters_init (); + + mono_counters_register ("Max native code in a domain", MONO_COUNTER_INT|MONO_COUNTER_JIT, &max_domain_code_size); + mono_counters_register ("Max code space allocated in a domain", MONO_COUNTER_INT|MONO_COUNTER_JIT, &max_domain_code_alloc); + mono_counters_register ("Total code space allocated", MONO_COUNTER_INT|MONO_COUNTER_JIT, &total_domain_code_alloc); + + mono_counters_register ("Max HashTable Chain Length", MONO_COUNTER_INT|MONO_COUNTER_METADATA, &mono_g_hash_table_max_chain_length); + + mono_gc_base_init (); + mono_thread_info_attach (); + + mono_coop_mutex_init_recursive (&appdomains_mutex); + + mono_metadata_init (); + mono_images_init (); + mono_assemblies_init (); + mono_classes_init (); + mono_loader_init (); + mono_reflection_init (); + mono_runtime_init_tls (); + + domain = mono_domain_create (); + mono_root_domain = domain; + + SET_APPDOMAIN (domain); + + /* Get a list of runtimes supported by the exe */ + if (exe_filename != NULL) { + /* + * This function will load the exe file as a MonoImage. We need to close it, but + * that would mean it would be reloaded later. So instead, we save it to + * exe_image, and close it during shutdown. + */ + get_runtimes_from_exe (exe_filename, &exe_image, runtimes); +#ifdef HOST_WIN32 + if (!exe_image) { + exe_image = mono_assembly_open_from_bundle (exe_filename, NULL, FALSE); + if (!exe_image) + exe_image = mono_image_open (exe_filename, NULL); + } + mono_fixup_exe_image (exe_image); +#endif + } else if (runtime_version != NULL) { + runtimes [0] = get_runtime_by_version (runtime_version); + runtimes [1] = NULL; + } + + if (runtimes [0] == NULL) { + const MonoRuntimeInfo *default_runtime = get_runtime_by_version (DEFAULT_RUNTIME_VERSION); + runtimes [0] = default_runtime; + runtimes [1] = NULL; + g_print ("WARNING: The runtime version supported by this application is unavailable.\n"); + g_print ("Using default runtime: %s\n", default_runtime->runtime_version); + } + + /* The selected runtime will be the first one for which there is a mscrolib.dll */ + for (n = 0; runtimes [n] != NULL && ass == NULL; n++) { + current_runtime = runtimes [n]; + ass = mono_assembly_load_corlib (current_runtime, &status); + if (status != MONO_IMAGE_OK && status != MONO_IMAGE_ERROR_ERRNO) + break; + + } + + if ((status != MONO_IMAGE_OK) || (ass == NULL)) { + switch (status){ + case MONO_IMAGE_ERROR_ERRNO: { + char *corlib_file = g_build_filename (mono_assembly_getrootdir (), "mono", current_runtime->framework_version, "mscorlib.dll", NULL); + g_print ("The assembly mscorlib.dll was not found or could not be loaded.\n"); + g_print ("It should have been installed in the `%s' directory.\n", corlib_file); + g_free (corlib_file); + break; + } + case MONO_IMAGE_IMAGE_INVALID: + g_print ("The file %s/mscorlib.dll is an invalid CIL image\n", + mono_assembly_getrootdir ()); + break; + case MONO_IMAGE_MISSING_ASSEMBLYREF: + g_print ("Missing assembly reference in %s/mscorlib.dll\n", + mono_assembly_getrootdir ()); + break; + case MONO_IMAGE_OK: + /* to suppress compiler warning */ + break; + } + + exit (1); + } + mono_defaults.corlib = mono_assembly_get_image (ass); + + mono_defaults.object_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "Object"); + + mono_defaults.void_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "Void"); + + mono_defaults.boolean_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "Boolean"); + + mono_defaults.byte_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "Byte"); + + mono_defaults.sbyte_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "SByte"); + + mono_defaults.int16_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "Int16"); + + mono_defaults.uint16_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "UInt16"); + + mono_defaults.int32_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "Int32"); + + mono_defaults.uint32_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "UInt32"); + + mono_defaults.uint_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "UIntPtr"); + + mono_defaults.int_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "IntPtr"); + + mono_defaults.int64_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "Int64"); + + mono_defaults.uint64_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "UInt64"); + + mono_defaults.single_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "Single"); + + mono_defaults.double_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "Double"); + + mono_defaults.char_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "Char"); + + mono_defaults.string_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "String"); + + mono_defaults.enum_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "Enum"); + + mono_defaults.array_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "Array"); + + mono_defaults.delegate_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "Delegate"); + + mono_defaults.multicastdelegate_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "MulticastDelegate"); + + mono_defaults.asyncresult_class = mono_class_load_from_name ( + mono_defaults.corlib, "System.Runtime.Remoting.Messaging", + "AsyncResult"); + + mono_defaults.manualresetevent_class = mono_class_load_from_name ( + mono_defaults.corlib, "System.Threading", "ManualResetEvent"); + + mono_defaults.typehandle_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "RuntimeTypeHandle"); + + mono_defaults.methodhandle_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "RuntimeMethodHandle"); + + mono_defaults.fieldhandle_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "RuntimeFieldHandle"); + + mono_defaults.systemtype_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "Type"); + + mono_defaults.runtimetype_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "RuntimeType"); + + mono_defaults.exception_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "Exception"); + + mono_defaults.threadabortexception_class = mono_class_load_from_name ( + mono_defaults.corlib, "System.Threading", "ThreadAbortException"); + + mono_defaults.thread_class = mono_class_load_from_name ( + mono_defaults.corlib, "System.Threading", "Thread"); + + mono_defaults.internal_thread_class = mono_class_load_from_name ( + mono_defaults.corlib, "System.Threading", "InternalThread"); + + mono_defaults.appdomain_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "AppDomain"); + +#ifndef DISABLE_REMOTING + mono_defaults.transparent_proxy_class = mono_class_load_from_name ( + mono_defaults.corlib, "System.Runtime.Remoting.Proxies", "TransparentProxy"); + + mono_defaults.real_proxy_class = mono_class_load_from_name ( + mono_defaults.corlib, "System.Runtime.Remoting.Proxies", "RealProxy"); + + mono_defaults.marshalbyrefobject_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "MarshalByRefObject"); + + mono_defaults.iremotingtypeinfo_class = mono_class_load_from_name ( + mono_defaults.corlib, "System.Runtime.Remoting", "IRemotingTypeInfo"); + +#endif + + mono_defaults.mono_method_message_class = mono_class_load_from_name ( + mono_defaults.corlib, "System.Runtime.Remoting.Messaging", "MonoMethodMessage"); + + mono_defaults.field_info_class = mono_class_load_from_name ( + mono_defaults.corlib, "System.Reflection", "FieldInfo"); + + mono_defaults.method_info_class = mono_class_load_from_name ( + mono_defaults.corlib, "System.Reflection", "MethodInfo"); + + mono_defaults.stringbuilder_class = mono_class_load_from_name ( + mono_defaults.corlib, "System.Text", "StringBuilder"); + + mono_defaults.math_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "Math"); + + mono_defaults.stack_frame_class = mono_class_load_from_name ( + mono_defaults.corlib, "System.Diagnostics", "StackFrame"); + + mono_defaults.stack_trace_class = mono_class_load_from_name ( + mono_defaults.corlib, "System.Diagnostics", "StackTrace"); + + mono_defaults.marshal_class = mono_class_load_from_name ( + mono_defaults.corlib, "System.Runtime.InteropServices", "Marshal"); + + mono_defaults.typed_reference_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "TypedReference"); + + mono_defaults.argumenthandle_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "RuntimeArgumentHandle"); + + mono_defaults.monitor_class = mono_class_load_from_name ( + mono_defaults.corlib, "System.Threading", "Monitor"); + /* + Not using GENERATE_TRY_GET_CLASS_WITH_CACHE_DECL as this type is heavily checked by sgen when computing finalization. + */ + mono_defaults.critical_finalizer_object = mono_class_try_load_from_name (mono_defaults.corlib, + "System.Runtime.ConstrainedExecution", "CriticalFinalizerObject"); + + mono_assembly_load_friends (ass); + + mono_defaults.handleref_class = mono_class_try_load_from_name ( + mono_defaults.corlib, "System.Runtime.InteropServices", "HandleRef"); + + mono_defaults.attribute_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "Attribute"); + + mono_defaults.customattribute_data_class = mono_class_load_from_name ( + mono_defaults.corlib, "System.Reflection", "CustomAttributeData"); + + mono_class_init (mono_defaults.array_class); + mono_defaults.generic_nullable_class = mono_class_load_from_name ( + mono_defaults.corlib, "System", "Nullable`1"); + mono_defaults.generic_ilist_class = mono_class_load_from_name ( + mono_defaults.corlib, "System.Collections.Generic", "IList`1"); + mono_defaults.generic_ireadonlylist_class = mono_class_load_from_name ( + mono_defaults.corlib, "System.Collections.Generic", "IReadOnlyList`1"); + + mono_defaults.threadpool_wait_callback_class = mono_class_load_from_name ( + mono_defaults.corlib, "System.Threading", "_ThreadPoolWaitCallback"); + + mono_defaults.threadpool_perform_wait_callback_method = mono_class_get_method_from_name ( + mono_defaults.threadpool_wait_callback_class, "PerformWaitCallback", 0); + + domain->friendly_name = g_path_get_basename (filename); + + MONO_PROFILER_RAISE (domain_name, (domain, domain->friendly_name)); + + return domain; +} + +/** + * mono_init: + * + * Creates the initial application domain and initializes the mono_defaults + * structure. + * + * This function is guaranteed to not run any IL code. + * The runtime is initialized using the default runtime version. + * + * Returns: the initial domain. + */ +MonoDomain * +mono_init (const char *domain_name) +{ + return mono_init_internal (domain_name, NULL, DEFAULT_RUNTIME_VERSION); +} + +/** + * mono_init_from_assembly: + * \param domain_name name to give to the initial domain + * \param filename filename to load on startup + * + * Used by the runtime, users should use mono_jit_init instead. + * + * Creates the initial application domain and initializes the mono_defaults + * structure. + * This function is guaranteed to not run any IL code. + * The runtime is initialized using the runtime version required by the + * provided executable. The version is determined by looking at the exe + * configuration file and the version PE field) + * + * \returns the initial domain. + */ +MonoDomain * +mono_init_from_assembly (const char *domain_name, const char *filename) +{ + return mono_init_internal (domain_name, filename, NULL); +} + +/** + * mono_init_version: + * + * Used by the runtime, users should use \c mono_jit_init instead. + * + * Creates the initial application domain and initializes the \c mono_defaults + * structure. + * + * This function is guaranteed to not run any IL code. + * The runtime is initialized using the provided rutime version. + * + * \returns the initial domain. + */ +MonoDomain * +mono_init_version (const char *domain_name, const char *version) +{ + return mono_init_internal (domain_name, NULL, version); +} + +/** + * mono_cleanup: + * + * Cleans up all metadata modules. + */ +void +mono_cleanup (void) +{ + mono_close_exe_image (); + + mono_thread_info_cleanup (); + + mono_defaults.corlib = NULL; + + mono_config_cleanup (); + mono_loader_cleanup (); + mono_classes_cleanup (); + mono_assemblies_cleanup (); + mono_debug_cleanup (); + mono_images_cleanup (); + mono_metadata_cleanup (); + + mono_coop_mutex_destroy (&appdomains_mutex); + + mono_w32process_cleanup (); + mono_w32file_cleanup (); +} + +void +mono_close_exe_image (void) +{ + if (exe_image) + mono_image_close (exe_image); +} + +/** + * mono_get_root_domain: + * + * The root AppDomain is the initial domain created by the runtime when it is + * initialized. Programs execute on this AppDomain, but can create new ones + * later. Currently there is no unmanaged API to create new AppDomains, this + * must be done from managed code. + * + * Returns: the root appdomain, to obtain the current domain, use mono_domain_get () + */ +MonoDomain* +mono_get_root_domain (void) +{ + return mono_root_domain; +} + +/** + * mono_domain_get: + * + * This method returns the value of the current \c MonoDomain that this thread + * and code are running under. To obtain the root domain use + * \c mono_get_root_domain API. + * + * \returns the current domain + */ +MonoDomain * +mono_domain_get () +{ + return GET_APPDOMAIN (); +} + +void +mono_domain_unset (void) +{ + SET_APPDOMAIN (NULL); +} + +void +mono_domain_set_internal_with_options (MonoDomain *domain, gboolean migrate_exception) +{ + MonoInternalThread *thread; + + if (mono_domain_get () == domain) + return; + + SET_APPDOMAIN (domain); + SET_APPCONTEXT (domain->default_context); + mono_gc_wbarrier_generic_nostore (&domain->default_context); + + if (migrate_exception) { + thread = mono_thread_internal_current (); + if (!thread->abort_exc) + return; + + g_assert (thread->abort_exc->object.vtable->domain != domain); + MONO_OBJECT_SETREF (thread, abort_exc, mono_get_exception_thread_abort ()); + g_assert (thread->abort_exc->object.vtable->domain == domain); + } +} + +/** + * mono_domain_set_internal: + * \param domain the new domain + * + * Sets the current domain to \p domain. + */ +void +mono_domain_set_internal (MonoDomain *domain) +{ + mono_domain_set_internal_with_options (domain, TRUE); +} + +/** + * mono_domain_foreach: + * \param func function to invoke with the domain data + * \param user_data user-defined pointer that is passed to the supplied \p func fo reach domain + * + * Use this method to safely iterate over all the loaded application + * domains in the current runtime. The provided \p func is invoked with a + * pointer to the \c MonoDomain and is given the value of the \p user_data + * parameter which can be used to pass state to your called routine. + */ +void +mono_domain_foreach (MonoDomainFunc func, gpointer user_data) +{ + int i, size; + MonoDomain **copy; + + /* + * Create a copy of the data to avoid calling the user callback + * inside the lock because that could lead to deadlocks. + * We can do this because this function is not perf. critical. + */ + mono_appdomains_lock (); + size = appdomain_list_size; + copy = (MonoDomain **)gc_alloc_fixed_non_heap_list (appdomain_list_size * sizeof (void*)); + memcpy (copy, appdomains_list, appdomain_list_size * sizeof (void*)); + mono_appdomains_unlock (); + + for (i = 0; i < size; ++i) { + if (copy [i]) + func (copy [i], user_data); + } + + gc_free_fixed_non_heap_list (copy); +} + +MONO_API void +mono_domain_jit_foreach (MonoDomain *domain, MonoJitInfoFunc func, void *user_data) +{ + mono_jit_info_table_foreach (domain, func, user_data); +} + +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: + * \param domain the application domain + * \param name file name of the assembly + */ +MonoAssembly * +mono_domain_assembly_open (MonoDomain *domain, const char *name) +{ + MonoDomain *current; + MonoAssembly *ass; + GSList *tmp; + + mono_domain_assemblies_lock (domain); + for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) { + ass = (MonoAssembly *)tmp->data; + if (strcmp (name, ass->aname.name) == 0) { + mono_domain_assemblies_unlock (domain); + return ass; + } + } + mono_domain_assemblies_unlock (domain); + + if (domain != mono_domain_get ()) { + current = mono_domain_get (); + + mono_domain_set (domain, FALSE); + ass = mono_assembly_open_predicate (name, FALSE, FALSE, NULL, NULL, NULL); + mono_domain_set (current, FALSE); + } else { + ass = mono_assembly_open_predicate (name, FALSE, FALSE, NULL, NULL, NULL); + } + + return ass; +} + +static void +unregister_vtable_reflection_type (MonoVTable *vtable) +{ + MonoObject *type = (MonoObject *)vtable->type; + + if (type->vtable->klass != mono_defaults.runtimetype_class) + MONO_GC_UNREGISTER_ROOT_IF_MOVING (vtable->type); +} + +/** + * mono_domain_free: + * \param domain the domain to release + * \param force if TRUE, it allows the root domain to be released (used at shutdown only). + * + * This releases the resources associated with the specific domain. + * This is a low-level function that is invoked by the AppDomain infrastructure + * when necessary. + */ +void +mono_domain_free (MonoDomain *domain, gboolean force) +{ + int code_size, code_alloc; + GSList *tmp; + gpointer *p; + + if ((domain == mono_root_domain) && !force) { + g_warning ("cant unload root domain"); + return; + } + + if (mono_dont_free_domains) + return; + + MONO_PROFILER_RAISE (domain_unloading, (domain)); + + mono_debug_domain_unload (domain); + + /* must do this early as it accesses fields and types */ + if (domain->special_static_fields) { + mono_alloc_special_static_data_free (domain->special_static_fields); + g_hash_table_destroy (domain->special_static_fields); + domain->special_static_fields = NULL; + } + + /* + * We must destroy all these hash tables here because they + * contain references to managed objects belonging to the + * domain. Once we let the GC clear the domain there must be + * no more such references, or we'll crash if a collection + * occurs. + */ + mono_g_hash_table_destroy (domain->ldstr_table); + domain->ldstr_table = NULL; + + mono_g_hash_table_destroy (domain->env); + domain->env = NULL; + + mono_reflection_cleanup_domain (domain); + + /* This must be done before type_hash is freed */ + if (domain->class_vtable_array) { + int i; + for (i = 0; i < domain->class_vtable_array->len; ++i) + unregister_vtable_reflection_type ((MonoVTable *)g_ptr_array_index (domain->class_vtable_array, i)); + } + + if (domain->type_hash) { + mono_g_hash_table_destroy (domain->type_hash); + domain->type_hash = NULL; + } + if (domain->type_init_exception_hash) { + mono_g_hash_table_destroy (domain->type_init_exception_hash); + domain->type_init_exception_hash = NULL; + } + + for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) { + MonoAssembly *ass = (MonoAssembly *)tmp->data; + mono_assembly_release_gc_roots (ass); + } + + /* Have to zero out reference fields since they will be invalidated by the clear_domain () call below */ + for (p = (gpointer*)&domain->MONO_DOMAIN_FIRST_OBJECT; p < (gpointer*)&domain->MONO_DOMAIN_FIRST_GC_TRACKED; ++p) + *p = NULL; + + /* This needs to be done before closing assemblies */ + mono_gc_clear_domain (domain); + + /* Close dynamic assemblies first, since they have no ref count */ + for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) { + MonoAssembly *ass = (MonoAssembly *)tmp->data; + if (!ass->image || !image_is_dynamic (ass->image)) + continue; + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Unloading domain %s[%p], assembly %s[%p], ref_count=%d", domain->friendly_name, domain, ass->aname.name, ass, ass->ref_count); + if (!mono_assembly_close_except_image_pools (ass)) + tmp->data = NULL; + } + + for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) { + MonoAssembly *ass = (MonoAssembly *)tmp->data; + if (!ass) + continue; + if (!ass->image || image_is_dynamic (ass->image)) + continue; + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Unloading domain %s[%p], assembly %s[%p], ref_count=%d", domain->friendly_name, domain, ass->aname.name, ass, ass->ref_count); + if (!mono_assembly_close_except_image_pools (ass)) + tmp->data = NULL; + } + + for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) { + MonoAssembly *ass = (MonoAssembly *)tmp->data; + if (ass) + mono_assembly_close_finish (ass); + } + g_slist_free (domain->domain_assemblies); + domain->domain_assemblies = NULL; + + /* + * Send this after the assemblies have been unloaded and the domain is still in a + * usable state. + */ + MONO_PROFILER_RAISE (domain_unloaded, (domain)); + + if (free_domain_hook) + free_domain_hook (domain); + + /* FIXME: free delegate_hash_table when it's used */ + if (domain->search_path) { + g_strfreev (domain->search_path); + domain->search_path = NULL; + } + domain->create_proxy_for_type_method = NULL; + domain->private_invoke_method = NULL; + domain->default_context = NULL; + domain->out_of_memory_ex = NULL; + domain->null_reference_ex = NULL; + domain->stack_overflow_ex = NULL; + domain->ephemeron_tombstone = NULL; + domain->entry_assembly = NULL; + + g_free (domain->friendly_name); + domain->friendly_name = NULL; + g_ptr_array_free (domain->class_vtable_array, TRUE); + domain->class_vtable_array = NULL; + g_hash_table_destroy (domain->proxy_vtable_hash); + domain->proxy_vtable_hash = NULL; + mono_internal_hash_table_destroy (&domain->jit_code_hash); + + /* + * There might still be jit info tables of this domain which + * are not freed. Since the domain cannot be in use anymore, + * this will free them. + */ + mono_thread_hazardous_try_free_all (); + if (domain->aot_modules) + mono_jit_info_table_free (domain->aot_modules); + g_assert (domain->num_jit_info_tables == 1); + mono_jit_info_table_free (domain->jit_info_table); + domain->jit_info_table = NULL; + g_assert (!domain->jit_info_free_queue); + + /* collect statistics */ + code_alloc = mono_code_manager_size (domain->code_mp, &code_size); + total_domain_code_alloc += code_alloc; + max_domain_code_alloc = MAX (max_domain_code_alloc, code_alloc); + max_domain_code_size = MAX (max_domain_code_size, code_size); + + if (debug_domain_unload) { + mono_mempool_invalidate (domain->mp); + mono_code_manager_invalidate (domain->code_mp); + } else { +#ifndef DISABLE_PERFCOUNTERS + /* FIXME: use an explicit subtraction method as soon as it's available */ + mono_atomic_fetch_add_i32 (&mono_perfcounters->loader_bytes, -1 * mono_mempool_get_allocated (domain->mp)); +#endif + mono_mempool_destroy (domain->mp); + domain->mp = NULL; + mono_code_manager_destroy (domain->code_mp); + domain->code_mp = NULL; + } + lock_free_mempool_free (domain->lock_free_mp); + domain->lock_free_mp = NULL; + + g_hash_table_destroy (domain->finalizable_objects_hash); + domain->finalizable_objects_hash = NULL; + if (domain->method_rgctx_hash) { + g_hash_table_destroy (domain->method_rgctx_hash); + domain->method_rgctx_hash = NULL; + } + if (domain->generic_virtual_cases) { + g_hash_table_destroy (domain->generic_virtual_cases); + domain->generic_virtual_cases = NULL; + } + if (domain->ftnptrs_hash) { + g_hash_table_destroy (domain->ftnptrs_hash); + domain->ftnptrs_hash = NULL; + } + if (domain->method_to_dyn_method) { + g_hash_table_destroy (domain->method_to_dyn_method); + domain->method_to_dyn_method = NULL; + } + + mono_os_mutex_destroy (&domain->finalizable_objects_hash_lock); + mono_os_mutex_destroy (&domain->assemblies_lock); + mono_os_mutex_destroy (&domain->jit_code_hash_lock); + + mono_coop_mutex_destroy (&domain->lock); + + 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)); + + mono_appdomains_lock (); + appdomains_list [domain->domain_id] = NULL; + mono_appdomains_unlock (); + + mono_gc_free_fixed (domain); + +#ifndef DISABLE_PERFCOUNTERS + mono_atomic_dec_i32 (&mono_perfcounters->loader_appdomains); +#endif + + if (domain == mono_root_domain) + mono_root_domain = NULL; + + mono_profiler_coverage_domain_free(domain); +} + +/** + * mono_domain_get_by_id: + * \param domainid the ID + * \returns the domain for a specific domain id. + */ +MonoDomain * +mono_domain_get_by_id (gint32 domainid) +{ + MonoDomain * domain; + + mono_appdomains_lock (); + if (domainid < appdomain_list_size) + domain = appdomains_list [domainid]; + else + domain = NULL; + mono_appdomains_unlock (); + + return domain; +} + +/** + * mono_domain_get_id: + * + * A domain ID is guaranteed to be unique for as long as the domain + * using it is alive. It may be reused later once the domain has been + * unloaded. + * + * \returns The unique ID for \p domain. + */ +gint32 +mono_domain_get_id (MonoDomain *domain) +{ + return domain->domain_id; +} + +/** + * mono_domain_get_friendly_name: + * + * The returned string's lifetime is the same as \p domain's. Consider + * copying it if you need to store it somewhere. + * + * \returns The friendly name of \p domain. Can be NULL if not yet set. + */ +const char * +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: + * + * LOCKING: Acquires the domain lock. + */ +gpointer +mono_domain_alloc (MonoDomain *domain, guint size) +{ + gpointer res; + + mono_domain_lock (domain); +#ifndef DISABLE_PERFCOUNTERS + mono_atomic_fetch_add_i32 (&mono_perfcounters->loader_bytes, size); +#endif + res = mono_mempool_alloc (domain->mp, size); + mono_domain_unlock (domain); + + return res; +} + +/* + * mono_domain_alloc0: + * + * LOCKING: Acquires the domain lock. + */ +gpointer +mono_domain_alloc0 (MonoDomain *domain, guint size) +{ + gpointer res; + + mono_domain_lock (domain); +#ifndef DISABLE_PERFCOUNTERS + mono_atomic_fetch_add_i32 (&mono_perfcounters->loader_bytes, size); +#endif + res = mono_mempool_alloc0 (domain->mp, size); + mono_domain_unlock (domain); + + return res; +} + +gpointer +mono_domain_alloc0_lock_free (MonoDomain *domain, guint size) +{ + return lock_free_mempool_alloc0 (domain->lock_free_mp, size); +} + +/* + * mono_domain_code_reserve: + * + * LOCKING: Acquires the domain lock. + */ +void* +mono_domain_code_reserve (MonoDomain *domain, int size) +{ + gpointer res; + + mono_domain_lock (domain); + res = mono_code_manager_reserve (domain->code_mp, size); + mono_domain_unlock (domain); + + return res; +} + +/* + * mono_domain_code_reserve_align: + * + * LOCKING: Acquires the domain lock. + */ +void* +mono_domain_code_reserve_align (MonoDomain *domain, int size, int alignment) +{ + gpointer res; + + mono_domain_lock (domain); + res = mono_code_manager_reserve_align (domain->code_mp, size, alignment); + mono_domain_unlock (domain); + + return res; +} + +/* + * mono_domain_code_commit: + * + * LOCKING: Acquires the domain lock. + */ +void +mono_domain_code_commit (MonoDomain *domain, void *data, int size, int newsize) +{ + mono_domain_lock (domain); + mono_code_manager_commit (domain->code_mp, data, size, newsize); + mono_domain_unlock (domain); +} + +/* + * mono_domain_code_foreach: + * Iterate over the code thunks of the code manager of @domain. + * + * The @func callback MUST not take any locks. If it really needs to, it must respect + * the locking rules of the runtime: http://www.mono-project.com/Mono:Runtime:Documentation:ThreadSafety + * LOCKING: Acquires the domain lock. + */ + +void +mono_domain_code_foreach (MonoDomain *domain, MonoCodeManagerFunc func, void *user_data) +{ + mono_domain_lock (domain); + mono_code_manager_foreach (domain->code_mp, func, user_data); + mono_domain_unlock (domain); +} + +/** + * mono_context_set: + */ +void +mono_context_set (MonoAppContext * new_context) +{ + SET_APPCONTEXT (new_context); +} + +void +mono_context_set_handle (MonoAppContextHandle new_context) +{ + SET_APPCONTEXT (MONO_HANDLE_RAW (new_context)); +} + +/** + * mono_context_get: + * + * Returns: the current Mono Application Context. + */ +MonoAppContext * +mono_context_get (void) +{ + return GET_APPCONTEXT (); +} + +/** + * mono_context_get_handle: + * + * Returns: the current Mono Application Context. + */ +MonoAppContextHandle +mono_context_get_handle (void) +{ + return MONO_HANDLE_NEW (MonoAppContext, GET_APPCONTEXT ()); +} + +/** + * mono_context_get_id: + * \param context the context to operate on. + * + * Context IDs are guaranteed to be unique for the duration of a Mono + * process; they are never reused. + * + * \returns The unique ID for \p context. + */ +gint32 +mono_context_get_id (MonoAppContext *context) +{ + return context->context_id; +} + +/** + * mono_context_get_domain_id: + * \param context the context to operate on. + * \returns The ID of the domain that \p context was created in. + */ +gint32 +mono_context_get_domain_id (MonoAppContext *context) +{ + return context->domain_id; +} + +/** + * mono_get_corlib: + * Use this function to get the \c MonoImage* for the \c mscorlib.dll assembly + * \returns The \c MonoImage for mscorlib.dll + */ +MonoImage* +mono_get_corlib (void) +{ + return mono_defaults.corlib; +} + +/** + * mono_get_object_class: + * Use this function to get the \c MonoClass* that the runtime is using for \c System.Object . + * \returns The \c MonoClass* for the \c System.Object type. + */ +MonoClass* +mono_get_object_class (void) +{ + return mono_defaults.object_class; +} + +/** + * mono_get_byte_class: + * Use this function to get the \c MonoClass* that the runtime is using for \c System.Byte . + * \returns The \c MonoClass* for the \c System.Byte type. + */ +MonoClass* +mono_get_byte_class (void) +{ + return mono_defaults.byte_class; +} + +/** + * mono_get_void_class: + * Use this function to get the \c MonoClass* that the runtime is using for \c System.Void . + * \returns The \c MonoClass* for the \c System.Void type. + */ +MonoClass* +mono_get_void_class (void) +{ + return mono_defaults.void_class; +} + +/** + * mono_get_boolean_class: + * Use this function to get the \c MonoClass* that the runtime is using for \c System.Boolean . + * \returns The \c MonoClass* for the \c System.Boolean type. + */ +MonoClass* +mono_get_boolean_class (void) +{ + return mono_defaults.boolean_class; +} + +/** + * mono_get_sbyte_class: + * Use this function to get the \c MonoClass* that the runtime is using for \c System.SByte. + * \returns The \c MonoClass* for the \c System.SByte type. + */ +MonoClass* +mono_get_sbyte_class (void) +{ + return mono_defaults.sbyte_class; +} + +/** + * mono_get_int16_class: + * Use this function to get the \c MonoClass* that the runtime is using for \c System.Int16 . + * \returns The \c MonoClass* for the \c System.Int16 type. + */ +MonoClass* +mono_get_int16_class (void) +{ + return mono_defaults.int16_class; +} + +/** + * mono_get_uint16_class: + * Use this function to get the \c MonoClass* that the runtime is using for \c System.UInt16 . + * \returns The \c MonoClass* for the \c System.UInt16 type. + */ +MonoClass* +mono_get_uint16_class (void) +{ + return mono_defaults.uint16_class; +} + +/** + * mono_get_int32_class: + * Use this function to get the \c MonoClass* that the runtime is using for \c System.Int32 . + * \returns The \c MonoClass* for the \c System.Int32 type. + */ +MonoClass* +mono_get_int32_class (void) +{ + return mono_defaults.int32_class; +} + +/** + * mono_get_uint32_class: + * Use this function to get the \c MonoClass* that the runtime is using for \c System.UInt32 . + * \returns The \c MonoClass* for the \c System.UInt32 type. + */ +MonoClass* +mono_get_uint32_class (void) +{ + return mono_defaults.uint32_class; +} + +/** + * mono_get_intptr_class: + * Use this function to get the \c MonoClass* that the runtime is using for \c System.IntPtr . + * \returns The \c MonoClass* for the \c System.IntPtr type. + */ +MonoClass* +mono_get_intptr_class (void) +{ + return mono_defaults.int_class; +} + +/** + * mono_get_uintptr_class: + * Use this function to get the \c MonoClass* that the runtime is using for \c System.UIntPtr . + * \returns The \c MonoClass* for the \c System.UIntPtr type. + */ +MonoClass* +mono_get_uintptr_class (void) +{ + return mono_defaults.uint_class; +} + +/** + * mono_get_int64_class: + * Use this function to get the \c MonoClass* that the runtime is using for \c System.Int64 . + * \returns The \c MonoClass* for the \c System.Int64 type. + */ +MonoClass* +mono_get_int64_class (void) +{ + return mono_defaults.int64_class; +} + +/** + * mono_get_uint64_class: + * Use this function to get the \c MonoClass* that the runtime is using for \c System.UInt64 . + * \returns The \c MonoClass* for the \c System.UInt64 type. + */ +MonoClass* +mono_get_uint64_class (void) +{ + return mono_defaults.uint64_class; +} + +/** + * mono_get_single_class: + * Use this function to get the \c MonoClass* that the runtime is using for \c System.Single (32-bit floating points). + * \returns The \c MonoClass* for the \c System.Single type. + */ +MonoClass* +mono_get_single_class (void) +{ + return mono_defaults.single_class; +} + +/** + * mono_get_double_class: + * Use this function to get the \c MonoClass* that the runtime is using for \c System.Double (64-bit floating points). + * \returns The \c MonoClass* for the \c System.Double type. + */ +MonoClass* +mono_get_double_class (void) +{ + return mono_defaults.double_class; +} + +/** + * mono_get_char_class: + * Use this function to get the \c MonoClass* that the runtime is using for \c System.Char . + * \returns The \c MonoClass* for the \c System.Char type. + */ +MonoClass* +mono_get_char_class (void) +{ + return mono_defaults.char_class; +} + +/** + * mono_get_string_class: + * Use this function to get the \c MonoClass* that the runtime is using for \c System.String . + * \returns The \c MonoClass* for the \c System.String type. + */ +MonoClass* +mono_get_string_class (void) +{ + return mono_defaults.string_class; +} + +/** + * mono_get_enum_class: + * Use this function to get the \c MonoClass* that the runtime is using for \c System.Enum . + * \returns The \c MonoClass* for the \c System.Enum type. + */ +MonoClass* +mono_get_enum_class (void) +{ + return mono_defaults.enum_class; +} + +/** + * mono_get_array_class: + * Use this function to get the \c MonoClass* that the runtime is using for \c System.Array . + * \returns The \c MonoClass* for the \c System.Array type. + */ +MonoClass* +mono_get_array_class (void) +{ + return mono_defaults.array_class; +} + +/** + * mono_get_thread_class: + * Use this function to get the \c MonoClass* that the runtime is using for \c System.Threading.Thread . + * \returns The \c MonoClass* for the \c System.Threading.Thread type. + */ +MonoClass* +mono_get_thread_class (void) +{ + return mono_defaults.thread_class; +} + +/** + * mono_get_exception_class: + * Use this function to get the \c MonoClass* that the runtime is using for \c System.Exception . + * \returns The \c MonoClass* for the \c type. + */ +MonoClass* +mono_get_exception_class (void) +{ + return mono_defaults.exception_class; +} + + +static char* get_attribute_value (const gchar **attribute_names, + const gchar **attribute_values, + const char *att_name) +{ + int n; + for (n=0; attribute_names[n] != NULL; n++) { + if (strcmp (attribute_names[n], att_name) == 0) + return g_strdup (attribute_values[n]); + } + return NULL; +} + +static void start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error) +{ + AppConfigInfo* app_config = (AppConfigInfo*) user_data; + + if (strcmp (element_name, "configuration") == 0) { + app_config->configuration_count++; + return; + } + if (strcmp (element_name, "startup") == 0) { + app_config->startup_count++; + return; + } + + if (app_config->configuration_count != 1 || app_config->startup_count != 1) + return; + + if (strcmp (element_name, "requiredRuntime") == 0) { + app_config->required_runtime = get_attribute_value (attribute_names, attribute_values, "version"); + } else if (strcmp (element_name, "supportedRuntime") == 0) { + char *version = get_attribute_value (attribute_names, attribute_values, "version"); + app_config->supported_runtimes = g_slist_append (app_config->supported_runtimes, version); + } +} + +static void end_element (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error) +{ + AppConfigInfo* app_config = (AppConfigInfo*) user_data; + + if (strcmp (element_name, "configuration") == 0) { + app_config->configuration_count--; + } else if (strcmp (element_name, "startup") == 0) { + app_config->startup_count--; + } +} + +static const GMarkupParser +mono_parser = { + start_element, + end_element, + NULL, + NULL, + NULL +}; + +static AppConfigInfo * +app_config_parse (const char *exe_filename) +{ + AppConfigInfo *app_config; + GMarkupParseContext *context; + char *text; + gsize len; + const char *bundled_config; + char *config_filename; + + bundled_config = mono_config_string_for_assembly_file (exe_filename); + + if (bundled_config) { + text = g_strdup (bundled_config); + len = strlen (text); + } else { + config_filename = g_strconcat (exe_filename, ".config", NULL); + + if (!g_file_get_contents (config_filename, &text, &len, NULL)) { + g_free (config_filename); + return NULL; + } + g_free (config_filename); + } + + app_config = g_new0 (AppConfigInfo, 1); + + context = g_markup_parse_context_new (&mono_parser, (GMarkupParseFlags)0, app_config, NULL); + if (g_markup_parse_context_parse (context, text, len, NULL)) { + g_markup_parse_context_end_parse (context, NULL); + } + g_markup_parse_context_free (context); + g_free (text); + return app_config; +} + +static void +app_config_free (AppConfigInfo* app_config) +{ + char *rt; + GSList *list = app_config->supported_runtimes; + while (list != NULL) { + rt = (char*)list->data; + g_free (rt); + list = g_slist_next (list); + } + g_slist_free (app_config->supported_runtimes); + g_free (app_config->required_runtime); + g_free (app_config); +} + + +static const MonoRuntimeInfo* +get_runtime_by_version (const char *version) +{ + int n; + int max = G_N_ELEMENTS (supported_runtimes); + int vlen; + + if (!version) + return NULL; + + for (n=0; n= 4 && version [1] - '0' >= 4) { + for (n=0; nsupported_runtimes != NULL) { + int n = 0; + GSList *list = app_config->supported_runtimes; + while (list != NULL) { + version = (char*) list->data; + runtime = get_runtime_by_version (version); + if (runtime != NULL) + runtimes [n++] = runtime; + list = g_slist_next (list); + } + runtimes [n] = NULL; + app_config_free (app_config); + return; + } + + /* Check the requiredRuntime element. This is for 1.0 apps only. */ + if (app_config->required_runtime != NULL) { + runtimes [0] = get_runtime_by_version (app_config->required_runtime); + runtimes [1] = NULL; + app_config_free (app_config); + return; + } + app_config_free (app_config); + } + + /* Look for a runtime with the exact version */ + image = mono_assembly_open_from_bundle (exe_file, NULL, FALSE); + + if (image == NULL) + image = mono_image_open (exe_file, NULL); + + if (image == NULL) { + /* The image is wrong or the file was not found. In this case return + * a default runtime and leave to the initialization method the work of + * reporting the error. + */ + runtimes [0] = get_runtime_by_version (DEFAULT_RUNTIME_VERSION); + runtimes [1] = NULL; + return; + } + + *exe_image = image; + + runtimes [0] = get_runtime_by_version (image->version); + runtimes [1] = NULL; +} + + +/** + * mono_get_runtime_info: + * + * Returns: the version of the current runtime instance. + */ +const MonoRuntimeInfo* +mono_get_runtime_info (void) +{ + return current_runtime; +} + +/** + * mono_framework_version: + * + * Return the major version of the framework curently executing. + */ +int +mono_framework_version (void) +{ + return current_runtime->framework_version [0] - '0'; +} + +void +mono_enable_debug_domain_unload (gboolean enable) +{ + debug_domain_unload = enable; +} + +MonoAotCacheConfig * +mono_get_aot_cache_config (void) +{ + return &aot_cache_config; +} + +void +mono_domain_lock (MonoDomain *domain) +{ + mono_locks_coop_acquire (&domain->lock, DomainLock); +} + +void +mono_domain_unlock (MonoDomain *domain) +{ + mono_locks_coop_release (&domain->lock, DomainLock); +} + +GPtrArray* +mono_domain_get_assemblies (MonoDomain *domain, gboolean refonly) +{ + GSList *tmp; + GPtrArray *assemblies; + MonoAssembly *ass; + + assemblies = g_ptr_array_new (); + mono_domain_assemblies_lock (domain); + for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) { + ass = (MonoAssembly *)tmp->data; + if (refonly != ass->ref_only) + continue; + if (ass->corlib_internal) + continue; + g_ptr_array_add (assemblies, ass); + } + mono_domain_assemblies_unlock (domain); + return assemblies; +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/dynamic-image-internals.h b/unity-2019.4.24f1-mbe/mono/metadata/dynamic-image-internals.h new file mode 100644 index 000000000..bb1084e98 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/dynamic-image-internals.h @@ -0,0 +1,55 @@ +/** + * \file + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#ifndef __MONO_METADATA_DYNAMIC_IMAGE_INTERNALS_H__ +#define __MONO_METADATA_DYNAMIC_IMAGE_INTERNALS_H__ + +#include +#include +#include + +typedef struct { + guint32 import_lookup_table; + guint32 timestamp; + guint32 forwarder; + guint32 name_rva; + guint32 import_address_table_rva; +} MonoIDT; + +typedef struct { + guint32 name_rva; + guint32 flags; +} MonoILT; + + +typedef enum { + MONO_DYN_IMAGE_TOK_NEW, /* assert if same token is registered already */ + MONO_DYN_IMAGE_TOK_SAME_OK, /* allow collision only with the same object */ + MONO_DYN_IMAGE_TOK_REPLACE, /* keep the new object, always */ +} MonoDynamicImageTokCollision; + +void +mono_dynamic_images_init (void); + +void +mono_dynamic_image_register_token (MonoDynamicImage *assembly, guint32 token, MonoObjectHandle obj, int tok_collision); + +gboolean +mono_dynamic_image_is_valid_token (MonoDynamicImage *image, guint32 token); + +MonoObjectHandle +mono_dynamic_image_get_registered_token (MonoDynamicImage *dynimage, guint32 token, MonoError *error); + +MonoDynamicImage* +mono_dynamic_image_create (MonoDynamicAssembly *assembly, char *assembly_name, char *module_name); + +guint32 +mono_dynamic_image_add_to_blob_cached (MonoDynamicImage *assembly, char *b1, int s1, char *b2, int s2); + +void +mono_dynimage_alloc_table (MonoDynamicTable *table, guint nrows); + +#endif /* __MONO_METADATA_DYNAMIC_IMAGE_INTERNALS_H__ */ + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/dynamic-image.c b/unity-2019.4.24f1-mbe/mono/metadata/dynamic-image.c new file mode 100644 index 000000000..e386fbbe7 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/dynamic-image.c @@ -0,0 +1,563 @@ +/** + * \file + * Images created at runtime. + * + * + * Author: + * Paolo Molaro (lupus@ximian.com) + * + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Copyright 2011 Rodrigo Kumpera + * Copyright 2016 Microsoft + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#include +#include "mono/metadata/object.h" +#include "mono/metadata/dynamic-image-internals.h" +#include "mono/metadata/dynamic-stream-internals.h" +#include "mono/metadata/gc-internals.h" +#include "mono/metadata/metadata-internals.h" +#include "mono/metadata/profiler-private.h" +#include "mono/metadata/reflection-internals.h" +#include "mono/metadata/sre-internals.h" +#include "mono/utils/checked-build.h" +#include "mono/utils/mono-error-internals.h" +#include "mono/utils/mono-os-mutex.h" + +const unsigned char table_sizes [MONO_TABLE_NUM] = { + MONO_MODULE_SIZE, + MONO_TYPEREF_SIZE, + MONO_TYPEDEF_SIZE, + 0, + MONO_FIELD_SIZE, + 0, + MONO_METHOD_SIZE, + 0, + MONO_PARAM_SIZE, + MONO_INTERFACEIMPL_SIZE, + MONO_MEMBERREF_SIZE, /* 0x0A */ + MONO_CONSTANT_SIZE, + MONO_CUSTOM_ATTR_SIZE, + MONO_FIELD_MARSHAL_SIZE, + MONO_DECL_SECURITY_SIZE, + MONO_CLASS_LAYOUT_SIZE, + MONO_FIELD_LAYOUT_SIZE, /* 0x10 */ + MONO_STAND_ALONE_SIGNATURE_SIZE, + MONO_EVENT_MAP_SIZE, + 0, + MONO_EVENT_SIZE, + MONO_PROPERTY_MAP_SIZE, + 0, + MONO_PROPERTY_SIZE, + MONO_METHOD_SEMA_SIZE, + MONO_METHODIMPL_SIZE, + MONO_MODULEREF_SIZE, /* 0x1A */ + MONO_TYPESPEC_SIZE, + MONO_IMPLMAP_SIZE, + MONO_FIELD_RVA_SIZE, + 0, + 0, + MONO_ASSEMBLY_SIZE, /* 0x20 */ + MONO_ASSEMBLY_PROCESSOR_SIZE, + MONO_ASSEMBLYOS_SIZE, + MONO_ASSEMBLYREF_SIZE, + MONO_ASSEMBLYREFPROC_SIZE, + MONO_ASSEMBLYREFOS_SIZE, + MONO_FILE_SIZE, + MONO_EXP_TYPE_SIZE, + MONO_MANIFEST_SIZE, + MONO_NESTED_CLASS_SIZE, + + MONO_GENERICPARAM_SIZE, /* 0x2A */ + MONO_METHODSPEC_SIZE, + MONO_GENPARCONSTRAINT_SIZE + +}; + +// The dynamic images list is only needed to support the mempool reference tracking feature in checked-build. +static GPtrArray *dynamic_images; +static mono_mutex_t dynamic_images_mutex; + +static inline void +dynamic_images_lock (void) +{ + mono_os_mutex_lock (&dynamic_images_mutex); +} + +static inline void +dynamic_images_unlock (void) +{ + mono_os_mutex_unlock (&dynamic_images_mutex); +} + +void +mono_dynamic_images_init (void) +{ + mono_os_mutex_init (&dynamic_images_mutex); +} + +#ifndef DISABLE_REFLECTION_EMIT +static void +string_heap_init (MonoDynamicStream *sh) +{ + mono_dynstream_init (sh); +} +#endif + +#ifndef DISABLE_REFLECTION_EMIT +static int +mono_blob_entry_hash (const char* str) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + guint len, h; + const char *end; + len = mono_metadata_decode_blob_size (str, &str); + if (len > 0) { + end = str + len; + h = *str; + for (str += 1; str < end; str++) + h = (h << 5) - h + *str; + return h; + } else { + return 0; + } +} + +static gboolean +mono_blob_entry_equal (const char *str1, const char *str2) { + MONO_REQ_GC_NEUTRAL_MODE; + + int len, len2; + const char *end1; + const char *end2; + len = mono_metadata_decode_blob_size (str1, &end1); + len2 = mono_metadata_decode_blob_size (str2, &end2); + if (len != len2) + return 0; + return memcmp (end1, end2, len) == 0; +} +#endif + + +/** + * mono_find_dynamic_image_owner: + * + * Find the dynamic image, if any, which a given pointer is located in the memory of. + */ +MonoImage * +mono_find_dynamic_image_owner (void *ptr) +{ + MonoImage *owner = NULL; + int i; + + dynamic_images_lock (); + + if (dynamic_images) + { + for (i = 0; !owner && i < dynamic_images->len; ++i) { + MonoImage *image = (MonoImage *)g_ptr_array_index (dynamic_images, i); + if (mono_mempool_contains_addr (image->mempool, ptr)) + owner = image; + } + } + + dynamic_images_unlock (); + + return owner; +} + +static inline void +dynamic_image_lock (MonoDynamicImage *image) +{ + MONO_ENTER_GC_SAFE; + mono_image_lock ((MonoImage*)image); + MONO_EXIT_GC_SAFE; +} + +static inline void +dynamic_image_unlock (MonoDynamicImage *image) +{ + mono_image_unlock ((MonoImage*)image); +} + +#ifndef DISABLE_REFLECTION_EMIT +/* + * mono_dynamic_image_register_token: + * + * Register the TOKEN->OBJ mapping in the mapping table in ASSEMBLY. This is required for + * the Module.ResolveXXXToken () methods to work. + */ +void +mono_dynamic_image_register_token (MonoDynamicImage *assembly, guint32 token, MonoObjectHandle obj, int how_collide) +{ + MONO_REQ_GC_UNSAFE_MODE; + + g_assert (!MONO_HANDLE_IS_NULL (obj)); + g_assert (strcmp (mono_handle_class (obj)->name, "EnumBuilder")); + dynamic_image_lock (assembly); + MonoObject *prev = (MonoObject *)mono_g_hash_table_lookup (assembly->tokens, GUINT_TO_POINTER (token)); + if (prev) { + switch (how_collide) { + case MONO_DYN_IMAGE_TOK_NEW: + g_warning ("%s: Unexpected previous object when called with MONO_DYN_IMAGE_TOK_NEW", __func__); + break; + case MONO_DYN_IMAGE_TOK_SAME_OK: + if (prev != MONO_HANDLE_RAW (obj)) { + g_warning ("%s: condition `prev == MONO_HANDLE_RAW (obj)' not met", __func__); + } + break; + case MONO_DYN_IMAGE_TOK_REPLACE: + break; + default: + g_assert_not_reached (); + } + } + mono_g_hash_table_insert (assembly->tokens, GUINT_TO_POINTER (token), MONO_HANDLE_RAW (obj)); + dynamic_image_unlock (assembly); +} +#else +void +mono_dynamic_image_register_token (MonoDynamicImage *assembly, guint32 token, MonoObjectHandle obj, int how_collide) +{ +} +#endif + +static MonoObject* +lookup_dyn_token (MonoDynamicImage *assembly, guint32 token) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoObject *obj; + + dynamic_image_lock (assembly); + obj = (MonoObject *)mono_g_hash_table_lookup (assembly->tokens, GUINT_TO_POINTER (token)); + dynamic_image_unlock (assembly); + + return obj; +} + +#ifndef DISABLE_REFLECTION_EMIT +MonoObjectHandle +mono_dynamic_image_get_registered_token (MonoDynamicImage *dynimage, guint32 token, MonoError *error) +{ + error_init (error); + return MONO_HANDLE_NEW (MonoObject, lookup_dyn_token (dynimage, token)); +} +#else /* DISABLE_REFLECTION_EMIT */ +MonoObjectHandle +mono_dynamic_image_get_registered_token (MonoDynamicImage *dynimage, guint32 token, MonoError *error) +{ + g_assert_not_reached (); + return NULL_HANDLE; +} +#endif + +/** + * + * mono_dynamic_image_is_valid_token: + * + * Returns TRUE if token is valid in the given image. + * + */ +gboolean +mono_dynamic_image_is_valid_token (MonoDynamicImage *image, guint32 token) +{ + return lookup_dyn_token (image, token) != NULL; +} + +#ifndef DISABLE_REFLECTION_EMIT + +#endif /* DISABLE_REFLECTION_EMIT */ + +#ifndef DISABLE_REFLECTION_EMIT +/** + * mono_reflection_lookup_dynamic_token: + * + * Finish the Builder object pointed to by TOKEN and return the corresponding + * runtime structure. If HANDLE_CLASS is not NULL, it is set to the class required by + * mono_ldtoken. If valid_token is TRUE, assert if it is not found in the token->object + * mapping table. + * + * LOCKING: Take the loader lock + */ +gpointer +mono_reflection_lookup_dynamic_token (MonoImage *image, guint32 token, gboolean valid_token, MonoClass **handle_class, MonoGenericContext *context, MonoError *error) +{ + MonoDynamicImage *assembly = (MonoDynamicImage*)image; + MonoObject *obj; + MonoClass *klass; + + error_init (error); + + obj = lookup_dyn_token (assembly, token); + if (!obj) { + if (valid_token) + g_error ("Could not find required dynamic token 0x%08x", token); + else { + mono_error_set_execution_engine (error, "Could not find dynamic token 0x%08x", token); + return NULL; + } + } + + if (!handle_class) + handle_class = &klass; + gpointer result = mono_reflection_resolve_object (image, obj, handle_class, context, error); + return result; +} +#else /* DISABLE_REFLECTION_EMIT */ +gpointer +mono_reflection_lookup_dynamic_token (MonoImage *image, guint32 token, gboolean valid_token, MonoClass **handle_class, MonoGenericContext *context, MonoError *error) +{ + error_init (error); + return NULL; +} +#endif /* DISABLE_REFLECTION_EMIT */ + +#ifndef DISABLE_REFLECTION_EMIT +MonoDynamicImage* +mono_dynamic_image_create (MonoDynamicAssembly *assembly, char *assembly_name, char *module_name) +{ + static const guchar entrycode [16] = {0xff, 0x25, 0}; + MonoDynamicImage *image; + int i; + + const char *version; + + if (!strcmp (mono_get_runtime_info ()->framework_version, "2.1")) + version = "v2.0.50727"; /* HACK: SL 2 enforces the .net 2 metadata version */ + else + version = mono_get_runtime_info ()->runtime_version; + + image = g_new0 (MonoDynamicImage, 1); + + MONO_PROFILER_RAISE (image_loading, (&image->image)); + + /*g_print ("created image %p\n", image);*/ + /* keep in sync with image.c */ + image->image.name = assembly_name; + image->image.assembly_name = image->image.name; /* they may be different */ + image->image.module_name = module_name; + image->image.version = g_strdup (version); + image->image.md_version_major = 1; + image->image.md_version_minor = 1; + image->image.dynamic = TRUE; + + image->image.references = g_new0 (MonoAssembly*, 1); + image->image.references [0] = NULL; + + mono_image_init (&image->image); + + image->token_fixups = mono_g_hash_table_new_type ((GHashFunc)mono_object_hash, NULL, MONO_HASH_KEY_GC, MONO_ROOT_SOURCE_REFLECTION, NULL, "Reflection Dynamic Image Token Fixup Table"); + image->method_to_table_idx = g_hash_table_new (NULL, NULL); + image->field_to_table_idx = g_hash_table_new (NULL, NULL); + image->method_aux_hash = g_hash_table_new (NULL, NULL); + image->vararg_aux_hash = g_hash_table_new (NULL, NULL); + image->handleref = g_hash_table_new (NULL, NULL); + image->tokens = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_REFLECTION, NULL, "Reflection Dynamic Image Token Table"); + image->generic_def_objects = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_REFLECTION, NULL, "Reflection Dynamic Image Generic Definition Table"); + image->typespec = g_hash_table_new ((GHashFunc)mono_metadata_type_hash, (GCompareFunc)mono_metadata_type_equal); + image->typeref = g_hash_table_new ((GHashFunc)mono_metadata_type_hash, (GCompareFunc)mono_metadata_type_equal); + image->blob_cache = g_hash_table_new ((GHashFunc)mono_blob_entry_hash, (GCompareFunc)mono_blob_entry_equal); + image->gen_params = g_ptr_array_new (); + image->remapped_tokens = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_REFLECTION, NULL, "Reflection Dynamic Image Remapped Token Table"); + + /*g_print ("string heap create for image %p (%s)\n", image, module_name);*/ + string_heap_init (&image->sheap); + mono_dynstream_add_data (&image->us, "", 1); + mono_dynamic_image_add_to_blob_cached (image, (char*) "", 1, NULL, 0); + /* import tables... */ + mono_dynstream_add_data (&image->code, (char*)entrycode, sizeof (entrycode)); + image->iat_offset = mono_dynstream_add_zero (&image->code, 8); /* two IAT entries */ + image->idt_offset = mono_dynstream_add_zero (&image->code, 2 * sizeof (MonoIDT)); /* two IDT entries */ + image->imp_names_offset = mono_dynstream_add_zero (&image->code, 2); /* flags for name entry */ + mono_dynstream_add_data (&image->code, "_CorExeMain", 12); + mono_dynstream_add_data (&image->code, "mscoree.dll", 12); + image->ilt_offset = mono_dynstream_add_zero (&image->code, 8); /* two ILT entries */ + mono_dynstream_data_align (&image->code); + + image->cli_header_offset = mono_dynstream_add_zero (&image->code, sizeof (MonoCLIHeader)); + + for (i=0; i < MONO_TABLE_NUM; ++i) { + image->tables [i].next_idx = 1; + image->tables [i].columns = table_sizes [i]; + } + + image->image.assembly = (MonoAssembly*)assembly; + image->run = assembly->run; + image->save = assembly->save; + image->pe_kind = 0x1; /* ILOnly */ + image->machine = 0x14c; /* I386 */ + + MONO_PROFILER_RAISE (image_loaded, (&image->image)); + + dynamic_images_lock (); + + if (!dynamic_images) + dynamic_images = g_ptr_array_new (); + + g_ptr_array_add (dynamic_images, image); + + dynamic_images_unlock (); + + return image; +} +#else /* DISABLE_REFLECTION_EMIT */ +MonoDynamicImage* +mono_dynamic_image_create (MonoDynamicAssembly *assembly, char *assembly_name, char *module_name) +{ + g_assert_not_reached (); + return NULL; +} +#endif /* DISABLE_REFLECTION_EMIT */ + +guint32 +mono_dynamic_image_add_to_blob_cached (MonoDynamicImage *assembly, char *b1, int s1, char *b2, int s2) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + guint32 idx; + char *copy; + gpointer oldkey, oldval; + + copy = (char *)g_malloc (s1+s2); + memcpy (copy, b1, s1); + memcpy (copy + s1, b2, s2); + if (g_hash_table_lookup_extended (assembly->blob_cache, copy, &oldkey, &oldval)) { + g_free (copy); + idx = GPOINTER_TO_UINT (oldval); + } else { + idx = mono_dynstream_add_data (&assembly->blob, b1, s1); + mono_dynstream_add_data (&assembly->blob, b2, s2); + g_hash_table_insert (assembly->blob_cache, copy, GUINT_TO_POINTER (idx)); + } + return idx; +} + +void +mono_dynimage_alloc_table (MonoDynamicTable *table, guint nrows) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + table->rows = nrows; + g_assert (table->columns); + if (nrows + 1 >= table->alloc_rows) { + while (nrows + 1 >= table->alloc_rows) { + if (table->alloc_rows == 0) + table->alloc_rows = 16; + else + table->alloc_rows *= 2; + } + + table->values = (guint32 *)g_renew (guint32, table->values, (table->alloc_rows) * table->columns); + } +} + + +static void +free_blob_cache_entry (gpointer key, gpointer val, gpointer user_data) +{ + g_free (key); +} + +static void +release_hashtable (MonoGHashTable **hash) +{ + if (*hash) { + mono_g_hash_table_destroy (*hash); + *hash = NULL; + } +} + +void +mono_dynamic_image_release_gc_roots (MonoDynamicImage *image) +{ + release_hashtable (&image->token_fixups); + release_hashtable (&image->tokens); + release_hashtable (&image->remapped_tokens); + release_hashtable (&image->generic_def_objects); +} + +// Free dynamic image pass one: Free resources but not image itself +void +mono_dynamic_image_free (MonoDynamicImage *image) +{ + MonoDynamicImage *di = image; + GList *list; + int i; + + if (di->typespec) + g_hash_table_destroy (di->typespec); + if (di->typeref) + g_hash_table_destroy (di->typeref); + if (di->handleref) + g_hash_table_destroy (di->handleref); + if (di->tokens) + mono_g_hash_table_destroy (di->tokens); + if (di->remapped_tokens) + mono_g_hash_table_destroy (di->remapped_tokens); + if (di->generic_def_objects) + mono_g_hash_table_destroy (di->generic_def_objects); + if (di->blob_cache) { + g_hash_table_foreach (di->blob_cache, free_blob_cache_entry, NULL); + g_hash_table_destroy (di->blob_cache); + } + if (di->standalonesig_cache) + g_hash_table_destroy (di->standalonesig_cache); + for (list = di->array_methods; list; list = list->next) { + ArrayMethod *am = (ArrayMethod *)list->data; + mono_sre_array_method_free (am); + } + g_list_free (di->array_methods); + if (di->gen_params) { + for (i = 0; i < di->gen_params->len; i++) { + GenericParamTableEntry *entry = (GenericParamTableEntry *)g_ptr_array_index (di->gen_params, i); + mono_sre_generic_param_table_entry_free (entry); + } + g_ptr_array_free (di->gen_params, TRUE); + } + if (di->token_fixups) + mono_g_hash_table_destroy (di->token_fixups); + if (di->method_to_table_idx) + g_hash_table_destroy (di->method_to_table_idx); + if (di->field_to_table_idx) + g_hash_table_destroy (di->field_to_table_idx); + if (di->method_aux_hash) + g_hash_table_destroy (di->method_aux_hash); + if (di->vararg_aux_hash) + g_hash_table_destroy (di->vararg_aux_hash); + g_free (di->strong_name); + g_free (di->win32_res); + if (di->public_key) + g_free (di->public_key); + + /*g_print ("string heap destroy for image %p\n", di);*/ + mono_dynamic_stream_reset (&di->sheap); + mono_dynamic_stream_reset (&di->code); + mono_dynamic_stream_reset (&di->resources); + mono_dynamic_stream_reset (&di->us); + mono_dynamic_stream_reset (&di->blob); + mono_dynamic_stream_reset (&di->tstream); + mono_dynamic_stream_reset (&di->guid); + for (i = 0; i < MONO_TABLE_NUM; ++i) { + g_free (di->tables [i].values); + } + + dynamic_images_lock (); + + if (dynamic_images) + g_ptr_array_remove (dynamic_images, di); + + dynamic_images_unlock (); +} + +// Free dynamic image pass two: Free image itself (might never get called in some debug modes) +void +mono_dynamic_image_free_image (MonoDynamicImage *image) +{ + g_free (image); +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/dynamic-stream-internals.h b/unity-2019.4.24f1-mbe/mono/metadata/dynamic-stream-internals.h new file mode 100644 index 000000000..19570fb39 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/dynamic-stream-internals.h @@ -0,0 +1,31 @@ +/** + * \file + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#ifndef __MONO_METADATA_DYNAMIC_STREAM_INTERNALS_H__ +#define __MONO_METADATA_DYNAMIC_STREAM_INTERNALS_H__ + +#include +#include + +void +mono_dynstream_init (MonoDynamicStream *stream); + +guint32 +mono_dynstream_insert_string (MonoDynamicStream *sh, const char *str); + +guint32 +mono_dynstream_insert_mstring (MonoDynamicStream *sh, MonoString *str, MonoError *error); + +guint32 +mono_dynstream_add_data (MonoDynamicStream *stream, const char *data, guint32 len); + +guint32 +mono_dynstream_add_zero (MonoDynamicStream *stream, guint32 len); + +void +mono_dynstream_data_align (MonoDynamicStream *stream); + +#endif /* __MONO_METADATA_DYNAMIC_STREAM_INTERNALS_H__ */ + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/dynamic-stream.c b/unity-2019.4.24f1-mbe/mono/metadata/dynamic-stream.c new file mode 100644 index 000000000..0081f0e3f --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/dynamic-stream.c @@ -0,0 +1,132 @@ +/** + * \file + * MonoDynamicStream + * Copyright 2016 Microsoft + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#include + +#include "mono/metadata/dynamic-stream-internals.h" +#include "mono/metadata/metadata-internals.h" +#include "mono/utils/checked-build.h" +#include "mono/utils/mono-error-internals.h" + +void +mono_dynstream_init (MonoDynamicStream *sh) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + sh->index = 0; + sh->alloc_size = 4096; + sh->data = (char *)g_malloc (4096); + sh->hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + mono_dynstream_insert_string (sh, ""); +} + +static void +make_room_in_stream (MonoDynamicStream *stream, int size) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + if (size <= stream->alloc_size) + return; + + while (stream->alloc_size <= size) { + if (stream->alloc_size < 4096) + stream->alloc_size = 4096; + else + stream->alloc_size *= 2; + } + + stream->data = (char *)g_realloc (stream->data, stream->alloc_size); +} + +guint32 +mono_dynstream_insert_string (MonoDynamicStream *sh, const char *str) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + guint32 idx; + guint32 len; + gpointer oldkey, oldval; + + if (g_hash_table_lookup_extended (sh->hash, str, &oldkey, &oldval)) + return GPOINTER_TO_UINT (oldval); + + len = strlen (str) + 1; + idx = sh->index; + + make_room_in_stream (sh, idx + len); + + /* + * We strdup the string even if we already copy them in sh->data + * so that the string pointers in the hash remain valid even if + * we need to realloc sh->data. We may want to avoid that later. + */ + g_hash_table_insert (sh->hash, g_strdup (str), GUINT_TO_POINTER (idx)); + memcpy (sh->data + idx, str, len); + sh->index += len; + return idx; +} + +guint32 +mono_dynstream_insert_mstring (MonoDynamicStream *sh, MonoString *str, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + char *name = mono_string_to_utf8_checked (str, error); + return_val_if_nok (error, -1); + guint32 idx; + idx = mono_dynstream_insert_string (sh, name); + g_free (name); + return idx; +} + +guint32 +mono_dynstream_add_data (MonoDynamicStream *stream, const char *data, guint32 len) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + guint32 idx; + + make_room_in_stream (stream, stream->index + len); + memcpy (stream->data + stream->index, data, len); + idx = stream->index; + stream->index += len; + /* + * align index? Not without adding an additional param that controls it since + * we may store a blob value in pieces. + */ + return idx; +} + +guint32 +mono_dynstream_add_zero (MonoDynamicStream *stream, guint32 len) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + guint32 idx; + + make_room_in_stream (stream, stream->index + len); + memset (stream->data + stream->index, 0, len); + idx = stream->index; + stream->index += len; + return idx; +} + +void +mono_dynstream_data_align (MonoDynamicStream *stream) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + guint32 count = stream->index % 4; + + /* we assume the stream data will be aligned */ + if (count) + mono_dynstream_add_zero (stream, 4 - count); +} + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/environment.c b/unity-2019.4.24f1-mbe/mono/metadata/environment.c new file mode 100644 index 000000000..b7001b57a --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/environment.c @@ -0,0 +1,79 @@ +/** + * \file + * System.Environment support internal calls + * + * Authors: + * Dick Porter (dick@ximian.com) + * Sebastien Pouliot (sebastien@ximian.com) + * + * Copyright 2002-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +extern MonoStringHandle ves_icall_System_Environment_GetOSVersionString (MonoError *error); + +#if !defined(HOST_WIN32) && defined(HAVE_SYS_UTSNAME_H) +#include +#endif + +static gint32 exitcode=0; + +/** + * mono_environment_exitcode_get: + */ +gint32 +mono_environment_exitcode_get (void) +{ + return(exitcode); +} + +/** + * mono_environment_exitcode_set: + */ +void +mono_environment_exitcode_set (gint32 value) +{ + exitcode=value; +} + +/* note: we better manipulate the string in managed code (easier and safer) */ +MonoStringHandle +ves_icall_System_Environment_GetOSVersionString (MonoError *error) +{ + error_init (error); +#ifdef HOST_WIN32 + OSVERSIONINFOEX verinfo; + + verinfo.dwOSVersionInfoSize = sizeof (OSVERSIONINFOEX); + if (GetVersionEx ((OSVERSIONINFO*)&verinfo)) { + char version [128]; + /* maximum string length is 45 bytes + 4 x 10 bytes per number, 1 byte for 0, 3 x 1 byte for dots, 1 for NULL */ + sprintf (version, "%ld.%ld.%ld.%d", + verinfo.dwMajorVersion, + verinfo.dwMinorVersion, + verinfo.dwBuildNumber, + verinfo.wServicePackMajor << 16); + return mono_string_new_handle (mono_domain_get (), version, error); + } +#elif defined(HAVE_SYS_UTSNAME_H) + struct utsname name; + + if (uname (&name) >= 0) { + return mono_string_new_handle (mono_domain_get (), name.release, error); + } +#endif + return mono_string_new_handle (mono_domain_get (), "0.0.0.0", error); +} + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/environment.h b/unity-2019.4.24f1-mbe/mono/metadata/environment.h new file mode 100644 index 000000000..617d18efc --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/environment.h @@ -0,0 +1,23 @@ +/** + * \file + * System.Environment support internal calls + * + * Author: + * Dick Porter (dick@ximian.com) + * + * (C) 2002 Ximian, Inc + */ + +#ifndef _MONO_METADATA_ENVIRONMENT_H_ +#define _MONO_METADATA_ENVIRONMENT_H_ + +#include + +MONO_BEGIN_DECLS + +MONO_API extern int32_t mono_environment_exitcode_get (void); +MONO_API extern void mono_environment_exitcode_set (int32_t value); + +MONO_END_DECLS + +#endif /* _MONO_METADATA_ENVIRONMENT_H_ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/exception-internals.h b/unity-2019.4.24f1-mbe/mono/metadata/exception-internals.h new file mode 100644 index 000000000..2b567e3fd --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/exception-internals.h @@ -0,0 +1,44 @@ +/** + * \file + */ + +#ifndef _MONO_METADATA_EXCEPTION_INTERNALS_H_ +#define _MONO_METADATA_EXCEPTION_INTERNALS_H_ + +#include + +#include +#include +#include + +MonoException * +mono_get_exception_type_initialization_checked (const gchar *type_name, MonoException *inner, MonoError *error); + +MonoExceptionHandle +mono_get_exception_reflection_type_load_checked (MonoArrayHandle types, MonoArrayHandle exceptions, MonoError *error); + +MonoException * +mono_get_exception_runtime_wrapped_checked (MonoObject *wrapped_exception, MonoError *error); + +MonoException * +mono_exception_from_name_two_strings_checked (MonoImage *image, const char *name_space, + const char *name, MonoString *a1, MonoString *a2, + MonoError *error); + +MonoException * +mono_exception_from_token_two_strings_checked (MonoImage *image, uint32_t token, + MonoString *a1, MonoString *a2, + MonoError *error); + +MonoException * +mono_exception_from_name_four_strings_checked (MonoImage *image, const char *name_space, + const char *name, MonoString *a1, MonoString *a2, MonoString *a3, MonoString *a4, + MonoError *error); + + +typedef int (*MonoGetSeqPointFunc) (MonoDomain *domain, MonoMethod *method, gint32 native_offset); + +void +mono_install_get_seq_point (MonoGetSeqPointFunc func); + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/exception.c b/unity-2019.4.24f1-mbe/mono/metadata/exception.c new file mode 100644 index 000000000..9ee13f95a --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/exception.c @@ -0,0 +1,1192 @@ +/** + * \file + * Exception handling + * + * Authors: + * Paolo Molaro (lupus@ximian.com) + * Dietmar Maurer (dietmar@ximian.com) + * Dick Porter (dick@ximian.com) + * Miguel de Icaza (miguel@ximian.com) + * + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_EXECINFO_H +#include +#endif + +static MonoUnhandledExceptionFunc unhandled_exception_hook = NULL; +static gpointer unhandled_exception_hook_data = NULL; + +/** + * mono_exception_from_name: + * \param image the Mono image where to look for the class + * \param name_space the namespace for the class + * \param name class name + * + * Creates an exception of the given namespace/name class in the + * current domain. + * + * \returns the initialized exception instance. + */ +MonoException * +mono_exception_from_name (MonoImage *image, const char *name_space, + const char *name) +{ + return mono_exception_from_name_domain (mono_domain_get (), image, name_space, name); +} + +/** + * mono_exception_from_name_domain: + * \param domain Domain where the return object will be created. + * \param image the Mono image where to look for the class + * \param name_space the namespace for the class + * \param name class name + * + * Creates an exception object of the given namespace/name class on + * the given domain. + * + * \returns the initialized exception instance. + */ +MonoException * +mono_exception_from_name_domain (MonoDomain *domain, MonoImage *image, + const char* name_space, const char *name) +{ + MonoError error; + MonoClass *klass; + MonoObject *o; + MonoDomain *caller_domain = mono_domain_get (); + + klass = mono_class_load_from_name (image, name_space, name); + + o = mono_object_new_checked (domain, klass, &error); + mono_error_assert_ok (&error); + + if (domain != caller_domain) + mono_domain_set_internal (domain); + mono_runtime_object_init_checked (o, &error); + mono_error_assert_ok (&error); + + if (domain != caller_domain) + mono_domain_set_internal (caller_domain); + + return (MonoException *)o; +} + + +/** + * mono_exception_from_token: + * \param image the Mono image where to look for the class + * \param token The type token of the class + * + * Creates an exception of the type given by \p token. + * + * \returns the initialized exception instance. + */ +MonoException * +mono_exception_from_token (MonoImage *image, guint32 token) +{ + MonoError error; + MonoClass *klass; + MonoObject *o; + + klass = mono_class_get_checked (image, token, &error); + mono_error_assert_ok (&error); + + o = mono_object_new_checked (mono_domain_get (), klass, &error); + mono_error_assert_ok (&error); + + mono_runtime_object_init_checked (o, &error); + mono_error_assert_ok (&error); + + return (MonoException *)o; +} + +static MonoException * +create_exception_two_strings (MonoClass *klass, MonoString *a1, MonoString *a2, MonoError *error) +{ + MonoDomain *domain = mono_domain_get (); + MonoMethod *method = NULL; + MonoObject *o; + int count = 1; + gpointer args [2]; + gpointer iter; + MonoMethod *m; + + if (a2 != NULL) + count++; + + o = mono_object_new_checked (domain, klass, error); + mono_error_assert_ok (error); + + iter = NULL; + while ((m = mono_class_get_methods (klass, &iter))) { + MonoMethodSignature *sig; + + if (strcmp (".ctor", mono_method_get_name (m))) + continue; + sig = mono_method_signature (m); + if (sig->param_count != count) + continue; + + if (sig->params [0]->type != MONO_TYPE_STRING) + continue; + if (count == 2 && sig->params [1]->type != MONO_TYPE_STRING) + continue; + method = m; + break; + } + + args [0] = a1; + args [1] = a2; + + mono_runtime_invoke_checked (method, o, args, error); + return_val_if_nok (error, NULL); + + return (MonoException *) o; +} + +/** + * mono_exception_from_name_two_strings: + * \param image the Mono image where to look for the class + * \param name_space the namespace for the class + * \param name class name + * \param a1 first string argument to pass + * \param a2 second string argument to pass + * + * Creates an exception from a constructor that takes two string + * arguments. + * + * \returns the initialized exception instance. + */ +MonoException * +mono_exception_from_name_two_strings (MonoImage *image, const char *name_space, + const char *name, MonoString *a1, MonoString *a2) +{ + MonoError error; + MonoException *ret; + + ret = mono_exception_from_name_two_strings_checked (image, name_space, name, a1, a2, &error); + mono_error_cleanup (&error); + return ret; +} + +/** + * mono_exception_from_name_two_strings_checked: + * \param image the Mono image where to look for the class + * \param name_space the namespace for the class + * \param name class name + * \param a1 first string argument to pass + * \param a2 second string argument to pass + * \param error set on error + * + * Creates an exception from a constructor that takes two string + * arguments. + * + * \returns the initialized exception instance. On failure returns + * NULL and sets \p error. + */ +MonoException * +mono_exception_from_name_two_strings_checked (MonoImage *image, const char *name_space, + const char *name, MonoString *a1, MonoString *a2, + MonoError *error) +{ + MonoClass *klass; + + error_init (error); + klass = mono_class_load_from_name (image, name_space, name); + + return create_exception_two_strings (klass, a1, a2, error); +} + +/** + * mono_exception_from_name_msg: + * \param image the Mono image where to look for the class + * \param name_space the namespace for the class + * \param name class name + * \param msg the message to embed inside the exception + * + * Creates an exception and initializes its message field. + * + * \returns the initialized exception instance. + */ +MonoException * +mono_exception_from_name_msg (MonoImage *image, const char *name_space, + const char *name, const char *msg) +{ + MonoError error; + MonoException *ex; + + ex = mono_exception_from_name (image, name_space, name); + + if (msg) { + MonoString *msg_str = mono_string_new_checked (mono_object_get_domain ((MonoObject*)ex), msg, &error); + mono_error_assert_ok (&error); + MONO_OBJECT_SETREF (ex, message, msg_str); + } + + return ex; +} + +/** + * mono_exception_from_token_two_strings: + * + * Same as mono_exception_from_name_two_strings, but lookup the exception class using + * IMAGE and TOKEN. + */ +MonoException * +mono_exception_from_token_two_strings (MonoImage *image, guint32 token, + MonoString *a1, MonoString *a2) +{ + MonoError error; + MonoException *ret; + ret = mono_exception_from_token_two_strings_checked (image, token, a1, a2, &error); + mono_error_cleanup (&error); + return ret; +} + +/** + * mono_exception_from_token_two_strings_checked: + * + * Same as mono_exception_from_name_two_strings, but lookup the exception class using + * IMAGE and TOKEN. + */ +MonoException * +mono_exception_from_token_two_strings_checked (MonoImage *image, guint32 token, + MonoString *a1, MonoString *a2, + MonoError *error) +{ + MonoClass *klass; + + error_init (error); + + klass = mono_class_get_checked (image, token, error); + mono_error_assert_ok (error); /* FIXME handle the error. */ + + return create_exception_two_strings (klass, a1, a2, error); +} + +/** + * mono_get_exception_divide_by_zero: + * \returns a new instance of the \c System.DivideByZeroException + */ +MonoException * +mono_get_exception_divide_by_zero () +{ + return mono_exception_from_name (mono_get_corlib (), "System", + "DivideByZeroException"); +} + +/** + * mono_get_exception_security: + * \returns a new instance of the \c System.Security.SecurityException + */ +MonoException * +mono_get_exception_security () +{ + return mono_exception_from_name (mono_get_corlib (), "System.Security", + "SecurityException"); +} + +/** + * mono_get_exception_thread_abort: + * \returns a new instance of the \c System.Threading.ThreadAbortException + */ +MonoException * +mono_get_exception_thread_abort () +{ + return mono_exception_from_name (mono_get_corlib (), "System.Threading", + "ThreadAbortException"); +} + +/** + * mono_get_exception_thread_interrupted: + * \returns a new instance of the \c System.Threading.ThreadInterruptedException + */ +MonoException * +mono_get_exception_thread_interrupted () +{ + return mono_exception_from_name (mono_get_corlib (), "System.Threading", + "ThreadInterruptedException"); +} + +/** + * mono_get_exception_arithmetic: + * \returns a new instance of the \c System.ArithmeticException + */ +MonoException * +mono_get_exception_arithmetic () +{ + return mono_exception_from_name (mono_get_corlib (), "System", + "ArithmeticException"); +} + +/** + * mono_get_exception_overflow: + * \returns a new instance of the \c System.OverflowException + */ +MonoException * +mono_get_exception_overflow () +{ + return mono_exception_from_name (mono_get_corlib (), "System", + "OverflowException"); +} + +/** + * mono_get_exception_null_reference: + * \returns a new instance of the \c System.NullReferenceException + */ +MonoException * +mono_get_exception_null_reference () +{ + return mono_exception_from_name (mono_get_corlib (), "System", + "NullReferenceException"); +} + +/** + * mono_get_exception_execution_engine: + * \param msg the message to pass to the user + * \returns a new instance of the \c System.ExecutionEngineException + */ +MonoException * +mono_get_exception_execution_engine (const char *msg) +{ + return mono_exception_from_name_msg (mono_get_corlib (), "System", "ExecutionEngineException", msg); +} + +/** + * mono_get_exception_serialization: + * \param msg the message to pass to the user + * \returns a new instance of the \c System.Runtime.Serialization.SerializationException + */ +MonoException * +mono_get_exception_serialization (const char *msg) +{ + return mono_exception_from_name_msg (mono_get_corlib (), "System.Runtime.Serialization", "SerializationException", msg); +} + +/** + * mono_get_exception_invalid_cast: + * \returns a new instance of the \c System.InvalidCastException + */ +MonoException * +mono_get_exception_invalid_cast () +{ + return mono_exception_from_name (mono_get_corlib (), "System", "InvalidCastException"); +} + +/** + * mono_get_exception_invalid_operation: + * \param msg the message to pass to the user + * \returns a new instance of the \c System.InvalidOperationException + */ +MonoException * +mono_get_exception_invalid_operation (const char *msg) +{ + return mono_exception_from_name_msg (mono_get_corlib (), "System", + "InvalidOperationException", msg); +} + +/** + * mono_get_exception_index_out_of_range: + * \returns a new instance of the \c System.IndexOutOfRangeException + */ +MonoException * +mono_get_exception_index_out_of_range () +{ + return mono_exception_from_name (mono_get_corlib (), "System", + "IndexOutOfRangeException"); +} + +/** + * mono_get_exception_array_type_mismatch: + * \returns a new instance of the \c System.ArrayTypeMismatchException + */ +MonoException * +mono_get_exception_array_type_mismatch () +{ + return mono_exception_from_name (mono_get_corlib (), "System", + "ArrayTypeMismatchException"); +} + +/** + * mono_get_exception_type_load: + * \param class_name the name of the class that could not be loaded + * \param assembly_name the assembly where the class was looked up. + * \returns a new instance of the \c System.TypeLoadException + */ +MonoException * +mono_get_exception_type_load (MonoString *class_name, char *assembly_name) +{ + MonoError error; + MonoString *s = NULL; + if (assembly_name) { + s = mono_string_new_checked (mono_domain_get (), assembly_name, &error); + mono_error_assert_ok (&error); + } else + s = mono_string_empty (mono_domain_get ()); + + MonoException *ret = mono_exception_from_name_two_strings_checked (mono_get_corlib (), "System", + "TypeLoadException", class_name, s, &error); + mono_error_assert_ok (&error); + return ret; +} + +/** + * mono_get_exception_not_implemented: + * \param msg the message to pass to the user + * \returns a new instance of the \c System.NotImplementedException + */ +MonoException * +mono_get_exception_not_implemented (const char *msg) +{ + return mono_exception_from_name_msg (mono_get_corlib (), "System", "NotImplementedException", msg); +} + +/** + * mono_get_exception_not_supported: + * \param msg the message to pass to the user + * \returns a new instance of the \c System.NotSupportedException + */ +MonoException * +mono_get_exception_not_supported (const char *msg) +{ + return mono_exception_from_name_msg (mono_get_corlib (), "System", "NotSupportedException", msg); +} + +/** + * mono_get_exception_missing_method: + * \param class_name the class where the lookup was performed. + * \param member_name the name of the missing method. + * \returns a new instance of the \c System.MissingMethodException + */ +MonoException * +mono_get_exception_missing_method (const char *class_name, const char *member_name) +{ + MonoError error; + MonoString *s1 = mono_string_new_checked (mono_domain_get (), class_name, &error); + mono_error_assert_ok (&error); + MonoString *s2 = mono_string_new_checked (mono_domain_get (), member_name, &error); + mono_error_assert_ok (&error); + + MonoException *ret = mono_exception_from_name_two_strings_checked (mono_get_corlib (), "System", + "MissingMethodException", s1, s2, &error); + mono_error_assert_ok (&error); + return ret; +} + +/** + * mono_get_exception_missing_field: + * \param class_name the class where the lookup was performed + * \param member_name the name of the missing method. + * \returns a new instance of the \c System.MissingFieldException + */ +MonoException * +mono_get_exception_missing_field (const char *class_name, const char *member_name) +{ + MonoError error; + MonoString *s1 = mono_string_new_checked (mono_domain_get (), class_name, &error); + mono_error_assert_ok (&error); + MonoString *s2 = mono_string_new_checked (mono_domain_get (), member_name, &error); + mono_error_assert_ok (&error); + + MonoException *ret = mono_exception_from_name_two_strings_checked (mono_get_corlib (), "System", + "MissingFieldException", s1, s2, &error); + mono_error_assert_ok (&error); + return ret; +} + +/** + * mono_get_exception_argument_null: + * \param arg the name of the argument that is null + * \returns a new instance of the \c System.ArgumentNullException + */ +MonoException* +mono_get_exception_argument_null (const char *arg) +{ + MonoException *ex; + + ex = mono_exception_from_name ( + mono_get_corlib (), "System", "ArgumentNullException"); + + if (arg) { + MonoError error; + MonoArgumentException *argex = (MonoArgumentException *)ex; + MonoString *arg_str = mono_string_new_checked (mono_object_get_domain ((MonoObject*)ex), arg, &error); + mono_error_assert_ok (&error); + MONO_OBJECT_SETREF (argex, param_name, arg_str); + } + + return ex; +} + +/** + * mono_get_exception_argument: + * \param arg the name of the invalid argument. + * \returns a new instance of the \c System.ArgumentException + */ +MonoException * +mono_get_exception_argument (const char *arg, const char *msg) +{ + MonoException *ex; + + ex = mono_exception_from_name_msg ( + mono_get_corlib (), "System", "ArgumentException", msg); + + if (arg) { + MonoError error; + MonoArgumentException *argex = (MonoArgumentException *)ex; + MonoString *arg_str = mono_string_new_checked (mono_object_get_domain ((MonoObject*)ex), arg, &error); + mono_error_assert_ok (&error); + MONO_OBJECT_SETREF (argex, param_name, arg_str); + } + + return ex; +} + +/** + * mono_get_exception_argument_out_of_range: + * \param arg the name of the out of range argument. + * \returns a new instance of the \c System.ArgumentOutOfRangeException + */ +MonoException * +mono_get_exception_argument_out_of_range (const char *arg) +{ + MonoException *ex; + + ex = mono_exception_from_name ( + mono_get_corlib (), "System", "ArgumentOutOfRangeException"); + + if (arg) { + MonoError error; + MonoArgumentException *argex = (MonoArgumentException *)ex; + MonoString *arg_str = mono_string_new_checked (mono_object_get_domain ((MonoObject*)ex), arg, &error); + mono_error_assert_ok (&error); + MONO_OBJECT_SETREF (argex, param_name, arg_str); + } + + return ex; +} + +/** + * mono_get_exception_thread_state: + * \param msg the message to present to the user + * \returns a new instance of the \c System.Threading.ThreadStateException + */ +MonoException * +mono_get_exception_thread_state (const char *msg) +{ + return mono_exception_from_name_msg ( + mono_get_corlib (), "System.Threading", "ThreadStateException", msg); +} + +/** + * mono_get_exception_io: + * \param msg the message to present to the user + * \returns a new instance of the \c System.IO.IOException + */ +MonoException * +mono_get_exception_io (const char *msg) +{ + return mono_exception_from_name_msg ( + mono_get_corlib (), "System.IO", "IOException", msg); +} + +/** + * mono_get_exception_file_not_found: + * \param fname the name of the file not found. + * \returns a new instance of the \c System.IO.FileNotFoundException + */ +MonoException * +mono_get_exception_file_not_found (MonoString *fname) +{ + MonoError error; + MonoException *ret = mono_exception_from_name_two_strings_checked ( + mono_get_corlib (), "System.IO", "FileNotFoundException", fname, fname, &error); + mono_error_assert_ok (&error); + return ret; +} + +/** + * mono_get_exception_file_not_found2: + * \param msg an informative message for the user. + * \param fname the name of the file not found. + * \returns a new instance of the \c System.IO.FileNotFoundException + */ +MonoException * +mono_get_exception_file_not_found2 (const char *msg, MonoString *fname) +{ + MonoError error; + MonoString *s = NULL; + if (msg) { + s = mono_string_new_checked (mono_domain_get (), msg, &error); + mono_error_assert_ok (&error); + } + + MonoException *ret = mono_exception_from_name_two_strings_checked ( + mono_get_corlib (), "System.IO", "FileNotFoundException", s, fname, &error); + mono_error_assert_ok (&error); + return ret; +} + +/** + * mono_get_exception_type_initialization: + * \param type_name the name of the type that failed to initialize. + * \param inner the inner exception. + * \returns a new instance of the \c System.TypeInitializationException + */ +MonoException * +mono_get_exception_type_initialization (const gchar *type_name, MonoException *inner) +{ + MonoError error; + MonoException *ret = mono_get_exception_type_initialization_checked (type_name, inner, &error); + if (!is_ok (&error)) { + mono_error_cleanup (&error); + return NULL; + } + + return ret; +} + +MonoException * +mono_get_exception_type_initialization_checked (const gchar *type_name, MonoException *inner, MonoError *error) +{ + MonoClass *klass; + gpointer args [2]; + MonoObject *exc; + MonoMethod *method; + gpointer iter; + + error_init (error); + + klass = mono_class_load_from_name (mono_get_corlib (), "System", "TypeInitializationException"); + + mono_class_init (klass); + + iter = NULL; + while ((method = mono_class_get_methods (klass, &iter))) { + if (!strcmp (".ctor", mono_method_get_name (method))) { + MonoMethodSignature *sig = mono_method_signature (method); + + if (sig->param_count == 2 && sig->params [0]->type == MONO_TYPE_STRING && mono_class_from_mono_type (sig->params [1]) == mono_defaults.exception_class) + break; + } + method = NULL; + } + g_assert (method); + + MonoString *type_name_str = mono_string_new_checked (mono_domain_get (), type_name, error); + mono_error_assert_ok (error); + args [0] = type_name_str; + args [1] = inner; + + exc = mono_object_new_checked (mono_domain_get (), klass, error); + mono_error_assert_ok (error); + + mono_runtime_invoke_checked (method, exc, args, error); + return_val_if_nok (error, NULL); + + return (MonoException *) exc; +} + +/** + * mono_get_exception_synchronization_lock: + * \param inner the inner exception. + * \returns a new instance of the \c System.SynchronizationLockException + */ +MonoException * +mono_get_exception_synchronization_lock (const char *msg) +{ + return mono_exception_from_name_msg (mono_get_corlib (), "System.Threading", "SynchronizationLockException", msg); +} + +/** + * mono_get_exception_cannot_unload_appdomain: + * \param inner the inner exception. + * \returns a new instance of the \c System.CannotUnloadAppDomainException + */ +MonoException * +mono_get_exception_cannot_unload_appdomain (const char *msg) +{ + return mono_exception_from_name_msg (mono_get_corlib (), "System", "CannotUnloadAppDomainException", msg); +} + +/** + * mono_get_exception_appdomain_unloaded + * \returns a new instance of the \c System.AppDomainUnloadedException + */ +MonoException * +mono_get_exception_appdomain_unloaded (void) +{ + return mono_exception_from_name (mono_get_corlib (), "System", "AppDomainUnloadedException"); +} + +/** + * mono_get_exception_bad_image_format: + * \param msg an informative message for the user. + * \returns a new instance of the \c System.BadImageFormatException + */ +MonoException * +mono_get_exception_bad_image_format (const char *msg) +{ + return mono_exception_from_name_msg (mono_get_corlib (), "System", "BadImageFormatException", msg); +} + +/** + * mono_get_exception_bad_image_format2: + * \param msg an informative message for the user. + * \param fname The full name of the file with the invalid image. + * \returns a new instance of the \c System.BadImageFormatException + */ +MonoException * +mono_get_exception_bad_image_format2 (const char *msg, MonoString *fname) +{ + MonoError error; + MonoString *s = NULL; + + if (msg) { + s = mono_string_new_checked (mono_domain_get (), msg, &error); + mono_error_assert_ok (&error); + } + + MonoException *ret = mono_exception_from_name_two_strings_checked ( + mono_get_corlib (), "System", "BadImageFormatException", s, fname, &error); + mono_error_assert_ok (&error); + return ret; +} + +/** + * mono_get_exception_stack_overflow: + * \returns a new instance of the \c System.StackOverflowException + */ +MonoException * +mono_get_exception_stack_overflow (void) +{ + return mono_exception_from_name (mono_get_corlib (), "System", "StackOverflowException"); +} + +/** + * mono_get_exception_out_of_memory: + * \returns a new instance of the \c System.OutOfMemoryException + */ +MonoException * +mono_get_exception_out_of_memory (void) +{ + return mono_exception_from_name (mono_get_corlib (), "System", "OutOfMemoryException"); +} + +/** + * mono_get_exception_field_access: + * \returns a new instance of the \c System.FieldAccessException + */ +MonoException * +mono_get_exception_field_access (void) +{ + return mono_exception_from_name (mono_get_corlib (), "System", "FieldAccessException"); +} + +/** + * mono_get_exception_field_access2: + * \param msg an informative message for the user. + * \returns a new instance of the \c System.FieldAccessException + */ +MonoException * +mono_get_exception_field_access_msg (const char *msg) +{ + return mono_exception_from_name_msg (mono_get_corlib (), "System", "FieldAccessException", msg); +} + +/** + * mono_get_exception_method_access: + * \returns a new instance of the \c System.MethodAccessException + */ +MonoException * +mono_get_exception_method_access (void) +{ + return mono_exception_from_name (mono_get_corlib (), "System", "MethodAccessException"); +} + +/** + * mono_get_exception_method_access2: + * \param msg an informative message for the user. + * \returns a new instance of the \c System.MethodAccessException + */ +MonoException * +mono_get_exception_method_access_msg (const char *msg) +{ + return mono_exception_from_name_msg (mono_get_corlib (), "System", "MethodAccessException", msg); +} + +/** + * mono_get_exception_reflection_type_load: + * \param types an array of types that were defined in the moduled loaded. + * \param exceptions an array of exceptions that were thrown during the type loading. + * \returns a new instance of the \c System.Reflection.ReflectionTypeLoadException + */ +MonoException * +mono_get_exception_reflection_type_load (MonoArray *types_raw, MonoArray *exceptions_raw) +{ + HANDLE_FUNCTION_ENTER (); + MonoError error; + MONO_HANDLE_DCL (MonoArray, types); + MONO_HANDLE_DCL (MonoArray, exceptions); + MonoExceptionHandle ret = mono_get_exception_reflection_type_load_checked (types, exceptions, &error); + if (is_ok (&error)) { + mono_error_cleanup (&error); + ret = MONO_HANDLE_CAST (MonoException, NULL_HANDLE); + goto leave; + } + +leave: + HANDLE_FUNCTION_RETURN_OBJ (ret); + +} + +MonoExceptionHandle +mono_get_exception_reflection_type_load_checked (MonoArrayHandle types, MonoArrayHandle exceptions, MonoError *error) +{ + MonoClass *klass; + MonoMethod *method; + gpointer iter; + + error_init (error); + + klass = mono_class_load_from_name (mono_get_corlib (), "System.Reflection", "ReflectionTypeLoadException"); + + mono_class_init (klass); + + /* Find the Type[], Exception[] ctor */ + iter = NULL; + while ((method = mono_class_get_methods (klass, &iter))) { + if (!strcmp (".ctor", mono_method_get_name (method))) { + MonoMethodSignature *sig = mono_method_signature (method); + + if (sig->param_count == 2 && sig->params [0]->type == MONO_TYPE_SZARRAY && sig->params [1]->type == MONO_TYPE_SZARRAY) + break; + } + method = NULL; + } + g_assert (method); + + MonoExceptionHandle exc = MONO_HANDLE_NEW (MonoException, mono_object_new_checked (mono_domain_get (), klass, error)); + mono_error_assert_ok (error); + + gpointer args [2]; + args [0] = MONO_HANDLE_RAW (types); + args [1] = MONO_HANDLE_RAW (exceptions); + + mono_runtime_invoke_checked (method, MONO_HANDLE_RAW (exc), args, error); + return_val_if_nok (error, MONO_HANDLE_CAST (MonoException, NULL_HANDLE)); + + return exc; +} + +/** + * mono_get_exception_runtime_wrapped: + */ +MonoException * +mono_get_exception_runtime_wrapped (MonoObject *wrapped_exception) +{ + MonoError error; + MonoException *ret = mono_get_exception_runtime_wrapped_checked (wrapped_exception, &error); + if (!is_ok (&error)) { + mono_error_cleanup (&error); + return NULL; + } + + return ret; +} + +MonoException * +mono_get_exception_runtime_wrapped_checked (MonoObject *wrapped_exception, MonoError *error) +{ + MonoClass *klass; + MonoObject *o; + MonoMethod *method; + MonoDomain *domain = mono_domain_get (); + gpointer params [16]; + + klass = mono_class_load_from_name (mono_get_corlib (), "System.Runtime.CompilerServices", "RuntimeWrappedException"); + + o = mono_object_new_checked (domain, klass, error); + mono_error_assert_ok (error); + g_assert (o != NULL); + + method = mono_class_get_method_from_name (klass, ".ctor", 1); + g_assert (method); + + params [0] = wrapped_exception; + + mono_runtime_invoke_checked (method, o, params, error); + return_val_if_nok (error, NULL); + + return (MonoException *)o; +} + +static gboolean +append_frame_and_continue (MonoMethod *method, gpointer ip, size_t native_offset, gboolean managed, gpointer user_data) +{ + MonoDomain *domain = mono_domain_get (); + GString *text = (GString*)user_data; + + if (method) { + char *msg = mono_debug_print_stack_frame (method, native_offset, domain); + g_string_append_printf (text, "%s\n", msg); + g_free (msg); + } else { + g_string_append_printf (text, "\n", ip); + } + + return FALSE; +} + +char * +mono_exception_get_managed_backtrace (MonoException *exc) +{ + GString *text; + + text = g_string_new_len (NULL, 20); + + if (!mono_get_eh_callbacks ()->mono_exception_walk_trace (exc, append_frame_and_continue, text)) + g_string_append (text, "managed backtrace not available\n"); + + return g_string_free (text, FALSE); +} + +char * +mono_exception_handle_get_native_backtrace (MonoExceptionHandle exc) +{ +#ifdef HAVE_BACKTRACE_SYMBOLS + MonoDomain *domain; + MonoArrayHandle arr = MONO_HANDLE_NEW(MonoArray, NULL); + int i, len; + GString *text; + char **messages; + + MONO_HANDLE_GET (arr, exc, native_trace_ips); + + if (MONO_HANDLE_IS_NULL(arr)) + return g_strdup (""); + domain = mono_domain_get (); + len = mono_array_handle_length (arr); + text = g_string_new_len (NULL, len * 20); + uint32_t gchandle; + void *addr = MONO_ARRAY_HANDLE_PIN (arr, gpointer, 0, &gchandle); + MONO_ENTER_GC_SAFE; + messages = backtrace_symbols (addr, len); + MONO_EXIT_GC_SAFE; + mono_gchandle_free (gchandle); + + for (i = 0; i < len; ++i) { + gpointer ip; + MONO_HANDLE_ARRAY_GETVAL (ip, arr, gpointer, i); + MonoJitInfo *ji = mono_jit_info_table_find (mono_domain_get (), (char *)ip); + if (ji) { + char *msg = mono_debug_print_stack_frame (mono_jit_info_get_method (ji), (char*)ip - (char*)ji->code_start, domain); + g_string_append_printf (text, "%s\n", msg); + g_free (msg); + } else { + g_string_append_printf (text, "%s\n", messages [i]); + } + } + + g_free (messages); + return g_string_free (text, FALSE); +#else + return g_strdup (""); +#endif +} + +MonoStringHandle +ves_icall_Mono_Runtime_GetNativeStackTrace (MonoExceptionHandle exc, MonoError *error) +{ + char *trace; + MonoStringHandle res; + error_init (error); + + if (!exc) { + mono_error_set_argument_null (error, "exception", ""); + return NULL_HANDLE_STRING; + } + + trace = mono_exception_handle_get_native_backtrace (exc); + res = mono_string_new_handle (mono_domain_get (), trace, error); + g_free (trace); + return res; +} + +/** + * mono_error_raise_exception_deprecated: + * \param target_error the exception to raise + * + * Raises the exception of \p target_error. + * Does nothing if \p target_error has a success error code. + * Aborts in case of a double fault. This happens when it can't recover from an error caused by trying + * to construct the first exception object. + * The error object \p target_error is cleaned up. +*/ +void +mono_error_raise_exception_deprecated (MonoError *target_error) +{ + MonoException *ex = mono_error_convert_to_exception (target_error); + if (ex) + mono_raise_exception_deprecated (ex); +} + +/** + * mono_error_set_pending_exception: + * \param error The error + * If \p error is set, convert it to an exception and set the pending exception for the current icall. + * \returns TRUE if \p error was set, or FALSE otherwise, so that you can write: + * if (mono_error_set_pending_exception (error)) { + * { ... cleanup code ... } + * return; + * } + */ +gboolean +mono_error_set_pending_exception (MonoError *error) +{ + MonoException *ex = mono_error_convert_to_exception (error); + if (ex) { + mono_set_pending_exception (ex); + return TRUE; + } else { + return FALSE; + } +} + +void +mono_install_unhandled_exception_hook (MonoUnhandledExceptionFunc func, void *user_data) +{ + unhandled_exception_hook = func; + unhandled_exception_hook_data = user_data; +} + +void +mono_invoke_unhandled_exception_hook (MonoObject *exc) +{ + if (unhandled_exception_hook) { + unhandled_exception_hook (exc, unhandled_exception_hook_data); + } else { + MonoError inner_error; + MonoObject *other = NULL; + MonoString *str = mono_object_try_to_string (exc, &other, &inner_error); + char *msg = NULL; + + if (str && is_ok (&inner_error)) { + msg = mono_string_to_utf8_checked (str, &inner_error); + if (!is_ok (&inner_error)) { + msg = g_strdup_printf ("Nested exception while formatting original exception"); + mono_error_cleanup (&inner_error); + } + } else if (other) { + char *original_backtrace = mono_exception_get_managed_backtrace ((MonoException*)exc); + char *nested_backtrace = mono_exception_get_managed_backtrace ((MonoException*)other); + + msg = g_strdup_printf ("Nested exception detected.\nOriginal Exception: %s\nNested exception:%s\n", + original_backtrace, nested_backtrace); + + g_free (original_backtrace); + g_free (nested_backtrace); + } else { + msg = g_strdup ("Nested exception trying to figure out what went wrong"); + } + mono_runtime_printf_err ("[ERROR] FATAL UNHANDLED EXCEPTION: %s", msg); + g_free (msg); +#if defined(HOST_IOS) + g_assertion_message ("Terminating runtime due to unhandled exception"); +#else + exit (mono_environment_exitcode_get ()); +#endif + } + + g_assert_not_reached (); +} + + +static MonoException * +create_exception_four_strings (MonoClass *klass, MonoString *a1, MonoString *a2, MonoString *a3, MonoString *a4, MonoError *error) +{ + MonoDomain *domain = mono_domain_get (); + MonoMethod *method = NULL; + MonoObject *o; + int count = 4; + gpointer args [4]; + gpointer iter; + MonoMethod *m; + + o = mono_object_new_checked (domain, klass, error); + mono_error_assert_ok (error); + + iter = NULL; + while ((m = mono_class_get_methods (klass, &iter))) { + MonoMethodSignature *sig; + + if (strcmp (".ctor", mono_method_get_name (m))) + continue; + sig = mono_method_signature (m); + if (sig->param_count != count) + continue; + + int i; + gboolean good = TRUE; + for (i = 0; i < count; ++i) { + if (sig->params [i]->type != MONO_TYPE_STRING) { + good = FALSE; + break; + } + } + if (good) { + method = m; + break; + } + } + + g_assert (method); + + args [0] = a1; + args [1] = a2; + args [2] = a3; + args [3] = a4; + + mono_runtime_invoke_checked (method, o, args, error); + return_val_if_nok (error, NULL); + + return (MonoException *) o; +} + +MonoException * +mono_exception_from_name_four_strings_checked (MonoImage *image, const char *name_space, + const char *name, MonoString *a1, MonoString *a2, MonoString *a3, MonoString *a4, + MonoError *error) +{ + MonoClass *klass; + + error_init (error); + klass = mono_class_load_from_name (image, name_space, name); + + 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/unity-2019.4.24f1-mbe/mono/metadata/exception.h b/unity-2019.4.24f1-mbe/mono/metadata/exception.h new file mode 100644 index 000000000..f53a2c8f4 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/exception.h @@ -0,0 +1,169 @@ +/** + * \file + */ + +#ifndef _MONO_METADATA_EXCEPTION_H_ +#define _MONO_METADATA_EXCEPTION_H_ + +#include +#include + +MONO_BEGIN_DECLS + +extern MONO_API MonoException * +mono_exception_from_name (MonoImage *image, + const char* name_space, + const char *name); + +MONO_API MonoException * +mono_exception_from_token (MonoImage *image, uint32_t token); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoException * +mono_exception_from_name_two_strings (MonoImage *image, const char *name_space, + const char *name, MonoString *a1, MonoString *a2); + +MONO_API MonoException * +mono_exception_from_name_msg (MonoImage *image, const char *name_space, + const char *name, const char *msg); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoException * +mono_exception_from_token_two_strings (MonoImage *image, uint32_t token, + MonoString *a1, MonoString *a2); + +extern MONO_API MonoException * +mono_exception_from_name_domain (MonoDomain *domain, MonoImage *image, + const char* name_space, + const char *name); + +MONO_API MonoException * +mono_get_exception_divide_by_zero (void); + +MONO_API MonoException * +mono_get_exception_security (void); + +MONO_API MonoException * +mono_get_exception_arithmetic (void); + +MONO_API MonoException * +mono_get_exception_overflow (void); + +MONO_API MonoException * +mono_get_exception_null_reference (void); + +MONO_API MonoException * +mono_get_exception_execution_engine (const char *msg); + +MONO_API MonoException * +mono_get_exception_thread_abort (void); + +MONO_API MonoException * +mono_get_exception_thread_state (const char *msg); + +MONO_API MonoException * +mono_get_exception_thread_interrupted (void); + +MONO_API MonoException * +mono_get_exception_serialization (const char *msg); + +MONO_API MonoException * +mono_get_exception_invalid_cast (void); + +MONO_API MonoException * +mono_get_exception_invalid_operation (const char *msg); + +MONO_API MonoException * +mono_get_exception_index_out_of_range (void); + +MONO_API MonoException * +mono_get_exception_array_type_mismatch (void); + +MONO_API MonoException * +mono_get_exception_type_load (MonoString *class_name, char *assembly_name); + +MONO_API MonoException * +mono_get_exception_missing_method (const char *class_name, const char *member_name); + +MONO_API MonoException * +mono_get_exception_missing_field (const char *class_name, const char *member_name); + +MONO_API MonoException * +mono_get_exception_not_implemented (const char *msg); + +MONO_API MonoException * +mono_get_exception_not_supported (const char *msg); + +MONO_API MonoException* +mono_get_exception_argument_null (const char *arg); + +MONO_API MonoException * +mono_get_exception_argument (const char *arg, const char *msg); + +MONO_API MonoException * +mono_get_exception_argument_out_of_range (const char *arg); + +MONO_API MonoException * +mono_get_exception_io (const char *msg); + +MONO_API MonoException * +mono_get_exception_file_not_found (MonoString *fname); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoException * +mono_get_exception_file_not_found2 (const char *msg, MonoString *fname); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoException * +mono_get_exception_type_initialization (const char *type_name, MonoException *inner); + +MONO_API MonoException * +mono_get_exception_synchronization_lock (const char *msg); + +MONO_API MonoException * +mono_get_exception_cannot_unload_appdomain (const char *msg); + +MONO_API MonoException * +mono_get_exception_appdomain_unloaded (void); + +MONO_API MonoException * +mono_get_exception_bad_image_format (const char *msg); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoException * +mono_get_exception_bad_image_format2 (const char *msg, MonoString *fname); + +MONO_API MonoException * +mono_get_exception_stack_overflow (void); + +MONO_API MonoException * +mono_get_exception_out_of_memory (void); + +MONO_API MonoException * +mono_get_exception_field_access (void); + +MONO_API MonoException * +mono_get_exception_method_access (void); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoException * +mono_get_exception_reflection_type_load (MonoArray *types, MonoArray *exceptions); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoException * +mono_get_exception_runtime_wrapped (MonoObject *wrapped_exception); + +/* Installs a function which is called when the runtime encounters an unhandled exception. + * This hook isn't expected to return. + * If no hook has been installed, the runtime will print a message before aborting. + */ +typedef void (*MonoUnhandledExceptionFunc) (MonoObject *exc, void *user_data); +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/unity-2019.4.24f1-mbe/mono/metadata/fdhandle.c b/unity-2019.4.24f1-mbe/mono/metadata/fdhandle.c new file mode 100644 index 000000000..39e531b51 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/fdhandle.c @@ -0,0 +1,142 @@ + +#include "fdhandle.h" +#include "utils/mono-lazy-init.h" +#include "utils/mono-coop-mutex.h" + +static GHashTable *fds; +static MonoCoopMutex fds_mutex; +static MonoFDHandleCallback fds_callback[MONO_FDTYPE_COUNT]; +static mono_lazy_init_t fds_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED; + +static const gchar *types_str[] = { + "File", + "Console", + "Pipe", + "Socket", + NULL +}; + +static void +fds_remove (gpointer data) +{ + MonoFDHandle* fdhandle; + + fdhandle = (MonoFDHandle*) data; + g_assert (fdhandle); + + g_assert (fds_callback [fdhandle->type].close); + fds_callback [fdhandle->type].close (fdhandle); + + mono_refcount_dec (fdhandle); +} + +static void +initialize (void) +{ + fds = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, fds_remove); + mono_coop_mutex_init (&fds_mutex); +} + +void +mono_fdhandle_register (MonoFDType type, MonoFDHandleCallback *callback) +{ + mono_lazy_initialize (&fds_init, initialize); + memcpy (&fds_callback [type], callback, sizeof (MonoFDHandleCallback)); +} + +static void +fdhandle_destroy (gpointer data) +{ + MonoFDHandle* fdhandle; + + fdhandle = (MonoFDHandle*) data; + g_assert (fdhandle); + + g_assert (fds_callback [fdhandle->type].destroy); + fds_callback [fdhandle->type].destroy (fdhandle); +} + +void +mono_fdhandle_init (MonoFDHandle *fdhandle, MonoFDType type, gint fd) +{ + mono_refcount_init (fdhandle, fdhandle_destroy); + fdhandle->type = type; + fdhandle->fd = fd; +} + +void +mono_fdhandle_insert (MonoFDHandle *fdhandle) +{ + mono_coop_mutex_lock (&fds_mutex); + + if (g_hash_table_lookup_extended (fds, GINT_TO_POINTER(fdhandle->fd), NULL, NULL)) + g_error("%s: duplicate %s fd %d", __func__, types_str [fdhandle->type], fdhandle->fd); + + g_hash_table_insert (fds, GINT_TO_POINTER(fdhandle->fd), fdhandle); + + mono_coop_mutex_unlock (&fds_mutex); +} + +gboolean +mono_fdhandle_try_insert (MonoFDHandle *fdhandle) +{ + mono_coop_mutex_lock (&fds_mutex); + + if (g_hash_table_lookup_extended (fds, GINT_TO_POINTER(fdhandle->fd), NULL, NULL)) { + /* we raced between 2 invocations of mono_fdhandle_try_insert */ + mono_coop_mutex_unlock (&fds_mutex); + + return FALSE; + } + + g_hash_table_insert (fds, GINT_TO_POINTER(fdhandle->fd), fdhandle); + + mono_coop_mutex_unlock (&fds_mutex); + + return TRUE; +} + +gboolean +mono_fdhandle_lookup_and_ref (gint fd, MonoFDHandle **fdhandle) +{ + mono_coop_mutex_lock (&fds_mutex); + + if (!g_hash_table_lookup_extended (fds, GINT_TO_POINTER(fd), NULL, (gpointer*) fdhandle)) { + mono_coop_mutex_unlock (&fds_mutex); + return FALSE; + } + + mono_refcount_inc (*fdhandle); + + mono_coop_mutex_unlock (&fds_mutex); + + return TRUE; +} + +void +mono_fdhandle_unref (MonoFDHandle *fdhandle) +{ + mono_refcount_dec (fdhandle); +} + +gboolean +mono_fdhandle_close (gint fd) +{ + MonoFDHandle *fdhandle; + gboolean removed; + + mono_coop_mutex_lock (&fds_mutex); + + if (!g_hash_table_lookup_extended (fds, GINT_TO_POINTER(fd), NULL, (gpointer*) &fdhandle)) { + mono_coop_mutex_unlock (&fds_mutex); + + return FALSE; + } + + removed = g_hash_table_remove (fds, GINT_TO_POINTER(fdhandle->fd)); + g_assert (removed); + + mono_coop_mutex_unlock (&fds_mutex); + + return TRUE; +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/fdhandle.h b/unity-2019.4.24f1-mbe/mono/metadata/fdhandle.h new file mode 100644 index 000000000..e3d7e5b9b --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/fdhandle.h @@ -0,0 +1,50 @@ + +#ifndef __MONO_METADATA_FDHANDLE_H__ +#define __MONO_METADATA_FDHANDLE_H__ + +#include +#include + +#include "utils/refcount.h" + +typedef enum { + MONO_FDTYPE_FILE, + MONO_FDTYPE_CONSOLE, + MONO_FDTYPE_PIPE, + MONO_FDTYPE_SOCKET, + MONO_FDTYPE_COUNT +} MonoFDType; + +typedef struct { + MonoRefCount ref; + MonoFDType type; + gint fd; +} MonoFDHandle; + +typedef struct { + void (*close) (MonoFDHandle *fdhandle); + void (*destroy) (MonoFDHandle *fdhandle); +} MonoFDHandleCallback; + +void +mono_fdhandle_register (MonoFDType type, MonoFDHandleCallback *callback); + +void +mono_fdhandle_init (MonoFDHandle *fdhandle, MonoFDType type, gint fd); + +void +mono_fdhandle_insert (MonoFDHandle *fdhandle); + +gboolean +mono_fdhandle_try_insert (MonoFDHandle *fdhandle); + +gboolean +mono_fdhandle_lookup_and_ref (gint fd, MonoFDHandle **fdhandle); + +void +mono_fdhandle_unref (MonoFDHandle *fdhandle); + +gboolean +mono_fdhandle_close (gint fd); + +#endif /* __MONO_METADATA_FDHANDLE_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/file-mmap-posix.c b/unity-2019.4.24f1-mbe/mono/metadata/file-mmap-posix.c new file mode 100644 index 000000000..adddf8b76 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/file-mmap-posix.c @@ -0,0 +1,552 @@ +/** + * \file + * File mmap internal calls + * + * Author: + * Rodrigo Kumpera + * + * Copyright 2014 Xamarin Inc (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include + +#ifndef HOST_WIN32 + +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#if HAVE_SYS_MMAN_H +#include +#endif + +#include + + +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + int kind; + int ref_count; + size_t capacity; + char *name; + int fd; +} MmapHandle; + +typedef struct { + void *address; + void *free_handle; + 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 +}; + +enum { + FILE_MODE_CREATE_NEW = 1, + FILE_MODE_CREATE = 2, + FILE_MODE_OPEN = 3, + FILE_MODE_OPEN_OR_CREATE = 4, + FILE_MODE_TRUNCATE = 5, + FILE_MODE_APPEND = 6, +}; + +enum { + MMAP_FILE_ACCESS_READ_WRITE = 0, + MMAP_FILE_ACCESS_READ = 1, + MMAP_FILE_ACCESS_WRITE = 2, + MMAP_FILE_ACCESS_COPY_ON_WRITE = 3, + MMAP_FILE_ACCESS_READ_EXECUTE = 4, + MMAP_FILE_ACCESS_READ_WRITE_EXECUTE = 5, +}; + +#ifdef DEFFILEMODE +#define DEFAULT_FILEMODE DEFFILEMODE +#else +#define DEFAULT_FILEMODE 0666 +#endif + +static int mmap_init_state; +static MonoCoopMutex named_regions_mutex; +static GHashTable *named_regions; + + +static gint64 +align_up_to_page_size (gint64 size) +{ + gint64 page_size = mono_pagesize (); + return (size + page_size - 1) & ~(page_size - 1); +} + +static gint64 +align_down_to_page_size (gint64 size) +{ + gint64 page_size = mono_pagesize (); + return size & ~(page_size - 1); +} + +static void +file_mmap_init (void) +{ +retry: + switch (mmap_init_state) { + case 0: + if (mono_atomic_cas_i32 (&mmap_init_state, 1, 0) != 0) + goto retry; + named_regions = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); + mono_coop_mutex_init (&named_regions_mutex); + + mono_atomic_store_release (&mmap_init_state, 2); + break; + + case 1: + do { + mono_thread_info_sleep (1, NULL); /* Been init'd by other threads, this is very rare. */ + } while (mmap_init_state != 2); + break; + case 2: + break; + default: + g_error ("Invalid init state %d", mmap_init_state); + } +} + +static void +named_regions_lock (void) +{ + file_mmap_init (); + mono_coop_mutex_lock (&named_regions_mutex); +} + +static void +named_regions_unlock (void) +{ + mono_coop_mutex_unlock (&named_regions_mutex); +} + + +static int +file_mode_to_unix (int mode) +{ + switch (mode) { + case FILE_MODE_CREATE_NEW: + return O_CREAT | O_EXCL; + case FILE_MODE_CREATE: + return O_CREAT | O_TRUNC; + case FILE_MODE_OPEN: + return 0; + case FILE_MODE_OPEN_OR_CREATE: + return O_CREAT; + case FILE_MODE_TRUNCATE: + return O_TRUNC; + case FILE_MODE_APPEND: + return O_APPEND; + default: + g_error ("unknown FileMode %d", mode); + } +} + +static int +access_mode_to_unix (int access) +{ + switch (access) { + case MMAP_FILE_ACCESS_READ_WRITE: + case MMAP_FILE_ACCESS_COPY_ON_WRITE: + case MMAP_FILE_ACCESS_READ_WRITE_EXECUTE: + return O_RDWR; + case MMAP_FILE_ACCESS_READ: + case MMAP_FILE_ACCESS_READ_EXECUTE: + return O_RDONLY; + case MMAP_FILE_ACCESS_WRITE: + return O_WRONLY; + default: + g_error ("unknown MemoryMappedFileAccess %d", access); + } +} + +static int +acess_to_mmap_flags (int access) +{ + switch (access) { + case MMAP_FILE_ACCESS_READ_WRITE: + return MONO_MMAP_WRITE | MONO_MMAP_READ | MONO_MMAP_SHARED; + + case MMAP_FILE_ACCESS_WRITE: + return MONO_MMAP_WRITE | MONO_MMAP_SHARED; + + case MMAP_FILE_ACCESS_COPY_ON_WRITE: + return MONO_MMAP_WRITE | MONO_MMAP_READ | MONO_MMAP_PRIVATE; + + case MMAP_FILE_ACCESS_READ_EXECUTE: + return MONO_MMAP_EXEC | MONO_MMAP_PRIVATE | MONO_MMAP_SHARED; + + case MMAP_FILE_ACCESS_READ_WRITE_EXECUTE: + return MONO_MMAP_WRITE | MONO_MMAP_READ | MONO_MMAP_EXEC | MONO_MMAP_SHARED; + + case MMAP_FILE_ACCESS_READ: + return MONO_MMAP_READ | MONO_MMAP_SHARED; + default: + g_error ("unknown MemoryMappedFileAccess %d", access); + } +} + +/* +This allow us to special case zero size files that can be arbitrarily mapped. +*/ +static gboolean +is_special_zero_size_file (struct stat *buf) +{ + return buf->st_size == 0 && (buf->st_mode & (S_IFCHR | S_IFBLK | S_IFIFO | S_IFSOCK)) != 0; +} + +/* +XXX implement options +*/ +static void* +open_file_map (const char *c_path, int input_fd, int mode, gint64 *capacity, int access, int options, int *ioerror) +{ + struct stat buf; + MmapHandle *handle = NULL; + int result, fd; + + if (c_path) + result = stat (c_path, &buf); + else + result = fstat (input_fd, &buf); + + if (mode == FILE_MODE_TRUNCATE || mode == FILE_MODE_APPEND || mode == FILE_MODE_OPEN) { + if (result == -1) { //XXX translate errno? + *ioerror = FILE_NOT_FOUND; + goto done; + } + } + + if (mode == FILE_MODE_CREATE_NEW && result == 0) { + *ioerror = FILE_ALREADY_EXISTS; + goto done; + } + + if (result == 0) { + if (*capacity == 0) { + /** + * Special files such as FIFOs, sockets, and devices can have a size of 0. Specifying a capacity for these + * also makes little sense, so don't do the check if th file is one of these. + */ + if (buf.st_size == 0 && !is_special_zero_size_file (&buf)) { + *ioerror = CAPACITY_SMALLER_THAN_FILE_SIZE; + goto done; + } + *capacity = buf.st_size; + } else if (*capacity < buf.st_size) { + *ioerror = CAPACITY_SMALLER_THAN_FILE_SIZE; + goto done; + } + } else { + if (mode == FILE_MODE_CREATE_NEW && *capacity == 0) { + *ioerror = CAPACITY_SMALLER_THAN_FILE_SIZE; + goto done; + } + } + + if (c_path) //FIXME use io portability? + fd = open (c_path, file_mode_to_unix (mode) | access_mode_to_unix (access), DEFAULT_FILEMODE); + else + fd = dup (input_fd); + + if (fd == -1) { //XXX translate errno? + *ioerror = COULD_NOT_OPEN; + goto done; + } + + if (result != 0 || *capacity > buf.st_size) { + int unused G_GNUC_UNUSED = ftruncate (fd, (off_t)*capacity); + } + + handle = g_new0 (MmapHandle, 1); + handle->ref_count = 1; + handle->capacity = *capacity; + handle->fd = fd; + +done: + return (void*)handle; +} + +#define MONO_ANON_FILE_TEMPLATE "/mono.anonmap.XXXXXXXXX" +static void* +open_memory_map (const char *c_mapName, int mode, gint64 *capacity, int access, int options, int *ioerror) +{ + MmapHandle *handle; + if (*capacity <= 0 && mode != FILE_MODE_OPEN) { + *ioerror = CAPACITY_MUST_BE_POSITIVE; + return NULL; + } +#if SIZEOF_VOID_P == 4 + if (*capacity > UINT32_MAX) { + *ioerror = CAPACITY_LARGER_THAN_LOGICAL_ADDRESS_SPACE; + return NULL; + } +#endif + + if (!(mode == FILE_MODE_CREATE_NEW || mode == FILE_MODE_OPEN_OR_CREATE || mode == FILE_MODE_OPEN)) { + *ioerror = INVALID_FILE_MODE; + return NULL; + } + + named_regions_lock (); + handle = (MmapHandle*)g_hash_table_lookup (named_regions, c_mapName); + if (handle) { + if (mode == FILE_MODE_CREATE_NEW) { + *ioerror = FILE_ALREADY_EXISTS; + goto done; + } + + handle->ref_count++; + //XXX should we ftruncate if the file is smaller than capacity? + } else { + int fd; + char *file_name; + const char *tmp_dir; + int unused G_GNUC_UNUSED, alloc_size; + + if (mode == FILE_MODE_OPEN) { + *ioerror = FILE_NOT_FOUND; + goto done; + } + *capacity = align_up_to_page_size (*capacity); + + tmp_dir = g_get_tmp_dir (); + alloc_size = strlen (tmp_dir) + strlen (MONO_ANON_FILE_TEMPLATE) + 1; + if (alloc_size > 1024) {//rather fail that stack overflow + *ioerror = COULD_NOT_MAP_MEMORY; + goto done; + } + file_name = (char *)alloca (alloc_size); + strcpy (file_name, tmp_dir); + strcat (file_name, MONO_ANON_FILE_TEMPLATE); + + fd = mkstemp (file_name); + if (fd == -1) { + *ioerror = COULD_NOT_MAP_MEMORY; + goto done; + } + + unlink (file_name); + unused = ftruncate (fd, (off_t)*capacity); + + handle = g_new0 (MmapHandle, 1); + handle->ref_count = 1; + handle->capacity = *capacity; + handle->fd = fd; + handle->name = g_strdup (c_mapName); + + g_hash_table_insert (named_regions, handle->name, handle); + + } + +done: + named_regions_unlock (); + + return handle; +} + + +/* This is an icall */ +void * +mono_mmap_open_file (MonoString *path, int mode, MonoString *mapName, gint64 *capacity, int access, int options, int *ioerror) +{ + MonoError error; + MmapHandle *handle = NULL; + g_assert (path || mapName); + + if (!mapName) { + char * c_path = mono_string_to_utf8_checked (path, &error); + if (mono_error_set_pending_exception (&error)) + return NULL; + handle = open_file_map (c_path, -1, mode, capacity, access, options, ioerror); + g_free (c_path); + return handle; + } + + char *c_mapName = mono_string_to_utf8_checked (mapName, &error); + if (mono_error_set_pending_exception (&error)) + return NULL; + + if (path) { + named_regions_lock (); + handle = (MmapHandle*)g_hash_table_lookup (named_regions, c_mapName); + if (handle) { + *ioerror = FILE_ALREADY_EXISTS; + handle = NULL; + } else { + char *c_path = mono_string_to_utf8_checked (path, &error); + if (is_ok (&error)) { + handle = (MmapHandle *)open_file_map (c_path, -1, mode, capacity, access, options, ioerror); + if (handle) { + handle->name = g_strdup (c_mapName); + g_hash_table_insert (named_regions, handle->name, handle); + } + } else { + handle = NULL; + } + g_free (c_path); + } + named_regions_unlock (); + } else + handle = open_memory_map (c_mapName, mode, capacity, access, options, ioerror); + + g_free (c_mapName); + return handle; +} + +/* this is an icall */ +void * +mono_mmap_open_handle (void *input_fd, MonoString *mapName, gint64 *capacity, int access, int options, int *ioerror) +{ + MonoError error; + MmapHandle *handle; + if (!mapName) { + handle = (MmapHandle *)open_file_map (NULL, GPOINTER_TO_INT (input_fd), FILE_MODE_OPEN, capacity, access, options, ioerror); + } else { + char *c_mapName = mono_string_to_utf8_checked (mapName, &error); + if (mono_error_set_pending_exception (&error)) + return NULL; + + named_regions_lock (); + handle = (MmapHandle*)g_hash_table_lookup (named_regions, c_mapName); + if (handle) { + *ioerror = FILE_ALREADY_EXISTS; + handle = NULL; + } else { + //XXX we're exploiting wapi HANDLE == FD equivalence. THIS IS FRAGILE, create a _wapi_handle_to_fd call + handle = (MmapHandle *)open_file_map (NULL, GPOINTER_TO_INT (input_fd), FILE_MODE_OPEN, capacity, access, options, ioerror); + handle->name = g_strdup (c_mapName); + g_hash_table_insert (named_regions, handle->name, handle); + } + named_regions_unlock (); + + g_free (c_mapName); + } + return handle; +} + +void +mono_mmap_close (void *mmap_handle) +{ + MmapHandle *handle = (MmapHandle *)mmap_handle; + + named_regions_lock (); + --handle->ref_count; + if (handle->ref_count == 0) { + if (handle->name) + g_hash_table_remove (named_regions, handle->name); + + g_free (handle->name); + close (handle->fd); + g_free (handle); + } + named_regions_unlock (); +} + +void +mono_mmap_configure_inheritability (void *mmap_handle, gboolean inheritability) +{ + MmapHandle *h = (MmapHandle *)mmap_handle; + int fd, flags; + + fd = h->fd; + flags = fcntl (fd, F_GETFD, 0); + if (inheritability) + flags &= ~FD_CLOEXEC; + else + flags |= FD_CLOEXEC; + fcntl (fd, F_SETFD, flags); +} + +void +mono_mmap_flush (void *mmap_handle) +{ + MmapInstance *h = (MmapInstance *)mmap_handle; + + if (h) + msync (h->address, h->length, MS_SYNC); +} + +int +mono_mmap_map (void *handle, gint64 offset, gint64 *size, int access, void **mmap_handle, void **base_address) +{ + gint64 mmap_offset = 0; + MmapHandle *fh = (MmapHandle *)handle; + MmapInstance res = { 0 }; + size_t eff_size = *size; + struct stat buf = { 0 }; + fstat (fh->fd, &buf); //FIXME error handling + + *mmap_handle = NULL; + *base_address = NULL; + + if (offset > buf.st_size || ((eff_size + offset) > buf.st_size && !is_special_zero_size_file (&buf))) + return ACCESS_DENIED; + /** + * We use the file size if one of the following conditions is true: + * -input size is zero + * -input size is bigger than the file and the file is not a magical zero size file such as /dev/mem. + */ + if (eff_size == 0) + eff_size = align_up_to_page_size (buf.st_size) - offset; + *size = eff_size; + + mmap_offset = align_down_to_page_size (offset); + eff_size += (offset - mmap_offset); + //FIXME translate some interesting errno values + res.address = mono_file_map ((size_t)eff_size, acess_to_mmap_flags (access), fh->fd, mmap_offset, &res.free_handle); + res.length = eff_size; + + if (res.address) { + *mmap_handle = g_memdup (&res, sizeof (MmapInstance)); + *base_address = (char*)res.address + (offset - mmap_offset); + return 0; + } + + return COULD_NOT_MAP_MEMORY; +} + +gboolean +mono_mmap_unmap (void *mmap_handle) +{ + int res = 0; + MmapInstance *h = (MmapInstance *)mmap_handle; + + res = mono_file_unmap (h->address, h->free_handle); + + g_free (h); + return res == 0; +} + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/file-mmap-unity.c b/unity-2019.4.24f1-mbe/mono/metadata/file-mmap-unity.c new file mode 100644 index 000000000..cc338f107 --- /dev/null +++ b/unity-2019.4.24f1-mbe/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/unity-2019.4.24f1-mbe/mono/metadata/file-mmap-windows.c b/unity-2019.4.24f1-mbe/mono/metadata/file-mmap-windows.c new file mode 100644 index 000000000..59abd6686 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/file-mmap-windows.c @@ -0,0 +1,421 @@ +/** + * \file + * MemoryMappedFile internal calls for Windows + * + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +/* + * The code in this file has been inspired by the CoreFX MemoryMappedFile Windows implementation contained in the files + * + * https://github.com/dotnet/corefx/blob/master/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.Windows.cs + * https://github.com/dotnet/corefx/blob/master/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.Windows.cs + */ + +#include +#include +#include +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) && defined(HOST_WIN32) + +#include + +#include + +// These control the retry behaviour when lock violation errors occur during Flush: +#define MAX_FLUSH_WAITS 15 // must be <=30 +#define MAX_FLUSH_RETIRES_PER_WAIT 20 + +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 +}; + +enum { + FILE_MODE_CREATE_NEW = 1, + FILE_MODE_CREATE = 2, + FILE_MODE_OPEN = 3, + FILE_MODE_OPEN_OR_CREATE = 4, + FILE_MODE_TRUNCATE = 5, + FILE_MODE_APPEND = 6, +}; + +enum { + MMAP_FILE_ACCESS_READ_WRITE = 0, + MMAP_FILE_ACCESS_READ = 1, + MMAP_FILE_ACCESS_WRITE = 2, + MMAP_FILE_ACCESS_COPY_ON_WRITE = 3, + MMAP_FILE_ACCESS_READ_EXECUTE = 4, + MMAP_FILE_ACCESS_READ_WRITE_EXECUTE = 5, +}; + +static DWORD get_page_access (int access) +{ + switch (access) { + case MMAP_FILE_ACCESS_READ: + return PAGE_READONLY; + case MMAP_FILE_ACCESS_READ_WRITE: + return PAGE_READWRITE; + case MMAP_FILE_ACCESS_COPY_ON_WRITE: + return PAGE_WRITECOPY; + case MMAP_FILE_ACCESS_READ_EXECUTE: + return PAGE_EXECUTE_READ; + case MMAP_FILE_ACCESS_READ_WRITE_EXECUTE: + return PAGE_EXECUTE_READWRITE; + default: + g_error ("unknown MemoryMappedFileAccess %d", access); + } +} + +static DWORD get_file_access (int access) +{ + switch (access) { + case MMAP_FILE_ACCESS_READ: + case MMAP_FILE_ACCESS_READ_EXECUTE: + return GENERIC_READ; + case MMAP_FILE_ACCESS_READ_WRITE: + case MMAP_FILE_ACCESS_COPY_ON_WRITE: + case MMAP_FILE_ACCESS_READ_WRITE_EXECUTE: + return GENERIC_READ | GENERIC_WRITE; + case MMAP_FILE_ACCESS_WRITE: + return GENERIC_WRITE; + default: + g_error ("unknown MemoryMappedFileAccess %d", access); + } +} + +static int get_file_map_access (int access) +{ + switch (access) { + case MMAP_FILE_ACCESS_READ: + return FILE_MAP_READ; + case MMAP_FILE_ACCESS_WRITE: + return FILE_MAP_WRITE; + case MMAP_FILE_ACCESS_READ_WRITE: + return FILE_MAP_READ | FILE_MAP_WRITE; + case MMAP_FILE_ACCESS_COPY_ON_WRITE: + return FILE_MAP_COPY; + case MMAP_FILE_ACCESS_READ_EXECUTE: + return FILE_MAP_EXECUTE | FILE_MAP_READ; + case MMAP_FILE_ACCESS_READ_WRITE_EXECUTE: + return FILE_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_WRITE; + default: + g_error ("unknown MemoryMappedFileAccess %d", access); + } +} + +static int convert_win32_error (int error, int def) +{ + switch (error) { + case ERROR_FILE_NOT_FOUND: + return FILE_NOT_FOUND; + case ERROR_FILE_EXISTS: + case ERROR_ALREADY_EXISTS: + return FILE_ALREADY_EXISTS; + case ERROR_ACCESS_DENIED: + return ACCESS_DENIED; + } + return def; +} + +static void *open_handle (void *handle, MonoString *mapName, int mode, gint64 *capacity, int access, int options, int *error) +{ + g_assert (handle != NULL); + + wchar_t *w_mapName = NULL; + HANDLE result = NULL; + + if (handle == INVALID_HANDLE_VALUE) { + if (*capacity <= 0 && mode != FILE_MODE_OPEN) { + *error = CAPACITY_MUST_BE_POSITIVE; + return NULL; + } +#if SIZEOF_VOID_P == 4 + if (*capacity > UINT32_MAX) { + *error = CAPACITY_LARGER_THAN_LOGICAL_ADDRESS_SPACE; + return NULL; + } +#endif + if (!(mode == FILE_MODE_CREATE_NEW || mode == FILE_MODE_OPEN_OR_CREATE || mode == FILE_MODE_OPEN)) { + *error = INVALID_FILE_MODE; + return NULL; + } + } else { + FILE_STANDARD_INFO info; + if (!GetFileInformationByHandleEx ((HANDLE) handle, FileStandardInfo, &info, sizeof (FILE_STANDARD_INFO))) { + *error = convert_win32_error (GetLastError (), COULD_NOT_OPEN); + return NULL; + } + if (*capacity == 0) { + if (info.EndOfFile.QuadPart == 0) { + *error = CAPACITY_SMALLER_THAN_FILE_SIZE; + return NULL; + } + } else if (*capacity < info.EndOfFile.QuadPart) { + *error = CAPACITY_SMALLER_THAN_FILE_SIZE; + return NULL; + } + } + + w_mapName = mapName ? mono_string_to_utf16 (mapName) : NULL; + + if (mode == FILE_MODE_CREATE_NEW || handle != INVALID_HANDLE_VALUE) { + result = CreateFileMappingW ((HANDLE)handle, NULL, get_page_access (access) | options, (DWORD)(((guint64)*capacity) >> 32), (DWORD)*capacity, w_mapName); + if (result && GetLastError () == ERROR_ALREADY_EXISTS) { + CloseHandle (result); + result = NULL; + *error = FILE_ALREADY_EXISTS; + } else if (!result && GetLastError () != NO_ERROR) { + *error = convert_win32_error (GetLastError (), COULD_NOT_OPEN); + } + } else if (mode == FILE_MODE_OPEN || mode == FILE_MODE_OPEN_OR_CREATE && access == MMAP_FILE_ACCESS_WRITE) { + result = OpenFileMappingW (get_file_map_access (access), FALSE, w_mapName); + if (!result) { + if (mode == FILE_MODE_OPEN_OR_CREATE && GetLastError () == ERROR_FILE_NOT_FOUND) { + *error = INVALID_FILE_MODE; + } else { + *error = convert_win32_error (GetLastError (), COULD_NOT_OPEN); + } + } + } else if (mode == FILE_MODE_OPEN_OR_CREATE) { + + // This replicates how CoreFX does MemoryMappedFile.CreateOrOpen (). + + /// Try to open the file if it exists -- this requires a bit more work. Loop until we can + /// either create or open a memory mapped file up to a timeout. CreateFileMapping may fail + /// if the file exists and we have non-null security attributes, in which case we need to + /// use OpenFileMapping. But, there exists a race condition because the memory mapped file + /// may have closed between the two calls -- hence the loop. + /// + /// The retry/timeout logic increases the wait time each pass through the loop and times + /// out in approximately 1.4 minutes. If after retrying, a MMF handle still hasn't been opened, + /// throw an InvalidOperationException. + + guint32 waitRetries = 14; //((2^13)-1)*10ms == approximately 1.4mins + guint32 waitSleep = 0; + + while (waitRetries > 0) { + result = CreateFileMappingW ((HANDLE)handle, NULL, get_page_access (access) | options, (DWORD)(((guint64)*capacity) >> 32), (DWORD)*capacity, w_mapName); + if (result) + break; + if (GetLastError() != ERROR_ACCESS_DENIED) { + *error = convert_win32_error (GetLastError (), COULD_NOT_OPEN); + break; + } + result = OpenFileMappingW (get_file_map_access (access), FALSE, w_mapName); + if (result) + break; + if (GetLastError () != ERROR_FILE_NOT_FOUND) { + *error = convert_win32_error (GetLastError (), COULD_NOT_OPEN); + break; + } + // increase wait time + --waitRetries; + if (waitSleep == 0) { + waitSleep = 10; + } else { + mono_thread_info_sleep (waitSleep, NULL); + waitSleep *= 2; + } + } + + if (!result) { + *error = COULD_NOT_OPEN; + } + } + + if (w_mapName) + g_free (w_mapName); + return result; +} + +void *mono_mmap_open_file (MonoString *path, int mode, MonoString *mapName, gint64 *capacity, int access, int options, int *error) +{ + g_assert (path != NULL || mapName != NULL); + + wchar_t *w_path = NULL; + HANDLE hFile = INVALID_HANDLE_VALUE; + HANDLE result = NULL; + gboolean delete_on_error = FALSE; + + if (path) { + w_path = mono_string_to_utf16 (path); + WIN32_FILE_ATTRIBUTE_DATA file_attrs; + gboolean existed = GetFileAttributesExW (w_path, GetFileExInfoStandard, &file_attrs); + if (!existed && mode == FILE_MODE_CREATE_NEW && *capacity == 0) { + *error = CAPACITY_SMALLER_THAN_FILE_SIZE; + goto done; + } + hFile = CreateFileW (w_path, get_file_access (access), FILE_SHARE_READ, NULL, mode, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + *error = convert_win32_error (GetLastError (), COULD_NOT_OPEN); + goto done; + } + delete_on_error = !existed; + } + + result = open_handle (hFile, mapName, mode, capacity, access, options, error); + +done: + if (hFile != INVALID_HANDLE_VALUE) + CloseHandle (hFile); + if (!result && delete_on_error) + DeleteFileW (w_path); + if (w_path) + g_free (w_path); + + return result; +} + +void *mono_mmap_open_handle (void *handle, MonoString *mapName, gint64 *capacity, int access, int options, int *error) +{ + g_assert (handle != NULL); + + return open_handle (handle, mapName, FILE_MODE_OPEN, capacity, access, options, error); +} + +void mono_mmap_close (void *mmap_handle) +{ + g_assert (mmap_handle); + CloseHandle ((HANDLE) mmap_handle); +} + +void mono_mmap_configure_inheritability (void *mmap_handle, gboolean inheritability) +{ + g_assert (mmap_handle); + if (!SetHandleInformation ((HANDLE) mmap_handle, HANDLE_FLAG_INHERIT, inheritability ? HANDLE_FLAG_INHERIT : 0)) { + g_error ("mono_mmap_configure_inheritability: SetHandleInformation failed with error %d!", GetLastError ()); + } +} + +void mono_mmap_flush (void *mmap_handle) +{ + g_assert (mmap_handle); + MmapInstance *h = (MmapInstance *)mmap_handle; + + if (FlushViewOfFile (h->address, h->length)) + return; + + // This replicates how CoreFX does MemoryMappedView.Flush (). + + // It is a known issue within the NTFS transaction log system that + // causes FlushViewOfFile to intermittently fail with ERROR_LOCK_VIOLATION + // As a workaround, we catch this particular error and retry the flush operation + // a few milliseconds later. If it does not work, we give it a few more tries with + // increasing intervals. Eventually, however, we need to give up. In ad-hoc tests + // this strategy successfully flushed the view after no more than 3 retries. + + if (GetLastError () != ERROR_LOCK_VIOLATION) + // TODO: Propagate error to caller + return; + + for (int w = 0; w < MAX_FLUSH_WAITS; w++) { + int pause = (1 << w); // MaxFlushRetries should never be over 30 + mono_thread_info_sleep (pause, NULL); + + for (int r = 0; r < MAX_FLUSH_RETIRES_PER_WAIT; r++) { + if (FlushViewOfFile (h->address, h->length)) + return; + + if (GetLastError () != ERROR_LOCK_VIOLATION) + // TODO: Propagate error to caller + return; + + mono_thread_info_yield (); + } + } + + // We got to here, so there was no success: + // TODO: Propagate error to caller +} + +int mono_mmap_map (void *handle, gint64 offset, gint64 *size, int access, void **mmap_handle, void **base_address) +{ + static DWORD allocationGranularity = 0; + if (allocationGranularity == 0) { + SYSTEM_INFO info; + GetSystemInfo (&info); + allocationGranularity = info.dwAllocationGranularity; + } + + gint64 extraMemNeeded = offset % allocationGranularity; + guint64 newOffset = offset - extraMemNeeded; + gint64 nativeSize = (*size != 0) ? *size + extraMemNeeded : 0; + +#if SIZEOF_VOID_P == 4 + if (nativeSize > UINT32_MAX) + return CAPACITY_LARGER_THAN_LOGICAL_ADDRESS_SPACE; +#endif + + void *address = MapViewOfFile ((HANDLE) handle, get_file_map_access (access), (DWORD) (newOffset >> 32), (DWORD) newOffset, (SIZE_T) nativeSize); + if (!address) + return convert_win32_error (GetLastError (), COULD_NOT_MAP_MEMORY); + + // Query the view for its size and allocation type + MEMORY_BASIC_INFORMATION viewInfo; + VirtualQuery (address, &viewInfo, sizeof (MEMORY_BASIC_INFORMATION)); + guint64 viewSize = (guint64) viewInfo.RegionSize; + + // Allocate the pages if we were using the MemoryMappedFileOptions.DelayAllocatePages option + // OR check if the allocated view size is smaller than the expected native size + // If multiple overlapping views are created over the file mapping object, the pages in a given region + // could have different attributes(MEM_RESERVE OR MEM_COMMIT) as MapViewOfFile preserves coherence between + // views created on a mapping object backed by same file. + // In which case, the viewSize will be smaller than nativeSize required and viewState could be MEM_COMMIT + // but more pages may need to be committed in the region. + // This is because, VirtualQuery function(that internally invokes VirtualQueryEx function) returns the attributes + // and size of the region of pages with matching attributes starting from base address. + // VirtualQueryEx: http://msdn.microsoft.com/en-us/library/windows/desktop/aa366907(v=vs.85).aspx + if (((viewInfo.State & MEM_RESERVE) != 0) || viewSize < (guint64) nativeSize) { + void *tempAddress = VirtualAlloc (address, nativeSize != 0 ? nativeSize : viewSize, MEM_COMMIT, get_page_access (access)); + if (!tempAddress) { + return convert_win32_error (GetLastError (), COULD_NOT_MAP_MEMORY); + } + // again query the view for its new size + VirtualQuery (address, &viewInfo, sizeof (MEMORY_BASIC_INFORMATION)); + viewSize = (guint64) viewInfo.RegionSize; + } + + if (*size == 0) + *size = viewSize - extraMemNeeded; + + MmapInstance *h = g_malloc0 (sizeof (MmapInstance)); + h->address = address; + h->length = *size + extraMemNeeded; + *mmap_handle = h; + *base_address = (char*) address + (offset - newOffset); + + return 0; +} + +gboolean mono_mmap_unmap (void *mmap_handle) +{ + g_assert (mmap_handle); + + MmapInstance *h = (MmapInstance *) mmap_handle; + + gboolean result = UnmapViewOfFile (h->address); + + g_free (h); + return result; +} + +#else + +MONO_EMPTY_SOURCE_FILE (file_mmap_windows); + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/file-mmap.h b/unity-2019.4.24f1-mbe/mono/metadata/file-mmap.h new file mode 100644 index 000000000..93d1f05af --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/file-mmap.h @@ -0,0 +1,35 @@ +/** + * \file + * Managed mmap wrappers. + * + * Authors: + * Rodrigo Kumpera + * + * Copyright 2014 Xamarin Inc (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#ifndef _MONO_METADATA_FILE_MMAP_H_ +#define _MONO_METADATA_FILE_MMAP_H_ + +#include +#include + +#include +#include + +extern void mono_mmap_close (void *mmap_handle); + +extern void mono_mmap_configure_inheritability (void *mmap_handle, gboolean inheritability); + +extern void mono_mmap_flush (void *mmap_handle); + +extern void *mono_mmap_open_file (MonoString *string, int mode, MonoString *mapName, gint64 *capacity, int access, int options, int *error); + +extern void *mono_mmap_open_handle (void *handle, MonoString *mapName, gint64 *capacity, int access, int options, int *error); + +extern int mono_mmap_map (void *handle, gint64 offset, gint64 *size, int access, void **mmap_handle, void **base_address); + +extern gboolean mono_mmap_unmap (void *base_address); + +#endif /* _MONO_METADATA_FILE_MMAP_H_ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/filewatcher.c b/unity-2019.4.24f1-mbe/mono/metadata/filewatcher.c new file mode 100644 index 000000000..159a2537b --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/filewatcher.c @@ -0,0 +1,262 @@ +/** + * \file + * File System Watcher internal calls + * + * Authors: + * Gonzalo Paniagua Javier (gonzalo@ximian.com) + * + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_EVENT_H +#include +#endif +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HOST_WIN32 + +/* + * TODO: + * We use the managed watcher on windows, so the code inside this #if is never used + */ +gint +ves_icall_System_IO_FSW_SupportsFSW (void) +{ + return 1; +} + +gboolean +ves_icall_System_IO_FAMW_InternalFAMNextEvent (gpointer conn, + MonoString **filename, + gint *code, + gint *reqnum) +{ + return FALSE; +} + +#else + +static int (*FAMNextEvent) (gpointer, gpointer); + +gint +ves_icall_System_IO_FSW_SupportsFSW (void) +{ +#if HAVE_KQUEUE + return 3; +#else + MonoDl *fam_module; + int lib_used = 4; /* gamin */ + int inotify_instance; + char *err; + + inotify_instance = ves_icall_System_IO_InotifyWatcher_GetInotifyInstance (); + if (inotify_instance != -1) { + close (inotify_instance); + return 5; /* inotify */ + } + + fam_module = mono_dl_open ("libgamin-1.so", MONO_DL_LAZY, NULL); + if (fam_module == NULL) { + lib_used = 2; /* FAM */ + fam_module = mono_dl_open ("libfam.so", MONO_DL_LAZY, NULL); + } + + if (fam_module == NULL) + return 0; + + err = mono_dl_symbol (fam_module, "FAMNextEvent", (gpointer *) &FAMNextEvent); + g_free (err); + if (FAMNextEvent == NULL) + return 0; + + return lib_used; +#endif +} + +/* Almost copied from fam.h. Weird, I know */ +typedef struct { + gint reqnum; +} FAMRequest; + +typedef struct FAMEvent { + gpointer fc; + FAMRequest fr; + gchar *hostname; + gchar filename [PATH_MAX]; + gpointer userdata; + gint code; +} FAMEvent; + +gboolean +ves_icall_System_IO_FAMW_InternalFAMNextEvent (gpointer conn, + MonoString **filename, + gint *code, + gint *reqnum) +{ + MonoError error; + FAMEvent ev; + + if (FAMNextEvent (conn, &ev) == 1) { + *filename = mono_string_new_checked (mono_domain_get (), ev.filename, &error); + *code = ev.code; + *reqnum = ev.fr.reqnum; + if (mono_error_set_pending_exception (&error)) + return FALSE; + return TRUE; + } + + return FALSE; +} +#endif + +#ifndef HAVE_SYS_INOTIFY_H +int ves_icall_System_IO_InotifyWatcher_GetInotifyInstance () +{ + return -1; +} + +int ves_icall_System_IO_InotifyWatcher_AddWatch (int fd, MonoString *directory, gint32 mask) +{ + return -1; +} + +int ves_icall_System_IO_InotifyWatcher_RemoveWatch (int fd, gint32 watch_descriptor) +{ + return -1; +} +#else +#include +#include + +int +ves_icall_System_IO_InotifyWatcher_GetInotifyInstance () +{ + return inotify_init (); +} + +int +ves_icall_System_IO_InotifyWatcher_AddWatch (int fd, MonoString *name, gint32 mask) +{ + MonoError error; + char *str, *path; + int retval; + + if (name == NULL) + return -1; + + str = mono_string_to_utf8_checked (name, &error); + if (mono_error_set_pending_exception (&error)) + return -1; + path = mono_portability_find_file (str, TRUE); + if (!path) + path = str; + + retval = inotify_add_watch (fd, path, mask); + if (retval < 0) { + switch (errno) { + case EACCES: + errno = ERROR_ACCESS_DENIED; + break; + case EBADF: + errno = ERROR_INVALID_HANDLE; + break; + case EFAULT: + errno = ERROR_INVALID_ACCESS; + break; + case EINVAL: + errno = ERROR_INVALID_DATA; + break; + case ENOMEM: + errno = ERROR_NOT_ENOUGH_MEMORY; + break; + case ENOSPC: + errno = ERROR_TOO_MANY_OPEN_FILES; + break; + default: + errno = ERROR_GEN_FAILURE; + break; + } + mono_marshal_set_last_error (); + } + if (path != str) + g_free (path); + g_free (str); + return retval; +} + +int +ves_icall_System_IO_InotifyWatcher_RemoveWatch (int fd, gint32 watch_descriptor) +{ + return inotify_rm_watch (fd, watch_descriptor); +} +#endif + +#if HAVE_KQUEUE + +static void +interrupt_kevent (gpointer data) +{ + int *kq_ptr = data; + + /* Interrupt the kevent () call by closing the fd */ + close (*kq_ptr); + /* Signal to managed code that the fd is closed */ + *kq_ptr = -1; +} + +/* + * ves_icall_System_IO_KqueueMonitor_kevent_notimeout: + * + * Call kevent (), while handling runtime interruptions. + */ +int +ves_icall_System_IO_KqueueMonitor_kevent_notimeout (int *kq_ptr, gpointer changelist, int nchanges, gpointer eventlist, int nevents) +{ + int res; + gboolean interrupted; + + mono_thread_info_install_interrupt (interrupt_kevent, kq_ptr, &interrupted); + if (interrupted) { + close (*kq_ptr); + *kq_ptr = -1; + return -1; + } + + MONO_ENTER_GC_SAFE; + res = kevent (*kq_ptr, changelist, nchanges, eventlist, nevents, NULL); + MONO_EXIT_GC_SAFE; + + mono_thread_info_uninstall_interrupt (&interrupted); + + return res; +} + +#else + +int +ves_icall_System_IO_KqueueMonitor_kevent_notimeout (int *kq_ptr, gpointer changelist, int nchanges, gpointer eventlist, int nevents) +{ + g_assert_not_reached (); + return -1; +} + +#endif /* #if HAVE_KQUEUE */ + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/filewatcher.h b/unity-2019.4.24f1-mbe/mono/metadata/filewatcher.h new file mode 100644 index 000000000..2a0f8b0e6 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/filewatcher.h @@ -0,0 +1,40 @@ +/** + * \file + * File System Watcher internal calls + * + * Authors: + * Gonzalo Paniagua Javier (gonzalo@ximian.com) + * + * (C) 2004 Novell, Inc. (http://www.novell.com) + */ + +#ifndef _MONO_METADATA_FILEWATCHER_H +#define _MONO_METADATA_FILEWATCHER_H + +#include +#include "mono/utils/mono-compiler.h" +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +G_BEGIN_DECLS + +gint ves_icall_System_IO_FSW_SupportsFSW (void); + +gboolean ves_icall_System_IO_FAMW_InternalFAMNextEvent (gpointer conn, + MonoString **filename, + gint *code, + gint *reqnum); + +int ves_icall_System_IO_InotifyWatcher_GetInotifyInstance (void); +int ves_icall_System_IO_InotifyWatcher_AddWatch (int fd, MonoString *directory, gint32 mask); +int ves_icall_System_IO_InotifyWatcher_RemoveWatch (int fd, gint32 watch_descriptor); + +int ves_icall_System_IO_KqueueMonitor_kevent_notimeout (int *kq, gpointer changelist, int nchanges, gpointer eventlist, int nevents); + +G_END_DECLS + +#endif + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/gc-internals.h b/unity-2019.4.24f1-mbe/mono/metadata/gc-internals.h new file mode 100644 index 000000000..50665278f --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/gc-internals.h @@ -0,0 +1,382 @@ +/** + * \file + * Internal GC interface + * + * Author: Paolo Molaro + * + * (C) 2002 Ximian, Inc. + * Copyright 2012 Xamarin Inc (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#ifndef __MONO_METADATA_GC_INTERNAL_H__ +#define __MONO_METADATA_GC_INTERNAL_H__ + +#include +#include +#include +#include +#include + +#define mono_domain_finalizers_lock(domain) mono_os_mutex_lock (&(domain)->finalizable_objects_hash_lock); +#define mono_domain_finalizers_unlock(domain) mono_os_mutex_unlock (&(domain)->finalizable_objects_hash_lock); + +/* Register a memory area as a conservatively scanned GC root */ +#define MONO_GC_REGISTER_ROOT_PINNING(x,src,key,msg) mono_gc_register_root ((char*)&(x), sizeof(x), MONO_GC_DESCRIPTOR_NULL, (src), (key), (msg)) + +#define MONO_GC_UNREGISTER_ROOT(x) mono_gc_deregister_root ((char*)&(x)) + +/* + * Return a GC descriptor for an array containing N pointers to memory allocated + * by mono_gc_alloc_fixed (). + */ +/* For SGEN, the result of alloc_fixed () is not GC tracked memory */ +#define MONO_GC_ROOT_DESCR_FOR_FIXED(n) (mono_gc_is_moving () ? mono_gc_make_root_descr_all_refs (0) : MONO_GC_DESCRIPTOR_NULL) + +/* Register a memory location holding a single object reference as a GC root */ +#define MONO_GC_REGISTER_ROOT_SINGLE(x,src,key,msg) do { \ + g_assert (sizeof (x) == sizeof (MonoObject*)); \ + mono_gc_register_root ((char*)&(x), sizeof(MonoObject*), mono_gc_make_root_descr_all_refs (1), (src), (key),(msg)); \ + } while (0) + +/* + * This is used for fields which point to objects which are kept alive by other references + * when using Boehm. + */ +#define MONO_GC_REGISTER_ROOT_IF_MOVING(x,src,key,msg) do { \ + if (mono_gc_is_moving ()) \ + MONO_GC_REGISTER_ROOT_SINGLE(x,src,key,msg); \ +} while (0) + +#define MONO_GC_UNREGISTER_ROOT_IF_MOVING(x) do { \ + if (mono_gc_is_moving ()) \ + MONO_GC_UNREGISTER_ROOT (x); \ +} while (0) + +/* useful until we keep track of gc-references in corlib etc. */ +#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); +gint64 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection); +void ves_icall_System_GC_KeepAlive (MonoObject *obj); +void ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj); +void ves_icall_System_GC_SuppressFinalize (MonoObject *obj); +void ves_icall_System_GC_WaitForPendingFinalizers (void); + +MonoObject *ves_icall_System_GCHandle_GetTarget (guint32 handle); +guint32 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type); +void ves_icall_System_GCHandle_FreeHandle (guint32 handle); +gpointer ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle); +void ves_icall_System_GC_register_ephemeron_array (MonoObject *array); +MonoObject *ves_icall_System_GC_get_ephemeron_tombstone (void); + + +extern void mono_gc_init (void); +extern void mono_gc_base_init (void); +extern void mono_gc_cleanup (void); +extern void mono_gc_base_cleanup (void); + +/* + * Return whenever the current thread is registered with the GC (i.e. started + * by the GC pthread wrappers on unix. + */ +extern gboolean mono_gc_is_gc_thread (void); + +extern gboolean mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread); + +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). + */ +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); + +/*Ephemeron functionality. Sgen only*/ +gboolean mono_gc_ephemeron_array_add (MonoObject *obj); + +MonoBoolean +mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle); + +/* User defined marking function */ +/* It should work like this: + * foreach (ref in GC references in the are structure pointed to by ADDR) + * mark_func (ref) + */ +typedef void (*MonoGCMarkFunc) (MonoObject **addr, void *gc_data); +typedef void (*MonoGCRootMarkFunc) (void *addr, MonoGCMarkFunc mark_func, void *gc_data); + +/* Create a descriptor with a user defined marking function */ +MonoGCDescriptor mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker); + +/* Return whenever user defined marking functions are supported */ +gboolean mono_gc_user_markers_supported (void); + +/* desc is the result from mono_gc_make_descr*. A NULL value means + * all the words might contain GC pointers. + * The memory is non-moving and it will be explicitly deallocated. + * size bytes will be available from the returned address (ie, descr + * must not be stored in the returned memory) + */ +void* mono_gc_alloc_fixed (size_t size, MonoGCDescriptor descr, MonoGCRootSource source, void *key, const char *msg); +void mono_gc_free_fixed (void* addr); + +/* make sure the gchandle was allocated for an object in 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); + +void* mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size); +void* mono_gc_alloc_obj (MonoVTable *vtable, size_t size); +void* mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length); +void* mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size); +void* mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len); +void* mono_gc_alloc_mature (MonoVTable *vtable, size_t size); +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); +void mono_gc_deregister_root (char* addr); +void mono_gc_finalize_domain (MonoDomain *domain); +void mono_gc_run_finalize (void *obj, void *data); +void mono_gc_clear_domain (MonoDomain * domain); +/* Signal early termination of finalizer processing inside the gc */ +void mono_gc_suspend_finalizers (void); + + +/* + * Register a root which can only be written using a write barrier. + * Writes to the root must be done using a write barrier (MONO_ROOT_SETREF). + * If the root uses an user defined mark routine, the writes are not required to be + * to the area between START and START+SIZE. + * The write barrier allows the GC to avoid scanning this root at each collection, so it + * is more efficient. + * FIXME: Add an API for clearing remset entries if a root with a user defined + * mark routine is deleted. + */ +int mono_gc_register_root_wbarrier (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, void *key, const char *msg); + +void mono_gc_wbarrier_set_root (gpointer ptr, MonoObject *value); + +/* Set a field of a root registered using mono_gc_register_root_wbarrier () */ +#define MONO_ROOT_SETREF(s,fieldname,value) do { \ + mono_gc_wbarrier_set_root (&((s)->fieldname), (MonoObject*)value); \ +} while (0) + +/* fast allocation support */ + +typedef enum { + // Regular fast path allocator. + MANAGED_ALLOCATOR_REGULAR, + // Managed allocator that just calls into the runtime. + MANAGED_ALLOCATOR_SLOW_PATH, + // Managed allocator that works like the regular one but also calls into the profiler. + MANAGED_ALLOCATOR_PROFILER, +} ManagedAllocatorVariant; + +int mono_gc_get_aligned_size_for_allocator (int size); +MonoMethod* mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size); +MonoMethod* mono_gc_get_managed_array_allocator (MonoClass *klass); +MonoMethod *mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant); + +guint32 mono_gc_get_managed_allocator_types (void); + +/* Return a short string identifying the GC, indented to be saved in AOT images */ +const char *mono_gc_get_gc_name (void); + +/* Fast write barriers */ +MonoMethod* mono_gc_get_specific_write_barrier (gboolean is_concurrent); +MonoMethod* mono_gc_get_write_barrier (void); + +/* Fast valuetype copy */ +/* WARNING: [dest, dest + size] must be within the bounds of a single type, otherwise the GC will lose remset entries */ +void mono_gc_wbarrier_range_copy (gpointer dest, gpointer src, int size); +void* mono_gc_get_range_copy_func (void); + + +/* helper for the managed alloc support */ +MonoString * +ves_icall_string_alloc (int length); + +/* + * Functions supplied by the runtime and called by the GC. Currently only used + * by SGEN. + */ +typedef struct { + /* + * Function called during thread startup/attach to allocate thread-local data + * needed by the other functions. + */ + gpointer (*thread_attach_func) (void); + /* + * Function called during thread deatch to free the data allocated by + * thread_attach_func. + */ + void (*thread_detach_func) (gpointer user_data); + /* + * Function called from every thread when suspending for GC. It can save + * data needed for marking from thread stacks. user_data is the data returned + * by attach_func. This might called with GC locks held and the word stopped, + * so it shouldn't do any synchronization etc. + */ + void (*thread_suspend_func) (gpointer user_data, void *sigcontext, MonoContext *ctx); + /* + * Function called to mark from thread stacks. user_data is the data returned + * by attach_func. This is called twice, with the word stopped: + * - in the first pass, it should mark areas of the stack using + * conservative marking by calling mono_gc_conservatively_scan_area (). + * - in the second pass, it should mark the remaining areas of the stack + * using precise marking by calling mono_gc_scan_object (). + */ + void (*thread_mark_func) (gpointer user_data, guint8 *stack_start, guint8 *stack_end, gboolean precise, void *gc_data); + /* + * Function called for debugging to get the current managed method for + * tracking the provenances of objects. + */ + gpointer (*get_provenance_func) (void); +} MonoGCCallbacks; + +/* Set the callback functions callable by the GC */ +void mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks); +MonoGCCallbacks *mono_gc_get_gc_callbacks (void); + +/* Functions callable from the thread mark func */ + +/* Scan the memory area between START and END conservatively */ +void mono_gc_conservatively_scan_area (void *start, void *end); + +/* Scan OBJ, returning its new address */ +void *mono_gc_scan_object (void *obj, void *gc_data); + +/* Return the suspend signal number used by the GC to suspend threads, + or -1 if not applicable. */ +int mono_gc_get_suspend_signal (void); + +/* Return the suspend signal number used by the GC to suspend threads, + or -1 if not applicable. */ +int mono_gc_get_restart_signal (void); + +/* + * Return a human readable description of the GC in malloc-ed memory. + */ +char* mono_gc_get_description (void); + +/* + * Configure the GC to desktop mode + */ +void mono_gc_set_desktop_mode (void); + +/* + * Return whenever this GC can move objects + */ +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); + +int mono_gc_get_los_limit (void); + +guint8* mono_gc_get_card_table (int *shift_bits, gpointer *card_mask); +gboolean mono_gc_card_table_nursery_check (void); + +void* mono_gc_get_nursery (int *shift_bits, size_t *size); + +void mono_gc_set_skip_thread (gboolean skip); + +#ifndef HOST_WIN32 +int mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg); +#endif + +/* + * Return whenever GC is disabled + */ +gboolean mono_gc_is_disabled (void); + +/* + * Return whenever this is the null GC + */ +gboolean mono_gc_is_null (void); + +void mono_gc_set_string_length (MonoString *str, gint32 new_length); + +#if defined(__MACH__) +void mono_gc_register_mach_exception_thread (pthread_t thread); +pthread_t mono_gc_get_mach_exception_thread (void); +#endif + +gboolean mono_gc_precise_stack_mark_enabled (void); + +typedef struct _RefQueueEntry RefQueueEntry; + +struct _RefQueueEntry { + void *dis_link; + guint32 gchandle; + MonoDomain *domain; + void *user_data; + RefQueueEntry *next; +}; + +struct _MonoReferenceQueue { + RefQueueEntry *queue; + mono_reference_queue_callback callback; + MonoReferenceQueue *next; + gboolean should_be_deleted; +}; + +enum { + MONO_GC_FINALIZER_EXTENSION_VERSION = 1, +}; + +typedef struct { + int version; + gboolean (*is_class_finalization_aware) (MonoClass *klass); + void (*object_queued_for_finalization) (MonoObject *object); +} MonoGCFinalizerCallbacks; + +MONO_API void mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks); + + +#ifdef HOST_WIN32 +BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved); +#endif + +MonoVTable *mono_gc_get_vtable (MonoObject *obj); + +guint mono_gc_get_vtable_bits (MonoClass *klass); + +void mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size); + +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); + +/* If set, print debugging messages around finalizers. */ +extern gboolean log_finalizers; + +/* If set, do not run finalizers. */ +extern gboolean mono_do_not_finalize; +/* List of names of classes not to finalize. */ +extern gchar **mono_do_not_finalize_class_names; + +#endif /* __MONO_METADATA_GC_INTERNAL_H__ */ + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/gc-stats.c b/unity-2019.4.24f1-mbe/mono/metadata/gc-stats.c new file mode 100644 index 000000000..908efed7d --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/gc-stats.c @@ -0,0 +1,20 @@ +/** + * \file + * GC statistics. + * + * Copyright (C) 2015 Xamarin Inc + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include "mono/sgen/gc-internal-agnostic.h" + +/* + * Due to a bug in the linker on Darwin we need to initialize this struct, or there will be + * "undefined symbol" errors. + */ +#if defined(__APPLE__) +GCStats gc_stats = {}; +#else +GCStats gc_stats; +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/gc.c b/unity-2019.4.24f1-mbe/mono/metadata/gc.c new file mode 100644 index 000000000..22b0275c9 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/gc.c @@ -0,0 +1,1316 @@ +/** + * \file + * GC icalls. + * + * Author: Paolo Molaro + * + * Copyright 2002-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Copyright 2012 Xamarin Inc (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for mono_delegate_free_ftnptr () */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef HOST_WIN32 +#include +#endif + +typedef struct DomainFinalizationReq { + gint32 ref; + MonoDomain *domain; + MonoCoopSem done; +} DomainFinalizationReq; + +static gboolean gc_disabled; + +static gboolean finalizing_root_domain; + +gboolean log_finalizers; +gboolean mono_do_not_finalize; +volatile gboolean suspend_finalizers; +gchar **mono_do_not_finalize_class_names ; + +#define mono_finalizer_lock() mono_coop_mutex_lock (&finalizer_mutex) +#define mono_finalizer_unlock() mono_coop_mutex_unlock (&finalizer_mutex) +static MonoCoopMutex finalizer_mutex; +static MonoCoopMutex reference_queue_mutex; + +static GSList *domains_to_finalize; + +static gboolean finalizer_thread_exited; +/* Uses finalizer_mutex */ +static MonoCoopCond exited_cond; + +static MonoInternalThread *gc_thread; + +#ifdef TARGET_WIN32 +static HANDLE pending_done_event; +#else +static gboolean pending_done; +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); +static void mono_reference_queue_cleanup (void); +static void reference_queue_clear_for_domain (MonoDomain *domain); +static void mono_runtime_do_background_work (void); + + +static MonoThreadInfoWaitRet +guarded_wait (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertable) +{ + MonoThreadInfoWaitRet result; + + MONO_ENTER_GC_SAFE; + result = mono_thread_info_wait_one_handle (thread_handle, timeout, alertable); + MONO_EXIT_GC_SAFE; + + return result; +} + +typedef struct { + MonoCoopCond *cond; + MonoCoopMutex *mutex; +} BreakCoopAlertableWaitUD; + +static inline void +break_coop_alertable_wait (gpointer user_data) +{ + BreakCoopAlertableWaitUD *ud = (BreakCoopAlertableWaitUD*)user_data; + + mono_coop_mutex_lock (ud->mutex); + mono_coop_cond_signal (ud->cond); + mono_coop_mutex_unlock (ud->mutex); + + g_free (ud); +} + +/* + * coop_cond_timedwait_alertable: + * + * Wait on COND/MUTEX. If ALERTABLE is non-null, the wait can be interrupted. + * In that case, *ALERTABLE will be set to TRUE, and 0 is returned. + */ +static inline gint +coop_cond_timedwait_alertable (MonoCoopCond *cond, MonoCoopMutex *mutex, guint32 timeout_ms, gboolean *alertable) +{ + BreakCoopAlertableWaitUD *ud; + int res; + + if (alertable) { + ud = g_new0 (BreakCoopAlertableWaitUD, 1); + ud->cond = cond; + ud->mutex = mutex; + + mono_thread_info_install_interrupt (break_coop_alertable_wait, ud, alertable); + if (*alertable) { + g_free (ud); + return 0; + } + } + res = mono_coop_cond_timedwait (cond, mutex, timeout_ms); + if (alertable) { + mono_thread_info_uninstall_interrupt (alertable); + if (*alertable) + return 0; + else { + /* the interrupt token has not been taken by another + * thread, so it's our responsability to free it up. */ + g_free (ud); + } + } + 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... + */ +void +mono_gc_run_finalize (void *obj, void *data) +{ + MonoError error; + MonoObject *exc = NULL; + MonoObject *o; +#ifndef HAVE_SGEN_GC + MonoObject *o2; +#endif + MonoMethod* finalizer = NULL; + MonoDomain *caller_domain = mono_domain_get (); + MonoDomain *domain; + + // This function is called from the innards of the GC, so our best alternative for now is to do polling here + mono_threads_safepoint (); + + o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data)); + + if (mono_do_not_finalize) { + if (!mono_do_not_finalize_class_names) + return; + + size_t namespace_len = strlen (o->vtable->klass->name_space); + for (int i = 0; mono_do_not_finalize_class_names [i]; ++i) { + const char *name = mono_do_not_finalize_class_names [i]; + if (strncmp (name, o->vtable->klass->name_space, namespace_len)) + break; + if (name [namespace_len] != '.') + break; + if (strcmp (name + namespace_len + 1, o->vtable->klass->name)) + break; + return; + } + } + + if (log_finalizers) + g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o); + + if (suspend_finalizers) + return; + + domain = o->vtable->domain; + +#ifndef HAVE_SGEN_GC + mono_domain_finalizers_lock (domain); + + o2 = (MonoObject *)g_hash_table_lookup (domain->finalizable_objects_hash, o); + + mono_domain_finalizers_unlock (domain); + + if (!o2) + /* Already finalized somehow */ + return; +#endif + + /* make sure the finalizer is not called again if the object is resurrected */ + object_register_finalizer ((MonoObject *)obj, NULL); + + if (log_finalizers) + g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o); + + if (o->vtable->klass == mono_defaults.internal_thread_class) { + MonoInternalThread *t = (MonoInternalThread*)o; + + if (mono_gc_is_finalizer_internal_thread (t)) + /* Avoid finalizing ourselves */ + return; + } + + if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) { + /* + * These can't be finalized during unloading/shutdown, since that would + * free the native code which can still be referenced by other + * finalizers. + * FIXME: This is not perfect, objects dying at the same time as + * dynamic methods can still reference them even when !shutdown. + */ + return; + } + + if (mono_runtime_get_no_exec ()) + return; + + /* speedup later... and use a timeout */ + /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */ + + /* Use _internal here, since this thread can enter a doomed appdomain */ + mono_domain_set_internal (mono_object_domain (o)); + + /* delegates that have a native function pointer allocated are + * registered for finalization, but they don't have a Finalize + * method, because in most cases it's not needed and it's just a waste. + */ + if (o->vtable->klass->delegate) { + MonoDelegate* del = (MonoDelegate*)o; + if (del->delegate_trampoline) + mono_delegate_free_ftnptr ((MonoDelegate*)o); + mono_domain_set_internal (caller_domain); + return; + } + + finalizer = mono_class_get_finalizer (o->vtable->klass); + + /* If object has a CCW but has no finalizer, it was only + * registered for finalization in order to free the CCW. + * Else it needs the regular finalizer run. + * FIXME: what to do about ressurection and suppression + * of finalizer on object with CCW. + */ + if (mono_marshal_free_ccw (o) && !finalizer) { + mono_domain_set_internal (caller_domain); + 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); + + if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) { + MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o), + 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); + + 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 + + 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); + +unhandled_error: + if (!is_ok (&error)) + exc = (MonoObject*)mono_error_convert_to_exception (&error); + if (exc) + mono_thread_internal_unhandled_exception (exc); + + mono_domain_set_internal (caller_domain); +} + +/* + * Some of our objects may point to a different address than the address returned by GC_malloc() + * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer. + * This also means that in the callback we need to adjust the pointer to get back the real + * MonoObject*. + * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer, + * since that, too, can cause the underlying pointer to be offset. + */ +static void +object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*)) +{ + MonoDomain *domain; + + g_assert (obj != NULL); + + domain = obj->vtable->domain; + +#if HAVE_BOEHM_GC + if (mono_domain_is_unloading (domain) && (callback != NULL)) + /* + * Can't register finalizers in a dying appdomain, since they + * could be invoked after the appdomain has been unloaded. + */ + return; + + mono_domain_finalizers_lock (domain); + + if (callback) + g_hash_table_insert (domain->finalizable_objects_hash, obj, obj); + else + g_hash_table_remove (domain->finalizable_objects_hash, obj); + + mono_domain_finalizers_unlock (domain); + + mono_gc_register_for_finalization (obj, callback); +#elif defined(HAVE_SGEN_GC) + /* + * If we register finalizers for domains that are unloading we might + * end up running them while or after the domain is being cleared, so + * the objects will not be valid anymore. + */ + if (!mono_domain_is_unloading (domain)) + mono_gc_register_for_finalization (obj, callback); +#endif +} + +/** + * mono_object_register_finalizer: + * \param obj object to register + * + * Records that object \p obj has a finalizer, this will call the + * Finalize method when the garbage collector disposes the object. + * + */ +void +mono_object_register_finalizer (MonoObject *obj) +{ + /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */ + object_register_finalizer (obj, mono_gc_run_finalize); +} + +/** + * mono_domain_finalize: + * \param domain the domain to finalize + * \param timeout msecs to wait for the finalization to complete, \c -1 to wait indefinitely + * + * Request finalization of all finalizable objects inside \p domain. Wait + * \p timeout msecs for the finalization to complete. + * + * \returns TRUE if succeeded, FALSE if there was a timeout + */ +gboolean +mono_domain_finalize (MonoDomain *domain, guint32 timeout) +{ + DomainFinalizationReq *req; + MonoInternalThread *thread = mono_thread_internal_current (); + gint res; + gboolean ret; + gint64 start; + + if (mono_thread_internal_current () == gc_thread) + /* We are called from inside a finalizer, not much we can do here */ + return FALSE; + + /* + * No need to create another thread 'cause the finalizer thread + * is still working and will take care of running the finalizers + */ + + if (gc_disabled) + return TRUE; + + /* We don't support domain finalization without a GC */ + if (mono_gc_is_null ()) + return FALSE; + + mono_gc_collect (mono_gc_max_generation ()); + + req = g_new0 (DomainFinalizationReq, 1); + req->ref = 2; + req->domain = domain; + mono_coop_sem_init (&req->done, 0); + + if (domain == mono_get_root_domain ()) + finalizing_root_domain = TRUE; + + mono_finalizer_lock (); + + domains_to_finalize = g_slist_append (domains_to_finalize, req); + + mono_finalizer_unlock (); + + /* Tell the finalizer thread to finalize this appdomain */ + mono_gc_finalize_notify (); + + if (timeout == -1) + timeout = MONO_INFINITE_WAIT; + if (timeout != MONO_INFINITE_WAIT) + start = mono_msec_ticks (); + + ret = TRUE; + + for (;;) { + if (timeout == MONO_INFINITE_WAIT) { + res = mono_coop_sem_wait (&req->done, MONO_SEM_FLAGS_ALERTABLE); + } else { + gint64 elapsed = mono_msec_ticks () - start; + if (elapsed >= timeout) { + ret = FALSE; + break; + } + + res = mono_coop_sem_timedwait (&req->done, timeout - elapsed, MONO_SEM_FLAGS_ALERTABLE); + } + + if (res == MONO_SEM_TIMEDWAIT_RET_SUCCESS) { + break; + } else if (res == MONO_SEM_TIMEDWAIT_RET_ALERTED) { + if ((thread->state & (ThreadState_AbortRequested | ThreadState_SuspendRequested)) != 0) { + ret = FALSE; + break; + } + } else if (res == MONO_SEM_TIMEDWAIT_RET_TIMEDOUT) { + ret = FALSE; + break; + } else { + g_error ("%s: unknown result %d", __func__, res); + } + } + + if (!ret) { + /* Try removing the req from domains_to_finalize: + * - if it's not found: the domain is being finalized, + * so we the ref count is already decremented + * - if it's found: the domain is not yet being finalized, + * so we can safely decrement the ref */ + + gboolean found; + + mono_finalizer_lock (); + + found = g_slist_index (domains_to_finalize, req) != -1; + if (found) + domains_to_finalize = g_slist_remove (domains_to_finalize, req); + + mono_finalizer_unlock (); + + if (found) { + /* We have to decrement it wherever we + * remove it from domains_to_finalize */ + if (mono_atomic_dec_i32 (&req->ref) != 1) + g_error ("%s: req->ref should be 1, as we are the first one to decrement it", __func__); + } + + goto done; + } + +done: + if (mono_atomic_dec_i32 (&req->ref) == 0) { + mono_coop_sem_destroy (&req->done); + g_free (req); + } + + return ret; +} + +void +ves_icall_System_GC_InternalCollect (int generation) +{ + mono_gc_collect (generation); +} + +gint64 +ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection) +{ + if (forceCollection) + mono_gc_collect (mono_gc_max_generation ()); + return mono_gc_get_used_size (); +} + +void +ves_icall_System_GC_KeepAlive (MonoObject *obj) +{ + /* + * Does nothing. + */ +} + +void +ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj) +{ + MONO_CHECK_ARG_NULL (obj,); + + object_register_finalizer (obj, mono_gc_run_finalize); +} + +void +ves_icall_System_GC_SuppressFinalize (MonoObject *obj) +{ + MONO_CHECK_ARG_NULL (obj,); + + /* delegates have no finalizers, but we register them to deal with the + * unmanaged->managed trampoline. We don't let the user suppress it + * otherwise we'd leak it. + */ + if (obj->vtable->klass->delegate) + return; + + /* FIXME: Need to handle case where obj has COM Callable Wrapper + * generated for it that needs cleaned up, but user wants to suppress + * their derived object finalizer. */ + + object_register_finalizer (obj, NULL); +} + +void +ves_icall_System_GC_WaitForPendingFinalizers (void) +{ + if (mono_gc_is_null ()) + return; + + if (!mono_gc_pending_finalizers ()) + return; + + if (mono_thread_internal_current () == gc_thread) + /* Avoid deadlocks */ + return; + + /* + If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might + be the one responsible for starting it up. + */ + if (gc_thread == NULL) + return; + +#ifdef TARGET_WIN32 + ResetEvent (pending_done_event); + mono_gc_finalize_notify (); + /* g_print ("Waiting for pending finalizers....\n"); */ + MONO_ENTER_GC_SAFE; + mono_win32_wait_for_single_object_ex (pending_done_event, INFINITE, TRUE); + MONO_EXIT_GC_SAFE; + /* g_print ("Done pending....\n"); */ +#else + gboolean alerted = FALSE; + mono_coop_mutex_lock (&pending_done_mutex); + pending_done = FALSE; + mono_gc_finalize_notify (); + while (!pending_done) { + coop_cond_timedwait_alertable (&pending_done_cond, &pending_done_mutex, MONO_INFINITE_WAIT, &alerted); + if (alerted) + break; + } + mono_coop_mutex_unlock (&pending_done_mutex); +#endif +} + +void +ves_icall_System_GC_register_ephemeron_array (MonoObject *array) +{ +#ifdef HAVE_SGEN_GC + if (!mono_gc_ephemeron_array_add (array)) { + mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex); + return; + } +#endif +} + +MonoObject* +ves_icall_System_GC_get_ephemeron_tombstone (void) +{ + return mono_domain_get ()->ephemeron_tombstone; +} + +MonoObject * +ves_icall_System_GCHandle_GetTarget (guint32 handle) +{ + return mono_gchandle_get_target (handle); +} + +/* + * if type == -1, change the target of the handle, otherwise allocate a new handle. + */ +guint32 +ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type) +{ + if (type == -1) { + mono_gchandle_set_target (handle, obj); + /* the handle doesn't change */ + return handle; + } + switch (type) { + case HANDLE_WEAK: + return mono_gchandle_new_weakref (obj, FALSE); + case HANDLE_WEAK_TRACK: + return mono_gchandle_new_weakref (obj, TRUE); + case HANDLE_NORMAL: + return mono_gchandle_new (obj, FALSE); + case HANDLE_PINNED: + return mono_gchandle_new (obj, TRUE); + default: + g_assert_not_reached (); + } + return 0; +} + +void +ves_icall_System_GCHandle_FreeHandle (guint32 handle) +{ + mono_gchandle_free (handle); +} + +gpointer +ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle) +{ + MonoObject *obj; + + if (MONO_GC_HANDLE_TYPE (handle) != HANDLE_PINNED) + return (gpointer)-2; + obj = mono_gchandle_get_target (handle); + if (obj) { + MonoClass *klass = mono_object_class (obj); + if (klass == mono_defaults.string_class) { + return mono_string_chars ((MonoString*)obj); + } else if (klass->rank) { + return mono_array_addr ((MonoArray*)obj, char, 0); + } else { + /* the C# code will check and throw the exception */ + /* FIXME: missing !klass->blittable test, see bug #61134 */ + if (mono_class_is_auto_layout (klass)) + return (gpointer)-1; + return (char*)obj + sizeof (MonoObject); + } + } + return NULL; +} + +MonoBoolean +mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle) +{ + return mono_gchandle_is_in_domain (gchandle, mono_domain_get ()); +} + +static MonoCoopSem finalizer_sem; +static volatile gboolean finished; + +/* + * mono_gc_finalize_notify: + * + * Notify the finalizer thread that finalizers etc. + * are available to be processed. + * This is async signal safe. + */ +void +mono_gc_finalize_notify (void) +{ +#if defined(DEBUG) && !defined(IL2CPP_ON_MONO) + g_message ( "%s: prodding finalizer", __func__); +#endif + + if (mono_gc_is_null ()) + return; + +#ifdef HOST_WASM + mono_threads_schedule_background_job (mono_runtime_do_background_work); +#else + mono_coop_sem_post (&finalizer_sem); +#endif +} + +/* +This is the number of entries allowed in the hazard free queue before +we explicitly cycle the finalizer thread to trigger pumping the queue. + +It was picked empirically by running the corlib test suite in a stress +scenario where all hazard entries are queued. + +In this extreme scenario we double the number of times we cycle the finalizer +thread compared to just GC calls. + +Entries are usually in the order of 100's of bytes each, so we're limiting +floating garbage to be in the order of a dozen kb. +*/ +static gboolean finalizer_thread_pulsed; +#define HAZARD_QUEUE_OVERFLOW_SIZE 20 + +static void +hazard_free_queue_is_too_big (size_t size) +{ + if (size < HAZARD_QUEUE_OVERFLOW_SIZE) + return; + + if (finalizer_thread_pulsed || mono_atomic_cas_i32 (&finalizer_thread_pulsed, TRUE, FALSE)) + return; + + mono_gc_finalize_notify (); +} + +static void +hazard_free_queue_pump (void) +{ + mono_thread_hazardous_try_free_all (); + finalizer_thread_pulsed = FALSE; +} + +#ifdef HAVE_BOEHM_GC + +static void +collect_objects (gpointer key, gpointer value, gpointer user_data) +{ + GPtrArray *arr = (GPtrArray*)user_data; + g_ptr_array_add (arr, key); +} + +#endif + +/* + * finalize_domain_objects: + * + * Run the finalizers of all finalizable objects in req->domain. + */ +static void +finalize_domain_objects (void) +{ + DomainFinalizationReq *req = NULL; + MonoDomain *domain; + + if (UnlockedReadPointer ((gpointer)&domains_to_finalize)) { + mono_finalizer_lock (); + if (domains_to_finalize) { + req = (DomainFinalizationReq *)domains_to_finalize->data; + domains_to_finalize = g_slist_remove (domains_to_finalize, req); + } + mono_finalizer_unlock (); + } + + if (!req) + return; + + domain = req->domain; + + /* Process finalizers which are already in the queue */ + mono_gc_invoke_finalizers (); + +#ifdef HAVE_BOEHM_GC + while (g_hash_table_size (domain->finalizable_objects_hash) > 0) { + int i; + GPtrArray *objs; + /* + * Since the domain is unloading, nobody is allowed to put + * new entries into the hash table. But finalize_object might + * remove entries from the hash table, so we make a copy. + */ + objs = g_ptr_array_new (); + g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs); + /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */ + + for (i = 0; i < objs->len; ++i) { + MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i); + /* FIXME: Avoid finalizing threads, etc */ + mono_gc_run_finalize (o, 0); + } + + g_ptr_array_free (objs, TRUE); + } +#elif defined(HAVE_SGEN_GC) + mono_gc_finalize_domain (domain); + mono_gc_invoke_finalizers (); +#endif + + /* cleanup the reference queue */ + reference_queue_clear_for_domain (domain); + + /* printf ("DONE.\n"); */ + mono_coop_sem_post (&req->done); + + if (mono_atomic_dec_i32 (&req->ref) == 0) { + /* mono_domain_finalize already returned, and + * doesn't hold a reference to req anymore. */ + mono_coop_sem_destroy (&req->done); + g_free (req); + } +} + + +static void +mono_runtime_do_background_work (void) +{ + mono_threads_perform_thread_dump (); + + mono_console_handle_async_ops (); + + mono_attach_maybe_start (); + + finalize_domain_objects (); + + MONO_PROFILER_RAISE (gc_finalizing, ()); + + /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup), + * before the domain is unloaded. + */ + mono_gc_invoke_finalizers (); + + MONO_PROFILER_RAISE (gc_finalized, ()); + + mono_threads_join_threads (); + + reference_queue_proccess_all (); + + mono_w32process_signal_finished (); + + hazard_free_queue_pump (); +} + +static gsize WINAPI +finalizer_thread (gpointer unused) +{ + MonoError error; + gboolean wait = TRUE; + + MonoString *finalizer = mono_string_new_checked (mono_get_root_domain (), "Finalizer", &error); + mono_error_assert_ok (&error); + mono_thread_set_name_internal (mono_thread_internal_current (), finalizer, FALSE, FALSE, &error); + mono_error_assert_ok (&error); + + /* 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 + */ + + g_assert (mono_domain_get () == mono_get_root_domain ()); + mono_gc_set_skip_thread (TRUE); + + if (wait) { + /* An alertable wait is required so this thread can be suspended on windows */ + mono_coop_sem_wait (&finalizer_sem, MONO_SEM_FLAGS_ALERTABLE); + } + wait = TRUE; + + mono_gc_set_skip_thread (FALSE); + + mono_runtime_do_background_work (); + + /* Avoid posting the pending done event until there are pending finalizers */ + if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS) { + /* Don't wait again at the start of the loop */ + wait = FALSE; + } else { +#ifdef TARGET_WIN32 + SetEvent (pending_done_event); +#else + mono_coop_mutex_lock (&pending_done_mutex); + pending_done = TRUE; + mono_coop_cond_signal (&pending_done_cond); + mono_coop_mutex_unlock (&pending_done_mutex); +#endif + } + } + + mono_finalizer_lock (); + finalizer_thread_exited = TRUE; + mono_coop_cond_signal (&exited_cond); + mono_finalizer_unlock (); + + return 0; +} + +#ifndef LAZY_GC_THREAD_CREATION +static +#endif +void +mono_gc_init_finalizer_thread (void) +{ + MonoError error; + gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, MONO_THREAD_CREATE_FLAGS_NONE, &error); + mono_error_assert_ok (&error); +} + +void +mono_gc_init (void) +{ + mono_coop_mutex_init_recursive (&finalizer_mutex); + mono_coop_mutex_init_recursive (&reference_queue_mutex); + + mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_INT, &gc_stats.minor_gc_count); + mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_INT, &gc_stats.major_gc_count); + mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time); + mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_LONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time); + mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_LONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent); + + mono_gc_base_init (); + + if (mono_gc_is_disabled ()) + gc_disabled = TRUE; + +#ifdef TARGET_WIN32 + pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL); + g_assert (pending_done_event); +#else + mono_coop_cond_init (&pending_done_cond); + mono_coop_mutex_init (&pending_done_mutex); +#endif + + mono_coop_cond_init (&exited_cond); + mono_coop_sem_init (&finalizer_sem, 0); + +#ifndef LAZY_GC_THREAD_CREATION + mono_gc_init_finalizer_thread (); +#endif +} + +void +mono_gc_cleanup (void) +{ +#ifdef DEBUG + g_message ("%s: cleaning up finalizer", __func__); +#endif + + if (mono_gc_is_null ()) + return; + + finished = TRUE; + if (!gc_disabled) { + if (mono_thread_internal_current () != gc_thread) { + int ret; + gint64 start; + const gint64 timeout = 40 * 1000; + + mono_gc_finalize_notify (); + + start = mono_msec_ticks (); + + /* Finishing the finalizer thread, so wait a little bit... */ + /* MS seems to wait for about 2 seconds per finalizer thread */ + /* and 40 seconds for all finalizers to finish */ + for (;;) { + gint64 elapsed; + + if (finalizer_thread_exited) { + /* Wait for the thread to actually exit. We don't want the wait + * to be alertable, because we assert on the result to be SUCCESS_0 */ + ret = guarded_wait (gc_thread->handle, MONO_INFINITE_WAIT, FALSE); + g_assert (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0); + + mono_threads_add_joinable_thread ((gpointer)(MONO_UINT_TO_NATIVE_THREAD_ID (gc_thread->tid))); + break; + } + + elapsed = mono_msec_ticks () - start; + if (elapsed >= timeout) { + /* timeout */ + + /* Set a flag which the finalizer thread can check */ + suspend_finalizers = TRUE; + mono_gc_suspend_finalizers (); + + /* Try to abort the thread, in the hope that it is running managed code */ + mono_thread_internal_abort (gc_thread, FALSE); + + /* Wait for it to stop */ + ret = guarded_wait (gc_thread->handle, 100, FALSE); + if (ret == MONO_THREAD_INFO_WAIT_RET_TIMEOUT) { + /* The finalizer thread refused to exit, suspend it forever. */ + mono_thread_internal_suspend_for_shutdown (gc_thread); + break; + } + + g_assert (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0); + + mono_threads_add_joinable_thread ((gpointer)(MONO_UINT_TO_NATIVE_THREAD_ID (gc_thread->tid))); + break; + } + + mono_finalizer_lock (); + if (!finalizer_thread_exited) + mono_coop_cond_timedwait (&exited_cond, &finalizer_mutex, timeout - elapsed); + mono_finalizer_unlock (); + } + } + gc_thread = NULL; + mono_gc_base_cleanup (); + + mono_reference_queue_cleanup (); + } + mono_coop_mutex_destroy (&finalizer_mutex); + mono_coop_mutex_destroy (&reference_queue_mutex); +} + +gboolean +mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread) +{ + return thread == gc_thread; +} + +/** + * mono_gc_is_finalizer_thread: + * \param thread the thread to test. + * + * In Mono objects are finalized asynchronously on a separate thread. + * This routine tests whether the \p thread argument represents the + * finalization thread. + * + * \returns TRUE if \p thread is the finalization thread. + */ +gboolean +mono_gc_is_finalizer_thread (MonoThread *thread) +{ + return mono_gc_is_finalizer_internal_thread (thread->internal_thread); +} + +#if defined(__MACH__) +static pthread_t mach_exception_thread; + +void +mono_gc_register_mach_exception_thread (pthread_t thread) +{ + mach_exception_thread = thread; +} + +pthread_t +mono_gc_get_mach_exception_thread (void) +{ + return mach_exception_thread; +} +#endif + +static MonoReferenceQueue *ref_queues; + +static void +ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element) +{ + do { + /* Guard if head is changed concurrently. */ + while (*prev != element) + prev = &(*prev)->next; + } while (prev && mono_atomic_cas_ptr ((volatile gpointer *)prev, element->next, element) != element); +} + +static void +ref_list_push (RefQueueEntry **head, RefQueueEntry *value) +{ + RefQueueEntry *current; + do { + current = *head; + value->next = current; + STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */ + } while (mono_atomic_cas_ptr ((volatile gpointer *)head, value, current) != current); +} + +static void +reference_queue_proccess (MonoReferenceQueue *queue) +{ + RefQueueEntry **iter = &queue->queue; + RefQueueEntry *entry; + while ((entry = *iter)) { + if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) { + mono_gchandle_free ((guint32)entry->gchandle); + ref_list_remove_element (iter, entry); + queue->callback (entry->user_data); + g_free (entry); + } else { + iter = &entry->next; + } + } +} + +static void +reference_queue_proccess_all (void) +{ + MonoReferenceQueue **iter; + MonoReferenceQueue *queue = ref_queues; + for (; queue; queue = queue->next) + reference_queue_proccess (queue); + +restart: + mono_coop_mutex_lock (&reference_queue_mutex); + for (iter = &ref_queues; *iter;) { + queue = *iter; + if (!queue->should_be_deleted) { + iter = &queue->next; + continue; + } + if (queue->queue) { + mono_coop_mutex_unlock (&reference_queue_mutex); + reference_queue_proccess (queue); + goto restart; + } + *iter = queue->next; + g_free (queue); + } + mono_coop_mutex_unlock (&reference_queue_mutex); +} + +static void +mono_reference_queue_cleanup (void) +{ + MonoReferenceQueue *queue = ref_queues; + for (; queue; queue = queue->next) + queue->should_be_deleted = TRUE; + reference_queue_proccess_all (); +} + +static void +reference_queue_clear_for_domain (MonoDomain *domain) +{ + MonoReferenceQueue *queue = ref_queues; + for (; queue; queue = queue->next) { + RefQueueEntry **iter = &queue->queue; + RefQueueEntry *entry; + while ((entry = *iter)) { + if (entry->domain == domain) { + mono_gchandle_free ((guint32)entry->gchandle); + ref_list_remove_element (iter, entry); + queue->callback (entry->user_data); + g_free (entry); + } else { + iter = &entry->next; + } + } + } +} +/** + * mono_gc_reference_queue_new: + * \param callback callback used when processing collected entries. + * + * Create a new reference queue used to process collected objects. + * A reference queue let you add a pair of (managed object, user data) + * using the \c mono_gc_reference_queue_add method. + * + * Once the managed object is collected \p callback will be called + * in the finalizer thread with 'user data' as argument. + * + * The callback is called from the finalizer thread without any locks held. + * When an AppDomain is unloaded, all callbacks for objects belonging to it + * will be invoked. + * + * \returns the new queue. + */ +MonoReferenceQueue* +mono_gc_reference_queue_new (mono_reference_queue_callback callback) +{ + MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1); + res->callback = callback; + + mono_coop_mutex_lock (&reference_queue_mutex); + res->next = ref_queues; + ref_queues = res; + mono_coop_mutex_unlock (&reference_queue_mutex); + + return res; +} + +/** + * mono_gc_reference_queue_add: + * \param queue the queue to add the reference to. + * \param obj the object to be watched for collection + * \param user_data parameter to be passed to the queue callback + * + * Queue an object to be watched for collection, when the \p obj is + * collected, the callback that was registered for the \p queue will + * be invoked with \p user_data as argument. + * + * \returns FALSE if the queue is scheduled to be freed. + */ +gboolean +mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data) +{ + RefQueueEntry *entry; + if (queue->should_be_deleted) + return FALSE; + + g_assert (obj != NULL); + + entry = g_new0 (RefQueueEntry, 1); + entry->user_data = user_data; + entry->domain = mono_object_domain (obj); + + entry->gchandle = mono_gchandle_new_weakref (obj, TRUE); + mono_object_register_finalizer (obj); + + ref_list_push (&queue->queue, entry); + return TRUE; +} + +/** + * mono_gc_reference_queue_free: + * \param queue the queue that should be freed. + * + * This operation signals that \p queue should be freed. This operation is deferred + * as it happens on the finalizer thread. + * + * After this call, no further objects can be queued. It's the responsibility of the + * caller to make sure that no further attempt to access queue will be made. + */ +void +mono_gc_reference_queue_free (MonoReferenceQueue *queue) +{ + queue->should_be_deleted = TRUE; +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/handle.c b/unity-2019.4.24f1-mbe/mono/metadata/handle.c new file mode 100644 index 000000000..3a88b0763 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/handle.c @@ -0,0 +1,556 @@ +/** + * \file + * Handle to object in native code + * + * Authors: + * - Ludovic Henry + * - Aleksey Klieger + * - Rodrigo Kumpera + * + * Copyright 2016 Dot net foundation. + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_BACKTRACE_SYMBOLS +#include +#endif + +/* TODO (missing pieces) + +Add counters for: + number of stack marks + stack marks per icall + mix/max/avg size of stack marks + handle stack wastage + +Actually do something in mono_handle_verify + +Shrink the handles stack in mono_handle_stack_scan +Add a boehm implementation + +TODO (things to explore): + +There's no convenient way to wrap the object allocation function. +Right now we do this: + MonoCultureInfoHandle culture = MONO_HANDLE_NEW (MonoCultureInfo, mono_object_new_checked (domain, klass, &error)); + +Maybe what we need is a round of cleanup around all exposed types in the runtime to unify all helpers under the same hoof. +Combine: MonoDefaults, GENERATE_GET_CLASS_WITH_CACHE, TYPED_HANDLE_DECL and friends. + This would solve the age old issue of making it clear which types are optional and tell that to the linker. + We could then generate neat type safe wrappers. +*/ + +/* + * NOTE: Async suspend + * + * If we are running with cooperative GC, all the handle stack + * manipulation will complete before a GC thread scans the handle + * stack. If we are using async suspend, however, a thread may be + * trying to allocate a new handle, or unwind the handle stack when + * the GC stops the world. + * + * In particular, we need to ensure that if the mutator thread is + * suspended while manipulating the handle stack, the stack is in a + * good enough state to be scanned. In particular, the size of each + * chunk should be updated before an object is written into the + * handle, and chunks to be scanned (between bottom and top) should + * always be valid. + * + * Note that the handle stack is scanned PRECISELY (see + * sgen_client_scan_thread_data ()). That means there should not be + * stale objects scanned. So when we manipulate the size of a chunk, + * wemust ensure that the newly scannable slot is either null or + * points to a valid value. + */ + +static HandleStack* +new_handle_stack (void) +{ + return g_new (HandleStack, 1); +} + +static void +free_handle_stack (HandleStack *stack) +{ + g_free (stack); +} + +static HandleChunk* +new_handle_chunk (void) +{ + return g_new (HandleChunk, 1); +} + +static void +free_handle_chunk (HandleChunk *chunk) +{ + g_free (chunk); +} + +const MonoObjectHandle mono_null_value_handle = NULL; + +#define THIS_IS_AN_OK_NUMBER_OF_HANDLES 100 + +static HandleChunkElem* +chunk_element (HandleChunk *chunk, int idx) +{ + return &chunk->elems[idx]; +} + +static HandleChunkElem* +handle_to_chunk_element (MonoObjectHandle o) +{ + return (HandleChunkElem*)o; +} + +/* Given a HandleChunkElem* search through the current handle stack to find its chunk and offset. */ +static HandleChunk* +chunk_element_to_chunk_idx (HandleStack *stack, HandleChunkElem *elem, int *out_idx) +{ + HandleChunk *top = stack->top; + HandleChunk *cur = stack->bottom; + + *out_idx = 0; + + while (cur != NULL) { + HandleChunkElem *front = &cur->elems [0]; + HandleChunkElem *back = &cur->elems [cur->size]; + + if (front <= elem && elem < back) { + *out_idx = (int)(elem - front); + return cur; + } + + if (cur == top) + break; /* didn't find it. */ + cur = cur->next; + } + return NULL; +} + +#ifdef MONO_HANDLE_TRACK_OWNER +#ifdef HAVE_BACKTRACE_SYMBOLS +#define SET_BACKTRACE(btaddrs) do { \ + backtrace(btaddrs, 7); \ + } while (0) +#else +#define SET_BACKTRACE(btaddrs) 0 +#endif +#define SET_OWNER(chunk,idx) do { (chunk)->elems[(idx)].owner = owner; SET_BACKTRACE (&((chunk)->elems[(idx)].backtrace_ips[0])); } while (0) +#else +#define SET_OWNER(chunk,idx) do { } while (0) +#endif + +#ifdef MONO_HANDLE_TRACK_SP +#define SET_SP(handles,chunk,idx) do { (chunk)->elems[(idx)].alloc_sp = handles->stackmark_sp; } while (0) +#else +#define SET_SP(handles,chunk,idx) do { } while (0) +#endif + +#ifdef MONO_HANDLE_TRACK_SP +void +mono_handle_chunk_leak_check (HandleStack *handles) { + if (handles->stackmark_sp) { + /* walk back from the top to the topmost non-empty chunk */ + HandleChunk *c = handles->top; + while (c && c->size <= 0 && c != handles->bottom) { + c = c->prev; + } + if (c == NULL || c->size == 0) + return; + g_assert (c && c->size > 0); + HandleChunkElem *e = chunk_element (c, c->size - 1); + if (e->alloc_sp < handles->stackmark_sp) { + /* If we get here, the topmost object on the handle stack was + * allocated from a function that is deeper in the call stack than + * the most recent HANDLE_FUNCTION_ENTER. That means it was + * probably not wrapped in a HANDLE_FUNCTION_ENTER/_RETURN pair + * and will never be reclaimed. */ + g_warning ("Handle %p (object = %p) (allocated from \"%s\") is leaking.\n", e, e->o, +#ifdef MONO_HANDLE_TRACK_OWNER + e->owner +#else + "" +#endif + ); + } + } +} +#endif + +/* Actual handles implementation */ +MonoRawHandle +#ifndef MONO_HANDLE_TRACK_OWNER +mono_handle_new (MonoObject *obj) +#else +mono_handle_new (MonoObject *obj, const char *owner) +#endif +{ + MonoThreadInfo *info = mono_thread_info_current (); + HandleStack *handles = (HandleStack *)info->handle_stack; + HandleChunk *top = handles->top; +#ifdef MONO_HANDLE_TRACK_SP + mono_handle_chunk_leak_check (handles); +#endif + +retry: + if (G_LIKELY (top->size < OBJECTS_PER_HANDLES_CHUNK)) { + int idx = top->size; + gpointer* objslot = &top->elems [idx].o; + /* can be interrupted anywhere here, so: + * 1. make sure the new slot is null + * 2. make the new slot scannable (increment size) + * 3. put a valid object in there + * + * (have to do 1 then 3 so that if we're interrupted + * between 1 and 2, the object is still live) + */ + *objslot = NULL; + SET_OWNER (top,idx); + SET_SP (handles, top, idx); + mono_memory_write_barrier (); + top->size++; + mono_memory_write_barrier (); + *objslot = obj; + return objslot; + } + if (G_LIKELY (top->next)) { + top->next->size = 0; + /* make sure size == 0 is visible to a GC thread before it sees the new top */ + mono_memory_write_barrier (); + top = top->next; + handles->top = top; + goto retry; + } + HandleChunk *new_chunk = new_handle_chunk (); + new_chunk->size = 0; + new_chunk->prev = top; + new_chunk->next = NULL; + /* make sure size == 0 before new chunk is visible */ + mono_memory_write_barrier (); + top->next = new_chunk; + handles->top = new_chunk; + goto retry; +} + +MonoRawHandle +#ifndef MONO_HANDLE_TRACK_OWNER +mono_handle_new_interior (gpointer rawptr) +#else +mono_handle_new_interior (gpointer rawptr, const char *owner) +#endif +{ + MonoThreadInfo *info = mono_thread_info_current (); + HandleStack *handles = (HandleStack *)info->handle_stack; + HandleChunk *top = handles->interior; +#ifdef MONO_HANDLE_TRACK_SP + mono_handle_chunk_leak_check (handles); +#endif + + g_assert (top); + + /* + * Don't extend the chunk now, interior handles are + * only used for icall arguments, they shouldn't + * overflow. + */ + g_assert (top->size < OBJECTS_PER_HANDLES_CHUNK); + int idx = top->size; + gpointer *objslot = &top->elems [idx].o; + *objslot = NULL; + mono_memory_write_barrier (); + top->size++; + mono_memory_write_barrier (); + *objslot = rawptr; + SET_OWNER (top,idx); + SET_SP (handles, top, idx); + return objslot; +} + +HandleStack* +mono_handle_stack_alloc (void) +{ + HandleStack *stack = new_handle_stack (); + HandleChunk *chunk = new_handle_chunk (); + HandleChunk *interior = new_handle_chunk (); + + chunk->prev = chunk->next = NULL; + chunk->size = 0; + interior->prev = interior->next = NULL; + interior->size = 0; + mono_memory_write_barrier (); + stack->top = stack->bottom = chunk; + stack->interior = interior; +#ifdef MONO_HANDLE_TRACK_SP + stack->stackmark_sp = NULL; +#endif + return stack; +} + +void +mono_handle_stack_free (HandleStack *stack) +{ + if (!stack) + return; + HandleChunk *c = stack->bottom; + stack->top = stack->bottom = NULL; + mono_memory_write_barrier (); + while (c) { + HandleChunk *next = c->next; + free_handle_chunk (c); + c = next; + } + free_handle_chunk (c); + free_handle_chunk (stack->interior); + free_handle_stack (stack); +} + +void +mono_handle_stack_free_domain (HandleStack *stack, MonoDomain *domain) +{ + /* Called by the GC while clearing out objects of the given domain from the heap. */ + /* If there are no handles-related bugs, there is nothing to do: if a + * thread accessed objects from the domain it was aborted, so any + * threads left alive cannot have any handles that point into the + * unloading domain. However if there is a handle leak, the handle stack is not */ + if (!stack) + return; + /* Root domain only unloaded when mono is shutting down, don't need to check anything */ + if (domain == mono_get_root_domain () || mono_runtime_is_shutting_down ()) + return; + HandleChunk *cur = stack->bottom; + HandleChunk *last = stack->top; + if (!cur) + return; + while (cur) { + for (int idx = 0; idx < cur->size; ++idx) { + HandleChunkElem *elem = &cur->elems[idx]; + if (!elem->o) + continue; + g_assert (mono_object_domain (elem->o) != domain); + } + if (cur == last) + break; + cur = cur->next; + } + /* We don't examine the interior pointers here because the GC treats + * them conservatively and anyway we don't have enough information here to + * find the object's vtable. + */ +} + +static void +check_handle_stack_monotonic (HandleStack *stack) +{ + /* check that every allocated handle in the current handle stack is at no higher in the native stack than its predecessors */ +#ifdef MONO_HANDLE_TRACK_SP + HandleChunk *cur = stack->bottom; + HandleChunk *last = stack->top; + if (!cur) + return; + HandleChunkElem *prev = NULL; + gboolean monotonic = TRUE; + while (cur) { + for (int i = 0;i < cur->size; ++i) { + HandleChunkElem *elem = chunk_element (cur, i); + if (prev && elem->alloc_sp > prev->alloc_sp) { + monotonic = FALSE; +#ifdef MONO_HANDLE_TRACK_OWNER + g_warning ("Handle %p (object %p) (allocated from \"%s\") was allocated deeper in the call stack than its successor Handle %p (object %p) (allocated from \"%s\").", prev, prev->o, prev->owner, elem, elem->o, elem->owner); +#else + g_warning ("Handle %p (object %p) was allocated deeper in the call stack than its successor Handle %p (object %p).", prev, prev->o, elem, elem->o); +#endif + } + prev = elem; + } + if (cur == last) + break; + cur = cur->next; + } + g_assert (monotonic); +#endif +} + +void +mono_handle_stack_scan (HandleStack *stack, GcScanFunc func, gpointer gc_data, gboolean precise, gboolean check) +{ + if (check) /* run just once (per handle stack) per GC */ + check_handle_stack_monotonic (stack); + + /* + We're called twice - on the imprecise pass we call func to pin the + objects where the handle points to its interior. On the precise + pass, we scan all the objects where the handles point to the start of + the object. + + Note that if we're running, we know the world is stopped. + */ + if (precise) { + HandleChunk *cur = stack->bottom; + HandleChunk *last = stack->top; + + while (cur) { + for (int i = 0; i < cur->size; ++i) { + HandleChunkElem* elem = chunk_element (cur, i); + gpointer* obj_slot = &elem->o; + if (*obj_slot != NULL) + func (obj_slot, gc_data); + } + if (cur == last) + break; + cur = cur->next; + } + } else { + HandleChunk *cur = stack->interior; + + if (!cur) + return; + for (int i = 0; i < cur->size; ++i) { + HandleChunkElem* elem = chunk_element (cur, i); + gpointer* ptr_slot = &elem->o; + if (*ptr_slot != NULL) + func (ptr_slot, gc_data); + } + } +} + +void +mono_stack_mark_record_size (MonoThreadInfo *info, HandleStackMark *stackmark, const char *func_name) +{ + HandleStack *handles = (HandleStack *)info->handle_stack; + HandleChunk *cur = stackmark->chunk; + int size = -stackmark->size; //discard the starting point of the stack + while (cur) { + size += cur->size; + if (cur == handles->top) + break; + cur = cur->next; + } + + if (size > THIS_IS_AN_OK_NUMBER_OF_HANDLES) + g_warning ("%s USED %d handles\n", func_name, size); +} + +/* + * Pop the stack until @stackmark and make @value the top value. + * + * @return the new handle for what @value points to + */ +MonoRawHandle +mono_stack_mark_pop_value (MonoThreadInfo *info, HandleStackMark *stackmark, MonoRawHandle value) +{ + MonoObject *obj = value ? *((MonoObject**)value) : NULL; + mono_stack_mark_pop (info, stackmark); +#ifndef MONO_HANDLE_TRACK_OWNER + return mono_handle_new (obj); +#else + return mono_handle_new (obj, ""); +#endif +} + +/* Temporary place for some of the handle enabled wrapper functions*/ + +MonoStringHandle +mono_string_new_handle (MonoDomain *domain, const char *data, MonoError *error) +{ + return MONO_HANDLE_NEW (MonoString, mono_string_new_checked (domain, data, error)); +} + +MonoArrayHandle +mono_array_new_handle (MonoDomain *domain, MonoClass *eclass, uintptr_t n, MonoError *error) +{ + return MONO_HANDLE_NEW (MonoArray, mono_array_new_checked (domain, eclass, n, error)); +} + +MonoArrayHandle +mono_array_new_full_handle (MonoDomain *domain, MonoClass *array_class, uintptr_t *lengths, intptr_t *lower_bounds, MonoError *error) +{ + return MONO_HANDLE_NEW (MonoArray, mono_array_new_full_checked (domain, array_class, lengths, lower_bounds, error)); +} + +#ifdef ENABLE_CHECKED_BUILD +/* Checked build helpers */ +void +mono_handle_verify (MonoRawHandle raw_handle) +{ + +} +#endif + +uintptr_t +mono_array_handle_length (MonoArrayHandle arr) +{ + MONO_REQ_GC_UNSAFE_MODE; + + return MONO_HANDLE_RAW (arr)->max_length; +} + +uint32_t +mono_gchandle_from_handle (MonoObjectHandle handle, mono_bool pinned) +{ + /* FIXME: chunk_element_to_chunk_idx does a linear search through the + * chunks and we only need it for the assert */ + MonoThreadInfo *info = mono_thread_info_current (); + HandleStack *stack = (HandleStack*) info->handle_stack; + HandleChunkElem* elem = handle_to_chunk_element (handle); + int elem_idx = 0; + HandleChunk *chunk = chunk_element_to_chunk_idx (stack, elem, &elem_idx); + /* gchandles cannot deal with interior pointers */ + g_assert (chunk != NULL); + return mono_gchandle_new (MONO_HANDLE_RAW (handle), pinned); +} + +MonoObjectHandle +mono_gchandle_get_target_handle (uint32_t gchandle) +{ + return MONO_HANDLE_NEW (MonoObject, mono_gchandle_get_target (gchandle)); +} + +gpointer +mono_array_handle_pin_with_size (MonoArrayHandle handle, int size, uintptr_t idx, uint32_t *gchandle) +{ + g_assert (gchandle != NULL); + *gchandle = mono_gchandle_from_handle (MONO_HANDLE_CAST(MonoObject,handle), TRUE); + MonoArray *raw = MONO_HANDLE_RAW (handle); + return mono_array_addr_with_size (raw, size, idx); +} + +gunichar2* +mono_string_handle_pin_chars (MonoStringHandle handle, uint32_t *gchandle) +{ + g_assert (gchandle != NULL); + *gchandle = mono_gchandle_from_handle (MONO_HANDLE_CAST (MonoObject, handle), TRUE); + MonoString *raw = MONO_HANDLE_RAW (handle); + return mono_string_chars (raw); +} + +gpointer +mono_object_handle_pin_unbox (MonoObjectHandle obj, uint32_t *gchandle) +{ + g_assert (!MONO_HANDLE_IS_NULL (obj)); + MonoClass *klass = mono_handle_class (obj); + g_assert (klass->valuetype); + *gchandle = mono_gchandle_from_handle (obj, TRUE); + return mono_object_unbox (MONO_HANDLE_RAW (obj)); +} + +void +mono_array_handle_memcpy_refs (MonoArrayHandle dest, uintptr_t dest_idx, MonoArrayHandle src, uintptr_t src_idx, uintptr_t len) +{ + mono_array_memcpy_refs (MONO_HANDLE_RAW (dest), dest_idx, MONO_HANDLE_RAW (src), src_idx, len); +} + +gboolean +mono_handle_stack_is_empty (HandleStack *stack) +{ + return (stack->top == stack->bottom && stack->top->size == 0); +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/handle.h b/unity-2019.4.24f1-mbe/mono/metadata/handle.h new file mode 100644 index 000000000..f4d965029 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/handle.h @@ -0,0 +1,568 @@ +/** + * \file + * Handle to object in native code + * + * Authors: + * - Ludovic Henry + * - Aleksey Klieger + * - Rodrigo Kumpera + * + * Copyright 2016 Dot net foundation. + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#ifndef __MONO_HANDLE_H__ +#define __MONO_HANDLE_H__ + +#include +#include + +#include +#include +#include +#include +#include + +G_BEGIN_DECLS + +/* +Handle stack. + +The handle stack is designed so it's efficient to pop a large amount of entries at once. +The stack is made out of a series of fixed size segments. + +To do bulk operations you use a stack mark. + +*/ + +/* +3 is the number of fields besides the data in the struct; +128 words makes each chunk 512 or 1024 bytes each +*/ +#define OBJECTS_PER_HANDLES_CHUNK (128 - 3) + +/* +Whether this config needs stack watermark recording to know where to start scanning from. +*/ +#ifdef HOST_WATCHOS +#define MONO_NEEDS_STACK_WATERMARK 1 +#endif + +typedef struct _HandleChunk HandleChunk; + +/* + * Define MONO_HANDLE_TRACK_OWNER to store the file and line number of each call to MONO_HANDLE_NEW + * in the handle stack. (This doubles the amount of memory used for handles, so it's only useful for debugging). + */ +/*#define MONO_HANDLE_TRACK_OWNER*/ + +/* + * Define MONO_HANDLE_TRACK_SP to record the C stack pointer at the time of each HANDLE_FUNCTION_ENTER and + * to ensure that when a new handle is allocated the previous newest handle is not lower in the stack. + * This is useful to catch missing HANDLE_FUNCTION_ENTER / HANDLE_FUNCTION_RETURN pairs which could cause + * handle leaks. + * + * If defined, keep HandleStackMark in sync in RuntimeStructs.cs + */ +/*#define MONO_HANDLE_TRACK_SP*/ + +typedef struct { + gpointer o; /* MonoObject ptr or interior ptr */ +#ifdef MONO_HANDLE_TRACK_OWNER + const char *owner; + gpointer backtrace_ips[7]; /* result of backtrace () at time of allocation */ +#endif +#ifdef MONO_HANDLE_TRACK_SP + gpointer alloc_sp; /* sp from HandleStack:stackmark_sp at time of allocation */ +#endif +} HandleChunkElem; + +struct _HandleChunk { + int size; //number of handles + HandleChunk *prev, *next; + HandleChunkElem elems [OBJECTS_PER_HANDLES_CHUNK]; +}; + +typedef struct { + HandleChunk *top; //alloc from here + HandleChunk *bottom; //scan from here +#ifdef MONO_HANDLE_TRACK_SP + gpointer stackmark_sp; // C stack pointer top when from most recent mono_stack_mark_init +#endif + /* Chunk for storing interior pointers. Not extended right now */ + HandleChunk *interior; +} HandleStack; + +// Keep this in sync with RuntimeStructs.cs +typedef struct { + int size, interior_size; + HandleChunk *chunk; +#ifdef MONO_HANDLE_TRACK_SP + gpointer prev_sp; // C stack pointer from prior mono_stack_mark_init +#endif +} HandleStackMark; + +typedef void *MonoRawHandle; + +typedef void (*GcScanFunc) (gpointer*, gpointer); + + +/* If Centrinel is analyzing Mono, use the SUPPRESS macros to mark the bodies + * of the handle macros as allowed to perform operations on raw pointers to + * managed objects. Take care to UNSUPPRESS the _arguments_ to the macros - we + * want warnings if the argument uses pointers unsafely. + */ +#ifdef __CENTRINEL__ +#define MONO_HANDLE_SUPPRESS_SCOPE(b) __CENTRINEL_SUPPRESS_SCOPE(b) +#define MONO_HANDLE_SUPPRESS(expr) __CENTRINEL_SUPPRESS(expr) +#define MONO_HANDLE_UNSUPPRESS(expr) __CENTRINEL_UNSUPPRESS(expr) +#else +#define MONO_HANDLE_SUPPRESS_SCOPE(b) ; +#define MONO_HANDLE_SUPPRESS(expr) (expr) +#define MONO_HANDLE_UNSUPPRESS(expr) (expr) +#endif + +#ifndef MONO_HANDLE_TRACK_OWNER +MonoRawHandle mono_handle_new (MonoObject *object); +MonoRawHandle mono_handle_new_full (gpointer rawptr, gboolean interior); +MonoRawHandle mono_handle_new_interior (gpointer rawptr); +#else +MonoRawHandle mono_handle_new (MonoObject *object, const char* owner); +MonoRawHandle mono_handle_new_full (gpointer rawptr, gboolean interior, const char *owner); +MonoRawHandle mono_handle_new_interior (gpointer rawptr, const char *owner); +#endif + +void mono_handle_stack_scan (HandleStack *stack, GcScanFunc func, gpointer gc_data, gboolean precise, gboolean check); +gboolean mono_handle_stack_is_empty (HandleStack *stack); +HandleStack* mono_handle_stack_alloc (void); +void mono_handle_stack_free (HandleStack *handlestack); +MonoRawHandle mono_stack_mark_pop_value (MonoThreadInfo *info, HandleStackMark *stackmark, MonoRawHandle value); +void mono_stack_mark_record_size (MonoThreadInfo *info, HandleStackMark *stackmark, const char *func_name); +void mono_handle_stack_free_domain (HandleStack *stack, MonoDomain *domain); + +#ifdef MONO_HANDLE_TRACK_SP +void mono_handle_chunk_leak_check (HandleStack *handles); +#endif + +static inline void +mono_stack_mark_init (MonoThreadInfo *info, HandleStackMark *stackmark) +{ +#ifdef MONO_HANDLE_TRACK_SP + gpointer sptop = (gpointer)(intptr_t)&stackmark; +#endif + HandleStack *handles = (HandleStack *)info->handle_stack; + stackmark->size = handles->top->size; + stackmark->chunk = handles->top; + stackmark->interior_size = handles->interior->size; +#ifdef MONO_HANDLE_TRACK_SP + stackmark->prev_sp = handles->stackmark_sp; + handles->stackmark_sp = sptop; +#endif +} + +static inline void +mono_stack_mark_pop (MonoThreadInfo *info, HandleStackMark *stackmark) +{ + HandleStack *handles = (HandleStack *)info->handle_stack; + HandleChunk *old_top = stackmark->chunk; + old_top->size = stackmark->size; + mono_memory_write_barrier (); + handles->top = old_top; + handles->interior->size = stackmark->interior_size; +#ifdef MONO_HANDLE_TRACK_SP + mono_memory_write_barrier (); /* write to top before prev_sp */ + handles->stackmark_sp = stackmark->prev_sp; +#endif +} + +/* +Icall macros +*/ +#define SETUP_ICALL_COMMON \ + do { \ + MonoError error; \ + MonoThreadInfo *__info = mono_thread_info_current (); \ + error_init (&error); \ + +#define CLEAR_ICALL_COMMON \ + mono_error_set_pending_exception (&error); + +#define SETUP_ICALL_FRAME \ + HandleStackMark __mark; \ + mono_stack_mark_init (__info, &__mark); + +#define CLEAR_ICALL_FRAME \ + mono_stack_mark_record_size (__info, &__mark, __FUNCTION__); \ + mono_stack_mark_pop (__info, &__mark); + +#define CLEAR_ICALL_FRAME_VALUE(RESULT, HANDLE) \ + mono_stack_mark_record_size (__info, &__mark, __FUNCTION__); \ + (RESULT) = mono_stack_mark_pop_value (__info, &__mark, (HANDLE)); + + +#define HANDLE_FUNCTION_ENTER() do { \ + MonoThreadInfo *__info = mono_thread_info_current (); \ + SETUP_ICALL_FRAME \ + +#define HANDLE_FUNCTION_RETURN() \ + CLEAR_ICALL_FRAME; \ + } while (0) + +#define HANDLE_FUNCTION_RETURN_VAL(VAL) \ + CLEAR_ICALL_FRAME; \ + return (VAL); \ + } while (0) + +#define HANDLE_FUNCTION_RETURN_OBJ(HANDLE) \ + do { \ + void* __result = (MONO_HANDLE_RAW (HANDLE)); \ + CLEAR_ICALL_FRAME; \ + return __result; \ + } while (0); } while (0); + +#define HANDLE_FUNCTION_RETURN_REF(TYPE, HANDLE) \ + do { \ + MonoRawHandle __result; \ + CLEAR_ICALL_FRAME_VALUE (__result, ((MonoRawHandle) (HANDLE))); \ + return MONO_HANDLE_CAST (TYPE, __result); \ + } while (0); } while (0); + +#ifdef MONO_NEEDS_STACK_WATERMARK + +static void +mono_thread_info_pop_stack_mark (MonoThreadInfo *info, void *old_mark) +{ + info->stack_mark = old_mark; +} + +static void* +mono_thread_info_push_stack_mark (MonoThreadInfo *info, void *mark) +{ + void *old = info->stack_mark; + info->stack_mark = mark; + return old; +} + +#define SETUP_STACK_WATERMARK \ + int __dummy; \ + __builtin_unwind_init (); \ + void *__old_stack_mark = mono_thread_info_push_stack_mark (__info, &__dummy); + +#define CLEAR_STACK_WATERMARK \ + mono_thread_info_pop_stack_mark (__info, __old_stack_mark); + +#else +#define SETUP_STACK_WATERMARK +#define CLEAR_STACK_WATERMARK +#endif + +#define ICALL_ENTRY() \ + SETUP_ICALL_COMMON \ + SETUP_ICALL_FRAME \ + SETUP_STACK_WATERMARK + +#define ICALL_RETURN() \ + do { \ + CLEAR_STACK_WATERMARK \ + CLEAR_ICALL_COMMON \ + CLEAR_ICALL_FRAME \ + return; \ + } while (0); } while (0) + +#define ICALL_RETURN_VAL(VAL) \ + do { \ + CLEAR_STACK_WATERMARK \ + CLEAR_ICALL_COMMON \ + CLEAR_ICALL_FRAME \ + return VAL; \ + } while (0); } while (0) + +#define ICALL_RETURN_OBJ(HANDLE) \ + do { \ + CLEAR_STACK_WATERMARK \ + CLEAR_ICALL_COMMON \ + void* __ret = MONO_HANDLE_RAW (HANDLE); \ + CLEAR_ICALL_FRAME \ + 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 +*/ + +#ifdef ENABLE_CHECKED_BUILD +void mono_handle_verify (MonoRawHandle handle); +#define HANDLE_INVARIANTS(H) mono_handle_verify((void*)(H)) +#else +#define HANDLE_INVARIANTS(H) (0) +#endif + +#define TYPED_HANDLE_PAYLOAD_NAME(TYPE) TYPE ## HandlePayload +#define TYPED_HANDLE_NAME(TYPE) TYPE ## Handle +#define TYPED_OUT_HANDLE_NAME(TYPE) TYPE ## HandleOut + +#ifdef MONO_HANDLE_TRACK_OWNER +#define STRINGIFY_(x) #x +#define STRINGIFY(x) STRINGIFY_(x) +#define HANDLE_OWNER_STRINGIFY(file,lineno) (const char*) (file ":" STRINGIFY(lineno)) +#endif + + +/* + * TYPED_HANDLE_DECL(SomeType): + * Expands to a decl for handles to SomeType and to an internal payload struct. + * + * For example, TYPED_HANDLE_DECL(MonoObject) (see below) expands to: + * + * typedef struct { + * MonoObject *__raw; + * } MonoObjectHandlePayload; + * + * typedef MonoObjectHandlePayload* MonoObjectHandle; + * typedef MonoObjectHandlePayload* MonoObjectHandleOut; + */ +#define TYPED_HANDLE_DECL(TYPE) \ + typedef struct { TYPE *__raw; } TYPED_HANDLE_PAYLOAD_NAME (TYPE) ; \ + typedef TYPED_HANDLE_PAYLOAD_NAME (TYPE) * TYPED_HANDLE_NAME (TYPE); \ + typedef TYPED_HANDLE_PAYLOAD_NAME (TYPE) * TYPED_OUT_HANDLE_NAME (TYPE) +/* + * TYPED_VALUE_HANDLE_DECL(SomeType): + * Expands to a decl for handles to SomeType (which is a managed valuetype (likely a struct) of some sort) and to an internal payload struct. + * For example TYPED_HANDLE_DECL(MonoMethodInfo) expands to: + * + * typedef struct { + * MonoMethodInfo *__raw; + * } MonoMethodInfoHandlePayload; + * typedef MonoMethodInfoHandlePayload* MonoMethodInfoHandle; + */ +#define TYPED_VALUE_HANDLE_DECL(TYPE) TYPED_HANDLE_DECL(TYPE) + +/* Have to double expand because MONO_STRUCT_OFFSET is doing token pasting on cross-compilers. */ +#define MONO_HANDLE_PAYLOAD_OFFSET_(PayloadType) MONO_STRUCT_OFFSET(PayloadType, __raw) +#define MONO_HANDLE_PAYLOAD_OFFSET(TYPE) MONO_HANDLE_PAYLOAD_OFFSET_(TYPED_HANDLE_PAYLOAD_NAME (TYPE)) + +#define MONO_HANDLE_INIT ((void*) mono_null_value_handle) +#define NULL_HANDLE mono_null_value_handle + +//XXX add functions to get/set raw, set field, set field to null, set array, set array to null +#define MONO_HANDLE_RAW(HANDLE) (HANDLE_INVARIANTS (HANDLE), ((HANDLE)->__raw)) +#define MONO_HANDLE_DCL(TYPE, NAME) TYPED_HANDLE_NAME(TYPE) NAME = MONO_HANDLE_NEW (TYPE, (NAME ## _raw)) + +#ifndef MONO_HANDLE_TRACK_OWNER +#define MONO_HANDLE_NEW(TYPE, VALUE) (TYPED_HANDLE_NAME(TYPE))( mono_handle_new ((MonoObject*)(VALUE)) ) +#else +#define MONO_HANDLE_NEW(TYPE, VALUE) (TYPED_HANDLE_NAME(TYPE))( mono_handle_new ((MonoObject*)(VALUE), HANDLE_OWNER_STRINGIFY(__FILE__, __LINE__))) +#endif + +#define MONO_HANDLE_CAST(TYPE, VALUE) (TYPED_HANDLE_NAME(TYPE))( VALUE ) + +#define MONO_HANDLE_IS_NULL(HANDLE) (mono_handle_is_null (HANDLE)) + + +/* +WARNING WARNING WARNING + +The following functions require a particular evaluation ordering to ensure correctness. +We must not have exposed handles while any sort of evaluation is happening as that very evaluation might trigger +a safepoint and break us. + +This is why we evaluate index and value before any call to MONO_HANDLE_RAW or other functions that deal with naked objects. +*/ +#define MONO_HANDLE_SETRAW(HANDLE, FIELD, VALUE) do { \ + MONO_HANDLE_SUPPRESS_SCOPE(1); \ + MonoObject *__val = MONO_HANDLE_SUPPRESS ((MonoObject*)(MONO_HANDLE_UNSUPPRESS (VALUE))); \ + MONO_OBJECT_SETREF (MONO_HANDLE_RAW (MONO_HANDLE_UNSUPPRESS (HANDLE)), FIELD, __val); \ + } while (0) + +#define MONO_HANDLE_SET(HANDLE, FIELD, VALUE) do { \ + MonoObjectHandle __val = MONO_HANDLE_CAST (MonoObject, VALUE); \ + do { \ + MONO_HANDLE_SUPPRESS_SCOPE(1); \ + MONO_OBJECT_SETREF (MONO_HANDLE_RAW (MONO_HANDLE_UNSUPPRESS (HANDLE)), FIELD, MONO_HANDLE_RAW (__val)); \ + } while (0); \ + } while (0) + +/* N.B. RESULT is evaluated before HANDLE */ +#define MONO_HANDLE_GET(RESULT, HANDLE, FIELD) do { \ + MonoObjectHandle __dest = MONO_HANDLE_CAST(MonoObject, RESULT); \ + MONO_HANDLE_SUPPRESS (mono_gc_wbarrier_generic_store (&__dest->__raw, (MonoObject*)(MONO_HANDLE_RAW(MONO_HANDLE_UNSUPPRESS (HANDLE))->FIELD))); \ + } while (0) + +#define MONO_HANDLE_NEW_GET(TYPE,HANDLE,FIELD) (MONO_HANDLE_NEW(TYPE,MONO_HANDLE_SUPPRESS (MONO_HANDLE_RAW (MONO_HANDLE_UNSUPPRESS (HANDLE))->FIELD))) + +#define MONO_HANDLE_GETVAL(HANDLE, FIELD) MONO_HANDLE_SUPPRESS (MONO_HANDLE_RAW (MONO_HANDLE_UNSUPPRESS (HANDLE))->FIELD) + +/* VS doesn't support typeof :( :( :( */ +#define MONO_HANDLE_SETVAL(HANDLE, FIELD, TYPE, VALUE) do { \ + TYPE __val = (VALUE); \ + MONO_HANDLE_SUPPRESS (MONO_HANDLE_RAW (MONO_HANDLE_UNSUPPRESS (HANDLE))->FIELD = __val); \ + } while (0) + +#define MONO_HANDLE_ARRAY_SETREF(HANDLE, IDX, VALUE) do { \ + int __idx = (IDX); \ + MonoObjectHandle __val = MONO_HANDLE_CAST (MonoObject, VALUE); \ + do { \ + MONO_HANDLE_SUPPRESS_SCOPE(1); \ + mono_array_setref_fast (MONO_HANDLE_RAW (MONO_HANDLE_UNSUPPRESS (HANDLE)), __idx, MONO_HANDLE_RAW (__val)); \ + } while (0); \ + } while (0) + +#define MONO_HANDLE_ARRAY_SETVAL(HANDLE, TYPE, IDX, VALUE) do { \ + int __idx = (IDX); \ + TYPE __val = (VALUE); \ + do { \ + MONO_HANDLE_SUPPRESS_SCOPE(1); \ + mono_array_set (MONO_HANDLE_RAW (MONO_HANDLE_UNSUPPRESS (HANDLE)), TYPE, __idx, __val); \ + } while (0); \ + } while (0) + +#define MONO_HANDLE_ARRAY_SETRAW(HANDLE, IDX, VALUE) do { \ + MONO_HANDLE_SUPPRESS_SCOPE(1); \ + int __idx = MONO_HANDLE_UNSUPPRESS(IDX); \ + MonoObject *__val = (MonoObject*)(VALUE); \ + mono_array_setref_fast (MONO_HANDLE_RAW (MONO_HANDLE_UNSUPPRESS (HANDLE)), __idx, __val); \ + } while (0) + +/* N.B. DEST is evaluated AFTER all the other arguments */ +#define MONO_HANDLE_ARRAY_GETVAL(DEST, HANDLE, TYPE, IDX) do { \ + MonoArrayHandle __arr = (HANDLE); \ + int __idx = (IDX); \ + TYPE __result = MONO_HANDLE_SUPPRESS (mono_array_get (MONO_HANDLE_RAW(__arr), TYPE, __idx)); \ + (DEST) = __result; \ + } while (0) + +#define MONO_HANDLE_ARRAY_GETREF(DEST, HANDLE, IDX) do { \ + mono_handle_array_getref (MONO_HANDLE_CAST(MonoObject, (DEST)), (HANDLE), (IDX)); \ + } while (0) + +#define MONO_HANDLE_ASSIGN(DESTH, SRCH) \ + mono_handle_assign (MONO_HANDLE_CAST (MonoObject, (DESTH)), MONO_HANDLE_CAST(MonoObject, (SRCH))) + +#define MONO_HANDLE_DOMAIN(HANDLE) MONO_HANDLE_SUPPRESS (mono_object_domain (MONO_HANDLE_RAW (MONO_HANDLE_CAST (MonoObject, MONO_HANDLE_UNSUPPRESS (HANDLE))))) + +/* Given an object and a MonoClassField, return the value (must be non-object) + * of the field. It's the caller's responsibility to check that the object is + * of the correct class. */ +#define MONO_HANDLE_GET_FIELD_VAL(HANDLE,TYPE,FIELD) *(TYPE *)(mono_handle_unsafe_field_addr (MONO_HANDLE_CAST (MonoObject, (HANDLE)), (FIELD))) + +#define MONO_HANDLE_NEW_GET_FIELD(HANDLE,TYPE,FIELD) MONO_HANDLE_NEW (TYPE, MONO_HANDLE_SUPPRESS (*(TYPE**)(mono_handle_unsafe_field_addr (MONO_HANDLE_CAST (MonoObject, MONO_HANDLE_UNSUPPRESS (HANDLE)), (FIELD))))) + +#define MONO_HANDLE_SET_FIELD_VAL(HANDLE,TYPE,FIELD,VAL) do { \ + MonoObjectHandle __obj = (HANDLE); \ + MonoClassField *__field = (FIELD); \ + TYPE __value = (VAL); \ + *(TYPE*)(mono_handle_unsafe_field_addr (__obj, __field)) = __value; \ + } while (0) + +#define MONO_HANDLE_SET_FIELD_REF(HANDLE,FIELD,VALH) do { \ + MonoObjectHandle __obj = MONO_HANDLE_CAST (MonoObject, (HANDLE)); \ + MonoClassField *__field = (FIELD); \ + MonoObjectHandle __value = MONO_HANDLE_CAST (MonoObject, (VALH)); \ + MONO_HANDLE_SUPPRESS (mono_gc_wbarrier_generic_store (mono_handle_unsafe_field_addr (__obj, __field), MONO_HANDLE_RAW (__value))); \ + } while (0) + +/* Baked typed handles we all want */ +TYPED_HANDLE_DECL (MonoString); +TYPED_HANDLE_DECL (MonoArray); +TYPED_HANDLE_DECL (MonoObject); +TYPED_HANDLE_DECL (MonoException); +TYPED_HANDLE_DECL (MonoAppContext); + +/* Unfortunately MonoThreadHandle is already a typedef used for something unrelated. So + * the coop handle for MonoThread* is MonoThreadObjectHandle. + */ +typedef MonoThread MonoThreadObject; +TYPED_HANDLE_DECL (MonoThreadObject); + +#define NULL_HANDLE_STRING MONO_HANDLE_CAST(MonoString, NULL_HANDLE) + +/* +This is the constant for a handle that points nowhere. +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) +{ + MONO_HANDLE_SUPPRESS (mono_gc_wbarrier_generic_store (&dest->__raw, src ? MONO_HANDLE_RAW(src) : NULL)); +} + +/* It is unsafe to call this function directly - it does not pin the handle! Use MONO_HANDLE_GET_FIELD_VAL(). */ +static inline gchar* +mono_handle_unsafe_field_addr (MonoObjectHandle h, MonoClassField *field) +{ + return MONO_HANDLE_SUPPRESS (((gchar *)MONO_HANDLE_RAW (h)) + field->offset); +} + +//FIXME this should go somewhere else +MonoStringHandle mono_string_new_handle (MonoDomain *domain, const char *data, MonoError *error); +MonoArrayHandle mono_array_new_handle (MonoDomain *domain, MonoClass *eclass, uintptr_t n, MonoError *error); +MonoArrayHandle +mono_array_new_full_handle (MonoDomain *domain, MonoClass *array_class, uintptr_t *lengths, intptr_t *lower_bounds, MonoError *error); + + +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),MonoObject*, index))); +} + +#define mono_handle_class(o) MONO_HANDLE_SUPPRESS (mono_object_class (MONO_HANDLE_RAW (MONO_HANDLE_UNSUPPRESS (o)))) + +/* Local handles to global GC handles and back */ + +uint32_t +mono_gchandle_from_handle (MonoObjectHandle handle, mono_bool pinned); + +MonoObjectHandle +mono_gchandle_get_target_handle (uint32_t gchandle); + +void +mono_array_handle_memcpy_refs (MonoArrayHandle dest, uintptr_t dest_idx, MonoArrayHandle src, uintptr_t src_idx, uintptr_t len); + +/* Pins the MonoArray using a gchandle and returns a pointer to the + * element with the given index (where each element is of the given + * size. Call mono_gchandle_free to unpin. + */ +gpointer +mono_array_handle_pin_with_size (MonoArrayHandle handle, int size, uintptr_t index, uint32_t *gchandle); + +#define MONO_ARRAY_HANDLE_PIN(handle,type,index,gchandle_out) mono_array_handle_pin_with_size (MONO_HANDLE_CAST(MonoArray,(handle)), sizeof (type), (index), (gchandle_out)) + +gunichar2 * +mono_string_handle_pin_chars (MonoStringHandle s, uint32_t *gchandle_out); + +gpointer +mono_object_handle_pin_unbox (MonoObjectHandle boxed_valuetype_obj, uint32_t *gchandle_out); + +void +mono_error_set_exception_handle (MonoError *error, MonoExceptionHandle exc); + +MonoAppContextHandle +mono_context_get_handle (void); + +void +mono_context_set_handle (MonoAppContextHandle new_context); + +G_END_DECLS + +#endif /* __MONO_HANDLE_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/icall-def.h b/unity-2019.4.24f1-mbe/mono/metadata/icall-def.h new file mode 100644 index 000000000..8efe62c3f --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/icall-def.h @@ -0,0 +1,1079 @@ +/** + * \file + * This file contains the default set of the mono internal calls. + * Each type that has internal call methods must be declared here + * with the ICALL_TYPE macro as follows: + * + * ICALL_TYPE(typeid, typename, first_icall_id) + * + * typeid must be a C symbol name unique to the type, don't worry about namespace + * pollution, since it will be automatically prefixed to avoid it. + * typename is a C string containing the full name of the type + * first_icall_id s the symbol ID of the first internal call of the declared + * type (see below) + * + * The list of internal calls of the methods of a type must follow the + * type declaration. Each internal call is defined by the following macro: + * + * ICALL(icallid, methodname, cfuncptr) + * + * icallid must be a C symbol, unique for each icall defined in this file and + * tipically equal to the typeid + '_' + a sequential number. + * methodname is a C string defining the method name and the optional signature + * (the signature is required only when several internal calls in the type + * have the same name) + * cfuncptr is the C function that implements the internal call. Note that this + * file is included at the end of metadata/icall.c, so the C function must be + * visible to the compiler there. + * + * *** Adding a new internal call *** + * Remember that ICALL_TYPE declarations must be kept sorted wrt each other + * ICALL_TYPE declaration. The same happens for ICALL declarations, but only + * limited to the icall list of each type. The sorting is based on the type or + * method name. + * When adding a new icall, make sure it is inserted correctly in the list and + * that it defines a unique ID. ID are currently numbered and ordered, but if + * you need to insert a method in the middle, don't bother renaming all the symbols. + * Remember to change also the first_icall_id argument in the ICALL_TYPE + * declaration if you add a new icall at the beginning of a type's icall list. + * + * + * *** (Experimental) Cooperative GC support via Handles and MonoError *** + * An icall can use the coop GC handles infrastructure from handles.h to avoid some + * boilerplate when manipulating managed objects from runtime code and to use MonoError for + * threading exceptions out to managed callerrs: + * + * HANDLES(ICALL(icallid, methodname, cfuncptr)) + * + * An icall with a HANDLES() declaration wrapped around it will have a generated wrapper + * that: + * (1) Updates the coop handle stack on entry and exit + * (2) Call the cfuncptr with a new signature: + * (a) All managed object reference in arguments will be wrapped in a handle + * (ie, MonoString* becomes MonoStringHandle) + * (b) the same for the return value (MonoObject* return becomes MonoObjectHandle) + * (c) An additional final argument is added of type MonoError* + * example: class object { + * [MethodImplOptions(InternalCall)] + * String some_icall (object[] x); + * } + * should be implemented as: + * MonoStringHandle some_icall (MonoObjectHandle this_handle, MonoArrayHandle x_handle, MonoError *error); + * (3) The wrapper will automatically call mono_error_set_pending_exception (error) and raise the resulting exception. + * Note: valuetypes use the same calling convention as normal. + * Limitations: "out" and "ref" arguments are not supported yet. + */ + +#ifndef DISABLE_PROCESS_HANDLING +ICALL_TYPE(NATIVEMETHODS, "Microsoft.Win32.NativeMethods", NATIVEMETHODS_1) +ICALL(NATIVEMETHODS_1, "CloseProcess", ves_icall_Microsoft_Win32_NativeMethods_CloseProcess) +ICALL(NATIVEMETHODS_2, "GetCurrentProcess", ves_icall_Microsoft_Win32_NativeMethods_GetCurrentProcess) +ICALL(NATIVEMETHODS_3, "GetCurrentProcessId", ves_icall_Microsoft_Win32_NativeMethods_GetCurrentProcessId) +ICALL(NATIVEMETHODS_4, "GetExitCodeProcess", ves_icall_Microsoft_Win32_NativeMethods_GetExitCodeProcess) +ICALL(NATIVEMETHODS_5, "GetPriorityClass", ves_icall_Microsoft_Win32_NativeMethods_GetPriorityClass) +ICALL(NATIVEMETHODS_6, "GetProcessTimes", ves_icall_Microsoft_Win32_NativeMethods_GetProcessTimes) +ICALL(NATIVEMETHODS_7, "GetProcessWorkingSetSize", ves_icall_Microsoft_Win32_NativeMethods_GetProcessWorkingSetSize) +ICALL(NATIVEMETHODS_8, "SetPriorityClass", ves_icall_Microsoft_Win32_NativeMethods_SetPriorityClass) +ICALL(NATIVEMETHODS_9, "SetProcessWorkingSetSize", ves_icall_Microsoft_Win32_NativeMethods_SetProcessWorkingSetSize) +ICALL(NATIVEMETHODS_10, "TerminateProcess", ves_icall_Microsoft_Win32_NativeMethods_TerminateProcess) +ICALL(NATIVEMETHODS_11, "WaitForInputIdle", ves_icall_Microsoft_Win32_NativeMethods_WaitForInputIdle) +#endif /* !DISABLE_PROCESS_HANDLING */ + +#ifndef DISABLE_COM +ICALL_TYPE(COMPROX, "Mono.Interop.ComInteropProxy", COMPROX_1) +ICALL(COMPROX_1, "AddProxy", ves_icall_Mono_Interop_ComInteropProxy_AddProxy) +ICALL(COMPROX_2, "FindProxy", ves_icall_Mono_Interop_ComInteropProxy_FindProxy) +#endif + +ICALL_TYPE(TLS_PROVIDER_FACTORY, "Mono.Net.Security.MonoTlsProviderFactory", TLS_PROVIDER_FACTORY_1) +ICALL(TLS_PROVIDER_FACTORY_1, "IsBtlsSupported", ves_icall_Mono_TlsProviderFactory_IsBtlsSupported) + +ICALL_TYPE(RUNTIME, "Mono.Runtime", RUNTIME_1) +HANDLES(ICALL(RUNTIME_1, "GetDisplayName", ves_icall_Mono_Runtime_GetDisplayName)) +HANDLES(ICALL(RUNTIME_12, "GetNativeStackTrace", ves_icall_Mono_Runtime_GetNativeStackTrace)) + +ICALL_TYPE(RTCLASS, "Mono.RuntimeClassHandle", RTCLASS_1) +ICALL(RTCLASS_1, "GetTypeFromClass", ves_icall_Mono_RuntimeClassHandle_GetTypeFromClass) + +ICALL_TYPE(RTPTRARRAY, "Mono.RuntimeGPtrArrayHandle", RTPTRARRAY_1) +ICALL(RTPTRARRAY_1, "GPtrArrayFree", ves_icall_Mono_RuntimeGPtrArrayHandle_GPtrArrayFree) + +ICALL_TYPE(RTMARSHAL, "Mono.RuntimeMarshal", RTMARSHAL_1) +ICALL(RTMARSHAL_1, "FreeAssemblyName", ves_icall_Mono_RuntimeMarshal_FreeAssemblyName) + +ICALL_TYPE(SAFESTRMARSHAL, "Mono.SafeStringMarshal", SAFESTRMARSHAL_1) +ICALL(SAFESTRMARSHAL_1, "GFree", ves_icall_Mono_SafeStringMarshal_GFree) +ICALL(SAFESTRMARSHAL_2, "StringToUtf8", ves_icall_Mono_SafeStringMarshal_StringToUtf8) + +#ifndef PLATFORM_RO_FS +ICALL_TYPE(KPAIR, "Mono.Security.Cryptography.KeyPairPersistence", KPAIR_1) +ICALL(KPAIR_1, "_CanSecure", ves_icall_Mono_Security_Cryptography_KeyPairPersistence_CanSecure) +ICALL(KPAIR_2, "_IsMachineProtected", ves_icall_Mono_Security_Cryptography_KeyPairPersistence_IsMachineProtected) +ICALL(KPAIR_3, "_IsUserProtected", ves_icall_Mono_Security_Cryptography_KeyPairPersistence_IsUserProtected) +ICALL(KPAIR_4, "_ProtectMachine", ves_icall_Mono_Security_Cryptography_KeyPairPersistence_ProtectMachine) +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)) +HANDLES(ICALL(APPDOM_2, "GetAssemblies", ves_icall_System_AppDomain_GetAssemblies)) +HANDLES(ICALL(APPDOM_3, "GetData", ves_icall_System_AppDomain_GetData)) +HANDLES(ICALL(APPDOM_4, "InternalGetContext", ves_icall_System_AppDomain_InternalGetContext)) +HANDLES(ICALL(APPDOM_5, "InternalGetDefaultContext", ves_icall_System_AppDomain_InternalGetDefaultContext)) +HANDLES(ICALL(APPDOM_6, "InternalGetProcessGuid", ves_icall_System_AppDomain_InternalGetProcessGuid)) +HANDLES(ICALL(APPDOM_7, "InternalIsFinalizingForUnload", ves_icall_System_AppDomain_InternalIsFinalizingForUnload)) +HANDLES(ICALL(APPDOM_8, "InternalPopDomainRef", ves_icall_System_AppDomain_InternalPopDomainRef)) +HANDLES(ICALL(APPDOM_9, "InternalPushDomainRef", ves_icall_System_AppDomain_InternalPushDomainRef)) +HANDLES(ICALL(APPDOM_10, "InternalPushDomainRefByID", ves_icall_System_AppDomain_InternalPushDomainRefByID)) +HANDLES(ICALL(APPDOM_11, "InternalSetContext", ves_icall_System_AppDomain_InternalSetContext)) +HANDLES(ICALL(APPDOM_12, "InternalSetDomain", ves_icall_System_AppDomain_InternalSetDomain)) +HANDLES(ICALL(APPDOM_13, "InternalSetDomainByID", ves_icall_System_AppDomain_InternalSetDomainByID)) +HANDLES(ICALL(APPDOM_14, "InternalUnload", ves_icall_System_AppDomain_InternalUnload)) +HANDLES(ICALL(APPDOM_15, "LoadAssembly", ves_icall_System_AppDomain_LoadAssembly)) +HANDLES(ICALL(APPDOM_16, "LoadAssemblyRaw", ves_icall_System_AppDomain_LoadAssemblyRaw)) +HANDLES(ICALL(APPDOM_17, "SetData", ves_icall_System_AppDomain_SetData)) +HANDLES(ICALL(APPDOM_18, "createDomain", ves_icall_System_AppDomain_createDomain)) +HANDLES(ICALL(APPDOM_19, "getCurDomain", ves_icall_System_AppDomain_getCurDomain)) +HANDLES(ICALL(APPDOM_20, "getFriendlyName", ves_icall_System_AppDomain_getFriendlyName)) +HANDLES(ICALL(APPDOM_21, "getRootDomain", ves_icall_System_AppDomain_getRootDomain)) +HANDLES(ICALL(APPDOM_22, "getSetup", ves_icall_System_AppDomain_getSetup)) + +ICALL_TYPE(ARGI, "System.ArgIterator", ARGI_1) +ICALL(ARGI_1, "IntGetNextArg()", mono_ArgIterator_IntGetNextArg) +ICALL(ARGI_2, "IntGetNextArg(intptr)", mono_ArgIterator_IntGetNextArgT) +ICALL(ARGI_3, "IntGetNextArgType", mono_ArgIterator_IntGetNextArgType) +ICALL(ARGI_4, "Setup", mono_ArgIterator_Setup) + +ICALL_TYPE(ARRAY, "System.Array", ARRAY_1) +ICALL(ARRAY_1, "ClearInternal", ves_icall_System_Array_ClearInternal) +ICALL(ARRAY_3, "CreateInstanceImpl", ves_icall_System_Array_CreateInstanceImpl) +ICALL(ARRAY_14, "CreateInstanceImpl64", ves_icall_System_Array_CreateInstanceImpl64) +ICALL(ARRAY_4, "FastCopy", ves_icall_System_Array_FastCopy) +ICALL(ARRAY_5, "GetGenericValueImpl", ves_icall_System_Array_GetGenericValueImpl) +ICALL(ARRAY_6, "GetLength", ves_icall_System_Array_GetLength) +ICALL(ARRAY_15, "GetLongLength", ves_icall_System_Array_GetLongLength) +ICALL(ARRAY_7, "GetLowerBound", ves_icall_System_Array_GetLowerBound) +ICALL(ARRAY_8, "GetRank", ves_icall_System_Array_GetRank) +ICALL(ARRAY_9, "GetValue", ves_icall_System_Array_GetValue) +ICALL(ARRAY_10, "GetValueImpl", ves_icall_System_Array_GetValueImpl) +ICALL(ARRAY_11, "SetGenericValueImpl", ves_icall_System_Array_SetGenericValueImpl) +HANDLES(ICALL(ARRAY_12, "SetValue", ves_icall_System_Array_SetValue)) +HANDLES(ICALL(ARRAY_13, "SetValueImpl", ves_icall_System_Array_SetValueImpl)) + +ICALL_TYPE(BUFFER, "System.Buffer", BUFFER_1) +ICALL(BUFFER_1, "InternalBlockCopy", ves_icall_System_Buffer_BlockCopyInternal) +ICALL(BUFFER_2, "_ByteLength", ves_icall_System_Buffer_ByteLengthInternal) +ICALL(BUFFER_3, "_GetByte", ves_icall_System_Buffer_GetByteInternal) +ICALL(BUFFER_4, "_SetByte", ves_icall_System_Buffer_SetByteInternal) + +ICALL_TYPE(CLRCONFIG, "System.CLRConfig", CLRCONFIG_1) +ICALL(CLRCONFIG_1, "CheckThrowUnobservedTaskExceptions", ves_icall_System_CLRConfig_CheckThrowUnobservedTaskExceptions) + +ICALL_TYPE(DEFAULTC, "System.Configuration.DefaultConfig", DEFAULTC_1) +HANDLES(ICALL(DEFAULTC_1, "get_bundled_machine_config", ves_icall_System_Configuration_DefaultConfig_get_bundled_machine_config)) +HANDLES(ICALL(DEFAULTC_2, "get_machine_config_path", ves_icall_System_Configuration_DefaultConfig_get_machine_config_path)) + +/* Note that the below icall shares the same function as DefaultConfig uses */ +ICALL_TYPE(INTCFGHOST, "System.Configuration.InternalConfigurationHost", INTCFGHOST_1) +HANDLES(ICALL(INTCFGHOST_1, "get_bundled_app_config", ves_icall_System_Configuration_InternalConfigurationHost_get_bundled_app_config)) +HANDLES(ICALL(INTCFGHOST_2, "get_bundled_machine_config", ves_icall_System_Configuration_InternalConfigurationHost_get_bundled_machine_config)) + +ICALL_TYPE(CONSOLE, "System.ConsoleDriver", CONSOLE_1) +ICALL(CONSOLE_1, "InternalKeyAvailable", ves_icall_System_ConsoleDriver_InternalKeyAvailable ) +ICALL(CONSOLE_2, "Isatty", ves_icall_System_ConsoleDriver_Isatty ) +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) + +#ifndef DISABLE_DECIMAL +ICALL_TYPE(DECIMAL, "System.Decimal", DECIMAL_1) +ICALL(DECIMAL_1, ".ctor(double)", mono_decimal_init_double) +ICALL(DECIMAL_2, ".ctor(single)", mono_decimal_init_single) +ICALL(DECIMAL_3, "FCallAddSub(System.Decimal&,System.Decimal&,byte)", mono_decimal_addsub) +ICALL(DECIMAL_4, "FCallCompare", mono_decimal_compare) +ICALL(DECIMAL_5, "FCallDivide", mono_decimal_divide) +ICALL(DECIMAL_6, "FCallFloor", mono_decimal_floor) +ICALL(DECIMAL_7, "FCallMultiply", mono_decimal_multiply) +ICALL(DECIMAL_8, "FCallRound", mono_decimal_round) +ICALL(DECIMAL_9, "FCallToInt32", mono_decimal_to_int32) +ICALL(DECIMAL_10, "FCallTruncate", mono_decimal_truncate) +ICALL(DECIMAL_11, "GetHashCode", mono_decimal_get_hash_code) +ICALL(DECIMAL_12, "ToDouble", mono_decimal_to_double) +ICALL(DECIMAL_13, "ToSingle", mono_decimal_to_float) +#endif + +ICALL_TYPE(DELEGATE, "System.Delegate", DELEGATE_1) +HANDLES(ICALL(DELEGATE_1, "AllocDelegateLike_internal", ves_icall_System_Delegate_AllocDelegateLike_internal)) +HANDLES(ICALL(DELEGATE_2, "CreateDelegate_internal", ves_icall_System_Delegate_CreateDelegate_internal)) +HANDLES(ICALL(DELEGATE_3, "GetVirtualMethod_internal", ves_icall_System_Delegate_GetVirtualMethod_internal)) + +ICALL_TYPE(DEBUGR, "System.Diagnostics.Debugger", DEBUGR_1) +ICALL(DEBUGR_1, "IsAttached_internal", ves_icall_System_Diagnostics_Debugger_IsAttached_internal) +ICALL(DEBUGR_2, "IsLogging", ves_icall_System_Diagnostics_Debugger_IsLogging) +ICALL(DEBUGR_3, "Log", ves_icall_System_Diagnostics_Debugger_Log) + +ICALL_TYPE(TRACEL, "System.Diagnostics.DefaultTraceListener", TRACEL_1) +ICALL(TRACEL_1, "WriteWindowsDebugString", ves_icall_System_Diagnostics_DefaultTraceListener_WriteWindowsDebugString) + +ICALL_TYPE(FILEV, "System.Diagnostics.FileVersionInfo", FILEV_1) +ICALL(FILEV_1, "GetVersionInfo_internal(string)", ves_icall_System_Diagnostics_FileVersionInfo_GetVersionInfo_internal) + +#ifndef DISABLE_PROCESS_HANDLING +ICALL_TYPE(PERFCTR, "System.Diagnostics.PerformanceCounter", PERFCTR_1) +ICALL(PERFCTR_1, "FreeData", mono_perfcounter_free_data) +ICALL(PERFCTR_2, "GetImpl", mono_perfcounter_get_impl) +ICALL(PERFCTR_3, "GetSample", mono_perfcounter_get_sample) +ICALL(PERFCTR_4, "UpdateValue", mono_perfcounter_update_value) + +ICALL_TYPE(PERFCTRCAT, "System.Diagnostics.PerformanceCounterCategory", PERFCTRCAT_1) +ICALL(PERFCTRCAT_1, "CategoryDelete", mono_perfcounter_category_del) +ICALL(PERFCTRCAT_2, "CategoryHelpInternal", mono_perfcounter_category_help) +ICALL(PERFCTRCAT_3, "CounterCategoryExists", mono_perfcounter_category_exists) +ICALL(PERFCTRCAT_4, "Create", mono_perfcounter_create) +ICALL(PERFCTRCAT_5, "GetCategoryNames", mono_perfcounter_category_names) +ICALL(PERFCTRCAT_6, "GetCounterNames", mono_perfcounter_counter_names) +ICALL(PERFCTRCAT_7, "GetInstanceNames", mono_perfcounter_instance_names) +ICALL(PERFCTRCAT_8, "InstanceExistsInternal", mono_perfcounter_instance_exists) + +ICALL_TYPE(PROCESS, "System.Diagnostics.Process", PROCESS_1) +ICALL(PROCESS_1, "CreateProcess_internal", ves_icall_System_Diagnostics_Process_CreateProcess_internal) +ICALL(PROCESS_4, "GetModules_internal(intptr)", ves_icall_System_Diagnostics_Process_GetModules_internal) +ICALL(PROCESS_5H, "GetProcessData", ves_icall_System_Diagnostics_Process_GetProcessData) +ICALL(PROCESS_6, "GetProcess_internal(int)", ves_icall_System_Diagnostics_Process_GetProcess_internal) +ICALL(PROCESS_7, "GetProcesses_internal()", ves_icall_System_Diagnostics_Process_GetProcesses_internal) +ICALL(PROCESS_10, "ProcessName_internal(intptr)", ves_icall_System_Diagnostics_Process_ProcessName_internal) +ICALL(PROCESS_13, "ShellExecuteEx_internal(System.Diagnostics.ProcessStartInfo,System.Diagnostics.Process/ProcInfo&)", ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal) +#endif /* !DISABLE_PROCESS_HANDLING */ + +ICALL_TYPE(STOPWATCH, "System.Diagnostics.Stopwatch", STOPWATCH_1) +ICALL(STOPWATCH_1, "GetTimestamp", mono_100ns_ticks) + +ICALL_TYPE(ENUM, "System.Enum", ENUM_1) +HANDLES(ICALL(ENUM_1, "GetEnumValuesAndNames", ves_icall_System_Enum_GetEnumValuesAndNames)) +ICALL(ENUM_2, "InternalBoxEnum", ves_icall_System_Enum_ToObject) +ICALL(ENUM_3, "InternalCompareTo", ves_icall_System_Enum_compare_value_to) +ICALL(ENUM_4, "InternalGetUnderlyingType", ves_icall_System_Enum_get_underlying_type) +ICALL(ENUM_5, "InternalHasFlag", ves_icall_System_Enum_InternalHasFlag) +ICALL(ENUM_6, "get_hashcode", ves_icall_System_Enum_get_hashcode) +ICALL(ENUM_7, "get_value", ves_icall_System_Enum_get_value) + +ICALL_TYPE(ENV, "System.Environment", ENV_1) +ICALL(ENV_1, "Exit", ves_icall_System_Environment_Exit) +HANDLES(ICALL(ENV_2, "GetCommandLineArgs", ves_icall_System_Environment_GetCommandLineArgs)) +ICALL(ENV_3, "GetEnvironmentVariableNames", ves_icall_System_Environment_GetEnvironmentVariableNames) +ICALL(ENV_31, "GetIs64BitOperatingSystem", ves_icall_System_Environment_GetIs64BitOperatingSystem) +ICALL(ENV_4, "GetLogicalDrivesInternal", ves_icall_System_Environment_GetLogicalDrives ) +HANDLES(ICALL(ENV_5, "GetMachineConfigPath", ves_icall_System_Configuration_DefaultConfig_get_machine_config_path)) +HANDLES(ICALL(ENV_51, "GetNewLine", ves_icall_System_Environment_get_NewLine)) +HANDLES(ICALL(ENV_6, "GetOSVersionString", ves_icall_System_Environment_GetOSVersionString)) +ICALL(ENV_6a, "GetPageSize", mono_pagesize) +HANDLES(ICALL(ENV_7, "GetWindowsFolderPath", ves_icall_System_Environment_GetWindowsFolderPath)) +ICALL(ENV_8, "InternalSetEnvironmentVariable", ves_icall_System_Environment_InternalSetEnvironmentVariable) +ICALL(ENV_9, "get_ExitCode", mono_environment_exitcode_get) +ICALL(ENV_10, "get_HasShutdownStarted", ves_icall_System_Environment_get_HasShutdownStarted) +HANDLES(ICALL(ENV_11, "get_MachineName", ves_icall_System_Environment_get_MachineName)) +ICALL(ENV_13, "get_Platform", ves_icall_System_Environment_get_Platform) +ICALL(ENV_14, "get_ProcessorCount", mono_cpu_count) +ICALL(ENV_15, "get_TickCount", ves_icall_System_Environment_get_TickCount) +HANDLES(ICALL(ENV_16, "get_UserName", ves_icall_System_Environment_get_UserName)) +HANDLES(ICALL(ENV_16b, "get_bundled_machine_config", ves_icall_System_Environment_get_bundled_machine_config)) +HANDLES(ICALL(ENV_16m, "internalBroadcastSettingChange", ves_icall_System_Environment_BroadcastSettingChange)) +HANDLES(ICALL(ENV_17, "internalGetEnvironmentVariable_native", ves_icall_System_Environment_GetEnvironmentVariable_native)) +HANDLES(ICALL(ENV_18, "internalGetGacPath", ves_icall_System_Environment_GetGacPath)) +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) +ICALL(GC_0b, "GetMaxGeneration", mono_gc_max_generation) +ICALL(GC_1, "GetTotalMemory", ves_icall_System_GC_GetTotalMemory) +ICALL(GC_2, "InternalCollect", ves_icall_System_GC_InternalCollect) +ICALL(GC_3, "KeepAlive", ves_icall_System_GC_KeepAlive) +ICALL(GC_4a, "RecordPressure", mono_gc_add_memory_pressure) +ICALL(GC_6, "WaitForPendingFinalizers", ves_icall_System_GC_WaitForPendingFinalizers) +ICALL(GC_6b, "_ReRegisterForFinalize", ves_icall_System_GC_ReRegisterForFinalize) +ICALL(GC_7, "_SuppressFinalize", ves_icall_System_GC_SuppressFinalize) +ICALL(GC_9, "get_ephemeron_tombstone", ves_icall_System_GC_get_ephemeron_tombstone) +ICALL(GC_8, "register_ephemeron_array", ves_icall_System_GC_register_ephemeron_array) + +ICALL_TYPE(CALDATA, "System.Globalization.CalendarData", CALDATA_1) +ICALL(CALDATA_1, "fill_calendar_data", ves_icall_System_Globalization_CalendarData_fill_calendar_data) + +ICALL_TYPE(COMPINF, "System.Globalization.CompareInfo", COMPINF_1) +ICALL(COMPINF_1, "assign_sortkey(object,string,System.Globalization.CompareOptions)", ves_icall_System_Globalization_CompareInfo_assign_sortkey) +ICALL(COMPINF_4, "internal_compare(string,int,int,string,int,int,System.Globalization.CompareOptions)", ves_icall_System_Globalization_CompareInfo_internal_compare) +ICALL(COMPINF_5, "internal_index(string,int,int,char,System.Globalization.CompareOptions,bool)", ves_icall_System_Globalization_CompareInfo_internal_index_char) +ICALL(COMPINF_6, "internal_index(string,int,int,string,System.Globalization.CompareOptions,bool)", ves_icall_System_Globalization_CompareInfo_internal_index) + +ICALL_TYPE(CULDATA, "System.Globalization.CultureData", CULDATA_1) +ICALL(CULDATA_1, "fill_culture_data", ves_icall_System_Globalization_CultureData_fill_culture_data) +ICALL(CULDATA_2, "fill_number_data", ves_icall_System_Globalization_CultureData_fill_number_data) + +ICALL_TYPE(CULINF, "System.Globalization.CultureInfo", CULINF_5) +ICALL(CULINF_5, "construct_internal_locale_from_lcid", ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_lcid) +ICALL(CULINF_6, "construct_internal_locale_from_name", ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_name) +HANDLES(ICALL(CULINF_7, "get_current_locale_name", ves_icall_System_Globalization_CultureInfo_get_current_locale_name)) +ICALL(CULINF_9, "internal_get_cultures", ves_icall_System_Globalization_CultureInfo_internal_get_cultures) +//ICALL(CULINF_10, "internal_is_lcid_neutral", ves_icall_System_Globalization_CultureInfo_internal_is_lcid_neutral) + +ICALL_TYPE(REGINF, "System.Globalization.RegionInfo", REGINF_1) +ICALL(REGINF_1, "construct_internal_region_from_lcid", ves_icall_System_Globalization_RegionInfo_construct_internal_region_from_lcid) +ICALL(REGINF_2, "construct_internal_region_from_name", ves_icall_System_Globalization_RegionInfo_construct_internal_region_from_name) + +#ifndef PLATFORM_NO_DRIVEINFO +ICALL_TYPE(IODRIVEINFO, "System.IO.DriveInfo", IODRIVEINFO_1) +ICALL(IODRIVEINFO_1, "GetDiskFreeSpaceInternal", ves_icall_System_IO_DriveInfo_GetDiskFreeSpace) +ICALL(IODRIVEINFO_2, "GetDriveFormat", ves_icall_System_IO_DriveInfo_GetDriveFormat) +ICALL(IODRIVEINFO_3, "GetDriveTypeInternal", ves_icall_System_IO_DriveInfo_GetDriveType) +#endif + +ICALL_TYPE(FAMW, "System.IO.FAMWatcher", FAMW_1) +ICALL(FAMW_1, "InternalFAMNextEvent", ves_icall_System_IO_FAMW_InternalFAMNextEvent) + +ICALL_TYPE(FILEW, "System.IO.FileSystemWatcher", FILEW_4) +ICALL(FILEW_4, "InternalSupportsFSW", ves_icall_System_IO_FSW_SupportsFSW) + +ICALL_TYPE(INOW, "System.IO.InotifyWatcher", INOW_1) +ICALL(INOW_1, "AddWatch", ves_icall_System_IO_InotifyWatcher_AddWatch) +ICALL(INOW_2, "GetInotifyInstance", ves_icall_System_IO_InotifyWatcher_GetInotifyInstance) +ICALL(INOW_3, "RemoveWatch", ves_icall_System_IO_InotifyWatcher_RemoveWatch) + +ICALL_TYPE(KQUEM, "System.IO.KqueueMonitor", KQUEM_1) +ICALL(KQUEM_1, "kevent_notimeout", ves_icall_System_IO_KqueueMonitor_kevent_notimeout) + +ICALL_TYPE(LOGCATEXTWRITER, "System.IO.LogcatTextWriter", LOGCATEXTWRITER_1) +HANDLES(ICALL(LOGCATEXTWRITER_1, "Log", ves_icall_System_IO_LogcatTextWriter_Log)) + +ICALL_TYPE(MMAPIMPL, "System.IO.MemoryMappedFiles.MemoryMapImpl", MMAPIMPL_1) +ICALL(MMAPIMPL_1, "CloseMapping", mono_mmap_close) +ICALL(MMAPIMPL_2, "ConfigureHandleInheritability", mono_mmap_configure_inheritability) +ICALL(MMAPIMPL_3, "Flush", mono_mmap_flush) +ICALL(MMAPIMPL_4, "MapInternal", mono_mmap_map) +ICALL(MMAPIMPL_5, "OpenFileInternal", mono_mmap_open_file) +ICALL(MMAPIMPL_6, "OpenHandleInternal", mono_mmap_open_handle) +ICALL(MMAPIMPL_7, "Unmap", mono_mmap_unmap) + +ICALL_TYPE(MONOIO, "System.IO.MonoIO", MONOIO_1) +ICALL(MONOIO_1, "Close(intptr,System.IO.MonoIOError&)", ves_icall_System_IO_MonoIO_Close) +#ifndef PLATFORM_RO_FS +ICALL(MONOIO_2, "CopyFile(char*,char*,bool,System.IO.MonoIOError&)", ves_icall_System_IO_MonoIO_CopyFile) +ICALL(MONOIO_3, "CreateDirectory(char*,System.IO.MonoIOError&)", ves_icall_System_IO_MonoIO_CreateDirectory) +ICALL(MONOIO_4, "CreatePipe", ves_icall_System_IO_MonoIO_CreatePipe) +ICALL(MONOIO_5, "DeleteFile(char*,System.IO.MonoIOError&)", ves_icall_System_IO_MonoIO_DeleteFile) +#endif /* !PLATFORM_RO_FS */ +ICALL(MONOIO_38, "DumpHandles", ves_icall_System_IO_MonoIO_DumpHandles) +ICALL(MONOIO_34, "DuplicateHandle", ves_icall_System_IO_MonoIO_DuplicateHandle) +ICALL(MONOIO_37a, "FindCloseFile", ves_icall_System_IO_MonoIO_FindCloseFile) +HANDLES(ICALL(MONOIO_35a, "FindFirstFile", ves_icall_System_IO_MonoIO_FindFirstFile)) +HANDLES(ICALL(MONOIO_36a, "FindNextFile", ves_icall_System_IO_MonoIO_FindNextFile)) +ICALL(MONOIO_6, "Flush(intptr,System.IO.MonoIOError&)", ves_icall_System_IO_MonoIO_Flush) +HANDLES(ICALL(MONOIO_7, "GetCurrentDirectory(System.IO.MonoIOError&)", ves_icall_System_IO_MonoIO_GetCurrentDirectory)) +ICALL(MONOIO_8, "GetFileAttributes(char*,System.IO.MonoIOError&)", ves_icall_System_IO_MonoIO_GetFileAttributes) +ICALL(MONOIO_9, "GetFileStat(char*,System.IO.MonoIOStat&,System.IO.MonoIOError&)", ves_icall_System_IO_MonoIO_GetFileStat) +ICALL(MONOIO_11, "GetFileType(intptr,System.IO.MonoIOError&)", ves_icall_System_IO_MonoIO_GetFileType) +ICALL(MONOIO_12, "GetLength(intptr,System.IO.MonoIOError&)", ves_icall_System_IO_MonoIO_GetLength) +#ifndef PLATFORM_RO_FS +ICALL(MONOIO_14, "Lock(intptr,long,long,System.IO.MonoIOError&)", ves_icall_System_IO_MonoIO_Lock) +ICALL(MONOIO_15, "MoveFile(char*,char*,System.IO.MonoIOError&)", ves_icall_System_IO_MonoIO_MoveFile) +#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) +#endif /* !PLATFORM_RO_FS */ +ICALL(MONOIO_19, "Seek(intptr,long,System.IO.SeekOrigin,System.IO.MonoIOError&)", ves_icall_System_IO_MonoIO_Seek) +ICALL(MONOIO_20, "SetCurrentDirectory(char*,System.IO.MonoIOError&)", ves_icall_System_IO_MonoIO_SetCurrentDirectory) +ICALL(MONOIO_21, "SetFileAttributes(char*,System.IO.FileAttributes,System.IO.MonoIOError&)", ves_icall_System_IO_MonoIO_SetFileAttributes) +ICALL(MONOIO_22, "SetFileTime(intptr,long,long,long,System.IO.MonoIOError&)", ves_icall_System_IO_MonoIO_SetFileTime) +ICALL(MONOIO_23, "SetLength(intptr,long,System.IO.MonoIOError&)", ves_icall_System_IO_MonoIO_SetLength) +#ifndef PLATFORM_RO_FS +ICALL(MONOIO_24, "Unlock(intptr,long,long,System.IO.MonoIOError&)", ves_icall_System_IO_MonoIO_Unlock) +#endif +HANDLES(ICALL(MONOIO_25, "Write(intptr,byte[],int,int,System.IO.MonoIOError&)", ves_icall_System_IO_MonoIO_Write)) +ICALL(MONOIO_26, "get_AltDirectorySeparatorChar", ves_icall_System_IO_MonoIO_get_AltDirectorySeparatorChar) +ICALL(MONOIO_27, "get_ConsoleError", ves_icall_System_IO_MonoIO_get_ConsoleError) +ICALL(MONOIO_28, "get_ConsoleInput", ves_icall_System_IO_MonoIO_get_ConsoleInput) +ICALL(MONOIO_29, "get_ConsoleOutput", ves_icall_System_IO_MonoIO_get_ConsoleOutput) +ICALL(MONOIO_30, "get_DirectorySeparatorChar", ves_icall_System_IO_MonoIO_get_DirectorySeparatorChar) +HANDLES(ICALL(MONOIO_31, "get_InvalidPathChars", ves_icall_System_IO_MonoIO_get_InvalidPathChars)) +ICALL(MONOIO_32, "get_PathSeparator", ves_icall_System_IO_MonoIO_get_PathSeparator) +ICALL(MONOIO_33, "get_VolumeSeparatorChar", ves_icall_System_IO_MonoIO_get_VolumeSeparatorChar) + +ICALL_TYPE(IOPATH, "System.IO.Path", IOPATH_1) +HANDLES(ICALL(IOPATH_1, "get_temp_path", ves_icall_System_IO_get_temp_path)) + +ICALL_TYPE(IOSELECTOR, "System.IOSelector", IOSELECTOR_1) +ICALL(IOSELECTOR_1, "Add", ves_icall_System_IOSelector_Add) +ICALL(IOSELECTOR_2, "Remove", ves_icall_System_IOSelector_Remove) + +ICALL_TYPE(MATH, "System.Math", MATH_19) +ICALL(MATH_19, "Abs(double)", ves_icall_System_Math_Abs_double) +ICALL(MATH_20, "Abs(single)", ves_icall_System_Math_Abs_single) +ICALL(MATH_1, "Acos", ves_icall_System_Math_Acos) +ICALL(MATH_2, "Asin", ves_icall_System_Math_Asin) +ICALL(MATH_3, "Atan", ves_icall_System_Math_Atan) +ICALL(MATH_4, "Atan2", ves_icall_System_Math_Atan2) +ICALL(MATH_21, "Ceiling", ves_icall_System_Math_Ceiling) +ICALL(MATH_5, "Cos", ves_icall_System_Math_Cos) +ICALL(MATH_6, "Cosh", ves_icall_System_Math_Cosh) +ICALL(MATH_7, "Exp", ves_icall_System_Math_Exp) +ICALL(MATH_8, "Floor", ves_icall_System_Math_Floor) +ICALL(MATH_9, "Log", ves_icall_System_Math_Log) +ICALL(MATH_10, "Log10", ves_icall_System_Math_Log10) +ICALL(MATH_11, "Pow", ves_icall_System_Math_Pow) +ICALL(MATH_12, "Round", ves_icall_System_Math_Round) +ICALL(MATH_14, "Sin", ves_icall_System_Math_Sin) +ICALL(MATH_15, "Sinh", ves_icall_System_Math_Sinh) +ICALL(MATH_22, "SplitFractionDouble", ves_icall_System_Math_SplitFractionDouble) +ICALL(MATH_16, "Sqrt", ves_icall_System_Math_Sqrt) +ICALL(MATH_17, "Tan", ves_icall_System_Math_Tan) +ICALL(MATH_18, "Tanh", ves_icall_System_Math_Tanh) + +ICALL_TYPE(MCATTR, "System.MonoCustomAttrs", MCATTR_1) +HANDLES(ICALL(MCATTR_1, "GetCustomAttributesDataInternal", ves_icall_MonoCustomAttrs_GetCustomAttributesDataInternal)) +HANDLES(ICALL(MCATTR_2, "GetCustomAttributesInternal", ves_icall_MonoCustomAttrs_GetCustomAttributesInternal)) +HANDLES(ICALL(MCATTR_3, "IsDefinedInternal", ves_icall_MonoCustomAttrs_IsDefinedInternal)) + +#ifndef DISABLE_SOCKETS +ICALL_TYPE(NDNS, "System.Net.Dns", NDNS_1) +HANDLES(ICALL(NDNS_1, "GetHostByAddr_internal(string,string&,string[]&,string[]&,int)", ves_icall_System_Net_Dns_GetHostByAddr_internal)) +HANDLES(ICALL(NDNS_2, "GetHostByName_internal(string,string&,string[]&,string[]&,int)", ves_icall_System_Net_Dns_GetHostByName_internal)) +HANDLES(ICALL(NDNS_3, "GetHostName_internal(string&)", ves_icall_System_Net_Dns_GetHostName_internal)) + +#if defined(HOST_DARWIN) || defined(HOST_BSD) +ICALL_TYPE(MAC_IFACE_PROPS, "System.Net.NetworkInformation.MacOsIPInterfaceProperties", MAC_IFACE_PROPS_1) +ICALL(MAC_IFACE_PROPS_1, "ParseRouteInfo_internal", ves_icall_System_Net_NetworkInformation_MacOsIPInterfaceProperties_ParseRouteInfo_internal) +#endif + +ICALL_TYPE(SOCK, "System.Net.Sockets.Socket", SOCK_1) +HANDLES(ICALL(SOCK_1, "Accept_internal(intptr,int&,bool)", ves_icall_System_Net_Sockets_Socket_Accept_internal)) +HANDLES(ICALL(SOCK_2, "Available_internal(intptr,int&)", ves_icall_System_Net_Sockets_Socket_Available_internal)) +HANDLES(ICALL(SOCK_3, "Bind_internal(intptr,System.Net.SocketAddress,int&)", ves_icall_System_Net_Sockets_Socket_Bind_internal)) +HANDLES(ICALL(SOCK_4, "Blocking_internal(intptr,bool,int&)", ves_icall_System_Net_Sockets_Socket_Blocking_internal)) +HANDLES(ICALL(SOCK_5, "Close_internal(intptr,int&)", ves_icall_System_Net_Sockets_Socket_Close_internal)) +HANDLES(ICALL(SOCK_6, "Connect_internal(intptr,System.Net.SocketAddress,int&,bool)", ves_icall_System_Net_Sockets_Socket_Connect_internal)) +HANDLES(ICALL(SOCK_6a, "Disconnect_internal(intptr,bool,int&)", ves_icall_System_Net_Sockets_Socket_Disconnect_internal)) +HANDLES(ICALL(SOCK_6b, "Duplicate_internal", ves_icall_System_Net_Sockets_Socket_Duplicate_internal)) +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)) +HANDLES(ICALL(SOCK_13, "ReceiveFrom_internal(intptr,byte*,int,System.Net.Sockets.SocketFlags,System.Net.SocketAddress&,int&,bool)", ves_icall_System_Net_Sockets_Socket_ReceiveFrom_internal)) +HANDLES(ICALL(SOCK_11a, "Receive_internal(intptr,System.Net.Sockets.Socket/WSABUF*,int,System.Net.Sockets.SocketFlags,int&,bool)", ves_icall_System_Net_Sockets_Socket_Receive_array_internal)) +HANDLES(ICALL(SOCK_12, "Receive_internal(intptr,byte*,int,System.Net.Sockets.SocketFlags,int&,bool)", ves_icall_System_Net_Sockets_Socket_Receive_internal)) +HANDLES(ICALL(SOCK_14, "RemoteEndPoint_internal(intptr,int,int&)", ves_icall_System_Net_Sockets_Socket_RemoteEndPoint_internal)) +HANDLES(ICALL(SOCK_15, "Select_internal(System.Net.Sockets.Socket[]&,int,int&)", ves_icall_System_Net_Sockets_Socket_Select_internal)) +HANDLES(ICALL(SOCK_15a, "SendFile_internal(intptr,string,byte[],byte[],System.Net.Sockets.TransmitFileOptions,int&,bool)", ves_icall_System_Net_Sockets_Socket_SendFile_internal)) +HANDLES(ICALL(SOCK_16, "SendTo_internal(intptr,byte*,int,System.Net.Sockets.SocketFlags,System.Net.SocketAddress,int&,bool)", ves_icall_System_Net_Sockets_Socket_SendTo_internal)) +HANDLES(ICALL(SOCK_16a, "Send_internal(intptr,System.Net.Sockets.Socket/WSABUF*,int,System.Net.Sockets.SocketFlags,int&,bool)", ves_icall_System_Net_Sockets_Socket_Send_array_internal)) +HANDLES(ICALL(SOCK_17, "Send_internal(intptr,byte*,int,System.Net.Sockets.SocketFlags,int&,bool)", ves_icall_System_Net_Sockets_Socket_Send_internal)) +HANDLES(ICALL(SOCK_18, "SetSocketOption_internal(intptr,System.Net.Sockets.SocketOptionLevel,System.Net.Sockets.SocketOptionName,object,byte[],int,int&)", ves_icall_System_Net_Sockets_Socket_SetSocketOption_internal)) +HANDLES(ICALL(SOCK_19, "Shutdown_internal(intptr,System.Net.Sockets.SocketShutdown,int&)", ves_icall_System_Net_Sockets_Socket_Shutdown_internal)) +HANDLES(ICALL(SOCK_20, "Socket_internal(System.Net.Sockets.AddressFamily,System.Net.Sockets.SocketType,System.Net.Sockets.ProtocolType,int&)", ves_icall_System_Net_Sockets_Socket_Socket_internal)) +HANDLES(ICALL(SOCK_20a, "SupportsPortReuse", ves_icall_System_Net_Sockets_Socket_SupportPortReuse)) +HANDLES(ICALL(SOCK_21a, "cancel_blocking_socket_operation", icall_cancel_blocking_socket_operation)) + +ICALL_TYPE(SOCKEX, "System.Net.Sockets.SocketException", SOCKEX_1) +ICALL(SOCKEX_1, "WSAGetLastError_internal", ves_icall_System_Net_Sockets_SocketException_WSAGetLastError_internal) +#endif /* !DISABLE_SOCKETS */ + +ICALL_TYPE(NUMBER, "System.Number", NUMBER_1) +ICALL(NUMBER_1, "NumberBufferToDecimal", mono_decimal_from_number) +ICALL(NUMBER_2, "NumberBufferToDouble", mono_double_from_number) + +ICALL_TYPE(NUMBER_FORMATTER, "System.NumberFormatter", NUMBER_FORMATTER_1) +ICALL(NUMBER_FORMATTER_1, "GetFormatterTables", ves_icall_System_NumberFormatter_GetFormatterTables) + +ICALL_TYPE(OBJ, "System.Object", OBJ_1) +HANDLES(ICALL(OBJ_1, "GetType", ves_icall_System_Object_GetType)) +ICALL(OBJ_2, "InternalGetHashCode", mono_object_hash) +ICALL(OBJ_3, "MemberwiseClone", ves_icall_System_Object_MemberwiseClone) + +ICALL_TYPE(ASSEM, "System.Reflection.Assembly", ASSEM_1a) +HANDLES(ICALL(ASSEM_1a, "GetAotId", ves_icall_System_Reflection_Assembly_GetAotId)) +HANDLES(ICALL(ASSEM_2, "GetCallingAssembly", ves_icall_System_Reflection_Assembly_GetCallingAssembly)) +HANDLES(ICALL(ASSEM_3, "GetEntryAssembly", ves_icall_System_Reflection_Assembly_GetEntryAssembly)) +HANDLES(ICALL(ASSEM_4, "GetExecutingAssembly", ves_icall_System_Reflection_Assembly_GetExecutingAssembly)) +HANDLES(ICALL(ASSEM_5, "GetFilesInternal", ves_icall_System_Reflection_Assembly_GetFilesInternal)) +HANDLES(ICALL(ASSEM_6, "GetManifestModuleInternal", ves_icall_System_Reflection_Assembly_GetManifestModuleInternal)) +HANDLES(ICALL(ASSEM_7, "GetManifestResourceInfoInternal", ves_icall_System_Reflection_Assembly_GetManifestResourceInfoInternal)) +HANDLES(ICALL(ASSEM_8, "GetManifestResourceInternal", ves_icall_System_Reflection_Assembly_GetManifestResourceInternal)) +HANDLES(ICALL(ASSEM_9, "GetManifestResourceNames", ves_icall_System_Reflection_Assembly_GetManifestResourceNames)) +HANDLES(ICALL(ASSEM_10, "GetModulesInternal", ves_icall_System_Reflection_Assembly_GetModulesInternal)) +//ICALL(ASSEM_11, "GetNamespaces", ves_icall_System_Reflection_Assembly_GetNamespaces) +HANDLES(ICALL(ASSEM_13, "GetTypes", ves_icall_System_Reflection_Assembly_GetTypes)) +HANDLES(ICALL(ASSEM_14, "InternalGetAssemblyName", ves_icall_System_Reflection_Assembly_InternalGetAssemblyName)) +HANDLES(ICALL(ASSEM_12, "InternalGetReferencedAssemblies", ves_icall_System_Reflection_Assembly_InternalGetReferencedAssemblies)) +HANDLES(ICALL(ASSEM_15, "InternalGetType", ves_icall_System_Reflection_Assembly_InternalGetType)) +HANDLES(ICALL(ASSEM_16, "InternalImageRuntimeVersion", ves_icall_System_Reflection_Assembly_InternalImageRuntimeVersion)) +HANDLES(ICALL(ASSEM_17, "LoadFrom", ves_icall_System_Reflection_Assembly_LoadFrom)) +HANDLES(ICALL(ASSEM_18, "LoadPermissions", ves_icall_System_Reflection_Assembly_LoadPermissions)) + + /* normal icalls again */ +HANDLES(ICALL(ASSEM_20, "get_EntryPoint", ves_icall_System_Reflection_Assembly_get_EntryPoint)) +HANDLES(ICALL(ASSEM_21, "get_ReflectionOnly", ves_icall_System_Reflection_Assembly_get_ReflectionOnly)) +HANDLES(ICALL(ASSEM_22, "get_code_base", ves_icall_System_Reflection_Assembly_get_code_base)) +HANDLES(ICALL(ASSEM_23, "get_fullname", ves_icall_System_Reflection_Assembly_get_fullName)) +HANDLES(ICALL(ASSEM_24, "get_global_assembly_cache", ves_icall_System_Reflection_Assembly_get_global_assembly_cache)) +HANDLES(ICALL(ASSEM_25, "get_location", ves_icall_System_Reflection_Assembly_get_location)) +HANDLES(ICALL(ASSEM_26, "load_with_partial_name", ves_icall_System_Reflection_Assembly_load_with_partial_name)) + +ICALL_TYPE(ASSEMN, "System.Reflection.AssemblyName", ASSEMN_0) +ICALL(ASSEMN_0, "GetNativeName", ves_icall_System_Reflection_AssemblyName_GetNativeName) +ICALL(ASSEMN_3, "ParseAssemblyName", ves_icall_System_Reflection_AssemblyName_ParseAssemblyName) +ICALL(ASSEMN_2, "get_public_token", mono_digest_get_public_token) + +ICALL_TYPE(CATTR_DATA, "System.Reflection.CustomAttributeData", CATTR_DATA_1) +ICALL(CATTR_DATA_1, "ResolveArgumentsInternal", ves_icall_System_Reflection_CustomAttributeData_ResolveArgumentsInternal) + +ICALL_TYPE(ASSEMB, "System.Reflection.Emit.AssemblyBuilder", ASSEMB_1) +HANDLES(ICALL(ASSEMB_1, "UpdateNativeCustomAttributes", ves_icall_AssemblyBuilder_UpdateNativeCustomAttributes)) +ICALL(ASSEMB_2, "basic_init", ves_icall_AssemblyBuilder_basic_init) + +#ifndef DISABLE_REFLECTION_EMIT +ICALL_TYPE(CATTRB, "System.Reflection.Emit.CustomAttributeBuilder", CATTRB_1) +ICALL(CATTRB_1, "GetBlob", ves_icall_CustomAttributeBuilder_GetBlob) +#endif + +ICALL_TYPE(DYNM, "System.Reflection.Emit.DynamicMethod", DYNM_1) +HANDLES(ICALL(DYNM_1, "create_dynamic_method", ves_icall_DynamicMethod_create_dynamic_method)) + +ICALL_TYPE(ENUMB, "System.Reflection.Emit.EnumBuilder", ENUMB_1) +HANDLES(ICALL(ENUMB_1, "setup_enum_type", ves_icall_EnumBuilder_setup_enum_type)) + +ICALL_TYPE(MODULEB, "System.Reflection.Emit.ModuleBuilder", MODULEB_10) +HANDLES(ICALL(MODULEB_10, "GetRegisteredToken", ves_icall_ModuleBuilder_GetRegisteredToken)) +HANDLES(ICALL(MODULEB_8, "RegisterToken", ves_icall_ModuleBuilder_RegisterToken)) +ICALL(MODULEB_1, "WriteToFile", ves_icall_ModuleBuilder_WriteToFile) +HANDLES(ICALL(MODULEB_2, "basic_init", ves_icall_ModuleBuilder_basic_init)) +ICALL(MODULEB_3, "build_metadata", ves_icall_ModuleBuilder_build_metadata) +HANDLES(ICALL(MODULEB_5, "getMethodToken", ves_icall_ModuleBuilder_getMethodToken)) +HANDLES(ICALL(MODULEB_6, "getToken", ves_icall_ModuleBuilder_getToken)) +HANDLES(ICALL(MODULEB_7, "getUSIndex", ves_icall_ModuleBuilder_getUSIndex)) +HANDLES(ICALL(MODULEB_9, "set_wrappers_type", ves_icall_ModuleBuilder_set_wrappers_type)) + +ICALL_TYPE(SIGH, "System.Reflection.Emit.SignatureHelper", SIGH_1) +HANDLES(ICALL(SIGH_1, "get_signature_field", ves_icall_SignatureHelper_get_signature_field)) +HANDLES(ICALL(SIGH_2, "get_signature_local", ves_icall_SignatureHelper_get_signature_local)) + +ICALL_TYPE(TYPEB, "System.Reflection.Emit.TypeBuilder", TYPEB_1) +HANDLES(ICALL(TYPEB_1, "create_runtime_class", ves_icall_TypeBuilder_create_runtime_class)) + +ICALL_TYPE(EVENTI, "System.Reflection.EventInfo", EVENTI_1) +HANDLES(ICALL(EVENTI_1, "internal_from_handle_type", ves_icall_System_Reflection_EventInfo_internal_from_handle_type)) + +ICALL_TYPE(FIELDI, "System.Reflection.FieldInfo", FILEDI_1) +HANDLES(ICALL(FILEDI_1, "GetTypeModifiers", ves_icall_System_Reflection_FieldInfo_GetTypeModifiers)) +HANDLES(ICALL(FILEDI_2, "get_marshal_info", ves_icall_System_Reflection_FieldInfo_get_marshal_info)) +HANDLES(ICALL(FILEDI_3, "internal_from_handle_type", ves_icall_System_Reflection_FieldInfo_internal_from_handle_type)) + +ICALL_TYPE(MEMBERI, "System.Reflection.MemberInfo", MEMBERI_1) +HANDLES(ICALL(MEMBERI_1, "get_MetadataToken", ves_icall_reflection_get_token)) + +ICALL_TYPE(MBASE, "System.Reflection.MethodBase", MBASE_1) +HANDLES(ICALL(MBASE_1, "GetCurrentMethod", ves_icall_GetCurrentMethod)) +HANDLES(ICALL(MBASE_2, "GetMethodBodyInternal", ves_icall_System_Reflection_MethodBase_GetMethodBodyInternal)) +HANDLES(ICALL(MBASE_4, "GetMethodFromHandleInternalType_native", ves_icall_System_Reflection_MethodBase_GetMethodFromHandleInternalType_native)) + +ICALL_TYPE(MODULE, "System.Reflection.Module", MODULE_1) +HANDLES(ICALL(MODULE_1, "Close", ves_icall_System_Reflection_Module_Close)) +HANDLES(ICALL(MODULE_2, "GetGlobalType", ves_icall_System_Reflection_Module_GetGlobalType)) +HANDLES(ICALL(MODULE_3, "GetGuidInternal", ves_icall_System_Reflection_Module_GetGuidInternal)) +HANDLES(ICALL(MODULE_14, "GetHINSTANCE", ves_icall_System_Reflection_Module_GetHINSTANCE)) +HANDLES(ICALL(MODULE_4, "GetMDStreamVersion", ves_icall_System_Reflection_Module_GetMDStreamVersion)) +HANDLES(ICALL(MODULE_5, "GetPEKind", ves_icall_System_Reflection_Module_GetPEKind)) +HANDLES(ICALL(MODULE_6, "InternalGetTypes", ves_icall_System_Reflection_Module_InternalGetTypes)) +HANDLES(ICALL(MODULE_7, "ResolveFieldToken", ves_icall_System_Reflection_Module_ResolveFieldToken)) +HANDLES(ICALL(MODULE_8, "ResolveMemberToken", ves_icall_System_Reflection_Module_ResolveMemberToken)) +HANDLES(ICALL(MODULE_9, "ResolveMethodToken", ves_icall_System_Reflection_Module_ResolveMethodToken)) +HANDLES(ICALL(MODULE_10, "ResolveSignature", ves_icall_System_Reflection_Module_ResolveSignature)) +ICALL(MODULE_11, "ResolveStringToken", ves_icall_System_Reflection_Module_ResolveStringToken) +HANDLES(ICALL(MODULE_12, "ResolveTypeToken", ves_icall_System_Reflection_Module_ResolveTypeToken)) +HANDLES(ICALL(MODULE_13, "get_MetadataToken", ves_icall_reflection_get_token)) + +ICALL_TYPE(MCMETH, "System.Reflection.MonoCMethod", MCMETH_1) +HANDLES(ICALL(MCMETH_1, "GetGenericMethodDefinition_impl", ves_icall_MonoMethod_GetGenericMethodDefinition)) +ICALL(MCMETH_2, "InternalInvoke", ves_icall_InternalInvoke) +HANDLES(ICALL(MCMETH_3, "get_core_clr_security_level", ves_icall_MonoMethod_get_core_clr_security_level)) + +ICALL_TYPE(MEVIN, "System.Reflection.MonoEventInfo", MEVIN_1) +HANDLES(ICALL(MEVIN_1, "get_event_info", ves_icall_MonoEventInfo_get_event_info)) + +ICALL_TYPE(MFIELD, "System.Reflection.MonoField", MFIELD_1) +ICALL(MFIELD_1, "GetFieldOffset", ves_icall_MonoField_GetFieldOffset) +HANDLES(ICALL(MFIELD_2, "GetParentType", ves_icall_MonoField_GetParentType)) +ICALL(MFIELD_5, "GetRawConstantValue", ves_icall_MonoField_GetRawConstantValue) +ICALL(MFIELD_3, "GetValueInternal", ves_icall_MonoField_GetValueInternal) +HANDLES(ICALL(MFIELD_6, "ResolveType", ves_icall_MonoField_ResolveType)) +HANDLES(ICALL(MFIELD_4, "SetValueInternal", ves_icall_MonoField_SetValueInternal)) +ICALL(MFIELD_7, "get_core_clr_security_level", ves_icall_MonoField_get_core_clr_security_level) + +ICALL_TYPE(MMETH, "System.Reflection.MonoMethod", MMETH_2) +HANDLES(ICALL(MMETH_2, "GetGenericArguments", ves_icall_MonoMethod_GetGenericArguments)) +HANDLES(ICALL(MMETH_3, "GetGenericMethodDefinition_impl", ves_icall_MonoMethod_GetGenericMethodDefinition)) +HANDLES(ICALL(MMETH_11, "GetPInvoke", ves_icall_MonoMethod_GetPInvoke)) +ICALL(MMETH_4, "InternalInvoke", ves_icall_InternalInvoke) +HANDLES(ICALL(MMETH_5, "MakeGenericMethod_impl", ves_icall_MonoMethod_MakeGenericMethod_impl)) +HANDLES(ICALL(MMETH_6, "get_IsGenericMethod", ves_icall_MonoMethod_get_IsGenericMethod)) +HANDLES(ICALL(MMETH_7, "get_IsGenericMethodDefinition", ves_icall_MonoMethod_get_IsGenericMethodDefinition)) +HANDLES(ICALL(MMETH_8, "get_base_method", ves_icall_MonoMethod_get_base_method)) +HANDLES(ICALL(MMETH_10, "get_core_clr_security_level", ves_icall_MonoMethod_get_core_clr_security_level)) +HANDLES(ICALL(MMETH_9, "get_name", ves_icall_MonoMethod_get_name)) + +ICALL_TYPE(MMETHI, "System.Reflection.MonoMethodInfo", MMETHI_4) +ICALL(MMETHI_4, "get_method_attributes", vell_icall_get_method_attributes) +HANDLES(ICALL(MMETHI_1, "get_method_info", ves_icall_get_method_info)) +HANDLES(ICALL(MMETHI_2, "get_parameter_info", ves_icall_System_Reflection_MonoMethodInfo_get_parameter_info)) +HANDLES(ICALL(MMETHI_3, "get_retval_marshal", ves_icall_System_MonoMethodInfo_get_retval_marshal)) + +ICALL_TYPE(MPROPI, "System.Reflection.MonoPropertyInfo", MPROPI_1) +HANDLES(ICALL(MPROPI_1, "GetTypeModifiers", ves_icall_MonoPropertyInfo_GetTypeModifiers)) +ICALL(MPROPI_3, "get_default_value", property_info_get_default_value) +HANDLES(ICALL(MPROPI_2, "get_property_info", ves_icall_MonoPropertyInfo_get_property_info)) + +ICALL_TYPE(PARAMI, "System.Reflection.ParameterInfo", PARAMI_1) +HANDLES(ICALL(PARAMI_1, "GetMetadataToken", ves_icall_reflection_get_token)) +HANDLES(ICALL(PARAMI_2, "GetTypeModifiers", ves_icall_ParameterInfo_GetTypeModifiers)) + +ICALL_TYPE(PROPI, "System.Reflection.PropertyInfo", PROPI_1) +HANDLES(ICALL(PROPI_1, "internal_from_handle_type", ves_icall_System_Reflection_PropertyInfo_internal_from_handle_type)) + +ICALL_TYPE(RTFIELD, "System.Reflection.RtFieldInfo", RTFIELD_1) +ICALL(RTFIELD_1, "UnsafeGetValue", ves_icall_MonoField_GetValueInternal) + +ICALL_TYPE(RUNH, "System.Runtime.CompilerServices.RuntimeHelpers", RUNH_1) +ICALL(RUNH_1, "GetObjectValue", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetObjectValue) + /* REMOVEME: no longer needed, just so we dont break things when not needed */ +ICALL(RUNH_2, "GetOffsetToStringData", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetOffsetToStringData) +HANDLES(ICALL(RUNH_3, "InitializeArray", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_InitializeArray)) +ICALL(RUNH_4, "RunClassConstructor", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_RunClassConstructor) +ICALL(RUNH_5, "RunModuleConstructor", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_RunModuleConstructor) +ICALL(RUNH_5h, "SufficientExecutionStack", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_SufficientExecutionStack) +ICALL(RUNH_6, "get_OffsetToStringData", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetOffsetToStringData) + +ICALL_TYPE(GCH, "System.Runtime.InteropServices.GCHandle", GCH_1) +ICALL(GCH_1, "CheckCurrentDomain", mono_gc_GCHandle_CheckCurrentDomain) +ICALL(GCH_2, "FreeHandle", ves_icall_System_GCHandle_FreeHandle) +ICALL(GCH_3, "GetAddrOfPinnedObject", ves_icall_System_GCHandle_GetAddrOfPinnedObject) +ICALL(GCH_4, "GetTarget", ves_icall_System_GCHandle_GetTarget) +ICALL(GCH_5, "GetTargetHandle", ves_icall_System_GCHandle_GetTargetHandle) + +#ifndef DISABLE_COM +ICALL_TYPE(MARSHAL, "System.Runtime.InteropServices.Marshal", MARSHAL_1) +ICALL(MARSHAL_1, "AddRefInternal", ves_icall_System_Runtime_InteropServices_Marshal_AddRefInternal) +#else +ICALL_TYPE(MARSHAL, "System.Runtime.InteropServices.Marshal", MARSHAL_2) +#endif +ICALL(MARSHAL_2, "AllocCoTaskMem", ves_icall_System_Runtime_InteropServices_Marshal_AllocCoTaskMem) +ICALL(MARSHAL_51,"AllocCoTaskMemSize(uintptr)", ves_icall_System_Runtime_InteropServices_Marshal_AllocCoTaskMemSize) +ICALL(MARSHAL_3, "AllocHGlobal", ves_icall_System_Runtime_InteropServices_Marshal_AllocHGlobal) +ICALL(MARSHAL_50, "BufferToBSTR", ves_icall_System_Runtime_InteropServices_Marshal_BufferToBSTR) +ICALL(MARSHAL_4, "DestroyStructure", ves_icall_System_Runtime_InteropServices_Marshal_DestroyStructure) +ICALL(MARSHAL_5, "FreeBSTR", ves_icall_System_Runtime_InteropServices_Marshal_FreeBSTR) +ICALL(MARSHAL_6, "FreeCoTaskMem", ves_icall_System_Runtime_InteropServices_Marshal_FreeCoTaskMem) +ICALL(MARSHAL_7, "FreeHGlobal", ves_icall_System_Runtime_InteropServices_Marshal_FreeHGlobal) +#ifndef DISABLE_COM +ICALL(MARSHAL_44, "GetCCW", ves_icall_System_Runtime_InteropServices_Marshal_GetCCW) +ICALL(MARSHAL_8, "GetComSlotForMethodInfoInternal", ves_icall_System_Runtime_InteropServices_Marshal_GetComSlotForMethodInfoInternal) +#endif +HANDLES(ICALL(MARSHAL_9, "GetDelegateForFunctionPointerInternal", ves_icall_System_Runtime_InteropServices_Marshal_GetDelegateForFunctionPointerInternal)) +HANDLES(ICALL(MARSHAL_10, "GetFunctionPointerForDelegateInternal", ves_icall_System_Runtime_InteropServices_Marshal_GetFunctionPointerForDelegateInternal)) +#ifndef DISABLE_COM +ICALL(MARSHAL_52, "GetHRForException_WinRT", ves_icall_System_Runtime_InteropServices_Marshal_GetHRForException_WinRT) +ICALL(MARSHAL_45, "GetIDispatchForObjectInternal", ves_icall_System_Runtime_InteropServices_Marshal_GetIDispatchForObjectInternal) +ICALL(MARSHAL_46, "GetIUnknownForObjectInternal", ves_icall_System_Runtime_InteropServices_Marshal_GetIUnknownForObjectInternal) +#endif +ICALL(MARSHAL_11, "GetLastWin32Error", ves_icall_System_Runtime_InteropServices_Marshal_GetLastWin32Error) +#ifndef DISABLE_COM +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) +#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)) +HANDLES(ICALL(MARSHAL_15, "PtrToStringAnsi(intptr)", ves_icall_System_Runtime_InteropServices_Marshal_PtrToStringAnsi)) +ICALL(MARSHAL_16, "PtrToStringAnsi(intptr,int)", ves_icall_System_Runtime_InteropServices_Marshal_PtrToStringAnsi_len) +ICALL(MARSHAL_17, "PtrToStringBSTR", ves_icall_System_Runtime_InteropServices_Marshal_PtrToStringBSTR) +ICALL(MARSHAL_18, "PtrToStringUni(intptr)", ves_icall_System_Runtime_InteropServices_Marshal_PtrToStringUni) +ICALL(MARSHAL_19, "PtrToStringUni(intptr,int)", ves_icall_System_Runtime_InteropServices_Marshal_PtrToStringUni_len) +ICALL(MARSHAL_20, "PtrToStructure(intptr,System.Type)", ves_icall_System_Runtime_InteropServices_Marshal_PtrToStructure_type) +ICALL(MARSHAL_21, "PtrToStructure(intptr,object)", ves_icall_System_Runtime_InteropServices_Marshal_PtrToStructure) +#ifndef DISABLE_COM +ICALL(MARSHAL_22, "QueryInterfaceInternal", ves_icall_System_Runtime_InteropServices_Marshal_QueryInterfaceInternal) +#endif +ICALL(MARSHAL_43, "ReAllocCoTaskMem", ves_icall_System_Runtime_InteropServices_Marshal_ReAllocCoTaskMem) +ICALL(MARSHAL_23, "ReAllocHGlobal", ves_icall_System_Runtime_InteropServices_Marshal_ReAllocHGlobal) +#ifndef DISABLE_COM +ICALL(MARSHAL_49, "ReleaseComObjectInternal", ves_icall_System_Runtime_InteropServices_Marshal_ReleaseComObjectInternal) +ICALL(MARSHAL_29, "ReleaseInternal", ves_icall_System_Runtime_InteropServices_Marshal_ReleaseInternal) +#endif +HANDLES(ICALL(MARSHAL_30, "SizeOf", ves_icall_System_Runtime_InteropServices_Marshal_SizeOf)) +ICALL(MARSHAL_31, "StringToBSTR", ves_icall_System_Runtime_InteropServices_Marshal_StringToBSTR) +ICALL(MARSHAL_32, "StringToHGlobalAnsi", ves_icall_System_Runtime_InteropServices_Marshal_StringToHGlobalAnsi) +ICALL(MARSHAL_33, "StringToHGlobalUni", ves_icall_System_Runtime_InteropServices_Marshal_StringToHGlobalUni) +ICALL(MARSHAL_34, "StructureToPtr", ves_icall_System_Runtime_InteropServices_Marshal_StructureToPtr) +ICALL(MARSHAL_35, "UnsafeAddrOfPinnedArrayElement", ves_icall_System_Runtime_InteropServices_Marshal_UnsafeAddrOfPinnedArrayElement) + +ICALL(MARSHAL_41, "copy_from_unmanaged", ves_icall_System_Runtime_InteropServices_Marshal_copy_from_unmanaged) +ICALL(MARSHAL_42, "copy_to_unmanaged", ves_icall_System_Runtime_InteropServices_Marshal_copy_to_unmanaged) + +#ifndef DISABLE_COM +ICALL_TYPE(WINDOWSRUNTIME_UNM, "System.Runtime.InteropServices.WindowsRuntime.UnsafeNativeMethods", WINDOWSRUNTIME_UNM_0) +ICALL(WINDOWSRUNTIME_UNM_0, "GetRestrictedErrorInfo", ves_icall_System_Runtime_InteropServices_WindowsRuntime_UnsafeNativeMethods_GetRestrictedErrorInfo) +ICALL(WINDOWSRUNTIME_UNM_1, "RoOriginateLanguageException", ves_icall_System_Runtime_InteropServices_WindowsRuntime_UnsafeNativeMethods_RoOriginateLanguageException) +ICALL(WINDOWSRUNTIME_UNM_2, "RoReportUnhandledError", ves_icall_System_Runtime_InteropServices_WindowsRuntime_UnsafeNativeMethods_RoReportUnhandledError) +ICALL(WINDOWSRUNTIME_UNM_3, "WindowsCreateString", ves_icall_System_Runtime_InteropServices_WindowsRuntime_UnsafeNativeMethods_WindowsCreateString) +ICALL(WINDOWSRUNTIME_UNM_4, "WindowsDeleteString", ves_icall_System_Runtime_InteropServices_WindowsRuntime_UnsafeNativeMethods_WindowsDeleteString) +ICALL(WINDOWSRUNTIME_UNM_5, "WindowsGetStringRawBuffer", ves_icall_System_Runtime_InteropServices_WindowsRuntime_UnsafeNativeMethods_WindowsGetStringRawBuffer) +#endif + +ICALL_TYPE(ACTS, "System.Runtime.Remoting.Activation.ActivationServices", ACTS_1) +HANDLES(ICALL(ACTS_1, "AllocateUninitializedClassInstance", ves_icall_System_Runtime_Activation_ActivationServices_AllocateUninitializedClassInstance)) +HANDLES(ICALL(ACTS_2, "EnableProxyActivation", ves_icall_System_Runtime_Activation_ActivationServices_EnableProxyActivation)) + +ICALL_TYPE(CONTEXT, "System.Runtime.Remoting.Contexts.Context", CONTEXT_1) +HANDLES(ICALL(CONTEXT_1, "RegisterContext", ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext)) +HANDLES(ICALL(CONTEXT_2, "ReleaseContext", ves_icall_System_Runtime_Remoting_Contexts_Context_ReleaseContext)) + +ICALL_TYPE(ARES, "System.Runtime.Remoting.Messaging.AsyncResult", ARES_1) +ICALL(ARES_1, "Invoke", ves_icall_System_Runtime_Remoting_Messaging_AsyncResult_Invoke) + +#ifndef DISABLE_REMOTING +ICALL_TYPE(REALP, "System.Runtime.Remoting.Proxies.RealProxy", REALP_1) +ICALL(REALP_1, "InternalGetProxyType", ves_icall_Remoting_RealProxy_InternalGetProxyType) +HANDLES(ICALL(REALP_2, "InternalGetTransparentProxy", ves_icall_Remoting_RealProxy_GetTransparentProxy)) + +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) +ICALL(RVH_1, "GetRuntimeId", ves_icall_System_Runtime_Versioning_VersioningHelper_GetRuntimeId) + +ICALL_TYPE(RFH, "System.RuntimeFieldHandle", RFH_1) +ICALL(RFH_1, "SetValueDirect", ves_icall_System_RuntimeFieldHandle_SetValueDirect) +HANDLES(ICALL(RFH_2, "SetValueInternal", ves_icall_MonoField_SetValueInternal)) + +ICALL_TYPE(MHAN, "System.RuntimeMethodHandle", MHAN_1) +HANDLES(ICALL(MHAN_1, "GetFunctionPointer", ves_icall_RuntimeMethodHandle_GetFunctionPointer)) + +ICALL_TYPE(RT, "System.RuntimeType", RT_1) +HANDLES(ICALL(RT_1, "CreateInstanceInternal", ves_icall_System_Activator_CreateInstanceInternal)) +HANDLES(ICALL(RT_2, "GetConstructors_native", ves_icall_RuntimeType_GetConstructors_native)) +HANDLES(ICALL(RT_30, "GetCorrespondingInflatedConstructor", ves_icall_RuntimeType_GetCorrespondingInflatedMethod)) +HANDLES(ICALL(RT_31, "GetCorrespondingInflatedMethod", ves_icall_RuntimeType_GetCorrespondingInflatedMethod)) +HANDLES(ICALL(RT_3, "GetEvents_native", ves_icall_RuntimeType_GetEvents_native)) +HANDLES(ICALL(RT_5, "GetFields_native", ves_icall_RuntimeType_GetFields_native)) +HANDLES(ICALL(RT_6, "GetGenericArgumentsInternal", ves_icall_RuntimeType_GetGenericArguments)) +HANDLES(ICALL(RT_9, "GetGenericParameterPosition", ves_icall_RuntimeType_GetGenericParameterPosition)) +HANDLES(ICALL(RT_10, "GetInterfaceMapData", ves_icall_RuntimeType_GetInterfaceMapData)) +HANDLES(ICALL(RT_11, "GetInterfaces", ves_icall_RuntimeType_GetInterfaces)) +HANDLES(ICALL(RT_12, "GetMethodsByName_native", ves_icall_RuntimeType_GetMethodsByName_native)) +HANDLES(ICALL(RT_13, "GetNestedTypes_native", ves_icall_RuntimeType_GetNestedTypes_native)) +HANDLES(ICALL(RT_14, "GetPacking", ves_icall_RuntimeType_GetPacking)) +HANDLES(ICALL(RT_15, "GetPropertiesByName_native", ves_icall_RuntimeType_GetPropertiesByName_native)) +HANDLES(ICALL(RT_16, "GetTypeCodeImplInternal", ves_icall_type_GetTypeCodeInternal)) +HANDLES(ICALL(RT_28, "IsTypeExportedToWindowsRuntime", ves_icall_System_RuntimeType_IsTypeExportedToWindowsRuntime)) +HANDLES(ICALL(RT_29, "IsWindowsRuntimeObjectType", ves_icall_System_RuntimeType_IsWindowsRuntimeObjectType)) +HANDLES(ICALL(RT_17, "MakeGenericType", ves_icall_RuntimeType_MakeGenericType)) +HANDLES(ICALL(RT_18, "MakePointerType", ves_icall_RuntimeType_MakePointerType)) +HANDLES(ICALL(RT_19, "getFullName", ves_icall_System_RuntimeType_getFullName)) +HANDLES(ICALL(RT_21, "get_DeclaringMethod", ves_icall_RuntimeType_get_DeclaringMethod)) +HANDLES(ICALL(RT_22, "get_DeclaringType", ves_icall_RuntimeType_get_DeclaringType)) +HANDLES(ICALL(RT_23, "get_Name", ves_icall_RuntimeType_get_Name)) +HANDLES(ICALL(RT_24, "get_Namespace", ves_icall_RuntimeType_get_Namespace)) +HANDLES(ICALL(RT_25, "get_core_clr_security_level", vell_icall_RuntimeType_get_core_clr_security_level)) +HANDLES(ICALL(RT_26, "make_array_type", ves_icall_RuntimeType_make_array_type)) +HANDLES(ICALL(RT_27, "make_byref_type", ves_icall_RuntimeType_make_byref_type)) + +ICALL_TYPE(RTH, "System.RuntimeTypeHandle", RTH_1) +HANDLES(ICALL(RTH_1, "GetArrayRank", ves_icall_RuntimeTypeHandle_GetArrayRank)) +HANDLES(ICALL(RTH_2, "GetAssembly", ves_icall_RuntimeTypeHandle_GetAssembly)) +HANDLES(ICALL(RTH_3, "GetAttributes", ves_icall_RuntimeTypeHandle_GetAttributes)) +HANDLES(ICALL(RTH_4, "GetBaseType", ves_icall_RuntimeTypeHandle_GetBaseType)) +HANDLES(ICALL(RTH_5, "GetElementType", ves_icall_RuntimeTypeHandle_GetElementType)) +HANDLES(ICALL(RTH_19, "GetGenericParameterInfo", ves_icall_RuntimeTypeHandle_GetGenericParameterInfo)) +HANDLES(ICALL(RTH_6, "GetGenericTypeDefinition_impl", ves_icall_RuntimeTypeHandle_GetGenericTypeDefinition_impl)) +HANDLES(ICALL(RTH_7, "GetMetadataToken", ves_icall_reflection_get_token)) +HANDLES(ICALL(RTH_8, "GetModule", ves_icall_RuntimeTypeHandle_GetModule)) +HANDLES(ICALL(RTH_9, "HasInstantiation", ves_icall_RuntimeTypeHandle_HasInstantiation)) +HANDLES(ICALL(RTH_20, "HasReferences", ves_icall_RuntimeTypeHandle_HasReferences)) +HANDLES(ICALL(RTH_10, "IsArray", ves_icall_RuntimeTypeHandle_IsArray)) +HANDLES(ICALL(RTH_11, "IsByRef", ves_icall_RuntimeTypeHandle_IsByRef)) +HANDLES(ICALL(RTH_12, "IsComObject", ves_icall_RuntimeTypeHandle_IsComObject)) +HANDLES(ICALL(RTH_13, "IsGenericTypeDefinition", ves_icall_RuntimeTypeHandle_IsGenericTypeDefinition)) +HANDLES(ICALL(RTH_14, "IsGenericVariable", ves_icall_RuntimeTypeHandle_IsGenericVariable)) +HANDLES(ICALL(RTH_15, "IsInstanceOfType", ves_icall_RuntimeTypeHandle_IsInstanceOfType)) +HANDLES(ICALL(RTH_16, "IsPointer", ves_icall_RuntimeTypeHandle_IsPointer)) +HANDLES(ICALL(RTH_17, "IsPrimitive", ves_icall_RuntimeTypeHandle_IsPrimitive)) +HANDLES(ICALL(RTH_18, "type_is_assignable_from", ves_icall_RuntimeTypeHandle_type_is_assignable_from)) + +ICALL_TYPE(RNG, "System.Security.Cryptography.RNGCryptoServiceProvider", RNG_1) +ICALL(RNG_1, "RngClose", ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngClose) +ICALL(RNG_2, "RngGetBytes", ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngGetBytes) +ICALL(RNG_3, "RngInitialize", ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngInitialize) +ICALL(RNG_4, "RngOpen", ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngOpen) + +#ifndef DISABLE_POLICY_EVIDENCE +ICALL_TYPE(EVID, "System.Security.Policy.Evidence", EVID_1) +HANDLES(ICALL(EVID_1, "IsAuthenticodePresent", ves_icall_System_Security_Policy_Evidence_IsAuthenticodePresent)) + +ICALL_TYPE(WINID, "System.Security.Principal.WindowsIdentity", WINID_1) +HANDLES(ICALL(WINID_1, "GetCurrentToken", ves_icall_System_Security_Principal_WindowsIdentity_GetCurrentToken)) +HANDLES(ICALL(WINID_2, "GetTokenName", ves_icall_System_Security_Principal_WindowsIdentity_GetTokenName)) +HANDLES(ICALL(WINID_3, "GetUserToken", ves_icall_System_Security_Principal_WindowsIdentity_GetUserToken)) +ICALL(WINID_4, "_GetRoles", ves_icall_System_Security_Principal_WindowsIdentity_GetRoles) + +ICALL_TYPE(WINIMP, "System.Security.Principal.WindowsImpersonationContext", WINIMP_1) +ICALL(WINIMP_1, "CloseToken", ves_icall_System_Security_Principal_WindowsImpersonationContext_CloseToken) +ICALL(WINIMP_2, "DuplicateToken", ves_icall_System_Security_Principal_WindowsImpersonationContext_DuplicateToken) +ICALL(WINIMP_3, "RevertToSelf", ves_icall_System_Security_Principal_WindowsImpersonationContext_RevertToSelf) +ICALL(WINIMP_4, "SetCurrentToken", ves_icall_System_Security_Principal_WindowsImpersonationContext_SetCurrentToken) + +ICALL_TYPE(WINPRIN, "System.Security.Principal.WindowsPrincipal", WINPRIN_1) +ICALL(WINPRIN_1, "IsMemberOfGroupId", ves_icall_System_Security_Principal_WindowsPrincipal_IsMemberOfGroupId) +ICALL(WINPRIN_2, "IsMemberOfGroupName", ves_icall_System_Security_Principal_WindowsPrincipal_IsMemberOfGroupName) + +ICALL_TYPE(SECSTRING, "System.Security.SecureString", SECSTRING_1) +ICALL(SECSTRING_1, "DecryptInternal", ves_icall_System_Security_SecureString_DecryptInternal) +ICALL(SECSTRING_2, "EncryptInternal", ves_icall_System_Security_SecureString_EncryptInternal) +#endif /* !DISABLE_POLICY_EVIDENCE */ + +ICALL_TYPE(SECMAN, "System.Security.SecurityManager", SECMAN_1) +ICALL(SECMAN_1, "get_RequiresElevatedPermissions", mono_security_core_clr_require_elevated_permissions) +ICALL(SECMAN_2, "get_SecurityEnabled", ves_icall_System_Security_SecurityManager_get_SecurityEnabled) +ICALL(SECMAN_3, "set_SecurityEnabled", ves_icall_System_Security_SecurityManager_set_SecurityEnabled) + +ICALL_TYPE(STRING, "System.String", STRING_1) +ICALL(STRING_1, ".ctor(char*)", ves_icall_System_String_ctor_RedirectToCreateString) +ICALL(STRING_2, ".ctor(char*,int,int)", ves_icall_System_String_ctor_RedirectToCreateString) +ICALL(STRING_3, ".ctor(char,int)", ves_icall_System_String_ctor_RedirectToCreateString) +ICALL(STRING_4, ".ctor(char[])", ves_icall_System_String_ctor_RedirectToCreateString) +ICALL(STRING_5, ".ctor(char[],int,int)", ves_icall_System_String_ctor_RedirectToCreateString) +ICALL(STRING_6, ".ctor(sbyte*)", ves_icall_System_String_ctor_RedirectToCreateString) +ICALL(STRING_7, ".ctor(sbyte*,int,int)", ves_icall_System_String_ctor_RedirectToCreateString) +ICALL(STRING_8, ".ctor(sbyte*,int,int,System.Text.Encoding)", ves_icall_System_String_ctor_RedirectToCreateString) +ICALL(STRING_9, "FastAllocateString", ves_icall_System_String_InternalAllocateStr) +ICALL(STRING_10, "InternalIntern", ves_icall_System_String_InternalIntern) +ICALL(STRING_11, "InternalIsInterned", ves_icall_System_String_InternalIsInterned) + +ICALL_TYPE(TENC, "System.Text.EncodingHelper", TENC_1) +HANDLES(ICALL(TENC_1, "InternalCodePage", ves_icall_System_Text_EncodingHelper_InternalCodePage)) + +ICALL_TYPE(UNORM, "System.Text.Normalization", UNORM_1) +HANDLES(ICALL(UNORM_1, "load_normalization_resource", ves_icall_System_Text_Normalization_load_normalization_resource)) + +ICALL_TYPE(ILOCK, "System.Threading.Interlocked", ILOCK_1) +ICALL(ILOCK_1, "Add(int&,int)", ves_icall_System_Threading_Interlocked_Add_Int) +ICALL(ILOCK_2, "Add(long&,long)", ves_icall_System_Threading_Interlocked_Add_Long) +ICALL(ILOCK_3, "CompareExchange(T&,T,T)", ves_icall_System_Threading_Interlocked_CompareExchange_T) +ICALL(ILOCK_4, "CompareExchange(double&,double,double)", ves_icall_System_Threading_Interlocked_CompareExchange_Double) +ICALL(ILOCK_5, "CompareExchange(int&,int,int)", ves_icall_System_Threading_Interlocked_CompareExchange_Int) +ICALL(ILOCK_6, "CompareExchange(int&,int,int,bool&)", ves_icall_System_Threading_Interlocked_CompareExchange_Int_Success) +ICALL(ILOCK_7, "CompareExchange(intptr&,intptr,intptr)", ves_icall_System_Threading_Interlocked_CompareExchange_IntPtr) +ICALL(ILOCK_8, "CompareExchange(long&,long,long)", ves_icall_System_Threading_Interlocked_CompareExchange_Long) +ICALL(ILOCK_9, "CompareExchange(object&,object,object)", ves_icall_System_Threading_Interlocked_CompareExchange_Object) +ICALL(ILOCK_10, "CompareExchange(single&,single,single)", ves_icall_System_Threading_Interlocked_CompareExchange_Single) +ICALL(ILOCK_11, "Decrement(int&)", ves_icall_System_Threading_Interlocked_Decrement_Int) +ICALL(ILOCK_12, "Decrement(long&)", ves_icall_System_Threading_Interlocked_Decrement_Long) +ICALL(ILOCK_13, "Exchange(T&,T)", ves_icall_System_Threading_Interlocked_Exchange_T) +ICALL(ILOCK_14, "Exchange(double&,double)", ves_icall_System_Threading_Interlocked_Exchange_Double) +ICALL(ILOCK_15, "Exchange(int&,int)", ves_icall_System_Threading_Interlocked_Exchange_Int) +ICALL(ILOCK_16, "Exchange(intptr&,intptr)", ves_icall_System_Threading_Interlocked_Exchange_IntPtr) +ICALL(ILOCK_17, "Exchange(long&,long)", ves_icall_System_Threading_Interlocked_Exchange_Long) +ICALL(ILOCK_18, "Exchange(object&,object)", ves_icall_System_Threading_Interlocked_Exchange_Object) +ICALL(ILOCK_19, "Exchange(single&,single)", ves_icall_System_Threading_Interlocked_Exchange_Single) +ICALL(ILOCK_20, "Increment(int&)", ves_icall_System_Threading_Interlocked_Increment_Int) +ICALL(ILOCK_21, "Increment(long&)", ves_icall_System_Threading_Interlocked_Increment_Long) +ICALL(ILOCK_22, "Read(long&)", ves_icall_System_Threading_Interlocked_Read_Long) + +ICALL_TYPE(ITHREAD, "System.Threading.InternalThread", ITHREAD_1) +ICALL(ITHREAD_1, "Thread_free_internal", ves_icall_System_Threading_InternalThread_Thread_free_internal) + +ICALL_TYPE(MONIT, "System.Threading.Monitor", MONIT_8) +ICALL(MONIT_8, "Enter", ves_icall_System_Threading_Monitor_Monitor_Enter) +ICALL(MONIT_1, "Exit", mono_monitor_exit) +ICALL(MONIT_2, "Monitor_pulse", ves_icall_System_Threading_Monitor_Monitor_pulse) +ICALL(MONIT_3, "Monitor_pulse_all", ves_icall_System_Threading_Monitor_Monitor_pulse_all) +ICALL(MONIT_4, "Monitor_test_owner", ves_icall_System_Threading_Monitor_Monitor_test_owner) +ICALL(MONIT_5, "Monitor_test_synchronised", ves_icall_System_Threading_Monitor_Monitor_test_synchronised) +ICALL(MONIT_7, "Monitor_wait", ves_icall_System_Threading_Monitor_Monitor_wait) +ICALL(MONIT_9, "try_enter_with_atomic_var", ves_icall_System_Threading_Monitor_Monitor_try_enter_with_atomic_var) + +ICALL_TYPE(MUTEX, "System.Threading.Mutex", MUTEX_1) +HANDLES(ICALL(MUTEX_1, "CreateMutex_internal(bool,string,bool&)", ves_icall_System_Threading_Mutex_CreateMutex_internal)) +HANDLES(ICALL(MUTEX_2, "OpenMutex_internal(string,System.Security.AccessControl.MutexRights,System.IO.MonoIOError&)", ves_icall_System_Threading_Mutex_OpenMutex_internal)) +ICALL(MUTEX_3, "ReleaseMutex_internal(intptr)", ves_icall_System_Threading_Mutex_ReleaseMutex_internal) + +ICALL_TYPE(NATIVEC, "System.Threading.NativeEventCalls", NATIVEC_1) +ICALL(NATIVEC_1, "CloseEvent_internal", ves_icall_System_Threading_Events_CloseEvent_internal) +HANDLES(ICALL(NATIVEC_2, "CreateEvent_internal(bool,bool,string,int&)", ves_icall_System_Threading_Events_CreateEvent_internal)) +HANDLES(ICALL(NATIVEC_3, "OpenEvent_internal(string,System.Security.AccessControl.EventWaitHandleRights,int&)", ves_icall_System_Threading_Events_OpenEvent_internal)) +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) +ICALL(SEMA_3, "ReleaseSemaphore_internal(intptr,int,int&)", ves_icall_System_Threading_Semaphore_ReleaseSemaphore_internal) + +ICALL_TYPE(THREAD, "System.Threading.Thread", THREAD_1) +ICALL(THREAD_1, "Abort_internal(System.Threading.InternalThread,object)", ves_icall_System_Threading_Thread_Abort) +ICALL(THREAD_1a, "ByteArrayToCurrentDomain(byte[])", ves_icall_System_Threading_Thread_ByteArrayToCurrentDomain) +ICALL(THREAD_1b, "ByteArrayToRootDomain(byte[])", ves_icall_System_Threading_Thread_ByteArrayToRootDomain) +ICALL(THREAD_2, "ClrState(System.Threading.InternalThread,System.Threading.ThreadState)", ves_icall_System_Threading_Thread_ClrState) +ICALL(THREAD_2a, "ConstructInternalThread", ves_icall_System_Threading_Thread_ConstructInternalThread) +ICALL(THREAD_55, "GetAbortExceptionState", ves_icall_System_Threading_Thread_GetAbortExceptionState) +ICALL(THREAD_60, "GetCurrentThread", ves_icall_System_Threading_Thread_GetCurrentThread) +ICALL(THREAD_7, "GetDomainID", ves_icall_System_Threading_Thread_GetDomainID) +ICALL(THREAD_8, "GetName_internal(System.Threading.InternalThread)", ves_icall_System_Threading_Thread_GetName_internal) +ICALL(THREAD_57, "GetPriorityNative", ves_icall_System_Threading_Thread_GetPriority) +ICALL(THREAD_59, "GetStackTraces", ves_icall_System_Threading_Thread_GetStackTraces) +ICALL(THREAD_11, "GetState(System.Threading.InternalThread)", ves_icall_System_Threading_Thread_GetState) +ICALL(THREAD_53, "InterruptInternal", ves_icall_System_Threading_Thread_Interrupt_internal) +ICALL(THREAD_12, "JoinInternal", ves_icall_System_Threading_Thread_Join_internal) +ICALL(THREAD_13, "MemoryBarrier", ves_icall_System_Threading_Thread_MemoryBarrier) +ICALL(THREAD_14, "ResetAbortNative", ves_icall_System_Threading_Thread_ResetAbort) +ICALL(THREAD_15, "ResumeInternal", ves_icall_System_Threading_Thread_Resume) +ICALL(THREAD_18, "SetName_internal(System.Threading.InternalThread,string)", ves_icall_System_Threading_Thread_SetName_internal) +ICALL(THREAD_58, "SetPriorityNative", ves_icall_System_Threading_Thread_SetPriority) +ICALL(THREAD_21, "SetState(System.Threading.InternalThread,System.Threading.ThreadState)", ves_icall_System_Threading_Thread_SetState) +ICALL(THREAD_22, "SleepInternal", ves_icall_System_Threading_Thread_Sleep_internal) +ICALL(THREAD_54, "SpinWait_nop", ves_icall_System_Threading_Thread_SpinWait_nop) +ICALL(THREAD_23, "SuspendInternal", ves_icall_System_Threading_Thread_Suspend) +ICALL(THREAD_56, "SystemMaxStackStize", mono_threads_get_max_stack_size) +ICALL(THREAD_25, "Thread_internal", ves_icall_System_Threading_Thread_Thread_internal) +ICALL(THREAD_26, "VolatileRead(byte&)", ves_icall_System_Threading_Thread_VolatileRead1) +ICALL(THREAD_27, "VolatileRead(double&)", ves_icall_System_Threading_Thread_VolatileReadDouble) +ICALL(THREAD_28, "VolatileRead(int&)", ves_icall_System_Threading_Thread_VolatileRead4) +ICALL(THREAD_29, "VolatileRead(int16&)", ves_icall_System_Threading_Thread_VolatileRead2) +ICALL(THREAD_30, "VolatileRead(intptr&)", ves_icall_System_Threading_Thread_VolatileReadIntPtr) +ICALL(THREAD_31, "VolatileRead(long&)", ves_icall_System_Threading_Thread_VolatileRead8) +ICALL(THREAD_32, "VolatileRead(object&)", ves_icall_System_Threading_Thread_VolatileReadObject) +ICALL(THREAD_33, "VolatileRead(sbyte&)", ves_icall_System_Threading_Thread_VolatileRead1) +ICALL(THREAD_34, "VolatileRead(single&)", ves_icall_System_Threading_Thread_VolatileReadFloat) +ICALL(THREAD_35, "VolatileRead(uint&)", ves_icall_System_Threading_Thread_VolatileRead4) +ICALL(THREAD_36, "VolatileRead(uint16&)", ves_icall_System_Threading_Thread_VolatileRead2) +ICALL(THREAD_37, "VolatileRead(uintptr&)", ves_icall_System_Threading_Thread_VolatileReadIntPtr) +ICALL(THREAD_38, "VolatileRead(ulong&)", ves_icall_System_Threading_Thread_VolatileRead8) +ICALL(THREAD_39, "VolatileWrite(byte&,byte)", ves_icall_System_Threading_Thread_VolatileWrite1) +ICALL(THREAD_40, "VolatileWrite(double&,double)", ves_icall_System_Threading_Thread_VolatileWriteDouble) +ICALL(THREAD_41, "VolatileWrite(int&,int)", ves_icall_System_Threading_Thread_VolatileWrite4) +ICALL(THREAD_42, "VolatileWrite(int16&,int16)", ves_icall_System_Threading_Thread_VolatileWrite2) +ICALL(THREAD_43, "VolatileWrite(intptr&,intptr)", ves_icall_System_Threading_Thread_VolatileWriteIntPtr) +ICALL(THREAD_44, "VolatileWrite(long&,long)", ves_icall_System_Threading_Thread_VolatileWrite8) +ICALL(THREAD_45, "VolatileWrite(object&,object)", ves_icall_System_Threading_Thread_VolatileWriteObject) +ICALL(THREAD_46, "VolatileWrite(sbyte&,sbyte)", ves_icall_System_Threading_Thread_VolatileWrite1) +ICALL(THREAD_47, "VolatileWrite(single&,single)", ves_icall_System_Threading_Thread_VolatileWriteFloat) +ICALL(THREAD_48, "VolatileWrite(uint&,uint)", ves_icall_System_Threading_Thread_VolatileWrite4) +ICALL(THREAD_49, "VolatileWrite(uint16&,uint16)", ves_icall_System_Threading_Thread_VolatileWrite2) +ICALL(THREAD_50, "VolatileWrite(uintptr&,uintptr)", ves_icall_System_Threading_Thread_VolatileWriteIntPtr) +ICALL(THREAD_51, "VolatileWrite(ulong&,ulong)", ves_icall_System_Threading_Thread_VolatileWrite8) +ICALL(THREAD_9, "YieldInternal", ves_icall_System_Threading_Thread_Yield) +ICALL(THREAD_52, "current_lcid()", ves_icall_System_Threading_Thread_current_lcid) + +ICALL_TYPE(THREADP, "System.Threading.ThreadPool", THREADP_1) +ICALL(THREADP_1, "BindIOCompletionCallbackNative", ves_icall_System_Threading_ThreadPool_BindIOCompletionCallbackNative) +ICALL(THREADP_2, "GetAvailableThreadsNative", ves_icall_System_Threading_ThreadPool_GetAvailableThreadsNative) +ICALL(THREADP_3, "GetMaxThreadsNative", ves_icall_System_Threading_ThreadPool_GetMaxThreadsNative) +ICALL(THREADP_4, "GetMinThreadsNative", ves_icall_System_Threading_ThreadPool_GetMinThreadsNative) +ICALL(THREADP_5, "InitializeVMTp", ves_icall_System_Threading_ThreadPool_InitializeVMTp) +ICALL(THREADP_6, "IsThreadPoolHosted", ves_icall_System_Threading_ThreadPool_IsThreadPoolHosted) +ICALL(THREADP_7, "NotifyWorkItemComplete", ves_icall_System_Threading_ThreadPool_NotifyWorkItemComplete) +ICALL(THREADP_8, "NotifyWorkItemProgressNative", ves_icall_System_Threading_ThreadPool_NotifyWorkItemProgressNative) +ICALL(THREADP_9, "PostQueuedCompletionStatus", ves_icall_System_Threading_ThreadPool_PostQueuedCompletionStatus) +ICALL(THREADP_11, "ReportThreadStatus", ves_icall_System_Threading_ThreadPool_ReportThreadStatus) +ICALL(THREADP_12, "RequestWorkerThread", ves_icall_System_Threading_ThreadPool_RequestWorkerThread) +ICALL(THREADP_13, "SetMaxThreadsNative", ves_icall_System_Threading_ThreadPool_SetMaxThreadsNative) +ICALL(THREADP_14, "SetMinThreadsNative", ves_icall_System_Threading_ThreadPool_SetMinThreadsNative) + +ICALL_TYPE(TTIMER, "System.Threading.Timer", TTIMER_1) +ICALL(TTIMER_1, "GetTimeMonotonic", mono_100ns_ticks) + +ICALL_TYPE(VOLATILE, "System.Threading.Volatile", VOLATILE_28) +ICALL(VOLATILE_28, "Read(T&)", ves_icall_System_Threading_Volatile_Read_T) +ICALL(VOLATILE_1, "Read(bool&)", ves_icall_System_Threading_Volatile_Read1) +ICALL(VOLATILE_2, "Read(byte&)", ves_icall_System_Threading_Volatile_Read1) +ICALL(VOLATILE_3, "Read(double&)", ves_icall_System_Threading_Volatile_ReadDouble) +ICALL(VOLATILE_4, "Read(int&)", ves_icall_System_Threading_Volatile_Read4) +ICALL(VOLATILE_5, "Read(int16&)", ves_icall_System_Threading_Volatile_Read2) +ICALL(VOLATILE_6, "Read(intptr&)", ves_icall_System_Threading_Volatile_ReadIntPtr) +ICALL(VOLATILE_7, "Read(long&)", ves_icall_System_Threading_Volatile_Read8) +ICALL(VOLATILE_8, "Read(sbyte&)", ves_icall_System_Threading_Volatile_Read1) +ICALL(VOLATILE_9, "Read(single&)", ves_icall_System_Threading_Volatile_ReadFloat) +ICALL(VOLATILE_10, "Read(uint&)", ves_icall_System_Threading_Volatile_Read4) +ICALL(VOLATILE_11, "Read(uint16&)", ves_icall_System_Threading_Volatile_Read2) +ICALL(VOLATILE_12, "Read(uintptr&)", ves_icall_System_Threading_Volatile_ReadIntPtr) +ICALL(VOLATILE_13, "Read(ulong&)", ves_icall_System_Threading_Volatile_Read8) +ICALL(VOLATILE_27, "Write(T&,T)", ves_icall_System_Threading_Volatile_Write_T) +ICALL(VOLATILE_14, "Write(bool&,bool)", ves_icall_System_Threading_Volatile_Write1) +ICALL(VOLATILE_15, "Write(byte&,byte)", ves_icall_System_Threading_Volatile_Write1) +ICALL(VOLATILE_16, "Write(double&,double)", ves_icall_System_Threading_Volatile_WriteDouble) +ICALL(VOLATILE_17, "Write(int&,int)", ves_icall_System_Threading_Volatile_Write4) +ICALL(VOLATILE_18, "Write(int16&,int16)", ves_icall_System_Threading_Volatile_Write2) +ICALL(VOLATILE_19, "Write(intptr&,intptr)", ves_icall_System_Threading_Volatile_WriteIntPtr) +ICALL(VOLATILE_20, "Write(long&,long)", ves_icall_System_Threading_Volatile_Write8) +ICALL(VOLATILE_21, "Write(sbyte&,sbyte)", ves_icall_System_Threading_Volatile_Write1) +ICALL(VOLATILE_22, "Write(single&,single)", ves_icall_System_Threading_Volatile_WriteFloat) +ICALL(VOLATILE_23, "Write(uint&,uint)", ves_icall_System_Threading_Volatile_Write4) +ICALL(VOLATILE_24, "Write(uint16&,uint16)", ves_icall_System_Threading_Volatile_Write2) +ICALL(VOLATILE_25, "Write(uintptr&,uintptr)", ves_icall_System_Threading_Volatile_WriteIntPtr) +ICALL(VOLATILE_26, "Write(ulong&,ulong)", ves_icall_System_Threading_Volatile_Write8) + +ICALL_TYPE(WAITH, "System.Threading.WaitHandle", WAITH_1) +HANDLES(ICALL(WAITH_1, "SignalAndWait_Internal", ves_icall_System_Threading_WaitHandle_SignalAndWait_Internal)) +HANDLES(ICALL(WAITH_2, "Wait_internal", ves_icall_System_Threading_WaitHandle_Wait_internal)) + +ICALL_TYPE(TYPE, "System.Type", TYPE_1) +HANDLES(ICALL(TYPE_1, "internal_from_handle", ves_icall_System_Type_internal_from_handle)) +HANDLES(ICALL(TYPE_2, "internal_from_name", ves_icall_System_Type_internal_from_name)) + +ICALL_TYPE(TYPEDR, "System.TypedReference", TYPEDR_1) +ICALL(TYPEDR_1, "InternalToObject", mono_TypedReference_ToObject) +ICALL(TYPEDR_2, "MakeTypedReferenceInternal", mono_TypedReference_MakeTypedReferenceInternal) + +ICALL_TYPE(VALUET, "System.ValueType", VALUET_1) +ICALL(VALUET_1, "InternalEquals", ves_icall_System_ValueType_Equals) +ICALL(VALUET_2, "InternalGetHashCode", ves_icall_System_ValueType_InternalGetHashCode) + +ICALL_TYPE(WEBIC, "System.Web.Util.ICalls", WEBIC_1) +HANDLES(ICALL(WEBIC_1, "GetMachineConfigPath", ves_icall_System_Configuration_DefaultConfig_get_machine_config_path)) +HANDLES(ICALL(WEBIC_2, "GetMachineInstallDirectory", ves_icall_System_Web_Util_ICalls_get_machine_install_dir)) +HANDLES(ICALL(WEBIC_3, "GetUnmanagedResourcesPtr", ves_icall_get_resources_ptr)) + +#ifndef DISABLE_COM +ICALL_TYPE(COMOBJ, "System.__ComObject", COMOBJ_1) +ICALL(COMOBJ_1, "CreateRCW", ves_icall_System_ComObject_CreateRCW) +ICALL(COMOBJ_2, "GetInterfaceInternal", ves_icall_System_ComObject_GetInterfaceInternal) +ICALL(COMOBJ_3, "ReleaseInterfaces", ves_icall_System_ComObject_ReleaseInterfaces) +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/icall-internals.h b/unity-2019.4.24f1-mbe/mono/metadata/icall-internals.h new file mode 100644 index 000000000..64b733980 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/icall-internals.h @@ -0,0 +1,72 @@ +/** + * \file + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#ifndef __MONO_METADATA_ICALL_INTERNALS_H__ +#define __MONO_METADATA_ICALL_INTERNALS_H__ + +#include +#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. +#ifdef HOST_WIN32 +void +mono_icall_make_platform_path (gchar *path); + +const gchar * +mono_icall_get_file_path_prefix (const gchar *path); + +gpointer +mono_icall_module_get_hinstance (MonoReflectionModuleHandle module); + +MonoStringHandle +mono_icall_get_machine_name (MonoError *error); + +int +mono_icall_get_platform (void); + +MonoStringHandle +mono_icall_get_new_line (MonoError *error); + +MonoBoolean +mono_icall_is_64bit_os (void); + +MonoArray * +mono_icall_get_environment_variable_names (MonoError *error); + +void +mono_icall_set_environment_variable (MonoString *name, MonoString *value); + +MonoStringHandle +mono_icall_get_windows_folder_path (int folder, MonoError *error); + +MonoBoolean +mono_icall_broadcast_setting_change (MonoError *error); + +void +mono_icall_write_windows_debug_string (MonoString *message); + +gint32 +mono_icall_wait_for_input_idle (gpointer handle, gint32 milliseconds); +#endif /* HOST_WIN32 */ + +// On platforms not using classic WIN API support the implementation of bellow methods are hosted in separate source file +// icall-windows-*.c. On platforms using classic WIN API the implementation is still keept in icall.c and still declared +// static and in some places even inlined. +#if !G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +MonoArray * +mono_icall_get_logical_drives (void); + +guint32 +mono_icall_drive_info_get_drive_type (MonoString *root_path_name); +#endif /* !G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ + +#endif /* __MONO_METADATA_ICALL_INTERNALS_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/icall-windows-internals.h b/unity-2019.4.24f1-mbe/mono/metadata/icall-windows-internals.h new file mode 100644 index 000000000..b18762154 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/icall-windows-internals.h @@ -0,0 +1,19 @@ +/** + * \file + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#ifndef __MONO_METADATA_ICALL_WINDOWS_INTERNALS_H__ +#define __MONO_METADATA_ICALL_WINDOWS_INTERNALS_H__ + +#include +#include + +#ifdef HOST_WIN32 +#include "mono/metadata/icall-internals.h" +#include "mono/metadata/object.h" +#include "mono/metadata/object-internals.h" +#include "mono/metadata/class.h" +#include "mono/metadata/class-internals.h" +#endif /* HOST_WIN32 */ +#endif /* __MONO_METADATA_ICALL_WINDOWS_INTERNALS_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/icall-windows-uwp.c b/unity-2019.4.24f1-mbe/mono/metadata/icall-windows-uwp.c new file mode 100644 index 000000000..918510b1c --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/icall-windows-uwp.c @@ -0,0 +1,92 @@ +/** + * \file + * UWP icall support for Mono. + * + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. +*/ +#include +#include +#include "mono/utils/mono-compiler.h" + +#if G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) +#include +#include "mono/metadata/icall-windows-internals.h" + +MonoStringHandle +mono_icall_get_machine_name (MonoError *error) +{ + g_unsupported_api ("GetComputerName"); + return mono_string_new_handle (mono_domain_get (), "mono", error); +} + +MonoStringHandle +mono_icall_get_windows_folder_path (int folder, MonoError *error) +{ + error_init (error); + g_unsupported_api ("SHGetFolderPath"); + return mono_string_new_handle (mono_domain_get (), "", error); +} + +MonoArray * +mono_icall_get_logical_drives (void) +{ + MonoError mono_error; + error_init (&mono_error); + + g_unsupported_api ("GetLogicalDriveStrings"); + + mono_error_set_not_supported (&mono_error, G_UNSUPPORTED_API, "GetLogicalDriveStrings"); + mono_error_set_pending_exception (&mono_error); + + SetLastError (ERROR_NOT_SUPPORTED); + + return NULL; +} + +MonoBoolean +mono_icall_broadcast_setting_change (MonoError *error) +{ + error_init (error); + + g_unsupported_api ("SendMessageTimeout"); + + mono_error_set_not_supported (error, G_UNSUPPORTED_API, "SendMessageTimeout"); + + SetLastError (ERROR_NOT_SUPPORTED); + + return is_ok (error); +} + +guint32 +mono_icall_drive_info_get_drive_type (MonoString *root_path_name) +{ + MonoError mono_error; + error_init (&mono_error); + + g_unsupported_api ("GetDriveType"); + + mono_error_set_not_supported (&mono_error, G_UNSUPPORTED_API, "GetDriveType"); + mono_error_set_pending_exception (&mono_error); + + return DRIVE_UNKNOWN; +} + +gint32 +mono_icall_wait_for_input_idle (gpointer handle, gint32 milliseconds) +{ + MonoError mono_error; + error_init (&mono_error); + + g_unsupported_api ("WaitForInputIdle"); + + mono_error_set_not_supported (&mono_error, G_UNSUPPORTED_API, "WaitForInputIdle"); + mono_error_set_pending_exception (&mono_error); + + return WAIT_TIMEOUT; +} + +#else /* G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) */ + +MONO_EMPTY_SOURCE_FILE (icall_windows_uwp); +#endif /* G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/icall-windows.c b/unity-2019.4.24f1-mbe/mono/metadata/icall-windows.c new file mode 100644 index 000000000..cb5215b01 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/icall-windows.c @@ -0,0 +1,220 @@ +/** + * \file + * Windows icall support. + * + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#include +#include + +#if defined(HOST_WIN32) +#include +#include +#include "mono/metadata/icall-windows-internals.h" + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +#include +#endif + +void +mono_icall_make_platform_path (gchar *path) +{ + for (size_t i = strlen (path); i > 0; i--) + if (path [i-1] == '\\') + path [i-1] = '/'; +} + +const gchar * +mono_icall_get_file_path_prefix (const gchar *path) +{ + if (*path == '/' && *(path + 1) == '/') { + return "file:"; + } else { + return "file:///"; + } +} + +gpointer +mono_icall_module_get_hinstance (MonoReflectionModuleHandle module) +{ + MonoImage *image = MONO_HANDLE_GETVAL (module, image); + if (image && image->is_module_handle) + return image->raw_data; + + return (gpointer) (-1); +} + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +MonoStringHandle +mono_icall_get_machine_name (MonoError *error) +{ + gunichar2 *buf; + guint32 len; + MonoStringHandle result; + + len = MAX_COMPUTERNAME_LENGTH + 1; + buf = g_new (gunichar2, len); + + result = NULL; + if (GetComputerName (buf, (PDWORD) &len)) { + result = mono_string_new_utf16_handle (mono_domain_get (), buf, len, error); + } else + result = MONO_HANDLE_NEW (MonoString, NULL); + + g_free (buf); + return result; +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ + +int +mono_icall_get_platform (void) +{ + /* Win32NT */ + return 2; +} + +MonoStringHandle +mono_icall_get_new_line (MonoError *error) +{ + error_init (error); + return mono_string_new_handle (mono_domain_get (), "\r\n", error); +} + +MonoBoolean +mono_icall_is_64bit_os (void) +{ +#if SIZEOF_VOID_P == 8 + return TRUE; +#else + gboolean isWow64Process = FALSE; + if (IsWow64Process (GetCurrentProcess (), &isWow64Process)) { + return (MonoBoolean)isWow64Process; + } + return FALSE; +#endif +} + +MonoArray * +mono_icall_get_environment_variable_names (MonoError *error) +{ + MonoArray *names; + MonoDomain *domain; + MonoString *str; + WCHAR* env_strings; + WCHAR* env_string; + WCHAR* equal_str; + int n = 0; + + error_init (error); + env_strings = GetEnvironmentStrings(); + + if (env_strings) { + env_string = env_strings; + while (*env_string != '\0') { + /* weird case that MS seems to skip */ + if (*env_string != '=') + n++; + while (*env_string != '\0') + env_string++; + env_string++; + } + } + + domain = mono_domain_get (); + names = mono_array_new_checked (domain, mono_defaults.string_class, n, error); + return_val_if_nok (error, NULL); + + if (env_strings) { + n = 0; + env_string = env_strings; + while (*env_string != '\0') { + /* weird case that MS seems to skip */ + if (*env_string != '=') { + equal_str = wcschr(env_string, '='); + g_assert(equal_str); + str = mono_string_new_utf16_checked (domain, env_string, (gint32)(equal_str - env_string), error); + goto_if_nok (error, cleanup); + + mono_array_setref (names, n, str); + n++; + } + while (*env_string != '\0') + env_string++; + env_string++; + } + + } + +cleanup: + if (env_strings) + FreeEnvironmentStrings (env_strings); + if (!is_ok (error)) + return NULL; + return names; +} + +void +mono_icall_set_environment_variable (MonoString *name, MonoString *value) +{ + gunichar2 *utf16_name, *utf16_value; + + utf16_name = mono_string_to_utf16 (name); + if ((value == NULL) || (mono_string_length (value) == 0) || (mono_string_chars (value)[0] == 0)) { + SetEnvironmentVariable (utf16_name, NULL); + g_free (utf16_name); + return; + } + + utf16_value = mono_string_to_utf16 (value); + + SetEnvironmentVariable (utf16_name, utf16_value); + + g_free (utf16_name); + g_free (utf16_value); +} + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +MonoStringHandle +mono_icall_get_windows_folder_path (int folder, MonoError *error) +{ + error_init (error); + #ifndef CSIDL_FLAG_CREATE + #define CSIDL_FLAG_CREATE 0x8000 + #endif + + WCHAR path [MAX_PATH]; + /* Create directory if no existing */ + if (SUCCEEDED (SHGetFolderPathW (NULL, folder | CSIDL_FLAG_CREATE, NULL, 0, path))) { + int len = 0; + while (path [len]) + ++ len; + return mono_string_new_utf16_handle (mono_domain_get (), path, len, error); + } + return mono_string_new_handle (mono_domain_get (), "", error); +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +MonoBoolean +mono_icall_broadcast_setting_change (MonoError *error) +{ + error_init (error); + SendMessageTimeout (HWND_BROADCAST, WM_SETTINGCHANGE, (WPARAM)NULL, (LPARAM)L"Environment", SMTO_ABORTIFHUNG, 2000, 0); + return TRUE; +} + +gint32 +mono_icall_wait_for_input_idle (gpointer handle, gint32 milliseconds) +{ + return WaitForInputIdle (handle, milliseconds); +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ + +void +mono_icall_write_windows_debug_string (MonoString *message) +{ + OutputDebugString (mono_string_chars (message)); +} + +#endif /* HOST_WIN32 */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/icall.c b/unity-2019.4.24f1-mbe/mono/metadata/icall.c new file mode 100644 index 000000000..ffec0503c --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/icall.c @@ -0,0 +1,8762 @@ +/** + * \file + * + * Authors: + * Dietmar Maurer (dietmar@ximian.com) + * Paolo Molaro (lupus@ximian.com) + * Patrik Torstensson (patrik.torstensson@labs2.com) + * Marek Safar (marek.safar@gmail.com) + * Aleksey Kliger (aleksey@xamarin.com) + * + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Copyright 2011-2015 Xamarin Inc (http://www.xamarin.com). + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#include +#include +#include +#include +#ifdef HAVE_ALLOCA_H +#include +#endif +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#if defined (HAVE_WCHAR_H) +#include +#endif +#include "mono/metadata/icall-internals.h" +#include "mono/utils/mono-membar.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "decimal-ms.h" +#include "number-ms.h" + +#if !defined(HOST_WIN32) && defined(HAVE_SYS_UTSNAME_H) +#include +#endif + +extern MonoStringHandle ves_icall_System_Environment_GetOSVersionString (MonoError *error); + +ICALL_EXPORT MonoReflectionAssemblyHandle ves_icall_System_Reflection_Assembly_GetCallingAssembly (MonoError *error); + +/* Lazy class loading functions */ +static GENERATE_GET_CLASS_WITH_CACHE (system_version, "System", "Version") +static GENERATE_GET_CLASS_WITH_CACHE (assembly_name, "System.Reflection", "AssemblyName") +static GENERATE_GET_CLASS_WITH_CACHE (constructor_info, "System.Reflection", "ConstructorInfo") +static GENERATE_GET_CLASS_WITH_CACHE (property_info, "System.Reflection", "PropertyInfo") +static GENERATE_GET_CLASS_WITH_CACHE (event_info, "System.Reflection", "EventInfo") +static GENERATE_GET_CLASS_WITH_CACHE (module, "System.Reflection", "Module") + +static void +array_set_value_impl (MonoArrayHandle arr, MonoObjectHandle value, guint32 pos, MonoError *error); + +static MonoArrayHandle +type_array_from_modifiers (MonoImage *image, MonoType *type, int optional, MonoError *error); + +static inline MonoBoolean +is_generic_parameter (MonoType *type) +{ + return !type->byref && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR); +} + +static void +mono_class_init_checked (MonoClass *klass, MonoError *error) +{ + error_init (error); + + if (!mono_class_init (klass)) + mono_error_set_for_class_failure (error, klass); +} + +#ifndef HOST_WIN32 +static inline void +mono_icall_make_platform_path (gchar *path) +{ + return; +} + +static inline const gchar * +mono_icall_get_file_path_prefix (const gchar *path) +{ + return "file://"; +} +#endif /* HOST_WIN32 */ + +ICALL_EXPORT MonoObject * +ves_icall_System_Array_GetValueImpl (MonoArray *arr, guint32 pos) +{ + MonoError error; + MonoClass *ac; + gint32 esize; + gpointer *ea; + MonoObject *result = NULL; + + ac = (MonoClass *)arr->obj.vtable->klass; + + esize = mono_array_element_size (ac); + ea = (gpointer*)((char*)arr->vector + (pos * esize)); + + if (ac->element_class->valuetype) { + result = mono_value_box_checked (arr->obj.vtable->domain, ac->element_class, ea, &error); + mono_error_set_pending_exception (&error); + } else + result = (MonoObject *)*ea; + return result; +} + +ICALL_EXPORT MonoObject * +ves_icall_System_Array_GetValue (MonoArray *arr, MonoArray *idxs) +{ + MonoClass *ac, *ic; + MonoArray *io; + gint32 i, pos, *ind; + + MONO_CHECK_ARG_NULL (idxs, NULL); + + io = idxs; + ic = (MonoClass *)io->obj.vtable->klass; + + ac = (MonoClass *)arr->obj.vtable->klass; + + g_assert (ic->rank == 1); + if (io->bounds != NULL || io->max_length != ac->rank) { + mono_set_pending_exception (mono_get_exception_argument (NULL, NULL)); + return NULL; + } + + ind = (gint32 *)io->vector; + + if (arr->bounds == NULL) { + if (*ind < 0 || *ind >= arr->max_length) { + mono_set_pending_exception (mono_get_exception_index_out_of_range ()); + return NULL; + } + + return ves_icall_System_Array_GetValueImpl (arr, *ind); + } + + for (i = 0; i < ac->rank; i++) { + if ((ind [i] < arr->bounds [i].lower_bound) || + (ind [i] >= (mono_array_lower_bound_t)arr->bounds [i].length + arr->bounds [i].lower_bound)) { + mono_set_pending_exception (mono_get_exception_index_out_of_range ()); + return NULL; + } + } + + pos = ind [0] - arr->bounds [0].lower_bound; + for (i = 1; i < ac->rank; i++) + pos = pos * arr->bounds [i].length + ind [i] - + arr->bounds [i].lower_bound; + + return ves_icall_System_Array_GetValueImpl (arr, pos); +} + +ICALL_EXPORT void +ves_icall_System_Array_SetValueImpl (MonoArrayHandle arr, MonoObjectHandle value, guint32 pos, MonoError *error) +{ + error_init (error); + array_set_value_impl (arr, value, pos, error); +} + +static void +array_set_value_impl (MonoArrayHandle arr, MonoObjectHandle value, guint32 pos, MonoError *error) +{ + MonoClass *ac, *vc, *ec; + gint32 esize, vsize; + gpointer *ea, *va; + int et, vt; + + guint64 u64 = 0; + gint64 i64 = 0; + gdouble r64 = 0; + + uint32_t arr_gchandle = 0; + uint32_t value_gchandle = 0; + + error_init (error); + + if (!MONO_HANDLE_IS_NULL (value)) + vc = mono_handle_class (value); + else + vc = NULL; + + ac = mono_handle_class (arr); + ec = ac->element_class; + + esize = mono_array_element_size (ac); + ea = mono_array_handle_pin_with_size (arr, esize, pos, &arr_gchandle); + + if (mono_class_is_nullable (ec)) { + mono_nullable_init_from_handle ((guint8*)ea, value, ec); + goto leave; + } + + if (MONO_HANDLE_IS_NULL (value)) { + mono_gc_bzero_atomic (ea, esize); + goto leave; + } + +#define NO_WIDENING_CONVERSION G_STMT_START{ \ + mono_error_set_argument (error, "value", "not a widening conversion"); \ + goto leave; \ + }G_STMT_END + +#define CHECK_WIDENING_CONVERSION(extra) G_STMT_START{ \ + if (esize < vsize + (extra)) { \ + mono_error_set_argument (error, "value", "not a widening conversion"); \ + goto leave; \ + } \ + }G_STMT_END + +#define INVALID_CAST G_STMT_START{ \ + mono_get_runtime_callbacks ()->set_cast_details (vc, ec); \ + mono_error_set_invalid_cast (error); \ + goto leave; \ + }G_STMT_END + + /* Check element (destination) type. */ + switch (ec->byval_arg.type) { + case MONO_TYPE_STRING: + switch (vc->byval_arg.type) { + case MONO_TYPE_STRING: + break; + default: + INVALID_CAST; + } + break; + case MONO_TYPE_BOOLEAN: + switch (vc->byval_arg.type) { + case MONO_TYPE_BOOLEAN: + break; + case MONO_TYPE_CHAR: + case MONO_TYPE_U1: + case MONO_TYPE_U2: + case MONO_TYPE_U4: + case MONO_TYPE_U8: + case MONO_TYPE_I1: + case MONO_TYPE_I2: + case MONO_TYPE_I4: + case MONO_TYPE_I8: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + NO_WIDENING_CONVERSION; + default: + INVALID_CAST; + } + break; + default: + break; + } + + MonoObjectHandle inst = mono_object_handle_isinst (value, ec, error); + goto_if_nok (error, leave); + gboolean castOk = !MONO_HANDLE_IS_NULL (inst); + + if (!ec->valuetype) { + if (!castOk) + INVALID_CAST; + MONO_HANDLE_ARRAY_SETREF (arr, pos, value); + goto leave; + } + + if (castOk) { + va = mono_object_handle_pin_unbox (value, &value_gchandle); + if (ec->has_references) + mono_value_copy (ea, va, ec); + else + mono_gc_memmove_atomic (ea, va, esize); + mono_gchandle_free (value_gchandle); + value_gchandle = 0; + goto leave; + } + + if (!vc->valuetype) + INVALID_CAST; + + va = mono_object_handle_pin_unbox (value, &value_gchandle); + + vsize = mono_class_instance_size (vc) - sizeof (MonoObject); + + et = ec->byval_arg.type; + if (et == MONO_TYPE_VALUETYPE && ec->byval_arg.data.klass->enumtype) + et = mono_class_enum_basetype (ec->byval_arg.data.klass)->type; + + vt = vc->byval_arg.type; + if (vt == MONO_TYPE_VALUETYPE && vc->byval_arg.data.klass->enumtype) + vt = mono_class_enum_basetype (vc->byval_arg.data.klass)->type; + +#define ASSIGN_UNSIGNED(etype) G_STMT_START{\ + switch (vt) { \ + case MONO_TYPE_U1: \ + case MONO_TYPE_U2: \ + case MONO_TYPE_U4: \ + case MONO_TYPE_U8: \ + case MONO_TYPE_CHAR: \ + CHECK_WIDENING_CONVERSION(0); \ + *(etype *) ea = (etype) u64; \ + goto leave; \ + /* You can't assign a signed value to an unsigned array. */ \ + case MONO_TYPE_I1: \ + case MONO_TYPE_I2: \ + case MONO_TYPE_I4: \ + case MONO_TYPE_I8: \ + /* You can't assign a floating point number to an integer array. */ \ + case MONO_TYPE_R4: \ + case MONO_TYPE_R8: \ + NO_WIDENING_CONVERSION; \ + } \ +}G_STMT_END + +#define ASSIGN_SIGNED(etype) G_STMT_START{\ + switch (vt) { \ + case MONO_TYPE_I1: \ + case MONO_TYPE_I2: \ + case MONO_TYPE_I4: \ + case MONO_TYPE_I8: \ + CHECK_WIDENING_CONVERSION(0); \ + *(etype *) ea = (etype) i64; \ + goto leave; \ + /* You can assign an unsigned value to a signed array if the array's */ \ + /* element size is larger than the value size. */ \ + case MONO_TYPE_U1: \ + case MONO_TYPE_U2: \ + case MONO_TYPE_U4: \ + case MONO_TYPE_U8: \ + case MONO_TYPE_CHAR: \ + CHECK_WIDENING_CONVERSION(1); \ + *(etype *) ea = (etype) u64; \ + goto leave; \ + /* You can't assign a floating point number to an integer array. */ \ + case MONO_TYPE_R4: \ + case MONO_TYPE_R8: \ + NO_WIDENING_CONVERSION; \ + } \ +}G_STMT_END + +#define ASSIGN_REAL(etype) G_STMT_START{\ + switch (vt) { \ + case MONO_TYPE_R4: \ + case MONO_TYPE_R8: \ + CHECK_WIDENING_CONVERSION(0); \ + *(etype *) ea = (etype) r64; \ + goto leave; \ + /* All integer values fit into a floating point array, so we don't */ \ + /* need to CHECK_WIDENING_CONVERSION here. */ \ + case MONO_TYPE_I1: \ + case MONO_TYPE_I2: \ + case MONO_TYPE_I4: \ + case MONO_TYPE_I8: \ + *(etype *) ea = (etype) i64; \ + goto leave; \ + case MONO_TYPE_U1: \ + case MONO_TYPE_U2: \ + case MONO_TYPE_U4: \ + case MONO_TYPE_U8: \ + case MONO_TYPE_CHAR: \ + *(etype *) ea = (etype) u64; \ + goto leave; \ + } \ +}G_STMT_END + + switch (vt) { + case MONO_TYPE_U1: + u64 = *(guint8 *) va; + break; + case MONO_TYPE_U2: + u64 = *(guint16 *) va; + break; + case MONO_TYPE_U4: + u64 = *(guint32 *) va; + break; + case MONO_TYPE_U8: + u64 = *(guint64 *) va; + break; + case MONO_TYPE_I1: + i64 = *(gint8 *) va; + break; + case MONO_TYPE_I2: + i64 = *(gint16 *) va; + break; + case MONO_TYPE_I4: + i64 = *(gint32 *) va; + break; + case MONO_TYPE_I8: + i64 = *(gint64 *) va; + break; + case MONO_TYPE_R4: + r64 = *(gfloat *) va; + break; + case MONO_TYPE_R8: + r64 = *(gdouble *) va; + break; + case MONO_TYPE_CHAR: + u64 = *(guint16 *) va; + break; + case MONO_TYPE_BOOLEAN: + /* Boolean is only compatible with itself. */ + switch (et) { + case MONO_TYPE_CHAR: + case MONO_TYPE_U1: + case MONO_TYPE_U2: + case MONO_TYPE_U4: + case MONO_TYPE_U8: + case MONO_TYPE_I1: + case MONO_TYPE_I2: + case MONO_TYPE_I4: + case MONO_TYPE_I8: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + NO_WIDENING_CONVERSION; + default: + INVALID_CAST; + } + break; + } + + /* If we can't do a direct copy, let's try a widening conversion. */ + switch (et) { + case MONO_TYPE_CHAR: + ASSIGN_UNSIGNED (guint16); + case MONO_TYPE_U1: + ASSIGN_UNSIGNED (guint8); + case MONO_TYPE_U2: + ASSIGN_UNSIGNED (guint16); + case MONO_TYPE_U4: + ASSIGN_UNSIGNED (guint32); + case MONO_TYPE_U8: + ASSIGN_UNSIGNED (guint64); + case MONO_TYPE_I1: + ASSIGN_SIGNED (gint8); + case MONO_TYPE_I2: + ASSIGN_SIGNED (gint16); + case MONO_TYPE_I4: + ASSIGN_SIGNED (gint32); + case MONO_TYPE_I8: + ASSIGN_SIGNED (gint64); + case MONO_TYPE_R4: + ASSIGN_REAL (gfloat); + case MONO_TYPE_R8: + ASSIGN_REAL (gdouble); + } + + INVALID_CAST; + /* Not reached, INVALID_CAST does fall thru. */ + g_assert_not_reached (); + +#undef INVALID_CAST +#undef NO_WIDENING_CONVERSION +#undef CHECK_WIDENING_CONVERSION +#undef ASSIGN_UNSIGNED +#undef ASSIGN_SIGNED +#undef ASSIGN_REAL +leave: + if (arr_gchandle) + mono_gchandle_free (arr_gchandle); + if (value_gchandle) + mono_gchandle_free (value_gchandle); + return; +} + +ICALL_EXPORT void +ves_icall_System_Array_SetValue (MonoArrayHandle arr, MonoObjectHandle value, + MonoArrayHandle idxs, MonoError *error) +{ + MonoArrayBounds dim; + MonoClass *ac, *ic; + gint32 idx; + gint32 i, pos; + + error_init (error); + + if (MONO_HANDLE_IS_NULL (idxs)) { + mono_error_set_argument_null (error, "idxs", ""); + return; + } + + ic = mono_handle_class (idxs); + ac = mono_handle_class (arr); + + g_assert (ic->rank == 1); + if (mono_handle_array_has_bounds (idxs) || MONO_HANDLE_GETVAL (idxs, max_length) != ac->rank) { + mono_error_set_argument (error, "idxs", ""); + return; + } + + if (!mono_handle_array_has_bounds (arr)) { + MONO_HANDLE_ARRAY_GETVAL (idx, idxs, gint32, 0); + if (idx < 0 || idx >= MONO_HANDLE_GETVAL (arr, max_length)) { + mono_error_set_exception_instance (error, mono_get_exception_index_out_of_range ()); + return; + } + + array_set_value_impl (arr, value, idx, error); + return; + } + + for (i = 0; i < ac->rank; i++) { + mono_handle_array_get_bounds_dim (arr, i, &dim); + MONO_HANDLE_ARRAY_GETVAL (idx, idxs, gint32, i); + if ((idx < dim.lower_bound) || + (idx >= (mono_array_lower_bound_t)dim.length + dim.lower_bound)) { + mono_error_set_exception_instance (error, mono_get_exception_index_out_of_range ()); + return; + } + } + + + MONO_HANDLE_ARRAY_GETVAL (idx, idxs, gint32, 0); + mono_handle_array_get_bounds_dim (arr, 0, &dim); + pos = idx - dim.lower_bound; + for (i = 1; i < ac->rank; i++) { + mono_handle_array_get_bounds_dim (arr, i, &dim); + MONO_HANDLE_ARRAY_GETVAL (idx, idxs, gint32, i); + pos = pos * dim.length + idx - dim.lower_bound; + } + + array_set_value_impl (arr, value, pos, error); +} + +ICALL_EXPORT MonoArray * +ves_icall_System_Array_CreateInstanceImpl (MonoReflectionType *type, MonoArray *lengths, MonoArray *bounds) +{ + MonoError error; + MonoClass *aklass, *klass; + MonoArray *array; + uintptr_t *sizes, i; + gboolean bounded = FALSE; + + MONO_CHECK_ARG_NULL (type, NULL); + MONO_CHECK_ARG_NULL (lengths, NULL); + + MONO_CHECK_ARG (lengths, mono_array_length (lengths) > 0, NULL); + if (bounds) + MONO_CHECK_ARG (bounds, mono_array_length (lengths) == mono_array_length (bounds), NULL); + + for (i = 0; i < mono_array_length (lengths); i++) { + if (mono_array_get (lengths, gint32, i) < 0) { + mono_set_pending_exception (mono_get_exception_argument_out_of_range (NULL)); + return NULL; + } + } + + klass = mono_class_from_mono_type (type->type); + mono_class_init_checked (klass, &error); + if (mono_error_set_pending_exception (&error)) + return NULL; + + if (klass->element_class->byval_arg.type == MONO_TYPE_VOID) { + mono_set_pending_exception (mono_get_exception_not_supported ("Arrays of System.Void are not supported.")); + return NULL; + } + + if (bounds && (mono_array_length (bounds) == 1) && (mono_array_get (bounds, gint32, 0) != 0)) + /* vectors are not the same as one dimensional arrays with no-zero bounds */ + bounded = TRUE; + else + bounded = FALSE; + + aklass = mono_bounded_array_class_get (klass, mono_array_length (lengths), bounded); + + sizes = (uintptr_t *)alloca (aklass->rank * sizeof(intptr_t) * 2); + for (i = 0; i < aklass->rank; ++i) { + sizes [i] = mono_array_get (lengths, guint32, i); + if (bounds) + sizes [i + aklass->rank] = mono_array_get (bounds, gint32, i); + else + sizes [i + aklass->rank] = 0; + } + + array = mono_array_new_full_checked (mono_object_domain (type), aklass, sizes, (intptr_t*)sizes + aklass->rank, &error); + mono_error_set_pending_exception (&error); + + return array; +} + +ICALL_EXPORT MonoArray * +ves_icall_System_Array_CreateInstanceImpl64 (MonoReflectionType *type, MonoArray *lengths, MonoArray *bounds) +{ + MonoError error; + MonoClass *aklass, *klass; + MonoArray *array; + uintptr_t *sizes, i; + gboolean bounded = FALSE; + + MONO_CHECK_ARG_NULL (type, NULL); + MONO_CHECK_ARG_NULL (lengths, NULL); + + MONO_CHECK_ARG (lengths, mono_array_length (lengths) > 0, NULL); + if (bounds) + MONO_CHECK_ARG (bounds, mono_array_length (lengths) == mono_array_length (bounds), NULL); + + for (i = 0; i < mono_array_length (lengths); i++) { + if ((mono_array_get (lengths, gint64, i) < 0) || + (mono_array_get (lengths, gint64, i) > MONO_ARRAY_MAX_INDEX)) { + mono_set_pending_exception (mono_get_exception_argument_out_of_range (NULL)); + return NULL; + } + } + + klass = mono_class_from_mono_type (type->type); + mono_class_init_checked (klass, &error); + if (mono_error_set_pending_exception (&error)) + return NULL; + + if (bounds && (mono_array_length (bounds) == 1) && (mono_array_get (bounds, gint64, 0) != 0)) + /* vectors are not the same as one dimensional arrays with no-zero bounds */ + bounded = TRUE; + else + bounded = FALSE; + + aklass = mono_bounded_array_class_get (klass, mono_array_length (lengths), bounded); + + sizes = (uintptr_t *)alloca (aklass->rank * sizeof(intptr_t) * 2); + for (i = 0; i < aklass->rank; ++i) { + sizes [i] = mono_array_get (lengths, guint64, i); + if (bounds) + sizes [i + aklass->rank] = (mono_array_size_t) mono_array_get (bounds, guint64, i); + else + sizes [i + aklass->rank] = 0; + } + + array = mono_array_new_full_checked (mono_object_domain (type), aklass, sizes, (intptr_t*)sizes + aklass->rank, &error); + mono_error_set_pending_exception (&error); + + return array; +} + +ICALL_EXPORT gint32 +ves_icall_System_Array_GetRank (MonoObject *arr) +{ + return arr->vtable->klass->rank; +} + +ICALL_EXPORT gint32 +ves_icall_System_Array_GetLength (MonoArray *arr, gint32 dimension) +{ + gint32 rank = arr->obj.vtable->klass->rank; + uintptr_t length; + + if ((dimension < 0) || (dimension >= rank)) { + mono_set_pending_exception (mono_get_exception_index_out_of_range ()); + return 0; + } + + if (arr->bounds == NULL) + length = arr->max_length; + else + length = arr->bounds [dimension].length; + +#ifdef MONO_BIG_ARRAYS + if (length > G_MAXINT32) { + mono_set_pending_exception (mono_get_exception_overflow ()); + return 0; + } +#endif + return length; +} + +ICALL_EXPORT gint64 +ves_icall_System_Array_GetLongLength (MonoArray *arr, gint32 dimension) +{ + gint32 rank = arr->obj.vtable->klass->rank; + + if ((dimension < 0) || (dimension >= rank)) { + mono_set_pending_exception (mono_get_exception_index_out_of_range ()); + return 0; + } + + if (arr->bounds == NULL) + return arr->max_length; + + return arr->bounds [dimension].length; +} + +ICALL_EXPORT gint32 +ves_icall_System_Array_GetLowerBound (MonoArray *arr, gint32 dimension) +{ + gint32 rank = arr->obj.vtable->klass->rank; + + if ((dimension < 0) || (dimension >= rank)) { + mono_set_pending_exception (mono_get_exception_index_out_of_range ()); + return 0; + } + + if (arr->bounds == NULL) + return 0; + + return arr->bounds [dimension].lower_bound; +} + +ICALL_EXPORT void +ves_icall_System_Array_ClearInternal (MonoArray *arr, int idx, int length) +{ + int sz = mono_array_element_size (mono_object_class (arr)); + mono_gc_bzero_atomic (mono_array_addr_with_size_fast (arr, sz, idx), length * sz); +} + + +ICALL_EXPORT gboolean +ves_icall_System_Array_FastCopy (MonoArray *source, int source_idx, MonoArray* dest, int dest_idx, int length) +{ + int element_size; + void * dest_addr; + void * source_addr; + MonoVTable *src_vtable; + MonoVTable *dest_vtable; + MonoClass *src_class; + MonoClass *dest_class; + + src_vtable = source->obj.vtable; + dest_vtable = dest->obj.vtable; + + if (src_vtable->rank != dest_vtable->rank) + return FALSE; + + if (source->bounds || dest->bounds) + return FALSE; + + /* there's no integer overflow since mono_array_length returns an unsigned integer */ + if ((dest_idx + length > mono_array_length_fast (dest)) || + (source_idx + length > mono_array_length_fast (source))) + return FALSE; + + src_class = src_vtable->klass->element_class; + dest_class = dest_vtable->klass->element_class; + + /* + * Handle common cases. + */ + + /* Case1: object[] -> valuetype[] (ArrayList::ToArray) + We fallback to managed here since we need to typecheck each boxed valuetype before storing them in the dest array. + */ + if (src_class == mono_defaults.object_class && dest_class->valuetype) + return FALSE; + + /* Check if we're copying a char[] <==> (u)short[] */ + if (src_class != dest_class) { + if (dest_class->valuetype || dest_class->enumtype || src_class->valuetype || src_class->enumtype) + return FALSE; + + /* It's only safe to copy between arrays if we can ensure the source will always have a subtype of the destination. We bail otherwise. */ + if (!mono_class_is_subclass_of (src_class, dest_class, FALSE)) + return FALSE; + } + + if (dest_class->valuetype) { + element_size = mono_array_element_size (source->obj.vtable->klass); + source_addr = mono_array_addr_with_size_fast (source, element_size, source_idx); + if (dest_class->has_references) { + mono_value_copy_array (dest, dest_idx, source_addr, length); + } else { + dest_addr = mono_array_addr_with_size_fast (dest, element_size, dest_idx); + mono_gc_memmove_atomic (dest_addr, source_addr, element_size * length); + } + } else { + mono_array_memcpy_refs_fast (dest, dest_idx, source, source_idx, length); + } + + return TRUE; +} + +ICALL_EXPORT void +ves_icall_System_Array_GetGenericValueImpl (MonoArray *arr, guint32 pos, gpointer value) +{ + MonoClass *ac; + gint32 esize; + gpointer *ea; + + ac = (MonoClass *)arr->obj.vtable->klass; + + esize = mono_array_element_size (ac); + ea = (gpointer*)((char*)arr->vector + (pos * esize)); + + mono_gc_memmove_atomic (value, ea, esize); +} + +ICALL_EXPORT void +ves_icall_System_Array_SetGenericValueImpl (MonoArray *arr, guint32 pos, gpointer value) +{ + MonoClass *ac, *ec; + gint32 esize; + gpointer *ea; + + ac = (MonoClass *)arr->obj.vtable->klass; + ec = ac->element_class; + + esize = mono_array_element_size (ac); + ea = (gpointer*)((char*)arr->vector + (pos * esize)); + + if (MONO_TYPE_IS_REFERENCE (&ec->byval_arg)) { + g_assert (esize == sizeof (gpointer)); + mono_gc_wbarrier_generic_store (ea, *(MonoObject **)value); + } else { + g_assert (ec->inited); + g_assert (esize == mono_class_value_size (ec, NULL)); + if (ec->has_references) + mono_gc_wbarrier_value_copy (ea, value, 1, ec); + else + mono_gc_memmove_atomic (ea, value, esize); + } +} + +ICALL_EXPORT void +ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_InitializeArray (MonoArrayHandle array, MonoClassField *field_handle, MonoError *error) +{ + error_init (error); + + MonoClass *klass = mono_handle_class (array); + guint32 size = mono_array_element_size (klass); + MonoType *type = mono_type_get_underlying_type (&klass->element_class->byval_arg); + int align; + const char *field_data; + + if (MONO_TYPE_IS_REFERENCE (type) || type->type == MONO_TYPE_VALUETYPE) { + mono_error_set_argument (error, "array", "Cannot initialize array of non-primitive type"); + return; + } + + + MonoType *field_type = mono_field_get_type_checked (field_handle, error); + if (!field_type) + return; + + if (!(field_type->attrs & FIELD_ATTRIBUTE_HAS_FIELD_RVA)) { + mono_error_set_argument (error, "field_handle", "Field '%s' doesn't have an RVA", mono_field_get_name (field_handle)); + return; + } + + size *= MONO_HANDLE_GETVAL(array, max_length); + field_data = mono_field_get_data (field_handle); + + if (size > mono_type_size (field_handle->type, &align)) { + mono_error_set_argument (error, "field_handle", "Field not large enough to fill array"); + return; + } + +#if G_BYTE_ORDER != G_LITTLE_ENDIAN +#define SWAP(n) { \ + guint ## n *data = (guint ## n *) mono_array_addr (MONO_HANDLE_RAW(array), char, 0); \ + guint ## n *src = (guint ## n *) field_data; \ + int i, \ + nEnt = (size / sizeof(guint ## n)); \ + \ + for (i = 0; i < nEnt; i++) { \ + data[i] = read ## n (&src[i]); \ + } \ +} + + /* printf ("Initialize array with elements of %s type\n", klass->element_class->name); */ + + switch (type->type) { + case MONO_TYPE_CHAR: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + SWAP (16); + break; + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_R4: + SWAP (32); + break; + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_R8: + SWAP (64); + break; + default: + memcpy (mono_array_addr (MONO_HANDLE_RAW(array), char, 0), field_data, size); + break; + } +#else + memcpy (mono_array_addr (MONO_HANDLE_RAW(array), char, 0), field_data, size); +#endif +} + +ICALL_EXPORT gint +ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetOffsetToStringData (void) +{ + return offsetof (MonoString, chars); +} + +ICALL_EXPORT MonoObject * +ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetObjectValue (MonoObject *obj) +{ + if ((obj == NULL) || (! (obj->vtable->klass->valuetype))) + return obj; + else { + MonoError error; + MonoObject *ret = mono_object_clone_checked (obj, &error); + mono_error_set_pending_exception (&error); + + return ret; + } +} + +ICALL_EXPORT void +ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_RunClassConstructor (MonoType *handle) +{ + MonoError error; + MonoClass *klass; + MonoVTable *vtable; + + MONO_CHECK_ARG_NULL (handle,); + + klass = mono_class_from_mono_type (handle); + MONO_CHECK_ARG (handle, klass,); + + if (mono_class_is_gtd (klass)) + return; + + vtable = mono_class_vtable_full (mono_domain_get (), klass, &error); + if (!is_ok (&error)) { + mono_error_set_pending_exception (&error); + return; + } + + /* This will call the type constructor */ + if (!mono_runtime_class_init_full (vtable, &error)) + mono_error_set_pending_exception (&error); +} + +ICALL_EXPORT void +ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_RunModuleConstructor (MonoImage *image) +{ + MonoError error; + + mono_image_check_for_module_cctor (image); + if (image->has_module_cctor) { + MonoClass *module_klass = mono_class_get_checked (image, MONO_TOKEN_TYPE_DEF | 1, &error); + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + return; + } + /*It's fine to raise the exception here*/ + MonoVTable * vtable = mono_class_vtable_full (mono_domain_get (), module_klass, &error); + if (!is_ok (&error)) { + mono_error_set_pending_exception (&error); + return; + } + if (!mono_runtime_class_init_full (vtable, &error)) + mono_error_set_pending_exception (&error); + } +} + +ICALL_EXPORT MonoBoolean +ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_SufficientExecutionStack (void) +{ +#if defined(TARGET_WIN32) || defined(HOST_WIN32) + // It does not work on win32 +#elif defined(TARGET_ANDROID) || defined(__linux__) + // No need for now +#else + guint8 *stack_addr; + guint8 *current; + size_t stack_size; + int min_size; + MonoInternalThread *thread; + + mono_thread_info_get_stack_bounds (&stack_addr, &stack_size); + /* if we have no info we are optimistic and assume there is enough room */ + if (!stack_addr) + return TRUE; + + thread = mono_thread_internal_current (); + // .net seems to check that at least 50% of stack is available + min_size = thread->stack_size / 2; + + // TODO: It's not always set + if (!min_size) + return TRUE; + + current = (guint8 *)&stack_addr; + if (current > stack_addr) { + if ((current - stack_addr) < min_size) + return FALSE; + } else { + if (current - (stack_addr - stack_size) < min_size) + return FALSE; + } +#endif + return TRUE; +} + +ICALL_EXPORT MonoObject * +ves_icall_System_Object_MemberwiseClone (MonoObject *this_obj) +{ + MonoError error; + MonoObject *ret = mono_object_clone_checked (this_obj, &error); + mono_error_set_pending_exception (&error); + + return ret; +} + +ICALL_EXPORT gint32 +ves_icall_System_ValueType_InternalGetHashCode (MonoObject *this_obj, MonoArray **fields) +{ + MonoError error; + MonoClass *klass; + MonoObject **values = NULL; + MonoObject *o; + int count = 0; + gint32 result = (int)(gsize)mono_defaults.int32_class; + MonoClassField* field; + gpointer iter; + + klass = mono_object_class (this_obj); + + if (mono_class_num_fields (klass) == 0) + return result; + + /* + * Compute the starting value of the hashcode for fields of primitive + * types, and return the remaining fields in an array to the managed side. + * This way, we can avoid costly reflection operations in managed code. + */ + iter = NULL; + while ((field = mono_class_get_fields (klass, &iter))) { + if (field->type->attrs & FIELD_ATTRIBUTE_STATIC) + continue; + if (mono_field_is_deleted (field)) + continue; + /* FIXME: Add more types */ + switch (field->type->type) { + 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); + if (s != NULL) + result ^= mono_string_hash (s); + break; + } + default: + if (!values) + values = g_newa (MonoObject*, mono_class_num_fields (klass)); + o = mono_field_get_value_object_checked (mono_object_domain (this_obj), field, this_obj, &error); + if (!is_ok (&error)) { + mono_error_set_pending_exception (&error); + return 0; + } + values [count++] = o; + } + } + + if (values) { + int i; + MonoArray *fields_arr = mono_array_new_checked (mono_domain_get (), mono_defaults.object_class, count, &error); + if (mono_error_set_pending_exception (&error)) + return 0; + mono_gc_wbarrier_generic_store (fields, (MonoObject*) fields_arr); + for (i = 0; i < count; ++i) + mono_array_setref (*fields, i, values [i]); + } else { + *fields = NULL; + } + return result; +} + +ICALL_EXPORT MonoBoolean +ves_icall_System_ValueType_Equals (MonoObject *this_obj, MonoObject *that, MonoArray **fields) +{ + MonoError error; + MonoClass *klass; + MonoObject **values = NULL; + MonoObject *o; + MonoClassField* field; + gpointer iter; + int count = 0; + + MONO_CHECK_ARG_NULL (that, FALSE); + + if (this_obj->vtable != that->vtable) + return FALSE; + + klass = mono_object_class (this_obj); + + if (klass->enumtype && mono_class_enum_basetype (klass) && mono_class_enum_basetype (klass)->type == MONO_TYPE_I4) + return (*(gint32*)((guint8*)this_obj + sizeof (MonoObject)) == *(gint32*)((guint8*)that + sizeof (MonoObject))); + + /* + * Do the comparison for fields of primitive type and return a result if + * possible. Otherwise, return the remaining fields in an array to the + * managed side. This way, we can avoid costly reflection operations in + * managed code. + */ + *fields = NULL; + iter = NULL; + while ((field = mono_class_get_fields (klass, &iter))) { + if (field->type->attrs & FIELD_ATTRIBUTE_STATIC) + continue; + if (mono_field_is_deleted (field)) + continue; + /* FIXME: Add more types */ + switch (field->type->type) { + case MONO_TYPE_U1: + case MONO_TYPE_I1: + case MONO_TYPE_BOOLEAN: + if (*((guint8*)this_obj + field->offset) != *((guint8*)that + field->offset)) + return FALSE; + break; + case MONO_TYPE_U2: + case MONO_TYPE_I2: + case MONO_TYPE_CHAR: + if (*(gint16*)((guint8*)this_obj + field->offset) != *(gint16*)((guint8*)that + field->offset)) + return FALSE; + break; + case MONO_TYPE_U4: + case MONO_TYPE_I4: + if (*(gint32*)((guint8*)this_obj + field->offset) != *(gint32*)((guint8*)that + field->offset)) + return FALSE; + break; + case MONO_TYPE_U8: + case MONO_TYPE_I8: + if (*(gint64*)((guint8*)this_obj + field->offset) != *(gint64*)((guint8*)that + field->offset)) + return FALSE; + break; + case MONO_TYPE_R4: + if (*(float*)((guint8*)this_obj + field->offset) != *(float*)((guint8*)that + field->offset)) + return FALSE; + break; + case MONO_TYPE_R8: + 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: { + MonoString *s1, *s2; + guint32 s1len, s2len; + s1 = *(MonoString**)((guint8*)this_obj + field->offset); + s2 = *(MonoString**)((guint8*)that + field->offset); + if (s1 == s2) + break; + if ((s1 == NULL) || (s2 == NULL)) + return FALSE; + s1len = mono_string_length (s1); + s2len = mono_string_length (s2); + if (s1len != s2len) + return FALSE; + + if (memcmp (mono_string_chars (s1), mono_string_chars (s2), s1len * sizeof (gunichar2)) != 0) + return FALSE; + break; + } + default: + if (!values) + values = g_newa (MonoObject*, mono_class_num_fields (klass) * 2); + o = mono_field_get_value_object_checked (mono_object_domain (this_obj), field, this_obj, &error); + if (!is_ok (&error)) { + mono_error_set_pending_exception (&error); + return FALSE; + } + values [count++] = o; + o = mono_field_get_value_object_checked (mono_object_domain (this_obj), field, that, &error); + if (!is_ok (&error)) { + mono_error_set_pending_exception (&error); + return FALSE; + } + values [count++] = o; + } + + if (klass->enumtype) + /* enums only have one non-static field */ + break; + } + + if (values) { + int i; + MonoArray *fields_arr = mono_array_new_checked (mono_domain_get (), mono_defaults.object_class, count, &error); + if (mono_error_set_pending_exception (&error)) + return FALSE; + mono_gc_wbarrier_generic_store (fields, (MonoObject*) fields_arr); + for (i = 0; i < count; ++i) + mono_array_setref_fast (*fields, i, values [i]); + return FALSE; + } else { + return TRUE; + } +} + +ICALL_EXPORT MonoReflectionTypeHandle +ves_icall_System_Object_GetType (MonoObjectHandle obj, MonoError *error) +{ + error_init (error); + MonoDomain *domain = MONO_HANDLE_DOMAIN (obj); + MonoClass *klass = mono_handle_class (obj); +#ifndef DISABLE_REMOTING + if (mono_class_is_transparent_proxy (klass)) { + MonoTransparentProxyHandle proxy_obj = MONO_HANDLE_CAST (MonoTransparentProxy, obj); + MonoRemoteClass *remote_class = MONO_HANDLE_GETVAL (proxy_obj, remote_class); + /* If it's a transparent proxy for an interface, return the + * interface type, not the unhelpful proxy_class class (which + * is just MarshalByRefObject). */ + MonoType *proxy_type = + mono_remote_class_is_interface_proxy (remote_class) ? + &remote_class->interfaces[0]->byval_arg : + &remote_class->proxy_class->byval_arg; + return mono_type_get_object_handle (domain, proxy_type, error); + } else +#endif + return mono_type_get_object_handle (domain, &klass->byval_arg, error); +} + +static gboolean +get_executing (MonoMethod *m, gint32 no, gint32 ilo, gboolean managed, gpointer data) +{ + MonoMethod **dest = (MonoMethod **)data; + + /* skip unmanaged frames */ + if (!managed) + return FALSE; + + if (!(*dest)) { + if (!strcmp (m->klass->name_space, "System.Reflection")) + return FALSE; + *dest = m; + return TRUE; + } + return FALSE; +} + +static gboolean +get_caller_no_reflection (MonoMethod *m, gint32 no, gint32 ilo, gboolean managed, gpointer data) +{ + MonoMethod **dest = (MonoMethod **)data; + + /* skip unmanaged frames */ + if (!managed) + return FALSE; + + if (m->wrapper_type != MONO_WRAPPER_NONE) + return FALSE; + + if (m == *dest) { + *dest = NULL; + return FALSE; + } + + if (m->klass->image == mono_defaults.corlib && !strcmp (m->klass->name_space, "System.Reflection")) + return FALSE; + + if (!(*dest)) { + *dest = m; + return TRUE; + } + return FALSE; +} + +static gboolean +get_caller_no_system_or_reflection (MonoMethod *m, gint32 no, gint32 ilo, gboolean managed, gpointer data) +{ + MonoMethod **dest = (MonoMethod **)data; + + /* skip unmanaged frames */ + if (!managed) + return FALSE; + + if (m->wrapper_type != MONO_WRAPPER_NONE) + return FALSE; + + if (m == *dest) { + *dest = NULL; + return FALSE; + } + + if (m->klass->image == mono_defaults.corlib && ((!strcmp (m->klass->name_space, "System.Reflection")) + || (!strcmp (m->klass->name_space, "System")))) + return FALSE; + + if (!(*dest)) { + *dest = m; + return TRUE; + } + return FALSE; +} + +static MonoReflectionTypeHandle +type_from_parsed_name (MonoTypeNameParse *info, MonoBoolean ignoreCase, MonoAssembly **caller_assembly, MonoError *error) +{ + MonoMethod *m, *dest; + + MonoType *type = NULL; + MonoAssembly *assembly = NULL; + gboolean type_resolve = FALSE; + MonoImage *rootimage = NULL; + + error_init (error); + + /* + * We must compute the calling assembly as type loading must happen under a metadata context. + * For example. The main assembly is a.exe and Type.GetType is called from dir/b.dll. Without + * the metadata context (basedir currently) set to dir/b.dll we won't be able to load a dir/c.dll. + */ + m = mono_method_get_last_managed (); + dest = m; + if (m && m->klass->image != mono_defaults.corlib) { + /* Happens with inlining */ + } else { + /* Ugly hack: type_from_parsed_name is called from + * System.Type.internal_from_name, which is called most + * directly from System.Type.GetType(string,bool,bool) but + * also indirectly from places such as + * System.Type.GetType(string,func,func) (via + * System.TypeNameParser.GetType and System.TypeSpec.Resolve) + * so we need to skip over all of those to find the true caller. + * + * It would be nice if we had stack marks. + */ + mono_stack_walk_no_il (get_caller_no_system_or_reflection, &dest); + if (!dest) + dest = m; + } + + /* + * FIXME: mono_method_get_last_managed() sometimes returns NULL, thus + * causing ves_icall_System_Reflection_Assembly_GetCallingAssembly() + * to crash. This only seems to happen in some strange remoting + * scenarios and I was unable to figure out what's happening there. + * Dec 10, 2005 - Martin. + */ + + if (dest) { + assembly = dest->klass->image->assembly; + type_resolve = TRUE; + rootimage = assembly->image; + } else { + g_warning (G_STRLOC); + } + *caller_assembly = assembly; + + if (info->assembly.name) + assembly = mono_assembly_load (&info->assembly, assembly ? assembly->basedir : NULL, NULL); + + if (assembly) { + /* When loading from the current assembly, AppDomain.TypeResolve will not be called yet */ + type = mono_reflection_get_type_checked (rootimage, assembly->image, info, ignoreCase, &type_resolve, error); + goto_if_nok (error, fail); + } + + // XXXX - aleksey - + // Say we're looking for System.Generic.Dict + // we FAIL the get type above, because S.G.Dict isn't in assembly->image. So we drop down here. + // but then we FAIL AGAIN because now we pass null as the image and the rootimage and everything + // is messed up when we go to construct the Local as the type arg... + // + // By contrast, if we started with Mine> we'd go in with assembly->image + // as the root and then even the detour into generics would still not screw us when we went to load Local. + if (!info->assembly.name && !type) { + /* try mscorlib */ + type = mono_reflection_get_type_checked (rootimage, NULL, info, ignoreCase, &type_resolve, error); + goto_if_nok (error, fail); + } + if (assembly && !type && type_resolve) { + type_resolve = FALSE; /* This will invoke TypeResolve if not done in the first 'if' */ + type = mono_reflection_get_type_checked (rootimage, assembly->image, info, ignoreCase, &type_resolve, error); + goto_if_nok (error, fail); + } + + if (!type) + goto fail; + + return mono_type_get_object_handle (mono_domain_get (), type, error); +fail: + return MONO_HANDLE_NEW (MonoReflectionType, NULL); +} + +ICALL_EXPORT MonoReflectionTypeHandle +ves_icall_System_Type_internal_from_name (MonoStringHandle name, + MonoBoolean throwOnError, + MonoBoolean ignoreCase, + MonoError *error) +{ + error_init (error); + MonoTypeNameParse info; + gboolean free_info = FALSE; + MonoAssembly *caller_assembly; + MonoReflectionTypeHandle type = MONO_HANDLE_NEW (MonoReflectionType, NULL); + + char *str = mono_string_handle_to_utf8 (name, error); + goto_if_nok (error, leave); + + free_info = TRUE; + if (!mono_reflection_parse_type_checked (str, &info, error)) + goto leave; + + /* mono_reflection_parse_type() mangles the string */ + + MONO_HANDLE_ASSIGN (type, type_from_parsed_name (&info, ignoreCase, &caller_assembly, error)); + + goto_if_nok (error, leave); + + if (MONO_HANDLE_IS_NULL (type)) { + if (throwOnError) { + char *tname = info.name_space ? g_strdup_printf ("%s.%s", info.name_space, info.name) : g_strdup (info.name); + char *aname; + if (info.assembly.name) + aname = mono_stringify_assembly_name (&info.assembly); + else if (caller_assembly) + aname = mono_stringify_assembly_name (mono_assembly_get_name (caller_assembly)); + else + aname = g_strdup (""); + mono_error_set_type_load_name (error, tname, aname, ""); + } + goto leave; + } + +leave: + if (free_info) + mono_reflection_free_type_info (&info); + g_free (str); + if (!is_ok (error)) { + if (!throwOnError) { + mono_error_cleanup (error); + error_init (error); + } + return MONO_HANDLE_CAST (MonoReflectionType, NULL_HANDLE); + } else + return type; +} + + +ICALL_EXPORT MonoReflectionTypeHandle +ves_icall_System_Type_internal_from_handle (MonoType *handle, MonoError *error) +{ + error_init (error); + MonoDomain *domain = mono_domain_get (); + + return mono_type_get_object_handle (domain, handle, error); +} + +ICALL_EXPORT MonoType* +ves_icall_Mono_RuntimeClassHandle_GetTypeFromClass (MonoClass *klass) +{ + return mono_class_get_type (klass); +} + +ICALL_EXPORT void +ves_icall_Mono_RuntimeGPtrArrayHandle_GPtrArrayFree (GPtrArray *ptr_array) +{ + g_ptr_array_free (ptr_array, TRUE); +} + +ICALL_EXPORT void +ves_icall_Mono_SafeStringMarshal_GFree (void *c_str) +{ + g_free (c_str); +} + +ICALL_EXPORT char* +ves_icall_Mono_SafeStringMarshal_StringToUtf8 (MonoString *s) +{ + MonoError error; + char *res = mono_string_to_utf8_checked (s, &error); + mono_error_set_pending_exception (&error); + return res; +} + +/* System.TypeCode */ +typedef enum { + TYPECODE_EMPTY, + TYPECODE_OBJECT, + TYPECODE_DBNULL, + TYPECODE_BOOLEAN, + TYPECODE_CHAR, + TYPECODE_SBYTE, + TYPECODE_BYTE, + TYPECODE_INT16, + TYPECODE_UINT16, + TYPECODE_INT32, + TYPECODE_UINT32, + TYPECODE_INT64, + TYPECODE_UINT64, + TYPECODE_SINGLE, + TYPECODE_DOUBLE, + TYPECODE_DECIMAL, + TYPECODE_DATETIME, + TYPECODE_STRING = 18 +} TypeCode; + +ICALL_EXPORT guint32 +ves_icall_type_GetTypeCodeInternal (MonoReflectionTypeHandle ref_type, MonoError *error) +{ + error_init (error); + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + int t = type->type; + + if (type->byref) + return TYPECODE_OBJECT; + +handle_enum: + switch (t) { + case MONO_TYPE_VOID: + return TYPECODE_OBJECT; + case MONO_TYPE_BOOLEAN: + return TYPECODE_BOOLEAN; + case MONO_TYPE_U1: + return TYPECODE_BYTE; + case MONO_TYPE_I1: + return TYPECODE_SBYTE; + case MONO_TYPE_U2: + return TYPECODE_UINT16; + case MONO_TYPE_I2: + return TYPECODE_INT16; + case MONO_TYPE_CHAR: + return TYPECODE_CHAR; + case MONO_TYPE_PTR: + case MONO_TYPE_U: + case MONO_TYPE_I: + return TYPECODE_OBJECT; + case MONO_TYPE_U4: + return TYPECODE_UINT32; + case MONO_TYPE_I4: + return TYPECODE_INT32; + case MONO_TYPE_U8: + return TYPECODE_UINT64; + case MONO_TYPE_I8: + return TYPECODE_INT64; + case MONO_TYPE_R4: + return TYPECODE_SINGLE; + case MONO_TYPE_R8: + return TYPECODE_DOUBLE; + case MONO_TYPE_VALUETYPE: { + MonoClass *klass = type->data.klass; + + if (klass->enumtype) { + t = mono_class_enum_basetype (klass)->type; + goto handle_enum; + } else if (mono_is_corlib_image (klass->image)) { + if (strcmp (klass->name_space, "System") == 0) { + if (strcmp (klass->name, "Decimal") == 0) + return TYPECODE_DECIMAL; + else if (strcmp (klass->name, "DateTime") == 0) + return TYPECODE_DATETIME; + } + } + return TYPECODE_OBJECT; + } + case MONO_TYPE_STRING: + return TYPECODE_STRING; + case MONO_TYPE_SZARRAY: + case MONO_TYPE_ARRAY: + case MONO_TYPE_OBJECT: + case MONO_TYPE_VAR: + case MONO_TYPE_MVAR: + case MONO_TYPE_TYPEDBYREF: + return TYPECODE_OBJECT; + case MONO_TYPE_CLASS: + { + MonoClass *klass = type->data.klass; + if (klass->image == mono_defaults.corlib && strcmp (klass->name_space, "System") == 0) { + if (strcmp (klass->name, "DBNull") == 0) + return TYPECODE_DBNULL; + } + } + return TYPECODE_OBJECT; + case MONO_TYPE_GENERICINST: + return TYPECODE_OBJECT; + default: + g_error ("type 0x%02x not handled in GetTypeCode()", t); + } + return 0; +} + +static MonoType* +mono_type_get_underlying_type_ignore_byref (MonoType *type) +{ + if (type->type == MONO_TYPE_VALUETYPE && type->data.klass->enumtype) + return mono_class_enum_basetype (type->data.klass); + if (type->type == MONO_TYPE_GENERICINST && type->data.generic_class->container_class->enumtype) + return mono_class_enum_basetype (type->data.generic_class->container_class); + return type; +} + +ICALL_EXPORT guint32 +ves_icall_RuntimeTypeHandle_type_is_assignable_from (MonoReflectionTypeHandle ref_type, MonoReflectionTypeHandle ref_c, MonoError *error) +{ + error_init (error); + + g_assert (!MONO_HANDLE_IS_NULL (ref_type)); + + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + MonoClass *klass = mono_class_from_mono_type (type); + MonoType *ctype = MONO_HANDLE_GETVAL (ref_c, type); + MonoClass *klassc = mono_class_from_mono_type (ctype); + + if (type->byref ^ ctype->byref) + return FALSE; + + if (type->byref) { + MonoType *t = mono_type_get_underlying_type_ignore_byref (type); + MonoType *ot = mono_type_get_underlying_type_ignore_byref (ctype); + + klass = mono_class_from_mono_type (t); + klassc = mono_class_from_mono_type (ot); + + if (mono_type_is_primitive (t)) { + return mono_type_is_primitive (ot) && klass->instance_size == klassc->instance_size; + } else if (t->type == MONO_TYPE_VAR || t->type == MONO_TYPE_MVAR) { + return t->type == ot->type && t->data.generic_param->num == ot->data.generic_param->num; + } else if (t->type == MONO_TYPE_PTR || t->type == MONO_TYPE_FNPTR) { + return t->type == ot->type; + } else { + if (ot->type == MONO_TYPE_VAR || ot->type == MONO_TYPE_MVAR) + return FALSE; + + if (klass->valuetype) + return klass == klassc; + return klass->valuetype == klassc->valuetype; + } + } + return mono_class_is_assignable_from (klass, klassc); +} + +ICALL_EXPORT guint32 +ves_icall_RuntimeTypeHandle_IsInstanceOfType (MonoReflectionTypeHandle ref_type, MonoObjectHandle obj, MonoError *error) +{ + error_init (error); + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + MonoClass *klass = mono_class_from_mono_type (type); + mono_class_init_checked (klass, error); + return_val_if_nok (error, FALSE); + MonoObjectHandle inst = mono_object_handle_isinst (obj, klass, error); + return_val_if_nok (error, FALSE); + return !MONO_HANDLE_IS_NULL (inst); +} + +ICALL_EXPORT guint32 +ves_icall_RuntimeTypeHandle_GetAttributes (MonoReflectionTypeHandle ref_type, MonoError *error) +{ + error_init (error); + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + MonoClass *klass = mono_class_from_mono_type (type); + return mono_class_get_flags (klass); +} + +ICALL_EXPORT MonoReflectionMarshalAsAttributeHandle +ves_icall_System_Reflection_FieldInfo_get_marshal_info (MonoReflectionFieldHandle field_h, MonoError *error) +{ + error_init (error); + MonoDomain *domain = MONO_HANDLE_DOMAIN (field_h); + MonoClassField *field = MONO_HANDLE_GETVAL (field_h, field); + MonoClass *klass = field->parent; + + MonoGenericClass *gklass = mono_class_try_get_generic_class (klass); + if (mono_class_is_gtd (klass) || + (gklass && gklass->context.class_inst->is_open)) + return MONO_HANDLE_CAST (MonoReflectionMarshalAsAttribute, NULL_HANDLE); + + MonoType *ftype = mono_field_get_type (field); + if (ftype && !(ftype->attrs & FIELD_ATTRIBUTE_HAS_FIELD_MARSHAL)) + return MONO_HANDLE_CAST (MonoReflectionMarshalAsAttribute, NULL_HANDLE); + + MonoMarshalType *info = mono_marshal_load_type_info (klass); + + for (int i = 0; i < info->num_fields; ++i) { + if (info->fields [i].field == field) { + if (!info->fields [i].mspec) + return MONO_HANDLE_CAST (MonoReflectionMarshalAsAttribute, NULL_HANDLE); + else { + return mono_reflection_marshal_as_attribute_from_marshal_spec (domain, klass, info->fields [i].mspec, error); + } + } + } + + return MONO_HANDLE_CAST (MonoReflectionMarshalAsAttribute, NULL_HANDLE); +} + +ICALL_EXPORT MonoReflectionFieldHandle +ves_icall_System_Reflection_FieldInfo_internal_from_handle_type (MonoClassField *handle, MonoType *type, MonoError *error) +{ + MonoClass *klass; + + g_assert (handle); + + error_init (error); + + if (!type) { + klass = handle->parent; + } else { + klass = mono_class_from_mono_type (type); + + gboolean found = klass == handle->parent || mono_class_has_parent (klass, handle->parent); + + if (!found) + /* The managed code will throw the exception */ + return MONO_HANDLE_CAST (MonoReflectionField, NULL_HANDLE); + } + + return mono_field_get_object_handle (mono_domain_get (), klass, handle, error); +} + +ICALL_EXPORT MonoReflectionEventHandle +ves_icall_System_Reflection_EventInfo_internal_from_handle_type (MonoEvent *handle, MonoType *type, MonoError *error) +{ + MonoClass *klass; + + g_assert (handle); + + error_init (error); + + if (!type) { + klass = handle->parent; + } else { + klass = mono_class_from_mono_type (type); + + gboolean found = klass == handle->parent || mono_class_has_parent (klass, handle->parent); + if (!found) + /* Managed code will throw an exception */ + return MONO_HANDLE_CAST (MonoReflectionEvent, NULL_HANDLE); + } + + return mono_event_get_object_handle (mono_domain_get (), klass, handle, error); +} + + +ICALL_EXPORT MonoReflectionPropertyHandle +ves_icall_System_Reflection_PropertyInfo_internal_from_handle_type (MonoProperty *handle, MonoType *type, MonoError *error) +{ + error_init (error); + MonoClass *klass; + + g_assert (handle); + + if (!type) { + klass = handle->parent; + } else { + klass = mono_class_from_mono_type (type); + + gboolean found = klass == handle->parent || mono_class_has_parent (klass, handle->parent); + if (!found) + /* Managed code will throw an exception */ + return MONO_HANDLE_CAST (MonoReflectionProperty, NULL_HANDLE); + } + + return mono_property_get_object_handle (mono_domain_get (), klass, handle, error); +} + +ICALL_EXPORT MonoArrayHandle +ves_icall_System_Reflection_FieldInfo_GetTypeModifiers (MonoReflectionFieldHandle field_h, MonoBoolean optional, MonoError *error) +{ + error_init (error); + MonoClassField *field = MONO_HANDLE_GETVAL (field_h, field); + + MonoType *type = mono_field_get_type_checked (field, error); + if (!is_ok (error)) + return MONO_HANDLE_CAST (MonoArray, NULL_HANDLE); + + return type_array_from_modifiers (field->parent->image, type, optional, error); +} + +ICALL_EXPORT int +vell_icall_get_method_attributes (MonoMethod *method) +{ + return method->flags; +} + +ICALL_EXPORT void +ves_icall_get_method_info (MonoMethod *method, MonoMethodInfo *info, MonoError *error) +{ + MonoDomain *domain = mono_domain_get (); + + MonoMethodSignature* sig = mono_method_signature_checked (method, error); + return_if_nok (error); + + MonoReflectionTypeHandle rt = mono_type_get_object_handle (domain, &method->klass->byval_arg, error); + return_if_nok (error); + + MONO_STRUCT_SETREF (info, parent, MONO_HANDLE_RAW (rt)); + + MONO_HANDLE_ASSIGN (rt, mono_type_get_object_handle (domain, sig->ret, error)); + return_if_nok (error); + + MONO_STRUCT_SETREF (info, ret, MONO_HANDLE_RAW (rt)); + + info->attrs = method->flags; + info->implattrs = method->iflags; + guint32 callconv; + if (sig->call_convention == MONO_CALL_DEFAULT) + callconv = sig->sentinelpos >= 0 ? 2 : 1; + else { + if (sig->call_convention == MONO_CALL_VARARG || sig->sentinelpos >= 0) + callconv = 2; + else + callconv = 1; + } + callconv |= (sig->hasthis << 5) | (sig->explicit_this << 6); + info->callconv = callconv; +} + +ICALL_EXPORT MonoArrayHandle +ves_icall_System_Reflection_MonoMethodInfo_get_parameter_info (MonoMethod *method, MonoReflectionMethodHandle member, MonoError *error) +{ + error_init (error); + MonoDomain *domain = mono_domain_get (); + + MonoReflectionTypeHandle reftype = MONO_HANDLE_NEW (MonoReflectionType, NULL); + MONO_HANDLE_GET (reftype, member, reftype); + MonoClass *klass = NULL; + if (!MONO_HANDLE_IS_NULL (reftype)) + klass = mono_class_from_mono_type (MONO_HANDLE_GETVAL (reftype, type)); + return mono_param_get_objects_internal (domain, method, klass, error); +} + +ICALL_EXPORT MonoReflectionMarshalAsAttributeHandle +ves_icall_System_MonoMethodInfo_get_retval_marshal (MonoMethod *method, MonoError *error) +{ + error_init (error); + MonoDomain *domain = mono_domain_get (); + MonoReflectionMarshalAsAttributeHandle res = MONO_HANDLE_NEW (MonoReflectionMarshalAsAttribute, NULL); + + MonoMarshalSpec **mspecs = g_new (MonoMarshalSpec*, mono_method_signature (method)->param_count + 1); + mono_method_get_marshal_info (method, mspecs); + + if (mspecs [0]) { + MONO_HANDLE_ASSIGN (res, mono_reflection_marshal_as_attribute_from_marshal_spec (domain, method->klass, mspecs [0], error)); + goto_if_nok (error, leave); + } + +leave: + for (int i = mono_method_signature (method)->param_count; i >= 0; i--) + if (mspecs [i]) + mono_metadata_free_marshal_spec (mspecs [i]); + g_free (mspecs); + + return res; +} + +ICALL_EXPORT gint32 +ves_icall_MonoField_GetFieldOffset (MonoReflectionField *field) +{ + MonoClass *parent = field->field->parent; + mono_class_setup_fields (parent); + + return field->field->offset - sizeof (MonoObject); +} + +ICALL_EXPORT MonoReflectionTypeHandle +ves_icall_MonoField_GetParentType (MonoReflectionFieldHandle field, MonoBoolean declaring, MonoError *error) +{ + error_init (error); + MonoDomain *domain = MONO_HANDLE_DOMAIN (field); + MonoClass *parent; + + if (declaring) { + MonoClassField *f = MONO_HANDLE_GETVAL (field, field); + parent = f->parent; + } else { + parent = MONO_HANDLE_GETVAL (field, klass); + } + + return mono_type_get_object_handle (domain, &parent->byval_arg, error); +} + +ICALL_EXPORT MonoObject * +ves_icall_MonoField_GetValueInternal (MonoReflectionField *field, MonoObject *obj) +{ + MonoError error; + MonoClass *fklass = field->klass; + MonoClassField *cf = field->field; + MonoDomain *domain = mono_object_domain (field); + + if (fklass->image->assembly->ref_only) { + mono_set_pending_exception (mono_get_exception_invalid_operation ( + "It is illegal to get the value on a field on a type loaded using the ReflectionOnly methods.")); + return NULL; + } + + if (mono_security_core_clr_enabled () && + !mono_security_core_clr_ensure_reflection_access_field (cf, &error)) { + mono_error_set_pending_exception (&error); + return NULL; + } + +#ifndef DISABLE_REMOTING + if (G_UNLIKELY (obj != NULL && mono_class_is_transparent_proxy (mono_object_class (obj)))) { + /* We get here if someone used a + * System.Reflection.FieldInfo:GetValue on a + * ContextBoundObject's or cross-domain MarshalByRefObject's + * transparent proxy. */ + MonoObject *result = mono_load_remote_field_new_checked (obj, fklass, cf, &error); + mono_error_set_pending_exception (&error); + return result; + } +#endif + + MonoObject * result = mono_field_get_value_object_checked (domain, cf, obj, &error); + mono_error_set_pending_exception (&error); + return result; +} + +ICALL_EXPORT void +ves_icall_MonoField_SetValueInternal (MonoReflectionFieldHandle field, MonoObjectHandle obj, MonoObjectHandle value, MonoError *error) +{ + MonoClassField *cf = MONO_HANDLE_GETVAL (field, field); + + MonoClass *field_klass = MONO_HANDLE_GETVAL (field, klass); + if (field_klass->image->assembly->ref_only) { + mono_error_set_invalid_operation (error, "It is illegal to set the value on a field on a type loaded using the ReflectionOnly methods."); + return; + } + + if (mono_security_core_clr_enabled () && + !mono_security_core_clr_ensure_reflection_access_field (cf, error)) { + return; + } + +#ifndef DISABLE_REMOTING + if (G_UNLIKELY (!MONO_HANDLE_IS_NULL (obj) && mono_class_is_transparent_proxy (mono_handle_class (obj)))) { + /* We get here if someone used a + * System.Reflection.FieldInfo:SetValue on a + * ContextBoundObject's or cross-domain MarshalByRefObject's + * transparent proxy. */ + /* FIXME: use handles for mono_store_remote_field_new_checked */ + MonoObject *v = MONO_HANDLE_RAW (value); + MonoObject *o = MONO_HANDLE_RAW (obj); + mono_store_remote_field_new_checked (o, field_klass, cf, v, error); + return; + } +#endif + + MonoType *type = mono_field_get_type_checked (cf, error); + return_if_nok (error); + + gboolean isref = FALSE; + uint32_t value_gchandle = 0; + gchar *v = NULL; + if (!type->byref) { + switch (type->type) { + case MONO_TYPE_U1: + case MONO_TYPE_I1: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_U2: + case MONO_TYPE_I2: + case MONO_TYPE_CHAR: + case MONO_TYPE_U: + case MONO_TYPE_I: + case MONO_TYPE_U4: + case MONO_TYPE_I4: + case MONO_TYPE_R4: + case MONO_TYPE_U8: + case MONO_TYPE_I8: + case MONO_TYPE_R8: + case MONO_TYPE_VALUETYPE: + case MONO_TYPE_PTR: + isref = FALSE; + if (!MONO_HANDLE_IS_NULL (value)) + v = mono_object_handle_pin_unbox (value, &value_gchandle); + break; + case MONO_TYPE_STRING: + case MONO_TYPE_OBJECT: + case MONO_TYPE_CLASS: + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: + /* Do nothing */ + isref = TRUE; + break; + case MONO_TYPE_GENERICINST: { + MonoGenericClass *gclass = type->data.generic_class; + g_assert (!gclass->context.class_inst->is_open); + + if (mono_class_is_nullable (mono_class_from_mono_type (type))) { + MonoClass *nklass = mono_class_from_mono_type (type); + + /* + * Convert the boxed vtype into a Nullable structure. + * This is complicated by the fact that Nullables have + * a variable structure. + */ + MonoObjectHandle nullable = MONO_HANDLE_NEW (MonoObject, mono_object_new_checked (mono_domain_get (), nklass, error)); + return_if_nok (error); + + uint32_t nullable_gchandle = 0; + guint8 *nval = mono_object_handle_pin_unbox (nullable, &nullable_gchandle); + mono_nullable_init_from_handle (nval, value, nklass); + + isref = FALSE; + value_gchandle = nullable_gchandle; + v = (gchar*)nval; + } + else { + isref = !gclass->container_class->valuetype; + if (!isref && !MONO_HANDLE_IS_NULL (value)) { + v = mono_object_handle_pin_unbox (value, &value_gchandle); + }; + } + break; + } + default: + g_error ("type 0x%x not handled in " + "ves_icall_FieldInfo_SetValueInternal", type->type); + return; + } + } + + /* either value is a reference type, or it's a value type and we pinned + * it and v points to the payload. */ + g_assert ((isref && v == NULL && value_gchandle == 0) || + (!isref && v != NULL && value_gchandle != 0) || + (!isref && v == NULL && value_gchandle == 0)); + + if (type->attrs & FIELD_ATTRIBUTE_STATIC) { + MonoVTable *vtable = mono_class_vtable_full (MONO_HANDLE_DOMAIN (field), cf->parent, error); + goto_if_nok (error, leave); + + if (!vtable->initialized) { + if (!mono_runtime_class_init_full (vtable, error)) + goto leave; + } + if (isref) + mono_field_static_set_value (vtable, cf, MONO_HANDLE_RAW (value)); /* FIXME make mono_field_static_set_value work with handles for value */ + else + mono_field_static_set_value (vtable, cf, v); + } else { + + if (isref) + MONO_HANDLE_SET_FIELD_REF (obj, cf, value); + else + mono_field_set_value (MONO_HANDLE_RAW (obj), cf, v); /* FIXME: make mono_field_set_value take a handle for obj */ + } +leave: + if (value_gchandle) + mono_gchandle_free (value_gchandle); +} + +ICALL_EXPORT void +ves_icall_System_RuntimeFieldHandle_SetValueDirect (MonoReflectionField *field, MonoReflectionType *field_type, MonoTypedRef *obj, MonoObject *value, MonoReflectionType *context_type) +{ + MonoClassField *f; + + g_assert (field); + g_assert (obj); + g_assert (value); + + f = field->field; + if (!MONO_TYPE_ISSTRUCT (&f->parent->byval_arg)) { + mono_set_pending_exception (mono_get_exception_not_implemented (NULL)); + return; + } + + if (MONO_TYPE_IS_REFERENCE (f->type)) + mono_copy_value (f->type, (guint8*)obj->value + f->offset - sizeof (MonoObject), value, FALSE); + else + mono_copy_value (f->type, (guint8*)obj->value + f->offset - sizeof (MonoObject), mono_object_unbox (value), FALSE); +} + +ICALL_EXPORT MonoObject * +ves_icall_MonoField_GetRawConstantValue (MonoReflectionField *rfield) +{ + MonoObject *o = NULL; + MonoClassField *field = rfield->field; + MonoClass *klass; + MonoDomain *domain = mono_object_domain (rfield); + gchar *v; + MonoTypeEnum def_type; + const char *def_value; + MonoType *t; + MonoError error; + + mono_class_init (field->parent); + + t = mono_field_get_type_checked (field, &error); + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + return NULL; + } + + if (!(t->attrs & FIELD_ATTRIBUTE_HAS_DEFAULT)) { + mono_set_pending_exception (mono_get_exception_invalid_operation (NULL)); + return NULL; + } + + if (image_is_dynamic (field->parent->image)) { + MonoClass *klass = field->parent; + int fidx = field - klass->fields; + MonoFieldDefaultValue *def_values = mono_class_get_field_def_values (klass); + + g_assert (def_values); + def_type = def_values [fidx].def_type; + def_value = def_values [fidx].data; + + if (def_type == MONO_TYPE_END) { + mono_set_pending_exception (mono_get_exception_invalid_operation (NULL)); + return NULL; + } + } else { + def_value = mono_class_get_field_default_value (field, &def_type); + /* FIXME, maybe we should try to raise TLE if field->parent is broken */ + if (!def_value) { + mono_set_pending_exception (mono_get_exception_invalid_operation (NULL)); + return NULL; + } + } + + /*FIXME unify this with reflection.c:mono_get_object_from_blob*/ + switch (def_type) { + case MONO_TYPE_U1: + case MONO_TYPE_I1: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_U2: + case MONO_TYPE_I2: + case MONO_TYPE_CHAR: + case MONO_TYPE_U: + case MONO_TYPE_I: + case MONO_TYPE_U4: + case MONO_TYPE_I4: + case MONO_TYPE_R4: + case MONO_TYPE_U8: + case MONO_TYPE_I8: + case MONO_TYPE_R8: { + MonoType *t; + + /* boxed value type */ + t = g_new0 (MonoType, 1); + t->type = def_type; + klass = mono_class_from_mono_type (t); + g_free (t); + o = mono_object_new_checked (domain, klass, &error); + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + return NULL; + } + v = ((gchar *) o) + sizeof (MonoObject); + mono_get_constant_value_from_blob (domain, def_type, def_value, v, &error); + if (mono_error_set_pending_exception (&error)) + return NULL; + break; + } + case MONO_TYPE_STRING: + case MONO_TYPE_CLASS: + mono_get_constant_value_from_blob (domain, def_type, def_value, &o, &error); + if (mono_error_set_pending_exception (&error)) + return NULL; + break; + default: + g_assert_not_reached (); + } + + return o; +} + +ICALL_EXPORT MonoReflectionTypeHandle +ves_icall_MonoField_ResolveType (MonoReflectionFieldHandle ref_field, MonoError *error) +{ + error_init (error); + MonoDomain *domain = MONO_HANDLE_DOMAIN (ref_field); + MonoClassField *field = MONO_HANDLE_GETVAL (ref_field, field); + MonoType *type = mono_field_get_type_checked (field, error); + if (!is_ok (error)) { + return MONO_HANDLE_CAST (MonoReflectionType, NULL_HANDLE); + } + return mono_type_get_object_handle (domain, type, error); +} + +/* From MonoProperty.cs */ +typedef enum { + PInfo_Attributes = 1, + PInfo_GetMethod = 1 << 1, + PInfo_SetMethod = 1 << 2, + PInfo_ReflectedType = 1 << 3, + PInfo_DeclaringType = 1 << 4, + PInfo_Name = 1 << 5 +} PInfo; + +ICALL_EXPORT void +ves_icall_MonoPropertyInfo_get_property_info (MonoReflectionPropertyHandle property, MonoPropertyInfo *info, PInfo req_info, MonoError *error) +{ + error_init (error); + MonoDomain *domain = MONO_HANDLE_DOMAIN (property); + const MonoProperty *pproperty = MONO_HANDLE_GETVAL (property, property); + + if ((req_info & PInfo_ReflectedType) != 0) { + MonoClass *klass = MONO_HANDLE_GETVAL (property, klass); + MonoReflectionTypeHandle rt = mono_type_get_object_handle (domain, &klass->byval_arg, error); + return_if_nok (error); + + MONO_STRUCT_SETREF (info, parent, MONO_HANDLE_RAW (rt)); + } + if ((req_info & PInfo_DeclaringType) != 0) { + MonoReflectionTypeHandle rt = mono_type_get_object_handle (domain, &pproperty->parent->byval_arg, error); + return_if_nok (error); + + MONO_STRUCT_SETREF (info, declaring_type, MONO_HANDLE_RAW (rt)); + } + + if ((req_info & PInfo_Name) != 0) { + MonoStringHandle name = mono_string_new_handle (domain, pproperty->name, error); + return_if_nok (error); + + MONO_STRUCT_SETREF (info, name, MONO_HANDLE_RAW (name)); + } + + if ((req_info & PInfo_Attributes) != 0) + info->attrs = pproperty->attrs; + + if ((req_info & PInfo_GetMethod) != 0) { + MonoClass *property_klass = MONO_HANDLE_GETVAL (property, klass); + MonoReflectionMethodHandle rm; + if (pproperty->get && + (((pproperty->get->flags & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK) != METHOD_ATTRIBUTE_PRIVATE) || + pproperty->get->klass == property_klass)) { + rm = mono_method_get_object_handle (domain, pproperty->get, property_klass, error); + return_if_nok (error); + } else { + rm = MONO_HANDLE_NEW (MonoReflectionMethod, NULL); + } + + MONO_STRUCT_SETREF (info, get, MONO_HANDLE_RAW (rm)); + } + if ((req_info & PInfo_SetMethod) != 0) { + MonoClass *property_klass = MONO_HANDLE_GETVAL (property, klass); + MonoReflectionMethodHandle rm; + if (pproperty->set && + (((pproperty->set->flags & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK) != METHOD_ATTRIBUTE_PRIVATE) || + pproperty->set->klass == property_klass)) { + rm = mono_method_get_object_handle (domain, pproperty->set, property_klass, error); + return_if_nok (error); + } else { + rm = MONO_HANDLE_NEW (MonoReflectionMethod, NULL); + } + + MONO_STRUCT_SETREF (info, set, MONO_HANDLE_RAW (rm)); + } + /* + * There may be other methods defined for properties, though, it seems they are not exposed + * in the reflection API + */ +} + +static gboolean +add_event_other_methods_to_array (MonoDomain *domain, MonoMethod *m, MonoArrayHandle dest, int i, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + error_init (error); + MonoReflectionMethodHandle rm = mono_method_get_object_handle (domain, m, NULL, error); + goto_if_nok (error, leave); + MONO_HANDLE_ARRAY_SETREF (dest, i, rm); +leave: + HANDLE_FUNCTION_RETURN_VAL (is_ok (error)); +} + +ICALL_EXPORT void +ves_icall_MonoEventInfo_get_event_info (MonoReflectionMonoEventHandle ref_event, MonoEventInfo *info, MonoError *error) +{ + error_init (error); + MonoDomain *domain = MONO_HANDLE_DOMAIN (ref_event); + + MonoClass *klass = MONO_HANDLE_GETVAL (ref_event, klass); + MonoEvent *event = MONO_HANDLE_GETVAL (ref_event, event); + + MonoReflectionTypeHandle rt = mono_type_get_object_handle (domain, &klass->byval_arg, error); + return_if_nok (error); + MONO_STRUCT_SETREF (info, reflected_type, MONO_HANDLE_RAW (rt)); + + rt = mono_type_get_object_handle (domain, &event->parent->byval_arg, error); + return_if_nok (error); + MONO_STRUCT_SETREF (info, declaring_type, MONO_HANDLE_RAW (rt)); + + MonoStringHandle ev_name = mono_string_new_handle (domain, event->name, error); + return_if_nok (error); + MONO_STRUCT_SETREF (info, name, MONO_HANDLE_RAW (ev_name)); + + info->attrs = event->attrs; + + MonoReflectionMethodHandle rm; + if (event->add) { + rm = mono_method_get_object_handle (domain, event->add, NULL, error); + return_if_nok (error); + } else { + rm = MONO_HANDLE_NEW (MonoReflectionMethod, NULL); + } + + MONO_STRUCT_SETREF (info, add_method, MONO_HANDLE_RAW (rm)); + + if (event->remove) { + rm = mono_method_get_object_handle (domain, event->remove, NULL, error); + return_if_nok (error); + } else { + rm = MONO_HANDLE_NEW (MonoReflectionMethod, NULL); + } + + MONO_STRUCT_SETREF (info, remove_method, MONO_HANDLE_RAW (rm)); + + if (event->raise) { + rm = mono_method_get_object_handle (domain, event->raise, NULL, error); + return_if_nok (error); + } else { + rm = MONO_HANDLE_NEW (MonoReflectionMethod, NULL); + } + + MONO_STRUCT_SETREF (info, raise_method, MONO_HANDLE_RAW (rm)); + +#ifndef MONO_SMALL_CONFIG + if (event->other) { + int i, n = 0; + while (event->other [n]) + n++; + MonoArrayHandle info_arr = mono_array_new_handle (domain, mono_defaults.method_info_class, n, error); + return_if_nok (error); + + MONO_STRUCT_SETREF (info, other_methods, MONO_HANDLE_RAW (info_arr)); + + for (i = 0; i < n; i++) + if (!add_event_other_methods_to_array (domain, event->other [i], info_arr, i, error)) + return; + } +#endif +} + +static void +collect_interfaces (MonoClass *klass, GHashTable *ifaces, MonoError *error) +{ + int i; + MonoClass *ic; + + mono_class_setup_interfaces (klass, error); + if (!mono_error_ok (error)) + return; + + for (i = 0; i < klass->interface_count; i++) { + ic = klass->interfaces [i]; + g_hash_table_insert (ifaces, ic, ic); + + collect_interfaces (ic, ifaces, error); + if (!mono_error_ok (error)) + return; + } +} + +typedef struct { + MonoArrayHandle iface_array; + MonoGenericContext *context; + MonoError *error; + MonoDomain *domain; + int next_idx; +} FillIfaceArrayData; + +static void +fill_iface_array (gpointer key, gpointer value, gpointer user_data) +{ + HANDLE_FUNCTION_ENTER (); + FillIfaceArrayData *data = (FillIfaceArrayData *)user_data; + MonoClass *ic = (MonoClass *)key; + MonoType *ret = &ic->byval_arg, *inflated = NULL; + MonoError *error = data->error; + + goto_if_nok (error, leave); + + if (data->context && mono_class_is_ginst (ic) && mono_class_get_generic_class (ic)->context.class_inst->is_open) { + inflated = ret = mono_class_inflate_generic_type_checked (ret, data->context, error); + goto_if_nok (error, leave); + } + + MonoReflectionTypeHandle rt = mono_type_get_object_handle (data->domain, ret, error); + goto_if_nok (error, leave); + + MONO_HANDLE_ARRAY_SETREF (data->iface_array, data->next_idx, rt); + data->next_idx++; + + if (inflated) + mono_metadata_free_type (inflated); +leave: + HANDLE_FUNCTION_RETURN (); +} + +static guint +get_interfaces_hash (gconstpointer v1) +{ + MonoClass *k = (MonoClass*)v1; + + return k->type_token; +} + +ICALL_EXPORT MonoArrayHandle +ves_icall_RuntimeType_GetInterfaces (MonoReflectionTypeHandle ref_type, MonoError *error) +{ + error_init (error); + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + MonoClass *klass = mono_class_from_mono_type (type); + + GHashTable *iface_hash = g_hash_table_new (get_interfaces_hash, NULL); + + MonoGenericContext *context = NULL; + if (mono_class_is_ginst (klass) && mono_class_get_generic_class (klass)->context.class_inst->is_open) { + context = mono_class_get_context (klass); + klass = mono_class_get_generic_class (klass)->container_class; + } + + for (MonoClass *parent = klass; parent; parent = parent->parent) { + mono_class_setup_interfaces (parent, error); + goto_if_nok (error, fail); + collect_interfaces (parent, iface_hash, error); + goto_if_nok (error, fail); + } + + MonoDomain *domain = MONO_HANDLE_DOMAIN (ref_type); + + int len = g_hash_table_size (iface_hash); + if (len == 0) { + 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); + } + + FillIfaceArrayData data; + data.iface_array = MONO_HANDLE_NEW (MonoArray, mono_array_new_cached (domain, mono_defaults.runtimetype_class, len, error)); + goto_if_nok (error, fail); + data.context = context; + data.error = error; + data.domain = domain; + data.next_idx = 0; + + g_hash_table_foreach (iface_hash, fill_iface_array, &data); + + goto_if_nok (error, fail); + + g_hash_table_destroy (iface_hash); + return data.iface_array; + +fail: + g_hash_table_destroy (iface_hash); + return MONO_HANDLE_CAST (MonoArray, NULL_HANDLE); +} + +static gboolean +set_interface_map_data_method_object (MonoDomain *domain, MonoMethod *method, MonoClass *iclass, int ioffset, MonoClass *klass, MonoArrayHandle targets, MonoArrayHandle methods, int i, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + error_init (error); + MonoReflectionMethodHandle member = mono_method_get_object_handle (domain, method, iclass, error); + goto_if_nok (error, leave); + + MONO_HANDLE_ARRAY_SETREF (methods, i, member); + + MONO_HANDLE_ASSIGN (member, mono_method_get_object_handle (domain, klass->vtable [i + ioffset], klass, error)); + goto_if_nok (error, leave); + + MONO_HANDLE_ARRAY_SETREF (targets, i, member); + +leave: + HANDLE_FUNCTION_RETURN_VAL (is_ok (error)); +} + +ICALL_EXPORT void +ves_icall_RuntimeType_GetInterfaceMapData (MonoReflectionTypeHandle ref_type, MonoReflectionTypeHandle ref_iface, MonoArrayHandleOut targets, MonoArrayHandleOut methods, MonoError *error) +{ + error_init (error); + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + MonoClass *klass = mono_class_from_mono_type (type); + MonoType *iface = MONO_HANDLE_GETVAL (ref_iface, type); + MonoClass *iclass = mono_class_from_mono_type (iface); + + mono_class_init_checked (klass, error); + return_if_nok (error); + mono_class_init_checked (iclass, error); + return_if_nok (error); + + mono_class_setup_vtable (klass); + + gboolean variance_used; + int ioffset = mono_class_interface_offset_with_variance (klass, iclass, &variance_used); + if (ioffset == -1) + return; + + int len = mono_class_num_methods (iclass); + MonoDomain *domain = MONO_HANDLE_DOMAIN (ref_type); + MonoArrayHandle targets_arr = mono_array_new_handle (domain, mono_defaults.method_info_class, len, error); + return_if_nok (error); + MONO_HANDLE_ASSIGN (targets, targets_arr); + + MonoArrayHandle methods_arr = mono_array_new_handle (domain, mono_defaults.method_info_class, len, error); + return_if_nok (error); + MONO_HANDLE_ASSIGN (methods, methods_arr); + + MonoMethod* method; + int i = 0; + gpointer iter = NULL; + while ((method = mono_class_get_methods (iclass, &iter))) { + if (!set_interface_map_data_method_object (domain, method, iclass, ioffset, klass, targets, methods, i, error)) + return; + i ++; + } +} + +ICALL_EXPORT void +ves_icall_RuntimeType_GetPacking (MonoReflectionTypeHandle ref_type, guint32 *packing, guint32 *size, MonoError *error) +{ + error_init (error); + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + MonoClass *klass = mono_class_from_mono_type (type); + + mono_class_init_checked (klass, error); + if (!is_ok (error)) + return; + + if (image_is_dynamic (klass->image)) { + MonoReflectionTypeBuilderHandle tb = MONO_HANDLE_CAST (MonoReflectionTypeBuilder, ref_type); + *packing = MONO_HANDLE_GETVAL (tb, packing_size); + *size = MONO_HANDLE_GETVAL (tb, class_size); + } else { + mono_metadata_packing_from_typedef (klass->image, klass->type_token, packing, size); + } +} + +ICALL_EXPORT MonoReflectionTypeHandle +ves_icall_RuntimeTypeHandle_GetElementType (MonoReflectionTypeHandle ref_type, MonoError *error) +{ + error_init (error); + + MonoDomain *domain = MONO_HANDLE_DOMAIN (ref_type); + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + + if (!type->byref && type->type == MONO_TYPE_SZARRAY) { + return mono_type_get_object_handle (domain, &type->data.klass->byval_arg, error); + } + + MonoClass *klass = mono_class_from_mono_type (type); + mono_class_init_checked (klass, error); + if (!is_ok (error)) + return MONO_HANDLE_CAST (MonoReflectionType, NULL_HANDLE); + + // GetElementType should only return a type for: + // Array Pointer PassedByRef + if (type->byref) + return mono_type_get_object_handle (domain, &klass->byval_arg, error); + else if (klass->element_class && MONO_CLASS_IS_ARRAY (klass)) + return mono_type_get_object_handle (domain, &klass->element_class->byval_arg, error); + else if (klass->element_class && type->type == MONO_TYPE_PTR) + return mono_type_get_object_handle (domain, &klass->element_class->byval_arg, error); + else + return MONO_HANDLE_CAST (MonoReflectionType, NULL_HANDLE); +} + +ICALL_EXPORT MonoReflectionTypeHandle +ves_icall_RuntimeTypeHandle_GetBaseType (MonoReflectionTypeHandle ref_type, MonoError *error) +{ + error_init (error); + + MonoDomain *domain = MONO_HANDLE_DOMAIN (ref_type); + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + + if (type->byref) + return MONO_HANDLE_CAST (MonoReflectionType, NULL_HANDLE); + + MonoClass *klass = mono_class_from_mono_type (type); + if (!klass->parent) + return MONO_HANDLE_CAST (MonoReflectionType, NULL_HANDLE); + + return mono_type_get_object_handle (domain, &klass->parent->byval_arg, error); +} + +ICALL_EXPORT MonoBoolean +ves_icall_RuntimeTypeHandle_IsPointer (MonoReflectionTypeHandle ref_type, MonoError *error) +{ + error_init (error); + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + return type->type == MONO_TYPE_PTR; +} + +ICALL_EXPORT MonoBoolean +ves_icall_RuntimeTypeHandle_IsPrimitive (MonoReflectionTypeHandle ref_type, MonoError *error) +{ + error_init (error); + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + return (!type->byref && (((type->type >= MONO_TYPE_BOOLEAN) && (type->type <= MONO_TYPE_R8)) || (type->type == MONO_TYPE_I) || (type->type == MONO_TYPE_U))); +} + +ICALL_EXPORT MonoBoolean +ves_icall_RuntimeTypeHandle_HasReferences (MonoReflectionTypeHandle ref_type, MonoError *error) +{ + error_init (error); + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + MonoClass *klass; + + klass = mono_class_from_mono_type (type); + mono_class_init (klass); + return klass->has_references; +} + +ICALL_EXPORT MonoBoolean +ves_icall_RuntimeTypeHandle_IsByRef (MonoReflectionTypeHandle ref_type, MonoError *error) +{ + error_init (error); + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + return type->byref; +} + +ICALL_EXPORT MonoBoolean +ves_icall_RuntimeTypeHandle_IsComObject (MonoReflectionTypeHandle ref_type, MonoError *error) +{ + error_init (error); + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + MonoClass *klass = mono_class_from_mono_type (type); + mono_class_init_checked (klass, error); + if (!is_ok (error)) + return FALSE; + + return mono_class_is_com_object (klass); +} + +ICALL_EXPORT guint32 +ves_icall_reflection_get_token (MonoObjectHandle obj, MonoError *error) +{ + error_init (error); + return mono_reflection_get_token_checked (obj, error); +} + +ICALL_EXPORT MonoReflectionModuleHandle +ves_icall_RuntimeTypeHandle_GetModule (MonoReflectionTypeHandle type, MonoError *error) +{ + error_init (error); + MonoDomain *domain = MONO_HANDLE_DOMAIN (type); + MonoType *t = MONO_HANDLE_GETVAL (type, type); + MonoClass *klass = mono_class_from_mono_type (t); + return mono_module_get_object_handle (domain, klass->image, error); +} + +ICALL_EXPORT MonoReflectionAssemblyHandle +ves_icall_RuntimeTypeHandle_GetAssembly (MonoReflectionTypeHandle type, MonoError *error) +{ + error_init (error); + MonoDomain *domain = mono_domain_get (); + MonoType *t = MONO_HANDLE_GETVAL (type, type); + MonoClass *klass = mono_class_from_mono_type (t); + return mono_assembly_get_object_handle (domain, klass->image->assembly, error); +} + +ICALL_EXPORT MonoReflectionTypeHandle +ves_icall_RuntimeType_get_DeclaringType (MonoReflectionTypeHandle ref_type, MonoError *error) +{ + error_init (error); + MonoDomain *domain = mono_domain_get (); + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + MonoClass *klass; + + if (type->byref) + return MONO_HANDLE_CAST (MonoReflectionType, NULL_HANDLE); + if (type->type == MONO_TYPE_VAR) { + MonoGenericContainer *param = mono_type_get_generic_param_owner (type); + klass = param ? param->owner.klass : NULL; + } else if (type->type == MONO_TYPE_MVAR) { + MonoGenericContainer *param = mono_type_get_generic_param_owner (type); + klass = param ? param->owner.method->klass : NULL; + } else { + klass = mono_class_from_mono_type (type)->nested_in; + } + + if (!klass) + return MONO_HANDLE_CAST (MonoReflectionType, NULL_HANDLE); + + return mono_type_get_object_handle (domain, &klass->byval_arg, error); +} + +ICALL_EXPORT MonoStringHandle +ves_icall_RuntimeType_get_Name (MonoReflectionTypeHandle reftype, MonoError *error) +{ + MonoDomain *domain = mono_domain_get (); + MonoType *type = MONO_HANDLE_RAW(reftype)->type; + MonoClass *klass = mono_class_from_mono_type (type); + + if (type->byref) { + char *n = g_strdup_printf ("%s&", klass->name); + MonoStringHandle res = mono_string_new_handle (domain, n, error); + + g_free (n); + + return res; + } else { + return mono_string_new_handle (domain, klass->name, error); + } +} + +ICALL_EXPORT MonoStringHandle +ves_icall_RuntimeType_get_Namespace (MonoReflectionTypeHandle type, MonoError *error) +{ + MonoDomain *domain = mono_domain_get (); + MonoClass *klass = mono_class_from_mono_type_handle (type); + + while (klass->nested_in) + klass = klass->nested_in; + + if (klass->name_space [0] == '\0') + return NULL_HANDLE_STRING; + else + return mono_string_new_handle (domain, klass->name_space, error); +} + +ICALL_EXPORT gint32 +ves_icall_RuntimeTypeHandle_GetArrayRank (MonoReflectionTypeHandle ref_type, MonoError *error) +{ + error_init (error); + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + + if (type->type != MONO_TYPE_ARRAY && type->type != MONO_TYPE_SZARRAY) { + mono_error_set_argument (error, "type", "Type must be an array type"); + return 0; + } + + MonoClass *klass = mono_class_from_mono_type (type); + + return klass->rank; +} + +static MonoArrayHandle +create_type_array (MonoDomain *domain, MonoBoolean runtimeTypeArray, int count, MonoError *error) +{ + return mono_array_new_handle (domain, runtimeTypeArray ? mono_defaults.runtimetype_class : mono_defaults.systemtype_class, count, error); +} + +static gboolean +set_type_object_in_array (MonoDomain *domain, MonoType *type, MonoArrayHandle dest, int i, MonoError *error) +{ + HANDLE_FUNCTION_ENTER(); + error_init (error); + MonoReflectionTypeHandle rt = mono_type_get_object_handle (domain, type, error); + goto_if_nok (error, leave); + + MONO_HANDLE_ARRAY_SETREF (dest, i, rt); + +leave: + HANDLE_FUNCTION_RETURN_VAL (is_ok (error)); +} + +ICALL_EXPORT MonoArrayHandle +ves_icall_RuntimeType_GetGenericArguments (MonoReflectionTypeHandle ref_type, MonoBoolean runtimeTypeArray, MonoError *error) +{ + error_init (error); + MonoDomain *domain = MONO_HANDLE_DOMAIN (ref_type); + + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + MonoClass *klass = mono_class_from_mono_type (type); + + MonoArrayHandle res = MONO_HANDLE_NEW (MonoArray, NULL); + if (mono_class_is_gtd (klass)) { + MonoGenericContainer *container = mono_class_get_generic_container (klass); + MONO_HANDLE_ASSIGN (res, create_type_array (domain, runtimeTypeArray, container->type_argc, error)); + goto_if_nok (error, leave); + for (int i = 0; i < container->type_argc; ++i) { + MonoClass *pklass = mono_class_from_generic_parameter_internal (mono_generic_container_get_param (container, i)); + + if (!set_type_object_in_array (domain, &pklass->byval_arg, res, i, error)) + goto leave; + } + + } else if (mono_class_is_ginst (klass)) { + MonoGenericInst *inst = mono_class_get_generic_class (klass)->context.class_inst; + MONO_HANDLE_ASSIGN (res, create_type_array (domain, runtimeTypeArray, inst->type_argc, error)); + goto_if_nok (error, leave); + for (int i = 0; i < inst->type_argc; ++i) { + if (!set_type_object_in_array (domain, inst->type_argv [i], res, i, error)) + goto leave; + } + } + +leave: + return res; +} + +ICALL_EXPORT gboolean +ves_icall_RuntimeTypeHandle_IsGenericTypeDefinition (MonoReflectionTypeHandle ref_type, MonoError *error) +{ + error_init (error); + + if (!IS_MONOTYPE (MONO_HANDLE_RAW(ref_type))) + return FALSE; + + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + if (type->byref) + return FALSE; + + MonoClass *klass = mono_class_from_mono_type (type); + return mono_class_is_gtd (klass); +} + +ICALL_EXPORT MonoReflectionTypeHandle +ves_icall_RuntimeTypeHandle_GetGenericTypeDefinition_impl (MonoReflectionTypeHandle ref_type, MonoError *error) +{ + error_init (error); + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + + MonoReflectionTypeHandle ret = MONO_HANDLE_NEW (MonoReflectionType, NULL); + + if (type->byref) + goto leave; + + MonoClass *klass = mono_class_from_mono_type (type); + + if (mono_class_is_gtd (klass)) { + /* check this one */ + MONO_HANDLE_ASSIGN (ret, ref_type); + goto leave; + } + if (mono_class_is_ginst (klass)) { + MonoClass *generic_class = mono_class_get_generic_class (klass)->container_class; + + guint32 ref_info_handle = mono_class_get_ref_info_handle (generic_class); + + if (generic_class->wastypebuilder && ref_info_handle) { + MonoObjectHandle tb = mono_gchandle_get_target_handle (ref_info_handle); + g_assert (!MONO_HANDLE_IS_NULL (tb)); + MONO_HANDLE_ASSIGN (ret, tb); + } else { + MonoDomain *domain = MONO_HANDLE_DOMAIN (ref_type); + MONO_HANDLE_ASSIGN (ret, mono_type_get_object_handle (domain, &generic_class->byval_arg, error)); + } + } +leave: + return ret; +} + +ICALL_EXPORT MonoReflectionTypeHandle +ves_icall_RuntimeType_MakeGenericType (MonoReflectionTypeHandle reftype, MonoArrayHandle type_array, MonoError *error) +{ + error_init (error); + MonoDomain *domain = MONO_HANDLE_DOMAIN (reftype); + + g_assert (IS_MONOTYPE_HANDLE (reftype)); + MonoType *type = MONO_HANDLE_GETVAL (reftype, type); + mono_class_init_checked (mono_class_from_mono_type (type), error); + if (!is_ok (error)) + return MONO_HANDLE_CAST (MonoReflectionType, NULL_HANDLE); + + int count = mono_array_handle_length (type_array); + MonoType **types = g_new0 (MonoType *, count); + + MonoReflectionTypeHandle t = MONO_HANDLE_NEW (MonoReflectionType, NULL); + for (int i = 0; i < count; i++) { + MONO_HANDLE_ARRAY_GETREF (t, type_array, i); + types [i] = MONO_HANDLE_GETVAL (t, type); + } + + MonoType *geninst = mono_reflection_bind_generic_parameters (reftype, count, types, error); + g_free (types); + if (!geninst) { + return MONO_HANDLE_CAST (MonoReflectionType, NULL_HANDLE); + } + + MonoClass *klass = mono_class_from_mono_type (geninst); + + /*we might inflate to the GTD*/ + if (mono_class_is_ginst (klass) && !mono_verifier_class_is_valid_generic_instantiation (klass)) { + mono_error_set_argument (error, "typeArguments", "Invalid generic arguments"); + return MONO_HANDLE_CAST (MonoReflectionType, NULL_HANDLE); + } + + return mono_type_get_object_handle (domain, geninst, error); +} + +ICALL_EXPORT gboolean +ves_icall_RuntimeTypeHandle_HasInstantiation (MonoReflectionTypeHandle ref_type, MonoError *error) +{ + error_init (error); + MonoClass *klass; + + if (!IS_MONOTYPE (MONO_HANDLE_RAW (ref_type))) + return FALSE; + + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + if (type->byref) + return FALSE; + + klass = mono_class_from_mono_type (type); + return mono_class_is_ginst (klass) || mono_class_is_gtd (klass); +} + +ICALL_EXPORT gint32 +ves_icall_RuntimeType_GetGenericParameterPosition (MonoReflectionTypeHandle ref_type, MonoError *error) +{ + error_init (error); + if (!IS_MONOTYPE_HANDLE (ref_type)) + return -1; + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + + if (is_generic_parameter (type)) + return mono_type_get_generic_param_num (type); + return -1; +} + +ICALL_EXPORT MonoGenericParamInfo * +ves_icall_RuntimeTypeHandle_GetGenericParameterInfo (MonoReflectionTypeHandle ref_type, MonoError *error) +{ + error_init (error); + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + return mono_generic_param_info (type->data.generic_param); +} + +ICALL_EXPORT MonoBoolean +ves_icall_RuntimeTypeHandle_IsGenericVariable (MonoReflectionTypeHandle ref_type, MonoError *error) +{ + MonoType *type = MONO_HANDLE_GETVAL(ref_type, type); + return is_generic_parameter (type); +} + +ICALL_EXPORT MonoReflectionMethodHandle +ves_icall_RuntimeType_GetCorrespondingInflatedMethod (MonoReflectionTypeHandle ref_type, + MonoReflectionMethodHandle generic, + MonoError *error) +{ + error_init (error); + MonoDomain *domain = MONO_HANDLE_DOMAIN (ref_type); + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + MonoClass *klass = mono_class_from_mono_type (type); + + mono_class_init_checked (klass, error); + if (!is_ok (error)) + return MONO_HANDLE_CAST (MonoReflectionMethod, NULL_HANDLE); + + MonoMethod *generic_method = MONO_HANDLE_GETVAL (generic, method); + + MonoReflectionMethodHandle ret = MONO_HANDLE_CAST (MonoReflectionMethod, NULL_HANDLE); + MonoMethod *method; + gpointer iter = NULL; + while ((method = mono_class_get_methods (klass, &iter))) { + if (method->token == generic_method->token) { + ret = mono_method_get_object_handle (domain, method, klass, error); + if (!is_ok (error)) + return MONO_HANDLE_CAST (MonoReflectionMethod, NULL_HANDLE); + } + } + + return ret; +} + +ICALL_EXPORT MonoReflectionMethodHandle +ves_icall_RuntimeType_get_DeclaringMethod (MonoReflectionTypeHandle ref_type, MonoError *error) +{ + error_init (error); + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + MonoReflectionMethodHandle ret = MONO_HANDLE_NEW (MonoReflectionMethod, NULL); + + if (type->byref || (type->type != MONO_TYPE_MVAR && type->type != MONO_TYPE_VAR)) { + mono_error_set_invalid_operation (error, "DeclaringMethod can only be used on generic arguments"); + goto leave; + } + if (type->type == MONO_TYPE_VAR) + goto leave; + + MonoMethod *method = mono_type_get_generic_param_owner (type)->owner.method; + g_assert (method); + + MonoDomain *domain = MONO_HANDLE_DOMAIN (ref_type); + + MONO_HANDLE_ASSIGN (ret, mono_method_get_object_handle (domain, method, method->klass, error)); +leave: + return ret; +} + +ICALL_EXPORT MonoBoolean +ves_icall_System_RuntimeType_IsTypeExportedToWindowsRuntime (MonoError *error) +{ + error_init (error); + mono_error_set_not_implemented (error, "%s", ""); + return FALSE; +} + +ICALL_EXPORT MonoBoolean +ves_icall_System_RuntimeType_IsWindowsRuntimeObjectType (MonoError *error) +{ + error_init (error); + mono_error_set_not_implemented (error, "%s", ""); + return FALSE; +} + +ICALL_EXPORT void +ves_icall_MonoMethod_GetPInvoke (MonoReflectionMethodHandle ref_method, int* flags, MonoStringHandleOut entry_point, MonoStringHandleOut dll_name, MonoError *error) +{ + MonoDomain *domain = mono_domain_get (); + MonoMethod *method = MONO_HANDLE_GETVAL (ref_method, method); + MonoImage *image = method->klass->image; + MonoMethodPInvoke *piinfo = (MonoMethodPInvoke *)method; + MonoTableInfo *tables = image->tables; + MonoTableInfo *im = &tables [MONO_TABLE_IMPLMAP]; + MonoTableInfo *mr = &tables [MONO_TABLE_MODULEREF]; + guint32 im_cols [MONO_IMPLMAP_SIZE]; + guint32 scope_token; + const char *import = NULL; + const char *scope = NULL; + + error_init (error); + + if (image_is_dynamic (image)) { + MonoReflectionMethodAux *method_aux = + (MonoReflectionMethodAux *)g_hash_table_lookup (((MonoDynamicImage*)image)->method_aux_hash, method); + if (method_aux) { + import = method_aux->dllentry; + scope = method_aux->dll; + } + + if (!import || !scope) { + mono_error_set_argument (error, "method", "System.Refleciton.Emit method with invalid pinvoke information"); + return; + } + } + else { + if (piinfo->implmap_idx) { + mono_metadata_decode_row (im, piinfo->implmap_idx - 1, im_cols, MONO_IMPLMAP_SIZE); + + piinfo->piflags = im_cols [MONO_IMPLMAP_FLAGS]; + import = mono_metadata_string_heap (image, im_cols [MONO_IMPLMAP_NAME]); + scope_token = mono_metadata_decode_row_col (mr, im_cols [MONO_IMPLMAP_SCOPE] - 1, MONO_MODULEREF_NAME); + scope = mono_metadata_string_heap (image, scope_token); + } + } + + *flags = piinfo->piflags; + MONO_HANDLE_ASSIGN (entry_point, mono_string_new_handle (domain, import, error)); + return_if_nok (error); + MONO_HANDLE_ASSIGN (dll_name, mono_string_new_handle (domain, scope, error)); +} + +ICALL_EXPORT MonoReflectionMethodHandle +ves_icall_MonoMethod_GetGenericMethodDefinition (MonoReflectionMethodHandle ref_method, MonoError *error) +{ + error_init (error); + MonoMethod *method = MONO_HANDLE_GETVAL (ref_method, method); + + if (method->is_generic) + return ref_method; + + if (!method->is_inflated) + return MONO_HANDLE_CAST (MonoReflectionMethod, NULL_HANDLE); + + MonoMethodInflated *imethod = (MonoMethodInflated *) method; + + MonoMethod *result = imethod->declaring; + /* Not a generic method. */ + if (!result->is_generic) + return MONO_HANDLE_CAST (MonoReflectionMethod, NULL_HANDLE); + + if (image_is_dynamic (method->klass->image)) { + MonoDynamicImage *image = (MonoDynamicImage*)method->klass->image; + + /* + * FIXME: Why is this stuff needed at all ? Why can't the code below work for + * the dynamic case as well ? + */ + mono_image_lock ((MonoImage*)image); + MonoReflectionMethodHandle res = MONO_HANDLE_NEW (MonoReflectionMethod, mono_g_hash_table_lookup (image->generic_def_objects, imethod)); + mono_image_unlock ((MonoImage*)image); + + if (!MONO_HANDLE_IS_NULL (res)) + return res; + } + + if (imethod->context.class_inst) { + MonoClass *klass = ((MonoMethod *) imethod)->klass; + /*Generic methods gets the context of the GTD.*/ + if (mono_class_get_context (klass)) { + result = mono_class_inflate_generic_method_full_checked (result, klass, mono_class_get_context (klass), error); + return_val_if_nok (error, MONO_HANDLE_CAST (MonoReflectionMethod, NULL_HANDLE)); + } + } + + return mono_method_get_object_handle (MONO_HANDLE_DOMAIN (ref_method), result, NULL, error); +} + +ICALL_EXPORT gboolean +ves_icall_MonoMethod_get_IsGenericMethod (MonoReflectionMethodHandle ref_method, MonoError *erro) +{ + MonoMethod *method = MONO_HANDLE_GETVAL (ref_method, method); + return mono_method_signature (method)->generic_param_count != 0; +} + +ICALL_EXPORT gboolean +ves_icall_MonoMethod_get_IsGenericMethodDefinition (MonoReflectionMethodHandle ref_method, MonoError *Error) +{ + MonoMethod *method = MONO_HANDLE_GETVAL (ref_method, method); + return method->is_generic; +} + +static gboolean +set_array_generic_argument_handle_inflated (MonoDomain *domain, MonoGenericInst *inst, int i, MonoArrayHandle arr, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + error_init (error); + MonoReflectionTypeHandle rt = mono_type_get_object_handle (domain, inst->type_argv [i], error); + goto_if_nok (error, leave); + MONO_HANDLE_ARRAY_SETREF (arr, i, rt); +leave: + HANDLE_FUNCTION_RETURN_VAL (is_ok (error)); +} + +static gboolean +set_array_generic_argument_handle_gparam (MonoDomain *domain, MonoGenericContainer *container, int i, MonoArrayHandle arr, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + error_init (error); + MonoGenericParam *param = mono_generic_container_get_param (container, i); + MonoClass *pklass = mono_class_from_generic_parameter_internal (param); + MonoReflectionTypeHandle rt = mono_type_get_object_handle (domain, &pklass->byval_arg, error); + goto_if_nok (error, leave); + MONO_HANDLE_ARRAY_SETREF (arr, i, rt); +leave: + HANDLE_FUNCTION_RETURN_VAL (is_ok (error)); +} + +ICALL_EXPORT MonoArrayHandle +ves_icall_MonoMethod_GetGenericArguments (MonoReflectionMethodHandle ref_method, MonoError *error) +{ + error_init (error); + MonoDomain *domain = MONO_HANDLE_DOMAIN (ref_method); + MonoMethod *method = MONO_HANDLE_GETVAL (ref_method, method); + + if (method->is_inflated) { + MonoGenericInst *inst = mono_method_get_context (method)->method_inst; + + if (inst) { + int count = inst->type_argc; + MonoArrayHandle res = mono_array_new_handle (domain, mono_defaults.systemtype_class, count, error); + return_val_if_nok (error, MONO_HANDLE_CAST (MonoArray, NULL_HANDLE)); + + for (int i = 0; i < count; i++) { + if (!set_array_generic_argument_handle_inflated (domain, inst, i, res, error)) + break; + } + return_val_if_nok (error, MONO_HANDLE_CAST (MonoArray, NULL_HANDLE)); + return res; + } + } + + int count = mono_method_signature (method)->generic_param_count; + MonoArrayHandle res = mono_array_new_handle (domain, mono_defaults.systemtype_class, count, error); + return_val_if_nok (error, MONO_HANDLE_CAST (MonoArray, NULL_HANDLE)); + + MonoGenericContainer *container = mono_method_get_generic_container (method); + for (int i = 0; i < count; i++) { + if (!set_array_generic_argument_handle_gparam (domain, container, i, res, error)) + break; + } + return_val_if_nok (error, MONO_HANDLE_CAST (MonoArray, NULL_HANDLE)); + return res; +} + +ICALL_EXPORT MonoObject * +ves_icall_InternalInvoke (MonoReflectionMethod *method, MonoObject *this_arg, MonoArray *params, MonoException **exc) +{ + MonoError error; + /* + * Invoke from reflection is supposed to always be a virtual call (the API + * is stupid), mono_runtime_invoke_*() calls the provided method, allowing + * greater flexibility. + */ + MonoMethod *m = method->method; + MonoMethodSignature *sig = mono_method_signature (m); + MonoImage *image; + int pcount; + void *obj = this_arg; + + *exc = NULL; + + if (mono_security_core_clr_enabled () && + !mono_security_core_clr_ensure_reflection_access_method (m, &error)) { + mono_error_set_pending_exception (&error); + return NULL; + } + + if (!(m->flags & METHOD_ATTRIBUTE_STATIC)) { + if (!mono_class_vtable_full (mono_object_domain (method), m->klass, &error)) { + mono_error_cleanup (&error); /* FIXME does this make sense? */ + mono_gc_wbarrier_generic_store (exc, (MonoObject*) mono_class_get_exception_for_failure (m->klass)); + return NULL; + } + + if (this_arg) { + if (!mono_object_isinst_checked (this_arg, m->klass, &error)) { + if (!is_ok (&error)) { + mono_gc_wbarrier_generic_store (exc, (MonoObject*) mono_error_convert_to_exception (&error)); + return NULL; + } + char *this_name = mono_type_get_full_name (mono_object_get_class (this_arg)); + char *target_name = mono_type_get_full_name (m->klass); + char *msg = g_strdup_printf ("Object of type '%s' doesn't match target type '%s'", this_name, target_name); + mono_gc_wbarrier_generic_store (exc, (MonoObject*) mono_exception_from_name_msg (mono_defaults.corlib, "System.Reflection", "TargetException", msg)); + g_free (msg); + g_free (target_name); + g_free (this_name); + return NULL; + } + m = mono_object_get_virtual_method (this_arg, m); + /* must pass the pointer to the value for valuetype methods */ + if (m->klass->valuetype) + obj = mono_object_unbox (this_arg); + } else if (strcmp (m->name, ".ctor") && !m->wrapper_type) { + mono_gc_wbarrier_generic_store (exc, (MonoObject*) mono_exception_from_name_msg (mono_defaults.corlib, "System.Reflection", "TargetException", "Non-static method requires a target.")); + return NULL; + } + } + + if (sig->ret->byref) { + mono_gc_wbarrier_generic_store (exc, (MonoObject*) mono_exception_from_name_msg (mono_defaults.corlib, "System", "NotSupportedException", "Cannot invoke method returning ByRef type via reflection")); + return NULL; + } + + pcount = params? mono_array_length (params): 0; + if (pcount != sig->param_count) { + mono_gc_wbarrier_generic_store (exc, (MonoObject*) mono_exception_from_name (mono_defaults.corlib, "System.Reflection", "TargetParameterCountException")); + return NULL; + } + + if (mono_class_is_abstract (m->klass) && !strcmp (m->name, ".ctor") && !this_arg) { + mono_gc_wbarrier_generic_store (exc, (MonoObject*) mono_exception_from_name_msg (mono_defaults.corlib, "System.Reflection", "TargetException", "Cannot invoke constructor of an abstract class.")); + return NULL; + } + + image = m->klass->image; + if (image->assembly->ref_only) { + mono_gc_wbarrier_generic_store (exc, (MonoObject*) mono_get_exception_invalid_operation ("It is illegal to invoke a method on a type loaded using the ReflectionOnly api.")); + return NULL; + } + + if (image_is_dynamic (image) && !((MonoDynamicImage*)image)->run) { + mono_gc_wbarrier_generic_store (exc, (MonoObject*) mono_get_exception_not_supported ("Cannot invoke a method in a dynamic assembly without run access.")); + return NULL; + } + + if (m->klass->rank && !strcmp (m->name, ".ctor")) { + MonoArray *arr; + int i; + uintptr_t *lengths; + intptr_t *lower_bounds; + pcount = mono_array_length (params); + lengths = (uintptr_t *)alloca (sizeof (uintptr_t) * pcount); + /* Note: the synthetized array .ctors have int32 as argument type */ + for (i = 0; i < pcount; ++i) + lengths [i] = *(int32_t*) ((char*)mono_array_get (params, gpointer, i) + sizeof (MonoObject)); + + if (m->klass->rank == 1 && sig->param_count == 2 && m->klass->element_class->rank) { + /* This is a ctor for jagged arrays. MS creates an array of arrays. */ + arr = mono_array_new_full_checked (mono_object_domain (params), m->klass, lengths, NULL, &error); + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + return NULL; + } + + for (i = 0; i < mono_array_length (arr); ++i) { + MonoArray *subarray = mono_array_new_full_checked (mono_object_domain (params), m->klass->element_class, &lengths [1], NULL, &error); + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + return NULL; + } + mono_array_setref_fast (arr, i, subarray); + } + return (MonoObject*)arr; + } + + if (m->klass->rank == pcount) { + /* Only lengths provided. */ + arr = mono_array_new_full_checked (mono_object_domain (params), m->klass, lengths, NULL, &error); + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + return NULL; + } + + return (MonoObject*)arr; + } else { + g_assert (pcount == (m->klass->rank * 2)); + /* The arguments are lower-bound-length pairs */ + lower_bounds = (intptr_t *)g_alloca (sizeof (intptr_t) * pcount); + + for (i = 0; i < pcount / 2; ++i) { + lower_bounds [i] = *(int32_t*) ((char*)mono_array_get (params, gpointer, (i * 2)) + sizeof (MonoObject)); + lengths [i] = *(int32_t*) ((char*)mono_array_get (params, gpointer, (i * 2) + 1) + sizeof (MonoObject)); + } + + arr = mono_array_new_full_checked (mono_object_domain (params), m->klass, lengths, lower_bounds, &error); + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + return NULL; + } + + return (MonoObject*)arr; + } + } + MonoObject *result = mono_runtime_invoke_array_checked (m, obj, params, &error); + mono_error_set_pending_exception (&error); + return result; +} + +#ifndef DISABLE_REMOTING +static void +internal_execute_field_getter (MonoDomain *domain, MonoObject *this_arg, MonoArray *params, MonoArray **outArgs, MonoError *error) +{ + error_init (error); + MonoArray *out_args; + MonoClass *k = mono_object_class (this_arg); + MonoString *name; + char *str; + + /* If this is a proxy, then it must be a CBO */ + if (mono_class_is_transparent_proxy (k)) { + MonoTransparentProxy *tp = (MonoTransparentProxy*) this_arg; + this_arg = tp->rp->unwrapped_server; + g_assert (this_arg); + k = mono_object_class (this_arg); + } + + name = mono_array_get (params, MonoString *, 1); + str = mono_string_to_utf8_checked (name, error); + return_if_nok (error); + + do { + MonoClassField* field = mono_class_get_field_from_name (k, str); + if (field) { + g_free (str); + MonoClass *field_klass = mono_class_from_mono_type (field->type); + MonoObject *result; + if (field_klass->valuetype) { + result = mono_value_box_checked (domain, field_klass, (char *)this_arg + field->offset, error); + return_if_nok (error); + } else + result = (MonoObject *)*((gpointer *)((char *)this_arg + field->offset)); + + out_args = mono_array_new_checked (domain, mono_defaults.object_class, 1, error); + return_if_nok (error); + mono_gc_wbarrier_generic_store (outArgs, (MonoObject*) out_args); + mono_array_setref (out_args, 0, result); + return; + } + k = k->parent; + } while (k); + + g_free (str); + g_assert_not_reached (); +} + +static void +internal_execute_field_setter (MonoDomain *domain, MonoObject *this_arg, MonoArray *params, MonoArray **outArgs, MonoError *error) +{ + error_init (error); + MonoArray *out_args; + MonoClass *k = mono_object_class (this_arg); + MonoString *name; + guint32 size; + gint32 align; + char *str; + + /* If this is a proxy, then it must be a CBO */ + if (mono_class_is_transparent_proxy (k)) { + MonoTransparentProxy *tp = (MonoTransparentProxy*) this_arg; + this_arg = tp->rp->unwrapped_server; + g_assert (this_arg); + k = mono_object_class (this_arg); + } + + name = mono_array_get (params, MonoString *, 1); + str = mono_string_to_utf8_checked (name, error); + return_if_nok (error); + + do { + MonoClassField* field = mono_class_get_field_from_name (k, str); + if (field) { + g_free (str); + MonoClass *field_klass = mono_class_from_mono_type (field->type); + MonoObject *val = (MonoObject *)mono_array_get (params, gpointer, 2); + + if (field_klass->valuetype) { + size = mono_type_size (field->type, &align); + g_assert (size == mono_class_value_size (field_klass, NULL)); + mono_gc_wbarrier_value_copy ((char *)this_arg + field->offset, (char*)val + sizeof (MonoObject), 1, field_klass); + } else { + mono_gc_wbarrier_set_field (this_arg, (char*)this_arg + field->offset, val); + } + + out_args = mono_array_new_checked (domain, mono_defaults.object_class, 0, error); + return_if_nok (error); + mono_gc_wbarrier_generic_store (outArgs, (MonoObject*) out_args); + return; + } + + k = k->parent; + } while (k); + + g_free (str); + g_assert_not_reached (); +} + +ICALL_EXPORT MonoObject * +ves_icall_InternalExecute (MonoReflectionMethod *method, MonoObject *this_arg, MonoArray *params, MonoArray **outArgs) +{ + MonoError error; + MonoDomain *domain = mono_object_domain (method); + MonoMethod *m = method->method; + MonoMethodSignature *sig = mono_method_signature (m); + MonoArray *out_args; + MonoObject *result; + int i, j, outarg_count = 0; + + if (m->klass == mono_defaults.object_class) { + if (!strcmp (m->name, "FieldGetter")) { + internal_execute_field_getter (domain, this_arg, params, outArgs, &error); + mono_error_set_pending_exception (&error); + return NULL; + } else if (!strcmp (m->name, "FieldSetter")) { + internal_execute_field_setter (domain, this_arg, params, outArgs, &error); + mono_error_set_pending_exception (&error); + return NULL; + } + } + + for (i = 0; i < mono_array_length (params); i++) { + if (sig->params [i]->byref) + outarg_count++; + } + + out_args = mono_array_new_checked (domain, mono_defaults.object_class, outarg_count, &error); + if (mono_error_set_pending_exception (&error)) + return NULL; + + /* handle constructors only for objects already allocated */ + if (!strcmp (method->method->name, ".ctor")) + g_assert (this_arg); + + /* This can be called only on MBR objects, so no need to unbox for valuetypes. */ + g_assert (!method->method->klass->valuetype); + result = mono_runtime_invoke_array_checked (method->method, this_arg, params, &error); + if (mono_error_set_pending_exception (&error)) + return NULL; + + for (i = 0, j = 0; i < mono_array_length (params); i++) { + if (sig->params [i]->byref) { + gpointer arg; + arg = mono_array_get (params, gpointer, i); + mono_array_setref (out_args, j, arg); + j++; + } + } + + mono_gc_wbarrier_generic_store (outArgs, (MonoObject*) out_args); + + return result; +} +#endif + +static guint64 +read_enum_value (const char *mem, int type) +{ + switch (type) { + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_U1: + return *(guint8*)mem; + case MONO_TYPE_I1: + return *(gint8*)mem; + case MONO_TYPE_CHAR: + case MONO_TYPE_U2: + return read16 (mem); + case MONO_TYPE_I2: + return (gint16) read16 (mem); + case MONO_TYPE_U4: + return read32 (mem); + case MONO_TYPE_I4: + return (gint32) read32 (mem); + case MONO_TYPE_U8: + case MONO_TYPE_I8: + return read64 (mem); + default: + g_assert_not_reached (); + } + return 0; +} + +static void +write_enum_value (char *mem, int type, guint64 value) +{ + switch (type) { + case MONO_TYPE_U1: + case MONO_TYPE_I1: { + guint8 *p = (guint8*)mem; + *p = value; + break; + } + case MONO_TYPE_U2: + case MONO_TYPE_I2: + case MONO_TYPE_CHAR: { + guint16 *p = (guint16 *)mem; + *p = value; + break; + } + case MONO_TYPE_U4: + case MONO_TYPE_I4: { + guint32 *p = (guint32 *)mem; + *p = value; + break; + } + case MONO_TYPE_U8: + case MONO_TYPE_I8: { + guint64 *p = (guint64 *)mem; + *p = value; + break; + } + default: + g_assert_not_reached (); + } + return; +} + +ICALL_EXPORT MonoObject * +ves_icall_System_Enum_ToObject (MonoReflectionType *enumType, guint64 value) +{ + MonoError error; + MonoDomain *domain; + MonoClass *enumc; + MonoObject *res; + MonoType *etype; + + domain = mono_object_domain (enumType); + enumc = mono_class_from_mono_type (enumType->type); + + mono_class_init_checked (enumc, &error); + if (mono_error_set_pending_exception (&error)) + return NULL; + + etype = mono_class_enum_basetype (enumc); + + res = mono_object_new_checked (domain, enumc, &error); + if (mono_error_set_pending_exception (&error)) + return NULL; + write_enum_value ((char *)res + sizeof (MonoObject), etype->type, value); + + return res; +} + +ICALL_EXPORT MonoBoolean +ves_icall_System_Enum_InternalHasFlag (MonoObject *a, MonoObject *b) +{ + int size = mono_class_value_size (a->vtable->klass, NULL); + guint64 a_val = 0, b_val = 0; + + memcpy (&a_val, mono_object_unbox (a), size); + memcpy (&b_val, mono_object_unbox (b), size); + + return (a_val & b_val) == b_val; +} + +ICALL_EXPORT MonoObject * +ves_icall_System_Enum_get_value (MonoObject *eobj) +{ + MonoError error; + MonoObject *res; + MonoClass *enumc; + gpointer dst; + gpointer src; + int size; + + if (!eobj) + return NULL; + + g_assert (eobj->vtable->klass->enumtype); + + enumc = mono_class_from_mono_type (mono_class_enum_basetype (eobj->vtable->klass)); + res = mono_object_new_checked (mono_object_domain (eobj), enumc, &error); + if (mono_error_set_pending_exception (&error)) + return NULL; + dst = (char *)res + sizeof (MonoObject); + src = (char *)eobj + sizeof (MonoObject); + size = mono_class_value_size (enumc, NULL); + + memcpy (dst, src, size); + + return res; +} + +ICALL_EXPORT MonoReflectionType * +ves_icall_System_Enum_get_underlying_type (MonoReflectionType *type) +{ + MonoError error; + MonoReflectionType *ret; + MonoType *etype; + MonoClass *klass; + + klass = mono_class_from_mono_type (type->type); + mono_class_init_checked (klass, &error); + if (mono_error_set_pending_exception (&error)) + return NULL; + + etype = mono_class_enum_basetype (klass); + if (!etype) { + mono_set_pending_exception (mono_get_exception_argument ("enumType", "Type provided must be an Enum.")); + return NULL; + } + + ret = mono_type_get_object_checked (mono_object_domain (type), etype, &error); + mono_error_set_pending_exception (&error); + + return ret; +} + +ICALL_EXPORT int +ves_icall_System_Enum_compare_value_to (MonoObject *eobj, MonoObject *other) +{ + gpointer tdata = (char *)eobj + sizeof (MonoObject); + gpointer odata = (char *)other + sizeof (MonoObject); + MonoType *basetype = mono_class_enum_basetype (eobj->vtable->klass); + g_assert (basetype); + + if (other == NULL) + return 1; + + if (eobj->vtable->klass != other->vtable->klass) + return 2; + +#define COMPARE_ENUM_VALUES(ENUM_TYPE) do { \ + ENUM_TYPE me = *((ENUM_TYPE*)tdata); \ + ENUM_TYPE other = *((ENUM_TYPE*)odata); \ + if (me == other) \ + return 0; \ + return me > other ? 1 : -1; \ + } while (0) + + switch (basetype->type) { + case MONO_TYPE_U1: + COMPARE_ENUM_VALUES (guint8); + case MONO_TYPE_I1: + COMPARE_ENUM_VALUES (gint8); + case MONO_TYPE_CHAR: + case MONO_TYPE_U2: + COMPARE_ENUM_VALUES (guint16); + case MONO_TYPE_I2: + COMPARE_ENUM_VALUES (gint16); + case MONO_TYPE_U4: + COMPARE_ENUM_VALUES (guint32); + case MONO_TYPE_I4: + COMPARE_ENUM_VALUES (gint32); + case MONO_TYPE_U8: + COMPARE_ENUM_VALUES (guint64); + case MONO_TYPE_I8: + COMPARE_ENUM_VALUES (gint64); + default: + break; + } +#undef COMPARE_ENUM_VALUES + /* indicates that the enum was of an unsupported unerlying type */ + return 3; +} + +ICALL_EXPORT int +ves_icall_System_Enum_get_hashcode (MonoObject *eobj) +{ + gpointer data = (char *)eobj + sizeof (MonoObject); + MonoType *basetype = mono_class_enum_basetype (eobj->vtable->klass); + g_assert (basetype); + + switch (basetype->type) { + case MONO_TYPE_I1: { + gint8 value = *((gint8*)data); + return ((int)value ^ (int)value << 8); + } + case MONO_TYPE_U1: + return *((guint8*)data); + case MONO_TYPE_CHAR: + case MONO_TYPE_U2: + return *((guint16*)data); + + case MONO_TYPE_I2: { + gint16 value = *((gint16*)data); + return ((int)(guint16)value | (((int)value) << 16)); + } + case MONO_TYPE_U4: + return *((guint32*)data); + case MONO_TYPE_I4: + return *((gint32*)data); + case MONO_TYPE_U8: + case MONO_TYPE_I8: { + gint64 value = *((gint64*)data); + return (gint)(value & 0xffffffff) ^ (int)(value >> 32); + } + default: + g_error ("Implement type 0x%02x in get_hashcode", basetype->type); + } + return 0; +} + +static void +get_enum_field (MonoDomain *domain, MonoArrayHandle names, MonoArrayHandle values, int base_type, MonoClassField *field, guint* j, guint64 *previous_value, gboolean *sorted, MonoError *error) +{ + error_init (error); + HANDLE_FUNCTION_ENTER(); + guint64 field_value; + const char *p; + MonoTypeEnum def_type; + + if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC)) + goto leave; + if (strcmp ("value__", mono_field_get_name (field)) == 0) + goto leave; + if (mono_field_is_deleted (field)) + goto leave; + MonoStringHandle name = mono_string_new_handle (domain, mono_field_get_name (field), error); + goto_if_nok (error, leave); + MONO_HANDLE_ARRAY_SETREF (names, *j, name); + + p = mono_class_get_field_default_value (field, &def_type); + /* len = */ mono_metadata_decode_blob_size (p, &p); + + field_value = read_enum_value (p, base_type); + MONO_HANDLE_ARRAY_SETVAL (values, guint64, *j, field_value); + + if (*previous_value > field_value) + *sorted = FALSE; + + *previous_value = field_value; + (*j)++; +leave: + HANDLE_FUNCTION_RETURN(); +} + +ICALL_EXPORT MonoBoolean +ves_icall_System_Enum_GetEnumValuesAndNames (MonoReflectionTypeHandle type, MonoArrayHandleOut values, MonoArrayHandleOut names, MonoError *error) +{ + MonoDomain *domain = MONO_HANDLE_DOMAIN (type); + MonoClass *enumc = mono_class_from_mono_type (MONO_HANDLE_RAW(type)->type); + guint j = 0, nvalues; + gpointer iter; + MonoClassField *field; + int base_type; + guint64 previous_value = 0; + gboolean sorted = TRUE; + + error_init (error); + mono_class_init_checked (enumc, error); + return_val_if_nok (error, FALSE); + + if (!enumc->enumtype) { + mono_error_set_argument (error, "enumType", "Type provided must be an Enum."); + return TRUE; + } + + base_type = mono_class_enum_basetype (enumc)->type; + + nvalues = mono_class_num_fields (enumc) > 0 ? mono_class_num_fields (enumc) - 1 : 0; + MONO_HANDLE_ASSIGN(names, mono_array_new_handle (domain, mono_defaults.string_class, nvalues, error)); + return_val_if_nok (error, FALSE); + MONO_HANDLE_ASSIGN(values, mono_array_new_handle (domain, mono_defaults.uint64_class, nvalues, error)); + return_val_if_nok (error, FALSE); + + iter = NULL; + while ((field = mono_class_get_fields (enumc, &iter))) { + get_enum_field(domain, names, values, base_type, field, &j, &previous_value, &sorted, error); + if (!is_ok (error)) + break; + } + return_val_if_nok (error, FALSE); + + return sorted; +} + +enum { + BFLAGS_IgnoreCase = 1, + BFLAGS_DeclaredOnly = 2, + BFLAGS_Instance = 4, + BFLAGS_Static = 8, + BFLAGS_Public = 0x10, + BFLAGS_NonPublic = 0x20, + BFLAGS_FlattenHierarchy = 0x40, + BFLAGS_InvokeMethod = 0x100, + BFLAGS_CreateInstance = 0x200, + BFLAGS_GetField = 0x400, + BFLAGS_SetField = 0x800, + BFLAGS_GetProperty = 0x1000, + BFLAGS_SetProperty = 0x2000, + BFLAGS_ExactBinding = 0x10000, + BFLAGS_SuppressChangeType = 0x20000, + BFLAGS_OptionalParamBinding = 0x40000 +}; + +ICALL_EXPORT GPtrArray* +ves_icall_RuntimeType_GetFields_native (MonoReflectionTypeHandle ref_type, char *utf8_name, guint32 bflags, MonoError *error) +{ + error_init (error); + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + + if (type->byref) { + return g_ptr_array_new (); + } + + int (*compare_func) (const char *s1, const char *s2) = NULL; + compare_func = (bflags & BFLAGS_IgnoreCase) ? mono_utf8_strcasecmp : strcmp; + + MonoClass *startklass, *klass; + klass = startklass = mono_class_from_mono_type (type); + + GPtrArray *ptr_array = g_ptr_array_sized_new (16); + +handle_parent: + if (mono_class_has_failure (klass)) { + mono_error_set_for_class_failure (error, klass); + goto fail; + } + + MonoClassField *field; + gpointer iter = NULL; + while ((field = mono_class_get_fields_lazy (klass, &iter))) { + guint32 flags = mono_field_get_flags (field); + int match = 0; + if (mono_field_is_deleted_with_flags (field, flags)) + continue; + if ((flags & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK) == FIELD_ATTRIBUTE_PUBLIC) { + if (bflags & BFLAGS_Public) + match++; + } else if ((klass == startklass) || (flags & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK) != FIELD_ATTRIBUTE_PRIVATE) { + if (bflags & BFLAGS_NonPublic) { + match++; + } + } + if (!match) + continue; + match = 0; + if (flags & FIELD_ATTRIBUTE_STATIC) { + if (bflags & BFLAGS_Static) + if ((bflags & BFLAGS_FlattenHierarchy) || (klass == startklass)) + match++; + } else { + if (bflags & BFLAGS_Instance) + match++; + } + + if (!match) + continue; + + if (utf8_name != NULL && compare_func (mono_field_get_name (field), utf8_name)) + continue; + + g_ptr_array_add (ptr_array, field); + } + if (!(bflags & BFLAGS_DeclaredOnly) && (klass = klass->parent)) + goto handle_parent; + + return ptr_array; + +fail: + g_ptr_array_free (ptr_array, TRUE); + return NULL; +} + +static gboolean +method_nonpublic (MonoMethod* method, gboolean start_klass) +{ + switch (method->flags & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK) { + case METHOD_ATTRIBUTE_ASSEM: + return (start_klass || mono_defaults.generic_ilist_class); + case METHOD_ATTRIBUTE_PRIVATE: + return start_klass; + case METHOD_ATTRIBUTE_PUBLIC: + return FALSE; + default: + return TRUE; + } +} + +GPtrArray* +mono_class_get_methods_by_name (MonoClass *klass, const char *name, guint32 bflags, gboolean ignore_case, gboolean allow_ctors, MonoError *error) +{ + GPtrArray *array; + MonoClass *startklass; + MonoMethod *method; + gpointer iter; + int match, nslots; + /*FIXME, use MonoBitSet*/ + guint32 method_slots_default [8]; + guint32 *method_slots = NULL; + int (*compare_func) (const char *s1, const char *s2) = NULL; + + array = g_ptr_array_new (); + startklass = klass; + error_init (error); + + if (name != NULL) + compare_func = (ignore_case) ? mono_utf8_strcasecmp : strcmp; + + /* An optimization for calls made from Delegate:CreateDelegate () */ + 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); + + g_ptr_array_add (array, method); + return array; + } + + mono_class_setup_methods (klass); + mono_class_setup_vtable (klass); + if (mono_class_has_failure (klass)) + goto loader_error; + + if (is_generic_parameter (&klass->byval_arg)) + nslots = mono_class_get_vtable_size (klass->parent); + else + nslots = MONO_CLASS_IS_INTERFACE (klass) ? mono_class_num_methods (klass) : mono_class_get_vtable_size (klass); + if (nslots >= sizeof (method_slots_default) * 8) { + method_slots = g_new0 (guint32, nslots / 32 + 1); + } else { + method_slots = method_slots_default; + memset (method_slots, 0, sizeof (method_slots_default)); + } +handle_parent: + mono_class_setup_methods (klass); + mono_class_setup_vtable (klass); + if (mono_class_has_failure (klass)) + goto loader_error; + + iter = NULL; + while ((method = mono_class_get_methods (klass, &iter))) { + match = 0; + if (method->slot != -1) { + g_assert (method->slot < nslots); + if (method_slots [method->slot >> 5] & (1 << (method->slot & 0x1f))) + continue; + if (!(method->flags & METHOD_ATTRIBUTE_NEW_SLOT)) + method_slots [method->slot >> 5] |= 1 << (method->slot & 0x1f); + } + + if (!allow_ctors && method->name [0] == '.' && (strcmp (method->name, ".ctor") == 0 || strcmp (method->name, ".cctor") == 0)) + continue; + if ((method->flags & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK) == METHOD_ATTRIBUTE_PUBLIC) { + if (bflags & BFLAGS_Public) + match++; + } else if ((bflags & BFLAGS_NonPublic) && method_nonpublic (method, (klass == startklass))) { + match++; + } + if (!match) + continue; + match = 0; + if (method->flags & METHOD_ATTRIBUTE_STATIC) { + if (bflags & BFLAGS_Static) + if ((bflags & BFLAGS_FlattenHierarchy) || (klass == startklass)) + match++; + } else { + if (bflags & BFLAGS_Instance) + match++; + } + + if (!match) + continue; + + if (name != NULL) { + if (compare_func (name, method->name)) + continue; + } + + match = 0; + g_ptr_array_add (array, method); + } + if (!(bflags & BFLAGS_DeclaredOnly) && (klass = klass->parent)) + goto handle_parent; + if (method_slots != method_slots_default) + g_free (method_slots); + + return array; + +loader_error: + if (method_slots != method_slots_default) + g_free (method_slots); + g_ptr_array_free (array, TRUE); + + g_assert (mono_class_has_failure (klass)); + mono_error_set_for_class_failure (error, klass); + return NULL; +} + +ICALL_EXPORT GPtrArray* +ves_icall_RuntimeType_GetMethodsByName_native (MonoReflectionTypeHandle ref_type, const char *mname, guint32 bflags, MonoBoolean ignore_case, MonoError *error) +{ + error_init (error); + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + + MonoClass *klass = mono_class_from_mono_type (type); + if (type->byref) { + return g_ptr_array_new (); + } + + return mono_class_get_methods_by_name (klass, mname, bflags, ignore_case, FALSE, error); +} + +ICALL_EXPORT GPtrArray* +ves_icall_RuntimeType_GetConstructors_native (MonoReflectionTypeHandle ref_type, guint32 bflags, MonoError *error) +{ + error_init (error); + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + if (type->byref) { + return g_ptr_array_new (); + } + + MonoClass *startklass, *klass; + klass = startklass = mono_class_from_mono_type (type); + + mono_class_setup_methods (klass); + if (mono_class_has_failure (klass)) { + mono_error_set_for_class_failure (error, klass); + return NULL; + } + + + GPtrArray *res_array = g_ptr_array_sized_new (4); /* FIXME, guestimating */ + + MonoMethod *method; + gpointer iter = NULL; + while ((method = mono_class_get_methods (klass, &iter))) { + int match = 0; + if (strcmp (method->name, ".ctor") && strcmp (method->name, ".cctor")) + continue; + if ((method->flags & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK) == METHOD_ATTRIBUTE_PUBLIC) { + if (bflags & BFLAGS_Public) + match++; + } else { + if (bflags & BFLAGS_NonPublic) + match++; + } + if (!match) + continue; + match = 0; + if (method->flags & METHOD_ATTRIBUTE_STATIC) { + if (bflags & BFLAGS_Static) + if ((bflags & BFLAGS_FlattenHierarchy) || (klass == startklass)) + match++; + } else { + if (bflags & BFLAGS_Instance) + match++; + } + + if (!match) + continue; + g_ptr_array_add (res_array, method); + } + + return res_array; +} + +static guint +property_hash (gconstpointer data) +{ + MonoProperty *prop = (MonoProperty*)data; + + return g_str_hash (prop->name); +} + +static gboolean +property_accessor_override (MonoMethod *method1, MonoMethod *method2) +{ + if (method1->slot != -1 && method1->slot == method2->slot) + return TRUE; + + if (mono_class_get_generic_type_definition (method1->klass) == mono_class_get_generic_type_definition (method2->klass)) { + if (method1->is_inflated) + method1 = ((MonoMethodInflated*) method1)->declaring; + if (method2->is_inflated) + method2 = ((MonoMethodInflated*) method2)->declaring; + } + + return mono_metadata_signature_equal (mono_method_signature (method1), mono_method_signature (method2)); +} + +static gboolean +property_equal (MonoProperty *prop1, MonoProperty *prop2) +{ + // Properties are hide-by-name-and-signature + if (!g_str_equal (prop1->name, prop2->name)) + return FALSE; + + /* If we see a property in a generic method, we want to + compare the generic signatures, not the inflated signatures + because we might conflate two properties that were + distinct: + + class Foo { + public T this[T t] { getter { return t; } } // method 1 + public U this[U u] { getter { return u; } } // method 2 + } + + If we see int Foo::Item[int] we need to know if + the indexer came from method 1 or from method 2, and we + shouldn't conflate them. (Bugzilla 36283) + */ + if (prop1->get && prop2->get && !property_accessor_override (prop1->get, prop2->get)) + return FALSE; + + if (prop1->set && prop2->set && !property_accessor_override (prop1->set, prop2->set)) + return FALSE; + + return TRUE; +} + +static gboolean +property_accessor_nonpublic (MonoMethod* accessor, gboolean start_klass) +{ + if (!accessor) + return FALSE; + + return method_nonpublic (accessor, start_klass); +} + +ICALL_EXPORT GPtrArray* +ves_icall_RuntimeType_GetPropertiesByName_native (MonoReflectionTypeHandle ref_type, gchar *propname, guint32 bflags, MonoBoolean ignore_case, MonoError *error) +{ + error_init (error); + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + + + if (type->byref) { + return g_ptr_array_new (); + } + + + MonoClass *startklass, *klass; + klass = startklass = mono_class_from_mono_type (type); + + int (*compare_func) (const char *s1, const char *s2) = (ignore_case) ? mono_utf8_strcasecmp : strcmp; + + GPtrArray *res_array = g_ptr_array_sized_new (8); /*This the average for ASP.NET types*/ + + GHashTable *properties = g_hash_table_new (property_hash, (GEqualFunc)property_equal); + +handle_parent: + mono_class_setup_methods (klass); + mono_class_setup_vtable (klass); + if (mono_class_has_failure (klass)) { + mono_error_set_for_class_failure (error, klass); + goto loader_error; + } + + MonoProperty *prop; + gpointer iter = NULL; + while ((prop = mono_class_get_properties (klass, &iter))) { + int match = 0; + MonoMethod *method = prop->get; + if (!method) + method = prop->set; + guint32 flags = 0; + if (method) + flags = method->flags; + if ((prop->get && ((prop->get->flags & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK) == METHOD_ATTRIBUTE_PUBLIC)) || + (prop->set && ((prop->set->flags & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK) == METHOD_ATTRIBUTE_PUBLIC))) { + if (bflags & BFLAGS_Public) + match++; + } else if (bflags & BFLAGS_NonPublic) { + if (property_accessor_nonpublic(prop->get, startklass == klass) || + property_accessor_nonpublic(prop->set, startklass == klass)) { + match++; + } + } + if (!match) + continue; + match = 0; + if (flags & METHOD_ATTRIBUTE_STATIC) { + if (bflags & BFLAGS_Static) + if ((bflags & BFLAGS_FlattenHierarchy) || (klass == startklass)) + match++; + } else { + if (bflags & BFLAGS_Instance) + match++; + } + + if (!match) + continue; + match = 0; + + if (propname != NULL && compare_func (propname, prop->name)) + continue; + + if (g_hash_table_lookup (properties, prop)) + continue; + + g_ptr_array_add (res_array, prop); + + g_hash_table_insert (properties, prop, prop); + } + if (!(bflags & BFLAGS_DeclaredOnly) && (klass = klass->parent)) + goto handle_parent; + + g_hash_table_destroy (properties); + + return res_array; + + +loader_error: + if (properties) + g_hash_table_destroy (properties); + g_ptr_array_free (res_array, TRUE); + + return NULL; +} + +static guint +event_hash (gconstpointer data) +{ + MonoEvent *event = (MonoEvent*)data; + + return g_str_hash (event->name); +} + +static gboolean +event_equal (MonoEvent *event1, MonoEvent *event2) +{ + // Events are hide-by-name + return g_str_equal (event1->name, event2->name); +} + +ICALL_EXPORT GPtrArray* +ves_icall_RuntimeType_GetEvents_native (MonoReflectionTypeHandle ref_type, char *utf8_name, guint32 bflags, MonoError *error) +{ + error_init (error); + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + + if (type->byref) { + return g_ptr_array_new (); + } + + int (*compare_func) (const char *s1, const char *s2) = (bflags & BFLAGS_IgnoreCase) ? mono_utf8_strcasecmp : strcmp; + + GPtrArray *res_array = g_ptr_array_sized_new (4); + + MonoClass *startklass, *klass; + klass = startklass = mono_class_from_mono_type (type); + + GHashTable *events = g_hash_table_new (event_hash, (GEqualFunc)event_equal); +handle_parent: + mono_class_setup_methods (klass); + mono_class_setup_vtable (klass); + if (mono_class_has_failure (klass)) { + mono_error_set_for_class_failure (error, klass); + goto failure; + } + + MonoEvent *event; + gpointer iter = NULL; + while ((event = mono_class_get_events (klass, &iter))) { + int match = 0; + MonoMethod *method = event->add; + if (!method) + method = event->remove; + if (!method) + method = event->raise; + if (method) { + if ((method->flags & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK) == METHOD_ATTRIBUTE_PUBLIC) { + if (bflags & BFLAGS_Public) + match++; + } else if ((klass == startklass) || (method->flags & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK) != METHOD_ATTRIBUTE_PRIVATE) { + if (bflags & BFLAGS_NonPublic) + match++; + } + } + else + if (bflags & BFLAGS_NonPublic) + match ++; + if (!match) + continue; + match = 0; + if (method) { + if (method->flags & METHOD_ATTRIBUTE_STATIC) { + if (bflags & BFLAGS_Static) + if ((bflags & BFLAGS_FlattenHierarchy) || (klass == startklass)) + match++; + } else { + if (bflags & BFLAGS_Instance) + match++; + } + } + else + if (bflags & BFLAGS_Instance) + match ++; + if (!match) + continue; + + if (utf8_name != NULL && compare_func (event->name, utf8_name)) + continue; + + if (g_hash_table_lookup (events, event)) + continue; + + g_ptr_array_add (res_array, event); + + g_hash_table_insert (events, event, event); + } + if (!(bflags & BFLAGS_DeclaredOnly) && (klass = klass->parent)) + goto handle_parent; + + g_hash_table_destroy (events); + + return res_array; + +failure: + if (events != NULL) + g_hash_table_destroy (events); + + g_ptr_array_free (res_array, TRUE); + + return NULL; +} + +ICALL_EXPORT GPtrArray * +ves_icall_RuntimeType_GetNestedTypes_native (MonoReflectionTypeHandle ref_type, char *str, guint32 bflags, MonoError *error) +{ + error_init (error); + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + + if (type->byref) { + return g_ptr_array_new (); + } + + MonoClass *klass = mono_class_from_mono_type (type); + + /* + * If a nested type is generic, return its generic type definition. + * Note that this means that the return value is essentially the set + * of nested types of the generic type definition of @klass. + * + * A note in MSDN claims that a generic type definition can have + * nested types that aren't generic. In any case, the container of that + * nested type would be the generic type definition. + */ + if (mono_class_is_ginst (klass)) + klass = mono_class_get_generic_class (klass)->container_class; + + GPtrArray *res_array = g_ptr_array_new (); + + MonoClass *nested; + gpointer iter = NULL; + while ((nested = mono_class_get_nested_types (klass, &iter))) { + int match = 0; + if ((mono_class_get_flags (nested) & TYPE_ATTRIBUTE_VISIBILITY_MASK) == TYPE_ATTRIBUTE_NESTED_PUBLIC) { + if (bflags & BFLAGS_Public) + match++; + } else { + if (bflags & BFLAGS_NonPublic) + match++; + } + if (!match) + continue; + + if (str != NULL && strcmp (nested->name, str)) + continue; + + g_ptr_array_add (res_array, &nested->byval_arg); + } + + return res_array; +} + +static MonoType* +get_type_from_module_builder_module (MonoArrayHandle modules, int i, MonoTypeNameParse *info, MonoBoolean ignoreCase, gboolean *type_resolve, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + error_init (error); + MonoType *type = NULL; + MonoReflectionModuleBuilderHandle mb = MONO_HANDLE_NEW (MonoReflectionModuleBuilder, NULL); + MONO_HANDLE_ARRAY_GETREF (mb, modules, i); + MonoDynamicImage *dynamic_image = MONO_HANDLE_GETVAL (mb, dynamic_image); + type = mono_reflection_get_type_checked (&dynamic_image->image, &dynamic_image->image, info, ignoreCase, type_resolve, error); + HANDLE_FUNCTION_RETURN_VAL (type); +} + +static MonoType* +get_type_from_module_builder_loaded_modules (MonoArrayHandle loaded_modules, int i, MonoTypeNameParse *info, MonoBoolean ignoreCase, gboolean *type_resolve, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + error_init (error); + MonoType *type = NULL; + MonoReflectionModuleHandle mod = MONO_HANDLE_NEW (MonoReflectionModule, NULL); + MONO_HANDLE_ARRAY_GETREF (mod, loaded_modules, i); + MonoImage *image = MONO_HANDLE_GETVAL (mod, image); + type = mono_reflection_get_type_checked (image, image, info, ignoreCase, type_resolve, error); + HANDLE_FUNCTION_RETURN_VAL (type); +} + +ICALL_EXPORT MonoReflectionTypeHandle +ves_icall_System_Reflection_Assembly_InternalGetType (MonoReflectionAssemblyHandle assembly_h, MonoReflectionModuleHandle module, MonoStringHandle name, MonoBoolean throwOnError, MonoBoolean ignoreCase, MonoError *error) +{ + error_init (error); + + MonoTypeNameParse info; + gboolean type_resolve; + + /* On MS.NET, this does not fire a TypeResolve event */ + type_resolve = TRUE; + char *str = mono_string_handle_to_utf8 (name, error); + goto_if_nok (error, fail); + + /*g_print ("requested type %s in %s\n", str, assembly->assembly->aname.name);*/ + MonoError parse_error; + if (!mono_reflection_parse_type_checked (str, &info, &parse_error)) { + g_free (str); + mono_reflection_free_type_info (&info); + mono_error_cleanup (&parse_error); + if (throwOnError) { + mono_error_set_argument (error, "name", "failed to parse the type"); + goto fail; + } + /*g_print ("failed parse\n");*/ + return MONO_HANDLE_CAST (MonoReflectionType, NULL_HANDLE); + } + + if (info.assembly.name) { + g_free (str); + mono_reflection_free_type_info (&info); + if (throwOnError) { + /* 1.0 and 2.0 throw different exceptions */ + if (mono_defaults.generic_ilist_class) + mono_error_set_argument (error, NULL, "Type names passed to Assembly.GetType() must not specify an assembly."); + else + mono_error_set_type_load_name (error, g_strdup (""), g_strdup (""), "Type names passed to Assembly.GetType() must not specify an assembly."); + goto fail; + } + return MONO_HANDLE_CAST (MonoReflectionType, NULL_HANDLE); + } + + MonoType *type = NULL; + if (!MONO_HANDLE_IS_NULL (module)) { + MonoImage *image = MONO_HANDLE_GETVAL (module, image); + if (image) { + type = mono_reflection_get_type_checked (image, image, &info, ignoreCase, &type_resolve, error); + if (!is_ok (error)) { + g_free (str); + mono_reflection_free_type_info (&info); + goto fail; + } + } + } + else { + MonoAssembly *assembly = MONO_HANDLE_GETVAL (assembly_h, assembly); + if (assembly_is_dynamic (assembly)) { + /* Enumerate all modules */ + MonoReflectionAssemblyBuilderHandle abuilder = MONO_HANDLE_NEW (MonoReflectionAssemblyBuilder, NULL); + MONO_HANDLE_ASSIGN (abuilder, assembly_h); + int i; + + MonoArrayHandle modules = MONO_HANDLE_NEW (MonoArray, NULL); + MONO_HANDLE_GET (modules, abuilder, modules); + if (!MONO_HANDLE_IS_NULL (modules)) { + int n = mono_array_handle_length (modules); + for (i = 0; i < n; ++i) { + type = get_type_from_module_builder_module (modules, i, &info, ignoreCase, &type_resolve, error); + if (!is_ok (error)) { + g_free (str); + mono_reflection_free_type_info (&info); + goto fail; + } + if (type) + break; + } + } + + MonoArrayHandle loaded_modules = MONO_HANDLE_NEW (MonoArray, NULL); + MONO_HANDLE_GET (loaded_modules, abuilder, loaded_modules); + if (!type && !MONO_HANDLE_IS_NULL (loaded_modules)) { + int n = mono_array_handle_length (loaded_modules); + for (i = 0; i < n; ++i) { + type = get_type_from_module_builder_loaded_modules (loaded_modules, i, &info, ignoreCase, &type_resolve, error); + + if (!is_ok (error)) { + g_free (str); + mono_reflection_free_type_info (&info); + goto fail; + } + if (type) + break; + } + } + } + else { + type = mono_reflection_get_type_checked (assembly->image, assembly->image, &info, ignoreCase, &type_resolve, error); + if (!is_ok (error)) { + g_free (str); + mono_reflection_free_type_info (&info); + goto fail; + } + } + } + g_free (str); + mono_reflection_free_type_info (&info); + + if (!type) { + if (throwOnError) { + MonoError inner_error; + char *typename = mono_string_handle_to_utf8 (name, &inner_error); + mono_error_assert_ok (&inner_error); + MonoAssembly *assembly = MONO_HANDLE_GETVAL (assembly_h, assembly); + char *assmname = mono_stringify_assembly_name (&assembly->aname); + mono_error_set_type_load_name (error, typename, assmname, "%s", ""); + goto fail; + } + + return MONO_HANDLE_CAST (MonoReflectionType, NULL_HANDLE); + } + + if (type->type == MONO_TYPE_CLASS) { + MonoClass *klass = mono_type_get_class (type); + + /* need to report exceptions ? */ + if (throwOnError && mono_class_has_failure (klass)) { + /* report SecurityException (or others) that occured when loading the assembly */ + mono_error_set_for_class_failure (error, klass); + goto fail; + } + } + + /* g_print ("got it\n"); */ + return mono_type_get_object_handle (MONO_HANDLE_DOMAIN (assembly_h), type, error); +fail: + g_assert (!is_ok (error)); + return MONO_HANDLE_CAST (MonoReflectionType, NULL_HANDLE); +} + +static gboolean +replace_shadow_path (MonoDomain *domain, gchar *dirname, gchar **filename) +{ + gchar *content; + gchar *shadow_ini_file; + gsize len; + + /* Check for shadow-copied assembly */ + if (mono_is_shadow_copy_enabled (domain, dirname)) { + shadow_ini_file = g_build_filename (dirname, "__AssemblyInfo__.ini", NULL); + content = NULL; + if (!g_file_get_contents (shadow_ini_file, &content, &len, NULL) || + !g_file_test (content, G_FILE_TEST_IS_REGULAR)) { + if (content) { + g_free (content); + content = NULL; + } + } + g_free (shadow_ini_file); + if (content != NULL) { + if (*filename) + g_free (*filename); + *filename = content; + return TRUE; + } + } + return FALSE; +} + +ICALL_EXPORT MonoStringHandle +ves_icall_System_Reflection_Assembly_get_code_base (MonoReflectionAssemblyHandle assembly, MonoBoolean escaped, MonoError *error) +{ + error_init (error); + MonoDomain *domain = MONO_HANDLE_DOMAIN (assembly); + MonoAssembly *mass = MONO_HANDLE_GETVAL (assembly, assembly); + gchar *absolute; + gchar *dirname; + + if (g_path_is_absolute (mass->image->name)) { + absolute = g_strdup (mass->image->name); + dirname = g_path_get_dirname (absolute); + } else { + absolute = g_build_filename (mass->basedir, mass->image->name, NULL); + dirname = g_strdup (mass->basedir); + } + + replace_shadow_path (domain, dirname, &absolute); + g_free (dirname); + + mono_icall_make_platform_path (absolute); + + gchar *uri; + if (escaped) { + uri = g_filename_to_uri (absolute, NULL, NULL); + } else { + const gchar *prepend = mono_icall_get_file_path_prefix (absolute); + uri = g_strconcat (prepend, absolute, NULL); + } + + g_free (absolute); + + MonoStringHandle res; + if (uri) { + res = mono_string_new_handle (domain, uri, error); + g_free (uri); + } else { + res = MONO_HANDLE_NEW (MonoString, NULL); + } + return res; +} + +ICALL_EXPORT MonoBoolean +ves_icall_System_Reflection_Assembly_get_global_assembly_cache (MonoReflectionAssemblyHandle assembly, MonoError *error) +{ + error_init (error); + MonoAssembly *mass = MONO_HANDLE_GETVAL (assembly,assembly); + + return mass->in_gac; +} + +ICALL_EXPORT MonoReflectionAssemblyHandle +ves_icall_System_Reflection_Assembly_load_with_partial_name (MonoStringHandle mname, MonoObjectHandle evidence, MonoError *error) +{ + gchar *name; + MonoImageOpenStatus status; + MonoReflectionAssemblyHandle result = MONO_HANDLE_CAST (MonoReflectionAssembly, NULL_HANDLE); + + name = mono_string_handle_to_utf8 (mname, error); + goto_if_nok (error, leave); + MonoAssembly *res = mono_assembly_load_with_partial_name (name, &status); + + g_free (name); + + if (res == NULL) + goto leave; + result = mono_assembly_get_object_handle (mono_domain_get (), res, error); +leave: + return result; +} + +ICALL_EXPORT MonoStringHandle +ves_icall_System_Reflection_Assembly_get_location (MonoReflectionAssemblyHandle refassembly, MonoError *error) +{ + MonoDomain *domain = MONO_HANDLE_DOMAIN (refassembly); + MonoAssembly *assembly = MONO_HANDLE_GETVAL (refassembly, assembly); + return mono_string_new_handle (domain, mono_image_get_filename (assembly->image), error); +} + +ICALL_EXPORT MonoBoolean +ves_icall_System_Reflection_Assembly_get_ReflectionOnly (MonoReflectionAssemblyHandle assembly_h, MonoError *error) +{ + error_init (error); + MonoAssembly *assembly = MONO_HANDLE_GETVAL (assembly_h, assembly); + return assembly->ref_only; +} + +ICALL_EXPORT MonoStringHandle +ves_icall_System_Reflection_Assembly_InternalImageRuntimeVersion (MonoReflectionAssemblyHandle refassembly, MonoError *error) +{ + MonoDomain *domain = MONO_HANDLE_DOMAIN (refassembly); + MonoAssembly *assembly = MONO_HANDLE_GETVAL (refassembly, assembly); + + return mono_string_new_handle (domain, assembly->image->version, error); +} + +ICALL_EXPORT MonoReflectionMethodHandle +ves_icall_System_Reflection_Assembly_get_EntryPoint (MonoReflectionAssemblyHandle assembly_h, MonoError *error) +{ + error_init (error); + MonoDomain *domain = MONO_HANDLE_DOMAIN (assembly_h); + MonoAssembly *assembly = MONO_HANDLE_GETVAL (assembly_h, assembly); + MonoMethod *method; + + MonoReflectionMethodHandle res = MONO_HANDLE_NEW (MonoReflectionMethod, NULL); + guint32 token = mono_image_get_entry_point (assembly->image); + + if (!token) + goto leave; + method = mono_get_method_checked (assembly->image, token, NULL, NULL, error); + goto_if_nok (error, leave); + + MONO_HANDLE_ASSIGN (res, mono_method_get_object_handle (domain, method, NULL, error)); +leave: + return res; +} + +ICALL_EXPORT MonoReflectionModuleHandle +ves_icall_System_Reflection_Assembly_GetManifestModuleInternal (MonoReflectionAssemblyHandle assembly, MonoError *error) +{ + error_init (error); + MonoDomain *domain = MONO_HANDLE_DOMAIN (assembly); + MonoAssembly *a = MONO_HANDLE_GETVAL (assembly, assembly); + return mono_module_get_object_handle (domain, a->image, error); +} + +static gboolean +add_manifest_resource_name_to_array (MonoDomain *domain, MonoImage *image, MonoTableInfo *table, int i, MonoArrayHandle dest, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + error_init (error); + const char *val = mono_metadata_string_heap (image, mono_metadata_decode_row_col (table, i, MONO_MANIFEST_NAME)); + MonoStringHandle str = mono_string_new_handle (domain, val, error); + goto_if_nok (error, leave); + MONO_HANDLE_ARRAY_SETREF (dest, i, str); +leave: + HANDLE_FUNCTION_RETURN_VAL (is_ok (error)); +} + +ICALL_EXPORT MonoArrayHandle +ves_icall_System_Reflection_Assembly_GetManifestResourceNames (MonoReflectionAssemblyHandle assembly_h, MonoError *error) +{ + error_init (error); + MonoDomain *domain = MONO_HANDLE_DOMAIN (assembly_h); + MonoAssembly *assembly = MONO_HANDLE_GETVAL (assembly_h, assembly); + MonoTableInfo *table = &assembly->image->tables [MONO_TABLE_MANIFESTRESOURCE]; + MonoArrayHandle result = mono_array_new_handle (domain, mono_defaults.string_class, table->rows, error); + goto_if_nok (error, fail); + int i; + + for (i = 0; i < table->rows; ++i) { + if (!add_manifest_resource_name_to_array (domain, assembly->image, table, i, result, error)) + goto fail; + } + return result; +fail: + return MONO_HANDLE_CAST (MonoArray, NULL_HANDLE); +} + +ICALL_EXPORT MonoStringHandle +ves_icall_System_Reflection_Assembly_GetAotId (MonoError *error) +{ + char *guid = mono_runtime_get_aotid (); + if (guid == NULL) + return NULL; + MonoStringHandle res = mono_string_new_handle (mono_domain_get (), guid, error); + g_free (guid); + return res; +} + +static MonoAssemblyName* +create_referenced_assembly_name (MonoDomain *domain, MonoImage *image, MonoTableInfo *t, int i, MonoError *error) +{ + error_init (error); + MonoAssemblyName *aname = g_new0 (MonoAssemblyName, 1); + + mono_assembly_get_assemblyref (image, i, aname); + aname->hash_alg = ASSEMBLY_HASH_SHA1 /* SHA1 (default) */; + /* name and culture are pointers into the image tables, but we need + * real malloc'd strings (so that we can g_free() them later from + * Mono.RuntimeMarshal.FreeAssemblyName) */ + aname->name = g_strdup (aname->name); + aname->culture = g_strdup (aname->culture); + /* Don't need the hash value in managed */ + aname->hash_value = NULL; + aname->hash_len = 0; + g_assert (aname->public_key == NULL); + + /* note: this function doesn't return the codebase on purpose (i.e. it can + be used under partial trust as path information isn't present). */ + return aname; +} + +ICALL_EXPORT GPtrArray* +ves_icall_System_Reflection_Assembly_InternalGetReferencedAssemblies (MonoReflectionAssemblyHandle assembly, MonoError *error) +{ + error_init (error); + MonoDomain *domain = MONO_HANDLE_DOMAIN (assembly); + MonoAssembly *ass = MONO_HANDLE_GETVAL(assembly, assembly); + MonoImage *image = ass->image; + + MonoTableInfo *t = &image->tables [MONO_TABLE_ASSEMBLYREF]; + int count = t->rows; + + GPtrArray *result = g_ptr_array_sized_new (count); + + for (int i = 0; i < count; i++) { + MonoAssemblyName *aname = create_referenced_assembly_name (domain, image, t, i, error); + if (!is_ok (error)) + break; + g_ptr_array_add (result, aname); + } + return result; +} + +/* move this in some file in mono/util/ */ +static char * +g_concat_dir_and_file (const char *dir, const char *file) +{ + g_return_val_if_fail (dir != NULL, NULL); + g_return_val_if_fail (file != NULL, NULL); + + /* + * If the directory name doesn't have a / on the end, we need + * to add one so we get a proper path to the file + */ + if (dir [strlen(dir) - 1] != G_DIR_SEPARATOR) + return g_strconcat (dir, G_DIR_SEPARATOR_S, file, NULL); + else + return g_strconcat (dir, file, NULL); +} + +ICALL_EXPORT void * +ves_icall_System_Reflection_Assembly_GetManifestResourceInternal (MonoReflectionAssemblyHandle assembly_h, MonoStringHandle name, gint32 *size, MonoReflectionModuleHandleOut ref_module, MonoError *error) +{ + error_init (error); + MonoDomain *domain = MONO_HANDLE_DOMAIN (assembly_h); + MonoAssembly *assembly = MONO_HANDLE_GETVAL (assembly_h, assembly); + MonoTableInfo *table = &assembly->image->tables [MONO_TABLE_MANIFESTRESOURCE]; + guint32 i; + guint32 cols [MONO_MANIFEST_SIZE]; + guint32 impl, file_idx; + const char *val; + MonoImage *module; + + char *n = mono_string_handle_to_utf8 (name, error); + return_val_if_nok (error, NULL); + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, cols, MONO_MANIFEST_SIZE); + val = mono_metadata_string_heap (assembly->image, cols [MONO_MANIFEST_NAME]); + if (strcmp (val, n) == 0) + break; + } + g_free (n); + if (i == table->rows) + return NULL; + /* FIXME */ + impl = cols [MONO_MANIFEST_IMPLEMENTATION]; + if (impl) { + /* + * this code should only be called after obtaining the + * ResourceInfo and handling the other cases. + */ + g_assert ((impl & MONO_IMPLEMENTATION_MASK) == MONO_IMPLEMENTATION_FILE); + file_idx = impl >> MONO_IMPLEMENTATION_BITS; + + module = mono_image_load_file_for_image_checked (assembly->image, file_idx, error); + if (!is_ok (error) || !module) + return NULL; + } + else + module = assembly->image; + + + MonoReflectionModuleHandle rm = mono_module_get_object_handle (domain, module, error); + if (!is_ok (error)) + return NULL; + MONO_HANDLE_ASSIGN (ref_module, rm); + + return (void*)mono_image_get_resource (module, cols [MONO_MANIFEST_OFFSET], (guint32*)size); +} + +static gboolean +get_manifest_resource_info_internal (MonoReflectionAssemblyHandle assembly_h, MonoStringHandle name, MonoManifestResourceInfoHandle info, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + MonoDomain *domain = MONO_HANDLE_DOMAIN (assembly_h); + MonoAssembly *assembly = MONO_HANDLE_GETVAL (assembly_h, assembly); + MonoTableInfo *table = &assembly->image->tables [MONO_TABLE_MANIFESTRESOURCE]; + int i; + guint32 cols [MONO_MANIFEST_SIZE]; + guint32 file_cols [MONO_FILE_SIZE]; + const char *val; + char *n; + + gboolean result = FALSE; + + n = mono_string_handle_to_utf8 (name, error); + goto_if_nok (error, leave); + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, cols, MONO_MANIFEST_SIZE); + val = mono_metadata_string_heap (assembly->image, cols [MONO_MANIFEST_NAME]); + if (strcmp (val, n) == 0) + break; + } + g_free (n); + if (i == table->rows) + goto leave; + + if (!cols [MONO_MANIFEST_IMPLEMENTATION]) { + MONO_HANDLE_SETVAL (info, location, guint32, RESOURCE_LOCATION_EMBEDDED | RESOURCE_LOCATION_IN_MANIFEST); + } + else { + switch (cols [MONO_MANIFEST_IMPLEMENTATION] & MONO_IMPLEMENTATION_MASK) { + case MONO_IMPLEMENTATION_FILE: + i = cols [MONO_MANIFEST_IMPLEMENTATION] >> MONO_IMPLEMENTATION_BITS; + table = &assembly->image->tables [MONO_TABLE_FILE]; + mono_metadata_decode_row (table, i - 1, file_cols, MONO_FILE_SIZE); + val = mono_metadata_string_heap (assembly->image, file_cols [MONO_FILE_NAME]); + MONO_HANDLE_SET (info, filename, mono_string_new_handle (domain, val, error)); + if (file_cols [MONO_FILE_FLAGS] & FILE_CONTAINS_NO_METADATA) + MONO_HANDLE_SETVAL (info, location, guint32, 0); + else + MONO_HANDLE_SETVAL (info, location, guint32, RESOURCE_LOCATION_EMBEDDED); + break; + + case MONO_IMPLEMENTATION_ASSEMBLYREF: + i = cols [MONO_MANIFEST_IMPLEMENTATION] >> MONO_IMPLEMENTATION_BITS; + mono_assembly_load_reference (assembly->image, i - 1); + if (assembly->image->references [i - 1] == REFERENCE_MISSING) { + mono_error_set_assembly_load (error, NULL, "Assembly %d referenced from assembly %s not found ", i - 1, assembly->image->name); + goto leave; + } + MonoReflectionAssemblyHandle assm_obj = mono_assembly_get_object_handle (mono_domain_get (), assembly->image->references [i - 1], error); + goto_if_nok (error, leave); + MONO_HANDLE_SET (info, assembly, assm_obj); + + /* Obtain info recursively */ + get_manifest_resource_info_internal (assm_obj, name, info, error); + goto_if_nok (error, leave); + guint32 location = MONO_HANDLE_GETVAL (info, location); + location |= RESOURCE_LOCATION_ANOTHER_ASSEMBLY; + MONO_HANDLE_SETVAL (info, location, guint32, location); + break; + + case MONO_IMPLEMENTATION_EXP_TYPE: + g_assert_not_reached (); + break; + } + } + + result = TRUE; +leave: + HANDLE_FUNCTION_RETURN_VAL (result); +} + +ICALL_EXPORT gboolean +ves_icall_System_Reflection_Assembly_GetManifestResourceInfoInternal (MonoReflectionAssemblyHandle assembly_h, MonoStringHandle name, MonoManifestResourceInfoHandle info_h, MonoError *error) +{ + error_init (error); + return get_manifest_resource_info_internal (assembly_h, name, info_h, error); +} + +static gboolean +add_filename_to_files_array (MonoDomain *domain, MonoAssembly * assembly, MonoTableInfo *table, int i, MonoArrayHandle dest, int dest_idx, MonoError *error) +{ + HANDLE_FUNCTION_ENTER(); + error_init (error); + const char *val = mono_metadata_string_heap (assembly->image, mono_metadata_decode_row_col (table, i, MONO_FILE_NAME)); + char *n = g_concat_dir_and_file (assembly->basedir, val); + MonoStringHandle str = mono_string_new_handle (domain, n, error); + g_free (n); + goto_if_nok (error, leave); + MONO_HANDLE_ARRAY_SETREF (dest, dest_idx, str); +leave: + HANDLE_FUNCTION_RETURN_VAL (is_ok (error)); +} + +ICALL_EXPORT MonoObjectHandle +ves_icall_System_Reflection_Assembly_GetFilesInternal (MonoReflectionAssemblyHandle assembly_h, MonoStringHandle name, MonoBoolean resource_modules, MonoError *error) +{ + error_init (error); + MonoDomain *domain = MONO_HANDLE_DOMAIN (assembly_h); + MonoAssembly *assembly = MONO_HANDLE_GETVAL (assembly_h, assembly); + MonoTableInfo *table = &assembly->image->tables [MONO_TABLE_FILE]; + int i, count; + + /* check hash if needed */ + if (!MONO_HANDLE_IS_NULL(name)) { + char *n = mono_string_handle_to_utf8 (name, error); + goto_if_nok (error, fail); + + for (i = 0; i < table->rows; ++i) { + const char *val = mono_metadata_string_heap (assembly->image, mono_metadata_decode_row_col (table, i, MONO_FILE_NAME)); + if (strcmp (val, n) == 0) { + g_free (n); + n = g_concat_dir_and_file (assembly->basedir, val); + MonoStringHandle fn = mono_string_new_handle (domain, n, error); + g_free (n); + goto_if_nok (error, fail); + return MONO_HANDLE_CAST (MonoObject, fn); + } + } + g_free (n); + return NULL_HANDLE; + } + + count = 0; + for (i = 0; i < table->rows; ++i) { + if (resource_modules || !(mono_metadata_decode_row_col (table, i, MONO_FILE_FLAGS) & FILE_CONTAINS_NO_METADATA)) + count ++; + } + + MonoArrayHandle result = mono_array_new_handle (domain, mono_defaults.string_class, count, error); + goto_if_nok (error, fail); + + count = 0; + for (i = 0; i < table->rows; ++i) { + if (resource_modules || !(mono_metadata_decode_row_col (table, i, MONO_FILE_FLAGS) & FILE_CONTAINS_NO_METADATA)) { + if (!add_filename_to_files_array (domain, assembly, table, i, result, count, error)) + goto fail; + count++; + } + } + return MONO_HANDLE_CAST (MonoObject, result); +fail: + return NULL_HANDLE; +} + +static gboolean +add_module_to_modules_array (MonoDomain *domain, MonoArrayHandle dest, int *dest_idx, MonoImage* module, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + error_init (error); + if (module) { + MonoReflectionModuleHandle rm = mono_module_get_object_handle (domain, module, error); + goto_if_nok (error, leave); + + MONO_HANDLE_ARRAY_SETREF (dest, *dest_idx, rm); + ++(*dest_idx); + } + +leave: + HANDLE_FUNCTION_RETURN_VAL (is_ok (error)); +} + +static gboolean +add_file_to_modules_array (MonoDomain *domain, MonoArrayHandle dest, int dest_idx, MonoImage *image, MonoTableInfo *table, int table_idx, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + error_init (error); + + guint32 cols [MONO_FILE_SIZE]; + mono_metadata_decode_row (table, table_idx, cols, MONO_FILE_SIZE); + if (cols [MONO_FILE_FLAGS] & FILE_CONTAINS_NO_METADATA) { + MonoReflectionModuleHandle rm = mono_module_file_get_object_handle (domain, image, table_idx, error); + goto_if_nok (error, leave); + MONO_HANDLE_ARRAY_SETREF (dest, dest_idx, rm); + } else { + MonoImage *m = mono_image_load_file_for_image_checked (image, table_idx + 1, error); + goto_if_nok (error, leave); + if (!m) { + const char *filename = mono_metadata_string_heap (image, cols [MONO_FILE_NAME]); + mono_error_set_assembly_load (error, g_strdup (filename), "%s", ""); + goto leave; + } + MonoReflectionModuleHandle rm = mono_module_get_object_handle (domain, m, error); + goto_if_nok (error, leave); + MONO_HANDLE_ARRAY_SETREF (dest, dest_idx, rm); + } + +leave: + HANDLE_FUNCTION_RETURN_VAL (is_ok (error)); +} + +ICALL_EXPORT MonoArrayHandle +ves_icall_System_Reflection_Assembly_GetModulesInternal (MonoReflectionAssemblyHandle assembly_h, MonoError *error) +{ + error_init (error); + MonoDomain *domain = mono_domain_get(); + MonoAssembly *assembly = MONO_HANDLE_GETVAL (assembly_h, assembly); + MonoClass *klass; + int i, j, file_count = 0; + MonoImage **modules; + guint32 module_count, real_module_count; + MonoTableInfo *table; + MonoImage *image = assembly->image; + + g_assert (image != NULL); + g_assert (!assembly_is_dynamic (assembly)); + + table = &image->tables [MONO_TABLE_FILE]; + file_count = table->rows; + + modules = image->modules; + module_count = image->module_count; + + real_module_count = 0; + for (i = 0; i < module_count; ++i) + if (modules [i]) + real_module_count ++; + + klass = mono_class_get_module_class (); + MonoArrayHandle res = mono_array_new_handle (domain, klass, 1 + real_module_count + file_count, error); + goto_if_nok (error, fail); + + MonoReflectionModuleHandle image_obj = mono_module_get_object_handle (domain, image, error); + goto_if_nok (error, fail); + + MONO_HANDLE_ARRAY_SETREF (res, 0, image_obj); + + j = 1; + for (i = 0; i < module_count; ++i) + if (!add_module_to_modules_array (domain, res, &j, modules[i], error)) + goto fail; + + for (i = 0; i < file_count; ++i, ++j) { + if (!add_file_to_modules_array (domain, res, j, image, table, i, error)) + goto fail; + } + + return res; +fail: + return MONO_HANDLE_CAST (MonoArray, NULL_HANDLE); +} + +ICALL_EXPORT MonoReflectionMethodHandle +ves_icall_GetCurrentMethod (MonoError *error) +{ + error_init (error); + + MonoMethod *m = mono_method_get_last_managed (); + + if (!m) { + mono_error_set_not_supported (error, "Stack walks are not supported on this platform."); + return MONO_HANDLE_CAST (MonoReflectionMethod, NULL_HANDLE); + } + + while (m->is_inflated) + m = ((MonoMethodInflated*)m)->declaring; + + return mono_method_get_object_handle (mono_domain_get (), m, NULL, error); +} + + +static MonoMethod* +mono_method_get_equivalent_method (MonoMethod *method, MonoClass *klass) +{ + int offset = -1, i; + if (method->is_inflated && ((MonoMethodInflated*)method)->context.method_inst) { + MonoError error; + MonoMethod *result; + MonoMethodInflated *inflated = (MonoMethodInflated*)method; + //method is inflated, we should inflate it on the other class + MonoGenericContext ctx; + ctx.method_inst = inflated->context.method_inst; + ctx.class_inst = inflated->context.class_inst; + if (mono_class_is_ginst (klass)) + ctx.class_inst = mono_class_get_generic_class (klass)->context.class_inst; + else if (mono_class_is_gtd (klass)) + ctx.class_inst = mono_class_get_generic_container (klass)->context.class_inst; + result = mono_class_inflate_generic_method_full_checked (inflated->declaring, klass, &ctx, &error); + g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */ + return result; + } + + mono_class_setup_methods (method->klass); + if (mono_class_has_failure (method->klass)) + return NULL; + int mcount = mono_class_get_method_count (method->klass); + for (i = 0; i < mcount; ++i) { + if (method->klass->methods [i] == method) { + offset = i; + break; + } + } + mono_class_setup_methods (klass); + if (mono_class_has_failure (klass)) + return NULL; + g_assert (offset >= 0 && offset < mono_class_get_method_count (klass)); + return klass->methods [offset]; +} + +ICALL_EXPORT MonoReflectionMethodHandle +ves_icall_System_Reflection_MethodBase_GetMethodFromHandleInternalType_native (MonoMethod *method, MonoType *type, MonoBoolean generic_check, MonoError *error) +{ + error_init (error); + MonoClass *klass; + if (type && generic_check) { + klass = mono_class_from_mono_type (type); + if (mono_class_get_generic_type_definition (method->klass) != mono_class_get_generic_type_definition (klass)) + return MONO_HANDLE_CAST (MonoReflectionMethod, NULL_HANDLE); + + if (method->klass != klass) { + method = mono_method_get_equivalent_method (method, klass); + if (!method) + return MONO_HANDLE_CAST (MonoReflectionMethod, NULL_HANDLE); + } + } else if (type) + klass = mono_class_from_mono_type (type); + else + klass = method->klass; + return mono_method_get_object_handle (mono_domain_get (), method, klass, error); +} + +ICALL_EXPORT MonoReflectionMethodBodyHandle +ves_icall_System_Reflection_MethodBase_GetMethodBodyInternal (MonoMethod *method, MonoError *error) +{ + error_init (error); + return mono_method_body_get_object_handle (mono_domain_get (), method, error); +} + +ICALL_EXPORT MonoReflectionAssemblyHandle +ves_icall_System_Reflection_Assembly_GetExecutingAssembly (MonoError *error) +{ + error_init (error); + + MonoMethod *dest = NULL; + mono_stack_walk_no_il (get_executing, &dest); + g_assert (dest); + return mono_assembly_get_object_handle (mono_domain_get (), dest->klass->image->assembly, error); +} + + +ICALL_EXPORT MonoReflectionAssemblyHandle +ves_icall_System_Reflection_Assembly_GetEntryAssembly (MonoError *error) +{ + error_init (error); + + MonoDomain* domain = mono_domain_get (); + + if (!domain->entry_assembly) + return MONO_HANDLE_CAST (MonoReflectionAssembly, NULL_HANDLE); + + return mono_assembly_get_object_handle (domain, domain->entry_assembly, error); +} + +ICALL_EXPORT MonoReflectionAssemblyHandle +ves_icall_System_Reflection_Assembly_GetCallingAssembly (MonoError *error) +{ + error_init (error); + MonoMethod *m; + MonoMethod *dest; + + dest = NULL; + mono_stack_walk_no_il (get_executing, &dest); + m = dest; + mono_stack_walk_no_il (get_caller_no_reflection, &dest); + if (!dest) + dest = m; + if (!m) { + mono_error_set_not_supported (error, "Stack walks are not supported on this platform."); + return MONO_HANDLE_CAST (MonoReflectionAssembly, NULL_HANDLE); + } + return mono_assembly_get_object_handle (mono_domain_get (), dest->klass->image->assembly, error); +} + +ICALL_EXPORT MonoStringHandle +ves_icall_System_RuntimeType_getFullName (MonoReflectionTypeHandle object, gboolean full_name, + gboolean assembly_qualified, MonoError *error) +{ + MonoDomain *domain = mono_object_domain (MONO_HANDLE_RAW (object)); + MonoType *type = MONO_HANDLE_RAW (object)->type; + MonoTypeNameFormat format; + MonoStringHandle res; + gchar *name; + + if (full_name) + format = assembly_qualified ? + MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED : + MONO_TYPE_NAME_FORMAT_FULL_NAME; + else + format = MONO_TYPE_NAME_FORMAT_REFLECTION; + + name = mono_type_get_name_full (type, format); + if (!name) + return NULL_HANDLE_STRING; + + if (full_name && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR)) { + g_free (name); + return NULL_HANDLE_STRING; + } + + res = mono_string_new_handle (domain, name, error); + g_free (name); + + return res; +} + +ICALL_EXPORT int +vell_icall_RuntimeType_get_core_clr_security_level (MonoReflectionTypeHandle rfield, MonoError *error) +{ + error_init (error); + MonoType *type = MONO_HANDLE_GETVAL (rfield, type); + MonoClass *klass = mono_class_from_mono_type (type); + + mono_class_init_checked (klass, error); + if (!is_ok (error)) + return -1; + return mono_security_core_clr_class_level (klass); +} + +ICALL_EXPORT int +ves_icall_MonoField_get_core_clr_security_level (MonoReflectionField *rfield) +{ + MonoClassField *field = rfield->field; + return mono_security_core_clr_field_level (field, TRUE); +} + +ICALL_EXPORT int +ves_icall_MonoMethod_get_core_clr_security_level (MonoReflectionMethodHandle rfield, MonoError *error) +{ + error_init (error); + MonoMethod *method = MONO_HANDLE_GETVAL (rfield, method); + return mono_security_core_clr_method_level (method, TRUE); +} + +ICALL_EXPORT MonoStringHandle +ves_icall_System_Reflection_Assembly_get_fullName (MonoReflectionAssemblyHandle assembly, MonoError *error) +{ + error_init (error); + MonoDomain *domain = MONO_HANDLE_DOMAIN (assembly); + MonoAssembly *mass = MONO_HANDLE_GETVAL (assembly, assembly); + gchar *name; + + name = mono_stringify_assembly_name (&mass->aname); + MonoStringHandle res = mono_string_new_handle (domain, name, error); + g_free (name); + return res; +} + +ICALL_EXPORT MonoAssemblyName * +ves_icall_System_Reflection_AssemblyName_GetNativeName (MonoAssembly *mass) +{ + return &mass->aname; +} + +ICALL_EXPORT void +ves_icall_System_Reflection_Assembly_InternalGetAssemblyName (MonoStringHandle fname, MonoAssemblyName *name, MonoStringHandleOut normalized_codebase, MonoError *error) +{ + char *filename; + MonoImageOpenStatus status = MONO_IMAGE_OK; + char *codebase = NULL; + gboolean res; + MonoImage *image; + char *dirname; + + error_init (error); + + filename = mono_string_handle_to_utf8 (fname, error); + return_if_nok (error); + + dirname = g_path_get_dirname (filename); + replace_shadow_path (mono_domain_get (), dirname, &filename); + g_free (dirname); + + image = mono_image_open_full (filename, &status, TRUE); + + if (!image){ + if (status == MONO_IMAGE_IMAGE_INVALID) + mono_error_set_bad_image_name (error, g_strdup (filename), "%s", ""); + else + mono_error_set_assembly_load (error, g_strdup (filename), "%s", ""); + g_free (filename); + return; + } + + res = mono_assembly_fill_assembly_name_full (image, name, TRUE); + if (!res) { + mono_image_close (image); + g_free (filename); + mono_error_set_argument (error, "assemblyFile", "The file does not contain a manifest"); + return; + } + + if (filename != NULL && *filename != '\0') { + gchar *result; + + codebase = g_strdup (filename); + + mono_icall_make_platform_path (codebase); + + const gchar *prepend = mono_icall_get_file_path_prefix (codebase); + + result = g_strconcat (prepend, codebase, NULL); + g_free (codebase); + codebase = result; + } + MONO_HANDLE_ASSIGN (normalized_codebase, mono_string_new_handle (mono_domain_get (), codebase, error)); + g_free (codebase); + + mono_image_close (image); + g_free (filename); +} + +ICALL_EXPORT MonoBoolean +ves_icall_System_Reflection_Assembly_LoadPermissions (MonoReflectionAssemblyHandle assembly_h, + char **minimum, guint32 *minLength, char **optional, guint32 *optLength, char **refused, guint32 *refLength, MonoError *error) +{ + error_init (error); + MonoAssembly *assembly = MONO_HANDLE_GETVAL (assembly_h, assembly); + MonoBoolean result = FALSE; + MonoDeclSecurityEntry entry; + + /* SecurityAction.RequestMinimum */ + if (mono_declsec_get_assembly_action (assembly, SECURITY_ACTION_REQMIN, &entry)) { + *minimum = entry.blob; + *minLength = entry.size; + result = TRUE; + } + /* SecurityAction.RequestOptional */ + if (mono_declsec_get_assembly_action (assembly, SECURITY_ACTION_REQOPT, &entry)) { + *optional = entry.blob; + *optLength = entry.size; + result = TRUE; + } + /* SecurityAction.RequestRefuse */ + if (mono_declsec_get_assembly_action (assembly, SECURITY_ACTION_REQREFUSE, &entry)) { + *refused = entry.blob; + *refLength = entry.size; + result = TRUE; + } + + return result; +} + +static gboolean +mono_module_type_is_visible (MonoTableInfo *tdef, MonoImage *image, int type) +{ + guint32 attrs, visibility; + do { + attrs = mono_metadata_decode_row_col (tdef, type - 1, MONO_TYPEDEF_FLAGS); + visibility = attrs & TYPE_ATTRIBUTE_VISIBILITY_MASK; + if (visibility != TYPE_ATTRIBUTE_PUBLIC && visibility != TYPE_ATTRIBUTE_NESTED_PUBLIC) + return FALSE; + + } while ((type = mono_metadata_token_index (mono_metadata_nested_in_typedef (image, type)))); + + return TRUE; +} + +static void +image_get_type (MonoDomain *domain, MonoImage *image, MonoTableInfo *tdef, int table_idx, int count, MonoArrayHandle res, MonoArrayHandle exceptions, MonoBoolean exportedOnly, MonoError *error) +{ + error_init (error); + HANDLE_FUNCTION_ENTER (); + MonoError klass_error; + MonoClass *klass = mono_class_get_checked (image, table_idx | MONO_TOKEN_TYPE_DEF, &klass_error); + + if (klass) { + MonoReflectionTypeHandle rt = mono_type_get_object_handle (domain, &klass->byval_arg, error); + return_if_nok (error); + + MONO_HANDLE_ARRAY_SETREF (res, count, rt); + } else { + MonoException *ex = mono_error_convert_to_exception (&klass_error); + MONO_HANDLE_ARRAY_SETRAW (exceptions, count, ex); + } + HANDLE_FUNCTION_RETURN (); +} + +static MonoArrayHandle +mono_module_get_types (MonoDomain *domain, MonoImage *image, MonoArrayHandleOut exceptions, MonoBoolean exportedOnly, MonoError *error) +{ + MonoTableInfo *tdef = &image->tables [MONO_TABLE_TYPEDEF]; + int i, count; + + error_init (error); + + /* we start the count from 1 because we skip the special type */ + if (exportedOnly) { + count = 0; + for (i = 1; i < tdef->rows; ++i) { + if (mono_module_type_is_visible (tdef, image, i + 1)) + count++; + } + } else { + count = tdef->rows - 1; + } + MonoArrayHandle res = mono_array_new_handle (domain, mono_defaults.runtimetype_class, count, error); + return_val_if_nok (error, MONO_HANDLE_CAST (MonoArray, NULL_HANDLE)); + MONO_HANDLE_ASSIGN (exceptions, mono_array_new_handle (domain, mono_defaults.exception_class, count, error)); + return_val_if_nok (error, MONO_HANDLE_CAST (MonoArray, NULL_HANDLE)); + count = 0; + for (i = 1; i < tdef->rows; ++i) { + if (!exportedOnly || mono_module_type_is_visible (tdef, image, i+1)) { + image_get_type (domain, image, tdef, i + 1, count, res, exceptions, exportedOnly, error); + return_val_if_nok (error, MONO_HANDLE_CAST (MonoArray, NULL_HANDLE)); + count++; + } + } + + return res; +} + +static void +append_module_types (MonoDomain *domain, MonoArrayHandleOut res, MonoArrayHandleOut exceptions, MonoImage *image, MonoBoolean exportedOnly, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + error_init (error); + MonoArrayHandle ex2 = MONO_HANDLE_NEW (MonoArray, NULL); + MonoArrayHandle res2 = mono_module_get_types (domain, image, ex2, exportedOnly, error); + goto_if_nok (error, leave); + + /* Append the new types to the end of the array */ + if (mono_array_handle_length (res2) > 0) { + guint32 len1, len2; + + len1 = mono_array_handle_length (res); + len2 = mono_array_handle_length (res2); + + MonoArrayHandle res3 = mono_array_new_handle (domain, mono_defaults.runtimetype_class, len1 + len2, error); + goto_if_nok (error, leave); + + mono_array_handle_memcpy_refs (res3, 0, res, 0, len1); + mono_array_handle_memcpy_refs (res3, len1, res2, 0, len2); + MONO_HANDLE_ASSIGN (res, res3); + + MonoArrayHandle ex3 = mono_array_new_handle (domain, mono_defaults.runtimetype_class, len1 + len2, error); + goto_if_nok (error, leave); + + mono_array_handle_memcpy_refs (ex3, 0, exceptions, 0, len1); + mono_array_handle_memcpy_refs (ex3, len1, ex2, 0, len2); + MONO_HANDLE_ASSIGN (exceptions, ex3); + } +leave: + HANDLE_FUNCTION_RETURN (); +} + +static void +set_class_failure_in_array (MonoArrayHandle exl, int i, MonoClass *klass) +{ + HANDLE_FUNCTION_ENTER (); + MonoError unboxed_error; + error_init (&unboxed_error); + mono_error_set_for_class_failure (&unboxed_error, klass); + + MonoExceptionHandle exc = MONO_HANDLE_NEW (MonoException, mono_error_convert_to_exception (&unboxed_error)); + MONO_HANDLE_ARRAY_SETREF (exl, i, exc); + HANDLE_FUNCTION_RETURN (); +} + +ICALL_EXPORT MonoArrayHandle +ves_icall_System_Reflection_Assembly_GetTypes (MonoReflectionAssemblyHandle assembly_handle, MonoBoolean exportedOnly, MonoError *error) +{ + MonoArrayHandle exceptions = MONO_HANDLE_NEW(MonoArray, NULL); + int i; + + MonoDomain *domain = MONO_HANDLE_DOMAIN (assembly_handle); + MonoAssembly *assembly = MONO_HANDLE_GETVAL (assembly_handle, assembly); + + g_assert (!assembly_is_dynamic (assembly)); + MonoImage *image = assembly->image; + MonoTableInfo *table = &image->tables [MONO_TABLE_FILE]; + MonoArrayHandle res = mono_module_get_types (domain, image, exceptions, exportedOnly, error); + return_val_if_nok (error, MONO_HANDLE_CAST (MonoArray, NULL_HANDLE)); + + /* Append data from all modules in the assembly */ + for (i = 0; i < table->rows; ++i) { + if (!(mono_metadata_decode_row_col (table, i, MONO_FILE_FLAGS) & FILE_CONTAINS_NO_METADATA)) { + MonoImage *loaded_image = mono_assembly_load_module_checked (image->assembly, i + 1, error); + return_val_if_nok (error, MONO_HANDLE_CAST (MonoArray, NULL_HANDLE)); + + if (loaded_image) { + append_module_types (domain, res, exceptions, loaded_image, exportedOnly, error); + return_val_if_nok (error, MONO_HANDLE_CAST (MonoArray, NULL_HANDLE)); + } + } + } + + /* the ReflectionTypeLoadException must have all the types (Types property), + * NULL replacing types which throws an exception. The LoaderException must + * contain all exceptions for NULL items. + */ + + int len = mono_array_handle_length (res); + + int ex_count = 0; + GList *list = NULL; + MonoReflectionTypeHandle t = MONO_HANDLE_NEW (MonoReflectionType, NULL); + for (i = 0; i < len; i++) { + MONO_HANDLE_ARRAY_GETREF (t, res, i); + + if (!MONO_HANDLE_IS_NULL (t)) { + MonoClass *klass = mono_type_get_class (MONO_HANDLE_GETVAL (t, type)); + if ((klass != NULL) && mono_class_has_failure (klass)) { + /* keep the class in the list */ + list = g_list_append (list, klass); + /* and replace Type with NULL */ + MONO_HANDLE_ARRAY_SETRAW (res, i, NULL); + } + } else { + ex_count ++; + } + } + + if (list || ex_count) { + GList *tmp = NULL; + int j, length = g_list_length (list) + ex_count; + + MonoArrayHandle exl = mono_array_new_handle (domain, mono_defaults.exception_class, length, error); + if (!is_ok (error)) { + g_list_free (list); + return MONO_HANDLE_CAST (MonoArray, NULL_HANDLE); + } + /* Types for which mono_class_get_checked () succeeded */ + MonoExceptionHandle exc = MONO_HANDLE_NEW (MonoException, NULL); + for (i = 0, tmp = list; tmp; i++, tmp = tmp->next) { + set_class_failure_in_array (exl, i, (MonoClass*)tmp->data); + } + /* Types for which it don't */ + for (j = 0; j < mono_array_handle_length (exceptions); ++j) { + MONO_HANDLE_ARRAY_GETREF (exc, exceptions, j); + if (!MONO_HANDLE_IS_NULL (exc)) { + g_assert (i < length); + MONO_HANDLE_ARRAY_SETREF (exl, i, exc); + i ++; + } + } + g_list_free (list); + list = NULL; + + MONO_HANDLE_ASSIGN (exc, mono_get_exception_reflection_type_load_checked (res, exl, error)); + if (!is_ok (error)) { + return MONO_HANDLE_CAST (MonoArray, NULL_HANDLE); + } + mono_error_set_exception_handle (error, exc); + return MONO_HANDLE_CAST (MonoArray, NULL_HANDLE); + } + + return res; +} + +ICALL_EXPORT void +ves_icall_Mono_RuntimeMarshal_FreeAssemblyName (MonoAssemblyName *aname, gboolean free_struct) +{ + mono_assembly_name_free (aname); + if (free_struct) + g_free (aname); +} + +ICALL_EXPORT MonoBoolean +ves_icall_System_Reflection_AssemblyName_ParseAssemblyName (const char *name, MonoAssemblyName *aname, MonoBoolean *is_version_defined_arg, MonoBoolean *is_token_defined_arg) +{ + gboolean is_version_defined = FALSE; + gboolean is_token_defined = FALSE; + gboolean result = FALSE; + + result = mono_assembly_name_parse_full (name, aname, TRUE, &is_version_defined, &is_token_defined); + + *is_version_defined_arg = (MonoBoolean)is_version_defined; + *is_token_defined_arg = (MonoBoolean)is_token_defined; + + return result; +} + +ICALL_EXPORT MonoReflectionTypeHandle +ves_icall_System_Reflection_Module_GetGlobalType (MonoReflectionModuleHandle module, MonoError *error) +{ + MonoDomain *domain = MONO_HANDLE_DOMAIN (module); + MonoImage *image = MONO_HANDLE_GETVAL (module, image); + MonoClass *klass; + + g_assert (image); + + MonoReflectionTypeHandle ret = MONO_HANDLE_CAST (MonoReflectionType, NULL_HANDLE); + + if (image_is_dynamic (image) && ((MonoDynamicImage*)image)->initial_image) + /* These images do not have a global type */ + goto leave; + + klass = mono_class_get_checked (image, 1 | MONO_TOKEN_TYPE_DEF, error); + goto_if_nok (error, leave); + + ret = mono_type_get_object_handle (domain, &klass->byval_arg, error); +leave: + return ret; +} + +ICALL_EXPORT void +ves_icall_System_Reflection_Module_Close (MonoReflectionModuleHandle module, MonoError *error) +{ + /*if (module->image) + mono_image_close (module->image);*/ +} + +ICALL_EXPORT MonoStringHandle +ves_icall_System_Reflection_Module_GetGuidInternal (MonoReflectionModuleHandle refmodule, MonoError *error) +{ + MonoDomain *domain = MONO_HANDLE_DOMAIN (refmodule); + MonoImage *image = MONO_HANDLE_GETVAL (refmodule, image); + + g_assert (image); + return mono_string_new_handle (domain, image->guid, error); +} + +#ifndef HOST_WIN32 +static inline gpointer +mono_icall_module_get_hinstance (MonoReflectionModuleHandle module) +{ + return (gpointer) (-1); +} +#endif /* HOST_WIN32 */ + +ICALL_EXPORT gpointer +ves_icall_System_Reflection_Module_GetHINSTANCE (MonoReflectionModuleHandle module, MonoError *error) +{ + return mono_icall_module_get_hinstance (module); +} + +ICALL_EXPORT void +ves_icall_System_Reflection_Module_GetPEKind (MonoImage *image, gint32 *pe_kind, gint32 *machine, MonoError *error) +{ + if (image_is_dynamic (image)) { + MonoDynamicImage *dyn = (MonoDynamicImage*)image; + *pe_kind = dyn->pe_kind; + *machine = dyn->machine; + } + else { + *pe_kind = ((MonoCLIImageInfo*)(image->image_info))->cli_cli_header.ch_flags & 0x3; + *machine = ((MonoCLIImageInfo*)(image->image_info))->cli_header.coff.coff_machine; + } +} + +ICALL_EXPORT gint32 +ves_icall_System_Reflection_Module_GetMDStreamVersion (MonoImage *image, MonoError *error) +{ + return (image->md_version_major << 16) | (image->md_version_minor); +} + +ICALL_EXPORT MonoArrayHandle +ves_icall_System_Reflection_Module_InternalGetTypes (MonoReflectionModuleHandle module, MonoError *error) +{ + error_init (error); + + MonoImage *image = MONO_HANDLE_GETVAL (module, image); + MonoDomain *domain = MONO_HANDLE_DOMAIN (module); + + if (!image) { + MonoArrayHandle arr = mono_array_new_handle (domain, mono_defaults.runtimetype_class, 0, error); + return arr; + } else { + MonoArrayHandle exceptions = MONO_HANDLE_NEW (MonoArray, NULL); + MonoArrayHandle res = mono_module_get_types (domain, image, exceptions, FALSE, error); + return_val_if_nok (error, MONO_HANDLE_CAST(MonoArray, NULL_HANDLE)); + + int n = mono_array_handle_length (exceptions); + MonoExceptionHandle ex = MONO_HANDLE_NEW (MonoException, NULL); + for (int i = 0; i < n; ++i) { + MONO_HANDLE_ARRAY_GETREF(ex, exceptions, i); + if (!MONO_HANDLE_IS_NULL (ex)) { + mono_error_set_exception_handle (error, ex); + return MONO_HANDLE_CAST(MonoArray, NULL_HANDLE); + } + } + return res; + } +} + +static gboolean +mono_memberref_is_method (MonoImage *image, guint32 token) +{ + if (!image_is_dynamic (image)) { + guint32 cols [MONO_MEMBERREF_SIZE]; + const char *sig; + mono_metadata_decode_row (&image->tables [MONO_TABLE_MEMBERREF], mono_metadata_token_index (token) - 1, cols, MONO_MEMBERREF_SIZE); + sig = mono_metadata_blob_heap (image, cols [MONO_MEMBERREF_SIGNATURE]); + mono_metadata_decode_blob_size (sig, &sig); + return (*sig != 0x6); + } else { + MonoError error; + MonoClass *handle_class; + + if (!mono_lookup_dynamic_token_class (image, token, FALSE, &handle_class, NULL, &error)) { + mono_error_cleanup (&error); /* just probing, ignore error */ + return FALSE; + } + + return mono_defaults.methodhandle_class == handle_class; + } +} + +static MonoGenericInst * +get_generic_inst_from_array_handle (MonoArrayHandle type_args) +{ + int type_argc = mono_array_handle_length (type_args); + int size = MONO_SIZEOF_GENERIC_INST + type_argc * sizeof (MonoType *); + + MonoGenericInst *ginst = (MonoGenericInst *)g_alloca (size); + memset (ginst, 0, sizeof (MonoGenericInst)); + ginst->type_argc = type_argc; + for (int i = 0; i < type_argc; i++) { + MONO_HANDLE_ARRAY_GETVAL (ginst->type_argv[i], type_args, MonoType*, i); + } + ginst->is_open = FALSE; + for (int i = 0; i < type_argc; i++) { + if (mono_class_is_open_constructed_type (ginst->type_argv[i])) { + ginst->is_open = TRUE; + break; + } + } + + return mono_metadata_get_canonical_generic_inst (ginst); +} + +static void +init_generic_context_from_args_handles (MonoGenericContext *context, MonoArrayHandle type_args, MonoArrayHandle method_args) +{ + if (!MONO_HANDLE_IS_NULL (type_args)) { + context->class_inst = get_generic_inst_from_array_handle (type_args); + } else { + context->class_inst = NULL; + } + if (!MONO_HANDLE_IS_NULL (method_args)) { + context->method_inst = get_generic_inst_from_array_handle (method_args); + } else { + context->method_inst = NULL; + } +} + + +static MonoType* +module_resolve_type_token (MonoImage *image, guint32 token, MonoArrayHandle type_args, MonoArrayHandle method_args, MonoResolveTokenError *resolve_error, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + error_init (error); + MonoType *result = NULL; + MonoClass *klass; + int table = mono_metadata_token_table (token); + int index = mono_metadata_token_index (token); + MonoGenericContext context; + + *resolve_error = ResolveTokenError_Other; + + /* Validate token */ + if ((table != MONO_TABLE_TYPEDEF) && (table != MONO_TABLE_TYPEREF) && + (table != MONO_TABLE_TYPESPEC)) { + *resolve_error = ResolveTokenError_BadTable; + goto leave; + } + + if (image_is_dynamic (image)) { + if ((table == MONO_TABLE_TYPEDEF) || (table == MONO_TABLE_TYPEREF)) { + MonoError inner_error; + klass = (MonoClass *)mono_lookup_dynamic_token_class (image, token, FALSE, NULL, NULL, &inner_error); + mono_error_cleanup (&inner_error); + result = klass ? &klass->byval_arg : NULL; + goto leave; + } + + init_generic_context_from_args_handles (&context, type_args, method_args); + MonoError inner_error; + klass = (MonoClass *)mono_lookup_dynamic_token_class (image, token, FALSE, NULL, &context, &inner_error); + mono_error_cleanup (&inner_error); + result = klass ? &klass->byval_arg : NULL; + goto leave; + } + + if ((index <= 0) || (index > image->tables [table].rows)) { + *resolve_error = ResolveTokenError_OutOfRange; + goto leave; + } + + init_generic_context_from_args_handles (&context, type_args, method_args); + klass = mono_class_get_checked (image, token, error); + if (klass) + klass = mono_class_inflate_generic_class_checked (klass, &context, error); + goto_if_nok (error, leave); + + if (klass) + result = &klass->byval_arg; +leave: + HANDLE_FUNCTION_RETURN_VAL (result); + +} +ICALL_EXPORT MonoType* +ves_icall_System_Reflection_Module_ResolveTypeToken (MonoImage *image, guint32 token, MonoArrayHandle type_args, MonoArrayHandle method_args, MonoResolveTokenError *resolve_error, MonoError *error) +{ + return module_resolve_type_token (image, token, type_args, method_args, resolve_error, error); +} + +static MonoMethod* +module_resolve_method_token (MonoImage *image, guint32 token, MonoArrayHandle type_args, MonoArrayHandle method_args, MonoResolveTokenError *resolve_error, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + error_init (error); + MonoMethod *method = NULL; + int table = mono_metadata_token_table (token); + int index = mono_metadata_token_index (token); + MonoGenericContext context; + + *resolve_error = ResolveTokenError_Other; + + /* Validate token */ + if ((table != MONO_TABLE_METHOD) && (table != MONO_TABLE_METHODSPEC) && + (table != MONO_TABLE_MEMBERREF)) { + *resolve_error = ResolveTokenError_BadTable; + goto leave; + } + + if (image_is_dynamic (image)) { + if (table == MONO_TABLE_METHOD) { + MonoError inner_error; + method = (MonoMethod *)mono_lookup_dynamic_token_class (image, token, FALSE, NULL, NULL, &inner_error); + mono_error_cleanup (&inner_error); + goto leave; + } + + if ((table == MONO_TABLE_MEMBERREF) && !(mono_memberref_is_method (image, token))) { + *resolve_error = ResolveTokenError_BadTable; + goto leave; + } + + init_generic_context_from_args_handles (&context, type_args, method_args); + MonoError inner_error; + method = (MonoMethod *)mono_lookup_dynamic_token_class (image, token, FALSE, NULL, &context, &inner_error); + mono_error_cleanup (&inner_error); + goto leave; + } + + if ((index <= 0) || (index > image->tables [table].rows)) { + *resolve_error = ResolveTokenError_OutOfRange; + goto leave; + } + if ((table == MONO_TABLE_MEMBERREF) && (!mono_memberref_is_method (image, token))) { + *resolve_error = ResolveTokenError_BadTable; + goto leave; + } + + init_generic_context_from_args_handles (&context, type_args, method_args); + method = mono_get_method_checked (image, token, NULL, &context, error); + +leave: + HANDLE_FUNCTION_RETURN_VAL (method); +} + +ICALL_EXPORT MonoMethod* +ves_icall_System_Reflection_Module_ResolveMethodToken (MonoImage *image, guint32 token, MonoArrayHandle type_args, MonoArrayHandle method_args, MonoResolveTokenError *resolve_error, MonoError *error) +{ + return module_resolve_method_token (image, token, type_args, method_args, resolve_error, error); +} + +ICALL_EXPORT MonoString* +ves_icall_System_Reflection_Module_ResolveStringToken (MonoImage *image, guint32 token, MonoResolveTokenError *resolve_error) +{ + MonoError error; + int index = mono_metadata_token_index (token); + + *resolve_error = ResolveTokenError_Other; + + /* Validate token */ + if (mono_metadata_token_code (token) != MONO_TOKEN_STRING) { + *resolve_error = ResolveTokenError_BadTable; + return NULL; + } + + if (image_is_dynamic (image)) { + MonoString * result = (MonoString *)mono_lookup_dynamic_token_class (image, token, FALSE, NULL, NULL, &error); + mono_error_cleanup (&error); + return result; + } + + if ((index <= 0) || (index >= image->heap_us.size)) { + *resolve_error = ResolveTokenError_OutOfRange; + return NULL; + } + + /* FIXME: What to do if the index points into the middle of a string ? */ + + MonoString *result = mono_ldstr_checked (mono_domain_get (), image, index, &error); + mono_error_set_pending_exception (&error); + return result; +} + +static MonoClassField* +module_resolve_field_token (MonoImage *image, guint32 token, MonoArrayHandle type_args, MonoArrayHandle method_args, MonoResolveTokenError *resolve_error, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + MonoClass *klass; + int table = mono_metadata_token_table (token); + int index = mono_metadata_token_index (token); + MonoGenericContext context; + MonoClassField *field = NULL; + + error_init (error); + *resolve_error = ResolveTokenError_Other; + + /* Validate token */ + if ((table != MONO_TABLE_FIELD) && (table != MONO_TABLE_MEMBERREF)) { + *resolve_error = ResolveTokenError_BadTable; + goto leave; + } + + if (image_is_dynamic (image)) { + if (table == MONO_TABLE_FIELD) { + MonoError inner_error; + field = (MonoClassField *)mono_lookup_dynamic_token_class (image, token, FALSE, NULL, NULL, &inner_error); + mono_error_cleanup (&inner_error); + goto leave; + } + + if (mono_memberref_is_method (image, token)) { + *resolve_error = ResolveTokenError_BadTable; + goto leave; + } + + init_generic_context_from_args_handles (&context, type_args, method_args); + MonoError inner_error; + field = (MonoClassField *)mono_lookup_dynamic_token_class (image, token, FALSE, NULL, &context, &inner_error); + mono_error_cleanup (&inner_error); + goto leave; + } + + if ((index <= 0) || (index > image->tables [table].rows)) { + *resolve_error = ResolveTokenError_OutOfRange; + goto leave; + } + if ((table == MONO_TABLE_MEMBERREF) && (mono_memberref_is_method (image, token))) { + *resolve_error = ResolveTokenError_BadTable; + goto leave; + } + + init_generic_context_from_args_handles (&context, type_args, method_args); + field = mono_field_from_token_checked (image, token, &klass, &context, error); + +leave: + HANDLE_FUNCTION_RETURN_VAL (field); +} + +ICALL_EXPORT MonoClassField* +ves_icall_System_Reflection_Module_ResolveFieldToken (MonoImage *image, guint32 token, MonoArrayHandle type_args, MonoArrayHandle method_args, MonoResolveTokenError *resolve_error, MonoError *error) +{ + return module_resolve_field_token (image, token, type_args, method_args, resolve_error, error); +} + +ICALL_EXPORT MonoObjectHandle +ves_icall_System_Reflection_Module_ResolveMemberToken (MonoImage *image, guint32 token, MonoArrayHandle type_args, MonoArrayHandle method_args, MonoResolveTokenError *error, MonoError *merror) +{ + int table = mono_metadata_token_table (token); + + error_init (merror); + *error = ResolveTokenError_Other; + + switch (table) { + case MONO_TABLE_TYPEDEF: + case MONO_TABLE_TYPEREF: + case MONO_TABLE_TYPESPEC: { + MonoType *t = module_resolve_type_token (image, token, type_args, method_args, error, merror); + if (t) { + return MONO_HANDLE_CAST (MonoObject, mono_type_get_object_handle (mono_domain_get (), t, merror)); + } + else + return NULL_HANDLE; + } + case MONO_TABLE_METHOD: + case MONO_TABLE_METHODSPEC: { + MonoMethod *m = module_resolve_method_token (image, token, type_args, method_args, error, merror); + if (m) { + return MONO_HANDLE_CAST (MonoObject, mono_method_get_object_handle (mono_domain_get (), m, m->klass, merror)); + } else + return NULL_HANDLE; + } + case MONO_TABLE_FIELD: { + MonoClassField *f = module_resolve_field_token (image, token, type_args, method_args, error, merror); + if (f) { + return MONO_HANDLE_CAST (MonoObject, mono_field_get_object_handle (mono_domain_get (), f->parent, f, merror)); + } + else + return NULL_HANDLE; + } + case MONO_TABLE_MEMBERREF: + if (mono_memberref_is_method (image, token)) { + MonoMethod *m = module_resolve_method_token (image, token, type_args, method_args, error, merror); + if (m) { + return MONO_HANDLE_CAST (MonoObject, mono_method_get_object_handle (mono_domain_get (), m, m->klass, merror)); + } else + return NULL_HANDLE; + } + else { + MonoClassField *f = module_resolve_field_token (image, token, type_args, method_args, error, merror); + if (f) { + return MONO_HANDLE_CAST (MonoObject, mono_field_get_object_handle (mono_domain_get (), f->parent, f, merror)); + } + else + return NULL_HANDLE; + } + break; + + default: + *error = ResolveTokenError_BadTable; + } + + return NULL_HANDLE; +} + +ICALL_EXPORT MonoArrayHandle +ves_icall_System_Reflection_Module_ResolveSignature (MonoImage *image, guint32 token, MonoResolveTokenError *resolve_error, MonoError *error) +{ + error_init (error); + int table = mono_metadata_token_table (token); + int idx = mono_metadata_token_index (token); + MonoTableInfo *tables = image->tables; + guint32 sig, len; + const char *ptr; + + *resolve_error = ResolveTokenError_OutOfRange; + + /* FIXME: Support other tables ? */ + if (table != MONO_TABLE_STANDALONESIG) + return MONO_HANDLE_CAST (MonoArray, NULL); + + if (image_is_dynamic (image)) + return MONO_HANDLE_CAST (MonoArray, NULL); + + if ((idx == 0) || (idx > tables [MONO_TABLE_STANDALONESIG].rows)) + return MONO_HANDLE_CAST (MonoArray, NULL); + + sig = mono_metadata_decode_row_col (&tables [MONO_TABLE_STANDALONESIG], idx - 1, 0); + + ptr = mono_metadata_blob_heap (image, sig); + len = mono_metadata_decode_blob_size (ptr, &ptr); + + MonoArrayHandle res = mono_array_new_handle (mono_domain_get (), mono_defaults.byte_class, len, error); + if (!is_ok (error)) + return MONO_HANDLE_CAST (MonoArray, NULL); + uint32_t h; + gpointer array_base = MONO_ARRAY_HANDLE_PIN (res, guint8, 0, &h); + memcpy (array_base, ptr, len); + mono_gchandle_free (h); + return res; +} + +ICALL_EXPORT MonoBoolean +ves_icall_RuntimeTypeHandle_IsArray (MonoReflectionTypeHandle ref_type, MonoError *error) +{ + error_init (error); + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + + MonoBoolean res = !type->byref && (type->type == MONO_TYPE_ARRAY || type->type == MONO_TYPE_SZARRAY); + + return res; +} + +static void +check_for_invalid_type (MonoClass *klass, MonoError *error) +{ + char *name; + + error_init (error); + + if (klass->byval_arg.type != MONO_TYPE_TYPEDBYREF) + return; + + name = mono_type_get_full_name (klass); + mono_error_set_type_load_name (error, name, g_strdup (""), ""); +} + +ICALL_EXPORT MonoReflectionTypeHandle +ves_icall_RuntimeType_make_array_type (MonoReflectionTypeHandle ref_type, int rank, MonoError *error) +{ + error_init (error); + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + + MonoClass *klass = mono_class_from_mono_type (type); + check_for_invalid_type (klass, error); + if (!is_ok (error)) + return MONO_HANDLE_CAST (MonoReflectionType, NULL_HANDLE); + + MonoClass *aklass; + if (rank == 0) //single dimentional array + aklass = mono_array_class_get (klass, 1); + else + aklass = mono_bounded_array_class_get (klass, rank, TRUE); + + if (mono_class_has_failure (aklass)) { + mono_error_set_for_class_failure (error, aklass); + return MONO_HANDLE_CAST (MonoReflectionType, NULL_HANDLE); + } + + MonoDomain *domain = MONO_HANDLE_DOMAIN (ref_type); + return mono_type_get_object_handle (domain, &aklass->byval_arg, error); +} + +ICALL_EXPORT MonoReflectionTypeHandle +ves_icall_RuntimeType_make_byref_type (MonoReflectionTypeHandle ref_type, MonoError *error) +{ + error_init (error); + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + + MonoClass *klass = mono_class_from_mono_type (type); + mono_class_init_checked (klass, error); + if (!is_ok (error)) + return MONO_HANDLE_CAST (MonoReflectionType, NULL_HANDLE); + + check_for_invalid_type (klass, error); + if (!is_ok (error)) + return MONO_HANDLE_CAST (MonoReflectionType, NULL_HANDLE); + + MonoDomain *domain = MONO_HANDLE_DOMAIN (ref_type); + return mono_type_get_object_handle (domain, &klass->this_arg, error); +} + +ICALL_EXPORT MonoReflectionTypeHandle +ves_icall_RuntimeType_MakePointerType (MonoReflectionTypeHandle ref_type, MonoError *error) +{ + error_init (error); + MonoDomain *domain = MONO_HANDLE_DOMAIN (ref_type); + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + MonoClass *klass = mono_class_from_mono_type (type); + mono_class_init_checked (klass, error); + if (!is_ok (error)) + return MONO_HANDLE_CAST (MonoReflectionType, NULL_HANDLE); + + check_for_invalid_type (klass, error); + if (!is_ok (error)) + return MONO_HANDLE_CAST (MonoReflectionType, NULL_HANDLE); + + MonoClass *pklass = mono_ptr_class_get (type); + + return mono_type_get_object_handle (domain, &pklass->byval_arg, error); +} + +ICALL_EXPORT MonoObjectHandle +ves_icall_System_Delegate_CreateDelegate_internal (MonoReflectionTypeHandle ref_type, MonoObjectHandle target, + MonoReflectionMethodHandle info, MonoBoolean throwOnBindFailure, MonoError *error) +{ + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + MonoClass *delegate_class = mono_class_from_mono_type (type); + gpointer func; + MonoMethod *method = MONO_HANDLE_GETVAL (info, method); + MonoMethodSignature *sig = mono_method_signature(method); + + mono_class_init_checked (delegate_class, error); + return_val_if_nok (error, NULL_HANDLE); + + if (!(delegate_class->parent == mono_defaults.multicastdelegate_class)) { + /* FIXME improve this exception message */ + mono_error_set_execution_engine (error, "file %s: line %d (%s): assertion failed: (%s)", __FILE__, __LINE__, + __func__, + "delegate_class->parent == mono_defaults.multicastdelegate_class"); + return NULL_HANDLE; + } + + if (mono_security_core_clr_enabled ()) { + MonoError security_error; + if (!mono_security_core_clr_ensure_delegate_creation (method, &security_error)) { + if (throwOnBindFailure) + mono_error_move (error, &security_error); + else + mono_error_cleanup (&security_error); + return NULL_HANDLE; + } + } + + if (sig->generic_param_count && method->wrapper_type == MONO_WRAPPER_NONE) { + if (!method->is_inflated) { + mono_error_set_argument (error, "method", " Cannot bind to the target method because its signature differs from that of the delegate type"); + return NULL_HANDLE; + } + } + + MonoObjectHandle delegate = MONO_HANDLE_NEW (MonoObject, mono_object_new_checked (MONO_HANDLE_DOMAIN (ref_type), delegate_class, error)); + return_val_if_nok (error, NULL_HANDLE); + + if (method_is_dynamic (method)) { + /* Creating a trampoline would leak memory */ + func = mono_compile_method_checked (method, error); + return_val_if_nok (error, NULL_HANDLE); + } else { + if (!MONO_HANDLE_IS_NULL (target) && method->flags & METHOD_ATTRIBUTE_VIRTUAL && method->klass != mono_handle_class (target)) { + method = mono_object_handle_get_virtual_method (target, method, error); + return_val_if_nok (error, NULL_HANDLE); + } + gpointer trampoline = mono_runtime_create_jump_trampoline (mono_domain_get (), method, TRUE, error); + return_val_if_nok (error, NULL_HANDLE); + func = mono_create_ftnptr (mono_domain_get (), trampoline); + } + + mono_delegate_ctor_with_method (delegate, target, func, method, error); + return_val_if_nok (error, NULL_HANDLE); + return delegate; +} + +ICALL_EXPORT MonoMulticastDelegateHandle +ves_icall_System_Delegate_AllocDelegateLike_internal (MonoDelegateHandle delegate, MonoError *error) +{ + error_init (error); + + MonoClass *klass = mono_handle_class (delegate); + g_assert (mono_class_has_parent (klass, mono_defaults.multicastdelegate_class)); + + MonoMulticastDelegateHandle ret = MONO_HANDLE_NEW (MonoMulticastDelegate, mono_object_new_checked (MONO_HANDLE_DOMAIN (delegate), klass, error)); + return_val_if_nok (error, MONO_HANDLE_CAST (MonoMulticastDelegate, NULL_HANDLE)); + + MONO_HANDLE_SETVAL (MONO_HANDLE_CAST (MonoDelegate, ret), invoke_impl, gpointer, mono_runtime_create_delegate_trampoline (klass)); + + return ret; +} + +ICALL_EXPORT MonoReflectionMethodHandle +ves_icall_System_Delegate_GetVirtualMethod_internal (MonoDelegateHandle delegate, MonoError *error) +{ + error_init (error); + + MonoObjectHandle delegate_target = MONO_HANDLE_NEW_GET (MonoObject, delegate, target); + MonoMethod *m = mono_object_handle_get_virtual_method (delegate_target, MONO_HANDLE_GETVAL (delegate, method), error); + return_val_if_nok (error, MONO_HANDLE_CAST (MonoReflectionMethod, NULL_HANDLE)); + return mono_method_get_object_handle (mono_domain_get (), m, m->klass, error); +} + +/* System.Buffer */ + +static inline gint32 +mono_array_get_byte_length (MonoArray *array) +{ + MonoClass *klass; + int length; + int i; + + klass = array->obj.vtable->klass; + + if (array->bounds == NULL) + length = array->max_length; + else { + length = 1; + for (i = 0; i < klass->rank; ++ i) + length *= array->bounds [i].length; + } + + switch (klass->element_class->byval_arg.type) { + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_BOOLEAN: + return length; + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_CHAR: + return length << 1; + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_R4: + return length << 2; + case MONO_TYPE_I: + case MONO_TYPE_U: + return length * sizeof (gpointer); + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_R8: + return length << 3; + default: + return -1; + } +} + +ICALL_EXPORT gint32 +ves_icall_System_Buffer_ByteLengthInternal (MonoArray *array) +{ + return mono_array_get_byte_length (array); +} + +ICALL_EXPORT gint8 +ves_icall_System_Buffer_GetByteInternal (MonoArray *array, gint32 idx) +{ + return mono_array_get (array, gint8, idx); +} + +ICALL_EXPORT void +ves_icall_System_Buffer_SetByteInternal (MonoArray *array, gint32 idx, gint8 value) +{ + mono_array_set (array, gint8, idx, value); +} + +ICALL_EXPORT MonoBoolean +ves_icall_System_Buffer_BlockCopyInternal (MonoArray *src, gint32 src_offset, MonoArray *dest, gint32 dest_offset, gint32 count) +{ + guint8 *src_buf, *dest_buf; + + if (count < 0) { + mono_set_pending_exception (mono_get_exception_argument ("count", "is negative")); + return FALSE; + } + + g_assert (count >= 0); + + /* This is called directly from the class libraries without going through the managed wrapper */ + MONO_CHECK_ARG_NULL (src, FALSE); + MONO_CHECK_ARG_NULL (dest, FALSE); + + /* watch out for integer overflow */ + if ((src_offset > mono_array_get_byte_length (src) - count) || (dest_offset > mono_array_get_byte_length (dest) - count)) + return FALSE; + + src_buf = (guint8 *)src->vector + src_offset; + dest_buf = (guint8 *)dest->vector + dest_offset; + + if (src != dest) + memcpy (dest_buf, src_buf, count); + else + memmove (dest_buf, src_buf, count); /* Source and dest are the same array */ + + return TRUE; +} + +#ifndef DISABLE_REMOTING +ICALL_EXPORT MonoObjectHandle +ves_icall_Remoting_RealProxy_GetTransparentProxy (MonoObjectHandle this_obj, MonoStringHandle class_name, MonoError *error) +{ + error_init (error); + MonoDomain *domain = MONO_HANDLE_DOMAIN (this_obj); + MonoRealProxyHandle rp = MONO_HANDLE_CAST (MonoRealProxy, this_obj); + + MonoObjectHandle res = MONO_HANDLE_NEW (MonoObject, mono_object_new_checked (domain, mono_defaults.transparent_proxy_class, error)); + if (!is_ok (error)) + return NULL_HANDLE; + + MonoTransparentProxyHandle tp = MONO_HANDLE_CAST (MonoTransparentProxy, res); + + MONO_HANDLE_SET (tp, rp, rp); + + MonoReflectionTypeHandle reftype = MONO_HANDLE_NEW (MonoReflectionType, NULL); + MONO_HANDLE_GET (reftype, rp, class_to_proxy); + MonoType *type = MONO_HANDLE_GETVAL (reftype, type); + MonoClass *klass = mono_class_from_mono_type (type); + + // mono_remote_class_vtable cannot handle errors well, so force any loading error to occur early + mono_class_setup_vtable (klass); + if (mono_class_has_failure (klass)) { + mono_error_set_for_class_failure (error, klass); + return NULL_HANDLE; + } + + MonoObjectHandle remoting_obj = mono_object_handle_isinst (this_obj, mono_defaults.iremotingtypeinfo_class, error); + if (!is_ok (error)) + return NULL_HANDLE; + MONO_HANDLE_SETVAL (tp, custom_type_info, MonoBoolean, !MONO_HANDLE_IS_NULL (remoting_obj)); + + MonoRemoteClass *remote_class = mono_remote_class (domain, class_name, klass, error); + if (!is_ok (error)) + return NULL_HANDLE; + MONO_HANDLE_SETVAL (tp, remote_class, MonoRemoteClass*, remote_class); + + MONO_HANDLE_SETVAL (res, vtable, MonoVTable*, mono_remote_class_vtable (domain, remote_class, rp, error)); + if (!is_ok (error)) + return NULL_HANDLE; + return res; +} + +ICALL_EXPORT MonoReflectionType * +ves_icall_Remoting_RealProxy_InternalGetProxyType (MonoTransparentProxy *tp) +{ + MonoError error; + g_assert (tp != NULL && mono_object_class (tp) == mono_defaults.transparent_proxy_class); + g_assert (tp->remote_class != NULL && tp->remote_class->proxy_class != NULL); + MonoReflectionType *ret = mono_type_get_object_checked (mono_object_domain (tp), &tp->remote_class->proxy_class->byval_arg, &error); + mono_error_set_pending_exception (&error); + + return ret; +} +#endif + +/* System.Environment */ + +MonoStringHandle +ves_icall_System_Environment_get_UserName (MonoError *error) +{ + error_init (error); + /* using glib is more portable */ + return mono_string_new_handle (mono_domain_get (), g_get_user_name (), error); +} + +#ifndef HOST_WIN32 +static MonoStringHandle +mono_icall_get_machine_name (MonoError *error) +{ + error_init (error); +#if !defined(DISABLE_SOCKETS) + MonoStringHandle result; + char *buf; + int n; +#if defined _SC_HOST_NAME_MAX + n = sysconf (_SC_HOST_NAME_MAX); + if (n == -1) +#endif + n = 512; + buf = g_malloc (n+1); + + if (gethostname (buf, n) == 0){ + buf [n] = 0; + result = mono_string_new_handle (mono_domain_get (), buf, error); + } else + result = MONO_HANDLE_CAST (MonoString, NULL_HANDLE); + g_free (buf); + + return result; +#else + return mono_string_new_handle (mono_domain_get (), "mono", error); +#endif +} +#endif /* !HOST_WIN32 */ + +ICALL_EXPORT MonoStringHandle +ves_icall_System_Environment_get_MachineName (MonoError *error) +{ + error_init (error); + return mono_icall_get_machine_name (error); +} + +#ifndef HOST_WIN32 +static inline int +mono_icall_get_platform (void) +{ +#if defined(__MACH__) + /* OSX */ + // + // Notice that the value is hidden from user code, and only exposed + // to mscorlib. This is due to Mono's Unix/MacOS code predating the + // define and making assumptions based on Unix/128/4 values before there + // was a MacOS define. Lots of code would assume that not-Unix meant + // Windows, but in this case, it would be OSX. + // + return 6; +#else + /* Unix */ + return 4; +#endif +} +#endif /* !HOST_WIN32 */ + +ICALL_EXPORT int +ves_icall_System_Environment_get_Platform (void) +{ + return mono_icall_get_platform (); +} + +#ifndef HOST_WIN32 +static inline MonoStringHandle +mono_icall_get_new_line (MonoError *error) +{ + error_init (error); + return mono_string_new_handle (mono_domain_get (), "\n", error); +} +#endif /* !HOST_WIN32 */ + +ICALL_EXPORT MonoStringHandle +ves_icall_System_Environment_get_NewLine (MonoError *error) +{ + return mono_icall_get_new_line (error); +} + +#ifndef HOST_WIN32 +static inline MonoBoolean +mono_icall_is_64bit_os (void) +{ +#if SIZEOF_VOID_P == 8 + return TRUE; +#else +#if defined(HAVE_SYS_UTSNAME_H) + struct utsname name; + + if (uname (&name) >= 0) { + return strcmp (name.machine, "x86_64") == 0 || strncmp (name.machine, "aarch64", 7) == 0 || strncmp (name.machine, "ppc64", 5) == 0; + } +#endif + return FALSE; +#endif +} +#endif /* !HOST_WIN32 */ + +ICALL_EXPORT MonoBoolean +ves_icall_System_Environment_GetIs64BitOperatingSystem (void) +{ + return mono_icall_is_64bit_os (); +} + +ICALL_EXPORT MonoStringHandle +ves_icall_System_Environment_GetEnvironmentVariable_native (const gchar *utf8_name, MonoError *error) +{ + gchar *value; + + if (utf8_name == NULL) + return NULL_HANDLE_STRING; + + value = g_getenv (utf8_name); + + if (value == 0) + return NULL_HANDLE_STRING; + + MonoStringHandle res = mono_string_new_handle (mono_domain_get (), value, error); + g_free (value); + return res; +} + +/* + * There is no standard way to get at environ. + */ +#ifndef _MSC_VER +#ifndef __MINGW32_VERSION +#if defined(__APPLE__) +#if defined (TARGET_OSX) +/* Apple defines this in crt_externs.h but doesn't provide that header for + * arm-apple-darwin9. We'll manually define the symbol on Apple as it does + * in fact exist on all implementations (so far) + */ +gchar ***_NSGetEnviron(void); +#define environ (*_NSGetEnviron()) +#else +static char *mono_environ[1] = { NULL }; +#define environ mono_environ +#endif /* defined (TARGET_OSX) */ +#else +extern +char **environ; +#endif +#endif +#endif + +ICALL_EXPORT MonoArrayHandle +ves_icall_System_Environment_GetCommandLineArgs (MonoError *error) +{ + error_init (error); + MonoArrayHandle result = mono_runtime_get_main_args_handle (error); + return result; +} + +#ifndef HOST_WIN32 +static MonoArray * +mono_icall_get_environment_variable_names (MonoError *error) +{ + MonoArray *names; + MonoDomain *domain; + MonoString *str; + gchar **e, **parts; + int n; + + error_init (error); + n = 0; + for (e = environ; *e != 0; ++ e) + ++ n; + + domain = mono_domain_get (); + names = mono_array_new_checked (domain, mono_defaults.string_class, n, error); + return_val_if_nok (error, NULL); + + n = 0; + for (e = environ; *e != 0; ++ e) { + parts = g_strsplit (*e, "=", 2); + if (*parts != 0) { + str = mono_string_new_checked (domain, *parts, error); + if (!is_ok (error)) { + g_strfreev (parts); + return NULL; + } + mono_array_setref (names, n, str); + } + + g_strfreev (parts); + + ++ n; + } + + return names; +} +#endif /* !HOST_WIN32 */ + +ICALL_EXPORT MonoArray * +ves_icall_System_Environment_GetEnvironmentVariableNames (void) +{ + MonoError error; + MonoArray *result = mono_icall_get_environment_variable_names (&error); + mono_error_set_pending_exception (&error); + return result; +} + +#ifndef HOST_WIN32 +static void +mono_icall_set_environment_variable (MonoString *name, MonoString *value) +{ + gchar *utf8_name, *utf8_value; + MonoError error; + + utf8_name = mono_string_to_utf8_checked (name, &error); /* FIXME: this should be ascii */ + if (mono_error_set_pending_exception (&error)) + return; + + if ((value == NULL) || (mono_string_length (value) == 0) || (mono_string_chars (value)[0] == 0)) { + g_unsetenv (utf8_name); + g_free (utf8_name); + return; + } + + utf8_value = mono_string_to_utf8_checked (value, &error); + if (!mono_error_ok (&error)) { + g_free (utf8_name); + mono_error_set_pending_exception (&error); + return; + } + g_setenv (utf8_name, utf8_value, TRUE); + + g_free (utf8_name); + g_free (utf8_value); +} +#endif /* !HOST_WIN32 */ + +ICALL_EXPORT void +ves_icall_System_Environment_InternalSetEnvironmentVariable (MonoString *name, MonoString *value) +{ + mono_icall_set_environment_variable (name, value); +} + +ICALL_EXPORT void +ves_icall_System_Environment_Exit (int result) +{ + mono_environment_exitcode_set (result); + + if (!mono_runtime_try_shutdown ()) + mono_thread_exit (); + + /* Suspend all managed threads since the runtime is going away */ + mono_thread_suspend_all_other_threads (); + + mono_runtime_quit (); + + /* we may need to do some cleanup here... */ + exit (result); +} + +ICALL_EXPORT MonoStringHandle +ves_icall_System_Environment_GetGacPath (MonoError *error) +{ + return mono_string_new_handle (mono_domain_get (), mono_assembly_getrootdir (), error); +} + +#ifndef HOST_WIN32 +static inline MonoStringHandle +mono_icall_get_windows_folder_path (int folder, MonoError *error) +{ + error_init (error); + g_warning ("ves_icall_System_Environment_GetWindowsFolderPath should only be called on Windows!"); + return mono_string_new_handle (mono_domain_get (), "", error); +} +#endif /* !HOST_WIN32 */ + +ICALL_EXPORT MonoStringHandle +ves_icall_System_Environment_GetWindowsFolderPath (int folder, MonoError *error) +{ + return mono_icall_get_windows_folder_path (folder, error); +} + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +static MonoArray * +mono_icall_get_logical_drives (void) +{ + MonoError error; + gunichar2 buf [256], *ptr, *dname; + gunichar2 *u16; + guint initial_size = 127, size = 128; + gint ndrives; + MonoArray *result; + MonoString *drivestr; + MonoDomain *domain = mono_domain_get (); + gint len; + + buf [0] = '\0'; + ptr = buf; + + while (size > initial_size) { + size = (guint) mono_w32file_get_logical_drive (initial_size, ptr); + if (size > initial_size) { + if (ptr != buf) + g_free (ptr); + ptr = (gunichar2 *)g_malloc0 ((size + 1) * sizeof (gunichar2)); + initial_size = size; + size++; + } + } + + /* Count strings */ + dname = ptr; + ndrives = 0; + do { + while (*dname++); + ndrives++; + } while (*dname); + + dname = ptr; + result = mono_array_new_checked (domain, mono_defaults.string_class, ndrives, &error); + if (mono_error_set_pending_exception (&error)) + goto leave; + + ndrives = 0; + do { + len = 0; + u16 = dname; + while (*u16) { u16++; len ++; } + drivestr = mono_string_new_utf16_checked (domain, dname, len, &error); + if (mono_error_set_pending_exception (&error)) + goto leave; + + mono_array_setref (result, ndrives++, drivestr); + while (*dname++); + } while (*dname); + +leave: + if (ptr != buf) + g_free (ptr); + + return result; +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ + +ICALL_EXPORT MonoArray * +ves_icall_System_Environment_GetLogicalDrives (void) +{ + return mono_icall_get_logical_drives (); +} + +ICALL_EXPORT MonoString * +ves_icall_System_IO_DriveInfo_GetDriveFormat (MonoString *path) +{ + MonoError error; + gunichar2 volume_name [MAX_PATH + 1]; + + if (mono_w32file_get_volume_information (mono_string_chars (path), NULL, 0, NULL, NULL, NULL, volume_name, MAX_PATH + 1) == FALSE) + return NULL; + MonoString *result = mono_string_from_utf16_checked (volume_name, &error); + mono_error_set_pending_exception (&error); + return result; +} + +ICALL_EXPORT MonoStringHandle +ves_icall_System_Environment_InternalGetHome (MonoError *error) +{ + return mono_string_new_handle (mono_domain_get (), g_get_home_dir (), error); +} + +static const char *encodings [] = { + (char *) 1, + "ascii", "us_ascii", "us", "ansi_x3.4_1968", + "ansi_x3.4_1986", "cp367", "csascii", "ibm367", + "iso_ir_6", "iso646_us", "iso_646.irv:1991", + (char *) 2, + "utf_7", "csunicode11utf7", "unicode_1_1_utf_7", + "unicode_2_0_utf_7", "x_unicode_1_1_utf_7", + "x_unicode_2_0_utf_7", + (char *) 3, + "utf_8", "unicode_1_1_utf_8", "unicode_2_0_utf_8", + "x_unicode_1_1_utf_8", "x_unicode_2_0_utf_8", + (char *) 4, + "utf_16", "UTF_16LE", "ucs_2", "unicode", + "iso_10646_ucs2", + (char *) 5, + "unicodefffe", "utf_16be", + (char *) 6, + "iso_8859_1", + (char *) 0 +}; + +/* + * Returns the internal codepage, if the value of "int_code_page" is + * 1 at entry, and we can not compute a suitable code page number, + * returns the code page as a string + */ +ICALL_EXPORT MonoStringHandle +ves_icall_System_Text_EncodingHelper_InternalCodePage (gint32 *int_code_page, MonoError *error) +{ + error_init (error); + const char *cset; + const char *p; + char *c; + char *codepage = NULL; + int code; + int want_name = *int_code_page; + int i; + + *int_code_page = -1; + + g_get_charset (&cset); + c = codepage = g_strdup (cset); + for (c = codepage; *c; c++){ + if (isascii (*c) && isalpha (*c)) + *c = tolower (*c); + if (*c == '-') + *c = '_'; + } + /* g_print ("charset: %s\n", cset); */ + + /* handle some common aliases */ + p = encodings [0]; + code = 0; + for (i = 0; p != 0; ){ + if ((gsize) p < 7){ + code = (gssize) p; + p = encodings [++i]; + continue; + } + if (strcmp (p, codepage) == 0){ + *int_code_page = code; + break; + } + p = encodings [++i]; + } + + if (strstr (codepage, "utf_8") != NULL) + *int_code_page |= 0x10000000; + g_free (codepage); + + if (want_name && *int_code_page == -1) + return mono_string_new_handle (mono_domain_get (), cset, error); + else + return MONO_HANDLE_CAST (MonoString, NULL_HANDLE); +} + +ICALL_EXPORT MonoBoolean +ves_icall_System_Environment_get_HasShutdownStarted (void) +{ + if (mono_runtime_is_shutting_down ()) + return TRUE; + + if (mono_domain_is_unloading (mono_domain_get ())) + return TRUE; + + return FALSE; +} + +#ifndef HOST_WIN32 +static inline MonoBoolean +mono_icall_broadcast_setting_change (MonoError *error) +{ + error_init (error); + return TRUE; +} +#endif /* !HOST_WIN32 */ + +ICALL_EXPORT void +ves_icall_System_Environment_BroadcastSettingChange (MonoError *error) +{ + error_init (error); + mono_icall_broadcast_setting_change (error); +} + +ICALL_EXPORT +gint32 +ves_icall_System_Environment_get_TickCount (void) +{ + /* this will overflow after ~24 days */ + return (gint32) (mono_msec_boottime () & 0xffffffff); +} + +ICALL_EXPORT gint32 +ves_icall_System_Runtime_Versioning_VersioningHelper_GetRuntimeId (void) +{ + return 9; +} + +#ifndef DISABLE_REMOTING +ICALL_EXPORT MonoBoolean +ves_icall_IsTransparentProxy (MonoObjectHandle proxy, MonoError *error) +{ + error_init (error); + if (MONO_HANDLE_IS_NULL (proxy)) + return 0; + + if (mono_class_is_transparent_proxy (mono_handle_class (proxy))) + return 1; + + return 0; +} + +ICALL_EXPORT MonoReflectionMethodHandle +ves_icall_Remoting_RemotingServices_GetVirtualMethod ( + MonoReflectionTypeHandle rtype, MonoReflectionMethodHandle rmethod, MonoError *error) +{ + MonoReflectionMethodHandle ret = MONO_HANDLE_CAST (MonoReflectionMethod, NULL_HANDLE); + + error_init (error); + if (MONO_HANDLE_IS_NULL (rtype)) { + mono_error_set_argument_null (error, "type", ""); + return ret; + } + if (MONO_HANDLE_IS_NULL (rmethod)) { + mono_error_set_argument_null (error, "method", ""); + return ret; + } + + MonoMethod *method = MONO_HANDLE_GETVAL (rmethod, method); + MonoType *type = MONO_HANDLE_GETVAL (rtype, type); + MonoClass *klass = mono_class_from_mono_type (type); + mono_class_init_checked (klass, error); + return_val_if_nok (error, ret); + + if (MONO_CLASS_IS_INTERFACE (klass)) + return ret; + + if (method->flags & METHOD_ATTRIBUTE_STATIC) + return ret; + + if ((method->flags & METHOD_ATTRIBUTE_FINAL) || !(method->flags & METHOD_ATTRIBUTE_VIRTUAL)) { + if (klass == method->klass || mono_class_is_subclass_of (klass, method->klass, FALSE)) + ret = rmethod; + return ret; + } + + mono_class_setup_vtable (klass); + MonoMethod **vtable = klass->vtable; + + MonoMethod *res = NULL; + if (mono_class_is_interface (method->klass)) { + gboolean variance_used = FALSE; + /*MS fails with variant interfaces but it's the right thing to do anyway.*/ + int offs = mono_class_interface_offset_with_variance (klass, method->klass, &variance_used); + if (offs >= 0) + res = vtable [offs + method->slot]; + } else { + if (!(klass == method->klass || mono_class_is_subclass_of (klass, method->klass, FALSE))) + return ret; + + if (method->slot != -1) + res = vtable [method->slot]; + } + + if (!res) + return ret; + + ret = mono_method_get_object_handle (mono_domain_get (), res, NULL, error); + return ret; +} + +ICALL_EXPORT void +ves_icall_System_Runtime_Activation_ActivationServices_EnableProxyActivation (MonoReflectionTypeHandle type, MonoBoolean enable, MonoError *error) +{ + error_init (error); + + MonoClass *klass = mono_class_from_mono_type (MONO_HANDLE_GETVAL (type, type)); + MonoVTable *vtable = mono_class_vtable_full (mono_domain_get (), klass, error); + return_if_nok (error); + + mono_vtable_set_is_remote (vtable, enable); +} + +#else /* DISABLE_REMOTING */ + +ICALL_EXPORT void +ves_icall_System_Runtime_Activation_ActivationServices_EnableProxyActivation (MonoReflectionTypeHandle type, MonoBoolean enable, MonoError *error) +{ + error_init (error); + g_assert_not_reached (); +} + +ICALL_EXPORT MonoBoolean +ves_icall_IsTransparentProxy (MonoObject *proxy) +{ + return 0; +} + +#endif + +ICALL_EXPORT MonoObjectHandle +ves_icall_System_Runtime_Activation_ActivationServices_AllocateUninitializedClassInstance (MonoReflectionTypeHandle type, MonoError *error) +{ + error_init (error); + + MonoDomain *domain = MONO_HANDLE_DOMAIN (type); + MonoClass *klass = mono_class_from_mono_type (MONO_HANDLE_GETVAL (type, type)); + mono_class_init_checked (klass, error); + return_val_if_nok (error, NULL_HANDLE); + + if (MONO_CLASS_IS_INTERFACE (klass) || mono_class_is_abstract (klass)) { + mono_error_set_argument (error, "type", "Type cannot be instantiated"); + return NULL_HANDLE; + } + + if (klass->rank >= 1) { + g_assert (klass->rank == 1); + return MONO_HANDLE_CAST (MonoObject, mono_array_new_handle (domain, klass->element_class, 0, error)); + } else { + MonoVTable *vtable = mono_class_vtable_full (domain, klass, error); + return_val_if_nok (error, NULL_HANDLE); + + /* Bypass remoting object creation check */ + return MONO_HANDLE_NEW (MonoObject, mono_object_new_alloc_specific_checked (vtable, error)); + } +} + +ICALL_EXPORT MonoStringHandle +ves_icall_System_IO_get_temp_path (MonoError *error) +{ + return mono_string_new_handle (mono_domain_get (), g_get_tmp_dir (), error); +} + +#ifndef PLATFORM_NO_DRIVEINFO +ICALL_EXPORT MonoBoolean +ves_icall_System_IO_DriveInfo_GetDiskFreeSpace (MonoString *path_name, guint64 *free_bytes_avail, + guint64 *total_number_of_bytes, guint64 *total_number_of_free_bytes, + gint32 *error) +{ + gboolean result; + + *error = ERROR_SUCCESS; + + result = mono_w32file_get_disk_free_space (mono_string_chars (path_name), free_bytes_avail, total_number_of_bytes, total_number_of_free_bytes); + if (!result) + *error = mono_w32error_get_last (); + + return result; +} + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +static inline guint32 +mono_icall_drive_info_get_drive_type (MonoString *root_path_name) +{ + return mono_w32file_get_drive_type (mono_string_chars (root_path_name)); +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ + +ICALL_EXPORT guint32 +ves_icall_System_IO_DriveInfo_GetDriveType (MonoString *root_path_name) +{ + return mono_icall_drive_info_get_drive_type (root_path_name); +} + +#endif /* PLATFORM_NO_DRIVEINFO */ + +ICALL_EXPORT gpointer +ves_icall_RuntimeMethodHandle_GetFunctionPointer (MonoMethod *method, MonoError *error) +{ + error_init (error); + return mono_compile_method_checked (method, error); +} + +ICALL_EXPORT MonoStringHandle +ves_icall_System_Configuration_DefaultConfig_get_machine_config_path (MonoError *error) +{ + error_init (error); + gchar *path; + + path = g_build_path (G_DIR_SEPARATOR_S, mono_get_config_dir (), "mono", mono_get_runtime_info ()->framework_version, "machine.config", NULL); + + mono_icall_make_platform_path (path); + + MonoStringHandle mcpath = mono_string_new_handle (mono_domain_get (), path, error); + g_free (path); + + mono_error_assert_ok (error); + + return mcpath; +} + +static MonoStringHandle +ves_icall_System_Configuration_InternalConfigurationHost_get_bundled_app_config (MonoError *error) +{ + error_init (error); + const gchar *app_config; + MonoDomain *domain; + gchar *config_file_name, *config_file_path; + gsize len, config_file_path_length, config_ext_length; + gchar *module; + + domain = mono_domain_get (); + MonoStringHandle file = MONO_HANDLE_NEW (MonoString, domain->setup->configuration_file); + if (MONO_HANDLE_IS_NULL (file) || MONO_HANDLE_GETVAL (file, length) == 0) + return NULL; + + // Retrieve config file and remove the extension + config_file_name = mono_string_handle_to_utf8 (file, error); + return_val_if_nok (error, MONO_HANDLE_CAST (MonoString, NULL_HANDLE)); + + config_file_path = mono_portability_find_file (config_file_name, TRUE); + if (!config_file_path) + config_file_path = config_file_name; + + config_file_path_length = strlen (config_file_path); + config_ext_length = strlen (".config"); + if (config_file_path_length <= config_ext_length) { + if (config_file_name != config_file_path) + g_free (config_file_name); + return MONO_HANDLE_CAST (MonoString, NULL_HANDLE); + } + + len = config_file_path_length - config_ext_length; + module = (gchar *)g_malloc0 (len + 1); + memcpy (module, config_file_path, len); + // Get the config file from the module name + app_config = mono_config_string_for_assembly_file (module); + // Clean-up + g_free (module); + if (config_file_name != config_file_path) + g_free (config_file_name); + g_free (config_file_path); + + if (!app_config) + return MONO_HANDLE_CAST (MonoString, NULL_HANDLE); + + return mono_string_new_handle (mono_domain_get (), app_config, error); +} + +static MonoStringHandle +get_bundled_machine_config (MonoError *error) +{ + const gchar *machine_config; + + machine_config = mono_get_machine_config (); + + if (!machine_config) + return NULL_HANDLE_STRING; + + return mono_string_new_handle (mono_domain_get (), machine_config, error); +} + +ICALL_EXPORT MonoStringHandle +ves_icall_System_Environment_get_bundled_machine_config (MonoError *error) +{ + return get_bundled_machine_config (error); +} + + +ICALL_EXPORT MonoStringHandle +ves_icall_System_Configuration_DefaultConfig_get_bundled_machine_config (MonoError *error) +{ + return get_bundled_machine_config (error); +} + +ICALL_EXPORT MonoStringHandle +ves_icall_System_Configuration_InternalConfigurationHost_get_bundled_machine_config (MonoError *error) +{ + return get_bundled_machine_config (error); +} + + +ICALL_EXPORT MonoStringHandle +ves_icall_System_Web_Util_ICalls_get_machine_install_dir (MonoError *error) +{ + error_init (error); + gchar *path; + + path = g_path_get_dirname (mono_get_config_dir ()); + + mono_icall_make_platform_path (path); + + MonoStringHandle ipath = mono_string_new_handle (mono_domain_get (), path, error); + g_free (path); + + return ipath; +} + +ICALL_EXPORT gboolean +ves_icall_get_resources_ptr (MonoReflectionAssemblyHandle assembly, gpointer *result, gint32 *size, MonoError *error) +{ + error_init (error); + MonoPEResourceDataEntry *entry; + MonoImage *image; + + if (!assembly || !result || !size) + return FALSE; + + *result = NULL; + *size = 0; + MonoAssembly *assm = MONO_HANDLE_GETVAL (assembly, assembly); + image = assm->image; + entry = (MonoPEResourceDataEntry *)mono_image_lookup_resource (image, MONO_PE_RESOURCE_ID_ASPNET_STRING, 0, NULL); + if (!entry) + return FALSE; + + *result = mono_image_rva_map (image, entry->rde_data_offset); + if (!(*result)) { + g_free (entry); + return FALSE; + } + *size = entry->rde_size; + g_free (entry); + return TRUE; +} + +ICALL_EXPORT MonoBoolean +ves_icall_System_Diagnostics_Debugger_IsAttached_internal (void) +{ + return mono_is_debugger_attached (); +} + +ICALL_EXPORT MonoBoolean +ves_icall_System_Diagnostics_Debugger_IsLogging (void) +{ + if (mono_get_runtime_callbacks ()->debug_log_is_enabled) + return mono_get_runtime_callbacks ()->debug_log_is_enabled (); + else + return FALSE; +} + +ICALL_EXPORT void +ves_icall_System_Diagnostics_Debugger_Log (int level, MonoString *category, MonoString *message) +{ + if (mono_get_runtime_callbacks ()->debug_log) + mono_get_runtime_callbacks ()->debug_log (level, category, message); +} + +#ifndef HOST_WIN32 +static inline void +mono_icall_write_windows_debug_string (MonoString *message) +{ + g_warning ("WriteWindowsDebugString called and HOST_WIN32 not defined!\n"); +} +#endif /* !HOST_WIN32 */ + +ICALL_EXPORT void +ves_icall_System_Diagnostics_DefaultTraceListener_WriteWindowsDebugString (MonoString *message) +{ + mono_icall_write_windows_debug_string (message); +} + +/* Only used for value types */ +ICALL_EXPORT MonoObjectHandle +ves_icall_System_Activator_CreateInstanceInternal (MonoReflectionTypeHandle ref_type, MonoError *error) +{ + error_init (error); + MonoDomain *domain = MONO_HANDLE_DOMAIN (ref_type); + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + MonoClass *klass = mono_class_from_mono_type (type); + + mono_class_init_checked (klass, error); + if (!is_ok (error)) + return NULL_HANDLE; + + if (mono_class_is_nullable (klass)) + /* No arguments -> null */ + return NULL_HANDLE; + + return MONO_HANDLE_NEW (MonoObject, mono_object_new_checked (domain, klass, error)); +} + +ICALL_EXPORT MonoReflectionMethodHandle +ves_icall_MonoMethod_get_base_method (MonoReflectionMethodHandle m, gboolean definition, MonoError *error) +{ + error_init (error); + MonoMethod *method = MONO_HANDLE_GETVAL (m, method); + + MonoMethod *base = mono_method_get_base_method (method, definition, error); + return_val_if_nok (error, MONO_HANDLE_CAST (MonoReflectionMethod, NULL_HANDLE)); + if (base == method) { + /* we want to short-circuit and return 'm' here. But we should + return the same method object that + mono_method_get_object_handle, below would return. Since + that call takes NULL for the reftype argument, it will take + base->klass as the reflected type for the MonoMethod. So we + need to check that m also has base->klass as the reflected + type. */ + MonoReflectionTypeHandle orig_reftype = MONO_HANDLE_NEW_GET (MonoReflectionType, m, reftype); + MonoClass *orig_klass = mono_class_from_mono_type (MONO_HANDLE_GETVAL (orig_reftype, type)); + if (base->klass == orig_klass) + return m; + } + return mono_method_get_object_handle (mono_domain_get (), base, NULL, error); +} + +ICALL_EXPORT MonoStringHandle +ves_icall_MonoMethod_get_name (MonoReflectionMethodHandle m, MonoError *error) +{ + error_init (error); + MonoMethod *method = MONO_HANDLE_GETVAL (m, method); + + MonoStringHandle s = mono_string_new_handle (MONO_HANDLE_DOMAIN (m), method->name, error); + if (!is_ok (error)) + return NULL_HANDLE_STRING; + MONO_HANDLE_SET (m, name, s); + return s; +} + +ICALL_EXPORT void +mono_ArgIterator_Setup (MonoArgIterator *iter, char* argsp, char* start) +{ + iter->sig = *(MonoMethodSignature**)argsp; + + g_assert (iter->sig->sentinelpos <= iter->sig->param_count); + g_assert (iter->sig->call_convention == MONO_CALL_VARARG); + + iter->next_arg = 0; + /* FIXME: it's not documented what start is exactly... */ + if (start) { + iter->args = start; + } else { + iter->args = argsp + sizeof (gpointer); + } + iter->num_args = iter->sig->param_count - iter->sig->sentinelpos; + + /* g_print ("sig %p, param_count: %d, sent: %d\n", iter->sig, iter->sig->param_count, iter->sig->sentinelpos); */ +} + +ICALL_EXPORT MonoTypedRef +mono_ArgIterator_IntGetNextArg (MonoArgIterator *iter) +{ + guint32 i, arg_size; + gint32 align; + MonoTypedRef res; + + i = iter->sig->sentinelpos + iter->next_arg; + + g_assert (i < iter->sig->param_count); + + res.type = iter->sig->params [i]; + res.klass = mono_class_from_mono_type (res.type); + arg_size = mono_type_stack_size (res.type, &align); +#if defined(__arm__) || defined(__mips__) + iter->args = (guint8*)(((gsize)iter->args + (align) - 1) & ~(align - 1)); +#endif + res.value = iter->args; +#if G_BYTE_ORDER != G_LITTLE_ENDIAN + if (arg_size <= sizeof (gpointer)) { + int dummy; + int padding = arg_size - mono_type_size (res.type, &dummy); + res.value = (guint8*)res.value + padding; + } +#endif + iter->args = (char*)iter->args + arg_size; + iter->next_arg++; + + /* g_print ("returning arg %d, type 0x%02x of size %d at %p\n", i, res.type->type, arg_size, res.value); */ + + return res; +} + +ICALL_EXPORT MonoTypedRef +mono_ArgIterator_IntGetNextArgT (MonoArgIterator *iter, MonoType *type) +{ + guint32 i, arg_size; + gint32 align; + MonoTypedRef res; + + i = iter->sig->sentinelpos + iter->next_arg; + + g_assert (i < iter->sig->param_count); + + while (i < iter->sig->param_count) { + if (!mono_metadata_type_equal (type, iter->sig->params [i])) + continue; + res.type = iter->sig->params [i]; + res.klass = mono_class_from_mono_type (res.type); + /* FIXME: endianess issue... */ + arg_size = mono_type_stack_size (res.type, &align); +#if defined(__arm__) || defined(__mips__) + iter->args = (guint8*)(((gsize)iter->args + (align) - 1) & ~(align - 1)); +#endif + res.value = iter->args; + iter->args = (char*)iter->args + arg_size; + iter->next_arg++; + /* g_print ("returning arg %d, type 0x%02x of size %d at %p\n", i, res.type->type, arg_size, res.value); */ + return res; + } + /* g_print ("arg type 0x%02x not found\n", res.type->type); */ + + res.type = NULL; + res.value = NULL; + res.klass = NULL; + return res; +} + +ICALL_EXPORT MonoType* +mono_ArgIterator_IntGetNextArgType (MonoArgIterator *iter) +{ + gint i; + + i = iter->sig->sentinelpos + iter->next_arg; + + g_assert (i < iter->sig->param_count); + + return iter->sig->params [i]; +} + +ICALL_EXPORT MonoObject* +mono_TypedReference_ToObject (MonoTypedRef* tref) +{ + MonoError error; + MonoObject *result = NULL; + if (MONO_TYPE_IS_REFERENCE (tref->type)) { + MonoObject** objp = (MonoObject **)tref->value; + return *objp; + } + + result = mono_value_box_checked (mono_domain_get (), tref->klass, tref->value, &error); + mono_error_set_pending_exception (&error); + return result; +} + +ICALL_EXPORT MonoTypedRef +mono_TypedReference_MakeTypedReferenceInternal (MonoObject *target, MonoArray *fields) +{ + MonoTypedRef res; + MonoReflectionField *f; + MonoClass *klass; + MonoType *ftype = NULL; + guint8 *p = NULL; + int i; + + memset (&res, 0, sizeof (res)); + + g_assert (fields); + g_assert (mono_array_length (fields) > 0); + + klass = target->vtable->klass; + + for (i = 0; i < mono_array_length (fields); ++i) { + f = mono_array_get (fields, MonoReflectionField*, i); + + g_assert (f); + + if (i == 0) + p = (guint8*)target + f->field->offset; + else + p += f->field->offset - sizeof (MonoObject); + klass = mono_class_from_mono_type (f->field->type); + ftype = f->field->type; + } + + res.type = ftype; + res.klass = mono_class_from_mono_type (ftype); + res.value = p; + + return res; +} + +static void +prelink_method (MonoMethod *method, MonoError *error) +{ + const char *exc_class, *exc_arg; + + error_init (error); + if (!(method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) + return; + mono_lookup_pinvoke_call (method, &exc_class, &exc_arg); + if (exc_class) { + mono_error_set_generic_error (error, "System", exc_class, "%s", exc_arg); + return; + } + /* create the wrapper, too? */ +} + +ICALL_EXPORT void +ves_icall_System_Runtime_InteropServices_Marshal_Prelink (MonoReflectionMethodHandle method, MonoError *error) +{ + error_init (error); + + prelink_method (MONO_HANDLE_GETVAL (method, method), error); +} + +ICALL_EXPORT void +ves_icall_System_Runtime_InteropServices_Marshal_PrelinkAll (MonoReflectionTypeHandle type, MonoError *error) +{ + error_init (error); + MonoClass *klass = mono_class_from_mono_type (MONO_HANDLE_GETVAL (type, type)); + MonoMethod* m; + gpointer iter = NULL; + + mono_class_init_checked (klass, error); + return_if_nok (error); + + while ((m = mono_class_get_methods (klass, &iter))) { + prelink_method (m, error); + return_if_nok (error); + } +} + +/* These parameters are "readonly" in corlib/System/NumberFormatter.cs */ +ICALL_EXPORT void +ves_icall_System_NumberFormatter_GetFormatterTables (guint64 const **mantissas, + gint32 const **exponents, + gunichar2 const **digitLowerTable, + gunichar2 const **digitUpperTable, + gint64 const **tenPowersList, + gint32 const **decHexDigits) +{ + *mantissas = Formatter_MantissaBitsTable; + *exponents = Formatter_TensExponentTable; + *digitLowerTable = Formatter_DigitLowerTable; + *digitUpperTable = Formatter_DigitUpperTable; + *tenPowersList = Formatter_TenPowersList; + *decHexDigits = Formatter_DecHexDigits; +} + +static gboolean +add_modifier_to_array (MonoDomain *domain, MonoImage *image, MonoCustomMod *modifier, MonoArrayHandle dest, int dest_idx, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + error_init (error); + MonoClass *klass = mono_class_get_checked (image, modifier->token, error); + goto_if_nok (error, leave); + + MonoReflectionTypeHandle rt = mono_type_get_object_handle (domain, &klass->byval_arg, error); + goto_if_nok (error, leave); + + MONO_HANDLE_ARRAY_SETREF (dest, dest_idx, rt); +leave: + HANDLE_FUNCTION_RETURN_VAL (is_ok (error)); +} + +/* + * We return NULL for no modifiers so the corlib code can return Type.EmptyTypes + * and avoid useless allocations. + */ +static MonoArrayHandle +type_array_from_modifiers (MonoImage *image, MonoType *type, int optional, MonoError *error) +{ + int i, count = 0; + MonoDomain *domain = mono_domain_get (); + + error_init (error); + for (i = 0; i < type->num_mods; ++i) { + if ((optional && !type->modifiers [i].required) || (!optional && type->modifiers [i].required)) + count++; + } + if (!count) + return MONO_HANDLE_NEW (MonoArray, NULL); + + MonoArrayHandle res = mono_array_new_handle (domain, mono_defaults.systemtype_class, count, error); + goto_if_nok (error, fail); + count = 0; + for (i = 0; i < type->num_mods; ++i) { + if ((optional && !type->modifiers [i].required) || (!optional && type->modifiers [i].required)) { + if (!add_modifier_to_array (domain, image, &type->modifiers[i], res, count , error)) + goto fail; + count++; + } + } + return res; +fail: + return MONO_HANDLE_NEW (MonoArray, NULL); +} + +ICALL_EXPORT MonoArrayHandle +ves_icall_ParameterInfo_GetTypeModifiers (MonoReflectionParameterHandle param, MonoBoolean optional, MonoError *error) +{ + error_init (error); + MonoReflectionTypeHandle rt = MONO_HANDLE_NEW (MonoReflectionType, NULL); + MONO_HANDLE_GET (rt, param, ClassImpl); + MonoType *type = MONO_HANDLE_GETVAL (rt, type); + MonoObjectHandle member = MONO_HANDLE_NEW (MonoObject, NULL); + MONO_HANDLE_GET (member, param, MemberImpl); + MonoClass *member_class = mono_handle_class (member); + MonoMethod *method = NULL; + MonoImage *image; + int pos; + MonoMethodSignature *sig; + + if (mono_class_is_reflection_method_or_constructor (member_class)) { + method = MONO_HANDLE_GETVAL (MONO_HANDLE_CAST (MonoReflectionMethod, member), method); + } else if (member_class->image == mono_defaults.corlib && !strcmp ("MonoProperty", member_class->name)) { + MonoProperty *prop = MONO_HANDLE_GETVAL (MONO_HANDLE_CAST (MonoReflectionProperty, member), property); + if (!(method = prop->get)) + method = prop->set; + g_assert (method); + } else { + char *type_name = mono_type_get_full_name (member_class); + mono_error_set_not_supported (error, "Custom modifiers on a ParamInfo with member %s are not supported", type_name); + g_free (type_name); + return MONO_HANDLE_CAST (MonoArray, NULL_HANDLE); + } + + image = method->klass->image; + pos = MONO_HANDLE_GETVAL (param, PositionImpl); + sig = mono_method_signature (method); + if (pos == -1) + type = sig->ret; + else + type = sig->params [pos]; + + return type_array_from_modifiers (image, type, optional, error); +} + +static MonoType* +get_property_type (MonoProperty *prop) +{ + MonoMethodSignature *sig; + if (prop->get) { + sig = mono_method_signature (prop->get); + return sig->ret; + } else if (prop->set) { + sig = mono_method_signature (prop->set); + return sig->params [sig->param_count - 1]; + } + return NULL; +} + +ICALL_EXPORT MonoArrayHandle +ves_icall_MonoPropertyInfo_GetTypeModifiers (MonoReflectionPropertyHandle property, MonoBoolean optional, MonoError *error) +{ + error_init (error); + MonoProperty *prop = MONO_HANDLE_GETVAL (property, property); + MonoClass *klass = MONO_HANDLE_GETVAL (property, klass); + MonoType *type = get_property_type (prop); + MonoImage *image = klass->image; + + if (!type) + return MONO_HANDLE_CAST (MonoArray, NULL_HANDLE); + return type_array_from_modifiers (image, type, optional, error); +} + +/* + *Construct a MonoType suited to be used to decode a constant blob object. + * + * @type is the target type which will be constructed + * @blob_type is the blob type, for example, that comes from the constant table + * @real_type is the expected constructed type. + */ +static void +mono_type_from_blob_type (MonoType *type, MonoTypeEnum blob_type, MonoType *real_type) +{ + type->type = blob_type; + type->data.klass = NULL; + if (blob_type == MONO_TYPE_CLASS) + type->data.klass = mono_defaults.object_class; + else if (real_type->type == MONO_TYPE_VALUETYPE && real_type->data.klass->enumtype) { + /* For enums, we need to use the base type */ + type->type = MONO_TYPE_VALUETYPE; + type->data.klass = mono_class_from_mono_type (real_type); + } else + type->data.klass = mono_class_from_mono_type (real_type); +} + +ICALL_EXPORT MonoObject* +property_info_get_default_value (MonoReflectionProperty *property) +{ + MonoError error; + MonoType blob_type; + MonoProperty *prop = property->property; + MonoType *type = get_property_type (prop); + MonoDomain *domain = mono_object_domain (property); + MonoTypeEnum def_type; + const char *def_value; + MonoObject *o; + + mono_class_init (prop->parent); + + if (!(prop->attrs & PROPERTY_ATTRIBUTE_HAS_DEFAULT)) { + mono_set_pending_exception (mono_get_exception_invalid_operation (NULL)); + return NULL; + } + + def_value = mono_class_get_property_default_value (prop, &def_type); + + mono_type_from_blob_type (&blob_type, def_type, type); + o = mono_get_object_from_blob (domain, &blob_type, def_value, &error); + + mono_error_set_pending_exception (&error); + return o; +} + +ICALL_EXPORT MonoBoolean +ves_icall_MonoCustomAttrs_IsDefinedInternal (MonoObjectHandle obj, MonoReflectionTypeHandle attr_type, MonoError *error) +{ + error_init (error); + MonoClass *attr_class = mono_class_from_mono_type (MONO_HANDLE_GETVAL (attr_type, type)); + + mono_class_init_checked (attr_class, error); + return_val_if_nok (error, FALSE); + + MonoCustomAttrInfo *cinfo = mono_reflection_get_custom_attrs_info_checked (obj, error); + return_val_if_nok (error, FALSE); + + if (!cinfo) + return FALSE; + gboolean found = mono_custom_attrs_has_attr (cinfo, attr_class); + if (!cinfo->cached) + mono_custom_attrs_free (cinfo); + return found; +} + +ICALL_EXPORT MonoArrayHandle +ves_icall_MonoCustomAttrs_GetCustomAttributesInternal (MonoObjectHandle obj, MonoReflectionTypeHandle attr_type, mono_bool pseudoattrs, MonoError *error) +{ + MonoClass *attr_class; + if (MONO_HANDLE_IS_NULL (attr_type)) + attr_class = NULL; + else + attr_class = mono_class_from_mono_type (MONO_HANDLE_GETVAL (attr_type, type)); + + if (attr_class) { + mono_class_init_checked (attr_class, error); + if (!is_ok (error)) + return MONO_HANDLE_CAST (MonoArray, NULL_HANDLE); + } + + return mono_reflection_get_custom_attrs_by_type_handle (obj, attr_class, error); +} + +ICALL_EXPORT MonoArrayHandle +ves_icall_MonoCustomAttrs_GetCustomAttributesDataInternal (MonoObjectHandle obj, MonoError *error) +{ + error_init (error); + return mono_reflection_get_custom_attrs_data_checked (obj, error); +} + + +ICALL_EXPORT MonoStringHandle +ves_icall_Mono_Runtime_GetDisplayName (MonoError *error) +{ + char *info; + MonoStringHandle display_name; + + error_init (error); + info = mono_get_runtime_callbacks ()->get_runtime_build_info (); + display_name = mono_string_new_handle (mono_domain_get (), info, error); + g_free (info); + return display_name; +} + +#ifndef HOST_WIN32 +static inline gint32 +mono_icall_wait_for_input_idle (gpointer handle, gint32 milliseconds) +{ + return WAIT_TIMEOUT; +} +#endif /* !HOST_WIN32 */ + +ICALL_EXPORT gint32 +ves_icall_Microsoft_Win32_NativeMethods_WaitForInputIdle (gpointer handle, gint32 milliseconds) +{ + return mono_icall_wait_for_input_idle (handle, milliseconds); +} + +ICALL_EXPORT gint32 +ves_icall_Microsoft_Win32_NativeMethods_GetCurrentProcessId (void) +{ + return mono_process_current_pid (); +} + +ICALL_EXPORT MonoBoolean +ves_icall_Mono_TlsProviderFactory_IsBtlsSupported (void) +{ +#if HAVE_BTLS + return TRUE; +#else + return FALSE; +#endif +} + +#ifndef DISABLE_COM + +ICALL_EXPORT int +ves_icall_System_Runtime_InteropServices_Marshal_GetHRForException_WinRT(MonoException* ex) +{ + mono_set_pending_exception(mono_get_exception_not_implemented("System.Runtime.InteropServices.Marshal.GetHRForException_WinRT internal call is not implemented.")); + return 0; +} + +ICALL_EXPORT MonoObject* +ves_icall_System_Runtime_InteropServices_Marshal_GetNativeActivationFactory(MonoObject* type) +{ + mono_set_pending_exception(mono_get_exception_not_implemented("System.Runtime.InteropServices.Marshal.GetNativeActivationFactory internal call is not implemented.")); + return NULL; +} + +ICALL_EXPORT void* +ves_icall_System_Runtime_InteropServices_Marshal_GetRawIUnknownForComObjectNoAddRef(MonoObject* obj) +{ + mono_set_pending_exception(mono_get_exception_not_implemented("System.Runtime.InteropServices.Marshal.GetRawIUnknownForComObjectNoAddRef internal call is not implemented.")); + return NULL; +} + +ICALL_EXPORT MonoObject* +ves_icall_System_Runtime_InteropServices_WindowsRuntime_UnsafeNativeMethods_GetRestrictedErrorInfo(void) +{ + mono_set_pending_exception(mono_get_exception_not_implemented("System.Runtime.InteropServices.WindowsRuntime.UnsafeNativeMethods.GetRestrictedErrorInfo internal call is not implemented.")); + return NULL; +} + +ICALL_EXPORT MonoBoolean +ves_icall_System_Runtime_InteropServices_WindowsRuntime_UnsafeNativeMethods_RoOriginateLanguageException(int error, MonoString* message, void* languageException) +{ + mono_set_pending_exception(mono_get_exception_not_implemented("System.Runtime.InteropServices.WindowsRuntime.UnsafeNativeMethods.RoOriginateLanguageException internal call is not implemented.")); + return FALSE; +} + +ICALL_EXPORT void +ves_icall_System_Runtime_InteropServices_WindowsRuntime_UnsafeNativeMethods_RoReportUnhandledError(MonoObject* error) +{ + mono_set_pending_exception(mono_get_exception_not_implemented("System.Runtime.InteropServices.WindowsRuntime.UnsafeNativeMethods.RoReportUnhandledError internal call is not implemented.")); +} + +ICALL_EXPORT int +ves_icall_System_Runtime_InteropServices_WindowsRuntime_UnsafeNativeMethods_WindowsCreateString(MonoString* sourceString, int length, void** hstring) +{ + mono_set_pending_exception(mono_get_exception_not_implemented("System.Runtime.InteropServices.WindowsRuntime.UnsafeNativeMethods.WindowsCreateString internal call is not implemented.")); + return 0; +} + +ICALL_EXPORT int +ves_icall_System_Runtime_InteropServices_WindowsRuntime_UnsafeNativeMethods_WindowsDeleteString(void* hstring) +{ + mono_set_pending_exception(mono_get_exception_not_implemented("System.Runtime.InteropServices.WindowsRuntime.UnsafeNativeMethods.WindowsDeleteString internal call is not implemented.")); + return 0; +} + +ICALL_EXPORT mono_unichar2* +ves_icall_System_Runtime_InteropServices_WindowsRuntime_UnsafeNativeMethods_WindowsGetStringRawBuffer(void* hstring, unsigned* length) +{ + mono_set_pending_exception(mono_get_exception_not_implemented("System.Runtime.InteropServices.WindowsRuntime.UnsafeNativeMethods.WindowsGetStringRawBuffer internal call is not implemented.")); + return NULL; +} + +#endif + +ICALL_EXPORT void +ves_icall_System_IO_LogcatTextWriter_Log (const char *appname, gint32 level, const char *message) +{ + g_log (appname, (GLogLevelFlags)level, message); +} + +#ifndef DISABLE_ICALL_TABLES + +#define ICALL_TYPE(id,name,first) +#define ICALL(id,name,func) Icall_ ## id, +#define HANDLES(inner) inner + +enum { +#include "metadata/icall-def.h" + Icall_last +}; + +#undef ICALL_TYPE +#undef ICALL +#define ICALL_TYPE(id,name,first) Icall_type_ ## id, +#define ICALL(id,name,func) +#undef HANDLES +#define HANDLES(inner) inner +enum { +#include "metadata/icall-def.h" + Icall_type_num +}; + +#undef ICALL_TYPE +#undef ICALL +#define ICALL_TYPE(id,name,firstic) {(Icall_ ## firstic)}, +#define ICALL(id,name,func) +#undef HANDLES +#define HANDLES(inner) inner +typedef struct { + guint16 first_icall; +} IcallTypeDesc; + +static const IcallTypeDesc +icall_type_descs [] = { +#include "metadata/icall-def.h" + {Icall_last} +}; + +#define icall_desc_num_icalls(desc) ((desc) [1].first_icall - (desc) [0].first_icall) + +#undef HANDLES +#define HANDLES(inner) inner +#undef ICALL_TYPE +#define ICALL_TYPE(id,name,first) +#undef ICALL + +#ifdef HAVE_ARRAY_ELEM_INIT +#define MSGSTRFIELD(line) MSGSTRFIELD1(line) +#define MSGSTRFIELD1(line) str##line + +static const struct msgstrtn_t { +#define ICALL(id,name,func) +#undef ICALL_TYPE +#define ICALL_TYPE(id,name,first) char MSGSTRFIELD(__LINE__) [sizeof (name)]; +#include "metadata/icall-def.h" +#undef ICALL_TYPE +} icall_type_names_str = { +#define ICALL_TYPE(id,name,first) (name), +#include "metadata/icall-def.h" +#undef ICALL_TYPE +}; +static const guint16 icall_type_names_idx [] = { +#define ICALL_TYPE(id,name,first) [Icall_type_ ## id] = offsetof (struct msgstrtn_t, MSGSTRFIELD(__LINE__)), +#include "metadata/icall-def.h" +#undef ICALL_TYPE +}; +#define icall_type_name_get(id) ((const char*)&icall_type_names_str + icall_type_names_idx [(id)]) + +static const struct msgstr_t { +#undef ICALL +#define ICALL_TYPE(id,name,first) +#define ICALL(id,name,func) char MSGSTRFIELD(__LINE__) [sizeof (name)]; +#include "metadata/icall-def.h" +#undef ICALL +} icall_names_str = { +#define ICALL(id,name,func) (name), +#include "metadata/icall-def.h" +#undef ICALL +}; +static const guint16 icall_names_idx [] = { +#define ICALL(id,name,func) [Icall_ ## id] = offsetof (struct msgstr_t, MSGSTRFIELD(__LINE__)), +#include "metadata/icall-def.h" +#undef ICALL +}; +#define icall_name_get(id) ((const char*)&icall_names_str + icall_names_idx [(id)]) + +#else + +#undef ICALL_TYPE +#undef ICALL +#define ICALL_TYPE(id,name,first) name, +#define ICALL(id,name,func) +static const char* const +icall_type_names [] = { +#include "metadata/icall-def.h" + NULL +}; + +#define icall_type_name_get(id) (icall_type_names [(id)]) + +#undef ICALL_TYPE +#undef ICALL +#define ICALL_TYPE(id,name,first) +#define ICALL(id,name,func) name, +static const char* const +icall_names [] = { +#include "metadata/icall-def.h" + NULL +}; +#define icall_name_get(id) icall_names [(id)] + +#endif /* !HAVE_ARRAY_ELEM_INIT */ + +#undef HANDLES +#define HANDLES(inner) inner +#undef ICALL_TYPE +#undef ICALL +#define ICALL_TYPE(id,name,first) +#define ICALL(id,name,func) func, +static const gconstpointer +icall_functions [] = { +#include "metadata/icall-def.h" + NULL +}; + +#ifdef ENABLE_ICALL_SYMBOL_MAP +#undef HANDLES +#define HANDLES(inner) inner +#undef ICALL_TYPE +#undef ICALL +#define ICALL_TYPE(id,name,first) +#define ICALL(id,name,func) #func, +static const gconstpointer +icall_symbols [] = { +#include "metadata/icall-def.h" + NULL +}; +#endif + +#undef ICALL_TYPE +#undef ICALL +#define ICALL_TYPE(id,name,first) +#define ICALL(id,name,func) 0, +#undef HANDLES +#define HANDLES(inner) 1, +static const guchar +icall_uses_handles [] = { +#include "metadata/icall-def.h" +#undef ICALL +#undef HANDLES +}; + +#endif /* DISABLE_ICALL_TABLES */ + +static mono_mutex_t icall_mutex; +static GHashTable *icall_hash = NULL; +static GHashTable *jit_icall_hash_name = NULL; +static GHashTable *jit_icall_hash_addr = NULL; + +void +mono_icall_init (void) +{ +#ifndef DISABLE_ICALL_TABLES + int i = 0; + + /* check that tables are sorted: disable in release */ + if (TRUE) { + int j; + const char *prev_class = NULL; + const char *prev_method; + + for (i = 0; i < Icall_type_num; ++i) { + const IcallTypeDesc *desc; + int num_icalls; + prev_method = NULL; + if (prev_class && strcmp (prev_class, icall_type_name_get (i)) >= 0) + g_print ("class %s should come before class %s\n", icall_type_name_get (i), prev_class); + prev_class = icall_type_name_get (i); + desc = &icall_type_descs [i]; + num_icalls = icall_desc_num_icalls (desc); + /*g_print ("class %s has %d icalls starting at %d\n", prev_class, num_icalls, desc->first_icall);*/ + for (j = 0; j < num_icalls; ++j) { + const char *methodn = icall_name_get (desc->first_icall + j); + if (prev_method && strcmp (prev_method, methodn) >= 0) + g_print ("method %s should come before method %s\n", methodn, prev_method); + prev_method = methodn; + } + } + } +#endif + + icall_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + mono_os_mutex_init (&icall_mutex); +} + +static void +mono_icall_lock (void) +{ + mono_locks_os_acquire (&icall_mutex, IcallLock); +} + +static void +mono_icall_unlock (void) +{ + mono_locks_os_release (&icall_mutex, IcallLock); +} + +void +mono_icall_cleanup (void) +{ + g_hash_table_destroy (icall_hash); + g_hash_table_destroy (jit_icall_hash_name); + g_hash_table_destroy (jit_icall_hash_addr); + mono_os_mutex_destroy (&icall_mutex); +} + +/** + * mono_add_internal_call: + * \param name method specification to surface to the managed world + * \param method pointer to a C method to invoke when the method is called + * + * This method surfaces the C function pointed by \p method as a method + * that has been surfaced in managed code with the method specified in + * \p name as an internal call. + * + * Internal calls are surfaced to all app domains loaded and they are + * accessibly by a type with the specified name. + * + * You must provide a fully qualified type name, that is namespaces + * and type name, followed by a colon and the method name, with an + * optional signature to bind. + * + * For example, the following are all valid declarations: + * + * \c MyApp.Services.ScriptService:Accelerate + * + * \c MyApp.Services.ScriptService:Slowdown(int,bool) + * + * You use method parameters in cases where there might be more than + * one surface method to managed code. That way you can register different + * internal calls for different method overloads. + * + * The internal calls are invoked with no marshalling. This means that .NET + * types like \c System.String are exposed as \c MonoString* parameters. This is + * different than the way that strings are surfaced in P/Invoke. + * + * For more information on how the parameters are marshalled, see the + * Mono Embedding + * page. + * + * See the Method Description + * reference for more information on the format of method descriptions. + */ +void +mono_add_internal_call (const char *name, gconstpointer method) +{ + mono_icall_lock (); + + g_hash_table_insert (icall_hash, g_strdup (name), (gpointer) method); + + mono_icall_unlock (); +} + +#ifndef DISABLE_ICALL_TABLES + +#ifdef HAVE_ARRAY_ELEM_INIT +static int +compare_method_imap (const void *key, const void *elem) +{ + const char* method_name = (const char*)&icall_names_str + (*(guint16*)elem); + return strcmp (key, method_name); +} + +static gsize +find_slot_icall (const IcallTypeDesc *imap, const char *name) +{ + const guint16 *nameslot = (const guint16 *)mono_binary_search (name, icall_names_idx + imap->first_icall, icall_desc_num_icalls (imap), sizeof (icall_names_idx [0]), compare_method_imap); + if (!nameslot) + return -1; + return (nameslot - &icall_names_idx [0]); +} + +static gboolean +find_uses_handles_icall (const IcallTypeDesc *imap, const char *name) +{ + gsize slotnum = find_slot_icall (imap, name); + if (slotnum == -1) + return FALSE; + return (gboolean)icall_uses_handles [slotnum]; +} + +static gpointer +find_method_icall (const IcallTypeDesc *imap, const char *name) +{ + gsize slotnum = find_slot_icall (imap, name); + if (slotnum == -1) + return NULL; + return (gpointer)icall_functions [slotnum]; +} + +static int +compare_class_imap (const void *key, const void *elem) +{ + const char* class_name = (const char*)&icall_type_names_str + (*(guint16*)elem); + return strcmp (key, class_name); +} + +static const IcallTypeDesc* +find_class_icalls (const char *name) +{ + const guint16 *nameslot = (const guint16 *)mono_binary_search (name, icall_type_names_idx, Icall_type_num, sizeof (icall_type_names_idx [0]), compare_class_imap); + if (!nameslot) + return NULL; + return &icall_type_descs [nameslot - &icall_type_names_idx [0]]; +} + +#else /* HAVE_ARRAY_ELEM_INIT */ + +static int +compare_method_imap (const void *key, const void *elem) +{ + const char** method_name = (const char**)elem; + return strcmp (key, *method_name); +} + +static gsize +find_slot_icall (const IcallTypeDesc *imap, const char *name) +{ + const char **nameslot = mono_binary_search (name, icall_names + imap->first_icall, icall_desc_num_icalls (imap), sizeof (icall_names [0]), compare_method_imap); + if (!nameslot) + return -1; + return nameslot - icall_names; +} + +static gpointer +find_method_icall (const IcallTypeDesc *imap, const char *name) +{ + gsize slotnum = find_slot_icall (imap, name); + if (slotnum == -1) + return NULL; + return (gpointer)icall_functions [slotnum]; +} + +static gboolean +find_uses_handles_icall (const IcallTypeDesc *imap, const char *name) +{ + gsize slotnum = find_slot_icall (imap, name); + if (slotnum == -1) + return FALSE; + return (gboolean)icall_uses_handles [slotnum]; +} + +static int +compare_class_imap (const void *key, const void *elem) +{ + const char** class_name = (const char**)elem; + return strcmp (key, *class_name); +} + +static const IcallTypeDesc* +find_class_icalls (const char *name) +{ + const char **nameslot = mono_binary_search (name, icall_type_names, Icall_type_num, sizeof (icall_type_names [0]), compare_class_imap); + if (!nameslot) + return NULL; + return &icall_type_descs [nameslot - icall_type_names]; +} + +#endif /* HAVE_ARRAY_ELEM_INIT */ + +#endif /* DISABLE_ICALL_TABLES */ + +/* + * we should probably export this as an helper (handle nested types). + * Returns the number of chars written in buf. + */ +static int +concat_class_name (char *buf, int bufsize, MonoClass *klass) +{ + int nspacelen, cnamelen; + nspacelen = strlen (klass->name_space); + cnamelen = strlen (klass->name); + if (nspacelen + cnamelen + 2 > bufsize) + return 0; + if (nspacelen) { + memcpy (buf, klass->name_space, nspacelen); + buf [nspacelen ++] = '.'; + } + memcpy (buf + nspacelen, klass->name, cnamelen); + buf [nspacelen + cnamelen] = 0; + return nspacelen + cnamelen; +} + +#ifdef DISABLE_ICALL_TABLES +static void +no_icall_table (void) +{ + g_assert_not_reached (); +} +#endif + +/** + * mono_lookup_internal_call_full: + * \param method the method to look up + * \param uses_handles out argument if method needs handles around managed objects. + * \returns a pointer to the icall code for the given method. If + * \p uses_handles is not NULL, it will be set to TRUE if the method + * needs managed objects wrapped using the infrastructure in handle.h + * + * If the method is not found, warns and returns NULL. + */ +gpointer +mono_lookup_internal_call_full (MonoMethod *method, mono_bool *uses_handles) +{ + char *sigstart; + char *tmpsig; + char mname [2048]; + int typelen = 0, mlen, siglen; + gpointer res; +#ifndef DISABLE_ICALL_TABLES + const IcallTypeDesc *imap = NULL; +#endif + + g_assert (method != NULL); + + if (method->is_inflated) + method = ((MonoMethodInflated *) method)->declaring; + + if (method->klass->nested_in) { + int pos = concat_class_name (mname, sizeof (mname)-2, method->klass->nested_in); + if (!pos) + return NULL; + + mname [pos++] = '/'; + mname [pos] = 0; + + typelen = concat_class_name (mname+pos, sizeof (mname)-pos-1, method->klass); + if (!typelen) + return NULL; + + typelen += pos; + } else { + typelen = concat_class_name (mname, sizeof (mname), method->klass); + if (!typelen) + return NULL; + } + +#ifndef DISABLE_ICALL_TABLES + imap = find_class_icalls (mname); +#endif + + mname [typelen] = ':'; + mname [typelen + 1] = ':'; + + mlen = strlen (method->name); + memcpy (mname + typelen + 2, method->name, mlen); + sigstart = mname + typelen + 2 + mlen; + *sigstart = 0; + + tmpsig = mono_signature_get_desc (mono_method_signature (method), TRUE); + siglen = strlen (tmpsig); + if (typelen + mlen + siglen + 6 > sizeof (mname)) { + g_free (tmpsig); + return NULL; + } + sigstart [0] = '('; + memcpy (sigstart + 1, tmpsig, siglen); + sigstart [siglen + 1] = ')'; + sigstart [siglen + 2] = 0; + g_free (tmpsig); + + mono_icall_lock (); + + res = g_hash_table_lookup (icall_hash, mname); + if (res) { + if (uses_handles) + *uses_handles = FALSE; + mono_icall_unlock ();; + return res; + } + /* try without signature */ + *sigstart = 0; + res = g_hash_table_lookup (icall_hash, mname); + if (res) { + if (uses_handles) + *uses_handles = FALSE; + mono_icall_unlock (); + return res; + } + +#ifdef DISABLE_ICALL_TABLES + mono_icall_unlock (); + /* Fail only when the result is actually used */ + /* mono_marshal_get_native_wrapper () depends on this */ + if (method->klass == mono_defaults.string_class && !strcmp (method->name, ".ctor")) + return ves_icall_System_String_ctor_RedirectToCreateString; + else + return no_icall_table; +#else + /* it wasn't found in the static call tables */ + if (!imap) { + if (uses_handles) + *uses_handles = FALSE; + mono_icall_unlock (); + return NULL; + } + res = find_method_icall (imap, sigstart - mlen); + if (res) { + if (uses_handles) + *uses_handles = find_uses_handles_icall (imap, sigstart - mlen); + mono_icall_unlock (); + return res; + } + /* try _with_ signature */ + *sigstart = '('; + res = find_method_icall (imap, sigstart - mlen); + if (res) { + if (uses_handles) + *uses_handles = find_uses_handles_icall (imap, sigstart - mlen); + mono_icall_unlock (); + return res; + } + + g_warning ("cant resolve internal call to \"%s\" (tested without signature also)", mname); + g_print ("\nYour mono runtime and class libraries are out of sync.\n"); + g_print ("The out of sync library is: %s\n", method->klass->image->name); + g_print ("\nWhen you update one from git you need to update, compile and install\nthe other too.\n"); + g_print ("Do not report this as a bug unless you're sure you have updated correctly:\nyou probably have a broken mono install.\n"); + g_print ("If you see other errors or faults after this message they are probably related\n"); + g_print ("and you need to fix your mono install first.\n"); + + mono_icall_unlock (); + + return NULL; +#endif +} + +/** + * mono_lookup_internal_call: + */ +gpointer +mono_lookup_internal_call (MonoMethod *method) +{ + return mono_lookup_internal_call_full (method, NULL); +} + +#ifdef ENABLE_ICALL_SYMBOL_MAP +static int +func_cmp (gconstpointer key, gconstpointer p) +{ + return (gsize)key - (gsize)*(gsize*)p; +} +#endif + +/* + * mono_lookup_icall_symbol: + * + * Given the icall METHOD, returns its C symbol. + */ +const char* +mono_lookup_icall_symbol (MonoMethod *m) +{ +#ifdef DISABLE_ICALL_TABLES + g_assert_not_reached (); + return NULL; +#else +#ifdef ENABLE_ICALL_SYMBOL_MAP + gpointer func; + int i; + gpointer slot; + static gconstpointer *functions_sorted; + static const char**symbols_sorted; + static gboolean inited; + + if (!inited) { + gboolean changed; + + functions_sorted = g_malloc (G_N_ELEMENTS (icall_functions) * sizeof (gpointer)); + memcpy (functions_sorted, icall_functions, G_N_ELEMENTS (icall_functions) * sizeof (gpointer)); + symbols_sorted = g_malloc (G_N_ELEMENTS (icall_functions) * sizeof (gpointer)); + memcpy (symbols_sorted, icall_symbols, G_N_ELEMENTS (icall_functions) * sizeof (gpointer)); + /* Bubble sort the two arrays */ + changed = TRUE; + while (changed) { + changed = FALSE; + for (i = 0; i < G_N_ELEMENTS (icall_functions) - 1; ++i) { + if (functions_sorted [i] > functions_sorted [i + 1]) { + gconstpointer tmp; + + tmp = functions_sorted [i]; + functions_sorted [i] = functions_sorted [i + 1]; + functions_sorted [i + 1] = tmp; + tmp = symbols_sorted [i]; + symbols_sorted [i] = symbols_sorted [i + 1]; + symbols_sorted [i + 1] = tmp; + changed = TRUE; + } + } + } + } + + func = mono_lookup_internal_call (m); + if (!func) + return NULL; + slot = mono_binary_search (func, functions_sorted, G_N_ELEMENTS (icall_functions), sizeof (gpointer), func_cmp); + if (!slot) + return NULL; + g_assert (slot); + return symbols_sorted [(gpointer*)slot - (gpointer*)functions_sorted]; +#else + fprintf (stderr, "icall symbol maps not enabled, pass --enable-icall-symbol-map to configure.\n"); + g_assert_not_reached (); + return 0; +#endif +#endif +} + +static MonoType* +type_from_typename (char *type_name) +{ + MonoClass *klass = NULL; /* assignment to shut GCC warning up */ + + if (!strcmp (type_name, "int")) + klass = mono_defaults.int_class; + else if (!strcmp (type_name, "ptr&")) + return mono_class_get_byref_type (mono_defaults.int_class); + else if (!strcmp (type_name, "ptr")) + klass = mono_defaults.int_class; + else if (!strcmp (type_name, "void")) + klass = mono_defaults.void_class; + else if (!strcmp (type_name, "int32")) + klass = mono_defaults.int32_class; + else if (!strcmp (type_name, "uint32")) + klass = mono_defaults.uint32_class; + else if (!strcmp (type_name, "int8")) + klass = mono_defaults.sbyte_class; + else if (!strcmp (type_name, "uint8")) + klass = mono_defaults.byte_class; + else if (!strcmp (type_name, "int16")) + klass = mono_defaults.int16_class; + else if (!strcmp (type_name, "uint16")) + klass = mono_defaults.uint16_class; + else if (!strcmp (type_name, "long")) + klass = mono_defaults.int64_class; + else if (!strcmp (type_name, "ulong")) + klass = mono_defaults.uint64_class; + else if (!strcmp (type_name, "float")) + klass = mono_defaults.single_class; + else if (!strcmp (type_name, "double")) + klass = mono_defaults.double_class; + else if (!strcmp (type_name, "object")) + klass = mono_defaults.object_class; + else if (!strcmp (type_name, "obj")) + klass = mono_defaults.object_class; + else if (!strcmp (type_name, "string")) + klass = mono_defaults.string_class; + else if (!strcmp (type_name, "bool")) + klass = mono_defaults.boolean_class; + else if (!strcmp (type_name, "boolean")) + klass = mono_defaults.boolean_class; + else { + g_error ("%s", type_name); + g_assert_not_reached (); + } + return &klass->byval_arg; +} + +/** + * LOCKING: Take the corlib image lock. + */ +MonoMethodSignature* +mono_create_icall_signature (const char *sigstr) +{ + gchar **parts; + int i, len; + gchar **tmp; + MonoMethodSignature *res, *res2; + MonoImage *corlib = mono_defaults.corlib; + + mono_image_lock (corlib); + res = (MonoMethodSignature *)g_hash_table_lookup (corlib->helper_signatures, sigstr); + mono_image_unlock (corlib); + + if (res) + return res; + + parts = g_strsplit (sigstr, " ", 256); + + tmp = parts; + len = 0; + while (*tmp) { + len ++; + tmp ++; + } + + res = mono_metadata_signature_alloc (corlib, len - 1); + res->pinvoke = 1; + +#ifdef TARGET_WIN32 + /* + * Under windows, the default pinvoke calling convention is STDCALL but + * we need CDECL. + */ + res->call_convention = MONO_CALL_C; +#endif + + res->ret = type_from_typename (parts [0]); + for (i = 1; i < len; ++i) { + res->params [i - 1] = type_from_typename (parts [i]); + } + + g_strfreev (parts); + + mono_image_lock (corlib); + res2 = (MonoMethodSignature *)g_hash_table_lookup (corlib->helper_signatures, sigstr); + if (res2) + res = res2; /*Value is allocated in the image pool*/ + else + g_hash_table_insert (corlib->helper_signatures, (gpointer)sigstr, res); + mono_image_unlock (corlib); + + return res; +} + +MonoJitICallInfo * +mono_find_jit_icall_by_name (const char *name) +{ + MonoJitICallInfo *info; + g_assert (jit_icall_hash_name); + + mono_icall_lock (); + info = (MonoJitICallInfo *)g_hash_table_lookup (jit_icall_hash_name, name); + mono_icall_unlock (); + return info; +} + +MonoJitICallInfo * +mono_find_jit_icall_by_addr (gconstpointer addr) +{ + MonoJitICallInfo *info; + g_assert (jit_icall_hash_addr); + + mono_icall_lock (); + info = (MonoJitICallInfo *)g_hash_table_lookup (jit_icall_hash_addr, (gpointer)addr); + mono_icall_unlock (); + + return info; +} + +/* + * mono_get_jit_icall_info: + * + * Return the hashtable mapping JIT icall names to MonoJitICallInfo structures. The + * caller should access it while holding the icall lock. + */ +GHashTable* +mono_get_jit_icall_info (void) +{ + return jit_icall_hash_name; +} + +/* + * mono_lookup_jit_icall_symbol: + * + * Given the jit icall NAME, returns its C symbol if possible, or NULL. + */ +const char* +mono_lookup_jit_icall_symbol (const char *name) +{ + MonoJitICallInfo *info; + const char *res = NULL; + + mono_icall_lock (); + info = (MonoJitICallInfo *)g_hash_table_lookup (jit_icall_hash_name, name); + if (info) + res = info->c_symbol; + mono_icall_unlock (); + return res; +} + +void +mono_register_jit_icall_wrapper (MonoJitICallInfo *info, gconstpointer wrapper) +{ + mono_icall_lock (); + g_hash_table_insert (jit_icall_hash_addr, (gpointer)wrapper, info); + mono_icall_unlock (); +} + +MonoJitICallInfo * +mono_register_jit_icall_full (gconstpointer func, const char *name, MonoMethodSignature *sig, gboolean avoid_wrapper, const char *c_symbol) +{ + MonoJitICallInfo *info; + + g_assert (func); + g_assert (name); + + mono_icall_lock (); + + if (!jit_icall_hash_name) { + jit_icall_hash_name = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free); + jit_icall_hash_addr = g_hash_table_new (NULL, NULL); + } + + if (g_hash_table_lookup (jit_icall_hash_name, name)) { + g_warning ("jit icall already defined \"%s\"\n", name); + g_assert_not_reached (); + } + + info = g_new0 (MonoJitICallInfo, 1); + + info->name = name; + info->func = func; + info->sig = sig; + info->c_symbol = c_symbol; + + if (avoid_wrapper) { + info->wrapper = func; + } else { + info->wrapper = NULL; + } + + g_hash_table_insert (jit_icall_hash_name, (gpointer)info->name, info); + g_hash_table_insert (jit_icall_hash_addr, (gpointer)func, info); + + mono_icall_unlock (); + return info; +} + +MonoJitICallInfo * +mono_register_jit_icall (gconstpointer func, const char *name, MonoMethodSignature *sig, gboolean no_wrapper) +{ + 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/unity-2019.4.24f1-mbe/mono/metadata/il2cpp-compat-metadata.h b/unity-2019.4.24f1-mbe/mono/metadata/il2cpp-compat-metadata.h new file mode 100644 index 000000000..14ef95b07 --- /dev/null +++ b/unity-2019.4.24f1-mbe/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/unity-2019.4.24f1-mbe/mono/metadata/image-internals.h b/unity-2019.4.24f1-mbe/mono/metadata/image-internals.h new file mode 100644 index 000000000..15bff3bee --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/image-internals.h @@ -0,0 +1,23 @@ +/** + * \file + * Copyright 2015 Xamarin Inc + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#ifndef __MONO_METADATA_IMAGE_INTERNALS_H__ +#define __MONO_METADATA_IMAGE_INTERNALS_H__ + +#include + +MonoImage * +mono_find_image_owner (void *ptr); + +MonoImage* +mono_image_load_file_for_image_checked (MonoImage *image, int fileidx, MonoError *error); + +MonoImage* +mono_image_load_module_checked (MonoImage *image, int idx, MonoError *error); + +MonoImage * +mono_image_open_a_lot (const char *fname, MonoImageOpenStatus *status, gboolean refonly, gboolean load_from_context); + +#endif /* __MONO_METADATA_IMAGE_INTERNALS_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/image.c b/unity-2019.4.24f1-mbe/mono/metadata/image.c new file mode 100644 index 000000000..bcfa81553 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/image.c @@ -0,0 +1,2918 @@ +/** + * \file + * Routines for manipulating an image stored in an + * extended PE/COFF file. + * + * Authors: + * Miguel de Icaza (miguel@ximian.com) + * Paolo Molaro (lupus@ximian.com) + * + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#include +#include +#include +#include +#include +#include +#include "image.h" +#include "cil-coff.h" +#include "mono-endian.h" +#include "tabledefs.h" +#include "tokentype.h" +#include "metadata-internals.h" +#include "profiler-private.h" +#include "loader.h" +#include "marshal.h" +#include "coree.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include + +#define INVALID_ADDRESS 0xffffffff + +// Amount initially reserved in each image's mempool. +// FIXME: This number is arbitrary, a more practical number should be found +#define INITIAL_IMAGE_SIZE 512 + +/* + * The "loaded images" hashes keep track of the various assemblies and netmodules loaded + * There are four, for all combinations of [look up by path or assembly name?] + * and [normal or reflection-only load?, as in Assembly.ReflectionOnlyLoad] + */ +enum { + IMAGES_HASH_PATH = 0, + IMAGES_HASH_PATH_REFONLY = 1, + IMAGES_HASH_NAME = 2, + IMAGES_HASH_NAME_REFONLY = 3, + IMAGES_HASH_COUNT = 4 +}; +static GHashTable *loaded_images_hashes [4] = {NULL, NULL, NULL, NULL}; + +static GHashTable * +get_loaded_images_hash (gboolean refonly) +{ + int idx = refonly ? IMAGES_HASH_PATH_REFONLY : IMAGES_HASH_PATH; + return loaded_images_hashes [idx]; +} + +static GHashTable * +get_loaded_images_by_name_hash (gboolean refonly) +{ + int idx = refonly ? IMAGES_HASH_NAME_REFONLY : IMAGES_HASH_NAME; + return loaded_images_hashes [idx]; +} + +// Change the assembly set in `image` to the assembly set in `assemblyImage`. Halt if overwriting is attempted. +// Can be used on modules loaded through either the "file" or "module" mechanism +static gboolean +assign_assembly_parent_for_netmodule (MonoImage *image, MonoImage *assemblyImage, MonoError *error) +{ + // Assembly to assign + MonoAssembly *assembly = assemblyImage->assembly; + + while (1) { + // Assembly currently assigned + MonoAssembly *assemblyOld = image->assembly; + if (assemblyOld) { + if (assemblyOld == assembly) + return TRUE; + mono_error_set_bad_image (error, assemblyImage, "Attempted to load module %s which has already been loaded by assembly %s. This is not supported in Mono.", image->name, assemblyOld->image->name); + return FALSE; + } + gpointer result = mono_atomic_xchg_ptr((gpointer *)&image->assembly, assembly); + if (result == assembly) + return TRUE; + } +} + +static gboolean debug_assembly_unload = FALSE; + +#define mono_images_lock() if (mutex_inited) mono_os_mutex_lock (&images_mutex) +#define mono_images_unlock() if (mutex_inited) mono_os_mutex_unlock (&images_mutex) +static gboolean mutex_inited; +static mono_mutex_t images_mutex; + +static void install_pe_loader (void); + +typedef struct ImageUnloadHook ImageUnloadHook; +struct ImageUnloadHook { + MonoImageUnloadFunc func; + gpointer user_data; +}; + +static GSList *image_unload_hooks; + +void +mono_install_image_unload_hook (MonoImageUnloadFunc func, gpointer user_data) +{ + ImageUnloadHook *hook; + + g_return_if_fail (func != NULL); + + hook = g_new0 (ImageUnloadHook, 1); + hook->func = func; + hook->user_data = user_data; + image_unload_hooks = g_slist_prepend (image_unload_hooks, hook); +} + +void +mono_remove_image_unload_hook (MonoImageUnloadFunc func, gpointer user_data) +{ + GSList *l; + ImageUnloadHook *hook; + + for (l = image_unload_hooks; l; l = l->next) { + hook = (ImageUnloadHook *)l->data; + + if (hook->func == func && hook->user_data == user_data) { + g_free (hook); + image_unload_hooks = g_slist_delete_link (image_unload_hooks, l); + break; + } + } +} + +static void +mono_image_invoke_unload_hook (MonoImage *image) +{ + GSList *l; + ImageUnloadHook *hook; + + for (l = image_unload_hooks; l; l = l->next) { + hook = (ImageUnloadHook *)l->data; + + hook->func (image, hook->user_data); + } +} + +static GSList *image_loaders; + +void +mono_install_image_loader (const MonoImageLoader *loader) +{ + image_loaders = g_slist_prepend (image_loaders, (MonoImageLoader*)loader); +} + +/* returns offset relative to image->raw_data */ +guint32 +mono_cli_rva_image_map (MonoImage *image, guint32 addr) +{ + MonoCLIImageInfo *iinfo = (MonoCLIImageInfo *)image->image_info; + const int top = iinfo->cli_section_count; + MonoSectionTable *tables = iinfo->cli_section_tables; + int i; + + if (image->metadata_only) + return addr; + + for (i = 0; i < top; i++){ + if ((addr >= tables->st_virtual_address) && + (addr < tables->st_virtual_address + tables->st_raw_data_size)){ +#ifdef HOST_WIN32 + if (image->is_module_handle) + return addr; +#endif + return addr - tables->st_virtual_address + tables->st_raw_data_ptr; + } + tables++; + } + return INVALID_ADDRESS; +} + +/** + * mono_image_rva_map: + * \param image a \c MonoImage + * \param addr relative virtual address (RVA) + * + * This is a low-level routine used by the runtime to map relative + * virtual address (RVA) into their location in memory. + * + * \returns the address in memory for the given RVA, or NULL if the + * RVA is not valid for this image. + */ +char * +mono_image_rva_map (MonoImage *image, guint32 addr) +{ + MonoCLIImageInfo *iinfo = (MonoCLIImageInfo *)image->image_info; + const int top = iinfo->cli_section_count; + MonoSectionTable *tables = iinfo->cli_section_tables; + int i; + +#ifdef HOST_WIN32 + if (image->is_module_handle) { + if (addr && addr < image->raw_data_len) + return image->raw_data + addr; + else + return NULL; + } +#endif + + for (i = 0; i < top; i++){ + if ((addr >= tables->st_virtual_address) && + (addr < tables->st_virtual_address + tables->st_raw_data_size)){ + if (!iinfo->cli_sections [i]) { + if (!mono_image_ensure_section_idx (image, i)) + return NULL; + } + return (char*)iinfo->cli_sections [i] + + (addr - tables->st_virtual_address); + } + tables++; + } + return NULL; +} + +/** + * mono_images_init: + * + * Initialize the global variables used by this module. + */ +void +mono_images_init (void) +{ + mono_os_mutex_init_recursive (&images_mutex); + + int hash_idx; + for(hash_idx = 0; hash_idx < IMAGES_HASH_COUNT; hash_idx++) + loaded_images_hashes [hash_idx] = g_hash_table_new (g_str_hash, g_str_equal); + + debug_assembly_unload = g_hasenv ("MONO_DEBUG_ASSEMBLY_UNLOAD"); + + install_pe_loader (); + + mutex_inited = TRUE; +} + +/** + * mono_images_cleanup: + * + * Free all resources used by this module. + */ +void +mono_images_cleanup (void) +{ + GHashTableIter iter; + MonoImage *image; + + mono_os_mutex_destroy (&images_mutex); + + // If an assembly image is still loaded at shutdown, this could indicate managed code is still running. + // Reflection-only images being still loaded doesn't indicate anything as harmful, so we don't check for it. + g_hash_table_iter_init (&iter, get_loaded_images_hash (FALSE)); + while (g_hash_table_iter_next (&iter, NULL, (void**)&image)) + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Assembly image '%s' still loaded at shutdown.", image->name); + + int hash_idx; + for(hash_idx = 0; hash_idx < IMAGES_HASH_COUNT; hash_idx++) + g_hash_table_destroy (loaded_images_hashes [hash_idx]); + + mutex_inited = FALSE; +} + +/** + * mono_image_ensure_section_idx: + * \param image The image we are operating on + * \param section section number that we will load/map into memory + * + * This routine makes sure that we have an in-memory copy of + * an image section (.text, .rsrc, .data). + * + * \returns TRUE on success + */ +int +mono_image_ensure_section_idx (MonoImage *image, int section) +{ + MonoCLIImageInfo *iinfo = (MonoCLIImageInfo *)image->image_info; + MonoSectionTable *sect; + + g_return_val_if_fail (section < iinfo->cli_section_count, FALSE); + + if (iinfo->cli_sections [section] != NULL) + return TRUE; + + sect = &iinfo->cli_section_tables [section]; + + if (sect->st_raw_data_ptr + sect->st_raw_data_size > image->raw_data_len) + return FALSE; +#ifdef HOST_WIN32 + if (image->is_module_handle) + iinfo->cli_sections [section] = image->raw_data + sect->st_virtual_address; + else +#endif + /* FIXME: we ignore the writable flag since we don't patch the binary */ + iinfo->cli_sections [section] = image->raw_data + sect->st_raw_data_ptr; + return TRUE; +} + +/** + * mono_image_ensure_section: + * \param image The image we are operating on + * \param section section name that we will load/map into memory + * + * This routine makes sure that we have an in-memory copy of + * an image section (.text, .rsrc, .data). + * + * \returns TRUE on success + */ +int +mono_image_ensure_section (MonoImage *image, const char *section) +{ + MonoCLIImageInfo *ii = (MonoCLIImageInfo *)image->image_info; + int i; + + for (i = 0; i < ii->cli_section_count; i++){ + if (strncmp (ii->cli_section_tables [i].st_name, section, 8) != 0) + continue; + + return mono_image_ensure_section_idx (image, i); + } + return FALSE; +} + +static int +load_section_tables (MonoImage *image, MonoCLIImageInfo *iinfo, guint32 offset) +{ + const int top = iinfo->cli_header.coff.coff_sections; + int i; + + iinfo->cli_section_count = top; + iinfo->cli_section_tables = g_new0 (MonoSectionTable, top); + iinfo->cli_sections = g_new0 (void *, top); + + for (i = 0; i < top; i++){ + MonoSectionTable *t = &iinfo->cli_section_tables [i]; + + if (offset + sizeof (MonoSectionTable) > image->raw_data_len) + return FALSE; + memcpy (t, image->raw_data + offset, sizeof (MonoSectionTable)); + offset += sizeof (MonoSectionTable); + +#if G_BYTE_ORDER != G_LITTLE_ENDIAN + t->st_virtual_size = GUINT32_FROM_LE (t->st_virtual_size); + t->st_virtual_address = GUINT32_FROM_LE (t->st_virtual_address); + t->st_raw_data_size = GUINT32_FROM_LE (t->st_raw_data_size); + t->st_raw_data_ptr = GUINT32_FROM_LE (t->st_raw_data_ptr); + t->st_reloc_ptr = GUINT32_FROM_LE (t->st_reloc_ptr); + t->st_lineno_ptr = GUINT32_FROM_LE (t->st_lineno_ptr); + t->st_reloc_count = GUINT16_FROM_LE (t->st_reloc_count); + t->st_line_count = GUINT16_FROM_LE (t->st_line_count); + t->st_flags = GUINT32_FROM_LE (t->st_flags); +#endif + /* consistency checks here */ + } + + return TRUE; +} + +gboolean +mono_image_load_cli_header (MonoImage *image, MonoCLIImageInfo *iinfo) +{ + guint32 offset; + + offset = mono_cli_rva_image_map (image, iinfo->cli_header.datadir.pe_cli_header.rva); + if (offset == INVALID_ADDRESS) + return FALSE; + + if (offset + sizeof (MonoCLIHeader) > image->raw_data_len) + return FALSE; + memcpy (&iinfo->cli_cli_header, image->raw_data + offset, sizeof (MonoCLIHeader)); + +#if G_BYTE_ORDER != G_LITTLE_ENDIAN +#define SWAP32(x) (x) = GUINT32_FROM_LE ((x)) +#define SWAP16(x) (x) = GUINT16_FROM_LE ((x)) +#define SWAPPDE(x) do { (x).rva = GUINT32_FROM_LE ((x).rva); (x).size = GUINT32_FROM_LE ((x).size);} while (0) + SWAP32 (iinfo->cli_cli_header.ch_size); + SWAP32 (iinfo->cli_cli_header.ch_flags); + SWAP32 (iinfo->cli_cli_header.ch_entry_point); + SWAP16 (iinfo->cli_cli_header.ch_runtime_major); + SWAP16 (iinfo->cli_cli_header.ch_runtime_minor); + SWAPPDE (iinfo->cli_cli_header.ch_metadata); + SWAPPDE (iinfo->cli_cli_header.ch_resources); + SWAPPDE (iinfo->cli_cli_header.ch_strong_name); + SWAPPDE (iinfo->cli_cli_header.ch_code_manager_table); + SWAPPDE (iinfo->cli_cli_header.ch_vtable_fixups); + SWAPPDE (iinfo->cli_cli_header.ch_export_address_table_jumps); + SWAPPDE (iinfo->cli_cli_header.ch_eeinfo_table); + SWAPPDE (iinfo->cli_cli_header.ch_helper_table); + SWAPPDE (iinfo->cli_cli_header.ch_dynamic_info); + SWAPPDE (iinfo->cli_cli_header.ch_delay_load_info); + SWAPPDE (iinfo->cli_cli_header.ch_module_image); + SWAPPDE (iinfo->cli_cli_header.ch_external_fixups); + SWAPPDE (iinfo->cli_cli_header.ch_ridmap); + SWAPPDE (iinfo->cli_cli_header.ch_debug_map); + SWAPPDE (iinfo->cli_cli_header.ch_ip_map); +#undef SWAP32 +#undef SWAP16 +#undef SWAPPDE +#endif + /* Catch new uses of the fields that are supposed to be zero */ + + if ((iinfo->cli_cli_header.ch_eeinfo_table.rva != 0) || + (iinfo->cli_cli_header.ch_helper_table.rva != 0) || + (iinfo->cli_cli_header.ch_dynamic_info.rva != 0) || + (iinfo->cli_cli_header.ch_delay_load_info.rva != 0) || + (iinfo->cli_cli_header.ch_module_image.rva != 0) || + (iinfo->cli_cli_header.ch_external_fixups.rva != 0) || + (iinfo->cli_cli_header.ch_ridmap.rva != 0) || + (iinfo->cli_cli_header.ch_debug_map.rva != 0) || + (iinfo->cli_cli_header.ch_ip_map.rva != 0)){ + + /* + * No need to scare people who are testing this, I am just + * labelling this as a LAMESPEC + */ + /* g_warning ("Some fields in the CLI header which should have been zero are not zero"); */ + + } + + return TRUE; +} + +static gboolean +load_metadata_ptrs (MonoImage *image, MonoCLIImageInfo *iinfo) +{ + guint32 offset, size; + guint16 streams; + int i; + guint32 pad; + char *ptr; + + offset = mono_cli_rva_image_map (image, iinfo->cli_cli_header.ch_metadata.rva); + if (offset == INVALID_ADDRESS) + return FALSE; + + size = iinfo->cli_cli_header.ch_metadata.size; + + if (offset + size > image->raw_data_len) + return FALSE; + image->raw_metadata = image->raw_data + offset; + + /* 24.2.1: Metadata root starts here */ + ptr = image->raw_metadata; + + if (strncmp (ptr, "BSJB", 4) == 0){ + guint32 version_string_len; + + ptr += 4; + image->md_version_major = read16 (ptr); + ptr += 2; + image->md_version_minor = read16 (ptr); + ptr += 6; + + version_string_len = read32 (ptr); + ptr += 4; + image->version = g_strndup (ptr, version_string_len); + ptr += version_string_len; + pad = ptr - image->raw_metadata; + if (pad % 4) + ptr += 4 - (pad % 4); + } else + return FALSE; + + /* skip over flags */ + ptr += 2; + + streams = read16 (ptr); + ptr += 2; + + for (i = 0; i < streams; i++){ + if (strncmp (ptr + 8, "#~", 3) == 0){ + image->heap_tables.data = image->raw_metadata + read32 (ptr); + image->heap_tables.size = read32 (ptr + 4); + ptr += 8 + 3; + } else if (strncmp (ptr + 8, "#Strings", 9) == 0){ + image->heap_strings.data = image->raw_metadata + read32 (ptr); + image->heap_strings.size = read32 (ptr + 4); + ptr += 8 + 9; + } else if (strncmp (ptr + 8, "#US", 4) == 0){ + image->heap_us.data = image->raw_metadata + read32 (ptr); + image->heap_us.size = read32 (ptr + 4); + ptr += 8 + 4; + } else if (strncmp (ptr + 8, "#Blob", 6) == 0){ + image->heap_blob.data = image->raw_metadata + read32 (ptr); + image->heap_blob.size = read32 (ptr + 4); + ptr += 8 + 6; + } else if (strncmp (ptr + 8, "#GUID", 6) == 0){ + image->heap_guid.data = image->raw_metadata + read32 (ptr); + image->heap_guid.size = read32 (ptr + 4); + ptr += 8 + 6; + } else if (strncmp (ptr + 8, "#-", 3) == 0) { + image->heap_tables.data = image->raw_metadata + read32 (ptr); + image->heap_tables.size = read32 (ptr + 4); + ptr += 8 + 3; + image->uncompressed_metadata = TRUE; + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Assembly '%s' has the non-standard metadata heap #-.\nRecompile it correctly (without the /incremental switch or in Release mode).", image->name); + } else if (strncmp (ptr + 8, "#Pdb", 5) == 0) { + image->heap_pdb.data = image->raw_metadata + read32 (ptr); + image->heap_pdb.size = read32 (ptr + 4); + ptr += 8 + 5; + } else { + g_message ("Unknown heap type: %s\n", ptr + 8); + ptr += 8 + strlen (ptr + 8) + 1; + } + pad = ptr - image->raw_metadata; + if (pad % 4) + ptr += 4 - (pad % 4); + } + + i = ((MonoImageLoader*)image->loader)->load_tables (image); + + if (!image->metadata_only) { + g_assert (image->heap_guid.data); + g_assert (image->heap_guid.size >= 16); + + image->guid = mono_guid_to_string ((guint8*)image->heap_guid.data); + } else { + /* PPDB files have no guid */ + guint8 empty_guid [16]; + + memset (empty_guid, 0, sizeof (empty_guid)); + + image->guid = mono_guid_to_string (empty_guid); + } + + return i; +} + +/* + * Load representation of logical metadata tables, from the "#~" stream + */ +static gboolean +load_tables (MonoImage *image) +{ + const char *heap_tables = image->heap_tables.data; + const guint32 *rows; + guint64 valid_mask; + int valid = 0, table; + int heap_sizes; + + heap_sizes = heap_tables [6]; + image->idx_string_wide = ((heap_sizes & 0x01) == 1); + image->idx_guid_wide = ((heap_sizes & 0x02) == 2); + image->idx_blob_wide = ((heap_sizes & 0x04) == 4); + + valid_mask = read64 (heap_tables + 8); + rows = (const guint32 *) (heap_tables + 24); + + for (table = 0; table < 64; table++){ + if ((valid_mask & ((guint64) 1 << table)) == 0){ + if (table > MONO_TABLE_LAST) + continue; + image->tables [table].rows = 0; + continue; + } + if (table > MONO_TABLE_LAST) { + g_warning("bits in valid must be zero above 0x37 (II - 23.1.6)"); + } else { + image->tables [table].rows = read32 (rows); + } + rows++; + valid++; + } + + image->tables_base = (heap_tables + 24) + (4 * valid); + + /* They must be the same */ + g_assert ((const void *) image->tables_base == (const void *) rows); + + if (image->heap_pdb.size) { + /* + * Obtain token sizes from the pdb stream. + */ + /* 24 = guid + entry point */ + int pos = 24; + image->referenced_tables = read64 (image->heap_pdb.data + pos); + pos += 8; + image->referenced_table_rows = g_new0 (int, 64); + for (int i = 0; i < 64; ++i) { + if (image->referenced_tables & ((guint64)1 << i)) { + image->referenced_table_rows [i] = read32 (image->heap_pdb.data + pos); + pos += 4; + } + } + } + + mono_metadata_compute_table_bases (image); + return TRUE; +} + +gboolean +mono_image_load_metadata (MonoImage *image, MonoCLIImageInfo *iinfo) +{ + if (!load_metadata_ptrs (image, iinfo)) + return FALSE; + + return load_tables (image); +} + +void +mono_image_check_for_module_cctor (MonoImage *image) +{ + MonoTableInfo *t, *mt; + t = &image->tables [MONO_TABLE_TYPEDEF]; + mt = &image->tables [MONO_TABLE_METHOD]; + if (image_is_dynamic (image)) { + /* FIXME: */ + image->checked_module_cctor = TRUE; + return; + } + if (t->rows >= 1) { + guint32 nameidx = mono_metadata_decode_row_col (t, 0, MONO_TYPEDEF_NAME); + const char *name = mono_metadata_string_heap (image, nameidx); + if (strcmp (name, "") == 0) { + guint32 first_method = mono_metadata_decode_row_col (t, 0, MONO_TYPEDEF_METHOD_LIST) - 1; + guint32 last_method; + if (t->rows > 1) + last_method = mono_metadata_decode_row_col (t, 1, MONO_TYPEDEF_METHOD_LIST) - 1; + else + last_method = mt->rows; + for (; first_method < last_method; first_method++) { + nameidx = mono_metadata_decode_row_col (mt, first_method, MONO_METHOD_NAME); + name = mono_metadata_string_heap (image, nameidx); + if (strcmp (name, ".cctor") == 0) { + image->has_module_cctor = TRUE; + image->checked_module_cctor = TRUE; + return; + } + } + } + } + image->has_module_cctor = FALSE; + image->checked_module_cctor = TRUE; +} + +static void +load_modules (MonoImage *image) +{ + MonoTableInfo *t; + + if (image->modules) + return; + + t = &image->tables [MONO_TABLE_MODULEREF]; + image->modules = g_new0 (MonoImage *, t->rows); + image->modules_loaded = g_new0 (gboolean, t->rows); + image->module_count = t->rows; +} + +/** + * mono_image_load_module_checked: + * + * Load the module with the one-based index IDX from IMAGE and return it. Return NULL if + * it cannot be loaded. NULL without MonoError being set will be interpreted as "not found". + */ +MonoImage* +mono_image_load_module_checked (MonoImage *image, int idx, MonoError *error) +{ + MonoTableInfo *t; + MonoTableInfo *file_table; + int i; + char *base_dir; + gboolean refonly = image->ref_only; + GList *list_iter, *valid_modules = NULL; + MonoImageOpenStatus status; + + error_init (error); + + if ((image->module_count == 0) || (idx > image->module_count || idx <= 0)) + return NULL; + if (image->modules_loaded [idx - 1]) + return image->modules [idx - 1]; + + file_table = &image->tables [MONO_TABLE_FILE]; + for (i = 0; i < file_table->rows; i++) { + guint32 cols [MONO_FILE_SIZE]; + mono_metadata_decode_row (file_table, i, cols, MONO_FILE_SIZE); + if (cols [MONO_FILE_FLAGS] == FILE_CONTAINS_NO_METADATA) + continue; + valid_modules = g_list_prepend (valid_modules, (char*)mono_metadata_string_heap (image, cols [MONO_FILE_NAME])); + } + + t = &image->tables [MONO_TABLE_MODULEREF]; + base_dir = g_path_get_dirname (image->name); + + { + char *module_ref; + const char *name; + guint32 cols [MONO_MODULEREF_SIZE]; + /* if there is no file table, we try to load the module... */ + int valid = file_table->rows == 0; + + mono_metadata_decode_row (t, idx - 1, cols, MONO_MODULEREF_SIZE); + name = mono_metadata_string_heap (image, cols [MONO_MODULEREF_NAME]); + for (list_iter = valid_modules; list_iter; list_iter = list_iter->next) { + /* be safe with string dups, but we could just compare string indexes */ + if (strcmp (list_iter->data, name) == 0) { + valid = TRUE; + break; + } + } + if (valid) { + module_ref = g_build_filename (base_dir, name, NULL); + MonoImage *moduleImage = mono_image_open_full (module_ref, &status, refonly); + if (moduleImage) { + if (!assign_assembly_parent_for_netmodule (moduleImage, image, error)) { + mono_image_close (moduleImage); + g_free (module_ref); + g_free (base_dir); + g_list_free (valid_modules); + return NULL; + } + + image->modules [idx - 1] = moduleImage; + +#ifdef HOST_WIN32 + if (image->modules [idx - 1]->is_module_handle) + mono_image_fixup_vtable (image->modules [idx - 1]); +#endif + /* g_print ("loaded module %s from %s (%p)\n", module_ref, image->name, image->assembly); */ + } + g_free (module_ref); + } + } + + image->modules_loaded [idx - 1] = TRUE; + + g_free (base_dir); + g_list_free (valid_modules); + + return image->modules [idx - 1]; +} + +/** + * mono_image_load_module: + */ +MonoImage* +mono_image_load_module (MonoImage *image, int idx) +{ + MonoError error; + MonoImage *result = mono_image_load_module_checked (image, idx, &error); + mono_error_assert_ok (&error); + return result; +} + +static gpointer +class_key_extract (gpointer value) +{ + MonoClass *klass = (MonoClass *)value; + + return GUINT_TO_POINTER (klass->type_token); +} + +static gpointer* +class_next_value (gpointer value) +{ + MonoClassDef *klass = (MonoClassDef *)value; + + return (gpointer*)&klass->next_class_cache; +} + +/** + * mono_image_init: + */ +void +mono_image_init (MonoImage *image) +{ + mono_os_mutex_init_recursive (&image->lock); + mono_os_mutex_init_recursive (&image->szarray_cache_lock); + + image->mempool = mono_mempool_new_size (INITIAL_IMAGE_SIZE); + mono_internal_hash_table_init (&image->class_cache, + g_direct_hash, + class_key_extract, + class_next_value); + image->field_cache = mono_conc_hashtable_new (NULL, NULL); + + image->typespec_cache = mono_conc_hashtable_new (NULL, NULL); + image->memberref_signatures = g_hash_table_new (NULL, NULL); + image->helper_signatures = g_hash_table_new (g_str_hash, g_str_equal); + image->method_signatures = g_hash_table_new (NULL, NULL); + + image->property_hash = mono_property_hash_new (); +} + +#if G_BYTE_ORDER != G_LITTLE_ENDIAN +#define SWAP64(x) (x) = GUINT64_FROM_LE ((x)) +#define SWAP32(x) (x) = GUINT32_FROM_LE ((x)) +#define SWAP16(x) (x) = GUINT16_FROM_LE ((x)) +#define SWAPPDE(x) do { (x).rva = GUINT32_FROM_LE ((x).rva); (x).size = GUINT32_FROM_LE ((x).size);} while (0) +#else +#define SWAP64(x) +#define SWAP32(x) +#define SWAP16(x) +#define SWAPPDE(x) +#endif + +/* + * Returns < 0 to indicate an error. + */ +static int +do_load_header (MonoImage *image, MonoDotNetHeader *header, int offset) +{ + MonoDotNetHeader64 header64; + +#ifdef HOST_WIN32 + if (!image->is_module_handle) +#endif + if (offset + sizeof (MonoDotNetHeader32) > image->raw_data_len) + return -1; + + memcpy (header, image->raw_data + offset, sizeof (MonoDotNetHeader)); + + if (header->pesig [0] != 'P' || header->pesig [1] != 'E') + return -1; + + /* endian swap the fields common between PE and PE+ */ + SWAP32 (header->coff.coff_time); + SWAP32 (header->coff.coff_symptr); + SWAP32 (header->coff.coff_symcount); + SWAP16 (header->coff.coff_machine); + SWAP16 (header->coff.coff_sections); + SWAP16 (header->coff.coff_opt_header_size); + SWAP16 (header->coff.coff_attributes); + /* MonoPEHeader */ + SWAP32 (header->pe.pe_code_size); + SWAP32 (header->pe.pe_uninit_data_size); + SWAP32 (header->pe.pe_rva_entry_point); + SWAP32 (header->pe.pe_rva_code_base); + SWAP32 (header->pe.pe_rva_data_base); + SWAP16 (header->pe.pe_magic); + + /* now we are ready for the basic tests */ + + if (header->pe.pe_magic == 0x10B) { + offset += sizeof (MonoDotNetHeader); + SWAP32 (header->pe.pe_data_size); + if (header->coff.coff_opt_header_size != (sizeof (MonoDotNetHeader) - sizeof (MonoCOFFHeader) - 4)) + return -1; + + SWAP32 (header->nt.pe_image_base); /* must be 0x400000 */ + SWAP32 (header->nt.pe_stack_reserve); + SWAP32 (header->nt.pe_stack_commit); + SWAP32 (header->nt.pe_heap_reserve); + SWAP32 (header->nt.pe_heap_commit); + } else if (header->pe.pe_magic == 0x20B) { + /* PE32+ file format */ + if (header->coff.coff_opt_header_size != (sizeof (MonoDotNetHeader64) - sizeof (MonoCOFFHeader) - 4)) + return -1; + memcpy (&header64, image->raw_data + offset, sizeof (MonoDotNetHeader64)); + offset += sizeof (MonoDotNetHeader64); + /* copy the fields already swapped. the last field, pe_data_size, is missing */ + memcpy (&header64, header, sizeof (MonoDotNetHeader) - 4); + /* FIXME: we lose bits here, but we don't use this stuff internally, so we don't care much. + * will be fixed when we change MonoDotNetHeader to not match the 32 bit variant + */ + SWAP64 (header64.nt.pe_image_base); + header->nt.pe_image_base = header64.nt.pe_image_base; + SWAP64 (header64.nt.pe_stack_reserve); + header->nt.pe_stack_reserve = header64.nt.pe_stack_reserve; + SWAP64 (header64.nt.pe_stack_commit); + header->nt.pe_stack_commit = header64.nt.pe_stack_commit; + SWAP64 (header64.nt.pe_heap_reserve); + header->nt.pe_heap_reserve = header64.nt.pe_heap_reserve; + SWAP64 (header64.nt.pe_heap_commit); + header->nt.pe_heap_commit = header64.nt.pe_heap_commit; + + header->nt.pe_section_align = header64.nt.pe_section_align; + header->nt.pe_file_alignment = header64.nt.pe_file_alignment; + header->nt.pe_os_major = header64.nt.pe_os_major; + header->nt.pe_os_minor = header64.nt.pe_os_minor; + header->nt.pe_user_major = header64.nt.pe_user_major; + header->nt.pe_user_minor = header64.nt.pe_user_minor; + header->nt.pe_subsys_major = header64.nt.pe_subsys_major; + header->nt.pe_subsys_minor = header64.nt.pe_subsys_minor; + header->nt.pe_reserved_1 = header64.nt.pe_reserved_1; + header->nt.pe_image_size = header64.nt.pe_image_size; + header->nt.pe_header_size = header64.nt.pe_header_size; + header->nt.pe_checksum = header64.nt.pe_checksum; + header->nt.pe_subsys_required = header64.nt.pe_subsys_required; + header->nt.pe_dll_flags = header64.nt.pe_dll_flags; + header->nt.pe_loader_flags = header64.nt.pe_loader_flags; + header->nt.pe_data_dir_count = header64.nt.pe_data_dir_count; + + /* copy the datadir */ + memcpy (&header->datadir, &header64.datadir, sizeof (MonoPEDatadir)); + } else { + return -1; + } + + /* MonoPEHeaderNT: not used yet */ + SWAP32 (header->nt.pe_section_align); /* must be 8192 */ + SWAP32 (header->nt.pe_file_alignment); /* must be 512 or 4096 */ + SWAP16 (header->nt.pe_os_major); /* must be 4 */ + SWAP16 (header->nt.pe_os_minor); /* must be 0 */ + SWAP16 (header->nt.pe_user_major); + SWAP16 (header->nt.pe_user_minor); + SWAP16 (header->nt.pe_subsys_major); + SWAP16 (header->nt.pe_subsys_minor); + SWAP32 (header->nt.pe_reserved_1); + SWAP32 (header->nt.pe_image_size); + SWAP32 (header->nt.pe_header_size); + SWAP32 (header->nt.pe_checksum); + SWAP16 (header->nt.pe_subsys_required); + SWAP16 (header->nt.pe_dll_flags); + SWAP32 (header->nt.pe_loader_flags); + SWAP32 (header->nt.pe_data_dir_count); + + /* MonoDotNetHeader: mostly unused */ + SWAPPDE (header->datadir.pe_export_table); + SWAPPDE (header->datadir.pe_import_table); + SWAPPDE (header->datadir.pe_resource_table); + SWAPPDE (header->datadir.pe_exception_table); + SWAPPDE (header->datadir.pe_certificate_table); + SWAPPDE (header->datadir.pe_reloc_table); + SWAPPDE (header->datadir.pe_debug); + SWAPPDE (header->datadir.pe_copyright); + SWAPPDE (header->datadir.pe_global_ptr); + SWAPPDE (header->datadir.pe_tls_table); + SWAPPDE (header->datadir.pe_load_config_table); + SWAPPDE (header->datadir.pe_bound_import); + SWAPPDE (header->datadir.pe_iat); + SWAPPDE (header->datadir.pe_delay_import_desc); + SWAPPDE (header->datadir.pe_cli_header); + SWAPPDE (header->datadir.pe_reserved); + +#ifdef HOST_WIN32 + if (image->is_module_handle) + image->raw_data_len = header->nt.pe_image_size; +#endif + + return offset; +} + +gboolean +mono_image_load_pe_data (MonoImage *image) +{ + return ((MonoImageLoader*)image->loader)->load_pe_data (image); +} + +static gboolean +pe_image_load_pe_data (MonoImage *image) +{ + MonoCLIImageInfo *iinfo; + MonoDotNetHeader *header; + MonoMSDOSHeader msdos; + gint32 offset = 0; + + iinfo = (MonoCLIImageInfo *)image->image_info; + header = &iinfo->cli_header; + +#ifdef HOST_WIN32 + if (!image->is_module_handle) +#endif + if (offset + sizeof (msdos) > image->raw_data_len) + goto invalid_image; + memcpy (&msdos, image->raw_data + offset, sizeof (msdos)); + + if (!(msdos.msdos_sig [0] == 'M' && msdos.msdos_sig [1] == 'Z')) + goto invalid_image; + + msdos.pe_offset = GUINT32_FROM_LE (msdos.pe_offset); + + offset = msdos.pe_offset; + + offset = do_load_header (image, header, offset); + if (offset < 0) + goto invalid_image; + + /* + * this tests for a x86 machine type, but itanium, amd64 and others could be used, too. + * we skip this test. + if (header->coff.coff_machine != 0x14c) + goto invalid_image; + */ + +#if 0 + /* + * The spec says that this field should contain 6.0, but Visual Studio includes a new compiler, + * which produces binaries with 7.0. From Sergey: + * + * The reason is that MSVC7 uses traditional compile/link + * sequence for CIL executables, and VS.NET (and Framework + * SDK) includes linker version 7, that puts 7.0 in this + * field. That's why it's currently not possible to load VC + * binaries with Mono. This field is pretty much meaningless + * anyway (what linker?). + */ + if (header->pe.pe_major != 6 || header->pe.pe_minor != 0) + goto invalid_image; +#endif + + /* + * FIXME: byte swap all addresses here for header. + */ + + if (!load_section_tables (image, iinfo, offset)) + goto invalid_image; + + return TRUE; + +invalid_image: + return FALSE; +} + +gboolean +mono_image_load_cli_data (MonoImage *image) +{ + return ((MonoImageLoader*)image->loader)->load_cli_data (image); +} + +static gboolean +pe_image_load_cli_data (MonoImage *image) +{ + MonoCLIImageInfo *iinfo; + MonoDotNetHeader *header; + + iinfo = (MonoCLIImageInfo *)image->image_info; + header = &iinfo->cli_header; + + /* Load the CLI header */ + if (!mono_image_load_cli_header (image, iinfo)) + return FALSE; + + if (!mono_image_load_metadata (image, iinfo)) + return FALSE; + + return TRUE; +} + +void +mono_image_load_names (MonoImage *image) +{ + /* modules don't have an assembly table row */ + if (image->tables [MONO_TABLE_ASSEMBLY].rows) { + image->assembly_name = mono_metadata_string_heap (image, + mono_metadata_decode_row_col (&image->tables [MONO_TABLE_ASSEMBLY], + 0, MONO_ASSEMBLY_NAME)); + } + + /* Portable pdb images don't have a MODULE row */ + if (image->tables [MONO_TABLE_MODULE].rows) { + image->module_name = mono_metadata_string_heap (image, + mono_metadata_decode_row_col (&image->tables [MONO_TABLE_MODULE], + 0, MONO_MODULE_NAME)); + } +} + +static gboolean +pe_image_load_tables (MonoImage *image) +{ + return TRUE; +} + +static gboolean +pe_image_match (MonoImage *image) +{ + if (image->raw_data [0] == 'M' && image->raw_data [1] == 'Z') + return TRUE; + return FALSE; +} + +static const MonoImageLoader pe_loader = { + pe_image_match, + pe_image_load_pe_data, + pe_image_load_cli_data, + pe_image_load_tables, +}; + +static void +install_pe_loader (void) +{ + mono_install_image_loader (&pe_loader); +} + +/* +Ignored assemblies. + +There are some assemblies we need to ignore because they include an implementation that doesn't work under mono. +Mono provides its own implementation of those assemblies so it's safe to do so. + +The ignored_assemblies list is generated using tools/nuget-hash-extractor and feeding the problematic nugets to it. + +Right now the list of nugets are the ones that provide the assemblies in $ignored_assemblies_file_names. + +This is to be removed once a proper fix is shipped through nuget. + +Please keep this in sync with mcs/tools/xbuild/data/deniedAssembliesList.txt +If any assemblies are added/removed, then this should be regenerated with: + + $ mono tools/nuget-hash-extractor/nuget-hash-extractor.exe nugets guids_for_msbuild > mcs/tools/xbuild/data/deniedAssembliesList.txt + +*/ + +typedef enum { + SYS_RT_INTEROP_RUNTIME_INFO = 0, //System.Runtime.InteropServices.RuntimeInformation + SYS_GLOBALIZATION_EXT = 1, //System.Globalization.Extensions + SYS_IO_COMPRESSION = 2, //System.IO.Compression + SYS_NET_HTTP = 3, //System.Net.Http + SYS_TEXT_ENC_CODEPAGES = 4, //System.Text.Encoding.CodePages + SYS_THREADING_OVERLAPPED = 5, //System.Threading.Overlapped +} IgnoredAssemblyNames; + +typedef struct { + int hash; + int assembly_name; + const char guid [40]; +} IgnoredAssembly; + +typedef struct { + int assembly_name; + guint16 major, minor, build, revision; +} IgnoredAssemblyVersion; + +const char *ignored_assemblies_file_names[] = { + "System.Runtime.InteropServices.RuntimeInformation.dll", + "System.Globalization.Extensions.dll", + "System.IO.Compression.dll", + "System.Net.Http.dll", + "System.Text.Encoding.CodePages.dll", + "System.Threading.Overlapped.dll" +}; + +#define IGNORED_ASSEMBLY(HASH, NAME, GUID, VER_STR) { .hash = HASH, .assembly_name = NAME, .guid = GUID } + +static const IgnoredAssembly ignored_assemblies [] = { + IGNORED_ASSEMBLY (0x1136045D, SYS_GLOBALIZATION_EXT, "475DBF02-9F68-44F1-8FB5-C9F69F1BD2B1", "4.0.0 net46"), + IGNORED_ASSEMBLY (0x358C9723, SYS_GLOBALIZATION_EXT, "5FCD54F0-4B97-4259-875D-30E481F02EA2", "4.0.1 net46"), + IGNORED_ASSEMBLY (0x450A096A, SYS_GLOBALIZATION_EXT, "E9FCFF5B-4DE1-4BDC-9CE8-08C640FC78CC", "4.3.0 net46"), + IGNORED_ASSEMBLY (0x1CBD59A2, SYS_IO_COMPRESSION, "44FCA06C-A510-4B3E-BDBF-D08D697EF65A", "4.1.0 net46"), + IGNORED_ASSEMBLY (0x5E393C29, SYS_IO_COMPRESSION, "3A58A219-266B-47C3-8BE8-4E4F394147AB", "4.3.0 net46"), + IGNORED_ASSEMBLY (0x27726A90, SYS_NET_HTTP, "269B562C-CC15-4736-B1B1-68D4A43CAA98", "4.1.0 net46"), + IGNORED_ASSEMBLY (0x10CADA75, SYS_NET_HTTP, "EA2EC6DC-51DD-479C-BFC2-E713FB9E7E47", "4.1.1 net46"), + IGNORED_ASSEMBLY (0x8437178B, SYS_NET_HTTP, "C0E04D9C-70CF-48A6-A179-FBFD8CE69FD0", "4.3.0 net46"), + IGNORED_ASSEMBLY (0xFAFDA422, SYS_NET_HTTP, "817F01C3-4011-477D-890A-98232B85553D", "4.3.1 net46"), + IGNORED_ASSEMBLY (0x472FA630, SYS_NET_HTTP, "09D4A140-061C-4884-9B63-22067E841931", "4.3.2 net46"), + IGNORED_ASSEMBLY (0xDB9397A9, SYS_NET_HTTP, "56203551-6937-47C1-9246-346A733913EE", "4.3.3 net46"), + IGNORED_ASSEMBLY (0x46A4A1C5, SYS_RT_INTEROP_RUNTIME_INFO, "F13660F8-9D0D-419F-BA4E-315693DD26EA", "4.0.0 net45"), + IGNORED_ASSEMBLY (0xD07383BB, SYS_RT_INTEROP_RUNTIME_INFO, "DD91439F-3167-478E-BD2C-BF9C036A1395", "4.3.0 net45"), + IGNORED_ASSEMBLY (0x911D9EC3, SYS_TEXT_ENC_CODEPAGES, "C142254F-DEB5-46A7-AE43-6F10320D1D1F", "4.0.1 net46"), + IGNORED_ASSEMBLY (0xFA686A38, SYS_TEXT_ENC_CODEPAGES, "FD178CD4-EF4F-44D5-9C3F-812B1E25126B", "4.3.0 net46"), + IGNORED_ASSEMBLY (0xF6D18A2E, SYS_TEXT_ENC_CODEPAGES, "F5CCCBEC-E1AD-4DBB-9B44-9B42C86B94B8", "4.4.0 net461"), + IGNORED_ASSEMBLY (0xAA21986B, SYS_THREADING_OVERLAPPED, "9F5D4F09-787A-458A-BA08-553AA71470F1", "4.0.0 net46"), + IGNORED_ASSEMBLY (0x7D927C2A, SYS_THREADING_OVERLAPPED, "FCBD003B-2BB4-4940-BAEF-63AF520C2336", "4.0.1 net46"), + IGNORED_ASSEMBLY (0x6FE03EE2, SYS_THREADING_OVERLAPPED, "87697E71-D192-4F0B-BAD4-02BBC7793005", "4.3.0 net46") +}; + + +const char *ignored_assemblies_names[] = { + "System.Runtime.InteropServices.RuntimeInformation", + "System.Globalization.Extensions", + "System.IO.Compression", + "System.Net.Http", + "System.Text.Encoding.CodePages", + "System.Threading.Overlapped" +}; + +#define IGNORED_ASM_VER(NAME, MAJOR, MINOR, BUILD, REVISION) { .assembly_name = NAME, .major = MAJOR, .minor = MINOR, .build = BUILD, .revision = REVISION } + +static const IgnoredAssemblyVersion ignored_assembly_versions [] = { + IGNORED_ASM_VER (SYS_GLOBALIZATION_EXT, 4, 0, 0, 0), + IGNORED_ASM_VER (SYS_GLOBALIZATION_EXT, 4, 0, 1, 0), + IGNORED_ASM_VER (SYS_GLOBALIZATION_EXT, 4, 0, 2, 0), + IGNORED_ASM_VER (SYS_IO_COMPRESSION, 4, 1, 0, 0), + IGNORED_ASM_VER (SYS_IO_COMPRESSION, 4, 1, 2, 0), + IGNORED_ASM_VER (SYS_NET_HTTP, 4, 1, 0, 0), + IGNORED_ASM_VER (SYS_NET_HTTP, 4, 1, 0, 1), + IGNORED_ASM_VER (SYS_NET_HTTP, 4, 1, 1, 0), + IGNORED_ASM_VER (SYS_NET_HTTP, 4, 1, 1, 1), + IGNORED_ASM_VER (SYS_RT_INTEROP_RUNTIME_INFO, 4, 0, 0, 0), + IGNORED_ASM_VER (SYS_RT_INTEROP_RUNTIME_INFO, 4, 0, 1, 0), + IGNORED_ASM_VER (SYS_TEXT_ENC_CODEPAGES, 4, 0, 1, 0), + IGNORED_ASM_VER (SYS_TEXT_ENC_CODEPAGES, 4, 0, 2, 0), + IGNORED_ASM_VER (SYS_TEXT_ENC_CODEPAGES, 4, 1, 0, 0), + IGNORED_ASM_VER (SYS_THREADING_OVERLAPPED, 4, 0, 0, 0), + IGNORED_ASM_VER (SYS_THREADING_OVERLAPPED, 4, 0, 1, 0), + IGNORED_ASM_VER (SYS_THREADING_OVERLAPPED, 4, 0, 2, 0), +}; + +gboolean +mono_assembly_is_problematic_version (const char *name, guint16 major, guint16 minor, guint16 build, guint16 revision) +{ + for (int i = 0; i < G_N_ELEMENTS (ignored_assembly_versions); ++i) { + if (ignored_assembly_versions [i].major != major || + ignored_assembly_versions [i].minor != minor || + ignored_assembly_versions [i].build != build || + ignored_assembly_versions [i].revision != revision) + continue; + if (!strcmp (ignored_assemblies_names [ignored_assembly_versions [i].assembly_name], name)) + return TRUE; + } + return FALSE; +} + +/* +Equivalent C# code: + static void Main () { + string str = "..."; + int h = 5381; + for (int i = 0; i < str.Length; ++i) + h = ((h << 5) + h) ^ str[i]; + + Console.WriteLine ("{0:X}", h); + } +*/ +static int +hash_guid (const char *str) +{ + int h = 5381; + while (*str) { + h = ((h << 5) + h) ^ *str; + ++str; + } + + return h; +} + +static gboolean +is_problematic_image (MonoImage *image) +{ + int h = hash_guid (image->guid); + + //TODO make this more cache effiecient. + // Either sort by hash and bseach or use SoA and make the linear search more cache efficient. + for (int i = 0; i < G_N_ELEMENTS (ignored_assemblies); ++i) { + if (ignored_assemblies [i].hash == h && !strcmp (image->guid, ignored_assemblies [i].guid)) { + const char *needle = ignored_assemblies_file_names [ignored_assemblies [i].assembly_name]; + size_t needle_len = strlen (needle); + size_t asm_len = strlen (image->name); + if (asm_len > needle_len && !g_ascii_strcasecmp (image->name + (asm_len - needle_len), needle)) + return TRUE; + } + } + return FALSE; +} + +static MonoImage * +do_mono_image_load (MonoImage *image, MonoImageOpenStatus *status, + gboolean care_about_cli, gboolean care_about_pecoff) +{ + MonoCLIImageInfo *iinfo; + MonoDotNetHeader *header; + GSList *errors = NULL; + GSList *l; + + MONO_PROFILER_RAISE (image_loading, (image)); + + mono_image_init (image); + + iinfo = (MonoCLIImageInfo *)image->image_info; + header = &iinfo->cli_header; + + if (!image->metadata_only) { + for (l = image_loaders; l; l = l->next) { + MonoImageLoader *loader = (MonoImageLoader *)l->data; + if (loader->match (image)) { + image->loader = loader; + break; + } + } + if (!image->loader) { + if (status) + *status = MONO_IMAGE_IMAGE_INVALID; + goto invalid_image; + } + + if (status) + *status = MONO_IMAGE_IMAGE_INVALID; + + if (care_about_pecoff == FALSE) + goto done; + + if (image->loader == &pe_loader && !mono_verifier_verify_pe_data (image, &errors)) + goto invalid_image; + + if (!mono_image_load_pe_data (image)) + goto invalid_image; + } else { + image->loader = (MonoImageLoader*)&pe_loader; + } + + if (care_about_cli == FALSE) { + goto done; + } + + if (image->loader == &pe_loader && !image->metadata_only && !mono_verifier_verify_cli_data (image, &errors)) + goto invalid_image; + + if (!mono_image_load_cli_data (image)) + goto invalid_image; + + if (!image->ref_only && is_problematic_image (image)) { + if (image->load_from_context) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Loading problematic image %s", image->name); + } else { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Denying load of problematic image %s", image->name); + *status = MONO_IMAGE_IMAGE_INVALID; + goto invalid_image; + } + } + + if (image->loader == &pe_loader && !image->metadata_only && !mono_verifier_verify_table_data (image, &errors)) + goto invalid_image; + + mono_image_load_names (image); + + load_modules (image); + +done: + MONO_PROFILER_RAISE (image_loaded, (image)); + if (status) + *status = MONO_IMAGE_OK; + + return image; + +invalid_image: + if (errors) { + MonoVerifyInfo *info = (MonoVerifyInfo *)errors->data; + g_warning ("Could not load image %s due to %s", image->name, info->message); + mono_free_verify_list (errors); + } + MONO_PROFILER_RAISE (image_failed, (image)); + mono_image_close (image); + return NULL; +} + +static MonoImage * +do_mono_image_open (const char *fname, MonoImageOpenStatus *status, + gboolean care_about_cli, gboolean care_about_pecoff, gboolean refonly, gboolean metadata_only, gboolean load_from_context) +{ + 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) { + gchar *ffname = mono_portability_find_file (fname, TRUE); + if (ffname) { + filed = mono_file_map_open (ffname); + g_free (ffname); + } + } + + if (filed == NULL) { + if (status) + *status = MONO_IMAGE_ERROR_ERRNO; + g_free((void*)fname_remap); + return NULL; + } + } + + image = g_new0 (MonoImage, 1); + image->raw_buffer_used = TRUE; + image->raw_data_len = mono_file_map_size (filed); + image->raw_data = (char *)mono_file_map (image->raw_data_len, MONO_MMAP_READ|MONO_MMAP_PRIVATE, mono_file_map_fd (filed), 0, &image->raw_data_handle); +#if defined(HAVE_MMAP) && !defined (HOST_WIN32) + if (!image->raw_data) { + image->fileio_used = TRUE; + image->raw_data = (char *)mono_file_map_fileio (image->raw_data_len, MONO_MMAP_READ|MONO_MMAP_PRIVATE, mono_file_map_fd (filed), 0, &image->raw_data_handle); + } +#endif + if (!image->raw_data) { + mono_file_map_close (filed); + g_free (image); + if (status) + *status = MONO_IMAGE_IMAGE_INVALID; + g_free((void*)fname_remap); + return NULL; + } + iinfo = g_new0 (MonoCLIImageInfo, 1); + image->image_info = iinfo; + image->name = mono_path_resolve_symlinks (fname); + image->ref_only = refonly; + image->metadata_only = metadata_only; + image->load_from_context = load_from_context; + image->ref_count = 1; + /* if MONO_SECURITY_MODE_CORE_CLR is set then determine if this image is platform code */ + 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); +} + +/** + * mono_image_loaded_full: + * \param name path or assembly name of the image to load + * \param refonly Check with respect to reflection-only loads? + * + * This routine verifies that the given image is loaded. + * It checks either reflection-only loads only, or normal loads only, as specified by parameter. + * + * \returns the loaded \c MonoImage, or NULL on failure. + */ +MonoImage * +mono_image_loaded_full (const char *name, gboolean refonly) +{ + MonoImage *res; + + mono_images_lock (); + res = (MonoImage *)g_hash_table_lookup (get_loaded_images_hash (refonly), name); + if (!res) + res = (MonoImage *)g_hash_table_lookup (get_loaded_images_by_name_hash (refonly), name); + mono_images_unlock (); + + return res; +} + +/** + * mono_image_loaded: + * \param name path or assembly name of the image to load + * This routine verifies that the given image is loaded. Reflection-only loads do not count. + * \returns the loaded \c MonoImage, or NULL on failure. + */ +MonoImage * +mono_image_loaded (const char *name) +{ + return mono_image_loaded_full (name, FALSE); +} + +typedef struct { + MonoImage *res; + const char* guid; +} GuidData; + +static void +find_by_guid (gpointer key, gpointer val, gpointer user_data) +{ + GuidData *data = (GuidData *)user_data; + MonoImage *image; + + if (data->res) + return; + image = (MonoImage *)val; + if (strcmp (data->guid, mono_image_get_guid (image)) == 0) + data->res = image; +} + +/** + * mono_image_loaded_by_guid_full: + */ +MonoImage * +mono_image_loaded_by_guid_full (const char *guid, gboolean refonly) +{ + GuidData data; + GHashTable *loaded_images = get_loaded_images_hash (refonly); + data.res = NULL; + data.guid = guid; + + mono_images_lock (); + g_hash_table_foreach (loaded_images, find_by_guid, &data); + mono_images_unlock (); + return data.res; +} + +/** + * mono_image_loaded_by_guid: + */ +MonoImage * +mono_image_loaded_by_guid (const char *guid) +{ + return mono_image_loaded_by_guid_full (guid, FALSE); +} + +static MonoImage * +register_image (MonoImage *image) +{ + MonoImage *image2; + GHashTable *loaded_images = get_loaded_images_hash (image->ref_only); + + mono_images_lock (); + image2 = (MonoImage *)g_hash_table_lookup (loaded_images, image->name); + + if (image2) { + /* Somebody else beat us to it */ + mono_image_addref (image2); + mono_images_unlock (); + mono_image_close (image); + return image2; + } + + GHashTable *loaded_images_by_name = get_loaded_images_by_name_hash (image->ref_only); + g_hash_table_insert (loaded_images, image->name, image); + if (image->assembly_name && (g_hash_table_lookup (loaded_images_by_name, image->assembly_name) == NULL)) + g_hash_table_insert (loaded_images_by_name, (char *) image->assembly_name, image); + mono_images_unlock (); + + return image; +} + +MonoImage * +mono_image_open_from_data_internal (char *data, guint32 data_len, gboolean need_copy, MonoImageOpenStatus *status, gboolean refonly, gboolean metadata_only, const char *name) +{ + MonoCLIImageInfo *iinfo; + MonoImage *image; + char *datac; + + if (!data || !data_len) { + if (status) + *status = MONO_IMAGE_IMAGE_INVALID; + return NULL; + } + datac = data; + if (need_copy) { + datac = (char *)g_try_malloc (data_len); + if (!datac) { + if (status) + *status = MONO_IMAGE_ERROR_ERRNO; + return NULL; + } + memcpy (datac, data, data_len); + } + + image = g_new0 (MonoImage, 1); + image->raw_data = datac; + image->raw_data_len = data_len; + image->raw_data_allocated = need_copy; + image->name = (name == NULL) ? g_strdup_printf ("data-%p", datac) : g_strdup(name); + iinfo = g_new0 (MonoCLIImageInfo, 1); + image->image_info = iinfo; + image->ref_only = refonly; + image->metadata_only = metadata_only; + image->ref_count = 1; + + image = do_mono_image_load (image, status, TRUE, TRUE); + if (image == NULL) + return NULL; + + return register_image (image); +} + +/** + * mono_image_open_from_data_with_name: + */ +MonoImage * +mono_image_open_from_data_with_name (char *data, guint32 data_len, gboolean need_copy, MonoImageOpenStatus *status, gboolean refonly, const char *name) +{ + return mono_image_open_from_data_internal (data, data_len, need_copy, status, refonly, FALSE, name); +} + +/** + * mono_image_open_from_data_full: + */ +MonoImage * +mono_image_open_from_data_full (char *data, guint32 data_len, gboolean need_copy, MonoImageOpenStatus *status, gboolean refonly) +{ + return mono_image_open_from_data_with_name (data, data_len, need_copy, status, refonly, NULL); +} + +/** + * mono_image_open_from_data: + */ +MonoImage * +mono_image_open_from_data (char *data, guint32 data_len, gboolean need_copy, MonoImageOpenStatus *status) +{ + return mono_image_open_from_data_full (data, data_len, need_copy, status, FALSE); +} + +#ifdef HOST_WIN32 +/* fname is not duplicated. */ +MonoImage* +mono_image_open_from_module_handle (HMODULE module_handle, char* fname, gboolean has_entry_point, MonoImageOpenStatus* status) +{ + MonoImage* image; + MonoCLIImageInfo* iinfo; + + image = g_new0 (MonoImage, 1); + image->raw_data = (char*) module_handle; + image->is_module_handle = TRUE; + iinfo = g_new0 (MonoCLIImageInfo, 1); + image->image_info = iinfo; + image->name = fname; + image->ref_count = has_entry_point ? 0 : 1; + image->has_entry_point = has_entry_point; + + image = do_mono_image_load (image, status, TRUE, TRUE); + if (image == NULL) + return NULL; + + return register_image (image); +} +#endif + +/** + * mono_image_open_full: + */ +MonoImage * +mono_image_open_full (const char *fname, MonoImageOpenStatus *status, gboolean refonly) +{ + return mono_image_open_a_lot (fname, status, refonly, FALSE); +} + +MonoImage * +mono_image_open_a_lot (const char *fname, MonoImageOpenStatus *status, gboolean refonly, gboolean load_from_context) +{ + MonoImage *image; + GHashTable *loaded_images = get_loaded_images_hash (refonly); + char *absfname; + + g_return_val_if_fail (fname != NULL, NULL); + +#ifdef HOST_WIN32 + // Win32 path: If we are running with mixed-mode assemblies enabled (ie have loaded mscoree.dll), + // then assemblies need to be loaded with LoadLibrary: + if (!refonly && coree_module_handle) { + HMODULE module_handle; + guint16 *fname_utf16; + DWORD last_error; + + absfname = mono_path_resolve_symlinks (fname); + fname_utf16 = NULL; + + /* There is little overhead because the OS loader lock is held by LoadLibrary. */ + mono_images_lock (); + image = g_hash_table_lookup (loaded_images, absfname); + if (image) { // Image already loaded + g_assert (image->is_module_handle); + if (image->has_entry_point && image->ref_count == 0) { + /* Increment reference count on images loaded outside of the runtime. */ + fname_utf16 = g_utf8_to_utf16 (absfname, -1, NULL, NULL, NULL); + /* The image is already loaded because _CorDllMain removes images from the hash. */ + module_handle = LoadLibrary (fname_utf16); + g_assert (module_handle == (HMODULE) image->raw_data); + } + mono_image_addref (image); + mono_images_unlock (); + if (fname_utf16) + g_free (fname_utf16); + g_free (absfname); + return image; + } + + // Image not loaded, load it now + fname_utf16 = g_utf8_to_utf16 (absfname, -1, NULL, NULL, NULL); + module_handle = MonoLoadImage (fname_utf16); + if (status && module_handle == NULL) + last_error = mono_w32error_get_last (); + + /* mono_image_open_from_module_handle is called by _CorDllMain. */ + image = g_hash_table_lookup (loaded_images, absfname); + if (image) + mono_image_addref (image); + mono_images_unlock (); + + g_free (fname_utf16); + + if (module_handle == NULL) { + g_assert (!image); + g_free (absfname); + if (status) { + if (last_error == ERROR_BAD_EXE_FORMAT || last_error == STATUS_INVALID_IMAGE_FORMAT) + *status = MONO_IMAGE_IMAGE_INVALID; + else { + if (last_error == ERROR_FILE_NOT_FOUND || last_error == ERROR_PATH_NOT_FOUND) + errno = ENOENT; + else + errno = 0; + } + } + return NULL; + } + + if (image) { + g_assert (image->is_module_handle); + g_assert (image->has_entry_point); + g_free (absfname); + return image; + } + + return mono_image_open_from_module_handle (module_handle, absfname, FALSE, status); + } +#endif + + absfname = mono_path_resolve_symlinks (fname); + + /* + * The easiest solution would be to do all the loading inside the mutex, + * but that would lead to scalability problems. So we let the loading + * happen outside the mutex, and if multiple threads happen to load + * the same image, we discard all but the first copy. + */ + mono_images_lock (); + image = (MonoImage *)g_hash_table_lookup (loaded_images, absfname); + g_free (absfname); + + if (image) { // Image already loaded + mono_image_addref (image); + mono_images_unlock (); + return image; + } + mono_images_unlock (); + + // Image not loaded, load it now + image = do_mono_image_open (fname, status, TRUE, TRUE, refonly, FALSE, load_from_context); + if (image == NULL) + return NULL; + + return register_image (image); +} + +/** + * mono_image_open: + * \param fname filename that points to the module we want to open + * \param status An error condition is returned in this field + * \returns An open image of type \c MonoImage or NULL on error. + * The caller holds a temporary reference to the returned image which should be cleared + * when no longer needed by calling \c mono_image_close. + * if NULL, then check the value of \p status for details on the error + */ +MonoImage * +mono_image_open (const char *fname, MonoImageOpenStatus *status) +{ + return mono_image_open_full (fname, status, FALSE); +} + +/** + * mono_pe_file_open: + * \param fname filename that points to the module we want to open + * \param status An error condition is returned in this field + * \returns An open image of type \c MonoImage or NULL on error. if + * NULL, then check the value of \p status for details on the error. + * This variant for \c mono_image_open DOES NOT SET UP CLI METADATA. + * It's just a PE file loader, used for \c FileVersionInfo. It also does + * not use the image cache. + */ +MonoImage * +mono_pe_file_open (const char *fname, MonoImageOpenStatus *status) +{ + g_return_val_if_fail (fname != NULL, NULL); + + return do_mono_image_open (fname, status, FALSE, TRUE, FALSE, FALSE, FALSE); +} + +/** + * mono_image_open_raw + * \param fname filename that points to the module we want to open + * \param status An error condition is returned in this field + * \returns an image without loading neither pe or cli data. + * Use mono_image_load_pe_data and mono_image_load_cli_data to load them. + */ +MonoImage * +mono_image_open_raw (const char *fname, MonoImageOpenStatus *status) +{ + g_return_val_if_fail (fname != NULL, NULL); + + return do_mono_image_open (fname, status, FALSE, FALSE, FALSE, FALSE, FALSE); +} + +/* + * mono_image_open_metadata_only: + * + * Open an image which contains metadata only without a PE header. + */ +MonoImage * +mono_image_open_metadata_only (const char *fname, MonoImageOpenStatus *status) +{ + return do_mono_image_open (fname, status, TRUE, TRUE, FALSE, TRUE, FALSE); +} + +/** + * mono_image_fixup_vtable: + */ +void +mono_image_fixup_vtable (MonoImage *image) +{ +#ifdef HOST_WIN32 + MonoCLIImageInfo *iinfo; + MonoPEDirEntry *de; + MonoVTableFixup *vtfixup; + int count; + gpointer slot; + guint16 slot_type; + int slot_count; + + g_assert (image->is_module_handle); + + iinfo = image->image_info; + de = &iinfo->cli_cli_header.ch_vtable_fixups; + if (!de->rva || !de->size) + return; + vtfixup = (MonoVTableFixup*) mono_image_rva_map (image, de->rva); + if (!vtfixup) + return; + + count = de->size / sizeof (MonoVTableFixup); + while (count--) { + if (!vtfixup->rva || !vtfixup->count) + continue; + + slot = mono_image_rva_map (image, vtfixup->rva); + g_assert (slot); + slot_type = vtfixup->type; + slot_count = vtfixup->count; + if (slot_type & VTFIXUP_TYPE_32BIT) + while (slot_count--) { + *((guint32*) slot) = (guint32) mono_marshal_get_vtfixup_ftnptr (image, *((guint32*) slot), slot_type); + slot = ((guint32*) slot) + 1; + } + else if (slot_type & VTFIXUP_TYPE_64BIT) + while (slot_count--) { + *((guint64*) slot) = (guint64) mono_marshal_get_vtfixup_ftnptr (image, *((guint64*) slot), slot_type); + slot = ((guint32*) slot) + 1; + } + else + g_assert_not_reached(); + + vtfixup++; + } +#else + g_assert_not_reached(); +#endif +} + +static void +free_hash_table (gpointer key, gpointer val, gpointer user_data) +{ + g_hash_table_destroy ((GHashTable*)val); +} + +/* +static void +free_mr_signatures (gpointer key, gpointer val, gpointer user_data) +{ + mono_metadata_free_method_signature ((MonoMethodSignature*)val); +} +*/ + +static void +free_array_cache_entry (gpointer key, gpointer val, gpointer user_data) +{ + g_slist_free ((GSList*)val); +} + +/** + * mono_image_addref: + * \param image The image file we wish to add a reference to + * Increases the reference count of an image. + */ +void +mono_image_addref (MonoImage *image) +{ + mono_atomic_inc_i32 (&image->ref_count); +} + +void +mono_dynamic_stream_reset (MonoDynamicStream* stream) +{ + stream->alloc_size = stream->index = stream->offset = 0; + g_free (stream->data); + stream->data = NULL; + if (stream->hash) { + g_hash_table_destroy (stream->hash); + stream->hash = NULL; + } +} + +static inline void +free_hash (GHashTable *hash) +{ + if (hash) + g_hash_table_destroy (hash); +} + +void +mono_wrapper_caches_free (MonoWrapperCaches *cache) +{ + free_hash (cache->delegate_invoke_cache); + free_hash (cache->delegate_begin_invoke_cache); + free_hash (cache->delegate_end_invoke_cache); + free_hash (cache->runtime_invoke_cache); + free_hash (cache->runtime_invoke_vtype_cache); + + free_hash (cache->delegate_abstract_invoke_cache); + + free_hash (cache->runtime_invoke_direct_cache); + free_hash (cache->managed_wrapper_cache); + + free_hash (cache->native_wrapper_cache); + free_hash (cache->native_wrapper_aot_cache); + free_hash (cache->native_wrapper_check_cache); + free_hash (cache->native_wrapper_aot_check_cache); + + free_hash (cache->native_func_wrapper_aot_cache); + free_hash (cache->remoting_invoke_cache); + free_hash (cache->synchronized_cache); + free_hash (cache->unbox_wrapper_cache); + free_hash (cache->cominterop_invoke_cache); + free_hash (cache->cominterop_wrapper_cache); + free_hash (cache->thunk_invoke_cache); +} + +static void +mono_image_close_except_pools_all (MonoImage**images, int image_count) +{ + for (int i = 0; i < image_count; ++i) { + if (images [i]) { + if (!mono_image_close_except_pools (images [i])) + images [i] = NULL; + } + } +} + +/* + * Returns whether mono_image_close_finish() must be called as well. + * We must unload images in two steps because clearing the domain in + * SGen requires the class metadata to be intact, but we need to free + * the mono_g_hash_tables in case a collection occurs during domain + * unloading and the roots would trip up the GC. + */ +gboolean +mono_image_close_except_pools (MonoImage *image) +{ + MonoImage *image2; + GHashTable *loaded_images, *loaded_images_by_name; + int i; + + g_return_val_if_fail (image != NULL, FALSE); + + /* + * Atomically decrement the refcount and remove ourselves from the hash tables, so + * register_image () can't grab an image which is being closed. + */ + mono_images_lock (); + + if (mono_atomic_dec_i32 (&image->ref_count) > 0) { + mono_images_unlock (); + return FALSE; + } + + loaded_images = get_loaded_images_hash (image->ref_only); + loaded_images_by_name = get_loaded_images_by_name_hash (image->ref_only); + image2 = (MonoImage *)g_hash_table_lookup (loaded_images, image->name); + if (image == image2) { + /* This is not true if we are called from mono_image_open () */ + g_hash_table_remove (loaded_images, image->name); + } + if (image->assembly_name && (g_hash_table_lookup (loaded_images_by_name, image->assembly_name) == image)) + g_hash_table_remove (loaded_images_by_name, (char *) image->assembly_name); + + mono_images_unlock (); + +#ifdef HOST_WIN32 + if (image->is_module_handle && image->has_entry_point) { + mono_images_lock (); + if (image->ref_count == 0) { + /* Image will be closed by _CorDllMain. */ + FreeLibrary ((HMODULE) image->raw_data); + mono_images_unlock (); + return FALSE; + } + mono_images_unlock (); + } +#endif + + MONO_PROFILER_RAISE (image_unloading, (image)); + + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Unloading image %s [%p].", image->name, image); + + mono_image_invoke_unload_hook (image); + + mono_metadata_clean_for_image (image); + + /* + * The caches inside a MonoImage might refer to metadata which is stored in referenced + * assemblies, so we can't release these references in mono_assembly_close () since the + * MonoImage might outlive its associated MonoAssembly. + */ + if (image->references && !image_is_dynamic (image)) { + for (i = 0; i < image->nreferences; i++) { + if (image->references [i] && image->references [i] != REFERENCE_MISSING) { + if (!mono_assembly_close_except_image_pools (image->references [i])) + image->references [i] = NULL; + } + } + } else { + if (image->references) { + g_free (image->references); + image->references = NULL; + } + } + +#ifdef HOST_WIN32 + mono_images_lock (); + if (image->is_module_handle && !image->has_entry_point) + FreeLibrary ((HMODULE) image->raw_data); + mono_images_unlock (); +#endif + + if (image->raw_buffer_used) { + if (image->raw_data != NULL) { +#ifndef HOST_WIN32 + if (image->fileio_used) + mono_file_unmap_fileio (image->raw_data, image->raw_data_handle); + else +#endif + mono_file_unmap (image->raw_data, image->raw_data_handle); + } + } + + if (image->raw_data_allocated) { + /* FIXME: do we need this? (image is disposed anyway) */ + /* image->raw_metadata and cli_sections might lie inside image->raw_data */ + MonoCLIImageInfo *ii = (MonoCLIImageInfo *)image->image_info; + + if ((image->raw_metadata > image->raw_data) && + (image->raw_metadata <= (image->raw_data + image->raw_data_len))) + image->raw_metadata = NULL; + + for (i = 0; i < ii->cli_section_count; i++) + if (((char*)(ii->cli_sections [i]) > image->raw_data) && + ((char*)(ii->cli_sections [i]) <= ((char*)image->raw_data + image->raw_data_len))) + ii->cli_sections [i] = NULL; + + g_free (image->raw_data); + } + + if (debug_assembly_unload) { + image->name = g_strdup_printf ("%s - UNLOADED", image->name); + } else { + g_free (image->name); + g_free (image->guid); + g_free (image->version); + } + + if (image->method_cache) + g_hash_table_destroy (image->method_cache); + if (image->methodref_cache) + g_hash_table_destroy (image->methodref_cache); + mono_internal_hash_table_destroy (&image->class_cache); + mono_conc_hashtable_destroy (image->field_cache); + if (image->array_cache) { + g_hash_table_foreach (image->array_cache, free_array_cache_entry, NULL); + g_hash_table_destroy (image->array_cache); + } + if (image->szarray_cache) + g_hash_table_destroy (image->szarray_cache); + if (image->ptr_cache) + g_hash_table_destroy (image->ptr_cache); + if (image->name_cache) { + g_hash_table_foreach (image->name_cache, free_hash_table, NULL); + g_hash_table_destroy (image->name_cache); + } + + free_hash (image->delegate_bound_static_invoke_cache); + free_hash (image->runtime_invoke_vcall_cache); + free_hash (image->ldfld_wrapper_cache); + free_hash (image->ldflda_wrapper_cache); + free_hash (image->stfld_wrapper_cache); + free_hash (image->isinst_cache); + free_hash (image->castclass_cache); + free_hash (image->icall_wrapper_cache); + free_hash (image->proxy_isinst_cache); + free_hash (image->var_cache_slow); + free_hash (image->mvar_cache_slow); + free_hash (image->var_cache_constrained); + free_hash (image->mvar_cache_constrained); + free_hash (image->wrapper_param_names); + free_hash (image->pinvoke_scopes); + free_hash (image->pinvoke_scope_filenames); + free_hash (image->native_func_wrapper_cache); + mono_conc_hashtable_destroy (image->typespec_cache); + free_hash (image->weak_field_indexes); + + mono_wrapper_caches_free (&image->wrapper_caches); + + /* The ownership of signatures is not well defined */ + g_hash_table_destroy (image->memberref_signatures); + g_hash_table_destroy (image->helper_signatures); + g_hash_table_destroy (image->method_signatures); + + if (image->rgctx_template_hash) + g_hash_table_destroy (image->rgctx_template_hash); + + if (image->property_hash) + mono_property_hash_destroy (image->property_hash); + + /* + reflection_info_unregister_classes is only required by dynamic images, which will not be properly + cleared during shutdown as we don't perform regular appdomain unload for the root one. + */ + g_assert (!image->reflection_info_unregister_classes || mono_runtime_is_shutting_down ()); + image->reflection_info_unregister_classes = NULL; + + if (image->interface_bitset) { + mono_unload_interface_ids (image->interface_bitset); + mono_bitset_free (image->interface_bitset); + } + if (image->image_info){ + MonoCLIImageInfo *ii = (MonoCLIImageInfo *)image->image_info; + + if (ii->cli_section_tables) + g_free (ii->cli_section_tables); + if (ii->cli_sections) + g_free (ii->cli_sections); + g_free (image->image_info); + } + + mono_image_close_except_pools_all (image->files, image->file_count); + mono_image_close_except_pools_all (image->modules, image->module_count); + if (image->modules_loaded) + g_free (image->modules_loaded); + + mono_os_mutex_destroy (&image->szarray_cache_lock); + mono_os_mutex_destroy (&image->lock); + + /*g_print ("destroy image %p (dynamic: %d)\n", image, image->dynamic);*/ + if (image_is_dynamic (image)) { + /* Dynamic images are GC_MALLOCed */ + g_free ((char*)image->module_name); + mono_dynamic_image_free ((MonoDynamicImage*)image); + } + + MONO_PROFILER_RAISE (image_unloaded, (image)); + + return TRUE; +} + +static void +mono_image_close_all (MonoImage**images, int image_count) +{ + for (int i = 0; i < image_count; ++i) { + if (images [i]) + mono_image_close_finish (images [i]); + } + if (images) + g_free (images); +} + +void +mono_image_close_finish (MonoImage *image) +{ + int i; + + if (image->references && !image_is_dynamic (image)) { + for (i = 0; i < image->nreferences; i++) { + if (image->references [i] && image->references [i] != REFERENCE_MISSING) + mono_assembly_close_finish (image->references [i]); + } + + g_free (image->references); + image->references = NULL; + } + + mono_image_close_all (image->files, image->file_count); + mono_image_close_all (image->modules, image->module_count); + +#ifndef DISABLE_PERFCOUNTERS + /* FIXME: use an explicit subtraction method as soon as it's available */ + mono_atomic_fetch_add_i32 (&mono_perfcounters->loader_bytes, -1 * mono_mempool_get_allocated (image->mempool)); +#endif + + if (!image_is_dynamic (image)) { + if (debug_assembly_unload) + mono_mempool_invalidate (image->mempool); + else { + mono_mempool_destroy (image->mempool); + g_free (image); + } + } else { + if (debug_assembly_unload) + mono_mempool_invalidate (image->mempool); + else { + mono_mempool_destroy (image->mempool); + mono_dynamic_image_free_image ((MonoDynamicImage*)image); + } + } +} + +/** + * mono_image_close: + * \param image The image file we wish to close + * Closes an image file, deallocates all memory consumed and + * unmaps all possible sections of the file + */ +void +mono_image_close (MonoImage *image) +{ + if (mono_image_close_except_pools (image)) + mono_image_close_finish (image); +} + +/** + * mono_image_strerror: + * \param status an code indicating the result from a recent operation + * \returns a string describing the error + */ +const char * +mono_image_strerror (MonoImageOpenStatus status) +{ + switch (status){ + case MONO_IMAGE_OK: + return "success"; + case MONO_IMAGE_ERROR_ERRNO: + return strerror (errno); + case MONO_IMAGE_IMAGE_INVALID: + return "File does not contain a valid CIL image"; + case MONO_IMAGE_MISSING_ASSEMBLYREF: + return "An assembly was referenced, but could not be found"; + } + return "Internal error"; +} + +static gpointer +mono_image_walk_resource_tree (MonoCLIImageInfo *info, guint32 res_id, + guint32 lang_id, gunichar2 *name, + MonoPEResourceDirEntry *entry, + MonoPEResourceDir *root, guint32 level) +{ + gboolean is_string, is_dir; + guint32 name_offset, dir_offset; + + /* Level 0 holds a directory entry for each type of resource + * (identified by ID or name). + * + * Level 1 holds a directory entry for each named resource + * item, and each "anonymous" item of a particular type of + * resource. + * + * Level 2 holds a directory entry for each language pointing to + * the actual data. + */ + is_string = MONO_PE_RES_DIR_ENTRY_NAME_IS_STRING (*entry); + name_offset = MONO_PE_RES_DIR_ENTRY_NAME_OFFSET (*entry); + + is_dir = MONO_PE_RES_DIR_ENTRY_IS_DIR (*entry); + dir_offset = MONO_PE_RES_DIR_ENTRY_DIR_OFFSET (*entry); + + if(level==0) { + if (is_string) + return NULL; + } else if (level==1) { + if (res_id != name_offset) + return NULL; +#if 0 + if(name!=NULL && + is_string==TRUE && name!=lookup (name_offset)) { + return(NULL); + } +#endif + } else if (level==2) { + if (is_string || (lang_id != 0 && name_offset != lang_id)) + return NULL; + } else { + g_assert_not_reached (); + } + + if (is_dir) { + MonoPEResourceDir *res_dir=(MonoPEResourceDir *)(((char *)root)+dir_offset); + MonoPEResourceDirEntry *sub_entries=(MonoPEResourceDirEntry *)(res_dir+1); + guint32 entries, i; + + entries = GUINT16_FROM_LE (res_dir->res_named_entries) + GUINT16_FROM_LE (res_dir->res_id_entries); + + for(i=0; irde_data_offset = GUINT32_TO_LE (data_entry->rde_data_offset); + res->rde_size = GUINT32_TO_LE (data_entry->rde_size); + res->rde_codepage = GUINT32_TO_LE (data_entry->rde_codepage); + res->rde_reserved = GUINT32_TO_LE (data_entry->rde_reserved); + + return (res); + } +} + +/** + * mono_image_lookup_resource: + * \param image the image to look up the resource in + * \param res_id A \c MONO_PE_RESOURCE_ID_ that represents the resource ID to lookup. + * \param lang_id The language id. + * \param name the resource name to lookup. + * \returns NULL if not found, otherwise a pointer to the in-memory representation + * of the given resource. The caller should free it using \c g_free when no longer + * needed. + */ +gpointer +mono_image_lookup_resource (MonoImage *image, guint32 res_id, guint32 lang_id, gunichar2 *name) +{ + MonoCLIImageInfo *info; + MonoDotNetHeader *header; + MonoPEDatadir *datadir; + MonoPEDirEntry *rsrc; + MonoPEResourceDir *resource_dir; + MonoPEResourceDirEntry *res_entries; + guint32 entries, i; + + if(image==NULL) { + return(NULL); + } + + mono_image_ensure_section_idx (image, MONO_SECTION_RSRC); + + info = (MonoCLIImageInfo *)image->image_info; + if(info==NULL) { + return(NULL); + } + + header=&info->cli_header; + if(header==NULL) { + return(NULL); + } + + datadir=&header->datadir; + if(datadir==NULL) { + return(NULL); + } + + rsrc=&datadir->pe_resource_table; + if(rsrc==NULL) { + return(NULL); + } + + resource_dir=(MonoPEResourceDir *)mono_image_rva_map (image, rsrc->rva); + if(resource_dir==NULL) { + return(NULL); + } + + entries = GUINT16_FROM_LE (resource_dir->res_named_entries) + GUINT16_FROM_LE (resource_dir->res_id_entries); + res_entries=(MonoPEResourceDirEntry *)(resource_dir+1); + + for(i=0; iimage_info)->cli_cli_header.ch_entry_point; +} + +/** + * mono_image_get_resource: + * \param image the image where the resource will be looked up. + * \param offset The offset to add to the resource + * \param size a pointer to an int where the size of the resource will be stored + * + * This is a low-level routine that fetches a resource from the + * metadata that starts at a given \p offset. The \p size parameter is + * filled with the data field as encoded in the metadata. + * + * \returns the pointer to the resource whose offset is \p offset. + */ +const char* +mono_image_get_resource (MonoImage *image, guint32 offset, guint32 *size) +{ + MonoCLIImageInfo *iinfo = (MonoCLIImageInfo *)image->image_info; + MonoCLIHeader *ch = &iinfo->cli_cli_header; + const char* data; + + if (!ch->ch_resources.rva || offset + 4 > ch->ch_resources.size) + return NULL; + + data = mono_image_rva_map (image, ch->ch_resources.rva); + if (!data) + return NULL; + data += offset; + if (size) + *size = read32 (data); + data += 4; + return data; +} + +// Returning NULL with no error set will be interpeted as "not found" +MonoImage* +mono_image_load_file_for_image_checked (MonoImage *image, int fileidx, MonoError *error) +{ + char *base_dir, *name; + MonoImage *res; + MonoTableInfo *t = &image->tables [MONO_TABLE_FILE]; + const char *fname; + guint32 fname_id; + + error_init (error); + + if (fileidx < 1 || fileidx > t->rows) + return NULL; + + mono_image_lock (image); + if (image->files && image->files [fileidx - 1]) { + mono_image_unlock (image); + return image->files [fileidx - 1]; + } + mono_image_unlock (image); + + fname_id = mono_metadata_decode_row_col (t, fileidx - 1, MONO_FILE_NAME); + fname = mono_metadata_string_heap (image, fname_id); + base_dir = g_path_get_dirname (image->name); + name = g_build_filename (base_dir, fname, NULL); + res = mono_image_open (name, NULL); + if (!res) + goto done; + + mono_image_lock (image); + if (image->files && image->files [fileidx - 1]) { + MonoImage *old = res; + res = image->files [fileidx - 1]; + mono_image_unlock (image); + mono_image_close (old); + } else { + int i; + /* g_print ("loaded file %s from %s (%p)\n", name, image->name, image->assembly); */ + if (!assign_assembly_parent_for_netmodule (res, image, error)) { + mono_image_unlock (image); + mono_image_close (res); + return NULL; + } + + for (i = 0; i < res->module_count; ++i) { + if (res->modules [i] && !res->modules [i]->assembly) + res->modules [i]->assembly = image->assembly; + } + + if (!image->files) { + image->files = g_new0 (MonoImage*, t->rows); + image->file_count = t->rows; + } + image->files [fileidx - 1] = res; + mono_image_unlock (image); + /* vtable fixup can't happen with the image lock held */ +#ifdef HOST_WIN32 + if (res->is_module_handle) + mono_image_fixup_vtable (res); +#endif + } + +done: + g_free (name); + g_free (base_dir); + return res; +} + +/** + * mono_image_load_file_for_image: + */ +MonoImage* +mono_image_load_file_for_image (MonoImage *image, int fileidx) +{ + MonoError error; + MonoImage *result = mono_image_load_file_for_image_checked (image, fileidx, &error); + mono_error_assert_ok (&error); + return result; +} + +/** + * mono_image_get_strong_name: + * \param image a MonoImage + * \param size a \c guint32 pointer, or NULL. + * + * If the image has a strong name, and \p size is not NULL, the value + * pointed to by size will have the size of the strong name. + * + * \returns NULL if the image does not have a strong name, or a + * pointer to the public key. + */ +const char* +mono_image_get_strong_name (MonoImage *image, guint32 *size) +{ + MonoCLIImageInfo *iinfo = (MonoCLIImageInfo *)image->image_info; + MonoPEDirEntry *de = &iinfo->cli_cli_header.ch_strong_name; + const char* data; + + if (!de->size || !de->rva) + return NULL; + data = mono_image_rva_map (image, de->rva); + if (!data) + return NULL; + if (size) + *size = de->size; + return data; +} + +/** + * mono_image_strong_name_position: + * \param image a \c MonoImage + * \param size a \c guint32 pointer, or NULL. + * + * If the image has a strong name, and \p size is not NULL, the value + * pointed to by size will have the size of the strong name. + * + * \returns the position within the image file where the strong name + * is stored. + */ +guint32 +mono_image_strong_name_position (MonoImage *image, guint32 *size) +{ + MonoCLIImageInfo *iinfo = (MonoCLIImageInfo *)image->image_info; + MonoPEDirEntry *de = &iinfo->cli_cli_header.ch_strong_name; + guint32 pos; + + if (size) + *size = de->size; + if (!de->size || !de->rva) + return 0; + pos = mono_cli_rva_image_map (image, de->rva); + return pos == INVALID_ADDRESS ? 0 : pos; +} + +/** + * mono_image_get_public_key: + * \param image a \c MonoImage + * \param size a \c guint32 pointer, or NULL. + * + * This is used to obtain the public key in the \p image. + * + * If the image has a public key, and \p size is not NULL, the value + * pointed to by size will have the size of the public key. + * + * \returns NULL if the image does not have a public key, or a pointer + * to the public key. + */ +const char* +mono_image_get_public_key (MonoImage *image, guint32 *size) +{ + const char *pubkey; + guint32 len, tok; + + if (image_is_dynamic (image)) { + if (size) + *size = ((MonoDynamicImage*)image)->public_key_len; + return (char*)((MonoDynamicImage*)image)->public_key; + } + if (image->tables [MONO_TABLE_ASSEMBLY].rows != 1) + return NULL; + tok = mono_metadata_decode_row_col (&image->tables [MONO_TABLE_ASSEMBLY], 0, MONO_ASSEMBLY_PUBLIC_KEY); + if (!tok) + return NULL; + pubkey = mono_metadata_blob_heap (image, tok); + len = mono_metadata_decode_blob_size (pubkey, &pubkey); + if (size) + *size = len; + return pubkey; +} + +/** + * mono_image_get_name: + * \param name a \c MonoImage + * \returns the name of the assembly. + */ +const char* +mono_image_get_name (MonoImage *image) +{ + return image->assembly_name; +} + +/** + * mono_image_get_filename: + * \param image a \c MonoImage + * Used to get the filename that hold the actual \c MonoImage + * \returns the filename. + */ +const char* +mono_image_get_filename (MonoImage *image) +{ + return image->name; +} + +/** + * mono_image_get_guid: + */ +const char* +mono_image_get_guid (MonoImage *image) +{ + return image->guid; +} + +/** + * mono_image_get_table_info: + */ +const MonoTableInfo* +mono_image_get_table_info (MonoImage *image, int table_id) +{ + if (table_id < 0 || table_id >= MONO_TABLE_NUM) + return NULL; + return &image->tables [table_id]; +} + +/** + * mono_image_get_table_rows: + */ +int +mono_image_get_table_rows (MonoImage *image, int table_id) +{ + if (table_id < 0 || table_id >= MONO_TABLE_NUM) + return 0; + return image->tables [table_id].rows; +} + +/** + * mono_table_info_get_rows: + */ +int +mono_table_info_get_rows (const MonoTableInfo *table) +{ + return table->rows; +} + +/** + * mono_image_get_assembly: + * \param image the \c MonoImage . + * Use this routine to get the assembly that owns this image. + * \returns the assembly that holds this image. + */ +MonoAssembly* +mono_image_get_assembly (MonoImage *image) +{ + return image->assembly; +} + +/** + * mono_image_is_dynamic: + * \param image the \c MonoImage + * + * Determines if the given image was created dynamically through the + * \c System.Reflection.Emit API + * \returns TRUE if the image was created dynamically, FALSE if not. + */ +gboolean +mono_image_is_dynamic (MonoImage *image) +{ + return image_is_dynamic (image); +} + +/** + * mono_image_has_authenticode_entry: + * \param image the \c MonoImage + * Use this routine to determine if the image has a Authenticode + * Certificate Table. + * \returns TRUE if the image contains an authenticode entry in the PE + * directory. + */ +gboolean +mono_image_has_authenticode_entry (MonoImage *image) +{ + MonoCLIImageInfo *iinfo = (MonoCLIImageInfo *)image->image_info; + MonoDotNetHeader *header = &iinfo->cli_header; + if (!header) + return FALSE; + MonoPEDirEntry *de = &header->datadir.pe_certificate_table; + // the Authenticode "pre" (non ASN.1) header is 8 bytes long + return ((de->rva != 0) && (de->size > 8)); +} + +gpointer +mono_image_alloc (MonoImage *image, guint size) +{ + gpointer res; + +#ifndef DISABLE_PERFCOUNTERS + mono_atomic_fetch_add_i32 (&mono_perfcounters->loader_bytes, size); +#endif + mono_image_lock (image); + res = mono_mempool_alloc (image->mempool, size); + mono_image_unlock (image); + + return res; +} + +gpointer +mono_image_alloc0 (MonoImage *image, guint size) +{ + gpointer res; + +#ifndef DISABLE_PERFCOUNTERS + mono_atomic_fetch_add_i32 (&mono_perfcounters->loader_bytes, size); +#endif + mono_image_lock (image); + res = mono_mempool_alloc0 (image->mempool, size); + mono_image_unlock (image); + + return res; +} + +char* +mono_image_strdup (MonoImage *image, const char *s) +{ + char *res; + +#ifndef DISABLE_PERFCOUNTERS + mono_atomic_fetch_add_i32 (&mono_perfcounters->loader_bytes, (gint32)strlen (s)); +#endif + mono_image_lock (image); + res = mono_mempool_strdup (image->mempool, s); + mono_image_unlock (image); + + return res; +} + +char* +mono_image_strdup_vprintf (MonoImage *image, const char *format, va_list args) +{ + char *buf; + mono_image_lock (image); + buf = mono_mempool_strdup_vprintf (image->mempool, format, args); + mono_image_unlock (image); +#ifndef DISABLE_PERFCOUNTERS + mono_atomic_fetch_add_i32 (&mono_perfcounters->loader_bytes, (gint32)strlen (buf)); +#endif + return buf; +} + +char* +mono_image_strdup_printf (MonoImage *image, const char *format, ...) +{ + char *buf; + va_list args; + + va_start (args, format); + buf = mono_image_strdup_vprintf (image, format, args); + va_end (args); + return buf; +} + +GList* +g_list_prepend_image (MonoImage *image, GList *list, gpointer data) +{ + GList *new_list; + + new_list = (GList *)mono_image_alloc (image, sizeof (GList)); + new_list->data = data; + new_list->prev = list ? list->prev : NULL; + new_list->next = list; + + if (new_list->prev) + new_list->prev->next = new_list; + if (list) + list->prev = new_list; + + return new_list; +} + +GSList* +g_slist_append_image (MonoImage *image, GSList *list, gpointer data) +{ + GSList *new_list; + + new_list = (GSList *)mono_image_alloc (image, sizeof (GSList)); + new_list->data = data; + new_list->next = NULL; + + return g_slist_concat (list, new_list); +} + +void +mono_image_lock (MonoImage *image) +{ + mono_locks_os_acquire (&image->lock, ImageDataLock); +} + +void +mono_image_unlock (MonoImage *image) +{ + mono_locks_os_release (&image->lock, ImageDataLock); +} + + +/** + * mono_image_property_lookup: + * Lookup a property on \p image . Used to store very rare fields of \c MonoClass and \c MonoMethod . + * + * LOCKING: Takes the image lock + */ +gpointer +mono_image_property_lookup (MonoImage *image, gpointer subject, guint32 property) +{ + gpointer res; + + mono_image_lock (image); + res = mono_property_hash_lookup (image->property_hash, subject, property); + mono_image_unlock (image); + + return res; +} + +/** + * mono_image_property_insert: + * Insert a new property \p property with value \p value on \p subject in \p + * image. Used to store very rare fields of \c MonoClass and \c MonoMethod. + * + * LOCKING: Takes the image lock + */ +void +mono_image_property_insert (MonoImage *image, gpointer subject, guint32 property, gpointer value) +{ + CHECKED_METADATA_STORE_LOCAL (image->mempool, value); + mono_image_lock (image); + mono_property_hash_insert (image->property_hash, subject, property, value); + mono_image_unlock (image); +} + +/** + * mono_image_property_remove: + * Remove all properties associated with \p subject in \p image. Used to store very rare fields of \c MonoClass and \c MonoMethod . + * + * LOCKING: Takes the image lock + */ +void +mono_image_property_remove (MonoImage *image, gpointer subject) +{ + mono_image_lock (image); + mono_property_hash_remove_object (image->property_hash, subject); + mono_image_unlock (image); +} + +void +mono_image_append_class_to_reflection_info_set (MonoClass *klass) +{ + MonoImage *image = klass->image; + g_assert (image_is_dynamic (image)); + mono_image_lock (image); + image->reflection_info_unregister_classes = g_slist_prepend_mempool (image->mempool, image->reflection_info_unregister_classes, klass); + mono_image_unlock (image); +} + +// This is support for the mempool reference tracking feature in checked-build, but lives in image.c due to use of static variables of this file. + +/** + * mono_find_image_owner: + * + * Find the image, if any, which a given pointer is located in the memory of. + */ +MonoImage * +mono_find_image_owner (void *ptr) +{ + mono_images_lock (); + + MonoImage *owner = NULL; + + // Iterate over both by-path image hashes + const int hash_candidates[] = {IMAGES_HASH_PATH, IMAGES_HASH_PATH_REFONLY}; + int hash_idx; + for (hash_idx = 0; !owner && hash_idx < G_N_ELEMENTS (hash_candidates); hash_idx++) + { + GHashTable *target = loaded_images_hashes [hash_candidates [hash_idx]]; + GHashTableIter iter; + MonoImage *image; + + // Iterate over images within a hash + g_hash_table_iter_init (&iter, target); + while (!owner && g_hash_table_iter_next(&iter, NULL, (gpointer *)&image)) + { + mono_image_lock (image); + if (mono_mempool_contains_addr (image->mempool, ptr)) + owner = image; + mono_image_unlock (image); + } + } + + mono_images_unlock (); + + return owner; +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/image.h b/unity-2019.4.24f1-mbe/mono/metadata/image.h new file mode 100644 index 000000000..72836fddb --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/image.h @@ -0,0 +1,88 @@ +/** + * \file + */ + +#ifndef _MONONET_METADATA_IMAGE_H_ +#define _MONONET_METADATA_IMAGE_H_ + +#include +#include +#include + +MONO_BEGIN_DECLS + +typedef struct _MonoImage MonoImage; +typedef struct _MonoAssembly MonoAssembly; +typedef struct _MonoAssemblyName MonoAssemblyName; +typedef struct _MonoTableInfo MonoTableInfo; + +typedef enum { + MONO_IMAGE_OK, + MONO_IMAGE_ERROR_ERRNO, + MONO_IMAGE_MISSING_ASSEMBLYREF, + MONO_IMAGE_IMAGE_INVALID +} MonoImageOpenStatus; + +MONO_API void mono_images_init (void); +MONO_API void mono_images_cleanup (void); + +MONO_API MonoImage *mono_image_open (const char *fname, + MonoImageOpenStatus *status); +MONO_API MonoImage *mono_image_open_full (const char *fname, + MonoImageOpenStatus *status, mono_bool refonly); +MONO_API MonoImage *mono_pe_file_open (const char *fname, + MonoImageOpenStatus *status); +MONO_API MonoImage *mono_image_open_from_data (char *data, uint32_t data_len, mono_bool need_copy, + MonoImageOpenStatus *status); +MONO_API MonoImage *mono_image_open_from_data_full (char *data, uint32_t data_len, mono_bool need_copy, + MonoImageOpenStatus *status, mono_bool refonly); +MONO_API MonoImage *mono_image_open_from_data_with_name (char *data, uint32_t data_len, mono_bool need_copy, + MonoImageOpenStatus *status, mono_bool refonly, const char *name); +MONO_API void mono_image_fixup_vtable (MonoImage *image); +MONO_API MonoImage *mono_image_loaded (const char *name); +MONO_API MonoImage *mono_image_loaded_full (const char *name, mono_bool refonly); +MONO_API MonoImage *mono_image_loaded_by_guid (const char *guid); +MONO_API MonoImage *mono_image_loaded_by_guid_full (const char *guid, mono_bool refonly); +MONO_API void mono_image_init (MonoImage *image); +MONO_API void mono_image_close (MonoImage *image); +MONO_API void mono_image_addref (MonoImage *image); +MONO_API const char *mono_image_strerror (MonoImageOpenStatus status); + +MONO_API int mono_image_ensure_section (MonoImage *image, + const char *section); +MONO_API int mono_image_ensure_section_idx (MonoImage *image, + int section); + +MONO_API uint32_t mono_image_get_entry_point (MonoImage *image); +MONO_API const char *mono_image_get_resource (MonoImage *image, uint32_t offset, uint32_t *size); +MONO_RT_EXTERNAL_ONLY MONO_API MonoImage* mono_image_load_file_for_image (MonoImage *image, int fileidx); + +MONO_RT_EXTERNAL_ONLY MONO_API MonoImage* mono_image_load_module (MonoImage *image, int idx); + +MONO_API const char* mono_image_get_name (MonoImage *image); +MONO_API const char* mono_image_get_filename (MonoImage *image); +MONO_API const char * mono_image_get_guid (MonoImage *image); +MONO_API MonoAssembly* mono_image_get_assembly (MonoImage *image); +MONO_API mono_bool mono_image_is_dynamic (MonoImage *image); +MONO_API char* mono_image_rva_map (MonoImage *image, uint32_t rva); + +MONO_API const MonoTableInfo *mono_image_get_table_info (MonoImage *image, int table_id); +MONO_API int mono_image_get_table_rows (MonoImage *image, int table_id); +MONO_API int mono_table_info_get_rows (const MonoTableInfo *table); + +/* This actually returns a MonoPEResourceDataEntry *, but declaring it + * causes an include file loop. + */ +MONO_API void* mono_image_lookup_resource (MonoImage *image, uint32_t res_id, + uint32_t lang_id, mono_unichar2 *name); + +MONO_API const char* mono_image_get_public_key (MonoImage *image, uint32_t *size); +MONO_API const char* mono_image_get_strong_name (MonoImage *image, uint32_t *size); +MONO_API uint32_t mono_image_strong_name_position (MonoImage *image, uint32_t *size); +MONO_API void mono_image_add_to_name_cache (MonoImage *image, + const char *nspace, const char *name, uint32_t idx); +MONO_API mono_bool mono_image_has_authenticode_entry (MonoImage *image); + +MONO_END_DECLS + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/jit-info.c b/unity-2019.4.24f1-mbe/mono/metadata/jit-info.c new file mode 100644 index 000000000..3b24eafb8 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/jit-info.c @@ -0,0 +1,1036 @@ +/** + * \file + * MonoJitInfo functionality + * + * Author: + * Dietmar Maurer (dietmar@ximian.com) + * Patrik Torstensson + * + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Copyright 2011-2012 Xamarin, Inc (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static MonoJitInfoFindInAot jit_info_find_in_aot_func = NULL; + +#define JIT_INFO_TABLE_FILL_RATIO_NOM 3 +#define JIT_INFO_TABLE_FILL_RATIO_DENOM 4 +#define JIT_INFO_TABLE_FILLED_NUM_ELEMENTS (MONO_JIT_INFO_TABLE_CHUNK_SIZE * JIT_INFO_TABLE_FILL_RATIO_NOM / JIT_INFO_TABLE_FILL_RATIO_DENOM) + +#define JIT_INFO_TABLE_LOW_WATERMARK(n) ((n) / 2) +#define JIT_INFO_TABLE_HIGH_WATERMARK(n) ((n) * 5 / 6) + +#define JIT_INFO_TOMBSTONE_MARKER ((MonoMethod*)NULL) +#define IS_JIT_INFO_TOMBSTONE(ji) ((ji)->d.method == JIT_INFO_TOMBSTONE_MARKER) + +#define JIT_INFO_TABLE_HAZARD_INDEX 0 +#define JIT_INFO_HAZARD_INDEX 1 + +static int +jit_info_table_num_elements (MonoJitInfoTable *table) +{ + return table->num_valid; +} + +static MonoJitInfoTableChunk* +jit_info_table_new_chunk (void) +{ + MonoJitInfoTableChunk *chunk = g_new0 (MonoJitInfoTableChunk, 1); + chunk->refcount = 1; + + return chunk; +} + +MonoJitInfoTable * +mono_jit_info_table_new (MonoDomain *domain) +{ + MonoJitInfoTable *table = (MonoJitInfoTable *)g_malloc0 (MONO_SIZEOF_JIT_INFO_TABLE + sizeof (MonoJitInfoTableChunk*)); + + table->domain = domain; + table->num_chunks = 1; + table->chunks [0] = jit_info_table_new_chunk (); + table->num_valid = 0; + + return table; +} + +void +mono_jit_info_table_free (MonoJitInfoTable *table) +{ + int i; + int num_chunks = table->num_chunks; + MonoDomain *domain = table->domain; + + mono_domain_lock (domain); + + table->domain->num_jit_info_tables--; + if (table->domain->num_jit_info_tables <= 1) { + GSList *list; + + for (list = table->domain->jit_info_free_queue; list; list = list->next) + g_free (list->data); + + g_slist_free (table->domain->jit_info_free_queue); + table->domain->jit_info_free_queue = NULL; + } + + /* At this point we assume that there are no other threads + still accessing the table, so we don't have to worry about + hazardous pointers. */ + + for (i = 0; i < num_chunks; ++i) { + MonoJitInfoTableChunk *chunk = table->chunks [i]; + MonoJitInfo *tombstone; + + if (--chunk->refcount > 0) + continue; + + for (tombstone = chunk->next_tombstone; tombstone; ) { + MonoJitInfo *next = tombstone->n.next_tombstone; + g_free (tombstone); + tombstone = next; + } + + g_free (chunk); + } + + mono_domain_unlock (domain); + + g_free (table); +} + +/* The jit_info_table is sorted in ascending order by the end + * addresses of the compiled methods. The reason why we have to do + * this is that once we introduce tombstones, it becomes possible for + * code ranges to overlap, and if we sort by code start and insert at + * the back of the table, we cannot guarantee that we won't overlook + * an entry. + * + * There are actually two possible ways to do the sorting and + * inserting which work with our lock-free mechanism: + * + * 1. Sort by start address and insert at the front. When looking for + * an entry, find the last one with a start address lower than the one + * you're looking for, then work your way to the front of the table. + * + * 2. Sort by end address and insert at the back. When looking for an + * entry, find the first one with an end address higher than the one + * you're looking for, then work your way to the end of the table. + * + * We chose the latter out of convenience. + */ +static int +jit_info_table_index (MonoJitInfoTable *table, gint8 *addr) +{ + int left = 0, right = table->num_chunks; + + g_assert (left < right); + + do { + int pos = (left + right) / 2; + MonoJitInfoTableChunk *chunk = table->chunks [pos]; + + if (addr < chunk->last_code_end) + right = pos; + else + left = pos + 1; + } while (left < right); + g_assert (left == right); + + if (left >= table->num_chunks) + return table->num_chunks - 1; + return left; +} + +static int +jit_info_table_chunk_index (MonoJitInfoTableChunk *chunk, MonoThreadHazardPointers *hp, gint8 *addr) +{ + int left = 0, right = chunk->num_elements; + + while (left < right) { + int pos = (left + right) / 2; + MonoJitInfo *ji = (MonoJitInfo *)mono_get_hazardous_pointer((gpointer volatile*)&chunk->data [pos], hp, JIT_INFO_HAZARD_INDEX); + gint8 *code_end = (gint8*)ji->code_start + ji->code_size; + + if (addr < code_end) + right = pos; + else + left = pos + 1; + } + g_assert (left == right); + + 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) +{ + MonoJitInfo *ji; + int chunk_pos, pos; + + chunk_pos = jit_info_table_index (table, (gint8*)addr); + g_assert (chunk_pos < table->num_chunks); + + pos = jit_info_table_chunk_index (table->chunks [chunk_pos], hp, (gint8*)addr); + + /* 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 { + MonoJitInfoTableChunk *chunk = table->chunks [chunk_pos]; + + while (pos < chunk->num_elements) { + ji = (MonoJitInfo *)mono_get_hazardous_pointer ((gpointer volatile*)&chunk->data [pos], hp, JIT_INFO_HAZARD_INDEX); + + ++pos; + + if (IS_JIT_INFO_TOMBSTONE (ji)) { + mono_hazard_pointer_clear (hp, JIT_INFO_HAZARD_INDEX); + continue; + } + if ((gint8*)addr >= (gint8*)ji->code_start + && (gint8*)addr < (gint8*)ji->code_start + ji->code_size) { + mono_hazard_pointer_clear (hp, JIT_INFO_HAZARD_INDEX); + return ji; + } + + /* 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) + goto not_found; + } + + ++chunk_pos; + pos = 0; + } while (chunk_pos < table->num_chunks); + + not_found: + if (hp) + mono_hazard_pointer_clear (hp, JIT_INFO_HAZARD_INDEX); + return NULL; +} + +/* + * mono_jit_info_table_find_internal: + * + * If TRY_AOT is FALSE, avoid loading information for missing methods from AOT images, which is currently not async safe. + * In this case, only those AOT methods will be found whose jit info is already loaded. + * If ALLOW_TRAMPOLINES is TRUE, this can return a MonoJitInfo which represents a trampoline (ji->is_trampoline is true). + * ASYNC SAFETY: When called in an async context (mono_thread_info_is_async_context ()), this is async safe. + * In this case, the returned MonoJitInfo might not have metadata information, in particular, + * mono_jit_info_get_method () could fail. + */ +MonoJitInfo* +mono_jit_info_table_find_internal (MonoDomain *domain, char *addr, gboolean try_aot, gboolean allow_trampolines) +{ + MonoJitInfoTable *table; + MonoJitInfo *ji, *module_ji; + MonoThreadHazardPointers *hp = mono_hazard_pointer_get (); + + UnlockedIncrement (&mono_stats.jit_info_table_lookup_count); + + /* First we have to get the domain's jit_info_table. This is + complicated by the fact that a writer might substitute a + new table and free the old one. What the writer guarantees + us is that it looks at the hazard pointers after it has + changed the jit_info_table pointer. So, if we guard the + table by a hazard pointer and make sure that the pointer is + still there after we've made it hazardous, we don't have to + worry about the writer freeing the table. */ + table = (MonoJitInfoTable *)mono_get_hazardous_pointer ((gpointer volatile*)&domain->jit_info_table, hp, JIT_INFO_TABLE_HAZARD_INDEX); + + ji = jit_info_table_find (table, hp, (gint8*)addr); + if (hp) + mono_hazard_pointer_clear (hp, JIT_INFO_TABLE_HAZARD_INDEX); + if (ji && ji->is_trampoline && !allow_trampolines) + return NULL; + if (ji) + return ji; + + /* Maybe its an AOT module */ + if (try_aot && mono_get_root_domain () && mono_get_root_domain ()->aot_modules) { + table = (MonoJitInfoTable *)mono_get_hazardous_pointer ((gpointer volatile*)&mono_get_root_domain ()->aot_modules, hp, JIT_INFO_TABLE_HAZARD_INDEX); + module_ji = jit_info_table_find (table, hp, (gint8*)addr); + if (module_ji) + ji = jit_info_find_in_aot_func (domain, module_ji->d.image, addr); + if (hp) + mono_hazard_pointer_clear (hp, JIT_INFO_TABLE_HAZARD_INDEX); + } + + if (ji && ji->is_trampoline && !allow_trampolines) + return NULL; + + return ji; +} + +/** + * mono_jit_info_table_find: + * \param domain Domain that you want to look up + * \param addr Points to an address with JITed code. + * + * Use this function to obtain a \c MonoJitInfo* object that can be used to get + * some statistics. You should provide both the \p domain on which you will be + * performing the probe, and an address. Since application domains can share code + * the same address can be in use by multiple domains at once. + * + * This does not return any results for trampolines. + * + * \returns NULL if the address does not belong to JITed code (it might be native + * code or a trampoline) or a valid pointer to a \c MonoJitInfo* . + */ +MonoJitInfo* +mono_jit_info_table_find (MonoDomain *domain, char *addr) +{ + return mono_jit_info_table_find_internal (domain, addr, TRUE, FALSE); +} + +static G_GNUC_UNUSED void +jit_info_table_check (MonoJitInfoTable *table) +{ + int i; + + for (i = 0; i < table->num_chunks; ++i) { + MonoJitInfoTableChunk *chunk = table->chunks [i]; + int j; + + g_assert (chunk->refcount > 0 /* && chunk->refcount <= 8 */); + if (chunk->refcount > 10) + printf("warning: chunk refcount is %d\n", chunk->refcount); + g_assert (chunk->num_elements <= MONO_JIT_INFO_TABLE_CHUNK_SIZE); + + for (j = 0; j < chunk->num_elements; ++j) { + MonoJitInfo *this_ji = chunk->data [j]; + MonoJitInfo *next; + + g_assert ((gint8*)this_ji->code_start + this_ji->code_size <= chunk->last_code_end); + + if (j < chunk->num_elements - 1) + next = chunk->data [j + 1]; + else if (i < table->num_chunks - 1) { + int k; + + for (k = i + 1; k < table->num_chunks; ++k) + if (table->chunks [k]->num_elements > 0) + break; + + if (k >= table->num_chunks) + return; + + g_assert (table->chunks [k]->num_elements > 0); + next = table->chunks [k]->data [0]; + } else + return; + + g_assert ((gint8*)this_ji->code_start + this_ji->code_size <= (gint8*)next->code_start + next->code_size); + } + } +} + +static MonoJitInfoTable* +jit_info_table_realloc (MonoJitInfoTable *old) +{ + int i; + int num_elements = jit_info_table_num_elements (old); + int required_size; + int num_chunks; + int new_chunk, new_element; + MonoJitInfoTable *result; + + /* number of needed places for elements needed */ + required_size = (int)((long)num_elements * JIT_INFO_TABLE_FILL_RATIO_DENOM / JIT_INFO_TABLE_FILL_RATIO_NOM); + num_chunks = (required_size + MONO_JIT_INFO_TABLE_CHUNK_SIZE - 1) / MONO_JIT_INFO_TABLE_CHUNK_SIZE; + if (num_chunks == 0) { + g_assert (num_elements == 0); + return mono_jit_info_table_new (old->domain); + } + g_assert (num_chunks > 0); + + result = (MonoJitInfoTable *)g_malloc (MONO_SIZEOF_JIT_INFO_TABLE + sizeof (MonoJitInfoTableChunk*) * num_chunks); + result->domain = old->domain; + result->num_chunks = num_chunks; + result->num_valid = old->num_valid; + + for (i = 0; i < num_chunks; ++i) + result->chunks [i] = jit_info_table_new_chunk (); + + new_chunk = 0; + new_element = 0; + for (i = 0; i < old->num_chunks; ++i) { + MonoJitInfoTableChunk *chunk = old->chunks [i]; + int chunk_num_elements = chunk->num_elements; + int j; + + for (j = 0; j < chunk_num_elements; ++j) { + if (!IS_JIT_INFO_TOMBSTONE (chunk->data [j])) { + g_assert (new_chunk < num_chunks); + result->chunks [new_chunk]->data [new_element] = chunk->data [j]; + if (++new_element >= JIT_INFO_TABLE_FILLED_NUM_ELEMENTS) { + result->chunks [new_chunk]->num_elements = new_element; + ++new_chunk; + new_element = 0; + } + } + } + } + + if (new_chunk < num_chunks) { + g_assert (new_chunk == num_chunks - 1); + result->chunks [new_chunk]->num_elements = new_element; + g_assert (result->chunks [new_chunk]->num_elements > 0); + } + + for (i = 0; i < num_chunks; ++i) { + MonoJitInfoTableChunk *chunk = result->chunks [i]; + MonoJitInfo *ji = chunk->data [chunk->num_elements - 1]; + + result->chunks [i]->last_code_end = (gint8*)ji->code_start + ji->code_size; + } + + return result; +} + +static void +jit_info_table_split_chunk (MonoJitInfoTableChunk *chunk, MonoJitInfoTableChunk **new1p, MonoJitInfoTableChunk **new2p) +{ + MonoJitInfoTableChunk *new1 = jit_info_table_new_chunk (); + MonoJitInfoTableChunk *new2 = jit_info_table_new_chunk (); + + g_assert (chunk->num_elements == MONO_JIT_INFO_TABLE_CHUNK_SIZE); + + new1->num_elements = MONO_JIT_INFO_TABLE_CHUNK_SIZE / 2; + new2->num_elements = MONO_JIT_INFO_TABLE_CHUNK_SIZE - new1->num_elements; + + memcpy ((void*)new1->data, (void*)chunk->data, sizeof (MonoJitInfo*) * new1->num_elements); + memcpy ((void*)new2->data, (void*)(chunk->data + new1->num_elements), sizeof (MonoJitInfo*) * new2->num_elements); + + new1->last_code_end = (gint8*)new1->data [new1->num_elements - 1]->code_start + + new1->data [new1->num_elements - 1]->code_size; + new2->last_code_end = (gint8*)new2->data [new2->num_elements - 1]->code_start + + new2->data [new2->num_elements - 1]->code_size; + + *new1p = new1; + *new2p = new2; +} + +static MonoJitInfoTable* +jit_info_table_copy_and_split_chunk (MonoJitInfoTable *table, MonoJitInfoTableChunk *chunk) +{ + MonoJitInfoTable *new_table = (MonoJitInfoTable *)g_malloc (MONO_SIZEOF_JIT_INFO_TABLE + + sizeof (MonoJitInfoTableChunk*) * (table->num_chunks + 1)); + int i, j; + + new_table->domain = table->domain; + new_table->num_chunks = table->num_chunks + 1; + new_table->num_valid = table->num_valid; + + j = 0; + for (i = 0; i < table->num_chunks; ++i) { + if (table->chunks [i] == chunk) { + jit_info_table_split_chunk (chunk, &new_table->chunks [j], &new_table->chunks [j + 1]); + j += 2; + } else { + new_table->chunks [j] = table->chunks [i]; + ++new_table->chunks [j]->refcount; + ++j; + } + } + + g_assert (j == new_table->num_chunks); + + return new_table; +} + +static MonoJitInfoTableChunk* +jit_info_table_purify_chunk (MonoJitInfoTableChunk *old) +{ + MonoJitInfoTableChunk *result = jit_info_table_new_chunk (); + int i, j; + + j = 0; + for (i = 0; i < old->num_elements; ++i) { + if (!IS_JIT_INFO_TOMBSTONE (old->data [i])) + result->data [j++] = old->data [i]; + } + + result->num_elements = j; + if (result->num_elements > 0) + result->last_code_end = (gint8*)result->data [j - 1]->code_start + result->data [j - 1]->code_size; + else + result->last_code_end = old->last_code_end; + + return result; +} + +static MonoJitInfoTable* +jit_info_table_copy_and_purify_chunk (MonoJitInfoTable *table, MonoJitInfoTableChunk *chunk) +{ + MonoJitInfoTable *new_table = (MonoJitInfoTable *)g_malloc (MONO_SIZEOF_JIT_INFO_TABLE + + sizeof (MonoJitInfoTableChunk*) * table->num_chunks); + int i, j; + + new_table->domain = table->domain; + new_table->num_chunks = table->num_chunks; + new_table->num_valid = table->num_valid; + + j = 0; + for (i = 0; i < table->num_chunks; ++i) { + if (table->chunks [i] == chunk) + new_table->chunks [j++] = jit_info_table_purify_chunk (table->chunks [i]); + else { + new_table->chunks [j] = table->chunks [i]; + ++new_table->chunks [j]->refcount; + ++j; + } + } + + g_assert (j == new_table->num_chunks); + + return new_table; +} + +/* As we add an element to the table the case can arise that the chunk + * to which we need to add is already full. In that case we have to + * allocate a new table and do something about that chunk. We have + * several strategies: + * + * If the number of elements in the table is below the low watermark + * or above the high watermark, we reallocate the whole table. + * Otherwise we only concern ourselves with the overflowing chunk: + * + * If there are no tombstones in the chunk then we split the chunk in + * two, each half full. + * + * If the chunk does contain tombstones, we just make a new copy of + * the chunk without the tombstones, which will have room for at least + * the one element we have to add. + */ +static MonoJitInfoTable* +jit_info_table_chunk_overflow (MonoJitInfoTable *table, MonoJitInfoTableChunk *chunk) +{ + int num_elements = jit_info_table_num_elements (table); + int i; + + if (num_elements < JIT_INFO_TABLE_LOW_WATERMARK (table->num_chunks * MONO_JIT_INFO_TABLE_CHUNK_SIZE) + || num_elements > JIT_INFO_TABLE_HIGH_WATERMARK (table->num_chunks * MONO_JIT_INFO_TABLE_CHUNK_SIZE)) { + //printf ("reallocing table\n"); + return jit_info_table_realloc (table); + } + + /* count the number of non-tombstone elements in the chunk */ + num_elements = 0; + for (i = 0; i < chunk->num_elements; ++i) { + if (!IS_JIT_INFO_TOMBSTONE (chunk->data [i])) + ++num_elements; + } + + if (num_elements == MONO_JIT_INFO_TABLE_CHUNK_SIZE) { + //printf ("splitting chunk\n"); + return jit_info_table_copy_and_split_chunk (table, chunk); + } + + //printf ("purifying chunk\n"); + return jit_info_table_copy_and_purify_chunk (table, chunk); +} + +/* We add elements to the table by first making space for them by + * shifting the elements at the back to the right, one at a time. + * This results in duplicate entries during the process, but during + * all the time the table is in a sorted state. Also, when an element + * is replaced by another one, the element that replaces it has an end + * address that is equal to or lower than that of the replaced + * element. That property is necessary to guarantee that when + * searching for an element we end up at a position not higher than + * the one we're looking for (i.e. we either find the element directly + * or we end up to the left of it). + */ +static void +jit_info_table_add (MonoDomain *domain, MonoJitInfoTable *volatile *table_ptr, MonoJitInfo *ji) +{ + MonoJitInfoTable *table; + MonoJitInfoTableChunk *chunk; + int chunk_pos, pos; + int num_elements; + int i; + + table = *table_ptr; + + restart: + chunk_pos = jit_info_table_index (table, (gint8*)ji->code_start + ji->code_size); + g_assert (chunk_pos < table->num_chunks); + chunk = table->chunks [chunk_pos]; + + if (chunk->num_elements >= MONO_JIT_INFO_TABLE_CHUNK_SIZE) { + MonoJitInfoTable *new_table = jit_info_table_chunk_overflow (table, chunk); + + /* Debugging code, should be removed. */ + //jit_info_table_check (new_table); + + *table_ptr = new_table; + mono_memory_barrier (); + domain->num_jit_info_tables++; + mono_thread_hazardous_try_free (table, (MonoHazardousFreeFunc)mono_jit_info_table_free); + table = new_table; + + goto restart; + } + + /* Debugging code, should be removed. */ + //jit_info_table_check (table); + + num_elements = chunk->num_elements; + + pos = jit_info_table_chunk_index (chunk, NULL, (gint8*)ji->code_start + ji->code_size); + + /* First we need to size up the chunk by one, by copying the + last item, or inserting the first one, if the table is + empty. */ + if (num_elements > 0) + chunk->data [num_elements] = chunk->data [num_elements - 1]; + else + chunk->data [0] = ji; + mono_memory_write_barrier (); + chunk->num_elements = ++num_elements; + + /* Shift the elements up one by one. */ + for (i = num_elements - 2; i >= pos; --i) { + mono_memory_write_barrier (); + chunk->data [i + 1] = chunk->data [i]; + } + + /* Now we have room and can insert the new item. */ + mono_memory_write_barrier (); + chunk->data [pos] = ji; + + /* Set the high code end address chunk entry. */ + chunk->last_code_end = (gint8*)chunk->data [chunk->num_elements - 1]->code_start + + chunk->data [chunk->num_elements - 1]->code_size; + + ++table->num_valid; + + /* Debugging code, should be removed. */ + //jit_info_table_check (table); +} + +void +mono_jit_info_table_add (MonoDomain *domain, MonoJitInfo *ji) +{ + g_assert (ji->d.method != NULL); + + mono_domain_lock (domain); + + UnlockedIncrement (&mono_stats.jit_info_table_insert_count); + + jit_info_table_add (domain, &domain->jit_info_table, ji); + + mono_domain_unlock (domain); +} + +static MonoJitInfo* +mono_jit_info_make_tombstone (MonoJitInfoTableChunk *chunk, MonoJitInfo *ji) +{ + MonoJitInfo *tombstone = g_new0 (MonoJitInfo, 1); + + tombstone->code_start = ji->code_start; + tombstone->code_size = ji->code_size; + tombstone->d.method = JIT_INFO_TOMBSTONE_MARKER; + tombstone->n.next_tombstone = chunk->next_tombstone; + chunk->next_tombstone = tombstone; + + return tombstone; +} + +/* + * LOCKING: domain lock + */ +static void +mono_jit_info_free_or_queue (MonoDomain *domain, MonoJitInfo *ji) +{ + if (domain->num_jit_info_tables <= 1) { + /* Can it actually happen that we only have one table + but ji is still hazardous? */ + mono_thread_hazardous_try_free (ji, g_free); + } else { + domain->jit_info_free_queue = g_slist_prepend (domain->jit_info_free_queue, ji); + } +} + +static void +jit_info_table_remove (MonoJitInfoTable *table, MonoJitInfo *ji) +{ + MonoJitInfoTableChunk *chunk; + gpointer start = ji->code_start; + int chunk_pos, pos; + + chunk_pos = jit_info_table_index (table, (gint8 *)start); + g_assert (chunk_pos < table->num_chunks); + + pos = jit_info_table_chunk_index (table->chunks [chunk_pos], NULL, (gint8 *)start); + + do { + chunk = table->chunks [chunk_pos]; + + while (pos < chunk->num_elements) { + if (chunk->data [pos] == ji) + goto found; + + g_assert (IS_JIT_INFO_TOMBSTONE (chunk->data [pos])); + g_assert ((guint8*)chunk->data [pos]->code_start + chunk->data [pos]->code_size + <= (guint8*)ji->code_start + ji->code_size); + + ++pos; + } + + ++chunk_pos; + pos = 0; + } while (chunk_pos < table->num_chunks); + + found: + g_assert (chunk->data [pos] == ji); + + chunk->data [pos] = mono_jit_info_make_tombstone (chunk, ji); + --table->num_valid; + + /* Debugging code, should be removed. */ + //jit_info_table_check (table); +} + +void +mono_jit_info_table_remove (MonoDomain *domain, MonoJitInfo *ji) +{ + MonoJitInfoTable *table; + + mono_domain_lock (domain); + table = domain->jit_info_table; + + UnlockedIncrement (&mono_stats.jit_info_table_remove_count); + + jit_info_table_remove (table, ji); + + mono_jit_info_free_or_queue (domain, ji); + + mono_domain_unlock (domain); +} + +void +mono_jit_info_table_foreach (MonoDomain *domain, MonoJitInfoFunc func, void *user_data) +{ + mono_domain_lock (domain); + + MonoJitInfoTable *table = domain->jit_info_table; + for (int chunk_index = 0; chunk_index < table->num_chunks; ++chunk_index) { + for (int info_index = 0; info_index < table->chunks[chunk_index]->num_elements; ++info_index) { + MonoJitInfo *ji = table->chunks[chunk_index]->data[info_index]; + if (IS_JIT_INFO_TOMBSTONE (ji) || ji->is_trampoline) + continue; + + func (domain, ji->d.method, ji, user_data); + } + } + + mono_domain_unlock (domain); +} + +void +mono_jit_info_add_aot_module (MonoImage *image, gpointer start, gpointer end) +{ + MonoJitInfo *ji; + MonoDomain *domain = mono_get_root_domain (); + + g_assert (domain); + mono_domain_lock (domain); + + /* + * We reuse MonoJitInfoTable to store AOT module info, + * this gives us async-safe lookup. + */ + if (!domain->aot_modules) { + domain->num_jit_info_tables ++; + domain->aot_modules = mono_jit_info_table_new (domain); + } + + ji = g_new0 (MonoJitInfo, 1); + ji->d.image = image; + ji->code_start = start; + ji->code_size = (guint8*)end - (guint8*)start; + jit_info_table_add (domain, &domain->aot_modules, ji); + + mono_domain_unlock (domain); +} + +void +mono_install_jit_info_find_in_aot (MonoJitInfoFindInAot func) +{ + jit_info_find_in_aot_func = func; +} + +int +mono_jit_info_size (MonoJitInfoFlags flags, int num_clauses, int num_holes) +{ + int size = MONO_SIZEOF_JIT_INFO; + + size += num_clauses * sizeof (MonoJitExceptionInfo); + if (flags & JIT_INFO_HAS_GENERIC_JIT_INFO) + size += sizeof (MonoGenericJitInfo); + if (flags & JIT_INFO_HAS_TRY_BLOCK_HOLES) + size += sizeof (MonoTryBlockHoleTableJitInfo) + num_holes * sizeof (MonoTryBlockHoleJitInfo); + if (flags & JIT_INFO_HAS_ARCH_EH_INFO) + size += sizeof (MonoArchEHJitInfo); + if (flags & JIT_INFO_HAS_THUNK_INFO) + size += sizeof (MonoThunkJitInfo); + if (flags & JIT_INFO_HAS_UNWIND_INFO) + size += sizeof (MonoUnwindJitInfo); + return size; +} + +void +mono_jit_info_init (MonoJitInfo *ji, MonoMethod *method, guint8 *code, int code_size, + MonoJitInfoFlags flags, int num_clauses, int num_holes) +{ + ji->d.method = method; + ji->code_start = code; + ji->code_size = code_size; + ji->num_clauses = num_clauses; + if (flags & JIT_INFO_HAS_GENERIC_JIT_INFO) + ji->has_generic_jit_info = 1; + if (flags & JIT_INFO_HAS_TRY_BLOCK_HOLES) + ji->has_try_block_holes = 1; + if (flags & JIT_INFO_HAS_ARCH_EH_INFO) + ji->has_arch_eh_info = 1; + if (flags & JIT_INFO_HAS_THUNK_INFO) + ji->has_thunk_info = 1; + if (flags & JIT_INFO_HAS_UNWIND_INFO) + ji->has_unwind_info = 1; +} + +/** + * mono_jit_info_get_code_start: + * \param ji the JIT information handle + * + * Use this function to get the starting address for the method described by + * the \p ji object. You can use this plus the \c mono_jit_info_get_code_size + * to determine the start and end of the native code. + * + * \returns Starting address with the native code. + */ +gpointer +mono_jit_info_get_code_start (MonoJitInfo* ji) +{ + return ji->code_start; +} + +/** + * mono_jit_info_get_code_size: + * \param ji the JIT information handle + * + * Use this function to get the code size for the method described by + * the \p ji object. You can use this plus the \c mono_jit_info_get_code_start + * to determine the start and end of the native code. + * + * \returns Starting address with the native code. + */ +int +mono_jit_info_get_code_size (MonoJitInfo* ji) +{ + return ji->code_size; +} + +/** + * mono_jit_info_get_method: + * \param ji the JIT information handle + * + * Use this function to get the \c MonoMethod* that backs + * the \p ji object. + * + * \returns The \c MonoMethod that represents the code tracked + * by \p ji. + */ +MonoMethod* +mono_jit_info_get_method (MonoJitInfo* ji) +{ + g_assert (!ji->async); + g_assert (!ji->is_trampoline); + return ji->d.method; +} + +static gpointer +jit_info_key_extract (gpointer value) +{ + MonoJitInfo *info = (MonoJitInfo*)value; + + return info->d.method; +} + +static gpointer* +jit_info_next_value (gpointer value) +{ + MonoJitInfo *info = (MonoJitInfo*)value; + + return (gpointer*)&info->n.next_jit_code_hash; +} + +void +mono_jit_code_hash_init (MonoInternalHashTable *jit_code_hash) +{ + mono_internal_hash_table_init (jit_code_hash, + mono_aligned_addr_hash, + jit_info_key_extract, + jit_info_next_value); +} + +MonoGenericJitInfo* +mono_jit_info_get_generic_jit_info (MonoJitInfo *ji) +{ + if (ji->has_generic_jit_info) + return (MonoGenericJitInfo*)&ji->clauses [ji->num_clauses]; + else + return NULL; +} + +/* + * mono_jit_info_get_generic_sharing_context: + * @ji: a jit info + * + * Returns the jit info's generic sharing context, or NULL if it + * doesn't have one. + */ +MonoGenericSharingContext* +mono_jit_info_get_generic_sharing_context (MonoJitInfo *ji) +{ + MonoGenericJitInfo *gi = mono_jit_info_get_generic_jit_info (ji); + + if (gi) + return gi->generic_sharing_context; + else + return NULL; +} + +/* + * mono_jit_info_set_generic_sharing_context: + * @ji: a jit info + * @gsctx: a generic sharing context + * + * Sets the jit info's generic sharing context. The jit info must + * have memory allocated for the context. + */ +void +mono_jit_info_set_generic_sharing_context (MonoJitInfo *ji, MonoGenericSharingContext *gsctx) +{ + MonoGenericJitInfo *gi = mono_jit_info_get_generic_jit_info (ji); + + g_assert (gi); + + gi->generic_sharing_context = gsctx; +} + +MonoTryBlockHoleTableJitInfo* +mono_jit_info_get_try_block_hole_table_info (MonoJitInfo *ji) +{ + if (ji->has_try_block_holes) { + char *ptr = (char*)&ji->clauses [ji->num_clauses]; + if (ji->has_generic_jit_info) + ptr += sizeof (MonoGenericJitInfo); + return (MonoTryBlockHoleTableJitInfo*)ptr; + } else { + return NULL; + } +} + +static int +try_block_hole_table_size (MonoJitInfo *ji) +{ + MonoTryBlockHoleTableJitInfo *table; + + table = mono_jit_info_get_try_block_hole_table_info (ji); + g_assert (table); + return sizeof (MonoTryBlockHoleTableJitInfo) + table->num_holes * sizeof (MonoTryBlockHoleJitInfo); +} + +MonoArchEHJitInfo* +mono_jit_info_get_arch_eh_info (MonoJitInfo *ji) +{ + if (ji->has_arch_eh_info) { + char *ptr = (char*)&ji->clauses [ji->num_clauses]; + if (ji->has_generic_jit_info) + ptr += sizeof (MonoGenericJitInfo); + if (ji->has_try_block_holes) + ptr += try_block_hole_table_size (ji); + return (MonoArchEHJitInfo*)ptr; + } else { + return NULL; + } +} + +MonoThunkJitInfo* +mono_jit_info_get_thunk_info (MonoJitInfo *ji) +{ + if (ji->has_thunk_info) { + char *ptr = (char*)&ji->clauses [ji->num_clauses]; + if (ji->has_generic_jit_info) + ptr += sizeof (MonoGenericJitInfo); + if (ji->has_try_block_holes) + ptr += try_block_hole_table_size (ji); + if (ji->has_arch_eh_info) + ptr += sizeof (MonoArchEHJitInfo); + return (MonoThunkJitInfo*)ptr; + } else { + return NULL; + } +} + +MonoUnwindJitInfo* +mono_jit_info_get_unwind_info (MonoJitInfo *ji) +{ + if (ji->has_unwind_info) { + char *ptr = (char*)&ji->clauses [ji->num_clauses]; + if (ji->has_generic_jit_info) + ptr += sizeof (MonoGenericJitInfo); + if (ji->has_try_block_holes) + ptr += try_block_hole_table_size (ji); + if (ji->has_arch_eh_info) + ptr += sizeof (MonoArchEHJitInfo); + if (ji->has_thunk_info) + ptr += sizeof (MonoThunkJitInfo); + return (MonoUnwindJitInfo*)ptr; + } else { + return NULL; + } +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/loader.c b/unity-2019.4.24f1-mbe/mono/metadata/loader.c new file mode 100644 index 000000000..8c7a8b7b9 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/loader.c @@ -0,0 +1,2773 @@ +/** + * \file + * Image Loader + * + * Authors: + * Paolo Molaro (lupus@ximian.com) + * Miguel de Icaza (miguel@ximian.com) + * Patrik Torstensson (patrik.torstensson@labs2.com) + * + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Copyright 2011 Xamarin, Inc (http://www.xamarin.com) + * + * This file is used by the interpreter and the JIT engine to locate + * assemblies. Used to load AssemblyRef and later to resolve various + * kinds of `Refs'. + * + * TODO: + * This should keep track of the assembly versions that we are loading. + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MonoDefaults mono_defaults; + +/* + * This lock protects the hash tables inside MonoImage used by the metadata + * loading functions in class.c and loader.c. + * + * See domain-internals.h for locking policy in combination with the + * domain lock. + */ +static MonoCoopMutex loader_mutex; +static mono_mutex_t global_loader_data_mutex; +static gboolean loader_lock_inited; + +/* Statistics */ +static gint32 inflated_signatures_size; +static gint32 memberref_sig_cache_size; +static gint32 methods_size; +static gint32 signatures_size; + +/* + * This TLS variable holds how many times the current thread has acquired the loader + * lock. + */ +MonoNativeTlsKey loader_lock_nest_id; + +static void dllmap_cleanup (void); +static void cached_module_cleanup(void); + +/* Class lazy loading functions */ +GENERATE_GET_CLASS_WITH_CACHE (appdomain_unloaded_exception, "System", "AppDomainUnloadedException") + +static void +global_loader_data_lock (void) +{ + mono_locks_os_acquire (&global_loader_data_mutex, LoaderGlobalDataLock); +} + +static void +global_loader_data_unlock (void) +{ + mono_locks_os_release (&global_loader_data_mutex, LoaderGlobalDataLock); +} + +void +mono_loader_init () +{ + static gboolean inited; + + if (!inited) { + mono_coop_mutex_init_recursive (&loader_mutex); + mono_os_mutex_init_recursive (&global_loader_data_mutex); + loader_lock_inited = TRUE; + + mono_native_tls_alloc (&loader_lock_nest_id, NULL); + + mono_counters_init (); + mono_counters_register ("Inflated signatures size", + MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &inflated_signatures_size); + mono_counters_register ("Memberref signature cache size", + MONO_COUNTER_METADATA | MONO_COUNTER_INT, &memberref_sig_cache_size); + mono_counters_register ("MonoMethod size", + MONO_COUNTER_METADATA | MONO_COUNTER_INT, &methods_size); + mono_counters_register ("MonoMethodSignature size", + MONO_COUNTER_METADATA | MONO_COUNTER_INT, &signatures_size); + + inited = TRUE; + } +} + +void +mono_loader_cleanup (void) +{ + dllmap_cleanup (); + cached_module_cleanup (); + + mono_native_tls_free (loader_lock_nest_id); + + mono_coop_mutex_destroy (&loader_mutex); + mono_os_mutex_destroy (&global_loader_data_mutex); + loader_lock_inited = FALSE; +} + +/* + * find_cached_memberref_sig: + * + * Return a cached copy of the memberref signature identified by SIG_IDX. + * We use a gpointer since the cache stores both MonoTypes and MonoMethodSignatures. + * A cache is needed since the type/signature parsing routines allocate everything + * from a mempool, so without a cache, multiple requests for the same signature would + * lead to unbounded memory growth. For normal methods/fields this is not a problem + * since the resulting methods/fields are cached, but inflated methods/fields cannot + * be cached. + * LOCKING: Acquires the loader lock. + */ +static gpointer +find_cached_memberref_sig (MonoImage *image, guint32 sig_idx) +{ + gpointer res; + + mono_image_lock (image); + res = g_hash_table_lookup (image->memberref_signatures, GUINT_TO_POINTER (sig_idx)); + mono_image_unlock (image); + + return res; +} + +static gpointer +cache_memberref_sig (MonoImage *image, guint32 sig_idx, gpointer sig) +{ + gpointer prev_sig; + + mono_image_lock (image); + prev_sig = g_hash_table_lookup (image->memberref_signatures, GUINT_TO_POINTER (sig_idx)); + if (prev_sig) { + /* Somebody got in before us */ + sig = prev_sig; + } + else { + g_hash_table_insert (image->memberref_signatures, GUINT_TO_POINTER (sig_idx), sig); + /* An approximation based on glib 2.18 */ + mono_atomic_fetch_add_i32 (&memberref_sig_cache_size, sizeof (gpointer) * 4); + } + mono_image_unlock (image); + + return sig; +} + +static MonoClassField* +field_from_memberref (MonoImage *image, guint32 token, MonoClass **retklass, + MonoGenericContext *context, MonoError *error) +{ + MonoClass *klass = NULL; + MonoClassField *field; + MonoTableInfo *tables = image->tables; + MonoType *sig_type; + guint32 cols[6]; + guint32 nindex, class_index; + const char *fname; + const char *ptr; + guint32 idx = mono_metadata_token_index (token); + + error_init (error); + + mono_metadata_decode_row (&tables [MONO_TABLE_MEMBERREF], idx-1, cols, MONO_MEMBERREF_SIZE); + nindex = cols [MONO_MEMBERREF_CLASS] >> MONO_MEMBERREF_PARENT_BITS; + class_index = cols [MONO_MEMBERREF_CLASS] & MONO_MEMBERREF_PARENT_MASK; + + fname = mono_metadata_string_heap (image, cols [MONO_MEMBERREF_NAME]); + + if (!mono_verifier_verify_memberref_field_signature (image, cols [MONO_MEMBERREF_SIGNATURE], NULL)) { + mono_error_set_bad_image (error, image, "Bad field '%u' signature 0x%08x", class_index, token); + return NULL; + } + + switch (class_index) { + case MONO_MEMBERREF_PARENT_TYPEDEF: + klass = mono_class_get_checked (image, MONO_TOKEN_TYPE_DEF | nindex, error); + break; + case MONO_MEMBERREF_PARENT_TYPEREF: + klass = mono_class_from_typeref_checked (image, MONO_TOKEN_TYPE_REF | nindex, error); + break; + case MONO_MEMBERREF_PARENT_TYPESPEC: + klass = mono_class_get_and_inflate_typespec_checked (image, MONO_TOKEN_TYPE_SPEC | nindex, context, error); + break; + default: + mono_error_set_bad_image (error, image, "Bad field field '%u' signature 0x%08x", class_index, token); + } + + if (!klass) + return NULL; + + ptr = mono_metadata_blob_heap (image, cols [MONO_MEMBERREF_SIGNATURE]); + mono_metadata_decode_blob_size (ptr, &ptr); + /* we may want to check the signature here... */ + + if (*ptr++ != 0x6) { + mono_error_set_field_load (error, klass, fname, "Bad field signature class token %08x field name %s token %08x", class_index, fname, token); + return NULL; + } + + /* FIXME: This needs a cache, especially for generic instances, since + * we ask mono_metadata_parse_type_checked () to allocates everything from a mempool. + * FIXME part2, mono_metadata_parse_type_checked actually allows for a transient type instead. + * FIXME part3, transient types are not 100% transient, so we need to take care of that first. + */ + sig_type = (MonoType *)find_cached_memberref_sig (image, cols [MONO_MEMBERREF_SIGNATURE]); + if (!sig_type) { + MonoError inner_error; + sig_type = mono_metadata_parse_type_checked (image, NULL, 0, FALSE, ptr, &ptr, &inner_error); + if (sig_type == NULL) { + mono_error_set_field_load (error, klass, fname, "Could not parse field '%s' signature %08x due to: %s", fname, token, mono_error_get_message (&inner_error)); + mono_error_cleanup (&inner_error); + return NULL; + } + sig_type = (MonoType *)cache_memberref_sig (image, cols [MONO_MEMBERREF_SIGNATURE], sig_type); + } + + mono_class_init (klass); /*FIXME is this really necessary?*/ + if (retklass) + *retklass = klass; + field = mono_class_get_field_from_name_full (klass, fname, sig_type); + + if (!field) { + mono_error_set_field_load (error, klass, fname, "Could not find field '%s'", fname); + } + + return field; +} + +/** + * mono_field_from_token: + * \deprecated use the \c _checked variant + * Notes: runtime code MUST not use this function + */ +MonoClassField* +mono_field_from_token (MonoImage *image, guint32 token, MonoClass **retklass, MonoGenericContext *context) +{ + MonoError error; + MonoClassField *res = mono_field_from_token_checked (image, token, retklass, context, &error); + g_assert (mono_error_ok (&error)); + return res; +} + +MonoClassField* +mono_field_from_token_checked (MonoImage *image, guint32 token, MonoClass **retklass, MonoGenericContext *context, MonoError *error) +{ + MonoClass *k; + guint32 type; + MonoClassField *field; + + error_init (error); + + if (image_is_dynamic (image)) { + MonoClassField *result; + MonoClass *handle_class; + + *retklass = NULL; + MonoError inner_error; + result = (MonoClassField *)mono_lookup_dynamic_token_class (image, token, TRUE, &handle_class, context, &inner_error); + mono_error_cleanup (&inner_error); + // This checks the memberref type as well + if (!result || handle_class != mono_defaults.fieldhandle_class) { + mono_error_set_bad_image (error, image, "Bad field token 0x%08x", token); + return NULL; + } + *retklass = result->parent; + return result; + } + + if ((field = (MonoClassField *)mono_conc_hashtable_lookup (image->field_cache, GUINT_TO_POINTER (token)))) { + *retklass = field->parent; + return field; + } + + if (mono_metadata_token_table (token) == MONO_TABLE_MEMBERREF) { + field = field_from_memberref (image, token, retklass, context, error); + } else { + type = mono_metadata_typedef_from_field (image, mono_metadata_token_index (token)); + if (!type) { + mono_error_set_bad_image (error, image, "Invalid field token 0x%08x", token); + return NULL; + } + k = mono_class_get_checked (image, MONO_TOKEN_TYPE_DEF | type, error); + if (!k) + return NULL; + + mono_class_init (k); + if (retklass) + *retklass = k; + if (mono_class_has_failure (k)) { + MonoError causedby_error; + error_init (&causedby_error); + mono_error_set_for_class_failure (&causedby_error, k); + mono_error_set_bad_image (error, image, "Could not resolve field token 0x%08x, due to: %s", token, mono_error_get_message (&causedby_error)); + mono_error_cleanup (&causedby_error); + } else { + field = mono_class_get_field (k, token); + if (!field) { + mono_error_set_bad_image (error, image, "Could not resolve field token 0x%08x", token); + } + } + } + + if (field && field->parent && !mono_class_is_ginst (field->parent) && !mono_class_is_gtd (field->parent)) { + mono_image_lock (image); + mono_conc_hashtable_insert (image->field_cache, GUINT_TO_POINTER (token), field); + mono_image_unlock (image); + } + + return field; +} + +static gboolean +mono_metadata_signature_vararg_match (MonoMethodSignature *sig1, MonoMethodSignature *sig2) +{ + int i; + + if (sig1->hasthis != sig2->hasthis || + sig1->sentinelpos != sig2->sentinelpos) + return FALSE; + + for (i = 0; i < sig1->sentinelpos; i++) { + MonoType *p1 = sig1->params[i]; + MonoType *p2 = sig2->params[i]; + + /*if (p1->attrs != p2->attrs) + return FALSE; + */ + if (!mono_metadata_type_equal (p1, p2)) + return FALSE; + } + + if (!mono_metadata_type_equal (sig1->ret, sig2->ret)) + return FALSE; + return TRUE; +} + +static MonoMethod * +find_method_in_class (MonoClass *klass, const char *name, const char *qname, const char *fqname, + MonoMethodSignature *sig, MonoClass *from_class, MonoError *error) +{ + int i; + + /* Search directly in the metadata to avoid calling setup_methods () */ + error_init (error); + + /* FIXME: !mono_class_is_ginst (from_class) condition causes test failures. */ + if (klass->type_token && !image_is_dynamic (klass->image) && !klass->methods && !klass->rank && klass == from_class && !mono_class_is_ginst (from_class)) { + int first_idx = mono_class_get_first_method_idx (klass); + int mcount = mono_class_get_method_count (klass); + for (i = 0; i < mcount; ++i) { + guint32 cols [MONO_METHOD_SIZE]; + MonoMethod *method; + const char *m_name; + MonoMethodSignature *other_sig; + + mono_metadata_decode_table_row (klass->image, MONO_TABLE_METHOD, first_idx + i, cols, MONO_METHOD_SIZE); + + m_name = mono_metadata_string_heap (klass->image, cols [MONO_METHOD_NAME]); + + if (!((fqname && !strcmp (m_name, fqname)) || + (qname && !strcmp (m_name, qname)) || + (name && !strcmp (m_name, name)))) + continue; + + method = mono_get_method_checked (klass->image, MONO_TOKEN_METHOD_DEF | (first_idx + i + 1), klass, NULL, error); + if (!mono_error_ok (error)) //bail out if we hit a loader error + return NULL; + if (method) { + other_sig = mono_method_signature_checked (method, error); + if (!mono_error_ok (error)) //bail out if we hit a loader error + return NULL; + if (other_sig && (sig->call_convention != MONO_CALL_VARARG) && mono_metadata_signature_equal (sig, other_sig)) + return method; + } + } + } + + mono_class_setup_methods (klass); /* FIXME don't swallow the error here. */ + /* + We can't fail lookup of methods otherwise the runtime will fail with MissingMethodException instead of TypeLoadException. + See mono/tests/generic-type-load-exception.2.il + FIXME we should better report this error to the caller + */ + if (!klass->methods || mono_class_has_failure (klass)) { + mono_error_set_type_load_class (error, klass, "Could not find method due to a type load error"); //FIXME get the error from the class + + return NULL; + } + int mcount = mono_class_get_method_count (klass); + for (i = 0; i < mcount; ++i) { + MonoMethod *m = klass->methods [i]; + MonoMethodSignature *msig; + + /* We must cope with failing to load some of the types. */ + if (!m) + continue; + + if (!((fqname && !strcmp (m->name, fqname)) || + (qname && !strcmp (m->name, qname)) || + (name && !strcmp (m->name, name)))) + continue; + msig = mono_method_signature_checked (m, error); + if (!mono_error_ok (error)) //bail out if we hit a loader error + return NULL; + + if (!msig) + continue; + + if (sig->call_convention == MONO_CALL_VARARG) { + if (mono_metadata_signature_vararg_match (sig, msig)) + break; + } else { + if (mono_metadata_signature_equal (sig, msig)) + break; + } + } + + if (i < mcount) + return mono_class_get_method_by_index (from_class, i); + return NULL; +} + +static MonoMethod * +find_method (MonoClass *in_class, MonoClass *ic, const char* name, MonoMethodSignature *sig, MonoClass *from_class, MonoError *error) +{ + int i; + char *qname, *fqname, *class_name; + gboolean is_interface; + MonoMethod *result = NULL; + MonoClass *initial_class = in_class; + + error_init (error); + is_interface = MONO_CLASS_IS_INTERFACE (in_class); + + if (ic) { + class_name = mono_type_get_name_full (&ic->byval_arg, MONO_TYPE_NAME_FORMAT_IL); + + qname = g_strconcat (class_name, ".", name, NULL); + if (ic->name_space && ic->name_space [0]) + fqname = g_strconcat (ic->name_space, ".", class_name, ".", name, NULL); + else + fqname = NULL; + } else + class_name = qname = fqname = NULL; + + while (in_class) { + g_assert (from_class); + result = find_method_in_class (in_class, name, qname, fqname, sig, from_class, error); + if (result || !mono_error_ok (error)) + goto out; + + if (name [0] == '.' && (!strcmp (name, ".ctor") || !strcmp (name, ".cctor"))) + break; + + /* + * This happens when we fail to lazily load the interfaces of one of the types. + * On such case we can't just bail out since user code depends on us trying harder. + */ + if (from_class->interface_offsets_count != in_class->interface_offsets_count) { + in_class = in_class->parent; + from_class = from_class->parent; + continue; + } + + for (i = 0; i < in_class->interface_offsets_count; i++) { + MonoClass *in_ic = in_class->interfaces_packed [i]; + MonoClass *from_ic = from_class->interfaces_packed [i]; + char *ic_qname, *ic_fqname, *ic_class_name; + + ic_class_name = mono_type_get_name_full (&in_ic->byval_arg, MONO_TYPE_NAME_FORMAT_IL); + ic_qname = g_strconcat (ic_class_name, ".", name, NULL); + if (in_ic->name_space && in_ic->name_space [0]) + ic_fqname = g_strconcat (in_ic->name_space, ".", ic_class_name, ".", name, NULL); + else + ic_fqname = NULL; + + result = find_method_in_class (in_ic, ic ? name : NULL, ic_qname, ic_fqname, sig, from_ic, error); + g_free (ic_class_name); + g_free (ic_fqname); + g_free (ic_qname); + if (result || !mono_error_ok (error)) + goto out; + } + + in_class = in_class->parent; + from_class = from_class->parent; + } + g_assert (!in_class == !from_class); + + if (is_interface) + result = find_method_in_class (mono_defaults.object_class, name, qname, fqname, sig, mono_defaults.object_class, error); + + //we did not find the method + if (!result && mono_error_ok (error)) { + char *desc = mono_signature_get_managed_fmt_string (sig); + mono_error_set_method_load (error, initial_class, g_strdup (name), desc, ""); + } + + out: + g_free (class_name); + g_free (fqname); + g_free (qname); + return result; +} + +static MonoMethodSignature* +inflate_generic_signature_checked (MonoImage *image, MonoMethodSignature *sig, MonoGenericContext *context, MonoError *error) +{ + MonoMethodSignature *res; + gboolean is_open; + int i; + + error_init (error); + if (!context) + return sig; + + res = (MonoMethodSignature *)g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + ((gint32)sig->param_count) * sizeof (MonoType*)); + res->param_count = sig->param_count; + res->sentinelpos = -1; + res->ret = mono_class_inflate_generic_type_checked (sig->ret, context, error); + if (!mono_error_ok (error)) + goto fail; + is_open = mono_class_is_open_constructed_type (res->ret); + for (i = 0; i < sig->param_count; ++i) { + res->params [i] = mono_class_inflate_generic_type_checked (sig->params [i], context, error); + if (!mono_error_ok (error)) + goto fail; + + if (!is_open) + is_open = mono_class_is_open_constructed_type (res->params [i]); + } + res->hasthis = sig->hasthis; + res->explicit_this = sig->explicit_this; + res->call_convention = sig->call_convention; + res->pinvoke = sig->pinvoke; + res->generic_param_count = sig->generic_param_count; + res->sentinelpos = sig->sentinelpos; + res->has_type_parameters = is_open; + res->is_inflated = 1; + return res; + +fail: + if (res->ret) + mono_metadata_free_type (res->ret); + for (i = 0; i < sig->param_count; ++i) { + if (res->params [i]) + mono_metadata_free_type (res->params [i]); + } + g_free (res); + return NULL; +} + +/** + * mono_inflate_generic_signature: + * + * Inflate \p sig with \p context, and return a canonical copy. On error, set \p error, and return NULL. + */ +MonoMethodSignature* +mono_inflate_generic_signature (MonoMethodSignature *sig, MonoGenericContext *context, MonoError *error) +{ + MonoMethodSignature *res, *cached; + + res = inflate_generic_signature_checked (NULL, sig, context, error); + if (!mono_error_ok (error)) + return NULL; + cached = mono_metadata_get_inflated_signature (res, context); + if (cached != res) + mono_metadata_free_inflated_signature (res); + return cached; +} + +static MonoMethodHeader* +inflate_generic_header (MonoMethodHeader *header, MonoGenericContext *context, MonoError *error) +{ + size_t locals_size = sizeof (gpointer) * header->num_locals; + size_t clauses_size = header->num_clauses * sizeof (MonoExceptionClause); + size_t header_size = MONO_SIZEOF_METHOD_HEADER + locals_size + clauses_size; + MonoMethodHeader *res = (MonoMethodHeader *)g_malloc0 (header_size); + res->num_locals = header->num_locals; + res->clauses = (MonoExceptionClause *) &res->locals [res->num_locals] ; + memcpy (res->clauses, header->clauses, clauses_size); + + res->code = header->code; + res->code_size = header->code_size; + res->max_stack = header->max_stack; + res->num_clauses = header->num_clauses; + res->init_locals = header->init_locals; + + res->is_transient = TRUE; + + error_init (error); + + for (int i = 0; i < header->num_locals; ++i) { + res->locals [i] = mono_class_inflate_generic_type_checked (header->locals [i], context, error); + goto_if_nok (error, fail); + } + if (res->num_clauses) { + for (int i = 0; i < header->num_clauses; ++i) { + MonoExceptionClause *clause = &res->clauses [i]; + if (clause->flags != MONO_EXCEPTION_CLAUSE_NONE) + continue; + clause->data.catch_class = mono_class_inflate_generic_class_checked (clause->data.catch_class, context, error); + goto_if_nok (error, fail); + } + } + return res; +fail: + g_free (res); + return NULL; +} + +/** + * mono_method_get_signature_full: + * \p token is the method ref/def/spec token used in a \c call IL instruction. + * \deprecated use the \c _checked variant + * Notes: runtime code MUST not use this function + */ +MonoMethodSignature* +mono_method_get_signature_full (MonoMethod *method, MonoImage *image, guint32 token, MonoGenericContext *context) +{ + MonoError error; + MonoMethodSignature *res = mono_method_get_signature_checked (method, image, token, context, &error); + mono_error_cleanup (&error); + return res; +} + +MonoMethodSignature* +mono_method_get_signature_checked (MonoMethod *method, MonoImage *image, guint32 token, MonoGenericContext *context, MonoError *error) +{ + int table = mono_metadata_token_table (token); + int idx = mono_metadata_token_index (token); + int sig_idx; + guint32 cols [MONO_MEMBERREF_SIZE]; + MonoMethodSignature *sig; + const char *ptr; + + error_init (error); + + /* !table is for wrappers: we should really assign their own token to them */ + if (!table || table == MONO_TABLE_METHOD) + return mono_method_signature_checked (method, error); + + if (table == MONO_TABLE_METHODSPEC) { + /* the verifier (do_invoke_method) will turn the NULL into a verifier error */ + if ((method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) || !method->is_inflated) { + mono_error_set_bad_image (error, image, "Method is a pinvoke or open generic"); + return NULL; + } + + return mono_method_signature_checked (method, error); + } + + if (mono_class_is_ginst (method->klass)) + return mono_method_signature_checked (method, error); + + if (image_is_dynamic (image)) { + sig = mono_reflection_lookup_signature (image, method, token, error); + if (!sig) + return NULL; + } else { + mono_metadata_decode_row (&image->tables [MONO_TABLE_MEMBERREF], idx-1, cols, MONO_MEMBERREF_SIZE); + sig_idx = cols [MONO_MEMBERREF_SIGNATURE]; + + sig = (MonoMethodSignature *)find_cached_memberref_sig (image, sig_idx); + if (!sig) { + if (!mono_verifier_verify_memberref_method_signature (image, sig_idx, NULL)) { + guint32 klass = cols [MONO_MEMBERREF_CLASS] & MONO_MEMBERREF_PARENT_MASK; + const char *fname = mono_metadata_string_heap (image, cols [MONO_MEMBERREF_NAME]); + + //FIXME include the verification error + mono_error_set_bad_image (error, image, "Bad method signature class token 0x%08x field name %s token 0x%08x", klass, fname, token); + return NULL; + } + + ptr = mono_metadata_blob_heap (image, sig_idx); + mono_metadata_decode_blob_size (ptr, &ptr); + + sig = mono_metadata_parse_method_signature_full (image, NULL, 0, ptr, NULL, error); + if (!sig) + return NULL; + + sig = (MonoMethodSignature *)cache_memberref_sig (image, sig_idx, sig); + } + /* FIXME: we probably should verify signature compat in the dynamic case too*/ + if (!mono_verifier_is_sig_compatible (image, method, sig)) { + guint32 klass = cols [MONO_MEMBERREF_CLASS] & MONO_MEMBERREF_PARENT_MASK; + const char *fname = mono_metadata_string_heap (image, cols [MONO_MEMBERREF_NAME]); + + mono_error_set_bad_image (error, image, "Incompatible method signature class token 0x%08x field name %s token 0x%08x", klass, fname, token); + return NULL; + } + } + + if (context) { + MonoMethodSignature *cached; + + /* This signature is not owned by a MonoMethod, so need to cache */ + sig = inflate_generic_signature_checked (image, sig, context, error); + if (!mono_error_ok (error)) + return NULL; + + cached = mono_metadata_get_inflated_signature (sig, context); + if (cached != sig) + mono_metadata_free_inflated_signature (sig); + else + mono_atomic_fetch_add_i32 (&inflated_signatures_size, mono_metadata_signature_size (cached)); + sig = cached; + } + + g_assert (mono_error_ok (error)); + return sig; +} + +/** + * mono_method_get_signature: + * \p token is the method_ref/def/spec token used in a call IL instruction. + * \deprecated use the \c _checked variant + * Notes: runtime code MUST not use this function + */ +MonoMethodSignature* +mono_method_get_signature (MonoMethod *method, MonoImage *image, guint32 token) +{ + MonoError error; + MonoMethodSignature *res = mono_method_get_signature_checked (method, image, token, NULL, &error); + mono_error_cleanup (&error); + return res; +} + +/* this is only for the typespec array methods */ +MonoMethod* +mono_method_search_in_array_class (MonoClass *klass, const char *name, MonoMethodSignature *sig) +{ + int i; + + mono_class_setup_methods (klass); + g_assert (!mono_class_has_failure (klass)); /*FIXME this should not fail, right?*/ + int mcount = mono_class_get_method_count (klass); + for (i = 0; i < mcount; ++i) { + MonoMethod *method = klass->methods [i]; + if (strcmp (method->name, name) == 0 && sig->param_count == method->signature->param_count) + return method; + } + return NULL; +} + +static MonoMethod * +method_from_memberref (MonoImage *image, guint32 idx, MonoGenericContext *typespec_context, + gboolean *used_context, MonoError *error) +{ + MonoClass *klass = NULL; + MonoMethod *method = NULL; + MonoTableInfo *tables = image->tables; + guint32 cols[6]; + guint32 nindex, class_index, sig_idx; + const char *mname; + MonoMethodSignature *sig; + const char *ptr; + + error_init (error); + + mono_metadata_decode_row (&tables [MONO_TABLE_MEMBERREF], idx-1, cols, 3); + nindex = cols [MONO_MEMBERREF_CLASS] >> MONO_MEMBERREF_PARENT_BITS; + class_index = cols [MONO_MEMBERREF_CLASS] & MONO_MEMBERREF_PARENT_MASK; + /*g_print ("methodref: 0x%x 0x%x %s\n", class, nindex, + mono_metadata_string_heap (m, cols [MONO_MEMBERREF_NAME]));*/ + + mname = mono_metadata_string_heap (image, cols [MONO_MEMBERREF_NAME]); + + /* + * Whether we actually used the `typespec_context' or not. + * This is used to tell our caller whether or not it's safe to insert the returned + * method into a cache. + */ + if (used_context) + *used_context = class_index == MONO_MEMBERREF_PARENT_TYPESPEC; + + switch (class_index) { + case MONO_MEMBERREF_PARENT_TYPEREF: + klass = mono_class_from_typeref_checked (image, MONO_TOKEN_TYPE_REF | nindex, error); + if (!klass) + goto fail; + break; + case MONO_MEMBERREF_PARENT_TYPESPEC: + /* + * Parse the TYPESPEC in the parent's context. + */ + klass = mono_class_get_and_inflate_typespec_checked (image, MONO_TOKEN_TYPE_SPEC | nindex, typespec_context, error); + if (!klass) + goto fail; + break; + case MONO_MEMBERREF_PARENT_TYPEDEF: + klass = mono_class_get_checked (image, MONO_TOKEN_TYPE_DEF | nindex, error); + if (!klass) + goto fail; + break; + case MONO_MEMBERREF_PARENT_METHODDEF: { + method = mono_get_method_checked (image, MONO_TOKEN_METHOD_DEF | nindex, NULL, NULL, error); + if (!method) + goto fail; + return method; + } + default: + mono_error_set_bad_image (error, image, "Memberref parent unknown: class: %d, index %d", class_index, nindex); + goto fail; + } + + g_assert (klass); + mono_class_init (klass); + + sig_idx = cols [MONO_MEMBERREF_SIGNATURE]; + + if (!mono_verifier_verify_memberref_method_signature (image, sig_idx, NULL)) { + mono_error_set_method_load (error, klass, g_strdup (mname), NULL, "Verifier rejected method signature"); + goto fail; + } + + ptr = mono_metadata_blob_heap (image, sig_idx); + mono_metadata_decode_blob_size (ptr, &ptr); + + sig = (MonoMethodSignature *)find_cached_memberref_sig (image, sig_idx); + if (!sig) { + sig = mono_metadata_parse_method_signature_full (image, NULL, 0, ptr, NULL, error); + if (sig == NULL) + goto fail; + + sig = (MonoMethodSignature *)cache_memberref_sig (image, sig_idx, sig); + } + + switch (class_index) { + case MONO_MEMBERREF_PARENT_TYPEREF: + case MONO_MEMBERREF_PARENT_TYPEDEF: + method = find_method (klass, NULL, mname, sig, klass, error); + break; + + case MONO_MEMBERREF_PARENT_TYPESPEC: { + MonoType *type; + + type = &klass->byval_arg; + + if (type->type != MONO_TYPE_ARRAY && type->type != MONO_TYPE_SZARRAY) { + MonoClass *in_class = mono_class_is_ginst (klass) ? mono_class_get_generic_class (klass)->container_class : klass; + method = find_method (in_class, NULL, mname, sig, klass, error); + break; + } + + /* we're an array and we created these methods already in klass in mono_class_init () */ + method = mono_method_search_in_array_class (klass, mname, sig); + break; + } + default: + mono_error_set_bad_image (error, image,"Memberref parent unknown: class: %d, index %d", class_index, nindex); + goto fail; + } + + if (!method && mono_error_ok (error)) { + + char *desc = mono_signature_get_managed_fmt_string (sig); + + mono_error_set_method_load (error, klass, g_strdup (mname), desc, "Failed to load due to unknown reasons"); + } + + return method; + +fail: + g_assert (!mono_error_ok (error)); + return NULL; +} + +static MonoMethod * +method_from_methodspec (MonoImage *image, MonoGenericContext *context, guint32 idx, MonoError *error) +{ + MonoMethod *method; + MonoClass *klass; + MonoTableInfo *tables = image->tables; + MonoGenericContext new_context; + MonoGenericInst *inst; + const char *ptr; + guint32 cols [MONO_METHODSPEC_SIZE]; + guint32 token, nindex, param_count; + + error_init (error); + + mono_metadata_decode_row (&tables [MONO_TABLE_METHODSPEC], idx - 1, cols, MONO_METHODSPEC_SIZE); + token = cols [MONO_METHODSPEC_METHOD]; + nindex = token >> MONO_METHODDEFORREF_BITS; + + if (!mono_verifier_verify_methodspec_signature (image, cols [MONO_METHODSPEC_SIGNATURE], NULL)) { + mono_error_set_bad_image (error, image, "Bad method signals signature 0x%08x", idx); + return NULL; + } + + ptr = mono_metadata_blob_heap (image, cols [MONO_METHODSPEC_SIGNATURE]); + + mono_metadata_decode_value (ptr, &ptr); + ptr++; + param_count = mono_metadata_decode_value (ptr, &ptr); + + inst = mono_metadata_parse_generic_inst (image, NULL, param_count, ptr, &ptr, error); + if (!inst) + return NULL; + + if (context && inst->is_open) { + inst = mono_metadata_inflate_generic_inst (inst, context, error); + if (!mono_error_ok (error)) + return NULL; + } + + if ((token & MONO_METHODDEFORREF_MASK) == MONO_METHODDEFORREF_METHODDEF) { + method = mono_get_method_checked (image, MONO_TOKEN_METHOD_DEF | nindex, NULL, context, error); + if (!method) + return NULL; + } else { + method = method_from_memberref (image, nindex, context, NULL, error); + } + + if (!method) + return NULL; + + klass = method->klass; + + if (mono_class_is_ginst (klass)) { + g_assert (method->is_inflated); + method = ((MonoMethodInflated *) method)->declaring; + } + + new_context.class_inst = mono_class_is_ginst (klass) ? mono_class_get_generic_class (klass)->context.class_inst : NULL; + new_context.method_inst = inst; + + method = mono_class_inflate_generic_method_full_checked (method, klass, &new_context, error); + return method; +} + +struct _MonoDllMap { + char *dll; + char *target; + char *func; + char *target_func; + MonoDllMap *next; +}; + +static MonoDllMap *global_dll_map; + +static int +mono_dllmap_lookup_list (MonoDllMap *dll_map, const char *dll, const char* func, const char **rdll, const char **rfunc) { + int found = 0; + + *rdll = dll; + + if (!dll_map) + return 0; + + global_loader_data_lock (); + + /* + * we use the first entry we find that matches, since entries from + * the config file are prepended to the list and we document that the + * later entries win. + */ + for (; dll_map; dll_map = dll_map->next) { + if (dll_map->dll [0] == 'i' && dll_map->dll [1] == ':') { + if (g_ascii_strcasecmp (dll_map->dll + 2, dll)) + continue; + } else if (strcmp (dll_map->dll, dll)) { + continue; + } + if (!found && dll_map->target) { + *rdll = dll_map->target; + found = 1; + /* we don't quit here, because we could find a full + * entry that matches also function and that has priority. + */ + } + if (dll_map->func && strcmp (dll_map->func, func) == 0) { + *rdll = dll_map->target; + *rfunc = dll_map->target_func; + break; + } + } + + global_loader_data_unlock (); + return found; +} + +static int +mono_dllmap_lookup (MonoImage *assembly, const char *dll, const char* func, const char **rdll, const char **rfunc) +{ + int res; + if (assembly && assembly->dll_map) { + res = mono_dllmap_lookup_list (assembly->dll_map, dll, func, rdll, rfunc); + if (res) + return res; + } + return mono_dllmap_lookup_list (global_dll_map, dll, func, rdll, rfunc); +} + +/** + * mono_dllmap_insert: + * \param assembly if NULL, this is a global mapping, otherwise the remapping of the dynamic library will only apply to the specified assembly + * \param dll The name of the external library, as it would be found in the \c DllImport declaration. If prefixed with i: the matching of the library name is done without case sensitivity + * \param func if not null, the mapping will only applied to the named function (the value of EntryPoint) + * \param tdll The name of the library to map the specified \p dll if it matches. + * \param tfunc The name of the function that replaces the invocation. If NULL, it is replaced with a copy of \p func. + * + * LOCKING: Acquires the loader lock. + * + * This function is used to programatically add \c DllImport remapping in either + * a specific assembly, or as a global remapping. This is done by remapping + * references in a \c DllImport attribute from the \p dll library name into the \p tdll + * name. If the \p dll name contains the prefix i:, the comparison of the + * library name is done without case sensitivity. + * + * If you pass \p func, this is the name of the \c EntryPoint in a \c DllImport if specified + * or the name of the function as determined by \c DllImport. If you pass \p func, you + * must also pass \p tfunc which is the name of the target function to invoke on a match. + * + * Example: + * + * mono_dllmap_insert (NULL, "i:libdemo.dll", NULL, relocated_demo_path, NULL); + * + * The above will remap \c DllImport statements for \c libdemo.dll and \c LIBDEMO.DLL to + * the contents of \c relocated_demo_path for all assemblies in the Mono process. + * + * NOTE: This can be called before the runtime is initialized, for example from + * \c mono_config_parse. + */ +void +mono_dllmap_insert (MonoImage *assembly, const char *dll, const char *func, const char *tdll, const char *tfunc) +{ + MonoDllMap *entry; + + mono_loader_init (); + + if (!assembly) { + entry = (MonoDllMap *)g_malloc0 (sizeof (MonoDllMap)); + entry->dll = dll? g_strdup (dll): NULL; + entry->target = tdll? g_strdup (tdll): NULL; + entry->func = func? g_strdup (func): NULL; + entry->target_func = tfunc? g_strdup (tfunc): (func? g_strdup (func): NULL); + + global_loader_data_lock (); + entry->next = global_dll_map; + global_dll_map = entry; + global_loader_data_unlock (); + } else { + entry = (MonoDllMap *)mono_image_alloc0 (assembly, sizeof (MonoDllMap)); + entry->dll = dll? mono_image_strdup (assembly, dll): NULL; + entry->target = tdll? mono_image_strdup (assembly, tdll): NULL; + entry->func = func? mono_image_strdup (assembly, func): NULL; + entry->target_func = tfunc? mono_image_strdup (assembly, tfunc): (func? mono_image_strdup (assembly, func): NULL); + + mono_image_lock (assembly); + entry->next = assembly->dll_map; + assembly->dll_map = entry; + mono_image_unlock (assembly); + } +} + +static void +free_dllmap (MonoDllMap *map) +{ + while (map) { + MonoDllMap *next = map->next; + + g_free (map->dll); + g_free (map->target); + g_free (map->func); + g_free (map->target_func); + g_free (map); + map = next; + } +} + +static void +dllmap_cleanup (void) +{ + free_dllmap (global_dll_map); + global_dll_map = NULL; +} + +static GHashTable *global_module_map; + +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; +} + +void +mono_loader_register_module (const char *name, MonoDl *module) +{ + if (!global_module_map) + global_module_map = g_hash_table_new (g_str_hash, g_str_equal); + g_hash_table_insert (global_module_map, g_strdup (name), module); +} + +static void +remove_cached_module(gpointer key, gpointer value, gpointer user_data) +{ + mono_dl_close((MonoDl*)value); +} + +static void +cached_module_cleanup(void) +{ + if (global_module_map != NULL) { + g_hash_table_foreach(global_module_map, remove_cached_module, NULL); + + g_hash_table_destroy(global_module_map); + global_module_map = NULL; + } +} + +static MonoDl *internal_module; + +static gboolean +is_absolute_path (const char *path) +{ +#ifdef HOST_DARWIN + if (!strncmp (path, "@executable_path/", 17) || !strncmp (path, "@loader_path/", 13) || + !strncmp (path, "@rpath/", 7)) + return TRUE; +#endif + return g_path_is_absolute (path); +} + +/** + * mono_lookup_pinvoke_call: + */ +gpointer +mono_lookup_pinvoke_call (MonoMethod *method, const char **exc_class, const char **exc_arg) +{ + MonoImage *image = method->klass->image; + MonoMethodPInvoke *piinfo = (MonoMethodPInvoke *)method; + MonoTableInfo *tables = image->tables; + MonoTableInfo *im = &tables [MONO_TABLE_IMPLMAP]; + MonoTableInfo *mr = &tables [MONO_TABLE_MODULEREF]; + guint32 im_cols [MONO_IMPLMAP_SIZE]; + guint32 scope_token; + const char *import = NULL; + const char *orig_scope; + const char *new_scope; + char *error_msg; + char *full_name, *file_name, *found_name = NULL; + int i,j; + MonoDl *module = NULL; + gboolean cached = FALSE; + gpointer addr = NULL; + + g_assert (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL); + + if (exc_class) { + *exc_class = NULL; + *exc_arg = NULL; + } + + if (piinfo->addr) + return piinfo->addr; + + if (image_is_dynamic (method->klass->image)) { + MonoReflectionMethodAux *method_aux = + (MonoReflectionMethodAux *)g_hash_table_lookup ( + ((MonoDynamicImage*)method->klass->image)->method_aux_hash, method); + if (!method_aux) + return NULL; + + import = method_aux->dllentry; + orig_scope = method_aux->dll; + } + else { + if (!piinfo->implmap_idx || piinfo->implmap_idx > im->rows) + return NULL; + + mono_metadata_decode_row (im, piinfo->implmap_idx - 1, im_cols, MONO_IMPLMAP_SIZE); + + if (!im_cols [MONO_IMPLMAP_SCOPE] || im_cols [MONO_IMPLMAP_SCOPE] > mr->rows) + return NULL; + + piinfo->piflags = im_cols [MONO_IMPLMAP_FLAGS]; + import = mono_metadata_string_heap (image, im_cols [MONO_IMPLMAP_NAME]); + scope_token = mono_metadata_decode_row_col (mr, im_cols [MONO_IMPLMAP_SCOPE] - 1, MONO_MODULEREF_NAME); + orig_scope = mono_metadata_string_heap (image, scope_token); + } + + mono_dllmap_lookup (image, orig_scope, import, &new_scope, &import); + + if (!module) { + mono_image_lock (image); + if (!image->pinvoke_scopes) { + image->pinvoke_scopes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + image->pinvoke_scope_filenames = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + } + module = (MonoDl *)g_hash_table_lookup (image->pinvoke_scopes, new_scope); + found_name = (char *)g_hash_table_lookup (image->pinvoke_scope_filenames, new_scope); + mono_image_unlock (image); + if (module) + cached = TRUE; + if (found_name) + found_name = g_strdup (found_name); + } + + if (!module) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, + "DllImport attempting to load: '%s'.", new_scope); + + /* we allow a special name to dlopen from the running process namespace */ + if (strcmp (new_scope, "__Internal") == 0){ + if (internal_module == NULL) + internal_module = mono_dl_open (NULL, MONO_DL_LAZY, &error_msg); + module = internal_module; + } + } + + 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 + */ + for (i = 0; i < 5; ++i) { + char *base_name = NULL, *dir_name = NULL; + gboolean is_absolute = is_absolute_path (new_scope); + + switch (i) { + case 0: + /* Try the original name */ + file_name = g_strdup (new_scope); + break; + case 1: + /* Try trimming the .dll extension */ + if (strstr (new_scope, ".dll") == (new_scope + strlen (new_scope) - 4)) { + file_name = g_strdup (new_scope); + file_name [strlen (new_scope) - 4] = '\0'; + } + else + continue; + break; + case 2: + if (is_absolute) { + dir_name = g_path_get_dirname (new_scope); + base_name = g_path_get_basename (new_scope); + if (strstr (base_name, "lib") != base_name) { + char *tmp = g_strdup_printf ("lib%s", base_name); + g_free (base_name); + base_name = tmp; + file_name = g_strdup_printf ("%s%s%s", dir_name, G_DIR_SEPARATOR_S, base_name); + break; + } + } else if (strstr (new_scope, "lib") != new_scope) { + file_name = g_strdup_printf ("lib%s", new_scope); + break; + } + continue; + case 3: + if (!is_absolute && mono_dl_get_system_dir ()) { + dir_name = (char*)mono_dl_get_system_dir (); + file_name = g_path_get_basename (new_scope); + base_name = NULL; + } else + continue; + break; + default: +#ifndef TARGET_WIN32 + if (!g_ascii_strcasecmp ("user32.dll", new_scope) || + !g_ascii_strcasecmp ("kernel32.dll", new_scope) || + !g_ascii_strcasecmp ("user32", new_scope) || + !g_ascii_strcasecmp ("kernel", new_scope)) { + file_name = g_strdup ("libMonoSupportW.so"); + } else +#endif + continue; +#ifndef TARGET_WIN32 + break; +#endif + } + + if (is_absolute) { + if (!dir_name) + dir_name = g_path_get_dirname (file_name); + if (!base_name) + base_name = g_path_get_basename (file_name); + } + + if (!module && is_absolute) { + module = cached_module_load (file_name, MONO_DL_LAZY, &error_msg); + if (!module) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, + "DllImport error loading library '%s': '%s'.", + file_name, error_msg); + g_free (error_msg); + } else { + found_name = g_strdup (file_name); + } + } + + if (!module && !is_absolute) { + void *iter; + char *mdirname; + + for (j = 0; j < 3; ++j) { + iter = NULL; + mdirname = NULL; + switch (j) { + case 0: + mdirname = g_path_get_dirname (image->name); + break; + case 1: /* @executable_path@/../lib */ + { + char buf [4096]; + int binl; + binl = mono_dl_get_executable_path (buf, sizeof (buf)); + if (binl != -1) { + char *base, *newbase; + char *resolvedname; + buf [binl] = 0; + resolvedname = mono_path_resolve_symlinks (buf); + + base = g_path_get_dirname (resolvedname); + newbase = g_path_get_dirname(base); + mdirname = g_strdup_printf ("%s/lib", newbase); + + g_free (resolvedname); + g_free (base); + g_free (newbase); + } + break; + } +#ifdef __MACH__ + case 2: /* @executable_path@/../Libraries */ + { + char buf [4096]; + int binl; + binl = mono_dl_get_executable_path (buf, sizeof (buf)); + if (binl != -1) { + char *base, *newbase; + char *resolvedname; + buf [binl] = 0; + resolvedname = mono_path_resolve_symlinks (buf); + + base = g_path_get_dirname (resolvedname); + newbase = g_path_get_dirname(base); + mdirname = g_strdup_printf ("%s/Libraries", newbase); + + g_free (resolvedname); + g_free (base); + g_free (newbase); + } + break; + } +#endif + } + + if (!mdirname) + continue; + + while ((full_name = mono_dl_build_path (mdirname, file_name, &iter))) { + module = cached_module_load (full_name, MONO_DL_LAZY, &error_msg); + if (!module) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, + "DllImport error loading library '%s': '%s'.", + full_name, error_msg); + g_free (error_msg); + } else { + found_name = g_strdup (full_name); + } + g_free (full_name); + if (module) + break; + + } + g_free (mdirname); + if (module) + break; + } + + } + + if (!module) { + void *iter = NULL; + char *file_or_base = is_absolute ? base_name : file_name; + while ((full_name = mono_dl_build_path (dir_name, file_or_base, &iter))) { + module = cached_module_load (full_name, MONO_DL_LAZY, &error_msg); + if (!module) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, + "DllImport error loading library '%s': '%s'.", + full_name, error_msg); + g_free (error_msg); + } else { + found_name = g_strdup (full_name); + } + g_free (full_name); + if (module) + break; + } + } + + if (!module) { + module = cached_module_load (file_name, MONO_DL_LAZY, &error_msg); + if (!module) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, + "DllImport error loading library '%s': '%s'.", + file_name, error_msg); + } else { + found_name = g_strdup (file_name); + } + } + + g_free (file_name); + if (is_absolute) { + g_free (base_name); + g_free (dir_name); + } + + if (module) + break; + } + + if (!module) { + mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_DLLIMPORT, + "DllImport unable to load library '%s'.", + error_msg); + g_free (error_msg); + + if (exc_class) { + *exc_class = "DllNotFoundException"; + *exc_arg = new_scope; + } + return NULL; + } + + if (!cached) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, + "DllImport loaded library '%s'.", found_name); + mono_image_lock (image); + if (!g_hash_table_lookup (image->pinvoke_scopes, new_scope)) { + g_hash_table_insert (image->pinvoke_scopes, g_strdup (new_scope), module); + g_hash_table_insert (image->pinvoke_scope_filenames, g_strdup (new_scope), g_strdup (found_name)); + } + mono_image_unlock (image); + } + + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, + "DllImport searching in: '%s' ('%s').", new_scope, found_name); + g_free (found_name); + +#ifdef TARGET_WIN32 + if (import && import [0] == '#' && isdigit (import [1])) { + char *end; + long id; + + id = strtol (import + 1, &end, 10); + if (id > 0 && *end == '\0') + import++; + } +#endif + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, + "Searching for '%s'.", import); + + if (piinfo->piflags & PINVOKE_ATTRIBUTE_NO_MANGLE) { + error_msg = mono_dl_symbol (module, import, &addr); + } else { + char *mangled_name = NULL, *mangled_name2 = NULL; + int mangle_charset; + int mangle_stdcall; + int mangle_param_count; +#ifdef TARGET_WIN32 + int param_count; +#endif + + /* + * Search using a variety of mangled names + */ + for (mangle_charset = 0; mangle_charset <= 1; mangle_charset ++) { + for (mangle_stdcall = 0; mangle_stdcall <= 1; mangle_stdcall ++) { + gboolean need_param_count = FALSE; +#ifdef TARGET_WIN32 + if (mangle_stdcall > 0) + need_param_count = TRUE; +#endif + for (mangle_param_count = 0; mangle_param_count <= (need_param_count ? 256 : 0); mangle_param_count += 4) { + + if (addr) + continue; + + mangled_name = (char*)import; + switch (piinfo->piflags & PINVOKE_ATTRIBUTE_CHAR_SET_MASK) { + case PINVOKE_ATTRIBUTE_CHAR_SET_UNICODE: + /* Try the mangled name first */ + if (mangle_charset == 0) + mangled_name = g_strconcat (import, "W", NULL); + break; + case PINVOKE_ATTRIBUTE_CHAR_SET_AUTO: +#ifdef TARGET_WIN32 + if (mangle_charset == 0) + mangled_name = g_strconcat (import, "W", NULL); +#else + /* Try the mangled name last */ + if (mangle_charset == 1) + mangled_name = g_strconcat (import, "A", NULL); +#endif + break; + case PINVOKE_ATTRIBUTE_CHAR_SET_ANSI: + default: + /* Try the mangled name last */ + if (mangle_charset == 1) + mangled_name = g_strconcat (import, "A", NULL); + break; + } + +#ifdef TARGET_WIN32 + if (mangle_param_count == 0) + param_count = mono_method_signature (method)->param_count * sizeof (gpointer); + else + /* Try brute force, since it would be very hard to compute the stack usage correctly */ + param_count = mangle_param_count; + + /* Try the stdcall mangled name */ + /* + * gcc under windows creates mangled names without the underscore, but MS.NET + * doesn't support it, so we doesn't support it either. + */ + if (mangle_stdcall == 1) + mangled_name2 = g_strdup_printf ("_%s@%d", mangled_name, param_count); + else + mangled_name2 = mangled_name; +#else + mangled_name2 = mangled_name; +#endif + + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, + "Probing '%s'.", mangled_name2); + + error_msg = mono_dl_symbol (module, mangled_name2, &addr); + + if (addr) + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, + "Found as '%s'.", mangled_name2); + else + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, + "Could not find '%s' due to '%s'.", mangled_name2, error_msg); + + g_free (error_msg); + error_msg = NULL; + + if (mangled_name != mangled_name2) + g_free (mangled_name2); + if (mangled_name != import) + g_free (mangled_name); + } + } + } + } + + if (!addr) { + g_free (error_msg); + if (exc_class) { + *exc_class = "EntryPointNotFoundException"; + *exc_arg = import; + } + return NULL; + } + piinfo->addr = addr; + return addr; +} + +/* + * LOCKING: assumes the loader lock to be taken. + */ +static MonoMethod * +mono_get_method_from_token (MonoImage *image, guint32 token, MonoClass *klass, + MonoGenericContext *context, gboolean *used_context, MonoError *error) +{ + MonoMethod *result; + int table = mono_metadata_token_table (token); + int idx = mono_metadata_token_index (token); + MonoTableInfo *tables = image->tables; + MonoGenericContainer *generic_container = NULL, *container = NULL; + const char *sig = NULL; + guint32 cols [MONO_TYPEDEF_SIZE]; + + error_init (error); + + if (image_is_dynamic (image)) { + MonoClass *handle_class; + + result = (MonoMethod *)mono_lookup_dynamic_token_class (image, token, TRUE, &handle_class, context, error); + mono_error_assert_ok (error); + + // This checks the memberref type as well + if (result && handle_class != mono_defaults.methodhandle_class) { + mono_error_set_bad_image (error, image, "Bad method token 0x%08x on dynamic image", token); + return NULL; + } + return result; + } + + if (table != MONO_TABLE_METHOD) { + if (table == MONO_TABLE_METHODSPEC) { + if (used_context) *used_context = TRUE; + return method_from_methodspec (image, context, idx, error); + } + if (table != MONO_TABLE_MEMBERREF) { + mono_error_set_bad_image (error, image, "Bad method token 0x%08x.", token); + return NULL; + } + return method_from_memberref (image, idx, context, used_context, error); + } + + if (used_context) *used_context = FALSE; + + if (idx > image->tables [MONO_TABLE_METHOD].rows) { + mono_error_set_bad_image (error, image, "Bad method token 0x%08x (out of bounds).", token); + return NULL; + } + + if (!klass) { + guint32 type = mono_metadata_typedef_from_method (image, token); + if (!type) { + mono_error_set_bad_image (error, image, "Bad method token 0x%08x (could not find corresponding typedef).", token); + return NULL; + } + klass = mono_class_get_checked (image, MONO_TOKEN_TYPE_DEF | type, error); + if (klass == NULL) + return NULL; + } + + mono_metadata_decode_row (&image->tables [MONO_TABLE_METHOD], idx - 1, cols, 6); + + if ((cols [2] & METHOD_ATTRIBUTE_PINVOKE_IMPL) || + (cols [1] & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)) { + result = (MonoMethod *)mono_image_alloc0 (image, sizeof (MonoMethodPInvoke)); + } else { + result = (MonoMethod *)mono_image_alloc0 (image, sizeof (MonoMethod)); + mono_atomic_fetch_add_i32 (&methods_size, sizeof (MonoMethod)); + } + + mono_atomic_inc_i32 (&mono_stats.method_count); + + result->slot = -1; + result->klass = klass; + result->flags = cols [2]; + result->iflags = cols [1]; + result->token = token; + result->name = mono_metadata_string_heap (image, cols [3]); + + if (!sig) /* already taken from the methodref */ + sig = mono_metadata_blob_heap (image, cols [4]); + /* size = */ mono_metadata_decode_blob_size (sig, &sig); + + container = mono_class_try_get_generic_container (klass); + + /* + * load_generic_params does a binary search so only call it if the method + * is generic. + */ + if (*sig & 0x10) { + generic_container = mono_metadata_load_generic_params (image, token, container); + } + if (generic_container) { + result->is_generic = TRUE; + generic_container->owner.method = result; + generic_container->is_anonymous = FALSE; // Method is now known, container is no longer anonymous + /*FIXME put this before the image alloc*/ + if (!mono_metadata_load_generic_param_constraints_checked (image, token, generic_container, error)) + return NULL; + + container = generic_container; + } + + if (cols [1] & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) { + if (result->klass == mono_defaults.string_class && !strcmp (result->name, ".ctor")) + result->string_ctor = 1; + } else if (cols [2] & METHOD_ATTRIBUTE_PINVOKE_IMPL) { + MonoMethodPInvoke *piinfo = (MonoMethodPInvoke *)result; + +#ifdef TARGET_WIN32 + /* IJW is P/Invoke with a predefined function pointer. */ + if (image->is_module_handle && (cols [1] & METHOD_IMPL_ATTRIBUTE_NATIVE)) { + piinfo->addr = mono_image_rva_map (image, cols [0]); + g_assert (piinfo->addr); + } +#endif + piinfo->implmap_idx = mono_metadata_implmap_from_method (image, idx - 1); + /* Native methods can have no map. */ + if (piinfo->implmap_idx) + piinfo->piflags = mono_metadata_decode_row_col (&tables [MONO_TABLE_IMPLMAP], piinfo->implmap_idx - 1, MONO_IMPLMAP_FLAGS); + } + + if (generic_container) + mono_method_set_generic_container (result, generic_container); + + return result; +} + +/** + * mono_get_method: + */ +MonoMethod * +mono_get_method (MonoImage *image, guint32 token, MonoClass *klass) +{ + MonoError error; + MonoMethod *result = mono_get_method_checked (image, token, klass, NULL, &error); + mono_error_cleanup (&error); + return result; +} + +/** + * mono_get_method_full: + */ +MonoMethod * +mono_get_method_full (MonoImage *image, guint32 token, MonoClass *klass, + MonoGenericContext *context) +{ + MonoError error; + MonoMethod *result = mono_get_method_checked (image, token, klass, context, &error); + mono_error_cleanup (&error); + return result; +} + +MonoMethod * +mono_get_method_checked (MonoImage *image, guint32 token, MonoClass *klass, MonoGenericContext *context, MonoError *error) +{ + MonoMethod *result = NULL; + gboolean used_context = FALSE; + + /* We do everything inside the lock to prevent creation races */ + + error_init (error); + + mono_image_lock (image); + + if (mono_metadata_token_table (token) == MONO_TABLE_METHOD) { + if (!image->method_cache) + image->method_cache = g_hash_table_new (NULL, NULL); + result = (MonoMethod *)g_hash_table_lookup (image->method_cache, GINT_TO_POINTER (mono_metadata_token_index (token))); + } else if (!image_is_dynamic (image)) { + if (!image->methodref_cache) + image->methodref_cache = g_hash_table_new (NULL, NULL); + result = (MonoMethod *)g_hash_table_lookup (image->methodref_cache, GINT_TO_POINTER (token)); + } + mono_image_unlock (image); + + if (result) + return result; + + + result = mono_get_method_from_token (image, token, klass, context, &used_context, error); + if (!result) + return NULL; + + mono_image_lock (image); + if (!used_context && !result->is_inflated) { + MonoMethod *result2 = NULL; + + if (mono_metadata_token_table (token) == MONO_TABLE_METHOD) + result2 = (MonoMethod *)g_hash_table_lookup (image->method_cache, GINT_TO_POINTER (mono_metadata_token_index (token))); + else if (!image_is_dynamic (image)) + result2 = (MonoMethod *)g_hash_table_lookup (image->methodref_cache, GINT_TO_POINTER (token)); + + if (result2) { + mono_image_unlock (image); + return result2; + } + + if (mono_metadata_token_table (token) == MONO_TABLE_METHOD) + g_hash_table_insert (image->method_cache, GINT_TO_POINTER (mono_metadata_token_index (token)), result); + else if (!image_is_dynamic (image)) + g_hash_table_insert (image->methodref_cache, GINT_TO_POINTER (token), result); + } + + mono_image_unlock (image); + + return result; +} + +static MonoMethod* +get_method_constrained (MonoImage *image, MonoMethod *method, MonoClass *constrained_class, MonoGenericContext *context, MonoError *error) +{ + MonoClass *base_class = method->klass; + + error_init (error); + + if (!mono_class_is_assignable_from (base_class, constrained_class)) { + char *base_class_name = mono_type_get_full_name (base_class); + char *constrained_class_name = mono_type_get_full_name (constrained_class); + mono_error_set_invalid_operation (error, "constrained call: %s is not assignable from %s", base_class_name, constrained_class_name); + g_free (base_class_name); + g_free (constrained_class_name); + return NULL; + } + + /* If the constraining class is actually an interface, we don't learn + * anything new by constraining. + */ + if (MONO_CLASS_IS_INTERFACE (constrained_class)) + return method; + + mono_class_setup_vtable (base_class); + if (mono_class_has_failure (base_class)) { + mono_error_set_for_class_failure (error, base_class); + return NULL; + } + + MonoGenericContext inflated_method_ctx = { .class_inst = NULL, .method_inst = NULL }; + gboolean inflated_generic_method = FALSE; + if (method->is_inflated) { + MonoGenericContext *method_ctx = mono_method_get_context (method); + /* If method is an instantiation of a generic method definition, ie + * class H { void M (...) { ... } } + * and method is H.M + * we will get at the end a refined HSubclass<...>.M and we will need to re-instantiate it with D. + * to get HSubclass<...>.M + * + */ + if (method_ctx->method_inst != NULL) { + inflated_generic_method = TRUE; + inflated_method_ctx.method_inst = method_ctx->method_inst; + } + } + int vtable_slot = 0; + if (!MONO_CLASS_IS_INTERFACE (base_class)) { + /*if the base class isn't an interface and the method isn't + * virtual, there's nothing to do, we're already on the method + * we want to call. */ + if ((method->flags & METHOD_ATTRIBUTE_VIRTUAL) == 0) + return method; + /* if this isn't an interface method, get the vtable slot and + * find the corresponding method in the constrained class, + * which is a subclass of the base class. */ + vtable_slot = mono_method_get_vtable_index (method); + + mono_class_setup_vtable (constrained_class); + if (mono_class_has_failure (constrained_class)) { + mono_error_set_for_class_failure (error, constrained_class); + return NULL; + } + } else { + mono_class_setup_vtable (constrained_class); + if (mono_class_has_failure (constrained_class)) { + mono_error_set_for_class_failure (error, constrained_class); + return NULL; + } + + /* Get the slot of the method in the interface. Then get the + * interface base in constrained_class */ + int itf_slot = mono_method_get_vtable_index (method); + g_assert (itf_slot >= 0); + gboolean variant = FALSE; + int itf_base = mono_class_interface_offset_with_variance (constrained_class, base_class, &variant); + vtable_slot = itf_slot + itf_base; + } + g_assert (vtable_slot >= 0); + + MonoMethod *res = mono_class_get_vtable_entry (constrained_class, vtable_slot); + if (res == NULL && mono_class_is_abstract (constrained_class) ) { + /* Constraining class is abstract, there may not be a refined method. */ + return method; + } + g_assert (res != NULL); + if (inflated_generic_method) { + g_assert (res->is_generic); + res = mono_class_inflate_generic_method_checked (res, &inflated_method_ctx, error); + return_val_if_nok (error, NULL); + } + return res; +} + +MonoMethod * +mono_get_method_constrained_with_method (MonoImage *image, MonoMethod *method, MonoClass *constrained_class, + MonoGenericContext *context, MonoError *error) +{ + g_assert (method); + + return get_method_constrained (image, method, constrained_class, context, error); +} + +/** + * mono_get_method_constrained: + * This is used when JITing the constrained. opcode. + * \returns The contrained method, which has been inflated + * as the function return value; and the original CIL-stream method as + * declared in \p cil_method. The latter is used for verification. + */ +MonoMethod * +mono_get_method_constrained (MonoImage *image, guint32 token, MonoClass *constrained_class, + MonoGenericContext *context, MonoMethod **cil_method) +{ + MonoError error; + MonoMethod *result = mono_get_method_constrained_checked (image, token, constrained_class, context, cil_method, &error); + mono_error_cleanup (&error); + return result; +} + +MonoMethod * +mono_get_method_constrained_checked (MonoImage *image, guint32 token, MonoClass *constrained_class, MonoGenericContext *context, MonoMethod **cil_method, MonoError *error) +{ + error_init (error); + + *cil_method = mono_get_method_checked (image, token, NULL, context, error); + if (!*cil_method) + return NULL; + + return get_method_constrained (image, *cil_method, constrained_class, context, error); +} + +/** + * mono_free_method: + */ +void +mono_free_method (MonoMethod *method) +{ + MONO_PROFILER_RAISE (method_free, (method)); + + /* FIXME: This hack will go away when the profiler will support freeing methods */ + if (G_UNLIKELY (mono_profiler_installed ())) + return; + + if (method->signature) { + /* + * FIXME: This causes crashes because the types inside signatures and + * locals are shared. + */ + /* mono_metadata_free_method_signature (method->signature); */ + /* g_free (method->signature); */ + } + + if (method_is_dynamic (method)) { + MonoMethodWrapper *mw = (MonoMethodWrapper*)method; + int i; + + mono_marshal_free_dynamic_wrappers (method); + + mono_image_property_remove (method->klass->image, method); + + g_free ((char*)method->name); + if (mw->header) { + g_free ((char*)mw->header->code); + for (i = 0; i < mw->header->num_locals; ++i) + g_free (mw->header->locals [i]); + g_free (mw->header->clauses); + g_free (mw->header); + } + g_free (mw->method_data); + g_free (method->signature); + g_free (method); + } +} + +/** + * mono_method_get_param_names: + */ +void +mono_method_get_param_names (MonoMethod *method, const char **names) +{ + int i, lastp; + MonoClass *klass; + MonoTableInfo *methodt; + MonoTableInfo *paramt; + MonoMethodSignature *signature; + guint32 idx; + + if (method->is_inflated) + method = ((MonoMethodInflated *) method)->declaring; + + signature = mono_method_signature (method); + /*FIXME this check is somewhat redundant since the caller usally will have to get the signature to figure out the + number of arguments and allocate a properly sized array. */ + if (signature == NULL) + return; + + if (!signature->param_count) + return; + + for (i = 0; i < signature->param_count; ++i) + names [i] = ""; + + klass = method->klass; + if (klass->rank) + return; + + mono_class_init (klass); + + if (image_is_dynamic (klass->image)) { + MonoReflectionMethodAux *method_aux = + (MonoReflectionMethodAux *)g_hash_table_lookup ( + ((MonoDynamicImage*)method->klass->image)->method_aux_hash, method); + if (method_aux && method_aux->param_names) { + for (i = 0; i < mono_method_signature (method)->param_count; ++i) + if (method_aux->param_names [i + 1]) + names [i] = method_aux->param_names [i + 1]; + } + return; + } + + if (method->wrapper_type) { + char **pnames = NULL; + + mono_image_lock (klass->image); + if (klass->image->wrapper_param_names) + pnames = (char **)g_hash_table_lookup (klass->image->wrapper_param_names, method); + mono_image_unlock (klass->image); + + if (pnames) { + for (i = 0; i < signature->param_count; ++i) + names [i] = pnames [i]; + } + return; + } + + methodt = &klass->image->tables [MONO_TABLE_METHOD]; + paramt = &klass->image->tables [MONO_TABLE_PARAM]; + idx = mono_method_get_index (method); + if (idx > 0) { + guint32 cols [MONO_PARAM_SIZE]; + guint param_index; + + param_index = mono_metadata_decode_row_col (methodt, idx - 1, MONO_METHOD_PARAMLIST); + + if (idx < methodt->rows) + lastp = mono_metadata_decode_row_col (methodt, idx, MONO_METHOD_PARAMLIST); + else + lastp = paramt->rows + 1; + for (i = param_index; i < lastp; ++i) { + mono_metadata_decode_row (paramt, i -1, cols, MONO_PARAM_SIZE); + if (cols [MONO_PARAM_SEQUENCE] && cols [MONO_PARAM_SEQUENCE] <= signature->param_count) /* skip return param spec and bounds check*/ + names [cols [MONO_PARAM_SEQUENCE] - 1] = mono_metadata_string_heap (klass->image, cols [MONO_PARAM_NAME]); + } + } +} + +/** + * mono_method_get_param_token: + */ +guint32 +mono_method_get_param_token (MonoMethod *method, int index) +{ + MonoClass *klass = method->klass; + MonoTableInfo *methodt; + guint32 idx; + + mono_class_init (klass); + + if (image_is_dynamic (klass->image)) + g_assert_not_reached (); + + methodt = &klass->image->tables [MONO_TABLE_METHOD]; + idx = mono_method_get_index (method); + if (idx > 0) { + guint param_index = mono_metadata_decode_row_col (methodt, idx - 1, MONO_METHOD_PARAMLIST); + + if (index == -1) + /* Return value */ + return mono_metadata_make_token (MONO_TABLE_PARAM, 0); + else + return mono_metadata_make_token (MONO_TABLE_PARAM, param_index + index); + } + + return 0; +} + +/** + * mono_method_get_marshal_info: + */ +void +mono_method_get_marshal_info (MonoMethod *method, MonoMarshalSpec **mspecs) +{ + int i, lastp; + MonoClass *klass = method->klass; + MonoTableInfo *methodt; + MonoTableInfo *paramt; + MonoMethodSignature *signature; + guint32 idx; + + signature = mono_method_signature (method); + g_assert (signature); /*FIXME there is no way to signal error from this function*/ + + for (i = 0; i < signature->param_count + 1; ++i) + mspecs [i] = NULL; + + if (image_is_dynamic (method->klass->image)) { + MonoReflectionMethodAux *method_aux = + (MonoReflectionMethodAux *)g_hash_table_lookup ( + ((MonoDynamicImage*)method->klass->image)->method_aux_hash, method); + if (method_aux && method_aux->param_marshall) { + MonoMarshalSpec **dyn_specs = method_aux->param_marshall; + for (i = 0; i < signature->param_count + 1; ++i) + if (dyn_specs [i]) { + mspecs [i] = g_new0 (MonoMarshalSpec, 1); + memcpy (mspecs [i], dyn_specs [i], sizeof (MonoMarshalSpec)); + mspecs [i]->data.custom_data.custom_name = g_strdup (dyn_specs [i]->data.custom_data.custom_name); + mspecs [i]->data.custom_data.cookie = g_strdup (dyn_specs [i]->data.custom_data.cookie); + } + } + return; + } + + /* dynamic method added to non-dynamic image */ + if (method->dynamic) + return; + + mono_class_init (klass); + + methodt = &klass->image->tables [MONO_TABLE_METHOD]; + paramt = &klass->image->tables [MONO_TABLE_PARAM]; + idx = mono_method_get_index (method); + if (idx > 0) { + guint32 cols [MONO_PARAM_SIZE]; + guint param_index = mono_metadata_decode_row_col (methodt, idx - 1, MONO_METHOD_PARAMLIST); + + if (idx < methodt->rows) + lastp = mono_metadata_decode_row_col (methodt, idx, MONO_METHOD_PARAMLIST); + else + lastp = paramt->rows + 1; + + for (i = param_index; i < lastp; ++i) { + mono_metadata_decode_row (paramt, i -1, cols, MONO_PARAM_SIZE); + + if (cols [MONO_PARAM_FLAGS] & PARAM_ATTRIBUTE_HAS_FIELD_MARSHAL && cols [MONO_PARAM_SEQUENCE] <= signature->param_count) { + const char *tp; + tp = mono_metadata_get_marshal_info (klass->image, i - 1, FALSE); + g_assert (tp); + mspecs [cols [MONO_PARAM_SEQUENCE]]= mono_metadata_parse_marshal_spec (klass->image, tp); + } + } + + return; + } +} + +/** + * mono_method_has_marshal_info: + */ +gboolean +mono_method_has_marshal_info (MonoMethod *method) +{ + int i, lastp; + MonoClass *klass = method->klass; + MonoTableInfo *methodt; + MonoTableInfo *paramt; + guint32 idx; + + if (image_is_dynamic (method->klass->image)) { + MonoReflectionMethodAux *method_aux = + (MonoReflectionMethodAux *)g_hash_table_lookup ( + ((MonoDynamicImage*)method->klass->image)->method_aux_hash, method); + MonoMarshalSpec **dyn_specs = method_aux->param_marshall; + if (dyn_specs) { + for (i = 0; i < mono_method_signature (method)->param_count + 1; ++i) + if (dyn_specs [i]) + return TRUE; + } + return FALSE; + } + + mono_class_init (klass); + + methodt = &klass->image->tables [MONO_TABLE_METHOD]; + paramt = &klass->image->tables [MONO_TABLE_PARAM]; + idx = mono_method_get_index (method); + if (idx > 0) { + guint32 cols [MONO_PARAM_SIZE]; + guint param_index = mono_metadata_decode_row_col (methodt, idx - 1, MONO_METHOD_PARAMLIST); + + if (idx + 1 < methodt->rows) + lastp = mono_metadata_decode_row_col (methodt, idx, MONO_METHOD_PARAMLIST); + else + lastp = paramt->rows + 1; + + for (i = param_index; i < lastp; ++i) { + mono_metadata_decode_row (paramt, i -1, cols, MONO_PARAM_SIZE); + + if (cols [MONO_PARAM_FLAGS] & PARAM_ATTRIBUTE_HAS_FIELD_MARSHAL) + return TRUE; + } + return FALSE; + } + return FALSE; +} + +gpointer +mono_method_get_wrapper_data (MonoMethod *method, guint32 id) +{ + void **data; + g_assert (method != NULL); + g_assert (method->wrapper_type != MONO_WRAPPER_NONE); + + data = (void **)((MonoMethodWrapper *)method)->method_data; + g_assert (data != NULL); + g_assert (id <= GPOINTER_TO_UINT (*data)); + return data [id]; +} + +typedef struct { + MonoStackWalk func; + gpointer user_data; +} StackWalkUserData; + +static gboolean +stack_walk_adapter (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data) +{ + StackWalkUserData *d = (StackWalkUserData *)data; + + switch (frame->type) { + case FRAME_TYPE_DEBUGGER_INVOKE: + case FRAME_TYPE_MANAGED_TO_NATIVE: + case FRAME_TYPE_TRAMPOLINE: + return FALSE; + case FRAME_TYPE_MANAGED: + g_assert (frame->ji); + return d->func (frame->actual_method, frame->native_offset, frame->il_offset, frame->managed, d->user_data); + break; + default: + g_assert_not_reached (); + return FALSE; + } +} + +void +mono_stack_walk (MonoStackWalk func, gpointer user_data) +{ + StackWalkUserData ud = { func, user_data }; + mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (stack_walk_adapter, NULL, MONO_UNWIND_LOOKUP_ALL, &ud); +} + +/** + * mono_stack_walk_no_il: + */ +void +mono_stack_walk_no_il (MonoStackWalk func, gpointer user_data) +{ + StackWalkUserData ud = { func, user_data }; + mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (stack_walk_adapter, NULL, MONO_UNWIND_DEFAULT, &ud); +} + +typedef struct { + MonoStackWalkAsyncSafe func; + gpointer user_data; +} AsyncStackWalkUserData; + + +static gboolean +async_stack_walk_adapter (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data) +{ + AsyncStackWalkUserData *d = (AsyncStackWalkUserData *)data; + + switch (frame->type) { + case FRAME_TYPE_DEBUGGER_INVOKE: + case FRAME_TYPE_MANAGED_TO_NATIVE: + case FRAME_TYPE_TRAMPOLINE: + return FALSE; + case FRAME_TYPE_MANAGED: + if (!frame->ji) + return FALSE; + if (frame->ji->async) { + return d->func (NULL, frame->domain, frame->ji->code_start, frame->native_offset, d->user_data); + } else { + return d->func (frame->actual_method, frame->domain, frame->ji->code_start, frame->native_offset, d->user_data); + } + break; + default: + g_assert_not_reached (); + return FALSE; + } +} + + +/** + * mono_stack_walk_async_safe: + * Async safe version callable from signal handlers. + */ +void +mono_stack_walk_async_safe (MonoStackWalkAsyncSafe func, void *initial_sig_context, void *user_data) +{ + MonoContext ctx; + AsyncStackWalkUserData ud = { func, user_data }; + + mono_sigctx_to_monoctx (initial_sig_context, &ctx); + mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (async_stack_walk_adapter, &ctx, MONO_UNWIND_SIGNAL_SAFE, &ud); +} + +static gboolean +last_managed (MonoMethod *m, gint no, gint ilo, gboolean managed, gpointer data) +{ + MonoMethod **dest = (MonoMethod **)data; + *dest = m; + /*g_print ("In %s::%s [%d] [%d]\n", m->klass->name, m->name, no, ilo);*/ + + return managed; +} + +/** + * mono_method_get_last_managed: + */ +MonoMethod* +mono_method_get_last_managed (void) +{ + MonoMethod *m = NULL; + mono_stack_walk_no_il (last_managed, &m); + return m; +} + +static gboolean loader_lock_track_ownership = FALSE; + +/** + * mono_loader_lock: + * + * See \c docs/thread-safety.txt for the locking strategy. + */ +void +mono_loader_lock (void) +{ + mono_locks_coop_acquire (&loader_mutex, LoaderLock); + if (G_UNLIKELY (loader_lock_track_ownership)) { + mono_native_tls_set_value (loader_lock_nest_id, GUINT_TO_POINTER (GPOINTER_TO_UINT (mono_native_tls_get_value (loader_lock_nest_id)) + 1)); + } +} + +/** + * mono_loader_unlock: + */ +void +mono_loader_unlock (void) +{ + mono_locks_coop_release (&loader_mutex, LoaderLock); + if (G_UNLIKELY (loader_lock_track_ownership)) { + mono_native_tls_set_value (loader_lock_nest_id, GUINT_TO_POINTER (GPOINTER_TO_UINT (mono_native_tls_get_value (loader_lock_nest_id)) - 1)); + } +} + +/* + * mono_loader_lock_track_ownership: + * + * Set whenever the runtime should track ownership of the loader lock. If set to TRUE, + * the mono_loader_lock_is_owned_by_self () can be called to query whenever the current + * thread owns the loader lock. + */ +void +mono_loader_lock_track_ownership (gboolean track) +{ + loader_lock_track_ownership = track; +} + +/* + * mono_loader_lock_is_owned_by_self: + * + * Return whenever the current thread owns the loader lock. + * This is useful to avoid blocking operations while holding the loader lock. + */ +gboolean +mono_loader_lock_is_owned_by_self (void) +{ + g_assert (loader_lock_track_ownership); + + return GPOINTER_TO_UINT (mono_native_tls_get_value (loader_lock_nest_id)) > 0; +} + +/* + * mono_loader_lock_if_inited: + * + * Acquire the loader lock if it has been initialized, no-op otherwise. This can + * be used in runtime initialization code which can be executed before mono_loader_init (). + */ +void +mono_loader_lock_if_inited (void) +{ + if (loader_lock_inited) + mono_loader_lock (); +} + +void +mono_loader_unlock_if_inited (void) +{ + if (loader_lock_inited) + mono_loader_unlock (); +} + +/** + * mono_method_signature_checked: + * + * Return the signature of the method M. On failure, returns NULL, and ERR is set. + */ +MonoMethodSignature* +mono_method_signature_checked (MonoMethod *m, MonoError *error) +{ + int idx; + MonoImage* img; + const char *sig; + gboolean can_cache_signature; + MonoGenericContainer *container; + MonoMethodSignature *signature = NULL, *sig2; + guint32 sig_offset; + + /* We need memory barriers below because of the double-checked locking pattern */ + + error_init (error); + + if (m->signature) + return m->signature; + + img = m->klass->image; + + if (m->is_inflated) { + MonoMethodInflated *imethod = (MonoMethodInflated *) m; + /* the lock is recursive */ + signature = mono_method_signature (imethod->declaring); + signature = inflate_generic_signature_checked (imethod->declaring->klass->image, signature, mono_method_get_context (m), error); + if (!mono_error_ok (error)) + return NULL; + + mono_atomic_fetch_add_i32 (&inflated_signatures_size, mono_metadata_signature_size (signature)); + + mono_image_lock (img); + + mono_memory_barrier (); + if (!m->signature) + m->signature = signature; + + mono_image_unlock (img); + + return m->signature; + } + + g_assert (mono_metadata_token_table (m->token) == MONO_TABLE_METHOD); + idx = mono_metadata_token_index (m->token); + + sig = mono_metadata_blob_heap (img, sig_offset = mono_metadata_decode_row_col (&img->tables [MONO_TABLE_METHOD], idx - 1, MONO_METHOD_SIGNATURE)); + + g_assert (!mono_class_is_ginst (m->klass)); + container = mono_method_get_generic_container (m); + if (!container) + container = mono_class_try_get_generic_container (m->klass); + + /* Generic signatures depend on the container so they cannot be cached */ + /* icall/pinvoke signatures cannot be cached cause we modify them below */ + can_cache_signature = !(m->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) && !(m->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) && !container; + + /* If the method has parameter attributes, that can modify the signature */ + if (mono_metadata_method_has_param_attrs (img, idx)) + can_cache_signature = FALSE; + + if (can_cache_signature) { + mono_image_lock (img); + signature = (MonoMethodSignature *)g_hash_table_lookup (img->method_signatures, sig); + mono_image_unlock (img); + } + + if (!signature) { + const char *sig_body; + /*TODO we should cache the failure result somewhere*/ + if (!mono_verifier_verify_method_signature (img, sig_offset, error)) + return NULL; + + /* size = */ mono_metadata_decode_blob_size (sig, &sig_body); + + signature = mono_metadata_parse_method_signature_full (img, container, idx, sig_body, NULL, error); + if (!signature) + return NULL; + + if (can_cache_signature) { + mono_image_lock (img); + sig2 = (MonoMethodSignature *)g_hash_table_lookup (img->method_signatures, sig); + if (!sig2) + g_hash_table_insert (img->method_signatures, (gpointer)sig, signature); + mono_image_unlock (img); + } + + mono_atomic_fetch_add_i32 (&signatures_size, mono_metadata_signature_size (signature)); + } + + /* Verify metadata consistency */ + if (signature->generic_param_count) { + if (!container || !container->is_method) { + mono_error_set_method_load (error, m->klass, g_strdup (m->name), mono_signature_get_managed_fmt_string (signature), "Signature claims method has generic parameters, but generic_params table says it doesn't for method 0x%08x from image %s", idx, img->name); + return NULL; + } + if (container->type_argc != signature->generic_param_count) { + mono_error_set_method_load (error, m->klass, g_strdup (m->name), mono_signature_get_managed_fmt_string (signature), "Inconsistent generic parameter count. Signature says %d, generic_params table says %d for method 0x%08x from image %s", signature->generic_param_count, container->type_argc, idx, img->name); + return NULL; + } + } else if (container && container->is_method && container->type_argc) { + mono_error_set_method_load (error, m->klass, g_strdup (m->name), mono_signature_get_managed_fmt_string (signature), "generic_params table claims method has generic parameters, but signature says it doesn't for method 0x%08x from image %s", idx, img->name); + return NULL; + } + if (m->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) { + signature->pinvoke = 1; +#ifdef TARGET_WIN32 + /* + * On Windows the default pinvoke calling convention is STDCALL but + * we need CDECL since this is actually an icall. + */ + signature->call_convention = MONO_CALL_C; +#endif + } else if (m->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) { + MonoCallConvention conv = (MonoCallConvention)0; + MonoMethodPInvoke *piinfo = (MonoMethodPInvoke *)m; + signature->pinvoke = 1; + + switch (piinfo->piflags & PINVOKE_ATTRIBUTE_CALL_CONV_MASK) { + case 0: /* no call conv, so using default */ + case PINVOKE_ATTRIBUTE_CALL_CONV_WINAPI: + conv = MONO_CALL_DEFAULT; + break; + case PINVOKE_ATTRIBUTE_CALL_CONV_CDECL: + conv = MONO_CALL_C; + break; + case PINVOKE_ATTRIBUTE_CALL_CONV_STDCALL: + conv = MONO_CALL_STDCALL; + break; + case PINVOKE_ATTRIBUTE_CALL_CONV_THISCALL: + conv = MONO_CALL_THISCALL; + break; + case PINVOKE_ATTRIBUTE_CALL_CONV_FASTCALL: + conv = MONO_CALL_FASTCALL; + break; + case PINVOKE_ATTRIBUTE_CALL_CONV_GENERIC: + case PINVOKE_ATTRIBUTE_CALL_CONV_GENERICINST: + default: { + mono_error_set_method_load (error, m->klass, g_strdup (m->name), mono_signature_get_managed_fmt_string (signature), "unsupported calling convention : 0x%04x for method 0x%08x from image %s", piinfo->piflags, idx, img->name); + } + return NULL; + } + signature->call_convention = conv; + } + + mono_image_lock (img); + + mono_memory_barrier (); + if (!m->signature) + m->signature = signature; + + mono_image_unlock (img); + + return m->signature; +} + +/** + * mono_method_signature: + * \returns the signature of the method \p m. On failure, returns NULL. + */ +MonoMethodSignature* +mono_method_signature (MonoMethod *m) +{ + MonoError error; + MonoMethodSignature *sig; + + sig = mono_method_signature_checked (m, &error); + if (!sig) { + char *type_name = mono_type_get_full_name (m->klass); + g_warning ("Could not load signature of %s:%s due to: %s", type_name, m->name, mono_error_get_message (&error)); + g_free (type_name); + mono_error_cleanup (&error); + } + + return sig; +} + +/** + * mono_method_get_name: + */ +const char* +mono_method_get_name (MonoMethod *method) +{ + return method->name; +} + +/** + * mono_method_get_class: + */ +MonoClass* +mono_method_get_class (MonoMethod *method) +{ + return method->klass; +} + +/** + * mono_method_get_token: + */ +guint32 +mono_method_get_token (MonoMethod *method) +{ + return method->token; +} + +MonoMethodHeader* +mono_method_get_header_checked (MonoMethod *method, MonoError *error) +{ + int idx; + guint32 rva; + MonoImage* img; + gpointer loc; + MonoGenericContainer *container; + + error_init (error); + img = method->klass->image; + + 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)) { + mono_error_set_bad_image (error, img, "Method has no body"); + return NULL; + } + + if (method->is_inflated) { + MonoMethodInflated *imethod = (MonoMethodInflated *) method; + MonoMethodHeader *header, *iheader; + + header = mono_method_get_header_checked (imethod->declaring, error); + if (!header) + return NULL; + + iheader = inflate_generic_header (header, mono_method_get_context (method), error); + mono_metadata_free_mh (header); + if (!iheader) { + return NULL; + } + + return iheader; + } + + if (method->wrapper_type != MONO_WRAPPER_NONE || method->sre_method) { + MonoMethodWrapper *mw = (MonoMethodWrapper *)method; + g_assert (mw->header); + return mw->header; + } + + /* + * We don't need locks here: the new header is allocated from malloc memory + * and is not stored anywhere in the runtime, the user needs to free it. + */ + g_assert (mono_metadata_token_table (method->token) == MONO_TABLE_METHOD); + idx = mono_metadata_token_index (method->token); + rva = mono_metadata_decode_row_col (&img->tables [MONO_TABLE_METHOD], idx - 1, MONO_METHOD_RVA); + + if (!mono_verifier_verify_method_header (img, rva, NULL)) { + mono_error_set_bad_image (error, img, "Invalid method header, failed verification"); + return NULL; + } + + loc = mono_image_rva_map (img, rva); + if (!loc) { + mono_error_set_bad_image (error, img, "Method has zero rva"); + return NULL; + } + + /* + * When parsing the types of local variables, we must pass any container available + * to ensure that both VAR and MVAR will get the right owner. + */ + container = mono_method_get_generic_container (method); + if (!container) + container = mono_class_try_get_generic_container (method->klass); + return mono_metadata_parse_mh_full (img, container, (const char *)loc, error); +} + +/** + * mono_method_get_header: + */ +MonoMethodHeader* +mono_method_get_header (MonoMethod *method) +{ + MonoError error; + MonoMethodHeader *header = mono_method_get_header_checked (method, &error); + mono_error_cleanup (&error); + return header; +} + + +/** + * mono_method_get_flags: + */ +guint32 +mono_method_get_flags (MonoMethod *method, guint32 *iflags) +{ + if (iflags) + *iflags = method->iflags; + return method->flags; +} + +/** + * mono_method_get_index: + * Find the method index in the metadata \c MethodDef table. + */ +guint32 +mono_method_get_index (MonoMethod *method) +{ + MonoClass *klass = method->klass; + int i; + + if (klass->rank) + /* constructed array methods are not in the MethodDef table */ + return 0; + + if (method->token) + return mono_metadata_token_index (method->token); + + mono_class_setup_methods (klass); + if (mono_class_has_failure (klass)) + return 0; + int first_idx = mono_class_get_first_method_idx (klass); + int mcount = mono_class_get_method_count (klass); + for (i = 0; i < mcount; ++i) { + if (method == klass->methods [i]) { + if (klass->image->uncompressed_metadata) + return mono_metadata_translate_token_index (klass->image, MONO_TABLE_METHOD, first_idx + i + 1); + else + return first_idx + i + 1; + } + } + return 0; +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/loader.h b/unity-2019.4.24f1-mbe/mono/metadata/loader.h new file mode 100644 index 000000000..0891613e4 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/loader.h @@ -0,0 +1,108 @@ +/** + * \file + */ + +#ifndef _MONO_METADATA_LOADER_H_ +#define _MONO_METADATA_LOADER_H_ 1 + +#include +#include +#include + +MONO_BEGIN_DECLS + +typedef mono_bool (*MonoStackWalk) (MonoMethod *method, int32_t native_offset, int32_t il_offset, mono_bool managed, void* data); + +MONO_RT_EXTERNAL_ONLY MONO_API MonoMethod * +mono_get_method (MonoImage *image, uint32_t token, MonoClass *klass); + +MONO_RT_EXTERNAL_ONLY MONO_API MonoMethod * +mono_get_method_full (MonoImage *image, uint32_t token, MonoClass *klass, + MonoGenericContext *context); + +MONO_RT_EXTERNAL_ONLY MONO_API MonoMethod * +mono_get_method_constrained (MonoImage *image, uint32_t token, MonoClass *constrained_class, + MonoGenericContext *context, MonoMethod **cil_method); + +MONO_API void +mono_free_method (MonoMethod *method); + +MONO_RT_EXTERNAL_ONLY MONO_API MonoMethodSignature* +mono_method_get_signature_full (MonoMethod *method, MonoImage *image, uint32_t token, + MonoGenericContext *context); + +MONO_RT_EXTERNAL_ONLY MONO_API MonoMethodSignature* +mono_method_get_signature (MonoMethod *method, MonoImage *image, uint32_t token); + +MONO_API MonoMethodSignature* +mono_method_signature (MonoMethod *method); + +MONO_RT_EXTERNAL_ONLY MONO_API MonoMethodHeader* +mono_method_get_header (MonoMethod *method); + +MONO_API const char* +mono_method_get_name (MonoMethod *method); + +MONO_API MonoClass* +mono_method_get_class (MonoMethod *method); + +MONO_API uint32_t +mono_method_get_token (MonoMethod *method); + +MONO_API uint32_t +mono_method_get_flags (MonoMethod *method, uint32_t *iflags); + +MONO_API uint32_t +mono_method_get_index (MonoMethod *method); + +MONO_API void +mono_add_internal_call (const char *name, const void* method); + +MONO_API void* +mono_lookup_internal_call (MonoMethod *method); + +void* +mono_lookup_internal_call_full (MonoMethod *method, mono_bool *uses_handles); + +MONO_API const char* +mono_lookup_icall_symbol (MonoMethod *m); + +MONO_API void +mono_dllmap_insert (MonoImage *assembly, const char *dll, const char *func, const char *tdll, const char *tfunc); + +MONO_API void* +mono_lookup_pinvoke_call (MonoMethod *method, const char **exc_class, const char **exc_arg); + +MONO_API void +mono_method_get_param_names (MonoMethod *method, const char **names); + +MONO_API uint32_t +mono_method_get_param_token (MonoMethod *method, int idx); + +MONO_API void +mono_method_get_marshal_info (MonoMethod *method, MonoMarshalSpec **mspecs); + +MONO_API mono_bool +mono_method_has_marshal_info (MonoMethod *method); + +MONO_API MonoMethod* +mono_method_get_last_managed (void); + +MONO_API void +mono_stack_walk (MonoStackWalk func, void* user_data); + +/* Use this if the IL offset is not needed: it's faster */ +MONO_API void +mono_stack_walk_no_il (MonoStackWalk func, void* user_data); + +typedef mono_bool (*MonoStackWalkAsyncSafe) (MonoMethod *method, MonoDomain *domain, void *base_address, int offset, void* data); +MONO_API void +mono_stack_walk_async_safe (MonoStackWalkAsyncSafe func, void *initial_sig_context, void* user_data); + +MONO_API MonoMethodHeader* +mono_method_get_header_checked (MonoMethod *method, MonoError *error); + +MONO_END_DECLS + +#endif + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/locales.c b/unity-2019.4.24f1-mbe/mono/metadata/locales.c new file mode 100644 index 000000000..2ef0d2145 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/locales.c @@ -0,0 +1,992 @@ +/** + * \file + * Culture-sensitive handling + * + * Authors: + * Dick Porter (dick@ximian.com) + * Mohammad DAMT (mdamt@cdl2000.com) + * Marek Safar (marek.safar@gmail.com) + * + * Copyright 2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * (C) 2003 PT Cakram Datalingga Duaribu http://www.cdl2000.com + * Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef DISABLE_NORMALIZATION +#include +#endif + +#include +#if defined(__APPLE__) +#include +#endif + +#undef DEBUG + +static gint32 string_invariant_compare_char (gunichar2 c1, gunichar2 c2, + gint32 options); +static gint32 string_invariant_compare (MonoString *str1, gint32 off1, + gint32 len1, MonoString *str2, + gint32 off2, gint32 len2, + gint32 options); +static gint32 string_invariant_indexof (MonoString *source, gint32 sindex, + gint32 count, MonoString *value, + MonoBoolean first); +static gint32 string_invariant_indexof_char (MonoString *source, gint32 sindex, + gint32 count, gunichar2 value, + MonoBoolean first); + +static const CultureInfoEntry* culture_info_entry_from_lcid (int lcid); + +static const RegionInfoEntry* region_info_entry_from_lcid (int lcid); + +/* Lazy class loading functions */ +static GENERATE_GET_CLASS_WITH_CACHE (culture_info, "System.Globalization", "CultureInfo") + +static int +culture_lcid_locator (const void *a, const void *b) +{ + const int *lcid = (const int *)a; + const CultureInfoEntry *bb = (const CultureInfoEntry *)b; + + return *lcid - bb->lcid; +} + +static int +culture_name_locator (const void *a, const void *b) +{ + const char *aa = (const char *)a; + const CultureInfoNameEntry *bb = (const CultureInfoNameEntry *)b; + int ret; + + ret = strcmp (aa, idx2string (bb->name)); + + return ret; +} + +static int +region_name_locator (const void *a, const void *b) +{ + const char *aa = (const char *)a; + const RegionInfoNameEntry *bb = (const RegionInfoNameEntry *)b; + int ret; + + ret = strcmp (aa, idx2string (bb->name)); + + return ret; +} + +static MonoArray* +create_group_sizes_array (const gint *gs, gint ml, MonoError *error) +{ + MonoArray *ret; + int i, len = 0; + + error_init (error); + + for (i = 0; i < ml; i++) { + if (gs [i] == -1) + break; + len++; + } + + ret = mono_array_new_cached (mono_domain_get (), + mono_get_int32_class (), len, error); + return_val_if_nok (error, NULL); + + for(i = 0; i < len; i++) + mono_array_set (ret, gint32, i, gs [i]); + + return ret; +} + +static MonoArray* +create_names_array_idx (const guint16 *names, int ml, MonoError *error) +{ + MonoArray *ret; + MonoDomain *domain; + int i; + + error_init (error); + + if (names == NULL) + return NULL; + + domain = mono_domain_get (); + + ret = mono_array_new_cached (mono_domain_get (), mono_get_string_class (), ml, error); + return_val_if_nok (error, NULL); + + for(i = 0; i < ml; i++) { + MonoString *s = mono_string_new_checked (domain, dtidx2string (names [i]), error); + return_val_if_nok (error, NULL); + mono_array_setref (ret, i, s); + } + + return ret; +} + +static MonoArray* +create_names_array_idx_dynamic (const guint16 *names, int ml, MonoError *error) +{ + MonoArray *ret; + MonoDomain *domain; + int i, len = 0; + + error_init (error); + + if (names == NULL) + return NULL; + + domain = mono_domain_get (); + + for (i = 0; i < ml; i++) { + if (names [i] == 0) + break; + len++; + } + + ret = mono_array_new_cached (mono_domain_get (), mono_get_string_class (), len, error); + return_val_if_nok (error, NULL); + + for(i = 0; i < len; i++) { + MonoString *s = mono_string_new_checked (domain, pattern2string (names [i]), error); + return_val_if_nok (error, NULL); + mono_array_setref (ret, i, s); + } + + return ret; +} + +MonoBoolean +ves_icall_System_Globalization_CalendarData_fill_calendar_data (MonoCalendarData *this_obj, MonoString *name, gint32 calendar_index) +{ + MonoError error; + MonoDomain *domain; + const DateTimeFormatEntry *dfe; + const CultureInfoNameEntry *ne; + const CultureInfoEntry *ci; + char *n; + + n = mono_string_to_utf8_checked (name, &error); + if (mono_error_set_pending_exception (&error)) + return FALSE; + ne = (const CultureInfoNameEntry *)mono_binary_search (n, culture_name_entries, NUM_CULTURE_ENTRIES, + sizeof (CultureInfoNameEntry), culture_name_locator); + g_free (n); + if (ne == NULL) { + return FALSE; + } + + ci = &culture_entries [ne->culture_entry_index]; + dfe = &datetime_format_entries [ci->datetime_format_index]; + + domain = mono_domain_get (); + + MonoString *native_name = mono_string_new_checked (domain, idx2string (ci->nativename), &error); + return_val_and_set_pending_if_nok (&error, FALSE); + MONO_OBJECT_SETREF (this_obj, NativeName, native_name); + MonoArray *short_date_patterns = create_names_array_idx_dynamic (dfe->short_date_patterns, + NUM_SHORT_DATE_PATTERNS, &error); + return_val_and_set_pending_if_nok (&error, FALSE); + MONO_OBJECT_SETREF (this_obj, ShortDatePatterns, short_date_patterns); + MonoArray *year_month_patterns =create_names_array_idx_dynamic (dfe->year_month_patterns, + NUM_YEAR_MONTH_PATTERNS, &error); + return_val_and_set_pending_if_nok (&error, FALSE); + MONO_OBJECT_SETREF (this_obj, YearMonthPatterns, year_month_patterns); + + MonoArray *long_date_patterns = create_names_array_idx_dynamic (dfe->long_date_patterns, + NUM_LONG_DATE_PATTERNS, &error); + return_val_and_set_pending_if_nok (&error, FALSE); + MONO_OBJECT_SETREF (this_obj, LongDatePatterns, long_date_patterns); + + MonoString *month_day_pattern = mono_string_new_checked (domain, pattern2string (dfe->month_day_pattern), &error); + return_val_and_set_pending_if_nok (&error, FALSE); + MONO_OBJECT_SETREF (this_obj, MonthDayPattern, month_day_pattern); + + MonoArray *day_names = create_names_array_idx (dfe->day_names, NUM_DAYS, &error); + return_val_and_set_pending_if_nok (&error, FALSE); + MONO_OBJECT_SETREF (this_obj, DayNames, day_names); + + MonoArray *abbr_day_names = create_names_array_idx (dfe->abbreviated_day_names, + NUM_DAYS, &error); + return_val_and_set_pending_if_nok (&error, FALSE); + MONO_OBJECT_SETREF (this_obj, AbbreviatedDayNames, abbr_day_names); + + MonoArray *ss_day_names = create_names_array_idx (dfe->shortest_day_names, NUM_DAYS, &error); + return_val_and_set_pending_if_nok (&error, FALSE); + MONO_OBJECT_SETREF (this_obj, SuperShortDayNames, ss_day_names); + + MonoArray *month_names = create_names_array_idx (dfe->month_names, NUM_MONTHS, &error); + return_val_and_set_pending_if_nok (&error, FALSE); + MONO_OBJECT_SETREF (this_obj, MonthNames, month_names); + + MonoArray *abbr_mon_names = create_names_array_idx (dfe->abbreviated_month_names, + NUM_MONTHS, &error); + return_val_and_set_pending_if_nok (&error, FALSE); + MONO_OBJECT_SETREF (this_obj, AbbreviatedMonthNames, abbr_mon_names); + + + MonoArray *gen_month_names = create_names_array_idx (dfe->month_genitive_names, NUM_MONTHS, &error); + return_val_and_set_pending_if_nok (&error, FALSE); + MONO_OBJECT_SETREF (this_obj, GenitiveMonthNames, gen_month_names); + + MonoArray *gen_abbr_mon_names = create_names_array_idx (dfe->abbreviated_month_genitive_names, NUM_MONTHS, &error); + return_val_and_set_pending_if_nok (&error, FALSE); + MONO_OBJECT_SETREF (this_obj, GenitiveAbbreviatedMonthNames, gen_abbr_mon_names); + + return TRUE; +} + +void +ves_icall_System_Globalization_CultureData_fill_culture_data (MonoCultureData *this_obj, gint32 datetime_index) +{ + MonoError error; + MonoDomain *domain; + const DateTimeFormatEntry *dfe; + + g_assert (datetime_index >= 0); + + dfe = &datetime_format_entries [datetime_index]; + + domain = mono_domain_get (); + +#define SET_STR(obj,field,domain,expr,err) do { \ + MonoString *_tmp_str = mono_string_new_checked ((domain), (expr), (err)); \ + if (mono_error_set_pending_exception ((err))) \ + return; \ + MONO_OBJECT_SETREF((obj), field, _tmp_str); \ + } while (0) + + SET_STR (this_obj, AMDesignator, domain, idx2string (dfe->am_designator), &error); + SET_STR (this_obj, PMDesignator, domain, idx2string (dfe->pm_designator), &error); + SET_STR (this_obj, TimeSeparator, domain, idx2string (dfe->time_separator), &error); +#undef SET_STR + + MonoArray *long_time_patterns = create_names_array_idx_dynamic (dfe->long_time_patterns, + NUM_LONG_TIME_PATTERNS, &error); + if (mono_error_set_pending_exception (&error)) + return; + MONO_OBJECT_SETREF (this_obj, LongTimePatterns, long_time_patterns); + + MonoArray *short_time_patterns = create_names_array_idx_dynamic (dfe->short_time_patterns, + NUM_SHORT_TIME_PATTERNS, &error); + if (mono_error_set_pending_exception (&error)) + return; + MONO_OBJECT_SETREF (this_obj, ShortTimePatterns, short_time_patterns); + this_obj->FirstDayOfWeek = dfe->first_day_of_week; + this_obj->CalendarWeekRule = dfe->calendar_week_rule; +} + +void +ves_icall_System_Globalization_CultureData_fill_number_data (MonoNumberFormatInfo* number, gint32 number_index) +{ + MonoError error; + MonoDomain *domain; + const NumberFormatEntry *nfe; + + g_assert (number_index >= 0); + + nfe = &number_format_entries [number_index]; + + domain = mono_domain_get (); + + number->currencyDecimalDigits = nfe->currency_decimal_digits; + +#define SET_STR(obj,field,domain,expr,err) do { \ + MonoString *_tmp_str = mono_string_new_checked ((domain), (expr), (err)); \ + if (mono_error_set_pending_exception ((err))) \ + return; \ + MONO_OBJECT_SETREF((obj), field, _tmp_str); \ + } while (0) + + SET_STR (number, currencyDecimalSeparator, domain, idx2string (nfe->currency_decimal_separator), &error); + SET_STR (number, currencyGroupSeparator, domain, idx2string (nfe->currency_group_separator), &error); + + MonoArray *currency_sizes_arr = create_group_sizes_array (nfe->currency_group_sizes, + GROUP_SIZE, &error); + if (mono_error_set_pending_exception (&error)) + return; + MONO_OBJECT_SETREF (number, currencyGroupSizes, currency_sizes_arr); + number->currencyNegativePattern = nfe->currency_negative_pattern; + number->currencyPositivePattern = nfe->currency_positive_pattern; + + SET_STR (number, currencySymbol, domain, idx2string (nfe->currency_symbol), &error); + SET_STR (number, naNSymbol, domain, idx2string (nfe->nan_symbol), &error); + SET_STR (number, negativeInfinitySymbol, domain, idx2string (nfe->negative_infinity_symbol), &error); + SET_STR (number, negativeSign, domain, idx2string (nfe->negative_sign), &error); + number->numberDecimalDigits = nfe->number_decimal_digits; + SET_STR (number, numberDecimalSeparator, domain, idx2string (nfe->number_decimal_separator), &error); + SET_STR (number, numberGroupSeparator, domain, idx2string (nfe->number_group_separator), &error); + MonoArray *number_sizes_arr = create_group_sizes_array (nfe->number_group_sizes, + GROUP_SIZE, &error); + if (mono_error_set_pending_exception (&error)) + return; + MONO_OBJECT_SETREF (number, numberGroupSizes, number_sizes_arr); + number->numberNegativePattern = nfe->number_negative_pattern; + number->percentNegativePattern = nfe->percent_negative_pattern; + number->percentPositivePattern = nfe->percent_positive_pattern; + SET_STR (number, percentSymbol, domain, idx2string (nfe->percent_symbol), &error); + SET_STR (number, perMilleSymbol, domain, idx2string (nfe->per_mille_symbol), &error); + SET_STR (number, positiveInfinitySymbol, domain, idx2string (nfe->positive_infinity_symbol), &error); + SET_STR (number, positiveSign, domain, idx2string (nfe->positive_sign), &error); +#undef SET_STR +} + +static MonoBoolean +construct_culture (MonoCultureInfo *this_obj, const CultureInfoEntry *ci, MonoError *error) +{ + MonoDomain *domain = mono_domain_get (); + + error_init (error); + + this_obj->lcid = ci->lcid; + +#define SET_STR(obj,field,domain,expr,err) do { \ + MonoString *_tmp_str = mono_string_new_checked ((domain), (expr), (err)); \ + return_val_if_nok (err, FALSE); \ + MONO_OBJECT_SETREF((obj), field, _tmp_str); \ + } while (0) + + SET_STR (this_obj, name, domain, idx2string (ci->name), error); + SET_STR (this_obj, englishname, domain, idx2string (ci->englishname), error); + SET_STR (this_obj, nativename, domain, idx2string (ci->nativename), error); + SET_STR (this_obj, win3lang, domain, idx2string (ci->win3lang), error); + SET_STR (this_obj, iso3lang, domain, idx2string (ci->iso3lang), error); + SET_STR (this_obj, iso2lang, domain, idx2string (ci->iso2lang), error); + + // It's null for neutral cultures + if (ci->territory > 0) { + SET_STR (this_obj, territory, domain, idx2string (ci->territory), error); + } + + MonoArray *native_calendar_names = create_names_array_idx (ci->native_calendar_names, NUM_CALENDARS, error); + return_val_if_nok (error, FALSE); + MONO_OBJECT_SETREF (this_obj, native_calendar_names, native_calendar_names); + this_obj->parent_lcid = ci->parent_lcid; + this_obj->datetime_index = ci->datetime_format_index; + this_obj->number_index = ci->number_format_index; + this_obj->calendar_type = ci->calendar_type; + this_obj->text_info_data = &ci->text_info; +#undef SET_STR + + return TRUE; +} + +static MonoBoolean +construct_region (MonoRegionInfo *this_obj, const RegionInfoEntry *ri, MonoError *error) +{ + MonoDomain *domain = mono_domain_get (); + + error_init (error); + +#define SET_STR(obj,field,domain,expr,err) do { \ + MonoString *_tmp_str = mono_string_new_checked ((domain), (expr), (err)); \ + return_val_if_nok (err, FALSE); \ + MONO_OBJECT_SETREF((obj), field, _tmp_str); \ + } while (0) + + this_obj->geo_id = ri->geo_id; + SET_STR (this_obj, iso2name, domain, idx2string (ri->iso2name), error); + SET_STR (this_obj, iso3name, domain, idx2string (ri->iso3name), error); + SET_STR (this_obj, win3name, domain, idx2string (ri->win3name), error); + SET_STR (this_obj, english_name, domain, idx2string (ri->english_name), error); + SET_STR (this_obj, native_name, domain, idx2string (ri->native_name), error); + SET_STR (this_obj, currency_symbol, domain, idx2string (ri->currency_symbol), error); + SET_STR (this_obj, iso_currency_symbol, domain, idx2string (ri->iso_currency_symbol), error); + SET_STR (this_obj, currency_english_name, domain, idx2string (ri->currency_english_name), error); + SET_STR (this_obj, currency_native_name, domain, idx2string (ri->currency_native_name), error); + +#undef SET_STR + + return TRUE; +} + +static const CultureInfoEntry* +culture_info_entry_from_lcid (int lcid) +{ + const CultureInfoEntry *ci; + + ci = (const CultureInfoEntry *)mono_binary_search (&lcid, culture_entries, NUM_CULTURE_ENTRIES, sizeof (CultureInfoEntry), culture_lcid_locator); + + return ci; +} + +static const RegionInfoEntry* +region_info_entry_from_lcid (int lcid) +{ + const RegionInfoEntry *entry; + const CultureInfoEntry *ne; + + ne = (const CultureInfoEntry *)mono_binary_search (&lcid, culture_entries, NUM_CULTURE_ENTRIES, sizeof (CultureInfoEntry), culture_lcid_locator); + + if (ne == NULL) + return FALSE; + + entry = ®ion_entries [ne->region_entry_index]; + + return entry; +} + +#if defined (__APPLE__) +static gchar* +get_darwin_locale (void) +{ + static gchar *cached_locale = NULL; + gchar *darwin_locale = NULL; + CFLocaleRef locale = NULL; + CFStringRef locale_language = NULL; + CFStringRef locale_country = NULL; + CFStringRef locale_script = NULL; + CFStringRef locale_cfstr = NULL; + CFIndex bytes_converted; + CFIndex bytes_written; + CFIndex len; + int i; + + if (cached_locale != NULL) + return g_strdup (cached_locale); + + locale = CFLocaleCopyCurrent (); + + if (locale) { + locale_language = CFLocaleGetValue (locale, kCFLocaleLanguageCode); + if (locale_language != NULL && CFStringGetBytes(locale_language, CFRangeMake (0, CFStringGetLength (locale_language)), kCFStringEncodingMacRoman, 0, FALSE, NULL, 0, &bytes_converted) > 0) { + len = bytes_converted + 1; + + locale_country = CFLocaleGetValue (locale, kCFLocaleCountryCode); + if (locale_country != NULL && CFStringGetBytes (locale_country, CFRangeMake (0, CFStringGetLength (locale_country)), kCFStringEncodingMacRoman, 0, FALSE, NULL, 0, &bytes_converted) > 0) { + len += bytes_converted + 1; + + locale_script = CFLocaleGetValue (locale, kCFLocaleScriptCode); + if (locale_script != NULL && CFStringGetBytes (locale_script, CFRangeMake (0, CFStringGetLength (locale_script)), kCFStringEncodingMacRoman, 0, FALSE, NULL, 0, &bytes_converted) > 0) { + len += bytes_converted + 1; + } + + darwin_locale = (char *) g_malloc (len + 1); + CFStringGetBytes (locale_language, CFRangeMake (0, CFStringGetLength (locale_language)), kCFStringEncodingMacRoman, 0, FALSE, (UInt8 *) darwin_locale, len, &bytes_converted); + + darwin_locale[bytes_converted] = '-'; + bytes_written = bytes_converted + 1; + if (locale_script != NULL && CFStringGetBytes (locale_script, CFRangeMake (0, CFStringGetLength (locale_script)), kCFStringEncodingMacRoman, 0, FALSE, (UInt8 *) &darwin_locale[bytes_written], len - bytes_written, &bytes_converted) > 0) { + darwin_locale[bytes_written + bytes_converted] = '-'; + bytes_written += bytes_converted + 1; + } + + CFStringGetBytes (locale_country, CFRangeMake (0, CFStringGetLength (locale_country)), kCFStringEncodingMacRoman, 0, FALSE, (UInt8 *) &darwin_locale[bytes_written], len - bytes_written, &bytes_converted); + darwin_locale[bytes_written + bytes_converted] = '\0'; + } + } + + if (darwin_locale == NULL) { + locale_cfstr = CFLocaleGetIdentifier (locale); + + if (locale_cfstr) { + len = CFStringGetMaximumSizeForEncoding (CFStringGetLength (locale_cfstr), kCFStringEncodingMacRoman) + 1; + darwin_locale = (char *) g_malloc (len); + if (!CFStringGetCString (locale_cfstr, darwin_locale, len, kCFStringEncodingMacRoman)) { + g_free (darwin_locale); + CFRelease (locale); + cached_locale = NULL; + return NULL; + } + + for (i = 0; i < strlen (darwin_locale); i++) + if (darwin_locale [i] == '_') + darwin_locale [i] = '-'; + } + } + + CFRelease (locale); + } + + mono_memory_barrier (); + cached_locale = darwin_locale; + return g_strdup (cached_locale); +} +#endif + +static char * +get_posix_locale (void) +{ + char *locale; + + locale = g_getenv ("LC_ALL"); + if (locale == NULL) { + locale = g_getenv ("LANG"); + if (locale == NULL) { + char *static_locale = setlocale (LC_ALL, NULL); + if (static_locale) + locale = g_strdup (static_locale); + } + } + if (locale == NULL) + return NULL; + + /* Skip English-only locale 'C' */ + if (strcmp (locale, "C") == 0) { + g_free (locale); + return NULL; + } + + return locale; +} + + +static gchar * +get_current_locale_name (void) +{ + char *locale; + char *p, *ret; + +#ifdef HOST_WIN32 + locale = g_win32_getlocale (); +#elif defined (__APPLE__) + locale = get_darwin_locale (); + if (!locale) + locale = get_posix_locale (); +#else + locale = get_posix_locale (); +#endif + + if (locale == NULL) + return NULL; + + p = strchr (locale, '.'); + if (p != NULL) + *p = 0; + p = strchr (locale, '@'); + if (p != NULL) + *p = 0; + p = strchr (locale, '_'); + if (p != NULL) + *p = '-'; + + ret = g_ascii_strdown (locale, -1); + g_free (locale); + + return ret; +} + +MonoStringHandle +ves_icall_System_Globalization_CultureInfo_get_current_locale_name (MonoError *error) +{ + error_init (error); + gchar *locale; + MonoDomain *domain; + + locale = get_current_locale_name (); + if (locale == NULL) + return MONO_HANDLE_CAST (MonoString, NULL_HANDLE); + + domain = mono_domain_get (); + MonoStringHandle ret = mono_string_new_handle (domain, locale, error); + g_free (locale); + + return ret; +} + +MonoBoolean +ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_lcid (MonoCultureInfo *this_obj, + gint lcid) +{ + MonoError error; + const CultureInfoEntry *ci; + + ci = culture_info_entry_from_lcid (lcid); + if(ci == NULL) + return FALSE; + + if (!construct_culture (this_obj, ci, &error)) { + mono_error_set_pending_exception (&error); + return FALSE; + } + return TRUE; +} + +MonoBoolean +ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_name (MonoCultureInfo *this_obj, + MonoString *name) +{ + MonoError error; + const CultureInfoNameEntry *ne; + char *n; + + n = mono_string_to_utf8_checked (name, &error); + if (mono_error_set_pending_exception (&error)) + return FALSE; + ne = (const CultureInfoNameEntry *)mono_binary_search (n, culture_name_entries, NUM_CULTURE_ENTRIES, + sizeof (CultureInfoNameEntry), culture_name_locator); + + if (ne == NULL) { + /*g_print ("ne (%s) is null\n", n);*/ + g_free (n); + return FALSE; + } + g_free (n); + + if (!construct_culture (this_obj, &culture_entries [ne->culture_entry_index], &error)) { + mono_error_set_pending_exception (&error); + return FALSE; + } + return TRUE; +} +/* +MonoBoolean +ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_specific_name (MonoCultureInfo *ci, + MonoString *name) +{ + gchar *locale; + gboolean ret; + + locale = mono_string_to_utf8 (name); + ret = construct_culture_from_specific_name (ci, locale); + g_free (locale); + + return ret; +} +*/ +MonoBoolean +ves_icall_System_Globalization_RegionInfo_construct_internal_region_from_lcid (MonoRegionInfo *this_obj, + gint lcid) +{ + MonoError error; + const RegionInfoEntry *ri; + + ri = region_info_entry_from_lcid (lcid); + if(ri == NULL) + return FALSE; + + MonoBoolean result = construct_region (this_obj, ri, &error); + mono_error_set_pending_exception (&error); + return result; +} + +MonoBoolean +ves_icall_System_Globalization_RegionInfo_construct_internal_region_from_name (MonoRegionInfo *this_obj, + MonoString *name) +{ + MonoError error; + const RegionInfoNameEntry *ne; + char *n; + + n = mono_string_to_utf8_checked (name, &error); + if (mono_error_set_pending_exception (&error)) + return FALSE; + ne = (const RegionInfoNameEntry *)mono_binary_search (n, region_name_entries, NUM_REGION_ENTRIES, + sizeof (RegionInfoNameEntry), region_name_locator); + + if (ne == NULL) { + /*g_print ("ne (%s) is null\n", n);*/ + g_free (n); + return FALSE; + } + g_free (n); + + MonoBoolean result = construct_region (this_obj, ®ion_entries [ne->region_entry_index], &error); + mono_error_set_pending_exception (&error); + return result; +} + +MonoArray* +ves_icall_System_Globalization_CultureInfo_internal_get_cultures (MonoBoolean neutral, + MonoBoolean specific, MonoBoolean installed) +{ + MonoError error; + MonoArray *ret; + MonoClass *klass; + MonoCultureInfo *culture; + MonoDomain *domain; + const CultureInfoEntry *ci; + gint i, len; + gboolean is_neutral; + + domain = mono_domain_get (); + + len = 0; + for (i = 0; i < NUM_CULTURE_ENTRIES; i++) { + ci = &culture_entries [i]; + is_neutral = ci->territory == 0; + if ((neutral && is_neutral) || (specific && !is_neutral)) + len++; + } + + klass = mono_class_get_culture_info_class (); + + /* The InvariantCulture is not in culture_entries */ + /* We reserve the first slot in the array for it */ + if (neutral) + len++; + + ret = mono_array_new_checked (domain, klass, len, &error); + goto_if_nok (&error, fail); + + if (len == 0) + return ret; + + len = 0; + if (neutral) + mono_array_setref (ret, len++, NULL); + + for (i = 0; i < NUM_CULTURE_ENTRIES; i++) { + ci = &culture_entries [i]; + is_neutral = ci->territory == 0; + if ((neutral && is_neutral) || (specific && !is_neutral)) { + culture = (MonoCultureInfo *) mono_object_new_checked (domain, klass, &error); + goto_if_nok (&error, fail); + mono_runtime_object_init_checked ((MonoObject *) culture, &error); + goto_if_nok (&error, fail); + if (!construct_culture (culture, ci, &error)) + goto fail; + culture->use_user_override = TRUE; + mono_array_setref (ret, len++, culture); + } + } + + return ret; + +fail: + mono_error_set_pending_exception (&error); + return ret; +} + +int ves_icall_System_Globalization_CompareInfo_internal_compare (MonoCompareInfo *this_obj, MonoString *str1, gint32 off1, gint32 len1, MonoString *str2, gint32 off2, gint32 len2, gint32 options) +{ + /* Do a normal ascii string compare, as we only know the + * invariant locale if we dont have ICU + */ + return(string_invariant_compare (str1, off1, len1, str2, off2, len2, + options)); +} + +void ves_icall_System_Globalization_CompareInfo_assign_sortkey (MonoCompareInfo *this_obj, MonoSortKey *key, MonoString *source, gint32 options) +{ + MonoError error; + MonoArray *arr; + gint32 keylen, i; + + keylen=mono_string_length (source); + + arr=mono_array_new_checked (mono_domain_get (), mono_get_byte_class (), + keylen, &error); + if (mono_error_set_pending_exception (&error)) + return; + + for(i=0; i 0) ? 1 : 0); +} + +static gint32 string_invariant_compare (MonoString *str1, gint32 off1, + gint32 len1, MonoString *str2, + gint32 off2, gint32 len2, + gint32 options) +{ + /* c translation of C# code from old string.cs.. :) */ + gint32 length; + gint32 charcmp; + gunichar2 *ustr1; + gunichar2 *ustr2; + gint32 pos; + + if(len1 >= len2) { + length=len1; + } else { + length=len2; + } + + ustr1 = mono_string_chars(str1)+off1; + ustr2 = mono_string_chars(str2)+off2; + + pos = 0; + + for (pos = 0; pos != length; pos++) { + if (pos >= len1 || pos >= len2) + break; + + charcmp = string_invariant_compare_char(ustr1[pos], ustr2[pos], + options); + if (charcmp != 0) { + return(charcmp); + } + } + + /* the lesser wins, so if we have looped until length we just + * need to check the last char + */ + if (pos == length) { + return(string_invariant_compare_char(ustr1[pos - 1], + ustr2[pos - 1], options)); + } + + /* Test if one of the strings has been compared to the end */ + if (pos >= len1) { + if (pos >= len2) { + return(0); + } else { + return(-1); + } + } else if (pos >= len2) { + return(1); + } + + /* if not, check our last char only.. (can this happen?) */ + return(string_invariant_compare_char(ustr1[pos], ustr2[pos], options)); +} + +static gint32 string_invariant_indexof (MonoString *source, gint32 sindex, + gint32 count, MonoString *value, + MonoBoolean first) +{ + gint32 lencmpstr; + gunichar2 *src; + gunichar2 *cmpstr; + gint32 pos,i; + + lencmpstr = mono_string_length(value); + + src = mono_string_chars(source); + cmpstr = mono_string_chars(value); + + if(first) { + count -= lencmpstr; + for(pos=sindex;pos <= sindex+count;pos++) { + for(i=0;src[pos+i]==cmpstr[i];) { + if(++i==lencmpstr) { + return(pos); + } + } + } + + return(-1); + } else { + for(pos=sindex-lencmpstr+1;pos>sindex-count;pos--) { + if(memcmp (src+pos, cmpstr, + lencmpstr*sizeof(gunichar2))==0) { + return(pos); + } + } + + return(-1); + } +} + +static gint32 string_invariant_indexof_char (MonoString *source, gint32 sindex, + gint32 count, gunichar2 value, + MonoBoolean first) +{ + gint32 pos; + gunichar2 *src; + + src = mono_string_chars(source); + if(first) { + for (pos = sindex; pos != count + sindex; pos++) { + if (src [pos] == value) { + return(pos); + } + } + + return(-1); + } else { + for (pos = sindex; pos > sindex - count; pos--) { + if (src [pos] == value) + return(pos); + } + + return(-1); + } +} + +void ves_icall_System_Text_Normalization_load_normalization_resource (guint8 **argProps, + guint8 **argMappedChars, + guint8 **argCharMapIndex, + guint8 **argHelperIndex, + guint8 **argMapIdxToComposite, + guint8 **argCombiningClass, + MonoError *error) +{ + error_init (error); +#ifdef DISABLE_NORMALIZATION + mono_error_set_not_supported (error, "This runtime has been compiled without string normalization support."); + return; +#else + *argProps = (guint8*)props; + *argMappedChars = (guint8*) mappedChars; + *argCharMapIndex = (guint8*) charMapIndex; + *argHelperIndex = (guint8*) helperIndex; + *argMapIdxToComposite = (guint8*) mapIdxToComposite; + *argCombiningClass = (guint8*)combiningClass; +#endif +} + + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/locales.h b/unity-2019.4.24f1-mbe/mono/metadata/locales.h new file mode 100644 index 000000000..0faee51a1 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/locales.h @@ -0,0 +1,56 @@ +/** + * \file + * Culture-sensitive handling + * + * Authors: + * Dick Porter (dick@ximian.com) + * + * (C) 2003 Ximian, Inc. + */ + +#ifndef _MONO_METADATA_LOCALES_H_ +#define _MONO_METADATA_LOCALES_H_ + +#include + +#include + +/* This is a copy of System.Globalization.CompareOptions */ +typedef enum { + CompareOptions_None=0x00, + CompareOptions_IgnoreCase=0x01, + CompareOptions_IgnoreNonSpace=0x02, + CompareOptions_IgnoreSymbols=0x04, + CompareOptions_IgnoreKanaType=0x08, + CompareOptions_IgnoreWidth=0x10, + CompareOptions_StringSort=0x20000000, + CompareOptions_Ordinal=0x40000000 +} MonoCompareOptions; + +extern MonoBoolean ves_icall_System_Globalization_CalendarData_fill_calendar_data (MonoCalendarData *this_obj, MonoString *name, gint32 calendar_index); +extern void ves_icall_System_Globalization_CultureData_fill_culture_data (MonoCultureData *this_obj, gint32 datetime_index); +extern void ves_icall_System_Globalization_CultureData_fill_number_data (MonoNumberFormatInfo* number, gint32 number_index); +extern void ves_icall_System_Globalization_CultureInfo_construct_internal_locale (MonoCultureInfo *this_obj, MonoString *locale); +extern MonoStringHandle ves_icall_System_Globalization_CultureInfo_get_current_locale_name (MonoError *error); +extern MonoBoolean ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_lcid (MonoCultureInfo *this_obj, gint lcid); +extern MonoBoolean ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_name (MonoCultureInfo *this_obj, MonoString *name); +extern MonoArray *ves_icall_System_Globalization_CultureInfo_internal_get_cultures (MonoBoolean neutral, MonoBoolean specific, MonoBoolean installed); +extern void ves_icall_System_Globalization_CompareInfo_construct_compareinfo (MonoCompareInfo *comp, MonoString *locale); +extern int ves_icall_System_Globalization_CompareInfo_internal_compare (MonoCompareInfo *this_obj, MonoString *str1, gint32 off1, gint32 len1, MonoString *str2, gint32 off2, gint32 len2, gint32 options); +extern void ves_icall_System_Globalization_CompareInfo_free_internal_collator (MonoCompareInfo *this_obj); +extern MonoBoolean +ves_icall_System_Globalization_RegionInfo_construct_internal_region_from_lcid (MonoRegionInfo *this_obj, gint lcid); +extern MonoBoolean +ves_icall_System_Globalization_RegionInfo_construct_internal_region_from_name (MonoRegionInfo *this_obj, + MonoString *name); +extern void ves_icall_System_Globalization_CompareInfo_assign_sortkey (MonoCompareInfo *this_obj, MonoSortKey *key, MonoString *source, gint32 options); +extern int ves_icall_System_Globalization_CompareInfo_internal_index (MonoCompareInfo *this_obj, MonoString *source, gint32 sindex, gint32 count, MonoString *value, gint32 options, MonoBoolean first); +extern int ves_icall_System_Globalization_CompareInfo_internal_index_char (MonoCompareInfo *this_obj, MonoString *source, gint32 sindex, gint32 count, gunichar2 value, gint32 options, MonoBoolean first); +extern int ves_icall_System_Threading_Thread_current_lcid (void); +extern MonoString *ves_icall_System_String_InternalToLower_Comp (MonoString *this_obj, MonoCultureInfo *cult); +extern MonoString *ves_icall_System_String_InternalToUpper_Comp (MonoString *this_obj, MonoCultureInfo *cult); +extern gunichar2 ves_icall_System_Char_InternalToUpper_Comp (gunichar2 c, MonoCultureInfo *cult); +extern gunichar2 ves_icall_System_Char_InternalToLower_Comp (gunichar2 c, MonoCultureInfo *cult); +extern void ves_icall_System_Text_Normalization_load_normalization_resource (guint8 **argProps, guint8** argMappedChars, guint8** argCharMapIndex, guint8** argHelperIndex, guint8** argMapIdxToComposite, guint8** argCombiningClass, MonoError *error); + +#endif /* _MONO_METADATA_FILEIO_H_ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/lock-tracer.c b/unity-2019.4.24f1-mbe/mono/metadata/lock-tracer.c new file mode 100644 index 000000000..bda05487d --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/lock-tracer.c @@ -0,0 +1,149 @@ +/** + * \file + * Runtime simple lock tracer + * + * Authors: + * Rodrigo Kumpera (rkumpera@novell.com) + * + */ + +#include +#include +#include + +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_EXECINFO_H +#include +#endif + +#include + +#include "lock-tracer.h" + +/* + * This is a very simple lock trace implementation. It can be used to verify that the runtime is + * correctly following all locking rules. + * + * To log more kind of locks just do the following: + * - add an entry into the RuntimeLocks enum + * - change mono_os_mutex_lock(mutex) to mono_locks_os_acquire (mutex, LockName) + * - change mono_os_mutex_unlock(mutex) to mono_locks_os_release (mutex, LockName) + * - change mono_coop_mutex_lock(mutex) to mono_locks_coop_acquire (mutex, LockName) + * - change mono_coop_mutex_unlock(mutex) to mono_locks_coop_release (mutex, LockName) + * - change the decoder to understand the new lock kind. + * + * TODO: + * - Use unbuffered IO without fsync + * - Switch to a binary log format + * - Enable tracing of more runtime locks + * - Add lock check assertions (must_not_hold_any_lock_but, must_hold_lock, etc) + * This should be used to verify methods that expect that a given lock is held at entrypoint, for example. + * + * To use the trace, define LOCK_TRACER in lock-trace.h and when running mono define MONO_ENABLE_LOCK_TRACER. + * This will produce a locks.ZZZ where ZZZ is the pid of the mono process. + * Use the decoder to verify the result. + */ + +#ifdef LOCK_TRACER + +#ifdef TARGET_OSX +#include +#endif + +static FILE *trace_file; +static mono_mutex_t tracer_lock; +static size_t base_address; + +typedef enum { + RECORD_MUST_NOT_HOLD_ANY, + RECORD_MUST_NOT_HOLD_ONE, + RECORD_MUST_HOLD_ONE, + RECORD_LOCK_ACQUIRED, + RECORD_LOCK_RELEASED +} RecordType; + +void +mono_locks_tracer_init (void) +{ + Dl_info info; + int res; + char *name; + mono_os_mutex_init_recursive (&tracer_lock); + + if (!g_hasenv ("MONO_ENABLE_LOCK_TRACER")) + return; + + name = g_strdup_printf ("locks.%d", getpid ()); + trace_file = fopen (name, "w+"); + g_free (name); + +#ifdef TARGET_OSX + res = dladdr ((void*)&mono_locks_tracer_init, &info); + /* The 0x1000 offset was found by empirically trying it. */ + if (res) + base_address = (size_t)info.dli_fbase - 0x1000; +#endif +} + + +#ifdef HAVE_EXECINFO_H + +static int +mono_backtrace (gpointer array[], int traces) +{ + return backtrace (array, traces); +} + +#else + +static int +mono_backtrace (gpointer array[], int traces) +{ + return 0; +} + +#endif + +static void +add_record (RecordType record_kind, RuntimeLocks kind, gpointer lock) +{ + int i = 0; + const int no_frames = 6; + gpointer frames[no_frames]; + + char *msg; + if (!trace_file) + return; + + memset (frames, 0, sizeof (gpointer) * no_frames); + mono_backtrace (frames, no_frames); + for (i = 0; i < no_frames; ++i) + frames [i] = (gpointer)((size_t)frames[i] - base_address); + + /*We only dump 5 frames, which should be more than enough to most analysis.*/ + msg = g_strdup_printf ("%x,%d,%d,%p,%p,%p,%p,%p,%p\n", (guint32)mono_native_thread_id_get (), record_kind, kind, lock, frames [1], frames [2], frames [3], frames [4], frames [5]); + fwrite (msg, strlen (msg), 1, trace_file); + fflush (trace_file); + g_free (msg); +} + +void +mono_locks_lock_acquired (RuntimeLocks kind, gpointer lock) +{ + add_record (RECORD_LOCK_ACQUIRED, kind, lock); +} + +void +mono_locks_lock_released (RuntimeLocks kind, gpointer lock) +{ + add_record (RECORD_LOCK_RELEASED, kind, lock); +} +#else + +MONO_EMPTY_SOURCE_FILE (lock_tracer); +#endif /* LOCK_TRACER */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/lock-tracer.h b/unity-2019.4.24f1-mbe/mono/metadata/lock-tracer.h new file mode 100644 index 000000000..2f1a9ef02 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/lock-tracer.h @@ -0,0 +1,73 @@ +/** + * \file + */ + +#ifndef __MONO_METADATA_LOCK_TRACER_H__ +#define __MONO_METADATA_LOCK_TRACER_H__ + +/*This is a private header*/ +#include + +#include "mono/utils/mono-os-mutex.h" +#include "mono/utils/mono-coop-mutex.h" + +G_BEGIN_DECLS + +typedef enum { + InvalidLock = 0, + LoaderLock, + ImageDataLock, + DomainLock, + DomainAssembliesLock, + DomainJitCodeHashLock, + IcallLock, + AssemblyBindingLock, + MarshalLock, + ClassesLock, + LoaderGlobalDataLock, + ThreadsLock, +} RuntimeLocks; + +#ifdef LOCK_TRACER + +void mono_locks_tracer_init (void); + +void mono_locks_lock_acquired (RuntimeLocks kind, gpointer lock); +void mono_locks_lock_released (RuntimeLocks kind, gpointer lock); + +#else + +#define mono_locks_tracer_init() do {} while (0) + +#define mono_locks_lock_acquired(__UNUSED0, __UNUSED1) do {} while (0) +#define mono_locks_lock_released(__UNUSED0, __UNUSED1) do {} while (0) + +#endif + +#define mono_locks_os_acquire(LOCK,NAME) \ + do { \ + mono_os_mutex_lock (LOCK); \ + mono_locks_lock_acquired (NAME, LOCK); \ + } while (0) + +#define mono_locks_os_release(LOCK,NAME) \ + do { \ + mono_locks_lock_released (NAME, LOCK); \ + mono_os_mutex_unlock (LOCK); \ + } while (0) + +#define mono_locks_coop_acquire(LOCK,NAME) \ + do { \ + mono_coop_mutex_lock (LOCK); \ + mono_locks_lock_acquired (NAME, LOCK); \ + } while (0) + +#define mono_locks_coop_release(LOCK,NAME) \ + do { \ + mono_locks_lock_released (NAME, LOCK); \ + mono_coop_mutex_unlock (LOCK); \ + } while (0) + +G_END_DECLS + +#endif /* __MONO_METADATA_LOCK_TRACER_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/marshal-internals.h b/unity-2019.4.24f1-mbe/mono/metadata/marshal-internals.h new file mode 100644 index 000000000..7316ac4a0 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/marshal-internals.h @@ -0,0 +1,42 @@ +/** + * \file + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#ifndef __MONO_METADATA_MARSHAL_INTERNALS_H__ +#define __MONO_METADATA_MARSHAL_INTERNALS_H__ + +#include +#include +#include + +MonoObjectHandle +mono_marshal_xdomain_copy_value_handle (MonoObjectHandle val, MonoError *error); + +// On Windows platform implementation of bellow methods are hosted in separate source file +// masrshal-windows.c or marshal-windows-*.c. On other platforms the implementation is still keept +// in marshal.c still declared as static and in some places even inlined. +#ifdef HOST_WIN32 +void* +mono_marshal_alloc_co_task_mem (size_t size); + +void +mono_marshal_free_co_task_mem (void *ptr); + +gpointer +mono_marshal_realloc_co_task_mem (gpointer ptr, size_t size); + +void* +mono_marshal_alloc_hglobal (size_t size); + +gpointer +mono_marshal_realloc_hglobal (gpointer ptr, size_t size); + +void +mono_marshal_free_hglobal (void *ptr); + +gpointer +mono_string_to_utf8str (MonoString *s); +#endif /* HOST_WIN32 */ + +#endif /* __MONO_METADATA_MARSHAL_INTERNALS_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/marshal-windows-internals.h b/unity-2019.4.24f1-mbe/mono/metadata/marshal-windows-internals.h new file mode 100644 index 000000000..9d251cdd7 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/marshal-windows-internals.h @@ -0,0 +1,18 @@ +/** + * \file + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#ifndef __MONO_METADATA_MARSHAL_WINDOWS_INTERNALS_H__ +#define __MONO_METADATA_MARSHAL_WINDOWS_INTERNALS_H__ + +#include +#include + +#ifdef HOST_WIN32 +#include "mono/metadata/marshal.h" +#include "mono/metadata/marshal-internals.h" +#include "mono/metadata/exception.h" +#endif /* HOST_WIN32 */ + +#endif /* __MONO_METADATA_MARSHAL_WINDOWS_INTERNALS_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/marshal-windows-uwp.c b/unity-2019.4.24f1-mbe/mono/metadata/marshal-windows-uwp.c new file mode 100644 index 000000000..f268d15cd --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/marshal-windows-uwp.c @@ -0,0 +1,38 @@ +/** + * \file + * UWP marshal support for Mono. + * + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. +*/ +#include +#include +#include "mono/utils/mono-compiler.h" + +#if G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) +#include +#include "mono/metadata/marshal-windows-internals.h" + +void * +mono_marshal_alloc_hglobal (size_t size) +{ + return HeapAlloc (GetProcessHeap (), 0, size); +} + +gpointer +mono_marshal_realloc_hglobal (gpointer ptr, size_t size) +{ + return HeapReAlloc (GetProcessHeap (), 0, ptr, size); +} + +void +mono_marshal_free_hglobal (gpointer ptr) +{ + HeapFree (GetProcessHeap (), 0, ptr); + return; +} + +#else /* G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) */ + +MONO_EMPTY_SOURCE_FILE (marshal_windows_uwp); +#endif /* G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/marshal-windows.c b/unity-2019.4.24f1-mbe/mono/metadata/marshal-windows.c new file mode 100644 index 000000000..73f9310b4 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/marshal-windows.c @@ -0,0 +1,126 @@ +/** + * \file + * Windows marshal support. + * + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#include +#include + +#if defined(HOST_WIN32) +#include +#include +#include +#include "mono/metadata/marshal-windows-internals.h" + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +void* +mono_marshal_alloc_hglobal (size_t size) +{ + return GlobalAlloc (GMEM_FIXED, size); +} + +gpointer +mono_marshal_realloc_hglobal (gpointer ptr, size_t size) +{ + return GlobalReAlloc (ptr, size, GMEM_MOVEABLE); +} + +void +mono_marshal_free_hglobal (gpointer ptr) +{ + GlobalFree (ptr); + return; +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ + +void* +mono_marshal_alloc_co_task_mem (size_t size) +{ + return CoTaskMemAlloc (size); +} + +void +mono_marshal_free_co_task_mem (void *ptr) +{ + CoTaskMemFree (ptr); + return; +} + +gpointer +mono_marshal_realloc_co_task_mem (gpointer ptr, size_t size) +{ + return CoTaskMemRealloc (ptr, size); +} + +gpointer +ves_icall_System_Runtime_InteropServices_Marshal_StringToHGlobalAnsi (MonoString *string) +{ + MonoError error; + char* tres, *ret; + size_t len; + tres = mono_string_to_utf8_checked (string, &error); + if (mono_error_set_pending_exception (&error)) + return NULL; + if (!tres) + return tres; + + /* + * mono_string_to_utf8_checked() returns a memory area at least as large as the size of the + * MonoString, even if it contains NULL characters. The copy we allocate here has to be equally + * large. + */ + len = MAX (strlen (tres) + 1, string->length); + ret = ves_icall_System_Runtime_InteropServices_Marshal_AllocHGlobal ((gpointer)len); + memcpy (ret, tres, len); + g_free (tres); + return ret; +} + +gpointer +ves_icall_System_Runtime_InteropServices_Marshal_StringToHGlobalUni (MonoString *string) +{ + if (string == NULL) + return NULL; + else { + size_t len = ((mono_string_length (string) + 1) * 2); + gunichar2 *res = ves_icall_System_Runtime_InteropServices_Marshal_AllocHGlobal ((gpointer)len); + + memcpy (res, mono_string_chars (string), mono_string_length (string) * 2); + res [mono_string_length (string)] = 0; + return res; + } +} + +gpointer +mono_string_to_utf8str (MonoString *s) +{ + char *as, *tmp; + glong len; + GError *error = NULL; + + if (s == NULL) + return NULL; + + if (!s->length) { + as = CoTaskMemAlloc (1); + as [0] = '\0'; + return as; + } + + tmp = g_utf16_to_utf8 (mono_string_chars (s), s->length, NULL, &len, &error); + if (error) { + MonoException *exc = mono_get_exception_argument ("string", error->message); + g_error_free (error); + mono_set_pending_exception (exc); + return NULL; + } else { + as = CoTaskMemAlloc (len + 1); + memcpy (as, tmp, len + 1); + g_free (tmp); + return as; + } +} + +#endif /* HOST_WIN32 */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/marshal.c b/unity-2019.4.24f1-mbe/mono/metadata/marshal.c new file mode 100644 index 000000000..f8e08f591 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/marshal.c @@ -0,0 +1,12476 @@ +/** + * \file + * Routines for marshaling complex types in P/Invoke methods. + * + * Author: + * Paolo Molaro (lupus@ximian.com) + * + * Copyright 2002-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Copyright 2011 Xamarin, Inc (http://www.xamarin.com) + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include "config.h" +#ifdef HAVE_ALLOCA_H +#include +#endif + +#include "object.h" +#include "loader.h" +#include "cil-coff.h" +#include "metadata/marshal.h" +#include "metadata/marshal-internals.h" +#include "metadata/method-builder.h" +#include "metadata/tabledefs.h" +#include "metadata/exception.h" +#include "metadata/appdomain.h" +#include "mono/metadata/abi-details.h" +#include "mono/metadata/debug-helpers.h" +#include "mono/metadata/threads.h" +#include "mono/metadata/monitor.h" +#include "mono/metadata/class-internals.h" +#include "mono/metadata/metadata-internals.h" +#include "mono/metadata/domain-internals.h" +#include "mono/metadata/gc-internals.h" +#include "mono/metadata/threads-types.h" +#include "mono/metadata/string-icalls.h" +#include "mono/metadata/attrdefs.h" +#include "mono/metadata/cominterop.h" +#include "mono/metadata/remoting.h" +#include "mono/metadata/reflection-internals.h" +#include "mono/metadata/threadpool.h" +#include "mono/metadata/handle.h" +#include "mono/utils/mono-counters.h" +#include "mono/utils/mono-tls.h" +#include "mono/utils/mono-memory-model.h" +#include "mono/utils/atomic.h" +#include +#include +#include + +#include +#include + +/* #define DEBUG_RUNTIME_CODE */ + +#define OPDEF(a,b,c,d,e,f,g,h,i,j) \ + a = i, + +enum { +#include "mono/cil/opcode.def" + LAST = 0xff +}; +#undef OPDEF + +/* + * This mutex protects the various marshalling related caches in MonoImage + * and a few other data structures static to this file. + * + * The marshal lock is a non-recursive complex lock that sits below the domain lock in the + * runtime locking latice. Which means it can take simple locks suck as the image lock. + */ +#define mono_marshal_lock() mono_locks_os_acquire (&marshal_mutex, MarshalLock) +#define mono_marshal_unlock() mono_locks_os_release (&marshal_mutex, MarshalLock) +static mono_mutex_t marshal_mutex; +static gboolean marshal_mutex_initialized; + +static MonoNativeTlsKey last_error_tls_id; + +static MonoNativeTlsKey load_type_info_tls_id; + +static gboolean use_aot_wrappers; + +static int class_marshal_info_count; + +static void ftnptr_eh_callback_default (guint32 gchandle); + +static MonoFtnPtrEHCallback ftnptr_eh_callback = ftnptr_eh_callback_default; + +static void +delegate_hash_table_add (MonoDelegateHandle d); + +static void +delegate_hash_table_remove (MonoDelegate *d); + +static void +emit_struct_conv (MonoMethodBuilder *mb, MonoClass *klass, gboolean to_object); + +static void +emit_struct_conv_full (MonoMethodBuilder *mb, MonoClass *klass, gboolean to_object, int offset_of_first_child_field, MonoMarshalNative string_encoding); + +static void +mono_struct_delete_old (MonoClass *klass, char *ptr); + +MONO_API void * +mono_marshal_string_to_utf16 (MonoString *s); + +static void * +mono_marshal_string_to_utf16_copy (MonoString *s); + +#ifndef HOST_WIN32 +static gpointer +mono_string_to_utf8str (MonoString *string_obj); +#endif + +static MonoStringBuilder * +mono_string_utf8_to_builder2 (char *text); + +static MonoStringBuilder * +mono_string_utf16_to_builder2 (gunichar2 *text); + +static MonoString* +mono_string_new_len_wrapper (const char *text, guint length); + +static MonoString * +mono_string_from_byvalstr (const char *data, int len); + +static MonoString * +mono_string_from_byvalwstr (gunichar2 *data, int len); + +static void +mono_byvalarray_to_array (MonoArray *arr, gpointer native_arr, MonoClass *eltype, guint32 elnum); + +static void +mono_byvalarray_to_byte_array (MonoArray *arr, gpointer native_arr, guint32 elnum); + +static void +mono_array_to_byvalarray (gpointer native_arr, MonoArray *arr, MonoClass *eltype, guint32 elnum); + +static void +mono_array_to_byte_byvalarray (gpointer native_arr, MonoArray *arr, guint32 elnum); + +static MonoAsyncResult * +mono_delegate_begin_invoke (MonoDelegate *delegate, gpointer *params); + +static MonoObject * +mono_delegate_end_invoke (MonoDelegate *delegate, gpointer *params); + +static void +mono_marshal_set_last_error_windows (int error); + +static MonoObject * +mono_marshal_isinst_with_cache (MonoObject *obj, MonoClass *klass, uintptr_t *cache); + +static void init_safe_handle (void); + +static void* +ves_icall_marshal_alloc (gsize size); + +void +mono_string_utf8_to_builder (MonoStringBuilder *sb, char *text); + +void +mono_string_utf16_to_builder (MonoStringBuilder *sb, gunichar2 *text); + +gchar* +mono_string_builder_to_utf8 (MonoStringBuilder *sb); + +gunichar2* +mono_string_builder_to_utf16 (MonoStringBuilder *sb); + +void +mono_string_to_byvalstr (gpointer dst, MonoString *src, int size); + +void +mono_string_to_byvalwstr (gpointer dst, MonoString *src, int size); + +gpointer +mono_delegate_to_ftnptr (MonoDelegate *delegate); + +gpointer +mono_delegate_handle_to_ftnptr (MonoDelegateHandle delegate, MonoError *error); + +MonoDelegate* +mono_ftnptr_to_delegate (MonoClass *klass, gpointer ftn); + +MonoDelegateHandle +mono_ftnptr_to_delegate_handle (MonoClass *klass, gpointer ftn, MonoError *error); + +gpointer +mono_array_to_savearray (MonoArray *array); + +gpointer +mono_array_to_lparray (MonoArray *array); + +void +mono_free_lparray (MonoArray *array, gpointer* nativeArray); + +gpointer +mono_marshal_asany (MonoObject *obj, MonoMarshalNative string_encoding, int param_attrs); + +void +mono_marshal_free_asany (MonoObject *o, gpointer ptr, MonoMarshalNative string_encoding, int param_attrs); + +gpointer +mono_array_to_savearray (MonoArray *array); + +gpointer +mono_array_to_lparray (MonoArray *array); + +void +mono_free_lparray (MonoArray *array, gpointer* nativeArray); + +static void +mono_marshal_ftnptr_eh_callback (guint32 gchandle); + +static MonoThreadInfo* +mono_icall_start (HandleStackMark *stackmark, MonoError *error); + +static void +mono_icall_end (MonoThreadInfo *info, HandleStackMark *stackmark, MonoError *error); + +static MonoObjectHandle +mono_icall_handle_new (gpointer rawobj); + +static MonoObjectHandle +mono_icall_handle_new_interior (gpointer rawobj); + +/* Lazy class loading functions */ +static GENERATE_GET_CLASS_WITH_CACHE (string_builder, "System.Text", "StringBuilder"); +static GENERATE_GET_CLASS_WITH_CACHE (date_time, "System", "DateTime"); +static GENERATE_GET_CLASS_WITH_CACHE (fixed_buffer_attribute, "System.Runtime.CompilerServices", "FixedBufferAttribute"); +static GENERATE_TRY_GET_CLASS_WITH_CACHE (unmanaged_function_pointer_attribute, "System.Runtime.InteropServices", "UnmanagedFunctionPointerAttribute"); +static GENERATE_TRY_GET_CLASS_WITH_CACHE (icustom_marshaler, "System.Runtime.InteropServices", "ICustomMarshaler"); + +/* MonoMethod pointers to SafeHandle::DangerousAddRef and ::DangerousRelease */ +static MonoMethod *sh_dangerous_add_ref; +static MonoMethod *sh_dangerous_release; + +static void +init_safe_handle () +{ + sh_dangerous_add_ref = mono_class_get_method_from_name ( + mono_class_try_get_safehandle_class (), "DangerousAddRef", 1); + sh_dangerous_release = mono_class_get_method_from_name ( + mono_class_try_get_safehandle_class (), "DangerousRelease", 0); +} + +static void +register_icall (gpointer func, const char *name, const char *sigstr, gboolean no_wrapper) +{ + MonoMethodSignature *sig = mono_create_icall_signature (sigstr); + + mono_register_jit_icall (func, name, sig, no_wrapper); +} + +static void +register_icall_no_wrapper (gpointer func, const char *name, const char *sigstr) +{ + MonoMethodSignature *sig = mono_create_icall_signature (sigstr); + + mono_register_jit_icall (func, name, sig, TRUE); +} + +MonoMethodSignature* +mono_signature_no_pinvoke (MonoMethod *method) +{ + MonoMethodSignature *sig = mono_method_signature (method); + if (sig->pinvoke) { + sig = mono_metadata_signature_dup_full (method->klass->image, sig); + sig->pinvoke = FALSE; + } + + return sig; +} + +void +mono_marshal_init_tls (void) +{ + mono_native_tls_alloc (&last_error_tls_id, NULL); + mono_native_tls_alloc (&load_type_info_tls_id, NULL); +} + +static MonoObject* +mono_object_isinst_icall (MonoObject *obj, MonoClass *klass) +{ + if (!klass) + return NULL; + + /* This is called from stelemref so it is expected to succeed */ + /* Fastpath */ + if (mono_class_is_interface (klass)) { + MonoVTable *vt = obj->vtable; + + if (!klass->inited) + mono_class_init (klass); + + if (MONO_VTABLE_IMPLEMENTS_INTERFACE (vt, klass->interface_id)) + return obj; + } + + MonoError error; + MonoObject *result = mono_object_isinst_checked (obj, klass, &error); + mono_error_set_pending_exception (&error); + return result; +} + +static MonoString* +ves_icall_mono_string_from_utf16 (gunichar2 *data) +{ + MonoError error; + MonoString *result = mono_string_from_utf16_checked (data, &error); + mono_error_set_pending_exception (&error); + return result; +} + +static char* +ves_icall_mono_string_to_utf8 (MonoString *str) +{ + MonoError error; + char *result = mono_string_to_utf8_checked (str, &error); + mono_error_set_pending_exception (&error); + return result; +} + +static MonoString* +ves_icall_string_new_wrapper (const char *text) +{ + if (text) { + MonoError error; + MonoString *res = mono_string_new_checked (mono_domain_get (), text, &error); + mono_error_set_pending_exception (&error); + return res; + } + + return NULL; +} + +void +mono_marshal_init (void) +{ + static gboolean module_initialized = FALSE; + + if (!module_initialized) { + module_initialized = TRUE; + mono_os_mutex_init_recursive (&marshal_mutex); + marshal_mutex_initialized = TRUE; + + register_icall (ves_icall_System_Threading_Thread_ResetAbort, "ves_icall_System_Threading_Thread_ResetAbort", "void", TRUE); + register_icall (mono_marshal_string_to_utf16, "mono_marshal_string_to_utf16", "ptr obj", FALSE); + register_icall (mono_marshal_string_to_utf16_copy, "mono_marshal_string_to_utf16_copy", "ptr obj", FALSE); + register_icall (mono_string_to_utf16, "mono_string_to_utf16", "ptr obj", FALSE); + register_icall (ves_icall_mono_string_from_utf16, "ves_icall_mono_string_from_utf16", "obj ptr", FALSE); + register_icall (mono_string_from_byvalstr, "mono_string_from_byvalstr", "obj ptr int", FALSE); + register_icall (mono_string_from_byvalwstr, "mono_string_from_byvalwstr", "obj ptr int", FALSE); + register_icall (mono_string_new_wrapper, "mono_string_new_wrapper", "obj ptr", FALSE); + register_icall (ves_icall_string_new_wrapper, "ves_icall_string_new_wrapper", "obj ptr", FALSE); + register_icall (mono_string_new_len_wrapper, "mono_string_new_len_wrapper", "obj ptr int", FALSE); + register_icall (ves_icall_mono_string_to_utf8, "ves_icall_mono_string_to_utf8", "ptr obj", FALSE); + register_icall (mono_string_to_utf8str, "mono_string_to_utf8str", "ptr obj", FALSE); + register_icall (mono_string_to_ansibstr, "mono_string_to_ansibstr", "ptr object", FALSE); + register_icall (mono_string_builder_to_utf8, "mono_string_builder_to_utf8", "ptr object", FALSE); + register_icall (mono_string_builder_to_utf16, "mono_string_builder_to_utf16", "ptr object", FALSE); + register_icall (mono_array_to_savearray, "mono_array_to_savearray", "ptr object", FALSE); + register_icall (mono_array_to_lparray, "mono_array_to_lparray", "ptr object", FALSE); + register_icall (mono_free_lparray, "mono_free_lparray", "void object ptr", FALSE); + register_icall (mono_byvalarray_to_array, "mono_byvalarray_to_array", "void object ptr ptr int32", FALSE); + register_icall (mono_byvalarray_to_byte_array, "mono_byvalarray_to_byte_array", "void object ptr int32", FALSE); + register_icall (mono_array_to_byvalarray, "mono_array_to_byvalarray", "void ptr object ptr int32", FALSE); + register_icall (mono_array_to_byte_byvalarray, "mono_array_to_byte_byvalarray", "void ptr object int32", FALSE); + register_icall (mono_delegate_to_ftnptr, "mono_delegate_to_ftnptr", "ptr object", FALSE); + register_icall (mono_ftnptr_to_delegate, "mono_ftnptr_to_delegate", "object ptr ptr", FALSE); + register_icall (mono_marshal_asany, "mono_marshal_asany", "ptr object int32 int32", FALSE); + register_icall (mono_marshal_free_asany, "mono_marshal_free_asany", "void object ptr int32 int32", FALSE); + register_icall (ves_icall_marshal_alloc, "ves_icall_marshal_alloc", "ptr ptr", FALSE); + register_icall (mono_marshal_free, "mono_marshal_free", "void ptr", FALSE); + register_icall (mono_marshal_set_last_error, "mono_marshal_set_last_error", "void", TRUE); + register_icall (mono_marshal_set_last_error_windows, "mono_marshal_set_last_error_windows", "void int32", TRUE); + register_icall (mono_string_utf8_to_builder, "mono_string_utf8_to_builder", "void ptr ptr", FALSE); + register_icall (mono_string_utf8_to_builder2, "mono_string_utf8_to_builder2", "object ptr", FALSE); + register_icall (mono_string_utf16_to_builder, "mono_string_utf16_to_builder", "void ptr ptr", FALSE); + register_icall (mono_string_utf16_to_builder2, "mono_string_utf16_to_builder2", "object ptr", FALSE); + register_icall (mono_marshal_free_array, "mono_marshal_free_array", "void ptr int32", FALSE); + register_icall (mono_string_to_byvalstr, "mono_string_to_byvalstr", "void ptr ptr int32", FALSE); + register_icall (mono_string_to_byvalwstr, "mono_string_to_byvalwstr", "void ptr ptr int32", FALSE); + register_icall (g_free, "g_free", "void ptr", FALSE); + register_icall_no_wrapper (mono_object_isinst_icall, "mono_object_isinst_icall", "object object ptr"); + register_icall (mono_struct_delete_old, "mono_struct_delete_old", "void ptr ptr", FALSE); + register_icall (mono_delegate_begin_invoke, "mono_delegate_begin_invoke", "object object ptr", FALSE); + register_icall (mono_delegate_end_invoke, "mono_delegate_end_invoke", "object object ptr", FALSE); + register_icall (mono_gc_wbarrier_generic_nostore, "wb_generic", "void ptr", FALSE); + register_icall (mono_gchandle_get_target, "mono_gchandle_get_target", "object int32", TRUE); + register_icall (mono_gchandle_new, "mono_gchandle_new", "uint32 object bool", TRUE); + register_icall (mono_marshal_isinst_with_cache, "mono_marshal_isinst_with_cache", "object object ptr ptr", FALSE); + register_icall (mono_marshal_ftnptr_eh_callback, "mono_marshal_ftnptr_eh_callback", "void uint32", TRUE); + register_icall (mono_threads_enter_gc_safe_region_unbalanced, "mono_threads_enter_gc_safe_region_unbalanced", "ptr ptr", TRUE); + register_icall (mono_threads_exit_gc_safe_region_unbalanced, "mono_threads_exit_gc_safe_region_unbalanced", "void ptr ptr", TRUE); + register_icall (mono_threads_attach_coop, "mono_threads_attach_coop", "ptr ptr ptr", TRUE); + register_icall (mono_threads_detach_coop, "mono_threads_detach_coop", "void ptr ptr", TRUE); + register_icall (mono_icall_start, "mono_icall_start", "ptr ptr ptr", TRUE); + register_icall (mono_icall_end, "mono_icall_end", "void ptr ptr ptr", TRUE); + register_icall (mono_icall_handle_new, "mono_icall_handle_new", "ptr ptr", TRUE); + register_icall (mono_icall_handle_new_interior, "mono_icall_handle_new_interior", "ptr ptr", TRUE); + + mono_cominterop_init (); + mono_remoting_init (); + + mono_counters_register ("MonoClass::class_marshal_info_count count", + MONO_COUNTER_METADATA | MONO_COUNTER_INT, &class_marshal_info_count); + + } +} + +void +mono_marshal_cleanup (void) +{ + mono_cominterop_cleanup (); + + mono_native_tls_free (load_type_info_tls_id); + mono_native_tls_free (last_error_tls_id); + mono_os_mutex_destroy (&marshal_mutex); + marshal_mutex_initialized = FALSE; +} + +void +mono_marshal_lock_internal (void) +{ + mono_marshal_lock (); +} + +void +mono_marshal_unlock_internal (void) +{ + mono_marshal_unlock (); +} + +/* This is a JIT icall, it sets the pending exception and return NULL on error */ +gpointer +mono_delegate_to_ftnptr (MonoDelegate *delegate_raw) +{ + HANDLE_FUNCTION_ENTER (); + MonoError error; + MONO_HANDLE_DCL (MonoDelegate, delegate); + gpointer result = mono_delegate_handle_to_ftnptr (delegate, &error); + mono_error_set_pending_exception (&error); + HANDLE_FUNCTION_RETURN_VAL (result); +} + +gpointer +mono_delegate_handle_to_ftnptr (MonoDelegateHandle delegate, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + gpointer result = NULL; + error_init (error); + MonoMethod *method, *wrapper; + MonoClass *klass; + uint32_t target_handle = 0; + + if (MONO_HANDLE_IS_NULL (delegate)) + goto leave; + + if (MONO_HANDLE_GETVAL (delegate, delegate_trampoline)) { + result = MONO_HANDLE_GETVAL (delegate, delegate_trampoline); + goto leave; + } + + klass = mono_handle_class (delegate); + g_assert (klass->delegate); + + method = MONO_HANDLE_GETVAL (delegate, method); + if (MONO_HANDLE_GETVAL (delegate, method_is_virtual)) { + MonoObjectHandle delegate_target = MONO_HANDLE_NEW_GET (MonoObject, delegate, target); + method = mono_object_handle_get_virtual_method (delegate_target, method, error); + goto_if_nok (error, leave); + } + + if (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) { + const char *exc_class, *exc_arg; + gpointer ftnptr; + + ftnptr = mono_lookup_pinvoke_call (method, &exc_class, &exc_arg); + if (!ftnptr) { + g_assert (exc_class); + mono_error_set_generic_error (error, "System", exc_class, "%s", exc_arg); + goto leave; + } + result = ftnptr; + goto leave; + } + + MonoObjectHandle delegate_target = MONO_HANDLE_NEW_GET (MonoObject, delegate, target); + if (!MONO_HANDLE_IS_NULL (delegate_target)) { + /* Produce a location which can be embedded in JITted code */ + target_handle = mono_gchandle_new_weakref (MONO_HANDLE_RAW (delegate_target), FALSE); /* FIXME: a version of mono_gchandle_new_weakref that takes a coop handle */ + } + + wrapper = mono_marshal_get_managed_wrapper (method, klass, target_handle, error); + goto_if_nok (error, leave); + + MONO_HANDLE_SETVAL (delegate, delegate_trampoline, gpointer, mono_compile_method_checked (wrapper, error)); + goto_if_nok (error, leave); + + // Add the delegate to the delegate hash table + delegate_hash_table_add (delegate); + + /* when the object is collected, collect the dynamic method, too */ + mono_object_register_finalizer ((MonoObject*) MONO_HANDLE_RAW (delegate)); + + result = MONO_HANDLE_GETVAL (delegate, delegate_trampoline); + +leave: + if (!is_ok (error) && target_handle != 0) + mono_gchandle_free (target_handle); + HANDLE_FUNCTION_RETURN_VAL (result); +} + +/* + * this hash table maps from a delegate trampoline object to a weak reference + * of the delegate. As an optimizations with a non-moving GC we store the + * object pointer itself, otherwise we use a GC handle. + */ +static GHashTable *delegate_hash_table; + +static GHashTable * +delegate_hash_table_new (void) { + return g_hash_table_new (NULL, NULL); +} + +static void +delegate_hash_table_remove (MonoDelegate *d) +{ + guint32 gchandle = 0; + + mono_marshal_lock (); + if (delegate_hash_table == NULL) + delegate_hash_table = delegate_hash_table_new (); + if (mono_gc_is_moving ()) + gchandle = GPOINTER_TO_UINT (g_hash_table_lookup (delegate_hash_table, d->delegate_trampoline)); + g_hash_table_remove (delegate_hash_table, d->delegate_trampoline); + mono_marshal_unlock (); + if (gchandle && mono_gc_is_moving ()) + mono_gchandle_free (gchandle); +} + +static void +delegate_hash_table_add (MonoDelegateHandle d) +{ + guint32 gchandle; + guint32 old_gchandle; + + mono_marshal_lock (); + if (delegate_hash_table == NULL) + delegate_hash_table = delegate_hash_table_new (); + gpointer delegate_trampoline = MONO_HANDLE_GETVAL (d, delegate_trampoline); + if (mono_gc_is_moving ()) { + gchandle = mono_gchandle_new_weakref ((MonoObject*) MONO_HANDLE_RAW (d), FALSE); + old_gchandle = GPOINTER_TO_UINT (g_hash_table_lookup (delegate_hash_table, delegate_trampoline)); + g_hash_table_insert (delegate_hash_table, delegate_trampoline, GUINT_TO_POINTER (gchandle)); + if (old_gchandle) + mono_gchandle_free (old_gchandle); + } else { + g_hash_table_insert (delegate_hash_table, delegate_trampoline, MONO_HANDLE_RAW (d)); + } + mono_marshal_unlock (); +} + +/* + * mono_marshal_use_aot_wrappers: + * + * Instructs this module to use AOT compatible wrappers. + */ +void +mono_marshal_use_aot_wrappers (gboolean use) +{ + use_aot_wrappers = use; +} + +static void +parse_unmanaged_function_pointer_attr (MonoClass *klass, MonoMethodPInvoke *piinfo) +{ + MonoError error; + MonoCustomAttrInfo *cinfo; + MonoReflectionUnmanagedFunctionPointerAttribute *attr; + + /* The attribute is only available in Net 2.0 */ + if (mono_class_try_get_unmanaged_function_pointer_attribute_class ()) { + /* + * The pinvoke attributes are stored in a real custom attribute so we have to + * construct it. + */ + cinfo = mono_custom_attrs_from_class_checked (klass, &error); + if (!mono_error_ok (&error)) { + g_warning ("Could not load UnmanagedFunctionPointerAttribute due to %s", mono_error_get_message (&error)); + mono_error_cleanup (&error); + } + if (cinfo && !mono_runtime_get_no_exec ()) { + attr = (MonoReflectionUnmanagedFunctionPointerAttribute*)mono_custom_attrs_get_attr_checked (cinfo, mono_class_try_get_unmanaged_function_pointer_attribute_class (), &error); + if (attr) { + piinfo->piflags = (attr->call_conv << 8) | (attr->charset ? (attr->charset - 1) * 2 : 1) | attr->set_last_error; + } else { + if (!mono_error_ok (&error)) { + g_warning ("Could not load UnmanagedFunctionPointerAttribute due to %s", mono_error_get_message (&error)); + mono_error_cleanup (&error); + } + } + if (!cinfo->cached) + mono_custom_attrs_free (cinfo); + } + } +} + +/* This is a JIT icall, it sets the pending exception and returns NULL on error */ +MonoDelegate* +mono_ftnptr_to_delegate (MonoClass *klass, gpointer ftn) +{ + HANDLE_FUNCTION_ENTER (); + MonoError error; + MonoDelegateHandle result = mono_ftnptr_to_delegate_handle (klass, ftn, &error); + mono_error_set_pending_exception (&error); + HANDLE_FUNCTION_RETURN_OBJ (result); +} + +MonoDelegateHandle +mono_ftnptr_to_delegate_handle (MonoClass *klass, gpointer ftn, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + error_init (error); + guint32 gchandle; + MonoDelegateHandle d = MONO_HANDLE_NEW (MonoDelegate, NULL); + + if (ftn == NULL) + goto leave; + + mono_marshal_lock (); + if (delegate_hash_table == NULL) + delegate_hash_table = delegate_hash_table_new (); + + if (mono_gc_is_moving ()) { + gchandle = GPOINTER_TO_UINT (g_hash_table_lookup (delegate_hash_table, ftn)); + mono_marshal_unlock (); + if (gchandle) + MONO_HANDLE_ASSIGN (d, MONO_HANDLE_CAST (MonoDelegate, mono_gchandle_get_target_handle (gchandle))); + } else { + MONO_HANDLE_ASSIGN (d, MONO_HANDLE_NEW (MonoDelegate, g_hash_table_lookup (delegate_hash_table, ftn))); + mono_marshal_unlock (); + } + if (MONO_HANDLE_IS_NULL (d)) { + /* This is a native function, so construct a delegate for it */ + MonoMethodSignature *sig; + MonoMethod *wrapper; + MonoMarshalSpec **mspecs; + MonoMethod *invoke = mono_get_delegate_invoke (klass); + MonoMethodPInvoke piinfo; + MonoObjectHandle this_obj; + int i; + + if (use_aot_wrappers) { + wrapper = mono_marshal_get_native_func_wrapper_aot (klass); + this_obj = MONO_HANDLE_NEW (MonoObject, mono_value_box_checked (mono_domain_get (), mono_defaults.int_class, &ftn, error)); + goto_if_nok (error, leave); + } else { + memset (&piinfo, 0, sizeof (piinfo)); + parse_unmanaged_function_pointer_attr (klass, &piinfo); + + mspecs = g_new0 (MonoMarshalSpec*, mono_method_signature (invoke)->param_count + 1); + mono_method_get_marshal_info (invoke, mspecs); + /* Freed below so don't alloc from mempool */ + sig = mono_metadata_signature_dup (mono_method_signature (invoke)); + sig->hasthis = 0; + + wrapper = mono_marshal_get_native_func_wrapper (klass->image, sig, &piinfo, mspecs, ftn); + this_obj = MONO_HANDLE_NEW (MonoObject, NULL); + + for (i = mono_method_signature (invoke)->param_count; i >= 0; i--) + if (mspecs [i]) + mono_metadata_free_marshal_spec (mspecs [i]); + g_free (mspecs); + g_free (sig); + } + + MONO_HANDLE_ASSIGN (d, MONO_HANDLE_NEW (MonoDelegate, mono_object_new_checked (mono_domain_get (), klass, error))); + goto_if_nok (error, leave); + gpointer compiled_ptr = mono_compile_method_checked (wrapper, error); + goto_if_nok (error, leave); + + mono_delegate_ctor_with_method (MONO_HANDLE_CAST (MonoObject, d), this_obj, compiled_ptr, wrapper, error); + goto_if_nok (error, leave); + } + + g_assert (!MONO_HANDLE_IS_NULL (d)); + if (MONO_HANDLE_DOMAIN (d) != mono_domain_get ()) + mono_error_set_not_supported (error, "Delegates cannot be marshalled from native code into a domain other than their home domain"); + +leave: + HANDLE_FUNCTION_RETURN_REF (MonoDelegate, d); +} + +void +mono_delegate_free_ftnptr (MonoDelegate *delegate) +{ + MonoJitInfo *ji; + void *ptr; + + delegate_hash_table_remove (delegate); + + ptr = (gpointer)mono_atomic_xchg_ptr (&delegate->delegate_trampoline, NULL); + + if (!delegate->target) { + /* The wrapper method is shared between delegates -> no need to free it */ + return; + } + + if (ptr) { + uint32_t gchandle; + void **method_data; + MonoMethod *method; + + ji = mono_jit_info_table_find (mono_domain_get (), (char *)mono_get_addr_from_ftnptr (ptr)); + g_assert (ji); + + method = mono_jit_info_get_method (ji); + method_data = (void **)((MonoMethodWrapper*)method)->method_data; + + /*the target gchandle is the first entry after size and the wrapper itself.*/ + gchandle = GPOINTER_TO_UINT (method_data [2]); + + if (gchandle) + mono_gchandle_free (gchandle); + + mono_runtime_free_method (mono_object_domain (delegate), method); + } +} + +/* This is a JIT icall, it sets the pending exception and returns NULL on error */ +static MonoString * +mono_string_from_byvalstr (const char *data, int max_len) +{ + MonoError error; + MonoDomain *domain = mono_domain_get (); + int len = 0; + + if (!data) + return NULL; + + while (len < max_len - 1 && data [len]) + len++; + + MonoString *result = mono_string_new_len_checked (domain, data, len, &error); + mono_error_set_pending_exception (&error); + return result; +} + +/* This is a JIT icall, it sets the pending exception and return NULL on error */ +static MonoString * +mono_string_from_byvalwstr (gunichar2 *data, int max_len) +{ + MonoError error; + MonoString *res = NULL; + MonoDomain *domain = mono_domain_get (); + int len = 0; + + if (!data) + return NULL; + + while (data [len]) len++; + + res = mono_string_new_utf16_checked (domain, data, MIN (len, max_len), &error); + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + return NULL; + } + return res; +} + +gpointer +mono_array_to_savearray (MonoArray *array) +{ + if (!array) + return NULL; + + g_assert_not_reached (); + return NULL; +} + +gpointer +mono_array_to_lparray (MonoArray *array) +{ +#ifndef DISABLE_COM + gpointer *nativeArray = NULL; + int nativeArraySize = 0; + + int i = 0; + MonoClass *klass; + MonoError error; +#endif + + if (!array) + return NULL; +#ifndef DISABLE_COM + error_init (&error); + klass = array->obj.vtable->klass; + + switch (klass->element_class->byval_arg.type) { + case MONO_TYPE_VOID: + g_assert_not_reached (); + break; + case MONO_TYPE_CLASS: + nativeArraySize = array->max_length; + nativeArray = (void **)g_malloc (sizeof(gpointer) * nativeArraySize); + for(i = 0; i < nativeArraySize; ++i) { + nativeArray[i] = mono_cominterop_get_com_interface (((MonoObject **)array->vector)[i], klass->element_class, &error); + if (mono_error_set_pending_exception (&error)) + break; + } + return nativeArray; + case MONO_TYPE_U1: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_I1: + case MONO_TYPE_U2: + case MONO_TYPE_CHAR: + case MONO_TYPE_I2: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_U8: + case MONO_TYPE_I8: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + case MONO_TYPE_VALUETYPE: + case MONO_TYPE_PTR: + /* nothing to do */ + break; + case MONO_TYPE_GENERICINST: + case MONO_TYPE_OBJECT: + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_STRING: + default: + g_warning ("type 0x%x not handled", klass->element_class->byval_arg.type); + g_assert_not_reached (); + } +#endif + return array->vector; +} + +void +mono_free_lparray (MonoArray *array, gpointer* nativeArray) +{ +#ifndef DISABLE_COM + MonoClass *klass; + + if (!array) + return; + + if (!nativeArray) + return; + klass = array->obj.vtable->klass; + + if (klass->element_class->byval_arg.type == MONO_TYPE_CLASS) + g_free (nativeArray); +#endif +} + +static void +mono_byvalarray_to_array (MonoArray *arr, gpointer native_arr, MonoClass *elclass, guint32 elnum) +{ + g_assert (arr->obj.vtable->klass->element_class == mono_defaults.char_class); + + if (elclass == mono_defaults.byte_class) { + GError *error = NULL; + guint16 *ut; + glong items_written; + + ut = g_utf8_to_utf16 ((const gchar *)native_arr, elnum, NULL, &items_written, &error); + + if (!error) { + memcpy (mono_array_addr (arr, guint16, 0), ut, items_written * sizeof (guint16)); + g_free (ut); + } + else + g_error_free (error); + } + else + g_assert_not_reached (); +} + +static void +mono_byvalarray_to_byte_array (MonoArray *arr, gpointer native_arr, guint32 elnum) +{ + mono_byvalarray_to_array (arr, native_arr, mono_defaults.byte_class, elnum); +} + +/* This is a JIT icall, it sets the pending exception and returns on error */ +static void +mono_array_to_byvalarray (gpointer native_arr, MonoArray *arr, MonoClass *elclass, guint32 elnum) +{ + g_assert (arr->obj.vtable->klass->element_class == mono_defaults.char_class); + + if (elclass == mono_defaults.byte_class) { + char *as; + GError *error = NULL; + + as = g_utf16_to_utf8 (mono_array_addr (arr, gunichar2, 0), mono_array_length (arr), NULL, NULL, &error); + if (error) { + mono_set_pending_exception (mono_get_exception_argument ("string", error->message)); + g_error_free (error); + return; + } + + memcpy (native_arr, as, MIN (strlen (as), elnum)); + g_free (as); + } else { + g_assert_not_reached (); + } +} + +static void +mono_array_to_byte_byvalarray (gpointer native_arr, MonoArray *arr, guint32 elnum) +{ + mono_array_to_byvalarray (native_arr, arr, mono_defaults.byte_class, elnum); +} + +static MonoStringBuilder * +mono_string_builder_new (int starting_string_length) +{ + static MonoClass *string_builder_class; + static MonoMethod *sb_ctor; + static void *args [1]; + + MonoError error; + int initial_len = starting_string_length; + + if (initial_len < 0) + initial_len = 0; + + if (!sb_ctor) { + MonoMethodDesc *desc; + MonoMethod *m; + + string_builder_class = mono_class_get_string_builder_class (); + g_assert (string_builder_class); + desc = mono_method_desc_new (":.ctor(int)", FALSE); + m = mono_method_desc_search_in_class (desc, string_builder_class); + g_assert (m); + mono_method_desc_free (desc); + mono_memory_barrier (); + sb_ctor = m; + } + + // We make a new array in the _to_builder function, so this + // array will always be garbage collected. + args [0] = &initial_len; + + MonoStringBuilder *sb = (MonoStringBuilder*)mono_object_new_checked (mono_domain_get (), string_builder_class, &error); + mono_error_assert_ok (&error); + + MonoObject *exc; + mono_runtime_try_invoke (sb_ctor, sb, args, &exc, &error); + g_assert (exc == NULL); + mono_error_assert_ok (&error); + + g_assert (sb->chunkChars->max_length >= initial_len); + + return sb; +} + +static void +mono_string_utf16_to_builder_copy (MonoStringBuilder *sb, gunichar2 *text, size_t string_len) +{ + gunichar2 *charDst = (gunichar2 *)sb->chunkChars->vector; + gunichar2 *charSrc = (gunichar2 *)text; + memcpy (charDst, charSrc, sizeof (gunichar2) * string_len); + + sb->chunkLength = string_len; + + return; +} + +MonoStringBuilder * +mono_string_utf16_to_builder2 (gunichar2 *text) +{ + if (!text) + return NULL; + + int len; + for (len = 0; text [len] != 0; ++len); + + MonoStringBuilder *sb = mono_string_builder_new (len); + mono_string_utf16_to_builder (sb, text); + + return sb; +} + +void +mono_string_utf8_to_builder (MonoStringBuilder *sb, char *text) +{ + if (!sb || !text) + return; + + GError *error = NULL; + glong copied; + gunichar2* ut = g_utf8_to_utf16 (text, strlen (text), NULL, &copied, &error); + int capacity = mono_string_builder_capacity (sb); + + if (copied > capacity) + copied = capacity; + + if (!error) { + MONO_OBJECT_SETREF (sb, chunkPrevious, NULL); + mono_string_utf16_to_builder_copy (sb, ut, copied); + } else + g_error_free (error); + + g_free (ut); +} + +MonoStringBuilder * +mono_string_utf8_to_builder2 (char *text) +{ + if (!text) + return NULL; + + int len = strlen (text); + MonoStringBuilder *sb = mono_string_builder_new (len); + mono_string_utf8_to_builder (sb, text); + + return sb; +} + +void +mono_string_utf16_to_builder (MonoStringBuilder *sb, gunichar2 *text) +{ + if (!sb || !text) + return; + + guint32 len; + for (len = 0; text [len] != 0; ++len); + + if (len > mono_string_builder_capacity (sb)) + len = mono_string_builder_capacity (sb); + + mono_string_utf16_to_builder_copy (sb, text, len); +} + +/** + * mono_string_builder_to_utf8: + * \param sb the string builder + * + * Converts to utf8 the contents of the \c MonoStringBuilder . + * + * \returns a utf8 string with the contents of the \c StringBuilder . + * + * The return value must be released with mono_marshal_free. + * + * This is a JIT icall, it sets the pending exception and returns NULL on error. + */ +gchar* +mono_string_builder_to_utf8 (MonoStringBuilder *sb) +{ + MonoError error; + GError *gerror = NULL; + glong byte_count; + if (!sb) + return NULL; + + gunichar2 *str_utf16 = mono_string_builder_to_utf16 (sb); + + guint str_len = mono_string_builder_string_length (sb); + + gchar *tmp = g_utf16_to_utf8 (str_utf16, str_len, NULL, &byte_count, &gerror); + + if (gerror) { + g_error_free (gerror); + mono_marshal_free (str_utf16); + mono_set_pending_exception (mono_get_exception_execution_engine ("Failed to convert StringBuilder from utf16 to utf8")); + return NULL; + } else { + guint len = mono_string_builder_capacity (sb) + 1; + gchar *res = (gchar *)mono_marshal_alloc (MAX (byte_count+1, len * sizeof (gchar)), &error); + if (!mono_error_ok (&error)) { + mono_marshal_free (str_utf16); + g_free (tmp); + mono_error_set_pending_exception (&error); + return NULL; + } + + memcpy (res, tmp, byte_count); + res[byte_count] = '\0'; + + mono_marshal_free (str_utf16); + g_free (tmp); + return res; + } +} + +/** + * mono_string_builder_to_utf16: + * \param sb the string builder + * + * Converts to utf16 the contents of the \c MonoStringBuilder . + * + * Returns: a utf16 string with the contents of the \c StringBuilder . + * + * The return value must be released with mono_marshal_free. + * + * This is a JIT icall, it sets the pending exception and returns NULL on error. + */ +gunichar2* +mono_string_builder_to_utf16 (MonoStringBuilder *sb) +{ + MonoError error; + + if (!sb) + return NULL; + + g_assert (sb->chunkChars); + + guint len = mono_string_builder_capacity (sb); + + if (len == 0) + len = 1; + + gunichar2 *str = (gunichar2 *)mono_marshal_alloc ((len + 1) * sizeof (gunichar2), &error); + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + return NULL; + } + + str[len] = '\0'; + + if (len == 0) + return str; + + MonoStringBuilder* chunk = sb; + do { + if (chunk->chunkLength > 0) { + // Check that we will not overrun our boundaries. + gunichar2 *source = (gunichar2 *)chunk->chunkChars->vector; + + if (chunk->chunkLength <= len) { + memcpy (str + chunk->chunkOffset, source, chunk->chunkLength * sizeof(gunichar2)); + } else { + g_error ("A chunk in the StringBuilder had a length longer than expected from the offset."); + } + + len -= chunk->chunkLength; + } + chunk = chunk->chunkPrevious; + } while (chunk != NULL); + + return str; +} + +#ifndef HOST_WIN32 +/* This is a JIT icall, it sets the pending exception and returns NULL on error. */ +static gpointer +mono_string_to_utf8str (MonoString *s) +{ + MonoError error; + char *result = mono_string_to_utf8_checked (s, &error); + mono_error_set_pending_exception (&error); + return result; +} +#endif + +gpointer +mono_string_to_ansibstr (MonoString *string_obj) +{ + g_error ("UnmanagedMarshal.BStr is not implemented."); + return NULL; +} + +/** + * mono_string_to_byvalstr: + * \param dst Where to store the null-terminated utf8 decoded string. + * \param src the \c MonoString to copy. + * \param size the maximum number of bytes to copy. + * + * Copies the \c MonoString pointed to by \p src as a utf8 string + * into \p dst, it copies at most \p size bytes into the destination. + */ +void +mono_string_to_byvalstr (gpointer dst, MonoString *src, int size) +{ + MonoError error; + char *s; + int len; + + g_assert (dst != NULL); + g_assert (size > 0); + + memset (dst, 0, size); + if (!src) + return; + + s = mono_string_to_utf8_checked (src, &error); + if (mono_error_set_pending_exception (&error)) + return; + len = MIN (size, strlen (s)); + if (len >= size) + len--; + memcpy (dst, s, len); + g_free (s); +} + +/** + * mono_string_to_byvalwstr: + * \param dst Where to store the null-terminated utf16 decoded string. + * \param src the \c MonoString to copy. + * \param size the maximum number of wide characters to copy (each consumes 2 bytes) + * + * Copies the \c MonoString pointed to by \p src as a utf16 string into + * \p dst, it copies at most \p size bytes into the destination (including + * a terminating 16-bit zero terminator). + */ +void +mono_string_to_byvalwstr (gpointer dst, MonoString *src, int size) +{ + int len; + + g_assert (dst != NULL); + g_assert (size > 1); + + if (!src) { + memset (dst, 0, size * 2); + return; + } + + len = MIN (size, (mono_string_length (src))); + memcpy (dst, mono_string_chars (src), len * 2); + if (size <= mono_string_length (src)) + len--; + *((gunichar2 *) dst + len) = 0; +} + +/* this is an icall, it sets the pending exception and returns NULL on error */ +static MonoString* +mono_string_new_len_wrapper (const char *text, guint length) +{ + MonoError error; + MonoString *result = mono_string_new_len_checked (mono_domain_get (), text, length, &error); + mono_error_set_pending_exception (&error); + return result; +} + +#ifdef ENABLE_ILGEN + +/* + * mono_mb_emit_exception_marshal_directive: + * + * This function assumes ownership of MSG, which should be malloc-ed. + */ +static void +mono_mb_emit_exception_marshal_directive (MonoMethodBuilder *mb, char *msg) +{ + char *s; + + if (!mb->dynamic) { + s = mono_image_strdup (mb->method->klass->image, msg); + g_free (msg); + } else { + s = g_strdup (msg); + } + mono_mb_emit_exception_full (mb, "System.Runtime.InteropServices", "MarshalDirectiveException", s); +} +#endif /* ENABLE_ILGEN */ + +guint +mono_type_to_ldind (MonoType *type) +{ + if (type->byref) + return CEE_LDIND_I; + +handle_enum: + switch (type->type) { + case MONO_TYPE_I1: + return CEE_LDIND_I1; + case MONO_TYPE_U1: + case MONO_TYPE_BOOLEAN: + return CEE_LDIND_U1; + case MONO_TYPE_I2: + return CEE_LDIND_I2; + case MONO_TYPE_U2: + case MONO_TYPE_CHAR: + return CEE_LDIND_U2; + case MONO_TYPE_I4: + return CEE_LDIND_I4; + case MONO_TYPE_U4: + return CEE_LDIND_U4; + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_PTR: + case MONO_TYPE_FNPTR: + return CEE_LDIND_I; + case MONO_TYPE_CLASS: + case MONO_TYPE_STRING: + case MONO_TYPE_OBJECT: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_ARRAY: + return CEE_LDIND_REF; + case MONO_TYPE_I8: + case MONO_TYPE_U8: + return CEE_LDIND_I8; + case MONO_TYPE_R4: + return CEE_LDIND_R4; + case MONO_TYPE_R8: + return CEE_LDIND_R8; + case MONO_TYPE_VALUETYPE: + if (type->data.klass->enumtype) { + type = mono_class_enum_basetype (type->data.klass); + goto handle_enum; + } + return CEE_LDOBJ; + case MONO_TYPE_TYPEDBYREF: + return CEE_LDOBJ; + case MONO_TYPE_GENERICINST: + type = &type->data.generic_class->container_class->byval_arg; + goto handle_enum; + default: + g_error ("unknown type 0x%02x in type_to_ldind", type->type); + } + return -1; +} + +guint +mono_type_to_stind (MonoType *type) +{ + if (type->byref) + return MONO_TYPE_IS_REFERENCE (type) ? CEE_STIND_REF : CEE_STIND_I; + + +handle_enum: + switch (type->type) { + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_BOOLEAN: + return CEE_STIND_I1; + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_CHAR: + return CEE_STIND_I2; + case MONO_TYPE_I4: + case MONO_TYPE_U4: + return CEE_STIND_I4; + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_PTR: + case MONO_TYPE_FNPTR: + return CEE_STIND_I; + case MONO_TYPE_CLASS: + case MONO_TYPE_STRING: + case MONO_TYPE_OBJECT: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_ARRAY: + return CEE_STIND_REF; + case MONO_TYPE_I8: + case MONO_TYPE_U8: + return CEE_STIND_I8; + case MONO_TYPE_R4: + return CEE_STIND_R4; + case MONO_TYPE_R8: + return CEE_STIND_R8; + case MONO_TYPE_VALUETYPE: + if (type->data.klass->enumtype) { + type = mono_class_enum_basetype (type->data.klass); + goto handle_enum; + } + return CEE_STOBJ; + case MONO_TYPE_TYPEDBYREF: + return CEE_STOBJ; + case MONO_TYPE_GENERICINST: + type = &type->data.generic_class->container_class->byval_arg; + goto handle_enum; + default: + g_error ("unknown type 0x%02x in type_to_stind", type->type); + } + return -1; +} + +#ifdef ENABLE_ILGEN + +static void +emit_ptr_to_object_conv (MonoMethodBuilder *mb, MonoType *type, MonoMarshalConv conv, MonoMarshalSpec *mspec) +{ + switch (conv) { + case MONO_MARSHAL_CONV_BOOL_I4: + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte (mb, CEE_LDIND_I4); + mono_mb_emit_byte (mb, CEE_BRFALSE_S); + mono_mb_emit_byte (mb, 3); + mono_mb_emit_byte (mb, CEE_LDC_I4_1); + mono_mb_emit_byte (mb, CEE_BR_S); + mono_mb_emit_byte (mb, 1); + mono_mb_emit_byte (mb, CEE_LDC_I4_0); + mono_mb_emit_byte (mb, CEE_STIND_I1); + break; + case MONO_MARSHAL_CONV_BOOL_VARIANTBOOL: + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte (mb, CEE_LDIND_I2); + mono_mb_emit_byte (mb, CEE_BRFALSE_S); + mono_mb_emit_byte (mb, 3); + mono_mb_emit_byte (mb, CEE_LDC_I4_1); + mono_mb_emit_byte (mb, CEE_BR_S); + mono_mb_emit_byte (mb, 1); + mono_mb_emit_byte (mb, CEE_LDC_I4_0); + mono_mb_emit_byte (mb, CEE_STIND_I1); + break; + case MONO_MARSHAL_CONV_ARRAY_BYVALARRAY: { + MonoClass *eklass = NULL; + int esize; + + if (type->type == MONO_TYPE_SZARRAY) { + eklass = type->data.klass; + } else { + g_assert_not_reached (); + } + + esize = mono_class_native_size (eklass, NULL); + + /* create a new array */ + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_icon (mb, mspec->data.array_data.num_elem); + mono_mb_emit_op (mb, CEE_NEWARR, eklass); + mono_mb_emit_byte (mb, CEE_STIND_REF); + + if (eklass->blittable) { + /* copy the elements */ + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoArray, vector)); + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_icon (mb, mspec->data.array_data.num_elem * esize); + mono_mb_emit_byte (mb, CEE_PREFIX1); + mono_mb_emit_byte (mb, CEE_CPBLK); + } + else { + int array_var, src_var, dst_var, index_var; + guint32 label2, label3; + + array_var = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + src_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + dst_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + + /* set array_var */ + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_stloc (mb, array_var); + + /* save the old src pointer */ + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_stloc (mb, src_var); + /* save the old dst pointer */ + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_stloc (mb, dst_var); + + /* Emit marshalling loop */ + index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + mono_mb_emit_byte (mb, CEE_LDC_I4_0); + mono_mb_emit_stloc (mb, index_var); + + /* Loop header */ + label2 = mono_mb_get_label (mb); + mono_mb_emit_ldloc (mb, index_var); + mono_mb_emit_ldloc (mb, array_var); + mono_mb_emit_byte (mb, CEE_LDLEN); + label3 = mono_mb_emit_branch (mb, CEE_BGE); + + /* src is already set */ + + /* Set dst */ + mono_mb_emit_ldloc (mb, array_var); + mono_mb_emit_ldloc (mb, index_var); + mono_mb_emit_op (mb, CEE_LDELEMA, eklass); + mono_mb_emit_stloc (mb, 1); + + /* Do the conversion */ + emit_struct_conv (mb, eklass, TRUE); + + /* Loop footer */ + mono_mb_emit_add_to_local (mb, index_var, 1); + + mono_mb_emit_branch_label (mb, CEE_BR, label2); + + mono_mb_patch_branch (mb, label3); + + /* restore the old src pointer */ + mono_mb_emit_ldloc (mb, src_var); + mono_mb_emit_stloc (mb, 0); + /* restore the old dst pointer */ + mono_mb_emit_ldloc (mb, dst_var); + mono_mb_emit_stloc (mb, 1); + } + break; + } + case MONO_MARSHAL_CONV_ARRAY_BYVALCHARARRAY: { + MonoClass *eclass = mono_defaults.char_class; + + /* create a new array */ + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_icon (mb, mspec->data.array_data.num_elem); + mono_mb_emit_op (mb, CEE_NEWARR, eclass); + mono_mb_emit_byte (mb, CEE_STIND_REF); + + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_icon (mb, mspec->data.array_data.num_elem); + mono_mb_emit_icall (mb, mono_byvalarray_to_byte_array); + break; + } + case MONO_MARSHAL_CONV_STR_BYVALSTR: + if (mspec && mspec->native == MONO_NATIVE_BYVALTSTR && mspec->data.array_data.num_elem) { + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_icon (mb, mspec->data.array_data.num_elem); + mono_mb_emit_icall (mb, mono_string_from_byvalstr); + } else { + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_icall (mb, ves_icall_string_new_wrapper); + } + mono_mb_emit_byte (mb, CEE_STIND_REF); + break; + case MONO_MARSHAL_CONV_STR_BYVALWSTR: + if (mspec && mspec->native == MONO_NATIVE_BYVALTSTR && mspec->data.array_data.num_elem) { + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_icon (mb, mspec->data.array_data.num_elem); + mono_mb_emit_icall (mb, mono_string_from_byvalwstr); + } else { + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_icall (mb, ves_icall_mono_string_from_utf16); + } + mono_mb_emit_byte (mb, CEE_STIND_REF); + break; + case MONO_MARSHAL_CONV_STR_LPTSTR: + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte (mb, CEE_LDIND_I); +#ifdef TARGET_WIN32 + mono_mb_emit_icall (mb, ves_icall_mono_string_from_utf16); +#else + mono_mb_emit_icall (mb, ves_icall_string_new_wrapper); +#endif + mono_mb_emit_byte (mb, CEE_STIND_REF); + break; + + // In Mono historically LPSTR was treated as a UTF8STR + case MONO_MARSHAL_CONV_STR_LPSTR: + case MONO_MARSHAL_CONV_STR_UTF8STR: + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_icall (mb, ves_icall_string_new_wrapper); + mono_mb_emit_byte (mb, CEE_STIND_REF); + break; + case MONO_MARSHAL_CONV_STR_LPWSTR: + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_icall (mb, ves_icall_mono_string_from_utf16); + mono_mb_emit_byte (mb, CEE_STIND_REF); + break; + case MONO_MARSHAL_CONV_OBJECT_STRUCT: { + MonoClass *klass = mono_class_from_mono_type (type); + int src_var, dst_var; + + src_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + dst_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + + /* *dst = new object */ + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_op (mb, CEE_MONO_NEWOBJ, klass); + mono_mb_emit_byte (mb, CEE_STIND_REF); + + /* save the old src pointer */ + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_stloc (mb, src_var); + /* save the old dst pointer */ + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_stloc (mb, dst_var); + + /* dst = pointer to newly created object data */ + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_icon (mb, sizeof (MonoObject)); + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_stloc (mb, 1); + + emit_struct_conv (mb, klass, TRUE); + + /* restore the old src pointer */ + mono_mb_emit_ldloc (mb, src_var); + mono_mb_emit_stloc (mb, 0); + /* restore the old dst pointer */ + mono_mb_emit_ldloc (mb, dst_var); + mono_mb_emit_stloc (mb, 1); + break; + } + case MONO_MARSHAL_CONV_DEL_FTN: { + MonoClass *klass = mono_class_from_mono_type (type); + + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_op (mb, CEE_MONO_CLASSCONST, klass); + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_icall (mb, mono_ftnptr_to_delegate); + mono_mb_emit_byte (mb, CEE_STIND_REF); + break; + } + 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: + case MONO_MARSHAL_CONV_OBJECT_IUNKNOWN: + case MONO_MARSHAL_CONV_OBJECT_IDISPATCH: + mono_cominterop_emit_ptr_to_object_conv (mb, type, conv, mspec); + break; +#endif /* DISABLE_COM */ + + case MONO_MARSHAL_CONV_SAFEHANDLE: { + /* + * Passing SafeHandles as ref does not allow the unmanaged code + * to change the SafeHandle value. If the value is changed, + * we should issue a diagnostic exception (NotSupportedException) + * that informs the user that changes to handles in unmanaged code + * is not supported. + * + * Since we currently have no access to the original + * SafeHandle that was used during the marshalling, + * for now we just ignore this, and ignore/discard any + * changes that might have happened to the handle. + */ + break; + } + + case MONO_MARSHAL_CONV_HANDLEREF: { + /* + * Passing HandleRefs in a struct that is ref()ed does not + * copy the values back to the HandleRef + */ + break; + } + + case MONO_MARSHAL_CONV_STR_BSTR: + case MONO_MARSHAL_CONV_STR_ANSIBSTR: + case MONO_MARSHAL_CONV_STR_TBSTR: + case MONO_MARSHAL_CONV_ARRAY_SAVEARRAY: + default: { + char *msg = g_strdup_printf ("marshaling conversion %d not implemented", conv); + + mono_mb_emit_exception_marshal_directive (mb, msg); + break; + } + } +} + +static gpointer +conv_to_icall (MonoMarshalConv conv, int *ind_store_type) +{ + int dummy; + if (!ind_store_type) + ind_store_type = &dummy; + *ind_store_type = CEE_STIND_I; + switch (conv) { + case MONO_MARSHAL_CONV_STR_LPWSTR: + return mono_marshal_string_to_utf16; + case MONO_MARSHAL_CONV_LPWSTR_STR: + *ind_store_type = CEE_STIND_REF; + return ves_icall_mono_string_from_utf16; + case MONO_MARSHAL_CONV_LPTSTR_STR: + *ind_store_type = CEE_STIND_REF; + return ves_icall_string_new_wrapper; + case MONO_MARSHAL_CONV_UTF8STR_STR: + case MONO_MARSHAL_CONV_LPSTR_STR: + *ind_store_type = CEE_STIND_REF; + return ves_icall_string_new_wrapper; + case MONO_MARSHAL_CONV_STR_LPTSTR: +#ifdef TARGET_WIN32 + return mono_marshal_string_to_utf16; +#else + return mono_string_to_utf8str; +#endif + // In Mono historically LPSTR was treated as a UTF8STR + case MONO_MARSHAL_CONV_STR_UTF8STR: + case MONO_MARSHAL_CONV_STR_LPSTR: + return mono_string_to_utf8str; + case MONO_MARSHAL_CONV_STR_BSTR: + return mono_string_to_bstr; + case MONO_MARSHAL_CONV_BSTR_STR: + *ind_store_type = CEE_STIND_REF; + return mono_string_from_bstr_icall; + case MONO_MARSHAL_CONV_STR_TBSTR: + case MONO_MARSHAL_CONV_STR_ANSIBSTR: + return mono_string_to_ansibstr; + case MONO_MARSHAL_CONV_SB_UTF8STR: + case MONO_MARSHAL_CONV_SB_LPSTR: + return mono_string_builder_to_utf8; + case MONO_MARSHAL_CONV_SB_LPTSTR: +#ifdef TARGET_WIN32 + return mono_string_builder_to_utf16; +#else + return mono_string_builder_to_utf8; +#endif + case MONO_MARSHAL_CONV_SB_LPWSTR: + return mono_string_builder_to_utf16; + case MONO_MARSHAL_CONV_ARRAY_SAVEARRAY: + return mono_array_to_savearray; + case MONO_MARSHAL_CONV_ARRAY_LPARRAY: + return mono_array_to_lparray; + case MONO_MARSHAL_FREE_LPARRAY: + return mono_free_lparray; + case MONO_MARSHAL_CONV_DEL_FTN: + return mono_delegate_to_ftnptr; + case MONO_MARSHAL_CONV_FTN_DEL: + *ind_store_type = CEE_STIND_REF; + return mono_ftnptr_to_delegate; + case MONO_MARSHAL_CONV_UTF8STR_SB: + case MONO_MARSHAL_CONV_LPSTR_SB: + *ind_store_type = CEE_STIND_REF; + return mono_string_utf8_to_builder; + case MONO_MARSHAL_CONV_LPTSTR_SB: + *ind_store_type = CEE_STIND_REF; +#ifdef TARGET_WIN32 + return mono_string_utf16_to_builder; +#else + return mono_string_utf8_to_builder; +#endif + case MONO_MARSHAL_CONV_LPWSTR_SB: + *ind_store_type = CEE_STIND_REF; + return mono_string_utf16_to_builder; + case MONO_MARSHAL_FREE_ARRAY: + return mono_marshal_free_array; + case MONO_MARSHAL_CONV_STR_BYVALSTR: + return mono_string_to_byvalstr; + case MONO_MARSHAL_CONV_STR_BYVALWSTR: + return mono_string_to_byvalwstr; + default: + g_assert_not_reached (); + } + + return NULL; +} + +static void +emit_object_to_ptr_conv (MonoMethodBuilder *mb, MonoType *type, MonoMarshalConv conv, MonoMarshalSpec *mspec) +{ + int pos; + int stind_op; + + switch (conv) { + case MONO_MARSHAL_CONV_BOOL_I4: + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte (mb, CEE_LDIND_U1); + mono_mb_emit_byte (mb, CEE_STIND_I4); + break; + case MONO_MARSHAL_CONV_BOOL_VARIANTBOOL: + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte (mb, CEE_LDIND_U1); + mono_mb_emit_byte (mb, CEE_NEG); + mono_mb_emit_byte (mb, CEE_STIND_I2); + break; + // In Mono historically LPSTR was treated as a UTF8STR + case MONO_MARSHAL_CONV_STR_UTF8STR: + case MONO_MARSHAL_CONV_STR_LPWSTR: + case MONO_MARSHAL_CONV_STR_LPSTR: + case MONO_MARSHAL_CONV_STR_LPTSTR: + case MONO_MARSHAL_CONV_STR_BSTR: + case MONO_MARSHAL_CONV_STR_ANSIBSTR: + case MONO_MARSHAL_CONV_STR_TBSTR: { + int pos; + + /* free space if free == true */ + mono_mb_emit_ldloc (mb, 2); + pos = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S); + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_icall (mb, g_free); + mono_mb_patch_short_branch (mb, pos); + + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_icall (mb, conv_to_icall (conv, &stind_op)); + mono_mb_emit_byte (mb, stind_op); + break; + } + case MONO_MARSHAL_CONV_ARRAY_SAVEARRAY: + case MONO_MARSHAL_CONV_ARRAY_LPARRAY: + case MONO_MARSHAL_CONV_DEL_FTN: + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_icall (mb, conv_to_icall (conv, &stind_op)); + mono_mb_emit_byte (mb, stind_op); + break; + case MONO_MARSHAL_CONV_STR_BYVALSTR: + case MONO_MARSHAL_CONV_STR_BYVALWSTR: { + g_assert (mspec); + + mono_mb_emit_ldloc (mb, 1); /* dst */ + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte (mb, CEE_LDIND_REF); /* src String */ + mono_mb_emit_icon (mb, mspec->data.array_data.num_elem); + mono_mb_emit_icall (mb, conv_to_icall (conv, NULL)); + break; + } + case MONO_MARSHAL_CONV_ARRAY_BYVALARRAY: { + MonoClass *eklass = NULL; + int esize; + + 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 (); + } + + if (eklass->valuetype) + esize = mono_class_native_size (eklass, NULL); + else + esize = sizeof (gpointer); + + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + pos = mono_mb_emit_branch (mb, CEE_BRFALSE); + + if (eklass->blittable) { + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoArray, vector)); + mono_mb_emit_icon (mb, mspec->data.array_data.num_elem * esize); + mono_mb_emit_byte (mb, CEE_PREFIX1); + mono_mb_emit_byte (mb, CEE_CPBLK); + } else { + int array_var, src_var, dst_var, index_var; + guint32 label2, label3; + + array_var = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + src_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + dst_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + + /* set array_var */ + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_stloc (mb, array_var); + + /* save the old src pointer */ + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_stloc (mb, src_var); + /* save the old dst pointer */ + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_stloc (mb, dst_var); + + /* Emit marshalling loop */ + index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + mono_mb_emit_byte (mb, CEE_LDC_I4_0); + mono_mb_emit_stloc (mb, index_var); + + /* Loop header */ + label2 = mono_mb_get_label (mb); + mono_mb_emit_ldloc (mb, index_var); + mono_mb_emit_ldloc (mb, array_var); + mono_mb_emit_byte (mb, CEE_LDLEN); + label3 = mono_mb_emit_branch (mb, CEE_BGE); + + /* Set src */ + mono_mb_emit_ldloc (mb, array_var); + mono_mb_emit_ldloc (mb, index_var); + mono_mb_emit_op (mb, CEE_LDELEMA, eklass); + mono_mb_emit_stloc (mb, 0); + + /* dst is already set */ + + /* Do the conversion */ + emit_struct_conv (mb, eklass, FALSE); + + /* Loop footer */ + mono_mb_emit_add_to_local (mb, index_var, 1); + + mono_mb_emit_branch_label (mb, CEE_BR, label2); + + mono_mb_patch_branch (mb, label3); + + /* restore the old src pointer */ + mono_mb_emit_ldloc (mb, src_var); + mono_mb_emit_stloc (mb, 0); + /* restore the old dst pointer */ + mono_mb_emit_ldloc (mb, dst_var); + mono_mb_emit_stloc (mb, 1); + } + + mono_mb_patch_branch (mb, pos); + break; + } + case MONO_MARSHAL_CONV_ARRAY_BYVALCHARARRAY: { + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + pos = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S); + + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_icon (mb, mspec->data.array_data.num_elem); + mono_mb_emit_icall (mb, mono_array_to_byte_byvalarray); + mono_mb_patch_short_branch (mb, pos); + break; + } + case MONO_MARSHAL_CONV_OBJECT_STRUCT: { + int src_var, dst_var; + + src_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + dst_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte (mb, CEE_LDIND_I); + pos = mono_mb_emit_branch (mb, CEE_BRFALSE); + + /* save the old src pointer */ + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_stloc (mb, src_var); + /* save the old dst pointer */ + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_stloc (mb, dst_var); + + /* src = pointer to object data */ + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_icon (mb, sizeof (MonoObject)); + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_stloc (mb, 0); + + emit_struct_conv (mb, mono_class_from_mono_type (type), FALSE); + + /* restore the old src pointer */ + mono_mb_emit_ldloc (mb, src_var); + mono_mb_emit_stloc (mb, 0); + /* restore the old dst pointer */ + mono_mb_emit_ldloc (mb, dst_var); + mono_mb_emit_stloc (mb, 1); + + mono_mb_patch_branch (mb, pos); + break; + } + +#ifndef DISABLE_COM + case MONO_MARSHAL_CONV_OBJECT_INTERFACE: + case MONO_MARSHAL_CONV_OBJECT_IDISPATCH: + case MONO_MARSHAL_CONV_OBJECT_IUNKNOWN: + mono_cominterop_emit_object_to_ptr_conv (mb, type, conv, mspec); + break; +#endif /* DISABLE_COM */ + + case MONO_MARSHAL_CONV_SAFEHANDLE: { + int pos; + + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte (mb, CEE_LDIND_I); + pos = mono_mb_emit_branch (mb, CEE_BRTRUE); + mono_mb_emit_exception (mb, "ArgumentNullException", NULL); + mono_mb_patch_branch (mb, pos); + + /* Pull the handle field from SafeHandle */ + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoSafeHandle, handle)); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_byte (mb, CEE_STIND_I); + break; + } + + case MONO_MARSHAL_CONV_HANDLEREF: { + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoHandleRef, handle)); + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_byte (mb, CEE_STIND_I); + break; + } + + default: { + g_error ("marshalling conversion %d not implemented", conv); + } + } +} + +static int +offset_of_first_nonstatic_field (MonoClass *klass) +{ + int i; + int fcount = mono_class_get_field_count (klass); + mono_class_setup_fields (klass); + for (i = 0; i < fcount; i++) { + if (!(klass->fields[i].type->attrs & FIELD_ATTRIBUTE_STATIC) && !mono_field_is_deleted (&klass->fields[i])) + return klass->fields[i].offset - sizeof (MonoObject); + } + + return 0; +} + +static gboolean +get_fixed_buffer_attr (MonoClassField *field, MonoType **out_etype, int *out_len) +{ + MonoError error; + MonoCustomAttrInfo *cinfo; + MonoCustomAttrEntry *attr; + int aindex; + + cinfo = mono_custom_attrs_from_field_checked (field->parent, field, &error); + if (!is_ok (&error)) + return FALSE; + attr = NULL; + if (cinfo) { + for (aindex = 0; aindex < cinfo->num_attrs; ++aindex) { + MonoClass *ctor_class = cinfo->attrs [aindex].ctor->klass; + if (mono_class_has_parent (ctor_class, mono_class_get_fixed_buffer_attribute_class ())) { + attr = &cinfo->attrs [aindex]; + break; + } + } + } + if (attr) { + MonoArray *typed_args, *named_args; + CattrNamedArg *arginfo; + MonoObject *o; + + mono_reflection_create_custom_attr_data_args (mono_defaults.corlib, attr->ctor, attr->data, attr->data_size, &typed_args, &named_args, &arginfo, &error); + if (!is_ok (&error)) + return FALSE; + g_assert (mono_array_length (typed_args) == 2); + + /* typed args */ + o = mono_array_get (typed_args, MonoObject*, 0); + *out_etype = monotype_cast (o)->type; + o = mono_array_get (typed_args, MonoObject*, 1); + g_assert (o->vtable->klass == mono_defaults.int32_class); + *out_len = *(gint32*)mono_object_unbox (o); + g_free (arginfo); + } + if (cinfo && !cinfo->cached) + mono_custom_attrs_free (cinfo); + return attr != NULL; +} + +static void +emit_fixed_buf_conv (MonoMethodBuilder *mb, MonoType *type, MonoType *etype, int len, gboolean to_object, int *out_usize) +{ + MonoClass *klass = mono_class_from_mono_type (type); + MonoClass *eklass = mono_class_from_mono_type (etype); + int esize; + + esize = mono_class_native_size (eklass, NULL); + + MonoMarshalNative string_encoding = klass->unicode ? MONO_NATIVE_LPWSTR : MONO_NATIVE_LPSTR; + int usize = mono_class_value_size (eklass, NULL); + int msize = mono_class_value_size (eklass, NULL); + + //printf ("FIXED: %s %d %d\n", mono_type_full_name (type), eklass->blittable, string_encoding); + + if (eklass->blittable) { + /* copy the elements */ + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_icon (mb, len * esize); + mono_mb_emit_byte (mb, CEE_PREFIX1); + mono_mb_emit_byte (mb, CEE_CPBLK); + } else { + int index_var; + guint32 label2, label3; + + /* Emit marshalling loop */ + index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + mono_mb_emit_byte (mb, CEE_LDC_I4_0); + mono_mb_emit_stloc (mb, index_var); + + /* Loop header */ + label2 = mono_mb_get_label (mb); + mono_mb_emit_ldloc (mb, index_var); + mono_mb_emit_icon (mb, len); + label3 = mono_mb_emit_branch (mb, CEE_BGE); + + /* src/dst is already set */ + + /* Do the conversion */ + MonoTypeEnum t = etype->type; + switch (t) { + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_CHAR: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_PTR: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_ldloc (mb, 0); + if (t == MONO_TYPE_CHAR && string_encoding != MONO_NATIVE_LPWSTR) { + if (to_object) { + mono_mb_emit_byte (mb, CEE_LDIND_U1); + mono_mb_emit_byte (mb, CEE_STIND_I2); + } else { + mono_mb_emit_byte (mb, CEE_LDIND_U2); + mono_mb_emit_byte (mb, CEE_STIND_I1); + } + usize = 1; + } else { + mono_mb_emit_byte (mb, mono_type_to_ldind (etype)); + mono_mb_emit_byte (mb, mono_type_to_stind (etype)); + } + break; + default: + g_assert_not_reached (); + break; + } + + if (to_object) { + mono_mb_emit_add_to_local (mb, 0, usize); + mono_mb_emit_add_to_local (mb, 1, msize); + } else { + mono_mb_emit_add_to_local (mb, 0, msize); + mono_mb_emit_add_to_local (mb, 1, usize); + } + + /* Loop footer */ + mono_mb_emit_add_to_local (mb, index_var, 1); + + mono_mb_emit_branch_label (mb, CEE_BR, label2); + + mono_mb_patch_branch (mb, label3); + } + + *out_usize = usize * len; +} + +static void +emit_struct_conv_full (MonoMethodBuilder *mb, MonoClass *klass, gboolean to_object, + int offset_of_first_child_field, MonoMarshalNative string_encoding) +{ + MonoMarshalType *info; + int i; + + if (klass->parent) + emit_struct_conv_full (mb, klass->parent, to_object, offset_of_first_nonstatic_field (klass), string_encoding); + + info = mono_marshal_load_type_info (klass); + + if (info->native_size == 0) + return; + + if (klass->blittable) { + int usize = mono_class_value_size (klass, NULL); + g_assert (usize == info->native_size); + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_icon (mb, usize); + mono_mb_emit_byte (mb, CEE_PREFIX1); + mono_mb_emit_byte (mb, CEE_CPBLK); + + if (to_object) { + mono_mb_emit_add_to_local (mb, 0, usize); + mono_mb_emit_add_to_local (mb, 1, offset_of_first_child_field); + } else { + mono_mb_emit_add_to_local (mb, 0, offset_of_first_child_field); + mono_mb_emit_add_to_local (mb, 1, usize); + } + return; + } + + if (klass != mono_class_try_get_safehandle_class ()) { + if (mono_class_is_auto_layout (klass)) { + char *msg = g_strdup_printf ("Type %s which is passed to unmanaged code must have a StructLayout attribute.", + mono_type_full_name (&klass->byval_arg)); + mono_mb_emit_exception_marshal_directive (mb, msg); + return; + } + } + + for (i = 0; i < info->num_fields; i++) { + MonoMarshalNative ntype; + MonoMarshalConv conv; + MonoType *ftype = info->fields [i].field->type; + int msize = 0; + int usize = 0; + gboolean last_field = i < (info->num_fields -1) ? 0 : 1; + + if (ftype->attrs & FIELD_ATTRIBUTE_STATIC) + continue; + + ntype = (MonoMarshalNative)mono_type_to_unmanaged (ftype, info->fields [i].mspec, TRUE, klass->unicode, &conv); + + if (last_field) { + msize = klass->instance_size - info->fields [i].field->offset; + usize = info->native_size - info->fields [i].offset; + } else { + msize = info->fields [i + 1].field->offset - info->fields [i].field->offset; + usize = info->fields [i + 1].offset - info->fields [i].offset; + } + + if (klass != mono_class_try_get_safehandle_class ()){ + /* + * FIXME: Should really check for usize==0 and msize>0, but we apply + * the layout to the managed structure as well. + */ + + if (mono_class_is_explicit_layout (klass) && (usize == 0)) { + if (MONO_TYPE_IS_REFERENCE (info->fields [i].field->type) || + ((!last_field && MONO_TYPE_IS_REFERENCE (info->fields [i + 1].field->type)))) + g_error ("Type %s which has an [ExplicitLayout] attribute cannot have a " + "reference field at the same offset as another field.", + mono_type_full_name (&klass->byval_arg)); + } + } + + switch (conv) { + case MONO_MARSHAL_CONV_NONE: { + int t; + + //XXX a byref field!?!? that's not allowed! and worse, it might miss a WB + g_assert (!ftype->byref); + if (ftype->type == MONO_TYPE_I || ftype->type == MONO_TYPE_U) { + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_byte (mb, CEE_STIND_I); + break; + } + + handle_enum: + t = ftype->type; + switch (t) { + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_CHAR: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_PTR: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_ldloc (mb, 0); + if (t == MONO_TYPE_CHAR && ntype == MONO_NATIVE_U1 && string_encoding != MONO_NATIVE_LPWSTR) { + if (to_object) { + mono_mb_emit_byte (mb, CEE_LDIND_U1); + mono_mb_emit_byte (mb, CEE_STIND_I2); + } else { + mono_mb_emit_byte (mb, CEE_LDIND_U2); + mono_mb_emit_byte (mb, CEE_STIND_I1); + } + } else { + mono_mb_emit_byte (mb, mono_type_to_ldind (ftype)); + 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 (t == MONO_TYPE_VALUETYPE && ftype->data.klass->enumtype) { + ftype = mono_class_enum_basetype (ftype->data.klass); + goto handle_enum; + } + + src_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + dst_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + + /* save the old src pointer */ + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_stloc (mb, src_var); + /* save the old dst pointer */ + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_stloc (mb, dst_var); + + 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, mono_class_from_mono_type (ftype), to_object); + } + + /* restore the old src pointer */ + mono_mb_emit_ldloc (mb, src_var); + mono_mb_emit_stloc (mb, 0); + /* restore the old dst pointer */ + mono_mb_emit_ldloc (mb, dst_var); + mono_mb_emit_stloc (mb, 1); + break; + } + case MONO_TYPE_OBJECT: { +#ifndef DISABLE_COM + if (to_object) { + static MonoMethod *variant_clear = NULL; + static MonoMethod *get_object_for_native_variant = NULL; + + if (!variant_clear) + variant_clear = mono_class_get_method_from_name (mono_class_get_variant_class (), "Clear", 0); + if (!get_object_for_native_variant) + get_object_for_native_variant = mono_class_get_method_from_name (mono_defaults.marshal_class, "GetObjectForNativeVariant", 1); + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_managed_call (mb, get_object_for_native_variant, NULL); + mono_mb_emit_byte (mb, CEE_STIND_REF); + + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_managed_call (mb, variant_clear, NULL); + } + else { + static MonoMethod *get_native_variant_for_object = NULL; + + if (!get_native_variant_for_object) + get_native_variant_for_object = mono_class_get_method_from_name (mono_defaults.marshal_class, "GetNativeVariantForObject", 2); + + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte(mb, CEE_LDIND_REF); + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_managed_call (mb, get_native_variant_for_object, NULL); + } +#else + char *msg = g_strdup_printf ("COM support was disabled at compilation time."); + mono_mb_emit_exception_marshal_directive (mb, msg); +#endif + break; + } + + default: + g_warning ("marshaling type %02x not implemented", ftype->type); + g_assert_not_reached (); + } + break; + } + default: { + int src_var, dst_var; + + src_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + dst_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + + /* save the old src pointer */ + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_stloc (mb, src_var); + /* save the old dst pointer */ + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_stloc (mb, dst_var); + + if (to_object) + emit_ptr_to_object_conv (mb, ftype, conv, info->fields [i].mspec); + else + emit_object_to_ptr_conv (mb, ftype, conv, info->fields [i].mspec); + + /* restore the old src pointer */ + mono_mb_emit_ldloc (mb, src_var); + mono_mb_emit_stloc (mb, 0); + /* restore the old dst pointer */ + mono_mb_emit_ldloc (mb, dst_var); + mono_mb_emit_stloc (mb, 1); + } + } + + if (to_object) { + mono_mb_emit_add_to_local (mb, 0, usize); + mono_mb_emit_add_to_local (mb, 1, msize); + } else { + mono_mb_emit_add_to_local (mb, 0, msize); + mono_mb_emit_add_to_local (mb, 1, usize); + } + } +} + +static void +emit_struct_conv (MonoMethodBuilder *mb, MonoClass *klass, gboolean to_object) +{ + emit_struct_conv_full (mb, klass, to_object, 0, (MonoMarshalNative)-1); +} + +static void +emit_struct_free (MonoMethodBuilder *mb, MonoClass *klass, int struct_var) +{ + /* Call DestroyStructure */ + /* FIXME: Only do this if needed */ + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_op (mb, CEE_MONO_CLASSCONST, klass); + mono_mb_emit_ldloc (mb, struct_var); + mono_mb_emit_icall (mb, mono_struct_delete_old); +} + +static void +emit_thread_interrupt_checkpoint_call (MonoMethodBuilder *mb, gpointer checkpoint_func) +{ + int pos_noabort, pos_noex; + + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_LDPTR_INT_REQ_FLAG); + mono_mb_emit_byte (mb, CEE_LDIND_U4); + pos_noabort = mono_mb_emit_branch (mb, CEE_BRFALSE); + + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN); + + mono_mb_emit_icall (mb, checkpoint_func); + /* Throw the exception returned by the checkpoint function, if any */ + mono_mb_emit_byte (mb, CEE_DUP); + pos_noex = mono_mb_emit_branch (mb, CEE_BRFALSE); + mono_mb_emit_byte (mb, CEE_THROW); + mono_mb_patch_branch (mb, pos_noex); + mono_mb_emit_byte (mb, CEE_POP); + + mono_mb_patch_branch (mb, pos_noabort); +} + +static void +emit_thread_interrupt_checkpoint (MonoMethodBuilder *mb) +{ + if (strstr (mb->name, "mono_thread_interruption_checkpoint")) + return; + + emit_thread_interrupt_checkpoint_call (mb, mono_thread_interruption_checkpoint); +} + +static void +emit_thread_force_interrupt_checkpoint (MonoMethodBuilder *mb) +{ + emit_thread_interrupt_checkpoint_call (mb, mono_thread_force_interruption_checkpoint_noraise); +} + +void +mono_marshal_emit_thread_interrupt_checkpoint (MonoMethodBuilder *mb) +{ + emit_thread_interrupt_checkpoint (mb); +} + +void +mono_marshal_emit_thread_force_interrupt_checkpoint (MonoMethodBuilder *mb) +{ + emit_thread_force_interrupt_checkpoint (mb); +} + +#endif /* ENABLE_ILGEN */ + +/* This is a JIT icall, it sets the pending exception and returns NULL on error. */ +static MonoAsyncResult * +mono_delegate_begin_invoke (MonoDelegate *delegate, gpointer *params) +{ + MonoError error; + MonoMulticastDelegate *mcast_delegate; + MonoClass *klass; + MonoMethod *method; + + g_assert (delegate); + mcast_delegate = (MonoMulticastDelegate *) delegate; + if (mcast_delegate->delegates != NULL) { + mono_set_pending_exception (mono_get_exception_argument (NULL, "The delegate must have only one target")); + return NULL; + } + +#ifndef DISABLE_REMOTING + if (delegate->target && mono_object_is_transparent_proxy (delegate->target)) { + MonoTransparentProxy* tp = (MonoTransparentProxy *)delegate->target; + if (!mono_class_is_contextbound (tp->remote_class->proxy_class) || tp->rp->context != (MonoObject *) mono_context_get ()) { + /* If the target is a proxy, make a direct call. Is proxy's work + // to make the call asynchronous. + */ + MonoMethodMessage *msg; + MonoDelegate *async_callback; + MonoObject *state; + MonoAsyncResult *ares; + MonoObject *exc; + MonoArray *out_args; + method = delegate->method; + + msg = mono_method_call_message_new (mono_marshal_method_from_wrapper (method), params, NULL, &async_callback, &state, &error); + if (mono_error_set_pending_exception (&error)) + return NULL; + ares = mono_async_result_new (mono_domain_get (), NULL, state, NULL, NULL, &error); + if (mono_error_set_pending_exception (&error)) + return NULL; + MONO_OBJECT_SETREF (ares, async_delegate, (MonoObject *)delegate); + MONO_OBJECT_SETREF (ares, async_callback, (MonoObject *)async_callback); + MONO_OBJECT_SETREF (msg, async_result, ares); + msg->call_type = CallType_BeginInvoke; + + exc = NULL; + mono_remoting_invoke ((MonoObject *)tp->rp, msg, &exc, &out_args, &error); + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + return NULL; + } + if (exc) + mono_set_pending_exception ((MonoException *) exc); + return ares; + } + } +#endif + + klass = delegate->object.vtable->klass; + + method = mono_class_get_method_from_name (klass, "BeginInvoke", -1); + if (!method) + method = mono_get_delegate_invoke (klass); + g_assert (method); + + MonoAsyncResult *result = mono_threadpool_begin_invoke (mono_domain_get (), (MonoObject*) delegate, method, params, &error); + mono_error_set_pending_exception (&error); + return result; +} + +#ifdef ENABLE_ILGEN + +int +mono_mb_emit_save_args (MonoMethodBuilder *mb, MonoMethodSignature *sig, gboolean save_this) +{ + int i, params_var, tmp_var; + + /* allocate local (pointer) *params[] */ + params_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + /* allocate local (pointer) tmp */ + tmp_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + + /* alloate space on stack to store an array of pointers to the arguments */ + mono_mb_emit_icon (mb, sizeof (gpointer) * (sig->param_count + 1)); + mono_mb_emit_byte (mb, CEE_PREFIX1); + mono_mb_emit_byte (mb, CEE_LOCALLOC); + mono_mb_emit_stloc (mb, params_var); + + /* tmp = params */ + mono_mb_emit_ldloc (mb, params_var); + mono_mb_emit_stloc (mb, tmp_var); + + if (save_this && sig->hasthis) { + mono_mb_emit_ldloc (mb, tmp_var); + mono_mb_emit_ldarg_addr (mb, 0); + mono_mb_emit_byte (mb, CEE_STIND_I); + /* tmp = tmp + sizeof (gpointer) */ + if (sig->param_count) + mono_mb_emit_add_to_local (mb, tmp_var, sizeof (gpointer)); + + } + + for (i = 0; i < sig->param_count; i++) { + mono_mb_emit_ldloc (mb, tmp_var); + mono_mb_emit_ldarg_addr (mb, i + sig->hasthis); + mono_mb_emit_byte (mb, CEE_STIND_I); + /* tmp = tmp + sizeof (gpointer) */ + if (i < (sig->param_count - 1)) + mono_mb_emit_add_to_local (mb, tmp_var, sizeof (gpointer)); + } + + return params_var; +} + +#endif /* ENABLE_ILGEN */ + +static char* +mono_signature_to_name (MonoMethodSignature *sig, const char *prefix) +{ + int i; + char *result; + GString *res = g_string_new (""); + + if (prefix) { + g_string_append (res, prefix); + g_string_append_c (res, '_'); + } + + mono_type_get_desc (res, sig->ret, FALSE); + + if (sig->hasthis) + g_string_append (res, "__this__"); + + for (i = 0; i < sig->param_count; ++i) { + g_string_append_c (res, '_'); + mono_type_get_desc (res, sig->params [i], FALSE); + } + result = res->str; + g_string_free (res, FALSE); + return result; +} + +/** + * mono_marshal_get_string_encoding: + * + * Return the string encoding which should be used for a given parameter. + */ +static MonoMarshalNative +mono_marshal_get_string_encoding (MonoMethodPInvoke *piinfo, MonoMarshalSpec *spec) +{ + /* First try the parameter marshal info */ + if (spec) { + if (spec->native == MONO_NATIVE_LPARRAY) { + if ((spec->data.array_data.elem_type != 0) && (spec->data.array_data.elem_type != MONO_NATIVE_MAX)) + return spec->data.array_data.elem_type; + } + else + return spec->native; + } + + if (!piinfo) + return MONO_NATIVE_LPSTR; + + /* Then try the method level marshal info */ + switch (piinfo->piflags & PINVOKE_ATTRIBUTE_CHAR_SET_MASK) { + case PINVOKE_ATTRIBUTE_CHAR_SET_ANSI: + return MONO_NATIVE_LPSTR; + case PINVOKE_ATTRIBUTE_CHAR_SET_UNICODE: + return MONO_NATIVE_LPWSTR; + case PINVOKE_ATTRIBUTE_CHAR_SET_AUTO: +#ifdef TARGET_WIN32 + return MONO_NATIVE_LPWSTR; +#else + return MONO_NATIVE_LPSTR; +#endif + default: + return MONO_NATIVE_LPSTR; + } +} + +static MonoMarshalConv +mono_marshal_get_string_to_ptr_conv (MonoMethodPInvoke *piinfo, MonoMarshalSpec *spec) +{ + MonoMarshalNative encoding = mono_marshal_get_string_encoding (piinfo, spec); + + switch (encoding) { + case MONO_NATIVE_LPWSTR: + return MONO_MARSHAL_CONV_STR_LPWSTR; + case MONO_NATIVE_LPSTR: + case MONO_NATIVE_VBBYREFSTR: + return MONO_MARSHAL_CONV_STR_LPSTR; + case MONO_NATIVE_LPTSTR: + return MONO_MARSHAL_CONV_STR_LPTSTR; + case MONO_NATIVE_BSTR: + return MONO_MARSHAL_CONV_STR_BSTR; + case MONO_NATIVE_UTF8STR: + return MONO_MARSHAL_CONV_STR_UTF8STR; + default: + return MONO_MARSHAL_CONV_INVALID; + } +} + +static MonoMarshalConv +mono_marshal_get_stringbuilder_to_ptr_conv (MonoMethodPInvoke *piinfo, MonoMarshalSpec *spec) +{ + MonoMarshalNative encoding = mono_marshal_get_string_encoding (piinfo, spec); + + switch (encoding) { + case MONO_NATIVE_LPWSTR: + return MONO_MARSHAL_CONV_SB_LPWSTR; + case MONO_NATIVE_LPSTR: + return MONO_MARSHAL_CONV_SB_LPSTR; + case MONO_NATIVE_UTF8STR: + return MONO_MARSHAL_CONV_SB_UTF8STR; + case MONO_NATIVE_LPTSTR: + return MONO_MARSHAL_CONV_SB_LPTSTR; + default: + return MONO_MARSHAL_CONV_INVALID; + } +} + +static MonoMarshalConv +mono_marshal_get_ptr_to_string_conv (MonoMethodPInvoke *piinfo, MonoMarshalSpec *spec, gboolean *need_free) +{ + MonoMarshalNative encoding = mono_marshal_get_string_encoding (piinfo, spec); + + *need_free = TRUE; + + switch (encoding) { + case MONO_NATIVE_LPWSTR: + *need_free = FALSE; + return MONO_MARSHAL_CONV_LPWSTR_STR; + case MONO_NATIVE_UTF8STR: + return MONO_MARSHAL_CONV_UTF8STR_STR; + case MONO_NATIVE_LPSTR: + case MONO_NATIVE_VBBYREFSTR: + return MONO_MARSHAL_CONV_LPSTR_STR; + case MONO_NATIVE_LPTSTR: +#ifdef TARGET_WIN32 + *need_free = FALSE; +#endif + return MONO_MARSHAL_CONV_LPTSTR_STR; + case MONO_NATIVE_BSTR: + return MONO_MARSHAL_CONV_BSTR_STR; + default: + return MONO_MARSHAL_CONV_INVALID; + } +} + +static MonoMarshalConv +mono_marshal_get_ptr_to_stringbuilder_conv (MonoMethodPInvoke *piinfo, MonoMarshalSpec *spec, gboolean *need_free) +{ + MonoMarshalNative encoding = mono_marshal_get_string_encoding (piinfo, spec); + + *need_free = TRUE; + + switch (encoding) { + case MONO_NATIVE_LPWSTR: + /* + * mono_string_builder_to_utf16 does not allocate a + * new buffer, so no need to free it. + */ + *need_free = FALSE; + return MONO_MARSHAL_CONV_LPWSTR_SB; + case MONO_NATIVE_UTF8STR: + return MONO_MARSHAL_CONV_UTF8STR_SB; + case MONO_NATIVE_LPSTR: + return MONO_MARSHAL_CONV_LPSTR_SB; + break; + case MONO_NATIVE_LPTSTR: + return MONO_MARSHAL_CONV_LPTSTR_SB; + break; + default: + return MONO_MARSHAL_CONV_INVALID; + } +} + +/* + * Return whenever a field of a native structure or an array member needs to + * be freed. + */ +static gboolean +mono_marshal_need_free (MonoType *t, MonoMethodPInvoke *piinfo, MonoMarshalSpec *spec) +{ + MonoMarshalNative encoding; + + switch (t->type) { + case MONO_TYPE_VALUETYPE: + /* FIXME: Optimize this */ + return TRUE; + case MONO_TYPE_OBJECT: + case MONO_TYPE_CLASS: + if (t->data.klass == mono_defaults.stringbuilder_class) { + gboolean need_free; + mono_marshal_get_ptr_to_stringbuilder_conv (piinfo, spec, &need_free); + return need_free; + } + return FALSE; + case MONO_TYPE_STRING: + encoding = mono_marshal_get_string_encoding (piinfo, spec); + return (encoding == MONO_NATIVE_LPWSTR) ? FALSE : TRUE; + default: + return FALSE; + } +} + +/* + * Return the hash table pointed to by VAR, lazily creating it if neccesary. + */ +static GHashTable* +get_cache (GHashTable **var, GHashFunc hash_func, GCompareFunc equal_func) +{ + if (!(*var)) { + mono_marshal_lock (); + if (!(*var)) { + GHashTable *cache = + g_hash_table_new (hash_func, equal_func); + mono_memory_barrier (); + *var = cache; + } + mono_marshal_unlock (); + } + return *var; +} + +GHashTable* +mono_marshal_get_cache (GHashTable **var, GHashFunc hash_func, GCompareFunc equal_func) +{ + return get_cache (var, hash_func, equal_func); +} + +MonoMethod* +mono_marshal_find_in_cache (GHashTable *cache, gpointer key) +{ + MonoMethod *res; + + mono_marshal_lock (); + res = (MonoMethod *)g_hash_table_lookup (cache, key); + mono_marshal_unlock (); + return res; +} + +/* + * mono_mb_create: + * + * Create a MonoMethod from MB, set INFO as wrapper info. + */ +MonoMethod* +mono_mb_create (MonoMethodBuilder *mb, MonoMethodSignature *sig, + int max_stack, WrapperInfo *info) +{ + MonoMethod *res; + + res = mono_mb_create_method (mb, sig, max_stack); + if (info) + mono_marshal_set_wrapper_info (res, info); + return res; +} + +/* Create the method from the builder and place it in the cache */ +MonoMethod* +mono_mb_create_and_cache_full (GHashTable *cache, gpointer key, + MonoMethodBuilder *mb, MonoMethodSignature *sig, + int max_stack, WrapperInfo *info, gboolean *out_found) +{ + MonoMethod *res; + + if (out_found) + *out_found = FALSE; + + mono_marshal_lock (); + res = (MonoMethod *)g_hash_table_lookup (cache, key); + mono_marshal_unlock (); + if (!res) { + MonoMethod *newm; + newm = mono_mb_create_method (mb, sig, max_stack); + mono_marshal_lock (); + res = (MonoMethod *)g_hash_table_lookup (cache, key); + if (!res) { + res = newm; + g_hash_table_insert (cache, key, res); + mono_marshal_set_wrapper_info (res, info); + mono_marshal_unlock (); + } else { + if (out_found) + *out_found = TRUE; + mono_marshal_unlock (); + mono_free_method (newm); + } + } + + return res; +} + +MonoMethod* +mono_mb_create_and_cache (GHashTable *cache, gpointer key, + MonoMethodBuilder *mb, MonoMethodSignature *sig, + int max_stack) +{ + return mono_mb_create_and_cache_full (cache, key, mb, sig, max_stack, NULL, NULL); +} + +/** + * mono_marshal_method_from_wrapper: + */ +MonoMethod * +mono_marshal_method_from_wrapper (MonoMethod *wrapper) +{ + MonoMethod *m; + int wrapper_type = wrapper->wrapper_type; + WrapperInfo *info; + + if (wrapper_type == MONO_WRAPPER_NONE || wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD) + return wrapper; + + info = mono_marshal_get_wrapper_info (wrapper); + + switch (wrapper_type) { + case MONO_WRAPPER_REMOTING_INVOKE: + case MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK: + case MONO_WRAPPER_XDOMAIN_INVOKE: + m = info->d.remoting.method; + if (wrapper->is_inflated) { + MonoError error; + MonoMethod *result; + /* + * A method cannot be inflated and a wrapper at the same time, so the wrapper info + * contains an uninflated method. + */ + result = mono_class_inflate_generic_method_checked (m, mono_method_get_context (wrapper), &error); + g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */ + return result; + } + return m; + case MONO_WRAPPER_SYNCHRONIZED: + m = info->d.synchronized.method; + if (wrapper->is_inflated) { + MonoError error; + MonoMethod *result; + result = mono_class_inflate_generic_method_checked (m, mono_method_get_context (wrapper), &error); + g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */ + return result; + } + return m; + case MONO_WRAPPER_UNBOX: + return info->d.unbox.method; + case MONO_WRAPPER_MANAGED_TO_NATIVE: + if (info && (info->subtype == WRAPPER_SUBTYPE_NONE || info->subtype == WRAPPER_SUBTYPE_NATIVE_FUNC_AOT || info->subtype == WRAPPER_SUBTYPE_PINVOKE)) + return info->d.managed_to_native.method; + else + return NULL; + case MONO_WRAPPER_RUNTIME_INVOKE: + if (info && (info->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_DIRECT || info->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_VIRTUAL)) + return info->d.runtime_invoke.method; + else + return NULL; + case MONO_WRAPPER_DELEGATE_INVOKE: + if (info) + return info->d.delegate_invoke.method; + else + return NULL; + default: + return NULL; + } +} + +/* + * mono_marshal_get_wrapper_info: + * + * Retrieve the WrapperInfo structure associated with WRAPPER. + */ +WrapperInfo* +mono_marshal_get_wrapper_info (MonoMethod *wrapper) +{ + g_assert (wrapper->wrapper_type); + + return (WrapperInfo *)mono_method_get_wrapper_data (wrapper, 1); +} + +/* + * mono_marshal_set_wrapper_info: + * + * Set the WrapperInfo structure associated with the wrapper + * method METHOD to INFO. + */ +void +mono_marshal_set_wrapper_info (MonoMethod *method, WrapperInfo *info) +{ + void **datav; + /* assert */ + if (method->wrapper_type == MONO_WRAPPER_NONE || method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD) + return; + + datav = (void **)((MonoMethodWrapper *)method)->method_data; + datav [1] = info; +} + +WrapperInfo* +mono_wrapper_info_create (MonoMethodBuilder *mb, WrapperSubtype subtype) +{ + WrapperInfo *info; + + info = (WrapperInfo *)mono_image_alloc0 (mb->method->klass->image, sizeof (WrapperInfo)); + info->subtype = subtype; + return info; +} + +/* + * get_wrapper_target_class: + * + * Return the class where a wrapper method should be placed. + */ +static MonoClass* +get_wrapper_target_class (MonoImage *image) +{ + MonoError error; + MonoClass *klass; + + /* + * Notes: + * - can't put all wrappers into an mscorlib class, because they reference + * metadata (signature) so they should be put into the same image as the + * method they wrap, so they are unloaded together. + * - putting them into a class with a type initalizer could cause the + * initializer to be executed which can be a problem if the wrappers are + * shared. + * - putting them into an inflated class can cause problems if the the + * class is deleted because it references an image which is unloaded. + * To avoid these problems, we put the wrappers into the class of + * the image. + */ + if (image_is_dynamic (image)) { + klass = ((MonoDynamicImage*)image)->wrappers_type; + } else { + klass = mono_class_get_checked (image, mono_metadata_make_token (MONO_TABLE_TYPEDEF, 1), &error); + g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */ + } + g_assert (klass); + + return klass; +} + +/* + * Wrappers for generic methods should be instances of generic wrapper methods, i.e .the wrapper for Sort should be + * an instance of the wrapper for Sort. This is required for full-aot to work. + */ + +/* + * check_generic_wrapper_cache: + * + * Check CACHE for the wrapper of the generic instance ORIG_METHOD, and return it if it is found. + * KEY should be the key for ORIG_METHOD in the cache, while DEF_KEY should be the key of its + * generic method definition. + */ +static MonoMethod* +check_generic_wrapper_cache (GHashTable *cache, MonoMethod *orig_method, gpointer key, gpointer def_key) +{ + MonoMethod *res; + MonoMethod *inst, *def; + MonoGenericContext *ctx; + + g_assert (orig_method->is_inflated); + ctx = mono_method_get_context (orig_method); + + /* + * Look for the instance + */ + res = mono_marshal_find_in_cache (cache, key); + if (res) + return res; + + /* + * Look for the definition + */ + def = mono_marshal_find_in_cache (cache, def_key); + if (def) { + MonoError error; + inst = mono_class_inflate_generic_method_checked (def, ctx, &error); + g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */ + /* Cache it */ + mono_memory_barrier (); + mono_marshal_lock (); + res = (MonoMethod *)g_hash_table_lookup (cache, key); + if (!res) { + g_hash_table_insert (cache, key, inst); + res = inst; + } + mono_marshal_unlock (); + return res; + } + return NULL; +} + +static MonoMethod* +cache_generic_wrapper (GHashTable *cache, MonoMethod *orig_method, MonoMethod *def, MonoGenericContext *ctx, gpointer key) +{ + MonoError error; + MonoMethod *inst, *res; + + /* + * We use the same cache for the generic definition and the instances. + */ + inst = mono_class_inflate_generic_method_checked (def, ctx, &error); + g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */ + mono_memory_barrier (); + mono_marshal_lock (); + res = (MonoMethod *)g_hash_table_lookup (cache, key); + if (!res) { + g_hash_table_insert (cache, key, inst); + res = inst; + } + mono_marshal_unlock (); + return res; +} + +static MonoMethod* +check_generic_delegate_wrapper_cache (GHashTable *cache, MonoMethod *orig_method, MonoMethod *def_method, MonoGenericContext *ctx) +{ + MonoError error; + MonoMethod *res; + MonoMethod *inst, *def; + + /* + * Look for the instance + */ + res = mono_marshal_find_in_cache (cache, orig_method->klass); + if (res) + return res; + + /* + * Look for the definition + */ + def = mono_marshal_find_in_cache (cache, def_method->klass); + if (def) { + inst = mono_class_inflate_generic_method_checked (def, ctx, &error); + g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */ + + /* Cache it */ + mono_memory_barrier (); + mono_marshal_lock (); + res = (MonoMethod *)g_hash_table_lookup (cache, orig_method->klass); + if (!res) { + g_hash_table_insert (cache, orig_method->klass, inst); + res = inst; + } + mono_marshal_unlock (); + return res; + } + return NULL; +} + +static MonoMethod* +cache_generic_delegate_wrapper (GHashTable *cache, MonoMethod *orig_method, MonoMethod *def, MonoGenericContext *ctx) +{ + MonoError error; + MonoMethod *inst, *res; + WrapperInfo *ginfo, *info; + + /* + * We use the same cache for the generic definition and the instances. + */ + inst = mono_class_inflate_generic_method_checked (def, ctx, &error); + g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */ + + ginfo = mono_marshal_get_wrapper_info (def); + if (ginfo) { + info = (WrapperInfo *)mono_image_alloc0 (def->klass->image, sizeof (WrapperInfo)); + info->subtype = ginfo->subtype; + if (info->subtype == WRAPPER_SUBTYPE_NONE) { + info->d.delegate_invoke.method = mono_class_inflate_generic_method_checked (ginfo->d.delegate_invoke.method, ctx, &error); + mono_error_assert_ok (&error); + } + } + + mono_memory_barrier (); + mono_marshal_lock (); + res = (MonoMethod *)g_hash_table_lookup (cache, orig_method->klass); + if (!res) { + g_hash_table_insert (cache, orig_method->klass, inst); + res = inst; + } + mono_marshal_unlock (); + return res; +} + +/** + * mono_marshal_get_delegate_begin_invoke: + */ +MonoMethod * +mono_marshal_get_delegate_begin_invoke (MonoMethod *method) +{ + MonoMethodSignature *sig; + MonoMethodBuilder *mb; + MonoMethod *res; + GHashTable *cache; + int params_var; + char *name; + MonoGenericContext *ctx = NULL; + MonoMethod *orig_method = NULL; + + g_assert (method && method->klass->parent == mono_defaults.multicastdelegate_class && + !strcmp (method->name, "BeginInvoke")); + + /* + * For generic delegates, create a generic wrapper, and returns an instance to help AOT. + */ + if (method->is_inflated) { + orig_method = method; + ctx = &((MonoMethodInflated*)method)->context; + method = ((MonoMethodInflated*)method)->declaring; + } + + sig = mono_signature_no_pinvoke (method); + + /* + * Check cache + */ + if (ctx) { + cache = get_cache (&((MonoMethodInflated*)orig_method)->owner->wrapper_caches.delegate_begin_invoke_cache, mono_aligned_addr_hash, NULL); + res = check_generic_delegate_wrapper_cache (cache, orig_method, method, ctx); + if (res) + return res; + } else { + cache = get_cache (&method->klass->image->wrapper_caches.delegate_begin_invoke_cache, + (GHashFunc)mono_signature_hash, + (GCompareFunc)mono_metadata_signature_equal); + if ((res = mono_marshal_find_in_cache (cache, sig))) + return res; + } + + g_assert (sig->hasthis); + + name = mono_signature_to_name (sig, "begin_invoke"); + if (ctx) + mb = mono_mb_new (method->klass, name, MONO_WRAPPER_DELEGATE_BEGIN_INVOKE); + else + mb = mono_mb_new (get_wrapper_target_class (method->klass->image), name, MONO_WRAPPER_DELEGATE_BEGIN_INVOKE); + g_free (name); + +#ifdef ENABLE_ILGEN + params_var = mono_mb_emit_save_args (mb, sig, FALSE); + + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldloc (mb, params_var); + mono_mb_emit_icall (mb, mono_delegate_begin_invoke); + mono_mb_emit_byte (mb, CEE_RET); +#endif + + if (ctx) { + MonoMethod *def; + def = mono_mb_create_and_cache (cache, method->klass, mb, sig, sig->param_count + 16); + res = cache_generic_delegate_wrapper (cache, orig_method, def, ctx); + } else { + res = mono_mb_create_and_cache (cache, sig, mb, sig, sig->param_count + 16); + } + + mono_mb_free (mb); + return res; +} + +/* This is a JIT icall, it sets the pending exception and returns NULL on error. */ +static MonoObject * +mono_delegate_end_invoke (MonoDelegate *delegate, gpointer *params) +{ + MonoError error; + MonoDomain *domain = mono_domain_get (); + MonoAsyncResult *ares; + MonoMethod *method = NULL; + MonoMethodSignature *sig; + MonoMethodMessage *msg; + MonoObject *res, *exc; + MonoArray *out_args; + MonoClass *klass; + + g_assert (delegate); + + if (!delegate->method_info) { + g_assert (delegate->method); + MonoReflectionMethod *rm = mono_method_get_object_checked (domain, delegate->method, NULL, &error); + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + return NULL; + } + MONO_OBJECT_SETREF (delegate, method_info, rm); + } + + if (!delegate->method_info || !delegate->method_info->method) + g_assert_not_reached (); + + klass = delegate->object.vtable->klass; + + method = mono_class_get_method_from_name (klass, "EndInvoke", -1); + g_assert (method != NULL); + + sig = mono_signature_no_pinvoke (method); + + msg = mono_method_call_message_new (method, params, NULL, NULL, NULL, &error); + if (mono_error_set_pending_exception (&error)) + return NULL; + + ares = (MonoAsyncResult *)mono_array_get (msg->args, gpointer, sig->param_count - 1); + if (ares == NULL) { + mono_set_pending_exception (mono_exception_from_name_msg (mono_defaults.corlib, "System.Runtime.Remoting", "RemotingException", "The async result object is null or of an unexpected type.")); + return NULL; + } + + if (ares->async_delegate != (MonoObject*)delegate) { + mono_set_pending_exception (mono_get_exception_invalid_operation ( + "The IAsyncResult object provided does not match this delegate.")); + return NULL; + } + +#ifndef DISABLE_REMOTING + if (delegate->target && mono_object_is_transparent_proxy (delegate->target)) { + MonoTransparentProxy* tp = (MonoTransparentProxy *)delegate->target; + msg = (MonoMethodMessage *)mono_object_new_checked (domain, mono_defaults.mono_method_message_class, &error); + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + return NULL; + } + mono_message_init (domain, msg, delegate->method_info, NULL, &error); + if (mono_error_set_pending_exception (&error)) + return NULL; + msg->call_type = CallType_EndInvoke; + MONO_OBJECT_SETREF (msg, async_result, ares); + res = mono_remoting_invoke ((MonoObject *)tp->rp, msg, &exc, &out_args, &error); + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + return NULL; + } + } else +#endif + { + res = mono_threadpool_end_invoke (ares, &out_args, &exc, &error); + if (mono_error_set_pending_exception (&error)) + return NULL; + } + + if (exc) { + if (((MonoException*)exc)->stack_trace) { + MonoError inner_error; + char *strace = mono_string_to_utf8_checked (((MonoException*)exc)->stack_trace, &inner_error); + if (is_ok (&inner_error)) { + char *tmp; + tmp = g_strdup_printf ("%s\nException Rethrown at:\n", strace); + g_free (strace); + MonoString *tmp_str = mono_string_new_checked (domain, tmp, &inner_error); + g_free (tmp); + if (is_ok (&inner_error)) + MONO_OBJECT_SETREF (((MonoException*)exc), stack_trace, tmp_str); + }; + if (!is_ok (&inner_error)) + mono_error_cleanup (&inner_error); /* no stack trace, but at least throw the original exception */ + } + mono_set_pending_exception ((MonoException*)exc); + } + + mono_method_return_message_restore (method, params, out_args, &error); + mono_error_set_pending_exception (&error); + return res; +} + +#ifdef ENABLE_ILGEN + +void +mono_mb_emit_restore_result (MonoMethodBuilder *mb, MonoType *return_type) +{ + MonoType *t = mono_type_get_underlying_type (return_type); + + if (return_type->byref) + return_type = &mono_defaults.int_class->byval_arg; + + switch (t->type) { + case MONO_TYPE_VOID: + g_assert_not_reached (); + break; + case MONO_TYPE_PTR: + case MONO_TYPE_STRING: + case MONO_TYPE_CLASS: + case MONO_TYPE_OBJECT: + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: + /* nothing to do */ + break; + case MONO_TYPE_U1: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_I1: + case MONO_TYPE_U2: + case MONO_TYPE_CHAR: + case MONO_TYPE_I2: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_U8: + case MONO_TYPE_I8: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + mono_mb_emit_op (mb, CEE_UNBOX, mono_class_from_mono_type (return_type)); + mono_mb_emit_byte (mb, mono_type_to_ldind (return_type)); + break; + case MONO_TYPE_GENERICINST: + if (!mono_type_generic_inst_is_valuetype (t)) + break; + /* fall through */ + case MONO_TYPE_VALUETYPE: { + MonoClass *klass = mono_class_from_mono_type (return_type); + mono_mb_emit_op (mb, CEE_UNBOX, klass); + mono_mb_emit_op (mb, CEE_LDOBJ, klass); + break; + } + case MONO_TYPE_VAR: + case MONO_TYPE_MVAR: { + MonoClass *klass = mono_class_from_mono_type (return_type); + mono_mb_emit_op (mb, CEE_UNBOX_ANY, klass); + break; + } + default: + g_warning ("type 0x%x not handled", return_type->type); + g_assert_not_reached (); + } + + mono_mb_emit_byte (mb, CEE_RET); +} + +#endif /* ENABLE_ILGEN */ + +/** + * mono_marshal_get_delegate_end_invoke: + */ +MonoMethod * +mono_marshal_get_delegate_end_invoke (MonoMethod *method) +{ + MonoMethodSignature *sig; + MonoMethodBuilder *mb; + MonoMethod *res; + GHashTable *cache; + int params_var; + char *name; + MonoGenericContext *ctx = NULL; + MonoMethod *orig_method = NULL; + + g_assert (method && method->klass->parent == mono_defaults.multicastdelegate_class && + !strcmp (method->name, "EndInvoke")); + + /* + * For generic delegates, create a generic wrapper, and returns an instance to help AOT. + */ + if (method->is_inflated) { + orig_method = method; + ctx = &((MonoMethodInflated*)method)->context; + method = ((MonoMethodInflated*)method)->declaring; + } + + sig = mono_signature_no_pinvoke (method); + + /* + * Check cache + */ + if (ctx) { + cache = get_cache (&((MonoMethodInflated*)orig_method)->owner->wrapper_caches.delegate_end_invoke_cache, mono_aligned_addr_hash, NULL); + res = check_generic_delegate_wrapper_cache (cache, orig_method, method, ctx); + if (res) + return res; + } else { + cache = get_cache (&method->klass->image->wrapper_caches.delegate_end_invoke_cache, + (GHashFunc)mono_signature_hash, + (GCompareFunc)mono_metadata_signature_equal); + if ((res = mono_marshal_find_in_cache (cache, sig))) + return res; + } + + g_assert (sig->hasthis); + + name = mono_signature_to_name (sig, "end_invoke"); + if (ctx) + mb = mono_mb_new (method->klass, name, MONO_WRAPPER_DELEGATE_END_INVOKE); + else + mb = mono_mb_new (get_wrapper_target_class (method->klass->image), name, MONO_WRAPPER_DELEGATE_END_INVOKE); + g_free (name); + +#ifdef ENABLE_ILGEN + params_var = mono_mb_emit_save_args (mb, sig, FALSE); + + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldloc (mb, params_var); + mono_mb_emit_icall (mb, mono_delegate_end_invoke); + + if (sig->ret->type == MONO_TYPE_VOID) { + mono_mb_emit_byte (mb, CEE_POP); + mono_mb_emit_byte (mb, CEE_RET); + } else + mono_mb_emit_restore_result (mb, sig->ret); +#endif + + if (ctx) { + MonoMethod *def; + def = mono_mb_create_and_cache (cache, method->klass, mb, sig, sig->param_count + 16); + res = cache_generic_delegate_wrapper (cache, orig_method, def, ctx); + } else { + res = mono_mb_create_and_cache (cache, sig, + mb, sig, sig->param_count + 16); + } + mono_mb_free (mb); + + return res; +} + +typedef struct +{ + MonoMethodSignature *sig; + gpointer pointer; +} SignaturePointerPair; + +static guint +signature_pointer_pair_hash (gconstpointer data) +{ + SignaturePointerPair *pair = (SignaturePointerPair*)data; + + return mono_signature_hash (pair->sig) ^ mono_aligned_addr_hash (pair->pointer); +} + +static gboolean +signature_pointer_pair_equal (gconstpointer data1, gconstpointer data2) +{ + SignaturePointerPair *pair1 = (SignaturePointerPair*) data1, *pair2 = (SignaturePointerPair*) data2; + return mono_metadata_signature_equal (pair1->sig, pair2->sig) && (pair1->pointer == pair2->pointer); +} + +static gboolean +signature_pointer_pair_matches_pointer (gpointer key, gpointer value, gpointer user_data) +{ + SignaturePointerPair *pair = (SignaturePointerPair*)key; + + return pair->pointer == user_data; +} + +static void +free_signature_pointer_pair (SignaturePointerPair *pair) +{ + g_free (pair); +} + +MonoMethod * +mono_marshal_get_delegate_invoke_internal (MonoMethod *method, gboolean callvirt, gboolean static_method_with_first_arg_bound, MonoMethod *target_method) +{ + MonoMethodSignature *sig, *static_sig, *invoke_sig; + int i; + MonoMethodBuilder *mb; + MonoMethod *res; + GHashTable *cache; + gpointer cache_key = NULL; + SignaturePointerPair key = { NULL, NULL }; + SignaturePointerPair *new_key; + int local_i, local_len, local_delegates, local_d, local_target, local_res; + int pos0, pos1, pos2; + char *name; + MonoClass *target_class = NULL; + gboolean closed_over_null = FALSE; + MonoGenericContext *ctx = NULL; + MonoGenericContainer *container = NULL; + MonoMethod *orig_method = method; + WrapperInfo *info; + WrapperSubtype subtype = WRAPPER_SUBTYPE_NONE; + gboolean found; + gboolean void_ret; + + g_assert (method && method->klass->parent == mono_defaults.multicastdelegate_class && + !strcmp (method->name, "Invoke")); + + invoke_sig = sig = mono_signature_no_pinvoke (method); + + /* + * If the delegate target is null, and the target method is not static, a virtual + * call is made to that method with the first delegate argument as this. This is + * a non-documented .NET feature. + */ + if (callvirt) { + subtype = WRAPPER_SUBTYPE_DELEGATE_INVOKE_VIRTUAL; + if (target_method->is_inflated) { + MonoError error; + MonoType *target_type; + + g_assert (method->signature->hasthis); + target_type = mono_class_inflate_generic_type_checked (method->signature->params [0], + mono_method_get_context (method), &error); + mono_error_assert_ok (&error); /* FIXME don't swallow the error */ + target_class = mono_class_from_mono_type (target_type); + } else { + target_class = target_method->klass; + } + + closed_over_null = sig->param_count == mono_method_signature (target_method)->param_count; + } + + if (static_method_with_first_arg_bound) { + subtype = WRAPPER_SUBTYPE_DELEGATE_INVOKE_BOUND; + g_assert (!callvirt); + invoke_sig = mono_method_signature (target_method); + } + + /* + * For generic delegates, create a generic wrapper, and return an instance to help AOT. + */ + if (method->is_inflated && subtype == WRAPPER_SUBTYPE_NONE) { + ctx = &((MonoMethodInflated*)method)->context; + method = ((MonoMethodInflated*)method)->declaring; + + container = mono_method_get_generic_container (method); + if (!container) + container = mono_class_try_get_generic_container (method->klass); //FIXME is this a case of a try? + g_assert (container); + + invoke_sig = sig = mono_signature_no_pinvoke (method); + } + + /* + * Check cache + */ + if (ctx) { + cache = get_cache (&((MonoMethodInflated*)orig_method)->owner->wrapper_caches.delegate_invoke_cache, mono_aligned_addr_hash, NULL); + res = check_generic_delegate_wrapper_cache (cache, orig_method, method, ctx); + if (res) + return res; + cache_key = method->klass; + } else if (static_method_with_first_arg_bound) { + cache = get_cache (&method->klass->image->delegate_bound_static_invoke_cache, + (GHashFunc)mono_signature_hash, + (GCompareFunc)mono_metadata_signature_equal); + /* + * The wrapper is based on sig+invoke_sig, but sig can be derived from invoke_sig. + */ + res = mono_marshal_find_in_cache (cache, invoke_sig); + if (res) + return res; + cache_key = invoke_sig; + } else if (callvirt) { + GHashTable **cache_ptr; + + cache_ptr = &mono_method_get_wrapper_cache (method)->delegate_abstract_invoke_cache; + + /* We need to cache the signature+method pair */ + mono_marshal_lock (); + if (!*cache_ptr) + *cache_ptr = g_hash_table_new_full (signature_pointer_pair_hash, (GEqualFunc)signature_pointer_pair_equal, (GDestroyNotify)free_signature_pointer_pair, NULL); + cache = *cache_ptr; + key.sig = invoke_sig; + key.pointer = target_method; + res = (MonoMethod *)g_hash_table_lookup (cache, &key); + mono_marshal_unlock (); + if (res) + return res; + } else { + // Inflated methods should not be in this cache because it's not stored on the imageset. + g_assert (!method->is_inflated); + cache = get_cache (&method->klass->image->wrapper_caches.delegate_invoke_cache, + (GHashFunc)mono_signature_hash, + (GCompareFunc)mono_metadata_signature_equal); + res = mono_marshal_find_in_cache (cache, sig); + if (res) + return res; + cache_key = sig; + } + + static_sig = mono_metadata_signature_dup_full (method->klass->image, sig); + static_sig->hasthis = 0; + if (!static_method_with_first_arg_bound) + invoke_sig = static_sig; + + if (static_method_with_first_arg_bound) + name = mono_signature_to_name (invoke_sig, "invoke_bound"); + else if (closed_over_null) + name = mono_signature_to_name (invoke_sig, "invoke_closed_over_null"); + else if (callvirt) + name = mono_signature_to_name (invoke_sig, "invoke_callvirt"); + else + name = mono_signature_to_name (invoke_sig, "invoke"); + if (ctx) + mb = mono_mb_new (method->klass, name, MONO_WRAPPER_DELEGATE_INVOKE); + else + mb = mono_mb_new (get_wrapper_target_class (method->klass->image), name, MONO_WRAPPER_DELEGATE_INVOKE); + g_free (name); + +#ifdef ENABLE_ILGEN + void_ret = sig->ret->type == MONO_TYPE_VOID && !method->string_ctor; + + /* allocate local 0 (object) */ + local_i = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg); + local_len = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg); + local_delegates = mono_mb_add_local (mb, &mono_defaults.array_class->byval_arg); + local_d = mono_mb_add_local (mb, &mono_defaults.multicastdelegate_class->byval_arg); + local_target = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + + if (!void_ret) + local_res = mono_mb_add_local (mb, &mono_class_from_mono_type (sig->ret)->byval_arg); + + g_assert (sig->hasthis); + + /* + * {type: sig->ret} res; + * if (delegates == null) { + * return this. ( args .. ); + * } else { + * int i = 0, len = this.delegates.Length; + * do { + * res = this.delegates [i].Invoke ( args .. ); + * } while (++i < len); + * return res; + * } + */ + + /* this wrapper can be used in unmanaged-managed transitions */ + emit_thread_interrupt_checkpoint (mb); + + /* delegates = this.delegates */ + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoMulticastDelegate, delegates)); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_stloc (mb, local_delegates); + + /* if (delegates == null) */ + mono_mb_emit_ldloc (mb, local_delegates); + pos2 = mono_mb_emit_branch (mb, CEE_BRTRUE); + + /* return target. ( args .. ); */ + + /* target = d.target; */ + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoDelegate, target)); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_stloc (mb, local_target); + + /*static methods with bound first arg can have null target and still be bound*/ + if (!static_method_with_first_arg_bound) { + /* if target != null */ + mono_mb_emit_ldloc (mb, local_target); + pos0 = mono_mb_emit_branch (mb, CEE_BRFALSE); + + /* then call this->method_ptr nonstatic */ + if (callvirt) { + // FIXME: + mono_mb_emit_exception_full (mb, "System", "NotImplementedException", ""); + } else { + mono_mb_emit_ldloc (mb, local_target); + for (i = 0; i < sig->param_count; ++i) + mono_mb_emit_ldarg (mb, i + 1); + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoDelegate, extra_arg)); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr)); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_op (mb, CEE_MONO_CALLI_EXTRA_ARG, sig); + mono_mb_emit_byte (mb, CEE_RET); + } + + /* else [target == null] call this->method_ptr static */ + mono_mb_patch_branch (mb, pos0); + } + + if (callvirt) { + if (!closed_over_null) { + if (target_class->valuetype) { + mono_mb_emit_ldarg (mb, 1); + for (i = 1; i < sig->param_count; ++i) + mono_mb_emit_ldarg (mb, i + 1); + mono_mb_emit_op (mb, CEE_CALL, target_method); + } else { + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_op (mb, CEE_CASTCLASS, target_class); + for (i = 1; i < sig->param_count; ++i) + mono_mb_emit_ldarg (mb, i + 1); + mono_mb_emit_op (mb, CEE_CALLVIRT, target_method); + } + } else { + mono_mb_emit_byte (mb, CEE_LDNULL); + for (i = 0; i < sig->param_count; ++i) + mono_mb_emit_ldarg (mb, i + 1); + mono_mb_emit_op (mb, CEE_CALL, target_method); + } + } else { + if (static_method_with_first_arg_bound) { + mono_mb_emit_ldloc (mb, local_target); + if (!MONO_TYPE_IS_REFERENCE (invoke_sig->params[0])) + mono_mb_emit_op (mb, CEE_UNBOX_ANY, mono_class_from_mono_type (invoke_sig->params[0])); + } + for (i = 0; i < sig->param_count; ++i) + mono_mb_emit_ldarg (mb, i + 1); + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoDelegate, extra_arg)); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr)); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_op (mb, CEE_MONO_CALLI_EXTRA_ARG, invoke_sig); + } + + mono_mb_emit_byte (mb, CEE_RET); + + /* else [delegates != null] */ + mono_mb_patch_branch (mb, pos2); + + /* len = delegates.Length; */ + mono_mb_emit_ldloc (mb, local_delegates); + mono_mb_emit_byte (mb, CEE_LDLEN); + mono_mb_emit_byte (mb, CEE_CONV_I4); + mono_mb_emit_stloc (mb, local_len); + + /* i = 0; */ + mono_mb_emit_icon (mb, 0); + mono_mb_emit_stloc (mb, local_i); + + pos1 = mono_mb_get_label (mb); + + /* d = delegates [i]; */ + mono_mb_emit_ldloc (mb, local_delegates); + mono_mb_emit_ldloc (mb, local_i); + mono_mb_emit_byte (mb, CEE_LDELEM_REF); + mono_mb_emit_stloc (mb, local_d); + + /* res = d.Invoke ( args .. ); */ + mono_mb_emit_ldloc (mb, local_d); + for (i = 0; i < sig->param_count; i++) + mono_mb_emit_ldarg (mb, i + 1); + if (!ctx) { + mono_mb_emit_op (mb, CEE_CALLVIRT, method); + } else { + MonoError error; + mono_mb_emit_op (mb, CEE_CALLVIRT, mono_class_inflate_generic_method_checked (method, &container->context, &error)); + g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */ + } + if (!void_ret) + mono_mb_emit_stloc (mb, local_res); + + /* i += 1 */ + mono_mb_emit_add_to_local (mb, local_i, 1); + + /* i < l */ + mono_mb_emit_ldloc (mb, local_i); + mono_mb_emit_ldloc (mb, local_len); + mono_mb_emit_branch_label (mb, CEE_BLT, pos1); + + /* return res */ + if (!void_ret) + mono_mb_emit_ldloc (mb, local_res); + mono_mb_emit_byte (mb, CEE_RET); + + mb->skip_visibility = 1; +#endif /* ENABLE_ILGEN */ + + info = mono_wrapper_info_create (mb, subtype); + info->d.delegate_invoke.method = method; + + if (ctx) { + MonoMethod *def; + + def = mono_mb_create_and_cache_full (cache, cache_key, mb, sig, sig->param_count + 16, info, NULL); + res = cache_generic_delegate_wrapper (cache, orig_method, def, ctx); + } else if (callvirt) { + new_key = g_new0 (SignaturePointerPair, 1); + *new_key = key; + + res = mono_mb_create_and_cache_full (cache, new_key, mb, sig, sig->param_count + 16, info, &found); + if (found) + g_free (new_key); + } else { + res = mono_mb_create_and_cache_full (cache, cache_key, mb, sig, sig->param_count + 16, info, NULL); + } + mono_mb_free (mb); + + /* mono_method_print_code (res); */ + + return res; +} + +/** + * mono_marshal_get_delegate_invoke: + * The returned method invokes all methods in a multicast delegate. + */ +MonoMethod * +mono_marshal_get_delegate_invoke (MonoMethod *method, MonoDelegate *del) +{ + gboolean callvirt = FALSE; + gboolean static_method_with_first_arg_bound = FALSE; + MonoMethod *target_method = NULL; + MonoMethodSignature *sig; + + sig = mono_signature_no_pinvoke (method); + + if (del && !del->target && del->method && mono_method_signature (del->method)->hasthis) { + callvirt = TRUE; + target_method = del->method; + } + + if (del && del->method && mono_method_signature (del->method)->param_count == sig->param_count + 1 && (del->method->flags & METHOD_ATTRIBUTE_STATIC)) { + static_method_with_first_arg_bound = TRUE; + target_method = del->method; + } + + return mono_marshal_get_delegate_invoke_internal (method, callvirt, static_method_with_first_arg_bound, target_method); +} + +typedef struct { + MonoMethodSignature *ctor_sig; + MonoMethodSignature *sig; +} CtorSigPair; + +/* protected by the marshal lock, contains CtorSigPair pointers */ +static GSList *strsig_list = NULL; + +static MonoMethodSignature * +lookup_string_ctor_signature (MonoMethodSignature *sig) +{ + MonoMethodSignature *callsig; + CtorSigPair *cs; + GSList *item; + + mono_marshal_lock (); + callsig = NULL; + for (item = strsig_list; item; item = item->next) { + cs = (CtorSigPair *)item->data; + /* mono_metadata_signature_equal () is safe to call with the marshal lock + * because it is lock-free. + */ + if (mono_metadata_signature_equal (sig, cs->ctor_sig)) { + callsig = cs->sig; + break; + } + } + mono_marshal_unlock (); + return callsig; +} + +static MonoMethodSignature * +add_string_ctor_signature (MonoMethod *method) +{ + MonoMethodSignature *callsig; + CtorSigPair *cs; + + callsig = mono_metadata_signature_dup_full (method->klass->image, mono_method_signature (method)); + callsig->ret = &mono_defaults.string_class->byval_arg; + cs = g_new (CtorSigPair, 1); + cs->sig = callsig; + cs->ctor_sig = mono_method_signature (method); + + mono_marshal_lock (); + strsig_list = g_slist_prepend (strsig_list, cs); + mono_marshal_unlock (); + return callsig; +} + +/* + * mono_marshal_get_string_ctor_signature: + * + * Return the modified signature used by string ctors (they return the newly created + * string). + */ +MonoMethodSignature* +mono_marshal_get_string_ctor_signature (MonoMethod *method) +{ + MonoMethodSignature *sig = lookup_string_ctor_signature (mono_method_signature (method)); + if (!sig) + sig = add_string_ctor_signature (method); + + return sig; +} + +static MonoType* +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; + } + + if (MONO_TYPE_IS_REFERENCE (t)) + return &mono_defaults.object_class->byval_arg; + + if (ret) + /* The result needs to be boxed */ + return t; + +handle_enum: + switch (t->type) { + /* Can't share these as the argument needs to be loaded using sign/zero extension */ + /* + case MONO_TYPE_U1: + return &mono_defaults.sbyte_class->byval_arg; + case MONO_TYPE_U2: + return &mono_defaults.int16_class->byval_arg; + case MONO_TYPE_U4: + return &mono_defaults.int32_class->byval_arg; + */ + case MONO_TYPE_U8: + return &mono_defaults.int64_class->byval_arg; + case MONO_TYPE_BOOLEAN: + return &mono_defaults.byte_class->byval_arg; + case MONO_TYPE_CHAR: + return &mono_defaults.uint16_class->byval_arg; + case MONO_TYPE_U: + return &mono_defaults.int_class->byval_arg; + case MONO_TYPE_VALUETYPE: + if (t->data.klass->enumtype) { + t = mono_class_enum_basetype (t->data.klass); + goto handle_enum; + } + return t; + default: + return t; + } +} + +/* + * mono_marshal_get_runtime_invoke_sig: + * + * Return a common signature used for sharing runtime invoke wrappers. + */ +static MonoMethodSignature* +mono_marshal_get_runtime_invoke_sig (MonoMethodSignature *sig) +{ + MonoMethodSignature *res = mono_metadata_signature_dup (sig); + int i; + + res->generic_param_count = 0; + res->ret = get_runtime_invoke_type (sig->ret, TRUE); + for (i = 0; i < res->param_count; ++i) + res->params [i] = get_runtime_invoke_type (sig->params [i], FALSE); + + return res; +} + +static gboolean +runtime_invoke_signature_equal (MonoMethodSignature *sig1, MonoMethodSignature *sig2) +{ + /* Can't share wrappers which return a vtype since it needs to be boxed */ + if (sig1->ret != sig2->ret && !(MONO_TYPE_IS_REFERENCE (sig1->ret) && MONO_TYPE_IS_REFERENCE (sig2->ret)) && !mono_metadata_type_equal (sig1->ret, sig2->ret)) + return FALSE; + else + return mono_metadata_signature_equal (sig1, sig2); +} + +#ifdef ENABLE_ILGEN + +/* + * emit_invoke_call: + * + * Emit the call to the wrapper method from a runtime invoke wrapper. + */ +static void +emit_invoke_call (MonoMethodBuilder *mb, MonoMethod *method, + MonoMethodSignature *sig, MonoMethodSignature *callsig, + int loc_res, + gboolean virtual_, gboolean need_direct_wrapper) +{ + static MonoString *string_dummy = NULL; + int i; + int *tmp_nullable_locals; + gboolean void_ret = FALSE; + gboolean string_ctor = method && method->string_ctor; + + /* to make it work with our special string constructors */ + if (!string_dummy) { + MonoError error; + MONO_GC_REGISTER_ROOT_SINGLE (string_dummy, MONO_ROOT_SOURCE_MARSHAL, NULL, "Marshal Dummy String"); + string_dummy = mono_string_new_checked (mono_get_root_domain (), "dummy", &error); + mono_error_assert_ok (&error); + } + + if (virtual_) { + g_assert (sig->hasthis); + g_assert (method->flags & METHOD_ATTRIBUTE_VIRTUAL); + } + + if (sig->hasthis) { + if (string_ctor) { + if (mono_gc_is_moving ()) { + mono_mb_emit_ptr (mb, &string_dummy); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + } else { + mono_mb_emit_ptr (mb, string_dummy); + } + } else { + mono_mb_emit_ldarg (mb, 0); + } + } + + tmp_nullable_locals = g_new0 (int, sig->param_count); + + for (i = 0; i < sig->param_count; i++) { + MonoType *t = sig->params [i]; + int type; + + mono_mb_emit_ldarg (mb, 1); + if (i) { + mono_mb_emit_icon (mb, sizeof (gpointer) * i); + mono_mb_emit_byte (mb, CEE_ADD); + } + + if (t->byref) { + mono_mb_emit_byte (mb, CEE_LDIND_I); + /* A Nullable type don't have a boxed form, it's either null or a boxed T. + * So to make this work we unbox it to a local variablee and push a reference to that. + */ + if (t->type == MONO_TYPE_GENERICINST && mono_class_is_nullable (mono_class_from_mono_type (t))) { + tmp_nullable_locals [i] = mono_mb_add_local (mb, &mono_class_from_mono_type (t)->byval_arg); + + mono_mb_emit_op (mb, CEE_UNBOX_ANY, mono_class_from_mono_type (t)); + mono_mb_emit_stloc (mb, tmp_nullable_locals [i]); + mono_mb_emit_ldloc_addr (mb, tmp_nullable_locals [i]); + } + continue; + } + + /*FIXME 'this doesn't handle generic enums. Shouldn't we?*/ + type = sig->params [i]->type; +handle_enum: + switch (type) { + case MONO_TYPE_I1: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_U1: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_CHAR: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_byte (mb, mono_type_to_ldind (sig->params [i])); + break; + case MONO_TYPE_STRING: + case MONO_TYPE_CLASS: + case MONO_TYPE_ARRAY: + case MONO_TYPE_PTR: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_OBJECT: + mono_mb_emit_byte (mb, mono_type_to_ldind (sig->params [i])); + break; + case MONO_TYPE_GENERICINST: + if (!mono_type_generic_inst_is_valuetype (sig->params [i])) { + mono_mb_emit_byte (mb, mono_type_to_ldind (sig->params [i])); + break; + } + + /* fall through */ + case MONO_TYPE_VALUETYPE: + if (type == MONO_TYPE_VALUETYPE && t->data.klass->enumtype) { + type = mono_class_enum_basetype (t->data.klass)->type; + goto handle_enum; + } + mono_mb_emit_byte (mb, CEE_LDIND_I); + if (mono_class_is_nullable (mono_class_from_mono_type (sig->params [i]))) { + /* Need to convert a boxed vtype to an mp to a Nullable struct */ + mono_mb_emit_op (mb, CEE_UNBOX, mono_class_from_mono_type (sig->params [i])); + mono_mb_emit_op (mb, CEE_LDOBJ, mono_class_from_mono_type (sig->params [i])); + } else { + mono_mb_emit_op (mb, CEE_LDOBJ, mono_class_from_mono_type (sig->params [i])); + } + break; + default: + g_assert_not_reached (); + } + } + + if (virtual_) { + mono_mb_emit_op (mb, CEE_CALLVIRT, method); + } else if (need_direct_wrapper) { + mono_mb_emit_op (mb, CEE_CALL, method); + } else { + mono_mb_emit_ldarg (mb, 3); + mono_mb_emit_calli (mb, callsig); + } + + if (sig->ret->byref) { + 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) { + case MONO_TYPE_VOID: + if (!string_ctor) + void_ret = TRUE; + break; + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_CHAR: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_VALUETYPE: + case MONO_TYPE_TYPEDBYREF: + case MONO_TYPE_GENERICINST: + /* box value types */ + mono_mb_emit_op (mb, CEE_BOX, mono_class_from_mono_type (sig->ret)); + break; + case MONO_TYPE_STRING: + case MONO_TYPE_CLASS: + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_OBJECT: + /* nothing to do */ + break; + case MONO_TYPE_PTR: + /* The result is an IntPtr */ + mono_mb_emit_op (mb, CEE_BOX, mono_defaults.int_class); + break; + default: + g_assert_not_reached (); + } + + if (!void_ret) + mono_mb_emit_stloc (mb, loc_res); + + /* Convert back nullable-byref arguments */ + for (i = 0; i < sig->param_count; i++) { + MonoType *t = sig->params [i]; + + /* + * Box the result and put it back into the array, the caller will have + * to obtain it from there. + */ + if (t->byref && t->type == MONO_TYPE_GENERICINST && mono_class_is_nullable (mono_class_from_mono_type (t))) { + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_icon (mb, sizeof (gpointer) * i); + mono_mb_emit_byte (mb, CEE_ADD); + + mono_mb_emit_ldloc (mb, tmp_nullable_locals [i]); + mono_mb_emit_op (mb, CEE_BOX, mono_class_from_mono_type (t)); + + mono_mb_emit_byte (mb, CEE_STIND_REF); + } + } + + g_free (tmp_nullable_locals); +} + +static void +emit_runtime_invoke_body (MonoMethodBuilder *mb, MonoImage *image, MonoMethod *method, + MonoMethodSignature *sig, MonoMethodSignature *callsig, + gboolean virtual_, gboolean need_direct_wrapper) +{ + gint32 labels [16]; + MonoExceptionClause *clause; + int loc_res, loc_exc; + + /* The wrapper looks like this: + * + * + * if (exc) { + * try { + * return + * } catch (Exception e) { + * *exc = e; + * } + * } else { + * return + * } + */ + + /* allocate local 0 (object) tmp */ + loc_res = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + /* allocate local 1 (object) exc */ + loc_exc = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + + /* *exc is assumed to be initialized to NULL by the caller */ + + mono_mb_emit_byte (mb, CEE_LDARG_2); + labels [0] = mono_mb_emit_branch (mb, CEE_BRFALSE); + + /* + * if (exc) case + */ + labels [1] = mono_mb_get_label (mb); + emit_thread_force_interrupt_checkpoint (mb); + emit_invoke_call (mb, method, sig, callsig, loc_res, virtual_, need_direct_wrapper); + + labels [2] = mono_mb_emit_branch (mb, CEE_LEAVE); + + /* Add a try clause around the call */ + clause = (MonoExceptionClause *)mono_image_alloc0 (image, sizeof (MonoExceptionClause)); + clause->flags = MONO_EXCEPTION_CLAUSE_NONE; + clause->data.catch_class = mono_defaults.exception_class; + clause->try_offset = labels [1]; + clause->try_len = mono_mb_get_label (mb) - labels [1]; + + clause->handler_offset = mono_mb_get_label (mb); + + /* handler code */ + mono_mb_emit_stloc (mb, loc_exc); + mono_mb_emit_byte (mb, CEE_LDARG_2); + mono_mb_emit_ldloc (mb, loc_exc); + mono_mb_emit_byte (mb, CEE_STIND_REF); + + mono_mb_emit_branch (mb, CEE_LEAVE); + + clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset; + + mono_mb_set_clauses (mb, 1, clause); + + mono_mb_patch_branch (mb, labels [2]); + mono_mb_emit_ldloc (mb, loc_res); + mono_mb_emit_byte (mb, CEE_RET); + + /* + * if (!exc) case + */ + mono_mb_patch_branch (mb, labels [0]); + emit_thread_force_interrupt_checkpoint (mb); + emit_invoke_call (mb, method, sig, callsig, loc_res, virtual_, need_direct_wrapper); + + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte (mb, CEE_RET); +} +#endif + +/** + * mono_marshal_get_runtime_invoke: + * Generates IL code for the runtime invoke function: + * + * MonoObject *runtime_invoke (MonoObject *this_obj, void **params, MonoObject **exc, void* method) + * + * We also catch exceptions if \p exc is not NULL. + * If \p virtual is TRUE, then \p method is invoked virtually on \p this. This is useful since + * it means that the compiled code for \p method does not have to be looked up + * before calling the runtime invoke wrapper. In this case, the wrapper ignores + * its \p method argument. + */ +MonoMethod * +mono_marshal_get_runtime_invoke_full (MonoMethod *method, gboolean virtual_, gboolean need_direct_wrapper) +{ + MonoMethodSignature *sig, *csig, *callsig; + MonoMethodBuilder *mb; + GHashTable *cache = NULL; + MonoClass *target_klass; + MonoMethod *res = NULL; + static MonoMethodSignature *cctor_signature = NULL; + static MonoMethodSignature *finalize_signature = NULL; + char *name; + const char *param_names [16]; + WrapperInfo *info; + + g_assert (method); + + if (!cctor_signature) { + cctor_signature = mono_metadata_signature_alloc (mono_defaults.corlib, 0); + cctor_signature->ret = &mono_defaults.void_class->byval_arg; + } + if (!finalize_signature) { + finalize_signature = mono_metadata_signature_alloc (mono_defaults.corlib, 0); + finalize_signature->ret = &mono_defaults.void_class->byval_arg; + finalize_signature->hasthis = 1; + } + + /* + * Use a separate cache indexed by methods to speed things up and to avoid the + * boundless mempool growth caused by the signature_dup stuff below. + */ + if (virtual_) + cache = get_cache (&method->klass->image->runtime_invoke_vcall_cache, mono_aligned_addr_hash, NULL); + else + cache = get_cache (&mono_method_get_wrapper_cache (method)->runtime_invoke_direct_cache, mono_aligned_addr_hash, NULL); + + res = mono_marshal_find_in_cache (cache, method); + if (res) + return res; + + if (method->string_ctor) { + callsig = lookup_string_ctor_signature (mono_method_signature (method)); + if (!callsig) + callsig = add_string_ctor_signature (method); + } else { + if (method_is_dynamic (method)) + callsig = mono_metadata_signature_dup_full (method->klass->image, mono_method_signature (method)); + else + callsig = mono_method_signature (method); + } + + sig = mono_method_signature (method); + + target_klass = get_wrapper_target_class (method->klass->image); + + /* Try to share wrappers for non-corlib methods with simple signatures */ + if (mono_metadata_signature_equal (callsig, cctor_signature)) { + callsig = cctor_signature; + target_klass = mono_defaults.object_class; + } else if (mono_metadata_signature_equal (callsig, finalize_signature)) { + callsig = finalize_signature; + target_klass = mono_defaults.object_class; + } + + if (need_direct_wrapper) { + /* Already searched at the start */ + } else { + MonoMethodSignature *tmp_sig; + + callsig = mono_marshal_get_runtime_invoke_sig (callsig); + GHashTable **cache_table = NULL; + + if (method->klass->valuetype && mono_method_signature (method)->hasthis) + cache_table = &mono_method_get_wrapper_cache (method)->runtime_invoke_vtype_cache; + else + cache_table = &mono_method_get_wrapper_cache (method)->runtime_invoke_cache; + + cache = get_cache (cache_table, (GHashFunc)mono_signature_hash, + (GCompareFunc)runtime_invoke_signature_equal); + + /* from mono_marshal_find_in_cache */ + mono_marshal_lock (); + res = (MonoMethod *)g_hash_table_lookup (cache, callsig); + mono_marshal_unlock (); + + if (res) { + g_free (callsig); + return res; + } + + /* Make a copy of the signature from the image mempool */ + tmp_sig = callsig; + callsig = mono_metadata_signature_dup_full (target_klass->image, callsig); + g_free (tmp_sig); + } + + csig = mono_metadata_signature_alloc (target_klass->image, 4); + + csig->ret = &mono_defaults.object_class->byval_arg; + if (method->klass->valuetype && mono_method_signature (method)->hasthis) + csig->params [0] = get_runtime_invoke_type (&method->klass->this_arg, FALSE); + else + csig->params [0] = &mono_defaults.object_class->byval_arg; + csig->params [1] = &mono_defaults.int_class->byval_arg; + csig->params [2] = &mono_defaults.int_class->byval_arg; + csig->params [3] = &mono_defaults.int_class->byval_arg; + csig->pinvoke = 1; +#if TARGET_WIN32 + /* This is called from runtime code so it has to be cdecl */ + csig->call_convention = MONO_CALL_C; +#endif + + name = mono_signature_to_name (callsig, virtual_ ? "runtime_invoke_virtual" : (need_direct_wrapper ? "runtime_invoke_direct" : "runtime_invoke")); + mb = mono_mb_new (target_klass, name, MONO_WRAPPER_RUNTIME_INVOKE); + g_free (name); + +#ifdef ENABLE_ILGEN + param_names [0] = "this"; + param_names [1] = "params"; + param_names [2] = "exc"; + param_names [3] = "method"; + mono_mb_set_param_names (mb, param_names); + + emit_runtime_invoke_body (mb, target_klass->image, method, sig, callsig, virtual_, need_direct_wrapper); +#endif + + if (need_direct_wrapper) { +#ifdef ENABLE_ILGEN + mb->skip_visibility = 1; +#endif + info = mono_wrapper_info_create (mb, virtual_ ? WRAPPER_SUBTYPE_RUNTIME_INVOKE_VIRTUAL : WRAPPER_SUBTYPE_RUNTIME_INVOKE_DIRECT); + info->d.runtime_invoke.method = method; + res = mono_mb_create_and_cache_full (cache, method, mb, csig, sig->param_count + 16, info, NULL); + } else { + /* taken from mono_mb_create_and_cache */ + mono_marshal_lock (); + res = (MonoMethod *)g_hash_table_lookup (cache, callsig); + mono_marshal_unlock (); + + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_RUNTIME_INVOKE_NORMAL); + info->d.runtime_invoke.sig = callsig; + + /* Somebody may have created it before us */ + if (!res) { + MonoMethod *newm; + newm = mono_mb_create (mb, csig, sig->param_count + 16, info); + + mono_marshal_lock (); + res = (MonoMethod *)g_hash_table_lookup (cache, callsig); + if (!res) { + GHashTable *direct_cache; + res = newm; + g_hash_table_insert (cache, callsig, res); + /* Can't insert it into wrapper_hash since the key is a signature */ + direct_cache = mono_method_get_wrapper_cache (method)->runtime_invoke_direct_cache; + + g_hash_table_insert (direct_cache, method, res); + } else { + mono_free_method (newm); + } + mono_marshal_unlock (); + } + + /* end mono_mb_create_and_cache */ + } + + mono_mb_free (mb); + + return res; +} + +MonoMethod * +mono_marshal_get_runtime_invoke (MonoMethod *method, gboolean virtual_) +{ + gboolean need_direct_wrapper = FALSE; + + if (virtual_) + need_direct_wrapper = TRUE; + + if (method->dynamic) + need_direct_wrapper = TRUE; + + if (method->klass->rank && (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) && + (method->iflags & METHOD_IMPL_ATTRIBUTE_NATIVE)) { + /* + * Array Get/Set/Address methods. The JIT implements them using inline code + * so we need to create an invoke wrapper which calls the method directly. + */ + need_direct_wrapper = TRUE; + } + + if (method->string_ctor) { + /* Can't share this as we push a string as this */ + need_direct_wrapper = TRUE; + } + + return mono_marshal_get_runtime_invoke_full (method, virtual_, need_direct_wrapper); +} + +/* + * mono_marshal_get_runtime_invoke_dynamic: + * + * Return a method which can be used to invoke managed methods from native code + * dynamically. + * The signature of the returned method is given by RuntimeInvokeDynamicFunction: + * void runtime_invoke (void *args, MonoObject **exc, void *compiled_method) + * ARGS should point to an architecture specific structure containing + * the arguments and space for the return value. + * The other arguments are the same as for runtime_invoke (), except that + * ARGS should contain the this argument too. + * This wrapper serves the same purpose as the runtime-invoke wrappers, but there + * is only one copy of it, which is useful in full-aot. + */ +MonoMethod* +mono_marshal_get_runtime_invoke_dynamic (void) +{ + static MonoMethod *method; + MonoMethodSignature *csig; + MonoExceptionClause *clause; + MonoMethodBuilder *mb; + int pos; + char *name; + WrapperInfo *info; + + if (method) + return method; + + csig = mono_metadata_signature_alloc (mono_defaults.corlib, 4); + + csig->ret = &mono_defaults.void_class->byval_arg; + csig->params [0] = &mono_defaults.int_class->byval_arg; + csig->params [1] = &mono_defaults.int_class->byval_arg; + csig->params [2] = &mono_defaults.int_class->byval_arg; + csig->params [3] = &mono_defaults.int_class->byval_arg; + + name = g_strdup ("runtime_invoke_dynamic"); + mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_RUNTIME_INVOKE); + g_free (name); + +#ifdef ENABLE_ILGEN + /* allocate local 0 (object) tmp */ + mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + /* allocate local 1 (object) exc */ + mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + + /* cond set *exc to null */ + mono_mb_emit_byte (mb, CEE_LDARG_1); + mono_mb_emit_byte (mb, CEE_BRFALSE_S); + mono_mb_emit_byte (mb, 3); + mono_mb_emit_byte (mb, CEE_LDARG_1); + mono_mb_emit_byte (mb, CEE_LDNULL); + mono_mb_emit_byte (mb, CEE_STIND_REF); + + emit_thread_force_interrupt_checkpoint (mb); + + mono_mb_emit_byte (mb, CEE_LDARG_0); + mono_mb_emit_byte (mb, CEE_LDARG_2); + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_DYN_CALL); + + pos = mono_mb_emit_branch (mb, CEE_LEAVE); + + clause = (MonoExceptionClause *)mono_image_alloc0 (mono_defaults.corlib, sizeof (MonoExceptionClause)); + clause->flags = MONO_EXCEPTION_CLAUSE_FILTER; + clause->try_len = mono_mb_get_label (mb); + + /* filter code */ + clause->data.filter_offset = mono_mb_get_label (mb); + + mono_mb_emit_byte (mb, CEE_POP); + mono_mb_emit_byte (mb, CEE_LDARG_1); + mono_mb_emit_byte (mb, CEE_LDC_I4_0); + mono_mb_emit_byte (mb, CEE_PREFIX1); + mono_mb_emit_byte (mb, CEE_CGT_UN); + mono_mb_emit_byte (mb, CEE_PREFIX1); + mono_mb_emit_byte (mb, CEE_ENDFILTER); + + clause->handler_offset = mono_mb_get_label (mb); + + /* handler code */ + /* store exception */ + mono_mb_emit_stloc (mb, 1); + + mono_mb_emit_byte (mb, CEE_LDARG_1); + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_byte (mb, CEE_STIND_REF); + + mono_mb_emit_byte (mb, CEE_LDNULL); + mono_mb_emit_stloc (mb, 0); + + mono_mb_emit_branch (mb, CEE_LEAVE); + + clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset; + + mono_mb_set_clauses (mb, 1, clause); + + /* return result */ + mono_mb_patch_branch (mb, pos); + //mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte (mb, CEE_RET); +#endif /* ENABLE_ILGEN */ + + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_RUNTIME_INVOKE_DYNAMIC); + + mono_marshal_lock (); + /* double-checked locking */ + if (!method) + method = mono_mb_create (mb, csig, 16, info); + + mono_marshal_unlock (); + + mono_mb_free (mb); + + return method; +} + +/* + * mono_marshal_get_runtime_invoke_for_sig: + * + * Return a runtime invoke wrapper for a given signature. + */ +MonoMethod * +mono_marshal_get_runtime_invoke_for_sig (MonoMethodSignature *sig) +{ + MonoMethodSignature *csig, *callsig; + MonoMethodBuilder *mb; + MonoImage *image; + GHashTable *cache = NULL; + GHashTable **cache_table = NULL; + MonoMethod *res = NULL; + char *name; + const char *param_names [16]; + WrapperInfo *info; + + /* A simplified version of mono_marshal_get_runtime_invoke */ + + image = mono_defaults.corlib; + + callsig = mono_marshal_get_runtime_invoke_sig (sig); + + cache_table = &image->wrapper_caches.runtime_invoke_sig_cache; + + cache = get_cache (cache_table, (GHashFunc)mono_signature_hash, + (GCompareFunc)runtime_invoke_signature_equal); + + /* from mono_marshal_find_in_cache */ + mono_marshal_lock (); + res = (MonoMethod *)g_hash_table_lookup (cache, callsig); + mono_marshal_unlock (); + + if (res) { + g_free (callsig); + return res; + } + + /* Make a copy of the signature from the image mempool */ + callsig = mono_metadata_signature_dup_full (image, callsig); + + csig = mono_metadata_signature_alloc (image, 4); + csig->ret = &mono_defaults.object_class->byval_arg; + csig->params [0] = &mono_defaults.object_class->byval_arg; + csig->params [1] = &mono_defaults.int_class->byval_arg; + csig->params [2] = &mono_defaults.int_class->byval_arg; + csig->params [3] = &mono_defaults.int_class->byval_arg; + csig->pinvoke = 1; +#if TARGET_WIN32 + /* This is called from runtime code so it has to be cdecl */ + csig->call_convention = MONO_CALL_C; +#endif + + name = mono_signature_to_name (callsig, "runtime_invoke_sig"); + mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_RUNTIME_INVOKE); + g_free (name); + +#ifdef ENABLE_ILGEN + param_names [0] = "this"; + param_names [1] = "params"; + param_names [2] = "exc"; + param_names [3] = "method"; + mono_mb_set_param_names (mb, param_names); + + emit_runtime_invoke_body (mb, image, NULL, sig, callsig, FALSE, FALSE); +#endif + + /* taken from mono_mb_create_and_cache */ + mono_marshal_lock (); + res = (MonoMethod *)g_hash_table_lookup (cache, callsig); + mono_marshal_unlock (); + + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_RUNTIME_INVOKE_NORMAL); + info->d.runtime_invoke.sig = callsig; + + /* Somebody may have created it before us */ + if (!res) { + MonoMethod *newm; + newm = mono_mb_create (mb, csig, sig->param_count + 16, info); + + mono_marshal_lock (); + res = (MonoMethod *)g_hash_table_lookup (cache, callsig); + if (!res) { + res = newm; + g_hash_table_insert (cache, callsig, res); + } else { + mono_free_method (newm); + } + mono_marshal_unlock (); + } + + /* end mono_mb_create_and_cache */ + + mono_mb_free (mb); + + return res; +} + +#ifdef ENABLE_ILGEN +static void +mono_mb_emit_auto_layout_exception (MonoMethodBuilder *mb, MonoClass *klass) +{ + char *msg = g_strdup_printf ("The type `%s.%s' layout needs to be Sequential or Explicit", + klass->name_space, klass->name); + + mono_mb_emit_exception_marshal_directive (mb, msg); +} +#endif + +/** + * mono_marshal_get_icall_wrapper: + * Generates IL code for the icall wrapper. The generated method + * calls the unmanaged code in \p func. + */ +MonoMethod * +mono_marshal_get_icall_wrapper (MonoMethodSignature *sig, const char *name, gconstpointer func, gboolean check_exceptions) +{ + MonoMethodSignature *csig, *csig2; + MonoMethodBuilder *mb; + MonoMethod *res; + int i; + WrapperInfo *info; + + GHashTable *cache = get_cache (&mono_defaults.object_class->image->icall_wrapper_cache, mono_aligned_addr_hash, NULL); + if ((res = mono_marshal_find_in_cache (cache, (gpointer) func))) + return res; + + g_assert (sig->pinvoke); + + mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_MANAGED_TO_NATIVE); + + mb->method->save_lmf = 1; + + /* Add an explicit this argument */ + if (sig->hasthis) + csig2 = mono_metadata_signature_dup_add_this (mono_defaults.corlib, sig, mono_defaults.object_class); + else + csig2 = mono_metadata_signature_dup_full (mono_defaults.corlib, sig); + +#ifdef ENABLE_ILGEN + if (sig->hasthis) + mono_mb_emit_byte (mb, CEE_LDARG_0); + + for (i = 0; i < sig->param_count; i++) + mono_mb_emit_ldarg (mb, i + sig->hasthis); + + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_op (mb, CEE_MONO_JIT_ICALL_ADDR, (gpointer)func); + mono_mb_emit_calli (mb, csig2); + if (check_exceptions) + emit_thread_interrupt_checkpoint (mb); + mono_mb_emit_byte (mb, CEE_RET); +#endif + + csig = mono_metadata_signature_dup_full (mono_defaults.corlib, sig); + csig->pinvoke = 0; + if (csig->call_convention == MONO_CALL_VARARG) + csig->call_convention = 0; + + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_ICALL_WRAPPER); + info->d.icall.func = (gpointer)func; + res = mono_mb_create_and_cache_full (cache, (gpointer) func, mb, csig, csig->param_count + 16, info, NULL); + mono_mb_free (mb); + + return res; +} + +static int +emit_marshal_custom (EmitMarshalContext *m, int argnum, MonoType *t, + MonoMarshalSpec *spec, + int conv_arg, MonoType **conv_arg_type, + MarshalAction action) +{ +#ifndef ENABLE_ILGEN + if (action == MARSHAL_ACTION_CONV_IN && t->type == MONO_TYPE_VALUETYPE) + *conv_arg_type = &mono_defaults.int_class->byval_arg; + return conv_arg; +#else + MonoError error; + MonoType *mtype; + MonoClass *mklass; + static MonoClass *ICustomMarshaler = NULL; + static MonoMethod *cleanup_native, *cleanup_managed; + static MonoMethod *marshal_managed_to_native, *marshal_native_to_managed; + MonoMethod *get_instance = NULL; + MonoMethodBuilder *mb = m->mb; + char *exception_msg = NULL; + guint32 loc1; + int pos2; + + if (!ICustomMarshaler) { + MonoClass *klass = mono_class_try_get_icustom_marshaler_class (); + if (!klass) { + exception_msg = g_strdup ("Current profile doesn't support ICustomMarshaler"); + goto handle_exception; + } + + cleanup_native = mono_class_get_method_from_name (klass, "CleanUpNativeData", 1); + g_assert (cleanup_native); + cleanup_managed = mono_class_get_method_from_name (klass, "CleanUpManagedData", 1); + g_assert (cleanup_managed); + marshal_managed_to_native = mono_class_get_method_from_name (klass, "MarshalManagedToNative", 1); + g_assert (marshal_managed_to_native); + marshal_native_to_managed = mono_class_get_method_from_name (klass, "MarshalNativeToManaged", 1); + g_assert (marshal_native_to_managed); + + mono_memory_barrier (); + ICustomMarshaler = klass; + } + + if (spec->data.custom_data.image) + mtype = mono_reflection_type_from_name_checked (spec->data.custom_data.custom_name, spec->data.custom_data.image, &error); + else + mtype = mono_reflection_type_from_name_checked (spec->data.custom_data.custom_name, m->image, &error); + g_assert (mtype != NULL); + mono_error_assert_ok (&error); + mklass = mono_class_from_mono_type (mtype); + g_assert (mklass != NULL); + + if (!mono_class_is_assignable_from (ICustomMarshaler, mklass)) + exception_msg = g_strdup_printf ("Custom marshaler '%s' does not implement the ICustomMarshaler interface.", mklass->name); + + get_instance = mono_class_get_method_from_name_flags (mklass, "GetInstance", 1, METHOD_ATTRIBUTE_STATIC); + if (get_instance) { + MonoMethodSignature *get_sig = mono_method_signature (get_instance); + if ((get_sig->ret->type != MONO_TYPE_CLASS) || + (mono_class_from_mono_type (get_sig->ret) != ICustomMarshaler) || + (get_sig->params [0]->type != MONO_TYPE_STRING)) + get_instance = NULL; + } + + if (!get_instance) + exception_msg = g_strdup_printf ("Custom marshaler '%s' does not implement a static GetInstance method that takes a single string parameter and returns an ICustomMarshaler.", mklass->name); + +handle_exception: + /* Throw exception and emit compensation code if neccesary */ + if (exception_msg) { + switch (action) { + case MARSHAL_ACTION_CONV_IN: + case MARSHAL_ACTION_CONV_RESULT: + case MARSHAL_ACTION_MANAGED_CONV_RESULT: + if ((action == MARSHAL_ACTION_CONV_RESULT) || (action == MARSHAL_ACTION_MANAGED_CONV_RESULT)) + mono_mb_emit_byte (mb, CEE_POP); + + mono_mb_emit_exception_full (mb, "System", "ApplicationException", exception_msg); + + break; + case MARSHAL_ACTION_PUSH: + mono_mb_emit_byte (mb, CEE_LDNULL); + break; + default: + break; + } + return 0; + } + + /* FIXME: MS.NET seems to create one instance for each klass + cookie pair */ + /* FIXME: MS.NET throws an exception if GetInstance returns null */ + + switch (action) { + case MARSHAL_ACTION_CONV_IN: + switch (t->type) { + case MONO_TYPE_CLASS: + case MONO_TYPE_OBJECT: + case MONO_TYPE_STRING: + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_VALUETYPE: + break; + + default: + g_warning ("custom marshalling of type %x is currently not supported", t->type); + g_assert_not_reached (); + break; + } + + conv_arg = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + + mono_mb_emit_byte (mb, CEE_LDNULL); + mono_mb_emit_stloc (mb, conv_arg); + + if (t->byref && (t->attrs & PARAM_ATTRIBUTE_OUT)) + break; + + /* Minic MS.NET behavior */ + if (!t->byref && (t->attrs & PARAM_ATTRIBUTE_OUT) && !(t->attrs & PARAM_ATTRIBUTE_IN)) + break; + + /* Check for null */ + mono_mb_emit_ldarg (mb, argnum); + if (t->byref) + mono_mb_emit_byte (mb, CEE_LDIND_I); + pos2 = mono_mb_emit_branch (mb, CEE_BRFALSE); + + mono_mb_emit_ldstr (mb, g_strdup (spec->data.custom_data.cookie)); + + mono_mb_emit_op (mb, CEE_CALL, get_instance); + + mono_mb_emit_ldarg (mb, argnum); + if (t->byref) + mono_mb_emit_byte (mb, CEE_LDIND_REF); + + if (t->type == MONO_TYPE_VALUETYPE) { + /* + * Since we can't determine the type of the argument, we + * will assume the unmanaged function takes a pointer. + */ + *conv_arg_type = &mono_defaults.int_class->byval_arg; + + mono_mb_emit_op (mb, CEE_BOX, mono_class_from_mono_type (t)); + } + + mono_mb_emit_op (mb, CEE_CALLVIRT, marshal_managed_to_native); + mono_mb_emit_stloc (mb, conv_arg); + + mono_mb_patch_branch (mb, pos2); + break; + + case MARSHAL_ACTION_CONV_OUT: + /* Check for null */ + mono_mb_emit_ldloc (mb, conv_arg); + pos2 = mono_mb_emit_branch (mb, CEE_BRFALSE); + + if (t->byref) { + mono_mb_emit_ldarg (mb, argnum); + + mono_mb_emit_ldstr (mb, g_strdup (spec->data.custom_data.cookie)); + + mono_mb_emit_op (mb, CEE_CALL, get_instance); + + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_op (mb, CEE_CALLVIRT, marshal_native_to_managed); + mono_mb_emit_byte (mb, CEE_STIND_REF); + } else if (t->attrs & PARAM_ATTRIBUTE_OUT) { + mono_mb_emit_ldstr (mb, g_strdup (spec->data.custom_data.cookie)); + + mono_mb_emit_op (mb, CEE_CALL, get_instance); + + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_op (mb, CEE_CALLVIRT, marshal_native_to_managed); + + /* We have nowhere to store the result */ + mono_mb_emit_byte (mb, CEE_POP); + } + + mono_mb_emit_ldstr (mb, g_strdup (spec->data.custom_data.cookie)); + + mono_mb_emit_op (mb, CEE_CALL, get_instance); + + mono_mb_emit_ldloc (mb, conv_arg); + + mono_mb_emit_op (mb, CEE_CALLVIRT, cleanup_native); + + mono_mb_patch_branch (mb, pos2); + break; + + case MARSHAL_ACTION_PUSH: + if (t->byref) + mono_mb_emit_ldloc_addr (mb, conv_arg); + else + mono_mb_emit_ldloc (mb, conv_arg); + break; + + case MARSHAL_ACTION_CONV_RESULT: + loc1 = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + + mono_mb_emit_stloc (mb, 3); + + mono_mb_emit_ldloc (mb, 3); + mono_mb_emit_stloc (mb, loc1); + + /* Check for null */ + mono_mb_emit_ldloc (mb, 3); + pos2 = mono_mb_emit_branch (mb, CEE_BRFALSE); + + mono_mb_emit_ldstr (mb, g_strdup (spec->data.custom_data.cookie)); + + mono_mb_emit_op (mb, CEE_CALL, get_instance); + mono_mb_emit_byte (mb, CEE_DUP); + + mono_mb_emit_ldloc (mb, 3); + mono_mb_emit_op (mb, CEE_CALLVIRT, marshal_native_to_managed); + mono_mb_emit_stloc (mb, 3); + + mono_mb_emit_ldloc (mb, loc1); + mono_mb_emit_op (mb, CEE_CALLVIRT, cleanup_native); + + mono_mb_patch_branch (mb, pos2); + break; + + case MARSHAL_ACTION_MANAGED_CONV_IN: + conv_arg = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + + mono_mb_emit_byte (mb, CEE_LDNULL); + mono_mb_emit_stloc (mb, conv_arg); + + if (t->byref && t->attrs & PARAM_ATTRIBUTE_OUT) + break; + + /* Check for null */ + mono_mb_emit_ldarg (mb, argnum); + if (t->byref) + mono_mb_emit_byte (mb, CEE_LDIND_I); + pos2 = mono_mb_emit_branch (mb, CEE_BRFALSE); + + mono_mb_emit_ldstr (mb, g_strdup (spec->data.custom_data.cookie)); + mono_mb_emit_op (mb, CEE_CALL, get_instance); + + mono_mb_emit_ldarg (mb, argnum); + if (t->byref) + mono_mb_emit_byte (mb, CEE_LDIND_I); + + mono_mb_emit_op (mb, CEE_CALLVIRT, marshal_native_to_managed); + mono_mb_emit_stloc (mb, conv_arg); + + mono_mb_patch_branch (mb, pos2); + break; + + case MARSHAL_ACTION_MANAGED_CONV_RESULT: + g_assert (!t->byref); + + loc1 = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + + mono_mb_emit_stloc (mb, 3); + + mono_mb_emit_ldloc (mb, 3); + mono_mb_emit_stloc (mb, loc1); + + /* Check for null */ + mono_mb_emit_ldloc (mb, 3); + pos2 = mono_mb_emit_branch (mb, CEE_BRFALSE); + + mono_mb_emit_ldstr (mb, g_strdup (spec->data.custom_data.cookie)); + mono_mb_emit_op (mb, CEE_CALL, get_instance); + mono_mb_emit_byte (mb, CEE_DUP); + + mono_mb_emit_ldloc (mb, 3); + mono_mb_emit_op (mb, CEE_CALLVIRT, marshal_managed_to_native); + mono_mb_emit_stloc (mb, 3); + + mono_mb_emit_ldloc (mb, loc1); + mono_mb_emit_op (mb, CEE_CALLVIRT, cleanup_managed); + + mono_mb_patch_branch (mb, pos2); + break; + + case MARSHAL_ACTION_MANAGED_CONV_OUT: + + /* Check for null */ + mono_mb_emit_ldloc (mb, conv_arg); + pos2 = mono_mb_emit_branch (mb, CEE_BRFALSE); + + if (t->byref) { + mono_mb_emit_ldarg (mb, argnum); + + mono_mb_emit_ldstr (mb, g_strdup (spec->data.custom_data.cookie)); + + mono_mb_emit_op (mb, CEE_CALL, get_instance); + + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_op (mb, CEE_CALLVIRT, marshal_managed_to_native); + mono_mb_emit_byte (mb, CEE_STIND_I); + } + + /* Call CleanUpManagedData */ + mono_mb_emit_ldstr (mb, g_strdup (spec->data.custom_data.cookie)); + + mono_mb_emit_op (mb, CEE_CALL, get_instance); + + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_op (mb, CEE_CALLVIRT, cleanup_managed); + + mono_mb_patch_branch (mb, pos2); + break; + + default: + g_assert_not_reached (); + } + return conv_arg; +#endif + +} + +static int +emit_marshal_asany (EmitMarshalContext *m, int argnum, MonoType *t, + MonoMarshalSpec *spec, + int conv_arg, MonoType **conv_arg_type, + MarshalAction action) +{ +#ifdef ENABLE_ILGEN + MonoMethodBuilder *mb = m->mb; + + switch (action) { + case MARSHAL_ACTION_CONV_IN: { + MonoMarshalNative encoding = mono_marshal_get_string_encoding (m->piinfo, NULL); + + g_assert (t->type == MONO_TYPE_OBJECT); + g_assert (!t->byref); + + conv_arg = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_icon (mb, encoding); + mono_mb_emit_icon (mb, t->attrs); + mono_mb_emit_icall (mb, mono_marshal_asany); + mono_mb_emit_stloc (mb, conv_arg); + break; + } + + case MARSHAL_ACTION_PUSH: + mono_mb_emit_ldloc (mb, conv_arg); + break; + + case MARSHAL_ACTION_CONV_OUT: { + MonoMarshalNative encoding = mono_marshal_get_string_encoding (m->piinfo, NULL); + + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_icon (mb, encoding); + mono_mb_emit_icon (mb, t->attrs); + mono_mb_emit_icall (mb, mono_marshal_free_asany); + break; + } + + default: + g_assert_not_reached (); + } +#endif + return conv_arg; +} + +static int +emit_marshal_vtype (EmitMarshalContext *m, int argnum, MonoType *t, + MonoMarshalSpec *spec, + int conv_arg, MonoType **conv_arg_type, + MarshalAction action) +{ +#ifdef ENABLE_ILGEN + MonoMethodBuilder *mb = m->mb; + MonoClass *klass, *date_time_class; + int pos = 0, pos2; + + klass = mono_class_from_mono_type (t); + + date_time_class = mono_class_get_date_time_class (); + + switch (action) { + case MARSHAL_ACTION_CONV_IN: + if (klass == date_time_class) { + /* Convert it to an OLE DATE type */ + static MonoMethod *to_oadate; + + if (!to_oadate) + to_oadate = mono_class_get_method_from_name (date_time_class, "ToOADate", 0); + g_assert (to_oadate); + + conv_arg = mono_mb_add_local (mb, &mono_defaults.double_class->byval_arg); + + if (t->byref) { + mono_mb_emit_ldarg (mb, argnum); + pos = mono_mb_emit_branch (mb, CEE_BRFALSE); + } + + if (!(t->byref && !(t->attrs & PARAM_ATTRIBUTE_IN) && (t->attrs & PARAM_ATTRIBUTE_OUT))) { + if (!t->byref) + m->csig->params [argnum - m->csig->hasthis] = &mono_defaults.double_class->byval_arg; + + mono_mb_emit_ldarg_addr (mb, argnum); + mono_mb_emit_managed_call (mb, to_oadate, NULL); + mono_mb_emit_stloc (mb, conv_arg); + } + + if (t->byref) + mono_mb_patch_branch (mb, pos); + break; + } + + if (mono_class_is_explicit_layout (klass) || klass->blittable || klass->enumtype) + break; + + conv_arg = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + + /* store the address of the source into local variable 0 */ + if (t->byref) + mono_mb_emit_ldarg (mb, argnum); + else + mono_mb_emit_ldarg_addr (mb, argnum); + + mono_mb_emit_stloc (mb, 0); + + /* allocate space for the native struct and + * store the address into local variable 1 (dest) */ + mono_mb_emit_icon (mb, mono_class_native_size (klass, NULL)); + mono_mb_emit_byte (mb, CEE_PREFIX1); + mono_mb_emit_byte (mb, CEE_LOCALLOC); + mono_mb_emit_stloc (mb, conv_arg); + + if (t->byref) { + mono_mb_emit_ldloc (mb, 0); + pos = mono_mb_emit_branch (mb, CEE_BRFALSE); + } + + if (!(t->byref && !(t->attrs & PARAM_ATTRIBUTE_IN) && (t->attrs & PARAM_ATTRIBUTE_OUT))) { + /* set dst_ptr */ + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_stloc (mb, 1); + + /* emit valuetype conversion code */ + emit_struct_conv (mb, klass, FALSE); + } + + if (t->byref) + mono_mb_patch_branch (mb, pos); + break; + + case MARSHAL_ACTION_PUSH: + if (spec && spec->native == MONO_NATIVE_LPSTRUCT) { + /* FIXME: */ + g_assert (!t->byref); + + /* Have to change the signature since the vtype is passed byref */ + m->csig->params [argnum - m->csig->hasthis] = &mono_defaults.int_class->byval_arg; + + if (mono_class_is_explicit_layout (klass) || klass->blittable || klass->enumtype) + mono_mb_emit_ldarg_addr (mb, argnum); + else + mono_mb_emit_ldloc (mb, conv_arg); + break; + } + + if (klass == date_time_class) { + if (t->byref) + mono_mb_emit_ldloc_addr (mb, conv_arg); + else + mono_mb_emit_ldloc (mb, conv_arg); + break; + } + + if (mono_class_is_explicit_layout (klass) || klass->blittable || klass->enumtype) { + mono_mb_emit_ldarg (mb, argnum); + break; + } + mono_mb_emit_ldloc (mb, conv_arg); + if (!t->byref) { + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_op (mb, CEE_MONO_LDNATIVEOBJ, klass); + } + break; + + case MARSHAL_ACTION_CONV_OUT: + if (klass == date_time_class) { + /* Convert from an OLE DATE type */ + static MonoMethod *from_oadate; + + if (!t->byref) + break; + + if (!((t->attrs & PARAM_ATTRIBUTE_IN) && !(t->attrs & PARAM_ATTRIBUTE_OUT))) { + if (!from_oadate) + from_oadate = mono_class_get_method_from_name (date_time_class, "FromOADate", 1); + g_assert (from_oadate); + + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_managed_call (mb, from_oadate, NULL); + mono_mb_emit_op (mb, CEE_STOBJ, date_time_class); + } + break; + } + + if (mono_class_is_explicit_layout (klass) || klass->blittable || klass->enumtype) + break; + + if (t->byref) { + /* dst = argument */ + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_stloc (mb, 1); + + mono_mb_emit_ldloc (mb, 1); + pos = mono_mb_emit_branch (mb, CEE_BRFALSE); + + if (!((t->attrs & PARAM_ATTRIBUTE_IN) && !(t->attrs & PARAM_ATTRIBUTE_OUT))) { + /* src = tmp_locals [i] */ + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_stloc (mb, 0); + + /* emit valuetype conversion code */ + emit_struct_conv (mb, klass, TRUE); + } + } + + emit_struct_free (mb, klass, conv_arg); + + if (t->byref) + mono_mb_patch_branch (mb, pos); + break; + + case MARSHAL_ACTION_CONV_RESULT: + if (mono_class_is_explicit_layout (klass) || klass->blittable) { + mono_mb_emit_stloc (mb, 3); + break; + } + + /* load pointer to returned value type */ + g_assert (m->vtaddr_var); + mono_mb_emit_ldloc (mb, m->vtaddr_var); + /* store the address of the source into local variable 0 */ + mono_mb_emit_stloc (mb, 0); + /* set dst_ptr */ + mono_mb_emit_ldloc_addr (mb, 3); + mono_mb_emit_stloc (mb, 1); + + /* emit valuetype conversion code */ + emit_struct_conv (mb, klass, TRUE); + break; + + case MARSHAL_ACTION_MANAGED_CONV_IN: + if (mono_class_is_explicit_layout (klass) || klass->blittable || klass->enumtype) { + conv_arg = 0; + break; + } + + conv_arg = mono_mb_add_local (mb, &klass->byval_arg); + + if (t->attrs & PARAM_ATTRIBUTE_OUT) + break; + + if (t->byref) + mono_mb_emit_ldarg (mb, argnum); + else + mono_mb_emit_ldarg_addr (mb, argnum); + mono_mb_emit_stloc (mb, 0); + + if (t->byref) { + mono_mb_emit_ldloc (mb, 0); + pos = mono_mb_emit_branch (mb, CEE_BRFALSE); + } + + mono_mb_emit_ldloc_addr (mb, conv_arg); + mono_mb_emit_stloc (mb, 1); + + /* emit valuetype conversion code */ + emit_struct_conv (mb, klass, TRUE); + + if (t->byref) + mono_mb_patch_branch (mb, pos); + break; + + case MARSHAL_ACTION_MANAGED_CONV_OUT: + if (mono_class_is_explicit_layout (klass) || klass->blittable || klass->enumtype) + break; + if (t->byref && (t->attrs & PARAM_ATTRIBUTE_IN) && !(t->attrs & PARAM_ATTRIBUTE_OUT)) + break; + + /* Check for null */ + mono_mb_emit_ldarg (mb, argnum); + pos2 = mono_mb_emit_branch (mb, CEE_BRFALSE); + + /* Set src */ + mono_mb_emit_ldloc_addr (mb, conv_arg); + mono_mb_emit_stloc (mb, 0); + + /* Set dest */ + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_stloc (mb, 1); + + /* emit valuetype conversion code */ + emit_struct_conv (mb, klass, FALSE); + + mono_mb_patch_branch (mb, pos2); + break; + + case MARSHAL_ACTION_MANAGED_CONV_RESULT: + if (mono_class_is_explicit_layout (klass) || klass->blittable || klass->enumtype) { + mono_mb_emit_stloc (mb, 3); + m->retobj_var = 0; + break; + } + + /* load pointer to returned value type */ + g_assert (m->vtaddr_var); + mono_mb_emit_ldloc (mb, m->vtaddr_var); + + /* store the address of the source into local variable 0 */ + mono_mb_emit_stloc (mb, 0); + /* allocate space for the native struct and + * store the address into dst_ptr */ + m->retobj_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + m->retobj_class = klass; + g_assert (m->retobj_var); + mono_mb_emit_icon (mb, mono_class_native_size (klass, NULL)); + mono_mb_emit_byte (mb, CEE_CONV_I); + mono_mb_emit_icall (mb, ves_icall_marshal_alloc); + mono_mb_emit_stloc (mb, 1); + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_stloc (mb, m->retobj_var); + + /* emit valuetype conversion code */ + emit_struct_conv (mb, klass, FALSE); + break; + + default: + g_assert_not_reached (); + } +#endif + return conv_arg; +} + +static int +emit_marshal_string (EmitMarshalContext *m, int argnum, MonoType *t, + MonoMarshalSpec *spec, + int conv_arg, MonoType **conv_arg_type, + MarshalAction action) +{ +#ifndef ENABLE_ILGEN + switch (action) { + case MARSHAL_ACTION_CONV_IN: + *conv_arg_type = &mono_defaults.int_class->byval_arg; + break; + case MARSHAL_ACTION_MANAGED_CONV_IN: + *conv_arg_type = &mono_defaults.int_class->byval_arg; + break; + } +#else + MonoMethodBuilder *mb = m->mb; + MonoMarshalNative encoding = mono_marshal_get_string_encoding (m->piinfo, spec); + MonoMarshalConv conv = mono_marshal_get_string_to_ptr_conv (m->piinfo, spec); + gboolean need_free; + + switch (action) { + case MARSHAL_ACTION_CONV_IN: + *conv_arg_type = &mono_defaults.int_class->byval_arg; + conv_arg = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + + if (t->byref) { + if (t->attrs & PARAM_ATTRIBUTE_OUT) + break; + + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_byte (mb, CEE_LDIND_I); + } else { + mono_mb_emit_ldarg (mb, argnum); + } + + if (conv == MONO_MARSHAL_CONV_INVALID) { + char *msg = g_strdup_printf ("string marshalling conversion %d not implemented", encoding); + mono_mb_emit_exception_marshal_directive (mb, msg); + } else { + mono_mb_emit_icall (mb, conv_to_icall (conv, NULL)); + + mono_mb_emit_stloc (mb, conv_arg); + } + break; + + case MARSHAL_ACTION_CONV_OUT: + conv = mono_marshal_get_ptr_to_string_conv (m->piinfo, spec, &need_free); + if (conv == MONO_MARSHAL_CONV_INVALID) { + char *msg = g_strdup_printf ("string marshalling conversion %d not implemented", encoding); + mono_mb_emit_exception_marshal_directive (mb, msg); + break; + } + + if (encoding == MONO_NATIVE_VBBYREFSTR) { + static MonoMethod *m; + + if (!m) { + m = mono_class_get_method_from_name_flags (mono_defaults.string_class, "get_Length", -1, 0); + g_assert (m); + } + + if (!t->byref) { + char *msg = g_strdup_printf ("VBByRefStr marshalling requires a ref parameter.", encoding); + mono_mb_emit_exception_marshal_directive (mb, msg); + break; + } + + /* + * Have to allocate a new string with the same length as the original, and + * copy the contents of the buffer pointed to by CONV_ARG into it. + */ + g_assert (t->byref); + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_managed_call (mb, m, NULL); + mono_mb_emit_icall (mb, mono_string_new_len_wrapper); + mono_mb_emit_byte (mb, CEE_STIND_REF); + } else if (t->byref && (t->attrs & PARAM_ATTRIBUTE_OUT || !(t->attrs & PARAM_ATTRIBUTE_IN))) { + int stind_op; + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_icall (mb, conv_to_icall (conv, &stind_op)); + mono_mb_emit_byte (mb, stind_op); + need_free = TRUE; + } + + if (need_free) { + mono_mb_emit_ldloc (mb, conv_arg); + if (conv == MONO_MARSHAL_CONV_BSTR_STR) + mono_mb_emit_icall (mb, mono_free_bstr); + else + mono_mb_emit_icall (mb, mono_marshal_free); + } + break; + + case MARSHAL_ACTION_PUSH: + if (t->byref && encoding != MONO_NATIVE_VBBYREFSTR) + mono_mb_emit_ldloc_addr (mb, conv_arg); + else + mono_mb_emit_ldloc (mb, conv_arg); + break; + + case MARSHAL_ACTION_CONV_RESULT: + mono_mb_emit_stloc (mb, 0); + + conv = mono_marshal_get_ptr_to_string_conv (m->piinfo, spec, &need_free); + if (conv == MONO_MARSHAL_CONV_INVALID) { + char *msg = g_strdup_printf ("string marshalling conversion %d not implemented", encoding); + mono_mb_emit_exception_marshal_directive (mb, msg); + break; + } + + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_icall (mb, conv_to_icall (conv, NULL)); + mono_mb_emit_stloc (mb, 3); + + /* free the string */ + mono_mb_emit_ldloc (mb, 0); + if (conv == MONO_MARSHAL_CONV_BSTR_STR) + mono_mb_emit_icall (mb, mono_free_bstr); + else + mono_mb_emit_icall (mb, mono_marshal_free); + break; + + case MARSHAL_ACTION_MANAGED_CONV_IN: + conv_arg = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + + *conv_arg_type = &mono_defaults.int_class->byval_arg; + + if (t->byref) { + if (t->attrs & PARAM_ATTRIBUTE_OUT) + break; + } + + conv = mono_marshal_get_ptr_to_string_conv (m->piinfo, spec, &need_free); + if (conv == MONO_MARSHAL_CONV_INVALID) { + char *msg = g_strdup_printf ("string marshalling conversion %d not implemented", encoding); + mono_mb_emit_exception_marshal_directive (mb, msg); + break; + } + + mono_mb_emit_ldarg (mb, argnum); + if (t->byref) + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_icall (mb, conv_to_icall (conv, NULL)); + mono_mb_emit_stloc (mb, conv_arg); + break; + + case MARSHAL_ACTION_MANAGED_CONV_OUT: + if (t->byref) { + if (conv_arg) { + int stind_op; + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_icall (mb, conv_to_icall (conv, &stind_op)); + mono_mb_emit_byte (mb, stind_op); + } + } + break; + + case MARSHAL_ACTION_MANAGED_CONV_RESULT: + if (conv_to_icall (conv, NULL) == mono_marshal_string_to_utf16) + /* We need to make a copy so the caller is able to free it */ + mono_mb_emit_icall (mb, mono_marshal_string_to_utf16_copy); + else + mono_mb_emit_icall (mb, conv_to_icall (conv, NULL)); + mono_mb_emit_stloc (mb, 3); + break; + + default: + g_assert_not_reached (); + } +#endif + return conv_arg; +} + + +static int +emit_marshal_safehandle (EmitMarshalContext *m, int argnum, MonoType *t, + MonoMarshalSpec *spec, int conv_arg, + MonoType **conv_arg_type, MarshalAction action) +{ +#ifndef ENABLE_ILGEN + if (action == MARSHAL_ACTION_CONV_IN) + *conv_arg_type = &mono_defaults.int_class->byval_arg; +#else + MonoMethodBuilder *mb = m->mb; + + switch (action){ + case MARSHAL_ACTION_CONV_IN: { + MonoType *intptr_type; + int dar_release_slot, pos; + + intptr_type = &mono_defaults.int_class->byval_arg; + conv_arg = mono_mb_add_local (mb, intptr_type); + *conv_arg_type = intptr_type; + + if (!sh_dangerous_add_ref) + init_safe_handle (); + + mono_mb_emit_ldarg (mb, argnum); + pos = mono_mb_emit_branch (mb, CEE_BRTRUE); + mono_mb_emit_exception (mb, "ArgumentNullException", NULL); + + mono_mb_patch_branch (mb, pos); + if (t->byref){ + /* + * My tests in show that ref SafeHandles are not really + * passed as ref objects. Instead a NULL is passed as the + * value of the ref + */ + mono_mb_emit_icon (mb, 0); + mono_mb_emit_stloc (mb, conv_arg); + break; + } + + /* Create local to hold the ref parameter to DangerousAddRef */ + dar_release_slot = mono_mb_add_local (mb, &mono_defaults.boolean_class->byval_arg); + + /* set release = false; */ + mono_mb_emit_icon (mb, 0); + mono_mb_emit_stloc (mb, dar_release_slot); + + /* safehandle.DangerousAddRef (ref release) */ + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_ldloc_addr (mb, dar_release_slot); + mono_mb_emit_managed_call (mb, sh_dangerous_add_ref, NULL); + + /* Pull the handle field from SafeHandle */ + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoSafeHandle, handle)); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_stloc (mb, conv_arg); + + break; + } + + case MARSHAL_ACTION_PUSH: + if (t->byref) + mono_mb_emit_ldloc_addr (mb, conv_arg); + else + mono_mb_emit_ldloc (mb, conv_arg); + break; + + case MARSHAL_ACTION_CONV_OUT: { + /* The slot for the boolean is the next temporary created after conv_arg, see the CONV_IN code */ + int dar_release_slot = conv_arg + 1; + int label_next; + + if (!sh_dangerous_release) + init_safe_handle (); + + if (t->byref){ + MonoMethod *ctor; + + /* + * My tests indicate that ref SafeHandles parameters are not actually + * passed by ref, but instead a new Handle is created regardless of + * whether a change happens in the unmanaged side. + * + * Also, the Handle is created before calling into unmanaged code, + * but we do not support that mechanism (getting to the original + * handle) and it makes no difference where we create this + */ + ctor = mono_class_get_method_from_name (t->data.klass, ".ctor", 0); + if (ctor == NULL){ + mono_mb_emit_exception (mb, "MissingMethodException", "paramterless constructor required"); + break; + } + /* refval = new SafeHandleDerived ()*/ + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_op (mb, CEE_NEWOBJ, ctor); + mono_mb_emit_byte (mb, CEE_STIND_REF); + + /* refval.handle = returned_handle */ + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoSafeHandle, handle)); + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_byte (mb, CEE_STIND_I); + } else { + mono_mb_emit_ldloc (mb, dar_release_slot); + label_next = mono_mb_emit_branch (mb, CEE_BRFALSE); + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_managed_call (mb, sh_dangerous_release, NULL); + mono_mb_patch_branch (mb, label_next); + } + break; + } + + case MARSHAL_ACTION_CONV_RESULT: { + MonoMethod *ctor = NULL; + int intptr_handle_slot; + + if (mono_class_is_abstract (t->data.klass)) { + mono_mb_emit_byte (mb, CEE_POP); + mono_mb_emit_exception_marshal_directive (mb, g_strdup ("Returned SafeHandles should not be abstract")); + break; + } + + ctor = mono_class_get_method_from_name (t->data.klass, ".ctor", 0); + if (ctor == NULL){ + mono_mb_emit_byte (mb, CEE_POP); + mono_mb_emit_exception (mb, "MissingMethodException", "paramterless constructor required"); + break; + } + /* Store the IntPtr results into a local */ + intptr_handle_slot = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + mono_mb_emit_stloc (mb, intptr_handle_slot); + + /* Create return value */ + mono_mb_emit_op (mb, CEE_NEWOBJ, ctor); + mono_mb_emit_stloc (mb, 3); + + /* Set the return.handle to the value, am using ldflda, not sure if thats a good idea */ + mono_mb_emit_ldloc (mb, 3); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoSafeHandle, handle)); + mono_mb_emit_ldloc (mb, intptr_handle_slot); + mono_mb_emit_byte (mb, CEE_STIND_I); + break; + } + + case MARSHAL_ACTION_MANAGED_CONV_IN: + fprintf (stderr, "mono/marshal: SafeHandles missing MANAGED_CONV_IN\n"); + break; + + case MARSHAL_ACTION_MANAGED_CONV_OUT: + fprintf (stderr, "mono/marshal: SafeHandles missing MANAGED_CONV_OUT\n"); + break; + + case MARSHAL_ACTION_MANAGED_CONV_RESULT: + fprintf (stderr, "mono/marshal: SafeHandles missing MANAGED_CONV_RESULT\n"); + break; + default: + printf ("Unhandled case for MarshalAction: %d\n", action); + } +#endif + return conv_arg; +} + + +static int +emit_marshal_handleref (EmitMarshalContext *m, int argnum, MonoType *t, + MonoMarshalSpec *spec, int conv_arg, + MonoType **conv_arg_type, MarshalAction action) +{ +#ifndef ENABLE_ILGEN + if (action == MARSHAL_ACTION_CONV_IN) + *conv_arg_type = &mono_defaults.int_class->byval_arg; +#else + MonoMethodBuilder *mb = m->mb; + + switch (action){ + case MARSHAL_ACTION_CONV_IN: { + MonoType *intptr_type; + + intptr_type = &mono_defaults.int_class->byval_arg; + conv_arg = mono_mb_add_local (mb, intptr_type); + *conv_arg_type = intptr_type; + + if (t->byref){ + char *msg = g_strdup ("HandleRefs can not be returned from unmanaged code (or passed by ref)"); + mono_mb_emit_exception_marshal_directive (mb, msg); + break; + } + mono_mb_emit_ldarg_addr (mb, argnum); + mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoHandleRef, handle)); + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_stloc (mb, conv_arg); + break; + } + + case MARSHAL_ACTION_PUSH: + mono_mb_emit_ldloc (mb, conv_arg); + break; + + case MARSHAL_ACTION_CONV_OUT: { + /* no resource release required */ + break; + } + + case MARSHAL_ACTION_CONV_RESULT: { + char *msg = g_strdup ("HandleRefs can not be returned from unmanaged code (or passed by ref)"); + mono_mb_emit_exception_marshal_directive (mb, msg); + break; + } + + case MARSHAL_ACTION_MANAGED_CONV_IN: + fprintf (stderr, "mono/marshal: SafeHandles missing MANAGED_CONV_IN\n"); + break; + + case MARSHAL_ACTION_MANAGED_CONV_OUT: + fprintf (stderr, "mono/marshal: SafeHandles missing MANAGED_CONV_OUT\n"); + break; + + case MARSHAL_ACTION_MANAGED_CONV_RESULT: + fprintf (stderr, "mono/marshal: SafeHandles missing MANAGED_CONV_RESULT\n"); + break; + default: + fprintf (stderr, "Unhandled case for MarshalAction: %d\n", action); + } +#endif + return conv_arg; +} + + +static int +emit_marshal_object (EmitMarshalContext *m, int argnum, MonoType *t, + MonoMarshalSpec *spec, + int conv_arg, MonoType **conv_arg_type, + MarshalAction action) +{ +#ifndef ENABLE_ILGEN + if (action == MARSHAL_ACTION_CONV_IN) + *conv_arg_type = &mono_defaults.int_class->byval_arg; +#else + MonoMethodBuilder *mb = m->mb; + MonoClass *klass = mono_class_from_mono_type (t); + int pos, pos2, loc; + + switch (action) { + case MARSHAL_ACTION_CONV_IN: + *conv_arg_type = &mono_defaults.int_class->byval_arg; + conv_arg = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + + m->orig_conv_args [argnum] = 0; + + if (mono_class_from_mono_type (t) == mono_defaults.object_class) { + char *msg = g_strdup_printf ("Marshalling of type object is not implemented"); + mono_mb_emit_exception_marshal_directive (mb, msg); + break; + } + + if (klass->delegate) { + if (t->byref) { + if (!(t->attrs & PARAM_ATTRIBUTE_OUT)) { + char *msg = g_strdup_printf ("Byref marshalling of delegates is not implemented."); + mono_mb_emit_exception_marshal_directive (mb, msg); + } + mono_mb_emit_byte (mb, CEE_LDNULL); + mono_mb_emit_stloc (mb, conv_arg); + } else { + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_icall (mb, conv_to_icall (MONO_MARSHAL_CONV_DEL_FTN, NULL)); + mono_mb_emit_stloc (mb, conv_arg); + } + } else if (klass == mono_defaults.stringbuilder_class) { + MonoMarshalNative encoding = mono_marshal_get_string_encoding (m->piinfo, spec); + MonoMarshalConv conv = mono_marshal_get_stringbuilder_to_ptr_conv (m->piinfo, spec); + +#if 0 + if (t->byref) { + if (!(t->attrs & PARAM_ATTRIBUTE_OUT)) { + char *msg = g_strdup_printf ("Byref marshalling of stringbuilders is not implemented."); + mono_mb_emit_exception_marshal_directive (mb, msg); + } + break; + } +#endif + + if (t->byref && !(t->attrs & PARAM_ATTRIBUTE_IN) && (t->attrs & PARAM_ATTRIBUTE_OUT)) + break; + + if (conv == MONO_MARSHAL_CONV_INVALID) { + char *msg = g_strdup_printf ("stringbuilder marshalling conversion %d not implemented", encoding); + mono_mb_emit_exception_marshal_directive (mb, msg); + break; + } + + mono_mb_emit_ldarg (mb, argnum); + if (t->byref) + mono_mb_emit_byte (mb, CEE_LDIND_I); + + mono_mb_emit_icall (mb, conv_to_icall (conv, NULL)); + mono_mb_emit_stloc (mb, conv_arg); + } else if (klass->blittable) { + mono_mb_emit_byte (mb, CEE_LDNULL); + mono_mb_emit_stloc (mb, conv_arg); + + mono_mb_emit_ldarg (mb, argnum); + pos = mono_mb_emit_branch (mb, CEE_BRFALSE); + + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_ldflda (mb, sizeof (MonoObject)); + mono_mb_emit_stloc (mb, conv_arg); + + mono_mb_patch_branch (mb, pos); + break; + } else { + mono_mb_emit_byte (mb, CEE_LDNULL); + mono_mb_emit_stloc (mb, conv_arg); + + if (t->byref) { + /* we dont need any conversions for out parameters */ + if (t->attrs & PARAM_ATTRIBUTE_OUT) + break; + + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_byte (mb, CEE_LDIND_I); + + } else { + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_OBJADDR); + } + + /* store the address of the source into local variable 0 */ + mono_mb_emit_stloc (mb, 0); + mono_mb_emit_ldloc (mb, 0); + pos = mono_mb_emit_branch (mb, CEE_BRFALSE); + + /* allocate space for the native struct and store the address */ + mono_mb_emit_icon (mb, mono_class_native_size (klass, NULL)); + mono_mb_emit_byte (mb, CEE_PREFIX1); + mono_mb_emit_byte (mb, CEE_LOCALLOC); + mono_mb_emit_stloc (mb, conv_arg); + + if (t->byref) { + /* Need to store the original buffer so we can free it later */ + m->orig_conv_args [argnum] = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_stloc (mb, m->orig_conv_args [argnum]); + } + + /* set the src_ptr */ + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_ldflda (mb, sizeof (MonoObject)); + mono_mb_emit_stloc (mb, 0); + + /* set dst_ptr */ + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_stloc (mb, 1); + + /* emit valuetype conversion code */ + emit_struct_conv (mb, klass, FALSE); + + mono_mb_patch_branch (mb, pos); + } + break; + + case MARSHAL_ACTION_CONV_OUT: + if (klass == mono_defaults.stringbuilder_class) { + gboolean need_free; + MonoMarshalNative encoding; + MonoMarshalConv conv; + + encoding = mono_marshal_get_string_encoding (m->piinfo, spec); + conv = mono_marshal_get_ptr_to_stringbuilder_conv (m->piinfo, spec, &need_free); + + g_assert (encoding != -1); + + if (t->byref) { + //g_assert (!(t->attrs & PARAM_ATTRIBUTE_OUT)); + + need_free = TRUE; + + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_ldloc (mb, conv_arg); + + switch (encoding) { + case MONO_NATIVE_LPWSTR: + mono_mb_emit_icall (mb, mono_string_utf16_to_builder2); + break; + case MONO_NATIVE_LPSTR: + mono_mb_emit_icall (mb, mono_string_utf8_to_builder2); + break; + case MONO_NATIVE_UTF8STR: + mono_mb_emit_icall (mb, mono_string_utf8_to_builder2); + break; + default: + g_assert_not_reached (); + } + + mono_mb_emit_byte (mb, CEE_STIND_REF); + } else { + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_ldloc (mb, conv_arg); + + mono_mb_emit_icall (mb, conv_to_icall (conv, NULL)); + } + + if (need_free) { + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_icall (mb, mono_marshal_free); + } + break; + } + + if (klass->delegate) { + if (t->byref) { + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_op (mb, CEE_MONO_CLASSCONST, klass); + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_icall (mb, conv_to_icall (MONO_MARSHAL_CONV_FTN_DEL, NULL)); + mono_mb_emit_byte (mb, CEE_STIND_REF); + } + break; + } + + if (t->byref && (t->attrs & PARAM_ATTRIBUTE_OUT)) { + /* allocate a new object */ + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_op (mb, CEE_MONO_NEWOBJ, klass); + mono_mb_emit_byte (mb, CEE_STIND_REF); + } + + /* dst = *argument */ + mono_mb_emit_ldarg (mb, argnum); + + if (t->byref) + mono_mb_emit_byte (mb, CEE_LDIND_I); + + mono_mb_emit_stloc (mb, 1); + + mono_mb_emit_ldloc (mb, 1); + pos = mono_mb_emit_branch (mb, CEE_BRFALSE); + + if (t->byref || (t->attrs & PARAM_ATTRIBUTE_OUT)) { + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_icon (mb, sizeof (MonoObject)); + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_stloc (mb, 1); + + /* src = tmp_locals [i] */ + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_stloc (mb, 0); + + /* emit valuetype conversion code */ + emit_struct_conv (mb, klass, TRUE); + + /* Free the structure returned by the native code */ + emit_struct_free (mb, klass, conv_arg); + + if (m->orig_conv_args [argnum]) { + /* + * If the native function changed the pointer, then free + * the original structure plus the new pointer. + */ + mono_mb_emit_ldloc (mb, m->orig_conv_args [argnum]); + mono_mb_emit_ldloc (mb, conv_arg); + pos2 = mono_mb_emit_branch (mb, CEE_BEQ); + + if (!(t->attrs & PARAM_ATTRIBUTE_OUT)) { + g_assert (m->orig_conv_args [argnum]); + + emit_struct_free (mb, klass, m->orig_conv_args [argnum]); + } + + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_icall (mb, mono_marshal_free); + + mono_mb_patch_branch (mb, pos2); + } + } + else + /* Free the original structure passed to native code */ + emit_struct_free (mb, klass, conv_arg); + + mono_mb_patch_branch (mb, pos); + break; + + case MARSHAL_ACTION_PUSH: + if (t->byref) + mono_mb_emit_ldloc_addr (mb, conv_arg); + else + mono_mb_emit_ldloc (mb, conv_arg); + break; + + case MARSHAL_ACTION_CONV_RESULT: + if (klass->delegate) { + g_assert (!t->byref); + mono_mb_emit_stloc (mb, 0); + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_op (mb, CEE_MONO_CLASSCONST, klass); + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_icall (mb, conv_to_icall (MONO_MARSHAL_CONV_FTN_DEL, NULL)); + mono_mb_emit_stloc (mb, 3); + } else if (klass == mono_defaults.stringbuilder_class) { + // FIXME: + char *msg = g_strdup_printf ("Return marshalling of stringbuilders is not implemented."); + mono_mb_emit_exception_marshal_directive (mb, msg); + } else { + /* set src */ + mono_mb_emit_stloc (mb, 0); + + /* Make a copy since emit_conv modifies local 0 */ + loc = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_stloc (mb, loc); + + mono_mb_emit_byte (mb, CEE_LDNULL); + mono_mb_emit_stloc (mb, 3); + + mono_mb_emit_ldloc (mb, 0); + pos = mono_mb_emit_branch (mb, CEE_BRFALSE); + + /* allocate result object */ + + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_op (mb, CEE_MONO_NEWOBJ, klass); + mono_mb_emit_stloc (mb, 3); + + /* set dst */ + + mono_mb_emit_ldloc (mb, 3); + mono_mb_emit_ldflda (mb, sizeof (MonoObject)); + mono_mb_emit_stloc (mb, 1); + + /* emit conversion code */ + emit_struct_conv (mb, klass, TRUE); + + emit_struct_free (mb, klass, loc); + + /* Free the pointer allocated by unmanaged code */ + mono_mb_emit_ldloc (mb, loc); + mono_mb_emit_icall (mb, mono_marshal_free); + mono_mb_patch_branch (mb, pos); + } + break; + + case MARSHAL_ACTION_MANAGED_CONV_IN: + conv_arg = mono_mb_add_local (mb, &klass->byval_arg); + + if (klass->delegate) { + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_op (mb, CEE_MONO_CLASSCONST, klass); + mono_mb_emit_ldarg (mb, argnum); + if (t->byref) + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_icall (mb, conv_to_icall (MONO_MARSHAL_CONV_FTN_DEL, NULL)); + mono_mb_emit_stloc (mb, conv_arg); + break; + } + + if (klass == mono_defaults.stringbuilder_class) { + MonoMarshalNative encoding; + + encoding = mono_marshal_get_string_encoding (m->piinfo, spec); + + // FIXME: + g_assert (encoding == MONO_NATIVE_LPSTR || encoding == MONO_NATIVE_UTF8STR); + + g_assert (!t->byref); + g_assert (encoding != -1); + + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_icall (mb, mono_string_utf8_to_builder2); + mono_mb_emit_stloc (mb, conv_arg); + break; + } + + /* The class can not have an automatic layout */ + if (mono_class_is_auto_layout (klass)) { + mono_mb_emit_auto_layout_exception (mb, klass); + break; + } + + if (t->attrs & PARAM_ATTRIBUTE_OUT) { + mono_mb_emit_byte (mb, CEE_LDNULL); + mono_mb_emit_stloc (mb, conv_arg); + break; + } + + /* Set src */ + mono_mb_emit_ldarg (mb, argnum); + if (t->byref) { + int pos2; + + /* Check for NULL and raise an exception */ + pos2 = mono_mb_emit_branch (mb, CEE_BRTRUE); + + mono_mb_emit_exception (mb, "ArgumentNullException", NULL); + + mono_mb_patch_branch (mb, pos2); + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_byte (mb, CEE_LDIND_I); + } + + mono_mb_emit_stloc (mb, 0); + + mono_mb_emit_byte (mb, CEE_LDC_I4_0); + mono_mb_emit_stloc (mb, conv_arg); + + mono_mb_emit_ldloc (mb, 0); + pos = mono_mb_emit_branch (mb, CEE_BRFALSE); + + /* Create and set dst */ + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_op (mb, CEE_MONO_NEWOBJ, klass); + mono_mb_emit_stloc (mb, conv_arg); + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_ldflda (mb, sizeof (MonoObject)); + mono_mb_emit_stloc (mb, 1); + + /* emit valuetype conversion code */ + emit_struct_conv (mb, klass, TRUE); + + mono_mb_patch_branch (mb, pos); + break; + + case MARSHAL_ACTION_MANAGED_CONV_OUT: + if (klass->delegate) { + if (t->byref) { + int stind_op; + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_icall (mb, conv_to_icall (MONO_MARSHAL_CONV_DEL_FTN, &stind_op)); + mono_mb_emit_byte (mb, stind_op); + break; + } + } + + if (t->byref) { + /* Check for null */ + mono_mb_emit_ldloc (mb, conv_arg); + pos = mono_mb_emit_branch (mb, CEE_BRTRUE); + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_byte (mb, CEE_LDC_I4_0); + mono_mb_emit_byte (mb, CEE_STIND_REF); + pos2 = mono_mb_emit_branch (mb, CEE_BR); + + mono_mb_patch_branch (mb, pos); + + /* Set src */ + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_ldflda (mb, sizeof (MonoObject)); + mono_mb_emit_stloc (mb, 0); + + /* Allocate and set dest */ + mono_mb_emit_icon (mb, mono_class_native_size (klass, NULL)); + mono_mb_emit_byte (mb, CEE_CONV_I); + mono_mb_emit_icall (mb, ves_icall_marshal_alloc); + mono_mb_emit_stloc (mb, 1); + + /* Update argument pointer */ + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_byte (mb, CEE_STIND_I); + + /* emit valuetype conversion code */ + emit_struct_conv (mb, klass, FALSE); + + mono_mb_patch_branch (mb, pos2); + } else if (klass == mono_defaults.stringbuilder_class) { + // FIXME: What to do here ? + } else { + /* byval [Out] marshalling */ + + /* FIXME: Handle null */ + + /* Set src */ + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_ldflda (mb, sizeof (MonoObject)); + mono_mb_emit_stloc (mb, 0); + + /* Set dest */ + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_stloc (mb, 1); + + /* emit valuetype conversion code */ + emit_struct_conv (mb, klass, FALSE); + } + break; + + case MARSHAL_ACTION_MANAGED_CONV_RESULT: + if (klass->delegate) { + mono_mb_emit_icall (mb, conv_to_icall (MONO_MARSHAL_CONV_DEL_FTN, NULL)); + mono_mb_emit_stloc (mb, 3); + break; + } + + /* The class can not have an automatic layout */ + if (mono_class_is_auto_layout (klass)) { + mono_mb_emit_auto_layout_exception (mb, klass); + break; + } + + mono_mb_emit_stloc (mb, 0); + /* Check for null */ + mono_mb_emit_ldloc (mb, 0); + pos = mono_mb_emit_branch (mb, CEE_BRTRUE); + mono_mb_emit_byte (mb, CEE_LDNULL); + mono_mb_emit_stloc (mb, 3); + pos2 = mono_mb_emit_branch (mb, CEE_BR); + + mono_mb_patch_branch (mb, pos); + + /* Set src */ + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_ldflda (mb, sizeof (MonoObject)); + mono_mb_emit_stloc (mb, 0); + + /* Allocate and set dest */ + mono_mb_emit_icon (mb, mono_class_native_size (klass, NULL)); + mono_mb_emit_byte (mb, CEE_CONV_I); + mono_mb_emit_icall (mb, ves_icall_marshal_alloc); + mono_mb_emit_byte (mb, CEE_DUP); + mono_mb_emit_stloc (mb, 1); + mono_mb_emit_stloc (mb, 3); + + emit_struct_conv (mb, klass, FALSE); + + mono_mb_patch_branch (mb, pos2); + break; + + default: + g_assert_not_reached (); + } +#endif + return conv_arg; +} + +#ifdef ENABLE_ILGEN +#ifndef DISABLE_COM + +static int +emit_marshal_variant (EmitMarshalContext *m, int argnum, MonoType *t, + MonoMarshalSpec *spec, + int conv_arg, MonoType **conv_arg_type, + MarshalAction action) +{ + MonoMethodBuilder *mb = m->mb; + static MonoMethod *get_object_for_native_variant = NULL; + static MonoMethod *get_native_variant_for_object = NULL; + + if (!get_object_for_native_variant) + get_object_for_native_variant = mono_class_get_method_from_name (mono_defaults.marshal_class, "GetObjectForNativeVariant", 1); + g_assert (get_object_for_native_variant); + + if (!get_native_variant_for_object) + get_native_variant_for_object = mono_class_get_method_from_name (mono_defaults.marshal_class, "GetNativeVariantForObject", 2); + g_assert (get_native_variant_for_object); + + switch (action) { + case MARSHAL_ACTION_CONV_IN: { + conv_arg = mono_mb_add_local (mb, &mono_class_get_variant_class ()->byval_arg); + + if (t->byref) + *conv_arg_type = &mono_class_get_variant_class ()->this_arg; + else + *conv_arg_type = &mono_class_get_variant_class ()->byval_arg; + + if (t->byref && !(t->attrs & PARAM_ATTRIBUTE_IN) && t->attrs & PARAM_ATTRIBUTE_OUT) + break; + + mono_mb_emit_ldarg (mb, argnum); + if (t->byref) + mono_mb_emit_byte(mb, CEE_LDIND_REF); + mono_mb_emit_ldloc_addr (mb, conv_arg); + mono_mb_emit_managed_call (mb, get_native_variant_for_object, NULL); + break; + } + + case MARSHAL_ACTION_CONV_OUT: { + static MonoMethod *variant_clear = NULL; + + if (!variant_clear) + variant_clear = mono_class_get_method_from_name (mono_class_get_variant_class (), "Clear", 0); + g_assert (variant_clear); + + + if (t->byref && (t->attrs & PARAM_ATTRIBUTE_OUT || !(t->attrs & PARAM_ATTRIBUTE_IN))) { + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_ldloc_addr (mb, conv_arg); + mono_mb_emit_managed_call (mb, get_object_for_native_variant, NULL); + mono_mb_emit_byte (mb, CEE_STIND_REF); + } + + mono_mb_emit_ldloc_addr (mb, conv_arg); + mono_mb_emit_managed_call (mb, variant_clear, NULL); + break; + } + + case MARSHAL_ACTION_PUSH: + if (t->byref) + mono_mb_emit_ldloc_addr (mb, conv_arg); + else + mono_mb_emit_ldloc (mb, conv_arg); + break; + + case MARSHAL_ACTION_CONV_RESULT: { + char *msg = g_strdup ("Marshalling of VARIANT not supported as a return type."); + mono_mb_emit_exception_marshal_directive (mb, msg); + break; + } + + case MARSHAL_ACTION_MANAGED_CONV_IN: { + conv_arg = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + + if (t->byref) + *conv_arg_type = &mono_class_get_variant_class ()->this_arg; + else + *conv_arg_type = &mono_class_get_variant_class ()->byval_arg; + + if (t->byref && !(t->attrs & PARAM_ATTRIBUTE_IN) && t->attrs & PARAM_ATTRIBUTE_OUT) + break; + + if (t->byref) + mono_mb_emit_ldarg (mb, argnum); + else + mono_mb_emit_ldarg_addr (mb, argnum); + mono_mb_emit_managed_call (mb, get_object_for_native_variant, NULL); + mono_mb_emit_stloc (mb, conv_arg); + break; + } + + case MARSHAL_ACTION_MANAGED_CONV_OUT: { + if (t->byref && (t->attrs & PARAM_ATTRIBUTE_OUT || !(t->attrs & PARAM_ATTRIBUTE_IN))) { + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_managed_call (mb, get_native_variant_for_object, NULL); + } + break; + } + + case MARSHAL_ACTION_MANAGED_CONV_RESULT: { + char *msg = g_strdup ("Marshalling of VARIANT not supported as a return type."); + mono_mb_emit_exception_marshal_directive (mb, msg); + break; + } + + default: + g_assert_not_reached (); + } + + return conv_arg; +} + +#endif /* DISABLE_COM */ +#endif /* ENABLE_ILGEN */ + +static gboolean +mono_pinvoke_is_unicode (MonoMethodPInvoke *piinfo) +{ + switch (piinfo->piflags & PINVOKE_ATTRIBUTE_CHAR_SET_MASK) { + case PINVOKE_ATTRIBUTE_CHAR_SET_ANSI: + return FALSE; + case PINVOKE_ATTRIBUTE_CHAR_SET_UNICODE: + return TRUE; + case PINVOKE_ATTRIBUTE_CHAR_SET_AUTO: + default: +#ifdef TARGET_WIN32 + return TRUE; +#else + return FALSE; +#endif + } +} + + +static int +emit_marshal_array (EmitMarshalContext *m, int argnum, MonoType *t, + MonoMarshalSpec *spec, + int conv_arg, MonoType **conv_arg_type, + MarshalAction action) +{ +#ifndef ENABLE_ILGEN + switch (action) { + case MARSHAL_ACTION_CONV_IN: + *conv_arg_type = &mono_defaults.object_class->byval_arg; + break; + case MARSHAL_ACTION_MANAGED_CONV_IN: + *conv_arg_type = &mono_defaults.int_class->byval_arg; + break; + } +#else + MonoMethodBuilder *mb = m->mb; + MonoClass *klass = mono_class_from_mono_type (t); + gboolean need_convert, need_free; + MonoMarshalNative encoding; + + encoding = mono_marshal_get_string_encoding (m->piinfo, spec); + + switch (action) { + case MARSHAL_ACTION_CONV_IN: + *conv_arg_type = &mono_defaults.object_class->byval_arg; + conv_arg = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + + if (klass->element_class->blittable) { + mono_mb_emit_ldarg (mb, argnum); + if (t->byref) + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_icall (mb, conv_to_icall (MONO_MARSHAL_CONV_ARRAY_LPARRAY, NULL)); + mono_mb_emit_stloc (mb, conv_arg); + } else { + MonoClass *eklass; + guint32 label1, label2, label3; + int index_var, src_var, dest_ptr, esize; + MonoMarshalConv conv; + gboolean is_string = FALSE; + + dest_ptr = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + + eklass = klass->element_class; + + if (eklass == mono_defaults.string_class) { + is_string = TRUE; + conv = mono_marshal_get_string_to_ptr_conv (m->piinfo, spec); + } + else if (eklass == mono_defaults.stringbuilder_class) { + is_string = TRUE; + conv = mono_marshal_get_stringbuilder_to_ptr_conv (m->piinfo, spec); + } + else + conv = MONO_MARSHAL_CONV_INVALID; + + if (is_string && conv == MONO_MARSHAL_CONV_INVALID) { + char *msg = g_strdup_printf ("string/stringbuilder marshalling conversion %d not implemented", encoding); + mono_mb_emit_exception_marshal_directive (mb, msg); + break; + } + + src_var = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + mono_mb_emit_ldarg (mb, argnum); + if (t->byref) + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_stloc (mb, src_var); + + /* Check null */ + mono_mb_emit_ldloc (mb, src_var); + mono_mb_emit_stloc (mb, conv_arg); + mono_mb_emit_ldloc (mb, src_var); + label1 = mono_mb_emit_branch (mb, CEE_BRFALSE); + + if (is_string) + esize = sizeof (gpointer); + else if (eklass == mono_defaults.char_class) /*can't call mono_marshal_type_size since it causes all sorts of asserts*/ + esize = mono_pinvoke_is_unicode (m->piinfo) ? 2 : 1; + else + esize = mono_class_native_size (eklass, NULL); + + /* allocate space for the native struct and store the address */ + mono_mb_emit_icon (mb, esize); + mono_mb_emit_ldloc (mb, src_var); + mono_mb_emit_byte (mb, CEE_LDLEN); + + if (eklass == mono_defaults.string_class) { + /* Make the array bigger for the terminating null */ + mono_mb_emit_byte (mb, CEE_LDC_I4_1); + mono_mb_emit_byte (mb, CEE_ADD); + } + mono_mb_emit_byte (mb, CEE_MUL); + mono_mb_emit_byte (mb, CEE_PREFIX1); + mono_mb_emit_byte (mb, CEE_LOCALLOC); + mono_mb_emit_stloc (mb, conv_arg); + + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_stloc (mb, dest_ptr); + + /* Emit marshalling loop */ + index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + mono_mb_emit_byte (mb, CEE_LDC_I4_0); + mono_mb_emit_stloc (mb, index_var); + label2 = mono_mb_get_label (mb); + mono_mb_emit_ldloc (mb, index_var); + mono_mb_emit_ldloc (mb, src_var); + mono_mb_emit_byte (mb, CEE_LDLEN); + label3 = mono_mb_emit_branch (mb, CEE_BGE); + + /* Emit marshalling code */ + + if (is_string) { + int stind_op; + mono_mb_emit_ldloc (mb, dest_ptr); + mono_mb_emit_ldloc (mb, src_var); + mono_mb_emit_ldloc (mb, index_var); + mono_mb_emit_byte (mb, CEE_LDELEM_REF); + mono_mb_emit_icall (mb, conv_to_icall (conv, &stind_op)); + mono_mb_emit_byte (mb, stind_op); + } else { + /* set the src_ptr */ + mono_mb_emit_ldloc (mb, src_var); + mono_mb_emit_ldloc (mb, index_var); + mono_mb_emit_op (mb, CEE_LDELEMA, eklass); + mono_mb_emit_stloc (mb, 0); + + /* set dst_ptr */ + mono_mb_emit_ldloc (mb, dest_ptr); + mono_mb_emit_stloc (mb, 1); + + /* emit valuetype conversion code */ + emit_struct_conv_full (mb, eklass, FALSE, 0, eklass == mono_defaults.char_class ? encoding : (MonoMarshalNative)-1); + } + + mono_mb_emit_add_to_local (mb, index_var, 1); + mono_mb_emit_add_to_local (mb, dest_ptr, esize); + + mono_mb_emit_branch_label (mb, CEE_BR, label2); + + mono_mb_patch_branch (mb, label3); + + if (eklass == mono_defaults.string_class) { + /* Null terminate */ + mono_mb_emit_ldloc (mb, dest_ptr); + mono_mb_emit_byte (mb, CEE_LDC_I4_0); + mono_mb_emit_byte (mb, CEE_STIND_REF); + } + + mono_mb_patch_branch (mb, label1); + } + + break; + + case MARSHAL_ACTION_CONV_OUT: + /* Unicode character arrays are implicitly marshalled as [Out] under MS.NET */ + need_convert = ((klass->element_class == mono_defaults.char_class) && (encoding == MONO_NATIVE_LPWSTR)) || (klass->element_class == mono_defaults.stringbuilder_class) || (t->attrs & PARAM_ATTRIBUTE_OUT); + need_free = mono_marshal_need_free (&klass->element_class->byval_arg, + m->piinfo, spec); + + if ((t->attrs & PARAM_ATTRIBUTE_OUT) && spec && spec->native == MONO_NATIVE_LPARRAY && spec->data.array_data.param_num != -1) { + int param_num = spec->data.array_data.param_num; + MonoType *param_type; + + param_type = m->sig->params [param_num]; + + if (param_type->byref && param_type->type != MONO_TYPE_I4) { + char *msg = g_strdup ("Not implemented."); + mono_mb_emit_exception_marshal_directive (mb, msg); + break; + } + + if (t->byref ) { + mono_mb_emit_ldarg (mb, argnum); + + /* Create the managed array */ + mono_mb_emit_ldarg (mb, param_num); + if (m->sig->params [param_num]->byref) + // FIXME: Support other types + mono_mb_emit_byte (mb, CEE_LDIND_I4); + mono_mb_emit_byte (mb, CEE_CONV_OVF_I); + mono_mb_emit_op (mb, CEE_NEWARR, klass->element_class); + /* Store into argument */ + mono_mb_emit_byte (mb, CEE_STIND_REF); + } + } + + if (need_convert || need_free) { + /* FIXME: Optimize blittable case */ + MonoClass *eklass; + guint32 label1, label2, label3; + int index_var, src_ptr, loc, esize; + + eklass = klass->element_class; + if ((eklass == mono_defaults.stringbuilder_class) || (eklass == mono_defaults.string_class)) + esize = sizeof (gpointer); + else if (eklass == mono_defaults.char_class) + esize = mono_pinvoke_is_unicode (m->piinfo) ? 2 : 1; + else + esize = mono_class_native_size (eklass, NULL); + src_ptr = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + loc = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + + /* Check null */ + mono_mb_emit_ldarg (mb, argnum); + if (t->byref) + mono_mb_emit_byte (mb, CEE_LDIND_I); + label1 = mono_mb_emit_branch (mb, CEE_BRFALSE); + + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_stloc (mb, src_ptr); + + /* Emit marshalling loop */ + index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + mono_mb_emit_byte (mb, CEE_LDC_I4_0); + mono_mb_emit_stloc (mb, index_var); + label2 = mono_mb_get_label (mb); + mono_mb_emit_ldloc (mb, index_var); + mono_mb_emit_ldarg (mb, argnum); + if (t->byref) + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_byte (mb, CEE_LDLEN); + label3 = mono_mb_emit_branch (mb, CEE_BGE); + + /* Emit marshalling code */ + + if (eklass == mono_defaults.stringbuilder_class) { + gboolean need_free2; + MonoMarshalConv conv = mono_marshal_get_ptr_to_stringbuilder_conv (m->piinfo, spec, &need_free2); + + g_assert (conv != MONO_MARSHAL_CONV_INVALID); + + /* dest */ + mono_mb_emit_ldarg (mb, argnum); + if (t->byref) + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_ldloc (mb, index_var); + mono_mb_emit_byte (mb, CEE_LDELEM_REF); + + /* src */ + mono_mb_emit_ldloc (mb, src_ptr); + mono_mb_emit_byte (mb, CEE_LDIND_I); + + mono_mb_emit_icall (mb, conv_to_icall (conv, NULL)); + + if (need_free) { + /* src */ + mono_mb_emit_ldloc (mb, src_ptr); + mono_mb_emit_byte (mb, CEE_LDIND_I); + + mono_mb_emit_icall (mb, mono_marshal_free); + } + } + else if (eklass == mono_defaults.string_class) { + if (need_free) { + /* src */ + mono_mb_emit_ldloc (mb, src_ptr); + mono_mb_emit_byte (mb, CEE_LDIND_I); + + mono_mb_emit_icall (mb, mono_marshal_free); + } + } + else { + if (need_convert) { + /* set the src_ptr */ + mono_mb_emit_ldloc (mb, src_ptr); + mono_mb_emit_stloc (mb, 0); + + /* set dst_ptr */ + mono_mb_emit_ldarg (mb, argnum); + if (t->byref) + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_ldloc (mb, index_var); + mono_mb_emit_op (mb, CEE_LDELEMA, eklass); + mono_mb_emit_stloc (mb, 1); + + /* emit valuetype conversion code */ + emit_struct_conv_full (mb, eklass, TRUE, 0, eklass == mono_defaults.char_class ? encoding : (MonoMarshalNative)-1); + } + + if (need_free) { + mono_mb_emit_ldloc (mb, src_ptr); + mono_mb_emit_stloc (mb, loc); + mono_mb_emit_ldloc (mb, loc); + + emit_struct_free (mb, eklass, loc); + } + } + + mono_mb_emit_add_to_local (mb, index_var, 1); + mono_mb_emit_add_to_local (mb, src_ptr, esize); + + mono_mb_emit_branch_label (mb, CEE_BR, label2); + + mono_mb_patch_branch (mb, label1); + mono_mb_patch_branch (mb, label3); + } + + if (klass->element_class->blittable) { + /* free memory allocated (if any) by MONO_MARSHAL_CONV_ARRAY_LPARRAY */ + + mono_mb_emit_ldarg (mb, argnum); + if (t->byref) + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_icall (mb, conv_to_icall (MONO_MARSHAL_FREE_LPARRAY, NULL)); + } + + break; + + case MARSHAL_ACTION_PUSH: + if (t->byref) + mono_mb_emit_ldloc_addr (mb, conv_arg); + else + mono_mb_emit_ldloc (mb, conv_arg); + break; + + case MARSHAL_ACTION_CONV_RESULT: + /* fixme: we need conversions here */ + mono_mb_emit_stloc (mb, 3); + break; + + case MARSHAL_ACTION_MANAGED_CONV_IN: { + MonoClass *eklass; + guint32 label1, label2, label3; + int index_var, src_ptr, esize, param_num, num_elem; + MonoMarshalConv conv; + gboolean is_string = FALSE; + + conv_arg = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + *conv_arg_type = &mono_defaults.int_class->byval_arg; + + if (t->byref) { + char *msg = g_strdup ("Byref array marshalling to managed code is not implemented."); + mono_mb_emit_exception_marshal_directive (mb, msg); + return conv_arg; + } + if (!spec) { + char *msg = g_strdup ("[MarshalAs] attribute required to marshal arrays to managed code."); + mono_mb_emit_exception_marshal_directive (mb, msg); + return conv_arg; + } + if (spec->native != MONO_NATIVE_LPARRAY) { + char *msg = g_strdup ("Non LPArray marshalling of arrays to managed code is not implemented."); + mono_mb_emit_exception_marshal_directive (mb, msg); + return conv_arg; + } + + /* FIXME: t is from the method which is wrapped, not the delegate type */ + /* g_assert (t->attrs & PARAM_ATTRIBUTE_IN); */ + + param_num = spec->data.array_data.param_num; + num_elem = spec->data.array_data.num_elem; + if (spec->data.array_data.elem_mult == 0) + /* param_num is not specified */ + param_num = -1; + + if (param_num == -1) { + if (num_elem <= 0) { + char *msg = g_strdup ("Either SizeConst or SizeParamIndex should be specified when marshalling arrays to managed code."); + mono_mb_emit_exception_marshal_directive (mb, msg); + return conv_arg; + } + } + + /* FIXME: Optimize blittable case */ + + eklass = klass->element_class; + if (eklass == mono_defaults.string_class) { + is_string = TRUE; + conv = mono_marshal_get_ptr_to_string_conv (m->piinfo, spec, &need_free); + } + else if (eklass == mono_defaults.stringbuilder_class) { + is_string = TRUE; + conv = mono_marshal_get_ptr_to_stringbuilder_conv (m->piinfo, spec, &need_free); + } + else + conv = MONO_MARSHAL_CONV_INVALID; + + mono_marshal_load_type_info (eklass); + + if (is_string) + esize = sizeof (gpointer); + else + esize = mono_class_native_size (eklass, NULL); + src_ptr = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + + mono_mb_emit_byte (mb, CEE_LDNULL); + mono_mb_emit_stloc (mb, conv_arg); + + /* Check param index */ + if (param_num != -1) { + if (param_num >= m->sig->param_count) { + char *msg = g_strdup ("Array size control parameter index is out of range."); + mono_mb_emit_exception_marshal_directive (mb, msg); + return conv_arg; + } + switch (m->sig->params [param_num]->type) { + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + break; + default: { + char *msg = g_strdup ("Array size control parameter must be an integral type."); + mono_mb_emit_exception_marshal_directive (mb, msg); + return conv_arg; + } + } + } + + /* Check null */ + mono_mb_emit_ldarg (mb, argnum); + label1 = mono_mb_emit_branch (mb, CEE_BRFALSE); + + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_stloc (mb, src_ptr); + + /* Create managed array */ + /* + * The LPArray marshalling spec says that sometimes param_num starts + * from 1, sometimes it starts from 0. But MS seems to allways start + * from 0. + */ + + if (param_num == -1) { + mono_mb_emit_icon (mb, num_elem); + } else { + mono_mb_emit_ldarg (mb, param_num); + if (num_elem > 0) { + mono_mb_emit_icon (mb, num_elem); + mono_mb_emit_byte (mb, CEE_ADD); + } + mono_mb_emit_byte (mb, CEE_CONV_OVF_I); + } + + mono_mb_emit_op (mb, CEE_NEWARR, eklass); + mono_mb_emit_stloc (mb, conv_arg); + + if (eklass->blittable) { + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_byte (mb, CEE_CONV_I); + mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoArray, vector)); + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_byte (mb, CEE_LDLEN); + mono_mb_emit_icon (mb, esize); + mono_mb_emit_byte (mb, CEE_MUL); + mono_mb_emit_byte (mb, CEE_PREFIX1); + mono_mb_emit_byte (mb, CEE_CPBLK); + mono_mb_patch_branch (mb, label1); + break; + } + + /* Emit marshalling loop */ + index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + mono_mb_emit_byte (mb, CEE_LDC_I4_0); + mono_mb_emit_stloc (mb, index_var); + label2 = mono_mb_get_label (mb); + mono_mb_emit_ldloc (mb, index_var); + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_byte (mb, CEE_LDLEN); + label3 = mono_mb_emit_branch (mb, CEE_BGE); + + /* Emit marshalling code */ + if (is_string) { + g_assert (conv != MONO_MARSHAL_CONV_INVALID); + + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_ldloc (mb, index_var); + + mono_mb_emit_ldloc (mb, src_ptr); + mono_mb_emit_byte (mb, CEE_LDIND_I); + + mono_mb_emit_icall (mb, conv_to_icall (conv, NULL)); + mono_mb_emit_byte (mb, CEE_STELEM_REF); + } + else { + char *msg = g_strdup ("Marshalling of non-string and non-blittable arrays to managed code is not implemented."); + mono_mb_emit_exception_marshal_directive (mb, msg); + return conv_arg; + } + + mono_mb_emit_add_to_local (mb, index_var, 1); + mono_mb_emit_add_to_local (mb, src_ptr, esize); + + mono_mb_emit_branch_label (mb, CEE_BR, label2); + + mono_mb_patch_branch (mb, label1); + mono_mb_patch_branch (mb, label3); + + break; + } + case MARSHAL_ACTION_MANAGED_CONV_OUT: { + MonoClass *eklass; + guint32 label1, label2, label3; + int index_var, dest_ptr, esize, param_num, num_elem; + MonoMarshalConv conv; + gboolean is_string = FALSE; + + if (!spec) + /* Already handled in CONV_IN */ + break; + + /* These are already checked in CONV_IN */ + g_assert (!t->byref); + g_assert (spec->native == MONO_NATIVE_LPARRAY); + g_assert (t->attrs & PARAM_ATTRIBUTE_OUT); + + param_num = spec->data.array_data.param_num; + num_elem = spec->data.array_data.num_elem; + + if (spec->data.array_data.elem_mult == 0) + /* param_num is not specified */ + param_num = -1; + + if (param_num == -1) { + if (num_elem <= 0) { + g_assert_not_reached (); + } + } + + /* FIXME: Optimize blittable case */ + + eklass = klass->element_class; + if (eklass == mono_defaults.string_class) { + is_string = TRUE; + conv = mono_marshal_get_string_to_ptr_conv (m->piinfo, spec); + } + else if (eklass == mono_defaults.stringbuilder_class) { + is_string = TRUE; + conv = mono_marshal_get_stringbuilder_to_ptr_conv (m->piinfo, spec); + } + else + conv = MONO_MARSHAL_CONV_INVALID; + + mono_marshal_load_type_info (eklass); + + if (is_string) + esize = sizeof (gpointer); + else + esize = mono_class_native_size (eklass, NULL); + + dest_ptr = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + + /* Check null */ + mono_mb_emit_ldloc (mb, conv_arg); + label1 = mono_mb_emit_branch (mb, CEE_BRFALSE); + + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_stloc (mb, dest_ptr); + + if (eklass->blittable) { + /* dest */ + mono_mb_emit_ldarg (mb, argnum); + /* src */ + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_byte (mb, CEE_CONV_I); + mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoArray, vector)); + mono_mb_emit_byte (mb, CEE_ADD); + /* length */ + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_byte (mb, CEE_LDLEN); + mono_mb_emit_icon (mb, esize); + mono_mb_emit_byte (mb, CEE_MUL); + mono_mb_emit_byte (mb, CEE_PREFIX1); + mono_mb_emit_byte (mb, CEE_CPBLK); + break; + } + + /* Emit marshalling loop */ + index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + mono_mb_emit_byte (mb, CEE_LDC_I4_0); + mono_mb_emit_stloc (mb, index_var); + label2 = mono_mb_get_label (mb); + mono_mb_emit_ldloc (mb, index_var); + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_byte (mb, CEE_LDLEN); + label3 = mono_mb_emit_branch (mb, CEE_BGE); + + /* Emit marshalling code */ + if (is_string) { + int stind_op; + g_assert (conv != MONO_MARSHAL_CONV_INVALID); + + /* dest */ + mono_mb_emit_ldloc (mb, dest_ptr); + + /* src */ + mono_mb_emit_ldloc (mb, conv_arg); + mono_mb_emit_ldloc (mb, index_var); + + mono_mb_emit_byte (mb, CEE_LDELEM_REF); + + mono_mb_emit_icall (mb, conv_to_icall (conv, &stind_op)); + mono_mb_emit_byte (mb, stind_op); + } + else { + char *msg = g_strdup ("Marshalling of non-string and non-blittable arrays to managed code is not implemented."); + mono_mb_emit_exception_marshal_directive (mb, msg); + return conv_arg; + } + + mono_mb_emit_add_to_local (mb, index_var, 1); + mono_mb_emit_add_to_local (mb, dest_ptr, esize); + + mono_mb_emit_branch_label (mb, CEE_BR, label2); + + mono_mb_patch_branch (mb, label1); + mono_mb_patch_branch (mb, label3); + + break; + } + case MARSHAL_ACTION_MANAGED_CONV_RESULT: { + MonoClass *eklass; + guint32 label1, label2, label3; + int index_var, src, dest, esize; + MonoMarshalConv conv = MONO_MARSHAL_CONV_INVALID; + gboolean is_string = FALSE; + + g_assert (!t->byref); + + eklass = klass->element_class; + + mono_marshal_load_type_info (eklass); + + if (eklass == mono_defaults.string_class) { + is_string = TRUE; + conv = mono_marshal_get_string_to_ptr_conv (m->piinfo, spec); + } + else { + g_assert_not_reached (); + } + + if (is_string) + esize = sizeof (gpointer); + else if (eklass == mono_defaults.char_class) + esize = mono_pinvoke_is_unicode (m->piinfo) ? 2 : 1; + else + esize = mono_class_native_size (eklass, NULL); + + src = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + dest = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + + mono_mb_emit_stloc (mb, src); + mono_mb_emit_ldloc (mb, src); + mono_mb_emit_stloc (mb, 3); + + /* Check for null */ + mono_mb_emit_ldloc (mb, src); + label1 = mono_mb_emit_branch (mb, CEE_BRFALSE); + + /* Allocate native array */ + mono_mb_emit_icon (mb, esize); + mono_mb_emit_ldloc (mb, src); + mono_mb_emit_byte (mb, CEE_LDLEN); + + if (eklass == mono_defaults.string_class) { + /* Make the array bigger for the terminating null */ + mono_mb_emit_byte (mb, CEE_LDC_I4_1); + mono_mb_emit_byte (mb, CEE_ADD); + } + mono_mb_emit_byte (mb, CEE_MUL); + mono_mb_emit_icall (mb, ves_icall_marshal_alloc); + mono_mb_emit_stloc (mb, dest); + mono_mb_emit_ldloc (mb, dest); + mono_mb_emit_stloc (mb, 3); + + /* Emit marshalling loop */ + index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + mono_mb_emit_byte (mb, CEE_LDC_I4_0); + mono_mb_emit_stloc (mb, index_var); + label2 = mono_mb_get_label (mb); + mono_mb_emit_ldloc (mb, index_var); + mono_mb_emit_ldloc (mb, src); + mono_mb_emit_byte (mb, CEE_LDLEN); + label3 = mono_mb_emit_branch (mb, CEE_BGE); + + /* Emit marshalling code */ + if (is_string) { + int stind_op; + g_assert (conv != MONO_MARSHAL_CONV_INVALID); + + /* dest */ + mono_mb_emit_ldloc (mb, dest); + + /* src */ + mono_mb_emit_ldloc (mb, src); + mono_mb_emit_ldloc (mb, index_var); + + mono_mb_emit_byte (mb, CEE_LDELEM_REF); + + mono_mb_emit_icall (mb, conv_to_icall (conv, &stind_op)); + mono_mb_emit_byte (mb, stind_op); + } + else { + char *msg = g_strdup ("Marshalling of non-string arrays to managed code is not implemented."); + mono_mb_emit_exception_marshal_directive (mb, msg); + return conv_arg; + } + + mono_mb_emit_add_to_local (mb, index_var, 1); + mono_mb_emit_add_to_local (mb, dest, esize); + + mono_mb_emit_branch_label (mb, CEE_BR, label2); + + mono_mb_patch_branch (mb, label3); + mono_mb_patch_branch (mb, label1); + break; + } + default: + g_assert_not_reached (); + } +#endif + return conv_arg; +} + +static MonoType* +marshal_boolean_conv_in_get_local_type (MonoMarshalSpec *spec, guint8 *ldc_op /*out*/) +{ + if (spec == NULL) { + return &mono_defaults.int32_class->byval_arg; + } else { + switch (spec->native) { + case MONO_NATIVE_I1: + case MONO_NATIVE_U1: + return &mono_defaults.byte_class->byval_arg; + case MONO_NATIVE_VARIANTBOOL: + if (ldc_op) *ldc_op = CEE_LDC_I4_M1; + return &mono_defaults.int16_class->byval_arg; + case MONO_NATIVE_BOOLEAN: + return &mono_defaults.int32_class->byval_arg; + default: + g_warning ("marshalling bool as native type %x is currently not supported", spec->native); + return &mono_defaults.int32_class->byval_arg; + } + } +} + +static MonoClass* +marshal_boolean_managed_conv_in_get_conv_arg_class (MonoMarshalSpec *spec, guint8 *ldop/*out*/) +{ + MonoClass* conv_arg_class = mono_defaults.int32_class; + if (spec) { + switch (spec->native) { + case MONO_NATIVE_I1: + case MONO_NATIVE_U1: + conv_arg_class = mono_defaults.byte_class; + if (ldop) *ldop = CEE_LDIND_I1; + break; + case MONO_NATIVE_VARIANTBOOL: + conv_arg_class = mono_defaults.int16_class; + if (ldop) *ldop = CEE_LDIND_I2; + break; + case MONO_NATIVE_BOOLEAN: + break; + default: + g_warning ("marshalling bool as native type %x is currently not supported", spec->native); + } + } + return conv_arg_class; +} + +static int +emit_marshal_boolean (EmitMarshalContext *m, int argnum, MonoType *t, + MonoMarshalSpec *spec, + int conv_arg, MonoType **conv_arg_type, + MarshalAction action) +{ +#ifndef ENABLE_ILGEN + switch (action) { + case MARSHAL_ACTION_CONV_IN: + if (t->byref) + *conv_arg_type = &mono_defaults.int_class->byval_arg; + else + *conv_arg_type = marshal_boolean_conv_in_get_local_type (spec, NULL); + break; + + case MARSHAL_ACTION_MANAGED_CONV_IN: { + MonoClass* conv_arg_class = marshal_boolean_managed_conv_in_get_conv_arg_class (spec, NULL); + if (t->byref) + *conv_arg_type = &conv_arg_class->this_arg; + else + *conv_arg_type = &conv_arg_class->byval_arg; + break; + } + + } +#else + MonoMethodBuilder *mb = m->mb; + + switch (action) { + case MARSHAL_ACTION_CONV_IN: { + MonoType *local_type; + int label_false; + guint8 ldc_op = CEE_LDC_I4_1; + + local_type = marshal_boolean_conv_in_get_local_type (spec, &ldc_op); + if (t->byref) + *conv_arg_type = &mono_defaults.int_class->byval_arg; + else + *conv_arg_type = local_type; + conv_arg = mono_mb_add_local (mb, local_type); + + mono_mb_emit_ldarg (mb, argnum); + if (t->byref) + mono_mb_emit_byte (mb, CEE_LDIND_I1); + label_false = mono_mb_emit_branch (mb, CEE_BRFALSE); + mono_mb_emit_byte (mb, ldc_op); + mono_mb_emit_stloc (mb, conv_arg); + mono_mb_patch_branch (mb, label_false); + + break; + } + + case MARSHAL_ACTION_CONV_OUT: + { + int label_false, label_end; + if (!t->byref) + break; + + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_ldloc (mb, conv_arg); + + label_false = mono_mb_emit_branch (mb, CEE_BRFALSE); + mono_mb_emit_byte (mb, CEE_LDC_I4_1); + + label_end = mono_mb_emit_branch (mb, CEE_BR); + mono_mb_patch_branch (mb, label_false); + mono_mb_emit_byte (mb, CEE_LDC_I4_0); + mono_mb_patch_branch (mb, label_end); + + mono_mb_emit_byte (mb, CEE_STIND_I1); + break; + } + + case MARSHAL_ACTION_PUSH: + if (t->byref) + mono_mb_emit_ldloc_addr (mb, conv_arg); + else if (conv_arg) + mono_mb_emit_ldloc (mb, conv_arg); + else + mono_mb_emit_ldarg (mb, argnum); + break; + + case MARSHAL_ACTION_CONV_RESULT: + /* maybe we need to make sure that it fits within 8 bits */ + mono_mb_emit_stloc (mb, 3); + break; + + case MARSHAL_ACTION_MANAGED_CONV_IN: { + MonoClass* conv_arg_class = mono_defaults.int32_class; + guint8 ldop = CEE_LDIND_I4; + int label_null, label_false; + + conv_arg_class = marshal_boolean_managed_conv_in_get_conv_arg_class (spec, &ldop); + conv_arg = mono_mb_add_local (mb, &mono_defaults.boolean_class->byval_arg); + + if (t->byref) + *conv_arg_type = &conv_arg_class->this_arg; + else + *conv_arg_type = &conv_arg_class->byval_arg; + + + mono_mb_emit_ldarg (mb, argnum); + + /* Check null */ + if (t->byref) { + label_null = mono_mb_emit_branch (mb, CEE_BRFALSE); + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_byte (mb, ldop); + } else + label_null = 0; + + label_false = mono_mb_emit_branch (mb, CEE_BRFALSE); + mono_mb_emit_byte (mb, CEE_LDC_I4_1); + mono_mb_emit_stloc (mb, conv_arg); + mono_mb_patch_branch (mb, label_false); + + if (t->byref) + mono_mb_patch_branch (mb, label_null); + break; + } + + case MARSHAL_ACTION_MANAGED_CONV_OUT: { + guint8 stop = CEE_STIND_I4; + guint8 ldc_op = CEE_LDC_I4_1; + int label_null,label_false, label_end;; + + if (!t->byref) + break; + if (spec) { + switch (spec->native) { + case MONO_NATIVE_I1: + case MONO_NATIVE_U1: + stop = CEE_STIND_I1; + break; + case MONO_NATIVE_VARIANTBOOL: + stop = CEE_STIND_I2; + ldc_op = CEE_LDC_I4_M1; + break; + default: + break; + } + } + + /* Check null */ + mono_mb_emit_ldarg (mb, argnum); + label_null = mono_mb_emit_branch (mb, CEE_BRFALSE); + + mono_mb_emit_ldarg (mb, argnum); + mono_mb_emit_ldloc (mb, conv_arg); + + label_false = mono_mb_emit_branch (mb, CEE_BRFALSE); + mono_mb_emit_byte (mb, ldc_op); + label_end = mono_mb_emit_branch (mb, CEE_BR); + + mono_mb_patch_branch (mb, label_false); + mono_mb_emit_byte (mb, CEE_LDC_I4_0); + mono_mb_patch_branch (mb, label_end); + + mono_mb_emit_byte (mb, stop); + mono_mb_patch_branch (mb, label_null); + break; + } + + default: + g_assert_not_reached (); + } +#endif + return conv_arg; +} + +static int +emit_marshal_ptr (EmitMarshalContext *m, int argnum, MonoType *t, + MonoMarshalSpec *spec, int conv_arg, + MonoType **conv_arg_type, MarshalAction action) +{ +#ifdef ENABLE_ILGEN + MonoMethodBuilder *mb = m->mb; + + switch (action) { + case MARSHAL_ACTION_CONV_IN: + /* MS seems to allow this in some cases, ie. bxc #158 */ + /* + if (MONO_TYPE_ISSTRUCT (t->data.type) && !mono_class_from_mono_type (t->data.type)->blittable) { + char *msg = g_strdup_printf ("Can not marshal 'parameter #%d': Pointers can not reference marshaled structures. Use byref instead.", argnum + 1); + mono_mb_emit_exception_marshal_directive (m->mb, msg); + } + */ + break; + + case MARSHAL_ACTION_PUSH: + mono_mb_emit_ldarg (mb, argnum); + break; + + case MARSHAL_ACTION_CONV_RESULT: + /* no conversions necessary */ + mono_mb_emit_stloc (mb, 3); + break; + + default: + break; + } +#endif + return conv_arg; +} + +static int +emit_marshal_char (EmitMarshalContext *m, int argnum, MonoType *t, + MonoMarshalSpec *spec, int conv_arg, + MonoType **conv_arg_type, MarshalAction action) +{ +#ifdef ENABLE_ILGEN + MonoMethodBuilder *mb = m->mb; + + switch (action) { + case MARSHAL_ACTION_PUSH: + /* fixme: dont know how to marshal that. We cant simply + * convert it to a one byte UTF8 character, because an + * unicode character may need more that one byte in UTF8 */ + mono_mb_emit_ldarg (mb, argnum); + break; + + case MARSHAL_ACTION_CONV_RESULT: + /* fixme: we need conversions here */ + mono_mb_emit_stloc (mb, 3); + break; + + default: + break; + } +#endif + return conv_arg; +} + +static int +emit_marshal_scalar (EmitMarshalContext *m, int argnum, MonoType *t, + MonoMarshalSpec *spec, int conv_arg, + MonoType **conv_arg_type, MarshalAction action) +{ +#ifdef ENABLE_ILGEN + MonoMethodBuilder *mb = m->mb; + + switch (action) { + case MARSHAL_ACTION_PUSH: + mono_mb_emit_ldarg (mb, argnum); + break; + + case MARSHAL_ACTION_CONV_RESULT: + /* no conversions necessary */ + mono_mb_emit_stloc (mb, 3); + break; + + default: + break; + } +#endif + return conv_arg; +} + +static int +emit_marshal (EmitMarshalContext *m, int argnum, MonoType *t, + MonoMarshalSpec *spec, int conv_arg, + MonoType **conv_arg_type, MarshalAction action) +{ + /* Ensure that we have marshalling info for this param */ + mono_marshal_load_type_info (mono_class_from_mono_type (t)); + + if (spec && spec->native == MONO_NATIVE_CUSTOM) + return emit_marshal_custom (m, argnum, t, spec, conv_arg, conv_arg_type, action); + + if (spec && spec->native == MONO_NATIVE_ASANY) + return emit_marshal_asany (m, argnum, t, spec, conv_arg, conv_arg_type, action); + + switch (t->type) { + case MONO_TYPE_VALUETYPE: + if (t->data.klass == mono_defaults.handleref_class) + return emit_marshal_handleref (m, argnum, t, spec, conv_arg, conv_arg_type, action); + + return emit_marshal_vtype (m, argnum, t, spec, conv_arg, conv_arg_type, action); + case MONO_TYPE_STRING: + return emit_marshal_string (m, argnum, t, spec, conv_arg, conv_arg_type, action); + case MONO_TYPE_CLASS: + case MONO_TYPE_OBJECT: +#if !defined(DISABLE_COM) && defined(ENABLE_ILGEN) + if (spec && spec->native == MONO_NATIVE_STRUCT) + return emit_marshal_variant (m, argnum, t, spec, conv_arg, conv_arg_type, action); +#endif + +#if !defined(DISABLE_COM) + if (spec && (spec->native == MONO_NATIVE_IUNKNOWN || + spec->native == MONO_NATIVE_IDISPATCH || + spec->native == MONO_NATIVE_INTERFACE)) + return mono_cominterop_emit_marshal_com_interface (m, argnum, t, spec, conv_arg, conv_arg_type, action); + if (spec && (spec->native == MONO_NATIVE_SAFEARRAY) && + (spec->data.safearray_data.elem_type == MONO_VARIANT_VARIANT) && + ((action == MARSHAL_ACTION_CONV_OUT) || (action == MARSHAL_ACTION_CONV_IN) || (action == MARSHAL_ACTION_PUSH))) + return mono_cominterop_emit_marshal_safearray (m, argnum, t, spec, conv_arg, conv_arg_type, action); +#endif + + if (mono_class_try_get_safehandle_class () != NULL && t->data.klass && + mono_class_is_subclass_of (t->data.klass, mono_class_try_get_safehandle_class (), FALSE)) + return emit_marshal_safehandle (m, argnum, t, spec, conv_arg, conv_arg_type, action); + + return emit_marshal_object (m, argnum, t, spec, conv_arg, conv_arg_type, action); + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: + return emit_marshal_array (m, argnum, t, spec, conv_arg, conv_arg_type, action); + case MONO_TYPE_BOOLEAN: + return emit_marshal_boolean (m, argnum, t, spec, conv_arg, conv_arg_type, action); + case MONO_TYPE_PTR: + return emit_marshal_ptr (m, argnum, t, spec, conv_arg, conv_arg_type, action); + case MONO_TYPE_CHAR: + return emit_marshal_char (m, argnum, t, spec, conv_arg, conv_arg_type, action); + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_FNPTR: + return emit_marshal_scalar (m, argnum, t, spec, conv_arg, conv_arg_type, action); + case MONO_TYPE_GENERICINST: + if (mono_type_generic_inst_is_valuetype (t)) + return emit_marshal_vtype (m, argnum, t, spec, conv_arg, conv_arg_type, action); + else + return emit_marshal_object (m, argnum, t, spec, conv_arg, conv_arg_type, action); + default: + return conv_arg; + } +} + +/* How the arguments of an icall should be wrapped */ +typedef enum { + /* Don't wrap at all, pass the argument as is */ + ICALL_HANDLES_WRAP_NONE, + /* Wrap the argument in an object handle, pass the handle to the icall */ + ICALL_HANDLES_WRAP_OBJ, + /* Wrap the argument in an object handle, pass the handle to the icall, + write the value out from the handle when the icall returns */ + ICALL_HANDLES_WRAP_OBJ_INOUT, + /* Initialized an object handle to null, pass to the icalls, + write the value out from the handle when the icall returns */ + ICALL_HANDLES_WRAP_OBJ_OUT, + /* Wrap the argument (a valuetype reference) in a handle to pin its enclosing object, + but pass the raw reference to the icall */ + ICALL_HANDLES_WRAP_VALUETYPE_REF, +} IcallHandlesWrap; + +typedef struct { + IcallHandlesWrap wrap; + /* if wrap is NONE or OBJ or VALUETYPE_REF, this is not meaningful. + if wrap is OBJ_INOUT it's the local var that holds the MonoObjectHandle. + */ + int handle; +} IcallHandlesLocal; + +/* + * Describes how to wrap the given parameter. + * + */ +static IcallHandlesWrap +signature_param_uses_handles (MonoMethodSignature *sig, int param) +{ + if (MONO_TYPE_IS_REFERENCE (sig->params [param])) { + if (mono_signature_param_is_out (sig, param)) + return ICALL_HANDLES_WRAP_OBJ_OUT; + else if (mono_type_is_byref (sig->params [param])) + return ICALL_HANDLES_WRAP_OBJ_INOUT; + else + return ICALL_HANDLES_WRAP_OBJ; + } else if (mono_type_is_byref (sig->params [param])) + return ICALL_HANDLES_WRAP_VALUETYPE_REF; + else + return ICALL_HANDLES_WRAP_NONE; +} + + +#ifdef ENABLE_ILGEN +/** + * mono_marshal_emit_native_wrapper: + * \param image the image to use for looking up custom marshallers + * \param sig The signature of the native function + * \param piinfo Marshalling information + * \param mspecs Marshalling information + * \param aot whenever the created method will be compiled by the AOT compiler + * \param method if non-NULL, the pinvoke method to call + * \param check_exceptions Whenever to check for pending exceptions after the native call + * \param func_param the function to call is passed as a boxed IntPtr as the first parameter + * + * generates IL code for the pinvoke wrapper, the generated code calls \p func . + */ +void +mono_marshal_emit_native_wrapper (MonoImage *image, MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethodPInvoke *piinfo, MonoMarshalSpec **mspecs, gpointer func, gboolean aot, gboolean check_exceptions, gboolean func_param) +{ + EmitMarshalContext m; + MonoMethodSignature *csig; + MonoClass *klass; + int i, argnum, *tmp_locals; + int type, param_shift = 0; + int coop_gc_stack_dummy, coop_gc_var; +#ifndef DISABLE_COM + int coop_cominterop_fnptr; +#endif + + memset (&m, 0, sizeof (m)); + m.mb = mb; + m.sig = sig; + m.piinfo = piinfo; + + /* we copy the signature, so that we can set pinvoke to 0 */ + if (func_param) { + /* The function address is passed as the first argument */ + g_assert (!sig->hasthis); + param_shift += 1; + } + csig = mono_metadata_signature_dup_full (mb->method->klass->image, sig); + csig->pinvoke = 1; + m.csig = csig; + m.image = image; + + if (sig->hasthis) + param_shift += 1; + + /* we allocate local for use with emit_struct_conv() */ + /* allocate local 0 (pointer) src_ptr */ + mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + /* allocate local 1 (pointer) dst_ptr */ + mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + /* allocate local 2 (boolean) delete_old */ + mono_mb_add_local (mb, &mono_defaults.boolean_class->byval_arg); + + /* delete_old = FALSE */ + mono_mb_emit_icon (mb, 0); + mono_mb_emit_stloc (mb, 2); + + if (!MONO_TYPE_IS_VOID (sig->ret)) { + /* allocate local 3 to store the return value */ + mono_mb_add_local (mb, sig->ret); + } + + if (mono_threads_is_blocking_transition_enabled ()) { + /* local 4, dummy local used to get a stack address for suspend funcs */ + coop_gc_stack_dummy = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + /* local 5, the local to be used when calling the suspend funcs */ + coop_gc_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); +#ifndef DISABLE_COM + if (!func_param && MONO_CLASS_IS_IMPORT (mb->method->klass)) { + coop_cominterop_fnptr = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + } +#endif + } + + /* + * cookie = mono_threads_enter_gc_safe_region_unbalanced (ref dummy); + * + * ret = method (...); + * + * mono_threads_exit_gc_safe_region_unbalanced (cookie, ref dummy); + * + * + * + * return ret; + */ + + if (MONO_TYPE_ISSTRUCT (sig->ret)) + m.vtaddr_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + + if (mspecs [0] && mspecs [0]->native == MONO_NATIVE_CUSTOM) { + /* Return type custom marshaling */ + /* + * Since we can't determine the return type of the unmanaged function, + * we assume it returns a pointer, and pass that pointer to + * MarshalNativeToManaged. + */ + csig->ret = &mono_defaults.int_class->byval_arg; + } + + /* we first do all conversions */ + tmp_locals = (int *)alloca (sizeof (int) * sig->param_count); + m.orig_conv_args = (int *)alloca (sizeof (int) * (sig->param_count + 1)); + + for (i = 0; i < sig->param_count; i ++) { + tmp_locals [i] = emit_marshal (&m, i + param_shift, sig->params [i], mspecs [i + 1], 0, &csig->params [i], MARSHAL_ACTION_CONV_IN); + } + + // In coop mode need to register blocking state during native call + if (mono_threads_is_blocking_transition_enabled ()) { + // Perform an extra, early lookup of the function address, so any exceptions + // potentially resulting from the lookup occur before entering blocking mode. + if (!func_param && !MONO_CLASS_IS_IMPORT (mb->method->klass) && aot) { + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_op (mb, CEE_MONO_ICALL_ADDR, &piinfo->method); + mono_mb_emit_byte (mb, CEE_POP); // Result not needed yet + } + +#ifndef DISABLE_COM + if (!func_param && MONO_CLASS_IS_IMPORT (mb->method->klass)) { + mono_mb_emit_cominterop_get_function_pointer (mb, &piinfo->method); + mono_mb_emit_stloc (mb, coop_cominterop_fnptr); + } +#endif + + mono_mb_emit_ldloc_addr (mb, coop_gc_stack_dummy); + mono_mb_emit_icall (mb, mono_threads_enter_gc_safe_region_unbalanced); + mono_mb_emit_stloc (mb, coop_gc_var); + } + + /* push all arguments */ + + if (sig->hasthis) + mono_mb_emit_byte (mb, CEE_LDARG_0); + + for (i = 0; i < sig->param_count; i++) { + emit_marshal (&m, i + param_shift, sig->params [i], mspecs [i + 1], tmp_locals [i], NULL, MARSHAL_ACTION_PUSH); + } + + /* call the native method */ + if (func_param) { + mono_mb_emit_byte (mb, CEE_LDARG_0); + mono_mb_emit_op (mb, CEE_UNBOX, mono_defaults.int_class); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_calli (mb, csig); + } else if (MONO_CLASS_IS_IMPORT (mb->method->klass)) { +#ifndef DISABLE_COM + if (!mono_threads_is_blocking_transition_enabled ()) { + mono_mb_emit_cominterop_call (mb, csig, &piinfo->method); + } else { + mono_mb_emit_ldloc (mb, coop_cominterop_fnptr); + mono_mb_emit_cominterop_call_function_pointer (mb, csig); + } +#else + g_assert_not_reached (); +#endif + } else { + if (aot) { + /* Reuse the ICALL_ADDR opcode for pinvokes too */ + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_op (mb, CEE_MONO_ICALL_ADDR, &piinfo->method); + mono_mb_emit_calli (mb, csig); + } else { + mono_mb_emit_native_call (mb, csig, func); + } + } + + /* Set LastError if needed */ + if (piinfo->piflags & PINVOKE_ATTRIBUTE_SUPPORTS_LAST_ERROR) { +#ifdef TARGET_WIN32 + if (!aot) { + static MonoMethodSignature *get_last_error_sig = NULL; + if (!get_last_error_sig) { + get_last_error_sig = mono_metadata_signature_alloc (mono_defaults.corlib, 0); + get_last_error_sig->ret = &mono_defaults.int_class->byval_arg; + get_last_error_sig->pinvoke = 1; + } + + /* + * Have to call GetLastError () early and without a wrapper, since various runtime components could + * clobber its value. + */ + mono_mb_emit_native_call (mb, get_last_error_sig, GetLastError); + mono_mb_emit_icall (mb, mono_marshal_set_last_error_windows); + } else { + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_GET_LAST_ERROR); + mono_mb_emit_icall (mb, mono_marshal_set_last_error_windows); + } +#else + mono_mb_emit_icall (mb, mono_marshal_set_last_error); +#endif + } + + if (MONO_TYPE_ISSTRUCT (sig->ret)) { + MonoClass *klass = mono_class_from_mono_type (sig->ret); + mono_class_init (klass); + if (!(mono_class_is_explicit_layout (klass) || klass->blittable)) { + /* This is used by emit_marshal_vtype (), but it needs to go right before the call */ + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_VTADDR); + mono_mb_emit_stloc (mb, m.vtaddr_var); + } + } + + /* Unblock before converting the result, since that can involve calls into the runtime */ + if (mono_threads_is_blocking_transition_enabled ()) { + mono_mb_emit_ldloc (mb, coop_gc_var); + mono_mb_emit_ldloc_addr (mb, coop_gc_stack_dummy); + mono_mb_emit_icall (mb, mono_threads_exit_gc_safe_region_unbalanced); + } + + /* convert the result */ + if (!sig->ret->byref) { + MonoMarshalSpec *spec = mspecs [0]; + type = sig->ret->type; + + if (spec && spec->native == MONO_NATIVE_CUSTOM) { + emit_marshal (&m, 0, sig->ret, spec, 0, NULL, MARSHAL_ACTION_CONV_RESULT); + } else { + handle_enum: + switch (type) { + case MONO_TYPE_VOID: + break; + case MONO_TYPE_VALUETYPE: + klass = sig->ret->data.klass; + if (klass->enumtype) { + type = mono_class_enum_basetype (sig->ret->data.klass)->type; + goto handle_enum; + } + emit_marshal (&m, 0, sig->ret, spec, 0, NULL, MARSHAL_ACTION_CONV_RESULT); + break; + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_FNPTR: + case MONO_TYPE_STRING: + case MONO_TYPE_CLASS: + case MONO_TYPE_OBJECT: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_CHAR: + case MONO_TYPE_PTR: + case MONO_TYPE_GENERICINST: + emit_marshal (&m, 0, sig->ret, spec, 0, NULL, MARSHAL_ACTION_CONV_RESULT); + break; + case MONO_TYPE_TYPEDBYREF: + default: + g_warning ("return type 0x%02x unknown", sig->ret->type); + g_assert_not_reached (); + } + } + } else { + mono_mb_emit_stloc (mb, 3); + } + + /* + * Need to call this after converting the result since MONO_VTADDR needs + * to be adjacent to the call instruction. + */ + if (check_exceptions) + emit_thread_interrupt_checkpoint (mb); + + /* we need to convert byref arguments back and free string arrays */ + for (i = 0; i < sig->param_count; i++) { + MonoType *t = sig->params [i]; + MonoMarshalSpec *spec = mspecs [i + 1]; + + argnum = i + param_shift; + + if (spec && ((spec->native == MONO_NATIVE_CUSTOM) || (spec->native == MONO_NATIVE_ASANY))) { + emit_marshal (&m, argnum, t, spec, tmp_locals [i], NULL, MARSHAL_ACTION_CONV_OUT); + continue; + } + + switch (t->type) { + case MONO_TYPE_STRING: + case MONO_TYPE_VALUETYPE: + case MONO_TYPE_CLASS: + case MONO_TYPE_OBJECT: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_BOOLEAN: + emit_marshal (&m, argnum, t, spec, tmp_locals [i], NULL, MARSHAL_ACTION_CONV_OUT); + break; + default: + break; + } + } + + if (!MONO_TYPE_IS_VOID(sig->ret)) + mono_mb_emit_ldloc (mb, 3); + + mono_mb_emit_byte (mb, CEE_RET); +} +#endif /* ENABLE_ILGEN */ + +/** + * mono_marshal_get_native_wrapper: + * \param method The \c MonoMethod to wrap. + * \param check_exceptions Whenever to check for pending exceptions + * + * Generates IL code for the pinvoke wrapper. The generated method + * calls the unmanaged code in \c piinfo->addr. + */ +MonoMethod * +mono_marshal_get_native_wrapper (MonoMethod *method, gboolean check_exceptions, gboolean aot) +{ + MonoMethodSignature *sig, *csig; + MonoMethodPInvoke *piinfo = (MonoMethodPInvoke *) method; + MonoMethodBuilder *mb; + MonoMarshalSpec **mspecs; + MonoMethod *res; + GHashTable *cache; + gboolean pinvoke = FALSE; + gpointer iter; + int i; + const char *exc_class = "MissingMethodException"; + const char *exc_arg = NULL; + WrapperInfo *info; + + g_assert (method != NULL); + g_assert (mono_method_signature (method)->pinvoke); + + GHashTable **cache_ptr; + + if (aot) { + if (check_exceptions) + cache_ptr = &mono_method_get_wrapper_cache (method)->native_wrapper_aot_check_cache; + else + cache_ptr = &mono_method_get_wrapper_cache (method)->native_wrapper_aot_cache; + } else { + if (check_exceptions) + cache_ptr = &mono_method_get_wrapper_cache (method)->native_wrapper_check_cache; + else + cache_ptr = &mono_method_get_wrapper_cache (method)->native_wrapper_cache; + } + + cache = get_cache (cache_ptr, mono_aligned_addr_hash, NULL); + + if ((res = mono_marshal_find_in_cache (cache, method))) + return res; + + if (MONO_CLASS_IS_IMPORT (method->klass)) { + /* The COM code is not AOT compatible, it calls mono_custom_attrs_get_attr_checked () */ + if (aot) + return method; +#ifndef DISABLE_COM + return mono_cominterop_get_native_wrapper (method); +#else + g_assert_not_reached (); +#endif + } + + sig = mono_method_signature (method); + + if (!(method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) && + (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) + pinvoke = TRUE; + + if (!piinfo->addr) { + if (pinvoke) { + if (method->iflags & METHOD_IMPL_ATTRIBUTE_NATIVE) + exc_arg = "Method contains unsupported native code"; + else if (!aot) + mono_lookup_pinvoke_call (method, &exc_class, &exc_arg); + } else { + piinfo->addr = mono_lookup_internal_call (method); + } + } + + /* hack - redirect certain string constructors to CreateString */ + if (piinfo->addr == ves_icall_System_String_ctor_RedirectToCreateString) { + g_assert (!pinvoke); + g_assert (method->string_ctor); + g_assert (sig->hasthis); + + /* CreateString returns a value */ + csig = mono_metadata_signature_dup_full (method->klass->image, sig); + csig->ret = &mono_defaults.string_class->byval_arg; + csig->pinvoke = 0; + + iter = NULL; + while ((res = mono_class_get_methods (mono_defaults.string_class, &iter))) { + if (!strcmp ("CreateString", res->name) && + mono_metadata_signature_equal (csig, mono_method_signature (res))) { + WrapperInfo *info; + + g_assert (!(res->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)); + g_assert (!(res->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)); + + /* create a wrapper to preserve .ctor in stack trace */ + mb = mono_mb_new (method->klass, method->name, MONO_WRAPPER_MANAGED_TO_MANAGED); + +#ifdef ENABLE_ILGEN + mono_mb_emit_byte (mb, CEE_LDARG_0); + for (i = 1; i <= csig->param_count; i++) + mono_mb_emit_ldarg (mb, i); + mono_mb_emit_managed_call (mb, res, NULL); + mono_mb_emit_byte (mb, CEE_RET); +#endif + + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_STRING_CTOR); + info->d.string_ctor.method = method; + + /* use native_wrapper_cache because internal calls are looked up there */ + res = mono_mb_create_and_cache_full (cache, method, mb, csig, + csig->param_count + 1, info, NULL); + mono_mb_free (mb); + + return res; + } + } + + /* exception will be thrown */ + piinfo->addr = NULL; + g_warning ("cannot find CreateString for .ctor"); + } + + mb = mono_mb_new (method->klass, method->name, MONO_WRAPPER_MANAGED_TO_NATIVE); + + mb->method->save_lmf = 1; + + /* + * In AOT mode and embedding scenarios, it is possible that the icall is not + * registered in the runtime doing the AOT compilation. + */ + if (!piinfo->addr && !aot) { +#ifdef ENABLE_ILGEN + mono_mb_emit_exception (mb, exc_class, exc_arg); +#endif + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); + info->d.managed_to_native.method = method; + + csig = mono_metadata_signature_dup_full (method->klass->image, sig); + csig->pinvoke = 0; + res = mono_mb_create_and_cache_full (cache, method, mb, csig, + csig->param_count + 16, info, NULL); + mono_mb_free (mb); + + return res; + } + + /* internal calls: we simply push all arguments and call the method (no conversions) */ + if (method->iflags & (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL | METHOD_IMPL_ATTRIBUTE_RUNTIME)) { + if (sig->hasthis) + csig = mono_metadata_signature_dup_add_this (method->klass->image, sig, method->klass); + else + csig = mono_metadata_signature_dup_full (method->klass->image, sig); + + //printf ("%s\n", mono_method_full_name (method, 1)); + + /* hack - string constructors returns a value */ + if (method->string_ctor) + csig->ret = &mono_defaults.string_class->byval_arg; + +#ifdef ENABLE_ILGEN + // FIXME: + MonoClass *handle_stack_mark_class; + MonoClass *error_class; + int thread_info_var = -1, stack_mark_var = -1, error_var = -1; + MonoMethodSignature *call_sig = csig; + gboolean uses_handles = FALSE; + gboolean save_handles_to_locals = FALSE; + IcallHandlesLocal *handles_locals = NULL; + + (void) mono_lookup_internal_call_full (method, &uses_handles); + + /* If it uses handles and MonoError, it had better check exceptions */ + g_assert (!uses_handles || check_exceptions); + + if (uses_handles) { + MonoMethodSignature *ret; + + /* Add a MonoError argument and figure out which args need to be wrapped in handles */ + // FIXME: The stuff from mono_metadata_signature_dup_internal_with_padding () + ret = mono_metadata_signature_alloc (method->klass->image, csig->param_count + 1); + + ret->param_count = csig->param_count + 1; + ret->ret = csig->ret; + + handles_locals = g_new0 (IcallHandlesLocal, csig->param_count); + for (int i = 0; i < csig->param_count; ++i) { + IcallHandlesWrap w = signature_param_uses_handles (csig, i); + handles_locals [i].wrap = w; + switch (w) { + case ICALL_HANDLES_WRAP_OBJ: + case ICALL_HANDLES_WRAP_OBJ_INOUT: + case ICALL_HANDLES_WRAP_OBJ_OUT: + ret->params [i] = mono_class_get_byref_type (mono_class_from_mono_type(csig->params[i])); + if (w == ICALL_HANDLES_WRAP_OBJ_OUT || w == ICALL_HANDLES_WRAP_OBJ_INOUT) + save_handles_to_locals = TRUE; + break; + case ICALL_HANDLES_WRAP_NONE: + case ICALL_HANDLES_WRAP_VALUETYPE_REF: + ret->params [i] = csig->params [i]; + break; + default: + g_assert_not_reached (); + } + } + /* Add MonoError* param */ + ret->params [csig->param_count] = &mono_get_intptr_class ()->byval_arg; + ret->pinvoke = csig->pinvoke; + + call_sig = ret; + } + + if (uses_handles) { + handle_stack_mark_class = mono_class_load_from_name (mono_get_corlib (), "Mono", "RuntimeStructs/HandleStackMark"); + error_class = mono_class_load_from_name (mono_get_corlib (), "Mono", "RuntimeStructs/MonoError"); + + thread_info_var = mono_mb_add_local (mb, &mono_get_intptr_class ()->byval_arg); + stack_mark_var = mono_mb_add_local (mb, &handle_stack_mark_class->byval_arg); + error_var = mono_mb_add_local (mb, &error_class->byval_arg); + + if (save_handles_to_locals) { + /* add a local var to hold the handles for each out arg */ + for (int i = 0; i < sig->param_count; ++i) { + int j = i + sig->hasthis; + switch (handles_locals[j].wrap) { + case ICALL_HANDLES_WRAP_NONE: + case ICALL_HANDLES_WRAP_OBJ: + case ICALL_HANDLES_WRAP_VALUETYPE_REF: + handles_locals [j].handle = -1; + break; + case ICALL_HANDLES_WRAP_OBJ_INOUT: + case ICALL_HANDLES_WRAP_OBJ_OUT: + handles_locals [j].handle = mono_mb_add_local (mb, sig->params [i]); + break; + default: + g_assert_not_reached (); + } + } + } + } + + if (sig->hasthis) { + int pos; + + /* + * Add a null check since public icalls can be called with 'call' which + * does no such check. + */ + mono_mb_emit_byte (mb, CEE_LDARG_0); + pos = mono_mb_emit_branch (mb, CEE_BRTRUE); + mono_mb_emit_exception (mb, "NullReferenceException", NULL); + mono_mb_patch_branch (mb, pos); + } + + if (uses_handles) { + mono_mb_emit_ldloc_addr (mb, stack_mark_var); + mono_mb_emit_ldloc_addr (mb, error_var); + mono_mb_emit_icall (mb, mono_icall_start); + mono_mb_emit_stloc (mb, thread_info_var); + + if (sig->hasthis) { + mono_mb_emit_byte (mb, CEE_LDARG_0); + /* TODO support adding wrappers to non-static struct methods */ + g_assert (!mono_class_is_valuetype(mono_method_get_class (method))); + mono_mb_emit_icall (mb, mono_icall_handle_new); + } + for (i = 0; i < sig->param_count; i++) { + /* load each argument. references into the managed heap get wrapped in handles */ + int j = i + sig->hasthis; + switch (handles_locals[j].wrap) { + case ICALL_HANDLES_WRAP_NONE: + mono_mb_emit_ldarg (mb, j); + break; + case ICALL_HANDLES_WRAP_OBJ: + /* argI = mono_handle_new (argI_raw) */ + mono_mb_emit_ldarg (mb, j); + mono_mb_emit_icall (mb, mono_icall_handle_new); + break; + case ICALL_HANDLES_WRAP_OBJ_INOUT: + case ICALL_HANDLES_WRAP_OBJ_OUT: + /* if inout: + * handleI = argI = mono_handle_new (*argI_raw) + * otherwise: + * handleI = argI = mono_handle_new (NULL) + */ + if (handles_locals[j].wrap == ICALL_HANDLES_WRAP_OBJ_INOUT) { + mono_mb_emit_ldarg (mb, j); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + } else + mono_mb_emit_byte (mb, CEE_LDNULL); + mono_mb_emit_icall (mb, mono_icall_handle_new); + /* tmp = argI */ + mono_mb_emit_byte (mb, CEE_DUP); + /* handleI = tmp */ + mono_mb_emit_stloc (mb, handles_locals[j].handle); + break; + case ICALL_HANDLES_WRAP_VALUETYPE_REF: + /* (void) mono_handle_new (argI); argI */ + mono_mb_emit_ldarg (mb, j); + mono_mb_emit_byte (mb, CEE_DUP); + mono_mb_emit_icall (mb, mono_icall_handle_new_interior); + mono_mb_emit_byte (mb, CEE_POP); +#if 0 + fprintf (stderr, " Method %s.%s.%s has byref valuetype argument %d\n", method->klass->name_space, method->klass->name, method->name, i); +#endif + break; + default: + g_assert_not_reached (); + } + } + mono_mb_emit_ldloc_addr (mb, error_var); + } else { + if (sig->hasthis) + mono_mb_emit_byte (mb, CEE_LDARG_0); + for (i = 0; i < sig->param_count; i++) + mono_mb_emit_ldarg (mb, i + sig->hasthis); + } + + if (aot) { + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_op (mb, CEE_MONO_ICALL_ADDR, &piinfo->method); + mono_mb_emit_calli (mb, call_sig); + } else { + g_assert (piinfo->addr); + mono_mb_emit_native_call (mb, call_sig, piinfo->addr); + } + + if (uses_handles) { + if (MONO_TYPE_IS_REFERENCE (sig->ret)) { + // if (ret != NULL_HANDLE) { + // ret = MONO_HANDLE_RAW(ret) + // } + mono_mb_emit_byte (mb, CEE_DUP); + int pos = mono_mb_emit_branch (mb, CEE_BRFALSE); + mono_mb_emit_ldflda (mb, MONO_HANDLE_PAYLOAD_OFFSET (MonoObject)); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_patch_branch (mb, pos); + } + if (save_handles_to_locals) { + for (i = 0; i < sig->param_count; i++) { + int j = i + sig->hasthis; + switch (handles_locals [j].wrap) { + case ICALL_HANDLES_WRAP_NONE: + case ICALL_HANDLES_WRAP_OBJ: + case ICALL_HANDLES_WRAP_VALUETYPE_REF: + break; + case ICALL_HANDLES_WRAP_OBJ_INOUT: + case ICALL_HANDLES_WRAP_OBJ_OUT: + /* *argI_raw = MONO_HANDLE_RAW (handleI) */ + + /* argI_raw */ + mono_mb_emit_ldarg (mb, j); + /* handleI */ + mono_mb_emit_ldloc (mb, handles_locals [j].handle); + /* MONO_HANDLE_RAW(handleI) */ + mono_mb_emit_ldflda (mb, MONO_HANDLE_PAYLOAD_OFFSET (MonoObject)); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + /* *argI_raw = MONO_HANDLE_RAW(handleI) */ + mono_mb_emit_byte (mb, CEE_STIND_REF); + break; + default: + g_assert_not_reached (); + } + } + } + g_free (handles_locals); + + mono_mb_emit_ldloc (mb, thread_info_var); + mono_mb_emit_ldloc_addr (mb, stack_mark_var); + mono_mb_emit_ldloc_addr (mb, error_var); + mono_mb_emit_icall (mb, mono_icall_end); + } + + if (check_exceptions) + emit_thread_interrupt_checkpoint (mb); + mono_mb_emit_byte (mb, CEE_RET); +#endif + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); + info->d.managed_to_native.method = method; + + csig = mono_metadata_signature_dup_full (method->klass->image, csig); + csig->pinvoke = 0; + res = mono_mb_create_and_cache_full (cache, method, mb, csig, csig->param_count + 16, + info, NULL); + + mono_mb_free (mb); + return res; + } + + g_assert (pinvoke); + if (!aot) + g_assert (piinfo->addr); + +#ifdef ENABLE_ILGEN + mspecs = g_new (MonoMarshalSpec*, sig->param_count + 1); + mono_method_get_marshal_info (method, mspecs); + + mono_marshal_emit_native_wrapper (mb->method->klass->image, mb, sig, piinfo, mspecs, piinfo->addr, aot, check_exceptions, FALSE); +#endif + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_PINVOKE); + info->d.managed_to_native.method = method; + + csig = mono_metadata_signature_dup_full (method->klass->image, sig); + csig->pinvoke = 0; + res = mono_mb_create_and_cache_full (cache, method, mb, csig, csig->param_count + 16, + info, NULL); + mono_mb_free (mb); + +#ifdef ENABLE_ILGEN + for (i = sig->param_count; i >= 0; i--) + if (mspecs [i]) + mono_metadata_free_marshal_spec (mspecs [i]); + g_free (mspecs); +#endif + + /* mono_method_print_code (res); */ + + return res; +} + +/** + * mono_marshal_get_native_func_wrapper: + * \param image The image to use for memory allocation and for looking up custom marshallers. + * \param sig The signature of the function + * \param func The native function to wrap + * + * \returns a wrapper method around native functions, similar to the pinvoke + * wrapper. + */ +MonoMethod * +mono_marshal_get_native_func_wrapper (MonoImage *image, MonoMethodSignature *sig, + MonoMethodPInvoke *piinfo, MonoMarshalSpec **mspecs, gpointer func) +{ + MonoMethodSignature *csig; + + SignaturePointerPair key, *new_key; + MonoMethodBuilder *mb; + MonoMethod *res; + GHashTable *cache; + gboolean found; + char *name; + + key.sig = sig; + key.pointer = func; + + // Generic types are not safe to place in MonoImage caches. + g_assert (!sig->is_inflated); + + cache = get_cache (&image->native_func_wrapper_cache, signature_pointer_pair_hash, signature_pointer_pair_equal); + if ((res = mono_marshal_find_in_cache (cache, &key))) + return res; + + name = g_strdup_printf ("wrapper_native_%p", func); + mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_MANAGED_TO_NATIVE); + mb->method->save_lmf = 1; + +#ifdef ENABLE_ILGEN + mono_marshal_emit_native_wrapper (image, mb, sig, piinfo, mspecs, func, FALSE, TRUE, FALSE); +#endif + + csig = mono_metadata_signature_dup_full (image, sig); + csig->pinvoke = 0; + + new_key = g_new (SignaturePointerPair,1); + new_key->sig = csig; + new_key->pointer = func; + + res = mono_mb_create_and_cache_full (cache, new_key, mb, csig, csig->param_count + 16, NULL, &found); + if (found) + g_free (new_key); + + mono_mb_free (mb); + + mono_marshal_set_wrapper_info (res, NULL); + + return res; +} + +/* + * The wrapper receives the native function as a boxed IntPtr as its 'this' argument. This is easier to support in + * AOT. + */ +MonoMethod* +mono_marshal_get_native_func_wrapper_aot (MonoClass *klass) +{ + MonoMethodSignature *sig, *csig; + MonoMethodBuilder *mb; + MonoMethod *res; + GHashTable *cache; + char *name; + WrapperInfo *info; + MonoMethodPInvoke mpiinfo; + MonoMethodPInvoke *piinfo = &mpiinfo; + MonoMarshalSpec **mspecs; + MonoMethod *invoke = mono_get_delegate_invoke (klass); + MonoImage *image = invoke->klass->image; + int i; + + // FIXME: include UnmanagedFunctionPointerAttribute info + + /* + * The wrapper is associated with the delegate type, to pick up the marshalling info etc. + */ + cache = get_cache (&mono_method_get_wrapper_cache (invoke)->native_func_wrapper_aot_cache, mono_aligned_addr_hash, NULL); + + if ((res = mono_marshal_find_in_cache (cache, invoke))) + return res; + + memset (&mpiinfo, 0, sizeof (mpiinfo)); + parse_unmanaged_function_pointer_attr (klass, &mpiinfo); + + mspecs = g_new0 (MonoMarshalSpec*, mono_method_signature (invoke)->param_count + 1); + mono_method_get_marshal_info (invoke, mspecs); + /* Freed below so don't alloc from mempool */ + sig = mono_metadata_signature_dup (mono_method_signature (invoke)); + sig->hasthis = 0; + + name = g_strdup_printf ("wrapper_aot_native"); + mb = mono_mb_new (invoke->klass, name, MONO_WRAPPER_MANAGED_TO_NATIVE); + mb->method->save_lmf = 1; + +#ifdef ENABLE_ILGEN + mono_marshal_emit_native_wrapper (image, mb, sig, piinfo, mspecs, NULL, FALSE, TRUE, TRUE); +#endif + + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NATIVE_FUNC_AOT); + info->d.managed_to_native.method = invoke; + + g_assert (!sig->hasthis); + csig = mono_metadata_signature_dup_add_this (image, sig, mono_defaults.object_class); + csig->pinvoke = 0; + res = mono_mb_create_and_cache_full (cache, invoke, + mb, csig, csig->param_count + 16, + info, NULL); + mono_mb_free (mb); + + for (i = mono_method_signature (invoke)->param_count; i >= 0; i--) + if (mspecs [i]) + mono_metadata_free_marshal_spec (mspecs [i]); + g_free (mspecs); + g_free (sig); + + return res; +} + +/* + * mono_marshal_emit_managed_wrapper: + * + * Emit the body of a native-to-managed wrapper. INVOKE_SIG is the signature of + * the delegate which wraps the managed method to be called. For closed delegates, + * it could have fewer parameters than the method it wraps. + * THIS_LOC is the memory location where the target of the delegate is stored. + */ +void +mono_marshal_emit_managed_wrapper (MonoMethodBuilder *mb, MonoMethodSignature *invoke_sig, MonoMarshalSpec **mspecs, EmitMarshalContext* m, MonoMethod *method, uint32_t target_handle) +{ +#ifndef ENABLE_ILGEN + MonoMethodSignature *sig, *csig; + int i; + + sig = m->sig; + csig = m->csig; + + /* we first do all conversions */ + for (i = 0; i < sig->param_count; i ++) { + MonoType *t = sig->params [i]; + + switch (t->type) { + case MONO_TYPE_OBJECT: + case MONO_TYPE_CLASS: + case MONO_TYPE_VALUETYPE: + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_STRING: + case MONO_TYPE_BOOLEAN: + emit_marshal (m, i, sig->params [i], mspecs [i + 1], 0, &csig->params [i], MARSHAL_ACTION_MANAGED_CONV_IN); + } + } + + if (!sig->ret->byref) { + switch (sig->ret->type) { + case MONO_TYPE_STRING: + csig->ret = &mono_defaults.int_class->byval_arg; + break; + default: + break; + } + } +#else + MonoMethodSignature *sig, *csig; + MonoExceptionClause *clauses, *clause_finally, *clause_catch; + int i, *tmp_locals, ex_local, e_local, attach_cookie_local, attach_dummy_local; + int leave_try_pos, leave_catch_pos, ex_m1_pos; + gboolean closed = FALSE; + + sig = m->sig; + csig = m->csig; + + /* allocate local 0 (pointer) src_ptr */ + mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + /* allocate local 1 (pointer) dst_ptr */ + mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + /* allocate local 2 (boolean) delete_old */ + mono_mb_add_local (mb, &mono_defaults.boolean_class->byval_arg); + + if (!sig->hasthis && sig->param_count != invoke_sig->param_count) { + /* Closed delegate */ + g_assert (sig->param_count == invoke_sig->param_count + 1); + closed = TRUE; + /* Use a new signature without the first argument */ + sig = mono_metadata_signature_dup (sig); + memmove (&sig->params [0], &sig->params [1], (sig->param_count - 1) * sizeof (MonoType*)); + sig->param_count --; + } + + if (!MONO_TYPE_IS_VOID(sig->ret)) { + /* allocate local 3 to store the return value */ + mono_mb_add_local (mb, sig->ret); + } + + if (MONO_TYPE_ISSTRUCT (sig->ret)) + m->vtaddr_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + + ex_local = mono_mb_add_local (mb, &mono_defaults.uint32_class->byval_arg); + e_local = mono_mb_add_local (mb, &mono_defaults.exception_class->byval_arg); + + attach_cookie_local = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + attach_dummy_local = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + + /* + * guint32 ex = -1; + * try { + * // does (STARTING|RUNNING|BLOCKING) -> RUNNING + set/switch domain + * mono_threads_attach_coop (); + * + * + * + * ret = method (...); + * } catch (Exception e) { + * ex = mono_gchandle_new (e, false); + * } finally { + * // does RUNNING -> (RUNNING|BLOCKING) + unset/switch domain + * mono_threads_detach_coop (); + * + * if (ex != -1) + * mono_marshal_ftnptr_eh_callback (ex); + * } + * + * return ret; + */ + + clauses = g_new0 (MonoExceptionClause, 2); + + clause_catch = &clauses [0]; + clause_catch->flags = MONO_EXCEPTION_CLAUSE_NONE; + clause_catch->data.catch_class = mono_defaults.exception_class; + + clause_finally = &clauses [1]; + clause_finally->flags = MONO_EXCEPTION_CLAUSE_FINALLY; + + mono_mb_emit_icon (mb, 0); + mono_mb_emit_stloc (mb, 2); + + mono_mb_emit_icon (mb, -1); + mono_mb_emit_byte (mb, CEE_CONV_U4); + mono_mb_emit_stloc (mb, ex_local); + + /* try { */ + clause_catch->try_offset = clause_finally->try_offset = mono_mb_get_label (mb); + + if (!mono_threads_is_blocking_transition_enabled ()) { + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_JIT_ATTACH); + } else { + /* mono_threads_attach_coop (); */ + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_LDDOMAIN); + mono_mb_emit_ldloc_addr (mb, attach_dummy_local); + /* + * This icall is special cased in the JIT so it works in native-to-managed wrappers in unattached threads. + * Keep this in sync with the CEE_JIT_ICALL code in the JIT. + */ + mono_mb_emit_icall (mb, mono_threads_attach_coop); + mono_mb_emit_stloc (mb, attach_cookie_local); + } + + /* */ + emit_thread_interrupt_checkpoint (mb); + + /* we first do all conversions */ + tmp_locals = (int *)alloca (sizeof (int) * sig->param_count); + for (i = 0; i < sig->param_count; i ++) { + MonoType *t = sig->params [i]; + + switch (t->type) { + case MONO_TYPE_OBJECT: + case MONO_TYPE_CLASS: + case MONO_TYPE_VALUETYPE: + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_STRING: + case MONO_TYPE_BOOLEAN: + tmp_locals [i] = emit_marshal (m, i, sig->params [i], mspecs [i + 1], 0, &csig->params [i], MARSHAL_ACTION_MANAGED_CONV_IN); + + break; + default: + tmp_locals [i] = 0; + break; + } + } + + if (sig->hasthis) { + if (target_handle) { + mono_mb_emit_icon (mb, (gint32)target_handle); + mono_mb_emit_icall (mb, mono_gchandle_get_target); + } else { + /* fixme: */ + g_assert_not_reached (); + } + } else if (closed) { + mono_mb_emit_icon (mb, (gint32)target_handle); + mono_mb_emit_icall (mb, mono_gchandle_get_target); + } + + for (i = 0; i < sig->param_count; i++) { + MonoType *t = sig->params [i]; + + if (tmp_locals [i]) { + if (t->byref) + mono_mb_emit_ldloc_addr (mb, tmp_locals [i]); + else + mono_mb_emit_ldloc (mb, tmp_locals [i]); + } + else + mono_mb_emit_ldarg (mb, i); + } + + /* ret = method (...) */ + mono_mb_emit_managed_call (mb, method, NULL); + + if (MONO_TYPE_ISSTRUCT (sig->ret)) { + MonoClass *klass = mono_class_from_mono_type (sig->ret); + mono_class_init (klass); + if (!(mono_class_is_explicit_layout (klass) || klass->blittable)) { + /* This is used by emit_marshal_vtype (), but it needs to go right before the call */ + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_VTADDR); + mono_mb_emit_stloc (mb, m->vtaddr_var); + } + } + + if (mspecs [0] && mspecs [0]->native == MONO_NATIVE_CUSTOM) { + emit_marshal (m, 0, sig->ret, mspecs [0], 0, NULL, MARSHAL_ACTION_MANAGED_CONV_RESULT); + } else if (!sig->ret->byref) { + switch (sig->ret->type) { + case MONO_TYPE_VOID: + break; + 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_I: + case MONO_TYPE_U: + case MONO_TYPE_PTR: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_OBJECT: + mono_mb_emit_stloc (mb, 3); + break; + case MONO_TYPE_STRING: + csig->ret = &mono_defaults.int_class->byval_arg; + emit_marshal (m, 0, sig->ret, mspecs [0], 0, NULL, MARSHAL_ACTION_MANAGED_CONV_RESULT); + break; + case MONO_TYPE_VALUETYPE: + case MONO_TYPE_CLASS: + case MONO_TYPE_SZARRAY: + emit_marshal (m, 0, sig->ret, mspecs [0], 0, NULL, MARSHAL_ACTION_MANAGED_CONV_RESULT); + break; + default: + g_warning ("return type 0x%02x unknown", sig->ret->type); + g_assert_not_reached (); + } + } else { + mono_mb_emit_stloc (mb, 3); + } + + /* Convert byref arguments back */ + for (i = 0; i < sig->param_count; i ++) { + MonoType *t = sig->params [i]; + MonoMarshalSpec *spec = mspecs [i + 1]; + + if (spec && spec->native == MONO_NATIVE_CUSTOM) { + emit_marshal (m, i, t, mspecs [i + 1], tmp_locals [i], NULL, MARSHAL_ACTION_MANAGED_CONV_OUT); + } + else if (t->byref) { + switch (t->type) { + case MONO_TYPE_CLASS: + case MONO_TYPE_VALUETYPE: + case MONO_TYPE_OBJECT: + case MONO_TYPE_STRING: + case MONO_TYPE_BOOLEAN: + emit_marshal (m, i, t, mspecs [i + 1], tmp_locals [i], NULL, MARSHAL_ACTION_MANAGED_CONV_OUT); + break; + default: + break; + } + } + else if (invoke_sig->params [i]->attrs & PARAM_ATTRIBUTE_OUT) { + /* The [Out] information is encoded in the delegate signature */ + switch (t->type) { + case MONO_TYPE_SZARRAY: + case MONO_TYPE_CLASS: + case MONO_TYPE_VALUETYPE: + emit_marshal (m, i, invoke_sig->params [i], mspecs [i + 1], tmp_locals [i], NULL, MARSHAL_ACTION_MANAGED_CONV_OUT); + break; + default: + g_assert_not_reached (); + } + } + } + + leave_try_pos = mono_mb_emit_branch (mb, CEE_LEAVE); + + /* } [endtry] */ + + /* catch (Exception e) { */ + clause_catch->try_len = mono_mb_get_label (mb) - clause_catch->try_offset; + clause_catch->handler_offset = mono_mb_get_label (mb); + + mono_mb_emit_stloc (mb, e_local); + + /* ex = mono_gchandle_new (e, false); */ + mono_mb_emit_ldloc (mb, e_local); + mono_mb_emit_icon (mb, 0); + mono_mb_emit_icall (mb, mono_gchandle_new); + mono_mb_emit_stloc (mb, ex_local); + + leave_catch_pos = mono_mb_emit_branch (mb, CEE_LEAVE); + + /* } [endcatch] */ + clause_catch->handler_len = mono_mb_get_pos (mb) - clause_catch->handler_offset; + + /* finally { */ + clause_finally->try_len = mono_mb_get_label (mb) - clause_finally->try_offset; + clause_finally->handler_offset = mono_mb_get_label (mb); + + if (!mono_threads_is_blocking_transition_enabled ()) { + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_JIT_DETACH); + } else { + /* mono_threads_detach_coop (); */ + mono_mb_emit_ldloc (mb, attach_cookie_local); + mono_mb_emit_ldloc_addr (mb, attach_dummy_local); + mono_mb_emit_icall (mb, mono_threads_detach_coop); + } + + /* if (ex != -1) */ + mono_mb_emit_ldloc (mb, ex_local); + mono_mb_emit_icon (mb, -1); + mono_mb_emit_byte (mb, CEE_CONV_U4); + ex_m1_pos = mono_mb_emit_branch (mb, CEE_BEQ); + + /* mono_marshal_ftnptr_eh_callback (ex) */ + mono_mb_emit_ldloc (mb, ex_local); + mono_mb_emit_icall (mb, mono_marshal_ftnptr_eh_callback); + + /* [ex == -1] */ + mono_mb_patch_branch (mb, ex_m1_pos); + + mono_mb_emit_byte (mb, CEE_ENDFINALLY); + + /* } [endfinally] */ + clause_finally->handler_len = mono_mb_get_pos (mb) - clause_finally->handler_offset; + + mono_mb_patch_branch (mb, leave_try_pos); + mono_mb_patch_branch (mb, leave_catch_pos); + + /* return ret; */ + if (m->retobj_var) { + mono_mb_emit_ldloc (mb, m->retobj_var); + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_op (mb, CEE_MONO_RETOBJ, m->retobj_class); + } + else { + if (!MONO_TYPE_IS_VOID(sig->ret)) + mono_mb_emit_ldloc (mb, 3); + mono_mb_emit_byte (mb, CEE_RET); + } + + mono_mb_set_clauses (mb, 2, clauses); + + if (closed) + g_free (sig); +#endif +} + +static void +mono_marshal_set_callconv_from_modopt (MonoMethod *method, MonoMethodSignature *csig) +{ + MonoMethodSignature *sig; + int i; + +#ifdef TARGET_WIN32 + /* + * Under windows, delegates passed to native code must use the STDCALL + * calling convention. + */ + csig->call_convention = MONO_CALL_STDCALL; +#endif + + sig = mono_method_signature (method); + + /* Change default calling convention if needed */ + /* Why is this a modopt ? */ + if (sig->ret && sig->ret->num_mods) { + for (i = 0; i < sig->ret->num_mods; ++i) { + MonoError error; + MonoClass *cmod_class = mono_class_get_checked (method->klass->image, sig->ret->modifiers [i].token, &error); + g_assert (mono_error_ok (&error)); + if ((cmod_class->image == mono_defaults.corlib) && !strcmp (cmod_class->name_space, "System.Runtime.CompilerServices")) { + if (!strcmp (cmod_class->name, "CallConvCdecl")) + csig->call_convention = MONO_CALL_C; + else if (!strcmp (cmod_class->name, "CallConvStdcall")) + csig->call_convention = MONO_CALL_STDCALL; + else if (!strcmp (cmod_class->name, "CallConvFastcall")) + csig->call_convention = MONO_CALL_FASTCALL; + else if (!strcmp (cmod_class->name, "CallConvThiscall")) + csig->call_convention = MONO_CALL_THISCALL; + } + } + } +} + +/** + * mono_marshal_get_managed_wrapper: + * Generates IL code to call managed methods from unmanaged code + * If \p target_handle is \c 0, the wrapper info will be a \c WrapperInfo structure. + */ +MonoMethod * +mono_marshal_get_managed_wrapper (MonoMethod *method, MonoClass *delegate_klass, uint32_t target_handle, MonoError *error) +{ + MonoMethodSignature *sig, *csig, *invoke_sig; + MonoMethodBuilder *mb; + MonoMethod *res, *invoke; + MonoMarshalSpec **mspecs; + MonoMethodPInvoke piinfo; + GHashTable *cache; + int i; + EmitMarshalContext m; + + g_assert (method != NULL); + error_init (error); + + if (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) { + mono_error_set_invalid_program (error, "Failed because method (%s) marked PInvokeCallback (managed method) and extern (unmanaged) simultaneously.", mono_method_full_name (method, TRUE)); + return NULL; + } + + /* + * FIXME: Should cache the method+delegate type pair, since the same method + * could be called with different delegates, thus different marshalling + * options. + */ + cache = get_cache (&mono_method_get_wrapper_cache (method)->managed_wrapper_cache, mono_aligned_addr_hash, NULL); + + if (!target_handle && (res = mono_marshal_find_in_cache (cache, method))) + return res; + + invoke = mono_get_delegate_invoke (delegate_klass); + invoke_sig = mono_method_signature (invoke); + + mspecs = g_new0 (MonoMarshalSpec*, mono_method_signature (invoke)->param_count + 1); + mono_method_get_marshal_info (invoke, mspecs); + + sig = mono_method_signature (method); + + mb = mono_mb_new (method->klass, method->name, MONO_WRAPPER_NATIVE_TO_MANAGED); + + /*the target gchandle must be the first entry after size and the wrapper itself.*/ + mono_mb_add_data (mb, GUINT_TO_POINTER (target_handle)); + + /* we copy the signature, so that we can modify it */ + if (target_handle) + /* Need to free this later */ + csig = mono_metadata_signature_dup (invoke_sig); + else + csig = mono_metadata_signature_dup_full (method->klass->image, invoke_sig); + csig->hasthis = 0; + csig->pinvoke = 1; + + memset (&m, 0, sizeof (m)); + m.mb = mb; + m.sig = sig; + m.piinfo = NULL; + m.retobj_var = 0; + m.csig = csig; + m.image = method->klass->image; + + mono_marshal_set_callconv_from_modopt (invoke, csig); + + /* The attribute is only available in Net 2.0 */ + if (mono_class_try_get_unmanaged_function_pointer_attribute_class ()) { + MonoCustomAttrInfo *cinfo; + MonoCustomAttrEntry *attr; + + /* + * The pinvoke attributes are stored in a real custom attribute. Obtain the + * contents of the attribute without constructing it, as that might not be + * possible when running in cross-compiling mode. + */ + cinfo = mono_custom_attrs_from_class_checked (delegate_klass, error); + mono_error_assert_ok (error); + attr = NULL; + if (cinfo) { + for (i = 0; i < cinfo->num_attrs; ++i) { + MonoClass *ctor_class = cinfo->attrs [i].ctor->klass; + if (mono_class_has_parent (ctor_class, mono_class_try_get_unmanaged_function_pointer_attribute_class ())) { + attr = &cinfo->attrs [i]; + break; + } + } + } + if (attr) { + MonoArray *typed_args, *named_args; + CattrNamedArg *arginfo; + MonoObject *o; + gint32 call_conv; + gint32 charset = 0; + MonoBoolean set_last_error = 0; + MonoError error; + + mono_reflection_create_custom_attr_data_args (mono_defaults.corlib, attr->ctor, attr->data, attr->data_size, &typed_args, &named_args, &arginfo, &error); + g_assert (mono_error_ok (&error)); + g_assert (mono_array_length (typed_args) == 1); + + /* typed args */ + o = mono_array_get (typed_args, MonoObject*, 0); + call_conv = *(gint32*)mono_object_unbox (o); + + /* named args */ + for (i = 0; i < mono_array_length (named_args); ++i) { + CattrNamedArg *narg = &arginfo [i]; + + o = mono_array_get (named_args, MonoObject*, i); + + g_assert (narg->field); + if (!strcmp (narg->field->name, "CharSet")) { + charset = *(gint32*)mono_object_unbox (o); + } else if (!strcmp (narg->field->name, "SetLastError")) { + set_last_error = *(MonoBoolean*)mono_object_unbox (o); + } else if (!strcmp (narg->field->name, "BestFitMapping")) { + // best_fit_mapping = *(MonoBoolean*)mono_object_unbox (o); + } else if (!strcmp (narg->field->name, "ThrowOnUnmappableChar")) { + // throw_on_unmappable = *(MonoBoolean*)mono_object_unbox (o); + } else { + g_assert_not_reached (); + } + } + + g_free (arginfo); + + memset (&piinfo, 0, sizeof (piinfo)); + m.piinfo = &piinfo; + piinfo.piflags = (call_conv << 8) | (charset ? (charset - 1) * 2 : 1) | set_last_error; + + csig->call_convention = call_conv - 1; + } + + if (cinfo && !cinfo->cached) + mono_custom_attrs_free (cinfo); + } + + mono_marshal_emit_managed_wrapper (mb, invoke_sig, mspecs, &m, method, target_handle); + + if (!target_handle) { + WrapperInfo *info; + + // FIXME: Associate it with the method+delegate_klass pair + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); + info->d.native_to_managed.method = method; + info->d.native_to_managed.klass = delegate_klass; + + res = mono_mb_create_and_cache_full (cache, method, + mb, csig, sig->param_count + 16, + info, NULL); + } else { +#ifdef ENABLE_ILGEN + mb->dynamic = TRUE; +#endif + res = mono_mb_create (mb, csig, sig->param_count + 16, NULL); + } + mono_mb_free (mb); + + for (i = mono_method_signature (invoke)->param_count; i >= 0; i--) + if (mspecs [i]) + mono_metadata_free_marshal_spec (mspecs [i]); + g_free (mspecs); + + /* mono_method_print_code (res); */ + + return res; +} + +gpointer +mono_marshal_get_vtfixup_ftnptr (MonoImage *image, guint32 token, guint16 type) +{ + MonoError error; + MonoMethod *method; + MonoMethodSignature *sig; + MonoMethodBuilder *mb; + int i, param_count; + + g_assert (token); + + method = mono_get_method_checked (image, token, NULL, NULL, &error); + if (!method) + g_error ("Could not load vtfixup token 0x%x due to %s", token, mono_error_get_message (&error)); + g_assert (method); + + if (type & (VTFIXUP_TYPE_FROM_UNMANAGED | VTFIXUP_TYPE_FROM_UNMANAGED_RETAIN_APPDOMAIN)) { + MonoMethodSignature *csig; + MonoMarshalSpec **mspecs; + EmitMarshalContext m; + + sig = mono_method_signature (method); + g_assert (!sig->hasthis); + + mspecs = g_new0 (MonoMarshalSpec*, sig->param_count + 1); + mono_method_get_marshal_info (method, mspecs); + + mb = mono_mb_new (method->klass, method->name, MONO_WRAPPER_NATIVE_TO_MANAGED); + csig = mono_metadata_signature_dup_full (image, sig); + csig->hasthis = 0; + csig->pinvoke = 1; + + memset (&m, 0, sizeof (m)); + m.mb = mb; + m.sig = sig; + m.piinfo = NULL; + m.retobj_var = 0; + m.csig = csig; + m.image = image; + + mono_marshal_set_callconv_from_modopt (method, csig); + + /* FIXME: Implement VTFIXUP_TYPE_FROM_UNMANAGED_RETAIN_APPDOMAIN. */ + + mono_marshal_emit_managed_wrapper (mb, sig, mspecs, &m, method, 0); + +#ifdef ENABLE_ILGEN + mb->dynamic = TRUE; +#endif + method = mono_mb_create (mb, csig, sig->param_count + 16, NULL); + mono_mb_free (mb); + + for (i = sig->param_count; i >= 0; i--) + if (mspecs [i]) + mono_metadata_free_marshal_spec (mspecs [i]); + g_free (mspecs); + + gpointer compiled_ptr = mono_compile_method_checked (method, &error); + mono_error_assert_ok (&error); + return compiled_ptr; + } + + sig = mono_method_signature (method); + mb = mono_mb_new (method->klass, method->name, MONO_WRAPPER_MANAGED_TO_MANAGED); + + param_count = sig->param_count + sig->hasthis; +#ifdef ENABLE_ILGEN + for (i = 0; i < param_count; i++) + mono_mb_emit_ldarg (mb, i); + + if (type & VTFIXUP_TYPE_CALL_MOST_DERIVED) + mono_mb_emit_op (mb, CEE_CALLVIRT, method); + else + mono_mb_emit_op (mb, CEE_CALL, method); + mono_mb_emit_byte (mb, CEE_RET); + + mb->dynamic = TRUE; +#endif + + method = mono_mb_create (mb, sig, param_count, NULL); + mono_mb_free (mb); + + gpointer compiled_ptr = mono_compile_method_checked (method, &error); + mono_error_assert_ok (&error); + return compiled_ptr; +} + +#ifdef ENABLE_ILGEN + +/* + * The code directly following this is the cache hit, value positive branch + * + * This function takes a new method builder with 0 locals and adds two locals + * to create multiple out-branches and the fall through state of having the object + * on the stack after a cache miss + */ +static void +generate_check_cache (int obj_arg_position, int class_arg_position, int cache_arg_position, // In-parameters + int *null_obj, int *cache_hit_neg, int *cache_hit_pos, // Out-parameters + MonoMethodBuilder *mb) +{ + int cache_miss_pos; + + /* allocate local 0 (pointer) obj_vtable */ + mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + /* allocate local 1 (pointer) cached_vtable */ + mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + + /*if (!obj)*/ + mono_mb_emit_ldarg (mb, obj_arg_position); + *null_obj = mono_mb_emit_branch (mb, CEE_BRFALSE); + + /*obj_vtable = obj->vtable;*/ + mono_mb_emit_ldarg (mb, obj_arg_position); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoObject, vtable)); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_stloc (mb, 0); + + /* cached_vtable = *cache*/ + mono_mb_emit_ldarg (mb, cache_arg_position); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_stloc (mb, 1); + + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_byte (mb, CEE_LDC_I4); + mono_mb_emit_i4 (mb, ~0x1); + mono_mb_emit_byte (mb, CEE_CONV_I); + mono_mb_emit_byte (mb, CEE_AND); + mono_mb_emit_ldloc (mb, 0); + /*if ((cached_vtable & ~0x1)== obj_vtable)*/ + cache_miss_pos = mono_mb_emit_branch (mb, CEE_BNE_UN); + + /*return (cached_vtable & 0x1) ? NULL : obj;*/ + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_byte(mb, CEE_LDC_I4_1); + mono_mb_emit_byte (mb, CEE_CONV_U); + mono_mb_emit_byte (mb, CEE_AND); + *cache_hit_neg = mono_mb_emit_branch (mb, CEE_BRTRUE); + *cache_hit_pos = mono_mb_emit_branch (mb, CEE_BR); + + // slow path + mono_mb_patch_branch (mb, cache_miss_pos); + + // if isinst + mono_mb_emit_ldarg (mb, obj_arg_position); + mono_mb_emit_ldarg (mb, class_arg_position); + mono_mb_emit_ldarg (mb, cache_arg_position); + mono_mb_emit_icall (mb, mono_marshal_isinst_with_cache); +} + +#endif /* ENABLE_ILGEN */ + +/** + * mono_marshal_get_castclass_with_cache: + * This does the equivalent of \c mono_object_castclass_with_cache. + */ +MonoMethod * +mono_marshal_get_castclass_with_cache (void) +{ + static MonoMethod *cached; + MonoMethod *res; + MonoMethodBuilder *mb; + MonoMethodSignature *sig; + int return_null_pos, positive_cache_hit_pos, negative_cache_hit_pos, invalid_cast_pos; + WrapperInfo *info; + + const int obj_arg_position = 0; + const int class_arg_position = 1; + const int cache_arg_position = 2; + + if (cached) + return cached; + + mb = mono_mb_new (mono_defaults.object_class, "__castclass_with_cache", MONO_WRAPPER_CASTCLASS); + sig = mono_metadata_signature_alloc (mono_defaults.corlib, 3); + sig->params [obj_arg_position] = &mono_defaults.object_class->byval_arg; + sig->params [class_arg_position] = &mono_defaults.int_class->byval_arg; + sig->params [cache_arg_position] = &mono_defaults.int_class->byval_arg; + sig->ret = &mono_defaults.object_class->byval_arg; + sig->pinvoke = 0; + +#ifdef ENABLE_ILGEN + generate_check_cache (obj_arg_position, class_arg_position, cache_arg_position, + &return_null_pos, &negative_cache_hit_pos, &positive_cache_hit_pos, mb); + invalid_cast_pos = mono_mb_emit_branch (mb, CEE_BRFALSE); + + /*return obj;*/ + mono_mb_patch_branch (mb, positive_cache_hit_pos); + mono_mb_emit_ldarg (mb, obj_arg_position); + mono_mb_emit_byte (mb, CEE_RET); + + /*fails*/ + mono_mb_patch_branch (mb, negative_cache_hit_pos); + mono_mb_patch_branch (mb, invalid_cast_pos); + mono_mb_emit_exception (mb, "InvalidCastException", NULL); + + /*return null*/ + mono_mb_patch_branch (mb, return_null_pos); + mono_mb_emit_byte (mb, CEE_LDNULL); + mono_mb_emit_byte (mb, CEE_RET); +#endif /* ENABLE_ILGEN */ + + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_CASTCLASS_WITH_CACHE); + res = mono_mb_create (mb, sig, 8, info); + STORE_STORE_FENCE; + + if (mono_atomic_cas_ptr ((volatile gpointer *)&cached, res, NULL)) { + mono_free_method (res); + mono_metadata_free_method_signature (sig); + } + mono_mb_free (mb); + + return cached; +} + +/* this is an icall */ +static MonoObject * +mono_marshal_isinst_with_cache (MonoObject *obj, MonoClass *klass, uintptr_t *cache) +{ + MonoError error; + MonoObject *isinst = mono_object_isinst_checked (obj, klass, &error); + if (mono_error_set_pending_exception (&error)) + return NULL; + + if (mono_object_is_transparent_proxy (obj)) + return isinst; + + uintptr_t cache_update = (uintptr_t)obj->vtable; + if (!isinst) + cache_update = cache_update | 0x1; + + *cache = cache_update; + + return isinst; +} + +/** + * mono_marshal_get_isinst_with_cache: + * This does the equivalent of \c mono_marshal_isinst_with_cache. + */ +MonoMethod * +mono_marshal_get_isinst_with_cache (void) +{ + static MonoMethod *cached; + MonoMethod *res; + MonoMethodBuilder *mb; + MonoMethodSignature *sig; + int return_null_pos, positive_cache_hit_pos, negative_cache_hit_pos; + WrapperInfo *info; + + const int obj_arg_position = 0; + const int class_arg_position = 1; + const int cache_arg_position = 2; + + if (cached) + return cached; + + mb = mono_mb_new (mono_defaults.object_class, "__isinst_with_cache", MONO_WRAPPER_CASTCLASS); + sig = mono_metadata_signature_alloc (mono_defaults.corlib, 3); + // The object + sig->params [obj_arg_position] = &mono_defaults.object_class->byval_arg; + // The class + sig->params [class_arg_position] = &mono_defaults.int_class->byval_arg; + // The cache + sig->params [cache_arg_position] = &mono_defaults.int_class->byval_arg; + sig->ret = &mono_defaults.object_class->byval_arg; + sig->pinvoke = 0; + +#ifdef ENABLE_ILGEN + generate_check_cache (obj_arg_position, class_arg_position, cache_arg_position, + &return_null_pos, &negative_cache_hit_pos, &positive_cache_hit_pos, mb); + // Return the object gotten via the slow path. + mono_mb_emit_byte (mb, CEE_RET); + + // return NULL; + mono_mb_patch_branch (mb, negative_cache_hit_pos); + mono_mb_patch_branch (mb, return_null_pos); + mono_mb_emit_byte (mb, CEE_LDNULL); + mono_mb_emit_byte (mb, CEE_RET); + + // return obj + mono_mb_patch_branch (mb, positive_cache_hit_pos); + mono_mb_emit_ldarg (mb, obj_arg_position); + mono_mb_emit_byte (mb, CEE_RET); +#endif + + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_ISINST_WITH_CACHE); + res = mono_mb_create (mb, sig, 8, info); + STORE_STORE_FENCE; + + if (mono_atomic_cas_ptr ((volatile gpointer *)&cached, res, NULL)) { + mono_free_method (res); + mono_metadata_free_method_signature (sig); + } + mono_mb_free (mb); + + return cached; +} + +/** + * mono_marshal_get_struct_to_ptr: + * \param klass \c MonoClass + * + * Generates IL code for StructureToPtr (object structure, IntPtr ptr, bool fDeleteOld) + */ +MonoMethod * +mono_marshal_get_struct_to_ptr (MonoClass *klass) +{ + MonoMethodBuilder *mb; + static MonoMethod *stoptr = NULL; + MonoMethod *res; + WrapperInfo *info; + + g_assert (klass != NULL); + + mono_marshal_load_type_info (klass); + + MonoMarshalType *marshal_info = mono_class_get_marshal_info (klass); + if (marshal_info->str_to_ptr) + return marshal_info->str_to_ptr; + + if (!stoptr) + stoptr = mono_class_get_method_from_name (mono_defaults.marshal_class, "StructureToPtr", 3); + g_assert (stoptr); + + mb = mono_mb_new (klass, stoptr->name, MONO_WRAPPER_UNKNOWN); + +#ifdef ENABLE_ILGEN + if (klass->blittable) { + mono_mb_emit_byte (mb, CEE_LDARG_1); + mono_mb_emit_byte (mb, CEE_LDARG_0); + mono_mb_emit_ldflda (mb, sizeof (MonoObject)); + mono_mb_emit_icon (mb, mono_class_value_size (klass, NULL)); + mono_mb_emit_byte (mb, CEE_PREFIX1); + mono_mb_emit_byte (mb, CEE_CPBLK); + } else { + + /* allocate local 0 (pointer) src_ptr */ + mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + /* allocate local 1 (pointer) dst_ptr */ + mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + /* allocate local 2 (boolean) delete_old */ + mono_mb_add_local (mb, &mono_defaults.boolean_class->byval_arg); + mono_mb_emit_byte (mb, CEE_LDARG_2); + mono_mb_emit_stloc (mb, 2); + + /* initialize src_ptr to point to the start of object data */ + mono_mb_emit_byte (mb, CEE_LDARG_0); + mono_mb_emit_ldflda (mb, sizeof (MonoObject)); + mono_mb_emit_stloc (mb, 0); + + /* initialize dst_ptr */ + mono_mb_emit_byte (mb, CEE_LDARG_1); + mono_mb_emit_stloc (mb, 1); + + emit_struct_conv (mb, klass, FALSE); + } + + mono_mb_emit_byte (mb, CEE_RET); +#endif + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_STRUCTURE_TO_PTR); + res = mono_mb_create (mb, mono_signature_no_pinvoke (stoptr), 0, info); + mono_mb_free (mb); + + mono_marshal_lock (); + if (!marshal_info->str_to_ptr) + marshal_info->str_to_ptr = res; + else + res = marshal_info->str_to_ptr; + mono_marshal_unlock (); + return res; +} + +/** + * mono_marshal_get_ptr_to_struct: + * \param klass \c MonoClass + * Generates IL code for PtrToStructure (IntPtr src, object structure) + */ +MonoMethod * +mono_marshal_get_ptr_to_struct (MonoClass *klass) +{ + MonoMethodBuilder *mb; + static MonoMethodSignature *ptostr = NULL; + MonoMethod *res; + WrapperInfo *info; + + g_assert (klass != NULL); + + mono_marshal_load_type_info (klass); + + MonoMarshalType *marshal_info = mono_class_get_marshal_info (klass); + if (marshal_info->ptr_to_str) + return marshal_info->ptr_to_str; + + if (!ptostr) { + MonoMethodSignature *sig; + + /* Create the signature corresponding to + static void PtrToStructure (IntPtr ptr, object structure); + defined in class/corlib/System.Runtime.InteropServices/Marshal.cs */ + sig = mono_create_icall_signature ("void ptr object"); + sig = mono_metadata_signature_dup_full (mono_defaults.corlib, sig); + sig->pinvoke = 0; + mono_memory_barrier (); + ptostr = sig; + } + + mb = mono_mb_new (klass, "PtrToStructure", MONO_WRAPPER_UNKNOWN); + +#ifdef ENABLE_ILGEN + if (klass->blittable) { + mono_mb_emit_byte (mb, CEE_LDARG_1); + mono_mb_emit_ldflda (mb, sizeof (MonoObject)); + mono_mb_emit_byte (mb, CEE_LDARG_0); + mono_mb_emit_icon (mb, mono_class_value_size (klass, NULL)); + mono_mb_emit_byte (mb, CEE_PREFIX1); + mono_mb_emit_byte (mb, CEE_CPBLK); + } else { + + /* allocate local 0 (pointer) src_ptr */ + mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + /* allocate local 1 (pointer) dst_ptr */ + mono_mb_add_local (mb, &klass->this_arg); + + /* initialize src_ptr to point to the start of object data */ + mono_mb_emit_byte (mb, CEE_LDARG_0); + mono_mb_emit_stloc (mb, 0); + + /* initialize dst_ptr */ + mono_mb_emit_byte (mb, CEE_LDARG_1); + mono_mb_emit_op (mb, CEE_UNBOX, klass); + mono_mb_emit_stloc (mb, 1); + + emit_struct_conv (mb, klass, TRUE); + } + + mono_mb_emit_byte (mb, CEE_RET); +#endif + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_PTR_TO_STRUCTURE); + res = mono_mb_create (mb, ptostr, 0, info); + mono_mb_free (mb); + + mono_marshal_lock (); + if (!marshal_info->ptr_to_str) + marshal_info->ptr_to_str = res; + else + res = marshal_info->ptr_to_str; + mono_marshal_unlock (); + return res; +} + +/* + * Return a dummy wrapper for METHOD which is called by synchronized wrappers. + * This is used to avoid infinite recursion since it is hard to determine where to + * replace a method with its synchronized wrapper, and where not. + * The runtime should execute METHOD instead of the wrapper. + */ +MonoMethod * +mono_marshal_get_synchronized_inner_wrapper (MonoMethod *method) +{ + MonoMethodBuilder *mb; + WrapperInfo *info; + MonoMethodSignature *sig; + MonoMethod *res; + MonoGenericContext *ctx = NULL; + MonoGenericContainer *container = NULL; + + if (method->is_inflated && !mono_method_get_context (method)->method_inst) { + ctx = &((MonoMethodInflated*)method)->context; + method = ((MonoMethodInflated*)method)->declaring; + container = mono_method_get_generic_container (method); + if (!container) + container = mono_class_try_get_generic_container (method->klass); //FIXME is this a case of a try? + g_assert (container); + } + + mb = mono_mb_new (method->klass, method->name, MONO_WRAPPER_UNKNOWN); +#ifdef ENABLE_ILGEN + mono_mb_emit_exception_full (mb, "System", "ExecutionEngineException", "Shouldn't be called."); + mono_mb_emit_byte (mb, CEE_RET); +#endif + sig = mono_metadata_signature_dup_full (method->klass->image, mono_method_signature (method)); + + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_SYNCHRONIZED_INNER); + info->d.synchronized_inner.method = method; + res = mono_mb_create (mb, sig, 0, info); + mono_mb_free (mb); + if (ctx) { + MonoError error; + res = mono_class_inflate_generic_method_checked (res, ctx, &error); + g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */ + } + return res; +} + +/** + * mono_marshal_get_synchronized_wrapper: + * Generates IL code for the synchronized wrapper: the generated method + * calls \p method while locking \c this or the parent type. + */ +MonoMethod * +mono_marshal_get_synchronized_wrapper (MonoMethod *method) +{ + static MonoMethod *enter_method, *exit_method, *gettypefromhandle_method; + MonoMethodSignature *sig; + MonoExceptionClause *clause; + MonoMethodBuilder *mb; + MonoMethod *res; + GHashTable *cache; + WrapperInfo *info; + int i, pos, pos2, this_local, taken_local, ret_local = 0; + MonoGenericContext *ctx = NULL; + MonoMethod *orig_method = NULL; + MonoGenericContainer *container = NULL; + + g_assert (method); + + if (method->wrapper_type == MONO_WRAPPER_SYNCHRONIZED) + return method; + + /* FIXME: Support generic methods too */ + if (method->is_inflated && !mono_method_get_context (method)->method_inst) { + orig_method = method; + ctx = &((MonoMethodInflated*)method)->context; + method = ((MonoMethodInflated*)method)->declaring; + container = mono_method_get_generic_container (method); + if (!container) + container = mono_class_try_get_generic_container (method->klass); //FIXME is this a case of a try? + g_assert (container); + } + + /* + * Check cache + */ + if (ctx) { + cache = get_cache (&((MonoMethodInflated*)orig_method)->owner->wrapper_caches.synchronized_cache, mono_aligned_addr_hash, NULL); + res = check_generic_wrapper_cache (cache, orig_method, orig_method, method); + if (res) + return res; + } else { + cache = get_cache (&method->klass->image->wrapper_caches.synchronized_cache, mono_aligned_addr_hash, NULL); + if ((res = mono_marshal_find_in_cache (cache, method))) + return res; + } + + sig = mono_metadata_signature_dup_full (method->klass->image, mono_method_signature (method)); + sig->pinvoke = 0; + + mb = mono_mb_new (method->klass, method->name, MONO_WRAPPER_SYNCHRONIZED); + + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); + info->d.synchronized.method = method; + +#ifdef ENABLE_ILGEN + mb->skip_visibility = 1; + /* result */ + if (!MONO_TYPE_IS_VOID (sig->ret)) + ret_local = mono_mb_add_local (mb, sig->ret); +#endif + + if (method->klass->valuetype && !(method->flags & MONO_METHOD_ATTR_STATIC)) { + /* FIXME Is this really the best way to signal an error here? Isn't this called much later after class setup? -AK */ + mono_class_set_type_load_failure (method->klass, ""); +#ifdef ENABLE_ILGEN + /* This will throw the type load exception when the wrapper is compiled */ + mono_mb_emit_byte (mb, CEE_LDNULL); + mono_mb_emit_op (mb, CEE_ISINST, method->klass); + mono_mb_emit_byte (mb, CEE_POP); + + if (!MONO_TYPE_IS_VOID (sig->ret)) + mono_mb_emit_ldloc (mb, ret_local); + mono_mb_emit_byte (mb, CEE_RET); +#endif + + res = mono_mb_create_and_cache_full (cache, method, + mb, sig, sig->param_count + 16, info, NULL); + mono_mb_free (mb); + + return res; + } + +#ifdef ENABLE_ILGEN + /* this */ + this_local = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + taken_local = mono_mb_add_local (mb, &mono_defaults.boolean_class->byval_arg); + + clause = (MonoExceptionClause *)mono_image_alloc0 (method->klass->image, sizeof (MonoExceptionClause)); + clause->flags = MONO_EXCEPTION_CLAUSE_FINALLY; +#endif + + mono_marshal_lock (); + + if (!enter_method) { + MonoMethodDesc *desc; + + desc = mono_method_desc_new ("Monitor:Enter(object,bool&)", FALSE); + enter_method = mono_method_desc_search_in_class (desc, mono_defaults.monitor_class); + g_assert (enter_method); + mono_method_desc_free (desc); + + desc = mono_method_desc_new ("Monitor:Exit", FALSE); + exit_method = mono_method_desc_search_in_class (desc, mono_defaults.monitor_class); + g_assert (exit_method); + mono_method_desc_free (desc); + + desc = mono_method_desc_new ("Type:GetTypeFromHandle", FALSE); + gettypefromhandle_method = mono_method_desc_search_in_class (desc, mono_defaults.systemtype_class); + g_assert (gettypefromhandle_method); + mono_method_desc_free (desc); + } + + mono_marshal_unlock (); + +#ifdef ENABLE_ILGEN + /* Push this or the type object */ + if (method->flags & METHOD_ATTRIBUTE_STATIC) { + /* We have special handling for this in the JIT */ + int index = mono_mb_add_data (mb, method->klass); + mono_mb_add_data (mb, mono_defaults.typehandle_class); + mono_mb_emit_byte (mb, CEE_LDTOKEN); + mono_mb_emit_i4 (mb, index); + + mono_mb_emit_managed_call (mb, gettypefromhandle_method, NULL); + } + else + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_stloc (mb, this_local); + + /* Call Monitor::Enter() */ + mono_mb_emit_ldloc (mb, this_local); + mono_mb_emit_ldloc_addr (mb, taken_local); + mono_mb_emit_managed_call (mb, enter_method, NULL); + + clause->try_offset = mono_mb_get_label (mb); + + /* Call the method */ + if (sig->hasthis) + mono_mb_emit_ldarg (mb, 0); + for (i = 0; i < sig->param_count; i++) + mono_mb_emit_ldarg (mb, i + (sig->hasthis == TRUE)); + + if (ctx) { + MonoError error; + mono_mb_emit_managed_call (mb, mono_class_inflate_generic_method_checked (method, &container->context, &error), NULL); + g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */ + } else { + mono_mb_emit_managed_call (mb, method, NULL); + } + + if (!MONO_TYPE_IS_VOID (sig->ret)) + mono_mb_emit_stloc (mb, ret_local); + + pos = mono_mb_emit_branch (mb, CEE_LEAVE); + + clause->try_len = mono_mb_get_pos (mb) - clause->try_offset; + clause->handler_offset = mono_mb_get_label (mb); + + /* Call Monitor::Exit() if needed */ + mono_mb_emit_ldloc (mb, taken_local); + pos2 = mono_mb_emit_branch (mb, CEE_BRFALSE); + mono_mb_emit_ldloc (mb, this_local); + mono_mb_emit_managed_call (mb, exit_method, NULL); + mono_mb_patch_branch (mb, pos2); + mono_mb_emit_byte (mb, CEE_ENDFINALLY); + + clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset; + + mono_mb_patch_branch (mb, pos); + if (!MONO_TYPE_IS_VOID (sig->ret)) + mono_mb_emit_ldloc (mb, ret_local); + mono_mb_emit_byte (mb, CEE_RET); + + mono_mb_set_clauses (mb, 1, clause); +#endif + + if (ctx) { + MonoMethod *def; + def = mono_mb_create_and_cache_full (cache, method, mb, sig, sig->param_count + 16, info, NULL); + res = cache_generic_wrapper (cache, orig_method, def, ctx, orig_method); + } else { + res = mono_mb_create_and_cache_full (cache, method, + mb, sig, sig->param_count + 16, info, NULL); + } + mono_mb_free (mb); + + return res; +} + + +/** + * mono_marshal_get_unbox_wrapper: + * The returned method calls \p method unboxing the \c this argument. + */ +MonoMethod * +mono_marshal_get_unbox_wrapper (MonoMethod *method) +{ + MonoMethodSignature *sig = mono_method_signature (method); + int i; + MonoMethodBuilder *mb; + MonoMethod *res; + GHashTable *cache; + WrapperInfo *info; + + cache = get_cache (&mono_method_get_wrapper_cache (method)->unbox_wrapper_cache, mono_aligned_addr_hash, NULL); + + if ((res = mono_marshal_find_in_cache (cache, method))) + return res; + + mb = mono_mb_new (method->klass, method->name, MONO_WRAPPER_UNBOX); + + g_assert (sig->hasthis); + +#ifdef ENABLE_ILGEN + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_icon (mb, sizeof (MonoObject)); + mono_mb_emit_byte (mb, CEE_ADD); + for (i = 0; i < sig->param_count; ++i) + mono_mb_emit_ldarg (mb, i + 1); + mono_mb_emit_managed_call (mb, method, NULL); + mono_mb_emit_byte (mb, CEE_RET); +#endif + + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); + info->d.unbox.method = method; + + res = mono_mb_create_and_cache_full (cache, method, + mb, sig, sig->param_count + 16, info, NULL); + mono_mb_free (mb); + + /* mono_method_print_code (res); */ + + return res; +} + +enum { + STELEMREF_OBJECT, /*no check at all*/ + STELEMREF_SEALED_CLASS, /*check vtable->klass->element_type */ + STELEMREF_CLASS, /*only the klass->parents check*/ + STELEMREF_CLASS_SMALL_IDEPTH, /* like STELEMREF_CLASS bit without the idepth check */ + STELEMREF_INTERFACE, /*interfaces without variant generic arguments. */ + STELEMREF_COMPLEX, /*arrays, MBR or types with variant generic args - go straight to icalls*/ + STELEMREF_KIND_COUNT +}; + +static const char *strelemref_wrapper_name[] = { + "object", "sealed_class", "class", "class_small_idepth", "interface", "complex" +}; + +static gboolean +is_monomorphic_array (MonoClass *klass) +{ + MonoClass *element_class; + if (klass->rank != 1) + return FALSE; + + element_class = klass->element_class; + return mono_class_is_sealed (element_class) || element_class->valuetype; +} + +static int +get_virtual_stelemref_kind (MonoClass *element_class) +{ + if (element_class == mono_defaults.object_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 + return STELEMREF_COMPLEX; +#else + return STELEMREF_INTERFACE; +#endif + /*Arrays are sealed but are covariant on their element type, We can't use any of the fast paths.*/ + if (mono_class_is_marshalbyref (element_class) || element_class->rank || mono_class_has_variant_generic_params (element_class)) + return STELEMREF_COMPLEX; + if (mono_class_is_sealed (element_class)) + return STELEMREF_SEALED_CLASS; + if (element_class->idepth <= MONO_DEFAULT_SUPERTABLE_SIZE) + return STELEMREF_CLASS_SMALL_IDEPTH; + + return STELEMREF_CLASS; +} + +#ifdef ENABLE_ILGEN + +static void +load_array_element_address (MonoMethodBuilder *mb) +{ + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_op (mb, CEE_LDELEMA, mono_defaults.object_class); +} + +static void +load_array_class (MonoMethodBuilder *mb, int aklass) +{ + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoObject, vtable)); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoVTable, klass)); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoClass, element_class)); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_stloc (mb, aklass); +} + +static void +load_value_class (MonoMethodBuilder *mb, int vklass) +{ + mono_mb_emit_ldarg (mb, 2); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoObject, vtable)); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoVTable, klass)); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_stloc (mb, vklass); +} +#endif + +#if 0 +static void +record_slot_vstore (MonoObject *array, size_t index, MonoObject *value) +{ + char *name = mono_type_get_full_name (array->vtable->klass->element_class); + printf ("slow vstore of %s\n", name); + g_free (name); +} +#endif + +/* + * TODO: + * - Separate simple interfaces from variant interfaces or mbr types. This way we can avoid the icall for them. + * - Emit a (new) mono bytecode that produces OP_COND_EXC_NE_UN to raise ArrayTypeMismatch + * - Maybe mve some MonoClass field into the vtable to reduce the number of loads + * - Add a case for arrays of arrays. + */ +static MonoMethod* +get_virtual_stelemref_wrapper (int kind) +{ + static MonoMethod *cached_methods [STELEMREF_KIND_COUNT] = { NULL }; /*object iface sealed regular*/ + static MonoMethodSignature *signature; + MonoMethodBuilder *mb; + MonoMethod *res; + char *name; + const char *param_names [16]; + guint32 b1, b2, b3, b4; + int aklass, vklass, vtable, uiid; + int array_slot_addr; + WrapperInfo *info; + + if (cached_methods [kind]) + return cached_methods [kind]; + + name = g_strdup_printf ("virt_stelemref_%s", strelemref_wrapper_name [kind]); + mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_STELEMREF); + g_free (name); + + if (!signature) { + MonoMethodSignature *sig = mono_metadata_signature_alloc (mono_defaults.corlib, 2); + + /* void this::stelemref (size_t idx, void* value) */ + sig->ret = &mono_defaults.void_class->byval_arg; + sig->hasthis = TRUE; + sig->params [0] = &mono_defaults.int_class->byval_arg; /* this is a natural sized int */ + sig->params [1] = &mono_defaults.object_class->byval_arg; + signature = sig; + } + +#ifdef ENABLE_ILGEN + param_names [0] = "index"; + param_names [1] = "value"; + mono_mb_set_param_names (mb, param_names); + + /*For now simply call plain old stelemref*/ + switch (kind) { + case STELEMREF_OBJECT: + /* ldelema (implicit bound check) */ + load_array_element_address (mb); + /* do_store */ + mono_mb_emit_ldarg (mb, 2); + mono_mb_emit_byte (mb, CEE_STIND_REF); + mono_mb_emit_byte (mb, CEE_RET); + break; + + case STELEMREF_COMPLEX: { + int b_fast; + /* + + if (!value) + goto store; + if (!mono_object_isinst (value, aklass)) + goto do_exception; + + do_store: + *array_slot_addr = value; + + do_exception: + throw new ArrayTypeMismatchException (); + */ + + aklass = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + vklass = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + array_slot_addr = mono_mb_add_local (mb, &mono_defaults.object_class->this_arg); + +#if 0 + { + /*Use this to debug/record stores that are going thru the slow path*/ + MonoMethodSignature *csig; + csig = mono_metadata_signature_alloc (mono_defaults.corlib, 3); + csig->ret = &mono_defaults.void_class->byval_arg; + csig->params [0] = &mono_defaults.object_class->byval_arg; + csig->params [1] = &mono_defaults.int_class->byval_arg; /* this is a natural sized int */ + csig->params [2] = &mono_defaults.object_class->byval_arg; + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_ldarg (mb, 2); + mono_mb_emit_native_call (mb, csig, record_slot_vstore); + } +#endif + + /* ldelema (implicit bound check) */ + load_array_element_address (mb); + mono_mb_emit_stloc (mb, array_slot_addr); + + /* if (!value) goto do_store */ + mono_mb_emit_ldarg (mb, 2); + b1 = mono_mb_emit_branch (mb, CEE_BRFALSE); + + /* aklass = array->vtable->klass->element_class */ + load_array_class (mb, aklass); + /* vklass = value->vtable->klass */ + load_value_class (mb, vklass); + + /* fastpath */ + mono_mb_emit_ldloc (mb, vklass); + mono_mb_emit_ldloc (mb, aklass); + b_fast = mono_mb_emit_branch (mb, CEE_BEQ); + + /*if (mono_object_isinst (value, aklass)) */ + mono_mb_emit_ldarg (mb, 2); + mono_mb_emit_ldloc (mb, aklass); + mono_mb_emit_icall (mb, mono_object_isinst_icall); + b2 = mono_mb_emit_branch (mb, CEE_BRFALSE); + + /* do_store: */ + mono_mb_patch_branch (mb, b1); + mono_mb_patch_branch (mb, b_fast); + mono_mb_emit_ldloc (mb, array_slot_addr); + mono_mb_emit_ldarg (mb, 2); + mono_mb_emit_byte (mb, CEE_STIND_REF); + mono_mb_emit_byte (mb, CEE_RET); + + /* do_exception: */ + mono_mb_patch_branch (mb, b2); + + mono_mb_emit_exception (mb, "ArrayTypeMismatchException", NULL); + break; + } + case STELEMREF_SEALED_CLASS: + /* + + if (!value) + goto store; + + aklass = array->vtable->klass->element_class; + vklass = value->vtable->klass; + + if (vklass != aklass) + goto do_exception; + + do_store: + *array_slot_addr = value; + + do_exception: + throw new ArrayTypeMismatchException (); + */ + aklass = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + vklass = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + array_slot_addr = mono_mb_add_local (mb, &mono_defaults.object_class->this_arg); + + /* ldelema (implicit bound check) */ + load_array_element_address (mb); + mono_mb_emit_stloc (mb, array_slot_addr); + + /* if (!value) goto do_store */ + mono_mb_emit_ldarg (mb, 2); + b1 = mono_mb_emit_branch (mb, CEE_BRFALSE); + + /* aklass = array->vtable->klass->element_class */ + load_array_class (mb, aklass); + + /* vklass = value->vtable->klass */ + load_value_class (mb, vklass); + + /*if (vklass != aklass) goto do_exception; */ + mono_mb_emit_ldloc (mb, aklass); + mono_mb_emit_ldloc (mb, vklass); + b2 = mono_mb_emit_branch (mb, CEE_BNE_UN); + + /* do_store: */ + mono_mb_patch_branch (mb, b1); + mono_mb_emit_ldloc (mb, array_slot_addr); + mono_mb_emit_ldarg (mb, 2); + mono_mb_emit_byte (mb, CEE_STIND_REF); + mono_mb_emit_byte (mb, CEE_RET); + + /* do_exception: */ + mono_mb_patch_branch (mb, b2); + mono_mb_emit_exception (mb, "ArrayTypeMismatchException", NULL); + break; + + case STELEMREF_CLASS: { + /* + the method: + + if (!value) + goto do_store; + + aklass = array->vtable->klass->element_class; + vklass = value->vtable->klass; + + if (vklass->idepth < aklass->idepth) + goto do_exception; + + if (vklass->supertypes [aklass->idepth - 1] != aklass) + goto do_exception; + + do_store: + *array_slot_addr = value; + return; + + long: + throw new ArrayTypeMismatchException (); + */ + aklass = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + vklass = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + array_slot_addr = mono_mb_add_local (mb, &mono_defaults.object_class->this_arg); + + /* ldelema (implicit bound check) */ + load_array_element_address (mb); + mono_mb_emit_stloc (mb, array_slot_addr); + + /* if (!value) goto do_store */ + mono_mb_emit_ldarg (mb, 2); + b1 = mono_mb_emit_branch (mb, CEE_BRFALSE); + + /* aklass = array->vtable->klass->element_class */ + load_array_class (mb, aklass); + + /* vklass = value->vtable->klass */ + load_value_class (mb, vklass); + + /* if (vklass->idepth < aklass->idepth) goto failue */ + mono_mb_emit_ldloc (mb, vklass); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoClass, idepth)); + mono_mb_emit_byte (mb, CEE_LDIND_U2); + + mono_mb_emit_ldloc (mb, aklass); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoClass, idepth)); + mono_mb_emit_byte (mb, CEE_LDIND_U2); + + b3 = mono_mb_emit_branch (mb, CEE_BLT_UN); + + /* if (vklass->supertypes [aklass->idepth - 1] != aklass) goto failure */ + mono_mb_emit_ldloc (mb, vklass); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoClass, supertypes)); + mono_mb_emit_byte (mb, CEE_LDIND_I); + + mono_mb_emit_ldloc (mb, aklass); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoClass, idepth)); + mono_mb_emit_byte (mb, CEE_LDIND_U2); + mono_mb_emit_icon (mb, 1); + mono_mb_emit_byte (mb, CEE_SUB); + mono_mb_emit_icon (mb, sizeof (void*)); + mono_mb_emit_byte (mb, CEE_MUL); + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_byte (mb, CEE_LDIND_I); + + mono_mb_emit_ldloc (mb, aklass); + b4 = mono_mb_emit_branch (mb, CEE_BNE_UN); + + /* do_store: */ + mono_mb_patch_branch (mb, b1); + mono_mb_emit_ldloc (mb, array_slot_addr); + mono_mb_emit_ldarg (mb, 2); + mono_mb_emit_byte (mb, CEE_STIND_REF); + mono_mb_emit_byte (mb, CEE_RET); + + /* do_exception: */ + mono_mb_patch_branch (mb, b3); + mono_mb_patch_branch (mb, b4); + + mono_mb_emit_exception (mb, "ArrayTypeMismatchException", NULL); + break; + } + + case STELEMREF_CLASS_SMALL_IDEPTH: + /* + the method: + + if (!value) + goto do_store; + + aklass = array->vtable->klass->element_class; + vklass = value->vtable->klass; + + if (vklass->supertypes [aklass->idepth - 1] != aklass) + goto do_exception; + + do_store: + *array_slot_addr = value; + return; + + long: + throw new ArrayTypeMismatchException (); + */ + aklass = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + vklass = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + array_slot_addr = mono_mb_add_local (mb, &mono_defaults.object_class->this_arg); + + /* ldelema (implicit bound check) */ + load_array_element_address (mb); + mono_mb_emit_stloc (mb, array_slot_addr); + + /* if (!value) goto do_store */ + mono_mb_emit_ldarg (mb, 2); + b1 = mono_mb_emit_branch (mb, CEE_BRFALSE); + + /* aklass = array->vtable->klass->element_class */ + load_array_class (mb, aklass); + + /* vklass = value->vtable->klass */ + load_value_class (mb, vklass); + + /* if (vklass->supertypes [aklass->idepth - 1] != aklass) goto failure */ + mono_mb_emit_ldloc (mb, vklass); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoClass, supertypes)); + mono_mb_emit_byte (mb, CEE_LDIND_I); + + mono_mb_emit_ldloc (mb, aklass); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoClass, idepth)); + mono_mb_emit_byte (mb, CEE_LDIND_U2); + mono_mb_emit_icon (mb, 1); + mono_mb_emit_byte (mb, CEE_SUB); + mono_mb_emit_icon (mb, sizeof (void*)); + mono_mb_emit_byte (mb, CEE_MUL); + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_byte (mb, CEE_LDIND_I); + + mono_mb_emit_ldloc (mb, aklass); + b4 = mono_mb_emit_branch (mb, CEE_BNE_UN); + + /* do_store: */ + mono_mb_patch_branch (mb, b1); + mono_mb_emit_ldloc (mb, array_slot_addr); + mono_mb_emit_ldarg (mb, 2); + mono_mb_emit_byte (mb, CEE_STIND_REF); + mono_mb_emit_byte (mb, CEE_RET); + + /* do_exception: */ + mono_mb_patch_branch (mb, b4); + + mono_mb_emit_exception (mb, "ArrayTypeMismatchException", NULL); + break; + + case STELEMREF_INTERFACE: + /*Mono *klass; + MonoVTable *vt; + unsigned uiid; + if (value == NULL) + goto store; + + klass = array->obj.vtable->klass->element_class; + vt = value->vtable; + uiid = klass->interface_id; + if (uiid > vt->max_interface_id) + goto exception; + if (!(vt->interface_bitmap [(uiid) >> 3] & (1 << ((uiid)&7)))) + goto exception; + store: + mono_array_setref (array, index, value); + return; + exception: + mono_raise_exception (mono_get_exception_array_type_mismatch ());*/ + + array_slot_addr = mono_mb_add_local (mb, &mono_defaults.object_class->this_arg); + aklass = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + vtable = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + uiid = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg); + + /* ldelema (implicit bound check) */ + load_array_element_address (mb); + mono_mb_emit_stloc (mb, array_slot_addr); + + /* if (!value) goto do_store */ + mono_mb_emit_ldarg (mb, 2); + b1 = mono_mb_emit_branch (mb, CEE_BRFALSE); + + /* klass = array->vtable->klass->element_class */ + load_array_class (mb, aklass); + + /* vt = value->vtable */ + mono_mb_emit_ldarg (mb, 2); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoObject, vtable)); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_stloc (mb, vtable); + + /* uiid = klass->interface_id; */ + mono_mb_emit_ldloc (mb, aklass); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoClass, interface_id)); + mono_mb_emit_byte (mb, CEE_LDIND_U4); + mono_mb_emit_stloc (mb, uiid); + + /*if (uiid > vt->max_interface_id)*/ + mono_mb_emit_ldloc (mb, uiid); + mono_mb_emit_ldloc (mb, vtable); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoVTable, max_interface_id)); + mono_mb_emit_byte (mb, CEE_LDIND_U4); + b2 = mono_mb_emit_branch (mb, CEE_BGT_UN); + + /* if (!(vt->interface_bitmap [(uiid) >> 3] & (1 << ((uiid)&7)))) */ + + /*vt->interface_bitmap*/ + mono_mb_emit_ldloc (mb, vtable); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoVTable, interface_bitmap)); + mono_mb_emit_byte (mb, CEE_LDIND_I); + + /*uiid >> 3*/ + mono_mb_emit_ldloc (mb, uiid); + mono_mb_emit_icon (mb, 3); + mono_mb_emit_byte (mb, CEE_SHR_UN); + + /*vt->interface_bitmap [(uiid) >> 3]*/ + mono_mb_emit_byte (mb, CEE_ADD); /*interface_bitmap is a guint8 array*/ + mono_mb_emit_byte (mb, CEE_LDIND_U1); + + /*(1 << ((uiid)&7)))*/ + mono_mb_emit_icon (mb, 1); + mono_mb_emit_ldloc (mb, uiid); + mono_mb_emit_icon (mb, 7); + mono_mb_emit_byte (mb, CEE_AND); + mono_mb_emit_byte (mb, CEE_SHL); + + /*bitwise and the whole thing*/ + mono_mb_emit_byte (mb, CEE_AND); + b3 = mono_mb_emit_branch (mb, CEE_BRFALSE); + + /* do_store: */ + mono_mb_patch_branch (mb, b1); + mono_mb_emit_ldloc (mb, array_slot_addr); + mono_mb_emit_ldarg (mb, 2); + mono_mb_emit_byte (mb, CEE_STIND_REF); + mono_mb_emit_byte (mb, CEE_RET); + + /* do_exception: */ + mono_mb_patch_branch (mb, b2); + mono_mb_patch_branch (mb, b3); + mono_mb_emit_exception (mb, "ArrayTypeMismatchException", NULL); + break; + + default: + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_ldarg (mb, 2); + mono_mb_emit_managed_call (mb, mono_marshal_get_stelemref (), NULL); + mono_mb_emit_byte (mb, CEE_RET); + g_assert (0); + } +#endif /* ENABLE_ILGEN */ + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_VIRTUAL_STELEMREF); + info->d.virtual_stelemref.kind = kind; + res = mono_mb_create (mb, signature, 4, info); + res->flags |= METHOD_ATTRIBUTE_VIRTUAL; + + mono_marshal_lock (); + if (!cached_methods [kind]) { + cached_methods [kind] = res; + mono_marshal_unlock (); + } else { + mono_marshal_unlock (); + mono_free_method (res); + } + + mono_mb_free (mb); + return cached_methods [kind]; +} + +MonoMethod* +mono_marshal_get_virtual_stelemref (MonoClass *array_class) +{ + int kind; + + g_assert (array_class->rank == 1); + kind = get_virtual_stelemref_kind (array_class->element_class); + + return get_virtual_stelemref_wrapper (kind); +} + +MonoMethod** +mono_marshal_get_virtual_stelemref_wrappers (int *nwrappers) +{ + MonoMethod **res; + int i; + + *nwrappers = STELEMREF_KIND_COUNT; + res = (MonoMethod **)g_malloc0 (STELEMREF_KIND_COUNT * sizeof (MonoMethod*)); + for (i = 0; i < STELEMREF_KIND_COUNT; ++i) + res [i] = get_virtual_stelemref_wrapper (i); + return res; +} + +/** + * mono_marshal_get_stelemref: + */ +MonoMethod* +mono_marshal_get_stelemref (void) +{ + static MonoMethod* ret = NULL; + MonoMethodSignature *sig; + MonoMethodBuilder *mb; + WrapperInfo *info; + + guint32 b1, b2, b3, b4; + guint32 copy_pos; + int aklass, vklass; + int array_slot_addr; + + if (ret) + return ret; + + mb = mono_mb_new (mono_defaults.object_class, "stelemref", MONO_WRAPPER_STELEMREF); + + + sig = mono_metadata_signature_alloc (mono_defaults.corlib, 3); + + /* void stelemref (void* array, int idx, void* value) */ + sig->ret = &mono_defaults.void_class->byval_arg; + sig->params [0] = &mono_defaults.object_class->byval_arg; + sig->params [1] = &mono_defaults.int_class->byval_arg; /* this is a natural sized int */ + sig->params [2] = &mono_defaults.object_class->byval_arg; + +#ifdef ENABLE_ILGEN + aklass = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + vklass = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + array_slot_addr = mono_mb_add_local (mb, &mono_defaults.object_class->this_arg); + + /* + the method: + + if (!value) + goto store; + + aklass = array->vtable->klass->element_class; + vklass = value->vtable->klass; + + if (vklass->idepth < aklass->idepth) + goto long; + + if (vklass->supertypes [aklass->idepth - 1] != aklass) + goto long; + + store: + *array_slot_addr = value; + return; + + long: + if (mono_object_isinst (value, aklass)) + goto store; + + throw new ArrayTypeMismatchException (); + */ + + /* ldelema (implicit bound check) */ + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_op (mb, CEE_LDELEMA, mono_defaults.object_class); + mono_mb_emit_stloc (mb, array_slot_addr); + + /* if (!value) goto do_store */ + mono_mb_emit_ldarg (mb, 2); + b1 = mono_mb_emit_branch (mb, CEE_BRFALSE); + + /* aklass = array->vtable->klass->element_class */ + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoObject, vtable)); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoVTable, klass)); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoClass, element_class)); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_stloc (mb, aklass); + + /* vklass = value->vtable->klass */ + mono_mb_emit_ldarg (mb, 2); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoObject, vtable)); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoVTable, klass)); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_stloc (mb, vklass); + + /* if (vklass->idepth < aklass->idepth) goto failue */ + mono_mb_emit_ldloc (mb, vklass); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoClass, idepth)); + mono_mb_emit_byte (mb, CEE_LDIND_U2); + + mono_mb_emit_ldloc (mb, aklass); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoClass, idepth)); + mono_mb_emit_byte (mb, CEE_LDIND_U2); + + b2 = mono_mb_emit_branch (mb, CEE_BLT_UN); + + /* if (vklass->supertypes [aklass->idepth - 1] != aklass) goto failure */ + mono_mb_emit_ldloc (mb, vklass); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoClass, supertypes)); + mono_mb_emit_byte (mb, CEE_LDIND_I); + + mono_mb_emit_ldloc (mb, aklass); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoClass, idepth)); + mono_mb_emit_byte (mb, CEE_LDIND_U2); + mono_mb_emit_icon (mb, 1); + mono_mb_emit_byte (mb, CEE_SUB); + mono_mb_emit_icon (mb, sizeof (void*)); + mono_mb_emit_byte (mb, CEE_MUL); + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_byte (mb, CEE_LDIND_I); + + mono_mb_emit_ldloc (mb, aklass); + + b3 = mono_mb_emit_branch (mb, CEE_BNE_UN); + + copy_pos = mono_mb_get_label (mb); + /* do_store */ + mono_mb_patch_branch (mb, b1); + mono_mb_emit_ldloc (mb, array_slot_addr); + mono_mb_emit_ldarg (mb, 2); + mono_mb_emit_byte (mb, CEE_STIND_REF); + + mono_mb_emit_byte (mb, CEE_RET); + + /* the hard way */ + mono_mb_patch_branch (mb, b2); + mono_mb_patch_branch (mb, b3); + + mono_mb_emit_ldarg (mb, 2); + mono_mb_emit_ldloc (mb, aklass); + mono_mb_emit_icall (mb, mono_object_isinst_icall); + + b4 = mono_mb_emit_branch (mb, CEE_BRTRUE); + mono_mb_patch_addr (mb, b4, copy_pos - (b4 + 4)); + mono_mb_emit_exception (mb, "ArrayTypeMismatchException", NULL); + + mono_mb_emit_byte (mb, CEE_RET); +#endif + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); + ret = mono_mb_create (mb, sig, 4, info); + mono_mb_free (mb); + + return ret; +} + +/* + * mono_marshal_get_gsharedvt_in_wrapper: + * + * This wrapper handles calls from normal code to gsharedvt code. + */ +MonoMethod* +mono_marshal_get_gsharedvt_in_wrapper (void) +{ + static MonoMethod* ret = NULL; + MonoMethodSignature *sig; + MonoMethodBuilder *mb; + WrapperInfo *info; + + if (ret) + return ret; + + mb = mono_mb_new (mono_defaults.object_class, "gsharedvt_in", MONO_WRAPPER_UNKNOWN); + + sig = mono_metadata_signature_alloc (mono_defaults.corlib, 0); + sig->ret = &mono_defaults.void_class->byval_arg; + +#ifdef ENABLE_ILGEN + /* + * The body is generated by the JIT, we use a wrapper instead of a trampoline so EH works. + */ + mono_mb_emit_byte (mb, CEE_RET); +#endif + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_GSHAREDVT_IN); + ret = mono_mb_create (mb, sig, 4, info); + mono_mb_free (mb); + + return ret; +} + +/* + * mono_marshal_get_gsharedvt_out_wrapper: + * + * This wrapper handles calls from gsharedvt code to normal code. + */ +MonoMethod* +mono_marshal_get_gsharedvt_out_wrapper (void) +{ + static MonoMethod* ret = NULL; + MonoMethodSignature *sig; + MonoMethodBuilder *mb; + WrapperInfo *info; + + if (ret) + return ret; + + mb = mono_mb_new (mono_defaults.object_class, "gsharedvt_out", MONO_WRAPPER_UNKNOWN); + + sig = mono_metadata_signature_alloc (mono_defaults.corlib, 0); + sig->ret = &mono_defaults.void_class->byval_arg; + +#ifdef ENABLE_ILGEN + /* + * The body is generated by the JIT, we use a wrapper instead of a trampoline so EH works. + */ + mono_mb_emit_byte (mb, CEE_RET); +#endif + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_GSHAREDVT_OUT); + ret = mono_mb_create (mb, sig, 4, info); + mono_mb_free (mb); + + return ret; +} + +typedef struct { + int rank; + int elem_size; + MonoMethod *method; +} ArrayElemAddr; + +/* LOCKING: vars accessed under the marshal lock */ +static ArrayElemAddr *elem_addr_cache = NULL; +static int elem_addr_cache_size = 0; +static int elem_addr_cache_next = 0; + +/** + * mono_marshal_get_array_address: + * \param rank rank of the array type + * \param elem_size size in bytes of an element of an array. + * + * Returns a MonoMethod that implements the code to get the address + * of an element in a multi-dimenasional array of \p rank dimensions. + * The returned method takes an array as the first argument and then + * \p rank indexes for the \p rank dimensions. + * If ELEM_SIZE is 0, read the array size from the array object. + */ +MonoMethod* +mono_marshal_get_array_address (int rank, int elem_size) +{ + MonoMethod *ret; + MonoMethodBuilder *mb; + MonoMethodSignature *sig; + WrapperInfo *info; + char *name; + int i, bounds, ind, realidx; + int branch_pos, *branch_positions; + int cached; + + ret = NULL; + mono_marshal_lock (); + for (i = 0; i < elem_addr_cache_next; ++i) { + if (elem_addr_cache [i].rank == rank && elem_addr_cache [i].elem_size == elem_size) { + ret = elem_addr_cache [i].method; + break; + } + } + mono_marshal_unlock (); + if (ret) + return ret; + + branch_positions = g_new0 (int, rank); + + sig = mono_metadata_signature_alloc (mono_defaults.corlib, 1 + rank); + + /* void* address (void* array, int idx0, int idx1, int idx2, ...) */ + sig->ret = &mono_defaults.int_class->byval_arg; + sig->params [0] = &mono_defaults.object_class->byval_arg; + for (i = 0; i < rank; ++i) { + sig->params [i + 1] = &mono_defaults.int32_class->byval_arg; + } + + name = g_strdup_printf ("ElementAddr_%d", elem_size); + mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_MANAGED_TO_MANAGED); + g_free (name); + +#ifdef ENABLE_ILGEN + bounds = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + ind = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg); + realidx = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg); + + /* bounds = array->bounds; */ + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoArray, bounds)); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_stloc (mb, bounds); + + /* ind is the overall element index, realidx is the partial index in a single dimension */ + /* ind = idx0 - bounds [0].lower_bound */ + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_ldloc (mb, bounds); + mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoArrayBounds, lower_bound)); + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_byte (mb, CEE_LDIND_I4); + mono_mb_emit_byte (mb, CEE_SUB); + mono_mb_emit_stloc (mb, ind); + /* if (ind >= bounds [0].length) goto exeception; */ + mono_mb_emit_ldloc (mb, ind); + mono_mb_emit_ldloc (mb, bounds); + mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoArrayBounds, length)); + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_byte (mb, CEE_LDIND_I4); + /* note that we use unsigned comparison */ + branch_pos = mono_mb_emit_branch (mb, CEE_BGE_UN); + + /* For large ranks (> 4?) use a loop n IL later to reduce code size. + * We could also decide to ignore the passed elem_size and get it + * from the array object, to reduce the number of methods we generate: + * the additional cost is 3 memory loads and a non-immediate mul. + */ + for (i = 1; i < rank; ++i) { + /* realidx = idxi - bounds [i].lower_bound */ + mono_mb_emit_ldarg (mb, 1 + i); + mono_mb_emit_ldloc (mb, bounds); + mono_mb_emit_icon (mb, (i * sizeof (MonoArrayBounds)) + MONO_STRUCT_OFFSET (MonoArrayBounds, lower_bound)); + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_byte (mb, CEE_LDIND_I4); + mono_mb_emit_byte (mb, CEE_SUB); + mono_mb_emit_stloc (mb, realidx); + /* if (realidx >= bounds [i].length) goto exeception; */ + mono_mb_emit_ldloc (mb, realidx); + mono_mb_emit_ldloc (mb, bounds); + mono_mb_emit_icon (mb, (i * sizeof (MonoArrayBounds)) + MONO_STRUCT_OFFSET (MonoArrayBounds, length)); + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_byte (mb, CEE_LDIND_I4); + branch_positions [i] = mono_mb_emit_branch (mb, CEE_BGE_UN); + /* ind = ind * bounds [i].length + realidx */ + mono_mb_emit_ldloc (mb, ind); + mono_mb_emit_ldloc (mb, bounds); + mono_mb_emit_icon (mb, (i * sizeof (MonoArrayBounds)) + MONO_STRUCT_OFFSET (MonoArrayBounds, length)); + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_byte (mb, CEE_LDIND_I4); + mono_mb_emit_byte (mb, CEE_MUL); + mono_mb_emit_ldloc (mb, realidx); + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_stloc (mb, ind); + } + + /* return array->vector + ind * element_size */ + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoArray, vector)); + mono_mb_emit_ldloc (mb, ind); + if (elem_size) { + mono_mb_emit_icon (mb, elem_size); + } else { + /* Load arr->vtable->klass->sizes.element_class */ + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_byte (mb, CEE_CONV_I); + mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoObject, vtable)); + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoVTable, klass)); + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_byte (mb, CEE_LDIND_I); + /* sizes is an union, so this reads sizes.element_size */ + mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoClass, sizes)); + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_byte (mb, CEE_LDIND_I4); + } + mono_mb_emit_byte (mb, CEE_MUL); + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_byte (mb, CEE_RET); + + /* patch the branches to get here and throw */ + for (i = 1; i < rank; ++i) { + mono_mb_patch_branch (mb, branch_positions [i]); + } + mono_mb_patch_branch (mb, branch_pos); + /* throw exception */ + mono_mb_emit_exception (mb, "IndexOutOfRangeException", NULL); + + g_free (branch_positions); +#endif /* ENABLE_ILGEN */ + + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_ELEMENT_ADDR); + info->d.element_addr.rank = rank; + info->d.element_addr.elem_size = elem_size; + ret = mono_mb_create (mb, sig, 4, info); + mono_mb_free (mb); + + /* cache the result */ + cached = 0; + mono_marshal_lock (); + for (i = 0; i < elem_addr_cache_next; ++i) { + if (elem_addr_cache [i].rank == rank && elem_addr_cache [i].elem_size == elem_size) { + /* FIXME: free ret */ + ret = elem_addr_cache [i].method; + cached = TRUE; + break; + } + } + if (!cached) { + if (elem_addr_cache_next >= elem_addr_cache_size) { + int new_size = elem_addr_cache_size + 4; + ArrayElemAddr *new_array = g_new0 (ArrayElemAddr, new_size); + memcpy (new_array, elem_addr_cache, elem_addr_cache_size * sizeof (ArrayElemAddr)); + g_free (elem_addr_cache); + elem_addr_cache = new_array; + elem_addr_cache_size = new_size; + } + elem_addr_cache [elem_addr_cache_next].rank = rank; + elem_addr_cache [elem_addr_cache_next].elem_size = elem_size; + elem_addr_cache [elem_addr_cache_next].method = ret; + elem_addr_cache_next ++; + } + mono_marshal_unlock (); + return ret; +} + +/* + * mono_marshal_get_array_accessor_wrapper: + * + * Return a wrapper which just calls METHOD, which should be an Array Get/Set/Address method. + */ +MonoMethod * +mono_marshal_get_array_accessor_wrapper (MonoMethod *method) +{ + MonoMethodSignature *sig; + MonoMethodBuilder *mb; + MonoMethod *res; + GHashTable *cache; + int i; + MonoGenericContext *ctx = NULL; + MonoMethod *orig_method = NULL; + MonoGenericContainer *container = NULL; + WrapperInfo *info; + + /* + * These wrappers are needed to avoid the JIT replacing the calls to these methods with intrinsics + * inside runtime invoke wrappers, thereby making the wrappers not unshareable. + * FIXME: Use generic methods. + */ + /* + * Check cache + */ + if (ctx) { + cache = NULL; + g_assert_not_reached (); + } else { + cache = get_cache (&method->klass->image->array_accessor_cache, mono_aligned_addr_hash, NULL); + if ((res = mono_marshal_find_in_cache (cache, method))) + return res; + } + + sig = mono_metadata_signature_dup_full (method->klass->image, mono_method_signature (method)); + sig->pinvoke = 0; + + mb = mono_mb_new (method->klass, method->name, MONO_WRAPPER_UNKNOWN); + +#ifdef ENABLE_ILGEN + /* Call the method */ + if (sig->hasthis) + mono_mb_emit_ldarg (mb, 0); + for (i = 0; i < sig->param_count; i++) + mono_mb_emit_ldarg (mb, i + (sig->hasthis == TRUE)); + + if (ctx) { + MonoError error; + mono_mb_emit_managed_call (mb, mono_class_inflate_generic_method_checked (method, &container->context, &error), NULL); + g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */ + } else { + mono_mb_emit_managed_call (mb, method, NULL); + } + mono_mb_emit_byte (mb, CEE_RET); +#endif + + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_ARRAY_ACCESSOR); + info->d.array_accessor.method = method; + + if (ctx) { + MonoMethod *def; + def = mono_mb_create_and_cache_full (cache, method, mb, sig, sig->param_count + 16, info, NULL); + res = cache_generic_wrapper (cache, orig_method, def, ctx, orig_method); + } else { + res = mono_mb_create_and_cache_full (cache, method, + mb, sig, sig->param_count + 16, + info, NULL); + } + mono_mb_free (mb); + + return res; +} + +#ifndef HOST_WIN32 +static inline void* +mono_marshal_alloc_co_task_mem (size_t size) +{ + if ((gulong)size == 0) + /* This returns a valid pointer for size 0 on MS.NET */ + size = 4; + + return g_try_malloc ((gulong)size); +} +#endif + +/** + * mono_marshal_alloc: + */ +void* +mono_marshal_alloc (gsize size, MonoError *error) +{ + gpointer res; + + error_init (error); + + res = mono_marshal_alloc_co_task_mem (size); + if (!res) + mono_error_set_out_of_memory (error, "Could not allocate %lu bytes", size); + + return res; +} + +/* This is a JIT icall, it sets the pending exception and returns NULL on error. */ +static void* +ves_icall_marshal_alloc (gsize size) +{ + MonoError error; + void *ret = mono_marshal_alloc (size, &error); + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + return NULL; + } + + return ret; +} + +#ifndef HOST_WIN32 +static inline void +mono_marshal_free_co_task_mem (void *ptr) +{ + g_free (ptr); + return; +} +#endif + +/** + * mono_marshal_free: + */ +void +mono_marshal_free (gpointer ptr) +{ + mono_marshal_free_co_task_mem (ptr); +} + +/** + * mono_marshal_free_array: + */ +void +mono_marshal_free_array (gpointer *ptr, int size) +{ + int i; + + if (!ptr) + return; + + for (i = 0; i < size; i++) + if (ptr [i]) + g_free (ptr [i]); +} + +void * +mono_marshal_string_to_utf16 (MonoString *s) +{ + return s ? mono_string_chars (s) : NULL; +} + +/* This is a JIT icall, it sets the pending exception and returns NULL on error. */ +static void * +mono_marshal_string_to_utf16_copy (MonoString *s) +{ + if (s == NULL) { + return NULL; + } else { + MonoError error; + gunichar2 *res = (gunichar2 *)mono_marshal_alloc ((mono_string_length (s) * 2) + 2, &error); + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + return NULL; + } + memcpy (res, mono_string_chars (s), mono_string_length (s) * 2); + res [mono_string_length (s)] = 0; + return res; + } +} + +/** + * mono_marshal_set_last_error: + * + * This function is invoked to set the last error value from a P/Invoke call + * which has \c SetLastError set. + */ +void +mono_marshal_set_last_error (void) +{ + /* This icall is called just after a P/Invoke call before the P/Invoke + * wrapper transitions the runtime back to running mode. */ + MONO_REQ_GC_SAFE_MODE; +#ifdef WIN32 + mono_native_tls_set_value (last_error_tls_id, GINT_TO_POINTER (GetLastError ())); +#else + mono_native_tls_set_value (last_error_tls_id, GINT_TO_POINTER (errno)); +#endif +} + +static void +mono_marshal_set_last_error_windows (int error) +{ +#ifdef WIN32 + /* This icall is called just after a P/Invoke call before the P/Invoke + * wrapper transitions the runtime back to running mode. */ + MONO_REQ_GC_SAFE_MODE; + mono_native_tls_set_value (last_error_tls_id, GINT_TO_POINTER (error)); +#endif +} + +void +ves_icall_System_Runtime_InteropServices_Marshal_copy_to_unmanaged (MonoArray *src, gint32 start_index, + gpointer dest, gint32 length) +{ + int element_size; + void *source_addr; + + MONO_CHECK_ARG_NULL (src,); + MONO_CHECK_ARG_NULL (dest,); + + if (src->obj.vtable->klass->rank != 1) { + mono_set_pending_exception (mono_get_exception_argument ("array", "array is multi-dimensional")); + return; + } + if (start_index < 0) { + mono_set_pending_exception (mono_get_exception_argument ("startIndex", "Must be >= 0")); + return; + } + if (length < 0) { + mono_set_pending_exception (mono_get_exception_argument ("length", "Must be >= 0")); + return; + } + if (start_index + length > mono_array_length (src)) { + mono_set_pending_exception (mono_get_exception_argument ("length", "start_index + length > array length")); + return; + } + + element_size = mono_array_element_size (src->obj.vtable->klass); + + /* no references should be involved */ + source_addr = mono_array_addr_with_size_fast (src, element_size, start_index); + + memcpy (dest, source_addr, length * element_size); +} + +void +ves_icall_System_Runtime_InteropServices_Marshal_copy_from_unmanaged (gpointer src, gint32 start_index, + MonoArray *dest, gint32 length) +{ + int element_size; + void *dest_addr; + + MONO_CHECK_ARG_NULL (src,); + MONO_CHECK_ARG_NULL (dest,); + + if (dest->obj.vtable->klass->rank != 1) { + mono_set_pending_exception (mono_get_exception_argument ("array", "array is multi-dimensional")); + return; + } + if (start_index < 0) { + mono_set_pending_exception (mono_get_exception_argument ("startIndex", "Must be >= 0")); + return; + } + if (length < 0) { + mono_set_pending_exception (mono_get_exception_argument ("length", "Must be >= 0")); + return; + } + if (start_index + length > mono_array_length (dest)) { + mono_set_pending_exception (mono_get_exception_argument ("length", "start_index + length > array length")); + return; + } + element_size = mono_array_element_size (dest->obj.vtable->klass); + + /* no references should be involved */ + dest_addr = mono_array_addr_with_size_fast (dest, element_size, start_index); + + memcpy (dest_addr, src, length * element_size); +} + +MonoStringHandle +ves_icall_System_Runtime_InteropServices_Marshal_PtrToStringAnsi (char *ptr, MonoError *error) +{ + error_init (error); + if (ptr == NULL) + return MONO_HANDLE_CAST (MonoString, NULL_HANDLE); + else + return mono_string_new_handle (mono_domain_get (), ptr, error); +} + +MonoString * +ves_icall_System_Runtime_InteropServices_Marshal_PtrToStringAnsi_len (char *ptr, gint32 len) +{ + MonoError error; + MonoString *result = NULL; + error_init (&error); + if (ptr == NULL) + mono_error_set_argument_null (&error, "ptr", ""); + else + result = mono_string_new_len_checked (mono_domain_get (), ptr, len, &error); + mono_error_set_pending_exception (&error); + return result; +} + +MonoString * +ves_icall_System_Runtime_InteropServices_Marshal_PtrToStringUni (guint16 *ptr) +{ + MonoError error; + MonoString *res = NULL; + MonoDomain *domain = mono_domain_get (); + int len = 0; + guint16 *t = ptr; + + if (ptr == NULL) + return NULL; + + while (*t++) + len++; + + res = mono_string_new_utf16_checked (domain, ptr, len, &error); + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + return NULL; + } + return res; +} + +MonoString * +ves_icall_System_Runtime_InteropServices_Marshal_PtrToStringUni_len (guint16 *ptr, gint32 len) +{ + MonoError error; + MonoString *res = NULL; + MonoDomain *domain = mono_domain_get (); + + error_init (&error); + + if (ptr == NULL) { + res = NULL; + mono_error_set_argument_null (&error, "ptr", ""); + } else { + res = mono_string_new_utf16_checked (domain, ptr, len, &error); + } + + if (!mono_error_ok (&error)) + mono_error_set_pending_exception (&error); + return res; +} + +guint32 +ves_icall_System_Runtime_InteropServices_Marshal_GetLastWin32Error (void) +{ + return (GPOINTER_TO_INT (mono_native_tls_get_value (last_error_tls_id))); +} + +guint32 +ves_icall_System_Runtime_InteropServices_Marshal_SizeOf (MonoReflectionTypeHandle rtype, MonoError *error) +{ + MonoClass *klass; + MonoType *type; + guint32 layout; + + error_init (error); + + if (MONO_HANDLE_IS_NULL (rtype)) { + mono_error_set_argument_null (error, "type", ""); + return 0; + } + + type = MONO_HANDLE_GETVAL (rtype, type); + klass = mono_class_from_mono_type (type); + if (!mono_class_init (klass)) { + mono_error_set_for_class_failure (error, klass); + return 0; + } + + layout = (mono_class_get_flags (klass) & TYPE_ATTRIBUTE_LAYOUT_MASK); + + if (type->type == MONO_TYPE_PTR || type->type == MONO_TYPE_FNPTR) { + return sizeof (gpointer); + } else if (layout == TYPE_ATTRIBUTE_AUTO_LAYOUT) { + mono_error_set_argument (error, "t", "Type %s cannot be marshaled as an unmanaged structure.", klass->name); + return 0; + } + + return mono_class_native_size (klass, NULL); +} + +void +ves_icall_System_Runtime_InteropServices_Marshal_StructureToPtr (MonoObject *obj, gpointer dst, MonoBoolean delete_old) +{ + MonoError error; + MonoMethod *method; + gpointer pa [3]; + + MONO_CHECK_ARG_NULL (obj,); + MONO_CHECK_ARG_NULL (dst,); + + method = mono_marshal_get_struct_to_ptr (obj->vtable->klass); + + pa [0] = obj; + pa [1] = &dst; + pa [2] = &delete_old; + + mono_runtime_invoke_checked (method, NULL, pa, &error); + if (!mono_error_ok (&error)) + mono_error_set_pending_exception (&error); +} + +static void +ptr_to_structure (gpointer src, MonoObject *dst, MonoError *error) +{ + MonoMethod *method; + gpointer pa [2]; + + error_init (error); + + method = mono_marshal_get_ptr_to_struct (dst->vtable->klass); + + pa [0] = &src; + pa [1] = dst; + + mono_runtime_invoke_checked (method, NULL, pa, error); +} + +void +ves_icall_System_Runtime_InteropServices_Marshal_PtrToStructure (gpointer src, MonoObject *dst) +{ + MonoType *t; + MonoError error; + + MONO_CHECK_ARG_NULL (src,); + MONO_CHECK_ARG_NULL (dst,); + + t = mono_type_get_underlying_type (mono_class_get_type (dst->vtable->klass)); + + if (t->type == MONO_TYPE_VALUETYPE) { + MonoException *exc; + gchar *tmp; + + tmp = g_strdup_printf ("Destination is a boxed value type."); + exc = mono_get_exception_argument ("dst", tmp); + g_free (tmp); + + mono_set_pending_exception (exc); + return; + } + + ptr_to_structure (src, dst, &error); + if (!mono_error_ok (&error)) + mono_error_set_pending_exception (&error); +} + +MonoObject * +ves_icall_System_Runtime_InteropServices_Marshal_PtrToStructure_type (gpointer src, MonoReflectionType *type) +{ + MonoError error; + MonoClass *klass; + MonoDomain *domain = mono_domain_get (); + MonoObject *res; + + if (src == NULL) + return NULL; + MONO_CHECK_ARG_NULL (type, NULL); + + klass = mono_class_from_mono_type (type->type); + if (!mono_class_init (klass)) { + mono_set_pending_exception (mono_class_get_exception_for_failure (klass)); + return NULL; + } + + res = mono_object_new_checked (domain, klass, &error); + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + return NULL; + } + + ptr_to_structure (src, res, &error); + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + return NULL; + } + + return res; +} + +int +ves_icall_System_Runtime_InteropServices_Marshal_OffsetOf (MonoReflectionTypeHandle ref_type, MonoStringHandle field_name, MonoError *error) +{ + error_init (error); + if (MONO_HANDLE_IS_NULL (ref_type)) { + mono_error_set_argument_null (error, "type", ""); + return 0; + } + if (MONO_HANDLE_IS_NULL (field_name)) { + mono_error_set_argument_null (error, "fieldName", ""); + return 0; + } + + char *fname = mono_string_handle_to_utf8 (field_name, error); + return_val_if_nok (error, 0); + + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + MonoClass *klass = mono_class_from_mono_type (type); + if (!mono_class_init (klass)) { + mono_error_set_for_class_failure (error, klass); + return 0; + } + + int match_index = -1; + while (klass && match_index == -1) { + MonoClassField* field; + int i = 0; + gpointer iter = NULL; + while ((field = mono_class_get_fields (klass, &iter))) { + if (field->type->attrs & FIELD_ATTRIBUTE_STATIC) + continue; + if (!strcmp (fname, mono_field_get_name (field))) { + match_index = i; + break; + } + i ++; + } + + if (match_index == -1) + klass = klass->parent; + } + + g_free (fname); + + if(match_index == -1) { + /* Get back original class instance */ + klass = mono_class_from_mono_type (type); + + mono_error_set_argument (error, "fieldName", "Field passed in is not a marshaled member of the type %s", klass->name); + return 0; + } + + MonoMarshalType *info = mono_marshal_load_type_info (klass); + return info->fields [match_index].offset; +} + +#ifndef HOST_WIN32 +gpointer +ves_icall_System_Runtime_InteropServices_Marshal_StringToHGlobalAnsi (MonoString *string) +{ + MonoError error; + char *ret = mono_string_to_utf8_checked (string, &error); + mono_error_set_pending_exception (&error); + return ret; +} + +gpointer +ves_icall_System_Runtime_InteropServices_Marshal_StringToHGlobalUni (MonoString *string) +{ + if (string == NULL) + return NULL; + else { + gunichar2 *res = (gunichar2 *)g_malloc ((mono_string_length (string) + 1) * 2); + + memcpy (res, mono_string_chars (string), mono_string_length (string) * 2); + res [mono_string_length (string)] = 0; + return res; + } +} +#endif /* !HOST_WIN32 */ + +static void +mono_struct_delete_old (MonoClass *klass, char *ptr) +{ + MonoMarshalType *info; + int i; + + info = mono_marshal_load_type_info (klass); + + for (i = 0; i < info->num_fields; i++) { + MonoMarshalConv conv; + MonoType *ftype = info->fields [i].field->type; + char *cpos; + + if (ftype->attrs & FIELD_ATTRIBUTE_STATIC) + continue; + + mono_type_to_unmanaged (ftype, info->fields [i].mspec, TRUE, + klass->unicode, &conv); + + cpos = ptr + info->fields [i].offset; + + switch (conv) { + case MONO_MARSHAL_CONV_NONE: + if (MONO_TYPE_ISSTRUCT (ftype)) { + mono_struct_delete_old (ftype->data.klass, cpos); + continue; + } + break; + case MONO_MARSHAL_CONV_STR_LPWSTR: + /* We assume this field points inside a MonoString */ + break; + case MONO_MARSHAL_CONV_STR_LPTSTR: +#ifdef TARGET_WIN32 + /* We assume this field points inside a MonoString + * on Win32 */ + break; +#endif + case MONO_MARSHAL_CONV_STR_LPSTR: + case MONO_MARSHAL_CONV_STR_BSTR: + case MONO_MARSHAL_CONV_STR_ANSIBSTR: + case MONO_MARSHAL_CONV_STR_TBSTR: + case MONO_MARSHAL_CONV_STR_UTF8STR: + mono_marshal_free (*(gpointer *)cpos); + break; + + default: + continue; + } + } +} + +void +ves_icall_System_Runtime_InteropServices_Marshal_DestroyStructure (gpointer src, MonoReflectionType *type) +{ + MonoClass *klass; + + MONO_CHECK_ARG_NULL (src,); + MONO_CHECK_ARG_NULL (type,); + + klass = mono_class_from_mono_type (type->type); + if (!mono_class_init (klass)) { + mono_set_pending_exception (mono_class_get_exception_for_failure (klass)); + return; + } + + mono_struct_delete_old (klass, (char *)src); +} + +#ifndef HOST_WIN32 +static inline void * +mono_marshal_alloc_hglobal (size_t size) +{ + return g_try_malloc (size); +} +#endif + +void* +ves_icall_System_Runtime_InteropServices_Marshal_AllocHGlobal (gpointer size) +{ + gpointer res; + size_t s = (size_t)size; + + if (s == 0) + /* This returns a valid pointer for size 0 on MS.NET */ + s = 4; + + res = mono_marshal_alloc_hglobal (s); + + if (!res) { + mono_set_pending_exception (mono_domain_get ()->out_of_memory_ex); + return NULL; + } + + return res; +} + +#ifndef HOST_WIN32 +static inline gpointer +mono_marshal_realloc_hglobal (gpointer ptr, size_t size) +{ + return g_try_realloc (ptr, size); +} +#endif + +gpointer +ves_icall_System_Runtime_InteropServices_Marshal_ReAllocHGlobal (gpointer ptr, gpointer size) +{ + gpointer res; + size_t s = (size_t)size; + + if (ptr == NULL) { + mono_set_pending_exception (mono_domain_get ()->out_of_memory_ex); + return NULL; + } + + res = mono_marshal_realloc_hglobal (ptr, s); + + if (!res) { + mono_set_pending_exception (mono_domain_get ()->out_of_memory_ex); + return NULL; + } + + return res; +} + +#ifndef HOST_WIN32 +static inline void +mono_marshal_free_hglobal (gpointer ptr) +{ + g_free (ptr); + return; +} +#endif + +void +ves_icall_System_Runtime_InteropServices_Marshal_FreeHGlobal (void *ptr) +{ + mono_marshal_free_hglobal (ptr); +} + +void* +ves_icall_System_Runtime_InteropServices_Marshal_AllocCoTaskMem (int size) +{ + void *res = mono_marshal_alloc_co_task_mem (size); + + if (!res) { + mono_set_pending_exception (mono_domain_get ()->out_of_memory_ex); + return NULL; + } + return res; +} + +void* +ves_icall_System_Runtime_InteropServices_Marshal_AllocCoTaskMemSize (gulong size) +{ + void *res = mono_marshal_alloc_co_task_mem (size); + + if (!res) { + mono_set_pending_exception (mono_domain_get ()->out_of_memory_ex); + return NULL; + } + return res; +} + +void +ves_icall_System_Runtime_InteropServices_Marshal_FreeCoTaskMem (void *ptr) +{ + mono_marshal_free_co_task_mem (ptr); + return; +} + +#ifndef HOST_WIN32 +static inline gpointer +mono_marshal_realloc_co_task_mem (gpointer ptr, size_t size) +{ + return g_try_realloc (ptr, (gulong)size); +} +#endif + +gpointer +ves_icall_System_Runtime_InteropServices_Marshal_ReAllocCoTaskMem (gpointer ptr, int size) +{ + void *res = mono_marshal_realloc_co_task_mem (ptr, size); + + if (!res) { + mono_set_pending_exception (mono_domain_get ()->out_of_memory_ex); + return NULL; + } + return res; +} + +void* +ves_icall_System_Runtime_InteropServices_Marshal_UnsafeAddrOfPinnedArrayElement (MonoArray *arrayobj, int index) +{ + return mono_array_addr_with_size_fast (arrayobj, mono_array_element_size (arrayobj->obj.vtable->klass), index); +} + +MonoDelegateHandle +ves_icall_System_Runtime_InteropServices_Marshal_GetDelegateForFunctionPointerInternal (void *ftn, MonoReflectionTypeHandle type, MonoError *error) +{ + error_init (error); + MonoClass *klass = mono_type_get_class (MONO_HANDLE_GETVAL (type, type)); + if (!mono_class_init (klass)) { + mono_error_set_for_class_failure (error, klass); + return NULL; + } + + return mono_ftnptr_to_delegate_handle (klass, ftn, error); +} + +gpointer +ves_icall_System_Runtime_InteropServices_Marshal_GetFunctionPointerForDelegateInternal (MonoDelegateHandle delegate, MonoError *error) +{ + error_init (error); + return mono_delegate_handle_to_ftnptr (delegate, error); +} + +/** + * mono_marshal_is_loading_type_info: + * + * Return whenever mono_marshal_load_type_info () is being executed for KLASS by this + * thread. + */ +static gboolean +mono_marshal_is_loading_type_info (MonoClass *klass) +{ + GSList *loads_list = (GSList *)mono_native_tls_get_value (load_type_info_tls_id); + + return g_slist_find (loads_list, klass) != NULL; +} + +/** + * mono_marshal_load_type_info: + * + * Initialize \c klass::marshal_info using information from metadata. This function can + * recursively call itself, and the caller is responsible to avoid that by calling + * \c mono_marshal_is_loading_type_info beforehand. + * + * LOCKING: Acquires the loader lock. + */ +MonoMarshalType * +mono_marshal_load_type_info (MonoClass* klass) +{ + int j, count = 0; + guint32 native_size = 0, min_align = 1, packing; + MonoMarshalType *info; + MonoClassField* field; + gpointer iter; + guint32 layout; + GSList *loads_list; + + g_assert (klass != NULL); + + info = mono_class_get_marshal_info (klass); + if (info) + return info; + + if (!klass->inited) + mono_class_init (klass); + + info = mono_class_get_marshal_info (klass); + if (info) + return info; + + /* + * This function can recursively call itself, so we keep the list of classes which are + * under initialization in a TLS list. + */ + g_assert (!mono_marshal_is_loading_type_info (klass)); + loads_list = (GSList *)mono_native_tls_get_value (load_type_info_tls_id); + loads_list = g_slist_prepend (loads_list, klass); + mono_native_tls_set_value (load_type_info_tls_id, loads_list); + + iter = NULL; + while ((field = mono_class_get_fields (klass, &iter))) { + if (field->type->attrs & FIELD_ATTRIBUTE_STATIC) + continue; + if (mono_field_is_deleted (field)) + continue; + count++; + } + + layout = mono_class_get_flags (klass) & TYPE_ATTRIBUTE_LAYOUT_MASK; + + info = (MonoMarshalType *)mono_image_alloc0 (klass->image, MONO_SIZEOF_MARSHAL_TYPE + sizeof (MonoMarshalField) * count); + info->num_fields = count; + + /* Try to find a size for this type in metadata */ + mono_metadata_packing_from_typedef (klass->image, klass->type_token, NULL, &native_size); + + if (klass->parent) { + int parent_size = mono_class_native_size (klass->parent, NULL); + + /* Add parent size to real size */ + native_size += parent_size; + info->native_size = parent_size; + } + + packing = klass->packing_size ? klass->packing_size : 8; + iter = NULL; + j = 0; + while ((field = mono_class_get_fields (klass, &iter))) { + int size; + guint32 align; + + if (field->type->attrs & FIELD_ATTRIBUTE_STATIC) + continue; + + if (mono_field_is_deleted (field)) + continue; + if (field->type->attrs & FIELD_ATTRIBUTE_HAS_FIELD_MARSHAL) + mono_metadata_field_info_with_mempool (klass->image, mono_metadata_token_index (mono_class_get_field_token (field)) - 1, + NULL, NULL, &info->fields [j].mspec); + + info->fields [j].field = field; + + if ((mono_class_num_fields (klass) == 1) && (klass->instance_size == sizeof (MonoObject)) && + (strcmp (mono_field_get_name (field), "$PRIVATE$") == 0)) { + /* This field is a hack inserted by MCS to empty structures */ + continue; + } + + switch (layout) { + case TYPE_ATTRIBUTE_AUTO_LAYOUT: + case TYPE_ATTRIBUTE_SEQUENTIAL_LAYOUT: + size = mono_marshal_type_size (field->type, info->fields [j].mspec, + &align, TRUE, klass->unicode); + align = klass->packing_size ? MIN (klass->packing_size, align): align; + min_align = MAX (align, min_align); + info->fields [j].offset = info->native_size; + info->fields [j].offset += align - 1; + info->fields [j].offset &= ~(align - 1); + info->native_size = info->fields [j].offset + size; + break; + case TYPE_ATTRIBUTE_EXPLICIT_LAYOUT: + size = mono_marshal_type_size (field->type, info->fields [j].mspec, + &align, TRUE, klass->unicode); + min_align = MAX (align, min_align); + info->fields [j].offset = field->offset - sizeof (MonoObject); + info->native_size = MAX (info->native_size, info->fields [j].offset + size); + break; + } + j++; + } + + if (klass->byval_arg.type == MONO_TYPE_PTR) + info->native_size = sizeof (gpointer); + + if (layout != TYPE_ATTRIBUTE_AUTO_LAYOUT) { + info->native_size = MAX (native_size, info->native_size); + /* + * If the provided Size is equal or larger than the calculated size, and there + * was no Pack attribute, we set min_align to 1 to avoid native_size being increased + */ + if (layout == TYPE_ATTRIBUTE_EXPLICIT_LAYOUT) { + if (native_size && native_size == info->native_size && klass->packing_size == 0) + min_align = 1; + else + min_align = MIN (min_align, packing); + } + } + + if (info->native_size & (min_align - 1)) { + info->native_size += min_align - 1; + info->native_size &= ~(min_align - 1); + } + + info->min_align = min_align; + + /* Update the class's blittable info, if the layouts don't match */ + if (info->native_size != mono_class_value_size (klass, NULL)) { + mono_loader_lock (); + klass->blittable = FALSE; + mono_loader_unlock (); + } + + /* If this is an array type, ensure that we have element info */ + if (klass->rank && !mono_marshal_is_loading_type_info (klass->element_class)) { + mono_marshal_load_type_info (klass->element_class); + } + + loads_list = (GSList *)mono_native_tls_get_value (load_type_info_tls_id); + loads_list = g_slist_remove (loads_list, klass); + mono_native_tls_set_value (load_type_info_tls_id, loads_list); + + mono_marshal_lock (); + MonoMarshalType *info2 = mono_class_get_marshal_info (klass); + if (!info2) { + /*We do double-checking locking on marshal_info */ + mono_memory_barrier (); + mono_class_set_marshal_info (klass, info); + ++class_marshal_info_count; + info2 = info; + } + mono_marshal_unlock (); + + return info2; +} + +/** + * mono_class_native_size: + * \param klass a class + * \returns the native size of an object instance (when marshaled + * to unmanaged code) + */ +gint32 +mono_class_native_size (MonoClass *klass, guint32 *align) +{ + MonoMarshalType *info = mono_class_get_marshal_info (klass); + if (!info) { + if (mono_marshal_is_loading_type_info (klass)) { + if (align) + *align = 0; + return 0; + } else { + mono_marshal_load_type_info (klass); + } + info = mono_class_get_marshal_info (klass); + } + + if (align) + *align = info->min_align; + + return info->native_size; +} + +/* + * mono_type_native_stack_size: + * @t: the type to return the size it uses on the stack + * + * Returns: the number of bytes required to hold an instance of this + * type on the native stack + */ +int +mono_type_native_stack_size (MonoType *t, guint32 *align) +{ + guint32 tmp; + + g_assert (t != NULL); + + if (!align) + align = &tmp; + + if (t->byref) { + *align = sizeof (gpointer); + return sizeof (gpointer); + } + + switch (t->type){ + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_CHAR: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + *align = 4; + return 4; + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_STRING: + case MONO_TYPE_OBJECT: + case MONO_TYPE_CLASS: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_PTR: + case MONO_TYPE_FNPTR: + case MONO_TYPE_ARRAY: + *align = sizeof (gpointer); + return sizeof (gpointer); + case MONO_TYPE_R4: + *align = 4; + return 4; + case MONO_TYPE_R8: + *align = MONO_ABI_ALIGNOF (double); + return 8; + case MONO_TYPE_I8: + case MONO_TYPE_U8: + *align = MONO_ABI_ALIGNOF (gint64); + return 8; + case MONO_TYPE_GENERICINST: + if (!mono_type_generic_inst_is_valuetype (t)) { + *align = sizeof (gpointer); + return sizeof (gpointer); + } + /* Fall through */ + case MONO_TYPE_TYPEDBYREF: + case MONO_TYPE_VALUETYPE: { + guint32 size; + MonoClass *klass = mono_class_from_mono_type (t); + + if (klass->enumtype) + return mono_type_native_stack_size (mono_class_enum_basetype (klass), align); + else { + size = mono_class_native_size (klass, align); + *align = *align + 3; + *align &= ~3; + + size += 3; + size &= ~3; + + return size; + } + } + default: + g_error ("type 0x%02x unknown", t->type); + } + return 0; +} + +/** + * mono_marshal_type_size: + */ +gint32 +mono_marshal_type_size (MonoType *type, MonoMarshalSpec *mspec, guint32 *align, + gboolean as_field, gboolean unicode) +{ + MonoMarshalNative native_type = mono_type_to_unmanaged (type, mspec, as_field, unicode, NULL); + MonoClass *klass; + + switch (native_type) { + case MONO_NATIVE_BOOLEAN: + *align = 4; + return 4; + case MONO_NATIVE_I1: + case MONO_NATIVE_U1: + *align = 1; + return 1; + case MONO_NATIVE_I2: + case MONO_NATIVE_U2: + case MONO_NATIVE_VARIANTBOOL: + *align = 2; + return 2; + case MONO_NATIVE_I4: + case MONO_NATIVE_U4: + case MONO_NATIVE_ERROR: + *align = 4; + return 4; + case MONO_NATIVE_I8: + case MONO_NATIVE_U8: + *align = MONO_ABI_ALIGNOF (gint64); + return 8; + case MONO_NATIVE_R4: + *align = 4; + return 4; + case MONO_NATIVE_R8: + *align = MONO_ABI_ALIGNOF (double); + return 8; + case MONO_NATIVE_INT: + case MONO_NATIVE_UINT: + case MONO_NATIVE_LPSTR: + case MONO_NATIVE_LPWSTR: + case MONO_NATIVE_LPTSTR: + case MONO_NATIVE_BSTR: + case MONO_NATIVE_ANSIBSTR: + case MONO_NATIVE_TBSTR: + case MONO_NATIVE_UTF8STR: + case MONO_NATIVE_LPARRAY: + case MONO_NATIVE_SAFEARRAY: + case MONO_NATIVE_IUNKNOWN: + case MONO_NATIVE_IDISPATCH: + case MONO_NATIVE_INTERFACE: + case MONO_NATIVE_ASANY: + case MONO_NATIVE_FUNC: + case MONO_NATIVE_LPSTRUCT: + *align = MONO_ABI_ALIGNOF (gpointer); + return sizeof (gpointer); + case MONO_NATIVE_STRUCT: + klass = mono_class_from_mono_type (type); + if (klass == mono_defaults.object_class && + (mspec && mspec->native == MONO_NATIVE_STRUCT)) { + *align = 16; + return 16; + } + return mono_class_native_size (klass, align); + case MONO_NATIVE_BYVALTSTR: { + int esize = unicode ? 2: 1; + g_assert (mspec); + *align = esize; + return mspec->data.array_data.num_elem * esize; + } + case MONO_NATIVE_BYVALARRAY: { + // FIXME: Have to consider ArraySubType + int esize; + klass = mono_class_from_mono_type (type); + if (klass->element_class == mono_defaults.char_class) { + esize = unicode ? 2 : 1; + *align = esize; + } else { + esize = mono_class_native_size (klass->element_class, align); + } + g_assert (mspec); + return mspec->data.array_data.num_elem * esize; + } + case MONO_NATIVE_CUSTOM: + *align = sizeof (gpointer); + return sizeof (gpointer); + break; + case MONO_NATIVE_CURRENCY: + case MONO_NATIVE_VBBYREFSTR: + default: + g_error ("native type %02x not implemented", native_type); + break; + } + g_assert_not_reached (); + return 0; +} + +/** + * mono_marshal_asany: + * This is a JIT icall, it sets the pending exception and returns NULL on error. + */ +gpointer +mono_marshal_asany (MonoObject *o, MonoMarshalNative string_encoding, int param_attrs) +{ + MonoError error; + MonoType *t; + MonoClass *klass; + + if (o == NULL) + return NULL; + + t = &o->vtable->klass->byval_arg; + switch (t->type) { + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_PTR: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_CHAR: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + return mono_object_unbox (o); + break; + case MONO_TYPE_STRING: + switch (string_encoding) { + case MONO_NATIVE_LPWSTR: + return mono_marshal_string_to_utf16_copy ((MonoString*)o); + case MONO_NATIVE_LPSTR: + case MONO_NATIVE_UTF8STR: + // Same code path, because in Mono, we treated strings as Utf8 + return mono_string_to_utf8str ((MonoString*)o); + default: + g_warning ("marshaling conversion %d not implemented", string_encoding); + g_assert_not_reached (); + } + break; + case MONO_TYPE_CLASS: + case MONO_TYPE_VALUETYPE: { + MonoMethod *method; + gpointer pa [3]; + gpointer res; + MonoBoolean delete_old = FALSE; + + klass = t->data.klass; + + if (mono_class_is_auto_layout (klass)) + break; + + if (klass->valuetype && (mono_class_is_explicit_layout (klass) || klass->blittable || klass->enumtype)) + return mono_object_unbox (o); + + res = mono_marshal_alloc (mono_class_native_size (klass, NULL), &error); + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + return NULL; + } + + if (!((param_attrs & PARAM_ATTRIBUTE_OUT) && !(param_attrs & PARAM_ATTRIBUTE_IN))) { + method = mono_marshal_get_struct_to_ptr (o->vtable->klass); + + pa [0] = o; + pa [1] = &res; + pa [2] = &delete_old; + + mono_runtime_invoke_checked (method, NULL, pa, &error); + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + return NULL; + } + } + + return res; + } + default: + break; + } + mono_set_pending_exception (mono_get_exception_argument ("", "No PInvoke conversion exists for value passed to Object-typed parameter.")); + return NULL; +} + +/** + * mono_marshal_free_asany: + * This is a JIT icall, it sets the pending exception + */ +void +mono_marshal_free_asany (MonoObject *o, gpointer ptr, MonoMarshalNative string_encoding, int param_attrs) +{ + MonoError error; + MonoType *t; + MonoClass *klass; + + if (o == NULL) + return; + + t = &o->vtable->klass->byval_arg; + switch (t->type) { + case MONO_TYPE_STRING: + switch (string_encoding) { + case MONO_NATIVE_LPWSTR: + case MONO_NATIVE_LPSTR: + case MONO_NATIVE_UTF8STR: + mono_marshal_free (ptr); + break; + default: + g_warning ("marshaling conversion %d not implemented", string_encoding); + g_assert_not_reached (); + } + break; + case MONO_TYPE_CLASS: + case MONO_TYPE_VALUETYPE: { + klass = t->data.klass; + + if (klass->valuetype && (mono_class_is_explicit_layout (klass) || klass->blittable || klass->enumtype)) + break; + + if (param_attrs & PARAM_ATTRIBUTE_OUT) { + MonoMethod *method = mono_marshal_get_ptr_to_struct (o->vtable->klass); + gpointer pa [2]; + + pa [0] = &ptr; + pa [1] = o; + + mono_runtime_invoke_checked (method, NULL, pa, &error); + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + return; + } + } + + if (!((param_attrs & PARAM_ATTRIBUTE_OUT) && !(param_attrs & PARAM_ATTRIBUTE_IN))) { + mono_struct_delete_old (klass, (char *)ptr); + } + + mono_marshal_free (ptr); + break; + } + default: + break; + } +} + +/* + * mono_marshal_get_generic_array_helper: + * + * Return a wrapper which is used to implement the implicit interfaces on arrays. + * The wrapper routes calls to METHOD, which is one of the InternalArray_ methods in Array. + */ +MonoMethod * +mono_marshal_get_generic_array_helper (MonoClass *klass, gchar *name, MonoMethod *method) +{ + MonoMethodSignature *sig, *csig; + MonoMethodBuilder *mb; + MonoMethod *res; + WrapperInfo *info; + int i; + + mb = mono_mb_new_no_dup_name (klass, name, MONO_WRAPPER_MANAGED_TO_MANAGED); + mb->method->slot = -1; + + mb->method->flags = METHOD_ATTRIBUTE_PRIVATE | METHOD_ATTRIBUTE_VIRTUAL | + METHOD_ATTRIBUTE_NEW_SLOT | METHOD_ATTRIBUTE_HIDE_BY_SIG | METHOD_ATTRIBUTE_FINAL; + + sig = mono_method_signature (method); + csig = mono_metadata_signature_dup_full (method->klass->image, sig); + csig->generic_param_count = 0; + +#ifdef ENABLE_ILGEN + mono_mb_emit_ldarg (mb, 0); + for (i = 0; i < csig->param_count; i++) + mono_mb_emit_ldarg (mb, i + 1); + mono_mb_emit_managed_call (mb, method, NULL); + mono_mb_emit_byte (mb, CEE_RET); + + /* We can corlib internal methods */ + mb->skip_visibility = TRUE; +#endif + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_GENERIC_ARRAY_HELPER); + info->d.generic_array_helper.method = method; + res = mono_mb_create (mb, csig, csig->param_count + 16, info); + + mono_mb_free (mb); + + return res; +} + +/* + * The mono_win32_compat_* functions are implementations of inline + * Windows kernel32 APIs, which are DllImport-able under MS.NET, + * although not exported by kernel32. + * + * We map the appropiate kernel32 entries to these functions using + * dllmaps declared in the global etc/mono/config. + */ + +void +mono_win32_compat_CopyMemory (gpointer dest, gconstpointer source, gsize length) +{ + if (!dest || !source) + return; + + memcpy (dest, source, length); +} + +void +mono_win32_compat_FillMemory (gpointer dest, gsize length, guchar fill) +{ + memset (dest, fill, length); +} + +void +mono_win32_compat_MoveMemory (gpointer dest, gconstpointer source, gsize length) +{ + if (!dest || !source) + return; + + memmove (dest, source, length); +} + +void +mono_win32_compat_ZeroMemory (gpointer dest, gsize length) +{ + memset (dest, 0, length); +} + +void +mono_marshal_find_nonzero_bit_offset (guint8 *buf, int len, int *byte_offset, guint8 *bitmask) +{ + int i; + guint8 byte; + + for (i = 0; i < len; ++i) + if (buf [i]) + break; + + g_assert (i < len); + + byte = buf [i]; + while (byte && !(byte & 1)) + byte >>= 1; + g_assert (byte == 1); + + *byte_offset = i; + *bitmask = buf [i]; +} + +MonoMethod * +mono_marshal_get_thunk_invoke_wrapper (MonoMethod *method) +{ + MonoMethodBuilder *mb; + MonoMethodSignature *sig, *csig; + MonoExceptionClause *clause; + MonoImage *image; + MonoClass *klass; + GHashTable *cache; + MonoMethod *res; + int i, param_count, sig_size, pos_leave; + + g_assert (method); + + // FIXME: we need to store the exception into a MonoHandle + g_assert (!mono_threads_is_coop_enabled ()); + + klass = method->klass; + image = method->klass->image; + + cache = get_cache (&mono_method_get_wrapper_cache (method)->thunk_invoke_cache, mono_aligned_addr_hash, NULL); + + if ((res = mono_marshal_find_in_cache (cache, method))) + return res; + + sig = mono_method_signature (method); + mb = mono_mb_new (klass, method->name, MONO_WRAPPER_NATIVE_TO_MANAGED); + + /* add "this" and exception param */ + param_count = sig->param_count + sig->hasthis + 1; + + /* dup & extend signature */ + csig = mono_metadata_signature_alloc (image, param_count); + sig_size = MONO_SIZEOF_METHOD_SIGNATURE + sig->param_count * sizeof (MonoType *); + memcpy (csig, sig, sig_size); + csig->param_count = param_count; + csig->hasthis = 0; + csig->pinvoke = 1; + csig->call_convention = MONO_CALL_DEFAULT; + + if (sig->hasthis) { + /* add "this" */ + csig->params [0] = &klass->byval_arg; + /* move params up by one */ + for (i = 0; i < sig->param_count; i++) + csig->params [i + 1] = sig->params [i]; + } + + /* setup exception param as byref+[out] */ + csig->params [param_count - 1] = mono_metadata_type_dup (image, + &mono_defaults.exception_class->byval_arg); + csig->params [param_count - 1]->byref = 1; + csig->params [param_count - 1]->attrs = PARAM_ATTRIBUTE_OUT; + + /* convert struct return to object */ + if (MONO_TYPE_ISSTRUCT (sig->ret)) + csig->ret = &mono_defaults.object_class->byval_arg; + +#ifdef ENABLE_ILGEN + /* local 0 (temp for exception object) */ + mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + + /* local 1 (temp for result) */ + if (!MONO_TYPE_IS_VOID (sig->ret)) + mono_mb_add_local (mb, sig->ret); + + /* clear exception arg */ + mono_mb_emit_ldarg (mb, param_count - 1); + mono_mb_emit_byte (mb, CEE_LDNULL); + mono_mb_emit_byte (mb, CEE_STIND_REF); + + /* try */ + clause = (MonoExceptionClause *)mono_image_alloc0 (image, sizeof (MonoExceptionClause)); + clause->try_offset = mono_mb_get_label (mb); + + /* push method's args */ + for (i = 0; i < param_count - 1; i++) { + MonoType *type; + MonoClass *klass; + + mono_mb_emit_ldarg (mb, i); + + /* get the byval type of the param */ + klass = mono_class_from_mono_type (csig->params [i]); + type = &klass->byval_arg; + + /* unbox struct args */ + if (MONO_TYPE_ISSTRUCT (type)) { + mono_mb_emit_op (mb, CEE_UNBOX, klass); + + /* byref args & and the "this" arg must remain a ptr. + Otherwise make a copy of the value type */ + if (!(csig->params [i]->byref || (i == 0 && sig->hasthis))) + mono_mb_emit_op (mb, CEE_LDOBJ, klass); + + csig->params [i] = &mono_defaults.object_class->byval_arg; + } + } + + /* call */ + if (method->flags & METHOD_ATTRIBUTE_VIRTUAL) + mono_mb_emit_op (mb, CEE_CALLVIRT, method); + else + mono_mb_emit_op (mb, CEE_CALL, method); + + /* save result at local 1 */ + if (!MONO_TYPE_IS_VOID (sig->ret)) + mono_mb_emit_stloc (mb, 1); + + pos_leave = mono_mb_emit_branch (mb, CEE_LEAVE); + + /* catch */ + clause->flags = MONO_EXCEPTION_CLAUSE_NONE; + clause->try_len = mono_mb_get_pos (mb) - clause->try_offset; + clause->data.catch_class = mono_defaults.object_class; + + clause->handler_offset = mono_mb_get_label (mb); + + /* store exception at local 0 */ + mono_mb_emit_stloc (mb, 0); + mono_mb_emit_ldarg (mb, param_count - 1); + mono_mb_emit_ldloc (mb, 0); + mono_mb_emit_byte (mb, CEE_STIND_REF); + mono_mb_emit_branch (mb, CEE_LEAVE); + + clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset; + + mono_mb_set_clauses (mb, 1, clause); + + mono_mb_patch_branch (mb, pos_leave); + /* end-try */ + + if (!MONO_TYPE_IS_VOID (sig->ret)) { + mono_mb_emit_ldloc (mb, 1); + + /* box the return value */ + if (MONO_TYPE_ISSTRUCT (sig->ret)) + mono_mb_emit_op (mb, CEE_BOX, mono_class_from_mono_type (sig->ret)); + } + + mono_mb_emit_byte (mb, CEE_RET); +#endif + + res = mono_mb_create_and_cache (cache, method, mb, csig, param_count + 16); + mono_mb_free (mb); + + return res; +} + +/* + * mono_marshal_free_dynamic_wrappers: + * + * Free wrappers of the dynamic method METHOD. + */ +void +mono_marshal_free_dynamic_wrappers (MonoMethod *method) +{ + MonoImage *image = method->klass->image; + + g_assert (method_is_dynamic (method)); + + /* This could be called during shutdown */ + if (marshal_mutex_initialized) + mono_marshal_lock (); + /* + * FIXME: We currently leak the wrappers. Freeing them would be tricky as + * they could be shared with other methods ? + */ + if (image->wrapper_caches.runtime_invoke_direct_cache) + g_hash_table_remove (image->wrapper_caches.runtime_invoke_direct_cache, method); + if (image->wrapper_caches.delegate_abstract_invoke_cache) + g_hash_table_foreach_remove (image->wrapper_caches.delegate_abstract_invoke_cache, signature_pointer_pair_matches_pointer, method); + // FIXME: Need to clear the caches in other images as well + if (image->delegate_bound_static_invoke_cache) + g_hash_table_remove (image->delegate_bound_static_invoke_cache, mono_method_signature (method)); + + if (marshal_mutex_initialized) + mono_marshal_unlock (); +} + +static void +mono_marshal_ftnptr_eh_callback (guint32 gchandle) +{ + g_assert (ftnptr_eh_callback); + ftnptr_eh_callback (gchandle); +} + +static void +ftnptr_eh_callback_default (guint32 gchandle) +{ + MonoException *exc; + gpointer stackdata; + + mono_threads_enter_gc_unsafe_region_unbalanced (&stackdata); + + exc = (MonoException*) mono_gchandle_get_target (gchandle); + + mono_gchandle_free (gchandle); + + mono_reraise_exception_deprecated (exc); +} + +/* + * mono_install_ftnptr_eh_callback: + * + * Install a callback that should be called when there is a managed exception + * in a native-to-managed wrapper. This is mainly used by iOS to convert a + * managed exception to a native exception, to properly unwind the native + * stack; this native exception will then be converted back to a managed + * exception in their managed-to-native wrapper. + */ +void +mono_install_ftnptr_eh_callback (MonoFtnPtrEHCallback callback) +{ + ftnptr_eh_callback = callback; +} + +static MonoThreadInfo* +mono_icall_start (HandleStackMark *stackmark, MonoError *error) +{ + MonoThreadInfo *info = mono_thread_info_current (); + + mono_stack_mark_init (info, stackmark); + error_init (error); + return info; +} + +static void +mono_icall_end (MonoThreadInfo *info, HandleStackMark *stackmark, MonoError *error) +{ + mono_stack_mark_pop (info, stackmark); + if (G_UNLIKELY (!is_ok (error))) + mono_error_set_pending_exception (error); +} + +static MonoObjectHandle +mono_icall_handle_new (gpointer rawobj) +{ +#ifdef MONO_HANDLE_TRACK_OWNER + return mono_handle_new (rawobj, ""); +#else + return mono_handle_new (rawobj); +#endif +} + +static MonoObjectHandle +mono_icall_handle_new_interior (gpointer rawobj) +{ +#ifdef MONO_HANDLE_TRACK_OWNER + return mono_handle_new_interior (rawobj, ""); +#else + return mono_handle_new_interior (rawobj); +#endif +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/marshal.h b/unity-2019.4.24f1-mbe/mono/metadata/marshal.h new file mode 100644 index 000000000..12f224d2d --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/marshal.h @@ -0,0 +1,643 @@ +/** + * \file + * Routines for marshaling complex types in P/Invoke methods. + * + * Author: + * Paolo Molaro (lupus@ximian.com) + * + * (C) 2002 Ximian, Inc. http://www.ximian.com + * + */ + +#ifndef __MONO_MARSHAL_H__ +#define __MONO_MARSHAL_H__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define mono_marshal_find_bitfield_offset(type, elem, byte_offset, bitmask) \ + do { \ + type tmp; \ + memset (&tmp, 0, sizeof (tmp)); \ + tmp.elem = 1; \ + mono_marshal_find_nonzero_bit_offset ((guint8*)&tmp, sizeof (tmp), (byte_offset), (bitmask)); \ + } while (0) + +/* + * This structure holds the state kept by the emit_ marshalling functions. + * This is exported so it can be used by cominterop.c. + */ +typedef struct { + MonoMethodBuilder *mb; + MonoMethodSignature *sig; + MonoMethodPInvoke *piinfo; + int *orig_conv_args; /* Locals containing the original values of byref args */ + int retobj_var; + int vtaddr_var; + MonoClass *retobj_class; + MonoMethodSignature *csig; /* Might need to be changed due to MarshalAs directives */ + MonoImage *image; /* The image to use for looking up custom marshallers */ +} EmitMarshalContext; + +typedef enum { + /* + * This is invoked to convert arguments from the current types to + * the underlying types expected by the platform routine. If required, + * the methods create a temporary variable with the proper type, and return + * the location for it (either the passed argument, or the newly allocated + * local slot). + */ + MARSHAL_ACTION_CONV_IN, + + /* + * This operation is called to push the actual value that was optionally + * converted on the first stage + */ + MARSHAL_ACTION_PUSH, + + /* + * Convert byref arguments back or free resources allocated during the + * CONV_IN stage + */ + MARSHAL_ACTION_CONV_OUT, + + /* + * The result from the unmanaged call is at the top of the stack when + * this action is invoked. The result should be stored in the + * third local variable slot. + */ + MARSHAL_ACTION_CONV_RESULT, + + MARSHAL_ACTION_MANAGED_CONV_IN, + MARSHAL_ACTION_MANAGED_CONV_OUT, + MARSHAL_ACTION_MANAGED_CONV_RESULT +} MarshalAction; + +/* + * This is an extension of the MONO_WRAPPER_ enum to avoid adding more elements to that + * enum. + */ +typedef enum { + WRAPPER_SUBTYPE_NONE, + /* Subtypes of MONO_WRAPPER_MANAGED_TO_MANAGED */ + WRAPPER_SUBTYPE_ELEMENT_ADDR, + WRAPPER_SUBTYPE_STRING_CTOR, + /* Subtypes of MONO_WRAPPER_STELEMREF */ + WRAPPER_SUBTYPE_VIRTUAL_STELEMREF, + /* Subtypes of MONO_WRAPPER_UNKNOWN */ + WRAPPER_SUBTYPE_FAST_MONITOR_ENTER, + WRAPPER_SUBTYPE_FAST_MONITOR_ENTER_V4, + WRAPPER_SUBTYPE_FAST_MONITOR_EXIT, + WRAPPER_SUBTYPE_PTR_TO_STRUCTURE, + WRAPPER_SUBTYPE_STRUCTURE_TO_PTR, + /* Subtypes of MONO_WRAPPER_CASTCLASS */ + WRAPPER_SUBTYPE_CASTCLASS_WITH_CACHE, + WRAPPER_SUBTYPE_ISINST_WITH_CACHE, + /* Subtypes of MONO_WRAPPER_RUNTIME_INVOKE */ + WRAPPER_SUBTYPE_RUNTIME_INVOKE_NORMAL, + WRAPPER_SUBTYPE_RUNTIME_INVOKE_DYNAMIC, + WRAPPER_SUBTYPE_RUNTIME_INVOKE_DIRECT, + WRAPPER_SUBTYPE_RUNTIME_INVOKE_VIRTUAL, + /* Subtypes of MONO_WRAPPER_MANAGED_TO_NATIVE */ + WRAPPER_SUBTYPE_ICALL_WRAPPER, + WRAPPER_SUBTYPE_NATIVE_FUNC_AOT, + WRAPPER_SUBTYPE_PINVOKE, + /* Subtypes of MONO_WRAPPER_UNKNOWN */ + WRAPPER_SUBTYPE_SYNCHRONIZED_INNER, + WRAPPER_SUBTYPE_GSHAREDVT_IN, + WRAPPER_SUBTYPE_GSHAREDVT_OUT, + WRAPPER_SUBTYPE_ARRAY_ACCESSOR, + /* Subtypes of MONO_WRAPPER_MANAGED_TO_MANAGED */ + WRAPPER_SUBTYPE_GENERIC_ARRAY_HELPER, + /* Subtypes of MONO_WRAPPER_DELEGATE_INVOKE */ + WRAPPER_SUBTYPE_DELEGATE_INVOKE_VIRTUAL, + WRAPPER_SUBTYPE_DELEGATE_INVOKE_BOUND, + /* Subtypes of MONO_WRAPPER_UNKNOWN */ + WRAPPER_SUBTYPE_GSHAREDVT_IN_SIG, + WRAPPER_SUBTYPE_GSHAREDVT_OUT_SIG, + WRAPPER_SUBTYPE_INTERP_IN +} WrapperSubtype; + +typedef struct { + MonoMethod *method; + MonoClass *klass; +} NativeToManagedWrapperInfo; + +typedef struct { + MonoMethod *method; +} StringCtorWrapperInfo; + +typedef struct { + int kind; +} VirtualStelemrefWrapperInfo; + +typedef struct { + guint32 rank, elem_size; +} ElementAddrWrapperInfo; + +typedef struct { + MonoMethod *method; + /* For WRAPPER_SUBTYPE_RUNTIME_INVOKE_NORMAL */ + MonoMethodSignature *sig; +} RuntimeInvokeWrapperInfo; + +typedef struct { + MonoMethod *method; +} ManagedToNativeWrapperInfo; + +typedef struct { + MonoMethod *method; +} SynchronizedWrapperInfo; + +typedef struct { + MonoMethod *method; +} SynchronizedInnerWrapperInfo; + +typedef struct { + MonoMethod *method; +} GenericArrayHelperWrapperInfo; + +typedef struct { + gpointer func; +} ICallWrapperInfo; + +typedef struct { + MonoMethod *method; +} ArrayAccessorWrapperInfo; + +typedef struct { + MonoClass *klass; +} ProxyWrapperInfo; + +typedef struct { + const char *gc_name; + int alloc_type; +} AllocatorWrapperInfo; + +typedef struct { + MonoMethod *method; +} UnboxWrapperInfo; + +typedef struct { + MonoMethod *method; +} RemotingWrapperInfo; + +typedef struct { + MonoMethodSignature *sig; +} GsharedvtWrapperInfo; + +typedef struct { + MonoMethod *method; +} DelegateInvokeWrapperInfo; + +typedef struct { + MonoMethodSignature *sig; +} InterpInWrapperInfo; + +/* + * This structure contains additional information to uniquely identify a given wrapper + * method. It can be retrieved by mono_marshal_get_wrapper_info () for certain types + * of wrappers, i.e. ones which do not have a 1-1 association with a method/class. + */ +typedef struct { + WrapperSubtype subtype; + union { + /* RUNTIME_INVOKE_... */ + RuntimeInvokeWrapperInfo runtime_invoke; + /* STRING_CTOR */ + StringCtorWrapperInfo string_ctor; + /* ELEMENT_ADDR */ + ElementAddrWrapperInfo element_addr; + /* VIRTUAL_STELEMREF */ + VirtualStelemrefWrapperInfo virtual_stelemref; + /* MONO_WRAPPER_NATIVE_TO_MANAGED */ + NativeToManagedWrapperInfo native_to_managed; + /* MONO_WRAPPER_MANAGED_TO_NATIVE */ + ManagedToNativeWrapperInfo managed_to_native; + /* SYNCHRONIZED */ + SynchronizedWrapperInfo synchronized; + /* SYNCHRONIZED_INNER */ + SynchronizedInnerWrapperInfo synchronized_inner; + /* GENERIC_ARRAY_HELPER */ + GenericArrayHelperWrapperInfo generic_array_helper; + /* ICALL_WRAPPER */ + ICallWrapperInfo icall; + /* ARRAY_ACCESSOR */ + ArrayAccessorWrapperInfo array_accessor; + /* PROXY_ISINST etc. */ + ProxyWrapperInfo proxy; + /* ALLOC */ + AllocatorWrapperInfo alloc; + /* UNBOX */ + UnboxWrapperInfo unbox; + /* MONO_WRAPPER_REMOTING_INVOKE/MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK/MONO_WRAPPER_XDOMAIN_INVOKE */ + RemotingWrapperInfo remoting; + /* GSHAREDVT_IN_SIG/GSHAREDVT_OUT_SIG */ + GsharedvtWrapperInfo gsharedvt; + /* DELEGATE_INVOKE */ + DelegateInvokeWrapperInfo delegate_invoke; + /* INTERP_IN */ + InterpInWrapperInfo interp_in; + } d; +} WrapperInfo; + +G_BEGIN_DECLS + +/*type of the function pointer of methods returned by mono_marshal_get_runtime_invoke*/ +typedef MonoObject *(*RuntimeInvokeFunction) (MonoObject *this_obj, void **params, MonoObject **exc, void* compiled_method); + +typedef void (*RuntimeInvokeDynamicFunction) (void *args, MonoObject **exc, void* compiled_method); + +/* marshaling helper functions */ + +void +mono_marshal_init (void); + +void +mono_marshal_init_tls (void); + +void +mono_marshal_cleanup (void); + +gint32 +mono_class_native_size (MonoClass *klass, guint32 *align); + +MonoMarshalType * +mono_marshal_load_type_info (MonoClass* klass); + +gint32 +mono_marshal_type_size (MonoType *type, MonoMarshalSpec *mspec, guint32 *align, + gboolean as_field, gboolean unicode); + +int +mono_type_native_stack_size (MonoType *type, guint32 *alignment); + +gpointer +mono_string_to_ansibstr (MonoString *string_obj); + +gpointer +mono_ptr_to_bstr (gpointer ptr, int slen); + +gpointer +mono_string_to_bstr(MonoString* str); + +void mono_delegate_free_ftnptr (MonoDelegate *delegate); + +void +mono_marshal_set_last_error (void); + +guint +mono_type_to_ldind (MonoType *type); + +guint +mono_type_to_stind (MonoType *type); + +/* functions to create various architecture independent helper functions */ + +MonoMethod * +mono_marshal_method_from_wrapper (MonoMethod *wrapper); + +WrapperInfo* +mono_wrapper_info_create (MonoMethodBuilder *mb, WrapperSubtype subtype); + +void +mono_marshal_set_wrapper_info (MonoMethod *method, WrapperInfo *info); + +WrapperInfo* +mono_marshal_get_wrapper_info (MonoMethod *wrapper); + +MonoMethod * +mono_marshal_get_delegate_begin_invoke (MonoMethod *method); + +MonoMethod * +mono_marshal_get_delegate_end_invoke (MonoMethod *method); + +MonoMethod * +mono_marshal_get_delegate_invoke (MonoMethod *method, MonoDelegate *del); + +MonoMethod * +mono_marshal_get_delegate_invoke_internal (MonoMethod *method, gboolean callvirt, gboolean static_method_with_first_arg_bound, MonoMethod *target_method); + +MonoMethod * +mono_marshal_get_runtime_invoke_full (MonoMethod *method, gboolean virtual_, gboolean need_direct_wrapper); + +MonoMethod * +mono_marshal_get_runtime_invoke (MonoMethod *method, gboolean is_virtual); + +MonoMethod* +mono_marshal_get_runtime_invoke_dynamic (void); + +MonoMethod * +mono_marshal_get_runtime_invoke_for_sig (MonoMethodSignature *sig); + +MonoMethodSignature* +mono_marshal_get_string_ctor_signature (MonoMethod *method); + +MonoMethod * +mono_marshal_get_managed_wrapper (MonoMethod *method, MonoClass *delegate_klass, uint32_t this_loc, MonoError *exernal_error); + +gpointer +mono_marshal_get_vtfixup_ftnptr (MonoImage *image, guint32 token, guint16 type); + +MonoMethod * +mono_marshal_get_icall_wrapper (MonoMethodSignature *sig, const char *name, gconstpointer func, gboolean check_exceptions); + +MonoMethod * +mono_marshal_get_native_wrapper (MonoMethod *method, gboolean check_exceptions, gboolean aot); + +MonoMethod * +mono_marshal_get_native_func_wrapper (MonoImage *image, MonoMethodSignature *sig, MonoMethodPInvoke *piinfo, MonoMarshalSpec **mspecs, gpointer func); + +MonoMethod* +mono_marshal_get_native_func_wrapper_aot (MonoClass *klass); + +MonoMethod * +mono_marshal_get_struct_to_ptr (MonoClass *klass); + +MonoMethod * +mono_marshal_get_ptr_to_struct (MonoClass *klass); + +MonoMethod * +mono_marshal_get_synchronized_wrapper (MonoMethod *method); + +MonoMethod * +mono_marshal_get_synchronized_inner_wrapper (MonoMethod *method); + +MonoMethod * +mono_marshal_get_unbox_wrapper (MonoMethod *method); + +MonoMethod * +mono_marshal_get_castclass_with_cache (void); + +MonoMethod * +mono_marshal_get_isinst_with_cache (void); + +MonoMethod * +mono_marshal_get_stelemref (void); + +MonoMethod* +mono_marshal_get_virtual_stelemref (MonoClass *array_class); + +MonoMethod** +mono_marshal_get_virtual_stelemref_wrappers (int *nwrappers); + +MonoMethod* +mono_marshal_get_array_address (int rank, int elem_size); + +MonoMethod * +mono_marshal_get_array_accessor_wrapper (MonoMethod *method); + +MonoMethod * +mono_marshal_get_generic_array_helper (MonoClass *klass, gchar *name, MonoMethod *method); + +MonoMethod * +mono_marshal_get_thunk_invoke_wrapper (MonoMethod *method); + +MonoMethod* +mono_marshal_get_gsharedvt_in_wrapper (void); + +MonoMethod* +mono_marshal_get_gsharedvt_out_wrapper (void); + +void +mono_marshal_free_dynamic_wrappers (MonoMethod *method); + +void +mono_marshal_lock_internal (void); + +void +mono_marshal_unlock_internal (void); + +/* marshaling internal calls */ + +void * +mono_marshal_alloc (gsize size, MonoError *error); + +void +mono_marshal_free (gpointer ptr); + +void +mono_marshal_free_array (gpointer *ptr, int size); + +gboolean +mono_marshal_free_ccw (MonoObject* obj); + +void +cominterop_release_all_rcws (void); + +void +ves_icall_System_Runtime_InteropServices_Marshal_copy_to_unmanaged (MonoArray *src, gint32 start_index, + gpointer dest, gint32 length); + +void +ves_icall_System_Runtime_InteropServices_Marshal_copy_from_unmanaged (gpointer src, gint32 start_index, + MonoArray *dest, gint32 length); + +MonoStringHandle +ves_icall_System_Runtime_InteropServices_Marshal_PtrToStringAnsi (char *ptr, MonoError *error); + +MonoString * +ves_icall_System_Runtime_InteropServices_Marshal_PtrToStringAnsi_len (char *ptr, gint32 len); + +MonoString * +ves_icall_System_Runtime_InteropServices_Marshal_PtrToStringUni (guint16 *ptr); + +MonoString * +ves_icall_System_Runtime_InteropServices_Marshal_PtrToStringUni_len (guint16 *ptr, gint32 len); + +MonoString * +ves_icall_System_Runtime_InteropServices_Marshal_PtrToStringBSTR (gpointer ptr); + +guint32 +ves_icall_System_Runtime_InteropServices_Marshal_GetComSlotForMethodInfoInternal (MonoReflectionMethod *m); + +guint32 +ves_icall_System_Runtime_InteropServices_Marshal_GetLastWin32Error (void); + +guint32 +ves_icall_System_Runtime_InteropServices_Marshal_SizeOf (MonoReflectionTypeHandle rtype, MonoError *error); + +void +ves_icall_System_Runtime_InteropServices_Marshal_StructureToPtr (MonoObject *obj, gpointer dst, MonoBoolean delete_old); + +void +ves_icall_System_Runtime_InteropServices_Marshal_PtrToStructure (gpointer src, MonoObject *dst); + +MonoObject * +ves_icall_System_Runtime_InteropServices_Marshal_PtrToStructure_type (gpointer src, MonoReflectionType *type); + +int +ves_icall_System_Runtime_InteropServices_Marshal_OffsetOf (MonoReflectionTypeHandle type, MonoStringHandle field_name, MonoError *error); + +gpointer +ves_icall_System_Runtime_InteropServices_Marshal_StringToBSTR (MonoString *string); + +gpointer +ves_icall_System_Runtime_InteropServices_Marshal_BufferToBSTR (MonoArray *ptr, int len); + +gpointer +ves_icall_System_Runtime_InteropServices_Marshal_StringToHGlobalAnsi (MonoString *string); + +gpointer +ves_icall_System_Runtime_InteropServices_Marshal_StringToHGlobalUni (MonoString *string); + +void +ves_icall_System_Runtime_InteropServices_Marshal_DestroyStructure (gpointer src, MonoReflectionType *type); + +void* +ves_icall_System_Runtime_InteropServices_Marshal_AllocCoTaskMem (int size); + +void* +ves_icall_System_Runtime_InteropServices_Marshal_AllocCoTaskMemSize (gulong size); + +void +ves_icall_System_Runtime_InteropServices_Marshal_FreeCoTaskMem (void *ptr); + +gpointer +ves_icall_System_Runtime_InteropServices_Marshal_ReAllocCoTaskMem (gpointer ptr, int size); + +void* +ves_icall_System_Runtime_InteropServices_Marshal_AllocHGlobal (gpointer size); + +gpointer +ves_icall_System_Runtime_InteropServices_Marshal_ReAllocHGlobal (gpointer ptr, gpointer size); + +void +ves_icall_System_Runtime_InteropServices_Marshal_FreeHGlobal (void *ptr); + +void +ves_icall_System_Runtime_InteropServices_Marshal_FreeBSTR (void *ptr); + +void* +ves_icall_System_Runtime_InteropServices_Marshal_UnsafeAddrOfPinnedArrayElement (MonoArray *arrayobj, int index); + +MonoDelegateHandle +ves_icall_System_Runtime_InteropServices_Marshal_GetDelegateForFunctionPointerInternal (void *ftn, MonoReflectionTypeHandle type, MonoError *error); + +gpointer +ves_icall_System_Runtime_InteropServices_Marshal_GetFunctionPointerForDelegateInternal (MonoDelegateHandle delegate, MonoError *error); + +int +ves_icall_System_Runtime_InteropServices_Marshal_AddRefInternal (gpointer pUnk); + +int +ves_icall_System_Runtime_InteropServices_Marshal_QueryInterfaceInternal (gpointer pUnk, gpointer riid, gpointer* ppv); + +int +ves_icall_System_Runtime_InteropServices_Marshal_ReleaseInternal (gpointer pUnk); + +void* +ves_icall_System_Runtime_InteropServices_Marshal_GetIUnknownForObjectInternal (MonoObject* object); + +MonoObject* +ves_icall_System_Runtime_InteropServices_Marshal_GetObjectForCCW (void* pUnk); + +void* +ves_icall_System_Runtime_InteropServices_Marshal_GetIDispatchForObjectInternal (MonoObject* object); + +void* +ves_icall_System_Runtime_InteropServices_Marshal_GetCCW (MonoObject* object, MonoReflectionType* type); + +MonoBoolean +ves_icall_System_Runtime_InteropServices_Marshal_IsComObject (MonoObject* object); + +gint32 +ves_icall_System_Runtime_InteropServices_Marshal_ReleaseComObjectInternal (MonoObject* object); + +MonoObject * +ves_icall_System_ComObject_CreateRCW (MonoReflectionType *type); + +void +ves_icall_System_ComObject_ReleaseInterfaces(MonoComObject* obj); + +gpointer +ves_icall_System_ComObject_GetInterfaceInternal (MonoComObject* obj, MonoReflectionType* type, MonoBoolean throw_exception); + +void +ves_icall_Mono_Interop_ComInteropProxy_AddProxy (gpointer pUnk, MonoComInteropProxy* proxy); + +MonoComInteropProxy* +ves_icall_Mono_Interop_ComInteropProxy_FindProxy (gpointer pUnk); + +MONO_API void +mono_win32_compat_CopyMemory (gpointer dest, gconstpointer source, gsize length); + +MONO_API void +mono_win32_compat_FillMemory (gpointer dest, gsize length, guchar fill); + +MONO_API void +mono_win32_compat_MoveMemory (gpointer dest, gconstpointer source, gsize length); + +MONO_API void +mono_win32_compat_ZeroMemory (gpointer dest, gsize length); + +void +mono_marshal_find_nonzero_bit_offset (guint8 *buf, int len, int *byte_offset, guint8 *bitmask) MONO_LLVM_INTERNAL; + +MonoMethodSignature* +mono_signature_no_pinvoke (MonoMethod *method); + +/* Called from cominterop.c/remoting.c */ + +void +mono_marshal_emit_native_wrapper (MonoImage *image, MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethodPInvoke *piinfo, MonoMarshalSpec **mspecs, gpointer func, gboolean aot, gboolean check_exceptions, gboolean func_param); + +void +mono_marshal_emit_managed_wrapper (MonoMethodBuilder *mb, MonoMethodSignature *invoke_sig, MonoMarshalSpec **mspecs, EmitMarshalContext* m, MonoMethod *method, uint32_t target_handle); + +GHashTable* +mono_marshal_get_cache (GHashTable **var, GHashFunc hash_func, GCompareFunc equal_func); + +MonoMethod* +mono_marshal_find_in_cache (GHashTable *cache, gpointer key); + +MonoMethod* +mono_mb_create_and_cache (GHashTable *cache, gpointer key, + MonoMethodBuilder *mb, MonoMethodSignature *sig, + int max_stack); +void +mono_marshal_emit_thread_interrupt_checkpoint (MonoMethodBuilder *mb); + +void +mono_marshal_emit_thread_force_interrupt_checkpoint (MonoMethodBuilder *mb); + +void +mono_marshal_use_aot_wrappers (gboolean use); + +MonoObject * +mono_marshal_xdomain_copy_value (MonoObject *val, MonoError *error); + +MonoObject * +ves_icall_mono_marshal_xdomain_copy_value (MonoObject *val); + +int +mono_mb_emit_save_args (MonoMethodBuilder *mb, MonoMethodSignature *sig, gboolean save_this); + +void +mono_mb_emit_restore_result (MonoMethodBuilder *mb, MonoType *return_type); + +MonoMethod* +mono_mb_create (MonoMethodBuilder *mb, MonoMethodSignature *sig, + int max_stack, WrapperInfo *info); + +MonoMethod* +mono_mb_create_and_cache_full (GHashTable *cache, gpointer key, + MonoMethodBuilder *mb, MonoMethodSignature *sig, + int max_stack, WrapperInfo *info, gboolean *out_found); + +typedef void (*MonoFtnPtrEHCallback) (guint32 gchandle); + +MONO_API void +mono_install_ftnptr_eh_callback (MonoFtnPtrEHCallback callback); + +G_END_DECLS + +#endif /* __MONO_MARSHAL_H__ */ + + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/mempool-internals.h b/unity-2019.4.24f1-mbe/mono/metadata/mempool-internals.h new file mode 100644 index 000000000..345823213 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/mempool-internals.h @@ -0,0 +1,87 @@ +/** + * \file + */ + +#ifndef _MONO_MEMPOOL_INTERNALS_H_ +#define _MONO_MEMPOOL_INTERNALS_H_ + +#include + +#include "mono/utils/mono-compiler.h" +#include "mono/metadata/mempool.h" + +static inline GList* +g_list_prepend_mempool (MonoMemPool *mp, GList *list, gpointer data) +{ + GList *new_list; + + new_list = (GList *) mono_mempool_alloc (mp, sizeof (GList)); + new_list->data = data; + new_list->prev = list ? list->prev : NULL; + new_list->next = list; + + if (new_list->prev) + new_list->prev->next = new_list; + if (list) + list->prev = new_list; + + return new_list; +} + +static inline GSList* +g_slist_prepend_mempool (MonoMemPool *mp, GSList *list, gpointer data) +{ + GSList *new_list; + + new_list = (GSList *) mono_mempool_alloc (mp, sizeof (GSList)); + new_list->data = data; + new_list->next = list; + + return new_list; +} + +static inline GSList* +g_slist_append_mempool (MonoMemPool *mp, GSList *list, gpointer data) +{ + GSList *new_list; + GSList *last; + + new_list = (GSList *) mono_mempool_alloc (mp, sizeof (GSList)); + new_list->data = data; + new_list->next = NULL; + + if (list) { + last = list; + while (last->next) + last = last->next; + last->next = new_list; + + return list; + } else + return new_list; +} + +static inline GList* +g_list_append_mempool (MonoMemPool *mp, GList *list, gpointer data) +{ + GList *new_list; + + new_list = (GList *) mono_mempool_alloc0 (mp, sizeof (GList)); + new_list->data = data; + new_list->prev = g_list_last (list); + if (new_list->prev) + new_list->prev->next = new_list; + + return list ? list : new_list; +} + +char* +mono_mempool_strdup_vprintf (MonoMemPool *pool, const char *format, va_list args); + +char* +mono_mempool_strdup_printf (MonoMemPool *pool, const char *format, ...) MONO_ATTR_FORMAT_PRINTF(2,3);; + +long +mono_mempool_get_bytes_allocated (void); + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/mempool.c b/unity-2019.4.24f1-mbe/mono/metadata/mempool.c new file mode 100644 index 000000000..fba667781 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/mempool.c @@ -0,0 +1,452 @@ +/** + * \file + * efficient memory allocation + * + * MonoMemPool is for fast allocation of memory. We free + * all memory when the pool is destroyed. + * + * Author: + * Dietmar Maurer (dietmar@ximian.com) + * + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Copyright 2011 Xamarin Inc. (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#include +#include + +#include "mempool.h" +#include "mempool-internals.h" +#include "utils/unlocked.h" + +/* + * MonoMemPool is for fast allocation of memory. We free + * all memory when the pool is destroyed. + */ + +#define MEM_ALIGN 8 +#define ALIGN_SIZE(s) (((s) + MEM_ALIGN - 1) & ~(MEM_ALIGN - 1)) + +// Size of memory at start of mempool reserved for header +#define SIZEOF_MEM_POOL (ALIGN_SIZE (sizeof (MonoMemPool))) + +#if MONO_SMALL_CONFIG +#define MONO_MEMPOOL_PAGESIZE 4096 +#define MONO_MEMPOOL_MINSIZE 256 +#else +#define MONO_MEMPOOL_PAGESIZE 8192 +#define MONO_MEMPOOL_MINSIZE 512 +#endif + +// The --with-malloc-mempools debug-build flag causes mempools to be allocated in single-element blocks, so tools like Valgrind can run better. +#if USE_MALLOC_FOR_MEMPOOLS +#define INDIVIDUAL_ALLOCATIONS +#define MONO_MEMPOOL_PREFER_INDIVIDUAL_ALLOCATION_SIZE 0 +#else +#define MONO_MEMPOOL_PREFER_INDIVIDUAL_ALLOCATION_SIZE MONO_MEMPOOL_PAGESIZE +#endif + +#ifndef G_LIKELY +#define G_LIKELY(a) (a) +#define G_UNLIKELY(a) (a) +#endif + +// A mempool is a linked list of memory blocks, each of which begins with this header structure. +// The initial block in the linked list is special, and tracks additional information. +struct _MonoMemPool { + // Next block after this one in linked list + MonoMemPool *next; + + // Size of this memory block only + guint32 size; + + // Used in "initial block" only: Beginning of current free space in mempool (may be in some block other than the first one) + guint8 *pos; + + // Used in "initial block" only: End of current free space in mempool (ie, the first byte following the end of usable space) + guint8 *end; + + union { + // Unused: Imposing floating point memory rules on _MonoMemPool's final field ensures proper alignment of whole header struct + double pad; + + // Used in "initial block" only: Number of bytes so far allocated (whether used or not) in the whole mempool + guint32 allocated; + } d; +}; + +static gint64 total_bytes_allocated = 0; + +/** + * mono_mempool_new: + * + * Returns: a new memory pool. + */ +MonoMemPool * +mono_mempool_new (void) +{ + return mono_mempool_new_size (MONO_MEMPOOL_PAGESIZE); +} + +/** + * mono_mempool_new_size: + * \param initial_size the amount of memory to initially reserve for the memory pool. + * \returns a new memory pool with a specific initial memory reservation. + */ +MonoMemPool * +mono_mempool_new_size (int initial_size) +{ + MonoMemPool *pool; + +#ifdef INDIVIDUAL_ALLOCATIONS + // In individual allocation mode, create initial block with zero storage space. + initial_size = SIZEOF_MEM_POOL; +#else + if (initial_size < MONO_MEMPOOL_MINSIZE) + initial_size = MONO_MEMPOOL_MINSIZE; +#endif + + pool = (MonoMemPool *)g_malloc (initial_size); + + pool->next = NULL; + pool->pos = (guint8*)pool + SIZEOF_MEM_POOL; // Start after header + pool->end = (guint8*)pool + initial_size; // End at end of allocated space + pool->d.allocated = pool->size = initial_size; + UnlockedAdd64 (&total_bytes_allocated, initial_size); + return pool; +} + +/** + * mono_mempool_destroy: + * \param pool the memory pool to destroy + * + * Free all memory associated with this pool. + */ +void +mono_mempool_destroy (MonoMemPool *pool) +{ + MonoMemPool *p, *n; + + UnlockedSubtract64 (&total_bytes_allocated, pool->d.allocated); + + p = pool; + while (p) { + n = p->next; + g_free (p); + p = n; + } +} + +/** + * mono_mempool_invalidate: + * \param pool the memory pool to invalidate + * + * Fill the memory associated with this pool to 0x2a (42). Useful for debugging. + */ +void +mono_mempool_invalidate (MonoMemPool *pool) +{ + MonoMemPool *p, *n; + + p = pool; + while (p) { + n = p->next; + memset (p, 42, p->size); + p = n; + } +} + +/** + * mono_mempool_stats: + * \param pool the memory pool we need stats for + * + * Print a few stats about the mempool: + * - Total memory allocated (malloced) by mem pool + * - Number of chunks/blocks memory is allocated in + * - How much memory is available to dispense before a new malloc must occur? + */ +void +mono_mempool_stats (MonoMemPool *pool) +{ + MonoMemPool *p; + int count = 0; + guint32 still_free; + + p = pool; + while (p) { + p = p->next; + count++; + } + if (pool) { + still_free = pool->end - pool->pos; + g_print ("Mempool %p stats:\n", pool); + g_print ("Total mem allocated: %d\n", pool->d.allocated); + g_print ("Num chunks: %d\n", count); + g_print ("Free memory: %d\n", still_free); + } +} + +#ifdef TRACE_ALLOCATIONS +#include +#include "metadata/appdomain.h" +#include "metadata/metadata-internals.h" + +static mono_mutex_t mempool_tracing_lock; +#define BACKTRACE_DEPTH 7 +static void +mono_backtrace (int size) +{ + void *array[BACKTRACE_DEPTH]; + char **names; + int i, symbols; + static gboolean inited; + + if (!inited) { + mono_os_mutex_init_recursive (&mempool_tracing_lock); + inited = TRUE; + } + + mono_os_mutex_lock (&mempool_tracing_lock); + g_print ("Allocating %d bytes\n", size); + MONO_ENTER_GC_SAFE; + symbols = backtrace (array, BACKTRACE_DEPTH); + names = backtrace_symbols (array, symbols); + MONO_EXIT_GC_SAFE; + for (i = 1; i < symbols; ++i) { + g_print ("\t%s\n", names [i]); + } + g_free (names); + mono_os_mutex_unlock (&mempool_tracing_lock); +} + +#endif + +/** + * get_next_size: + * @pool: the memory pool to use + * @size: size of the memory entity we are trying to allocate + * + * A mempool is growing; give a recommended size for the next block. + * Each block in a mempool should be about 150% bigger than the previous one, + * or bigger if it is necessary to include the new entity. + * + * Returns: the recommended size. + */ +static guint +get_next_size (MonoMemPool *pool, int size) +{ + int target = pool->next? pool->next->size: pool->size; + size += SIZEOF_MEM_POOL; + /* increase the size */ + target += target / 2; + while (target < size) { + target += target / 2; + } + if (target > MONO_MEMPOOL_PAGESIZE && size <= MONO_MEMPOOL_PAGESIZE) + target = MONO_MEMPOOL_PAGESIZE; + return target; +} + +/** + * mono_mempool_alloc: + * \param pool the memory pool to use + * \param size size of the memory block + * + * Allocates a new block of memory in \p pool . + * + * \returns the address of a newly allocated memory block. + */ +gpointer +mono_mempool_alloc (MonoMemPool *pool, guint size) +{ + gpointer rval = pool->pos; // Return value + + // Normal case: Just bump up pos pointer and we are done + size = ALIGN_SIZE (size); + pool->pos = (guint8*)rval + size; + +#ifdef TRACE_ALLOCATIONS + if (pool == mono_get_corlib ()->mempool) { + mono_backtrace (size); + } +#endif + + // If we have just overflowed the current block, we need to back up and try again. + if (G_UNLIKELY (pool->pos >= pool->end)) { + pool->pos -= size; // Back out + + // For large objects, allocate the object into its own block. + // (In individual allocation mode, the constant will be 0 and this path will always be taken) + if (size >= MONO_MEMPOOL_PREFER_INDIVIDUAL_ALLOCATION_SIZE) { + guint new_size = SIZEOF_MEM_POOL + size; + MonoMemPool *np = (MonoMemPool *)g_malloc (new_size); + + np->next = pool->next; + np->size = new_size; + pool->next = np; + pool->d.allocated += new_size; + UnlockedAdd64 (&total_bytes_allocated, new_size); + + rval = (guint8*)np + SIZEOF_MEM_POOL; + } else { + // Notice: any unused memory at the end of the old head becomes simply abandoned in this case until the mempool is freed (see Bugzilla #35136) + guint new_size = get_next_size (pool, size); + MonoMemPool *np = (MonoMemPool *)g_malloc (new_size); + + np->next = pool->next; + np->size = new_size; + pool->next = np; + pool->pos = (guint8*)np + SIZEOF_MEM_POOL; + pool->end = (guint8*)np + new_size; + pool->d.allocated += new_size; + UnlockedAdd64 (&total_bytes_allocated, new_size); + + rval = pool->pos; + pool->pos += size; + } + } + + return rval; +} + +/** + * mono_mempool_alloc0: + * + * same as \c mono_mempool_alloc, but fills memory with zero. + */ +gpointer +mono_mempool_alloc0 (MonoMemPool *pool, guint size) +{ + gpointer rval; + + // For the fast path, repeat the first few lines of mono_mempool_alloc + size = ALIGN_SIZE (size); + rval = pool->pos; + pool->pos = (guint8*)rval + size; + + // If that doesn't work fall back on mono_mempool_alloc to handle new chunk allocation + if (G_UNLIKELY (pool->pos >= pool->end)) { + rval = mono_mempool_alloc (pool, size); + } +#ifdef TRACE_ALLOCATIONS + else if (pool == mono_get_corlib ()->mempool) { + mono_backtrace (size); + } +#endif + + memset (rval, 0, size); + return rval; +} + +/** + * mono_mempool_contains_addr: + * + * Determines whether \p addr is inside the memory used by the mempool. + */ +gboolean +mono_mempool_contains_addr (MonoMemPool *pool, + gpointer addr) +{ + MonoMemPool *p = pool; + + while (p) { + if (addr >= (gpointer)p && addr < (gpointer)((guint8*)p + p->size)) + return TRUE; + p = p->next; + } + + return FALSE; +} + +/** + * mono_mempool_strdup: + * + * Same as strdup, but allocates memory from the mempool. + * Returns: a pointer to the newly allocated string data inside the mempool. + */ +char* +mono_mempool_strdup (MonoMemPool *pool, + const char *s) +{ + int l; + char *res; + + if (s == NULL) + return NULL; + + l = strlen (s); + res = (char *)mono_mempool_alloc (pool, l + 1); + memcpy (res, s, l + 1); + + return res; +} + +char* +mono_mempool_strdup_vprintf (MonoMemPool *pool, const char *format, va_list args) +{ + size_t buflen; + char *buf; + va_list args2; + va_copy (args2, args); + int len = vsnprintf (NULL, 0, format, args2); + va_end (args2); + + if (len >= 0 && (buf = (char*)mono_mempool_alloc (pool, (buflen = (size_t) (len + 1)))) != NULL) { + vsnprintf (buf, buflen, format, args); + } else { + buf = NULL; + } + return buf; +} + +char* +mono_mempool_strdup_printf (MonoMemPool *pool, const char *format, ...) +{ + char *buf; + va_list args; + va_start (args, format); + buf = mono_mempool_strdup_vprintf (pool, format, args); + va_end (args); + return buf; +} + +/** + * mono_mempool_get_allocated: + * + * Return the amount of memory allocated for this mempool. + */ +guint32 +mono_mempool_get_allocated (MonoMemPool *pool) +{ + return pool->d.allocated; +} + +/** + * mono_mempool_get_bytes_allocated: + * + * Return the number of bytes currently allocated for mempools. + */ +long +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/unity-2019.4.24f1-mbe/mono/metadata/mempool.h b/unity-2019.4.24f1-mbe/mono/metadata/mempool.h new file mode 100644 index 000000000..7e75cd138 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/mempool.h @@ -0,0 +1,51 @@ +/** + * \file + */ + +#ifndef _MONO_MEMPOOL_H_ +#define _MONO_MEMPOOL_H_ + +#include + +MONO_BEGIN_DECLS + +typedef struct _MonoMemPool MonoMemPool; + +MONO_API MonoMemPool * +mono_mempool_new (void); + +MONO_API MonoMemPool * +mono_mempool_new_size (int initial_size); + +MONO_API void +mono_mempool_destroy (MonoMemPool *pool); + +MONO_API void +mono_mempool_invalidate (MonoMemPool *pool); + +MONO_API void +mono_mempool_stats (MonoMemPool *pool); + +MONO_API void* +mono_mempool_alloc (MonoMemPool *pool, unsigned int size); + +MONO_API void* +mono_mempool_alloc0 (MonoMemPool *pool, unsigned int size); + +MONO_API mono_bool +mono_mempool_contains_addr (MonoMemPool *pool, void* addr); + +MONO_API char* +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/unity-2019.4.24f1-mbe/mono/metadata/metadata-cross-helpers.c b/unity-2019.4.24f1-mbe/mono/metadata/metadata-cross-helpers.c new file mode 100644 index 000000000..c4c81eaf3 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/metadata-cross-helpers.c @@ -0,0 +1,149 @@ +/** + * \file + */ + +#include + +#include "config.h" +#include + +#include +#include +#include +#include +#include +#ifdef HAVE_SGEN_GC +#include +#endif + +static int +dump_arch (void) +{ +#if defined (TARGET_X86) + g_print ("#ifdef TARGET_X86\n"); +#elif defined (TARGET_AMD64) + g_print ("#ifdef TARGET_AMD64\n"); +#elif defined (TARGET_ARM) + g_print ("#ifdef TARGET_ARM\n"); +#elif defined (TARGET_ARM64) + g_print ("#ifdef TARGET_ARM64\n"); +#else + return 0; +#endif + return 1; +} + +static int +dump_os (void) +{ +#if defined (HOST_WIN32) + g_print ("#ifdef TARGET_WIN32\n"); +#elif defined (HOST_ANDROID) + g_print ("#ifdef TARGET_ANDROID\n"); +#elif defined (HOST_DARWIN) + g_print ("#ifdef TARGET_OSX\n"); +#elif defined (PLATFORM_IOS) + g_print ("#ifdef TARGET_IOS\n"); +#else + return 0; +#endif + return 1; +} + +void +mono_dump_metadata_offsets (void); + +void +mono_dump_metadata_offsets (void) +{ +#ifdef USED_CROSS_COMPILER_OFFSETS + g_print ("not using native offsets\n"); +#else + g_print ("#ifndef USED_CROSS_COMPILER_OFFSETS\n"); + + if (!dump_arch ()) { + g_print ("#error failed to figure out the current arch\n"); + return; + } + + if (!dump_os ()) { + g_print ("#error failed to figure out the current OS\n"); + return; + } + +#ifdef HAVE_SGEN_GC + g_print ("#ifndef HAVE_BOEHM_GC\n"); +#elif HAVE_BOEHM_GC + g_print ("#ifndef HAVE_SGEN_GC\n"); +#else + g_print ("#error no gc conf not supported\n"); + return; +#endif + + g_print ("#define HAS_CROSS_COMPILER_OFFSETS\n"); + g_print ("#if defined (USE_CROSS_COMPILE_OFFSETS) || defined (MONO_CROSS_COMPILE)\n"); + g_print ("#if !defined (DISABLE_METADATA_OFFSETS)\n"); + g_print ("#define USED_CROSS_COMPILER_OFFSETS\n"); + +#define DISABLE_JIT_OFFSETS +#define DECL_OFFSET2(struct,field,offset) this_should_not_happen +#define DECL_ALIGN2(type,size) this_should_not_happen + +#define DECL_OFFSET(struct,field) g_print ("DECL_OFFSET2(%s,%s,%d)\n", #struct, #field, (int)MONO_STRUCT_OFFSET (struct, field)); +#define DECL_ALIGN(type) g_print ("DECL_ALIGN2(%s,%d)\n", #type, (int)MONO_ABI_ALIGNOF (type)); +#define DECL_SIZE(type) g_print ("DECL_SIZE2(%s,%d)\n", #type, (int)MONO_ABI_SIZEOF (type)); +#include + + g_print ("#endif //disable metadata check\n"); + g_print ("#endif //gc check\n"); +#endif +} + +void +mono_metadata_cross_helpers_run (void); + +/* + * mono_metadata_cross_helpers_run: + * + * Check that the offsets given by object-offsets.h match the offsets + * on the host. This only checks the metadata offsets. + */ +void +mono_metadata_cross_helpers_run (void) +{ +#if defined (HAS_CROSS_COMPILER_OFFSETS) && !defined (MONO_CROSS_COMPILE) + gboolean is_broken = FALSE; + +#define DISABLE_JIT_OFFSETS +#define USE_CROSS_COMPILE_OFFSETS +#define DECL_OFFSET(struct,field) this_should_not_happen_for_cross_fields +#define DECL_OFFSET2(struct,field,offset) \ + if ((int)G_STRUCT_OFFSET (struct, field) != offset) { \ + g_print (#struct ":" #field " invalid struct offset %d (expected %d)\n", \ + offset, \ + (int)G_STRUCT_OFFSET (struct, field)); \ + is_broken = TRUE; \ + } +#define DECL_ALIGN(type) this_should_not_happen_for_cross_align +#define DECL_ALIGN2(name,size) \ + if (MONO_ALIGN_ ## name != size) { \ + g_print (#name ": invalid alignment %d (expected %d)\n", \ + size, \ + MONO_ALIGN_ ## name); \ + is_broken = TRUE; \ + } +#define DECL_SIZE(type) this_should_not_happen_for_cross_size +#define DECL_SIZE2(name,size) \ + if (MONO_SIZEOF_ ## name != size) { \ + g_print (#name ": invalid size %d (expected %d)\n", \ + size, \ + MONO_SIZEOF_ ## name); \ + is_broken = TRUE; \ + } + +#include + + g_assert (!is_broken); +#endif +} + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/metadata-internals.h b/unity-2019.4.24f1-mbe/mono/metadata/metadata-internals.h new file mode 100644 index 000000000..d14ef8488 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/metadata-internals.h @@ -0,0 +1,986 @@ +/** + * \file + */ + +#ifndef __MONO_METADATA_INTERNALS_H__ +#define __MONO_METADATA_INTERNALS_H__ + +#include "mono/metadata/image.h" +#include "mono/metadata/blob.h" +#include "mono/metadata/cil-coff.h" +#include "mono/metadata/mempool.h" +#include "mono/metadata/domain-internals.h" +#include "mono/metadata/mono-hash.h" +#include "mono/utils/mono-compiler.h" +#include "mono/utils/mono-dl.h" +#include "mono/utils/monobitset.h" +#include "mono/utils/mono-property-hash.h" +#include "mono/utils/mono-value-hash.h" +#include +#include "mono/utils/mono-conc-hashtable.h" + +struct _MonoType { + union { + MonoClass *klass; /* for VALUETYPE and CLASS */ + MonoType *type; /* for PTR */ + MonoArrayType *array; /* for ARRAY */ + MonoMethodSignature *method; + MonoGenericParam *generic_param; /* for VAR and MVAR */ + MonoGenericClass *generic_class; /* for GENERICINST */ + } data; + unsigned int attrs : 16; /* param attributes or field flags */ + MonoTypeEnum type : 8; + unsigned int num_mods : 6; /* max 64 modifiers follow at the end */ + unsigned int byref : 1; + unsigned int pinned : 1; /* valid when included in a local var signature */ + MonoCustomMod modifiers [MONO_ZERO_LEN_ARRAY]; /* this may grow */ +}; + +#define MONO_SIZEOF_TYPE (offsetof (struct _MonoType, modifiers)) + +#define MONO_SECMAN_FLAG_INIT(x) (x & 0x2) +#define MONO_SECMAN_FLAG_GET_VALUE(x) (x & 0x1) +#define MONO_SECMAN_FLAG_SET_VALUE(x,y) do { x = ((y) ? 0x3 : 0x2); } while (0) + +#define MONO_PUBLIC_KEY_TOKEN_LENGTH 17 + +#define MONO_PROCESSOR_ARCHITECTURE_NONE 0 +#define MONO_PROCESSOR_ARCHITECTURE_MSIL 1 +#define MONO_PROCESSOR_ARCHITECTURE_X86 2 +#define MONO_PROCESSOR_ARCHITECTURE_IA64 3 +#define MONO_PROCESSOR_ARCHITECTURE_AMD64 4 +#define MONO_PROCESSOR_ARCHITECTURE_ARM 5 + +#if !defined(DISABLE_JIT) || !defined(DISABLE_INTERPRETER) +/* Some VES is available at runtime */ +#define ENABLE_ILGEN +#endif + +struct _MonoAssemblyName { + const char *name; + const char *culture; + const char *hash_value; + const mono_byte* public_key; + // string of 16 hex chars + 1 NULL + mono_byte public_key_token [MONO_PUBLIC_KEY_TOKEN_LENGTH]; + uint32_t hash_alg; + uint32_t hash_len; + uint32_t flags; + uint16_t major, minor, build, revision, arch; +}; + +struct MonoTypeNameParse { + char *name_space; + char *name; + MonoAssemblyName assembly; + GList *modifiers; /* 0 -> byref, -1 -> pointer, > 0 -> array rank */ + GPtrArray *type_arguments; + GList *nested; +}; + +struct _MonoAssembly { + /* + * The number of appdomains which have this assembly loaded plus the number of + * assemblies referencing this assembly through an entry in their image->references + * arrays. The latter is needed because entries in the image->references array + * might point to assemblies which are only loaded in some appdomains, and without + * the additional reference, they can be freed at any time. + * The ref_count is initially 0. + */ + int ref_count; /* use atomic operations only */ + char *basedir; + MonoAssemblyName aname; + MonoImage *image; + GSList *friend_assembly_names; /* Computed by mono_assembly_load_friends () */ + guint8 friend_assembly_names_inited; + guint8 in_gac; + guint8 dynamic; + guint8 corlib_internal; + gboolean ref_only; + guint8 wrap_non_exception_throws; + guint8 wrap_non_exception_throws_inited; + guint8 jit_optimizer_disabled; + guint8 jit_optimizer_disabled_inited; + /* security manager flags (one bit is for lazy initialization) */ + guint32 ecma:2; /* Has the ECMA key */ + guint32 aptc:2; /* Has the [AllowPartiallyTrustedCallers] attributes */ + guint32 fulltrust:2; /* Has FullTrust permission */ + guint32 unmanaged:2; /* Has SecurityPermissionFlag.UnmanagedCode permission */ + guint32 skipverification:2; /* Has SecurityPermissionFlag.SkipVerification permission */ +}; + +typedef struct { + /* + * indexed by MonoMethodSignature + * Protected by the marshal lock + */ + GHashTable *delegate_invoke_cache; + GHashTable *delegate_begin_invoke_cache; + GHashTable *delegate_end_invoke_cache; + GHashTable *runtime_invoke_cache; + GHashTable *runtime_invoke_vtype_cache; + GHashTable *runtime_invoke_sig_cache; + + /* + * indexed by SignaturePointerPair + */ + GHashTable *delegate_abstract_invoke_cache; + + /* + * indexed by MonoMethod pointers + * Protected by the marshal lock + */ + GHashTable *runtime_invoke_direct_cache; + GHashTable *managed_wrapper_cache; + + GHashTable *native_wrapper_cache; + GHashTable *native_wrapper_aot_cache; + GHashTable *native_wrapper_check_cache; + GHashTable *native_wrapper_aot_check_cache; + + GHashTable *native_func_wrapper_aot_cache; + GHashTable *remoting_invoke_cache; + GHashTable *synchronized_cache; + GHashTable *unbox_wrapper_cache; + GHashTable *cominterop_invoke_cache; + GHashTable *cominterop_wrapper_cache; /* LOCKING: marshal lock */ + GHashTable *thunk_invoke_cache; +} MonoWrapperCaches; + +typedef struct { + const char* data; + guint32 size; +} MonoStreamHeader; + +struct _MonoTableInfo { + const char *base; + guint rows : 24; + guint row_size : 8; + + /* + * Tables contain up to 9 columns and the possible sizes of the + * fields in the documentation are 1, 2 and 4 bytes. So we + * can encode in 2 bits the size. + * + * A 32 bit value can encode the resulting size + * + * The top eight bits encode the number of columns in the table. + * we only need 4, but 8 is aligned no shift required. + */ + guint32 size_bitfield; +}; + +#define REFERENCE_MISSING ((gpointer) -1) + +typedef struct _MonoDllMap MonoDllMap; + +typedef struct { + gboolean (*match) (MonoImage*); + gboolean (*load_pe_data) (MonoImage*); + gboolean (*load_cli_data) (MonoImage*); + gboolean (*load_tables) (MonoImage*); +} MonoImageLoader; + +struct _MonoImage { + /* + * This count is incremented during these situations: + * - An assembly references this MonoImage though its 'image' field + * - This MonoImage is present in the 'files' field of an image + * - This MonoImage is present in the 'modules' field of an image + * - A thread is holding a temporary reference to this MonoImage between + * calls to mono_image_open and mono_image_close () + */ + int ref_count; + + /* 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; + guint8 fileio_used : 1; + +#ifdef HOST_WIN32 + /* Module was loaded using LoadLibrary. */ + guint8 is_module_handle : 1; + + /* Module entry point is _CorDllMain. */ + guint8 has_entry_point : 1; +#endif + + /* Whenever this is a dynamically emitted module */ + guint8 dynamic : 1; + + /* Whenever this is a reflection only image */ + guint8 ref_only : 1; + + /* Whenever this image contains uncompressed metadata */ + guint8 uncompressed_metadata : 1; + + /* Whenever this image contains metadata only without PE data */ + guint8 metadata_only : 1; + + /* Whether this image belongs to load-from context */ + guint8 load_from_context: 1; + + guint8 checked_module_cctor : 1; + guint8 has_module_cctor : 1; + + guint8 idx_string_wide : 1; + guint8 idx_guid_wide : 1; + guint8 idx_blob_wide : 1; + + /* Whenever this image is considered as platform code for the CoreCLR security model */ + guint8 core_clr_platform_code : 1; + + /* The path to the file for this image. */ + char *name; + + /* The assembly name reported in the file for this image (expected to be NULL for a netmodule) */ + const char *assembly_name; + + /* The module name reported in the file for this image (could be NULL for a malformed file) */ + const char *module_name; + + char *version; + gint16 md_version_major, md_version_minor; + char *guid; + void *image_info; + MonoMemPool *mempool; /*protected by the image lock*/ + + char *raw_metadata; + + MonoStreamHeader heap_strings; + MonoStreamHeader heap_us; + MonoStreamHeader heap_blob; + MonoStreamHeader heap_guid; + MonoStreamHeader heap_tables; + MonoStreamHeader heap_pdb; + + const char *tables_base; + + /* For PPDB files */ + guint64 referenced_tables; + int *referenced_table_rows; + + /**/ + MonoTableInfo tables [MONO_TABLE_NUM]; + + /* + * references is initialized only by using the mono_assembly_open + * function, and not by using the lowlevel mono_image_open. + * + * It is NULL terminated. + */ + MonoAssembly **references; + int nreferences; + + /* Code files in the assembly. The main assembly has a "file" table and also a "module" + * table, where the module table is a subset of the file table. We track both lists, + * and because we can lazy-load them at different times we reference-increment both. + */ + MonoImage **modules; + guint32 module_count; + gboolean *modules_loaded; + + MonoImage **files; + guint32 file_count; + + gpointer aot_module; + + guint8 aotid[16]; + + /* + * The Assembly this image was loaded from. + */ + MonoAssembly *assembly; + + /* + * Indexed by method tokens and typedef tokens. + */ + GHashTable *method_cache; /*protected by the image lock*/ + MonoInternalHashTable class_cache; + + /* Indexed by memberref + methodspec tokens */ + GHashTable *methodref_cache; /*protected by the image lock*/ + + /* + * Indexed by fielddef and memberref tokens + */ + MonoConcurrentHashTable *field_cache; /*protected by the image lock*/ + + /* indexed by typespec tokens. */ + MonoConcurrentHashTable *typespec_cache; /* protected by the image lock */ + /* indexed by token */ + GHashTable *memberref_signatures; + GHashTable *helper_signatures; + + /* Indexed by blob heap indexes */ + GHashTable *method_signatures; + + /* + * Indexes namespaces to hash tables that map class name to typedef token. + */ + GHashTable *name_cache; /*protected by the image lock*/ + + /* + * Indexed by MonoClass + */ + GHashTable *array_cache; + GHashTable *ptr_cache; + + GHashTable *szarray_cache; + /* This has a separate lock to improve scalability */ + mono_mutex_t szarray_cache_lock; + + /* + * indexed by SignaturePointerPair + */ + GHashTable *delegate_bound_static_invoke_cache; + GHashTable *native_func_wrapper_cache; + + /* + * indexed by MonoMethod pointers + */ + GHashTable *runtime_invoke_vcall_cache; + GHashTable *wrapper_param_names; + GHashTable *array_accessor_cache; + + /* + * indexed by MonoClass pointers + */ + GHashTable *ldfld_wrapper_cache; + GHashTable *ldflda_wrapper_cache; + GHashTable *stfld_wrapper_cache; + GHashTable *isinst_cache; + + GHashTable *icall_wrapper_cache; + GHashTable *castclass_cache; + GHashTable *proxy_isinst_cache; + GHashTable *rgctx_template_hash; /* LOCKING: templates lock */ + + /* Contains rarely used fields of runtime structures belonging to this image */ + MonoPropertyHash *property_hash; + + void *reflection_info; + + /* + * user_info is a public field and is not touched by the + * metadata engine + */ + void *user_info; + + /* dll map entries */ + MonoDllMap *dll_map; + + /* interfaces IDs from this image */ + /* protected by the classes lock */ + MonoBitSet *interface_bitset; + + /* when the image is being closed, this is abused as a list of + malloc'ed regions to be freed. */ + GSList *reflection_info_unregister_classes; + + /* List of dependent image sets containing this image */ + /* Protected by image_sets_lock */ + GSList *image_sets; + + /* Caches for wrappers that DO NOT reference generic */ + /* arguments */ + MonoWrapperCaches wrapper_caches; + + /* Caches for MonoClass-es representing anon generic params */ + MonoClass **var_cache_fast; + MonoClass **mvar_cache_fast; + GHashTable *var_cache_slow; + GHashTable *mvar_cache_slow; + GHashTable *var_cache_constrained; + GHashTable *mvar_cache_constrained; + + /* Maps malloc-ed char* pinvoke scope -> MonoDl* */ + GHashTable *pinvoke_scopes; + + /* Maps malloc-ed char* pinvoke scope -> malloced-ed char* filename */ + GHashTable *pinvoke_scope_filenames; + + /* The loader used to load this image */ + MonoImageLoader *loader; + + // Containers for MonoGenericParams associated with this image but not with any specific class or method. Created on demand. + // This could happen, for example, for MonoTypes associated with TypeSpec table entries. + MonoGenericContainer *anonymous_generic_class_container; + MonoGenericContainer *anonymous_generic_method_container; + + gboolean weak_fields_inited; + /* Contains 1 based indexes */ + GHashTable *weak_field_indexes; + + /* + * No other runtime locks must be taken while holding this lock. + * It's meant to be used only to mutate and query structures part of this image. + */ + mono_mutex_t lock; +}; + +/* + * Generic instances depend on many images, and they need to be deleted if one + * of the images they depend on is unloaded. For example, + * List depends on both List's image and Foo's image. + * A MonoImageSet is the owner of all generic instances depending on the same set of + * images. + */ +typedef struct { + int nimages; + MonoImage **images; + + // Generic-specific caches + GHashTable *ginst_cache, *gmethod_cache, *gsignature_cache; + MonoConcurrentHashTable *gclass_cache; + + /* mirror caches of ones already on MonoImage. These ones contain generics */ + GHashTable *szarray_cache, *array_cache, *ptr_cache; + + MonoWrapperCaches wrapper_caches; + + /* Indexed by MonoGenericParam pointers */ + GHashTable **gshared_types; + /* The length of the above array */ + int gshared_types_len; + + mono_mutex_t lock; + + /* + * Memory for generic instances owned by this image set should be allocated from + * this mempool, using the mono_image_set_alloc family of functions. + */ + MonoMemPool *mempool; +} MonoImageSet; + +enum { + MONO_SECTION_TEXT, + MONO_SECTION_RSRC, + MONO_SECTION_RELOC, + MONO_SECTION_MAX +}; + +typedef struct { + GHashTable *hash; + char *data; + guint32 alloc_size; /* malloced bytes */ + guint32 index; + guint32 offset; /* from start of metadata */ +} MonoDynamicStream; + +typedef struct { + guint32 alloc_rows; + guint32 rows; + guint8 row_size; /* calculated later with column_sizes */ + guint8 columns; + guint32 next_idx; + guint32 *values; /* rows * columns */ +} MonoDynamicTable; + +/* "Dynamic" assemblies and images arise from System.Reflection.Emit */ +struct _MonoDynamicAssembly { + MonoAssembly assembly; + char *strong_name; + guint32 strong_name_size; + guint8 run; + guint8 save; + MonoDomain *domain; +}; + +struct _MonoDynamicImage { + MonoImage image; + guint32 meta_size; + guint32 text_rva; + guint32 metadata_rva; + guint32 image_base; + guint32 cli_header_offset; + guint32 iat_offset; + guint32 idt_offset; + guint32 ilt_offset; + guint32 imp_names_offset; + struct { + guint32 rva; + guint32 size; + guint32 offset; + guint32 attrs; + } sections [MONO_SECTION_MAX]; + GHashTable *typespec; + GHashTable *typeref; + GHashTable *handleref; + MonoGHashTable *tokens; + GHashTable *blob_cache; + GHashTable *standalonesig_cache; + GList *array_methods; + GPtrArray *gen_params; + MonoGHashTable *token_fixups; + GHashTable *method_to_table_idx; + GHashTable *field_to_table_idx; + GHashTable *method_aux_hash; + GHashTable *vararg_aux_hash; + MonoGHashTable *generic_def_objects; + /* + * Maps final token values to the object they describe. + */ + MonoGHashTable *remapped_tokens; + gboolean run; + gboolean save; + gboolean initial_image; + guint32 pe_kind, machine; + char *strong_name; + guint32 strong_name_size; + char *win32_res; + guint32 win32_res_size; + guint8 *public_key; + int public_key_len; + MonoDynamicStream sheap; + MonoDynamicStream code; /* used to store method headers and bytecode */ + MonoDynamicStream resources; /* managed embedded resources */ + MonoDynamicStream us; + MonoDynamicStream blob; + MonoDynamicStream tstream; + MonoDynamicStream guid; + MonoDynamicTable tables [MONO_TABLE_NUM]; + MonoClass *wrappers_type; /*wrappers are bound to this type instead of */ +}; + +/* Contains information about assembly binding */ +typedef struct _MonoAssemblyBindingInfo { + char *name; + char *culture; + guchar public_key_token [MONO_PUBLIC_KEY_TOKEN_LENGTH]; + int major; + int minor; + AssemblyVersionSet old_version_bottom; + AssemblyVersionSet old_version_top; + AssemblyVersionSet new_version; + guint has_old_version_bottom : 1; + guint has_old_version_top : 1; + guint has_new_version : 1; + guint is_valid : 1; + gint32 domain_id; /*Needed to unload per-domain binding*/ +} MonoAssemblyBindingInfo; + +struct _MonoMethodHeader { + const unsigned char *code; +#ifdef MONO_SMALL_CONFIG + guint16 code_size; +#else + guint32 code_size; +#endif + guint16 max_stack : 15; + unsigned int is_transient: 1; /* mono_metadata_free_mh () will actually free this header */ + unsigned int num_clauses : 15; + /* if num_locals != 0, then the following apply: */ + unsigned int init_locals : 1; + guint16 num_locals; + MonoExceptionClause *clauses; + MonoType *locals [MONO_ZERO_LEN_ARRAY]; +}; + +typedef struct { + guint32 code_size; + gboolean has_clauses; +} MonoMethodHeaderSummary; + +#define MONO_SIZEOF_METHOD_HEADER (sizeof (struct _MonoMethodHeader) - MONO_ZERO_LEN_ARRAY * SIZEOF_VOID_P) + +struct _MonoMethodSignature { + MonoType *ret; +#ifdef MONO_SMALL_CONFIG + guint8 param_count; + gint8 sentinelpos; + unsigned int generic_param_count : 5; +#else + guint16 param_count; + gint16 sentinelpos; + unsigned int generic_param_count : 16; +#endif + unsigned int call_convention : 6; + unsigned int hasthis : 1; + unsigned int explicit_this : 1; + unsigned int pinvoke : 1; + unsigned int is_inflated : 1; + unsigned int has_type_parameters : 1; + MonoType *params [MONO_ZERO_LEN_ARRAY]; +}; + +/* + * AOT cache configuration loaded from config files. + * Doesn't really belong here. + */ +typedef struct { + /* + * Enable aot caching for applications whose main assemblies are in + * this list. + */ + GSList *apps; + GSList *assemblies; + 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 +image_is_dynamic (MonoImage *image) +{ +#ifdef DISABLE_REFLECTION_EMIT + return FALSE; +#else + return image->dynamic; +#endif +} + +static inline gboolean +assembly_is_dynamic (MonoAssembly *assembly) +{ +#ifdef DISABLE_REFLECTION_EMIT + return FALSE; +#else + return assembly->dynamic; +#endif +} + +/* for use with allocated memory blocks (assumes alignment is to 8 bytes) */ +guint mono_aligned_addr_hash (gconstpointer ptr); + +void +mono_image_check_for_module_cctor (MonoImage *image); + +gpointer +mono_image_alloc (MonoImage *image, guint size); + +gpointer +mono_image_alloc0 (MonoImage *image, guint size); + +#define mono_image_new0(image,type,size) ((type *) mono_image_alloc0 (image, sizeof (type)* (size))) + +char* +mono_image_strdup (MonoImage *image, const char *s); + +char* +mono_image_strdup_vprintf (MonoImage *image, const char *format, va_list args); + +char* +mono_image_strdup_printf (MonoImage *image, const char *format, ...) MONO_ATTR_FORMAT_PRINTF(2,3);; + +GList* +g_list_prepend_image (MonoImage *image, GList *list, gpointer data); + +GSList* +g_slist_append_image (MonoImage *image, GSList *list, gpointer data); + +void +mono_image_lock (MonoImage *image); + +void +mono_image_unlock (MonoImage *image); + +gpointer +mono_image_property_lookup (MonoImage *image, gpointer subject, guint32 property); + +void +mono_image_property_insert (MonoImage *image, gpointer subject, guint32 property, gpointer value); + +void +mono_image_property_remove (MonoImage *image, gpointer subject); + +gboolean +mono_image_close_except_pools (MonoImage *image); + +void +mono_image_close_finish (MonoImage *image); + +typedef void (*MonoImageUnloadFunc) (MonoImage *image, gpointer user_data); + +void +mono_install_image_unload_hook (MonoImageUnloadFunc func, gpointer user_data); + +void +mono_remove_image_unload_hook (MonoImageUnloadFunc func, gpointer user_data); + +void +mono_install_image_loader (const MonoImageLoader *loader); + +void +mono_image_append_class_to_reflection_info_set (MonoClass *klass); + +gpointer +mono_image_set_alloc (MonoImageSet *set, guint size); + +gpointer +mono_image_set_alloc0 (MonoImageSet *set, guint size); + +void +mono_image_set_lock (MonoImageSet *set); + +void +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 +mono_image_load_cli_header (MonoImage *image, MonoCLIImageInfo *iinfo); + +gboolean +mono_image_load_metadata (MonoImage *image, MonoCLIImageInfo *iinfo); + +MonoType* +mono_metadata_get_shared_type (MonoType *type); + +void +mono_metadata_clean_for_image (MonoImage *image); + +void +mono_metadata_clean_generic_classes_for_image (MonoImage *image); + +MONO_API void +mono_metadata_cleanup (void); + +const char * mono_meta_table_name (int table); +void mono_metadata_compute_table_bases (MonoImage *meta); + +gboolean +mono_metadata_interfaces_from_typedef_full (MonoImage *image, + guint32 table_index, + MonoClass ***interfaces, + guint *count, + gboolean heap_alloc_result, + MonoGenericContext *context, + MonoError *error); + +MONO_API MonoMethodSignature * +mono_metadata_parse_method_signature_full (MonoImage *image, + MonoGenericContainer *generic_container, + int def, + const char *ptr, + const char **rptr, + MonoError *error); + +MONO_API MonoMethodHeader * +mono_metadata_parse_mh_full (MonoImage *image, + MonoGenericContainer *container, + const char *ptr, + MonoError *error); + +MonoMethodSignature *mono_metadata_parse_signature_checked (MonoImage *image, + uint32_t token, + MonoError *error); + +gboolean +mono_method_get_header_summary (MonoMethod *method, MonoMethodHeaderSummary *summary); + +int* mono_metadata_get_param_attrs (MonoImage *m, int def, int param_count); +gboolean mono_metadata_method_has_param_attrs (MonoImage *m, int def); + +guint +mono_metadata_generic_context_hash (const MonoGenericContext *context); + +gboolean +mono_metadata_generic_context_equal (const MonoGenericContext *g1, + const MonoGenericContext *g2); + +MonoGenericInst * +mono_metadata_parse_generic_inst (MonoImage *image, + MonoGenericContainer *container, + int count, + const char *ptr, + const char **rptr, + MonoError *error); + +MonoGenericInst * +mono_metadata_get_generic_inst (int type_argc, + MonoType **type_argv); + +MonoGenericInst * +mono_metadata_get_canonical_generic_inst (MonoGenericInst *candidate); + +MonoGenericClass * +mono_metadata_lookup_generic_class (MonoClass *gclass, + MonoGenericInst *inst, + gboolean is_dynamic); + +MonoGenericInst * mono_metadata_inflate_generic_inst (MonoGenericInst *ginst, MonoGenericContext *context, MonoError *error); + +guint +mono_metadata_generic_param_hash (MonoGenericParam *p); + +gboolean +mono_metadata_generic_param_equal (MonoGenericParam *p1, MonoGenericParam *p2); + +void mono_dynamic_stream_reset (MonoDynamicStream* stream); +MONO_API void mono_assembly_addref (MonoAssembly *assembly); +void mono_assembly_load_friends (MonoAssembly* ass); +gboolean mono_assembly_has_skip_verification (MonoAssembly* ass); + +void mono_assembly_release_gc_roots (MonoAssembly *assembly); +gboolean mono_assembly_close_except_image_pools (MonoAssembly *assembly); +void mono_assembly_close_finish (MonoAssembly *assembly); + + +gboolean mono_public_tokens_are_equal (const unsigned char *pubt1, const unsigned char *pubt2); + +void mono_config_parse_publisher_policy (const char *filename, MonoAssemblyBindingInfo *binding_info); +void mono_config_parse_assembly_bindings (const char *filename, int major, int minor, void *user_data, + void (*infocb)(MonoAssemblyBindingInfo *info, void *user_data)); + +gboolean +mono_assembly_name_parse_full (const char *name, + MonoAssemblyName *aname, + gboolean save_public_key, + gboolean *is_version_defined, + gboolean *is_token_defined); + +gboolean +mono_assembly_fill_assembly_name_full (MonoImage *image, MonoAssemblyName *aname, gboolean copyBlobs); + + +MONO_API guint32 mono_metadata_get_generic_param_row (MonoImage *image, guint32 token, guint32 *owner); + +void mono_unload_interface_ids (MonoBitSet *bitset); + + +MonoType *mono_metadata_type_dup (MonoImage *image, const MonoType *original); +MonoMethodSignature *mono_metadata_signature_dup_full (MonoImage *image,MonoMethodSignature *sig); +MonoMethodSignature *mono_metadata_signature_dup_mempool (MonoMemPool *mp, MonoMethodSignature *sig); +MonoMethodSignature *mono_metadata_signature_dup_add_this (MonoImage *image, MonoMethodSignature *sig, MonoClass *klass); + +MonoGenericInst * +mono_get_shared_generic_inst (MonoGenericContainer *container); + +int +mono_type_stack_size_internal (MonoType *t, int *align, gboolean allow_open); + +MONO_API void mono_type_get_desc (GString *res, MonoType *type, mono_bool include_namespace); + +gboolean +mono_metadata_type_equal_full (MonoType *t1, MonoType *t2, gboolean signature_only); + +MonoMarshalSpec * +mono_metadata_parse_marshal_spec_full (MonoImage *image, MonoImage *parent_image, const char *ptr); + +guint mono_metadata_generic_inst_hash (gconstpointer data); +gboolean mono_metadata_generic_inst_equal (gconstpointer ka, gconstpointer kb); + +MONO_API void +mono_metadata_field_info_with_mempool ( + MonoImage *meta, + guint32 table_index, + guint32 *offset, + guint32 *rva, + MonoMarshalSpec **marshal_spec); + +MonoClassField* +mono_metadata_get_corresponding_field_from_generic_type_definition (MonoClassField *field); + +MonoEvent* +mono_metadata_get_corresponding_event_from_generic_type_definition (MonoEvent *event); + +MonoProperty* +mono_metadata_get_corresponding_property_from_generic_type_definition (MonoProperty *property); + +guint32 +mono_metadata_signature_size (MonoMethodSignature *sig); + +guint mono_metadata_str_hash (gconstpointer v1); + +gboolean mono_image_load_pe_data (MonoImage *image); + +gboolean mono_image_load_cli_data (MonoImage *image); + +void mono_image_load_names (MonoImage *image); + +MonoImage *mono_image_open_raw (const char *fname, MonoImageOpenStatus *status); + +MonoImage *mono_image_open_metadata_only (const char *fname, MonoImageOpenStatus *status); + +MonoImage *mono_image_open_from_data_internal (char *data, guint32 data_len, gboolean need_copy, MonoImageOpenStatus *status, gboolean refonly, gboolean metadata_only, const char *name); + +MonoException *mono_get_exception_field_access_msg (const char *msg); + +MonoException *mono_get_exception_method_access_msg (const char *msg); + +MonoMethod* method_from_method_def_or_ref (MonoImage *m, guint32 tok, MonoGenericContext *context, MonoError *error); + +MonoMethod *mono_get_method_constrained_with_method (MonoImage *image, MonoMethod *method, MonoClass *constrained_class, MonoGenericContext *context, MonoError *error); +MonoMethod *mono_get_method_constrained_checked (MonoImage *image, guint32 token, MonoClass *constrained_class, MonoGenericContext *context, MonoMethod **cil_method, MonoError *error); + +void mono_type_set_alignment (MonoTypeEnum type, int align); + +MonoAotCacheConfig *mono_get_aot_cache_config (void); +MonoType * +mono_type_create_from_typespec_checked (MonoImage *image, guint32 type_spec, MonoError *error); + +MonoMethodSignature* +mono_method_get_signature_checked (MonoMethod *method, MonoImage *image, guint32 token, MonoGenericContext *context, MonoError *error); + +MonoMethod * +mono_get_method_checked (MonoImage *image, guint32 token, MonoClass *klass, MonoGenericContext *context, MonoError *error); + +guint32 +mono_metadata_localscope_from_methoddef (MonoImage *meta, guint32 index); + +void +mono_wrapper_caches_free (MonoWrapperCaches *cache); + +MonoWrapperCaches* +mono_method_get_wrapper_cache (MonoMethod *method); + +MonoWrapperCaches* +mono_method_get_wrapper_cache (MonoMethod *method); + +MonoType* +mono_metadata_parse_type_checked (MonoImage *m, MonoGenericContainer *container, short opt_attrs, gboolean transient, const char *ptr, const char **rptr, MonoError *error); + +MonoGenericContainer * +get_anonymous_container_for_image (MonoImage *image, gboolean is_mvar); + +char * +mono_image_set_description (MonoImageSet *); + +MonoImageSet * +mono_find_image_set_owner (void *ptr); + +MONO_API void +mono_loader_register_module (const char *name, MonoDl *module); + +gboolean +mono_assembly_is_problematic_version (const char *name, guint16 major, guint16 minor, guint16 build, guint16 revision); + +void +mono_ginst_get_desc (GString *str, MonoGenericInst *ginst); + +void +mono_loader_set_strict_strong_names (gboolean enabled); + +gboolean +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/unity-2019.4.24f1-mbe/mono/metadata/metadata-verify.c b/unity-2019.4.24f1-mbe/mono/metadata/metadata-verify.c new file mode 100644 index 000000000..94ecae8b9 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/metadata-verify.c @@ -0,0 +1,4517 @@ +/** + * \file + * Metadata verfication support + * + * Author: + * Mono Project (http://www.mono-project.com) + * + * Copyright (C) 2005-2008 Novell, Inc. (http://www.novell.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include + +#ifndef DISABLE_VERIFIER +/* + TODO add fail fast mode + TODO add PE32+ support + TODO verify the entry point RVA and content. + TODO load_section_table and load_data_directories must take PE32+ into account + TODO add section relocation support + TODO verify the relocation table, since we really don't use, no need so far. + TODO do full PECOFF resources verification + TODO verify in the CLI header entry point and resources + TODO implement null token typeref validation + TODO verify table wide invariants for typedef (sorting and uniqueness) + TODO implement proper authenticode data directory validation + TODO verify properties that require multiple tables to be valid + FIXME use subtraction based bounds checking to avoid overflows + FIXME get rid of metadata_streams and other fields from VerifyContext +*/ + +#ifdef MONO_VERIFIER_DEBUG +#define VERIFIER_DEBUG(code) do { code; } while (0) +#else +#define VERIFIER_DEBUG(code) +#endif + +#define INVALID_OFFSET ((guint32)-1) +#define INVALID_ADDRESS 0xffffffff + +enum { + STAGE_PE, + STAGE_CLI, + STAGE_TABLES +}; + +enum { + IMPORT_TABLE_IDX = 1, + RESOURCE_TABLE_IDX = 2, + CERTIFICATE_TABLE_IDX = 4, + RELOCATION_TABLE_IDX = 5, + IAT_IDX = 12, + CLI_HEADER_IDX = 14, +}; + +enum { + STRINGS_STREAM, + USER_STRINGS_STREAM, + BLOB_STREAM, + GUID_STREAM, + TILDE_STREAM +}; + + +#define INVALID_TABLE (0xFF) +/*format: number of bits, number of tables, tables{n. tables} */ +const static unsigned char coded_index_desc[] = { +#define TYPEDEF_OR_REF_DESC (0) + 2, /*bits*/ + 3, /*tables*/ + MONO_TABLE_TYPEDEF, + MONO_TABLE_TYPEREF, + MONO_TABLE_TYPESPEC, + +#define HAS_CONSTANT_DESC (TYPEDEF_OR_REF_DESC + 5) + 2, /*bits*/ + 3, /*tables*/ + MONO_TABLE_FIELD, + MONO_TABLE_PARAM, + MONO_TABLE_PROPERTY, + +#define HAS_CATTR_DESC (HAS_CONSTANT_DESC + 5) + 5, /*bits*/ + 20, /*tables*/ + MONO_TABLE_METHOD, + MONO_TABLE_FIELD, + MONO_TABLE_TYPEREF, + MONO_TABLE_TYPEDEF, + MONO_TABLE_PARAM, + MONO_TABLE_INTERFACEIMPL, + MONO_TABLE_MEMBERREF, + MONO_TABLE_MODULE, + MONO_TABLE_DECLSECURITY, + MONO_TABLE_PROPERTY, + MONO_TABLE_EVENT, + MONO_TABLE_STANDALONESIG, + MONO_TABLE_MODULEREF, + MONO_TABLE_TYPESPEC, + MONO_TABLE_ASSEMBLY, + MONO_TABLE_ASSEMBLYREF, + MONO_TABLE_FILE, + MONO_TABLE_EXPORTEDTYPE, + MONO_TABLE_MANIFESTRESOURCE, + MONO_TABLE_GENERICPARAM, + +#define HAS_FIELD_MARSHAL_DESC (HAS_CATTR_DESC + 22) + 1, /*bits*/ + 2, /*tables*/ + MONO_TABLE_FIELD, + MONO_TABLE_PARAM, + +#define HAS_DECL_SECURITY_DESC (HAS_FIELD_MARSHAL_DESC + 4) + 2, /*bits*/ + 3, /*tables*/ + MONO_TABLE_TYPEDEF, + MONO_TABLE_METHOD, + MONO_TABLE_ASSEMBLY, + +#define MEMBERREF_PARENT_DESC (HAS_DECL_SECURITY_DESC + 5) + 3, /*bits*/ + 5, /*tables*/ + MONO_TABLE_TYPEDEF, + MONO_TABLE_TYPEREF, + MONO_TABLE_MODULEREF, + MONO_TABLE_METHOD, + MONO_TABLE_TYPESPEC, + +#define HAS_SEMANTICS_DESC (MEMBERREF_PARENT_DESC + 7) + 1, /*bits*/ + 2, /*tables*/ + MONO_TABLE_EVENT, + MONO_TABLE_PROPERTY, + +#define METHODDEF_OR_REF_DESC (HAS_SEMANTICS_DESC + 4) + 1, /*bits*/ + 2, /*tables*/ + MONO_TABLE_METHOD, + MONO_TABLE_MEMBERREF, + +#define MEMBER_FORWARDED_DESC (METHODDEF_OR_REF_DESC + 4) + 1, /*bits*/ + 2, /*tables*/ + MONO_TABLE_FIELD, + MONO_TABLE_METHOD, + +#define IMPLEMENTATION_DESC (MEMBER_FORWARDED_DESC + 4) + 2, /*bits*/ + 3, /*tables*/ + MONO_TABLE_FILE, + MONO_TABLE_ASSEMBLYREF, + MONO_TABLE_EXPORTEDTYPE, + +#define CATTR_TYPE_DESC (IMPLEMENTATION_DESC + 5) + 3, /*bits*/ + 5, /*tables*/ + INVALID_TABLE, + INVALID_TABLE, + MONO_TABLE_METHOD, + MONO_TABLE_MEMBERREF, + INVALID_TABLE, + +#define RES_SCOPE_DESC (CATTR_TYPE_DESC + 7) + 2, /*bits*/ + 4, /*tables*/ + MONO_TABLE_MODULE, + MONO_TABLE_MODULEREF, + MONO_TABLE_ASSEMBLYREF, + MONO_TABLE_TYPEREF, + +#define TYPE_OR_METHODDEF_DESC (RES_SCOPE_DESC + 6) + 1, /*bits*/ + 2, /*tables*/ + MONO_TABLE_TYPEDEF, + MONO_TABLE_METHOD +}; + +typedef struct { + guint32 rva; + guint32 size; + guint32 translated_offset; +} DataDirectory; + +typedef struct { + guint32 offset; + guint32 size; +} OffsetAndSize; + +typedef struct { + guint32 baseRVA; + guint32 baseOffset; + guint32 size; + guint32 rellocationsRVA; + guint16 numberOfRelocations; +} SectionHeader; + +typedef struct { + guint32 row_count; + guint32 row_size; + guint32 offset; +} TableInfo; + +typedef struct { + const char *data; + guint32 size, token; + GSList *errors; + int valid; + MonoImage *image; + gboolean report_error; + gboolean report_warning; + int stage; + + DataDirectory data_directories [16]; + guint32 section_count; + SectionHeader *sections; + + OffsetAndSize metadata_streams [5]; //offset from begin of the image +} VerifyContext; + +#define ADD_VERIFY_INFO(__ctx, __msg, __status, __exception) \ + do { \ + MonoVerifyInfoExtended *vinfo = g_new (MonoVerifyInfoExtended, 1); \ + vinfo->info.status = __status; \ + vinfo->info.message = ( __msg); \ + vinfo->exception_type = (__exception); \ + (__ctx)->errors = g_slist_prepend ((__ctx)->errors, vinfo); \ + } while (0) + +#define ADD_WARNING(__ctx, __msg) \ + do { \ + if ((__ctx)->report_warning) { \ + ADD_VERIFY_INFO(__ctx, __msg, MONO_VERIFY_WARNING, MONO_EXCEPTION_INVALID_PROGRAM); \ + (__ctx)->valid = 0; \ + return; \ + } \ + } while (0) + +#define ADD_ERROR_NO_RETURN(__ctx, __msg) \ + do { \ + if ((__ctx)->report_error) \ + ADD_VERIFY_INFO(__ctx, __msg, MONO_VERIFY_ERROR, MONO_EXCEPTION_INVALID_PROGRAM); \ + (__ctx)->valid = 0; \ + } while (0) + +#define ADD_ERROR(__ctx, __msg) \ + do { \ + if ((__ctx)->report_error) \ + ADD_VERIFY_INFO(__ctx, __msg, MONO_VERIFY_ERROR, MONO_EXCEPTION_INVALID_PROGRAM); \ + (__ctx)->valid = 0; \ + return; \ + } while (0) + +#define FAIL(__ctx, __msg) \ + do { \ + if ((__ctx)->report_error) \ + ADD_VERIFY_INFO(__ctx, __msg, MONO_VERIFY_ERROR, MONO_EXCEPTION_INVALID_PROGRAM); \ + (__ctx)->valid = 0; \ + return FALSE; \ + } while (0) + +#define CHECK_STATE() do { if (!ctx.valid) goto cleanup; } while (0) + +#define CHECK_ERROR() do { if (!ctx->valid) return; } while (0) + +#define CHECK_ADD4_OVERFLOW_UN(a, b) ((guint32)(0xFFFFFFFFU) - (guint32)(b) < (guint32)(a)) +#define CHECK_ADD8_OVERFLOW_UN(a, b) ((guint64)(0xFFFFFFFFFFFFFFFFUL) - (guint64)(b) < (guint64)(a)) + +#if SIZEOF_VOID_P == 4 +#define CHECK_ADDP_OVERFLOW_UN(a,b) CHECK_ADD4_OVERFLOW_UN(a, b) +#else +#define CHECK_ADDP_OVERFLOW_UN(a,b) CHECK_ADD8_OVERFLOW_UN(a, b) +#endif + +#define ADDP_IS_GREATER_OR_OVF(a, b, c) (((a) + (b) > (c)) || CHECK_ADDP_OVERFLOW_UN (a, b)) +#define ADD_IS_GREATER_OR_OVF(a, b, c) (((a) + (b) > (c)) || CHECK_ADD4_OVERFLOW_UN (a, b)) + +static const char * +dword_align (const char *ptr) +{ +#if SIZEOF_VOID_P == 8 + return (const char *) (((guint64) (ptr + 3)) & ~3); +#else + return (const char *) (((guint32) (ptr + 3)) & ~3); +#endif +} + +static void +add_from_mono_error (VerifyContext *ctx, MonoError *error) +{ + if (mono_error_ok (error)) + return; + + ADD_ERROR (ctx, g_strdup (mono_error_get_message (error))); + mono_error_cleanup (error); +} + +static guint32 +pe_signature_offset (VerifyContext *ctx) +{ + return read32 (ctx->data + 0x3c); +} + +static guint32 +pe_header_offset (VerifyContext *ctx) +{ + return read32 (ctx->data + 0x3c) + 4; +} + +static gboolean +bounds_check_virtual_address (VerifyContext *ctx, guint32 rva, guint32 size) +{ + int i; + + if (rva + size < rva) //overflow + return FALSE; + + if (ctx->stage > STAGE_PE) { + MonoCLIImageInfo *iinfo = (MonoCLIImageInfo *)ctx->image->image_info; + const int top = iinfo->cli_section_count; + MonoSectionTable *tables = iinfo->cli_section_tables; + int i; + + for (i = 0; i < top; i++) { + guint32 base = tables->st_virtual_address; + guint32 end = base + tables->st_raw_data_size; + + if (rva >= base && rva + size <= end) + return TRUE; + + /*if ((addr >= tables->st_virtual_address) && + (addr < tables->st_virtual_address + tables->st_raw_data_size)){ + + return addr - tables->st_virtual_address + tables->st_raw_data_ptr; + }*/ + tables++; + } + return FALSE; + } + + if (!ctx->sections) + return FALSE; + + for (i = 0; i < ctx->section_count; ++i) { + guint32 base = ctx->sections [i].baseRVA; + guint32 end = ctx->sections [i].baseRVA + ctx->sections [i].size; + if (rva >= base && rva + size <= end) + return TRUE; + } + return FALSE; +} + +static gboolean +bounds_check_datadir (DataDirectory *dir, guint32 offset, guint32 size) +{ + if (dir->translated_offset > offset) + return FALSE; + if (dir->size < size) + return FALSE; + return offset + size <= dir->translated_offset + dir->size; +} + +static gboolean +bounds_check_offset (OffsetAndSize *off, guint32 offset, guint32 size) +{ + if (off->offset > offset) + return FALSE; + + if (off->size < size) + return FALSE; + + return offset + size <= off->offset + off->size; +} + +static guint32 +translate_rva (VerifyContext *ctx, guint32 rva) +{ + int i; + + if (ctx->stage > STAGE_PE) + return mono_cli_rva_image_map (ctx->image, rva); + + if (!ctx->sections) + return FALSE; + + for (i = 0; i < ctx->section_count; ++i) { + guint32 base = ctx->sections [i].baseRVA; + guint32 end = ctx->sections [i].baseRVA + ctx->sections [i].size; + if (rva >= base && rva <= end) { + guint32 res = (rva - base) + ctx->sections [i].baseOffset; + /* double check */ + return res >= ctx->size ? INVALID_OFFSET : res; + } + } + + return INVALID_OFFSET; +} + +static void +verify_msdos_header (VerifyContext *ctx) +{ + guint32 lfanew; + if (ctx->size < 128) + ADD_ERROR (ctx, g_strdup ("Not enough space for the MS-DOS header")); + if (ctx->data [0] != 0x4d || ctx->data [1] != 0x5a) + ADD_ERROR (ctx, g_strdup ("Invalid MS-DOS watermark")); + lfanew = pe_signature_offset (ctx); + if (lfanew > ctx->size - 4) + ADD_ERROR (ctx, g_strdup ("MS-DOS lfanew offset points to outside of the file")); +} + +static void +verify_pe_header (VerifyContext *ctx) +{ + guint32 offset = pe_signature_offset (ctx); + const char *pe_header = ctx->data + offset; + if (pe_header [0] != 'P' || pe_header [1] != 'E' ||pe_header [2] != 0 ||pe_header [3] != 0) + ADD_ERROR (ctx, g_strdup ("Invalid PE header watermark")); + pe_header += 4; + offset += 4; + + if (offset > ctx->size - 20) + ADD_ERROR (ctx, g_strdup ("File with truncated pe header")); + if (read16 (pe_header) != 0x14c) + ADD_ERROR (ctx, g_strdup ("Invalid PE header Machine value")); +} + +static void +verify_pe_optional_header (VerifyContext *ctx) +{ + guint32 offset = pe_header_offset (ctx); + guint32 header_size, file_alignment; + const char *pe_header = ctx->data + offset; + const char *pe_optional_header = pe_header + 20; + + header_size = read16 (pe_header + 16); + offset += 20; + + if (header_size < 2) /*must be at least 2 or we won't be able to read magic*/ + ADD_ERROR (ctx, g_strdup ("Invalid PE optional header size")); + + if (offset > ctx->size - header_size || header_size > ctx->size) + ADD_ERROR (ctx, g_strdup ("Invalid PE optional header size")); + + if (read16 (pe_optional_header) == 0x10b) { + if (header_size != 224) + ADD_ERROR (ctx, g_strdup_printf ("Invalid optional header size %d", header_size)); + + /* LAMESPEC MS plays around this value and ignore it during validation + if (read32 (pe_optional_header + 28) != 0x400000) + ADD_ERROR (ctx, g_strdup_printf ("Invalid Image base %x", read32 (pe_optional_header + 28)));*/ + if (read32 (pe_optional_header + 32) != 0x2000) + ADD_ERROR (ctx, g_strdup_printf ("Invalid Section Aligmnent %x", read32 (pe_optional_header + 32))); + file_alignment = read32 (pe_optional_header + 36); + if (file_alignment != 0x200 && file_alignment != 0x1000) + ADD_ERROR (ctx, g_strdup_printf ("Invalid file Aligmnent %x", file_alignment)); + /* All the junk in the middle is irrelevant, specially for mono. */ + if (read32 (pe_optional_header + 92) > 0x10) + ADD_ERROR (ctx, g_strdup_printf ("Too many data directories %x", read32 (pe_optional_header + 92))); + } else { + if (read16 (pe_optional_header) == 0x20B) + ADD_ERROR (ctx, g_strdup ("Metadata verifier doesn't handle PE32+")); + else + ADD_ERROR (ctx, g_strdup_printf ("Invalid optional header magic %d", read16 (pe_optional_header))); + } +} + +static void +load_section_table (VerifyContext *ctx) +{ + int i; + SectionHeader *sections; + guint32 offset = pe_header_offset (ctx); + const char *ptr = ctx->data + offset; + guint16 num_sections = ctx->section_count = read16 (ptr + 2); + + offset += 244;/*FIXME, this constant is different under PE32+*/ + ptr += 244; + + if (num_sections * 40 > ctx->size - offset) + ADD_ERROR (ctx, g_strdup ("Invalid PE optional header size")); + + sections = ctx->sections = g_new0 (SectionHeader, num_sections); + for (i = 0; i < num_sections; ++i) { + sections [i].size = read32 (ptr + 8); + sections [i].baseRVA = read32 (ptr + 12); + sections [i].baseOffset = read32 (ptr + 20); + sections [i].rellocationsRVA = read32 (ptr + 24); + sections [i].numberOfRelocations = read16 (ptr + 32); + ptr += 40; + } + + ptr = ctx->data + offset; /*reset it to the beggining*/ + for (i = 0; i < num_sections; ++i) { + guint32 raw_size, flags; + if (sections [i].baseOffset == 0) + ADD_ERROR (ctx, g_strdup ("Metadata verifier doesn't handle sections with intialized data only")); + if (sections [i].baseOffset >= ctx->size) + ADD_ERROR (ctx, g_strdup_printf ("Invalid PointerToRawData %x points beyond EOF", sections [i].baseOffset)); + if (sections [i].size > ctx->size - sections [i].baseOffset) + ADD_ERROR (ctx, g_strdup ("Invalid VirtualSize points beyond EOF")); + + raw_size = read32 (ptr + 16); + if (raw_size < sections [i].size) + ADD_ERROR (ctx, g_strdup ("Metadata verifier doesn't handle sections with SizeOfRawData < VirtualSize")); + + if (raw_size > ctx->size - sections [i].baseOffset) + ADD_ERROR (ctx, g_strdup_printf ("Invalid SizeOfRawData %x points beyond EOF", raw_size)); + + if (sections [i].rellocationsRVA || sections [i].numberOfRelocations) + ADD_ERROR (ctx, g_strdup_printf ("Metadata verifier doesn't handle section relocation")); + + flags = read32 (ptr + 36); + /*TODO 0xFE0000E0 is all flags from cil-coff.h OR'd. Make it a less magical number*/ + if (flags == 0 || (flags & ~0xFE0000E0) != 0) + ADD_ERROR (ctx, g_strdup_printf ("Invalid section flags %x", flags)); + + ptr += 40; + } +} + +static gboolean +is_valid_data_directory (int i) +{ + /*LAMESPEC 4 == certificate 6 == debug, MS uses both*/ + return i == 1 || i == 2 || i == 5 || i == 12 || i == 14 || i == 4 || i == 6; +} + +static void +load_data_directories (VerifyContext *ctx) +{ + guint32 offset = pe_header_offset (ctx) + 116; /*FIXME, this constant is different under PE32+*/ + const char *ptr = ctx->data + offset; + int i; + + for (i = 0; i < 16; ++i) { + guint32 rva = read32 (ptr); + guint32 size = read32 (ptr + 4); + + /*LAMESPEC the authenticode data directory format is different. We don't support CAS, so lets ignore for now.*/ + if (i == CERTIFICATE_TABLE_IDX) { + ptr += 8; + continue; + } + if ((rva != 0 || size != 0) && !is_valid_data_directory (i)) + ADD_ERROR (ctx, g_strdup_printf ("Invalid data directory %d", i)); + + if (rva != 0 && !bounds_check_virtual_address (ctx, rva, size)) + ADD_ERROR (ctx, g_strdup_printf ("Invalid data directory %d rva/size pair %x/%x", i, rva, size)); + + ctx->data_directories [i].rva = rva; + ctx->data_directories [i].size = size; + ctx->data_directories [i].translated_offset = translate_rva (ctx, rva); + + ptr += 8; + } +} + +#define SIZE_OF_MSCOREE (sizeof ("mscoree.dll")) + +#define SIZE_OF_CORMAIN (sizeof ("_CorExeMain")) + +static void +verify_hint_name_table (VerifyContext *ctx, guint32 import_rva, const char *table_name) +{ + const char *ptr; + guint32 hint_table_rva; + + import_rva = translate_rva (ctx, import_rva); + g_assert (import_rva != INVALID_OFFSET); + + hint_table_rva = read32 (ctx->data + import_rva); + if (!bounds_check_virtual_address (ctx, hint_table_rva, SIZE_OF_CORMAIN + 2)) + ADD_ERROR (ctx, g_strdup_printf ("Invalid Hint/Name rva %d for %s", hint_table_rva, table_name)); + + hint_table_rva = translate_rva (ctx, hint_table_rva); + g_assert (hint_table_rva != INVALID_OFFSET); + ptr = ctx->data + hint_table_rva + 2; + + if (memcmp ("_CorExeMain", ptr, SIZE_OF_CORMAIN) && memcmp ("_CorDllMain", ptr, SIZE_OF_CORMAIN)) { + char name[SIZE_OF_CORMAIN]; + memcpy (name, ptr, SIZE_OF_CORMAIN); + name [SIZE_OF_CORMAIN - 1] = 0; + ADD_ERROR (ctx, g_strdup_printf ("Invalid Hint / Name: '%s'", name)); + } +} + +static void +verify_import_table (VerifyContext *ctx) +{ + DataDirectory it = ctx->data_directories [IMPORT_TABLE_IDX]; + guint32 offset = it.translated_offset; + const char *ptr = ctx->data + offset; + guint32 name_rva, ilt_rva, iat_rva; + + g_assert (offset != INVALID_OFFSET); + + if (it.size < 40) + ADD_ERROR (ctx, g_strdup_printf ("Import table size %d is smaller than 40", it.size)); + + ilt_rva = read32 (ptr); + if (ilt_rva && !bounds_check_virtual_address (ctx, ilt_rva, 8)) + ADD_ERROR (ctx, g_strdup_printf ("Invalid Import Lookup Table rva %x", ilt_rva)); + + name_rva = read32 (ptr + 12); + if (name_rva && !bounds_check_virtual_address (ctx, name_rva, SIZE_OF_MSCOREE)) + ADD_ERROR (ctx, g_strdup_printf ("Invalid Import Table Name rva %x", name_rva)); + + iat_rva = read32 (ptr + 16); + if (iat_rva) { + if (!bounds_check_virtual_address (ctx, iat_rva, 8)) + ADD_ERROR (ctx, g_strdup_printf ("Invalid Import Address Table rva %x", iat_rva)); + + if (iat_rva != ctx->data_directories [IAT_IDX].rva) + ADD_ERROR (ctx, g_strdup_printf ("Import Address Table rva %x different from data directory entry %x", read32 (ptr + 16), ctx->data_directories [IAT_IDX].rva)); + } + + if (name_rva) { + name_rva = translate_rva (ctx, name_rva); + g_assert (name_rva != INVALID_OFFSET); + ptr = ctx->data + name_rva; + + if (memcmp ("mscoree.dll", ptr, SIZE_OF_MSCOREE)) { + char name[SIZE_OF_MSCOREE]; + memcpy (name, ptr, SIZE_OF_MSCOREE); + name [SIZE_OF_MSCOREE - 1] = 0; + ADD_ERROR (ctx, g_strdup_printf ("Invalid Import Table Name: '%s'", name)); + } + } + + if (ilt_rva) { + verify_hint_name_table (ctx, ilt_rva, "Import Lookup Table"); + CHECK_ERROR (); + } + + if (iat_rva) + verify_hint_name_table (ctx, iat_rva, "Import Address Table"); +} + +static void +verify_resources_table (VerifyContext *ctx) +{ + DataDirectory it = ctx->data_directories [RESOURCE_TABLE_IDX]; + guint32 offset; + guint16 named_entries, id_entries; + const char *ptr; + + if (it.rva == 0) + return; + + if (it.size < 16) + ADD_ERROR (ctx, g_strdup_printf ("Resource section is too small, must be at least 16 bytes long but it's %d long", it.size)); + + offset = it.translated_offset; + ptr = ctx->data + offset; + + g_assert (offset != INVALID_OFFSET); + + named_entries = read16 (ptr + 12); + id_entries = read16 (ptr + 14); + + if ((named_entries + id_entries) * 8 + 16 > it.size) + ADD_ERROR (ctx, g_strdup_printf ("Resource section is too small, the number of entries (%d) doesn't fit on it's size %d", named_entries + id_entries, it.size)); + + /* XXX at least one unmanaged resource is added due to a call to AssemblyBuilder::DefineVersionInfoResource () + if (named_entries || id_entries) + ADD_ERROR (ctx, g_strdup_printf ("The metadata verifier doesn't support full verification of PECOFF resources")); + */ +} + +/*----------nothing from here on can use data_directory---*/ + +static DataDirectory +get_data_dir (VerifyContext *ctx, int idx) +{ + MonoCLIImageInfo *iinfo = (MonoCLIImageInfo *)ctx->image->image_info; + MonoPEDirEntry *entry= &iinfo->cli_header.datadir.pe_export_table; + DataDirectory res; + + entry += idx; + res.rva = entry->rva; + res.size = entry->size; + res.translated_offset = translate_rva (ctx, res.rva); + return res; + +} +static void +verify_cli_header (VerifyContext *ctx) +{ + DataDirectory it = get_data_dir (ctx, CLI_HEADER_IDX); + guint32 offset; + const char *ptr; + int i; + + if (it.rva == 0) + ADD_ERROR (ctx, g_strdup_printf ("CLI header missing")); + + if (it.size != 72) + ADD_ERROR (ctx, g_strdup_printf ("Invalid cli header size in data directory %d must be 72", it.size)); + + offset = it.translated_offset; + ptr = ctx->data + offset; + + g_assert (offset != INVALID_OFFSET); + + if (read16 (ptr) != 72) + ADD_ERROR (ctx, g_strdup_printf ("Invalid cli header size %d must be 72", read16 (ptr))); + + if (!bounds_check_virtual_address (ctx, read32 (ptr + 8), read32 (ptr + 12))) + ADD_ERROR (ctx, g_strdup_printf ("Invalid medatata section rva/size pair %x/%x", read32 (ptr + 8), read32 (ptr + 12))); + + + if (!read32 (ptr + 8) || !read32 (ptr + 12)) + ADD_ERROR (ctx, g_strdup_printf ("Missing medatata section in the CLI header")); + + if ((read32 (ptr + 16) & ~0x0003000B) != 0) + ADD_ERROR (ctx, g_strdup_printf ("Invalid CLI header flags")); + + ptr += 24; + for (i = 0; i < 6; ++i) { + guint32 rva = read32 (ptr); + guint32 size = read32 (ptr + 4); + + if (rva != 0 && !bounds_check_virtual_address (ctx, rva, size)) + ADD_ERROR (ctx, g_strdup_printf ("Invalid cli section %i rva/size pair %x/%x", i, rva, size)); + + ptr += 8; + + if (rva && i > 1) + ADD_ERROR (ctx, g_strdup_printf ("Metadata verifier doesn't support cli header section %d", i)); + } +} + +static guint32 +pad4 (guint32 offset) +{ + if (offset & 0x3) //pad to the next 4 byte boundary + offset = (offset & ~0x3) + 4; + return offset; +} + +static void +verify_metadata_header (VerifyContext *ctx) +{ + int i; + DataDirectory it = get_data_dir (ctx, CLI_HEADER_IDX); + guint32 offset, section_count; + const char *ptr; + + offset = it.translated_offset; + ptr = ctx->data + offset; + g_assert (offset != INVALID_OFFSET); + + //build a directory entry for the metadata root + ptr += 8; + it.rva = read32 (ptr); + ptr += 4; + it.size = read32 (ptr); + it.translated_offset = offset = translate_rva (ctx, it.rva); + + ptr = ctx->data + offset; + g_assert (offset != INVALID_OFFSET); + + if (it.size < 20) + ADD_ERROR (ctx, g_strdup_printf ("Metadata root section is too small %d (at least 20 bytes required for initial decoding)", it.size)); + + if (read32 (ptr) != 0x424A5342) + ADD_ERROR (ctx, g_strdup_printf ("Invalid metadata signature, expected 0x424A5342 but got %08x", read32 (ptr))); + + offset = pad4 (offset + 16 + read32 (ptr + 12)); + + if (!bounds_check_datadir (&it, offset, 4)) + ADD_ERROR (ctx, g_strdup_printf ("Metadata root section is too small %d (at least %d bytes required for flags decoding)", it.size, offset + 4 - it.translated_offset)); + + ptr = ctx->data + offset; //move to streams header + + section_count = read16 (ptr + 2); + if (section_count < 2) + ADD_ERROR (ctx, g_strdup_printf ("Metadata root section must have at least 2 streams (#~ and #GUID)")); + + ptr += 4; + offset += 4; + + for (i = 0; i < section_count; ++i) { + guint32 stream_off, stream_size; + int string_size, stream_idx; + + if (!bounds_check_datadir (&it, offset, 8)) + ADD_ERROR (ctx, g_strdup_printf ("Metadata root section is too small for initial decode of stream header %d, missing %d bytes", i, offset + 9 - it.translated_offset)); + + stream_off = it.translated_offset + read32 (ptr); + stream_size = read32 (ptr + 4); + + if (!bounds_check_datadir (&it, stream_off, stream_size)) + ADD_ERROR (ctx, g_strdup_printf ("Invalid stream header %d offset/size pair %x/%x", 0, stream_off, stream_size)); + + ptr += 8; + offset += 8; + + for (string_size = 0; string_size < 32; ++string_size) { + if (!bounds_check_datadir (&it, offset++, 1)) + ADD_ERROR (ctx, g_strdup_printf ("Metadata root section is too small to decode stream header %d name", i)); + if (!ptr [string_size]) + break; + } + + if (ptr [string_size]) + ADD_ERROR (ctx, g_strdup_printf ("Metadata stream header %d name larger than 32 bytes", i)); + + if (!strncmp ("#Strings", ptr, 9)) + stream_idx = STRINGS_STREAM; + else if (!strncmp ("#US", ptr, 4)) + stream_idx = USER_STRINGS_STREAM; + else if (!strncmp ("#Blob", ptr, 6)) + stream_idx = BLOB_STREAM; + else if (!strncmp ("#GUID", ptr, 6)) + stream_idx = GUID_STREAM; + else if (!strncmp ("#~", ptr, 3)) + stream_idx = TILDE_STREAM; + else { + ADD_WARNING (ctx, g_strdup_printf ("Metadata stream header %d invalid name %s", i, ptr)); + offset = pad4 (offset); + ptr = ctx->data + offset; + continue; + } + + if (ctx->metadata_streams [stream_idx].offset != 0) + ADD_ERROR (ctx, g_strdup_printf ("Duplicated metadata stream header %s", ptr)); + + ctx->metadata_streams [stream_idx].offset = stream_off; + ctx->metadata_streams [stream_idx].size = stream_size; + + offset = pad4 (offset); + ptr = ctx->data + offset; + } + + if (!ctx->metadata_streams [TILDE_STREAM].size) + ADD_ERROR (ctx, g_strdup_printf ("Metadata #~ stream missing")); + if (!ctx->metadata_streams [GUID_STREAM].size) + ADD_ERROR (ctx, g_strdup_printf ("Metadata guid stream missing")); +} + +static void +verify_tables_schema (VerifyContext *ctx) +{ + OffsetAndSize tables_area = ctx->metadata_streams [TILDE_STREAM]; + unsigned offset = tables_area.offset; + const char *ptr = ctx->data + offset; + guint64 valid_tables; + guint32 count; + int i; + + if (tables_area.size < 24) + ADD_ERROR (ctx, g_strdup_printf ("Table schemata size (%d) too small to for initial decoding (requires 24 bytes)", tables_area.size)); + + if (ptr [4] != 2 && ptr [4] != 1) + ADD_ERROR (ctx, g_strdup_printf ("Invalid table schemata major version %d, expected 2", ptr [4])); + if (ptr [5] != 0) + ADD_ERROR (ctx, g_strdup_printf ("Invalid table schemata minor version %d, expected 0", ptr [5])); + + if ((ptr [6] & ~0x7) != 0) + ADD_ERROR (ctx, g_strdup_printf ("Invalid table schemata heap sizes 0x%02x, only bits 0, 1 and 2 can be set", ((unsigned char *) ptr) [6])); + + valid_tables = read64 (ptr + 8); + count = 0; + for (i = 0; i < 64; ++i) { + if (!(valid_tables & ((guint64)1 << i))) + continue; + + /*MS Extensions: 0x3 0x5 0x7 0x13 0x16 + Unused: 0x1E 0x1F 0x2D-0x3F + We don't care about the MS extensions.*/ + if (i == 0x3 || i == 0x5 || i == 0x7 || i == 0x13 || i == 0x16) + ADD_ERROR (ctx, g_strdup_printf ("The metadata verifier doesn't support MS specific table %x", i)); + if (i == 0x1E || i == 0x1F || i >= 0x2D) + ADD_ERROR (ctx, g_strdup_printf ("Invalid table %x", i)); + ++count; + } + + if (tables_area.size < 24 + count * 4) + ADD_ERROR (ctx, g_strdup_printf ("Table schemata size (%d) too small to for decoding row counts (requires %d bytes)", tables_area.size, 24 + count * 4)); + ptr += 24; + + for (i = 0; i < 64; ++i) { + if (valid_tables & ((guint64)1 << i)) { + guint32 row_count = read32 (ptr); + if (row_count > (1 << 24) - 1) + ADD_ERROR (ctx, g_strdup_printf ("Invalid Table %d row count: %d. Mono only supports 16777215 rows", i, row_count)); + ptr += 4; + } + } +} + +/*----------nothing from here on can use data_directory or metadata_streams ---*/ + +static guint32 +get_col_offset (VerifyContext *ctx, int table, int column) +{ + guint32 bitfield = ctx->image->tables [table].size_bitfield; + guint32 offset = 0; + + while (column-- > 0) + offset += mono_metadata_table_size (bitfield, column); + + return offset; +} + +static guint32 +get_col_size (VerifyContext *ctx, int table, int column) +{ + return mono_metadata_table_size (ctx->image->tables [table].size_bitfield, column); +} + +static OffsetAndSize +get_metadata_stream (VerifyContext *ctx, MonoStreamHeader *header) +{ + OffsetAndSize res; + res.offset = header->data - ctx->data; + res.size = header->size; + + return res; +} + +static gboolean +is_valid_string_full_with_image (MonoImage *image, guint32 offset, gboolean allow_empty) +{ + guint32 heap_offset = (char*)image->heap_strings.data - image->raw_data; + guint32 heap_size = image->heap_strings.size; + + glong length; + const char *data = image->raw_data + heap_offset; + + if (offset >= heap_size) + return FALSE; + if (CHECK_ADDP_OVERFLOW_UN (data, offset)) + return FALSE; + + if (!mono_utf8_validate_and_len_with_bounds (data + offset, heap_size - offset, &length, NULL)) + return FALSE; + return allow_empty || length > 0; +} + + +static gboolean +is_valid_string_full (VerifyContext *ctx, guint32 offset, gboolean allow_empty) +{ + return is_valid_string_full_with_image (ctx->image, offset, allow_empty); +} + +static gboolean +is_valid_string (VerifyContext *ctx, guint32 offset) +{ + return is_valid_string_full (ctx, offset, TRUE); +} + +static gboolean +is_valid_non_empty_string (VerifyContext *ctx, guint32 offset) +{ + return is_valid_string_full (ctx, offset, FALSE); +} + +static gboolean +is_valid_guid (VerifyContext *ctx, guint32 offset) +{ + OffsetAndSize guids = get_metadata_stream (ctx, &ctx->image->heap_guid); + return guids.size >= 8 && guids.size - 8 >= offset; +} + +static guint32 +get_coded_index_token (int token_kind, guint32 coded_token) +{ + guint32 bits = coded_index_desc [token_kind]; + return coded_token >> bits; +} + +static guint32 +get_coded_index_table (int kind, guint32 coded_token) +{ + guint32 idx, bits = coded_index_desc [kind]; + kind += 2; + idx = coded_token & ((1 << bits) - 1); + return coded_index_desc [kind + idx]; +} + +static guint32 +make_coded_token (int kind, guint32 table, guint32 table_idx) +{ + guint32 bits = coded_index_desc [kind++]; + guint32 tables = coded_index_desc [kind++]; + guint32 i; + for (i = 0; i < tables; ++i) { + if (coded_index_desc [kind++] == table) + return ((table_idx + 1) << bits) | i; + } + g_assert_not_reached (); + return -1; +} + +static gboolean +is_valid_coded_index_with_image (MonoImage *image, int token_kind, guint32 coded_token) +{ + guint32 bits = coded_index_desc [token_kind++]; + guint32 table_count = coded_index_desc [token_kind++]; + guint32 table = coded_token & ((1 << bits) - 1); + guint32 token = coded_token >> bits; + + if (table >= table_count) + return FALSE; + + /*token_kind points to the first table idx*/ + table = coded_index_desc [token_kind + table]; + + if (table == INVALID_TABLE) + return FALSE; + return token <= image->tables [table].rows; +} + +static gboolean +is_valid_coded_index (VerifyContext *ctx, int token_kind, guint32 coded_token) +{ + return is_valid_coded_index_with_image (ctx->image, token_kind, coded_token); +} + +typedef struct { + guint32 token; + guint32 col_size; + guint32 col_offset; + MonoTableInfo *table; +} RowLocator; + +static int +token_locator (const void *a, const void *b) +{ + RowLocator *loc = (RowLocator *)a; + unsigned const char *row = (unsigned const char *)b; + guint32 token = loc->col_size == 2 ? read16 (row + loc->col_offset) : read32 (row + loc->col_offset); + + VERIFIER_DEBUG ( printf ("\tfound token %x at idx %d\n", token, ((const char*)row - loc->table->base) / loc->table->row_size) ); + return (int)loc->token - (int)token; +} + +static int +search_sorted_table (VerifyContext *ctx, int table, int column, guint32 coded_token) +{ + MonoTableInfo *tinfo = &ctx->image->tables [table]; + RowLocator locator; + const char *res, *base; + locator.token = coded_token; + locator.col_offset = get_col_offset (ctx, table, column); + locator.col_size = get_col_size (ctx, table, column); + locator.table = tinfo; + + base = tinfo->base; + + VERIFIER_DEBUG ( printf ("looking token %x table %d col %d rsize %d roff %d\n", coded_token, table, column, locator.col_size, locator.col_offset) ); + res = (const char *)mono_binary_search (&locator, base, tinfo->rows, tinfo->row_size, token_locator); + if (!res) + return -1; + + return (res - base) / tinfo->row_size; +} + +/*WARNING: This function doesn't verify if the strings @offset points to a valid string*/ +static const char* +get_string_ptr (VerifyContext *ctx, guint offset) +{ + return ctx->image->heap_strings.data + offset; +} + +/*WARNING: This function doesn't verify if the strings @offset points to a valid string*/ +static int +string_cmp (VerifyContext *ctx, const char *str, guint offset) +{ + if (offset == 0) + return strcmp (str, ""); + + return strcmp (str, get_string_ptr (ctx, offset)); +} + +static gboolean +mono_verifier_is_corlib (MonoImage *image) +{ + gboolean trusted_location = !mono_security_core_clr_enabled () ? + TRUE : mono_security_core_clr_is_platform_image (image); + + return trusted_location && image->module_name && !strcmp ("mscorlib.dll", image->module_name); +} + +static gboolean +typedef_is_system_object (VerifyContext *ctx, guint32 *data) +{ + return mono_verifier_is_corlib (ctx->image) && !string_cmp (ctx, "System", data [MONO_TYPEDEF_NAMESPACE]) && !string_cmp (ctx, "Object", data [MONO_TYPEDEF_NAME]); +} + +static gboolean +decode_value (const char *_ptr, unsigned available, unsigned *value, unsigned *size) +{ + unsigned char b; + const unsigned char *ptr = (const unsigned char *)_ptr; + + if (!available) + return FALSE; + + b = *ptr; + *value = *size = 0; + + if ((b & 0x80) == 0) { + *size = 1; + *value = b; + } else if ((b & 0x40) == 0) { + if (available < 2) + return FALSE; + *size = 2; + *value = ((b & 0x3f) << 8 | ptr [1]); + } else { + if (available < 4) + return FALSE; + *size = 4; + *value = ((b & 0x1f) << 24) | + (ptr [1] << 16) | + (ptr [2] << 8) | + ptr [3]; + } + + return TRUE; +} + +static gboolean +decode_signature_header (VerifyContext *ctx, guint32 offset, guint32 *size, const char **first_byte) +{ + MonoStreamHeader blob = ctx->image->heap_blob; + guint32 value, enc_size; + + if (offset >= blob.size) + return FALSE; + + if (!decode_value (blob.data + offset, blob.size - offset, &value, &enc_size)) + return FALSE; + + if (CHECK_ADD4_OVERFLOW_UN (offset, enc_size)) + return FALSE; + + offset += enc_size; + + if (ADD_IS_GREATER_OR_OVF (offset, value, blob.size)) + return FALSE; + + *size = value; + *first_byte = blob.data + offset; + return TRUE; +} + +static gboolean +safe_read (const char **_ptr, const char *limit, unsigned *dest, int size) +{ + const char *ptr = *_ptr; + if (ptr + size > limit) + return FALSE; + switch (size) { + case 1: + *dest = *((guint8*)ptr); + ++ptr; + break; + case 2: + *dest = read16 (ptr); + ptr += 2; + break; + case 4: + *dest = read32 (ptr); + ptr += 4; + break; + } + *_ptr = ptr; + return TRUE; +} + +static gboolean +safe_read_compressed_int (const char **_ptr, const char *limit, unsigned *dest) +{ + unsigned size = 0; + const char *ptr = *_ptr; + gboolean res = decode_value (ptr, limit - ptr, dest, &size); + *_ptr = ptr + size; + return res; +} + +#define safe_read8(VAR, PTR, LIMIT) safe_read (&PTR, LIMIT, &VAR, 1) +#define safe_read_cint(VAR, PTR, LIMIT) safe_read_compressed_int (&PTR, LIMIT, &VAR) +#define safe_read16(VAR, PTR, LIMIT) safe_read (&PTR, LIMIT, &VAR, 2) +#define safe_read32(VAR, PTR, LIMIT) safe_read (&PTR, LIMIT, &VAR, 4) + +static gboolean +parse_type (VerifyContext *ctx, const char **_ptr, const char *end); + +static gboolean +parse_method_signature (VerifyContext *ctx, const char **_ptr, const char *end, gboolean allow_sentinel, gboolean allow_unmanaged); + +static gboolean +parse_custom_mods (VerifyContext *ctx, const char **_ptr, const char *end) +{ + const char *ptr = *_ptr; + unsigned type = 0; + unsigned token = 0; + + while (TRUE) { + if (!safe_read8 (type, ptr, end)) + FAIL (ctx, g_strdup ("CustomMod: Not enough room for the type")); + + if (type != MONO_TYPE_CMOD_REQD && type != MONO_TYPE_CMOD_OPT) { + --ptr; + break; + } + + if (!safe_read_cint (token, ptr, end)) + FAIL (ctx, g_strdup ("CustomMod: Not enough room for the token")); + + if (!is_valid_coded_index (ctx, TYPEDEF_OR_REF_DESC, token) || !get_coded_index_token (TYPEDEF_OR_REF_DESC, token)) + FAIL (ctx, g_strdup_printf ("CustomMod: invalid TypeDefOrRef token %x", token)); + } + + *_ptr = ptr; + return TRUE; +} + +static gboolean +parse_array_shape (VerifyContext *ctx, const char **_ptr, const char *end) +{ + const char *ptr = *_ptr; + unsigned val = 0; + unsigned size, num, i; + + if (!safe_read8 (val, ptr, end)) + FAIL (ctx, g_strdup ("ArrayShape: Not enough room for Rank")); + + if (val == 0) + FAIL (ctx, g_strdup ("ArrayShape: Invalid shape with zero Rank")); + + if (!safe_read_cint (size, ptr, end)) + FAIL (ctx, g_strdup ("ArrayShape: Not enough room for NumSizes")); + + for (i = 0; i < size; ++i) { + if (!safe_read_cint (num, ptr, end)) + FAIL (ctx, g_strdup_printf ("ArrayShape: Not enough room for Size of rank %d", i + 1)); + } + + if (!safe_read_cint (size, ptr, end)) + FAIL (ctx, g_strdup ("ArrayShape: Not enough room for NumLoBounds")); + + for (i = 0; i < size; ++i) { + if (!safe_read_cint (num, ptr, end)) + FAIL (ctx, g_strdup_printf ("ArrayShape: Not enough room for LoBound of rank %d", i + 1)); + } + + *_ptr = ptr; + return TRUE; +} + +static gboolean +parse_generic_inst (VerifyContext *ctx, const char **_ptr, const char *end) +{ + const char *ptr = *_ptr; + unsigned type; + unsigned count, token, i; + + if (!safe_read8 (type, ptr, end)) + FAIL (ctx, g_strdup ("GenericInst: Not enough room for kind")); + + if (type != MONO_TYPE_CLASS && type != MONO_TYPE_VALUETYPE) + FAIL (ctx, g_strdup_printf ("GenericInst: Invalid GenericInst kind %x\n", type)); + + if (!safe_read_cint (token, ptr, end)) + FAIL (ctx, g_strdup ("GenericInst: Not enough room for type token")); + + if (!is_valid_coded_index (ctx, TYPEDEF_OR_REF_DESC, token) || !get_coded_index_token (TYPEDEF_OR_REF_DESC, token)) + FAIL (ctx, g_strdup_printf ("GenericInst: invalid TypeDefOrRef token %x", token)); + + if (ctx->token) { + if (mono_metadata_token_index (ctx->token) == get_coded_index_token (TYPEDEF_OR_REF_DESC, token) && + mono_metadata_token_table (ctx->token) == get_coded_index_table (TYPEDEF_OR_REF_DESC, token)) + FAIL (ctx, g_strdup_printf ("Type: Recurside generic instance specification (%x). A type signature can't reference itself", ctx->token)); + } + + if (!safe_read_cint (count, ptr, end)) + FAIL (ctx, g_strdup ("GenericInst: Not enough room for argument count")); + + if (count == 0) + FAIL (ctx, g_strdup ("GenericInst: Zero arguments generic instance")); + + for (i = 0; i < count; ++i) { + if (!parse_custom_mods (ctx, &ptr, end)) + FAIL (ctx, g_strdup ("Type: Failed to parse pointer custom attr")); + + if (!parse_type (ctx, &ptr, end)) + FAIL (ctx, g_strdup_printf ("GenericInst: invalid generic argument %d", i + 1)); + } + *_ptr = ptr; + return TRUE; +} + +static gboolean +parse_type (VerifyContext *ctx, const char **_ptr, const char *end) +{ + const char *ptr = *_ptr; + unsigned type; + unsigned token = 0; + + if (!safe_read8 (type, ptr, end)) + FAIL (ctx, g_strdup ("Type: Not enough room for the type")); + + if (!((type >= MONO_TYPE_BOOLEAN && type <= MONO_TYPE_PTR) || + (type >= MONO_TYPE_VALUETYPE && type <= MONO_TYPE_GENERICINST) || + (type >= MONO_TYPE_I && type <= MONO_TYPE_U) || + (type >= MONO_TYPE_FNPTR && type <= MONO_TYPE_MVAR))) + FAIL (ctx, g_strdup_printf ("Type: Invalid type kind %x\n", type)); + + switch (type) { + case MONO_TYPE_PTR: + if (!parse_custom_mods (ctx, &ptr, end)) + FAIL (ctx, g_strdup ("Type: Failed to parse pointer custom attr")); + + if (!safe_read8 (type, ptr, end)) + FAIL (ctx, g_strdup ("Type: Not enough room to parse the pointer type")); + + if (type != MONO_TYPE_VOID) { + --ptr; + if (!parse_type (ctx, &ptr, end)) + FAIL (ctx, g_strdup ("Type: Could not parse pointer type")); + } + break; + + case MONO_TYPE_VALUETYPE: + case MONO_TYPE_CLASS: + if (!safe_read_cint (token, ptr, end)) + FAIL (ctx, g_strdup ("Type: Not enough room for the type token")); + + if (!is_valid_coded_index (ctx, TYPEDEF_OR_REF_DESC, token) || !get_coded_index_token (TYPEDEF_OR_REF_DESC, token)) + FAIL (ctx, g_strdup_printf ("Type: invalid TypeDefOrRef token %x", token)); + + if (!get_coded_index_token (TYPEDEF_OR_REF_DESC, token)) + FAIL (ctx, g_strdup_printf ("Type: zero TypeDefOrRef token %x", token)); + if (ctx->token) { + if (mono_metadata_token_index (ctx->token) == get_coded_index_token (TYPEDEF_OR_REF_DESC, token) && + mono_metadata_token_table (ctx->token) == get_coded_index_table (TYPEDEF_OR_REF_DESC, token)) + FAIL (ctx, g_strdup_printf ("Type: Recursive type specification (%x). A type signature can't reference itself", ctx->token)); + } + break; + + case MONO_TYPE_VAR: + case MONO_TYPE_MVAR: + if (!safe_read_cint (token, ptr, end)) + FAIL (ctx, g_strdup ("Type: Not enough room for to decode generic argument number")); + break; + + case MONO_TYPE_ARRAY: + if (!parse_type (ctx, &ptr, end)) + FAIL (ctx, g_strdup ("Type: Could not parse array type")); + if (!parse_array_shape (ctx, &ptr, end)) + FAIL (ctx, g_strdup ("Type: Could not parse array shape")); + break; + + case MONO_TYPE_GENERICINST: + if (!parse_generic_inst (ctx, &ptr, end)) + FAIL (ctx, g_strdup ("Type: Could not parse generic inst")); + break; + + case MONO_TYPE_FNPTR: + if (!parse_method_signature (ctx, &ptr, end, TRUE, TRUE)) + FAIL (ctx, g_strdup ("Type: Could not parse method pointer signature")); + break; + + case MONO_TYPE_SZARRAY: + if (!parse_custom_mods (ctx, &ptr, end)) + FAIL (ctx, g_strdup ("Type: Failed to parse array element custom attr")); + if (!parse_type (ctx, &ptr, end)) + FAIL (ctx, g_strdup ("Type: Could not parse array type")); + break; + } + *_ptr = ptr; + return TRUE; +} + +static gboolean +parse_return_type (VerifyContext *ctx, const char **_ptr, const char *end) +{ + const char *ptr; + unsigned type = 0; + + if (!parse_custom_mods (ctx, _ptr, end)) + return FALSE; + + ptr = *_ptr; + if (!safe_read8 (type, ptr, end)) + FAIL (ctx, g_strdup ("ReturnType: Not enough room for the type")); + + if (type == MONO_TYPE_VOID || type == MONO_TYPE_TYPEDBYREF) { + *_ptr = ptr; + return TRUE; + } + + //it's a byref, update the cursor ptr + if (type == MONO_TYPE_BYREF) + *_ptr = ptr; + + return parse_type (ctx, _ptr, end); +} + +static gboolean +parse_param (VerifyContext *ctx, const char **_ptr, const char *end) +{ + const char *ptr; + unsigned type = 0; + + if (!parse_custom_mods (ctx, _ptr, end)) + return FALSE; + + ptr = *_ptr; + if (!safe_read8 (type, ptr, end)) + FAIL (ctx, g_strdup ("Param: Not enough room for the type")); + + if (type == MONO_TYPE_TYPEDBYREF) { + *_ptr = ptr; + return TRUE; + } + + //it's a byref, update the cursor ptr + if (type == MONO_TYPE_BYREF) { + *_ptr = ptr; + if (!parse_custom_mods (ctx, _ptr, end)) + return FALSE; + } + + return parse_type (ctx, _ptr, end); +} + +static gboolean +parse_method_signature (VerifyContext *ctx, const char **_ptr, const char *end, gboolean allow_sentinel, gboolean allow_unmanaged) +{ + unsigned cconv = 0; + unsigned param_count = 0, gparam_count = 0, type = 0, i; + const char *ptr = *_ptr; + gboolean saw_sentinel = FALSE; + + if (!safe_read8 (cconv, ptr, end)) + FAIL (ctx, g_strdup ("MethodSig: Not enough room for the call conv")); + + if (cconv & 0x80) + FAIL (ctx, g_strdup ("MethodSig: CallConv has 0x80 set")); + + if (allow_unmanaged) { + if ((cconv & 0x0F) > MONO_CALL_VARARG) + FAIL (ctx, g_strdup_printf ("MethodSig: CallConv is not valid, it's %x", cconv & 0x0F)); + } else if ((cconv & 0x0F) != MONO_CALL_DEFAULT && (cconv & 0x0F) != MONO_CALL_VARARG) + FAIL (ctx, g_strdup_printf ("MethodSig: CallConv is not Default or Vararg, it's %x", cconv & 0x0F)); + + if ((cconv & 0x10) && !safe_read_cint (gparam_count, ptr, end)) + FAIL (ctx, g_strdup ("MethodSig: Not enough room for the generic param count")); + + if ((cconv & 0x10) && gparam_count == 0) + FAIL (ctx, g_strdup ("MethodSig: Signature with generics but zero arity")); + + if (allow_unmanaged && (cconv & 0x10)) + FAIL (ctx, g_strdup ("MethodSig: Standalone signature with generic params")); + + if (!safe_read_cint (param_count, ptr, end)) + FAIL (ctx, g_strdup ("MethodSig: Not enough room for the param count")); + + if (!parse_return_type (ctx, &ptr, end)) + FAIL (ctx, g_strdup ("MethodSig: Error parsing return type")); + + for (i = 0; i < param_count; ++i) { + if (allow_sentinel) { + if (!safe_read8 (type, ptr, end)) + FAIL (ctx, g_strdup_printf ("MethodSig: Not enough room for param %d type", i)); + + if (type == MONO_TYPE_SENTINEL) { + if ((cconv & 0x0F) != MONO_CALL_VARARG) + FAIL (ctx, g_strdup ("MethodSig: Found sentinel but signature is not vararg")); + + if (saw_sentinel) + FAIL (ctx, g_strdup ("MethodSig: More than one sentinel type")); + + saw_sentinel = TRUE; + } else { + --ptr; + } + } + + if (!parse_param (ctx, &ptr, end)) + FAIL (ctx, g_strdup_printf ("MethodSig: Error parsing arg %d", i)); + } + + *_ptr = ptr; + return TRUE; +} + +static gboolean +parse_property_signature (VerifyContext *ctx, const char **_ptr, const char *end) +{ + unsigned type = 0; + unsigned sig = 0; + unsigned param_count = 0, i; + const char *ptr = *_ptr; + + if (!safe_read8 (sig, ptr, end)) + FAIL (ctx, g_strdup ("PropertySig: Not enough room for signature")); + + if (sig != 0x08 && sig != 0x28) + FAIL (ctx, g_strdup_printf ("PropertySig: Signature is not 0x28 or 0x08: %x", sig)); + + if (!safe_read_cint (param_count, ptr, end)) + FAIL (ctx, g_strdup ("PropertySig: Not enough room for the param count")); + + if (!parse_custom_mods (ctx, &ptr, end)) + return FALSE; + + if (!safe_read8 (type, ptr, end)) + FAIL (ctx, g_strdup ("PropertySig: Not enough room for the type")); + + //check if it's a byref. safe_read8 did update ptr, so we rollback if it's not a byref + if (type != MONO_TYPE_BYREF) + --ptr; + + if (!parse_type (ctx, &ptr, end)) + FAIL (ctx, g_strdup ("PropertySig: Could not parse property type")); + + for (i = 0; i < param_count; ++i) { + if (!parse_custom_mods (ctx, &ptr, end)) + FAIL (ctx, g_strdup ("Type: Failed to parse pointer custom attr")); + if (!parse_type (ctx, &ptr, end)) + FAIL (ctx, g_strdup_printf ("PropertySig: Error parsing arg %d", i)); + } + + *_ptr = ptr; + return TRUE; +} + +static gboolean +parse_field (VerifyContext *ctx, const char **_ptr, const char *end) +{ + const char *ptr = *_ptr; + unsigned signature = 0; + + if (!safe_read8 (signature, ptr, end)) + FAIL (ctx, g_strdup ("Field: Not enough room for field signature")); + + if (signature != 0x06) + FAIL (ctx, g_strdup_printf ("Field: Invalid signature 0x%x, must be 6", signature)); + + if (!parse_custom_mods (ctx, &ptr, end)) + return FALSE; + + if (safe_read8 (signature, ptr, end)) { + if (signature != MONO_TYPE_BYREF) + --ptr; + } + *_ptr = ptr; + + return parse_type (ctx, _ptr, end); +} + +static gboolean +parse_locals_signature (VerifyContext *ctx, const char **_ptr, const char *end) +{ + unsigned sig = 0; + unsigned locals_count = 0, i; + const char *ptr = *_ptr; + + if (!safe_read8 (sig, ptr, end)) + FAIL (ctx, g_strdup ("LocalsSig: Not enough room for signature")); + + if (sig != 0x07) + FAIL (ctx, g_strdup_printf ("LocalsSig: Signature is not 0x28 or 0x08: %x", sig)); + + if (!safe_read_cint (locals_count, ptr, end)) + FAIL (ctx, g_strdup ("LocalsSig: Not enough room for the param count")); + + /* LAMEIMPL: MS sometimes generates empty local signatures and its verifier is ok with. + if (locals_count == 0) + FAIL (ctx, g_strdup ("LocalsSig: Signature with zero locals")); + */ + + for (i = 0; i < locals_count; ++i) { + if (!safe_read8 (sig, ptr, end)) + FAIL (ctx, g_strdup ("LocalsSig: Not enough room for type")); + + while (sig == MONO_TYPE_CMOD_REQD || sig == MONO_TYPE_CMOD_OPT || sig == MONO_TYPE_PINNED) { + if (sig != MONO_TYPE_PINNED && !parse_custom_mods (ctx, &ptr, end)) + FAIL (ctx, g_strdup_printf ("LocalsSig: Error parsing local %d", i)); + if (!safe_read8 (sig, ptr, end)) + FAIL (ctx, g_strdup ("LocalsSig: Not enough room for type")); + } + + if (sig == MONO_TYPE_BYREF) { + if (!safe_read8 (sig, ptr, end)) + FAIL (ctx, g_strdup_printf ("Type: Not enough room for byref type for local %d", i)); + if (sig == MONO_TYPE_TYPEDBYREF) + FAIL (ctx, g_strdup_printf ("Type: Invalid type typedref& for local %d", i)); + } + + if (sig == MONO_TYPE_TYPEDBYREF) + continue; + + --ptr; + + if (!parse_type (ctx, &ptr, end)) + FAIL (ctx, g_strdup_printf ("LocalsSig: Error parsing local %d", i)); + } + + *_ptr = ptr; + return TRUE; +} + +static gboolean +is_valid_field_signature (VerifyContext *ctx, guint32 offset) +{ + guint32 size = 0; + unsigned signature = 0; + const char *ptr = NULL, *end; + + if (!decode_signature_header (ctx, offset, &size, &ptr)) + FAIL (ctx, g_strdup ("FieldSig: Could not decode signature header")); + end = ptr + size; + + if (!safe_read8 (signature, ptr, end)) + FAIL (ctx, g_strdup ("FieldSig: Not enough room for the signature")); + + if (signature != 6) + FAIL (ctx, g_strdup_printf ("FieldSig: Invalid signature %x", signature)); + --ptr; + + return parse_field (ctx, &ptr, end); +} + +static gboolean +is_valid_method_signature (VerifyContext *ctx, guint32 offset) +{ + guint32 size = 0; + const char *ptr = NULL, *end; + + if (!decode_signature_header (ctx, offset, &size, &ptr)) + FAIL (ctx, g_strdup ("MethodSig: Could not decode signature header")); + end = ptr + size; + + return parse_method_signature (ctx, &ptr, end, FALSE, FALSE); +} + +static gboolean +is_valid_memberref_method_signature (VerifyContext *ctx, guint32 offset) +{ + guint32 size = 0; + const char *ptr = NULL, *end; + + if (!decode_signature_header (ctx, offset, &size, &ptr)) + FAIL (ctx, g_strdup ("MemberRefSig: Could not decode signature header")); + end = ptr + size; + + return parse_method_signature (ctx, &ptr, end, TRUE, FALSE); +} + + +static gboolean +is_valid_method_or_field_signature (VerifyContext *ctx, guint32 offset) +{ + guint32 size = 0; + unsigned signature = 0; + const char *ptr = NULL, *end; + + if (!decode_signature_header (ctx, offset, &size, &ptr)) + FAIL (ctx, g_strdup ("MemberRefSig: Could not decode signature header")); + end = ptr + size; + + if (!safe_read8 (signature, ptr, end)) + FAIL (ctx, g_strdup ("MemberRefSig: Not enough room for the call conv")); + --ptr; + + if (signature == 0x06) + return parse_field (ctx, &ptr, end); + + return parse_method_signature (ctx, &ptr, end, TRUE, FALSE); +} + +static gboolean +is_valid_cattr_blob (VerifyContext *ctx, guint32 offset) +{ + guint32 size = 0; + unsigned prolog = 0; + const char *ptr = NULL, *end; + + if (!offset) + return TRUE; + + if (!decode_signature_header (ctx, offset, &size, &ptr)) + FAIL (ctx, g_strdup ("CustomAttribute: Could not decode signature header")); + end = ptr + size; + + if (!safe_read16 (prolog, ptr, end)) + FAIL (ctx, g_strdup ("CustomAttribute: Not enough room for prolog")); + + if (prolog != 1) + FAIL (ctx, g_strdup_printf ("CustomAttribute: Prolog is 0x%x, expected 0x1", prolog)); + + return TRUE; +} + +static gboolean +is_valid_cattr_type (MonoType *type) +{ + MonoClass *klass; + + if (type->type == MONO_TYPE_OBJECT || (type->type >= MONO_TYPE_BOOLEAN && type->type <= MONO_TYPE_STRING)) + return TRUE; + + if (type->type == MONO_TYPE_VALUETYPE) { + klass = mono_class_from_mono_type (type); + return klass && klass->enumtype; + } + + if (type->type == MONO_TYPE_CLASS) + return mono_class_from_mono_type (type) == mono_defaults.systemtype_class; + + return FALSE; +} + +static gboolean +is_valid_ser_string_full (VerifyContext *ctx, const char **str_start, guint32 *str_len, const char **_ptr, const char *end) +{ + guint32 size = 0; + const char *ptr = *_ptr; + + *str_start = NULL; + *str_len = 0; + + if (ptr >= end) + FAIL (ctx, g_strdup ("CustomAttribute: Not enough room for string size")); + + /*NULL string*/ + if (*ptr == (char)0xFF) { + *_ptr = ptr + 1; + return TRUE; + } + + if (!safe_read_cint (size, ptr, end)) + FAIL (ctx, g_strdup ("CustomAttribute: Not enough room for string size")); + + if (ADDP_IS_GREATER_OR_OVF (ptr, size, end)) + FAIL (ctx, g_strdup ("CustomAttribute: Not enough room for string")); + + *str_start = ptr; + *str_len = size; + + *_ptr = ptr + size; + return TRUE; +} + +static gboolean +is_valid_ser_string (VerifyContext *ctx, const char **_ptr, const char *end) +{ + const char *dummy_str; + guint32 dummy_int; + return is_valid_ser_string_full (ctx, &dummy_str, &dummy_int, _ptr, end); +} + +static MonoClass* +get_enum_by_encoded_name (VerifyContext *ctx, const char **_ptr, const char *end) +{ + MonoError error; + MonoType *type; + MonoClass *klass; + const char *str_start = NULL; + const char *ptr = *_ptr; + char *enum_name; + guint32 str_len = 0; + + if (!is_valid_ser_string_full (ctx, &str_start, &str_len, &ptr, end)) + return NULL; + + /*NULL or empty string*/ + if (str_start == NULL || str_len == 0) { + ADD_ERROR_NO_RETURN (ctx, g_strdup ("CustomAttribute: Null or empty enum name")); + return NULL; + } + + enum_name = (char *)g_memdup (str_start, str_len + 1); + enum_name [str_len] = 0; + type = mono_reflection_type_from_name_checked (enum_name, ctx->image, &error); + if (!type || !is_ok (&error)) { + ADD_ERROR_NO_RETURN (ctx, g_strdup_printf ("CustomAttribute: Invalid enum class %s, due to %s", enum_name, mono_error_get_message (&error))); + g_free (enum_name); + mono_error_cleanup (&error); + return NULL; + } + g_free (enum_name); + + klass = mono_class_from_mono_type (type); + if (!klass || !klass->enumtype) { + ADD_ERROR_NO_RETURN (ctx, g_strdup_printf ("CustomAttribute:Class %s::%s is not an enum", klass->name_space, klass->name)); + return NULL; + } + + *_ptr = ptr; + return klass; +} + +static gboolean +is_valid_fixed_param (VerifyContext *ctx, MonoType *mono_type, const char **_ptr, const char *end) +{ + MonoClass *klass; + const char *ptr = *_ptr; + int elem_size = 0; + guint32 element_count, i; + int type; + + klass = mono_type->data.klass; + type = mono_type->type; + +handle_enum: + switch (type) { + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + elem_size = 1; + break; + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_CHAR: + elem_size = 2; + break; + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_R4: + elem_size = 4; + break; + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_R8: + elem_size = 8; + break; + + case MONO_TYPE_STRING: + *_ptr = ptr; + return is_valid_ser_string (ctx, _ptr, end); + + case MONO_TYPE_OBJECT: { + unsigned sub_type = 0; + if (!safe_read8 (sub_type, ptr, end)) + FAIL (ctx, g_strdup ("CustomAttribute: Not enough room for array type")); + + if (sub_type >= MONO_TYPE_BOOLEAN && sub_type <= MONO_TYPE_STRING) { + type = sub_type; + goto handle_enum; + } + if (sub_type == MONO_TYPE_ENUM) { + klass = get_enum_by_encoded_name (ctx, &ptr, end); + if (!klass) + return FALSE; + + klass = klass->element_class; + type = klass->byval_arg.type; + goto handle_enum; + } + if (sub_type == 0x50) { /*Type*/ + *_ptr = ptr; + return is_valid_ser_string (ctx, _ptr, end); + } + if (sub_type == MONO_TYPE_SZARRAY) { + MonoType simple_type = {{0}}; + unsigned etype = 0; + if (!safe_read8 (etype, ptr, end)) + FAIL (ctx, g_strdup ("CustomAttribute: Not enough room for array element type")); + + if (etype == MONO_TYPE_ENUM) { + klass = get_enum_by_encoded_name (ctx, &ptr, end); + if (!klass) + return FALSE; + } else if (etype == 0x50 || etype == MONO_TYPE_CLASS) { + klass = mono_defaults.systemtype_class; + } else if ((etype >= MONO_TYPE_BOOLEAN && etype <= MONO_TYPE_STRING) || etype == 0x51) { + simple_type.type = etype == 0x51 ? MONO_TYPE_OBJECT : (MonoTypeEnum)etype; + klass = mono_class_from_mono_type (&simple_type); + } else + FAIL (ctx, g_strdup_printf ("CustomAttribute: Invalid array element type %x", etype)); + + type = MONO_TYPE_SZARRAY; + goto handle_enum; + } + FAIL (ctx, g_strdup_printf ("CustomAttribute: Invalid boxed object type %x", sub_type)); + } + + case MONO_TYPE_CLASS: + if (klass && klass->enumtype) { + klass = klass->element_class; + type = klass->byval_arg.type; + goto handle_enum; + } + + if (klass != mono_defaults.systemtype_class) + FAIL (ctx, g_strdup_printf ("CustomAttribute: Invalid class parameter type %s:%s ",klass->name_space, klass->name)); + *_ptr = ptr; + return is_valid_ser_string (ctx, _ptr, end); + + case MONO_TYPE_VALUETYPE: + if (!klass || !klass->enumtype) + FAIL (ctx, g_strdup_printf ("CustomAttribute: Invalid valuetype parameter expected enum %s:%s ",klass->name_space, klass->name)); + + klass = klass->element_class; + type = klass->byval_arg.type; + goto handle_enum; + + case MONO_TYPE_SZARRAY: + mono_type = &klass->byval_arg; + if (!is_valid_cattr_type (mono_type)) + FAIL (ctx, g_strdup_printf ("CustomAttribute: Invalid array element type %s:%s ",klass->name_space, klass->name)); + if (!safe_read32 (element_count, ptr, end)) + FAIL (ctx, g_strdup_printf ("CustomAttribute: Invalid class parameter type %s:%s ",klass->name_space, klass->name)); + if (element_count == 0xFFFFFFFFu) { + *_ptr = ptr; + return TRUE; + } + for (i = 0; i < element_count; ++i) { + if (!is_valid_fixed_param (ctx, mono_type, &ptr, end)) + return FALSE; + } + *_ptr = ptr; + return TRUE; + default: + FAIL (ctx, g_strdup_printf ("CustomAttribute: Invalid parameter type %x ", type)); + } + + if (ADDP_IS_GREATER_OR_OVF (ptr, elem_size, end)) + FAIL (ctx, g_strdup ("CustomAttribute: Not enough space for element")); + *_ptr = ptr + elem_size; + return TRUE; +} + +static gboolean +is_valid_cattr_content (VerifyContext *ctx, MonoMethod *ctor, const char *ptr, guint32 size) +{ + MonoError error; + unsigned prolog = 0; + const char *end; + MonoMethodSignature *sig; + int args, i; + unsigned num_named; + + if (!ctor) + FAIL (ctx, g_strdup ("CustomAttribute: Invalid constructor")); + + sig = mono_method_signature_checked (ctor, &error); + if (!mono_error_ok (&error)) { + ADD_ERROR_NO_RETURN (ctx, g_strdup_printf ("CustomAttribute: Invalid constructor signature %s", mono_error_get_message (&error))); + mono_error_cleanup (&error); + return FALSE; + } + + if (sig->sentinelpos != -1 || sig->call_convention == MONO_CALL_VARARG) + FAIL (ctx, g_strdup ("CustomAttribute: Constructor cannot have VARAG signature")); + + end = ptr + size; + + if (!safe_read16 (prolog, ptr, end)) + FAIL (ctx, g_strdup ("CustomAttribute: Not enough room for prolog")); + + if (prolog != 1) + FAIL (ctx, g_strdup_printf ("CustomAttribute: Prolog is 0x%x, expected 0x1", prolog)); + + args = sig->param_count; + for (i = 0; i < args; ++i) { + MonoType *arg_type = sig->params [i]; + if (!is_valid_fixed_param (ctx, arg_type, &ptr, end)) + return FALSE; + } + + if (!safe_read16 (num_named, ptr, end)) + FAIL (ctx, g_strdup ("CustomAttribute: Not enough space for num_named field")); + + for (i = 0; i < num_named; ++i) { + MonoType *type, simple_type = {{0}}; + unsigned kind; + + if (!safe_read8 (kind, ptr, end)) + FAIL (ctx, g_strdup_printf ("CustomAttribute: Not enough space for named parameter %d kind", i)); + if (kind != 0x53 && kind != 0x54) + FAIL (ctx, g_strdup_printf ("CustomAttribute: Invalid named parameter %d kind %x", i, kind)); + if (!safe_read8 (kind, ptr, end)) + FAIL (ctx, g_strdup_printf ("CustomAttribute: Not enough space for named parameter %d type", i)); + + if (kind >= MONO_TYPE_BOOLEAN && kind <= MONO_TYPE_STRING) { + simple_type.type = (MonoTypeEnum)kind; + type = &simple_type; + } else if (kind == MONO_TYPE_ENUM) { + MonoClass *klass = get_enum_by_encoded_name (ctx, &ptr, end); + if (!klass) + return FALSE; + type = &klass->byval_arg; + } else if (kind == 0x50) { + type = &mono_defaults.systemtype_class->byval_arg; + } else if (kind == 0x51) { + type = &mono_defaults.object_class->byval_arg; + } else if (kind == MONO_TYPE_SZARRAY) { + MonoClass *klass; + unsigned etype = 0; + if (!safe_read8 (etype, ptr, end)) + FAIL (ctx, g_strdup ("CustomAttribute: Not enough room for array element type")); + + if (etype == MONO_TYPE_ENUM) { + klass = get_enum_by_encoded_name (ctx, &ptr, end); + if (!klass) + return FALSE; + } else if (etype == 0x50 || etype == MONO_TYPE_CLASS) { + klass = mono_defaults.systemtype_class; + } else if ((etype >= MONO_TYPE_BOOLEAN && etype <= MONO_TYPE_STRING) || etype == 0x51) { + simple_type.type = etype == 0x51 ? MONO_TYPE_OBJECT : (MonoTypeEnum)etype; + klass = mono_class_from_mono_type (&simple_type); + } else + FAIL (ctx, g_strdup_printf ("CustomAttribute: Invalid array element type %x", etype)); + + type = &mono_array_class_get (klass, 1)->byval_arg; + } else { + FAIL (ctx, g_strdup_printf ("CustomAttribute: Invalid named parameter type %x", kind)); + } + + if (!is_valid_ser_string (ctx, &ptr, end)) + return FALSE; + + if (!is_valid_fixed_param (ctx, type, &ptr, end)) + return FALSE; + + } + + return TRUE; +} + +static gboolean +is_valid_marshal_spec (VerifyContext *ctx, guint32 offset) +{ + OffsetAndSize blob = get_metadata_stream (ctx, &ctx->image->heap_blob); + //TODO do proper verification + return blob.size >= 1 && blob.size - 1 >= offset; +} + +static gboolean +is_valid_permission_set (VerifyContext *ctx, guint32 offset) +{ + OffsetAndSize blob = get_metadata_stream (ctx, &ctx->image->heap_blob); + //TODO do proper verification + return blob.size >= 1 && blob.size - 1 >= offset; +} + +static gboolean +is_valid_standalonesig_blob (VerifyContext *ctx, guint32 offset) +{ + guint32 size = 0; + unsigned signature = 0; + const char *ptr = NULL, *end; + + if (!decode_signature_header (ctx, offset, &size, &ptr)) + FAIL (ctx, g_strdup ("StandAloneSig: Could not decode signature header")); + end = ptr + size; + + if (!safe_read8 (signature, ptr, end)) + FAIL (ctx, g_strdup ("StandAloneSig: Not enough room for the call conv")); + + --ptr; + if (signature == 0x07) + return parse_locals_signature (ctx, &ptr, end); + + /*F# and managed C++ produce standalonesig for fields even thou the spec doesn't mention it.*/ + if (signature == 0x06) + return parse_field (ctx, &ptr, end); + + return parse_method_signature (ctx, &ptr, end, TRUE, TRUE); +} + +static gboolean +is_valid_property_sig_blob (VerifyContext *ctx, guint32 offset) +{ + guint32 size = 0; + const char *ptr = NULL, *end; + + if (!decode_signature_header (ctx, offset, &size, &ptr)) + FAIL (ctx, g_strdup ("PropertySig: Could not decode signature header")); + end = ptr + size; + + return parse_property_signature (ctx, &ptr, end); +} + +static gboolean +is_valid_typespec_blob (VerifyContext *ctx, guint32 offset) +{ + guint32 size = 0; + const char *ptr = NULL, *end; + unsigned type = 0; + + if (!decode_signature_header (ctx, offset, &size, &ptr)) + FAIL (ctx, g_strdup ("TypeSpec: Could not decode signature header")); + end = ptr + size; + + if (!parse_custom_mods (ctx, &ptr, end)) + return FALSE; + + if (!safe_read8 (type, ptr, end)) + FAIL (ctx, g_strdup ("TypeSpec: Not enough room for type")); + + if (type == MONO_TYPE_BYREF) { + if (!safe_read8 (type, ptr, end)) + FAIL (ctx, g_strdup ("TypeSpec: Not enough room for byref type")); + if (type == MONO_TYPE_TYPEDBYREF) + FAIL (ctx, g_strdup ("TypeSpec: Invalid type typedref&")); + } + + if (type == MONO_TYPE_TYPEDBYREF) + return TRUE; + + --ptr; + return parse_type (ctx, &ptr, end); +} + +static gboolean +is_valid_methodspec_blob (VerifyContext *ctx, guint32 offset) +{ + guint32 size = 0; + const char *ptr = NULL, *end; + unsigned type = 0; + unsigned count = 0, i; + + if (!decode_signature_header (ctx, offset, &size, &ptr)) + FAIL (ctx, g_strdup ("MethodSpec: Could not decode signature header")); + end = ptr + size; + + if (!safe_read8 (type, ptr, end)) + FAIL (ctx, g_strdup ("MethodSpec: Not enough room for call convention")); + + if (type != 0x0A) + FAIL (ctx, g_strdup_printf ("MethodSpec: Invalid call convention 0x%x, expected 0x0A", type)); + + if (!safe_read_cint (count, ptr, end)) + FAIL (ctx, g_strdup ("MethodSpec: Not enough room for parameter count")); + + if (!count) + FAIL (ctx, g_strdup ("MethodSpec: Zero generic argument count")); + + for (i = 0; i < count; ++i) { + if (!parse_custom_mods (ctx, &ptr, end)) + return FALSE; + if (!parse_type (ctx, &ptr, end)) + FAIL (ctx, g_strdup_printf ("MethodSpec: Could not parse parameter %d", i + 1)); + } + return TRUE; +} + +static gboolean +is_valid_blob_object (VerifyContext *ctx, guint32 offset, guint32 minsize) +{ + OffsetAndSize blob = get_metadata_stream (ctx, &ctx->image->heap_blob); + guint32 entry_size, bytes; + + if (blob.size < offset) + return FALSE; + + if (!decode_value (ctx->data + offset + blob.offset, blob.size - blob.offset, &entry_size, &bytes)) + return FALSE; + + if (entry_size < minsize) + return FALSE; + + if (CHECK_ADD4_OVERFLOW_UN (entry_size, bytes)) + return FALSE; + entry_size += bytes; + + return !ADD_IS_GREATER_OR_OVF (offset, entry_size, blob.size); +} + +static gboolean +is_valid_constant (VerifyContext *ctx, guint32 type, guint32 offset) +{ + OffsetAndSize blob = get_metadata_stream (ctx, &ctx->image->heap_blob); + guint32 size, entry_size, bytes; + + if (blob.size < offset) + FAIL (ctx, g_strdup ("ContantValue: invalid offset")); + + if (!decode_value (ctx->data + offset + blob.offset, blob.size - blob.offset, &entry_size, &bytes)) + FAIL (ctx, g_strdup ("ContantValue: not enough space to decode size")); + + if (type == MONO_TYPE_STRING) { + //String is encoded as: compressed_int:len len *bytes + offset += bytes; + + if (ADD_IS_GREATER_OR_OVF (offset, entry_size, blob.size)) + FAIL (ctx, g_strdup_printf ("ContantValue: not enough space for string, required %d but got %d", entry_size * 2, blob.size - offset)); + + return TRUE; + } + + switch (type) { + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + size = 1; + break; + case MONO_TYPE_CHAR: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + size = 2; + break; + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_R4: + case MONO_TYPE_CLASS: + size = 4; + break; + + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_R8: + size = 8; + break; + default: + g_assert_not_reached (); + } + + if (size != entry_size) + FAIL (ctx, g_strdup_printf ("ContantValue: Expected size %d but got %d", size, entry_size)); + + offset += bytes; + + if (ADD_IS_GREATER_OR_OVF (offset, size, blob.size)) + FAIL (ctx, g_strdup_printf ("ContantValue: Not enough room for constant, required %d but have %d", size, blob.size - offset)); + + if (type == MONO_TYPE_CLASS && read32 (ctx->data + blob.offset + offset)) + FAIL (ctx, g_strdup_printf ("ContantValue: Type is class but value is not null")); + return TRUE; +} + +#define FAT_HEADER_INVALID_FLAGS ~(0x3 | 0x8 | 0x10 | 0xF000) +//only 0x01, 0x40 and 0x80 are allowed +#define SECTION_HEADER_INVALID_FLAGS 0x3E + +static gboolean +is_valid_method_header (VerifyContext *ctx, guint32 rva, guint32 *locals_token) +{ + unsigned local_vars_tok, code_size, offset = mono_cli_rva_image_map (ctx->image, rva); + unsigned header = 0; + unsigned fat_header = 0, size = 0, max_stack; + const char *ptr = NULL, *end; + + *locals_token = 0; + + if (offset == INVALID_ADDRESS) + FAIL (ctx, g_strdup ("MethodHeader: Invalid RVA")); + + ptr = ctx->data + offset; + end = ctx->data + ctx->size; /*no worries if it spawns multiple sections*/ + + if (!safe_read8 (header, ptr, end)) + FAIL (ctx, g_strdup ("MethodHeader: Not enough room for header")); + + switch (header & 0x3) { + case 0: + case 1: + FAIL (ctx, g_strdup_printf ("MethodHeader: Invalid header type 0x%x", header & 0x3)); + case 2: + header >>= 2; + if (ADDP_IS_GREATER_OR_OVF (ptr, header, end)) + FAIL (ctx, g_strdup_printf ("MethodHeader: Not enough room for method body. Required %d, but only %d is available", header, (int)(end - ptr))); + return TRUE; + } + //FAT HEADER + --ptr; + if (!safe_read16 (fat_header, ptr, end)) + FAIL (ctx, g_strdup ("MethodHeader: Not enough room for fat header")); + + size = (fat_header >> 12) & 0xF; + if (size != 3) + FAIL (ctx, g_strdup ("MethodHeader: header size must be 3")); + + if (!safe_read16 (max_stack, ptr, end)) + FAIL (ctx, g_strdup ("MethodHeader: Not enough room for max stack")); + + if (!safe_read32 (code_size, ptr, end)) + FAIL (ctx, g_strdup ("MethodHeader: Not enough room for code size")); + + if (!safe_read32 (local_vars_tok, ptr, end)) + FAIL (ctx, g_strdup ("MethodHeader: Not enough room for local vars tok")); + + if (local_vars_tok) { + if (((local_vars_tok >> 24) & 0xFF) != 0x11) + FAIL (ctx, g_strdup_printf ("MethodHeader: Invalid local vars signature table 0x%x", ((local_vars_tok >> 24) & 0xFF))); + if ((local_vars_tok & 0xFFFFFF) > ctx->image->tables [MONO_TABLE_STANDALONESIG].rows) + FAIL (ctx, g_strdup_printf ("MethodHeader: Invalid local vars signature points to invalid row 0x%x", local_vars_tok & 0xFFFFFF)); + if (!(local_vars_tok & 0xFFFFFF)) + FAIL (ctx, g_strdup_printf ("MethodHeader: Invalid local vars signature with zero index")); + *locals_token = local_vars_tok & 0xFFFFFF; + } + + if (fat_header & FAT_HEADER_INVALID_FLAGS) + FAIL (ctx, g_strdup_printf ("MethodHeader: Invalid fat signature flags %x", fat_header & FAT_HEADER_INVALID_FLAGS)); + + if (ADDP_IS_GREATER_OR_OVF (ptr, code_size, end)) + FAIL (ctx, g_strdup_printf ("MethodHeader: Not enough room for code %d", code_size)); + + if (!(fat_header & 0x08)) + return TRUE; + + ptr += code_size; + + do { + unsigned section_header = 0, section_size = 0; + gboolean is_fat; + + ptr = dword_align (ptr); + if (!safe_read32 (section_header, ptr, end)) + FAIL (ctx, g_strdup ("MethodHeader: Not enough room for data section header")); + + if (section_header & SECTION_HEADER_INVALID_FLAGS) + FAIL (ctx, g_strdup_printf ("MethodHeader: Invalid section header flags 0x%x", section_header & SECTION_HEADER_INVALID_FLAGS)); + + is_fat = (section_header & METHOD_HEADER_SECTION_FAT_FORMAT) != 0; + section_size = (section_header >> 8) & (is_fat ? 0xFFFFFF : 0xFF); + + if (section_size < 4) + FAIL (ctx, g_strdup_printf ("MethodHeader: Section size too small")); + + if (ADDP_IS_GREATER_OR_OVF (ptr, section_size - 4, end)) /*must be section_size -4 as ptr was incremented by safe_read32*/ + FAIL (ctx, g_strdup_printf ("MethodHeader: Not enough room for section content %d", section_size)); + + if (section_header & METHOD_HEADER_SECTION_EHTABLE) { + guint32 i, clauses = section_size / (is_fat ? 24 : 12); + /* + LAMEIMPL: MS emits section_size without accounting for header size. + Mono does as the spec says. section_size is header + section + MS's peverify happily accepts both. + */ + if ((clauses * (is_fat ? 24 : 12) != section_size) && (clauses * (is_fat ? 24 : 12) + 4 != section_size)) + FAIL (ctx, g_strdup_printf ("MethodHeader: Invalid EH section size %d, it's not of the expected size %d", section_size, clauses * (is_fat ? 24 : 12))); + + /* only verify the class token is verified as the rest is done by the IL verifier*/ + for (i = 0; i < clauses; ++i) { + unsigned flags = *(unsigned char*)ptr; + unsigned class_token = 0; + ptr += (is_fat ? 20 : 8); + if (!safe_read32 (class_token, ptr, end)) + FAIL (ctx, g_strdup_printf ("MethodHeader: Not enough room for section %d", i)); + if (flags == MONO_EXCEPTION_CLAUSE_NONE && class_token) { + guint table = mono_metadata_token_table (class_token); + if (table != MONO_TABLE_TYPEREF && table != MONO_TABLE_TYPEDEF && table != MONO_TABLE_TYPESPEC) + FAIL (ctx, g_strdup_printf ("MethodHeader: Invalid section %d class token table %x", i, table)); + if (mono_metadata_token_index (class_token) > ctx->image->tables [table].rows) + FAIL (ctx, g_strdup_printf ("MethodHeader: Invalid section %d class token index %x", i, mono_metadata_token_index (class_token))); + } + } + } + + if (!(section_header & METHOD_HEADER_SECTION_MORE_SECTS)) + break; + } while (1); + return TRUE; +} + +static void +verify_module_table (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_MODULE]; + guint32 data [MONO_MODULE_SIZE]; + + if (table->rows != 1) + ADD_ERROR (ctx, g_strdup_printf ("Module table must have exactly one row, but have %d", table->rows)); + + mono_metadata_decode_row (table, 0, data, MONO_MODULE_SIZE); + + if (!is_valid_non_empty_string (ctx, data [MONO_MODULE_NAME])) + ADD_ERROR (ctx, g_strdup_printf ("Module has an invalid name, string index 0x%08x", data [MONO_MODULE_NAME])); + + if (!is_valid_guid (ctx, data [MONO_MODULE_MVID])) + ADD_ERROR (ctx, g_strdup_printf ("Module has an invalid Mvid, guid index %x", data [MONO_MODULE_MVID])); + + if (data [MONO_MODULE_ENC] != 0) + ADD_ERROR (ctx, g_strdup_printf ("Module has a non zero Enc field %x", data [MONO_MODULE_ENC])); + + if (data [MONO_MODULE_ENCBASE] != 0) + ADD_ERROR (ctx, g_strdup_printf ("Module has a non zero EncBase field %x", data [MONO_MODULE_ENCBASE])); +} + +static void +verify_typeref_table (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_TYPEREF]; + MonoError error; + guint32 i; + + for (i = 0; i < table->rows; ++i) { + mono_verifier_verify_typeref_row (ctx->image, i, &error); + add_from_mono_error (ctx, &error); + } +} + +/*bits 9,11,14,15,19,21,24-31 */ +#define INVALID_TYPEDEF_FLAG_BITS ((1 << 6) | (1 << 9) | (1 << 15) | (1 << 19) | (1 << 21) | 0xFF000000) +static void +verify_typedef_table (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_TYPEDEF]; + guint32 data [MONO_TYPEDEF_SIZE]; + guint32 fieldlist = 1, methodlist = 1, visibility; + int i; + + if (table->rows == 0) + ADD_ERROR (ctx, g_strdup_printf ("Typedef table must have exactly at least one row")); + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_TYPEDEF_SIZE); + if (data [MONO_TYPEDEF_FLAGS] & INVALID_TYPEDEF_FLAG_BITS) + ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d invalid flags field 0x%08x rejected bits: 0x%08x", i, data [MONO_TYPEDEF_FLAGS], data [MONO_TYPEDEF_FLAGS] & INVALID_TYPEDEF_FLAG_BITS)); + + if ((data [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_LAYOUT_MASK) == 0x18) + ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d invalid class layout 0x18", i)); + + if ((data [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_STRING_FORMAT_MASK) == 0x30000) + ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d mono doesn't support custom string format", i)); + + if ((data [MONO_TYPEDEF_FLAGS] & 0xC00000) != 0) + ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d mono doesn't support custom string format", i)); + + if ((data [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_INTERFACE) && (data [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_ABSTRACT) == 0) + ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d for interface type must be abstract", i)); + + if (!data [MONO_TYPEDEF_NAME] || !is_valid_non_empty_string (ctx, data [MONO_TYPEDEF_NAME])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d invalid name token %08x", i, data [MONO_TYPEDEF_NAME])); + + if (data [MONO_TYPEREF_NAMESPACE] && !is_valid_non_empty_string (ctx, data [MONO_TYPEREF_NAMESPACE])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d invalid namespace token %08x", i, data [MONO_TYPEREF_NAMESPACE])); + + if (data [MONO_TYPEDEF_EXTENDS] && !is_valid_coded_index (ctx, TYPEDEF_OR_REF_DESC, data [MONO_TYPEDEF_EXTENDS])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d extend field coded index 0x%08x", i, data [MONO_TYPEDEF_EXTENDS])); + + if (data [MONO_TYPEDEF_EXTENDS] && !get_coded_index_token (TYPEDEF_OR_REF_DESC, data [MONO_TYPEDEF_EXTENDS])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d zero coded extend field coded index 0x%08x", i, data [MONO_TYPEDEF_EXTENDS])); + + visibility = data [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_VISIBILITY_MASK; + if ((visibility >= TYPE_ATTRIBUTE_NESTED_PUBLIC && visibility <= TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM) && + search_sorted_table (ctx, MONO_TABLE_NESTEDCLASS, MONO_NESTED_CLASS_NESTED, i + 1) == -1) + ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d has nested visibility but no rows in the NestedClass table", i)); + + if (data [MONO_TYPEDEF_FIELD_LIST] == 0) + ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d FieldList be be >= 1", i)); + + if (data [MONO_TYPEDEF_FIELD_LIST] > ctx->image->tables [MONO_TABLE_FIELD].rows + 1) + ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d FieldList rowid 0x%08x is out of range", i, data [MONO_TYPEDEF_FIELD_LIST])); + + if (data [MONO_TYPEDEF_FIELD_LIST] < fieldlist) + ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d FieldList rowid 0x%08x can't be smaller than of previous row 0x%08x", i, data [MONO_TYPEDEF_FIELD_LIST], fieldlist)); + + if (data [MONO_TYPEDEF_METHOD_LIST] == 0) + ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d MethodList be be >= 1", i)); + + if (data [MONO_TYPEDEF_METHOD_LIST] > ctx->image->tables [MONO_TABLE_METHOD].rows + 1) + ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d MethodList rowid 0x%08x is out of range", i, data [MONO_TYPEDEF_METHOD_LIST])); + + if (data [MONO_TYPEDEF_METHOD_LIST] < methodlist) + ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d MethodList rowid 0x%08x can't be smaller than of previous row 0x%08x", i, data [MONO_TYPEDEF_METHOD_LIST], methodlist)); + + fieldlist = data [MONO_TYPEDEF_FIELD_LIST]; + methodlist = data [MONO_TYPEDEF_METHOD_LIST]; + } +} + +static void +verify_typedef_table_full (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_TYPEDEF]; + guint32 data [MONO_TYPEDEF_SIZE]; + int i; + + if (table->rows == 0) + ADD_ERROR (ctx, g_strdup_printf ("Typedef table must have exactly at least one row")); + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_TYPEDEF_SIZE); + + if (i == 0) { + /*XXX it's ok if extends object, or anything at all, actually. */ + /*if (data [MONO_TYPEDEF_EXTENDS] != 0) + ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row 0 for the special type must have a null extend field")); + */ + continue; + } + + if (data [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_INTERFACE) { + if (data [MONO_TYPEDEF_EXTENDS]) + ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d for interface type must have a null extend field", i)); + } else { + gboolean is_sys_obj = typedef_is_system_object (ctx, data); + gboolean has_parent = get_coded_index_token (TYPEDEF_OR_REF_DESC, data [MONO_TYPEDEF_EXTENDS]) != 0; + + if (is_sys_obj) { + if (has_parent) + ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d for System.Object must have a null extend field", i)); + } else { + if (!has_parent) { + ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d for non-interface type must have a non-null extend field", i)); + } + } + } + } +} + +/*bits 3,11,14 */ +#define INVALID_FIELD_FLAG_BITS ((1 << 3) | (1 << 11) | (1 << 14)) +static void +verify_field_table (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_FIELD]; + guint32 data [MONO_FIELD_SIZE], flags, module_field_list; + int i; + + module_field_list = (guint32)-1; + if (ctx->image->tables [MONO_TABLE_TYPEDEF].rows > 1) { + MonoTableInfo *type = &ctx->image->tables [MONO_TABLE_TYPEDEF]; + module_field_list = mono_metadata_decode_row_col (type, 1, MONO_TYPEDEF_FIELD_LIST); + } + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_FIELD_SIZE); + flags = data [MONO_FIELD_FLAGS]; + + if (flags & INVALID_FIELD_FLAG_BITS) + ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d invalid flags field 0x%08x", i, flags)); + + if ((flags & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK) == 0x7) + ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d invalid field visibility 0x7", i)); + + if ((flags & (FIELD_ATTRIBUTE_LITERAL | FIELD_ATTRIBUTE_INIT_ONLY)) == (FIELD_ATTRIBUTE_LITERAL | FIELD_ATTRIBUTE_INIT_ONLY)) + ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d cannot be InitOnly and Literal at the same time", i)); + + if ((flags & FIELD_ATTRIBUTE_RT_SPECIAL_NAME) && !(flags & FIELD_ATTRIBUTE_SPECIAL_NAME)) + ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d is RTSpecialName but not SpecialName", i)); + + if ((flags & FIELD_ATTRIBUTE_LITERAL) && !(flags & FIELD_ATTRIBUTE_STATIC)) + ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d is Literal but not Static", i)); + + if ((flags & FIELD_ATTRIBUTE_HAS_FIELD_MARSHAL) && + search_sorted_table (ctx, MONO_TABLE_FIELDMARSHAL, MONO_FIELD_MARSHAL_PARENT, make_coded_token (HAS_FIELD_MARSHAL_DESC, MONO_TABLE_FIELD, i)) == -1) + ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d has FieldMarshal but there is no corresponding row in the FieldMarshal table", i)); + + if ((flags & FIELD_ATTRIBUTE_HAS_DEFAULT) && + search_sorted_table (ctx, MONO_TABLE_CONSTANT, MONO_CONSTANT_PARENT, make_coded_token (HAS_CONSTANT_DESC, MONO_TABLE_FIELD, i)) == -1) + ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d has Default but there is no corresponding row in the Constant table", i)); + + if ((flags & FIELD_ATTRIBUTE_LITERAL) && + search_sorted_table (ctx, MONO_TABLE_CONSTANT, MONO_CONSTANT_PARENT, make_coded_token (HAS_CONSTANT_DESC, MONO_TABLE_FIELD, i)) == -1) + ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d is Literal but there is no corresponding row in the Constant table", i)); + + if ((flags & FIELD_ATTRIBUTE_HAS_FIELD_RVA) && + search_sorted_table (ctx, MONO_TABLE_FIELDRVA, MONO_FIELD_RVA_FIELD, i + 1) == -1) + ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d has Default but there is no corresponding row in the Constant table", i)); + + if (!data [MONO_FIELD_NAME] || !is_valid_non_empty_string (ctx, data [MONO_FIELD_NAME])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d invalid name token %08x", i, data [MONO_FIELD_NAME])); + + if (data [MONO_FIELD_SIGNATURE] && !is_valid_blob_object (ctx, data [MONO_FIELD_SIGNATURE], 1)) + ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d invalid signature blob token 0x%x", i, data [MONO_FIELD_SIGNATURE])); + + //TODO verify contant flag + + if (i + 1 < module_field_list) { + guint32 access = flags & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK; + if (!(flags & FIELD_ATTRIBUTE_STATIC)) + ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d is a global variable but is not static", i)); + if (access != FIELD_ATTRIBUTE_COMPILER_CONTROLLED && access != FIELD_ATTRIBUTE_PRIVATE && access != FIELD_ATTRIBUTE_PUBLIC) + ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d is a global variable but have wrong visibility %x", i, access)); + } + } +} + +static void +verify_field_table_full (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_FIELD]; + guint32 data [MONO_FIELD_SIZE]; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_FIELD_SIZE); + + if (!data [MONO_FIELD_SIGNATURE] || !is_valid_field_signature (ctx, data [MONO_FIELD_SIGNATURE])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d invalid signature token %08x", i, data [MONO_FIELD_SIGNATURE])); + } +} + +/*bits 8,9,10,11,13,14,15*/ +#define INVALID_METHOD_IMPLFLAG_BITS ((1 << 9) | (1 << 10) | (1 << 11) | (1 << 13) | (1 << 14) | (1 << 15)) +static void +verify_method_table (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_METHOD]; + guint32 data [MONO_METHOD_SIZE], flags, implflags, rva, module_method_list, access, code_type; + guint32 paramlist = 1; + gboolean is_ctor, is_cctor; + const char *name; + int i; + + module_method_list = (guint32)-1; + if (ctx->image->tables [MONO_TABLE_TYPEDEF].rows > 1) { + MonoTableInfo *type = &ctx->image->tables [MONO_TABLE_TYPEDEF]; + module_method_list = mono_metadata_decode_row_col (type, 1, MONO_TYPEDEF_METHOD_LIST); + } + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_METHOD_SIZE); + rva = data [MONO_METHOD_RVA]; + implflags = data [MONO_METHOD_IMPLFLAGS]; + flags = data [MONO_METHOD_FLAGS]; + access = flags & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK; + code_type = implflags & METHOD_IMPL_ATTRIBUTE_CODE_TYPE_MASK; + + + if (implflags & INVALID_METHOD_IMPLFLAG_BITS) + ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d invalid implflags field 0x%08x", i, implflags)); + + if (access == 0x7) + ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d invalid MemberAccessMask 0x7", i)); + + if (!data [MONO_METHOD_NAME] || !is_valid_non_empty_string (ctx, data [MONO_METHOD_NAME])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d invalid name field 0x%08x", i, data [MONO_METHOD_NAME])); + + name = get_string_ptr (ctx, data [MONO_METHOD_NAME]); + is_ctor = !strcmp (".ctor", name); + is_cctor = !strcmp (".cctor", name); + + if ((is_ctor || is_cctor) && + search_sorted_table (ctx, MONO_TABLE_GENERICPARAM, MONO_GENERICPARAM_OWNER, make_coded_token (TYPE_OR_METHODDEF_DESC, MONO_TABLE_METHOD, i)) != -1) + ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d .ctor or .cctor has generic param", i)); + + if ((flags & METHOD_ATTRIBUTE_STATIC) && (flags & (METHOD_ATTRIBUTE_FINAL | METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_NEW_SLOT))) + ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is static and (final, virtual or new slot)", i)); + + if (flags & METHOD_ATTRIBUTE_ABSTRACT) { + if (flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) + ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is Abstract and PinvokeImpl", i)); + if (flags & METHOD_ATTRIBUTE_FINAL) + ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is Abstract and Final", i)); + if (!(flags & METHOD_ATTRIBUTE_VIRTUAL)) + ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is Abstract but not Virtual", i)); + } + + if (access == METHOD_ATTRIBUTE_COMPILER_CONTROLLED && (flags & (METHOD_ATTRIBUTE_RT_SPECIAL_NAME | METHOD_ATTRIBUTE_SPECIAL_NAME))) + ADD_WARNING (ctx, g_strdup_printf ("Invalid method row %d is CompileControlled and SpecialName or RtSpecialName", i)); + + if ((flags & METHOD_ATTRIBUTE_RT_SPECIAL_NAME) && !(flags & METHOD_ATTRIBUTE_SPECIAL_NAME)) + ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is RTSpecialName but not SpecialName", i)); + + //XXX no checks against cas stuff 10,11,12,13) + + //TODO check iface with .ctor (15,16) + + if (i + 1 < module_method_list) { + if (!(flags & METHOD_ATTRIBUTE_STATIC)) + ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is a global method but not Static", i)); + if (flags & (METHOD_ATTRIBUTE_ABSTRACT | METHOD_ATTRIBUTE_VIRTUAL)) + ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is a global method but is Abstract or Virtual", i)); + if (access == METHOD_ATTRIBUTE_FAMILY || access == METHOD_ATTRIBUTE_FAM_AND_ASSEM || access == METHOD_ATTRIBUTE_FAM_OR_ASSEM) + ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is a global method but not CompilerControled, Public, Private or Assembly", i)); + } + + //TODO check valuetype for synchronized + + if ((flags & (METHOD_ATTRIBUTE_FINAL | METHOD_ATTRIBUTE_NEW_SLOT | METHOD_ATTRIBUTE_STRICT)) && !(flags & METHOD_ATTRIBUTE_VIRTUAL)) + ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is (Final, NewSlot or Strict) but not Virtual", i)); + + if (flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) { + if (flags & METHOD_ATTRIBUTE_VIRTUAL) + ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is PinvokeImpl and Virtual", i)); + if (!(flags & METHOD_ATTRIBUTE_STATIC)) + ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is PinvokeImpl but not Static", i)); + } + + if (!(flags & METHOD_ATTRIBUTE_ABSTRACT) && !rva && !(flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) && + !(implflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) && code_type != METHOD_IMPL_ATTRIBUTE_RUNTIME) + ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is not Abstract and neither PinvokeImpl, Runtime, InternalCall or with RVA != 0", i)); + + if (access == METHOD_ATTRIBUTE_COMPILER_CONTROLLED && !(rva || (flags & METHOD_ATTRIBUTE_PINVOKE_IMPL))) + ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is CompilerControlled but neither RVA != 0 or PinvokeImpl", i)); + + //TODO check signature contents + + if (rva) { + if ((flags & (METHOD_ATTRIBUTE_ABSTRACT | METHOD_ATTRIBUTE_PINVOKE_IMPL)) || (implflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)) + ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d has RVA != 0 but is either Abstract, InternalCall or PinvokeImpl", i)); + if (code_type == METHOD_IMPL_ATTRIBUTE_OPTIL) + ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d has RVA != 0 but is CodeTypeMask is neither Native, CIL or Runtime", i)); + } else { + if (!(flags & (METHOD_ATTRIBUTE_ABSTRACT | METHOD_ATTRIBUTE_PINVOKE_IMPL)) && !(implflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) && code_type != METHOD_IMPL_ATTRIBUTE_RUNTIME) + ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d has RVA = 0 but neither Abstract, InternalCall, Runtime or PinvokeImpl", i)); + } + + if ((flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) { + if (rva) + ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is PinvokeImpl but has RVA != 0", i)); + if (search_sorted_table (ctx, MONO_TABLE_IMPLMAP, MONO_IMPLMAP_MEMBER, make_coded_token (MEMBER_FORWARDED_DESC, MONO_TABLE_METHOD, i)) == -1) + ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is PinvokeImpl but has no row in the ImplMap table", i)); + } + if (flags & METHOD_ATTRIBUTE_RT_SPECIAL_NAME && !is_ctor && !is_cctor) + ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is RtSpecialName but not named .ctor or .cctor", i)); + + if ((is_ctor || is_cctor) && !(flags & METHOD_ATTRIBUTE_RT_SPECIAL_NAME)) + ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is named .ctor or .cctor but is not RtSpecialName", i)); + + if (data [MONO_METHOD_SIGNATURE] && !is_valid_blob_object (ctx, data [MONO_METHOD_SIGNATURE], 1)) + ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d invalid signature blob token 0x%x", i, data [MONO_METHOD_SIGNATURE])); + + if (data [MONO_METHOD_PARAMLIST] == 0) + ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d ParamList be be >= 1", i)); + + if (data [MONO_METHOD_PARAMLIST] < paramlist) + ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d ParamList rowid 0x%08x can't be smaller than of previous row 0x%08x", i, data [MONO_METHOD_PARAMLIST], paramlist)); + + if (data [MONO_METHOD_PARAMLIST] > ctx->image->tables [MONO_TABLE_PARAM].rows + 1) + ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d ParamList rowid 0x%08x is out of range", i, data [MONO_METHOD_PARAMLIST])); + + paramlist = data [MONO_METHOD_PARAMLIST]; + + } +} + +static void +verify_method_table_full (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_METHOD]; + guint32 data [MONO_METHOD_SIZE], rva, locals_token; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_METHOD_SIZE); + rva = data [MONO_METHOD_RVA]; + + if (!data [MONO_METHOD_SIGNATURE] || !is_valid_method_signature (ctx, data [MONO_METHOD_SIGNATURE])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d invalid signature token 0x%08x", i, data [MONO_METHOD_SIGNATURE])); + + if (rva && !is_valid_method_header (ctx, rva, &locals_token)) + ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d RVA points to an invalid method header", i)); + } +} + +static guint32 +get_next_param_count (VerifyContext *ctx, guint32 *current_method) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_METHOD]; + guint32 row = *current_method; + guint32 paramlist, tmp; + + + paramlist = mono_metadata_decode_row_col (table, row++, MONO_METHOD_PARAMLIST); + while (row < table->rows) { + tmp = mono_metadata_decode_row_col (table, row, MONO_METHOD_PARAMLIST); + if (tmp > paramlist) { + *current_method = row; + return tmp - paramlist; + } + ++row; + } + + /*no more methods, all params apply to the last one*/ + *current_method = table->rows; + return (guint32)-1; +} + + +#define INVALID_PARAM_FLAGS_BITS ((1 << 2) | (1 << 3) | (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8) | (1 << 9) | (1 << 10) | (1 << 11) | (1 << 14) | (1 << 15)) +static void +verify_param_table (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_PARAM]; + guint32 data [MONO_PARAM_SIZE], flags, sequence = 0, remaining_params, current_method = 0; + gboolean first_param = TRUE; + int i; + + if (ctx->image->tables [MONO_TABLE_METHOD].rows == 0) { + if (table->rows > 0) + ADD_ERROR (ctx, g_strdup ("Param table has rows while the method table has zero")); + return; + } + + remaining_params = get_next_param_count (ctx, ¤t_method); + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_PARAM_SIZE); + flags = data [MONO_PARAM_FLAGS]; + + if (flags & INVALID_PARAM_FLAGS_BITS) + ADD_ERROR (ctx, g_strdup_printf ("Invalid param row %d bad Flags value 0x%08x", i, flags)); + + if (search_sorted_table (ctx, MONO_TABLE_CONSTANT, MONO_CONSTANT_PARENT, make_coded_token (HAS_CONSTANT_DESC, MONO_TABLE_PARAM, i)) == -1) { + if (flags & PARAM_ATTRIBUTE_HAS_DEFAULT) + ADD_ERROR (ctx, g_strdup_printf ("Invalid param row %d HasDefault = 1 but no owned row in Contant table", i)); + } else { + if (!(flags & PARAM_ATTRIBUTE_HAS_DEFAULT)) + ADD_ERROR (ctx, g_strdup_printf ("Invalid param row %d HasDefault = 0 but has owned row in Contant table", i)); + } + + if ((flags & PARAM_ATTRIBUTE_HAS_FIELD_MARSHAL) && search_sorted_table (ctx, MONO_TABLE_FIELDMARSHAL, MONO_FIELD_MARSHAL_PARENT, make_coded_token (HAS_FIELD_MARSHAL_DESC, MONO_TABLE_PARAM, i)) == -1) + ADD_ERROR (ctx, g_strdup_printf ("Invalid param row %d HasFieldMarshal = 1 but no owned row in FieldMarshal table", i)); + + if (!is_valid_string (ctx, data [MONO_PARAM_NAME])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid param row %d Name = 1 bad token 0x%08x", i, data [MONO_PARAM_NAME])); + + if (!first_param && data [MONO_PARAM_SEQUENCE] <= sequence) + ADD_ERROR (ctx, g_strdup_printf ("Invalid param row %d sequece = %d previus param has %d", i, data [MONO_PARAM_SEQUENCE], sequence)); + + first_param = FALSE; + sequence = data [MONO_PARAM_SEQUENCE]; + if (--remaining_params == 0) { + remaining_params = get_next_param_count (ctx, ¤t_method); + first_param = TRUE; + } + } +} + +static void +verify_interfaceimpl_table (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_INTERFACEIMPL]; + guint32 data [MONO_INTERFACEIMPL_SIZE]; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_INTERFACEIMPL_SIZE); + if (data [MONO_INTERFACEIMPL_CLASS] && data [MONO_INTERFACEIMPL_CLASS] > ctx->image->tables [MONO_TABLE_TYPEDEF].rows) + ADD_ERROR (ctx, g_strdup_printf ("Invalid InterfaceImpl row %d Class field 0x%08x", i, data [MONO_INTERFACEIMPL_CLASS])); + + if (!is_valid_coded_index (ctx, TYPEDEF_OR_REF_DESC, data [MONO_INTERFACEIMPL_INTERFACE])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid InterfaceImpl row %d Inteface field coded index 0x%08x", i, data [MONO_INTERFACEIMPL_INTERFACE])); + + if (!get_coded_index_token (TYPEDEF_OR_REF_DESC, data [MONO_INTERFACEIMPL_INTERFACE])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid InterfaceImpl row %d Inteface field is null", i)); + } +} + +static void +verify_memberref_table (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_MEMBERREF]; + guint32 data [MONO_MEMBERREF_SIZE]; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_MEMBERREF_SIZE); + + if (!is_valid_coded_index (ctx, MEMBERREF_PARENT_DESC, data [MONO_MEMBERREF_CLASS])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid MemberRef row %d Class field coded index 0x%08x", i, data [MONO_MEMBERREF_CLASS])); + + if (!get_coded_index_token (MEMBERREF_PARENT_DESC, data [MONO_MEMBERREF_CLASS])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid MemberRef row %d Class field coded is null", i)); + + if (!is_valid_non_empty_string (ctx, data [MONO_MEMBERREF_NAME])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid MemberRef row %d Name field coded is invalid or empty 0x%08x", i, data [MONO_MEMBERREF_NAME])); + + if (data [MONO_MEMBERREF_SIGNATURE] && !is_valid_blob_object (ctx, data [MONO_MEMBERREF_SIGNATURE], 1)) + ADD_ERROR (ctx, g_strdup_printf ("Invalid MemberRef row %d invalid signature blob token 0x%x", i, data [MONO_MEMBERREF_SIGNATURE])); + } +} + + +static void +verify_memberref_table_full (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_MEMBERREF]; + guint32 data [MONO_MEMBERREF_SIZE]; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_MEMBERREF_SIZE); + + if (!is_valid_method_or_field_signature (ctx, data [MONO_MEMBERREF_SIGNATURE])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid MemberRef row %d Signature field 0x%08x", i, data [MONO_MEMBERREF_SIGNATURE])); + } +} + +static void +verify_constant_table (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_CONSTANT]; + guint32 data [MONO_CONSTANT_SIZE], type; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_CONSTANT_SIZE); + type = data [MONO_CONSTANT_TYPE]; + + if (!((type >= MONO_TYPE_BOOLEAN && type <= MONO_TYPE_STRING) || type == MONO_TYPE_CLASS)) + ADD_ERROR (ctx, g_strdup_printf ("Invalid Constant row %d Type field 0x%08x", i, type)); + + if (!is_valid_coded_index (ctx, HAS_CONSTANT_DESC, data [MONO_CONSTANT_PARENT])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid Constant row %d Parent field coded index 0x%08x", i, data [MONO_CONSTANT_PARENT])); + + if (!get_coded_index_token (HAS_CONSTANT_DESC, data [MONO_CONSTANT_PARENT])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid Constant row %d Parent field coded is null", i)); + + if (!is_valid_constant (ctx, type, data [MONO_CONSTANT_VALUE])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid Constant row %d Value field 0x%08x", i, data [MONO_CONSTANT_VALUE])); + } +} + +static void +verify_cattr_table (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_CUSTOMATTRIBUTE]; + guint32 data [MONO_CUSTOM_ATTR_SIZE]; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_CUSTOM_ATTR_SIZE); + + if (!is_valid_coded_index (ctx, HAS_CATTR_DESC, data [MONO_CUSTOM_ATTR_PARENT])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid CustomAttribute row %d Parent field 0x%08x", i, data [MONO_CUSTOM_ATTR_PARENT])); + + if (!is_valid_coded_index (ctx, CATTR_TYPE_DESC, data [MONO_CUSTOM_ATTR_TYPE]) || !get_coded_index_token (CATTR_TYPE_DESC, data [MONO_CUSTOM_ATTR_TYPE])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid CustomAttribute row %d Type field 0x%08x", i, data [MONO_CUSTOM_ATTR_TYPE])); + + if (data [MONO_CUSTOM_ATTR_VALUE] && !is_valid_blob_object (ctx, data [MONO_CUSTOM_ATTR_VALUE], 0)) + ADD_ERROR (ctx, g_strdup_printf ("Invalid CustomAttribute row %d invalid value blob 0x%x", i, data [MONO_CUSTOM_ATTR_VALUE])); + } +} + +static void +verify_cattr_table_full (VerifyContext *ctx) +{ + MonoError error; + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_CUSTOMATTRIBUTE]; + MonoMethod *ctor; + const char *ptr; + guint32 data [MONO_CUSTOM_ATTR_SIZE], mtoken, size; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_CUSTOM_ATTR_SIZE); + + if (!is_valid_cattr_blob (ctx, data [MONO_CUSTOM_ATTR_VALUE])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid CustomAttribute row %d Value field 0x%08x", i, data [MONO_CUSTOM_ATTR_VALUE])); + + mtoken = data [MONO_CUSTOM_ATTR_TYPE] >> MONO_CUSTOM_ATTR_TYPE_BITS; + switch (data [MONO_CUSTOM_ATTR_TYPE] & MONO_CUSTOM_ATTR_TYPE_MASK) { + case MONO_CUSTOM_ATTR_TYPE_METHODDEF: + mtoken |= MONO_TOKEN_METHOD_DEF; + break; + case MONO_CUSTOM_ATTR_TYPE_MEMBERREF: + mtoken |= MONO_TOKEN_MEMBER_REF; + break; + default: + ADD_ERROR (ctx, g_strdup_printf ("Invalid CustomAttribute constructor row %d Token 0x%08x", i, data [MONO_CUSTOM_ATTR_TYPE])); + } + + ctor = mono_get_method_checked (ctx->image, mtoken, NULL, NULL, &error); + + if (!ctor) { + ADD_ERROR (ctx, g_strdup_printf ("Invalid CustomAttribute content row %d Could not load ctor due to %s", i, mono_error_get_message (&error))); + mono_error_cleanup (&error); + } + + /*This can't fail since this is checked in is_valid_cattr_blob*/ + g_assert (decode_signature_header (ctx, data [MONO_CUSTOM_ATTR_VALUE], &size, &ptr)); + + if (!is_valid_cattr_content (ctx, ctor, ptr, size)) { + char *ctor_name = mono_method_full_name (ctor, TRUE); + ADD_ERROR (ctx, g_strdup_printf ("Invalid CustomAttribute content row %d Value field 0x%08x ctor: %s", i, data [MONO_CUSTOM_ATTR_VALUE], ctor_name)); + g_free (ctor_name); + } + } +} + +static void +verify_field_marshal_table (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_FIELDMARSHAL]; + guint32 data [MONO_FIELD_MARSHAL_SIZE]; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_FIELD_MARSHAL_SIZE); + + if (!is_valid_coded_index (ctx, HAS_FIELD_MARSHAL_DESC, data [MONO_FIELD_MARSHAL_PARENT])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid FieldMarshal row %d Parent field 0x%08x", i, data [MONO_FIELD_MARSHAL_PARENT])); + + if (!get_coded_index_token (HAS_FIELD_MARSHAL_DESC, data [MONO_FIELD_MARSHAL_PARENT])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid FieldMarshal row %d Parent field is null", i)); + + if (!data [MONO_FIELD_MARSHAL_NATIVE_TYPE]) + ADD_ERROR (ctx, g_strdup_printf ("Invalid FieldMarshal row %d NativeType field is null", i)); + + if (!is_valid_blob_object (ctx, data [MONO_FIELD_MARSHAL_NATIVE_TYPE], 1)) + ADD_ERROR (ctx, g_strdup_printf ("Invalid FieldMarshal row %d invalid NativeType blob 0x%x", i, data [MONO_FIELD_MARSHAL_NATIVE_TYPE])); + } +} + +static void +verify_field_marshal_table_full (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_FIELDMARSHAL]; + guint32 data [MONO_FIELD_MARSHAL_SIZE]; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_FIELD_MARSHAL_SIZE); + + if (!is_valid_marshal_spec (ctx, data [MONO_FIELD_MARSHAL_NATIVE_TYPE])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid FieldMarshal row %d NativeType field 0x%08x", i, data [MONO_FIELD_MARSHAL_NATIVE_TYPE])); + } +} + +static void +verify_decl_security_table (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_DECLSECURITY]; + guint32 data [MONO_DECL_SECURITY_SIZE]; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_DECL_SECURITY_SIZE); + + if (!is_valid_coded_index (ctx, HAS_DECL_SECURITY_DESC, data [MONO_DECL_SECURITY_PARENT])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid DeclSecurity row %d Parent field 0x%08x", i, data [MONO_DECL_SECURITY_PARENT])); + + if (!get_coded_index_token (HAS_DECL_SECURITY_DESC, data [MONO_DECL_SECURITY_PARENT])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid DeclSecurity row %d Parent field is null", i)); + + if (!data [MONO_DECL_SECURITY_PERMISSIONSET]) + ADD_ERROR (ctx, g_strdup_printf ("Invalid DeclSecurity row %d PermissionSet field is null", i)); + } +} + +static void +verify_decl_security_table_full (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_DECLSECURITY]; + guint32 data [MONO_DECL_SECURITY_SIZE]; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_DECL_SECURITY_SIZE); + + if (!is_valid_permission_set (ctx, data [MONO_DECL_SECURITY_PERMISSIONSET])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid DeclSecurity row %d PermissionSet field 0x%08x", i, data [MONO_DECL_SECURITY_PERMISSIONSET])); + } +} + +static void +verify_class_layout_table (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_CLASSLAYOUT]; + guint32 data [MONO_CLASS_LAYOUT_SIZE]; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_CLASS_LAYOUT_SIZE); + + if (!data [MONO_CLASS_LAYOUT_PARENT] || data[MONO_CLASS_LAYOUT_PARENT] > ctx->image->tables [MONO_TABLE_TYPEDEF].rows + 1) + ADD_ERROR (ctx, g_strdup_printf ("Invalid ClassLayout row %d Parent field 0x%08x", i, data [MONO_TABLE_TYPEDEF])); + + switch (data [MONO_CLASS_LAYOUT_PACKING_SIZE]) { + case 0: + case 1: + case 2: + case 4: + case 8: + case 16: + case 32: + case 64: + case 128: + break; + default: + ADD_ERROR (ctx, g_strdup_printf ("Invalid ClassLayout row %d Packing field %d", i, data [MONO_CLASS_LAYOUT_PACKING_SIZE])); + } + } +} + +static void +verify_field_layout_table (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_FIELDLAYOUT]; + guint32 data [MONO_FIELD_LAYOUT_SIZE]; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_FIELD_LAYOUT_SIZE); + + if (!data [MONO_FIELD_LAYOUT_FIELD] || data[MONO_FIELD_LAYOUT_FIELD] > ctx->image->tables [MONO_TABLE_FIELD].rows + 1) + ADD_ERROR (ctx, g_strdup_printf ("Invalid FieldLayout row %d Field field 0x%08x", i, data [MONO_FIELD_LAYOUT_FIELD])); + } +} + +static void +verify_standalonesig_table (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_STANDALONESIG]; + guint32 data [MONO_STAND_ALONE_SIGNATURE_SIZE]; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_STAND_ALONE_SIGNATURE_SIZE); + + if (data [MONO_STAND_ALONE_SIGNATURE] && !is_valid_blob_object (ctx, data [MONO_STAND_ALONE_SIGNATURE], 1)) + ADD_ERROR (ctx, g_strdup_printf ("Invalid StandAloneSig row %d invalid signature 0x%x", i, data [MONO_STAND_ALONE_SIGNATURE])); + } +} + +static void +verify_standalonesig_table_full (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_STANDALONESIG]; + guint32 data [MONO_STAND_ALONE_SIGNATURE_SIZE]; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_STAND_ALONE_SIGNATURE_SIZE); + + if (!is_valid_standalonesig_blob (ctx, data [MONO_STAND_ALONE_SIGNATURE])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid StandAloneSig row %d Signature field 0x%08x", i, data [MONO_STAND_ALONE_SIGNATURE])); + } +} + +static void +verify_eventmap_table (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_EVENTMAP]; + guint32 data [MONO_EVENT_MAP_SIZE], eventlist = 0; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_EVENT_MAP_SIZE); + + if (!data [MONO_EVENT_MAP_PARENT] || data [MONO_EVENT_MAP_PARENT] > ctx->image->tables [MONO_TABLE_TYPEDEF].rows + 1) + ADD_ERROR (ctx, g_strdup_printf ("Invalid EventMap row %d Parent field 0x%08x", i, data [MONO_EVENT_MAP_PARENT])); + + if (!data [MONO_EVENT_MAP_EVENTLIST] || data [MONO_EVENT_MAP_EVENTLIST] <= eventlist) + ADD_ERROR (ctx, g_strdup_printf ("Invalid EventMap row %d EventList field %d", i, data [MONO_EVENT_MAP_EVENTLIST])); + + eventlist = data [MONO_EVENT_MAP_EVENTLIST]; + } +} + +#define INVALID_EVENT_FLAGS_BITS ~((1 << 9) | (1 << 10)) +static void +verify_event_table (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_EVENT]; + guint32 data [MONO_EVENT_SIZE]; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_EVENT_SIZE); + + if (data [MONO_EVENT_FLAGS] & INVALID_EVENT_FLAGS_BITS) + ADD_ERROR (ctx, g_strdup_printf ("Invalid Event row %d EventFlags field %08x", i, data [MONO_EVENT_FLAGS])); + + if (!is_valid_non_empty_string (ctx, data [MONO_EVENT_NAME])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid Event row %d Name field %08x", i, data [MONO_EVENT_NAME])); + + if (!is_valid_coded_index (ctx, TYPEDEF_OR_REF_DESC, data [MONO_EVENT_TYPE])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid Event row %d EventType field %08x", i, data [MONO_EVENT_TYPE])); + } +} + +static void +verify_event_table_full (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_EVENT]; + MonoTableInfo *sema_table = &ctx->image->tables [MONO_TABLE_METHODSEMANTICS]; + guint32 data [MONO_EVENT_SIZE], sema_data [MONO_METHOD_SEMA_SIZE], token; + gboolean found_add, found_remove; + int i, idx; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_EVENT_SIZE); + + token = make_coded_token (HAS_SEMANTICS_DESC, MONO_TABLE_EVENT, i); + idx = search_sorted_table (ctx, MONO_TABLE_METHODSEMANTICS, MONO_METHOD_SEMA_ASSOCIATION, token); + if (idx == -1) + ADD_ERROR (ctx, g_strdup_printf ("Invalid Event row %d has no AddOn or RemoveOn associated methods", i)); + + //first we move to the first row for this event + while (idx > 0) { + if (mono_metadata_decode_row_col (sema_table, idx - 1, MONO_METHOD_SEMA_ASSOCIATION) != token) + break; + --idx; + } + //now move forward looking for AddOn and RemoveOn rows + found_add = found_remove = FALSE; + while (idx < sema_table->rows) { + mono_metadata_decode_row (sema_table, idx, sema_data, MONO_METHOD_SEMA_SIZE); + if (sema_data [MONO_METHOD_SEMA_ASSOCIATION] != token) + break; + if (sema_data [MONO_METHOD_SEMA_SEMANTICS] & METHOD_SEMANTIC_ADD_ON) + found_add = TRUE; + if (sema_data [MONO_METHOD_SEMA_SEMANTICS] & METHOD_SEMANTIC_REMOVE_ON) + found_remove = TRUE; + if (found_add && found_remove) + break; + ++idx; + } + + if (!found_add) + ADD_ERROR (ctx, g_strdup_printf ("Invalid Event row %d has no AddOn associated method", i)); + if (!found_remove) + ADD_ERROR (ctx, g_strdup_printf ("Invalid Event row %d has no RemoveOn associated method", i)); + } +} + +static void +verify_propertymap_table (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_PROPERTYMAP]; + guint32 data [MONO_PROPERTY_MAP_SIZE], propertylist = 0; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_PROPERTY_MAP_SIZE); + + if (!data [MONO_PROPERTY_MAP_PARENT] || data [MONO_PROPERTY_MAP_PARENT] > ctx->image->tables [MONO_TABLE_TYPEDEF].rows + 1) + ADD_ERROR (ctx, g_strdup_printf ("Invalid PropertyMap row %d Parent field 0x%08x", i, data [MONO_PROPERTY_MAP_PARENT])); + + if (!data [MONO_PROPERTY_MAP_PROPERTY_LIST] || data [MONO_PROPERTY_MAP_PROPERTY_LIST] <= propertylist) + ADD_ERROR (ctx, g_strdup_printf ("Invalid PropertyMap row %d PropertyList field %d", i, data [MONO_PROPERTY_MAP_PROPERTY_LIST])); + + propertylist = data [MONO_PROPERTY_MAP_PROPERTY_LIST]; + } +} + +#define INVALID_PROPERTY_FLAGS_BITS ~((1 << 9) | (1 << 10) | (1 << 12)) +static void +verify_property_table (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_PROPERTY]; + guint32 data [MONO_PROPERTY_SIZE]; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_PROPERTY_SIZE); + + if (data [MONO_PROPERTY_FLAGS] & INVALID_PROPERTY_FLAGS_BITS) + ADD_ERROR (ctx, g_strdup_printf ("Invalid Property row %d PropertyFlags field %08x", i, data [MONO_PROPERTY_FLAGS])); + + if (!is_valid_non_empty_string (ctx, data [MONO_PROPERTY_NAME])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid Property row %d Name field %08x", i, data [MONO_PROPERTY_NAME])); + + if (!is_valid_property_sig_blob (ctx, data [MONO_PROPERTY_TYPE])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid Property row %d Type field %08x", i, data [MONO_PROPERTY_TYPE])); + + if ((data [MONO_PROPERTY_FLAGS] & PROPERTY_ATTRIBUTE_HAS_DEFAULT) && + search_sorted_table (ctx, MONO_TABLE_CONSTANT, MONO_CONSTANT_PARENT, make_coded_token (HAS_CONSTANT_DESC, MONO_TABLE_PROPERTY, i)) == -1) + ADD_ERROR (ctx, g_strdup_printf ("Invalid Property row %d has HasDefault but there is no corresponding row in the Constant table", i)); + + } +} + +static void +verify_methodimpl_table (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_METHODIMPL]; + guint32 data [MONO_METHODIMPL_SIZE]; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_METHODIMPL_SIZE); + + if (!data [MONO_METHODIMPL_CLASS] || data [MONO_METHODIMPL_CLASS] > ctx->image->tables [MONO_TABLE_TYPEDEF].rows + 1) + ADD_ERROR (ctx, g_strdup_printf ("Invalid MethodImpl row %d Class field %08x", i, data [MONO_TABLE_TYPEDEF])); + + if (!get_coded_index_token (METHODDEF_OR_REF_DESC, data [MONO_METHODIMPL_BODY])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid MethodImpl row %d MethodBody field %08x", i, data [MONO_METHODIMPL_BODY])); + + if (!is_valid_coded_index (ctx, METHODDEF_OR_REF_DESC, data [MONO_METHODIMPL_BODY])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid MethodImpl row %d MethodBody field %08x", i, data [MONO_METHODIMPL_BODY])); + + if (!get_coded_index_token (METHODDEF_OR_REF_DESC, data [MONO_METHODIMPL_DECLARATION])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid MethodImpl row %d MethodDeclaration field %08x", i, data [MONO_METHODIMPL_DECLARATION])); + + if (!is_valid_coded_index (ctx, METHODDEF_OR_REF_DESC, data [MONO_METHODIMPL_DECLARATION])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid MethodImpl row %d MethodDeclaration field %08x", i, data [MONO_METHODIMPL_DECLARATION])); + } +} + +static void +verify_moduleref_table (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_MODULEREF]; + guint32 data [MONO_MODULEREF_SIZE]; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_MODULEREF_SIZE); + + if (!is_valid_non_empty_string (ctx, data[MONO_MODULEREF_NAME])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid ModuleRef row %d name field %08x", i, data [MONO_MODULEREF_NAME])); + } +} + +static void +verify_typespec_table (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_TYPESPEC]; + guint32 data [MONO_TYPESPEC_SIZE]; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_TYPESPEC_SIZE); + + if (data [MONO_TYPESPEC_SIGNATURE] && !is_valid_blob_object (ctx, data [MONO_TYPESPEC_SIGNATURE], 1)) + ADD_ERROR (ctx, g_strdup_printf ("Invalid TypeSpec row %d Signature field %08x", i, data [MONO_TYPESPEC_SIGNATURE])); + } +} + +static void +verify_typespec_table_full (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_TYPESPEC]; + guint32 data [MONO_TYPESPEC_SIZE]; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_TYPESPEC_SIZE); + ctx->token = (i + 1) | MONO_TOKEN_TYPE_SPEC; + if (!is_valid_typespec_blob (ctx, data [MONO_TYPESPEC_SIGNATURE])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid TypeSpec row %d Signature field %08x", i, data [MONO_TYPESPEC_SIGNATURE])); + } + ctx->token = 0; +} + +#define INVALID_IMPLMAP_FLAGS_BITS ~((1 << 0) | (1 << 1) | (1 << 2) | (1 << 4) | (1 << 5) | (1 << 6) | (1 << 8) | (1 << 9) | (1 << 10) | (1 << 12) | (1 << 13)) +static void +verify_implmap_table (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_IMPLMAP]; + guint32 data [MONO_IMPLMAP_SIZE], cconv; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_IMPLMAP_SIZE); + + if (data [MONO_IMPLMAP_FLAGS] & INVALID_IMPLMAP_FLAGS_BITS) + ADD_ERROR (ctx, g_strdup_printf ("Invalid ImplMap row %d Flags field %08x", i, data [MONO_IMPLMAP_FLAGS])); + + cconv = data [MONO_IMPLMAP_FLAGS] & PINVOKE_ATTRIBUTE_CALL_CONV_MASK; + if (cconv == 0 || cconv == 0x0600 || cconv == 0x0700) + ADD_ERROR (ctx, g_strdup_printf ("Invalid ImplMap row %d Invalid call conv field %x", i, cconv)); + + if (!is_valid_coded_index (ctx, MEMBER_FORWARDED_DESC, data [MONO_IMPLMAP_MEMBER])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid ImplMap row %d Invalid MemberForward token %x", i, data [MONO_IMPLMAP_MEMBER])); + + if (get_coded_index_table (MEMBER_FORWARDED_DESC, data [MONO_IMPLMAP_MEMBER]) != MONO_TABLE_METHOD) + ADD_ERROR (ctx, g_strdup_printf ("Invalid ImplMap row %d only methods are supported token %x", i, data [MONO_IMPLMAP_MEMBER])); + + if (!get_coded_index_token (MEMBER_FORWARDED_DESC, data [MONO_IMPLMAP_MEMBER])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid ImplMap row %d null token", i)); + + if (!is_valid_non_empty_string (ctx, data [MONO_IMPLMAP_NAME])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid ImplMap row %d ImportName Token %x", i, data [MONO_IMPLMAP_NAME])); + + if (!data [MONO_IMPLMAP_SCOPE] || data [MONO_IMPLMAP_SCOPE] > ctx->image->tables [MONO_TABLE_MODULEREF].rows) + ADD_ERROR (ctx, g_strdup_printf ("Invalid ImplMap row %d Invalid ImportScope token %x", i, data [MONO_IMPLMAP_SCOPE])); + } +} + +static void +verify_fieldrva_table (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_FIELDRVA]; + guint32 data [MONO_FIELD_RVA_SIZE]; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_FIELD_RVA_SIZE); + + if (!data [MONO_FIELD_RVA_RVA] || mono_cli_rva_image_map (ctx->image, data [MONO_FIELD_RVA_RVA]) == INVALID_ADDRESS) + ADD_ERROR (ctx, g_strdup_printf ("Invalid FieldRVA row %d RVA %08x", i, data [MONO_FIELD_RVA_RVA])); + + if (!data [MONO_FIELD_RVA_FIELD] || data [MONO_FIELD_RVA_FIELD] > ctx->image->tables [MONO_TABLE_FIELD].rows + 1) + ADD_ERROR (ctx, g_strdup_printf ("Invalid FieldRVA row %d Field %08x", i, data [MONO_FIELD_RVA_FIELD])); + } +} + +#define INVALID_ASSEMBLY_FLAGS_BITS ~((1 << 0) | (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8) | (1 << 14) | (1 << 15)) +static void +verify_assembly_table (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_ASSEMBLY]; + guint32 data [MONO_ASSEMBLY_SIZE], hash; + int i; + + if (table->rows > 1) + ADD_ERROR (ctx, g_strdup_printf ("Assembly table can have zero or one rows, but now %d", table->rows)); + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_ASSEMBLY_SIZE); + + hash = data [MONO_ASSEMBLY_HASH_ALG]; + if (!(hash == 0 || hash == 0x8003 || hash == 0x8004)) + ADD_ERROR (ctx, g_strdup_printf ("Assembly table row %d has invalid HashAlgId %x", i, hash)); + + if (data [MONO_ASSEMBLY_FLAGS] & INVALID_ASSEMBLY_FLAGS_BITS) + ADD_ERROR (ctx, g_strdup_printf ("Assembly table row %d has invalid Flags %08x", i, data [MONO_ASSEMBLY_FLAGS])); + + if (data [MONO_ASSEMBLY_PUBLIC_KEY] && !is_valid_blob_object (ctx, data [MONO_ASSEMBLY_PUBLIC_KEY], 1)) + ADD_ERROR (ctx, g_strdup_printf ("Assembly table row %d has invalid PublicKey %08x", i, data [MONO_ASSEMBLY_FLAGS])); + + if (!is_valid_non_empty_string (ctx, data [MONO_ASSEMBLY_NAME])) + ADD_ERROR (ctx, g_strdup_printf ("Assembly table row %d has invalid Name %08x", i, data [MONO_ASSEMBLY_NAME])); + + if (data [MONO_ASSEMBLY_CULTURE] && !is_valid_string (ctx, data [MONO_ASSEMBLY_CULTURE])) + ADD_ERROR (ctx, g_strdup_printf ("Assembly table row %d has invalid Culture %08x", i, data [MONO_ASSEMBLY_CULTURE])); + } +} + +#define INVALID_ASSEMBLYREF_FLAGS_BITS ~((1 << 0) | (1 << 8) | (1 << 14) | (1 << 15)) +static void +verify_assemblyref_table (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_ASSEMBLYREF]; + guint32 data [MONO_ASSEMBLYREF_SIZE]; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_ASSEMBLYREF_SIZE); + + if (data [MONO_ASSEMBLYREF_FLAGS] & INVALID_ASSEMBLYREF_FLAGS_BITS) + ADD_ERROR (ctx, g_strdup_printf ("AssemblyRef table row %d has invalid Flags %08x", i, data [MONO_ASSEMBLYREF_FLAGS])); + + if (data [MONO_ASSEMBLYREF_PUBLIC_KEY] && !is_valid_blob_object (ctx, data [MONO_ASSEMBLYREF_PUBLIC_KEY], 1)) + ADD_ERROR (ctx, g_strdup_printf ("AssemblyRef table row %d has invalid PublicKeyOrToken %08x", i, data [MONO_ASSEMBLYREF_PUBLIC_KEY])); + + if (!is_valid_non_empty_string (ctx, data [MONO_ASSEMBLYREF_NAME])) + ADD_ERROR (ctx, g_strdup_printf ("AssemblyRef table row %d has invalid Name %08x", i, data [MONO_ASSEMBLYREF_NAME])); + + if (data [MONO_ASSEMBLYREF_CULTURE] && !is_valid_string (ctx, data [MONO_ASSEMBLYREF_CULTURE])) + ADD_ERROR (ctx, g_strdup_printf ("AssemblyRef table row %d has invalid Culture %08x", i, data [MONO_ASSEMBLYREF_CULTURE])); + + if (data [MONO_ASSEMBLYREF_HASH_VALUE] && !is_valid_blob_object (ctx, data [MONO_ASSEMBLYREF_HASH_VALUE], 1)) + ADD_ERROR (ctx, g_strdup_printf ("AssemblyRef table row %d has invalid HashValue %08x", i, data [MONO_ASSEMBLYREF_HASH_VALUE])); + } +} + +#define INVALID_FILE_FLAGS_BITS ~(1) +static void +verify_file_table (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_FILE]; + guint32 data [MONO_FILE_SIZE]; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_FILE_SIZE); + + if (data [MONO_FILE_FLAGS] & INVALID_FILE_FLAGS_BITS) + ADD_ERROR (ctx, g_strdup_printf ("File table row %d has invalid Flags %08x", i, data [MONO_FILE_FLAGS])); + + if (!is_valid_non_empty_string (ctx, data [MONO_FILE_NAME])) + ADD_ERROR (ctx, g_strdup_printf ("File table row %d has invalid Name %08x", i, data [MONO_FILE_NAME])); + + if (!data [MONO_FILE_HASH_VALUE] || !is_valid_blob_object (ctx, data [MONO_FILE_HASH_VALUE], 1)) + ADD_ERROR (ctx, g_strdup_printf ("File table row %d has invalid HashValue %08x", i, data [MONO_FILE_HASH_VALUE])); + } +} + +#define INVALID_EXPORTED_TYPE_FLAGS_BITS (INVALID_TYPEDEF_FLAG_BITS & ~TYPE_ATTRIBUTE_FORWARDER) +static void +verify_exportedtype_table (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_EXPORTEDTYPE]; + guint32 data [MONO_EXP_TYPE_SIZE]; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_EXP_TYPE_SIZE); + + if (data [MONO_EXP_TYPE_FLAGS] & INVALID_EXPORTED_TYPE_FLAGS_BITS) + ADD_ERROR (ctx, g_strdup_printf ("ExportedType table row %d has invalid Flags %08x", i, data [MONO_EXP_TYPE_FLAGS])); + + if (!is_valid_non_empty_string (ctx, data [MONO_EXP_TYPE_NAME])) + ADD_ERROR (ctx, g_strdup_printf ("ExportedType table row %d has invalid TypeName %08x", i, data [MONO_FILE_NAME])); + + if (data [MONO_EXP_TYPE_NAMESPACE] && !is_valid_string (ctx, data [MONO_EXP_TYPE_NAMESPACE])) + ADD_ERROR (ctx, g_strdup_printf ("ExportedType table row %d has invalid TypeNamespace %08x", i, data [MONO_EXP_TYPE_NAMESPACE])); + + if (!is_valid_coded_index (ctx, IMPLEMENTATION_DESC, data [MONO_EXP_TYPE_IMPLEMENTATION])) + ADD_ERROR (ctx, g_strdup_printf ("ExportedType table row %d has invalid Implementation token %08x", i, data [MONO_EXP_TYPE_IMPLEMENTATION])); + + if (!get_coded_index_token (IMPLEMENTATION_DESC, data [MONO_EXP_TYPE_IMPLEMENTATION])) + ADD_ERROR (ctx, g_strdup_printf ("ExportedType table row %d has null Implementation token", i)); + + /*nested type can't have a namespace*/ + if (get_coded_index_table (IMPLEMENTATION_DESC, data [MONO_EXP_TYPE_IMPLEMENTATION]) == MONO_TABLE_EXPORTEDTYPE && data [MONO_EXP_TYPE_NAMESPACE]) + ADD_ERROR (ctx, g_strdup_printf ("ExportedType table row %d has denotes a nested type but has a non null TypeNamespace", i)); + } +} + +#define INVALID_MANIFEST_RESOURCE_FLAGS_BITS ~((1 << 0) | (1 << 1) | (1 << 2)) +static void +verify_manifest_resource_table (VerifyContext *ctx) +{ + MonoCLIImageInfo *iinfo = (MonoCLIImageInfo *)ctx->image->image_info; + MonoCLIHeader *ch = &iinfo->cli_cli_header; + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_MANIFESTRESOURCE]; + guint32 data [MONO_MANIFEST_SIZE], impl_table, token, resources_size; + int i; + + resources_size = ch->ch_resources.size; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_MANIFEST_SIZE); + + if (data [MONO_MANIFEST_FLAGS] & INVALID_MANIFEST_RESOURCE_FLAGS_BITS) + ADD_ERROR (ctx, g_strdup_printf ("ManifestResource table row %d has invalid Flags %08x", i, data [MONO_MANIFEST_FLAGS])); + + if (data [MONO_MANIFEST_FLAGS] != 1 && data [MONO_MANIFEST_FLAGS] != 2) + ADD_ERROR (ctx, g_strdup_printf ("ManifestResource table row %d has invalid Flags VisibilityMask %08x", i, data [MONO_MANIFEST_FLAGS])); + + if (!is_valid_non_empty_string (ctx, data [MONO_MANIFEST_NAME])) + ADD_ERROR (ctx, g_strdup_printf ("ManifestResource table row %d has invalid Name %08x", i, data [MONO_MANIFEST_NAME])); + + if (!is_valid_coded_index (ctx, IMPLEMENTATION_DESC, data [MONO_MANIFEST_IMPLEMENTATION])) + ADD_ERROR (ctx, g_strdup_printf ("ManifestResource table row %d has invalid Implementation token %08x", i, data [MONO_MANIFEST_IMPLEMENTATION])); + + impl_table = get_coded_index_table (IMPLEMENTATION_DESC, data [MONO_MANIFEST_IMPLEMENTATION]); + token = get_coded_index_token (IMPLEMENTATION_DESC, data [MONO_MANIFEST_IMPLEMENTATION]); + + if (impl_table == MONO_TABLE_EXPORTEDTYPE) + ADD_ERROR (ctx, g_strdup_printf ("ManifestResource table row %d has invalid Implementation token table %08x", i, get_coded_index_table (IMPLEMENTATION_DESC, data [MONO_MANIFEST_IMPLEMENTATION]))); + + if (impl_table == MONO_TABLE_FILE && token && data [MONO_MANIFEST_OFFSET]) + ADD_ERROR (ctx, g_strdup_printf ("ManifestResource table row %d points to a file but has non-zero offset", i)); + + if (!token && data [MONO_MANIFEST_OFFSET] >= resources_size) + ADD_ERROR (ctx, g_strdup_printf ("ManifestResource table row %d invalid Offset field %08x ", i, data [MONO_MANIFEST_OFFSET])); + } +} + +static void +verify_nested_class_table (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_NESTEDCLASS]; + guint32 data [MONO_NESTED_CLASS_SIZE]; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_NESTED_CLASS_SIZE); + + if (!data [MONO_NESTED_CLASS_NESTED] || data [MONO_NESTED_CLASS_NESTED] > ctx->image->tables [MONO_TABLE_TYPEDEF].rows) + ADD_ERROR (ctx, g_strdup_printf ("NestedClass table row %d has invalid NestedClass token %08x", i, data [MONO_NESTED_CLASS_NESTED])); + if (!data [MONO_NESTED_CLASS_ENCLOSING] || data [MONO_NESTED_CLASS_ENCLOSING] > ctx->image->tables [MONO_TABLE_TYPEDEF].rows) + ADD_ERROR (ctx, g_strdup_printf ("NestedClass table row %d has invalid EnclosingClass token %08x", i, data [MONO_NESTED_CLASS_ENCLOSING])); + if (data [MONO_NESTED_CLASS_ENCLOSING] == data [MONO_NESTED_CLASS_NESTED]) + ADD_ERROR (ctx, g_strdup_printf ("NestedClass table row %d has same token for NestedClass and EnclosingClass %08x", i, data [MONO_NESTED_CLASS_ENCLOSING])); + } +} + +#define INVALID_GENERIC_PARAM_FLAGS_BITS ~((1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4)) +static void +verify_generic_param_table (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_GENERICPARAM]; + guint32 data [MONO_GENERICPARAM_SIZE], token, last_token = 0; + int i, param_number = 0; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_GENERICPARAM_SIZE); + + if (data [MONO_GENERICPARAM_FLAGS] & INVALID_GENERIC_PARAM_FLAGS_BITS) + ADD_ERROR (ctx, g_strdup_printf ("GenericParam table row %d has invalid Flags token %08x", i, data [MONO_GENERICPARAM_FLAGS])); + + if ((data [MONO_GENERICPARAM_FLAGS] & MONO_GEN_PARAM_VARIANCE_MASK) == 0x3) + ADD_ERROR (ctx, g_strdup_printf ("GenericParam table row %d has invalid VarianceMask 0x3", i)); + + if (!is_valid_non_empty_string (ctx, data [MONO_GENERICPARAM_NAME])) + ADD_ERROR (ctx, g_strdup_printf ("GenericParam table row %d has invalid Name token %08x", i, data [MONO_GENERICPARAM_NAME])); + + token = data [MONO_GENERICPARAM_OWNER]; + + if (!is_valid_coded_index (ctx, TYPE_OR_METHODDEF_DESC, token)) + ADD_ERROR (ctx, g_strdup_printf ("GenericParam table row %d has invalid Owner token %08x", i, token)); + + if (!get_coded_index_token (TYPE_OR_METHODDEF_DESC, token)) + ADD_ERROR (ctx, g_strdup_printf ("GenericParam table row %d has null Owner token", i)); + + if (token != last_token) { + param_number = 0; + last_token = token; + } + + if (data [MONO_GENERICPARAM_NUMBER] != param_number) + ADD_ERROR (ctx, g_strdup_printf ("GenericParam table row %d Number is out of order %d expected %d", i, data [MONO_GENERICPARAM_NUMBER], param_number)); + + ++param_number; + } +} + +static void +verify_method_spec_table (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_METHODSPEC]; + guint32 data [MONO_METHODSPEC_SIZE]; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_METHODSPEC_SIZE); + + if (!is_valid_coded_index (ctx, METHODDEF_OR_REF_DESC, data [MONO_METHODSPEC_METHOD])) + ADD_ERROR (ctx, g_strdup_printf ("MethodSpec table row %d has invalid Method token %08x", i, data [MONO_METHODSPEC_METHOD])); + + if (!get_coded_index_token (METHODDEF_OR_REF_DESC, data [MONO_METHODSPEC_METHOD])) + ADD_ERROR (ctx, g_strdup_printf ("MethodSpec table row %d has null Method token", i)); + + if (data [MONO_METHODSPEC_SIGNATURE] && !is_valid_blob_object (ctx, data [MONO_METHODSPEC_SIGNATURE], 1)) + ADD_ERROR (ctx, g_strdup_printf ("MethodSpec table row %d has invalid signature token %08x", i, data [MONO_METHODSPEC_SIGNATURE])); + } +} + +static void +verify_method_spec_table_full (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_METHODSPEC]; + guint32 data [MONO_METHODSPEC_SIZE]; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_METHODSPEC_SIZE); + + if (!is_valid_methodspec_blob (ctx, data [MONO_METHODSPEC_SIGNATURE])) + ADD_ERROR (ctx, g_strdup_printf ("MethodSpec table row %d has invalid Instantiation token %08x", i, data [MONO_METHODSPEC_SIGNATURE])); + } +} + +static void +verify_generic_param_constraint_table (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_GENERICPARAMCONSTRAINT]; + guint32 data [MONO_GENPARCONSTRAINT_SIZE]; + int i; + guint32 last_owner = 0, last_constraint = 0; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_GENPARCONSTRAINT_SIZE); + + if (!data [MONO_GENPARCONSTRAINT_GENERICPAR] || data [MONO_GENPARCONSTRAINT_GENERICPAR] > ctx->image->tables [MONO_TABLE_GENERICPARAM].rows) + ADD_ERROR (ctx, g_strdup_printf ("GenericParamConstraint table row %d has invalid Owner token %08x", i, data [MONO_GENPARCONSTRAINT_GENERICPAR])); + + if (!is_valid_coded_index (ctx, TYPEDEF_OR_REF_DESC, data [MONO_GENPARCONSTRAINT_CONSTRAINT])) + ADD_ERROR (ctx, g_strdup_printf ("GenericParamConstraint table row %d has invalid Constraint token %08x", i, data [MONO_GENPARCONSTRAINT_CONSTRAINT])); + + if (!get_coded_index_token (TYPEDEF_OR_REF_DESC, data [MONO_GENPARCONSTRAINT_CONSTRAINT])) + ADD_ERROR (ctx, g_strdup_printf ("GenericParamConstraint table row %d has null Constraint token", i)); + + if (last_owner > data [MONO_GENPARCONSTRAINT_GENERICPAR]) + ADD_ERROR (ctx, g_strdup_printf ("GenericParamConstraint table row %d is not properly sorted. Previous value of the owner column is 0x%08x current value is 0x%08x", i, last_owner, data [MONO_GENPARCONSTRAINT_GENERICPAR])); + + if (last_owner == data [MONO_GENPARCONSTRAINT_GENERICPAR]) { + if (last_constraint == data [MONO_GENPARCONSTRAINT_CONSTRAINT]) + ADD_ERROR (ctx, g_strdup_printf ("GenericParamConstraint table row %d has duplicate constraint 0x%08x", i, last_constraint)); + } else { + last_owner = data [MONO_GENPARCONSTRAINT_GENERICPAR]; + } + last_constraint = data [MONO_GENPARCONSTRAINT_CONSTRAINT]; + } +} + + +typedef struct { + const char *name; + const char *name_space; + guint32 resolution_scope; +} TypeDefUniqueId; + +static guint +typedef_hash (gconstpointer _key) +{ + const TypeDefUniqueId *key = (const TypeDefUniqueId *)_key; + return g_str_hash (key->name) ^ g_str_hash (key->name_space) ^ key->resolution_scope; /*XXX better salt the int key*/ +} + +static gboolean +typedef_equals (gconstpointer _a, gconstpointer _b) +{ + const TypeDefUniqueId *a = (const TypeDefUniqueId *)_a; + const TypeDefUniqueId *b = (const TypeDefUniqueId *)_b; + return !strcmp (a->name, b->name) && !strcmp (a->name_space, b->name_space) && a->resolution_scope == b->resolution_scope; +} + +static void +verify_typedef_table_global_constraints (VerifyContext *ctx) +{ + int i; + guint32 data [MONO_TYPEDEF_SIZE]; + guint32 nested_data [MONO_NESTED_CLASS_SIZE]; + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_TYPEDEF]; + MonoTableInfo *nested_table = &ctx->image->tables [MONO_TABLE_NESTEDCLASS]; + GHashTable *unique_types = g_hash_table_new_full (&typedef_hash, &typedef_equals, g_free, NULL); + + for (i = 0; i < table->rows; ++i) { + guint visibility; + TypeDefUniqueId *type = g_new (TypeDefUniqueId, 1); + mono_metadata_decode_row (table, i, data, MONO_TYPEDEF_SIZE); + + type->name = mono_metadata_string_heap (ctx->image, data [MONO_TYPEDEF_NAME]); + type->name_space = mono_metadata_string_heap (ctx->image, data [MONO_TYPEDEF_NAMESPACE]); + type->resolution_scope = 0; + + visibility = data [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_VISIBILITY_MASK; + if (visibility >= TYPE_ATTRIBUTE_NESTED_PUBLIC && visibility <= TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM) { + int res = search_sorted_table (ctx, MONO_TABLE_NESTEDCLASS, MONO_NESTED_CLASS_NESTED, i + 1); + g_assert (res >= 0); + + mono_metadata_decode_row (nested_table, res, nested_data, MONO_NESTED_CLASS_SIZE); + type->resolution_scope = nested_data [MONO_NESTED_CLASS_ENCLOSING]; + } + + if (g_hash_table_lookup (unique_types, type)) { + ADD_ERROR_NO_RETURN (ctx, g_strdup_printf ("TypeDef table row %d has duplicate for tuple (%s,%s,%x)", i, type->name, type->name_space, type->resolution_scope)); + g_hash_table_destroy (unique_types); + g_free (type); + return; + } + g_hash_table_insert (unique_types, type, GUINT_TO_POINTER (1)); + } + + g_hash_table_destroy (unique_types); +} + +static void +verify_typeref_table_global_constraints (VerifyContext *ctx) +{ + int i; + guint32 data [MONO_TYPEREF_SIZE]; + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_TYPEREF]; + GHashTable *unique_types = g_hash_table_new_full (&typedef_hash, &typedef_equals, g_free, NULL); + + for (i = 0; i < table->rows; ++i) { + TypeDefUniqueId *type = g_new (TypeDefUniqueId, 1); + mono_metadata_decode_row (table, i, data, MONO_TYPEREF_SIZE); + + type->resolution_scope = data [MONO_TYPEREF_SCOPE]; + type->name = mono_metadata_string_heap (ctx->image, data [MONO_TYPEREF_NAME]); + type->name_space = mono_metadata_string_heap (ctx->image, data [MONO_TYPEREF_NAMESPACE]); + + if (g_hash_table_lookup (unique_types, type)) { + ADD_ERROR_NO_RETURN (ctx, g_strdup_printf ("TypeRef table row %d has duplicate for tuple (%s,%s,%x)", i, type->name, type->name_space, type->resolution_scope)); + g_hash_table_destroy (unique_types); + g_free (type); + return; + } + g_hash_table_insert (unique_types, type, GUINT_TO_POINTER (1)); + } + + g_hash_table_destroy (unique_types); +} + +typedef struct { + guint32 klass; + guint32 method_declaration; +} MethodImplUniqueId; + +static guint +methodimpl_hash (gconstpointer _key) +{ + const MethodImplUniqueId *key = (const MethodImplUniqueId *)_key; + return key->klass ^ key->method_declaration; +} + +static gboolean +methodimpl_equals (gconstpointer _a, gconstpointer _b) +{ + const MethodImplUniqueId *a = (const MethodImplUniqueId *)_a; + const MethodImplUniqueId *b = (const MethodImplUniqueId *)_b; + return a->klass == b->klass && a->method_declaration == b->method_declaration; +} + +static void +verify_methodimpl_table_global_constraints (VerifyContext *ctx) +{ + int i; + guint32 data [MONO_METHODIMPL_SIZE]; + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_METHODIMPL]; + GHashTable *unique_impls = g_hash_table_new_full (&methodimpl_hash, &methodimpl_equals, g_free, NULL); + + for (i = 0; i < table->rows; ++i) { + MethodImplUniqueId *impl = g_new (MethodImplUniqueId, 1); + mono_metadata_decode_row (table, i, data, MONO_METHODIMPL_SIZE); + + impl->klass = data [MONO_METHODIMPL_CLASS]; + impl->method_declaration = data [MONO_METHODIMPL_DECLARATION]; + + if (g_hash_table_lookup (unique_impls, impl)) { + ADD_ERROR_NO_RETURN (ctx, g_strdup_printf ("MethodImpl table row %d has duplicate for tuple (0x%x, 0x%x)", impl->klass, impl->method_declaration)); + g_hash_table_destroy (unique_impls); + g_free (impl); + return; + } + g_hash_table_insert (unique_impls, impl, GUINT_TO_POINTER (1)); + } + + g_hash_table_destroy (unique_impls); +} + + +static void +verify_tables_data_global_constraints (VerifyContext *ctx) +{ + verify_typedef_table_global_constraints (ctx); +} + +static void +verify_tables_data_global_constraints_full (VerifyContext *ctx) +{ + verify_typeref_table (ctx); + verify_typeref_table_global_constraints (ctx); + verify_methodimpl_table_global_constraints (ctx); +} + +static void +verify_tables_data (VerifyContext *ctx) +{ + OffsetAndSize tables_area = get_metadata_stream (ctx, &ctx->image->heap_tables); + guint32 size = 0, tables_offset; + int i; + + for (i = 0; i < 0x2D; ++i) { + MonoTableInfo *table = &ctx->image->tables [i]; + guint32 tmp_size; + tmp_size = size + (guint32)table->row_size * (guint32)table->rows; + if (tmp_size < size) { + size = 0; + break; + } + size = tmp_size; + } + + if (size == 0) + ADD_ERROR (ctx, g_strdup_printf ("table space is either empty or overflowed")); + + tables_offset = ctx->image->tables_base - ctx->data; + if (!bounds_check_offset (&tables_area, tables_offset, size)) + ADD_ERROR (ctx, g_strdup_printf ("Tables data require %d bytes but the only %d are available in the #~ stream", size, tables_area.size - (tables_offset - tables_area.offset))); + + verify_module_table (ctx); + CHECK_ERROR (); + /*Obfuscators love to place broken stuff in the typeref table + verify_typeref_table (ctx); + CHECK_ERROR ();*/ + verify_typedef_table (ctx); + CHECK_ERROR (); + verify_field_table (ctx); + CHECK_ERROR (); + verify_method_table (ctx); + CHECK_ERROR (); + verify_param_table (ctx); + CHECK_ERROR (); + verify_interfaceimpl_table (ctx); + CHECK_ERROR (); + verify_memberref_table (ctx); + CHECK_ERROR (); + verify_constant_table (ctx); + CHECK_ERROR (); + verify_cattr_table (ctx); + CHECK_ERROR (); + verify_field_marshal_table (ctx); + CHECK_ERROR (); + verify_decl_security_table (ctx); + CHECK_ERROR (); + verify_class_layout_table (ctx); + CHECK_ERROR (); + verify_field_layout_table (ctx); + CHECK_ERROR (); + verify_standalonesig_table (ctx); + CHECK_ERROR (); + verify_eventmap_table (ctx); + CHECK_ERROR (); + verify_event_table (ctx); + CHECK_ERROR (); + verify_propertymap_table (ctx); + CHECK_ERROR (); + verify_property_table (ctx); + CHECK_ERROR (); + verify_methodimpl_table (ctx); + CHECK_ERROR (); + verify_moduleref_table (ctx); + CHECK_ERROR (); + verify_typespec_table (ctx); + CHECK_ERROR (); + verify_implmap_table (ctx); + CHECK_ERROR (); + verify_fieldrva_table (ctx); + CHECK_ERROR (); + verify_assembly_table (ctx); + CHECK_ERROR (); + verify_assemblyref_table (ctx); + CHECK_ERROR (); + verify_file_table (ctx); + CHECK_ERROR (); + verify_exportedtype_table (ctx); + CHECK_ERROR (); + verify_manifest_resource_table (ctx); + CHECK_ERROR (); + verify_nested_class_table (ctx); + CHECK_ERROR (); + verify_generic_param_table (ctx); + CHECK_ERROR (); + verify_method_spec_table (ctx); + CHECK_ERROR (); + verify_generic_param_constraint_table (ctx); + CHECK_ERROR (); + verify_tables_data_global_constraints (ctx); +} + +static void +init_verify_context (VerifyContext *ctx, MonoImage *image, gboolean report_error) +{ + memset (ctx, 0, sizeof (VerifyContext)); + ctx->image = image; + ctx->report_error = report_error; + ctx->report_warning = FALSE; //export this setting in the API + ctx->valid = 1; + ctx->size = image->raw_data_len; + ctx->data = image->raw_data; +} + +static gboolean +cleanup_context (VerifyContext *ctx, GSList **error_list) +{ + g_free (ctx->sections); + if (error_list) + *error_list = ctx->errors; + else + mono_free_verify_list (ctx->errors); + return ctx->valid; +} + +static gboolean +cleanup_context_checked (VerifyContext *ctx, MonoError *error) +{ + g_free (ctx->sections); + if (ctx->errors) { + MonoVerifyInfo *info = (MonoVerifyInfo *)ctx->errors->data; + mono_error_set_bad_image (error, ctx->image, "%s", info->message); + mono_free_verify_list (ctx->errors); + } + return ctx->valid; +} + +gboolean +mono_verifier_verify_pe_data (MonoImage *image, GSList **error_list) +{ + VerifyContext ctx; + + if (!mono_verifier_is_enabled_for_image (image)) + return TRUE; + + init_verify_context (&ctx, image, error_list != NULL); + ctx.stage = STAGE_PE; + + verify_msdos_header (&ctx); + CHECK_STATE(); + verify_pe_header (&ctx); + CHECK_STATE(); + verify_pe_optional_header (&ctx); + CHECK_STATE(); + load_section_table (&ctx); + CHECK_STATE(); + load_data_directories (&ctx); + CHECK_STATE(); + verify_import_table (&ctx); + CHECK_STATE(); + /*No need to check the IAT directory entry, it's content is indirectly verified by verify_import_table*/ + verify_resources_table (&ctx); + +cleanup: + return cleanup_context (&ctx, error_list); +} + +gboolean +mono_verifier_verify_cli_data (MonoImage *image, GSList **error_list) +{ + VerifyContext ctx; + + if (!mono_verifier_is_enabled_for_image (image)) + return TRUE; + + init_verify_context (&ctx, image, error_list != NULL); + ctx.stage = STAGE_CLI; + + verify_cli_header (&ctx); + CHECK_STATE(); + verify_metadata_header (&ctx); + CHECK_STATE(); + verify_tables_schema (&ctx); + +cleanup: + return cleanup_context (&ctx, error_list); +} + + +/* + * Verifies basic table constraints such as global table invariants (sorting, field monotonicity, etc). + * Other verification checks are meant to be done lazily by the runtime. Those include: + * blob items (signatures, method headers, custom attributes, etc) + * type semantics related + * vtable related + * stuff that should not block other pieces from running such as bad types/methods/fields/etc. + * + * The whole idea is that if this succeed the runtime is free to play around safely but any complex + * operation still need more checking. + */ +gboolean +mono_verifier_verify_table_data (MonoImage *image, GSList **error_list) +{ + VerifyContext ctx; + + if (!mono_verifier_is_enabled_for_image (image)) + return TRUE; + + init_verify_context (&ctx, image, error_list != NULL); + ctx.stage = STAGE_TABLES; + + verify_tables_data (&ctx); + + return cleanup_context (&ctx, error_list); +} + + +/* + * Verifies all other constraints. + */ +gboolean +mono_verifier_verify_full_table_data (MonoImage *image, GSList **error_list) +{ + VerifyContext ctx; + + if (!mono_verifier_is_enabled_for_image (image)) + return TRUE; + + init_verify_context (&ctx, image, error_list != NULL); + ctx.stage = STAGE_TABLES; + + verify_typedef_table_full (&ctx); + CHECK_STATE (); + verify_field_table_full (&ctx); + CHECK_STATE (); + verify_method_table_full (&ctx); + CHECK_STATE (); + verify_memberref_table_full (&ctx); + CHECK_STATE (); + verify_cattr_table_full (&ctx); + CHECK_STATE (); + verify_field_marshal_table_full (&ctx); + CHECK_STATE (); + verify_decl_security_table_full (&ctx); + CHECK_STATE (); + verify_standalonesig_table_full (&ctx); + CHECK_STATE (); + verify_event_table_full (&ctx); + CHECK_STATE (); + verify_typespec_table_full (&ctx); + CHECK_STATE (); + verify_method_spec_table_full (&ctx); + CHECK_STATE (); + verify_tables_data_global_constraints_full (&ctx); + +cleanup: + return cleanup_context (&ctx, error_list); +} + +gboolean +mono_verifier_verify_field_signature (MonoImage *image, guint32 offset, GSList **error_list) +{ + VerifyContext ctx; + + if (!mono_verifier_is_enabled_for_image (image)) + return TRUE; + + init_verify_context (&ctx, image, error_list != NULL); + ctx.stage = STAGE_TABLES; + + is_valid_field_signature (&ctx, offset); + return cleanup_context (&ctx, error_list); +} + +gboolean +mono_verifier_verify_method_header (MonoImage *image, guint32 offset, GSList **error_list) +{ + VerifyContext ctx; + guint32 locals_token; + + if (!mono_verifier_is_enabled_for_image (image)) + return TRUE; + + init_verify_context (&ctx, image, error_list != NULL); + ctx.stage = STAGE_TABLES; + + is_valid_method_header (&ctx, offset, &locals_token); + if (locals_token) { + guint32 sig_offset = mono_metadata_decode_row_col (&image->tables [MONO_TABLE_STANDALONESIG], locals_token - 1, MONO_STAND_ALONE_SIGNATURE); + is_valid_standalonesig_blob (&ctx, sig_offset); + } + + return cleanup_context (&ctx, error_list); +} + +gboolean +mono_verifier_verify_method_signature (MonoImage *image, guint32 offset, MonoError *error) +{ + VerifyContext ctx; + + error_init (error); + + if (!mono_verifier_is_enabled_for_image (image)) + return TRUE; + + init_verify_context (&ctx, image, TRUE); + ctx.stage = STAGE_TABLES; + + is_valid_method_signature (&ctx, offset); + /*XXX This returns a bad image exception, it might be the case that the right exception is method load.*/ + return cleanup_context_checked (&ctx, error); +} + +gboolean +mono_verifier_verify_memberref_method_signature (MonoImage *image, guint32 offset, GSList **error_list) +{ + VerifyContext ctx; + + if (!mono_verifier_is_enabled_for_image (image)) + return TRUE; + + init_verify_context (&ctx, image, error_list != NULL); + ctx.stage = STAGE_TABLES; + + is_valid_memberref_method_signature (&ctx, offset); + return cleanup_context (&ctx, error_list); +} + +gboolean +mono_verifier_verify_memberref_field_signature (MonoImage *image, guint32 offset, GSList **error_list) +{ + VerifyContext ctx; + + if (!mono_verifier_is_enabled_for_image (image)) + return TRUE; + + init_verify_context (&ctx, image, error_list != NULL); + ctx.stage = STAGE_TABLES; + + is_valid_field_signature (&ctx, offset); + return cleanup_context (&ctx, error_list); +} + +gboolean +mono_verifier_verify_standalone_signature (MonoImage *image, guint32 offset, GSList **error_list) +{ + VerifyContext ctx; + + if (!mono_verifier_is_enabled_for_image (image)) + return TRUE; + + init_verify_context (&ctx, image, error_list != NULL); + ctx.stage = STAGE_TABLES; + + is_valid_standalonesig_blob (&ctx, offset); + return cleanup_context (&ctx, error_list); +} + +gboolean +mono_verifier_verify_typespec_signature (MonoImage *image, guint32 offset, guint32 token, GSList **error_list) +{ + VerifyContext ctx; + + if (!mono_verifier_is_enabled_for_image (image)) + return TRUE; + + init_verify_context (&ctx, image, error_list != NULL); + ctx.stage = STAGE_TABLES; + ctx.token = token; + + is_valid_typespec_blob (&ctx, offset); + return cleanup_context (&ctx, error_list); +} + +gboolean +mono_verifier_verify_methodspec_signature (MonoImage *image, guint32 offset, GSList **error_list) +{ + VerifyContext ctx; + + if (!mono_verifier_is_enabled_for_image (image)) + return TRUE; + + init_verify_context (&ctx, image, error_list != NULL); + ctx.stage = STAGE_TABLES; + + is_valid_methodspec_blob (&ctx, offset); + return cleanup_context (&ctx, error_list); +} + +static void +verify_user_string (VerifyContext *ctx, guint32 offset) +{ + OffsetAndSize heap_us = get_metadata_stream (ctx, &ctx->image->heap_us); + guint32 entry_size, bytes; + + if (heap_us.size < offset) + ADD_ERROR (ctx, g_strdup ("User string offset beyond heap_us size")); + + if (!decode_value (ctx->data + offset + heap_us.offset, heap_us.size - heap_us.offset, &entry_size, &bytes)) + ADD_ERROR (ctx, g_strdup ("Could not decode user string blob size")); + + if (CHECK_ADD4_OVERFLOW_UN (entry_size, bytes)) + ADD_ERROR (ctx, g_strdup ("User string size overflow")); + + entry_size += bytes; + + if (ADD_IS_GREATER_OR_OVF (offset, entry_size, heap_us.size)) + ADD_ERROR (ctx, g_strdup ("User string oveflow heap_us")); +} + +gboolean +mono_verifier_verify_string_signature (MonoImage *image, guint32 offset, GSList **error_list) +{ + VerifyContext ctx; + + if (!mono_verifier_is_enabled_for_image (image)) + return TRUE; + + init_verify_context (&ctx, image, error_list != NULL); + ctx.stage = STAGE_TABLES; + + verify_user_string (&ctx, offset); + + return cleanup_context (&ctx, error_list); +} + +gboolean +mono_verifier_verify_cattr_blob (MonoImage *image, guint32 offset, GSList **error_list) +{ + VerifyContext ctx; + + if (!mono_verifier_is_enabled_for_image (image)) + return TRUE; + + init_verify_context (&ctx, image, error_list != NULL); + ctx.stage = STAGE_TABLES; + + is_valid_cattr_blob (&ctx, offset); + + return cleanup_context (&ctx, error_list); +} + +gboolean +mono_verifier_verify_cattr_content (MonoImage *image, MonoMethod *ctor, const guchar *data, guint32 size, GSList **error_list) +{ + VerifyContext ctx; + + if (!mono_verifier_is_enabled_for_image (image)) + return TRUE; + + init_verify_context (&ctx, image, error_list != NULL); + ctx.stage = STAGE_TABLES; + + is_valid_cattr_content (&ctx, ctor, (const char*)data, size); + + return cleanup_context (&ctx, error_list); +} + +gboolean +mono_verifier_is_sig_compatible (MonoImage *image, MonoMethod *method, MonoMethodSignature *signature) +{ + MonoMethodSignature *original_sig; + if (!mono_verifier_is_enabled_for_image (image)) + return TRUE; + + original_sig = mono_method_signature (method); + if (original_sig->call_convention == MONO_CALL_VARARG) { + if (original_sig->hasthis != signature->hasthis) + return FALSE; + if (original_sig->call_convention != signature->call_convention) + return FALSE; + if (original_sig->explicit_this != signature->explicit_this) + return FALSE; + if (original_sig->pinvoke != signature->pinvoke) + return FALSE; + if (original_sig->sentinelpos != signature->sentinelpos) + return FALSE; + } else if (!mono_metadata_signature_equal (signature, original_sig)) { + return FALSE; + } + + return TRUE; +} + +gboolean +mono_verifier_verify_typeref_row (MonoImage *image, guint32 row, MonoError *error) +{ + MonoTableInfo *table = &image->tables [MONO_TABLE_TYPEREF]; + guint32 data [MONO_TYPEREF_SIZE]; + + error_init (error); + + if (!mono_verifier_is_enabled_for_image (image)) + return TRUE; + + if (row >= table->rows) { + mono_error_set_bad_image (error, image, "Invalid typeref row %d - table has %d rows", row, table->rows); + return FALSE; + } + + mono_metadata_decode_row (table, row, data, MONO_TYPEREF_SIZE); + if (!is_valid_coded_index_with_image (image, RES_SCOPE_DESC, data [MONO_TYPEREF_SCOPE])) { + mono_error_set_bad_image (error, image, "Invalid typeref row %d coded index 0x%08x", row, data [MONO_TYPEREF_SCOPE]); + return FALSE; + } + + if (!get_coded_index_token (RES_SCOPE_DESC, data [MONO_TYPEREF_SCOPE])) { + mono_error_set_bad_image (error, image, "The metadata verifier doesn't support null ResolutionScope tokens for typeref row %d", row); + return FALSE; + } + + if (!data [MONO_TYPEREF_NAME] || !is_valid_string_full_with_image (image, data [MONO_TYPEREF_NAME], FALSE)) { + mono_error_set_bad_image (error, image, "Invalid typeref row %d name token 0x%08x", row, data [MONO_TYPEREF_NAME]); + return FALSE; + } + + if (data [MONO_TYPEREF_NAMESPACE] && !is_valid_string_full_with_image (image, data [MONO_TYPEREF_NAMESPACE], FALSE)) { + mono_error_set_bad_image (error, image, "Invalid typeref row %d namespace token 0x%08x", row, data [MONO_TYPEREF_NAMESPACE]); + return FALSE; + } + + return TRUE; +} + +/*Perform additional verification including metadata ones*/ +gboolean +mono_verifier_verify_methodimpl_row (MonoImage *image, guint32 row, MonoError *error) +{ + MonoMethod *declaration, *body; + MonoMethodSignature *body_sig, *decl_sig; + MonoTableInfo *table = &image->tables [MONO_TABLE_METHODIMPL]; + guint32 data [MONO_METHODIMPL_SIZE]; + + error_init (error); + + if (!mono_verifier_is_enabled_for_image (image)) + return TRUE; + + if (row >= table->rows) { + mono_error_set_bad_image (error, image, "Invalid methodimpl row %d - table has %d rows", row, table->rows); + return FALSE; + } + + mono_metadata_decode_row (table, row, data, MONO_METHODIMPL_SIZE); + + body = method_from_method_def_or_ref (image, data [MONO_METHODIMPL_BODY], NULL, error); + if (!body) + return FALSE; + + declaration = method_from_method_def_or_ref (image, data [MONO_METHODIMPL_DECLARATION], NULL, error); + if (!declaration) + return FALSE; + + /* FIXME + mono_class_setup_supertypes (class); + if (!mono_class_has_parent (class, body->klass)) { + mono_error_set_bad_image (error, image, "Invalid methodimpl body doesn't belong to parent for row %x", row); + return FALSE; + }*/ + + if (!(body_sig = mono_method_signature_checked (body, error))) { + return FALSE; + } + + if (!(decl_sig = mono_method_signature_checked (declaration, error))) { + return FALSE; + } + + if (!mono_verifier_is_signature_compatible (decl_sig, body_sig)) { + mono_error_set_bad_image (error, image, "Invalid methodimpl body signature not compatible with declaration row %x", row); + return FALSE; + } + + return TRUE; +} + +#else +gboolean +mono_verifier_verify_table_data (MonoImage *image, GSList **error_list) +{ + return TRUE; +} + +gboolean +mono_verifier_verify_cli_data (MonoImage *image, GSList **error_list) +{ + return TRUE; +} + +gboolean +mono_verifier_verify_pe_data (MonoImage *image, GSList **error_list) +{ + return TRUE; +} + +gboolean +mono_verifier_verify_full_table_data (MonoImage *image, GSList **error_list) +{ + return TRUE; +} + +gboolean +mono_verifier_verify_field_signature (MonoImage *image, guint32 offset, GSList **error_list) +{ + return TRUE; +} + +gboolean +mono_verifier_verify_method_header (MonoImage *image, guint32 offset, GSList **error_list) +{ + return TRUE; +} + +gboolean +mono_verifier_verify_method_signature (MonoImage *image, guint32 offset, MonoError *error) +{ + error_init (error); + return TRUE; +} + +gboolean +mono_verifier_verify_standalone_signature (MonoImage *image, guint32 offset, GSList **error_list) +{ + return TRUE; +} + +gboolean +mono_verifier_verify_typespec_signature (MonoImage *image, guint32 offset, guint32 token, GSList **error_list) +{ + return TRUE; +} + +gboolean +mono_verifier_verify_methodspec_signature (MonoImage *image, guint32 offset, GSList **error_list) +{ + return TRUE; +} + +gboolean +mono_verifier_verify_string_signature (MonoImage *image, guint32 offset, GSList **error_list) +{ + return TRUE; +} + +gboolean +mono_verifier_verify_cattr_blob (MonoImage *image, guint32 offset, GSList **error_list) +{ + return TRUE; +} + +gboolean +mono_verifier_verify_cattr_content (MonoImage *image, MonoMethod *ctor, const guchar *data, guint32 size, GSList **error_list) +{ + return TRUE; +} + +gboolean +mono_verifier_is_sig_compatible (MonoImage *image, MonoMethod *method, MonoMethodSignature *signature) +{ + return TRUE; +} + + +gboolean +mono_verifier_verify_typeref_row (MonoImage *image, guint32 row, MonoError *error) +{ + error_init (error); + return TRUE; +} + +gboolean +mono_verifier_verify_methodimpl_row (MonoImage *image, guint32 row, MonoError *error) +{ + error_init (error); + return TRUE; +} + +gboolean +mono_verifier_verify_memberref_method_signature (MonoImage *image, guint32 offset, GSList **error_list) +{ + return TRUE; +} + +gboolean +mono_verifier_verify_memberref_field_signature (MonoImage *image, guint32 offset, GSList **error_list) +{ + return TRUE; +} + +#endif /* DISABLE_VERIFIER */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/metadata.c b/unity-2019.4.24f1-mbe/mono/metadata/metadata.c new file mode 100644 index 000000000..f51a07473 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/metadata.c @@ -0,0 +1,7113 @@ +/** + * \file + * Routines for accessing the metadata + * + * Authors: + * Miguel de Icaza (miguel@ximian.com) + * Paolo Molaro (lupus@ximian.com) + * + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#include +#include +#include +#include +#include "metadata.h" +#include "tabledefs.h" +#include "mono-endian.h" +#include "cil-coff.h" +#include "tokentype.h" +#include "class-internals.h" +#include "metadata-internals.h" +#include "verify-internals.h" +#include "class.h" +#include "marshal.h" +#include "debug-helpers.h" +#include "abi-details.h" +#include +#include +#include +#include +#include + +static gint32 img_set_cache_hit, img_set_cache_miss, img_set_count; + + +/* Auxiliary structure used for caching inflated signatures */ +typedef struct { + MonoMethodSignature *sig; + MonoGenericContext context; +} MonoInflatedMethodSignature; + +static gboolean do_mono_metadata_parse_type (MonoType *type, MonoImage *m, MonoGenericContainer *container, gboolean transient, + const char *ptr, const char **rptr, MonoError *error); + +static gboolean do_mono_metadata_type_equal (MonoType *t1, MonoType *t2, gboolean signature_only); +static gboolean mono_metadata_class_equal (MonoClass *c1, MonoClass *c2, gboolean signature_only); +static gboolean mono_metadata_fnptr_equal (MonoMethodSignature *s1, MonoMethodSignature *s2, gboolean signature_only); +static gboolean _mono_metadata_generic_class_equal (const MonoGenericClass *g1, const MonoGenericClass *g2, + gboolean signature_only); +static void free_generic_inst (MonoGenericInst *ginst); +static void free_generic_class (MonoGenericClass *ginst); +static void free_inflated_method (MonoMethodInflated *method); +static void free_inflated_signature (MonoInflatedMethodSignature *sig); +static void mono_metadata_field_info_full (MonoImage *meta, guint32 index, guint32 *offset, guint32 *rva, MonoMarshalSpec **marshal_spec, gboolean alloc_from_image); + +/* + * This enumeration is used to describe the data types in the metadata + * tables + */ +enum { + MONO_MT_END, + + /* Sized elements */ + MONO_MT_UINT32, + MONO_MT_UINT16, + MONO_MT_UINT8, + + /* Index into Blob heap */ + MONO_MT_BLOB_IDX, + + /* Index into String heap */ + MONO_MT_STRING_IDX, + + /* GUID index */ + MONO_MT_GUID_IDX, + + /* Pointer into a table */ + MONO_MT_TABLE_IDX, + + /* HasConstant:Parent pointer (Param, Field or Property) */ + MONO_MT_CONST_IDX, + + /* HasCustomAttribute index. Indexes any table except CustomAttribute */ + MONO_MT_HASCAT_IDX, + + /* CustomAttributeType encoded index */ + MONO_MT_CAT_IDX, + + /* HasDeclSecurity index: TypeDef Method or Assembly */ + MONO_MT_HASDEC_IDX, + + /* Implementation coded index: File, Export AssemblyRef */ + MONO_MT_IMPL_IDX, + + /* HasFieldMarshal coded index: Field or Param table */ + MONO_MT_HFM_IDX, + + /* MemberForwardedIndex: Field or Method */ + MONO_MT_MF_IDX, + + /* TypeDefOrRef coded index: typedef, typeref, typespec */ + MONO_MT_TDOR_IDX, + + /* MemberRefParent coded index: typeref, moduleref, method, memberref, typesepc, typedef */ + MONO_MT_MRP_IDX, + + /* MethodDefOrRef coded index: Method or Member Ref table */ + MONO_MT_MDOR_IDX, + + /* HasSemantic coded index: Event or Property */ + MONO_MT_HS_IDX, + + /* ResolutionScope coded index: Module, ModuleRef, AssemblytRef, TypeRef */ + MONO_MT_RS_IDX, + + /* CustomDebugInformation parent encoded index */ + MONO_MT_HASCUSTDEBUG_IDX +}; + +const static unsigned char TableSchemas [] = { +#define ASSEMBLY_SCHEMA_OFFSET 0 + MONO_MT_UINT32, /* "HashId" }, */ + MONO_MT_UINT16, /* "Major" }, */ + MONO_MT_UINT16, /* "Minor" }, */ + MONO_MT_UINT16, /* "BuildNumber" }, */ + MONO_MT_UINT16, /* "RevisionNumber" }, */ + MONO_MT_UINT32, /* "Flags" }, */ + MONO_MT_BLOB_IDX, /* "PublicKey" }, */ + MONO_MT_STRING_IDX, /* "Name" }, */ + MONO_MT_STRING_IDX, /* "Culture" }, */ + MONO_MT_END, + +#define ASSEMBLYOS_SCHEMA_OFFSET ASSEMBLY_SCHEMA_OFFSET + 10 + MONO_MT_UINT32, /* "OSPlatformID" }, */ + MONO_MT_UINT32, /* "OSMajor" }, */ + MONO_MT_UINT32, /* "OSMinor" }, */ + MONO_MT_END, + +#define ASSEMBLYPROC_SCHEMA_OFFSET ASSEMBLYOS_SCHEMA_OFFSET + 4 + MONO_MT_UINT32, /* "Processor" }, */ + MONO_MT_END, + +#define ASSEMBLYREF_SCHEMA_OFFSET ASSEMBLYPROC_SCHEMA_OFFSET + 2 + MONO_MT_UINT16, /* "Major" }, */ + MONO_MT_UINT16, /* "Minor" }, */ + MONO_MT_UINT16, /* "Build" }, */ + MONO_MT_UINT16, /* "Revision" }, */ + MONO_MT_UINT32, /* "Flags" }, */ + MONO_MT_BLOB_IDX, /* "PublicKeyOrToken" }, */ + MONO_MT_STRING_IDX, /* "Name" }, */ + MONO_MT_STRING_IDX, /* "Culture" }, */ + MONO_MT_BLOB_IDX, /* "HashValue" }, */ + MONO_MT_END, + +#define ASSEMBLYREFOS_SCHEMA_OFFSET ASSEMBLYREF_SCHEMA_OFFSET + 10 + MONO_MT_UINT32, /* "OSPlatformID" }, */ + MONO_MT_UINT32, /* "OSMajorVersion" }, */ + MONO_MT_UINT32, /* "OSMinorVersion" }, */ + MONO_MT_TABLE_IDX, /* "AssemblyRef:AssemblyRef" }, */ + MONO_MT_END, + +#define ASSEMBLYREFPROC_SCHEMA_OFFSET ASSEMBLYREFOS_SCHEMA_OFFSET + 5 + MONO_MT_UINT32, /* "Processor" }, */ + MONO_MT_TABLE_IDX, /* "AssemblyRef:AssemblyRef" }, */ + MONO_MT_END, + +#define CLASS_LAYOUT_SCHEMA_OFFSET ASSEMBLYREFPROC_SCHEMA_OFFSET + 3 + MONO_MT_UINT16, /* "PackingSize" }, */ + MONO_MT_UINT32, /* "ClassSize" }, */ + MONO_MT_TABLE_IDX, /* "Parent:TypeDef" }, */ + MONO_MT_END, + +#define CONSTANT_SCHEMA_OFFSET CLASS_LAYOUT_SCHEMA_OFFSET + 4 + MONO_MT_UINT8, /* "Type" }, */ + MONO_MT_UINT8, /* "PaddingZero" }, */ + MONO_MT_CONST_IDX, /* "Parent" }, */ + MONO_MT_BLOB_IDX, /* "Value" }, */ + MONO_MT_END, + +#define CUSTOM_ATTR_SCHEMA_OFFSET CONSTANT_SCHEMA_OFFSET + 5 + MONO_MT_HASCAT_IDX, /* "Parent" }, */ + MONO_MT_CAT_IDX, /* "Type" }, */ + MONO_MT_BLOB_IDX, /* "Value" }, */ + MONO_MT_END, + +#define DECL_SEC_SCHEMA_OFFSET CUSTOM_ATTR_SCHEMA_OFFSET + 4 + MONO_MT_UINT16, /* "Action" }, */ + MONO_MT_HASDEC_IDX, /* "Parent" }, */ + MONO_MT_BLOB_IDX, /* "PermissionSet" }, */ + MONO_MT_END, + +#define EVENTMAP_SCHEMA_OFFSET DECL_SEC_SCHEMA_OFFSET + 4 + MONO_MT_TABLE_IDX, /* "Parent:TypeDef" }, */ + MONO_MT_TABLE_IDX, /* "EventList:Event" }, */ + MONO_MT_END, + +#define EVENT_SCHEMA_OFFSET EVENTMAP_SCHEMA_OFFSET + 3 + MONO_MT_UINT16, /* "EventFlags#EventAttribute" }, */ + MONO_MT_STRING_IDX, /* "Name" }, */ + MONO_MT_TDOR_IDX, /* "EventType" }, TypeDef or TypeRef or TypeSpec */ + MONO_MT_END, + +#define EVENT_POINTER_SCHEMA_OFFSET EVENT_SCHEMA_OFFSET + 4 + MONO_MT_TABLE_IDX, /* "Event" }, */ + MONO_MT_END, + +#define EXPORTED_TYPE_SCHEMA_OFFSET EVENT_POINTER_SCHEMA_OFFSET + 2 + MONO_MT_UINT32, /* "Flags" }, */ + MONO_MT_TABLE_IDX, /* "TypeDefId" }, */ + MONO_MT_STRING_IDX, /* "TypeName" }, */ + MONO_MT_STRING_IDX, /* "TypeNameSpace" }, */ + MONO_MT_IMPL_IDX, /* "Implementation" }, */ + MONO_MT_END, + +#define FIELD_SCHEMA_OFFSET EXPORTED_TYPE_SCHEMA_OFFSET + 6 + MONO_MT_UINT16, /* "Flags" }, */ + MONO_MT_STRING_IDX, /* "Name" }, */ + MONO_MT_BLOB_IDX, /* "Signature" }, */ + MONO_MT_END, + +#define FIELD_LAYOUT_SCHEMA_OFFSET FIELD_SCHEMA_OFFSET + 4 + MONO_MT_UINT32, /* "Offset" }, */ + MONO_MT_TABLE_IDX, /* "Field:Field" }, */ + MONO_MT_END, + +#define FIELD_MARSHAL_SCHEMA_OFFSET FIELD_LAYOUT_SCHEMA_OFFSET + 3 + MONO_MT_HFM_IDX, /* "Parent" }, */ + MONO_MT_BLOB_IDX, /* "NativeType" }, */ + MONO_MT_END, + +#define FIELD_RVA_SCHEMA_OFFSET FIELD_MARSHAL_SCHEMA_OFFSET + 3 + MONO_MT_UINT32, /* "RVA" }, */ + MONO_MT_TABLE_IDX, /* "Field:Field" }, */ + MONO_MT_END, + +#define FIELD_POINTER_SCHEMA_OFFSET FIELD_RVA_SCHEMA_OFFSET + 3 + MONO_MT_TABLE_IDX, /* "Field" }, */ + MONO_MT_END, + +#define FILE_SCHEMA_OFFSET FIELD_POINTER_SCHEMA_OFFSET + 2 + MONO_MT_UINT32, /* "Flags" }, */ + MONO_MT_STRING_IDX, /* "Name" }, */ + MONO_MT_BLOB_IDX, /* "Value" }, */ + MONO_MT_END, + +#define IMPLMAP_SCHEMA_OFFSET FILE_SCHEMA_OFFSET + 4 + MONO_MT_UINT16, /* "MappingFlag" }, */ + MONO_MT_MF_IDX, /* "MemberForwarded" }, */ + MONO_MT_STRING_IDX, /* "ImportName" }, */ + MONO_MT_TABLE_IDX, /* "ImportScope:ModuleRef" }, */ + MONO_MT_END, + +#define IFACEMAP_SCHEMA_OFFSET IMPLMAP_SCHEMA_OFFSET + 5 + MONO_MT_TABLE_IDX, /* "Class:TypeDef" }, */ + MONO_MT_TDOR_IDX, /* "Interface=TypeDefOrRef" }, */ + MONO_MT_END, + +#define MANIFEST_SCHEMA_OFFSET IFACEMAP_SCHEMA_OFFSET + 3 + MONO_MT_UINT32, /* "Offset" }, */ + MONO_MT_UINT32, /* "Flags" }, */ + MONO_MT_STRING_IDX, /* "Name" }, */ + MONO_MT_IMPL_IDX, /* "Implementation" }, */ + MONO_MT_END, + +#define MEMBERREF_SCHEMA_OFFSET MANIFEST_SCHEMA_OFFSET + 5 + MONO_MT_MRP_IDX, /* "Class" }, */ + MONO_MT_STRING_IDX, /* "Name" }, */ + MONO_MT_BLOB_IDX, /* "Signature" }, */ + MONO_MT_END, + +#define METHOD_SCHEMA_OFFSET MEMBERREF_SCHEMA_OFFSET + 4 + MONO_MT_UINT32, /* "RVA" }, */ + MONO_MT_UINT16, /* "ImplFlags#MethodImplAttributes" }, */ + MONO_MT_UINT16, /* "Flags#MethodAttribute" }, */ + MONO_MT_STRING_IDX, /* "Name" }, */ + MONO_MT_BLOB_IDX, /* "Signature" }, */ + MONO_MT_TABLE_IDX, /* "ParamList:Param" }, */ + MONO_MT_END, + +#define METHOD_IMPL_SCHEMA_OFFSET METHOD_SCHEMA_OFFSET + 7 + MONO_MT_TABLE_IDX, /* "Class:TypeDef" }, */ + MONO_MT_MDOR_IDX, /* "MethodBody" }, */ + MONO_MT_MDOR_IDX, /* "MethodDeclaration" }, */ + MONO_MT_END, + +#define METHOD_SEMA_SCHEMA_OFFSET METHOD_IMPL_SCHEMA_OFFSET + 4 + MONO_MT_UINT16, /* "MethodSemantic" }, */ + MONO_MT_TABLE_IDX, /* "Method:Method" }, */ + MONO_MT_HS_IDX, /* "Association" }, */ + MONO_MT_END, + +#define METHOD_POINTER_SCHEMA_OFFSET METHOD_SEMA_SCHEMA_OFFSET + 4 + MONO_MT_TABLE_IDX, /* "Method" }, */ + MONO_MT_END, + +#define MODULE_SCHEMA_OFFSET METHOD_POINTER_SCHEMA_OFFSET + 2 + MONO_MT_UINT16, /* "Generation" }, */ + MONO_MT_STRING_IDX, /* "Name" }, */ + MONO_MT_GUID_IDX, /* "MVID" }, */ + MONO_MT_GUID_IDX, /* "EncID" }, */ + MONO_MT_GUID_IDX, /* "EncBaseID" }, */ + MONO_MT_END, + +#define MODULEREF_SCHEMA_OFFSET MODULE_SCHEMA_OFFSET + 6 + MONO_MT_STRING_IDX, /* "Name" }, */ + MONO_MT_END, + +#define NESTED_CLASS_SCHEMA_OFFSET MODULEREF_SCHEMA_OFFSET + 2 + MONO_MT_TABLE_IDX, /* "NestedClass:TypeDef" }, */ + MONO_MT_TABLE_IDX, /* "EnclosingClass:TypeDef" }, */ + MONO_MT_END, + +#define PARAM_SCHEMA_OFFSET NESTED_CLASS_SCHEMA_OFFSET + 3 + MONO_MT_UINT16, /* "Flags" }, */ + MONO_MT_UINT16, /* "Sequence" }, */ + MONO_MT_STRING_IDX, /* "Name" }, */ + MONO_MT_END, + +#define PARAM_POINTER_SCHEMA_OFFSET PARAM_SCHEMA_OFFSET + 4 + MONO_MT_TABLE_IDX, /* "Param" }, */ + MONO_MT_END, + +#define PROPERTY_SCHEMA_OFFSET PARAM_POINTER_SCHEMA_OFFSET + 2 + MONO_MT_UINT16, /* "Flags" }, */ + MONO_MT_STRING_IDX, /* "Name" }, */ + MONO_MT_BLOB_IDX, /* "Type" }, */ + MONO_MT_END, + +#define PROPERTY_POINTER_SCHEMA_OFFSET PROPERTY_SCHEMA_OFFSET + 4 + MONO_MT_TABLE_IDX, /* "Property" }, */ + MONO_MT_END, + +#define PROPERTY_MAP_SCHEMA_OFFSET PROPERTY_POINTER_SCHEMA_OFFSET + 2 + MONO_MT_TABLE_IDX, /* "Parent:TypeDef" }, */ + MONO_MT_TABLE_IDX, /* "PropertyList:Property" }, */ + MONO_MT_END, + +#define STDALON_SIG_SCHEMA_OFFSET PROPERTY_MAP_SCHEMA_OFFSET + 3 + MONO_MT_BLOB_IDX, /* "Signature" }, */ + MONO_MT_END, + +#define TYPEDEF_SCHEMA_OFFSET STDALON_SIG_SCHEMA_OFFSET + 2 + MONO_MT_UINT32, /* "Flags" }, */ + MONO_MT_STRING_IDX, /* "Name" }, */ + MONO_MT_STRING_IDX, /* "Namespace" }, */ + MONO_MT_TDOR_IDX, /* "Extends" }, */ + MONO_MT_TABLE_IDX, /* "FieldList:Field" }, */ + MONO_MT_TABLE_IDX, /* "MethodList:Method" }, */ + MONO_MT_END, + +#define TYPEREF_SCHEMA_OFFSET TYPEDEF_SCHEMA_OFFSET + 7 + MONO_MT_RS_IDX, /* "ResolutionScope=ResolutionScope" }, */ + MONO_MT_STRING_IDX, /* "Name" }, */ + MONO_MT_STRING_IDX, /* "Namespace" }, */ + MONO_MT_END, + +#define TYPESPEC_SCHEMA_OFFSET TYPEREF_SCHEMA_OFFSET + 4 + MONO_MT_BLOB_IDX, /* "Signature" }, */ + MONO_MT_END, + +#define GENPARAM_SCHEMA_OFFSET TYPESPEC_SCHEMA_OFFSET + 2 + MONO_MT_UINT16, /* "Number" }, */ + MONO_MT_UINT16, /* "Flags" }, */ + MONO_MT_TABLE_IDX, /* "Owner" }, TypeDef or MethodDef */ + MONO_MT_STRING_IDX, /* "Name" }, */ + MONO_MT_END, + +#define METHOD_SPEC_SCHEMA_OFFSET GENPARAM_SCHEMA_OFFSET + 5 + MONO_MT_MDOR_IDX, /* "Method" }, */ + MONO_MT_BLOB_IDX, /* "Signature" }, */ + MONO_MT_END, + +#define GEN_CONSTRAINT_SCHEMA_OFFSET METHOD_SPEC_SCHEMA_OFFSET + 3 + MONO_MT_TABLE_IDX, /* "GenericParam" }, */ + MONO_MT_TDOR_IDX, /* "Constraint" }, */ + MONO_MT_END, + +#define DOCUMENT_SCHEMA_OFFSET GEN_CONSTRAINT_SCHEMA_OFFSET + 3 + MONO_MT_BLOB_IDX, /* Name */ + MONO_MT_GUID_IDX, /* HashAlgorithm */ + MONO_MT_BLOB_IDX, /* Hash */ + MONO_MT_GUID_IDX, /* Language */ + MONO_MT_END, + +#define METHODBODY_SCHEMA_OFFSET DOCUMENT_SCHEMA_OFFSET + 5 + MONO_MT_TABLE_IDX, /* Document */ + MONO_MT_BLOB_IDX, /* SequencePoints */ + MONO_MT_END, + +#define LOCALSCOPE_SCHEMA_OFFSET METHODBODY_SCHEMA_OFFSET + 3 + MONO_MT_TABLE_IDX, /* Method */ + MONO_MT_TABLE_IDX, /* ImportScope */ + MONO_MT_TABLE_IDX, /* VariableList */ + MONO_MT_TABLE_IDX, /* ConstantList */ + MONO_MT_UINT32, /* StartOffset */ + MONO_MT_UINT32, /* Length */ + MONO_MT_END, + +#define LOCALVARIABLE_SCHEMA_OFFSET LOCALSCOPE_SCHEMA_OFFSET + 7 + MONO_MT_UINT16, /* Attributes */ + MONO_MT_UINT16, /* Index */ + MONO_MT_STRING_IDX, /* Name */ + MONO_MT_END, + +#define LOCALCONSTANT_SCHEMA_OFFSET LOCALVARIABLE_SCHEMA_OFFSET + 4 + MONO_MT_STRING_IDX, /* Name (String heap index) */ + MONO_MT_BLOB_IDX, /* Signature (Blob heap index, LocalConstantSig blob) */ + MONO_MT_END, + +#define IMPORTSCOPE_SCHEMA_OFFSET LOCALCONSTANT_SCHEMA_OFFSET + 3 + MONO_MT_TABLE_IDX, /* Parent (ImportScope row id or nil) */ + MONO_MT_BLOB_IDX, /* Imports (Blob index, encoding: Imports blob) */ + MONO_MT_END, + +#define ASYNCMETHOD_SCHEMA_OFFSET IMPORTSCOPE_SCHEMA_OFFSET + 3 + MONO_MT_TABLE_IDX, /* MoveNextMethod (MethodDef row id) */ + MONO_MT_TABLE_IDX, /* KickoffMethod (MethodDef row id) */ + MONO_MT_END, + +#define CUSTOMDEBUGINFORMATION_SCHEMA_OFFSET ASYNCMETHOD_SCHEMA_OFFSET + 3 + MONO_MT_HASCUSTDEBUG_IDX, /* Parent (HasCustomDebugInformation coded index) */ + MONO_MT_GUID_IDX, /* Kind (Guid heap index) */ + MONO_MT_BLOB_IDX, /* Value (Blob heap index) */ + MONO_MT_END, + +#define NULL_SCHEMA_OFFSET CUSTOMDEBUGINFORMATION_SCHEMA_OFFSET + 4 + MONO_MT_END +}; + +/* Must be the same order as MONO_TABLE_* */ +const static unsigned char +table_description [] = { + MODULE_SCHEMA_OFFSET, + TYPEREF_SCHEMA_OFFSET, + TYPEDEF_SCHEMA_OFFSET, + FIELD_POINTER_SCHEMA_OFFSET, + FIELD_SCHEMA_OFFSET, + METHOD_POINTER_SCHEMA_OFFSET, + METHOD_SCHEMA_OFFSET, + PARAM_POINTER_SCHEMA_OFFSET, + PARAM_SCHEMA_OFFSET, + IFACEMAP_SCHEMA_OFFSET, + MEMBERREF_SCHEMA_OFFSET, /* 0xa */ + CONSTANT_SCHEMA_OFFSET, + CUSTOM_ATTR_SCHEMA_OFFSET, + FIELD_MARSHAL_SCHEMA_OFFSET, + DECL_SEC_SCHEMA_OFFSET, + CLASS_LAYOUT_SCHEMA_OFFSET, + FIELD_LAYOUT_SCHEMA_OFFSET, /* 0x10 */ + STDALON_SIG_SCHEMA_OFFSET, + EVENTMAP_SCHEMA_OFFSET, + EVENT_POINTER_SCHEMA_OFFSET, + EVENT_SCHEMA_OFFSET, + PROPERTY_MAP_SCHEMA_OFFSET, + PROPERTY_POINTER_SCHEMA_OFFSET, + PROPERTY_SCHEMA_OFFSET, + METHOD_SEMA_SCHEMA_OFFSET, + METHOD_IMPL_SCHEMA_OFFSET, + MODULEREF_SCHEMA_OFFSET, /* 0x1a */ + TYPESPEC_SCHEMA_OFFSET, + IMPLMAP_SCHEMA_OFFSET, + FIELD_RVA_SCHEMA_OFFSET, + NULL_SCHEMA_OFFSET, + NULL_SCHEMA_OFFSET, + ASSEMBLY_SCHEMA_OFFSET, /* 0x20 */ + ASSEMBLYPROC_SCHEMA_OFFSET, + ASSEMBLYOS_SCHEMA_OFFSET, + ASSEMBLYREF_SCHEMA_OFFSET, + ASSEMBLYREFPROC_SCHEMA_OFFSET, + ASSEMBLYREFOS_SCHEMA_OFFSET, + FILE_SCHEMA_OFFSET, + EXPORTED_TYPE_SCHEMA_OFFSET, + MANIFEST_SCHEMA_OFFSET, + NESTED_CLASS_SCHEMA_OFFSET, + GENPARAM_SCHEMA_OFFSET, /* 0x2a */ + METHOD_SPEC_SCHEMA_OFFSET, + GEN_CONSTRAINT_SCHEMA_OFFSET, + NULL_SCHEMA_OFFSET, + NULL_SCHEMA_OFFSET, + NULL_SCHEMA_OFFSET, + DOCUMENT_SCHEMA_OFFSET, /* 0x30 */ + METHODBODY_SCHEMA_OFFSET, + LOCALSCOPE_SCHEMA_OFFSET, + LOCALVARIABLE_SCHEMA_OFFSET, + LOCALCONSTANT_SCHEMA_OFFSET, + IMPORTSCOPE_SCHEMA_OFFSET, + ASYNCMETHOD_SCHEMA_OFFSET, + CUSTOMDEBUGINFORMATION_SCHEMA_OFFSET +}; + +#ifdef HAVE_ARRAY_ELEM_INIT +#define MSGSTRFIELD(line) MSGSTRFIELD1(line) +#define MSGSTRFIELD1(line) str##line +static const struct msgstr_t { +#define TABLEDEF(a,b) char MSGSTRFIELD(__LINE__) [sizeof (b)]; +#include "mono/cil/tables.def" +#undef TABLEDEF +} tablestr = { +#define TABLEDEF(a,b) b, +#include "mono/cil/tables.def" +#undef TABLEDEF +}; +static const gint16 tableidx [] = { +#define TABLEDEF(a,b) [a] = offsetof (struct msgstr_t, MSGSTRFIELD(__LINE__)), +#include "mono/cil/tables.def" +#undef TABLEDEF +}; + +#else +#define TABLEDEF(a,b) b, +static const char* const +mono_tables_names [] = { +#include "mono/cil/tables.def" + NULL +}; + +#endif + +/* If TRUE (but also see DISABLE_STICT_STRONG_NAMES #define), Mono will check + * that the public key token, culture and version of a candidate assembly matches + * the requested strong name. If FALSE, as long as the name matches, the candidate + * will be allowed. + */ +static gboolean check_strong_names_strictly = FALSE; + +// Amount initially reserved in each imageset's mempool. +// FIXME: This number is arbitrary, a more practical number should be found +#define INITIAL_IMAGE_SET_SIZE 1024 + +/** + * mono_meta_table_name: + * \param table table index + * + * Returns the name of the given ECMA metadata logical format table + * as described in ECMA 335, Partition II, Section 22. + * + * \returns the name for the \p table index + */ +const char * +mono_meta_table_name (int table) +{ + if ((table < 0) || (table > MONO_TABLE_LAST)) + return ""; + +#ifdef HAVE_ARRAY_ELEM_INIT + return (const char*)&tablestr + tableidx [table]; +#else + return mono_tables_names [table]; +#endif +} + +/* The guy who wrote the spec for this should not be allowed near a + * computer again. + +If e is a coded token(see clause 23.1.7) that points into table ti out of n possible tables t0, .. tn-1, +then it is stored as e << (log n) & tag{ t0, .. tn-1}[ ti] using 2 bytes if the maximum number of +rows of tables t0, ..tn-1, is less than 2^16 - (log n), and using 4 bytes otherwise. The family of +finite maps tag{ t0, ..tn-1} is defined below. Note that to decode a physical row, you need the +inverse of this mapping. + + */ +#define rtsize(meta,s,b) (((s) < (1 << (b)) ? 2 : 4)) + +static inline int +idx_size (MonoImage *meta, int tableidx) +{ + if (meta->referenced_tables && (meta->referenced_tables & ((guint64)1 << tableidx))) + return meta->referenced_table_rows [tableidx] < 65536 ? 2 : 4; + else + return meta->tables [tableidx].rows < 65536 ? 2 : 4; +} + +static inline int +get_nrows (MonoImage *meta, int tableidx) +{ + if (meta->referenced_tables && (meta->referenced_tables & ((guint64)1 << tableidx))) + return meta->referenced_table_rows [tableidx]; + else + return meta->tables [tableidx].rows; +} + +/* Reference: Partition II - 23.2.6 */ +/** + * mono_metadata_compute_size: + * \param meta metadata context + * \param tableindex metadata table number + * \param result_bitfield pointer to \c guint32 where to store additional info + * + * \c mono_metadata_compute_size computes the length in bytes of a single + * row in a metadata table. The size of each column is encoded in the + * \p result_bitfield return value along with the number of columns in the table. + * the resulting bitfield should be handed to the \c mono_metadata_table_size + * and \c mono_metadata_table_count macros. + * This is a Mono runtime internal only function. + */ +int +mono_metadata_compute_size (MonoImage *meta, int tableindex, guint32 *result_bitfield) +{ + guint32 bitfield = 0; + int size = 0, field_size = 0; + int i, n, code; + int shift = 0; + const unsigned char *description = TableSchemas + table_description [tableindex]; + + for (i = 0; (code = description [i]) != MONO_MT_END; i++){ + switch (code){ + case MONO_MT_UINT32: + field_size = 4; break; + + case MONO_MT_UINT16: + field_size = 2; break; + + case MONO_MT_UINT8: + field_size = 1; break; + + case MONO_MT_BLOB_IDX: + field_size = meta->idx_blob_wide ? 4 : 2; break; + + case MONO_MT_STRING_IDX: + field_size = meta->idx_string_wide ? 4 : 2; break; + + case MONO_MT_GUID_IDX: + field_size = meta->idx_guid_wide ? 4 : 2; break; + + case MONO_MT_TABLE_IDX: + /* Uhm, a table index can point to other tables besides the current one + * so, it's not correct to use the rowcount of the current table to + * get the size for this column - lupus + */ + switch (tableindex) { + case MONO_TABLE_ASSEMBLYREFOS: + g_assert (i == 3); + field_size = idx_size (meta, MONO_TABLE_ASSEMBLYREF); break; + case MONO_TABLE_ASSEMBLYREFPROCESSOR: + g_assert (i == 1); + field_size = idx_size (meta, MONO_TABLE_ASSEMBLYREF); break; + case MONO_TABLE_CLASSLAYOUT: + g_assert (i == 2); + field_size = idx_size (meta, MONO_TABLE_TYPEDEF); break; + case MONO_TABLE_EVENTMAP: + g_assert (i == 0 || i == 1); + field_size = i ? idx_size (meta, MONO_TABLE_EVENT): + idx_size (meta, MONO_TABLE_TYPEDEF); + break; + case MONO_TABLE_EVENT_POINTER: + g_assert (i == 0); + field_size = idx_size (meta, MONO_TABLE_EVENT); break; + case MONO_TABLE_EXPORTEDTYPE: + g_assert (i == 1); + /* the index is in another metadata file, so it must be 4 */ + field_size = 4; break; + case MONO_TABLE_FIELDLAYOUT: + g_assert (i == 1); + field_size = idx_size (meta, MONO_TABLE_FIELD); break; + case MONO_TABLE_FIELDRVA: + g_assert (i == 1); + field_size = idx_size (meta, MONO_TABLE_FIELD); break; + case MONO_TABLE_FIELD_POINTER: + g_assert (i == 0); + field_size = idx_size (meta, MONO_TABLE_FIELD); break; + case MONO_TABLE_IMPLMAP: + g_assert (i == 3); + field_size = idx_size (meta, MONO_TABLE_MODULEREF); break; + case MONO_TABLE_INTERFACEIMPL: + g_assert (i == 0); + field_size = idx_size (meta, MONO_TABLE_TYPEDEF); break; + case MONO_TABLE_METHOD: + g_assert (i == 5); + field_size = idx_size (meta, MONO_TABLE_PARAM); break; + case MONO_TABLE_METHODIMPL: + g_assert (i == 0); + field_size = idx_size (meta, MONO_TABLE_TYPEDEF); break; + case MONO_TABLE_METHODSEMANTICS: + g_assert (i == 1); + field_size = idx_size (meta, MONO_TABLE_METHOD); break; + case MONO_TABLE_METHOD_POINTER: + g_assert (i == 0); + field_size = idx_size (meta, MONO_TABLE_METHOD); break; + case MONO_TABLE_NESTEDCLASS: + g_assert (i == 0 || i == 1); + field_size = idx_size (meta, MONO_TABLE_TYPEDEF); break; + case MONO_TABLE_PARAM_POINTER: + g_assert (i == 0); + field_size = idx_size (meta, MONO_TABLE_PARAM); break; + case MONO_TABLE_PROPERTYMAP: + g_assert (i == 0 || i == 1); + field_size = i ? idx_size (meta, MONO_TABLE_PROPERTY): + idx_size (meta, MONO_TABLE_TYPEDEF); + break; + case MONO_TABLE_PROPERTY_POINTER: + g_assert (i == 0); + field_size = idx_size (meta, MONO_TABLE_PROPERTY); break; + case MONO_TABLE_TYPEDEF: + g_assert (i == 4 || i == 5); + field_size = i == 4 ? idx_size (meta, MONO_TABLE_FIELD): + idx_size (meta, MONO_TABLE_METHOD); + break; + case MONO_TABLE_GENERICPARAM: + g_assert (i == 2); + n = MAX (get_nrows (meta, MONO_TABLE_METHOD), get_nrows (meta, MONO_TABLE_TYPEDEF)); + /*This is a coded token for 2 tables, so takes 1 bit */ + field_size = rtsize (meta, n, 16 - MONO_TYPEORMETHOD_BITS); + break; + case MONO_TABLE_GENERICPARAMCONSTRAINT: + g_assert (i == 0); + field_size = idx_size (meta, MONO_TABLE_GENERICPARAM); + break; + case MONO_TABLE_LOCALSCOPE: + switch (i) { + case 0: + // FIXME: This table is in another file + field_size = idx_size (meta, MONO_TABLE_METHOD); + break; + case 1: + field_size = idx_size (meta, MONO_TABLE_IMPORTSCOPE); + break; + case 2: + field_size = idx_size (meta, MONO_TABLE_LOCALVARIABLE); + break; + case 3: + field_size = idx_size (meta, MONO_TABLE_LOCALCONSTANT); + break; + default: + g_assert_not_reached (); + break; + } + break; + case MONO_TABLE_METHODBODY: + g_assert (i == 0); + field_size = idx_size (meta, MONO_TABLE_DOCUMENT); break; + case MONO_TABLE_IMPORTSCOPE: + g_assert(i == 0); + field_size = idx_size (meta, MONO_TABLE_IMPORTSCOPE); break; + case MONO_TABLE_STATEMACHINEMETHOD: + g_assert(i == 0 || i == 1); + field_size = idx_size(meta, MONO_TABLE_METHOD); break; + default: + g_error ("Can't handle MONO_MT_TABLE_IDX for table %d element %d", tableindex, i); + } + break; + + /* + * HasConstant: ParamDef, FieldDef, Property + */ + case MONO_MT_CONST_IDX: + n = MAX (get_nrows (meta, MONO_TABLE_PARAM), + get_nrows (meta, MONO_TABLE_FIELD)); + n = MAX (n, get_nrows (meta, MONO_TABLE_PROPERTY)); + + /* 2 bits to encode tag */ + field_size = rtsize (meta, n, 16-2); + break; + + /* + * HasCustomAttribute: points to any table but + * itself. + */ + case MONO_MT_HASCAT_IDX: + /* + * We believe that since the signature and + * permission are indexing the Blob heap, + * we should consider the blob size first + */ + /* I'm not a believer - lupus + if (meta->idx_blob_wide){ + field_size = 4; + break; + }*/ + + n = MAX (get_nrows (meta, MONO_TABLE_METHOD), + get_nrows (meta, MONO_TABLE_FIELD)); + n = MAX (n, get_nrows (meta, MONO_TABLE_TYPEREF)); + n = MAX (n, get_nrows (meta, MONO_TABLE_TYPEDEF)); + n = MAX (n, get_nrows (meta, MONO_TABLE_PARAM)); + n = MAX (n, get_nrows (meta, MONO_TABLE_INTERFACEIMPL)); + n = MAX (n, get_nrows (meta, MONO_TABLE_MEMBERREF)); + n = MAX (n, get_nrows (meta, MONO_TABLE_MODULE)); + n = MAX (n, get_nrows (meta, MONO_TABLE_DECLSECURITY)); + n = MAX (n, get_nrows (meta, MONO_TABLE_PROPERTY)); + n = MAX (n, get_nrows (meta, MONO_TABLE_EVENT)); + n = MAX (n, get_nrows (meta, MONO_TABLE_STANDALONESIG)); + n = MAX (n, get_nrows (meta, MONO_TABLE_MODULEREF)); + n = MAX (n, get_nrows (meta, MONO_TABLE_TYPESPEC)); + n = MAX (n, get_nrows (meta, MONO_TABLE_ASSEMBLY)); + n = MAX (n, get_nrows (meta, MONO_TABLE_ASSEMBLYREF)); + n = MAX (n, get_nrows (meta, MONO_TABLE_FILE)); + n = MAX (n, get_nrows (meta, MONO_TABLE_EXPORTEDTYPE)); + n = MAX (n, get_nrows (meta, MONO_TABLE_MANIFESTRESOURCE)); + n = MAX (n, get_nrows (meta, MONO_TABLE_GENERICPARAM)); + n = MAX (n, get_nrows (meta, MONO_TABLE_GENERICPARAMCONSTRAINT)); + n = MAX (n, get_nrows (meta, MONO_TABLE_METHODSPEC)); + + /* 5 bits to encode */ + field_size = rtsize (meta, n, 16-5); + break; + + /* + * HasCustomAttribute: points to any table but + * itself. + */ + + case MONO_MT_HASCUSTDEBUG_IDX: + n = MAX(get_nrows (meta, MONO_TABLE_METHOD), + get_nrows (meta, MONO_TABLE_FIELD)); + n = MAX(n, get_nrows (meta, MONO_TABLE_TYPEREF)); + n = MAX(n, get_nrows (meta, MONO_TABLE_TYPEDEF)); + n = MAX(n, get_nrows (meta, MONO_TABLE_PARAM)); + n = MAX(n, get_nrows (meta, MONO_TABLE_INTERFACEIMPL)); + n = MAX(n, get_nrows (meta, MONO_TABLE_MEMBERREF)); + n = MAX(n, get_nrows (meta, MONO_TABLE_MODULE)); + n = MAX(n, get_nrows (meta, MONO_TABLE_DECLSECURITY)); + n = MAX(n, get_nrows (meta, MONO_TABLE_PROPERTY)); + n = MAX(n, get_nrows (meta, MONO_TABLE_EVENT)); + n = MAX(n, get_nrows (meta, MONO_TABLE_STANDALONESIG)); + n = MAX(n, get_nrows (meta, MONO_TABLE_MODULEREF)); + n = MAX(n, get_nrows (meta, MONO_TABLE_TYPESPEC)); + n = MAX(n, get_nrows (meta, MONO_TABLE_ASSEMBLY)); + n = MAX(n, get_nrows (meta, MONO_TABLE_ASSEMBLYREF)); + n = MAX(n, get_nrows (meta, MONO_TABLE_FILE)); + n = MAX(n, get_nrows (meta, MONO_TABLE_EXPORTEDTYPE)); + n = MAX(n, get_nrows (meta, MONO_TABLE_MANIFESTRESOURCE)); + n = MAX(n, get_nrows (meta, MONO_TABLE_GENERICPARAM)); + n = MAX(n, get_nrows (meta, MONO_TABLE_GENERICPARAMCONSTRAINT)); + n = MAX(n, get_nrows (meta, MONO_TABLE_METHODSPEC)); + n = MAX(n, get_nrows (meta, MONO_TABLE_DOCUMENT)); + n = MAX(n, get_nrows (meta, MONO_TABLE_LOCALSCOPE)); + n = MAX(n, get_nrows (meta, MONO_TABLE_LOCALVARIABLE)); + n = MAX(n, get_nrows (meta, MONO_TABLE_LOCALCONSTANT)); + n = MAX(n, get_nrows (meta, MONO_TABLE_IMPORTSCOPE)); + + /* 5 bits to encode */ + field_size = rtsize(meta, n, 16 - 5); + break; + + /* + * CustomAttributeType: MethodDef, MemberRef. + */ + case MONO_MT_CAT_IDX: + n = MAX (get_nrows (meta, MONO_TABLE_METHOD), + get_nrows (meta, MONO_TABLE_MEMBERREF)); + + /* 3 bits to encode */ + field_size = rtsize (meta, n, 16-3); + break; + + /* + * HasDeclSecurity: Typedef, MethodDef, Assembly + */ + case MONO_MT_HASDEC_IDX: + n = MAX (get_nrows (meta, MONO_TABLE_TYPEDEF), + get_nrows (meta, MONO_TABLE_METHOD)); + n = MAX (n, get_nrows (meta, MONO_TABLE_ASSEMBLY)); + + /* 2 bits to encode */ + field_size = rtsize (meta, n, 16-2); + break; + + /* + * Implementation: File, AssemblyRef, ExportedType + */ + case MONO_MT_IMPL_IDX: + n = MAX (get_nrows (meta, MONO_TABLE_FILE), + get_nrows (meta, MONO_TABLE_ASSEMBLYREF)); + n = MAX (n, get_nrows (meta, MONO_TABLE_EXPORTEDTYPE)); + + /* 2 bits to encode tag */ + field_size = rtsize (meta, n, 16-2); + break; + + /* + * HasFieldMarshall: FieldDef, ParamDef + */ + case MONO_MT_HFM_IDX: + n = MAX (get_nrows (meta, MONO_TABLE_FIELD), + get_nrows (meta, MONO_TABLE_PARAM)); + + /* 1 bit used to encode tag */ + field_size = rtsize (meta, n, 16-1); + break; + + /* + * MemberForwarded: FieldDef, MethodDef + */ + case MONO_MT_MF_IDX: + n = MAX (get_nrows (meta, MONO_TABLE_FIELD), + get_nrows (meta, MONO_TABLE_METHOD)); + + /* 1 bit used to encode tag */ + field_size = rtsize (meta, n, 16-1); + break; + + /* + * TypeDefOrRef: TypeDef, ParamDef, TypeSpec + * LAMESPEC + * It is TypeDef, _TypeRef_, TypeSpec, instead. + */ + case MONO_MT_TDOR_IDX: + n = MAX (get_nrows (meta, MONO_TABLE_TYPEDEF), + get_nrows (meta, MONO_TABLE_TYPEREF)); + n = MAX (n, get_nrows (meta, MONO_TABLE_TYPESPEC)); + + /* 2 bits to encode */ + field_size = rtsize (meta, n, 16-2); + break; + + /* + * MemberRefParent: TypeDef, TypeRef, MethodDef, ModuleRef, TypeSpec, MemberRef + */ + case MONO_MT_MRP_IDX: + n = MAX (get_nrows (meta, MONO_TABLE_TYPEDEF), + get_nrows (meta, MONO_TABLE_TYPEREF)); + n = MAX (n, get_nrows (meta, MONO_TABLE_METHOD)); + n = MAX (n, get_nrows (meta, MONO_TABLE_MODULEREF)); + n = MAX (n, get_nrows (meta, MONO_TABLE_TYPESPEC)); + + /* 3 bits to encode */ + field_size = rtsize (meta, n, 16 - 3); + break; + + /* + * MethodDefOrRef: MethodDef, MemberRef + */ + case MONO_MT_MDOR_IDX: + n = MAX (get_nrows (meta, MONO_TABLE_METHOD), + get_nrows (meta, MONO_TABLE_MEMBERREF)); + + /* 1 bit used to encode tag */ + field_size = rtsize (meta, n, 16-1); + break; + + /* + * HasSemantics: Property, Event + */ + case MONO_MT_HS_IDX: + n = MAX (get_nrows (meta, MONO_TABLE_PROPERTY), + get_nrows (meta, MONO_TABLE_EVENT)); + + /* 1 bit used to encode tag */ + field_size = rtsize (meta, n, 16-1); + break; + + /* + * ResolutionScope: Module, ModuleRef, AssemblyRef, TypeRef + */ + case MONO_MT_RS_IDX: + n = MAX (get_nrows (meta, MONO_TABLE_MODULE), + get_nrows (meta, MONO_TABLE_MODULEREF)); + n = MAX (n, get_nrows (meta, MONO_TABLE_ASSEMBLYREF)); + n = MAX (n, get_nrows (meta, MONO_TABLE_TYPEREF)); + + /* 2 bits used to encode tag (ECMA spec claims 3) */ + field_size = rtsize (meta, n, 16 - 2); + break; + } + + /* + * encode field size as follows (we just need to + * distinguish them). + * + * 4 -> 3 + * 2 -> 1 + * 1 -> 0 + */ + bitfield |= (field_size-1) << shift; + shift += 2; + size += field_size; + /*g_print ("table %02x field %d size %d\n", tableindex, i, field_size);*/ + } + + *result_bitfield = (i << 24) | bitfield; + return size; +} + +/** + * mono_metadata_compute_table_bases: + * \param meta metadata context to compute table values + * + * Computes the table bases for the metadata structure. + * This is an internal function used by the image loader code. + */ +void +mono_metadata_compute_table_bases (MonoImage *meta) +{ + int i; + const char *base = meta->tables_base; + + for (i = 0; i < MONO_TABLE_NUM; i++) { + MonoTableInfo *table = &meta->tables [i]; + if (table->rows == 0) + continue; + + table->row_size = mono_metadata_compute_size (meta, i, &table->size_bitfield); + table->base = base; + base += table->rows * table->row_size; + } +} + +/** + * mono_metadata_locate: + * \param meta metadata context + * \param table table code. + * \param idx index of element to retrieve from \p table. + * + * \returns a pointer to the \p idx element in the metadata table + * whose code is \p table. + */ +const char * +mono_metadata_locate (MonoImage *meta, int table, int idx) +{ + /* idx == 0 refers always to NULL */ + g_return_val_if_fail (idx > 0 && idx <= meta->tables [table].rows, ""); /*FIXME shouldn't we return NULL here?*/ + + return meta->tables [table].base + (meta->tables [table].row_size * (idx - 1)); +} + +/** + * mono_metadata_locate_token: + * \param meta metadata context + * \param token metadata token + * + * \returns a pointer to the data in the metadata represented by the + * token \p token . + */ +const char * +mono_metadata_locate_token (MonoImage *meta, guint32 token) +{ + return mono_metadata_locate (meta, token >> 24, token & 0xffffff); +} + +/** + * mono_metadata_string_heap: + * \param meta metadata context + * \param index index into the string heap. + * \returns an in-memory pointer to the \p index in the string heap. + */ +const char * +mono_metadata_string_heap (MonoImage *meta, guint32 index) +{ + g_assert (index < meta->heap_strings.size); + g_return_val_if_fail (index < meta->heap_strings.size, ""); + return meta->heap_strings.data + index; +} + +/** + * mono_metadata_user_string: + * \param meta metadata context + * \param index index into the user string heap. + * \returns an in-memory pointer to the \p index in the user string heap (#US). + */ +const char * +mono_metadata_user_string (MonoImage *meta, guint32 index) +{ + g_assert (index < meta->heap_us.size); + g_return_val_if_fail (index < meta->heap_us.size, ""); + return meta->heap_us.data + index; +} + +/** + * mono_metadata_blob_heap: + * \param meta metadata context + * \param index index into the blob. + * \returns an in-memory pointer to the \p index in the Blob heap. + */ +const char * +mono_metadata_blob_heap (MonoImage *meta, guint32 index) +{ + g_assert (index < meta->heap_blob.size); + g_return_val_if_fail (index < meta->heap_blob.size, "");/*FIXME shouldn't we return NULL and check for index == 0?*/ + return meta->heap_blob.data + index; +} + +/** + * mono_metadata_guid_heap: + * \param meta metadata context + * \param index index into the guid heap. + * \returns an in-memory pointer to the \p index in the guid heap. + */ +const char * +mono_metadata_guid_heap (MonoImage *meta, guint32 index) +{ + --index; + index *= 16; /* adjust for guid size and 1-based index */ + g_return_val_if_fail (index < meta->heap_guid.size, ""); + return meta->heap_guid.data + index; +} + +static const unsigned char * +dword_align (const unsigned char *ptr) +{ +#if SIZEOF_VOID_P == 8 + return (const unsigned char *) (((guint64) (ptr + 3)) & ~3); +#else + return (const unsigned char *) (((guint32) (ptr + 3)) & ~3); +#endif +} + +/** + * mono_metadata_decode_row: + * \param t table to extract information from. + * \param idx index in table. + * \param res array of \p res_size cols to store the results in + * + * This decompresses the metadata element \p idx in table \p t + * into the \c guint32 \p res array that has \p res_size elements + */ +void +mono_metadata_decode_row (const MonoTableInfo *t, int idx, guint32 *res, int res_size) +{ + guint32 bitfield = t->size_bitfield; + int i, count = mono_metadata_table_count (bitfield); + const char *data; + + g_assert (idx < t->rows); + g_assert (idx >= 0); + data = t->base + idx * t->row_size; + + g_assert (res_size == count); + + for (i = 0; i < count; i++) { + int n = mono_metadata_table_size (bitfield, i); + + switch (n){ + case 1: + res [i] = *data; break; + case 2: + res [i] = read16 (data); break; + case 4: + res [i] = read32 (data); break; + default: + g_assert_not_reached (); + } + data += n; + } +} + +/** + * mono_metadata_decode_row_col: + * \param t table to extract information from. + * \param idx index for row in table. + * \param col column in the row. + * + * This function returns the value of column \p col from the \p idx + * row in the table \p t . + */ +guint32 +mono_metadata_decode_row_col (const MonoTableInfo *t, int idx, guint col) +{ + guint32 bitfield = t->size_bitfield; + int i; + register const char *data; + register int n; + + g_assert (idx < t->rows); + g_assert (col < mono_metadata_table_count (bitfield)); + data = t->base + idx * t->row_size; + + n = mono_metadata_table_size (bitfield, 0); + for (i = 0; i < col; ++i) { + data += n; + n = mono_metadata_table_size (bitfield, i + 1); + } + switch (n) { + case 1: + return *data; + case 2: + return read16 (data); + case 4: + return read32 (data); + default: + g_assert_not_reached (); + } + return 0; +} + +/** + * mono_metadata_decode_blob_size: + * \param ptr pointer to a blob object + * \param rptr the new position of the pointer + * + * This decodes a compressed size as described by 24.2.4 (#US and #Blob a blob or user string object) + * + * \returns the size of the blob object + */ +guint32 +mono_metadata_decode_blob_size (const char *xptr, const char **rptr) +{ + const unsigned char *ptr = (const unsigned char *)xptr; + guint32 size; + + if ((*ptr & 0x80) == 0){ + size = ptr [0] & 0x7f; + ptr++; + } else if ((*ptr & 0x40) == 0){ + size = ((ptr [0] & 0x3f) << 8) + ptr [1]; + ptr += 2; + } else { + size = ((ptr [0] & 0x1f) << 24) + + (ptr [1] << 16) + + (ptr [2] << 8) + + ptr [3]; + ptr += 4; + } + if (rptr) + *rptr = (char*)ptr; + return size; +} + +/** + * mono_metadata_decode_value: + * \param ptr pointer to decode from + * \param rptr the new position of the pointer + * + * This routine decompresses 32-bit values as specified in the "Blob and + * Signature" section (23.2) + * + * \returns the decoded value + */ +guint32 +mono_metadata_decode_value (const char *_ptr, const char **rptr) +{ + const unsigned char *ptr = (const unsigned char *) _ptr; + unsigned char b = *ptr; + guint32 len; + + if ((b & 0x80) == 0){ + len = b; + ++ptr; + } else if ((b & 0x40) == 0){ + len = ((b & 0x3f) << 8 | ptr [1]); + ptr += 2; + } else { + len = ((b & 0x1f) << 24) | + (ptr [1] << 16) | + (ptr [2] << 8) | + ptr [3]; + ptr += 4; + } + if (rptr) + *rptr = (char*)ptr; + + return len; +} + +/** + * mono_metadata_decode_signed_value: + * \param ptr pointer to decode from + * \param rptr the new position of the pointer + * + * This routine decompresses 32-bit signed values + * (not specified in the spec) + * + * \returns the decoded value + */ +gint32 +mono_metadata_decode_signed_value (const char *ptr, const char **rptr) +{ + guint32 uval = mono_metadata_decode_value (ptr, rptr); + gint32 ival = uval >> 1; + if (!(uval & 1)) + return ival; + /* ival is a truncated 2's complement negative number. */ + if (ival < 0x40) + /* 6 bits = 7 bits for compressed representation (top bit is '0') - 1 sign bit */ + return ival - 0x40; + if (ival < 0x2000) + /* 13 bits = 14 bits for compressed representation (top bits are '10') - 1 sign bit */ + return ival - 0x2000; + if (ival < 0x10000000) + /* 28 bits = 29 bits for compressed representation (top bits are '110') - 1 sign bit */ + return ival - 0x10000000; + g_assert (ival < 0x20000000); + g_warning ("compressed signed value appears to use 29 bits for compressed representation: %x (raw: %8x)", ival, uval); + return ival - 0x20000000; +} + +/** + * mono_metadata_translate_token_index: + * Translates the given 1-based index into the \c Method, \c Field, \c Event, or \c Param tables + * using the \c *Ptr tables in uncompressed metadata, if they are available. + * + * FIXME: The caller is not forced to call this function, which is error-prone, since + * forgetting to call it would only show up as a bug on uncompressed metadata. + */ +guint32 +mono_metadata_translate_token_index (MonoImage *image, int table, guint32 idx) +{ + if (!image->uncompressed_metadata) + return idx; + + switch (table) { + case MONO_TABLE_METHOD: + if (image->tables [MONO_TABLE_METHOD_POINTER].rows) + return mono_metadata_decode_row_col (&image->tables [MONO_TABLE_METHOD_POINTER], idx - 1, MONO_METHOD_POINTER_METHOD); + else + return idx; + case MONO_TABLE_FIELD: + if (image->tables [MONO_TABLE_FIELD_POINTER].rows) + return mono_metadata_decode_row_col (&image->tables [MONO_TABLE_FIELD_POINTER], idx - 1, MONO_FIELD_POINTER_FIELD); + else + return idx; + case MONO_TABLE_EVENT: + if (image->tables [MONO_TABLE_EVENT_POINTER].rows) + return mono_metadata_decode_row_col (&image->tables [MONO_TABLE_EVENT_POINTER], idx - 1, MONO_EVENT_POINTER_EVENT); + else + return idx; + case MONO_TABLE_PROPERTY: + if (image->tables [MONO_TABLE_PROPERTY_POINTER].rows) + return mono_metadata_decode_row_col (&image->tables [MONO_TABLE_PROPERTY_POINTER], idx - 1, MONO_PROPERTY_POINTER_PROPERTY); + else + return idx; + case MONO_TABLE_PARAM: + if (image->tables [MONO_TABLE_PARAM_POINTER].rows) + return mono_metadata_decode_row_col (&image->tables [MONO_TABLE_PARAM_POINTER], idx - 1, MONO_PARAM_POINTER_PARAM); + else + return idx; + default: + return idx; + } +} + +/** + * mono_metadata_decode_table_row: + * + * Same as \c mono_metadata_decode_row, but takes an \p image + \p table ID pair, and takes + * uncompressed metadata into account, so it should be used to access the + * \c Method, \c Field, \c Param and \c Event tables when the access is made from metadata, i.e. + * \p idx is retrieved from a metadata table, like \c MONO_TYPEDEF_FIELD_LIST. + */ +void +mono_metadata_decode_table_row (MonoImage *image, int table, int idx, guint32 *res, int res_size) +{ + if (image->uncompressed_metadata) + idx = mono_metadata_translate_token_index (image, table, idx + 1) - 1; + + mono_metadata_decode_row (&image->tables [table], idx, res, res_size); +} + +/** + * mono_metadata_decode_table_row_col: + * + * Same as \c mono_metadata_decode_row_col, but takes an \p image + \p table ID pair, and takes + * uncompressed metadata into account, so it should be used to access the + * \c Method, \c Field, \c Param and \c Event tables. + */ +guint32 mono_metadata_decode_table_row_col (MonoImage *image, int table, int idx, guint col) +{ + if (image->uncompressed_metadata) + idx = mono_metadata_translate_token_index (image, table, idx + 1) - 1; + + return mono_metadata_decode_row_col (&image->tables [table], idx, col); +} + +/** + * mono_metadata_parse_typedef_or_ref: + * \param m a metadata context. + * \param ptr a pointer to an encoded TypedefOrRef in \p m + * \param rptr pointer updated to match the end of the decoded stream + * \returns a token valid in the \p m metadata decoded from + * the compressed representation. + */ +guint32 +mono_metadata_parse_typedef_or_ref (MonoImage *m, const char *ptr, const char **rptr) +{ + guint32 token; + token = mono_metadata_decode_value (ptr, &ptr); + if (rptr) + *rptr = ptr; + return mono_metadata_token_from_dor (token); +} + +/** + * mono_metadata_parse_custom_mod: + * \param m a metadata context. + * \param dest storage where the info about the custom modifier is stored (may be NULL) + * \param ptr a pointer to (possibly) the start of a custom modifier list + * \param rptr pointer updated to match the end of the decoded stream + * + * Checks if \p ptr points to a type custom modifier compressed representation. + * + * \returns TRUE if a custom modifier was found, FALSE if not. + */ +int +mono_metadata_parse_custom_mod (MonoImage *m, MonoCustomMod *dest, const char *ptr, const char **rptr) +{ + MonoCustomMod local; + if ((*ptr == MONO_TYPE_CMOD_OPT) || (*ptr == MONO_TYPE_CMOD_REQD)) { + if (!dest) + dest = &local; + dest->required = *ptr == MONO_TYPE_CMOD_REQD ? 1 : 0; + dest->token = mono_metadata_parse_typedef_or_ref (m, ptr + 1, rptr); + return TRUE; + } + return FALSE; +} + +/* + * mono_metadata_parse_array_internal: + * @m: a metadata context. + * @transient: whenever to allocate data from the heap + * @ptr: a pointer to an encoded array description. + * @rptr: pointer updated to match the end of the decoded stream + * + * Decodes the compressed array description found in the metadata @m at @ptr. + * + * Returns: a #MonoArrayType structure describing the array type + * and dimensions. Memory is allocated from the heap or from the image mempool, depending + * on the value of @transient. + * + * LOCKING: Acquires the loader lock + */ +static MonoArrayType * +mono_metadata_parse_array_internal (MonoImage *m, MonoGenericContainer *container, + gboolean transient, const char *ptr, const char **rptr, MonoError *error) +{ + int i; + MonoArrayType *array; + MonoType *etype; + + etype = mono_metadata_parse_type_checked (m, container, 0, FALSE, ptr, &ptr, error); //FIXME this doesn't respect @transient + if (!etype) + return NULL; + + array = transient ? (MonoArrayType *)g_malloc0 (sizeof (MonoArrayType)) : (MonoArrayType *)mono_image_alloc0 (m, sizeof (MonoArrayType)); + array->eklass = mono_class_from_mono_type (etype); + array->rank = mono_metadata_decode_value (ptr, &ptr); + + array->numsizes = mono_metadata_decode_value (ptr, &ptr); + if (array->numsizes) + array->sizes = transient ? (int *)g_malloc0 (sizeof (int) * array->numsizes) : (int *)mono_image_alloc0 (m, sizeof (int) * array->numsizes); + for (i = 0; i < array->numsizes; ++i) + array->sizes [i] = mono_metadata_decode_value (ptr, &ptr); + + array->numlobounds = mono_metadata_decode_value (ptr, &ptr); + if (array->numlobounds) + array->lobounds = transient ? (int *)g_malloc0 (sizeof (int) * array->numlobounds) : (int *)mono_image_alloc0 (m, sizeof (int) * array->numlobounds); + for (i = 0; i < array->numlobounds; ++i) + array->lobounds [i] = mono_metadata_decode_signed_value (ptr, &ptr); + + if (rptr) + *rptr = ptr; + return array; +} + +/** + * mono_metadata_parse_array: + */ +MonoArrayType * +mono_metadata_parse_array (MonoImage *m, const char *ptr, const char **rptr) +{ + MonoError error; + MonoArrayType *ret = mono_metadata_parse_array_internal (m, NULL, FALSE, ptr, rptr, &error); + mono_error_cleanup (&error); + + return ret; +} + +/** + * mono_metadata_free_array: + * \param array array description + * + * Frees the array description returned from \c mono_metadata_parse_array. + */ +void +mono_metadata_free_array (MonoArrayType *array) +{ + g_free (array->sizes); + g_free (array->lobounds); + g_free (array); +} + +/* + * need to add common field and param attributes combinations: + * [out] param + * public static + * public static literal + * private + * private static + * private static literal + */ +static const MonoType +builtin_types[] = { + /* data, attrs, type, nmods, byref, pinned */ + {{NULL}, 0, MONO_TYPE_VOID, 0, 0, 0}, + {{NULL}, 0, MONO_TYPE_BOOLEAN, 0, 0, 0}, + {{NULL}, 0, MONO_TYPE_BOOLEAN, 0, 1, 0}, + {{NULL}, 0, MONO_TYPE_CHAR, 0, 0, 0}, + {{NULL}, 0, MONO_TYPE_CHAR, 0, 1, 0}, + {{NULL}, 0, MONO_TYPE_I1, 0, 0, 0}, + {{NULL}, 0, MONO_TYPE_I1, 0, 1, 0}, + {{NULL}, 0, MONO_TYPE_U1, 0, 0, 0}, + {{NULL}, 0, MONO_TYPE_U1, 0, 1, 0}, + {{NULL}, 0, MONO_TYPE_I2, 0, 0, 0}, + {{NULL}, 0, MONO_TYPE_I2, 0, 1, 0}, + {{NULL}, 0, MONO_TYPE_U2, 0, 0, 0}, + {{NULL}, 0, MONO_TYPE_U2, 0, 1, 0}, + {{NULL}, 0, MONO_TYPE_I4, 0, 0, 0}, + {{NULL}, 0, MONO_TYPE_I4, 0, 1, 0}, + {{NULL}, 0, MONO_TYPE_U4, 0, 0, 0}, + {{NULL}, 0, MONO_TYPE_U4, 0, 1, 0}, + {{NULL}, 0, MONO_TYPE_I8, 0, 0, 0}, + {{NULL}, 0, MONO_TYPE_I8, 0, 1, 0}, + {{NULL}, 0, MONO_TYPE_U8, 0, 0, 0}, + {{NULL}, 0, MONO_TYPE_U8, 0, 1, 0}, + {{NULL}, 0, MONO_TYPE_R4, 0, 0, 0}, + {{NULL}, 0, MONO_TYPE_R4, 0, 1, 0}, + {{NULL}, 0, MONO_TYPE_R8, 0, 0, 0}, + {{NULL}, 0, MONO_TYPE_R8, 0, 1, 0}, + {{NULL}, 0, MONO_TYPE_STRING, 0, 0, 0}, + {{NULL}, 0, MONO_TYPE_STRING, 0, 1, 0}, + {{NULL}, 0, MONO_TYPE_OBJECT, 0, 0, 0}, + {{NULL}, 0, MONO_TYPE_OBJECT, 0, 1, 0}, + {{NULL}, 0, MONO_TYPE_TYPEDBYREF, 0, 0, 0}, + {{NULL}, 0, MONO_TYPE_I, 0, 0, 0}, + {{NULL}, 0, MONO_TYPE_I, 0, 1, 0}, + {{NULL}, 0, MONO_TYPE_U, 0, 0, 0}, + {{NULL}, 0, MONO_TYPE_U, 0, 1, 0}, +}; + +#define NBUILTIN_TYPES() (sizeof (builtin_types) / sizeof (builtin_types [0])) + +static GHashTable *type_cache = NULL; +static gint32 next_generic_inst_id = 0; + +/* Protected by image_sets_mutex */ +static MonoImageSet *mscorlib_image_set; +/* Protected by image_sets_mutex */ +static GPtrArray *image_sets; +static mono_mutex_t image_sets_mutex; + +static guint mono_generic_class_hash (gconstpointer data); + +/* + * MonoTypes with modifies are never cached, so we never check or use that field. + */ +static guint +mono_type_hash (gconstpointer data) +{ + const MonoType *type = (const MonoType *) data; + if (type->type == MONO_TYPE_GENERICINST) + return mono_generic_class_hash (type->data.generic_class); + else + return type->type | (type->byref << 8) | (type->attrs << 9); +} + +static gint +mono_type_equal (gconstpointer ka, gconstpointer kb) +{ + const MonoType *a = (const MonoType *) ka; + const MonoType *b = (const MonoType *) kb; + + if (a->type != b->type || a->byref != b->byref || a->attrs != b->attrs || a->pinned != b->pinned) + return 0; + /* need other checks */ + return 1; +} + +guint +mono_metadata_generic_inst_hash (gconstpointer data) +{ + const MonoGenericInst *ginst = (const MonoGenericInst *) data; + guint hash = 0; + int i; + + for (i = 0; i < ginst->type_argc; ++i) { + hash *= 13; + hash += mono_metadata_type_hash (ginst->type_argv [i]); + } + + return hash ^ (ginst->is_open << 8); +} + +static gboolean +mono_generic_inst_equal_full (const MonoGenericInst *a, const MonoGenericInst *b, gboolean signature_only) +{ + int i; + + // An optimization: if the ids of two insts are the same, we know they are the same inst and don't check contents. + // Furthermore, because we perform early de-duping, if the ids differ, we know the contents differ. +#ifndef MONO_SMALL_CONFIG // Optimization does not work in MONO_SMALL_CONFIG: There are no IDs + if (a->id && b->id) { // "id 0" means "object has no id"-- de-duping hasn't been performed yet, must check contents. + if (a->id == b->id) + return TRUE; + // In signature-comparison mode id equality implies object equality, but this is not true for inequality. + // Two separate objects could have signature-equavalent contents. + if (!signature_only) + return FALSE; + } +#endif + + if (a->is_open != b->is_open || a->type_argc != b->type_argc) + return FALSE; + for (i = 0; i < a->type_argc; ++i) { + if (!do_mono_metadata_type_equal (a->type_argv [i], b->type_argv [i], signature_only)) + return FALSE; + } + return TRUE; +} + +gboolean +mono_metadata_generic_inst_equal (gconstpointer ka, gconstpointer kb) +{ + const MonoGenericInst *a = (const MonoGenericInst *) ka; + const MonoGenericInst *b = (const MonoGenericInst *) kb; + + return mono_generic_inst_equal_full (a, b, FALSE); +} + +static guint +mono_generic_class_hash (gconstpointer data) +{ + const MonoGenericClass *gclass = (const MonoGenericClass *) data; + guint hash = mono_metadata_type_hash (&gclass->container_class->byval_arg); + + hash *= 13; + hash += gclass->is_tb_open; + hash += mono_metadata_generic_context_hash (&gclass->context); + + return hash; +} + +static gboolean +mono_generic_class_equal (gconstpointer ka, gconstpointer kb) +{ + const MonoGenericClass *a = (const MonoGenericClass *) ka; + const MonoGenericClass *b = (const MonoGenericClass *) kb; + + return _mono_metadata_generic_class_equal (a, b, FALSE); +} + +/** + * mono_metadata_init: + * + * Initialize the global variables of this module. + * This is a Mono runtime internal function. + */ +void +mono_metadata_init (void) +{ + int i; + + /* We guard against double initialization due to how pedump in verification mode works. + Until runtime initialization is properly factored to work with what it needs we need workarounds like this. + FIXME: https://bugzilla.xamarin.com/show_bug.cgi?id=58793 + */ + static gboolean inited; + + if (inited) + return; + inited = TRUE; + + type_cache = g_hash_table_new (mono_type_hash, mono_type_equal); + + for (i = 0; i < NBUILTIN_TYPES (); ++i) + g_hash_table_insert (type_cache, (gpointer) &builtin_types [i], (gpointer) &builtin_types [i]); + + mono_os_mutex_init_recursive (&image_sets_mutex); + + mono_counters_register ("ImgSet Cache Hit", MONO_COUNTER_METADATA | MONO_COUNTER_INT, &img_set_cache_hit); + mono_counters_register ("ImgSet Cache Miss", MONO_COUNTER_METADATA | MONO_COUNTER_INT, &img_set_cache_miss); + mono_counters_register ("ImgSet Count", MONO_COUNTER_METADATA | MONO_COUNTER_INT, &img_set_count); +} + +/** + * mono_metadata_cleanup: + * + * Free all resources used by this module. + * This is a Mono runtime internal function. + */ +void +mono_metadata_cleanup (void) +{ + g_hash_table_destroy (type_cache); + type_cache = NULL; + g_ptr_array_free (image_sets, TRUE); + image_sets = NULL; + mono_os_mutex_destroy (&image_sets_mutex); +} + +/** + * mono_metadata_parse_type: + * \param m metadata context + * \param mode kind of type that may be found at \p ptr + * \param opt_attrs optional attributes to store in the returned type + * \param ptr pointer to the type representation + * \param rptr pointer updated to match the end of the decoded stream + * \param transient whenever to allocate the result from the heap or from a mempool + * + * Decode a compressed type description found at \p ptr in \p m . + * \p mode can be one of \c MONO_PARSE_MOD_TYPE, \c MONO_PARSE_PARAM, \c MONO_PARSE_RET, + * \c MONO_PARSE_FIELD, \c MONO_PARSE_LOCAL, \c MONO_PARSE_TYPE. + * This function can be used to decode type descriptions in method signatures, + * field signatures, locals signatures etc. + * + * To parse a generic type, \c generic_container points to the current class'es + * (the \c generic_container field in the MonoClass) or the current generic method's + * (stored in image->property_hash) generic container. + * When we encounter a \c MONO_TYPE_VAR or \c MONO_TYPE_MVAR, it's looked up in + * this \c MonoGenericContainer. + * + * LOCKING: Acquires the loader lock. + * + * \returns a \c MonoType structure representing the decoded type. + */ +static MonoType* +mono_metadata_parse_type_internal (MonoImage *m, MonoGenericContainer *container, + short opt_attrs, gboolean transient, const char *ptr, const char **rptr, MonoError *error) +{ + MonoType *type, *cached; + MonoType stype; + gboolean byref = FALSE; + gboolean pinned = FALSE; + const char *tmp_ptr; + int count = 0; // Number of mod arguments + gboolean found; + + error_init (error); + + /* + * According to the spec, custom modifiers should come before the byref + * flag, but the IL produced by ilasm from the following signature: + * object modopt(...) & + * starts with a byref flag, followed by the modifiers. (bug #49802) + * Also, this type seems to be different from 'object & modopt(...)'. Maybe + * it would be better to treat byref as real type constructor instead of + * a modifier... + * Also, pinned should come before anything else, but some MSV++ produced + * assemblies violate this (#bug 61990). + */ + + /* Count the modifiers first */ + tmp_ptr = ptr; + found = TRUE; + while (found) { + switch (*tmp_ptr) { + case MONO_TYPE_PINNED: + case MONO_TYPE_BYREF: + ++tmp_ptr; + break; + case MONO_TYPE_CMOD_REQD: + case MONO_TYPE_CMOD_OPT: + count ++; + mono_metadata_parse_custom_mod (m, NULL, tmp_ptr, &tmp_ptr); + break; + default: + found = FALSE; + } + } + + if (count) { // There are mods, so the MonoType will be of nonstandard size. + int size; + + size = MONO_SIZEOF_TYPE + ((gint32)count) * sizeof (MonoCustomMod); + type = transient ? (MonoType *)g_malloc0 (size) : (MonoType *)mono_image_alloc0 (m, size); + type->num_mods = count; + if (count > 64) { + mono_error_set_bad_image (error, m, "Invalid type with more than 64 modifiers"); + return NULL; + } + } else { // The type is of standard size, so we can allocate it on the stack. + type = &stype; + memset (type, 0, MONO_SIZEOF_TYPE); + } + + /* Iterate again, but now parse pinned, byref and custom modifiers */ + found = TRUE; + count = 0; + while (found) { + switch (*ptr) { + case MONO_TYPE_PINNED: + pinned = TRUE; + ++ptr; + break; + case MONO_TYPE_BYREF: + byref = TRUE; + ++ptr; + break; + case MONO_TYPE_CMOD_REQD: + case MONO_TYPE_CMOD_OPT: + mono_metadata_parse_custom_mod (m, &(type->modifiers [count]), ptr, &ptr); + count ++; + break; + default: + found = FALSE; + } + } + + type->attrs = opt_attrs; + type->byref = byref; + type->pinned = pinned ? 1 : 0; + + if (!do_mono_metadata_parse_type (type, m, container, transient, ptr, &ptr, error)) + return NULL; + + if (rptr) + *rptr = ptr; + + // Possibly we can return an already-allocated type instead of the one we decoded + if (!type->num_mods && !transient) { + /* no need to free type here, because it is on the stack */ + if ((type->type == MONO_TYPE_CLASS || type->type == MONO_TYPE_VALUETYPE) && !type->pinned && !type->attrs) { + MonoType *ret = type->byref ? &type->data.klass->this_arg : &type->data.klass->byval_arg; + + /* Consider the case: + + class Foo { class Bar {} } + class Test : Foo.Bar {} + + When Foo is being expanded, 'Test' isn't yet initialized. It's actually in + a really pristine state: it doesn't even know whether 'Test' is a reference or a value type. + + We ensure that the MonoClass is in a state that we can canonicalize to: + + klass->byval_arg.data.klass == klass + klass->this_arg.data.klass == klass + + If we can't canonicalize 'type', it doesn't matter, since later users of 'type' will do it. + + LOCKING: even though we don't explicitly hold a lock, in the problematic case 'ret' is a field + of a MonoClass which currently holds the loader lock. 'type' is local. + */ + if (ret->data.klass == type->data.klass) { + return ret; + } + } + /* No need to use locking since nobody is modifying the hash table */ + if ((cached = (MonoType *)g_hash_table_lookup (type_cache, type))) { + return cached; + } + } + + /* printf ("%x %x %c %s\n", type->attrs, type->num_mods, type->pinned ? 'p' : ' ', mono_type_full_name (type)); */ + + if (type == &stype) { // Type was allocated on the stack, so we need to copy it to safety + type = transient ? (MonoType *)g_malloc (MONO_SIZEOF_TYPE) : (MonoType *)mono_image_alloc (m, MONO_SIZEOF_TYPE); + memcpy (type, &stype, MONO_SIZEOF_TYPE); + } + return type; +} + + +MonoType* +mono_metadata_parse_type_checked (MonoImage *m, MonoGenericContainer *container, + short opt_attrs, gboolean transient, const char *ptr, const char **rptr, MonoError *error) +{ + return mono_metadata_parse_type_internal (m, container, opt_attrs, transient, ptr, rptr, error); +} + +/* + * LOCKING: Acquires the loader lock. + */ +MonoType* +mono_metadata_parse_type (MonoImage *m, MonoParseTypeMode mode, short opt_attrs, + const char *ptr, const char **rptr) +{ + MonoError error; + MonoType * type = mono_metadata_parse_type_internal (m, NULL, opt_attrs, FALSE, ptr, rptr, &error); + mono_error_cleanup (&error); + return type; +} + +gboolean +mono_metadata_method_has_param_attrs (MonoImage *m, int def) +{ + MonoTableInfo *paramt = &m->tables [MONO_TABLE_PARAM]; + MonoTableInfo *methodt = &m->tables [MONO_TABLE_METHOD]; + guint lastp, i, param_index = mono_metadata_decode_row_col (methodt, def - 1, MONO_METHOD_PARAMLIST); + + if (def < methodt->rows) + lastp = mono_metadata_decode_row_col (methodt, def, MONO_METHOD_PARAMLIST); + else + lastp = m->tables [MONO_TABLE_PARAM].rows + 1; + + for (i = param_index; i < lastp; ++i) { + guint32 flags = mono_metadata_decode_row_col (paramt, i - 1, MONO_PARAM_FLAGS); + if (flags) + return TRUE; + } + + return FALSE; +} + +/* + * mono_metadata_get_param_attrs: + * + * @m The image to loader parameter attributes from + * @def method def token (one based) + * @param_count number of params to decode including the return value + * + * Return the parameter attributes for the method whose MethodDef index is DEF. The + * returned memory needs to be freed by the caller. If all the param attributes are + * 0, then NULL is returned. + */ +int* +mono_metadata_get_param_attrs (MonoImage *m, int def, int param_count) +{ + MonoTableInfo *paramt = &m->tables [MONO_TABLE_PARAM]; + MonoTableInfo *methodt = &m->tables [MONO_TABLE_METHOD]; + guint32 cols [MONO_PARAM_SIZE]; + guint lastp, i, param_index = mono_metadata_decode_row_col (methodt, def - 1, MONO_METHOD_PARAMLIST); + int *pattrs = NULL; + + if (def < methodt->rows) + lastp = mono_metadata_decode_row_col (methodt, def, MONO_METHOD_PARAMLIST); + else + lastp = paramt->rows + 1; + + for (i = param_index; i < lastp; ++i) { + mono_metadata_decode_row (paramt, i - 1, cols, MONO_PARAM_SIZE); + if (cols [MONO_PARAM_FLAGS]) { + if (!pattrs) + pattrs = g_new0 (int, param_count); + /* at runtime we just ignore this kind of malformed file: + * the verifier can signal the error to the user + */ + if (cols [MONO_PARAM_SEQUENCE] >= param_count) + continue; + pattrs [cols [MONO_PARAM_SEQUENCE]] = cols [MONO_PARAM_FLAGS]; + } + } + + return pattrs; +} + + +/** + * mono_metadata_parse_signature: + * \param image metadata context + * \param token metadata token + * + * Decode a method signature stored in the \c StandAloneSig table + * + * \returns a \c MonoMethodSignature describing the signature. + */ +MonoMethodSignature* +mono_metadata_parse_signature (MonoImage *image, guint32 token) +{ + MonoError error; + MonoMethodSignature *ret; + ret = mono_metadata_parse_signature_checked (image, token, &error); + mono_error_cleanup (&error); + return ret; +} + +/* + * mono_metadata_parse_signature_checked: + * @image: metadata context + * @token: metadata token + * @error: set on error + * + * Decode a method signature stored in the STANDALONESIG table + * + * Returns: a MonoMethodSignature describing the signature. On failure + * returns NULL and sets @error. + */ +MonoMethodSignature* +mono_metadata_parse_signature_checked (MonoImage *image, guint32 token, MonoError *error) +{ + + error_init (error); + MonoTableInfo *tables = image->tables; + guint32 idx = mono_metadata_token_index (token); + guint32 sig; + const char *ptr; + + if (image_is_dynamic (image)) { + return (MonoMethodSignature *)mono_lookup_dynamic_token (image, token, NULL, error); + } + + g_assert (mono_metadata_token_table(token) == MONO_TABLE_STANDALONESIG); + + sig = mono_metadata_decode_row_col (&tables [MONO_TABLE_STANDALONESIG], idx - 1, 0); + + ptr = mono_metadata_blob_heap (image, sig); + mono_metadata_decode_blob_size (ptr, &ptr); + + return mono_metadata_parse_method_signature_full (image, NULL, 0, ptr, NULL, error); +} + +/** + * mono_metadata_signature_alloc: + * \param image metadata context + * \param nparams number of parameters in the signature + * + * Allocate a \c MonoMethodSignature structure with the specified number of params. + * The return type and the params types need to be filled later. + * This is a Mono runtime internal function. + * + * LOCKING: Assumes the loader lock is held. + * + * \returns the new \c MonoMethodSignature structure. + */ +MonoMethodSignature* +mono_metadata_signature_alloc (MonoImage *m, guint32 nparams) +{ + MonoMethodSignature *sig; + + sig = (MonoMethodSignature *)mono_image_alloc0 (m, MONO_SIZEOF_METHOD_SIGNATURE + ((gint32)nparams) * sizeof (MonoType*)); + sig->param_count = nparams; + sig->sentinelpos = -1; + + return sig; +} + +static MonoMethodSignature* +mono_metadata_signature_dup_internal_with_padding (MonoImage *image, MonoMemPool *mp, MonoMethodSignature *sig, size_t padding) +{ + int sigsize, sig_header_size; + MonoMethodSignature *ret; + sigsize = sig_header_size = MONO_SIZEOF_METHOD_SIGNATURE + sig->param_count * sizeof (MonoType *) + padding; + if (sig->ret) + sigsize += MONO_SIZEOF_TYPE; + + if (image) { + ret = (MonoMethodSignature *)mono_image_alloc (image, sigsize); + } else if (mp) { + ret = (MonoMethodSignature *)mono_mempool_alloc (mp, sigsize); + } else { + ret = (MonoMethodSignature *)g_malloc (sigsize); + } + + memcpy (ret, sig, sig_header_size - padding); + + // Copy return value because of ownership semantics. + if (sig->ret) { + // Danger! Do not alter padding use without changing the dup_add_this below + intptr_t end_of_header = (intptr_t)( (char*)(ret) + sig_header_size); + ret->ret = (MonoType *)end_of_header; + memcpy (ret->ret, sig->ret, MONO_SIZEOF_TYPE); + } + + return ret; +} + +static MonoMethodSignature* +mono_metadata_signature_dup_internal (MonoImage *image, MonoMemPool *mp, MonoMethodSignature *sig) +{ + return mono_metadata_signature_dup_internal_with_padding (image, mp, sig, 0); +} +/* + * signature_dup_add_this: + * + * Make a copy of @sig, adding an explicit this argument. + */ +MonoMethodSignature* +mono_metadata_signature_dup_add_this (MonoImage *image, MonoMethodSignature *sig, MonoClass *klass) +{ + MonoMethodSignature *ret; + ret = mono_metadata_signature_dup_internal_with_padding (image, NULL, sig, sizeof (MonoType *)); + + ret->param_count = sig->param_count + 1; + ret->hasthis = FALSE; + + for (int i = sig->param_count - 1; i >= 0; i --) + ret->params [i + 1] = sig->params [i]; + ret->params [0] = klass->valuetype ? &klass->this_arg : &klass->byval_arg; + + for (int i = sig->param_count - 1; i >= 0; i --) + g_assert(ret->params [i + 1]->type == sig->params [i]->type && ret->params [i+1]->type != MONO_TYPE_END); + g_assert (ret->ret->type == sig->ret->type && ret->ret->type != MONO_TYPE_END); + + return ret; +} + + + +MonoMethodSignature* +mono_metadata_signature_dup_full (MonoImage *image, MonoMethodSignature *sig) +{ + MonoMethodSignature *ret = mono_metadata_signature_dup_internal (image, NULL, sig); + + for (int i = 0 ; i < sig->param_count; i ++) + g_assert(ret->params [i]->type == sig->params [i]->type); + g_assert (ret->ret->type == sig->ret->type); + + return ret; +} + +/*The mempool is accessed without synchronization*/ +MonoMethodSignature* +mono_metadata_signature_dup_mempool (MonoMemPool *mp, MonoMethodSignature *sig) +{ + return mono_metadata_signature_dup_internal (NULL, mp, sig); +} + +/** + * mono_metadata_signature_dup: + * \param sig method signature + * + * Duplicate an existing \c MonoMethodSignature so it can be modified. + * This is a Mono runtime internal function. + * + * \returns the new \c MonoMethodSignature structure. + */ +MonoMethodSignature* +mono_metadata_signature_dup (MonoMethodSignature *sig) +{ + return mono_metadata_signature_dup_full (NULL, sig); +} + +/* + * mono_metadata_signature_size: + * + * Return the amount of memory allocated to SIG. + */ +guint32 +mono_metadata_signature_size (MonoMethodSignature *sig) +{ + return MONO_SIZEOF_METHOD_SIGNATURE + sig->param_count * sizeof (MonoType *); +} + +/* + * mono_metadata_parse_method_signature: + * @m: metadata context + * @generic_container: generics container + * @def: the MethodDef index or 0 for Ref signatures. + * @ptr: pointer to the signature metadata representation + * @rptr: pointer updated to match the end of the decoded stream + * + * Decode a method signature stored at @ptr. + * This is a Mono runtime internal function. + * + * LOCKING: Assumes the loader lock is held. + * + * Returns: a MonoMethodSignature describing the signature. + */ +MonoMethodSignature * +mono_metadata_parse_method_signature_full (MonoImage *m, MonoGenericContainer *container, + int def, const char *ptr, const char **rptr, MonoError *error) +{ + MonoMethodSignature *method; + int i, *pattrs = NULL; + guint32 hasthis = 0, explicit_this = 0, call_convention, param_count; + guint32 gen_param_count = 0; + gboolean is_open = FALSE; + + error_init (error); + + if (*ptr & 0x10) + gen_param_count = 1; + if (*ptr & 0x20) + hasthis = 1; + if (*ptr & 0x40) + explicit_this = 1; + call_convention = *ptr & 0x0F; + ptr++; + if (gen_param_count) + gen_param_count = mono_metadata_decode_value (ptr, &ptr); + param_count = mono_metadata_decode_value (ptr, &ptr); + + if (def) + pattrs = mono_metadata_get_param_attrs (m, def, param_count + 1); /*Must be + 1 since signature's param count doesn't account for the return value */ + + method = mono_metadata_signature_alloc (m, param_count); + method->hasthis = hasthis; + method->explicit_this = explicit_this; + method->call_convention = call_convention; + method->generic_param_count = gen_param_count; + + if (call_convention != 0xa) { + method->ret = mono_metadata_parse_type_checked (m, container, pattrs ? pattrs [0] : 0, FALSE, ptr, &ptr, error); + if (!method->ret) { + mono_metadata_free_method_signature (method); + g_free (pattrs); + return NULL; + } + is_open = mono_class_is_open_constructed_type (method->ret); + } + + for (i = 0; i < method->param_count; ++i) { + if (*ptr == MONO_TYPE_SENTINEL) { + if (method->call_convention != MONO_CALL_VARARG || def) { + mono_error_set_bad_image (error, m, "Found sentinel for methoddef or no vararg"); + g_free (pattrs); + return NULL; + } + if (method->sentinelpos >= 0) { + mono_error_set_bad_image (error, m, "Found sentinel twice in the same signature."); + g_free (pattrs); + return NULL; + } + method->sentinelpos = i; + ptr++; + } + method->params [i] = mono_metadata_parse_type_checked (m, container, pattrs ? pattrs [i+1] : 0, FALSE, ptr, &ptr, error); + if (!method->params [i]) { + mono_metadata_free_method_signature (method); + g_free (pattrs); + return NULL; + } + if (!is_open) + is_open = mono_class_is_open_constructed_type (method->params [i]); + } + + /* The sentinel could be missing if the caller does not pass any additional arguments */ + if (!def && method->call_convention == MONO_CALL_VARARG && method->sentinelpos < 0) + method->sentinelpos = method->param_count; + + method->has_type_parameters = is_open; + + if (def && (method->call_convention == MONO_CALL_VARARG)) + method->sentinelpos = method->param_count; + + g_free (pattrs); + + if (rptr) + *rptr = ptr; + /* + * Add signature to a cache and increase ref count... + */ + + return method; +} + +/** + * mono_metadata_parse_method_signature: + * \param m metadata context + * \param def the \c MethodDef index or 0 for \c Ref signatures. + * \param ptr pointer to the signature metadata representation + * \param rptr pointer updated to match the end of the decoded stream + * + * Decode a method signature stored at \p ptr. + * This is a Mono runtime internal function. + * + * LOCKING: Assumes the loader lock is held. + * + * \returns a \c MonoMethodSignature describing the signature. + */ +MonoMethodSignature * +mono_metadata_parse_method_signature (MonoImage *m, int def, const char *ptr, const char **rptr) +{ + /* + * This function MUST NOT be called by runtime code as it does error handling incorrectly. + * Use mono_metadata_parse_method_signature_full instead. + * It's ok to asser on failure as we no longer use it. + */ + MonoError error; + MonoMethodSignature *ret; + ret = mono_metadata_parse_method_signature_full (m, NULL, def, ptr, rptr, &error); + g_assert (mono_error_ok (&error)); + + return ret; +} + +/** + * mono_metadata_free_method_signature: + * \param sig signature to destroy + * + * Free the memory allocated in the signature \p sig. + * This method needs to be robust and work also on partially-built + * signatures, so it does extra checks. + */ +void +mono_metadata_free_method_signature (MonoMethodSignature *sig) +{ + /* Everything is allocated from mempools */ + /* + int i; + if (sig->ret) + mono_metadata_free_type (sig->ret); + for (i = 0; i < sig->param_count; ++i) { + if (sig->params [i]) + mono_metadata_free_type (sig->params [i]); + } + */ +} + +void +mono_metadata_free_inflated_signature (MonoMethodSignature *sig) +{ + int i; + + /* Allocated in inflate_generic_signature () */ + if (sig->ret) + mono_metadata_free_type (sig->ret); + for (i = 0; i < sig->param_count; ++i) { + if (sig->params [i]) + mono_metadata_free_type (sig->params [i]); + } + g_free (sig); +} + +static gboolean +inflated_method_equal (gconstpointer a, gconstpointer b) +{ + const MonoMethodInflated *ma = (const MonoMethodInflated *)a; + const MonoMethodInflated *mb = (const MonoMethodInflated *)b; + if (ma->declaring != mb->declaring) + return FALSE; + return mono_metadata_generic_context_equal (&ma->context, &mb->context); +} + +static guint +inflated_method_hash (gconstpointer a) +{ + const MonoMethodInflated *ma = (const MonoMethodInflated *)a; + return (mono_metadata_generic_context_hash (&ma->context) ^ mono_aligned_addr_hash (ma->declaring)); +} + +static gboolean +inflated_signature_equal (gconstpointer a, gconstpointer b) +{ + const MonoInflatedMethodSignature *sig1 = (const MonoInflatedMethodSignature *)a; + const MonoInflatedMethodSignature *sig2 = (const MonoInflatedMethodSignature *)b; + + /* sig->sig is assumed to be canonized */ + if (sig1->sig != sig2->sig) + return FALSE; + /* The generic instances are canonized */ + return mono_metadata_generic_context_equal (&sig1->context, &sig2->context); +} + +static guint +inflated_signature_hash (gconstpointer a) +{ + const MonoInflatedMethodSignature *sig = (const MonoInflatedMethodSignature *)a; + + /* sig->sig is assumed to be canonized */ + return mono_metadata_generic_context_hash (&sig->context) ^ mono_aligned_addr_hash (sig->sig); +} + +/*static void +dump_ginst (MonoGenericInst *ginst) +{ + int i; + char *name; + + g_print ("Ginst: <"); + for (i = 0; i < ginst->type_argc; ++i) { + if (i != 0) + g_print (", "); + name = mono_type_get_name (ginst->type_argv [i]); + g_print ("%s", name); + g_free (name); + } + g_print (">"); +}*/ + +static gboolean type_in_image (MonoType *type, MonoImage *image); + +static gboolean +signature_in_image (MonoMethodSignature *sig, MonoImage *image) +{ + gpointer iter = NULL; + MonoType *p; + + while ((p = mono_signature_get_params (sig, &iter)) != NULL) + if (type_in_image (p, image)) + return TRUE; + + return type_in_image (mono_signature_get_return_type (sig), image); +} + +static gboolean +ginst_in_image (MonoGenericInst *ginst, MonoImage *image) +{ + int i; + + for (i = 0; i < ginst->type_argc; ++i) { + if (type_in_image (ginst->type_argv [i], image)) + return TRUE; + } + + return FALSE; +} + +static gboolean +gclass_in_image (MonoGenericClass *gclass, MonoImage *image) +{ + return gclass->container_class->image == image || + ginst_in_image (gclass->context.class_inst, image); +} + +static gboolean +type_in_image (MonoType *type, MonoImage *image) +{ +retry: + switch (type->type) { + case MONO_TYPE_GENERICINST: + return gclass_in_image (type->data.generic_class, image); + case MONO_TYPE_PTR: + type = type->data.type; + goto retry; + case MONO_TYPE_SZARRAY: + type = &type->data.klass->byval_arg; + goto retry; + case MONO_TYPE_ARRAY: + type = &type->data.array->eklass->byval_arg; + goto retry; + case MONO_TYPE_FNPTR: + return signature_in_image (type->data.method, image); + case MONO_TYPE_VAR: + case MONO_TYPE_MVAR: + 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) +{ + mono_os_mutex_lock (&image_sets_mutex); +} + +static inline void +image_sets_unlock (void) +{ + mono_os_mutex_unlock (&image_sets_mutex); +} + +static int +compare_pointers (const void *a, const void *b) +{ + return (size_t)a - (size_t)b; +} + +//1103, 1327, 1597 +#define HASH_TABLE_SIZE 1103 +static MonoImageSet *img_set_cache [HASH_TABLE_SIZE]; + +static guint32 +mix_hash (uintptr_t source) +{ + unsigned int hash = source; + + // Actual hash + hash = (((hash * 215497) >> 16) ^ ((hash * 1823231) + hash)); + + // Mix in highest bits on 64-bit systems only + if (sizeof (source) > 4) + hash = hash ^ (source >> 32); + + return hash; +} + +static guint32 +hash_images (MonoImage **images, int nimages) +{ + guint32 res = 0; + int i; + for (i = 0; i < nimages; ++i) + res += mix_hash ((size_t)images [i]); + + return res; +} + +static gboolean +compare_img_set (MonoImageSet *set, MonoImage **images, int nimages) +{ + int j, k; + + if (set->nimages != nimages) + return FALSE; + + for (j = 0; j < nimages; ++j) { + for (k = 0; k < nimages; ++k) + if (set->images [k] == images [j]) + break; // Break on match + + // If we iterated all the way through set->images, images[j] was *not* found. + if (k == nimages) + break; // Break on "image not found" + } + + // If we iterated all the way through images without breaking, all items in images were found in set->images + return j == nimages; +} + + +static MonoImageSet* +img_set_cache_get (MonoImage **images, int nimages) +{ + guint32 hash_code = hash_images (images, nimages); + int index = hash_code % HASH_TABLE_SIZE; + MonoImageSet *img = img_set_cache [index]; + if (!img || !compare_img_set (img, images, nimages)) { + UnlockedIncrement (&img_set_cache_miss); + return NULL; + } + UnlockedIncrement (&img_set_cache_hit); + return img; +} + +static void +img_set_cache_add (MonoImageSet *set) +{ + guint32 hash_code = hash_images (set->images, set->nimages); + int index = hash_code % HASH_TABLE_SIZE; + img_set_cache [index] = set; +} + +static void +img_set_cache_remove (MonoImageSet *is) +{ + guint32 hash_code = hash_images (is->images, is->nimages); + int index = hash_code % HASH_TABLE_SIZE; + if (img_set_cache [index] == is) + img_set_cache [index] = NULL; +} +/* + * get_image_set: + * + * Return a MonoImageSet representing the set of images in IMAGES. + */ +static MonoImageSet* +get_image_set (MonoImage **images, int nimages) +{ + int i, j, k; + MonoImageSet *set; + GSList *l; + + /* Common case: Image set contains corlib only. If we've seen that case before, we cached the set. */ + if (nimages == 1 && images [0] == mono_defaults.corlib && mscorlib_image_set) + return mscorlib_image_set; + + /* Happens with empty generic instances */ + // FIXME: Is corlib the correct thing to return here? If so, why? This may be an artifact of generic instances previously defaulting to allocating from corlib. + if (nimages == 0) + return mscorlib_image_set; + + set = img_set_cache_get (images, nimages); + if (set) + return set; + + image_sets_lock (); + + if (!image_sets) + image_sets = g_ptr_array_new (); + + // Before we go on, we should check to see whether a MonoImageSet with these images already exists. + // We can search the referred-by imagesets of any one of our images to do this. Arbitrarily pick one here: + if (images [0] == mono_defaults.corlib && nimages > 1) + l = images [1]->image_sets; // Prefer not to search the imagesets of corlib-- that will be a long list. + else + l = images [0]->image_sets; + + set = NULL; + while (l) // Iterate over selected list, looking for an imageset with members equal to our target one + { + set = (MonoImageSet *)l->data; + + if (set->nimages == nimages) { // Member count differs, this can't be it + // Compare all members to all members-- order might be different + for (j = 0; j < nimages; ++j) { + for (k = 0; k < nimages; ++k) + if (set->images [k] == images [j]) + break; // Break on match + + // If we iterated all the way through set->images, images[j] was *not* found. + if (k == nimages) + break; // Break on "image not found" + } + + // If we iterated all the way through images without breaking, all items in images were found in set->images + if (j == nimages) { + // Break on "found a set with equal members". + // This happens in case of a hash collision with a previously cached set. + break; + } + } + + l = l->next; + } + + // If we iterated all the way through l without breaking, the imageset does not already exist and we should create it + if (!l) { + set = g_new0 (MonoImageSet, 1); + set->nimages = nimages; + set->images = g_new0 (MonoImage*, nimages); + mono_os_mutex_init_recursive (&set->lock); + for (i = 0; i < nimages; ++i) + set->images [i] = images [i]; + set->gclass_cache = mono_conc_hashtable_new_full (mono_generic_class_hash, mono_generic_class_equal, NULL, (GDestroyNotify)free_generic_class); + set->ginst_cache = g_hash_table_new_full (mono_metadata_generic_inst_hash, mono_metadata_generic_inst_equal, NULL, (GDestroyNotify)free_generic_inst); + set->gmethod_cache = g_hash_table_new_full (inflated_method_hash, inflated_method_equal, NULL, (GDestroyNotify)free_inflated_method); + set->gsignature_cache = g_hash_table_new_full (inflated_signature_hash, inflated_signature_equal, NULL, (GDestroyNotify)free_inflated_signature); + + set->szarray_cache = g_hash_table_new_full (mono_aligned_addr_hash, NULL, NULL, NULL); + set->array_cache = g_hash_table_new_full (mono_aligned_addr_hash, NULL, NULL, NULL); + + for (i = 0; i < nimages; ++i) + set->images [i]->image_sets = g_slist_prepend (set->images [i]->image_sets, set); + + g_ptr_array_add (image_sets, set); + UnlockedIncrement (&img_set_count); /* locked by image_sets_lock () */ + } + + /* Cache the set. If there was a cache collision, the previously cached value will be replaced. */ + img_set_cache_add (set); + + if (nimages == 1 && images [0] == mono_defaults.corlib) { + mono_memory_barrier (); + mscorlib_image_set = set; + } + + image_sets_unlock (); + + return set; +} + +static void +delete_image_set (MonoImageSet *set) +{ + int i; + + mono_conc_hashtable_destroy (set->gclass_cache); + g_hash_table_destroy (set->ginst_cache); + g_hash_table_destroy (set->gmethod_cache); + g_hash_table_destroy (set->gsignature_cache); + + g_hash_table_destroy (set->szarray_cache); + g_hash_table_destroy (set->array_cache); + 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 (); + + for (i = 0; i < set->nimages; ++i) + set->images [i]->image_sets = g_slist_remove (set->images [i]->image_sets, set); + + g_ptr_array_remove (image_sets, set); + + image_sets_unlock (); + + img_set_cache_remove (set); + + if (set->mempool) + mono_mempool_destroy (set->mempool); + g_free (set->images); + mono_os_mutex_destroy (&set->lock); + g_free (set); +} + +void +mono_image_set_lock (MonoImageSet *set) +{ + mono_os_mutex_lock (&set->lock); +} + +void +mono_image_set_unlock (MonoImageSet *set) +{ + mono_os_mutex_unlock (&set->lock); +} + +gpointer +mono_image_set_alloc (MonoImageSet *set, guint size) +{ + gpointer res; + + mono_image_set_lock (set); + if (!set->mempool) + set->mempool = mono_mempool_new_size (INITIAL_IMAGE_SET_SIZE); + res = mono_mempool_alloc (set->mempool, size); + mono_image_set_unlock (set); + + return res; +} + +gpointer +mono_image_set_alloc0 (MonoImageSet *set, guint size) +{ + gpointer res; + + mono_image_set_lock (set); + if (!set->mempool) + set->mempool = mono_mempool_new_size (INITIAL_IMAGE_SET_SIZE); + res = mono_mempool_alloc0 (set->mempool, size); + mono_image_set_unlock (set); + + return res; +} + +char* +mono_image_set_strdup (MonoImageSet *set, const char *s) +{ + char *res; + + mono_image_set_lock (set); + if (!set->mempool) + set->mempool = mono_mempool_new_size (INITIAL_IMAGE_SET_SIZE); + res = mono_mempool_strdup (set->mempool, s); + mono_image_set_unlock (set); + + return res; +} + +// Get a descriptive string for a MonoImageSet +// Callers are obligated to free buffer with g_free after use +char * +mono_image_set_description (MonoImageSet *set) +{ + GString *result = g_string_new (NULL); + int img; + g_string_append (result, "["); + for (img = 0; img < set->nimages; img++) + { + if (img > 0) + g_string_append (result, ", "); + g_string_append (result, set->images[img]->name); + } + g_string_append (result, "]"); + return g_string_free (result, FALSE); +} + +/* + * Structure used by the collect_..._images functions to store the image list. + */ +typedef struct { + MonoImage *image_buf [64]; + MonoImage **images; + int nimages, images_len; +} CollectData; + +static void +collect_data_init (CollectData *data) +{ + data->images = data->image_buf; + data->images_len = 64; + data->nimages = 0; +} + +static void +collect_data_free (CollectData *data) +{ + if (data->images != data->image_buf) + g_free (data->images); +} + +static void +enlarge_data (CollectData *data) +{ + int new_len = data->images_len < 16 ? 16 : data->images_len * 2; + MonoImage **d = g_new (MonoImage *, new_len); + + // FIXME: test this + g_assert_not_reached (); + memcpy (d, data->images, data->images_len); + if (data->images != data->image_buf) + g_free (data->images); + data->images = d; + data->images_len = new_len; +} + +static inline void +add_image (MonoImage *image, CollectData *data) +{ + int i; + + /* The arrays are small, so use a linear search instead of a hash table */ + for (i = 0; i < data->nimages; ++i) + if (data->images [i] == image) + return; + + if (data->nimages == data->images_len) + enlarge_data (data); + + data->images [data->nimages ++] = image; +} + +static void +collect_type_images (MonoType *type, CollectData *data); + +static void +collect_ginst_images (MonoGenericInst *ginst, CollectData *data) +{ + int i; + + for (i = 0; i < ginst->type_argc; ++i) { + collect_type_images (ginst->type_argv [i], data); + } +} + +static void +collect_gclass_images (MonoGenericClass *gclass, CollectData *data) +{ + add_image (gclass->container_class->image, data); + if (gclass->context.class_inst) + collect_ginst_images (gclass->context.class_inst, data); +} + +static void +collect_signature_images (MonoMethodSignature *sig, CollectData *data) +{ + gpointer iter = NULL; + MonoType *p; + + collect_type_images (mono_signature_get_return_type (sig), data); + while ((p = mono_signature_get_params (sig, &iter)) != NULL) + collect_type_images (p, data); +} + +static void +collect_inflated_signature_images (MonoInflatedMethodSignature *sig, CollectData *data) +{ + collect_signature_images (sig->sig, data); + if (sig->context.class_inst) + collect_ginst_images (sig->context.class_inst, data); + if (sig->context.method_inst) + collect_ginst_images (sig->context.method_inst, data); +} + +static void +collect_method_images (MonoMethodInflated *method, CollectData *data) +{ + MonoMethod *m = method->declaring; + + add_image (method->declaring->klass->image, data); + if (method->context.class_inst) + collect_ginst_images (method->context.class_inst, data); + if (method->context.method_inst) + collect_ginst_images (method->context.method_inst, data); + /* + * Dynamic assemblies have no references, so the images they depend on can be unloaded before them. + */ + if (image_is_dynamic (m->klass->image)) + collect_signature_images (mono_method_signature (m), data); +} + +static void +collect_type_images (MonoType *type, CollectData *data) +{ +retry: + switch (type->type) { + case MONO_TYPE_GENERICINST: + collect_gclass_images (type->data.generic_class, data); + break; + case MONO_TYPE_PTR: + type = type->data.type; + goto retry; + case MONO_TYPE_SZARRAY: + type = &type->data.klass->byval_arg; + goto retry; + case MONO_TYPE_ARRAY: + type = &type->data.array->eklass->byval_arg; + goto retry; + case MONO_TYPE_FNPTR: + //return signature_in_image (type->data.method, image); + g_assert_not_reached (); + case MONO_TYPE_VAR: + case MONO_TYPE_MVAR: + { + 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: + case MONO_TYPE_VALUETYPE: + add_image (mono_class_from_mono_type (type)->image, data); + break; + default: + add_image (mono_defaults.corlib, data); + } +} + +typedef struct { + MonoImage *image; + GSList *list; +} CleanForImageUserData; + +static gboolean +steal_gclass_in_image (gpointer key, gpointer value, gpointer data) +{ + MonoGenericClass *gclass = (MonoGenericClass *)key; + CleanForImageUserData *user_data = (CleanForImageUserData *)data; + + g_assert (gclass_in_image (gclass, user_data->image)); + + user_data->list = g_slist_prepend (user_data->list, gclass); + return TRUE; +} + +static gboolean +steal_ginst_in_image (gpointer key, gpointer value, gpointer data) +{ + MonoGenericInst *ginst = (MonoGenericInst *)key; + CleanForImageUserData *user_data = (CleanForImageUserData *)data; + + // This doesn't work during corlib compilation + //g_assert (ginst_in_image (ginst, user_data->image)); + + user_data->list = g_slist_prepend (user_data->list, ginst); + return TRUE; +} + +static gboolean +inflated_method_in_image (gpointer key, gpointer value, gpointer data) +{ + MonoImage *image = (MonoImage *)data; + MonoMethodInflated *method = (MonoMethodInflated *)key; + + // FIXME: + // https://bugzilla.novell.com/show_bug.cgi?id=458168 + g_assert (method->declaring->klass->image == image || + (method->context.class_inst && ginst_in_image (method->context.class_inst, image)) || + (method->context.method_inst && ginst_in_image (method->context.method_inst, image)) || (((MonoMethod*)method)->signature && signature_in_image (mono_method_signature ((MonoMethod*)method), image))); + + return TRUE; +} + +static gboolean +inflated_signature_in_image (gpointer key, gpointer value, gpointer data) +{ + MonoImage *image = (MonoImage *)data; + MonoInflatedMethodSignature *sig = (MonoInflatedMethodSignature *)key; + + return signature_in_image (sig->sig, image) || + (sig->context.class_inst && ginst_in_image (sig->context.class_inst, image)) || + (sig->context.method_inst && ginst_in_image (sig->context.method_inst, image)); +} + +static gboolean +class_in_image (gpointer key, gpointer value, gpointer data) +{ + MonoImage *image = (MonoImage *)data; + MonoClass *klass = (MonoClass *)key; + + g_assert (type_in_image (&klass->byval_arg, image)); + + return TRUE; +} + +static void +check_gmethod (gpointer key, gpointer value, gpointer data) +{ + MonoMethodInflated *method = (MonoMethodInflated *)key; + MonoImage *image = (MonoImage *)data; + + if (method->context.class_inst) + g_assert (!ginst_in_image (method->context.class_inst, image)); + if (method->context.method_inst) + g_assert (!ginst_in_image (method->context.method_inst, image)); + if (((MonoMethod*)method)->signature) + g_assert (!signature_in_image (mono_method_signature ((MonoMethod*)method), image)); +} + +/* + * check_image_sets: + * + * Run a consistency check on the image set data structures. + */ +static G_GNUC_UNUSED void +check_image_sets (MonoImage *image) +{ + int i; + GSList *l = image->image_sets; + + if (!image_sets) + return; + + for (i = 0; i < image_sets->len; ++i) { + MonoImageSet *set = (MonoImageSet *)g_ptr_array_index (image_sets, i); + + if (!g_slist_find (l, set)) { + g_hash_table_foreach (set->gmethod_cache, check_gmethod, image); + } + } +} + +void +mono_metadata_clean_for_image (MonoImage *image) +{ + CleanForImageUserData ginst_data, gclass_data; + GSList *l, *set_list; + + //check_image_sets (image); + + /* + * The data structures could reference each other so we delete them in two phases. + * This is required because of the hashing functions in gclass/ginst_cache. + */ + ginst_data.image = gclass_data.image = image; + ginst_data.list = gclass_data.list = NULL; + + /* Collect the items to delete */ + /* delete_image_set () modifies the lists so make a copy */ + for (l = image->image_sets; l; l = l->next) { + MonoImageSet *set = (MonoImageSet *)l->data; + + mono_image_set_lock (set); + mono_conc_hashtable_foreach_steal (set->gclass_cache, steal_gclass_in_image, &gclass_data); + g_hash_table_foreach_steal (set->ginst_cache, steal_ginst_in_image, &ginst_data); + g_hash_table_foreach_remove (set->gmethod_cache, inflated_method_in_image, image); + g_hash_table_foreach_remove (set->gsignature_cache, inflated_signature_in_image, image); + + g_hash_table_foreach_steal (set->szarray_cache, class_in_image, image); + g_hash_table_foreach_steal (set->array_cache, class_in_image, image); + if (set->ptr_cache) + g_hash_table_foreach_steal (set->ptr_cache, class_in_image, image); + mono_image_set_unlock (set); + } + + /* Delete the removed items */ + for (l = ginst_data.list; l; l = l->next) + free_generic_inst ((MonoGenericInst *)l->data); + for (l = gclass_data.list; l; l = l->next) + free_generic_class ((MonoGenericClass *)l->data); + g_slist_free (ginst_data.list); + g_slist_free (gclass_data.list); + /* delete_image_set () modifies the lists so make a copy */ + set_list = g_slist_copy (image->image_sets); + for (l = set_list; l; l = l->next) { + MonoImageSet *set = (MonoImageSet *)l->data; + + delete_image_set (set); + } + g_slist_free (set_list); +} + +static void +free_inflated_method (MonoMethodInflated *imethod) +{ + MonoMethod *method = (MonoMethod*)imethod; + + if (method->signature) + mono_metadata_free_inflated_signature (method->signature); + + if (method->wrapper_type) + g_free (((MonoMethodWrapper*)method)->method_data); + + g_free (method); +} + +static void +free_generic_inst (MonoGenericInst *ginst) +{ + int i; + + /* The ginst itself is allocated from the image set mempool */ + for (i = 0; i < ginst->type_argc; ++i) + mono_metadata_free_type (ginst->type_argv [i]); +} + +static void +free_generic_class (MonoGenericClass *gclass) +{ + /* The gclass itself is allocated from the image set mempool */ + if (gclass->cached_class && gclass->cached_class->interface_id) + mono_unload_interface_id (gclass->cached_class); +} + +static void +free_inflated_signature (MonoInflatedMethodSignature *sig) +{ + mono_metadata_free_inflated_signature (sig->sig); + g_free (sig); +} + +/* + * mono_metadata_get_inflated_signature: + * + * Given an inflated signature and a generic context, return a canonical copy of the + * signature. The returned signature might be equal to SIG or it might be a cached copy. + */ +MonoMethodSignature * +mono_metadata_get_inflated_signature (MonoMethodSignature *sig, MonoGenericContext *context) +{ + MonoInflatedMethodSignature helper; + MonoInflatedMethodSignature *res; + CollectData data; + MonoImageSet *set; + + helper.sig = sig; + helper.context.class_inst = context->class_inst; + helper.context.method_inst = context->method_inst; + + collect_data_init (&data); + + collect_inflated_signature_images (&helper, &data); + + set = get_image_set (data.images, data.nimages); + + collect_data_free (&data); + + mono_image_set_lock (set); + + res = (MonoInflatedMethodSignature *)g_hash_table_lookup (set->gsignature_cache, &helper); + if (!res) { + res = g_new0 (MonoInflatedMethodSignature, 1); + res->sig = sig; + res->context.class_inst = context->class_inst; + res->context.method_inst = context->method_inst; + g_hash_table_insert (set->gsignature_cache, res, res); + } + + mono_image_set_unlock (set); + + return res->sig; +} + +MonoImageSet * +mono_metadata_get_image_set_for_type (MonoType *type) +{ + MonoImageSet *set; + CollectData image_set_data; + + collect_data_init (&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) +{ + MonoImageSet *set; + CollectData image_set_data; + + collect_data_init (&image_set_data); + collect_method_images (method, &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_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) +{ + switch (type->type) { + case MONO_TYPE_CLASS: + case MONO_TYPE_VALUETYPE: + return mono_class_is_gtd (type->data.klass); + default: + return FALSE; + } +} + +/* + * mono_metadata_get_generic_inst: + * + * Given a list of types, return a MonoGenericInst that represents that list. + * The returned MonoGenericInst has its own copy of the list of types. The list + * passed in the argument can be freed, modified or disposed of. + * + */ +MonoGenericInst * +mono_metadata_get_generic_inst (int type_argc, MonoType **type_argv) +{ + MonoGenericInst *ginst; + gboolean is_open; + int i; + int size = MONO_SIZEOF_GENERIC_INST + type_argc * sizeof (MonoType *); + + for (i = 0; i < type_argc; ++i) + if (mono_class_is_open_constructed_type (type_argv [i])) + break; + is_open = (i < type_argc); + + ginst = (MonoGenericInst *)g_alloca (size); + memset (ginst, 0, sizeof (MonoGenericInst)); + ginst->is_open = is_open; + ginst->type_argc = type_argc; + memcpy (ginst->type_argv, type_argv, type_argc * sizeof (MonoType *)); + + for (i = 0; i < type_argc; ++i) { + MonoType *t = ginst->type_argv [i]; + if (type_is_gtd (t)) { + ginst->type_argv [i] = mono_class_gtd_get_canonical_inst (t->data.klass); + } + } + + return mono_metadata_get_canonical_generic_inst (ginst); +} + + +/** + * mono_metadata_get_canonical_generic_inst: + * \param candidate an arbitrary generic instantiation + * + * \returns the canonical generic instantiation that represents the given + * candidate by identifying the image set for the candidate instantiation and + * finding the instance in the image set or adding a copy of the given instance + * to the image set. + * + * The returned MonoGenericInst has its own copy of the list of types. The list + * passed in the argument can be freed, modified or disposed of. + * + */ +MonoGenericInst * +mono_metadata_get_canonical_generic_inst (MonoGenericInst *candidate) +{ + CollectData data; + int type_argc = candidate->type_argc; + gboolean is_open = candidate->is_open; + MonoImageSet *set; + + collect_data_init (&data); + + collect_ginst_images (candidate, &data); + + set = get_image_set (data.images, data.nimages); + + collect_data_free (&data); + + mono_image_set_lock (set); + + MonoGenericInst *ginst = (MonoGenericInst *)g_hash_table_lookup (set->ginst_cache, candidate); + if (!ginst) { + int size = MONO_SIZEOF_GENERIC_INST + type_argc * sizeof (MonoType *); + ginst = (MonoGenericInst *)mono_image_set_alloc0 (set, size); +#ifndef MONO_SMALL_CONFIG + ginst->id = mono_atomic_inc_i32 (&next_generic_inst_id); +#endif + ginst->is_open = is_open; + ginst->type_argc = type_argc; + + for (int i = 0; i < type_argc; ++i) + ginst->type_argv [i] = mono_metadata_type_dup (NULL, candidate->type_argv [i]); + + g_hash_table_insert (set->ginst_cache, ginst, ginst); + } + + mono_image_set_unlock (set); + return ginst; +} + +static gboolean +mono_metadata_is_type_builder_generic_type_definition (MonoClass *container_class, MonoGenericInst *inst, gboolean is_dynamic) +{ + MonoGenericContainer *container = mono_class_get_generic_container (container_class); + + if (!is_dynamic || container_class->wastypebuilder || container->type_argc != inst->type_argc) + return FALSE; + return inst == container->context.class_inst; +} + +/* + * mono_metadata_lookup_generic_class: + * + * Returns a MonoGenericClass with the given properties. + * + */ +MonoGenericClass * +mono_metadata_lookup_generic_class (MonoClass *container_class, MonoGenericInst *inst, gboolean is_dynamic) +{ + MonoGenericClass *gclass; + MonoGenericClass helper; + gboolean is_tb_open = mono_metadata_is_type_builder_generic_type_definition (container_class, inst, is_dynamic); + MonoImageSet *set; + CollectData data; + + g_assert (mono_class_get_generic_container (container_class)->type_argc == inst->type_argc); + + memset (&helper, 0, sizeof(helper)); // act like g_new0 + helper.container_class = container_class; + helper.context.class_inst = inst; + helper.is_dynamic = is_dynamic; /* We use this in a hash lookup, which does not attempt to downcast the pointer */ + helper.is_tb_open = is_tb_open; + + collect_data_init (&data); + + collect_gclass_images (&helper, &data); + + set = get_image_set (data.images, data.nimages); + + collect_data_free (&data); + + gclass = (MonoGenericClass *)mono_conc_hashtable_lookup (set->gclass_cache, &helper); + + /* A tripwire just to keep us honest */ + g_assert (!helper.cached_class); + + if (gclass) + return gclass; + + gclass = mono_image_set_new0 (set, MonoGenericClass, 1); + if (is_dynamic) + gclass->is_dynamic = 1; + + gclass->is_tb_open = is_tb_open; + gclass->container_class = container_class; + gclass->context.class_inst = inst; + gclass->context.method_inst = NULL; + gclass->owner = set; + if (inst == mono_class_get_generic_container (container_class)->context.class_inst && !is_tb_open) + gclass->cached_class = container_class; + + mono_image_set_lock (set); + + MonoGenericClass *gclass2 = mono_conc_hashtable_insert (set->gclass_cache, gclass, gclass); + if (!gclass2) + gclass2 = gclass; + + // g_hash_table_insert (set->gclass_cache, gclass, gclass); + + mono_image_set_unlock (set); + + return gclass2; +} + +/* + * mono_metadata_inflate_generic_inst: + * + * Instantiate the generic instance @ginst with the context @context. + * Check @error for success. + * + */ +MonoGenericInst * +mono_metadata_inflate_generic_inst (MonoGenericInst *ginst, MonoGenericContext *context, MonoError *error) +{ + MonoType **type_argv; + MonoGenericInst *nginst = NULL; + int i, count = 0; + + error_init (error); + + if (!ginst->is_open) + return ginst; + + type_argv = g_new0 (MonoType*, ginst->type_argc); + + for (i = 0; i < ginst->type_argc; i++) { + type_argv [i] = mono_class_inflate_generic_type_checked (ginst->type_argv [i], context, error); + if (!mono_error_ok (error)) + goto cleanup; + ++count; + } + + nginst = mono_metadata_get_generic_inst (ginst->type_argc, type_argv); + +cleanup: + for (i = 0; i < count; i++) + mono_metadata_free_type (type_argv [i]); + g_free (type_argv); + + return nginst; +} + +MonoGenericInst * +mono_metadata_parse_generic_inst (MonoImage *m, MonoGenericContainer *container, + int count, const char *ptr, const char **rptr, MonoError *error) +{ + MonoType **type_argv; + MonoGenericInst *ginst; + int i; + + error_init (error); + type_argv = g_new0 (MonoType*, count); + + for (i = 0; i < count; i++) { + MonoType *t = mono_metadata_parse_type_checked (m, container, 0, FALSE, ptr, &ptr, error); + if (!t) { + g_free (type_argv); + return NULL; + } + type_argv [i] = t; + } + + if (rptr) + *rptr = ptr; + + ginst = mono_metadata_get_generic_inst (count, type_argv); + + g_free (type_argv); + + return ginst; +} + +static gboolean +do_mono_metadata_parse_generic_class (MonoType *type, MonoImage *m, MonoGenericContainer *container, + const char *ptr, const char **rptr, MonoError *error) +{ + MonoGenericInst *inst; + MonoClass *gklass; + MonoType *gtype; + int count; + + error_init (error); + + // XXX how about transient? + gtype = mono_metadata_parse_type_checked (m, NULL, 0, FALSE, ptr, &ptr, error); + if (gtype == NULL) + return FALSE; + + gklass = mono_class_from_mono_type (gtype); + if (!mono_class_is_gtd (gklass)) { + mono_error_set_bad_image (error, m, "Generic instance with non-generic definition"); + return FALSE; + } + + count = mono_metadata_decode_value (ptr, &ptr); + inst = mono_metadata_parse_generic_inst (m, container, count, ptr, &ptr, error); + if (inst == NULL) + return FALSE; + + if (rptr) + *rptr = ptr; + + type->data.generic_class = mono_metadata_lookup_generic_class (gklass, inst, FALSE); + return TRUE; +} + +/* + * select_container: + * @gc: The generic container to normalize + * @type: The kind of generic parameters the resulting generic-container should contain + */ + +static MonoGenericContainer * +select_container (MonoGenericContainer *gc, MonoTypeEnum type) +{ + gboolean is_var = (type == MONO_TYPE_VAR); + if (!gc) + return NULL; + + g_assert (is_var || type == MONO_TYPE_MVAR); + + if (is_var) { + if (gc->is_method || gc->parent) + /* + * The current MonoGenericContainer is a generic method -> its `parent' + * points to the containing class'es container. + */ + return gc->parent; + } + + return gc; +} + +MonoGenericContainer * +get_anonymous_container_for_image (MonoImage *image, gboolean is_mvar) +{ + MonoGenericContainer **container_pointer; + if (is_mvar) + container_pointer = &image->anonymous_generic_method_container; + else + container_pointer = &image->anonymous_generic_class_container; + MonoGenericContainer *result = *container_pointer; + + // This container has never been created; make it now. + if (!result) + { + // Note this is never deallocated anywhere-- it exists for the lifetime of the image it's allocated from + result = (MonoGenericContainer *)mono_image_alloc0 (image, sizeof (MonoGenericContainer)); + result->owner.image = image; + result->is_anonymous = TRUE; + result->is_small_param = TRUE; + result->is_method = is_mvar; + + // If another thread already made a container, use that and leak this new one. + // (Technically it would currently be safe to just assign instead of CASing.) + MonoGenericContainer *exchange = (MonoGenericContainer *)mono_atomic_cas_ptr ((volatile gpointer *)container_pointer, result, NULL); + if (exchange) + result = exchange; + } + return result; +} + +/* + * mono_metadata_parse_generic_param: + * @generic_container: Our MonoClass's or MonoMethod's MonoGenericContainer; + * see mono_metadata_parse_type_checked() for details. + * Internal routine to parse a generic type parameter. + * LOCKING: Acquires the loader lock + */ +static MonoGenericParam * +mono_metadata_parse_generic_param (MonoImage *m, MonoGenericContainer *generic_container, + MonoTypeEnum type, const char *ptr, const char **rptr, MonoError *error) +{ + int index = mono_metadata_decode_value (ptr, &ptr); + if (rptr) + *rptr = ptr; + + error_init (error); + + generic_container = select_container (generic_container, type); + if (!generic_container) { + gboolean is_mvar = FALSE; + switch (type) + { + case MONO_TYPE_VAR: + break; + case MONO_TYPE_MVAR: + is_mvar = TRUE; + break; + default: + g_error ("Cerating generic param object with invalid MonoType"); // This is not a generic param + } + + /* Create dummy MonoGenericParam */ + MonoGenericParam *param; + + param = (MonoGenericParam *)mono_image_alloc0 (m, sizeof (MonoGenericParam)); + param->num = index; + param->owner = get_anonymous_container_for_image (m, is_mvar); + + return param; + } + + if (index >= generic_container->type_argc) { + mono_error_set_bad_image (error, m, "Invalid generic %s parameter index %d, max index is %d", + generic_container->is_method ? "method" : "type", + index, generic_container->type_argc); + return NULL; + } + + //This can't return NULL + return mono_generic_container_get_param (generic_container, index); +} + +/* + * mono_metadata_get_shared_type: + * + * Return a shared instance of TYPE, if available, NULL otherwise. + * Shared MonoType instances help save memory. Their contents should not be modified + * by the caller. They do not need to be freed as their lifetime is bound by either + * the lifetime of the runtime (builtin types), or the lifetime of the MonoClass + * instance they are embedded in. If they are freed, they should be freed using + * mono_metadata_free_type () instead of g_free (). + */ +MonoType* +mono_metadata_get_shared_type (MonoType *type) +{ + MonoType *cached; + + /* No need to use locking since nobody is modifying the hash table */ + if ((cached = (MonoType *)g_hash_table_lookup (type_cache, type))) + return cached; + + switch (type->type){ + case MONO_TYPE_CLASS: + case MONO_TYPE_VALUETYPE: + if (type == &type->data.klass->byval_arg) + return type; + if (type == &type->data.klass->this_arg) + return type; + break; + default: + break; + } + + return NULL; +} + +static gboolean +compare_type_literals (MonoImage *image, int class_type, int type_type, MonoError *error) +{ + error_init (error); + + /* byval_arg.type can be zero if we're decoding a type that references a class been loading. + * See mcs/test/gtest-440. and #650936. + * FIXME This better be moved to the metadata verifier as it can catch more cases. + */ + if (!class_type) + return TRUE; + /* NET 1.1 assemblies might encode string and object in a denormalized way. + * See #675464. + */ + if (class_type == type_type) + return TRUE; + + if (type_type == MONO_TYPE_CLASS) { + if (class_type == MONO_TYPE_STRING || class_type == MONO_TYPE_OBJECT) + return TRUE; + //XXX stringify this argument + mono_error_set_bad_image (error, image, "Expected reference type but got type kind %d", class_type); + return FALSE; + } + + g_assert (type_type == MONO_TYPE_VALUETYPE); + switch (class_type) { + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_CHAR: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_CLASS: + return TRUE; + default: + //XXX stringify this argument + mono_error_set_bad_image (error, image, "Expected value type but got type kind %d", class_type); + return FALSE; + } +} + +static gboolean +verify_var_type_and_container (MonoImage *image, int var_type, MonoGenericContainer *container, MonoError *error) +{ + error_init (error); + if (var_type == MONO_TYPE_MVAR) { + if (!container->is_method) { //MVAR and a method container + mono_error_set_bad_image (error, image, "MVAR parsed in a context without a method container"); + return FALSE; + } + } else { + if (!(!container->is_method || //VAR and class container + (container->is_method && container->parent))) { //VAR and method container with parent + mono_error_set_bad_image (error, image, "VAR parsed in a context without a class container"); + return FALSE; + } + } + return TRUE; +} + +/* + * do_mono_metadata_parse_type: + * @type: MonoType to be filled in with the return value + * @m: image context + * @generic_context: generics_context + * @transient: whenever to allocate data from the heap + * @ptr: pointer to the encoded type + * @rptr: pointer where the end of the encoded type is saved + * + * Internal routine used to "fill" the contents of @type from an + * allocated pointer. This is done this way to avoid doing too + * many mini-allocations (particularly for the MonoFieldType which + * most of the time is just a MonoType, but sometimes might be augmented). + * + * This routine is used by mono_metadata_parse_type and + * mono_metadata_parse_field_type + * + * This extracts a Type as specified in Partition II (22.2.12) + * + * Returns: FALSE if the type could not be loaded + */ +static gboolean +do_mono_metadata_parse_type (MonoType *type, MonoImage *m, MonoGenericContainer *container, + gboolean transient, const char *ptr, const char **rptr, MonoError *error) +{ + error_init (error); + + type->type = (MonoTypeEnum)mono_metadata_decode_value (ptr, &ptr); + + switch (type->type){ + case MONO_TYPE_VOID: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_CHAR: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_STRING: + case MONO_TYPE_OBJECT: + case MONO_TYPE_TYPEDBYREF: + break; + case MONO_TYPE_VALUETYPE: + case MONO_TYPE_CLASS: { + guint32 token; + MonoClass *klass; + token = mono_metadata_parse_typedef_or_ref (m, ptr, &ptr); + klass = mono_class_get_checked (m, token, error); + type->data.klass = klass; + if (!klass) + return FALSE; + + if (!compare_type_literals (m, klass->byval_arg.type, type->type, error)) + return FALSE; + + break; + } + case MONO_TYPE_SZARRAY: { + MonoType *etype = mono_metadata_parse_type_checked (m, container, 0, transient, ptr, &ptr, error); + if (!etype) + return FALSE; + + type->data.klass = mono_class_from_mono_type (etype); + + if (transient) + mono_metadata_free_type (etype); + + g_assert (type->data.klass); //This was previously a check for NULL, but mcfmt should never fail. It can return a borken MonoClass, but should return at least something. + break; + } + case MONO_TYPE_PTR: { + type->data.type = mono_metadata_parse_type_checked (m, container, 0, transient, ptr, &ptr, error); + if (!type->data.type) + return FALSE; + break; + } + case MONO_TYPE_FNPTR: { + type->data.method = mono_metadata_parse_method_signature_full (m, container, 0, ptr, &ptr, error); + if (!type->data.method) + return FALSE; + break; + } + case MONO_TYPE_ARRAY: { + type->data.array = mono_metadata_parse_array_internal (m, container, transient, ptr, &ptr, error); + if (!type->data.array) + return FALSE; + break; + } + case MONO_TYPE_MVAR: + case MONO_TYPE_VAR: { + if (container && !verify_var_type_and_container (m, type->type, container, error)) + return FALSE; + + type->data.generic_param = mono_metadata_parse_generic_param (m, container, type->type, ptr, &ptr, error); + if (!type->data.generic_param) + return FALSE; + + break; + } + case MONO_TYPE_GENERICINST: { + if (!do_mono_metadata_parse_generic_class (type, m, container, ptr, &ptr, error)) + return FALSE; + break; + } + default: + mono_error_set_bad_image (error, m, "type 0x%02x not handled in do_mono_metadata_parse_type on image %s", type->type, m->name); + return FALSE; + } + + if (rptr) + *rptr = ptr; + return TRUE; +} + +/** + * mono_metadata_free_type: + * \param type type to free + * + * Free the memory allocated for type \p type which is allocated on the heap. + */ +void +mono_metadata_free_type (MonoType *type) +{ + if (type >= builtin_types && type < builtin_types + NBUILTIN_TYPES ()) + return; + + switch (type->type){ + case MONO_TYPE_OBJECT: + case MONO_TYPE_STRING: + if (!type->data.klass) + break; + /* fall through */ + case MONO_TYPE_CLASS: + case MONO_TYPE_VALUETYPE: + if (type == &type->data.klass->byval_arg || type == &type->data.klass->this_arg) + return; + break; + case MONO_TYPE_PTR: + mono_metadata_free_type (type->data.type); + break; + case MONO_TYPE_FNPTR: + mono_metadata_free_method_signature (type->data.method); + break; + case MONO_TYPE_ARRAY: + mono_metadata_free_array (type->data.array); + break; + default: + break; + } + + g_free (type); +} + +#if 0 +static void +hex_dump (const char *buffer, int base, int count) +{ + int show_header = 1; + int i; + + if (count < 0){ + count = -count; + show_header = 0; + } + + for (i = 0; i < count; i++){ + if (show_header) + if ((i % 16) == 0) + printf ("\n0x%08x: ", (unsigned char) base + i); + + printf ("%02x ", (unsigned char) (buffer [i])); + } + fflush (stdout); +} +#endif + +/** + * @ptr: Points to the beginning of the Section Data (25.3) + */ +static MonoExceptionClause* +parse_section_data (MonoImage *m, int *num_clauses, const unsigned char *ptr, MonoError *error) +{ + unsigned char sect_data_flags; + int is_fat; + guint32 sect_data_len; + MonoExceptionClause* clauses = NULL; + + error_init (error); + + while (1) { + /* align on 32-bit boundary */ + ptr = dword_align (ptr); + sect_data_flags = *ptr; + ptr++; + + is_fat = sect_data_flags & METHOD_HEADER_SECTION_FAT_FORMAT; + if (is_fat) { + sect_data_len = (ptr [2] << 16) | (ptr [1] << 8) | ptr [0]; + ptr += 3; + } else { + sect_data_len = ptr [0]; + ++ptr; + } + + if (sect_data_flags & METHOD_HEADER_SECTION_EHTABLE) { + const unsigned char *p = dword_align (ptr); + int i; + *num_clauses = is_fat ? sect_data_len / 24: sect_data_len / 12; + /* we could just store a pointer if we don't need to byteswap */ + clauses = (MonoExceptionClause *)g_malloc0 (sizeof (MonoExceptionClause) * (*num_clauses)); + for (i = 0; i < *num_clauses; ++i) { + MonoExceptionClause *ec = &clauses [i]; + guint32 tof_value; + if (is_fat) { + ec->flags = read32 (p); + ec->try_offset = read32 (p + 4); + ec->try_len = read32 (p + 8); + ec->handler_offset = read32 (p + 12); + ec->handler_len = read32 (p + 16); + tof_value = read32 (p + 20); + p += 24; + } else { + ec->flags = read16 (p); + ec->try_offset = read16 (p + 2); + ec->try_len = *(p + 4); + ec->handler_offset = read16 (p + 5); + ec->handler_len = *(p + 7); + tof_value = read32 (p + 8); + p += 12; + } + if (ec->flags == MONO_EXCEPTION_CLAUSE_FILTER) { + ec->data.filter_offset = tof_value; + } else if (ec->flags == MONO_EXCEPTION_CLAUSE_NONE) { + ec->data.catch_class = NULL; + if (tof_value) { + ec->data.catch_class = mono_class_get_checked (m, tof_value, error); + if (!is_ok (error)) { + g_free (clauses); + return NULL; + } + } + } else { + ec->data.catch_class = NULL; + } + /* g_print ("try %d: %x %04x-%04x %04x\n", i, ec->flags, ec->try_offset, ec->try_offset+ec->try_len, ec->try_len); */ + } + + } + if (sect_data_flags & METHOD_HEADER_SECTION_MORE_SECTS) + ptr += sect_data_len - 4; /* LAMESPEC: it seems the size includes the header */ + else + return clauses; + } +} + +/* + * mono_method_get_header_summary: + * @method: The method to get the header. + * @summary: Where to store the header + * + * + * Returns: TRUE if the header was properly decoded. + */ +gboolean +mono_method_get_header_summary (MonoMethod *method, MonoMethodHeaderSummary *summary) +{ + int idx; + guint32 rva; + MonoImage* img; + const char *ptr; + unsigned char flags, format; + guint16 fat_flags; + + /*Only the GMD has a pointer to the metadata.*/ + while (method->is_inflated) + method = ((MonoMethodInflated*)method)->declaring; + + summary->code_size = 0; + summary->has_clauses = FALSE; + + /*FIXME extract this into a MACRO and share it with mono_method_get_header*/ + 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; + + if (method->wrapper_type != MONO_WRAPPER_NONE || method->sre_method) { + MonoMethodHeader *header = ((MonoMethodWrapper *)method)->header; + if (!header) + return FALSE; + summary->code_size = header->code_size; + summary->has_clauses = header->num_clauses > 0; + return TRUE; + } + + + idx = mono_metadata_token_index (method->token); + img = method->klass->image; + rva = mono_metadata_decode_row_col (&img->tables [MONO_TABLE_METHOD], idx - 1, MONO_METHOD_RVA); + + /*We must run the verifier since we'll be decoding it.*/ + if (!mono_verifier_verify_method_header (img, rva, NULL)) + return FALSE; + + ptr = mono_image_rva_map (img, rva); + if (!ptr) + return FALSE; + + flags = *(const unsigned char *)ptr; + format = flags & METHOD_HEADER_FORMAT_MASK; + + switch (format) { + case METHOD_HEADER_TINY_FORMAT: + ptr++; + summary->code_size = flags >> 2; + break; + case METHOD_HEADER_FAT_FORMAT: + fat_flags = read16 (ptr); + ptr += 4; + summary->code_size = read32 (ptr); + if (fat_flags & METHOD_HEADER_MORE_SECTS) + summary->has_clauses = TRUE; + break; + default: + return FALSE; + } + return TRUE; +} + +/* + * mono_metadata_parse_mh_full: + * @m: metadata context + * @generic_context: generics context + * @ptr: pointer to the method header. + * + * Decode the method header at @ptr, including pointer to the IL code, + * info about local variables and optional exception tables. + * This is a Mono runtime internal function. + * + * LOCKING: Acquires the loader lock. + * + * Returns: a transient MonoMethodHeader allocated from the heap. + */ +MonoMethodHeader * +mono_metadata_parse_mh_full (MonoImage *m, MonoGenericContainer *container, const char *ptr, MonoError *error) +{ + MonoMethodHeader *mh = NULL; + unsigned char flags = *(const unsigned char *) ptr; + unsigned char format = flags & METHOD_HEADER_FORMAT_MASK; + guint16 fat_flags; + guint32 local_var_sig_tok, max_stack, code_size, init_locals; + const unsigned char *code; + MonoExceptionClause* clauses = NULL; + int num_clauses = 0; + MonoTableInfo *t = &m->tables [MONO_TABLE_STANDALONESIG]; + guint32 cols [MONO_STAND_ALONE_SIGNATURE_SIZE]; + + error_init (error); + + if (!ptr) { + mono_error_set_bad_image (error, m, "Method header with null pointer"); + return NULL; + } + + switch (format) { + case METHOD_HEADER_TINY_FORMAT: + mh = (MonoMethodHeader *)g_malloc0 (MONO_SIZEOF_METHOD_HEADER); + ptr++; + mh->max_stack = 8; + mh->is_transient = TRUE; + local_var_sig_tok = 0; + mh->code_size = flags >> 2; + mh->code = (unsigned char*)ptr; + return mh; + case METHOD_HEADER_FAT_FORMAT: + fat_flags = read16 (ptr); + ptr += 2; + max_stack = read16 (ptr); + ptr += 2; + code_size = read32 (ptr); + ptr += 4; + local_var_sig_tok = read32 (ptr); + ptr += 4; + + if (fat_flags & METHOD_HEADER_INIT_LOCALS) + init_locals = 1; + else + init_locals = 0; + + code = (unsigned char*)ptr; + + if (!(fat_flags & METHOD_HEADER_MORE_SECTS)) + break; + + /* + * There are more sections + */ + ptr = (char*)code + code_size; + break; + default: + mono_error_set_bad_image (error, m, "Invalid method header format %d", format); + return NULL; + } + + if (local_var_sig_tok) { + int idx = (local_var_sig_tok & 0xffffff)-1; + if (idx >= t->rows || idx < 0) { + mono_error_set_bad_image (error, m, "Invalid method header local vars signature token 0x%8x", idx); + goto fail; + } + mono_metadata_decode_row (t, idx, cols, 1); + + if (!mono_verifier_verify_standalone_signature (m, cols [MONO_STAND_ALONE_SIGNATURE], NULL)) { + mono_error_set_bad_image (error, m, "Method header locals signature 0x%8x verification failed", idx); + goto fail; + } + } + if (fat_flags & METHOD_HEADER_MORE_SECTS) { + clauses = parse_section_data (m, &num_clauses, (const unsigned char*)ptr, error); + goto_if_nok (error, fail); + } + if (local_var_sig_tok) { + const char *locals_ptr; + int len=0, i; + + locals_ptr = mono_metadata_blob_heap (m, cols [MONO_STAND_ALONE_SIGNATURE]); + mono_metadata_decode_blob_size (locals_ptr, &locals_ptr); + if (*locals_ptr != 0x07) + g_warning ("wrong signature for locals blob"); + locals_ptr++; + len = mono_metadata_decode_value (locals_ptr, &locals_ptr); + mh = (MonoMethodHeader *)g_malloc0 (MONO_SIZEOF_METHOD_HEADER + len * sizeof (MonoType*) + num_clauses * sizeof (MonoExceptionClause)); + mh->num_locals = len; + for (i = 0; i < len; ++i) { + mh->locals [i] = mono_metadata_parse_type_internal (m, container, 0, TRUE, locals_ptr, &locals_ptr, error); + goto_if_nok (error, fail); + } + } else { + mh = (MonoMethodHeader *)g_malloc0 (MONO_SIZEOF_METHOD_HEADER + num_clauses * sizeof (MonoExceptionClause)); + } + mh->code = code; + mh->code_size = code_size; + mh->max_stack = max_stack; + mh->is_transient = TRUE; + mh->init_locals = init_locals; + if (clauses) { + MonoExceptionClause* clausesp = (MonoExceptionClause*)&mh->locals [mh->num_locals]; + memcpy (clausesp, clauses, num_clauses * sizeof (MonoExceptionClause)); + g_free (clauses); + mh->clauses = clausesp; + mh->num_clauses = num_clauses; + } + return mh; +fail: + g_free (clauses); + g_free (mh); + return NULL; + +} + +/** + * mono_metadata_parse_mh: + * \param generic_context generics context + * \param ptr pointer to the method header. + * + * Decode the method header at \p ptr, including pointer to the IL code, + * info about local variables and optional exception tables. + * + * \returns a transient \c MonoMethodHeader allocated from the heap. + */ +MonoMethodHeader * +mono_metadata_parse_mh (MonoImage *m, const char *ptr) +{ + MonoError error; + MonoMethodHeader *header = mono_metadata_parse_mh_full (m, NULL, ptr, &error); + mono_error_cleanup (&error); + return header; +} + +/** + * mono_metadata_free_mh: + * \param mh a method header + * + * Free the memory allocated for the method header. + */ +void +mono_metadata_free_mh (MonoMethodHeader *mh) +{ + int i; + + /* If it is not transient it means it's part of a wrapper method, + * or a SRE-generated method, so the lifetime in that case is + * dictated by the method's own lifetime + */ + if (mh->is_transient) { + for (i = 0; i < mh->num_locals; ++i) + mono_metadata_free_type (mh->locals [i]); + g_free (mh); + } +} + +/** + * mono_method_header_get_code: + * \param header a \c MonoMethodHeader pointer + * \param code_size memory location for returning the code size + * \param max_stack memory location for returning the max stack + * + * Method header accessor to retreive info about the IL code properties: + * a pointer to the IL code itself, the size of the code and the max number + * of stack slots used by the code. + * + * \returns pointer to the IL code represented by the method header. + */ +const unsigned char* +mono_method_header_get_code (MonoMethodHeader *header, guint32* code_size, guint32* max_stack) +{ + if (code_size) + *code_size = header->code_size; + if (max_stack) + *max_stack = header->max_stack; + return header->code; +} + +/** + * mono_method_header_get_locals: + * \param header a \c MonoMethodHeader pointer + * \param num_locals memory location for returning the number of local variables + * \param init_locals memory location for returning the init_locals flag + * + * Method header accessor to retreive info about the local variables: + * an array of local types, the number of locals and whether the locals + * are supposed to be initialized to 0 on method entry + * + * \returns pointer to an array of types of the local variables + */ +MonoType** +mono_method_header_get_locals (MonoMethodHeader *header, guint32* num_locals, gboolean *init_locals) +{ + if (num_locals) + *num_locals = header->num_locals; + if (init_locals) + *init_locals = header->init_locals; + return header->locals; +} + +/* + * mono_method_header_get_num_clauses: + * @header: a MonoMethodHeader pointer + * + * Method header accessor to retreive the number of exception clauses. + * + * Returns: the number of exception clauses present + */ +int +mono_method_header_get_num_clauses (MonoMethodHeader *header) +{ + return header->num_clauses; +} + +/** + * mono_method_header_get_clauses: + * \param header a \c MonoMethodHeader pointer + * \param method \c MonoMethod the header belongs to + * \param iter pointer to a iterator + * \param clause pointer to a \c MonoExceptionClause structure which will be filled with the info + * + * Get the info about the exception clauses in the method. Set \c *iter to NULL to + * initiate the iteration, then call the method repeatedly until it returns FALSE. + * At each iteration, the structure pointed to by clause if filled with the + * exception clause information. + * + * \returns TRUE if clause was filled with info, FALSE if there are no more exception + * clauses. + */ +int +mono_method_header_get_clauses (MonoMethodHeader *header, MonoMethod *method, gpointer *iter, MonoExceptionClause *clause) +{ + MonoExceptionClause *sc; + /* later we'll be able to use this interface to parse the clause info on demand, + * without allocating anything. + */ + if (!iter || !header->num_clauses) + return FALSE; + if (!*iter) { + *iter = sc = header->clauses; + *clause = *sc; + return TRUE; + } + sc = (MonoExceptionClause *)*iter; + sc++; + if (sc < header->clauses + header->num_clauses) { + *iter = sc; + *clause = *sc; + return TRUE; + } + return FALSE; +} + +/** + * mono_metadata_parse_field_type: + * \param m metadata context to extract information from + * \param ptr pointer to the field signature + * \param rptr pointer updated to match the end of the decoded stream + * + * Parses the field signature, and returns the type information for it. + * + * \returns The \c MonoType that was extracted from \p ptr . + */ +MonoType * +mono_metadata_parse_field_type (MonoImage *m, short field_flags, const char *ptr, const char **rptr) +{ + MonoError error; + MonoType * type = mono_metadata_parse_type_internal (m, NULL, field_flags, FALSE, ptr, rptr, &error); + mono_error_cleanup (&error); + return type; +} + +/** + * mono_metadata_parse_param: + * \param m metadata context to extract information from + * \param ptr pointer to the param signature + * \param rptr pointer updated to match the end of the decoded stream + * + * Parses the param signature, and returns the type information for it. + * + * \returns The \c MonoType that was extracted from \p ptr . + */ +MonoType * +mono_metadata_parse_param (MonoImage *m, const char *ptr, const char **rptr) +{ + MonoError error; + MonoType * type = mono_metadata_parse_type_internal (m, NULL, 0, FALSE, ptr, rptr, &error); + mono_error_cleanup (&error); + return type; +} + +/** + * mono_metadata_token_from_dor: + * \param dor_token A \c TypeDefOrRef coded index + * + * \p dor_token is a \c TypeDefOrRef coded index: it contains either + * a \c TypeDef, \c TypeRef or \c TypeSpec in the lower bits, and the upper + * bits contain an index into the table. + * + * \returns an expanded token + */ +guint32 +mono_metadata_token_from_dor (guint32 dor_index) +{ + guint32 table, idx; + + table = dor_index & 0x03; + idx = dor_index >> 2; + + switch (table){ + case 0: /* TypeDef */ + return MONO_TOKEN_TYPE_DEF | idx; + case 1: /* TypeRef */ + return MONO_TOKEN_TYPE_REF | idx; + case 2: /* TypeSpec */ + return MONO_TOKEN_TYPE_SPEC | idx; + default: + g_assert_not_reached (); + } + + return 0; +} + +/* + * We use this to pass context information to the row locator + */ +typedef struct { + int idx; /* The index that we are trying to locate */ + int col_idx; /* The index in the row where idx may be stored */ + MonoTableInfo *t; /* pointer to the table */ + guint32 result; +} locator_t; + +/* + * How the row locator works. + * + * Table A + * ___|___ + * ___|___ Table B + * ___|___------> _______ + * ___|___ _______ + * + * A column in the rows of table A references an index in table B. + * For example A may be the TYPEDEF table and B the METHODDEF table. + * + * Given an index in table B we want to get the row in table A + * where the column n references our index in B. + * + * In the locator_t structure: + * t is table A + * col_idx is the column number + * index is the index in table B + * result will be the index in table A + * + * Examples: + * Table A Table B column (in table A) + * TYPEDEF METHODDEF MONO_TYPEDEF_METHOD_LIST + * TYPEDEF FIELD MONO_TYPEDEF_FIELD_LIST + * PROPERTYMAP PROPERTY MONO_PROPERTY_MAP_PROPERTY_LIST + * INTERFIMPL TYPEDEF MONO_INTERFACEIMPL_CLASS + * METHODSEM PROPERTY ASSOCIATION (encoded index) + * + * Note that we still don't support encoded indexes. + * + */ +static int +typedef_locator (const void *a, const void *b) +{ + locator_t *loc = (locator_t *) a; + const char *bb = (const char *) b; + int typedef_index = (bb - loc->t->base) / loc->t->row_size; + guint32 col, col_next; + + col = mono_metadata_decode_row_col (loc->t, typedef_index, loc->col_idx); + + if (loc->idx < col) + return -1; + + /* + * Need to check that the next row is valid. + */ + if (typedef_index + 1 < loc->t->rows) { + col_next = mono_metadata_decode_row_col (loc->t, typedef_index + 1, loc->col_idx); + if (loc->idx >= col_next) + return 1; + + if (col == col_next) + return 1; + } + + loc->result = typedef_index; + + return 0; +} + +static int +table_locator (const void *a, const void *b) +{ + locator_t *loc = (locator_t *) a; + const char *bb = (const char *) b; + guint32 table_index = (bb - loc->t->base) / loc->t->row_size; + guint32 col; + + col = mono_metadata_decode_row_col (loc->t, table_index, loc->col_idx); + + if (loc->idx == col) { + loc->result = table_index; + return 0; + } + if (loc->idx < col) + return -1; + else + return 1; +} + +static int +declsec_locator (const void *a, const void *b) +{ + locator_t *loc = (locator_t *) a; + const char *bb = (const char *) b; + guint32 table_index = (bb - loc->t->base) / loc->t->row_size; + guint32 col; + + col = mono_metadata_decode_row_col (loc->t, table_index, loc->col_idx); + + if (loc->idx == col) { + loc->result = table_index; + return 0; + } + if (loc->idx < col) + return -1; + else + return 1; +} + +/** + * search_ptr_table: + * + * Return the 1-based row index in TABLE, which must be one of the *Ptr tables, + * which contains IDX. + */ +static guint32 +search_ptr_table (MonoImage *image, int table, int idx) +{ + MonoTableInfo *ptrdef = &image->tables [table]; + int i; + + /* Use a linear search to find our index in the table */ + for (i = 0; i < ptrdef->rows; i ++) + /* All the Ptr tables have the same structure */ + if (mono_metadata_decode_row_col (ptrdef, i, 0) == idx) + break; + + if (i < ptrdef->rows) + return i + 1; + else + return idx; +} + +/** + * mono_metadata_typedef_from_field: + * \param meta metadata context + * \param index FieldDef token + * + * \returns the 1-based index into the \c TypeDef table of the type that + * declared the field described by \p index, or 0 if not found. + */ +guint32 +mono_metadata_typedef_from_field (MonoImage *meta, guint32 index) +{ + MonoTableInfo *tdef = &meta->tables [MONO_TABLE_TYPEDEF]; + locator_t loc; + + if (!tdef->base) + return 0; + + loc.idx = mono_metadata_token_index (index); + loc.col_idx = MONO_TYPEDEF_FIELD_LIST; + loc.t = tdef; + + if (meta->uncompressed_metadata) + loc.idx = search_ptr_table (meta, MONO_TABLE_FIELD_POINTER, loc.idx); + + if (!mono_binary_search (&loc, tdef->base, tdef->rows, tdef->row_size, typedef_locator)) + return 0; + + /* loc_result is 0..1, needs to be mapped to table index (that is +1) */ + return loc.result + 1; +} + +/** + * mono_metadata_typedef_from_method: + * \param meta metadata context + * \param index \c MethodDef token + * \returns the 1-based index into the \c TypeDef table of the type that + * declared the method described by \p index. 0 if not found. + */ +guint32 +mono_metadata_typedef_from_method (MonoImage *meta, guint32 index) +{ + MonoTableInfo *tdef = &meta->tables [MONO_TABLE_TYPEDEF]; + locator_t loc; + + if (!tdef->base) + return 0; + + loc.idx = mono_metadata_token_index (index); + loc.col_idx = MONO_TYPEDEF_METHOD_LIST; + loc.t = tdef; + + if (meta->uncompressed_metadata) + loc.idx = search_ptr_table (meta, MONO_TABLE_METHOD_POINTER, loc.idx); + + if (!mono_binary_search (&loc, tdef->base, tdef->rows, tdef->row_size, typedef_locator)) + return 0; + + /* loc_result is 0..1, needs to be mapped to table index (that is +1) */ + return loc.result + 1; +} + +/* + * mono_metadata_interfaces_from_typedef_full: + * @meta: metadata context + * @index: typedef token + * @interfaces: Out parameter used to store the interface array + * @count: Out parameter used to store the number of interfaces + * @heap_alloc_result: if TRUE the result array will be g_malloc'd + * @context: The generic context + * + * The array of interfaces that the @index typedef token implements is returned in + * @interfaces. The number of elements in the array is returned in @count. + * + + * Returns: TRUE on success, FALSE on failure. + */ +gboolean +mono_metadata_interfaces_from_typedef_full (MonoImage *meta, guint32 index, MonoClass ***interfaces, guint *count, gboolean heap_alloc_result, MonoGenericContext *context, MonoError *error) +{ + MonoTableInfo *tdef = &meta->tables [MONO_TABLE_INTERFACEIMPL]; + locator_t loc; + guint32 start, pos; + guint32 cols [MONO_INTERFACEIMPL_SIZE]; + MonoClass **result; + + *interfaces = NULL; + *count = 0; + + error_init (error); + + if (!tdef->base) + return TRUE; + + loc.idx = mono_metadata_token_index (index); + loc.col_idx = MONO_INTERFACEIMPL_CLASS; + loc.t = tdef; + + if (!mono_binary_search (&loc, tdef->base, tdef->rows, tdef->row_size, table_locator)) + return TRUE; + + start = loc.result; + /* + * We may end up in the middle of the rows... + */ + while (start > 0) { + if (loc.idx == mono_metadata_decode_row_col (tdef, start - 1, MONO_INTERFACEIMPL_CLASS)) + start--; + else + break; + } + pos = start; + while (pos < tdef->rows) { + mono_metadata_decode_row (tdef, pos, cols, MONO_INTERFACEIMPL_SIZE); + if (cols [MONO_INTERFACEIMPL_CLASS] != loc.idx) + break; + ++pos; + } + + if (heap_alloc_result) + result = g_new0 (MonoClass*, pos - start); + else + result = (MonoClass **)mono_image_alloc0 (meta, sizeof (MonoClass*) * (pos - start)); + + pos = start; + while (pos < tdef->rows) { + MonoClass *iface; + + mono_metadata_decode_row (tdef, pos, cols, MONO_INTERFACEIMPL_SIZE); + if (cols [MONO_INTERFACEIMPL_CLASS] != loc.idx) + break; + iface = mono_class_get_and_inflate_typespec_checked ( + meta, mono_metadata_token_from_dor (cols [MONO_INTERFACEIMPL_INTERFACE]), context, error); + if (iface == NULL) + return FALSE; + result [pos - start] = iface; + ++pos; + } + *count = pos - start; + *interfaces = result; + return TRUE; +} + +/** + * mono_metadata_interfaces_from_typedef: + * \param meta metadata context + * \param index typedef token + * \param count Out parameter used to store the number of interfaces + * + * The array of interfaces that the \p index typedef token implements is returned in + * \p interfaces. The number of elements in the array is returned in \p count. The returned + * array is allocated with \c g_malloc and the caller must free it. + * + * LOCKING: Acquires the loader lock . + * + * \returns the interface array on success, NULL on failure. + */ +MonoClass** +mono_metadata_interfaces_from_typedef (MonoImage *meta, guint32 index, guint *count) +{ + MonoError error; + MonoClass **interfaces = NULL; + gboolean rv; + + rv = mono_metadata_interfaces_from_typedef_full (meta, index, &interfaces, count, TRUE, NULL, &error); + g_assert (mono_error_ok (&error)); /* FIXME dont swallow the error */ + if (rv) + return interfaces; + else + return NULL; +} + +/** + * mono_metadata_nested_in_typedef: + * \param meta metadata context + * \param index typedef token + * \returns the 1-based index into the TypeDef table of the type + * where the type described by \p index is nested. + * Returns 0 if \p index describes a non-nested type. + */ +guint32 +mono_metadata_nested_in_typedef (MonoImage *meta, guint32 index) +{ + MonoTableInfo *tdef = &meta->tables [MONO_TABLE_NESTEDCLASS]; + locator_t loc; + + if (!tdef->base) + return 0; + + loc.idx = mono_metadata_token_index (index); + loc.col_idx = MONO_NESTED_CLASS_NESTED; + loc.t = tdef; + + if (!mono_binary_search (&loc, tdef->base, tdef->rows, tdef->row_size, table_locator)) + return 0; + + /* loc_result is 0..1, needs to be mapped to table index (that is +1) */ + return mono_metadata_decode_row_col (tdef, loc.result, MONO_NESTED_CLASS_ENCLOSING) | MONO_TOKEN_TYPE_DEF; +} + +/** + * mono_metadata_nesting_typedef: + * \param meta metadata context + * \param index typedef token + * \returns the 1-based index into the \c TypeDef table of the first type + * that is nested inside the type described by \p index. The search starts at + * \p start_index. Returns 0 if no such type is found. + */ +guint32 +mono_metadata_nesting_typedef (MonoImage *meta, guint32 index, guint32 start_index) +{ + MonoTableInfo *tdef = &meta->tables [MONO_TABLE_NESTEDCLASS]; + guint32 start; + guint32 class_index = mono_metadata_token_index (index); + + if (!tdef->base) + return 0; + + start = start_index; + + while (start <= tdef->rows) { + if (class_index == mono_metadata_decode_row_col (tdef, start - 1, MONO_NESTED_CLASS_ENCLOSING)) + break; + else + start++; + } + + if (start > tdef->rows) + return 0; + else + return start; +} + +/** + * mono_metadata_packing_from_typedef: + * \param meta metadata context + * \param index token representing a type + * \returns the info stored in the \c ClassLayout table for the given typedef token + * into the \p packing and \p size pointers. + * Returns 0 if the info is not found. + */ +guint32 +mono_metadata_packing_from_typedef (MonoImage *meta, guint32 index, guint32 *packing, guint32 *size) +{ + MonoTableInfo *tdef = &meta->tables [MONO_TABLE_CLASSLAYOUT]; + locator_t loc; + guint32 cols [MONO_CLASS_LAYOUT_SIZE]; + + if (!tdef->base) + return 0; + + loc.idx = mono_metadata_token_index (index); + loc.col_idx = MONO_CLASS_LAYOUT_PARENT; + loc.t = tdef; + + if (!mono_binary_search (&loc, tdef->base, tdef->rows, tdef->row_size, table_locator)) + return 0; + + mono_metadata_decode_row (tdef, loc.result, cols, MONO_CLASS_LAYOUT_SIZE); + if (packing) + *packing = cols [MONO_CLASS_LAYOUT_PACKING_SIZE]; + if (size) + *size = cols [MONO_CLASS_LAYOUT_CLASS_SIZE]; + + /* loc_result is 0..1, needs to be mapped to table index (that is +1) */ + return loc.result + 1; +} + +/** + * mono_metadata_custom_attrs_from_index: + * \param meta metadata context + * \param index token representing the parent + * \returns: the 1-based index into the \c CustomAttribute table of the first + * attribute which belongs to the metadata object described by \p index. + * Returns 0 if no such attribute is found. + */ +guint32 +mono_metadata_custom_attrs_from_index (MonoImage *meta, guint32 index) +{ + MonoTableInfo *tdef = &meta->tables [MONO_TABLE_CUSTOMATTRIBUTE]; + locator_t loc; + + if (!tdef->base) + return 0; + + loc.idx = index; + loc.col_idx = MONO_CUSTOM_ATTR_PARENT; + loc.t = tdef; + + /* FIXME: Index translation */ + + if (!mono_binary_search (&loc, tdef->base, tdef->rows, tdef->row_size, table_locator)) + return 0; + + /* Find the first entry by searching backwards */ + while ((loc.result > 0) && (mono_metadata_decode_row_col (tdef, loc.result - 1, MONO_CUSTOM_ATTR_PARENT) == index)) + loc.result --; + + /* loc_result is 0..1, needs to be mapped to table index (that is +1) */ + return loc.result + 1; +} + +/** + * mono_metadata_declsec_from_index: + * \param meta metadata context + * \param index token representing the parent + * \returns the 0-based index into the \c DeclarativeSecurity table of the first + * attribute which belongs to the metadata object described by \p index. + * Returns \c -1 if no such attribute is found. + */ +guint32 +mono_metadata_declsec_from_index (MonoImage *meta, guint32 index) +{ + MonoTableInfo *tdef = &meta->tables [MONO_TABLE_DECLSECURITY]; + locator_t loc; + + if (!tdef->base) + return -1; + + loc.idx = index; + loc.col_idx = MONO_DECL_SECURITY_PARENT; + loc.t = tdef; + + if (!mono_binary_search (&loc, tdef->base, tdef->rows, tdef->row_size, declsec_locator)) + return -1; + + /* Find the first entry by searching backwards */ + while ((loc.result > 0) && (mono_metadata_decode_row_col (tdef, loc.result - 1, MONO_DECL_SECURITY_PARENT) == index)) + loc.result --; + + return loc.result; +} + +/* + * mono_metadata_localscope_from_methoddef: + * @meta: metadata context + * @index: methoddef index + * + * Returns: the 1-based index into the LocalScope table of the first + * scope which belongs to the method described by @index. + * Returns 0 if no such row is found. + */ +guint32 +mono_metadata_localscope_from_methoddef (MonoImage *meta, guint32 index) +{ + MonoTableInfo *tdef = &meta->tables [MONO_TABLE_LOCALSCOPE]; + locator_t loc; + + if (!tdef->base) + return 0; + + loc.idx = index; + loc.col_idx = MONO_LOCALSCOPE_METHOD; + loc.t = tdef; + + if (!mono_binary_search (&loc, tdef->base, tdef->rows, tdef->row_size, table_locator)) + return 0; + + /* Find the first entry by searching backwards */ + while ((loc.result > 0) && (mono_metadata_decode_row_col (tdef, loc.result - 1, MONO_LOCALSCOPE_METHOD) == index)) + loc.result --; + + return loc.result + 1; +} + +#ifdef DEBUG +static void +mono_backtrace (int limit) +{ +#ifndef _MSC_VER + void *array[limit]; + char **names; + int i; + backtrace (array, limit); + names = backtrace_symbols (array, limit); + for (i =0; i < limit; ++i) { + g_print ("\t%s\n", names [i]); + } + g_free (names); +#endif +} +#endif + +static int i8_align; + +/* + * mono_type_set_alignment: + * + * Set the alignment used by runtime to layout fields etc. of type TYPE to ALIGN. + * This should only be used in AOT mode since the resulting layout will not match the + * host abi layout. + */ +void +mono_type_set_alignment (MonoTypeEnum type, int align) +{ + /* Support only a few types whose alignment is abi dependent */ + switch (type) { + case MONO_TYPE_I8: + i8_align = align; + break; + default: + g_assert_not_reached (); + break; + } +} + +/** + * mono_type_size: + * \param t the type to return the size of + * \returns The number of bytes required to hold an instance of this + * type in memory + */ +int +mono_type_size (MonoType *t, int *align) +{ + MonoTypeEnum simple_type; + + if (!t) { + *align = 1; + return 0; + } + if (t->byref) { + *align = MONO_ABI_ALIGNOF (gpointer); + return MONO_ABI_SIZEOF (gpointer); + } + + simple_type = t->type; + again: + switch (simple_type) { + case MONO_TYPE_VOID: + *align = 1; + return 0; + case MONO_TYPE_BOOLEAN: + *align = MONO_ABI_ALIGNOF (gint8); + return 1; + case MONO_TYPE_I1: + case MONO_TYPE_U1: + *align = MONO_ABI_ALIGNOF (gint8); + return 1; + case MONO_TYPE_CHAR: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + *align = MONO_ABI_ALIGNOF (gint16); + return 2; + case MONO_TYPE_I4: + case MONO_TYPE_U4: + *align = MONO_ABI_ALIGNOF (gint32); + return 4; + case MONO_TYPE_R4: + *align = MONO_ABI_ALIGNOF (float); + return 4; + case MONO_TYPE_I8: + case MONO_TYPE_U8: + *align = MONO_ABI_ALIGNOF (gint64); + return 8; + case MONO_TYPE_R8: + *align = MONO_ABI_ALIGNOF (double); + return 8; + case MONO_TYPE_I: + case MONO_TYPE_U: + *align = MONO_ABI_ALIGNOF (gpointer); + return MONO_ABI_SIZEOF (gpointer); + case MONO_TYPE_VALUETYPE: { + if (t->data.klass->enumtype) + return mono_type_size (mono_class_enum_basetype (t->data.klass), align); + else + return mono_class_value_size (t->data.klass, (guint32*)align); + } + case MONO_TYPE_STRING: + case MONO_TYPE_OBJECT: + case MONO_TYPE_CLASS: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_PTR: + case MONO_TYPE_FNPTR: + case MONO_TYPE_ARRAY: + *align = MONO_ABI_ALIGNOF (gpointer); + return MONO_ABI_SIZEOF (gpointer); + case MONO_TYPE_TYPEDBYREF: + return mono_class_value_size (mono_defaults.typed_reference_class, (guint32*)align); + case MONO_TYPE_GENERICINST: { + MonoGenericClass *gclass = t->data.generic_class; + MonoClass *container_class = gclass->container_class; + + // g_assert (!gclass->inst->is_open); + + if (container_class->valuetype) { + if (container_class->enumtype) + return mono_type_size (mono_class_enum_basetype (container_class), align); + else + return mono_class_value_size (mono_class_from_mono_type (t), (guint32*)align); + } else { + *align = MONO_ABI_ALIGNOF (gpointer); + return MONO_ABI_SIZEOF (gpointer); + } + } + case MONO_TYPE_VAR: + case MONO_TYPE_MVAR: + if (!t->data.generic_param->gshared_constraint || t->data.generic_param->gshared_constraint->type == MONO_TYPE_VALUETYPE) { + *align = MONO_ABI_ALIGNOF (gpointer); + return MONO_ABI_SIZEOF (gpointer); + } else { + /* The gparam can only match types given by gshared_constraint */ + return mono_type_size (t->data.generic_param->gshared_constraint, align); + goto again; + } + default: + g_error ("mono_type_size: type 0x%02x unknown", t->type); + } + return 0; +} + +/** + * mono_type_stack_size: + * \param t the type to return the size it uses on the stack + * \returns The number of bytes required to hold an instance of this + * type on the runtime stack + */ +int +mono_type_stack_size (MonoType *t, int *align) +{ + return mono_type_stack_size_internal (t, align, FALSE); +} + +int +mono_type_stack_size_internal (MonoType *t, int *align, gboolean allow_open) +{ + int tmp; + MonoTypeEnum simple_type; +#if SIZEOF_VOID_P == SIZEOF_REGISTER + int stack_slot_size = MONO_ABI_SIZEOF (gpointer); + int stack_slot_align = MONO_ABI_ALIGNOF (gpointer); +#elif SIZEOF_VOID_P < SIZEOF_REGISTER + int stack_slot_size = SIZEOF_REGISTER; + int stack_slot_align = SIZEOF_REGISTER; +#endif + + g_assert (t != NULL); + + if (!align) + align = &tmp; + + if (t->byref) { + *align = stack_slot_align; + return stack_slot_size; + } + + simple_type = t->type; + switch (simple_type) { + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_CHAR: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_STRING: + case MONO_TYPE_OBJECT: + case MONO_TYPE_CLASS: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_PTR: + case MONO_TYPE_FNPTR: + case MONO_TYPE_ARRAY: + *align = stack_slot_align; + return stack_slot_size; + case MONO_TYPE_VAR: + case MONO_TYPE_MVAR: + g_assert (allow_open); + if (!t->data.generic_param->gshared_constraint || t->data.generic_param->gshared_constraint->type == MONO_TYPE_VALUETYPE) { + *align = stack_slot_align; + return stack_slot_size; + } else { + /* The gparam can only match types given by gshared_constraint */ + return mono_type_stack_size_internal (t->data.generic_param->gshared_constraint, align, allow_open); + } + case MONO_TYPE_TYPEDBYREF: + *align = stack_slot_align; + return stack_slot_size * 3; + case MONO_TYPE_R4: + *align = MONO_ABI_ALIGNOF (float); + return sizeof (float); + case MONO_TYPE_I8: + case MONO_TYPE_U8: + *align = MONO_ABI_ALIGNOF (gint64); + return sizeof (gint64); + case MONO_TYPE_R8: + *align = MONO_ABI_ALIGNOF (double); + return sizeof (double); + case MONO_TYPE_VALUETYPE: { + guint32 size; + + if (t->data.klass->enumtype) + return mono_type_stack_size_internal (mono_class_enum_basetype (t->data.klass), align, allow_open); + else { + size = mono_class_value_size (t->data.klass, (guint32*)align); + + *align = *align + stack_slot_align - 1; + *align &= ~(stack_slot_align - 1); + + size += stack_slot_size - 1; + size &= ~(stack_slot_size - 1); + + return size; + } + } + case MONO_TYPE_GENERICINST: { + MonoGenericClass *gclass = t->data.generic_class; + MonoClass *container_class = gclass->container_class; + + if (!allow_open) + g_assert (!gclass->context.class_inst->is_open); + + if (container_class->valuetype) { + if (container_class->enumtype) + return mono_type_stack_size_internal (mono_class_enum_basetype (container_class), align, allow_open); + else { + guint32 size = mono_class_value_size (mono_class_from_mono_type (t), (guint32*)align); + + *align = *align + stack_slot_align - 1; + *align &= ~(stack_slot_align - 1); + + size += stack_slot_size - 1; + size &= ~(stack_slot_size - 1); + + return size; + } + } else { + *align = stack_slot_align; + return stack_slot_size; + } + } + default: + g_error ("type 0x%02x unknown", t->type); + } + return 0; +} + +gboolean +mono_type_generic_inst_is_valuetype (MonoType *type) +{ + g_assert (type->type == MONO_TYPE_GENERICINST); + return type->data.generic_class->container_class->valuetype; +} + +/** + * mono_metadata_generic_class_is_valuetype: + */ +gboolean +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) +{ + MonoGenericInst *i1 = g1->context.class_inst; + MonoGenericInst *i2 = g2->context.class_inst; + + if (g1->is_dynamic != g2->is_dynamic) + return FALSE; + if (!mono_metadata_class_equal (g1->container_class, g2->container_class, signature_only)) + return FALSE; + if (!mono_generic_inst_equal_full (i1, i2, signature_only)) + return FALSE; + return g1->is_tb_open == g2->is_tb_open; +} + +static gboolean +_mono_metadata_generic_class_container_equal (const MonoGenericClass *g1, MonoClass *c2, gboolean signature_only) +{ + MonoGenericInst *i1 = g1->context.class_inst; + MonoGenericInst *i2 = mono_class_get_generic_container (c2)->context.class_inst; + + if (!mono_metadata_class_equal (g1->container_class, c2, signature_only)) + return FALSE; + if (!mono_generic_inst_equal_full (i1, i2, signature_only)) + return FALSE; + return !g1->is_tb_open; +} + +guint +mono_metadata_generic_context_hash (const MonoGenericContext *context) +{ + /* FIXME: check if this seed is good enough */ + guint hash = 0xc01dfee7; + if (context->class_inst) + hash = ((hash << 5) - hash) ^ mono_metadata_generic_inst_hash (context->class_inst); + if (context->method_inst) + hash = ((hash << 5) - hash) ^ mono_metadata_generic_inst_hash (context->method_inst); + return hash; +} + +gboolean +mono_metadata_generic_context_equal (const MonoGenericContext *g1, const MonoGenericContext *g2) +{ + return g1->class_inst == g2->class_inst && g1->method_inst == g2->method_inst; +} + +/* + * mono_metadata_str_hash: + * + * This should be used instead of g_str_hash for computing hash codes visible + * outside this module, since g_str_hash () is not guaranteed to be stable + * (its not the same in eglib for example). + */ +guint +mono_metadata_str_hash (gconstpointer v1) +{ + /* Same as g_str_hash () in glib */ + char *p = (char *) v1; + guint hash = *p; + + while (*p++) { + if (*p) + hash = (hash << 5) - hash + *p; + } + + return hash; +} + +/** + * mono_metadata_type_hash: + * \param t1 a type + * Computes a hash value for \p t1 to be used in \c GHashTable. + * The returned hash is guaranteed to be the same across executions. + */ +guint +mono_metadata_type_hash (MonoType *t1) +{ + guint hash = t1->type; + + hash |= t1->byref << 6; /* do not collide with t1->type values */ + switch (t1->type) { + case MONO_TYPE_VALUETYPE: + case MONO_TYPE_CLASS: + case MONO_TYPE_SZARRAY: { + MonoClass *klass = t1->data.klass; + /* + * Dynamic classes must not be hashed on their type since it can change + * during runtime. For example, if we hash a reference type that is + * later made into a valuetype. + * + * This is specially problematic with generic instances since they are + * inserted in a bunch of hash tables before been finished. + */ + if (image_is_dynamic (klass->image)) + return (t1->byref << 6) | mono_metadata_str_hash (klass->name); + return ((hash << 5) - hash) ^ mono_metadata_str_hash (klass->name); + } + case MONO_TYPE_PTR: + return ((hash << 5) - hash) ^ mono_metadata_type_hash (t1->data.type); + case MONO_TYPE_ARRAY: + return ((hash << 5) - hash) ^ mono_metadata_type_hash (&t1->data.array->eklass->byval_arg); + case MONO_TYPE_GENERICINST: + return ((hash << 5) - hash) ^ mono_generic_class_hash (t1->data.generic_class); + case MONO_TYPE_VAR: + case MONO_TYPE_MVAR: + return ((hash << 5) - hash) ^ mono_metadata_generic_param_hash (t1->data.generic_param); + default: + return hash; + } +} + +guint +mono_metadata_generic_param_hash (MonoGenericParam *p) +{ + guint hash; + MonoGenericParamInfo *info; + + hash = (mono_generic_param_num (p) << 2); + if (p->gshared_constraint) + hash = ((hash << 5) - hash) ^ mono_metadata_type_hash (p->gshared_constraint); + info = mono_generic_param_info (p); + /* Can't hash on the owner klass/method, since those might not be set when this is called */ + if (info) + hash = ((hash << 5) - hash) ^ info->token; + return hash; +} + +static gboolean +mono_metadata_generic_param_equal_internal (MonoGenericParam *p1, MonoGenericParam *p2, gboolean signature_only) +{ + if (p1 == p2) + return TRUE; + if (mono_generic_param_num (p1) != mono_generic_param_num (p2)) + return FALSE; + if (p1->gshared_constraint && p2->gshared_constraint) { + if (!mono_metadata_type_equal (p1->gshared_constraint, p2->gshared_constraint)) + return FALSE; + } else { + if (p1->gshared_constraint != p2->gshared_constraint) + return FALSE; + } + + /* + * We have to compare the image as well because if we didn't, + * the generic_inst_cache lookup wouldn't care about the image + * of generic params, so what could happen is that a generic + * inst with params from image A is put into the cache, then + * image B gets that generic inst from the cache, image A is + * unloaded, so the inst is deleted, but image B still retains + * a pointer to it. + */ + if (mono_generic_param_owner (p1) == mono_generic_param_owner (p2)) + return TRUE; + + /* + * If `signature_only' is true, we're comparing two (method) signatures. + * In this case, the owner of two type parameters doesn't need to match. + */ + + return signature_only; +} + +gboolean +mono_metadata_generic_param_equal (MonoGenericParam *p1, MonoGenericParam *p2) +{ + return mono_metadata_generic_param_equal_internal (p1, p2, TRUE); +} + +static gboolean +mono_metadata_class_equal (MonoClass *c1, MonoClass *c2, gboolean signature_only) +{ + if (c1 == c2) + return TRUE; + if (mono_class_is_ginst (c1) && mono_class_is_ginst (c2)) + return _mono_metadata_generic_class_equal (mono_class_get_generic_class (c1), mono_class_get_generic_class (c2), signature_only); + if (mono_class_is_ginst (c1) && mono_class_is_gtd (c2)) + return _mono_metadata_generic_class_container_equal (mono_class_get_generic_class (c1), c2, signature_only); + if (mono_class_is_gtd (c1) && mono_class_is_ginst (c2)) + return _mono_metadata_generic_class_container_equal (mono_class_get_generic_class (c2), c1, signature_only); + if ((c1->byval_arg.type == MONO_TYPE_VAR) && (c2->byval_arg.type == MONO_TYPE_VAR)) + return mono_metadata_generic_param_equal_internal ( + c1->byval_arg.data.generic_param, c2->byval_arg.data.generic_param, signature_only); + if ((c1->byval_arg.type == MONO_TYPE_MVAR) && (c2->byval_arg.type == MONO_TYPE_MVAR)) + return mono_metadata_generic_param_equal_internal ( + c1->byval_arg.data.generic_param, c2->byval_arg.data.generic_param, signature_only); + if (signature_only && + (c1->byval_arg.type == MONO_TYPE_SZARRAY) && (c2->byval_arg.type == MONO_TYPE_SZARRAY)) + return mono_metadata_class_equal (c1->byval_arg.data.klass, c2->byval_arg.data.klass, signature_only); + if (signature_only && + (c1->byval_arg.type == MONO_TYPE_ARRAY) && (c2->byval_arg.type == MONO_TYPE_ARRAY)) + return do_mono_metadata_type_equal (&c1->byval_arg, &c2->byval_arg, signature_only); + return FALSE; +} + +static gboolean +mono_metadata_fnptr_equal (MonoMethodSignature *s1, MonoMethodSignature *s2, gboolean signature_only) +{ + gpointer iter1 = 0, iter2 = 0; + + if (s1 == s2) + return TRUE; + if (s1->call_convention != s2->call_convention) + return FALSE; + if (s1->sentinelpos != s2->sentinelpos) + return FALSE; + if (s1->hasthis != s2->hasthis) + return FALSE; + if (s1->explicit_this != s2->explicit_this) + return FALSE; + if (! do_mono_metadata_type_equal (s1->ret, s2->ret, signature_only)) + return FALSE; + if (s1->param_count != s2->param_count) + return FALSE; + + while (TRUE) { + MonoType *t1 = mono_signature_get_params (s1, &iter1); + MonoType *t2 = mono_signature_get_params (s2, &iter2); + + if (t1 == NULL || t2 == NULL) + return (t1 == t2); + if (! do_mono_metadata_type_equal (t1, t2, signature_only)) + return FALSE; + } +} + +/* + * mono_metadata_type_equal: + * @t1: a type + * @t2: another type + * @signature_only: If true, treat ginsts as equal which are instantiated separately but have equal positional value + * + * Determine if @t1 and @t2 represent the same type. + * Returns: #TRUE if @t1 and @t2 are equal. + */ +static gboolean +do_mono_metadata_type_equal (MonoType *t1, MonoType *t2, gboolean signature_only) +{ + if (t1->type != t2->type || t1->byref != t2->byref) + return FALSE; + + switch (t1->type) { + case MONO_TYPE_VOID: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_CHAR: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + case MONO_TYPE_STRING: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_OBJECT: + case MONO_TYPE_TYPEDBYREF: + return TRUE; + case MONO_TYPE_VALUETYPE: + case MONO_TYPE_CLASS: + case MONO_TYPE_SZARRAY: + return mono_metadata_class_equal (t1->data.klass, t2->data.klass, signature_only); + case MONO_TYPE_PTR: + return do_mono_metadata_type_equal (t1->data.type, t2->data.type, signature_only); + case MONO_TYPE_ARRAY: + if (t1->data.array->rank != t2->data.array->rank) + return FALSE; + return mono_metadata_class_equal (t1->data.array->eklass, t2->data.array->eklass, signature_only); + case MONO_TYPE_GENERICINST: + return _mono_metadata_generic_class_equal ( + t1->data.generic_class, t2->data.generic_class, signature_only); + case MONO_TYPE_VAR: + return mono_metadata_generic_param_equal_internal ( + t1->data.generic_param, t2->data.generic_param, signature_only); + case MONO_TYPE_MVAR: + return mono_metadata_generic_param_equal_internal ( + t1->data.generic_param, t2->data.generic_param, signature_only); + case MONO_TYPE_FNPTR: + return mono_metadata_fnptr_equal (t1->data.method, t2->data.method, signature_only); + default: + g_error ("implement type compare for %0x!", t1->type); + return FALSE; + } + + return FALSE; +} + +/** + * mono_metadata_type_equal: + */ +gboolean +mono_metadata_type_equal (MonoType *t1, MonoType *t2) +{ + return do_mono_metadata_type_equal (t1, t2, FALSE); +} + +/** + * mono_metadata_type_equal_full: + * \param t1 a type + * \param t2 another type + * \param signature_only if signature only comparison should be made + * + * Determine if \p t1 and \p t2 are signature compatible if \p signature_only is TRUE, otherwise + * behaves the same way as mono_metadata_type_equal. + * The function mono_metadata_type_equal(a, b) is just a shortcut for mono_metadata_type_equal_full(a, b, FALSE). + * \returns TRUE if \p t1 and \p t2 are equal taking \p signature_only into account. + */ +gboolean +mono_metadata_type_equal_full (MonoType *t1, MonoType *t2, gboolean signature_only) +{ + return do_mono_metadata_type_equal (t1, t2, signature_only); +} + +/** + * mono_metadata_signature_equal: + * \param sig1 a signature + * \param sig2 another signature + * + * Determine if \p sig1 and \p sig2 represent the same signature, with the + * same number of arguments and the same types. + * \returns TRUE if \p sig1 and \p sig2 are equal. + */ +gboolean +mono_metadata_signature_equal (MonoMethodSignature *sig1, MonoMethodSignature *sig2) +{ + int i; + + if (sig1->hasthis != sig2->hasthis || sig1->param_count != sig2->param_count) + return FALSE; + + if (sig1->generic_param_count != sig2->generic_param_count) + return FALSE; + + /* + * We're just comparing the signatures of two methods here: + * + * If we have two generic methods `void Foo (U u)' and `void Bar (V v)', + * U and V are equal here. + * + * That's what the `signature_only' argument of do_mono_metadata_type_equal() is for. + */ + + for (i = 0; i < sig1->param_count; i++) { + MonoType *p1 = sig1->params[i]; + MonoType *p2 = sig2->params[i]; + + /* if (p1->attrs != p2->attrs) + return FALSE; + */ + if (!do_mono_metadata_type_equal (p1, p2, TRUE)) + return FALSE; + } + + if (!do_mono_metadata_type_equal (sig1->ret, sig2->ret, TRUE)) + return FALSE; + return TRUE; +} + +/** + * mono_metadata_type_dup: + * \param image image to alloc memory from + * \param original type to duplicate + * \returns copy of type allocated from the image's mempool (or from the heap, if \p image is null). + */ +MonoType * +mono_metadata_type_dup (MonoImage *image, const MonoType *o) +{ + MonoType *r = NULL; + int sizeof_o = MONO_SIZEOF_TYPE; + if (o->num_mods) + sizeof_o += o->num_mods * sizeof (MonoCustomMod); + + r = image ? (MonoType *)mono_image_alloc0 (image, sizeof_o) : (MonoType *)g_malloc (sizeof_o); + + memcpy (r, o, sizeof_o); + + if (o->type == MONO_TYPE_PTR) { + r->data.type = mono_metadata_type_dup (image, o->data.type); + } else if (o->type == MONO_TYPE_ARRAY) { + r->data.array = mono_dup_array_type (image, o->data.array); + } else if (o->type == MONO_TYPE_FNPTR) { + /*FIXME the dup'ed signature is leaked mono_metadata_free_type*/ + r->data.method = mono_metadata_signature_deep_dup (image, o->data.method); + } + return r; +} + +/** + * mono_signature_hash: + */ +guint +mono_signature_hash (MonoMethodSignature *sig) +{ + guint i, res = sig->ret->type; + + for (i = 0; i < sig->param_count; i++) + res = (res << 5) - res + mono_type_hash (sig->params[i]); + + return res; +} + +/* + * mono_metadata_encode_value: + * @value: value to encode + * @buf: buffer where to write the compressed representation + * @endbuf: pointer updated to point at the end of the encoded output + * + * Encodes the value @value in the compressed representation used + * in metadata and stores the result in @buf. @buf needs to be big + * enough to hold the data (4 bytes). + */ +void +mono_metadata_encode_value (guint32 value, char *buf, char **endbuf) +{ + char *p = buf; + + if (value < 0x80) + *p++ = value; + else if (value < 0x4000) { + p [0] = 0x80 | (value >> 8); + p [1] = value & 0xff; + p += 2; + } else { + p [0] = (value >> 24) | 0xc0; + p [1] = (value >> 16) & 0xff; + p [2] = (value >> 8) & 0xff; + p [3] = value & 0xff; + p += 4; + } + if (endbuf) + *endbuf = p; +} + +/** + * mono_metadata_field_info: + * \param meta the Image the field is defined in + * \param index the index in the field table representing the field + * \param offset a pointer to an integer where to store the offset that may have been specified for the field in a FieldLayout table + * \param rva a pointer to the RVA of the field data in the image that may have been defined in a \c FieldRVA table + * \param marshal_spec a pointer to the marshal spec that may have been defined for the field in a \c FieldMarshal table. + * + * Gather info for field \p index that may have been defined in the \c FieldLayout, + * \c FieldRVA and \c FieldMarshal tables. + * Either of \p offset, \p rva and \p marshal_spec can be NULL if you're not interested + * in the data. + */ +void +mono_metadata_field_info (MonoImage *meta, guint32 index, guint32 *offset, guint32 *rva, + MonoMarshalSpec **marshal_spec) +{ + mono_metadata_field_info_full (meta, index, offset, rva, marshal_spec, FALSE); +} + +void +mono_metadata_field_info_with_mempool (MonoImage *meta, guint32 index, guint32 *offset, guint32 *rva, + MonoMarshalSpec **marshal_spec) +{ + mono_metadata_field_info_full (meta, index, offset, rva, marshal_spec, TRUE); +} + +static void +mono_metadata_field_info_full (MonoImage *meta, guint32 index, guint32 *offset, guint32 *rva, + MonoMarshalSpec **marshal_spec, gboolean alloc_from_image) +{ + MonoTableInfo *tdef; + locator_t loc; + + loc.idx = index + 1; + if (meta->uncompressed_metadata) + loc.idx = search_ptr_table (meta, MONO_TABLE_FIELD_POINTER, loc.idx); + + if (offset) { + tdef = &meta->tables [MONO_TABLE_FIELDLAYOUT]; + + loc.col_idx = MONO_FIELD_LAYOUT_FIELD; + loc.t = tdef; + + if (tdef->base && mono_binary_search (&loc, tdef->base, tdef->rows, tdef->row_size, table_locator)) { + *offset = mono_metadata_decode_row_col (tdef, loc.result, MONO_FIELD_LAYOUT_OFFSET); + } else { + *offset = (guint32)-1; + } + } + if (rva) { + tdef = &meta->tables [MONO_TABLE_FIELDRVA]; + + loc.col_idx = MONO_FIELD_RVA_FIELD; + loc.t = tdef; + + if (tdef->base && mono_binary_search (&loc, tdef->base, tdef->rows, tdef->row_size, table_locator)) { + /* + * LAMESPEC: There is no signature, no nothing, just the raw data. + */ + *rva = mono_metadata_decode_row_col (tdef, loc.result, MONO_FIELD_RVA_RVA); + } else { + *rva = 0; + } + } + if (marshal_spec) { + const char *p; + + if ((p = mono_metadata_get_marshal_info (meta, index, TRUE))) { + *marshal_spec = mono_metadata_parse_marshal_spec_full (alloc_from_image ? meta : NULL, meta, p); + } + } + +} + +/** + * mono_metadata_get_constant_index: + * \param meta the Image the field is defined in + * \param index the token that may have a row defined in the constants table + * \param hint possible position for the row + * + * \p token must be a \c FieldDef, \c ParamDef or \c PropertyDef token. + * + * \returns the index into the \c Constants table or 0 if not found. + */ +guint32 +mono_metadata_get_constant_index (MonoImage *meta, guint32 token, guint32 hint) +{ + MonoTableInfo *tdef; + locator_t loc; + guint32 index = mono_metadata_token_index (token); + + tdef = &meta->tables [MONO_TABLE_CONSTANT]; + index <<= MONO_HASCONSTANT_BITS; + switch (mono_metadata_token_table (token)) { + case MONO_TABLE_FIELD: + index |= MONO_HASCONSTANT_FIEDDEF; + break; + case MONO_TABLE_PARAM: + index |= MONO_HASCONSTANT_PARAM; + break; + case MONO_TABLE_PROPERTY: + index |= MONO_HASCONSTANT_PROPERTY; + break; + default: + g_warning ("Not a valid token for the constant table: 0x%08x", token); + return 0; + } + loc.idx = index; + loc.col_idx = MONO_CONSTANT_PARENT; + loc.t = tdef; + + /* FIXME: Index translation */ + + if ((hint > 0) && (hint < tdef->rows) && (mono_metadata_decode_row_col (tdef, hint - 1, MONO_CONSTANT_PARENT) == index)) + return hint; + + if (tdef->base && mono_binary_search (&loc, tdef->base, tdef->rows, tdef->row_size, table_locator)) { + return loc.result + 1; + } + return 0; +} + +/** + * mono_metadata_events_from_typedef: + * \param meta metadata context + * \param index 0-based index (in the \c TypeDef table) describing a type + * \returns the 0-based index in the \c Event table for the events in the + * type. The last event that belongs to the type (plus 1) is stored + * in the \p end_idx pointer. + */ +guint32 +mono_metadata_events_from_typedef (MonoImage *meta, guint32 index, guint *end_idx) +{ + locator_t loc; + guint32 start, end; + MonoTableInfo *tdef = &meta->tables [MONO_TABLE_EVENTMAP]; + + *end_idx = 0; + + if (!tdef->base) + return 0; + + loc.t = tdef; + loc.col_idx = MONO_EVENT_MAP_PARENT; + loc.idx = index + 1; + + if (!mono_binary_search (&loc, tdef->base, tdef->rows, tdef->row_size, table_locator)) + return 0; + + start = mono_metadata_decode_row_col (tdef, loc.result, MONO_EVENT_MAP_EVENTLIST); + if (loc.result + 1 < tdef->rows) { + end = mono_metadata_decode_row_col (tdef, loc.result + 1, MONO_EVENT_MAP_EVENTLIST) - 1; + } else { + end = meta->tables [MONO_TABLE_EVENT].rows; + } + + *end_idx = end; + return start - 1; +} + +/** + * mono_metadata_methods_from_event: + * \param meta metadata context + * \param index 0-based index (in the \c Event table) describing a event + * \returns the 0-based index in the \c MethodDef table for the methods in the + * event. The last method that belongs to the event (plus 1) is stored + * in the \p end_idx pointer. + */ +guint32 +mono_metadata_methods_from_event (MonoImage *meta, guint32 index, guint *end_idx) +{ + locator_t loc; + guint start, end; + guint32 cols [MONO_METHOD_SEMA_SIZE]; + MonoTableInfo *msemt = &meta->tables [MONO_TABLE_METHODSEMANTICS]; + + *end_idx = 0; + if (!msemt->base) + return 0; + + if (meta->uncompressed_metadata) + index = search_ptr_table (meta, MONO_TABLE_EVENT_POINTER, index + 1) - 1; + + loc.t = msemt; + loc.col_idx = MONO_METHOD_SEMA_ASSOCIATION; + loc.idx = ((index + 1) << MONO_HAS_SEMANTICS_BITS) | MONO_HAS_SEMANTICS_EVENT; /* Method association coded index */ + + if (!mono_binary_search (&loc, msemt->base, msemt->rows, msemt->row_size, table_locator)) + return 0; + + start = loc.result; + /* + * We may end up in the middle of the rows... + */ + while (start > 0) { + if (loc.idx == mono_metadata_decode_row_col (msemt, start - 1, MONO_METHOD_SEMA_ASSOCIATION)) + start--; + else + break; + } + end = start + 1; + while (end < msemt->rows) { + mono_metadata_decode_row (msemt, end, cols, MONO_METHOD_SEMA_SIZE); + if (cols [MONO_METHOD_SEMA_ASSOCIATION] != loc.idx) + break; + ++end; + } + *end_idx = end; + return start; +} + +/** + * mono_metadata_properties_from_typedef: + * \param meta metadata context + * \param index 0-based index (in the \c TypeDef table) describing a type + * \returns the 0-based index in the \c Property table for the properties in the + * type. The last property that belongs to the type (plus 1) is stored + * in the \p end_idx pointer. + */ +guint32 +mono_metadata_properties_from_typedef (MonoImage *meta, guint32 index, guint *end_idx) +{ + locator_t loc; + guint32 start, end; + MonoTableInfo *tdef = &meta->tables [MONO_TABLE_PROPERTYMAP]; + + *end_idx = 0; + + if (!tdef->base) + return 0; + + loc.t = tdef; + loc.col_idx = MONO_PROPERTY_MAP_PARENT; + loc.idx = index + 1; + + if (!mono_binary_search (&loc, tdef->base, tdef->rows, tdef->row_size, table_locator)) + return 0; + + start = mono_metadata_decode_row_col (tdef, loc.result, MONO_PROPERTY_MAP_PROPERTY_LIST); + if (loc.result + 1 < tdef->rows) { + end = mono_metadata_decode_row_col (tdef, loc.result + 1, MONO_PROPERTY_MAP_PROPERTY_LIST) - 1; + } else { + end = meta->tables [MONO_TABLE_PROPERTY].rows; + } + + *end_idx = end; + return start - 1; +} + +/** + * mono_metadata_methods_from_property: + * \param meta metadata context + * \param index 0-based index (in the \c PropertyDef table) describing a property + * \returns the 0-based index in the \c MethodDef table for the methods in the + * property. The last method that belongs to the property (plus 1) is stored + * in the \p end_idx pointer. + */ +guint32 +mono_metadata_methods_from_property (MonoImage *meta, guint32 index, guint *end_idx) +{ + locator_t loc; + guint start, end; + guint32 cols [MONO_METHOD_SEMA_SIZE]; + MonoTableInfo *msemt = &meta->tables [MONO_TABLE_METHODSEMANTICS]; + + *end_idx = 0; + if (!msemt->base) + return 0; + + if (meta->uncompressed_metadata) + index = search_ptr_table (meta, MONO_TABLE_PROPERTY_POINTER, index + 1) - 1; + + loc.t = msemt; + loc.col_idx = MONO_METHOD_SEMA_ASSOCIATION; + loc.idx = ((index + 1) << MONO_HAS_SEMANTICS_BITS) | MONO_HAS_SEMANTICS_PROPERTY; /* Method association coded index */ + + if (!mono_binary_search (&loc, msemt->base, msemt->rows, msemt->row_size, table_locator)) + return 0; + + start = loc.result; + /* + * We may end up in the middle of the rows... + */ + while (start > 0) { + if (loc.idx == mono_metadata_decode_row_col (msemt, start - 1, MONO_METHOD_SEMA_ASSOCIATION)) + start--; + else + break; + } + end = start + 1; + while (end < msemt->rows) { + mono_metadata_decode_row (msemt, end, cols, MONO_METHOD_SEMA_SIZE); + if (cols [MONO_METHOD_SEMA_ASSOCIATION] != loc.idx) + break; + ++end; + } + *end_idx = end; + return start; +} + +/** + * mono_metadata_implmap_from_method: + */ +guint32 +mono_metadata_implmap_from_method (MonoImage *meta, guint32 method_idx) +{ + locator_t loc; + MonoTableInfo *tdef = &meta->tables [MONO_TABLE_IMPLMAP]; + + if (!tdef->base) + return 0; + + /* No index translation seems to be needed */ + + loc.t = tdef; + loc.col_idx = MONO_IMPLMAP_MEMBER; + loc.idx = ((method_idx + 1) << MONO_MEMBERFORWD_BITS) | MONO_MEMBERFORWD_METHODDEF; + + if (!mono_binary_search (&loc, tdef->base, tdef->rows, tdef->row_size, table_locator)) + return 0; + + return loc.result + 1; +} + +/** + * mono_type_create_from_typespec: + * \param image context where the image is created + * \param type_spec typespec token + * \deprecated use \c mono_type_create_from_typespec_checked that has proper error handling + * + * Creates a \c MonoType representing the \c TypeSpec indexed by the \p type_spec + * token. + */ +MonoType * +mono_type_create_from_typespec (MonoImage *image, guint32 type_spec) +{ + MonoError error; + MonoType *type = mono_type_create_from_typespec_checked (image, type_spec, &error); + if (!type) + g_error ("Could not create typespec %x due to %s", type_spec, mono_error_get_message (&error)); + return type; +} + +MonoType * +mono_type_create_from_typespec_checked (MonoImage *image, guint32 type_spec, MonoError *error) + +{ + guint32 idx = mono_metadata_token_index (type_spec); + MonoTableInfo *t; + guint32 cols [MONO_TYPESPEC_SIZE]; + const char *ptr; + MonoType *type, *type2; + + error_init (error); + + type = (MonoType *)mono_conc_hashtable_lookup (image->typespec_cache, GUINT_TO_POINTER (type_spec)); + if (type) + return type; + + t = &image->tables [MONO_TABLE_TYPESPEC]; + + mono_metadata_decode_row (t, idx-1, cols, MONO_TYPESPEC_SIZE); + ptr = mono_metadata_blob_heap (image, cols [MONO_TYPESPEC_SIGNATURE]); + + if (!mono_verifier_verify_typespec_signature (image, cols [MONO_TYPESPEC_SIGNATURE], type_spec, NULL)) { + mono_error_set_bad_image (error, image, "Could not verify type spec %08x.", type_spec); + return NULL; + } + + mono_metadata_decode_value (ptr, &ptr); + + type = mono_metadata_parse_type_checked (image, NULL, 0, TRUE, ptr, &ptr, error); + if (!type) + return NULL; + + type2 = mono_metadata_type_dup (image, type); + mono_metadata_free_type (type); + + mono_image_lock (image); + + /* We might leak some data in the image mempool if found */ + type = mono_conc_hashtable_insert (image->typespec_cache, GUINT_TO_POINTER (type_spec), type2); + if (!type) + type = type2; + + mono_image_unlock (image); + + return type; +} + + +static char* +mono_image_strndup (MonoImage *image, const char *data, guint len) +{ + char *res; + if (!image) + return g_strndup (data, len); + res = (char *)mono_image_alloc (image, len + 1); + memcpy (res, data, len); + res [len] = 0; + return res; +} + +/** + * mono_metadata_parse_marshal_spec: + */ +MonoMarshalSpec * +mono_metadata_parse_marshal_spec (MonoImage *image, const char *ptr) +{ + return mono_metadata_parse_marshal_spec_full (NULL, image, ptr); +} + +/* + * If IMAGE is non-null, memory will be allocated from its mempool, otherwise it will be allocated using malloc. + * PARENT_IMAGE is the image containing the marshal spec. + */ +MonoMarshalSpec * +mono_metadata_parse_marshal_spec_full (MonoImage *image, MonoImage *parent_image, const char *ptr) +{ + MonoMarshalSpec *res; + int len; + const char *start = ptr; + + /* fixme: this is incomplete, but I cant find more infos in the specs */ + + if (image) + res = (MonoMarshalSpec *)mono_image_alloc0 (image, sizeof (MonoMarshalSpec)); + else + res = g_new0 (MonoMarshalSpec, 1); + + len = mono_metadata_decode_value (ptr, &ptr); + res->native = (MonoMarshalNative)*ptr++; + + if (res->native == MONO_NATIVE_LPARRAY) { + res->data.array_data.param_num = -1; + res->data.array_data.num_elem = -1; + res->data.array_data.elem_mult = -1; + + if (ptr - start <= len) + res->data.array_data.elem_type = (MonoMarshalNative)*ptr++; + if (ptr - start <= len) + res->data.array_data.param_num = mono_metadata_decode_value (ptr, &ptr); + if (ptr - start <= len) + res->data.array_data.num_elem = mono_metadata_decode_value (ptr, &ptr); + if (ptr - start <= len) { + /* + * LAMESPEC: Older spec versions say this parameter comes before + * num_elem. Never spec versions don't talk about elem_mult at + * all, but csc still emits it, and it is used to distinguish + * between param_num being 0, and param_num being omitted. + * So if (param_num == 0) && (num_elem > 0), then + * elem_mult == 0 -> the array size is num_elem + * elem_mult == 1 -> the array size is @param_num + num_elem + */ + res->data.array_data.elem_mult = mono_metadata_decode_value (ptr, &ptr); + } + } + + if (res->native == MONO_NATIVE_BYVALTSTR) { + if (ptr - start <= len) + res->data.array_data.num_elem = mono_metadata_decode_value (ptr, &ptr); + } + + if (res->native == MONO_NATIVE_BYVALARRAY) { + if (ptr - start <= len) + res->data.array_data.num_elem = mono_metadata_decode_value (ptr, &ptr); + } + + if (res->native == MONO_NATIVE_CUSTOM) { + /* skip unused type guid */ + len = mono_metadata_decode_value (ptr, &ptr); + ptr += len; + /* skip unused native type name */ + len = mono_metadata_decode_value (ptr, &ptr); + ptr += len; + /* read custom marshaler type name */ + len = mono_metadata_decode_value (ptr, &ptr); + res->data.custom_data.custom_name = mono_image_strndup (image, ptr, len); + ptr += len; + /* read cookie string */ + len = mono_metadata_decode_value (ptr, &ptr); + res->data.custom_data.cookie = mono_image_strndup (image, ptr, len); + res->data.custom_data.image = parent_image; + } + + if (res->native == MONO_NATIVE_SAFEARRAY) { + res->data.safearray_data.elem_type = (MonoMarshalVariant)0; + res->data.safearray_data.num_elem = 0; + if (ptr - start <= len) + res->data.safearray_data.elem_type = (MonoMarshalVariant)*ptr++; + if (ptr - start <= len) + res->data.safearray_data.num_elem = *ptr++; + } + return res; +} + +/** + * mono_metadata_free_marshal_spec: + */ +void +mono_metadata_free_marshal_spec (MonoMarshalSpec *spec) +{ + if (spec->native == MONO_NATIVE_CUSTOM) { + g_free (spec->data.custom_data.custom_name); + g_free (spec->data.custom_data.cookie); + } + g_free (spec); +} + +/** + * mono_type_to_unmanaged: + * The value pointed to by \p conv will contain the kind of marshalling required for this + * particular type one of the \c MONO_MARSHAL_CONV_ enumeration values. + * \returns A \c MonoMarshalNative enumeration value (MONO_NATIVE_) value + * describing the underlying native reprensetation of the type. + */ +guint32 +mono_type_to_unmanaged (MonoType *type, MonoMarshalSpec *mspec, gboolean as_field, + gboolean unicode, MonoMarshalConv *conv) +{ + MonoMarshalConv dummy_conv; + int t = type->type; + + if (!conv) + conv = &dummy_conv; + + *conv = MONO_MARSHAL_CONV_NONE; + + if (type->byref) + return MONO_NATIVE_UINT; + +handle_enum: + switch (t) { + case MONO_TYPE_BOOLEAN: + if (mspec) { + switch (mspec->native) { + case MONO_NATIVE_VARIANTBOOL: + *conv = MONO_MARSHAL_CONV_BOOL_VARIANTBOOL; + return MONO_NATIVE_VARIANTBOOL; + case MONO_NATIVE_BOOLEAN: + *conv = MONO_MARSHAL_CONV_BOOL_I4; + return MONO_NATIVE_BOOLEAN; + case MONO_NATIVE_I1: + case MONO_NATIVE_U1: + return mspec->native; + default: + g_error ("cant marshal bool to native type %02x", mspec->native); + } + } + *conv = MONO_MARSHAL_CONV_BOOL_I4; + return MONO_NATIVE_BOOLEAN; + case MONO_TYPE_CHAR: + if (mspec) { + switch (mspec->native) { + case MONO_NATIVE_U2: + case MONO_NATIVE_U1: + return mspec->native; + default: + g_error ("cant marshal char to native type %02x", mspec->native); + } + } + return unicode ? MONO_NATIVE_U2 : MONO_NATIVE_U1; + case MONO_TYPE_I1: return MONO_NATIVE_I1; + case MONO_TYPE_U1: return MONO_NATIVE_U1; + case MONO_TYPE_I2: return MONO_NATIVE_I2; + case MONO_TYPE_U2: return MONO_NATIVE_U2; + case MONO_TYPE_I4: return MONO_NATIVE_I4; + case MONO_TYPE_U4: return MONO_NATIVE_U4; + case MONO_TYPE_I8: return MONO_NATIVE_I8; + case MONO_TYPE_U8: return MONO_NATIVE_U8; + case MONO_TYPE_R4: return MONO_NATIVE_R4; + case MONO_TYPE_R8: return MONO_NATIVE_R8; + case MONO_TYPE_STRING: + if (mspec) { + switch (mspec->native) { + case MONO_NATIVE_BSTR: + *conv = MONO_MARSHAL_CONV_STR_BSTR; + return MONO_NATIVE_BSTR; + case MONO_NATIVE_LPSTR: + *conv = MONO_MARSHAL_CONV_STR_LPSTR; + return MONO_NATIVE_LPSTR; + case MONO_NATIVE_LPWSTR: + *conv = MONO_MARSHAL_CONV_STR_LPWSTR; + return MONO_NATIVE_LPWSTR; + case MONO_NATIVE_LPTSTR: + *conv = MONO_MARSHAL_CONV_STR_LPTSTR; + return MONO_NATIVE_LPTSTR; + case MONO_NATIVE_ANSIBSTR: + *conv = MONO_MARSHAL_CONV_STR_ANSIBSTR; + return MONO_NATIVE_ANSIBSTR; + case MONO_NATIVE_TBSTR: + *conv = MONO_MARSHAL_CONV_STR_TBSTR; + return MONO_NATIVE_TBSTR; + case MONO_NATIVE_UTF8STR: + *conv = MONO_MARSHAL_CONV_STR_UTF8STR; + return MONO_NATIVE_UTF8STR; + case MONO_NATIVE_BYVALTSTR: + if (unicode) + *conv = MONO_MARSHAL_CONV_STR_BYVALWSTR; + else + *conv = MONO_MARSHAL_CONV_STR_BYVALSTR; + return MONO_NATIVE_BYVALTSTR; + default: + g_error ("Can not marshal string to native type '%02x': Invalid managed/unmanaged type combination (String fields must be paired with LPStr, LPWStr, BStr or ByValTStr).", mspec->native); + } + } + if (unicode) { + *conv = MONO_MARSHAL_CONV_STR_LPWSTR; + return MONO_NATIVE_LPWSTR; + } + else { + *conv = MONO_MARSHAL_CONV_STR_LPSTR; + return MONO_NATIVE_LPSTR; + } + case MONO_TYPE_PTR: return MONO_NATIVE_UINT; + case MONO_TYPE_VALUETYPE: /*FIXME*/ + if (type->data.klass->enumtype) { + t = mono_class_enum_basetype (type->data.klass)->type; + goto handle_enum; + } + if (type->data.klass == mono_defaults.handleref_class){ + *conv = MONO_MARSHAL_CONV_HANDLEREF; + return MONO_NATIVE_INT; + } + return MONO_NATIVE_STRUCT; + case MONO_TYPE_SZARRAY: + case MONO_TYPE_ARRAY: + if (mspec) { + switch (mspec->native) { + case MONO_NATIVE_BYVALARRAY: + if ((type->data.klass->element_class == mono_defaults.char_class) && !unicode) + *conv = MONO_MARSHAL_CONV_ARRAY_BYVALCHARARRAY; + else + *conv = MONO_MARSHAL_CONV_ARRAY_BYVALARRAY; + return MONO_NATIVE_BYVALARRAY; + case MONO_NATIVE_SAFEARRAY: + *conv = MONO_MARSHAL_CONV_ARRAY_SAVEARRAY; + return MONO_NATIVE_SAFEARRAY; + case MONO_NATIVE_LPARRAY: + *conv = MONO_MARSHAL_CONV_ARRAY_LPARRAY; + return MONO_NATIVE_LPARRAY; + default: + g_error ("cant marshal array as native type %02x", mspec->native); + } + } + + *conv = MONO_MARSHAL_CONV_ARRAY_LPARRAY; + return MONO_NATIVE_LPARRAY; + case MONO_TYPE_I: return MONO_NATIVE_INT; + case MONO_TYPE_U: return MONO_NATIVE_UINT; + case MONO_TYPE_CLASS: + case MONO_TYPE_OBJECT: { + /* FIXME : we need to handle ArrayList and StringBuilder here, probably */ + if (mspec) { + switch (mspec->native) { + case MONO_NATIVE_STRUCT: + return MONO_NATIVE_STRUCT; + case MONO_NATIVE_CUSTOM: + return MONO_NATIVE_CUSTOM; + case MONO_NATIVE_INTERFACE: + *conv = MONO_MARSHAL_CONV_OBJECT_INTERFACE; + return MONO_NATIVE_INTERFACE; + case MONO_NATIVE_IDISPATCH: + *conv = MONO_MARSHAL_CONV_OBJECT_IDISPATCH; + return MONO_NATIVE_IDISPATCH; + case MONO_NATIVE_IUNKNOWN: + *conv = MONO_MARSHAL_CONV_OBJECT_IUNKNOWN; + return MONO_NATIVE_IUNKNOWN; + case MONO_NATIVE_FUNC: + if (t == MONO_TYPE_CLASS && (type->data.klass == mono_defaults.multicastdelegate_class || + type->data.klass == mono_defaults.delegate_class || + type->data.klass->parent == mono_defaults.multicastdelegate_class)) { + *conv = MONO_MARSHAL_CONV_DEL_FTN; + return MONO_NATIVE_FUNC; + } + /* Fall through */ + default: + g_error ("cant marshal object as native type %02x", mspec->native); + } + } + if (t == MONO_TYPE_CLASS && (type->data.klass == mono_defaults.multicastdelegate_class || + type->data.klass == mono_defaults.delegate_class || + type->data.klass->parent == mono_defaults.multicastdelegate_class)) { + *conv = MONO_MARSHAL_CONV_DEL_FTN; + return MONO_NATIVE_FUNC; + } + if (mono_class_try_get_safehandle_class () && type->data.klass == mono_class_try_get_safehandle_class ()){ + *conv = MONO_MARSHAL_CONV_SAFEHANDLE; + return MONO_NATIVE_INT; + } + *conv = MONO_MARSHAL_CONV_OBJECT_STRUCT; + return MONO_NATIVE_STRUCT; + } + case MONO_TYPE_FNPTR: return MONO_NATIVE_FUNC; + case MONO_TYPE_GENERICINST: + type = &type->data.generic_class->container_class->byval_arg; + t = type->type; + goto handle_enum; + case MONO_TYPE_TYPEDBYREF: + default: + g_error ("type 0x%02x not handled in marshal", t); + } + return MONO_NATIVE_MAX; +} + +/** + * mono_metadata_get_marshal_info: + */ +const char* +mono_metadata_get_marshal_info (MonoImage *meta, guint32 idx, gboolean is_field) +{ + locator_t loc; + MonoTableInfo *tdef = &meta->tables [MONO_TABLE_FIELDMARSHAL]; + + if (!tdef->base) + return NULL; + + loc.t = tdef; + loc.col_idx = MONO_FIELD_MARSHAL_PARENT; + loc.idx = ((idx + 1) << MONO_HAS_FIELD_MARSHAL_BITS) | (is_field? MONO_HAS_FIELD_MARSHAL_FIELDSREF: MONO_HAS_FIELD_MARSHAL_PARAMDEF); + + /* FIXME: Index translation */ + + if (!mono_binary_search (&loc, tdef->base, tdef->rows, tdef->row_size, table_locator)) + return NULL; + + return mono_metadata_blob_heap (meta, mono_metadata_decode_row_col (tdef, loc.result, MONO_FIELD_MARSHAL_NATIVE_TYPE)); +} + +MonoMethod* +method_from_method_def_or_ref (MonoImage *m, guint32 tok, MonoGenericContext *context, MonoError *error) +{ + MonoMethod *result = NULL; + guint32 idx = tok >> MONO_METHODDEFORREF_BITS; + + error_init (error); + + switch (tok & MONO_METHODDEFORREF_MASK) { + case MONO_METHODDEFORREF_METHODDEF: + result = mono_get_method_checked (m, MONO_TOKEN_METHOD_DEF | idx, NULL, context, error); + break; + case MONO_METHODDEFORREF_METHODREF: + result = mono_get_method_checked (m, MONO_TOKEN_MEMBER_REF | idx, NULL, context, error); + break; + default: + mono_error_set_bad_image (error, m, "Invalid MethodDefOfRef token %x", tok); + } + + return result; +} + +/* + * mono_class_get_overrides_full: + * + * Compute the method overrides belonging to class @type_token in @overrides, and the number of overrides in @num_overrides. + * + */ +void +mono_class_get_overrides_full (MonoImage *image, guint32 type_token, MonoMethod ***overrides, gint32 *num_overrides, MonoGenericContext *generic_context, MonoError *error) +{ + locator_t loc; + MonoTableInfo *tdef = &image->tables [MONO_TABLE_METHODIMPL]; + guint32 start, end; + gint32 i, num; + guint32 cols [MONO_METHODIMPL_SIZE]; + MonoMethod **result; + + error_init (error) + + *overrides = NULL; + if (num_overrides) + *num_overrides = 0; + + if (!tdef->base) + return; + + loc.t = tdef; + loc.col_idx = MONO_METHODIMPL_CLASS; + loc.idx = mono_metadata_token_index (type_token); + + if (!mono_binary_search (&loc, tdef->base, tdef->rows, tdef->row_size, table_locator)) + return; + + start = loc.result; + end = start + 1; + /* + * We may end up in the middle of the rows... + */ + while (start > 0) { + if (loc.idx == mono_metadata_decode_row_col (tdef, start - 1, MONO_METHODIMPL_CLASS)) + start--; + else + break; + } + while (end < tdef->rows) { + if (loc.idx == mono_metadata_decode_row_col (tdef, end, MONO_METHODIMPL_CLASS)) + end++; + else + break; + } + num = end - start; + result = g_new (MonoMethod*, num * 2); + for (i = 0; i < num; ++i) { + MonoMethod *method; + + if (!mono_verifier_verify_methodimpl_row (image, start + i, error)) + break; + + mono_metadata_decode_row (tdef, start + i, cols, MONO_METHODIMPL_SIZE); + method = method_from_method_def_or_ref (image, cols [MONO_METHODIMPL_DECLARATION], generic_context, error); + if (!method) + break; + + result [i * 2] = method; + method = method_from_method_def_or_ref (image, cols [MONO_METHODIMPL_BODY], generic_context, error); + if (!method) + break; + + result [i * 2 + 1] = method; + } + + if (!is_ok (error)) { + g_free (result); + *overrides = NULL; + if (num_overrides) + *num_overrides = 0; + } else { + *overrides = result; + if (num_overrides) + *num_overrides = num; + } +} + +/** + * mono_guid_to_string: + * + * Converts a 16 byte Microsoft GUID to the standard string representation. + */ +char * +mono_guid_to_string (const guint8 *guid) +{ + return g_strdup_printf ("%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", + guid[3], guid[2], guid[1], guid[0], + guid[5], guid[4], + guid[7], guid[6], + guid[8], guid[9], + guid[10], guid[11], guid[12], guid[13], guid[14], guid[15]); +} + +/** + * mono_guid_to_string_minimal: + * + * Converts a 16 byte Microsoft GUID to lower case no '-' representation.. + */ +char * +mono_guid_to_string_minimal (const guint8 *guid) +{ + return g_strdup_printf ("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + guid[3], guid[2], guid[1], guid[0], + guid[5], guid[4], + guid[7], guid[6], + guid[8], guid[9], + guid[10], guid[11], guid[12], guid[13], guid[14], guid[15]); +} +static gboolean +get_constraints (MonoImage *image, int owner, MonoClass ***constraints, MonoGenericContainer *container, MonoError *error) +{ + MonoTableInfo *tdef = &image->tables [MONO_TABLE_GENERICPARAMCONSTRAINT]; + guint32 cols [MONO_GENPARCONSTRAINT_SIZE]; + guint32 i, token, found; + MonoClass *klass, **res; + GSList *cons = NULL, *tmp; + MonoGenericContext *context = &container->context; + + error_init (error); + + *constraints = NULL; + found = 0; + for (i = 0; i < tdef->rows; ++i) { + mono_metadata_decode_row (tdef, i, cols, MONO_GENPARCONSTRAINT_SIZE); + if (cols [MONO_GENPARCONSTRAINT_GENERICPAR] == owner) { + token = mono_metadata_token_from_dor (cols [MONO_GENPARCONSTRAINT_CONSTRAINT]); + klass = mono_class_get_and_inflate_typespec_checked (image, token, context, error); + if (!klass) { + g_slist_free (cons); + return FALSE; + } + cons = g_slist_append (cons, klass); + ++found; + } else { + /* contiguous list finished */ + if (found) + break; + } + } + if (!found) + return TRUE; + res = (MonoClass **)mono_image_alloc0 (image, sizeof (MonoClass*) * (found + 1)); + for (i = 0, tmp = cons; i < found; ++i, tmp = tmp->next) { + res [i] = (MonoClass *)tmp->data; + } + g_slist_free (cons); + *constraints = res; + return TRUE; +} + +/* + * mono_metadata_get_generic_param_row: + * + * @image: + * @token: TypeOrMethodDef token, owner for GenericParam + * @owner: coded token, set on return + * + * Returns: 1-based row-id in the GenericParam table whose + * owner is @token. 0 if not found. + */ +guint32 +mono_metadata_get_generic_param_row (MonoImage *image, guint32 token, guint32 *owner) +{ + MonoTableInfo *tdef = &image->tables [MONO_TABLE_GENERICPARAM]; + locator_t loc; + + g_assert (owner); + if (!tdef->base) + return 0; + + if (mono_metadata_token_table (token) == MONO_TABLE_TYPEDEF) + *owner = MONO_TYPEORMETHOD_TYPE; + else if (mono_metadata_token_table (token) == MONO_TABLE_METHOD) + *owner = MONO_TYPEORMETHOD_METHOD; + else { + g_error ("wrong token %x to get_generic_param_row", token); + return 0; + } + *owner |= mono_metadata_token_index (token) << MONO_TYPEORMETHOD_BITS; + + loc.idx = *owner; + loc.col_idx = MONO_GENERICPARAM_OWNER; + loc.t = tdef; + + if (!mono_binary_search (&loc, tdef->base, tdef->rows, tdef->row_size, table_locator)) + return 0; + + /* Find the first entry by searching backwards */ + while ((loc.result > 0) && (mono_metadata_decode_row_col (tdef, loc.result - 1, MONO_GENERICPARAM_OWNER) == loc.idx)) + loc.result --; + + return loc.result + 1; +} + +gboolean +mono_metadata_has_generic_params (MonoImage *image, guint32 token) +{ + guint32 owner; + return mono_metadata_get_generic_param_row (image, token, &owner); +} + +/* + * Memory is allocated from IMAGE's mempool. + */ +gboolean +mono_metadata_load_generic_param_constraints_checked (MonoImage *image, guint32 token, + MonoGenericContainer *container, MonoError *error) +{ + + guint32 start_row, i, owner; + error_init (error); + + if (! (start_row = mono_metadata_get_generic_param_row (image, token, &owner))) + return TRUE; + for (i = 0; i < container->type_argc; i++) { + if (!get_constraints (image, start_row + i, &mono_generic_container_get_param_info (container, i)->constraints, container, error)) { + return FALSE; + } + } + return TRUE; +} + +/* + * mono_metadata_load_generic_params: + * + * Load the type parameters from the type or method definition @token. + * + * Use this method after parsing a type or method definition to figure out whether it's a generic + * type / method. When parsing a method definition, @parent_container points to the generic container + * of the current class, if any. + * + * Note: This method does not load the constraints: for typedefs, this has to be done after fully + * creating the type. + * + * Returns: NULL if @token is not a generic type or method definition or the new generic container. + * + * LOCKING: Acquires the loader lock + * + */ +MonoGenericContainer * +mono_metadata_load_generic_params (MonoImage *image, guint32 token, MonoGenericContainer *parent_container) +{ + MonoTableInfo *tdef = &image->tables [MONO_TABLE_GENERICPARAM]; + guint32 cols [MONO_GENERICPARAM_SIZE]; + guint32 i, owner = 0, n; + MonoGenericContainer *container; + MonoGenericParamFull *params; + MonoGenericContext *context; + + if (!(i = mono_metadata_get_generic_param_row (image, token, &owner))) + return NULL; + mono_metadata_decode_row (tdef, i - 1, cols, MONO_GENERICPARAM_SIZE); + params = NULL; + n = 0; + container = (MonoGenericContainer *)mono_image_alloc0 (image, sizeof (MonoGenericContainer)); + container->owner.image = image; // Temporarily mark as anonymous, but this will be overriden by caller + container->is_anonymous = TRUE; + do { + n++; + params = (MonoGenericParamFull *)g_realloc (params, sizeof (MonoGenericParamFull) * n); + memset (¶ms [n - 1], 0, sizeof (MonoGenericParamFull)); + params [n - 1].param.owner = container; + params [n - 1].param.num = cols [MONO_GENERICPARAM_NUMBER]; + params [n - 1].info.token = i | MONO_TOKEN_GENERIC_PARAM; + params [n - 1].info.flags = cols [MONO_GENERICPARAM_FLAGS]; + params [n - 1].info.name = mono_metadata_string_heap (image, cols [MONO_GENERICPARAM_NAME]); + if (params [n - 1].param.num != n - 1) + g_warning ("GenericParam table unsorted or hole in generic param sequence: token %d", i); + if (++i > tdef->rows) + break; + mono_metadata_decode_row (tdef, i - 1, cols, MONO_GENERICPARAM_SIZE); + } while (cols [MONO_GENERICPARAM_OWNER] == owner); + + container->type_argc = n; + container->type_params = (MonoGenericParamFull *)mono_image_alloc0 (image, sizeof (MonoGenericParamFull) * n); + memcpy (container->type_params, params, sizeof (MonoGenericParamFull) * n); + g_free (params); + container->parent = parent_container; + + if (mono_metadata_token_table (token) == MONO_TABLE_METHOD) + container->is_method = 1; + + g_assert (container->parent == NULL || container->is_method); + + context = &container->context; + if (container->is_method) { + context->class_inst = container->parent ? container->parent->context.class_inst : NULL; + context->method_inst = mono_get_shared_generic_inst (container); + } else { + context->class_inst = mono_get_shared_generic_inst (container); + } + + return container; +} + +MonoGenericInst * +mono_get_shared_generic_inst (MonoGenericContainer *container) +{ + MonoType **type_argv; + MonoType *helper; + MonoGenericInst *nginst; + int i; + + type_argv = g_new0 (MonoType *, container->type_argc); + helper = g_new0 (MonoType, container->type_argc); + + for (i = 0; i < container->type_argc; i++) { + MonoType *t = &helper [i]; + + t->type = container->is_method ? MONO_TYPE_MVAR : MONO_TYPE_VAR; + t->data.generic_param = mono_generic_container_get_param (container, i); + + type_argv [i] = t; + } + + nginst = mono_metadata_get_generic_inst (container->type_argc, type_argv); + + g_free (type_argv); + g_free (helper); + + return nginst; +} + +/** + * mono_type_is_byref: + * \param type the \c MonoType operated on + * \returns TRUE if \p type represents a type passed by reference, + * FALSE otherwise. + */ +gboolean +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 + * \returns the IL type value for \p type. This is one of the \c MonoTypeEnum + * enum members like \c MONO_TYPE_I4 or \c MONO_TYPE_STRING. + */ +int +mono_type_get_type (MonoType *type) +{ + return type->type; +} + +/** + * mono_type_get_signature: + * \param type the \c MonoType operated on + * It is only valid to call this function if \p type is a \c MONO_TYPE_FNPTR . + * \returns the \c MonoMethodSignature pointer that describes the signature + * of the function pointer \p type represents. + */ +MonoMethodSignature* +mono_type_get_signature (MonoType *type) +{ + g_assert (type->type == MONO_TYPE_FNPTR); + return type->data.method; +} + +/** + * mono_type_get_class: + * \param type the \c MonoType operated on + * It is only valid to call this function if \p type is a \c MONO_TYPE_CLASS or a + * \c MONO_TYPE_VALUETYPE . For more general functionality, use \c mono_class_from_mono_type, + * instead. + * \returns the \c MonoClass pointer that describes the class that \p type represents. + */ +MonoClass* +mono_type_get_class (MonoType *type) +{ + /* FIXME: review the runtime users before adding the assert here */ + 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 + * It is only valid to call this function if \p type is a \c MONO_TYPE_ARRAY . + * \returns a \c MonoArrayType struct describing the array type that \p type + * represents. The info includes details such as rank, array element type + * and the sizes and bounds of multidimensional arrays. + */ +MonoArrayType* +mono_type_get_array_type (MonoType *type) +{ + return type->data.array; +} + +/** + * mono_type_get_ptr_type: + * \pararm type the \c MonoType operated on + * It is only valid to call this function if \p type is a \c MONO_TYPE_PTR . + * \returns the \c MonoType pointer that describes the type that \p type + * represents a pointer to. + */ +MonoType* +mono_type_get_ptr_type (MonoType *type) +{ + g_assert (type->type == MONO_TYPE_PTR); + return type->data.type; +} + +/** + * mono_type_get_modifiers: + */ +MonoClass* +mono_type_get_modifiers (MonoType *type, gboolean *is_required, gpointer *iter) +{ + /* FIXME: implement */ + return NULL; +} + +/** + * mono_type_is_struct: + * \param type the \c MonoType operated on + * \returns TRUE if \p type is a struct, that is a \c ValueType but not an enum + * or a basic type like \c System.Int32 . FALSE otherwise. + */ +mono_bool +mono_type_is_struct (MonoType *type) +{ + return (!type->byref && ((type->type == MONO_TYPE_VALUETYPE && + !type->data.klass->enumtype) || (type->type == MONO_TYPE_TYPEDBYREF) || + ((type->type == MONO_TYPE_GENERICINST) && + mono_metadata_generic_class_is_valuetype (type->data.generic_class) && + !type->data.generic_class->container_class->enumtype))); +} + +/** + * mono_type_is_void: + * \param type the \c MonoType operated on + * \returns TRUE if \p type is \c System.Void . FALSE otherwise. + */ +mono_bool +mono_type_is_void (MonoType *type) +{ + return (type && (type->type == MONO_TYPE_VOID) && !type->byref); +} + +/** + * mono_type_is_pointer: + * \param type the \c MonoType operated on + * \returns TRUE if \p type is a managed or unmanaged pointer type. FALSE otherwise. + */ +mono_bool +mono_type_is_pointer (MonoType *type) +{ + return (type && ((type->byref || (type->type == MONO_TYPE_I) || type->type == MONO_TYPE_STRING) + || (type->type == MONO_TYPE_SZARRAY) || (type->type == MONO_TYPE_CLASS) || + (type->type == MONO_TYPE_U) || (type->type == MONO_TYPE_OBJECT) || + (type->type == MONO_TYPE_ARRAY) || (type->type == MONO_TYPE_PTR) || + (type->type == MONO_TYPE_FNPTR))); +} + +/** + * mono_type_is_reference: + * \param type the \c MonoType operated on + * \returns TRUE if \p type represents an object reference. FALSE otherwise. + */ +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)) || + ((type->type == MONO_TYPE_GENERICINST) && + !mono_metadata_generic_class_is_valuetype (type->data.generic_class)))); +} + +mono_bool +mono_type_is_generic_parameter (MonoType *type) +{ + return !type->byref && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR); +} + +/** + * mono_signature_get_return_type: + * \param sig the method signature inspected + * \returns the return type of the method signature \p sig + */ +MonoType* +mono_signature_get_return_type (MonoMethodSignature *sig) +{ + return sig->ret; +} + +/** + * mono_signature_get_params: + * \param sig the method signature inspected + * \param iter pointer to an iterator + * Iterates over the parameters for the method signature \p sig. + * A \c void* pointer must be initialized to NULL to start the iteration + * and its address is passed to this function repeteadly until it returns + * NULL. + * \returns the next parameter type of the method signature \p sig, + * NULL when finished. + */ +MonoType* +mono_signature_get_params (MonoMethodSignature *sig, gpointer *iter) +{ + MonoType** type; + if (!iter) + return NULL; + if (!*iter) { + /* start from the first */ + if (sig->param_count) { + *iter = &sig->params [0]; + return sig->params [0]; + } else { + /* no method */ + return NULL; + } + } + type = (MonoType **)*iter; + type++; + if (type < &sig->params [sig->param_count]) { + *iter = type; + return *type; + } + return NULL; +} + +/** + * mono_signature_get_param_count: + * \param sig the method signature inspected + * \returns the number of parameters in the method signature \p sig. + */ +guint32 +mono_signature_get_param_count (MonoMethodSignature *sig) +{ + return sig->param_count; +} + +/** + * mono_signature_get_call_conv: + * \param sig the method signature inspected + * \returns the call convention of the method signature \p sig. + */ +guint32 +mono_signature_get_call_conv (MonoMethodSignature *sig) +{ + return sig->call_convention; +} + +/** + * mono_signature_vararg_start: + * \param sig the method signature inspected + * \returns the number of the first vararg parameter in the + * method signature \param sig. \c -1 if this is not a vararg signature. + */ +int +mono_signature_vararg_start (MonoMethodSignature *sig) +{ + return sig->sentinelpos; +} + +/** + * mono_signature_is_instance: + * \param sig the method signature inspected + * \returns TRUE if this the method signature \p sig has an implicit + * first instance argument. FALSE otherwise. + */ +gboolean +mono_signature_is_instance (MonoMethodSignature *sig) +{ + return sig->hasthis; +} + +/** + * mono_signature_param_is_out + * \param sig the method signature inspected + * \param param_num the 0-based index of the inspected parameter + * \returns TRUE if the parameter is an out parameter, FALSE + * otherwise. + */ +mono_bool +mono_signature_param_is_out (MonoMethodSignature *sig, int param_num) +{ + g_assert (param_num >= 0 && param_num < sig->param_count); + return (sig->params [param_num]->attrs & PARAM_ATTRIBUTE_OUT) != 0; +} + +/** + * mono_signature_explicit_this: + * \param sig the method signature inspected + * \returns TRUE if this the method signature \p sig has an explicit + * instance argument. FALSE otherwise. + */ +gboolean +mono_signature_explicit_this (MonoMethodSignature *sig) +{ + return sig->explicit_this; +} + +/* for use with allocated memory blocks (assumes alignment is to 8 bytes) */ +guint +mono_aligned_addr_hash (gconstpointer ptr) +{ + /* Same hashing we use for objects */ + return (GPOINTER_TO_UINT (ptr) >> 3) * 2654435761u; +} + +/* + * If @field belongs to an inflated generic class, return the corresponding field of the + * generic type definition class. + */ +MonoClassField* +mono_metadata_get_corresponding_field_from_generic_type_definition (MonoClassField *field) +{ + MonoClass *gtd; + int offset; + + if (!mono_class_is_ginst (field->parent)) + return field; + + gtd = mono_class_get_generic_class (field->parent)->container_class; + offset = field - field->parent->fields; + return gtd->fields + offset; +} + +/* + * If @event belongs to an inflated generic class, return the corresponding event of the + * generic type definition class. + */ +MonoEvent* +mono_metadata_get_corresponding_event_from_generic_type_definition (MonoEvent *event) +{ + MonoClass *gtd; + int offset; + + if (!mono_class_is_ginst (event->parent)) + return event; + + gtd = mono_class_get_generic_class (event->parent)->container_class; + offset = event - mono_class_get_event_info (event->parent)->events; + return mono_class_get_event_info (gtd)->events + offset; +} + +/* + * If @property belongs to an inflated generic class, return the corresponding property of the + * generic type definition class. + */ +MonoProperty* +mono_metadata_get_corresponding_property_from_generic_type_definition (MonoProperty *property) +{ + MonoClassPropertyInfo *info; + MonoClass *gtd; + int offset; + + if (!mono_class_is_ginst (property->parent)) + return property; + + info = mono_class_get_property_info (property->parent); + gtd = mono_class_get_generic_class (property->parent)->container_class; + offset = property - info->properties; + return mono_class_get_property_info (gtd)->properties + offset; +} + +MonoWrapperCaches* +mono_method_get_wrapper_cache (MonoMethod *method) +{ + if (method->is_inflated) { + MonoMethodInflated *imethod = (MonoMethodInflated *)method; + return &imethod->owner->wrapper_caches; + } else { + return &method->klass->image->wrapper_caches; + } +} + +// This is support for the mempool reference tracking feature in checked-build, but lives in metadata.c due to use of static variables of this file. + +/** + * mono_find_image_set_owner: + * + * Find the imageset, if any, which a given pointer is located in the memory of. + */ +MonoImageSet * +mono_find_image_set_owner (void *ptr) +{ + MonoImageSet *owner = NULL; + int i; + + image_sets_lock (); + + if (image_sets) + { + for (i = 0; !owner && i < image_sets->len; ++i) { + MonoImageSet *set = (MonoImageSet *)g_ptr_array_index (image_sets, i); + if (mono_mempool_contains_addr (set->mempool, ptr)) + owner = set; + } + } + + image_sets_unlock (); + + return owner; +} + +void +mono_loader_set_strict_strong_names (gboolean enabled) +{ + check_strong_names_strictly = enabled; +} + +gboolean +mono_loader_get_strict_strong_names (void) +{ + return check_strong_names_strictly; +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/metadata.h b/unity-2019.4.24f1-mbe/mono/metadata/metadata.h new file mode 100644 index 000000000..b4a75a2ff --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/metadata.h @@ -0,0 +1,518 @@ +/** + * \file + */ + +#ifndef __MONO_METADATA_H__ +#define __MONO_METADATA_H__ + +#include + +#include +#include +#include + +MONO_BEGIN_DECLS + +#define MONO_TYPE_ISSTRUCT(t) mono_type_is_struct (t) +#define MONO_TYPE_IS_VOID(t) mono_type_is_void (t) +#define MONO_TYPE_IS_POINTER(t) mono_type_is_pointer (t) +#define MONO_TYPE_IS_REFERENCE(t) mono_type_is_reference (t) + +#define MONO_CLASS_IS_INTERFACE(c) ((mono_class_get_flags (c) & TYPE_ATTRIBUTE_INTERFACE) || (c->byval_arg.type == MONO_TYPE_VAR) || (c->byval_arg.type == MONO_TYPE_MVAR)) + +#define MONO_CLASS_IS_IMPORT(c) ((mono_class_get_flags (c) & TYPE_ATTRIBUTE_IMPORT)) + +typedef struct _MonoClass MonoClass; +typedef struct _MonoDomain MonoDomain; +typedef struct _MonoMethod MonoMethod; + +typedef enum { + MONO_EXCEPTION_CLAUSE_NONE, + MONO_EXCEPTION_CLAUSE_FILTER, + MONO_EXCEPTION_CLAUSE_FINALLY, + MONO_EXCEPTION_CLAUSE_FAULT = 4 +} MonoExceptionEnum; + +typedef enum { + MONO_CALL_DEFAULT, + MONO_CALL_C, + MONO_CALL_STDCALL, + MONO_CALL_THISCALL, + MONO_CALL_FASTCALL, + MONO_CALL_VARARG +} MonoCallConvention; + +/* ECMA lamespec: the old spec had more info... */ +typedef enum { + MONO_NATIVE_BOOLEAN = 0x02, /* 4 bytes, 0 is false, != 0 is true */ + MONO_NATIVE_I1 = 0x03, + MONO_NATIVE_U1 = 0x04, + MONO_NATIVE_I2 = 0x05, + MONO_NATIVE_U2 = 0x06, + MONO_NATIVE_I4 = 0x07, + MONO_NATIVE_U4 = 0x08, + MONO_NATIVE_I8 = 0x09, + MONO_NATIVE_U8 = 0x0a, + MONO_NATIVE_R4 = 0x0b, + MONO_NATIVE_R8 = 0x0c, + MONO_NATIVE_CURRENCY = 0x0f, + MONO_NATIVE_BSTR = 0x13, /* prefixed length, Unicode */ + MONO_NATIVE_LPSTR = 0x14, /* ANSI, null terminated */ + MONO_NATIVE_LPWSTR = 0x15, /* UNICODE, null terminated */ + MONO_NATIVE_LPTSTR = 0x16, /* plattform dep., null terminated */ + MONO_NATIVE_BYVALTSTR = 0x17, + MONO_NATIVE_IUNKNOWN = 0x19, + MONO_NATIVE_IDISPATCH = 0x1a, + MONO_NATIVE_STRUCT = 0x1b, + MONO_NATIVE_INTERFACE = 0x1c, + MONO_NATIVE_SAFEARRAY = 0x1d, + MONO_NATIVE_BYVALARRAY = 0x1e, + MONO_NATIVE_INT = 0x1f, + MONO_NATIVE_UINT = 0x20, + MONO_NATIVE_VBBYREFSTR = 0x22, + MONO_NATIVE_ANSIBSTR = 0x23, /* prefixed length, ANSI */ + MONO_NATIVE_TBSTR = 0x24, /* prefixed length, plattform dep. */ + MONO_NATIVE_VARIANTBOOL = 0x25, + MONO_NATIVE_FUNC = 0x26, + MONO_NATIVE_ASANY = 0x28, + MONO_NATIVE_LPARRAY = 0x2a, + MONO_NATIVE_LPSTRUCT = 0x2b, + MONO_NATIVE_CUSTOM = 0x2c, + MONO_NATIVE_ERROR = 0x2d, + // TODO: MONO_NATIVE_IINSPECTABLE = 0x2e + // TODO: MONO_NATIVE_HSTRING = 0x2f + MONO_NATIVE_UTF8STR = 0x30, + MONO_NATIVE_MAX = 0x50 /* no info */ +} MonoMarshalNative; + +/* Used only in context of SafeArray */ +typedef enum { + MONO_VARIANT_EMPTY = 0x00, + MONO_VARIANT_NULL = 0x01, + MONO_VARIANT_I2 = 0x02, + MONO_VARIANT_I4 = 0x03, + MONO_VARIANT_R4 = 0x04, + MONO_VARIANT_R8 = 0x05, + MONO_VARIANT_CY = 0x06, + MONO_VARIANT_DATE = 0x07, + MONO_VARIANT_BSTR = 0x08, + MONO_VARIANT_DISPATCH = 0x09, + MONO_VARIANT_ERROR = 0x0a, + MONO_VARIANT_BOOL = 0x0b, + MONO_VARIANT_VARIANT = 0x0c, + MONO_VARIANT_UNKNOWN = 0x0d, + MONO_VARIANT_DECIMAL = 0x0e, + MONO_VARIANT_I1 = 0x10, + MONO_VARIANT_UI1 = 0x11, + MONO_VARIANT_UI2 = 0x12, + MONO_VARIANT_UI4 = 0x13, + MONO_VARIANT_I8 = 0x14, + MONO_VARIANT_UI8 = 0x15, + MONO_VARIANT_INT = 0x16, + MONO_VARIANT_UINT = 0x17, + MONO_VARIANT_VOID = 0x18, + MONO_VARIANT_HRESULT = 0x19, + MONO_VARIANT_PTR = 0x1a, + MONO_VARIANT_SAFEARRAY = 0x1b, + MONO_VARIANT_CARRAY = 0x1c, + MONO_VARIANT_USERDEFINED = 0x1d, + MONO_VARIANT_LPSTR = 0x1e, + MONO_VARIANT_LPWSTR = 0x1f, + MONO_VARIANT_RECORD = 0x24, + MONO_VARIANT_FILETIME = 0x40, + MONO_VARIANT_BLOB = 0x41, + MONO_VARIANT_STREAM = 0x42, + MONO_VARIANT_STORAGE = 0x43, + MONO_VARIANT_STREAMED_OBJECT = 0x44, + MONO_VARIANT_STORED_OBJECT = 0x45, + MONO_VARIANT_BLOB_OBJECT = 0x46, + MONO_VARIANT_CF = 0x47, + MONO_VARIANT_CLSID = 0x48, + MONO_VARIANT_VECTOR = 0x1000, + MONO_VARIANT_ARRAY = 0x2000, + MONO_VARIANT_BYREF = 0x4000 +} MonoMarshalVariant; + +typedef enum { + MONO_MARSHAL_CONV_NONE, + MONO_MARSHAL_CONV_BOOL_VARIANTBOOL, + MONO_MARSHAL_CONV_BOOL_I4, + MONO_MARSHAL_CONV_STR_BSTR, + MONO_MARSHAL_CONV_STR_LPSTR, + MONO_MARSHAL_CONV_LPSTR_STR, + MONO_MARSHAL_CONV_LPTSTR_STR, + MONO_MARSHAL_CONV_STR_LPWSTR, + MONO_MARSHAL_CONV_LPWSTR_STR, + MONO_MARSHAL_CONV_STR_LPTSTR, + MONO_MARSHAL_CONV_STR_ANSIBSTR, + MONO_MARSHAL_CONV_STR_TBSTR, + MONO_MARSHAL_CONV_STR_BYVALSTR, + MONO_MARSHAL_CONV_STR_BYVALWSTR, + MONO_MARSHAL_CONV_SB_LPSTR, + MONO_MARSHAL_CONV_SB_LPTSTR, + MONO_MARSHAL_CONV_SB_LPWSTR, + MONO_MARSHAL_CONV_LPSTR_SB, + MONO_MARSHAL_CONV_LPTSTR_SB, + MONO_MARSHAL_CONV_LPWSTR_SB, + MONO_MARSHAL_CONV_ARRAY_BYVALARRAY, + MONO_MARSHAL_CONV_ARRAY_BYVALCHARARRAY, + MONO_MARSHAL_CONV_ARRAY_SAVEARRAY, + MONO_MARSHAL_CONV_ARRAY_LPARRAY, + MONO_MARSHAL_FREE_LPARRAY, + MONO_MARSHAL_CONV_OBJECT_INTERFACE, + MONO_MARSHAL_CONV_OBJECT_IDISPATCH, + MONO_MARSHAL_CONV_OBJECT_IUNKNOWN, + MONO_MARSHAL_CONV_OBJECT_STRUCT, + MONO_MARSHAL_CONV_DEL_FTN, + MONO_MARSHAL_CONV_FTN_DEL, + MONO_MARSHAL_FREE_ARRAY, + MONO_MARSHAL_CONV_BSTR_STR, + MONO_MARSHAL_CONV_SAFEHANDLE, + MONO_MARSHAL_CONV_HANDLEREF, + MONO_MARSHAL_CONV_STR_UTF8STR, + MONO_MARSHAL_CONV_SB_UTF8STR, + MONO_MARSHAL_CONV_UTF8STR_STR, + MONO_MARSHAL_CONV_UTF8STR_SB, + MONO_MARSHAL_CONV_FIXED_BUFFER +} MonoMarshalConv; + +#define MONO_MARSHAL_CONV_INVALID ((MonoMarshalConv)-1) + +typedef struct { + MonoMarshalNative native; + union { + struct { + MonoMarshalNative elem_type; + int32_t num_elem; /* -1 if not set */ + int16_t param_num; /* -1 if not set */ + int16_t elem_mult; /* -1 if not set */ + } array_data; + struct { + char *custom_name; + char *cookie; + MonoImage *image; + } custom_data; + struct { + MonoMarshalVariant elem_type; + int32_t num_elem; + } safearray_data; + } data; +} MonoMarshalSpec; + +MONO_API void mono_metadata_init (void); + +MONO_API void mono_metadata_decode_row (const MonoTableInfo *t, + int idx, + uint32_t *res, + int res_size); + +MONO_API uint32_t mono_metadata_decode_row_col (const MonoTableInfo *t, + int idx, + unsigned int col); + +/* + * This macro is used to extract the size of the table encoded in + * the size_bitfield of MonoTableInfo. + */ +#define mono_metadata_table_size(bitfield,table) ((((bitfield) >> ((table)*2)) & 0x3) + 1) +#define mono_metadata_table_count(bitfield) ((bitfield) >> 24) + +MONO_API int mono_metadata_compute_size (MonoImage *meta, + int tableindex, + uint32_t *result_bitfield); + +/* + * + */ +MONO_API const char *mono_metadata_locate (MonoImage *meta, int table, int idx); +MONO_API const char *mono_metadata_locate_token (MonoImage *meta, uint32_t token); + +MONO_API const char *mono_metadata_string_heap (MonoImage *meta, uint32_t table_index); +MONO_API const char *mono_metadata_blob_heap (MonoImage *meta, uint32_t table_index); +MONO_API const char *mono_metadata_user_string (MonoImage *meta, uint32_t table_index); +MONO_API const char *mono_metadata_guid_heap (MonoImage *meta, uint32_t table_index); + +MONO_API uint32_t mono_metadata_typedef_from_field (MonoImage *meta, uint32_t table_index); +MONO_API uint32_t mono_metadata_typedef_from_method (MonoImage *meta, uint32_t table_index); +MONO_API uint32_t mono_metadata_nested_in_typedef (MonoImage *meta, uint32_t table_index); +MONO_API uint32_t mono_metadata_nesting_typedef (MonoImage *meta, uint32_t table_index, uint32_t start_index); + +MONO_API MonoClass** mono_metadata_interfaces_from_typedef (MonoImage *meta, uint32_t table_index, unsigned int *count); + +MONO_API uint32_t mono_metadata_events_from_typedef (MonoImage *meta, uint32_t table_index, unsigned int *end_idx); +MONO_API uint32_t mono_metadata_methods_from_event (MonoImage *meta, uint32_t table_index, unsigned int *end); +MONO_API uint32_t mono_metadata_properties_from_typedef (MonoImage *meta, uint32_t table_index, unsigned int *end); +MONO_API uint32_t mono_metadata_methods_from_property (MonoImage *meta, uint32_t table_index, unsigned int *end); +MONO_API uint32_t mono_metadata_packing_from_typedef (MonoImage *meta, uint32_t table_index, uint32_t *packing, uint32_t *size); +MONO_API const char* mono_metadata_get_marshal_info (MonoImage *meta, uint32_t idx, mono_bool is_field); +MONO_API uint32_t mono_metadata_custom_attrs_from_index (MonoImage *meta, uint32_t cattr_index); + +MONO_API MonoMarshalSpec *mono_metadata_parse_marshal_spec (MonoImage *image, const char *ptr); + +MONO_API void mono_metadata_free_marshal_spec (MonoMarshalSpec *spec); + +MONO_API uint32_t mono_metadata_implmap_from_method (MonoImage *meta, uint32_t method_idx); + +MONO_API void mono_metadata_field_info (MonoImage *meta, + uint32_t table_index, + uint32_t *offset, + uint32_t *rva, + MonoMarshalSpec **marshal_spec); + +MONO_API uint32_t mono_metadata_get_constant_index (MonoImage *meta, uint32_t token, uint32_t hint); + +/* + * Functions to extract information from the Blobs + */ +MONO_API uint32_t mono_metadata_decode_value (const char *ptr, + const char **rptr); +MONO_API int32_t mono_metadata_decode_signed_value (const char *ptr, const char **rptr); + +MONO_API uint32_t mono_metadata_decode_blob_size (const char *ptr, + const char **rptr); + +MONO_API void mono_metadata_encode_value (uint32_t value, char *bug, char **endbuf); + +#define MONO_OFFSET_IN_CLAUSE(clause,offset) \ + ((clause)->try_offset <= (offset) && (offset) < ((clause)->try_offset + (clause)->try_len)) +#define MONO_OFFSET_IN_HANDLER(clause,offset) \ + ((clause)->handler_offset <= (offset) && (offset) < ((clause)->handler_offset + (clause)->handler_len)) +#define MONO_OFFSET_IN_FILTER(clause,offset) \ + ((clause)->flags == MONO_EXCEPTION_CLAUSE_FILTER && (clause)->data.filter_offset <= (offset) && (offset) < ((clause)->handler_offset)) + +typedef struct { + uint32_t flags; + uint32_t try_offset; + uint32_t try_len; + uint32_t handler_offset; + uint32_t handler_len; + union { + uint32_t filter_offset; + MonoClass *catch_class; + } data; +} MonoExceptionClause; + +typedef struct _MonoType MonoType; +typedef struct _MonoGenericInst MonoGenericInst; +typedef struct _MonoGenericClass MonoGenericClass; +typedef struct _MonoGenericContext MonoGenericContext; +typedef struct _MonoGenericContainer MonoGenericContainer; +typedef struct _MonoGenericParam MonoGenericParam; +typedef struct _MonoArrayType MonoArrayType; +typedef struct _MonoMethodSignature MonoMethodSignature; + +/* FIXME: Keeping this name alive for now, since it is part of the exposed API, even though no entrypoint uses it. */ +typedef struct invalid_name MonoGenericMethod; + +typedef struct { + unsigned int required : 1; + unsigned int token : 31; +} MonoCustomMod; + +struct _MonoArrayType { + MonoClass *eklass; + // Number of dimensions of the array + uint8_t rank; + + // Arrays recording known upper and lower index bounds for each dimension + uint8_t numsizes; + uint8_t numlobounds; + int *sizes; + int *lobounds; +}; + +typedef struct _MonoMethodHeader MonoMethodHeader; + +typedef enum { + MONO_PARSE_TYPE, + MONO_PARSE_MOD_TYPE, + MONO_PARSE_LOCAL, + MONO_PARSE_PARAM, + MONO_PARSE_RET, + 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); + +/* For MONO_TYPE_FNPTR */ +MONO_API MonoMethodSignature* +mono_type_get_signature (MonoType *type); + +/* For MONO_TYPE_CLASS, VALUETYPE */ +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); + +/* For MONO_TYPE_PTR */ +MONO_API MonoType* +mono_type_get_ptr_type (MonoType *type); + +MONO_API MonoClass* +mono_type_get_modifiers (MonoType *type, mono_bool *is_required, void **iter); + +MONO_API mono_bool mono_type_is_struct (MonoType *type); +MONO_API mono_bool mono_type_is_void (MonoType *type); +MONO_API mono_bool mono_type_is_pointer (MonoType *type); +MONO_API mono_bool mono_type_is_reference (MonoType *type); +mono_bool mono_type_is_generic_parameter (MonoType *type); + +MONO_API MonoType* +mono_signature_get_return_type (MonoMethodSignature *sig); + +MONO_API MonoType* +mono_signature_get_params (MonoMethodSignature *sig, void **iter); + +MONO_API uint32_t +mono_signature_get_param_count (MonoMethodSignature *sig); + +MONO_API uint32_t +mono_signature_get_call_conv (MonoMethodSignature *sig); + +MONO_API int +mono_signature_vararg_start (MonoMethodSignature *sig); + +MONO_API mono_bool +mono_signature_is_instance (MonoMethodSignature *sig); + +MONO_API mono_bool +mono_signature_explicit_this (MonoMethodSignature *sig); + +MONO_API mono_bool +mono_signature_param_is_out (MonoMethodSignature *sig, int param_num); + +MONO_API uint32_t mono_metadata_parse_typedef_or_ref (MonoImage *m, + const char *ptr, + const char **rptr); +MONO_API int mono_metadata_parse_custom_mod (MonoImage *m, + MonoCustomMod *dest, + const char *ptr, + const char **rptr); +MONO_RT_EXTERNAL_ONLY +MONO_API MonoArrayType *mono_metadata_parse_array (MonoImage *m, + const char *ptr, + const char **rptr); +MONO_API void mono_metadata_free_array (MonoArrayType *array); +MONO_RT_EXTERNAL_ONLY +MONO_API MonoType *mono_metadata_parse_type (MonoImage *m, + MonoParseTypeMode mode, + short opt_attrs, + const char *ptr, + const char **rptr); +MONO_RT_EXTERNAL_ONLY +MONO_API MonoType *mono_metadata_parse_param (MonoImage *m, + const char *ptr, + const char **rptr); +MONO_RT_EXTERNAL_ONLY +MONO_API MonoType *mono_metadata_parse_field_type (MonoImage *m, + short field_flags, + const char *ptr, + const char **rptr); +MONO_API MonoType *mono_type_create_from_typespec (MonoImage *image, + uint32_t type_spec); +MONO_API void mono_metadata_free_type (MonoType *type); +MONO_API int mono_type_size (MonoType *type, + int *alignment); +MONO_API int mono_type_stack_size (MonoType *type, + int *alignment); + +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); + +MONO_API MonoMethodSignature *mono_metadata_signature_alloc (MonoImage *image, uint32_t nparams); + +MONO_API MonoMethodSignature *mono_metadata_signature_dup (MonoMethodSignature *sig); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoMethodSignature *mono_metadata_parse_signature (MonoImage *image, + uint32_t token); + +MONO_API MonoMethodSignature *mono_metadata_parse_method_signature (MonoImage *m, + int def, + const char *ptr, + const char **rptr); +MONO_API void mono_metadata_free_method_signature (MonoMethodSignature *method); + +MONO_API mono_bool mono_metadata_signature_equal (MonoMethodSignature *sig1, + MonoMethodSignature *sig2); + +MONO_API unsigned int mono_signature_hash (MonoMethodSignature *sig); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoMethodHeader *mono_metadata_parse_mh (MonoImage *m, const char *ptr); +MONO_API void mono_metadata_free_mh (MonoMethodHeader *mh); + +/* MonoMethodHeader acccessors */ +MONO_API const unsigned char* +mono_method_header_get_code (MonoMethodHeader *header, uint32_t* code_size, uint32_t* max_stack); + +MONO_API MonoType** +mono_method_header_get_locals (MonoMethodHeader *header, uint32_t* num_locals, mono_bool *init_locals); + +MONO_API int +mono_method_header_get_num_clauses (MonoMethodHeader *header); + +MONO_API int +mono_method_header_get_clauses (MonoMethodHeader *header, MonoMethod *method, void **iter, MonoExceptionClause *clause); + +MONO_API uint32_t +mono_type_to_unmanaged (MonoType *type, MonoMarshalSpec *mspec, + mono_bool as_field, mono_bool unicode, MonoMarshalConv *conv); + +/* + * Makes a token based on a table and an index + */ +#define mono_metadata_make_token(table,idx) (((table) << 24)| (idx)) + +/* + * Returns the table index that this token encodes. + */ +#define mono_metadata_token_table(token) ((token) >> 24) + + /* + * Returns the index that a token refers to + */ +#define mono_metadata_token_index(token) ((token & 0xffffff)) + + +#define mono_metadata_token_code(token) ((token & 0xff000000)) + +MONO_API uint32_t mono_metadata_token_from_dor (uint32_t dor_index); + +MONO_API char *mono_guid_to_string (const uint8_t *guid); + +MONO_API char *mono_guid_to_string_minimal (const uint8_t *guid); + +MONO_API uint32_t mono_metadata_declsec_from_index (MonoImage *meta, uint32_t idx); + +MONO_API uint32_t mono_metadata_translate_token_index (MonoImage *image, int table, uint32_t idx); + +MONO_API void mono_metadata_decode_table_row (MonoImage *image, int table, + int idx, + uint32_t *res, + int res_size); + +MONO_API uint32_t mono_metadata_decode_table_row_col (MonoImage *image, int table, + int idx, + unsigned int col); + +MONO_END_DECLS + +#endif /* __MONO_METADATA_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/method-builder.c b/unity-2019.4.24f1-mbe/mono/metadata/method-builder.c new file mode 100644 index 000000000..ccff8c91a --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/method-builder.c @@ -0,0 +1,701 @@ +/** + * \file + * Functions for creating IL methods at runtime. + * + * Author: + * Paolo Molaro (lupus@ximian.com) + * + * Copyright 2002-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include "config.h" +#include "loader.h" +#include "mono/metadata/abi-details.h" +#include "mono/metadata/method-builder.h" +#include "mono/metadata/tabledefs.h" +#include "mono/metadata/exception.h" +#include "mono/metadata/appdomain.h" +#include "mono/metadata/debug-helpers.h" +#include "mono/metadata/metadata-internals.h" +#include "mono/metadata/domain-internals.h" +#include +#include + +/* #define DEBUG_RUNTIME_CODE */ + +#define OPDEF(a,b,c,d,e,f,g,h,i,j) \ + a = i, + +enum { +#include "mono/cil/opcode.def" + LAST = 0xff +}; +#undef OPDEF + +#ifdef DEBUG_RUNTIME_CODE +static char* +indenter (MonoDisHelper *dh, MonoMethod *method, guint32 ip_offset) +{ + return g_strdup (" "); +} + +static MonoDisHelper marshal_dh = { + "\n", + "IL_%04x: ", + "IL_%04x", + indenter, + NULL, + NULL +}; +#endif + +static MonoMethodBuilder * +mono_mb_new_base (MonoClass *klass, MonoWrapperType type) +{ + MonoMethodBuilder *mb; + MonoMethod *m; + + g_assert (klass != NULL); + + mb = g_new0 (MonoMethodBuilder, 1); + + mb->method = m = (MonoMethod *)g_new0 (MonoMethodWrapper, 1); + + m->klass = klass; + m->inline_info = 1; + m->wrapper_type = type; + +#ifdef ENABLE_ILGEN + mb->code_size = 40; + mb->code = (unsigned char *)g_malloc (mb->code_size); + mb->init_locals = TRUE; +#endif + /* placeholder for the wrapper always at index 1 */ + mono_mb_add_data (mb, NULL); + + return mb; +} + +MonoMethodBuilder * +mono_mb_new_no_dup_name (MonoClass *klass, const char *name, MonoWrapperType type) +{ + MonoMethodBuilder *mb = mono_mb_new_base (klass, type); + mb->name = (char*)name; + mb->no_dup_name = TRUE; + return mb; +} + +/** + * mono_mb_new: + */ +MonoMethodBuilder * +mono_mb_new (MonoClass *klass, const char *name, MonoWrapperType type) +{ + MonoMethodBuilder *mb = mono_mb_new_base (klass, type); + mb->name = g_strdup (name); + return mb; +} + +/** + * mono_mb_free: + */ +void +mono_mb_free (MonoMethodBuilder *mb) +{ +#ifdef ENABLE_ILGEN + GList *l; + + for (l = mb->locals_list; l; l = l->next) { + /* Allocated in mono_mb_add_local () */ + g_free (l->data); + } + g_list_free (mb->locals_list); + if (!mb->dynamic) { + g_free (mb->method); + if (!mb->no_dup_name) + g_free (mb->name); + g_free (mb->code); + } +#else + g_free (mb->method); + if (!mb->no_dup_name) + g_free (mb->name); +#endif + g_free (mb); +} + +/** + * mono_mb_create_method: + * Create a \c MonoMethod from this method builder. + * \returns the newly created method. + */ +MonoMethod * +mono_mb_create_method (MonoMethodBuilder *mb, MonoMethodSignature *signature, int max_stack) +{ +#ifdef ENABLE_ILGEN + MonoMethodHeader *header; +#endif + MonoMethodWrapper *mw; + MonoImage *image; + MonoMethod *method; + GList *l; + int i; + + g_assert (mb != NULL); + + image = mb->method->klass->image; + +#ifdef ENABLE_ILGEN + if (mb->dynamic) { + method = mb->method; + mw = (MonoMethodWrapper*)method; + + method->name = mb->name; + method->dynamic = TRUE; + + mw->header = header = (MonoMethodHeader *) + g_malloc0 (MONO_SIZEOF_METHOD_HEADER + mb->locals * sizeof (MonoType *)); + + header->code = mb->code; + + for (i = 0, l = mb->locals_list; l; l = l->next, i++) { + header->locals [i] = (MonoType*)l->data; + } + } else +#endif + { + /* Realloc the method info into a mempool */ + + method = (MonoMethod *)mono_image_alloc0 (image, sizeof (MonoMethodWrapper)); + memcpy (method, mb->method, sizeof (MonoMethodWrapper)); + mw = (MonoMethodWrapper*) method; + + if (mb->no_dup_name) + method->name = mb->name; + else + method->name = mono_image_strdup (image, mb->name); + +#ifdef ENABLE_ILGEN + mw->header = header = (MonoMethodHeader *) + mono_image_alloc0 (image, MONO_SIZEOF_METHOD_HEADER + mb->locals * sizeof (MonoType *)); + + header->code = (const unsigned char *)mono_image_alloc (image, mb->pos); + memcpy ((char*)header->code, mb->code, mb->pos); + + for (i = 0, l = mb->locals_list; l; l = l->next, i++) { + header->locals [i] = (MonoType*)l->data; + } +#endif + } + +#ifdef ENABLE_ILGEN + /* Free the locals list so mono_mb_free () doesn't free the types twice */ + g_list_free (mb->locals_list); + mb->locals_list = NULL; +#endif + + method->signature = signature; + if (!signature->hasthis) + method->flags |= METHOD_ATTRIBUTE_STATIC; + +#ifdef ENABLE_ILGEN + if (max_stack < 8) + max_stack = 8; + + header->max_stack = max_stack; + + header->code_size = mb->pos; + header->num_locals = mb->locals; + header->init_locals = mb->init_locals; + + header->num_clauses = mb->num_clauses; + header->clauses = mb->clauses; + + method->skip_visibility = mb->skip_visibility; +#endif + + i = g_list_length ((GList *)mw->method_data); + if (i) { + GList *tmp; + void **data; + l = g_list_reverse ((GList *)mw->method_data); + if (method_is_dynamic (method)) + data = (void **)g_malloc (sizeof (gpointer) * (i + 1)); + else + data = (void **)mono_image_alloc (image, sizeof (gpointer) * (i + 1)); + /* store the size in the first element */ + data [0] = GUINT_TO_POINTER (i); + i = 1; + for (tmp = l; tmp; tmp = tmp->next) { + data [i++] = tmp->data; + } + g_list_free (l); + + mw->method_data = data; + } + +#ifdef ENABLE_ILGEN + /*{ + static int total_code = 0; + static int total_alloc = 0; + total_code += mb->pos; + total_alloc += mb->code_size; + g_print ("code size: %d of %d (allocated: %d)\n", mb->pos, total_code, total_alloc); + }*/ + +#ifdef DEBUG_RUNTIME_CODE + printf ("RUNTIME CODE FOR %s\n", mono_method_full_name (method, TRUE)); + printf ("%s\n", mono_disasm_code (&marshal_dh, method, mb->code, mb->code + mb->pos)); +#endif + + if (mb->param_names) { + char **param_names = (char **)mono_image_alloc0 (image, signature->param_count * sizeof (gpointer)); + for (i = 0; i < signature->param_count; ++i) + param_names [i] = mono_image_strdup (image, mb->param_names [i]); + + mono_image_lock (image); + if (!image->wrapper_param_names) + image->wrapper_param_names = g_hash_table_new (NULL, NULL); + g_hash_table_insert (image->wrapper_param_names, method, param_names); + mono_image_unlock (image); + } +#endif + + return method; +} + +/** + * mono_mb_add_data: + */ +guint32 +mono_mb_add_data (MonoMethodBuilder *mb, gpointer data) +{ + MonoMethodWrapper *mw; + + g_assert (mb != NULL); + + mw = (MonoMethodWrapper *)mb->method; + + /* one O(n) is enough */ + mw->method_data = g_list_prepend ((GList *)mw->method_data, data); + + return g_list_length ((GList *)mw->method_data); +} + +#ifdef ENABLE_ILGEN + +/** + * mono_mb_add_local: + */ +int +mono_mb_add_local (MonoMethodBuilder *mb, MonoType *type) +{ + int res; + MonoType *t; + + /* + * Have to make a copy early since type might be sig->ret, + * which is transient, see mono_metadata_signature_dup_internal_with_padding (). + */ + t = mono_metadata_type_dup (NULL, type); + + g_assert (mb != NULL); + g_assert (type != NULL); + + res = mb->locals; + mb->locals_list = g_list_append (mb->locals_list, t); + mb->locals++; + + return res; +} + +/** + * mono_mb_patch_addr: + */ +void +mono_mb_patch_addr (MonoMethodBuilder *mb, int pos, int value) +{ + mb->code [pos] = value & 0xff; + mb->code [pos + 1] = (value >> 8) & 0xff; + mb->code [pos + 2] = (value >> 16) & 0xff; + mb->code [pos + 3] = (value >> 24) & 0xff; +} + +/** + * mono_mb_patch_addr_s: + */ +void +mono_mb_patch_addr_s (MonoMethodBuilder *mb, int pos, gint8 value) +{ + *((gint8 *)(&mb->code [pos])) = value; +} + +/** + * mono_mb_emit_byte: + */ +void +mono_mb_emit_byte (MonoMethodBuilder *mb, guint8 op) +{ + if (mb->pos >= mb->code_size) { + mb->code_size += mb->code_size >> 1; + mb->code = (unsigned char *)g_realloc (mb->code, mb->code_size); + } + + mb->code [mb->pos++] = op; +} + +/** + * mono_mb_emit_ldflda: + */ +void +mono_mb_emit_ldflda (MonoMethodBuilder *mb, gint32 offset) +{ + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_OBJADDR); + + if (offset) { + mono_mb_emit_icon (mb, offset); + mono_mb_emit_byte (mb, CEE_ADD); + } +} + +/** + * mono_mb_emit_i4: + */ +void +mono_mb_emit_i4 (MonoMethodBuilder *mb, gint32 data) +{ + if ((mb->pos + 4) >= mb->code_size) { + mb->code_size += mb->code_size >> 1; + mb->code = (unsigned char *)g_realloc (mb->code, mb->code_size); + } + + mono_mb_patch_addr (mb, mb->pos, data); + mb->pos += 4; +} + +void +mono_mb_emit_i8 (MonoMethodBuilder *mb, gint64 data) +{ + if ((mb->pos + 8) >= mb->code_size) { + mb->code_size += mb->code_size >> 1; + mb->code = (unsigned char *)g_realloc (mb->code, mb->code_size); + } + + mono_mb_patch_addr (mb, mb->pos, data); + mono_mb_patch_addr (mb, mb->pos + 4, data >> 32); + mb->pos += 8; +} + +/** + * mono_mb_emit_i2: + */ +void +mono_mb_emit_i2 (MonoMethodBuilder *mb, gint16 data) +{ + if ((mb->pos + 2) >= mb->code_size) { + mb->code_size += mb->code_size >> 1; + mb->code = (unsigned char *)g_realloc (mb->code, mb->code_size); + } + + mb->code [mb->pos] = data & 0xff; + mb->code [mb->pos + 1] = (data >> 8) & 0xff; + mb->pos += 2; +} + +void +mono_mb_emit_op (MonoMethodBuilder *mb, guint8 op, gpointer data) +{ + mono_mb_emit_byte (mb, op); + mono_mb_emit_i4 (mb, mono_mb_add_data (mb, data)); +} + +/** + * mono_mb_emit_ldstr: + */ +void +mono_mb_emit_ldstr (MonoMethodBuilder *mb, char *str) +{ + mono_mb_emit_op (mb, CEE_LDSTR, str); +} + +/** + * mono_mb_emit_ldarg: + */ +void +mono_mb_emit_ldarg (MonoMethodBuilder *mb, guint argnum) +{ + if (argnum < 4) { + mono_mb_emit_byte (mb, CEE_LDARG_0 + argnum); + } else if (argnum < 256) { + mono_mb_emit_byte (mb, CEE_LDARG_S); + mono_mb_emit_byte (mb, argnum); + } else { + mono_mb_emit_byte (mb, CEE_PREFIX1); + mono_mb_emit_byte (mb, CEE_LDARG); + mono_mb_emit_i2 (mb, argnum); + } +} + +/** + * mono_mb_emit_ldarg_addr: + */ +void +mono_mb_emit_ldarg_addr (MonoMethodBuilder *mb, guint argnum) +{ + if (argnum < 256) { + mono_mb_emit_byte (mb, CEE_LDARGA_S); + mono_mb_emit_byte (mb, argnum); + } else { + mono_mb_emit_byte (mb, CEE_PREFIX1); + mono_mb_emit_byte (mb, CEE_LDARGA); + mono_mb_emit_i2 (mb, argnum); + } +} + +/** + * mono_mb_emit_ldloc_addr: + */ +void +mono_mb_emit_ldloc_addr (MonoMethodBuilder *mb, guint locnum) +{ + if (locnum < 256) { + mono_mb_emit_byte (mb, CEE_LDLOCA_S); + mono_mb_emit_byte (mb, locnum); + } else { + mono_mb_emit_byte (mb, CEE_PREFIX1); + mono_mb_emit_byte (mb, CEE_LDLOCA); + mono_mb_emit_i2 (mb, locnum); + } +} + +/** + * mono_mb_emit_ldloc: + */ +void +mono_mb_emit_ldloc (MonoMethodBuilder *mb, guint num) +{ + if (num < 4) { + mono_mb_emit_byte (mb, CEE_LDLOC_0 + num); + } else if (num < 256) { + mono_mb_emit_byte (mb, CEE_LDLOC_S); + mono_mb_emit_byte (mb, num); + } else { + mono_mb_emit_byte (mb, CEE_PREFIX1); + mono_mb_emit_byte (mb, CEE_LDLOC); + mono_mb_emit_i2 (mb, num); + } +} + +/** + * mono_mb_emit_stloc: + */ +void +mono_mb_emit_stloc (MonoMethodBuilder *mb, guint num) +{ + if (num < 4) { + mono_mb_emit_byte (mb, CEE_STLOC_0 + num); + } else if (num < 256) { + mono_mb_emit_byte (mb, CEE_STLOC_S); + mono_mb_emit_byte (mb, num); + } else { + mono_mb_emit_byte (mb, CEE_PREFIX1); + mono_mb_emit_byte (mb, CEE_STLOC); + mono_mb_emit_i2 (mb, num); + } +} + +/** + * mono_mb_emit_icon: + */ +void +mono_mb_emit_icon (MonoMethodBuilder *mb, gint32 value) +{ + if (value >= -1 && value < 8) { + mono_mb_emit_byte (mb, CEE_LDC_I4_0 + value); + } else if (value >= -128 && value <= 127) { + mono_mb_emit_byte (mb, CEE_LDC_I4_S); + mono_mb_emit_byte (mb, value); + } else { + mono_mb_emit_byte (mb, CEE_LDC_I4); + mono_mb_emit_i4 (mb, value); + } +} + +void +mono_mb_emit_icon8 (MonoMethodBuilder *mb, gint64 value) +{ + mono_mb_emit_byte (mb, CEE_LDC_I8); + mono_mb_emit_i8 (mb, value); +} + +int +mono_mb_get_label (MonoMethodBuilder *mb) +{ + return mb->pos; +} + +int +mono_mb_get_pos (MonoMethodBuilder *mb) +{ + return mb->pos; +} + +/** + * mono_mb_emit_branch: + */ +guint32 +mono_mb_emit_branch (MonoMethodBuilder *mb, guint8 op) +{ + guint32 res; + mono_mb_emit_byte (mb, op); + res = mb->pos; + mono_mb_emit_i4 (mb, 0); + return res; +} + +guint32 +mono_mb_emit_short_branch (MonoMethodBuilder *mb, guint8 op) +{ + guint32 res; + mono_mb_emit_byte (mb, op); + res = mb->pos; + mono_mb_emit_byte (mb, 0); + + return res; +} + +void +mono_mb_emit_branch_label (MonoMethodBuilder *mb, guint8 op, guint32 label) +{ + mono_mb_emit_byte (mb, op); + mono_mb_emit_i4 (mb, label - (mb->pos + 4)); +} + +void +mono_mb_patch_branch (MonoMethodBuilder *mb, guint32 pos) +{ + mono_mb_patch_addr (mb, pos, mb->pos - (pos + 4)); +} + +void +mono_mb_patch_short_branch (MonoMethodBuilder *mb, guint32 pos) +{ + mono_mb_patch_addr_s (mb, pos, mb->pos - (pos + 1)); +} + +void +mono_mb_emit_ptr (MonoMethodBuilder *mb, gpointer ptr) +{ + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_op (mb, CEE_MONO_LDPTR, ptr); +} + +void +mono_mb_emit_calli (MonoMethodBuilder *mb, MonoMethodSignature *sig) +{ + mono_mb_emit_op (mb, CEE_CALLI, sig); +} + +/** + * mono_mb_emit_managed_call: + */ +void +mono_mb_emit_managed_call (MonoMethodBuilder *mb, MonoMethod *method, MonoMethodSignature *opt_sig) +{ + mono_mb_emit_op (mb, CEE_CALL, method); +} + +/** + * mono_mb_emit_native_call: + */ +void +mono_mb_emit_native_call (MonoMethodBuilder *mb, MonoMethodSignature *sig, gpointer func) +{ + mono_mb_emit_ptr (mb, func); + mono_mb_emit_calli (mb, sig); +} + +void +mono_mb_emit_icall (MonoMethodBuilder *mb, gpointer func) +{ + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_op (mb, CEE_MONO_ICALL, func); +} + +void +mono_mb_emit_exception_full (MonoMethodBuilder *mb, const char *exc_nspace, const char *exc_name, const char *msg) +{ + MonoMethod *ctor = NULL; + + MonoClass *mme = mono_class_load_from_name (mono_defaults.corlib, exc_nspace, exc_name); + mono_class_init (mme); + ctor = mono_class_get_method_from_name (mme, ".ctor", 0); + g_assert (ctor); + mono_mb_emit_op (mb, CEE_NEWOBJ, ctor); + if (msg != NULL) { + mono_mb_emit_byte (mb, CEE_DUP); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoException, message)); + mono_mb_emit_ldstr (mb, (char*)msg); + mono_mb_emit_byte (mb, CEE_STIND_REF); + } + mono_mb_emit_byte (mb, CEE_THROW); +} + +/** + * mono_mb_emit_exception: + */ +void +mono_mb_emit_exception (MonoMethodBuilder *mb, const char *exc_name, const char *msg) +{ + mono_mb_emit_exception_full (mb, "System", exc_name, msg); +} + +/** + * mono_mb_emit_exception_for_error: + */ +void +mono_mb_emit_exception_for_error (MonoMethodBuilder *mb, MonoError *error) +{ + /* + * If at some point there is need to support other types of errors, + * the behaviour should conform with mono_error_prepare_exception(). + */ + g_assert (mono_error_get_error_code (error) == MONO_ERROR_GENERIC && "Unsupported error code."); + mono_mb_emit_exception_full (mb, "System", mono_error_get_exception_name (error), mono_error_get_message (error)); +} + +/** + * mono_mb_emit_add_to_local: + */ +void +mono_mb_emit_add_to_local (MonoMethodBuilder *mb, guint16 local, gint32 incr) +{ + mono_mb_emit_ldloc (mb, local); + mono_mb_emit_icon (mb, incr); + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_stloc (mb, local); +} + +void +mono_mb_set_clauses (MonoMethodBuilder *mb, int num_clauses, MonoExceptionClause *clauses) +{ + mb->num_clauses = num_clauses; + mb->clauses = clauses; +} + +/* + * mono_mb_set_param_names: + * + * PARAM_NAMES should have length equal to the sig->param_count, the caller retains + * ownership of the array, and its entries. + */ +void +mono_mb_set_param_names (MonoMethodBuilder *mb, const char **param_names) +{ + mb->param_names = param_names; +} + +#endif /* DISABLE_JIT */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/method-builder.h b/unity-2019.4.24f1-mbe/mono/metadata/method-builder.h new file mode 100644 index 000000000..92d71dd4e --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/method-builder.h @@ -0,0 +1,167 @@ +/** + * \file + * Functions for creating IL methods at runtime. + * + * Author: + * Paolo Molaro (lupus@ximian.com) + * + * (C) 2002 Ximian, Inc. http://www.ximian.com + * + */ + +#ifndef __MONO_METHOD_BUILDER_H__ +#define __MONO_METHOD_BUILDER_H__ + +#include "config.h" +#include +#include +#include +#include +#include + +G_BEGIN_DECLS + +typedef struct _MonoMethodBuilder { + MonoMethod *method; + char *name; + gboolean no_dup_name; +#ifdef ENABLE_ILGEN + GList *locals_list; + int locals; + gboolean dynamic; + gboolean skip_visibility, init_locals; + guint32 code_size, pos; + unsigned char *code; + int num_clauses; + MonoExceptionClause *clauses; + const char **param_names; +#endif +} MonoMethodBuilder; + +MonoMethodBuilder * +mono_mb_new (MonoClass *klass, const char *name, MonoWrapperType type); + +MonoMethodBuilder * +mono_mb_new_no_dup_name (MonoClass *klass, const char *name, MonoWrapperType type); + +void +mono_mb_free (MonoMethodBuilder *mb); + +MonoMethod * +mono_mb_create_method (MonoMethodBuilder *mb, MonoMethodSignature *signature, int max_stack); + +guint32 +mono_mb_add_data (MonoMethodBuilder *mb, gpointer data); + +#ifdef ENABLE_ILGEN +void +mono_mb_patch_addr (MonoMethodBuilder *mb, int pos, int value); + +void +mono_mb_patch_addr_s (MonoMethodBuilder *mb, int pos, gint8 value); + +void +mono_mb_patch_branch (MonoMethodBuilder *mb, guint32 pos); + +void +mono_mb_patch_short_branch (MonoMethodBuilder *mb, guint32 pos); + +int +mono_mb_get_label (MonoMethodBuilder *mb); + +int +mono_mb_get_pos (MonoMethodBuilder *mb); + +void +mono_mb_emit_ptr (MonoMethodBuilder *mb, gpointer ptr); + +void +mono_mb_emit_calli (MonoMethodBuilder *mb, MonoMethodSignature *sig); + +void +mono_mb_emit_native_call (MonoMethodBuilder *mb, MonoMethodSignature *sig, gpointer func); + +void +mono_mb_emit_managed_call (MonoMethodBuilder *mb, MonoMethod *method, MonoMethodSignature *opt_sig); + +void +mono_mb_emit_icall (MonoMethodBuilder *mb, gpointer func); + +int +mono_mb_add_local (MonoMethodBuilder *mb, MonoType *type); + +void +mono_mb_emit_ldarg (MonoMethodBuilder *mb, guint argnum); + +void +mono_mb_emit_ldarg_addr (MonoMethodBuilder *mb, guint argnum); + +void +mono_mb_emit_ldloc (MonoMethodBuilder *mb, guint num); + +void +mono_mb_emit_ldloc_addr (MonoMethodBuilder *mb, guint locnum); + +void +mono_mb_emit_stloc (MonoMethodBuilder *mb, guint num); + +void +mono_mb_emit_exception (MonoMethodBuilder *mb, const char *exc_name, const char *msg); + +void +mono_mb_emit_exception_full (MonoMethodBuilder *mb, const char *exc_nspace, const char *exc_name, const char *msg); + +void +mono_mb_emit_exception_for_error (MonoMethodBuilder *mb, MonoError *error); + +void +mono_mb_emit_icon (MonoMethodBuilder *mb, gint32 value); + +void +mono_mb_emit_icon8 (MonoMethodBuilder *mb, gint64 value); + +guint32 +mono_mb_emit_branch (MonoMethodBuilder *mb, guint8 op); + +guint32 +mono_mb_emit_short_branch (MonoMethodBuilder *mb, guint8 op); + +void +mono_mb_emit_branch_label (MonoMethodBuilder *mb, guint8 op, guint32 label); + +void +mono_mb_emit_add_to_local (MonoMethodBuilder *mb, guint16 local, gint32 incr); + +void +mono_mb_emit_ldflda (MonoMethodBuilder *mb, gint32 offset); + +void +mono_mb_emit_byte (MonoMethodBuilder *mb, guint8 op); + +void +mono_mb_emit_i2 (MonoMethodBuilder *mb, gint16 data); + +void +mono_mb_emit_i4 (MonoMethodBuilder *mb, gint32 data); + +void +mono_mb_emit_i8 (MonoMethodBuilder *mb, gint64 data); + +void +mono_mb_emit_op (MonoMethodBuilder *mb, guint8 op, gpointer data); + +void +mono_mb_emit_ldstr (MonoMethodBuilder *mb, char *str); + +void +mono_mb_set_clauses (MonoMethodBuilder *mb, int num_clauses, MonoExceptionClause *clauses); + +void +mono_mb_set_param_names (MonoMethodBuilder *mb, const char **param_names); + +#endif + +G_END_DECLS + +#endif /* __MONO_METHOD_BUILDER_H__ */ + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/monitor.c b/unity-2019.4.24f1-mbe/mono/metadata/monitor.c new file mode 100644 index 000000000..6642edbf7 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/monitor.c @@ -0,0 +1,1456 @@ +/** + * \file + * Monitor locking functions + * + * Author: + * Dick Porter (dick@ximian.com) + * + * Copyright 2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Pull the list of opcodes + */ +#define OPDEF(a,b,c,d,e,f,g,h,i,j) \ + a = i, + +enum { +#include "mono/cil/opcode.def" + LAST = 0xff +}; +#undef OPDEF + +/*#define LOCK_DEBUG(a) do { a; } while (0)*/ +#define LOCK_DEBUG(a) + +/* + * The monitor implementation here is based on + * http://www.usenix.org/events/jvm01/full_papers/dice/dice.pdf and + * http://www.research.ibm.com/people/d/dfb/papers/Bacon98Thin.ps + * + * The Dice paper describes a technique for saving lock record space + * by returning records to a free list when they become unused. That + * sounds like unnecessary complexity to me, though if it becomes + * clear that unused lock records are taking up lots of space or we + * need to shave more time off by avoiding a malloc then we can always + * implement the free list idea later. The timeout parameter to + * try_enter voids some of the assumptions about the reference count + * field in Dice's implementation too. In his version, the thread + * attempting to lock a contended object will block until it succeeds, + * so the reference count will never be decremented while an object is + * locked. + * + * Bacon's thin locks have a fast path that doesn't need a lock record + * for the common case of locking an unlocked or shallow-nested + * object. + */ + + +typedef struct _MonitorArray MonitorArray; + +struct _MonitorArray { + MonitorArray *next; + int num_monitors; + MonoThreadsSync monitors [MONO_ZERO_LEN_ARRAY]; +}; + +#define mono_monitor_allocator_lock() mono_os_mutex_lock (&monitor_mutex) +#define mono_monitor_allocator_unlock() mono_os_mutex_unlock (&monitor_mutex) +static mono_mutex_t monitor_mutex; +static MonoThreadsSync *monitor_freelist; +static MonitorArray *monitor_allocated; +static int array_size = 16; + +/* MonoThreadsSync status helpers */ + +static inline guint32 +mon_status_get_owner (guint32 status) +{ + return status & OWNER_MASK; +} + +static inline guint32 +mon_status_set_owner (guint32 status, guint32 owner) +{ + return (status & ENTRY_COUNT_MASK) | owner; +} + +static inline gint32 +mon_status_get_entry_count (guint32 status) +{ + gint32 entry_count = (gint32)((status & ENTRY_COUNT_MASK) >> ENTRY_COUNT_SHIFT); + gint32 zero = (gint32)(((guint32)ENTRY_COUNT_ZERO) >> ENTRY_COUNT_SHIFT); + return entry_count - zero; +} + +static inline guint32 +mon_status_init_entry_count (guint32 status) +{ + return (status & OWNER_MASK) | ENTRY_COUNT_ZERO; +} + +static inline guint32 +mon_status_increment_entry_count (guint32 status) +{ + return status + (1 << ENTRY_COUNT_SHIFT); +} + +static inline guint32 +mon_status_decrement_entry_count (guint32 status) +{ + return status - (1 << ENTRY_COUNT_SHIFT); +} + +static inline gboolean +mon_status_have_waiters (guint32 status) +{ + return status & ENTRY_COUNT_WAITERS; +} + +/* LockWord helpers */ + +static inline MonoThreadsSync* +lock_word_get_inflated_lock (LockWord lw) +{ + lw.lock_word &= (~LOCK_WORD_STATUS_MASK); + return lw.sync; +} + +static inline gboolean +lock_word_is_inflated (LockWord lw) +{ + return lw.lock_word & LOCK_WORD_INFLATED; +} + +static inline gboolean +lock_word_has_hash (LockWord lw) +{ + return lw.lock_word & LOCK_WORD_HAS_HASH; +} + +static inline LockWord +lock_word_set_has_hash (LockWord lw) +{ + LockWord nlw; + nlw.lock_word = lw.lock_word | LOCK_WORD_HAS_HASH; + return nlw; +} + +static inline gboolean +lock_word_is_free (LockWord lw) +{ + return !lw.lock_word; +} + +static inline gboolean +lock_word_is_flat (LockWord lw) +{ + /* Return whether the lock is flat or free */ + return (lw.lock_word & LOCK_WORD_STATUS_MASK) == LOCK_WORD_FLAT; +} + +static inline gint32 +lock_word_get_hash (LockWord lw) +{ + return (gint32) (lw.lock_word >> LOCK_WORD_HASH_SHIFT); +} + +static inline gint32 +lock_word_get_nest (LockWord lw) +{ + if (lock_word_is_free (lw)) + return 0; + /* Inword nest count starts from 0 */ + return ((lw.lock_word & LOCK_WORD_NEST_MASK) >> LOCK_WORD_NEST_SHIFT) + 1; +} + +static inline gboolean +lock_word_is_nested (LockWord lw) +{ + return lw.lock_word & LOCK_WORD_NEST_MASK; +} + +static inline gboolean +lock_word_is_max_nest (LockWord lw) +{ + return (lw.lock_word & LOCK_WORD_NEST_MASK) == LOCK_WORD_NEST_MASK; +} + +static inline LockWord +lock_word_increment_nest (LockWord lw) +{ + lw.lock_word += 1 << LOCK_WORD_NEST_SHIFT; + return lw; +} + +static inline LockWord +lock_word_decrement_nest (LockWord lw) +{ + lw.lock_word -= 1 << LOCK_WORD_NEST_SHIFT; + return lw; +} + +static inline gint32 +lock_word_get_owner (LockWord lw) +{ + return lw.lock_word >> LOCK_WORD_OWNER_SHIFT; +} + +static inline LockWord +lock_word_new_thin_hash (gint32 hash) +{ + LockWord lw; + lw.lock_word = (guint32)hash; + lw.lock_word = (lw.lock_word << LOCK_WORD_HASH_SHIFT) | LOCK_WORD_HAS_HASH; + return lw; +} + +static inline LockWord +lock_word_new_inflated (MonoThreadsSync *mon) +{ + LockWord lw; + lw.sync = mon; + lw.lock_word |= LOCK_WORD_INFLATED; + return lw; +} + +static inline LockWord +lock_word_new_flat (gint32 owner) +{ + LockWord lw; + lw.lock_word = owner; + lw.lock_word <<= LOCK_WORD_OWNER_SHIFT; + return lw; +} + +void +mono_monitor_init (void) +{ + mono_os_mutex_init_recursive (&monitor_mutex); +} + +void +mono_monitor_cleanup (void) +{ + MonoThreadsSync *mon; + /* MonitorArray *marray, *next = NULL; */ + + /*mono_os_mutex_destroy (&monitor_mutex);*/ + + /* The monitors on the freelist don't have weak links - mark them */ + for (mon = monitor_freelist; mon; mon = (MonoThreadsSync *)mon->data) + mon->wait_list = (GSList *)-1; + + /* + * FIXME: This still crashes with sgen (async_read.exe) + * + * In mini_cleanup() we first call mono_runtime_cleanup(), which calls + * mono_monitor_cleanup(), which is supposed to free all monitor memory. + * + * Later in mini_cleanup(), we call mono_domain_free(), which calls + * mono_gc_clear_domain(), which frees all weak links associated with objects. + * Those weak links reside in the monitor structures, which we've freed earlier. + * + * Unless we fix this dependency in the shutdown sequence this code has to remain + * disabled, or at least the call to g_free(). + */ + /* + for (marray = monitor_allocated; marray; marray = next) { + int i; + + for (i = 0; i < marray->num_monitors; ++i) { + mon = &marray->monitors [i]; + if (mon->wait_list != (gpointer)-1) + mono_gc_weak_link_remove (&mon->data); + } + + next = marray->next; + g_free (marray); + } + */ +} + +static int +monitor_is_on_freelist (MonoThreadsSync *mon) +{ + MonitorArray *marray; + for (marray = monitor_allocated; marray; marray = marray->next) { + if (mon >= marray->monitors && mon < &marray->monitors [marray->num_monitors]) + return TRUE; + } + return FALSE; +} + +/** + * mono_locks_dump: + * \param include_untaken Whether to list unheld inflated locks. + * Print a report on stdout of the managed locks currently held by + * threads. If \p include_untaken is specified, list also inflated locks + * which are unheld. + * This is supposed to be used in debuggers like gdb. + */ +void +mono_locks_dump (gboolean include_untaken) +{ + int i; + int used = 0, on_freelist = 0, to_recycle = 0, total = 0, num_arrays = 0; + MonoThreadsSync *mon; + MonitorArray *marray; + for (mon = monitor_freelist; mon; mon = (MonoThreadsSync *)mon->data) + on_freelist++; + for (marray = monitor_allocated; marray; marray = marray->next) { + total += marray->num_monitors; + num_arrays++; + for (i = 0; i < marray->num_monitors; ++i) { + mon = &marray->monitors [i]; + if (mon->data == NULL) { + if (i < marray->num_monitors - 1) + to_recycle++; + } else { + if (!monitor_is_on_freelist ((MonoThreadsSync *)mon->data)) { + MonoObject *holder = (MonoObject *)mono_gchandle_get_target ((guint32)mon->data); + if (mon_status_get_owner (mon->status)) { + g_print ("Lock %p in object %p held by thread %d, nest level: %d\n", + mon, holder, mon_status_get_owner (mon->status), mon->nest); + if (mon->entry_sem) + g_print ("\tWaiting on semaphore %p: %d\n", mon->entry_sem, mon_status_get_entry_count (mon->status)); + } else if (include_untaken) { + g_print ("Lock %p in object %p untaken\n", mon, holder); + } + used++; + } + } + } + } + g_print ("Total locks (in %d array(s)): %d, used: %d, on freelist: %d, to recycle: %d\n", + num_arrays, total, used, on_freelist, to_recycle); +} + +/* LOCKING: this is called with monitor_mutex held */ +static void +mon_finalize (MonoThreadsSync *mon) +{ + LOCK_DEBUG (g_message ("%s: Finalizing sync %p", __func__, mon)); + + if (mon->entry_sem != NULL) { + mono_coop_sem_destroy (mon->entry_sem); + g_free (mon->entry_sem); + mon->entry_sem = NULL; + } + /* If this isn't empty then something is seriously broken - it + * means a thread is still waiting on the object that owned + * this lock, but the object has been finalized. + */ + g_assert (mon->wait_list == NULL); + + /* owner and nest are set in mon_new, no need to zero them out */ + + mon->data = monitor_freelist; + monitor_freelist = mon; +#ifndef DISABLE_PERFCOUNTERS + mono_atomic_dec_i32 (&mono_perfcounters->gc_sync_blocks); +#endif +} + +/* LOCKING: this is called with monitor_mutex held */ +static MonoThreadsSync * +mon_new (gsize id) +{ + MonoThreadsSync *new_; + + if (!monitor_freelist) { + MonitorArray *marray; + int i; + /* see if any sync block has been collected */ + new_ = NULL; + for (marray = monitor_allocated; marray; marray = marray->next) { + for (i = 0; i < marray->num_monitors; ++i) { + if (mono_gchandle_get_target ((guint32)marray->monitors [i].data) == NULL) { + new_ = &marray->monitors [i]; + if (new_->wait_list) { + /* Orphaned events left by aborted threads */ + while (new_->wait_list) { + LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d): Closing orphaned event %d", mono_thread_info_get_small_id (), new_->wait_list->data)); + mono_w32event_close (new_->wait_list->data); + new_->wait_list = g_slist_remove (new_->wait_list, new_->wait_list->data); + } + } + mono_gchandle_free ((guint32)new_->data); + new_->data = monitor_freelist; + monitor_freelist = new_; + } + } + /* small perf tweak to avoid scanning all the blocks */ + if (new_) + break; + } + /* need to allocate a new array of monitors */ + if (!monitor_freelist) { + MonitorArray *last; + LOCK_DEBUG (g_message ("%s: allocating more monitors: %d", __func__, array_size)); + marray = (MonitorArray *)g_malloc0 (MONO_SIZEOF_MONO_ARRAY + array_size * sizeof (MonoThreadsSync)); + marray->num_monitors = array_size; + array_size *= 2; + /* link into the freelist */ + for (i = 0; i < marray->num_monitors - 1; ++i) { + marray->monitors [i].data = &marray->monitors [i + 1]; + } + marray->monitors [i].data = NULL; /* the last one */ + monitor_freelist = &marray->monitors [0]; + /* we happend the marray instead of prepending so that + * the collecting loop above will need to scan smaller arrays first + */ + if (!monitor_allocated) { + monitor_allocated = marray; + } else { + last = monitor_allocated; + while (last->next) + last = last->next; + last->next = marray; + } + } + } + + new_ = monitor_freelist; + monitor_freelist = (MonoThreadsSync *)new_->data; + + new_->status = mon_status_set_owner (0, id); + new_->status = mon_status_init_entry_count (new_->status); + new_->nest = 1; + new_->data = NULL; + +#ifndef DISABLE_PERFCOUNTERS + mono_atomic_inc_i32 (&mono_perfcounters->gc_sync_blocks); +#endif + return new_; +} + +static MonoThreadsSync* +alloc_mon (MonoObject *obj, gint32 id) +{ + MonoThreadsSync *mon; + + mono_monitor_allocator_lock (); + mon = mon_new (id); + mon->data = (void *)(size_t)mono_gchandle_new_weakref (obj, TRUE); + mono_monitor_allocator_unlock (); + + return mon; +} + + +static void +discard_mon (MonoThreadsSync *mon) +{ + mono_monitor_allocator_lock (); + mono_gchandle_free ((guint32)mon->data); + mon_finalize (mon); + mono_monitor_allocator_unlock (); +} + +static void +mono_monitor_inflate_owned (MonoObject *obj, int id) +{ + MonoThreadsSync *mon; + LockWord nlw, old_lw, tmp_lw; + guint32 nest; + + old_lw.sync = obj->synchronisation; + LOCK_DEBUG (g_message ("%s: (%d) Inflating owned lock object %p; LW = %p", __func__, id, obj, old_lw.sync)); + + if (lock_word_is_inflated (old_lw)) { + /* Someone else inflated the lock in the meantime */ + return; + } + + mon = alloc_mon (obj, id); + + nest = lock_word_get_nest (old_lw); + mon->nest = nest; + + nlw = lock_word_new_inflated (mon); + + mono_memory_write_barrier (); + tmp_lw.sync = (MonoThreadsSync *)mono_atomic_cas_ptr ((gpointer*)&obj->synchronisation, nlw.sync, old_lw.sync); + if (tmp_lw.sync != old_lw.sync) { + /* Someone else inflated the lock in the meantime */ + discard_mon (mon); + } +} + +static void +mono_monitor_inflate (MonoObject *obj) +{ + MonoThreadsSync *mon; + LockWord nlw, old_lw; + + LOCK_DEBUG (g_message ("%s: (%d) Inflating lock object %p; LW = %p", __func__, mono_thread_info_get_small_id (), obj, obj->synchronisation)); + + mon = alloc_mon (obj, 0); + + nlw = lock_word_new_inflated (mon); + + old_lw.sync = obj->synchronisation; + + for (;;) { + LockWord tmp_lw; + + if (lock_word_is_inflated (old_lw)) { + break; + } +#ifdef HAVE_MOVING_COLLECTOR + else if (lock_word_has_hash (old_lw)) { + mon->hash_code = lock_word_get_hash (old_lw); + mon->status = mon_status_set_owner (mon->status, 0); + nlw = lock_word_set_has_hash (nlw); + } +#endif + else if (lock_word_is_free (old_lw)) { + mon->status = mon_status_set_owner (mon->status, 0); + mon->nest = 1; + } else { + /* Lock is flat */ + mon->status = mon_status_set_owner (mon->status, lock_word_get_owner (old_lw)); + mon->nest = lock_word_get_nest (old_lw); + } + mono_memory_write_barrier (); + tmp_lw.sync = (MonoThreadsSync *)mono_atomic_cas_ptr ((gpointer*)&obj->synchronisation, nlw.sync, old_lw.sync); + if (tmp_lw.sync == old_lw.sync) { + /* Successfully inflated the lock */ + return; + } + + old_lw.sync = tmp_lw.sync; + } + + /* Someone else inflated the lock before us */ + discard_mon (mon); +} + +#define MONO_OBJECT_ALIGNMENT_SHIFT 3 + +/* + * mono_object_hash: + * @obj: an object + * + * Calculate a hash code for @obj that is constant while @obj is alive. + */ +int +mono_object_hash (MonoObject* obj) +{ +#ifdef HAVE_MOVING_COLLECTOR + LockWord lw; + unsigned int hash; + if (!obj) + return 0; + lw.sync = obj->synchronisation; + + LOCK_DEBUG (g_message("%s: (%d) Get hash for object %p; LW = %p", __func__, mono_thread_info_get_small_id (), obj, obj->synchronisation)); + + if (lock_word_has_hash (lw)) { + if (lock_word_is_inflated (lw)) { + return lock_word_get_inflated_lock (lw)->hash_code; + } else { + return lock_word_get_hash (lw); + } + } + /* + * while we are inside this function, the GC will keep this object pinned, + * since we are in the unmanaged stack. Thanks to this and to the hash + * function that depends only on the address, we can ignore the races if + * another thread computes the hash at the same time, because it'll end up + * with the same value. + */ + hash = (GPOINTER_TO_UINT (obj) >> MONO_OBJECT_ALIGNMENT_SHIFT) * 2654435761u; +#if SIZEOF_VOID_P == 4 + /* clear the top bits as they can be discarded */ + hash &= ~(LOCK_WORD_STATUS_MASK << (32 - LOCK_WORD_STATUS_BITS)); +#endif + if (lock_word_is_free (lw)) { + LockWord old_lw; + lw = lock_word_new_thin_hash (hash); + + old_lw.sync = (MonoThreadsSync *)mono_atomic_cas_ptr ((gpointer*)&obj->synchronisation, lw.sync, NULL); + if (old_lw.sync == NULL) { + return hash; + } + + if (lock_word_has_hash (old_lw)) { + /* Done by somebody else */ + return hash; + } + + mono_monitor_inflate (obj); + lw.sync = obj->synchronisation; + } else if (lock_word_is_flat (lw)) { + int id = mono_thread_info_get_small_id (); + if (lock_word_get_owner (lw) == id) + mono_monitor_inflate_owned (obj, id); + else + mono_monitor_inflate (obj); + lw.sync = obj->synchronisation; + } + + /* At this point, the lock is inflated */ + lock_word_get_inflated_lock (lw)->hash_code = hash; + lw = lock_word_set_has_hash (lw); + mono_memory_write_barrier (); + obj->synchronisation = lw.sync; + return hash; +#else +/* + * Wang's address-based hash function: + * http://www.concentric.net/~Ttwang/tech/addrhash.htm + */ + return (GPOINTER_TO_UINT (obj) >> MONO_OBJECT_ALIGNMENT_SHIFT) * 2654435761u; +#endif +} + +static gboolean +mono_monitor_ensure_owned (LockWord lw, guint32 id) +{ + if (lock_word_is_flat (lw)) { + if (lock_word_get_owner (lw) == id) + return TRUE; + } else if (lock_word_is_inflated (lw)) { + if (mon_status_get_owner (lock_word_get_inflated_lock (lw)->status) == id) + return TRUE; + } + + mono_set_pending_exception (mono_get_exception_synchronization_lock ("Object synchronization method was called from an unsynchronized block of code.")); + return FALSE; +} + +/* + * When this function is called it has already been established that the + * current thread owns the monitor. + */ +static void +mono_monitor_exit_inflated (MonoObject *obj) +{ + LockWord lw; + MonoThreadsSync *mon; + guint32 nest; + + lw.sync = obj->synchronisation; + mon = lock_word_get_inflated_lock (lw); + + nest = mon->nest - 1; + if (nest == 0) { + guint32 new_status, old_status, tmp_status; + + old_status = mon->status; + + /* + * Release lock and do the wakeup stuff. It's possible that + * the last blocking thread gave up waiting just before we + * release the semaphore resulting in a negative entry count + * and a futile wakeup next time there's contention for this + * object. + */ + for (;;) { + gboolean have_waiters = mon_status_have_waiters (old_status); + + new_status = mon_status_set_owner (old_status, 0); + if (have_waiters) + new_status = mon_status_decrement_entry_count (new_status); + tmp_status = mono_atomic_cas_i32 ((gint32*)&mon->status, new_status, old_status); + if (tmp_status == old_status) { + if (have_waiters) + mono_coop_sem_post (mon->entry_sem); + break; + } + old_status = tmp_status; + } + LOCK_DEBUG (g_message ("%s: (%d) Object %p is now unlocked", __func__, mono_thread_info_get_small_id (), obj)); + + /* object is now unlocked, leave nest==1 so we don't + * need to set it when the lock is reacquired + */ + } else { + LOCK_DEBUG (g_message ("%s: (%d) Object %p is now locked %d times", __func__, mono_thread_info_get_small_id (), obj, nest)); + mon->nest = nest; + } +} + +/* + * When this function is called it has already been established that the + * current thread owns the monitor. + */ +static void +mono_monitor_exit_flat (MonoObject *obj, LockWord old_lw) +{ + LockWord new_lw, tmp_lw; + if (G_UNLIKELY (lock_word_is_nested (old_lw))) + new_lw = lock_word_decrement_nest (old_lw); + else + new_lw.lock_word = 0; + + tmp_lw.sync = (MonoThreadsSync *)mono_atomic_cas_ptr ((gpointer*)&obj->synchronisation, new_lw.sync, old_lw.sync); + if (old_lw.sync != tmp_lw.sync) { + /* Someone inflated the lock in the meantime */ + mono_monitor_exit_inflated (obj); + } + + LOCK_DEBUG (g_message ("%s: (%d) Object %p is now locked %d times; LW = %p", __func__, mono_thread_info_get_small_id (), obj, lock_word_get_nest (new_lw), obj->synchronisation)); +} + +static void +mon_decrement_entry_count (MonoThreadsSync *mon) +{ + guint32 old_status, tmp_status, new_status; + + /* Decrement entry count */ + old_status = mon->status; + for (;;) { + new_status = mon_status_decrement_entry_count (old_status); + tmp_status = mono_atomic_cas_i32 ((gint32*)&mon->status, new_status, old_status); + if (tmp_status == old_status) { + break; + } + old_status = tmp_status; + } +} + +/* If allow_interruption==TRUE, the method will be interrumped if abort or suspend + * is requested. In this case it returns -1. + */ +static inline gint32 +mono_monitor_try_enter_inflated (MonoObject *obj, guint32 ms, gboolean allow_interruption, guint32 id) +{ + LockWord lw; + MonoThreadsSync *mon; + HANDLE sem; + gint64 then = 0, now, delta; + guint32 waitms; + guint32 new_status, old_status, tmp_status; + MonoSemTimedwaitRet wait_ret; + MonoInternalThread *thread; + gboolean interrupted = FALSE; + + LOCK_DEBUG (g_message("%s: (%d) Trying to lock object %p (%d ms)", __func__, id, obj, ms)); + + if (G_UNLIKELY (!obj)) { + mono_set_pending_exception (mono_get_exception_argument_null ("obj")); + return FALSE; + } + + lw.sync = obj->synchronisation; + mon = lock_word_get_inflated_lock (lw); +retry: + /* This case differs from Dice's case 3 because we don't + * deflate locks or cache unused lock records + */ + old_status = mon->status; + if (G_LIKELY (mon_status_get_owner (old_status) == 0)) { + /* Try to install our ID in the owner field, nest + * should have been left at 1 by the previous unlock + * operation + */ + new_status = mon_status_set_owner (old_status, id); + tmp_status = mono_atomic_cas_i32 ((gint32*)&mon->status, new_status, old_status); + if (G_LIKELY (tmp_status == old_status)) { + /* Success */ + g_assert (mon->nest == 1); + return 1; + } else { + /* Trumped again! */ + goto retry; + } + } + + /* If the object is currently locked by this thread... */ + if (mon_status_get_owner (old_status) == id) { + mon->nest++; + return 1; + } + + /* The object must be locked by someone else... */ +#ifndef DISABLE_PERFCOUNTERS + mono_atomic_inc_i32 (&mono_perfcounters->thread_contentions); +#endif + + /* If ms is 0 we don't block, but just fail straight away */ + if (ms == 0) { + LOCK_DEBUG (g_message ("%s: (%d) timed out, returning FALSE", __func__, id)); + return 0; + } + + MONO_PROFILER_RAISE (monitor_contention, (obj)); + + /* The slow path begins here. */ +retry_contended: + /* a small amount of duplicated code, but it allows us to insert the profiler + * callbacks without impacting the fast path: from here on we don't need to go back to the + * retry label, but to retry_contended. At this point mon is already installed in the object + * header. + */ + /* This case differs from Dice's case 3 because we don't + * deflate locks or cache unused lock records + */ + old_status = mon->status; + if (G_LIKELY (mon_status_get_owner (old_status) == 0)) { + /* Try to install our ID in the owner field, nest + * should have been left at 1 by the previous unlock + * operation + */ + new_status = mon_status_set_owner (old_status, id); + tmp_status = mono_atomic_cas_i32 ((gint32*)&mon->status, new_status, old_status); + if (G_LIKELY (tmp_status == old_status)) { + /* Success */ + g_assert (mon->nest == 1); + MONO_PROFILER_RAISE (monitor_acquired, (obj)); + return 1; + } + } + + /* If the object is currently locked by this thread... */ + if (mon_status_get_owner (old_status) == id) { + mon->nest++; + MONO_PROFILER_RAISE (monitor_acquired, (obj)); + return 1; + } + + /* We need to make sure there's a semaphore handle (creating it if + * necessary), and block on it + */ + if (mon->entry_sem == NULL) { + /* Create the semaphore */ + sem = g_new0 (MonoCoopSem, 1); + mono_coop_sem_init (sem, 0); + if (mono_atomic_cas_ptr ((gpointer*)&mon->entry_sem, sem, NULL) != NULL) { + /* Someone else just put a handle here */ + mono_coop_sem_destroy (sem); + g_free (sem); + } + } + + /* + * We need to register ourselves as waiting if it is the first time we are waiting, + * of if we were signaled and failed to acquire the lock. + */ + if (!interrupted) { + old_status = mon->status; + for (;;) { + if (mon_status_get_owner (old_status) == 0) + goto retry_contended; + new_status = mon_status_increment_entry_count (old_status); + tmp_status = mono_atomic_cas_i32 ((gint32*)&mon->status, new_status, old_status); + if (tmp_status == old_status) { + break; + } + old_status = tmp_status; + } + } + + if (ms != MONO_INFINITE_WAIT) { + then = mono_msec_ticks (); + } + waitms = ms; + +#ifndef DISABLE_PERFCOUNTERS + mono_atomic_inc_i32 (&mono_perfcounters->thread_queue_len); + mono_atomic_inc_i32 (&mono_perfcounters->thread_queue_max); +#endif + thread = mono_thread_internal_current (); + + /* + * If we allow interruption, we check the test state for an abort request before going into sleep. + * This is a workaround to the fact that Thread.Abort does non-sticky interruption of semaphores. + * + * Semaphores don't support the sticky interruption with mono_thread_info_install_interrupt. + * + * A better fix would be to switch to wait with something that allows sticky interrupts together + * with wrapping it with abort_protected_block_count for the non-alertable cases. + * And somehow make this whole dance atomic and not crazy expensive. Good luck. + * + */ + if (allow_interruption) { + if (!mono_thread_test_and_set_state (thread, ThreadState_AbortRequested, ThreadState_WaitSleepJoin)) { + wait_ret = MONO_SEM_TIMEDWAIT_RET_ALERTED; + goto done_waiting; + } + } else { + mono_thread_set_state (thread, ThreadState_WaitSleepJoin); + } + + /* + * We pass ALERTABLE instead of allow_interruption since we have to check for the + * StopRequested case below. + */ + wait_ret = mono_coop_sem_timedwait (mon->entry_sem, waitms, MONO_SEM_FLAGS_ALERTABLE); + + mono_thread_clr_state (thread, ThreadState_WaitSleepJoin); + +done_waiting: +#ifndef DISABLE_PERFCOUNTERS + mono_atomic_dec_i32 (&mono_perfcounters->thread_queue_len); +#endif + + if (wait_ret == MONO_SEM_TIMEDWAIT_RET_ALERTED && !allow_interruption) { + interrupted = TRUE; + /* + * We have to obey a stop/suspend request even if + * allow_interruption is FALSE to avoid hangs at shutdown. + */ + if (!mono_thread_test_state (mono_thread_internal_current (), ThreadState_SuspendRequested | ThreadState_AbortRequested)) { + if (ms != MONO_INFINITE_WAIT) { + now = mono_msec_ticks (); + + /* it should not overflow before ~30k years */ + g_assert (now >= then); + + delta = now - then; + if (delta >= ms) { + ms = 0; + } else { + ms -= delta; + } + } + /* retry from the top */ + goto retry_contended; + } + } else if (wait_ret == MONO_SEM_TIMEDWAIT_RET_SUCCESS) { + interrupted = FALSE; + /* retry from the top */ + goto retry_contended; + } else if (wait_ret == MONO_SEM_TIMEDWAIT_RET_TIMEDOUT) { + /* we're done */ + } + + /* Timed out or interrupted */ + mon_decrement_entry_count (mon); + + MONO_PROFILER_RAISE (monitor_failed, (obj)); + + if (wait_ret == MONO_SEM_TIMEDWAIT_RET_ALERTED) { + LOCK_DEBUG (g_message ("%s: (%d) interrupted waiting, returning -1", __func__, id)); + return -1; + } else if (wait_ret == MONO_SEM_TIMEDWAIT_RET_TIMEDOUT) { + LOCK_DEBUG (g_message ("%s: (%d) timed out waiting, returning FALSE", __func__, id)); + return 0; + } else { + g_assert_not_reached (); + return 0; + } +} + +/* + * If allow_interruption == TRUE, the method will be interrupted if abort or suspend + * is requested. In this case it returns -1. + */ +static inline gint32 +mono_monitor_try_enter_internal (MonoObject *obj, guint32 ms, gboolean allow_interruption) +{ + LockWord lw; + int id = mono_thread_info_get_small_id (); + + LOCK_DEBUG (g_message("%s: (%d) Trying to lock object %p (%d ms)", __func__, id, obj, ms)); + + lw.sync = obj->synchronisation; + + if (G_LIKELY (lock_word_is_free (lw))) { + LockWord nlw = lock_word_new_flat (id); + if (mono_atomic_cas_ptr ((gpointer*)&obj->synchronisation, nlw.sync, NULL) == NULL) { + return 1; + } else { + /* Someone acquired it in the meantime or put a hash */ + mono_monitor_inflate (obj); + return mono_monitor_try_enter_inflated (obj, ms, allow_interruption, id); + } + } else if (lock_word_is_inflated (lw)) { + return mono_monitor_try_enter_inflated (obj, ms, allow_interruption, id); + } else if (lock_word_is_flat (lw)) { + if (lock_word_get_owner (lw) == id) { + if (lock_word_is_max_nest (lw)) { + mono_monitor_inflate_owned (obj, id); + return mono_monitor_try_enter_inflated (obj, ms, allow_interruption, id); + } else { + LockWord nlw, old_lw; + nlw = lock_word_increment_nest (lw); + old_lw.sync = (MonoThreadsSync *)mono_atomic_cas_ptr ((gpointer*)&obj->synchronisation, nlw.sync, lw.sync); + if (old_lw.sync != lw.sync) { + /* Someone else inflated it in the meantime */ + g_assert (lock_word_is_inflated (old_lw)); + return mono_monitor_try_enter_inflated (obj, ms, allow_interruption, id); + } + return 1; + } + } else { + mono_monitor_inflate (obj); + return mono_monitor_try_enter_inflated (obj, ms, allow_interruption, id); + } + } else if (lock_word_has_hash (lw)) { + mono_monitor_inflate (obj); + return mono_monitor_try_enter_inflated (obj, ms, allow_interruption, id); + } + + g_assert_not_reached (); + return -1; +} + +/* This is an icall */ +MonoBoolean +mono_monitor_enter_internal (MonoObject *obj) +{ + gint32 res; + gboolean allow_interruption = TRUE; + if (G_UNLIKELY (!obj)) { + mono_set_pending_exception (mono_get_exception_argument_null ("obj")); + return FALSE; + } + + /* + * An inquisitive mind could ask what's the deal with this loop. + * It exists to deal with interrupting a monitor enter that happened within an abort-protected block, like a .cctor. + * + * The thread will be set with a pending abort and the wait might even be interrupted. Either way, once we call mono_thread_interruption_checkpoint, + * it will return NULL meaning we can't be aborted right now. Once that happens we switch to non-alertable. + */ + do { + res = mono_monitor_try_enter_internal (obj, MONO_INFINITE_WAIT, allow_interruption); + /*This means we got interrupted during the wait and didn't got the monitor.*/ + if (res == -1) { + MonoException *exc = mono_thread_interruption_checkpoint (); + if (exc) { + mono_set_pending_exception (exc); + return FALSE; + } else { + //we detected a pending interruption but it turned out to be a false positive, we ignore it from now on (this feels like a hack, right?, threads.c should give us less confusing directions) + allow_interruption = FALSE; + } + } + } while (res == -1); + return TRUE; +} + +/** + * mono_monitor_enter: + */ +gboolean +mono_monitor_enter (MonoObject *obj) +{ + return mono_monitor_enter_internal (obj); +} + +/* Called from JITted code so we return guint32 instead of gboolean */ +guint32 +mono_monitor_enter_fast (MonoObject *obj) +{ + if (G_UNLIKELY (!obj)) { + /* don't set pending exn on the fast path, just return + * FALSE and let the slow path take care of it. */ + return FALSE; + } + return mono_monitor_try_enter_internal (obj, 0, FALSE) == 1; +} + +/** + * mono_monitor_try_enter: + */ +gboolean +mono_monitor_try_enter (MonoObject *obj, guint32 ms) +{ + if (G_UNLIKELY (!obj)) { + mono_set_pending_exception (mono_get_exception_argument_null ("obj")); + return FALSE; + } + return mono_monitor_try_enter_internal (obj, ms, FALSE) == 1; +} + +/** + * mono_monitor_exit: + */ +void +mono_monitor_exit (MonoObject *obj) +{ + LockWord lw; + + LOCK_DEBUG (g_message ("%s: (%d) Unlocking %p", __func__, mono_thread_info_get_small_id (), obj)); + + if (G_UNLIKELY (!obj)) { + mono_set_pending_exception (mono_get_exception_argument_null ("obj")); + return; + } + + lw.sync = obj->synchronisation; + + if (!mono_monitor_ensure_owned (lw, mono_thread_info_get_small_id ())) + return; + + if (G_UNLIKELY (lock_word_is_inflated (lw))) + mono_monitor_exit_inflated (obj); + else + mono_monitor_exit_flat (obj, lw); +} + +guint32 +mono_monitor_get_object_monitor_gchandle (MonoObject *object) +{ + LockWord lw; + + lw.sync = object->synchronisation; + + if (lock_word_is_inflated (lw)) { + MonoThreadsSync *mon = lock_word_get_inflated_lock (lw); + return (guint32)mon->data; + } + return 0; +} + +/* + * mono_monitor_threads_sync_member_offset: + * @status_offset: returns size and offset of the "status" member + * @nest_offset: returns size and offset of the "nest" member + * + * Returns the offsets and sizes of two members of the + * MonoThreadsSync struct. The Monitor ASM fastpaths need this. + */ +void +mono_monitor_threads_sync_members_offset (int *status_offset, int *nest_offset) +{ + MonoThreadsSync ts; + +#define ENCODE_OFF_SIZE(o,s) (((o) << 8) | ((s) & 0xff)) + + *status_offset = ENCODE_OFF_SIZE (MONO_STRUCT_OFFSET (MonoThreadsSync, status), sizeof (ts.status)); + *nest_offset = ENCODE_OFF_SIZE (MONO_STRUCT_OFFSET (MonoThreadsSync, nest), sizeof (ts.nest)); +} + +void +ves_icall_System_Threading_Monitor_Monitor_try_enter_with_atomic_var (MonoObject *obj, guint32 ms, MonoBoolean *lockTaken) +{ + gint32 res; + gboolean allow_interruption = TRUE; + if (G_UNLIKELY (!obj)) { + mono_set_pending_exception (mono_get_exception_argument_null ("obj")); + return; + } + do { + res = mono_monitor_try_enter_internal (obj, ms, allow_interruption); + /*This means we got interrupted during the wait and didn't got the monitor.*/ + if (res == -1) { + MonoException *exc = mono_thread_interruption_checkpoint (); + if (exc) { + mono_set_pending_exception (exc); + return; + } else { + //we detected a pending interruption but it turned out to be a false positive, we ignore it from now on (this feels like a hack, right?, threads.c should give us less confusing directions) + allow_interruption = FALSE; + } + } + } while (res == -1); + /*It's safe to do it from here since interruption would happen only on the wrapper.*/ + *lockTaken = res == 1; +} + +/** + * mono_monitor_enter_v4: + */ +void +mono_monitor_enter_v4 (MonoObject *obj, char *lock_taken) +{ + if (*lock_taken == 1) { + mono_set_pending_exception (mono_get_exception_argument ("lockTaken", "lockTaken is already true")); + return; + } + + MonoBoolean taken; + + ves_icall_System_Threading_Monitor_Monitor_try_enter_with_atomic_var (obj, MONO_INFINITE_WAIT, &taken); + *lock_taken = taken; +} + +/* Called from JITted code */ +void +mono_monitor_enter_v4_internal (MonoObject *obj, MonoBoolean *lock_taken) +{ + if (*lock_taken == 1) { + mono_set_pending_exception (mono_get_exception_argument ("lockTaken", "lockTaken is already true")); + return; + } + + ves_icall_System_Threading_Monitor_Monitor_try_enter_with_atomic_var (obj, MONO_INFINITE_WAIT, lock_taken); +} + +/* + * mono_monitor_enter_v4_fast: + * + * Same as mono_monitor_enter_v4, but return immediately if the + * monitor cannot be acquired. + * Returns TRUE if the lock was acquired, FALSE otherwise. + * Called from JITted code so we return guint32 instead of gboolean. + */ +guint32 +mono_monitor_enter_v4_fast (MonoObject *obj, MonoBoolean *lock_taken) +{ + if (*lock_taken == 1) + return FALSE; + if (G_UNLIKELY (!obj)) + return FALSE; + gint32 res = mono_monitor_try_enter_internal (obj, 0, TRUE); + *lock_taken = res == 1; + return res == 1; +} + +MonoBoolean +ves_icall_System_Threading_Monitor_Monitor_test_owner (MonoObject *obj) +{ + LockWord lw; + + LOCK_DEBUG (g_message ("%s: Testing if %p is owned by thread %d", __func__, obj, mono_thread_info_get_small_id())); + + lw.sync = obj->synchronisation; + + if (lock_word_is_flat (lw)) { + return lock_word_get_owner (lw) == mono_thread_info_get_small_id (); + } else if (lock_word_is_inflated (lw)) { + return mon_status_get_owner (lock_word_get_inflated_lock (lw)->status) == mono_thread_info_get_small_id (); + } + + return(FALSE); +} + +MonoBoolean +ves_icall_System_Threading_Monitor_Monitor_test_synchronised (MonoObject *obj) +{ + LockWord lw; + + LOCK_DEBUG (g_message("%s: (%d) Testing if %p is owned by any thread", __func__, mono_thread_info_get_small_id (), obj)); + + lw.sync = obj->synchronisation; + + if (lock_word_is_flat (lw)) { + return !lock_word_is_free (lw); + } else if (lock_word_is_inflated (lw)) { + return mon_status_get_owner (lock_word_get_inflated_lock (lw)->status) != 0; + } + + return FALSE; +} + +/* All wait list manipulation in the pulse, pulseall and wait + * functions happens while the monitor lock is held, so we don't need + * any extra struct locking + */ + +void +ves_icall_System_Threading_Monitor_Monitor_pulse (MonoObject *obj) +{ + int id; + LockWord lw; + MonoThreadsSync *mon; + + LOCK_DEBUG (g_message ("%s: (%d) Pulsing %p", __func__, mono_thread_info_get_small_id (), obj)); + + id = mono_thread_info_get_small_id (); + lw.sync = obj->synchronisation; + + if (!mono_monitor_ensure_owned (lw, id)) + return; + + if (!lock_word_is_inflated (lw)) { + /* No threads waiting. A wait would have inflated the lock */ + return; + } + + mon = lock_word_get_inflated_lock (lw); + + LOCK_DEBUG (g_message ("%s: (%d) %d threads waiting", __func__, mono_thread_info_get_small_id (), g_slist_length (mon->wait_list))); + + if (mon->wait_list != NULL) { + LOCK_DEBUG (g_message ("%s: (%d) signalling and dequeuing handle %p", __func__, mono_thread_info_get_small_id (), mon->wait_list->data)); + + mono_w32event_set (mon->wait_list->data); + mon->wait_list = g_slist_remove (mon->wait_list, mon->wait_list->data); + } +} + +void +ves_icall_System_Threading_Monitor_Monitor_pulse_all (MonoObject *obj) +{ + int id; + LockWord lw; + MonoThreadsSync *mon; + + LOCK_DEBUG (g_message("%s: (%d) Pulsing all %p", __func__, mono_thread_info_get_small_id (), obj)); + + id = mono_thread_info_get_small_id (); + lw.sync = obj->synchronisation; + + if (!mono_monitor_ensure_owned (lw, id)) + return; + + if (!lock_word_is_inflated (lw)) { + /* No threads waiting. A wait would have inflated the lock */ + return; + } + + mon = lock_word_get_inflated_lock (lw); + + LOCK_DEBUG (g_message ("%s: (%d) %d threads waiting", __func__, mono_thread_info_get_small_id (), g_slist_length (mon->wait_list))); + + while (mon->wait_list != NULL) { + LOCK_DEBUG (g_message ("%s: (%d) signalling and dequeuing handle %p", __func__, mono_thread_info_get_small_id (), mon->wait_list->data)); + + mono_w32event_set (mon->wait_list->data); + mon->wait_list = g_slist_remove (mon->wait_list, mon->wait_list->data); + } +} + +MonoBoolean +ves_icall_System_Threading_Monitor_Monitor_wait (MonoObject *obj, guint32 ms) +{ + LockWord lw; + MonoThreadsSync *mon; + HANDLE event; + guint32 nest; + MonoW32HandleWaitRet ret; + gboolean success = FALSE; + gint32 regain; + MonoInternalThread *thread = mono_thread_internal_current (); + int id = mono_thread_info_get_small_id (); + + LOCK_DEBUG (g_message ("%s: (%d) Trying to wait for %p with timeout %dms", __func__, mono_thread_info_get_small_id (), obj, ms)); + + lw.sync = obj->synchronisation; + + if (!mono_monitor_ensure_owned (lw, id)) + return FALSE; + + if (!lock_word_is_inflated (lw)) { + mono_monitor_inflate_owned (obj, id); + lw.sync = obj->synchronisation; + } + + mon = lock_word_get_inflated_lock (lw); + + /* Do this WaitSleepJoin check before creating the event handle */ + if (mono_thread_current_check_pending_interrupt ()) + return FALSE; + + event = mono_w32event_create (FALSE, FALSE); + if (event == NULL) { + mono_set_pending_exception (mono_get_exception_synchronization_lock ("Failed to set up wait event")); + return FALSE; + } + + LOCK_DEBUG (g_message ("%s: (%d) queuing handle %p", __func__, mono_thread_info_get_small_id (), event)); + + /* This looks superfluous */ + if (mono_thread_current_check_pending_interrupt ()) { + mono_w32event_close (event); + return FALSE; + } + + mono_thread_set_state (thread, ThreadState_WaitSleepJoin); + + mon->wait_list = g_slist_append (mon->wait_list, event); + + /* Save the nest count, and release the lock */ + nest = mon->nest; + mon->nest = 1; + mono_memory_write_barrier (); + mono_monitor_exit_inflated (obj); + + LOCK_DEBUG (g_message ("%s: (%d) Unlocked %p lock %p", __func__, mono_thread_info_get_small_id (), obj, mon)); + + /* There's no race between unlocking mon and waiting for the + * event, because auto reset events are sticky, and this event + * is private to this thread. Therefore even if the event was + * signalled before we wait, we still succeed. + */ +#ifdef HOST_WIN32 + MONO_ENTER_GC_SAFE; + ret = mono_w32handle_convert_wait_ret (mono_win32_wait_for_single_object_ex (event, ms, TRUE), 1); + MONO_EXIT_GC_SAFE; +#else + ret = mono_w32handle_wait_one (event, ms, TRUE); +#endif /* HOST_WIN32 */ + + /* Reset the thread state fairly early, so we don't have to worry + * about the monitor error checking + */ + mono_thread_clr_state (thread, ThreadState_WaitSleepJoin); + + /* Regain the lock with the previous nest count */ + do { + regain = mono_monitor_try_enter_inflated (obj, MONO_INFINITE_WAIT, TRUE, id); + /* We must regain the lock before handling interruption requests */ + } while (regain == -1); + + g_assert (regain == 1); + + mon->nest = nest; + + LOCK_DEBUG (g_message ("%s: (%d) Regained %p lock %p", __func__, mono_thread_info_get_small_id (), obj, mon)); + + if (ret == MONO_W32HANDLE_WAIT_RET_TIMEOUT) { + /* Poll the event again, just in case it was signalled + * while we were trying to regain the monitor lock + */ +#ifdef HOST_WIN32 + MONO_ENTER_GC_SAFE; + ret = mono_w32handle_convert_wait_ret (mono_win32_wait_for_single_object_ex (event, 0, FALSE), 1); + MONO_EXIT_GC_SAFE; +#else + ret = mono_w32handle_wait_one (event, 0, FALSE); +#endif /* HOST_WIN32 */ + } + + /* Pulse will have popped our event from the queue if it signalled + * us, so we only do it here if the wait timed out. + * + * This avoids a race condition where the thread holding the + * lock can Pulse several times before the WaitForSingleObject + * returns. If we popped the queue here then this event might + * be signalled more than once, thereby starving another + * thread. + */ + + if (ret == MONO_W32HANDLE_WAIT_RET_SUCCESS_0) { + LOCK_DEBUG (g_message ("%s: (%d) Success", __func__, mono_thread_info_get_small_id ())); + success = TRUE; + } else { + LOCK_DEBUG (g_message ("%s: (%d) Wait failed, dequeuing handle %p", __func__, mono_thread_info_get_small_id (), event)); + /* No pulse, so we have to remove ourself from the + * wait queue + */ + mon->wait_list = g_slist_remove (mon->wait_list, event); + } + mono_w32event_close (event); + + return success; +} + +void +ves_icall_System_Threading_Monitor_Monitor_Enter (MonoObject *obj) +{ + mono_monitor_enter_internal (obj); +} \ No newline at end of file diff --git a/unity-2019.4.24f1-mbe/mono/metadata/monitor.h b/unity-2019.4.24f1-mbe/mono/metadata/monitor.h new file mode 100644 index 000000000..0abf849a9 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/monitor.h @@ -0,0 +1,131 @@ +/** + * \file + * Monitor locking functions + * + * Author: + * Dick Porter (dick@ximian.com) + * + * (C) 2003 Ximian, Inc + */ + +#ifndef _MONO_METADATA_MONITOR_H_ +#define _MONO_METADATA_MONITOR_H_ + +#include +#include +#include +#include + +G_BEGIN_DECLS + +#define OWNER_MASK 0x0000ffff +#define ENTRY_COUNT_MASK 0xffff0000 +#define ENTRY_COUNT_WAITERS 0x80000000 +#define ENTRY_COUNT_ZERO 0x7fff0000 +#define ENTRY_COUNT_SHIFT 16 + +struct _MonoThreadsSync +{ + /* + * The entry count field can be negative, which would mean that the entry_sem is + * signaled and nobody is waiting to acquire it. This can happen when the thread + * that was waiting is either interrupted or timeouts, and the owner releases + * the lock before the forementioned thread updates the entry count. + * + * The 0 entry_count value is encoded as ENTRY_COUNT_ZERO, positive numbers being + * greater than it and negative numbers smaller than it. + */ + guint32 status; /* entry_count (16) | owner_id (16) */ + guint32 nest; +#ifdef HAVE_MOVING_COLLECTOR + gint32 hash_code; +#endif + GSList *wait_list; + void *data; + MonoCoopSem *entry_sem; +}; + +/* + * Lock word format: + * + * The least significant bit stores whether a hash for the object is computed + * which is stored either in the lock word or in the MonoThreadsSync structure + * that the lock word points to. + * + * The second bit stores whether the lock word is inflated, containing an + * address to the MonoThreadsSync structure. + * + * If both bits are 0, either the lock word is free (entire lock word is 0) + * or it is a thin/flat lock. + * + * 32-bit + * LOCK_WORD_FLAT: [owner:22 | nest:8 | status:2] + * LOCK_WORD_THIN_HASH: [hash:30 | status:2] + * LOCK_WORD_INFLATED: [sync:30 | status:2] + * LOCK_WORD_FAT_HASH: [sync:30 | status:2] + * + * 64-bit + * LOCK_WORD_FLAT: [unused:22 | owner:32 | nest:8 | status:2] + * LOCK_WORD_THIN_HASH: [hash:62 | status:2] + * LOCK_WORD_INFLATED: [sync:62 | status:2] + * LOCK_WORD_FAT_HASH: [sync:62 | status:2] + * + * In order to save processing time and to have one additional value, the nest + * count starts from 0 for the lock word (just valid thread ID in the lock word + * means that the thread holds the lock once, although nest is 0). + * FIXME Have the same convention on inflated locks + */ + +typedef union { +#if SIZEOF_REGISTER == 8 + guint64 lock_word; +#elif SIZEOF_REGISTER == 4 + guint32 lock_word; +#endif + MonoThreadsSync *sync; +} LockWord; + + +enum { + LOCK_WORD_FLAT = 0, + LOCK_WORD_HAS_HASH = 1, + LOCK_WORD_INFLATED = 2, + + LOCK_WORD_STATUS_BITS = 2, + LOCK_WORD_NEST_BITS = 8, + + LOCK_WORD_STATUS_MASK = (1 << LOCK_WORD_STATUS_BITS) - 1, + LOCK_WORD_NEST_MASK = ((1 << LOCK_WORD_NEST_BITS) - 1) << LOCK_WORD_STATUS_BITS, + + LOCK_WORD_HASH_SHIFT = LOCK_WORD_STATUS_BITS, + LOCK_WORD_NEST_SHIFT = LOCK_WORD_STATUS_BITS, + LOCK_WORD_OWNER_SHIFT = LOCK_WORD_STATUS_BITS + LOCK_WORD_NEST_BITS +}; + +MONO_API void mono_locks_dump (gboolean include_untaken); + +void mono_monitor_init (void); +void mono_monitor_cleanup (void); + +MonoBoolean mono_monitor_enter_internal (MonoObject *obj); +void mono_monitor_enter_v4_internal (MonoObject *obj, MonoBoolean *lock_taken); + +guint32 mono_monitor_enter_fast (MonoObject *obj); +guint32 mono_monitor_enter_v4_fast (MonoObject *obj, MonoBoolean *lock_taken); + +guint32 mono_monitor_get_object_monitor_gchandle (MonoObject *object); + +void mono_monitor_threads_sync_members_offset (int *status_offset, int *nest_offset); +#define MONO_THREADS_SYNC_MEMBER_OFFSET(o) ((o)>>8) +#define MONO_THREADS_SYNC_MEMBER_SIZE(o) ((o)&0xff) + +extern MonoBoolean ves_icall_System_Threading_Monitor_Monitor_test_owner(MonoObject *obj); +extern MonoBoolean ves_icall_System_Threading_Monitor_Monitor_test_synchronised(MonoObject *obj); +extern void ves_icall_System_Threading_Monitor_Monitor_pulse(MonoObject *obj); +extern void ves_icall_System_Threading_Monitor_Monitor_pulse_all(MonoObject *obj); +extern MonoBoolean ves_icall_System_Threading_Monitor_Monitor_wait(MonoObject *obj, guint32 ms); +extern void ves_icall_System_Threading_Monitor_Monitor_try_enter_with_atomic_var (MonoObject *obj, guint32 ms, MonoBoolean *lockTaken); +extern void ves_icall_System_Threading_Monitor_Monitor_Enter (MonoObject *obj); +G_END_DECLS + +#endif /* _MONO_METADATA_MONITOR_H_ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/mono-basic-block.c b/unity-2019.4.24f1-mbe/mono/metadata/mono-basic-block.c new file mode 100644 index 000000000..9d28614b1 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/mono-basic-block.c @@ -0,0 +1,638 @@ +/** + * \file + * Routines for parsing basic blocks from the IL stream + * + * Authors: + * Rodrigo Kumpera (rkumpera@novell.com) + * + * Copyright 2010 Novell, Inc (http://www.novell.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#define CHECK_ADD4_OVERFLOW_UN(a, b) ((guint32)(0xFFFFFFFFU) - (guint32)(b) < (guint32)(a)) +#define CHECK_ADD8_OVERFLOW_UN(a, b) ((guint64)(0xFFFFFFFFFFFFFFFFUL) - (guint64)(b) < (guint64)(a)) + +#if SIZEOF_VOID_P == 4 +#define CHECK_ADDP_OVERFLOW_UN(a,b) CHECK_ADD4_OVERFLOW_UN(a, b) +#else +#define CHECK_ADDP_OVERFLOW_UN(a,b) CHECK_ADD8_OVERFLOW_UN(a, b) +#endif + +#define ADDP_IS_GREATER_OR_OVF(a, b, c) (((a) + (b) > (c)) || CHECK_ADDP_OVERFLOW_UN (a, b)) +#define ADD_IS_GREATER_OR_OVF(a, b, c) (((a) + (b) > (c)) || CHECK_ADD4_OVERFLOW_UN (a, b)) + +#define DEBUG_BB 0 + +enum { + RED, + BLACK +}; + +#if DEBUG_BB + +static void +dump_bb_list (MonoSimpleBasicBlock *bb, MonoSimpleBasicBlock **root, const char *msg) +{ + printf ("------- %s --- root is %x ---\n", msg, (*root)->start); + while (bb) { + GSList *tmp; + printf ("BB start %x end %x left ", bb->start, bb->end); + if (bb->left) + printf ("%x", bb->left->start); + else + printf ("NIL"); + + printf (" right "); + if (bb->right) + printf ("%x", bb->right->start); + else + printf ("NIL"); + + printf (" parent "); + if (bb->parent) + printf ("%x", bb->parent->start); + else + printf ("NIL"); + + printf(" color %s out [", bb->colour == RED ? "red" : "black"); + + for (tmp = bb->out_bb; tmp; tmp = tmp->next) { + MonoSimpleBasicBlock *to = tmp->data; + printf ("%x ", to->start); + } + printf ("] %s\n", bb->dead ? "dead" : "alive"); + bb = bb->next; + } +} + +#endif + +static void +bb_unlink (MonoSimpleBasicBlock *from, MonoSimpleBasicBlock *to) +{ + if (from->out_bb) + from->out_bb = g_slist_remove (from->out_bb, to); +} + +static void +bb_link (MonoSimpleBasicBlock *from, MonoSimpleBasicBlock *to) +{ + if (g_slist_find (from->out_bb, to)) + return; + from->out_bb = g_slist_prepend (from->out_bb, to); +} + + +static MonoSimpleBasicBlock* +bb_grandparent (MonoSimpleBasicBlock *bb) +{ + return bb && bb->parent ? bb->parent->parent : NULL; +} + +static MonoSimpleBasicBlock* +bb_uncle (MonoSimpleBasicBlock *bb) +{ + MonoSimpleBasicBlock *gp = bb_grandparent (bb); + if (gp == NULL) + return NULL; + if (bb->parent == gp->left) + return gp->right; + return gp->left; +} + +static void +change_node (MonoSimpleBasicBlock *from, MonoSimpleBasicBlock *to, MonoSimpleBasicBlock **root) +{ + MonoSimpleBasicBlock *parent = from->parent; + if (parent) { + if (parent->left == from) + parent->left = to; + else + parent->right = to; + } else { + *root = to; + } + to->parent = parent; +} + +static void +rotate_right (MonoSimpleBasicBlock *parent, MonoSimpleBasicBlock **root) +{ + MonoSimpleBasicBlock *bb = parent->left; + if (bb->right) { + parent->left = bb->right; + parent->left->parent = parent; + } else + parent->left = NULL; + bb->right = parent; + change_node (parent, bb, root); + parent->parent = bb; +} + +static void +rotate_left (MonoSimpleBasicBlock *bb, MonoSimpleBasicBlock **root) +{ + MonoSimpleBasicBlock *other = bb->right; + if (other->left) { + bb->right = other->left; + bb->right->parent = bb; + } else + bb->right = NULL; + other->left = bb; + change_node (bb, other, root); + bb->parent = other; +} + +/* School book implementation of a RB tree with insert then fix (which requires a parent pointer) + * TODO implement Sedgewick's version that doesn't require parent pointers + */ +static void +bb_insert (MonoSimpleBasicBlock *first, MonoSimpleBasicBlock *bb, MonoSimpleBasicBlock **root) +{ + MonoSimpleBasicBlock *parent, *uncle, *grandparent; + int bb_start = bb->start; + + parent = *root; + do { + if (bb_start < parent->start) { + if (parent->left == NULL) { + parent->left = bb; + break; + } + parent = parent->left; + } else { + if (parent->right == NULL) { + parent->right = bb; + break; + } + parent = parent->right; + } + } while (parent); + g_assert (parent); + bb->parent = parent; + + bb->colour = RED; + + do { + if (bb->parent == NULL) { + bb->colour = BLACK; + break; + } + + if (bb->parent->colour == BLACK) + break; + + uncle = bb_uncle (bb); + if (uncle && uncle->colour == RED) { + grandparent = bb_grandparent (bb); + + bb->parent->colour = BLACK; + uncle->colour = BLACK; + grandparent->colour = RED; + bb = grandparent; + continue; + } + + grandparent = bb_grandparent (bb); + if ((bb == bb->parent->right) && (bb->parent == grandparent->left)) { + rotate_left (bb->parent, root); + bb = bb->left; + } else if ((bb == bb->parent->left) && (bb->parent == grandparent->right)) { + rotate_right (bb->parent, root); + bb = bb->right; + } + + grandparent = bb_grandparent (bb); + bb->parent->colour = BLACK; + grandparent->colour = RED; + if ((bb == bb->parent->left) && (bb->parent == grandparent->left)) + rotate_right (grandparent, root); + else + rotate_left (grandparent, root); + break; + } while (TRUE); +} + +static gboolean +bb_idx_is_contained (MonoSimpleBasicBlock *bb, int target) +{ + return bb->start <= target && target < bb->end; +} + +/* + * Split the basic blocks from @first at @target. + * @hint is a guess of a very close to the target basic block. It is probed before the RB tree as it's often possible + * to provide a near to exact guess (next block splits, switch branch targets, etc) + * + */ +static MonoSimpleBasicBlock* +bb_split (MonoSimpleBasicBlock *first, MonoSimpleBasicBlock *hint, MonoSimpleBasicBlock **root, guint target, gboolean link_blocks, MonoMethod *method, MonoError *error) +{ + MonoSimpleBasicBlock *res, *bb = first; + + error_init (error); + + if (bb_idx_is_contained (hint, target)) { + first = hint; + } else if (hint->next && bb_idx_is_contained (hint->next, target)) { + first = hint->next; + } else { + first = *root; + do { + if (bb_idx_is_contained (first, target)) + break; + if (first->start > target) + first = first->left; + else + first = first->right; + } while (first); + } + + if (first == NULL) { + mono_error_set_not_verifiable (error, method, "Invalid instruction target %x", target); + return NULL; + } + + if (first->start == target) + return first; + + res = g_new0 (MonoSimpleBasicBlock, 1); + res->start = target; + res->end = first->end; + res->next = first->next; + res->out_bb = first->out_bb; + res->dead = TRUE; + + first->end = res->start; + first->next = res; + first->out_bb = NULL; + + if (link_blocks) + bb_link (first, res); + bb_insert (bb, res, root); + + return res; +} + +static void +bb_liveness (MonoSimpleBasicBlock *bb) +{ + GPtrArray* mark_stack = g_ptr_array_new (); + GSList *tmp; + + /*All function entry points (prologue, EH handler/filter) are already marked*/ + while (bb) { + if (!bb->dead) + g_ptr_array_add (mark_stack, bb); + bb = bb->next; + } + + while (mark_stack->len > 0) { + MonoSimpleBasicBlock *block = (MonoSimpleBasicBlock *)g_ptr_array_remove_index_fast (mark_stack, mark_stack->len - 1); + block->dead = FALSE; + + for (tmp = block->out_bb; tmp; tmp = tmp->next) { + MonoSimpleBasicBlock *to = (MonoSimpleBasicBlock *)tmp->data; + if (to->dead) + g_ptr_array_add (mark_stack, to); + } + } + + g_ptr_array_free (mark_stack, TRUE); +} + +/*This doesn't returns endfilter because it's not useful to split at its boundary. +Endfilter must be the last instruction of a filter clause and MS enforces this, so we can't have +dead code after it. +*/ +static gboolean +mono_opcode_has_static_branch (int opcode) +{ + switch (opcode) { + case MONO_CEE_RET: + case MONO_CEE_THROW: + case MONO_CEE_RETHROW: + case MONO_CEE_ENDFINALLY: + return TRUE; + } + return FALSE; +} + + +static void +bb_formation_il_pass (const unsigned char *start, const unsigned char *end, MonoSimpleBasicBlock *bb, MonoSimpleBasicBlock **root, MonoMethod *method, MonoError *error) +{ + unsigned const char *ip = start; + int value, size; + guint cli_addr, offset; + MonoSimpleBasicBlock *branch, *next, *current; + const MonoOpcode *opcode; + + error_init (error); + + current = bb; + + while (ip < end) { + cli_addr = ip - start; + size = mono_opcode_value_and_size (&ip, end, &value); + if (size < 0) { + mono_error_set_not_verifiable (error, method, "Invalid instruction %x", *ip); + return; + } + + while (current && cli_addr >= current->end) + current = current->next; + g_assert (current); + + opcode = &mono_opcodes [value]; + switch (opcode->argument) { + case MonoInlineNone: + ip++; + if (!mono_opcode_has_static_branch (value) || ip >= end) + break; + if (!(next = bb_split (bb, current, root, ip - start, FALSE, method, error))) + return; + + bb_unlink (current, next); + current = next; + break; + case MonoInlineString: + case MonoInlineType: + case MonoInlineField: + case MonoInlineTok: + case MonoInlineSig: + case MonoShortInlineR: + case MonoInlineI: + ip += 5; + break; + + case MonoInlineMethod: + ip += 5; + if (value != MONO_CEE_JMP || ip >= end) + break; + if (!(next = bb_split (bb, current, root, ip - start, FALSE, method, error))) + return; + + bb_unlink (current, next); + current = next; + + break; + case MonoInlineVar: + ip += 3; + break; + case MonoShortInlineVar: + case MonoShortInlineI: + ip += 2; + break; + case MonoInlineR: + case MonoInlineI8: + ip += 9; + break; + case MonoShortInlineBrTarget: + case MonoInlineBrTarget: + if (opcode->argument == MonoShortInlineBrTarget) { + offset = cli_addr + 2 + (signed char)ip [1]; + ip += 2; + } else { + offset = cli_addr + 5 + (gint32)read32 (ip + 1); + ip += 5; + } + + branch = bb_split (bb, current, root, offset, TRUE, method, error); + if (!branch) + return; + + /*If we splitted the current BB*/ + if (offset < cli_addr && branch->start > current->start) + current = branch; + if (ip < end) { + next = bb_split (bb, current, root, ip - start, opcode->flow_type != MONO_FLOW_BRANCH, method, error); + if (!next) + return; + } else { + next = NULL; + } + + bb_link (current, branch); + if (next && opcode->flow_type == MONO_FLOW_BRANCH && next != branch) { + bb_unlink (current, next); + current = next; + } + break; + case MonoInlineSwitch: { + MonoSimpleBasicBlock *tmp; + guint32 j, n = read32 (ip + 1); + + ip += 5; + offset = cli_addr + 5 + 4 * n; + if (!(next = bb_split (bb, current, root, offset, TRUE, method, error))) + return; + + bb_link (current, next); + tmp = next; + + for (j = 0; j < n; ++j) { + if (ip >= end) { + mono_error_set_not_verifiable (error, method, "Invalid switch instruction %x", cli_addr); + return; + } + if (!(next = bb_split (bb, next, root, offset + (gint32)read32 (ip), TRUE, method, error))) + return; + bb_link (current, next); + ip += 4; + } + current = tmp; + break; + } + default: + mono_error_set_not_verifiable (error, method, "Invalid instruction %x", *ip); + return; + } + } + if (ip != end) + mono_error_set_not_verifiable (error, method, "Invalid last instruction"); +} + +static void +bb_formation_eh_pass (MonoMethodHeader *header, MonoSimpleBasicBlock *bb, MonoSimpleBasicBlock **root, MonoMethod *method, MonoError *error) +{ + int i; + int end = header->code_size; + + error_init (error); + + /*We must split at all points to verify for targets in the middle of an instruction*/ + for (i = 0; i < header->num_clauses; ++i) { + MonoExceptionClause *clause = header->clauses + i; + MonoSimpleBasicBlock *try_block, *handler; + + if (!(try_block = bb_split (bb, bb, root, clause->try_offset, TRUE, method, error))) + return; + + handler = bb_split (bb, try_block, root, clause->handler_offset, FALSE, method, error); + if (!handler) + return; + handler->dead = FALSE; + + if (clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) { + MonoSimpleBasicBlock *filter = bb_split (bb, try_block, root, clause->data.filter_offset, FALSE, method, error); + if (!filter) + return; + filter->dead = FALSE; + } + + if (clause->try_offset + clause->try_len < end && !bb_split (bb, try_block, root, clause->try_offset + clause->try_len, FALSE, method, error)) + return; + + if (clause->handler_offset + clause->handler_len < end && !bb_split (bb, handler, root, clause->handler_offset + clause->handler_len, FALSE, method, error)) + return; + } +} + +/* + * mono_basic_block_free: + * + * Release the memory associated with the list of basis blocks @bb. +*/ +void +mono_basic_block_free (MonoSimpleBasicBlock *bb) +{ + while (bb) { + MonoSimpleBasicBlock *next = bb->next; + if (bb->out_bb) + g_slist_free (bb->out_bb); + g_free (bb); + bb = next; + } +} + +/* + * mono_basic_block_split: + * + * Return the list of basic blocks of method. Return NULL on failure and set @error. +*/ +MonoSimpleBasicBlock* +mono_basic_block_split (MonoMethod *method, MonoError *error, MonoMethodHeader *header) +{ + MonoSimpleBasicBlock *bb, *root; + const unsigned char *start, *end; + + error_init (error); + + start = header->code; + end = start + header->code_size; + + bb = g_new0 (MonoSimpleBasicBlock, 1); + bb->start = 0; + bb->end = end - start; + bb->colour = BLACK; + bb->dead = FALSE; + + root = bb; + bb_formation_il_pass (start, end, bb, &root, method, error); + if (!mono_error_ok (error)) + goto fail; + + bb_formation_eh_pass (header, bb, &root, method, error); + if (!mono_error_ok (error)) + goto fail; + + bb_liveness (bb); + +#if DEBUG_BB + dump_bb_list (bb, &root, g_strdup_printf("AFTER LIVENESS %s", mono_method_full_name (method, TRUE))); +#endif + + return bb; + +fail: + mono_basic_block_free (bb); + return NULL; +} + +/* + * mono_opcode_value_and_size: + * + * Returns the size of the opcode starting at *@ip, or -1 on error. + * Value is the opcode number. +*/ +int +mono_opcode_value_and_size (const unsigned char **ip, const unsigned char *end, int *value) +{ + const unsigned char *start = *ip, *p; + int i = *value = mono_opcode_value (ip, end); + int size = 0; + if (i < 0 || i >= MONO_CEE_LAST) + return -1; + p = *ip; + + switch (mono_opcodes [i].argument) { + case MonoInlineNone: + size = 1; + break; + case MonoInlineString: + case MonoInlineType: + case MonoInlineField: + case MonoInlineMethod: + case MonoInlineTok: + case MonoInlineSig: + case MonoShortInlineR: + case MonoInlineI: + case MonoInlineBrTarget: + size = 5; + break; + case MonoInlineVar: + size = 3; + break; + case MonoShortInlineVar: + case MonoShortInlineI: + case MonoShortInlineBrTarget: + size = 2; + break; + case MonoInlineR: + case MonoInlineI8: + size = 9; + break; + case MonoInlineSwitch: { + guint32 entries; + if (ADDP_IS_GREATER_OR_OVF (p, 5, end)) + return -1; + entries = read32 (p + 1); + if (entries >= (0xFFFFFFFFU / 4)) + return -1; + size = 4 + 4 * entries; + break; + } + default: + g_error ("Invalid opcode %d argument %d max opcode %d\n", i, mono_opcodes [i].argument, MONO_CEE_LAST); + } + + if (ADDP_IS_GREATER_OR_OVF (p, size, end)) + return -1; + + return (p - start) + size; +} + +/* + * mono_opcode_size: + * + * Returns the size of the opcode starting at @ip, or -1 on error. +*/ +int +mono_opcode_size (const unsigned char *ip, const unsigned char *end) +{ + int tmp; + return mono_opcode_value_and_size (&ip, end, &tmp); +} + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/mono-basic-block.h b/unity-2019.4.24f1-mbe/mono/metadata/mono-basic-block.h new file mode 100644 index 000000000..bed87a177 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/mono-basic-block.h @@ -0,0 +1,42 @@ +/** + * \file + */ + +#ifndef __MONO_METADATA_BASIC_BLOCK_H__ +#define __MONO_METADATA_BASIC_BLOCK_H__ + +#include +#include +#include +#include + +G_BEGIN_DECLS + +typedef struct _MonoSimpleBasicBlock MonoSimpleBasicBlock; + +struct _MonoSimpleBasicBlock { + MonoSimpleBasicBlock *next, *left, *right, *parent; + GSList *out_bb; + int start, end; + unsigned colour : 1; + unsigned dead : 1; +}; + +MonoSimpleBasicBlock* +mono_basic_block_split (MonoMethod *method, MonoError *error, MonoMethodHeader *header); + +void +mono_basic_block_free (MonoSimpleBasicBlock *bb); + + +/*This function is here because opcodes.h is a public header*/ +int +mono_opcode_value_and_size (const unsigned char **ip, const unsigned char *end, int *value); + +int +mono_opcode_size (const unsigned char *ip, const unsigned char *end); + +G_END_DECLS + +#endif /* __MONO_METADATA_BASIC_BLOCK_H__ */ + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/mono-conc-hash.c b/unity-2019.4.24f1-mbe/mono/metadata/mono-conc-hash.c new file mode 100644 index 000000000..2aa1fe225 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/mono-conc-hash.c @@ -0,0 +1,434 @@ +/** + * \file + * Conc GC aware Hashtable implementation + * + * Author: + * Rodrigo Kumpera (kumpera@gmail.com) + * + */ +#include +#include +#include +#include +#include "mono-conc-hash.h" +#include "metadata/gc-internals.h" +#include +#include + +#define INITIAL_SIZE 32 +#define LOAD_FACTOR 0.75f +#define PTR_TOMBSTONE ((gpointer)(ssize_t)-1) +/* Expand ration must be a power of two */ +#define EXPAND_RATIO 2 + +typedef struct { + int table_size; + MonoGHashGCType gc_type; + void **keys; + void **values; +} conc_table; + +struct _MonoConcGHashTable { + volatile conc_table *table; /* goes to HP0 */ + GHashFunc hash_func; + GEqualFunc equal_func; + int element_count; + int overflow_count; + GDestroyNotify key_destroy_func; + GDestroyNotify value_destroy_func; + MonoGHashGCType gc_type; + MonoGCRootSource source; + void *key; + const char *msg; +}; + + +static conc_table* +conc_table_new (MonoConcGHashTable *hash, int size) +{ + conc_table *table = g_new0 (conc_table, 1); + + table->keys = g_new0 (void*, size); + table->values = g_new0 (void*, size); + table->table_size = size; + table->gc_type = hash->gc_type; + + if (hash->gc_type & MONO_HASH_KEY_GC) + mono_gc_register_root_wbarrier ((char*)table->keys, sizeof (MonoObject*) * size, mono_gc_make_vector_descr (), hash->source, hash->key, hash->msg); + if (hash->gc_type & MONO_HASH_VALUE_GC) + mono_gc_register_root_wbarrier ((char*)table->values, sizeof (MonoObject*) * size, mono_gc_make_vector_descr (), hash->source, hash->key, hash->msg); + + return table; +} + +static void +conc_table_free (gpointer ptr) +{ + conc_table *table = (conc_table *)ptr; + if (table->gc_type & MONO_HASH_KEY_GC) + mono_gc_deregister_root ((char*)table->keys); + if (table->gc_type & MONO_HASH_VALUE_GC) + mono_gc_deregister_root ((char*)table->values); + + g_free (table->keys); + g_free (table->values); + g_free (table); +} + +static void +conc_table_lf_free (conc_table *table) +{ + mono_thread_hazardous_try_free (table, conc_table_free); +} + + +static gboolean +key_is_tombstone (MonoConcGHashTable *hash, gpointer ptr) +{ + if (hash->gc_type & MONO_HASH_KEY_GC) + return ptr == mono_domain_get()->ephemeron_tombstone; + return ptr == PTR_TOMBSTONE; +} + +/* +A common problem with power of two hashtables is that it leads of bad clustering when dealing +with aligned numbers. + +The solution here is to mix the bits from two primes plus the hash itself, it produces a better spread +than just the numbers. +*/ + +static MONO_ALWAYS_INLINE int +mix_hash (int hash) +{ + return ((hash * 215497) >> 16) ^ (hash * 1823231 + hash); +} + + +static inline void +set_key (conc_table *table, int slot, gpointer key) +{ + gpointer *key_addr = &table->keys [slot]; + if (table->gc_type & MONO_HASH_KEY_GC) + mono_gc_wbarrier_generic_store (key_addr, key); + else + *key_addr = key; +} + +static inline void +set_key_to_tombstone (conc_table *table, int slot) +{ + gpointer *key_addr = &table->keys [slot]; + if (table->gc_type & MONO_HASH_KEY_GC) + mono_gc_wbarrier_generic_store (key_addr, mono_domain_get()->ephemeron_tombstone); + else + *key_addr = PTR_TOMBSTONE; +} + +static inline void +set_value (conc_table *table, int slot, gpointer value) +{ + gpointer *value_addr = &table->values [slot]; + if (table->gc_type & MONO_HASH_VALUE_GC) + mono_gc_wbarrier_generic_store (value_addr, value); + else + *value_addr = value; +} + +static MONO_ALWAYS_INLINE void +insert_one_local (conc_table *table, GHashFunc hash_func, gpointer key, gpointer value) +{ + int table_mask = table->table_size - 1; + int hash = mix_hash (hash_func (key)); + int i = hash & table_mask; + + while (table->keys [i]) + i = (i + 1) & table_mask; + + set_key (table, i, key); + set_value (table, i, value); +} + +static void +expand_table (MonoConcGHashTable *hash_table) +{ + conc_table *old_table = (conc_table*)hash_table->table; + conc_table *new_table = conc_table_new (hash_table, old_table->table_size * EXPAND_RATIO); + int i; + + for (i = 0; i < old_table->table_size; ++i) { + if (old_table->keys [i] && !key_is_tombstone (hash_table, old_table->keys [i])) + insert_one_local (new_table, hash_table->hash_func, old_table->keys [i], old_table->values [i]); + } + + mono_memory_barrier (); + hash_table->table = new_table; + hash_table->overflow_count = (int)(new_table->table_size * LOAD_FACTOR); + conc_table_lf_free (old_table); +} + + +MonoConcGHashTable * +mono_conc_g_hash_table_new_type (GHashFunc hash_func, GEqualFunc key_equal_func, MonoGHashGCType type, MonoGCRootSource source, void *key, const char *msg) +{ + MonoConcGHashTable *hash; + + if (!hash_func) + hash_func = g_direct_hash; + + hash = g_new0 (MonoConcGHashTable, 1); + hash->hash_func = hash_func; + hash->equal_func = key_equal_func; + + hash->element_count = 0; + hash->overflow_count = (int)(INITIAL_SIZE * LOAD_FACTOR); + hash->gc_type = type; + hash->source = source; + hash->key = key; + hash->msg = msg; + + hash->table = conc_table_new (hash, INITIAL_SIZE); + + if (type > MONO_HASH_KEY_VALUE_GC) + g_error ("wrong type for gc hashtable"); + + return hash; +} + +gpointer +mono_conc_g_hash_table_lookup (MonoConcGHashTable *hash, gconstpointer key) +{ + gpointer orig_key, value; + + if (mono_conc_g_hash_table_lookup_extended (hash, key, &orig_key, &value)) + return value; + else + return NULL; +} + +gboolean +mono_conc_g_hash_table_lookup_extended (MonoConcGHashTable *hash_table, gconstpointer key, gpointer *orig_key_ptr, gpointer *value_ptr) +{ + MonoThreadHazardPointers* hp; + conc_table *table; + int hash, i, table_mask; + hash = mix_hash (hash_table->hash_func (key)); + hp = mono_hazard_pointer_get (); + +retry: + table = (conc_table *)mono_get_hazardous_pointer ((gpointer volatile*)&hash_table->table, hp, 0); + table_mask = table->table_size - 1; + i = hash & table_mask; + + if (G_LIKELY (!hash_table->equal_func)) { + while (table->keys [i]) { + gpointer orig_key = table->keys [i]; + if (key == orig_key) { + gpointer value; + /* The read of keys must happen before the read of values */ + mono_memory_barrier (); + value = table->values [i]; + + /* We just read a value been deleted, try again. */ + if (G_UNLIKELY (!value)) + goto retry; + + mono_hazard_pointer_clear (hp, 0); + + *orig_key_ptr = orig_key; + *value_ptr = value; + return TRUE; + } + i = (i + 1) & table_mask; + } + } else { + GEqualFunc equal = hash_table->equal_func; + + while (table->keys [i]) { + gpointer orig_key = table->keys [i]; + if (!key_is_tombstone (hash_table, orig_key) && equal (key, orig_key)) { + gpointer value; + /* The read of keys must happen before the read of values */ + mono_memory_barrier (); + value = table->values [i]; + + /* We just read a value been deleted, try again. */ + if (G_UNLIKELY (!value)) + goto retry; + + mono_hazard_pointer_clear (hp, 0); + *orig_key_ptr = orig_key; + *value_ptr = value; + return TRUE; + + } + i = (i + 1) & table_mask; + } + } + + /* The table might have expanded and the value is now on the newer table */ + mono_memory_barrier (); + if (hash_table->table != table) + goto retry; + + mono_hazard_pointer_clear (hp, 0); + + *orig_key_ptr = NULL; + *value_ptr = NULL; + return FALSE; +} + +void +mono_conc_g_hash_table_foreach (MonoConcGHashTable *hash_table, GHFunc func, gpointer user_data) +{ + int i; + conc_table *table = (conc_table*)hash_table->table; + + for (i = 0; i < table->table_size; ++i) { + if (table->keys [i] && !key_is_tombstone (hash_table, table->keys [i])) { + func (table->keys [i], table->values [i], user_data); + } + } +} + +void +mono_conc_g_hash_table_destroy (MonoConcGHashTable *hash_table) +{ + if (hash_table->key_destroy_func || hash_table->value_destroy_func) { + int i; + conc_table *table = (conc_table*)hash_table->table; + + for (i = 0; i < table->table_size; ++i) { + if (table->keys [i] && !key_is_tombstone (hash_table, table->keys [i])) { + if (hash_table->key_destroy_func) + (hash_table->key_destroy_func) (table->keys [i]); + if (hash_table->value_destroy_func) + (hash_table->value_destroy_func) (table->values [i]); + } + } + } + conc_table_free ((gpointer)hash_table->table); + g_free (hash_table); +} + +/* Return NULL on success or the old value in failure */ +gpointer +mono_conc_g_hash_table_insert (MonoConcGHashTable *hash_table, gpointer key, gpointer value) +{ + conc_table *table; + int hash, i, table_mask; + + g_assert (key != NULL); + g_assert (value != NULL); + + hash = mix_hash (hash_table->hash_func (key)); + + if (hash_table->element_count >= hash_table->overflow_count) + expand_table (hash_table); + + table = (conc_table*)hash_table->table; + table_mask = table->table_size - 1; + i = hash & table_mask; + + if (!hash_table->equal_func) { + for (;;) { + gpointer cur_key = table->keys [i]; + if (!cur_key || key_is_tombstone (hash_table, cur_key)) { + set_value (table, i, value); + + /* The write to values must happen after the write to keys */ + mono_memory_barrier (); + set_key (table, i, key); + ++hash_table->element_count; + return NULL; + } + if (key == cur_key) { + gpointer value = table->values [i]; + return value; + } + i = (i + 1) & table_mask; + } + } else { + GEqualFunc equal = hash_table->equal_func; + for (;;) { + gpointer cur_key = table->keys [i]; + if (!cur_key || key_is_tombstone (hash_table, cur_key)) { + set_value (table, i, value); + /* The write to values must happen after the write to keys */ + mono_memory_barrier (); + set_key (table, i, key); + ++hash_table->element_count; + return NULL; + } + if (equal (key, cur_key)) { + gpointer value = table->values [i]; + return value; + } + i = (i + 1) & table_mask; + } + } +} + +gpointer +mono_conc_g_hash_table_remove (MonoConcGHashTable *hash_table, gconstpointer key) +{ + conc_table *table; + int hash, i, table_mask; + + g_assert (key != NULL); + + hash = mix_hash (hash_table->hash_func (key)); + + table = (conc_table*)hash_table->table; + table_mask = table->table_size - 1; + i = hash & table_mask; + + if (!hash_table->equal_func) { + for (;;) { + gpointer cur_key = table->keys [i]; + if (!cur_key) { + return NULL; /*key not found*/ + } + + if (key == cur_key) { + gpointer value = table->values [i]; + table->values [i] = NULL; + mono_memory_barrier (); + set_key_to_tombstone (table, i); + + --hash_table->element_count; + + if (hash_table->key_destroy_func != NULL) + (*hash_table->key_destroy_func) (cur_key); + if (hash_table->value_destroy_func != NULL) + (*hash_table->value_destroy_func) (value); + + return value; + } + i = (i + 1) & table_mask; + } + } else { + GEqualFunc equal = hash_table->equal_func; + for (;;) { + gpointer cur_key = table->keys [i]; + if (!cur_key) { + return NULL; /*key not found*/ + } + + if (!key_is_tombstone (hash_table, cur_key) && equal (key, cur_key)) { + gpointer value = table->values [i]; + table->values [i] = NULL; + mono_memory_barrier (); + set_key_to_tombstone (table, i); + + if (hash_table->key_destroy_func != NULL) + (*hash_table->key_destroy_func) (cur_key); + if (hash_table->value_destroy_func != NULL) + (*hash_table->value_destroy_func) (value); + return value; + } + + i = (i + 1) & table_mask; + } + } +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/mono-conc-hash.h b/unity-2019.4.24f1-mbe/mono/metadata/mono-conc-hash.h new file mode 100644 index 000000000..e15629c30 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/mono-conc-hash.h @@ -0,0 +1,22 @@ +/** + * \file + * GC-aware concurrent hashtable, based on utils/mono-conc-hashtable + */ + +#ifndef __MONO_CONC_G_HASH_H__ +#define __MONO_CONC_G_HASH_H__ + +#include + + +typedef struct _MonoConcGHashTable MonoConcGHashTable; + +MonoConcGHashTable * mono_conc_g_hash_table_new_type (GHashFunc hash_func, GEqualFunc key_equal_func, MonoGHashGCType type, MonoGCRootSource source, void *key, const char *msg); +gpointer mono_conc_g_hash_table_lookup (MonoConcGHashTable *hash, gconstpointer key); +gboolean mono_conc_g_hash_table_lookup_extended (MonoConcGHashTable *hash, gconstpointer key, gpointer *orig_key, gpointer *value); +void mono_conc_g_hash_table_foreach (MonoConcGHashTable *hash, GHFunc func, gpointer user_data); +void mono_conc_g_hash_table_destroy (MonoConcGHashTable *hash); +gpointer mono_conc_g_hash_table_insert (MonoConcGHashTable *h, gpointer k, gpointer v); +gpointer mono_conc_g_hash_table_remove (MonoConcGHashTable *hash, gconstpointer key); + +#endif /* __MONO_CONC_G_HASH_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/mono-config-dirs.c b/unity-2019.4.24f1-mbe/mono/metadata/mono-config-dirs.c new file mode 100644 index 000000000..b526b414c --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/mono-config-dirs.c @@ -0,0 +1,53 @@ +/** + * \file + * + * Copyright 2015 Xamarin Inc (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +/* + * This file contains functions to return the values of various directories defined in Makefile.am. + */ + +#include + +const char* +mono_config_get_assemblies_dir (void) +{ +#ifdef MONO_ASSEMBLIES + return MONO_ASSEMBLIES; +#else + return NULL; +#endif +} + +const char* +mono_config_get_cfg_dir (void) +{ +#ifdef MONO_CFG_DIR + return MONO_CFG_DIR; +#else + return NULL; +#endif +} + +const char* +mono_config_get_bin_dir (void) +{ +#ifdef MONO_BINDIR + return MONO_BINDIR; +#else + return NULL; +#endif +} + +const char* +mono_config_get_reloc_lib_dir (void) +{ +#ifdef MONO_RELOC_LIBDIR + return MONO_RELOC_LIBDIR; +#else + return NULL; +#endif +} + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/mono-config-dirs.h b/unity-2019.4.24f1-mbe/mono/metadata/mono-config-dirs.h new file mode 100644 index 000000000..d0755f397 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/mono-config-dirs.h @@ -0,0 +1,23 @@ +/** + * \file + */ + +#ifndef __MONO_CONFIG_INTERNAL_H__ +#define __MONO_CONFIG_INTERNAL_H__ + +#include +#include + +const char* +mono_config_get_assemblies_dir (void); + +const char* +mono_config_get_cfg_dir (void); + +const char* +mono_config_get_bin_dir (void); + +const char* +mono_config_get_reloc_lib_dir (void); + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/mono-config.c b/unity-2019.4.24f1-mbe/mono/metadata/mono-config.c new file mode 100644 index 000000000..c3840963b --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/mono-config.c @@ -0,0 +1,935 @@ +/** + * \file + * + * Runtime and assembly configuration file support routines. + * + * Author: Paolo Molaro (lupus@ximian.com) + * + * Copyright 2002-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#include "config.h" +#include +#include + +#include "mono/metadata/assembly.h" +#include "mono/metadata/loader.h" +#include "mono/metadata/mono-config.h" +#include "mono/metadata/metadata-internals.h" +#include "mono/metadata/object-internals.h" +#include "mono/utils/mono-logger-internals.h" + +#if defined(TARGET_PS3) +#define CONFIG_OS "CellOS" +#elif defined(__linux__) +#define CONFIG_OS "linux" +#elif defined(__APPLE__) +#define CONFIG_OS "osx" +#elif defined(sun) +#define CONFIG_OS "solaris" +#elif defined(__FreeBSD__) +#define CONFIG_OS "freebsd" +#elif defined(__NetBSD__) +#define CONFIG_OS "netbsd" +#elif defined(__OpenBSD__) +#define CONFIG_OS "openbsd" +#elif defined(__WIN32__) || defined(TARGET_WIN32) +#define CONFIG_OS "windows" +#elif defined(_IBMR2) +#define CONFIG_OS "aix" +#elif defined(__hpux) +#define CONFIG_OS "hpux" +#elif defined(__HAIKU__) +#define CONFIG_OS "haiku" +#elif defined (TARGET_WASM) +#define CONFIG_OS "wasm" +#else +#warning Unknown operating system +#define CONFIG_OS "unknownOS" +#endif + +#ifndef CONFIG_CPU +#if defined(__i386__) || defined(TARGET_X86) +#define CONFIG_CPU "x86" +#define CONFIG_WORDSIZE "32" +#elif defined(__x86_64__) || defined(TARGET_AMD64) +#define CONFIG_CPU "x86-64" +#define CONFIG_WORDSIZE "64" +#elif defined(sparc) || defined(__sparc__) +#define CONFIG_CPU "sparc" +#define CONFIG_WORDSIZE "32" +#elif defined(__ppc64__) || defined(__powerpc64__) || defined(TARGET_POWERPC) +#define CONFIG_WORDSIZE "64" +#ifdef __mono_ppc_ilp32__ +# define CONFIG_CPU "ppc64ilp32" +#else +# define CONFIG_CPU "ppc64" +#endif +#elif defined(__ppc__) || defined(__powerpc__) +#define CONFIG_CPU "ppc" +#define CONFIG_WORDSIZE "32" +#elif defined(__s390x__) +#define CONFIG_CPU "s390x" +#define CONFIG_WORDSIZE "64" +#elif defined(__s390__) +#define CONFIG_CPU "s390" +#define CONFIG_WORDSIZE "32" +#elif defined(__arm__) +#define CONFIG_CPU "arm" +#define CONFIG_WORDSIZE "32" +#elif defined(__aarch64__) +#define CONFIG_CPU "armv8" +#define CONFIG_WORDSIZE "64" +#elif defined(mips) || defined(__mips) || defined(_mips) +#define CONFIG_CPU "mips" +#define CONFIG_WORDSIZE "32" +#elif defined(TARGET_WASM) +#define CONFIG_CPU "wasm" +#define CONFIG_WORDSIZE "32" +#else +#error Unknown CPU +#define CONFIG_CPU "unknownCPU" +#endif +#endif + +/** + * mono_config_get_os: + * + * Returns the operating system that Mono is running on, as used for dllmap entries. + */ +const char * +mono_config_get_os (void) +{ + return CONFIG_OS; +} + +/** + * mono_config_get_cpu: + * + * Returns the architecture that Mono is running on, as used for dllmap entries. + */ +const char * +mono_config_get_cpu (void) +{ + return CONFIG_CPU; +} + +/** + * mono_config_get_wordsize: + * + * Returns the word size that Mono is running on, as used for dllmap entries. + */ +const char * +mono_config_get_wordsize (void) +{ + return CONFIG_WORDSIZE; +} + +static void start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error); + +static void end_element (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error); + +static void parse_text (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error); + +static void passthrough (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error); + +static void parse_error (GMarkupParseContext *context, + GError *error, + gpointer user_data); + +static const GMarkupParser +mono_parser = { + start_element, + end_element, + parse_text, + passthrough, + parse_error +}; + +static GHashTable *config_handlers; + +static char *mono_cfg_dir = NULL; + +/* when this interface is stable, export it. */ +typedef struct MonoParseHandler MonoParseHandler; + +struct MonoParseHandler { + const char *element_name; + void*(*init) (MonoImage *assembly); + void (*start) (gpointer user_data, const gchar *name, + const gchar **attributes, + const gchar **values); + void (*text) (gpointer user_data, const char *text, gsize test_len); + void (*end) (gpointer user_data, const char *name); + void (*finish) (gpointer user_data); +}; + +typedef struct { + MonoAssemblyBindingInfo *info; + void (*info_parsed)(MonoAssemblyBindingInfo *info, void *user_data); + void *user_data; +} ParserUserData; + +typedef struct { + MonoParseHandler *current; + void *user_data; + MonoImage *assembly; + int inited; +} ParseState; + +static void start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error) +{ + ParseState *state = (ParseState *)user_data; + if (!state->current) { + state->current = (MonoParseHandler *)g_hash_table_lookup (config_handlers, element_name); + if (state->current && state->current->init) + state->user_data = state->current->init (state->assembly); + } + if (state->current && state->current->start) + state->current->start (state->user_data, element_name, attribute_names, attribute_values); +} + +static void end_element (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error) +{ + ParseState *state = (ParseState *)user_data; + if (state->current) { + if (state->current->end) + state->current->end (state->user_data, element_name); + if (strcmp (state->current->element_name, element_name) == 0) { + if (state->current->finish) + state->current->finish (state->user_data); + state->current = NULL; + state->user_data = NULL; + } + } +} + +static void parse_text (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + ParseState *state = (ParseState *)user_data; + if (state->current && state->current->text) + state->current->text (state->user_data, text, text_len); +} + +static void passthrough (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + /* do nothing */ +} + +static void parse_error (GMarkupParseContext *context, + GError *error, + gpointer user_data) +{ + ParseState *state = (ParseState *)user_data; + const gchar *msg; + const gchar *filename; + + filename = state && state->user_data ? (gchar *) state->user_data : ""; + msg = error && error->message ? error->message : ""; + g_warning ("Error parsing %s: %s", filename, msg); +} + +static int +arch_matches (const char* arch, const char *value) +{ + char **splitted, **p; + int found = FALSE; + if (value [0] == '!') + return !arch_matches (arch, value + 1); + p = splitted = g_strsplit (value, ",", 0); + while (*p) { + if (strcmp (arch, *p) == 0) { + found = TRUE; + break; + } + p++; + } + g_strfreev (splitted); + return found; +} + +typedef struct { + char *dll; + char *target; + int ignore; + MonoImage *assembly; +} DllInfo; + +static void* +dllmap_init (MonoImage *assembly) { + DllInfo *info = g_new0 (DllInfo, 1); + info->assembly = assembly; + return info; +} + +static void +dllmap_start (gpointer user_data, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values) +{ + int i; + DllInfo *info = (DllInfo *)user_data; + + if (strcmp (element_name, "dllmap") == 0) { + g_free (info->dll); + g_free (info->target); + info->dll = info->target = NULL; + info->ignore = FALSE; + for (i = 0; attribute_names [i]; ++i) { + if (strcmp (attribute_names [i], "dll") == 0) + info->dll = g_strdup (attribute_values [i]); + else if (strcmp (attribute_names [i], "target") == 0){ + char *p = strstr (attribute_values [i], "$mono_libdir"); + if (p != NULL){ + char *libdir = mono_native_getrootdir (); + size_t libdir_len = strlen (libdir); + char *result; + + result = (char *)g_malloc (libdir_len-strlen("$mono_libdir")+strlen(attribute_values[i])+1); + strncpy (result, attribute_values[i], p-attribute_values[i]); + strcpy (result+(p-attribute_values[i]), libdir); + g_free (libdir); + strcat (result, p+strlen("$mono_libdir")); + info->target = result; + } else + info->target = g_strdup (attribute_values [i]); + } else if (strcmp (attribute_names [i], "os") == 0 && !arch_matches (CONFIG_OS, attribute_values [i])) + info->ignore = TRUE; + else if (strcmp (attribute_names [i], "cpu") == 0 && !arch_matches (CONFIG_CPU, attribute_values [i])) + info->ignore = TRUE; + else if (strcmp (attribute_names [i], "wordsize") == 0 && !arch_matches (CONFIG_WORDSIZE, attribute_values [i])) + info->ignore = TRUE; + } + if (!info->ignore) + mono_dllmap_insert (info->assembly, info->dll, NULL, info->target, NULL); + } else if (strcmp (element_name, "dllentry") == 0) { + const char *name = NULL, *target = NULL, *dll = NULL; + int ignore = FALSE; + for (i = 0; attribute_names [i]; ++i) { + if (strcmp (attribute_names [i], "dll") == 0) + dll = attribute_values [i]; + else if (strcmp (attribute_names [i], "target") == 0) + target = attribute_values [i]; + else if (strcmp (attribute_names [i], "name") == 0) + name = attribute_values [i]; + else if (strcmp (attribute_names [i], "os") == 0 && !arch_matches (CONFIG_OS, attribute_values [i])) + ignore = TRUE; + else if (strcmp (attribute_names [i], "cpu") == 0 && !arch_matches (CONFIG_CPU, attribute_values [i])) + ignore = TRUE; + else if (strcmp (attribute_names [i], "wordsize") == 0 && !arch_matches (CONFIG_WORDSIZE, attribute_values [i])) + ignore = TRUE; + } + if (!dll) + dll = info->dll; + if (!info->ignore && !ignore) + mono_dllmap_insert (info->assembly, info->dll, name, dll, target); + } +} + +static void +dllmap_finish (gpointer user_data) +{ + DllInfo *info = (DllInfo *)user_data; + + g_free (info->dll); + g_free (info->target); + g_free (info); +} + +static const MonoParseHandler +dllmap_handler = { + "dllmap", + dllmap_init, + dllmap_start, + NULL, /* text */ + NULL, /* end */ + dllmap_finish +}; + +static void +legacyUEP_start (gpointer user_data, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values) { + if ((strcmp (element_name, "legacyUnhandledExceptionPolicy") == 0) && + (attribute_names [0] != NULL) && + (strcmp (attribute_names [0], "enabled") == 0)) { + if ((strcmp (attribute_values [0], "1") == 0) || + (g_ascii_strcasecmp (attribute_values [0], "true") == 0)) { + mono_runtime_unhandled_exception_policy_set (MONO_UNHANDLED_POLICY_LEGACY); + } + } +} + +static const MonoParseHandler +legacyUEP_handler = { + "legacyUnhandledExceptionPolicy", + NULL, /* init */ + legacyUEP_start, + NULL, /* text */ + NULL, /* end */ + NULL, /* finish */ +}; + +static void +aot_cache_start (gpointer user_data, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values) +{ + int i; + MonoAotCacheConfig *config; + + if (strcmp (element_name, "aotcache") != 0) + return; + + config = mono_get_aot_cache_config (); + + /* Per-app configuration */ + for (i = 0; attribute_names [i]; ++i) { + if (!strcmp (attribute_names [i], "app")) { + config->apps = g_slist_prepend (config->apps, g_strdup (attribute_values [i])); + } + } + + /* Global configuration */ + for (i = 0; attribute_names [i]; ++i) { + if (!strcmp (attribute_names [i], "assemblies")) { + char **parts, **ptr; + char *part; + + parts = g_strsplit (attribute_values [i], " ", -1); + for (ptr = parts; ptr && *ptr; ptr ++) { + part = *ptr; + config->assemblies = g_slist_prepend (config->assemblies, g_strdup (part)); + } + g_strfreev (parts); + } else if (!strcmp (attribute_names [i], "options")) { + config->aot_options = g_strdup (attribute_values [i]); + } + } +} + +static const MonoParseHandler +aot_cache_handler = { + "aotcache", + NULL, /* init */ + aot_cache_start, + NULL, /* text */ + NULL, /* end */ + NULL, /* finish */ +}; + +static int inited = 0; + +static void +mono_config_init (void) +{ + inited = 1; + config_handlers = g_hash_table_new (g_str_hash, g_str_equal); + g_hash_table_insert (config_handlers, (gpointer) dllmap_handler.element_name, (gpointer) &dllmap_handler); + g_hash_table_insert (config_handlers, (gpointer) legacyUEP_handler.element_name, (gpointer) &legacyUEP_handler); + g_hash_table_insert (config_handlers, (gpointer) aot_cache_handler.element_name, (gpointer) &aot_cache_handler); +} + +/** + * mono_config_cleanup: + */ +void +mono_config_cleanup (void) +{ + if (config_handlers) + g_hash_table_destroy (config_handlers); + g_free (mono_cfg_dir); +} + +/* FIXME: error handling */ + +static void +mono_config_parse_xml_with_context (ParseState *state, const char *text, gsize len) +{ + GMarkupParseContext *context; + + if (!inited) + mono_config_init (); + + context = g_markup_parse_context_new (&mono_parser, (GMarkupParseFlags)0, state, NULL); + if (g_markup_parse_context_parse (context, text, len, NULL)) { + g_markup_parse_context_end_parse (context, NULL); + } + g_markup_parse_context_free (context); +} + +/* If assembly is NULL, parse in the global context */ +static int +mono_config_parse_file_with_context (ParseState *state, const char *filename) +{ + gchar *text; + gsize len; + gint offset; + + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_CONFIG, + "Config attempting to parse: '%s'.", filename); + + if (!g_file_get_contents (filename, &text, &len, NULL)) + return 0; + + offset = 0; + if (len > 3 && text [0] == '\xef' && text [1] == (gchar) '\xbb' && text [2] == '\xbf') + offset = 3; /* Skip UTF-8 BOM */ + if (state->user_data == NULL) + state->user_data = (gpointer) filename; + mono_config_parse_xml_with_context (state, text + offset, len - offset); + g_free (text); + return 1; +} + +/** + * mono_config_parse_memory: + * \param buffer a pointer to an string XML representation of the configuration + * Parses the configuration from a buffer + */ +void +mono_config_parse_memory (const char *buffer) +{ + ParseState state = {NULL}; + + state.user_data = (gpointer) ""; + mono_config_parse_xml_with_context (&state, buffer, strlen (buffer)); +} + +static void +mono_config_parse_file (const char *filename) +{ + ParseState state = {NULL}; + state.user_data = (gpointer) filename; + mono_config_parse_file_with_context (&state, filename); +} + +/* + * use the equivalent lookup code from the GAC when available. + * Depending on state, this should give something like: + * aname/version-pubtoken/ + * aname/version/ + * aname + */ +static char* +get_assembly_filename (MonoImage *image, int state) +{ + switch (state) { + case 0: + return g_strdup (mono_image_get_name (image)); + default: + return NULL; + } +} + +typedef struct _BundledConfig BundledConfig; + +struct _BundledConfig { + BundledConfig *next; + const char* aname; + const char* config_xml; +}; + +static BundledConfig *bundled_configs = NULL; + +static const char *bundled_machine_config = NULL; + +/** + * mono_register_config_for_assembly: + */ +void +mono_register_config_for_assembly (const char* assembly_name, const char* config_xml) +{ + BundledConfig *bconfig; + + bconfig = g_new0 (BundledConfig, 1); + bconfig->aname = assembly_name; + bconfig->config_xml = config_xml; + bconfig->next = bundled_configs; + bundled_configs = bconfig; +} + +/** + * mono_config_string_for_assembly_file: + */ +const char * +mono_config_string_for_assembly_file (const char *filename) +{ + BundledConfig *bconfig; + + for (bconfig = bundled_configs; bconfig; bconfig = bconfig->next) { + if (bconfig->aname && strcmp (bconfig->aname, filename) == 0) + return bconfig->config_xml; + } + return NULL; +} + +/** + * mono_config_for_assembly: + */ +void +mono_config_for_assembly (MonoImage *assembly) +{ + ParseState state = {NULL}; + int got_it = 0, i; + char *aname, *cfg, *cfg_name; + const char *bundled_config; + + state.assembly = assembly; + + bundled_config = mono_config_string_for_assembly_file (assembly->module_name); + if (bundled_config) { + state.user_data = (gpointer) ""; + mono_config_parse_xml_with_context (&state, bundled_config, strlen (bundled_config)); + } + + cfg_name = g_strdup_printf ("%s.config", mono_image_get_filename (assembly)); + mono_config_parse_file_with_context (&state, cfg_name); + g_free (cfg_name); + + cfg_name = g_strdup_printf ("%s.config", mono_image_get_name (assembly)); + + for (i = 0; (aname = get_assembly_filename (assembly, i)) != NULL; ++i) { + cfg = g_build_filename (mono_get_config_dir (), "mono", "assemblies", aname, cfg_name, NULL); + got_it += mono_config_parse_file_with_context (&state, cfg); + g_free (cfg); + +#ifdef TARGET_WIN32 + const char *home = g_get_home_dir (); + cfg = g_build_filename (home, ".mono", "assemblies", aname, cfg_name, NULL); + got_it += mono_config_parse_file_with_context (&state, cfg); + g_free (cfg); +#endif + g_free (aname); + if (got_it) + break; + } + g_free (cfg_name); +} + +/** + * mono_config_parse: + * \param filename the filename to load the configuration variables from. + * Pass a NULL filename to parse the default config files + * (or the file in the \c MONO_CONFIG env var). + */ +void +mono_config_parse (const char *filename) { + const char *home; + char *mono_cfg; +#ifndef TARGET_WIN32 + char *user_cfg; +#endif + + if (filename) { + mono_config_parse_file (filename); + return; + } + + // FIXME: leak, do we store any references to home + char *env_home = g_getenv ("MONO_CONFIG"); + if (env_home) { + mono_config_parse_file (env_home); + return; + } + + mono_cfg = g_build_filename (mono_get_config_dir (), "mono", "config", NULL); + mono_config_parse_file (mono_cfg); + g_free (mono_cfg); + +#if !defined(TARGET_WIN32) + home = g_get_home_dir (); + user_cfg = g_strconcat (home, G_DIR_SEPARATOR_S, ".mono/config", NULL); + mono_config_parse_file (user_cfg); + g_free (user_cfg); +#endif +} + +/** + * mono_set_config_dir: + * Invoked during startup + */ +void +mono_set_config_dir (const char *dir) +{ + /* If this environment variable is set, overrides the directory computed */ + char *env_mono_cfg_dir = g_getenv ("MONO_CFG_DIR"); + if (env_mono_cfg_dir == NULL && dir != NULL) + env_mono_cfg_dir = g_strdup (dir); + + mono_cfg_dir = env_mono_cfg_dir; +} + +/** + * mono_get_config_dir: + */ +const char* +mono_get_config_dir (void) +{ + if (mono_cfg_dir == NULL) + mono_set_dirs (NULL, NULL); + + return mono_cfg_dir; +} + +/** + * mono_register_machine_config: + */ +void +mono_register_machine_config (const char *config_xml) +{ + bundled_machine_config = config_xml; +} + +/** + * mono_get_machine_config: + */ +const char * +mono_get_machine_config (void) +{ + return bundled_machine_config; +} + +static void +assembly_binding_end (gpointer user_data, const char *element_name) +{ + ParserUserData *pud = (ParserUserData *)user_data; + + if (!strcmp (element_name, "dependentAssembly")) { + if (pud->info_parsed && pud->info) { + pud->info_parsed (pud->info, pud->user_data); + g_free (pud->info->name); + g_free (pud->info->culture); + } + } +} + +static void +publisher_policy_start (gpointer user_data, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values) +{ + ParserUserData *pud; + MonoAssemblyBindingInfo *info; + int n; + + pud = (ParserUserData *)user_data; + info = pud->info; + if (!strcmp (element_name, "dependentAssembly")) { + info->name = NULL; + info->culture = NULL; + info->has_old_version_bottom = FALSE; + info->has_old_version_top = FALSE; + info->has_new_version = FALSE; + info->is_valid = FALSE; + memset (&info->old_version_bottom, 0, sizeof (info->old_version_bottom)); + memset (&info->old_version_top, 0, sizeof (info->old_version_top)); + memset (&info->new_version, 0, sizeof (info->new_version)); + } else if (!strcmp (element_name, "assemblyIdentity")) { + for (n = 0; attribute_names [n]; n++) { + const gchar *attribute_name = attribute_names [n]; + + if (!strcmp (attribute_name, "name")) + info->name = g_strdup (attribute_values [n]); + else if (!strcmp (attribute_name, "publicKeyToken")) { + if (strlen (attribute_values [n]) == MONO_PUBLIC_KEY_TOKEN_LENGTH - 1) + g_strlcpy ((char *) info->public_key_token, attribute_values [n], MONO_PUBLIC_KEY_TOKEN_LENGTH); + } else if (!strcmp (attribute_name, "culture")) { + if (!strcmp (attribute_values [n], "neutral")) + info->culture = g_strdup (""); + else + info->culture = g_strdup (attribute_values [n]); + } + } + } else if (!strcmp (element_name, "bindingRedirect")) { + for (n = 0; attribute_names [n]; n++) { + const gchar *attribute_name = attribute_names [n]; + + if (!strcmp (attribute_name, "oldVersion")) { + gchar **numbers, **version, **versions; + gint major, minor, build, revision; + + /* Invalid value */ + if (!strcmp (attribute_values [n], "")) + return; + + versions = g_strsplit (attribute_values [n], "-", 2); + version = g_strsplit (*versions, ".", 4); + + /* We assign the values to gint vars to do the checks */ + numbers = version; + major = *numbers ? atoi (*numbers++) : -1; + minor = *numbers ? atoi (*numbers++) : -1; + build = *numbers ? atoi (*numbers++) : -1; + revision = *numbers ? atoi (*numbers) : -1; + g_strfreev (version); + if (major < 0 || minor < 0 || build < 0 || revision < 0) { + g_strfreev (versions); + return; + } + + info->old_version_bottom.major = major; + info->old_version_bottom.minor = minor; + info->old_version_bottom.build = build; + info->old_version_bottom.revision = revision; + info->has_old_version_bottom = TRUE; + + if (!*(versions + 1)) { + g_strfreev (versions); + continue; + } + + numbers = version = g_strsplit (*(versions + 1), ".", 4); + major = *numbers ? atoi (*numbers++) : -1; + minor = *numbers ? atoi (*numbers++) : -1; + build = *numbers ? atoi (*numbers++) : -1; + revision = *numbers ? atoi (*numbers) : 1; + g_strfreev (version); + if (major < 0 || minor < 0 || build < 0 || revision < 0) { + g_strfreev (versions); + return; + } + + info->old_version_top.major = major; + info->old_version_top.minor = minor; + info->old_version_top.build = build; + info->old_version_top.revision = revision; + info->has_old_version_top = TRUE; + + g_strfreev (versions); + } else if (!strcmp (attribute_name, "newVersion")) { + gchar **numbers, **version; + + /* Invalid value */ + if (!strcmp (attribute_values [n], "")) + return; + + numbers = version = g_strsplit (attribute_values [n], ".", 4); + info->new_version.major = *numbers ? atoi (*numbers++) : -1; + info->new_version.minor = *numbers ? atoi (*numbers++) : -1; + info->new_version.build = *numbers ? atoi (*numbers++) : -1; + info->new_version.revision = *numbers ? atoi (*numbers) : -1; + info->has_new_version = TRUE; + g_strfreev (version); + } + } + } +} + +static MonoParseHandler +publisher_policy_parser = { + "", /* We don't need to use declare an xml element */ + NULL, + publisher_policy_start, + NULL, + NULL, + NULL +}; + +void +mono_config_parse_publisher_policy (const gchar *filename, MonoAssemblyBindingInfo *info) +{ + ParserUserData user_data = { + info, + NULL, + NULL + }; + ParseState state = { + &publisher_policy_parser, /* MonoParseHandler */ + &user_data, /* user_data */ + NULL, /* MonoImage (we don't need it right now)*/ + TRUE /* We are already inited */ + }; + + mono_config_parse_file_with_context (&state, filename); +} + +static MonoParseHandler +config_assemblybinding_parser = { + "", /* We don't need to use declare an xml element */ + NULL, + publisher_policy_start, + NULL, + assembly_binding_end, + NULL +}; + +void +mono_config_parse_assembly_bindings (const char *filename, int amajor, int aminor, void *user_data, void (*infocb)(MonoAssemblyBindingInfo *info, void *user_data)) +{ + MonoAssemblyBindingInfo info; + ParserUserData pud; + ParseState state; + + info.major = amajor; + info.minor = aminor; + + pud.info = &info; + pud.info_parsed = infocb; + pud.user_data = user_data; + + state.current = &config_assemblybinding_parser; /* MonoParseHandler */ + state.user_data = &pud; + state.assembly = NULL; /* MonoImage (we don't need it right now)*/ + state.inited = TRUE; /* We are already inited */ + + mono_config_parse_file_with_context (&state, filename); +} + +static mono_bool mono_server_mode = FALSE; + +/** + * mono_config_set_server_mode: + */ +void +mono_config_set_server_mode (mono_bool server_mode) +{ + mono_server_mode = server_mode; +} + +/** + * mono_config_is_server_mode: + */ +mono_bool +mono_config_is_server_mode (void) +{ + return mono_server_mode; +} + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/mono-config.h b/unity-2019.4.24f1-mbe/mono/metadata/mono-config.h new file mode 100644 index 000000000..daebd02c2 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/mono-config.h @@ -0,0 +1,38 @@ +/** + * \file + * + * Author: Paolo Molaro (lupus@ximian.com) + * + * (C) 2002 Ximian, Inc. + */ +#ifndef __MONO_METADATA_CONFIG_H__ +#define __MONO_METADATA_CONFIG_H__ + +#include +#include + +MONO_BEGIN_DECLS + +MONO_API const char *mono_config_get_os (void); +MONO_API const char *mono_config_get_cpu (void); +MONO_API const char *mono_config_get_wordsize (void); + +MONO_API const char* mono_get_config_dir (void); +MONO_API void mono_set_config_dir (const char *dir); + +MONO_API const char* mono_get_machine_config (void); + +MONO_API void mono_config_cleanup (void); +MONO_API void mono_config_parse (const char *filename); +MONO_API void mono_config_for_assembly (MonoImage *assembly); +MONO_API void mono_config_parse_memory (const char *buffer); + +MONO_API const char* mono_config_string_for_assembly_file (const char *filename); + +MONO_API void mono_config_set_server_mode (mono_bool server_mode); +MONO_API mono_bool mono_config_is_server_mode (void); + +MONO_END_DECLS + +#endif /* __MONO_METADATA_CONFIG_H__ */ + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/mono-debug.c b/unity-2019.4.24f1-mbe/mono/metadata/mono-debug.c new file mode 100644 index 000000000..4230399ec --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/mono-debug.c @@ -0,0 +1,1111 @@ +/** + * \file + * + * Author: + * Mono Project (http://www.mono-project.com) + * + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Copyright 2011 Xamarin Inc (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1)) + +#if NO_UNALIGNED_ACCESS +#define WRITE_UNALIGNED(type, addr, val) \ + memcpy(addr, &val, sizeof(type)) +#define READ_UNALIGNED(type, addr, val) \ + memcpy(&val, addr, sizeof(type)) +#else +#define WRITE_UNALIGNED(type, addr, val) \ + (*(type *)(addr) = (val)) +#define READ_UNALIGNED(type, addr, val) \ + val = (*(type *)(addr)) +#endif + +/* This contains per-domain info */ +struct _MonoDebugDataTable { + MonoMemPool *mp; + GHashTable *method_address_hash; +}; + +/* This contains JIT debugging information about a method in serialized format */ +struct _MonoDebugMethodAddress { + const guint8 *code_start; + guint32 code_size; + guint8 data [MONO_ZERO_LEN_ARRAY]; +}; + +static MonoDebugFormat mono_debug_format = MONO_DEBUG_FORMAT_NONE; + +static gboolean mono_debug_initialized = FALSE; +/* Maps MonoImage -> MonoMonoDebugHandle */ +static GHashTable *mono_debug_handles; +/* Maps MonoDomain -> MonoDataTable */ +static GHashTable *data_table_hash; + +static mono_mutex_t debugger_lock_mutex; + +static gboolean is_attached = FALSE; + +static MonoDebugHandle *mono_debug_open_image (MonoImage *image, const guint8 *raw_contents, int size); + +static MonoDebugHandle *mono_debug_get_image (MonoImage *image); +static void mono_debug_add_assembly (MonoAssembly *assembly, + gpointer user_data); + +static MonoDebugHandle *open_symfile_from_bundle (MonoImage *image); + +static MonoDebugDataTable * +create_data_table (MonoDomain *domain) +{ + MonoDebugDataTable *table; + + table = g_new0 (MonoDebugDataTable, 1); + + table->mp = mono_mempool_new (); + table->method_address_hash = g_hash_table_new (NULL, NULL); + + if (domain) + g_hash_table_insert (data_table_hash, domain, table); + + return table; +} + +static void +free_data_table (MonoDebugDataTable *table) +{ + mono_mempool_destroy (table->mp); + g_hash_table_destroy (table->method_address_hash); + + g_free (table); +} + +static MonoDebugDataTable * +lookup_data_table (MonoDomain *domain) +{ + MonoDebugDataTable *table; + + table = (MonoDebugDataTable *)g_hash_table_lookup (data_table_hash, domain); + if (!table) { + g_error ("lookup_data_table () failed for %p\n", domain); + g_assert (table); + } + return table; +} + +static void +free_debug_handle (MonoDebugHandle *handle) +{ + if (handle->ppdb) + mono_ppdb_close (handle); + if (handle->symfile) + mono_debug_close_mono_symbol_file (handle->symfile); + /* decrease the refcount added with mono_image_addref () */ + mono_image_close (handle->image); + g_free (handle); +} + +/* + * Initialize debugging support. + * + * This method must be called after loading corlib, + * but before opening the application's main assembly because we need to set some + * callbacks here. + */ +void +mono_debug_init (MonoDebugFormat format) +{ + g_assert (!mono_debug_initialized); + if (format == MONO_DEBUG_FORMAT_DEBUGGER) + g_error ("The mdb debugger is no longer supported."); + + mono_debug_initialized = TRUE; + mono_debug_format = format; + + mono_os_mutex_init_recursive (&debugger_lock_mutex); + + mono_debugger_lock (); + + mono_debug_handles = g_hash_table_new_full + (NULL, NULL, NULL, (GDestroyNotify) free_debug_handle); + + data_table_hash = g_hash_table_new_full ( + NULL, NULL, NULL, (GDestroyNotify) free_data_table); + + mono_install_assembly_load_hook (mono_debug_add_assembly, NULL); + + mono_debugger_unlock (); +} + +void +mono_debug_open_image_from_memory (MonoImage *image, const guint8 *raw_contents, int size) +{ + if (!mono_debug_initialized) + return; + + mono_debug_open_image (image, raw_contents, size); +} + +void +mono_debug_cleanup (void) +{ + if (mono_debug_handles) + g_hash_table_destroy (mono_debug_handles); + mono_debug_handles = NULL; + + if (data_table_hash) { + g_hash_table_destroy (data_table_hash); + data_table_hash = NULL; + } +} + +/** + * mono_debug_domain_create: + */ +void +mono_debug_domain_create (MonoDomain *domain) +{ + if (!mono_debug_initialized) + return; + + mono_debugger_lock (); + + create_data_table (domain); + + mono_debugger_unlock (); +} + +void +mono_debug_domain_unload (MonoDomain *domain) +{ + MonoDebugDataTable *table; + + if (!mono_debug_initialized) + return; + + mono_debugger_lock (); + + table = (MonoDebugDataTable *)g_hash_table_lookup (data_table_hash, domain); + if (!table) { + g_warning (G_STRLOC ": unloading unknown domain %p / %d", + domain, mono_domain_get_id (domain)); + mono_debugger_unlock (); + return; + } + + g_hash_table_remove (data_table_hash, domain); + + mono_debugger_unlock (); +} + +/* + * LOCKING: Assumes the debug lock is held. + */ +static MonoDebugHandle * +mono_debug_get_image (MonoImage *image) +{ + return (MonoDebugHandle *)g_hash_table_lookup (mono_debug_handles, image); +} + +/** + * mono_debug_close_image: + */ +void +mono_debug_close_image (MonoImage *image) +{ + MonoDebugHandle *handle; + + if (!mono_debug_initialized) + return; + + mono_debugger_lock (); + + handle = mono_debug_get_image (image); + if (!handle) { + mono_debugger_unlock (); + return; + } + + g_hash_table_remove (mono_debug_handles, image); + + mono_debugger_unlock (); +} + +static MonoDebugHandle * +mono_debug_open_image (MonoImage *image, const guint8 *raw_contents, int size) +{ + MonoDebugHandle *handle; + + if (mono_image_is_dynamic (image)) + return NULL; + + mono_debugger_lock (); + + handle = mono_debug_get_image (image); + if (handle != NULL) { + mono_debugger_unlock (); + return handle; + } + + handle = g_new0 (MonoDebugHandle, 1); + + handle->image = image; + mono_image_addref (image); + + /* Try a ppdb file first */ + handle->ppdb = mono_ppdb_load_file (handle->image, raw_contents, size); + + if (!handle->ppdb) + handle->symfile = mono_debug_open_mono_symbols (handle, raw_contents, size, FALSE); + + g_hash_table_insert (mono_debug_handles, image, handle); + + mono_debugger_unlock (); + + return handle; +} + +static void +mono_debug_add_assembly (MonoAssembly *assembly, gpointer user_data) +{ + MonoDebugHandle *handle; + MonoImage *image; + + mono_debugger_lock (); + image = mono_assembly_get_image (assembly); + handle = open_symfile_from_bundle (image); + if (!handle) + mono_debug_open_image (image, NULL, 0); + mono_debugger_unlock (); +} + +struct LookupMethodData +{ + MonoDebugMethodInfo *minfo; + MonoMethod *method; +}; + +static void +lookup_method_func (gpointer key, gpointer value, gpointer user_data) +{ + MonoDebugHandle *handle = (MonoDebugHandle *) value; + struct LookupMethodData *data = (struct LookupMethodData *) user_data; + + if (data->minfo) + return; + + if (handle->ppdb) + data->minfo = mono_ppdb_lookup_method (handle, data->method); + else if (handle->symfile) + data->minfo = mono_debug_symfile_lookup_method (handle, data->method); +} + +static MonoDebugMethodInfo * +mono_debug_lookup_method_internal (MonoMethod *method) +{ + struct LookupMethodData data; + + data.minfo = NULL; + data.method = method; + + if (!mono_debug_handles) + return NULL; + + g_hash_table_foreach (mono_debug_handles, lookup_method_func, &data); + return data.minfo; +} + +/** + * mono_debug_lookup_method: + * + * Lookup symbol file information for the method \p method. The returned + * \c MonoDebugMethodInfo is a private structure, but it can be passed to + * \c mono_debug_symfile_lookup_location. + */ +MonoDebugMethodInfo * +mono_debug_lookup_method (MonoMethod *method) +{ + MonoDebugMethodInfo *minfo; + + if (mono_debug_format == MONO_DEBUG_FORMAT_NONE) + return NULL; + + mono_debugger_lock (); + minfo = mono_debug_lookup_method_internal (method); + mono_debugger_unlock (); + return minfo; +} + +typedef struct +{ + gboolean found; + MonoImage *image; +} LookupImageData; + +static void +lookup_image_func (gpointer key, gpointer value, gpointer user_data) +{ + MonoDebugHandle *handle = (MonoDebugHandle *) value; + LookupImageData *data = (LookupImageData *) user_data; + + if (data->found) + return; + + if (handle->image == data->image && handle->symfile) + data->found = TRUE; +} + +gboolean +mono_debug_image_has_debug_info (MonoImage *image) +{ + LookupImageData data; + + if (!mono_debug_handles) + return FALSE; + + memset (&data, 0, sizeof (data)); + data.image = image; + + mono_debugger_lock (); + g_hash_table_foreach (mono_debug_handles, lookup_image_func, &data); + mono_debugger_unlock (); + return data.found; +} + +static inline void +write_leb128 (guint32 value, guint8 *ptr, guint8 **rptr) +{ + do { + guint8 byte = value & 0x7f; + value >>= 7; + if (value) + byte |= 0x80; + *ptr++ = byte; + } while (value); + + *rptr = ptr; +} + +static inline void +write_sleb128 (gint32 value, guint8 *ptr, guint8 **rptr) +{ + gboolean more = 1; + + while (more) { + guint8 byte = value & 0x7f; + value >>= 7; + + if (((value == 0) && ((byte & 0x40) == 0)) || ((value == -1) && (byte & 0x40))) + more = 0; + else + byte |= 0x80; + *ptr++ = byte; + } + + *rptr = ptr; +} + +static void +write_variable (MonoDebugVarInfo *var, guint8 *ptr, guint8 **rptr) +{ + write_leb128 (var->index, ptr, &ptr); + write_sleb128 (var->offset, ptr, &ptr); + write_leb128 (var->size, ptr, &ptr); + write_leb128 (var->begin_scope, ptr, &ptr); + write_leb128 (var->end_scope, ptr, &ptr); + WRITE_UNALIGNED (gpointer, ptr, var->type); + ptr += sizeof (gpointer); + *rptr = ptr; +} + +/** + * mono_debug_add_method: + */ +MonoDebugMethodAddress * +mono_debug_add_method (MonoMethod *method, MonoDebugMethodJitInfo *jit, MonoDomain *domain) +{ + MonoDebugDataTable *table; + MonoDebugMethodAddress *address; + guint8 buffer [BUFSIZ]; + guint8 *ptr, *oldptr; + guint32 i, size, total_size, max_size; + + mono_debugger_lock (); + + table = lookup_data_table (domain); + + max_size = (5 * 5) + 1 + (10 * jit->num_line_numbers) + + (25 + sizeof (gpointer)) * (1 + jit->num_params + jit->num_locals); + + if (max_size > BUFSIZ) + ptr = oldptr = (guint8 *)g_malloc (max_size); + else + ptr = oldptr = buffer; + + write_leb128 (jit->prologue_end, ptr, &ptr); + write_leb128 (jit->epilogue_begin, ptr, &ptr); + + write_leb128 (jit->num_line_numbers, ptr, &ptr); + for (i = 0; i < jit->num_line_numbers; i++) { + MonoDebugLineNumberEntry *lne = &jit->line_numbers [i]; + + write_sleb128 (lne->il_offset, ptr, &ptr); + write_sleb128 (lne->native_offset, ptr, &ptr); + } + write_leb128 (jit->has_var_info, ptr, &ptr); + if (jit->has_var_info) { + *ptr++ = jit->this_var ? 1 : 0; + if (jit->this_var) + write_variable (jit->this_var, ptr, &ptr); + + write_leb128 (jit->num_params, ptr, &ptr); + for (i = 0; i < jit->num_params; i++) + write_variable (&jit->params [i], ptr, &ptr); + + write_leb128 (jit->num_locals, ptr, &ptr); + for (i = 0; i < jit->num_locals; i++) + write_variable (&jit->locals [i], ptr, &ptr); + + *ptr++ = jit->gsharedvt_info_var ? 1 : 0; + if (jit->gsharedvt_info_var) { + write_variable (jit->gsharedvt_info_var, ptr, &ptr); + write_variable (jit->gsharedvt_locals_var, ptr, &ptr); + } + } + + size = ptr - oldptr; + g_assert (size < max_size); + total_size = size + sizeof (MonoDebugMethodAddress); + + if (method_is_dynamic (method)) { + address = (MonoDebugMethodAddress *)g_malloc0 (total_size); + } else { + address = (MonoDebugMethodAddress *)mono_mempool_alloc (table->mp, total_size); + } + + address->code_start = jit->code_start; + address->code_size = jit->code_size; + + memcpy (&address->data, oldptr, size); + if (max_size > BUFSIZ) + g_free (oldptr); + + g_hash_table_insert (table->method_address_hash, method, address); + + mono_debugger_unlock (); + return address; +} + +void +mono_debug_remove_method (MonoMethod *method, MonoDomain *domain) +{ + MonoDebugDataTable *table; + MonoDebugMethodAddress *address; + + if (!mono_debug_initialized) + return; + + g_assert (method_is_dynamic (method)); + + mono_debugger_lock (); + + table = lookup_data_table (domain); + + address = (MonoDebugMethodAddress *)g_hash_table_lookup (table->method_address_hash, method); + if (address) + g_free (address); + + g_hash_table_remove (table->method_address_hash, method); + + mono_debugger_unlock (); +} + +/** + * mono_debug_add_delegate_trampoline: + */ +void +mono_debug_add_delegate_trampoline (gpointer code, int size) +{ +} + +static inline guint32 +read_leb128 (guint8 *ptr, guint8 **rptr) +{ + guint32 result = 0, shift = 0; + + while (TRUE) { + guint8 byte = *ptr++; + + result |= (byte & 0x7f) << shift; + if ((byte & 0x80) == 0) + break; + shift += 7; + } + + *rptr = ptr; + return result; +} + +static inline gint32 +read_sleb128 (guint8 *ptr, guint8 **rptr) +{ + gint32 result = 0; + guint32 shift = 0; + + while (TRUE) { + guint8 byte = *ptr++; + + result |= (byte & 0x7f) << shift; + shift += 7; + + if (byte & 0x80) + continue; + + if ((shift < 32) && (byte & 0x40)) + result |= - (1 << shift); + break; + } + + *rptr = ptr; + return result; +} + +static void +read_variable (MonoDebugVarInfo *var, guint8 *ptr, guint8 **rptr) +{ + var->index = read_leb128 (ptr, &ptr); + var->offset = read_sleb128 (ptr, &ptr); + var->size = read_leb128 (ptr, &ptr); + var->begin_scope = read_leb128 (ptr, &ptr); + var->end_scope = read_leb128 (ptr, &ptr); + READ_UNALIGNED (MonoType *, ptr, var->type); + ptr += sizeof (gpointer); + *rptr = ptr; +} + +void +mono_debug_free_method_jit_info (MonoDebugMethodJitInfo *jit) +{ + if (!jit) + return; + g_free (jit->line_numbers); + g_free (jit->this_var); + g_free (jit->params); + g_free (jit->locals); + g_free (jit->gsharedvt_info_var); + g_free (jit->gsharedvt_locals_var); + g_free (jit); +} + +static MonoDebugMethodJitInfo * +mono_debug_read_method (MonoDebugMethodAddress *address) +{ + MonoDebugMethodJitInfo *jit; + guint32 i; + guint8 *ptr; + + jit = g_new0 (MonoDebugMethodJitInfo, 1); + jit->code_start = address->code_start; + jit->code_size = address->code_size; + + ptr = (guint8 *) &address->data; + + jit->prologue_end = read_leb128 (ptr, &ptr); + jit->epilogue_begin = read_leb128 (ptr, &ptr); + + jit->num_line_numbers = read_leb128 (ptr, &ptr); + jit->line_numbers = g_new0 (MonoDebugLineNumberEntry, jit->num_line_numbers); + for (i = 0; i < jit->num_line_numbers; i++) { + MonoDebugLineNumberEntry *lne = &jit->line_numbers [i]; + + lne->il_offset = read_sleb128 (ptr, &ptr); + lne->native_offset = read_sleb128 (ptr, &ptr); + } + jit->has_var_info = read_leb128 (ptr, &ptr); + if (jit->has_var_info) { + if (*ptr++) { + jit->this_var = g_new0 (MonoDebugVarInfo, 1); + read_variable (jit->this_var, ptr, &ptr); + } + + jit->num_params = read_leb128 (ptr, &ptr); + jit->params = g_new0 (MonoDebugVarInfo, jit->num_params); + for (i = 0; i < jit->num_params; i++) + read_variable (&jit->params [i], ptr, &ptr); + + jit->num_locals = read_leb128 (ptr, &ptr); + jit->locals = g_new0 (MonoDebugVarInfo, jit->num_locals); + for (i = 0; i < jit->num_locals; i++) + read_variable (&jit->locals [i], ptr, &ptr); + + if (*ptr++) { + jit->gsharedvt_info_var = g_new0 (MonoDebugVarInfo, 1); + jit->gsharedvt_locals_var = g_new0 (MonoDebugVarInfo, 1); + read_variable (jit->gsharedvt_info_var, ptr, &ptr); + read_variable (jit->gsharedvt_locals_var, ptr, &ptr); + } + } + + return jit; +} + +static MonoDebugMethodJitInfo * +find_method (MonoMethod *method, MonoDomain *domain) +{ + MonoDebugDataTable *table; + MonoDebugMethodAddress *address; + + table = lookup_data_table (domain); + address = (MonoDebugMethodAddress *)g_hash_table_lookup (table->method_address_hash, method); + + if (!address) + return NULL; + + return mono_debug_read_method (address); +} + +MonoDebugMethodJitInfo * +mono_debug_find_method (MonoMethod *method, MonoDomain *domain) +{ + MonoDebugMethodJitInfo *res; + + if (mono_debug_format == MONO_DEBUG_FORMAT_NONE) + return NULL; + + mono_debugger_lock (); + res = find_method (method, domain); + mono_debugger_unlock (); + return res; +} + +MonoDebugMethodAddressList * +mono_debug_lookup_method_addresses (MonoMethod *method) +{ + g_assert_not_reached (); + return NULL; +} + +static gint32 +il_offset_from_address (MonoMethod *method, MonoDomain *domain, guint32 native_offset) +{ + MonoDebugMethodJitInfo *jit; + int i; + + jit = find_method (method, domain); + if (!jit || !jit->line_numbers) + goto cleanup_and_fail; + + for (i = jit->num_line_numbers - 1; i >= 0; i--) { + MonoDebugLineNumberEntry lne = jit->line_numbers [i]; + + if (lne.native_offset <= native_offset) { + mono_debug_free_method_jit_info (jit); + return lne.il_offset; + } + } + +cleanup_and_fail: + mono_debug_free_method_jit_info (jit); + return -1; +} + +/** + * mono_debug_il_offset_from_address: + * + * Compute the IL offset corresponding to \p native_offset inside the native + * code of \p method in \p domain. + */ +gint32 +mono_debug_il_offset_from_address (MonoMethod *method, MonoDomain *domain, guint32 native_offset) +{ + gint32 res; + + mono_debugger_lock (); + + res = il_offset_from_address (method, domain, native_offset); + + mono_debugger_unlock (); + + return res; +} + +/** + * mono_debug_lookup_source_location: + * \param address Native offset within the \p method's machine code. + * Lookup the source code corresponding to the machine instruction located at + * native offset \p address within \p method. + * The returned \c MonoDebugSourceLocation contains both file / line number + * information and the corresponding IL offset. It must be freed by + * \c mono_debug_free_source_location. + */ +MonoDebugSourceLocation * +mono_debug_lookup_source_location (MonoMethod *method, guint32 address, MonoDomain *domain) +{ + MonoDebugMethodInfo *minfo; + MonoDebugSourceLocation *location; + gint32 offset; + + if (mono_debug_format == MONO_DEBUG_FORMAT_NONE) + return NULL; + + mono_debugger_lock (); + minfo = mono_debug_lookup_method_internal (method); + if (!minfo || !minfo->handle) { + mono_debugger_unlock (); + return NULL; + } + + if (!minfo->handle->ppdb && (!minfo->handle->symfile || !mono_debug_symfile_is_loaded (minfo->handle->symfile))) { + mono_debugger_unlock (); + return NULL; + } + + offset = il_offset_from_address (method, domain, address); + if (offset < 0) { + mono_debugger_unlock (); + return NULL; + } + + if (minfo->handle->ppdb) + location = mono_ppdb_lookup_location (minfo, offset); + else + location = mono_debug_symfile_lookup_location (minfo, offset); + mono_debugger_unlock (); + return location; +} + +/** + * mono_debug_lookup_source_location_by_il: + * + * Same as mono_debug_lookup_source_location but take an IL_OFFSET argument. + */ +MonoDebugSourceLocation * +mono_debug_lookup_source_location_by_il (MonoMethod *method, guint32 il_offset, MonoDomain *domain) +{ + MonoDebugMethodInfo *minfo; + MonoDebugSourceLocation *location; + + if (mono_debug_format == MONO_DEBUG_FORMAT_NONE) + return NULL; + + mono_debugger_lock (); + minfo = mono_debug_lookup_method_internal (method); + if (!minfo || !minfo->handle) { + mono_debugger_unlock (); + return NULL; + } + + if (!minfo->handle->ppdb && (!minfo->handle->symfile || !mono_debug_symfile_is_loaded (minfo->handle->symfile))) { + mono_debugger_unlock (); + return NULL; + } + + if (minfo->handle->ppdb) + location = mono_ppdb_lookup_location (minfo, il_offset); + else + location = mono_debug_symfile_lookup_location (minfo, il_offset); + mono_debugger_unlock (); + return location; +} + +MonoDebugSourceLocation * +mono_debug_method_lookup_location (MonoDebugMethodInfo *minfo, int il_offset) +{ + MonoDebugSourceLocation *location; + + mono_debugger_lock (); + if (minfo->handle->ppdb) + location = mono_ppdb_lookup_location (minfo, il_offset); + else + location = mono_debug_symfile_lookup_location (minfo, il_offset); + mono_debugger_unlock (); + return location; +} + +/* + * mono_debug_lookup_locals: + * + * Return information about the local variables of MINFO. + * The result should be freed using mono_debug_free_locals (). + */ +MonoDebugLocalsInfo* +mono_debug_lookup_locals (MonoMethod *method) +{ + MonoDebugMethodInfo *minfo; + MonoDebugLocalsInfo *res; + + if (mono_debug_format == MONO_DEBUG_FORMAT_NONE) + return NULL; + + mono_debugger_lock (); + minfo = mono_debug_lookup_method_internal (method); + if (!minfo || !minfo->handle) { + mono_debugger_unlock (); + return NULL; + } + + if (minfo->handle->ppdb) { + res = mono_ppdb_lookup_locals (minfo); + } else { + if (!minfo->handle->symfile || !mono_debug_symfile_is_loaded (minfo->handle->symfile)) + res = NULL; + else + res = mono_debug_symfile_lookup_locals (minfo); + } + mono_debugger_unlock (); + + return res; +} + +/* + * mono_debug_free_locals: + * + * Free all the data allocated by mono_debug_lookup_locals (). + */ +void +mono_debug_free_locals (MonoDebugLocalsInfo *info) +{ + int i; + + for (i = 0; i < info->num_locals; ++i) + g_free (info->locals [i].name); + g_free (info->locals); + g_free (info->code_blocks); + g_free (info); +} + +/* +* mono_debug_lookup_method_async_debug_info: +* +* Return information about the async stepping information of method. +* The result should be freed using mono_debug_free_async_debug_info (). +*/ +MonoDebugMethodAsyncInfo* +mono_debug_lookup_method_async_debug_info (MonoMethod *method) +{ + MonoDebugMethodInfo *minfo; + MonoDebugMethodAsyncInfo *res = NULL; + + if (mono_debug_format == MONO_DEBUG_FORMAT_NONE) + return NULL; + + mono_debugger_lock (); + minfo = mono_debug_lookup_method_internal (method); + if (!minfo || !minfo->handle) { + mono_debugger_unlock (); + return NULL; + } + + if (minfo->handle->ppdb) + res = mono_ppdb_lookup_method_async_debug_info (minfo); + + mono_debugger_unlock (); + + return res; +} + +/* + * mono_debug_free_method_async_debug_info: + * + * Free all the data allocated by mono_debug_lookup_method_async_debug_info (). + */ +void +mono_debug_free_method_async_debug_info (MonoDebugMethodAsyncInfo *info) +{ + if (info->num_awaits) { + g_free (info->yield_offsets); + g_free (info->resume_offsets); + g_free (info->move_next_method_token); + } + g_free (info); +} + +/** + * mono_debug_free_source_location: + * \param location A \c MonoDebugSourceLocation + * Frees the \p location. + */ +void +mono_debug_free_source_location (MonoDebugSourceLocation *location) +{ + if (location) { + g_free (location->source_file); + g_free (location); + } +} + +static int (*get_seq_point) (MonoDomain *domain, MonoMethod *method, gint32 native_offset); + +void +mono_install_get_seq_point (MonoGetSeqPointFunc func) +{ + get_seq_point = func; +} + +/** + * mono_debug_print_stack_frame: + * \param native_offset Native offset within the \p method's machine code. + * Conventient wrapper around \c mono_debug_lookup_source_location which can be + * used if you only want to use the location to print a stack frame. + */ +gchar * +mono_debug_print_stack_frame (MonoMethod *method, guint32 native_offset, MonoDomain *domain) +{ + MonoDebugSourceLocation *location; + gchar *fname, *ptr, *res; + int offset; + + fname = mono_method_full_name (method, TRUE); + for (ptr = fname; *ptr; ptr++) { + if (*ptr == ':') *ptr = '.'; + } + + location = mono_debug_lookup_source_location (method, native_offset, domain); + + if (!location) { + if (mono_debug_initialized) { + mono_debugger_lock (); + offset = il_offset_from_address (method, domain, native_offset); + mono_debugger_unlock (); + } else { + offset = -1; + } + + if (offset < 0 && get_seq_point) + offset = get_seq_point (domain, method, native_offset); + + if (offset < 0) + res = g_strdup_printf ("at %s <0x%05x>", fname, native_offset); + else { + char *mvid = mono_guid_to_string_minimal ((uint8_t*)method->klass->image->heap_guid.data); + char *aotid = mono_runtime_get_aotid (); + if (aotid) + res = g_strdup_printf ("at %s [0x%05x] in <%s#%s>:0" , fname, offset, mvid, aotid); + else + res = g_strdup_printf ("at %s [0x%05x] in <%s>:0" , fname, offset, mvid); + + g_free (aotid); + g_free (mvid); + } + g_free (fname); + return res; + } + + res = g_strdup_printf ("at %s [0x%05x] in %s:%d", fname, location->il_offset, + location->source_file, location->row); + + g_free (fname); + mono_debug_free_source_location (location); + return res; +} + +void +mono_set_is_debugger_attached (gboolean attached) +{ + is_attached = attached; +} + +gboolean +mono_is_debugger_attached (void) +{ + return is_attached; +} + +/* + * Bundles + */ + +typedef struct _BundledSymfile BundledSymfile; + +struct _BundledSymfile { + BundledSymfile *next; + const char *aname; + const mono_byte *raw_contents; + int size; +}; + +static BundledSymfile *bundled_symfiles = NULL; + +/** + * mono_register_symfile_for_assembly: + */ +void +mono_register_symfile_for_assembly (const char *assembly_name, const mono_byte *raw_contents, int size) +{ + BundledSymfile *bsymfile; + + bsymfile = g_new0 (BundledSymfile, 1); + bsymfile->aname = assembly_name; + bsymfile->raw_contents = raw_contents; + bsymfile->size = size; + bsymfile->next = bundled_symfiles; + bundled_symfiles = bsymfile; +} + +static MonoDebugHandle * +open_symfile_from_bundle (MonoImage *image) +{ + BundledSymfile *bsymfile; + + for (bsymfile = bundled_symfiles; bsymfile; bsymfile = bsymfile->next) { + if (strcmp (bsymfile->aname, image->module_name)) + continue; + + return mono_debug_open_image (image, bsymfile->raw_contents, bsymfile->size); + } + + return NULL; +} + +void +mono_debugger_lock (void) +{ + g_assert (mono_debug_initialized); + mono_os_mutex_lock (&debugger_lock_mutex); +} + +void +mono_debugger_unlock (void) +{ + g_assert (mono_debug_initialized); + mono_os_mutex_unlock (&debugger_lock_mutex); +} + +/** + * mono_debug_enabled: + * + * Returns true is debug information is enabled. This doesn't relate if a debugger is present or not. + */ +mono_bool +mono_debug_enabled (void) +{ + return mono_debug_format != MONO_DEBUG_FORMAT_NONE; +} + +void +mono_debug_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrArray **source_file_list, int **source_files, MonoSymSeqPoint **seq_points, int *n_seq_points) +{ + if (minfo->handle->ppdb) + mono_ppdb_get_seq_points (minfo, source_file, source_file_list, source_files, seq_points, n_seq_points); + else + mono_debug_symfile_get_seq_points (minfo, source_file, source_file_list, source_files, seq_points, n_seq_points); +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/mono-debug.h b/unity-2019.4.24f1-mbe/mono/metadata/mono-debug.h new file mode 100644 index 000000000..29bbcaa9a --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/mono-debug.h @@ -0,0 +1,230 @@ +/** + * \file + * This header is only installed for use by the debugger: + * the structures and the API declared here are not supported. + */ + +#ifndef __MONO_DEBUG_H__ +#define __MONO_DEBUG_H__ + +#include +#include +#include + +MONO_BEGIN_DECLS + +typedef struct _MonoSymbolTable MonoSymbolTable; +typedef struct _MonoDebugDataTable MonoDebugDataTable; + +typedef struct _MonoSymbolFile MonoSymbolFile; +typedef struct _MonoPPDBFile MonoPPDBFile; + +typedef struct _MonoDebugHandle MonoDebugHandle; + +typedef struct _MonoDebugLineNumberEntry MonoDebugLineNumberEntry; + +typedef struct _MonoDebugVarInfo MonoDebugVarInfo; +typedef struct _MonoDebugMethodJitInfo MonoDebugMethodJitInfo; +typedef struct _MonoDebugMethodAddress MonoDebugMethodAddress; +typedef struct _MonoDebugMethodAddressList MonoDebugMethodAddressList; +typedef struct _MonoDebugClassEntry MonoDebugClassEntry; + +typedef struct _MonoDebugMethodInfo MonoDebugMethodInfo; +typedef struct _MonoDebugLocalsInfo MonoDebugLocalsInfo; +typedef struct _MonoDebugMethodAsyncInfo MonoDebugMethodAsyncInfo; +typedef struct _MonoDebugSourceLocation MonoDebugSourceLocation; + +typedef struct _MonoDebugList MonoDebugList; + +typedef enum { + MONO_DEBUG_FORMAT_NONE, + MONO_DEBUG_FORMAT_MONO, + /* Deprecated, the mdb debugger is not longer supported. */ + MONO_DEBUG_FORMAT_DEBUGGER +} MonoDebugFormat; + +/* + * NOTE: + * We intentionally do not use GList here since the debugger needs to know about + * the layout of the fields. +*/ +struct _MonoDebugList { + MonoDebugList *next; + const void* data; +}; + +struct _MonoSymbolTable { + uint64_t magic; + uint32_t version; + uint32_t total_size; + + /* + * Corlib and metadata info. + */ + MonoDebugHandle *corlib; + MonoDebugDataTable *global_data_table; + MonoDebugList *data_tables; + + /* + * The symbol files. + */ + MonoDebugList *symbol_files; +}; + +struct _MonoDebugHandle { + uint32_t index; + char *image_file; + MonoImage *image; + MonoDebugDataTable *type_table; + MonoSymbolFile *symfile; + MonoPPDBFile *ppdb; +}; + +struct _MonoDebugMethodJitInfo { + const mono_byte *code_start; + uint32_t code_size; + uint32_t prologue_end; + uint32_t epilogue_begin; + const mono_byte *wrapper_addr; + uint32_t num_line_numbers; + MonoDebugLineNumberEntry *line_numbers; + uint32_t has_var_info; + uint32_t num_params; + MonoDebugVarInfo *this_var; + MonoDebugVarInfo *params; + uint32_t num_locals; + MonoDebugVarInfo *locals; + MonoDebugVarInfo *gsharedvt_info_var; + MonoDebugVarInfo *gsharedvt_locals_var; +}; + +struct _MonoDebugMethodAddressList { + uint32_t size; + uint32_t count; + mono_byte data [MONO_ZERO_LEN_ARRAY]; +}; + +struct _MonoDebugSourceLocation { + char *source_file; + uint32_t row, column; + uint32_t il_offset; +}; + +MONO_API mono_bool mono_debug_enabled (void); + +/* + * These bits of the MonoDebugLocalInfo's "index" field are flags specifying + * where the variable is actually stored. + * + * See relocate_variable() in debug-symfile.c for more info. + */ +#define MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS 0xf0000000 + +/* The variable is in register "index". */ +#define MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER 0 + +/* The variable is at offset "offset" from register "index". */ +#define MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET 0x10000000 + +/* The variable is in the two registers "offset" and "index". */ +#define MONO_DEBUG_VAR_ADDRESS_MODE_TWO_REGISTERS 0x20000000 + +/* The variable is dead. */ +#define MONO_DEBUG_VAR_ADDRESS_MODE_DEAD 0x30000000 + +/* Same as REGOFFSET, but do an indirection */ +#define MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET_INDIR 0x40000000 + +/* gsharedvt local */ +#define MONO_DEBUG_VAR_ADDRESS_MODE_GSHAREDVT_LOCAL 0x50000000 + +/* variable is a vt address */ +#define MONO_DEBUG_VAR_ADDRESS_MODE_VTADDR 0x60000000 + +struct _MonoDebugVarInfo { + uint32_t index; + uint32_t offset; + uint32_t size; + uint32_t begin_scope; + uint32_t end_scope; + MonoType *type; +}; + +#define MONO_DEBUGGER_MAJOR_VERSION 81 +#define MONO_DEBUGGER_MINOR_VERSION 6 +#define MONO_DEBUGGER_MAGIC 0x7aff65af4253d427ULL + +MONO_API void mono_debug_init (MonoDebugFormat format); +MONO_API void mono_debug_open_image_from_memory (MonoImage *image, const mono_byte *raw_contents, int size); +MONO_API void mono_debug_cleanup (void); + +MONO_API void mono_debug_close_image (MonoImage *image); + +MONO_API void mono_debug_domain_unload (MonoDomain *domain); +MONO_API void mono_debug_domain_create (MonoDomain *domain); + +MONO_API MonoDebugMethodAddress * +mono_debug_add_method (MonoMethod *method, MonoDebugMethodJitInfo *jit, MonoDomain *domain); + +MONO_API void +mono_debug_remove_method (MonoMethod *method, MonoDomain *domain); + +MONO_API MonoDebugMethodInfo * +mono_debug_lookup_method (MonoMethod *method); + +MONO_API MonoDebugMethodAddressList * +mono_debug_lookup_method_addresses (MonoMethod *method); + +MONO_API MonoDebugMethodJitInfo* +mono_debug_find_method (MonoMethod *method, MonoDomain *domain); + +MONO_API void +mono_debug_free_method_jit_info (MonoDebugMethodJitInfo *jit); + + +MONO_API void +mono_debug_add_delegate_trampoline (void* code, int size); + +MONO_API MonoDebugLocalsInfo* +mono_debug_lookup_locals (MonoMethod *method); + +MonoDebugMethodAsyncInfo* +mono_debug_lookup_method_async_debug_info (MonoMethod *method); + +MonoDebugSourceLocation * +mono_debug_method_lookup_location (MonoDebugMethodInfo *minfo, int il_offset); + +/* + * Line number support. + */ + +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); + +MONO_API void +mono_debug_free_source_location (MonoDebugSourceLocation *location); + +MONO_API char * +mono_debug_print_stack_frame (MonoMethod *method, uint32_t native_offset, MonoDomain *domain); + +/* + * Mono Debugger support functions + * + * These methods are used by the JIT while running inside the Mono Debugger. + */ + +MONO_API int mono_debugger_method_has_breakpoint (MonoMethod *method); +MONO_API int mono_debugger_insert_breakpoint (const char *method_name, mono_bool include_namespace); + +MONO_API void mono_set_is_debugger_attached (mono_bool attached); +MONO_API mono_bool mono_is_debugger_attached (void); + +MONO_END_DECLS + +#endif /* __MONO_DEBUG_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/mono-endian.c b/unity-2019.4.24f1-mbe/mono/metadata/mono-endian.c new file mode 100644 index 000000000..d7822c72f --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/mono-endian.c @@ -0,0 +1,94 @@ +/** + * \file + * + * Author: + * Mono Project (http://www.mono-project.com) + * + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#include +#include +#include "mono-endian.h" + +#if NO_UNALIGNED_ACCESS + +typedef union { + char c [2]; + guint16 i; +} mono_rint16; + +typedef union { + char c [4]; + guint32 i; +} mono_rint32; + +typedef union { + char c [8]; + guint64 i; +} mono_rint64; + +guint16 +mono_read16 (const unsigned char *x) +{ + mono_rint16 r; +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + r.c [0] = x [0]; + r.c [1] = x [1]; +#else + r.c [1] = x [0]; + r.c [0] = x [1]; +#endif + return r.i; +} + +guint32 +mono_read32 (const unsigned char *x) +{ + mono_rint32 r; +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + r.c [0] = x [0]; + r.c [1] = x [1]; + r.c [2] = x [2]; + r.c [3] = x [3]; +#else + r.c [3] = x [0]; + r.c [2] = x [1]; + r.c [1] = x [2]; + r.c [0] = x [3]; +#endif + return r.i; +} + +guint64 +mono_read64 (const unsigned char *x) +{ + mono_rint64 r; +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + r.c [0] = x [0]; + r.c [1] = x [1]; + r.c [2] = x [2]; + r.c [3] = x [3]; + r.c [4] = x [4]; + r.c [5] = x [5]; + r.c [6] = x [6]; + r.c [7] = x [7]; +#else + r.c [7] = x [0]; + r.c [6] = x [1]; + r.c [5] = x [2]; + r.c [4] = x [3]; + r.c [3] = x [4]; + r.c [2] = x [5]; + r.c [1] = x [6]; + r.c [0] = x [7]; +#endif + return r.i; +} + +#else /* NO_UNALIGNED_ACCESS */ + +MONO_EMPTY_SOURCE_FILE (mono_endian); + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/mono-endian.h b/unity-2019.4.24f1-mbe/mono/metadata/mono-endian.h new file mode 100644 index 000000000..8704dc595 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/mono-endian.h @@ -0,0 +1,97 @@ +/** + * \file + */ + +#ifndef _MONO_METADATA_ENDIAN_H_ +#define _MONO_METADATA_ENDIAN_H_ 1 + +#include + +typedef union { + guint32 ival; + float fval; +} mono_rfloat; + +typedef union { + guint64 ival; + double fval; + unsigned char cval [8]; +} mono_rdouble; + +#if defined(__s390x__) + +#define read16(x) s390x_read16(*(guint16 *)(x)) +#define read32(x) s390x_read32(*(guint32 *)(x)) +#define read64(x) s390x_read64(*(guint64 *)(x)) + +static __inline__ guint16 +s390x_read16(guint16 x) +{ + guint16 ret; + + __asm__ (" lrvr %0,%1\n" + " sra %0,16\n" + : "=r" (ret) : "r" (x)); + + return(ret); +} + +static __inline__ guint32 +s390x_read32(guint32 x) +{ + guint32 ret; + + __asm__ (" lrvr %0,%1\n" + : "=r" (ret) : "r" (x)); + + return(ret); +} + +static __inline__ guint64 +s390x_read64(guint64 x) +{ + guint64 ret; + + __asm__ (" lrvgr %0,%1\n" + : "=r" (ret) : "r" (x)); + + return(ret); +} + +#else + +# if NO_UNALIGNED_ACCESS + +guint16 mono_read16 (const unsigned char *x); +guint32 mono_read32 (const unsigned char *x); +guint64 mono_read64 (const unsigned char *x); + +#define read16(x) (mono_read16 ((const unsigned char *)(x))) +#define read32(x) (mono_read32 ((const unsigned char *)(x))) +#define read64(x) (mono_read64 ((const unsigned char *)(x))) + +# else + +#define read16(x) GUINT16_FROM_LE (*((const guint16 *) (x))) +#define read32(x) GUINT32_FROM_LE (*((const guint32 *) (x))) +#define read64(x) GUINT64_FROM_LE (*((const guint64 *) (x))) + +# endif + +#endif + +#define readr4(x,dest) \ + do { \ + mono_rfloat mf; \ + mf.ival = read32 ((x)); \ + *(dest) = mf.fval; \ + } while (0) + +#define readr8(x,dest) \ + do { \ + mono_rdouble mf; \ + mf.ival = read64 ((x)); \ + *(dest) = mf.fval; \ + } while (0) + +#endif /* _MONO_METADATA_ENDIAN_H_ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/mono-gc.h b/unity-2019.4.24f1-mbe/mono/metadata/mono-gc.h new file mode 100644 index 000000000..f2ce265ab --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/mono-gc.h @@ -0,0 +1,126 @@ +/** + * \file + * GC related public interface + * + */ +#ifndef __METADATA_MONO_GC_H__ +#define __METADATA_MONO_GC_H__ + +#include + +MONO_BEGIN_DECLS + +typedef int (*MonoGCReferences) (MonoObject *obj, MonoClass *klass, uintptr_t size, uintptr_t num, MonoObject **refs, uintptr_t *offsets, void *data); + +/** + * This enum is used by the profiler API when reporting root registration. + */ +typedef enum { + /** + * Roots external to Mono. Embedders may only use this value. + */ + MONO_ROOT_SOURCE_EXTERNAL = 0, + /** + * Thread call stack. + * + * The \c key parameter is a thread ID as a \c uintptr_t. + */ + MONO_ROOT_SOURCE_STACK = 1, + /** + * Roots in the finalizer queue. This is a pseudo-root. + */ + MONO_ROOT_SOURCE_FINALIZER_QUEUE = 2, + /** + * Managed \c static variables. + * + * The \c key parameter is a \c MonoVTable pointer. + */ + MONO_ROOT_SOURCE_STATIC = 3, + /** + * Managed \c static variables with \c ThreadStaticAttribute. + * + * The \c key parameter is a thread ID as a \c uintptr_t. + */ + MONO_ROOT_SOURCE_THREAD_STATIC = 4, + /** + * Managed \c static variables with \c ContextStaticAttribute. + * + * The \c key parameter is a \c MonoAppContext pointer. + */ + MONO_ROOT_SOURCE_CONTEXT_STATIC = 5, + /** + * \c GCHandle structures. + */ + MONO_ROOT_SOURCE_GC_HANDLE = 6, + /** + * Roots in the just-in-time compiler. + */ + MONO_ROOT_SOURCE_JIT = 7, + /** + * Roots in the threading subsystem. + * + * The \c key parameter, if not \c NULL, is a thread ID as a \c uintptr_t. + */ + MONO_ROOT_SOURCE_THREADING = 8, + /** + * Roots in application domains. + * + * The \c key parameter, if not \c NULL, is a \c MonoDomain pointer. + */ + MONO_ROOT_SOURCE_DOMAIN = 9, + /** + * Roots in reflection code. + * + * The \c key parameter, if not \c NULL, is a \c MonoVTable pointer. + */ + MONO_ROOT_SOURCE_REFLECTION = 10, + /** + * Roots from P/Invoke or other marshaling infrastructure. + */ + MONO_ROOT_SOURCE_MARSHAL = 11, + /** + * Roots in the thread pool data structures. + */ + MONO_ROOT_SOURCE_THREAD_POOL = 12, + /** + * Roots in the debugger agent. + */ + MONO_ROOT_SOURCE_DEBUGGER = 13, + /** + * Roots in the runtime handle stack. This is a pseudo-root. + * + * The \c key parameter is a thread ID as a \c uintptr_t. + */ + MONO_ROOT_SOURCE_HANDLE = 14, +} MonoGCRootSource; + +typedef enum { + MONO_GC_HANDLE_TYPE_MIN = 0, + MONO_GC_HANDLE_WEAK = MONO_GC_HANDLE_TYPE_MIN, + MONO_GC_HANDLE_WEAK_TRACK_RESURRECTION, + MONO_GC_HANDLE_NORMAL, + MONO_GC_HANDLE_PINNED, + MONO_GC_HANDLE_TYPE_MAX, +} 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); +/* 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); + +MONO_END_DECLS + +#endif /* __METADATA_MONO_GC_H__ */ + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/mono-hash.c b/unity-2019.4.24f1-mbe/mono/metadata/mono-hash.c new file mode 100644 index 000000000..73d623d57 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/mono-hash.c @@ -0,0 +1,505 @@ +/** + * \file + * Hashtable implementation + * + * Author: + * Miguel de Icaza (miguel@novell.com) + * + * (C) 2006 Novell, Inc. + * + * 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. + */ +#include +#include +#include +#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 { + GHashFunc hash_func; + GEqualFunc key_equal_func; + + MonoObject **keys; + MonoObject **values; + int table_size; + int in_use; + GDestroyNotify value_destroy_func, key_destroy_func; + MonoGHashGCType gc_type; + MonoGCRootSource source; + void *key; + const char *msg; +}; + +#if UNUSED +static gboolean +test_prime (int x) +{ + if ((x & 1) != 0) { + int n; + for (n = 3; n< (int)sqrt (x); n += 2) { + if ((x % n) == 0) + return FALSE; + } + return TRUE; + } + // There is only one even prime - 2. + return (x == 2); +} + +static int +calc_prime (int x) +{ + int i; + + for (i = (x & (~1))-1; i< G_MAXINT32; i += 2) { + if (test_prime (i)) + return i; + } + return x; +} +#endif + +#define HASH_TABLE_MAX_LOAD_FACTOR 0.7f +/* We didn't really do compaction before, keep it lenient for now */ +#define HASH_TABLE_MIN_LOAD_FACTOR 0.05f +/* We triple the table size at rehash time, similar with previous implementation */ +#define HASH_TABLE_RESIZE_RATIO 3 + +static inline void mono_g_hash_table_key_store (MonoGHashTable *hash, int slot, MonoObject* key) +{ + MonoObject **key_addr = &hash->keys [slot]; + if (hash->gc_type & MONO_HASH_KEY_GC) + mono_gc_wbarrier_generic_store (key_addr, key); + else + *key_addr = key; +} + +static inline void mono_g_hash_table_value_store (MonoGHashTable *hash, int slot, MonoObject* value) +{ + MonoObject **value_addr = &hash->values [slot]; + if (hash->gc_type & MONO_HASH_VALUE_GC) + mono_gc_wbarrier_generic_store (value_addr, value); + else + *value_addr = value; +} + +/* Returns position of key or of an empty slot for it */ +static inline int mono_g_hash_table_find_slot (MonoGHashTable *hash, const MonoObject *key) +{ + guint start = ((*hash->hash_func) (key)) % hash->table_size; + guint i = start; + + if (hash->key_equal_func) { + GEqualFunc equal = hash->key_equal_func; + + while (hash->keys [i] && !(*equal) (hash->keys [i], key)) { + i++; + if (i == hash->table_size) + i = 0; + } + } else { + while (hash->keys [i] && hash->keys [i] != key) { + i++; + if (i == hash->table_size) + i = 0; + } + } + + gint32 max_length = UnlockedRead (&mono_g_hash_table_max_chain_length); + if (i > start && (i - start) > max_length) + UnlockedWrite (&mono_g_hash_table_max_chain_length, i - start); + else if (i < start && (hash->table_size - (start - i)) > max_length) + UnlockedWrite (&mono_g_hash_table_max_chain_length, hash->table_size - (start - i)); + + return i; +} + + +MonoGHashTable * +mono_g_hash_table_new_type (GHashFunc hash_func, GEqualFunc key_equal_func, MonoGHashGCType type, MonoGCRootSource source, void *key, const char *msg) +{ + MonoGHashTable *hash; + + if (!hash_func) + hash_func = g_direct_hash; + + hash = g_new0 (MonoGHashTable, 1); + + hash->hash_func = hash_func; + hash->key_equal_func = key_equal_func; + + hash->table_size = g_spaced_primes_closest (1); + hash->keys = g_new0 (MonoObject*, hash->table_size); + hash->values = g_new0 (MonoObject*, hash->table_size); + + hash->gc_type = type; + hash->source = source; + hash->key = key; + hash->msg = msg; + + if (type > MONO_HASH_KEY_VALUE_GC) + g_error ("wrong type for gc hashtable"); + + if (hash->gc_type & MONO_HASH_KEY_GC) + mono_gc_register_root_wbarrier ((char*)hash->keys, sizeof (MonoObject*) * hash->table_size, mono_gc_make_vector_descr (), hash->source, hash->key, hash->msg); + if (hash->gc_type & MONO_HASH_VALUE_GC) + mono_gc_register_root_wbarrier ((char*)hash->values, sizeof (MonoObject*) * hash->table_size, mono_gc_make_vector_descr (), hash->source, hash->key, hash->msg); + + return hash; +} + +typedef struct { + MonoGHashTable *hash; + int new_size; + MonoObject **keys; + MonoObject **values; +} RehashData; + +static void* +do_rehash (void *_data) +{ + RehashData *data = (RehashData *)_data; + MonoGHashTable *hash = data->hash; + int current_size, i; + MonoObject **old_keys; + MonoObject **old_values; + + current_size = hash->table_size; + hash->table_size = data->new_size; + old_keys = hash->keys; + old_values = hash->values; + hash->keys = data->keys; + hash->values = data->values; + + for (i = 0; i < current_size; i++) { + if (old_keys [i]) { + int slot = mono_g_hash_table_find_slot (hash, old_keys [i]); + mono_g_hash_table_key_store (hash, slot, old_keys [i]); + mono_g_hash_table_value_store (hash, slot, old_values [i]); + } + } + return NULL; +} + +static void +rehash (MonoGHashTable *hash) +{ + MONO_REQ_GC_UNSAFE_MODE; //we must run in unsafe mode to make rehash safe + + RehashData data; + void *old_keys = hash->keys; + void *old_values = hash->values; + + data.hash = hash; + /* + * Rehash to a size that can fit the current elements. Rehash relative to in_use + * to allow also for compaction. + */ + data.new_size = g_spaced_primes_closest (hash->in_use / HASH_TABLE_MAX_LOAD_FACTOR * HASH_TABLE_RESIZE_RATIO); + data.keys = g_new0 (MonoObject*, data.new_size); + data.values = g_new0 (MonoObject*, data.new_size); + + if (hash->gc_type & MONO_HASH_KEY_GC) + mono_gc_register_root_wbarrier ((char*)data.keys, sizeof (MonoObject*) * data.new_size, mono_gc_make_vector_descr (), hash->source, hash->key, hash->msg); + if (hash->gc_type & MONO_HASH_VALUE_GC) + mono_gc_register_root_wbarrier ((char*)data.values, sizeof (MonoObject*) * data.new_size, mono_gc_make_vector_descr (), hash->source, hash->key, hash->msg); + + if (!mono_threads_is_coop_enabled ()) { + mono_gc_invoke_with_gc_lock (do_rehash, &data); + } else { + /* We cannot be preempted */ + do_rehash (&data); + } + + if (hash->gc_type & MONO_HASH_KEY_GC) + mono_gc_deregister_root ((char*)old_keys); + if (hash->gc_type & MONO_HASH_VALUE_GC) + mono_gc_deregister_root ((char*)old_values); + + g_free (old_keys); + g_free (old_values); +} + +/** + * mono_g_hash_table_size: + */ +guint +mono_g_hash_table_size (MonoGHashTable *hash) +{ + g_return_val_if_fail (hash != NULL, 0); + + return hash->in_use; +} + +/** + * mono_g_hash_table_lookup: + */ +gpointer +mono_g_hash_table_lookup (MonoGHashTable *hash, gconstpointer key) +{ + gpointer orig_key, value; + + if (mono_g_hash_table_lookup_extended (hash, key, &orig_key, &value)) + return value; + else + return NULL; +} + +/** + * mono_g_hash_table_lookup_extended: + */ +gboolean +mono_g_hash_table_lookup_extended (MonoGHashTable *hash, gconstpointer key, gpointer *orig_key, gpointer *value) +{ + int slot; + + g_return_val_if_fail (hash != NULL, FALSE); + + slot = mono_g_hash_table_find_slot (hash, key); + + if (hash->keys [slot]) { + *orig_key = hash->keys [slot]; + *value = hash->values [slot]; + return TRUE; + } + + return FALSE; +} + +/** + * mono_g_hash_table_foreach: + */ +void +mono_g_hash_table_foreach (MonoGHashTable *hash, GHFunc func, gpointer user_data) +{ + int i; + + g_return_if_fail (hash != NULL); + g_return_if_fail (func != NULL); + + for (i = 0; i < hash->table_size; i++) { + if (hash->keys [i]) + (*func)(hash->keys [i], hash->values [i], user_data); + } +} + +gpointer +mono_g_hash_table_find (MonoGHashTable *hash, GHRFunc predicate, gpointer user_data) +{ + int i; + + g_return_val_if_fail (hash != NULL, NULL); + g_return_val_if_fail (predicate != NULL, NULL); + + for (i = 0; i < hash->table_size; i++) { + if (hash->keys [i] && (*predicate)(hash->keys [i], hash->values [i], user_data)) + return hash->values [i]; + } + return NULL; +} + +/** + * mono_g_hash_table_remove: + */ +gboolean +mono_g_hash_table_remove (MonoGHashTable *hash, gconstpointer key) +{ + int slot, last_clear_slot; + + g_return_val_if_fail (hash != NULL, FALSE); + slot = mono_g_hash_table_find_slot (hash, key); + + if (!hash->keys [slot]) + return FALSE; + + if (hash->key_destroy_func) + (*hash->key_destroy_func)(hash->keys [slot]); + hash->keys [slot] = NULL; + if (hash->value_destroy_func) + (*hash->value_destroy_func)(hash->values [slot]); + hash->values [slot] = NULL; + hash->in_use--; + + /* + * When we insert in the hashtable, if the required position is occupied we + * consecutively try out following positions. In order to be able to find + * if a key exists or not in the array (without traversing the entire hash) + * we maintain the constraint that there can be no free slots between two + * entries that are hashed to the same position. This means that, at search + * time, when we encounter a free slot we can stop looking for collissions. + * Similarly, at remove time, we need to shift all following slots to their + * normal slot, until we reach an empty slot. + */ + last_clear_slot = slot; + slot = (slot + 1) % hash->table_size; + while (hash->keys [slot]) { + guint hashcode = ((*hash->hash_func)(hash->keys [slot])) % hash->table_size; + /* + * We try to move the current element to last_clear_slot, but only if + * it brings it closer to its normal position (hashcode) + */ + if ((last_clear_slot < slot && (hashcode > slot || hashcode <= last_clear_slot)) || + (last_clear_slot > slot && (hashcode > slot && hashcode <= last_clear_slot))) { + mono_g_hash_table_key_store (hash, last_clear_slot, hash->keys [slot]); + mono_g_hash_table_value_store (hash, last_clear_slot, hash->values [slot]); + hash->keys [slot] = NULL; + hash->values [slot] = NULL; + last_clear_slot = slot; + } + slot++; + if (slot == hash->table_size) + slot = 0; + } + return TRUE; +} + +/** + * mono_g_hash_table_foreach_remove: + */ +guint +mono_g_hash_table_foreach_remove (MonoGHashTable *hash, GHRFunc func, gpointer user_data) +{ + int i; + int count = 0; + + g_return_val_if_fail (hash != NULL, 0); + g_return_val_if_fail (func != NULL, 0); + + for (i = 0; i < hash->table_size; i++) { + if (hash->keys [i] && (*func)(hash->keys [i], hash->values [i], user_data)) { + mono_g_hash_table_remove (hash, hash->keys [i]); + count++; + /* Retry current slot in case the removal shifted elements */ + i--; + } + } + if (hash->in_use < hash->table_size * HASH_TABLE_MIN_LOAD_FACTOR) + rehash (hash); + return count; +} + +/** + * mono_g_hash_table_destroy: + */ +void +mono_g_hash_table_destroy (MonoGHashTable *hash) +{ + int i; + + g_return_if_fail (hash != NULL); + + if (hash->gc_type & MONO_HASH_KEY_GC) + mono_gc_deregister_root ((char*)hash->keys); + if (hash->gc_type & MONO_HASH_VALUE_GC) + mono_gc_deregister_root ((char*)hash->values); + + for (i = 0; i < hash->table_size; i++) { + if (hash->keys [i]) { + if (hash->key_destroy_func) + (*hash->key_destroy_func)(hash->keys [i]); + if (hash->value_destroy_func) + (*hash->value_destroy_func)(hash->values [i]); + } + } + g_free (hash->keys); + g_free (hash->values); + g_free (hash); +} + +static void +mono_g_hash_table_insert_replace (MonoGHashTable *hash, gpointer key, gpointer value, gboolean replace) +{ + int slot; + g_return_if_fail (hash != NULL); + + if (hash->in_use > (hash->table_size * HASH_TABLE_MAX_LOAD_FACTOR)) + rehash (hash); + + slot = mono_g_hash_table_find_slot (hash, key); + + if (hash->keys [slot]) { + if (replace) { + if (hash->key_destroy_func) + (*hash->key_destroy_func)(hash->keys [slot]); + mono_g_hash_table_key_store (hash, slot, (MonoObject*)key); + } + if (hash->value_destroy_func) + (*hash->value_destroy_func) (hash->values [slot]); + mono_g_hash_table_value_store (hash, slot, (MonoObject*)value); + } else { + mono_g_hash_table_key_store (hash, slot, (MonoObject*)key); + mono_g_hash_table_value_store (hash, slot, (MonoObject*)value); + hash->in_use++; + } +} + +/** + * mono_g_hash_table_insert: + */ +void +mono_g_hash_table_insert (MonoGHashTable *h, gpointer k, gpointer v) +{ + mono_g_hash_table_insert_replace (h, k, v, FALSE); +} + +/** + * mono_g_hash_table_replace: + */ +void +mono_g_hash_table_replace(MonoGHashTable *h, gpointer k, gpointer v) +{ + mono_g_hash_table_insert_replace (h, k, v, TRUE); +} + +void +mono_g_hash_table_print_stats (MonoGHashTable *hash) +{ + int i = 0, chain_size = 0, max_chain_size = 0; + gboolean wrapped_around = FALSE; + + while (TRUE) { + if (hash->keys [i]) { + chain_size++; + } else { + max_chain_size = MAX(max_chain_size, chain_size); + chain_size = 0; + if (wrapped_around) + break; + } + + if (i == (hash->table_size - 1)) { + wrapped_around = TRUE; + i = 0; + } else { + i++; + } + } + /* Rehash to a size that can fit the current elements */ + printf ("Size: %d Table Size: %d Max Chain Length: %d\n", hash->in_use, hash->table_size, max_chain_size); +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/mono-hash.h b/unity-2019.4.24f1-mbe/mono/metadata/mono-hash.h new file mode 100644 index 000000000..4e945a04e --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/mono-hash.h @@ -0,0 +1,43 @@ +/** + * \file + * GC-aware hashtable, based on Eglib's Hashtable + * + * Authors: + * Paolo Molaro (lupus@xamarin.com) + * + * Copyright 2013 Xamarin Inc (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#ifndef __MONO_G_HASH_H__ +#define __MONO_G_HASH_H__ + +#include + +MONO_BEGIN_DECLS +/* do not change the values of this enum */ +typedef enum { + MONO_HASH_KEY_GC = 1, + MONO_HASH_VALUE_GC = 2, + MONO_HASH_KEY_VALUE_GC = MONO_HASH_KEY_GC | MONO_HASH_VALUE_GC, +} MonoGHashGCType; + +extern gint32 mono_g_hash_table_max_chain_length; + +typedef struct _MonoGHashTable MonoGHashTable; + +MONO_API MonoGHashTable *mono_g_hash_table_new_type (GHashFunc hash_func, GEqualFunc key_equal_func, MonoGHashGCType type, MonoGCRootSource source, void *key, const char *msg); +MONO_API guint mono_g_hash_table_size (MonoGHashTable *hash); +MONO_API gpointer mono_g_hash_table_lookup (MonoGHashTable *hash, gconstpointer key); +MONO_API gboolean mono_g_hash_table_lookup_extended (MonoGHashTable *hash, gconstpointer key, gpointer *orig_key, gpointer *value); +MONO_API void mono_g_hash_table_foreach (MonoGHashTable *hash, GHFunc func, gpointer user_data); +MONO_API gpointer mono_g_hash_table_find (MonoGHashTable *hash, GHRFunc predicate, gpointer user_data); +MONO_API gboolean mono_g_hash_table_remove (MonoGHashTable *hash, gconstpointer key); +MONO_API guint mono_g_hash_table_foreach_remove (MonoGHashTable *hash, GHRFunc func, gpointer user_data); +MONO_API void mono_g_hash_table_destroy (MonoGHashTable *hash); +MONO_API void mono_g_hash_table_insert (MonoGHashTable *h, gpointer k, gpointer v); +MONO_API void mono_g_hash_table_replace (MonoGHashTable *h, gpointer k, gpointer v); +MONO_API void mono_g_hash_table_print_stats (MonoGHashTable *table); + +MONO_END_DECLS +#endif /* __MONO_G_HASH_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/mono-mlist.c b/unity-2019.4.24f1-mbe/mono/metadata/mono-mlist.c new file mode 100644 index 000000000..fcf91ae3a --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/mono-mlist.c @@ -0,0 +1,283 @@ +/** + * \file + * Managed object list implementation + * + * Author: + * Paolo Molaro (lupus@ximian.com) + * + * Copyright 2006-2009 Novell, Inc (http://www.novell.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#include "mono/metadata/mono-mlist.h" +#include "mono/metadata/appdomain.h" +#include "mono/metadata/class-internals.h" +#include "mono/metadata/object-internals.h" + + +static +MonoMList* mono_mlist_alloc_checked (MonoObject *data, MonoError *error); + + +/* matches the System.MonoListItem object*/ +struct _MonoMList { + MonoObject object; + MonoMList *next; + MonoObject *data; +}; + +/* + * note: we only allocate in the root domain: this lists are + * not exposed to managed code + */ +static MonoVTable *monolist_item_vtable = NULL; + +/** + * mono_mlist_alloc: + * \param data object to use as data + * Allocates a new managed list node with \p data as the contents. + * A managed list node also represents a singly-linked list. + * Managed lists are garbage collected, so there is no free routine + * and the user is required to keep references to the managed list + * to prevent it from being garbage collected. + */ +MonoMList* +mono_mlist_alloc (MonoObject *data) +{ + MonoError error; + MonoMList *result = mono_mlist_alloc_checked (data, &error); + mono_error_cleanup (&error); + return result; +} + +/** + * mono_mlist_alloc_checked: + * \param data object to use as data + * \param error set on error + * Allocates a new managed list node with \p data as the contents. A + * managed list node also represents a singly-linked list. Managed + * lists are garbage collected, so there is no free routine and the + * user is required to keep references to the managed list to prevent + * it from being garbage collected. On failure returns NULL and sets + * \p error. + */ +MonoMList* +mono_mlist_alloc_checked (MonoObject *data, MonoError *error) +{ + error_init (error); + MonoMList* res; + if (!monolist_item_vtable) { + MonoClass *klass = mono_class_load_from_name (mono_defaults.corlib, "System", "MonoListItem"); + monolist_item_vtable = mono_class_vtable (mono_get_root_domain (), klass); + g_assert (monolist_item_vtable); + } + res = (MonoMList*)mono_object_new_specific_checked (monolist_item_vtable, error); + return_val_if_nok (error, NULL); + MONO_OBJECT_SETREF (res, data, data); + return res; +} + +/** + * mono_mlist_get_data: + * \param list the managed list node + * Get the object stored in the list node \p list. + */ +MonoObject* +mono_mlist_get_data (MonoMList* list) +{ + return list->data; +} + +/** + * mono_mlist_set_data: + * \param list the managed list node + * Set the object content in the list node \p list. + */ +void +mono_mlist_set_data (MonoMList* list, MonoObject *data) +{ + MONO_OBJECT_SETREF (list, data, data); +} + +/** + * mono_mlist_set_next: + * \param list a managed list node + * \param next list node that will be next for the \p list node. + * Set next node for \p list to \p next. + */ +MonoMList * +mono_mlist_set_next (MonoMList* list, MonoMList *next) +{ + if (!list) + return next; + + MONO_OBJECT_SETREF (list, next, next); + return list; +} + +/** + * mono_mlist_length: + * \param list the managed list + * Get the number of items in the list \p list. + * Since managed lists are singly-linked, this operation takes O(n) time. + */ +int +mono_mlist_length (MonoMList* list) +{ + int len = 0; + while (list) { + list = list->next; + ++len; + } + return len; +} + +/** + * mono_mlist_next: + * \param list the managed list node + * Returns the next managed list node starting from \p list. + */ +MonoMList* +mono_mlist_next (MonoMList* list) +{ + return list->next; +} + +/** + * mono_mlist_last: + * \param list the managed list node + * Returns the last managed list node in list \p list. + * Since managed lists are singly-linked, this operation takes O(n) time. + */ +MonoMList* +mono_mlist_last (MonoMList* list) +{ + if (list) { + while (list->next) + list = list->next; + return list; + } + return NULL; +} + +/** + * mono_mlist_prepend: + * \param list the managed list + * \param data the object to add to the list + * Allocate a new list node with \p data as content and prepend it + * to the list \p list. \p list can be NULL. + */ +MonoMList* +mono_mlist_prepend (MonoMList* list, MonoObject *data) +{ + MonoError error; + MonoMList *result = mono_mlist_prepend_checked (list, data, &error); + mono_error_cleanup (&error); + return result; +} + +/** + * mono_mlist_prepend_checked: + * \param list the managed list + * \param data the object to add to the list + * \param error set on error + * Allocate a new list node with \p data as content and prepend it to + * the list \p list. \p list can be NULL. On failure returns NULL and sets + * \p error. + */ +MonoMList* +mono_mlist_prepend_checked (MonoMList* list, MonoObject *data, MonoError *error) +{ + error_init (error); + MonoMList* res = mono_mlist_alloc_checked (data, error); + return_val_if_nok (error, NULL); + + if (list) + MONO_OBJECT_SETREF (res, next, list); + return res; +} + +/** + * mono_mlist_append: + * \param list the managed list + * \param data the object to add to the list + * Allocate a new list node with \p data as content and append it + * to the list \p list. \p list can be NULL. + * Since managed lists are singly-linked, this operation takes O(n) time. + */ +MonoMList* +mono_mlist_append (MonoMList* list, MonoObject *data) +{ + MonoError error; + MonoMList *result = mono_mlist_append_checked (list, data, &error); + mono_error_cleanup (&error); + return result; +} + +/** + * mono_mlist_append_checked: + * \param list the managed list + * \param data the object to add to the list + * \param error set on error + * Allocate a new list node with \p data as content and append it + * to the list \p list. \p list can be NULL. + * Since managed lists are singly-linked, this operation takes O(n) time. + * On failure returns NULL and sets \p error. + */ +MonoMList* +mono_mlist_append_checked (MonoMList* list, MonoObject *data, MonoError *error) +{ + error_init (error); + MonoMList* res = mono_mlist_alloc_checked (data, error); + return_val_if_nok (error, NULL); + + if (list) { + MonoMList* last = mono_mlist_last (list); + MONO_OBJECT_SETREF (last, next, res); + return list; + } else { + return res; + } +} + +static MonoMList* +find_prev (MonoMList* list, MonoMList *item) +{ + MonoMList* prev = NULL; + while (list) { + if (list == item) + break; + prev = list; + list = list->next; + } + return prev; +} + +/** + * mono_mlist_remove_item: + * \param list the managed list + * \param data the object to remove from the list + * Remove the list node \p item from the managed list \p list. + * Since managed lists are singly-linked, this operation can take O(n) time. + */ +MonoMList* +mono_mlist_remove_item (MonoMList* list, MonoMList *item) +{ + MonoMList* prev; + if (list == item) { + list = item->next; + item->next = NULL; + return list; + } + prev = find_prev (list, item); + if (prev) { + MONO_OBJECT_SETREF (prev, next, item->next); + item->next = NULL; + return list; + } else { + /* not found */ + return list; + } +} + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/mono-mlist.h b/unity-2019.4.24f1-mbe/mono/metadata/mono-mlist.h new file mode 100644 index 000000000..cfeeba39a --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/mono-mlist.h @@ -0,0 +1,34 @@ +/** + * \file + */ + +#ifndef __MONO_METADATA_MONO_MLIST_H__ +#define __MONO_METADATA_MONO_MLIST_H__ + +/* + * mono-mlist.h: Managed object list implementation + */ + +#include + +typedef struct _MonoMList MonoMList; +MONO_RT_EXTERNAL_ONLY +MONO_API MonoMList* mono_mlist_alloc (MonoObject *data); +MONO_API MonoObject* mono_mlist_get_data (MonoMList* list); +MONO_API void mono_mlist_set_data (MonoMList* list, MonoObject *data); +MONO_API MonoMList* mono_mlist_set_next (MonoMList* list, MonoMList *next); +MONO_API int mono_mlist_length (MonoMList* list); +MONO_API MonoMList* mono_mlist_next (MonoMList* list); +MONO_API MonoMList* mono_mlist_last (MonoMList* list); +MONO_RT_EXTERNAL_ONLY +MONO_API MonoMList* mono_mlist_prepend (MonoMList* list, MonoObject *data); +MONO_RT_EXTERNAL_ONLY +MONO_API MonoMList* mono_mlist_append (MonoMList* list, MonoObject *data); + +MonoMList* mono_mlist_prepend_checked (MonoMList* list, MonoObject *data, MonoError *error); +MonoMList* mono_mlist_append_checked (MonoMList* list, MonoObject *data, MonoError *error); + +MONO_API MonoMList* mono_mlist_remove_item (MonoMList* list, MonoMList *item); + +#endif /* __MONO_METADATA_MONO_MLIST_H__ */ + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/mono-perfcounters-def.h b/unity-2019.4.24f1-mbe/mono/metadata/mono-perfcounters-def.h new file mode 100644 index 000000000..3a4dbc891 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/mono-perfcounters-def.h @@ -0,0 +1,136 @@ +/** + * \file + * Define the system and runtime performance counters. + * Each category is defined with the macro: + * PERFCTR_CAT(catid, name, help, type, instances, first_counter_id) + * and after that follows the counters inside the category, defined by the macro: + * PERFCTR_COUNTER(counter_id, name, help, type, field) + * field is the field inside MonoPerfCounters per predefined counters. + * Note we set it to unused for unrelated counters: it is unused + * in those cases. + */ +PERFCTR_CAT(CPU, "Processor", "", MultiInstance, CPU, CPU_USER_TIME) +PERFCTR_COUNTER(CPU_USER_TIME, "% User Time", "", Timer100Ns, unused) +PERFCTR_COUNTER(CPU_PRIV_TIME, "% Privileged Time", "", Timer100Ns, unused) +PERFCTR_COUNTER(CPU_INTR_TIME, "% Interrupt Time", "", Timer100Ns, unused) +PERFCTR_COUNTER(CPU_DCP_TIME, "% DCP Time", "", Timer100Ns, unused) +PERFCTR_COUNTER(CPU_PROC_TIME, "% Processor Time", "", Timer100NsInverse, unused) + +PERFCTR_CAT(PROC, "Process", "", MultiInstance, Process, PROC_USER_TIME) +PERFCTR_COUNTER(PROC_USER_TIME, "% User Time", "", Timer100Ns, unused) +PERFCTR_COUNTER(PROC_PRIV_TIME, "% Privileged Time", "", Timer100Ns, unused) +PERFCTR_COUNTER(PROC_PROC_TIME, "% Processor Time", "", Timer100Ns, unused) +PERFCTR_COUNTER(PROC_THREADS, "Thread Count", "", NumberOfItems64, unused) +PERFCTR_COUNTER(PROC_VBYTES, "Virtual Bytes", "", NumberOfItems64, unused) +PERFCTR_COUNTER(PROC_WSET, "Working Set", "", NumberOfItems64, unused) +PERFCTR_COUNTER(PROC_PBYTES, "Private Bytes", "", NumberOfItems64, unused) + +/* sample runtime counter */ +PERFCTR_CAT(MONO_MEM, "Mono Memory", "", SingleInstance, Mono, MEM_NUM_OBJECTS) +PERFCTR_COUNTER(MEM_NUM_OBJECTS, "Allocated Objects", "", NumberOfItems64, unused) +PERFCTR_COUNTER(MEM_PHYS_TOTAL, "Total Physical Memory", "Physical memory installed in the machine, in bytes", NumberOfItems64, unused) +PERFCTR_COUNTER(MEM_PHYS_AVAILABLE, "Available Physical Memory", "Physical memory available in the machine, in bytes", NumberOfItems64, unused) + +PERFCTR_CAT(ASPNET, "ASP.NET", "", MultiInstance, Mono, ASPNET_REQ_Q) +PERFCTR_COUNTER(ASPNET_REQ_Q, "Requests Queued", "", NumberOfItems64, aspnet_requests_queued) +PERFCTR_COUNTER(ASPNET_REQ_TOTAL, "Requests Total", "", NumberOfItems32, aspnet_requests) +PERFCTR_COUNTER(ASPNET_REQ_PSEC, "Requests/Sec", "", RateOfCountsPerSecond32, aspnet_requests) + +PERFCTR_CAT(JIT, ".NET CLR JIT", "", MultiInstance, Mono, JIT_BYTES) +PERFCTR_COUNTER(JIT_BYTES, "# of IL Bytes JITted", "", NumberOfItems32, jit_bytes) +PERFCTR_COUNTER(JIT_METHODS, "# of IL Methods JITted", "", NumberOfItems32, jit_methods) +PERFCTR_COUNTER(JIT_TIME, "% Time in JIT", "", RawFraction, jit_time) +PERFCTR_COUNTER(JIT_BYTES_PSEC, "IL Bytes Jitted/Sec", "", RateOfCountsPerSecond32, jit_bytes) +PERFCTR_COUNTER(JIT_FAILURES, "Standard Jit Failures", "", NumberOfItems32, jit_failures) + +PERFCTR_CAT(EXC, ".NET CLR Exceptions", "", MultiInstance, Mono, EXC_THROWN) +PERFCTR_COUNTER(EXC_THROWN, "# of Exceps Thrown", "", NumberOfItems32, exceptions_thrown) +PERFCTR_COUNTER(EXC_THROWN_PSEC, "# of Exceps Thrown/Sec", "", RateOfCountsPerSecond32, exceptions_thrown) +PERFCTR_COUNTER(EXC_FILTERS_PSEC, "# of Filters/Sec", "", RateOfCountsPerSecond32, exceptions_filters) +PERFCTR_COUNTER(EXC_FINALLYS_PSEC, "# of Finallys/Sec", "", RateOfCountsPerSecond32, exceptions_finallys) +PERFCTR_COUNTER(EXC_CATCH_DEPTH, "Throw to Catch Depth/Sec", "", NumberOfItems32, exceptions_depth) + +PERFCTR_CAT(GC, ".NET CLR Memory", "", MultiInstance, Mono, GC_GEN0) +PERFCTR_COUNTER(GC_GEN0, "# Gen 0 Collections", "", NumberOfItems32, gc_collections0) +PERFCTR_COUNTER(GC_GEN1, "# Gen 1 Collections", "", NumberOfItems32, gc_collections1) +PERFCTR_COUNTER(GC_GEN2, "# Gen 2 Collections", "", NumberOfItems32, gc_collections2) +PERFCTR_COUNTER(GC_PROM0, "Promoted Memory from Gen 0", "", NumberOfItems32, gc_promotions0) +PERFCTR_COUNTER(GC_PROM1, "Promoted Memory from Gen 1", "", NumberOfItems32, gc_promotions1) +PERFCTR_COUNTER(GC_PROM0SEC, "Gen 0 Promoted Bytes/Sec", "", RateOfCountsPerSecond32, gc_promotions0) +PERFCTR_COUNTER(GC_PROM1SEC, "Gen 1 Promoted Bytes/Sec", "", RateOfCountsPerSecond32, gc_promotions1) +PERFCTR_COUNTER(GC_PROMFIN, "Promoted Finalization-Memory from Gen 0", "", NumberOfItems32, gc_promotion_finalizers) +PERFCTR_COUNTER(GC_GEN0SIZE, "Gen 0 heap size", "", NumberOfItems64, gc_gen0size) +PERFCTR_COUNTER(GC_GEN1SIZE, "Gen 1 heap size", "", NumberOfItems64, gc_gen1size) +PERFCTR_COUNTER(GC_GEN2SIZE, "Gen 2 heap size", "", NumberOfItems64, gc_gen2size) +PERFCTR_COUNTER(GC_LOSIZE, "Large Object Heap size", "", NumberOfItems32, gc_lossize) +PERFCTR_COUNTER(GC_FINSURV, "Finalization Survivors", "", NumberOfItems32, gc_fin_survivors) +PERFCTR_COUNTER(GC_NHANDLES, "# GC Handles", "", NumberOfItems32, gc_num_handles) +PERFCTR_COUNTER(GC_BYTESSEC, "Allocated Bytes/sec", "", RateOfCountsPerSecond32, gc_allocated) +PERFCTR_COUNTER(GC_INDGC, "# Induced GC", "", NumberOfItems32, gc_induced) +PERFCTR_COUNTER(GC_PERCTIME, "% Time in GC", "", RawFraction, gc_time) +PERFCTR_COUNTER(GC_BYTES, "# Bytes in all Heaps", "", NumberOfItems64, gc_total_bytes) +PERFCTR_COUNTER(GC_COMMBYTES, "# Total committed Bytes", "", NumberOfItems64, gc_committed_bytes) +PERFCTR_COUNTER(GC_RESBYTES, "# Total reserved Bytes", "", NumberOfItems64, gc_reserved_bytes) +PERFCTR_COUNTER(GC_PINNED, "# of Pinned Objects", "", NumberOfItems32, gc_num_pinned) +PERFCTR_COUNTER(GC_SYNKB, "# of Sink Blocks in use", "", NumberOfItems32, gc_sync_blocks) + +PERFCTR_CAT(REMOTING, ".NET CLR Remoting", "", MultiInstance, Mono, REMOTING_CALLSEC) +PERFCTR_COUNTER(REMOTING_CALLSEC, "Remote Calls/sec", "", RateOfCountsPerSecond32, remoting_calls) +PERFCTR_COUNTER(REMOTING_CALLS, "Total Remote Calls", "", NumberOfItems32, remoting_calls) +PERFCTR_COUNTER(REMOTING_CHANNELS, "Channels", "", NumberOfItems32, remoting_channels) +PERFCTR_COUNTER(REMOTING_CPROXIES, "Context Proxies", "", NumberOfItems32, remoting_proxies) +PERFCTR_COUNTER(REMOTING_CBLOADED, "Context-Bound Classes Loaded", "", NumberOfItems32, remoting_classes) +PERFCTR_COUNTER(REMOTING_CBALLOCSEC, "Context-Bound Objects Alloc / sec", "", RateOfCountsPerSecond32, remoting_objects) +PERFCTR_COUNTER(REMOTING_CONTEXTS, "Contexts", "", NumberOfItems32, remoting_contexts) + +PERFCTR_CAT(LOADING, ".NET CLR Loading", "", MultiInstance, Mono, LOADING_CLASSES) +PERFCTR_COUNTER(LOADING_CLASSES, "Current Classes Loaded", "", NumberOfItems32, loader_classes) +PERFCTR_COUNTER(LOADING_TOTCLASSES, "Total Classes Loaded", "", NumberOfItems32, loader_total_classes) +PERFCTR_COUNTER(LOADING_CLASSESSEC, "Rate of Classes Loaded", "", RateOfCountsPerSecond32, loader_total_classes) +PERFCTR_COUNTER(LOADING_APPDOMAINS, "Current appdomains", "", NumberOfItems32, loader_appdomains) +PERFCTR_COUNTER(LOADING_TOTAPPDOMAINS, "Total Appdomains", "", NumberOfItems32, loader_total_appdomains) +PERFCTR_COUNTER(LOADING_APPDOMAINSEC, "Rate of appdomains", "", RateOfCountsPerSecond32, loader_total_appdomains) +PERFCTR_COUNTER(LOADING_ASSEMBLIES, "Current Assemblies", "", NumberOfItems32, loader_assemblies) +PERFCTR_COUNTER(LOADING_TOTASSEMBLIES, "Total Assemblies", "", NumberOfItems32, loader_total_assemblies) +PERFCTR_COUNTER(LOADING_ASSEMBLIESEC, "Rate of Assemblies", "", RateOfCountsPerSecond32, loader_total_assemblies) +PERFCTR_COUNTER(LOADING_FAILURES, "Total # of Load Failures", "", NumberOfItems32, loader_failures) +PERFCTR_COUNTER(LOADING_FAILURESSEC, "Rate of Load Failures", "", RateOfCountsPerSecond32, loader_failures) +PERFCTR_COUNTER(LOADING_BYTES, "Bytes in Loader Heap", "", NumberOfItems32, loader_bytes) +PERFCTR_COUNTER(LOADING_APPUNLOADED, "Total appdomains unloaded", "", NumberOfItems32, loader_appdomains_uloaded) +PERFCTR_COUNTER(LOADING_APPUNLOADEDSEC, "Rate of appdomains unloaded", "", RateOfCountsPerSecond32, loader_appdomains_uloaded) + +PERFCTR_CAT(THREAD, ".NET CLR LocksAndThreads", "", MultiInstance, Mono, THREAD_CONTENTIONS) +PERFCTR_COUNTER(THREAD_CONTENTIONS, "Total # of Contentions", "", NumberOfItems32, thread_contentions) +PERFCTR_COUNTER(THREAD_CONTENTIONSSEC, "Contention Rate / sec", "", RateOfCountsPerSecond32, thread_contentions) +PERFCTR_COUNTER(THREAD_QUEUELEN, "Current Queue Length", "", NumberOfItems32, thread_queue_len) +PERFCTR_COUNTER(THREAD_QUEUELENP, "Queue Length Peak", "", NumberOfItems32, thread_queue_max) +PERFCTR_COUNTER(THREAD_QUEUELENSEC, "Queue Length / sec", "", RateOfCountsPerSecond32, thread_queue_max) +PERFCTR_COUNTER(THREAD_NUMLOG, "# of current logical Threads", "", NumberOfItems32, thread_num_logical) +PERFCTR_COUNTER(THREAD_NUMPHYS, "# of current physical Threads", "", NumberOfItems32, thread_num_physical) +PERFCTR_COUNTER(THREAD_NUMREC, "# of current recognized threads", "", NumberOfItems32, thread_cur_recognized) +PERFCTR_COUNTER(THREAD_TOTREC, "# of total recognized threads", "", NumberOfItems32, thread_num_recognized) +PERFCTR_COUNTER(THREAD_TOTRECSEC, "rate of recognized threads / sec", "", RateOfCountsPerSecond32, thread_num_recognized) + +PERFCTR_CAT(INTEROP, ".NET CLR Interop", "", MultiInstance, Mono, INTEROP_NUMCCW) +PERFCTR_COUNTER(INTEROP_NUMCCW, "# of CCWs", "", NumberOfItems32, interop_num_ccw) +PERFCTR_COUNTER(INTEROP_STUBS, "# of Stubs", "", NumberOfItems32, interop_num_stubs) +PERFCTR_COUNTER(INTEROP_MARSH, "# of marshalling", "", NumberOfItems32, interop_num_marshals) + +PERFCTR_CAT(SECURITY, ".NET CLR Security", "", MultiInstance, Mono, SECURITY_CHECKS) +PERFCTR_COUNTER(SECURITY_CHECKS, "Total Runtime Checks", "", NumberOfItems32, security_num_checks) +PERFCTR_COUNTER(SECURITY_LCHECKS, "# Link Time Checks", "", NumberOfItems32, security_num_link_checks) +PERFCTR_COUNTER(SECURITY_PERCTIME, "% Time in RT checks", "", RawFraction, security_time) +PERFCTR_COUNTER(SECURITY_SWDEPTH, "Stack Walk Depth", "", NumberOfItems32, security_depth) + +PERFCTR_CAT(THREADPOOL, "Mono Threadpool", "", MultiInstance, Mono, THREADPOOL_WORKITEMS) +PERFCTR_COUNTER(THREADPOOL_WORKITEMS, "Work Items Added", "", NumberOfItems64, threadpool_workitems) +PERFCTR_COUNTER(THREADPOOL_WORKITEMS_PSEC, "Work Items Added/Sec", "", RateOfCountsPerSecond32, threadpool_workitems) +PERFCTR_COUNTER(THREADPOOL_IOWORKITEMS, "IO Work Items Added", "", NumberOfItems64, threadpool_ioworkitems) +PERFCTR_COUNTER(THREADPOOL_IOWORKITEMS_PSEC, "IO Work Items Added/Sec", "", RateOfCountsPerSecond32, threadpool_ioworkitems) +PERFCTR_COUNTER(THREADPOOL_THREADS, "# of Threads", "", NumberOfItems32, threadpool_threads) +PERFCTR_COUNTER(THREADPOOL_IOTHREADS, "# of IO Threads", "", NumberOfItems32, threadpool_iothreads) + +PERFCTR_CAT(NETWORK, "Network Interface", "", MultiInstance, NetworkInterface, NETWORK_BYTESRECSEC) +PERFCTR_COUNTER(NETWORK_BYTESRECSEC, "Bytes Received/sec", "", RateOfCountsPerSecond64, unused) +PERFCTR_COUNTER(NETWORK_BYTESSENTSEC, "Bytes Sent/sec", "", RateOfCountsPerSecond64, unused) +PERFCTR_COUNTER(NETWORK_BYTESTOTALSEC, "Bytes Total/sec", "", RateOfCountsPerSecond64, unused) diff --git a/unity-2019.4.24f1-mbe/mono/metadata/mono-perfcounters.c b/unity-2019.4.24f1-mbe/mono/metadata/mono-perfcounters.c new file mode 100644 index 000000000..378750c0c --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/mono-perfcounters.c @@ -0,0 +1,2014 @@ +/** + * \file + * + * Performance counters support. + * + * Author: Paolo Molaro (lupus@ximian.com) + * + * Copyright 2008-2009 Novell, Inc (http://www.novell.com) + * 2011 Xamarin, Inc + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include "config.h" +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#if defined (__OpenBSD__) +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_TIME_H +#include +#endif +#if defined (__APPLE__) +#include +#include +#include +#include +#endif +#if defined (__NetBSD__) +#include +#include +#include +#endif +#include "metadata/mono-perfcounters.h" +#include "metadata/appdomain.h" +#include "metadata/object-internals.h" +/* for mono_stats */ +#include "metadata/class-internals.h" +#include "utils/mono-time.h" +#include "utils/mono-mmap.h" +#include "utils/mono-proclib.h" +#include "utils/mono-networkinterfaces.h" +#include "utils/mono-error-internals.h" +#include "utils/atomic.h" +#include "utils/unlocked.h" + +/* map of CounterSample.cs */ +struct _MonoCounterSample { + gint64 rawValue; + gint64 baseValue; + gint64 counterFrequency; + gint64 systemFrequency; + gint64 timeStamp; + gint64 timeStamp100nSec; + gint64 counterTimeStamp; + int counterType; +}; + +#ifndef DISABLE_PERFCOUNTERS +/* map of PerformanceCounterType.cs */ +enum { + NumberOfItemsHEX32=0x00000000, + NumberOfItemsHEX64=0x00000100, + NumberOfItems32=0x00010000, + NumberOfItems64=0x00010100, + CounterDelta32=0x00400400, + CounterDelta64=0x00400500, + SampleCounter=0x00410400, + CountPerTimeInterval32=0x00450400, + CountPerTimeInterval64=0x00450500, + RateOfCountsPerSecond32=0x10410400, + RateOfCountsPerSecond64=0x10410500, + RawFraction=0x20020400, + CounterTimer=0x20410500, + Timer100Ns=0x20510500, + SampleFraction=0x20C20400, + CounterTimerInverse=0x21410500, + Timer100NsInverse=0x21510500, + CounterMultiTimer=0x22410500, + CounterMultiTimer100Ns=0x22510500, + CounterMultiTimerInverse=0x23410500, + CounterMultiTimer100NsInverse=0x23510500, + AverageTimer32=0x30020400, + ElapsedTime=0x30240500, + AverageCount64=0x40020500, + SampleBase=0x40030401, + AverageBase=0x40030402, + RawBase=0x40030403, + CounterMultiBase=0x42030500 +}; + +/* maps a small integer type to the counter types above */ +static const int +simple_type_to_type [] = { + NumberOfItemsHEX32, NumberOfItemsHEX64, + NumberOfItems32, NumberOfItems64, + CounterDelta32, CounterDelta64, + SampleCounter, CountPerTimeInterval32, + CountPerTimeInterval64, RateOfCountsPerSecond32, + RateOfCountsPerSecond64, RawFraction, + CounterTimer, Timer100Ns, + SampleFraction, CounterTimerInverse, + Timer100NsInverse, CounterMultiTimer, + CounterMultiTimer100Ns, CounterMultiTimerInverse, + CounterMultiTimer100NsInverse, AverageTimer32, + ElapsedTime, AverageCount64, + SampleBase, AverageBase, + RawBase, CounterMultiBase +}; + +enum { + SingleInstance, + MultiInstance, + CatTypeUnknown = -1 +}; + +enum { + ProcessInstance, + ThreadInstance, + CPUInstance, + MonoInstance, + NetworkInterfaceInstance, + CustomInstance +}; + +#define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_ ## id, +#define PERFCTR_COUNTER(id,name,help,type,field) +enum { +#include "mono-perfcounters-def.h" + NUM_CATEGORIES +}; + +#undef PERFCTR_CAT +#undef PERFCTR_COUNTER +#define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_START_ ## id = -1, +#define PERFCTR_COUNTER(id,name,help,type,field) COUNTER_ ## id, +/* each counter is assigned an id starting from 0 inside the category */ +enum { +#include "mono-perfcounters-def.h" + END_COUNTERS +}; + +#undef PERFCTR_CAT +#undef PERFCTR_COUNTER +#define PERFCTR_CAT(id,name,help,type,inst,first_counter) +#define PERFCTR_COUNTER(id,name,help,type,field) CCOUNTER_ ## id, +/* this is used just to count the number of counters */ +enum { +#include "mono-perfcounters-def.h" + NUM_COUNTERS +}; + +static mono_mutex_t perfctr_mutex; +#define perfctr_lock() mono_os_mutex_lock (&perfctr_mutex) +#define perfctr_unlock() mono_os_mutex_unlock (&perfctr_mutex) + +typedef struct { + char reserved [16]; + int size; + unsigned short counters_start; + unsigned short counters_size; + unsigned short data_start; + MonoPerfCounters counters; + char data [1]; +} MonoSharedArea; + +/* + binary format of custom counters in shared memory, starting from MonoSharedArea* + data_start; + basic stanza: + struct stanza_header { + byte stanza_type; // FTYPE_* + byte other_info; + ushort stanza_length; // includeas header + ... data ... + } + +// strings are utf8 +// perfcat and perfinstance are 4-bytes aligned +struct perfcat { + byte typeidx; + byte categorytype; + ushort length; // includes the counters + ushort num_counters; + ushort counters_data_size; + int num_instances; + char name[]; // null terminated + char help[]; // null terminated + // perfcounters follow + { + byte countertype; + char name[]; // null terminated + char help[]; // null terminated + } + 0-byte +}; + +struct perfinstance { + byte typeidx; + byte data_offset; // offset of counters from beginning of struct + ushort length; + uint category_offset; // offset of category in the shared area + char name[]; // null terminated + // data follows: this is always 8-byte aligned +}; + +*/ + +enum { + FTYPE_CATEGORY = 'C', + FTYPE_DELETED = 'D', + FTYPE_PREDEF_INSTANCE = 'P', // an instance of a predef counter + FTYPE_INSTANCE = 'I', + FTYPE_DIRTY = 'd', + FTYPE_END = 0 +}; + +typedef struct { + unsigned char ftype; + unsigned char extra; + unsigned short size; +} SharedHeader; + +typedef struct { + SharedHeader header; + unsigned short num_counters; + unsigned short counters_data_size; + int num_instances; + /* variable length data follows */ + char name [1]; + // string name + // string help + // SharedCounter counters_info [num_counters] +} SharedCategory; + +typedef struct { + SharedHeader header; + unsigned int category_offset; + /* variable length data follows */ + char instance_name [1]; + // string name +} SharedInstance; + +typedef struct { + unsigned char type; + guint8 seq_num; + /* variable length data follows */ + char name [1]; + // string name + // string help +} SharedCounter; + +typedef struct { + const char *name; + const char *help; + unsigned char id; + signed int type : 2; + unsigned int instance_type : 6; + short first_counter; +} CategoryDesc; + +typedef struct { + const char *name; + const char *help; + short id; + unsigned short offset; // offset inside MonoPerfCounters + int type; +} CounterDesc; + +#undef PERFCTR_CAT +#undef PERFCTR_COUNTER +#define PERFCTR_CAT(id,name,help,type,inst,first_counter) {name, help, CATEGORY_ ## id, type, inst ## Instance, CCOUNTER_ ## first_counter}, +#define PERFCTR_COUNTER(id,name,help,type,field) +static const CategoryDesc +predef_categories [] = { +#include "mono-perfcounters-def.h" + {NULL, NULL, NUM_CATEGORIES, -1, 0, NUM_COUNTERS} +}; + +#undef PERFCTR_CAT +#undef PERFCTR_COUNTER +#define PERFCTR_CAT(id,name,help,type,inst,first_counter) +#define PERFCTR_COUNTER(id,name,help,type,field) {name, help, COUNTER_ ## id, G_STRUCT_OFFSET (MonoPerfCounters, field), type}, +static const CounterDesc +predef_counters [] = { +#include "mono-perfcounters-def.h" + {NULL, NULL, -1, 0, 0} +}; + +/* + * We have several different classes of counters: + * *) system counters + * *) runtime counters + * *) remote counters + * *) user-defined counters + * *) windows counters (the implementation on windows will use this) + * + * To easily handle the differences we create a vtable for each class that contains the + * function pointers with the actual implementation to access the counters. + */ +typedef struct _ImplVtable ImplVtable; + +typedef MonoBoolean (*SampleFunc) (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample* sample); +typedef gint64 (*UpdateFunc) (ImplVtable *vtable, MonoBoolean do_incr, gint64 value); +typedef void (*CleanupFunc) (ImplVtable *vtable); + +struct _ImplVtable { + void *arg; + SampleFunc sample; + UpdateFunc update; + CleanupFunc cleanup; +}; + +typedef struct { + int id; + char *name; +} NetworkVtableArg; + +typedef struct { + ImplVtable vtable; + MonoPerfCounters *counters; + int pid; +} PredefVtable; + +typedef struct { + ImplVtable vtable; + SharedInstance *instance_desc; + SharedCounter *counter_desc; +} CustomVTable; + +static ImplVtable* +create_vtable (void *arg, SampleFunc sample, UpdateFunc update) +{ + ImplVtable *vtable = g_new0 (ImplVtable, 1); + vtable->arg = arg; + vtable->sample = sample; + vtable->update = update; + return vtable; +} + +MonoPerfCounters *mono_perfcounters = NULL; +static MonoSharedArea *shared_area = NULL; + +typedef struct { + void *sarea; + int refcount; +} ExternalSArea; + +/* maps a pid to a ExternalSArea pointer */ +static GHashTable *pid_to_shared_area = NULL; + +static MonoSharedArea * +load_sarea_for_pid (int pid) +{ + ExternalSArea *data; + MonoSharedArea *area = NULL; + + perfctr_lock (); + if (pid_to_shared_area == NULL) + pid_to_shared_area = g_hash_table_new (NULL, NULL); + data = (ExternalSArea *)g_hash_table_lookup (pid_to_shared_area, GINT_TO_POINTER (pid)); + if (!data) { + area = (MonoSharedArea *)mono_shared_area_for_pid (GINT_TO_POINTER (pid)); + if (area) { + data = g_new (ExternalSArea, 1); + data->sarea = area; + data->refcount = 1; + g_hash_table_insert (pid_to_shared_area, GINT_TO_POINTER (pid), data); + } + } else { + area = (MonoSharedArea *)data->sarea; + data->refcount ++; + } + perfctr_unlock (); + return area; +} + +static void +unref_pid_unlocked (int pid) +{ + ExternalSArea *data; + data = (ExternalSArea *)g_hash_table_lookup (pid_to_shared_area, GINT_TO_POINTER (pid)); + if (data) { + data->refcount--; + if (!data->refcount) { + g_hash_table_remove (pid_to_shared_area, GINT_TO_POINTER (pid)); + mono_shared_area_unload (data->sarea); + g_free (data); + } + } +} + +static void +predef_cleanup (ImplVtable *vtable) +{ + PredefVtable *vt = (PredefVtable*)vtable; + /* ExternalSArea *data; */ + + perfctr_lock (); + if (!pid_to_shared_area) { + perfctr_unlock (); + return; + } + unref_pid_unlocked (vt->pid); + perfctr_unlock (); +} + +static guint64 +mono_determine_physical_ram_size (void) +{ +#if defined (TARGET_WIN32) + MEMORYSTATUSEX memstat; + + memstat.dwLength = sizeof (memstat); + GlobalMemoryStatusEx (&memstat); + return (guint64)memstat.ullTotalPhys; +#elif defined (__NetBSD__) || defined (__APPLE__) +#ifdef __NetBSD__ + unsigned long value; +#else + guint64 value; +#endif + int mib[2] = { + CTL_HW, +#ifdef __NetBSD__ + HW_PHYSMEM64 +#else + HW_MEMSIZE +#endif + }; + size_t size_sys = sizeof (value); + + sysctl (mib, 2, &value, &size_sys, NULL, 0); + if (value == 0) + return 134217728; + + return (guint64)value; +#elif defined (HAVE_SYSCONF) + guint64 page_size = 0, num_pages = 0; + + /* sysconf works on most *NIX operating systems, if your system doesn't have it or if it + * reports invalid values, please add your OS specific code below. */ +#ifdef _SC_PAGESIZE + page_size = (guint64)sysconf (_SC_PAGESIZE); +#endif + +#ifdef _SC_PHYS_PAGES + num_pages = (guint64)sysconf (_SC_PHYS_PAGES); +#endif + + if (!page_size || !num_pages) { + g_warning ("Your operating system's sysconf (3) function doesn't correctly report physical memory size!"); + return 134217728; + } + + return page_size * num_pages; +#else + return 134217728; +#endif +} + +static guint64 +mono_determine_physical_ram_available_size (void) +{ +#if defined (TARGET_WIN32) + MEMORYSTATUSEX memstat; + + memstat.dwLength = sizeof (memstat); + GlobalMemoryStatusEx (&memstat); + return (guint64)memstat.ullAvailPhys; + +#elif defined (__NetBSD__) + struct vmtotal vm_total; + guint64 page_size; + int mib[2]; + size_t len; + + mib[0] = CTL_VM; + mib[1] = VM_METER; + + len = sizeof (vm_total); + sysctl (mib, 2, &vm_total, &len, NULL, 0); + + mib[0] = CTL_HW; + mib[1] = HW_PAGESIZE; + + len = sizeof (page_size); + sysctl (mib, 2, &page_size, &len, NULL, 0); + + return ((guint64) vm_total.t_free * page_size) / 1024; +#elif defined (__APPLE__) + mach_msg_type_number_t count = HOST_VM_INFO_COUNT; + mach_port_t host = mach_host_self(); + vm_size_t page_size; + vm_statistics_data_t vmstat; + kern_return_t ret; + do { + ret = host_statistics(host, HOST_VM_INFO, (host_info_t)&vmstat, &count); + } while (ret == KERN_ABORTED); + + if (ret != KERN_SUCCESS) { + g_warning ("Mono was unable to retrieve memory usage!"); + return 0; + } + + host_page_size(host, &page_size); + return (guint64) vmstat.free_count * page_size; + +#elif defined (HAVE_SYSCONF) + guint64 page_size = 0, num_pages = 0; + + /* sysconf works on most *NIX operating systems, if your system doesn't have it or if it + * reports invalid values, please add your OS specific code below. */ +#ifdef _SC_PAGESIZE + page_size = (guint64)sysconf (_SC_PAGESIZE); +#endif + +#ifdef _SC_AVPHYS_PAGES + num_pages = (guint64)sysconf (_SC_AVPHYS_PAGES); +#endif + + if (!page_size || !num_pages) { + g_warning ("Your operating system's sysconf (3) function doesn't correctly report physical memory size!"); + return 0; + } + + return page_size * num_pages; +#else + return 0; +#endif +} + +void +mono_perfcounters_init (void) +{ + int d_offset = G_STRUCT_OFFSET (MonoSharedArea, data); + d_offset += 7; + d_offset &= ~7; + + mono_os_mutex_init_recursive (&perfctr_mutex); + + shared_area = (MonoSharedArea *)mono_shared_area (); + shared_area->counters_start = G_STRUCT_OFFSET (MonoSharedArea, counters); + shared_area->counters_size = sizeof (MonoPerfCounters); + shared_area->data_start = d_offset; + shared_area->size = 4096; + mono_perfcounters = &shared_area->counters; +} + +static int +perfctr_type_compress (int type) +{ + int i; + for (i = 0; i < G_N_ELEMENTS (simple_type_to_type); ++i) { + if (simple_type_to_type [i] == type) + return i; + } + /* NumberOfItems32 */ + return 2; +} + +static SharedHeader* +shared_data_reserve_room (int size, int ftype) +{ + SharedHeader* header; + unsigned char *p = (unsigned char *)shared_area + shared_area->data_start; + unsigned char *end = (unsigned char *)shared_area + shared_area->size; + + size += 7; + size &= ~7; + while (p < end) { + unsigned short *next; + if (*p == FTYPE_END) { + if (size < (end - p)) + goto res; + return NULL; + } + if (p + 4 > end) + return NULL; + next = (unsigned short*)(p + 2); + if (*p == FTYPE_DELETED) { + /* we reuse only if it's the same size */ + if (*next == size) { + goto res; + } + } + p += *next; + } + return NULL; + +res: + header = (SharedHeader*)p; + header->ftype = ftype; + header->extra = 0; /* data_offset could overflow here, so we leave this field unused */ + header->size = size; + + return header; +} + +typedef gboolean (*SharedFunc) (SharedHeader *header, void *data); + +static void +foreach_shared_item_in_area (unsigned char *p, unsigned char *end, SharedFunc func, void *data) +{ + while (p < end) { + unsigned short *next; + if (p + 4 > end) + return; + next = (unsigned short*)(p + 2); + if (!func ((SharedHeader*)p, data)) + return; + if (*p == FTYPE_END) + return; + p += *next; + } +} + +static void +foreach_shared_item (SharedFunc func, void *data) +{ + unsigned char *p = (unsigned char *)shared_area + shared_area->data_start; + unsigned char *end = (unsigned char *)shared_area + shared_area->size; + + foreach_shared_item_in_area (p, end, func, data); +} + +static int +mono_string_compare_ascii (MonoString *str, const char *ascii_str) +{ + /* FIXME: make this case insensitive */ + guint16 *strc = mono_string_chars (str); + while (*strc == *ascii_str++) { + if (*strc == 0) + return 0; + strc++; + } + return *strc - *(const unsigned char *)(ascii_str - 1); +} + +typedef struct { + MonoString *name; + SharedCategory *cat; +} CatSearch; + +static gboolean +category_search (SharedHeader *header, void *data) +{ + CatSearch *search = (CatSearch *)data; + if (header->ftype == FTYPE_CATEGORY) { + SharedCategory *cat = (SharedCategory*)header; + if (mono_string_compare_ascii (search->name, cat->name) == 0) { + search->cat = cat; + return FALSE; + } + } + return TRUE; +} + +static SharedCategory* +find_custom_category (MonoString *name) +{ + CatSearch search; + search.name = name; + search.cat = NULL; + foreach_shared_item (category_search, &search); + return search.cat; +} + +static gboolean +category_collect (SharedHeader *header, void *data) +{ + GSList **list = (GSList **)data; + if (header->ftype == FTYPE_CATEGORY) { + *list = g_slist_prepend (*list, header); + } + return TRUE; +} + +static GSList* +get_custom_categories (void) { + GSList *list = NULL; + foreach_shared_item (category_collect, &list); + return list; +} + +static char* +custom_category_counters (SharedCategory* cat) +{ + char *p = cat->name + strlen (cat->name) + 1; + p += strlen (p) + 1; /* skip category help */ + return p; +} + +static SharedCounter* +find_custom_counter (SharedCategory* cat, MonoString *name) +{ + int i; + char *p = custom_category_counters (cat); + for (i = 0; i < cat->num_counters; ++i) { + SharedCounter *counter = (SharedCounter*)p; + if (mono_string_compare_ascii (name, counter->name) == 0) + return counter; + p += 2; /* skip counter type */ + p += strlen (p) + 1; /* skip counter name */ + p += strlen (p) + 1; /* skip counter help */ + } + return NULL; +} + +typedef struct { + unsigned int cat_offset; + SharedCategory* cat; + char *name; + SharedInstance* result; + GSList *list; +} InstanceSearch; + +static gboolean +instance_search (SharedHeader *header, void *data) +{ + InstanceSearch *search = (InstanceSearch *)data; + if (header->ftype == FTYPE_INSTANCE) { + SharedInstance *ins = (SharedInstance*)header; + if (search->cat_offset == ins->category_offset) { + if (search->name) { + if (strcmp (search->name, ins->instance_name) == 0) { + search->result = ins; + return FALSE; + } + } else { + search->list = g_slist_prepend (search->list, ins); + } + } + } + return TRUE; +} + +static SharedInstance* +find_custom_instance (SharedCategory* cat, char *name) +{ + InstanceSearch search; + search.cat_offset = (char*)cat - (char*)shared_area; + search.cat = cat; + search.name = name; + search.list = NULL; + search.result = NULL; + foreach_shared_item (instance_search, &search); + return search.result; +} + +static GSList* +get_custom_instances_list (SharedCategory* cat) +{ + InstanceSearch search; + search.cat_offset = (char*)cat - (char*)shared_area; + search.cat = cat; + search.name = NULL; + search.list = NULL; + search.result = NULL; + foreach_shared_item (instance_search, &search); + return search.list; +} + +static char* +custom_category_help (SharedCategory* cat) +{ + return cat->name + strlen (cat->name) + 1; +} + +static const CounterDesc* +get_counter_in_category (const CategoryDesc *desc, MonoString *counter) +{ + const CounterDesc *cdesc = &predef_counters [desc->first_counter]; + const CounterDesc *end = &predef_counters [desc [1].first_counter]; + for (; cdesc < end; ++cdesc) { + if (mono_string_compare_ascii (counter, cdesc->name) == 0) + return cdesc; + } + return NULL; +} + +/* fill the info in sample (except the raw value) */ +static void +fill_sample (MonoCounterSample *sample) +{ + sample->timeStamp = mono_100ns_ticks (); + sample->timeStamp100nSec = sample->timeStamp; + sample->counterTimeStamp = sample->timeStamp; + sample->counterFrequency = 10000000; + sample->systemFrequency = 10000000; + // the real basevalue needs to be get from a different counter... + sample->baseValue = 0; +} + +static int +id_from_string (const gchar *id_str, gboolean is_process) +{ + int id = -1; + if (strcmp("", id_str) != 0) { + char *end; + id = strtol (id_str, &end, 0); + if (end == id_str && !is_process) + id = -1; + } + return id; +} + +static MonoBoolean +get_cpu_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample) +{ + MonoProcessError error; + int id = GPOINTER_TO_INT (vtable->arg); + int pid = id >> 5; + id &= 0x1f; + if (!only_value) { + fill_sample (sample); + sample->baseValue = 1; + } + sample->counterType = predef_counters [predef_categories [CATEGORY_CPU].first_counter + id].type; + switch (id) { + case COUNTER_CPU_USER_TIME: + sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_USER_TIME, &error); + return TRUE; + case COUNTER_CPU_PRIV_TIME: + sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_PRIV_TIME, &error); + return TRUE; + case COUNTER_CPU_INTR_TIME: + sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_INTR_TIME, &error); + return TRUE; + case COUNTER_CPU_DCP_TIME: + sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_DCP_TIME, &error); + return TRUE; + case COUNTER_CPU_PROC_TIME: + sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_IDLE_TIME, &error); + return TRUE; + } + return FALSE; +} + +static void* +cpu_get_impl (MonoString* counter, const gchar* instance, int *type, MonoBoolean *custom) +{ + int id = id_from_string (instance, FALSE) << 5; + const CounterDesc *cdesc; + *custom = FALSE; + /* increase the shift above and the mask also in the implementation functions */ + //g_assert (32 > desc [1].first_counter - desc->first_counter); + if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_CPU], counter))) { + *type = cdesc->type; + return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_cpu_counter, NULL); + } + return NULL; +} + +static MonoBoolean +get_network_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample) +{ + MonoNetworkError error = MONO_NETWORK_ERROR_OTHER; + NetworkVtableArg *narg = (NetworkVtableArg*) vtable->arg; + if (!only_value) { + fill_sample (sample); + } + + sample->counterType = predef_counters [predef_categories [CATEGORY_NETWORK].first_counter + narg->id].type; + switch (narg->id) { + case COUNTER_NETWORK_BYTESRECSEC: + sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESREC, &error); + break; + case COUNTER_NETWORK_BYTESSENTSEC: + sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESSENT, &error); + break; + case COUNTER_NETWORK_BYTESTOTALSEC: + sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESTOTAL, &error); + break; + } + + if (error == MONO_NETWORK_ERROR_NONE) + return TRUE; + else + return FALSE; +} + +static void +network_cleanup (ImplVtable *vtable) +{ + NetworkVtableArg *narg; + + if (vtable == NULL) + return; + + narg = (NetworkVtableArg *)vtable->arg; + if (narg == NULL) + return; + + g_free (narg->name); + narg->name = NULL; + g_free (narg); + vtable->arg = NULL; +} + +static void* +network_get_impl (MonoString* counter, const gchar* instance, int *type, MonoBoolean *custom) +{ + const CounterDesc *cdesc; + NetworkVtableArg *narg; + ImplVtable *vtable; + char *instance_name; + + *custom = FALSE; + if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_NETWORK], counter))) { + instance_name = g_strdup (instance); + narg = g_new0 (NetworkVtableArg, 1); + narg->id = cdesc->id; + narg->name = instance_name; + *type = cdesc->type; + vtable = create_vtable (narg, get_network_counter, NULL); + vtable->cleanup = network_cleanup; + return vtable; + } + return NULL; +} + +static MonoBoolean +get_process_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample) +{ + int id = GPOINTER_TO_INT (vtable->arg); + int pid = id >> 5; + if (pid < 0) + return FALSE; + id &= 0x1f; + if (!only_value) { + fill_sample (sample); + sample->baseValue = 1; + } + sample->counterType = predef_counters [predef_categories [CATEGORY_PROC].first_counter + id].type; + switch (id) { + case COUNTER_PROC_USER_TIME: + sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_USER_TIME); + return TRUE; + case COUNTER_PROC_PRIV_TIME: + sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_SYSTEM_TIME); + return TRUE; + case COUNTER_PROC_PROC_TIME: + sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_TOTAL_TIME); + return TRUE; + case COUNTER_PROC_THREADS: + sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_NUM_THREADS); + return TRUE; + case COUNTER_PROC_VBYTES: + sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_VIRTUAL_BYTES); + return TRUE; + case COUNTER_PROC_WSET: + sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_WORKING_SET); + return TRUE; + case COUNTER_PROC_PBYTES: + sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_PRIVATE_BYTES); + return TRUE; + } + return FALSE; +} + +static void* +process_get_impl (MonoString* counter, const gchar* instance, int *type, MonoBoolean *custom) +{ + int id = id_from_string (instance, TRUE) << 5; + const CounterDesc *cdesc; + *custom = FALSE; + /* increase the shift above and the mask also in the implementation functions */ + //g_assert (32 > desc [1].first_counter - desc->first_counter); + if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_PROC], counter))) { + *type = cdesc->type; + return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_process_counter, NULL); + } + return NULL; +} + +static MonoBoolean +mono_mem_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample) +{ + int id = GPOINTER_TO_INT (vtable->arg); + if (!only_value) { + fill_sample (sample); + sample->baseValue = 1; + } + sample->counterType = predef_counters [predef_categories [CATEGORY_MONO_MEM].first_counter + id].type; + switch (id) { + case COUNTER_MEM_NUM_OBJECTS: + sample->rawValue = 0; + return TRUE; + case COUNTER_MEM_PHYS_TOTAL: + sample->rawValue = mono_determine_physical_ram_size ();; + return TRUE; + case COUNTER_MEM_PHYS_AVAILABLE: + sample->rawValue = mono_determine_physical_ram_available_size ();; + return TRUE; + } + return FALSE; +} + +static void* +mono_mem_get_impl (MonoString* counter, const gchar* instance, int *type, MonoBoolean *custom) +{ + const CounterDesc *cdesc; + *custom = FALSE; + if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_MONO_MEM], counter))) { + *type = cdesc->type; + return create_vtable (GINT_TO_POINTER ((gint) cdesc->id), mono_mem_counter, NULL); + } + return NULL; +} + +static MonoBoolean +predef_readonly_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample) +{ + PredefVtable *vt = (PredefVtable *)vtable; + const CounterDesc *desc; + int cat_id = GPOINTER_TO_INT (vtable->arg); + int id = cat_id >> 16; + cat_id &= 0xffff; + if (!only_value) { + fill_sample (sample); + sample->baseValue = 1; + } + desc = &predef_counters [predef_categories [cat_id].first_counter + id]; + sample->counterType = desc->type; + /* FIXME: check that the offset fits inside imported counters */ + /*g_print ("loading %s at %d\n", desc->name, desc->offset);*/ + sample->rawValue = *(guint32*)((char*)vt->counters + desc->offset); + return TRUE; +} + +static ImplVtable* +predef_vtable (void *arg, const gchar *pids) +{ + MonoSharedArea *area; + PredefVtable *vtable; + int pid; + + pid = atoi (pids); + area = load_sarea_for_pid (pid); + if (!area) + return NULL; + + vtable = g_new (PredefVtable, 1); + vtable->vtable.arg = arg; + vtable->vtable.sample = predef_readonly_counter; + vtable->vtable.cleanup = predef_cleanup; + vtable->counters = (MonoPerfCounters*)((char*)area + area->counters_start); + vtable->pid = pid; + + return (ImplVtable*)vtable; +} + +/* consider storing the pointer directly in vtable->arg, so the runtime overhead is lower: + * this needs some way to set sample->counterType as well, though. + */ +static MonoBoolean +predef_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample) +{ + int cat_id = GPOINTER_TO_INT (vtable->arg); + int id = cat_id >> 16; + cat_id &= 0xffff; + if (!only_value) { + fill_sample (sample); + sample->baseValue = 1; + } + sample->counterType = predef_counters [predef_categories [cat_id].first_counter + id].type; + switch (cat_id) { + case CATEGORY_EXC: + switch (id) { + case COUNTER_EXC_THROWN: + sample->rawValue = mono_atomic_load_i32 (&mono_perfcounters->exceptions_thrown); + return TRUE; + } + break; + case CATEGORY_ASPNET: + switch (id) { + case COUNTER_ASPNET_REQ_Q: + sample->rawValue = mono_atomic_load_i32 (&mono_perfcounters->aspnet_requests_queued); + return TRUE; + case COUNTER_ASPNET_REQ_TOTAL: + sample->rawValue = mono_atomic_load_i32 (&mono_perfcounters->aspnet_requests); + return TRUE; + } + break; + case CATEGORY_THREADPOOL: + switch (id) { + case COUNTER_THREADPOOL_WORKITEMS: + sample->rawValue = mono_atomic_load_i64 (&mono_perfcounters->threadpool_workitems); + return TRUE; + case COUNTER_THREADPOOL_IOWORKITEMS: + sample->rawValue = mono_atomic_load_i64 (&mono_perfcounters->threadpool_ioworkitems); + return TRUE; + case COUNTER_THREADPOOL_THREADS: + sample->rawValue = mono_atomic_load_i32 (&mono_perfcounters->threadpool_threads); + return TRUE; + case COUNTER_THREADPOOL_IOTHREADS: + sample->rawValue = mono_atomic_load_i32 (&mono_perfcounters->threadpool_iothreads); + return TRUE; + } + break; + case CATEGORY_JIT: + switch (id) { + case COUNTER_JIT_BYTES: + sample->rawValue = mono_atomic_load_i32 (&mono_perfcounters->jit_bytes); + return TRUE; + case COUNTER_JIT_METHODS: + sample->rawValue = mono_atomic_load_i32 (&mono_perfcounters->jit_methods); + return TRUE; + case COUNTER_JIT_TIME: + sample->rawValue = mono_atomic_load_i32 (&mono_perfcounters->jit_time); + return TRUE; + case COUNTER_JIT_BYTES_PSEC: + sample->rawValue = mono_atomic_load_i32 (&mono_perfcounters->jit_bytes); + return TRUE; + case COUNTER_JIT_FAILURES: + sample->rawValue = mono_atomic_load_i32 (&mono_perfcounters->jit_failures); + return TRUE; + } + break; + } + return FALSE; +} + +static gint64 +predef_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value) +{ + gint32 *volatile ptr = NULL; + gint64 *volatile ptr64 = NULL; + int cat_id = GPOINTER_TO_INT (vtable->arg); + int id = cat_id >> 16; + cat_id &= 0xffff; + switch (cat_id) { + case CATEGORY_ASPNET: + switch (id) { + case COUNTER_ASPNET_REQ_Q: ptr = &mono_perfcounters->aspnet_requests_queued; break; + case COUNTER_ASPNET_REQ_TOTAL: ptr = &mono_perfcounters->aspnet_requests; break; + } + break; + case CATEGORY_THREADPOOL: + switch (id) { + case COUNTER_THREADPOOL_WORKITEMS: ptr64 = &mono_perfcounters->threadpool_workitems; break; + case COUNTER_THREADPOOL_IOWORKITEMS: ptr64 = &mono_perfcounters->threadpool_ioworkitems; break; + case COUNTER_THREADPOOL_THREADS: ptr = &mono_perfcounters->threadpool_threads; break; + case COUNTER_THREADPOOL_IOTHREADS: ptr = &mono_perfcounters->threadpool_iothreads; break; + } + break; + } + if (ptr) { + if (do_incr) { + if (value == 1) + return mono_atomic_inc_i32 (ptr); + if (value == -1) + return mono_atomic_dec_i32 (ptr); + + return mono_atomic_add_i32 (ptr, (gint32)value); + } + /* this can be non-atomic */ + *ptr = value; + return value; + } else if (ptr64) { + if (do_incr) { + if (value == 1) + return UnlockedIncrement64 (ptr64); /* FIXME: use mono_atomic_inc_i64 () */ + if (value == -1) + return UnlockedDecrement64 (ptr64); /* FIXME: use mono_atomic_dec_i64 () */ + + return UnlockedAdd64 (ptr64, value); /* FIXME: use mono_atomic_add_i64 () */ + } + /* this can be non-atomic */ + *ptr64 = value; + return value; + } + return 0; +} + +static void* +predef_writable_get_impl (int cat, MonoString* counter, const gchar *instance, int *type, MonoBoolean *custom) +{ + const CounterDesc *cdesc; + *custom = TRUE; + if ((cdesc = get_counter_in_category (&predef_categories [cat], counter))) { + *type = cdesc->type; + if (instance == NULL || strcmp (instance, "") == 0) + return create_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), predef_writable_counter, predef_writable_update); + else + return predef_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), instance); + } + return NULL; +} + +static MonoBoolean +custom_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample) +{ + CustomVTable *counter_data = (CustomVTable *)vtable; + if (!only_value) { + fill_sample (sample); + sample->baseValue = 1; + } + sample->counterType = simple_type_to_type [counter_data->counter_desc->type]; + if (!vtable->arg) + sample->rawValue = 0; + else + sample->rawValue = *(guint64*)vtable->arg; + return TRUE; +} + +static gint64 +custom_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value) +{ + /* FIXME: check writability */ + guint64 *ptr = (guint64 *)vtable->arg; + if (ptr) { + if (do_incr) { + /* FIXME: we need to do this atomically */ + *ptr += value; + return *ptr; + } + /* this can be non-atomic */ + *ptr = value; + return value; + } + return 0; +} + +static SharedInstance* +custom_get_instance (SharedCategory *cat, SharedCounter *scounter, char* name) +{ + SharedInstance* inst; + char *p; + int size; + inst = find_custom_instance (cat, name); + if (inst) + return inst; + size = sizeof (SharedInstance) + strlen (name); + size += 7; + size &= ~7; + size += (sizeof (guint64) * cat->num_counters); + perfctr_lock (); + inst = (SharedInstance*) shared_data_reserve_room (size, FTYPE_INSTANCE); + if (!inst) { + perfctr_unlock (); + g_free (name); + return NULL; + } + inst->category_offset = (char*)cat - (char*)shared_area; + cat->num_instances++; + /* now copy the variable data */ + p = inst->instance_name; + strcpy (p, name); + p += strlen (name) + 1; + perfctr_unlock (); + + return inst; +} + +static ImplVtable* +custom_vtable (SharedCounter *scounter, SharedInstance* inst, char *data) +{ + CustomVTable* vtable; + vtable = g_new0 (CustomVTable, 1); + vtable->vtable.arg = data; + vtable->vtable.sample = custom_writable_counter; + vtable->vtable.update = custom_writable_update; + vtable->instance_desc = inst; + vtable->counter_desc = scounter; + + return (ImplVtable*)vtable; +} + +static gpointer +custom_get_value_address (SharedCounter *scounter, SharedInstance* sinst) +{ + int offset = sizeof (SharedInstance) + strlen (sinst->instance_name); + offset += 7; + offset &= ~7; + offset += scounter->seq_num * sizeof (guint64); + return (char*)sinst + offset; +} + +static void* +custom_get_impl (SharedCategory *cat, MonoString *counter, MonoString* instance, int *type, MonoError *error) +{ + SharedCounter *scounter; + SharedInstance* inst; + char *name; + + error_init (error); + scounter = find_custom_counter (cat, counter); + if (!scounter) + return NULL; + name = mono_string_to_utf8_checked (counter, error); + return_val_if_nok (error, NULL); + *type = simple_type_to_type [scounter->type]; + inst = custom_get_instance (cat, scounter, name); + g_free (name); + if (!inst) + return NULL; + return custom_vtable (scounter, inst, (char *)custom_get_value_address (scounter, inst)); +} + +static const CategoryDesc* +find_category (MonoString *category) +{ + int i; + for (i = 0; i < NUM_CATEGORIES; ++i) { + if (mono_string_compare_ascii (category, predef_categories [i].name) == 0) + return &predef_categories [i]; + } + return NULL; +} + +void* +mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance, + MonoString* machine, int *type, MonoBoolean *custom) +{ + MonoError error; + const CategoryDesc *cdesc; + void *result = NULL; + /* no support for counters on other machines */ + if (mono_string_compare_ascii (machine, ".")) + return NULL; + cdesc = find_category (category); + if (!cdesc) { + SharedCategory *scat = find_custom_category (category); + if (!scat) + return NULL; + *custom = TRUE; + result = custom_get_impl (scat, counter, instance, type, &error); + if (mono_error_set_pending_exception (&error)) + return NULL; + return result; + } + gchar *c_instance = mono_string_to_utf8_checked (instance, &error); + if (mono_error_set_pending_exception (&error)) + return NULL; + switch (cdesc->id) { + case CATEGORY_CPU: + result = cpu_get_impl (counter, c_instance, type, custom); + break; + case CATEGORY_PROC: + result = process_get_impl (counter, c_instance, type, custom); + break; + case CATEGORY_MONO_MEM: + result = mono_mem_get_impl (counter, c_instance, type, custom); + break; + case CATEGORY_NETWORK: + result = network_get_impl (counter, c_instance, type, custom); + break; + case CATEGORY_JIT: + case CATEGORY_EXC: + case CATEGORY_GC: + case CATEGORY_REMOTING: + case CATEGORY_LOADING: + case CATEGORY_THREAD: + case CATEGORY_INTEROP: + case CATEGORY_SECURITY: + case CATEGORY_ASPNET: + case CATEGORY_THREADPOOL: + result = predef_writable_get_impl (cdesc->id, counter, c_instance, type, custom); + break; + } + g_free (c_instance); + return result; +} + +MonoBoolean +mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample) +{ + ImplVtable *vtable = (ImplVtable *)impl; + if (vtable && vtable->sample) + return vtable->sample (vtable, only_value, sample); + return FALSE; +} + +gint64 +mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value) +{ + ImplVtable *vtable = (ImplVtable *)impl; + if (vtable && vtable->update) + return vtable->update (vtable, do_incr, value); + return 0; +} + +void +mono_perfcounter_free_data (void *impl) +{ + ImplVtable *vtable = (ImplVtable *)impl; + if (vtable && vtable->cleanup) + vtable->cleanup (vtable); + g_free (impl); +} + +/* Category icalls */ +MonoBoolean +mono_perfcounter_category_del (MonoString *name) +{ + const CategoryDesc *cdesc; + SharedCategory *cat; + cdesc = find_category (name); + /* can't delete a predefined category */ + if (cdesc) + return FALSE; + perfctr_lock (); + cat = find_custom_category (name); + /* FIXME: check the semantics, if deleting a category means also deleting the instances */ + if (!cat || cat->num_instances) { + perfctr_unlock (); + return FALSE; + } + cat->header.ftype = FTYPE_DELETED; + perfctr_unlock (); + return TRUE; +} + +/* this is an icall */ +MonoString* +mono_perfcounter_category_help (MonoString *category, MonoString *machine) +{ + MonoError error; + MonoString *result = NULL; + const CategoryDesc *cdesc; + error_init (&error); + /* no support for counters on other machines */ + if (mono_string_compare_ascii (machine, ".")) + return NULL; + cdesc = find_category (category); + if (!cdesc) { + SharedCategory *scat = find_custom_category (category); + if (!scat) + return NULL; + result = mono_string_new_checked (mono_domain_get (), custom_category_help (scat), &error); + if (mono_error_set_pending_exception (&error)) + return NULL; + return result; + } + result = mono_string_new_checked (mono_domain_get (), cdesc->help, &error); + if (mono_error_set_pending_exception (&error)) + return NULL; + return result; +} + +/* + * Check if the category named @category exists on @machine. If @counter is not NULL, return + * TRUE only if a counter with that name exists in the category. + */ +MonoBoolean +mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine) +{ + const CategoryDesc *cdesc; + /* no support for counters on other machines */ + if (mono_string_compare_ascii (machine, ".")) + return FALSE; + cdesc = find_category (category); + if (!cdesc) { + SharedCategory *scat = find_custom_category (category); + if (!scat) + return FALSE; + /* counter is allowed to be null */ + if (!counter) + return TRUE; + /* search through the custom category */ + return find_custom_counter (scat, counter) != NULL; + } + /* counter is allowed to be null */ + if (!counter) + return TRUE; + if (get_counter_in_category (cdesc, counter)) + return TRUE; + return FALSE; +} + +/* C map of the type with the same name */ +typedef struct { + MonoObject object; + MonoString *help; + MonoString *name; + int type; +} CounterCreationData; + +/* + * Since we'll keep a copy of the category per-process, we should also make sure + * categories with the same name are compatible. + */ +MonoBoolean +mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items) +{ + MonoError error; + int result = FALSE; + int i, size; + int num_counters = mono_array_length (items); + int counters_data_size; + char *name = NULL; + char *chelp = NULL; + char **counter_info = NULL; + char *p; + SharedCategory *cat; + + /* FIXME: ensure there isn't a category created already */ + name = mono_string_to_utf8_checked (category, &error); + if (!mono_error_ok (&error)) + goto failure; + chelp = mono_string_to_utf8_checked (help, &error); + if (!mono_error_ok (&error)) + goto failure; + counter_info = g_new0 (char*, num_counters * 2); + /* calculate the size we need structure size + name/help + 2 0 string terminators */ + size = G_STRUCT_OFFSET (SharedCategory, name) + strlen (name) + strlen (chelp) + 2; + for (i = 0; i < num_counters; ++i) { + CounterCreationData *data = mono_array_get (items, CounterCreationData*, i); + counter_info [i * 2] = mono_string_to_utf8_checked (data->name, &error); + if (!mono_error_ok (&error)) + goto failure; + counter_info [i * 2 + 1] = mono_string_to_utf8_checked (data->help, &error); + if (!mono_error_ok (&error)) + goto failure; + size += sizeof (SharedCounter) + 1; /* 1 is for the help 0 terminator */ + } + for (i = 0; i < num_counters * 2; ++i) { + if (!counter_info [i]) + goto failure; + size += strlen (counter_info [i]) + 1; + } + size += 7; + size &= ~7; + counters_data_size = num_counters * 8; /* optimize for size later */ + if (size > 65535) + goto failure; + perfctr_lock (); + cat = (SharedCategory*) shared_data_reserve_room (size, FTYPE_CATEGORY); + if (!cat) { + perfctr_unlock (); + goto failure; + } + cat->num_counters = num_counters; + cat->counters_data_size = counters_data_size; + /* now copy the vaiable data */ + p = cat->name; + strcpy (p, name); + p += strlen (name) + 1; + strcpy (p, chelp); + p += strlen (chelp) + 1; + for (i = 0; i < num_counters; ++i) { + CounterCreationData *data = mono_array_get (items, CounterCreationData*, i); + /* emit the SharedCounter structures */ + *p++ = perfctr_type_compress (data->type); + *p++ = i; + strcpy (p, counter_info [i * 2]); + p += strlen (counter_info [i * 2]) + 1; + strcpy (p, counter_info [i * 2 + 1]); + p += strlen (counter_info [i * 2 + 1]) + 1; + } + + perfctr_unlock (); + result = TRUE; +failure: + if (counter_info) { + for (i = 0; i < num_counters * 2; ++i) { + g_free (counter_info [i]); + } + g_free (counter_info); + } + g_free (name); + g_free (chelp); + mono_error_cleanup (&error); + return result; +} + +int +mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine) +{ + MonoError error; + const CategoryDesc *cdesc; + SharedInstance *sinst; + char *name; + /* no support for counters on other machines */ + /*FIXME: machine appears to be wrong + if (mono_string_compare_ascii (machine, ".")) + return FALSE;*/ + cdesc = find_category (category); + if (!cdesc) { + SharedCategory *scat; + scat = find_custom_category (category); + if (!scat) + return FALSE; + name = mono_string_to_utf8_checked (instance, &error); + if (mono_error_set_pending_exception (&error)) + return FALSE; + sinst = find_custom_instance (scat, name); + g_free (name); + if (sinst) + return TRUE; + } else { + /* FIXME: search instance */ + } + return FALSE; +} + +/* this is an icall */ +MonoArray* +mono_perfcounter_category_names (MonoString *machine) +{ + MonoError error; + int i; + MonoArray *res; + MonoDomain *domain = mono_domain_get (); + GSList *custom_categories, *tmp; + /* no support for counters on other machines */ + if (mono_string_compare_ascii (machine, ".")) { + res = mono_array_new_checked (domain, mono_get_string_class (), 0, &error); + mono_error_set_pending_exception (&error); + return res; + } + perfctr_lock (); + custom_categories = get_custom_categories (); + res = mono_array_new_checked (domain, mono_get_string_class (), NUM_CATEGORIES + g_slist_length (custom_categories), &error); + if (mono_error_set_pending_exception (&error)) { + perfctr_unlock (); + return NULL; + } + + for (i = 0; i < NUM_CATEGORIES; ++i) { + const CategoryDesc *cdesc = &predef_categories [i]; + MonoString *name = mono_string_new_checked (domain, cdesc->name, &error); + goto_if_nok (&error, leave); + mono_array_setref (res, i, name); + } + for (tmp = custom_categories; tmp; tmp = tmp->next) { + SharedCategory *scat = (SharedCategory *)tmp->data; + MonoString *name = mono_string_new_checked (domain, scat->name, &error); + goto_if_nok (&error, leave); + mono_array_setref (res, i, name); + i++; + } +leave: + perfctr_unlock (); + g_slist_free (custom_categories); + mono_error_set_pending_exception (&error); + return res; +} + +MonoArray* +mono_perfcounter_counter_names (MonoString *category, MonoString *machine) +{ + MonoError error; + int i; + SharedCategory *scat; + const CategoryDesc *cdesc; + MonoArray *res; + MonoDomain *domain = mono_domain_get (); + /* no support for counters on other machines */ + if (mono_string_compare_ascii (machine, ".")) { + res = mono_array_new_checked (domain, mono_get_string_class (), 0, &error); + mono_error_set_pending_exception (&error); + return res; + } + cdesc = find_category (category); + if (cdesc) { + res = mono_array_new_checked (domain, mono_get_string_class (), cdesc [1].first_counter - cdesc->first_counter, &error); + if (mono_error_set_pending_exception (&error)) + return NULL; + for (i = cdesc->first_counter; i < cdesc [1].first_counter; ++i) { + const CounterDesc *desc = &predef_counters [i]; + MonoString *name = mono_string_new_checked (domain, desc->name, &error); + if (mono_error_set_pending_exception (&error)) + return NULL; + mono_array_setref (res, i - cdesc->first_counter, name); + } + return res; + } + perfctr_lock (); + scat = find_custom_category (category); + if (scat) { + char *p = custom_category_counters (scat); + int i; + res = mono_array_new_checked (domain, mono_get_string_class (), scat->num_counters, &error); + if (mono_error_set_pending_exception (&error)) { + perfctr_unlock (); + return NULL; + } + + for (i = 0; i < scat->num_counters; ++i) { + MonoString *str = mono_string_new_checked (domain, p + 1, &error); + goto_if_nok (&error, leave); + mono_array_setref (res, i, str); + p += 2; /* skip counter type */ + p += strlen (p) + 1; /* skip counter name */ + p += strlen (p) + 1; /* skip counter help */ + } + } else + res = mono_array_new_checked (domain, mono_get_string_class (), 0, &error); +leave: + perfctr_unlock (); + mono_error_set_pending_exception (&error); + return res; +} + +static MonoArray* +get_string_array (void **array, int count, gboolean is_process, MonoError *error) +{ + int i; + MonoDomain *domain = mono_domain_get (); + error_init (error); + MonoArray * res = mono_array_new_checked (mono_domain_get (), mono_get_string_class (), count, error); + return_val_if_nok (error, NULL); + for (i = 0; i < count; ++i) { + char buf [128]; + char *p; + if (is_process) { + char *pname = mono_process_get_name (array [i], buf, sizeof (buf)); + p = g_strdup_printf ("%d/%s", GPOINTER_TO_INT (array [i]), pname); + } else { + sprintf (buf, "%d", GPOINTER_TO_INT (array [i])); + p = buf; + } + MonoString *str = mono_string_new_checked (domain, p, error); + if (p != buf) + g_free (p); + return_val_if_nok (error, NULL); + mono_array_setref (res, i, str); + } + return res; +} + +static MonoArray* +get_string_array_of_strings (void **array, int count, MonoError *error) +{ + int i; + MonoDomain *domain = mono_domain_get (); + error_init (error); + MonoArray * res = mono_array_new_checked (mono_domain_get (), mono_get_string_class (), count, error); + return_val_if_nok (error, NULL); + for (i = 0; i < count; ++i) { + char* p = (char *)array[i]; + MonoString *str = mono_string_new_checked (domain, p, error); + return_val_if_nok (error, NULL); + mono_array_setref (res, i, str); + } + + return res; +} + +static MonoArray* +get_mono_instances (MonoError *error) +{ + int count = 64; + int res; + void **buf = NULL; + MonoArray *array; + error_init (error); + do { + count *= 2; + g_free (buf); + buf = g_new (void*, count); + res = mono_shared_area_instances (buf, count); + } while (res == count); + array = get_string_array (buf, res, TRUE, error); + g_free (buf); + return array; +} + +static MonoArray* +get_cpu_instances (MonoError *error) +{ + void **buf = NULL; + int i, count; + MonoArray *array; + error_init (error); + count = mono_cpu_count () + 1; /* +1 for "_Total" */ + buf = g_new (void*, count); + for (i = 0; i < count; ++i) + buf [i] = GINT_TO_POINTER (i - 1); /* -1 => _Total */ + array = get_string_array (buf, count, FALSE, error); + g_free (buf); + MonoString *total = mono_string_new_checked (mono_domain_get (), "_Total", error); + return_val_if_nok (error, NULL); + mono_array_setref (array, 0, total); + return array; +} + +static MonoArray* +get_processes_instances (MonoError *error) +{ + MonoArray *array; + int count = 0; + void **buf = mono_process_list (&count); + error_init (error); + if (!buf) + return get_string_array (NULL, 0, FALSE, error); + array = get_string_array (buf, count, TRUE, error); + g_free (buf); + return array; +} + +static MonoArray* +get_networkinterface_instances (MonoError *error) +{ + MonoArray *array; + int count = 0; + error_init (error); + void **buf = mono_networkinterface_list (&count); + if (!buf) + return get_string_array_of_strings (NULL, 0, error); + array = get_string_array_of_strings (buf, count, error); + g_strfreev ((char **) buf); + return array; +} + +static MonoArray* +get_custom_instances (MonoString *category, MonoError *error) +{ + SharedCategory *scat; + error_init (error); + scat = find_custom_category (category); + if (scat) { + GSList *list = get_custom_instances_list (scat); + GSList *tmp; + int i = 0; + MonoArray *array = mono_array_new_checked (mono_domain_get (), mono_get_string_class (), g_slist_length (list), error); + if (!is_ok (error)) { + g_slist_free (list); + return NULL; + } + for (tmp = list; tmp; tmp = tmp->next) { + SharedInstance *inst = (SharedInstance *)tmp->data; + MonoString *str = mono_string_new_checked (mono_domain_get (), inst->instance_name, error); + if (!is_ok (error)) { + g_slist_free (list); + return NULL; + } + mono_array_setref (array, i, str); + i++; + } + g_slist_free (list); + return array; + } + return mono_array_new_checked (mono_domain_get (), mono_get_string_class (), 0, error); +} + +MonoArray* +mono_perfcounter_instance_names (MonoString *category, MonoString *machine) +{ + MonoError error; + const CategoryDesc* cat; + MonoArray *result = NULL; + if (mono_string_compare_ascii (machine, ".")) { + result = mono_array_new_checked (mono_domain_get (), mono_get_string_class (), 0, &error); + mono_error_set_pending_exception (&error); + return result; + } + + cat = find_category (category); + if (!cat) { + MonoArray *result = get_custom_instances (category, &error); + mono_error_set_pending_exception (&error); + return result; + } + switch (cat->instance_type) { + case MonoInstance: + result = get_mono_instances (&error); + break; + case CPUInstance: + result = get_cpu_instances (&error); + break; + case ProcessInstance: + result = get_processes_instances (&error); + break; + case NetworkInterfaceInstance: + result = get_networkinterface_instances (&error); + break; + case ThreadInstance: + default: + result = mono_array_new_checked (mono_domain_get (), mono_get_string_class (), 0, &error); + } + mono_error_set_pending_exception (&error); + return result; +} + +typedef struct { + PerfCounterEnumCallback cb; + void *data; +} PerfCounterForeachData; + +static gboolean +mono_perfcounter_foreach_shared_item (SharedHeader *header, gpointer data) +{ + int i; + char *p, *name; + unsigned char type; + void *addr; + SharedCategory *cat; + SharedCounter *counter; + SharedInstance *inst; + PerfCounterForeachData *foreach_data = (PerfCounterForeachData *)data; + + if (header->ftype == FTYPE_CATEGORY) { + cat = (SharedCategory*)header; + + p = cat->name; + p += strlen (p) + 1; /* skip category name */ + p += strlen (p) + 1; /* skip category help */ + + for (i = 0; i < cat->num_counters; ++i) { + counter = (SharedCounter*) p; + type = (unsigned char)*p++; + /* seq_num = (int)* */ p++; + name = p; + p += strlen (p) + 1; + /* help = p; */ + p += strlen (p) + 1; + + inst = custom_get_instance (cat, counter, name); + if (!inst) + return FALSE; + addr = custom_get_value_address (counter, inst); + if (!foreach_data->cb (cat->name, name, type, addr ? *(gint64*)addr : 0, foreach_data->data)) + return FALSE; + } + } + + return TRUE; +} + +void +mono_perfcounter_foreach (PerfCounterEnumCallback cb, gpointer data) +{ + PerfCounterForeachData foreach_data = { cb, data }; + + perfctr_lock (); + + foreach_shared_item (mono_perfcounter_foreach_shared_item, &foreach_data); + + perfctr_unlock (); +} + +#else +void* +mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance, MonoString* machine, int *type, MonoBoolean *custom) +{ + g_assert_not_reached (); +} + +MonoBoolean +mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample) +{ + g_assert_not_reached (); +} + +gint64 +mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value) +{ + g_assert_not_reached (); +} + +void +mono_perfcounter_free_data (void *impl) +{ + g_assert_not_reached (); +} + +/* Category icalls */ +MonoBoolean +mono_perfcounter_category_del (MonoString *name) +{ + g_assert_not_reached (); +} + +MonoString* +mono_perfcounter_category_help (MonoString *category, MonoString *machine) +{ + g_assert_not_reached (); +} + +MonoBoolean +mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine) +{ + g_assert_not_reached (); +} + +MonoBoolean +mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items) +{ + g_assert_not_reached (); +} + +int +mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine) +{ + g_assert_not_reached (); +} + +MonoArray* +mono_perfcounter_category_names (MonoString *machine) +{ + g_assert_not_reached (); +} + +MonoArray* +mono_perfcounter_counter_names (MonoString *category, MonoString *machine) +{ + g_assert_not_reached (); +} + +MonoArray* +mono_perfcounter_instance_names (MonoString *category, MonoString *machine) +{ + g_assert_not_reached (); +} +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/mono-perfcounters.h b/unity-2019.4.24f1-mbe/mono/metadata/mono-perfcounters.h new file mode 100644 index 000000000..e7c702f44 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/mono-perfcounters.h @@ -0,0 +1,36 @@ +/** + * \file + */ + +#ifndef __MONO_PERFCOUNTERS_H__ +#define __MONO_PERFCOUNTERS_H__ + +#include +#include +#include + +typedef struct _MonoCounterSample MonoCounterSample; + +void* mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance, + MonoString* machine, int *type, MonoBoolean *custom); + +MonoBoolean mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample); + +gint64 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value); +void mono_perfcounter_free_data (void *impl); + +/* Category icalls */ +MonoBoolean mono_perfcounter_category_del (MonoString *name); +MonoString* mono_perfcounter_category_help (MonoString *category, MonoString *machine); +MonoBoolean mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine); +MonoBoolean mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items); +int mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine); +MonoArray* mono_perfcounter_category_names (MonoString *machine); +MonoArray* mono_perfcounter_counter_names (MonoString *category, MonoString *machine); +MonoArray* mono_perfcounter_instance_names (MonoString *category, MonoString *machine); + +typedef gboolean (*PerfCounterEnumCallback) (char *category_name, char *name, unsigned char type, gint64 value, gpointer user_data); +MONO_API void mono_perfcounter_foreach (PerfCounterEnumCallback cb, gpointer user_data); + +#endif /* __MONO_PERFCOUNTERS_H__ */ + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/mono-ptr-array.h b/unity-2019.4.24f1-mbe/mono/metadata/mono-ptr-array.h new file mode 100644 index 000000000..70694eff8 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/mono-ptr-array.h @@ -0,0 +1,83 @@ +/** + * \file + * GC aware equivalente of g_ptr_array + * + * Author: + * Rodrigo Kumpera + * + * (C) 2010 Novell, Inc + */ + +#ifndef __MONO_PTR_ARRAY_H__ +#define __MONO_PTR_ARRAY_H__ + + +#include + +#include "mono/metadata/gc-internals.h" + +/* This is an implementation of a growable pointer array that avoids doing memory allocations for small sizes. + * It works by allocating an initial small array on stack and only going to gc tracked memory if needed. + * The array elements are assumed to be object references. + */ +typedef struct { + void **data; + int size; + int capacity; + MonoGCRootSource source; + void *key; + const char *msg; +} MonoPtrArray; + +#define MONO_PTR_ARRAY_MAX_ON_STACK (16) + +#define mono_ptr_array_init(ARRAY, INITIAL_SIZE, SOURCE, KEY, MSG) do {\ + (ARRAY).size = 0; \ + (ARRAY).capacity = MAX (INITIAL_SIZE, MONO_PTR_ARRAY_MAX_ON_STACK); \ + (ARRAY).source = SOURCE; \ + (ARRAY).key = KEY; \ + (ARRAY).msg = MSG; \ + (ARRAY).data = INITIAL_SIZE > MONO_PTR_ARRAY_MAX_ON_STACK \ + ? (void **)mono_gc_alloc_fixed (sizeof (void*) * INITIAL_SIZE, mono_gc_make_root_descr_all_refs (INITIAL_SIZE), SOURCE, NULL, MSG) \ + : g_newa (void*, MONO_PTR_ARRAY_MAX_ON_STACK); \ +} while (0) + +#define mono_ptr_array_destroy(ARRAY) do {\ + if ((ARRAY).capacity > MONO_PTR_ARRAY_MAX_ON_STACK) \ + mono_gc_free_fixed ((ARRAY).data); \ +} while (0) + +#define mono_ptr_array_append(ARRAY, VALUE) do { \ + if ((ARRAY).size >= (ARRAY).capacity) {\ + void **__tmp = (void **)mono_gc_alloc_fixed (sizeof (void*) * (ARRAY).capacity * 2, mono_gc_make_root_descr_all_refs ((ARRAY).capacity * 2), (ARRAY).source, (ARRAY).key, (ARRAY).msg); \ + mono_gc_memmove_aligned ((void *)__tmp, (ARRAY).data, (ARRAY).capacity * sizeof (void*)); \ + if ((ARRAY).capacity > MONO_PTR_ARRAY_MAX_ON_STACK) \ + mono_gc_free_fixed ((ARRAY).data); \ + (ARRAY).data = __tmp; \ + (ARRAY).capacity *= 2;\ + }\ + ((ARRAY).data [(ARRAY).size++] = VALUE); \ +} while (0) + +#define mono_ptr_array_sort(ARRAY, COMPARE_FUNC) do { \ + qsort ((ARRAY).data, (ARRAY).size, sizeof (gpointer), (COMPARE_FUNC)); \ +} while (0) + +#define mono_ptr_array_set(ARRAY, IDX, VALUE) do { \ + ((ARRAY).data [(IDX)] = VALUE); \ +} while (0) + +#define mono_ptr_array_get(ARRAY, IDX) ((ARRAY).data [(IDX)]) + +#define mono_ptr_array_size(ARRAY) ((ARRAY).size) + +#define mono_ptr_array_reset(ARRAY) do { \ + (ARRAY).size = 0; \ +} while (0) + +#define mono_ptr_array_clear(ARRAY) do { \ + (ARRAY).size = 0; \ + mono_gc_bzero_aligned ((ARRAY).data, (ARRAY).capacity * sizeof (void*)); \ +} while (0) + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/mono-route.c b/unity-2019.4.24f1-mbe/mono/metadata/mono-route.c new file mode 100644 index 000000000..44dfb52cd --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/mono-route.c @@ -0,0 +1,135 @@ +/** + * \file + * Read the network routing tables using sysctl(3) calls + * Required for Unix-like systems that don't have Linux's /proc/net/route + * + * Author: + * Ben Woods (woodsb02@gmail.com) + */ + +#include + +#if defined(HOST_DARWIN) || defined(HOST_BSD) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern MonoBoolean ves_icall_System_Net_NetworkInformation_MacOsIPInterfaceProperties_ParseRouteInfo_internal(MonoString *iface, MonoArray **gw_addr_list) +{ + MonoError error; + size_t needed; + in_addr_t in; + int mib[6]; + int num_gws=0, gwnum=0; + unsigned int ifindex = 0; + char *buf, *next, *lim, *ifacename; + struct rt_msghdr *rtm; + + MonoDomain *domain = mono_domain_get (); + + ifacename = mono_string_to_utf8_checked(iface, &error); + if (mono_error_set_pending_exception (&error)) + return FALSE; + + if ((ifindex = if_nametoindex(ifacename)) == 0) + return FALSE; + g_free(ifacename); + + // MIB array defining data to read from sysctl + mib[0] = CTL_NET; // Networking + mib[1] = PF_ROUTE; // Routing messages + mib[2] = 0; // Protocol number (always zero) + mib[3] = AF_INET; // Address family (IPv4) + mib[4] = NET_RT_DUMP; // Dump routing table + mib[5] = 0; // + + // First sysctl call with oldp set to NULL to determine size of available data + if (sysctl(mib, G_N_ELEMENTS(mib), NULL, &needed, NULL, 0) < 0) + return FALSE; + + // Allocate suffcient memory for available data based on the previous sysctl call + if ((buf = g_malloc (needed)) == NULL) + return FALSE; + + // Second sysctl call to retrieve data into appropriately sized buffer + if (sysctl(mib, G_N_ELEMENTS(mib), buf, &needed, NULL, 0) < 0) { + g_free (buf); + return FALSE; + } + + lim = buf + needed; + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + if (rtm->rtm_version != RTM_VERSION) + continue; + if (rtm->rtm_index != ifindex) + continue; + if((in = gateway_from_rtm(rtm)) == 0) + continue; + num_gws++; + } + + *gw_addr_list = mono_array_new_checked (domain, mono_get_string_class (), num_gws, &error); + goto_if_nok (&error, leave); + + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + if (rtm->rtm_version != RTM_VERSION) + continue; + if (rtm->rtm_index != ifindex) + continue; + if ((in = gateway_from_rtm(rtm)) == 0) + continue; + + MonoString *addr_string; + char addr [16], *ptr; + int len; + + ptr = (char *) ∈ + len = snprintf(addr, sizeof(addr), "%u.%u.%u.%u", + (unsigned char) ptr [0], + (unsigned char) ptr [1], + (unsigned char) ptr [2], + (unsigned char) ptr [3]); + + if ((len >= sizeof(addr)) || (len < 0)) + // snprintf output truncated + continue; + + addr_string = mono_string_new_checked (domain, addr, &error); + goto_if_nok (&error, leave); + mono_array_setref (*gw_addr_list, gwnum, addr_string); + gwnum++; + } +leave: + g_free (buf); + return is_ok (&error); +} + +in_addr_t gateway_from_rtm(struct rt_msghdr *rtm) +{ + struct sockaddr *gw; + unsigned int l; + + struct sockaddr *addr = (struct sockaddr *)(rtm + 1); + l = roundup(addr->sa_len, sizeof(long)); \ + gw = (struct sockaddr *)((char *) addr + l); \ + + if (rtm->rtm_addrs & RTA_GATEWAY) { + if(gw->sa_family == AF_INET) { + struct sockaddr_in *sockin = (struct sockaddr_in *)gw; + return(sockin->sin_addr.s_addr); + } + } + + return 0; +} + +#endif /* #if defined(HOST_DARWIN) || defined(HOST_BSD) */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/mono-route.h b/unity-2019.4.24f1-mbe/mono/metadata/mono-route.h new file mode 100644 index 000000000..03d179d71 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/mono-route.h @@ -0,0 +1,27 @@ +/** + * \file + */ + +#ifndef __MONO_ROUTE_H__ +#define __MONO_ROUTE_H__ + +#if defined(HOST_DARWIN) || defined(HOST_BSD) + +#include + +#if defined (HOST_IOS) || defined (HOST_WATCHOS) || defined (HOST_APPLETVOS) +// The iOS SDK does not provide the net/route.h header but using the Darwin version works fine. +#include "../../support/ios/net/route.h" +#else +#include +#endif + +#include + +in_addr_t gateway_from_rtm (struct rt_msghdr *rtm); + +/* Category icalls */ +extern MonoBoolean ves_icall_System_Net_NetworkInformation_MacOsIPInterfaceProperties_ParseRouteInfo_internal (MonoString *iface, MonoArray **gw_addr_list); + +#endif /* #if defined(HOST_DARWIN) || defined(HOST_BSD) */ +#endif /* __MONO_ROUTE_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/mono-security-windows-internals.h b/unity-2019.4.24f1-mbe/mono/metadata/mono-security-windows-internals.h new file mode 100644 index 000000000..dc76e58cd --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/mono-security-windows-internals.h @@ -0,0 +1,35 @@ +/** + * \file + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#ifndef __MONO_METADATA_MONO_SECURITY_WINDOWS_INTERNALS_H__ +#define __MONO_METADATA_MONO_SECURITY_WINDOWS_INTERNALS_H__ + +#include +#include + +#ifdef HOST_WIN32 +#include "mono/metadata/security.h" +#include "mono/metadata/object.h" +#include "mono/metadata/object-internals.h" +#include "mono/metadata/metadata.h" +#include "mono/metadata/metadata-internals.h" + +gint32 +mono_security_win_get_token_name (gpointer token, gunichar2 ** uniname); + +gboolean +mono_security_win_is_machine_protected (gunichar2 *path); + +gboolean +mono_security_win_is_user_protected (gunichar2 *path); + +gboolean +mono_security_win_protect_machine (gunichar2 *path); + +gboolean +mono_security_win_protect_user (gunichar2 *path); +#endif /* HOST_WIN32 */ + +#endif /* __MONO_METADATA_MONO_SECURITY_WINDOWS_INTERNALS_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/mono-security-windows-uwp.c b/unity-2019.4.24f1-mbe/mono/metadata/mono-security-windows-uwp.c new file mode 100644 index 000000000..72989994c --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/mono-security-windows-uwp.c @@ -0,0 +1,180 @@ +/** + * \file + * UWP security support for Mono. + * + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. +*/ +#include +#include +#include "mono/utils/mono-compiler.h" + +#if G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) +#include +#include "mono/metadata/mono-security-windows-internals.h" + +gpointer +mono_security_principal_windows_identity_get_current_token () +{ + g_unsupported_api ("OpenThreadToken, OpenProcessToken"); + + SetLastError (ERROR_NOT_SUPPORTED); + return NULL; +} + +gpointer +ves_icall_System_Security_Principal_WindowsIdentity_GetCurrentToken (MonoError *error) +{ + error_init (error); + + mono_error_set_not_supported (error, G_UNSUPPORTED_API, "OpenThreadToken, OpenProcessToken"); + return mono_security_principal_windows_identity_get_current_token (); +} + +MonoArray* +ves_icall_System_Security_Principal_WindowsIdentity_GetRoles (gpointer token) +{ + MonoError mono_error; + error_init (&mono_error); + + g_unsupported_api ("GetTokenInformation"); + + mono_error_set_not_supported (&mono_error, G_UNSUPPORTED_API, "GetTokenInformation"); + mono_error_set_pending_exception (&mono_error); + + SetLastError (ERROR_NOT_SUPPORTED); + + return NULL; +} + +gpointer +ves_icall_System_Security_Principal_WindowsImpersonationContext_DuplicateToken (gpointer token) +{ + MonoError mono_error; + error_init (&mono_error); + + g_unsupported_api ("DuplicateToken"); + + mono_error_set_not_supported (&mono_error, G_UNSUPPORTED_API, "DuplicateToken"); + mono_error_set_pending_exception (&mono_error); + + SetLastError (ERROR_NOT_SUPPORTED); + + return NULL; +} + +gboolean +ves_icall_System_Security_Principal_WindowsImpersonationContext_SetCurrentToken (gpointer token) +{ + MonoError mono_error; + error_init (&mono_error); + + g_unsupported_api ("ImpersonateLoggedOnUser"); + + mono_error_set_not_supported (&mono_error, G_UNSUPPORTED_API, "ImpersonateLoggedOnUser"); + mono_error_set_pending_exception (&mono_error); + + SetLastError (ERROR_NOT_SUPPORTED); + + return FALSE; +} + +gboolean +ves_icall_System_Security_Principal_WindowsImpersonationContext_RevertToSelf (void) +{ + MonoError mono_error; + error_init (&mono_error); + + g_unsupported_api ("RevertToSelf"); + + mono_error_set_not_supported(&mono_error, G_UNSUPPORTED_API, "RevertToSelf"); + mono_error_set_pending_exception (&mono_error); + + SetLastError (ERROR_NOT_SUPPORTED); + + return FALSE; +} + +gint32 +mono_security_win_get_token_name (gpointer token, gunichar2 ** uniname) +{ + MonoError mono_error; + error_init (&mono_error); + + g_unsupported_api ("GetTokenInformation"); + + mono_error_set_not_supported (&mono_error, G_UNSUPPORTED_API, "GetTokenInformation"); + mono_error_set_pending_exception (&mono_error); + + SetLastError (ERROR_NOT_SUPPORTED); + + return 0; +} + +gboolean +mono_security_win_is_machine_protected (gunichar2 *path) +{ + MonoError mono_error; + error_init (&mono_error); + + g_unsupported_api ("GetNamedSecurityInfo, LocalFree"); + + mono_error_set_not_supported (&mono_error, G_UNSUPPORTED_API, "GetNamedSecurityInfo, LocalFree"); + mono_error_set_pending_exception (&mono_error); + + SetLastError (ERROR_NOT_SUPPORTED); + + return FALSE; +} + +gboolean +mono_security_win_is_user_protected (gunichar2 *path) +{ + MonoError mono_error; + error_init (&mono_error); + + g_unsupported_api ("GetNamedSecurityInfo, LocalFree"); + + mono_error_set_not_supported (&mono_error, G_UNSUPPORTED_API, "GetNamedSecurityInfo, LocalFree"); + mono_error_set_pending_exception (&mono_error); + + SetLastError (ERROR_NOT_SUPPORTED); + + return FALSE; +} + +gboolean +mono_security_win_protect_machine (gunichar2 *path) +{ + MonoError mono_error; + error_init (&mono_error); + + g_unsupported_api ("BuildTrusteeWithSid, SetEntriesInAcl, SetNamedSecurityInfo, LocalFree, FreeSid"); + + mono_error_set_not_supported (&mono_error, G_UNSUPPORTED_API, "BuildTrusteeWithSid, SetEntriesInAcl, SetNamedSecurityInfo, LocalFree, FreeSid"); + mono_error_set_pending_exception (&mono_error); + + SetLastError (ERROR_NOT_SUPPORTED); + + return FALSE; +} + +gboolean +mono_security_win_protect_user (gunichar2 *path) +{ + MonoError mono_error; + error_init (&mono_error); + + g_unsupported_api ("BuildTrusteeWithSid, SetEntriesInAcl, SetNamedSecurityInfo, LocalFree"); + + mono_error_set_not_supported (&mono_error, G_UNSUPPORTED_API, "BuildTrusteeWithSid, SetEntriesInAcl, SetNamedSecurityInfo, LocalFree"); + mono_error_set_pending_exception (&mono_error); + + SetLastError (ERROR_NOT_SUPPORTED); + + return FALSE; +} +#else /* G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) */ + +MONO_EMPTY_SOURCE_FILE (mono_security_windows_uwp); +#endif /* G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/mono-security-windows.c b/unity-2019.4.24f1-mbe/mono/metadata/mono-security-windows.c new file mode 100644 index 000000000..bfdc96a9b --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/mono-security-windows.c @@ -0,0 +1,502 @@ +/** + * \file + * Windows security support. + * + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#include +#include + +#if defined(HOST_WIN32) +#include +#include +#include "mono/metadata/mono-security-windows-internals.h" + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +#include +#include +#endif + +#ifndef PROTECTED_DACL_SECURITY_INFORMATION +#define PROTECTED_DACL_SECURITY_INFORMATION 0x80000000L +#endif + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +static gunichar2* +GetSidName (gunichar2 *server, PSID sid, gint32 *size) +{ + gunichar2 *uniname = NULL; + DWORD cchName = 0; + DWORD cchDomain = 0; + SID_NAME_USE peUse; /* out */ + + LookupAccountSid (server, sid, NULL, &cchName, NULL, + &cchDomain, &peUse); + + if ((cchName > 0) && (cchDomain > 0)) { + gunichar2 *user = g_malloc0 ((cchName + 1) * 2); + gunichar2 *domain = g_malloc0 ((cchDomain + 1) * 2); + + LookupAccountSid (server, sid, user, &cchName, domain, + &cchDomain, &peUse); + + if (cchName > 0) { + if (cchDomain > 0) { + /* domain/machine name included (+ sepearator) */ + *size = cchName + cchDomain + 1; + uniname = g_malloc0 ((*size + 1) * 2); + memcpy (uniname, domain, cchDomain * 2); + *(uniname + cchDomain) = '\\'; + memcpy (uniname + cchDomain + 1, user, cchName * 2); + g_free (user); + } + else { + /* no domain / machine */ + *size = cchName; + uniname = user; + } + } + else { + /* nothing -> return NULL */ + g_free (user); + } + + g_free (domain); + } + + return uniname; +} + +gpointer +mono_security_principal_windows_identity_get_current_token (void) +{ + gpointer token = NULL; + + /* Note: This isn't a copy of the Token - we must not close it!!! + * http://www.develop.com/kbrown/book/html/whatis_windowsprincipal.html + */ + + /* thread may be impersonating somebody */ + if (OpenThreadToken (GetCurrentThread (), MAXIMUM_ALLOWED, 1, &token) == 0) { + /* if not take the process identity */ + OpenProcessToken (GetCurrentProcess (), MAXIMUM_ALLOWED, &token); + } + + return token; +} + +gpointer +ves_icall_System_Security_Principal_WindowsIdentity_GetCurrentToken (MonoError *error) +{ + error_init (error); + return mono_security_principal_windows_identity_get_current_token (); +} + +gint32 +mono_security_win_get_token_name (gpointer token, gunichar2 ** uniname) +{ + gint32 size = 0; + + GetTokenInformation (token, TokenUser, NULL, size, (PDWORD)&size); + if (size > 0) { + TOKEN_USER *tu = g_malloc0 (size); + if (GetTokenInformation (token, TokenUser, tu, size, (PDWORD)&size)) { + *uniname = GetSidName (NULL, tu->User.Sid, &size); + } + g_free (tu); + } + + return size; +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ + +MonoStringHandle +ves_icall_System_Security_Principal_WindowsIdentity_GetTokenName (gpointer token, MonoError *error) +{ + MonoStringHandle result; + gunichar2 *uniname = NULL; + gint32 size = 0; + + error_init (error); + + size = mono_security_win_get_token_name (token, &uniname); + + if (size > 0) { + result = mono_string_new_utf16_handle (mono_domain_get (), uniname, size, error); + } + else + result = mono_string_new_handle (mono_domain_get (), "", error); + + if (uniname) + g_free (uniname); + + return result; +} + +gpointer +ves_icall_System_Security_Principal_WindowsIdentity_GetUserToken (MonoStringHandle username, MonoError *error) +{ + error_init (error); + gpointer token = NULL; + + /* TODO: MS has something like this working in Windows 2003 (client and + * server) but works only for domain accounts (so it's quite limiting). + * http://www.develop.com/kbrown/book/html/howto_logonuser.html + */ + g_warning ("Unsupported on Win32 (anyway requires W2K3 minimum)"); + return token; +} + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +MonoArray* +ves_icall_System_Security_Principal_WindowsIdentity_GetRoles (gpointer token) +{ + MonoError error; + MonoArray *array = NULL; + MonoDomain *domain = mono_domain_get (); + + gint32 size = 0; + + GetTokenInformation (token, TokenGroups, NULL, size, (PDWORD)&size); + if (size > 0) { + TOKEN_GROUPS *tg = g_malloc0 (size); + if (GetTokenInformation (token, TokenGroups, tg, size, (PDWORD)&size)) { + int i=0; + int num = tg->GroupCount; + + array = mono_array_new_checked (domain, mono_get_string_class (), num, &error); + if (mono_error_set_pending_exception (&error)) { + g_free (tg); + return NULL; + } + + for (i=0; i < num; i++) { + gint32 size = 0; + gunichar2 *uniname = GetSidName (NULL, tg->Groups [i].Sid, &size); + + if (uniname) { + MonoString *str = mono_string_new_utf16_checked (domain, uniname, size, &error); + if (!is_ok (&error)) { + g_free (uniname); + g_free (tg); + mono_error_set_pending_exception (&error); + return NULL; + } + mono_array_setref (array, i, str); + g_free (uniname); + } + } + } + g_free (tg); + } + + if (!array) { + /* return empty array of string, i.e. string [0] */ + array = mono_array_new_checked (domain, mono_get_string_class (), 0, &error); + mono_error_set_pending_exception (&error); + } + return array; +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ + +gboolean +ves_icall_System_Security_Principal_WindowsImpersonationContext_CloseToken (gpointer token) +{ + gboolean result = TRUE; + result = (CloseHandle (token) != 0); + return result; +} + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +gpointer +ves_icall_System_Security_Principal_WindowsImpersonationContext_DuplicateToken (gpointer token) +{ + gpointer dupe = NULL; + + if (DuplicateToken (token, SecurityImpersonation, &dupe) == 0) { + dupe = NULL; + } + return dupe; +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ + +gboolean +ves_icall_System_Security_Principal_WindowsPrincipal_IsMemberOfGroupId (gpointer user, gpointer group) +{ + gboolean result = FALSE; + + /* The convertion from an ID to a string is done in managed code for Windows */ + g_warning ("IsMemberOfGroupId should never be called on Win32"); + return result; +} + +gboolean +ves_icall_System_Security_Principal_WindowsPrincipal_IsMemberOfGroupName (gpointer user, MonoString *group) +{ + gboolean result = FALSE; + + /* Windows version use a cache built using WindowsIdentity._GetRoles */ + g_warning ("IsMemberOfGroupName should never be called on Win32"); + return result; +} + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +static PSID +GetAdministratorsSid (void) +{ + SID_IDENTIFIER_AUTHORITY admins = { SECURITY_NT_AUTHORITY }; + PSID pSid = NULL; + if (!AllocateAndInitializeSid (&admins, 2, SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pSid)) + return NULL; + /* Note: this SID must be freed with FreeSid () */ + return pSid; +} + +static PSID +GetEveryoneSid (void) +{ + SID_IDENTIFIER_AUTHORITY everyone = { SECURITY_WORLD_SID_AUTHORITY }; + PSID pSid = NULL; + if (!AllocateAndInitializeSid (&everyone, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pSid)) + return NULL; + /* Note: this SID must be freed with FreeSid () */ + return pSid; +} + +static PSID +GetCurrentUserSid (void) +{ + PSID sid = NULL; + guint32 size = 0; + gpointer token = mono_security_principal_windows_identity_get_current_token (); + + GetTokenInformation (token, TokenUser, NULL, size, (PDWORD)&size); + if (size > 0) { + TOKEN_USER *tu = g_malloc0 (size); + if (GetTokenInformation (token, TokenUser, tu, size, (PDWORD)&size)) { + DWORD length = GetLengthSid (tu->User.Sid); + sid = (PSID) g_malloc0 (length); + if (!CopySid (length, sid, tu->User.Sid)) { + g_free (sid); + sid = NULL; + } + } + g_free (tu); + } + /* Note: this SID must be freed with g_free () */ + return sid; +} + +static ACCESS_MASK +GetRightsFromSid (PSID sid, PACL acl) +{ + ACCESS_MASK rights = 0; + TRUSTEE trustee; + + BuildTrusteeWithSidW (&trustee, sid); + if (GetEffectiveRightsFromAcl (acl, &trustee, &rights) != ERROR_SUCCESS) + return 0; + + return rights; +} + +gboolean +mono_security_win_is_machine_protected (gunichar2 *path) +{ + gboolean success = FALSE; + PACL pDACL = NULL; + PSECURITY_DESCRIPTOR pSD = NULL; + PSID pEveryoneSid = NULL; + + DWORD dwRes = GetNamedSecurityInfoW (path, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, &pDACL, NULL, &pSD); + if (dwRes != ERROR_SUCCESS) + return FALSE; + + /* We check that Everyone is still limited to READ-ONLY - + but not if new entries have been added by an Administrator */ + + pEveryoneSid = GetEveryoneSid (); + if (pEveryoneSid) { + ACCESS_MASK rights = GetRightsFromSid (pEveryoneSid, pDACL); + /* http://msdn.microsoft.com/library/en-us/security/security/generic_access_rights.asp?frame=true */ + success = (rights == (READ_CONTROL | SYNCHRONIZE | FILE_READ_DATA | FILE_READ_EA | FILE_READ_ATTRIBUTES)); + FreeSid (pEveryoneSid); + } + /* Note: we don't need to check our own access - + we'll know soon enough when reading the file */ + + if (pSD) + LocalFree (pSD); + + return success; +} + +gboolean +mono_security_win_is_user_protected (gunichar2 *path) +{ + gboolean success = FALSE; + PACL pDACL = NULL; + PSID pEveryoneSid = NULL; + PSECURITY_DESCRIPTOR pSecurityDescriptor = NULL; + + DWORD dwRes = GetNamedSecurityInfoW (path, SE_FILE_OBJECT, + DACL_SECURITY_INFORMATION, NULL, NULL, &pDACL, NULL, &pSecurityDescriptor); + if (dwRes != ERROR_SUCCESS) + return FALSE; + + /* We check that our original entries in the ACL are in place - + but not if new entries have been added by the user */ + + /* Everyone should be denied */ + pEveryoneSid = GetEveryoneSid (); + if (pEveryoneSid) { + ACCESS_MASK rights = GetRightsFromSid (pEveryoneSid, pDACL); + success = (rights == 0); + FreeSid (pEveryoneSid); + } + /* Note: we don't need to check our own access - + we'll know soon enough when reading the file */ + + if (pSecurityDescriptor) + LocalFree (pSecurityDescriptor); + + return success; +} + +gboolean +mono_security_win_protect_machine (gunichar2 *path) +{ + PSID pEveryoneSid = GetEveryoneSid (); + PSID pAdminsSid = GetAdministratorsSid (); + DWORD retval = -1; + + if (pEveryoneSid && pAdminsSid) { + PACL pDACL = NULL; + EXPLICIT_ACCESS ea [2]; + ZeroMemory (&ea, 2 * sizeof (EXPLICIT_ACCESS)); + + /* grant all access to the BUILTIN\Administrators group */ + BuildTrusteeWithSidW (&ea [0].Trustee, pAdminsSid); + ea [0].grfAccessPermissions = GENERIC_ALL; + ea [0].grfAccessMode = SET_ACCESS; + ea [0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; + ea [0].Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea [0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; + + /* read-only access everyone */ + BuildTrusteeWithSidW (&ea [1].Trustee, pEveryoneSid); + ea [1].grfAccessPermissions = GENERIC_READ; + ea [1].grfAccessMode = SET_ACCESS; + ea [1].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; + ea [1].Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea [1].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; + + retval = SetEntriesInAcl (2, ea, NULL, &pDACL); + if (retval == ERROR_SUCCESS) { + /* with PROTECTED_DACL_SECURITY_INFORMATION we */ + /* remove any existing ACL (like inherited ones) */ + retval = SetNamedSecurityInfo (path, SE_FILE_OBJECT, + DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION, + NULL, NULL, pDACL, NULL); + } + if (pDACL) + LocalFree (pDACL); + } + + if (pEveryoneSid) + FreeSid (pEveryoneSid); + if (pAdminsSid) + FreeSid (pAdminsSid); + return (retval == ERROR_SUCCESS); +} + +gboolean +mono_security_win_protect_user (gunichar2 *path) +{ + DWORD retval = -1; + + PSID pCurrentSid = GetCurrentUserSid (); + if (pCurrentSid) { + PACL pDACL = NULL; + EXPLICIT_ACCESS ea; + ZeroMemory (&ea, sizeof (EXPLICIT_ACCESS)); + + /* grant exclusive access to the current user */ + BuildTrusteeWithSidW (&ea.Trustee, pCurrentSid); + ea.grfAccessPermissions = GENERIC_ALL; + ea.grfAccessMode = SET_ACCESS; + ea.grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; + ea.Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea.Trustee.TrusteeType = TRUSTEE_IS_USER; + + retval = SetEntriesInAcl (1, &ea, NULL, &pDACL); + if (retval == ERROR_SUCCESS) { + /* with PROTECTED_DACL_SECURITY_INFORMATION we + remove any existing ACL (like inherited ones) */ + retval = SetNamedSecurityInfo (path, SE_FILE_OBJECT, + DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION, + NULL, NULL, pDACL, NULL); + } + + if (pDACL) + LocalFree (pDACL); + g_free (pCurrentSid); /* g_malloc0 */ + } + + return (retval == ERROR_SUCCESS); +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ + +MonoBoolean +ves_icall_Mono_Security_Cryptography_KeyPairPersistence_CanSecure (MonoString *root) +{ + gint32 flags; + + /* ACL are nice... unless you have FAT or other uncivilized filesystem */ + if (!GetVolumeInformation (mono_string_chars (root), NULL, 0, NULL, NULL, (LPDWORD)&flags, NULL, 0)) + return FALSE; + return ((flags & FS_PERSISTENT_ACLS) == FS_PERSISTENT_ACLS); +} + +MonoBoolean +ves_icall_Mono_Security_Cryptography_KeyPairPersistence_IsMachineProtected (MonoString *path) +{ + gboolean ret = FALSE; + + /* no one, but the owner, should have write access to the directory */ + ret = mono_security_win_is_machine_protected (mono_string_chars (path)); + return (MonoBoolean)ret; +} + +MonoBoolean +ves_icall_Mono_Security_Cryptography_KeyPairPersistence_IsUserProtected (MonoString *path) +{ + gboolean ret = FALSE; + + /* no one, but the user, should have access to the directory */ + ret = mono_security_win_is_user_protected (mono_string_chars (path)); + return (MonoBoolean)ret; +} + +MonoBoolean +ves_icall_Mono_Security_Cryptography_KeyPairPersistence_ProtectMachine (MonoString *path) +{ + gboolean ret = FALSE; + + /* read/write to owner, read to everyone else */ + ret = mono_security_win_protect_machine (mono_string_chars (path)); + return (MonoBoolean)ret; +} + +MonoBoolean +ves_icall_Mono_Security_Cryptography_KeyPairPersistence_ProtectUser (MonoString *path) +{ + gboolean ret = FALSE; + + /* read/write to user, no access to everyone else */ + ret = mono_security_win_protect_user (mono_string_chars (path)); + return (MonoBoolean)ret; +} +#endif /* HOST_WIN32 */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/mono-security.c b/unity-2019.4.24f1-mbe/mono/metadata/mono-security.c new file mode 100644 index 000000000..52c8efcf6 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/mono-security.c @@ -0,0 +1,649 @@ +/** + * \file + * Security internal calls + * + * Author: + * Sebastien Pouliot + * + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef HOST_WIN32 +#include +#ifdef HAVE_GRP_H +#include +#endif +#ifdef HAVE_PWD_H +#include +#endif +#include +#include +#include +#include + +/* Disclaimers */ + +#if defined(__GNUC__) + +#ifndef HAVE_GETGRGID_R + #warning Non-thread safe getgrgid being used! +#endif +#ifndef HAVE_GETGRNAM_R + #warning Non-thread safe getgrnam being used! +#endif +#ifndef HAVE_GETPWNAM_R + #warning Non-thread safe getpwnam being used! +#endif +#ifndef HAVE_GETPWUID_R + #warning Non-thread safe getpwuid being used! +#endif + +#endif /* defined(__GNUC__) */ +#endif /* !HOST_WIN32 */ + +/* internal functions - reuse driven */ + +/* ask a server to translate a SID into a textual representation */ +#ifndef HOST_WIN32 +#define MONO_SYSCONF_DEFAULT_SIZE ((size_t) 1024) + +/* + * Ensure we always get a valid (usable) value from sysconf. + * In case of error, we return the default value. + */ +static size_t mono_sysconf (int name) +{ + size_t size = (size_t) sysconf (name); + /* default value */ + return (size == -1) ? MONO_SYSCONF_DEFAULT_SIZE : size; +} + +static gchar* +GetTokenName (uid_t uid) +{ + gchar *uname = NULL; + +#ifdef HAVE_PWD_H + +#ifdef HAVE_GETPWUID_R + struct passwd pwd; + size_t fbufsize; + gchar *fbuf; + gint32 retval; +#endif + struct passwd *p = NULL; + gboolean result; + +#ifdef HAVE_GETPWUID_R +#ifdef _SC_GETPW_R_SIZE_MAX + fbufsize = mono_sysconf (_SC_GETPW_R_SIZE_MAX); +#else + fbufsize = MONO_SYSCONF_DEFAULT_SIZE; +#endif + fbuf = (gchar *)g_malloc0 (fbufsize); + retval = getpwuid_r (uid, &pwd, fbuf, fbufsize, &p); + result = ((retval == 0) && (p == &pwd)); +#else + /* default to non thread-safe but posix compliant function */ + p = getpwuid (uid); + result = (p != NULL); +#endif + + if (result) { + uname = g_strdup (p->pw_name); + } + +#ifdef HAVE_GETPWUID_R + g_free (fbuf); +#endif + +#endif /* HAVE_PWD_H */ + + return uname; +} + +#ifdef HAVE_GRP_H + +static gboolean +IsMemberInList (uid_t user, struct group *g) +{ + gboolean result = FALSE; + gchar *utf8_username = GetTokenName (user); + + if (!utf8_username) + return FALSE; + + if (g) { + gchar **users = g->gr_mem; + + while (*users) { + gchar *u = *(users); + if (strcmp (utf8_username, u) == 0) { + result = TRUE; + break; + } + users++; + } + } + + g_free (utf8_username); + return result; +} + +#endif /* HAVE_GRP_H */ + +static gboolean +IsDefaultGroup (uid_t user, gid_t group) +{ + gboolean result = FALSE; + +#ifdef HAVE_PWD_H + +#ifdef HAVE_GETPWUID_R + struct passwd pwd; + size_t fbufsize; + gchar *fbuf; + gint32 retval; +#endif + struct passwd *p = NULL; + +#ifdef HAVE_GETPWUID_R +#ifdef _SC_GETPW_R_SIZE_MAX + fbufsize = mono_sysconf (_SC_GETPW_R_SIZE_MAX); +#else + fbufsize = MONO_SYSCONF_DEFAULT_SIZE; +#endif + + fbuf = (gchar *)g_malloc0 (fbufsize); + retval = getpwuid_r (user, &pwd, fbuf, fbufsize, &p); + result = ((retval == 0) && (p == &pwd)); +#else + /* default to non thread-safe but posix compliant function */ + p = getpwuid (user); + result = (p != NULL); +#endif + + if (result) { + result = (p->pw_gid == group); + } + +#ifdef HAVE_GETPWUID_R + g_free (fbuf); +#endif + +#endif /* HAVE_PWD_H */ + + return result; +} + +#ifdef HAVE_GRP_H + +static gboolean +IsMemberOf (gid_t user, struct group *g) +{ + if (!g) + return FALSE; + + /* is it the user default group */ + if (IsDefaultGroup (user, g->gr_gid)) + return TRUE; + + /* is the user in the group list */ + return IsMemberInList (user, g); +} + +#endif /* HAVE_GRP_H */ + +#endif /* !HOST_WIN32 */ + +/* ICALLS */ + +/* System.Security.Principal.WindowsIdentity */ + +#ifndef HOST_WIN32 +gpointer +mono_security_principal_windows_identity_get_current_token () +{ + return GINT_TO_POINTER (geteuid ()); +} + +gpointer +ves_icall_System_Security_Principal_WindowsIdentity_GetCurrentToken (MonoError *error) +{ + error_init (error); + return mono_security_principal_windows_identity_get_current_token (); +} + +static gint32 +internal_get_token_name (gpointer token, gunichar2 ** uniname) +{ + gint32 size = 0; + + gchar *uname = GetTokenName ((uid_t) GPOINTER_TO_INT (token)); + + if (uname) { + size = strlen (uname); + *uniname = g_utf8_to_utf16 (uname, size, NULL, NULL, NULL); + g_free (uname); + } + + return size; +} + +MonoStringHandle +ves_icall_System_Security_Principal_WindowsIdentity_GetTokenName (gpointer token, MonoError *error) +{ + MonoStringHandle result; + gunichar2 *uniname = NULL; + gint32 size = 0; + + error_init (error); + + size = internal_get_token_name (token, &uniname); + + if (size > 0) { + result = mono_string_new_utf16_handle (mono_domain_get (), uniname, size, error); + } + else + result = mono_string_new_handle (mono_domain_get (), "", error); + + if (uniname) + g_free (uniname); + + return result; +} +#endif /* !HOST_WIN32 */ + +#ifndef HOST_WIN32 +gpointer +ves_icall_System_Security_Principal_WindowsIdentity_GetUserToken (MonoStringHandle username, MonoError *error) +{ + gpointer token = (gpointer)-2; + + error_init (error); +#if defined (HAVE_PWD_H) && !defined (HOST_WASM) + +#ifdef HAVE_GETPWNAM_R + struct passwd pwd; + size_t fbufsize; + gchar *fbuf; + gint32 retval; +#endif + struct passwd *p; + gchar *utf8_name; + gboolean result; + + utf8_name = mono_string_handle_to_utf8 (username, error); + return_val_if_nok (error, NULL); + +#ifdef HAVE_GETPWNAM_R +#ifdef _SC_GETPW_R_SIZE_MAX + fbufsize = mono_sysconf (_SC_GETPW_R_SIZE_MAX); +#else + fbufsize = MONO_SYSCONF_DEFAULT_SIZE; +#endif + + fbuf = (gchar *)g_malloc0 (fbufsize); + retval = getpwnam_r (utf8_name, &pwd, fbuf, fbufsize, &p); + result = ((retval == 0) && (p == &pwd)); +#else + /* default to non thread-safe but posix compliant function */ + p = getpwnam (utf8_name); + result = (p != NULL); +#endif + + if (result) { + token = GINT_TO_POINTER (p->pw_uid); + } + +#ifdef HAVE_GETPWNAM_R + g_free (fbuf); +#endif + g_free (utf8_name); + +#endif /* HAVE_PWD_H */ + + return token; +} +#endif /* HOST_WIN32 */ + +/* http://www.dotnet247.com/247reference/msgs/39/195403.aspx +// internal static string[] WindowsIdentity._GetRoles (IntPtr token) +*/ + +#ifndef HOST_WIN32 +MonoArray* +ves_icall_System_Security_Principal_WindowsIdentity_GetRoles (gpointer token) +{ + MonoError error; + MonoArray *array = NULL; + MonoDomain *domain = mono_domain_get (); + + /* POSIX-compliant systems should use IsMemberOfGroupId or IsMemberOfGroupName */ + g_warning ("WindowsIdentity._GetRoles should never be called on POSIX"); + + if (!array) { + /* return empty array of string, i.e. string [0] */ + array = mono_array_new_checked (domain, mono_get_string_class (), 0, &error); + mono_error_set_pending_exception (&error); + } + return array; +} +#endif /* !HOST_WIN32 */ + +/* System.Security.Principal.WindowsImpersonationContext */ + +#ifndef HOST_WIN32 +gboolean +ves_icall_System_Security_Principal_WindowsImpersonationContext_CloseToken (gpointer token) +{ + return TRUE; +} +#endif /* !HOST_WIN32 */ + +#ifndef HOST_WIN32 +gpointer +ves_icall_System_Security_Principal_WindowsImpersonationContext_DuplicateToken (gpointer token) +{ + return token; +} +#endif /* !HOST_WIN32 */ + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +gboolean +ves_icall_System_Security_Principal_WindowsImpersonationContext_SetCurrentToken (gpointer token) +{ +#ifdef HOST_WIN32 + return (ImpersonateLoggedOnUser (token) != 0); +#else + uid_t itoken = (uid_t) GPOINTER_TO_INT (token); +#ifdef HAVE_SETRESUID + if (setresuid (-1, itoken, getuid ()) < 0) + return FALSE; +#endif + return geteuid () == itoken; +#endif +} + +gboolean +ves_icall_System_Security_Principal_WindowsImpersonationContext_RevertToSelf (void) +{ +#ifdef HOST_WIN32 + return (RevertToSelf () != 0); +#else +#ifdef HAVE_GETRESUID + uid_t ruid, euid; +#endif + uid_t suid = -1; + +#ifdef HAVE_GETRESUID + if (getresuid (&ruid, &euid, &suid) < 0) + return FALSE; +#endif +#ifdef HAVE_SETRESUID + if (setresuid (-1, suid, -1) < 0) + return FALSE; +#else + return TRUE; +#endif + return geteuid () == suid; +#endif +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ + +/* System.Security.Principal.WindowsPrincipal */ + +#ifndef HOST_WIN32 +gboolean +ves_icall_System_Security_Principal_WindowsPrincipal_IsMemberOfGroupId (gpointer user, gpointer group) +{ + gboolean result = FALSE; + +#ifdef HAVE_GRP_H + +#ifdef HAVE_GETGRGID_R + struct group grp; + size_t fbufsize; + gchar *fbuf; + gint32 retval; +#endif + struct group *g = NULL; + +#ifdef HAVE_GETGRGID_R +#ifdef _SC_GETGR_R_SIZE_MAX + fbufsize = mono_sysconf (_SC_GETGR_R_SIZE_MAX); +#else + fbufsize = MONO_SYSCONF_DEFAULT_SIZE; +#endif + fbuf = (gchar *)g_malloc0 (fbufsize); + retval = getgrgid_r ((gid_t) GPOINTER_TO_INT (group), &grp, fbuf, fbufsize, &g); + result = ((retval == 0) && (g == &grp)); +#else + /* default to non thread-safe but posix compliant function */ + g = getgrgid ((gid_t) GPOINTER_TO_INT (group)); + result = (g != NULL); +#endif + + if (result) { + result = IsMemberOf ((uid_t) GPOINTER_TO_INT (user), g); + } + +#ifdef HAVE_GETGRGID_R + g_free (fbuf); +#endif + +#endif /* HAVE_GRP_H */ + + return result; +} + +gboolean +ves_icall_System_Security_Principal_WindowsPrincipal_IsMemberOfGroupName (gpointer user, MonoString *group) +{ + gboolean result = FALSE; + +#ifdef HAVE_GRP_H + + gchar *utf8_groupname; + + utf8_groupname = mono_unicode_to_external (mono_string_chars (group)); + if (utf8_groupname) { + struct group *g = NULL; +#ifdef HAVE_GETGRNAM_R + struct group grp; + gchar *fbuf; + gint32 retval; +#ifdef _SC_GETGR_R_SIZE_MAX + size_t fbufsize = mono_sysconf (_SC_GETGR_R_SIZE_MAX); +#else + size_t fbufsize = MONO_SYSCONF_DEFAULT_SIZE; +#endif + fbuf = (gchar *)g_malloc0 (fbufsize); + retval = getgrnam_r (utf8_groupname, &grp, fbuf, fbufsize, &g); + result = ((retval == 0) && (g == &grp)); +#else + /* default to non thread-safe but posix compliant function */ + g = getgrnam (utf8_groupname); + result = (g != NULL); +#endif + + if (result) { + result = IsMemberOf ((uid_t) GPOINTER_TO_INT (user), g); + } + +#ifdef HAVE_GETGRNAM_R + g_free (fbuf); +#endif + g_free (utf8_groupname); + } + +#endif /* HAVE_GRP_H */ + + return result; +} +#endif /* !HOST_WIN32 */ + +/* Mono.Security.Cryptography IO related internal calls */ + +#ifndef HOST_WIN32 +static gboolean +IsProtected (MonoString *path, gint32 protection) +{ + gboolean result = FALSE; + gchar *utf8_name = mono_unicode_to_external (mono_string_chars (path)); + if (utf8_name) { + struct stat st; + if (stat (utf8_name, &st) == 0) { + result = (((st.st_mode & 0777) & protection) == 0); + } + g_free (utf8_name); + } + return result; +} + + +static gboolean +Protect (MonoString *path, gint32 file_mode, gint32 add_dir_mode) +{ + gboolean result = FALSE; + gchar *utf8_name = mono_unicode_to_external (mono_string_chars (path)); + if (utf8_name) { + struct stat st; + if (stat (utf8_name, &st) == 0) { + int mode = file_mode; + if (st.st_mode & S_IFDIR) + mode |= add_dir_mode; + result = (chmod (utf8_name, mode) == 0); + } + g_free (utf8_name); + } + return result; +} + +MonoBoolean +ves_icall_Mono_Security_Cryptography_KeyPairPersistence_CanSecure (MonoString *root) +{ + /* we assume some kind of security is applicable outside Windows */ + return TRUE; +} + +MonoBoolean +ves_icall_Mono_Security_Cryptography_KeyPairPersistence_IsMachineProtected (MonoString *path) +{ + gboolean ret = FALSE; + + /* no one, but the owner, should have write access to the directory */ + ret = IsProtected (path, (S_IWGRP | S_IWOTH)); + return (MonoBoolean)ret; +} + +MonoBoolean +ves_icall_Mono_Security_Cryptography_KeyPairPersistence_IsUserProtected (MonoString *path) +{ + gboolean ret = FALSE; + + /* no one, but the user, should have access to the directory */ + ret = IsProtected (path, (S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH)); + return (MonoBoolean)ret; +} + +MonoBoolean +ves_icall_Mono_Security_Cryptography_KeyPairPersistence_ProtectMachine (MonoString *path) +{ + gboolean ret = FALSE; + + /* read/write to owner, read to everyone else */ + ret = Protect (path, (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH), (S_IXUSR | S_IXGRP | S_IXOTH)); + return (MonoBoolean)ret; +} + +MonoBoolean +ves_icall_Mono_Security_Cryptography_KeyPairPersistence_ProtectUser (MonoString *path) +{ + gboolean ret = FALSE; + + /* read/write to user, no access to everyone else */ + ret = Protect (path, (S_IRUSR | S_IWUSR), S_IXUSR); + return (MonoBoolean)ret; +} +#endif /* !HOST_WIN32 */ + +/* + * Returns TRUE if there is "something" where the Authenticode signature is + * normally located. Returns FALSE is data directory is empty. + * + * Note: Neither the structure nor the signature is verified by this function. + */ +MonoBoolean +ves_icall_System_Security_Policy_Evidence_IsAuthenticodePresent (MonoReflectionAssemblyHandle refass, MonoError *error) +{ + error_init (error); + if (MONO_HANDLE_IS_NULL (refass)) + return FALSE; + MonoAssembly *assembly = MONO_HANDLE_GETVAL (refass, assembly); + if (assembly && assembly->image) { + return (MonoBoolean)mono_image_has_authenticode_entry (assembly->image); + } + return FALSE; +} + + +/* System.Security.SecureString related internal calls */ + +static MonoImage *system_security_assembly = NULL; + +void +ves_icall_System_Security_SecureString_DecryptInternal (MonoArray *data, MonoObject *scope) +{ + MonoError error; + invoke_protected_memory_method (data, scope, FALSE, &error); + mono_error_set_pending_exception (&error); +} +void +ves_icall_System_Security_SecureString_EncryptInternal (MonoArray* data, MonoObject *scope) +{ + MonoError error; + invoke_protected_memory_method (data, scope, TRUE, &error); + mono_error_set_pending_exception (&error); +} + +void invoke_protected_memory_method (MonoArray *data, MonoObject *scope, gboolean encrypt, MonoError *error) +{ + MonoClass *klass; + MonoMethod *method; + void *params [2]; + + error_init (error); + + if (system_security_assembly == NULL) { + system_security_assembly = mono_image_loaded ("System.Security"); + if (!system_security_assembly) { + MonoAssembly *sa = mono_assembly_open_predicate ("System.Security.dll", FALSE, FALSE, NULL, NULL, NULL); + if (!sa) + g_assert_not_reached (); + system_security_assembly = mono_assembly_get_image (sa); + } + } + + klass = mono_class_load_from_name (system_security_assembly, + "System.Security.Cryptography", "ProtectedMemory"); + method = mono_class_get_method_from_name (klass, encrypt ? "Protect" : "Unprotect", 2); + params [0] = data; + params [1] = scope; /* MemoryProtectionScope.SameProcess */ + + mono_runtime_invoke_checked (method, NULL, params, error); +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/normalization-tables.h b/unity-2019.4.24f1-mbe/mono/metadata/normalization-tables.h new file mode 100644 index 000000000..ff83729b0 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/normalization-tables.h @@ -0,0 +1,3920 @@ +static const guint8 props [] = { +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,128,128,128,128,0, +0,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, +128,0,128,128,128,128,128,128,128,128,128,0,0,0,0,0, +128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, +128,0,128,128,128,128,128,128,128,128,128,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +18,0,0,0,0,0,0,0,146,0,18,0,0,0,0,18, +0,0,18,18,146,18,0,128,18,18,18,0,18,18,18,0, +3,3,131,3,131,131,128,131,3,3,131,3,3,3,3,131, +0,3,3,3,131,131,131,0,128,3,3,3,131,3,0,0, +3,3,131,3,131,131,128,131,3,3,131,3,3,3,3,131, +0,3,3,3,131,131,131,0,128,3,3,3,131,3,0,3, +3,3,131,131,3,3,3,3,3,3,3,3,3,3,3,3, +0,0,131,131,3,3,3,3,3,3,3,3,3,3,3,3, +3,3,3,3,3,3,0,0,3,3,3,3,3,3,3,3, +3,0,18,18,3,3,3,3,0,3,3,3,3,3,3,18, +18,0,0,3,3,3,3,3,3,18,0,0,131,131,3,3, +3,3,0,0,3,3,3,3,3,3,131,131,3,3,3,3, +131,131,3,3,3,3,0,0,131,131,131,131,3,3,3,3, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,146, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +131,131,0,0,0,0,0,0,0,0,0,0,0,0,0,131, +131,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0, +0,0,0,0,18,18,18,18,18,18,18,18,18,3,3,3, +3,3,3,3,3,3,3,3,3,3,3,3,3,0,3,3, +3,3,3,3,0,0,3,3,3,3,131,131,3,3,3,3, +3,18,18,18,3,3,0,0,3,3,3,3,3,3,3,3, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, +3,3,3,3,3,3,3,3,3,3,3,3,0,0,3,3, +0,0,0,0,0,0,131,131,131,131,3,3,3,3,131,131, +3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +18,18,18,18,18,18,18,18,18,128,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,18,18,18,18,18,18,0,0, +18,18,18,18,18,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +168,168,168,168,168,0,168,168,168,168,168,168,168,0,0,168, +0,168,0,168,168,0,0,0,0,0,0,168,0,0,0,0, +0,0,0,168,168,168,168,168,168,0,0,0,0,168,168,0, +168,168,0,0,0,0,0,0,168,0,0,0,0,0,0,0, +87,87,168,87,87,168,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,87,0,0,0,0,0,18,0,0,0,87,0, +0,0,0,0,18,147,131,87,131,131,131,0,131,0,131,131, +131,128,0,0,0,128,0,128,0,128,0,0,0,0,0,128, +0,128,0,0,0,128,0,0,0,128,3,3,131,131,131,131, +131,128,0,0,0,128,0,128,0,128,0,0,0,0,0,128, +0,128,0,0,0,128,0,0,0,128,131,131,131,131,131,0, +18,18,146,19,19,18,18,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +18,18,18,0,18,18,0,0,0,18,0,0,0,0,0,0, +3,3,0,3,0,0,128,3,0,0,0,0,3,3,3,0, +128,0,0,128,0,128,128,128,128,3,128,0,0,0,128,0, +0,0,0,128,0,0,0,128,0,0,0,128,0,128,0,0, +128,0,0,128,0,128,128,128,128,3,128,0,0,0,128,0, +0,0,0,128,0,0,0,128,0,0,0,128,0,128,0,0, +3,3,0,3,0,0,128,3,0,0,0,0,3,3,3,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,128,128,3,3,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,0,0,3,3,128,128,3,3,3,3,3,3, +0,0,3,3,3,3,3,3,128,128,3,3,3,3,3,3, +3,3,3,3,3,3,0,0,3,3,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,18,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,128,0,0,128,128,128,0,0,128,0,0,128, +0,128,128,0,0,0,0,0,0,0,0,0,0,0,0,0, +128,128,128,128,128,128,128,0,128,128,128,128,128,0,128,0, +128,128,0,128,128,0,128,128,128,128,128,0,0,0,0,0, +0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,3,3,3,3,3,128,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,128,0,128,0,0,0,0,0, +0,0,0,168,168,168,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,18,18,18,18,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,128,3,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,128,3,0,128,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,128,128,128,0,0,0,0,128,0,0,0, +0,128,128,0,0,0,0,0,128,3,0,128,0,0,0,128, +128,3,0,128,3,0,0,0,0,0,0,0,168,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,87,87,87,87,87,87,87,87, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,128,128,0,0,0,0,0,0,0,0,0,0,0,0,128, +0,0,0,0,0,0,0,0,0,0,0,0,128,0,168,0, +0,0,0,0,0,0,0,128,0,0,0,3,3,0,0,0, +0,0,0,0,0,0,0,168,0,0,0,0,87,87,0,87, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,128,128,0,0,0,0,128,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0, +0,0,128,87,0,0,87,0,128,0,0,0,128,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,87,87,87,0,0,87,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,128,128,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,128,0,168,0, +0,0,0,0,0,0,0,128,3,0,0,3,3,0,0,0, +0,0,0,0,0,0,168,168,0,0,0,0,87,87,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,128,0,3,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,168,0, +0,0,0,0,0,0,128,128,0,0,3,3,3,0,0,0, +0,0,0,0,0,0,0,168,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,128,0,3,0,0,0,0,0,0,0, +0,0,0,0,0,0,168,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128, +3,0,168,0,0,0,128,3,3,0,131,3,0,0,0,0, +0,0,0,0,0,168,168,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,168,0, +0,0,0,0,0,0,128,128,0,0,3,3,3,0,0,0, +0,0,0,0,0,0,0,168,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,168,0,0,0,0,168, +0,0,0,0,0,0,0,0,0,128,3,0,131,3,3,168, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,18,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,18,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,18,18,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,18,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +128,0,128,87,0,0,0,0,0,0,0,0,128,87,0,0, +0,128,87,0,0,0,128,87,0,0,0,128,87,0,0,0, +0,0,0,0,0,0,0,0,0,87,0,0,0,0,0,0, +0,128,128,87,128,87,87,18,87,18,0,0,0,0,0,0, +128,87,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +128,0,128,87,0,0,0,0,0,0,0,0,128,87,0,0, +0,128,87,0,0,0,128,87,0,0,0,128,87,0,0,0, +0,0,128,128,0,128,0,128,0,87,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,128,3,0,0,0,0,0,0,0,168,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,18,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40, +40,40,40,40,40,40,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,40,40,40,40,40,40,40,40, +40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40, +40,40,40,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,128,3,128,3,128,3,128,3,128,3,0, +0,128,3,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,168,0,0,0,0,128,3,128,3,128,128, +3,3,128,3,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,18,18,18,0, +18,18,18,18,18,18,18,18,18,18,18,0,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,0,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,0,0,0,0,0, +0,0,0,0,0,0,0,0,18,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, +3,3,3,3,3,3,131,131,3,3,3,3,3,3,3,3, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, +3,3,3,3,3,3,3,3,3,3,131,131,3,3,3,3, +3,3,131,131,3,3,3,3,3,3,3,3,3,3,3,3, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, +3,3,3,3,3,3,3,3,3,3,18,19,0,0,0,0, +131,131,3,3,3,3,3,3,3,3,3,3,3,3,3,3, +3,3,3,3,3,3,3,3,131,131,3,3,3,3,3,3, +3,3,3,3,3,3,3,3,3,3,3,3,131,131,3,3, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, +3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0, +131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131, +131,131,3,3,3,3,0,0,131,131,3,3,3,3,0,0, +131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131, +131,131,3,3,3,3,3,3,131,131,3,3,3,3,3,3, +131,131,3,3,3,3,0,0,131,131,3,3,3,3,0,0, +131,131,3,3,3,3,3,3,0,131,0,3,0,3,0,3, +131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131, +131,87,3,87,131,87,3,87,3,87,3,87,131,87,0,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, +3,3,3,3,3,0,131,3,3,3,3,87,3,18,87,146, +18,19,3,3,3,0,131,3,3,87,3,87,3,19,19,19, +3,3,3,87,0,0,3,3,3,3,3,87,0,19,19,19, +3,3,3,87,3,3,3,3,3,3,3,87,3,19,87,87, +0,0,3,3,3,0,131,3,3,87,3,87,3,87,146,0, +87,87,146,146,18,18,18,18,18,18,18,0,0,0,0,0, +0,18,0,0,0,0,0,18,0,0,0,0,0,0,0,0, +0,0,0,0,18,18,18,0,0,0,0,0,0,0,0,18, +0,0,0,18,18,0,18,18,0,0,0,0,18,0,18,0, +0,0,0,0,0,0,0,18,18,18,0,0,0,0,0,0, +0,0,0,0,0,0,0,18,0,0,0,0,0,0,0,18, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +18,18,0,0,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,0, +18,18,18,18,18,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,18,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +18,18,18,18,0,18,18,18,0,18,18,18,18,18,18,18, +18,18,18,18,0,18,18,0,0,18,18,18,18,18,0,0, +18,18,18,0,18,0,87,0,18,0,87,87,18,18,0,18, +18,18,0,18,18,18,18,18,18,18,0,18,18,18,18,18, +18,0,0,0,0,18,18,18,18,18,0,0,0,0,0,0, +0,0,0,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +128,0,128,0,128,0,0,0,0,0,3,3,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3, +128,0,128,0,128,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,128,3,0,0,0,128,3,0,128,3,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,128,3,128,3,0,0,0,0,0,18,18,0,18, +18,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0, +0,3,0,128,3,128,0,3,128,3,0,0,0,128,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,128,3,0,128,128,0,0,0,0,0,0,0,3,3,3, +3,3,128,128,3,3,128,128,3,3,128,128,128,128,0,0, +3,3,128,128,3,3,128,128,3,3,0,0,0,0,0,0, +0,128,128,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,128,0,0,0,0,0,128,128,0,128,3,3,3,3, +0,0,128,128,128,128,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,0,0,0,0,0,0,3,3,3,3,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,87,87,0,0,0,0,0, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,18,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,18,18,18,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,87,128,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,18,18,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,18,0,0,0,0,0,0,0,0,0,0,0,0, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +18,0,0,0,0,0,0,0,128,128,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,18,0,18,18,18,0,0,0,0,0, +0,0,0,0,0,0,128,0,0,0,0,128,3,128,3,128, +3,128,3,128,3,128,3,128,3,128,3,128,3,128,3,128, +3,128,3,0,128,3,128,3,128,3,0,0,0,0,0,128, +3,3,128,3,3,128,3,3,128,3,3,128,3,3,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,3,0,0,0,0,168,168,18,18,128,3,18, +0,0,0,0,0,0,128,0,0,0,0,128,3,128,3,128, +3,128,3,128,3,128,3,128,3,128,3,128,3,128,3,128, +3,128,3,0,128,3,128,3,128,3,0,0,0,0,0,128, +3,3,128,3,3,128,3,3,128,3,3,128,3,3,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128, +128,128,128,0,3,0,0,3,3,3,3,0,0,128,3,18, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,0, +0,0,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,0, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,0,0,0,0,0,0,0,0,0,0,0,0, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,0, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,0, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87, +87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87, +87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87, +87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87, +87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87, +87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87, +87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87, +87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87, +87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87, +87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87, +87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87, +87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87, +87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87, +87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87, +87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87, +87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87, +87,87,87,87,87,87,87,87,87,87,87,87,87,87,0,0, +87,0,87,0,0,87,87,87,87,87,87,87,87,87,87,0, +87,0,87,0,0,87,87,0,0,0,87,87,87,87,0,0, +87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87, +87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87, +87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87, +87,87,87,87,87,87,87,87,87,87,87,0,0,0,0,0, +87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87, +87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87, +87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87, +87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87, +87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87, +87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87, +87,87,87,87,87,87,87,87,87,87,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +18,18,18,18,18,18,18,0,0,0,0,0,0,0,0,0, +0,0,0,18,18,18,18,18,0,0,0,0,0,87,0,87, +18,18,18,18,18,18,18,18,18,18,87,87,87,87,87,87, +87,87,87,87,87,87,87,0,87,87,87,87,87,0,87,0, +87,87,0,87,87,0,87,87,87,215,87,87,87,87,87,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +0,0,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +18,18,18,18,18,18,18,18,18,18,18,18,18,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +18,18,18,18,18,18,18,18,18,18,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,0,0,18,18,18,18,18,18,18,18,18, +18,18,18,0,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,0,18,18,18,18,0,0,0,0, +18,18,18,0,18,0,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,0,0,0, +0,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,0, +0,0,18,18,18,18,18,18,0,0,18,18,18,18,18,18, +0,0,18,18,18,18,18,18,0,0,18,18,18,0,0,0, +18,18,18,18,18,18,18,0,18,18,18,18,18,18,18,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0}; +static const guint32 mappedChars [] = { +0,32,0,32,769,0,32,771,0,32,772,0,32,773,0,32, +774,0,32,775,0,32,776,0,32,778,0,32,779,0,32,787, +0,32,788,0,32,807,0,32,808,0,32,819,0,32,834,0, +32,837,0,32,1611,0,32,1612,0,32,1612,1617,0,32,1613,0, +32,1613,1617,0,32,1614,0,32,1614,1617,0,32,1615,0,32,1615, +1617,0,32,1616,0,32,1616,1617,0,32,1617,0,32,1617,1648,0, +32,1618,0,32,12441,0,32,12442,0,33,0,33,33,0,33,63, +0,34,0,35,0,36,0,37,0,38,0,39,0,40,0,40, +49,41,0,40,49,48,41,0,40,49,49,41,0,40,49,50, +41,0,40,49,51,41,0,40,49,52,41,0,40,49,53,41, +0,40,49,54,41,0,40,49,55,41,0,40,49,56,41,0, +40,49,57,41,0,40,50,41,0,40,50,48,41,0,40,51, +41,0,40,52,41,0,40,53,41,0,40,54,41,0,40,55, +41,0,40,56,41,0,40,57,41,0,40,97,41,0,40,98, +41,0,40,99,41,0,40,100,41,0,40,101,41,0,40,102, +41,0,40,103,41,0,40,104,41,0,40,105,41,0,40,106, +41,0,40,107,41,0,40,108,41,0,40,109,41,0,40,110, +41,0,40,111,41,0,40,112,41,0,40,113,41,0,40,114, +41,0,40,115,41,0,40,116,41,0,40,117,41,0,40,118, +41,0,40,119,41,0,40,120,41,0,40,121,41,0,40,122, +41,0,40,4352,41,0,40,4352,4449,41,0,40,4354,41,0,40, +4354,4449,41,0,40,4355,41,0,40,4355,4449,41,0,40,4357,41, +0,40,4357,4449,41,0,40,4358,41,0,40,4358,4449,41,0,40, +4359,41,0,40,4359,4449,41,0,40,4361,41,0,40,4361,4449,41, +0,40,4363,41,0,40,4363,4449,41,0,40,4363,4457,4364,4453,4523, +41,0,40,4363,4457,4370,4462,41,0,40,4364,41,0,40,4364,4449, +41,0,40,4364,4462,41,0,40,4366,41,0,40,4366,4449,41,0, +40,4367,41,0,40,4367,4449,41,0,40,4368,41,0,40,4368,4449, +41,0,40,4369,41,0,40,4369,4449,41,0,40,4370,41,0,40, +4370,4449,41,0,40,19968,41,0,40,19971,41,0,40,19977,41,0, +40,20061,41,0,40,20108,41,0,40,20116,41,0,40,20195,41,0, +40,20225,41,0,40,20241,41,0,40,20843,41,0,40,20845,41,0, +40,21172,41,0,40,21313,41,0,40,21332,41,0,40,21517,41,0, +40,21628,41,0,40,22235,41,0,40,22303,41,0,40,23398,41,0, +40,26085,41,0,40,26376,41,0,40,26377,41,0,40,26408,41,0, +40,26666,41,0,40,27700,41,0,40,28779,41,0,40,29305,41,0, +40,30435,41,0,40,31038,41,0,40,31069,41,0,40,31085,41,0, +40,33258,41,0,40,33267,41,0,40,36001,41,0,40,36039,41,0, +40,37329,41,0,41,0,42,0,43,0,44,0,45,0,46,0, +46,46,0,46,46,46,0,47,0,48,0,48,28857,0,49,0, +49,46,0,49,48,0,49,48,46,0,49,48,26085,0,49,48, +26376,0,49,48,28857,0,49,49,0,49,49,46,0,49,49,26085, +0,49,49,26376,0,49,49,28857,0,49,50,0,49,50,46,0, +49,50,26085,0,49,50,26376,0,49,50,28857,0,49,51,0,49, +51,46,0,49,51,26085,0,49,51,28857,0,49,52,0,49,52, +46,0,49,52,26085,0,49,52,28857,0,49,53,0,49,53,46, +0,49,53,26085,0,49,53,28857,0,49,54,0,49,54,46,0, +49,54,26085,0,49,54,28857,0,49,55,0,49,55,46,0,49, +55,26085,0,49,55,28857,0,49,56,0,49,56,46,0,49,56, +26085,0,49,56,28857,0,49,57,0,49,57,46,0,49,57,26085, +0,49,57,28857,0,49,8260,0,49,8260,50,0,49,8260,51,0, +49,8260,52,0,49,8260,53,0,49,8260,54,0,49,8260,56,0, +49,26085,0,49,26376,0,49,28857,0,50,0,50,46,0,50,48, +0,50,48,46,0,50,48,26085,0,50,48,28857,0,50,49,0, +50,49,26085,0,50,49,28857,0,50,50,0,50,50,26085,0,50, +50,28857,0,50,51,0,50,51,26085,0,50,51,28857,0,50,52, +0,50,52,26085,0,50,52,28857,0,50,53,0,50,53,26085,0, +50,54,0,50,54,26085,0,50,55,0,50,55,26085,0,50,56, +0,50,56,26085,0,50,57,0,50,57,26085,0,50,8260,51,0, +50,8260,53,0,50,26085,0,50,26376,0,50,28857,0,51,0,51, +46,0,51,48,0,51,48,26085,0,51,49,0,51,49,26085,0, +51,50,0,51,51,0,51,52,0,51,53,0,51,54,0,51, +55,0,51,56,0,51,57,0,51,8260,52,0,51,8260,53,0, +51,8260,56,0,51,26085,0,51,26376,0,51,28857,0,52,0,52, +46,0,52,48,0,52,49,0,52,50,0,52,51,0,52,52, +0,52,53,0,52,54,0,52,55,0,52,56,0,52,57,0, +52,8260,53,0,52,26085,0,52,26376,0,52,28857,0,53,0,53, +46,0,53,48,0,53,8260,54,0,53,8260,56,0,53,26085,0, +53,26376,0,53,28857,0,54,0,54,46,0,54,26085,0,54,26376, +0,54,28857,0,55,0,55,46,0,55,8260,56,0,55,26085,0, +55,26376,0,55,28857,0,56,0,56,46,0,56,26085,0,56,26376, +0,56,28857,0,57,0,57,46,0,57,26085,0,57,26376,0,57, +28857,0,58,0,58,58,61,0,59,0,60,0,60,824,0,61, +0,61,61,0,61,61,61,0,61,824,0,62,0,62,824,0, +63,0,63,33,0,63,63,0,64,0,65,0,65,85,0,65, +768,0,65,769,0,65,770,0,65,771,0,65,772,0,65,774, +0,65,775,0,65,776,0,65,777,0,65,778,0,65,780,0, +65,783,0,65,785,0,65,803,0,65,805,0,65,808,0,65, +8725,109,0,66,0,66,113,0,66,775,0,66,803,0,66,817, +0,67,0,67,111,46,0,67,769,0,67,770,0,67,775,0, +67,780,0,67,807,0,67,8725,107,103,0,68,0,68,90,0, +68,122,0,68,381,0,68,382,0,68,775,0,68,780,0,68, +803,0,68,807,0,68,813,0,68,817,0,69,0,69,768,0, +69,769,0,69,770,0,69,771,0,69,772,0,69,774,0,69, +775,0,69,776,0,69,777,0,69,780,0,69,783,0,69,785, +0,69,803,0,69,807,0,69,808,0,69,813,0,69,816,0, +70,0,70,65,88,0,70,775,0,71,0,71,66,0,71,72, +122,0,71,80,97,0,71,121,0,71,769,0,71,770,0,71, +772,0,71,774,0,71,775,0,71,780,0,71,807,0,72,0, +72,80,0,72,103,0,72,122,0,72,770,0,72,775,0,72, +776,0,72,780,0,72,803,0,72,807,0,72,814,0,73,0, +73,73,0,73,73,73,0,73,74,0,73,85,0,73,86,0, +73,88,0,73,768,0,73,769,0,73,770,0,73,771,0,73, +772,0,73,774,0,73,775,0,73,776,0,73,777,0,73,780, +0,73,783,0,73,785,0,73,803,0,73,808,0,73,816,0, +74,0,74,770,0,75,0,75,66,0,75,75,0,75,77,0, +75,769,0,75,780,0,75,803,0,75,807,0,75,817,0,76, +0,76,74,0,76,84,68,0,76,106,0,76,183,0,76,769, +0,76,780,0,76,803,0,76,807,0,76,813,0,76,817,0, +77,0,77,66,0,77,72,122,0,77,80,97,0,77,86,0, +77,87,0,77,769,0,77,775,0,77,803,0,77,937,0,78, +0,78,74,0,78,106,0,78,111,0,78,768,0,78,769,0, +78,771,0,78,775,0,78,780,0,78,803,0,78,807,0,78, +813,0,78,817,0,79,0,79,768,0,79,769,0,79,770,0, +79,771,0,79,772,0,79,774,0,79,775,0,79,776,0,79, +777,0,79,779,0,79,780,0,79,783,0,79,785,0,79,795, +0,79,803,0,79,808,0,80,0,80,72,0,80,80,77,0, +80,82,0,80,84,69,0,80,97,0,80,769,0,80,775,0, +81,0,82,0,82,115,0,82,769,0,82,775,0,82,780,0, +82,783,0,82,785,0,82,803,0,82,807,0,82,817,0,83, +0,83,77,0,83,118,0,83,769,0,83,770,0,83,775,0, +83,780,0,83,803,0,83,806,0,83,807,0,84,0,84,69, +76,0,84,72,122,0,84,77,0,84,775,0,84,780,0,84, +803,0,84,806,0,84,807,0,84,813,0,84,817,0,85,0, +85,768,0,85,769,0,85,770,0,85,771,0,85,772,0,85, +774,0,85,776,0,85,777,0,85,778,0,85,779,0,85,780, +0,85,783,0,85,785,0,85,795,0,85,803,0,85,804,0, +85,808,0,85,813,0,85,816,0,86,0,86,73,0,86,73, +73,0,86,73,73,73,0,86,771,0,86,803,0,86,8725,109, +0,87,0,87,98,0,87,768,0,87,769,0,87,770,0,87, +775,0,87,776,0,87,803,0,88,0,88,73,0,88,73,73, +0,88,775,0,88,776,0,89,0,89,768,0,89,769,0,89, +770,0,89,771,0,89,772,0,89,775,0,89,776,0,89,777, +0,89,803,0,90,0,90,769,0,90,770,0,90,775,0,90, +780,0,90,803,0,90,817,0,91,0,92,0,93,0,94,0, +95,0,96,0,97,0,97,46,109,46,0,97,47,99,0,97, +47,115,0,97,702,0,97,768,0,97,769,0,97,770,0,97, +771,0,97,772,0,97,774,0,97,775,0,97,776,0,97,777, +0,97,778,0,97,780,0,97,783,0,97,785,0,97,803,0, +97,805,0,97,808,0,98,0,98,97,114,0,98,775,0,98, +803,0,98,817,0,99,0,99,47,111,0,99,47,117,0,99, +97,108,0,99,99,0,99,100,0,99,109,0,99,109,178,0, +99,109,179,0,99,769,0,99,770,0,99,775,0,99,780,0, +99,807,0,100,0,100,66,0,100,97,0,100,109,0,100,109, +178,0,100,109,179,0,100,122,0,100,382,0,100,775,0,100, +780,0,100,803,0,100,807,0,100,813,0,100,817,0,100,8467, +0,101,0,101,86,0,101,114,103,0,101,768,0,101,769,0, +101,770,0,101,771,0,101,772,0,101,774,0,101,775,0,101, +776,0,101,777,0,101,780,0,101,783,0,101,785,0,101,803, +0,101,807,0,101,808,0,101,813,0,101,816,0,102,0,102, +102,0,102,102,105,0,102,102,108,0,102,105,0,102,108,0, +102,109,0,102,775,0,103,0,103,97,108,0,103,769,0,103, +770,0,103,772,0,103,774,0,103,775,0,103,780,0,103,807, +0,104,0,104,80,97,0,104,97,0,104,770,0,104,775,0, +104,776,0,104,780,0,104,803,0,104,807,0,104,814,0,104, +817,0,105,0,105,105,0,105,105,105,0,105,106,0,105,110, +0,105,118,0,105,120,0,105,768,0,105,769,0,105,770,0, +105,771,0,105,772,0,105,774,0,105,776,0,105,777,0,105, +780,0,105,783,0,105,785,0,105,803,0,105,808,0,105,816, +0,106,0,106,770,0,106,780,0,107,0,107,65,0,107,72, +122,0,107,80,97,0,107,86,0,107,87,0,107,99,97,108, +0,107,103,0,107,109,0,107,109,178,0,107,109,179,0,107, +116,0,107,769,0,107,780,0,107,803,0,107,807,0,107,817, +0,107,937,0,107,8467,0,108,0,108,106,0,108,109,0,108, +110,0,108,111,103,0,108,120,0,108,183,0,108,769,0,108, +780,0,108,803,0,108,807,0,108,813,0,108,817,0,109,0, +109,65,0,109,86,0,109,87,0,109,98,0,109,103,0,109, +105,108,0,109,109,0,109,109,178,0,109,109,179,0,109,111, +108,0,109,115,0,109,178,0,109,179,0,109,769,0,109,775, +0,109,803,0,109,8467,0,109,8725,115,0,109,8725,115,178,0, +110,0,110,65,0,110,70,0,110,86,0,110,87,0,110,106, +0,110,109,0,110,115,0,110,768,0,110,769,0,110,771,0, +110,775,0,110,780,0,110,803,0,110,807,0,110,813,0,110, +817,0,111,0,111,86,0,111,768,0,111,769,0,111,770,0, +111,771,0,111,772,0,111,774,0,111,775,0,111,776,0,111, +777,0,111,779,0,111,780,0,111,783,0,111,785,0,111,795, +0,111,803,0,111,808,0,112,0,112,46,109,46,0,112,65, +0,112,70,0,112,86,0,112,87,0,112,99,0,112,115,0, +112,769,0,112,775,0,113,0,114,0,114,97,100,0,114,97, +100,8725,115,0,114,97,100,8725,115,178,0,114,769,0,114,775, +0,114,780,0,114,783,0,114,785,0,114,803,0,114,807,0, +114,817,0,115,0,115,114,0,115,116,0,115,769,0,115,770, +0,115,775,0,115,780,0,115,803,0,115,806,0,115,807,0, +116,0,116,775,0,116,776,0,116,780,0,116,803,0,116,806, +0,116,807,0,116,813,0,116,817,0,117,0,117,768,0,117, +769,0,117,770,0,117,771,0,117,772,0,117,774,0,117,776, +0,117,777,0,117,778,0,117,779,0,117,780,0,117,783,0, +117,785,0,117,795,0,117,803,0,117,804,0,117,808,0,117, +813,0,117,816,0,118,0,118,105,0,118,105,105,0,118,105, +105,105,0,118,771,0,118,803,0,119,0,119,768,0,119,769, +0,119,770,0,119,775,0,119,776,0,119,778,0,119,803,0, +120,0,120,105,0,120,105,105,0,120,775,0,120,776,0,121, +0,121,768,0,121,769,0,121,770,0,121,771,0,121,772,0, +121,775,0,121,776,0,121,777,0,121,778,0,121,803,0,122, +0,122,769,0,122,770,0,122,775,0,122,780,0,122,803,0, +122,817,0,123,0,124,0,125,0,126,0,162,0,163,0,165, +0,166,0,168,768,0,168,769,0,168,834,0,172,0,175,0, +176,67,0,176,70,0,180,0,183,0,194,768,0,194,769,0, +194,771,0,194,777,0,196,772,0,197,0,197,769,0,198,0, +198,769,0,198,772,0,199,769,0,202,768,0,202,769,0,202, +771,0,202,777,0,207,769,0,212,768,0,212,769,0,212,771, +0,212,777,0,213,769,0,213,772,0,213,776,0,214,772,0, +216,769,0,220,768,0,220,769,0,220,772,0,220,780,0,226, +768,0,226,769,0,226,771,0,226,777,0,228,772,0,229,769, +0,230,769,0,230,772,0,231,769,0,234,768,0,234,769,0, +234,771,0,234,777,0,239,769,0,240,0,244,768,0,244,769, +0,244,771,0,244,777,0,245,769,0,245,772,0,245,776,0, +246,772,0,248,769,0,252,768,0,252,769,0,252,772,0,252, +780,0,258,768,0,258,769,0,258,771,0,258,777,0,259,768, +0,259,769,0,259,771,0,259,777,0,274,768,0,274,769,0, +275,768,0,275,769,0,295,0,331,0,332,768,0,332,769,0, +333,768,0,333,769,0,346,775,0,347,775,0,352,775,0,353, +775,0,360,769,0,361,769,0,362,776,0,363,776,0,383,116, +0,383,775,0,398,0,400,0,416,768,0,416,769,0,416,771, +0,416,777,0,416,803,0,417,768,0,417,769,0,417,771,0, +417,777,0,417,803,0,427,0,431,768,0,431,769,0,431,771, +0,431,777,0,431,803,0,432,768,0,432,769,0,432,771,0, +432,777,0,432,803,0,439,780,0,490,772,0,491,772,0,546, +0,550,772,0,551,772,0,552,774,0,553,774,0,558,772,0, +559,772,0,592,0,593,0,594,0,596,0,597,0,601,0,603, +0,604,0,607,0,609,0,611,0,613,0,614,0,616,0,617, +0,618,0,621,0,623,0,624,0,625,0,626,0,627,0,628, +0,629,0,632,0,633,0,635,0,641,0,642,0,643,0,649, +0,650,0,651,0,652,0,656,0,657,0,658,0,658,780,0, +661,0,669,0,671,0,697,0,700,110,0,768,0,769,0,776, +769,0,787,0,901,0,902,0,904,0,905,0,906,0,908,0, +910,0,911,0,912,0,913,768,0,913,769,0,913,772,0,913, +774,0,913,787,0,913,788,0,913,837,0,915,0,917,768,0, +917,769,0,917,787,0,917,788,0,919,768,0,919,769,0,919, +787,0,919,788,0,919,837,0,920,0,921,768,0,921,769,0, +921,772,0,921,774,0,921,776,0,921,787,0,921,788,0,927, +768,0,927,769,0,927,787,0,927,788,0,928,0,929,788,0, +931,0,933,0,933,768,0,933,769,0,933,772,0,933,774,0, +933,776,0,933,788,0,937,0,937,768,0,937,769,0,937,787, +0,937,788,0,937,837,0,940,0,940,837,0,941,0,942,0, +942,837,0,943,0,944,0,945,768,0,945,769,0,945,772,0, +945,774,0,945,787,0,945,788,0,945,834,0,945,837,0,946, +0,947,0,948,0,949,0,949,768,0,949,769,0,949,787,0, +949,788,0,951,768,0,951,769,0,951,787,0,951,788,0,951, +834,0,951,837,0,952,0,953,0,953,768,0,953,769,0,953, +772,0,953,774,0,953,776,0,953,787,0,953,788,0,953,834, +0,954,0,956,0,956,65,0,956,70,0,956,86,0,956,87, +0,956,103,0,956,109,0,956,115,0,956,8467,0,959,768,0, +959,769,0,959,787,0,959,788,0,960,0,961,0,961,787,0, +961,788,0,962,0,965,768,0,965,769,0,965,772,0,965,774, +0,965,776,0,965,787,0,965,788,0,965,834,0,966,0,967, +0,969,768,0,969,769,0,969,787,0,969,788,0,969,834,0, +969,837,0,970,768,0,970,769,0,970,834,0,971,768,0,971, +769,0,971,834,0,972,0,973,0,974,0,974,837,0,978,769, +0,978,776,0,1030,776,0,1040,774,0,1040,776,0,1043,769,0, +1045,768,0,1045,774,0,1045,776,0,1046,774,0,1046,776,0,1047, +776,0,1048,768,0,1048,772,0,1048,774,0,1048,776,0,1050,769, +0,1054,776,0,1059,772,0,1059,774,0,1059,776,0,1059,779,0, +1063,776,0,1067,776,0,1069,776,0,1072,774,0,1072,776,0,1075, +769,0,1077,768,0,1077,774,0,1077,776,0,1078,774,0,1078,776, +0,1079,776,0,1080,768,0,1080,772,0,1080,774,0,1080,776,0, +1082,769,0,1085,0,1086,776,0,1091,772,0,1091,774,0,1091,776, +0,1091,779,0,1095,776,0,1099,776,0,1101,776,0,1110,776,0, +1140,783,0,1141,783,0,1240,776,0,1241,776,0,1256,776,0,1257, +776,0,1381,1410,0,1396,1381,0,1396,1387,0,1396,1389,0,1396,1398, +0,1406,1398,0,1488,0,1488,1463,0,1488,1464,0,1488,1468,0,1488, +1500,0,1489,0,1489,1468,0,1489,1471,0,1490,0,1490,1468,0,1491, +0,1491,1468,0,1492,0,1492,1468,0,1493,1465,0,1493,1468,0,1494, +1468,0,1496,1468,0,1497,1460,0,1497,1468,0,1498,1468,0,1499,0, +1499,1468,0,1499,1471,0,1500,0,1500,1468,0,1501,0,1502,1468,0, +1504,1468,0,1505,1468,0,1506,0,1507,1468,0,1508,1468,0,1508,1471, +0,1510,1468,0,1511,1468,0,1512,0,1512,1468,0,1513,1468,0,1513, +1473,0,1513,1474,0,1514,0,1514,1468,0,1522,1463,0,1569,0,1570, +0,1571,0,1572,0,1573,0,1574,0,1574,1575,0,1574,1580,0,1574, +1581,0,1574,1582,0,1574,1585,0,1574,1586,0,1574,1605,0,1574,1606, +0,1574,1607,0,1574,1608,0,1574,1609,0,1574,1610,0,1574,1734,0, +1574,1735,0,1574,1736,0,1574,1744,0,1574,1749,0,1575,0,1575,1603, +1576,1585,0,1575,1604,1604,1607,0,1575,1611,0,1575,1619,0,1575,1620, +0,1575,1621,0,1575,1652,0,1576,0,1576,1580,0,1576,1581,0,1576, +1581,1610,0,1576,1582,0,1576,1582,1610,0,1576,1585,0,1576,1586,0, +1576,1605,0,1576,1606,0,1576,1607,0,1576,1609,0,1576,1610,0,1577, +0,1578,0,1578,1580,0,1578,1580,1605,0,1578,1580,1609,0,1578,1580, +1610,0,1578,1581,0,1578,1581,1580,0,1578,1581,1605,0,1578,1582,0, +1578,1582,1605,0,1578,1582,1609,0,1578,1582,1610,0,1578,1585,0,1578, +1586,0,1578,1605,0,1578,1605,1580,0,1578,1605,1581,0,1578,1605,1582, +0,1578,1605,1609,0,1578,1605,1610,0,1578,1606,0,1578,1607,0,1578, +1609,0,1578,1610,0,1579,0,1579,1580,0,1579,1585,0,1579,1586,0, +1579,1605,0,1579,1606,0,1579,1607,0,1579,1609,0,1579,1610,0,1580, +0,1580,1581,0,1580,1581,1609,0,1580,1581,1610,0,1580,1604,32,1580, +1604,1575,1604,1607,0,1580,1605,0,1580,1605,1581,0,1580,1605,1609,0, +1580,1605,1610,0,1580,1609,0,1580,1610,0,1581,0,1581,1580,0,1581, +1580,1610,0,1581,1605,0,1581,1605,1609,0,1581,1605,1610,0,1581,1609, +0,1581,1610,0,1582,0,1582,1580,0,1582,1581,0,1582,1605,0,1582, +1609,0,1582,1610,0,1583,0,1584,0,1584,1648,0,1585,0,1585,1587, +1608,1604,0,1585,1648,0,1585,1740,1575,1604,0,1586,0,1587,0,1587, +1580,0,1587,1580,1581,0,1587,1580,1609,0,1587,1581,0,1587,1581,1580, +0,1587,1582,0,1587,1582,1609,0,1587,1582,1610,0,1587,1585,0,1587, +1605,0,1587,1605,1580,0,1587,1605,1581,0,1587,1605,1605,0,1587,1607, +0,1587,1609,0,1587,1610,0,1588,0,1588,1580,0,1588,1580,1610,0, +1588,1581,0,1588,1581,1605,0,1588,1581,1610,0,1588,1582,0,1588,1585, +0,1588,1605,0,1588,1605,1582,0,1588,1605,1605,0,1588,1607,0,1588, +1609,0,1588,1610,0,1589,0,1589,1581,0,1589,1581,1581,0,1589,1581, +1610,0,1589,1582,0,1589,1585,0,1589,1604,1593,1605,0,1589,1604,1609, +0,1589,1604,1609,32,1575,1604,1604,1607,32,1593,1604,1610,1607,32,1608, +1587,1604,1605,0,1589,1604,1746,0,1589,1605,0,1589,1605,1605,0,1589, +1609,0,1589,1610,0,1590,0,1590,1580,0,1590,1581,0,1590,1581,1609, +0,1590,1581,1610,0,1590,1582,0,1590,1582,1605,0,1590,1585,0,1590, +1605,0,1590,1609,0,1590,1610,0,1591,0,1591,1581,0,1591,1605,0, +1591,1605,1581,0,1591,1605,1605,0,1591,1605,1610,0,1591,1609,0,1591, +1610,0,1592,0,1592,1605,0,1593,0,1593,1580,0,1593,1580,1605,0, +1593,1604,1610,1607,0,1593,1605,0,1593,1605,1605,0,1593,1605,1609,0, +1593,1605,1610,0,1593,1609,0,1593,1610,0,1594,0,1594,1580,0,1594, +1605,0,1594,1605,1605,0,1594,1605,1609,0,1594,1605,1610,0,1594,1609, +0,1594,1610,0,1600,1611,0,1600,1614,0,1600,1614,1617,0,1600,1615, +0,1600,1615,1617,0,1600,1616,0,1600,1616,1617,0,1600,1617,0,1600, +1618,0,1601,0,1601,1580,0,1601,1581,0,1601,1582,0,1601,1582,1605, +0,1601,1605,0,1601,1605,1610,0,1601,1609,0,1601,1610,0,1602,0, +1602,1581,0,1602,1604,1746,0,1602,1605,0,1602,1605,1581,0,1602,1605, +1605,0,1602,1605,1610,0,1602,1609,0,1602,1610,0,1603,0,1603,1575, +0,1603,1580,0,1603,1581,0,1603,1582,0,1603,1604,0,1603,1605,0, +1603,1605,1605,0,1603,1605,1610,0,1603,1609,0,1603,1610,0,1604,0, +1604,1570,0,1604,1571,0,1604,1573,0,1604,1575,0,1604,1580,0,1604, +1580,1580,0,1604,1580,1605,0,1604,1580,1610,0,1604,1581,0,1604,1581, +1605,0,1604,1581,1609,0,1604,1581,1610,0,1604,1582,0,1604,1582,1605, +0,1604,1605,0,1604,1605,1581,0,1604,1605,1610,0,1604,1607,0,1604, +1609,0,1604,1610,0,1605,0,1605,1575,0,1605,1580,0,1605,1580,1581, +0,1605,1580,1582,0,1605,1580,1605,0,1605,1580,1610,0,1605,1581,0, +1605,1581,1580,0,1605,1581,1605,0,1605,1581,1605,1583,0,1605,1581,1610, +0,1605,1582,0,1605,1582,1580,0,1605,1582,1605,0,1605,1582,1610,0, +1605,1605,0,1605,1605,1610,0,1605,1609,0,1605,1610,0,1606,0,1606, +1580,0,1606,1580,1581,0,1606,1580,1605,0,1606,1580,1609,0,1606,1580, +1610,0,1606,1581,0,1606,1581,1605,0,1606,1581,1609,0,1606,1581,1610, +0,1606,1582,0,1606,1585,0,1606,1586,0,1606,1605,0,1606,1605,1609, +0,1606,1605,1610,0,1606,1606,0,1606,1607,0,1606,1609,0,1606,1610, +0,1607,0,1607,1580,0,1607,1605,0,1607,1605,1580,0,1607,1605,1605, +0,1607,1609,0,1607,1610,0,1607,1648,0,1608,0,1608,1587,1604,1605, +0,1608,1620,0,1608,1652,0,1609,0,1609,1648,0,1610,0,1610,1580, +0,1610,1580,1610,0,1610,1581,0,1610,1581,1610,0,1610,1582,0,1610, +1585,0,1610,1586,0,1610,1605,0,1610,1605,1605,0,1610,1605,1610,0, +1610,1606,0,1610,1607,0,1610,1609,0,1610,1610,0,1610,1620,0,1610, +1652,0,1649,0,1655,0,1657,0,1658,0,1659,0,1662,0,1663,0, +1664,0,1667,0,1668,0,1670,0,1671,0,1672,0,1676,0,1677,0, +1678,0,1681,0,1688,0,1700,0,1702,0,1705,0,1709,0,1711,0, +1713,0,1715,0,1722,0,1723,0,1726,0,1728,0,1729,0,1729,1620, +0,1733,0,1734,0,1735,0,1735,1652,0,1736,0,1737,0,1739,0, +1740,0,1744,0,1746,0,1746,1620,0,1747,0,1749,1620,0,2325,2364, +0,2326,2364,0,2327,2364,0,2332,2364,0,2337,2364,0,2338,2364,0, +2344,2364,0,2347,2364,0,2351,2364,0,2352,2364,0,2355,2364,0,2465, +2492,0,2466,2492,0,2479,2492,0,2503,2494,0,2503,2519,0,2582,2620, +0,2583,2620,0,2588,2620,0,2603,2620,0,2610,2620,0,2616,2620,0, +2849,2876,0,2850,2876,0,2887,2878,0,2887,2902,0,2887,2903,0,2962, +3031,0,3014,3006,0,3014,3031,0,3015,3006,0,3142,3158,0,3263,3285, +0,3270,3266,0,3270,3285,0,3270,3286,0,3274,3285,0,3398,3390,0, +3398,3415,0,3399,3390,0,3545,3530,0,3545,3535,0,3545,3551,0,3548, +3530,0,3661,3634,0,3755,3737,0,3755,3745,0,3789,3762,0,3851,0, +3904,4021,0,3906,4023,0,3916,4023,0,3921,4023,0,3926,4023,0,3931, +4023,0,3953,3954,0,3953,3956,0,3953,3968,0,3984,4021,0,3986,4023, +0,3996,4023,0,4001,4023,0,4006,4023,0,4011,4023,0,4018,3968,0, +4018,3969,0,4019,3968,0,4019,3969,0,4133,4142,0,4316,0,4352,0, +4352,4449,0,4353,0,4354,0,4354,4449,0,4355,0,4355,4449,0,4356, +0,4357,0,4357,4449,0,4358,0,4358,4449,0,4359,0,4359,4449,0, +4360,0,4361,0,4361,4449,0,4362,0,4363,0,4363,4449,0,4363,4462, +0,4364,0,4364,4449,0,4364,4462,4363,4468,0,4365,0,4366,0,4366, +4449,0,4366,4449,4535,4352,4457,0,4367,0,4367,4449,0,4368,0,4368, +4449,0,4369,0,4369,4449,0,4370,0,4370,4449,0,4372,0,4373,0, +4378,0,4380,0,4381,0,4382,0,4384,0,4385,0,4386,0,4387,0, +4391,0,4393,0,4395,0,4396,0,4397,0,4398,0,4399,0,4402,0, +4406,0,4416,0,4423,0,4428,0,4439,0,4440,0,4441,0,4448,0, +4449,0,4450,0,4451,0,4452,0,4453,0,4454,0,4455,0,4456,0, +4457,0,4458,0,4459,0,4460,0,4461,0,4462,0,4463,0,4464,0, +4465,0,4466,0,4467,0,4468,0,4469,0,4484,0,4485,0,4488,0, +4497,0,4498,0,4500,0,4510,0,4513,0,4522,0,4524,0,4525,0, +4528,0,4529,0,4530,0,4531,0,4532,0,4533,0,4551,0,4552,0, +4556,0,4558,0,4563,0,4567,0,4569,0,4573,0,4575,0,4593,0, +4594,0,6917,6965,0,6919,6965,0,6921,6965,0,6923,6965,0,6925,6965, +0,6929,6965,0,6970,6965,0,6972,6965,0,6974,6965,0,6975,6965,0, +6978,6965,0,7426,0,7446,0,7447,0,7452,0,7453,0,7461,0,7547, +0,7557,0,7734,772,0,7735,772,0,7770,772,0,7771,772,0,7778, +775,0,7779,775,0,7840,770,0,7840,774,0,7841,770,0,7841,774, +0,7864,770,0,7865,770,0,7884,770,0,7885,770,0,7936,768,0, +7936,769,0,7936,834,0,7936,837,0,7937,768,0,7937,769,0,7937, +834,0,7937,837,0,7938,837,0,7939,837,0,7940,837,0,7941,837, +0,7942,837,0,7943,837,0,7944,768,0,7944,769,0,7944,834,0, +7944,837,0,7945,768,0,7945,769,0,7945,834,0,7945,837,0,7946, +837,0,7947,837,0,7948,837,0,7949,837,0,7950,837,0,7951,837, +0,7952,768,0,7952,769,0,7953,768,0,7953,769,0,7960,768,0, +7960,769,0,7961,768,0,7961,769,0,7968,768,0,7968,769,0,7968, +834,0,7968,837,0,7969,768,0,7969,769,0,7969,834,0,7969,837, +0,7970,837,0,7971,837,0,7972,837,0,7973,837,0,7974,837,0, +7975,837,0,7976,768,0,7976,769,0,7976,834,0,7976,837,0,7977, +768,0,7977,769,0,7977,834,0,7977,837,0,7978,837,0,7979,837, +0,7980,837,0,7981,837,0,7982,837,0,7983,837,0,7984,768,0, +7984,769,0,7984,834,0,7985,768,0,7985,769,0,7985,834,0,7992, +768,0,7992,769,0,7992,834,0,7993,768,0,7993,769,0,7993,834, +0,8000,768,0,8000,769,0,8001,768,0,8001,769,0,8008,768,0, +8008,769,0,8009,768,0,8009,769,0,8016,768,0,8016,769,0,8016, +834,0,8017,768,0,8017,769,0,8017,834,0,8025,768,0,8025,769, +0,8025,834,0,8032,768,0,8032,769,0,8032,834,0,8032,837,0, +8033,768,0,8033,769,0,8033,834,0,8033,837,0,8034,837,0,8035, +837,0,8036,837,0,8037,837,0,8038,837,0,8039,837,0,8040,768, +0,8040,769,0,8040,834,0,8040,837,0,8041,768,0,8041,769,0, +8041,834,0,8041,837,0,8042,837,0,8043,837,0,8044,837,0,8045, +837,0,8046,837,0,8047,837,0,8048,837,0,8052,837,0,8060,837, +0,8118,837,0,8127,768,0,8127,769,0,8127,834,0,8134,837,0, +8182,837,0,8190,768,0,8190,769,0,8190,834,0,8194,0,8195,0, +8208,0,8211,0,8212,0,8229,0,8230,0,8242,8242,0,8242,8242,8242, +0,8242,8242,8242,8242,0,8245,8245,0,8245,8245,8245,0,8254,0,8361, +0,8592,0,8592,824,0,8593,0,8594,0,8594,824,0,8595,0,8596, +824,0,8656,824,0,8658,824,0,8660,824,0,8707,824,0,8712,824, +0,8715,824,0,8721,0,8722,0,8739,824,0,8741,824,0,8747,8747, +0,8747,8747,8747,0,8747,8747,8747,8747,0,8750,8750,0,8750,8750,8750, +0,8764,824,0,8771,824,0,8773,824,0,8776,824,0,8781,824,0, +8801,824,0,8804,824,0,8805,824,0,8818,824,0,8819,824,0,8822, +824,0,8823,824,0,8826,824,0,8827,824,0,8828,824,0,8829,824, +0,8834,824,0,8835,824,0,8838,824,0,8839,824,0,8849,824,0, +8850,824,0,8866,824,0,8872,824,0,8873,824,0,8875,824,0,8882, +824,0,8883,824,0,8884,824,0,8885,824,0,9474,0,9632,0,9675, +0,10629,0,10630,0,10973,824,0,11617,0,12289,0,12290,0,12296,0, +12297,0,12298,0,12299,0,12300,0,12301,0,12302,0,12303,0,12304,0, +12305,0,12306,0,12308,0,12309,0,12310,0,12311,0,12358,12441,0,12363, +12441,0,12365,12441,0,12367,12441,0,12369,12441,0,12371,12441,0,12373,12441, +0,12375,12441,0,12377,12441,0,12379,12441,0,12381,12441,0,12383,12441,0, +12385,12441,0,12388,12441,0,12390,12441,0,12392,12441,0,12399,12441,0,12399, +12442,0,12402,12441,0,12402,12442,0,12405,12441,0,12405,12442,0,12408,12441, +0,12408,12442,0,12411,12441,0,12411,12442,0,12424,12426,0,12441,0,12442, +0,12445,12441,0,12449,0,12450,0,12450,12497,12540,12488,0,12450,12523,12501, +12449,0,12450,12531,12506,12450,0,12450,12540,12523,0,12451,0,12452,0,12452, +12491,12531,12464,0,12452,12531,12481,0,12453,0,12454,0,12454,12441,0,12454, +12457,12531,0,12455,0,12456,0,12456,12473,12463,12540,12489,0,12456,12540,12459, +12540,0,12457,0,12458,0,12458,12531,12473,0,12458,12540,12512,0,12459,0, +12459,12441,0,12459,12452,12522,0,12459,12521,12483,12488,0,12459,12525,12522,12540, +0,12460,12525,12531,0,12460,12531,12510,0,12461,0,12461,12441,0,12461,12517, +12522,12540,0,12461,12525,0,12461,12525,12464,12521,12512,0,12461,12525,12513,12540, +12488,12523,0,12461,12525,12527,12483,12488,0,12462,12460,0,12462,12491,12540,0, +12462,12523,12480,12540,0,12463,0,12463,12441,0,12463,12523,12476,12452,12525,0, +12463,12525,12540,12493,0,12464,12521,12512,0,12464,12521,12512,12488,12531,0,12465, +0,12465,12441,0,12465,12540,12473,0,12467,0,12467,12441,0,12467,12488,0, +12467,12523,12490,0,12467,12540,12509,0,12469,0,12469,12441,0,12469,12452,12463, +12523,0,12469,12531,12481,12540,12512,0,12471,0,12471,12441,0,12471,12522,12531, +12464,0,12473,0,12473,12441,0,12475,0,12475,12441,0,12475,12531,12481,0, +12475,12531,12488,0,12477,0,12477,12441,0,12479,0,12479,12441,0,12480,12540, +12473,0,12481,0,12481,12441,0,12483,0,12484,0,12484,12441,0,12486,0, +12486,12441,0,12487,12471,0,12488,0,12488,12441,0,12488,12531,0,12489,12523, +0,12490,0,12490,12494,0,12491,0,12492,0,12493,0,12494,0,12494,12483, +12488,0,12495,0,12495,12441,0,12495,12442,0,12495,12452,12484,0,12496,12540, +12524,12523,0,12497,12540,12475,12531,12488,0,12497,12540,12484,0,12498,0,12498, +12441,0,12498,12442,0,12499,12523,0,12500,12450,12473,12488,12523,0,12500,12463, +12523,0,12500,12467,0,12501,0,12501,12441,0,12501,12442,0,12501,12449,12521, +12483,12489,0,12501,12451,12540,12488,0,12501,12521,12531,0,12502,12483,12471,12455, +12523,0,12504,0,12504,12441,0,12504,12442,0,12504,12463,12479,12540,12523,0, +12504,12523,12484,0,12505,12540,12479,0,12506,12477,0,12506,12491,12498,0,12506, +12531,12473,0,12506,12540,12472,0,12507,0,12507,12441,0,12507,12442,0,12507, +12531,0,12507,12540,12523,0,12507,12540,12531,0,12508,12523,12488,0,12509,12452, +12531,12488,0,12509,12531,12489,0,12510,0,12510,12452,12463,12525,0,12510,12452, +12523,0,12510,12483,12495,0,12510,12523,12463,0,12510,12531,12471,12519,12531,0, +12511,0,12511,12463,12525,12531,0,12511,12522,0,12511,12522,12496,12540,12523,0, +12512,0,12513,0,12513,12460,0,12513,12460,12488,12531,0,12513,12540,12488,12523, +0,12514,0,12515,0,12516,0,12516,12540,12489,0,12516,12540,12523,0,12517, +0,12518,0,12518,12450,12531,0,12519,0,12520,0,12521,0,12522,0,12522, +12483,12488,12523,0,12522,12521,0,12523,0,12523,12500,12540,0,12523,12540,12502, +12523,0,12524,0,12524,12512,0,12524,12531,12488,12466,12531,0,12525,0,12527, +0,12527,12441,0,12527,12483,12488,0,12528,0,12528,12441,0,12529,0,12529, +12441,0,12530,0,12530,12441,0,12531,0,12539,0,12540,0,12541,12441,0, +12593,0,12594,0,12595,0,12596,0,12597,0,12598,0,12599,0,12600,0, +12601,0,12602,0,12603,0,12604,0,12605,0,12606,0,12607,0,12608,0, +12609,0,12610,0,12611,0,12612,0,12613,0,12614,0,12615,0,12616,0, +12617,0,12618,0,12619,0,12620,0,12621,0,12622,0,12623,0,12624,0, +12625,0,12626,0,12627,0,12628,0,12629,0,12630,0,12631,0,12632,0, +12633,0,12634,0,12635,0,12636,0,12637,0,12638,0,12639,0,12640,0, +12641,0,12642,0,12643,0,12644,0,15261,0,16408,0,16441,0,19968,0, +19969,0,19971,0,19977,0,19978,0,19979,0,19981,0,19993,0,20006,0, +20008,0,20013,0,20018,0,20022,0,20025,0,20031,0,20057,0,20061,0, +20098,0,20101,0,20102,0,20108,0,20116,0,20128,0,20142,0,20154,0, +20160,0,20196,0,20225,0,20241,0,20352,0,20358,0,20363,0,20398,0, +20415,0,20523,0,20698,0,20711,0,20778,0,20799,0,20800,0,20805,0, +20813,0,20837,0,20840,0,20841,0,20843,0,20845,0,20864,0,20866,0, +20886,0,20889,0,20907,0,20917,0,20919,0,20937,0,20940,0,20956,0, +20958,0,20960,0,20981,0,20992,0,20999,0,21015,0,21033,0,21050,0, +21129,0,21147,0,21155,0,21172,0,21191,0,21193,0,21202,0,21214,0, +21220,0,21237,0,21241,0,21242,0,21269,0,21271,0,21274,0,21304,0, +21307,0,21311,0,21313,0,21316,0,21317,0,21329,0,21332,0,21340,0, +21353,0,21360,0,21365,0,21378,0,21430,0,21443,0,21448,0,21475,0, +21477,0,21491,0,21517,0,21519,0,21533,0,21570,0,21693,0,21845,0, +21895,0,21913,0,21917,0,21952,0,21986,0,22022,0,22120,0,22231,0, +22235,0,22265,0,22303,0,22320,0,22592,0,22618,0,22622,0,22696,0, +22707,0,22744,0,22751,0,22763,0,22786,0,22794,0,22805,0,22812,0, +22823,0,22823,27491,0,22825,0,22852,0,22856,0,22865,0,22868,0,22899, +0,23138,0,23336,0,23376,0,23398,0,23424,0,23429,0,23447,0,23527, +0,23534,0,23544,0,23567,0,23586,0,23608,0,23615,0,23650,0,23652, +0,23653,0,23662,0,23665,0,23833,0,23888,0,23994,0,24027,0,24037, +0,24038,0,24049,0,24062,0,24178,0,24179,25104,0,24180,0,24186,0, +24191,0,24230,0,24265,0,24266,0,24274,0,24275,0,24281,0,24300,0, +24308,0,24318,0,24324,0,24331,0,24339,0,24400,0,24417,0,24425,0, +24435,0,24459,0,24489,0,24493,0,24515,0,24565,0,24594,0,24604,0, +24724,0,24792,0,24801,0,24840,0,24900,0,24910,0,24928,0,24936,0, +24974,0,24976,0,25074,0,25078,0,25088,0,25096,0,25134,0,25140,0, +25142,0,25163,0,25289,0,25295,0,25299,0,25342,0,25467,0,25504,0, +25540,0,25628,0,25682,0,25754,0,25796,0,25903,0,25908,0,25935,0, +25942,0,25976,0,25991,0,26007,0,26009,0,26020,0,26041,0,26053,0, +26080,0,26082,0,26085,0,26126,27835,0,26131,0,26157,21644,0,26228,0, +26248,0,26257,0,26292,0,26310,0,26352,0,26356,0,26376,0,26377,0, +26391,0,26395,0,26408,0,26446,0,26454,0,26491,0,26519,0,26611,0, +26647,0,26666,0,26666,24335,20250,31038,0,26753,0,26757,0,26792,0,27138, +0,27155,0,27347,0,27396,0,27424,0,27490,0,27491,0,27511,0,27513, +0,27566,0,27571,0,27578,0,27595,0,27597,0,27604,0,27611,0,27663, +0,27668,0,27700,0,27784,0,27852,0,27877,0,27880,0,27931,0,27934, +0,27969,0,28010,0,28023,0,28107,0,28122,0,28138,0,28186,0,28316, +0,28346,0,28363,0,28369,0,28379,0,28431,0,28450,0,28451,0,28651, +0,28670,0,28702,0,28779,0,28825,0,28872,0,28889,0,29001,0,29038, +0,29134,0,29136,0,29200,0,29211,0,29226,0,29227,0,29237,0,29238, +0,29243,0,29247,0,29255,0,29273,0,29275,0,29282,0,29305,0,29356, +0,29359,0,29376,0,29436,0,29482,0,29557,0,29572,0,29575,0,29577, +0,29618,0,29662,0,29702,0,29705,0,29730,0,29801,0,29809,0,29833, +0,29848,0,29916,0,29926,0,29958,0,29976,0,29983,0,29992,0,30000, +0,30002,0,30007,0,30011,0,30041,0,30053,0,30064,0,30091,0,30098, +0,30178,0,30237,0,30239,0,30274,0,30313,0,30326,0,30333,0,30382, +0,30399,0,30410,0,30427,0,30435,0,30439,0,30446,0,30452,0,30465, +0,30528,0,30538,0,30631,0,30683,0,30690,0,30707,0,30827,0,30860, +0,30865,0,30922,0,30924,0,30971,0,31018,0,31034,0,31036,0,31038, +0,31048,0,31049,0,31056,0,31062,0,31069,0,31070,0,31077,0,31103, +0,31117,0,31118,0,31119,0,31150,0,31160,0,31166,0,31178,0,31192, +0,31260,0,31296,0,31348,0,31361,0,31409,0,31435,0,31481,0,31520, +0,31680,0,31806,0,31840,0,31859,0,31867,0,31890,0,31934,0,31958, +0,31975,0,31992,0,32016,0,32034,0,32047,0,32091,0,32160,0,32190, +0,32244,0,32265,0,32311,0,32321,0,32566,0,32574,0,32593,0,32626, +0,32633,0,32645,0,32650,0,32666,0,32701,0,32769,0,32773,0,32780, +0,32786,0,32819,0,32838,0,32879,0,32894,0,32895,0,32905,0,32907, +0,33240,0,33251,0,33256,0,33258,0,33261,0,33267,0,33276,0,33292, +0,33307,0,33311,0,33390,0,33391,0,33394,0,33400,0,33401,0,33509, +0,33590,0,33618,0,33737,0,33775,0,33777,0,33853,0,33865,0,33879, +0,34030,0,34044,0,34253,0,34298,0,34310,0,34322,0,34349,0,34367, +0,34381,0,34396,0,34411,0,34681,0,34746,0,34847,0,34880,0,34892, +0,34915,0,35010,0,35023,0,35041,0,35064,0,35088,0,35137,0,35172, +0,35198,0,35206,0,35211,0,35222,0,35282,0,35328,0,35498,0,35519, +0,35531,0,35538,0,35542,0,35565,0,35576,0,35582,0,35585,0,35641, +0,35672,0,35712,0,35722,0,35895,0,35910,0,35912,0,35925,0,35960, +0,35997,0,36001,0,36034,0,36039,0,36040,0,36051,0,36104,0,36196, +0,36208,0,36275,0,36335,0,36523,0,36554,0,36646,0,36650,0,36664, +0,36667,0,36706,0,36763,0,36784,0,36789,0,36790,0,36899,0,36920, +0,36969,0,36978,0,36988,0,37007,0,37009,0,37070,0,37117,0,37193, +0,37226,0,37273,0,37300,0,37318,0,37324,0,37327,0,37329,0,37428, +0,37494,0,37636,0,37706,0,38263,0,38272,0,38317,0,38428,0,38446, +0,38475,0,38477,0,38517,0,38520,0,38524,0,38534,0,38563,0,38582, +0,38584,0,38585,0,38626,0,38627,0,38632,0,38646,0,38647,0,38706, +0,38728,0,38737,0,38742,0,38750,0,38754,0,38761,0,38859,0,38875, +0,38893,0,38899,0,38911,0,38913,0,38917,0,38923,0,38936,0,38971, +0,39006,0,39080,0,39131,0,39135,0,39151,0,39164,0,39208,0,39318, +0,39321,0,39340,0,39409,0,39530,0,39592,0,39640,0,39647,0,39698, +0,39717,0,39727,0,39730,0,39740,0,39770,0,39791,0,40023,0,40165, +0,40372,0,40442,0,40478,0,40565,0,40575,0,40599,0,40607,0,40613, +0,40635,0,40643,0,40653,0,40654,0,40657,0,40697,0,40701,0,40718, +0,40723,0,40736,0,40763,0,40771,0,40778,0,40786,0,40845,0,40846, +0,40860,0,40863,0,40864,0,42863,0,64329,1473,0,64329,1474,0,141380, +0,141386,0,144341,0,152137,0,154832,0,163539,0,0}; +static const guint16 charMapIndex [] = { +8599,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,0,0,0,0,0,21,0,2004,0,0,0,0,9, +0,0,841,957,3,3699,0,0,36,638,2610,0,816,808,1000,0, +1199,1202,1205,1208,1220,1226,0,1283,1325,1328,1331,1346,1475,1478,1481,1496, +0,1632,1655,1658,1661,1664,1676,0,0,1824,1827,1830,1842,1948,0,0, +2022,2025,2028,2031,2043,2049,0,2128,2186,2189,2192,2207,2343,2346,2349,2361, +0,2589,2615,2618,2621,2624,2636,0,0,2796,2799,2802,2814,2916,0,2931, +1211,2034,1214,2037,1244,2067,1271,2116,1274,2119,1277,2122,1280,2125,1308,2159, +0,0,1337,2198,1340,2201,1343,2204,1367,2228,1352,2213,1404,2271,1410,2277, +1413,2280,1419,2286,1433,2298,0,0,1484,2352,1487,2355,1490,2358,1514,2379, +1493,0,1463,2331,1522,2387,1545,2443,0,1566,2476,1575,2485,1569,2479,1563, +2473,0,0,1629,2586,1644,2601,1638,2595,3432,0,0,1667,2627,1670,2630, +1682,2642,0,0,1735,2715,1753,2733,1741,2721,1767,2747,1770,2750,1785,2765, +1776,2756,1813,2785,1804,2776,0,0,1833,2805,1836,2808,1839,2811,1848,2820, +1851,2823,1872,2844,1916,2881,1951,2919,1963,1974,2945,1980,2951,1983,2954,2739, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1694,2654,0,0,0,0,0,0,0,0,0,0,0,0,0,1863, +2835,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,1299,1302,2153,1553,1560,2457,1617,1620,2574,1229,2052,1502, +2367,1685,2645,1854,2826,3081,3164,3078,3161,3084,3167,3075,3158,0,3014,3099, +3329,3332,3027,3108,0,0,1416,2283,1539,2437,1700,2660,3321,3324,3318,3421, +2390,1293,1296,2150,1401,2268,0,0,1626,2583,3019,3102,3024,3105,3072,3155, +1232,2055,1235,2058,1355,2216,1358,2219,1505,2370,1508,2373,1688,2648,1691,2651, +1744,2724,1747,2727,1857,2829,1860,2832,1782,2762,1810,2782,0,0,1442,2307, +0,0,0,0,0,0,1217,2040,1364,2225,3069,3152,3063,3146,1673,2633, +3341,3344,1957,2925,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2289,3371,2385,2696,3397,3399,3401,2873,2911,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,15,18,24,39,6,27,0,0, +3367,2455,2739,2896,3424,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3435,3437,0,3442,3439,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,3430,0,0,0,0,0,48,0,0,0,1160,0, +0,0,0,0,3,2982,3465,3000,3488,3500,3517,0,3538,0,3559,3579, +3798,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,3526,3568,3610,3642,3654,3676, +3807,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,3685,3761,3728,3752,3780,0, +3631,3669,3554,3822,3825,3773,3737,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3697,3739,3747,0,3512,3637,0,0,0,3552,0,0,0,0,0,0, +3840,3846,0,3837,0,0,0,3828,0,0,0,0,3870,3858,3879,0, +0,0,0,0,0,0,0,0,0,3864,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,3930,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3906,3912,0,3903,0,0,0,3965,0,0,0,0,3936,3924,3947,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,3968,3971,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3849,3915,0,0,0,0,0,0,0,0,0,0,0,0,0, +3831,3897,3834,3900,0,0,3843,3909,0,0,3974,3977,3852,3918,3855,3921, +0,0,3861,3927,3867,3933,3873,3941,0,0,3980,3983,3894,3962,3876,3944, +3882,3950,3885,3953,3888,3956,0,0,3891,3959,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,3986,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,4203,4206,5137,4209,5196,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,4212,5140,5271,5199,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +5291,0,5262,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,5286,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,5312,0,0,0,0,0,0, +0,5321,0,0,5324,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,5294,5297,5300,5303,5306,5309,5315,5318, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,5336,5339,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,5327,5330,0,5333, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,5354,0,0,5357,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,5342,5345,5348,0,0,5351,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,5369,0,0,5366,5372,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,5360,5363,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,5375,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,5378,5384,5381,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,5387,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +5390,0,0,0,0,0,0,5396,5399,0,5393,5402,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,5405,5411,5408,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,5414,0,5417,5423,5420,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,5426,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,5435,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,5429,5432,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,5438,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,5443,0,0,0,0,0,0,0,0,0,5446,0,0, +0,0,5449,0,0,0,0,5452,0,0,0,0,5455,0,0,0, +0,0,0,0,0,0,0,0,0,5440,0,0,0,0,0,0, +0,0,0,5458,0,5461,5485,5488,5491,5494,0,0,0,0,0,0, +0,5464,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,5470,0,0,0,0,0,0,0,0,0,5473,0,0, +0,0,5476,0,0,0,0,5479,0,0,0,0,5482,0,0,0, +0,0,0,0,0,0,0,0,0,5467,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,5497,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,5500,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,1194,3022,1251,0, +1291,1323,3252,1385,1422,1454,1520,1525,1551,1584,1615,0,1653,3327,1703,1730, +1788,1822,1905,2004,3347,3349,5779,2070,2131,2177,3357,3359,3361,2262,0,2393, +2494,3208,2610,3353,5781,5783,2663,2768,2794,5787,3381,2853,5789,3631,3633,3635, +3773,3775,2322,2696,2794,2853,3631,3633,3739,3773,3775,0,0,0,0,0, +0,0,0,0,0,0,0,0,3939,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,3351,2085,3355,3129,3361, +2237,3363,3365,3369,3373,3375,3377,5791,3426,3379,5793,3428,3385,3383,3387,3389, +3391,3393,3395,3403,3405,3286,3407,3409,5785,3411,3413,2943,3415,3417,3419,3669, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1241,2064,1256,2076,1259,2079,1262,2082,3030,3111,1305,2156,1311,2162,1320,2171, +1314,2165,1317,2168,3194,3200,3197,3203,1370,2231,1373,2234,3335,3338,1382,2259, +1407,2274,1436,2301,1445,2310,1439,2304,1448,2313,1451,2316,1517,2382,3045,3126, +1536,2434,1542,2440,1548,2446,1572,2482,5795,5798,1581,2491,1578,2488,1603,2539, +1606,2542,1609,2545,1635,2592,1641,2598,1650,2607,1647,2604,3060,3143,3066,3149, +3210,3216,3213,3219,1722,2688,1725,2691,1738,2718,1750,2730,5801,5804,1756,2736, +1773,2753,1779,2759,3222,3225,3228,3231,5807,5810,1801,2770,1807,2779,1819,2791, +1816,2788,1869,2841,1878,2850,1875,2847,3234,3237,3240,3243,1895,2867,1898,2870, +1910,2875,1913,2878,1922,2887,1919,2884,1925,2893,1937,2905,1940,2908,1960,2928, +1977,2948,1986,2957,1989,2960,2319,2773,2890,2937,2019,3249,0,0,0,0, +1238,2061,1223,2046,3005,3090,3002,3087,3011,3096,3008,3093,5813,5819,3173,3185, +3170,3182,3179,3191,3176,3188,5816,5822,1361,2222,1349,2210,1334,2195,3036,3117, +3033,3114,3042,3123,3039,3120,5825,5828,1499,2364,1511,2376,1697,2657,1679,2639, +3051,3134,3048,3131,3057,3140,3054,3137,5831,5834,3259,3274,3256,3271,3265,3280, +3262,3277,3268,3283,1866,2838,1845,2817,3291,3306,3288,3303,3297,3312,3294,3309, +3300,3315,1945,2913,1969,2940,1966,2934,1954,2922,0,0,0,0,0,0, +3619,3622,5837,5849,5840,5852,5843,5855,3474,3477,5879,5891,5882,5894,5885,5897, +3645,3648,5921,5927,5924,5930,0,0,3491,3494,5933,5939,5936,5942,0,0, +3657,3660,5945,5957,5948,5960,5951,5963,3503,3506,5987,5999,5990,6002,5993,6005, +3688,3691,6029,6038,6032,6041,6035,6044,3529,3532,6047,6056,6050,6059,6053,6062, +3731,3734,6065,6071,6068,6074,0,0,3541,3544,6077,6083,6080,6086,0,0, +3764,3767,6089,6098,6092,6101,6095,6104,0,3571,0,6107,0,6110,0,6113, +3783,3786,6116,6128,6119,6131,6122,6134,3582,3585,6158,6170,6161,6173,6164,6176, +3607,3591,3639,3596,3651,3598,3673,3603,3725,3813,3749,3815,3777,3817,0,0, +5846,5858,5861,5864,5867,5870,5873,5876,5888,5900,5903,5906,5909,5912,5915,5918, +5954,5966,5969,5972,5975,5978,5981,5984,5996,6008,6011,6014,6017,6020,6023,6026, +6125,6137,6140,6143,6146,6149,6152,6155,6167,6179,6182,6185,6188,6191,6194,6197, +3616,3613,6200,3628,3593,0,3625,6209,3471,3468,3462,3446,3480,30,3671,30, +45,2985,6203,3666,3600,0,3663,6221,3485,3448,3497,3450,3509,6212,6215,6218, +3682,3679,3795,3460,0,0,3694,3801,3523,3520,3514,3452,0,6227,6230,6233, +3758,3755,3804,3605,3741,3744,3770,3810,3565,3562,3556,3456,3549,2979,3444,2002, +0,0,6206,3792,3819,0,3789,6224,3535,3454,3576,3458,3588,2998,33,0, +6236,6238,1,1,1,1,1,1,1,1,1,0,0,0,0,0, +0,6240,0,0,0,0,0,42,0,0,0,0,0,0,0,0, +0,0,0,0,622,624,627,0,0,0,0,0,0,0,0,1, +0,0,0,6250,6253,0,6262,6265,0,0,0,0,107,0,12,0, +0,0,0,0,0,0,0,1189,1186,110,0,0,0,0,0,0, +0,0,0,0,0,0,0,6257,0,0,0,0,0,0,0,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +633,2322,0,0,1021,1069,1094,1108,1126,1140,616,6310,1167,125,612,2560, +633,638,841,957,1021,1069,1094,1108,1126,1140,616,6310,1167,125,612,0, +2004,2177,2610,2896,3357,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1732,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2011,2015,1265,2992,0,2087,2091,3254,0,2995,2262,1422,1422,1422,2289,3206, +1454,1454,1551,2455,0,1615,1623,0,0,1703,1728,1730,1730,1730,0,0, +1761,1790,1798,0,1972,0,3574,0,1972,0,1525,3017,1251,1265,0,2177, +1323,1376,0,1584,2610,4004,4018,4026,4031,2322,0,1378,3737,3633,3483,3547, +6308,0,0,0,0,1291,2131,2177,2322,2385,0,0,0,0,0,0, +0,0,0,812,940,820,944,1004,1056,824,1077,828,1008,1081,1113,805, +1454,1456,1459,1469,1881,1883,1886,1890,1472,1928,1930,1933,1551,1265,1291,1584, +2322,2324,2327,2337,2853,2855,2858,2862,2340,2896,2898,2901,2455,2085,2131,2494, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,6275,6282,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,6287,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,6290,6296,6293, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,6299,0,0,0,0,6302,0,0,6305,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,6312,0,6315,0,0,0,0,0,6318,6321,0,6330, +6333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,6337,0,0,6340,0,0,6343,0,6346,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1176,0,6352,0,0,0,0,0,0,0,0,0,0,6349,1164,1181, +6355,6358,0,0,6361,6364,0,0,6367,6370,0,0,0,0,0,0, +6373,6376,0,0,6385,6388,0,0,6391,6394,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,6403,6406,6409,6412, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +6379,6382,6397,6400,0,0,0,0,0,0,6415,6418,6421,6424,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,6446,6448,0,0,0,0,0, +638,841,957,1021,1069,1094,1108,1126,1140,643,662,681,700,715,730,745, +760,775,790,846,127,181,190,194,198,202,206,210,214,131,136,141, +146,151,156,161,166,171,176,185,640,843,959,1023,1071,1096,1110,1128, +1142,646,665,684,703,718,733,748,763,778,793,849,218,222,226,230, +234,238,242,246,250,254,258,262,266,270,274,278,282,286,290,294, +298,302,306,310,314,318,1194,1251,1265,1291,1323,1376,1385,1422,1454,1520, +1525,1551,1584,1615,1653,1703,1728,1730,1759,1788,1822,1881,1905,1928,1943,1972, +2004,2070,2085,2131,2177,2237,2262,2289,2322,2385,2393,2455,2494,2560,2610,2663, +2694,2696,2739,2768,2794,2853,2873,2896,2911,2943,633,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,6325,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,1156,1169,1172,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,6437,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6440, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7833, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,8595,0,0,0,0,0,0,0,0,0,0,0,0, +7278,7296,7302,7306,7308,7314,7318,7322,7326,7354,7362,7368,7374,7376,7380,7394, +7396,7398,7410,7428,7432,7436,7438,7444,7454,7456,7462,7464,7468,7470,7502,7508, +7526,7528,7530,7532,7536,7551,7557,7561,7571,7573,7575,7577,7587,7589,7597,7599, +7603,7605,7607,7614,7616,7632,7634,7638,7640,7642,7644,7648,7656,7690,7696,7698, +7722,7724,7732,7734,7738,7740,7744,7748,7768,7772,7780,7815,7817,7823,7827,7831, +7835,7837,7839,7841,7843,7893,7913,7919,7921,7923,7925,7927,7929,7935,7947,7951, +7971,7973,7977,7979,7981,7983,7997,7999,8011,8013,8015,8017,8027,8039,8041,8043, +8059,8089,8091,8101,8107,8109,8119,8131,8153,8157,8165,8169,8171,8175,8177,8179, +8187,8189,8195,8199,8203,8205,8207,8209,8211,8213,8217,8219,8257,8261,8269,8271, +8273,8289,8293,8297,8299,8327,8329,8333,8335,8337,8351,8353,8355,8359,8361,8373, +8375,8377,8393,8399,8407,8409,8413,8423,8425,8429,8447,8451,8457,8467,8471,8473, +8475,8477,8481,8483,8487,8499,8501,8503,8511,8513,8515,8521,8523,8525,8529,8531, +8533,8535,8537,8543,8551,8553,8559,8561,8563,8565,8569,8571,8573,8575,8577,8579, +8581,8585,8587,8589,8593,8597,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,6466,0,7444,7446,7448,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,6479,0,6482,0, +6485,0,6488,0,6491,0,6494,0,6497,0,6500,0,6503,0,6506,0, +6509,0,6512,0,0,6515,0,6518,0,6521,0,0,0,0,0,0, +6524,6527,0,6530,6533,0,6536,6539,0,6542,6545,0,6548,6551,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,6476,0,0,0,0,0,0,99,102,0,6561,6554, +0,0,0,0,0,0,0,0,0,0,0,0,6640,0,6667,0, +6711,0,6737,0,6746,0,6762,0,6778,0,6788,0,6793,0,6806,0, +6811,0,6820,0,0,6827,0,6832,0,6840,0,0,0,0,0,0, +6868,6871,0,6895,6898,0,6919,6922,0,6948,6951,0,6985,6988,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,6604,0,0,7137,7146,7151,7156,0,0,0,7165,6749, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,5502,5507,5706,5509,5708,5710,5514,5519,5521,5712,5714,5716,5718,5720,5722, +5600,5526,5531,5536,5610,5538,5543,5545,5553,5563,5565,5576,5581,5586,5591,5648, +5650,5652,5654,5656,5658,5660,5662,5664,5666,5668,5670,5672,5674,5676,5678,5680, +5682,5684,5686,5688,5646,5596,5598,5724,5726,5728,5730,5732,5734,5736,5602,5738, +5740,5604,5606,5608,5612,5614,5616,5618,5620,5622,5624,5626,5628,5630,5632,5634, +5636,5638,5742,5744,5640,5642,5644,5690,5692,5694,5696,5698,5700,5702,5704,0, +0,0,7278,7318,7284,7504,7286,7298,7288,7985,7308,7292,7280,7541,7510,7326, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +322,331,340,349,358,367,376,385,409,423,432,441,450,459,326,335, +344,353,362,371,380,389,413,427,436,445,454,463,418,394,402,0, +468,484,476,532,488,508,472,504,480,516,548,568,564,556,608,536, +544,560,552,580,524,572,600,584,512,492,528,540,576,496,604,520, +588,500,592,596,0,0,0,0,0,0,0,0,0,0,0,0, +1715,861,872,883,894,905,912,919,926,933,962,969,976,979,982,985, +5502,5509,5514,5521,5526,5531,5538,5545,5553,5565,5576,5581,5586,5591,5504,5511, +5516,5523,5528,5533,5540,5547,5555,5567,5578,5583,5588,5593,5570,5558,5550,0, +7278,7318,7284,7504,7320,7370,7282,7368,7310,7444,7772,7893,7843,7780,8413,7508, +7748,7794,7774,8063,7476,7933,8339,8073,7414,8095,7987,7551,8385,7352,7458,7851, +8489,7334,7378,7819,7286,7298,7288,7601,7474,7440,7565,7559,8023,7332,8343,7452, +7534,988,991,994,997,1026,1029,1032,1035,1038,1041,1044,1047,1050,1053,1074, +835,951,1015,1063,1088,1102,1120,1134,1148,654,673,692,1427,2182,2179,1556, +6566,6589,6602,6613,6628,6638,6665,6709,6735,6744,6760,6776,6786,6791,6804,6809, +6818,6825,6830,6838,6849,6854,6856,6858,6860,6866,6893,6917,6946,6983,7015,7040, +7056,7058,7073,7077,7089,7097,7099,7101,7111,7122,7133,7135,7144,7149,7154,0, +6568,6573,6578,6583,6591,6596,6607,6615,6621,6630,6634,6643,6647,6652,6657,6661, +6697,6700,6670,6704,6675,6678,6684,6691,6725,6729,6714,6720,6740,6752,6756,6765, +6770,6781,6796,6800,6814,6835,6846,6843,6851,6862,6874,6883,6889,6878,6904,6910, +6914,6901,6925,6931,6940,6936,6954,6968,6971,6960,6975,6979,6964,7006,7002,6991, +7011,6994,6998,7017,7022,7026,7030,7034,7042,7047,7050,7060,7063,7068,7079,7083, +7091,7103,7108,7113,7117,7124,7127,7140,635,838,954,1018,1066,1091,1105,1123, +1137,1151,658,677,696,711,726,741,756,771,786,801,857,868,879,890, +901,2291,2136,1196,2072,2612,2682,2139,2142,2146,1466,7609,7755,7538,7750,7796, +2670,2562,3701,2496,2395,1527,1586,1387,2095,2412,2673,2565,3704,3713,2508,2417, +1430,2398,1589,1390,1794,3722,2548,2174,2452,2256,2577,3716,2515,2105,2420,2518, +2108,2533,2423,2522,2112,2536,2427,2551,2555,1719,2402,1593,1394,2698,2702,2708, +2685,2580,3719,2530,2676,2568,3707,2499,2406,1597,2679,2571,3710,2502,2409,1600, +2449,1612,2006,1253,2099,2102,1286,1267,2133,1398,2295,1424,2334,1530,1533,2431, +2460,2463,2466,2470,2505,2511,2526,1705,2665,1708,1712,2741,1764,1907,1901,1247, +832,948,1012,1060,1085,1099,1117,1131,1145,650,669,688,707,722,737,752, +767,782,797,853,864,875,886,897,908,915,922,929,936,965,972,2264, +8331,7770,8361,8345,7877,7300,7472,8593,8593,7547,8413,7488,7545,7686,8009,8163, +8255,8265,8281,8391,7807,7853,7899,7955,8235,8401,8517,7312,7460,7813,7911,8253, +8549,7593,7887,8245,8287,7700,8193,8267,7622,7776,7859,7941,8395,7338,7384,7422, +7720,7811,7909,8025,8171,8249,8259,8357,8463,8539,8547,8047,8079,8141,8229,8419, +8553,8309,7524,7636,8117,8185,7931,8051,8341,8461,7522,7581,7809,7865,7881,8137, +8149,8433,7420,8191,7390,7388,8097,8143,8233,8437,8323,7702,7807,8315,7304,7567, +7660,7949,7995,7434,8055,7344,7652,7290,7847,7730,8135,7466,7516,8031,8237,8301, +7829,8375,7845,7706,8223,7710,7993,7324,7366,7386,7801,8129,8215,8307,8411,7426, +7482,7551,7630,7742,7889,8057,8427,8519,8555,8567,7410,7766,7821,8371,7612,7682, +7688,7718,7885,7901,7967,8093,8145,8183,8363,8241,8381,8421,7402,7412,7484,7897, +8275,8301,7620,7658,7708,7825,8115,7945,7330,7506,7567,7595,7662,7953,7963,8167, +8181,8415,8459,8465,8493,7340,8087,8405,8449,7668,7316,7348,7569,7579,7736,7807, +7905,8007,8243,8389,8589,7760,8431,7408,7786,7790,7857,7871,7959,7991,8045,8133, +8497,7370,7692,8439,7346,7591,7867,8365,7650,7672,7792,7949,8443,7404,7478,7585, +7753,7782,7805,7849,7957,8001,8161,8277,8279,8409,8453,7442,7873,7480,7907,7969, +8247,8445,8541,8557,7788,7863,8197,8107,8111,8123,7939,7895,8321,7328,8225,7406, +7400,7618,7704,8127,7563,7855,7764,8369,8271,8435,8293,7626,7356,7494,0,0, +7514,0,7758,0,0,7392,7943,8019,8061,8075,8077,8085,8469,8125,8169,0, +8251,0,8313,0,0,8383,8397,0,0,0,8505,8507,8509,8545,0,0, +7342,7350,7360,7418,7424,7450,7492,7498,7500,7512,7518,7583,7587,7664,7678,7680, +7684,7726,7746,7762,7803,7861,7869,7883,7903,7915,7961,8049,8063,8067,8065,8069, +8071,8073,8081,8083,8099,8103,8113,8145,8147,8151,8159,8173,8201,8221,8221,8239, +8283,8295,8317,8319,8347,8349,8379,8383,8455,8485,8495,0,0,0,0,0, +7294,7382,7364,7336,7358,7372,7416,7430,7492,7486,7490,7496,7514,7520,7543,7549, +7553,7555,7624,7628,7646,7654,7666,7674,7670,7680,7676,7684,7694,7712,7714,7716, +7728,7758,7776,7778,7784,7823,7829,7857,7879,7875,7883,7891,7903,8037,7917,7937, +7943,7965,7975,7989,8003,8005,8019,8021,8029,8035,8033,8053,8105,8113,8121,8139, +8145,8155,8173,8227,8231,8263,8285,8291,8295,8303,8313,8305,8317,8315,8311,8319, +8325,8349,8367,8387,8403,8417,8441,8455,8469,8479,8485,8491,8495,8527,8593,8609, +8607,8611,7272,7274,7276,8613,8615,8617,8583,8591,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2239,2250,2253,2242,2246,3246,2744,0,0,0,0,0,0,0,0,0, +0,0,0,3998,3989,3992,4001,3995,0,0,0,0,0,4053,0,4122, +4086,4004,4031,4036,4062,4070,4075,4103,4117,616,4111,4114,8601,8604,4006,4009, +4012,4020,4028,4033,4038,4044,4047,0,4050,4056,4059,4064,4072,0,4077,0, +4080,4083,0,4088,4091,0,4097,4100,4105,4108,4119,4041,4023,4067,4094,4015, +5202,5202,5210,5210,5210,5210,5212,5212,5212,5212,5216,5216,5216,5216,5208,5208, +5208,5208,5214,5214,5214,5214,5206,5206,5206,5206,5238,5238,5238,5238,5240,5240, +5240,5240,5220,5220,5220,5220,5218,5218,5218,5218,5222,5222,5222,5222,5224,5224, +5224,5224,5230,5230,5228,5228,5232,5232,5226,5226,5236,5236,5234,5234,5242,5242, +5242,5242,5246,5246,5246,5246,5250,5250,5250,5250,5248,5248,5248,5248,5252,5252, +5254,5254,5254,5254,5258,5258,5260,5260,5260,5260,5256,5256,5256,5256,5284,5284, +5289,5289,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,5244,5244,5244,5244,5269,5269,5267,5267,5274,5274,5204,5278,5278, +5265,5265,5276,5276,5282,5282,5282,5282,5143,5143,4137,4137,4185,4185,4164,4164, +4176,4176,4173,4173,4179,4179,4182,4182,4182,4167,4167,4167,5280,5280,5280,5280, +4140,4143,4155,4167,4170,4217,4220,4227,4240,4249,4252,4259,4274,4285,4306,4335, +4338,4343,4352,4361,4364,4369,4389,4412,4419,4438,4441,4444,4479,4490,4497,4511, +4583,4632,4647,4650,4661,4671,4682,4685,4708,4713,4725,4748,4751,4804,4807,4810, +4817,4824,4827,4832,4839,4854,4857,4862,4865,4868,4871,4874,4877,4888,4891,4908, +4923,4938,4945,4959,4962,4970,4989,5009,5024,5031,5034,5039,5058,5073,5082,5099, +5102,5107,5110,5121,5124,5150,5157,5164,5173,5190,5193,4457,4467,5145,57,64, +71,78,85,92,4149,4152,4155,4158,4167,4170,4234,4237,4240,4243,4249,4252, +4300,4303,4306,4329,4335,4338,4346,4349,4352,4355,4361,4364,4824,4827,4854,4857, +4862,4874,4877,4888,4891,4945,4959,4962,4967,5024,5076,5079,5082,5093,5099,5102, +5145,5167,5170,5173,5184,5190,5193,4140,4143,4146,4155,4161,4217,4220,4227,4240, +4246,4259,4274,4285,4306,4332,4352,4369,4389,4412,4419,4438,4444,4479,4490,4497, +4511,4583,4594,4632,4647,4650,4661,4671,4682,4708,4713,4725,4748,4751,4804,4807, +4810,4817,4832,4839,4865,4868,4871,4874,4877,4908,4923,4938,4945,4956,4970,4989, +5009,5024,5039,5058,5073,5082,5096,5107,5110,5127,5150,5157,5164,5173,5187,4155, +4161,4240,4246,4306,4332,4352,4358,4511,4526,4561,4572,4874,4877,4945,5082,5096, +5173,5187,4778,4785,4792,4700,4703,4740,4743,4766,4769,4529,4532,4575,4578,4430, +4433,4404,4407,4447,4450,4639,4642,4674,4677,4537,4544,4555,4561,4558,4508,4597, +4668,4700,4703,4740,4743,4766,4769,4529,4532,4575,4578,4430,4433,4404,4407,4447, +4450,4639,4642,4674,4677,4537,4544,4555,4561,4558,4508,4597,4668,4537,4544,4555, +4561,4526,4572,4685,4479,4490,4497,4537,4544,4555,4685,4708,4200,4200,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +4262,4277,4277,4281,4288,4309,4313,4317,4392,4392,4426,4422,4493,4482,4486,4518, +4518,4514,4522,4522,4586,4586,4635,4547,4547,4540,4564,4564,4568,4568,4653,4664, +4664,4688,4688,4692,4696,4716,4728,4728,4732,4754,4762,4758,4813,4813,4842,4846, +4926,4934,4930,4911,4911,4941,4941,4948,4948,4992,4996,5005,4973,4981,5012,5016, +0,0,4977,5113,5117,5061,5065,5046,5046,5050,5089,5085,5176,5176,4230,4270, +4266,4296,4292,4325,4321,4400,4372,4396,4500,4590,4551,4657,4919,4952,5160,5153, +5180,5027,4850,5069,4842,4926,4736,4884,5042,5020,4915,4880,4915,5042,4376,4415, +4985,4820,4223,4880,4716,4635,4504,5054,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +4628,4835,4195,4190,5000,4600,4462,4720,5132,4605,4609,4380,4470,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +618,6442,6444,1154,1160,105,1184,6472,6474,6248,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +6246,6244,6242,2000,2000,125,612,2963,2967,6468,6470,6462,6464,6450,6452,6446, +6448,6454,6456,6458,6460,0,0,1992,1996,6269,6269,6269,6269,2000,2000,2000, +618,6442,622,0,1160,1154,1184,105,6244,125,612,2963,2967,6468,6470,115, +121,614,616,620,1162,1179,1167,0,1994,117,119,1192,0,0,0,0, +51,4772,54,0,61,0,68,4775,75,4782,82,4789,89,4796,96,4799, +4125,4127,4127,4129,4129,4131,4131,4133,4133,4135,4135,4135,4135,4188,4188,4215, +4215,4215,4215,4255,4255,4257,4257,4257,4257,4341,4341,4341,4341,4367,4367,4367, +4367,4410,4410,4410,4410,4436,4436,4436,4436,4453,4453,4455,4455,4460,4460,4475, +4475,4477,4477,4477,4477,4535,4535,4535,4535,4581,4581,4581,4581,4645,4645,4645, +4645,4680,4680,4680,4680,4706,4706,4706,4706,4711,4711,4711,4711,4746,4746,4746, +4746,4802,4802,4802,4802,4830,4830,4830,4830,4860,4860,4860,4860,4894,4894,4894, +4894,4965,4965,4965,4965,5037,5037,5037,5037,5105,5105,5105,5105,5130,5130,5143, +5143,5148,5148,5148,5148,4896,4896,4899,4899,4902,4902,4905,4905,0,0,0, +0,105,113,115,117,119,121,123,125,612,614,616,618,620,622,631, +633,638,841,957,1021,1069,1094,1108,1126,1140,1154,1160,1162,1167,1179,1184, +1192,1194,1251,1265,1291,1323,1376,1385,1422,1454,1520,1525,1551,1584,1615,1653, +1703,1728,1730,1759,1788,1822,1881,1905,1928,1943,1972,1992,1994,1996,1998,2000, +2002,2004,2070,2085,2131,2177,2237,2262,2289,2322,2385,2393,2455,2494,2560,2610, +2663,2694,2696,2739,2768,2794,2853,2873,2896,2911,2943,2963,2965,2967,2969,6433, +6435,6444,6454,6456,6442,7161,7154,6564,6587,6600,6611,6626,7075,7087,7095,6823, +7163,6566,6589,6602,6613,6628,6638,6665,6709,6735,6744,6760,6776,6786,6791,6804, +6809,6818,6825,6830,6838,6849,6854,6856,6858,6860,6866,6893,6917,6946,6983,7015, +7040,7056,7058,7073,7077,7089,7097,7099,7101,7111,7122,7133,7135,7159,6557,6559, +7270,7168,7170,7172,7174,7176,7178,7180,7182,7184,7186,7188,7190,7192,7194,7196, +7198,7200,7202,7204,7206,7208,7210,7212,7214,7216,7218,7220,7222,7224,7226,0, +0,0,7228,7230,7232,7234,7236,7238,0,0,7240,7242,7244,7246,7248,7250, +0,0,7252,7254,7256,7258,7260,7262,0,0,7264,7266,7268,0,0,0, +2971,2973,2988,2990,2977,2975,6271,0,6427,6273,6278,6280,6285,6429,6431,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0}; +static const guint16 helperIndex [] = { +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,1160,1164,1176,1181,0, +0,1199,1256,1271,1305,1325,1382,1401,1433,1475,1522,1525,1566,1603,1626,1655, +1722,0,1735,1767,1801,1824,1895,1910,1937,1945,1974,0,0,0,0,0, +2002,2022,2076,2116,2156,2186,2259,2268,2298,2343,2387,2434,2476,2539,2583,2615, +2688,0,2715,2747,2770,2796,2867,2875,2905,2913,2945,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,2979,0,0,0,0,0,0,0, +0,0,0,0,2998,0,0,3000,0,0,0,0,0,0,0,0, +0,0,3002,0,3014,3017,3024,3030,0,0,3033,0,0,0,0,3045, +0,0,0,0,3048,3060,3069,0,3072,0,0,0,3075,0,0,0, +0,0,3087,0,3099,3102,3105,3111,0,0,3114,0,0,0,0,3126, +0,0,0,0,3131,3143,3152,0,3155,0,0,0,3158,0,0,0, +0,0,3170,3182,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,3194,3200,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,3210,3216,0,0, +0,0,0,0,0,0,0,0,0,0,3222,3225,0,0,0,0, +3228,3231,0,0,0,0,0,0,3234,3237,3240,3243,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3249, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3256,3271,0,0,0,0,0,0,0,0,0,0,0,0,0,3288, +3303,0,0,0,0,0,0,3318,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,3321,3324,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,3329,3332,3335,3338,0,0,0,0,3341,3344, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,3421,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,3430,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3435,3437,0,0,0,0,0,0,3439,0,0,0,0,0,0,0, +0,0,0,3442,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,3444,3446,0,3448,3450,3452,0,3454,0,3456,3458, +3460,3462,0,0,0,3485,0,3497,0,3514,0,0,0,0,0,3535, +0,3549,0,0,0,3556,0,0,0,3574,0,0,3591,3596,3598,3603, +3605,3607,0,0,0,3639,0,3651,0,3671,0,0,0,0,0,3725, +0,3741,0,0,0,3749,0,0,0,3777,3795,3804,3813,3815,3817,0, +0,0,3822,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,3828,0,0,0,0,0,0,0,0,0, +3831,0,0,3837,0,3840,3849,3855,3858,0,3870,0,0,0,3873,0, +0,0,0,3876,0,0,0,3888,0,0,0,3891,0,3894,0,0, +3897,0,0,3903,0,3906,3915,3921,3924,0,3936,0,0,0,3941,0, +0,0,0,3944,0,0,0,3956,0,0,0,3959,0,3962,0,0, +0,0,0,0,0,0,3965,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,3968,3971,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,3974,3977,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,3980,3983,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +4006,4020,4028,4033,4038,4041,4047,0,4050,4053,4059,4064,4072,0,4077,0, +4080,4083,0,4088,4091,0,4097,4100,4105,4108,4119,0,0,0,0,0, +0,0,4122,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,4203,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,5137,0,5196,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,5262,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,5286,0,0,5291,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,5294,5297,5300,0,0,0,0,5303,0,0,0, +0,5306,5309,0,0,0,0,0,5312,0,0,5315,0,0,0,5318, +5321,0,0,5324,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,5327,5330,0,0,0,0,0,0,0,0,0,0,0,0,5333, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,5336,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,5342,5345,0,0,0,0,5348,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,5351,0,0,0,0, +0,0,5354,0,0,0,0,0,5357,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,5360,5363,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,5366,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,5375,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,5378,5384,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,5387,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5390, +0,0,0,0,0,0,5393,0,0,0,5402,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,5405,5411,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,5414,0,0,5423,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +5440,0,5443,0,0,0,0,0,0,0,0,0,5446,0,0,0, +0,5449,0,0,0,0,5452,0,0,0,0,5455,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,5458,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +5467,0,5470,0,0,0,0,0,0,0,0,0,5473,0,0,0, +0,5476,0,0,0,0,5479,0,0,0,0,5482,0,0,0,0, +0,0,5485,5491,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,5497,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,5795,5798,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,5801,5804,0,0,0,0, +0,0,5807,5810,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +5813,5819,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,5825,5828,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,5831,5834,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +5837,5849,5861,5864,5867,5870,5873,5876,5879,5891,5903,5906,5909,5912,5915,5918, +5921,5927,0,0,0,0,0,0,5933,5939,0,0,0,0,0,0, +5945,5957,5969,5972,5975,5978,5981,5984,5987,5999,6011,6014,6017,6020,6023,6026, +6029,6038,0,0,0,0,0,0,6047,6056,0,0,0,0,0,0, +6065,6071,0,0,0,0,0,0,6077,6083,0,0,0,0,0,0, +6089,6098,0,0,0,0,0,0,0,6107,0,0,0,0,0,0, +6116,6128,6140,6143,6146,6149,6152,6155,6158,6170,6182,6185,6188,6191,6194,6197, +6200,0,0,0,6203,0,0,0,0,0,0,0,6206,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,6209,0,0,0,0,0,0,0,0,6212, +0,0,0,0,0,0,6221,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,6224,0,0,0,0,0,0,0,6227,0, +0,0,6236,6238,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +6275,0,6282,0,6287,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +6290,0,6293,0,6296,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,6299,0,0,0,0,6302,0,0,6305,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,6312,0,6315,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,6337,0,0,0, +0,0,0,6340,0,6343,0,0,6346,0,0,0,0,6349,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,6352,0,0,6355,6358,0,0,0,0,0,0,0,0,0,0, +0,0,6361,6364,0,0,6367,6370,0,0,6373,6376,6379,6382,0,0, +0,0,6385,6388,0,0,6391,6394,0,0,0,0,0,0,0,0, +0,6397,6400,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,6403,0,0,0,0,0,6406,6409,0,6412,0,0,0,0, +0,0,6415,6418,6421,6424,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,6446,6448,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,6476,0,0,0,0,6479,0,6482,0,6485, +0,6488,0,6491,0,6494,0,6497,0,6500,0,6503,0,6506,0,6509, +0,6512,0,0,6515,0,6518,0,6521,0,0,0,0,0,0,6524, +0,0,6530,0,0,6536,0,0,6542,0,0,6548,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,6561,0,0, +0,0,0,0,0,0,6604,0,0,0,0,6640,0,6667,0,6711, +0,6737,0,6746,0,6762,0,6778,0,6788,0,6793,0,6806,0,6811, +0,6820,0,0,6827,0,6832,0,6840,0,0,0,0,0,0,6868, +0,0,6895,0,0,6919,0,0,6948,0,0,6985,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7137, +7146,7151,7156,0,0,0,0,0,0,0,0,0,0,7165,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,7272,0,0, +0,0,0,0,0,0,0,0,7274,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,7290,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,7294,0,0,0,0,0,0,0,0,0, +0,0,7300,0,0,0,0,0,0,7304,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,7312,0,0,0,7316,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,7324,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +7328,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,7330,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +7336,0,0,0,0,0,7338,0,0,0,0,7340,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,7342,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7344, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,7346,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,7348,0,0,0,0,0, +0,0,0,0,0,0,0,7350,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +7356,0,0,0,0,7358,0,0,0,0,0,0,0,7360,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,7364,7366,0,0,0,7370,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +7372,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,7382,0,7384,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,7386,0,0,7388,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,7390,0,7392,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,7400,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,7402,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,7404,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,7406,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,7408,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,7410,0,0,0,0, +0,0,0,7412,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,7416,0,7418,0,0,0,0,0,0, +0,0,7420,0,0,0,0,0,0,0,0,0,0,0,7422,0, +0,0,0,0,7424,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,7426,0,0,0,0,7430,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,7434,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7442, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,7450,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,7460,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,7466,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,7472,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7478, +0,0,0,0,0,0,0,0,0,0,0,0,0,7480,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,7482,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,7484,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,7486,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,7488,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,7490,0,0,0,7492,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +7494,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,7496,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,7498,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,7500,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,7506,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +7512,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,7514,0,0,0,7516,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,7518,0,0,0,0,0,0,0, +0,0,0,7520,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,7522,0,0,0,0,0,0,7524, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,7543,0,0,0,7545,0,0,0,0,0,0,0, +0,7547,0,0,7549,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,7551,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,7553,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,7555,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,7563,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,7567,0,0,0,0,0,0,7569,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7579, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,7581,0,7583,7585,0,0,0,0,0,0,0,0,7587,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,7591,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +7593,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,7595,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,7612,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,7618,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,7620,7622,0,0,0,0,0, +0,0,7624,7626,0,0,0,0,0,7628,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,7630,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,7636,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,7646,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,7650,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,7652,0,0,0,7654,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,7658,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,7660,0,0,0,0,0,0,0,0,0,7662,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,7664,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,7666,0,0,0,0,0,0,0, +0,7668,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,7670,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,7672,0,0,0,0,0,0,0,0,0,7674,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +7676,0,0,0,0,0,0,0,7678,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,7680,0, +7682,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,7684,0,0,0,7686,0,0,0,0,0,0,0,0,0, +7688,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,7692,0, +0,0,0,0,7694,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,7700,0,0,0,0,0,7702, +0,0,0,7704,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,7706,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,7708,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +7710,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,7712,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,7714,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,7716,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,7718,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,7720,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7726, +0,0,0,0,0,0,7728,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,7730,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,7736,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,7742,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,7746,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,7753,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,7758,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,7760,0,0,0,0,0,0,0, +0,7762,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,7764,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,7766,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,7770,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,7776,0,0,0,7778,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,7782,0, +0,0,0,0,0,0,7784,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,7786,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,7788,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,7790,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,7792,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,7801,0,0,0,7803,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,7805,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,7807,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,7809,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,7811,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,7813,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,7821,0,7823,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,7825,0, +0,0,0,0,0,0,0,0,0,0,7829,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,7845,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,7847,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,7849,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,7853,0,0,7855,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,7857,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,7859,0,0,0,0,0, +0,0,0,0,0,0,0,7861,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,7863,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,7865,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,7867,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,7869,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,7871,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,7873,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,7875,0,0,0,0, +0,7877,0,0,0,0,0,0,0,0,0,7879,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7881, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,7883,7885,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,7887,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,7889,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,7891,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,7895,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,7897,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,7899,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,7901,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,7903,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,7905,0, +7907,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +7909,0,0,0,0,0,0,0,0,0,0,7911,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,7915,0,0,0,0, +0,0,0,0,0,7917,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,7931,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7937, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +7939,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,7941,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,7943,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,7945,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,7949,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,7953,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,7955,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,7957,0,0,7959,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,7961,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,7963,0,0,0,0,0,0, +0,7965,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,7967,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,7969,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,7975,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,7989,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,7991,0,0,0,0,0,0, +0,0,0,0,0,7993,0,0,0,0,0,0,0,0,0,0, +7995,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,8001,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,8003,0,8005, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,8007,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,8009,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,8019,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,8021,0,0,0,0, +0,0,0,0,0,0,0,8025,0,0,0,0,0,0,0,0, +0,0,0,0,8029,0,0,0,0,0,0,0,0,0,0,0, +0,8031,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +8033,0,0,0,0,0,0,0,0,0,8035,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,8037,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,8045,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,8047,0,0,0, +0,8049,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,8051,0,8053,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,8055,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,8057,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,8061,0,8063,0, +0,0,0,0,0,0,0,0,8065,8067,0,0,0,0,0,0, +8069,0,0,0,0,0,8071,0,0,0,0,0,0,8073,8075,0, +0,0,0,0,0,8077,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8079, +0,0,0,0,0,0,0,0,0,0,0,0,0,8081,8083,8085, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,8087,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,8093,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,8097,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +8099,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,8103,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,8105,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,8107,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +8111,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +8113,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,8115,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +8117,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,8121,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,8123,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,8125,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,8127,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,8129,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +8133,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,8135,0,0,0,0,0,0,0,0,0,0,0,0,8137, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,8139,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +8141,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,8143,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,8145,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,8147,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,8149,0,0,0,0,0,0,0,0, +0,8151,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,8155,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,8159,0,0,0,0,0,0,8161,0,0,0,0,0,0, +0,0,0,0,0,8163,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,8167,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,8169,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,8171,0,0,0,8173,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,8181,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8183, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,8185,0, +0,0,0,0,0,0,0,0,0,0,0,8191,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,8193,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,8197,0,0,0,0,8201,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8215, +0,0,0,0,0,0,0,0,0,8221,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,8223,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,8225,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,8227,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,8229,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8231, +0,8233,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,8235,0,0, +0,0,0,0,0,0,0,0,0,8237,0,0,0,0,0,0, +0,0,0,0,0,0,0,8239,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,8241,0, +0,0,0,0,0,0,0,0,0,0,0,0,8243,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,8245,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,8247,0,0,0,0,0, +0,0,0,0,0,0,8249,0,0,0,0,0,0,0,0,0, +0,0,8251,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,8253,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8255, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,8259,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,8263,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,8265,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8267, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,8271,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,8275,0,0,0,0,0,0,0,0,0,0,0,0,8277, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,8279,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,8281,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +8283,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,8285,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,8287,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,8291,0,0,0,0,8293,0,0,0,0, +0,0,0,0,0,0,8295,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,8301,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8303, +0,0,0,0,0,0,0,0,0,0,0,8305,0,0,0,0, +0,0,8307,0,0,0,8309,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,8311,0,0, +0,0,0,0,0,0,0,0,8313,0,0,0,0,0,8315,0, +0,8317,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,8319,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,8321,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +8323,0,0,0,0,0,0,0,0,0,8325,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,8331,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,8341,0,0,0,0,0,8345,0,0,0,0,0,0,0, +0,0,0,8347,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,8349,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8357, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,8361,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,8363,0,0,0,8365,0,0,0,0,0, +0,0,0,0,0,0,0,0,8367,0,0,8369,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,8371,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +8375,0,0,0,0,0,8379,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,8381,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,8383,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,8387,0,0,0,0,0,0,0,0,0,8389,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8391, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,8395,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,8397,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,8401,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,8403,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,8405,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,8409,0,0,8411, +0,8413,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,8415,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,8417,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,8419,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,8421,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,8427,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,8431,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,8433,0,8435,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,8437,0,0,8439,0,0,0,8441,0,0,0, +0,0,0,0,0,0,8443,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,8445,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,8449,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,8453,8455,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,8459,8461,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,8463,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,8465,0,0,0,0,0,0,0, +0,0,0,0,0,0,8469,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,8479,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8485, +0,0,0,0,0,0,0,0,0,0,0,8491,0,0,0,0, +0,0,0,0,0,0,0,0,8493,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,8495,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,8497,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8505, +0,0,0,0,0,0,0,0,0,0,0,0,8507,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,8509,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,8517,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,8519,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,8527,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8539, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,8541,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,8545,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,8547,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,8549,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8553, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,8555,0,0,0,0,0,0,0,8557, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,8567,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,8583,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,8589,8591,0, +0,0,0,0,0,0,0,0,0,0,0,0,8593,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,8601,0,0,0,0,0,0, +0}; +static const guint16 mapIdxToComposite [] = { +0,0,0,0,0,0,0,0,894,0,0,0,8814,0,0,0, +0,0,0,0,0,0,0,0,8800,0,0,0,0,8815,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192, +0,0,193,0,0,194,0,0,195,0,0,256,0,0,258,0, +0,550,0,0,196,0,0,7842,0,0,197,0,0,461,0,0, +512,0,0,514,0,0,7840,0,0,7680,0,0,260,0,0,0, +0,0,0,0,0,0,0,0,7682,0,0,7684,0,0,7686,0, +0,0,0,0,0,0,0,262,0,0,264,0,0,266,0,0, +268,0,0,199,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,7690,0,0,270,0,0,7692, +0,0,7696,0,0,7698,0,0,7694,0,0,0,0,200,0,0, +201,0,0,202,0,0,7868,0,0,274,0,0,276,0,0,278, +0,0,203,0,0,7866,0,0,282,0,0,516,0,0,518,0, +0,7864,0,0,552,0,0,280,0,0,7704,0,0,7706,0,0, +0,0,0,0,0,0,7710,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,500,0,0,284,0,0,7712, +0,0,286,0,0,288,0,0,486,0,0,290,0,0,0,0, +0,0,0,0,0,0,0,0,0,292,0,0,7714,0,0,7718, +0,0,542,0,0,7716,0,0,7720,0,0,7722,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,204,0,0,205,0,0,206,0,0,296,0,0,298, +0,0,300,0,0,304,0,0,207,0,0,7880,0,0,463,0, +0,520,0,0,522,0,0,7882,0,0,302,0,0,7724,0,0, +0,0,308,0,0,8490,0,0,0,0,0,0,0,0,0,0, +7728,0,0,488,0,0,7730,0,0,310,0,0,7732,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,313,0, +0,317,0,0,7734,0,0,315,0,0,7740,0,0,7738,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,7742,0,0,7744,0,0,7746,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,504,0,0,323,0,0, +209,0,0,7748,0,0,327,0,0,7750,0,0,325,0,0,7754, +0,0,7752,0,0,0,0,210,0,0,211,0,0,212,0,0, +213,0,0,332,0,0,334,0,0,558,0,0,214,0,0,7886, +0,0,336,0,0,465,0,0,524,0,0,526,0,0,416,0, +0,7884,0,0,490,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,7764,0,0,7766,0,0, +0,0,0,0,0,0,0,340,0,0,7768,0,0,344,0,0, +528,0,0,530,0,0,7770,0,0,342,0,0,7774,0,0,0, +0,0,0,0,0,0,0,346,0,0,348,0,0,7776,0,0, +352,0,0,7778,0,0,536,0,0,350,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,7786,0,0,356,0,0,7788, +0,0,538,0,0,354,0,0,7792,0,0,7790,0,0,0,0, +217,0,0,218,0,0,219,0,0,360,0,0,362,0,0,364, +0,0,220,0,0,7910,0,0,366,0,0,368,0,0,467,0, +0,532,0,0,534,0,0,431,0,0,7908,0,0,7794,0,0, +370,0,0,7798,0,0,7796,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,7804,0,0,7806,0,0,0,0,0, +0,0,0,0,0,0,7808,0,0,7810,0,0,372,0,0,7814, +0,0,7812,0,0,7816,0,0,0,0,0,0,0,0,0,0, +0,7818,0,0,7820,0,0,0,0,7922,0,0,221,0,0,374, +0,0,7928,0,0,562,0,0,7822,0,0,376,0,0,7926,0, +0,7924,0,0,0,0,377,0,0,7824,0,0,379,0,0,381, +0,0,7826,0,0,7828,0,0,0,0,0,0,0,0,0,0, +0,0,8175,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,224,0,0,225,0,0,226,0,0,227, +0,0,257,0,0,259,0,0,551,0,0,228,0,0,7843,0, +0,229,0,0,462,0,0,513,0,0,515,0,0,7841,0,0, +7681,0,0,261,0,0,0,0,0,0,0,0,7683,0,0,7685, +0,0,7687,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,263,0,0,265,0,0,267,0,0,269,0,0, +231,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,7691,0,0,271, +0,0,7693,0,0,7697,0,0,7699,0,0,7695,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,232,0,0,233,0,0, +234,0,0,7869,0,0,275,0,0,277,0,0,279,0,0,235, +0,0,7867,0,0,283,0,0,517,0,0,519,0,0,7865,0, +0,553,0,0,281,0,0,7705,0,0,7707,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,7711,0,0,0,0,0,0,0,0,501,0,0,285, +0,0,7713,0,0,287,0,0,289,0,0,487,0,0,291,0, +0,0,0,0,0,0,0,0,0,0,293,0,0,7715,0,0, +7719,0,0,543,0,0,7717,0,0,7721,0,0,7723,0,0,7830, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,236,0,0,237,0,0,238,0,0, +297,0,0,299,0,0,301,0,0,239,0,0,7881,0,0,464, +0,0,521,0,0,523,0,0,7883,0,0,303,0,0,7725,0, +0,0,0,309,0,0,496,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,7729,0,0,489,0,0,7731,0,0,311,0,0,7733,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,314,0,0,318, +0,0,7735,0,0,316,0,0,7741,0,0,7739,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,7743,0,0,7745,0, +0,7747,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,505,0,0,324,0,0,241,0,0, +7749,0,0,328,0,0,7751,0,0,326,0,0,7755,0,0,7753, +0,0,0,0,0,0,0,242,0,0,243,0,0,244,0,0, +245,0,0,333,0,0,335,0,0,559,0,0,246,0,0,7887, +0,0,337,0,0,466,0,0,525,0,0,527,0,0,417,0, +0,7885,0,0,491,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +7765,0,0,7767,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,341,0,0,7769,0, +0,345,0,0,529,0,0,531,0,0,7771,0,0,343,0,0, +7775,0,0,0,0,0,0,0,0,0,0,347,0,0,349,0, +0,7777,0,0,353,0,0,7779,0,0,537,0,0,351,0,0, +0,0,7787,0,0,7831,0,0,357,0,0,7789,0,0,539,0, +0,355,0,0,7793,0,0,7791,0,0,0,0,249,0,0,250, +0,0,251,0,0,361,0,0,363,0,0,365,0,0,252,0, +0,7911,0,0,367,0,0,369,0,0,468,0,0,533,0,0, +535,0,0,432,0,0,7909,0,0,7795,0,0,371,0,0,7799, +0,0,7797,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,7805,0,0,7807,0,0,0,0,7809,0,0,7811,0, +0,373,0,0,7815,0,0,7813,0,0,7832,0,0,7817,0,0, +0,0,0,0,0,0,0,0,0,7819,0,0,7821,0,0,0, +0,7923,0,0,253,0,0,375,0,0,7929,0,0,563,0,0, +7823,0,0,255,0,0,7927,0,0,7833,0,0,7925,0,0,0, +0,378,0,0,7825,0,0,380,0,0,382,0,0,7827,0,0, +7829,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,8173,0,0,901,0,0,8129,0,0,0,0,0,0, +0,0,0,0,0,0,8189,0,903,0,7846,0,0,7844,0,0, +7850,0,0,7848,0,0,478,0,0,8491,0,506,0,0,0,0, +508,0,0,482,0,0,7688,0,0,7872,0,0,7870,0,0,7876, +0,0,7874,0,0,7726,0,0,7890,0,0,7888,0,0,7894,0, +0,7892,0,0,7756,0,0,556,0,0,7758,0,0,554,0,0, +510,0,0,475,0,0,471,0,0,469,0,0,473,0,0,7847, +0,0,7845,0,0,7851,0,0,7849,0,0,479,0,0,507,0, +0,509,0,0,483,0,0,7689,0,0,7873,0,0,7871,0,0, +7877,0,0,7875,0,0,7727,0,0,0,0,7891,0,0,7889,0, +0,7895,0,0,7893,0,0,7757,0,0,557,0,0,7759,0,0, +555,0,0,511,0,0,476,0,0,472,0,0,470,0,0,474, +0,0,7856,0,0,7854,0,0,7860,0,0,7858,0,0,7857,0, +0,7855,0,0,7861,0,0,7859,0,0,7700,0,0,7702,0,0, +7701,0,0,7703,0,0,0,0,0,0,7760,0,0,7762,0,0, +7761,0,0,7763,0,0,7780,0,0,7781,0,0,7782,0,0,7783, +0,0,7800,0,0,7801,0,0,7802,0,0,7803,0,0,0,0, +0,7835,0,0,0,0,0,0,7900,0,0,7898,0,0,7904,0, +0,7902,0,0,7906,0,0,7901,0,0,7899,0,0,7905,0,0, +7903,0,0,7907,0,0,0,0,7914,0,0,7912,0,0,7918,0, +0,7916,0,0,7920,0,0,7915,0,0,7913,0,0,7919,0,0, +7917,0,0,7921,0,0,494,0,0,492,0,0,493,0,0,0, +0,480,0,0,481,0,0,7708,0,0,7709,0,0,560,0,0, +561,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,495,0,0, +0,0,0,0,0,0,884,0,0,0,0,832,0,833,0,836, +0,0,835,0,8174,0,8123,0,8137,0,8139,0,8155,0,8185,0, +8171,0,8187,0,8147,0,8122,0,0,902,0,0,8121,0,0,8120, +0,0,7944,0,0,7945,0,0,8124,0,0,0,0,8136,0,0, +904,0,0,7960,0,0,7961,0,0,8138,0,0,905,0,0,7976, +0,0,7977,0,0,8140,0,0,0,0,8154,0,0,906,0,0, +8153,0,0,8152,0,0,938,0,0,7992,0,0,7993,0,0,8184, +0,0,908,0,0,8008,0,0,8009,0,0,0,0,8172,0,0, +0,0,0,0,8170,0,0,910,0,0,8169,0,0,8168,0,0, +939,0,0,8025,0,0,8486,0,8186,0,0,911,0,0,8040,0, +0,8041,0,0,8188,0,0,8049,0,8116,0,0,8051,0,8053,0, +8132,0,0,8055,0,8163,0,8048,0,0,940,0,0,8113,0,0, +8112,0,0,7936,0,0,7937,0,0,8118,0,0,8115,0,0,0, +0,0,0,0,0,0,0,8050,0,0,941,0,0,7952,0,0, +7953,0,0,8052,0,0,942,0,0,7968,0,0,7969,0,0,8134, +0,0,8131,0,0,0,0,8126,0,8054,0,0,943,0,0,8145, +0,0,8144,0,0,970,0,0,7984,0,0,7985,0,0,8150,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,8056,0,0, +972,0,0,8000,0,0,8001,0,0,0,0,0,0,8164,0,0, +8165,0,0,0,0,8058,0,0,973,0,0,8161,0,0,8160,0, +0,971,0,0,8016,0,0,8017,0,0,8166,0,0,0,0,0, +0,8060,0,0,974,0,0,8032,0,0,8033,0,0,8182,0,0, +8179,0,0,8146,0,0,912,0,0,8151,0,0,8162,0,0,944, +0,0,8167,0,0,8057,0,8059,0,8061,0,8180,0,0,979,0, +0,980,0,0,1031,0,0,1232,0,0,1234,0,0,1027,0,0, +1024,0,0,1238,0,0,1025,0,0,1217,0,0,1244,0,0,1246, +0,0,1037,0,0,1250,0,0,1049,0,0,1252,0,0,1036,0, +0,1254,0,0,1262,0,0,1038,0,0,1264,0,0,1266,0,0, +1268,0,0,1272,0,0,1260,0,0,1233,0,0,1235,0,0,1107, +0,0,1104,0,0,1239,0,0,1105,0,0,1218,0,0,1245,0, +0,1247,0,0,1117,0,0,1251,0,0,1081,0,0,1253,0,0, +1116,0,0,0,0,1255,0,0,1263,0,0,1118,0,0,1265,0, +0,1267,0,0,1269,0,0,1273,0,0,1261,0,0,1111,0,0, +1142,0,0,1143,0,0,1242,0,0,1243,0,0,1258,0,0,1259, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,64302,0,0,64303,0,0,64304,0,0,0, +0,0,0,0,64305,0,0,64332,0,0,0,0,64306,0,0,0, +0,64307,0,0,0,0,64308,0,0,64331,0,0,64309,0,0,64310, +0,0,64312,0,0,64285,0,0,64313,0,0,64314,0,0,0,0, +64315,0,0,64333,0,0,0,0,64316,0,0,0,0,64318,0,0, +64320,0,0,64321,0,0,0,0,64323,0,0,64324,0,0,64334,0, +0,64326,0,0,64327,0,0,0,0,64328,0,0,64329,0,0,64298, +0,0,64299,0,0,0,0,64330,0,0,64287,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,1570,0,0,1571,0, +0,1573,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,1572,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,1574,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,1730,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,1747,0,0,0,0,1728,0,0,2392,0, +0,2393,0,0,2394,0,0,2395,0,0,2396,0,0,2397,0,0, +2345,0,0,2398,0,0,2399,0,0,2353,0,0,2356,0,0,2524, +0,0,2525,0,0,2527,0,0,2507,0,0,2508,0,0,2649,0, +0,2650,0,0,2651,0,0,2654,0,0,2611,0,0,2614,0,0, +2908,0,0,2909,0,0,2891,0,0,2888,0,0,2892,0,0,2964, +0,0,3018,0,0,3020,0,0,3019,0,0,3144,0,0,3264,0, +0,3274,0,0,3271,0,0,3272,0,0,3275,0,0,3402,0,0, +3404,0,0,3403,0,0,3546,0,0,3548,0,0,3550,0,0,3549, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3945,0,0,3907,0,0,3917,0,0,3922,0,0,3927,0,0,3932, +0,0,3955,0,0,3957,0,0,3969,0,0,4025,0,0,3987,0, +0,3997,0,0,4002,0,0,4007,0,0,4012,0,0,3958,0,0, +0,0,0,3960,0,0,0,0,0,4134,0,0,0,0,0,0, +0,0,6918,0,0,6920,0,0,6922,0,0,6924,0,0,6926,0, +0,6930,0,0,6971,0,0,6973,0,0,6976,0,0,6977,0,0, +6979,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,7736,0,0,7737,0,0,7772,0,0,7773,0,0,7784, +0,0,7785,0,0,7852,0,0,7862,0,0,7853,0,0,7863,0, +0,7878,0,0,7879,0,0,7896,0,0,7897,0,0,7938,0,0, +7940,0,0,7942,0,0,8064,0,0,7939,0,0,7941,0,0,7943, +0,0,8065,0,0,8066,0,0,8067,0,0,8068,0,0,8069,0, +0,8070,0,0,8071,0,0,7946,0,0,7948,0,0,7950,0,0, +8072,0,0,7947,0,0,7949,0,0,7951,0,0,8073,0,0,8074, +0,0,8075,0,0,8076,0,0,8077,0,0,8078,0,0,8079,0, +0,7954,0,0,7956,0,0,7955,0,0,7957,0,0,7962,0,0, +7964,0,0,7963,0,0,7965,0,0,7970,0,0,7972,0,0,7974, +0,0,8080,0,0,7971,0,0,7973,0,0,7975,0,0,8081,0, +0,8082,0,0,8083,0,0,8084,0,0,8085,0,0,8086,0,0, +8087,0,0,7978,0,0,7980,0,0,7982,0,0,8088,0,0,7979, +0,0,7981,0,0,7983,0,0,8089,0,0,8090,0,0,8091,0, +0,8092,0,0,8093,0,0,8094,0,0,8095,0,0,7986,0,0, +7988,0,0,7990,0,0,7987,0,0,7989,0,0,7991,0,0,7994, +0,0,7996,0,0,7998,0,0,7995,0,0,7997,0,0,7999,0, +0,8002,0,0,8004,0,0,8003,0,0,8005,0,0,8010,0,0, +8012,0,0,8011,0,0,8013,0,0,8018,0,0,8020,0,0,8022, +0,0,8019,0,0,8021,0,0,8023,0,0,8027,0,0,8029,0, +0,8031,0,0,8034,0,0,8036,0,0,8038,0,0,8096,0,0, +8035,0,0,8037,0,0,8039,0,0,8097,0,0,8098,0,0,8099, +0,0,8100,0,0,8101,0,0,8102,0,0,8103,0,0,8042,0, +0,8044,0,0,8046,0,0,8104,0,0,8043,0,0,8045,0,0, +8047,0,0,8105,0,0,8106,0,0,8107,0,0,8108,0,0,8109, +0,0,8110,0,0,8111,0,0,8114,0,0,8130,0,0,8178,0, +0,8119,0,0,8141,0,0,8142,0,0,8143,0,0,8135,0,0, +8183,0,0,8157,0,0,8158,0,0,8159,0,0,8192,0,8193,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,8602,0,0,0,0,0,0,8603,0,0,0,0,8622, +0,0,8653,0,0,8655,0,0,8654,0,0,8708,0,0,8713,0, +0,8716,0,0,0,0,0,0,8740,0,0,8742,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,8769,0,0,8772,0,0,8775,0,0,8777,0,0,8813,0,0, +8802,0,0,8816,0,0,8817,0,0,8820,0,0,8821,0,0,8824, +0,0,8825,0,0,8832,0,0,8833,0,0,8928,0,0,8929,0, +0,8836,0,0,8837,0,0,8840,0,0,8841,0,0,8930,0,0, +8931,0,0,8876,0,0,8877,0,0,8878,0,0,8879,0,0,8938, +0,0,8939,0,0,8940,0,0,8941,0,0,0,0,0,0,0, +0,0,0,0,0,10972,0,0,0,0,0,0,0,0,9001,0, +9002,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,12436,0,0,12364, +0,0,12366,0,0,12368,0,0,12370,0,0,12372,0,0,12374,0, +0,12376,0,0,12378,0,0,12380,0,0,12382,0,0,12384,0,0, +12386,0,0,12389,0,0,12391,0,0,12393,0,0,12400,0,0,12401, +0,0,12403,0,0,12404,0,0,12406,0,0,12407,0,0,12409,0, +0,12410,0,0,12412,0,0,12413,0,0,0,0,0,0,0,0, +0,12446,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,12532,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +12460,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,12462,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,12464,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,12466,0,0,0,0,0,0,0,0,12468,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,12470,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,12472,0,0,0,0,0, +0,0,0,0,12474,0,0,0,0,12476,0,0,0,0,0,0, +0,0,0,0,0,0,12478,0,0,0,0,12480,0,0,0,0, +0,0,0,0,12482,0,0,0,0,0,0,12485,0,0,0,0, +12487,0,0,0,0,0,0,0,12489,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,12496,0,0,12497,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12499, +0,0,12500,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,12502,0,0,12503,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,12505,0,0,12506,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,12508,0,0,12509,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,12535,0,0,0,0,0,0,0,0,12536,0,0,0,0,12537, +0,0,0,0,12538,0,0,0,0,0,0,0,0,12542,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,64210,0,64211,0,64212,0,0,0, +0,0,0,0,0,0,0,0,0,0,63847,0,0,0,64112,0, +0,0,0,0,63749,0,0,0,63838,0,0,0,0,0,0,0, +63771,0,0,0,63930,0,0,0,0,0,0,0,63863,0,0,0, +63997,0,63912,0,0,0,0,0,64115,0,63789,0,63925,0,64048,0, +63845,0,63956,0,63931,0,64049,0,0,0,0,0,64012,0,64116,0, +64050,0,0,0,64114,0,63864,0,0,0,63953,0,64117,0,0,0, +0,0,0,0,0,0,64113,0,63790,0,63865,0,63829,0,63828,0, +64021,0,0,0,0,0,0,0,64000,0,63900,0,63965,0,63999,0, +63943,0,63882,0,63901,0,0,0,64118,0,64051,0,63826,0,63791,0, +64052,0,63871,0,0,0,64119,0,0,0,63843,0,0,0,0,0, +0,0,63979,0,0,0,0,0,0,0,64053,0,0,0,0,0, +0,0,0,0,63772,0,0,0,0,0,63851,0,0,0,0,0, +63750,0,0,0,0,0,63966,0,63981,0,63872,0,63902,0,64121,0, +63755,0,64122,0,64054,0,64013,0,64123,0,64055,0,64056,0,0,0, +0,0,63913,0,0,0,0,0,64057,0,64124,0,63852,0,64058,0, +64125,0,63818,0,63810,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,64126,0,63756,0,63753,0,64127,0,63873, +0,64128,0,64129,0,0,0,0,0,0,0,64004,0,0,0,63914, +0,63932,0,0,0,0,0,0,0,0,0,63933,0,63819,0,64059, +0,63967,0,64060,0,0,0,63957,0,63777,0,63915,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,63886,0,0,0, +0,0,64001,0,63906,0,63784,0,64130,0,64011,0,64131,0,63874,0, +0,0,0,0,63811,0,0,0,0,0,0,0,0,0,64132,0, +0,0,63960,0,63846,0,64133,0,0,0,63907,0,63840,0,63916,0, +64061,0,64134,0,63929,0,64136,0,63961,0,64135,0,64138,0,64062,0, +64063,0,63887,0,64139,0,63757,0,63888,0,0,0,63954,0,64140,0, +0,0,0,0,63781,0,63835,0,64002,0,63859,0,63908,0,63861,0, +64141,0,64142,0,64143,0,63889,0,63792,0,0,0,0,0,64065,0, +64144,0,63849,0,0,0,0,0,63934,0,0,0,0,0,63875,0, +0,0,64066,0,0,0,0,0,0,63968,0,0,0,0,64018,0, +63941,0,64067,0,64006,0,63883,0,0,0,63745,0,0,0,0,0, +64146,0,64147,0,0,0,63969,0,64148,0,63944,0,63988,0,63945,0, +63962,0,0,0,0,0,0,0,0,63866,0,64068,0,63970,0,63764, +0,63820,0,63793,0,63773,0,0,0,0,0,0,0,63884,0,64149, +0,63909,0,0,0,64150,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,63858,0,63848,0,63971,0,0,0,63765,0,64005, +0,64151,0,63786,0,64069,0,63989,0,63821,0,63958,0,64070,0,63947, +0,63980,0,64153,0,63748,0,64152,0,63822,0,64071,0,63890,0,63778, +0,63876,0,64155,0,0,0,63995,0,63903,0,63766,0,63891,0,64072, +0,63936,0,63982,0,63794,0,63774,0,0,0,64073,0,64158,0,0, +0,0,0,0,0,0,0,0,0,0,0,63814,0,0,0,0, +0,64159,0,63994,0,63787,0,64022,0,63911,0,0,0,63963,0,0, +0,63917,0,63767,0,63972,0,63948,0,64074,0,63918,0,64161,0,63892, +0,63983,0,0,0,0,0,64162,0,0,0,0,0,0,0,0, +0,0,0,0,0,64163,0,63949,0,63862,0,63842,0,0,0,0, +0,63973,0,64164,0,64165,0,63937,0,63758,0,0,0,0,0,0, +0,0,0,64166,0,64167,0,0,0,63795,0,0,0,64168,0,63853, +0,64170,0,64169,0,64157,0,0,0,0,0,0,0,63950,0,63803, +0,64075,0,63815,0,64171,0,63844,0,63877,0,0,0,64024,0,64076, +0,64078,0,64077,0,64079,0,64080,0,64081,0,64025,0,64026,0,63804, +0,64082,0,64083,0,64027,0,63926,0,0,0,0,0,63893,0,0, +0,63830,0,64084,0,0,0,64085,0,64172,0,63991,0,0,0,63992, +0,64086,0,63910,0,63812,0,0,0,64174,0,63993,0,64029,0,64003, +0,63867,0,0,0,63951,0,63850,0,63823,0,64175,0,63805,0,63831, +0,64087,0,64088,0,63824,0,64089,0,0,0,64177,0,0,0,64090, +0,63974,0,63759,0,0,0,63919,0,64030,0,63796,0,64178,0,0, +0,0,0,0,0,63920,0,63895,0,63813,0,0,0,0,0,63827, +0,63782,0,0,0,63990,0,0,0,64092,0,0,0,0,0,0, +0,0,0,0,0,0,0,63868,0,0,0,0,0,64093,0,63860, +0,63998,0,64179,0,63806,0,64180,0,63832,0,63768,0,63854,0,64095, +0,63897,0,63938,0,63779,0,63984,0,63797,0,64032,0,63775,0,63760, +0,0,0,63798,0,0,0,64181,0,63761,0,63783,0,0,0,64008, +0,0,0,63904,0,63975,0,63976,0,63762,0,64096,0,64182,0,63780, +0,0,0,64183,0,64010,0,64097,0,0,0,0,0,63905,0,64185, +0,64187,0,63869,0,63809,0,64190,0,64034,0,64189,0,64188,0,64099, +0,63996,0,63834,0,64192,0,0,0,0,0,63744,0,0,0,0, +0,0,0,0,0,63816,0,0,0,63747,0,64100,0,64101,0,0, +0,0,0,0,0,63799,0,0,0,63746,0,63896,0,63959,0,64194, +0,64007,0,63885,0,0,0,63857,0,0,0,64102,0,63898,0,64037, +0,0,0,64195,0,63939,0,63763,0,0,0,63788,0,64038,0,0, +0,63769,0,64196,0,63927,0,0,0,63977,0,63870,0,63754,0,63921, +0,64197,0,63807,0,63899,0,0,0,0,0,63878,0,0,0,63942, +0,63825,0,64009,0,63833,0,63955,0,64198,0,63964,0,63985,0,0, +0,63928,0,0,0,63978,0,64199,0,0,0,63922,0,63817,0,63800, +0,63923,0,0,0,64028,0,0,0,0,0,0,0,0,0,64201, +0,0,0,0,0,64202,0,0,0,0,0,64203,0,63924,0,64204, +0,63952,0,0,0,0,0,0,0,64042,0,64043,0,64044,0,0, +0,0,0,0,0,63770,0,63879,0,0,0,0,0,0,0,64205, +0,0,0,0,0,0,0,0,0,0,0,63801,0,63986,0,0, +0,64045,0,63802,0,63776,0,0,0,63808,0,63880,0,63987,0,0, +0,0,0,0,0,0,0,63881,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,64216,0,0,0,0,0,63940,0,64217, +0,63752,0,0,0,0,0,0,0,64300,0,0,64301,0,0,64208, +0,64207,0,64209,0,64213,0,64214,0,64215,0,0,0,0,0,0, +0}; +static const guint8 combiningClass [] = { +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230, +230,230,230,230,230,232,220,220,220,220,232,216,220,220,220,220, +220,202,202,220,220,220,220,202,202,220,220,220,220,220,220,220, +220,220,220,220,1,1,1,1,1,220,220,220,220,230,230,230, +230,230,230,230,230,240,230,220,220,220,230,230,230,220,220,0, +230,230,230,220,220,220,220,230,232,220,220,230,233,234,234,233, +0,0,0,230,230,230,230,230,0,0,0,0,0,0,0,0, +0,220,230,230,230,230,220,230,230,230,222,220,230,230,230,230, +230,230,220,220,220,220,220,220,230,230,220,230,230,222,228,230, +10,11,12,13,14,15,16,17,18,19,19,20,21,22,0,23, +0,24,25,0,230,220,0,18,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +230,230,230,230,230,230,230,230,30,31,32,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,27,28,29,30,31, +32,33,34,230,230,220,220,230,230,230,230,230,220,230,230,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +35,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,230,230,230,230,230,230,230,0,0,230, +230,230,230,220,230,0,0,230,230,0,220,230,230,220,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +230,220,230,230,220,230,230,220,220,220,230,220,220,230,220,230, +230,230,220,230,220,230,220,230,220,230,230,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0, +0,230,220,230,230,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0, +0,0,0,0,0,84,91,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0, +0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0, +0,0,0,0,0,0,0,0,103,103,9,0,0,0,0,0, +0,0,0,0,0,0,0,0,107,107,107,107,0,0,0,0, +0,0,0,0,0,0,0,0,118,118,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,122,122,122,122,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,220,220,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,220,0,220,0,216,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,129,130,0,132,0,0,0,0,0,130,130,130,130,0,0, +130,0,230,230,9,0,230,230,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,220,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,7,0,9,9,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,230, +0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0, +0,0,9,0,0,0,0,0,0,0,0,0,0,230,0,0, +0,0,0,0,0,0,0,0,0,228,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,222,230,220,0,0,0,0, +0,0,0,0,0,0,0,230,220,0,0,0,0,0,0,0, +230,230,220,230,230,230,230,230,230,230,220,230,230,234,214,220, +230,230,1,1,230,230,230,230,1,1,1,230,230,0,0,0, +0,230,0,0,0,1,1,230,220,230,1,1,220,220,220,220, +0,0,0,0,0,0,0,0,0,0,218,228,232,222,224,224, +0,0,0,0,0,0,0,0,0,8,8,0,0,0,0,0, +0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,0, +230,230,230,230,230,230,230,0,0,0,0,0,0,0,0,0, +0}; diff --git a/unity-2019.4.24f1-mbe/mono/metadata/null-gc-handles.c b/unity-2019.4.24f1-mbe/mono/metadata/null-gc-handles.c new file mode 100644 index 000000000..60e372bd6 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/null-gc-handles.c @@ -0,0 +1,461 @@ +/* Borrowed from ./boehm-gc.c */ + +/* GC Handles */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + + +#ifdef HAVE_NULL_GC + +#define HIDE_POINTER(obj) (obj) +#define REVEAL_POINTER(obj) (obj) + +#define GC_call_with_alloc_lock(fnptr,arg) ((fnptr)((arg))) + +static mono_mutex_t handle_section; +#define lock_handles(handles) mono_os_mutex_lock (&handle_section) +#define unlock_handles(handles) mono_os_mutex_unlock (&handle_section) + +typedef struct { + guint32 *bitmap; + gpointer *entries; + guint32 size; + guint8 type; + guint slot_hint : 24; /* starting slot for search in bitmap */ + /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */ + /* we alloc this only for weak refs, since we can get the domain directly in the other cases */ + guint16 *domain_ids; +} HandleData; + +#define EMPTY_HANDLE_DATA(type) {NULL, NULL, 0, (type), 0, NULL} + +/* weak and weak-track arrays will be allocated in malloc memory + */ +static HandleData gc_handles [] = { + EMPTY_HANDLE_DATA (HANDLE_WEAK), + EMPTY_HANDLE_DATA (HANDLE_WEAK_TRACK), + EMPTY_HANDLE_DATA (HANDLE_NORMAL), + EMPTY_HANDLE_DATA (HANDLE_PINNED) +}; + +#define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT) + +void +null_gc_handles_init (void) +{ + mono_os_mutex_init_recursive (&handle_section); +} + +static void +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); +} + +static void +mono_gc_weak_link_remove (void **link_addr, gboolean track) +{ + *link_addr = NULL; +} + +static gpointer +reveal_link (gpointer link_addr) +{ + void **link_a = (void **)link_addr; + return REVEAL_POINTER (*link_a); +} + +static MonoObject * +mono_gc_weak_link_get (void **link_addr) +{ + MonoObject *obj = (MonoObject *)GC_call_with_alloc_lock (reveal_link, link_addr); + if (obj == (MonoObject *) -1) + return NULL; + return obj; +} + +static inline gboolean +slot_occupied (HandleData *handles, guint slot) { + return handles->bitmap [slot / BITMAP_SIZE] & (1 << (slot % BITMAP_SIZE)); +} + +static inline void +vacate_slot (HandleData *handles, guint slot) { + handles->bitmap [slot / BITMAP_SIZE] &= ~(1 << (slot % BITMAP_SIZE)); +} + +static inline void +occupy_slot (HandleData *handles, guint slot) { + handles->bitmap [slot / BITMAP_SIZE] |= 1 << (slot % BITMAP_SIZE); +} + +static int +find_first_unset (guint32 bitmap) +{ + int i; + for (i = 0; i < 32; ++i) { + if (!(bitmap & (1 << i))) + return i; + } + return -1; +} + +static void +handle_data_alloc_entries (HandleData *handles) +{ + handles->size = 32; + if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) { + handles->entries = (void **)g_malloc0 (sizeof (*handles->entries) * handles->size); + handles->domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * handles->size); + } else { + handles->entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * handles->size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "GC Handle Table (Null)"); + } + handles->bitmap = (guint32 *)g_malloc0 (handles->size / CHAR_BIT); +} + +static gint +handle_data_next_unset (HandleData *handles) +{ + gint slot; + for (slot = handles->slot_hint; slot < handles->size / BITMAP_SIZE; ++slot) { + if (handles->bitmap [slot] == 0xffffffff) + continue; + handles->slot_hint = slot; + return find_first_unset (handles->bitmap [slot]); + } + return -1; +} + +static gint +handle_data_first_unset (HandleData *handles) +{ + gint slot; + for (slot = 0; slot < handles->slot_hint; ++slot) { + if (handles->bitmap [slot] == 0xffffffff) + continue; + handles->slot_hint = slot; + return find_first_unset (handles->bitmap [slot]); + } + return -1; +} + +/* Returns the index of the current slot in the bitmap. */ +static void +handle_data_grow (HandleData *handles, gboolean track) +{ + guint32 *new_bitmap; + guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */ + + /* resize and copy the bitmap */ + new_bitmap = (guint32 *)g_malloc0 (new_size / CHAR_BIT); + memcpy (new_bitmap, handles->bitmap, handles->size / CHAR_BIT); + g_free (handles->bitmap); + handles->bitmap = new_bitmap; + + /* resize and copy the entries */ + if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) { + gpointer *entries; + guint16 *domain_ids; + gint i; + domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * new_size); + entries = (void **)g_malloc0 (sizeof (*handles->entries) * new_size); + memcpy (domain_ids, handles->domain_ids, sizeof (*handles->domain_ids) * handles->size); + for (i = 0; i < handles->size; ++i) { + MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i])); + if (obj) { + mono_gc_weak_link_add (&(entries [i]), obj, track); + mono_gc_weak_link_remove (&(handles->entries [i]), track); + } else { + g_assert (!handles->entries [i]); + } + } + g_free (handles->entries); + g_free (handles->domain_ids); + handles->entries = entries; + handles->domain_ids = domain_ids; + } else { + gpointer *entries; + entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * new_size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "GC Handle Table (Null)"); + mono_gc_memmove_aligned (entries, handles->entries, sizeof (*handles->entries) * handles->size); + mono_gc_free_fixed (handles->entries); + handles->entries = entries; + } + handles->slot_hint = handles->size / BITMAP_SIZE; + handles->size = new_size; +} + +static guint32 +alloc_handle (HandleData *handles, MonoObject *obj, gboolean track) +{ + gint slot, i; + guint32 res; + lock_handles (handles); + if (!handles->size) + handle_data_alloc_entries (handles); + i = handle_data_next_unset (handles); + if (i == -1 && handles->slot_hint != 0) + i = handle_data_first_unset (handles); + if (i == -1) { + handle_data_grow (handles, track); + i = 0; + } + slot = handles->slot_hint * BITMAP_SIZE + i; + occupy_slot (handles, slot); + handles->entries [slot] = NULL; + if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) { + /*FIXME, what to use when obj == null?*/ + handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id; + if (obj) + mono_gc_weak_link_add (&(handles->entries [slot]), obj, track); + } else { + handles->entries [slot] = obj; + } + +#ifndef DISABLE_PERFCOUNTERS + mono_atomic_inc_i32 (&mono_perfcounters->gc_num_handles); +#endif + unlock_handles (handles); + res = MONO_GC_HANDLE (slot, handles->type); + mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj); + return res; +} + +/** + * mono_gchandle_new: + * \param obj managed object to get a handle for + * \param pinned whether the object should be pinned + * + * This returns a handle that wraps the object, this is used to keep a + * reference to a managed object from the unmanaged world and preventing the + * object from being disposed. + * + * If \p pinned is false the address of the object can not be obtained, if it is + * true the address of the object can be obtained. This will also pin the + * object so it will not be possible by a moving garbage collector to move the + * object. + * + * \returns a handle that can be used to access the object from + * unmanaged code. + */ +guint32 +mono_gchandle_new (MonoObject *obj, gboolean pinned) +{ + return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE); +} + +/** + * mono_gchandle_new_weakref: + * \param obj managed object to get a handle for + * \param track_resurrection Determines how long to track the object, if this is set to TRUE, the object is tracked after finalization, if FALSE, the object is only tracked up until the point of finalization. + * + * This returns a weak handle that wraps the object, this is used to + * keep a reference to a managed object from the unmanaged world. + * Unlike the \c mono_gchandle_new the object can be reclaimed by the + * garbage collector. In this case the value of the GCHandle will be + * set to zero. + * + * If \p track_resurrection is TRUE the object will be tracked through + * finalization and if the object is resurrected during the execution + * of the finalizer, then the returned weakref will continue to hold + * a reference to the object. If \p track_resurrection is FALSE, then + * the weak reference's target will become NULL as soon as the object + * is passed on to the finalizer. + * + * \returns a handle that can be used to access the object from + * unmanaged code. + */ +guint32 +mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection) +{ + return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection); +} + +/** + * mono_gchandle_get_target: + * \param gchandle a GCHandle's handle. + * + * The handle was previously created by calling \c mono_gchandle_new or + * \c mono_gchandle_new_weakref. + * + * \returns A pointer to the \c MonoObject* represented by the handle or + * NULL for a collected object if using a weakref handle. + */ +MonoObject* +mono_gchandle_get_target (guint32 gchandle) +{ + guint slot = MONO_GC_HANDLE_SLOT (gchandle); + guint type = MONO_GC_HANDLE_TYPE (gchandle); + HandleData *handles = &gc_handles [type]; + MonoObject *obj = NULL; + if (type >= HANDLE_TYPE_MAX) + return NULL; + + lock_handles (handles); + if (slot < handles->size && slot_occupied (handles, slot)) { + if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) { + obj = mono_gc_weak_link_get (&handles->entries [slot]); + } else { + obj = (MonoObject *)handles->entries [slot]; + } + } else { + /* print a warning? */ + } + unlock_handles (handles); + /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/ + return obj; +} + +void +mono_gchandle_set_target (guint32 gchandle, MonoObject *obj) +{ + guint slot = MONO_GC_HANDLE_SLOT (gchandle); + guint type = MONO_GC_HANDLE_TYPE (gchandle); + HandleData *handles = &gc_handles [type]; + MonoObject *old_obj = NULL; + + g_assert (type < HANDLE_TYPE_MAX); + lock_handles (handles); + if (slot < handles->size && slot_occupied (handles, slot)) { + if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) { + old_obj = (MonoObject *)handles->entries [slot]; + if (handles->entries [slot]) + mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK); + if (obj) + mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK); + /*FIXME, what to use when obj == null?*/ + handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id; + } else { + handles->entries [slot] = obj; + } + } else { + /* print a warning? */ + } + /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/ + unlock_handles (handles); +} + +/** + * mono_gchandle_is_in_domain: + * \param gchandle a GCHandle's handle. + * \param domain An application domain. + * + * Use this function to determine if the \p gchandle points to an + * object allocated in the specified \p domain. + * + * \returns TRUE if the object wrapped by the \p gchandle belongs to the specific \p domain. + */ +gboolean +mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain) +{ + guint slot = MONO_GC_HANDLE_SLOT (gchandle); + guint type = MONO_GC_HANDLE_TYPE (gchandle); + HandleData *handles = &gc_handles [type]; + gboolean result = FALSE; + + if (type >= HANDLE_TYPE_MAX) + return FALSE; + + lock_handles (handles); + if (slot < handles->size && slot_occupied (handles, slot)) { + if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) { + result = domain->domain_id == handles->domain_ids [slot]; + } else { + MonoObject *obj; + obj = (MonoObject *)handles->entries [slot]; + if (obj == NULL) + result = TRUE; + else + result = domain == mono_object_domain (obj); + } + } else { + /* print a warning? */ + } + unlock_handles (handles); + return result; +} + +/** + * mono_gchandle_free: + * \param gchandle a GCHandle's handle. + * + * Frees the \p gchandle handle. If there are no outstanding + * references, the garbage collector can reclaim the memory of the + * object wrapped. + */ +void +mono_gchandle_free (guint32 gchandle) +{ + guint slot = MONO_GC_HANDLE_SLOT (gchandle); + guint type = MONO_GC_HANDLE_TYPE (gchandle); + HandleData *handles = &gc_handles [type]; + if (type >= HANDLE_TYPE_MAX) + return; + + lock_handles (handles); + if (slot < handles->size && slot_occupied (handles, slot)) { + if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) { + if (handles->entries [slot]) + mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK); + } else { + handles->entries [slot] = NULL; + } + vacate_slot (handles, slot); + } else { + /* print a warning? */ + } +#ifndef DISABLE_PERFCOUNTERS + mono_atomic_dec_i32 (&mono_perfcounters->gc_num_handles); +#endif + /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/ + unlock_handles (handles); + mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handles->type, gchandle, NULL); +} + +/** + * mono_gchandle_free_domain: + * \param domain domain that is unloading + * + * Function used internally to cleanup any GC handle for objects belonging + * to the specified domain during appdomain unload. + */ +void +mono_gchandle_free_domain (MonoDomain *domain) +{ + guint type; + + for (type = HANDLE_TYPE_MIN; type < HANDLE_PINNED; ++type) { + guint slot; + HandleData *handles = &gc_handles [type]; + lock_handles (handles); + for (slot = 0; slot < handles->size; ++slot) { + if (!slot_occupied (handles, slot)) + continue; + if (MONO_GC_HANDLE_TYPE_IS_WEAK (type)) { + if (domain->domain_id == handles->domain_ids [slot]) { + vacate_slot (handles, slot); + if (handles->entries [slot]) + mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK); + } + } else { + if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) { + vacate_slot (handles, slot); + handles->entries [slot] = NULL; + } + } + } + unlock_handles (handles); + } + +} +#else + +MONO_EMPTY_SOURCE_FILE (null_gc_handles); +#endif /* HAVE_NULL_GC */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/null-gc-handles.h b/unity-2019.4.24f1-mbe/mono/metadata/null-gc-handles.h new file mode 100644 index 000000000..c57cda40f --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/null-gc-handles.h @@ -0,0 +1,7 @@ +#ifndef __METADATA_NULL_GC_HANDLES_H__ +#define __METADATA_NULL_GC_HANDLES_H__ + +void +null_gc_handles_init (void); + +#endif /* __METADATA_NULL_GC_HANDLES_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/null-gc.c b/unity-2019.4.24f1-mbe/mono/metadata/null-gc.c new file mode 100644 index 000000000..b95ffa4e2 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/null-gc.c @@ -0,0 +1,596 @@ +/** + * \file + * GC implementation using malloc: will leak everything, just for testing. + * + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2011 Novell, Inc (http://www.novell.com) + * Copyright 2011 Xamarin, Inc (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_NULL_GC + +static gboolean gc_inited = FALSE; + +void +mono_gc_base_init (void) +{ + if (gc_inited) + return; + + mono_counters_init (); + +#ifndef HOST_WIN32 + mono_w32handle_init (); +#endif + + mono_thread_callbacks_init (); + mono_thread_info_init (sizeof (MonoThreadInfo)); + + mono_thread_info_attach (); + + null_gc_handles_init (); + + gc_inited = TRUE; +} + +void +mono_gc_base_cleanup (void) +{ +} + +void +mono_gc_collect (int generation) +{ +} + +int +mono_gc_max_generation (void) +{ + return 0; +} + +int +mono_gc_get_generation (MonoObject *object) +{ + return 0; +} + +int +mono_gc_collection_count (int generation) +{ + return 0; +} + +void +mono_gc_add_memory_pressure (gint64 value) +{ +} + +/* maybe track the size, not important, though */ +int64_t +mono_gc_get_used_size (void) +{ + return 1024*1024; +} + +int64_t +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) +{ + return TRUE; +} + +int +mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data) +{ + return 1; +} + +gboolean +mono_object_is_alive (MonoObject* o) +{ + return TRUE; +} + +int +mono_gc_register_root (char *start, size_t size, void *descr, MonoGCRootSource source, void *key, const char *msg) +{ + return TRUE; +} + +int +mono_gc_register_root_wbarrier (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, void *key, const char *msg) +{ + return TRUE; +} + + +void +mono_gc_deregister_root (char* addr) +{ +} + +void* +mono_gc_make_descr_for_string (gsize *bitmap, int numbits) +{ + return NULL; +} + +void* +mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size) +{ + return NULL; +} + +void* +mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size) +{ + return NULL; +} + +void* +mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits) +{ + return NULL; +} + +void* +mono_gc_make_vector_descr (void) +{ + return NULL; +} + +void* +mono_gc_make_root_descr_all_refs (int numbits) +{ + return NULL; +} + +void* +mono_gc_alloc_fixed (size_t size, void *descr, MonoGCRootSource source, void *key, const char *msg) +{ + return g_malloc0 (size); +} + +void +mono_gc_free_fixed (void* addr) +{ + g_free (addr); +} + +void * +mono_gc_alloc_obj (MonoVTable *vtable, size_t size) +{ + MonoObject *obj = g_calloc (1, size); + + obj->vtable = vtable; + + return obj; +} + +void * +mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length) +{ + MonoArray *obj = g_calloc (1, size); + + obj->obj.vtable = vtable; + obj->max_length = max_length; + + return obj; +} + +void * +mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size) +{ + MonoArray *obj = g_calloc (1, size); + + obj->obj.vtable = vtable; + obj->max_length = max_length; + + if (bounds_size) + obj->bounds = (MonoArrayBounds *) ((char *) obj + size - bounds_size); + + return obj; +} + +void * +mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len) +{ + MonoString *obj = g_calloc (1, size); + + obj->object.vtable = vtable; + obj->length = len; + obj->chars [len] = 0; + + return obj; +} + +void* +mono_gc_alloc_mature (MonoVTable *vtable, size_t size) +{ + return mono_gc_alloc_obj (vtable, size); +} + +void* +mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size) +{ + return mono_gc_alloc_obj (vtable, size); +} + +void +mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value) +{ + *(void**)field_ptr = value; +} + +void +mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value) +{ + *(void**)slot_ptr = value; +} + +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)); +} + +void +mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value) +{ + *(void**)ptr = value; +} + +void +mono_gc_wbarrier_generic_store_atomic (gpointer ptr, MonoObject *value) +{ + mono_atomic_store_ptr (ptr, value); +} + +void +mono_gc_wbarrier_generic_nostore (gpointer 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)); +} + +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)); +} + +gboolean +mono_gc_is_critical_method (MonoMethod *method) +{ + return FALSE; +} + +gpointer +mono_gc_thread_attach (MonoThreadInfo* info) +{ + info->handle_stack = mono_handle_stack_alloc (); + return info; +} + +void +mono_gc_thread_detach (MonoThreadInfo *p) +{ +} + +void +mono_gc_thread_detach_with_lock (MonoThreadInfo *p) +{ + mono_handle_stack_free (p->handle_stack); +} + +gboolean +mono_gc_thread_in_critical_region (MonoThreadInfo *info) +{ + return FALSE; +} + +int +mono_gc_get_aligned_size_for_allocator (int size) +{ + return size; +} + +MonoMethod* +mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size) +{ + return NULL; +} + +MonoMethod* +mono_gc_get_managed_array_allocator (MonoClass *klass) +{ + return NULL; +} + +MonoMethod* +mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant) +{ + return NULL; +} + +guint32 +mono_gc_get_managed_allocator_types (void) +{ + return 0; +} + +const char * +mono_gc_get_gc_name (void) +{ + return "null"; +} + +void +mono_gc_clear_domain (MonoDomain *domain) +{ +} + +void +mono_gc_suspend_finalizers (void) +{ +} + +int +mono_gc_get_suspend_signal (void) +{ + return -1; +} + +int +mono_gc_get_restart_signal (void) +{ + return -1; +} + +MonoMethod* +mono_gc_get_specific_write_barrier (gboolean is_concurrent) +{ + g_assert_not_reached (); + return NULL; +} + +MonoMethod* +mono_gc_get_write_barrier (void) +{ + g_assert_not_reached (); + return NULL; +} + +void* +mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data) +{ + return func (data); +} + +char* +mono_gc_get_description (void) +{ + return g_strdup (DEFAULT_GC_NAME); +} + +void +mono_gc_set_desktop_mode (void) +{ +} + +gboolean +mono_gc_is_moving (void) +{ + return FALSE; +} + +gboolean +mono_gc_needs_write_barriers(void) +{ + return FALSE; +} + +gboolean +mono_gc_is_disabled (void) +{ + return FALSE; +} + +void +mono_gc_wbarrier_range_copy (gpointer _dest, gpointer _src, int size) +{ + g_assert_not_reached (); +} + +void* +mono_gc_get_range_copy_func (void) +{ + return &mono_gc_wbarrier_range_copy; +} + +guint8* +mono_gc_get_card_table (int *shift_bits, gpointer *card_mask) +{ + g_assert_not_reached (); + return NULL; +} + +gboolean +mono_gc_card_table_nursery_check (void) +{ + g_assert_not_reached (); + return TRUE; +} + +void* +mono_gc_get_nursery (int *shift_bits, size_t *size) +{ + return NULL; +} + +gboolean +mono_gc_precise_stack_mark_enabled (void) +{ + return FALSE; +} + +FILE * +mono_gc_get_logfile (void) +{ + return NULL; +} + +void +mono_gc_conservatively_scan_area (void *start, void *end) +{ + g_assert_not_reached (); +} + +void * +mono_gc_scan_object (void *obj, void *gc_data) +{ + g_assert_not_reached (); + return NULL; +} + +gsize* +mono_gc_get_bitmap_for_descr (void *descr, int *numbits) +{ + g_assert_not_reached (); + return NULL; +} + +void +mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks) +{ +} + +void +mono_gc_set_stack_end (void *stack_end) +{ +} + +int +mono_gc_get_los_limit (void) +{ + return G_MAXINT; +} + +gboolean +mono_gc_user_markers_supported (void) +{ + return FALSE; +} + +void * +mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker) +{ + g_assert_not_reached (); + return NULL; +} + +#ifndef HOST_WIN32 +int +mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) +{ + return pthread_create (new_thread, attr, start_routine, arg); +} +#endif + +void mono_gc_set_skip_thread (gboolean value) +{ +} + +#ifdef HOST_WIN32 +BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved) +{ + return TRUE; +} +#endif + +MonoVTable * +mono_gc_get_vtable (MonoObject *obj) +{ + // No pointer tagging. + return obj->vtable; +} + +guint +mono_gc_get_vtable_bits (MonoClass *klass) +{ + return 0; +} + +void +mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size) +{ +} + +gboolean +mono_gc_is_null (void) +{ + return TRUE; +} + +int +mono_gc_invoke_finalizers (void) +{ + return 0; +} + +MonoBoolean +mono_gc_pending_finalizers (void) +{ + return FALSE; +} + +#else + +MONO_EMPTY_SOURCE_FILE (null_gc); +#endif /* HAVE_NULL_GC */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/number-formatter.h b/unity-2019.4.24f1-mbe/mono/metadata/number-formatter.h new file mode 100644 index 000000000..e1e4eb2d8 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/number-formatter.h @@ -0,0 +1,913 @@ +/** + * \file + */ + +#ifndef _MONO_METADATA_NUMBER_FORMATTER_H_ +#define _MONO_METADATA_NUMBER_FORMATTER_H_ 1 + +static const guint64 Formatter_MantissaBitsTable [] = { + 4556951262222748432ULL, 9113902524445496865ULL, 1822780504889099373ULL, + 3645561009778198746ULL, 7291122019556397492ULL, 14582244039112794984ULL, + 2916448807822558996ULL, 5832897615645117993ULL, 11665795231290235987ULL, + 2333159046258047197ULL, 4666318092516094394ULL, 9332636185032188789ULL, + 1866527237006437757ULL, 3733054474012875515ULL, 7466108948025751031ULL, + 14932217896051502063ULL, 2986443579210300412ULL, 5972887158420600825ULL, + 11945774316841201651ULL, 2389154863368240330ULL, 4778309726736480660ULL, + 9556619453472961320ULL, 1911323890694592264ULL, 3822647781389184528ULL, + 7645295562778369056ULL, 15290591125556738113ULL, 3058118225111347622ULL, + 6116236450222695245ULL, 12232472900445390490ULL, 2446494580089078098ULL, + 4892989160178156196ULL, 9785978320356312392ULL, 1957195664071262478ULL, + 3914391328142524957ULL, 7828782656285049914ULL, 15657565312570099828ULL, + 3131513062514019965ULL, 6263026125028039931ULL, 12526052250056079862ULL, + 2505210450011215972ULL, 5010420900022431944ULL, 10020841800044863889ULL, + 2004168360008972777ULL, 4008336720017945555ULL, 8016673440035891111ULL, + 16033346880071782223ULL, 3206669376014356444ULL, 6413338752028712889ULL, + 12826677504057425779ULL, 2565335500811485155ULL, 5130671001622970311ULL, + 10261342003245940623ULL, 2052268400649188124ULL, 4104536801298376249ULL, + 8209073602596752498ULL, 16418147205193504997ULL, 3283629441038700999ULL, + 6567258882077401998ULL, 13134517764154803997ULL, 2626903552830960799ULL, + 5253807105661921599ULL, 10507614211323843198ULL, 2101522842264768639ULL, + 4203045684529537279ULL, 8406091369059074558ULL, 16812182738118149117ULL, + 3362436547623629823ULL, 6724873095247259646ULL, 13449746190494519293ULL, + 2689949238098903858ULL, 5379898476197807717ULL, 10759796952395615435ULL, + 2151959390479123087ULL, 4303918780958246174ULL, 8607837561916492348ULL, + 17215675123832984696ULL, 3443135024766596939ULL, 6886270049533193878ULL, + 13772540099066387756ULL, 2754508019813277551ULL, 5509016039626555102ULL, + 11018032079253110205ULL, 2203606415850622041ULL, 4407212831701244082ULL, + 8814425663402488164ULL, 17628851326804976328ULL, 3525770265360995265ULL, + 7051540530721990531ULL, 14103081061443981063ULL, 2820616212288796212ULL, + 5641232424577592425ULL, 11282464849155184850ULL, 2256492969831036970ULL, + 4512985939662073940ULL, 9025971879324147880ULL, 18051943758648295760ULL, + 3610388751729659152ULL, 7220777503459318304ULL, 14441555006918636608ULL, + 2888311001383727321ULL, 5776622002767454643ULL, 11553244005534909286ULL, + 2310648801106981857ULL, 4621297602213963714ULL, 9242595204427927429ULL, + 1848519040885585485ULL, 3697038081771170971ULL, 7394076163542341943ULL, + 14788152327084683887ULL, 2957630465416936777ULL, 5915260930833873554ULL, + 11830521861667747109ULL, 2366104372333549421ULL, 4732208744667098843ULL, + 9464417489334197687ULL, 1892883497866839537ULL, 3785766995733679075ULL, + 7571533991467358150ULL, 15143067982934716300ULL, 3028613596586943260ULL, + 6057227193173886520ULL, 12114454386347773040ULL, 2422890877269554608ULL, + 4845781754539109216ULL, 9691563509078218432ULL, 1938312701815643686ULL, + 3876625403631287372ULL, 7753250807262574745ULL, 15506501614525149491ULL, + 3101300322905029898ULL, 6202600645810059796ULL, 12405201291620119593ULL, + 2481040258324023918ULL, 4962080516648047837ULL, 9924161033296095674ULL, + 1984832206659219134ULL, 3969664413318438269ULL, 7939328826636876539ULL, + 15878657653273753079ULL, 3175731530654750615ULL, 6351463061309501231ULL, + 12702926122619002463ULL, 2540585224523800492ULL, 5081170449047600985ULL, + 10162340898095201970ULL, 2032468179619040394ULL, 4064936359238080788ULL, + 8129872718476161576ULL, 16259745436952323153ULL, 3251949087390464630ULL, + 6503898174780929261ULL, 13007796349561858522ULL, 2601559269912371704ULL, + 5203118539824743409ULL, 10406237079649486818ULL, 2081247415929897363ULL, + 4162494831859794727ULL, 8324989663719589454ULL, 16649979327439178909ULL, + 3329995865487835781ULL, 6659991730975671563ULL, 13319983461951343127ULL, + 2663996692390268625ULL, 5327993384780537250ULL, 10655986769561074501ULL, + 2131197353912214900ULL, 4262394707824429800ULL, 8524789415648859601ULL, + 17049578831297719202ULL, 3409915766259543840ULL, 6819831532519087681ULL, + 13639663065038175362ULL, 2727932613007635072ULL, 5455865226015270144ULL, + 10911730452030540289ULL, 2182346090406108057ULL, 4364692180812216115ULL, + 8729384361624432231ULL, 17458768723248864463ULL, 3491753744649772892ULL, + 6983507489299545785ULL, 13967014978599091570ULL, 2793402995719818314ULL, + 5586805991439636628ULL, 11173611982879273256ULL, 2234722396575854651ULL, + 4469444793151709302ULL, 8938889586303418605ULL, 17877779172606837210ULL, + 3575555834521367442ULL, 7151111669042734884ULL, 14302223338085469768ULL, + 2860444667617093953ULL, 5720889335234187907ULL, 11441778670468375814ULL, + 2288355734093675162ULL, 4576711468187350325ULL, 9153422936374700651ULL, + 1830684587274940130ULL, 3661369174549880260ULL, 7322738349099760521ULL, + 14645476698199521043ULL, 2929095339639904208ULL, 5858190679279808417ULL, + 11716381358559616834ULL, 2343276271711923366ULL, 4686552543423846733ULL, + 9373105086847693467ULL, 1874621017369538693ULL, 3749242034739077387ULL, + 7498484069478154774ULL, 14996968138956309548ULL, 2999393627791261909ULL, + 5998787255582523819ULL, 11997574511165047638ULL, 2399514902233009527ULL, + 4799029804466019055ULL, 9598059608932038110ULL, 1919611921786407622ULL, + 3839223843572815244ULL, 7678447687145630488ULL, 15356895374291260977ULL, + 3071379074858252195ULL, 6142758149716504390ULL, 12285516299433008781ULL, + 2457103259886601756ULL, 4914206519773203512ULL, 9828413039546407025ULL, + 1965682607909281405ULL, 3931365215818562810ULL, 7862730431637125620ULL, + 15725460863274251240ULL, 3145092172654850248ULL, 6290184345309700496ULL, + 12580368690619400992ULL, 2516073738123880198ULL, 5032147476247760397ULL, + 10064294952495520794ULL, 2012858990499104158ULL, 4025717980998208317ULL, + 8051435961996416635ULL, 16102871923992833270ULL, 3220574384798566654ULL, + 6441148769597133308ULL, 12882297539194266616ULL, 2576459507838853323ULL, + 5152919015677706646ULL, 10305838031355413293ULL, 2061167606271082658ULL, + 4122335212542165317ULL, 8244670425084330634ULL, 16489340850168661269ULL, + 3297868170033732253ULL, 6595736340067464507ULL, 13191472680134929015ULL, + 2638294536026985803ULL, 5276589072053971606ULL, 10553178144107943212ULL, + 2110635628821588642ULL, 4221271257643177284ULL, 8442542515286354569ULL, + 16885085030572709139ULL, 3377017006114541827ULL, 6754034012229083655ULL, + 13508068024458167311ULL, 2701613604891633462ULL, 5403227209783266924ULL, + 10806454419566533849ULL, 2161290883913306769ULL, 4322581767826613539ULL, + 8645163535653227079ULL, 17290327071306454158ULL, 3458065414261290831ULL, + 6916130828522581663ULL, 13832261657045163327ULL, 2766452331409032665ULL, + 5532904662818065330ULL, 11065809325636130661ULL, 2213161865127226132ULL, + 4426323730254452264ULL, 8852647460508904529ULL, 17705294921017809058ULL, + 3541058984203561811ULL, 7082117968407123623ULL, 14164235936814247246ULL, + 2832847187362849449ULL, 5665694374725698898ULL, 11331388749451397797ULL, + 2266277749890279559ULL, 4532555499780559119ULL, 9065110999561118238ULL, + 1813022199912223647ULL, 3626044399824447295ULL, 7252088799648894590ULL, + 14504177599297789180ULL, 2900835519859557836ULL, 5801671039719115672ULL, + 11603342079438231344ULL, 2320668415887646268ULL, 4641336831775292537ULL, + 9282673663550585075ULL, 1856534732710117015ULL, 3713069465420234030ULL, + 7426138930840468060ULL, 14852277861680936121ULL, 2970455572336187224ULL, + 5940911144672374448ULL, 11881822289344748896ULL, 2376364457868949779ULL, + 4752728915737899558ULL, 9505457831475799117ULL, 1901091566295159823ULL, + 3802183132590319647ULL, 7604366265180639294ULL, 15208732530361278588ULL, + 3041746506072255717ULL, 6083493012144511435ULL, 12166986024289022870ULL, + 2433397204857804574ULL, 4866794409715609148ULL, 9733588819431218296ULL, + 1946717763886243659ULL, 3893435527772487318ULL, 7786871055544974637ULL, + 15573742111089949274ULL, 3114748422217989854ULL, 6229496844435979709ULL, + 12458993688871959419ULL, 2491798737774391883ULL, 4983597475548783767ULL, + 9967194951097567535ULL, 1993438990219513507ULL, 3986877980439027014ULL, + 7973755960878054028ULL, 15947511921756108056ULL, 3189502384351221611ULL, + 6379004768702443222ULL, 12758009537404886445ULL, 2551601907480977289ULL, + 5103203814961954578ULL, 10206407629923909156ULL, 2041281525984781831ULL, + 4082563051969563662ULL, 8165126103939127325ULL, 16330252207878254650ULL, + 3266050441575650930ULL, 6532100883151301860ULL, 13064201766302603720ULL, + 2612840353260520744ULL, 5225680706521041488ULL, 10451361413042082976ULL, + 2090272282608416595ULL, 4180544565216833190ULL, 8361089130433666380ULL, + 16722178260867332761ULL, 3344435652173466552ULL, 6688871304346933104ULL, + 13377742608693866209ULL, 2675548521738773241ULL, 5351097043477546483ULL, + 10702194086955092967ULL, 2140438817391018593ULL, 4280877634782037187ULL, + 8561755269564074374ULL, 17123510539128148748ULL, 3424702107825629749ULL, + 6849404215651259499ULL, 13698808431302518998ULL, 2739761686260503799ULL, + 5479523372521007599ULL, 10959046745042015198ULL, 2191809349008403039ULL, + 4383618698016806079ULL, 8767237396033612159ULL, 17534474792067224318ULL, + 3506894958413444863ULL, 7013789916826889727ULL, 14027579833653779454ULL, + 2805515966730755890ULL, 5611031933461511781ULL, 11222063866923023563ULL, + 2244412773384604712ULL, 4488825546769209425ULL, 8977651093538418850ULL, + 17955302187076837701ULL, 3591060437415367540ULL, 7182120874830735080ULL, + 14364241749661470161ULL, 2872848349932294032ULL, 5745696699864588064ULL, + 11491393399729176129ULL, 2298278679945835225ULL, 4596557359891670451ULL, + 9193114719783340903ULL, 1838622943956668180ULL, 3677245887913336361ULL, + 7354491775826672722ULL, 14708983551653345445ULL, 2941796710330669089ULL, + 5883593420661338178ULL, 11767186841322676356ULL, 2353437368264535271ULL, + 4706874736529070542ULL, 9413749473058141084ULL, 1882749894611628216ULL, + 3765499789223256433ULL, 7530999578446512867ULL, 15061999156893025735ULL, + 3012399831378605147ULL, 6024799662757210294ULL, 12049599325514420588ULL, + 2409919865102884117ULL, 4819839730205768235ULL, 9639679460411536470ULL, + 1927935892082307294ULL, 3855871784164614588ULL, 7711743568329229176ULL, + 15423487136658458353ULL, 3084697427331691670ULL, 6169394854663383341ULL, + 12338789709326766682ULL, 2467757941865353336ULL, 4935515883730706673ULL, + 9871031767461413346ULL, 1974206353492282669ULL, 3948412706984565338ULL, + 7896825413969130677ULL, 15793650827938261354ULL, 3158730165587652270ULL, + 6317460331175304541ULL, 12634920662350609083ULL, 2526984132470121816ULL, + 5053968264940243633ULL, 10107936529880487266ULL, 2021587305976097453ULL, + 4043174611952194906ULL, 8086349223904389813ULL, 16172698447808779626ULL, + 3234539689561755925ULL, 6469079379123511850ULL, 12938158758247023701ULL, + 2587631751649404740ULL, 5175263503298809480ULL, 10350527006597618960ULL, + 2070105401319523792ULL, 4140210802639047584ULL, 8280421605278095168ULL, + 16560843210556190337ULL, 3312168642111238067ULL, 6624337284222476135ULL, + 13248674568444952270ULL, 2649734913688990454ULL, 5299469827377980908ULL, + 10598939654755961816ULL, 2119787930951192363ULL, 4239575861902384726ULL, + 8479151723804769452ULL, 16958303447609538905ULL, 3391660689521907781ULL, + 6783321379043815562ULL, 13566642758087631124ULL, 2713328551617526224ULL, + 5426657103235052449ULL, 10853314206470104899ULL, 2170662841294020979ULL, + 4341325682588041959ULL, 8682651365176083919ULL, 17365302730352167839ULL, + 3473060546070433567ULL, 6946121092140867135ULL, 13892242184281734271ULL, + 2778448436856346854ULL, 5556896873712693708ULL, 11113793747425387417ULL, + 2222758749485077483ULL, 4445517498970154966ULL, 8891034997940309933ULL, + 17782069995880619867ULL, 3556413999176123973ULL, 7112827998352247947ULL, + 14225655996704495894ULL, 2845131199340899178ULL, 5690262398681798357ULL, + 11380524797363596715ULL, 2276104959472719343ULL, 4552209918945438686ULL, + 9104419837890877372ULL, 1820883967578175474ULL, 3641767935156350948ULL, + 7283535870312701897ULL, 14567071740625403795ULL, 2913414348125080759ULL, + 5826828696250161518ULL, 11653657392500323036ULL, 2330731478500064607ULL, + 4661462957000129214ULL, 9322925914000258429ULL, 1864585182800051685ULL, + 3729170365600103371ULL, 7458340731200206743ULL, 14916681462400413486ULL, + 2983336292480082697ULL, 5966672584960165394ULL, 11933345169920330789ULL, + 2386669033984066157ULL, 4773338067968132315ULL, 9546676135936264631ULL, + 1909335227187252926ULL, 3818670454374505852ULL, 7637340908749011705ULL, + 15274681817498023410ULL, 3054936363499604682ULL, 6109872726999209364ULL, + 12219745453998418728ULL, 2443949090799683745ULL, 4887898181599367491ULL, + 9775796363198734982ULL, 1955159272639746996ULL, 3910318545279493993ULL, + 7820637090558987986ULL, 15641274181117975972ULL, 3128254836223595194ULL, + 6256509672447190388ULL, 12513019344894380777ULL, 2502603868978876155ULL, + 5005207737957752311ULL, 10010415475915504622ULL, 2002083095183100924ULL, + 4004166190366201848ULL, 8008332380732403697ULL, 16016664761464807395ULL, + 3203332952292961479ULL, 6406665904585922958ULL, 12813331809171845916ULL, + 2562666361834369183ULL, 5125332723668738366ULL, 10250665447337476733ULL, + 2050133089467495346ULL, 4100266178934990693ULL, 8200532357869981386ULL, + 16401064715739962772ULL, 3280212943147992554ULL, 6560425886295985109ULL, + 13120851772591970218ULL, 2624170354518394043ULL, 5248340709036788087ULL, + 10496681418073576174ULL, 2099336283614715234ULL, 4198672567229430469ULL, + 8397345134458860939ULL, 16794690268917721879ULL, 3358938053783544375ULL, + 6717876107567088751ULL, 13435752215134177503ULL, 2687150443026835500ULL, + 5374300886053671001ULL, 10748601772107342002ULL, 2149720354421468400ULL, + 4299440708842936801ULL, 8598881417685873602ULL, 17197762835371747204ULL, + 3439552567074349440ULL, 6879105134148698881ULL, 13758210268297397763ULL, + 2751642053659479552ULL, 5503284107318959105ULL, 11006568214637918210ULL, + 2201313642927583642ULL, 4402627285855167284ULL, 8805254571710334568ULL, + 17610509143420669137ULL, 3522101828684133827ULL, 7044203657368267654ULL, + 14088407314736535309ULL, 2817681462947307061ULL, 5635362925894614123ULL, + 11270725851789228247ULL, 2254145170357845649ULL, 4508290340715691299ULL, + 9016580681431382598ULL, 18033161362862765196ULL, 3606632272572553039ULL, + 7213264545145106078ULL, 14426529090290212157ULL, 2885305818058042431ULL, + 5770611636116084862ULL, 11541223272232169725ULL, 2308244654446433945ULL, + 4616489308892867890ULL, 9232978617785735780ULL, 1846595723557147156ULL, + 3693191447114294312ULL, 7386382894228588624ULL, 14772765788457177249ULL, + 2954553157691435449ULL, 5909106315382870899ULL, 11818212630765741799ULL, + 2363642526153148359ULL, 4727285052306296719ULL, 9454570104612593439ULL, + 1890914020922518687ULL, 3781828041845037375ULL, 7563656083690074751ULL, + 15127312167380149503ULL, 3025462433476029900ULL, 6050924866952059801ULL, + 12101849733904119602ULL, 2420369946780823920ULL, 4840739893561647841ULL, + 9681479787123295682ULL, 1936295957424659136ULL, 3872591914849318272ULL, + 7745183829698636545ULL, 15490367659397273091ULL, 3098073531879454618ULL, + 6196147063758909236ULL, 12392294127517818473ULL, 2478458825503563694ULL, + 4956917651007127389ULL, 9913835302014254778ULL, 1982767060402850955ULL, + 3965534120805701911ULL, 7931068241611403822ULL, 15862136483222807645ULL, + 3172427296644561529ULL, 6344854593289123058ULL, 12689709186578246116ULL, + 2537941837315649223ULL, 5075883674631298446ULL, 10151767349262596893ULL, + 2030353469852519378ULL, 4060706939705038757ULL, 8121413879410077514ULL, + 16242827758820155028ULL, 3248565551764031005ULL, 6497131103528062011ULL, + 12994262207056124023ULL, 2598852441411224804ULL, 5197704882822449609ULL, + 10395409765644899218ULL, 2079081953128979843ULL, 4158163906257959687ULL, + 8316327812515919374ULL, 16632655625031838749ULL, 3326531125006367749ULL, + 6653062250012735499ULL, 13306124500025470999ULL, 2661224900005094199ULL, + 5322449800010188399ULL, 10644899600020376799ULL, 2128979920004075359ULL, + 4257959840008150719ULL, 8515919680016301439ULL, 17031839360032602879ULL, + 3406367872006520575ULL, 6812735744013041151ULL, 13625471488026082303ULL, + 2725094297605216460ULL, 5450188595210432921ULL, 10900377190420865842ULL, + 2180075438084173168ULL, 4360150876168346337ULL, 8720301752336692674ULL, + 17440603504673385348ULL, 3488120700934677069ULL, 6976241401869354139ULL, + 13952482803738708279ULL, 2790496560747741655ULL, 5580993121495483311ULL, + 11161986242990966623ULL, 2232397248598193324ULL, 4464794497196386649ULL, + 8929588994392773298ULL, 17859177988785546597ULL, 3571835597757109319ULL, + 7143671195514218638ULL, 14287342391028437277ULL, 2857468478205687455ULL, + 5714936956411374911ULL, 11429873912822749822ULL, 2285974782564549964ULL, + 4571949565129099928ULL, 9143899130258199857ULL, 1828779826051639971ULL, + 3657559652103279943ULL, 7315119304206559886ULL, 14630238608413119772ULL, + 2926047721682623954ULL, 5852095443365247908ULL, 11704190886730495817ULL, + 2340838177346099163ULL, 4681676354692198327ULL, 9363352709384396654ULL, + 1872670541876879330ULL, 3745341083753758661ULL, 7490682167507517323ULL, + 14981364335015034646ULL, 2996272867003006929ULL, 5992545734006013858ULL, + 11985091468012027717ULL, 2397018293602405543ULL, 4794036587204811087ULL, + 9588073174409622174ULL, 1917614634881924434ULL, 3835229269763848869ULL, + 7670458539527697739ULL, 15340917079055395478ULL, 3068183415811079095ULL, + 6136366831622158191ULL, 12272733663244316382ULL, 2454546732648863276ULL, + 4909093465297726553ULL, 9818186930595453106ULL, 1963637386119090621ULL, + 3927274772238181242ULL, 7854549544476362484ULL, 15709099088952724969ULL, + 3141819817790544993ULL, 6283639635581089987ULL, 12567279271162179975ULL, + 2513455854232435995ULL, 5026911708464871990ULL, 10053823416929743980ULL, + 2010764683385948796ULL, 4021529366771897592ULL, 8043058733543795184ULL, + 16086117467087590369ULL, 3217223493417518073ULL, 6434446986835036147ULL, + 12868893973670072295ULL, 2573778794734014459ULL, 5147557589468028918ULL, + 10295115178936057836ULL, 2059023035787211567ULL, 4118046071574423134ULL, + 8236092143148846269ULL, 16472184286297692538ULL, 3294436857259538507ULL, + 6588873714519077015ULL, 13177747429038154030ULL, 2635549485807630806ULL, + 5271098971615261612ULL, 10542197943230523224ULL, 2108439588646104644ULL, + 4216879177292209289ULL, 8433758354584418579ULL, 16867516709168837158ULL, + 3373503341833767431ULL, 6747006683667534863ULL, 13494013367335069727ULL, + 2698802673467013945ULL, 5397605346934027890ULL, 10795210693868055781ULL, + 2159042138773611156ULL, 4318084277547222312ULL, 8636168555094444625ULL, + 17272337110188889250ULL, 3454467422037777850ULL, 6908934844075555700ULL, + 13817869688151111400ULL, 2763573937630222280ULL, 5527147875260444560ULL, + 11054295750520889120ULL, 2210859150104177824ULL, 4421718300208355648ULL, + 8843436600416711296ULL, 17686873200833422592ULL, 3537374640166684518ULL, + 7074749280333369037ULL, 14149498560666738074ULL, 2829899712133347614ULL, + 5659799424266695229ULL, 11319598848533390459ULL, 2263919769706678091ULL, + 4527839539413356183ULL, 9055679078826712367ULL, 1811135815765342473ULL, + 3622271631530684947ULL, 7244543263061369894ULL, 14489086526122739788ULL, + 2897817305224547957ULL, 5795634610449095915ULL, 11591269220898191830ULL, + 2318253844179638366ULL, 4636507688359276732ULL, 9273015376718553464ULL, + 1854603075343710692ULL, 3709206150687421385ULL, 7418412301374842771ULL, + 14836824602749685542ULL, 2967364920549937108ULL, 5934729841099874217ULL, + 11869459682199748434ULL, 2373891936439949686ULL, 4747783872879899373ULL, + 9495567745759798747ULL, 1899113549151959749ULL, 3798227098303919498ULL, + 7596454196607838997ULL, 15192908393215677995ULL, 3038581678643135599ULL, + 6077163357286271198ULL, 12154326714572542396ULL, 2430865342914508479ULL, + 4861730685829016958ULL, 9723461371658033917ULL, 1944692274331606783ULL, + 3889384548663213566ULL, 7778769097326427133ULL, 15557538194652854267ULL, + 3111507638930570853ULL, 6223015277861141707ULL, 12446030555722283414ULL, + 2489206111144456682ULL, 4978412222288913365ULL, 9956824444577826731ULL, + 1991364888915565346ULL, 3982729777831130692ULL, 7965459555662261385ULL, + 15930919111324522770ULL, 3186183822264904554ULL, 6372367644529809108ULL, + 12744735289059618216ULL, 2548947057811923643ULL, 5097894115623847286ULL, + 10195788231247694572ULL, 2039157646249538914ULL, 4078315292499077829ULL, + 8156630584998155658ULL, 16313261169996311316ULL, 3262652233999262263ULL, + 6525304467998524526ULL, 13050608935997049053ULL, 2610121787199409810ULL, + 5220243574398819621ULL, 10440487148797639242ULL, 2088097429759527848ULL, + 4176194859519055697ULL, 8352389719038111394ULL, 16704779438076222788ULL, + 3340955887615244557ULL, 6681911775230489115ULL, 13363823550460978230ULL, + 2672764710092195646ULL, 5345529420184391292ULL, 10691058840368782584ULL, + 2138211768073756516ULL, 4276423536147513033ULL, 8552847072295026067ULL, + 17105694144590052135ULL, 3421138828918010427ULL, 6842277657836020854ULL, + 13684555315672041708ULL, 2736911063134408341ULL, 5473822126268816683ULL, + 10947644252537633366ULL, 2189528850507526673ULL, 4379057701015053346ULL, + 8758115402030106693ULL, 17516230804060213386ULL, 3503246160812042677ULL, + 7006492321624085354ULL, 14012984643248170709ULL, 2802596928649634141ULL, + 5605193857299268283ULL, 11210387714598536567ULL, 2242077542919707313ULL, + 4484155085839414626ULL, 8968310171678829253ULL, 17936620343357658507ULL, + 3587324068671531701ULL, 7174648137343063403ULL, 14349296274686126806ULL, + 2869859254937225361ULL, 5739718509874450722ULL, 11479437019748901445ULL, + 2295887403949780289ULL, 4591774807899560578ULL, 9183549615799121156ULL, + 1836709923159824231ULL, 3673419846319648462ULL, 7346839692639296924ULL, + 14693679385278593849ULL, 2938735877055718769ULL, 5877471754111437539ULL, + 11754943508222875079ULL, 2350988701644575015ULL, 4701977403289150031ULL, + 9403954806578300063ULL, 1880790961315660012ULL, 3761581922631320025ULL, + 7523163845262640050ULL, 15046327690525280101ULL, 3009265538105056020ULL, + 6018531076210112040ULL, 12037062152420224081ULL, 2407412430484044816ULL, + 4814824860968089632ULL, 9629649721936179265ULL, 1925929944387235853ULL, + 3851859888774471706ULL, 7703719777548943412ULL, 15407439555097886824ULL, + 3081487911019577364ULL, 6162975822039154729ULL, 12325951644078309459ULL, + 2465190328815661891ULL, 4930380657631323783ULL, 9860761315262647567ULL, + 1972152263052529513ULL, 3944304526105059027ULL, 7888609052210118054ULL, + 15777218104420236108ULL, 3155443620884047221ULL, 6310887241768094443ULL, + 12621774483536188886ULL, 2524354896707237777ULL, 5048709793414475554ULL, + 10097419586828951109ULL, 2019483917365790221ULL, 4038967834731580443ULL, + 8077935669463160887ULL, 16155871338926321774ULL, 3231174267785264354ULL, + 6462348535570528709ULL, 12924697071141057419ULL, 2584939414228211483ULL, + 5169878828456422967ULL, 10339757656912845935ULL, 2067951531382569187ULL, + 4135903062765138374ULL, 8271806125530276748ULL, 16543612251060553497ULL, + 3308722450212110699ULL, 6617444900424221398ULL, 13234889800848442797ULL, + 2646977960169688559ULL, 5293955920339377119ULL, 10587911840678754238ULL, + 2117582368135750847ULL, 4235164736271501695ULL, 8470329472543003390ULL, + 16940658945086006781ULL, 3388131789017201356ULL, 6776263578034402712ULL, + 13552527156068805425ULL, 2710505431213761085ULL, 5421010862427522170ULL, + 10842021724855044340ULL, 2168404344971008868ULL, 4336808689942017736ULL, + 8673617379884035472ULL, 17347234759768070944ULL, 3469446951953614188ULL, + 6938893903907228377ULL, 13877787807814456755ULL, 2775557561562891351ULL, + 5551115123125782702ULL, 11102230246251565404ULL, 2220446049250313080ULL, + 4440892098500626161ULL, 8881784197001252323ULL, 17763568394002504646ULL, + 3552713678800500929ULL, 7105427357601001858ULL, 14210854715202003717ULL, + 2842170943040400743ULL, 5684341886080801486ULL, 11368683772161602973ULL, + 2273736754432320594ULL, 4547473508864641189ULL, 9094947017729282379ULL, + 1818989403545856475ULL, 3637978807091712951ULL, 7275957614183425903ULL, + 14551915228366851806ULL, 2910383045673370361ULL, 5820766091346740722ULL, + 11641532182693481445ULL, 2328306436538696289ULL, 4656612873077392578ULL, + 9313225746154785156ULL, 1862645149230957031ULL, 3725290298461914062ULL, + 7450580596923828125ULL, 14901161193847656250ULL, 2980232238769531250ULL, + 5960464477539062500ULL, 11920928955078125000ULL, 2384185791015625000ULL, + 4768371582031250000ULL, 9536743164062500000ULL, 1907348632812500000ULL, + 3814697265625000000ULL, 7629394531250000000ULL, 15258789062500000000ULL, + 3051757812500000000ULL, 6103515625000000000ULL, 12207031250000000000ULL, + 2441406250000000000ULL, 4882812500000000000ULL, 9765625000000000000ULL, + 1953125000000000000ULL, 3906250000000000000ULL, 7812500000000000000ULL, + 15625000000000000000ULL, 3125000000000000000ULL, 6250000000000000000ULL, + 12500000000000000000ULL, 2500000000000000000ULL, 5000000000000000000ULL, + 10000000000000000000ULL, 2000000000000000000ULL, 4000000000000000000ULL, + 8000000000000000000ULL, 16000000000000000000ULL, 3200000000000000000ULL, + 6400000000000000000ULL, 12800000000000000000ULL, 2560000000000000000ULL, + 5120000000000000000ULL, 10240000000000000000ULL, 2048000000000000000ULL, + 4096000000000000000ULL, 8192000000000000000ULL, 16384000000000000000ULL, + 3276800000000000000ULL, 6553600000000000000ULL, 13107200000000000000ULL, + 2621440000000000000ULL, 5242880000000000000ULL, 10485760000000000000ULL, + 2097152000000000000ULL, 4194304000000000000ULL, 8388608000000000000ULL, + 16777216000000000000ULL, 3355443200000000000ULL, 6710886400000000000ULL, + 13421772800000000000ULL, 2684354560000000000ULL, 5368709120000000000ULL, + 10737418240000000000ULL, 2147483648000000000ULL, 4294967296000000000ULL, + 8589934592000000000ULL, 17179869184000000000ULL, 3435973836800000000ULL, + 6871947673600000000ULL, 13743895347200000000ULL, 2748779069440000000ULL, + 5497558138880000000ULL, 10995116277760000000ULL, 2199023255552000000ULL, + 4398046511104000000ULL, 8796093022208000000ULL, 17592186044416000000ULL, + 3518437208883200000ULL, 7036874417766400000ULL, 14073748835532800000ULL, + 2814749767106560000ULL, 5629499534213120000ULL, 11258999068426240000ULL, + 2251799813685248000ULL, 4503599627370496000ULL, 9007199254740992000ULL, + 18014398509481984000ULL, 3602879701896396800ULL, 7205759403792793600ULL, + 14411518807585587200ULL, 2882303761517117440ULL, 5764607523034234880ULL, + 11529215046068469760ULL, 2305843009213693952ULL, 4611686018427387904ULL, + 9223372036854775808ULL, 1844674407370955161ULL, 3689348814741910323ULL, + 7378697629483820646ULL, 14757395258967641292ULL, 2951479051793528258ULL, + 5902958103587056517ULL, 11805916207174113034ULL, 2361183241434822606ULL, + 4722366482869645213ULL, 9444732965739290427ULL, 1888946593147858085ULL, + 3777893186295716170ULL, 7555786372591432341ULL, 15111572745182864683ULL, + 3022314549036572936ULL, 6044629098073145873ULL, 12089258196146291747ULL, + 2417851639229258349ULL, 4835703278458516698ULL, 9671406556917033397ULL, + 1934281311383406679ULL, 3868562622766813359ULL, 7737125245533626718ULL, + 15474250491067253436ULL, 3094850098213450687ULL, 6189700196426901374ULL, + 12379400392853802748ULL, 2475880078570760549ULL, 4951760157141521099ULL, + 9903520314283042199ULL, 1980704062856608439ULL, 3961408125713216879ULL, + 7922816251426433759ULL, 15845632502852867518ULL, 3169126500570573503ULL, + 6338253001141147007ULL, 12676506002282294014ULL, 2535301200456458802ULL, + 5070602400912917605ULL, 10141204801825835211ULL, 2028240960365167042ULL, + 4056481920730334084ULL, 8112963841460668169ULL, 16225927682921336339ULL, + 3245185536584267267ULL, 6490371073168534535ULL, 12980742146337069071ULL, + 2596148429267413814ULL, 5192296858534827628ULL, 10384593717069655257ULL, + 2076918743413931051ULL, 4153837486827862102ULL, 8307674973655724205ULL, + 16615349947311448411ULL, 3323069989462289682ULL, 6646139978924579364ULL, + 13292279957849158729ULL, 2658455991569831745ULL, 5316911983139663491ULL, + 10633823966279326983ULL, 2126764793255865396ULL, 4253529586511730793ULL, + 8507059173023461586ULL, 17014118346046923173ULL, 3402823669209384634ULL, + 6805647338418769269ULL, 13611294676837538538ULL, 2722258935367507707ULL, + 5444517870735015415ULL, 10889035741470030830ULL, 2177807148294006166ULL, + 4355614296588012332ULL, 8711228593176024664ULL, 17422457186352049329ULL, + 3484491437270409865ULL, 6968982874540819731ULL, 13937965749081639463ULL, + 2787593149816327892ULL, 5575186299632655785ULL, 11150372599265311570ULL, + 2230074519853062314ULL, 4460149039706124628ULL, 8920298079412249256ULL, + 17840596158824498513ULL, 3568119231764899702ULL, 7136238463529799405ULL, + 14272476927059598810ULL, 2854495385411919762ULL, 5708990770823839524ULL, + 11417981541647679048ULL, 2283596308329535809ULL, 4567192616659071619ULL, + 9134385233318143238ULL, 1826877046663628647ULL, 3653754093327257295ULL, + 7307508186654514591ULL, 14615016373309029182ULL, 2923003274661805836ULL, + 5846006549323611672ULL, 11692013098647223345ULL, 2338402619729444669ULL, + 4676805239458889338ULL, 9353610478917778676ULL, 1870722095783555735ULL, + 3741444191567111470ULL, 7482888383134222941ULL, 14965776766268445882ULL, + 2993155353253689176ULL, 5986310706507378352ULL, 11972621413014756705ULL, + 2394524282602951341ULL, 4789048565205902682ULL, 9578097130411805364ULL, + 1915619426082361072ULL, 3831238852164722145ULL, 7662477704329444291ULL, + 15324955408658888583ULL, 3064991081731777716ULL, 6129982163463555433ULL, + 12259964326927110866ULL, 2451992865385422173ULL, 4903985730770844346ULL, + 9807971461541688693ULL, 1961594292308337738ULL, 3923188584616675477ULL, + 7846377169233350954ULL, 15692754338466701909ULL, 3138550867693340381ULL, + 6277101735386680763ULL, 12554203470773361527ULL, 2510840694154672305ULL, + 5021681388309344611ULL, 10043362776618689222ULL, 2008672555323737844ULL, + 4017345110647475688ULL, 8034690221294951377ULL, 16069380442589902755ULL, + 3213876088517980551ULL, 6427752177035961102ULL, 12855504354071922204ULL, + 2571100870814384440ULL, 5142201741628768881ULL, 10284403483257537763ULL, + 2056880696651507552ULL, 4113761393303015105ULL, 8227522786606030210ULL, + 16455045573212060421ULL, 3291009114642412084ULL, 6582018229284824168ULL, + 13164036458569648337ULL, 2632807291713929667ULL, 5265614583427859334ULL, + 10531229166855718669ULL, 2106245833371143733ULL, 4212491666742287467ULL, + 8424983333484574935ULL, 16849966666969149871ULL, 3369993333393829974ULL, + 6739986666787659948ULL, 13479973333575319897ULL, 2695994666715063979ULL, + 5391989333430127958ULL, 10783978666860255917ULL, 2156795733372051183ULL, + 4313591466744102367ULL, 8627182933488204734ULL, 17254365866976409468ULL, + 3450873173395281893ULL, 6901746346790563787ULL, 13803492693581127574ULL, + 2760698538716225514ULL, 5521397077432451029ULL, 11042794154864902059ULL, + 2208558830972980411ULL, 4417117661945960823ULL, 8834235323891921647ULL, + 17668470647783843295ULL, 3533694129556768659ULL, 7067388259113537318ULL, + 14134776518227074636ULL, 2826955303645414927ULL, 5653910607290829854ULL, + 11307821214581659709ULL, 2261564242916331941ULL, 4523128485832663883ULL, + 9046256971665327767ULL, 18092513943330655534ULL, 3618502788666131106ULL, + 7237005577332262213ULL, 14474011154664524427ULL, 2894802230932904885ULL, + 5789604461865809771ULL, 11579208923731619542ULL, 2315841784746323908ULL, + 4631683569492647816ULL, 9263367138985295633ULL, 1852673427797059126ULL, + 3705346855594118253ULL, 7410693711188236507ULL, 14821387422376473014ULL, + 2964277484475294602ULL, 5928554968950589205ULL, 11857109937901178411ULL, + 2371421987580235682ULL, 4742843975160471364ULL, 9485687950320942729ULL, + 1897137590064188545ULL, 3794275180128377091ULL, 7588550360256754183ULL, + 15177100720513508366ULL, 3035420144102701673ULL, 6070840288205403346ULL, + 12141680576410806693ULL, 2428336115282161338ULL, 4856672230564322677ULL, + 9713344461128645354ULL, 1942668892225729070ULL, 3885337784451458141ULL, + 7770675568902916283ULL, 15541351137805832567ULL, 3108270227561166513ULL, + 6216540455122333026ULL, 12433080910244666053ULL, 2486616182048933210ULL, + 4973232364097866421ULL, 9946464728195732843ULL, 1989292945639146568ULL, + 3978585891278293137ULL, 7957171782556586274ULL, 15914343565113172548ULL, + 3182868713022634509ULL, 6365737426045269019ULL, 12731474852090538039ULL, + 2546294970418107607ULL, 5092589940836215215ULL, 10185179881672430431ULL, + 2037035976334486086ULL, 4074071952668972172ULL, 8148143905337944345ULL, + 16296287810675888690ULL, 3259257562135177738ULL, 6518515124270355476ULL, + 13037030248540710952ULL, 2607406049708142190ULL, 5214812099416284380ULL, + 10429624198832568761ULL, 2085924839766513752ULL, 4171849679533027504ULL, + 8343699359066055009ULL, 16687398718132110018ULL, 3337479743626422003ULL, + 6674959487252844007ULL, 13349918974505688014ULL, 2669983794901137602ULL, + 5339967589802275205ULL, 10679935179604550411ULL, 2135987035920910082ULL, + 4271974071841820164ULL, 8543948143683640329ULL, 17087896287367280659ULL, + 3417579257473456131ULL, 6835158514946912263ULL, 13670317029893824527ULL, + 2734063405978764905ULL, 5468126811957529810ULL, 10936253623915059621ULL, + 2187250724783011924ULL, 4374501449566023848ULL, 8749002899132047697ULL, + 17498005798264095394ULL, 3499601159652819078ULL, 6999202319305638157ULL, + 13998404638611276315ULL, 2799680927722255263ULL, 5599361855444510526ULL, + 11198723710889021052ULL, 2239744742177804210ULL, 4479489484355608421ULL, + 8958978968711216842ULL, 17917957937422433684ULL, 3583591587484486736ULL, + 7167183174968973473ULL, 14334366349937946947ULL, 2866873269987589389ULL, + 5733746539975178779ULL, 11467493079950357558ULL, 2293498615990071511ULL, + 4586997231980143023ULL, 9173994463960286046ULL, 1834798892792057209ULL, + 3669597785584114418ULL, 7339195571168228837ULL, 14678391142336457674ULL, + 2935678228467291534ULL, 5871356456934583069ULL, 11742712913869166139ULL, + 2348542582773833227ULL, 4697085165547666455ULL, 9394170331095332911ULL, + 1878834066219066582ULL, 3757668132438133164ULL, 7515336264876266329ULL, + 15030672529752532658ULL, 3006134505950506531ULL, 6012269011901013063ULL, + 12024538023802026126ULL, 2404907604760405225ULL, 4809815209520810450ULL, + 9619630419041620901ULL, 1923926083808324180ULL, 3847852167616648360ULL, + 7695704335233296721ULL, 15391408670466593442ULL, 3078281734093318688ULL, + 6156563468186637376ULL, 12313126936373274753ULL, 2462625387274654950ULL, + 4925250774549309901ULL, 9850501549098619803ULL, 1970100309819723960ULL, + 3940200619639447921ULL, 7880401239278895842ULL, 15760802478557791684ULL, + 3152160495711558336ULL, 6304320991423116673ULL, 12608641982846233347ULL, + 2521728396569246669ULL, 5043456793138493339ULL, 10086913586276986678ULL, + 2017382717255397335ULL, 4034765434510794671ULL, 8069530869021589342ULL, + 16139061738043178685ULL, 3227812347608635737ULL, 6455624695217271474ULL, + 12911249390434542948ULL, 2582249878086908589ULL, 5164499756173817179ULL, + 10328999512347634358ULL, 2065799902469526871ULL, 4131599804939053743ULL, + 8263199609878107486ULL, 16526399219756214973ULL, 3305279843951242994ULL, + 6610559687902485989ULL, 13221119375804971979ULL, 2644223875160994395ULL, + 5288447750321988791ULL, 10576895500643977583ULL, 2115379100128795516ULL, + 4230758200257591033ULL, 8461516400515182066ULL, 16923032801030364133ULL, + 3384606560206072826ULL, 6769213120412145653ULL, 13538426240824291306ULL, + 2707685248164858261ULL, 5415370496329716522ULL, 10830740992659433045ULL, + 2166148198531886609ULL, 4332296397063773218ULL, 8664592794127546436ULL, + 17329185588255092872ULL, 3465837117651018574ULL, 6931674235302037148ULL, + 13863348470604074297ULL, 2772669694120814859ULL, 5545339388241629719ULL, + 11090678776483259438ULL, 2218135755296651887ULL, 4436271510593303775ULL, + 8872543021186607550ULL, 17745086042373215101ULL, 3549017208474643020ULL, + 7098034416949286040ULL, 14196068833898572081ULL, 2839213766779714416ULL, + 5678427533559428832ULL, 11356855067118857664ULL, 2271371013423771532ULL, + 4542742026847543065ULL, 9085484053695086131ULL, 1817096810739017226ULL, + 3634193621478034452ULL, 7268387242956068905ULL, 14536774485912137810ULL, + 2907354897182427562ULL, 5814709794364855124ULL, 11629419588729710248ULL, + 2325883917745942049ULL, 4651767835491884099ULL, 9303535670983768199ULL, + 1860707134196753639ULL, 3721414268393507279ULL, 7442828536787014559ULL, + 14885657073574029118ULL, 2977131414714805823ULL, 5954262829429611647ULL, + 11908525658859223294ULL, 2381705131771844658ULL, 4763410263543689317ULL, + 9526820527087378635ULL, 1905364105417475727ULL, 3810728210834951454ULL, + 7621456421669902908ULL, 15242912843339805817ULL, 3048582568667961163ULL, + 6097165137335922326ULL, 12194330274671844653ULL, 2438866054934368930ULL, + 4877732109868737861ULL, 9755464219737475723ULL, 1951092843947495144ULL, + 3902185687894990289ULL, 7804371375789980578ULL, 15608742751579961156ULL, + 3121748550315992231ULL, 6243497100631984462ULL, 12486994201263968925ULL, + 2497398840252793785ULL, 4994797680505587570ULL, 9989595361011175140ULL, + 1997919072202235028ULL, 3995838144404470056ULL, 7991676288808940112ULL, + 15983352577617880224ULL, 3196670515523576044ULL, 6393341031047152089ULL, + 12786682062094304179ULL, 2557336412418860835ULL, 5114672824837721671ULL, + 10229345649675443343ULL, 2045869129935088668ULL, 4091738259870177337ULL, + 8183476519740354675ULL, 16366953039480709350ULL, 3273390607896141870ULL, + 6546781215792283740ULL, 13093562431584567480ULL, 2618712486316913496ULL, + 5237424972633826992ULL, 10474849945267653984ULL, 2094969989053530796ULL, + 4189939978107061593ULL, 8379879956214123187ULL, 16759759912428246374ULL, + 3351951982485649274ULL, 6703903964971298549ULL, 13407807929942597099ULL, + 2681561585988519419ULL, 5363123171977038839ULL, 10726246343954077679ULL, + 2145249268790815535ULL, 4290498537581631071ULL, 8580997075163262143ULL, + 17161994150326524287ULL, 3432398830065304857ULL, 6864797660130609714ULL, + 13729595320261219429ULL, 2745919064052243885ULL, 5491838128104487771ULL, + 10983676256208975543ULL, 2196735251241795108ULL, 4393470502483590217ULL, + 8786941004967180435ULL, 17573882009934360870ULL, 3514776401986872174ULL, + 7029552803973744348ULL, 14059105607947488696ULL, 2811821121589497739ULL, + 5623642243178995478ULL, 11247284486357990957ULL, 2249456897271598191ULL, + 4498913794543196382ULL, 8997827589086392765ULL, 17995655178172785531ULL, + 3599131035634557106ULL, 7198262071269114212ULL, 14396524142538228424ULL, + 2879304828507645684ULL, 5758609657015291369ULL, 11517219314030582739ULL, + 2303443862806116547ULL, 4606887725612233095ULL, 9213775451224466191ULL, + 1842755090244893238ULL, 3685510180489786476ULL, 7371020360979572953ULL, + 14742040721959145907ULL, 2948408144391829181ULL, 5896816288783658362ULL, + 11793632577567316725ULL, 2358726515513463345ULL, 4717453031026926690ULL, + 9434906062053853380ULL, 1886981212410770676ULL, 3773962424821541352ULL, + 7547924849643082704ULL, 15095849699286165408ULL, 3019169939857233081ULL, + 6038339879714466163ULL, 12076679759428932327ULL, 2415335951885786465ULL, + 4830671903771572930ULL, 9661343807543145861ULL, 1932268761508629172ULL, + 3864537523017258344ULL, 7729075046034516689ULL, 15458150092069033378ULL, + 3091630018413806675ULL, 6183260036827613351ULL, 12366520073655226703ULL, + 2473304014731045340ULL, 4946608029462090681ULL, 9893216058924181362ULL, + 1978643211784836272ULL, 3957286423569672544ULL, 7914572847139345089ULL, + 15829145694278690179ULL, 3165829138855738035ULL, 6331658277711476071ULL, + 12663316555422952143ULL, 2532663311084590428ULL, 5065326622169180857ULL, + 10130653244338361715ULL, 2026130648867672343ULL, 4052261297735344686ULL, + 8104522595470689372ULL, 16209045190941378744ULL, 3241809038188275748ULL, + 6483618076376551497ULL, 12967236152753102995ULL, 2593447230550620599ULL, + 5186894461101241198ULL, 10373788922202482396ULL, 2074757784440496479ULL, + 4149515568880992958ULL, 8299031137761985917ULL, 16598062275523971834ULL, + 3319612455104794366ULL, 6639224910209588733ULL, 13278449820419177467ULL, + 2655689964083835493ULL, 5311379928167670986ULL, 10622759856335341973ULL, + 2124551971267068394ULL, 4249103942534136789ULL, 8498207885068273579ULL, + 16996415770136547158ULL, 3399283154027309431ULL, 6798566308054618863ULL, + 13597132616109237726ULL, 2719426523221847545ULL, 5438853046443695090ULL, + 10877706092887390181ULL, 2175541218577478036ULL, 4351082437154956072ULL, + 8702164874309912144ULL, 17404329748619824289ULL, 3480865949723964857ULL, + 6961731899447929715ULL, 13923463798895859431ULL, 2784692759779171886ULL, + 5569385519558343772ULL, 11138771039116687545ULL, 2227754207823337509ULL, + 4455508415646675018ULL, 8911016831293350036ULL, 17822033662586700072ULL, + 3564406732517340014ULL, 7128813465034680029ULL, 14257626930069360058ULL, + 2851525386013872011ULL, 5703050772027744023ULL, 11406101544055488046ULL, + 2281220308811097609ULL, 4562440617622195218ULL, 9124881235244390437ULL, + 1824976247048878087ULL, 3649952494097756174ULL, 7299904988195512349ULL, + 14599809976391024699ULL, 2919961995278204939ULL, 5839923990556409879ULL, + 11679847981112819759ULL, 2335969596222563951ULL, 4671939192445127903ULL, + 9343878384890255807ULL, 1868775676978051161ULL, 3737551353956102323ULL, + 7475102707912204646ULL, 14950205415824409292ULL, 2990041083164881858ULL, + 5980082166329763716ULL, 11960164332659527433ULL, 2392032866531905486ULL, + 4784065733063810973ULL, 9568131466127621947ULL, 1913626293225524389ULL, + 3827252586451048778ULL, 7654505172902097557ULL, 15309010345804195115ULL, + 3061802069160839023ULL, 6123604138321678046ULL, 12247208276643356092ULL, + 2449441655328671218ULL, 4898883310657342436ULL, 9797766621314684873ULL, + 1959553324262936974ULL, 3919106648525873949ULL, 7838213297051747899ULL, + 15676426594103495798ULL, 3135285318820699159ULL, 6270570637641398319ULL, + 12541141275282796638ULL, 2508228255056559327ULL, 5016456510113118655ULL, + 10032913020226237310ULL, 2006582604045247462ULL, 4013165208090494924ULL, + 8026330416180989848ULL, 16052660832361979697ULL, 3210532166472395939ULL, + 6421064332944791878ULL, 12842128665889583757ULL, 2568425733177916751ULL, + 5136851466355833503ULL, 10273702932711667006ULL, 2054740586542333401ULL, + 4109481173084666802ULL, 8218962346169333605ULL, 16437924692338667210ULL, + 3287584938467733442ULL, 6575169876935466884ULL, 13150339753870933768ULL, + 2630067950774186753ULL, 5260135901548373507ULL, 10520271803096747014ULL, + 2104054360619349402ULL, 4208108721238698805ULL, 8416217442477397611ULL, + 16832434884954795223ULL, 3366486976990959044ULL, 6732973953981918089ULL, + 13465947907963836178ULL, 2693189581592767235ULL, 5386379163185534471ULL, + 10772758326371068942ULL, 2154551665274213788ULL, 4309103330548427577ULL, + 8618206661096855154ULL, 17236413322193710308ULL, 3447282664438742061ULL, + 6894565328877484123ULL, 13789130657754968246ULL, 2757826131550993649ULL, + 5515652263101987298ULL, 11031304526203974597ULL, 2206260905240794919ULL, + 4412521810481589838ULL, 8825043620963179677ULL, 17650087241926359355ULL, + 3530017448385271871ULL, 7060034896770543742ULL, 14120069793541087484ULL, + 2824013958708217496ULL, 5648027917416434993ULL, 11296055834832869987ULL, + 2259211166966573997ULL, 4518422333933147995ULL, 9036844667866295990ULL, + 18073689335732591980ULL, 3614737867146518396ULL, 7229475734293036792ULL, + 14458951468586073584ULL, 2891790293717214716ULL, 5783580587434429433ULL, + 11567161174868858867ULL, 2313432234973771773ULL, 4626864469947543547ULL, + 9253728939895087094ULL, 1850745787979017418ULL, 3701491575958034837ULL, + 7402983151916069675ULL, 14805966303832139350ULL, 2961193260766427870ULL, + 5922386521532855740ULL, 11844773043065711480ULL, 2368954608613142296ULL, + 4737909217226284592ULL, 9475818434452569184ULL, 1895163686890513836ULL, + 3790327373781027673ULL, 7580654747562055347ULL, 15161309495124110694ULL, + 3032261899024822138ULL, 6064523798049644277ULL, 12129047596099288555ULL, + 2425809519219857711ULL, 4851619038439715422ULL, 9703238076879430844ULL, + 1940647615375886168ULL, 3881295230751772337ULL, 7762590461503544675ULL, + 15525180923007089351ULL, 3105036184601417870ULL, 6210072369202835740ULL, + 12420144738405671481ULL, 2484028947681134296ULL, 4968057895362268592ULL, + 9936115790724537184ULL, 1987223158144907436ULL, 3974446316289814873ULL, + 7948892632579629747ULL, 15897785265159259495ULL, 3179557053031851899ULL, + 6359114106063703798ULL, 12718228212127407596ULL, 2543645642425481519ULL, + 5087291284850963038ULL, 10174582569701926077ULL, 2034916513940385215ULL, + 4069833027880770430ULL, 8139666055761540861ULL, 16279332111523081723ULL, + 3255866422304616344ULL, 6511732844609232689ULL, 13023465689218465379ULL, + 2604693137843693075ULL, 5209386275687386151ULL, 10418772551374772303ULL, + 2083754510274954460ULL, 4167509020549908921ULL, 8335018041099817842ULL, + 16670036082199635685ULL, 3334007216439927137ULL, 6668014432879854274ULL, + 13336028865759708548ULL, 2667205773151941709ULL, 5334411546303883419ULL, + 10668823092607766838ULL, 2133764618521553367ULL, 4267529237043106735ULL, + 8535058474086213470ULL, 17070116948172426941ULL, 3414023389634485388ULL, + 6828046779268970776ULL, 13656093558537941553ULL, 2731218711707588310ULL, + 5462437423415176621ULL, 10924874846830353242ULL, 2184974969366070648ULL, + 4369949938732141297ULL, 8739899877464282594ULL, 17479799754928565188ULL, + 3495959950985713037ULL, 6991919901971426075ULL, 13983839803942852150ULL, + 2796767960788570430ULL, 5593535921577140860ULL, 11187071843154281720ULL, + 2237414368630856344ULL, 4474828737261712688ULL, 8949657474523425376ULL, + 17899314949046850752ULL, 3579862989809370150ULL, 7159725979618740301ULL, + 14319451959237480602ULL, 2863890391847496120ULL, 5727780783694992240ULL, + 11455561567389984481ULL, 2291112313477996896ULL, 4582224626955993792ULL, + 9164449253911987585ULL, 1832889850782397517ULL, 3665779701564795034ULL, + 7331559403129590068ULL, 14663118806259180136ULL, 2932623761251836027ULL, + 5865247522503672054ULL, 11730495045007344109ULL, 2346099009001468821ULL, + 4692198018002937643ULL, 9384396036005875287ULL, 1876879207201175057ULL, + 3753758414402350114ULL, 7507516828804700229ULL, 15015033657609400459ULL, + 3003006731521880091ULL, 6006013463043760183ULL, 12012026926087520367ULL, + 2402405385217504073ULL, 4804810770435008147ULL, 9609621540870016294ULL, + 1921924308174003258ULL, 3843848616348006517ULL, 7687697232696013035ULL, + 15375394465392026070ULL, 3075078893078405214ULL, 6150157786156810428ULL, + 12300315572313620856ULL, 2460063114462724171ULL, 4920126228925448342ULL, + 9840252457850896685ULL, 1968050491570179337ULL, 3936100983140358674ULL, + 7872201966280717348ULL, 15744403932561434696ULL, 3148880786512286939ULL, + 6297761573024573878ULL, 12595523146049147757ULL, 2519104629209829551ULL, + 5038209258419659102ULL, 10076418516839318205ULL, 2015283703367863641ULL, + 4030567406735727282ULL, 8061134813471454564ULL, 16122269626942909129ULL, + 3224453925388581825ULL, 6448907850777163651ULL, 12897815701554327303ULL, + 2579563140310865460ULL, 5159126280621730921ULL, 10318252561243461842ULL, + 2063650512248692368ULL, 4127301024497384737ULL, 8254602048994769474ULL, + 16509204097989538948ULL, 3301840819597907789ULL, 6603681639195815579ULL, + 13207363278391631158ULL, 2641472655678326231ULL, 5282945311356652463ULL, + 10565890622713304927ULL, 2113178124542660985ULL, 4226356249085321970ULL, + 8452712498170643941ULL, 16905424996341287883ULL, 3381084999268257576ULL, + 6762169998536515153ULL, 13524339997073030306ULL, 2704867999414606061ULL, + 5409735998829212122ULL, 10819471997658424245ULL, 2163894399531684849ULL, + 4327788799063369698ULL, 8655577598126739396ULL, 17311155196253478792ULL, + 3462231039250695758ULL, 6924462078501391516ULL, 13848924157002783033ULL, + 2769784831400556606ULL, 5539569662801113213ULL, 11079139325602226427ULL, + 2215827865120445285ULL, 4431655730240890570ULL, 8863311460481781141ULL, + 17726622920963562283ULL, 3545324584192712456ULL, 7090649168385424913ULL, + 14181298336770849826ULL, 2836259667354169965ULL, 5672519334708339930ULL, + 11345038669416679861ULL, 2269007733883335972ULL, 4538015467766671944ULL, + 9076030935533343889ULL, 18152061871066687778ULL, 3630412374213337555ULL, + 7260824748426675111ULL, 14521649496853350222ULL, 2904329899370670044ULL, + 5808659798741340089ULL, 11617319597482680178ULL, 2323463919496536035ULL, + 4646927838993072071ULL, 9293855677986144142ULL, 1858771135597228828ULL, + 3717542271194457656ULL, 7435084542388915313ULL, 14870169084777830627ULL, + 2974033816955566125ULL, 5948067633911132251ULL, 11896135267822264502ULL, + 2379227053564452900ULL, 4758454107128905800ULL, 9516908214257811601ULL, + 1903381642851562320ULL, 3806763285703124640ULL, 7613526571406249281ULL, + 15227053142812498563ULL, 3045410628562499712ULL, 6090821257124999425ULL, + 12181642514249998850ULL, 2436328502849999770ULL, 4872657005699999540ULL, + 9745314011399999080ULL, 1949062802279999816ULL, 3898125604559999632ULL, + 7796251209119999264ULL, 15592502418239998528ULL, 3118500483647999705ULL, + 6237000967295999411ULL, 12474001934591998822ULL, 2494800386918399764ULL, + 4989600773836799529ULL, 9979201547673599058ULL, 1995840309534719811ULL, + 3991680619069439623ULL, 7983361238138879246ULL, 15966722476277758493ULL, + 3193344495255551698ULL, 6386688990511103397ULL, 12773377981022206794ULL, + 2554675596204441358ULL, 5109351192408882717ULL, 10218702384817765435ULL, + 2043740476963553087ULL, 4087480953927106174ULL, 8174961907854212348ULL, + 16349923815708424697ULL, 3269984763141684939ULL, 6539969526283369878ULL, + 13079939052566739757ULL, 2615987810513347951ULL, 5231975621026695903ULL, + 10463951242053391806ULL, 2092790248410678361ULL, 4185580496821356722ULL, + 8371160993642713444ULL, 16742321987285426889ULL, 3348464397457085377ULL, + 6696928794914170755ULL, 13393857589828341511ULL, 2678771517965668302ULL, + 5357543035931336604ULL, 10715086071862673209ULL, 2143017214372534641ULL, + 4286034428745069283ULL, 8572068857490138567ULL, 17144137714980277135ULL, + 3428827542996055427ULL, 6857655085992110854ULL, 13715310171984221708ULL, + 2743062034396844341ULL, 5486124068793688683ULL, 10972248137587377366ULL, + 2194449627517475473ULL, 4388899255034950946ULL, 8777798510069901893ULL, + 17555597020139803786ULL, 3511119404027960757ULL, 7022238808055921514ULL, + 14044477616111843029ULL, 2808895523222368605ULL, 5617791046444737211ULL, + 11235582092889474423ULL, 2247116418577894884ULL, 4494232837155789769ULL, + 8988465674311579538ULL, 17976931348623159077ULL, 3595386269724631815ULL, + 7190772539449263630ULL, 14381545078898527261ULL, 2876309015779705452ULL, + 5752618031559410904ULL, 11505236063118821809ULL, 2301047212623764361ULL, + 4602094425247528723ULL, 9204188850495057447ULL, 1840837770099011489ULL, + 3681675540198022979ULL, 7363351080396045958ULL, +}; + +static const gint32 Formatter_TensExponentTable [] = { + -323, -323, -322, -322, -322, -322, -321, -321, -321, -320, -320, -320, + -319, -319, -319, -319, -318, -318, -318, -317, -317, -317, -316, -316, + -316, -316, -315, -315, -315, -314, -314, -314, -313, -313, -313, -313, + -312, -312, -312, -311, -311, -311, -310, -310, -310, -310, -309, -309, + -309, -308, -308, -308, -307, -307, -307, -307, -306, -306, -306, -305, + -305, -305, -304, -304, -304, -304, -303, -303, -303, -302, -302, -302, + -301, -301, -301, -301, -300, -300, -300, -299, -299, -299, -298, -298, + -298, -298, -297, -297, -297, -296, -296, -296, -295, -295, -295, -295, + -294, -294, -294, -293, -293, -293, -292, -292, -292, -291, -291, -291, + -291, -290, -290, -290, -289, -289, -289, -288, -288, -288, -288, -287, + -287, -287, -286, -286, -286, -285, -285, -285, -285, -284, -284, -284, + -283, -283, -283, -282, -282, -282, -282, -281, -281, -281, -280, -280, + -280, -279, -279, -279, -279, -278, -278, -278, -277, -277, -277, -276, + -276, -276, -276, -275, -275, -275, -274, -274, -274, -273, -273, -273, + -273, -272, -272, -272, -271, -271, -271, -270, -270, -270, -270, -269, + -269, -269, -268, -268, -268, -267, -267, -267, -267, -266, -266, -266, + -265, -265, -265, -264, -264, -264, -263, -263, -263, -263, -262, -262, + -262, -261, -261, -261, -260, -260, -260, -260, -259, -259, -259, -258, + -258, -258, -257, -257, -257, -257, -256, -256, -256, -255, -255, -255, + -254, -254, -254, -254, -253, -253, -253, -252, -252, -252, -251, -251, + -251, -251, -250, -250, -250, -249, -249, -249, -248, -248, -248, -248, + -247, -247, -247, -246, -246, -246, -245, -245, -245, -245, -244, -244, + -244, -243, -243, -243, -242, -242, -242, -242, -241, -241, -241, -240, + -240, -240, -239, -239, -239, -239, -238, -238, -238, -237, -237, -237, + -236, -236, -236, -235, -235, -235, -235, -234, -234, -234, -233, -233, + -233, -232, -232, -232, -232, -231, -231, -231, -230, -230, -230, -229, + -229, -229, -229, -228, -228, -228, -227, -227, -227, -226, -226, -226, + -226, -225, -225, -225, -224, -224, -224, -223, -223, -223, -223, -222, + -222, -222, -221, -221, -221, -220, -220, -220, -220, -219, -219, -219, + -218, -218, -218, -217, -217, -217, -217, -216, -216, -216, -215, -215, + -215, -214, -214, -214, -214, -213, -213, -213, -212, -212, -212, -211, + -211, -211, -211, -210, -210, -210, -209, -209, -209, -208, -208, -208, + -208, -207, -207, -207, -206, -206, -206, -205, -205, -205, -204, -204, + -204, -204, -203, -203, -203, -202, -202, -202, -201, -201, -201, -201, + -200, -200, -200, -199, -199, -199, -198, -198, -198, -198, -197, -197, + -197, -196, -196, -196, -195, -195, -195, -195, -194, -194, -194, -193, + -193, -193, -192, -192, -192, -192, -191, -191, -191, -190, -190, -190, + -189, -189, -189, -189, -188, -188, -188, -187, -187, -187, -186, -186, + -186, -186, -185, -185, -185, -184, -184, -184, -183, -183, -183, -183, + -182, -182, -182, -181, -181, -181, -180, -180, -180, -180, -179, -179, + -179, -178, -178, -178, -177, -177, -177, -176, -176, -176, -176, -175, + -175, -175, -174, -174, -174, -173, -173, -173, -173, -172, -172, -172, + -171, -171, -171, -170, -170, -170, -170, -169, -169, -169, -168, -168, + -168, -167, -167, -167, -167, -166, -166, -166, -165, -165, -165, -164, + -164, -164, -164, -163, -163, -163, -162, -162, -162, -161, -161, -161, + -161, -160, -160, -160, -159, -159, -159, -158, -158, -158, -158, -157, + -157, -157, -156, -156, -156, -155, -155, -155, -155, -154, -154, -154, + -153, -153, -153, -152, -152, -152, -152, -151, -151, -151, -150, -150, + -150, -149, -149, -149, -149, -148, -148, -148, -147, -147, -147, -146, + -146, -146, -145, -145, -145, -145, -144, -144, -144, -143, -143, -143, + -142, -142, -142, -142, -141, -141, -141, -140, -140, -140, -139, -139, + -139, -139, -138, -138, -138, -137, -137, -137, -136, -136, -136, -136, + -135, -135, -135, -134, -134, -134, -133, -133, -133, -133, -132, -132, + -132, -131, -131, -131, -130, -130, -130, -130, -129, -129, -129, -128, + -128, -128, -127, -127, -127, -127, -126, -126, -126, -125, -125, -125, + -124, -124, -124, -124, -123, -123, -123, -122, -122, -122, -121, -121, + -121, -121, -120, -120, -120, -119, -119, -119, -118, -118, -118, -117, + -117, -117, -117, -116, -116, -116, -115, -115, -115, -114, -114, -114, + -114, -113, -113, -113, -112, -112, -112, -111, -111, -111, -111, -110, + -110, -110, -109, -109, -109, -108, -108, -108, -108, -107, -107, -107, + -106, -106, -106, -105, -105, -105, -105, -104, -104, -104, -103, -103, + -103, -102, -102, -102, -102, -101, -101, -101, -100, -100, -100, -99, + -99, -99, -99, -98, -98, -98, -97, -97, -97, -96, -96, -96, + -96, -95, -95, -95, -94, -94, -94, -93, -93, -93, -93, -92, + -92, -92, -91, -91, -91, -90, -90, -90, -89, -89, -89, -89, + -88, -88, -88, -87, -87, -87, -86, -86, -86, -86, -85, -85, + -85, -84, -84, -84, -83, -83, -83, -83, -82, -82, -82, -81, + -81, -81, -80, -80, -80, -80, -79, -79, -79, -78, -78, -78, + -77, -77, -77, -77, -76, -76, -76, -75, -75, -75, -74, -74, + -74, -74, -73, -73, -73, -72, -72, -72, -71, -71, -71, -71, + -70, -70, -70, -69, -69, -69, -68, -68, -68, -68, -67, -67, + -67, -66, -66, -66, -65, -65, -65, -65, -64, -64, -64, -63, + -63, -63, -62, -62, -62, -62, -61, -61, -61, -60, -60, -60, + -59, -59, -59, -58, -58, -58, -58, -57, -57, -57, -56, -56, + -56, -55, -55, -55, -55, -54, -54, -54, -53, -53, -53, -52, + -52, -52, -52, -51, -51, -51, -50, -50, -50, -49, -49, -49, + -49, -48, -48, -48, -47, -47, -47, -46, -46, -46, -46, -45, + -45, -45, -44, -44, -44, -43, -43, -43, -43, -42, -42, -42, + -41, -41, -41, -40, -40, -40, -40, -39, -39, -39, -38, -38, + -38, -37, -37, -37, -37, -36, -36, -36, -35, -35, -35, -34, + -34, -34, -34, -33, -33, -33, -32, -32, -32, -31, -31, -31, + -30, -30, -30, -30, -29, -29, -29, -28, -28, -28, -27, -27, + -27, -27, -26, -26, -26, -25, -25, -25, -24, -24, -24, -24, + -23, -23, -23, -22, -22, -22, -21, -21, -21, -21, -20, -20, + -20, -19, -19, -19, -18, -18, -18, -18, -17, -17, -17, -16, + -16, -16, -15, -15, -15, -15, -14, -14, -14, -13, -13, -13, + -12, -12, -12, -12, -11, -11, -11, -10, -10, -10, -9, -9, + -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -6, + -5, -5, -5, -4, -4, -4, -3, -3, -3, -3, -2, -2, + -2, -1, -1, -1, 0, 0, 0, 1, 1, 1, 1, 2, + 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, + 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, + 9, 10, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13, + 13, 13, 13, 14, 14, 14, 15, 15, 15, 16, 16, 16, + 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20, + 20, 20, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, + 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 27, 27, + 27, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 31, + 31, 31, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, + 35, 35, 35, 35, 36, 36, 36, 37, 37, 37, 38, 38, + 38, 38, 39, 39, 39, 40, 40, 40, 41, 41, 41, 41, + 42, 42, 42, 43, 43, 43, 44, 44, 44, 44, 45, 45, + 45, 46, 46, 46, 47, 47, 47, 47, 48, 48, 48, 49, + 49, 49, 50, 50, 50, 50, 51, 51, 51, 52, 52, 52, + 53, 53, 53, 53, 54, 54, 54, 55, 55, 55, 56, 56, + 56, 56, 57, 57, 57, 58, 58, 58, 59, 59, 59, 60, + 60, 60, 60, 61, 61, 61, 62, 62, 62, 63, 63, 63, + 63, 64, 64, 64, 65, 65, 65, 66, 66, 66, 66, 67, + 67, 67, 68, 68, 68, 69, 69, 69, 69, 70, 70, 70, + 71, 71, 71, 72, 72, 72, 72, 73, 73, 73, 74, 74, + 74, 75, 75, 75, 75, 76, 76, 76, 77, 77, 77, 78, + 78, 78, 78, 79, 79, 79, 80, 80, 80, 81, 81, 81, + 81, 82, 82, 82, 83, 83, 83, 84, 84, 84, 84, 85, + 85, 85, 86, 86, 86, 87, 87, 87, 88, 88, 88, 88, + 89, 89, 89, 90, 90, 90, 91, 91, 91, 91, 92, 92, + 92, 93, 93, 93, 94, 94, 94, 94, 95, 95, 95, 96, + 96, 96, 97, 97, 97, 97, 98, 98, 98, 99, 99, 99, + 100, 100, 100, 100, 101, 101, 101, 102, 102, 102, 103, 103, + 103, 103, 104, 104, 104, 105, 105, 105, 106, 106, 106, 106, + 107, 107, 107, 108, 108, 108, 109, 109, 109, 109, 110, 110, + 110, 111, 111, 111, 112, 112, 112, 112, 113, 113, 113, 114, + 114, 114, 115, 115, 115, 116, 116, 116, 116, 117, 117, 117, + 118, 118, 118, 119, 119, 119, 119, 120, 120, 120, 121, 121, + 121, 122, 122, 122, 122, 123, 123, 123, 124, 124, 124, 125, + 125, 125, 125, 126, 126, 126, 127, 127, 127, 128, 128, 128, + 128, 129, 129, 129, 130, 130, 130, 131, 131, 131, 131, 132, + 132, 132, 133, 133, 133, 134, 134, 134, 134, 135, 135, 135, + 136, 136, 136, 137, 137, 137, 137, 138, 138, 138, 139, 139, + 139, 140, 140, 140, 140, 141, 141, 141, 142, 142, 142, 143, + 143, 143, 143, 144, 144, 144, 145, 145, 145, 146, 146, 146, + 147, 147, 147, 147, 148, 148, 148, 149, 149, 149, 150, 150, + 150, 150, 151, 151, 151, 152, 152, 152, 153, 153, 153, 153, + 154, 154, 154, 155, 155, 155, 156, 156, 156, 156, 157, 157, + 157, 158, 158, 158, 159, 159, 159, 159, 160, 160, 160, 161, + 161, 161, 162, 162, 162, 162, 163, 163, 163, 164, 164, 164, + 165, 165, 165, 165, 166, 166, 166, 167, 167, 167, 168, 168, + 168, 168, 169, 169, 169, 170, 170, 170, 171, 171, 171, 171, + 172, 172, 172, 173, 173, 173, 174, 174, 174, 175, 175, 175, + 175, 176, 176, 176, 177, 177, 177, 178, 178, 178, 178, 179, + 179, 179, 180, 180, 180, 181, 181, 181, 181, 182, 182, 182, + 183, 183, 183, 184, 184, 184, 184, 185, 185, 185, 186, 186, + 186, 187, 187, 187, 187, 188, 188, 188, 189, 189, 189, 190, + 190, 190, 190, 191, 191, 191, 192, 192, 192, 193, 193, 193, + 193, 194, 194, 194, 195, 195, 195, 196, 196, 196, 196, 197, + 197, 197, 198, 198, 198, 199, 199, 199, 199, 200, 200, 200, + 201, 201, 201, 202, 202, 202, 202, 203, 203, 203, 204, 204, + 204, 205, 205, 205, 206, 206, 206, 206, 207, 207, 207, 208, + 208, 208, 209, 209, 209, 209, 210, 210, 210, 211, 211, 211, + 212, 212, 212, 212, 213, 213, 213, 214, 214, 214, 215, 215, + 215, 215, 216, 216, 216, 217, 217, 217, 218, 218, 218, 218, + 219, 219, 219, 220, 220, 220, 221, 221, 221, 221, 222, 222, + 222, 223, 223, 223, 224, 224, 224, 224, 225, 225, 225, 226, + 226, 226, 227, 227, 227, 227, 228, 228, 228, 229, 229, 229, + 230, 230, 230, 230, 231, 231, 231, 232, 232, 232, 233, 233, + 233, 234, 234, 234, 234, 235, 235, 235, 236, 236, 236, 237, + 237, 237, 237, 238, 238, 238, 239, 239, 239, 240, 240, 240, + 240, 241, 241, 241, 242, 242, 242, 243, 243, 243, 243, 244, + 244, 244, 245, 245, 245, 246, 246, 246, 246, 247, 247, 247, + 248, 248, 248, 249, 249, 249, 249, 250, 250, 250, 251, 251, + 251, 252, 252, 252, 252, 253, 253, 253, 254, 254, 254, 255, + 255, 255, 255, 256, 256, 256, 257, 257, 257, 258, 258, 258, + 258, 259, 259, 259, 260, 260, 260, 261, 261, 261, 261, 262, + 262, 262, 263, 263, 263, 264, 264, 264, 265, 265, 265, 265, + 266, 266, 266, 267, 267, 267, 268, 268, 268, 268, 269, 269, + 269, 270, 270, 270, 271, 271, 271, 271, 272, 272, 272, 273, + 273, 273, 274, 274, 274, 274, 275, 275, 275, 276, 276, 276, + 277, 277, 277, 277, 278, 278, 278, 279, 279, 279, 280, 280, + 280, 280, 281, 281, 281, 282, 282, 282, 283, 283, 283, 283, + 284, 284, 284, 285, 285, 285, 286, 286, 286, 286, 287, 287, + 287, 288, 288, 288, 289, 289, 289, 289, 290, 290, 290, 291, + 291, 291, 292, 292, 292, 293, 293, 293, +}; + +static const gunichar2 Formatter_DigitLowerTable [] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' +}; + +static const gunichar2 Formatter_DigitUpperTable [] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' +}; + +static const gint64 Formatter_TenPowersList [] = { + 1LL, + 10LL, + 100LL, + 1000LL, + 10000LL, + 100000LL, + 1000000LL, + 10000000LL, + 100000000LL, + 1000000000LL, + 10000000000LL, + 100000000000LL, + 1000000000000LL, + 10000000000000LL, + 100000000000000LL, + 1000000000000000LL, + 10000000000000000LL, + 100000000000000000LL, + 1000000000000000000LL, +}; + +// DecHexDigits s a translation table from a decimal number to its +// digits hexadecimal representation (e.g. DecHexDigits [34] = 0x34). +static const gint32 Formatter_DecHexDigits [] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, +}; + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/number-ms.c b/unity-2019.4.24f1-mbe/mono/metadata/number-ms.c new file mode 100644 index 000000000..bb6bc6aea --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/number-ms.c @@ -0,0 +1,331 @@ +/** + * \file + * System.Double, System.Single and System.Number runtime support + * + * Author: + * Ludovic Henry (ludovic@xamarin.com) + * + * Copyright 2015 Xamarin, Inc (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// Files: +// - src/classlibnative/bcltype/number.cpp +// +// Ported from C++ to C and adjusted to Mono runtime + +#include + +#include "number-ms.h" + +static const guint64 rgval64Power10[] = { + /* powers of 10 */ + 0xa000000000000000LL, + 0xc800000000000000LL, + 0xfa00000000000000LL, + 0x9c40000000000000LL, + 0xc350000000000000LL, + 0xf424000000000000LL, + 0x9896800000000000LL, + 0xbebc200000000000LL, + 0xee6b280000000000LL, + 0x9502f90000000000LL, + 0xba43b74000000000LL, + 0xe8d4a51000000000LL, + 0x9184e72a00000000LL, + 0xb5e620f480000000LL, + 0xe35fa931a0000000LL, + + /* powers of 0.1 */ + 0xcccccccccccccccdLL, + 0xa3d70a3d70a3d70bLL, + 0x83126e978d4fdf3cLL, + 0xd1b71758e219652eLL, + 0xa7c5ac471b478425LL, + 0x8637bd05af6c69b7LL, + 0xd6bf94d5e57a42beLL, + 0xabcc77118461ceffLL, + 0x89705f4136b4a599LL, + 0xdbe6fecebdedd5c2LL, + 0xafebff0bcb24ab02LL, + 0x8cbccc096f5088cfLL, + 0xe12e13424bb40e18LL, + 0xb424dc35095cd813LL, + 0x901d7cf73ab0acdcLL, +}; + +static const gint8 rgexp64Power10[] = { + /* exponents for both powers of 10 and 0.1 */ + 4, + 7, + 10, + 14, + 17, + 20, + 24, + 27, + 30, + 34, + 37, + 40, + 44, + 47, + 50, +}; + +static const guint64 rgval64Power10By16[] = { + /* powers of 10^16 */ + 0x8e1bc9bf04000000LL, + 0x9dc5ada82b70b59eLL, + 0xaf298d050e4395d6LL, + 0xc2781f49ffcfa6d4LL, + 0xd7e77a8f87daf7faLL, + 0xefb3ab16c59b14a0LL, + 0x850fadc09923329cLL, + 0x93ba47c980e98cdeLL, + 0xa402b9c5a8d3a6e6LL, + 0xb616a12b7fe617a8LL, + 0xca28a291859bbf90LL, + 0xe070f78d39275566LL, + 0xf92e0c3537826140LL, + 0x8a5296ffe33cc92cLL, + 0x9991a6f3d6bf1762LL, + 0xaa7eebfb9df9de8aLL, + 0xbd49d14aa79dbc7eLL, + 0xd226fc195c6a2f88LL, + 0xe950df20247c83f8LL, + 0x81842f29f2cce373LL, + 0x8fcac257558ee4e2LL, + + /* powers of 0.1^16 */ + 0xe69594bec44de160LL, + 0xcfb11ead453994c3LL, + 0xbb127c53b17ec165LL, + 0xa87fea27a539e9b3LL, + 0x97c560ba6b0919b5LL, + 0x88b402f7fd7553abLL, + 0xf64335bcf065d3a0LL, + 0xddd0467c64bce4c4LL, + 0xc7caba6e7c5382edLL, + 0xb3f4e093db73a0b7LL, + 0xa21727db38cb0053LL, + 0x91ff83775423cc29LL, + 0x8380dea93da4bc82LL, + 0xece53cec4a314f00LL, + 0xd5605fcdcf32e217LL, + 0xc0314325637a1978LL, + 0xad1c8eab5ee43ba2LL, + 0x9becce62836ac5b0LL, + 0x8c71dcd9ba0b495cLL, + 0xfd00b89747823938LL, + 0xe3e27a444d8d991aLL, +}; + +static const gint16 rgexp64Power10By16[] = { + /* exponents for both powers of 10^16 and 0.1^16 */ + 54, + 107, + 160, + 213, + 266, + 319, + 373, + 426, + 479, + 532, + 585, + 638, + 691, + 745, + 798, + 851, + 904, + 957, + 1010, + 1064, + 1117, +}; + +static inline guint64 +digits_to_int (guint16 *p, int count) +{ + g_assert (1 <= count && count <= 9); + guint8 i = 0; + guint64 res = 0; + switch (count) { + case 9: res += 100000000 * (p [i++] - '0'); + case 8: res += 10000000 * (p [i++] - '0'); + case 7: res += 1000000 * (p [i++] - '0'); + case 6: res += 100000 * (p [i++] - '0'); + case 5: res += 10000 * (p [i++] - '0'); + case 4: res += 1000 * (p [i++] - '0'); + case 3: res += 100 * (p [i++] - '0'); + case 2: res += 10 * (p [i++] - '0'); + case 1: res += 1 * (p [i++] - '0'); + } + return res; +} + +static inline guint64 +mul_64_lossy (guint64 a, guint64 b, gint *pexp) +{ + /* it's ok to losse some precision here - it will be called + * at most twice during the conversion, so the error won't + * propagate to any of the 53 significant bits of the result */ + guint64 val = + ((((guint64) (guint32) (a >> 32)) * ((guint64) (guint32) (b >> 32))) ) + + ((((guint64) (guint32) (a >> 32)) * ((guint64) (guint32) (b ))) >> 32) + + ((((guint64) (guint32) (a )) * ((guint64) (guint32) (b >> 32))) >> 32); + + /* normalize */ + if ((val & 0x8000000000000000LL) == 0) { + val <<= 1; + *pexp -= 1; + } + + return val; +} + +static inline void +number_to_double (MonoNumber *number, gdouble *value) +{ + guint64 val; + guint16 *src; + gint exp, remaining, total, count, scale, absscale, index; + + total = 0; + src = number->digits; + while (*src++) total ++; + + remaining = total; + + src = number->digits; + while (*src == '0') { + remaining --; + src ++; + } + + if (remaining == 0) { + *value = 0; + goto done; + } + + count = MIN (remaining, 9); + remaining -= count; + val = digits_to_int (src, count); + + if (remaining > 0) { + count = MIN (remaining, 9); + remaining -= count; + + /* get the denormalized power of 10 */ + guint32 mult = (guint32) (rgval64Power10 [count - 1] >> (64 - rgexp64Power10 [count - 1])); + val = ((guint64) (guint32) val) * ((guint64) mult) + digits_to_int (src + 9, count); + } + + scale = number->scale - (total - remaining); + absscale = abs (scale); + + if (absscale >= 22 * 16) { + /* overflow / underflow */ + *(guint64*) value = (scale > 0) ? 0x7FF0000000000000LL : 0; + goto done; + } + + exp = 64; + + /* normalize the mantiss */ + if ((val & 0xFFFFFFFF00000000LL) == 0) { val <<= 32; exp -= 32; } + if ((val & 0xFFFF000000000000LL) == 0) { val <<= 16; exp -= 16; } + if ((val & 0xFF00000000000000LL) == 0) { val <<= 8; exp -= 8; } + if ((val & 0xF000000000000000LL) == 0) { val <<= 4; exp -= 4; } + if ((val & 0xC000000000000000LL) == 0) { val <<= 2; exp -= 2; } + if ((val & 0x8000000000000000LL) == 0) { val <<= 1; exp -= 1; } + + index = absscale & 15; + if (index) { + gint multexp = rgexp64Power10 [index - 1]; + /* the exponents are shared between the inverted and regular table */ + exp += (scale < 0) ? (-multexp + 1) : multexp; + + guint64 multval = rgval64Power10 [index + ((scale < 0) ? 15 : 0) - 1]; + val = mul_64_lossy (val, multval, &exp); + } + + index = absscale >> 4; + if (index) { + gint multexp = rgexp64Power10By16 [index - 1]; + /* the exponents are shared between the inverted and regular table */ + exp += (scale < 0) ? (-multexp + 1) : multexp; + + guint64 multval = rgval64Power10By16 [index + ((scale < 0) ? 21 : 0) - 1]; + val = mul_64_lossy (val, multval, &exp); + } + + if ((guint32) val & (1 << 10)) { + /* IEEE round to even */ + guint64 tmp = val + ((1 << 10) - 1) + (((guint32) val >> 11) & 1); + if (tmp < val) { + /* overflow */ + tmp = (tmp >> 1) | 0x8000000000000000LL; + exp += 1; + } + val = tmp; + } + + /* return the exponent to a biased state */ + exp += 0x3FE; + + /* handle overflow, underflow, "Epsilon - 1/2 Epsilon", denormalized, and the normal case */ + if (exp <= 0) { + if (exp == -52 && (val >= 0x8000000000000058LL)) { + /* round X where {Epsilon > X >= 2.470328229206232730000000E-324} up to Epsilon (instead of down to zero) */ + val = 0x0000000000000001LL; + } else if (exp <= -52) { + /* underflow */ + val = 0; + } else { + /* denormalized */ + val >>= (-exp + 11 + 1); + } + } else if (exp >= 0x7FF) { + /* overflow */ + val = 0x7FF0000000000000LL; + } else { + /* normal postive exponent case */ + val = ((guint64) exp << 52) + ((val >> 11) & 0x000FFFFFFFFFFFFFLL); + } + + *(guint64*) value = val; + +done: + if (number->sign) + *(guint64*) value |= 0x8000000000000000LL; +} + +gint +mono_double_from_number (gpointer from, MonoDouble *target) +{ + MonoDouble_double res; + guint e, mant_lo, mant_hi; + + res.d = 0; + + number_to_double ((MonoNumber*) from, &res.d); + e = res.s.exp; + mant_lo = res.s.mantLo; + mant_hi = res.s.mantHi; + + if (e == 0x7ff) + return 0; + + if (e == 0 && mant_lo == 0 && mant_hi == 0) + res.d = 0; + + *target = res.s; + return 1; +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/number-ms.h b/unity-2019.4.24f1-mbe/mono/metadata/number-ms.h new file mode 100644 index 000000000..f20cc95cc --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/number-ms.h @@ -0,0 +1,67 @@ +/** + * \file + */ + +#ifndef __MONO_NUMBER_MS_H__ +#define __MONO_NUMBER_MS_H__ + +#include + +// Double floating point Bias +#define MONO_DOUBLE_BIAS 1022 + +// Structure to access an encoded double floating point +typedef struct { +#if G_BYTE_ORDER == G_BIG_ENDIAN + guint sign : 1; + guint exp : 11; + guint mantHi : 20; + guint mantLo : 32; +#else // BIGENDIAN + guint mantLo : 32; + guint mantHi : 20; + guint exp : 11; + guint sign : 1; +#endif +} MonoDouble; + +typedef union { + MonoDouble s; + gdouble d; +} MonoDouble_double; + +// Single floating point Bias +#define MONO_SINGLE_BIAS 126 + +// Structure to access an encoded single floating point +typedef struct { +#if G_BYTE_ORDER == G_BIG_ENDIAN + guint sign : 1; + guint exp : 8; + guint mant : 23; +#else + guint mant : 23; + guint exp : 8; + guint sign : 1; +#endif +} MonoSingle; + +typedef union { + MonoSingle s; + gfloat f; +} MonoSingle_float; + +#define MONO_NUMBER_MAXDIGITS 50 + +typedef struct { + gint32 precision; + gint32 scale; + gint32 sign; + guint16 digits [MONO_NUMBER_MAXDIGITS + 1]; + guint16 *allDigits; +} MonoNumber; + +gint +mono_double_from_number (gpointer from, MonoDouble *target); + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/object-internals.h b/unity-2019.4.24f1-mbe/mono/metadata/object-internals.h new file mode 100644 index 000000000..ed7bdd3f1 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/object-internals.h @@ -0,0 +1,1966 @@ +/** + * \file + */ + +#ifndef __MONO_OBJECT_INTERNALS_H__ +#define __MONO_OBJECT_INTERNALS_H__ + +#include +#include +#include +#include +#include +#include +#include +#include "mono/utils/mono-compiler.h" +#include "mono/utils/mono-error.h" +#include "mono/utils/mono-error-internals.h" +#include "mono/utils/mono-stack-unwinding.h" +#include "mono/utils/mono-tls.h" +#include "mono/utils/mono-coop-mutex.h" + +/* Use this as MONO_CHECK_ARG_NULL (arg,expr,) in functions returning void */ +#define MONO_CHECK_ARG(arg, expr, retval) G_STMT_START{ \ + if (G_UNLIKELY (!(expr))) \ + { \ + MonoException *ex; \ + char *msg = g_strdup_printf ("assertion `%s' failed", \ + #expr); \ + if (arg) {} /* check if the name exists */ \ + ex = mono_get_exception_argument (#arg, msg); \ + g_free (msg); \ + mono_set_pending_exception (ex); \ + return retval; \ + }; }G_STMT_END + +/* Use this as MONO_CHECK_ARG_NULL (arg,) in functions returning void */ +#define MONO_CHECK_ARG_NULL(arg, retval) G_STMT_START{ \ + if (G_UNLIKELY (arg == NULL)) \ + { \ + MonoException *ex; \ + if (arg) {} /* check if the name exists */ \ + ex = mono_get_exception_argument_null (#arg); \ + mono_set_pending_exception (ex); \ + return retval; \ + }; }G_STMT_END + +/* Use this as MONO_ARG_NULL (arg,) in functions returning void */ +#define MONO_CHECK_NULL(arg, retval) G_STMT_START{ \ + if (G_UNLIKELY (arg == NULL)) \ + { \ + MonoException *ex; \ + if (arg) {} /* check if the name exists */ \ + ex = mono_get_exception_null_reference (); \ + mono_set_pending_exception (ex); \ + return retval; \ + }; }G_STMT_END + +#define mono_string_builder_capacity(sb) sb->chunkOffset + sb->chunkChars->max_length +#define mono_string_builder_string_length(sb) sb->chunkOffset + sb->chunkLength + +/* + * Macros which cache the results of lookups locally. + * These should be used instead of the original versions, if the __GNUC__ + * restriction is acceptable. + */ + +#ifdef __GNUC__ + +/* name should be a compile-time constant */ +#define mono_class_get_field_from_name_cached(klass,name) ({ \ + static MonoClassField *tmp_field; \ + if (!tmp_field) { \ + tmp_field = mono_class_get_field_from_name ((klass), (name)); \ + g_assert (tmp_field); \ + }; \ + tmp_field; }) +/* eclass should be a run-time constant */ +#define mono_array_class_get_cached(eclass,rank) ({ \ + static MonoClass *tmp_klass; \ + if (!tmp_klass) { \ + tmp_klass = mono_array_class_get ((eclass), (rank)); \ + g_assert (tmp_klass); \ + }; \ + tmp_klass; }) +/* eclass should be a run-time constant */ +#define mono_array_new_cached(domain, eclass, size, error) ({ \ + MonoVTable *__vtable = mono_class_vtable ((domain), mono_array_class_get_cached ((eclass), 1)); \ + MonoArray *__arr = mono_array_new_specific_checked (__vtable, (size), (error)); \ + __arr; }) + +#else + +#define mono_class_get_field_from_name_cached(klass,name) mono_class_get_field_from_name ((klass), (name)) +#define mono_array_class_get_cached(eclass,rank) mono_array_class_get ((eclass), (rank)) +#define mono_array_new_cached(domain, eclass, size, error) mono_array_new_specific_checked (mono_class_vtable ((domain), mono_array_class_get_cached ((eclass), 1)), (size), (error)) + +#endif + +#ifdef MONO_BIG_ARRAYS +typedef uint64_t mono_array_size_t; +typedef int64_t mono_array_lower_bound_t; +#define MONO_ARRAY_MAX_INDEX G_MAXINT64 +#define MONO_ARRAY_MAX_SIZE G_MAXUINT64 +#else +typedef uint32_t mono_array_size_t; +typedef int32_t mono_array_lower_bound_t; +#define MONO_ARRAY_MAX_INDEX ((int32_t) 0x7fffffff) +#define MONO_ARRAY_MAX_SIZE ((uint32_t) 0xffffffff) +#endif + +typedef struct { + mono_array_size_t length; + mono_array_lower_bound_t lower_bound; +} MonoArrayBounds; + +struct _MonoArray { + MonoObject obj; + /* bounds is NULL for szarrays */ + MonoArrayBounds *bounds; + /* total number of elements of the array */ + mono_array_size_t max_length; + /* we use double to ensure proper alignment on platforms that need it */ + double vector [MONO_ZERO_LEN_ARRAY]; +}; + +#define MONO_SIZEOF_MONO_ARRAY (sizeof (MonoArray) - MONO_ZERO_LEN_ARRAY * sizeof (double)) + +struct _MonoString { + MonoObject object; + int32_t length; + mono_unichar2 chars [MONO_ZERO_LEN_ARRAY]; +}; + +#define mono_object_class(obj) (((MonoObject*)(obj))->vtable->klass) +#define mono_object_domain(obj) (((MonoObject*)(obj))->vtable->domain) + +#define mono_string_chars_fast(s) ((mono_unichar2*)(s)->chars) +#define mono_string_length_fast(s) ((s)->length) + +#define mono_array_length_fast(array) ((array)->max_length) +#define mono_array_addr_with_size_fast(array,size,index) ( ((char*)(array)->vector) + (size) * (index) ) + +#define mono_array_addr_fast(array,type,index) ((type*)(void*) mono_array_addr_with_size_fast (array, sizeof (type), index)) +#define mono_array_get_fast(array,type,index) ( *(type*)mono_array_addr_fast ((array), type, (index)) ) +#define mono_array_set_fast(array,type,index,value) \ + do { \ + type *__p = (type *) mono_array_addr_fast ((array), type, (index)); \ + *__p = (value); \ + } while (0) +#define mono_array_setref_fast(array,index,value) \ + do { \ + void **__p = (void **) mono_array_addr_fast ((array), void*, (index)); \ + mono_gc_wbarrier_set_arrayref ((array), __p, (MonoObject*)(value)); \ + /* *__p = (value);*/ \ + } while (0) +#define mono_array_memcpy_refs_fast(dest,destidx,src,srcidx,count) \ + do { \ + void **__p = (void **) mono_array_addr_fast ((dest), void*, (destidx)); \ + void **__s = mono_array_addr_fast ((src), void*, (srcidx)); \ + mono_gc_wbarrier_arrayref_copy (__p, __s, (count)); \ + } while (0) + +static inline gboolean +mono_handle_array_has_bounds (MonoArrayHandle arr) +{ + return MONO_HANDLE_GETVAL (arr, bounds) != NULL; +} + +static inline void +mono_handle_array_get_bounds_dim (MonoArrayHandle arr, gint32 dim, MonoArrayBounds *bounds) +{ + MonoArrayBounds *src = MONO_HANDLE_GETVAL (arr, bounds); + memcpy (bounds, &src[dim], sizeof (MonoArrayBounds)); +} + + +typedef struct { + MonoObject obj; + MonoObject *identity; +} MonoMarshalByRefObject; + +/* This is a copy of System.AppDomain */ +struct _MonoAppDomain { + MonoMarshalByRefObject mbr; + MonoDomain *data; +}; + +/* Safely access System.AppDomain from native code */ +TYPED_HANDLE_DECL (MonoAppDomain); + +/* Safely access System.AppDomainSetup from native code. (struct is in domain-internals.h) */ +TYPED_HANDLE_DECL (MonoAppDomainSetup); + +typedef struct _MonoStringBuilder MonoStringBuilder; + +struct _MonoStringBuilder { + MonoObject object; + MonoArray *chunkChars; + MonoStringBuilder* chunkPrevious; // Link to the block logically before this block + int chunkLength; // The index in ChunkChars that represent the end of the block + int chunkOffset; // The logial offset (sum of all characters in previous blocks) + int maxCapacity; +}; + +typedef struct { + MonoType *type; + gpointer value; + MonoClass *klass; +} MonoTypedRef; + +typedef struct { + gpointer args; +} MonoArgumentHandle; + +typedef struct { + MonoMethodSignature *sig; + gpointer args; + gint32 next_arg; + gint32 num_args; +} MonoArgIterator; + +struct _MonoException { + MonoObject object; + MonoString *class_name; + MonoString *message; + MonoObject *_data; + MonoObject *inner_ex; + MonoString *help_link; + /* Stores the IPs and the generic sharing infos + (vtable/MRGCTX) of the frames. */ + MonoArray *trace_ips; + MonoString *stack_trace; + MonoString *remote_stack_trace; + gint32 remote_stack_index; + /* Dynamic methods referenced by the stack trace */ + MonoObject *dynamic_methods; + gint32 hresult; + MonoString *source; + MonoObject *serialization_manager; + MonoObject *captured_traces; + MonoArray *native_trace_ips; +}; + +typedef struct { + MonoException base; +} MonoSystemException; + +typedef struct { + MonoSystemException base; + MonoString *param_name; +} MonoArgumentException; + +typedef struct { + MonoObject object; + MonoObject *async_state; + MonoObject *handle; + MonoObject *async_delegate; + gpointer *data; + MonoObject *object_data; + MonoBoolean sync_completed; + MonoBoolean completed; + MonoBoolean endinvoke_called; + MonoObject *async_callback; + MonoObject *execution_context; + MonoObject *original_context; + gint64 add_time; +} MonoAsyncResult; + +typedef struct { + MonoMarshalByRefObject object; + gpointer handle; +} MonoWaitHandle; + +/* This is a copy of System.Runtime.Remoting.Messaging.CallType */ +typedef enum { + CallType_Sync = 0, + CallType_BeginInvoke = 1, + CallType_EndInvoke = 2, + CallType_OneWay = 3 +} MonoCallType; + +/* MonoSafeHandle is in class-internals.h. */ +/* Safely access System.Net.Sockets.SafeSocketHandle from native code */ +TYPED_HANDLE_DECL (MonoSafeHandle); + +/* This corresponds to System.Type */ +struct _MonoReflectionType { + MonoObject object; + MonoType *type; +}; + +/* Safely access System.Type from native code */ +TYPED_HANDLE_DECL (MonoReflectionType); + +/* This corresponds to System.RuntimeType */ +typedef struct { + MonoReflectionType type; + MonoObject *type_info; +} MonoReflectionMonoType; + +typedef struct { + MonoObject object; + MonoReflectionType *class_to_proxy; + MonoObject *context; + MonoObject *unwrapped_server; + gint32 target_domain_id; + MonoString *target_uri; + MonoObject *object_identity; + MonoObject *obj_TP; + MonoObject *stub_data; +} MonoRealProxy; + +/* Safely access System.Runtime.Remoting.Proxies.RealProxy from native code */ +TYPED_HANDLE_DECL (MonoRealProxy); + +typedef struct { + MonoMarshalByRefObject object; + gpointer iunknown; + GHashTable* itf_hash; + MonoObject *synchronization_context; +} MonoComObject; + +typedef struct { + MonoRealProxy real_proxy; + MonoComObject *com_object; + gint32 ref_count; +} MonoComInteropProxy; + +typedef struct { + MonoObject object; + MonoRealProxy *rp; + MonoRemoteClass *remote_class; + MonoBoolean custom_type_info; +} MonoTransparentProxy; + +/* Safely access System.Runtime.Remoting.Proxies.TransparentProxy from native code */ +TYPED_HANDLE_DECL (MonoTransparentProxy); + +typedef struct { + MonoObject obj; + MonoReflectionMethod *method; + MonoArray *args; + MonoArray *names; + MonoArray *arg_types; + MonoObject *ctx; + MonoObject *rval; + MonoObject *exc; + MonoAsyncResult *async_result; + guint32 call_type; +} MonoMethodMessage; + +/* Keep in sync with the System.MonoAsyncCall */ +typedef struct { + MonoObject object; + MonoMethodMessage *msg; + MonoMethod *cb_method; + MonoDelegate *cb_target; + MonoObject *state; + MonoObject *res; + MonoArray *out_args; +} MonoAsyncCall; + +typedef struct { + MonoObject obj; + gint32 il_offset; + gint32 native_offset; + gint64 method_address; + gint32 method_index; + MonoReflectionMethod *method; + MonoString *filename; + gint32 line; + gint32 column; + MonoString *internal_method_name; +} MonoStackFrame; + +typedef enum { + MONO_THREAD_FLAG_DONT_MANAGE = 1, // Don't wait for or abort this thread + MONO_THREAD_FLAG_NAME_SET = 2, // Thread name set from managed code + MONO_THREAD_FLAG_APPDOMAIN_ABORT = 4, // Current requested abort originates from appdomain unload +} MonoThreadFlags; + +struct _MonoInternalThread { + MonoObject obj; + volatile int lock_thread_id; /* to be used as the pre-shifted thread id in thin locks. Used for appdomain_ref push/pop */ + MonoThreadHandle *handle; + gpointer native_handle; + gpointer unused3; + gunichar2 *name; + guint32 name_len; + guint32 state; + MonoException *abort_exc; + int abort_state_handle; + guint64 tid; /* This is accessed as a gsize in the code (so it can hold a 64bit pointer on systems that need it), but needs to reserve 64 bits of space on all machines as it corresponds to a field in managed code */ + gsize debugger_thread; // FIXME switch to bool as soon as CI testing with corlib version bump works + gpointer *static_data; + void *thread_info; /*This is MonoThreadInfo*, but to simplify dependencies, let's make it a void* here. */ + MonoAppContext *current_appcontext; + MonoThread *root_domain_thread; + MonoObject *_serialized_principal; + int _serialized_principal_version; + gpointer appdomain_refs; + /* This is modified using atomic ops, so keep it a gint32 */ + gint32 __interruption_requested; + MonoCoopMutex *synch_cs; + MonoBoolean threadpool_thread; + MonoBoolean thread_interrupt_requested; + int stack_size; + guint8 apartment_state; + gint32 critical_region_level; + gint32 managed_id; + guint32 small_id; + MonoThreadManageCallback manage_callback; + gpointer unused4; + gsize flags; + gpointer thread_pinning_ref; + gsize __abort_protected_block_count; + gint32 priority; + GPtrArray *owned_mutexes; + MonoOSEvent *suspended; + gint32 self_suspended; // TRUE | FALSE + + gsize thread_state; + /* + * These fields are used to avoid having to increment corlib versions + * when a new field is added to this structure. + * Please synchronize any changes with InternalThread in Thread.cs, i.e. add the + * same field there. + */ + gsize unused2; + + /* This is used only to check that we are in sync between the representation + * of MonoInternalThread in native and InternalThread in managed + * + * DO NOT RENAME! DO NOT ADD FIELDS AFTER! */ + gpointer last; +}; + +/* It's safe to access System.Threading.InternalThread from native code via a + * raw pointer because all instances should be pinned. But for uniformity of + * icall wrapping, let's declare a MonoInternalThreadHandle anyway. + */ +TYPED_HANDLE_DECL (MonoInternalThread); + +struct _MonoThread { + MonoObject obj; + struct _MonoInternalThread *internal_thread; + MonoObject *start_obj; + MonoException *pending_exception; +}; + +typedef struct { + guint32 state; + MonoObject *additional; +} MonoStreamingContext; + +typedef struct { + MonoObject obj; + MonoBoolean readOnly; + MonoString *AMDesignator; + MonoString *PMDesignator; + MonoString *DateSeparator; + MonoString *TimeSeparator; + MonoString *ShortDatePattern; + MonoString *LongDatePattern; + MonoString *ShortTimePattern; + MonoString *LongTimePattern; + MonoString *MonthDayPattern; + MonoString *YearMonthPattern; + guint32 FirstDayOfWeek; + guint32 CalendarWeekRule; + MonoArray *AbbreviatedDayNames; + MonoArray *DayNames; + MonoArray *MonthNames; + MonoArray *GenitiveMonthNames; + MonoArray *AbbreviatedMonthNames; + MonoArray *GenitiveAbbreviatedMonthNames; + MonoArray *ShortDatePatterns; + MonoArray *LongDatePatterns; + MonoArray *ShortTimePatterns; + MonoArray *LongTimePatterns; + MonoArray *MonthDayPatterns; + MonoArray *YearMonthPatterns; + MonoArray *ShortestDayNames; +} MonoDateTimeFormatInfo; + +typedef struct +{ + MonoObject obj; + MonoArray *numberGroupSizes; + MonoArray *currencyGroupSizes; + MonoArray *percentGroupSizes; + MonoString *positiveSign; + MonoString *negativeSign; + MonoString *numberDecimalSeparator; + MonoString *numberGroupSeparator; + MonoString *currencyGroupSeparator; + MonoString *currencyDecimalSeparator; + MonoString *currencySymbol; + MonoString *ansiCurrencySymbol; /* unused */ + MonoString *naNSymbol; + MonoString *positiveInfinitySymbol; + MonoString *negativeInfinitySymbol; + MonoString *percentDecimalSeparator; + MonoString *percentGroupSeparator; + MonoString *percentSymbol; + MonoString *perMilleSymbol; + MonoString *nativeDigits; /* unused */ + gint32 dataItem; /* unused */ + guint32 numberDecimalDigits; + gint32 currencyDecimalDigits; + gint32 currencyPositivePattern; + gint32 currencyNegativePattern; + gint32 numberNegativePattern; + gint32 percentPositivePattern; + gint32 percentNegativePattern; + gint32 percentDecimalDigits; +} MonoNumberFormatInfo; + +typedef struct { + MonoObject obj; + gint32 lcid; + MonoString *icu_name; + gpointer ICU_collator; +} MonoCompareInfo; + +typedef struct { + MonoObject obj; + MonoString *NativeName; + MonoArray *ShortDatePatterns; + MonoArray *YearMonthPatterns; + MonoArray *LongDatePatterns; + MonoString *MonthDayPattern; + + MonoArray *EraNames; + MonoArray *AbbreviatedEraNames; + MonoArray *AbbreviatedEnglishEraNames; + MonoArray *DayNames; + MonoArray *AbbreviatedDayNames; + MonoArray *SuperShortDayNames; + MonoArray *MonthNames; + MonoArray *AbbreviatedMonthNames; + MonoArray *GenitiveMonthNames; + MonoArray *GenitiveAbbreviatedMonthNames; +} MonoCalendarData; + +typedef struct { + MonoObject obj; + MonoString *AMDesignator; + MonoString *PMDesignator; + MonoString *TimeSeparator; + MonoArray *LongTimePatterns; + MonoArray *ShortTimePatterns; + guint32 FirstDayOfWeek; + guint32 CalendarWeekRule; +} MonoCultureData; + +typedef struct { + MonoObject obj; + MonoBoolean is_read_only; + gint32 lcid; + gint32 parent_lcid; + gint32 datetime_index; + gint32 number_index; + gint32 calendar_type; + MonoBoolean use_user_override; + MonoNumberFormatInfo *number_format; + MonoDateTimeFormatInfo *datetime_format; + MonoObject *textinfo; + MonoString *name; + MonoString *englishname; + MonoString *nativename; + MonoString *iso3lang; + MonoString *iso2lang; + MonoString *win3lang; + MonoString *territory; + MonoArray *native_calendar_names; + MonoCompareInfo *compareinfo; + const void* text_info_data; +} MonoCultureInfo; + +typedef struct { + MonoObject obj; + gint32 geo_id; + MonoString *iso2name; + MonoString *iso3name; + MonoString *win3name; + MonoString *english_name; + MonoString *native_name; + MonoString *currency_symbol; + MonoString *iso_currency_symbol; + MonoString *currency_english_name; + MonoString *currency_native_name; +} MonoRegionInfo; + +typedef struct { + MonoObject obj; + MonoString *str; + gint32 options; + MonoArray *key; + gint32 lcid; +} MonoSortKey; + +typedef struct { + MonoObject object; + guint32 intType; +} MonoInterfaceTypeAttribute; + +/* + * Callbacks supplied by the runtime and called by the modules in metadata/ + * This interface is easier to extend than adding a new function type + + * a new 'install' function for every callback. + */ +typedef struct { + gpointer (*create_ftnptr) (MonoDomain *domain, gpointer addr); + gpointer (*get_addr_from_ftnptr) (gpointer descr); + char* (*get_runtime_build_info) (void); + gpointer (*get_vtable_trampoline) (MonoVTable *vtable, int slot_index); + gpointer (*get_imt_trampoline) (MonoVTable *vtable, int imt_slot_index); + gboolean (*imt_entry_inited) (MonoVTable *vtable, int imt_slot_index); + void (*set_cast_details) (MonoClass *from, MonoClass *to); + void (*debug_log) (int level, MonoString *category, MonoString *message); + gboolean (*debug_log_is_enabled) (void); + void (*init_delegate) (MonoDelegate *del); + MonoObject* (*runtime_invoke) (MonoMethod *method, void *obj, void **params, MonoObject **exc, MonoError *error); + void* (*compile_method) (MonoMethod *method, MonoError *error); + gpointer (*create_jump_trampoline) (MonoDomain *domain, MonoMethod *method, gboolean add_sync_wrapper, MonoError *error); + gpointer (*create_jit_trampoline) (MonoDomain *domain, MonoMethod *method, MonoError *error); + /* used to free a dynamic method */ + void (*free_method) (MonoDomain *domain, MonoMethod *method); + gpointer (*create_remoting_trampoline) (MonoDomain *domain, MonoMethod *method, MonoRemotingTarget target, MonoError *error); + gpointer (*create_delegate_trampoline) (MonoDomain *domain, MonoClass *klass); + gpointer (*interp_get_remoting_invoke) (gpointer imethod, MonoError *error); + GHashTable *(*get_weak_field_indexes) (MonoImage *image); +} MonoRuntimeCallbacks; + +typedef gboolean (*MonoInternalStackWalk) (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data); +typedef gboolean (*MonoInternalExceptionFrameWalk) (MonoMethod *method, gpointer ip, size_t native_offset, gboolean managed, gpointer user_data); + +typedef struct { + void (*mono_walk_stack_with_ctx) (MonoInternalStackWalk func, MonoContext *ctx, MonoUnwindOptions options, void *user_data); + void (*mono_walk_stack_with_state) (MonoInternalStackWalk func, MonoThreadUnwindState *state, MonoUnwindOptions options, void *user_data); + void (*mono_raise_exception) (MonoException *ex); + void (*mono_raise_exception_with_ctx) (MonoException *ex, MonoContext *ctx); + gboolean (*mono_exception_walk_trace) (MonoException *ex, MonoInternalExceptionFrameWalk func, gpointer user_data); + gboolean (*mono_install_handler_block_guard) (MonoThreadUnwindState *unwind_state); + gboolean (*mono_current_thread_has_handle_block_guard) (void); + gboolean (*mono_above_abort_threshold) (void); + void (*mono_clear_abort_threshold) (void); + void (*mono_reraise_exception) (MonoException *ex); +} MonoRuntimeExceptionHandlingCallbacks; + +MONO_COLD void mono_set_pending_exception (MonoException *exc); + +/* remoting and async support */ + +MonoAsyncResult * +mono_async_result_new (MonoDomain *domain, gpointer handle, + MonoObject *state, gpointer data, MonoObject *object_data, MonoError *error); + +MonoObject * +ves_icall_System_Runtime_Remoting_Messaging_AsyncResult_Invoke (MonoAsyncResult *ares); + +MonoWaitHandle * +mono_wait_handle_new (MonoDomain *domain, gpointer handle, MonoError *error); + +gpointer +mono_wait_handle_get_handle (MonoWaitHandle *handle); + +gboolean +mono_message_init (MonoDomain *domain, MonoMethodMessage *this_obj, + MonoReflectionMethod *method, MonoArray *out_args, MonoError *error); + +MonoObject * +mono_message_invoke (MonoObject *target, MonoMethodMessage *msg, + MonoObject **exc, MonoArray **out_args, MonoError *error); + +MonoMethodMessage * +mono_method_call_message_new (MonoMethod *method, gpointer *params, MonoMethod *invoke, + MonoDelegate **cb, MonoObject **state, MonoError *error); + +void +mono_method_return_message_restore (MonoMethod *method, gpointer *params, MonoArray *out_args, MonoError *error); + +gboolean +mono_delegate_ctor_with_method (MonoObjectHandle this_obj, MonoObjectHandle target, gpointer addr, MonoMethod *method, MonoError *error); + +gboolean +mono_delegate_ctor (MonoObjectHandle this_obj, MonoObjectHandle target, gpointer addr, MonoError *error); + +void +mono_runtime_free_method (MonoDomain *domain, MonoMethod *method); + +void +mono_install_callbacks (MonoRuntimeCallbacks *cbs); + +MonoRuntimeCallbacks* +mono_get_runtime_callbacks (void); + +void +mono_install_eh_callbacks (MonoRuntimeExceptionHandlingCallbacks *cbs); + +MonoRuntimeExceptionHandlingCallbacks * +mono_get_eh_callbacks (void); + +void +mono_raise_exception_deprecated (MonoException *ex); + +void +mono_reraise_exception_deprecated (MonoException *ex); + +void +mono_raise_exception_with_context (MonoException *ex, MonoContext *ctx); + +void +mono_type_initialization_init (void); + +void +mono_type_initialization_cleanup (void); + +int +mono_thread_kill (MonoInternalThread *thread, int signal); + +MonoNativeTlsKey +mono_thread_get_tls_key (void); + +gint32 +mono_thread_get_tls_offset (void); + +MonoNativeTlsKey +mono_domain_get_tls_key (void); + +gint32 +mono_domain_get_tls_offset (void); + +/* Reflection and Reflection.Emit support */ + +/* + * Handling System.Type objects: + * + * Fields defined as System.Type in managed code should be defined as MonoObject* + * in unmanaged structures, and the monotype_cast () function should be used for + * casting them to MonoReflectionType* to avoid crashes/security issues when + * encountering instances of user defined subclasses of System.Type. + */ + +#define IS_MONOTYPE(obj) (!(obj) || (((MonoObject*)(obj))->vtable->klass->image == mono_defaults.corlib && ((MonoReflectionType*)(obj))->type != NULL)) + +#define IS_MONOTYPE_HANDLE(obj) IS_MONOTYPE (MONO_HANDLE_RAW (obj)) + +/* This should be used for accessing members of Type[] arrays */ +#define mono_type_array_get(arr,index) monotype_cast (mono_array_get ((arr), gpointer, (index))) + +/* + * Cast an object to MonoReflectionType, making sure it is a System.MonoType or + * a subclass of it. + */ +static inline MonoReflectionType* +monotype_cast (MonoObject *obj) +{ + g_assert (IS_MONOTYPE (obj)); + + return (MonoReflectionType*)obj; +} + +/* + * The following structure must match the C# implementation in our corlib. + */ + +struct _MonoReflectionMethod { + MonoObject object; + MonoMethod *method; + MonoString *name; + MonoReflectionType *reftype; +}; + +/* Safely access System.Reflection.MonoMethod from native code */ +TYPED_HANDLE_DECL (MonoReflectionMethod); + +struct _MonoDelegate { + MonoObject object; + /* The compiled code of the target method */ + gpointer method_ptr; + /* The invoke code */ + gpointer invoke_impl; + MonoObject *target; + MonoMethod *method; + gpointer delegate_trampoline; + /* Extra argument passed to the target method in llvmonly mode */ + gpointer extra_arg; + /* + * If non-NULL, this points to a memory location which stores the address of + * the compiled code of the method, or NULL if it is not yet compiled. + */ + guint8 **method_code; + MonoReflectionMethod *method_info; + MonoReflectionMethod *original_method_info; + MonoObject *data; + MonoBoolean method_is_virtual; +}; + +/* Safely access System.Delegate from native code */ +TYPED_HANDLE_DECL (MonoDelegate); + +typedef struct _MonoMulticastDelegate MonoMulticastDelegate; +struct _MonoMulticastDelegate { + MonoDelegate delegate; + MonoArray *delegates; +}; + +/* Safely access System.MulticastDelegate from native code */ +TYPED_HANDLE_DECL (MonoMulticastDelegate); + +struct _MonoReflectionField { + MonoObject object; + MonoClass *klass; + MonoClassField *field; + MonoString *name; + MonoReflectionType *type; + guint32 attrs; +}; + +/* Safely access System.Reflection.MonoField from native code */ +TYPED_HANDLE_DECL (MonoReflectionField); + +struct _MonoReflectionProperty { + MonoObject object; + MonoClass *klass; + MonoProperty *property; +}; + +/* Safely access System.Reflection.MonoProperty from native code */ +TYPED_HANDLE_DECL (MonoReflectionProperty); + +/*This is System.EventInfo*/ +struct _MonoReflectionEvent { + MonoObject object; + MonoObject *cached_add_event; +}; + +/* Safely access System.Reflection.EventInfo from native code */ +TYPED_HANDLE_DECL (MonoReflectionEvent); + +typedef struct { + MonoReflectionEvent object; + MonoClass *klass; + MonoEvent *event; +} MonoReflectionMonoEvent; + +/* Safely access Systme.Reflection.MonoEvent from native code */ +TYPED_HANDLE_DECL (MonoReflectionMonoEvent); + +typedef struct { + MonoObject object; + MonoReflectionType *ClassImpl; + MonoObject *DefaultValueImpl; + MonoObject *MemberImpl; + MonoString *NameImpl; + gint32 PositionImpl; + guint32 AttrsImpl; + MonoObject *MarshalAsImpl; +} MonoReflectionParameter; + +/* Safely access System.Reflection.ParameterInfo from native code */ +TYPED_HANDLE_DECL (MonoReflectionParameter); + +struct _MonoReflectionMethodBody { + MonoObject object; + MonoArray *clauses; + MonoArray *locals; + MonoArray *il; + MonoBoolean init_locals; + guint32 local_var_sig_token; + guint32 max_stack; +}; + +/* Safely access System.Reflection.MethodBody from native code */ +TYPED_HANDLE_DECL (MonoReflectionMethodBody); + +struct _MonoReflectionAssembly { + MonoObject object; + MonoAssembly *assembly; + MonoObject *resolve_event_holder; + /* CAS related */ + MonoObject *evidence; /* Evidence */ + MonoObject *minimum; /* PermissionSet - for SecurityAction.RequestMinimum */ + MonoObject *optional; /* PermissionSet - for SecurityAction.RequestOptional */ + MonoObject *refuse; /* PermissionSet - for SecurityAction.RequestRefuse */ + MonoObject *granted; /* PermissionSet - for the resolved assembly granted permissions */ + MonoObject *denied; /* PermissionSet - for the resolved assembly denied permissions */ + /* */ + MonoBoolean from_byte_array; + MonoString *name; +}; + +/* Safely access System.Reflection.Assembly from native code */ +TYPED_HANDLE_DECL (MonoReflectionAssembly); + +typedef struct { + MonoReflectionType *utype; + MonoArray *values; + MonoArray *names; +} MonoEnumInfo; + +typedef struct { + MonoReflectionType *parent; + MonoReflectionType *ret; + guint32 attrs; + guint32 implattrs; + guint32 callconv; +} MonoMethodInfo; + +typedef struct { + MonoReflectionType *parent; + MonoReflectionType *declaring_type; + MonoString *name; + MonoReflectionMethod *get; + MonoReflectionMethod *set; + guint32 attrs; +} MonoPropertyInfo; + +typedef struct { + MonoReflectionType *declaring_type; + MonoReflectionType *reflected_type; + MonoString *name; + MonoReflectionMethod *add_method; + MonoReflectionMethod *remove_method; + MonoReflectionMethod *raise_method; + guint32 attrs; + MonoArray *other_methods; +} MonoEventInfo; + +typedef struct { + MonoString *name; + MonoString *name_space; + MonoReflectionType *etype; + MonoReflectionType *nested_in; + MonoReflectionAssembly *assembly; + guint32 rank; + MonoBoolean isprimitive; +} MonoTypeInfo; + +typedef struct { + MonoObject *member; + gint32 code_pos; +} MonoReflectionILTokenInfo; + +typedef struct { + MonoObject object; + MonoArray *code; + gint32 code_len; + gint32 max_stack; + gint32 cur_stack; + MonoArray *locals; + MonoArray *ex_handlers; + gint32 num_token_fixups; + MonoArray *token_fixups; +} MonoReflectionILGen; + +typedef struct { + MonoArray *handlers; + gint32 start; + gint32 len; + gint32 label; +} MonoILExceptionInfo; + +typedef struct { + MonoObject *extype; + gint32 type; + gint32 start; + gint32 len; + gint32 filter_offset; +} MonoILExceptionBlock; + +typedef struct { + MonoObject object; + MonoObject *catch_type; + gint32 filter_offset; + gint32 flags; + gint32 try_offset; + gint32 try_length; + gint32 handler_offset; + gint32 handler_length; +} MonoReflectionExceptionHandlingClause; + + +/* Safely access System.Reflection.ExceptionHandlingClause from native code */ +TYPED_HANDLE_DECL (MonoReflectionExceptionHandlingClause); + +typedef struct { + MonoObject object; + MonoReflectionType *local_type; + MonoBoolean is_pinned; + guint16 local_index; +} MonoReflectionLocalVariableInfo; + +/* Safely access System.Reflection.LocalVariableInfo from native code */ +TYPED_HANDLE_DECL (MonoReflectionLocalVariableInfo); + +typedef struct { + /* + * Must have the same layout as MonoReflectionLocalVariableInfo, since + * LocalBuilder inherits from it under net 2.0. + */ + MonoObject object; + MonoObject *type; + MonoBoolean is_pinned; + guint16 local_index; + MonoString *name; +} MonoReflectionLocalBuilder; + +typedef struct { + MonoObject object; + gint32 count; + gint32 type; + gint32 eltype; + MonoString *guid; + MonoString *mcookie; + MonoString *marshaltype; + MonoObject *marshaltyperef; + gint32 param_num; + MonoBoolean has_size; +} MonoReflectionMarshal; + +typedef struct { + MonoObject object; + MonoObject* methodb; + MonoString *name; + MonoArray *cattrs; + MonoReflectionMarshal *marshal_info; + guint32 attrs; + int position; + guint32 table_idx; + MonoObject *def_value; +} MonoReflectionParamBuilder; + +typedef struct { + MonoObject object; + MonoMethod *mhandle; + MonoReflectionILGen *ilgen; + MonoArray *parameters; + guint32 attrs; + guint32 iattrs; + guint32 table_idx; + guint32 call_conv; + MonoObject *type; + MonoArray *pinfo; + MonoArray *cattrs; + MonoBoolean init_locals; + MonoArray *param_modreq; + MonoArray *param_modopt; + MonoArray *permissions; +} MonoReflectionCtorBuilder; + +/* Safely access System.Reflection.Emit.ConstructorBuilder from native code */ +TYPED_HANDLE_DECL (MonoReflectionCtorBuilder); + +typedef struct { + MonoObject object; + MonoMethod *mhandle; + MonoObject *rtype; + MonoArray *parameters; + guint32 attrs; + guint32 iattrs; + MonoString *name; + guint32 table_idx; + MonoArray *code; + MonoReflectionILGen *ilgen; + MonoObject *type; + MonoArray *pinfo; + MonoArray *cattrs; + MonoArray *override_methods; + MonoString *dll; + MonoString *dllentry; + guint32 charset; + guint32 extra_flags; + guint32 native_cc; + guint32 call_conv; + MonoBoolean init_locals; + MonoGenericContainer *generic_container; + MonoArray *generic_params; + MonoArray *return_modreq; + MonoArray *return_modopt; + MonoArray *param_modreq; + MonoArray *param_modopt; + MonoArray *permissions; +} MonoReflectionMethodBuilder; + +/* Safely access System.Reflection.Emit.MethodBuilder from native code */ +TYPED_HANDLE_DECL (MonoReflectionMethodBuilder); + +typedef struct { + MonoObject object; + MonoMethod *mhandle; + MonoReflectionType *parent; + MonoReflectionType *ret; + MonoArray *parameters; + MonoString *name; + guint32 table_idx; + guint32 call_conv; +} MonoReflectionArrayMethod; + +/* Safely access System.Reflection.Emit.MonoArrayMethod from native code */ +TYPED_HANDLE_DECL (MonoReflectionArrayMethod); + +typedef struct { + MonoArray *data; + MonoString *name; + MonoString *filename; + guint32 attrs; + guint32 offset; + MonoObject *stream; +} MonoReflectionResource; + +typedef struct { + guint32 res_type; + guint32 res_id; + guint32 lang_id; + MonoArray *res_data; +} MonoReflectionWin32Resource; + +typedef struct { + guint32 action; + MonoString *pset; +} MonoReflectionPermissionSet; + +typedef struct { + MonoReflectionAssembly assembly; + MonoDynamicAssembly *dynamic_assembly; + MonoReflectionMethod *entry_point; + MonoArray *modules; + MonoString *name; + MonoString *dir; + MonoArray *cattrs; + MonoArray *resources; + MonoArray *public_key; + MonoString *version; + MonoString *culture; + guint32 algid; + guint32 flags; + guint32 pekind; + MonoBoolean delay_sign; + guint32 access; + MonoArray *loaded_modules; + MonoArray *win32_resources; + /* CAS related */ + MonoArray *permissions_minimum; + MonoArray *permissions_optional; + MonoArray *permissions_refused; + gint32 pe_kind; + gint32 machine; + MonoBoolean corlib_internal; + MonoArray *type_forwarders; + MonoArray *pktoken; /* as hexadecimal byte[] */ +} MonoReflectionAssemblyBuilder; + +/* Safely access System.Reflection.Emit.AssemblyBuilder from native code */ +TYPED_HANDLE_DECL (MonoReflectionAssemblyBuilder); + +typedef struct { + MonoObject object; + guint32 attrs; + MonoObject *type; + MonoString *name; + MonoObject *def_value; + gint32 offset; + gint32 table_idx; + MonoReflectionType *typeb; + MonoArray *rva_data; + MonoArray *cattrs; + MonoReflectionMarshal *marshal_info; + MonoClassField *handle; + MonoArray *modreq; + MonoArray *modopt; +} MonoReflectionFieldBuilder; + +/* Safely access System.Reflection.Emit.FieldBuilder from native code */ +TYPED_HANDLE_DECL (MonoReflectionFieldBuilder); + +typedef struct { + MonoObject object; + guint32 attrs; + MonoString *name; + MonoObject *type; + MonoArray *parameters; + MonoArray *cattrs; + MonoObject *def_value; + MonoReflectionMethodBuilder *set_method; + MonoReflectionMethodBuilder *get_method; + gint32 table_idx; + MonoObject *type_builder; + MonoArray *returnModReq; + MonoArray *returnModOpt; + MonoArray *paramModReq; + MonoArray *paramModOpt; + guint32 call_conv; +} MonoReflectionPropertyBuilder; + +struct _MonoReflectionModule { + MonoObject obj; + MonoImage *image; + MonoReflectionAssembly *assembly; + MonoString *fqname; + MonoString *name; + MonoString *scopename; + MonoBoolean is_resource; + guint32 token; +}; + +/* Safely access System.Reflection.Module from native code */ +TYPED_HANDLE_DECL (MonoReflectionModule); + +typedef struct { + MonoReflectionModule module; + MonoDynamicImage *dynamic_image; + gint32 num_types; + MonoArray *types; + MonoArray *cattrs; + MonoArray *guid; + guint32 table_idx; + MonoReflectionAssemblyBuilder *assemblyb; + MonoArray *global_methods; + MonoArray *global_fields; + gboolean is_main; + MonoArray *resources; + GHashTable *unparented_classes; +} MonoReflectionModuleBuilder; + +/* Safely acess System.Reflection.Emit.ModuleBuidler from native code */ +TYPED_HANDLE_DECL (MonoReflectionModuleBuilder); + +typedef enum { + MonoTypeBuilderNew = 0, + MonoTypeBuilderEntered = 1, + MonoTypeBuilderFinished = 2 +} MonoTypeBuilderState; + +typedef struct { + MonoReflectionType type; + MonoString *name; + MonoString *nspace; + MonoObject *parent; + MonoReflectionType *nesting_type; + MonoArray *interfaces; + gint32 num_methods; + MonoArray *methods; + MonoArray *ctors; + MonoArray *properties; + gint32 num_fields; + MonoArray *fields; + MonoArray *events; + MonoArray *cattrs; + MonoArray *subtypes; + guint32 attrs; + guint32 table_idx; + MonoReflectionModuleBuilder *module; + gint32 class_size; + gint32 packing_size; + MonoGenericContainer *generic_container; + MonoArray *generic_params; + MonoArray *permissions; + MonoReflectionType *created; + gint32 state; +} MonoReflectionTypeBuilder; + +/* Safely access System.Reflection.Emit.TypeBuilder from native code */ +TYPED_HANDLE_DECL (MonoReflectionTypeBuilder); + +typedef struct { + MonoReflectionType type; + MonoReflectionType *element_type; + gint32 rank; +} MonoReflectionArrayType; + +/* Safely access System.Reflection.Emit.ArrayType (in DerivedTypes.cs) from native code */ +TYPED_HANDLE_DECL (MonoReflectionArrayType); + +typedef struct { + MonoReflectionType type; + MonoReflectionType *element_type; +} MonoReflectionDerivedType; + +/* Safely access System.Reflection.Emit.SymbolType and subclasses (in DerivedTypes.cs) from native code */ +TYPED_HANDLE_DECL (MonoReflectionDerivedType); + +typedef struct { + MonoReflectionType type; + MonoReflectionTypeBuilder *tbuilder; + MonoReflectionMethodBuilder *mbuilder; + MonoString *name; + guint32 index; + MonoReflectionType *base_type; + MonoArray *iface_constraints; + MonoArray *cattrs; + guint32 attrs; +} MonoReflectionGenericParam; + +/* Safely access System.Reflection.Emit.GenericTypeParameterBuilder from native code */ +TYPED_HANDLE_DECL (MonoReflectionGenericParam); + +typedef struct { + MonoReflectionType type; + MonoReflectionTypeBuilder *tb; +} MonoReflectionEnumBuilder; + +/* Safely access System.Reflection.Emit.EnumBuilder from native code */ +TYPED_HANDLE_DECL (MonoReflectionEnumBuilder); + +typedef struct _MonoReflectionGenericClass MonoReflectionGenericClass; +struct _MonoReflectionGenericClass { + MonoReflectionType type; + MonoReflectionType *generic_type; /*Can be either a MonoType or a TypeBuilder*/ + MonoArray *type_arguments; +}; + +/* Safely access System.Reflection.Emit.TypeBuilderInstantiation from native code */ +TYPED_HANDLE_DECL (MonoReflectionGenericClass); + +typedef struct { + MonoObject obj; + MonoString *name; + MonoString *codebase; + gint32 major, minor, build, revision; + MonoObject *cultureInfo; + guint32 flags; + guint32 hashalg; + MonoObject *keypair; + MonoArray *publicKey; + MonoArray *keyToken; + guint32 versioncompat; + MonoObject *version; + guint32 processor_architecture; +} MonoReflectionAssemblyName; + +/* Safely access System.Reflection.AssemblyName from native code */ +TYPED_HANDLE_DECL (MonoReflectionAssemblyName); + +typedef struct { + MonoObject obj; + MonoString *name; + MonoReflectionType *type; + MonoReflectionTypeBuilder *typeb; + MonoArray *cattrs; + MonoReflectionMethodBuilder *add_method; + MonoReflectionMethodBuilder *remove_method; + MonoReflectionMethodBuilder *raise_method; + MonoArray *other_methods; + guint32 attrs; + guint32 table_idx; +} MonoReflectionEventBuilder; + +typedef struct { + MonoObject obj; + MonoReflectionMethod *ctor; + MonoArray *data; +} MonoReflectionCustomAttr; + +typedef struct { + MonoObject object; + MonoString *marshal_cookie; + MonoString *marshal_type; + MonoReflectionType *marshal_type_ref; + MonoReflectionType *marshal_safe_array_user_defined_subtype; + guint32 utype; + guint32 array_subtype; + gint32 safe_array_subtype; + gint32 size_const; + gint32 IidParameterIndex; + gint16 size_param_index; +} MonoReflectionMarshalAsAttribute; + +/* Safely access System.Runtime.InteropServices.MarshalAsAttribute */ +TYPED_HANDLE_DECL (MonoReflectionMarshalAsAttribute); + +typedef struct { + MonoObject object; + gint32 call_conv; + gint32 charset; + MonoBoolean best_fit_mapping; + MonoBoolean throw_on_unmappable; + MonoBoolean set_last_error; +} MonoReflectionUnmanagedFunctionPointerAttribute; + +typedef struct { + MonoObject object; + MonoString *guid; +} MonoReflectionGuidAttribute; + +typedef struct { + MonoObject object; + MonoMethod *mhandle; + MonoString *name; + MonoReflectionType *rtype; + MonoArray *parameters; + guint32 attrs; + guint32 call_conv; + MonoReflectionModule *module; + MonoBoolean skip_visibility; + MonoBoolean init_locals; + MonoReflectionILGen *ilgen; + gint32 nrefs; + MonoArray *refs; + GSList *referenced_by; + MonoReflectionType *owner; +} MonoReflectionDynamicMethod; + +/* Safely access System.Reflection.Emit.DynamicMethod from native code */ +TYPED_HANDLE_DECL (MonoReflectionDynamicMethod); + +typedef struct { + MonoObject object; + MonoReflectionModuleBuilder *module; + MonoArray *arguments; + guint32 type; + MonoReflectionType *return_type; + guint32 call_conv; + guint32 unmanaged_call_conv; + MonoArray *modreqs; + MonoArray *modopts; +} MonoReflectionSigHelper; + +/* Safely access System.Reflection.Emit.SignatureHelper from native code */ +TYPED_HANDLE_DECL (MonoReflectionSigHelper); + +typedef struct { + MonoObject object; + MonoBoolean visible; +} MonoReflectionComVisibleAttribute; + +enum { + RESOURCE_LOCATION_EMBEDDED = 1, + RESOURCE_LOCATION_ANOTHER_ASSEMBLY = 2, + RESOURCE_LOCATION_IN_MANIFEST = 4 +}; + +typedef struct { + MonoObject object; + MonoReflectionAssembly *assembly; + MonoString *filename; + guint32 location; +} MonoManifestResourceInfo; + +/* Safely access System.Reflection.ManifestResourceInfo from native code */ +TYPED_HANDLE_DECL (MonoManifestResourceInfo); + +/* A boxed IntPtr */ +typedef struct { + MonoObject object; + gpointer m_value; +} MonoIntPtr; + +/* Keep in sync with System.GenericParameterAttributes */ +typedef enum { + GENERIC_PARAMETER_ATTRIBUTE_NON_VARIANT = 0, + GENERIC_PARAMETER_ATTRIBUTE_COVARIANT = 1, + GENERIC_PARAMETER_ATTRIBUTE_CONTRAVARIANT = 2, + GENERIC_PARAMETER_ATTRIBUTE_VARIANCE_MASK = 3, + + GENERIC_PARAMETER_ATTRIBUTE_NO_SPECIAL_CONSTRAINT = 0, + GENERIC_PARAMETER_ATTRIBUTE_REFERENCE_TYPE_CONSTRAINT = 4, + GENERIC_PARAMETER_ATTRIBUTE_VALUE_TYPE_CONSTRAINT = 8, + GENERIC_PARAMETER_ATTRIBUTE_CONSTRUCTOR_CONSTRAINT = 16, + GENERIC_PARAMETER_ATTRIBUTE_SPECIAL_CONSTRAINTS_MASK = 28 +} GenericParameterAttributes; + +typedef struct { + MonoType *type; + MonoClassField *field; + MonoProperty *prop; +} CattrNamedArg; + +/* All MonoInternalThread instances should be pinned, so it's safe to use the raw ptr. However + * for uniformity, icall wrapping will make handles anyway. So this is the method for getting the payload. + */ +static inline MonoInternalThread* +mono_internal_thread_handle_ptr (MonoInternalThreadHandle h) +{ + return MONO_HANDLE_RAW (h); /* Safe */ +} + +gboolean mono_image_create_pefile (MonoReflectionModuleBuilder *module, gpointer file, MonoError *error); +guint32 mono_image_insert_string (MonoReflectionModuleBuilderHandle module, MonoStringHandle str, MonoError *error); +guint32 mono_image_create_token (MonoDynamicImage *assembly, MonoObjectHandle obj, gboolean create_methodspec, gboolean register_token, MonoError *error); +void mono_dynamic_image_free (MonoDynamicImage *image); +void mono_dynamic_image_free_image (MonoDynamicImage *image); +void mono_dynamic_image_release_gc_roots (MonoDynamicImage *image); + +void mono_reflection_setup_internal_class (MonoReflectionTypeBuilder *tb); + +void mono_reflection_get_dynamic_overrides (MonoClass *klass, MonoMethod ***overrides, int *num_overrides, MonoError *error); + +void mono_reflection_destroy_dynamic_method (MonoReflectionDynamicMethod *mb); + +void +ves_icall_SymbolType_create_unmanaged_type (MonoReflectionType *type); + +void mono_reflection_register_with_runtime (MonoReflectionType *type); + +void mono_reflection_create_custom_attr_data_args (MonoImage *image, MonoMethod *method, const guchar *data, guint32 len, MonoArray **typed_args, MonoArray **named_args, CattrNamedArg **named_arg_info, MonoError *error); +MonoMethodSignature * mono_reflection_lookup_signature (MonoImage *image, MonoMethod *method, guint32 token, MonoError *error); + +MonoArrayHandle mono_param_get_objects_internal (MonoDomain *domain, MonoMethod *method, MonoClass *refclass, MonoError *error); + +MonoClass* +mono_class_bind_generic_parameters (MonoClass *klass, int type_argc, MonoType **types, gboolean is_dynamic); +MonoType* +mono_reflection_bind_generic_parameters (MonoReflectionTypeHandle type, int type_argc, MonoType **types, MonoError *error); +void +mono_reflection_generic_class_initialize (MonoReflectionGenericClass *type, MonoArray *fields); + +MonoReflectionEvent * +ves_icall_TypeBuilder_get_event_info (MonoReflectionTypeBuilder *tb, MonoReflectionEventBuilder *eb); + +MonoArrayHandle +ves_icall_SignatureHelper_get_signature_local (MonoReflectionSigHelperHandle sig, MonoError *error); + +MonoArrayHandle +ves_icall_SignatureHelper_get_signature_field (MonoReflectionSigHelperHandle sig, MonoError *error); + +MonoReflectionMarshalAsAttributeHandle +mono_reflection_marshal_as_attribute_from_marshal_spec (MonoDomain *domain, MonoClass *klass, MonoMarshalSpec *spec, MonoError *error); + +gpointer +mono_reflection_lookup_dynamic_token (MonoImage *image, guint32 token, gboolean valid_token, MonoClass **handle_class, MonoGenericContext *context, MonoError *error); + +gboolean +mono_reflection_call_is_assignable_to (MonoClass *klass, MonoClass *oklass, MonoError *error); + +void +ves_icall_System_Reflection_CustomAttributeData_ResolveArgumentsInternal (MonoReflectionMethod *method, MonoReflectionAssembly *assembly, gpointer data, guint32 data_length, MonoArray **ctor_args, MonoArray ** named_args); + +gboolean +mono_image_build_metadata (MonoReflectionModuleBuilder *module, MonoError *error); + +int +mono_get_constant_value_from_blob (MonoDomain* domain, MonoTypeEnum type, const char *blob, void *value, MonoError *error); + +void +mono_release_type_locks (MonoInternalThread *thread); + +int +mono_string_handle_length (MonoStringHandle s); + +char * +mono_string_handle_to_utf8 (MonoStringHandle s, MonoError *error); + +char * +mono_string_to_utf8_mp (MonoMemPool *mp, MonoString *s, MonoError *error); + +char * +mono_string_to_utf8_image (MonoImage *image, MonoStringHandle s, MonoError *error); + + +MonoArrayHandle +mono_array_clone_in_domain (MonoDomain *domain, MonoArrayHandle array, MonoError *error); + +MonoArray* +mono_array_clone_checked (MonoArray *array, MonoError *error); + +void +mono_array_full_copy (MonoArray *src, MonoArray *dest); + +gboolean +mono_array_calc_byte_len (MonoClass *klass, uintptr_t len, uintptr_t *res); + +MonoArray* +mono_array_new_checked (MonoDomain *domain, MonoClass *eclass, uintptr_t n, MonoError *error); + +MonoArray* +mono_array_new_full_checked (MonoDomain *domain, MonoClass *array_class, uintptr_t *lengths, intptr_t *lower_bounds, MonoError *error); + +MonoArray* +mono_array_new_specific_checked (MonoVTable *vtable, uintptr_t n, MonoError *error); + +MonoArray* +ves_icall_array_new (MonoDomain *domain, MonoClass *eclass, uintptr_t n); + +MonoArray* +ves_icall_array_new_specific (MonoVTable *vtable, uintptr_t n); + +#ifndef DISABLE_REMOTING +MonoRemoteClass* +mono_remote_class (MonoDomain *domain, MonoStringHandle class_name, MonoClass *proxy_class, MonoError *error); + +gboolean +mono_remote_class_is_interface_proxy (MonoRemoteClass *remote_class); + +MonoObject * +mono_remoting_invoke (MonoObject *real_proxy, MonoMethodMessage *msg, MonoObject **exc, MonoArray **out_args, MonoError *error); + +gpointer +mono_remote_class_vtable (MonoDomain *domain, MonoRemoteClass *remote_class, MonoRealProxyHandle real_proxy, MonoError *error); + +gboolean +mono_upgrade_remote_class (MonoDomain *domain, MonoObjectHandle tproxy, MonoClass *klass, MonoError *error); + +void* +mono_load_remote_field_checked (MonoObject *this_obj, MonoClass *klass, MonoClassField *field, void **res, MonoError *error); + +MonoObject * +mono_load_remote_field_new_checked (MonoObject *this_obj, MonoClass *klass, MonoClassField *field, MonoError *error); + +gboolean +mono_store_remote_field_checked (MonoObject *this_obj, MonoClass *klass, MonoClassField *field, void* val, MonoError *error); + +gboolean +mono_store_remote_field_new_checked (MonoObject *this_obj, MonoClass *klass, MonoClassField *field, MonoObject *arg, MonoError *error); + + +#endif + +gpointer +mono_create_ftnptr (MonoDomain *domain, gpointer addr); + +gpointer +mono_get_addr_from_ftnptr (gpointer descr); + +void +mono_nullable_init (guint8 *buf, MonoObject *value, MonoClass *klass); + +void +mono_nullable_init_from_handle (guint8 *buf, MonoObjectHandle value, MonoClass *klass); + +MonoObject * +mono_value_box_checked (MonoDomain *domain, MonoClass *klass, void* val, MonoError *error); + +MonoObject* +mono_nullable_box (guint8 *buf, MonoClass *klass, MonoError *error); + +#ifdef MONO_SMALL_CONFIG +#define MONO_IMT_SIZE 9 +#else +#define MONO_IMT_SIZE 19 +#endif + +typedef union { + int vtable_slot; + gpointer target_code; +} MonoImtItemValue; + +typedef struct _MonoImtBuilderEntry { + gpointer key; + struct _MonoImtBuilderEntry *next; + MonoImtItemValue value; + int children; + guint8 has_target_code : 1; +} MonoImtBuilderEntry; + +typedef struct _MonoIMTCheckItem MonoIMTCheckItem; + +struct _MonoIMTCheckItem { + gpointer key; + int check_target_idx; + MonoImtItemValue value; + guint8 *jmp_code; + guint8 *code_target; + guint8 is_equals; + guint8 compare_done; + guint8 chunk_size; + guint8 short_branch; + guint8 has_target_code; +}; + +typedef gpointer (*MonoImtTrampolineBuilder) (MonoVTable *vtable, MonoDomain *domain, + MonoIMTCheckItem **imt_entries, int count, gpointer fail_trunk); + +void +mono_install_imt_trampoline_builder (MonoImtTrampolineBuilder func); + +void +mono_set_always_build_imt_trampolines (gboolean value); + +void +mono_vtable_build_imt_slot (MonoVTable* vtable, int imt_slot); + +guint32 +mono_method_get_imt_slot (MonoMethod *method); + +void +mono_method_add_generic_virtual_invocation (MonoDomain *domain, MonoVTable *vtable, + gpointer *vtable_slot, + MonoMethod *method, gpointer code); + +gpointer +mono_method_alloc_generic_virtual_trampoline (MonoDomain *domain, int size); + +typedef enum { + MONO_UNHANDLED_POLICY_LEGACY, + MONO_UNHANDLED_POLICY_CURRENT +} MonoRuntimeUnhandledExceptionPolicy; + +MonoRuntimeUnhandledExceptionPolicy +mono_runtime_unhandled_exception_policy_get (void); +UNITY_MONO_API void +mono_runtime_unhandled_exception_policy_set (MonoRuntimeUnhandledExceptionPolicy policy); + +void +mono_unhandled_exception_checked (MonoObjectHandle exc, MonoError *error); + +MonoVTable * +mono_class_try_get_vtable (MonoDomain *domain, MonoClass *klass); + +gboolean +mono_runtime_class_init_full (MonoVTable *vtable, MonoError *error); + +void +mono_method_clear_object (MonoDomain *domain, MonoMethod *method); + +void +mono_class_compute_gc_descriptor (MonoClass *klass); + +gsize* +mono_class_compute_bitmap (MonoClass *klass, gsize *bitmap, int size, int offset, int *max_set, gboolean static_fields); + +MonoObject* +mono_object_xdomain_representation (MonoObject *obj, MonoDomain *target_domain, MonoError *error); + +gboolean +mono_class_is_reflection_method_or_constructor (MonoClass *klass); + +MonoObject * +mono_get_object_from_blob (MonoDomain *domain, MonoType *type, const char *blob, MonoError *error); + +MonoObjectHandle +mono_class_get_ref_info (MonoClass *klass); + +gboolean +mono_class_has_ref_info (MonoClass *klass); + +MonoObject* +mono_class_get_ref_info_raw (MonoClass *klass); + +void +mono_class_set_ref_info (MonoClass *klass, MonoObjectHandle obj); + +void +mono_class_free_ref_info (MonoClass *klass); + +MonoObject * +mono_object_new_pinned (MonoDomain *domain, MonoClass *klass, MonoError *error); + +MonoObject * +mono_object_new_specific_checked (MonoVTable *vtable, MonoError *error); + +MonoObject * +ves_icall_object_new (MonoDomain *domain, MonoClass *klass); + +MonoObject * +ves_icall_object_new_specific (MonoVTable *vtable); + +MonoObject * +mono_object_new_alloc_specific_checked (MonoVTable *vtable, MonoError *error); + +void +mono_field_static_get_value_checked (MonoVTable *vt, MonoClassField *field, void *value, MonoError *error); + +void +mono_field_static_get_value_for_thread (MonoInternalThread *thread, MonoVTable *vt, MonoClassField *field, void *value, MonoError *error); + +MonoMethod* +mono_object_handle_get_virtual_method (MonoObjectHandle obj, MonoMethod *method, MonoError *error); + +/* exported, used by the debugger */ +MONO_API void * +mono_vtable_get_static_field_data (MonoVTable *vt); + +MonoObject * +mono_field_get_value_object_checked (MonoDomain *domain, MonoClassField *field, MonoObject *obj, MonoError *error); + +gboolean +mono_property_set_value_checked (MonoProperty *prop, void *obj, void **params, MonoError *error); + +MonoObject* +mono_property_get_value_checked (MonoProperty *prop, void *obj, void **params, MonoError *error); + +MonoString* +mono_object_to_string_checked (MonoObject *obj, MonoError *error); + +MonoString* +mono_object_try_to_string (MonoObject *obj, MonoObject **exc, MonoError *error); + +char * +mono_string_to_utf8_ignore (MonoString *s); + +char * +mono_string_to_utf8_image_ignore (MonoImage *image, MonoString *s); + +char * +mono_string_to_utf8_mp_ignore (MonoMemPool *mp, MonoString *s); + +gboolean +mono_monitor_is_il_fastpath_wrapper (MonoMethod *method); + +MonoString* +mono_string_intern_checked (MonoString *str, MonoError *error); + +char * +mono_exception_handle_get_native_backtrace (MonoExceptionHandle exc); + +MonoStringHandle +ves_icall_Mono_Runtime_GetNativeStackTrace (MonoExceptionHandle exc, MonoError *erro); + +char * +mono_exception_get_managed_backtrace (MonoException *exc); + +void +mono_copy_value (MonoType *type, void *dest, void *value, int deref_pointer); + +void +mono_error_raise_exception_deprecated (MonoError *target_error); + +gboolean +mono_error_set_pending_exception (MonoError *error); + +MonoArray * +mono_glist_to_array (GList *list, MonoClass *eclass, MonoError *error); + +MonoObject * +mono_object_new_checked (MonoDomain *domain, MonoClass *klass, MonoError *error); + +MonoObject* +mono_object_new_mature (MonoVTable *vtable, MonoError *error); + +MonoObject* +mono_object_new_fast_checked (MonoVTable *vtable, MonoError *error); + +MonoObject * +mono_object_clone_checked (MonoObject *obj, MonoError *error); + +MonoObject * +mono_object_isinst_checked (MonoObject *obj, MonoClass *klass, MonoError *error); + +MonoObjectHandle +mono_object_handle_isinst (MonoObjectHandle obj, MonoClass *klass, MonoError *error); + +MonoObjectHandle +mono_object_handle_isinst_mbyref (MonoObjectHandle obj, MonoClass *klass, MonoError *error); + +MonoString * +mono_string_new_size_checked (MonoDomain *domain, gint32 len, MonoError *error); + +MonoString* +mono_ldstr_checked (MonoDomain *domain, MonoImage *image, uint32_t str_index, MonoError *error); + +MonoString* +mono_string_new_len_checked (MonoDomain *domain, const char *text, guint length, MonoError *error); + +MonoString* +mono_string_new_checked (MonoDomain *domain, const char *text, MonoError *merror); + +MonoString * +mono_string_new_utf16_checked (MonoDomain *domain, const guint16 *text, gint32 len, MonoError *error); + +MonoStringHandle +mono_string_new_utf16_handle (MonoDomain *domain, const guint16 *text, gint32 len, MonoError *error); + +MonoString * +mono_string_from_utf16_checked (mono_unichar2 *data, MonoError *error); + +MonoString * +mono_string_from_utf32_checked (mono_unichar4 *data, MonoError *error); + +char* +mono_ldstr_utf8 (MonoImage *image, guint32 idx, MonoError *error); + +gboolean +mono_runtime_object_init_checked (MonoObject *this_obj, MonoError *error); + +MonoObject* +mono_runtime_try_invoke (MonoMethod *method, void *obj, void **params, MonoObject **exc, MonoError *error); + +MonoObject* +mono_runtime_invoke_checked (MonoMethod *method, void *obj, void **params, MonoError *error); + +MonoObject* +mono_runtime_try_invoke_array (MonoMethod *method, void *obj, MonoArray *params, + MonoObject **exc, MonoError *error); + +MonoObject* +mono_runtime_invoke_array_checked (MonoMethod *method, void *obj, MonoArray *params, + MonoError *error); + +void* +mono_compile_method_checked (MonoMethod *method, MonoError *error); + +MonoObject* +mono_runtime_delegate_try_invoke (MonoObject *delegate, void **params, + MonoObject **exc, MonoError *error); + +MonoObject* +mono_runtime_delegate_invoke_checked (MonoObject *delegate, void **params, + MonoError *error); + +MonoArrayHandle +mono_runtime_get_main_args_handle (MonoError *error); + +int +mono_runtime_run_main_checked (MonoMethod *method, int argc, char* argv[], + MonoError *error); + +int +mono_runtime_try_run_main (MonoMethod *method, int argc, char* argv[], + MonoObject **exc); + +int +mono_runtime_exec_main_checked (MonoMethod *method, MonoArray *args, MonoError *error); + +int +mono_runtime_try_exec_main (MonoMethod *method, MonoArray *args, MonoObject **exc); + +MonoReflectionMethodHandle +ves_icall_MonoMethod_MakeGenericMethod_impl (MonoReflectionMethodHandle rmethod, MonoArrayHandle types, MonoError *error); + +gint32 +ves_icall_ModuleBuilder_getToken (MonoReflectionModuleBuilderHandle mb, MonoObjectHandle obj, gboolean create_open_instance, MonoError *error); + +gint32 +ves_icall_ModuleBuilder_getMethodToken (MonoReflectionModuleBuilderHandle mb, + MonoReflectionMethodHandle method, + MonoArrayHandle opt_param_types, + MonoError *error); + +void +ves_icall_ModuleBuilder_WriteToFile (MonoReflectionModuleBuilder *mb, gpointer file); + +void +ves_icall_ModuleBuilder_build_metadata (MonoReflectionModuleBuilder *mb); + +void +ves_icall_ModuleBuilder_RegisterToken (MonoReflectionModuleBuilderHandle mb, MonoObjectHandle obj, guint32 token, MonoError *error); + +MonoObjectHandle +ves_icall_ModuleBuilder_GetRegisteredToken (MonoReflectionModuleBuilderHandle mb, guint32 token, MonoError *error); + +void +ves_icall_AssemblyBuilder_basic_init (MonoReflectionAssemblyBuilder *assemblyb); + +void +ves_icall_AssemblyBuilder_UpdateNativeCustomAttributes (MonoReflectionAssemblyBuilderHandle assemblyb, MonoError *error); + +MonoArray* +ves_icall_CustomAttributeBuilder_GetBlob (MonoReflectionAssembly *assembly, MonoObject *ctor, MonoArray *ctorArgs, MonoArray *properties, MonoArray *propValues, MonoArray *fields, MonoArray* fieldValues); + +void +ves_icall_DynamicMethod_create_dynamic_method (MonoReflectionDynamicMethodHandle mb, MonoError *error); + +MonoReflectionTypeHandle +ves_icall_TypeBuilder_create_runtime_class (MonoReflectionTypeBuilderHandle tb, MonoError *error); + +void +ves_icall_EnumBuilder_setup_enum_type (MonoReflectionTypeHandle enumtype, + MonoReflectionTypeHandle t, + MonoError *error); + +void +ves_icall_ModuleBuilder_basic_init (MonoReflectionModuleBuilderHandle moduleb, MonoError *error); + +guint32 +ves_icall_ModuleBuilder_getUSIndex (MonoReflectionModuleBuilderHandle module, MonoStringHandle str, MonoError *error); + +void +ves_icall_ModuleBuilder_set_wrappers_type (MonoReflectionModuleBuilderHandle moduleb, MonoReflectionTypeHandle type, MonoError *error); + +MonoAssembly* +mono_try_assembly_resolve_handle (MonoDomain *domain, MonoStringHandle fname, MonoAssembly *requesting, gboolean refonly, MonoError *error); + +#endif /* __MONO_OBJECT_INTERNALS_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/object-offsets.h b/unity-2019.4.24f1-mbe/mono/metadata/object-offsets.h new file mode 100644 index 000000000..a4f2c27ea --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/object-offsets.h @@ -0,0 +1,302 @@ + +/** +\file +This is a parameterized header. It's supposed/ok to be included multiple times. + +Input defines: (those to be defined by the includer file) + +Required: +DECL_OFFSET(struct,field) +DECL_OFFSET2(struct,field,offset) +DECL_ALIGN(name,type) +DECL_ALIGN2(name,alignment) + +Optional: +USE_CROSS_COMPILE_OFFSETS - if defined, force the cross compiler offsets to be used, otherwise + they will only be used if MONO_CROSS_COMPILE is defined +DISABLE_METADATA_OFFSETS - Disable the definition of offsets for structures defined in metadata/. +DISABLE_JIT_OFFSETS - Disable the definition of offsets for structures defined in mini/. + +The last two are needed because metadata shouldn't include JIT offsets since the structures +are not defined, while the JIT shouldn't include metadata offsets, since some of them +are GC specific, and the JIT needs to remain GC agnostic. + +Output defines: + +HAS_CROSS_COMPILER_OFFSETS - if set, it means we found some cross offsets, it doesnt mean we'll use it. +USED_CROSS_COMPILER_OFFSETS - if set, it means we used the cross offsets + +Environment defines (from config.h and CFLAGS): + +MONO_GENERATING_OFFSETS - Set by an offsets generating tool to disable the usage of any (possibly non-existing) generated header. +MONO_OFFSETS_FILE - Name of the header file containing the offsets to be used. + +*/ + + +#undef HAS_CROSS_COMPILER_OFFSETS +#undef USED_CROSS_COMPILER_OFFSETS + +#if !defined (MONO_GENERATING_OFFSETS) && defined (MONO_OFFSETS_FILE) +#include MONO_OFFSETS_FILE +#endif + +#ifndef USED_CROSS_COMPILER_OFFSETS + +DECL_ALIGN(gint8) +DECL_ALIGN(gint16) +DECL_ALIGN(gint32) +DECL_ALIGN(gint64) +DECL_ALIGN(float) +DECL_ALIGN(double) +DECL_ALIGN(gpointer) + +DECL_SIZE(gint8) +DECL_SIZE(gint16) +DECL_SIZE(gint32) +DECL_SIZE(gint64) +DECL_SIZE(float) +DECL_SIZE(double) +DECL_SIZE(gpointer) + +// Offsets for structures defined in metadata/ +#ifndef DISABLE_METADATA_OFFSETS +DECL_OFFSET(MonoObject, vtable) +DECL_OFFSET(MonoObject, synchronisation) + +DECL_OFFSET(MonoObjectHandlePayload, __raw) + +DECL_OFFSET(MonoClass, interface_bitmap) +DECL_OFFSET(MonoClass, byval_arg) +DECL_OFFSET(MonoClass, cast_class) +DECL_OFFSET(MonoClass, element_class) +DECL_OFFSET(MonoClass, idepth) +DECL_OFFSET(MonoClass, instance_size) +DECL_OFFSET(MonoClass, interface_id) +DECL_OFFSET(MonoClass, max_interface_id) +DECL_OFFSET(MonoClass, parent) +DECL_OFFSET(MonoClass, rank) +DECL_OFFSET(MonoClass, sizes) +DECL_OFFSET(MonoClass, supertypes) + +DECL_OFFSET(MonoVTable, klass) +DECL_OFFSET(MonoVTable, max_interface_id) +DECL_OFFSET(MonoVTable, interface_bitmap) +DECL_OFFSET(MonoVTable, vtable) +DECL_OFFSET(MonoVTable, rank) +DECL_OFFSET(MonoVTable, initialized) +DECL_OFFSET(MonoVTable, type) +DECL_OFFSET(MonoVTable, runtime_generic_context) + +DECL_OFFSET(MonoDomain, stack_overflow_ex) + +DECL_OFFSET(MonoDelegate, target) +DECL_OFFSET(MonoDelegate, method_ptr) +DECL_OFFSET(MonoDelegate, invoke_impl) +DECL_OFFSET(MonoDelegate, method) +DECL_OFFSET(MonoDelegate, method_code) +DECL_OFFSET(MonoDelegate, method_is_virtual) +DECL_OFFSET(MonoDelegate, extra_arg) + +DECL_OFFSET(MonoInternalThread, tid) +DECL_OFFSET(MonoInternalThread, small_id) +DECL_OFFSET(MonoInternalThread, static_data) +DECL_OFFSET(MonoInternalThread, last) + +DECL_OFFSET(MonoMulticastDelegate, delegates) + +DECL_OFFSET(MonoTransparentProxy, rp) +DECL_OFFSET(MonoTransparentProxy, remote_class) +DECL_OFFSET(MonoTransparentProxy, custom_type_info) + +DECL_OFFSET(MonoRealProxy, target_domain_id) +DECL_OFFSET(MonoRealProxy, context) +DECL_OFFSET(MonoRealProxy, unwrapped_server) + +DECL_OFFSET(MonoRemoteClass, proxy_class) + +DECL_OFFSET(MonoArray, vector) +DECL_OFFSET(MonoArray, max_length) +DECL_OFFSET(MonoArray, bounds) + +DECL_OFFSET(MonoArrayBounds, lower_bound) +DECL_OFFSET(MonoArrayBounds, length) + +DECL_OFFSET(MonoSafeHandle, handle) + +DECL_OFFSET(MonoHandleRef, handle) + +DECL_OFFSET(MonoComInteropProxy, com_object) + +DECL_OFFSET(MonoString, length) +DECL_OFFSET(MonoString, chars) + +DECL_OFFSET(MonoException, message) + +DECL_OFFSET(MonoTypedRef, type) +DECL_OFFSET(MonoTypedRef, klass) +DECL_OFFSET(MonoTypedRef, value) + +//Internal structs +DECL_OFFSET(MonoThreadsSync, status) +DECL_OFFSET(MonoThreadsSync, nest) + +DECL_OFFSET(MonoProfilerCallContext, method) +DECL_OFFSET(MonoProfilerCallContext, return_value) + +#ifdef HAVE_SGEN_GC +DECL_OFFSET(SgenClientThreadInfo, in_critical_region) +DECL_OFFSET(SgenThreadInfo, tlab_next) +DECL_OFFSET(SgenThreadInfo, tlab_temp_end) +#endif + +#endif //DISABLE METADATA OFFSETS + +// Offsets for structures defined in mini/ +#ifndef DISABLE_JIT_OFFSETS +DECL_OFFSET(MonoLMF, previous_lmf) + +DECL_OFFSET(MonoMethodRuntimeGenericContext, class_vtable) + +DECL_OFFSET(MonoJitTlsData, lmf) +DECL_OFFSET(MonoJitTlsData, class_cast_from) +DECL_OFFSET(MonoJitTlsData, class_cast_to) +DECL_OFFSET(MonoJitTlsData, restore_stack_prot) + +DECL_OFFSET(MonoGSharedVtMethodRuntimeInfo, locals_size) +DECL_OFFSET(MonoGSharedVtMethodRuntimeInfo, entries) //XXX more to fix here + +DECL_OFFSET(MonoContinuation, stack_used_size) +DECL_OFFSET(MonoContinuation, saved_stack) +DECL_OFFSET(MonoContinuation, return_sp) +DECL_OFFSET(MonoContinuation, lmf) +DECL_OFFSET(MonoContinuation, return_ip) + +DECL_OFFSET(MonoDelegateTrampInfo, invoke_impl) +DECL_OFFSET(MonoDelegateTrampInfo, method_ptr) + +// Architecture-specific offsets +// ----------------------------- + +#if defined(TARGET_WASM) +DECL_OFFSET(MonoContext, wasm_ip) +DECL_OFFSET(MonoContext, wasm_bp) +DECL_OFFSET(MonoContext, wasm_sp) +DECL_OFFSET(MonoContext, llvm_exc_reg) + +DECL_OFFSET(MonoLMF, method) +DECL_OFFSET(MonoLMF, lmf_addr) + +#elif defined(TARGET_X86) +DECL_OFFSET(MonoContext, eax) +DECL_OFFSET(MonoContext, ebx) +DECL_OFFSET(MonoContext, ecx) +DECL_OFFSET(MonoContext, edx) +DECL_OFFSET(MonoContext, edi) +DECL_OFFSET(MonoContext, esi) +DECL_OFFSET(MonoContext, esp) +DECL_OFFSET(MonoContext, ebp) +DECL_OFFSET(MonoContext, eip) + +DECL_OFFSET(MonoLMF, method) +DECL_OFFSET(MonoLMF, lmf_addr) +DECL_OFFSET(MonoLMF, esp) +DECL_OFFSET(MonoLMF, ebx) +DECL_OFFSET(MonoLMF, edi) +DECL_OFFSET(MonoLMF, esi) +DECL_OFFSET(MonoLMF, ebp) +DECL_OFFSET(MonoLMF, eip) +#elif defined(TARGET_AMD64) +DECL_OFFSET(MonoContext, gregs) +DECL_OFFSET(MonoContext, fregs) + +DECL_OFFSET(MonoLMF, rsp) +DECL_OFFSET(MonoLMF, rbp) + +DECL_OFFSET(DynCallArgs, res) + +DECL_OFFSET(MonoLMFTramp, ctx) +DECL_OFFSET(MonoLMFTramp, lmf_addr) +#elif defined(TARGET_ARM) +DECL_OFFSET(MonoLMF, sp) +DECL_OFFSET(MonoLMF, fp) +DECL_OFFSET(MonoLMF, ip) +DECL_OFFSET(MonoLMF, iregs) +DECL_OFFSET(MonoLMF, fregs) +DECL_OFFSET(DynCallArgs, fpregs) +DECL_OFFSET(DynCallArgs, has_fpregs) +DECL_OFFSET(DynCallArgs, regs) +DECL_OFFSET(DynCallArgs, n_stackargs) +DECL_OFFSET(SeqPointInfo, ss_tramp_addr) +#elif defined(TARGET_ARM64) +DECL_OFFSET(MonoLMF, pc) +DECL_OFFSET(MonoLMF, gregs) +DECL_OFFSET(DynCallArgs, regs) +DECL_OFFSET(DynCallArgs, fpregs) +DECL_OFFSET(DynCallArgs, n_stackargs) +DECL_OFFSET(DynCallArgs, n_fpargs) +DECL_OFFSET(DynCallArgs, n_fpret) +#endif + +// Shared architecture offfsets +// ---------------------------- + +#if defined(TARGET_ARM) || defined(TARGET_ARM64) +DECL_OFFSET (MonoContext, pc) +DECL_OFFSET (MonoContext, regs) +DECL_OFFSET (MonoContext, fregs) + +DECL_OFFSET(MonoLMF, lmf_addr) + +DECL_OFFSET(SeqPointInfo, ss_trigger_page) + +DECL_OFFSET(DynCallArgs, res) +DECL_OFFSET(DynCallArgs, res2) +#endif + +#if defined(TARGET_ARM) +DECL_OFFSET(MonoLMF, method) +DECL_OFFSET(GSharedVtCallInfo, stack_usage) +DECL_OFFSET(GSharedVtCallInfo, vret_arg_reg) +DECL_OFFSET(GSharedVtCallInfo, ret_marshal) +DECL_OFFSET(GSharedVtCallInfo, vret_slot) +DECL_OFFSET(GSharedVtCallInfo, gsharedvt_in) +#endif + +#if defined(TARGET_ARM64) +DECL_OFFSET (MonoContext, has_fregs) + +DECL_OFFSET(GSharedVtCallInfo, stack_usage) +DECL_OFFSET(GSharedVtCallInfo, gsharedvt_in) +DECL_OFFSET(GSharedVtCallInfo, ret_marshal) +DECL_OFFSET(GSharedVtCallInfo, vret_slot) +#endif + +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) +DECL_OFFSET(SeqPointInfo, ss_tramp_addr) +#endif + +#if defined(TARGET_AMD64) || defined(TARGET_ARM) || defined(TARGET_ARM64) +DECL_OFFSET(SeqPointInfo, bp_addrs) +#endif + +#ifndef DISABLE_INTERPRETER +DECL_OFFSET(InterpMethodArguments, ilen) +DECL_OFFSET(InterpMethodArguments, iargs) +DECL_OFFSET(InterpMethodArguments, flen) +DECL_OFFSET(InterpMethodArguments, fargs) +DECL_OFFSET(InterpMethodArguments, retval) +DECL_OFFSET(InterpMethodArguments, is_float_ret) +#endif + +#endif //DISABLE_JIT_OFFSETS + +#endif //USED_CROSS_COMPILER_OFFSETS + +#undef DECL_OFFSET +#undef DECL_OFFSET2 +#undef DECL_ALIGN +#undef DECL_ALIGN2 +#undef DECL_SIZE +#undef DECL_SIZE2 +#undef USE_CROSS_COMPILE_OFFSETS diff --git a/unity-2019.4.24f1-mbe/mono/metadata/object.c b/unity-2019.4.24f1-mbe/mono/metadata/object.c new file mode 100644 index 000000000..6f0d4c03c --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/object.c @@ -0,0 +1,8642 @@ +/** + * \file + * Object creation for the Mono runtime + * + * Author: + * Miguel de Icaza (miguel@ximian.com) + * Paolo Molaro (lupus@ximian.com) + * + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2011 Novell, Inc (http://www.novell.com) + * Copyright 2001 Xamarin Inc (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#include +#ifdef HAVE_ALLOCA_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mono/metadata/metadata-internals.h" +#include "mono/metadata/class-internals.h" +#include +#include +#include "mono/metadata/debug-helpers.h" +#include +#include +#include +#include "mono/metadata/profiler-private.h" +#include "mono/metadata/security-manager.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cominterop.h" +#include +#include + +static void +get_default_field_value (MonoDomain* domain, MonoClassField *field, void *value, MonoError *error); + +static MonoString* +mono_ldstr_metadata_sig (MonoDomain *domain, const char* sig, MonoError *error); + +static void +free_main_args (void); + +static char * +mono_string_to_utf8_internal (MonoMemPool *mp, MonoImage *image, MonoString *s, gboolean ignore_error, MonoError *error); + +static void +array_full_copy_unchecked_size (MonoArray *src, MonoArray *dest, MonoClass *klass, uintptr_t size); + +static MonoMethod* +class_get_virtual_method (MonoClass *klass, MonoMethod *method, gboolean is_proxy, MonoError *error); + +/* Class lazy loading functions */ +static GENERATE_GET_CLASS_WITH_CACHE (pointer, "System.Reflection", "Pointer") +static GENERATE_GET_CLASS_WITH_CACHE (remoting_services, "System.Runtime.Remoting", "RemotingServices") +static GENERATE_GET_CLASS_WITH_CACHE (unhandled_exception_event_args, "System", "UnhandledExceptionEventArgs") +static GENERATE_GET_CLASS_WITH_CACHE (sta_thread_attribute, "System", "STAThreadAttribute") +static GENERATE_GET_CLASS_WITH_CACHE (activation_services, "System.Runtime.Remoting.Activation", "ActivationServices") + + +#define ldstr_lock() mono_os_mutex_lock (&ldstr_section) +#define ldstr_unlock() mono_os_mutex_unlock (&ldstr_section) +static mono_mutex_t ldstr_section; + + +/** + * mono_runtime_object_init: + * \param this_obj the object to initialize + * This function calls the zero-argument constructor (which must + * exist) for the given object. + */ +void +mono_runtime_object_init (MonoObject *this_obj) +{ + MonoError error; + mono_runtime_object_init_checked (this_obj, &error); + mono_error_assert_ok (&error); +} + +/** + * mono_runtime_object_init_checked: + * \param this_obj the object to initialize + * \param error set on error. + * This function calls the zero-argument constructor (which must + * exist) for the given object and returns TRUE on success, or FALSE + * on error and sets \p error. + */ +gboolean +mono_runtime_object_init_checked (MonoObject *this_obj, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoMethod *method = NULL; + MonoClass *klass = this_obj->vtable->klass; + + error_init (error); + method = mono_class_get_method_from_name (klass, ".ctor", 0); + if (!method) + g_error ("Could not lookup zero argument constructor for class %s", mono_type_get_full_name (klass)); + + if (method->klass->valuetype) + this_obj = (MonoObject *)mono_object_unbox (this_obj); + + mono_runtime_invoke_checked (method, this_obj, NULL, error); + return is_ok (error); +} + +/* The pseudo algorithm for type initialization from the spec +Note it doesn't say anything about domains - only threads. + +2. If the type is initialized you are done. +2.1. If the type is not yet initialized, try to take an + initialization lock. +2.2. If successful, record this thread as responsible for + initializing the type and proceed to step 2.3. +2.2.1. If not, see whether this thread or any thread + waiting for this thread to complete already holds the lock. +2.2.2. If so, return since blocking would create a deadlock. This thread + will now see an incompletely initialized state for the type, + but no deadlock will arise. +2.2.3 If not, block until the type is initialized then return. +2.3 Initialize the parent type and then all interfaces implemented + by this type. +2.4 Execute the type initialization code for this type. +2.5 Mark the type as initialized, release the initialization lock, + awaken any threads waiting for this type to be initialized, + and return. + +*/ + +typedef struct +{ + MonoNativeThreadId initializing_tid; + guint32 waiting_count; + gboolean done; + MonoCoopMutex mutex; + /* condvar used to wait for 'done' becoming TRUE */ + MonoCoopCond cond; +} TypeInitializationLock; + +/* for locking access to type_initialization_hash and blocked_thread_hash */ +static MonoCoopMutex type_initialization_section; + +static inline void +mono_type_initialization_lock (void) +{ + /* The critical sections protected by this lock in mono_runtime_class_init_full () can block */ + mono_coop_mutex_lock (&type_initialization_section); +} + +static inline void +mono_type_initialization_unlock (void) +{ + mono_coop_mutex_unlock (&type_initialization_section); +} + +static void +mono_type_init_lock (TypeInitializationLock *lock) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + mono_coop_mutex_lock (&lock->mutex); +} + +static void +mono_type_init_unlock (TypeInitializationLock *lock) +{ + mono_coop_mutex_unlock (&lock->mutex); +} + +/* from vtable to lock */ +static GHashTable *type_initialization_hash; + +/* from thread id to thread id being waited on */ +static GHashTable *blocked_thread_hash; + +/* Main thread */ +static MonoThread *main_thread; + +/* Functions supplied by the runtime */ +static MonoRuntimeCallbacks callbacks; + +/** + * mono_thread_set_main: + * \param thread thread to set as the main thread + * This function can be used to instruct the runtime to treat \p thread + * as the main thread, ie, the thread that would normally execute the \c Main + * method. This basically means that at the end of \p thread, the runtime will + * wait for the existing foreground threads to quit and other such details. + */ +void +mono_thread_set_main (MonoThread *thread) +{ + MONO_REQ_GC_UNSAFE_MODE; + + static gboolean registered = FALSE; + + if (!registered) { + void *key = thread->internal_thread ? (void *) MONO_UINT_TO_NATIVE_THREAD_ID (thread->internal_thread->tid) : NULL; + MONO_GC_REGISTER_ROOT_SINGLE (main_thread, MONO_ROOT_SOURCE_THREADING, key, "Thread Main Object"); + registered = TRUE; + } + + main_thread = thread; +} + +/** + * mono_thread_get_main: + */ +MonoThread* +mono_thread_get_main (void) +{ + MONO_REQ_GC_UNSAFE_MODE; + + return main_thread; +} + +void +mono_type_initialization_init (void) +{ + mono_coop_mutex_init_recursive (&type_initialization_section); + type_initialization_hash = g_hash_table_new (NULL, NULL); + blocked_thread_hash = g_hash_table_new (NULL, NULL); + mono_os_mutex_init_recursive (&ldstr_section); + mono_register_jit_icall (ves_icall_string_alloc, "ves_icall_string_alloc", mono_create_icall_signature ("object int"), FALSE); +} + +void +mono_type_initialization_cleanup (void) +{ +#if 0 + /* This is causing race conditions with + * mono_release_type_locks + */ + mono_coop_mutex_destroy (&type_initialization_section); + g_hash_table_destroy (type_initialization_hash); + type_initialization_hash = NULL; +#endif + mono_os_mutex_destroy (&ldstr_section); + g_hash_table_destroy (blocked_thread_hash); + blocked_thread_hash = NULL; + + free_main_args (); +} + +/** + * get_type_init_exception_for_vtable: + * + * Return the stored type initialization exception for VTABLE. + */ +static MonoException* +get_type_init_exception_for_vtable (MonoVTable *vtable) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoError error; + MonoDomain *domain = vtable->domain; + MonoClass *klass = vtable->klass; + MonoException *ex; + gchar *full_name; + + if (!vtable->init_failed) + g_error ("Trying to get the init exception for a non-failed vtable of class %s", mono_type_get_full_name (klass)); + + /* + * If the initializing thread was rudely aborted, the exception is not stored + * in the hash. + */ + ex = NULL; + mono_domain_lock (domain); + if (domain->type_init_exception_hash) + ex = (MonoException *)mono_g_hash_table_lookup (domain->type_init_exception_hash, klass); + mono_domain_unlock (domain); + + if (!ex) { + if (klass->name_space && *klass->name_space) + full_name = g_strdup_printf ("%s.%s", klass->name_space, klass->name); + else + full_name = g_strdup (klass->name); + ex = mono_get_exception_type_initialization_checked (full_name, NULL, &error); + g_free (full_name); + return_val_if_nok (&error, NULL); + } + + return ex; +} + +/** + * mono_runtime_class_init: + * \param vtable vtable that needs to be initialized + * This routine calls the class constructor for \p vtable. + */ +void +mono_runtime_class_init (MonoVTable *vtable) +{ + MONO_REQ_GC_UNSAFE_MODE; + MonoError error; + + mono_runtime_class_init_full (vtable, &error); + mono_error_assert_ok (&error); +} + +/* + * Returns TRUE if the lock was freed. + * LOCKING: Caller should hold type_initialization_lock. + */ +static gboolean +unref_type_lock (TypeInitializationLock *lock) +{ + --lock->waiting_count; + if (lock->waiting_count == 0) { + mono_coop_mutex_destroy (&lock->mutex); + mono_coop_cond_destroy (&lock->cond); + g_free (lock); + return TRUE; + } else { + return FALSE; + } +} + +/** + * mono_runtime_class_init_full: + * \param vtable that neeeds to be initialized + * \param error set on error + * \returns TRUE if class constructor \c .cctor has been initialized successfully, or FALSE otherwise and sets \p error. + */ +gboolean +mono_runtime_class_init_full (MonoVTable *vtable, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoMethod *method = NULL; + MonoClass *klass; + gchar *full_name; + MonoDomain *domain = vtable->domain; + TypeInitializationLock *lock; + MonoNativeThreadId tid; + int do_initialization = 0; + MonoDomain *last_domain = NULL; + gboolean pending_tae = FALSE; + + error_init (error); + + if (vtable->initialized) + return TRUE; + + klass = vtable->klass; + + if (!klass->image->checked_module_cctor) { + mono_image_check_for_module_cctor (klass->image); + if (klass->image->has_module_cctor) { + MonoClass *module_klass; + MonoVTable *module_vtable; + + module_klass = mono_class_get_checked (klass->image, MONO_TOKEN_TYPE_DEF | 1, error); + if (!module_klass) { + return FALSE; + } + + module_vtable = mono_class_vtable_full (vtable->domain, module_klass, error); + if (!module_vtable) + return FALSE; + if (!mono_runtime_class_init_full (module_vtable, error)) + return FALSE; + } + } + method = mono_class_get_cctor (klass); + if (!method) { + vtable->initialized = 1; + return TRUE; + } + + tid = mono_native_thread_id_get (); + + /* + * Due some preprocessing inside a global lock. If we are the first thread + * trying to initialize this class, create a separate lock+cond var, and + * acquire it before leaving the global lock. The other threads will wait + * on this cond var. + */ + + mono_type_initialization_lock (); + /* double check... */ + if (vtable->initialized) { + mono_type_initialization_unlock (); + return TRUE; + } + if (vtable->init_failed) { + mono_type_initialization_unlock (); + + /* The type initialization already failed once, rethrow the same exception */ + mono_error_set_exception_instance (error, get_type_init_exception_for_vtable (vtable)); + return FALSE; + } + lock = (TypeInitializationLock *)g_hash_table_lookup (type_initialization_hash, vtable); + if (lock == NULL) { + /* This thread will get to do the initialization */ + if (mono_domain_get () != domain) { + /* Transfer into the target domain */ + last_domain = mono_domain_get (); + if (!mono_domain_set (domain, FALSE)) { + vtable->initialized = 1; + mono_type_initialization_unlock (); + mono_error_set_exception_instance (error, mono_get_exception_appdomain_unloaded ()); + return FALSE; + } + } + lock = (TypeInitializationLock *)g_malloc0 (sizeof (TypeInitializationLock)); + mono_coop_mutex_init_recursive (&lock->mutex); + mono_coop_cond_init (&lock->cond); + lock->initializing_tid = tid; + lock->waiting_count = 1; + lock->done = FALSE; + g_hash_table_insert (type_initialization_hash, vtable, lock); + do_initialization = 1; + } else { + gpointer blocked; + TypeInitializationLock *pending_lock; + + if (mono_native_thread_id_equals (lock->initializing_tid, tid)) { + mono_type_initialization_unlock (); + return TRUE; + } + /* see if the thread doing the initialization is already blocked on this thread */ + gboolean is_blocked = TRUE; + blocked = GUINT_TO_POINTER (MONO_NATIVE_THREAD_ID_TO_UINT (lock->initializing_tid)); + while ((pending_lock = (TypeInitializationLock*) g_hash_table_lookup (blocked_thread_hash, blocked))) { + if (mono_native_thread_id_equals (pending_lock->initializing_tid, tid)) { + if (!pending_lock->done) { + mono_type_initialization_unlock (); + return TRUE; + } else { + /* the thread doing the initialization is blocked on this thread, + but on a lock that has already been freed. It just hasn't got + time to awake */ + is_blocked = FALSE; + break; + } + } + blocked = GUINT_TO_POINTER (MONO_NATIVE_THREAD_ID_TO_UINT (pending_lock->initializing_tid)); + } + ++lock->waiting_count; + /* record the fact that we are waiting on the initializing thread */ + if (is_blocked) + g_hash_table_insert (blocked_thread_hash, GUINT_TO_POINTER (tid), lock); + } + mono_type_initialization_unlock (); + + if (do_initialization) { + MonoException *exc = NULL; + + /* We are holding the per-vtable lock, do the actual initialization */ + + mono_threads_begin_abort_protected_block (); + mono_runtime_try_invoke (method, NULL, NULL, (MonoObject**) &exc, error); + mono_threads_end_abort_protected_block (); + + //exception extracted, error will be set to the right value later + if (exc == NULL && !mono_error_ok (error))//invoking failed but exc was not set + exc = mono_error_convert_to_exception (error); + else + mono_error_cleanup (error); + + error_init (error); + + /* If the initialization failed, mark the class as unusable. */ + /* Avoid infinite loops */ + if (!(!exc || + (klass->image == mono_defaults.corlib && + !strcmp (klass->name_space, "System") && + !strcmp (klass->name, "TypeInitializationException")))) { + vtable->init_failed = 1; + + if (klass->name_space && *klass->name_space) + full_name = g_strdup_printf ("%s.%s", klass->name_space, klass->name); + else + full_name = g_strdup (klass->name); + + MonoException *exc_to_throw = mono_get_exception_type_initialization_checked (full_name, exc, error); + g_free (full_name); + + mono_error_assert_ok (error); //We can't recover from this, no way to fail a type we can't alloc a failure. + + /* + * Store the exception object so it could be thrown on subsequent + * accesses. + */ + mono_domain_lock (domain); + if (!domain->type_init_exception_hash) + domain->type_init_exception_hash = mono_g_hash_table_new_type (mono_aligned_addr_hash, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_DOMAIN, domain, "Domain Type Initialization Exception Table"); + mono_g_hash_table_insert (domain->type_init_exception_hash, klass, exc_to_throw); + mono_domain_unlock (domain); + } + + if (last_domain) + mono_domain_set (last_domain, TRUE); + + /* Signal to the other threads that we are done */ + mono_type_init_lock (lock); + lock->done = TRUE; + mono_coop_cond_broadcast (&lock->cond); + mono_type_init_unlock (lock); + + /* + * This can happen if the cctor self-aborts. We need to reactivate tae + * (next interruption checkpoint will throw it) and make sure we won't + * throw tie for the type. + */ + if (exc && mono_object_class (exc) == mono_defaults.threadabortexception_class) { + pending_tae = TRUE; + mono_thread_resume_interruption (FALSE); + } + } else { + /* this just blocks until the initializing thread is done */ + mono_type_init_lock (lock); + while (!lock->done) + mono_coop_cond_wait (&lock->cond, &lock->mutex); + mono_type_init_unlock (lock); + } + + /* Do cleanup and setting vtable->initialized inside the global lock again */ + mono_type_initialization_lock (); + if (!do_initialization) + g_hash_table_remove (blocked_thread_hash, GUINT_TO_POINTER (tid)); + gboolean deleted = unref_type_lock (lock); + if (deleted) + g_hash_table_remove (type_initialization_hash, vtable); + /* Have to set this here since we check it inside the global lock */ + if (do_initialization && !vtable->init_failed) + vtable->initialized = 1; + mono_type_initialization_unlock (); + + /* If vtable init fails because of TAE, we don't throw TIE, only the TAE */ + if (vtable->init_failed && !pending_tae) { + /* Either we were the initializing thread or we waited for the initialization */ + mono_error_set_exception_instance (error, get_type_init_exception_for_vtable (vtable)); + return FALSE; + } + return TRUE; +} + +MonoDomain * +mono_vtable_domain (MonoVTable *vtable) +{ + return vtable->domain; +} + +MonoClass * +mono_vtable_class (MonoVTable *vtable) +{ + return vtable->klass; +} + +static +gboolean release_type_locks (gpointer key, gpointer value, gpointer user) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + MonoVTable *vtable = (MonoVTable*)key; + + TypeInitializationLock *lock = (TypeInitializationLock*) value; + if (mono_native_thread_id_equals (lock->initializing_tid, MONO_UINT_TO_NATIVE_THREAD_ID (GPOINTER_TO_UINT (user))) && !lock->done) { + lock->done = TRUE; + /* + * Have to set this since it cannot be set by the normal code in + * mono_runtime_class_init (). In this case, the exception object is not stored, + * and get_type_init_exception_for_class () needs to be aware of this. + */ + mono_type_init_lock (lock); + vtable->init_failed = 1; + mono_coop_cond_broadcast (&lock->cond); + mono_type_init_unlock (lock); + gboolean deleted = unref_type_lock (lock); + if (deleted) + return TRUE; + } + return FALSE; +} + +void +mono_release_type_locks (MonoInternalThread *thread) +{ + MONO_REQ_GC_UNSAFE_MODE; + + mono_type_initialization_lock (); + g_hash_table_foreach_remove (type_initialization_hash, release_type_locks, GUINT_TO_POINTER (thread->tid)); + mono_type_initialization_unlock (); +} + +#ifndef DISABLE_REMOTING + +static gpointer +create_remoting_trampoline (MonoDomain *domain, MonoMethod *method, MonoRemotingTarget target, MonoError *error) +{ + if (!callbacks.create_remoting_trampoline) + g_error ("remoting not installed"); + return callbacks.create_remoting_trampoline (domain, method, target, error); +} + +#endif + +static MonoImtTrampolineBuilder imt_trampoline_builder; +static gboolean always_build_imt_trampolines; + +#if (MONO_IMT_SIZE > 32) +#error "MONO_IMT_SIZE cannot be larger than 32" +#endif + +void +mono_install_callbacks (MonoRuntimeCallbacks *cbs) +{ + memcpy (&callbacks, cbs, sizeof (*cbs)); +} + +MonoRuntimeCallbacks* +mono_get_runtime_callbacks (void) +{ + return &callbacks; +} + +void +mono_install_imt_trampoline_builder (MonoImtTrampolineBuilder func) +{ + imt_trampoline_builder = func; +} + +void +mono_set_always_build_imt_trampolines (gboolean value) +{ + always_build_imt_trampolines = value; +} + +/** + * mono_compile_method: + * \param method The method to compile. + * This JIT-compiles the method, and returns the pointer to the native code + * produced. + */ +gpointer +mono_compile_method (MonoMethod *method) +{ + MonoError error; + gpointer result = mono_compile_method_checked (method, &error); + mono_error_cleanup (&error); + return result; +} + +/** + * mono_compile_method_checked: + * \param method The method to compile. + * \param error set on error. + * This JIT-compiles the method, and returns the pointer to the native code + * produced. On failure returns NULL and sets \p error. + */ +gpointer +mono_compile_method_checked (MonoMethod *method, MonoError *error) +{ + gpointer res; + + MONO_REQ_GC_NEUTRAL_MODE + + error_init (error); + + g_assert (callbacks.compile_method); + res = callbacks.compile_method (method, error); + return res; +} + +gpointer +mono_runtime_create_jump_trampoline (MonoDomain *domain, MonoMethod *method, gboolean add_sync_wrapper, MonoError *error) +{ + gpointer res; + + MONO_REQ_GC_NEUTRAL_MODE; + + error_init (error); + res = callbacks.create_jump_trampoline (domain, method, add_sync_wrapper, error); + return res; +} + +gpointer +mono_runtime_create_delegate_trampoline (MonoClass *klass) +{ + MONO_REQ_GC_NEUTRAL_MODE + + g_assert (callbacks.create_delegate_trampoline); + return callbacks.create_delegate_trampoline (mono_domain_get (), klass); +} + +/** + * mono_runtime_free_method: + * \param domain domain where the method is hosted + * \param method method to release + * This routine is invoked to free the resources associated with + * a method that has been JIT compiled. This is used to discard + * methods that were used only temporarily (for example, used in marshalling) + */ +void +mono_runtime_free_method (MonoDomain *domain, MonoMethod *method) +{ + MONO_REQ_GC_NEUTRAL_MODE + + if (callbacks.free_method) + callbacks.free_method (domain, method); + + mono_method_clear_object (domain, method); + + mono_free_method (method); +} + +/* + * The vtables in the root appdomain are assumed to be reachable by other + * roots, and we don't use typed allocation in the other domains. + */ + +/* The sync block is no longer a GC pointer */ +#define GC_HEADER_BITMAP (0) + +#define BITMAP_EL_SIZE (sizeof (gsize) * 8) + +static gsize* +compute_class_bitmap (MonoClass *klass, gsize *bitmap, int size, int offset, int *max_set, gboolean static_fields) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + MonoClassField *field; + MonoClass *p; + guint32 pos; + int max_size; + + if (static_fields) + max_size = mono_class_data_size (klass) / sizeof (gpointer); + else + max_size = klass->instance_size / sizeof (gpointer); + if (max_size > size) { + g_assert (offset <= 0); + bitmap = (gsize *)g_malloc0 ((max_size + BITMAP_EL_SIZE - 1) / BITMAP_EL_SIZE * sizeof (gsize)); + size = max_size; + } + + /* An Ephemeron cannot be marked by sgen */ + if (mono_gc_is_moving () && !static_fields && klass->image == mono_defaults.corlib && !strcmp ("Ephemeron", klass->name)) { + *max_set = 0; + memset (bitmap, 0, size / 8); + return bitmap; + } + + for (p = klass; p != NULL; p = p->parent) { + gpointer iter = NULL; + while ((field = mono_class_get_fields (p, &iter))) { + MonoType *type; + + if (static_fields) { + if (!(field->type->attrs & (FIELD_ATTRIBUTE_STATIC | FIELD_ATTRIBUTE_HAS_FIELD_RVA))) + continue; + if (field->type->attrs & FIELD_ATTRIBUTE_LITERAL) + continue; + } else { + if (field->type->attrs & (FIELD_ATTRIBUTE_STATIC | FIELD_ATTRIBUTE_HAS_FIELD_RVA)) + continue; + } + /* FIXME: should not happen, flag as type load error */ + if (field->type->byref) + break; + + if (static_fields && field->offset == -1) + /* special static */ + continue; + + pos = field->offset / sizeof (gpointer); + pos += offset; + + type = mono_type_get_underlying_type (field->type); + switch (type->type) { + case MONO_TYPE_U: + case MONO_TYPE_I: + case MONO_TYPE_PTR: + case MONO_TYPE_FNPTR: + break; + case MONO_TYPE_STRING: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_CLASS: + case MONO_TYPE_OBJECT: + case MONO_TYPE_ARRAY: + g_assert ((field->offset % sizeof(gpointer)) == 0); + + g_assert (pos < size || pos <= max_size); + bitmap [pos / BITMAP_EL_SIZE] |= ((gsize)1) << (pos % BITMAP_EL_SIZE); + *max_set = MAX (*max_set, pos); + break; + case MONO_TYPE_GENERICINST: + if (!mono_type_generic_inst_is_valuetype (type)) { + g_assert ((field->offset % sizeof(gpointer)) == 0); + + bitmap [pos / BITMAP_EL_SIZE] |= ((gsize)1) << (pos % BITMAP_EL_SIZE); + *max_set = MAX (*max_set, pos); + break; + } else { + /* fall through */ + } + case MONO_TYPE_VALUETYPE: { + MonoClass *fclass = mono_class_from_mono_type (field->type); + if (fclass->has_references) { + /* remove the object header */ + compute_class_bitmap (fclass, bitmap, size, pos - (sizeof (MonoObject) / sizeof (gpointer)), max_set, FALSE); + } + break; + } + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_CHAR: + break; + default: + g_error ("compute_class_bitmap: Invalid type %x for field %s:%s\n", type->type, mono_type_get_full_name (field->parent), field->name); + break; + } + } + if (static_fields) + break; + } + return bitmap; +} + +/** + * mono_class_compute_bitmap: + * + * Mono internal function to compute a bitmap of reference fields in a class. + */ +gsize* +mono_class_compute_bitmap (MonoClass *klass, gsize *bitmap, int size, int offset, int *max_set, gboolean static_fields) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + return compute_class_bitmap (klass, bitmap, size, offset, max_set, static_fields); +} + +#if 0 +/* + * similar to the above, but sets the bits in the bitmap for any non-ref field + * and ignores static fields + */ +static gsize* +compute_class_non_ref_bitmap (MonoClass *klass, gsize *bitmap, int size, int offset) +{ + MonoClassField *field; + MonoClass *p; + guint32 pos, pos2; + int max_size; + + max_size = class->instance_size / sizeof (gpointer); + if (max_size >= size) { + bitmap = g_malloc0 (sizeof (gsize) * ((max_size) + 1)); + } + + for (p = class; p != NULL; p = p->parent) { + gpointer iter = NULL; + while ((field = mono_class_get_fields (p, &iter))) { + MonoType *type; + + if (field->type->attrs & (FIELD_ATTRIBUTE_STATIC | FIELD_ATTRIBUTE_HAS_FIELD_RVA)) + continue; + /* FIXME: should not happen, flag as type load error */ + if (field->type->byref) + break; + + pos = field->offset / sizeof (gpointer); + pos += offset; + + type = mono_type_get_underlying_type (field->type); + switch (type->type) { +#if SIZEOF_VOID_P == 8 + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_PTR: + case MONO_TYPE_FNPTR: +#endif + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_R8: + if ((((field->offset + 7) / sizeof (gpointer)) + offset) != pos) { + pos2 = ((field->offset + 7) / sizeof (gpointer)) + offset; + bitmap [pos2 / BITMAP_EL_SIZE] |= ((gsize)1) << (pos2 % BITMAP_EL_SIZE); + } + /* fall through */ +#if SIZEOF_VOID_P == 4 + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_PTR: + case MONO_TYPE_FNPTR: +#endif + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_R4: + if ((((field->offset + 3) / sizeof (gpointer)) + offset) != pos) { + pos2 = ((field->offset + 3) / sizeof (gpointer)) + offset; + bitmap [pos2 / BITMAP_EL_SIZE] |= ((gsize)1) << (pos2 % BITMAP_EL_SIZE); + } + /* fall through */ + case MONO_TYPE_CHAR: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + if ((((field->offset + 1) / sizeof (gpointer)) + offset) != pos) { + pos2 = ((field->offset + 1) / sizeof (gpointer)) + offset; + bitmap [pos2 / BITMAP_EL_SIZE] |= ((gsize)1) << (pos2 % BITMAP_EL_SIZE); + } + /* fall through */ + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + bitmap [pos / BITMAP_EL_SIZE] |= ((gsize)1) << (pos % BITMAP_EL_SIZE); + break; + case MONO_TYPE_STRING: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_CLASS: + case MONO_TYPE_OBJECT: + case MONO_TYPE_ARRAY: + break; + case MONO_TYPE_GENERICINST: + if (!mono_type_generic_inst_is_valuetype (type)) { + break; + } else { + /* fall through */ + } + case MONO_TYPE_VALUETYPE: { + MonoClass *fclass = mono_class_from_mono_type (field->type); + /* remove the object header */ + compute_class_non_ref_bitmap (fclass, bitmap, size, pos - (sizeof (MonoObject) / sizeof (gpointer))); + break; + } + default: + g_assert_not_reached (); + break; + } + } + } + return bitmap; +} + +/** + * mono_class_insecure_overlapping: + * check if a class with explicit layout has references and non-references + * fields overlapping. + * + * Returns: TRUE if it is insecure to load the type. + */ +gboolean +mono_class_insecure_overlapping (MonoClass *klass) +{ + int max_set = 0; + gsize *bitmap; + gsize default_bitmap [4] = {0}; + gsize *nrbitmap; + gsize default_nrbitmap [4] = {0}; + int i, insecure = FALSE; + return FALSE; + + bitmap = compute_class_bitmap (klass, default_bitmap, sizeof (default_bitmap) * 8, 0, &max_set, FALSE); + nrbitmap = compute_class_non_ref_bitmap (klass, default_nrbitmap, sizeof (default_nrbitmap) * 8, 0); + + for (i = 0; i <= max_set; i += sizeof (bitmap [0]) * 8) { + int idx = i % (sizeof (bitmap [0]) * 8); + if (bitmap [idx] & nrbitmap [idx]) { + insecure = TRUE; + break; + } + } + if (bitmap != default_bitmap) + g_free (bitmap); + if (nrbitmap != default_nrbitmap) + g_free (nrbitmap); + if (insecure) { + g_print ("class %s.%s in assembly %s has overlapping references\n", klass->name_space, klass->name, klass->image->name); + return FALSE; + } + return insecure; +} +#endif + +MonoString* +ves_icall_string_alloc (int length) +{ + MonoError error; + MonoString *str = mono_string_new_size_checked (mono_domain_get (), length, &error); + mono_error_set_pending_exception (&error); + + return str; +} + +#define BITMAP_EL_SIZE (sizeof (gsize) * 8) + +/* LOCKING: Acquires the loader lock */ +/* + * Sets the following fields in KLASS: + * - gc_desc + * - gc_descr_inited + */ +void +mono_class_compute_gc_descriptor (MonoClass *klass) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + int max_set = 0; + gsize *bitmap; + gsize default_bitmap [4] = {0}; + MonoGCDescriptor gc_descr; + + if (!klass->inited) + mono_class_init (klass); + + if (klass->gc_descr_inited) + return; + + bitmap = default_bitmap; + if (klass == mono_defaults.string_class) { + gc_descr = mono_gc_make_descr_for_string (bitmap, 2); + } else if (klass->rank) { + mono_class_compute_gc_descriptor (klass->element_class); + if (MONO_TYPE_IS_REFERENCE (&klass->element_class->byval_arg)) { + gsize abm = 1; + gc_descr = mono_gc_make_descr_for_array (klass->byval_arg.type == MONO_TYPE_SZARRAY, &abm, 1, sizeof (gpointer)); + /*printf ("new array descriptor: 0x%x for %s.%s\n", class->gc_descr, + class->name_space, class->name);*/ + } else { + /* remove the object header */ + bitmap = compute_class_bitmap (klass->element_class, default_bitmap, sizeof (default_bitmap) * 8, - (int)(sizeof (MonoObject) / sizeof (gpointer)), &max_set, FALSE); + gc_descr = mono_gc_make_descr_for_array (klass->byval_arg.type == MONO_TYPE_SZARRAY, bitmap, mono_array_element_size (klass) / sizeof (gpointer), mono_array_element_size (klass)); + /*printf ("new vt array descriptor: 0x%x for %s.%s\n", class->gc_descr, + class->name_space, class->name);*/ + } + } else { + /*static int count = 0; + if (count++ > 58) + return;*/ + bitmap = compute_class_bitmap (klass, default_bitmap, sizeof (default_bitmap) * 8, 0, &max_set, FALSE); + /* + if (class->gc_descr == MONO_GC_DESCRIPTOR_NULL) + g_print ("disabling typed alloc (%d) for %s.%s\n", max_set, class->name_space, class->name); + */ + /*printf ("new descriptor: %p 0x%x for %s.%s\n", class->gc_descr, bitmap [0], class->name_space, class->name);*/ + + if (klass->has_weak_fields) { + gsize *weak_bitmap = NULL; + int weak_bitmap_nbits = 0; + + weak_bitmap = (gsize *)mono_class_alloc0 (klass, klass->instance_size / sizeof (gsize)); + if (mono_class_has_static_metadata (klass)) { + for (MonoClass *p = klass; p != NULL; p = p->parent) { + gpointer iter = NULL; + guint32 first_field_idx = mono_class_get_first_field_idx (p); + MonoClassField *field; + + while ((field = mono_class_get_fields (p, &iter))) { + guint32 field_idx = first_field_idx + (field - p->fields); + if (MONO_TYPE_IS_REFERENCE (field->type) && mono_assembly_is_weak_field (p->image, field_idx + 1)) { + int pos = field->offset / sizeof (gpointer); + if (pos + 1 > weak_bitmap_nbits) + weak_bitmap_nbits = pos + 1; + weak_bitmap [pos / BITMAP_EL_SIZE] |= ((gsize)1) << (pos % BITMAP_EL_SIZE); + } + } + } + } + + for (int pos = 0; pos < weak_bitmap_nbits; ++pos) { + if (weak_bitmap [pos / BITMAP_EL_SIZE] & ((gsize)1) << (pos % BITMAP_EL_SIZE)) { + /* Clear the normal bitmap so these refs don't keep an object alive */ + bitmap [pos / BITMAP_EL_SIZE] &= ~((gsize)1) << (pos % BITMAP_EL_SIZE); + } + } + + mono_loader_lock (); + mono_class_set_weak_bitmap (klass, weak_bitmap_nbits, weak_bitmap); + mono_loader_unlock (); + } + + gc_descr = mono_gc_make_descr_for_object (bitmap, max_set + 1, klass->instance_size); + } + + if (bitmap != default_bitmap) + g_free (bitmap); + + /* Publish the data */ + mono_loader_lock (); + klass->gc_descr = gc_descr; + mono_memory_barrier (); + klass->gc_descr_inited = TRUE; + mono_loader_unlock (); +} + +/** + * field_is_special_static: + * @fklass: The MonoClass to look up. + * @field: The MonoClassField describing the field. + * + * Returns: SPECIAL_STATIC_THREAD if the field is thread static, SPECIAL_STATIC_CONTEXT if it is context static, + * SPECIAL_STATIC_NONE otherwise. + */ +static gint32 +field_is_special_static (MonoClass *fklass, MonoClassField *field) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + MonoError error; + MonoCustomAttrInfo *ainfo; + int i; + ainfo = mono_custom_attrs_from_field_checked (fklass, field, &error); + mono_error_cleanup (&error); /* FIXME don't swallow the error? */ + if (!ainfo) + return FALSE; + for (i = 0; i < ainfo->num_attrs; ++i) { + MonoClass *klass = ainfo->attrs [i].ctor->klass; + if (klass->image == mono_defaults.corlib) { + if (strcmp (klass->name, "ThreadStaticAttribute") == 0) { + mono_custom_attrs_free (ainfo); + return SPECIAL_STATIC_THREAD; + } + else if (strcmp (klass->name, "ContextStaticAttribute") == 0) { + mono_custom_attrs_free (ainfo); + return SPECIAL_STATIC_CONTEXT; + } + } + } + mono_custom_attrs_free (ainfo); + return SPECIAL_STATIC_NONE; +} + +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) +#define mix(a,b,c) { \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} +#define final(a,b,c) { \ + c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ +} + +/* + * mono_method_get_imt_slot: + * + * The IMT slot is embedded into AOTed code, so this must return the same value + * for the same method across all executions. This means: + * - pointers shouldn't be used as hash values. + * - mono_metadata_str_hash () should be used for hashing strings. + */ +guint32 +mono_method_get_imt_slot (MonoMethod *method) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + MonoMethodSignature *sig; + int hashes_count; + guint32 *hashes_start, *hashes; + guint32 a, b, c; + int i; + + /* This can be used to stress tests the collision code */ + //return 0; + + /* + * We do this to simplify generic sharing. It will hurt + * performance in cases where a class implements two different + * instantiations of the same generic interface. + * The code in build_imt_slots () depends on this. + */ + if (method->is_inflated) + method = ((MonoMethodInflated*)method)->declaring; + + sig = mono_method_signature (method); + hashes_count = sig->param_count + 4; + hashes_start = (guint32 *)g_malloc (hashes_count * sizeof (guint32)); + hashes = hashes_start; + + if (! MONO_CLASS_IS_INTERFACE (method->klass)) { + g_error ("mono_method_get_imt_slot: %s.%s.%s is not an interface MonoMethod", + method->klass->name_space, method->klass->name, method->name); + } + + /* Initialize hashes */ + hashes [0] = mono_metadata_str_hash (method->klass->name); + hashes [1] = mono_metadata_str_hash (method->klass->name_space); + hashes [2] = mono_metadata_str_hash (method->name); + hashes [3] = mono_metadata_type_hash (sig->ret); + for (i = 0; i < sig->param_count; i++) { + hashes [4 + i] = mono_metadata_type_hash (sig->params [i]); + } + + /* Setup internal state */ + a = b = c = 0xdeadbeef + (((guint32)hashes_count)<<2); + + /* Handle most of the hashes */ + while (hashes_count > 3) { + a += hashes [0]; + b += hashes [1]; + c += hashes [2]; + mix (a,b,c); + hashes_count -= 3; + hashes += 3; + } + + /* Handle the last 3 hashes (all the case statements fall through) */ + switch (hashes_count) { + case 3 : c += hashes [2]; + case 2 : b += hashes [1]; + case 1 : a += hashes [0]; + final (a,b,c); + case 0: /* nothing left to add */ + break; + } + + g_free (hashes_start); + /* Report the result */ + return c % MONO_IMT_SIZE; +} +#undef rot +#undef mix +#undef final + +#define DEBUG_IMT 0 + +static void +add_imt_builder_entry (MonoImtBuilderEntry **imt_builder, MonoMethod *method, guint32 *imt_collisions_bitmap, int vtable_slot, int slot_num) { + MONO_REQ_GC_NEUTRAL_MODE; + + guint32 imt_slot = mono_method_get_imt_slot (method); + MonoImtBuilderEntry *entry; + + if (slot_num >= 0 && imt_slot != slot_num) { + /* we build just a single imt slot and this is not it */ + return; + } + + entry = (MonoImtBuilderEntry *)g_malloc0 (sizeof (MonoImtBuilderEntry)); + entry->key = method; + entry->value.vtable_slot = vtable_slot; + entry->next = imt_builder [imt_slot]; + if (imt_builder [imt_slot] != NULL) { + entry->children = imt_builder [imt_slot]->children + 1; + if (entry->children == 1) { + UnlockedIncrement (&mono_stats.imt_slots_with_collisions); + *imt_collisions_bitmap |= (1 << imt_slot); + } + } else { + entry->children = 0; + UnlockedIncrement (&mono_stats.imt_used_slots); + } + imt_builder [imt_slot] = entry; +#if DEBUG_IMT + { + char *method_name = mono_method_full_name (method, TRUE); + printf ("Added IMT slot for method (%p) %s: imt_slot = %d, vtable_slot = %d, colliding with other %d entries\n", + method, method_name, imt_slot, vtable_slot, entry->children); + g_free (method_name); + } +#endif +} + +#if DEBUG_IMT +static void +print_imt_entry (const char* message, MonoImtBuilderEntry *e, int num) { + if (e != NULL) { + MonoMethod *method = e->key; + printf (" * %s [%d]: (%p) '%s.%s.%s'\n", + message, + num, + method, + method->klass->name_space, + method->klass->name, + method->name); + } else { + printf (" * %s: NULL\n", message); + } +} +#endif + +static int +compare_imt_builder_entries (const void *p1, const void *p2) { + MonoImtBuilderEntry *e1 = *(MonoImtBuilderEntry**) p1; + MonoImtBuilderEntry *e2 = *(MonoImtBuilderEntry**) p2; + + return (e1->key < e2->key) ? -1 : ((e1->key > e2->key) ? 1 : 0); +} + +static int +imt_emit_ir (MonoImtBuilderEntry **sorted_array, int start, int end, GPtrArray *out_array) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + int count = end - start; + int chunk_start = out_array->len; + if (count < 4) { + int i; + for (i = start; i < end; ++i) { + MonoIMTCheckItem *item = g_new0 (MonoIMTCheckItem, 1); + item->key = sorted_array [i]->key; + item->value = sorted_array [i]->value; + item->has_target_code = sorted_array [i]->has_target_code; + item->is_equals = TRUE; + if (i < end - 1) + item->check_target_idx = out_array->len + 1; + else + item->check_target_idx = 0; + g_ptr_array_add (out_array, item); + } + } else { + int middle = start + count / 2; + MonoIMTCheckItem *item = g_new0 (MonoIMTCheckItem, 1); + + item->key = sorted_array [middle]->key; + item->is_equals = FALSE; + g_ptr_array_add (out_array, item); + imt_emit_ir (sorted_array, start, middle, out_array); + item->check_target_idx = imt_emit_ir (sorted_array, middle, end, out_array); + } + return chunk_start; +} + +static GPtrArray* +imt_sort_slot_entries (MonoImtBuilderEntry *entries) { + MONO_REQ_GC_NEUTRAL_MODE; + + int number_of_entries = entries->children + 1; + MonoImtBuilderEntry **sorted_array = (MonoImtBuilderEntry **)g_malloc (sizeof (MonoImtBuilderEntry*) * number_of_entries); + GPtrArray *result = g_ptr_array_new (); + MonoImtBuilderEntry *current_entry; + int i; + + for (current_entry = entries, i = 0; current_entry != NULL; current_entry = current_entry->next, i++) { + sorted_array [i] = current_entry; + } + qsort (sorted_array, number_of_entries, sizeof (MonoImtBuilderEntry*), compare_imt_builder_entries); + + /*for (i = 0; i < number_of_entries; i++) { + print_imt_entry (" sorted array:", sorted_array [i], i); + }*/ + + imt_emit_ir (sorted_array, 0, number_of_entries, result); + + g_free (sorted_array); + return result; +} + +static gpointer +initialize_imt_slot (MonoVTable *vtable, MonoDomain *domain, MonoImtBuilderEntry *imt_builder_entry, gpointer fail_tramp) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + if (imt_builder_entry != NULL) { + if (imt_builder_entry->children == 0 && !fail_tramp && !always_build_imt_trampolines) { + /* No collision, return the vtable slot contents */ + return vtable->vtable [imt_builder_entry->value.vtable_slot]; + } else { + /* Collision, build the trampoline */ + GPtrArray *imt_ir = imt_sort_slot_entries (imt_builder_entry); + gpointer result; + int i; + result = imt_trampoline_builder (vtable, domain, + (MonoIMTCheckItem**)imt_ir->pdata, imt_ir->len, fail_tramp); + for (i = 0; i < imt_ir->len; ++i) + g_free (g_ptr_array_index (imt_ir, i)); + g_ptr_array_free (imt_ir, TRUE); + return result; + } + } else { + if (fail_tramp) + return fail_tramp; + else + /* Empty slot */ + return NULL; + } +} + +static MonoImtBuilderEntry* +get_generic_virtual_entries (MonoDomain *domain, gpointer *vtable_slot); + +/* + * LOCKING: requires the loader and domain locks. + * +*/ +static void +build_imt_slots (MonoClass *klass, MonoVTable *vt, MonoDomain *domain, gpointer* imt, GSList *extra_interfaces, int slot_num) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + int i; + GSList *list_item; + guint32 imt_collisions_bitmap = 0; + MonoImtBuilderEntry **imt_builder = (MonoImtBuilderEntry **)g_calloc (MONO_IMT_SIZE, sizeof (MonoImtBuilderEntry*)); + int method_count = 0; + gboolean record_method_count_for_max_collisions = FALSE; + gboolean has_generic_virtual = FALSE, has_variant_iface = FALSE; + +#if DEBUG_IMT + printf ("Building IMT for class %s.%s slot %d\n", klass->name_space, klass->name, slot_num); +#endif + for (i = 0; i < klass->interface_offsets_count; ++i) { + MonoClass *iface = klass->interfaces_packed [i]; + int interface_offset = klass->interface_offsets_packed [i]; + int method_slot_in_interface, vt_slot; + + if (mono_class_has_variant_generic_params (iface)) + has_variant_iface = TRUE; + + mono_class_setup_methods (iface); + vt_slot = interface_offset; + int mcount = mono_class_get_method_count (iface); + for (method_slot_in_interface = 0; method_slot_in_interface < mcount; method_slot_in_interface++) { + MonoMethod *method; + + if (slot_num >= 0 && mono_class_is_ginst (iface)) { + /* + * The imt slot of the method is the same as for its declaring method, + * see the comment in mono_method_get_imt_slot (), so we can + * avoid inflating methods which will be discarded by + * add_imt_builder_entry anyway. + */ + method = mono_class_get_method_by_index (mono_class_get_generic_class (iface)->container_class, method_slot_in_interface); + if (mono_method_get_imt_slot (method) != slot_num) { + vt_slot ++; + continue; + } + } + method = mono_class_get_method_by_index (iface, method_slot_in_interface); + if (method->is_generic) { + has_generic_virtual = TRUE; + vt_slot ++; + continue; + } + + if (!(method->flags & METHOD_ATTRIBUTE_STATIC)) { + add_imt_builder_entry (imt_builder, method, &imt_collisions_bitmap, vt_slot, slot_num); + vt_slot ++; + } + } + } + if (extra_interfaces) { + int interface_offset = klass->vtable_size; + + for (list_item = extra_interfaces; list_item != NULL; list_item=list_item->next) { + MonoClass* iface = (MonoClass *)list_item->data; + int method_slot_in_interface; + int mcount = mono_class_get_method_count (iface); + for (method_slot_in_interface = 0; method_slot_in_interface < mcount; method_slot_in_interface++) { + MonoMethod *method = mono_class_get_method_by_index (iface, method_slot_in_interface); + + if (method->is_generic) + has_generic_virtual = TRUE; + add_imt_builder_entry (imt_builder, method, &imt_collisions_bitmap, interface_offset + method_slot_in_interface, slot_num); + } + interface_offset += mcount; + } + } + for (i = 0; i < MONO_IMT_SIZE; ++i) { + /* overwrite the imt slot only if we're building all the entries or if + * we're building this specific one + */ + if (slot_num < 0 || i == slot_num) { + MonoImtBuilderEntry *entries = get_generic_virtual_entries (domain, &imt [i]); + + if (entries) { + if (imt_builder [i]) { + MonoImtBuilderEntry *entry; + + /* Link entries with imt_builder [i] */ + for (entry = entries; entry->next; entry = entry->next) { +#if DEBUG_IMT + MonoMethod *method = (MonoMethod*)entry->key; + char *method_name = mono_method_full_name (method, TRUE); + printf ("Added extra entry for method (%p) %s: imt_slot = %d\n", method, method_name, i); + g_free (method_name); +#endif + } + entry->next = imt_builder [i]; + entries->children += imt_builder [i]->children + 1; + } + imt_builder [i] = entries; + } + + if (has_generic_virtual || has_variant_iface) { + /* + * There might be collisions later when the the trampoline is expanded. + */ + imt_collisions_bitmap |= (1 << i); + + /* + * The IMT trampoline might be called with an instance of one of the + * generic virtual methods, so has to fallback to the IMT trampoline. + */ + imt [i] = initialize_imt_slot (vt, domain, imt_builder [i], callbacks.get_imt_trampoline (vt, i)); + } else { + imt [i] = initialize_imt_slot (vt, domain, imt_builder [i], NULL); + } +#if DEBUG_IMT + printf ("initialize_imt_slot[%d]: %p methods %d\n", i, imt [i], imt_builder [i]->children + 1); +#endif + } + + if (imt_builder [i] != NULL) { + int methods_in_slot = imt_builder [i]->children + 1; + if (methods_in_slot > UnlockedRead (&mono_stats.imt_max_collisions_in_slot)) { + UnlockedWrite (&mono_stats.imt_max_collisions_in_slot, methods_in_slot); + record_method_count_for_max_collisions = TRUE; + } + method_count += methods_in_slot; + } + } + + UnlockedAdd (&mono_stats.imt_number_of_methods, method_count); + if (record_method_count_for_max_collisions) { + UnlockedWrite (&mono_stats.imt_method_count_when_max_collisions, method_count); + } + + for (i = 0; i < MONO_IMT_SIZE; i++) { + MonoImtBuilderEntry* entry = imt_builder [i]; + while (entry != NULL) { + MonoImtBuilderEntry* next = entry->next; + g_free (entry); + entry = next; + } + } + g_free (imt_builder); + /* we OR the bitmap since we may build just a single imt slot at a time */ + vt->imt_collisions_bitmap |= imt_collisions_bitmap; +} + +static void +build_imt (MonoClass *klass, MonoVTable *vt, MonoDomain *domain, gpointer* imt, GSList *extra_interfaces) { + MONO_REQ_GC_NEUTRAL_MODE; + + build_imt_slots (klass, vt, domain, imt, extra_interfaces, -1); +} + +/** + * mono_vtable_build_imt_slot: + * \param vtable virtual object table struct + * \param imt_slot slot in the IMT table + * Fill the given \p imt_slot in the IMT table of \p vtable with + * a trampoline or a trampoline for the case of collisions. + * This is part of the internal mono API. + * LOCKING: Take the domain lock. + */ +void +mono_vtable_build_imt_slot (MonoVTable* vtable, int imt_slot) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + gpointer *imt = (gpointer*)vtable; + imt -= MONO_IMT_SIZE; + g_assert (imt_slot >= 0 && imt_slot < MONO_IMT_SIZE); + + /* no support for extra interfaces: the proxy objects will need + * to build the complete IMT + * Update and heck needs to ahppen inside the proper domain lock, as all + * the changes made to a MonoVTable. + */ + mono_loader_lock (); /*FIXME build_imt_slots requires the loader lock.*/ + mono_domain_lock (vtable->domain); + /* we change the slot only if it wasn't changed from the generic imt trampoline already */ + if (!callbacks.imt_entry_inited (vtable, imt_slot)) + build_imt_slots (vtable->klass, vtable, vtable->domain, imt, NULL, imt_slot); + mono_domain_unlock (vtable->domain); + mono_loader_unlock (); +} + +#define THUNK_THRESHOLD 10 + +/** + * mono_method_alloc_generic_virtual_trampoline: + * \param domain a domain + * \param size size in bytes + * Allocs \p size bytes to be used for the code of a generic virtual + * trampoline. It's either allocated from the domain's code manager or + * reused from a previously invalidated piece. + * LOCKING: The domain lock must be held. + */ +gpointer +mono_method_alloc_generic_virtual_trampoline (MonoDomain *domain, int size) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + static gboolean inited = FALSE; + static int generic_virtual_trampolines_size = 0; + + if (!inited) { + mono_counters_register ("Generic virtual trampoline bytes", + MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &generic_virtual_trampolines_size); + inited = TRUE; + } + generic_virtual_trampolines_size += size; + + return mono_domain_code_reserve (domain, size); +} + +typedef struct _GenericVirtualCase { + MonoMethod *method; + gpointer code; + int count; + struct _GenericVirtualCase *next; +} GenericVirtualCase; + +/* + * get_generic_virtual_entries: + * + * Return IMT entries for the generic virtual method instances and + * variant interface methods for vtable slot + * VTABLE_SLOT. + */ +static MonoImtBuilderEntry* +get_generic_virtual_entries (MonoDomain *domain, gpointer *vtable_slot) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + GenericVirtualCase *list; + MonoImtBuilderEntry *entries; + + mono_domain_lock (domain); + if (!domain->generic_virtual_cases) + domain->generic_virtual_cases = g_hash_table_new (mono_aligned_addr_hash, NULL); + + list = (GenericVirtualCase *)g_hash_table_lookup (domain->generic_virtual_cases, vtable_slot); + + entries = NULL; + for (; list; list = list->next) { + MonoImtBuilderEntry *entry; + + if (list->count < THUNK_THRESHOLD) + continue; + + entry = g_new0 (MonoImtBuilderEntry, 1); + entry->key = list->method; + entry->value.target_code = mono_get_addr_from_ftnptr (list->code); + entry->has_target_code = 1; + if (entries) + entry->children = entries->children + 1; + entry->next = entries; + entries = entry; + } + + mono_domain_unlock (domain); + + /* FIXME: Leaking memory ? */ + return entries; +} + +/** + * \param domain a domain + * \param vtable_slot pointer to the vtable slot + * \param method the inflated generic virtual method + * \param code the method's code + * + * Registers a call via unmanaged code to a generic virtual method + * instantiation or variant interface method. If the number of calls reaches a threshold + * (THUNK_THRESHOLD), the method is added to the vtable slot's generic + * virtual method trampoline. + */ +void +mono_method_add_generic_virtual_invocation (MonoDomain *domain, MonoVTable *vtable, + gpointer *vtable_slot, + MonoMethod *method, gpointer code) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + static gboolean inited = FALSE; + static int num_added = 0; + static int num_freed = 0; + + GenericVirtualCase *gvc, *list; + MonoImtBuilderEntry *entries; + int i; + GPtrArray *sorted; + + mono_domain_lock (domain); + if (!domain->generic_virtual_cases) + domain->generic_virtual_cases = g_hash_table_new (mono_aligned_addr_hash, NULL); + + if (!inited) { + mono_counters_register ("Generic virtual cases", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_added); + mono_counters_register ("Freed IMT trampolines", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_freed); + inited = TRUE; + } + + /* Check whether the case was already added */ + list = (GenericVirtualCase *)g_hash_table_lookup (domain->generic_virtual_cases, vtable_slot); + gvc = list; + while (gvc) { + if (gvc->method == method) + break; + gvc = gvc->next; + } + + /* If not found, make a new one */ + if (!gvc) { + gvc = (GenericVirtualCase *)mono_domain_alloc (domain, sizeof (GenericVirtualCase)); + gvc->method = method; + gvc->code = code; + gvc->count = 0; + gvc->next = (GenericVirtualCase *)g_hash_table_lookup (domain->generic_virtual_cases, vtable_slot); + + g_hash_table_insert (domain->generic_virtual_cases, vtable_slot, gvc); + + num_added++; + } + + if (++gvc->count == THUNK_THRESHOLD) { + gpointer *old_thunk = (void **)*vtable_slot; + gpointer vtable_trampoline = NULL; + gpointer imt_trampoline = NULL; + + if ((gpointer)vtable_slot < (gpointer)vtable) { + int displacement = (gpointer*)vtable_slot - (gpointer*)vtable; + int imt_slot = MONO_IMT_SIZE + displacement; + + /* Force the rebuild of the trampoline at the next call */ + imt_trampoline = callbacks.get_imt_trampoline (vtable, imt_slot); + *vtable_slot = imt_trampoline; + } else { + vtable_trampoline = callbacks.get_vtable_trampoline ? callbacks.get_vtable_trampoline (vtable, (gpointer*)vtable_slot - (gpointer*)vtable->vtable) : NULL; + + entries = get_generic_virtual_entries (domain, vtable_slot); + + sorted = imt_sort_slot_entries (entries); + + *vtable_slot = imt_trampoline_builder (NULL, domain, (MonoIMTCheckItem**)sorted->pdata, sorted->len, + vtable_trampoline); + + while (entries) { + MonoImtBuilderEntry *next = entries->next; + g_free (entries); + entries = next; + } + + for (i = 0; i < sorted->len; ++i) + g_free (g_ptr_array_index (sorted, i)); + g_ptr_array_free (sorted, TRUE); + + if (old_thunk != vtable_trampoline && old_thunk != imt_trampoline) + num_freed ++; + } + } + + mono_domain_unlock (domain); +} + +static MonoVTable *mono_class_create_runtime_vtable (MonoDomain *domain, MonoClass *klass, MonoError *error); + +/** + * mono_class_vtable: + * \param domain the application domain + * \param class the class to initialize + * VTables are domain specific because we create domain specific code, and + * they contain the domain specific static class data. + * On failure, NULL is returned, and \c class->exception_type is set. + */ +MonoVTable * +mono_class_vtable (MonoDomain *domain, MonoClass *klass) +{ + MonoError error; + MonoVTable* vtable = mono_class_vtable_full (domain, klass, &error); + mono_error_cleanup (&error); + return vtable; +} + +/** + * mono_class_vtable_full: + * \param domain the application domain + * \param class the class to initialize + * \param error set on failure. + * VTables are domain specific because we create domain specific code, and + * they contain the domain specific static class data. + */ +MonoVTable * +mono_class_vtable_full (MonoDomain *domain, MonoClass *klass, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoClassRuntimeInfo *runtime_info; + + error_init (error); + + g_assert (klass); + + if (mono_class_has_failure (klass)) { + mono_error_set_for_class_failure (error, klass); + return NULL; + } + + /* this check can be inlined in jitted code, too */ + runtime_info = klass->runtime_info; + if (runtime_info && runtime_info->max_domain >= domain->domain_id && runtime_info->domain_vtables [domain->domain_id]) + return runtime_info->domain_vtables [domain->domain_id]; + return mono_class_create_runtime_vtable (domain, klass, error); +} + +/** + * mono_class_try_get_vtable: + * \param domain the application domain + * \param class the class to initialize + * This function tries to get the associated vtable from \p class if + * it was already created. + */ +MonoVTable * +mono_class_try_get_vtable (MonoDomain *domain, MonoClass *klass) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + MonoClassRuntimeInfo *runtime_info; + + g_assert (klass); + + runtime_info = klass->runtime_info; + if (runtime_info && runtime_info->max_domain >= domain->domain_id && runtime_info->domain_vtables [domain->domain_id]) + return runtime_info->domain_vtables [domain->domain_id]; + return NULL; +} + +static gpointer* +alloc_vtable (MonoDomain *domain, size_t vtable_size, size_t imt_table_bytes) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + size_t alloc_offset; + + /* + * We want the pointer to the MonoVTable aligned to 8 bytes because SGen uses three + * address bits. The IMT has an odd number of entries, however, so on 32 bits the + * alignment will be off. In that case we allocate 4 more bytes and skip over them. + */ + if (sizeof (gpointer) == 4 && (imt_table_bytes & 7)) { + g_assert ((imt_table_bytes & 7) == 4); + vtable_size += 4; + alloc_offset = 4; + } else { + alloc_offset = 0; + } + + return (gpointer*) ((char*)mono_domain_alloc0 (domain, vtable_size) + alloc_offset); +} + +static MonoVTable * +mono_class_create_runtime_vtable (MonoDomain *domain, MonoClass *klass, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoVTable *vt; + MonoClassRuntimeInfo *runtime_info, *old_info; + MonoClassField *field; + char *t; + int i, vtable_slots; + size_t imt_table_bytes; + int gc_bits; + guint32 vtable_size, class_size; + gpointer iter; + gpointer *interface_offsets; + + error_init (error); + + mono_loader_lock (); /*FIXME mono_class_init acquires it*/ + mono_domain_lock (domain); + runtime_info = klass->runtime_info; + if (runtime_info && runtime_info->max_domain >= domain->domain_id && runtime_info->domain_vtables [domain->domain_id]) { + mono_domain_unlock (domain); + mono_loader_unlock (); + return runtime_info->domain_vtables [domain->domain_id]; + } + if (!klass->inited || mono_class_has_failure (klass)) { + if (!mono_class_init (klass) || mono_class_has_failure (klass)) { + mono_domain_unlock (domain); + mono_loader_unlock (); + mono_error_set_for_class_failure (error, klass); + return NULL; + } + } + + /* Array types require that their element type be valid*/ + if (klass->byval_arg.type == MONO_TYPE_ARRAY || klass->byval_arg.type == MONO_TYPE_SZARRAY) { + MonoClass *element_class = klass->element_class; + if (!element_class->inited) + mono_class_init (element_class); + + /*mono_class_init can leave the vtable layout to be lazily done and we can't afford this here*/ + if (!mono_class_has_failure (element_class) && !element_class->vtable_size) + mono_class_setup_vtable (element_class); + + if (mono_class_has_failure (element_class)) { + /*Can happen if element_class only got bad after mono_class_setup_vtable*/ + if (!mono_class_has_failure (klass)) + mono_class_set_type_load_failure (klass, ""); + mono_domain_unlock (domain); + mono_loader_unlock (); + mono_error_set_for_class_failure (error, klass); + return NULL; + } + } + + /* + * For some classes, mono_class_init () already computed klass->vtable_size, and + * that is all that is needed because of the vtable trampolines. + */ + if (!klass->vtable_size) + mono_class_setup_vtable (klass); + + if (mono_class_is_ginst (klass) && !klass->vtable) + mono_class_check_vtable_constraints (klass, NULL); + + /* Initialize klass->has_finalize */ + mono_class_has_finalizer (klass); + + if (mono_class_has_failure (klass)) { + mono_domain_unlock (domain); + mono_loader_unlock (); + mono_error_set_for_class_failure (error, klass); + return NULL; + } + + vtable_slots = klass->vtable_size; + /* we add an additional vtable slot to store the pointer to static field data only when needed */ + class_size = mono_class_data_size (klass); + if (class_size) + vtable_slots++; + + if (klass->interface_offsets_count) { + imt_table_bytes = sizeof (gpointer) * (MONO_IMT_SIZE); + UnlockedIncrement (&mono_stats.imt_number_of_tables); + UnlockedAdd (&mono_stats.imt_tables_size, imt_table_bytes); + } else { + imt_table_bytes = 0; + } + + vtable_size = imt_table_bytes + MONO_SIZEOF_VTABLE + vtable_slots * sizeof (gpointer); + + UnlockedIncrement (&mono_stats.used_class_count); + UnlockedAdd (&mono_stats.class_vtable_size, vtable_size); + + interface_offsets = alloc_vtable (domain, vtable_size, imt_table_bytes); + vt = (MonoVTable*) ((char*)interface_offsets + imt_table_bytes); + g_assert (!((gsize)vt & 7)); + + vt->klass = klass; + vt->rank = klass->rank; + vt->domain = domain; + + MONO_PROFILER_RAISE (vtable_loading, (vt)); + + mono_class_compute_gc_descriptor (klass); + /* + * For Boehm: + * We can't use typed allocation in the non-root domains, since the + * collector needs the GC descriptor stored in the vtable even after + * the mempool containing the vtable is destroyed when the domain is + * unloaded. An alternative might be to allocate vtables in the GC + * heap, but this does not seem to work (it leads to crashes inside + * libgc). If that approach is tried, two gc descriptors need to be + * allocated for each class: one for the root domain, and one for all + * other domains. The second descriptor should contain a bit for the + * vtable field in MonoObject, since we can no longer assume the + * vtable is reachable by other roots after the appdomain is unloaded. + */ +#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); + g_assert (!(gc_bits & ~((1 << MONO_VTABLE_AVAILABLE_GC_BITS) - 1))); + + vt->gc_bits = gc_bits; + + if (class_size) { + /* we store the static field pointer at the end of the vtable: vt->vtable [class->vtable_size] */ + if (klass->has_static_refs) { + MonoGCDescriptor statics_gc_descr; + int max_set = 0; + gsize default_bitmap [4] = {0}; + gsize *bitmap; + + bitmap = compute_class_bitmap (klass, default_bitmap, sizeof (default_bitmap) * 8, 0, &max_set, TRUE); + /*g_print ("bitmap 0x%x for %s.%s (size: %d)\n", bitmap [0], klass->name_space, klass->name, class_size);*/ + statics_gc_descr = mono_gc_make_descr_from_bitmap (bitmap, max_set + 1); + vt->vtable [klass->vtable_size] = mono_gc_alloc_fixed (class_size, statics_gc_descr, MONO_ROOT_SOURCE_STATIC, vt, "Static Fields"); + + if (bitmap != default_bitmap) + g_free (bitmap); + } else { + vt->vtable [klass->vtable_size] = mono_domain_alloc0 (domain, class_size); + } + vt->has_static_fields = TRUE; + UnlockedAdd (&mono_stats.class_static_data_size, class_size); + } + + iter = NULL; + while ((field = mono_class_get_fields (klass, &iter))) { + if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC)) + continue; + if (mono_field_is_deleted (field)) + continue; + if (!(field->type->attrs & FIELD_ATTRIBUTE_LITERAL)) { + gint32 special_static = klass->no_special_static_fields ? SPECIAL_STATIC_NONE : field_is_special_static (klass, field); + if (special_static != SPECIAL_STATIC_NONE) { + guint32 size, offset; + gint32 align; + gsize default_bitmap [4] = {0}; + gsize *bitmap; + int max_set = 0; + int numbits; + MonoClass *fclass; + if (mono_type_is_reference (field->type)) { + default_bitmap [0] = 1; + numbits = 1; + bitmap = default_bitmap; + } else if (mono_type_is_struct (field->type)) { + fclass = mono_class_from_mono_type (field->type); + bitmap = compute_class_bitmap (fclass, default_bitmap, sizeof (default_bitmap) * 8, - (int)(sizeof (MonoObject) / sizeof (gpointer)), &max_set, FALSE); + numbits = max_set + 1; + } else { + default_bitmap [0] = 0; + numbits = 0; + bitmap = default_bitmap; + } + size = mono_type_size (field->type, &align); + offset = mono_alloc_special_static_data (special_static, size, align, (uintptr_t*)bitmap, numbits); + if (!domain->special_static_fields) + domain->special_static_fields = g_hash_table_new (NULL, NULL); + g_hash_table_insert (domain->special_static_fields, field, GUINT_TO_POINTER (offset)); + if (bitmap != default_bitmap) + g_free (bitmap); + /* + * This marks the field as special static to speed up the + * checks in mono_field_static_get/set_value (). + */ + field->offset = -1; + continue; + } + } + if ((field->type->attrs & FIELD_ATTRIBUTE_HAS_FIELD_RVA)) { + MonoClass *fklass = mono_class_from_mono_type (field->type); + const char *data = mono_field_get_data (field); + + g_assert (!(field->type->attrs & FIELD_ATTRIBUTE_HAS_DEFAULT)); + t = (char*)mono_vtable_get_static_field_data (vt) + field->offset; + /* some fields don't really have rva, they are just zeroed (bss? bug #343083) */ + if (!data) + continue; + if (fklass->valuetype) { + memcpy (t, data, mono_class_value_size (fklass, NULL)); + } else { + /* 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; + } + } + + vt->max_interface_id = klass->max_interface_id; + vt->interface_bitmap = klass->interface_bitmap; + + //printf ("Initializing VT for class %s (interface_offsets_count = %d)\n", + // class->name, klass->interface_offsets_count); + + /* Initialize vtable */ + if (callbacks.get_vtable_trampoline) { + // This also covers the AOT case + for (i = 0; i < klass->vtable_size; ++i) { + vt->vtable [i] = callbacks.get_vtable_trampoline (vt, i); + } + } else { + mono_class_setup_vtable (klass); + + for (i = 0; i < klass->vtable_size; ++i) { + MonoMethod *cm; + + cm = klass->vtable [i]; + if (cm) { + vt->vtable [i] = callbacks.create_jit_trampoline (domain, cm, error); + if (!is_ok (error)) { + mono_domain_unlock (domain); + mono_loader_unlock (); + MONO_PROFILER_RAISE (vtable_failed, (vt)); + return NULL; + } + } + } + } + + if (imt_table_bytes) { + /* Now that the vtable is full, we can actually fill up the IMT */ + for (i = 0; i < MONO_IMT_SIZE; ++i) + interface_offsets [i] = callbacks.get_imt_trampoline (vt, i); + } + + /* + * FIXME: Is it ok to allocate while holding the domain/loader locks ? If not, we can release them, allocate, then + * re-acquire them and check if another thread has created the vtable in the meantime. + */ + /* Special case System.MonoType to avoid infinite recursion */ + if (klass != mono_defaults.runtimetype_class) { + vt->type = mono_type_get_object_checked (domain, &klass->byval_arg, error); + if (!is_ok (error)) { + mono_domain_unlock (domain); + mono_loader_unlock (); + MONO_PROFILER_RAISE (vtable_failed, (vt)); + return NULL; + } + + if (mono_object_get_class ((MonoObject *)vt->type) != mono_defaults.runtimetype_class) + /* This is unregistered in + unregister_vtable_reflection_type() in + domain.c. */ + MONO_GC_REGISTER_ROOT_IF_MOVING (vt->type, MONO_ROOT_SOURCE_REFLECTION, vt, "Reflection Type Object"); + } + + mono_vtable_set_is_remote (vt, mono_class_is_contextbound (klass)); + + /* class_vtable_array keeps an array of created vtables + */ + g_ptr_array_add (domain->class_vtable_array, vt); + /* klass->runtime_info is protected by the loader lock, both when + * it it enlarged and when it is stored info. + */ + + /* + * Store the vtable in klass->runtime_info. + * klass->runtime_info is accessed without locking, so this do this last after the vtable has been constructed. + */ + mono_memory_barrier (); + + old_info = klass->runtime_info; + if (old_info && old_info->max_domain >= domain->domain_id) { + /* someone already created a large enough runtime info */ + old_info->domain_vtables [domain->domain_id] = vt; + } else { + int new_size = domain->domain_id; + if (old_info) + new_size = MAX (new_size, old_info->max_domain); + new_size++; + /* make the new size a power of two */ + i = 2; + while (new_size > i) + i <<= 1; + new_size = i; + /* this is a bounded memory retention issue: may want to + * handle it differently when we'll have a rcu-like system. + */ + runtime_info = (MonoClassRuntimeInfo *)mono_image_alloc0 (klass->image, MONO_SIZEOF_CLASS_RUNTIME_INFO + new_size * sizeof (gpointer)); + runtime_info->max_domain = new_size - 1; + /* copy the stuff from the older info */ + if (old_info) { + memcpy (runtime_info->domain_vtables, old_info->domain_vtables, (old_info->max_domain + 1) * sizeof (gpointer)); + } + runtime_info->domain_vtables [domain->domain_id] = vt; + /* keep this last*/ + mono_memory_barrier (); + klass->runtime_info = runtime_info; + } + + if (klass == mono_defaults.runtimetype_class) { + vt->type = mono_type_get_object_checked (domain, &klass->byval_arg, error); + if (!is_ok (error)) { + mono_domain_unlock (domain); + mono_loader_unlock (); + MONO_PROFILER_RAISE (vtable_failed, (vt)); + return NULL; + } + + if (mono_object_get_class ((MonoObject *)vt->type) != mono_defaults.runtimetype_class) + /* This is unregistered in + unregister_vtable_reflection_type() in + domain.c. */ + MONO_GC_REGISTER_ROOT_IF_MOVING(vt->type, MONO_ROOT_SOURCE_REFLECTION, vt, "Reflection Type Object"); + } + + mono_domain_unlock (domain); + mono_loader_unlock (); + + /* make sure the parent is initialized */ + /*FIXME shouldn't this fail the current type?*/ + if (klass->parent) + mono_class_vtable_full (domain, klass->parent, error); + + MONO_PROFILER_RAISE (vtable_loaded, (vt)); + + return vt; +} + +#ifndef DISABLE_REMOTING +/** + * mono_remote_class_is_interface_proxy: + * \param remote_class + * + * Returns TRUE if the given remote class is a proxying an interface (as + * opposed to a class deriving from MarshalByRefObject). + */ +gboolean +mono_remote_class_is_interface_proxy (MonoRemoteClass *remote_class) +{ + /* This if condition is taking advantage of how mono_remote_class () + * works: if that code changes, this needs to change too. */ + return (remote_class->interface_count >= 1 && + remote_class->proxy_class == mono_defaults.marshalbyrefobject_class); +} + +/** + * mono_class_proxy_vtable: + * \param domain the application domain + * \param remove_class the remote class + * \param error set on error + * Creates a vtable for transparent proxies. It is basically + * a copy of the real vtable of the class wrapped in \p remote_class, + * but all function pointers invoke the remoting functions, and + * \c vtable->klass points to the transparent proxy class, and not to \p class. + * + * On failure returns NULL and sets \p error + */ +static MonoVTable * +mono_class_proxy_vtable (MonoDomain *domain, MonoRemoteClass *remote_class, MonoRemotingTarget target_type, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoVTable *vt, *pvt; + int i, j, vtsize, extra_interface_vtsize = 0; + guint32 max_interface_id; + MonoClass *k; + GSList *extra_interfaces = NULL; + MonoClass *klass = remote_class->proxy_class; + gpointer *interface_offsets; + uint8_t *bitmap = NULL; + int bsize; + size_t imt_table_bytes; + +#ifdef COMPRESSED_INTERFACE_BITMAP + int bcsize; +#endif + + error_init (error); + + vt = mono_class_vtable (domain, klass); + g_assert (vt); /*FIXME property handle failure*/ + max_interface_id = vt->max_interface_id; + + /* Calculate vtable space for extra interfaces */ + for (j = 0; j < remote_class->interface_count; j++) { + MonoClass* iclass = remote_class->interfaces[j]; + GPtrArray *ifaces; + int method_count; + + /*FIXME test for interfaces with variant generic arguments*/ + if (MONO_CLASS_IMPLEMENTS_INTERFACE (klass, iclass->interface_id)) + continue; /* interface implemented by the class */ + if (g_slist_find (extra_interfaces, iclass)) + continue; + + extra_interfaces = g_slist_prepend (extra_interfaces, iclass); + + method_count = mono_class_num_methods (iclass); + + ifaces = mono_class_get_implemented_interfaces (iclass, error); + goto_if_nok (error, failure); + if (ifaces) { + for (i = 0; i < ifaces->len; ++i) { + MonoClass *ic = (MonoClass *)g_ptr_array_index (ifaces, i); + /*FIXME test for interfaces with variant generic arguments*/ + if (MONO_CLASS_IMPLEMENTS_INTERFACE (klass, ic->interface_id)) + continue; /* interface implemented by the class */ + if (g_slist_find (extra_interfaces, ic)) + continue; + extra_interfaces = g_slist_prepend (extra_interfaces, ic); + method_count += mono_class_num_methods (ic); + } + g_ptr_array_free (ifaces, TRUE); + ifaces = NULL; + } + + extra_interface_vtsize += method_count * sizeof (gpointer); + if (iclass->max_interface_id > max_interface_id) max_interface_id = iclass->max_interface_id; + } + + imt_table_bytes = sizeof (gpointer) * MONO_IMT_SIZE; + UnlockedIncrement (&mono_stats.imt_number_of_tables); + UnlockedAdd (&mono_stats.imt_tables_size, imt_table_bytes); + + vtsize = imt_table_bytes + MONO_SIZEOF_VTABLE + klass->vtable_size * sizeof (gpointer); + + UnlockedAdd (&mono_stats.class_vtable_size, vtsize + extra_interface_vtsize); + + interface_offsets = alloc_vtable (domain, vtsize + extra_interface_vtsize, imt_table_bytes); + pvt = (MonoVTable*) ((char*)interface_offsets + imt_table_bytes); + g_assert (!((gsize)pvt & 7)); + + memcpy (pvt, vt, MONO_SIZEOF_VTABLE + klass->vtable_size * sizeof (gpointer)); + + pvt->klass = mono_defaults.transparent_proxy_class; + + MONO_PROFILER_RAISE (vtable_loading, (pvt)); + + /* we need to keep the GC descriptor for a transparent proxy or we confuse the precise GC */ + pvt->gc_descr = mono_defaults.transparent_proxy_class->gc_descr; + + if (mono_remote_class_is_interface_proxy (remote_class)) { + /* If it's a transparent proxy for an interface, set the + * MonoVTable:type to the interface type, not the placeholder + * MarshalByRefObject class. This is used when mini JITs calls + * to Object.GetType () + */ + MonoType *itf_proxy_type = &remote_class->interfaces[0]->byval_arg; + pvt->type = mono_type_get_object_checked (domain, itf_proxy_type, error); + goto_if_nok (error, failure); + } + + /* initialize vtable */ + mono_class_setup_vtable (klass); + for (i = 0; i < klass->vtable_size; ++i) { + MonoMethod *cm; + + if ((cm = klass->vtable [i])) { + pvt->vtable [i] = create_remoting_trampoline (domain, cm, target_type, error); + goto_if_nok (error, failure); + } else + pvt->vtable [i] = NULL; + } + + if (mono_class_is_abstract (klass)) { + /* create trampolines for abstract methods */ + for (k = klass; k; k = k->parent) { + MonoMethod* m; + gpointer iter = NULL; + while ((m = mono_class_get_methods (k, &iter))) + if (!pvt->vtable [m->slot]) { + pvt->vtable [m->slot] = create_remoting_trampoline (domain, m, target_type, error); + goto_if_nok (error, failure); + } + } + } + + pvt->max_interface_id = max_interface_id; + bsize = sizeof (guint8) * (max_interface_id/8 + 1 ); +#ifdef COMPRESSED_INTERFACE_BITMAP + bitmap = (uint8_t *)g_malloc0 (bsize); +#else + bitmap = (uint8_t *)mono_domain_alloc0 (domain, bsize); +#endif + + for (i = 0; i < klass->interface_offsets_count; ++i) { + int interface_id = klass->interfaces_packed [i]->interface_id; + bitmap [interface_id >> 3] |= (1 << (interface_id & 7)); + } + + if (extra_interfaces) { + int slot = klass->vtable_size; + MonoClass* interf; + gpointer iter; + MonoMethod* cm; + GSList *list_item; + + /* Create trampolines for the methods of the interfaces */ + for (list_item = extra_interfaces; list_item != NULL; list_item=list_item->next) { + interf = (MonoClass *)list_item->data; + + bitmap [interf->interface_id >> 3] |= (1 << (interf->interface_id & 7)); + + iter = NULL; + j = 0; + while ((cm = mono_class_get_methods (interf, &iter))) { + pvt->vtable [slot + j++] = create_remoting_trampoline (domain, cm, target_type, error); + goto_if_nok (error, failure); + } + + slot += mono_class_num_methods (interf); + } + } + + /* Now that the vtable is full, we can actually fill up the IMT */ + build_imt (klass, pvt, domain, interface_offsets, extra_interfaces); + if (extra_interfaces) { + g_slist_free (extra_interfaces); + } + +#ifdef COMPRESSED_INTERFACE_BITMAP + bcsize = mono_compress_bitmap (NULL, bitmap, bsize); + pvt->interface_bitmap = mono_domain_alloc0 (domain, bcsize); + mono_compress_bitmap (pvt->interface_bitmap, bitmap, bsize); + g_free (bitmap); +#else + pvt->interface_bitmap = bitmap; +#endif + MONO_PROFILER_RAISE (vtable_loaded, (pvt)); + return pvt; +failure: + if (extra_interfaces) + g_slist_free (extra_interfaces); +#ifdef COMPRESSED_INTERFACE_BITMAP + g_free (bitmap); +#endif + MONO_PROFILER_RAISE (vtable_failed, (pvt)); + return NULL; +} + +#endif /* DISABLE_REMOTING */ + +/** + * mono_class_field_is_special_static: + * \returns whether \p field is a thread/context static field. + */ +gboolean +mono_class_field_is_special_static (MonoClassField *field) +{ + MONO_REQ_GC_NEUTRAL_MODE + + if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC)) + return FALSE; + if (mono_field_is_deleted (field)) + return FALSE; + if (!(field->type->attrs & FIELD_ATTRIBUTE_LITERAL)) { + if (field_is_special_static (field->parent, field) != SPECIAL_STATIC_NONE) + return TRUE; + } + return FALSE; +} + +/** + * mono_class_field_get_special_static_type: + * \param field The \c MonoClassField describing the field. + * \returns \c SPECIAL_STATIC_THREAD if the field is thread static, \c SPECIAL_STATIC_CONTEXT if it is context static, + * \c SPECIAL_STATIC_NONE otherwise. + */ +guint32 +mono_class_field_get_special_static_type (MonoClassField *field) +{ + MONO_REQ_GC_NEUTRAL_MODE + + if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC)) + return SPECIAL_STATIC_NONE; + if (mono_field_is_deleted (field)) + return SPECIAL_STATIC_NONE; + if (!(field->type->attrs & FIELD_ATTRIBUTE_LITERAL)) + return field_is_special_static (field->parent, field); + return SPECIAL_STATIC_NONE; +} + +/** + * mono_class_has_special_static_fields: + * \returns whether \p klass has any thread/context static fields. + */ +gboolean +mono_class_has_special_static_fields (MonoClass *klass) +{ + MONO_REQ_GC_NEUTRAL_MODE + + MonoClassField *field; + gpointer iter; + + iter = NULL; + while ((field = mono_class_get_fields (klass, &iter))) { + g_assert (field->parent == klass); + if (mono_class_field_is_special_static (field)) + return TRUE; + } + + return FALSE; +} + +#ifndef DISABLE_REMOTING +/** + * create_remote_class_key: + * Creates an array of pointers that can be used as a hash key for a remote class. + * The first element of the array is the number of pointers. + */ +static gpointer* +create_remote_class_key (MonoRemoteClass *remote_class, MonoClass *extra_class) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + gpointer *key; + int i, j; + + if (remote_class == NULL) { + if (mono_class_is_interface (extra_class)) { + key = (void **)g_malloc (sizeof(gpointer) * 3); + key [0] = GINT_TO_POINTER (2); + key [1] = mono_defaults.marshalbyrefobject_class; + key [2] = extra_class; + } else { + key = (void **)g_malloc (sizeof(gpointer) * 2); + key [0] = GINT_TO_POINTER (1); + key [1] = extra_class; + } + } else { + if (extra_class != NULL && mono_class_is_interface (extra_class)) { + key = (void **)g_malloc (sizeof(gpointer) * (remote_class->interface_count + 3)); + key [0] = GINT_TO_POINTER (remote_class->interface_count + 2); + key [1] = remote_class->proxy_class; + + // Keep the list of interfaces sorted + for (i = 0, j = 2; i < remote_class->interface_count; i++, j++) { + if (extra_class && remote_class->interfaces [i] > extra_class) { + key [j++] = extra_class; + extra_class = NULL; + } + key [j] = remote_class->interfaces [i]; + } + if (extra_class) + key [j] = extra_class; + } else { + // Replace the old class. The interface list is the same + key = (void **)g_malloc (sizeof(gpointer) * (remote_class->interface_count + 2)); + key [0] = GINT_TO_POINTER (remote_class->interface_count + 1); + key [1] = extra_class != NULL ? extra_class : remote_class->proxy_class; + for (i = 0; i < remote_class->interface_count; i++) + key [2 + i] = remote_class->interfaces [i]; + } + } + + return key; +} + +/** + * copy_remote_class_key: + * + * Make a copy of KEY in the domain and return the copy. + */ +static gpointer* +copy_remote_class_key (MonoDomain *domain, gpointer *key) +{ + MONO_REQ_GC_NEUTRAL_MODE + + int key_size = (GPOINTER_TO_UINT (key [0]) + 1) * sizeof (gpointer); + gpointer *mp_key = (gpointer *)mono_domain_alloc (domain, key_size); + + memcpy (mp_key, key, key_size); + + return mp_key; +} + +/** + * mono_remote_class: + * \param domain the application domain + * \param class_name name of the remote class + * \param error set on error + * Creates and initializes a \c MonoRemoteClass object for a remote type. + * On failure returns NULL and sets \p error + */ +MonoRemoteClass* +mono_remote_class (MonoDomain *domain, MonoStringHandle class_name, MonoClass *proxy_class, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoRemoteClass *rc; + gpointer* key, *mp_key; + char *name; + + error_init (error); + + key = create_remote_class_key (NULL, proxy_class); + + mono_domain_lock (domain); + rc = (MonoRemoteClass *)g_hash_table_lookup (domain->proxy_vtable_hash, key); + + if (rc) { + g_free (key); + mono_domain_unlock (domain); + return rc; + } + + name = mono_string_to_utf8_mp (domain->mp, MONO_HANDLE_RAW (class_name), error); + if (!is_ok (error)) { + g_free (key); + mono_domain_unlock (domain); + return NULL; + } + + mp_key = copy_remote_class_key (domain, key); + g_free (key); + key = mp_key; + + if (mono_class_is_interface (proxy_class)) { + /* If we need to proxy an interface, we use this stylized + * representation (interface_count >= 1, proxy_class is + * MarshalByRefObject). The code in + * mono_remote_class_is_interface_proxy () depends on being + * able to detect that we're doing this, so if this + * representation changes, change GetType, too. */ + rc = (MonoRemoteClass *)mono_domain_alloc (domain, MONO_SIZEOF_REMOTE_CLASS + sizeof(MonoClass*)); + rc->interface_count = 1; + rc->interfaces [0] = proxy_class; + rc->proxy_class = mono_defaults.marshalbyrefobject_class; + } else { + rc = (MonoRemoteClass *)mono_domain_alloc (domain, MONO_SIZEOF_REMOTE_CLASS); + rc->interface_count = 0; + rc->proxy_class = proxy_class; + } + + rc->default_vtable = NULL; + rc->xdomain_vtable = NULL; + rc->proxy_class_name = name; +#ifndef DISABLE_PERFCOUNTERS + mono_atomic_fetch_add_i32 (&mono_perfcounters->loader_bytes, mono_string_length (MONO_HANDLE_RAW (class_name)) + 1); +#endif + + g_hash_table_insert (domain->proxy_vtable_hash, key, rc); + + mono_domain_unlock (domain); + return rc; +} + +/** + * clone_remote_class: + * Creates a copy of the remote_class, adding the provided class or interface + */ +static MonoRemoteClass* +clone_remote_class (MonoDomain *domain, MonoRemoteClass* remote_class, MonoClass *extra_class) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + MonoRemoteClass *rc; + gpointer* key, *mp_key; + + key = create_remote_class_key (remote_class, extra_class); + rc = (MonoRemoteClass *)g_hash_table_lookup (domain->proxy_vtable_hash, key); + if (rc != NULL) { + g_free (key); + return rc; + } + + mp_key = copy_remote_class_key (domain, key); + g_free (key); + key = mp_key; + + if (mono_class_is_interface (extra_class)) { + int i,j; + rc = (MonoRemoteClass *)mono_domain_alloc (domain, MONO_SIZEOF_REMOTE_CLASS + sizeof(MonoClass*) * (remote_class->interface_count + 1)); + rc->proxy_class = remote_class->proxy_class; + rc->interface_count = remote_class->interface_count + 1; + + // Keep the list of interfaces sorted, since the hash key of + // the remote class depends on this + for (i = 0, j = 0; i < remote_class->interface_count; i++, j++) { + if (remote_class->interfaces [i] > extra_class && i == j) + rc->interfaces [j++] = extra_class; + rc->interfaces [j] = remote_class->interfaces [i]; + } + if (i == j) + rc->interfaces [j] = extra_class; + } else { + // Replace the old class. The interface array is the same + rc = (MonoRemoteClass *)mono_domain_alloc (domain, MONO_SIZEOF_REMOTE_CLASS + sizeof(MonoClass*) * remote_class->interface_count); + rc->proxy_class = extra_class; + rc->interface_count = remote_class->interface_count; + if (rc->interface_count > 0) + memcpy (rc->interfaces, remote_class->interfaces, rc->interface_count * sizeof (MonoClass*)); + } + + rc->default_vtable = NULL; + rc->xdomain_vtable = NULL; + rc->proxy_class_name = remote_class->proxy_class_name; + + g_hash_table_insert (domain->proxy_vtable_hash, key, rc); + + return rc; +} + +gpointer +mono_remote_class_vtable (MonoDomain *domain, MonoRemoteClass *remote_class, MonoRealProxyHandle rp, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + + mono_loader_lock (); /*FIXME mono_class_from_mono_type and mono_class_proxy_vtable take it*/ + mono_domain_lock (domain); + gint32 target_domain_id = MONO_HANDLE_GETVAL (rp, target_domain_id); + if (target_domain_id != -1) { + if (remote_class->xdomain_vtable == NULL) + remote_class->xdomain_vtable = mono_class_proxy_vtable (domain, remote_class, MONO_REMOTING_TARGET_APPDOMAIN, error); + mono_domain_unlock (domain); + mono_loader_unlock (); + return_val_if_nok (error, NULL); + return remote_class->xdomain_vtable; + } + if (remote_class->default_vtable == NULL) { + MonoReflectionTypeHandle reftype = MONO_HANDLE_NEW (MonoReflectionType, NULL); + MONO_HANDLE_GET (reftype, rp, class_to_proxy); + + MonoType *type = MONO_HANDLE_GETVAL (reftype, type); + MonoClass *klass = mono_class_from_mono_type (type); +#ifndef DISABLE_COM + if ((mono_class_is_com_object (klass) || (mono_class_get_com_object_class () && klass == mono_class_get_com_object_class ())) && !mono_vtable_is_remote (mono_class_vtable (mono_domain_get (), klass))) + remote_class->default_vtable = mono_class_proxy_vtable (domain, remote_class, MONO_REMOTING_TARGET_COMINTEROP, error); + else +#endif + remote_class->default_vtable = mono_class_proxy_vtable (domain, remote_class, MONO_REMOTING_TARGET_UNKNOWN, error); + /* N.B. both branches of the if modify error */ + if (!is_ok (error)) { + mono_domain_unlock (domain); + mono_loader_unlock (); + return NULL; + } + } + + mono_domain_unlock (domain); + mono_loader_unlock (); + return remote_class->default_vtable; +} + +/** + * mono_upgrade_remote_class: + * \param domain the application domain + * \param tproxy the proxy whose remote class has to be upgraded. + * \param klass class to which the remote class can be casted. + * \param error set on error + * Updates the vtable of the remote class by adding the necessary method slots + * and interface offsets so it can be safely casted to klass. klass can be a + * class or an interface. On success returns TRUE, on failure returns FALSE and sets \p error. + */ +gboolean +mono_upgrade_remote_class (MonoDomain *domain, MonoObjectHandle proxy_object, MonoClass *klass, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + + MonoTransparentProxyHandle tproxy = MONO_HANDLE_CAST (MonoTransparentProxy, proxy_object); + MonoRemoteClass *remote_class = MONO_HANDLE_GETVAL (tproxy, remote_class); + + gboolean redo_vtable; + if (mono_class_is_interface (klass)) { + int i; + redo_vtable = TRUE; + for (i = 0; i < remote_class->interface_count && redo_vtable; i++) + if (remote_class->interfaces [i] == klass) + redo_vtable = FALSE; + } + else { + redo_vtable = (remote_class->proxy_class != klass); + } + + mono_loader_lock (); /*FIXME mono_remote_class_vtable requires it.*/ + mono_domain_lock (domain); + if (redo_vtable) { + MonoRemoteClass *fresh_remote_class = clone_remote_class (domain, remote_class, klass); + MONO_HANDLE_SETVAL (tproxy, remote_class, MonoRemoteClass*, fresh_remote_class); + MonoRealProxyHandle real_proxy = MONO_HANDLE_NEW (MonoRealProxy, NULL); + MONO_HANDLE_GET (real_proxy, tproxy, rp); + MONO_HANDLE_SETVAL (proxy_object, vtable, MonoVTable*, mono_remote_class_vtable (domain, fresh_remote_class, real_proxy, error)); + goto_if_nok (error, leave); + } + +leave: + mono_domain_unlock (domain); + mono_loader_unlock (); + return is_ok (error); +} +#endif /* DISABLE_REMOTING */ + + +/** + * mono_object_get_virtual_method: + * \param obj object to operate on. + * \param method method + * Retrieves the \c MonoMethod that would be called on \p obj if \p obj is passed as + * the instance of a callvirt of \p method. + */ +MonoMethod* +mono_object_get_virtual_method (MonoObject *obj_raw, MonoMethod *method) +{ + MONO_REQ_GC_UNSAFE_MODE; + HANDLE_FUNCTION_ENTER (); + MonoError error; + MONO_HANDLE_DCL (MonoObject, obj); + MonoMethod *result = mono_object_handle_get_virtual_method (obj, method, &error); + mono_error_assert_ok (&error); + HANDLE_FUNCTION_RETURN_VAL (result); +} + +/** + * mono_object_handle_get_virtual_method: + * \param obj object to operate on. + * \param method method + * Retrieves the \c MonoMethod that would be called on \p obj if \p obj is passed as + * the instance of a callvirt of \p method. + */ +MonoMethod* +mono_object_handle_get_virtual_method (MonoObjectHandle obj, MonoMethod *method, MonoError *error) +{ + error_init (error); + + gboolean is_proxy = FALSE; + MonoClass *klass = mono_handle_class (obj); + if (mono_class_is_transparent_proxy (klass)) { + MonoRemoteClass *remote_class = MONO_HANDLE_GETVAL (MONO_HANDLE_CAST (MonoTransparentProxy, obj), remote_class); + klass = remote_class->proxy_class; + is_proxy = TRUE; + } + return class_get_virtual_method (klass, method, is_proxy, error); +} + +static MonoMethod* +class_get_virtual_method (MonoClass *klass, MonoMethod *method, gboolean is_proxy, MonoError *error) +{ + error_init (error); + + + if (!is_proxy && ((method->flags & METHOD_ATTRIBUTE_FINAL) || !(method->flags & METHOD_ATTRIBUTE_VIRTUAL))) + return method; + + mono_class_setup_vtable (klass); + MonoMethod **vtable = klass->vtable; + + if (method->slot == -1) { + /* method->slot might not be set for instances of generic methods */ + if (method->is_inflated) { + g_assert (((MonoMethodInflated*)method)->declaring->slot != -1); + method->slot = ((MonoMethodInflated*)method)->declaring->slot; + } else { + if (!is_proxy) + g_assert_not_reached (); + } + } + + MonoMethod *res = NULL; + /* check method->slot is a valid index: perform isinstance? */ + if (method->slot != -1) { + if (mono_class_is_interface (method->klass)) { + if (!is_proxy) { + gboolean variance_used = FALSE; + int iface_offset = mono_class_interface_offset_with_variance (klass, method->klass, &variance_used); + g_assert (iface_offset > 0); + res = vtable [iface_offset + method->slot]; + } + } else { + res = vtable [method->slot]; + } + } + +#ifndef DISABLE_REMOTING + if (is_proxy) { + /* It may be an interface, abstract class method or generic method */ + if (!res || mono_method_signature (res)->generic_param_count) + res = method; + + /* generic methods demand invoke_with_check */ + if (mono_method_signature (res)->generic_param_count) + res = mono_marshal_get_remoting_invoke_with_check (res); + else { +#ifndef DISABLE_COM + if (klass == mono_class_get_com_object_class () || mono_class_is_com_object (klass)) + res = mono_cominterop_get_invoke (res); + else +#endif + res = mono_marshal_get_remoting_invoke (res); + } + } else +#endif + { + if (method->is_inflated) { + /* Have to inflate the result */ + res = mono_class_inflate_generic_method_checked (res, &((MonoMethodInflated*)method)->context, error); + } + } + + return res; +} + +static MonoObject* +do_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObject **exc, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoObject *result = NULL; + + g_assert (callbacks.runtime_invoke); + + error_init (error); + + MONO_PROFILER_RAISE (method_begin_invoke, (method)); + + result = callbacks.runtime_invoke (method, obj, params, exc, error); + + MONO_PROFILER_RAISE (method_end_invoke, (method)); + + if (!mono_error_ok (error)) + return NULL; + + return result; +} + +/** + * mono_runtime_invoke: + * \param method method to invoke + * \param obj object instance + * \param params arguments to the method + * \param exc exception information. + * Invokes the method represented by \p method on the object \p obj. + * \p obj is the \c this pointer, it should be NULL for static + * methods, a \c MonoObject* for object instances and a pointer to + * the value type for value types. + * + * The params array contains the arguments to the method with the + * same convention: \c MonoObject* pointers for object instances and + * pointers to the value type otherwise. + * + * From unmanaged code you'll usually use the + * \c mono_runtime_invoke variant. + * + * Note that this function doesn't handle virtual methods for + * you, it will exec the exact method you pass: we still need to + * expose a function to lookup the derived class implementation + * of a virtual method (there are examples of this in the code, + * though). + * + * You can pass NULL as the \p exc argument if you don't want to + * catch exceptions, otherwise, \c *exc will be set to the exception + * thrown, if any. if an exception is thrown, you can't use the + * \c MonoObject* result from the function. + * + * If the method returns a value type, it is boxed in an object + * reference. + */ +MonoObject* +mono_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObject **exc) +{ + MonoError error; + MonoObject *res; + if (exc) { + res = mono_runtime_try_invoke (method, obj, params, exc, &error); + if (*exc == NULL && !mono_error_ok(&error)) { + *exc = (MonoObject*) mono_error_convert_to_exception (&error); + } else + mono_error_cleanup (&error); + } else { + res = mono_runtime_invoke_checked (method, obj, params, &error); + mono_error_raise_exception_deprecated (&error); /* OK to throw, external only without a good alternative */ + } + return res; +} + +/** + * mono_runtime_try_invoke: + * \param method method to invoke + * \param obj object instance + * \param params arguments to the method + * \param exc exception information. + * \param error set on error + * Invokes the method represented by \p method on the object \p obj. + * + * \p obj is the \c this pointer, it should be NULL for static + * methods, a \c MonoObject* for object instances and a pointer to + * the value type for value types. + * + * The params array contains the arguments to the method with the + * same convention: \c MonoObject* pointers for object instances and + * pointers to the value type otherwise. + * + * From unmanaged code you'll usually use the + * mono_runtime_invoke() variant. + * + * Note that this function doesn't handle virtual methods for + * you, it will exec the exact method you pass: we still need to + * expose a function to lookup the derived class implementation + * of a virtual method (there are examples of this in the code, + * though). + * + * For this function, you must not pass NULL as the \p exc argument if + * you don't want to catch exceptions, use + * mono_runtime_invoke_checked(). If an exception is thrown, you + * can't use the \c MonoObject* result from the function. + * + * If this method cannot be invoked, \p error will be set and \p exc and + * the return value must not be used. + * + * If the method returns a value type, it is boxed in an object + * reference. + */ +MonoObject* +mono_runtime_try_invoke (MonoMethod *method, void *obj, void **params, MonoObject **exc, MonoError* error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + g_assert (exc != NULL); + + if (mono_runtime_get_no_exec ()) + g_warning ("Invoking method '%s' when running in no-exec mode.\n", mono_method_full_name (method, TRUE)); + + return do_runtime_invoke (method, obj, params, exc, error); +} + +/** + * mono_runtime_invoke_checked: + * \param method method to invoke + * \param obj object instance + * \param params arguments to the method + * \param error set on error + * Invokes the method represented by \p method on the object \p obj. + * + * \p obj is the \c this pointer, it should be NULL for static + * methods, a \c MonoObject* for object instances and a pointer to + * the value type for value types. + * + * The \p params array contains the arguments to the method with the + * same convention: \c MonoObject* pointers for object instances and + * pointers to the value type otherwise. + * + * From unmanaged code you'll usually use the + * mono_runtime_invoke() variant. + * + * Note that this function doesn't handle virtual methods for + * you, it will exec the exact method you pass: we still need to + * expose a function to lookup the derived class implementation + * of a virtual method (there are examples of this in the code, + * though). + * + * If an exception is thrown, you can't use the \c MonoObject* result + * from the function. + * + * If this method cannot be invoked, \p error will be set. If the + * method throws an exception (and we're in coop mode) the exception + * will be set in \p error. + * + * If the method returns a value type, it is boxed in an object + * reference. + */ +MonoObject* +mono_runtime_invoke_checked (MonoMethod *method, void *obj, void **params, MonoError* error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + if (mono_runtime_get_no_exec ()) + g_warning ("Invoking method '%s' when running in no-exec mode.\n", mono_method_full_name (method, TRUE)); + + return do_runtime_invoke (method, obj, params, NULL, error); +} + +/** + * mono_method_get_unmanaged_thunk: + * \param method method to generate a thunk for. + * + * Returns an \c unmanaged->managed thunk that can be used to call + * a managed method directly from C. + * + * The thunk's C signature closely matches the managed signature: + * + * C#: public bool Equals (object obj); + * + * C: typedef MonoBoolean (*Equals)(MonoObject*, MonoObject*, MonoException**); + * + * The 1st (this) parameter must not be used with static methods: + * + * C#: public static bool ReferenceEquals (object a, object b); + * + * C: typedef MonoBoolean (*ReferenceEquals)(MonoObject*, MonoObject*, MonoException**); + * + * The last argument must be a non-null \c MonoException* pointer. + * It has "out" semantics. After invoking the thunk, \c *ex will be NULL if no + * exception has been thrown in managed code. Otherwise it will point + * to the \c MonoException* caught by the thunk. In this case, the result of + * the thunk is undefined: + * + *
+ * MonoMethod *method = ... // MonoMethod* of System.Object.Equals
+ *
+ * MonoException *ex = NULL;
+ *
+ * Equals func = mono_method_get_unmanaged_thunk (method);
+ *
+ * MonoBoolean res = func (thisObj, objToCompare, &ex);
+ *
+ * if (ex) {
+ *
+ *    // handle exception
+ *
+ * }
+ * 
+ * + * The calling convention of the thunk matches the platform's default + * convention. This means that under Windows, C declarations must + * contain the \c __stdcall attribute: + * + * C: typedef MonoBoolean (__stdcall *Equals)(MonoObject*, MonoObject*, MonoException**); + * + * LIMITATIONS + * + * Value type arguments and return values are treated as they were objects: + * + * C#: public static Rectangle Intersect (Rectangle a, Rectangle b); + * C: typedef MonoObject* (*Intersect)(MonoObject*, MonoObject*, MonoException**); + * + * Arguments must be properly boxed upon trunk's invocation, while return + * values must be unboxed. + */ +gpointer +mono_method_get_unmanaged_thunk (MonoMethod *method) +{ + MONO_REQ_GC_NEUTRAL_MODE; + MONO_REQ_API_ENTRYPOINT; + + MonoError error; + gpointer res; + + g_assert (!mono_threads_is_coop_enabled ()); + + MONO_ENTER_GC_UNSAFE; + method = mono_marshal_get_thunk_invoke_wrapper (method); + res = mono_compile_method_checked (method, &error); + mono_error_cleanup (&error); + MONO_EXIT_GC_UNSAFE; + + return res; +} + +void +mono_copy_value (MonoType *type, void *dest, void *value, int deref_pointer) +{ + MONO_REQ_GC_UNSAFE_MODE; + + int t; + if (type->byref) { + /* object fields cannot be byref, so we don't need a + wbarrier here */ + gpointer *p = (gpointer*)dest; + *p = value; + return; + } + t = type->type; +handle_enum: + switch (t) { + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_I1: + case MONO_TYPE_U1: { + guint8 *p = (guint8*)dest; + *p = value ? *(guint8*)value : 0; + return; + } + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_CHAR: { + guint16 *p = (guint16*)dest; + *p = value ? *(guint16*)value : 0; + return; + } +#if SIZEOF_VOID_P == 4 + case MONO_TYPE_I: + case MONO_TYPE_U: +#endif + case MONO_TYPE_I4: + case MONO_TYPE_U4: { + gint32 *p = (gint32*)dest; + *p = value ? *(gint32*)value : 0; + return; + } +#if SIZEOF_VOID_P == 8 + case MONO_TYPE_I: + case MONO_TYPE_U: +#endif + case MONO_TYPE_I8: + case MONO_TYPE_U8: { + gint64 *p = (gint64*)dest; + *p = value ? *(gint64*)value : 0; + return; + } + case MONO_TYPE_R4: { + float *p = (float*)dest; + *p = value ? *(float*)value : 0; + return; + } + case MONO_TYPE_R8: { + double *p = (double*)dest; + *p = value ? *(double*)value : 0; + return; + } + case MONO_TYPE_STRING: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_CLASS: + case MONO_TYPE_OBJECT: + case MONO_TYPE_ARRAY: + mono_gc_wbarrier_generic_store (dest, deref_pointer ? *(MonoObject **)value : (MonoObject *)value); + return; + case MONO_TYPE_FNPTR: + case MONO_TYPE_PTR: { + gpointer *p = (gpointer*)dest; + *p = deref_pointer? *(gpointer*)value: value; + return; + } + case MONO_TYPE_VALUETYPE: + /* note that 't' and 'type->type' can be different */ + if (type->type == MONO_TYPE_VALUETYPE && type->data.klass->enumtype) { + t = mono_class_enum_basetype (type->data.klass)->type; + goto handle_enum; + } else { + MonoClass *klass = mono_class_from_mono_type (type); + int size = mono_class_value_size (klass, NULL); + if (value == NULL) + mono_gc_bzero_atomic (dest, size); + else + mono_gc_wbarrier_value_copy (dest, value, 1, klass); + } + return; + case MONO_TYPE_GENERICINST: + t = type->data.generic_class->container_class->byval_arg.type; + goto handle_enum; + default: + g_error ("got type %x", type->type); + } +} + +/** + * mono_field_set_value: + * \param obj Instance object + * \param field \c MonoClassField describing the field to set + * \param value The value to be set + * + * Sets the value of the field described by \p field in the object instance \p obj + * to the value passed in \p value. This method should only be used for instance + * fields. For static fields, use \c mono_field_static_set_value. + * + * The value must be in the native format of the field type. + */ +void +mono_field_set_value (MonoObject *obj, MonoClassField *field, void *value) +{ + MONO_REQ_GC_UNSAFE_MODE; + + void *dest; + + g_return_if_fail (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC)); + + dest = (char*)obj + field->offset; + mono_copy_value (field->type, dest, value, FALSE); +} + +/** + * mono_field_static_set_value: + * \param field \c MonoClassField describing the field to set + * \param value The value to be set + * Sets the value of the static field described by \p field + * to the value passed in \p value. + * The value must be in the native format of the field type. + */ +void +mono_field_static_set_value (MonoVTable *vt, MonoClassField *field, void *value) +{ + MONO_REQ_GC_UNSAFE_MODE; + + void *dest; + + g_return_if_fail (field->type->attrs & FIELD_ATTRIBUTE_STATIC); + /* you cant set a constant! */ + g_return_if_fail (!(field->type->attrs & FIELD_ATTRIBUTE_LITERAL)); + + 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); + dest = mono_get_special_static_data (GPOINTER_TO_UINT (addr)); + } else { + 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); +} + +/** + * mono_vtable_get_static_field_data: + * + * Internal use function: return a pointer to the memory holding the static fields + * for a class or NULL if there are no static fields. + * This is exported only for use by the debugger. + */ +void * +mono_vtable_get_static_field_data (MonoVTable *vt) +{ + MONO_REQ_GC_NEUTRAL_MODE + + if (!vt->has_static_fields) + return NULL; + return vt->vtable [vt->klass->vtable_size]; +} + +static guint8* +mono_field_get_addr (MonoObject *obj, MonoVTable *vt, MonoClassField *field) +{ + 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; +} + +/** + * mono_field_get_value: + * \param obj Object instance + * \param field \c MonoClassField describing the field to fetch information from + * \param value pointer to the location where the value will be stored + * Use this routine to get the value of the field \p field in the object + * passed. + * + * The pointer provided by value must be of the field type, for reference + * types this is a \c MonoObject*, for value types its the actual pointer to + * the value type. + * + * For example: + * + *
+ * int i;
+ *
+ * mono_field_get_value (obj, int_field, &i);
+ * 
+ */ +void +mono_field_get_value (MonoObject *obj, MonoClassField *field, void *value) +{ + MONO_REQ_GC_UNSAFE_MODE; + + void *src; + + g_assert (obj); + + g_return_if_fail (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC)); + + src = (char*)obj + field->offset; + mono_copy_value (field->type, value, src, TRUE); +} + +/** + * mono_field_get_value_object: + * \param domain domain where the object will be created (if boxing) + * \param field \c MonoClassField describing the field to fetch information from + * \param obj The object instance for the field. + * \returns a new \c MonoObject with the value from the given field. If the + * field represents a value type, the value is boxed. + */ +MonoObject * +mono_field_get_value_object (MonoDomain *domain, MonoClassField *field, MonoObject *obj) +{ + MonoError error; + MonoObject* result = mono_field_get_value_object_checked (domain, field, obj, &error); + mono_error_assert_ok (&error); + return result; +} + +/** + * mono_field_get_value_object_checked: + * \param domain domain where the object will be created (if boxing) + * \param field \c MonoClassField describing the field to fetch information from + * \param obj The object instance for the field. + * \param error Set on error. + * \returns a new \c MonoObject with the value from the given field. If the + * field represents a value type, the value is boxed. On error returns NULL and sets \p error. + */ +MonoObject * +mono_field_get_value_object_checked (MonoDomain *domain, MonoClassField *field, MonoObject *obj, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + + MonoObject *o; + MonoClass *klass; + MonoVTable *vtable = NULL; + gchar *v; + gboolean is_static = FALSE; + gboolean is_ref = FALSE; + gboolean is_literal = FALSE; + gboolean is_ptr = FALSE; + MonoType *type = mono_field_get_type_checked (field, error); + + return_val_if_nok (error, NULL); + + switch (type->type) { + case MONO_TYPE_STRING: + case MONO_TYPE_OBJECT: + case MONO_TYPE_CLASS: + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: + is_ref = TRUE; + break; + case MONO_TYPE_U1: + case MONO_TYPE_I1: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_U2: + case MONO_TYPE_I2: + case MONO_TYPE_CHAR: + case MONO_TYPE_U: + case MONO_TYPE_I: + case MONO_TYPE_U4: + case MONO_TYPE_I4: + case MONO_TYPE_R4: + case MONO_TYPE_U8: + case MONO_TYPE_I8: + case MONO_TYPE_R8: + case MONO_TYPE_VALUETYPE: + is_ref = type->byref; + break; + case MONO_TYPE_GENERICINST: + is_ref = !mono_type_generic_inst_is_valuetype (type); + break; + case MONO_TYPE_PTR: + is_ptr = TRUE; + break; + default: + g_error ("type 0x%x not handled in " + "mono_field_get_value_object", type->type); + return NULL; + } + + if (type->attrs & FIELD_ATTRIBUTE_LITERAL) + is_literal = TRUE; + + if (type->attrs & FIELD_ATTRIBUTE_STATIC) { + is_static = TRUE; + + if (!is_literal) { + vtable = mono_class_vtable_full (domain, field->parent, error); + return_val_if_nok (error, NULL); + + if (!vtable->initialized) { + mono_runtime_class_init_full (vtable, error); + return_val_if_nok (error, NULL); + } + } + } else { + g_assert (obj); + } + + if (is_ref) { + if (is_literal) { + get_default_field_value (domain, field, &o, error); + return_val_if_nok (error, NULL); + } else if (is_static) { + mono_field_static_get_value_checked (vtable, field, &o, error); + return_val_if_nok (error, NULL); + } else { + mono_field_get_value (obj, field, &o); + } + return o; + } + + if (is_ptr) { + static MonoMethod *m; + gpointer args [2]; + gpointer *ptr; + gpointer v; + + if (!m) { + MonoClass *ptr_klass = mono_class_get_pointer_class (); + m = mono_class_get_method_from_name_flags (ptr_klass, "Box", 2, METHOD_ATTRIBUTE_STATIC); + g_assert (m); + } + + v = &ptr; + if (is_literal) { + get_default_field_value (domain, field, v, error); + return_val_if_nok (error, NULL); + } else if (is_static) { + mono_field_static_get_value_checked (vtable, field, v, error); + return_val_if_nok (error, NULL); + } else { + mono_field_get_value (obj, field, v); + } + + /* MONO_TYPE_PTR is passed by value to runtime_invoke () */ + args [0] = ptr ? *ptr : NULL; + args [1] = mono_type_get_object_checked (mono_domain_get (), type, error); + return_val_if_nok (error, NULL); + + o = mono_runtime_invoke_checked (m, NULL, args, error); + return_val_if_nok (error, NULL); + + return o; + } + + /* boxed value type */ + klass = mono_class_from_mono_type (type); + + if (mono_class_is_nullable (klass)) + return mono_nullable_box (mono_field_get_addr (obj, vtable, field), klass, error); + + o = mono_object_new_checked (domain, klass, error); + return_val_if_nok (error, NULL); + v = ((gchar *) o) + sizeof (MonoObject); + + if (is_literal) { + get_default_field_value (domain, field, v, error); + return_val_if_nok (error, NULL); + } else if (is_static) { + mono_field_static_get_value_checked (vtable, field, v, error); + return_val_if_nok (error, NULL); + } else { + mono_field_get_value (obj, field, v); + } + + return o; +} + +int +mono_get_constant_value_from_blob (MonoDomain* domain, MonoTypeEnum type, const char *blob, void *value, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + int retval = 0; + const char *p = blob; + mono_metadata_decode_blob_size (p, &p); + + switch (type) { + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_U1: + case MONO_TYPE_I1: + *(guint8 *) value = *p; + break; + case MONO_TYPE_CHAR: + case MONO_TYPE_U2: + case MONO_TYPE_I2: + *(guint16*) value = read16 (p); + break; + case MONO_TYPE_U4: + case MONO_TYPE_I4: + *(guint32*) value = read32 (p); + break; + case MONO_TYPE_U8: + case MONO_TYPE_I8: + *(guint64*) value = read64 (p); + break; + case MONO_TYPE_R4: + readr4 (p, (float*) value); + break; + case MONO_TYPE_R8: + readr8 (p, (double*) value); + break; + case MONO_TYPE_STRING: + *(gpointer*) value = mono_ldstr_metadata_sig (domain, blob, error); + break; + case MONO_TYPE_CLASS: + *(gpointer*) value = NULL; + break; + default: + retval = -1; + g_warning ("type 0x%02x should not be in constant table", type); + } + return retval; +} + +static void +get_default_field_value (MonoDomain* domain, MonoClassField *field, void *value, MonoError *error) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + MonoTypeEnum def_type; + const char* data; + + error_init (error); + + data = mono_class_get_field_default_value (field, &def_type); + mono_get_constant_value_from_blob (domain, def_type, data, value, error); +} + +void +mono_field_static_get_value_for_thread (MonoInternalThread *thread, MonoVTable *vt, MonoClassField *field, void *value, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + void *src; + + error_init (error); + + g_return_if_fail (field->type->attrs & FIELD_ATTRIBUTE_STATIC); + + if (field->type->attrs & FIELD_ATTRIBUTE_LITERAL) { + get_default_field_value (vt->domain, field, value, error); + return; + } + + if (field->offset == -1) { + /* Special static */ + gpointer addr = g_hash_table_lookup (vt->domain->special_static_fields, field); + src = mono_get_special_static_data_for_thread (thread, GPOINTER_TO_UINT (addr)); + } else { + src = (char*)mono_vtable_get_static_field_data (vt) + field->offset; + } + mono_copy_value (field->type, value, src, TRUE); +} + +/** + * mono_field_static_get_value: + * \param vt vtable to the object + * \param field \c MonoClassField describing the field to fetch information from + * \param value where the value is returned + * Use this routine to get the value of the static field \p field value. + * + * The pointer provided by value must be of the field type, for reference + * types this is a \c MonoObject*, for value types its the actual pointer to + * the value type. + * + * For example: + * + *
+ *     int i;
+ *
+ *     mono_field_static_get_value (vt, int_field, &i);
+ * 
+ */ +void +mono_field_static_get_value (MonoVTable *vt, MonoClassField *field, void *value) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + MonoError error; + mono_field_static_get_value_checked (vt, field, value, &error); + mono_error_cleanup (&error); +} + +/** + * mono_field_static_get_value_checked: + * \param vt vtable to the object + * \param field \c MonoClassField describing the field to fetch information from + * \param value where the value is returned + * \param error set on error + * Use this routine to get the value of the static field \p field value. + * + * The pointer provided by value must be of the field type, for reference + * types this is a \c MonoObject*, for value types its the actual pointer to + * the value type. + * + * For example: + * int i; + * mono_field_static_get_value_checked (vt, int_field, &i, error); + * if (!is_ok (error)) { ... } + * + * On failure sets \p error. + */ +void +mono_field_static_get_value_checked (MonoVTable *vt, MonoClassField *field, void *value, MonoError *error) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + mono_field_static_get_value_for_thread (mono_thread_internal_current (), vt, field, value, error); +} + +/** + * mono_property_set_value: + * \param prop MonoProperty to set + * \param obj instance object on which to act + * \param params parameters to pass to the propery + * \param exc optional exception + * Invokes the property's set method with the given arguments on the + * object instance obj (or NULL for static properties). + * + * You can pass NULL as the exc argument if you don't want to + * catch exceptions, otherwise, \c *exc will be set to the exception + * thrown, if any. if an exception is thrown, you can't use the + * \c MonoObject* result from the function. + */ +void +mono_property_set_value (MonoProperty *prop, void *obj, void **params, MonoObject **exc) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoError error; + do_runtime_invoke (prop->set, obj, params, exc, &error); + if (exc && *exc == NULL && !mono_error_ok (&error)) { + *exc = (MonoObject*) mono_error_convert_to_exception (&error); + } else { + mono_error_cleanup (&error); + } +} + +/** + * mono_property_set_value_checked: + * \param prop \c MonoProperty to set + * \param obj instance object on which to act + * \param params parameters to pass to the propery + * \param error set on error + * Invokes the property's set method with the given arguments on the + * object instance \p obj (or NULL for static properties). + * \returns TRUE on success. On failure returns FALSE and sets \p error. + * If an exception is thrown, it will be caught and returned via \p error. + */ +gboolean +mono_property_set_value_checked (MonoProperty *prop, void *obj, void **params, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoObject *exc; + + error_init (error); + do_runtime_invoke (prop->set, obj, params, &exc, error); + if (exc != NULL && is_ok (error)) + mono_error_set_exception_instance (error, (MonoException*)exc); + return is_ok (error); +} + +/** + * mono_property_get_value: + * \param prop \c MonoProperty to fetch + * \param obj instance object on which to act + * \param params parameters to pass to the propery + * \param exc optional exception + * Invokes the property's \c get method with the given arguments on the + * object instance \p obj (or NULL for static properties). + * + * You can pass NULL as the \p exc argument if you don't want to + * catch exceptions, otherwise, \c *exc will be set to the exception + * thrown, if any. if an exception is thrown, you can't use the + * \c MonoObject* result from the function. + * + * \returns the value from invoking the \c get method on the property. + */ +MonoObject* +mono_property_get_value (MonoProperty *prop, void *obj, void **params, MonoObject **exc) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoError error; + MonoObject *val = do_runtime_invoke (prop->get, obj, params, exc, &error); + if (exc && *exc == NULL && !mono_error_ok (&error)) { + *exc = (MonoObject*) mono_error_convert_to_exception (&error); + } else { + mono_error_cleanup (&error); /* FIXME don't raise here */ + } + + return val; +} + +/** + * mono_property_get_value_checked: + * \param prop \c MonoProperty to fetch + * \param obj instance object on which to act + * \param params parameters to pass to the propery + * \param error set on error + * Invokes the property's \c get method with the given arguments on the + * object instance obj (or NULL for static properties). + * + * If an exception is thrown, you can't use the + * \c MonoObject* result from the function. The exception will be propagated via \p error. + * + * \returns the value from invoking the get method on the property. On + * failure returns NULL and sets \p error. + */ +MonoObject* +mono_property_get_value_checked (MonoProperty *prop, void *obj, void **params, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoObject *exc; + MonoObject *val = do_runtime_invoke (prop->get, obj, params, &exc, error); + if (exc != NULL && !is_ok (error)) + mono_error_set_exception_instance (error, (MonoException*) exc); + if (!is_ok (error)) + val = NULL; + return val; +} + + +/* + * mono_nullable_init: + * @buf: The nullable structure to initialize. + * @value: the value to initialize from + * @klass: the type for the object + * + * Initialize the nullable structure pointed to by @buf from @value which + * should be a boxed value type. The size of @buf should be able to hold + * as much data as the @klass->instance_size (which is the number of bytes + * that will be copies). + * + * Since Nullables have variable structure, we can not define a C + * structure for them. + */ +void +mono_nullable_init (guint8 *buf, MonoObject *value, MonoClass *klass) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoClass *param_class = klass->cast_class; + + mono_class_setup_fields (klass); + g_assert (klass->fields_inited); + + g_assert (mono_class_from_mono_type (klass->fields [0].type) == param_class); + g_assert (mono_class_from_mono_type (klass->fields [1].type) == mono_defaults.boolean_class); + + *(guint8*)(buf + klass->fields [1].offset - sizeof (MonoObject)) = value ? 1 : 0; + if (value) { + if (param_class->has_references) + mono_gc_wbarrier_value_copy (buf + klass->fields [0].offset - sizeof (MonoObject), mono_object_unbox (value), 1, param_class); + else + mono_gc_memmove_atomic (buf + klass->fields [0].offset - sizeof (MonoObject), mono_object_unbox (value), mono_class_value_size (param_class, NULL)); + } else { + mono_gc_bzero_atomic (buf + klass->fields [0].offset - sizeof (MonoObject), mono_class_value_size (param_class, NULL)); + } +} + +/* + * mono_nullable_init_from_handle: + * @buf: The nullable structure to initialize. + * @value: the value to initialize from + * @klass: the type for the object + * + * Initialize the nullable structure pointed to by @buf from @value which + * should be a boxed value type. The size of @buf should be able to hold + * as much data as the @klass->instance_size (which is the number of bytes + * that will be copies). + * + * Since Nullables have variable structure, we can not define a C + * structure for them. + */ +void +mono_nullable_init_from_handle (guint8 *buf, MonoObjectHandle value, MonoClass *klass) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoClass *param_class = klass->cast_class; + + mono_class_setup_fields (klass); + g_assert (klass->fields_inited); + + g_assert (mono_class_from_mono_type (klass->fields [0].type) == param_class); + g_assert (mono_class_from_mono_type (klass->fields [1].type) == mono_defaults.boolean_class); + + *(guint8*)(buf + klass->fields [1].offset - sizeof (MonoObject)) = MONO_HANDLE_IS_NULL (value) ? 0 : 1; + if (!MONO_HANDLE_IS_NULL (value)) { + uint32_t value_gchandle = 0; + gpointer src = mono_object_handle_pin_unbox (value, &value_gchandle); + if (param_class->has_references) + mono_gc_wbarrier_value_copy (buf + klass->fields [0].offset - sizeof (MonoObject), src, 1, param_class); + else + mono_gc_memmove_atomic (buf + klass->fields [0].offset - sizeof (MonoObject), src, mono_class_value_size (param_class, NULL)); + mono_gchandle_free (value_gchandle); + } else { + mono_gc_bzero_atomic (buf + klass->fields [0].offset - sizeof (MonoObject), mono_class_value_size (param_class, NULL)); + } +} + + + +/** + * mono_nullable_box: + * \param buf The buffer representing the data to be boxed + * \param klass the type to box it as. + * \param error set on error + * + * Creates a boxed vtype or NULL from the \c Nullable structure pointed to by + * \p buf. On failure returns NULL and sets \p error. + */ +MonoObject* +mono_nullable_box (guint8 *buf, MonoClass *klass, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + MonoClass *param_class = klass->cast_class; + + mono_class_setup_fields (klass); + g_assert (klass->fields_inited); + + g_assert (mono_class_from_mono_type (klass->fields [0].type) == param_class); + g_assert (mono_class_from_mono_type (klass->fields [1].type) == mono_defaults.boolean_class); + + if (*(guint8*)(buf + klass->fields [1].offset - sizeof (MonoObject))) { + MonoObject *o = mono_object_new_checked (mono_domain_get (), param_class, error); + return_val_if_nok (error, NULL); + if (param_class->has_references) + mono_gc_wbarrier_value_copy (mono_object_unbox (o), buf + klass->fields [0].offset - sizeof (MonoObject), 1, param_class); + else + mono_gc_memmove_atomic (mono_object_unbox (o), buf + klass->fields [0].offset - sizeof (MonoObject), mono_class_value_size (param_class, NULL)); + return o; + } + else + return NULL; +} + +/** + * mono_get_delegate_invoke: + * \param klass The delegate class + * \returns the \c MonoMethod for the \c Invoke method in the delegate class or NULL if \p klass is a broken delegate type + */ +MonoMethod * +mono_get_delegate_invoke (MonoClass *klass) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + MonoMethod *im; + + /* This is called at runtime, so avoid the slower search in metadata */ + mono_class_setup_methods (klass); + if (mono_class_has_failure (klass)) + return NULL; + im = mono_class_get_method_from_name (klass, "Invoke", -1); + return im; +} + +/** + * mono_get_delegate_begin_invoke: + * \param klass The delegate class + * \returns the \c MonoMethod for the \c BeginInvoke method in the delegate class or NULL if \p klass is a broken delegate type + */ +MonoMethod * +mono_get_delegate_begin_invoke (MonoClass *klass) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + MonoMethod *im; + + /* This is called at runtime, so avoid the slower search in metadata */ + mono_class_setup_methods (klass); + if (mono_class_has_failure (klass)) + return NULL; + im = mono_class_get_method_from_name (klass, "BeginInvoke", -1); + return im; +} + +/** + * mono_get_delegate_end_invoke: + * \param klass The delegate class + * \returns the \c MonoMethod for the \c EndInvoke method in the delegate class or NULL if \p klass is a broken delegate type + */ +MonoMethod * +mono_get_delegate_end_invoke (MonoClass *klass) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + MonoMethod *im; + + /* This is called at runtime, so avoid the slower search in metadata */ + mono_class_setup_methods (klass); + if (mono_class_has_failure (klass)) + return NULL; + im = mono_class_get_method_from_name (klass, "EndInvoke", -1); + return im; +} + +/** + * mono_runtime_delegate_invoke: + * \param delegate pointer to a delegate object. + * \param params parameters for the delegate. + * \param exc Pointer to the exception result. + * + * Invokes the delegate method \p delegate with the parameters provided. + * + * You can pass NULL as the \p exc argument if you don't want to + * catch exceptions, otherwise, \c *exc will be set to the exception + * thrown, if any. if an exception is thrown, you can't use the + * \c MonoObject* result from the function. + */ +MonoObject* +mono_runtime_delegate_invoke (MonoObject *delegate, void **params, MonoObject **exc) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoError error; + if (exc) { + MonoObject *result = mono_runtime_delegate_try_invoke (delegate, params, exc, &error); + if (*exc) { + mono_error_cleanup (&error); + return NULL; + } else { + if (!is_ok (&error)) + *exc = (MonoObject*)mono_error_convert_to_exception (&error); + return result; + } + } else { + MonoObject *result = mono_runtime_delegate_invoke_checked (delegate, params, &error); + mono_error_raise_exception_deprecated (&error); /* OK to throw, external only without a good alternative */ + return result; + } +} + +/** + * mono_runtime_delegate_try_invoke: + * \param delegate pointer to a delegate object. + * \param params parameters for the delegate. + * \param exc Pointer to the exception result. + * \param error set on error + * Invokes the delegate method \p delegate with the parameters provided. + * + * You can pass NULL as the \p exc argument if you don't want to + * catch exceptions, otherwise, \c *exc will be set to the exception + * thrown, if any. On failure to execute, \p error will be set. + * if an exception is thrown, you can't use the + * \c MonoObject* result from the function. + */ +MonoObject* +mono_runtime_delegate_try_invoke (MonoObject *delegate, void **params, MonoObject **exc, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + MonoMethod *im; + MonoClass *klass = delegate->vtable->klass; + MonoObject *o; + + im = mono_get_delegate_invoke (klass); + if (!im) + g_error ("Could not lookup delegate invoke method for delegate %s", mono_type_get_full_name (klass)); + + if (exc) { + o = mono_runtime_try_invoke (im, delegate, params, exc, error); + } else { + o = mono_runtime_invoke_checked (im, delegate, params, error); + } + + return o; +} + +/** + * mono_runtime_delegate_invoke_checked: + * \param delegate pointer to a delegate object. + * \param params parameters for the delegate. + * \param error set on error + * Invokes the delegate method \p delegate with the parameters provided. + * On failure \p error will be set and you can't use the \c MonoObject* + * result from the function. + */ +MonoObject* +mono_runtime_delegate_invoke_checked (MonoObject *delegate, void **params, MonoError *error) +{ + error_init (error); + return mono_runtime_delegate_try_invoke (delegate, params, NULL, error); +} + +static char **main_args = NULL; +static int num_main_args = 0; + +/** + * mono_runtime_get_main_args: + * \returns A \c MonoArray with the arguments passed to the main program + */ +MonoArray* +mono_runtime_get_main_args (void) +{ + HANDLE_FUNCTION_ENTER (); + MONO_REQ_GC_UNSAFE_MODE; + MonoError error; + MonoArrayHandle result = MONO_HANDLE_NEW (MonoArray, NULL); + error_init (&error); + MonoArrayHandle arg_array = mono_runtime_get_main_args_handle (&error); + goto_if_nok (&error, leave); + MONO_HANDLE_ASSIGN (result, arg_array); +leave: + /* FIXME: better external API that doesn't swallow the error */ + mono_error_cleanup (&error); + HANDLE_FUNCTION_RETURN_OBJ (result); +} + +static gboolean +handle_main_arg_array_set (MonoDomain *domain, int idx, MonoArrayHandle dest, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + error_init (error); + MonoStringHandle value = mono_string_new_handle (domain, main_args [idx], error); + goto_if_nok (error, leave); + MONO_HANDLE_ARRAY_SETREF (dest, idx, value); +leave: + HANDLE_FUNCTION_RETURN_VAL (is_ok (error)); +} + +/** + * mono_runtime_get_main_args_handle: + * \param error set on error + * \returns a \c MonoArray with the arguments passed to the main + * program. On failure returns NULL and sets \p error. + */ +MonoArrayHandle +mono_runtime_get_main_args_handle (MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + MonoArrayHandle array; + int i; + MonoDomain *domain = mono_domain_get (); + error_init (error); + + array = mono_array_new_handle (domain, mono_defaults.string_class, num_main_args, error); + if (!is_ok (error)) { + array = MONO_HANDLE_CAST (MonoArray, NULL_HANDLE); + goto leave; + } + for (i = 0; i < num_main_args; ++i) { + if (!handle_main_arg_array_set (domain, i, array, error)) + goto leave; + } +leave: + HANDLE_FUNCTION_RETURN_REF (MonoArray, array); +} + +static void +free_main_args (void) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + int i; + + for (i = 0; i < num_main_args; ++i) + g_free (main_args [i]); + g_free (main_args); + num_main_args = 0; + main_args = NULL; +} + +/** + * mono_runtime_set_main_args: + * \param argc number of arguments from the command line + * \param argv array of strings from the command line + * Set the command line arguments from an embedding application that doesn't otherwise call + * \c mono_runtime_run_main. + */ +int +mono_runtime_set_main_args (int argc, char* argv[]) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + int i; + + free_main_args (); + main_args = g_new0 (char*, argc); + num_main_args = argc; + + for (i = 0; i < argc; ++i) { + gchar *utf8_arg; + + utf8_arg = mono_utf8_from_external (argv[i]); + if (utf8_arg == NULL) { + g_print ("\nCannot determine the text encoding for argument %d (%s).\n", i, argv [i]); + g_print ("Please add the correct encoding to MONO_EXTERNAL_ENCODINGS and try again.\n"); + exit (-1); + } + + main_args [i] = utf8_arg; + } + + return 0; +} + +/* + * Prepare an array of arguments in order to execute a standard Main() + * method (argc/argv contains the executable name). This method also + * sets the command line argument value needed by System.Environment. + * + */ +static MonoArray* +prepare_run_main (MonoMethod *method, int argc, char *argv[]) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoError error; + int i; + MonoArray *args = NULL; + MonoDomain *domain = mono_domain_get (); + gchar *utf8_fullpath; + MonoMethodSignature *sig; + + g_assert (method != NULL); + + mono_thread_set_main (mono_thread_current ()); + + main_args = g_new0 (char*, argc); + num_main_args = argc; + + if (!g_path_is_absolute (argv [0])) { + gchar *basename = g_path_get_basename (argv [0]); + gchar *fullpath = g_build_filename (method->klass->image->assembly->basedir, + basename, + NULL); + + utf8_fullpath = mono_utf8_from_external (fullpath); + if(utf8_fullpath == NULL) { + /* Printing the arg text will cause glib to + * whinge about "Invalid UTF-8", but at least + * its relevant, and shows the problem text + * string. + */ + g_print ("\nCannot determine the text encoding for the assembly location: %s\n", fullpath); + g_print ("Please add the correct encoding to MONO_EXTERNAL_ENCODINGS and try again.\n"); + exit (-1); + } + + g_free (fullpath); + g_free (basename); + } else { + utf8_fullpath = mono_utf8_from_external (argv[0]); + if(utf8_fullpath == NULL) { + g_print ("\nCannot determine the text encoding for the assembly location: %s\n", argv[0]); + g_print ("Please add the correct encoding to MONO_EXTERNAL_ENCODINGS and try again.\n"); + exit (-1); + } + } + + main_args [0] = utf8_fullpath; + + for (i = 1; i < argc; ++i) { + gchar *utf8_arg; + + utf8_arg=mono_utf8_from_external (argv[i]); + if(utf8_arg==NULL) { + /* Ditto the comment about Invalid UTF-8 here */ + g_print ("\nCannot determine the text encoding for argument %d (%s).\n", i, argv[i]); + g_print ("Please add the correct encoding to MONO_EXTERNAL_ENCODINGS and try again.\n"); + exit (-1); + } + + main_args [i] = utf8_arg; + } + argc--; + argv++; + + sig = mono_method_signature (method); + if (!sig) { + g_print ("Unable to load Main method.\n"); + exit (-1); + } + + if (sig->param_count) { + args = (MonoArray*)mono_array_new_checked (domain, mono_defaults.string_class, argc, &error); + mono_error_assert_ok (&error); + for (i = 0; i < argc; ++i) { + /* The encodings should all work, given that + * we've checked all these args for the + * main_args array. + */ + gchar *str = mono_utf8_from_external (argv [i]); + MonoString *arg = mono_string_new_checked (domain, str, &error); + mono_error_assert_ok (&error); + mono_array_setref (args, i, arg); + g_free (str); + } + } else { + args = (MonoArray*)mono_array_new_checked (domain, mono_defaults.string_class, 0, &error); + mono_error_assert_ok (&error); + } + + mono_assembly_set_main (method->klass->image->assembly); + + return args; +} + +/** + * mono_runtime_run_main: + * \param method the method to start the application with (usually Main) + * \param argc number of arguments from the command line + * \param argv array of strings from the command line + * \param exc excetption results + * Execute a standard \c Main method (\p argc / \p argv contains the + * executable name). This method also sets the command line argument value + * needed by \c System.Environment. + */ +int +mono_runtime_run_main (MonoMethod *method, int argc, char* argv[], + MonoObject **exc) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoError error; + MonoArray *args = prepare_run_main (method, argc, argv); + int res; + if (exc) { + res = mono_runtime_try_exec_main (method, args, exc); + } else { + res = mono_runtime_exec_main_checked (method, args, &error); + mono_error_raise_exception_deprecated (&error); /* OK to throw, external only without a better alternative */ + } + return res; +} + +/** + * mono_runtime_run_main_checked: + * \param method the method to start the application with (usually \c Main) + * \param argc number of arguments from the command line + * \param argv array of strings from the command line + * \param error set on error + * + * Execute a standard \c Main method (\p argc / \p argv contains the + * executable name). This method also sets the command line argument value + * needed by \c System.Environment. On failure sets \p error. + */ +int +mono_runtime_run_main_checked (MonoMethod *method, int argc, char* argv[], + MonoError *error) +{ + error_init (error); + MonoArray *args = prepare_run_main (method, argc, argv); + return mono_runtime_exec_main_checked (method, args, error); +} + +/** + * mono_runtime_try_run_main: + * \param method the method to start the application with (usually \c Main) + * \param argc number of arguments from the command line + * \param argv array of strings from the command line + * \param exc set if \c Main throws an exception + * \param error set if \c Main can't be executed + * Execute a standard \c Main method (\p argc / \p argv contains the executable + * name). This method also sets the command line argument value needed + * by \c System.Environment. On failure sets \p error if Main can't be + * executed or \p exc if it threw an exception. + */ +int +mono_runtime_try_run_main (MonoMethod *method, int argc, char* argv[], + MonoObject **exc) +{ + g_assert (exc); + MonoArray *args = prepare_run_main (method, argc, argv); + return mono_runtime_try_exec_main (method, args, exc); +} + + +static MonoObject* +serialize_object (MonoObject *obj, gboolean *failure, MonoObject **exc) +{ + static MonoMethod *serialize_method; + + MonoError error; + void *params [1]; + MonoObject *array; + + if (!serialize_method) { + MonoClass *klass = mono_class_get_remoting_services_class (); + serialize_method = mono_class_get_method_from_name (klass, "SerializeCallData", -1); + } + + if (!serialize_method) { + *failure = TRUE; + return NULL; + } + + g_assert (!mono_class_is_marshalbyref (mono_object_class (obj))); + + params [0] = obj; + *exc = NULL; + + array = mono_runtime_try_invoke (serialize_method, NULL, params, exc, &error); + if (*exc == NULL && !mono_error_ok (&error)) + *exc = (MonoObject*) mono_error_convert_to_exception (&error); /* FIXME convert serialize_object to MonoError */ + else + mono_error_cleanup (&error); + + if (*exc) + *failure = TRUE; + + return array; +} + +static MonoObject* +deserialize_object (MonoObject *obj, gboolean *failure, MonoObject **exc) +{ + MONO_REQ_GC_UNSAFE_MODE; + + static MonoMethod *deserialize_method; + + MonoError error; + void *params [1]; + MonoObject *result; + + if (!deserialize_method) { + MonoClass *klass = mono_class_get_remoting_services_class (); + deserialize_method = mono_class_get_method_from_name (klass, "DeserializeCallData", -1); + } + if (!deserialize_method) { + *failure = TRUE; + return NULL; + } + + params [0] = obj; + *exc = NULL; + + result = mono_runtime_try_invoke (deserialize_method, NULL, params, exc, &error); + if (*exc == NULL && !mono_error_ok (&error)) + *exc = (MonoObject*) mono_error_convert_to_exception (&error); /* FIXME convert deserialize_object to MonoError */ + else + mono_error_cleanup (&error); + + if (*exc) + *failure = TRUE; + + return result; +} + +#ifndef DISABLE_REMOTING +static MonoObject* +make_transparent_proxy (MonoObject *obj, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + static MonoMethod *get_proxy_method; + + MonoDomain *domain = mono_domain_get (); + MonoRealProxy *real_proxy; + MonoReflectionType *reflection_type; + MonoTransparentProxy *transparent_proxy; + + error_init (error); + + if (!get_proxy_method) + get_proxy_method = mono_class_get_method_from_name (mono_defaults.real_proxy_class, "GetTransparentProxy", 0); + + g_assert (mono_class_is_marshalbyref (obj->vtable->klass)); + + real_proxy = (MonoRealProxy*) mono_object_new_checked (domain, mono_defaults.real_proxy_class, error); + return_val_if_nok (error, NULL); + reflection_type = mono_type_get_object_checked (domain, &obj->vtable->klass->byval_arg, error); + return_val_if_nok (error, NULL); + + MONO_OBJECT_SETREF (real_proxy, class_to_proxy, reflection_type); + MONO_OBJECT_SETREF (real_proxy, unwrapped_server, obj); + + MonoObject *exc = NULL; + + transparent_proxy = (MonoTransparentProxy*) mono_runtime_try_invoke (get_proxy_method, real_proxy, NULL, &exc, error); + if (exc != NULL && is_ok (error)) + mono_error_set_exception_instance (error, (MonoException*)exc); + + return (MonoObject*) transparent_proxy; +} +#endif /* DISABLE_REMOTING */ + +/** + * mono_object_xdomain_representation + * \param obj an object + * \param target_domain a domain + * \param error set on error. + * Creates a representation of obj in the domain \p target_domain. This + * is either a copy of \p obj arrived through via serialization and + * deserialization or a proxy, depending on whether the object is + * serializable or marshal by ref. \p obj must not be in \p target_domain. + * If the object cannot be represented in \p target_domain, NULL is + * returned and \p error is set appropriately. + */ +MonoObject* +mono_object_xdomain_representation (MonoObject *obj, MonoDomain *target_domain, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + MonoObject *deserialized = NULL; + +#ifndef DISABLE_REMOTING + if (mono_class_is_marshalbyref (mono_object_class (obj))) { + deserialized = make_transparent_proxy (obj, error); + } + else +#endif + { + gboolean failure = FALSE; + MonoDomain *domain = mono_domain_get (); + MonoObject *serialized; + MonoObject *exc = NULL; + + mono_domain_set_internal_with_options (mono_object_domain (obj), FALSE); + serialized = serialize_object (obj, &failure, &exc); + mono_domain_set_internal_with_options (target_domain, FALSE); + if (!failure) + deserialized = deserialize_object (serialized, &failure, &exc); + if (domain != target_domain) + mono_domain_set_internal_with_options (domain, FALSE); + if (failure) + mono_error_set_exception_instance (error, (MonoException*)exc); + } + + return deserialized; +} + +/* Used in call_unhandled_exception_delegate */ +static MonoObject * +create_unhandled_exception_eventargs (MonoObject *exc, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + MonoClass *klass; + gpointer args [2]; + MonoMethod *method = NULL; + MonoBoolean is_terminating = TRUE; + MonoObject *obj; + + klass = mono_class_get_unhandled_exception_event_args_class (); + mono_class_init (klass); + + /* UnhandledExceptionEventArgs only has 1 public ctor with 2 args */ + method = mono_class_get_method_from_name_flags (klass, ".ctor", 2, METHOD_ATTRIBUTE_PUBLIC); + g_assert (method); + + args [0] = exc; + args [1] = &is_terminating; + + obj = mono_object_new_checked (mono_domain_get (), klass, error); + return_val_if_nok (error, NULL); + + mono_runtime_invoke_checked (method, obj, args, error); + return_val_if_nok (error, NULL); + + return obj; +} + +/* Used in mono_unhandled_exception */ +static void +call_unhandled_exception_delegate (MonoDomain *domain, MonoObject *delegate, MonoObject *exc) { + MONO_REQ_GC_UNSAFE_MODE; + + MonoError error; + MonoObject *e = NULL; + gpointer pa [2]; + MonoDomain *current_domain = mono_domain_get (); + + if (domain != current_domain) + mono_domain_set_internal_with_options (domain, FALSE); + + g_assert (domain == mono_object_domain (domain->domain)); + + if (mono_object_domain (exc) != domain) { + + exc = mono_object_xdomain_representation (exc, domain, &error); + if (!exc) { + if (!is_ok (&error)) { + MonoError inner_error; + MonoException *serialization_exc = mono_error_convert_to_exception (&error); + exc = mono_object_xdomain_representation ((MonoObject*)serialization_exc, domain, &inner_error); + mono_error_assert_ok (&inner_error); + } else { + exc = (MonoObject*) mono_exception_from_name_msg (mono_get_corlib (), + "System.Runtime.Serialization", "SerializationException", + "Could not serialize unhandled exception."); + } + } + } + g_assert (mono_object_domain (exc) == domain); + + pa [0] = domain->domain; + pa [1] = create_unhandled_exception_eventargs (exc, &error); + mono_error_assert_ok (&error); + mono_runtime_delegate_try_invoke (delegate, pa, &e, &error); + if (!is_ok (&error)) { + if (e == NULL) + e = (MonoObject*)mono_error_convert_to_exception (&error); + else + mono_error_cleanup (&error); + } + + if (domain != current_domain) + mono_domain_set_internal_with_options (current_domain, FALSE); + + if (e) { + gchar *msg = mono_string_to_utf8_checked (((MonoException *) e)->message, &error); + if (!mono_error_ok (&error)) { + g_warning ("Exception inside UnhandledException handler with invalid message (Invalid characters)\n"); + mono_error_cleanup (&error); + } else { + g_warning ("exception inside UnhandledException handler: %s\n", msg); + g_free (msg); + } + } +} + +static MonoRuntimeUnhandledExceptionPolicy runtime_unhandled_exception_policy = MONO_UNHANDLED_POLICY_CURRENT; + +/** + * mono_runtime_unhandled_exception_policy_set: + * \param policy the new policy + * This is a VM internal routine. + * Sets the runtime policy for handling unhandled exceptions. + */ +void +mono_runtime_unhandled_exception_policy_set (MonoRuntimeUnhandledExceptionPolicy policy) { + runtime_unhandled_exception_policy = policy; +} + +/** + * mono_runtime_unhandled_exception_policy_get: + * + * This is a VM internal routine. + * + * Gets the runtime policy for handling unhandled exceptions. + */ +MonoRuntimeUnhandledExceptionPolicy +mono_runtime_unhandled_exception_policy_get (void) { + return runtime_unhandled_exception_policy; +} + +/** + * mono_unhandled_exception: + * \param exc exception thrown + * This is a VM internal routine. + * + * We call this function when we detect an unhandled exception + * in the default domain. + * + * It invokes the \c UnhandledException event in \c AppDomain or prints + * a warning to the console + */ +void +mono_unhandled_exception (MonoObject *exc_raw) +{ + MonoError error; + HANDLE_FUNCTION_ENTER (); + MONO_HANDLE_DCL (MonoObject, exc); + error_init (&error); + mono_unhandled_exception_checked (exc, &error); + mono_error_assert_ok (&error); + HANDLE_FUNCTION_RETURN (); +} + +/** + * mono_unhandled_exception: + * @exc: exception thrown + * + * This is a VM internal routine. + * + * We call this function when we detect an unhandled exception + * in the default domain. + * + * It invokes the * UnhandledException event in AppDomain or prints + * a warning to the console + */ +void +mono_unhandled_exception_checked (MonoObjectHandle exc, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + MonoClassField *field; + MonoDomain *current_domain, *root_domain; + MonoObjectHandle current_appdomain_delegate = MONO_HANDLE_NEW (MonoObject, NULL); + + MonoClass *klass = mono_handle_class (exc); + /* + * AppDomainUnloadedException don't behave like unhandled exceptions unless thrown from + * a thread started in unmanaged world. + * https://msdn.microsoft.com/en-us/library/system.appdomainunloadedexception(v=vs.110).aspx#Anchor_6 + */ + if (klass == mono_defaults.threadabortexception_class || + (klass == mono_class_get_appdomain_unloaded_exception_class () && + mono_thread_info_current ()->runtime_thread)) + return; + + field = mono_class_get_field_from_name (mono_defaults.appdomain_class, "UnhandledException"); + g_assert (field); + + current_domain = mono_domain_get (); + root_domain = mono_get_root_domain (); + + MonoObjectHandle root_appdomain_delegate = MONO_HANDLE_NEW (MonoObject, mono_field_get_value_object_checked (root_domain, field, (MonoObject*) root_domain->domain, error)); /* FIXME use handles for mono_field_get_value_object_checked */ + return_if_nok (error); + if (current_domain != root_domain) { + MONO_HANDLE_ASSIGN (current_appdomain_delegate, MONO_HANDLE_NEW (MonoObject, mono_field_get_value_object_checked (current_domain, field, (MonoObject*) current_domain->domain, error))); /* FIXME use handles for mono_field_get_value_object_checked */ + return_if_nok (error); + } + + if (MONO_HANDLE_IS_NULL (current_appdomain_delegate) && MONO_HANDLE_IS_NULL (root_appdomain_delegate)) { + mono_print_unhandled_exception (MONO_HANDLE_RAW (exc)); /* FIXME use handles for mono_print_unhandled_exception */ + } else { + /* unhandled exception callbacks must not be aborted */ + mono_threads_begin_abort_protected_block (); + if (!MONO_HANDLE_IS_NULL (root_appdomain_delegate)) + call_unhandled_exception_delegate (root_domain, MONO_HANDLE_RAW (root_appdomain_delegate), MONO_HANDLE_RAW (exc)); /* FIXME use handles in call_unhandled_exception_delegate */ + if (!MONO_HANDLE_IS_NULL (current_appdomain_delegate)) + call_unhandled_exception_delegate (current_domain, MONO_HANDLE_RAW (current_appdomain_delegate), MONO_HANDLE_RAW (exc)); + mono_threads_end_abort_protected_block (); + } + + /* set exitcode only if we will abort the process */ + if ((main_thread && mono_thread_internal_current () == main_thread->internal_thread) + || mono_runtime_unhandled_exception_policy_get () == MONO_UNHANDLED_POLICY_CURRENT) + { + mono_environment_exitcode_set (1); + } +} + +/** + * mono_runtime_exec_managed_code: + * \param domain Application domain + * \param main_func function to invoke from the execution thread + * \param main_args parameter to the main_func + * Launch a new thread to execute a function + * + * \p main_func is called back from the thread with main_args as the + * parameter. The callback function is expected to start \c Main + * eventually. This function then waits for all managed threads to + * finish. + * It is not necessary anymore to execute managed code in a subthread, + * so this function should not be used anymore by default: just + * execute the code and then call mono_thread_manage(). + */ +void +mono_runtime_exec_managed_code (MonoDomain *domain, + MonoMainThreadFunc main_func, + gpointer main_args) +{ + MonoError error; + mono_thread_create_checked (domain, main_func, main_args, &error); + mono_error_assert_ok (&error); + + mono_thread_manage (); +} + +static void +prepare_thread_to_exec_main (MonoDomain *domain, MonoMethod *method) +{ + MonoInternalThread* thread = mono_thread_internal_current (); + MonoCustomAttrInfo* cinfo; + gboolean has_stathread_attribute; + + if (!domain->entry_assembly) { + gchar *str; + MonoError error; + MonoAssembly *assembly; + + assembly = method->klass->image->assembly; + domain->entry_assembly = assembly; + /* Domains created from another domain already have application_base and configuration_file set */ + if (domain->setup->application_base == NULL) { + MonoString *basedir = mono_string_new_checked (domain, assembly->basedir, &error); + mono_error_assert_ok (&error); + MONO_OBJECT_SETREF (domain->setup, application_base, basedir); + } + + if (domain->setup->configuration_file == NULL) { + str = g_strconcat (assembly->image->name, ".config", NULL); + MonoString *config_file = mono_string_new_checked (domain, str, &error); + mono_error_assert_ok (&error); + MONO_OBJECT_SETREF (domain->setup, configuration_file, config_file); + g_free (str); + mono_domain_set_options_from_config (domain); + } + } + + MonoError cattr_error; + cinfo = mono_custom_attrs_from_method_checked (method, &cattr_error); + mono_error_cleanup (&cattr_error); /* FIXME warn here? */ + if (cinfo) { + has_stathread_attribute = mono_custom_attrs_has_attr (cinfo, mono_class_get_sta_thread_attribute_class ()); + if (!cinfo->cached) + mono_custom_attrs_free (cinfo); + } else { + has_stathread_attribute = FALSE; + } + if (has_stathread_attribute) { + thread->apartment_state = ThreadApartmentState_STA; + } else { + thread->apartment_state = ThreadApartmentState_MTA; + } + mono_thread_init_apartment_state (); + +} + +static int +do_exec_main_checked (MonoMethod *method, MonoArray *args, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + gpointer pa [1]; + int rval; + + error_init (error); + g_assert (args); + + pa [0] = args; + + /* FIXME: check signature of method */ + if (mono_method_signature (method)->ret->type == MONO_TYPE_I4) { + MonoObject *res; + res = mono_runtime_invoke_checked (method, NULL, pa, error); + if (is_ok (error)) + rval = *(guint32 *)((char *)res + sizeof (MonoObject)); + else + rval = -1; + mono_environment_exitcode_set (rval); + } else { + mono_runtime_invoke_checked (method, NULL, pa, error); + + if (is_ok (error)) + rval = 0; + else { + rval = -1; + } + } + return rval; +} + +static int +do_try_exec_main (MonoMethod *method, MonoArray *args, MonoObject **exc) +{ + MONO_REQ_GC_UNSAFE_MODE; + + gpointer pa [1]; + int rval; + + g_assert (args); + g_assert (exc); + + pa [0] = args; + + /* FIXME: check signature of method */ + if (mono_method_signature (method)->ret->type == MONO_TYPE_I4) { + MonoError inner_error; + MonoObject *res; + res = mono_runtime_try_invoke (method, NULL, pa, exc, &inner_error); + if (*exc == NULL && !mono_error_ok (&inner_error)) + *exc = (MonoObject*) mono_error_convert_to_exception (&inner_error); + else + mono_error_cleanup (&inner_error); + + if (*exc == NULL) + rval = *(guint32 *)((char *)res + sizeof (MonoObject)); + else + rval = -1; + + mono_environment_exitcode_set (rval); + } else { + MonoError inner_error; + mono_runtime_try_invoke (method, NULL, pa, exc, &inner_error); + if (*exc == NULL && !mono_error_ok (&inner_error)) + *exc = (MonoObject*) mono_error_convert_to_exception (&inner_error); + else + mono_error_cleanup (&inner_error); + + if (*exc == NULL) + rval = 0; + else { + /* If the return type of Main is void, only + * set the exitcode if an exception was thrown + * (we don't want to blow away an + * explicitly-set exit code) + */ + rval = -1; + mono_environment_exitcode_set (rval); + } + } + + return rval; +} + +/* + * Execute a standard Main() method (args doesn't contain the + * executable name). + */ +int +mono_runtime_exec_main (MonoMethod *method, MonoArray *args, MonoObject **exc) +{ + MonoError error; + prepare_thread_to_exec_main (mono_object_domain (args), method); + if (exc) { + int rval = do_try_exec_main (method, args, exc); + return rval; + } else { + int rval = do_exec_main_checked (method, args, &error); + mono_error_raise_exception_deprecated (&error); /* OK to throw, external only with no better option */ + return rval; + } +} + +/* + * Execute a standard Main() method (args doesn't contain the + * executable name). + * + * On failure sets @error + */ +int +mono_runtime_exec_main_checked (MonoMethod *method, MonoArray *args, MonoError *error) +{ + error_init (error); + prepare_thread_to_exec_main (mono_object_domain (args), method); + return do_exec_main_checked (method, args, error); +} + +/* + * Execute a standard Main() method (args doesn't contain the + * executable name). + * + * On failure sets @error if Main couldn't be executed, or @exc if it threw an exception. + */ +int +mono_runtime_try_exec_main (MonoMethod *method, MonoArray *args, MonoObject **exc) +{ + prepare_thread_to_exec_main (mono_object_domain (args), method); + return do_try_exec_main (method, args, exc); +} + + + +/** invoke_array_extract_argument: + * @params: array of arguments to the method. + * @i: the index of the argument to extract. + * @t: ith type from the method signature. + * @has_byref_nullables: outarg - TRUE if method expects a byref nullable argument + * @error: set on error. + * + * Given an array of method arguments, return the ith one using the corresponding type + * to perform necessary unboxing. If method expects a ref nullable argument, writes TRUE to @has_byref_nullables. + * + * On failure sets @error and returns NULL. + */ +static gpointer +invoke_array_extract_argument (MonoArray *params, int i, MonoType *t, gboolean* has_byref_nullables, MonoError *error) +{ + MonoType *t_orig = t; + gpointer result = NULL; + error_init (error); + again: + switch (t->type) { + case MONO_TYPE_U1: + case MONO_TYPE_I1: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_U2: + case MONO_TYPE_I2: + case MONO_TYPE_CHAR: + case MONO_TYPE_U: + case MONO_TYPE_I: + case MONO_TYPE_U4: + case MONO_TYPE_I4: + case MONO_TYPE_U8: + case MONO_TYPE_I8: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + case MONO_TYPE_VALUETYPE: + if (t->type == MONO_TYPE_VALUETYPE && mono_class_is_nullable (mono_class_from_mono_type (t_orig))) { + /* The runtime invoke wrapper needs the original boxed vtype, it does handle byref values as well. */ + result = mono_array_get (params, MonoObject*, i); + if (t->byref) + *has_byref_nullables = TRUE; + } else { + /* MS seems to create the objects if a null is passed in */ + gboolean was_null = FALSE; + if (!mono_array_get (params, MonoObject*, i)) { + MonoObject *o = mono_object_new_checked (mono_domain_get (), mono_class_from_mono_type (t_orig), error); + return_val_if_nok (error, NULL); + mono_array_setref (params, i, o); + was_null = TRUE; + } + + if (t->byref) { + /* + * We can't pass the unboxed vtype byref to the callee, since + * that would mean the callee would be able to modify boxed + * primitive types. So we (and MS) make a copy of the boxed + * object, pass that to the callee, and replace the original + * boxed object in the arg array with the copy. + */ + MonoObject *orig = mono_array_get (params, MonoObject*, i); + MonoObject *copy = mono_value_box_checked (mono_domain_get (), orig->vtable->klass, mono_object_unbox (orig), error); + return_val_if_nok (error, NULL); + mono_array_setref (params, i, copy); + } + + result = mono_object_unbox (mono_array_get (params, MonoObject*, i)); + if (!t->byref && was_null) + mono_array_setref (params, i, NULL); + } + break; + case MONO_TYPE_STRING: + case MONO_TYPE_OBJECT: + case MONO_TYPE_CLASS: + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: + if (t->byref) + result = mono_array_addr (params, MonoObject*, i); + // FIXME: I need to check this code path + else + result = mono_array_get (params, MonoObject*, i); + break; + case MONO_TYPE_GENERICINST: + if (t->byref) + t = &t->data.generic_class->container_class->this_arg; + else + t = &t->data.generic_class->container_class->byval_arg; + goto again; + case MONO_TYPE_PTR: { + MonoObject *arg; + + /* The argument should be an IntPtr */ + arg = mono_array_get (params, MonoObject*, i); + if (arg == NULL) { + result = NULL; + } else { + g_assert (arg->vtable->klass == mono_defaults.int_class); + result = ((MonoIntPtr*)arg)->m_value; + } + break; + } + default: + g_error ("type 0x%x not handled in mono_runtime_invoke_array", t_orig->type); + } + return result; +} +/** + * mono_runtime_invoke_array: + * \param method method to invoke + * \param obj object instance + * \param params arguments to the method + * \param exc exception information. + * Invokes the method represented by \p method on the object \p obj. + * + * \p obj is the \c this pointer, it should be NULL for static + * methods, a \c MonoObject* for object instances and a pointer to + * the value type for value types. + * + * The \p params array contains the arguments to the method with the + * same convention: \c MonoObject* pointers for object instances and + * pointers to the value type otherwise. The \c _invoke_array + * variant takes a C# \c object[] as the params argument (\c MonoArray*): + * in this case the value types are boxed inside the + * respective reference representation. + * + * From unmanaged code you'll usually use the + * mono_runtime_invoke_checked() variant. + * + * Note that this function doesn't handle virtual methods for + * you, it will exec the exact method you pass: we still need to + * expose a function to lookup the derived class implementation + * of a virtual method (there are examples of this in the code, + * though). + * + * You can pass NULL as the \p exc argument if you don't want to + * catch exceptions, otherwise, \c *exc will be set to the exception + * thrown, if any. if an exception is thrown, you can't use the + * \c MonoObject* result from the function. + * + * If the method returns a value type, it is boxed in an object + * reference. + */ +MonoObject* +mono_runtime_invoke_array (MonoMethod *method, void *obj, MonoArray *params, + MonoObject **exc) +{ + MonoError error; + if (exc) { + MonoObject *result = mono_runtime_try_invoke_array (method, obj, params, exc, &error); + if (*exc) { + mono_error_cleanup (&error); + return NULL; + } else { + if (!is_ok (&error)) + *exc = (MonoObject*)mono_error_convert_to_exception (&error); + return result; + } + } else { + MonoObject *result = mono_runtime_try_invoke_array (method, obj, params, NULL, &error); + mono_error_raise_exception_deprecated (&error); /* OK to throw, external only without a good alternative */ + return result; + } +} + +/** + * mono_runtime_invoke_array_checked: + * \param method method to invoke + * \param obj object instance + * \param params arguments to the method + * \param error set on failure. + * Invokes the method represented by \p method on the object \p obj. + * + * \p obj is the \c this pointer, it should be NULL for static + * methods, a \c MonoObject* for object instances and a pointer to + * the value type for value types. + * + * The \p params array contains the arguments to the method with the + * same convention: \c MonoObject* pointers for object instances and + * pointers to the value type otherwise. The \c _invoke_array + * variant takes a C# \c object[] as the \p params argument (\c MonoArray*): + * in this case the value types are boxed inside the + * respective reference representation. + * + * From unmanaged code you'll usually use the + * mono_runtime_invoke_checked() variant. + * + * Note that this function doesn't handle virtual methods for + * you, it will exec the exact method you pass: we still need to + * expose a function to lookup the derived class implementation + * of a virtual method (there are examples of this in the code, + * though). + * + * On failure or exception, \p error will be set. In that case, you + * can't use the \c MonoObject* result from the function. + * + * If the method returns a value type, it is boxed in an object + * reference. + */ +MonoObject* +mono_runtime_invoke_array_checked (MonoMethod *method, void *obj, MonoArray *params, + MonoError *error) +{ + error_init (error); + return mono_runtime_try_invoke_array (method, obj, params, NULL, error); +} + +/** + * mono_runtime_try_invoke_array: + * \param method method to invoke + * \param obj object instance + * \param params arguments to the method + * \param exc exception information. + * \param error set on failure. + * Invokes the method represented by \p method on the object \p obj. + * + * \p obj is the \c this pointer, it should be NULL for static + * methods, a \c MonoObject* for object instances and a pointer to + * the value type for value types. + * + * The \p params array contains the arguments to the method with the + * same convention: \c MonoObject* pointers for object instances and + * pointers to the value type otherwise. The \c _invoke_array + * variant takes a C# \c object[] as the params argument (\c MonoArray*): + * in this case the value types are boxed inside the + * respective reference representation. + * + * From unmanaged code you'll usually use the + * mono_runtime_invoke_checked() variant. + * + * Note that this function doesn't handle virtual methods for + * you, it will exec the exact method you pass: we still need to + * expose a function to lookup the derived class implementation + * of a virtual method (there are examples of this in the code, + * though). + * + * You can pass NULL as the \p exc argument if you don't want to catch + * exceptions, otherwise, \c *exc will be set to the exception thrown, if + * any. On other failures, \p error will be set. If an exception is + * thrown or there's an error, you can't use the \c MonoObject* result + * from the function. + * + * If the method returns a value type, it is boxed in an object + * reference. + */ +MonoObject* +mono_runtime_try_invoke_array (MonoMethod *method, void *obj, MonoArray *params, + MonoObject **exc, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + + MonoMethodSignature *sig = mono_method_signature (method); + gpointer *pa = NULL; + MonoObject *res; + int i; + gboolean has_byref_nullables = FALSE; + + if (NULL != params) { + pa = (void **)alloca (sizeof (gpointer) * mono_array_length (params)); + for (i = 0; i < mono_array_length (params); i++) { + MonoType *t = sig->params [i]; + pa [i] = invoke_array_extract_argument (params, i, t, &has_byref_nullables, error); + return_val_if_nok (error, NULL); + } + } + + if (!strcmp (method->name, ".ctor") && method->klass != mono_defaults.string_class) { + void *o = obj; + + 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; + else { + return mono_value_box_checked (mono_domain_get (), method->klass->cast_class, pa [0], error); + } + } + + if (!obj) { + obj = mono_object_new_checked (mono_domain_get (), method->klass, error); + mono_error_assert_ok (error); + g_assert (obj); /*maybe we should raise a TLE instead?*/ +#ifndef DISABLE_REMOTING + if (mono_object_is_transparent_proxy (obj)) { + method = mono_marshal_get_remoting_invoke (method->slot == -1 ? method : method->klass->vtable [method->slot]); + } +#endif + if (method->klass->valuetype) + o = (MonoObject *)mono_object_unbox ((MonoObject *)obj); + else + o = obj; + } else if (method->klass->valuetype) { + obj = mono_value_box_checked (mono_domain_get (), method->klass, obj, error); + return_val_if_nok (error, NULL); + } + + if (exc) { + mono_runtime_try_invoke (method, o, pa, exc, error); + } else { + mono_runtime_invoke_checked (method, o, pa, error); + } + + return (MonoObject *)obj; + } else { + if (mono_class_is_nullable (method->klass)) { + MonoObject *nullable; + + /* Convert the unboxed vtype into a Nullable structure */ + nullable = mono_object_new_checked (mono_domain_get (), method->klass, error); + return_val_if_nok (error, NULL); + + MonoObject *boxed = mono_value_box_checked (mono_domain_get (), method->klass->cast_class, obj, error); + return_val_if_nok (error, NULL); + mono_nullable_init ((guint8 *)mono_object_unbox (nullable), boxed, method->klass); + obj = mono_object_unbox (nullable); + } + + /* obj must be already unboxed if needed */ + if (exc) { + res = mono_runtime_try_invoke (method, obj, pa, exc, error); + } else { + res = mono_runtime_invoke_checked (method, obj, pa, error); + } + return_val_if_nok (error, NULL); + + if (sig->ret->type == MONO_TYPE_PTR) { + MonoClass *pointer_class; + static MonoMethod *box_method; + void *box_args [2]; + MonoObject *box_exc; + + /* + * The runtime-invoke wrapper returns a boxed IntPtr, need to + * convert it to a Pointer object. + */ + pointer_class = mono_class_get_pointer_class (); + if (!box_method) + box_method = mono_class_get_method_from_name (pointer_class, "Box", -1); + + g_assert (res->vtable->klass == mono_defaults.int_class); + box_args [0] = ((MonoIntPtr*)res)->m_value; + box_args [1] = mono_type_get_object_checked (mono_domain_get (), sig->ret, error); + return_val_if_nok (error, NULL); + + res = mono_runtime_try_invoke (box_method, NULL, box_args, &box_exc, error); + g_assert (box_exc == NULL); + mono_error_assert_ok (error); + } + + if (has_byref_nullables) { + /* + * The runtime invoke wrapper already converted byref nullables back, + * and stored them in pa, we just need to copy them back to the + * managed array. + */ + for (i = 0; i < mono_array_length (params); i++) { + MonoType *t = sig->params [i]; + + if (t->byref && t->type == MONO_TYPE_GENERICINST && mono_class_is_nullable (mono_class_from_mono_type (t))) + mono_array_setref (params, i, pa [i]); + } + } + + return res; + } +} + +/** + * mono_object_new: + * \param klass the class of the object that we want to create + * \returns a newly created object whose definition is + * looked up using \p klass. This will not invoke any constructors, + * so the consumer of this routine has to invoke any constructors on + * its own to initialize the object. + * + * It returns NULL on failure. + */ +MonoObject * +mono_object_new (MonoDomain *domain, MonoClass *klass) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoError error; + + MonoObject * result = mono_object_new_checked (domain, klass, &error); + + mono_error_cleanup (&error); + return result; +} + +MonoObject * +ves_icall_object_new (MonoDomain *domain, MonoClass *klass) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoError error; + + MonoObject * result = mono_object_new_checked (domain, klass, &error); + + mono_error_set_pending_exception (&error); + return result; +} + +/** + * mono_object_new_checked: + * \param klass the class of the object that we want to create + * \param error set on error + * \returns a newly created object whose definition is + * looked up using \p klass. This will not invoke any constructors, + * so the consumer of this routine has to invoke any constructors on + * its own to initialize the object. + * + * It returns NULL on failure and sets \p error. + */ +MonoObject * +mono_object_new_checked (MonoDomain *domain, MonoClass *klass, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoVTable *vtable; + + vtable = mono_class_vtable_full (domain, klass, error); + if (!is_ok (error)) + return NULL; + + MonoObject *o = mono_object_new_specific_checked (vtable, error); + return o; +} + +/** + * mono_object_new_pinned: + * + * Same as mono_object_new, but the returned object will be pinned. + * For SGEN, these objects will only be freed at appdomain unload. + */ +MonoObject * +mono_object_new_pinned (MonoDomain *domain, MonoClass *klass, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoVTable *vtable; + + error_init (error); + + vtable = mono_class_vtable (domain, klass); + g_assert (vtable); /* FIXME don't swallow the error */ + + MonoObject *o = (MonoObject *)mono_gc_alloc_pinned_obj (vtable, mono_class_instance_size (klass)); + + if (G_UNLIKELY (!o)) + mono_error_set_out_of_memory (error, "Could not allocate %i bytes", mono_class_instance_size (klass)); + else if (G_UNLIKELY (vtable->klass->has_finalize)) + mono_object_register_finalizer (o); + + return o; +} + +/** + * mono_object_new_specific: + * \param vtable the vtable of the object that we want to create + * \returns A newly created object with class and domain specified + * by \p vtable + */ +MonoObject * +mono_object_new_specific (MonoVTable *vtable) +{ + MonoError error; + MonoObject *o = mono_object_new_specific_checked (vtable, &error); + mono_error_cleanup (&error); + + return o; +} + +MonoObject * +mono_object_new_specific_checked (MonoVTable *vtable, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoObject *o; + + error_init (error); + + /* check for is_com_object for COM Interop */ + if (mono_vtable_is_remote (vtable) || mono_class_is_com_object (vtable->klass)) + { + gpointer pa [1]; + MonoMethod *im = vtable->domain->create_proxy_for_type_method; + + if (im == NULL) { + MonoClass *klass = mono_class_get_activation_services_class (); + + if (!klass->inited) + mono_class_init (klass); + + im = mono_class_get_method_from_name (klass, "CreateProxyForType", 1); + if (!im) { + mono_error_set_not_supported (error, "Linked away."); + return NULL; + } + vtable->domain->create_proxy_for_type_method = im; + } + + pa [0] = mono_type_get_object_checked (mono_domain_get (), &vtable->klass->byval_arg, error); + if (!mono_error_ok (error)) + return NULL; + + o = mono_runtime_invoke_checked (im, NULL, pa, error); + if (!mono_error_ok (error)) + return NULL; + + if (o != NULL) + return o; + } + + return mono_object_new_alloc_specific_checked (vtable, error); +} + +MonoObject * +ves_icall_object_new_specific (MonoVTable *vtable) +{ + MonoError error; + MonoObject *o = mono_object_new_specific_checked (vtable, &error); + mono_error_set_pending_exception (&error); + + return o; +} + +/** + * mono_object_new_alloc_specific: + * \param vtable virtual table for the object. + * This function allocates a new \c MonoObject with the type derived + * from the \p vtable information. If the class of this object has a + * finalizer, then the object will be tracked for finalization. + * + * This method might raise an exception on errors. Use the + * \c mono_object_new_fast_checked method if you want to manually raise + * the exception. + * + * \returns the allocated object. + */ +MonoObject * +mono_object_new_alloc_specific (MonoVTable *vtable) +{ + MonoError error; + MonoObject *o = mono_object_new_alloc_specific_checked (vtable, &error); + mono_error_cleanup (&error); + + return o; +} + +/** + * mono_object_new_alloc_specific_checked: + * \param vtable virtual table for the object. + * \param error holds the error return value. + * + * This function allocates a new \c MonoObject with the type derived + * from the \p vtable information. If the class of this object has a + * finalizer, then the object will be tracked for finalization. + * + * If there is not enough memory, the \p error parameter will be set + * and will contain a user-visible message with the amount of bytes + * that were requested. + * + * \returns the allocated object, or NULL if there is not enough memory + */ +MonoObject * +mono_object_new_alloc_specific_checked (MonoVTable *vtable, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoObject *o; + + error_init (error); + + o = (MonoObject *)mono_gc_alloc_obj (vtable, vtable->klass->instance_size); + + if (G_UNLIKELY (!o)) + mono_error_set_out_of_memory (error, "Could not allocate %i bytes", vtable->klass->instance_size); + else if (G_UNLIKELY (vtable->klass->has_finalize || vtable->klass->has_weak_fields)) { + if (vtable->klass->has_finalize) + mono_object_register_finalizer (o); + if (vtable->klass->has_weak_fields) + mono_gc_register_obj_with_weak_fields (o); + } + + return o; +} + +/** + * mono_object_new_fast: + * \param vtable virtual table for the object. + * + * This function allocates a new \c MonoObject with the type derived + * from the \p vtable information. The returned object is not tracked + * for finalization. If your object implements a finalizer, you should + * use \c mono_object_new_alloc_specific instead. + * + * This method might raise an exception on errors. Use the + * \c mono_object_new_fast_checked method if you want to manually raise + * the exception. + * + * \returns the allocated object. + */ +MonoObject* +mono_object_new_fast (MonoVTable *vtable) +{ + MonoError error; + MonoObject *o = mono_object_new_fast_checked (vtable, &error); + mono_error_cleanup (&error); + + return o; +} + +/** + * mono_object_new_fast_checked: + * \param vtable virtual table for the object. + * \param error holds the error return value. + * + * This function allocates a new \c MonoObject with the type derived + * from the \p vtable information. The returned object is not tracked + * for finalization. If your object implements a finalizer, you should + * use \c mono_object_new_alloc_specific_checked instead. + * + * If there is not enough memory, the \p error parameter will be set + * and will contain a user-visible message with the amount of bytes + * that were requested. + * + * \returns the allocated object, or NULL if there is not enough memory + */ +MonoObject* +mono_object_new_fast_checked (MonoVTable *vtable, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoObject *o; + + error_init (error); + + o = mono_gc_alloc_obj (vtable, vtable->klass->instance_size); + + if (G_UNLIKELY (!o)) + mono_error_set_out_of_memory (error, "Could not allocate %i bytes", vtable->klass->instance_size); + + return o; +} + +MonoObject* +mono_object_new_mature (MonoVTable *vtable, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoObject *o; + + error_init (error); + + o = mono_gc_alloc_mature (vtable, vtable->klass->instance_size); + + if (G_UNLIKELY (!o)) + mono_error_set_out_of_memory (error, "Could not allocate %i bytes", vtable->klass->instance_size); + else if (G_UNLIKELY (vtable->klass->has_finalize)) + mono_object_register_finalizer (o); + + return o; +} + +/** + * mono_object_new_from_token: + * \param image Context where the type_token is hosted + * \param token a token of the type that we want to create + * \returns A newly created object whose definition is + * looked up using \p token in the \p image image + */ +MonoObject * +mono_object_new_from_token (MonoDomain *domain, MonoImage *image, guint32 token) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoError error; + MonoObject *result; + MonoClass *klass; + + klass = mono_class_get_checked (image, token, &error); + mono_error_assert_ok (&error); + + result = mono_object_new_checked (domain, klass, &error); + + mono_error_cleanup (&error); + return result; + +} + + +/** + * mono_object_clone: + * \param obj the object to clone + * \returns A newly created object who is a shallow copy of \p obj + */ +MonoObject * +mono_object_clone (MonoObject *obj) +{ + MonoError error; + MonoObject *o = mono_object_clone_checked (obj, &error); + mono_error_cleanup (&error); + + return o; +} + +MonoObject * +mono_object_clone_checked (MonoObject *obj, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoObject *o; + int size; + + error_init (error); + + size = obj->vtable->klass->instance_size; + + if (obj->vtable->klass->rank) + return (MonoObject*)mono_array_clone_checked ((MonoArray*)obj, error); + + o = (MonoObject *)mono_gc_alloc_obj (obj->vtable, size); + + if (G_UNLIKELY (!o)) { + mono_error_set_out_of_memory (error, "Could not allocate %i bytes", size); + return NULL; + } + + /* If the object doesn't contain references this will do a simple memmove. */ + mono_gc_wbarrier_object_copy (o, obj); + + if (obj->vtable->klass->has_finalize) + mono_object_register_finalizer (o); + return o; +} + +/** + * mono_array_full_copy: + * \param src source array to copy + * \param dest destination array + * Copies the content of one array to another with exactly the same type and size. + */ +void +mono_array_full_copy (MonoArray *src, MonoArray *dest) +{ + MONO_REQ_GC_UNSAFE_MODE; + + uintptr_t size; + MonoClass *klass = src->obj.vtable->klass; + + g_assert (klass == dest->obj.vtable->klass); + + size = mono_array_length (src); + g_assert (size == mono_array_length (dest)); + size *= mono_array_element_size (klass); + + array_full_copy_unchecked_size (src, dest, klass, size); +} + +static void +array_full_copy_unchecked_size (MonoArray *src, MonoArray *dest, MonoClass *klass, uintptr_t size) +{ + 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)); + else + mono_gc_memmove_atomic (&dest->vector, &src->vector, size); + } else { + mono_array_memcpy_refs (dest, 0, src, 0, mono_array_length (src)); + } + } else { + mono_gc_memmove_atomic (&dest->vector, &src->vector, size); + } +} + +/** + * mono_array_clone_in_domain: + * \param domain the domain in which the array will be cloned into + * \param array the array to clone + * \param error set on error + * This routine returns a copy of the array that is hosted on the + * specified \c MonoDomain. On failure returns NULL and sets \p error. + */ +MonoArrayHandle +mono_array_clone_in_domain (MonoDomain *domain, MonoArrayHandle array_handle, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoArrayHandle result = MONO_HANDLE_NEW (MonoArray, NULL); + uintptr_t size = 0; + MonoClass *klass = mono_handle_class (array_handle); + + error_init (error); + + /* Pin source array here - if bounds is non-NULL, it's a pointer into the object data */ + uint32_t src_handle = mono_gchandle_from_handle (MONO_HANDLE_CAST (MonoObject, array_handle), TRUE); + + MonoArrayBounds *array_bounds = MONO_HANDLE_GETVAL (array_handle, bounds); + MonoArrayHandle o; + if (array_bounds == NULL) { + size = mono_array_handle_length (array_handle); + o = mono_array_new_full_handle (domain, klass, &size, NULL, error); + goto_if_nok (error, leave); + size *= mono_array_element_size (klass); + } else { + uintptr_t *sizes = (uintptr_t *)alloca (klass->rank * sizeof (uintptr_t)); + intptr_t *lower_bounds = (intptr_t *)alloca (klass->rank * sizeof (intptr_t)); + size = mono_array_element_size (klass); + for (int i = 0; i < klass->rank; ++i) { + sizes [i] = array_bounds [i].length; + size *= array_bounds [i].length; + lower_bounds [i] = array_bounds [i].lower_bound; + } + o = mono_array_new_full_handle (domain, klass, sizes, lower_bounds, error); + goto_if_nok (error, leave); + } + + uint32_t dst_handle = mono_gchandle_from_handle (MONO_HANDLE_CAST (MonoObject, o), TRUE); + array_full_copy_unchecked_size (MONO_HANDLE_RAW (array_handle), MONO_HANDLE_RAW (o), klass, size); + mono_gchandle_free (dst_handle); + + MONO_HANDLE_ASSIGN (result, o); + +leave: + mono_gchandle_free (src_handle); + return result; +} + +/** + * mono_array_clone: + * \param array the array to clone + * \returns A newly created array who is a shallow copy of \p array + */ +MonoArray* +mono_array_clone (MonoArray *array) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoError error; + MonoArray *result = mono_array_clone_checked (array, &error); + mono_error_cleanup (&error); + return result; +} + +/** + * mono_array_clone_checked: + * \param array the array to clone + * \param error set on error + * \returns A newly created array who is a shallow copy of \p array. On + * failure returns NULL and sets \p error. + */ +MonoArray* +mono_array_clone_checked (MonoArray *array_raw, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + HANDLE_FUNCTION_ENTER (); + /* FIXME: callers of mono_array_clone_checked should use handles */ + error_init (error); + MONO_HANDLE_DCL (MonoArray, array); + MonoArrayHandle result = mono_array_clone_in_domain (MONO_HANDLE_DOMAIN (array), array, error); + HANDLE_FUNCTION_RETURN_OBJ (result); +} + +/* helper macros to check for overflow when calculating the size of arrays */ +#ifdef MONO_BIG_ARRAYS +#define MYGUINT64_MAX 0x0000FFFFFFFFFFFFUL +#define MYGUINT_MAX MYGUINT64_MAX +#define CHECK_ADD_OVERFLOW_UN(a,b) \ + (G_UNLIKELY ((guint64)(MYGUINT64_MAX) - (guint64)(b) < (guint64)(a))) +#define CHECK_MUL_OVERFLOW_UN(a,b) \ + (G_UNLIKELY (((guint64)(a) > 0) && ((guint64)(b) > 0) && \ + ((guint64)(b) > ((MYGUINT64_MAX) / (guint64)(a))))) +#else +#define MYGUINT32_MAX 4294967295U +#define MYGUINT_MAX MYGUINT32_MAX +#define CHECK_ADD_OVERFLOW_UN(a,b) \ + (G_UNLIKELY ((guint32)(MYGUINT32_MAX) - (guint32)(b) < (guint32)(a))) +#define CHECK_MUL_OVERFLOW_UN(a,b) \ + (G_UNLIKELY (((guint32)(a) > 0) && ((guint32)(b) > 0) && \ + ((guint32)(b) > ((MYGUINT32_MAX) / (guint32)(a))))) +#endif + +gboolean +mono_array_calc_byte_len (MonoClass *klass, uintptr_t len, uintptr_t *res) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + uintptr_t byte_len; + + byte_len = mono_array_element_size (klass); + if (CHECK_MUL_OVERFLOW_UN (byte_len, len)) + return FALSE; + byte_len *= len; + if (CHECK_ADD_OVERFLOW_UN (byte_len, MONO_SIZEOF_MONO_ARRAY)) + return FALSE; + byte_len += MONO_SIZEOF_MONO_ARRAY; + + *res = byte_len; + + return TRUE; +} + +/** + * mono_array_new_full: + * \param domain domain where the object is created + * \param array_class array class + * \param lengths lengths for each dimension in the array + * \param lower_bounds lower bounds for each dimension in the array (may be NULL) + * This routine creates a new array object with the given dimensions, + * lower bounds and type. + */ +MonoArray* +mono_array_new_full (MonoDomain *domain, MonoClass *array_class, uintptr_t *lengths, intptr_t *lower_bounds) +{ + MonoError error; + MonoArray *array = mono_array_new_full_checked (domain, array_class, lengths, lower_bounds, &error); + mono_error_cleanup (&error); + + return array; +} + +MonoArray* +mono_array_new_full_checked (MonoDomain *domain, MonoClass *array_class, uintptr_t *lengths, intptr_t *lower_bounds, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + uintptr_t byte_len = 0, len, bounds_size; + MonoObject *o; + MonoArray *array; + MonoArrayBounds *bounds; + MonoVTable *vtable; + int i; + + error_init (error); + + if (!array_class->inited) + mono_class_init (array_class); + + len = 1; + + /* A single dimensional array with a 0 lower bound is the same as an szarray */ + if (array_class->rank == 1 && ((array_class->byval_arg.type == MONO_TYPE_SZARRAY) || (lower_bounds && lower_bounds [0] == 0))) { + len = lengths [0]; + if (len > MONO_ARRAY_MAX_INDEX) { + mono_error_set_generic_error (error, "System", "OverflowException", ""); + return NULL; + } + bounds_size = 0; + } else { + bounds_size = sizeof (MonoArrayBounds) * array_class->rank; + + for (i = 0; i < array_class->rank; ++i) { + if (lengths [i] > MONO_ARRAY_MAX_INDEX) { + mono_error_set_generic_error (error, "System", "OverflowException", ""); + return NULL; + } + if (CHECK_MUL_OVERFLOW_UN (len, lengths [i])) { + mono_error_set_out_of_memory (error, "Could not allocate %i bytes", MONO_ARRAY_MAX_SIZE); + return NULL; + } + len *= lengths [i]; + } + } + + if (!mono_array_calc_byte_len (array_class, len, &byte_len)) { + mono_error_set_out_of_memory (error, "Could not allocate %i bytes", MONO_ARRAY_MAX_SIZE); + return NULL; + } + + if (bounds_size) { + /* align */ + if (CHECK_ADD_OVERFLOW_UN (byte_len, 3)) { + mono_error_set_out_of_memory (error, "Could not allocate %i bytes", MONO_ARRAY_MAX_SIZE); + return NULL; + } + byte_len = (byte_len + 3) & ~3; + if (CHECK_ADD_OVERFLOW_UN (byte_len, bounds_size)) { + mono_error_set_out_of_memory (error, "Could not allocate %i bytes", MONO_ARRAY_MAX_SIZE); + return NULL; + } + byte_len += bounds_size; + } + /* + * Following three lines almost taken from mono_object_new (): + * they need to be kept in sync. + */ + vtable = mono_class_vtable_full (domain, array_class, error); + return_val_if_nok (error, NULL); + + if (bounds_size) + o = (MonoObject *)mono_gc_alloc_array (vtable, byte_len, len, bounds_size); + else + o = (MonoObject *)mono_gc_alloc_vector (vtable, byte_len, len); + + if (G_UNLIKELY (!o)) { + mono_error_set_out_of_memory (error, "Could not allocate %zd bytes", (gsize) byte_len); + return NULL; + } + + array = (MonoArray*)o; + + bounds = array->bounds; + + if (bounds_size) { + for (i = 0; i < array_class->rank; ++i) { + bounds [i].length = lengths [i]; + if (lower_bounds) + bounds [i].lower_bound = lower_bounds [i]; + } + } + + return array; +} + +/** + * mono_array_new: + * \param domain domain where the object is created + * \param eclass element class + * \param n number of array elements + * This routine creates a new szarray with \p n elements of type \p eclass. + */ +MonoArray * +mono_array_new (MonoDomain *domain, MonoClass *eclass, uintptr_t n) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoError error; + MonoArray *result = mono_array_new_checked (domain, eclass, n, &error); + mono_error_cleanup (&error); + return result; +} + +/** + * mono_array_new_checked: + * \param domain domain where the object is created + * \param eclass element class + * \param n number of array elements + * \param error set on error + * This routine creates a new szarray with \p n elements of type \p eclass. + * On failure returns NULL and sets \p error. + */ +MonoArray * +mono_array_new_checked (MonoDomain *domain, MonoClass *eclass, uintptr_t n, MonoError *error) +{ + MonoClass *ac; + + error_init (error); + + ac = mono_array_class_get (eclass, 1); + g_assert (ac); + + MonoVTable *vtable = mono_class_vtable_full (domain, ac, error); + return_val_if_nok (error, NULL); + + return mono_array_new_specific_checked (vtable, n, error); +} + +MonoArray* +ves_icall_array_new (MonoDomain *domain, MonoClass *eclass, uintptr_t n) +{ + MonoError error; + MonoArray *arr = mono_array_new_checked (domain, eclass, n, &error); + mono_error_set_pending_exception (&error); + + return arr; +} + +/** + * mono_array_new_specific: + * \param vtable a vtable in the appropriate domain for an initialized class + * \param n number of array elements + * This routine is a fast alternative to \c mono_array_new for code which + * can be sure about the domain it operates in. + */ +MonoArray * +mono_array_new_specific (MonoVTable *vtable, uintptr_t n) +{ + MonoError error; + MonoArray *arr = mono_array_new_specific_checked (vtable, n, &error); + mono_error_cleanup (&error); + + return arr; +} + +MonoArray* +mono_array_new_specific_checked (MonoVTable *vtable, uintptr_t n, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoObject *o; + uintptr_t byte_len; + + error_init (error); + + if (G_UNLIKELY (n > MONO_ARRAY_MAX_INDEX)) { + mono_error_set_generic_error (error, "System", "OverflowException", ""); + return NULL; + } + + if (!mono_array_calc_byte_len (vtable->klass, n, &byte_len)) { + mono_error_set_out_of_memory (error, "Could not allocate %i bytes", MONO_ARRAY_MAX_SIZE); + return NULL; + } + o = (MonoObject *)mono_gc_alloc_vector (vtable, byte_len, n); + + if (G_UNLIKELY (!o)) { + mono_error_set_out_of_memory (error, "Could not allocate %zd bytes", (gsize) byte_len); + return NULL; + } + + return (MonoArray*)o; +} + +MonoArray* +ves_icall_array_new_specific (MonoVTable *vtable, uintptr_t n) +{ + MonoError error; + MonoArray *arr = mono_array_new_specific_checked (vtable, n, &error); + mono_error_set_pending_exception (&error); + + return arr; +} + +/** + * mono_string_empty_wrapper: + * + * Returns: The same empty string instance as the managed string.Empty + */ +MonoString* +mono_string_empty_wrapper (void) +{ + MonoDomain *domain = mono_domain_get (); + return mono_string_empty (domain); +} + +/** + * mono_string_empty: + * + * Returns: The same empty string instance as the managed string.Empty + */ +MonoString* +mono_string_empty (MonoDomain *domain) +{ + g_assert (domain); + g_assert (domain->empty_string); + return domain->empty_string; +} + +/** + * mono_string_new_utf16: + * \param text a pointer to an utf16 string + * \param len the length of the string + * \returns A newly created string object which contains \p text. + */ +MonoString * +mono_string_new_utf16 (MonoDomain *domain, const guint16 *text, gint32 len) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoError error; + MonoString *res = NULL; + res = mono_string_new_utf16_checked (domain, text, len, &error); + mono_error_cleanup (&error); + + return res; +} + +/** + * mono_string_new_utf16_checked: + * \param text a pointer to an utf16 string + * \param len the length of the string + * \param error written on error. + * \returns A newly created string object which contains \p text. + * On error, returns NULL and sets \p error. + */ +MonoString * +mono_string_new_utf16_checked (MonoDomain *domain, const guint16 *text, gint32 len, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoString *s; + + error_init (error); + + s = mono_string_new_size_checked (domain, len, error); + if (s != NULL) + memcpy (mono_string_chars (s), text, len * 2); + + return s; +} + +/** + * mono_string_new_utf16_handle: + * \param text a pointer to an utf16 string + * \param len the length of the string + * \param error written on error. + * \returns A newly created string object which contains \p text. + * On error, returns NULL and sets \p error. + */ +MonoStringHandle +mono_string_new_utf16_handle (MonoDomain *domain, const guint16 *text, gint32 len, MonoError *error) +{ + return MONO_HANDLE_NEW (MonoString, mono_string_new_utf16_checked (domain, text, len, error)); +} + +/** + * mono_string_new_utf32_checked: + * \param text a pointer to an utf32 string + * \param len the length of the string + * \param error set on failure. + * \returns A newly created string object which contains \p text. On failure returns NULL and sets \p error. + */ +static MonoString * +mono_string_new_utf32_checked (MonoDomain *domain, const mono_unichar4 *text, gint32 len, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoString *s; + mono_unichar2 *utf16_output = NULL; + gint32 utf16_len = 0; + GError *gerror = NULL; + glong items_written; + + error_init (error); + utf16_output = g_ucs4_to_utf16 (text, len, NULL, &items_written, &gerror); + + if (gerror) + g_error_free (gerror); + + while (utf16_output [utf16_len]) utf16_len++; + + s = mono_string_new_size_checked (domain, utf16_len, error); + return_val_if_nok (error, NULL); + + memcpy (mono_string_chars (s), utf16_output, utf16_len * 2); + + g_free (utf16_output); + + return s; +} + +/** + * mono_string_new_utf32: + * \param text a pointer to a UTF-32 string + * \param len the length of the string + * \returns A newly created string object which contains \p text. + */ +MonoString * +mono_string_new_utf32 (MonoDomain *domain, const mono_unichar4 *text, gint32 len) +{ + MonoError error; + MonoString *result = mono_string_new_utf32_checked (domain, text, len, &error); + mono_error_cleanup (&error); + return result; +} + +/** + * mono_string_new_size: + * \param text a pointer to a UTF-16 string + * \param len the length of the string + * \returns A newly created string object of \p len + */ +MonoString * +mono_string_new_size (MonoDomain *domain, gint32 len) +{ + MonoError error; + MonoString *str = mono_string_new_size_checked (domain, len, &error); + mono_error_cleanup (&error); + + return str; +} + +MonoString * +mono_string_new_size_checked (MonoDomain *domain, gint32 len, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoString *s; + MonoVTable *vtable; + size_t size; + + error_init (error); + + /* check for overflow */ + if (len < 0 || len > ((SIZE_MAX - G_STRUCT_OFFSET (MonoString, chars) - 8) / 2)) { + mono_error_set_out_of_memory (error, "Could not allocate %i bytes", -1); + return NULL; + } + + size = (G_STRUCT_OFFSET (MonoString, chars) + (((size_t)len + 1) * 2)); + g_assert (size > 0); + + vtable = mono_class_vtable (domain, mono_defaults.string_class); + g_assert (vtable); + + s = (MonoString *)mono_gc_alloc_string (vtable, size, len); + + if (G_UNLIKELY (!s)) { + mono_error_set_out_of_memory (error, "Could not allocate %zd bytes", size); + return NULL; + } + + return s; +} + +/** + * mono_string_new_len: + * \param text a pointer to an utf8 string + * \param length number of bytes in \p text to consider + * \returns A newly created string object which contains \p text. + */ +MonoString* +mono_string_new_len (MonoDomain *domain, const char *text, guint length) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoError error; + MonoString *result = mono_string_new_len_checked (domain, text, length, &error); + mono_error_cleanup (&error); + return result; +} + +/** + * mono_string_new_len_checked: + * \param text a pointer to an utf8 string + * \param length number of bytes in \p text to consider + * \param error set on error + * \returns A newly created string object which contains \p text. On + * failure returns NULL and sets \p error. + */ +MonoString* +mono_string_new_len_checked (MonoDomain *domain, const char *text, guint length, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + + GError *eg_error = NULL; + MonoString *o = NULL; + guint16 *ut = NULL; + glong items_written; + + ut = eg_utf8_to_utf16_with_nuls (text, length, NULL, &items_written, &eg_error); + + if (!eg_error) + o = mono_string_new_utf16_checked (domain, ut, items_written, error); + else + g_error_free (eg_error); + + g_free (ut); + + 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 + * \deprecated Use \c mono_string_new_checked in new code. + * This function asserts if it cannot allocate a new string. + * \returns A newly created string object which contains \p text. + */ +MonoString* +mono_string_new (MonoDomain *domain, const char *text) +{ + return mono_string_new_internal (domain, text); +} + +/** + * mono_string_new_checked: + * \param text a pointer to an utf8 string + * \param merror set on error + * \returns A newly created string object which contains \p text. + * On error returns NULL and sets \p merror. + */ +MonoString* +mono_string_new_checked (MonoDomain *domain, const char *text, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + GError *eg_error = NULL; + MonoString *o = NULL; + guint16 *ut; + glong items_written; + int l; + + error_init (error); + + l = strlen (text); + + ut = g_utf8_to_utf16 (text, l, NULL, &items_written, &eg_error); + + if (!eg_error) { + o = mono_string_new_utf16_checked (domain, ut, items_written, error); + } else { + mono_error_set_execution_engine (error, "String conversion error: %s", eg_error->message); + } + + g_free (ut); + +/*FIXME g_utf8_get_char, g_utf8_next_char and g_utf8_validate are not part of eglib.*/ +#if 0 + gunichar2 *str; + const gchar *end; + int len; + MonoString *o = NULL; + + if (!g_utf8_validate (text, -1, &end)) { + mono_error_set_argument (error, "text", "Not a valid utf8 string"); + goto leave; + } + + len = g_utf8_strlen (text, -1); + o = mono_string_new_size_checked (domain, len, error); + if (!o) + goto leave; + str = mono_string_chars (o); + + while (text < end) { + *str++ = g_utf8_get_char (text); + text = g_utf8_next_char (text); + } + +leave: +#endif + return o; +} + +/** + * mono_string_new_wrapper: + * \param text pointer to UTF-8 characters. + * Helper function to create a string object from \p text in the current domain. + */ +MonoString* +mono_string_new_wrapper (const char *text) +{ + MONO_REQ_GC_UNSAFE_MODE; + + return mono_string_new_internal (mono_domain_get (), text); +} + +/** + * mono_value_box: + * \param class the class of the value + * \param value a pointer to the unboxed data + * \returns A newly created object which contains \p value. + */ +MonoObject * +mono_value_box (MonoDomain *domain, MonoClass *klass, gpointer value) +{ + MonoError error; + MonoObject *result = mono_value_box_checked (domain, klass, value, &error); + mono_error_cleanup (&error); + return result; +} + +/** + * mono_value_box_checked: + * \param domain the domain of the new object + * \param class the class of the value + * \param value a pointer to the unboxed data + * \param error set on error + * \returns A newly created object which contains \p value. On failure + * returns NULL and sets \p error. + */ +MonoObject * +mono_value_box_checked (MonoDomain *domain, MonoClass *klass, gpointer value, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + MonoObject *res; + int size; + MonoVTable *vtable; + + error_init (error); + + g_assert (klass->valuetype); + if (mono_class_is_nullable (klass)) + return mono_nullable_box ((guint8 *)value, klass, error); + + vtable = mono_class_vtable (domain, klass); + if (!vtable) + return NULL; + size = mono_class_instance_size (klass); + res = mono_object_new_alloc_specific_checked (vtable, error); + return_val_if_nok (error, NULL); + + size = size - sizeof (MonoObject); + + 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 { +#if NO_UNALIGNED_ACCESS + mono_gc_memmove_atomic ((char *)res + sizeof (MonoObject), value, size); +#else + switch (size) { + case 1: + *((guint8 *) res + sizeof (MonoObject)) = *(guint8 *) value; + break; + case 2: + *(guint16 *)((guint8 *) res + sizeof (MonoObject)) = *(guint16 *) value; + break; + case 4: + *(guint32 *)((guint8 *) res + sizeof (MonoObject)) = *(guint32 *) value; + break; + case 8: + *(guint64 *)((guint8 *) res + sizeof (MonoObject)) = *(guint64 *) value; + break; + default: + mono_gc_memmove_atomic ((char *)res + sizeof (MonoObject), value, size); + } +#endif + } + if (klass->has_finalize) { + mono_object_register_finalizer (res); + return_val_if_nok (error, NULL); + } + return res; +} + +/** + * mono_value_copy: + * \param dest destination pointer + * \param src source pointer + * \param klass a valuetype class + * Copy a valuetype from \p src to \p dest. This function must be used + * when \p klass contains reference fields. + */ +void +mono_value_copy (gpointer dest, gpointer src, MonoClass *klass) +{ + MONO_REQ_GC_UNSAFE_MODE; + + mono_gc_wbarrier_value_copy (dest, src, 1, klass); +} + +/** + * mono_value_copy_array: + * \param dest destination array + * \param dest_idx index in the \p dest array + * \param src source pointer + * \param count number of items + * Copy \p count valuetype items from \p src to the array \p dest at index \p dest_idx. + * This function must be used when \p klass contains references fields. + * Overlap is handled. + */ +void +mono_value_copy_array (MonoArray *dest, int dest_idx, gpointer src, int count) +{ + MONO_REQ_GC_UNSAFE_MODE; + + int size = mono_array_element_size (dest->obj.vtable->klass); + char *d = mono_array_addr_with_size_fast (dest, size, dest_idx); + g_assert (size == mono_class_value_size (mono_object_class (dest)->element_class, NULL)); + mono_gc_wbarrier_value_copy (d, src, count, mono_object_class (dest)->element_class); +} + +MonoVTable * +mono_object_get_vtable (MonoObject *obj) +{ + // This could be called during STW, so untag the vtable if needed. + return mono_gc_get_vtable (obj); +} + +/** + * mono_object_get_domain: + * \param obj object to query + * \returns the \c MonoDomain where the object is hosted + */ +MonoDomain* +mono_object_get_domain (MonoObject *obj) +{ + MONO_REQ_GC_UNSAFE_MODE; + + return mono_object_domain (obj); +} + +/** + * mono_object_get_class: + * \param obj object to query + * Use this function to obtain the \c MonoClass* for a given \c MonoObject. + * \returns the \c MonoClass of the object. + */ +MonoClass* +mono_object_get_class (MonoObject *obj) +{ + MONO_REQ_GC_UNSAFE_MODE; + + return mono_object_class (obj); +} +/** + * mono_object_get_size: + * \param o object to query + * \returns the size, in bytes, of \p o + */ +guint +mono_object_get_size (MonoObject* o) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoClass* klass = mono_object_class (o); + if (klass == mono_defaults.string_class) { + return sizeof (MonoString) + 2 * mono_string_length ((MonoString*) o) + 2; + } else if (o->vtable->rank) { + MonoArray *array = (MonoArray*)o; + size_t size = MONO_SIZEOF_MONO_ARRAY + mono_array_element_size (klass) * mono_array_length (array); + if (array->bounds) { + size += 3; + size &= ~3; + size += sizeof (MonoArrayBounds) * o->vtable->rank; + } + return size; + } else { + return mono_class_instance_size (klass); + } +} + +/** + * mono_object_unbox: + * \param obj object to unbox + * \returns a pointer to the start of the valuetype boxed in this + * object. + * + * This method will assert if the object passed is not a valuetype. + */ +gpointer +mono_object_unbox (MonoObject *obj) +{ + MONO_REQ_GC_UNSAFE_MODE; + + /* add assert for valuetypes? */ + g_assert (obj->vtable->klass->valuetype); + return ((char*)obj) + sizeof (MonoObject); +} + +/** + * mono_object_isinst: + * \param obj an object + * \param klass a pointer to a class + * \returns \p obj if \p obj is derived from \p klass or NULL otherwise. + */ +MonoObject * +mono_object_isinst (MonoObject *obj_raw, MonoClass *klass) +{ + MONO_REQ_GC_UNSAFE_MODE; + + HANDLE_FUNCTION_ENTER (); + MONO_HANDLE_DCL (MonoObject, obj); + MonoError error; + MonoObjectHandle result = mono_object_handle_isinst (obj, klass, &error); + mono_error_cleanup (&error); + HANDLE_FUNCTION_RETURN_OBJ (result); +} + + +/** + * mono_object_isinst_checked: + * \param obj an object + * \param klass a pointer to a class + * \param error set on error + * \returns \p obj if \p obj is derived from \p klass or NULL if it isn't. + * On failure returns NULL and sets \p error. + */ +MonoObject * +mono_object_isinst_checked (MonoObject *obj_raw, MonoClass *klass, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + HANDLE_FUNCTION_ENTER (); + error_init (error); + MONO_HANDLE_DCL (MonoObject, obj); + MonoObjectHandle result = mono_object_handle_isinst (obj, klass, error); + HANDLE_FUNCTION_RETURN_OBJ (result); +} + +/** + * mono_object_handle_isinst: + * \param obj an object + * \param klass a pointer to a class + * \param error set on error + * \returns \p obj if \p obj is derived from \p klass or NULL if it isn't. + * On failure returns NULL and sets \p error. + */ +MonoObjectHandle +mono_object_handle_isinst (MonoObjectHandle obj, MonoClass *klass, MonoError *error) +{ + error_init (error); + + if (!klass->inited) + mono_class_init (klass); + + if (mono_class_is_marshalbyref (klass) || mono_class_is_interface (klass)) { + return mono_object_handle_isinst_mbyref (obj, klass, error); + } + + MonoObjectHandle result = MONO_HANDLE_NEW (MonoObject, NULL); + + if (!MONO_HANDLE_IS_NULL (obj) && mono_class_is_assignable_from (klass, mono_handle_class (obj))) + MONO_HANDLE_ASSIGN (result, obj); + return result; +} + +/** + * mono_object_isinst_mbyref: + */ +MonoObject * +mono_object_isinst_mbyref (MonoObject *obj_raw, MonoClass *klass) +{ + MONO_REQ_GC_UNSAFE_MODE; + + HANDLE_FUNCTION_ENTER (); + MonoError error; + MONO_HANDLE_DCL (MonoObject, obj); + MonoObjectHandle result = mono_object_handle_isinst_mbyref (obj, klass, &error); + mono_error_cleanup (&error); /* FIXME better API that doesn't swallow the error */ + HANDLE_FUNCTION_RETURN_OBJ (result); +} + +MonoObjectHandle +mono_object_handle_isinst_mbyref (MonoObjectHandle obj, MonoClass *klass, MonoError *error) +{ + error_init (error); + + MonoObjectHandle result = MONO_HANDLE_NEW (MonoObject, NULL); + + if (MONO_HANDLE_IS_NULL (obj)) + goto leave; + + MonoVTable *vt = MONO_HANDLE_GETVAL (obj, vtable); + + if (mono_class_is_interface (klass)) { + if (MONO_VTABLE_IMPLEMENTS_INTERFACE (vt, klass->interface_id)) { + MONO_HANDLE_ASSIGN (result, obj); + goto leave; + } + + /* casting an array one of the invariant interfaces that must act as such */ + if (klass->is_array_special_interface) { + if (mono_class_is_assignable_from (klass, vt->klass)) { + MONO_HANDLE_ASSIGN (result, obj); + goto leave; + } + } + + /*If the above check fails we are in the slow path of possibly raising an exception. So it's ok to it this way.*/ + else if (mono_class_has_variant_generic_params (klass) && mono_class_is_assignable_from (klass, mono_handle_class (obj))) { + MONO_HANDLE_ASSIGN (result, obj); + goto leave; + } + } else { + MonoClass *oklass = vt->klass; + if (mono_class_is_transparent_proxy (oklass)){ + MonoRemoteClass *remote_class = MONO_HANDLE_GETVAL (MONO_HANDLE_CAST (MonoTransparentProxy, obj), remote_class); + oklass = remote_class->proxy_class; + } + + mono_class_setup_supertypes (klass); + if ((oklass->idepth >= klass->idepth) && (oklass->supertypes [klass->idepth - 1] == klass)) { + MONO_HANDLE_ASSIGN (result, obj); + goto leave; + } + } +#ifndef DISABLE_REMOTING + if (mono_class_is_transparent_proxy (vt->klass)) + { + MonoBoolean custom_type_info = MONO_HANDLE_GETVAL (MONO_HANDLE_CAST (MonoTransparentProxy, obj), custom_type_info); + if (!custom_type_info) + goto leave; + MonoDomain *domain = mono_domain_get (); + MonoObjectHandle rp = MONO_HANDLE_NEW (MonoObject, NULL); + MONO_HANDLE_GET (rp, MONO_HANDLE_CAST (MonoTransparentProxy, obj), rp); + MonoClass *rpklass = mono_defaults.iremotingtypeinfo_class; + MonoMethod *im = NULL; + gpointer pa [2]; + + im = mono_class_get_method_from_name (rpklass, "CanCastTo", -1); + if (!im) { + mono_error_set_not_supported (error, "Linked away."); + goto leave; + } + im = mono_object_handle_get_virtual_method (rp, im, error); + goto_if_nok (error, leave); + g_assert (im); + + MonoReflectionTypeHandle reftype = mono_type_get_object_handle (domain, &klass->byval_arg, error); + goto_if_nok (error, leave); + + pa [0] = MONO_HANDLE_RAW (reftype); + pa [1] = MONO_HANDLE_RAW (obj); + MonoObject *res = mono_runtime_invoke_checked (im, MONO_HANDLE_RAW (rp), pa, error); + goto_if_nok (error, leave); + + if (*(MonoBoolean *) mono_object_unbox(res)) { + /* Update the vtable of the remote type, so it can safely cast to this new type */ + mono_upgrade_remote_class (domain, obj, klass, error); + goto_if_nok (error, leave); + MONO_HANDLE_ASSIGN (result, obj); + } + } +#endif /* DISABLE_REMOTING */ +leave: + return result; +} + +/** + * mono_object_castclass_mbyref: + * \param obj an object + * \param klass a pointer to a class + * \returns \p obj if \p obj is derived from \p klass, returns NULL otherwise. + */ +MonoObject * +mono_object_castclass_mbyref (MonoObject *obj_raw, MonoClass *klass) +{ + MONO_REQ_GC_UNSAFE_MODE; + HANDLE_FUNCTION_ENTER (); + MonoError error; + MONO_HANDLE_DCL (MonoObject, obj); + MonoObjectHandle result = MONO_HANDLE_NEW (MonoObject, NULL); + if (MONO_HANDLE_IS_NULL (obj)) + goto leave; + MONO_HANDLE_ASSIGN (result, mono_object_handle_isinst_mbyref (obj, klass, &error)); + mono_error_cleanup (&error); +leave: + HANDLE_FUNCTION_RETURN_OBJ (result); +} + +typedef struct { + MonoDomain *orig_domain; + MonoString *ins; + MonoString *res; +} LDStrInfo; + +static void +str_lookup (MonoDomain *domain, gpointer user_data) +{ + MONO_REQ_GC_UNSAFE_MODE; + + LDStrInfo *info = (LDStrInfo *)user_data; + if (info->res || domain == info->orig_domain) + return; + info->res = (MonoString *)mono_g_hash_table_lookup (domain->ldstr_table, info->ins); +} + +static MonoString* +mono_string_get_pinned (MonoString *str, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + + /* We only need to make a pinned version of a string if this is a moving GC */ + if (!mono_gc_is_moving ()) + return str; + int size; + MonoString *news; + size = sizeof (MonoString) + 2 * (mono_string_length (str) + 1); + news = (MonoString *)mono_gc_alloc_pinned_obj (((MonoObject*)str)->vtable, size); + if (news) { + memcpy (mono_string_chars (news), mono_string_chars (str), mono_string_length (str) * 2); + news->length = mono_string_length (str); + } else { + mono_error_set_out_of_memory (error, "Could not allocate %i bytes", size); + } + return news; +} + +static MonoString* +mono_string_is_interned_lookup (MonoString *str, int insert, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoGHashTable *ldstr_table; + MonoString *s, *res; + MonoDomain *domain; + + error_init (error); + + domain = ((MonoObject *)str)->vtable->domain; + ldstr_table = domain->ldstr_table; + ldstr_lock (); + res = (MonoString *)mono_g_hash_table_lookup (ldstr_table, str); + if (res) { + ldstr_unlock (); + return res; + } + if (insert) { + /* Allocate outside the lock */ + ldstr_unlock (); + s = mono_string_get_pinned (str, error); + return_val_if_nok (error, NULL); + if (s) { + ldstr_lock (); + res = (MonoString *)mono_g_hash_table_lookup (ldstr_table, str); + if (res) { + ldstr_unlock (); + return res; + } + mono_g_hash_table_insert (ldstr_table, s, s); + ldstr_unlock (); + } + return s; + } else { + LDStrInfo ldstr_info; + ldstr_info.orig_domain = domain; + ldstr_info.ins = str; + ldstr_info.res = NULL; + + mono_domain_foreach (str_lookup, &ldstr_info); + if (ldstr_info.res) { + /* + * the string was already interned in some other domain: + * intern it in the current one as well. + */ + mono_g_hash_table_insert (ldstr_table, str, str); + ldstr_unlock (); + return str; + } + } + ldstr_unlock (); + return NULL; +} + +/** + * mono_string_is_interned: + * \param o String to probe + * \returns Whether the string has been interned. + */ +MonoString* +mono_string_is_interned (MonoString *o) +{ + MonoError error; + MonoString *result = mono_string_is_interned_lookup (o, FALSE, &error); + /* This function does not fail. */ + mono_error_assert_ok (&error); + return result; +} + +/** + * mono_string_intern: + * \param o String to intern + * Interns the string passed. + * \returns The interned string. + */ +MonoString* +mono_string_intern (MonoString *str) +{ + MonoError error; + MonoString *result = mono_string_intern_checked (str, &error); + mono_error_assert_ok (&error); + return result; +} + +/** + * mono_string_intern_checked: + * \param o String to intern + * \param error set on error. + * Interns the string passed. + * \returns The interned string. On failure returns NULL and sets \p error + */ +MonoString* +mono_string_intern_checked (MonoString *str, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + + return mono_string_is_interned_lookup (str, TRUE, error); +} + +/** + * mono_ldstr: + * \param domain the domain where the string will be used. + * \param image a metadata context + * \param idx index into the user string table. + * Implementation for the \c ldstr opcode. + * \returns a loaded string from the \p image / \p idx combination. + */ +MonoString* +mono_ldstr (MonoDomain *domain, MonoImage *image, guint32 idx) +{ + MonoError error; + MonoString *result = mono_ldstr_checked (domain, image, idx, &error); + mono_error_cleanup (&error); + return result; +} + +/** + * mono_ldstr_checked: + * \param domain the domain where the string will be used. + * \param image a metadata context + * \param idx index into the user string table. + * \param error set on error. + * Implementation for the \c ldstr opcode. + * \returns A loaded string from the \p image / \p idx combination. + * On failure returns NULL and sets \p error. + */ +MonoString* +mono_ldstr_checked (MonoDomain *domain, MonoImage *image, guint32 idx, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + error_init (error); + + if (image->dynamic) { + MonoString *str = (MonoString *)mono_lookup_dynamic_token (image, MONO_TOKEN_STRING | idx, NULL, error); + return str; + } else { + if (!mono_verifier_verify_string_signature (image, idx, NULL)) + return NULL; /*FIXME we should probably be raising an exception here*/ + MonoString *str = mono_ldstr_metadata_sig (domain, mono_metadata_user_string (image, idx), error); + return str; + } +} + +/** + * mono_ldstr_metadata_sig + * \param domain the domain for the string + * \param sig the signature of a metadata string + * \param error set on error + * \returns a \c MonoString for a string stored in the metadata. On + * failure returns NULL and sets \p error. + */ +static MonoString* +mono_ldstr_metadata_sig (MonoDomain *domain, const char* sig, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + const char *str = sig; + MonoString *o, *interned; + size_t len2; + + len2 = mono_metadata_decode_blob_size (str, &str); + len2 >>= 1; + + o = mono_string_new_utf16_checked (domain, (guint16*)str, len2, error); + return_val_if_nok (error, NULL); +#if G_BYTE_ORDER != G_LITTLE_ENDIAN + { + int i; + guint16 *p2 = (guint16*)mono_string_chars (o); + for (i = 0; i < len2; ++i) { + *p2 = GUINT16_FROM_LE (*p2); + ++p2; + } + } +#endif + ldstr_lock (); + interned = (MonoString *)mono_g_hash_table_lookup (domain->ldstr_table, o); + ldstr_unlock (); + if (interned) + return interned; /* o will get garbage collected */ + + o = mono_string_get_pinned (o, error); + if (o) { + ldstr_lock (); + interned = (MonoString *)mono_g_hash_table_lookup (domain->ldstr_table, o); + if (!interned) { + mono_g_hash_table_insert (domain->ldstr_table, o, o); + interned = o; + } + ldstr_unlock (); + } + + return interned; +} + +/* + * mono_ldstr_utf8: + * + * Same as mono_ldstr, but return a NULL terminated utf8 string instead + * of an object. + */ +char* +mono_ldstr_utf8 (MonoImage *image, guint32 idx, MonoError *error) +{ + const char *str; + size_t len2; + long written = 0; + char *as; + GError *gerror = NULL; + + error_init (error); + + if (!mono_verifier_verify_string_signature (image, idx, NULL)) + return NULL; /*FIXME we should probably be raising an exception here*/ + str = mono_metadata_user_string (image, idx); + + len2 = mono_metadata_decode_blob_size (str, &str); + len2 >>= 1; + + as = g_utf16_to_utf8 ((guint16*)str, len2, NULL, &written, &gerror); + if (gerror) { + mono_error_set_argument (error, "string", "%s", gerror->message); + g_error_free (gerror); + return NULL; + } + /* g_utf16_to_utf8 may not be able to complete the convertion (e.g. NULL values were found, #335488) */ + if (len2 > written) { + /* allocate the total length and copy the part of the string that has been converted */ + char *as2 = (char *)g_malloc0 (len2); + memcpy (as2, as, written); + g_free (as); + as = as2; + } + + return as; +} + +/** + * mono_string_to_utf8: + * \param s a \c System.String + * \deprecated Use \c mono_string_to_utf8_checked to avoid having an exception arbitrarily raised. + * \returns the UTF-8 representation for \p s. + * The resulting buffer needs to be freed with \c mono_free(). + */ +char * +mono_string_to_utf8 (MonoString *s) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoError error; + char *result = mono_string_to_utf8_checked (s, &error); + + if (!is_ok (&error)) { + mono_error_cleanup (&error); + return NULL; + } + return result; +} + +/** + * mono_string_to_utf8_checked: + * \param s a \c System.String + * \param error a \c MonoError. + * Converts a \c MonoString to its UTF-8 representation. May fail; check + * \p error to determine whether the conversion was successful. + * The resulting buffer should be freed with \c mono_free(). + */ +char * +mono_string_to_utf8_checked (MonoString *s, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + long written = 0; + char *as; + GError *gerror = NULL; + + error_init (error); + + if (s == NULL) + return NULL; + + if (!s->length) + return g_strdup (""); + + as = g_utf16_to_utf8 (mono_string_chars (s), s->length, NULL, &written, &gerror); + if (gerror) { + mono_error_set_argument (error, "string", "%s", gerror->message); + g_error_free (gerror); + return NULL; + } + /* g_utf16_to_utf8 may not be able to complete the convertion (e.g. NULL values were found, #335488) */ + if (s->length > written) { + /* allocate the total length and copy the part of the string that has been converted */ + char *as2 = (char *)g_malloc0 (s->length); + memcpy (as2, as, written); + g_free (as); + as = as2; + } + + return as; +} + +char * +mono_string_handle_to_utf8 (MonoStringHandle s, MonoError *error) +{ + return mono_string_to_utf8_checked (MONO_HANDLE_RAW (s), error); +} + +/** + * mono_string_to_utf8_ignore: + * \param s a MonoString + * Converts a \c MonoString to its UTF-8 representation. Will ignore + * invalid surrogate pairs. + * The resulting buffer should be freed with \c mono_free(). + */ +char * +mono_string_to_utf8_ignore (MonoString *s) +{ + MONO_REQ_GC_UNSAFE_MODE; + + long written = 0; + char *as; + + if (s == NULL) + return NULL; + + if (!s->length) + return g_strdup (""); + + as = g_utf16_to_utf8 (mono_string_chars (s), s->length, NULL, &written, NULL); + + /* g_utf16_to_utf8 may not be able to complete the convertion (e.g. NULL values were found, #335488) */ + if (s->length > written) { + /* allocate the total length and copy the part of the string that has been converted */ + char *as2 = (char *)g_malloc0 (s->length); + memcpy (as2, as, written); + g_free (as); + as = as2; + } + + return as; +} + +/** + * mono_string_to_utf8_image_ignore: + * \param s a \c System.String + * Same as \c mono_string_to_utf8_ignore, but allocate the string from the image mempool. + */ +char * +mono_string_to_utf8_image_ignore (MonoImage *image, MonoString *s) +{ + MONO_REQ_GC_UNSAFE_MODE; + + return mono_string_to_utf8_internal (NULL, image, s, TRUE, NULL); +} + +/** + * mono_string_to_utf8_mp_ignore: + * \param s a \c System.String + * Same as \c mono_string_to_utf8_ignore, but allocate the string from a mempool. + */ +char * +mono_string_to_utf8_mp_ignore (MonoMemPool *mp, MonoString *s) +{ + MONO_REQ_GC_UNSAFE_MODE; + + return mono_string_to_utf8_internal (mp, NULL, s, TRUE, NULL); +} + + +/** + * mono_string_to_utf16: + * \param s a \c MonoString + * \returns a null-terminated array of the UTF-16 chars + * contained in \p s. The result must be freed with \c g_free(). + * This is a temporary helper until our string implementation + * is reworked to always include the null-terminating char. + */ +mono_unichar2* +mono_string_to_utf16 (MonoString *s) +{ + MONO_REQ_GC_UNSAFE_MODE; + + char *as; + + if (s == NULL) + return NULL; + + as = (char *)g_malloc ((s->length * 2) + 2); + as [(s->length * 2)] = '\0'; + as [(s->length * 2) + 1] = '\0'; + + if (!s->length) { + return (gunichar2 *)(as); + } + + memcpy (as, mono_string_chars(s), s->length * 2); + return (gunichar2 *)(as); +} + +/** + * mono_string_to_utf32: + * \param s a \c MonoString + * \returns a null-terminated array of the UTF-32 (UCS-4) chars + * contained in \p s. The result must be freed with \c g_free(). + */ +mono_unichar4* +mono_string_to_utf32 (MonoString *s) +{ + MONO_REQ_GC_UNSAFE_MODE; + + mono_unichar4 *utf32_output = NULL; + GError *error = NULL; + glong items_written; + + if (s == NULL) + return NULL; + + utf32_output = g_utf16_to_ucs4 (s->chars, s->length, NULL, &items_written, &error); + + if (error) + g_error_free (error); + + return utf32_output; +} + +/** + * mono_string_from_utf16: + * \param data the UTF-16 string (LPWSTR) to convert + * Converts a NULL-terminated UTF-16 string (LPWSTR) to a \c MonoString. + * \returns a \c MonoString. + */ +MonoString * +mono_string_from_utf16 (gunichar2 *data) +{ + MonoError error; + MonoString *result = mono_string_from_utf16_checked (data, &error); + mono_error_cleanup (&error); + return result; +} + +/** + * mono_string_from_utf16_checked: + * \param data the UTF-16 string (LPWSTR) to convert + * \param error set on error + * Converts a NULL-terminated UTF-16 string (LPWSTR) to a \c MonoString. + * \returns a \c MonoString. On failure sets \p error and returns NULL. + */ +MonoString * +mono_string_from_utf16_checked (gunichar2 *data, MonoError *error) +{ + + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + MonoDomain *domain = mono_domain_get (); + int len = 0; + + if (!data) + return NULL; + + while (data [len]) len++; + + return mono_string_new_utf16_checked (domain, data, len, error); +} + +/** + * mono_string_from_utf32: + * \param data the UTF-32 string (LPWSTR) to convert + * Converts a UTF-32 (UCS-4) string to a \c MonoString. + * \returns a \c MonoString. + */ +MonoString * +mono_string_from_utf32 (mono_unichar4 *data) +{ + MonoError error; + MonoString *result = mono_string_from_utf32_checked (data, &error); + mono_error_cleanup (&error); + return result; +} + +/** + * mono_string_from_utf32_checked: + * \param data the UTF-32 string (LPWSTR) to convert + * \param error set on error + * Converts a UTF-32 (UCS-4) string to a \c MonoString. + * \returns a \c MonoString. On failure returns NULL and sets \p error. + */ +MonoString * +mono_string_from_utf32_checked (mono_unichar4 *data, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + MonoString* result = NULL; + mono_unichar2 *utf16_output = NULL; + GError *gerror = NULL; + glong items_written; + int len = 0; + + if (!data) + return NULL; + + while (data [len]) len++; + + utf16_output = g_ucs4_to_utf16 (data, len, NULL, &items_written, &gerror); + + if (gerror) + g_error_free (gerror); + + result = mono_string_from_utf16_checked (utf16_output, error); + g_free (utf16_output); + return result; +} + +static char * +mono_string_to_utf8_internal (MonoMemPool *mp, MonoImage *image, MonoString *s, gboolean ignore_error, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + char *r; + char *mp_s; + int len; + + if (ignore_error) { + r = mono_string_to_utf8_ignore (s); + } else { + r = mono_string_to_utf8_checked (s, error); + if (!mono_error_ok (error)) + return NULL; + } + + if (!mp && !image) + return r; + + len = strlen (r) + 1; + if (mp) + mp_s = (char *)mono_mempool_alloc (mp, len); + else + mp_s = (char *)mono_image_alloc (image, len); + + memcpy (mp_s, r, len); + + g_free (r); + + return mp_s; +} + +/** + * mono_string_to_utf8_image: + * \param s a \c System.String + * Same as \c mono_string_to_utf8, but allocate the string from the image mempool. + */ +char * +mono_string_to_utf8_image (MonoImage *image, MonoStringHandle s, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + return mono_string_to_utf8_internal (NULL, image, MONO_HANDLE_RAW (s), FALSE, error); /* FIXME pin the string */ +} + +/** + * mono_string_to_utf8_mp: + * \param s a \c System.String + * Same as \c mono_string_to_utf8, but allocate the string from a mempool. + */ +char * +mono_string_to_utf8_mp (MonoMemPool *mp, MonoString *s, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + return mono_string_to_utf8_internal (mp, NULL, s, FALSE, error); +} + + +static MonoRuntimeExceptionHandlingCallbacks eh_callbacks; + +void +mono_install_eh_callbacks (MonoRuntimeExceptionHandlingCallbacks *cbs) +{ + eh_callbacks = *cbs; +} + +MonoRuntimeExceptionHandlingCallbacks * +mono_get_eh_callbacks (void) +{ + return &eh_callbacks; +} + +/** + * mono_raise_exception: + * \param ex exception object + * Signal the runtime that the exception \p ex has been raised in unmanaged code. + * DEPRECATED. DO NOT ADD NEW CALLERS FOR THIS FUNCTION. + */ +void +mono_raise_exception (MonoException *ex) +{ + mono_raise_exception_deprecated (ex); +} + +/* + * DEPRECATED. DO NOT ADD NEW CALLERS FOR THIS FUNCTION. + */ +void +mono_raise_exception_deprecated (MonoException *ex) +{ + MONO_REQ_GC_UNSAFE_MODE; + + eh_callbacks.mono_raise_exception (ex); +} + +/** + * mono_reraise_exception: + * \param ex exception object + * Signal the runtime that the exception \p ex has been raised in unmanaged code. + * DEPRECATED. DO NOT ADD NEW CALLERS FOR THIS FUNCTION. + */ +void +mono_reraise_exception (MonoException *ex) +{ + mono_reraise_exception_deprecated (ex); +} + +/* + * DEPRECATED. DO NOT ADD NEW CALLERS FOR THIS FUNCTION. + */ +void +mono_reraise_exception_deprecated (MonoException *ex) +{ + MONO_REQ_GC_UNSAFE_MODE; + + eh_callbacks.mono_reraise_exception (ex); +} + +/* + * CTX must point to managed code. + */ +void +mono_raise_exception_with_context (MonoException *ex, MonoContext *ctx) +{ + MONO_REQ_GC_UNSAFE_MODE; + + eh_callbacks.mono_raise_exception_with_ctx (ex, ctx); +} + +/** + * mono_wait_handle_new: + * \param domain Domain where the object will be created + * \param handle Handle for the wait handle + * \param error set on error. + * \returns A new \c MonoWaitHandle created in the given domain for the + * given handle. On failure returns NULL and sets \p error. + */ +MonoWaitHandle * +mono_wait_handle_new (MonoDomain *domain, HANDLE handle, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoWaitHandle *res; + gpointer params [1]; + static MonoMethod *handle_set; + + error_init (error); + res = (MonoWaitHandle *)mono_object_new_checked (domain, mono_defaults.manualresetevent_class, error); + return_val_if_nok (error, NULL); + + /* Even though this method is virtual, it's safe to invoke directly, since the object type matches. */ + if (!handle_set) + handle_set = mono_class_get_property_from_name (mono_defaults.manualresetevent_class, "Handle")->set; + + params [0] = &handle; + + mono_runtime_invoke_checked (handle_set, res, params, error); + return res; +} + +HANDLE +mono_wait_handle_get_handle (MonoWaitHandle *handle) +{ + MONO_REQ_GC_UNSAFE_MODE; + + static MonoClassField *f_safe_handle = NULL; + MonoSafeHandle *sh; + + if (!f_safe_handle) { + f_safe_handle = mono_class_get_field_from_name (mono_defaults.manualresetevent_class, "safeWaitHandle"); + g_assert (f_safe_handle); + } + + mono_field_get_value ((MonoObject*)handle, f_safe_handle, &sh); + return sh->handle; +} + + +static MonoObject* +mono_runtime_capture_context (MonoDomain *domain, MonoError *error) +{ +#ifdef HOST_WASM + return mono_runtime_invoke_checked (mono_get_context_capture_method (), NULL, NULL, error); +#else + MONO_REQ_GC_UNSAFE_MODE; + + RuntimeInvokeFunction runtime_invoke; + + error_init (error); + + if (!domain->capture_context_runtime_invoke || !domain->capture_context_method) { + MonoMethod *method = mono_get_context_capture_method (); + MonoMethod *wrapper; + if (!method) + return NULL; + wrapper = mono_marshal_get_runtime_invoke (method, FALSE); + domain->capture_context_runtime_invoke = mono_compile_method_checked (wrapper, error); + return_val_if_nok (error, NULL); + domain->capture_context_method = mono_compile_method_checked (method, error); + return_val_if_nok (error, NULL); + } + + runtime_invoke = (RuntimeInvokeFunction)domain->capture_context_runtime_invoke; + + return runtime_invoke (NULL, NULL, NULL, domain->capture_context_method); +#endif +} +/** + * mono_async_result_new: + * \param domain domain where the object will be created. + * \param handle wait handle. + * \param state state to pass to AsyncResult + * \param data C closure data. + * \param error set on error. + * Creates a new MonoAsyncResult (\c AsyncResult C# class) in the given domain. + * If the handle is not null, the handle is initialized to a \c MonoWaitHandle. + * On failure returns NULL and sets \p error. + */ +MonoAsyncResult * +mono_async_result_new (MonoDomain *domain, HANDLE handle, MonoObject *state, gpointer data, MonoObject *object_data, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + MonoAsyncResult *res = (MonoAsyncResult *)mono_object_new_checked (domain, mono_defaults.asyncresult_class, error); + return_val_if_nok (error, NULL); + MonoObject *context = mono_runtime_capture_context (domain, error); + return_val_if_nok (error, NULL); + /* we must capture the execution context from the original thread */ + if (context) { + MONO_OBJECT_SETREF (res, execution_context, context); + /* note: result may be null if the flow is suppressed */ + } + + res->data = (void **)data; + MONO_OBJECT_SETREF (res, object_data, object_data); + MONO_OBJECT_SETREF (res, async_state, state); + MonoWaitHandle *wait_handle = mono_wait_handle_new (domain, handle, error); + return_val_if_nok (error, NULL); + if (handle != NULL) + MONO_OBJECT_SETREF (res, handle, (MonoObject *) wait_handle); + + res->sync_completed = FALSE; + res->completed = FALSE; + + return res; +} + +MonoObject * +ves_icall_System_Runtime_Remoting_Messaging_AsyncResult_Invoke (MonoAsyncResult *ares) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoError error; + MonoAsyncCall *ac; + MonoObject *res; + + g_assert (ares); + g_assert (ares->async_delegate); + + ac = (MonoAsyncCall*) ares->object_data; + if (!ac) { + res = mono_runtime_delegate_invoke_checked (ares->async_delegate, (void**) &ares->async_state, &error); + if (mono_error_set_pending_exception (&error)) + return NULL; + } else { + gpointer wait_event = NULL; + + ac->msg->exc = NULL; + + res = mono_message_invoke (ares->async_delegate, ac->msg, &ac->msg->exc, &ac->out_args, &error); + + /* The exit side of the invoke must not be aborted as it would leave the runtime in an undefined state */ + mono_threads_begin_abort_protected_block (); + + if (!ac->msg->exc) { + MonoException *ex = mono_error_convert_to_exception (&error); + ac->msg->exc = (MonoObject *)ex; + } else { + mono_error_cleanup (&error); + } + + MONO_OBJECT_SETREF (ac, res, res); + + mono_monitor_enter ((MonoObject*) ares); + ares->completed = 1; + if (ares->handle) + wait_event = mono_wait_handle_get_handle ((MonoWaitHandle*) ares->handle); + mono_monitor_exit ((MonoObject*) ares); + + if (wait_event != NULL) + mono_w32event_set (wait_event); + + error_init (&error); //the else branch would leave it in an undefined state + if (ac->cb_method) + mono_runtime_invoke_checked (ac->cb_method, ac->cb_target, (gpointer*) &ares, &error); + + mono_threads_end_abort_protected_block (); + + if (mono_error_set_pending_exception (&error)) + return NULL; + } + + return res; +} + +gboolean +mono_message_init (MonoDomain *domain, + MonoMethodMessage *this_obj, + MonoReflectionMethod *method, + MonoArray *out_args, + MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + static MonoMethod *init_message_method = NULL; + + if (!init_message_method) { + init_message_method = mono_class_get_method_from_name (mono_defaults.mono_method_message_class, "InitMessage", 2); + g_assert (init_message_method != NULL); + } + + error_init (error); + /* FIXME set domain instead? */ + g_assert (domain == mono_domain_get ()); + + gpointer args[2]; + + args[0] = method; + args[1] = out_args; + + mono_runtime_invoke_checked (init_message_method, this_obj, args, error); + return is_ok (error); +} + +#ifndef DISABLE_REMOTING +/** + * mono_remoting_invoke: + * \param real_proxy pointer to a \c RealProxy object + * \param msg The \c MonoMethodMessage to execute + * \param exc used to store exceptions + * \param out_args used to store output arguments + * This is used to call \c RealProxy::Invoke(). \c RealProxy::Invoke() returns an + * \c IMessage interface and it is not trivial to extract results from there. So + * we call an helper method \c PrivateInvoke instead of calling + * \c RealProxy::Invoke() directly. + * \returns the result object. + */ +MonoObject * +mono_remoting_invoke (MonoObject *real_proxy, MonoMethodMessage *msg, MonoObject **exc, MonoArray **out_args, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoObject *o; + MonoMethod *im = real_proxy->vtable->domain->private_invoke_method; + gpointer pa [4]; + + g_assert (exc); + + error_init (error); + + /*static MonoObject *(*invoke) (gpointer, gpointer, MonoObject **, MonoArray **) = NULL;*/ + + if (!im) { + im = mono_class_get_method_from_name (mono_defaults.real_proxy_class, "PrivateInvoke", 4); + if (!im) { + mono_error_set_not_supported (error, "Linked away."); + return NULL; + } + real_proxy->vtable->domain->private_invoke_method = im; + } + + pa [0] = real_proxy; + pa [1] = msg; + pa [2] = exc; + pa [3] = out_args; + + o = mono_runtime_try_invoke (im, NULL, pa, exc, error); + return_val_if_nok (error, NULL); + + return o; +} +#endif + +MonoObject * +mono_message_invoke (MonoObject *target, MonoMethodMessage *msg, + MonoObject **exc, MonoArray **out_args, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + static MonoClass *object_array_klass; + error_init (error); + + MonoDomain *domain; + MonoMethod *method; + MonoMethodSignature *sig; + MonoArray *arr; + int i, j, outarg_count = 0; + +#ifndef DISABLE_REMOTING + if (target && mono_object_is_transparent_proxy (target)) { + MonoTransparentProxy* tp = (MonoTransparentProxy *)target; + if (mono_class_is_contextbound (tp->remote_class->proxy_class) && tp->rp->context == (MonoObject *) mono_context_get ()) { + target = tp->rp->unwrapped_server; + } else { + return mono_remoting_invoke ((MonoObject *)tp->rp, msg, exc, out_args, error); + } + } +#endif + + domain = mono_domain_get (); + method = msg->method->method; + sig = mono_method_signature (method); + + for (i = 0; i < sig->param_count; i++) { + if (sig->params [i]->byref) + outarg_count++; + } + + if (!object_array_klass) { + MonoClass *klass; + + klass = mono_array_class_get (mono_defaults.object_class, 1); + g_assert (klass); + + mono_memory_barrier (); + object_array_klass = klass; + } + + arr = mono_array_new_specific_checked (mono_class_vtable (domain, object_array_klass), outarg_count, error); + return_val_if_nok (error, NULL); + + mono_gc_wbarrier_generic_store (out_args, (MonoObject*) arr); + *exc = NULL; + + MonoObject *ret = mono_runtime_try_invoke_array (method, method->klass->valuetype? mono_object_unbox (target): target, msg->args, exc, error); + return_val_if_nok (error, NULL); + + for (i = 0, j = 0; i < sig->param_count; i++) { + if (sig->params [i]->byref) { + MonoObject* arg; + arg = (MonoObject *)mono_array_get (msg->args, gpointer, i); + mono_array_setref (*out_args, j, arg); + j++; + } + } + + return ret; +} + +/** + * prepare_to_string_method: + * @obj: The object + * @target: Set to @obj or unboxed value if a valuetype + * + * Returns: the ToString override for @obj. If @obj is a valuetype, @target is unboxed otherwise it's @obj. + */ +static MonoMethod * +prepare_to_string_method (MonoObject *obj, void **target) +{ + MONO_REQ_GC_UNSAFE_MODE; + + static MonoMethod *to_string = NULL; + MonoMethod *method; + g_assert (target); + g_assert (obj); + + *target = obj; + + if (!to_string) + to_string = mono_class_get_method_from_name_flags (mono_get_object_class (), "ToString", 0, METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_PUBLIC); + + method = mono_object_get_virtual_method (obj, to_string); + + // Unbox value type if needed + if (mono_class_is_valuetype (mono_method_get_class (method))) { + *target = mono_object_unbox (obj); + } + return method; +} + +/** + * mono_object_to_string: + * \param obj The object + * \param exc Any exception thrown by \c ToString. May be NULL. + * \returns the result of calling \c ToString on an object. + */ +MonoString * +mono_object_to_string (MonoObject *obj, MonoObject **exc) +{ + MonoError error; + MonoString *s = NULL; + void *target; + MonoMethod *method = prepare_to_string_method (obj, &target); + if (exc) { + s = (MonoString *) mono_runtime_try_invoke (method, target, NULL, exc, &error); + if (*exc == NULL && !mono_error_ok (&error)) + *exc = (MonoObject*) mono_error_convert_to_exception (&error); + else + mono_error_cleanup (&error); + } else { + s = (MonoString *) mono_runtime_invoke_checked (method, target, NULL, &error); + mono_error_raise_exception_deprecated (&error); /* OK to throw, external only without a good alternative */ + } + + return s; +} + +/** + * mono_object_to_string_checked: + * \param obj The object + * \param error Set on error. + * \returns the result of calling \c ToString() on an object. If the + * method cannot be invoked or if it raises an exception, sets \p error + * and returns NULL. + */ +MonoString * +mono_object_to_string_checked (MonoObject *obj, MonoError *error) +{ + error_init (error); + void *target; + MonoMethod *method = prepare_to_string_method (obj, &target); + return (MonoString*) mono_runtime_invoke_checked (method, target, NULL, error); +} + +/** + * mono_object_try_to_string: + * \param obj The object + * \param exc Any exception thrown by \c ToString(). Must not be NULL. + * \param error Set if method cannot be invoked. + * \returns the result of calling \c ToString() on an object. If the + * method cannot be invoked sets \p error, if it raises an exception sets \p exc, + * and returns NULL. + */ +MonoString * +mono_object_try_to_string (MonoObject *obj, MonoObject **exc, MonoError *error) +{ + g_assert (exc); + error_init (error); + void *target; + MonoMethod *method = prepare_to_string_method (obj, &target); + return (MonoString*) mono_runtime_try_invoke (method, target, NULL, exc, error); +} + + + +static char * +get_native_backtrace (MonoException *exc_raw) +{ + HANDLE_FUNCTION_ENTER (); + MONO_HANDLE_DCL(MonoException, exc); + char * trace = mono_exception_handle_get_native_backtrace (exc); + HANDLE_FUNCTION_RETURN_VAL (trace); +} + +/** + * mono_print_unhandled_exception: + * \param exc The exception + * Prints the unhandled exception. + */ +void +mono_print_unhandled_exception (MonoObject *exc) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoString * str; + char *message = (char*)""; + gboolean free_message = FALSE; + MonoError error; + + if (exc == (MonoObject*)mono_object_domain (exc)->out_of_memory_ex) { + message = g_strdup ("OutOfMemoryException"); + free_message = TRUE; + } else if (exc == (MonoObject*)mono_object_domain (exc)->stack_overflow_ex) { + message = g_strdup ("StackOverflowException"); //if we OVF, we can't expect to have stack space to JIT Exception::ToString. + free_message = TRUE; + } else { + + if (((MonoException*)exc)->native_trace_ips) { + message = get_native_backtrace ((MonoException*)exc); + free_message = TRUE; + } else { + MonoObject *other_exc = NULL; + str = mono_object_try_to_string (exc, &other_exc, &error); + if (other_exc == NULL && !is_ok (&error)) + other_exc = (MonoObject*)mono_error_convert_to_exception (&error); + else + mono_error_cleanup (&error); + if (other_exc) { + char *original_backtrace = mono_exception_get_managed_backtrace ((MonoException*)exc); + char *nested_backtrace = mono_exception_get_managed_backtrace ((MonoException*)other_exc); + + message = g_strdup_printf ("Nested exception detected.\nOriginal Exception: %s\nNested exception:%s\n", + original_backtrace, nested_backtrace); + + g_free (original_backtrace); + g_free (nested_backtrace); + free_message = TRUE; + } else if (str) { + message = mono_string_to_utf8_checked (str, &error); + if (!mono_error_ok (&error)) { + mono_error_cleanup (&error); + message = (char *) ""; + } else { + free_message = TRUE; + } + } + } + } + + /* + * g_printerr ("\nUnhandled Exception: %s.%s: %s\n", exc->vtable->klass->name_space, + * exc->vtable->klass->name, message); + */ + g_printerr ("\nUnhandled Exception:\n%s\n", message); + + if (free_message) + g_free (message); +} + +/** + * mono_delegate_ctor_with_method: + * \param this pointer to an uninitialized delegate object + * \param target target object + * \param addr pointer to native code + * \param method method + * \param error set on error. + * Initialize a delegate and sets a specific method, not the one + * associated with \p addr. This is useful when sharing generic code. + * In that case \p addr will most probably not be associated with the + * correct instantiation of the method. + * On failure returns FALSE and sets \p error. + */ +gboolean +mono_delegate_ctor_with_method (MonoObjectHandle this_obj, MonoObjectHandle target, gpointer addr, MonoMethod *method, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + MonoDelegateHandle delegate = MONO_HANDLE_CAST (MonoDelegate, this_obj); + + g_assert (!MONO_HANDLE_IS_NULL (this_obj)); + g_assert (addr); + + MonoClass *klass = mono_handle_class (this_obj); + g_assert (mono_class_has_parent (klass, mono_defaults.multicastdelegate_class)); + + if (method) + MONO_HANDLE_SETVAL (delegate, method, MonoMethod*, method); + + UnlockedIncrement (&mono_stats.delegate_creations); + +#ifndef DISABLE_REMOTING + if (!MONO_HANDLE_IS_NULL (target) && mono_class_is_transparent_proxy (mono_handle_class (target))) { + if (callbacks.interp_get_remoting_invoke) { + MONO_HANDLE_SETVAL (delegate, method_ptr, gpointer, callbacks.interp_get_remoting_invoke (addr, error)); + } else { + g_assert (method); + method = mono_marshal_get_remoting_invoke (method); + MONO_HANDLE_SETVAL (delegate, method_ptr, gpointer, mono_compile_method_checked (method, error)); + } + return_val_if_nok (error, FALSE); + MONO_HANDLE_SET (delegate, target, target); + } else +#endif + { + MONO_HANDLE_SETVAL (delegate, method_ptr, gpointer, addr); + MONO_HANDLE_SET (delegate, target, target); + } + + MONO_HANDLE_SETVAL (delegate, invoke_impl, gpointer, callbacks.create_delegate_trampoline (MONO_HANDLE_DOMAIN (delegate), mono_handle_class (delegate))); + if (callbacks.init_delegate) + callbacks.init_delegate (MONO_HANDLE_RAW (delegate)); /* FIXME: update init_delegate callback to take a MonoDelegateHandle */ + return TRUE; +} + +/** + * mono_delegate_ctor: + * \param this pointer to an uninitialized delegate object + * \param target target object + * \param addr pointer to native code + * \param error set on error. + * This is used to initialize a delegate. + * On failure returns FALSE and sets \p error. + */ +gboolean +mono_delegate_ctor (MonoObjectHandle this_obj, MonoObjectHandle target, gpointer addr, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + MonoDomain *domain = mono_domain_get (); + MonoJitInfo *ji; + MonoMethod *method = NULL; + + g_assert (addr); + + ji = mono_jit_info_table_find (domain, (char *)mono_get_addr_from_ftnptr (addr)); + /* Shared code */ + if (!ji && domain != mono_get_root_domain ()) + ji = mono_jit_info_table_find (mono_get_root_domain (), (char *)mono_get_addr_from_ftnptr (addr)); + if (ji) { + method = mono_jit_info_get_method (ji); + g_assert (!mono_class_is_gtd (method->klass)); + } + + return mono_delegate_ctor_with_method (this_obj, target, addr, method, error); +} + +/** + * mono_method_call_message_new: + * \param method method to encapsulate + * \param params parameters to the method + * \param invoke optional, delegate invoke. + * \param cb async callback delegate. + * \param state state passed to the async callback. + * \param error set on error. + * Translates arguments pointers into a \c MonoMethodMessage. + * On failure returns NULL and sets \p error. + */ +MonoMethodMessage * +mono_method_call_message_new (MonoMethod *method, gpointer *params, MonoMethod *invoke, + MonoDelegate **cb, MonoObject **state, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + + MonoDomain *domain = mono_domain_get (); + MonoMethodSignature *sig = mono_method_signature (method); + MonoMethodMessage *msg; + int i, count; + + msg = (MonoMethodMessage *)mono_object_new_checked (domain, mono_defaults.mono_method_message_class, error); + return_val_if_nok (error, NULL); + + if (invoke) { + MonoReflectionMethod *rm = mono_method_get_object_checked (domain, invoke, NULL, error); + return_val_if_nok (error, NULL); + mono_message_init (domain, msg, rm, NULL, error); + return_val_if_nok (error, NULL); + count = sig->param_count - 2; + } else { + MonoReflectionMethod *rm = mono_method_get_object_checked (domain, method, NULL, error); + return_val_if_nok (error, NULL); + mono_message_init (domain, msg, rm, NULL, error); + return_val_if_nok (error, NULL); + count = sig->param_count; + } + + for (i = 0; i < count; i++) { + gpointer vpos; + MonoClass *klass; + MonoObject *arg; + + if (sig->params [i]->byref) + vpos = *((gpointer *)params [i]); + else + vpos = params [i]; + + klass = mono_class_from_mono_type (sig->params [i]); + + if (klass->valuetype) { + arg = mono_value_box_checked (domain, klass, vpos, error); + return_val_if_nok (error, NULL); + } else + arg = *((MonoObject **)vpos); + + mono_array_setref (msg->args, i, arg); + } + + if (cb != NULL && state != NULL) { + *cb = *((MonoDelegate **)params [i]); + i++; + *state = *((MonoObject **)params [i]); + } + + return msg; +} + +/** + * mono_method_return_message_restore: + * + * Restore results from message based processing back to arguments pointers + */ +void +mono_method_return_message_restore (MonoMethod *method, gpointer *params, MonoArray *out_args, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + + MonoMethodSignature *sig = mono_method_signature (method); + int i, j, type, size, out_len; + + if (out_args == NULL) + return; + out_len = mono_array_length (out_args); + if (out_len == 0) + return; + + for (i = 0, j = 0; i < sig->param_count; i++) { + MonoType *pt = sig->params [i]; + + if (pt->byref) { + char *arg; + if (j >= out_len) { + mono_error_set_execution_engine (error, "The proxy call returned an incorrect number of output arguments"); + return; + } + + arg = (char *)mono_array_get (out_args, gpointer, j); + type = pt->type; + + g_assert (type != MONO_TYPE_VOID); + + if (MONO_TYPE_IS_REFERENCE (pt)) { + mono_gc_wbarrier_generic_store (*((MonoObject ***)params [i]), (MonoObject *)arg); + } else { + if (arg) { + MonoClass *klass = ((MonoObject*)arg)->vtable->klass; + size = mono_class_value_size (klass, NULL); + if (klass->has_references) + mono_gc_wbarrier_value_copy (*((gpointer *)params [i]), arg + sizeof (MonoObject), 1, klass); + else + mono_gc_memmove_atomic (*((gpointer *)params [i]), arg + sizeof (MonoObject), size); + } else { + size = mono_class_value_size (mono_class_from_mono_type (pt), NULL); + mono_gc_bzero_atomic (*((gpointer *)params [i]), size); + } + } + + j++; + } + } +} + +#ifndef DISABLE_REMOTING + +/** + * mono_load_remote_field: + * \param this pointer to an object + * \param klass klass of the object containing \p field + * \param field the field to load + * \param res a storage to store the result + * This method is called by the runtime on attempts to load fields of + * transparent proxy objects. \p this points to such TP, \p klass is the class of + * the object containing \p field. \p res is a storage location which can be + * used to store the result. + * \returns an address pointing to the value of field. + */ +gpointer +mono_load_remote_field (MonoObject *this_obj, MonoClass *klass, MonoClassField *field, gpointer *res) +{ + MonoError error; + gpointer result = mono_load_remote_field_checked (this_obj, klass, field, res, &error); + mono_error_cleanup (&error); + return result; +} + +/** + * mono_load_remote_field_checked: + * \param this pointer to an object + * \param klass klass of the object containing \p field + * \param field the field to load + * \param res a storage to store the result + * \param error set on error + * This method is called by the runtime on attempts to load fields of + * transparent proxy objects. \p this points to such TP, \p klass is the class of + * the object containing \p field. \p res is a storage location which can be + * used to store the result. + * \returns an address pointing to the value of field. On failure returns NULL and sets \p error. + */ +gpointer +mono_load_remote_field_checked (MonoObject *this_obj, MonoClass *klass, MonoClassField *field, gpointer *res, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + static MonoMethod *getter = NULL; + + error_init (error); + + MonoDomain *domain = mono_domain_get (); + MonoTransparentProxy *tp = (MonoTransparentProxy *) this_obj; + MonoClass *field_class; + MonoMethodMessage *msg; + MonoArray *out_args; + MonoObject *exc; + char* full_name; + + g_assert (mono_object_is_transparent_proxy (this_obj)); + g_assert (res != NULL); + + if (mono_class_is_contextbound (tp->remote_class->proxy_class) && tp->rp->context == (MonoObject *) mono_context_get ()) { + mono_field_get_value (tp->rp->unwrapped_server, field, res); + return res; + } + + if (!getter) { + getter = mono_class_get_method_from_name (mono_defaults.object_class, "FieldGetter", -1); + if (!getter) { + mono_error_set_not_supported (error, "Linked away."); + return NULL; + } + } + + field_class = mono_class_from_mono_type (field->type); + + msg = (MonoMethodMessage *)mono_object_new_checked (domain, mono_defaults.mono_method_message_class, error); + return_val_if_nok (error, NULL); + out_args = mono_array_new_checked (domain, mono_defaults.object_class, 1, error); + return_val_if_nok (error, NULL); + MonoReflectionMethod *rm = mono_method_get_object_checked (domain, getter, NULL, error); + return_val_if_nok (error, NULL); + mono_message_init (domain, msg, rm, out_args, error); + return_val_if_nok (error, NULL); + + full_name = mono_type_get_full_name (klass); + MonoString *full_name_str = mono_string_new_checked (domain, full_name, error); + g_free (full_name); + return_val_if_nok (error, NULL); + mono_array_setref (msg->args, 0, full_name_str); + MonoString *field_name = mono_string_new_checked (domain, mono_field_get_name (field), error); + return_val_if_nok (error, NULL); + mono_array_setref (msg->args, 1, field_name); + + mono_remoting_invoke ((MonoObject *)(tp->rp), msg, &exc, &out_args, error); + return_val_if_nok (error, NULL); + + if (exc) { + mono_error_set_exception_instance (error, (MonoException *)exc); + return NULL; + } + + if (mono_array_length (out_args) == 0) + return NULL; + + mono_gc_wbarrier_generic_store (res, mono_array_get (out_args, MonoObject *, 0)); + + if (field_class->valuetype) { + return ((char *)*res) + sizeof (MonoObject); + } else + return res; +} + +/** + * mono_load_remote_field_new: + * \param this + * \param klass + * \param field + * Missing documentation. + */ +MonoObject * +mono_load_remote_field_new (MonoObject *this_obj, MonoClass *klass, MonoClassField *field) +{ + MonoError error; + + MonoObject *result = mono_load_remote_field_new_checked (this_obj, klass, field, &error); + mono_error_cleanup (&error); + return result; +} + +/** + * mono_load_remote_field_new_checked: + * \param this pointer to an object + * \param klass klass of the object containing \p field + * \param field the field to load + * \param error set on error. + * This method is called by the runtime on attempts to load fields of + * transparent proxy objects. \p this points to such TP, \p klass is the class of + * the object containing \p field. + * \returns a freshly allocated object containing the value of the field. On failure returns NULL and sets \p error. + */ +MonoObject * +mono_load_remote_field_new_checked (MonoObject *this_obj, MonoClass *klass, MonoClassField *field, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + + static MonoMethod *tp_load = NULL; + + g_assert (mono_object_is_transparent_proxy (this_obj)); + + if (!tp_load) { + tp_load = mono_class_get_method_from_name (mono_defaults.transparent_proxy_class, "LoadRemoteFieldNew", -1); + if (!tp_load) { + mono_error_set_not_supported (error, "Linked away."); + return NULL; + } + } + + /* MonoType *type = mono_class_get_type (klass); */ + + gpointer args[2]; + args [0] = &klass; + args [1] = &field; + + return mono_runtime_invoke_checked (tp_load, this_obj, args, error); +} + +/** + * mono_store_remote_field: + * \param this_obj pointer to an object + * \param klass klass of the object containing \p field + * \param field the field to load + * \param val the value/object to store + * This method is called by the runtime on attempts to store fields of + * transparent proxy objects. \p this_obj points to such TP, \p klass is the class of + * the object containing \p field. \p val is the new value to store in \p field. + */ +void +mono_store_remote_field (MonoObject *this_obj, MonoClass *klass, MonoClassField *field, gpointer val) +{ + MonoError error; + (void) mono_store_remote_field_checked (this_obj, klass, field, val, &error); + mono_error_cleanup (&error); +} + +/** + * mono_store_remote_field_checked: + * \param this_obj pointer to an object + * \param klass klass of the object containing \p field + * \param field the field to load + * \param val the value/object to store + * \param error set on error + * This method is called by the runtime on attempts to store fields of + * transparent proxy objects. \p this_obj points to such TP, \p klass is the class of + * the object containing \p field. \p val is the new value to store in \p field. + * \returns on success returns TRUE, on failure returns FALSE and sets \p error. + */ +gboolean +mono_store_remote_field_checked (MonoObject *this_obj, MonoClass *klass, MonoClassField *field, gpointer val, MonoError *error) +{ + + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + + MonoDomain *domain = mono_domain_get (); + MonoClass *field_class; + MonoObject *arg; + + g_assert (mono_object_is_transparent_proxy (this_obj)); + + field_class = mono_class_from_mono_type (field->type); + + if (field_class->valuetype) { + arg = mono_value_box_checked (domain, field_class, val, error); + return_val_if_nok (error, FALSE); + } else { + arg = *((MonoObject**)val); + } + + return mono_store_remote_field_new_checked (this_obj, klass, field, arg, error); +} + +/** + * mono_store_remote_field_new: + * \param this_obj + * \param klass + * \param field + * \param arg + * Missing documentation + */ +void +mono_store_remote_field_new (MonoObject *this_obj, MonoClass *klass, MonoClassField *field, MonoObject *arg) +{ + MonoError error; + (void) mono_store_remote_field_new_checked (this_obj, klass, field, arg, &error); + mono_error_cleanup (&error); +} + +/** + * mono_store_remote_field_new_checked: + * \param this_obj + * \param klass + * \param field + * \param arg + * \param error + * Missing documentation + */ +gboolean +mono_store_remote_field_new_checked (MonoObject *this_obj, MonoClass *klass, MonoClassField *field, MonoObject *arg, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + static MonoMethod *tp_store = NULL; + + error_init (error); + + g_assert (mono_object_is_transparent_proxy (this_obj)); + + if (!tp_store) { + tp_store = mono_class_get_method_from_name (mono_defaults.transparent_proxy_class, "StoreRemoteField", -1); + if (!tp_store) { + mono_error_set_not_supported (error, "Linked away."); + return FALSE; + } + } + + gpointer args[3]; + args [0] = &klass; + args [1] = &field; + args [2] = arg; + + mono_runtime_invoke_checked (tp_store, this_obj, args, error); + return is_ok (error); +} +#endif + +/** + * mono_create_ftnptr: + * + * Given a function address, create a function descriptor for it. + * This is only needed on some platforms. + */ +gpointer +mono_create_ftnptr (MonoDomain *domain, gpointer addr) +{ + return callbacks.create_ftnptr (domain, addr); +} + +/* + * mono_get_addr_from_ftnptr: + * + * Given a pointer to a function descriptor, return the function address. + * This is only needed on some platforms. + */ +gpointer +mono_get_addr_from_ftnptr (gpointer descr) +{ + return callbacks.get_addr_from_ftnptr (descr); +} + +/** + * mono_string_chars: + * \param s a \c MonoString + * \returns a pointer to the UTF-16 characters stored in the \c MonoString + */ +gunichar2 * +mono_string_chars (MonoString *s) +{ + // MONO_REQ_GC_UNSAFE_MODE; //FIXME too much trouble for now + + return s->chars; +} + +/** + * mono_string_length: + * \param s MonoString + * \returns the length in characters of the string + */ +int +mono_string_length (MonoString *s) +{ + MONO_REQ_GC_UNSAFE_MODE; + + return s->length; +} + +/** + * mono_string_handle_length: + * \param s \c MonoString + * \returns the length in characters of the string + */ +int +mono_string_handle_length (MonoStringHandle s) +{ + MONO_REQ_GC_UNSAFE_MODE; + + return MONO_HANDLE_GETVAL (s, length); +} + + +/** + * mono_array_length: + * \param array a \c MonoArray* + * \returns the total number of elements in the array. This works for + * both vectors and multidimensional arrays. + */ +uintptr_t +mono_array_length (MonoArray *array) +{ + MONO_REQ_GC_UNSAFE_MODE; + + return array->max_length; +} + +/** + * mono_array_addr_with_size: + * \param array a \c MonoArray* + * \param size size of the array elements + * \param idx index into the array + * Use this function to obtain the address for the \p idx item on the + * \p array containing elements of size \p size. + * + * This method performs no bounds checking or type checking. + * \returns the address of the \p idx element in the array. + */ +char* +mono_array_addr_with_size (MonoArray *array, int size, uintptr_t idx) +{ + MONO_REQ_GC_UNSAFE_MODE; + + return ((char*)(array)->vector) + size * idx; +} + + +MonoArray * +mono_glist_to_array (GList *list, MonoClass *eclass, MonoError *error) +{ + MonoDomain *domain = mono_domain_get (); + MonoArray *res; + int len, i; + + error_init (error); + if (!list) + return NULL; + + len = g_list_length (list); + res = mono_array_new_checked (domain, eclass, len, error); + return_val_if_nok (error, NULL); + + for (i = 0; list; list = list->next, i++) + mono_array_set (res, gpointer, i, list->data); + + return res; +} + +#if NEVER_DEFINED +/* + * The following section is purely to declare prototypes and + * document the API, as these C files are processed by our + * tool + */ + +/** + * mono_array_set: + * \param array array to alter + * \param element_type A C type name, this macro will use the sizeof(type) to determine the element size + * \param index index into the array + * \param value value to set + * Value Type version: This sets the \p index's element of the \p array + * with elements of size sizeof(type) to the provided \p value. + * + * This macro does not attempt to perform type checking or bounds checking. + * + * Use this to set value types in a \c MonoArray. + */ +void mono_array_set(MonoArray *array, Type element_type, uintptr_t index, Value value) +{ +} + +/** + * mono_array_setref: + * \param array array to alter + * \param index index into the array + * \param value value to set + * Reference Type version. This sets the \p index's element of the + * \p array with elements of size sizeof(type) to the provided \p value. + * + * This macro does not attempt to perform type checking or bounds checking. + * + * Use this to reference types in a \c MonoArray. + */ +void mono_array_setref(MonoArray *array, uintptr_t index, MonoObject *object) +{ +} + +/** + * mono_array_get: + * \param array array on which to operate on + * \param element_type C element type (example: \c MonoString*, \c int, \c MonoObject*) + * \param index index into the array + * + * Use this macro to retrieve the \p index element of an \p array and + * extract the value assuming that the elements of the array match + * the provided type value. + * + * This method can be used with both arrays holding value types and + * reference types. For reference types, the \p type parameter should + * be a \c MonoObject* or any subclass of it, like \c MonoString*. + * + * This macro does not attempt to perform type checking or bounds checking. + * + * \returns The element at the \p index position in the \p array. + */ +Type mono_array_get (MonoArray *array, Type element_type, uintptr_t index) +{ +} +#endif + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/object.h b/unity-2019.4.24f1-mbe/mono/metadata/object.h new file mode 100644 index 000000000..4956577b4 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/object.h @@ -0,0 +1,426 @@ +/** + * \file + */ + +#ifndef _MONO_CLI_OBJECT_H_ +#define _MONO_CLI_OBJECT_H_ + +#include +#include + +MONO_BEGIN_DECLS + +typedef mono_byte MonoBoolean; + +typedef struct _MonoString MONO_RT_MANAGED_ATTR MonoString; +typedef struct _MonoArray MONO_RT_MANAGED_ATTR MonoArray; +typedef struct _MonoReflectionMethod MONO_RT_MANAGED_ATTR MonoReflectionMethod; +typedef struct _MonoReflectionAssembly MONO_RT_MANAGED_ATTR MonoReflectionAssembly; +typedef struct _MonoReflectionModule MONO_RT_MANAGED_ATTR MonoReflectionModule; +typedef struct _MonoReflectionField MONO_RT_MANAGED_ATTR MonoReflectionField; +typedef struct _MonoReflectionProperty MONO_RT_MANAGED_ATTR MonoReflectionProperty; +typedef struct _MonoReflectionEvent MONO_RT_MANAGED_ATTR MonoReflectionEvent; +typedef struct _MonoReflectionType MONO_RT_MANAGED_ATTR MonoReflectionType; +typedef struct _MonoDelegate MONO_RT_MANAGED_ATTR MonoDelegate; +typedef struct _MonoException MONO_RT_MANAGED_ATTR MonoException; +typedef struct _MonoThreadsSync MonoThreadsSync; +typedef struct _MonoThread MONO_RT_MANAGED_ATTR MonoThread; +typedef struct _MonoDynamicAssembly MonoDynamicAssembly; +typedef struct _MonoDynamicImage MonoDynamicImage; +typedef struct _MonoReflectionMethodBody MONO_RT_MANAGED_ATTR MonoReflectionMethodBody; +typedef struct _MonoAppContext MONO_RT_MANAGED_ATTR MonoAppContext; + +typedef struct MONO_RT_MANAGED_ATTR _MonoObject { + MonoVTable *vtable; + MonoThreadsSync *synchronisation; +} MonoObject; + +typedef MonoObject* (*MonoInvokeFunc) (MonoMethod *method, void *obj, void **params, MonoObject **exc, MonoError *error); +typedef void* (*MonoCompileFunc) (MonoMethod *method); +typedef void (*MonoMainThreadFunc) (void* user_data); + +#define MONO_OBJECT_SETREF(obj,fieldname,value) do { \ + mono_gc_wbarrier_set_field ((MonoObject*)(obj), &((obj)->fieldname), (MonoObject*)value); \ + /*(obj)->fieldname = (value);*/ \ + } while (0) + +/* This should be used if 's' can reside on the heap */ +#define MONO_STRUCT_SETREF(s,field,value) do { \ + mono_gc_wbarrier_generic_store (&((s)->field), (MonoObject*)(value)); \ + } while (0) + +#define mono_array_addr(array,type,index) ((type*)(void*) mono_array_addr_with_size (array, sizeof (type), index)) +#define mono_array_get(array,type,index) ( *(type*)mono_array_addr ((array), type, (index)) ) +#define mono_array_set(array,type,index,value) \ + do { \ + type *__p = (type *) mono_array_addr ((array), type, (index)); \ + *__p = (value); \ + } while (0) +#define mono_array_setref(array,index,value) \ + do { \ + void **__p = (void **) mono_array_addr ((array), void*, (index)); \ + mono_gc_wbarrier_set_arrayref ((array), __p, (MonoObject*)(value)); \ + /* *__p = (value);*/ \ + } while (0) +#define mono_array_memcpy_refs(dest,destidx,src,srcidx,count) \ + do { \ + void **__p = (void **) mono_array_addr ((dest), void*, (destidx)); \ + void **__s = mono_array_addr ((src), void*, (srcidx)); \ + mono_gc_wbarrier_arrayref_copy (__p, __s, (count)); \ + } while (0) + +MONO_API mono_unichar2 *mono_string_chars (MonoString *s); +MONO_API int mono_string_length (MonoString *s); + +MONO_RT_EXTERNAL_ONLY MONO_API MonoObject * +mono_object_new (MonoDomain *domain, MonoClass *klass); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoObject * +mono_object_new_specific (MonoVTable *vtable); + +/* can be used for classes without finalizer in non-profiling mode */ +MONO_RT_EXTERNAL_ONLY +MONO_API MonoObject * +mono_object_new_fast (MonoVTable *vtable); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoObject * +mono_object_new_alloc_specific (MonoVTable *vtable); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoObject * +mono_object_new_from_token (MonoDomain *domain, MonoImage *image, uint32_t token); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoArray* +mono_array_new (MonoDomain *domain, MonoClass *eclass, uintptr_t n); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoArray* +mono_array_new_full (MonoDomain *domain, MonoClass *array_class, + uintptr_t *lengths, intptr_t *lower_bounds); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoArray * +mono_array_new_specific (MonoVTable *vtable, uintptr_t n); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoArray* +mono_array_clone (MonoArray *array); + +MONO_API char* +mono_array_addr_with_size (MonoArray *array, int size, uintptr_t idx); + +MONO_API uintptr_t +mono_array_length (MonoArray *array); + +MONO_API MonoString* +mono_string_empty (MonoDomain *domain); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoString* +mono_string_empty_wrapper (void); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoString* +mono_string_new_utf16 (MonoDomain *domain, const mono_unichar2 *text, int32_t len); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoString* +mono_string_new_size (MonoDomain *domain, int32_t len); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoString* +mono_ldstr (MonoDomain *domain, MonoImage *image, uint32_t str_index); + +MONO_API MonoString* +mono_string_is_interned (MonoString *str); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoString* +mono_string_intern (MonoString *str); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoString* +mono_string_new (MonoDomain *domain, const char *text); + +MONO_API MonoString* +mono_string_new_wrapper (const char *text); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoString* +mono_string_new_len (MonoDomain *domain, const char *text, unsigned int length); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoString* +mono_string_new_utf32 (MonoDomain *domain, const mono_unichar4 *text, int32_t len); + +MONO_RT_EXTERNAL_ONLY +MONO_API char * +mono_string_to_utf8 (MonoString *string_obj); + +MONO_API char * +mono_string_to_utf8_checked (MonoString *string_obj, MonoError *error); + +MONO_API mono_unichar2 * +mono_string_to_utf16 (MonoString *string_obj); + +MONO_API mono_unichar4 * +mono_string_to_utf32 (MonoString *string_obj); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoString * +mono_string_from_utf16 (mono_unichar2 *data); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoString * +mono_string_from_utf32 (mono_unichar4 *data); + +MONO_API mono_bool +mono_string_equal (MonoString *s1, MonoString *s2); + +MONO_API unsigned int +mono_string_hash (MonoString *s); + +MONO_API int +mono_object_hash (MonoObject* obj); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoString * +mono_object_to_string (MonoObject *obj, MonoObject **exc); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoObject * +mono_value_box (MonoDomain *domain, MonoClass *klass, void* val); + +MONO_API void +mono_value_copy (void* dest, void* src, MonoClass *klass); + +MONO_API void +mono_value_copy_array (MonoArray *dest, int dest_idx, void* src, int count); + +MONO_API MonoVTable * +mono_object_get_vtable (MonoObject *obj); + +MONO_API MonoDomain* +mono_object_get_domain (MonoObject *obj); + +MONO_API MonoClass* +mono_object_get_class (MonoObject *obj); + +MONO_API void* +mono_object_unbox (MonoObject *obj); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoObject * +mono_object_clone (MonoObject *obj); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoObject * +mono_object_isinst (MonoObject *obj, MonoClass *klass); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoObject * +mono_object_isinst_mbyref (MonoObject *obj, MonoClass *klass); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoObject * +mono_object_castclass_mbyref (MonoObject *obj, MonoClass *klass); + +MONO_API mono_bool +mono_monitor_try_enter (MonoObject *obj, uint32_t ms); + +MONO_API mono_bool +mono_monitor_enter (MonoObject *obj); + +MONO_API void +mono_monitor_enter_v4 (MonoObject *obj, char *lock_taken); + +MONO_API unsigned int +mono_object_get_size (MonoObject *o); + +MONO_API void +mono_monitor_exit (MonoObject *obj); + +MONO_RT_EXTERNAL_ONLY +MONO_API void +mono_raise_exception (MonoException *ex); + +MONO_RT_EXTERNAL_ONLY +MONO_API void +mono_reraise_exception (MonoException *ex); + +MONO_RT_EXTERNAL_ONLY +MONO_API void +mono_runtime_object_init (MonoObject *this_obj); + +MONO_RT_EXTERNAL_ONLY +MONO_API void +mono_runtime_class_init (MonoVTable *vtable); + +MONO_API MonoDomain * +mono_vtable_domain (MonoVTable *vtable); + +MONO_API MonoClass * +mono_vtable_class (MonoVTable *vtable); + +MONO_API MonoMethod* +mono_object_get_virtual_method (MonoObject *obj, MonoMethod *method); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoObject* +mono_runtime_invoke (MonoMethod *method, void *obj, void **params, + MonoObject **exc); + +MONO_API MonoMethod * +mono_get_delegate_invoke (MonoClass *klass); + +MONO_API MonoMethod * +mono_get_delegate_begin_invoke (MonoClass *klass); + +MONO_API MonoMethod * +mono_get_delegate_end_invoke (MonoClass *klass); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoObject* +mono_runtime_delegate_invoke (MonoObject *delegate, void **params, + MonoObject **exc); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoObject* +mono_runtime_invoke_array (MonoMethod *method, void *obj, MonoArray *params, + MonoObject **exc); + +MONO_API void* +mono_method_get_unmanaged_thunk (MonoMethod *method); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoArray* +mono_runtime_get_main_args (void); + +MONO_API void +mono_runtime_exec_managed_code (MonoDomain *domain, + MonoMainThreadFunc main_func, + void* main_args); + +MONO_RT_EXTERNAL_ONLY +MONO_API int +mono_runtime_run_main (MonoMethod *method, int argc, char* argv[], + MonoObject **exc); + +MONO_RT_EXTERNAL_ONLY +MONO_API int +mono_runtime_exec_main (MonoMethod *method, MonoArray *args, + MonoObject **exc); + +MONO_API int +mono_runtime_set_main_args (int argc, char* argv[]); + +/* The following functions won't be available with mono was configured with remoting disabled. */ +/*#ifndef DISABLE_REMOTING */ +MONO_RT_EXTERNAL_ONLY +MONO_API void* +mono_load_remote_field (MonoObject *this_obj, MonoClass *klass, MonoClassField *field, void **res); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoObject * +mono_load_remote_field_new (MonoObject *this_obj, MonoClass *klass, MonoClassField *field); + +MONO_RT_EXTERNAL_ONLY +MONO_API void +mono_store_remote_field (MonoObject *this_obj, MonoClass *klass, MonoClassField *field, void* val); + +MONO_RT_EXTERNAL_ONLY +MONO_API void +mono_store_remote_field_new (MonoObject *this_obj, MonoClass *klass, MonoClassField *field, MonoObject *arg); + +/* #endif */ + +MONO_API void +mono_unhandled_exception (MonoObject *exc); + +MONO_API void +mono_print_unhandled_exception (MonoObject *exc); + +MONO_RT_EXTERNAL_ONLY +MONO_API void* +mono_compile_method (MonoMethod *method); + +/* accessors for fields and properties */ +MONO_API void +mono_field_set_value (MonoObject *obj, MonoClassField *field, void *value); + +MONO_API void +mono_field_static_set_value (MonoVTable *vt, MonoClassField *field, void *value); + +MONO_API void +mono_field_get_value (MonoObject *obj, MonoClassField *field, void *value); + +MONO_RT_EXTERNAL_ONLY +MONO_API void +mono_field_static_get_value (MonoVTable *vt, MonoClassField *field, void *value); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoObject * +mono_field_get_value_object (MonoDomain *domain, MonoClassField *field, MonoObject *obj); + +MONO_RT_EXTERNAL_ONLY +MONO_API void +mono_property_set_value (MonoProperty *prop, void *obj, void **params, MonoObject **exc); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoObject* +mono_property_get_value (MonoProperty *prop, void *obj, void **params, MonoObject **exc); + +/* GC handles support + * + * A handle can be created to refer to a managed object and either prevent it + * from being garbage collected or moved or to be able to know if it has been + * collected or not (weak references). + * mono_gchandle_new () is used to prevent an object from being garbage collected + * until mono_gchandle_free() is called. Use a TRUE value for the pinned argument to + * prevent the object from being moved (this should be avoided as much as possible + * and this should be used only for shorts periods of time or performance will suffer). + * To create a weakref use mono_gchandle_new_weakref (): track_resurrection should + * usually be false (see the GC docs for more details). + * mono_gchandle_get_target () can be used to get the object referenced by both kinds + * of handle: for a weakref handle, if an object has been collected, it will return NULL. + */ +MONO_API uint32_t mono_gchandle_new (MonoObject *obj, mono_bool pinned); +MONO_API uint32_t mono_gchandle_new_weakref (MonoObject *obj, mono_bool track_resurrection); +MONO_API MonoObject* mono_gchandle_get_target (uint32_t gchandle); +MONO_API void mono_gchandle_free (uint32_t gchandle); + +/* Reference queue support + * + * A reference queue is used to get notifications of when objects are collected. + * Call mono_gc_reference_queue_new to create a new queue and pass the callback that + * will be invoked when registered objects are collected. + * Call mono_gc_reference_queue_add to register a pair of objects and data within a queue. + * The callback will be triggered once an object is both unreachable and finalized. + */ + +typedef void (*mono_reference_queue_callback) (void *user_data); +typedef struct _MonoReferenceQueue MonoReferenceQueue; + +MONO_API MonoReferenceQueue* mono_gc_reference_queue_new (mono_reference_queue_callback callback); +MONO_API void mono_gc_reference_queue_free (MonoReferenceQueue *queue); +MONO_API mono_bool mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data); + + + +/* GC write barriers support */ +MONO_API void mono_gc_wbarrier_set_field (MonoObject *obj, void* field_ptr, MonoObject* value); +MONO_API void mono_gc_wbarrier_set_arrayref (MonoArray *arr, void* slot_ptr, MonoObject* value); +MONO_API void mono_gc_wbarrier_arrayref_copy (void* dest_ptr, void* src_ptr, int count); +MONO_API void mono_gc_wbarrier_generic_store (void* ptr, MonoObject* value); +MONO_API void mono_gc_wbarrier_generic_store_atomic (void *ptr, MonoObject *value); +MONO_API void mono_gc_wbarrier_generic_nostore (void* ptr); +MONO_API void mono_gc_wbarrier_value_copy (void* dest, void* src, int count, MonoClass *klass); +MONO_API void mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src); + +MONO_END_DECLS + +#endif + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/oop.c b/unity-2019.4.24f1-mbe/mono/metadata/oop.c new file mode 100644 index 000000000..f58fa1389 --- /dev/null +++ b/unity-2019.4.24f1-mbe/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/unity-2019.4.24f1-mbe/mono/metadata/opcodes.c b/unity-2019.4.24f1-mbe/mono/metadata/opcodes.c new file mode 100644 index 000000000..511b291df --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/opcodes.c @@ -0,0 +1,97 @@ +/** + * \file + * CIL instruction information + * + * Author: + * Paolo Molaro (lupus@ximian.com) + * + * Copyright 2002-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#include +#include /* for NULL */ +#include + +#define MONO_PREFIX1_OFFSET MONO_CEE_ARGLIST +#define MONO_CUSTOM_PREFIX_OFFSET MONO_CEE_MONO_ICALL + +#define OPDEF(a,b,c,d,e,f,g,h,i,j) \ + { Mono ## e, MONO_FLOW_ ## j, MONO_ ## a }, + +const MonoOpcode +mono_opcodes [MONO_CEE_LAST + 1] = { +#include "mono/cil/opcode.def" + {0} +}; + +#undef OPDEF + +#ifdef HAVE_ARRAY_ELEM_INIT +#define MSGSTRFIELD(line) MSGSTRFIELD1(line) +#define MSGSTRFIELD1(line) str##line +static const struct msgstr_t { +#define OPDEF(a,b,c,d,e,f,g,h,i,j) char MSGSTRFIELD(__LINE__) [sizeof (b)]; +#include "mono/cil/opcode.def" +#undef OPDEF +} opstr = { +#define OPDEF(a,b,c,d,e,f,g,h,i,j) b, +#include "mono/cil/opcode.def" +#undef OPDEF +}; +static const int16_t opidx [] = { +#define OPDEF(a,b,c,d,e,f,g,h,i,j) [MONO_ ## a] = offsetof (struct msgstr_t, MSGSTRFIELD(__LINE__)), +#include "mono/cil/opcode.def" +#undef OPDEF +}; + +/** + * mono_opcode_name: + */ +const char* +mono_opcode_name (int opcode) +{ + return (const char*)&opstr + opidx [opcode]; +} + +#else +#define OPDEF(a,b,c,d,e,f,g,h,i,j) b, +static const char* const +mono_opcode_names [MONO_CEE_LAST + 1] = { +#include "mono/cil/opcode.def" + NULL +}; + +const char* +mono_opcode_name (int opcode) +{ + return mono_opcode_names [opcode]; +} + +#endif + +MonoOpcodeEnum +mono_opcode_value (const mono_byte **ip, const mono_byte *end) +{ + MonoOpcodeEnum res; + const mono_byte *p = *ip; + + if (p >= end) + return (MonoOpcodeEnum)-1; + if (*p == 0xfe) { + ++p; + if (p >= end) + return (MonoOpcodeEnum)-1; + res = (MonoOpcodeEnum)(*p + MONO_PREFIX1_OFFSET); + } else if (*p == MONO_CUSTOM_PREFIX) { + ++p; + if (p >= end) + return (MonoOpcodeEnum)-1; + res = (MonoOpcodeEnum)(*p + MONO_CUSTOM_PREFIX_OFFSET); + } else { + res = (MonoOpcodeEnum)*p; + } + *ip = p; + return res; +} + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/opcodes.h b/unity-2019.4.24f1-mbe/mono/metadata/opcodes.h new file mode 100644 index 000000000..976a46df2 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/opcodes.h @@ -0,0 +1,80 @@ +/** + * \file + */ + +#ifndef __MONO_METADATA_OPCODES_H__ +#define __MONO_METADATA_OPCODES_H__ + +/* + * opcodes.h: CIL instruction information + * + * Author: + * Paolo Molaro (lupus@ximian.com) + * + * (C) 2002 Ximian, Inc. + */ + +#include + +MONO_BEGIN_DECLS + +#define MONO_CUSTOM_PREFIX 0xf0 + +#define OPDEF(a,b,c,d,e,f,g,h,i,j) \ + MONO_ ## a, + +typedef enum { +#include "mono/cil/opcode.def" + MONO_CEE_LAST +} MonoOpcodeEnum; + +#undef OPDEF + +enum { + MONO_FLOW_NEXT, + MONO_FLOW_BRANCH, + MONO_FLOW_COND_BRANCH, + MONO_FLOW_ERROR, + MONO_FLOW_CALL, + MONO_FLOW_RETURN, + MONO_FLOW_META +}; + +enum { + MonoInlineNone, + MonoInlineType, + MonoInlineField, + MonoInlineMethod, + MonoInlineTok, + MonoInlineString, + MonoInlineSig, + MonoInlineVar, + MonoShortInlineVar, + MonoInlineBrTarget, + MonoShortInlineBrTarget, + MonoInlineSwitch, + MonoInlineR, + MonoShortInlineR, + MonoInlineI, + MonoShortInlineI, + MonoInlineI8 +}; + +typedef struct { + unsigned char argument; + unsigned char flow_type; + unsigned short opval; +} MonoOpcode; + +MONO_API extern const MonoOpcode mono_opcodes []; + +MONO_API const char* +mono_opcode_name (int opcode); + +MONO_API MonoOpcodeEnum +mono_opcode_value (const mono_byte **ip, const mono_byte *end); + +MONO_END_DECLS + +#endif /* __MONO_METADATA_OPCODES_H__ */ + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/profiler-events.h b/unity-2019.4.24f1-mbe/mono/metadata/profiler-events.h new file mode 100644 index 000000000..c603ca224 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/profiler-events.h @@ -0,0 +1,112 @@ +/* + * 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. + */ + +/* + * To #include this file, #define the following macros first: + * + * MONO_PROFILER_EVENT_0(name, type) + * MONO_PROFILER_EVENT_1(name, type, arg1_type, arg1_name) + * MONO_PROFILER_EVENT_2(name, type, arg1_type, arg1_name, arg2_type, arg2_name) + * MONO_PROFILER_EVENT_3(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name) + * MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) + * MONO_PROFILER_EVENT_5(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name, arg5_type, arg5_name) + * + * To add new callbacks to the API, simply add a line in this file and use + * MONO_PROFILER_RAISE to raise the event wherever. + * + * If you need more arguments then the current macros provide, add another + * macro and update all areas where the macros are used. Remember that this is + * a public header and not all users will be defining the newly added macro. So + * to prevent errors in existing code, you must add something like this at the + * beginning of this file: + * + * #ifndef MONO_PROFILER_EVENT_6 + * #define MONO_PROFILER_EVENT_6(...) # Do nothing. + * #endif + */ + +#ifndef MONO_PROFILER_EVENT_5 +#define MONO_PROFILER_EVENT_5(...) +#endif + +MONO_PROFILER_EVENT_0(runtime_initialized, RuntimeInitialized) +MONO_PROFILER_EVENT_0(runtime_shutdown_begin, RuntimeShutdownBegin) +MONO_PROFILER_EVENT_0(runtime_shutdown_end, RuntimeShutdownEnd) + +MONO_PROFILER_EVENT_1(context_loaded, ContextLoaded, MonoAppContext *, context) +MONO_PROFILER_EVENT_1(context_unloaded, ContextUnloaded, MonoAppContext *, context) + +MONO_PROFILER_EVENT_1(domain_loading, DomainLoading, MonoDomain *, domain) +MONO_PROFILER_EVENT_1(domain_loaded, DomainLoaded, MonoDomain *, domain) +MONO_PROFILER_EVENT_1(domain_unloading, DomainUnloading, MonoDomain *, domain) +MONO_PROFILER_EVENT_1(domain_unloaded, DomainUnloaded, MonoDomain *, domain) +MONO_PROFILER_EVENT_2(domain_name, DomainName, MonoDomain *, domain, const char *, name) + +MONO_PROFILER_EVENT_1(jit_begin, JitBegin, MonoMethod *, method) +MONO_PROFILER_EVENT_1(jit_failed, JitFailed, MonoMethod *, method) +MONO_PROFILER_EVENT_2(jit_done, JitDone, MonoMethod *, method, MonoJitInfo *, jinfo) +MONO_PROFILER_EVENT_2(jit_chunk_created, JitChunkCreated, const mono_byte *, chunk, uintptr_t, size) +MONO_PROFILER_EVENT_1(jit_chunk_destroyed, JitChunkDestroyed, const mono_byte *, chunk) +MONO_PROFILER_EVENT_4(jit_code_buffer, JitCodeBuffer, const mono_byte *, buffer, uint64_t, size, MonoProfilerCodeBufferType, type, const void *, data) + +MONO_PROFILER_EVENT_1(class_loading, ClassLoading, MonoClass *, klass) +MONO_PROFILER_EVENT_1(class_failed, ClassFailed, MonoClass *, klass) +MONO_PROFILER_EVENT_1(class_loaded, ClassLoaded, MonoClass *, klass) + +MONO_PROFILER_EVENT_1(vtable_loading, VTableLoading, MonoVTable *, vtable) +MONO_PROFILER_EVENT_1(vtable_failed, VTableFailed, MonoVTable *, vtable) +MONO_PROFILER_EVENT_1(vtable_loaded, VTableLoaded, MonoVTable *, vtable) + +MONO_PROFILER_EVENT_1(image_loading, ModuleLoading, MonoImage *, image) +MONO_PROFILER_EVENT_1(image_failed, ModuleFailed, MonoImage *, image) +MONO_PROFILER_EVENT_1(image_loaded, ModuleLoaded, MonoImage *, image) +MONO_PROFILER_EVENT_1(image_unloading, ModuleUnloading, MonoImage *, image) +MONO_PROFILER_EVENT_1(image_unloaded, ModuleUnloaded, MonoImage *, image) + +MONO_PROFILER_EVENT_1(assembly_loading, AssemblyLoading, MonoAssembly *, assembly) +MONO_PROFILER_EVENT_1(assembly_loaded, AssemblyLLoaded, MonoAssembly *, assembly) +MONO_PROFILER_EVENT_1(assembly_unloading, AssemblyLUnloading, MonoAssembly *, assembly) +MONO_PROFILER_EVENT_1(assembly_unloaded, AssemblyLUnloaded, MonoAssembly *, assembly) + +MONO_PROFILER_EVENT_2(method_enter, MethodEnter, MonoMethod *, method, MonoProfilerCallContext *, context) +MONO_PROFILER_EVENT_2(method_leave, MethodLeave, MonoMethod *, method, MonoProfilerCallContext *, context) +MONO_PROFILER_EVENT_2(method_tail_call, MethodTailCall, MonoMethod *, method, MonoMethod *, target) +MONO_PROFILER_EVENT_2(method_exception_leave, MethodExceptionLeave, MonoMethod *, method, MonoObject *, exception) +MONO_PROFILER_EVENT_1(method_free, MethodFree, MonoMethod *, method) +MONO_PROFILER_EVENT_1(method_begin_invoke, MethodBeginInvoke, MonoMethod *, method) +MONO_PROFILER_EVENT_1(method_end_invoke, MethodEndInvoke, MonoMethod *, method) + +MONO_PROFILER_EVENT_1(exception_throw, ExceptionThrow, MonoObject *, exception) +MONO_PROFILER_EVENT_4(exception_clause, ExceptionClause, MonoMethod *, method, uint32_t, index, MonoExceptionEnum, type, MonoObject *, exception) + +MONO_PROFILER_EVENT_2(gc_event, GCEvent, MonoProfilerGCEvent, event, uint32_t, generation) +MONO_PROFILER_EVENT_1(gc_allocation, GCAllocation, MonoObject *, object) +MONO_PROFILER_EVENT_2(gc_moves, GCMoves, MonoObject *const *, objects, uint64_t, count) +MONO_PROFILER_EVENT_1(gc_resize, GCResize, uintptr_t, size) +MONO_PROFILER_EVENT_3(gc_handle_created, GCHandleCreated, uint32_t, handle, MonoGCHandleType, type, MonoObject *, object) +MONO_PROFILER_EVENT_2(gc_handle_deleted, GCHandleDeleted, uint32_t, handle, MonoGCHandleType, type) +MONO_PROFILER_EVENT_0(gc_finalizing, GCFinalizing) +MONO_PROFILER_EVENT_0(gc_finalized, GCFinalized) +MONO_PROFILER_EVENT_1(gc_finalizing_object, GCFinalizingObject, MonoObject *, object) +MONO_PROFILER_EVENT_1(gc_finalized_object, GCFinalizedObject, MonoObject *, object) +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) +MONO_PROFILER_EVENT_1(monitor_acquired, MonitorAcquired, MonoObject *, object) + +MONO_PROFILER_EVENT_1(thread_started, ThreadStarted, uintptr_t, tid) +MONO_PROFILER_EVENT_1(thread_stopping, ThreadStopping, uintptr_t, tid) +MONO_PROFILER_EVENT_1(thread_stopped, ThreadStopped, uintptr_t, tid) +MONO_PROFILER_EVENT_1(thread_exited, ThreadExited, uintptr_t, tid) +MONO_PROFILER_EVENT_2(thread_name, ThreadName, uintptr_t, tid, const char *, name) + +MONO_PROFILER_EVENT_2(sample_hit, SampleHit, const mono_byte *, ip, const void *, context) + +MONO_PROFILER_EVENT_3(iomap_report, IOMap, const char *, report, const char *, old_path, const char *, new_path) diff --git a/unity-2019.4.24f1-mbe/mono/metadata/profiler-private.h b/unity-2019.4.24f1-mbe/mono/metadata/profiler-private.h new file mode 100644 index 000000000..08c8288a2 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/profiler-private.h @@ -0,0 +1,192 @@ +/* + * 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. + */ + +#ifndef __MONO_PROFILER_PRIVATE_H__ +#define __MONO_PROFILER_PRIVATE_H__ + +#include +#include +#include +#include +#include + +struct _MonoProfilerDesc { + MonoProfilerHandle next; + MonoProfiler *prof; + volatile gpointer cleanup_callback; + volatile gpointer coverage_filter; + volatile gpointer call_instrumentation_filter; + +#define _MONO_PROFILER_EVENT(name) \ + volatile gpointer name ## _cb; +#define MONO_PROFILER_EVENT_0(name, type) \ + _MONO_PROFILER_EVENT(name) +#define MONO_PROFILER_EVENT_1(name, type, arg1_type, arg1_name) \ + _MONO_PROFILER_EVENT(name) +#define MONO_PROFILER_EVENT_2(name, type, arg1_type, arg1_name, arg2_type, arg2_name) \ + _MONO_PROFILER_EVENT(name) +#define MONO_PROFILER_EVENT_3(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name) \ + _MONO_PROFILER_EVENT(name) +#define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \ + _MONO_PROFILER_EVENT(name) +#define MONO_PROFILER_EVENT_5(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name, arg5_type, arg5_name) \ + _MONO_PROFILER_EVENT(name) +#include +#undef MONO_PROFILER_EVENT_0 +#undef MONO_PROFILER_EVENT_1 +#undef MONO_PROFILER_EVENT_2 +#undef MONO_PROFILER_EVENT_3 +#undef MONO_PROFILER_EVENT_4 +#undef MONO_PROFILER_EVENT_5 +#undef _MONO_PROFILER_EVENT +}; + +typedef struct { + gboolean startup_done; + + MonoProfilerHandle profilers; + + gboolean code_coverage; + mono_mutex_t coverage_mutex; + MonoDomainCoverage *coverage_domains; + + MonoProfilerHandle sampling_owner; + MonoSemType sampling_semaphore; + MonoProfilerSampleMode sample_mode; + guint32 sample_freq; + + gboolean allocations; + gboolean fileio; + + gboolean call_contexts; + void (*context_enable) (void); + gpointer (*context_get_this) (MonoProfilerCallContext *); + gpointer (*context_get_argument) (MonoProfilerCallContext *, guint32); + gpointer (*context_get_local) (MonoProfilerCallContext *, guint32); + gpointer (*context_get_result) (MonoProfilerCallContext *); + void (*context_free_buffer) (gpointer); + +#define _MONO_PROFILER_EVENT(name) \ + volatile gint32 name ## _count; +#define MONO_PROFILER_EVENT_0(name, type) \ + _MONO_PROFILER_EVENT(name) +#define MONO_PROFILER_EVENT_1(name, type, arg1_type, arg1_name) \ + _MONO_PROFILER_EVENT(name) +#define MONO_PROFILER_EVENT_2(name, type, arg1_type, arg1_name, arg2_type, arg2_name) \ + _MONO_PROFILER_EVENT(name) +#define MONO_PROFILER_EVENT_3(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name) \ + _MONO_PROFILER_EVENT(name) +#define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \ + _MONO_PROFILER_EVENT(name) +#define MONO_PROFILER_EVENT_5(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name, arg5_type, arg5_name) \ + _MONO_PROFILER_EVENT(name) +#include +#undef MONO_PROFILER_EVENT_0 +#undef MONO_PROFILER_EVENT_1 +#undef MONO_PROFILER_EVENT_2 +#undef MONO_PROFILER_EVENT_3 +#undef MONO_PROFILER_EVENT_4 +#undef MONO_PROFILER_EVENT_5 +#undef _MONO_PROFILER_EVENT +} MonoProfilerState; + +extern MonoProfilerState mono_profiler_state; + +typedef struct { + guint32 entries; + struct { + guchar *cil_code; + guint32 count; + } data [1]; +} MonoProfilerCoverageInfo; + +void mono_profiler_started (void); +void mono_profiler_cleanup (void); + +static inline gboolean +mono_profiler_installed (void) +{ + return !!mono_profiler_state.profilers; +} + +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 { + /* + * Must be the first field (the JIT relies on it). Only filled out if this + * is a JIT frame; otherwise, zeroed. + */ + MonoContext context; + /* + * A non-NULL MonoInterpFrameHandle if this is an interpreter frame. + */ + gpointer interp_frame; + MonoMethod *method; + /* + * Points to the return value for an epilogue context. For a prologue, this + * is set to NULL. + */ + gpointer return_value; +}; + +MonoProfilerCallInstrumentationFlags mono_profiler_get_call_instrumentation_flags (MonoMethod *method); + +gboolean mono_profiler_sampling_enabled (void); +void mono_profiler_sampling_thread_post (void); +void mono_profiler_sampling_thread_wait (void); + +static inline gboolean +mono_profiler_allocations_enabled (void) +{ + return mono_profiler_state.allocations; +} + +#define _MONO_PROFILER_EVENT(name, ...) \ + ICALL_DECL_EXPORT void mono_profiler_raise_ ## name (__VA_ARGS__); +#define MONO_PROFILER_EVENT_0(name, type) \ + _MONO_PROFILER_EVENT(name, void) +#define MONO_PROFILER_EVENT_1(name, type, arg1_type, arg1_name) \ + _MONO_PROFILER_EVENT(name, arg1_type arg1_name) +#define MONO_PROFILER_EVENT_2(name, type, arg1_type, arg1_name, arg2_type, arg2_name) \ + _MONO_PROFILER_EVENT(name, arg1_type arg1_name, arg2_type arg2_name) +#define MONO_PROFILER_EVENT_3(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name) \ + _MONO_PROFILER_EVENT(name, arg1_type arg1_name, arg2_type arg2_name, arg3_type arg3_name) +#define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \ + _MONO_PROFILER_EVENT(name, arg1_type arg1_name, arg2_type arg2_name, arg3_type arg3_name, arg4_type arg4_name) +#define MONO_PROFILER_EVENT_5(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name, arg5_type, arg5_name) \ + _MONO_PROFILER_EVENT(name, arg1_type arg1_name, arg2_type arg2_name, arg3_type arg3_name, arg4_type arg4_name, arg5_type arg5_name) +#include +#undef MONO_PROFILER_EVENT_0 +#undef MONO_PROFILER_EVENT_1 +#undef MONO_PROFILER_EVENT_2 +#undef MONO_PROFILER_EVENT_3 +#undef MONO_PROFILER_EVENT_4 +#undef MONO_PROFILER_EVENT_5 +#undef _MONO_PROFILER_EVENT + +/* These are the macros the rest of the runtime should use. */ + +#define MONO_PROFILER_ENABLED(name) \ + G_UNLIKELY (mono_profiler_state.name ## _count) + +#define MONO_PROFILER_RAISE(name, args) \ + do { \ + if (MONO_PROFILER_ENABLED (name)) \ + mono_profiler_raise_ ## name args; \ + } while (0) + +#endif // __MONO_PROFILER_PRIVATE_H__ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/profiler.c b/unity-2019.4.24f1-mbe/mono/metadata/profiler.c new file mode 100644 index 000000000..52e6984d4 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/profiler.c @@ -0,0 +1,1411 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MonoProfilerState mono_profiler_state; + +typedef void (*MonoProfilerInitializer) (const char *); + +#define OLD_INITIALIZER_NAME "mono_profiler_startup" +#define NEW_INITIALIZER_NAME "mono_profiler_init" + +static gboolean +load_profiler (MonoDl *module, const char *name, const char *desc) +{ + if (!module) + return FALSE; + + char *err, *old_name = g_strdup_printf (OLD_INITIALIZER_NAME); + MonoProfilerInitializer func; + + if (!(err = mono_dl_symbol (module, old_name, (gpointer) &func))) { + mono_profiler_printf_err ("Found old-style startup symbol '%s' for the '%s' profiler; it has not been migrated to the new API.", old_name, name); + g_free (old_name); + return FALSE; + } + + g_free (err); + g_free (old_name); + + char *new_name = g_strdup_printf (NEW_INITIALIZER_NAME "_%s", name); + + if ((err = mono_dl_symbol (module, new_name, (gpointer *) &func))) { + g_free (err); + g_free (new_name); + return FALSE; + } + + g_free (new_name); + + func (desc); + + return TRUE; +} + +static gboolean +load_profiler_from_executable (const char *name, const char *desc) +{ + char *err; + + /* + * Some profilers (such as ours) may need to call back into the runtime + * from their sampling callback (which is called in async-signal context). + * They need to be able to know that all references back to the runtime + * have been resolved; otherwise, calling runtime functions may result in + * invoking the dynamic linker which is not async-signal-safe. Passing + * MONO_DL_EAGER will ask the dynamic linker to resolve everything upfront. + */ + MonoDl *module = mono_dl_open (NULL, MONO_DL_EAGER, &err); + + if (!module) { + mono_profiler_printf_err ("Could not open main executable: %s", err); + g_free (err); + return FALSE; + } + + return load_profiler (module, name, desc); +} + +static gboolean +load_profiler_from_directory (const char *directory, const char *libname, const char *name, const char *desc) +{ + char* path; + void *iter = NULL; + + while ((path = mono_dl_build_path (directory, libname, &iter))) { + // See the comment in load_embedded_profiler (). + MonoDl *module = mono_dl_open (path, MONO_DL_EAGER, NULL); + + g_free (path); + + if (module) + return load_profiler (module, name, desc); + } + + return FALSE; +} + +static gboolean +load_profiler_from_installation (const char *libname, const char *name, const char *desc) +{ + char *err; + MonoDl *module = mono_dl_open_runtime_lib (libname, MONO_DL_EAGER, &err); + + g_free (err); + + if (module) + return load_profiler (module, name, desc); + + return FALSE; +} + +/** + * mono_profiler_load: + * + * Loads a profiler module based on the specified description. \p desc can be + * of the form \c name:args or just \c name. For example, \c log:sample and + * \c log will both load \c libmono-profiler-log.so. The description is passed + * to the module after it has been loaded. If the specified module has already + * been loaded, this function has no effect. + * + * A module called \c foo should declare an entry point like so: + * + * \code + * void mono_profiler_init_foo (const char *desc) + * { + * } + * \endcode + * + * This function is \b not async safe. + * + * This function may \b only be called by embedders prior to running managed + * code. + */ +#ifndef RUNTIME_IL2CPP +void +mono_profiler_load (const char *desc) +{ + if (!desc || !strcmp ("default", desc)) + desc = "log:report"; + + const char *col = strchr (desc, ':'); + char *mname; + + if (col != NULL) { + mname = (char *) g_memdup (desc, col - desc + 1); + mname [col - desc] = 0; + } else + mname = g_strdup (desc); + + if (!load_profiler_from_executable (mname, desc)) { + char *libname = g_strdup_printf ("mono-profiler-%s", mname); + gboolean res = load_profiler_from_installation (libname, mname, desc); + + if (!res && mono_config_get_assemblies_dir ()) + res = load_profiler_from_directory (mono_assembly_getrootdir (), libname, mname, desc); + + if (!res) + res = load_profiler_from_directory (NULL, libname, mname, desc); + + if (!res) + mono_profiler_printf_err ("The '%s' profiler wasn't found in the main executable nor could it be loaded from '%s'.", mname, libname); + + g_free (libname); + } + + g_free (mname); +} +#endif + +/** + * mono_profiler_create: + * + * Installs a profiler and returns a handle for it. The handle is used with the + * other functions in the profiler API (e.g. for setting up callbacks). The + * given structure pointer, \p prof, will be passed to all callbacks from the + * profiler API. It can be \c NULL. + * + * Example usage: + * + * \code + * struct _MonoProfiler { + * int my_stuff; + * // ... + * }; + * + * MonoProfiler *prof = malloc (sizeof (MonoProfiler)); + * prof->my_stuff = 42; + * MonoProfilerHandle handle = mono_profiler_create (prof); + * mono_profiler_set_shutdown_callback (handle, my_shutdown_cb); + * \endcode + * + * This function is \b not async safe. + * + * This function may \b only be called from a profiler's init function or prior + * to running managed code. + */ +MonoProfilerHandle +mono_profiler_create (MonoProfiler *prof) +{ + MonoProfilerHandle handle = g_new0 (struct _MonoProfilerDesc, 1); + + handle->prof = prof; + handle->next = mono_profiler_state.profilers; + + mono_profiler_state.profilers = handle; + + return handle; +} + +/** + * mono_profiler_set_cleanup_callback: + * + * Sets a profiler cleanup function. This function will be invoked at shutdown + * when the profiler API is cleaning up its internal structures. It's mainly + * intended for a profiler to free the structure pointer that was passed to + * \c mono_profiler_create, if necessary. + * + * This function is async safe. + */ +void +mono_profiler_set_cleanup_callback (MonoProfilerHandle handle, MonoProfilerCleanupCallback cb) +{ + mono_atomic_store_ptr (&handle->cleanup_callback, (gpointer) cb); +} + +/** + * mono_profiler_enable_coverage: + * + * Enables support for code coverage instrumentation. At the moment, this means + * enabling the debug info subsystem. If this function is not called, it will + * not be possible to use \c mono_profiler_get_coverage_data. Returns \c TRUE + * if code coverage support was enabled, or \c FALSE if the function was called + * too late for this to be possible. + * + * This function is \b not async safe. + * + * 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) +{ + if (mono_profiler_state.startup_done) + return FALSE; + + mono_os_mutex_init (&mono_profiler_state.coverage_mutex); + + 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: + * + * Sets a code coverage filter function. The profiler API will invoke filter + * functions from all installed profilers. If any of them return \c TRUE, then + * the given method will be instrumented for coverage analysis. All filters are + * guaranteed to be called at least once per method, even if an earlier filter + * has already returned \c TRUE. + * + * Note that filter functions must be installed before a method is compiled in + * order to have any effect, i.e. a filter should be registered in a profiler's + * init function or prior to running managed code (if embedding). + * + * This function is async safe. + */ +void +mono_profiler_set_coverage_filter_callback (MonoProfilerHandle handle, MonoProfilerCoverageFilterCallback cb) +{ + mono_atomic_store_ptr (&handle->coverage_filter, (gpointer) cb); +} + +static void +coverage_domains_lock (void) +{ + mono_os_mutex_lock (&mono_profiler_state.coverage_mutex); +} + +static 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: + * + * Retrieves all coverage data for \p method and invokes \p cb for each entry. + * Source location information will only be filled out if \p method has debug + * info available. Returns \c TRUE if \p method was instrumented for code + * coverage; otherwise, \c FALSE. + * + * Please note that the structure passed to \p cb is only valid for the + * duration of the callback. + * + * This function is \b not async safe. + */ +#ifndef RUNTIME_IL2CPP +mono_bool +mono_profiler_get_coverage_data (MonoProfilerHandle handle, MonoMethod *method, MonoProfilerCoverageCallback cb) +{ + 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); + + 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); + + if (!info) { + char *source_file; + int i, n_il_offsets; + int *source_files; + GPtrArray *source_file_list; + MonoSymSeqPoint *sym_seq_points; + + if (!minfo) + return TRUE; + + /* Return 0 counts for all locations */ + + mono_debug_get_seq_points (minfo, &source_file, &source_file_list, &source_files, &sym_seq_points, &n_il_offsets); + for (i = 0; i < n_il_offsets; ++i) { + MonoSymSeqPoint *sp = &sym_seq_points [i]; + const char *srcfile = ""; + + if (source_files [i] != -1) { + MonoDebugSourceInfo *sinfo = (MonoDebugSourceInfo *)g_ptr_array_index (source_file_list, source_files [i]); + srcfile = sinfo->source_file; + } + + MonoProfilerCoverageData data = { + .method = method, + .il_offset = sp->il_offset, + .counter = 0, + .file_name = srcfile, + .line = sp->line, + .column = 0, + }; + + cb (handle->prof, &data); + } + + g_free (source_files); + g_free (sym_seq_points); + g_ptr_array_free (source_file_list, TRUE); + + mono_metadata_free_mh (header); + return TRUE; + } + + 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); + } + } + + cb (handle->prof, &data); + + g_free ((char *) data.file_name); + } + } + + mono_metadata_free_mh (header); + + 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 (MonoDomain *domain, MonoMethod *method, guint32 entries) +{ + if (!mono_profiler_state.code_coverage) + return FALSE; + + if (method->wrapper_type) + return FALSE; + + gboolean cover = FALSE; + + for (MonoProfilerHandle handle = mono_profiler_state.profilers; handle; handle = handle->next) { + MonoProfilerCoverageFilterCallback cb = handle->coverage_filter; + + if (cb) + cover |= cb (handle->prof, method); + } + + if (!cover) + return NULL; + + 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 (covdomain->coverage_hash, method, info); + + mono_os_mutex_unlock(&covdomain->mutex); + + return info; +} + +/** + * mono_profiler_enable_sampling: + * + * Enables the sampling thread. Users must call this function if they intend + * to use statistical sampling; \c mono_profiler_set_sample_mode will have no + * effect if this function has not been called. The first profiler to call this + * function will get ownership over sampling settings (mode and frequency) so + * that no other profiler can change those settings. Returns \c TRUE if the + * sampling thread was enabled, or \c FALSE if the function was called too late + * for this to be possible. + * + * Note that \c mono_profiler_set_sample_mode must still be called with a mode + * other than \c MONO_PROFILER_SAMPLE_MODE_NONE to actually start sampling. + * + * This function is \b not async safe. + * + * This function may \b only be called from a profiler's init function or prior + * to running managed code. + */ +mono_bool +mono_profiler_enable_sampling (MonoProfilerHandle handle) +{ + if (mono_profiler_state.startup_done) + return FALSE; + + if (mono_profiler_state.sampling_owner) + return TRUE; + + mono_profiler_state.sampling_owner = handle; + mono_profiler_state.sample_mode = MONO_PROFILER_SAMPLE_MODE_NONE; + mono_profiler_state.sample_freq = 100; + mono_os_sem_init (&mono_profiler_state.sampling_semaphore, 0); + + return TRUE; +} + +/** + * mono_profiler_set_sample_mode: + * + * Sets the sampling mode and frequency (in Hz). \p freq must be a positive + * number. If the calling profiler has ownership over sampling settings, the + * settings will be changed and this function will return \c TRUE; otherwise, + * it returns \c FALSE without changing any settings. + * + * This function is async safe. + */ +mono_bool +mono_profiler_set_sample_mode (MonoProfilerHandle handle, MonoProfilerSampleMode mode, uint32_t freq) +{ + if (handle != mono_profiler_state.sampling_owner) + return FALSE; + + mono_profiler_state.sample_mode = mode; + mono_profiler_state.sample_freq = freq; + + mono_profiler_sampling_thread_post (); + + return TRUE; +} + +/** + * mono_profiler_get_sample_mode: + * + * Retrieves the current sampling mode and/or frequency (in Hz). Returns + * \c TRUE if the calling profiler is allowed to change the sampling settings; + * otherwise, \c FALSE. + * + * This function is async safe. + */ +mono_bool +mono_profiler_get_sample_mode (MonoProfilerHandle handle, MonoProfilerSampleMode *mode, uint32_t *freq) +{ + if (mode) + *mode = mono_profiler_state.sample_mode; + + if (freq) + *freq = mono_profiler_state.sample_freq; + + return handle == mono_profiler_state.sampling_owner; +} + +gboolean +mono_profiler_sampling_enabled (void) +{ + return !!mono_profiler_state.sampling_owner; +} + +void +mono_profiler_sampling_thread_post (void) +{ + mono_os_sem_post (&mono_profiler_state.sampling_semaphore); +} + +void +mono_profiler_sampling_thread_wait (void) +{ + mono_os_sem_wait (&mono_profiler_state.sampling_semaphore, MONO_SEM_FLAGS_NONE); +} + +/** + * mono_profiler_enable_allocations: + * + * Enables instrumentation of GC allocations. This is necessary so that managed + * allocators can be instrumented with a call into the profiler API. + * Allocations will not be reported unless this function is called. Returns + * \c TRUE if allocation instrumentation was enabled, or \c FALSE if the + * function was called too late for this to be possible. + * + * This function is \b not async safe. + * + * This function may \b only be called from a profiler's init function or prior + * to running managed code. + */ +mono_bool +mono_profiler_enable_allocations (void) +{ +#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: + * + * Sets a call instrumentation filter function. The profiler API will invoke + * filter functions from all installed profilers. If any of them return flags + * other than \c MONO_PROFILER_CALL_INSTRUMENTATION_NONE, then the given method + * will be instrumented as requested. All filters are guaranteed to be called + * exactly once per method, even if earlier filters have already specified all + * flags. + * + * Note that filter functions must be installed before a method is compiled in + * order to have any effect, i.e. a filter should be registered in a profiler's + * init function or prior to running managed code (if embedding). Also, to + * instrument a method that's going to be AOT-compiled, a filter must be + * installed at AOT time. This can be done in exactly the same way as one would + * normally, i.e. by passing the \c --profile option on the command line, by + * calling \c mono_profiler_load, or simply by using the profiler API as an + * embedder. + * + * Indiscriminate method instrumentation is extremely heavy and will slow down + * most applications to a crawl. Users should consider sampling as a possible + * alternative to such heavy-handed instrumentation. + * + * This function is async safe. + */ +void +mono_profiler_set_call_instrumentation_filter_callback (MonoProfilerHandle handle, MonoProfilerCallInstrumentationFilterCallback cb) +{ + mono_atomic_store_ptr (&handle->call_instrumentation_filter, (gpointer) cb); +} + +/** + * mono_profiler_enable_call_context_introspection: + * + * Enables support for retrieving stack frame data from a call context. At the + * moment, this means enabling the debug info subsystem. If this function is not + * called, it will not be possible to use the call context introspection + * functions (they will simply return \c NULL). Returns \c TRUE if call context + * introspection was enabled, or \c FALSE if the function was called too late for + * this to be possible. + * + * Please note: Mono's LLVM backend does not support this feature. This means + * that methods with call context instrumentation will be handled by Mono's + * JIT even in LLVM mode. There is also a special case when Mono is compiling + * in LLVM-only mode: Since LLVM does not provide a way to implement call + * contexts, a \c NULL context will always be passed to enter/leave events even + * though this method returns \c TRUE. + * + * This function is \b not async safe. + * + * This function may \b only be called from a profiler's init function or prior + * to running managed code. + */ +mono_bool +mono_profiler_enable_call_context_introspection (void) +{ + if (mono_profiler_state.startup_done) + return FALSE; + + mono_profiler_state.context_enable (); + + return mono_profiler_state.call_contexts = TRUE; +} + +/** + * mono_profiler_call_context_get_this: + * + * Given a valid call context from an enter/leave event, retrieves a pointer to + * the \c this reference for the method. Returns \c NULL if none exists (i.e. + * it's a static method) or if call context introspection was not enabled. + * + * The buffer returned by this function must be freed with + * \c mono_profiler_call_context_free_buffer. + * + * Please note that a call context is only valid for the duration of the + * enter/leave callback it was passed to. + * + * This function is \b not async safe. + */ +void * +mono_profiler_call_context_get_this (MonoProfilerCallContext *context) +{ + if (!mono_profiler_state.call_contexts) + return NULL; + + return mono_profiler_state.context_get_this (context); +} + +/** + * mono_profiler_call_context_get_argument: + * + * Given a valid call context from an enter/leave event, retrieves a pointer to + * the method argument at the given position. Returns \c NULL if \p position is + * out of bounds or if call context introspection was not enabled. + * + * The buffer returned by this function must be freed with + * \c mono_profiler_call_context_free_buffer. + * + * Please note that a call context is only valid for the duration of the + * enter/leave callback it was passed to. + * + * This function is \b not async safe. + */ +void * +mono_profiler_call_context_get_argument (MonoProfilerCallContext *context, uint32_t position) +{ + if (!mono_profiler_state.call_contexts) + return NULL; + + return mono_profiler_state.context_get_argument (context, position); +} + +/** + * mono_profiler_call_context_get_local: + * + * Given a valid call context from an enter/leave event, retrieves a pointer to + * the local variable at the given position. Returns \c NULL if \p position is + * out of bounds or if call context introspection was not enabled. + * + * The buffer returned by this function must be freed with + * \c mono_profiler_call_context_free_buffer. + * + * Please note that a call context is only valid for the duration of the + * enter/leave callback it was passed to. + * + * This function is \b not async safe. + */ +void * +mono_profiler_call_context_get_local (MonoProfilerCallContext *context, uint32_t position) +{ + if (!mono_profiler_state.call_contexts) + return NULL; + + return mono_profiler_state.context_get_local (context, position); +} + +/** + * mono_profiler_call_context_get_result: + * + * Given a valid call context from an enter/leave event, retrieves a pointer to + * return value of a method. Returns \c NULL if the method has no return value + * (i.e. it returns \c void), if the leave event was the result of a tail call, + * if the function is called on a context from an enter event, or if call + * context introspection was not enabled. + * + * The buffer returned by this function must be freed with + * \c mono_profiler_call_context_free_buffer. + * + * Please note that a call context is only valid for the duration of the + * enter/leave callback it was passed to. + * + * This function is \b not async safe. + */ +void * +mono_profiler_call_context_get_result (MonoProfilerCallContext *context) +{ + if (!mono_profiler_state.call_contexts) + return NULL; + + return mono_profiler_state.context_get_result (context); +} + +/** + * mono_profiler_call_context_free_buffer: + * + * Frees a buffer returned by one of the call context introspection functions. + * Passing a \c NULL value for \p buffer is allowed, which makes this function + * a no-op. + * + * This function is \b not async safe. + */ +void +mono_profiler_call_context_free_buffer (void *buffer) +{ + mono_profiler_state.context_free_buffer (buffer); +} + +MonoProfilerCallInstrumentationFlags +mono_profiler_get_call_instrumentation_flags (MonoMethod *method) +{ + MonoProfilerCallInstrumentationFlags flags = MONO_PROFILER_CALL_INSTRUMENTATION_NONE; + + for (MonoProfilerHandle handle = mono_profiler_state.profilers; handle; handle = handle->next) { + MonoProfilerCallInstrumentationFilterCallback cb = handle->call_instrumentation_filter; + + if (cb) + flags |= cb (handle->prof, method); + } + + return flags; +} + +void +mono_profiler_started (void) +{ + mono_profiler_state.startup_done = TRUE; +} + +void +mono_profiler_cleanup (void) +{ + for (MonoProfilerHandle handle = mono_profiler_state.profilers; handle; handle = handle->next) { +#define _MONO_PROFILER_EVENT(name) \ + mono_profiler_set_ ## name ## _callback (handle, NULL); \ + g_assert (!handle->name ## _cb); +#define MONO_PROFILER_EVENT_0(name, type) \ + _MONO_PROFILER_EVENT(name) +#define MONO_PROFILER_EVENT_1(name, type, arg1_type, arg1_name) \ + _MONO_PROFILER_EVENT(name) +#define MONO_PROFILER_EVENT_2(name, type, arg1_type, arg1_name, arg2_type, arg2_name) \ + _MONO_PROFILER_EVENT(name) +#define MONO_PROFILER_EVENT_3(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name) \ + _MONO_PROFILER_EVENT(name) +#define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \ + _MONO_PROFILER_EVENT(name) +#define MONO_PROFILER_EVENT_5(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name, arg5_type, arg5_name) \ + _MONO_PROFILER_EVENT(name) +#include +#undef MONO_PROFILER_EVENT_0 +#undef MONO_PROFILER_EVENT_1 +#undef MONO_PROFILER_EVENT_2 +#undef MONO_PROFILER_EVENT_3 +#undef MONO_PROFILER_EVENT_4 +#undef MONO_PROFILER_EVENT_5 +#undef _MONO_PROFILER_EVENT + } + +#define _MONO_PROFILER_EVENT(name, type) \ + g_assert (!mono_profiler_state.name ## _count); +#define MONO_PROFILER_EVENT_0(name, type) \ + _MONO_PROFILER_EVENT(name, type) +#define MONO_PROFILER_EVENT_1(name, type, arg1_type, arg1_name) \ + _MONO_PROFILER_EVENT(name, type) +#define MONO_PROFILER_EVENT_2(name, type, arg1_type, arg1_name, arg2_type, arg2_name) \ + _MONO_PROFILER_EVENT(name, type) +#define MONO_PROFILER_EVENT_3(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name) \ + _MONO_PROFILER_EVENT(name, type) +#define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \ + _MONO_PROFILER_EVENT(name, type) +#define MONO_PROFILER_EVENT_5(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name, arg5_type, arg5_name) \ + _MONO_PROFILER_EVENT(name, type) +#include +#undef MONO_PROFILER_EVENT_0 +#undef MONO_PROFILER_EVENT_1 +#undef MONO_PROFILER_EVENT_2 +#undef MONO_PROFILER_EVENT_3 +#undef MONO_PROFILER_EVENT_4 +#undef MONO_PROFILER_EVENT_5 +#undef _MONO_PROFILER_EVENT + + MonoProfilerHandle head = mono_profiler_state.profilers; + + while (head) { + MonoProfilerCleanupCallback cb = head->cleanup_callback; + + if (cb) + cb (head->prof); + + MonoProfilerHandle cur = head; + head = head->next; + + g_free (cur); + } + + if (mono_profiler_state.sampling_owner) + mono_os_sem_destroy (&mono_profiler_state.sampling_semaphore); +} + +static void +update_callback (volatile gpointer *location, gpointer new_, volatile gint32 *counter) +{ + gpointer old; + + do { + old = mono_atomic_load_ptr (location); + } while (mono_atomic_cas_ptr (location, new_, old) != old); + + /* + * At this point, we could have installed a NULL callback while the counter + * is still non-zero, i.e. setting the callback and modifying the counter + * is not a single atomic operation. This is fine as we make sure callbacks + * are non-NULL before invoking them (see the code below that generates the + * raise functions), and besides, updating callbacks at runtime is an + * inherently racy operation. + */ + + if (old) + mono_atomic_dec_i32 (counter); + + if (new_) + mono_atomic_inc_i32 (counter); +} + +#define _MONO_PROFILER_EVENT(name, type) \ + void \ + mono_profiler_set_ ## name ## _callback (MonoProfilerHandle handle, MonoProfiler ## type ## Callback cb) \ + { \ + update_callback (&handle->name ## _cb, (gpointer) cb, &mono_profiler_state.name ## _count); \ + } +#define MONO_PROFILER_EVENT_0(name, type) \ + _MONO_PROFILER_EVENT(name, type) +#define MONO_PROFILER_EVENT_1(name, type, arg1_type, arg1_name) \ + _MONO_PROFILER_EVENT(name, type) +#define MONO_PROFILER_EVENT_2(name, type, arg1_type, arg1_name, arg2_type, arg2_name) \ + _MONO_PROFILER_EVENT(name, type) +#define MONO_PROFILER_EVENT_3(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name) \ + _MONO_PROFILER_EVENT(name, type) +#define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \ + _MONO_PROFILER_EVENT(name, type) +#define MONO_PROFILER_EVENT_5(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name, arg5_type, arg5_name) \ + _MONO_PROFILER_EVENT(name, type) +#include +#undef MONO_PROFILER_EVENT_0 +#undef MONO_PROFILER_EVENT_1 +#undef MONO_PROFILER_EVENT_2 +#undef MONO_PROFILER_EVENT_3 +#undef MONO_PROFILER_EVENT_4 +#undef MONO_PROFILER_EVENT_5 +#undef _MONO_PROFILER_EVENT + +#define _MONO_PROFILER_EVENT(name, type, params, args) \ + void \ + mono_profiler_raise_ ## name params \ + { \ + for (MonoProfilerHandle h = mono_profiler_state.profilers; h; h = h->next) { \ + MonoProfiler ## type ## Callback cb = h->name ## _cb; \ + if (cb) \ + cb args; \ + } \ + } +#define MONO_PROFILER_EVENT_0(name, type) \ + _MONO_PROFILER_EVENT(name, type, (void), (h->prof)) +#define MONO_PROFILER_EVENT_1(name, type, arg1_type, arg1_name) \ + _MONO_PROFILER_EVENT(name, type, (arg1_type arg1_name), (h->prof, arg1_name)) +#define MONO_PROFILER_EVENT_2(name, type, arg1_type, arg1_name, arg2_type, arg2_name) \ + _MONO_PROFILER_EVENT(name, type, (arg1_type arg1_name, arg2_type arg2_name), (h->prof, arg1_name, arg2_name)) +#define MONO_PROFILER_EVENT_3(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name) \ + _MONO_PROFILER_EVENT(name, type, (arg1_type arg1_name, arg2_type arg2_name, arg3_type arg3_name), (h->prof, arg1_name, arg2_name, arg3_name)) +#define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \ + _MONO_PROFILER_EVENT(name, type, (arg1_type arg1_name, arg2_type arg2_name, arg3_type arg3_name, arg4_type arg4_name), (h->prof, arg1_name, arg2_name, arg3_name, arg4_name)) +#define MONO_PROFILER_EVENT_5(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name, arg5_type, arg5_name) \ + _MONO_PROFILER_EVENT(name, type, (arg1_type arg1_name, arg2_type arg2_name, arg3_type arg3_name, arg4_type arg4_name, arg5_type arg5_name), (h->prof, arg1_name, arg2_name, arg3_name, arg4_name, arg5_name)) +#include +#undef MONO_PROFILER_EVENT_0 +#undef MONO_PROFILER_EVENT_1 +#undef MONO_PROFILER_EVENT_2 +#undef MONO_PROFILER_EVENT_3 +#undef MONO_PROFILER_EVENT_4 +#undef MONO_PROFILER_EVENT_5 +#undef _MONO_PROFILER_EVENT + +/* + * The following code is here to maintain compatibility with a few profiler API + * functions used by Xamarin.{Android,iOS,Mac} so that they keep working + * regardless of which system Mono version is used. + * + * TODO: Remove this some day if we're OK with breaking compatibility. + */ + +typedef void *MonoLegacyProfiler; + +typedef void (*MonoLegacyProfileFunc) (MonoLegacyProfiler *prof); +typedef void (*MonoLegacyProfileThreadFunc) (MonoLegacyProfiler *prof, uintptr_t tid); +typedef void (*MonoLegacyProfileGCFunc) (MonoLegacyProfiler *prof, MonoProfilerGCEvent event, int generation); +typedef void (*MonoLegacyProfileGCResizeFunc) (MonoLegacyProfiler *prof, int64_t new_size); +typedef void (*MonoLegacyProfileJitResult) (MonoLegacyProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo, int result); +typedef void (*MonoLegacyProfileAllocFunc) (MonoLegacyProfiler *prof, MonoObject *obj, MonoClass *klass); +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; + MonoLegacyProfiler *profiler; + MonoLegacyProfileFunc shutdown_callback; + MonoLegacyProfileThreadFunc thread_start, thread_end; + MonoLegacyProfileGCFunc gc_event; + MonoLegacyProfileGCResizeFunc gc_heap_resize; + MonoLegacyProfileJitResult jit_end2; + MonoLegacyProfileAllocFunc allocation; + MonoLegacyProfileFileIOFunc fileio; + MonoLegacyProfileMethodFunc enter; + MonoLegacyProfileMethodFunc leave; + MonoLegacyProfileExceptionFunc throw_callback; + MonoLegacyProfileMethodFunc exc_method_leave; + MonoLegacyProfileExceptionClauseFunc clause_callback; +}; + +static MonoProfiler *current; + +MONO_API void mono_profiler_install (MonoLegacyProfiler *prof, MonoLegacyProfileFunc callback); +MONO_API void mono_profiler_install_thread (MonoLegacyProfileThreadFunc start, MonoLegacyProfileThreadFunc end); +MONO_API void mono_profiler_install_gc (MonoLegacyProfileGCFunc callback, MonoLegacyProfileGCResizeFunc heap_resize_callback); +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 +shutdown_cb (MonoProfiler *prof) +{ + prof->shutdown_callback (prof->profiler); +} + +void +mono_profiler_install (MonoLegacyProfiler *prof, MonoLegacyProfileFunc callback) +{ + current = g_new0 (MonoProfiler, 1); + current->handle = mono_profiler_create (current); + current->profiler = prof; + current->shutdown_callback = callback; + + if (callback) + mono_profiler_set_runtime_shutdown_end_callback (current->handle, shutdown_cb); +} + +static void +thread_start_cb (MonoProfiler *prof, uintptr_t tid) +{ + prof->thread_start (prof->profiler, tid); +} + +static void +thread_stop_cb (MonoProfiler *prof, uintptr_t tid) +{ + prof->thread_end (prof->profiler, tid); +} + +void +mono_profiler_install_thread (MonoLegacyProfileThreadFunc start, MonoLegacyProfileThreadFunc end) +{ + current->thread_start = start; + current->thread_end = end; + + if (start) + mono_profiler_set_thread_started_callback (current->handle, thread_start_cb); + + if (end) + mono_profiler_set_thread_stopped_callback (current->handle, thread_stop_cb); +} + +static void +gc_event_cb (MonoProfiler *prof, MonoProfilerGCEvent event, uint32_t generation) +{ + prof->gc_event (prof->profiler, event, generation); +} + +static void +gc_resize_cb (MonoProfiler *prof, uintptr_t size) +{ + prof->gc_heap_resize (prof->profiler, size); +} + +void +mono_profiler_install_gc (MonoLegacyProfileGCFunc callback, MonoLegacyProfileGCResizeFunc heap_resize_callback) +{ + current->gc_event = callback; + current->gc_heap_resize = heap_resize_callback; + + if (callback) + mono_profiler_set_gc_event_callback (current->handle, gc_event_cb); + + if (heap_resize_callback) + mono_profiler_set_gc_resize_callback (current->handle, gc_resize_cb); +} + +static void +jit_done_cb (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo) +{ + prof->jit_end2 (prof->profiler, method, jinfo, 0); +} + +static void +jit_failed_cb (MonoProfiler *prof, MonoMethod *method) +{ + prof->jit_end2 (prof->profiler, method, NULL, 1); +} + +void +mono_profiler_install_jit_end (MonoLegacyProfileJitResult end) +{ + current->jit_end2 = end; + + if (end) { + mono_profiler_set_jit_done_callback (current->handle, jit_done_cb); + mono_profiler_set_jit_failed_callback (current->handle, jit_failed_cb); + } +} + + +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 +allocation_cb (MonoProfiler *prof, MonoObject* object) +{ + prof->allocation (prof->profiler, object, object->vtable->klass); +} + +void +mono_profiler_install_allocation (MonoLegacyProfileAllocFunc callback) +{ + current->allocation = callback; + + if (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) +{ + prof->enter (prof->profiler, method); +} + +static void +leave_cb (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *context) +{ + prof->leave (prof->profiler, method); +} + +static void +tail_call_cb (MonoProfiler *prof, MonoMethod *method, MonoMethod *target) +{ + prof->leave (prof->profiler, method); +} + +void +mono_profiler_install_enter_leave (MonoLegacyProfileMethodFunc enter, MonoLegacyProfileMethodFunc fleave) +{ + current->enter = enter; + current->leave = fleave; + + if (enter) + mono_profiler_set_method_enter_callback (current->handle, enter_cb); + + if (fleave) { + mono_profiler_set_method_leave_callback (current->handle, leave_cb); + mono_profiler_set_method_tail_call_callback (current->handle, tail_call_cb); + } +} + +static void +throw_callback_cb (MonoProfiler *prof, MonoObject *exception) +{ + prof->throw_callback (prof->profiler, exception); +} + +static void +exc_method_leave_cb (MonoProfiler *prof, MonoMethod *method, MonoObject *exception) +{ + prof->exc_method_leave (prof->profiler, method); +} + +static void +clause_callback_cb (MonoProfiler *prof, MonoMethod *method, uint32_t index, MonoExceptionEnum type, MonoObject *exception) +{ + prof->clause_callback (prof->profiler, method, type, index); +} + +void +mono_profiler_install_exception (MonoLegacyProfileExceptionFunc throw_callback, MonoLegacyProfileMethodFunc exc_method_leave, MonoLegacyProfileExceptionClauseFunc clause_callback) +{ + current->throw_callback = throw_callback; + current->exc_method_leave = exc_method_leave; + current->clause_callback = clause_callback; + + if (throw_callback) + mono_profiler_set_exception_throw_callback (current->handle, throw_callback_cb); + + if (exc_method_leave) + mono_profiler_set_method_exception_leave_callback (current->handle, exc_method_leave_cb); + + if (clause_callback) + mono_profiler_set_exception_clause_callback (current->handle, clause_callback_cb); +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/profiler.h b/unity-2019.4.24f1-mbe/mono/metadata/profiler.h new file mode 100644 index 000000000..f49115001 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/profiler.h @@ -0,0 +1,235 @@ +/* + * 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. + */ + +#ifndef __MONO_PROFILER_H__ +#define __MONO_PROFILER_H__ + +#include +#include +#include + +MONO_BEGIN_DECLS + +/** + * This value will be incremented whenever breaking changes to the profiler API + * are made. This macro is intended for use in profiler modules that wish to + * support older versions of the profiler API. + */ +#define MONO_PROFILER_API_VERSION 2 + +typedef struct _MonoProfiler MonoProfiler; +typedef struct _MonoProfilerDesc *MonoProfilerHandle; + +typedef void (*MonoProfilerCleanupCallback) (MonoProfiler *prof); + +MONO_API void mono_profiler_load (const char *desc); +MONO_API MonoProfilerHandle mono_profiler_create (MonoProfiler *prof); +MONO_API void mono_profiler_set_cleanup_callback (MonoProfilerHandle handle, MonoProfilerCleanupCallback cb); + +typedef struct { + MonoMethod *method; + uint32_t il_offset; + uint32_t counter; + const char *file_name; + uint32_t line; + 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 { + /** + * Do not perform sampling. Will make the sampling thread sleep until the + * sampling mode is changed to one of the below modes. + */ + MONO_PROFILER_SAMPLE_MODE_NONE = 0, + /** + * Try to base sampling frequency on process activity. Falls back to + * MONO_PROFILER_SAMPLE_MODE_REAL if such a clock is not available. + */ + MONO_PROFILER_SAMPLE_MODE_PROCESS = 1, + /** + * Base sampling frequency on wall clock time. Uses a monotonic clock when + * available (all major platforms). + */ + MONO_PROFILER_SAMPLE_MODE_REAL = 2, +} MonoProfilerSampleMode; + +MONO_API mono_bool mono_profiler_enable_sampling (MonoProfilerHandle handle); +MONO_API mono_bool mono_profiler_set_sample_mode (MonoProfilerHandle handle, MonoProfilerSampleMode mode, uint32_t freq); +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; + +typedef enum { + /** + * Do not instrument calls. + */ + MONO_PROFILER_CALL_INSTRUMENTATION_NONE = 0, + /** + * Instrument method entries. + */ + MONO_PROFILER_CALL_INSTRUMENTATION_ENTER = 1 << 1, + /** + * Also capture a call context for method entries. + */ + MONO_PROFILER_CALL_INSTRUMENTATION_ENTER_CONTEXT = 1 << 2, + /** + * Instrument method exits. + */ + MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE = 1 << 3, + /** + * Also capture a call context for method exits. + */ + MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE_CONTEXT = 1 << 4, + /** + * Instrument method exits as a result of a tail call. + */ + MONO_PROFILER_CALL_INSTRUMENTATION_TAIL_CALL = 1 << 5, + /** + * Instrument exceptional method exits. + */ + MONO_PROFILER_CALL_INSTRUMENTATION_EXCEPTION_LEAVE = 1 << 6, +} MonoProfilerCallInstrumentationFlags; + +typedef MonoProfilerCallInstrumentationFlags (*MonoProfilerCallInstrumentationFilterCallback) (MonoProfiler *prof, MonoMethod *method); + +MONO_API void mono_profiler_set_call_instrumentation_filter_callback (MonoProfilerHandle handle, MonoProfilerCallInstrumentationFilterCallback cb); +MONO_API mono_bool mono_profiler_enable_call_context_introspection (void); +MONO_API void *mono_profiler_call_context_get_this (MonoProfilerCallContext *context); +MONO_API void *mono_profiler_call_context_get_argument (MonoProfilerCallContext *context, uint32_t position); +MONO_API void *mono_profiler_call_context_get_local (MonoProfilerCallContext *context, uint32_t position); +MONO_API void *mono_profiler_call_context_get_result (MonoProfilerCallContext *context); +MONO_API void mono_profiler_call_context_free_buffer (void *buffer); + +typedef enum { + /** + * The \c data parameter is a \c MonoMethod pointer. + */ + MONO_PROFILER_CODE_BUFFER_METHOD = 0, + MONO_PROFILER_CODE_BUFFER_METHOD_TRAMPOLINE = 1, + MONO_PROFILER_CODE_BUFFER_UNBOX_TRAMPOLINE = 2, + MONO_PROFILER_CODE_BUFFER_IMT_TRAMPOLINE = 3, + MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE = 4, + /** + * The \c data parameter is a C string. + */ + MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE = 5, + MONO_PROFILER_CODE_BUFFER_HELPER = 6, + MONO_PROFILER_CODE_BUFFER_MONITOR = 7, + MONO_PROFILER_CODE_BUFFER_DELEGATE_INVOKE = 8, + MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING = 9, +} MonoProfilerCodeBufferType; + +typedef enum { + MONO_GC_EVENT_PRE_STOP_WORLD = 6, + /** + * When this event arrives, the GC and suspend locks are acquired. + */ + MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED = 10, + MONO_GC_EVENT_POST_STOP_WORLD = 7, + MONO_GC_EVENT_START = 0, + MONO_GC_EVENT_END = 5, + MONO_GC_EVENT_PRE_START_WORLD = 8, + /** + * When this event arrives, the GC and suspend locks are released. + */ + MONO_GC_EVENT_POST_START_WORLD_UNLOCKED = 11, + MONO_GC_EVENT_POST_START_WORLD = 9, +} MonoProfilerGCEvent; + +/* + * The macros below will generate the majority of the callback API. Refer to + * mono/metadata/profiler-events.h for a list of callbacks. They are expanded + * like so: + * + * typedef void (*MonoProfilerRuntimeInitializedCallback (MonoProfiler *prof); + * MONO_API void mono_profiler_set_runtime_initialized_callback (MonoProfiler *prof, MonoProfilerRuntimeInitializedCallback cb); + * + * typedef void (*MonoProfilerRuntimeShutdownCallback (MonoProfiler *prof); + * MONO_API void mono_profiler_set_runtime_shutdown_callback (MonoProfiler *prof, MonoProfilerRuntimeShutdownCallback cb); + * + * typedef void (*MonoProfilerContextLoadedCallback (MonoProfiler *prof); + * MONO_API void mono_profiler_set_context_loaded_callback (MonoProfiler *prof, MonoProfilerContextLoadedCallback cb); + * + * typedef void (*MonoProfilerContextUnloadedCallback (MonoProfiler *prof); + * MONO_API void mono_profiler_set_context_unloaded_callback (MonoProfiler *prof, MonoProfilerContextUnloadedCallback cb); + * + * Etc. + * + * To remove a callback, pass NULL instead of a valid function pointer. + * Callbacks can be changed at any point, but note that doing so is inherently + * racy with respect to threads that aren't suspended, i.e. you may still see a + * call from another thread right after you change a callback. + * + * These functions are async safe. + */ + +#define _MONO_PROFILER_EVENT(type, ...) \ + typedef void (*MonoProfiler ## type ## Callback) (__VA_ARGS__); +#define MONO_PROFILER_EVENT_0(name, type) \ + _MONO_PROFILER_EVENT(type, MonoProfiler *prof) +#define MONO_PROFILER_EVENT_1(name, type, arg1_type, arg1_name) \ + _MONO_PROFILER_EVENT(type, MonoProfiler *prof, arg1_type arg1_name) +#define MONO_PROFILER_EVENT_2(name, type, arg1_type, arg1_name, arg2_type, arg2_name) \ + _MONO_PROFILER_EVENT(type, MonoProfiler *prof, arg1_type arg1_name, arg2_type arg2_name) +#define MONO_PROFILER_EVENT_3(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name) \ + _MONO_PROFILER_EVENT(type, MonoProfiler *prof, arg1_type arg1_name, arg2_type arg2_name, arg3_type arg3_name) +#define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \ + _MONO_PROFILER_EVENT(type, MonoProfiler *prof, arg1_type arg1_name, arg2_type arg2_name, arg3_type arg3_name, arg4_type arg4_name) +#define MONO_PROFILER_EVENT_5(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name, arg5_type, arg5_name) \ + _MONO_PROFILER_EVENT(type, MonoProfiler *prof, arg1_type arg1_name, arg2_type arg2_name, arg3_type arg3_name, arg4_type arg4_name, arg5_type arg5_name) +#include +#undef MONO_PROFILER_EVENT_0 +#undef MONO_PROFILER_EVENT_1 +#undef MONO_PROFILER_EVENT_2 +#undef MONO_PROFILER_EVENT_3 +#undef MONO_PROFILER_EVENT_4 +#undef MONO_PROFILER_EVENT_5 +#undef _MONO_PROFILER_EVENT + +#define _MONO_PROFILER_EVENT(name, type) \ + MONO_API void mono_profiler_set_ ## name ## _callback (MonoProfilerHandle handle, MonoProfiler ## type ## Callback cb); +#define MONO_PROFILER_EVENT_0(name, type) \ + _MONO_PROFILER_EVENT(name, type) +#define MONO_PROFILER_EVENT_1(name, type, arg1_type, arg1_name) \ + _MONO_PROFILER_EVENT(name, type) +#define MONO_PROFILER_EVENT_2(name, type, arg1_type, arg1_name, arg2_type, arg2_name) \ + _MONO_PROFILER_EVENT(name, type) +#define MONO_PROFILER_EVENT_3(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name) \ + _MONO_PROFILER_EVENT(name, type) +#define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \ + _MONO_PROFILER_EVENT(name, type) +#define MONO_PROFILER_EVENT_5(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name, arg5_type, arg5_name) \ + _MONO_PROFILER_EVENT(name, type) +#include +#undef MONO_PROFILER_EVENT_0 +#undef MONO_PROFILER_EVENT_1 +#undef MONO_PROFILER_EVENT_2 +#undef MONO_PROFILER_EVENT_3 +#undef MONO_PROFILER_EVENT_4 +#undef MONO_PROFILER_EVENT_5 +#undef _MONO_PROFILER_EVENT + +MONO_END_DECLS + +#endif // __MONO_PROFILER_H__ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/property-bag.c b/unity-2019.4.24f1-mbe/mono/metadata/property-bag.c new file mode 100644 index 000000000..41c2d6a12 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/property-bag.c @@ -0,0 +1,64 @@ +/** + * \file + * Linearizable property bag. + * + * Authors: + * Rodrigo Kumpera (kumpera@gmail.com) + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#include +#include +#include + +/* + * mono_property_bag_get: + * + * Return the value of the property with TAG or NULL. + * This doesn't take any locks. + */ +void* +mono_property_bag_get (MonoPropertyBag *bag, int tag) +{ + MonoPropertyBagItem *item; + + for (item = bag->head; item && item->tag <= tag; item = item->next) { + if (item->tag == tag) + return item; + } + return NULL; +} + +/* + * mono_property_bag_add: + * + * Store VALUE in the property bag. Return the previous value + * with the same tag, or NULL. VALUE should point to a structure + * extending the MonoPropertyBagItem structure with the 'tag' + * field set. + * This doesn't take any locks. + */ +void* +mono_property_bag_add (MonoPropertyBag *bag, void *value) +{ + MonoPropertyBagItem *cur, **prev, *item = value; + int tag = item->tag; + mono_memory_barrier (); //publish the values in value + +retry: + prev = &bag->head; + while (1) { + cur = *prev; + if (!cur || cur->tag > tag) { + item->next = cur; + if (mono_atomic_cas_ptr ((void*)prev, item, cur) == cur) + return item; + goto retry; + } else if (cur->tag == tag) { + return cur; + } else { + prev = &cur->next; + } + } + return value; +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/property-bag.h b/unity-2019.4.24f1-mbe/mono/metadata/property-bag.h new file mode 100644 index 000000000..b6eef75d3 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/property-bag.h @@ -0,0 +1,29 @@ +/** + * \file + * Linearizable property bag. + * + * Authors: + * Rodrigo Kumpera (kumpera@gmail.com) + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#ifndef __MONO_METADATA_PROPERTY_BAG_H__ +#define __MONO_METADATA_PROPERTY_BAG_H__ + +#include + +typedef struct _MonoPropertyBagItem MonoPropertyBagItem; + +struct _MonoPropertyBagItem { + MonoPropertyBagItem *next; + int tag; +}; + +typedef struct { + MonoPropertyBagItem *head; +} MonoPropertyBag; + +void* mono_property_bag_get (MonoPropertyBag *bag, int tag); +void* mono_property_bag_add (MonoPropertyBag *bag, void *value); + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/rand.c b/unity-2019.4.24f1-mbe/mono/metadata/rand.c new file mode 100644 index 000000000..31eb980fa --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/rand.c @@ -0,0 +1,50 @@ +/** + * \file + * System.Security.Cryptography.RNGCryptoServiceProvider support + * + * Authors: + * Mark Crichton (crichton@gimp.org) + * Patrik Torstensson (p@rxc.se) + * Sebastien Pouliot (sebastien@ximian.com) + * + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#include + +#include "object.h" +#include "object-internals.h" +#include "rand.h" +#include "utils/mono-rand.h" + +MonoBoolean +ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngOpen (void) +{ + return (MonoBoolean) mono_rand_open (); +} + +gpointer +ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngInitialize (MonoArray *seed) +{ + + return mono_rand_init (seed ? mono_array_addr (seed, guchar, 0) : NULL, seed ? mono_array_length (seed) : 0); +} + +gpointer +ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngGetBytes (gpointer handle, MonoArray *arry) +{ + MonoError error; + g_assert (arry); + mono_rand_try_get_bytes (&handle, mono_array_addr (arry, guchar, 0), mono_array_length (arry), &error); + mono_error_set_pending_exception (&error); + return handle; +} + +void +ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngClose (gpointer handle) +{ + mono_rand_close (handle); +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/rand.h b/unity-2019.4.24f1-mbe/mono/metadata/rand.h new file mode 100644 index 000000000..898f44f0c --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/rand.h @@ -0,0 +1,26 @@ +/** + * \file + * System.Security.Cryptography.RNGCryptoServiceProvider support + * + * Author: + * Mark Crichton (crichton@gimp.org) + * Sebastien Pouliot (sebastien@ximian.com) + * + * (C) 2001 Ximian, Inc. + * Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#ifndef _MONO_METADATA_RAND_H_ +#define _MONO_METADATA_RAND_H_ + +#include +#include +#include "mono/utils/mono-compiler.h" + +MonoBoolean ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngOpen (void); +gpointer ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngInitialize (MonoArray *seed); +gpointer ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngGetBytes (gpointer handle, MonoArray *arry); +void ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngClose (gpointer handle); + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/reflection-cache.h b/unity-2019.4.24f1-mbe/mono/metadata/reflection-cache.h new file mode 100644 index 000000000..9fd05486f --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/reflection-cache.h @@ -0,0 +1,136 @@ +/** + * \file + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#ifndef __MONO_METADATA_REFLECTION_CACHE_H__ +#define __MONO_METADATA_REFLECTION_CACHE_H__ + +#include +#include +#include +#include +#include +#include + +/* + * We need to return always the same object for MethodInfo, FieldInfo etc.. + * but we need to consider the reflected type. + * type uses a different hash, since it uses custom hash/equal functions. + */ + +typedef struct { + gpointer item; + MonoClass *refclass; +} ReflectedEntry; + +gboolean +reflected_equal (gconstpointer a, gconstpointer b); + +guint +reflected_hash (gconstpointer a); + +static inline ReflectedEntry* +alloc_reflected_entry (MonoDomain *domain) +{ + if (!mono_gc_is_moving ()) + return g_new0 (ReflectedEntry, 1); + else + return (ReflectedEntry *)mono_mempool_alloc (domain->mp, sizeof (ReflectedEntry)); +} + +static void +free_reflected_entry (ReflectedEntry *entry) +{ + if (!mono_gc_is_moving ()) + g_free (entry); +} + +static inline MonoObject* +cache_object (MonoDomain *domain, MonoClass *klass, gpointer item, MonoObject* o) +{ + MonoObject *obj; + ReflectedEntry pe; + pe.item = item; + pe.refclass = klass; + + mono_domain_lock (domain); + if (!domain->refobject_hash) + domain->refobject_hash = mono_conc_g_hash_table_new_type (reflected_hash, reflected_equal, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_DOMAIN, domain, "Domain Reflection Object Table"); + + obj = (MonoObject*) mono_conc_g_hash_table_lookup (domain->refobject_hash, &pe); + if (obj == NULL) { + ReflectedEntry *e = alloc_reflected_entry (domain); + e->item = item; + e->refclass = klass; + mono_conc_g_hash_table_insert (domain->refobject_hash, e, o); + obj = o; + } + mono_domain_unlock (domain); + return obj; +} + + +static inline MonoObjectHandle +cache_object_handle (MonoDomain *domain, MonoClass *klass, gpointer item, MonoObjectHandle o) +{ + ReflectedEntry pe; + pe.item = item; + pe.refclass = klass; + + mono_domain_lock (domain); + if (!domain->refobject_hash) + domain->refobject_hash = mono_conc_g_hash_table_new_type (reflected_hash, reflected_equal, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_DOMAIN, domain, "Domain Reflection Object Table"); + + MonoObjectHandle obj = MONO_HANDLE_NEW (MonoObject, mono_conc_g_hash_table_lookup (domain->refobject_hash, &pe)); + if (MONO_HANDLE_IS_NULL (obj)) { + ReflectedEntry *e = alloc_reflected_entry (domain); + e->item = item; + e->refclass = klass; + mono_conc_g_hash_table_insert (domain->refobject_hash, e, MONO_HANDLE_RAW (o)); + MONO_HANDLE_ASSIGN (obj, o); + } + mono_domain_unlock (domain); + return obj; +} + +#define CACHE_OBJECT(t,p,o,k) ((t) (cache_object (domain, (k), (p), (o)))) +#define CACHE_OBJECT_HANDLE(t,p,o,k) ((t) (cache_object_handle (domain, (k), (p), (o)))) + +static inline MonoObjectHandle +check_object_handle (MonoDomain* domain, MonoClass *klass, gpointer item) +{ + ReflectedEntry e; + e.item = item; + e.refclass = klass; + MonoConcGHashTable *hash = domain->refobject_hash; + if (!hash) + return MONO_HANDLE_NEW (MonoObject, NULL); + + MonoObjectHandle obj = MONO_HANDLE_NEW (MonoObject, mono_conc_g_hash_table_lookup (hash, &e)); + return obj; +} + + +typedef MonoObjectHandle (*ReflectionCacheConstructFunc_handle) (MonoDomain*, MonoClass*, gpointer, gpointer, MonoError *); + +static inline MonoObjectHandle +check_or_construct_handle (MonoDomain *domain, MonoClass *klass, gpointer item, gpointer user_data, MonoError *error, ReflectionCacheConstructFunc_handle construct) +{ + error_init (error); + MonoObjectHandle obj = check_object_handle (domain, klass, item); + if (!MONO_HANDLE_IS_NULL (obj)) + return obj; + MONO_HANDLE_ASSIGN (obj, construct (domain, klass, item, user_data, error)); + return_val_if_nok (error, NULL); + if (MONO_HANDLE_IS_NULL (obj)) + return obj; + /* note no caching if there was an error in construction */ + return cache_object_handle (domain, klass, item, obj); +} + + +#define CHECK_OR_CONSTRUCT_HANDLE(t,p,k,construct,ud) ((t) check_or_construct_handle (domain, (k), (p), (ud), error, (ReflectionCacheConstructFunc_handle) (construct))) + + +#endif /*__MONO_METADATA_REFLECTION_CACHE_H__*/ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/reflection-internals.h b/unity-2019.4.24f1-mbe/mono/metadata/reflection-internals.h new file mode 100644 index 000000000..dab5c6817 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/reflection-internals.h @@ -0,0 +1,119 @@ +/** + * \file + * Copyright 2014 Xamarin Inc + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#ifndef __MONO_METADATA_REFLECTION_INTERNALS_H__ +#define __MONO_METADATA_REFLECTION_INTERNALS_H__ + +#include +#include +#include +#include +#include + +gboolean +mono_reflection_parse_type_checked (char *name, MonoTypeNameParse *info, MonoError *error); + +gboolean +mono_reflection_is_usertype (MonoReflectionTypeHandle ref); + +MonoReflectionType* +mono_reflection_type_resolve_user_types (MonoReflectionType *type, MonoError *error); + +MonoType * +mono_reflection_type_handle_mono_type (MonoReflectionTypeHandle ref_type, MonoError *error); + +MonoType* +mono_reflection_get_type_checked (MonoImage *rootimage, MonoImage* image, MonoTypeNameParse *info, mono_bool ignorecase, mono_bool *type_resolve, MonoError *error); + +MonoType* +mono_reflection_type_from_name_checked (char *name, MonoImage *image, MonoError *error); + +guint32 +mono_reflection_get_token_checked (MonoObjectHandle obj, MonoError *error); + +MonoObject* +mono_custom_attrs_get_attr_checked (MonoCustomAttrInfo *ainfo, MonoClass *attr_klass, MonoError *error); + +MonoCustomAttrInfo* +mono_reflection_get_custom_attrs_info_checked (MonoObjectHandle obj, MonoError *error); + +MonoArrayHandle +mono_reflection_get_custom_attrs_data_checked (MonoObjectHandle obj, MonoError *error); + +MonoArrayHandle +mono_reflection_get_custom_attrs_by_type_handle (MonoObjectHandle obj, MonoClass *attr_klass, MonoError *error); + +MonoArray* +mono_reflection_get_custom_attrs_blob_checked (MonoReflectionAssembly *assembly, MonoObject *ctor, MonoArray *ctorArgs, MonoArray *properties, MonoArray *propValues, MonoArray *fields, MonoArray* fieldValues, MonoError *error); + +MonoCustomAttrInfo* +mono_custom_attrs_from_index_checked (MonoImage *image, uint32_t idx, gboolean ignore_missing, MonoError *error); +MonoCustomAttrInfo* +mono_custom_attrs_from_method_checked (MonoMethod *method, MonoError *error); +MonoCustomAttrInfo* +mono_custom_attrs_from_class_checked (MonoClass *klass, MonoError *error); +MonoCustomAttrInfo* +mono_custom_attrs_from_assembly_checked (MonoAssembly *assembly, gboolean ignore_missing, MonoError *error); +MonoCustomAttrInfo* +mono_custom_attrs_from_property_checked (MonoClass *klass, MonoProperty *property, MonoError *error); +MonoCustomAttrInfo* +mono_custom_attrs_from_event_checked (MonoClass *klass, MonoEvent *event, MonoError *error); +MonoCustomAttrInfo* +mono_custom_attrs_from_field_checked (MonoClass *klass, MonoClassField *field, MonoError *error); +MonoCustomAttrInfo* +mono_custom_attrs_from_param_checked (MonoMethod *method, uint32_t param, MonoError *error); + + +char* +mono_identifier_unescape_type_name_chars (char* identifier); + +MonoImage * +mono_find_dynamic_image_owner (void *ptr); + +MonoReflectionAssemblyHandle +mono_assembly_get_object_handle (MonoDomain *domain, MonoAssembly *assembly, MonoError *error); + +MonoReflectionType* +mono_type_get_object_checked (MonoDomain *domain, MonoType *type, MonoError *error); + +MonoReflectionTypeHandle +mono_type_get_object_handle (MonoDomain *domain, MonoType *type, MonoError *error); + +MonoReflectionField* +mono_field_get_object_checked (MonoDomain *domain, MonoClass *klass, MonoClassField *field, MonoError *error); + +MonoReflectionFieldHandle +mono_field_get_object_handle (MonoDomain *domain, MonoClass *klass, MonoClassField *field, MonoError *error); + +MonoReflectionMethod* +mono_method_get_object_checked (MonoDomain *domain, MonoMethod *method, MonoClass *refclass, MonoError *error); + +MonoReflectionMethodHandle +mono_method_get_object_handle (MonoDomain *domain, MonoMethod *method, MonoClass *refclass, MonoError *error); + +MonoReflectionProperty* +mono_property_get_object_checked (MonoDomain *domain, MonoClass *klass, MonoProperty *property, MonoError *error); + +MonoReflectionPropertyHandle +mono_property_get_object_handle (MonoDomain *domain, MonoClass *klass, MonoProperty *property, MonoError *error); + +MonoReflectionEventHandle +mono_event_get_object_handle (MonoDomain *domain, MonoClass *klass, MonoEvent *event, MonoError *error); + +MonoReflectionModuleHandle +mono_module_get_object_handle (MonoDomain *domain, MonoImage *image, MonoError *error); + +MonoReflectionModuleHandle +mono_module_file_get_object_handle (MonoDomain *domain, MonoImage *image, int table_index, MonoError *error); + +MonoReflectionMethodBodyHandle +mono_method_body_get_object_handle (MonoDomain *domain, MonoMethod *method, MonoError *error); + +MonoClass * +mono_class_from_mono_type_handle (MonoReflectionTypeHandle h); + + +#endif /* __MONO_METADATA_REFLECTION_INTERNALS_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/reflection.c b/unity-2019.4.24f1-mbe/mono/metadata/reflection.c new file mode 100644 index 000000000..e5c0ec8a3 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/reflection.c @@ -0,0 +1,3023 @@ +/** + * \file + * System.Type icalls and related reflection queries. + * + * Author: + * Paolo Molaro (lupus@ximian.com) + * + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Copyright 2011 Rodrigo Kumpera + * Copyright 2016 Microsoft + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#include +#include "mono/utils/mono-membar.h" +#include "mono/metadata/reflection-internals.h" +#include "mono/metadata/tabledefs.h" +#include "mono/metadata/metadata-internals.h" +#include +#include "mono/metadata/class-internals.h" +#include "mono/metadata/gc-internals.h" +#include "mono/metadata/domain-internals.h" +#include "mono/metadata/opcodes.h" +#include "mono/metadata/assembly.h" +#include "mono/metadata/object-internals.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "image.h" +#include "cil-coff.h" +#include "mono-endian.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void get_default_param_value_blobs (MonoMethod *method, char **blobs, guint32 *types); +static MonoType* mono_reflection_get_type_with_rootimage (MonoImage *rootimage, MonoImage* image, MonoTypeNameParse *info, gboolean ignorecase, gboolean *type_resolve, MonoError *error); + +/* Class lazy loading functions */ +static GENERATE_GET_CLASS_WITH_CACHE (mono_assembly, "System.Reflection", "MonoAssembly") +static GENERATE_GET_CLASS_WITH_CACHE (mono_module, "System.Reflection", "MonoModule") +static GENERATE_GET_CLASS_WITH_CACHE (mono_method, "System.Reflection", "MonoMethod"); +static GENERATE_GET_CLASS_WITH_CACHE (mono_cmethod, "System.Reflection", "MonoCMethod"); +static GENERATE_GET_CLASS_WITH_CACHE (mono_field, "System.Reflection", "MonoField"); +static GENERATE_GET_CLASS_WITH_CACHE (mono_event, "System.Reflection", "MonoEvent"); +static GENERATE_GET_CLASS_WITH_CACHE (mono_property, "System.Reflection", "MonoProperty"); +static GENERATE_GET_CLASS_WITH_CACHE (mono_parameter_info, "System.Reflection", "MonoParameterInfo"); +static GENERATE_GET_CLASS_WITH_CACHE (missing, "System.Reflection", "Missing"); +static GENERATE_GET_CLASS_WITH_CACHE (method_body, "System.Reflection", "MethodBody"); +static GENERATE_GET_CLASS_WITH_CACHE (local_variable_info, "System.Reflection", "LocalVariableInfo"); +static GENERATE_GET_CLASS_WITH_CACHE (exception_handling_clause, "System.Reflection", "ExceptionHandlingClause"); +static GENERATE_GET_CLASS_WITH_CACHE (type_builder, "System.Reflection.Emit", "TypeBuilder"); +static GENERATE_GET_CLASS_WITH_CACHE (dbnull, "System", "DBNull"); + + +static int class_ref_info_handle_count; + +void +mono_reflection_init (void) +{ + mono_reflection_emit_init (); + + mono_counters_register ("MonoClass::ref_info_handle count", + MONO_COUNTER_METADATA | MONO_COUNTER_INT, &class_ref_info_handle_count); + +} + +/* + * mono_class_get_ref_info: + * + * Return the type builder/generic param builder corresponding to KLASS, if it exists. + */ +MonoObjectHandle +mono_class_get_ref_info (MonoClass *klass) +{ + MONO_REQ_GC_UNSAFE_MODE; + guint32 ref_info_handle = mono_class_get_ref_info_handle (klass); + + if (ref_info_handle == 0) + return MONO_HANDLE_NEW (MonoObject, NULL); + else + return mono_gchandle_get_target_handle (ref_info_handle); +} + +gboolean +mono_class_has_ref_info (MonoClass *klass) +{ + MONO_REQ_GC_UNSAFE_MODE; + return 0 != mono_class_get_ref_info_handle (klass); +} + +MonoObject* +mono_class_get_ref_info_raw (MonoClass *klass) +{ + /* FIXME callers of mono_class_get_ref_info_raw should use handles */ + MONO_REQ_GC_UNSAFE_MODE; + guint32 ref_info_handle = mono_class_get_ref_info_handle (klass); + + if (ref_info_handle == 0) + return NULL; + else + return mono_gchandle_get_target (ref_info_handle); +} + +void +mono_class_set_ref_info (MonoClass *klass, MonoObjectHandle obj) +{ + MONO_REQ_GC_UNSAFE_MODE; + + guint32 candidate = mono_gchandle_from_handle (obj, FALSE); + guint32 handle = mono_class_set_ref_info_handle (klass, candidate); + ++class_ref_info_handle_count; + + if (handle != candidate) + mono_gchandle_free (candidate); +} + +void +mono_class_free_ref_info (MonoClass *klass) +{ + MONO_REQ_GC_NEUTRAL_MODE; + guint32 handle = mono_class_get_ref_info_handle (klass); + + if (handle) { + mono_gchandle_free (handle); + mono_class_set_ref_info_handle (klass, 0); + } +} + +/** + * mono_custom_attrs_free: + */ +void +mono_custom_attrs_free (MonoCustomAttrInfo *ainfo) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + if (ainfo && !ainfo->cached) + g_free (ainfo); +} + +gboolean +reflected_equal (gconstpointer a, gconstpointer b) +{ + const ReflectedEntry *ea = (const ReflectedEntry *)a; + const ReflectedEntry *eb = (const ReflectedEntry *)b; + + return (ea->item == eb->item) && (ea->refclass == eb->refclass); +} + +guint +reflected_hash (gconstpointer a) { + const ReflectedEntry *ea = (const ReflectedEntry *)a; + /* Combine hashes for item and refclass. Identical to boost's hash_combine */ + guint seed = mono_aligned_addr_hash (ea->item) + 0x9e3779b9; + seed ^= mono_aligned_addr_hash (ea->refclass) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + return seed; +} + +static void +clear_cached_object (MonoDomain *domain, gpointer o, MonoClass *klass) +{ + mono_domain_lock (domain); + if (domain->refobject_hash) { + ReflectedEntry pe; + gpointer orig_pe, orig_value; + + pe.item = o; + pe.refclass = klass; + + if (mono_conc_g_hash_table_lookup_extended (domain->refobject_hash, &pe, &orig_pe, &orig_value)) { + mono_conc_g_hash_table_remove (domain->refobject_hash, &pe); + free_reflected_entry (orig_pe); + } + } + mono_domain_unlock (domain); +} + +static void +cleanup_refobject_hash (gpointer key, gpointer value, gpointer user_data) +{ + free_reflected_entry (key); +} + +void +mono_reflection_cleanup_domain (MonoDomain *domain) +{ + if (domain->refobject_hash) { + mono_conc_g_hash_table_foreach (domain->refobject_hash, cleanup_refobject_hash, NULL); + mono_conc_g_hash_table_destroy (domain->refobject_hash); + domain->refobject_hash = NULL; + } +} + +/** + * mono_assembly_get_object: + * \param domain an app domain + * \param assembly an assembly + * \returns a \c System.Reflection.Assembly object representing the \c MonoAssembly \p assembly. + */ +MonoReflectionAssembly* +mono_assembly_get_object (MonoDomain *domain, MonoAssembly *assembly) +{ + HANDLE_FUNCTION_ENTER (); + MonoError error; + MonoReflectionAssemblyHandle result = mono_assembly_get_object_handle (domain, assembly, &error); + mono_error_cleanup (&error); /* FIXME new API that doesn't swallow the error */ + HANDLE_FUNCTION_RETURN_OBJ (result); +} + +static MonoReflectionAssemblyHandle +assembly_object_construct (MonoDomain *domain, MonoClass *unused_klass, MonoAssembly *assembly, gpointer user_data, MonoError *error) +{ + error_init (error); + MonoReflectionAssemblyHandle res = MONO_HANDLE_NEW (MonoReflectionAssembly, mono_object_new_checked (domain, mono_class_get_mono_assembly_class (), error)); + return_val_if_nok (error, MONO_HANDLE_CAST (MonoReflectionAssembly, NULL_HANDLE)); + MONO_HANDLE_SETVAL (res, assembly, MonoAssembly*, assembly); + return res; +} + +/* + * mono_assembly_get_object_handle: + * @domain: an app domain + * @assembly: an assembly + * + * Return an System.Reflection.Assembly object representing the MonoAssembly @assembly. + */ +MonoReflectionAssemblyHandle +mono_assembly_get_object_handle (MonoDomain *domain, MonoAssembly *assembly, MonoError *error) +{ + error_init (error); + return CHECK_OR_CONSTRUCT_HANDLE (MonoReflectionAssemblyHandle, assembly, NULL, assembly_object_construct, NULL); +} + +/** + * mono_module_get_object: + */ +MonoReflectionModule* +mono_module_get_object (MonoDomain *domain, MonoImage *image) +{ + HANDLE_FUNCTION_ENTER (); + MonoError error; + MonoReflectionModuleHandle result = mono_module_get_object_handle (domain, image, &error); + mono_error_cleanup (&error); + HANDLE_FUNCTION_RETURN_OBJ (result); +} + +static MonoReflectionModuleHandle +module_object_construct (MonoDomain *domain, MonoClass *unused_klass, MonoImage *image, gpointer user_data, MonoError *error) +{ + char* basename; + + error_init (error); + MonoReflectionModuleHandle res = MONO_HANDLE_NEW (MonoReflectionModule, mono_object_new_checked (domain, mono_class_get_mono_module_class (), error)); + goto_if_nok (error, fail); + + MONO_HANDLE_SETVAL (res, image, MonoImage *, image); + MonoReflectionAssemblyHandle assm_obj = mono_assembly_get_object_handle (domain, image->assembly, error); + goto_if_nok (error, fail); + MONO_HANDLE_SET (res, assembly, assm_obj); + + MONO_HANDLE_SET (res, fqname, mono_string_new_handle (domain, image->name, error)); + goto_if_nok (error, fail); + basename = g_path_get_basename (image->name); + MONO_HANDLE_SET (res, name, mono_string_new_handle (domain, basename, error)); + goto_if_nok (error, fail); + MONO_HANDLE_SET (res, scopename, mono_string_new_handle (domain, image->module_name, error)); + goto_if_nok (error, fail); + + g_free (basename); + + guint32 token = 0; + if (image->assembly->image == image) { + token = mono_metadata_make_token (MONO_TABLE_MODULE, 1); + } else { + int i; + if (image->assembly->image->modules) { + for (i = 0; i < image->assembly->image->module_count; i++) { + if (image->assembly->image->modules [i] == image) + token = mono_metadata_make_token (MONO_TABLE_MODULEREF, i + 1); + } + g_assert (token != 0); + } + } + MONO_HANDLE_SETVAL (res, token, guint32, token); + + return res; +fail: + return MONO_HANDLE_CAST (MonoReflectionModule, NULL_HANDLE); +} + +MonoReflectionModuleHandle +mono_module_get_object_handle (MonoDomain *domain, MonoImage *image, MonoError *error) +{ + error_init (error); + return CHECK_OR_CONSTRUCT_HANDLE (MonoReflectionModuleHandle, image, NULL, module_object_construct, NULL); +} + +/** + * mono_module_file_get_object: + */ +MonoReflectionModule* +mono_module_file_get_object (MonoDomain *domain, MonoImage *image, int table_index) +{ + HANDLE_FUNCTION_ENTER (); + MonoError error; + MonoReflectionModuleHandle result = mono_module_file_get_object_handle (domain, image, table_index, &error); + mono_error_cleanup (&error); + HANDLE_FUNCTION_RETURN_OBJ (result); +} + +MonoReflectionModuleHandle +mono_module_file_get_object_handle (MonoDomain *domain, MonoImage *image, int table_index, MonoError *error) +{ + MonoTableInfo *table; + guint32 cols [MONO_FILE_SIZE]; + const char *name; + guint32 i, name_idx; + const char *val; + + error_init (error); + + MonoReflectionModuleHandle res = MONO_HANDLE_NEW (MonoReflectionModule, mono_object_new_checked (domain, mono_class_get_mono_module_class (), error)); + goto_if_nok (error, fail); + + table = &image->tables [MONO_TABLE_FILE]; + g_assert (table_index < table->rows); + mono_metadata_decode_row (table, table_index, cols, MONO_FILE_SIZE); + + MONO_HANDLE_SETVAL (res, image, MonoImage*, NULL); + MonoReflectionAssemblyHandle assm_obj = mono_assembly_get_object_handle (domain, image->assembly, error); + goto_if_nok (error, fail); + MONO_HANDLE_SET (res, assembly, assm_obj); + name = mono_metadata_string_heap (image, cols [MONO_FILE_NAME]); + + /* Check whenever the row has a corresponding row in the moduleref table */ + table = &image->tables [MONO_TABLE_MODULEREF]; + for (i = 0; i < table->rows; ++i) { + name_idx = mono_metadata_decode_row_col (table, i, MONO_MODULEREF_NAME); + val = mono_metadata_string_heap (image, name_idx); + if (strcmp (val, name) == 0) + MONO_HANDLE_SETVAL (res, image, MonoImage*, image->modules [i]); + } + + MONO_HANDLE_SET (res, fqname, mono_string_new_handle (domain, name, error)); + goto_if_nok (error, fail); + MONO_HANDLE_SET (res, name, mono_string_new_handle (domain, name, error)); + goto_if_nok (error, fail); + MONO_HANDLE_SET (res, scopename, mono_string_new_handle (domain, name, error)); + goto_if_nok (error, fail); + MONO_HANDLE_SETVAL (res, is_resource, MonoBoolean, cols [MONO_FILE_FLAGS] & FILE_CONTAINS_NO_METADATA); + MONO_HANDLE_SETVAL (res, token, guint32, mono_metadata_make_token (MONO_TABLE_FILE, table_index + 1)); + + return res; +fail: + return MONO_HANDLE_CAST (MonoReflectionModule, NULL_HANDLE); +} + +static MonoType* +mono_type_normalize (MonoType *type) +{ + int i; + MonoGenericClass *gclass; + MonoGenericInst *ginst; + MonoClass *gtd; + MonoGenericContainer *gcontainer; + MonoType **argv = NULL; + gboolean is_denorm_gtd = TRUE, requires_rebind = FALSE; + + if (type->type != MONO_TYPE_GENERICINST) + return type; + + gclass = type->data.generic_class; + ginst = gclass->context.class_inst; + if (!ginst->is_open) + return type; + + gtd = gclass->container_class; + gcontainer = mono_class_get_generic_container (gtd); + argv = g_newa (MonoType*, ginst->type_argc); + + for (i = 0; i < ginst->type_argc; ++i) { + MonoType *t = ginst->type_argv [i], *norm; + if (t->type != MONO_TYPE_VAR || t->data.generic_param->num != i || t->data.generic_param->owner != gcontainer) + is_denorm_gtd = FALSE; + norm = mono_type_normalize (t); + argv [i] = norm; + if (norm != t) + requires_rebind = TRUE; + } + + if (is_denorm_gtd) + return type->byref == gtd->byval_arg.byref ? >d->byval_arg : >d->this_arg; + + if (requires_rebind) { + MonoClass *klass = mono_class_bind_generic_parameters (gtd, ginst->type_argc, argv, gclass->is_dynamic); + return type->byref == klass->byval_arg.byref ? &klass->byval_arg : &klass->this_arg; + } + + return type; +} + +/** + * mono_type_get_object: + * \param domain an app domain + * \param type a type + * \returns A \c System.MonoType object representing the type \p type. + */ +MonoReflectionType* +mono_type_get_object (MonoDomain *domain, MonoType *type) +{ + MonoError error; + MonoReflectionType *ret = mono_type_get_object_checked (domain, type, &error); + mono_error_cleanup (&error); + + return ret; +} + +MonoReflectionType* +mono_type_get_object_checked (MonoDomain *domain, MonoType *type, MonoError *error) +{ + MonoType *norm_type; + MonoReflectionType *res; + MonoClass *klass; + + error_init (error); + + g_assert (type != NULL); + klass = mono_class_from_mono_type (type); + + /*we must avoid using @type as it might have come + * from a mono_metadata_type_dup and the caller + * expects that is can be freed. + * Using the right type from + */ + type = klass->byval_arg.byref == type->byref ? &klass->byval_arg : &klass->this_arg; + + /* void is very common */ + if (type->type == MONO_TYPE_VOID && domain->typeof_void) + return (MonoReflectionType*)domain->typeof_void; + + /* + * If the vtable of the given class was already created, we can use + * the MonoType from there and avoid all locking and hash table lookups. + * + * We cannot do this for TypeBuilders as mono_reflection_create_runtime_class expects + * that the resulting object is different. + */ + if (type == &klass->byval_arg && !image_is_dynamic (klass->image)) { + MonoVTable *vtable = mono_class_try_get_vtable (domain, klass); + if (vtable && vtable->type) + return (MonoReflectionType *)vtable->type; + } + + mono_loader_lock (); /*FIXME mono_class_init and mono_class_vtable acquire it*/ + mono_domain_lock (domain); + if (!domain->type_hash) + domain->type_hash = mono_g_hash_table_new_type ((GHashFunc)mono_metadata_type_hash, + (GCompareFunc)mono_metadata_type_equal, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_DOMAIN, domain, "Domain Reflection Type Table"); + if ((res = (MonoReflectionType *)mono_g_hash_table_lookup (domain->type_hash, type))) { + mono_domain_unlock (domain); + mono_loader_unlock (); + return res; + } + + /*Types must be normalized so a generic instance of the GTD get's the same inner type. + * For example in: Foo; Bar : Foo> + * The second Bar will be encoded a generic instance of Bar with as parameter. + * On all other places, Bar will be encoded as the GTD itself. This is an implementation + * artifact of how generics are encoded and should be transparent to managed code so we + * need to weed out this diference when retrieving managed System.Type objects. + */ + norm_type = mono_type_normalize (type); + if (norm_type != type) { + res = mono_type_get_object_checked (domain, norm_type, error); + if (!mono_error_ok (error)) + return NULL; + mono_g_hash_table_insert (domain->type_hash, type, res); + mono_domain_unlock (domain); + mono_loader_unlock (); + return res; + } + + if ((type->type == MONO_TYPE_GENERICINST) && type->data.generic_class->is_dynamic && !type->data.generic_class->container_class->wastypebuilder) { + /* This can happen if a TypeBuilder for a generic class K + * had reflection_create_generic_class) called on it, but not + * ves_icall_TypeBuilder_create_runtime_class. This can happen + * if the K`2 is refernced from a generic instantiation + * (e.g. K) that appears as type argument + * (e.g. Dict>), field (e.g. K + * Foo) or method signature, parent class or any of the above + * in a nested class of some other TypeBuilder. Such an + * occurrence caused mono_reflection_type_get_handle to be + * called on the sre generic instance (K) which + * required the container_class for the generic class K`2 to be + * set up, but the remainder of class construction for K`2 has + * not been done. */ + char * full_name = mono_type_get_full_name (klass); + /* I would have expected ReflectionTypeLoadException, but evidently .NET throws TLE in this case. */ + mono_error_set_type_load_class (error, klass, "TypeBuilder.CreateType() not called for generic class %s", full_name); + g_free (full_name); + mono_domain_unlock (domain); + mono_loader_unlock (); + return NULL; + } + + if (mono_class_has_ref_info (klass) && !klass->wastypebuilder && !type->byref) { + mono_domain_unlock (domain); + mono_loader_unlock (); + return (MonoReflectionType *)mono_class_get_ref_info_raw (klass); /* FIXME use handles */ + } + /* This is stored in vtables/JITted code so it has to be pinned */ + res = (MonoReflectionType *)mono_object_new_pinned (domain, mono_defaults.runtimetype_class, error); + if (!mono_error_ok (error)) + return NULL; + + res->type = type; + 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 (); + return res; +} + +MonoReflectionTypeHandle +mono_type_get_object_handle (MonoDomain *domain, MonoType *type, MonoError *error) +{ + /* NOTE: We happen to know that mono_type_get_object_checked returns + * pinned objects, so we can just wrap its return value in a handle for + * uniformity. If it ever starts returning unpinned, objects, this + * implementation would need to change! + */ + return MONO_HANDLE_NEW (MonoReflectionType, mono_type_get_object_checked (domain, type, error)); +} + +/** + * mono_method_get_object: + * \param domain an app domain + * \param method a method + * \param refclass the reflected type (can be NULL) + * \returns A \c System.Reflection.MonoMethod object representing the method \p method. + */ +MonoReflectionMethod* +mono_method_get_object (MonoDomain *domain, MonoMethod *method, MonoClass *refclass) +{ + HANDLE_FUNCTION_ENTER (); + MonoError error; + MonoReflectionMethodHandle ret = mono_method_get_object_handle (domain, method, refclass, &error); + mono_error_cleanup (&error); + HANDLE_FUNCTION_RETURN_OBJ (ret); +} + +static MonoReflectionMethodHandle +method_object_construct (MonoDomain *domain, MonoClass *refclass, MonoMethod *method, gpointer user_data, MonoError *error) +{ + error_init (error); + g_assert (refclass != NULL); + /* + * We use the same C representation for methods and constructors, but the type + * name in C# is different. + */ + MonoClass *klass; + + error_init (error); + + if (*method->name == '.' && (strcmp (method->name, ".ctor") == 0 || strcmp (method->name, ".cctor") == 0)) { + klass = mono_class_get_mono_cmethod_class (); + } + else { + klass = mono_class_get_mono_method_class (); + } + MonoReflectionMethodHandle ret = MONO_HANDLE_NEW (MonoReflectionMethod, mono_object_new_checked (domain, klass, error)); + goto_if_nok (error, fail); + MONO_HANDLE_SETVAL (ret, method, MonoMethod*, method); + + MonoReflectionTypeHandle rt = mono_type_get_object_handle (domain, &refclass->byval_arg, error); + goto_if_nok (error, fail); + + MONO_HANDLE_SET (ret, reftype, rt); + + return ret; + +fail: + return MONO_HANDLE_CAST (MonoReflectionMethod, NULL_HANDLE); +} + +/* + * mono_method_get_object_handle: + * @domain: an app domain + * @method: a method + * @refclass: the reflected type (can be NULL) + * @error: set on error. + * + * Return an System.Reflection.MonoMethod object representing the method @method. + * Returns NULL and sets @error on error. + */ +MonoReflectionMethodHandle +mono_method_get_object_handle (MonoDomain *domain, MonoMethod *method, MonoClass *refclass, MonoError *error) +{ + error_init (error); + if (!refclass) + refclass = method->klass; + + return CHECK_OR_CONSTRUCT_HANDLE (MonoReflectionMethodHandle, method, refclass, method_object_construct, NULL); +} +/* + * mono_method_get_object_checked: + * @domain: an app domain + * @method: a method + * @refclass: the reflected type (can be NULL) + * @error: set on error. + * + * Return an System.Reflection.MonoMethod object representing the method @method. + * Returns NULL and sets @error on error. + */ +MonoReflectionMethod* +mono_method_get_object_checked (MonoDomain *domain, MonoMethod *method, MonoClass *refclass, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + MonoReflectionMethodHandle result = mono_method_get_object_handle (domain, method, refclass, error); + HANDLE_FUNCTION_RETURN_OBJ (result); +} + +/* + * mono_method_clear_object: + * + * Clear the cached reflection objects for the dynamic method METHOD. + */ +void +mono_method_clear_object (MonoDomain *domain, MonoMethod *method) +{ + MonoClass *klass; + g_assert (method_is_dynamic (method)); + + klass = method->klass; + while (klass) { + clear_cached_object (domain, method, klass); + klass = klass->parent; + } + /* Added by mono_param_get_objects () */ + clear_cached_object (domain, &(method->signature), NULL); + klass = method->klass; + while (klass) { + clear_cached_object (domain, &(method->signature), klass); + klass = klass->parent; + } +} + +/** + * mono_field_get_object: + * \param domain an app domain + * \param klass a type + * \param field a field + * \returns A \c System.Reflection.MonoField object representing the field \p field + * in class \p klass. + */ +MonoReflectionField* +mono_field_get_object (MonoDomain *domain, MonoClass *klass, MonoClassField *field) +{ + HANDLE_FUNCTION_ENTER (); + MonoError error; + MonoReflectionFieldHandle result = mono_field_get_object_handle (domain, klass, field, &error); + mono_error_cleanup (&error); + HANDLE_FUNCTION_RETURN_OBJ (result); +} + +static MonoReflectionFieldHandle +field_object_construct (MonoDomain *domain, MonoClass *klass, MonoClassField *field, gpointer user_data, MonoError *error) +{ + error_init (error); + + MonoReflectionFieldHandle res = MONO_HANDLE_NEW (MonoReflectionField, mono_object_new_checked (domain, mono_class_get_mono_field_class (), error)); + goto_if_nok (error, fail); + MONO_HANDLE_SETVAL (res, klass, MonoClass *, klass); + MONO_HANDLE_SETVAL (res, field, MonoClassField *, field); + MonoStringHandle name = mono_string_new_handle (domain, mono_field_get_name (field), error); + goto_if_nok (error, fail); + MONO_HANDLE_SET (res, name, name); + + if (field->type) { + MonoReflectionTypeHandle rt = mono_type_get_object_handle (domain, field->type, error); + goto_if_nok (error, fail); + + MONO_HANDLE_SET (res, type, rt); + } + MONO_HANDLE_SETVAL (res, attrs, guint32, mono_field_get_flags (field)); + return res; +fail: + return MONO_HANDLE_CAST (MonoReflectionField, NULL_HANDLE); +} + +/* + * mono_field_get_object_handle: + * @domain: an app domain + * @klass: a type + * @field: a field + * @error: set on error + * + * Return an System.Reflection.MonoField object representing the field @field + * in class @klass. On error, returns NULL and sets @error. + */ +MonoReflectionFieldHandle +mono_field_get_object_handle (MonoDomain *domain, MonoClass *klass, MonoClassField *field, MonoError *error) +{ + error_init (error); + return CHECK_OR_CONSTRUCT_HANDLE (MonoReflectionFieldHandle, field, klass, field_object_construct, NULL); +} + + +/* + * mono_field_get_object_checked: + * @domain: an app domain + * @klass: a type + * @field: a field + * @error: set on error + * + * Return an System.Reflection.MonoField object representing the field @field + * in class @klass. On error, returns NULL and sets @error. + */ +MonoReflectionField* +mono_field_get_object_checked (MonoDomain *domain, MonoClass *klass, MonoClassField *field, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + MonoReflectionFieldHandle result = mono_field_get_object_handle (domain, klass, field, error); + HANDLE_FUNCTION_RETURN_OBJ (result); +} + +/* + * mono_property_get_object: + * @domain: an app domain + * @klass: a type + * @property: a property + * + * Return an System.Reflection.MonoProperty object representing the property @property + * in class @klass. + */ +MonoReflectionProperty* +mono_property_get_object (MonoDomain *domain, MonoClass *klass, MonoProperty *property) +{ + HANDLE_FUNCTION_ENTER (); + MonoError error; + MonoReflectionPropertyHandle result = mono_property_get_object_handle (domain, klass, property, &error); + mono_error_cleanup (&error); + HANDLE_FUNCTION_RETURN_OBJ (result); +} + +static MonoReflectionPropertyHandle +property_object_construct (MonoDomain *domain, MonoClass *klass, MonoProperty *property, gpointer user_data, MonoError *error) +{ + error_init (error); + + MonoReflectionPropertyHandle res = MONO_HANDLE_NEW (MonoReflectionProperty, mono_object_new_checked (domain, mono_class_get_mono_property_class (), error)); + goto_if_nok (error, fail); + MONO_HANDLE_SETVAL (res, klass, MonoClass *, klass); + MONO_HANDLE_SETVAL (res, property, MonoProperty *, property); + return res; +fail: + return MONO_HANDLE_CAST (MonoReflectionProperty, NULL_HANDLE); +} + +/** + * mono_property_get_object_handle: + * \param domain an app domain + * \param klass a type + * \param property a property + * \param error set on error + * + * \returns A \c System.Reflection.MonoProperty object representing the property \p property + * in class \p klass. On error returns NULL and sets \p error. + */ +MonoReflectionPropertyHandle +mono_property_get_object_handle (MonoDomain *domain, MonoClass *klass, MonoProperty *property, MonoError *error) +{ + return CHECK_OR_CONSTRUCT_HANDLE (MonoReflectionPropertyHandle, property, klass, property_object_construct, NULL); +} + +/** + * mono_property_get_object: + * \param domain an app domain + * \param klass a type + * \param property a property + * \param error set on error + * \returns a \c System.Reflection.MonoProperty object representing the property \p property + * in class \p klass. On error returns NULL and sets \p error. + */ +MonoReflectionProperty* +mono_property_get_object_checked (MonoDomain *domain, MonoClass *klass, MonoProperty *property, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + MonoReflectionPropertyHandle res = mono_property_get_object_handle (domain, klass, property, error); + HANDLE_FUNCTION_RETURN_OBJ (res); +} + +/** + * mono_event_get_object: + * \param domain an app domain + * \param klass a type + * \param event a event + * \returns A \c System.Reflection.MonoEvent object representing the event \p event + * in class \p klass. + */ +MonoReflectionEvent* +mono_event_get_object (MonoDomain *domain, MonoClass *klass, MonoEvent *event) +{ + HANDLE_FUNCTION_ENTER (); + MonoError error; + MonoReflectionEventHandle result = mono_event_get_object_handle (domain, klass, event, &error); + mono_error_cleanup (&error); + HANDLE_FUNCTION_RETURN_OBJ (result); +} + +static MonoReflectionEventHandle +event_object_construct (MonoDomain *domain, MonoClass *klass, MonoEvent *event, gpointer user_data, MonoError *error) +{ + + error_init (error); + MonoReflectionMonoEventHandle mono_event = MONO_HANDLE_NEW (MonoReflectionMonoEvent, mono_object_new_checked (domain, mono_class_get_mono_event_class (), error)); + if (!is_ok (error)) + return MONO_HANDLE_CAST (MonoReflectionEvent, NULL_HANDLE); + MONO_HANDLE_SETVAL (mono_event, klass, MonoClass* , klass); + MONO_HANDLE_SETVAL (mono_event, event, MonoEvent* , event); + return MONO_HANDLE_CAST (MonoReflectionEvent, mono_event); +} + +/** + * mono_event_get_object_handle: + * \param domain an app domain + * \param klass a type + * \param event a event + * \param error set on error + * \returns a \c System.Reflection.MonoEvent object representing the event \p event + * in class \p klass. On failure sets \p error and returns NULL + */ +MonoReflectionEventHandle +mono_event_get_object_handle (MonoDomain *domain, MonoClass *klass, MonoEvent *event, MonoError *error) +{ + error_init (error); + return CHECK_OR_CONSTRUCT_HANDLE (MonoReflectionEventHandle, event, klass, event_object_construct, NULL); +} + + +/** + * mono_get_reflection_missing_object: + * \param domain Domain where the object lives + * + * \returns the \c System.Reflection.Missing.Value singleton object + * (of type \c System.Reflection.Missing). + * + * Used as the value for \c ParameterInfo.DefaultValue when Optional + * is present + */ +static MonoObjectHandle +mono_get_reflection_missing_object (MonoDomain *domain) +{ + MonoError error; + static MonoClassField *missing_value_field = NULL; + + if (!missing_value_field) { + MonoClass *missing_klass; + missing_klass = mono_class_get_missing_class (); + mono_class_init (missing_klass); + missing_value_field = mono_class_get_field_from_name (missing_klass, "Value"); + g_assert (missing_value_field); + } + /* FIXME change mono_field_get_value_object_checked to return a handle */ + MonoObjectHandle obj = MONO_HANDLE_NEW (MonoObject, mono_field_get_value_object_checked (domain, missing_value_field, NULL, &error)); + mono_error_assert_ok (&error); + return obj; +} + +static MonoObjectHandle +get_dbnull_object (MonoDomain *domain, MonoError *error) +{ + static MonoClassField *dbnull_value_field = NULL; + + error_init (error); + + if (!dbnull_value_field) { + MonoClass *dbnull_klass; + dbnull_klass = mono_class_get_dbnull_class (); + dbnull_value_field = mono_class_get_field_from_name (dbnull_klass, "Value"); + g_assert (dbnull_value_field); + } + /* FIXME change mono_field_get_value_object_checked to return a handle */ + MonoObjectHandle obj = MONO_HANDLE_NEW (MonoObject, mono_field_get_value_object_checked (domain, dbnull_value_field, NULL, error)); + return obj; +} + +static MonoObjectHandle +get_dbnull (MonoDomain *domain, MonoObjectHandle dbnull, MonoError *error) +{ + error_init (error); + if (MONO_HANDLE_IS_NULL (dbnull)) + MONO_HANDLE_ASSIGN (dbnull, get_dbnull_object (domain, error)); + return dbnull; +} + +static MonoObjectHandle +get_reflection_missing (MonoDomain *domain, MonoObjectHandleOut reflection_missing) +{ + if (MONO_HANDLE_IS_NULL (reflection_missing)) + MONO_HANDLE_ASSIGN (reflection_missing, mono_get_reflection_missing_object (domain)); + return reflection_missing; +} + +static gboolean +add_parameter_object_to_array (MonoDomain *domain, MonoMethod *method, MonoObjectHandle member, int idx, const char *name, MonoType *sig_param, guint32 blob_type_enum, const char *blob, MonoMarshalSpec *mspec, MonoObjectHandle missing, MonoObjectHandle dbnull, MonoArrayHandle dest, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + error_init (error); + MonoReflectionParameterHandle param = MONO_HANDLE_NEW (MonoReflectionParameter, mono_object_new_checked (domain, mono_class_get_mono_parameter_info_class (), error)); + goto_if_nok (error, leave); + + MonoReflectionTypeHandle rt = mono_type_get_object_handle (domain, sig_param, error); + goto_if_nok (error, leave); + + MONO_HANDLE_SET (param, ClassImpl, rt); + + MONO_HANDLE_SET (param, MemberImpl, member); + + MonoStringHandle name_str = mono_string_new_handle (domain, name, error); + goto_if_nok (error, leave); + + MONO_HANDLE_SET (param, NameImpl, name_str); + + MONO_HANDLE_SETVAL (param, PositionImpl, gint32, idx); + + MONO_HANDLE_SETVAL (param, AttrsImpl, guint32, sig_param->attrs); + + if (!(sig_param->attrs & PARAM_ATTRIBUTE_HAS_DEFAULT)) { + if (sig_param->attrs & PARAM_ATTRIBUTE_OPTIONAL) + MONO_HANDLE_SET (param, DefaultValueImpl, get_reflection_missing (domain, missing)); + else + MONO_HANDLE_SET (param, DefaultValueImpl, get_dbnull (domain, dbnull, error)); + goto_if_nok (error, leave); + } else { + + MonoType blob_type; + + blob_type.type = (MonoTypeEnum)blob_type_enum; + blob_type.data.klass = NULL; + if (blob_type_enum == MONO_TYPE_CLASS) + blob_type.data.klass = mono_defaults.object_class; + else if ((sig_param->type == MONO_TYPE_VALUETYPE) && sig_param->data.klass->enumtype) { + /* For enums, types [i] contains the base type */ + + blob_type.type = MONO_TYPE_VALUETYPE; + blob_type.data.klass = mono_class_from_mono_type (sig_param); + } else + blob_type.data.klass = mono_class_from_mono_type (&blob_type); + + MonoObjectHandle default_val_obj = MONO_HANDLE_NEW (MonoObject, mono_get_object_from_blob (domain, &blob_type, blob, error)); /* FIXME make mono_get_object_from_blob return a handle */ + goto_if_nok (error, leave); + MONO_HANDLE_SET (param, DefaultValueImpl, default_val_obj); + + /* Type in the Constant table is MONO_TYPE_CLASS for nulls */ + if (blob_type_enum != MONO_TYPE_CLASS && MONO_HANDLE_IS_NULL(default_val_obj)) { + if (sig_param->attrs & PARAM_ATTRIBUTE_OPTIONAL) + MONO_HANDLE_SET (param, DefaultValueImpl, get_reflection_missing (domain, missing)); + else + MONO_HANDLE_SET (param, DefaultValueImpl, get_dbnull (domain, dbnull, error)); + goto_if_nok (error, leave); + } + } + + if (mspec) { + MonoReflectionMarshalAsAttributeHandle mobj = mono_reflection_marshal_as_attribute_from_marshal_spec (domain, method->klass, mspec, error); + goto_if_nok (error, leave); + MONO_HANDLE_SET (param, MarshalAsImpl, mobj); + } + + MONO_HANDLE_ARRAY_SETREF (dest, idx, param); + +leave: + HANDLE_FUNCTION_RETURN_VAL (is_ok (error)); +} + +static MonoArrayHandle +param_objects_construct (MonoDomain *domain, MonoClass *refclass, MonoMethodSignature **addr_of_sig, gpointer user_data, MonoError *error) +{ + MonoMethod *method = (MonoMethod*)user_data; + MonoMethodSignature *sig = *addr_of_sig; /* see note in mono_param_get_objects_internal */ + + MonoArrayHandle res = MONO_HANDLE_NEW (MonoArray, NULL); + char **names = NULL, **blobs = NULL; + guint32 *types = NULL; + MonoMarshalSpec **mspecs = NULL; + int i; + + error_init (error); + + MonoReflectionMethodHandle member = mono_method_get_object_handle (domain, method, refclass, error); + goto_if_nok (error, leave); + names = g_new (char *, sig->param_count); + mono_method_get_param_names (method, (const char **) names); + + mspecs = g_new (MonoMarshalSpec*, sig->param_count + 1); + mono_method_get_marshal_info (method, mspecs); + + res = mono_array_new_handle (domain, mono_class_get_mono_parameter_info_class (), sig->param_count, error); + if (!res) + goto leave; + + gboolean any_default_value = FALSE; + for (i = 0; i < sig->param_count; ++i) { + if ((sig->params [i]->attrs & PARAM_ATTRIBUTE_HAS_DEFAULT) != 0) { + any_default_value = TRUE; + break; + } + } + if (any_default_value) { + blobs = g_new0 (char *, sig->param_count); + types = g_new0 (guint32, sig->param_count); + get_default_param_value_blobs (method, blobs, types); + } + + /* Handles missing and dbnull are assigned in add_parameter_object_to_array when needed */ + MonoObjectHandle missing = MONO_HANDLE_NEW (MonoObject, NULL); + MonoObjectHandle dbnull = MONO_HANDLE_NEW (MonoObject, NULL); + for (i = 0; i < sig->param_count; ++i) { + if (!add_parameter_object_to_array (domain, method, MONO_HANDLE_CAST(MonoObject, member), i, names[i], sig->params[i], types ? types[i] : 0, blobs ? blobs[i] : NULL, mspecs [i + 1], missing, dbnull, res, error)) + goto leave; + } + +leave: + g_free (names); + g_free (blobs); + g_free (types); + + if (sig && mspecs) { + for (i = sig->param_count; i >= 0; i--) { + if (mspecs [i]) + mono_metadata_free_marshal_spec (mspecs [i]); + } + } + g_free (mspecs); + + if (!is_ok (error)) + return NULL; + + return res; +} + +/* + * mono_param_get_objects: + * @domain: an app domain + * @method: a method + * + * Return an System.Reflection.ParameterInfo array object representing the parameters + * in the method @method. + */ +MonoArrayHandle +mono_param_get_objects_internal (MonoDomain *domain, MonoMethod *method, MonoClass *refclass, MonoError *error) +{ + error_init (error); + + /* side-effect: sets method->signature non-NULL on success */ + MonoMethodSignature *sig = mono_method_signature_checked (method, error); + goto_if_nok (error, fail); + + if (!sig->param_count) { + MonoArrayHandle res = mono_array_new_handle (domain, mono_class_get_mono_parameter_info_class (), 0, error); + goto_if_nok (error, fail); + + 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. + */ + return CHECK_OR_CONSTRUCT_HANDLE (MonoArrayHandle, &method->signature, refclass, param_objects_construct, method); +fail: + return MONO_HANDLE_NEW (MonoArray, NULL_HANDLE); +} + +/** + * mono_param_get_objects: + */ +MonoArray* +mono_param_get_objects (MonoDomain *domain, MonoMethod *method) +{ + HANDLE_FUNCTION_ENTER (); + MonoError error; + MonoArrayHandle result = mono_param_get_objects_internal (domain, method, NULL, &error); + mono_error_assert_ok (&error); + HANDLE_FUNCTION_RETURN_OBJ (result); +} + +static gboolean +add_local_var_info_to_array (MonoDomain *domain, MonoMethodHeader *header, int idx, MonoArrayHandle dest, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + error_init (error); + MonoReflectionLocalVariableInfoHandle info = MONO_HANDLE_NEW (MonoReflectionLocalVariableInfo, mono_object_new_checked (domain, mono_class_get_local_variable_info_class (), error)); + goto_if_nok (error, leave); + + MonoReflectionTypeHandle rt = mono_type_get_object_handle (domain, header->locals [idx], error); + goto_if_nok (error, leave); + + MONO_HANDLE_SET (info, local_type, rt); + + MONO_HANDLE_SETVAL (info, is_pinned, MonoBoolean, header->locals [idx]->pinned); + MONO_HANDLE_SETVAL (info, local_index, guint16, idx); + + MONO_HANDLE_ARRAY_SETREF (dest, idx, info); + +leave: + HANDLE_FUNCTION_RETURN_VAL (is_ok (error)); +} + +static gboolean +add_exception_handling_clause_to_array (MonoDomain *domain, MonoMethodHeader *header, int idx, MonoArrayHandle dest, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + error_init (error); + MonoReflectionExceptionHandlingClauseHandle info = MONO_HANDLE_NEW (MonoReflectionExceptionHandlingClause, mono_object_new_checked (domain, mono_class_get_exception_handling_clause_class (), error)); + goto_if_nok (error, leave); + MonoExceptionClause *clause = &header->clauses [idx]; + + MONO_HANDLE_SETVAL (info, flags, gint32, clause->flags); + MONO_HANDLE_SETVAL (info, try_offset, gint32, clause->try_offset); + MONO_HANDLE_SETVAL (info, try_length, gint32, clause->try_len); + MONO_HANDLE_SETVAL (info, handler_offset, gint32, clause->handler_offset); + MONO_HANDLE_SETVAL (info, handler_length, gint32, clause->handler_len); + if (clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) + MONO_HANDLE_SETVAL (info, filter_offset, gint32, clause->data.filter_offset); + else if (clause->data.catch_class) { + MonoReflectionTypeHandle rt = mono_type_get_object_handle (mono_domain_get (), &clause->data.catch_class->byval_arg, error); + goto_if_nok (error, leave); + + MONO_HANDLE_SET (info, catch_type, rt); + } + + MONO_HANDLE_ARRAY_SETREF (dest, idx, info); +leave: + HANDLE_FUNCTION_RETURN_VAL (is_ok (error)); +} + +/** + * mono_method_body_get_object: + * \param domain an app domain + * \param method a method + * \return A \c System.Reflection.MethodBody object representing the method \p method. + */ +MonoReflectionMethodBody* +mono_method_body_get_object (MonoDomain *domain, MonoMethod *method) +{ + HANDLE_FUNCTION_ENTER (); + MonoError error; + MonoReflectionMethodBodyHandle result = mono_method_body_get_object_handle (domain, method, &error); + mono_error_cleanup (&error); + HANDLE_FUNCTION_RETURN_OBJ (result); +} + +/* WARNING: This method can return NULL on sucess */ +static MonoReflectionMethodBodyHandle +method_body_object_construct (MonoDomain *domain, MonoClass *unused_class, MonoMethod *method, gpointer user_data, MonoError *error) +{ + MonoMethodHeader *header = NULL; + MonoImage *image; + guint32 method_rva, local_var_sig_token; + char *ptr; + unsigned char format, flags; + int i; + + error_init (error); + + /* for compatibility with .net */ + if (method_is_dynamic (method)) { + mono_error_set_generic_error (error, "System", "InvalidOperationException", ""); + goto fail; + } + + if ((method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) || + (method->flags & METHOD_ATTRIBUTE_ABSTRACT) || + (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) || + (method->klass->image->raw_data && method->klass->image->raw_data [1] != 'Z') || + (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME)) + return MONO_HANDLE_CAST (MonoReflectionMethodBody, NULL_HANDLE); + + image = method->klass->image; + header = mono_method_get_header_checked (method, error); + goto_if_nok (error, fail); + + if (!image_is_dynamic (image)) { + /* Obtain local vars signature token */ + method_rva = mono_metadata_decode_row_col (&image->tables [MONO_TABLE_METHOD], mono_metadata_token_index (method->token) - 1, MONO_METHOD_RVA); + ptr = mono_image_rva_map (image, method_rva); + flags = *(const unsigned char *) ptr; + format = flags & METHOD_HEADER_FORMAT_MASK; + switch (format){ + case METHOD_HEADER_TINY_FORMAT: + local_var_sig_token = 0; + break; + case METHOD_HEADER_FAT_FORMAT: + ptr += 2; + ptr += 2; + ptr += 4; + local_var_sig_token = read32 (ptr); + break; + default: + g_assert_not_reached (); + } + } else + local_var_sig_token = 0; //FIXME + + MonoReflectionMethodBodyHandle ret = MONO_HANDLE_NEW (MonoReflectionMethodBody, mono_object_new_checked (domain, mono_class_get_method_body_class (), error)); + goto_if_nok (error, fail); + + MONO_HANDLE_SETVAL (ret, init_locals, MonoBoolean, header->init_locals); + MONO_HANDLE_SETVAL (ret, max_stack, guint32, header->max_stack); + MONO_HANDLE_SETVAL (ret, local_var_sig_token, guint32, local_var_sig_token); + MonoArrayHandle il_arr = mono_array_new_handle (domain, mono_defaults.byte_class, header->code_size, error); + goto_if_nok (error, fail); + MONO_HANDLE_SET (ret, il, il_arr); + uint32_t il_gchandle; + guint8* il_data = MONO_ARRAY_HANDLE_PIN (il_arr, guint8, 0, &il_gchandle); + memcpy (il_data, header->code, header->code_size); + mono_gchandle_free (il_gchandle); + + /* Locals */ + MonoArrayHandle locals_arr = mono_array_new_handle (domain, mono_class_get_local_variable_info_class (), header->num_locals, error); + goto_if_nok (error, fail); + MONO_HANDLE_SET (ret, locals, locals_arr); + for (i = 0; i < header->num_locals; ++i) { + if (!add_local_var_info_to_array (domain, header, i, locals_arr, error)) + goto fail; + } + + /* Exceptions */ + MonoArrayHandle exn_clauses = mono_array_new_handle (domain, mono_class_get_exception_handling_clause_class (), header->num_clauses, error); + goto_if_nok (error, fail); + MONO_HANDLE_SET (ret, clauses, exn_clauses); + for (i = 0; i < header->num_clauses; ++i) { + if (!add_exception_handling_clause_to_array (domain, header, i, exn_clauses, error)) + goto fail; + } + + mono_metadata_free_mh (header); + return ret; +fail: + if (header) + mono_metadata_free_mh (header); + return NULL; +} + +/** + * mono_method_body_get_object_handle: + * \param domain an app domain + * \param method a method + * \param error set on error + * \returns a \c System.Reflection.MethodBody object representing the + * method \p method. On failure, returns NULL and sets \p error. + */ +MonoReflectionMethodBodyHandle +mono_method_body_get_object_handle (MonoDomain *domain, MonoMethod *method, MonoError *error) +{ + error_init (error); + return CHECK_OR_CONSTRUCT_HANDLE (MonoReflectionMethodBodyHandle, method, NULL, method_body_object_construct, NULL); +} + + +/** + * mono_get_dbnull_object: + * \param domain Domain where the object lives + * Used as the value for \c ParameterInfo.DefaultValue + * \returns the \c System.DBNull.Value singleton object + */ +MonoObject * +mono_get_dbnull_object (MonoDomain *domain) +{ + HANDLE_FUNCTION_ENTER (); + MonoError error; + MonoObjectHandle obj = get_dbnull_object (domain, &error); + mono_error_assert_ok (&error); + HANDLE_FUNCTION_RETURN_OBJ (obj); +} + +static void +get_default_param_value_blobs (MonoMethod *method, char **blobs, guint32 *types) +{ + guint32 param_index, i, lastp, crow = 0; + guint32 param_cols [MONO_PARAM_SIZE], const_cols [MONO_CONSTANT_SIZE]; + gint32 idx; + + MonoClass *klass = method->klass; + MonoImage *image = klass->image; + MonoMethodSignature *methodsig = mono_method_signature (method); + + MonoTableInfo *constt; + MonoTableInfo *methodt; + MonoTableInfo *paramt; + + if (!methodsig->param_count) + return; + + mono_class_init (klass); + + if (image_is_dynamic (klass->image)) { + MonoReflectionMethodAux *aux; + if (method->is_inflated) + method = ((MonoMethodInflated*)method)->declaring; + aux = (MonoReflectionMethodAux *)g_hash_table_lookup (((MonoDynamicImage*)method->klass->image)->method_aux_hash, method); + if (aux && aux->param_defaults) { + memcpy (blobs, &(aux->param_defaults [1]), methodsig->param_count * sizeof (char*)); + memcpy (types, &(aux->param_default_types [1]), methodsig->param_count * sizeof (guint32)); + } + return; + } + + methodt = &klass->image->tables [MONO_TABLE_METHOD]; + paramt = &klass->image->tables [MONO_TABLE_PARAM]; + constt = &image->tables [MONO_TABLE_CONSTANT]; + + idx = mono_method_get_index (method) - 1; + g_assert (idx != -1); + + param_index = mono_metadata_decode_row_col (methodt, idx, MONO_METHOD_PARAMLIST); + if (idx + 1 < methodt->rows) + lastp = mono_metadata_decode_row_col (methodt, idx + 1, MONO_METHOD_PARAMLIST); + else + lastp = paramt->rows + 1; + + for (i = param_index; i < lastp; ++i) { + guint32 paramseq; + + mono_metadata_decode_row (paramt, i - 1, param_cols, MONO_PARAM_SIZE); + paramseq = param_cols [MONO_PARAM_SEQUENCE]; + + if (!(param_cols [MONO_PARAM_FLAGS] & PARAM_ATTRIBUTE_HAS_DEFAULT)) + continue; + + crow = mono_metadata_get_constant_index (image, MONO_TOKEN_PARAM_DEF | i, crow + 1); + if (!crow) { + continue; + } + + mono_metadata_decode_row (constt, crow - 1, const_cols, MONO_CONSTANT_SIZE); + blobs [paramseq - 1] = (char *)mono_metadata_blob_heap (image, const_cols [MONO_CONSTANT_VALUE]); + types [paramseq - 1] = const_cols [MONO_CONSTANT_TYPE]; + } + + return; +} + +MonoObject * +mono_get_object_from_blob (MonoDomain *domain, MonoType *type, const char *blob, MonoError *error) +{ + void *retval; + MonoClass *klass; + MonoObject *object; + MonoType *basetype = type; + + error_init (error); + + if (!blob) + return NULL; + + klass = mono_class_from_mono_type (type); + if (klass->valuetype) { + object = mono_object_new_checked (domain, klass, error); + return_val_if_nok (error, NULL); + retval = ((gchar *) object + sizeof (MonoObject)); + if (klass->enumtype) + basetype = mono_class_enum_basetype (klass); + } else { + retval = &object; + } + + if (!mono_get_constant_value_from_blob (domain, basetype->type, blob, retval, error)) + return object; + else + return NULL; +} + +static int +assembly_name_to_aname (MonoAssemblyName *assembly, char *p) { + int found_sep; + char *s; + gboolean quoted = FALSE; + + memset (assembly, 0, sizeof (MonoAssemblyName)); + assembly->culture = ""; + memset (assembly->public_key_token, 0, MONO_PUBLIC_KEY_TOKEN_LENGTH); + + if (*p == '"') { + quoted = TRUE; + p++; + } + assembly->name = p; + while (*p && (isalnum (*p) || *p == '.' || *p == '-' || *p == '_' || *p == '$' || *p == '@' || g_ascii_isspace (*p))) + p++; + if (quoted) { + if (*p != '"') + return 1; + *p = 0; + p++; + } + if (*p != ',') + return 1; + *p = 0; + /* Remove trailing whitespace */ + s = p - 1; + while (*s && g_ascii_isspace (*s)) + *s-- = 0; + p ++; + while (g_ascii_isspace (*p)) + p++; + while (*p) { + if (*p == 'V' && g_ascii_strncasecmp (p, "Version=", 8) == 0) { + p += 8; + assembly->major = strtoul (p, &s, 10); + if (s == p || *s != '.') + return 1; + p = ++s; + assembly->minor = strtoul (p, &s, 10); + if (s == p || *s != '.') + return 1; + p = ++s; + assembly->build = strtoul (p, &s, 10); + if (s == p || *s != '.') + return 1; + p = ++s; + assembly->revision = strtoul (p, &s, 10); + if (s == p) + return 1; + p = s; + } else if (*p == 'C' && g_ascii_strncasecmp (p, "Culture=", 8) == 0) { + p += 8; + if (g_ascii_strncasecmp (p, "neutral", 7) == 0) { + assembly->culture = ""; + p += 7; + } else { + assembly->culture = p; + while (*p && *p != ',') { + p++; + } + } + } else if (*p == 'P' && g_ascii_strncasecmp (p, "PublicKeyToken=", 15) == 0) { + p += 15; + if (strncmp (p, "null", 4) == 0) { + p += 4; + } else { + int len; + gchar *start = p; + while (*p && *p != ',') { + p++; + } + len = (p - start + 1); + if (len > MONO_PUBLIC_KEY_TOKEN_LENGTH) + len = MONO_PUBLIC_KEY_TOKEN_LENGTH; + g_strlcpy ((char*)assembly->public_key_token, start, len); + } + } else { + while (*p && *p != ',') + p++; + } + found_sep = 0; + while (g_ascii_isspace (*p) || *p == ',') { + *p++ = 0; + found_sep = 1; + continue; + } + /* failed */ + if (!found_sep) + return 1; + } + + return 0; +} + +/* + * mono_reflection_parse_type: + * @name: type name + * + * Parse a type name as accepted by the GetType () method and output the info + * extracted in the info structure. + * the name param will be mangled, so, make a copy before passing it to this function. + * The fields in info will be valid until the memory pointed to by name is valid. + * + * See also mono_type_get_name () below. + * + * Returns: 0 on parse error. + */ +static int +_mono_reflection_parse_type (char *name, char **endptr, gboolean is_recursed, + MonoTypeNameParse *info) +{ + char *start, *p, *w, *last_point, *startn; + int in_modifiers = 0; + int isbyref = 0, rank = 0, isptr = 0; + + start = p = w = name; + + memset (info, 0, sizeof (MonoTypeNameParse)); + + /* last_point separates the namespace from the name */ + last_point = NULL; + /* Skips spaces */ + while (*p == ' ') p++, start++, w++, name++; + + while (*p) { + switch (*p) { + case '+': + *p = 0; /* NULL terminate the name */ + startn = p + 1; + info->nested = g_list_append (info->nested, startn); + /* we have parsed the nesting namespace + name */ + if (info->name) + break; + if (last_point) { + info->name_space = start; + *last_point = 0; + info->name = last_point + 1; + } else { + info->name_space = (char *)""; + info->name = start; + } + break; + case '.': + last_point = p; + break; + case '\\': + ++p; + break; + case '&': + case '*': + case '[': + case ',': + case ']': + in_modifiers = 1; + break; + default: + break; + } + if (in_modifiers) + break; + // *w++ = *p++; + p++; + } + + if (!info->name) { + if (last_point) { + info->name_space = start; + *last_point = 0; + info->name = last_point + 1; + } else { + info->name_space = (char *)""; + info->name = start; + } + } + while (*p) { + switch (*p) { + case '&': + if (isbyref) /* only one level allowed by the spec */ + return 0; + isbyref = 1; + isptr = 0; + info->modifiers = g_list_append (info->modifiers, GUINT_TO_POINTER (0)); + *p++ = 0; + break; + case '*': + if (isbyref) /* pointer to ref not okay */ + return 0; + info->modifiers = g_list_append (info->modifiers, GUINT_TO_POINTER (-1)); + isptr = 1; + *p++ = 0; + break; + case '[': + if (isbyref) /* array of ref and generic ref are not okay */ + return 0; + //Decide if it's an array of a generic argument list + *p++ = 0; + + if (!*p) //XXX test + return 0; + if (*p == ',' || *p == '*' || *p == ']') { //array + isptr = 0; + rank = 1; + while (*p) { + if (*p == ']') + break; + if (*p == ',') + rank++; + else if (*p == '*') /* '*' means unknown lower bound */ + info->modifiers = g_list_append (info->modifiers, GUINT_TO_POINTER (-2)); + else + return 0; + ++p; + } + if (*p++ != ']') + return 0; + info->modifiers = g_list_append (info->modifiers, GUINT_TO_POINTER (rank)); + } else { + if (rank || isptr) /* generic args after array spec or ptr*/ //XXX test + return 0; + isptr = 0; + info->type_arguments = g_ptr_array_new (); + while (*p) { + MonoTypeNameParse *subinfo = g_new0 (MonoTypeNameParse, 1); + gboolean fqname = FALSE; + + g_ptr_array_add (info->type_arguments, subinfo); + + while (*p == ' ') p++; + if (*p == '[') { + p++; + fqname = TRUE; + } + + if (!_mono_reflection_parse_type (p, &p, TRUE, subinfo)) + return 0; + + /*MS is lenient on [] delimited parameters that aren't fqn - and F# uses them.*/ + if (fqname && (*p != ']')) { + char *aname; + + if (*p != ',') + return 0; + *p++ = 0; + + aname = p; + while (*p && (*p != ']')) + p++; + + if (*p != ']') + return 0; + + *p++ = 0; + while (*aname) { + if (g_ascii_isspace (*aname)) { + ++aname; + continue; + } + break; + } + if (!*aname || + !assembly_name_to_aname (&subinfo->assembly, aname)) + return 0; + } else if (fqname && (*p == ']')) { + *p++ = 0; + } + if (*p == ']') { + *p++ = 0; + break; + } else if (!*p) { + return 0; + } + *p++ = 0; + } + } + break; + case ']': + if (is_recursed) + goto end; + return 0; + case ',': + if (is_recursed) + goto end; + *p++ = 0; + while (*p) { + if (g_ascii_isspace (*p)) { + ++p; + continue; + } + break; + } + if (!*p) + return 0; /* missing assembly name */ + if (!assembly_name_to_aname (&info->assembly, p)) + return 0; + break; + default: + return 0; + } + if (info->assembly.name) + break; + } + // *w = 0; /* terminate class name */ + end: + if (!info->name || !*info->name) + return 0; + if (endptr) + *endptr = p; + /* add other consistency checks */ + return 1; +} + + +/** + * mono_identifier_unescape_type_name_chars: + * \param identifier the display name of a mono type + * + * \returns The name in internal form, that is without escaping backslashes. + * + * The string is modified in place! + */ +char* +mono_identifier_unescape_type_name_chars(char* identifier) +{ + char *w, *r; + if (!identifier) + return NULL; + for (w = r = identifier; *r != 0; r++) + { + char c = *r; + if (c == '\\') { + r++; + if (*r == 0) + break; + c = *r; + } + *w = c; + w++; + } + if (w != r) + *w = 0; + return identifier; +} + +void +mono_identifier_unescape_info (MonoTypeNameParse* info); + +static void +unescape_each_type_argument(void* data, void* user_data) +{ + MonoTypeNameParse* info = (MonoTypeNameParse*)data; + mono_identifier_unescape_info (info); +} + +static void +unescape_each_nested_name (void* data, void* user_data) +{ + char* nested_name = (char*) data; + mono_identifier_unescape_type_name_chars(nested_name); +} + +/** + * mono_identifier_unescape_info: + * + * \param info a parsed display form of an (optionally assembly qualified) full type name. + * + * Destructively updates the info by unescaping the identifiers that + * comprise the type namespace, name, nested types (if any) and + * generic type arguments (if any). + * + * The resulting info has the names in internal form. + * + */ +void +mono_identifier_unescape_info (MonoTypeNameParse *info) +{ + if (!info) + return; + mono_identifier_unescape_type_name_chars(info->name_space); + mono_identifier_unescape_type_name_chars(info->name); + // but don't escape info->assembly + if (info->type_arguments) + g_ptr_array_foreach(info->type_arguments, &unescape_each_type_argument, NULL); + if (info->nested) + g_list_foreach(info->nested, &unescape_each_nested_name, NULL); +} + +/** + * mono_reflection_parse_type: + */ +int +mono_reflection_parse_type (char *name, MonoTypeNameParse *info) +{ + MonoError error; + gboolean result = mono_reflection_parse_type_checked (name, info, &error); + mono_error_cleanup (&error); + return result ? 1 : 0; +} + +/** + * mono_reflection_parse_type_checked: + * \param name the string to parse + * \param info the parsed name components + * \param error set on error + * + * Parse the given \p name and write the results to \p info, setting \p error + * on error. The string \p name is modified in place and \p info points into + * its memory and into allocated memory. + * + * \returns TRUE if parsing succeeded, otherwise returns FALSE and sets \p error. + * + */ +gboolean +mono_reflection_parse_type_checked (char *name, MonoTypeNameParse *info, MonoError *error) +{ + error_init (error); + int ok = _mono_reflection_parse_type (name, NULL, FALSE, info); + if (ok) { + mono_identifier_unescape_info (info); + } else { + mono_error_set_argument (error, "typeName", "failed parse: %s", name); + } + return (ok != 0); +} + +static MonoType* +_mono_reflection_get_type_from_info (MonoTypeNameParse *info, MonoImage *image, gboolean ignorecase, MonoError *error) +{ + gboolean type_resolve = FALSE; + MonoType *type; + MonoImage *rootimage = image; + + error_init (error); + + if (info->assembly.name) { + MonoAssembly *assembly = mono_assembly_loaded (&info->assembly); + if (!assembly && image && image->assembly && mono_assembly_names_equal (&info->assembly, &image->assembly->aname)) + /* + * This could happen in the AOT compiler case when the search hook is not + * installed. + */ + assembly = image->assembly; + if (!assembly) { + /* then we must load the assembly ourselve - see #60439 */ + assembly = mono_assembly_load (&info->assembly, image->assembly->basedir, NULL); + if (!assembly) + return NULL; + } + image = assembly->image; + } else if (!image) { + image = mono_defaults.corlib; + } + + type = mono_reflection_get_type_with_rootimage (rootimage, image, info, ignorecase, &type_resolve, error); + if (type == NULL && !info->assembly.name && image != mono_defaults.corlib) { + /* ignore the error and try again */ + mono_error_cleanup (error); + error_init (error); + image = mono_defaults.corlib; + type = mono_reflection_get_type_with_rootimage (rootimage, image, info, ignorecase, &type_resolve, error); + } + + return type; +} + +/** + * mono_reflection_get_type_internal: + * + * Returns: may return NULL on success, sets error on failure. + */ +static MonoType* +mono_reflection_get_type_internal (MonoImage *rootimage, MonoImage* image, MonoTypeNameParse *info, gboolean ignorecase, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + MonoClass *klass; + GList *mod; + int modval; + gboolean bounded = FALSE; + MonoType* type = NULL; + + error_init (error); + if (!image) + image = mono_defaults.corlib; + + if (!rootimage) + rootimage = mono_defaults.corlib; + + if (ignorecase) + klass = mono_class_from_name_case_checked (image, info->name_space, info->name, error); + else + klass = mono_class_from_name_checked (image, info->name_space, info->name, error); + + if (!klass) + goto leave; + + for (mod = info->nested; mod; mod = mod->next) { + gpointer iter = NULL; + MonoClass *parent; + + parent = klass; + mono_class_init (parent); + + while ((klass = mono_class_get_nested_types (parent, &iter))) { + char *lastp; + char *nested_name, *nested_nspace; + gboolean match = TRUE; + + lastp = strrchr ((const char *)mod->data, '.'); + if (lastp) { + /* Nested classes can have namespaces */ + int nspace_len; + + nested_name = g_strdup (lastp + 1); + nspace_len = lastp - (char*)mod->data; + nested_nspace = (char *)g_malloc (nspace_len + 1); + memcpy (nested_nspace, mod->data, nspace_len); + nested_nspace [nspace_len] = '\0'; + + } else { + nested_name = (char *)mod->data; + nested_nspace = NULL; + } + + if (nested_nspace) { + if (ignorecase) { + if (!(klass->name_space && mono_utf8_strcasecmp (klass->name_space, nested_nspace) == 0)) + match = FALSE; + } else { + if (!(klass->name_space && strcmp (klass->name_space, nested_nspace) == 0)) + match = FALSE; + } + } + if (match) { + if (ignorecase) { + if (mono_utf8_strcasecmp (klass->name, nested_name) != 0) + match = FALSE; + } else { + if (strcmp (klass->name, nested_name) != 0) + match = FALSE; + } + } + if (lastp) { + g_free (nested_name); + g_free (nested_nspace); + } + if (match) + break; + } + + if (!klass) + break; + } + if (!klass) + goto leave; + + if (info->type_arguments) { + MonoType **type_args = g_new0 (MonoType *, info->type_arguments->len); + MonoReflectionTypeHandle the_type; + MonoType *instance; + int i; + + for (i = 0; i < info->type_arguments->len; i++) { + MonoTypeNameParse *subinfo = (MonoTypeNameParse *)g_ptr_array_index (info->type_arguments, i); + + type_args [i] = _mono_reflection_get_type_from_info (subinfo, rootimage, ignorecase, error); + if (!type_args [i]) { + g_free (type_args); + goto leave; + } + } + + the_type = mono_type_get_object_handle (mono_domain_get (), &klass->byval_arg, error); + if (!is_ok (error) || MONO_HANDLE_IS_NULL (the_type)) + goto leave; + + instance = mono_reflection_bind_generic_parameters ( + the_type, info->type_arguments->len, type_args, error); + + g_free (type_args); + if (!instance) + goto leave; + + klass = mono_class_from_mono_type (instance); + } + + for (mod = info->modifiers; mod; mod = mod->next) { + modval = GPOINTER_TO_UINT (mod->data); + if (!modval) { /* byref: must be last modifier */ + type = &klass->this_arg; + goto leave; + } else if (modval == -1) { + klass = mono_ptr_class_get (&klass->byval_arg); + } else if (modval == -2) { + bounded = TRUE; + } else { /* array rank */ + klass = mono_bounded_array_class_get (klass, modval, bounded); + } + } + + type = &klass->byval_arg; + +leave: + HANDLE_FUNCTION_RETURN_VAL (type); +} + +/** + * mono_reflection_get_type: + * \param image a metadata context + * \param info type description structure + * \param ignorecase flag for case-insensitive string compares + * \param type_resolve whenever type resolve was already tried + * + * Build a MonoType from the type description in \p info. + * + */ +MonoType* +mono_reflection_get_type (MonoImage* image, MonoTypeNameParse *info, gboolean ignorecase, gboolean *type_resolve) { + MonoError error; + MonoType *result = mono_reflection_get_type_with_rootimage (image, image, info, ignorecase, type_resolve, &error); + mono_error_cleanup (&error); + return result; +} + +/** + * mono_reflection_get_type_checked: + * \param rootimage the image of the currently active managed caller + * \param image a metadata context + * \param info type description structure + * \param ignorecase flag for case-insensitive string compares + * \param type_resolve whenever type resolve was already tried + * \param error set on error. + * Build a \c MonoType from the type description in \p info. On failure returns NULL and sets \p error. + */ +MonoType* +mono_reflection_get_type_checked (MonoImage *rootimage, MonoImage* image, MonoTypeNameParse *info, gboolean ignorecase, gboolean *type_resolve, MonoError *error) { + error_init (error); + return mono_reflection_get_type_with_rootimage (rootimage, image, info, ignorecase, type_resolve, error); +} + + +static MonoType* +module_builder_array_get_type (MonoArrayHandle module_builders, int i, MonoImage *rootimage, MonoTypeNameParse *info, gboolean ignorecase, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + error_init (error); + MonoType *type = NULL; + MonoReflectionModuleBuilderHandle mb = MONO_HANDLE_NEW (MonoReflectionModuleBuilder, NULL); + MONO_HANDLE_ARRAY_GETREF (mb, module_builders, i); + MonoDynamicImage *dynamic_image = MONO_HANDLE_GETVAL (mb, dynamic_image); + type = mono_reflection_get_type_internal (rootimage, &dynamic_image->image, info, ignorecase, error); + HANDLE_FUNCTION_RETURN_VAL (type); +} + +static MonoType* +module_array_get_type (MonoArrayHandle modules, int i, MonoImage *rootimage, MonoTypeNameParse *info, gboolean ignorecase, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + error_init (error); + MonoType *type = NULL; + MonoReflectionModuleHandle mod = MONO_HANDLE_NEW (MonoReflectionModule, NULL); + MONO_HANDLE_ARRAY_GETREF (mod, modules, i); + MonoImage *image = MONO_HANDLE_GETVAL (mod, image); + type = mono_reflection_get_type_internal (rootimage, image, info, ignorecase, error); + HANDLE_FUNCTION_RETURN_VAL (type); +} + +static MonoType* +mono_reflection_get_type_internal_dynamic (MonoImage *rootimage, MonoAssembly *assembly, MonoTypeNameParse *info, gboolean ignorecase, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + MonoType *type = NULL; + int i; + + error_init (error); + g_assert (assembly_is_dynamic (assembly)); + MonoReflectionAssemblyBuilderHandle abuilder = MONO_HANDLE_CAST (MonoReflectionAssemblyBuilder, mono_assembly_get_object_handle (((MonoDynamicAssembly*)assembly)->domain, assembly, error)); + goto_if_nok (error, leave); + + /* Enumerate all modules */ + + MonoArrayHandle modules = MONO_HANDLE_NEW (MonoArray, NULL); + MONO_HANDLE_GET (modules, abuilder, modules); + if (!MONO_HANDLE_IS_NULL (modules)) { + int n = mono_array_handle_length (modules); + for (i = 0; i < n; ++i) { + type = module_builder_array_get_type (modules, i, rootimage, info, ignorecase, error); + if (type) + break; + goto_if_nok (error, leave); + } + } + + MonoArrayHandle loaded_modules = MONO_HANDLE_NEW (MonoArray, NULL); + MONO_HANDLE_GET (loaded_modules, abuilder, loaded_modules); + if (!type && !MONO_HANDLE_IS_NULL(loaded_modules)) { + int n = mono_array_handle_length (loaded_modules); + for (i = 0; i < n; ++i) { + type = module_array_get_type (loaded_modules, i, rootimage, info, ignorecase, error); + if (type) + break; + goto_if_nok (error, leave); + } + } + +leave: + HANDLE_FUNCTION_RETURN_VAL (type); +} + +MonoType* +mono_reflection_get_type_with_rootimage (MonoImage *rootimage, MonoImage* image, MonoTypeNameParse *info, gboolean ignorecase, gboolean *type_resolve, MonoError *error) +{ + MonoType *type; + MonoReflectionAssembly *assembly; + GString *fullName; + GList *mod; + + error_init (error); + + if (image && image_is_dynamic (image)) + type = mono_reflection_get_type_internal_dynamic (rootimage, image->assembly, info, ignorecase, error); + else { + type = mono_reflection_get_type_internal (rootimage, image, info, ignorecase, error); + } + return_val_if_nok (error, NULL); + + if (type) + return type; + if (!mono_domain_has_type_resolve (mono_domain_get ())) + return NULL; + + if (type_resolve) { + if (*type_resolve) + return NULL; + else + *type_resolve = TRUE; + } + + /* Reconstruct the type name */ + fullName = g_string_new (""); + if (info->name_space && (info->name_space [0] != '\0')) + g_string_printf (fullName, "%s.%s", info->name_space, info->name); + else + g_string_printf (fullName, "%s", info->name); + for (mod = info->nested; mod; mod = mod->next) + g_string_append_printf (fullName, "+%s", (char*)mod->data); + + assembly = mono_domain_try_type_resolve_checked ( mono_domain_get (), fullName->str, NULL, error); + if (!is_ok (error)) { + g_string_free (fullName, TRUE); + return NULL; + } + + if (assembly) { + if (assembly_is_dynamic (assembly->assembly)) + type = mono_reflection_get_type_internal_dynamic (rootimage, assembly->assembly, + info, ignorecase, error); + else + type = mono_reflection_get_type_internal (rootimage, assembly->assembly->image, + info, ignorecase, error); + } + g_string_free (fullName, TRUE); + return_val_if_nok (error, NULL); + return type; +} + +/** + * mono_reflection_free_type_info: + */ +void +mono_reflection_free_type_info (MonoTypeNameParse *info) +{ + g_list_free (info->modifiers); + g_list_free (info->nested); + + if (info->type_arguments) { + int i; + + for (i = 0; i < info->type_arguments->len; i++) { + MonoTypeNameParse *subinfo = (MonoTypeNameParse *)g_ptr_array_index (info->type_arguments, i); + + mono_reflection_free_type_info (subinfo); + /*We free the subinfo since it is allocated by _mono_reflection_parse_type*/ + g_free (subinfo); + } + + g_ptr_array_free (info->type_arguments, TRUE); + } +} + +/** + * mono_reflection_type_from_name: + * \param name type name. + * \param image a metadata context (can be NULL). + * + * Retrieves a \c MonoType from its \p name. If the name is not fully qualified, + * it defaults to get the type from \p image or, if \p image is NULL or loading + * from it fails, uses corlib. + * + */ +MonoType* +mono_reflection_type_from_name (char *name, MonoImage *image) +{ + MonoError error; + MonoType *result = mono_reflection_type_from_name_checked (name, image, &error); + mono_error_cleanup (&error); + return result; +} + +/** + * mono_reflection_type_from_name_checked: + * \param name type name. + * \param image a metadata context (can be NULL). + * \param error set on errror. + * Retrieves a MonoType from its \p name. If the name is not fully qualified, + * it defaults to get the type from \p image or, if \p image is NULL or loading + * from it fails, uses corlib. On failure returns NULL and sets \p error. + */ +MonoType* +mono_reflection_type_from_name_checked (char *name, MonoImage *image, MonoError *error) +{ + MonoType *type = NULL; + MonoTypeNameParse info; + char *tmp; + + error_init (error); + /* Make a copy since parse_type modifies its argument */ + tmp = g_strdup (name); + + /*g_print ("requested type %s\n", str);*/ + MonoError parse_error; + if (!mono_reflection_parse_type_checked (tmp, &info, &parse_error)) { + mono_error_cleanup (&parse_error); + goto leave; + } + type = _mono_reflection_get_type_from_info (&info, image, FALSE, error); +leave: + g_free (tmp); + mono_reflection_free_type_info (&info); + return type; +} + +/** + * mono_reflection_get_token: + * \returns the metadata token of \p obj which should be an object + * representing a metadata element. + */ +guint32 +mono_reflection_get_token (MonoObject *obj_raw) +{ + HANDLE_FUNCTION_ENTER (); + MONO_HANDLE_DCL (MonoObject, obj); + MonoError error; + guint32 result = mono_reflection_get_token_checked (obj, &error); + mono_error_assert_ok (&error); + HANDLE_FUNCTION_RETURN_VAL (result); +} + +/** + * mono_reflection_get_token_checked: + * \param obj the object + * \param error set on error + * \returns the metadata token of \p obj which should be an object + * representing a metadata element. On failure sets \p error. + */ +guint32 +mono_reflection_get_token_checked (MonoObjectHandle obj, MonoError *error) +{ + guint32 token = 0; + + error_init (error); + + MonoClass *klass = mono_handle_class (obj); + + if (strcmp (klass->name, "MethodBuilder") == 0) { + MonoReflectionMethodBuilderHandle mb = MONO_HANDLE_CAST (MonoReflectionMethodBuilder, obj); + + token = MONO_HANDLE_GETVAL (mb, table_idx) | MONO_TOKEN_METHOD_DEF; + } else if (strcmp (klass->name, "ConstructorBuilder") == 0) { + MonoReflectionCtorBuilderHandle mb = MONO_HANDLE_CAST (MonoReflectionCtorBuilder, obj); + + token = MONO_HANDLE_GETVAL (mb, table_idx) | MONO_TOKEN_METHOD_DEF; + } else if (strcmp (klass->name, "FieldBuilder") == 0) { + MonoReflectionFieldBuilderHandle fb = MONO_HANDLE_CAST (MonoReflectionFieldBuilder, obj); + + token = MONO_HANDLE_GETVAL (fb, table_idx) | MONO_TOKEN_FIELD_DEF; + } else if (strcmp (klass->name, "TypeBuilder") == 0) { + MonoReflectionTypeBuilderHandle tb = MONO_HANDLE_CAST (MonoReflectionTypeBuilder, obj); + token = MONO_HANDLE_GETVAL (tb, table_idx) | MONO_TOKEN_TYPE_DEF; + } else if (strcmp (klass->name, "RuntimeType") == 0) { + MonoType *type = mono_reflection_type_handle_mono_type (MONO_HANDLE_CAST (MonoReflectionType, obj), error); + return_val_if_nok (error, 0); + MonoClass *mc = mono_class_from_mono_type (type); + if (!mono_class_init (mc)) { + mono_error_set_for_class_failure (error, mc); + return 0; + } + + token = mc->type_token; + } else if (strcmp (klass->name, "MonoCMethod") == 0 || + strcmp (klass->name, "MonoMethod") == 0) { + MonoReflectionMethodHandle m = MONO_HANDLE_CAST (MonoReflectionMethod, obj); + MonoMethod *method = MONO_HANDLE_GETVAL (m, method); + if (method->is_inflated) { + MonoMethodInflated *inflated = (MonoMethodInflated *) method; + return inflated->declaring->token; + } else { + token = method->token; + } + } else if (strcmp (klass->name, "MonoField") == 0) { + MonoReflectionFieldHandle f = MONO_HANDLE_CAST (MonoReflectionField, obj); + + token = mono_class_get_field_token (MONO_HANDLE_GETVAL (f, field)); + } else if (strcmp (klass->name, "MonoProperty") == 0) { + MonoReflectionPropertyHandle p = MONO_HANDLE_CAST (MonoReflectionProperty, obj); + + token = mono_class_get_property_token (MONO_HANDLE_GETVAL (p, property)); + } else if (strcmp (klass->name, "MonoEvent") == 0) { + MonoReflectionMonoEventHandle p = MONO_HANDLE_CAST (MonoReflectionMonoEvent, obj); + + token = mono_class_get_event_token (MONO_HANDLE_GETVAL (p, event)); + } else if (strcmp (klass->name, "ParameterInfo") == 0 || strcmp (klass->name, "MonoParameterInfo") == 0) { + MonoReflectionParameterHandle p = MONO_HANDLE_CAST (MonoReflectionParameter, obj); + MonoObjectHandle member_impl = MONO_HANDLE_NEW (MonoObject, NULL); + MONO_HANDLE_GET (member_impl, p, MemberImpl); + MonoClass *member_class = mono_handle_class (member_impl); + g_assert (mono_class_is_reflection_method_or_constructor (member_class)); + MonoMethod *method = MONO_HANDLE_GETVAL (MONO_HANDLE_CAST (MonoReflectionMethod, member_impl), method); + + token = mono_method_get_param_token (method, MONO_HANDLE_GETVAL (p, PositionImpl)); + } else if (strcmp (klass->name, "Module") == 0 || strcmp (klass->name, "MonoModule") == 0 || strcmp (klass->name, "ModuleBuilder") == 0) { + MonoReflectionModuleHandle m = MONO_HANDLE_CAST (MonoReflectionModule, obj); + + token = MONO_HANDLE_GETVAL (m, token); + } else if (strcmp (klass->name, "Assembly") == 0 || strcmp (klass->name, "MonoAssembly") == 0) { + token = mono_metadata_make_token (MONO_TABLE_ASSEMBLY, 1); + } else { + mono_error_set_not_implemented (error, "MetadataToken is not supported for type '%s.%s'", + klass->name_space, klass->name); + return 0; + } + + return token; +} + + +gboolean +mono_reflection_is_usertype (MonoReflectionTypeHandle ref) +{ + MonoClass *klass = mono_handle_class (ref); + return klass->image != mono_defaults.corlib || strcmp ("TypeDelegator", klass->name) == 0; +} + +/** + * mono_reflection_bind_generic_parameters: + * \param type a managed type object (which should be some kind of generic (instance? definition?)) + * \param type_args the number of type arguments to bind + * \param types array of type arguments + * \param error set on error + * Given a managed type object for a generic type instance, binds each of its arguments to the specified types. + * \returns the \c MonoType* for the resulting type instantiation. On failure returns NULL and sets \p error. + */ +MonoType* +mono_reflection_bind_generic_parameters (MonoReflectionTypeHandle reftype, int type_argc, MonoType **types, MonoError *error) +{ + gboolean is_dynamic = FALSE; + MonoClass *geninst; + + error_init (error); + + mono_loader_lock (); + + MonoClass *klass = mono_handle_class (reftype); + if (mono_is_sre_type_builder (klass)) { + is_dynamic = TRUE; + } else if (mono_is_sre_generic_instance (klass)) { + /* Does this ever make sense? what does instantiating a generic instance even mean? */ + g_assert_not_reached (); + MonoReflectionGenericClassHandle rgi = MONO_HANDLE_CAST (MonoReflectionGenericClass, reftype); + MonoReflectionTypeHandle gtd = MONO_HANDLE_NEW_GET (MonoReflectionType, rgi, generic_type); + + if (mono_is_sre_type_builder (mono_handle_class (gtd))) + is_dynamic = TRUE; + } + + MonoType *t = mono_reflection_type_handle_mono_type (reftype, error); + if (!is_ok (error)) { + mono_loader_unlock (); + return NULL; + } + + klass = mono_class_from_mono_type (t); + if (!mono_class_is_gtd (klass)) { + mono_loader_unlock (); + mono_error_set_type_load_class (error, klass, "Cannot bind generic parameters of a non-generic type"); + return NULL; + } + + guint gtd_type_argc = mono_class_get_generic_container (klass)->type_argc; + if (gtd_type_argc != type_argc) { + mono_loader_unlock (); + mono_error_set_argument (error, "types", "The generic type definition needs %d type arguments, but was instantiated with %d ", gtd_type_argc, type_argc); + return NULL; + } + + + if (klass->wastypebuilder) + is_dynamic = TRUE; + + mono_loader_unlock (); + + geninst = mono_class_bind_generic_parameters (klass, type_argc, types, is_dynamic); + + return &geninst->byval_arg; +} + +MonoClass* +mono_class_bind_generic_parameters (MonoClass *klass, int type_argc, MonoType **types, gboolean is_dynamic) +{ + MonoGenericClass *gclass; + MonoGenericInst *inst; + + g_assert (mono_class_is_gtd (klass)); + + inst = mono_metadata_get_generic_inst (type_argc, types); + gclass = mono_metadata_lookup_generic_class (klass, inst, is_dynamic); + + return mono_generic_class_get_class (gclass); +} + +static MonoGenericInst* +generic_inst_from_type_array_handle (MonoArrayHandle types, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + error_init (error); + MonoGenericInst *ginst = NULL; + int count = mono_array_handle_length (types); + MonoType **type_argv = g_new0 (MonoType *, count); + MonoReflectionTypeHandle garg = MONO_HANDLE_NEW (MonoReflectionType, NULL); + for (int i = 0; i < count; i++) { + MONO_HANDLE_ARRAY_GETREF (garg, types, i); + type_argv [i] = mono_reflection_type_handle_mono_type (garg, error); + goto_if_nok (error, leave); + + } + ginst = mono_metadata_get_generic_inst (count, type_argv); +leave: + g_free (type_argv); + HANDLE_FUNCTION_RETURN_VAL (ginst); +} + +static MonoMethod* +reflection_bind_generic_method_parameters (MonoMethod *method, MonoArrayHandle types, MonoError *error) +{ + MonoClass *klass; + MonoMethod *inflated; + MonoGenericContext tmp_context; + + error_init (error); + + klass = method->klass; + + if (method->is_inflated) + method = ((MonoMethodInflated *) method)->declaring; + + int count = mono_method_signature (method)->generic_param_count; + if (count != mono_array_handle_length (types)) { + mono_error_set_argument (error, "typeArguments", "Incorrect number of generic arguments"); + return NULL; + } + + MonoGenericInst *ginst = generic_inst_from_type_array_handle (types, error); + return_val_if_nok (error, NULL); + + tmp_context.class_inst = mono_class_is_ginst (klass) ? mono_class_get_generic_class (klass)->context.class_inst : NULL; + tmp_context.method_inst = ginst; + + inflated = mono_class_inflate_generic_method_checked (method, &tmp_context, error); + mono_error_assert_ok (error); + + if (!mono_verifier_is_method_valid_generic_instantiation (inflated)) { + mono_error_set_argument (error, "typeArguments", "Invalid generic arguments"); + return NULL; + } + + return inflated; +} + +MonoReflectionMethodHandle +ves_icall_MonoMethod_MakeGenericMethod_impl (MonoReflectionMethodHandle rmethod, MonoArrayHandle types, MonoError *error) +{ + error_init (error); + g_assert (0 != strcmp (mono_handle_class (rmethod)->name, "MethodBuilder")); + + MonoMethod *method = MONO_HANDLE_GETVAL (rmethod, method); + MonoMethod *imethod = reflection_bind_generic_method_parameters (method, types, error); + return_val_if_nok (error, MONO_HANDLE_CAST (MonoReflectionMethod, NULL_HANDLE)); + + /*FIXME but I think this is no longer necessary*/ + if (image_is_dynamic (method->klass->image)) { + MonoDynamicImage *image = (MonoDynamicImage*)method->klass->image; + /* + * This table maps metadata structures representing inflated methods/fields + * to the reflection objects representing their generic definitions. + */ + mono_image_lock ((MonoImage*)image); + mono_g_hash_table_insert (image->generic_def_objects, imethod, MONO_HANDLE_RAW (rmethod)); + mono_image_unlock ((MonoImage*)image); + } + + return mono_method_get_object_handle (MONO_HANDLE_DOMAIN (rmethod), imethod, NULL, error); +} + + +/* SECURITY_ACTION_* are defined in mono/metadata/tabledefs.h */ +const static guint32 declsec_flags_map[] = { + 0x00000000, /* empty */ + MONO_DECLSEC_FLAG_REQUEST, /* SECURITY_ACTION_REQUEST (x01) */ + MONO_DECLSEC_FLAG_DEMAND, /* SECURITY_ACTION_DEMAND (x02) */ + MONO_DECLSEC_FLAG_ASSERT, /* SECURITY_ACTION_ASSERT (x03) */ + MONO_DECLSEC_FLAG_DENY, /* SECURITY_ACTION_DENY (x04) */ + MONO_DECLSEC_FLAG_PERMITONLY, /* SECURITY_ACTION_PERMITONLY (x05) */ + MONO_DECLSEC_FLAG_LINKDEMAND, /* SECURITY_ACTION_LINKDEMAND (x06) */ + MONO_DECLSEC_FLAG_INHERITANCEDEMAND, /* SECURITY_ACTION_INHERITANCEDEMAND (x07) */ + MONO_DECLSEC_FLAG_REQUEST_MINIMUM, /* SECURITY_ACTION_REQUEST_MINIMUM (x08) */ + MONO_DECLSEC_FLAG_REQUEST_OPTIONAL, /* SECURITY_ACTION_REQUEST_OPTIONAL (x09) */ + MONO_DECLSEC_FLAG_REQUEST_REFUSE, /* SECURITY_ACTION_REQUEST_REFUSE (x0A) */ + MONO_DECLSEC_FLAG_PREJIT_GRANT, /* SECURITY_ACTION_PREJIT_GRANT (x0B) */ + MONO_DECLSEC_FLAG_PREJIT_DENY, /* SECURITY_ACTION_PREJIT_DENY (x0C) */ + MONO_DECLSEC_FLAG_NONCAS_DEMAND, /* SECURITY_ACTION_NONCAS_DEMAND (x0D) */ + MONO_DECLSEC_FLAG_NONCAS_LINKDEMAND, /* SECURITY_ACTION_NONCAS_LINKDEMAND (x0E) */ + MONO_DECLSEC_FLAG_NONCAS_INHERITANCEDEMAND, /* SECURITY_ACTION_NONCAS_INHERITANCEDEMAND (x0F) */ + MONO_DECLSEC_FLAG_LINKDEMAND_CHOICE, /* SECURITY_ACTION_LINKDEMAND_CHOICE (x10) */ + MONO_DECLSEC_FLAG_INHERITANCEDEMAND_CHOICE, /* SECURITY_ACTION_INHERITANCEDEMAND_CHOICE (x11) */ + MONO_DECLSEC_FLAG_DEMAND_CHOICE, /* SECURITY_ACTION_DEMAND_CHOICE (x12) */ +}; + +/* + * Returns flags that includes all available security action associated to the handle. + * @token: metadata token (either for a class or a method) + * @image: image where resides the metadata. + */ +static guint32 +mono_declsec_get_flags (MonoImage *image, guint32 token) +{ + int index = mono_metadata_declsec_from_index (image, token); + MonoTableInfo *t = &image->tables [MONO_TABLE_DECLSECURITY]; + guint32 result = 0; + guint32 action; + int i; + + /* HasSecurity can be present for other, not specially encoded, attributes, + e.g. SuppressUnmanagedCodeSecurityAttribute */ + if (index < 0) + return 0; + + for (i = index; i < t->rows; i++) { + guint32 cols [MONO_DECL_SECURITY_SIZE]; + + mono_metadata_decode_row (t, i, cols, MONO_DECL_SECURITY_SIZE); + if (cols [MONO_DECL_SECURITY_PARENT] != token) + break; + + action = cols [MONO_DECL_SECURITY_ACTION]; + if ((action >= MONO_DECLSEC_ACTION_MIN) && (action <= MONO_DECLSEC_ACTION_MAX)) { + result |= declsec_flags_map [action]; + } else { + g_assert_not_reached (); + } + } + return result; +} + +/** + * mono_declsec_flags_from_method: + * \param method The method for which we want the declarative security flags. + * Get the security actions (in the form of flags) associated with the specified method. + * To keep \c MonoMethod size down we do not cache the declarative security flags + * (except for the stack modifiers which are kept in the MonoJitInfo structure) + * \returns the declarative security flags for the method (only). + */ +guint32 +mono_declsec_flags_from_method (MonoMethod *method) +{ + if (method->flags & METHOD_ATTRIBUTE_HAS_SECURITY) { + /* FIXME: No cache (for the moment) */ + guint32 idx = mono_method_get_index (method); + idx <<= MONO_HAS_DECL_SECURITY_BITS; + idx |= MONO_HAS_DECL_SECURITY_METHODDEF; + return mono_declsec_get_flags (method->klass->image, idx); + } + return 0; +} + +/** + * mono_declsec_flags_from_class: + * \param klass The class for which we want the declarative security flags. + * Get the security actions (in the form of flags) associated with the specified class. + * We cache the flags inside the \c MonoClass structure as this will get + * called very often (at least for each method). + * \returns the declarative security flags for the class. + */ +guint32 +mono_declsec_flags_from_class (MonoClass *klass) +{ + if (mono_class_get_flags (klass) & TYPE_ATTRIBUTE_HAS_SECURITY) { + guint32 flags = mono_class_get_declsec_flags (klass); + + if (!flags) { + guint32 idx; + + idx = mono_metadata_token_index (klass->type_token); + idx <<= MONO_HAS_DECL_SECURITY_BITS; + idx |= MONO_HAS_DECL_SECURITY_TYPEDEF; + flags = mono_declsec_get_flags (klass->image, idx); + /* we cache the flags on classes */ + mono_class_set_declsec_flags (klass, flags); + } + return flags; + } + return 0; +} + +/** + * mono_declsec_flags_from_assembly: + * \param assembly The assembly for which we want the declarative security flags. + * Get the security actions (in the form of flags) associated with the specified assembly. + * \returns the declarative security flags for the assembly. + */ +guint32 +mono_declsec_flags_from_assembly (MonoAssembly *assembly) +{ + guint32 idx = 1; /* there is only one assembly */ + idx <<= MONO_HAS_DECL_SECURITY_BITS; + idx |= MONO_HAS_DECL_SECURITY_ASSEMBLY; + return mono_declsec_get_flags (assembly->image, idx); +} + + +/* + * Fill actions for the specific index (which may either be an encoded class token or + * an encoded method token) from the metadata image. + * Returns TRUE if some actions requiring code generation are present, FALSE otherwise. + */ +static MonoBoolean +fill_actions_from_index (MonoImage *image, guint32 token, MonoDeclSecurityActions* actions, + guint32 id_std, guint32 id_noncas, guint32 id_choice) +{ + MonoBoolean result = FALSE; + MonoTableInfo *t; + guint32 cols [MONO_DECL_SECURITY_SIZE]; + int index = mono_metadata_declsec_from_index (image, token); + int i; + + t = &image->tables [MONO_TABLE_DECLSECURITY]; + for (i = index; i < t->rows; i++) { + mono_metadata_decode_row (t, i, cols, MONO_DECL_SECURITY_SIZE); + + if (cols [MONO_DECL_SECURITY_PARENT] != token) + return result; + + /* if present only replace (class) permissions with method permissions */ + /* if empty accept either class or method permissions */ + if (cols [MONO_DECL_SECURITY_ACTION] == id_std) { + if (!actions->demand.blob) { + const char *blob = mono_metadata_blob_heap (image, cols [MONO_DECL_SECURITY_PERMISSIONSET]); + actions->demand.index = cols [MONO_DECL_SECURITY_PERMISSIONSET]; + actions->demand.blob = (char*) (blob + 2); + actions->demand.size = mono_metadata_decode_blob_size (blob, &blob); + result = TRUE; + } + } else if (cols [MONO_DECL_SECURITY_ACTION] == id_noncas) { + if (!actions->noncasdemand.blob) { + const char *blob = mono_metadata_blob_heap (image, cols [MONO_DECL_SECURITY_PERMISSIONSET]); + actions->noncasdemand.index = cols [MONO_DECL_SECURITY_PERMISSIONSET]; + actions->noncasdemand.blob = (char*) (blob + 2); + actions->noncasdemand.size = mono_metadata_decode_blob_size (blob, &blob); + result = TRUE; + } + } else if (cols [MONO_DECL_SECURITY_ACTION] == id_choice) { + if (!actions->demandchoice.blob) { + const char *blob = mono_metadata_blob_heap (image, cols [MONO_DECL_SECURITY_PERMISSIONSET]); + actions->demandchoice.index = cols [MONO_DECL_SECURITY_PERMISSIONSET]; + actions->demandchoice.blob = (char*) (blob + 2); + actions->demandchoice.size = mono_metadata_decode_blob_size (blob, &blob); + result = TRUE; + } + } + } + + return result; +} + +static MonoBoolean +mono_declsec_get_class_demands_params (MonoClass *klass, MonoDeclSecurityActions* demands, + guint32 id_std, guint32 id_noncas, guint32 id_choice) +{ + guint32 idx = mono_metadata_token_index (klass->type_token); + idx <<= MONO_HAS_DECL_SECURITY_BITS; + idx |= MONO_HAS_DECL_SECURITY_TYPEDEF; + return fill_actions_from_index (klass->image, idx, demands, id_std, id_noncas, id_choice); +} + +static MonoBoolean +mono_declsec_get_method_demands_params (MonoMethod *method, MonoDeclSecurityActions* demands, + guint32 id_std, guint32 id_noncas, guint32 id_choice) +{ + guint32 idx = mono_method_get_index (method); + idx <<= MONO_HAS_DECL_SECURITY_BITS; + idx |= MONO_HAS_DECL_SECURITY_METHODDEF; + return fill_actions_from_index (method->klass->image, idx, demands, id_std, id_noncas, id_choice); +} + +/** + * mono_declsec_get_demands: + * Collect all actions (that requires to generate code in mini) assigned for + * the specified method. + * Don't use the content of actions if the function return FALSE. + */ +MonoBoolean +mono_declsec_get_demands (MonoMethod *method, MonoDeclSecurityActions* demands) +{ + guint32 mask = MONO_DECLSEC_FLAG_DEMAND | MONO_DECLSEC_FLAG_NONCAS_DEMAND | + MONO_DECLSEC_FLAG_DEMAND_CHOICE; + MonoBoolean result = FALSE; + guint32 flags; + + /* quick exit if no declarative security is present in the metadata */ + if (!method->klass->image->tables [MONO_TABLE_DECLSECURITY].rows) + return FALSE; + + /* we want the original as the wrapper is "free" of the security informations */ + if (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE || method->wrapper_type == MONO_WRAPPER_MANAGED_TO_MANAGED) { + method = mono_marshal_method_from_wrapper (method); + if (!method) + return FALSE; + } + + /* First we look for method-level attributes */ + if (method->flags & METHOD_ATTRIBUTE_HAS_SECURITY) { + mono_class_init (method->klass); + memset (demands, 0, sizeof (MonoDeclSecurityActions)); + + result = mono_declsec_get_method_demands_params (method, demands, + SECURITY_ACTION_DEMAND, SECURITY_ACTION_NONCASDEMAND, SECURITY_ACTION_DEMANDCHOICE); + } + + /* Here we use (or create) the class declarative cache to look for demands */ + flags = mono_declsec_flags_from_class (method->klass); + if (flags & mask) { + if (!result) { + mono_class_init (method->klass); + memset (demands, 0, sizeof (MonoDeclSecurityActions)); + } + result |= mono_declsec_get_class_demands_params (method->klass, demands, + SECURITY_ACTION_DEMAND, SECURITY_ACTION_NONCASDEMAND, SECURITY_ACTION_DEMANDCHOICE); + } + + /* The boolean return value is used as a shortcut in case nothing needs to + be generated (e.g. LinkDemand[Choice] and InheritanceDemand[Choice]) */ + return result; +} + + +/** + * mono_declsec_get_linkdemands: + * Collect all Link actions: \c LinkDemand, \c NonCasLinkDemand and \c LinkDemandChoice (2.0). + * Don't use the content of actions if the function return FALSE. + */ +MonoBoolean +mono_declsec_get_linkdemands (MonoMethod *method, MonoDeclSecurityActions* klass, MonoDeclSecurityActions *cmethod) +{ + MonoBoolean result = FALSE; + guint32 flags; + + /* quick exit if no declarative security is present in the metadata */ + if (!method->klass->image->tables [MONO_TABLE_DECLSECURITY].rows) + return FALSE; + + /* we want the original as the wrapper is "free" of the security informations */ + if (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE || method->wrapper_type == MONO_WRAPPER_MANAGED_TO_MANAGED) { + method = mono_marshal_method_from_wrapper (method); + if (!method) + return FALSE; + } + + /* results are independant - zeroize both */ + memset (cmethod, 0, sizeof (MonoDeclSecurityActions)); + memset (klass, 0, sizeof (MonoDeclSecurityActions)); + + /* First we look for method-level attributes */ + if (method->flags & METHOD_ATTRIBUTE_HAS_SECURITY) { + mono_class_init (method->klass); + + result = mono_declsec_get_method_demands_params (method, cmethod, + SECURITY_ACTION_LINKDEMAND, SECURITY_ACTION_NONCASLINKDEMAND, SECURITY_ACTION_LINKDEMANDCHOICE); + } + + /* Here we use (or create) the class declarative cache to look for demands */ + flags = mono_declsec_flags_from_class (method->klass); + if (flags & (MONO_DECLSEC_FLAG_LINKDEMAND | MONO_DECLSEC_FLAG_NONCAS_LINKDEMAND | MONO_DECLSEC_FLAG_LINKDEMAND_CHOICE)) { + mono_class_init (method->klass); + + result |= mono_declsec_get_class_demands_params (method->klass, klass, + SECURITY_ACTION_LINKDEMAND, SECURITY_ACTION_NONCASLINKDEMAND, SECURITY_ACTION_LINKDEMANDCHOICE); + } + + return result; +} + +/** + * mono_declsec_get_inheritdemands_class: + * \param klass The inherited class - this is the class that provides the security check (attributes) + * \param demands + * Collect all Inherit actions - \c InheritanceDemand, \c NonCasInheritanceDemand and \c InheritanceDemandChoice (2.0). + * Don't use the content of actions if the function return FALSE. + * \returns TRUE if inheritance demands (any kind) are present, FALSE otherwise. + */ +MonoBoolean +mono_declsec_get_inheritdemands_class (MonoClass *klass, MonoDeclSecurityActions* demands) +{ + MonoBoolean result = FALSE; + guint32 flags; + + /* quick exit if no declarative security is present in the metadata */ + if (!klass->image->tables [MONO_TABLE_DECLSECURITY].rows) + return FALSE; + + /* Here we use (or create) the class declarative cache to look for demands */ + flags = mono_declsec_flags_from_class (klass); + if (flags & (MONO_DECLSEC_FLAG_INHERITANCEDEMAND | MONO_DECLSEC_FLAG_NONCAS_INHERITANCEDEMAND | MONO_DECLSEC_FLAG_INHERITANCEDEMAND_CHOICE)) { + mono_class_init (klass); + memset (demands, 0, sizeof (MonoDeclSecurityActions)); + + result |= mono_declsec_get_class_demands_params (klass, demands, + SECURITY_ACTION_INHERITDEMAND, SECURITY_ACTION_NONCASINHERITANCE, SECURITY_ACTION_INHERITDEMANDCHOICE); + } + + return result; +} + +/** + * mono_declsec_get_inheritdemands_method: + * Collect all Inherit actions: \c InheritanceDemand, \c NonCasInheritanceDemand and \c InheritanceDemandChoice (2.0). + * Don't use the content of actions if the function return FALSE. + */ +MonoBoolean +mono_declsec_get_inheritdemands_method (MonoMethod *method, MonoDeclSecurityActions* demands) +{ + /* quick exit if no declarative security is present in the metadata */ + if (!method->klass->image->tables [MONO_TABLE_DECLSECURITY].rows) + return FALSE; + + /* we want the original as the wrapper is "free" of the security informations */ + if (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE || method->wrapper_type == MONO_WRAPPER_MANAGED_TO_MANAGED) { + method = mono_marshal_method_from_wrapper (method); + if (!method) + return FALSE; + } + + if (method->flags & METHOD_ATTRIBUTE_HAS_SECURITY) { + mono_class_init (method->klass); + memset (demands, 0, sizeof (MonoDeclSecurityActions)); + + return mono_declsec_get_method_demands_params (method, demands, + SECURITY_ACTION_INHERITDEMAND, SECURITY_ACTION_NONCASINHERITANCE, SECURITY_ACTION_INHERITDEMANDCHOICE); + } + return FALSE; +} + + +static MonoBoolean +get_declsec_action (MonoImage *image, guint32 token, guint32 action, MonoDeclSecurityEntry *entry) +{ + guint32 cols [MONO_DECL_SECURITY_SIZE]; + MonoTableInfo *t; + int i; + + int index = mono_metadata_declsec_from_index (image, token); + if (index == -1) + return FALSE; + + t = &image->tables [MONO_TABLE_DECLSECURITY]; + for (i = index; i < t->rows; i++) { + mono_metadata_decode_row (t, i, cols, MONO_DECL_SECURITY_SIZE); + + /* shortcut - index are ordered */ + if (token != cols [MONO_DECL_SECURITY_PARENT]) + return FALSE; + + if (cols [MONO_DECL_SECURITY_ACTION] == action) { + const char *metadata = mono_metadata_blob_heap (image, cols [MONO_DECL_SECURITY_PERMISSIONSET]); + entry->blob = (char*) (metadata + 2); + entry->size = mono_metadata_decode_blob_size (metadata, &metadata); + return TRUE; + } + } + + return FALSE; +} + +MonoBoolean +mono_declsec_get_method_action (MonoMethod *method, guint32 action, MonoDeclSecurityEntry *entry) +{ + if (method->flags & METHOD_ATTRIBUTE_HAS_SECURITY) { + guint32 idx = mono_method_get_index (method); + idx <<= MONO_HAS_DECL_SECURITY_BITS; + idx |= MONO_HAS_DECL_SECURITY_METHODDEF; + return get_declsec_action (method->klass->image, idx, action, entry); + } + return FALSE; +} + +/** + * mono_declsec_get_class_action: + */ +MonoBoolean +mono_declsec_get_class_action (MonoClass *klass, guint32 action, MonoDeclSecurityEntry *entry) +{ + /* use cache */ + guint32 flags = mono_declsec_flags_from_class (klass); + if (declsec_flags_map [action] & flags) { + guint32 idx = mono_metadata_token_index (klass->type_token); + idx <<= MONO_HAS_DECL_SECURITY_BITS; + idx |= MONO_HAS_DECL_SECURITY_TYPEDEF; + return get_declsec_action (klass->image, idx, action, entry); + } + return FALSE; +} + +/** + * mono_declsec_get_assembly_action: + */ +MonoBoolean +mono_declsec_get_assembly_action (MonoAssembly *assembly, guint32 action, MonoDeclSecurityEntry *entry) +{ + guint32 idx = 1; /* there is only one assembly */ + idx <<= MONO_HAS_DECL_SECURITY_BITS; + idx |= MONO_HAS_DECL_SECURITY_ASSEMBLY; + + return get_declsec_action (assembly->image, idx, action, entry); +} + +gboolean +mono_reflection_call_is_assignable_to (MonoClass *klass, MonoClass *oklass, MonoError *error) +{ + MonoObject *res, *exc; + void *params [1]; + static MonoMethod *method = NULL; + + error_init (error); + + if (method == NULL) { + method = mono_class_get_method_from_name (mono_class_get_type_builder_class (), "IsAssignableTo", 1); + g_assert (method); + } + + /* + * The result of mono_type_get_object_checked () might be a System.MonoType but we + * need a TypeBuilder so use mono_class_get_ref_info (klass). + */ + g_assert (mono_class_has_ref_info (klass)); + g_assert (!strcmp (mono_object_class (mono_class_get_ref_info_raw (klass))->name, "TypeBuilder")); /* FIXME use handles */ + + params [0] = mono_type_get_object_checked (mono_domain_get (), &oklass->byval_arg, error); + return_val_if_nok (error, FALSE); + + MonoError inner_error; + res = mono_runtime_try_invoke (method, mono_class_get_ref_info_raw (klass), params, &exc, &inner_error); /* FIXME use handles */ + + if (exc || !is_ok (&inner_error)) { + mono_error_cleanup (&inner_error); + return FALSE; + } else + return *(MonoBoolean*)mono_object_unbox (res); +} + +/** + * mono_reflection_type_get_type: + * \param reftype the \c System.Type object + * \returns the \c MonoType* associated with the C# \c System.Type object \p reftype. + */ +MonoType* +mono_reflection_type_get_type (MonoReflectionType *reftype) +{ + g_assert (reftype); + + MonoError error; + MonoType *result = mono_reflection_type_get_handle (reftype, &error); + mono_error_assert_ok (&error); + return result; +} + +/** + * mono_reflection_assembly_get_assembly: + * \param refassembly the \c System.Reflection.Assembly object + * \returns the \c MonoAssembly* associated with the C# \c System.Reflection.Assembly object \p refassembly. + */ +MonoAssembly* +mono_reflection_assembly_get_assembly (MonoReflectionAssembly *refassembly) +{ + g_assert (refassembly); + + return refassembly->assembly; +} + +/** + * mono_class_from_mono_type_handle: + * \param reftype the \c System.Type handle + * \returns the \c MonoClass* corresponding to the given type. + */ +MonoClass* +mono_class_from_mono_type_handle (MonoReflectionTypeHandle reftype) +{ + return mono_class_from_mono_type (MONO_HANDLE_RAW (reftype)->type); +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/reflection.h b/unity-2019.4.24f1-mbe/mono/metadata/reflection.h new file mode 100644 index 000000000..70c039a2b --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/reflection.h @@ -0,0 +1,173 @@ +/** + * \file + */ + +#ifndef __METADATA_REFLECTION_H__ +#define __METADATA_REFLECTION_H__ + +#include +#include + +MONO_BEGIN_DECLS + +typedef struct MonoTypeNameParse MonoTypeNameParse; + +typedef struct { + MonoMethod *ctor; + uint32_t data_size; + const mono_byte* data; +} MonoCustomAttrEntry; + +typedef struct { + int num_attrs; + int cached; + MonoImage *image; + MonoCustomAttrEntry attrs [MONO_ZERO_LEN_ARRAY]; +} MonoCustomAttrInfo; + +#define MONO_SIZEOF_CUSTOM_ATTR_INFO (offsetof (MonoCustomAttrInfo, attrs)) + +/* + * Information which isn't in the MonoMethod structure is stored here for + * dynamic methods. + */ +typedef struct { + char **param_names; + MonoMarshalSpec **param_marshall; + MonoCustomAttrInfo **param_cattr; + uint8_t** param_defaults; + uint32_t *param_default_types; + char *dllentry, *dll; +} MonoReflectionMethodAux; + +typedef enum { + ResolveTokenError_OutOfRange, + ResolveTokenError_BadTable, + ResolveTokenError_Other +} MonoResolveTokenError; + +MONO_RT_EXTERNAL_ONLY +MONO_API int mono_reflection_parse_type (char *name, MonoTypeNameParse *info); +MONO_RT_EXTERNAL_ONLY +MONO_API MonoType* mono_reflection_get_type (MonoImage* image, MonoTypeNameParse *info, mono_bool ignorecase, mono_bool *type_resolve); +MONO_API void mono_reflection_free_type_info (MonoTypeNameParse *info); +MONO_RT_EXTERNAL_ONLY +MONO_API MonoType* mono_reflection_type_from_name (char *name, MonoImage *image); +MONO_RT_EXTERNAL_ONLY +MONO_API uint32_t mono_reflection_get_token (MonoObject *obj); + +MONO_RT_EXTERNAL_ONLY +MONO_API MonoReflectionAssembly* mono_assembly_get_object (MonoDomain *domain, MonoAssembly *assembly); +MONO_RT_EXTERNAL_ONLY +MONO_API MonoReflectionModule* mono_module_get_object (MonoDomain *domain, MonoImage *image); +MONO_RT_EXTERNAL_ONLY +MONO_API MonoReflectionModule* mono_module_file_get_object (MonoDomain *domain, MonoImage *image, int table_index); +MONO_API MonoReflectionType* mono_type_get_object (MonoDomain *domain, MonoType *type); +MONO_RT_EXTERNAL_ONLY +MONO_API MonoReflectionMethod* mono_method_get_object (MonoDomain *domain, MonoMethod *method, MonoClass *refclass); +MONO_RT_EXTERNAL_ONLY +MONO_API MonoReflectionField* mono_field_get_object (MonoDomain *domain, MonoClass *klass, MonoClassField *field); +MONO_RT_EXTERNAL_ONLY +MONO_API MonoReflectionProperty* mono_property_get_object (MonoDomain *domain, MonoClass *klass, MonoProperty *property); +MONO_RT_EXTERNAL_ONLY +MONO_API MonoReflectionEvent* mono_event_get_object (MonoDomain *domain, MonoClass *klass, MonoEvent *event); +/* note: this one is slightly different: we keep the whole array of params in the cache */ +MONO_RT_EXTERNAL_ONLY +MONO_API MonoArray* mono_param_get_objects (MonoDomain *domain, MonoMethod *method); +MONO_RT_EXTERNAL_ONLY +MONO_API MonoReflectionMethodBody* mono_method_body_get_object (MonoDomain *domain, MonoMethod *method); + +MONO_API MonoObject *mono_get_dbnull_object (MonoDomain *domain); + +MONO_API MonoArray* mono_reflection_get_custom_attrs_by_type (MonoObject *obj, MonoClass *attr_klass, MonoError *error); +MONO_API MonoArray* mono_reflection_get_custom_attrs (MonoObject *obj); +MONO_RT_EXTERNAL_ONLY +MONO_API MonoArray* mono_reflection_get_custom_attrs_data (MonoObject *obj); +MONO_RT_EXTERNAL_ONLY +MONO_API MonoArray* mono_reflection_get_custom_attrs_blob (MonoReflectionAssembly *assembly, MonoObject *ctor, MonoArray *ctorArgs, MonoArray *properties, MonoArray *porpValues, MonoArray *fields, MonoArray* fieldValues); + +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 +MONO_API MonoCustomAttrInfo* mono_custom_attrs_from_method (MonoMethod *method); +MONO_RT_EXTERNAL_ONLY +MONO_API MonoCustomAttrInfo* mono_custom_attrs_from_class (MonoClass *klass); +MONO_RT_EXTERNAL_ONLY +MONO_API MonoCustomAttrInfo* mono_custom_attrs_from_assembly (MonoAssembly *assembly); +MONO_RT_EXTERNAL_ONLY +MONO_API MonoCustomAttrInfo* mono_custom_attrs_from_property (MonoClass *klass, MonoProperty *property); +MONO_RT_EXTERNAL_ONLY +MONO_API MonoCustomAttrInfo* mono_custom_attrs_from_event (MonoClass *klass, MonoEvent *event); +MONO_RT_EXTERNAL_ONLY +MONO_API MonoCustomAttrInfo* mono_custom_attrs_from_field (MonoClass *klass, MonoClassField *field); +MONO_RT_EXTERNAL_ONLY +MONO_API MonoCustomAttrInfo* mono_custom_attrs_from_param (MonoMethod *method, uint32_t param); +MONO_API mono_bool mono_custom_attrs_has_attr (MonoCustomAttrInfo *ainfo, MonoClass *attr_klass); +MONO_RT_EXTERNAL_ONLY +MONO_API MonoObject* mono_custom_attrs_get_attr (MonoCustomAttrInfo *ainfo, MonoClass *attr_klass); +MONO_API void mono_custom_attrs_free (MonoCustomAttrInfo *ainfo); + + +#define MONO_DECLSEC_ACTION_MIN 0x1 +#define MONO_DECLSEC_ACTION_MAX 0x12 + +enum { + MONO_DECLSEC_FLAG_REQUEST = 0x00000001, + MONO_DECLSEC_FLAG_DEMAND = 0x00000002, + MONO_DECLSEC_FLAG_ASSERT = 0x00000004, + MONO_DECLSEC_FLAG_DENY = 0x00000008, + MONO_DECLSEC_FLAG_PERMITONLY = 0x00000010, + MONO_DECLSEC_FLAG_LINKDEMAND = 0x00000020, + MONO_DECLSEC_FLAG_INHERITANCEDEMAND = 0x00000040, + MONO_DECLSEC_FLAG_REQUEST_MINIMUM = 0x00000080, + MONO_DECLSEC_FLAG_REQUEST_OPTIONAL = 0x00000100, + MONO_DECLSEC_FLAG_REQUEST_REFUSE = 0x00000200, + MONO_DECLSEC_FLAG_PREJIT_GRANT = 0x00000400, + MONO_DECLSEC_FLAG_PREJIT_DENY = 0x00000800, + MONO_DECLSEC_FLAG_NONCAS_DEMAND = 0x00001000, + MONO_DECLSEC_FLAG_NONCAS_LINKDEMAND = 0x00002000, + MONO_DECLSEC_FLAG_NONCAS_INHERITANCEDEMAND = 0x00004000, + MONO_DECLSEC_FLAG_LINKDEMAND_CHOICE = 0x00008000, + MONO_DECLSEC_FLAG_INHERITANCEDEMAND_CHOICE = 0x00010000, + MONO_DECLSEC_FLAG_DEMAND_CHOICE = 0x00020000 +}; + +MONO_API uint32_t mono_declsec_flags_from_method (MonoMethod *method); +MONO_API uint32_t mono_declsec_flags_from_class (MonoClass *klass); +MONO_API uint32_t mono_declsec_flags_from_assembly (MonoAssembly *assembly); + +/* this structure MUST be kept in synch with RuntimeDeclSecurityEntry + * located in /mcs/class/corlib/System.Security/SecurityFrame.cs */ +typedef struct { + char *blob; /* pointer to metadata blob */ + uint32_t size; /* size of the metadata blob */ + uint32_t index; +} MonoDeclSecurityEntry; + +typedef struct { + MonoDeclSecurityEntry demand; + MonoDeclSecurityEntry noncasdemand; + MonoDeclSecurityEntry demandchoice; +} MonoDeclSecurityActions; + +MONO_API MonoBoolean mono_declsec_get_demands (MonoMethod *callee, MonoDeclSecurityActions* demands); +MONO_API MonoBoolean mono_declsec_get_linkdemands (MonoMethod *callee, MonoDeclSecurityActions* klass, MonoDeclSecurityActions* cmethod); +MONO_API MonoBoolean mono_declsec_get_inheritdemands_class (MonoClass *klass, MonoDeclSecurityActions* demands); +MONO_API MonoBoolean mono_declsec_get_inheritdemands_method (MonoMethod *callee, MonoDeclSecurityActions* demands); + +MONO_API MonoBoolean mono_declsec_get_method_action (MonoMethod *method, uint32_t action, MonoDeclSecurityEntry *entry); +MONO_API MonoBoolean mono_declsec_get_class_action (MonoClass *klass, uint32_t action, MonoDeclSecurityEntry *entry); +MONO_API MonoBoolean mono_declsec_get_assembly_action (MonoAssembly *assembly, uint32_t action, MonoDeclSecurityEntry *entry); + +MONO_API MonoType* mono_reflection_type_get_type (MonoReflectionType *reftype); + +MONO_API MonoAssembly* mono_reflection_assembly_get_assembly (MonoReflectionAssembly *refassembly); + +MONO_END_DECLS + +#endif /* __METADATA_REFLECTION_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/remoting.c b/unity-2019.4.24f1-mbe/mono/metadata/remoting.c new file mode 100644 index 000000000..73d7cfb69 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/remoting.c @@ -0,0 +1,2105 @@ +/** + * \file + * Remoting support + * + * Copyright 2002-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Copyright 2011-2014 Xamarin, Inc (http://www.xamarin.com) + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include "config.h" + +#include "mono/metadata/handle.h" +#include "mono/metadata/remoting.h" +#include "mono/metadata/marshal.h" +#include "mono/metadata/marshal-internals.h" +#include "mono/metadata/abi-details.h" +#include "mono/metadata/cominterop.h" +#include "mono/metadata/tabledefs.h" +#include "mono/metadata/exception.h" +#include "mono/metadata/debug-helpers.h" +#include "mono/metadata/reflection-internals.h" +#include "mono/metadata/assembly.h" + +typedef enum { + MONO_MARSHAL_NONE, /* No marshalling needed */ + MONO_MARSHAL_COPY, /* Can be copied by value to the new domain */ + MONO_MARSHAL_COPY_OUT, /* out parameter that needs to be copied back to the original instance */ + MONO_MARSHAL_SERIALIZE /* Value needs to be serialized into the new domain */ +} MonoXDomainMarshalType; + +#ifndef DISABLE_REMOTING + +#define OPDEF(a,b,c,d,e,f,g,h,i,j) \ + a = i, + +enum { +#include "mono/cil/opcode.def" + LAST = 0xff +}; +#undef OPDEF + +struct _MonoRemotingMethods { + MonoMethod *invoke; + MonoMethod *invoke_with_check; + MonoMethod *xdomain_invoke; + MonoMethod *xdomain_dispatch; +}; + +typedef struct _MonoRemotingMethods MonoRemotingMethods; + +static MonoObject * +mono_remoting_wrapper (MonoMethod *method, gpointer *params); + +static MonoException * +mono_remoting_update_exception (MonoException *exc); + +static gint32 +mono_marshal_set_domain_by_id (gint32 id, MonoBoolean push); + +static gboolean +mono_marshal_check_domain_image (gint32 domain_id, MonoImage *image); + +MONO_API void +mono_upgrade_remote_class_wrapper (MonoReflectionType *rtype, MonoTransparentProxy *tproxy); + +static MonoXDomainMarshalType +mono_get_xdomain_marshal_type (MonoType *t); + +static void +mono_marshal_xdomain_copy_out_value (MonoObject *src, MonoObject *dst); + +static MonoReflectionType * +type_from_handle (MonoType *handle); + +static void +mono_context_set_icall (MonoAppContext *new_context); + +static MonoAppContext* +mono_context_get_icall (void); + + +/* Class lazy loading functions */ +static GENERATE_GET_CLASS_WITH_CACHE (remoting_services, "System.Runtime.Remoting", "RemotingServices") +static GENERATE_GET_CLASS_WITH_CACHE (call_context, "System.Runtime.Remoting.Messaging", "CallContext") +static GENERATE_GET_CLASS_WITH_CACHE (context, "System.Runtime.Remoting.Contexts", "Context") + +static mono_mutex_t remoting_mutex; +static gboolean remoting_mutex_inited; + +static MonoClass *byte_array_class; +#ifndef DISABLE_JIT +static MonoMethod *method_rs_serialize, *method_rs_deserialize, *method_exc_fixexc, *method_rs_appdomain_target; +static MonoMethod *method_set_call_context, *method_needs_context_sink, *method_rs_serialize_exc; +#endif + +static gpointer +mono_compile_method_icall (MonoMethod *method); + +static void +register_icall (gpointer func, const char *name, const char *sigstr, gboolean save) +{ + MonoMethodSignature *sig = mono_create_icall_signature (sigstr); + + mono_register_jit_icall (func, name, sig, save); +} + +static inline void +remoting_lock (void) +{ + g_assert (remoting_mutex_inited); + mono_os_mutex_lock (&remoting_mutex); +} + +static inline void +remoting_unlock (void) +{ + g_assert (remoting_mutex_inited); + mono_os_mutex_unlock (&remoting_mutex); +} + +/* + * Return the hash table pointed to by VAR, lazily creating it if neccesary. + */ +static GHashTable* +get_cache (GHashTable **var, GHashFunc hash_func, GCompareFunc equal_func) +{ + if (!(*var)) { + remoting_lock (); + if (!(*var)) { + GHashTable *cache = + g_hash_table_new (hash_func, equal_func); + mono_memory_barrier (); + *var = cache; + } + remoting_unlock (); + } + return *var; +} + +static GHashTable* +get_cache_full (GHashTable **var, GHashFunc hash_func, GCompareFunc equal_func, GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func) +{ + if (!(*var)) { + remoting_lock (); + if (!(*var)) { + GHashTable *cache = + g_hash_table_new_full (hash_func, equal_func, key_destroy_func, value_destroy_func); + mono_memory_barrier (); + *var = cache; + } + remoting_unlock (); + } + return *var; +} + +void +mono_remoting_init (void) +{ + mono_os_mutex_init (&remoting_mutex); + remoting_mutex_inited = TRUE; +} + +static void +mono_remoting_marshal_init (void) +{ + MonoClass *klass; + + static gboolean module_initialized = FALSE; + static gboolean icalls_registered = FALSE; + + if (module_initialized) + return; + + byte_array_class = mono_array_class_get (mono_defaults.byte_class, 1); + +#ifndef DISABLE_JIT + klass = mono_class_get_remoting_services_class (); + method_rs_serialize = mono_class_get_method_from_name (klass, "SerializeCallData", -1); + g_assert (method_rs_serialize); + method_rs_deserialize = mono_class_get_method_from_name (klass, "DeserializeCallData", -1); + g_assert (method_rs_deserialize); + method_rs_serialize_exc = mono_class_get_method_from_name (klass, "SerializeExceptionData", -1); + g_assert (method_rs_serialize_exc); + + klass = mono_defaults.real_proxy_class; + method_rs_appdomain_target = mono_class_get_method_from_name (klass, "GetAppDomainTarget", -1); + g_assert (method_rs_appdomain_target); + + klass = mono_defaults.exception_class; + method_exc_fixexc = mono_class_get_method_from_name (klass, "FixRemotingException", -1); + g_assert (method_exc_fixexc); + + klass = mono_class_get_call_context_class (); + method_set_call_context = mono_class_get_method_from_name (klass, "SetCurrentCallContext", -1); + g_assert (method_set_call_context); + + klass = mono_class_get_context_class (); + method_needs_context_sink = mono_class_get_method_from_name (klass, "get_NeedsContextSink", -1); + g_assert (method_needs_context_sink); +#endif + + mono_loader_lock (); + + if (!icalls_registered) { + register_icall (type_from_handle, "type_from_handle", "object ptr", FALSE); + register_icall (mono_marshal_set_domain_by_id, "mono_marshal_set_domain_by_id", "int32 int32 int32", FALSE); + register_icall (mono_marshal_check_domain_image, "mono_marshal_check_domain_image", "int32 int32 ptr", FALSE); + register_icall (ves_icall_mono_marshal_xdomain_copy_value, "ves_icall_mono_marshal_xdomain_copy_value", "object object", FALSE); + register_icall (mono_marshal_xdomain_copy_out_value, "mono_marshal_xdomain_copy_out_value", "void object object", FALSE); + register_icall (mono_remoting_wrapper, "mono_remoting_wrapper", "object ptr ptr", FALSE); + register_icall (mono_remoting_update_exception, "mono_remoting_update_exception", "object object", FALSE); + register_icall (mono_upgrade_remote_class_wrapper, "mono_upgrade_remote_class_wrapper", "void object object", FALSE); + +#ifndef DISABLE_JIT + register_icall (mono_compile_method_icall, "mono_compile_method_icall", "ptr ptr", FALSE); +#endif + + register_icall (mono_context_get_icall, "mono_context_get_icall", "object", FALSE); + register_icall (mono_context_set_icall, "mono_context_set_icall", "void object", FALSE); + + } + + icalls_registered = TRUE; + + mono_loader_unlock (); + + module_initialized = TRUE; +} + +/* This is an icall, it will return NULL and set pending exception on failure */ +static MonoReflectionType * +type_from_handle (MonoType *handle) +{ + MonoError error; + MonoReflectionType *ret; + MonoDomain *domain = mono_domain_get (); + MonoClass *klass = mono_class_from_mono_type (handle); + + mono_class_init (klass); + + ret = mono_type_get_object_checked (domain, handle, &error); + mono_error_set_pending_exception (&error); + + return ret; +} + +#ifndef DISABLE_JIT +static int +mono_mb_emit_proxy_check (MonoMethodBuilder *mb, int branch_code) +{ + int pos; + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoObject, vtable)); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoVTable, klass)); + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_CLASSCONST); + mono_mb_emit_i4 (mb, mono_mb_add_data (mb, mono_defaults.transparent_proxy_class)); + pos = mono_mb_emit_branch (mb, branch_code); + return pos; +} + +static int +mono_mb_emit_xdomain_check (MonoMethodBuilder *mb, int branch_code) +{ + int pos; + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoTransparentProxy, rp)); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoRealProxy, target_domain_id)); + mono_mb_emit_byte (mb, CEE_LDIND_I4); + mono_mb_emit_icon (mb, -1); + pos = mono_mb_emit_branch (mb, branch_code); + return pos; +} + +static int +mono_mb_emit_contextbound_check (MonoMethodBuilder *mb, int branch_code) +{ + static int offset = -1; + static guint8 mask; + + if (offset < 0) + mono_marshal_find_bitfield_offset (MonoClass, contextbound, &offset, &mask); + + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoTransparentProxy, remote_class)); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoRemoteClass, proxy_class)); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_ldflda (mb, offset); + mono_mb_emit_byte (mb, CEE_LDIND_U1); + mono_mb_emit_icon (mb, mask); + mono_mb_emit_byte (mb, CEE_AND); + mono_mb_emit_icon (mb, 0); + return mono_mb_emit_branch (mb, branch_code); +} +#endif /* !DISABLE_JIT */ + +static inline MonoMethod* +mono_marshal_remoting_find_in_cache (MonoMethod *method, int wrapper_type) +{ + MonoMethod *res = NULL; + MonoRemotingMethods *wrps = NULL; + + mono_marshal_lock_internal (); + if (mono_method_get_wrapper_cache (method)->remoting_invoke_cache) + wrps = (MonoRemotingMethods *)g_hash_table_lookup (mono_method_get_wrapper_cache (method)->remoting_invoke_cache, method); + + if (wrps) { + switch (wrapper_type) { + case MONO_WRAPPER_REMOTING_INVOKE: res = wrps->invoke; break; + case MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK: res = wrps->invoke_with_check; break; + case MONO_WRAPPER_XDOMAIN_INVOKE: res = wrps->xdomain_invoke; break; + case MONO_WRAPPER_XDOMAIN_DISPATCH: res = wrps->xdomain_dispatch; break; + } + } + + /* it is important to do the unlock after the load from wrps, since in + * mono_remoting_mb_create_and_cache () we drop the marshal lock to be able + * to take the loader lock and some other thread may set the fields. + */ + mono_marshal_unlock_internal (); + return res; +} + +/* Create the method from the builder and place it in the cache */ +static inline MonoMethod* +mono_remoting_mb_create_and_cache (MonoMethod *key, MonoMethodBuilder *mb, + MonoMethodSignature *sig, int max_stack, WrapperInfo *info) +{ + MonoMethod **res = NULL; + MonoRemotingMethods *wrps; + GHashTable *cache; + + cache = get_cache_full (&mono_method_get_wrapper_cache (key)->remoting_invoke_cache, mono_aligned_addr_hash, NULL, NULL, g_free); + + mono_marshal_lock_internal (); + wrps = (MonoRemotingMethods *)g_hash_table_lookup (cache, key); + if (!wrps) { + wrps = g_new0 (MonoRemotingMethods, 1); + g_hash_table_insert (cache, key, wrps); + } + + switch (mb->method->wrapper_type) { + case MONO_WRAPPER_REMOTING_INVOKE: res = &wrps->invoke; break; + case MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK: res = &wrps->invoke_with_check; break; + case MONO_WRAPPER_XDOMAIN_INVOKE: res = &wrps->xdomain_invoke; break; + case MONO_WRAPPER_XDOMAIN_DISPATCH: res = &wrps->xdomain_dispatch; break; + default: g_assert_not_reached (); break; + } + mono_marshal_unlock_internal (); + + if (*res == NULL) { + MonoMethod *newm; + newm = mono_mb_create_method (mb, sig, max_stack); + + mono_marshal_lock_internal (); + if (!*res) { + *res = newm; + mono_marshal_set_wrapper_info (*res, info); + mono_marshal_unlock_internal (); + } else { + mono_marshal_unlock_internal (); + mono_free_method (newm); + } + } + + return *res; +} + +static MonoObject * +mono_remoting_wrapper (MonoMethod *method, gpointer *params) +{ + MonoError error; + MonoMethodMessage *msg; + MonoTransparentProxy *this_obj; + MonoObject *res, *exc; + MonoArray *out_args; + + this_obj = *((MonoTransparentProxy **)params [0]); + + g_assert (this_obj); + g_assert (mono_object_is_transparent_proxy (this_obj)); + + /* skip the this pointer */ + params++; + + if (mono_class_is_contextbound (this_obj->remote_class->proxy_class) && this_obj->rp->context == (MonoObject *) mono_context_get ()) + { + int i; + MonoMethodSignature *sig = mono_method_signature (method); + int count = sig->param_count; + gpointer* mparams = (gpointer*) alloca(count*sizeof(gpointer)); + + for (i=0; iparams [i]); + if (klass->valuetype) { + if (sig->params [i]->byref) { + mparams[i] = *((gpointer *)params [i]); + } else { + /* runtime_invoke expects a boxed instance */ + if (mono_class_is_nullable (mono_class_from_mono_type (sig->params [i]))) { + mparams[i] = mono_nullable_box ((guint8 *)params [i], klass, &error); + goto_if_nok (&error, fail); + } else + mparams[i] = params [i]; + } + } else { + mparams[i] = *((gpointer**)params [i]); + } + } + + res = mono_runtime_invoke_checked (method, method->klass->valuetype? mono_object_unbox ((MonoObject*)this_obj): this_obj, mparams, &error); + goto_if_nok (&error, fail); + + return res; + } + + msg = mono_method_call_message_new (method, params, NULL, NULL, NULL, &error); + goto_if_nok (&error, fail); + + res = mono_remoting_invoke ((MonoObject *)this_obj->rp, msg, &exc, &out_args, &error); + goto_if_nok (&error, fail); + + if (exc) { + error_init (&error); + exc = (MonoObject*) mono_remoting_update_exception ((MonoException*)exc); + mono_error_set_exception_instance (&error, (MonoException *)exc); + goto fail; + } + + mono_method_return_message_restore (method, params, out_args, &error); + goto_if_nok (&error, fail); + + return res; +fail: + mono_error_set_pending_exception (&error); + return NULL; +} + +/* + * Handles exception transformation at appdomain call boundary. + * Note this is called from target appdomain inside xdomain wrapper, but from + * source domain in the mono_remoting_wrapper slowpath. + */ +static MonoException * +mono_remoting_update_exception (MonoException *exc) +{ + MonoInternalThread *thread; + MonoClass *klass = mono_object_get_class ((MonoObject*)exc); + + /* Serialization error can only happen when still in the target appdomain */ + if (!(mono_class_get_flags (klass) & TYPE_ATTRIBUTE_SERIALIZABLE)) { + MonoException *ret; + char *aname = mono_stringify_assembly_name (&klass->image->assembly->aname); + char *message = g_strdup_printf ("Type '%s' in Assembly '%s' is not marked as serializable", klass->name, aname); + ret = mono_get_exception_serialization (message); + g_free (aname); + g_free (message); + return ret; + } + + thread = mono_thread_internal_current (); + if (mono_object_get_class ((MonoObject*)exc) == mono_defaults.threadabortexception_class && + thread->flags & MONO_THREAD_FLAG_APPDOMAIN_ABORT) { + mono_thread_internal_reset_abort (thread); + return mono_get_exception_appdomain_unloaded (); + } + + return exc; +} + +/** + * mono_marshal_get_remoting_invoke: + */ +MonoMethod * +mono_marshal_get_remoting_invoke (MonoMethod *method) +{ + MonoMethodSignature *sig; + MonoMethodBuilder *mb; + MonoMethod *res; + int params_var; + WrapperInfo *info; + + g_assert (method); + + if (method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE || method->wrapper_type == MONO_WRAPPER_XDOMAIN_INVOKE) + return method; + + /* this seems to be the best plase to put this, as all remoting invokes seem to get filtered through here */ +#ifndef DISABLE_COM + if (mono_class_is_com_object (method->klass) || method->klass == mono_class_try_get_com_object_class ()) { + MonoVTable *vtable = mono_class_vtable (mono_domain_get (), method->klass); + g_assert (vtable); /*FIXME do proper error handling*/ + + if (!mono_vtable_is_remote (vtable)) { + return mono_cominterop_get_invoke (method); + } + } +#endif + + sig = mono_signature_no_pinvoke (method); + + /* we cant remote methods without this pointer */ + if (!sig->hasthis) + return method; + + if ((res = mono_marshal_remoting_find_in_cache (method, MONO_WRAPPER_REMOTING_INVOKE))) + return res; + + mono_remoting_marshal_init (); + + mb = mono_mb_new (method->klass, method->name, MONO_WRAPPER_REMOTING_INVOKE); + +#ifndef DISABLE_JIT + mb->method->save_lmf = 1; + + params_var = mono_mb_emit_save_args (mb, sig, TRUE); + + mono_mb_emit_ptr (mb, method); + mono_mb_emit_ldloc (mb, params_var); + mono_mb_emit_icall (mb, mono_remoting_wrapper); + // FIXME: this interrupt checkpoint code is a no-op since 'mb' + // is a MONO_WRAPPER_REMOTING_INVOKE, and + // mono_thread_interruption_checkpoint_request (FALSE) + // considers such wrappers "protected" and always returns + // NULL as if there's no pending interruption. + mono_marshal_emit_thread_interrupt_checkpoint (mb); + + if (sig->ret->type == MONO_TYPE_VOID) { + mono_mb_emit_byte (mb, CEE_POP); + mono_mb_emit_byte (mb, CEE_RET); + } else { + mono_mb_emit_restore_result (mb, sig->ret); + } +#endif + + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); + info->d.remoting.method = method; + res = mono_remoting_mb_create_and_cache (method, mb, sig, sig->param_count + 16, info); + mono_mb_free (mb); + + return res; +} + +/* mono_marshal_xdomain_copy_out_value() + * Copies the contents of the src instance into the dst instance. src and dst + * must have the same type, and if they are arrays, the same size. + * + * This is an icall, it may use mono_error_set_pending_exception + */ +static void +mono_marshal_xdomain_copy_out_value (MonoObject *src, MonoObject *dst) +{ + MonoError error; + if (src == NULL || dst == NULL) return; + + g_assert (mono_object_class (src) == mono_object_class (dst)); + + switch (mono_object_class (src)->byval_arg.type) { + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: { + int mt = mono_get_xdomain_marshal_type (&(mono_object_class (src)->element_class->byval_arg)); + if (mt == MONO_MARSHAL_SERIALIZE) return; + if (mt == MONO_MARSHAL_COPY) { + int i, len = mono_array_length ((MonoArray *)dst); + for (i = 0; i < len; i++) { + MonoObject *item = (MonoObject *)mono_array_get ((MonoArray *)src, gpointer, i); + MonoObject *item_copy = mono_marshal_xdomain_copy_value (item, &error); + if (mono_error_set_pending_exception (&error)) + return; + mono_array_setref ((MonoArray *)dst, i, item_copy); + } + } else { + mono_array_full_copy ((MonoArray *)src, (MonoArray *)dst); + } + return; + } + default: + break; + } + +} + + +#if !defined (DISABLE_JIT) +static void +mono_marshal_emit_xdomain_copy_value (MonoMethodBuilder *mb, MonoClass *pclass) +{ + mono_mb_emit_icall (mb, ves_icall_mono_marshal_xdomain_copy_value); + mono_mb_emit_op (mb, CEE_CASTCLASS, pclass); +} + +static void +mono_marshal_emit_xdomain_copy_out_value (MonoMethodBuilder *mb, MonoClass *pclass) +{ + mono_mb_emit_icall (mb, mono_marshal_xdomain_copy_out_value); +} +#endif + +/* mono_marshal_supports_fast_xdomain() + * Returns TRUE if the method can use the fast xdomain wrapper. + */ +static gboolean +mono_marshal_supports_fast_xdomain (MonoMethod *method) +{ + return !mono_class_is_contextbound (method->klass) && + !((method->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) && (strcmp (".ctor", method->name) == 0)); +} + +static gint32 +mono_marshal_set_domain_by_id (gint32 id, MonoBoolean push) +{ + MonoDomain *current_domain = mono_domain_get (); + MonoDomain *domain = mono_domain_get_by_id (id); + + if (!domain || !mono_domain_set (domain, FALSE)) { + mono_set_pending_exception (mono_get_exception_appdomain_unloaded ()); + return 0; + } + + if (push) + mono_thread_push_appdomain_ref (domain); + else + mono_thread_pop_appdomain_ref (); + + return current_domain->domain_id; +} + +#if !defined (DISABLE_JIT) +static void +mono_marshal_emit_switch_domain (MonoMethodBuilder *mb) +{ + mono_mb_emit_icall (mb, mono_marshal_set_domain_by_id); +} + +gpointer +mono_compile_method_icall (MonoMethod *method) +{ + MonoError error; + gpointer result = mono_compile_method_checked (method, &error); + mono_error_set_pending_exception (&error); + return result; +} + +/* mono_marshal_emit_load_domain_method () + * Loads into the stack a pointer to the code of the provided method for + * the current domain. + */ +static void +mono_marshal_emit_load_domain_method (MonoMethodBuilder *mb, MonoMethod *method) +{ + /* We need a pointer to the method for the running domain (not the domain + * that compiles the method). + */ + mono_mb_emit_ptr (mb, method); + mono_mb_emit_icall (mb, mono_compile_method_icall); +} +#endif + +/* mono_marshal_check_domain_image () + * Returns TRUE if the image is loaded in the specified + * application domain. + */ +static gboolean +mono_marshal_check_domain_image (gint32 domain_id, MonoImage *image) +{ + MonoAssembly* ass; + GSList *tmp; + + MonoDomain *domain = mono_domain_get_by_id (domain_id); + if (!domain) + return FALSE; + + mono_domain_assemblies_lock (domain); + for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) { + ass = (MonoAssembly *)tmp->data; + if (ass->image == image) + break; + } + mono_domain_assemblies_unlock (domain); + + return tmp != NULL; +} + +/* mono_marshal_get_xappdomain_dispatch () + * Generates a method that dispatches a method call from another domain into + * the current domain. + */ +static MonoMethod * +mono_marshal_get_xappdomain_dispatch (MonoMethod *method, int *marshal_types, int complex_count, int complex_out_count, int ret_marshal_type) +{ + MonoMethodSignature *sig, *csig; + MonoMethodBuilder *mb; + MonoMethod *res; + int i, j, param_index, copy_locals_base; + MonoClass *ret_class = NULL; + int loc_array=0, loc_return=0, loc_serialized_exc=0; + MonoExceptionClause *main_clause; + int pos, pos_leave; + gboolean copy_return; + WrapperInfo *info; + + if ((res = mono_marshal_remoting_find_in_cache (method, MONO_WRAPPER_XDOMAIN_DISPATCH))) + return res; + + sig = mono_method_signature (method); + copy_return = (sig->ret->type != MONO_TYPE_VOID && ret_marshal_type != MONO_MARSHAL_SERIALIZE); + + j = 0; + csig = mono_metadata_signature_alloc (mono_defaults.corlib, 3 + sig->param_count - complex_count); + csig->params [j++] = &mono_defaults.object_class->byval_arg; + csig->params [j++] = &byte_array_class->this_arg; + csig->params [j++] = &byte_array_class->this_arg; + for (i = 0; i < sig->param_count; i++) { + if (marshal_types [i] != MONO_MARSHAL_SERIALIZE) + csig->params [j++] = sig->params [i]; + } + if (copy_return) + csig->ret = sig->ret; + else + csig->ret = &mono_defaults.void_class->byval_arg; + csig->pinvoke = 1; + csig->hasthis = FALSE; + + mb = mono_mb_new (method->klass, method->name, MONO_WRAPPER_XDOMAIN_DISPATCH); + mb->method->save_lmf = 1; + +#ifndef DISABLE_JIT + /* Locals */ + + loc_serialized_exc = mono_mb_add_local (mb, &byte_array_class->byval_arg); + if (complex_count > 0) + loc_array = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + if (sig->ret->type != MONO_TYPE_VOID) { + loc_return = mono_mb_add_local (mb, sig->ret); + ret_class = mono_class_from_mono_type (sig->ret); + } + + /* try */ + + main_clause = (MonoExceptionClause *)mono_image_alloc0 (method->klass->image, sizeof (MonoExceptionClause)); + main_clause->try_offset = mono_mb_get_label (mb); + + /* Clean the call context */ + + mono_mb_emit_byte (mb, CEE_LDNULL); + mono_mb_emit_managed_call (mb, method_set_call_context, NULL); + mono_mb_emit_byte (mb, CEE_POP); + + /* Deserialize call data */ + + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_byte (mb, CEE_DUP); + pos = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S); + + mono_marshal_emit_xdomain_copy_value (mb, byte_array_class); + mono_mb_emit_managed_call (mb, method_rs_deserialize, NULL); + + if (complex_count > 0) + mono_mb_emit_stloc (mb, loc_array); + else + mono_mb_emit_byte (mb, CEE_POP); + + mono_mb_patch_short_branch (mb, pos); + + /* Get the target object */ + + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_managed_call (mb, method_rs_appdomain_target, NULL); + + /* Load the arguments */ + + copy_locals_base = mb->locals; + param_index = 3; // Index of the first non-serialized parameter of this wrapper + j = 0; + for (i = 0; i < sig->param_count; i++) { + MonoType *pt = sig->params [i]; + MonoClass *pclass = mono_class_from_mono_type (pt); + switch (marshal_types [i]) { + case MONO_MARSHAL_SERIALIZE: { + /* take the value from the serialized array */ + mono_mb_emit_ldloc (mb, loc_array); + mono_mb_emit_icon (mb, j++); + if (pt->byref) { + if (pclass->valuetype) { + mono_mb_emit_byte (mb, CEE_LDELEM_REF); + mono_mb_emit_op (mb, CEE_UNBOX, pclass); + } else { + mono_mb_emit_op (mb, CEE_LDELEMA, pclass); + } + } else { + if (pclass->valuetype) { + mono_mb_emit_byte (mb, CEE_LDELEM_REF); + mono_mb_emit_op (mb, CEE_UNBOX, pclass); + mono_mb_emit_op (mb, CEE_LDOBJ, pclass); + } else { + mono_mb_emit_byte (mb, CEE_LDELEM_REF); + if (pclass != mono_defaults.object_class) { + mono_mb_emit_op (mb, CEE_CASTCLASS, pclass); + } + } + } + break; + } + case MONO_MARSHAL_COPY_OUT: { + /* Keep a local copy of the value since we need to copy it back after the call */ + int copy_local = mono_mb_add_local (mb, &(pclass->byval_arg)); + mono_mb_emit_ldarg (mb, param_index++); + mono_marshal_emit_xdomain_copy_value (mb, pclass); + mono_mb_emit_byte (mb, CEE_DUP); + mono_mb_emit_stloc (mb, copy_local); + break; + } + case MONO_MARSHAL_COPY: { + mono_mb_emit_ldarg (mb, param_index); + if (pt->byref) { + mono_mb_emit_byte (mb, CEE_DUP); + mono_mb_emit_byte (mb, CEE_DUP); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_marshal_emit_xdomain_copy_value (mb, pclass); + mono_mb_emit_byte (mb, CEE_STIND_REF); + } else { + mono_marshal_emit_xdomain_copy_value (mb, pclass); + } + param_index++; + break; + } + case MONO_MARSHAL_NONE: + mono_mb_emit_ldarg (mb, param_index++); + break; + } + } + + /* Make the call to the real object */ + + mono_marshal_emit_thread_force_interrupt_checkpoint (mb); + + mono_mb_emit_op (mb, CEE_CALLVIRT, method); + + if (sig->ret->type != MONO_TYPE_VOID) + mono_mb_emit_stloc (mb, loc_return); + + /* copy back MONO_MARSHAL_COPY_OUT parameters */ + + j = 0; + param_index = 3; + for (i = 0; i < sig->param_count; i++) { + if (marshal_types [i] == MONO_MARSHAL_SERIALIZE) continue; + if (marshal_types [i] == MONO_MARSHAL_COPY_OUT) { + mono_mb_emit_ldloc (mb, copy_locals_base + (j++)); + mono_mb_emit_ldarg (mb, param_index); + mono_marshal_emit_xdomain_copy_out_value (mb, mono_class_from_mono_type (sig->params [i])); + } + param_index++; + } + + /* Serialize the return values */ + + if (complex_out_count > 0) { + /* Reset parameters in the array that don't need to be serialized back */ + j = 0; + for (i = 0; i < sig->param_count; i++) { + if (marshal_types[i] != MONO_MARSHAL_SERIALIZE) continue; + if (!sig->params [i]->byref) { + mono_mb_emit_ldloc (mb, loc_array); + mono_mb_emit_icon (mb, j); + mono_mb_emit_byte (mb, CEE_LDNULL); + mono_mb_emit_byte (mb, CEE_STELEM_REF); + } + j++; + } + + /* Add the return value to the array */ + + if (ret_marshal_type == MONO_MARSHAL_SERIALIZE) { + mono_mb_emit_ldloc (mb, loc_array); + mono_mb_emit_icon (mb, complex_count); /* The array has an additional slot to hold the ret value */ + mono_mb_emit_ldloc (mb, loc_return); + + g_assert (ret_class); /*FIXME properly fail here*/ + if (ret_class->valuetype) { + mono_mb_emit_op (mb, CEE_BOX, ret_class); + } + mono_mb_emit_byte (mb, CEE_STELEM_REF); + } + + /* Serialize */ + + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_ldloc (mb, loc_array); + mono_mb_emit_managed_call (mb, method_rs_serialize, NULL); + mono_mb_emit_byte (mb, CEE_STIND_REF); + } else if (ret_marshal_type == MONO_MARSHAL_SERIALIZE) { + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_ldloc (mb, loc_return); + if (ret_class->valuetype) { + mono_mb_emit_op (mb, CEE_BOX, ret_class); + } + mono_mb_emit_managed_call (mb, method_rs_serialize, NULL); + mono_mb_emit_byte (mb, CEE_STIND_REF); + } else { + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_byte (mb, CEE_LDNULL); + mono_mb_emit_managed_call (mb, method_rs_serialize, NULL); + mono_mb_emit_byte (mb, CEE_STIND_REF); + } + + mono_mb_emit_ldarg (mb, 2); + mono_mb_emit_byte (mb, CEE_LDNULL); + mono_mb_emit_byte (mb, CEE_STIND_REF); + pos_leave = mono_mb_emit_branch (mb, CEE_LEAVE); + + /* Main exception catch */ + main_clause->flags = MONO_EXCEPTION_CLAUSE_NONE; + main_clause->try_len = mono_mb_get_pos (mb) - main_clause->try_offset; + main_clause->data.catch_class = mono_defaults.object_class; + + /* handler code */ + main_clause->handler_offset = mono_mb_get_label (mb); + + mono_mb_emit_icall (mb, mono_remoting_update_exception); + mono_mb_emit_op (mb, CEE_CASTCLASS, mono_defaults.exception_class); + mono_mb_emit_managed_call (mb, method_rs_serialize_exc, NULL); + mono_mb_emit_stloc (mb, loc_serialized_exc); + mono_mb_emit_ldarg (mb, 2); + mono_mb_emit_ldloc (mb, loc_serialized_exc); + mono_mb_emit_byte (mb, CEE_STIND_REF); + mono_mb_emit_branch (mb, CEE_LEAVE); + + main_clause->handler_len = mono_mb_get_pos (mb) - main_clause->handler_offset; + /* end catch */ + mono_mb_patch_branch (mb, pos_leave); + + if (copy_return) + mono_mb_emit_ldloc (mb, loc_return); + + mono_mb_emit_byte (mb, CEE_RET); + + mono_mb_set_clauses (mb, 1, main_clause); +#endif + + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); + info->d.remoting.method = method; + res = mono_remoting_mb_create_and_cache (method, mb, csig, csig->param_count + 16, info); + mono_mb_free (mb); + + return res; +} + +/** + * mono_marshal_get_xappdomain_invoke: + * Generates a fast remoting wrapper for cross app domain calls. + */ +MonoMethod * +mono_marshal_get_xappdomain_invoke (MonoMethod *method) +{ + MonoMethodSignature *sig; + MonoMethodBuilder *mb; + MonoMethod *res; + int i, j, complex_count, complex_out_count, copy_locals_base; + int *marshal_types; + MonoClass *ret_class = NULL; + MonoMethod *xdomain_method; + int ret_marshal_type = MONO_MARSHAL_NONE; + int loc_array=0, loc_serialized_data=-1, loc_real_proxy; + int loc_old_domainid, loc_domainid, loc_return=0, loc_serialized_exc=0, loc_context; + int pos, pos_dispatch, pos_noex; + gboolean copy_return = FALSE; + WrapperInfo *info; + + g_assert (method); + + if (method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE || method->wrapper_type == MONO_WRAPPER_XDOMAIN_INVOKE) + return method; + + /* we cant remote methods without this pointer */ + if (!mono_method_signature (method)->hasthis) + return method; + + mono_remoting_marshal_init (); + + if (!mono_marshal_supports_fast_xdomain (method)) + return mono_marshal_get_remoting_invoke (method); + + if ((res = mono_marshal_remoting_find_in_cache (method, MONO_WRAPPER_XDOMAIN_INVOKE))) + return res; + + sig = mono_signature_no_pinvoke (method); + + mb = mono_mb_new (method->klass, method->name, MONO_WRAPPER_XDOMAIN_INVOKE); + mb->method->save_lmf = 1; + + /* Count the number of parameters that need to be serialized */ + + marshal_types = (int *)alloca (sizeof (int) * sig->param_count); + complex_count = complex_out_count = 0; + for (i = 0; i < sig->param_count; i++) { + MonoType *ptype = sig->params[i]; + int mt = mono_get_xdomain_marshal_type (ptype); + + /* If the [Out] attribute is applied to a parameter that can be internally copied, + * the copy will be made by reusing the original object instance + */ + if ((ptype->attrs & PARAM_ATTRIBUTE_OUT) != 0 && mt == MONO_MARSHAL_COPY && !ptype->byref) + mt = MONO_MARSHAL_COPY_OUT; + else if (mt == MONO_MARSHAL_SERIALIZE) { + complex_count++; + if (ptype->byref) complex_out_count++; + } + marshal_types [i] = mt; + } + + if (sig->ret->type != MONO_TYPE_VOID) { + ret_marshal_type = mono_get_xdomain_marshal_type (sig->ret); + ret_class = mono_class_from_mono_type (sig->ret); + copy_return = ret_marshal_type != MONO_MARSHAL_SERIALIZE; + } + + /* Locals */ + +#ifndef DISABLE_JIT + if (complex_count > 0) + loc_array = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + loc_serialized_data = mono_mb_add_local (mb, &byte_array_class->byval_arg); + loc_real_proxy = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + if (copy_return) + loc_return = mono_mb_add_local (mb, sig->ret); + loc_old_domainid = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg); + loc_domainid = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg); + loc_serialized_exc = mono_mb_add_local (mb, &byte_array_class->byval_arg); + loc_context = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + + /* Save thread domain data */ + + mono_mb_emit_icall (mb, mono_context_get_icall); + mono_mb_emit_byte (mb, CEE_DUP); + mono_mb_emit_stloc (mb, loc_context); + + /* If the thread is not running in the default context, it needs to go + * through the whole remoting sink, since the context is going to change + */ + mono_mb_emit_managed_call (mb, method_needs_context_sink, NULL); + pos = mono_mb_emit_short_branch (mb, CEE_BRTRUE_S); + + /* Another case in which the fast path can't be used: when the target domain + * has a different image for the same assembly. + */ + + /* Get the target domain id */ + + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoTransparentProxy, rp)); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_byte (mb, CEE_DUP); + mono_mb_emit_stloc (mb, loc_real_proxy); + + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoRealProxy, target_domain_id)); + mono_mb_emit_byte (mb, CEE_LDIND_I4); + mono_mb_emit_stloc (mb, loc_domainid); + + /* Check if the target domain has the same image for the required assembly */ + + mono_mb_emit_ldloc (mb, loc_domainid); + mono_mb_emit_ptr (mb, method->klass->image); + mono_mb_emit_icall (mb, mono_marshal_check_domain_image); + pos_dispatch = mono_mb_emit_short_branch (mb, CEE_BRTRUE_S); + + /* Use the whole remoting sink to dispatch this message */ + + mono_mb_patch_short_branch (mb, pos); + + mono_mb_emit_ldarg (mb, 0); + for (i = 0; i < sig->param_count; i++) + mono_mb_emit_ldarg (mb, i + 1); + + mono_mb_emit_managed_call (mb, mono_marshal_get_remoting_invoke (method), NULL); + mono_mb_emit_byte (mb, CEE_RET); + mono_mb_patch_short_branch (mb, pos_dispatch); + + /* Create the array that will hold the parameters to be serialized */ + + if (complex_count > 0) { + mono_mb_emit_icon (mb, (ret_marshal_type == MONO_MARSHAL_SERIALIZE && complex_out_count > 0) ? complex_count + 1 : complex_count); /* +1 for the return type */ + mono_mb_emit_op (mb, CEE_NEWARR, mono_defaults.object_class); + + j = 0; + for (i = 0; i < sig->param_count; i++) { + MonoClass *pclass; + if (marshal_types [i] != MONO_MARSHAL_SERIALIZE) continue; + pclass = mono_class_from_mono_type (sig->params[i]); + mono_mb_emit_byte (mb, CEE_DUP); + mono_mb_emit_icon (mb, j); + mono_mb_emit_ldarg (mb, i + 1); /* 0=this */ + if (sig->params[i]->byref) { + if (pclass->valuetype) + mono_mb_emit_op (mb, CEE_LDOBJ, pclass); + else + mono_mb_emit_byte (mb, CEE_LDIND_REF); + } + if (pclass->valuetype) + mono_mb_emit_op (mb, CEE_BOX, pclass); + mono_mb_emit_byte (mb, CEE_STELEM_REF); + j++; + } + mono_mb_emit_stloc (mb, loc_array); + + /* Serialize parameters */ + + mono_mb_emit_ldloc (mb, loc_array); + mono_mb_emit_managed_call (mb, method_rs_serialize, NULL); + mono_mb_emit_stloc (mb, loc_serialized_data); + } else { + mono_mb_emit_byte (mb, CEE_LDNULL); + mono_mb_emit_managed_call (mb, method_rs_serialize, NULL); + mono_mb_emit_stloc (mb, loc_serialized_data); + } + + /* switch domain */ + + mono_mb_emit_ldloc (mb, loc_domainid); + mono_mb_emit_byte (mb, CEE_LDC_I4_1); + mono_marshal_emit_switch_domain (mb); + mono_mb_emit_stloc (mb, loc_old_domainid); + + /* Load the arguments */ + + mono_mb_emit_ldloc (mb, loc_real_proxy); + mono_mb_emit_ldloc_addr (mb, loc_serialized_data); + mono_mb_emit_ldloc_addr (mb, loc_serialized_exc); + + copy_locals_base = mb->locals; + for (i = 0; i < sig->param_count; i++) { + switch (marshal_types [i]) { + case MONO_MARSHAL_SERIALIZE: + continue; + case MONO_MARSHAL_COPY: { + mono_mb_emit_ldarg (mb, i+1); + if (sig->params [i]->byref) { + /* make a local copy of the byref parameter. The real parameter + * will be updated after the xdomain call + */ + MonoClass *pclass = mono_class_from_mono_type (sig->params [i]); + int copy_local = mono_mb_add_local (mb, &(pclass->byval_arg)); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_stloc (mb, copy_local); + mono_mb_emit_ldloc_addr (mb, copy_local); + } + break; + } + case MONO_MARSHAL_COPY_OUT: + case MONO_MARSHAL_NONE: + mono_mb_emit_ldarg (mb, i+1); + break; + } + } + + /* Make the call to the invoke wrapper in the target domain */ + + xdomain_method = mono_marshal_get_xappdomain_dispatch (method, marshal_types, complex_count, complex_out_count, ret_marshal_type); + mono_marshal_emit_load_domain_method (mb, xdomain_method); + mono_mb_emit_calli (mb, mono_method_signature (xdomain_method)); + + if (copy_return) + mono_mb_emit_stloc (mb, loc_return); + + /* Switch domain */ + + mono_mb_emit_ldloc (mb, loc_old_domainid); + mono_mb_emit_byte (mb, CEE_LDC_I4_0); + mono_marshal_emit_switch_domain (mb); + mono_mb_emit_byte (mb, CEE_POP); + + /* Restore thread domain data */ + + mono_mb_emit_ldloc (mb, loc_context); + mono_mb_emit_icall (mb, mono_context_set_icall); + + /* if (loc_serialized_exc != null) ... */ + + mono_mb_emit_ldloc (mb, loc_serialized_exc); + pos_noex = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S); + + mono_mb_emit_ldloc (mb, loc_serialized_exc); + mono_marshal_emit_xdomain_copy_value (mb, byte_array_class); + mono_mb_emit_managed_call (mb, method_rs_deserialize, NULL); + mono_mb_emit_op (mb, CEE_CASTCLASS, mono_defaults.exception_class); + mono_mb_emit_managed_call (mb, method_exc_fixexc, NULL); + mono_mb_emit_byte (mb, CEE_THROW); + mono_mb_patch_short_branch (mb, pos_noex); + + /* copy back non-serialized output parameters */ + + j = 0; + for (i = 0; i < sig->param_count; i++) { + if (!sig->params [i]->byref || marshal_types [i] != MONO_MARSHAL_COPY) continue; + mono_mb_emit_ldarg (mb, i + 1); + mono_mb_emit_ldloc (mb, copy_locals_base + (j++)); + mono_marshal_emit_xdomain_copy_value (mb, mono_class_from_mono_type (sig->params [i])); + mono_mb_emit_byte (mb, CEE_STIND_REF); + } + + /* Deserialize out parameters */ + + if (complex_out_count > 0) { + mono_mb_emit_ldloc (mb, loc_serialized_data); + mono_marshal_emit_xdomain_copy_value (mb, byte_array_class); + mono_mb_emit_managed_call (mb, method_rs_deserialize, NULL); + mono_mb_emit_stloc (mb, loc_array); + + /* Copy back output parameters and return type */ + + j = 0; + for (i = 0; i < sig->param_count; i++) { + if (marshal_types [i] != MONO_MARSHAL_SERIALIZE) continue; + if (sig->params[i]->byref) { + MonoClass *pclass = mono_class_from_mono_type (sig->params [i]); + mono_mb_emit_ldarg (mb, i + 1); + mono_mb_emit_ldloc (mb, loc_array); + mono_mb_emit_icon (mb, j); + mono_mb_emit_byte (mb, CEE_LDELEM_REF); + if (pclass->valuetype) { + mono_mb_emit_op (mb, CEE_UNBOX, pclass); + mono_mb_emit_op (mb, CEE_LDOBJ, pclass); + mono_mb_emit_op (mb, CEE_STOBJ, pclass); + } else { + if (pclass != mono_defaults.object_class) + mono_mb_emit_op (mb, CEE_CASTCLASS, pclass); + mono_mb_emit_byte (mb, CEE_STIND_REF); + } + } + j++; + } + + if (ret_marshal_type == MONO_MARSHAL_SERIALIZE) { + mono_mb_emit_ldloc (mb, loc_array); + mono_mb_emit_icon (mb, complex_count); + mono_mb_emit_byte (mb, CEE_LDELEM_REF); + if (ret_class->valuetype) { + mono_mb_emit_op (mb, CEE_UNBOX, ret_class); + mono_mb_emit_op (mb, CEE_LDOBJ, ret_class); + } + } + } else if (ret_marshal_type == MONO_MARSHAL_SERIALIZE) { + mono_mb_emit_ldloc (mb, loc_serialized_data); + mono_marshal_emit_xdomain_copy_value (mb, byte_array_class); + mono_mb_emit_managed_call (mb, method_rs_deserialize, NULL); + if (ret_class->valuetype) { + mono_mb_emit_op (mb, CEE_UNBOX, ret_class); + mono_mb_emit_op (mb, CEE_LDOBJ, ret_class); + } else if (ret_class != mono_defaults.object_class) { + mono_mb_emit_op (mb, CEE_CASTCLASS, ret_class); + } + } else { + mono_mb_emit_ldloc (mb, loc_serialized_data); + mono_mb_emit_byte (mb, CEE_DUP); + pos = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S); + mono_marshal_emit_xdomain_copy_value (mb, byte_array_class); + + mono_mb_patch_short_branch (mb, pos); + mono_mb_emit_managed_call (mb, method_rs_deserialize, NULL); + mono_mb_emit_byte (mb, CEE_POP); + } + + if (copy_return) { + mono_mb_emit_ldloc (mb, loc_return); + if (ret_marshal_type == MONO_MARSHAL_COPY) + mono_marshal_emit_xdomain_copy_value (mb, ret_class); + } + + mono_mb_emit_byte (mb, CEE_RET); +#endif /* DISABLE_JIT */ + + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); + info->d.remoting.method = method; + res = mono_remoting_mb_create_and_cache (method, mb, sig, sig->param_count + 16, info); + mono_mb_free (mb); + + return res; +} + +/** + * mono_marshal_get_remoting_invoke_for_target: + */ +MonoMethod * +mono_marshal_get_remoting_invoke_for_target (MonoMethod *method, MonoRemotingTarget target_type) +{ + if (target_type == MONO_REMOTING_TARGET_APPDOMAIN) { + return mono_marshal_get_xappdomain_invoke (method); + } else if (target_type == MONO_REMOTING_TARGET_COMINTEROP) { +#ifndef DISABLE_COM + return mono_cominterop_get_invoke (method); +#else + g_assert_not_reached (); +#endif + } else { + return mono_marshal_get_remoting_invoke (method); + } + /* Not erached */ + return NULL; +} + +G_GNUC_UNUSED static gpointer +mono_marshal_load_remoting_wrapper (MonoRealProxy *rp, MonoMethod *method) +{ + MonoError error; + MonoMethod *marshal_method = NULL; + if (rp->target_domain_id != -1) + marshal_method = mono_marshal_get_xappdomain_invoke (method); + else + marshal_method = mono_marshal_get_remoting_invoke (method); + gpointer compiled_ptr = mono_compile_method_checked (marshal_method, &error); + mono_error_assert_ok (&error); + return compiled_ptr; +} + +/** + * mono_marshal_get_remoting_invoke_with_check: + */ +MonoMethod * +mono_marshal_get_remoting_invoke_with_check (MonoMethod *method) +{ + MonoMethodSignature *sig; + MonoMethodBuilder *mb; + MonoMethod *res, *native; + WrapperInfo *info; + int i, pos, pos_rem; + + g_assert (method); + + if (method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK) + return method; + + /* we cant remote methods without this pointer */ + g_assert (mono_method_signature (method)->hasthis); + + if ((res = mono_marshal_remoting_find_in_cache (method, MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK))) + return res; + + sig = mono_signature_no_pinvoke (method); + + mb = mono_mb_new (method->klass, method->name, MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK); + +#ifndef DISABLE_JIT + for (i = 0; i <= sig->param_count; i++) + mono_mb_emit_ldarg (mb, i); + + mono_mb_emit_ldarg (mb, 0); + pos = mono_mb_emit_proxy_check (mb, CEE_BNE_UN); + + if (mono_marshal_supports_fast_xdomain (method)) { + mono_mb_emit_ldarg (mb, 0); + pos_rem = mono_mb_emit_xdomain_check (mb, CEE_BEQ); + + /* wrapper for cross app domain calls */ + native = mono_marshal_get_xappdomain_invoke (method); + mono_mb_emit_managed_call (mb, native, mono_method_signature (native)); + mono_mb_emit_byte (mb, CEE_RET); + + mono_mb_patch_branch (mb, pos_rem); + } + /* wrapper for normal remote calls */ + native = mono_marshal_get_remoting_invoke (method); + mono_mb_emit_managed_call (mb, native, mono_method_signature (native)); + mono_mb_emit_byte (mb, CEE_RET); + + /* not a proxy */ + mono_mb_patch_branch (mb, pos); + mono_mb_emit_managed_call (mb, method, mono_method_signature (method)); + mono_mb_emit_byte (mb, CEE_RET); +#endif + + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); + info->d.remoting.method = method; + res = mono_remoting_mb_create_and_cache (method, mb, sig, sig->param_count + 16, info); + mono_mb_free (mb); + + return res; +} + +/** + * mono_marshal_get_ldfld_wrapper: + * \param type the type of the field + * + * This method generates a function which can be use to load a field with type + * \p type from an object. The generated function has the following signature: + * + * type ldfld_wrapper (MonoObject *this_obj, MonoClass *klass, MonoClassField *field, int offset) + */ +MonoMethod * +mono_marshal_get_ldfld_wrapper (MonoType *type) +{ + MonoMethodSignature *sig; + MonoMethodBuilder *mb; + MonoMethod *res; + MonoClass *klass; + GHashTable *cache; + WrapperInfo *info; + char *name; + int t, pos0, pos1 = 0; + static MonoMethod* tp_load = NULL; + + type = mono_type_get_underlying_type (type); + + t = type->type; + + if (!type->byref) { + if (type->type == MONO_TYPE_SZARRAY) { + klass = mono_defaults.array_class; + } else if (type->type == MONO_TYPE_VALUETYPE) { + klass = type->data.klass; + } else if (t == MONO_TYPE_OBJECT || t == MONO_TYPE_CLASS || t == MONO_TYPE_STRING) { + klass = mono_defaults.object_class; + } else if (t == MONO_TYPE_PTR || t == MONO_TYPE_FNPTR) { + klass = mono_defaults.int_class; + } else if (t == MONO_TYPE_GENERICINST) { + if (mono_type_generic_inst_is_valuetype (type)) + klass = mono_class_from_mono_type (type); + else + klass = mono_defaults.object_class; + } else { + klass = mono_class_from_mono_type (type); + } + } else { + klass = mono_defaults.int_class; + } + + cache = get_cache (&klass->image->ldfld_wrapper_cache, mono_aligned_addr_hash, NULL); + if ((res = mono_marshal_find_in_cache (cache, klass))) + return res; + +#ifndef DISABLE_REMOTING + if (!tp_load) { + tp_load = mono_class_get_method_from_name (mono_defaults.transparent_proxy_class, "LoadRemoteFieldNew", -1); + g_assert (tp_load != NULL); + } +#endif + + /* we add the %p pointer value of klass because class names are not unique */ + name = g_strdup_printf ("__ldfld_wrapper_%p_%s.%s", klass, klass->name_space, klass->name); + mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_LDFLD); + g_free (name); + + sig = mono_metadata_signature_alloc (mono_defaults.corlib, 4); + sig->params [0] = &mono_defaults.object_class->byval_arg; + sig->params [1] = &mono_defaults.int_class->byval_arg; + sig->params [2] = &mono_defaults.int_class->byval_arg; + sig->params [3] = &mono_defaults.int_class->byval_arg; + sig->ret = &klass->byval_arg; + +#ifndef DISABLE_JIT + mono_mb_emit_ldarg (mb, 0); + pos0 = mono_mb_emit_proxy_check (mb, CEE_BNE_UN); + +#ifndef DISABLE_REMOTING + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_ldarg (mb, 2); + + mono_mb_emit_managed_call (mb, tp_load, NULL); + + /* + csig = mono_metadata_signature_alloc (mono_defaults.corlib, 3); + csig->params [0] = &mono_defaults.object_class->byval_arg; + csig->params [1] = &mono_defaults.int_class->byval_arg; + csig->params [2] = &mono_defaults.int_class->byval_arg; + csig->ret = &klass->this_arg; + csig->pinvoke = 1; + + mono_mb_emit_native_call (mb, csig, mono_load_remote_field_new); + mono_marshal_emit_thread_interrupt_checkpoint (mb); + */ + + if (klass->valuetype) { + mono_mb_emit_op (mb, CEE_UNBOX, klass); + pos1 = mono_mb_emit_branch (mb, CEE_BR); + } else { + mono_mb_emit_byte (mb, CEE_RET); + } +#endif + + mono_mb_patch_branch (mb, pos0); + + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_OBJADDR); + mono_mb_emit_ldarg (mb, 3); + mono_mb_emit_byte (mb, CEE_ADD); + + if (klass->valuetype) + mono_mb_patch_branch (mb, pos1); + + switch (t) { + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_CHAR: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_OBJECT: + case MONO_TYPE_CLASS: + case MONO_TYPE_STRING: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_PTR: + case MONO_TYPE_FNPTR: + mono_mb_emit_byte (mb, mono_type_to_ldind (type)); + break; + case MONO_TYPE_VALUETYPE: + g_assert (!klass->enumtype); + mono_mb_emit_op (mb, CEE_LDOBJ, klass); + break; + case MONO_TYPE_GENERICINST: + if (mono_type_generic_inst_is_valuetype (type)) { + mono_mb_emit_op (mb, CEE_LDOBJ, klass); + } else { + mono_mb_emit_byte (mb, CEE_LDIND_REF); + } + break; + case MONO_TYPE_VAR: + case MONO_TYPE_MVAR: + mono_mb_emit_op (mb, CEE_LDOBJ, klass); + break; + default: + g_warning ("type %x not implemented", type->type); + g_assert_not_reached (); + } + + mono_mb_emit_byte (mb, CEE_RET); +#endif /* DISABLE_JIT */ + + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); + info->d.proxy.klass = klass; + res = mono_mb_create_and_cache_full (cache, klass, + mb, sig, sig->param_count + 16, info, NULL); + mono_mb_free (mb); + + return res; +} + +/* + * mono_marshal_get_ldflda_wrapper: + * @type: the type of the field + * + * This method generates a function which can be used to load a field address + * from an object. The generated function has the following signature: + * gpointer ldflda_wrapper (MonoObject *this_obj, MonoClass *klass, MonoClassField *field, int offset); + */ +MonoMethod * +mono_marshal_get_ldflda_wrapper (MonoType *type) +{ + MonoMethodSignature *sig; + MonoMethodBuilder *mb; + MonoMethod *res; + MonoClass *klass; + GHashTable *cache; + WrapperInfo *info; + char *name; + int t, pos0, pos1, pos2, pos3; + + type = mono_type_get_underlying_type (type); + t = type->type; + + if (!type->byref) { + if (type->type == MONO_TYPE_SZARRAY) { + klass = mono_defaults.array_class; + } else if (type->type == MONO_TYPE_VALUETYPE) { + klass = type->data.klass; + } else if (t == MONO_TYPE_OBJECT || t == MONO_TYPE_CLASS || t == MONO_TYPE_STRING) { + klass = mono_defaults.object_class; + } else if (t == MONO_TYPE_PTR || t == MONO_TYPE_FNPTR) { + klass = mono_defaults.int_class; + } else if (t == MONO_TYPE_GENERICINST) { + if (mono_type_generic_inst_is_valuetype (type)) + klass = mono_class_from_mono_type (type); + else + klass = mono_defaults.object_class; + } else { + klass = mono_class_from_mono_type (type); + } + } else { + klass = mono_defaults.int_class; + } + + cache = get_cache (&klass->image->ldflda_wrapper_cache, mono_aligned_addr_hash, NULL); + if ((res = mono_marshal_find_in_cache (cache, klass))) + return res; + + /* we add the %p pointer value of klass because class names are not unique */ + name = g_strdup_printf ("__ldflda_wrapper_%p_%s.%s", klass, klass->name_space, klass->name); + mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_LDFLDA); + g_free (name); + + sig = mono_metadata_signature_alloc (mono_defaults.corlib, 4); + sig->params [0] = &mono_defaults.object_class->byval_arg; + sig->params [1] = &mono_defaults.int_class->byval_arg; + sig->params [2] = &mono_defaults.int_class->byval_arg; + sig->params [3] = &mono_defaults.int_class->byval_arg; + sig->ret = &mono_defaults.int_class->byval_arg; + +#ifndef DISABLE_JIT + /* if typeof (this) != transparent_proxy goto pos0 */ + mono_mb_emit_ldarg (mb, 0); + pos0 = mono_mb_emit_proxy_check (mb, CEE_BNE_UN); + + /* if same_appdomain goto pos1 */ + mono_mb_emit_ldarg (mb, 0); + pos1 = mono_mb_emit_xdomain_check (mb, CEE_BEQ); + + mono_mb_emit_exception_full (mb, "System", "InvalidOperationException", "Attempt to load field address from object in another appdomain."); + + /* same app domain */ + mono_mb_patch_branch (mb, pos1); + + /* if typeof (this) != contextbound goto pos2 */ + mono_mb_emit_ldarg (mb, 0); + pos2 = mono_mb_emit_contextbound_check (mb, CEE_BEQ); + + /* if this->rp->context == mono_context_get goto pos3 */ + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoTransparentProxy, rp)); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoRealProxy, context)); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_icall (mb, mono_context_get_icall); + pos3 = mono_mb_emit_branch (mb, CEE_BEQ); + + mono_mb_emit_exception_full (mb, "System", "InvalidOperationException", "Attempt to load field address from object in another context."); + + mono_mb_patch_branch (mb, pos2); + mono_mb_patch_branch (mb, pos3); + + /* return the address of the field from this->rp->unwrapped_server */ + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoTransparentProxy, rp)); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoRealProxy, unwrapped_server)); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_OBJADDR); + mono_mb_emit_ldarg (mb, 3); + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_byte (mb, CEE_RET); + + /* not a proxy: return the address of the field directly */ + mono_mb_patch_branch (mb, pos0); + + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_OBJADDR); + mono_mb_emit_ldarg (mb, 3); + mono_mb_emit_byte (mb, CEE_ADD); + + mono_mb_emit_byte (mb, CEE_RET); +#endif + + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); + info->d.proxy.klass = klass; + res = mono_mb_create_and_cache_full (cache, klass, + mb, sig, sig->param_count + 16, + info, NULL); + mono_mb_free (mb); + + return res; +} + + +/** + * mono_marshal_get_stfld_wrapper: + * \param type the type of the field + * + * This method generates a function which can be use to store a field with type + * \p type. The generated function has the following signature: + * + * void stfld_wrapper (MonoObject *this_obj, MonoClass *klass, MonoClassField *field, int offset, type val) + */ +MonoMethod * +mono_marshal_get_stfld_wrapper (MonoType *type) +{ + MonoMethodSignature *sig; + MonoMethodBuilder *mb; + MonoMethod *res; + MonoClass *klass; + GHashTable *cache; + WrapperInfo *info; + char *name; + int t, pos; + static MonoMethod *tp_store = NULL; + + type = mono_type_get_underlying_type (type); + t = type->type; + + if (!type->byref) { + if (type->type == MONO_TYPE_SZARRAY) { + klass = mono_defaults.array_class; + } else if (type->type == MONO_TYPE_VALUETYPE) { + klass = type->data.klass; + } else if (t == MONO_TYPE_OBJECT || t == MONO_TYPE_CLASS || t == MONO_TYPE_STRING) { + klass = mono_defaults.object_class; + } else if (t == MONO_TYPE_PTR || t == MONO_TYPE_FNPTR) { + klass = mono_defaults.int_class; + } else if (t == MONO_TYPE_GENERICINST) { + if (mono_type_generic_inst_is_valuetype (type)) + klass = mono_class_from_mono_type (type); + else + klass = mono_defaults.object_class; + } else { + klass = mono_class_from_mono_type (type); + } + } else { + klass = mono_defaults.int_class; + } + + cache = get_cache (&klass->image->stfld_wrapper_cache, mono_aligned_addr_hash, NULL); + if ((res = mono_marshal_find_in_cache (cache, klass))) + return res; + +#ifndef DISABLE_REMOTING + if (!tp_store) { + tp_store = mono_class_get_method_from_name (mono_defaults.transparent_proxy_class, "StoreRemoteField", -1); + g_assert (tp_store != NULL); + } +#endif + + /* we add the %p pointer value of klass because class names are not unique */ + name = g_strdup_printf ("__stfld_wrapper_%p_%s.%s", klass, klass->name_space, klass->name); + mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_STFLD); + g_free (name); + + sig = mono_metadata_signature_alloc (mono_defaults.corlib, 5); + sig->params [0] = &mono_defaults.object_class->byval_arg; + sig->params [1] = &mono_defaults.int_class->byval_arg; + sig->params [2] = &mono_defaults.int_class->byval_arg; + sig->params [3] = &mono_defaults.int_class->byval_arg; + sig->params [4] = &klass->byval_arg; + sig->ret = &mono_defaults.void_class->byval_arg; + +#ifndef DISABLE_JIT + mono_mb_emit_ldarg (mb, 0); + pos = mono_mb_emit_proxy_check (mb, CEE_BNE_UN); + +#ifndef DISABLE_REMOTING + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_ldarg (mb, 2); + mono_mb_emit_ldarg (mb, 4); + if (klass->valuetype) + mono_mb_emit_op (mb, CEE_BOX, klass); + + mono_mb_emit_managed_call (mb, tp_store, NULL); + + mono_mb_emit_byte (mb, CEE_RET); +#endif + + mono_mb_patch_branch (mb, pos); + + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_OBJADDR); + mono_mb_emit_ldarg (mb, 3); + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_ldarg (mb, 4); + + switch (t) { + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_CHAR: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_OBJECT: + case MONO_TYPE_CLASS: + case MONO_TYPE_STRING: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_PTR: + case MONO_TYPE_FNPTR: + mono_mb_emit_byte (mb, mono_type_to_stind (type)); + break; + case MONO_TYPE_VALUETYPE: + g_assert (!klass->enumtype); + mono_mb_emit_op (mb, CEE_STOBJ, klass); + break; + case MONO_TYPE_GENERICINST: + case MONO_TYPE_VAR: + case MONO_TYPE_MVAR: + mono_mb_emit_op (mb, CEE_STOBJ, klass); + break; + default: + g_warning ("type %x not implemented", type->type); + g_assert_not_reached (); + } + + mono_mb_emit_byte (mb, CEE_RET); +#endif + + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); + info->d.proxy.klass = klass; + res = mono_mb_create_and_cache_full (cache, klass, + mb, sig, sig->param_count + 16, + info, NULL); + mono_mb_free (mb); + + return res; +} + +/** + * mono_marshal_get_proxy_cancast: + */ +MonoMethod * +mono_marshal_get_proxy_cancast (MonoClass *klass) +{ + static MonoMethodSignature *isint_sig = NULL; + GHashTable *cache; + MonoMethod *res; + WrapperInfo *info; + int pos_failed, pos_end; + char *name, *klass_name; + MonoMethod *can_cast_to; + MonoMethodDesc *desc; + MonoMethodBuilder *mb; + + cache = get_cache (&klass->image->proxy_isinst_cache, mono_aligned_addr_hash, NULL); + if ((res = mono_marshal_find_in_cache (cache, klass))) + return res; + + if (!isint_sig) { + isint_sig = mono_metadata_signature_alloc (mono_defaults.corlib, 1); + isint_sig->params [0] = &mono_defaults.object_class->byval_arg; + isint_sig->ret = &mono_defaults.object_class->byval_arg; + isint_sig->pinvoke = 0; + } + + klass_name = mono_type_full_name (&klass->byval_arg); + name = g_strdup_printf ("__proxy_isinst_wrapper_%s", klass_name); + mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_PROXY_ISINST); + g_free (klass_name); + g_free (name); + + mb->method->save_lmf = 1; + +#ifndef DISABLE_JIT + /* get the real proxy from the transparent proxy*/ + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoTransparentProxy, rp)); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + + /* get the reflection type from the type handle */ + mono_mb_emit_ptr (mb, &klass->byval_arg); + mono_mb_emit_icall (mb, type_from_handle); + + mono_mb_emit_ldarg (mb, 0); + + /* make the call to CanCastTo (type, ob) */ + desc = mono_method_desc_new ("IRemotingTypeInfo:CanCastTo", FALSE); + can_cast_to = mono_method_desc_search_in_class (desc, mono_defaults.iremotingtypeinfo_class); + g_assert (can_cast_to); + mono_method_desc_free (desc); + mono_mb_emit_op (mb, CEE_CALLVIRT, can_cast_to); + + pos_failed = mono_mb_emit_branch (mb, CEE_BRFALSE); + + /* Upgrade the proxy vtable by calling: mono_upgrade_remote_class_wrapper (type, ob)*/ + mono_mb_emit_ptr (mb, &klass->byval_arg); + mono_mb_emit_icall (mb, type_from_handle); + mono_mb_emit_ldarg (mb, 0); + + mono_mb_emit_icall (mb, mono_upgrade_remote_class_wrapper); + mono_marshal_emit_thread_interrupt_checkpoint (mb); + + mono_mb_emit_ldarg (mb, 0); + pos_end = mono_mb_emit_branch (mb, CEE_BR); + + /* fail */ + + mono_mb_patch_branch (mb, pos_failed); + mono_mb_emit_byte (mb, CEE_LDNULL); + + /* the end */ + + mono_mb_patch_branch (mb, pos_end); + mono_mb_emit_byte (mb, CEE_RET); +#endif + + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); + info->d.proxy.klass = klass; + res = mono_mb_create_and_cache_full (cache, klass, mb, isint_sig, isint_sig->param_count + 16, info, NULL); + mono_mb_free (mb); + + return res; +} + +void +mono_upgrade_remote_class_wrapper (MonoReflectionType *rtype_raw, MonoTransparentProxy *tproxy_raw) +{ + ICALL_ENTRY (); + MONO_HANDLE_DCL (MonoReflectionType, rtype); + MONO_HANDLE_DCL (MonoTransparentProxy, tproxy); + MonoDomain *domain = MONO_HANDLE_DOMAIN (tproxy); + MonoClass *klass = mono_class_from_mono_type (MONO_HANDLE_GETVAL (rtype, type)); + mono_upgrade_remote_class (domain, MONO_HANDLE_CAST (MonoObject, tproxy), klass, &error); + ICALL_RETURN (); +} + +#else /* DISABLE_REMOTING */ + +void +mono_remoting_init (void) +{ +} + +#endif /* DISABLE_REMOTING */ + +/* mono_get_xdomain_marshal_type() + * Returns the kind of marshalling that a type needs for cross domain calls. + */ +static MonoXDomainMarshalType +mono_get_xdomain_marshal_type (MonoType *t) +{ + switch (t->type) { + case MONO_TYPE_VOID: + g_assert_not_reached (); + break; + case MONO_TYPE_U1: + case MONO_TYPE_I1: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_U2: + case MONO_TYPE_I2: + case MONO_TYPE_CHAR: + case MONO_TYPE_U4: + case MONO_TYPE_I4: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + return MONO_MARSHAL_NONE; + case MONO_TYPE_STRING: + return MONO_MARSHAL_COPY; + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: { + MonoClass *elem_class = mono_class_from_mono_type (t)->element_class; + if (mono_get_xdomain_marshal_type (&(elem_class->byval_arg)) != MONO_MARSHAL_SERIALIZE) + return MONO_MARSHAL_COPY; + break; + } + default: + break; + } + return MONO_MARSHAL_SERIALIZE; +} + +/* Replace the given array element by a copy in the current domain */ +static gboolean +xdomain_copy_array_element_inplace (MonoArrayHandle arr, int i, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + error_init (error); + MonoObjectHandle item = MONO_HANDLE_NEW (MonoObject, NULL); + MONO_HANDLE_ARRAY_GETREF (item, arr, i); + + MonoObjectHandle item_copy = mono_marshal_xdomain_copy_value_handle (item, error); + goto_if_nok (error, leave); + MONO_HANDLE_ARRAY_SETREF (arr, i, item_copy); +leave: + HANDLE_FUNCTION_RETURN_VAL (is_ok (error)); +} + +/** + * mono_marshal_xdomain_copy_value_handle: + * \param val The value to copy. + * \param error set on failure. + * Makes a copy of \p val suitable for the current domain. + * On failure returns NULL and sets \p error. + */ +MonoObjectHandle +mono_marshal_xdomain_copy_value_handle (MonoObjectHandle val, MonoError *error) +{ + error_init (error); + MonoObjectHandle result = MONO_HANDLE_NEW (MonoObject, NULL); + if (MONO_HANDLE_IS_NULL (val)) + goto leave; + + MonoDomain *domain = mono_domain_get (); + + MonoClass *klass = mono_handle_class (val); + + switch (klass->byval_arg.type) { + case MONO_TYPE_VOID: + g_assert_not_reached (); + break; + case MONO_TYPE_U1: + case MONO_TYPE_I1: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_U2: + case MONO_TYPE_I2: + case MONO_TYPE_CHAR: + case MONO_TYPE_U4: + case MONO_TYPE_I4: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_R4: + case MONO_TYPE_R8: { + uint32_t gchandle = mono_gchandle_from_handle (val, TRUE); + MonoObjectHandle res = MONO_HANDLE_NEW (MonoObject, mono_value_box_checked (domain, klass, ((char*)val) + sizeof(MonoObject), error)); /* FIXME use handles in mono_value_box_checked */ + mono_gchandle_free (gchandle); + goto_if_nok (error, leave); + MONO_HANDLE_ASSIGN (result, res); + break; + } + case MONO_TYPE_STRING: { + MonoStringHandle str = MONO_HANDLE_CAST (MonoString, val); + uint32_t gchandle = mono_gchandle_from_handle (val, TRUE); + MonoStringHandle res = mono_string_new_utf16_handle (domain, mono_string_chars (MONO_HANDLE_RAW (str)), mono_string_handle_length (str), error); + mono_gchandle_free (gchandle); + goto_if_nok (error, leave); + MONO_HANDLE_ASSIGN (result, res); + break; + } + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: { + MonoArrayHandle arr = MONO_HANDLE_CAST (MonoArray, val); + MonoXDomainMarshalType mt = mono_get_xdomain_marshal_type (&klass->element_class->byval_arg); + if (mt == MONO_MARSHAL_SERIALIZE) + goto leave; + MonoArrayHandle acopy = mono_array_clone_in_domain (domain, arr, error); + goto_if_nok (error, leave); + + if (mt == MONO_MARSHAL_COPY) { + int i, len = mono_array_handle_length (acopy); + for (i = 0; i < len; i++) { + if (!xdomain_copy_array_element_inplace (acopy, i, error)) + goto leave; + } + } + MONO_HANDLE_ASSIGN (result, acopy); + break; + } + default: + break; + } + +leave: + return result; +} + +/* mono_marshal_xdomain_copy_value + * Makes a copy of "val" suitable for the current domain. + */ +MonoObject* +mono_marshal_xdomain_copy_value (MonoObject* val_raw, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + /* FIXME callers of mono_marshal_xdomain_copy_value should use handles */ + MONO_HANDLE_DCL (MonoObject, val); + MonoObjectHandle result = mono_marshal_xdomain_copy_value_handle (val, error); + HANDLE_FUNCTION_RETURN_OBJ (result); +} + +/* mono_marshal_xdomain_copy_value + * Makes a copy of "val" suitable for the current domain. + */ +MonoObject * +ves_icall_mono_marshal_xdomain_copy_value (MonoObject *val) +{ + MonoError error; + MonoObject *result = mono_marshal_xdomain_copy_value (val, &error); + mono_error_set_pending_exception (&error); + return result; +} + +void +mono_context_set_icall (MonoAppContext *new_context_raw) +{ + HANDLE_FUNCTION_ENTER (); + MONO_HANDLE_DCL (MonoAppContext, new_context); + mono_context_set_handle (new_context); + HANDLE_FUNCTION_RETURN (); +} + +static MonoAppContext* +mono_context_get_icall (void) +{ + HANDLE_FUNCTION_ENTER (); + MonoAppContextHandle context = mono_context_get_handle (); + HANDLE_FUNCTION_RETURN_OBJ (context); +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/remoting.h b/unity-2019.4.24f1-mbe/mono/metadata/remoting.h new file mode 100644 index 000000000..aeeb4bb8a --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/remoting.h @@ -0,0 +1,47 @@ +/** + * \file + * Remoting support + * + * (C) 2014 Xamarin, Inc. http://www.xamarin.com + * + */ + +#ifndef __MONO_REMOTING_H__ +#define __MONO_REMOTING_H__ + +#include "config.h" +#include +#include +#include + +void mono_remoting_init (void); + +#ifndef DISABLE_REMOTING + +MonoMethod * +mono_marshal_get_remoting_invoke (MonoMethod *method); + +MonoMethod * +mono_marshal_get_xappdomain_invoke (MonoMethod *method); + +MonoMethod * +mono_marshal_get_remoting_invoke_for_target (MonoMethod *method, MonoRemotingTarget target_type); + +MonoMethod * +mono_marshal_get_remoting_invoke_with_check (MonoMethod *method); + +MonoMethod * +mono_marshal_get_stfld_wrapper (MonoType *type); + +MonoMethod * +mono_marshal_get_ldfld_wrapper (MonoType *type); + +MonoMethod * +mono_marshal_get_ldflda_wrapper (MonoType *type); + +MonoMethod * +mono_marshal_get_proxy_cancast (MonoClass *klass); + +#endif + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/row-indexes.h b/unity-2019.4.24f1-mbe/mono/metadata/row-indexes.h new file mode 100644 index 000000000..992dbdeea --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/row-indexes.h @@ -0,0 +1,491 @@ +/** + * \file + */ + +#ifndef __MONO_METADATA_ROW_INDEXES_H__ +#define __MONO_METADATA_ROW_INDEXES_H__ + +/* + * The last entry in the enum is used to give the number + * of columns in the row. + */ + +enum { + MONO_ASSEMBLY_HASH_ALG, + MONO_ASSEMBLY_MAJOR_VERSION, + MONO_ASSEMBLY_MINOR_VERSION, + MONO_ASSEMBLY_BUILD_NUMBER, + MONO_ASSEMBLY_REV_NUMBER, + MONO_ASSEMBLY_FLAGS, + MONO_ASSEMBLY_PUBLIC_KEY, + MONO_ASSEMBLY_NAME, + MONO_ASSEMBLY_CULTURE, + MONO_ASSEMBLY_SIZE +}; + +enum { + MONO_ASSEMBLYOS_PLATFORM, + MONO_ASSEMBLYOS_MAJOR_VERSION, + MONO_ASSEMBLYOS_MINOR_VERSION, + MONO_ASSEMBLYOS_SIZE +}; + +enum { + MONO_ASSEMBLY_PROCESSOR, + MONO_ASSEMBLY_PROCESSOR_SIZE +}; + +enum { + MONO_ASSEMBLYREF_MAJOR_VERSION, + MONO_ASSEMBLYREF_MINOR_VERSION, + MONO_ASSEMBLYREF_BUILD_NUMBER, + MONO_ASSEMBLYREF_REV_NUMBER, + MONO_ASSEMBLYREF_FLAGS, + MONO_ASSEMBLYREF_PUBLIC_KEY, + MONO_ASSEMBLYREF_NAME, + MONO_ASSEMBLYREF_CULTURE, + MONO_ASSEMBLYREF_HASH_VALUE, + MONO_ASSEMBLYREF_SIZE +}; + +enum { + MONO_ASSEMBLYREFOS_PLATFORM, + MONO_ASSEMBLYREFOS_MAJOR_VERSION, + MONO_ASSEMBLYREFOS_MINOR_VERSION, + MONO_ASSEMBLYREFOS_ASSEMBLYREF, + MONO_ASSEMBLYREFOS_SIZE +}; + +enum { + MONO_ASSEMBLYREFPROC_PROCESSOR, + MONO_ASSEMBLYREFPROC_ASSEMBLYREF, + MONO_ASSEMBLYREFPROC_SIZE +}; + +enum { + MONO_CLASS_LAYOUT_PACKING_SIZE, + MONO_CLASS_LAYOUT_CLASS_SIZE, + MONO_CLASS_LAYOUT_PARENT, + MONO_CLASS_LAYOUT_SIZE +}; + +enum { + MONO_CONSTANT_TYPE, + MONO_CONSTANT_PADDING, + MONO_CONSTANT_PARENT, + MONO_CONSTANT_VALUE, + MONO_CONSTANT_SIZE +}; + +enum { + MONO_CUSTOM_ATTR_PARENT, + MONO_CUSTOM_ATTR_TYPE, + MONO_CUSTOM_ATTR_VALUE, + MONO_CUSTOM_ATTR_SIZE +}; + +enum { + MONO_DECL_SECURITY_ACTION, + MONO_DECL_SECURITY_PARENT, + MONO_DECL_SECURITY_PERMISSIONSET, + MONO_DECL_SECURITY_SIZE +}; + +enum { + MONO_EVENT_MAP_PARENT, + MONO_EVENT_MAP_EVENTLIST, + MONO_EVENT_MAP_SIZE +}; + +enum { + MONO_EVENT_FLAGS, + MONO_EVENT_NAME, + MONO_EVENT_TYPE, + MONO_EVENT_SIZE +}; + +enum { + MONO_EVENT_POINTER_EVENT, + MONO_EVENT_POINTER_SIZE +}; + +enum { + MONO_EXP_TYPE_FLAGS, + MONO_EXP_TYPE_TYPEDEF, + MONO_EXP_TYPE_NAME, + MONO_EXP_TYPE_NAMESPACE, + MONO_EXP_TYPE_IMPLEMENTATION, + MONO_EXP_TYPE_SIZE +}; + +enum { + MONO_FIELD_FLAGS, + MONO_FIELD_NAME, + MONO_FIELD_SIGNATURE, + MONO_FIELD_SIZE +}; + +enum { + MONO_FIELD_LAYOUT_OFFSET, + MONO_FIELD_LAYOUT_FIELD, + MONO_FIELD_LAYOUT_SIZE +}; + +enum { + MONO_FIELD_MARSHAL_PARENT, + MONO_FIELD_MARSHAL_NATIVE_TYPE, + MONO_FIELD_MARSHAL_SIZE +}; + +enum { + MONO_FIELD_POINTER_FIELD, + MONO_FIELD_POINTER_SIZE +}; + +enum { + MONO_FIELD_RVA_RVA, + MONO_FIELD_RVA_FIELD, + MONO_FIELD_RVA_SIZE +}; + +enum { + MONO_FILE_FLAGS, + MONO_FILE_NAME, + MONO_FILE_HASH_VALUE, + MONO_FILE_SIZE +}; + +enum { + MONO_IMPLMAP_FLAGS, + MONO_IMPLMAP_MEMBER, + MONO_IMPLMAP_NAME, + MONO_IMPLMAP_SCOPE, + MONO_IMPLMAP_SIZE +}; + +enum { + MONO_INTERFACEIMPL_CLASS, + MONO_INTERFACEIMPL_INTERFACE, + MONO_INTERFACEIMPL_SIZE +}; + +enum { + MONO_MANIFEST_OFFSET, + MONO_MANIFEST_FLAGS, + MONO_MANIFEST_NAME, + MONO_MANIFEST_IMPLEMENTATION, + MONO_MANIFEST_SIZE +}; + +enum { + MONO_MEMBERREF_CLASS, + MONO_MEMBERREF_NAME, + MONO_MEMBERREF_SIGNATURE, + MONO_MEMBERREF_SIZE +}; + +enum { + MONO_METHOD_RVA, + MONO_METHOD_IMPLFLAGS, + MONO_METHOD_FLAGS, + MONO_METHOD_NAME, + MONO_METHOD_SIGNATURE, + MONO_METHOD_PARAMLIST, + MONO_METHOD_SIZE +}; + +enum { + MONO_METHODIMPL_CLASS, + MONO_METHODIMPL_BODY, + MONO_METHODIMPL_DECLARATION, + MONO_METHODIMPL_SIZE +}; + +enum { + MONO_METHOD_POINTER_METHOD, + MONO_METHOD_POINTER_SIZE +}; + +enum { + MONO_METHOD_SEMA_SEMANTICS, + MONO_METHOD_SEMA_METHOD, + MONO_METHOD_SEMA_ASSOCIATION, + MONO_METHOD_SEMA_SIZE +}; + +enum { + MONO_MODULE_GENERATION, + MONO_MODULE_NAME, + MONO_MODULE_MVID, + MONO_MODULE_ENC, + MONO_MODULE_ENCBASE, + MONO_MODULE_SIZE +}; + +enum { + MONO_MODULEREF_NAME, + MONO_MODULEREF_SIZE +}; + +enum { + MONO_NESTED_CLASS_NESTED, + MONO_NESTED_CLASS_ENCLOSING, + MONO_NESTED_CLASS_SIZE +}; + +enum { + MONO_PARAM_FLAGS, + MONO_PARAM_SEQUENCE, + MONO_PARAM_NAME, + MONO_PARAM_SIZE +}; + +enum { + MONO_PARAM_POINTER_PARAM, + MONO_PARAM_POINTER_SIZE +}; + +enum { + MONO_PROPERTY_FLAGS, + MONO_PROPERTY_NAME, + MONO_PROPERTY_TYPE, + MONO_PROPERTY_SIZE +}; + +enum { + MONO_PROPERTY_POINTER_PROPERTY, + MONO_PROPERTY_POINTER_SIZE +}; + +enum { + MONO_PROPERTY_MAP_PARENT, + MONO_PROPERTY_MAP_PROPERTY_LIST, + MONO_PROPERTY_MAP_SIZE +}; + +enum { + MONO_STAND_ALONE_SIGNATURE, + MONO_STAND_ALONE_SIGNATURE_SIZE +}; + +enum { + MONO_TYPEDEF_FLAGS, + MONO_TYPEDEF_NAME, + MONO_TYPEDEF_NAMESPACE, + MONO_TYPEDEF_EXTENDS, + MONO_TYPEDEF_FIELD_LIST, + MONO_TYPEDEF_METHOD_LIST, + MONO_TYPEDEF_SIZE +}; + +enum { + MONO_TYPEREF_SCOPE, + MONO_TYPEREF_NAME, + MONO_TYPEREF_NAMESPACE, + MONO_TYPEREF_SIZE +}; + +enum { + MONO_TYPESPEC_SIGNATURE, + MONO_TYPESPEC_SIZE +}; + +enum { + MONO_GENERICPARAM_NUMBER, + MONO_GENERICPARAM_FLAGS, + MONO_GENERICPARAM_OWNER, + MONO_GENERICPARAM_NAME, + + MONO_GENERICPARAM_SIZE +}; + +enum { + MONO_METHODSPEC_METHOD, + MONO_METHODSPEC_SIGNATURE, + MONO_METHODSPEC_SIZE +}; + +enum { + MONO_GENPARCONSTRAINT_GENERICPAR, + MONO_GENPARCONSTRAINT_CONSTRAINT, + MONO_GENPARCONSTRAINT_SIZE +}; + +enum { + MONO_DOCUMENT_NAME, + MONO_DOCUMENT_HASHALG, + MONO_DOCUMENT_HASH, + MONO_DOCUMENT_LANGUAGE, + MONO_DOCUMENT_SIZE +}; + +enum { + MONO_METHODBODY_DOCUMENT, + MONO_METHODBODY_SEQ_POINTS, + MONO_METHODBODY_SIZE +}; + +enum { + MONO_LOCALSCOPE_METHOD, + MONO_LOCALSCOPE_IMPORTSCOPE, + MONO_LOCALSCOPE_VARIABLELIST, + MONO_LOCALSCOPE_CONSTANTLIST, + MONO_LOCALSCOPE_STARTOFFSET, + MONO_LOCALSCOPE_LENGTH, + MONO_LOCALSCOPE_SIZE +}; + +enum { + MONO_LOCALVARIABLE_ATTRIBUTES, + MONO_LOCALVARIABLE_INDEX, + MONO_LOCALVARIABLE_NAME, + MONO_LOCALVARIABLE_SIZE +}; + +enum { + MONO_CUSTOMDEBUGINFORMATION_PARENT, + MONO_CUSTOMDEBUGINFORMATION_KIND, + MONO_CUSTOMDEBUGINFORMATION_VALUE, + MONO_CUSTOMDEBUGINFORMATION_SIZE +}; + +/* + * Coded Tokens + * The _BITS entry is for the bits used in the token. + * The _MASK entry is for mask the index out. + */ + +enum { + MONO_TYPEDEFORREF_TYPEDEF, + MONO_TYPEDEFORREF_TYPEREF, + MONO_TYPEDEFORREF_TYPESPEC, + MONO_TYPEDEFORREF_BITS = 2, + MONO_TYPEDEFORREF_MASK = 3 +}; + +enum { + MONO_HASCONSTANT_FIEDDEF, + MONO_HASCONSTANT_PARAM, + MONO_HASCONSTANT_PROPERTY, + MONO_HASCONSTANT_BITS = 2, + MONO_HASCONSTANT_MASK = 3 +}; + +enum { + MONO_CUSTOM_ATTR_METHODDEF, + MONO_CUSTOM_ATTR_FIELDDEF, + MONO_CUSTOM_ATTR_TYPEREF, + MONO_CUSTOM_ATTR_TYPEDEF, + MONO_CUSTOM_ATTR_PARAMDEF, + MONO_CUSTOM_ATTR_INTERFACE, + MONO_CUSTOM_ATTR_MEMBERREF, + MONO_CUSTOM_ATTR_MODULE, + MONO_CUSTOM_ATTR_PERMISSION, + MONO_CUSTOM_ATTR_PROPERTY, + MONO_CUSTOM_ATTR_EVENT, + MONO_CUSTOM_ATTR_SIGNATURE, + MONO_CUSTOM_ATTR_MODULEREF, + MONO_CUSTOM_ATTR_TYPESPEC, + MONO_CUSTOM_ATTR_ASSEMBLY, + MONO_CUSTOM_ATTR_ASSEMBLYREF, + MONO_CUSTOM_ATTR_FILE, + MONO_CUSTOM_ATTR_EXP_TYPE, + MONO_CUSTOM_ATTR_MANIFEST, + MONO_CUSTOM_ATTR_GENERICPAR, + MONO_CUSTOM_ATTR_BITS = 5, + MONO_CUSTOM_ATTR_MASK = 0x1F +}; + +enum { + MONO_HAS_FIELD_MARSHAL_FIELDSREF, + MONO_HAS_FIELD_MARSHAL_PARAMDEF, + MONO_HAS_FIELD_MARSHAL_BITS = 1, + MONO_HAS_FIELD_MARSHAL_MASK = 1 +}; + +enum { + MONO_HAS_DECL_SECURITY_TYPEDEF, + MONO_HAS_DECL_SECURITY_METHODDEF, + MONO_HAS_DECL_SECURITY_ASSEMBLY, + MONO_HAS_DECL_SECURITY_BITS = 2, + MONO_HAS_DECL_SECURITY_MASK = 3 +}; + +enum { + MONO_MEMBERREF_PARENT_TYPEDEF, /* not used */ + MONO_MEMBERREF_PARENT_TYPEREF, + MONO_MEMBERREF_PARENT_MODULEREF, + MONO_MEMBERREF_PARENT_METHODDEF, + MONO_MEMBERREF_PARENT_TYPESPEC, + MONO_MEMBERREF_PARENT_BITS = 3, + MONO_MEMBERREF_PARENT_MASK = 7 +}; + +enum { + MONO_HAS_SEMANTICS_EVENT, + MONO_HAS_SEMANTICS_PROPERTY, + MONO_HAS_SEMANTICS_BITS = 1, + MONO_HAS_SEMANTICS_MASK = 1 +}; + +enum { + MONO_METHODDEFORREF_METHODDEF, + MONO_METHODDEFORREF_METHODREF, + MONO_METHODDEFORREF_BITS = 1, + MONO_METHODDEFORREF_MASK = 1 +}; + +enum { + MONO_MEMBERFORWD_FIELDDEF, + MONO_MEMBERFORWD_METHODDEF, + MONO_MEMBERFORWD_BITS = 1, + MONO_MEMBERFORWD_MASK = 1 +}; + +enum { + MONO_IMPLEMENTATION_FILE, + MONO_IMPLEMENTATION_ASSEMBLYREF, + MONO_IMPLEMENTATION_EXP_TYPE, + MONO_IMPLEMENTATION_BITS = 2, + MONO_IMPLEMENTATION_MASK = 3 +}; + +enum { + MONO_CUSTOM_ATTR_TYPE_TYPEREF, /* not used */ + MONO_CUSTOM_ATTR_TYPE_TYPEDEF, /* not used */ + MONO_CUSTOM_ATTR_TYPE_METHODDEF, + MONO_CUSTOM_ATTR_TYPE_MEMBERREF, + MONO_CUSTOM_ATTR_TYPE_STRING, /* not used */ + MONO_CUSTOM_ATTR_TYPE_BITS = 3, + MONO_CUSTOM_ATTR_TYPE_MASK = 7 +}; + +enum { + MONO_RESOLUTION_SCOPE_MODULE, + MONO_RESOLUTION_SCOPE_MODULEREF, + MONO_RESOLUTION_SCOPE_ASSEMBLYREF, + MONO_RESOLUTION_SCOPE_TYPEREF, + MONO_RESOLUTION_SCOPE_BITS = 2, + MONO_RESOLUTION_SCOPE_MASK = 3 +}; + +/* Kept for compatibility since this is a public header file */ +enum { + MONO_RESOLTION_SCOPE_MODULE, + MONO_RESOLTION_SCOPE_MODULEREF, + MONO_RESOLTION_SCOPE_ASSEMBLYREF, + MONO_RESOLTION_SCOPE_TYPEREF, + MONO_RESOLTION_SCOPE_BITS = 2, + MONO_RESOLTION_SCOPE_MASK = 3 +}; + +enum { + MONO_TYPEORMETHOD_TYPE, + MONO_TYPEORMETHOD_METHOD, + MONO_TYPEORMETHOD_BITS = 1, + MONO_TYPEORMETHOD_MASK = 1 +}; + +#endif /* __MONO_METADATA_ROW_INDEXES_H__ */ + + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/runtime.c b/unity-2019.4.24f1-mbe/mono/metadata/runtime.c new file mode 100644 index 000000000..e0edcfa7b --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/runtime.c @@ -0,0 +1,146 @@ +/** + * \file + * Runtime functions + * + * Authors: + * Jonathan Pryor + * + * Copyright 2010 Novell, Inc (http://www.novell.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static gboolean shutting_down_inited = FALSE; +static gboolean shutting_down = FALSE; + +/** + * mono_runtime_set_shutting_down: + * \deprecated This function can break the shutdown sequence. + * + * Invoked by \c System.Environment.Exit to flag that the runtime + * is shutting down. + */ +void +mono_runtime_set_shutting_down (void) +{ + UnlockedWriteBool (&shutting_down, TRUE); +} + +/** + * mono_runtime_is_shutting_down: + * This is consumed by the \c P:System.Environment.HasShutdownStarted property. + * \returns whether the runtime has been flagged for shutdown. + */ +gboolean +mono_runtime_is_shutting_down (void) +{ + return UnlockedReadBool (&shutting_down); +} + +static void +fire_process_exit_event (MonoDomain *domain, gpointer user_data) +{ + MonoError error; + MonoClassField *field; + gpointer pa [2]; + MonoObject *delegate, *exc; + + field = mono_class_get_field_from_name (mono_defaults.appdomain_class, "ProcessExit"); + g_assert (field); + + delegate = *(MonoObject **)(((char *)domain->domain) + field->offset); + if (delegate == NULL) + return; + + pa [0] = domain; + pa [1] = NULL; + mono_runtime_delegate_try_invoke (delegate, pa, &exc, &error); + mono_error_cleanup (&error); +} + +static void +mono_runtime_fire_process_exit_event (void) +{ +#ifndef MONO_CROSS_COMPILE + mono_domain_foreach (fire_process_exit_event, NULL); +#endif +} + +/** + * mono_runtime_try_shutdown: + * + * Try to initialize runtime shutdown. + * + * After this call completes the thread pool will stop accepting new jobs and no further threads will be created. + * + * Returns: TRUE if shutdown was initiated by this call or false is other thread beat this one. + */ +gboolean +mono_runtime_try_shutdown (void) +{ + if (mono_atomic_cas_i32 (&shutting_down_inited, TRUE, FALSE)) + return FALSE; + + mono_runtime_fire_process_exit_event (); + + mono_runtime_set_shutting_down (); + + mono_threads_set_shutting_down (); + + /* No new threads will be created after this point */ + + /*TODO move the follow to here: + mono_thread_suspend_all_other_threads (); OR mono_thread_wait_all_other_threads + + mono_runtime_quit (); + */ + + return TRUE; +} + +/* +Coordinate the creation of all remaining TLS slots in the runtime. +No further TLS slots should be created after this function finishes. +This restriction exists because AOT requires offsets to be constant +across runs. +*/ +void +mono_runtime_init_tls (void) +{ + mono_marshal_init_tls (); +} + +char* +mono_runtime_get_aotid (void) +{ + int i; + guint8 aotid_sum = 0; + MonoDomain* domain = mono_domain_get (); + + if (!domain->entry_assembly || !domain->entry_assembly->image) + return NULL; + + guint8 (*aotid)[16] = &domain->entry_assembly->image->aotid; + + for (i = 0; i < 16; ++i) + aotid_sum |= (*aotid)[i]; + + if (aotid_sum == 0) + return NULL; + + return mono_guid_to_string ((guint8*) aotid); +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/runtime.h b/unity-2019.4.24f1-mbe/mono/metadata/runtime.h new file mode 100644 index 000000000..ddd6d5deb --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/runtime.h @@ -0,0 +1,30 @@ +/** + * \file + * Runtime functions + * + * Author: + * Jonathan Pryor + * + * (C) 2010 Novell, Inc. + */ + +#ifndef _MONO_METADATA_RUNTIME_H_ +#define _MONO_METADATA_RUNTIME_H_ + +#include +#include +#include +#include + +MONO_BEGIN_DECLS + +gboolean mono_runtime_try_shutdown (void); + +void mono_runtime_init_tls (void); + +char* mono_runtime_get_aotid (void); +MONO_END_DECLS + +#endif /* _MONO_METADATA_RUNTIME_H_ */ + + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/security-core-clr.c b/unity-2019.4.24f1-mbe/mono/metadata/security-core-clr.c new file mode 100644 index 000000000..3498ef3c5 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/security-core-clr.c @@ -0,0 +1,1138 @@ +/** + * \file + * CoreCLR security + * + * Authors: + * Mark Probst + * Sebastien Pouliot + * + * Copyright 2007-2010 Novell, Inc (http://www.novell.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "security-core-clr.h" + +gboolean mono_security_core_clr_test = FALSE; + +static MonoSecurityCoreCLROptions security_core_clr_options = MONO_SECURITY_CORE_CLR_OPTIONS_DEFAULT; + +/** + * mono_security_core_clr_set_options: + * \param options the new options for the coreclr system to use + * + * By default, the CoreCLRs security model forbids execution trough reflection of methods not visible from the calling code. + * Even if the method being called is not in a platform assembly. For non moonlight CoreCLR users this restriction does not + * make a lot of sense, since the author could have just changed the non platform assembly to allow the method to be called. + * This function allows specific relaxations from the default behaviour to be set. + * + * Use \c MONO_SECURITY_CORE_CLR_OPTIONS_DEFAULT for the default coreclr coreclr behaviour as used in Moonlight. + * + * Use \c MONO_SECURITY_CORE_CLR_OPTIONS_RELAX_REFLECTION to allow transparent code to execute methods and access + * fields that are not in platformcode, even if those methods and fields are private or otherwise not visible to the calling code. + * + * Use \c MONO_SECURITY_CORE_CLR_OPTIONS_RELAX_DELEGATE to allow delegates to be created that point at methods that are not in + * platformcode even if those methods and fields are private or otherwise not visible to the calling code. + * + */ +void +mono_security_core_clr_set_options (MonoSecurityCoreCLROptions options) { + security_core_clr_options = options; +} + +/** + * mono_security_core_clr_get_options: + * + * Retrieves the current options used by the coreclr system. + */ + +MonoSecurityCoreCLROptions +mono_security_core_clr_get_options () +{ + return security_core_clr_options; +} + +/* + * default_platform_check: + * + * Default platform check. Always TRUE for current corlib (minimum + * trust-able subset) otherwise return FALSE. Any real CoreCLR host + * should provide its own callback to define platform code (i.e. + * this default is meant for test only). + */ +static gboolean +default_platform_check (const char *image_name) +{ + if (mono_defaults.corlib) { + return (strcmp (mono_defaults.corlib->name, image_name) == 0); + } else { + /* this can get called even before we load corlib (e.g. the EXE itself) */ + const char *corlib = "mscorlib.dll"; + int ilen = strlen (image_name); + int clen = strlen (corlib); + return ((ilen >= clen) && (strcmp ("mscorlib.dll", image_name + ilen - clen) == 0)); + } +} + +static MonoCoreClrPlatformCB platform_callback = default_platform_check; + +/* + * mono_security_core_clr_determine_platform_image: + * + * Call the supplied callback (from mono_security_set_core_clr_platform_callback) + * to determine if this image represents platform code. + */ +gboolean +mono_security_core_clr_determine_platform_image (MonoImage *image) +{ + return platform_callback (image->name); +} + +/* + * mono_security_set_core_clr_platform_callback: + * + * Set the callback function that will be used to determine if an image + * is part, or not, of the platform code. + */ +void +mono_security_set_core_clr_platform_callback (MonoCoreClrPlatformCB callback) +{ + platform_callback = callback; +} + +/* + * mono_security_core_clr_is_platform_image: + * + * Return the (cached) boolean value indicating if this image represent platform code + */ +gboolean +mono_security_core_clr_is_platform_image (MonoImage *image) +{ + return image->core_clr_platform_code; +} + +/* Note: The above functions are outside this guard so that the public API isn't affected. */ + +#ifndef DISABLE_SECURITY + +/* Class lazy loading functions */ +static GENERATE_GET_CLASS_WITH_CACHE (security_critical, "System.Security", "SecurityCriticalAttribute") +static GENERATE_GET_CLASS_WITH_CACHE (security_safe_critical, "System.Security", "SecuritySafeCriticalAttribute") + +static MonoClass* +security_critical_attribute (void) +{ + return mono_class_get_security_critical_class (); +} + +static MonoClass* +security_safe_critical_attribute (void) +{ + return mono_class_get_security_safe_critical_class (); + +} + +/* sometime we get a NULL (not found) caller (e.g. get_reflection_caller) */ +static char* +get_method_full_name (MonoMethod * method) +{ + return method ? mono_method_full_name (method, TRUE) : g_strdup ("'no caller found'"); +} + +/* + * set_type_load_exception_type + * + * Set MONO_EXCEPTION_TYPE_LOAD on the specified 'class' and provide + * a descriptive message for the exception. This message is also, + * optionally, being logged (export MONO_LOG_MASK="security") for + * debugging purposes. + */ +static void +set_type_load_exception_type (const char *format, MonoClass *klass) +{ + char *type_name = mono_type_get_full_name (klass); + char *parent_name = mono_type_get_full_name (klass->parent); + char *message = mono_image_strdup_printf (klass->image, format, type_name, parent_name); + + g_free (parent_name); + g_free (type_name); + + mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_SECURITY, "%s", message); + mono_class_set_type_load_failure (klass, "%s", message); + // note: do not free string given to mono_class_set_failure +} + +/* + * set_type_load_exception_methods + * + * Set MONO_EXCEPTION_TYPE_LOAD on the 'override' class and provide + * a descriptive message for the exception. This message is also, + * optionally, being logged (export MONO_LOG_MASK="security") for + * debugging purposes. + */ +static void +set_type_load_exception_methods (const char *format, MonoMethod *override, MonoMethod *base) +{ + char *method_name = get_method_full_name (override); + char *base_name = get_method_full_name (base); + char *message = mono_image_strdup_printf (override->klass->image, format, method_name, base_name); + + g_free (base_name); + g_free (method_name); + + mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_SECURITY, "%s", message); + mono_class_set_type_load_failure (override->klass, "%s", message); + // note: do not free string given to mono_class_set_failure +} + +/* MonoClass is not fully initialized (inited is not yet == 1) when we + * check the inheritance rules so we need to look for the default ctor + * ourselve to avoid recursion (and aborting) + */ +static MonoMethod* +get_default_ctor (MonoClass *klass) +{ + int i; + + mono_class_setup_methods (klass); + if (!klass->methods) + return NULL; + + int mcount = mono_class_get_method_count (klass); + for (i = 0; i < mcount; ++i) { + MonoMethodSignature *sig; + MonoMethod *method = klass->methods [i]; + + if (!method) + continue; + + if ((method->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) == 0) + continue; + if ((method->name[0] != '.') || strcmp (".ctor", method->name)) + continue; + sig = mono_method_signature (method); + if (sig && (sig->param_count == 0)) + return method; + } + + return NULL; +} + +/* + * mono_security_core_clr_check_inheritance: + * + * Determine if the specified class can inherit from its parent using + * the CoreCLR inheritance rules. + * + * Base Type Allow Derived Type + * ------------ ------------------ + * Transparent Transparent, SafeCritical, Critical + * SafeCritical SafeCritical, Critical + * Critical Critical + * + * Reference: http://msdn.microsoft.com/en-us/magazine/cc765416.aspx#id0190030 + * + * Furthermore a class MUST have a default constructor if its base + * class has a non-transparent, public or protected, default constructor. + * The same inheritance rule applies to both default constructors. + * + * Reference: message from a SecurityException in SL4RC + * Reference: fxcop CA2132 rule + */ +void +mono_security_core_clr_check_inheritance (MonoClass *klass) +{ + MonoSecurityCoreCLRLevel class_level, parent_level; + MonoClass *parent = klass->parent; + + if (!parent) + return; + + class_level = mono_security_core_clr_class_level (klass); + parent_level = mono_security_core_clr_class_level (parent); + + if (class_level < parent_level) { + set_type_load_exception_type ( + "Inheritance failure for type %s. Parent class %s is more restricted.", + klass); + } else { + MonoMethod *parent_ctor = get_default_ctor (parent); + if (parent_ctor && ((parent_ctor->flags & METHOD_ATTRIBUTE_PUBLIC) != 0)) { + class_level = mono_security_core_clr_method_level (get_default_ctor (klass), FALSE); + parent_level = mono_security_core_clr_method_level (parent_ctor, FALSE); + if (class_level < parent_level) { + set_type_load_exception_type ( + "Inheritance failure for type %s. Default constructor security mismatch with %s.", + klass); + } + } + } +} + +/* + * mono_security_core_clr_check_override: + * + * Determine if the specified override can "legally" override the + * specified base method using the CoreCLR inheritance rules. + * + * Base (virtual/interface) Allowed override + * ------------------------ ------------------------- + * Transparent Transparent, SafeCritical + * SafeCritical Transparent, SafeCritical + * Critical Critical + * + * Reference: http://msdn.microsoft.com/en-us/magazine/cc765416.aspx#id0190030 + */ +void +mono_security_core_clr_check_override (MonoClass *klass, MonoMethod *override, MonoMethod *base) +{ + MonoSecurityCoreCLRLevel base_level = mono_security_core_clr_method_level (base, FALSE); + MonoSecurityCoreCLRLevel override_level = mono_security_core_clr_method_level (override, FALSE); + /* if the base method is decorated with [SecurityCritical] then the overrided method MUST be too */ + if (base_level == MONO_SECURITY_CORE_CLR_CRITICAL) { + if (override_level != MONO_SECURITY_CORE_CLR_CRITICAL) { + set_type_load_exception_methods ( + "Override failure for %s over %s. Override MUST be [SecurityCritical].", + override, base); + } + } else { + /* base is [SecuritySafeCritical] or [SecurityTransparent], override MUST NOT be [SecurityCritical] */ + if (override_level == MONO_SECURITY_CORE_CLR_CRITICAL) { + set_type_load_exception_methods ( + "Override failure for %s over %s. Override must NOT be [SecurityCritical].", + override, base); + } + } +} + +/* + * get_caller_no_reflection_related: + * + * Find the first managed caller that is either: + * (a) located outside the platform code assemblies; or + * (b) not related to reflection and delegates + * + * Returns TRUE to stop the stackwalk, FALSE to continue to the next frame. + */ +static gboolean +get_caller_no_reflection_related (MonoMethod *m, gint32 no, gint32 ilo, gboolean managed, gpointer data) +{ + MonoMethod **dest = (MonoMethod **)data; + const char *ns; + + /* skip unmanaged frames */ + if (!managed) + return FALSE; + + if (m->wrapper_type != MONO_WRAPPER_NONE) + return FALSE; + + /* quick out (any namespace not starting with an 'S' */ + ns = m->klass->name_space; + if (!ns || (*ns != 'S')) { + *dest = m; + return TRUE; + } + + /* stop if the method is not part of platform code */ + if (!mono_security_core_clr_is_platform_image (m->klass->image)) { + *dest = m; + return TRUE; + } + + /* any number of calls inside System.Reflection are allowed */ + if (strcmp (ns, "System.Reflection") == 0) + return FALSE; + + /* any number of calls inside System.Reflection are allowed */ + if (strcmp (ns, "System.Reflection.Emit") == 0) + return FALSE; + + /* calls from System.Delegate are also possible and allowed */ + if (strcmp (ns, "System") == 0) { + const char *kname = m->klass->name; + if ((*kname == 'A') && (strcmp (kname, "Activator") == 0)) + return FALSE; + + /* unlike most Invoke* cases InvokeMember is not inside System.Reflection[.Emit] but is SecuritySafeCritical */ + if (((*kname == 'T') && (strcmp (kname, "Type") == 0)) || + ((*kname == 'R') && (strcmp (kname, "RuntimeType")) == 0)) { + + /* if calling InvokeMember then we can't stop the stackwalk here and need to look at the caller */ + if (strcmp (m->name, "InvokeMember") == 0) + return FALSE; + } + + /* the security check on the delegate is made at creation time, not at invoke time */ + if (((*kname == 'D') && (strcmp (kname, "Delegate") == 0)) || + ((*kname == 'M') && (strcmp (kname, "MulticastDelegate")) == 0)) { + + /* if we're invoking then we can stop our stack walk */ + if (strcmp (m->name, "DynamicInvoke") != 0) + return FALSE; + } + } + + if (m == *dest) { + *dest = NULL; + return FALSE; + } + + *dest = m; + return TRUE; +} + +/* + * get_reflection_caller: + * + * Walk to the first managed method outside: + * - System.Reflection* namespaces + * - System.[Multicast]Delegate or Activator type + * - platform code + * and return a pointer to its MonoMethod. + * + * This is required since CoreCLR checks needs to be done on this "real" caller. + */ +static MonoMethod* +get_reflection_caller (void) +{ + MonoMethod *m = NULL; + mono_stack_walk_no_il (get_caller_no_reflection_related, &m); + if (G_UNLIKELY (!m)) { + mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_SECURITY, "No caller outside reflection was found"); + } + return m; +} + +typedef struct { + int depth; + MonoMethod *caller; +} ElevatedTrustCookie; + +/* + * get_caller_of_elevated_trust_code + * + * Stack walk to find who is calling code requiring Elevated Trust. + * If a critical method is found then the caller is platform code + * and has elevated trust, otherwise (transparent) a check needs to + * be done (on the managed side) to determine if the application is + * running with elevated permissions. + */ +static gboolean +get_caller_of_elevated_trust_code (MonoMethod *m, gint32 no, gint32 ilo, gboolean managed, gpointer data) +{ + ElevatedTrustCookie *cookie = (ElevatedTrustCookie *)data; + + /* skip unmanaged frames and wrappers */ + if (!managed || (m->wrapper_type != MONO_WRAPPER_NONE)) + return FALSE; + + /* end stack walk if we find ourselves outside platform code (we won't find critical code anymore) */ + if (!mono_security_core_clr_is_platform_image (m->klass->image)) { + cookie->caller = m; + return TRUE; + } + + switch (cookie->depth) { + /* while depth == 0 look for SecurityManager::[Check|Ensure]ElevatedPermissions */ + case 0: + if (strcmp (m->klass->name_space, "System.Security")) + return FALSE; + if (strcmp (m->klass->name, "SecurityManager")) + return FALSE; + if ((strcmp (m->name, "EnsureElevatedPermissions")) && strcmp (m->name, "CheckElevatedPermissions")) + return FALSE; + cookie->depth = 1; + break; + /* while depth == 1 look for the caller to SecurityManager::[Check|Ensure]ElevatedPermissions */ + case 1: + /* this frame is [SecuritySafeCritical] because it calls [SecurityCritical] [Check|Ensure]ElevatedPermissions */ + /* the next frame will contain the caller(s) we want to check */ + cookie->depth = 2; + break; + /* while depth >= 2 look for [safe]critical caller, end stack walk if we find it */ + default: + cookie->depth++; + /* if the caller is transparent then we continue the stack walk */ + if (mono_security_core_clr_method_level (m, TRUE) == MONO_SECURITY_CORE_CLR_TRANSPARENT) + break; + + /* Security[Safe]Critical code is always allowed to call elevated-trust code */ + cookie->caller = m; + return TRUE; + } + + return FALSE; +} + +/* + * mono_security_core_clr_require_elevated_permissions: + * + * Return TRUE if the caller of the current method (the code who + * called SecurityManager.get_RequiresElevatedPermissions) needs + * elevated trust to perform an action. + * + * A stack walk is done to find the callers. If one of the callers + * is either [SecurityCritical] or [SecuritySafeCritical] then the + * action is needed for platform code (i.e. no restriction). + * Otherwise (transparent) the requested action needs elevated trust + */ +gboolean +mono_security_core_clr_require_elevated_permissions (void) +{ + ElevatedTrustCookie cookie; + cookie.depth = 0; + cookie.caller = NULL; + mono_stack_walk_no_il (get_caller_of_elevated_trust_code, &cookie); + + /* return TRUE if the stack walk did not reach far enough or did not find callers */ + if (!cookie.caller || cookie.depth < 3) + return TRUE; + + /* return TRUE if the caller is transparent, i.e. if elevated trust is required to continue executing the method */ + return (mono_security_core_clr_method_level (cookie.caller, TRUE) == MONO_SECURITY_CORE_CLR_TRANSPARENT); +} + + +/* + * check_field_access: + * + * Return TRUE if the caller method can access the specified field, FALSE otherwise. + */ +static gboolean +check_field_access (MonoMethod *caller, MonoClassField *field) +{ + /* if get_reflection_caller returns NULL then we assume the caller has NO privilege */ + if (caller) { + MonoError error; + MonoClass *klass; + + /* this check can occur before the field's type is resolved (and that can fail) */ + mono_field_get_type_checked (field, &error); + if (!mono_error_ok (&error)) { + mono_error_cleanup (&error); + return FALSE; + } + + klass = (mono_field_get_flags (field) & FIELD_ATTRIBUTE_STATIC) ? NULL : mono_field_get_parent (field); + return mono_method_can_access_field_full (caller, field, klass); + } + return FALSE; +} + +/* + * check_method_access: + * + * Return TRUE if the caller method can access the specified callee method, FALSE otherwise. + */ +static gboolean +check_method_access (MonoMethod *caller, MonoMethod *callee) +{ + /* if get_reflection_caller returns NULL then we assume the caller has NO privilege */ + if (caller) { + MonoClass *klass = (callee->flags & METHOD_ATTRIBUTE_STATIC) ? NULL : callee->klass; + return mono_method_can_access_method_full (caller, callee, klass); + } + return FALSE; +} + +/* + * get_argument_exception + * + * Helper function to create an MonoException (ArgumentException in + * managed-land) and provide a descriptive message for it. This + * message is also, optionally, being logged (export + * MONO_LOG_MASK="security") for debugging purposes. + */ +static MonoException* +get_argument_exception (const char *format, MonoMethod *caller, MonoMethod *callee) +{ + MonoException *ex; + char *caller_name = get_method_full_name (caller); + char *callee_name = get_method_full_name (callee); + char *message = g_strdup_printf (format, caller_name, callee_name); + g_free (callee_name); + g_free (caller_name); + + mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_SECURITY, "%s", message); + ex = mono_get_exception_argument ("method", message); + g_free (message); + + return ex; +} + +/* + * get_field_access_exception + * + * Helper function to create an MonoException (FieldAccessException + * in managed-land) and provide a descriptive message for it. This + * message is also, optionally, being logged (export + * MONO_LOG_MASK="security") for debugging purposes. + */ +static MonoException* +get_field_access_exception (const char *format, MonoMethod *caller, MonoClassField *field) +{ + MonoException *ex; + char *caller_name = get_method_full_name (caller); + char *field_name = mono_field_full_name (field); + char *message = g_strdup_printf (format, caller_name, field_name); + g_free (field_name); + g_free (caller_name); + + mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_SECURITY, "%s", message); + ex = mono_get_exception_field_access_msg (message); + g_free (message); + + return ex; +} + +/* + * get_method_access_exception + * + * Helper function to create an MonoException (MethodAccessException + * in managed-land) and provide a descriptive message for it. This + * message is also, optionally, being logged (export + * MONO_LOG_MASK="security") for debugging purposes. + */ +static MonoException* +get_method_access_exception (const char *format, MonoMethod *caller, MonoMethod *callee) +{ + MonoException *ex; + char *caller_name = get_method_full_name (caller); + char *callee_name = get_method_full_name (callee); + char *message = g_strdup_printf (format, caller_name, callee_name); + g_free (callee_name); + g_free (caller_name); + + mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_SECURITY, "%s", message); + ex = mono_get_exception_method_access_msg (message); + g_free (message); + + return ex; +} + +/* + * mono_security_core_clr_ensure_reflection_access_field: + * + * Ensure that the specified field can be used with reflection since + * Transparent code cannot access to Critical fields and can only use + * them if they are visible from it's point of view. + * + * Returns TRUE if acess is allowed. Otherwise returns FALSE and sets @error to a FieldAccessException if the field is cannot be accessed. + */ +gboolean +mono_security_core_clr_ensure_reflection_access_field (MonoClassField *field, MonoError *error) +{ + error_init (error); + MonoMethod *caller = get_reflection_caller (); + /* CoreCLR restrictions applies to Transparent code/caller */ + if (mono_security_core_clr_method_level (caller, TRUE) != MONO_SECURITY_CORE_CLR_TRANSPARENT) + return TRUE; + + if (mono_security_core_clr_get_options () & MONO_SECURITY_CORE_CLR_OPTIONS_RELAX_REFLECTION) { + if (!mono_security_core_clr_is_platform_image (mono_field_get_parent(field)->image)) + return TRUE; + } + + /* Transparent code cannot [get|set]value on Critical fields */ + if (mono_security_core_clr_class_level (mono_field_get_parent (field)) == MONO_SECURITY_CORE_CLR_CRITICAL) { + mono_error_set_exception_instance (error, get_field_access_exception ( + "Transparent method %s cannot get or set Critical field %s.", + caller, field)); + return FALSE; + } + + /* also it cannot access a fields that is not visible from it's (caller) point of view */ + if (!check_field_access (caller, field)) { + mono_error_set_exception_instance (error, get_field_access_exception ( + "Transparent method %s cannot get or set private/internal field %s.", + caller, field)); + return FALSE; + } + return TRUE; +} + +/* + * mono_security_core_clr_ensure_reflection_access_method: + * + * Ensure that the specified method can be used with reflection since + * Transparent code cannot call Critical methods and can only call them + * if they are visible from it's point of view. + * + * If access is allowed returns TRUE. Returns FALSE and sets @error to a MethodAccessException if the field is cannot be accessed. + */ +gboolean +mono_security_core_clr_ensure_reflection_access_method (MonoMethod *method, MonoError *error) +{ + error_init (error); + MonoMethod *caller = get_reflection_caller (); + /* CoreCLR restrictions applies to Transparent code/caller */ + if (mono_security_core_clr_method_level (caller, TRUE) != MONO_SECURITY_CORE_CLR_TRANSPARENT) + return TRUE; + + if (mono_security_core_clr_get_options () & MONO_SECURITY_CORE_CLR_OPTIONS_RELAX_REFLECTION) { + if (!mono_security_core_clr_is_platform_image (method->klass->image)) + return TRUE; + } + + /* Transparent code cannot invoke, even using reflection, Critical code */ + if (mono_security_core_clr_method_level (method, TRUE) == MONO_SECURITY_CORE_CLR_CRITICAL) { + mono_error_set_exception_instance (error, get_method_access_exception ( + "Transparent method %s cannot invoke Critical method %s.", + caller, method)); + return FALSE; + } + + /* also it cannot invoke a method that is not visible from it's (caller) point of view */ + if (!check_method_access (caller, method)) { + mono_error_set_exception_instance (error, get_method_access_exception ( + "Transparent method %s cannot invoke private/internal method %s.", + caller, method)); + return FALSE; + } + return TRUE; +} + +/* + * can_avoid_corlib_reflection_delegate_optimization: + * + * Mono's mscorlib use delegates to optimize PropertyInfo and EventInfo + * reflection calls. This requires either a bunch of additional, and not + * really required, [SecuritySafeCritical] in the class libraries or + * (like this) a way to skip them. As a bonus we also avoid the stack + * walk to find the caller. + * + * Return TRUE if we can skip this "internal" delegate creation, FALSE + * otherwise. + */ +static gboolean +can_avoid_corlib_reflection_delegate_optimization (MonoMethod *method) +{ + if (!mono_security_core_clr_is_platform_image (method->klass->image)) + return FALSE; + + if (strcmp (method->klass->name_space, "System.Reflection") != 0) + return FALSE; + + if (strcmp (method->klass->name, "MonoProperty") == 0) { + if ((strcmp (method->name, "GetterAdapterFrame") == 0) || strcmp (method->name, "StaticGetterAdapterFrame") == 0) + return TRUE; + } else if (strcmp (method->klass->name, "EventInfo") == 0) { + if ((strcmp (method->name, "AddEventFrame") == 0) || strcmp (method->name, "StaticAddEventAdapterFrame") == 0) + return TRUE; + } + + return FALSE; +} + +/* + * mono_security_core_clr_ensure_delegate_creation: + * + * Return TRUE if a delegate can be created on the specified + * method. CoreCLR can also affect the binding, this function may + * return (FALSE) and set @error to an ArgumentException. + * + * @error is set to a MethodAccessException if the specified method is not + * visible from the caller point of view. + */ +gboolean +mono_security_core_clr_ensure_delegate_creation (MonoMethod *method, MonoError *error) +{ + MonoMethod *caller; + + error_init (error); + + /* note: mscorlib creates delegates to avoid reflection (optimization), we ignore those cases */ + if (can_avoid_corlib_reflection_delegate_optimization (method)) + return TRUE; + + caller = get_reflection_caller (); + /* if the "real" caller is not Transparent then it do can anything */ + if (mono_security_core_clr_method_level (caller, TRUE) != MONO_SECURITY_CORE_CLR_TRANSPARENT) + return TRUE; + + /* otherwise it (as a Transparent caller) cannot create a delegate on a Critical method... */ + if (mono_security_core_clr_method_level (method, TRUE) == MONO_SECURITY_CORE_CLR_CRITICAL) { + mono_error_set_exception_instance (error, get_argument_exception ( + "Transparent method %s cannot create a delegate on Critical method %s.", + caller, method)); + return FALSE; + } + + if (mono_security_core_clr_get_options () & MONO_SECURITY_CORE_CLR_OPTIONS_RELAX_DELEGATE) { + if (!mono_security_core_clr_is_platform_image (method->klass->image)) + return TRUE; + } + + /* also it cannot create the delegate on a method that is not visible from it's (caller) point of view */ + if (!check_method_access (caller, method)) { + mono_error_set_exception_instance (error, get_method_access_exception ( + "Transparent method %s cannot create a delegate on private/internal method %s.", + caller, method)); + return FALSE; + } + + return TRUE; +} + +/* + * mono_security_core_clr_ensure_dynamic_method_resolved_object: + * + * Called from mono_reflection_create_dynamic_method (reflection.c) to add some extra checks required for CoreCLR. + * Dynamic methods needs to check to see if the objects being used (e.g. methods, fields) comes from platform code + * and do an accessibility check in this case. Otherwise (i.e. user/application code) can be used without this extra + * accessbility check. + */ +MonoException* +mono_security_core_clr_ensure_dynamic_method_resolved_object (gpointer ref, MonoClass *handle_class) +{ + /* XXX find/create test cases for other handle_class XXX */ + if (handle_class == mono_defaults.fieldhandle_class) { + MonoClassField *field = (MonoClassField*) ref; + MonoClass *klass = mono_field_get_parent (field); + /* fields coming from platform code have extra protection (accessibility check) */ + if (mono_security_core_clr_is_platform_image (klass->image)) { + MonoMethod *caller = get_reflection_caller (); + /* XXX Critical code probably can do this / need some test cases (safer off otherwise) XXX */ + if (!check_field_access (caller, field)) { + return get_field_access_exception ( + "Dynamic method %s cannot create access private/internal field %s.", + caller, field); + } + } + } else if (handle_class == mono_defaults.methodhandle_class) { + MonoMethod *method = (MonoMethod*) ref; + /* methods coming from platform code have extra protection (accessibility check) */ + if (mono_security_core_clr_is_platform_image (method->klass->image)) { + MonoMethod *caller = get_reflection_caller (); + /* XXX Critical code probably can do this / need some test cases (safer off otherwise) XXX */ + if (!check_method_access (caller, method)) { + return get_method_access_exception ( + "Dynamic method %s cannot create access private/internal method %s.", + caller, method); + } + } + } + return NULL; +} + +/* + * mono_security_core_clr_can_access_internals + * + * Check if we allow [InternalsVisibleTo] to work between two images. + */ +gboolean +mono_security_core_clr_can_access_internals (MonoImage *accessing, MonoImage* accessed) +{ + /* are we trying to access internals of a platform assembly ? if not this is acceptable */ + if (!mono_security_core_clr_is_platform_image (accessed)) + return TRUE; + + /* we can't let everyone with the right name and public key token access the internals of platform code. + * (Silverlight can rely on the strongname signature of the assemblies, but Mono does not verify them) + * However platform code is fully trusted so it can access the internals of other platform code assemblies */ + if (mono_security_core_clr_is_platform_image (accessing)) + return TRUE; + + /* catch-22: System.Xml needs access to mscorlib's internals (e.g. ArrayList) but is not considered platform code. + * Promoting it to platform code would create another issue since (both Mono/Moonlight or MS version of) + * System.Xml.Linq.dll (an SDK, not platform, assembly) needs access to System.Xml.dll internals (either ). + * The solution is to trust, even transparent code, in the plugin directory to access platform code internals */ + if (!accessed->assembly->basedir || !accessing->assembly->basedir) + return FALSE; + return (strcmp (accessed->assembly->basedir, accessing->assembly->basedir) == 0); +} + +/* + * mono_security_core_clr_is_field_access_allowed + * + * Return a MonoException (FieldccessException in managed-land) if + * the access from "caller" to "field" is not valid under CoreCLR - + * i.e. a [SecurityTransparent] method calling a [SecurityCritical] + * field. + */ +MonoException* +mono_security_core_clr_is_field_access_allowed (MonoMethod *caller, MonoClassField *field) +{ + /* there's no restriction to access Transparent or SafeCritical fields, so we only check calls to Critical methods */ + if (mono_security_core_clr_class_level (mono_field_get_parent (field)) != MONO_SECURITY_CORE_CLR_CRITICAL) + return NULL; + + /* caller is Critical! only SafeCritical and Critical callers can access the field, so we throw if caller is Transparent */ + if (!caller || (mono_security_core_clr_method_level (caller, TRUE) != MONO_SECURITY_CORE_CLR_TRANSPARENT)) + return NULL; + + return get_field_access_exception ( + "Transparent method %s cannot call use Critical field %s.", + caller, field); +} + +/* + * mono_security_core_clr_is_call_allowed + * + * Return a MonoException (MethodAccessException in managed-land) if + * the call from "caller" to "callee" is not valid under CoreCLR - + * i.e. a [SecurityTransparent] method calling a [SecurityCritical] + * method. + */ +MonoException* +mono_security_core_clr_is_call_allowed (MonoMethod *caller, MonoMethod *callee) +{ + /* there's no restriction to call Transparent or SafeCritical code, so we only check calls to Critical methods */ + if (mono_security_core_clr_method_level (callee, TRUE) != MONO_SECURITY_CORE_CLR_CRITICAL) + return NULL; + + /* callee is Critical! only SafeCritical and Critical callers can call it, so we throw if the caller is Transparent */ + if (!caller || (mono_security_core_clr_method_level (caller, TRUE) != MONO_SECURITY_CORE_CLR_TRANSPARENT)) + return NULL; + + return get_method_access_exception ( + "Transparent method %s cannot call Critical method %s.", + caller, callee); +} + +/* + * mono_security_core_clr_level_from_cinfo: + * + * Return the MonoSecurityCoreCLRLevel that match the attribute located + * in the specified custom attributes. If no attribute is present it + * defaults to MONO_SECURITY_CORE_CLR_TRANSPARENT, which is the default + * level for all code under the CoreCLR. + */ +static MonoSecurityCoreCLRLevel +mono_security_core_clr_level_from_cinfo (MonoCustomAttrInfo *cinfo, MonoImage *image) +{ + int level = MONO_SECURITY_CORE_CLR_TRANSPARENT; + + if (cinfo && mono_custom_attrs_has_attr (cinfo, security_safe_critical_attribute ())) + level = MONO_SECURITY_CORE_CLR_SAFE_CRITICAL; + if (cinfo && mono_custom_attrs_has_attr (cinfo, security_critical_attribute ())) + level = MONO_SECURITY_CORE_CLR_CRITICAL; + + return (MonoSecurityCoreCLRLevel)level; +} + +/* + * mono_security_core_clr_class_level_no_platform_check: + * + * Return the MonoSecurityCoreCLRLevel for the specified class, without + * checking for platform code. This help us avoid multiple redundant + * checks, e.g. + * - a check for the method and one for the class; + * - a check for the class and outer class(es) ... + */ +static MonoSecurityCoreCLRLevel +mono_security_core_clr_class_level_no_platform_check (MonoClass *klass) +{ + MonoError error; + MonoSecurityCoreCLRLevel level = MONO_SECURITY_CORE_CLR_TRANSPARENT; + MonoCustomAttrInfo *cinfo = mono_custom_attrs_from_class_checked (klass, &error); + mono_error_cleanup (&error); + if (cinfo) { + level = mono_security_core_clr_level_from_cinfo (cinfo, klass->image); + mono_custom_attrs_free (cinfo); + } + + if (level == MONO_SECURITY_CORE_CLR_TRANSPARENT && klass->nested_in) + level = mono_security_core_clr_class_level_no_platform_check (klass->nested_in); + + return level; +} + +/* + * mono_security_core_clr_class_level: + * + * Return the MonoSecurityCoreCLRLevel for the specified class. + */ +MonoSecurityCoreCLRLevel +mono_security_core_clr_class_level (MonoClass *klass) +{ + /* non-platform code is always Transparent - whatever the attributes says */ + if (!mono_security_core_clr_test && !mono_security_core_clr_is_platform_image (klass->image)) + return MONO_SECURITY_CORE_CLR_TRANSPARENT; + + return mono_security_core_clr_class_level_no_platform_check (klass); +} + +/* + * mono_security_core_clr_field_level: + * + * Return the MonoSecurityCoreCLRLevel for the specified field. + * If with_class_level is TRUE then the type (class) will also be + * checked, otherwise this will only report the information about + * the field itself. + */ +MonoSecurityCoreCLRLevel +mono_security_core_clr_field_level (MonoClassField *field, gboolean with_class_level) +{ + MonoError error; + MonoCustomAttrInfo *cinfo; + MonoSecurityCoreCLRLevel level = MONO_SECURITY_CORE_CLR_TRANSPARENT; + + /* if get_reflection_caller returns NULL then we assume the caller has NO privilege */ + if (!field) + return level; + + /* non-platform code is always Transparent - whatever the attributes says */ + if (!mono_security_core_clr_test && !mono_security_core_clr_is_platform_image (field->parent->image)) + return level; + + cinfo = mono_custom_attrs_from_field_checked (field->parent, field, &error); + mono_error_cleanup (&error); + if (cinfo) { + level = mono_security_core_clr_level_from_cinfo (cinfo, field->parent->image); + mono_custom_attrs_free (cinfo); + } + + if (with_class_level && level == MONO_SECURITY_CORE_CLR_TRANSPARENT) + level = mono_security_core_clr_class_level (field->parent); + + return level; +} + +/* + * mono_security_core_clr_method_level: + * + * Return the MonoSecurityCoreCLRLevel for the specified method. + * If with_class_level is TRUE then the type (class) will also be + * checked, otherwise this will only report the information about + * the method itself. + */ +MonoSecurityCoreCLRLevel +mono_security_core_clr_method_level (MonoMethod *method, gboolean with_class_level) +{ + MonoError error; + MonoCustomAttrInfo *cinfo; + MonoSecurityCoreCLRLevel level = MONO_SECURITY_CORE_CLR_TRANSPARENT; + + /* if get_reflection_caller returns NULL then we assume the caller has NO privilege */ + if (!method) + return level; + + /* non-platform code is always Transparent - whatever the attributes says */ + if (!mono_security_core_clr_test && !mono_security_core_clr_is_platform_image (method->klass->image)) + return level; + + cinfo = mono_custom_attrs_from_method_checked (method, &error); + mono_error_cleanup (&error); + if (cinfo) { + level = mono_security_core_clr_level_from_cinfo (cinfo, method->klass->image); + mono_custom_attrs_free (cinfo); + } + + if (with_class_level && level == MONO_SECURITY_CORE_CLR_TRANSPARENT) + level = mono_security_core_clr_class_level (method->klass); + + return level; +} + +/* + * mono_security_enable_core_clr: + * + * Enable the verifier and the CoreCLR security model + */ +void +mono_security_enable_core_clr () +{ + mono_verifier_set_mode (MONO_VERIFIER_MODE_VERIFIABLE); + mono_security_set_mode (MONO_SECURITY_MODE_CORE_CLR); +} + +#else + +void +mono_security_core_clr_check_inheritance (MonoClass *klass) +{ +} + +void +mono_security_core_clr_check_override (MonoClass *klass, MonoMethod *override, MonoMethod *base) +{ +} + +gboolean +mono_security_core_clr_require_elevated_permissions (void) +{ + return FALSE; +} + +gboolean +mono_security_core_clr_ensure_reflection_access_field (MonoClassField *field, MonoError *error) +{ + error_init (error); + return TRUE; +} + +gboolean +mono_security_core_clr_ensure_reflection_access_method (MonoMethod *method, MonoError *error) +{ + error_init (error); + return TRUE; +} + +gboolean +mono_security_core_clr_ensure_delegate_creation (MonoMethod *method, MonoError *error) +{ + error_init (error); + return TRUE; +} + +MonoException* +mono_security_core_clr_ensure_dynamic_method_resolved_object (gpointer ref, MonoClass *handle_class) +{ + return NULL; +} + +gboolean +mono_security_core_clr_can_access_internals (MonoImage *accessing, MonoImage* accessed) +{ + return TRUE; +} + +MonoException* +mono_security_core_clr_is_field_access_allowed (MonoMethod *caller, MonoClassField *field) +{ + return NULL; +} + +MonoException* +mono_security_core_clr_is_call_allowed (MonoMethod *caller, MonoMethod *callee) +{ + return NULL; +} + +MonoSecurityCoreCLRLevel +mono_security_core_clr_class_level (MonoClass *klass) +{ + return MONO_SECURITY_CORE_CLR_TRANSPARENT; +} + +MonoSecurityCoreCLRLevel +mono_security_core_clr_field_level (MonoClassField *field, gboolean with_class_level) +{ + return MONO_SECURITY_CORE_CLR_TRANSPARENT; +} + +MonoSecurityCoreCLRLevel +mono_security_core_clr_method_level (MonoMethod *method, gboolean with_class_level) +{ + return MONO_SECURITY_CORE_CLR_TRANSPARENT; +} + +void +mono_security_enable_core_clr () +{ +} + +#endif /* DISABLE_SECURITY */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/security-core-clr.h b/unity-2019.4.24f1-mbe/mono/metadata/security-core-clr.h new file mode 100644 index 000000000..04103412e --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/security-core-clr.h @@ -0,0 +1,70 @@ +/** + * \file + * CoreCLR security + * + * Author: + * Mark Probst + * + * (C) 2007, 2010 Novell, Inc + */ + +#ifndef _MONO_METADATA_SECURITY_CORE_CLR_H_ +#define _MONO_METADATA_SECURITY_CORE_CLR_H_ + +#include +#include +#include + +typedef enum { + /* We compare these values as integers, so the order must not + be changed. */ + MONO_SECURITY_CORE_CLR_TRANSPARENT = 0, + MONO_SECURITY_CORE_CLR_SAFE_CRITICAL, + MONO_SECURITY_CORE_CLR_CRITICAL +} MonoSecurityCoreCLRLevel; + +typedef enum { + //The following flags can be used in combination, and control specific behaviour of the CoreCLR securit system. + + //Default coreclr behaviour, as used in moonlight. + MONO_SECURITY_CORE_CLR_OPTIONS_DEFAULT = 0, + + //Allow transparent code to execute methods and access fields that are not in platformcode, + //even if those methods and fields are private or otherwise not visible to the calling code. + MONO_SECURITY_CORE_CLR_OPTIONS_RELAX_REFLECTION = 1, + + //Allow delegates to be created that point at methods that are not in platformcode, + //even if those methods and fields are private or otherwise not visible to the calling code. + MONO_SECURITY_CORE_CLR_OPTIONS_RELAX_DELEGATE = 2 +} MonoSecurityCoreCLROptions; + +extern gboolean mono_security_core_clr_test; + +extern void mono_security_core_clr_check_inheritance (MonoClass *klass); +extern void mono_security_core_clr_check_override (MonoClass *klass, MonoMethod *override, MonoMethod *base); + +extern gboolean +mono_security_core_clr_ensure_reflection_access_field (MonoClassField *field, MonoError *error); +extern gboolean +mono_security_core_clr_ensure_reflection_access_method (MonoMethod *method, MonoError *error); +extern gboolean mono_security_core_clr_ensure_delegate_creation (MonoMethod *method, MonoError *error); +extern MonoException* mono_security_core_clr_ensure_dynamic_method_resolved_object (gpointer ref, MonoClass *handle_class); + +extern gboolean mono_security_core_clr_can_access_internals (MonoImage *accessing, MonoImage* accessed); + +extern MonoException* mono_security_core_clr_is_field_access_allowed (MonoMethod *caller, MonoClassField *field); +extern MonoException* mono_security_core_clr_is_call_allowed (MonoMethod *caller, MonoMethod *callee); + +extern MonoSecurityCoreCLRLevel mono_security_core_clr_class_level (MonoClass *klass); +extern MonoSecurityCoreCLRLevel mono_security_core_clr_field_level (MonoClassField *field, gboolean with_class_level); +extern MonoSecurityCoreCLRLevel mono_security_core_clr_method_level (MonoMethod *method, gboolean with_class_level); + +extern gboolean mono_security_core_clr_is_platform_image (MonoImage *image); +extern gboolean mono_security_core_clr_determine_platform_image (MonoImage *image); + +extern MONO_API gboolean mono_security_core_clr_require_elevated_permissions (void); + +extern MONO_API void mono_security_core_clr_set_options (MonoSecurityCoreCLROptions options); +extern MONO_API MonoSecurityCoreCLROptions mono_security_core_clr_get_options (void); + +#endif /* _MONO_METADATA_SECURITY_CORE_CLR_H_ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/security-manager.c b/unity-2019.4.24f1-mbe/mono/metadata/security-manager.c new file mode 100644 index 000000000..eba523cfa --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/security-manager.c @@ -0,0 +1,127 @@ +/** + * \file + * Security Manager (Unmanaged side) + * + * Author: + * Sebastien Pouliot + * + * Copyright 2005-2009 Novell, Inc (http://www.novell.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#include "security-manager.h" + +/* Class lazy loading functions */ +static GENERATE_GET_CLASS_WITH_CACHE (security_manager, "System.Security", "SecurityManager") +static GENERATE_TRY_GET_CLASS_WITH_CACHE (execution_context, "System.Threading", "ExecutionContext") + +static MonoSecurityMode mono_security_mode = MONO_SECURITY_MODE_NONE; + +void +mono_security_set_mode (MonoSecurityMode mode) +{ + mono_security_mode = mode; +} + +MonoSecurityMode +mono_security_get_mode (void) +{ + return mono_security_mode; +} + +#ifndef DISABLE_SECURITY + +static MonoSecurityManager secman; + +MonoSecurityManager* +mono_security_manager_get_methods (void) +{ + /* Already initialized ? */ + if (secman.securitymanager) + return &secman; + + /* Initialize */ + secman.securitymanager = mono_class_get_security_manager_class (); + if (!secman.securitymanager->inited) + mono_class_init (secman.securitymanager); + + return &secman; +} + +#else + +MonoSecurityManager* +mono_security_manager_get_methods (void) +{ + return NULL; +} + +#endif /* DISABLE_SECURITY */ + +/* + * @publickey An encoded (with header) public key + * @size The length of the public key + * + * returns TRUE if the public key is the ECMA "key", FALSE otherwise + * + * ECMA key isn't a real public key - it's simply an empty (but valid) header + * so it's length (16) and value (00000000000000000400000000000000) are + * constants. + */ +gboolean +mono_is_ecma_key (const char *publickey, int size) +{ + int i; + if ((publickey == NULL) || (size != MONO_ECMA_KEY_LENGTH) || (publickey [8] != 0x04)) + return FALSE; + + for (i=0; i < size; i++) { + if ((publickey [i] != 0x00) && (i != 8)) + return FALSE; + } + return TRUE; +} + +/* + * Context propagation is required when: + * (a) the security manager is active (1.x and later) + * (b) other contexts needs to be propagated (2.x and later) + * + * returns NULL if no context propagation is required, else the returns the + * MonoMethod to call to Capture the ExecutionContext. + */ +MonoMethod* +mono_get_context_capture_method (void) +{ + static MonoMethod *method = NULL; + + if (mono_image_get_assembly (mono_defaults.corlib)->aname.major < 2) + return NULL; + + /* older corlib revisions won't have the class (nor the method) */ + MonoClass *execution_context = mono_class_try_get_execution_context_class (); + if (execution_context && !method) { + mono_class_init (execution_context); + method = mono_class_get_method_from_name (execution_context, "Capture", 0); + } + + return method; +} + + +/* System.Security icalls */ + +MonoBoolean +ves_icall_System_Security_SecurityManager_get_SecurityEnabled (void) +{ + /* SecurityManager is internal for Moonlight and SecurityEnabled is used to know if CoreCLR is active + * (e.g. plugin executing in the browser) or not (e.g. smcs compiling source code with corlib 2.1) + */ + return (mono_security_get_mode () == MONO_SECURITY_MODE_CORE_CLR); +} + +void +ves_icall_System_Security_SecurityManager_set_SecurityEnabled (MonoBoolean value) +{ +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/security-manager.h b/unity-2019.4.24f1-mbe/mono/metadata/security-manager.h new file mode 100644 index 000000000..d92e169d2 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/security-manager.h @@ -0,0 +1,73 @@ +/** + * \file + * Security Manager + * + * Author: + * Sebastien Pouliot + * + * Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#ifndef _MONO_METADATA_SECURITY_MANAGER_H_ +#define _MONO_METADATA_SECURITY_MANAGER_H_ + +#include + +#include "object.h" +#include "metadata-internals.h" +#include "domain-internals.h" +#include "tokentype.h" +#include "threads.h" +#include "marshal.h" +#include "image.h" +#include "reflection.h" +#include "tabledefs.h" + + +/* Definitions */ + +#define MONO_ECMA_KEY_LENGTH 16 +#define MONO_PUBLIC_KEY_HEADER_LENGTH 32 +#define MONO_MINIMUM_PUBLIC_KEY_LENGTH 48 +#define MONO_DEFAULT_PUBLIC_KEY_LENGTH 128 + +#define MONO_PUBLIC_KEY_BIT_SIZE(x) ((x - MONO_PUBLIC_KEY_HEADER_LENGTH) << 3) + +enum { + MONO_METADATA_SECURITY_OK = 0x00, + MONO_METADATA_INHERITANCEDEMAND_CLASS = 0x01, + MONO_METADATA_INHERITANCEDEMAND_METHOD = 0x02 +}; + +typedef enum { + MONO_SECURITY_MODE_NONE, + MONO_SECURITY_MODE_CORE_CLR, +} MonoSecurityMode; + +/* Structures */ + +typedef struct { + MonoClass *securitymanager; /* System.Security.SecurityManager */ +} MonoSecurityManager; + +gboolean mono_is_ecma_key (const char *publickey, int size); +MonoMethod* mono_get_context_capture_method (void); + +MonoSecurityManager* mono_security_manager_get_methods (void); + +/* Security mode */ +UNITY_MONO_API void mono_security_set_mode (MonoSecurityMode mode); +MonoSecurityMode mono_security_get_mode (void); + +/* internal calls */ +MonoBoolean ves_icall_System_Security_SecurityManager_get_SecurityEnabled (void); +void ves_icall_System_Security_SecurityManager_set_SecurityEnabled (MonoBoolean value); + +#ifndef DISABLE_SECURITY +#define mono_security_core_clr_enabled() (mono_security_get_mode () == MONO_SECURITY_MODE_CORE_CLR) +#else +#define mono_security_core_clr_enabled() (FALSE) +#endif + +#endif /* _MONO_METADATA_SECURITY_MANAGER_H_ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/security.h b/unity-2019.4.24f1-mbe/mono/metadata/security.h new file mode 100644 index 000000000..e6d57cff5 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/security.h @@ -0,0 +1,66 @@ +/** + * \file + * Security internal calls + * + * Author: + * Sebastien Pouliot + * + * (C) 2004 Novell (http://www.novell.com) + */ + + +#ifndef _MONO_METADATA_SECURITY_H_ +#define _MONO_METADATA_SECURITY_H_ + +#include +#include +#include +#include +#include +#include + +G_BEGIN_DECLS + +/* System.Environment */ +extern MonoStringHandle ves_icall_System_Environment_get_UserName (MonoError *error); + + +/* System.Security.Principal.WindowsIdentity */ +gpointer mono_security_principal_windows_identity_get_current_token (void); +extern MonoArray* ves_icall_System_Security_Principal_WindowsIdentity_GetRoles (gpointer token); +extern gpointer ves_icall_System_Security_Principal_WindowsIdentity_GetCurrentToken (MonoError *error); +extern MonoStringHandle ves_icall_System_Security_Principal_WindowsIdentity_GetTokenName (gpointer token, MonoError *error); +extern gpointer ves_icall_System_Security_Principal_WindowsIdentity_GetUserToken (MonoStringHandle username, MonoError *error); + + +/* System.Security.Principal.WindowsImpersonationContext */ +extern gboolean ves_icall_System_Security_Principal_WindowsImpersonationContext_CloseToken (gpointer token); +extern gpointer ves_icall_System_Security_Principal_WindowsImpersonationContext_DuplicateToken (gpointer token); +extern gboolean ves_icall_System_Security_Principal_WindowsImpersonationContext_SetCurrentToken (gpointer token); +extern gboolean ves_icall_System_Security_Principal_WindowsImpersonationContext_RevertToSelf (void); + + +/* System.Security.Principal.WindowsPrincipal */ +extern gboolean ves_icall_System_Security_Principal_WindowsPrincipal_IsMemberOfGroupId (gpointer user, gpointer group); +extern gboolean ves_icall_System_Security_Principal_WindowsPrincipal_IsMemberOfGroupName (gpointer user, MonoString *group); + + +/* Mono.Security.Cryptography.KeyPairPersistance */ +extern MonoBoolean ves_icall_Mono_Security_Cryptography_KeyPairPersistence_CanSecure (MonoString *root); +extern MonoBoolean ves_icall_Mono_Security_Cryptography_KeyPairPersistence_IsMachineProtected (MonoString *path); +extern MonoBoolean ves_icall_Mono_Security_Cryptography_KeyPairPersistence_IsUserProtected (MonoString *path); +extern MonoBoolean ves_icall_Mono_Security_Cryptography_KeyPairPersistence_ProtectMachine (MonoString *path); +extern MonoBoolean ves_icall_Mono_Security_Cryptography_KeyPairPersistence_ProtectUser (MonoString *path); + + +/* System.Security.Policy.Evidence */ +MonoBoolean ves_icall_System_Security_Policy_Evidence_IsAuthenticodePresent (MonoReflectionAssemblyHandle refass, MonoError *error); + +/* System.Security.SecureString */ +extern void ves_icall_System_Security_SecureString_DecryptInternal (MonoArray *data, MonoObject *scope); +extern void ves_icall_System_Security_SecureString_EncryptInternal (MonoArray *data, MonoObject *scope); +void invoke_protected_memory_method (MonoArray *data, MonoObject *scope, gboolean encrypt, MonoError *error); + +G_END_DECLS + +#endif /* _MONO_METADATA_SECURITY_H_ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/seq-points-data.c b/unity-2019.4.24f1-mbe/mono/metadata/seq-points-data.c new file mode 100644 index 000000000..7adbfe9f7 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/seq-points-data.c @@ -0,0 +1,505 @@ +/** + * \file + * Sequence Points functions + * + * Authors: + * Marcos Henrich (marcos.henrich@xamarin.com) + * + * Copyright 2015 Xamarin, Inc (http://www.xamarin.com) + */ + +#include "seq-points-data.h" + +typedef struct { + guint8 *data; + int len; + /* When has_debug_data is set to false only il and native deltas are saved */ + gboolean has_debug_data; + /* When alloc_data is set to true data allocation/deallocation is managed by this structure */ + gboolean alloc_data; +} SeqPointInfoInflated; + +static int +encode_var_int (guint8 *buf, guint8 **out_buf, int val) +{ + guint8 size = 0; + + do { + guint8 byte = val & 0x7f; + g_assert (size < 4 && "value has more than 28 bits"); + val >>= 7; + if(val) byte |= 0x80; + *(buf++) = byte; + size++; + } while (val); + + if (out_buf) + *out_buf = buf; + + return size; +} + +static int +decode_var_int (guint8* buf, guint8 **out_buf) +{ + guint8* p = buf; + + int low; + int b; + b = *(p++); low = (b & 0x7f) ; if(!(b & 0x80)) goto done; + b = *(p++); low |= (b & 0x7f) << 7; if(!(b & 0x80)) goto done; + b = *(p++); low |= (b & 0x7f) << 14; if(!(b & 0x80)) goto done; + b = *(p++); low |= (b & 0x7f) << 21; if(!(b & 0x80)) goto done; + + g_assert (FALSE && "value has more than 28 bits"); + +done: + + if (out_buf) + *out_buf = p; + + return low; +} + +static guint32 +encode_zig_zag (int val) +{ + return (val << 1) ^ (val >> 31); +} + +static int +decode_zig_zag (guint32 val) +{ + int n = val; + return (n >> 1) ^ (-(n & 1)); +} + +static SeqPointInfoInflated +seq_point_info_inflate (MonoSeqPointInfo *info) +{ + SeqPointInfoInflated info_inflated; + guint8 *ptr = (guint8*) info; + int value; + + value = decode_var_int (ptr, &ptr); + + info_inflated.len = value >> 2; + info_inflated.has_debug_data = (value & 1) != 0; + info_inflated.alloc_data = (value & 2) != 0; + + if (info_inflated.alloc_data) + info_inflated.data = ptr; + else + memcpy (&info_inflated.data, ptr, sizeof (guint8*)); + + return info_inflated; +} + +MonoSeqPointInfo* +mono_seq_point_info_new (int len, gboolean alloc_data, guint8 *data, gboolean has_debug_data, int *out_size) +{ + MonoSeqPointInfo *info; + guint8 *info_ptr; + guint8 buffer[4]; + int buffer_len; + int value; + int data_size; + + value = len << 2; + if (has_debug_data) + value |= 1; + if (alloc_data) + value |= 2; + + buffer_len = encode_var_int (buffer, NULL, value); + + *out_size = data_size = buffer_len + (alloc_data? len : sizeof (guint8*)); + info_ptr = g_new0 (guint8, data_size); + info = (MonoSeqPointInfo*) info_ptr; + + memcpy (info_ptr, buffer, buffer_len); + info_ptr += buffer_len; + + if (alloc_data) + memcpy (info_ptr, data, len); + else + memcpy (info_ptr, &data, sizeof (guint8*)); + + return info; +} + +void +mono_seq_point_info_free (gpointer ptr) +{ + MonoSeqPointInfo* info = (MonoSeqPointInfo*) ptr; + g_free (info); +} + +static int +seq_point_read (SeqPoint* seq_point, guint8* ptr, guint8* buffer_ptr, gboolean has_debug_data) +{ + int value, i; + guint8* ptr0 = ptr; + + value = decode_var_int (ptr, &ptr); + seq_point->il_offset += decode_zig_zag (value); + + value = decode_var_int (ptr, &ptr); + seq_point->native_offset += decode_zig_zag (value); + + if (has_debug_data) { + value = decode_var_int (ptr, &ptr); + seq_point->flags = value; + + if (seq_point->flags & MONO_SEQ_POINT_FLAG_EXIT_IL) + seq_point->il_offset = METHOD_EXIT_IL_OFFSET; + + value = decode_var_int (ptr, &ptr); + seq_point->next_len = value; + + if (seq_point->next_len) { + // store next offset and skip it + seq_point->next_offset = ptr - buffer_ptr; + for (i = 0; i < seq_point->next_len; ++i) + decode_var_int (ptr, &ptr); + } + } + + return ptr - ptr0; +} + +gboolean +mono_seq_point_info_add_seq_point (GByteArray* array, SeqPoint *sp, SeqPoint *last_seq_point, GSList *next, gboolean has_debug_data) +{ + int il_delta, native_delta; + GSList *l; + guint8 buffer[4]; + guint8 len; + int flags; + + if (!has_debug_data && + (sp->il_offset == METHOD_ENTRY_IL_OFFSET || sp->il_offset == METHOD_EXIT_IL_OFFSET)) + return FALSE; + + il_delta = sp->il_offset - last_seq_point->il_offset; + native_delta = sp->native_offset - last_seq_point->native_offset; + + flags = sp->flags; + + if (has_debug_data && sp->il_offset == METHOD_EXIT_IL_OFFSET) { + il_delta = 0; + flags |= MONO_SEQ_POINT_FLAG_EXIT_IL; + } + + len = encode_var_int (buffer, NULL, encode_zig_zag (il_delta)); + g_byte_array_append (array, buffer, len); + + len = encode_var_int (buffer, NULL, encode_zig_zag (native_delta)); + g_byte_array_append (array, buffer, len); + + if (has_debug_data) { + sp->next_offset = array->len; + sp->next_len = g_slist_length (next); + + len = encode_var_int (buffer, NULL, flags); + g_byte_array_append (array, buffer, len); + + len = encode_var_int (buffer, NULL, sp->next_len); + g_byte_array_append (array, buffer, len); + + for (l = next; l; l = l->next) { + int next_index = GPOINTER_TO_UINT (l->data); + guint8 buffer[4]; + int len = encode_var_int (buffer, NULL, next_index); + g_byte_array_append (array, buffer, len); + } + } + + return TRUE; +} + +gboolean +mono_seq_point_find_next_by_native_offset (MonoSeqPointInfo* info, int native_offset, SeqPoint* seq_point) +{ + SeqPointIterator it; + mono_seq_point_iterator_init (&it, info); + while (mono_seq_point_iterator_next (&it)) { + if (it.seq_point.native_offset >= native_offset) { + memcpy (seq_point, &it.seq_point, sizeof (SeqPoint)); + return TRUE; + } + } + + return FALSE; +} + +gboolean +mono_seq_point_find_prev_by_native_offset (MonoSeqPointInfo* info, int native_offset, SeqPoint* seq_point) +{ + SeqPoint prev_seq_point; + gboolean is_first = TRUE; + SeqPointIterator it; + mono_seq_point_iterator_init (&it, info); + while (mono_seq_point_iterator_next (&it) && it.seq_point.native_offset <= native_offset) { + memcpy (&prev_seq_point, &it.seq_point, sizeof (SeqPoint)); + is_first = FALSE; + } + + if (!is_first && prev_seq_point.native_offset <= native_offset) { + memcpy (seq_point, &prev_seq_point, sizeof (SeqPoint)); + return TRUE; + } + + return FALSE; +} + +gboolean +mono_seq_point_find_by_il_offset (MonoSeqPointInfo* info, int il_offset, SeqPoint* seq_point) +{ + SeqPointIterator it; + mono_seq_point_iterator_init (&it, info); + while (mono_seq_point_iterator_next (&it)) { + if (it.seq_point.il_offset == il_offset) { + memcpy (seq_point, &it.seq_point, sizeof (SeqPoint)); + return TRUE; + } + } + + return FALSE; +} + +void +mono_seq_point_init_next (MonoSeqPointInfo* info, SeqPoint sp, SeqPoint* next) +{ + int i; + guint8* ptr; + SeqPointIterator it; + GArray* seq_points = g_array_new (FALSE, TRUE, sizeof (SeqPoint)); + SeqPointInfoInflated info_inflated = seq_point_info_inflate (info); + + g_assert (info_inflated.has_debug_data); + + mono_seq_point_iterator_init (&it, info); + while (mono_seq_point_iterator_next (&it)) + g_array_append_vals (seq_points, &it.seq_point, 1); + + ptr = info_inflated.data + sp.next_offset; + for (i = 0; i < sp.next_len; i++) { + int next_index; + next_index = decode_var_int (ptr, &ptr); + g_assert (next_index < seq_points->len); + memcpy (&next[i], seq_points->data + next_index * sizeof (SeqPoint), sizeof (SeqPoint)); + } + + g_array_free (seq_points, TRUE); +} + +gboolean +mono_seq_point_iterator_next (SeqPointIterator* it) +{ + if (it->ptr >= it->end) + return FALSE; + + it->ptr += seq_point_read (&it->seq_point, it->ptr, it->begin, it->has_debug_data); + + return TRUE; +} + +void +mono_seq_point_iterator_init (SeqPointIterator* it, MonoSeqPointInfo* info) +{ + SeqPointInfoInflated info_inflated = seq_point_info_inflate (info); + it->ptr = info_inflated.data; + it->begin = info_inflated.data; + it->end = it->begin + info_inflated.len; + it->has_debug_data = info_inflated.has_debug_data; + memset(&it->seq_point, 0, sizeof(SeqPoint)); +} + +int +mono_seq_point_info_write (MonoSeqPointInfo* info, guint8* buffer) +{ + guint8* buffer0 = buffer; + SeqPointInfoInflated info_inflated = seq_point_info_inflate (info); + + encode_var_int (buffer, &buffer, info_inflated.has_debug_data); + + //Write sequence points + encode_var_int (buffer, &buffer, info_inflated.len); + memcpy (buffer, info_inflated.data, info_inflated.len); + buffer += info_inflated.len; + + return buffer - buffer0; +} + +int +mono_seq_point_info_read (MonoSeqPointInfo** info, guint8* buffer, gboolean copy) +{ + guint8* buffer0 = buffer; + int size, info_size; + gboolean has_debug_data; + + has_debug_data = decode_var_int (buffer, &buffer); + + size = decode_var_int (buffer, &buffer); + (*info) = mono_seq_point_info_new (size, copy, buffer, has_debug_data, &info_size); + buffer += size; + + return buffer - buffer0; +} + +/* + * Returns the maximum size of mono_seq_point_info_write. + */ +int +mono_seq_point_info_get_write_size (MonoSeqPointInfo* info) +{ + SeqPointInfoInflated info_inflated = seq_point_info_inflate (info); + + //4 is the maximum size required to store the size of the data. + //1 is the byte used to store has_debug_data. + int size = 4 + 1 + info_inflated.len; + + return size; +} + +/* + * SeqPointData struct and functions + * This is used to store/load/use sequence point from a file + */ + +void +mono_seq_point_data_init (SeqPointData *data, int entry_capacity) +{ + data->entry_count = 0; + data->entry_capacity = entry_capacity; + data->entries = (SeqPointDataEntry *)g_malloc (sizeof (SeqPointDataEntry) * entry_capacity); +} + +void +mono_seq_point_data_free (SeqPointData *data) +{ + int i; + for (i=0; ientry_count; i++) { + if (data->entries [i].free_seq_points) + g_free (data->entries [i].seq_points); + } + g_free (data->entries); +} + +gboolean +mono_seq_point_data_read (SeqPointData *data, char *path) +{ + guint8 *buffer, *buffer_orig; + int entry_count, i; + long fsize; + FILE *f; + + f = fopen (path, "r"); + if (!f) + return FALSE; + + fseek(f, 0, SEEK_END); + fsize = ftell(f); + fseek(f, 0, SEEK_SET); + + buffer_orig = buffer = (guint8 *)g_malloc (fsize + 1); + fread(buffer_orig, fsize, 1, f); + fclose(f); + + entry_count = decode_var_int (buffer, &buffer); + mono_seq_point_data_init (data, entry_count); + data->entry_count = entry_count; + + for (i=0; ientries [i].method_token = decode_var_int (buffer, &buffer); + data->entries [i].method_index = decode_var_int (buffer, &buffer); + buffer += mono_seq_point_info_read (&data->entries [i].seq_points, buffer, TRUE); + data->entries [i].free_seq_points = TRUE; + } + + g_free (buffer_orig); + return TRUE; +} + +gboolean +mono_seq_point_data_write (SeqPointData *data, char *path) +{ + guint8 *buffer, *buffer_orig; + FILE *f; + int i, size = 0; + + f = fopen (path, "w+"); + if (!f) + return FALSE; + + for (i=0; ientry_count; i++) { + size += mono_seq_point_info_get_write_size (data->entries [i].seq_points); + } + // Add size of entry_count and native_base_offsets + size += 4 + data->entry_count * 4; + + buffer_orig = buffer = (guint8 *)g_malloc (size); + + encode_var_int (buffer, &buffer, data->entry_count); + + for (i=0; ientry_count; i++) { + encode_var_int (buffer, &buffer, data->entries [i].method_token); + encode_var_int (buffer, &buffer, data->entries [i].method_index); + buffer += mono_seq_point_info_write (data->entries [i].seq_points, buffer); + } + + fwrite (buffer_orig, 1, buffer - buffer_orig, f); + g_free (buffer_orig); + fclose (f); + + return TRUE; +} + +void +mono_seq_point_data_add (SeqPointData *data, guint32 method_token, guint32 method_index, MonoSeqPointInfo* info) +{ + int i; + + g_assert (data->entry_count < data->entry_capacity); + i = data->entry_count++; + data->entries [i].seq_points = info; + data->entries [i].method_token = method_token; + data->entries [i].method_index = method_index; + data->entries [i].free_seq_points = FALSE; +} + +gboolean +mono_seq_point_data_get (SeqPointData *data, guint32 method_token, guint32 method_index, MonoSeqPointInfo** info) +{ + int i; + + for (i=0; ientry_count; i++) { + if (data->entries [i].method_token == method_token && (method_index == 0xffffff || data->entries [i].method_index == method_index)) { + (*info) = data->entries [i].seq_points; + return TRUE; + } + } + return FALSE; +} + +gboolean +mono_seq_point_data_get_il_offset (char *path, guint32 method_token, guint32 method_index, guint32 native_offset, guint32 *il_offset) +{ + SeqPointData sp_data; + MonoSeqPointInfo *seq_points; + SeqPoint sp; + + if (!mono_seq_point_data_read (&sp_data, path)) + return FALSE; + + if (!mono_seq_point_data_get (&sp_data, method_token, method_index, &seq_points)) + return FALSE; + + if (!mono_seq_point_find_prev_by_native_offset (seq_points, native_offset, &sp)) + return FALSE; + + *il_offset = sp.il_offset; + + return TRUE; +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/seq-points-data.h b/unity-2019.4.24f1-mbe/mono/metadata/seq-points-data.h new file mode 100644 index 000000000..7b24b9996 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/seq-points-data.h @@ -0,0 +1,119 @@ +/** + * \file + * Copyright 2015 Xamarin Inc + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#ifndef __MONO_SEQ_POINTS_DATA_H__ +#define __MONO_SEQ_POINTS_DATA_H__ + +#include + +#define MONO_SEQ_POINT_FLAG_NONEMPTY_STACK 1 +#define MONO_SEQ_POINT_FLAG_EXIT_IL 2 + +/* IL offsets used to mark the sequence points belonging to method entry/exit events */ +#define METHOD_ENTRY_IL_OFFSET -1 +#define METHOD_EXIT_IL_OFFSET 0xffffff + +#define SEQ_POINT_AOT_EXT ".msym" + +/* Native offset used to mark seq points in dead code */ +#define SEQ_POINT_NATIVE_OFFSET_DEAD_CODE -1 + +typedef struct { + int il_offset, native_offset, flags; + /* Offset of indexes of successor sequence points on the compressed buffer */ + int next_offset; + /* Number of entries in next */ + int next_len; +} SeqPoint; + +typedef struct MonoSeqPointInfo { + int dummy [1]; +} MonoSeqPointInfo; + +typedef struct { + SeqPoint seq_point; + guint8* ptr; + guint8* begin; + guint8* end; + gboolean has_debug_data; +} SeqPointIterator; + +void +mono_seq_point_info_free (gpointer info); + +gboolean +mono_seq_point_iterator_next (SeqPointIterator* it); + +void +mono_seq_point_iterator_init (SeqPointIterator* it, MonoSeqPointInfo* info); + +void +mono_seq_point_init_next (MonoSeqPointInfo* info, SeqPoint sp, SeqPoint* next); + +int +mono_seq_point_info_write (MonoSeqPointInfo* info, guint8* buffer); + +int +mono_seq_point_info_read (MonoSeqPointInfo** info, guint8* buffer, gboolean copy); + +int +mono_seq_point_info_get_write_size (MonoSeqPointInfo* info); + +gboolean +mono_seq_point_info_add_seq_point (GByteArray* array, SeqPoint *sp, SeqPoint *last_seq_point, GSList *next, gboolean has_debug_data); + +MonoSeqPointInfo* +mono_seq_point_info_new (int len, gboolean alloc_data, guint8 *data, gboolean has_debug_data, int *out_size); + +gboolean +mono_seq_point_find_prev_by_native_offset (MonoSeqPointInfo* info, int native_offset, SeqPoint* seq_point); + +gboolean +mono_seq_point_find_next_by_native_offset (MonoSeqPointInfo* info, int native_offset, SeqPoint* seq_point); + +gboolean +mono_seq_point_find_by_il_offset (MonoSeqPointInfo* info, int il_offset, SeqPoint* seq_point); + +/* + * SeqPointData struct and functions + * This is used to store/load/use sequence point from a file + */ + +typedef struct { + guint32 method_token; + guint32 method_index; + MonoSeqPointInfo* seq_points; + gboolean free_seq_points; +} SeqPointDataEntry; + +typedef struct { + SeqPointDataEntry* entries; + int entry_count; + int entry_capacity; +} SeqPointData; + +void +mono_seq_point_data_init (SeqPointData *data, int entry_capacity); + +void +mono_seq_point_data_free (SeqPointData *data); + +gboolean +mono_seq_point_data_read (SeqPointData *data, char *path); + +gboolean +mono_seq_point_data_write (SeqPointData *data, char *path); + +void +mono_seq_point_data_add (SeqPointData *data, guint32 methodToken, guint32 methodIndex, MonoSeqPointInfo* info); + +gboolean +mono_seq_point_data_get (SeqPointData *data, guint32 methodToken, guint32 methodIndex, MonoSeqPointInfo** info); + +gboolean +mono_seq_point_data_get_il_offset (char *path, guint32 methodToken, guint32 methodIndex, guint32 native_offset, guint32 *il_offset); + +#endif /* __MONO_SEQ_POINTS_DATA_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/sgen-bridge-internals.h b/unity-2019.4.24f1-mbe/mono/metadata/sgen-bridge-internals.h new file mode 100644 index 000000000..cf987d045 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/sgen-bridge-internals.h @@ -0,0 +1,78 @@ +/** + * \file + * The cross-GC bridge. + * + * Copyright (C) 2015 Xamarin Inc + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#ifndef __MONO_SGENBRIDGEINTERNAL_H__ +#define __MONO_SGENBRIDGEINTERNAL_H__ + +#include "config.h" + +#ifdef HAVE_SGEN_GC + +#include "mono/utils/mono-compiler.h" + +#include "mono/sgen/sgen-gc.h" +#include "mono/metadata/sgen-bridge.h" + +extern volatile gboolean bridge_processing_in_progress; +extern MonoGCBridgeCallbacks bridge_callbacks; + +gboolean sgen_need_bridge_processing (void); +void sgen_bridge_reset_data (void); +void sgen_bridge_processing_stw_step (void); +void sgen_bridge_processing_finish (int generation); +gboolean sgen_is_bridge_object (GCObject *obj); +MonoGCBridgeObjectKind sgen_bridge_class_kind (MonoClass *klass); +void sgen_bridge_register_finalized_object (GCObject *object); +void sgen_bridge_describe_pointer (GCObject *object); + +gboolean sgen_is_bridge_object (GCObject *obj); +void sgen_mark_bridge_object (GCObject *obj); + +gboolean sgen_bridge_handle_gc_param (const char *opt); +gboolean sgen_bridge_handle_gc_debug (const char *opt); +void sgen_bridge_print_gc_debug_usage (void); + +typedef struct { + char *dump_prefix; + gboolean accounting; + gboolean scc_precise_merge; // Used by Tarjan +} SgenBridgeProcessorConfig; + +typedef struct { + void (*reset_data) (void); + void (*processing_stw_step) (void); + void (*processing_build_callback_data) (int generation); + void (*processing_after_callback) (int generation); + MonoGCBridgeObjectKind (*class_kind) (MonoClass *klass); + void (*register_finalized_object) (GCObject *object); + void (*describe_pointer) (GCObject *object); + + /* Should be called once, immediately after init */ + void (*set_config) (const SgenBridgeProcessorConfig *); + + /* + * These are set by processing_build_callback_data(). + */ + int num_sccs; + MonoGCBridgeSCC **api_sccs; + + int num_xrefs; + MonoGCBridgeXRef *api_xrefs; +} SgenBridgeProcessor; + +void sgen_old_bridge_init (SgenBridgeProcessor *collector); +void sgen_new_bridge_init (SgenBridgeProcessor *collector); +void sgen_tarjan_bridge_init (SgenBridgeProcessor *collector); +void sgen_set_bridge_implementation (const char *name); +void sgen_bridge_set_dump_prefix (const char *prefix); +void sgen_init_bridge (void); + +#endif + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/sgen-bridge.c b/unity-2019.4.24f1-mbe/mono/metadata/sgen-bridge.c new file mode 100644 index 000000000..9d7809a21 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/sgen-bridge.c @@ -0,0 +1,727 @@ +/** + * \file + * Simple generational GC. + * + * Copyright 2011 Novell, Inc (http://www.novell.com) + * Copyright 2011 Xamarin Inc (http://www.xamarin.com) + * Copyright 2001-2003 Ximian, Inc + * Copyright 2003-2010 Novell, Inc. + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include "config.h" + +#ifdef HAVE_SGEN_GC + +#include + +#include "sgen/sgen-gc.h" +#include "sgen-bridge-internals.h" +#include "sgen/sgen-hash-table.h" +#include "sgen/sgen-qsort.h" +#include "utils/mono-logger-internals.h" + +typedef enum { + BRIDGE_PROCESSOR_INVALID, + BRIDGE_PROCESSOR_OLD, + BRIDGE_PROCESSOR_NEW, + BRIDGE_PROCESSOR_TARJAN, + BRIDGE_PROCESSOR_DEFAULT = BRIDGE_PROCESSOR_TARJAN +} BridgeProcessorSelection; + +// Bridge processor type pending / in use +static BridgeProcessorSelection bridge_processor_selection = BRIDGE_PROCESSOR_DEFAULT; +// Most recently requested callbacks +static MonoGCBridgeCallbacks pending_bridge_callbacks; +// Configuration to be passed to bridge processor after init +static SgenBridgeProcessorConfig bridge_processor_config; +// Currently-in-use callbacks +MonoGCBridgeCallbacks bridge_callbacks; + +// Bridge processor state +static SgenBridgeProcessor bridge_processor; +// This is used for a special debug feature +static SgenBridgeProcessor compare_to_bridge_processor; + +volatile gboolean bridge_processing_in_progress = FALSE; + +// FIXME: The current usage pattern for this function is unsafe. Bridge processing could start immediately after unlock +/** + * mono_gc_wait_for_bridge_processing: + */ +void +mono_gc_wait_for_bridge_processing (void) +{ + if (!bridge_processing_in_progress) + return; + + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_BRIDGE waiting for bridge processing to finish"); + + sgen_gc_lock (); + sgen_gc_unlock (); +} + +/** + * mono_gc_register_bridge_callbacks: + */ +void +mono_gc_register_bridge_callbacks (MonoGCBridgeCallbacks *callbacks) +{ + if (callbacks->bridge_version != SGEN_BRIDGE_VERSION) + g_error ("Invalid bridge callback version. Expected %d but got %d\n", SGEN_BRIDGE_VERSION, callbacks->bridge_version); + + // Defer assigning to bridge_callbacks until we have the gc lock. + // Note: This line is unsafe if we are on a separate thread from the one the runtime was initialized on. + pending_bridge_callbacks = *callbacks; + + // If sgen has started, will assign bridge callbacks and init bridge + sgen_init_bridge (); +} + +static BridgeProcessorSelection +bridge_processor_name (const char *name) +{ + if (!strcmp ("old", name)) { + return BRIDGE_PROCESSOR_OLD; + } else if (!strcmp ("new", name)) { + return BRIDGE_PROCESSOR_NEW; + } else if (!strcmp ("tarjan", name)) { + return BRIDGE_PROCESSOR_TARJAN; + } else { + return BRIDGE_PROCESSOR_INVALID; + } +} + +static gboolean +bridge_processor_started (void) +{ + return bridge_processor.reset_data != NULL; +} + +// Initialize a single bridge processor +static void +init_bridge_processor (SgenBridgeProcessor *processor, BridgeProcessorSelection selection) +{ + memset (processor, 0, sizeof (SgenBridgeProcessor)); + + switch (selection) { + case BRIDGE_PROCESSOR_OLD: + sgen_old_bridge_init (processor); + break; + case BRIDGE_PROCESSOR_NEW: + sgen_new_bridge_init (processor); + break; + case BRIDGE_PROCESSOR_TARJAN: + sgen_tarjan_bridge_init (processor); + break; + default: + g_assert_not_reached (); + } +} + +/* + * Initializing the sgen bridge consists of setting the bridge callbacks, + * and initializing the bridge processor. Init should follow these rules: + * + * - Init happens only after sgen is initialized (because we don't + * know which bridge processor to initialize until then, and also + * to allow bridge processor init to interact with sgen if it wants) + * + * - Init happens only after mono_gc_register_bridge_callbacks is called + * + * - Init should not happen concurrently with a GC (because a GC will + * call sgen_need_bridge_processing at various times) + * + * - Initializing the bridge processor should happen only once + * + * We call sgen_init_bridge when the callbacks are set, and also when sgen + * is done initing. Actual initialization then only occurs if it is ready. + */ +void +sgen_init_bridge (void) +{ + if (sgen_gc_initialized ()) { + // This lock is not initialized until the GC is + sgen_gc_lock (); + + bridge_callbacks = pending_bridge_callbacks; + + // If a bridge was registered but there is no bridge processor yet + if (bridge_callbacks.cross_references && !bridge_processor_started ()) { + init_bridge_processor (&bridge_processor, bridge_processor_selection); + + if (bridge_processor.set_config) + bridge_processor.set_config (&bridge_processor_config); + + // Config is no longer needed so free its memory + free (bridge_processor_config.dump_prefix); + bridge_processor_config.dump_prefix = NULL; + } + + sgen_gc_unlock (); + } +} + +void +sgen_set_bridge_implementation (const char *name) +{ + BridgeProcessorSelection selection = bridge_processor_name (name); + + if (selection == BRIDGE_PROCESSOR_INVALID) + g_warning ("Invalid value for bridge processor implementation, valid values are: 'new', 'old' and 'tarjan'."); + else if (bridge_processor_started ()) + g_warning ("Cannot set bridge processor implementation once bridge has already started"); + else + bridge_processor_selection = selection; +} + +gboolean +sgen_is_bridge_object (GCObject *obj) +{ + if ((obj->vtable->gc_bits & SGEN_GC_BIT_BRIDGE_OBJECT) != SGEN_GC_BIT_BRIDGE_OBJECT) + return FALSE; + return bridge_callbacks.is_bridge_object (obj); +} + +gboolean +sgen_need_bridge_processing (void) +{ + return bridge_callbacks.cross_references != NULL; +} + +static gboolean +compare_bridge_processors (void) +{ + return compare_to_bridge_processor.reset_data != NULL; +} + +/* Dispatch wrappers */ +void +sgen_bridge_reset_data (void) +{ + bridge_processor.reset_data (); + if (compare_bridge_processors ()) + compare_to_bridge_processor.reset_data (); +} + +void +sgen_bridge_processing_stw_step (void) +{ + /* + * bridge_processing_in_progress must be set with the world + * stopped. If not there would be race conditions. + */ + bridge_processing_in_progress = TRUE; + + bridge_processor.processing_stw_step (); + if (compare_bridge_processors ()) + compare_to_bridge_processor.processing_stw_step (); +} + +static gboolean +is_bridge_object_dead (GCObject *obj, void *data) +{ + SgenHashTable *table = (SgenHashTable *)data; + unsigned char *value = (unsigned char *)sgen_hash_table_lookup (table, obj); + if (!value) + return FALSE; + return !*value; +} + +static void +null_weak_links_to_dead_objects (SgenBridgeProcessor *processor, int generation) +{ + int i, j; + int num_sccs = processor->num_sccs; + MonoGCBridgeSCC **api_sccs = processor->api_sccs; + SgenHashTable alive_hash = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_ALIVE_HASH_TABLE, INTERNAL_MEM_BRIDGE_ALIVE_HASH_TABLE_ENTRY, 1, mono_aligned_addr_hash, NULL); + + for (i = 0; i < num_sccs; ++i) { + unsigned char alive = api_sccs [i]->is_alive ? 1 : 0; + for (j = 0; j < api_sccs [i]->num_objs; ++j) { + /* Build hash table for nulling weak links. */ + sgen_hash_table_replace (&alive_hash, api_sccs [i]->objs [j], &alive, NULL); + + /* Release for finalization those objects we no longer care. */ + if (!api_sccs [i]->is_alive) + sgen_mark_bridge_object (api_sccs [i]->objs [j]); + } + } + + /* Null weak links to dead objects. */ + sgen_null_links_if (is_bridge_object_dead, &alive_hash, GENERATION_NURSERY, FALSE); + sgen_null_links_if (is_bridge_object_dead, &alive_hash, GENERATION_NURSERY, TRUE); + if (generation == GENERATION_OLD) { + sgen_null_links_if (is_bridge_object_dead, &alive_hash, GENERATION_OLD, FALSE); + sgen_null_links_if (is_bridge_object_dead, &alive_hash, GENERATION_OLD, TRUE); + } + + sgen_hash_table_clean (&alive_hash); +} + +static void +free_callback_data (SgenBridgeProcessor *processor) +{ + int i; + int num_sccs = processor->num_sccs; + int num_xrefs = processor->num_xrefs; + MonoGCBridgeSCC **api_sccs = processor->api_sccs; + MonoGCBridgeXRef *api_xrefs = processor->api_xrefs; + + for (i = 0; i < num_sccs; ++i) { + sgen_free_internal_dynamic (api_sccs [i], + sizeof (MonoGCBridgeSCC) + sizeof (MonoObject*) * api_sccs [i]->num_objs, + INTERNAL_MEM_BRIDGE_DATA); + } + sgen_free_internal_dynamic (api_sccs, sizeof (MonoGCBridgeSCC*) * num_sccs, INTERNAL_MEM_BRIDGE_DATA); + + sgen_free_internal_dynamic (api_xrefs, sizeof (MonoGCBridgeXRef) * num_xrefs, INTERNAL_MEM_BRIDGE_DATA); + + processor->num_sccs = 0; + processor->api_sccs = NULL; + processor->num_xrefs = 0; + processor->api_xrefs = NULL; +} + +static int +compare_xrefs (const void *a_ptr, const void *b_ptr) +{ + const MonoGCBridgeXRef *a = (const MonoGCBridgeXRef *)a_ptr; + const MonoGCBridgeXRef *b = (const MonoGCBridgeXRef *)b_ptr; + + if (a->src_scc_index < b->src_scc_index) + return -1; + if (a->src_scc_index > b->src_scc_index) + return 1; + + if (a->dst_scc_index < b->dst_scc_index) + return -1; + if (a->dst_scc_index > b->dst_scc_index) + return 1; + + return 0; +} + +/* +static void +dump_processor_state (SgenBridgeProcessor *p) +{ + int i; + + printf ("------\n"); + printf ("SCCS %d\n", p->num_sccs); + for (i = 0; i < p->num_sccs; ++i) { + int j; + MonoGCBridgeSCC *scc = p->api_sccs [i]; + printf ("\tSCC %d:", i); + for (j = 0; j < scc->num_objs; ++j) { + MonoObject *obj = scc->objs [j]; + printf (" %p", obj); + } + printf ("\n"); + } + + printf ("XREFS %d\n", p->num_xrefs); + for (i = 0; i < p->num_xrefs; ++i) + printf ("\t%d -> %d\n", p->api_xrefs [i].src_scc_index, p->api_xrefs [i].dst_scc_index); + + printf ("-------\n"); +} +*/ + +static gboolean +sgen_compare_bridge_processor_results (SgenBridgeProcessor *a, SgenBridgeProcessor *b) +{ + int i; + SgenHashTable obj_to_a_scc = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_DEBUG, INTERNAL_MEM_BRIDGE_DEBUG, sizeof (int), mono_aligned_addr_hash, NULL); + SgenHashTable b_scc_to_a_scc = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_DEBUG, INTERNAL_MEM_BRIDGE_DEBUG, sizeof (int), g_direct_hash, NULL); + MonoGCBridgeXRef *a_xrefs, *b_xrefs; + size_t xrefs_alloc_size; + + // dump_processor_state (a); + // dump_processor_state (b); + + if (a->num_sccs != b->num_sccs) + g_error ("SCCS count expected %d but got %d", a->num_sccs, b->num_sccs); + if (a->num_xrefs != b->num_xrefs) + g_error ("SCCS count expected %d but got %d", a->num_xrefs, b->num_xrefs); + + /* + * First we build a hash of each object in `a` to its respective SCC index within + * `a`. Along the way we also assert that no object is more than one SCC. + */ + for (i = 0; i < a->num_sccs; ++i) { + int j; + MonoGCBridgeSCC *scc = a->api_sccs [i]; + + g_assert (scc->num_objs > 0); + + for (j = 0; j < scc->num_objs; ++j) { + GCObject *obj = scc->objs [j]; + gboolean new_entry = sgen_hash_table_replace (&obj_to_a_scc, obj, &i, NULL); + g_assert (new_entry); + } + } + + /* + * Now we check whether each of the objects in `b` are in `a`, and whether the SCCs + * of `b` contain the same sets of objects as those of `a`. + * + * While we're doing this, build a hash table to map from `b` SCC indexes to `a` SCC + * indexes. + */ + for (i = 0; i < b->num_sccs; ++i) { + MonoGCBridgeSCC *scc = b->api_sccs [i]; + MonoGCBridgeSCC *a_scc; + int *a_scc_index_ptr; + int a_scc_index; + int j; + gboolean new_entry; + + g_assert (scc->num_objs > 0); + a_scc_index_ptr = (int *)sgen_hash_table_lookup (&obj_to_a_scc, scc->objs [0]); + g_assert (a_scc_index_ptr); + a_scc_index = *a_scc_index_ptr; + + //g_print ("A SCC %d -> B SCC %d\n", a_scc_index, i); + + a_scc = a->api_sccs [a_scc_index]; + g_assert (a_scc->num_objs == scc->num_objs); + + for (j = 1; j < scc->num_objs; ++j) { + a_scc_index_ptr = (int *)sgen_hash_table_lookup (&obj_to_a_scc, scc->objs [j]); + g_assert (a_scc_index_ptr); + g_assert (*a_scc_index_ptr == a_scc_index); + } + + new_entry = sgen_hash_table_replace (&b_scc_to_a_scc, GINT_TO_POINTER (i), &a_scc_index, NULL); + g_assert (new_entry); + } + + /* + * Finally, check that we have the same xrefs. We do this by making copies of both + * xref arrays, and replacing the SCC indexes in the copy for `b` with the + * corresponding indexes in `a`. Then we sort both arrays and assert that they're + * the same. + * + * At the same time, check that no xref is self-referential and that there are no + * duplicate ones. + */ + + xrefs_alloc_size = a->num_xrefs * sizeof (MonoGCBridgeXRef); + a_xrefs = (MonoGCBridgeXRef *)sgen_alloc_internal_dynamic (xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG, TRUE); + b_xrefs = (MonoGCBridgeXRef *)sgen_alloc_internal_dynamic (xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG, TRUE); + + memcpy (a_xrefs, a->api_xrefs, xrefs_alloc_size); + for (i = 0; i < b->num_xrefs; ++i) { + MonoGCBridgeXRef *xref = &b->api_xrefs [i]; + int *scc_index_ptr; + + g_assert (xref->src_scc_index != xref->dst_scc_index); + + scc_index_ptr = (int *)sgen_hash_table_lookup (&b_scc_to_a_scc, GINT_TO_POINTER (xref->src_scc_index)); + g_assert (scc_index_ptr); + b_xrefs [i].src_scc_index = *scc_index_ptr; + + scc_index_ptr = (int *)sgen_hash_table_lookup (&b_scc_to_a_scc, GINT_TO_POINTER (xref->dst_scc_index)); + g_assert (scc_index_ptr); + b_xrefs [i].dst_scc_index = *scc_index_ptr; + } + + qsort (a_xrefs, a->num_xrefs, sizeof (MonoGCBridgeXRef), compare_xrefs); + qsort (b_xrefs, a->num_xrefs, sizeof (MonoGCBridgeXRef), compare_xrefs); + + for (i = 0; i < a->num_xrefs; ++i) { + g_assert (a_xrefs [i].src_scc_index == b_xrefs [i].src_scc_index); + g_assert (a_xrefs [i].dst_scc_index == b_xrefs [i].dst_scc_index); + } + + sgen_hash_table_clean (&obj_to_a_scc); + sgen_hash_table_clean (&b_scc_to_a_scc); + sgen_free_internal_dynamic (a_xrefs, xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG); + sgen_free_internal_dynamic (b_xrefs, xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG); + + return TRUE; +} + +void +sgen_bridge_processing_finish (int generation) +{ + bridge_processor.processing_build_callback_data (generation); + if (compare_bridge_processors ()) + compare_to_bridge_processor.processing_build_callback_data (generation); + + if (bridge_processor.num_sccs == 0) { + g_assert (bridge_processor.num_xrefs == 0); + goto after_callback; + } + + bridge_callbacks.cross_references (bridge_processor.num_sccs, bridge_processor.api_sccs, + bridge_processor.num_xrefs, bridge_processor.api_xrefs); + + if (compare_bridge_processors ()) + sgen_compare_bridge_processor_results (&bridge_processor, &compare_to_bridge_processor); + + null_weak_links_to_dead_objects (&bridge_processor, generation); + + free_callback_data (&bridge_processor); + if (compare_bridge_processors ()) + free_callback_data (&compare_to_bridge_processor); + + after_callback: + bridge_processor.processing_after_callback (generation); + if (compare_bridge_processors ()) + compare_to_bridge_processor.processing_after_callback (generation); + + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_BRIDGE: Complete, was running for %.2fms", mono_time_since_last_stw () / 10000.0f); + + bridge_processing_in_progress = FALSE; +} + +MonoGCBridgeObjectKind +sgen_bridge_class_kind (MonoClass *klass) +{ + return bridge_processor.class_kind (klass); +} + +void +sgen_bridge_register_finalized_object (GCObject *obj) +{ + bridge_processor.register_finalized_object (obj); + if (compare_bridge_processors ()) + compare_to_bridge_processor.register_finalized_object (obj); +} + +void +sgen_bridge_describe_pointer (GCObject *obj) +{ + if (bridge_processor.describe_pointer) + bridge_processor.describe_pointer (obj); +} + +static void +set_dump_prefix (const char *prefix) +{ + if (bridge_processor_config.dump_prefix) + free (bridge_processor_config.dump_prefix); + bridge_processor_config.dump_prefix = strdup (prefix); +} + +/* Test support code */ +static const char *bridge_class; + +static MonoGCBridgeObjectKind +bridge_test_bridge_class_kind (MonoClass *klass) +{ + if (!strcmp (bridge_class, klass->name)) + return GC_BRIDGE_TRANSPARENT_BRIDGE_CLASS; + return GC_BRIDGE_TRANSPARENT_CLASS; +} + +static gboolean +bridge_test_is_bridge_object (MonoObject *object) +{ + return TRUE; +} + +static void +bridge_test_cross_reference (int num_sccs, MonoGCBridgeSCC **sccs, int num_xrefs, MonoGCBridgeXRef *xrefs) +{ + int i; + for (i = 0; i < num_sccs; ++i) { + int j; + // g_print ("--- SCC %d\n", i); + for (j = 0; j < sccs [i]->num_objs; ++j) { + // g_print (" %s\n", sgen_safe_name (sccs [i]->objs [j])); + if (i & 1) /*retain half of the bridged objects */ + sccs [i]->is_alive = TRUE; + } + } + for (i = 0; i < num_xrefs; ++i) { + g_assert (xrefs [i].src_scc_index >= 0 && xrefs [i].src_scc_index < num_sccs); + g_assert (xrefs [i].dst_scc_index >= 0 && xrefs [i].dst_scc_index < num_sccs); + // g_print ("%d -> %d\n", xrefs [i].src_scc_index, xrefs [i].dst_scc_index); + } +} + +static MonoClassField *mono_bridge_test_field; + +enum { + BRIDGE_DEAD, + BRIDGE_ROOT, + BRIDGE_SAME_SCC, + BRIDGE_XREF, +}; + +static gboolean +test_scc (MonoGCBridgeSCC *scc, int i) +{ + int status = BRIDGE_DEAD; + mono_field_get_value (scc->objs [i], mono_bridge_test_field, &status); + return status > 0; +} + +static void +mark_scc (MonoGCBridgeSCC *scc, int value) +{ + int i; + for (i = 0; i < scc->num_objs; ++i) { + if (!test_scc (scc, i)) { + int status = value; + mono_field_set_value (scc->objs [i], mono_bridge_test_field, &status); + } + } +} + +static void +bridge_test_cross_reference2 (int num_sccs, MonoGCBridgeSCC **sccs, int num_xrefs, MonoGCBridgeXRef *xrefs) +{ + int i; + gboolean modified; + + if (!mono_bridge_test_field) { + mono_bridge_test_field = mono_class_get_field_from_name (mono_object_get_class (sccs[0]->objs [0]), "__test"); + g_assert (mono_bridge_test_field); + } + + /*We mark all objects in a scc with live objects as reachable by scc*/ + for (i = 0; i < num_sccs; ++i) { + int j; + gboolean live = FALSE; + for (j = 0; j < sccs [i]->num_objs; ++j) { + if (test_scc (sccs [i], j)) { + live = TRUE; + break; + } + } + if (!live) + continue; + for (j = 0; j < sccs [i]->num_objs; ++j) { + if (!test_scc (sccs [i], j)) { + int status = BRIDGE_SAME_SCC; + mono_field_set_value (sccs [i]->objs [j], mono_bridge_test_field, &status); + } + } + } + + /*Now we mark the transitive closure of reachable objects from the xrefs*/ + modified = TRUE; + while (modified) { + modified = FALSE; + /* Mark all objects that are brought to life due to xrefs*/ + for (i = 0; i < num_xrefs; ++i) { + MonoGCBridgeXRef ref = xrefs [i]; + if (test_scc (sccs [ref.src_scc_index], 0) && !test_scc (sccs [ref.dst_scc_index], 0)) { + modified = TRUE; + mark_scc (sccs [ref.dst_scc_index], BRIDGE_XREF); + } + } + } + + /* keep everything in memory, all we want to do is test persistence */ + for (i = 0; i < num_sccs; ++i) + sccs [i]->is_alive = TRUE; +} + +/* This bridge keeps all peers with __test > 0 */ +static void +bridge_test_positive_status (int num_sccs, MonoGCBridgeSCC **sccs, int num_xrefs, MonoGCBridgeXRef *xrefs) +{ + int i; + + if (!mono_bridge_test_field) { + mono_bridge_test_field = mono_class_get_field_from_name (mono_object_get_class (sccs[0]->objs [0]), "__test"); + g_assert (mono_bridge_test_field); + } + + /*We mark all objects in a scc with live objects as reachable by scc*/ + for (i = 0; i < num_sccs; ++i) { + int j; + for (j = 0; j < sccs [i]->num_objs; ++j) { + if (test_scc (sccs [i], j)) { + sccs [i]->is_alive = TRUE; + break; + } + } + } +} + + +static void +register_test_bridge_callbacks (const char *bridge_class_name) +{ + MonoGCBridgeCallbacks callbacks; + callbacks.bridge_version = SGEN_BRIDGE_VERSION; + callbacks.bridge_class_kind = bridge_test_bridge_class_kind; + callbacks.is_bridge_object = bridge_test_is_bridge_object; + + switch (bridge_class_name [0]) { + case '2': + bridge_class = bridge_class_name + 1; + callbacks.cross_references = bridge_test_cross_reference2; + break; + case '3': + bridge_class = bridge_class_name + 1; + callbacks.cross_references = bridge_test_positive_status; + break; + default: + bridge_class = bridge_class_name; + callbacks.cross_references = bridge_test_cross_reference; + } + mono_gc_register_bridge_callbacks (&callbacks); +} + +gboolean +sgen_bridge_handle_gc_param (const char *opt) +{ + g_assert (!bridge_processor_started ()); + + if (!strcmp (opt, "bridge-require-precise-merge")) { + bridge_processor_config.scc_precise_merge = TRUE; + } else { + return FALSE; + } + + return TRUE; +} + +gboolean +sgen_bridge_handle_gc_debug (const char *opt) +{ + g_assert (!bridge_processor_started ()); + + if (g_str_has_prefix (opt, "bridge=")) { + opt = strchr (opt, '=') + 1; + register_test_bridge_callbacks (g_strdup (opt)); + } else if (!strcmp (opt, "enable-bridge-accounting")) { + bridge_processor_config.accounting = TRUE; + } else if (g_str_has_prefix (opt, "bridge-dump=")) { + char *prefix = strchr (opt, '=') + 1; + set_dump_prefix(prefix); + } else if (g_str_has_prefix (opt, "bridge-compare-to=")) { + const char *name = strchr (opt, '=') + 1; + BridgeProcessorSelection selection = bridge_processor_name (name); + + if (selection != BRIDGE_PROCESSOR_INVALID) { + // Compare processor doesn't get config + init_bridge_processor (&compare_to_bridge_processor, selection); + } else { + g_warning ("Invalid bridge implementation to compare against - ignoring."); + } + } else { + return FALSE; + } + return TRUE; +} + +void +sgen_bridge_print_gc_debug_usage (void) +{ + fprintf (stderr, " bridge=\n"); + fprintf (stderr, " enable-bridge-accounting\n"); + fprintf (stderr, " bridge-dump=\n"); + fprintf (stderr, " bridge-compare-to=\n"); +} + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/sgen-bridge.h b/unity-2019.4.24f1-mbe/mono/metadata/sgen-bridge.h new file mode 100644 index 000000000..c131f3066 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/sgen-bridge.h @@ -0,0 +1,110 @@ +/** + * \file + * Copyright 2011 Novell, Inc. + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +/* + * The bridge is a mechanism for SGen to let clients override the death of some + * unreachable objects. We use it in monodroid to do garbage collection across + * the Mono and Java heaps. + * + * The client (Monodroid) can designate some objects as "bridged", which means + * that they participate in the bridge processing step once SGen considers them + * unreachable, i.e., dead. Bridged objects must be registered for + * finalization. + * + * When SGen is done marking, it puts together a list of all dead bridged + * objects. This is passed to the bridge processor, which does an analysis to + * simplify the graph: It replaces strongly-connected components with single + * nodes, and may remove nodes corresponding to components which do not contain + * bridged objects. + * + * The output of the SCC analysis is passed to the client's `cross_references()` + * callback. This consists of 2 arrays, an array of SCCs (MonoGCBridgeSCC), + * and an array of "xrefs" (edges between SCCs, MonoGCBridgeXRef). Edges are + * encoded as pairs of "API indices", ie indexes in the SCC array. The client + * is expected to set the `is_alive` flag on those strongly connected components + * that it wishes to be kept alive. + * + * In monodroid each bridged object has a corresponding Java mirror object. In + * the bridge callback it reifies the Mono object graph in the Java heap so that + * the full, combined object graph is now instantiated on the Java side. Then + * it triggers a Java GC, waits for it to finish, and checks which of the Java + * mirror objects are still alive. For those it sets the `is_alive` flag and + * returns from the callback. + * + * The SCC analysis is done while the world is stopped, but the callback is made + * with the world running again. Weak links to bridged objects and other + * objects reachable from them are kept until the callback returns, at which + * point all links to bridged objects that don't have `is_alive` set are nulled. + * Note that weak links to non-bridged objects reachable from bridged objects + * are not nulled. This might be considered a bug. + * + * There are three different implementations of the bridge processor, each of + * which implements 8 callbacks (see SgenBridgeProcessor). The implementations + * differ in the algorithm they use to compute the "simplified" SCC graph. + */ + +#ifndef _MONO_SGEN_BRIDGE_H_ +#define _MONO_SGEN_BRIDGE_H_ + +#include + +MONO_BEGIN_DECLS + +enum { + SGEN_BRIDGE_VERSION = 5 +}; + +typedef enum { + /* Instances of this class should be scanned when computing the transitive dependency among bridges. E.g. List*/ + GC_BRIDGE_TRANSPARENT_CLASS, + /* Instances of this class should not be scanned when computing the transitive dependency among bridges. E.g. String*/ + GC_BRIDGE_OPAQUE_CLASS, + /* Instances of this class should be bridged and have their dependency computed. */ + GC_BRIDGE_TRANSPARENT_BRIDGE_CLASS, + /* Instances of this class should be bridged but no dependencies should not be calculated. */ + GC_BRIDGE_OPAQUE_BRIDGE_CLASS, +} MonoGCBridgeObjectKind; + +typedef struct { + mono_bool is_alive; /* to be set by the cross reference callback */ + int num_objs; + MonoObject *objs [MONO_ZERO_LEN_ARRAY]; +} MonoGCBridgeSCC; + +typedef struct { + int src_scc_index; + int dst_scc_index; +} MonoGCBridgeXRef; + +typedef struct { + int bridge_version; + /* + * Tells the runtime which classes to even consider when looking for + * bridged objects. If subclasses are to be considered as well, the + * subclass check must be done in the callback. + */ + MonoGCBridgeObjectKind (*bridge_class_kind) (MonoClass *klass); + /* + * This is only called on objects for whose classes + * `bridge_class_kind()` returned `XXX_BRIDGE_CLASS`. + */ + mono_bool (*is_bridge_object) (MonoObject *object); + void (*cross_references) (int num_sccs, MonoGCBridgeSCC **sccs, int num_xrefs, MonoGCBridgeXRef *xrefs); +} MonoGCBridgeCallbacks; + +/* + * Note: This may be called at any time, but cannot be called concurrently + * with (during and on a separate thread from) sgen init. Callers are + * responsible for enforcing this. + */ +MONO_API void mono_gc_register_bridge_callbacks (MonoGCBridgeCallbacks *callbacks); + +MONO_API void mono_gc_wait_for_bridge_processing (void); + +MONO_END_DECLS + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/sgen-client-mono.h b/unity-2019.4.24f1-mbe/mono/metadata/sgen-client-mono.h new file mode 100644 index 000000000..981e6e9a9 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/sgen-client-mono.h @@ -0,0 +1,738 @@ +/** + * \file + * Mono's client definitions for SGen. + * + * Copyright (C) 2014 Xamarin Inc + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#ifdef SGEN_DEFINE_OBJECT_VTABLE + +#include "sgen/sgen-archdep.h" +#include "utils/mono-threads.h" +#include "utils/mono-mmap.h" +#include "metadata/object-internals.h" + +typedef MonoObject GCObject; +typedef MonoVTable* GCVTable; + +static inline GCVTable +SGEN_LOAD_VTABLE_UNCHECKED (GCObject *obj) +{ + return obj->vtable; +} + +static inline SgenDescriptor +sgen_vtable_get_descriptor (GCVTable vtable) +{ + return (SgenDescriptor)vtable->gc_descr; +} + +typedef struct _SgenClientThreadInfo SgenClientThreadInfo; +struct _SgenClientThreadInfo { + MonoThreadInfo info; + + /* + * `skip` is set to TRUE when STW fails to suspend a thread, most probably because + * the underlying thread is dead. + */ + gboolean skip, suspend_done; + volatile int in_critical_region; + + /* + This is set the argument of mono_gc_set_skip_thread. + + A thread that knowingly holds no managed state can call this + function around blocking loops to reduce the GC burden by not + been scanned. + */ + gboolean gc_disabled; + +#ifdef SGEN_POSIX_STW + /* This is -1 until the first suspend. */ + int signal; + /* FIXME: kill this, we only use signals on systems that have rt-posix, which doesn't have issues with duplicates. */ + unsigned int stop_count; /* to catch duplicate signals. */ +#endif + + gpointer runtime_data; + + void *stack_end; + void *stack_start; + void *stack_start_limit; + + MonoContext ctx; /* ditto */ +}; + +#else + +#include "metadata/profiler-private.h" +#include "utils/dtrace.h" +#include "utils/mono-counters.h" +#include "utils/mono-logger-internals.h" +#include "utils/mono-time.h" +#include "utils/mono-os-semaphore.h" +#include "metadata/sgen-bridge-internals.h" + +extern void mono_sgen_register_moved_object (void *obj, void *destination); +extern void mono_sgen_gc_event_moves (void); + +extern void mono_sgen_init_stw (void); + +enum { + INTERNAL_MEM_EPHEMERON_LINK = INTERNAL_MEM_FIRST_CLIENT, + INTERNAL_MEM_MOVED_OBJECT, + INTERNAL_MEM_MAX +}; + +static inline mword +sgen_mono_array_size (GCVTable vtable, MonoArray *array, mword *bounds_size, mword descr) +{ + mword size, size_without_bounds; + int element_size; + + if ((descr & DESC_TYPE_MASK) == DESC_TYPE_VECTOR) + element_size = ((descr) >> VECTOR_ELSIZE_SHIFT) & MAX_ELEMENT_SIZE; + else + element_size = vtable->klass->sizes.element_size; + + size_without_bounds = size = MONO_SIZEOF_MONO_ARRAY + element_size * mono_array_length_fast (array); + + if (G_UNLIKELY (array->bounds)) { + size += sizeof (mono_array_size_t) - 1; + size &= ~(sizeof (mono_array_size_t) - 1); + size += sizeof (MonoArrayBounds) * vtable->klass->rank; + } + + if (bounds_size) + *bounds_size = size - size_without_bounds; + return size; +} + +#define SGEN_CLIENT_OBJECT_HEADER_SIZE (sizeof (GCObject)) +#define SGEN_CLIENT_MINIMUM_OBJECT_SIZE SGEN_CLIENT_OBJECT_HEADER_SIZE + +static mword /*__attribute__ ((__noinline__)) not sure if this hint is a good idea*/ +sgen_client_slow_object_get_size (GCVTable vtable, GCObject* o) +{ + MonoClass *klass = ((MonoVTable*)vtable)->klass; + + /* + * We depend on mono_string_length_fast and + * mono_array_length_fast not using the object's vtable. + */ + if (klass == mono_defaults.string_class) { + return G_STRUCT_OFFSET (MonoString, chars) + 2 * mono_string_length_fast ((MonoString*) o) + 2; + } else if (klass->rank) { + return sgen_mono_array_size (vtable, (MonoArray*)o, NULL, 0); + } else { + /* from a created object: the class must be inited already */ + return klass->instance_size; + } +} + +/* + * This function can be called on an object whose first word, the + * vtable field, is not intact. This is necessary for the parallel + * collector. + */ +static MONO_NEVER_INLINE mword +sgen_client_par_object_get_size (GCVTable vtable, GCObject* o) +{ + SgenDescriptor descr = sgen_vtable_get_descriptor (vtable); + mword type = descr & DESC_TYPE_MASK; + + if (type == DESC_TYPE_RUN_LENGTH || type == DESC_TYPE_SMALL_PTRFREE) { + mword size = descr & 0xfff8; + SGEN_ASSERT (9, size >= sizeof (MonoObject), "Run length object size to small"); + return size; + } else if (descr == SGEN_DESC_STRING) { + return G_STRUCT_OFFSET (MonoString, chars) + 2 * mono_string_length_fast ((MonoString*) o) + 2; + } else if (type == DESC_TYPE_VECTOR) { + return sgen_mono_array_size (vtable, (MonoArray*)o, NULL, descr); + } + + return sgen_client_slow_object_get_size (vtable, o); +} + +static MONO_ALWAYS_INLINE size_t G_GNUC_UNUSED +sgen_client_array_element_size (GCVTable gc_vtable) +{ + MonoVTable *vt = (MonoVTable*)gc_vtable; + return mono_array_element_size (vt->klass); +} + +static MONO_ALWAYS_INLINE G_GNUC_UNUSED char* +sgen_client_array_data_start (GCObject *obj) +{ + return (char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector); +} + +static MONO_ALWAYS_INLINE size_t G_GNUC_UNUSED +sgen_client_array_length (GCObject *obj) +{ + return mono_array_length_fast ((MonoArray*)obj); +} + +static MONO_ALWAYS_INLINE gboolean G_GNUC_UNUSED +sgen_client_object_is_array_fill (GCObject *o) +{ + return ((MonoObject*)o)->synchronisation == GINT_TO_POINTER (-1); +} + +static MONO_ALWAYS_INLINE void G_GNUC_UNUSED +sgen_client_pre_copy_checks (char *destination, GCVTable gc_vtable, void *obj, mword objsize) +{ + MonoVTable *vt = (MonoVTable*)gc_vtable; + SGEN_ASSERT (9, vt->klass->inited, "vtable %p for class %s:%s was not initialized", vt, vt->klass->name_space, vt->klass->name); +} + +static MONO_ALWAYS_INLINE void G_GNUC_UNUSED +sgen_client_update_copied_object (char *destination, GCVTable gc_vtable, void *obj, mword objsize) +{ + MonoVTable *vt = (MonoVTable*)gc_vtable; + if (G_UNLIKELY (vt->rank && ((MonoArray*)obj)->bounds)) { + MonoArray *array = (MonoArray*)destination; + array->bounds = (MonoArrayBounds*)((char*)destination + ((char*)((MonoArray*)obj)->bounds - (char*)obj)); + SGEN_LOG (9, "Array instance %p: size: %lu, rank: %d, length: %lu", array, (unsigned long)objsize, vt->rank, (unsigned long)mono_array_length (array)); + } + + if (MONO_PROFILER_ENABLED (gc_moves)) + mono_sgen_register_moved_object (obj, destination); +} + +#ifdef XDOMAIN_CHECKS_IN_WBARRIER +extern gboolean sgen_mono_xdomain_checks; + +#define sgen_client_wbarrier_generic_nostore_check(ptr) do { \ + /* FIXME: ptr_in_heap must be called with the GC lock held */ \ + if (sgen_mono_xdomain_checks && *(MonoObject**)ptr && ptr_in_heap (ptr)) { \ + char *start = find_object_for_ptr (ptr); \ + MonoObject *value = *(MonoObject**)ptr; \ + LOCK_GC; \ + SGEN_ASSERT (0, start, "Write barrier outside an object?"); \ + if (start) { \ + MonoObject *obj = (MonoObject*)start; \ + if (obj->vtable->domain != value->vtable->domain) \ + SGEN_ASSERT (0, is_xdomain_ref_allowed (ptr, start, obj->vtable->domain), "Cross-domain ref not allowed"); \ + } \ + UNLOCK_GC; \ + } \ + } while (0) +#else +#define sgen_client_wbarrier_generic_nostore_check(ptr) +#endif + +static gboolean G_GNUC_UNUSED +sgen_client_object_has_critical_finalizer (GCObject *obj) +{ + MonoClass *klass; + + if (!mono_defaults.critical_finalizer_object) + return FALSE; + + klass = SGEN_LOAD_VTABLE (obj)->klass; + + return mono_class_has_parent_fast (klass, mono_defaults.critical_finalizer_object); +} + +const char* sgen_client_vtable_get_namespace (GCVTable vtable); +const char* sgen_client_vtable_get_name (GCVTable vtable); + +static gboolean G_GNUC_UNUSED +sgen_client_bridge_need_processing (void) +{ + return sgen_need_bridge_processing (); +} + +static void G_GNUC_UNUSED +sgen_client_bridge_reset_data (void) +{ + sgen_bridge_reset_data (); +} + +static void G_GNUC_UNUSED +sgen_client_bridge_processing_stw_step (void) +{ + sgen_bridge_processing_stw_step (); +} + +static void G_GNUC_UNUSED +sgen_client_bridge_wait_for_processing (void) +{ + mono_gc_wait_for_bridge_processing (); +} + +static void G_GNUC_UNUSED +sgen_client_bridge_processing_finish (int generation) +{ + sgen_bridge_processing_finish (generation); +} + +static gboolean G_GNUC_UNUSED +sgen_client_bridge_is_bridge_object (GCObject *obj) +{ + return sgen_is_bridge_object (obj); +} + +static void G_GNUC_UNUSED +sgen_client_bridge_register_finalized_object (GCObject *object) +{ + sgen_bridge_register_finalized_object (object); +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_collection_requested (int generation, size_t requested_size, gboolean force) +{ + MONO_GC_REQUESTED (generation, requested_size, force); +} + +void +sgen_client_binary_protocol_collection_begin (int minor_gc_count, int generation); + +void +sgen_client_binary_protocol_collection_end (int minor_gc_count, int generation, long long num_objects_scanned, long long num_unique_objects_scanned); + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_concurrent_start (void) +{ + MONO_GC_CONCURRENT_START_BEGIN (GENERATION_OLD); +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_concurrent_update (void) +{ + MONO_GC_CONCURRENT_UPDATE_FINISH_BEGIN (GENERATION_OLD, sgen_get_major_collector ()->get_and_reset_num_major_objects_marked ()); +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_concurrent_finish (void) +{ + MONO_GC_CONCURRENT_UPDATE_FINISH_BEGIN (GENERATION_OLD, sgen_get_major_collector ()->get_and_reset_num_major_objects_marked ()); +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_sweep_begin (int generation, int full_sweep) +{ + MONO_GC_SWEEP_BEGIN (generation, full_sweep); +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_sweep_end (int generation, int full_sweep) +{ + MONO_GC_SWEEP_END (generation, full_sweep); +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_world_stopping (int generation, long long timestamp, gpointer thread) +{ + MONO_GC_WORLD_STOP_BEGIN (); +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_world_stopped (int generation, long long timestamp, long long total_major_cards, long long marked_major_cards, long long total_los_cards, long long marked_los_cards) +{ + MONO_GC_WORLD_STOP_END (); +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_world_restarting (int generation, long long timestamp, long long total_major_cards, long long marked_major_cards, long long total_los_cards, long long marked_los_cards) +{ + MONO_GC_WORLD_RESTART_BEGIN (generation); +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_world_restarted (int generation, long long timestamp) +{ + MONO_GC_WORLD_RESTART_END (generation); +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_block_alloc (gpointer addr, size_t size) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_block_free (gpointer addr, size_t size) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_block_set_state (gpointer addr, size_t size, int old, int new_) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_mark_start (int generation) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_mark_end (int generation) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_reclaim_start (int generation) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_reclaim_end (int generation) +{ +} + +static void +mono_binary_protocol_alloc_generic (gpointer obj, gpointer vtable, size_t size, gboolean pinned) +{ +#ifdef ENABLE_DTRACE + const char *namespace = sgen_client_vtable_get_namespace (vtable); + const char *name = sgen_client_vtable_get_name (vtable); + + if (sgen_ptr_in_nursery (obj)) { + if (G_UNLIKELY (MONO_GC_NURSERY_OBJ_ALLOC_ENABLED ())) + MONO_GC_NURSERY_OBJ_ALLOC ((mword)obj, size, namespace, name); + } else { + if (size > SGEN_MAX_SMALL_OBJ_SIZE) { + if (G_UNLIKELY (MONO_GC_MAJOR_OBJ_ALLOC_LARGE_ENABLED ())) + MONO_GC_MAJOR_OBJ_ALLOC_LARGE ((mword)obj, size, namespace, name); + } else if (pinned) { + MONO_GC_MAJOR_OBJ_ALLOC_PINNED ((mword)obj, size, namespace, name); + } + } +#endif +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_alloc (gpointer obj, gpointer vtable, size_t size, gpointer provenance) +{ + mono_binary_protocol_alloc_generic (obj, vtable, size, FALSE); +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_alloc_pinned (gpointer obj, gpointer vtable, size_t size, gpointer provenance) +{ + mono_binary_protocol_alloc_generic (obj, vtable, size, TRUE); +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_alloc_degraded (gpointer obj, gpointer vtable, size_t size, gpointer provenance) +{ + MONO_GC_MAJOR_OBJ_ALLOC_DEGRADED ((mword)obj, size, sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable)); +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_card_scan (gpointer start, size_t size) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_pin_stage (gpointer addr_ptr, gpointer addr) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_cement_stage (gpointer addr) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_pin (gpointer obj, gpointer vtable, size_t size) +{ +#ifdef ENABLE_DTRACE + if (G_UNLIKELY (MONO_GC_OBJ_PINNED_ENABLED ())) { + int gen = sgen_ptr_in_nursery (obj) ? GENERATION_NURSERY : GENERATION_OLD; + MONO_GC_OBJ_PINNED ((mword)obj, + sgen_safe_object_get_size (obj), + sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable), gen); + } +#endif +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_mark (gpointer obj, gpointer vtable, size_t size) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_scan_begin (gpointer obj, gpointer vtable, size_t size) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_scan_vtype_begin (gpointer obj, size_t size) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_scan_process_reference (gpointer obj, gpointer ptr, gpointer value) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_scan_stack (gpointer thread, gpointer stack_start, gpointer stack_end, int skip_reason) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_wbarrier (gpointer ptr, gpointer value, gpointer value_vtable) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_cement (gpointer ptr, gpointer vtable, size_t size) +{ +#ifdef ENABLE_DTRACE + if (G_UNLIKELY (MONO_GC_OBJ_CEMENTED_ENABLED())) { + MONO_GC_OBJ_CEMENTED ((mword)ptr, sgen_safe_object_get_size ((GCObject*)ptr), + sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable)); + } +#endif +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_copy (gpointer from, gpointer to, gpointer vtable, size_t size) +{ +#ifdef ENABLE_DTRACE + if (G_UNLIKELY (MONO_GC_OBJ_MOVED_ENABLED ())) { + int dest_gen = sgen_ptr_in_nursery (to) ? GENERATION_NURSERY : GENERATION_OLD; + int src_gen = sgen_ptr_in_nursery (from) ? GENERATION_NURSERY : GENERATION_OLD; + MONO_GC_OBJ_MOVED ((mword)to, (mword)from, dest_gen, src_gen, size, sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable)); + } +#endif +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_global_remset (gpointer ptr, gpointer value, gpointer value_vtable) +{ +#ifdef ENABLE_DTRACE + if (G_UNLIKELY (MONO_GC_GLOBAL_REMSET_ADD_ENABLED ())) { + MONO_GC_GLOBAL_REMSET_ADD ((mword)ptr, (mword)value, sgen_safe_object_get_size (value), + sgen_client_vtable_get_namespace (value_vtable), sgen_client_vtable_get_name (value_vtable)); + } +#endif +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_mod_union_remset (gpointer obj, gpointer ptr, gpointer value, gpointer value_vtable) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_ptr_update (gpointer ptr, gpointer old_value, gpointer new_value, gpointer vtable, size_t size) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_cleanup (gpointer ptr, gpointer vtable, size_t size) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_dislink_add (gpointer link, gpointer obj, gboolean track) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_dislink_update (gpointer link, gpointer obj, gboolean track) +{ +#ifdef ENABLE_DTRACE + if (MONO_GC_WEAK_UPDATE_ENABLED ()) { + GCVTable vt = obj ? SGEN_LOAD_VTABLE (obj) : NULL; + MONO_GC_WEAK_UPDATE ((mword)link, + (mword)obj, + obj ? (mword)sgen_safe_object_get_size (obj) : (mword)0, + obj ? sgen_client_vtable_get_namespace (vt) : NULL, + obj ? sgen_client_vtable_get_name (vt) : NULL, + track ? 1 : 0); + } +#endif +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_dislink_remove (gpointer link, gboolean track) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_empty (gpointer start, size_t size) +{ + if (sgen_ptr_in_nursery (start)) + MONO_GC_NURSERY_SWEPT ((mword)start, size); + else + MONO_GC_MAJOR_SWEPT ((mword)start, size); +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_thread_suspend (gpointer thread, gpointer stopped_ip) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_thread_restart (gpointer thread) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_thread_register (gpointer thread) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_thread_unregister (gpointer thread) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_missing_remset (gpointer obj, gpointer obj_vtable, int offset, gpointer value, gpointer value_vtable, gboolean value_pinned) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_cement_reset (void) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_domain_unload_begin (gpointer domain) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_domain_unload_end (gpointer domain) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_gray_enqueue (gpointer queue, gpointer cursor, gpointer value) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_gray_dequeue (gpointer queue, gpointer cursor, gpointer value) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_major_card_table_scan_start (long long timestamp, gboolean mod_union) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_major_card_table_scan_end (long long timestamp, gboolean mod_union) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_los_card_table_scan_start (long long timestamp, gboolean mod_union) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_los_card_table_scan_end (long long timestamp, gboolean mod_union) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_finish_gray_stack_start (long long timestamp, int generation) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_finish_gray_stack_end (long long timestamp, int generation) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_worker_finish (long long timestamp, gboolean forced) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_evacuating_blocks (size_t block_size) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_concurrent_sweep_end (long long timestamp) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_header (long long check, int version, int ptr_size, gboolean little_endian) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_pin_stats (int objects_pinned_in_nursery, size_t bytes_pinned_in_nursery, int objects_pinned_in_major, size_t bytes_pinned_in_major) +{ +} + +static void G_GNUC_UNUSED +sgen_client_root_registered (char *start, size_t size, int source, void *key, const char *msg) +{ + MONO_PROFILER_RAISE (gc_root_register, ((const mono_byte *) start, size, source, key, msg)); +} + +static void G_GNUC_UNUSED +sgen_client_root_deregistered (char *start) +{ + MONO_PROFILER_RAISE (gc_root_unregister, ((const mono_byte *) start)); +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_worker_finish_stats (int worker_index, int generation, gboolean forced, long long major_scan, long long los_scan, long long work_time) +{ +} + +static void G_GNUC_UNUSED +sgen_client_binary_protocol_collection_end_stats (long long major_scan, long long los_scan, long long finish_stack) +{ +} + +#define TLAB_ACCESS_INIT SgenThreadInfo *__thread_info__ = (SgenThreadInfo*)mono_tls_get_sgen_thread_info () +#define IN_CRITICAL_REGION (__thread_info__->client_info.in_critical_region) + +/* Enter must be visible before anything is done in the critical region. */ +#define ENTER_CRITICAL_REGION do { mono_atomic_store_acquire (&IN_CRITICAL_REGION, 1); } while (0) + +/* Exit must make sure all critical regions stores are visible before it signal the end of the region. + * We don't need to emit a full barrier since we + */ +#define EXIT_CRITICAL_REGION do { mono_atomic_store_release (&IN_CRITICAL_REGION, 0); } while (0) + +#ifndef DISABLE_CRITICAL_REGION +/* + * We can only use a critical region in the managed allocator if the JIT supports OP_ATOMIC_STORE_I4. + * + * TODO: Query the JIT instead of this ifdef hack. + */ +#if defined (TARGET_X86) || defined (TARGET_AMD64) || (defined (TARGET_ARM) && defined (HAVE_ARMV7)) || defined (TARGET_ARM64) +#define MANAGED_ALLOCATOR_CAN_USE_CRITICAL_REGION +#endif +#endif + +#define SGEN_TV_DECLARE(name) gint64 name +#define SGEN_TV_GETTIME(tv) tv = mono_100ns_ticks () +#define SGEN_TV_ELAPSED(start,end) ((gint64)(end-start)) + +guint64 mono_time_since_last_stw (void); + +typedef MonoSemType SgenSemaphore; + +#define SGEN_SEMAPHORE_INIT(sem,initial) mono_os_sem_init ((sem), (initial)) +#define SGEN_SEMAPHORE_POST(sem) mono_os_sem_post ((sem)) +#define SGEN_SEMAPHORE_WAIT(sem) mono_os_sem_wait ((sem), MONO_SEM_FLAGS_NONE) + +gboolean sgen_has_critical_method (void); +gboolean sgen_is_critical_method (MonoMethod *method); + +void sgen_set_use_managed_allocator (gboolean flag); +gboolean sgen_is_managed_allocator (MonoMethod *method); +gboolean sgen_has_managed_allocator (void); + +void sgen_scan_for_registered_roots_in_domain (MonoDomain *domain, int root_type); +void sgen_null_links_for_domain (MonoDomain *domain); + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/sgen-dynarray.h b/unity-2019.4.24f1-mbe/mono/metadata/sgen-dynarray.h new file mode 100644 index 000000000..2d1587d49 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/sgen-dynarray.h @@ -0,0 +1,347 @@ +/** + * \file + * Copyright 2016 Xamarin, Inc. + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + + // Growable array implementation used by sgen-new-bridge and sgen-tarjan-bridge. + +typedef struct { + int size; + int capacity; /* if negative, data points to another DynArray's data */ + char *data; +} DynArray; + +/*Specializations*/ + +// IntArray supports an optimization (in sgen-new-bridge.c): If capacity is less than 0 it is a "copy" and does not own its buffer. +typedef struct { + DynArray array; +} DynIntArray; + +// PtrArray supports an optimization: If size is equal to 1 it is a "singleton" and data points to the single held item, not to a buffer. +typedef struct { + DynArray array; +} DynPtrArray; + +typedef struct { + DynArray array; +} DynSCCArray; + +static void +dyn_array_init (DynArray *da) +{ + da->size = 0; + da->capacity = 0; + da->data = NULL; +} + +static void +dyn_array_uninit (DynArray *da, int elem_size) +{ + if (da->capacity < 0) { + dyn_array_init (da); + return; + } + + if (da->capacity == 0) + return; + + sgen_free_internal_dynamic (da->data, elem_size * da->capacity, INTERNAL_MEM_BRIDGE_DATA); + da->data = NULL; +} + +static void +dyn_array_empty (DynArray *da) +{ + if (da->capacity < 0) + dyn_array_init (da); + else + da->size = 0; +} + +static char * +dyn_array_ensure_capacity_internal (DynArray *da, int capacity, int elem_size) +{ + if (da->capacity <= 0) + da->capacity = 2; + while (capacity > da->capacity) + da->capacity *= 2; + + return (char *)sgen_alloc_internal_dynamic (elem_size * da->capacity, INTERNAL_MEM_BRIDGE_DATA, TRUE); +} + +static void +dyn_array_ensure_capacity (DynArray *da, int capacity, int elem_size) +{ + int old_capacity = da->capacity; + char *new_data; + + g_assert (capacity > 0); + + if (capacity <= old_capacity) + return; + + new_data = dyn_array_ensure_capacity_internal (da, capacity, elem_size); + memcpy (new_data, da->data, elem_size * da->size); + if (old_capacity > 0) + sgen_free_internal_dynamic (da->data, elem_size * old_capacity, INTERNAL_MEM_BRIDGE_DATA); + da->data = new_data; +} + +static gboolean +dyn_array_is_copy (DynArray *da) +{ + return da->capacity < 0; +} + +static void +dyn_array_ensure_independent (DynArray *da, int elem_size) +{ + if (!dyn_array_is_copy (da)) + return; + dyn_array_ensure_capacity (da, da->size, elem_size); + g_assert (da->capacity > 0); +} + +static void* +dyn_array_add (DynArray *da, int elem_size) +{ + void *p; + + dyn_array_ensure_capacity (da, da->size + 1, elem_size); + + p = da->data + da->size * elem_size; + ++da->size; + return p; +} + +static void +dyn_array_copy (DynArray *dst, DynArray *src, int elem_size) +{ + dyn_array_uninit (dst, elem_size); + + if (src->size == 0) + return; + + dst->size = src->size; + dst->capacity = -1; + dst->data = src->data; +} + +/* int */ +static void +dyn_array_int_init (DynIntArray *da) +{ + dyn_array_init (&da->array); +} + +static void +dyn_array_int_uninit (DynIntArray *da) +{ + dyn_array_uninit (&da->array, sizeof (int)); +} + +static int +dyn_array_int_size (DynIntArray *da) +{ + return da->array.size; +} + +#ifdef NEW_XREFS +static void +dyn_array_int_empty (DynIntArray *da) +{ + dyn_array_empty (&da->array); +} +#endif + +static void +dyn_array_int_add (DynIntArray *da, int x) +{ + int *p = (int *)dyn_array_add (&da->array, sizeof (int)); + *p = x; +} + +static int +dyn_array_int_get (DynIntArray *da, int x) +{ + return ((int*)da->array.data)[x]; +} + +#ifdef NEW_XREFS +static void +dyn_array_int_set (DynIntArray *da, int idx, int val) +{ + ((int*)da->array.data)[idx] = val; +} +#endif + +static void +dyn_array_int_ensure_independent (DynIntArray *da) +{ + dyn_array_ensure_independent (&da->array, sizeof (int)); +} + +static void +dyn_array_int_copy (DynIntArray *dst, DynIntArray *src) +{ + dyn_array_copy (&dst->array, &src->array, sizeof (int)); +} + +static gboolean +dyn_array_int_is_copy (DynIntArray *da) +{ + return dyn_array_is_copy (&da->array); +} + +/* ptr */ + +static void +dyn_array_ptr_init (DynPtrArray *da) +{ + dyn_array_init (&da->array); +} + +static void +dyn_array_ptr_uninit (DynPtrArray *da) +{ +#ifdef OPTIMIZATION_SINGLETON_DYN_ARRAY + if (da->array.capacity == 1) + dyn_array_ptr_init (da); + else +#endif + dyn_array_uninit (&da->array, sizeof (void*)); +} + +static int +dyn_array_ptr_size (DynPtrArray *da) +{ + return da->array.size; +} + +static void +dyn_array_ptr_empty (DynPtrArray *da) +{ +#ifdef OPTIMIZATION_SINGLETON_DYN_ARRAY + if (da->array.capacity == 1) + dyn_array_ptr_init (da); + else +#endif + dyn_array_empty (&da->array); +} + +static void* +dyn_array_ptr_get (DynPtrArray *da, int x) +{ +#ifdef OPTIMIZATION_SINGLETON_DYN_ARRAY + if (da->array.capacity == 1) { + g_assert (x == 0); + return da->array.data; + } +#endif + return ((void**)da->array.data)[x]; +} + +static void +dyn_array_ptr_set (DynPtrArray *da, int x, void *ptr) +{ +#ifdef OPTIMIZATION_SINGLETON_DYN_ARRAY + if (da->array.capacity == 1) { + g_assert (x == 0); + da->array.data = ptr; + } else +#endif + { + ((void**)da->array.data)[x] = ptr; + } +} + +static void +dyn_array_ptr_add (DynPtrArray *da, void *ptr) +{ + void **p; + +#ifdef OPTIMIZATION_SINGLETON_DYN_ARRAY + if (da->array.capacity == 0) { + da->array.capacity = 1; + da->array.size = 1; + p = (void**)&da->array.data; + } else if (da->array.capacity == 1) { + void *ptr0 = da->array.data; + void **p0; + dyn_array_init (&da->array); + p0 = (void **)dyn_array_add (&da->array, sizeof (void*)); + *p0 = ptr0; + p = (void **)dyn_array_add (&da->array, sizeof (void*)); + } else +#endif + { + p = (void **)dyn_array_add (&da->array, sizeof (void*)); + } + *p = ptr; +} + +#define dyn_array_ptr_push dyn_array_ptr_add + +static void* +dyn_array_ptr_pop (DynPtrArray *da) +{ + int size = da->array.size; + void *p; + g_assert (size > 0); +#ifdef OPTIMIZATION_SINGLETON_DYN_ARRAY + if (da->array.capacity == 1) { + p = dyn_array_ptr_get (da, 0); + dyn_array_init (&da->array); + } else +#endif + { + g_assert (da->array.capacity > 1); + dyn_array_ensure_independent (&da->array, sizeof (void*)); + p = dyn_array_ptr_get (da, size - 1); + --da->array.size; + } + return p; +} + +static void +dyn_array_ptr_ensure_capacity (DynPtrArray *da, int capacity) +{ +#ifdef OPTIMIZATION_SINGLETON_DYN_ARRAY + if (capacity == 1 && da->array.capacity < 1) { + da->array.capacity = 1; + } else if (da->array.capacity == 1) // TODO size==1 + { + if (capacity > 1) + { + void *ptr = dyn_array_ptr_get (da, 0); + da->array.data = dyn_array_ensure_capacity_internal(&da->array, capacity, sizeof (void*)); + dyn_array_ptr_set (da, 0, ptr); + } + } +#endif + { + dyn_array_ensure_capacity (&da->array, capacity, sizeof (void*)); + } +} + +static void +dyn_array_ptr_set_all (DynPtrArray *dst, DynPtrArray *src) +{ + const int copysize = src->array.size; + if (copysize > 0) { + dyn_array_ptr_ensure_capacity (dst, copysize); + +#ifdef OPTIMIZATION_SINGLETON_DYN_ARRAY + if (copysize == 1) { + dyn_array_ptr_set (dst, 0, dyn_array_ptr_get (src, 0)); + } else +#endif + { + memcpy (dst->array.data, src->array.data, copysize * sizeof (void*)); + } + } + dst->array.size = src->array.size; +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/sgen-mono.c b/unity-2019.4.24f1-mbe/mono/metadata/sgen-mono.c new file mode 100644 index 000000000..98197c1ca --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/sgen-mono.c @@ -0,0 +1,3380 @@ +/** + * \file + * SGen features specific to Mono. + * + * Copyright (C) 2014 Xamarin Inc + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include "config.h" +#ifdef HAVE_SGEN_GC + +#include "sgen/sgen-gc.h" +#include "sgen/sgen-protocol.h" +#include "metadata/monitor.h" +#include "sgen/sgen-layout-stats.h" +#include "sgen/sgen-client.h" +#include "sgen/sgen-cardtable.h" +#include "sgen/sgen-pinning.h" +#include "sgen/sgen-workers.h" +#include "metadata/marshal.h" +#include "metadata/method-builder.h" +#include "metadata/abi-details.h" +#include "metadata/mono-gc.h" +#include "metadata/runtime.h" +#include "metadata/sgen-bridge-internals.h" +#include "metadata/gc-internals.h" +#include "metadata/handle.h" +#include "utils/mono-memory-model.h" +#include "utils/mono-logger-internals.h" +#include "utils/mono-threads-coop.h" +#include "utils/mono-threads.h" +#include "metadata/w32handle.h" + +#ifdef HEAVY_STATISTICS +static guint64 stat_wbarrier_set_arrayref = 0; +static guint64 stat_wbarrier_value_copy = 0; +static guint64 stat_wbarrier_object_copy = 0; + +static guint64 los_marked_cards; +static guint64 los_array_cards; +static guint64 los_array_remsets; +#endif + +/* If set, mark stacks conservatively, even if precise marking is possible */ +static gboolean conservative_stack_mark = FALSE; +/* If set, check that there are no references to the domain left at domain unload */ +gboolean sgen_mono_xdomain_checks = FALSE; + +/* Functions supplied by the runtime to be called by the GC */ +static MonoGCCallbacks gc_callbacks; + +#define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1)) + +#define OPDEF(a,b,c,d,e,f,g,h,i,j) \ + a = i, + +enum { +#include "mono/cil/opcode.def" + CEE_LAST +}; + +#undef OPDEF + +/* + * Write barriers + */ + +static gboolean +ptr_on_stack (void *ptr) +{ + gpointer stack_start = &stack_start; + SgenThreadInfo *info = mono_thread_info_current (); + + if (ptr >= stack_start && ptr < (gpointer)info->client_info.info.stack_end) + return TRUE; + return FALSE; +} + +#ifdef SGEN_HEAVY_BINARY_PROTOCOL +#undef HANDLE_PTR +#define HANDLE_PTR(ptr,obj) do { \ + gpointer o = *(gpointer*)(ptr); \ + if ((o)) { \ + gpointer d = ((char*)dest) + ((char*)(ptr) - (char*)(obj)); \ + binary_protocol_wbarrier (d, o, (gpointer) SGEN_LOAD_VTABLE (o)); \ + } \ + } while (0) + +static void +scan_object_for_binary_protocol_copy_wbarrier (gpointer dest, char *start, mword desc) +{ +#define SCAN_OBJECT_NOVTABLE +#include "sgen/sgen-scan-object.h" +} +#endif + +void +mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass) +{ + HEAVY_STAT (++stat_wbarrier_value_copy); + g_assert (klass->valuetype); + + SGEN_LOG (8, "Adding value remset at %p, count %d, descr %p for class %s (%p)", dest, count, (gpointer)klass->gc_descr, klass->name, klass); + + if (sgen_ptr_in_nursery (dest) || ptr_on_stack (dest) || !sgen_gc_descr_has_references ((mword)klass->gc_descr)) { + size_t element_size = mono_class_value_size (klass, NULL); + size_t size = count * element_size; + mono_gc_memmove_atomic (dest, src, size); + return; + } + +#ifdef SGEN_HEAVY_BINARY_PROTOCOL + if (binary_protocol_is_heavy_enabled ()) { + size_t element_size = mono_class_value_size (klass, NULL); + int i; + for (i = 0; i < count; ++i) { + scan_object_for_binary_protocol_copy_wbarrier ((char*)dest + i * element_size, + (char*)src + i * element_size - sizeof (MonoObject), + (mword) klass->gc_descr); + } + } +#endif + + sgen_get_remset ()->wbarrier_value_copy (dest, src, count, mono_class_value_size (klass, NULL)); +} + +/** + * mono_gc_wbarrier_object_copy: + * + * Write barrier to call when \p obj is the result of a clone or copy of an object. + */ +void +mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src) +{ + int size; + + HEAVY_STAT (++stat_wbarrier_object_copy); + + SGEN_ASSERT (6, !ptr_on_stack (obj), "Why is this called for a non-reference type?"); + if (sgen_ptr_in_nursery (obj) || !SGEN_OBJECT_HAS_REFERENCES (src)) { + size = mono_object_class (obj)->instance_size; + mono_gc_memmove_aligned ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject), + size - sizeof (MonoObject)); + return; + } + +#ifdef SGEN_HEAVY_BINARY_PROTOCOL + if (binary_protocol_is_heavy_enabled ()) + scan_object_for_binary_protocol_copy_wbarrier (obj, (char*)src, (mword) src->vtable->gc_descr); +#endif + + sgen_get_remset ()->wbarrier_object_copy (obj, src); +} + +/** + * mono_gc_wbarrier_set_arrayref: + */ +void +mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value) +{ + HEAVY_STAT (++stat_wbarrier_set_arrayref); + if (sgen_ptr_in_nursery (slot_ptr)) { + *(void**)slot_ptr = value; + return; + } + SGEN_LOG (8, "Adding remset at %p", slot_ptr); + if (value) + binary_protocol_wbarrier (slot_ptr, value, value->vtable); + + sgen_get_remset ()->wbarrier_set_field ((GCObject*)arr, slot_ptr, value); +} + +/** + * mono_gc_wbarrier_set_field: + */ +void +mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value) +{ + mono_gc_wbarrier_set_arrayref ((MonoArray*)obj, field_ptr, value); +} + +void +mono_gc_wbarrier_range_copy (gpointer _dest, gpointer _src, int size) +{ + sgen_wbarrier_range_copy (_dest, _src, size); +} + +void* +mono_gc_get_range_copy_func (void) +{ + return sgen_get_remset ()->wbarrier_range_copy; +} + +int +mono_gc_get_suspend_signal (void) +{ + return mono_threads_suspend_get_suspend_signal (); +} + +int +mono_gc_get_restart_signal (void) +{ + return mono_threads_suspend_get_restart_signal (); +} + +static MonoMethod *write_barrier_conc_method; +static MonoMethod *write_barrier_noconc_method; + +gboolean +sgen_is_critical_method (MonoMethod *method) +{ + return sgen_is_managed_allocator (method); +} + +gboolean +sgen_has_critical_method (void) +{ + return sgen_has_managed_allocator (); +} + +gboolean +mono_gc_is_critical_method (MonoMethod *method) +{ + return sgen_is_critical_method (method); +} + +#ifdef ENABLE_ILGEN + +static void +emit_nursery_check (MonoMethodBuilder *mb, int *nursery_check_return_labels, gboolean is_concurrent) +{ + int shifted_nursery_start = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + + memset (nursery_check_return_labels, 0, sizeof (int) * 2); + // if (ptr_in_nursery (ptr)) return; + /* + * Masking out the bits might be faster, but we would have to use 64 bit + * immediates, which might be slower. + */ + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_LDPTR_NURSERY_START); + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_LDPTR_NURSERY_BITS); + mono_mb_emit_byte (mb, CEE_SHR_UN); + mono_mb_emit_stloc (mb, shifted_nursery_start); + + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_LDPTR_NURSERY_BITS); + mono_mb_emit_byte (mb, CEE_SHR_UN); + mono_mb_emit_ldloc (mb, shifted_nursery_start); + nursery_check_return_labels [0] = mono_mb_emit_branch (mb, CEE_BEQ); + + if (!is_concurrent) { + // if (!ptr_in_nursery (*ptr)) return; + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_LDPTR_NURSERY_BITS); + mono_mb_emit_byte (mb, CEE_SHR_UN); + mono_mb_emit_ldloc (mb, shifted_nursery_start); + nursery_check_return_labels [1] = mono_mb_emit_branch (mb, CEE_BNE_UN); + } +} +#endif + +MonoMethod* +mono_gc_get_specific_write_barrier (gboolean is_concurrent) +{ + MonoMethod *res; + MonoMethodBuilder *mb; + MonoMethodSignature *sig; + MonoMethod **write_barrier_method_addr; + WrapperInfo *info; +#ifdef MANAGED_WBARRIER + int i, nursery_check_labels [2]; +#endif + + // FIXME: Maybe create a separate version for ctors (the branch would be + // correctly predicted more times) + if (is_concurrent) + write_barrier_method_addr = &write_barrier_conc_method; + else + write_barrier_method_addr = &write_barrier_noconc_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; + + if (is_concurrent) + mb = mono_mb_new (mono_defaults.object_class, "wbarrier_conc", MONO_WRAPPER_WRITE_BARRIER); + else + mb = mono_mb_new (mono_defaults.object_class, "wbarrier_noconc", MONO_WRAPPER_WRITE_BARRIER); + +#ifdef ENABLE_ILGEN +#ifdef MANAGED_WBARRIER + emit_nursery_check (mb, nursery_check_labels, is_concurrent); + /* + addr = sgen_cardtable + ((address >> CARD_BITS) & CARD_MASK) + *addr = 1; + + sgen_cardtable: + LDC_PTR sgen_cardtable + + address >> CARD_BITS + LDARG_0 + LDC_I4 CARD_BITS + SHR_UN + if (SGEN_HAVE_OVERLAPPING_CARDS) { + LDC_PTR card_table_mask + AND + } + AND + ldc_i4_1 + stind_i1 + */ + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_LDPTR_CARD_TABLE); + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_icon (mb, CARD_BITS); + mono_mb_emit_byte (mb, CEE_SHR_UN); + mono_mb_emit_byte (mb, CEE_CONV_I); +#ifdef SGEN_HAVE_OVERLAPPING_CARDS +#if SIZEOF_VOID_P == 8 + mono_mb_emit_icon8 (mb, CARD_MASK); +#else + mono_mb_emit_icon (mb, CARD_MASK); +#endif + mono_mb_emit_byte (mb, CEE_CONV_I); + mono_mb_emit_byte (mb, CEE_AND); +#endif + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_icon (mb, 1); + mono_mb_emit_byte (mb, CEE_STIND_I1); + + // return; + for (i = 0; i < 2; ++i) { + if (nursery_check_labels [i]) + mono_mb_patch_branch (mb, nursery_check_labels [i]); + } + mono_mb_emit_byte (mb, CEE_RET); +#else + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore); + mono_mb_emit_byte (mb, CEE_RET); +#endif +#endif + 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); + + LOCK_GC; + if (*write_barrier_method_addr) { + /* Already created */ + mono_free_method (res); + } else { + /* double-checked locking */ + mono_memory_barrier (); + *write_barrier_method_addr = res; + } + UNLOCK_GC; + + return *write_barrier_method_addr; +} + +MonoMethod* +mono_gc_get_write_barrier (void) +{ + return mono_gc_get_specific_write_barrier (major_collector.is_concurrent); +} + +/* + * Dummy filler objects + */ + +/* Vtable of the objects used to fill out nursery fragments before a collection */ +static GCVTable array_fill_vtable; + +static GCVTable +get_array_fill_vtable (void) +{ + if (!array_fill_vtable) { + static MonoClass klass; + static char _vtable[sizeof(MonoVTable)+8]; + MonoVTable* vtable = (MonoVTable*) ALIGN_TO((mword)_vtable, 8); + gsize bmap; + + MonoDomain *domain = mono_get_root_domain (); + g_assert (domain); + + klass.element_class = mono_defaults.byte_class; + klass.rank = 1; + klass.instance_size = MONO_SIZEOF_MONO_ARRAY; + klass.sizes.element_size = 1; + klass.size_inited = 1; + klass.name = "array_filler_type"; + + vtable->klass = &klass; + bmap = 0; + vtable->gc_descr = mono_gc_make_descr_for_array (TRUE, &bmap, 0, 1); + vtable->rank = 1; + + array_fill_vtable = vtable; + } + return array_fill_vtable; +} + +gboolean +sgen_client_array_fill_range (char *start, size_t size) +{ + MonoArray *o; + + if (size < MONO_SIZEOF_MONO_ARRAY) { + memset (start, 0, size); + return FALSE; + } + + o = (MonoArray*)start; + o->obj.vtable = (MonoVTable*)get_array_fill_vtable (); + /* Mark this as not a real object */ + o->obj.synchronisation = (MonoThreadsSync *)GINT_TO_POINTER (-1); + o->bounds = NULL; + o->max_length = (mono_array_size_t)(size - MONO_SIZEOF_MONO_ARRAY); + + return TRUE; +} + +void +sgen_client_zero_array_fill_header (void *p, size_t size) +{ + if (size >= MONO_SIZEOF_MONO_ARRAY) { + memset (p, 0, MONO_SIZEOF_MONO_ARRAY); + } else { + static guint8 zeros [MONO_SIZEOF_MONO_ARRAY]; + + SGEN_ASSERT (0, !memcmp (p, zeros, size), "TLAB segment must be zeroed out."); + } +} + +MonoVTable * +mono_gc_get_vtable (MonoObject *obj) +{ + // See sgen/sgen-tagged-pointer.h. + return SGEN_LOAD_VTABLE (obj); +} + +/* + * Finalization + */ + +static MonoGCFinalizerCallbacks fin_callbacks; + +guint +mono_gc_get_vtable_bits (MonoClass *klass) +{ + guint res = 0; + /* FIXME move this to the bridge code */ + if (sgen_need_bridge_processing ()) { + switch (sgen_bridge_class_kind (klass)) { + case GC_BRIDGE_TRANSPARENT_BRIDGE_CLASS: + case GC_BRIDGE_OPAQUE_BRIDGE_CLASS: + res = SGEN_GC_BIT_BRIDGE_OBJECT; + break; + case GC_BRIDGE_OPAQUE_CLASS: + res = SGEN_GC_BIT_BRIDGE_OPAQUE_OBJECT; + break; + case GC_BRIDGE_TRANSPARENT_CLASS: + break; + } + } + if (fin_callbacks.is_class_finalization_aware) { + if (fin_callbacks.is_class_finalization_aware (klass)) + res |= SGEN_GC_BIT_FINALIZER_AWARE; + } + return res; +} + +static gboolean +is_finalization_aware (MonoObject *obj) +{ + MonoVTable *vt = SGEN_LOAD_VTABLE (obj); + return (vt->gc_bits & SGEN_GC_BIT_FINALIZER_AWARE) == SGEN_GC_BIT_FINALIZER_AWARE; +} + +void +sgen_client_object_queued_for_finalization (GCObject *obj) +{ + if (fin_callbacks.object_queued_for_finalization && is_finalization_aware (obj)) + fin_callbacks.object_queued_for_finalization (obj); + +#ifdef ENABLE_DTRACE + if (G_UNLIKELY (MONO_GC_FINALIZE_ENQUEUE_ENABLED ())) { + int gen = sgen_ptr_in_nursery (obj) ? GENERATION_NURSERY : GENERATION_OLD; + GCVTable vt = SGEN_LOAD_VTABLE (obj); + MONO_GC_FINALIZE_ENQUEUE ((mword)obj, sgen_safe_object_get_size (obj), + sgen_client_vtable_get_namespace (vt), sgen_client_vtable_get_name (vt), gen, + sgen_client_object_has_critical_finalizer (obj)); + } +#endif +} + +void +mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks) +{ + if (callbacks->version != MONO_GC_FINALIZER_EXTENSION_VERSION) + g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION, callbacks->version); + + fin_callbacks = *callbacks; +} + +void +sgen_client_run_finalize (MonoObject *obj) +{ + mono_gc_run_finalize (obj, NULL); +} + +/** + * mono_gc_invoke_finalizers: + */ +int +mono_gc_invoke_finalizers (void) +{ + return sgen_gc_invoke_finalizers (); +} + +/** + * mono_gc_pending_finalizers: + */ +MonoBoolean +mono_gc_pending_finalizers (void) +{ + return sgen_have_pending_finalizers (); +} + +void +sgen_client_finalize_notify (void) +{ + mono_gc_finalize_notify (); +} + +void +mono_gc_register_for_finalization (MonoObject *obj, void *user_data) +{ + sgen_object_register_for_finalization (obj, user_data); +} + +static gboolean +object_in_domain_predicate (MonoObject *obj, void *user_data) +{ + MonoDomain *domain = (MonoDomain *)user_data; + if (mono_object_domain (obj) == domain) { + SGEN_LOG (5, "Unregistering finalizer for object: %p (%s)", obj, sgen_client_vtable_get_name (SGEN_LOAD_VTABLE (obj))); + return TRUE; + } + return FALSE; +} + +/** + * mono_gc_finalizers_for_domain: + * \param domain the unloading appdomain + * \param out_array output array + * \param out_size size of output array + * Enqueue for finalization all objects that belong to the unloading appdomain \p domain. + * \p suspend is used for early termination of the enqueuing process. + */ +void +mono_gc_finalize_domain (MonoDomain *domain) +{ + sgen_finalize_if (object_in_domain_predicate, domain); +} + +void +mono_gc_suspend_finalizers (void) +{ + sgen_set_suspend_finalizers (); +} + +/* + * Ephemerons + */ + +typedef struct _EphemeronLinkNode EphemeronLinkNode; + +struct _EphemeronLinkNode { + EphemeronLinkNode *next; + MonoArray *array; +}; + +typedef struct { + GCObject *key; + GCObject *value; +} Ephemeron; + +static EphemeronLinkNode *ephemeron_list; + +/* LOCKING: requires that the GC lock is held */ +static MONO_PERMIT (need (sgen_gc_locked)) void +null_ephemerons_for_domain (MonoDomain *domain) +{ + EphemeronLinkNode *current = ephemeron_list, *prev = NULL; + + while (current) { + MonoObject *object = (MonoObject*)current->array; + + if (object) + SGEN_ASSERT (0, object->vtable, "Can't have objects without vtables."); + + if (object && object->vtable->domain == domain) { + EphemeronLinkNode *tmp = current; + + if (prev) + prev->next = current->next; + else + ephemeron_list = current->next; + + current = current->next; + sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK); + } else { + prev = current; + current = current->next; + } + } +} + +/* LOCKING: requires that the GC lock is held */ +void +sgen_client_clear_unreachable_ephemerons (ScanCopyContext ctx) +{ + CopyOrMarkObjectFunc copy_func = ctx.ops->copy_or_mark_object; + SgenGrayQueue *queue = ctx.queue; + EphemeronLinkNode *current = ephemeron_list, *prev = NULL; + Ephemeron *cur, *array_end; + GCObject *tombstone; + + while (current) { + MonoArray *array = current->array; + + if (!sgen_is_object_alive_for_current_gen ((GCObject*)array)) { + EphemeronLinkNode *tmp = current; + + SGEN_LOG (5, "Dead Ephemeron array at %p", array); + + if (prev) + prev->next = current->next; + else + ephemeron_list = current->next; + + current = current->next; + sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK); + + continue; + } + + copy_func ((GCObject**)&array, queue); + current->array = array; + + SGEN_LOG (5, "Clearing unreachable entries for ephemeron array at %p", array); + + cur = mono_array_addr (array, Ephemeron, 0); + array_end = cur + mono_array_length_fast (array); + tombstone = SGEN_LOAD_VTABLE ((GCObject*)array)->domain->ephemeron_tombstone; + + for (; cur < array_end; ++cur) { + GCObject *key = cur->key; + + if (!key || key == tombstone) + continue; + + SGEN_LOG (5, "[%zd] key %p (%s) value %p (%s)", cur - mono_array_addr (array, Ephemeron, 0), + key, sgen_is_object_alive_for_current_gen (key) ? "reachable" : "unreachable", + cur->value, cur->value && sgen_is_object_alive_for_current_gen (cur->value) ? "reachable" : "unreachable"); + + if (!sgen_is_object_alive_for_current_gen (key)) { + cur->key = tombstone; + cur->value = NULL; + continue; + } + } + prev = current; + current = current->next; + } +} + +/* +LOCKING: requires that the GC lock is held + +Limitations: We scan all ephemerons on every collection since the current design doesn't allow for a simple nursery/mature split. +*/ +gboolean +sgen_client_mark_ephemerons (ScanCopyContext ctx) +{ + CopyOrMarkObjectFunc copy_func = ctx.ops->copy_or_mark_object; + SgenGrayQueue *queue = ctx.queue; + gboolean nothing_marked = TRUE; + EphemeronLinkNode *current = ephemeron_list; + Ephemeron *cur, *array_end; + GCObject *tombstone; + + for (current = ephemeron_list; current; current = current->next) { + MonoArray *array = current->array; + SGEN_LOG (5, "Ephemeron array at %p", array); + + /*It has to be alive*/ + if (!sgen_is_object_alive_for_current_gen ((GCObject*)array)) { + SGEN_LOG (5, "\tnot reachable"); + continue; + } + + copy_func ((GCObject**)&array, queue); + + cur = mono_array_addr (array, Ephemeron, 0); + array_end = cur + mono_array_length_fast (array); + tombstone = SGEN_LOAD_VTABLE ((GCObject*)array)->domain->ephemeron_tombstone; + + for (; cur < array_end; ++cur) { + GCObject *key = cur->key; + + if (!key || key == tombstone) + continue; + + SGEN_LOG (5, "[%zd] key %p (%s) value %p (%s)", cur - mono_array_addr (array, Ephemeron, 0), + key, sgen_is_object_alive_for_current_gen (key) ? "reachable" : "unreachable", + cur->value, cur->value && sgen_is_object_alive_for_current_gen (cur->value) ? "reachable" : "unreachable"); + + if (sgen_is_object_alive_for_current_gen (key)) { + GCObject *value = cur->value; + + copy_func (&cur->key, queue); + if (value) { + if (!sgen_is_object_alive_for_current_gen (value)) + nothing_marked = FALSE; + copy_func (&cur->value, queue); + } + } + } + } + + SGEN_LOG (5, "Ephemeron run finished. Is it done %d", nothing_marked); + return nothing_marked; +} + +gboolean +mono_gc_ephemeron_array_add (MonoObject *obj) +{ + EphemeronLinkNode *node; + + LOCK_GC; + + node = (EphemeronLinkNode *)sgen_alloc_internal (INTERNAL_MEM_EPHEMERON_LINK); + if (!node) { + UNLOCK_GC; + return FALSE; + } + node->array = (MonoArray*)obj; + node->next = ephemeron_list; + ephemeron_list = node; + + SGEN_LOG (5, "Registered ephemeron array %p", obj); + + UNLOCK_GC; + return TRUE; +} + +/* + * Appdomain handling + */ + +static gboolean +need_remove_object_for_domain (GCObject *start, MonoDomain *domain) +{ + if (mono_object_domain (start) == domain) { + SGEN_LOG (4, "Need to cleanup object %p", start); + binary_protocol_cleanup (start, (gpointer)SGEN_LOAD_VTABLE (start), sgen_safe_object_get_size ((GCObject*)start)); + return TRUE; + } + return FALSE; +} + +static void +process_object_for_domain_clearing (GCObject *start, MonoDomain *domain) +{ + MonoVTable *vt = SGEN_LOAD_VTABLE (start); + if (vt->klass == mono_defaults.internal_thread_class) + g_assert (mono_object_domain (start) == mono_get_root_domain ()); + /* The object could be a proxy for an object in the domain + we're deleting. */ +#ifndef DISABLE_REMOTING + if (mono_defaults.real_proxy_class->supertypes && mono_class_has_parent_fast (vt->klass, mono_defaults.real_proxy_class)) { + MonoObject *server = ((MonoRealProxy*)start)->unwrapped_server; + + /* The server could already have been zeroed out, so + we need to check for that, too. */ + if (server && (!SGEN_LOAD_VTABLE (server) || mono_object_domain (server) == domain)) { + SGEN_LOG (4, "Cleaning up remote pointer in %p to object %p", start, server); + ((MonoRealProxy*)start)->unwrapped_server = NULL; + } + } +#endif +} + +static gboolean +clear_domain_process_object (GCObject *obj, MonoDomain *domain) +{ + gboolean remove; + + process_object_for_domain_clearing (obj, domain); + remove = need_remove_object_for_domain (obj, domain); + + if (remove && obj->synchronisation) { + guint32 dislink = mono_monitor_get_object_monitor_gchandle (obj); + if (dislink) + mono_gchandle_free (dislink); + } + + return remove; +} + +static void +clear_domain_process_minor_object_callback (GCObject *obj, size_t size, MonoDomain *domain) +{ + if (clear_domain_process_object (obj, domain)) { + CANARIFY_SIZE (size); + memset (obj, 0, size); + } +} + +static void +clear_domain_process_major_object_callback (GCObject *obj, size_t size, MonoDomain *domain) +{ + clear_domain_process_object (obj, domain); +} + +static void +clear_domain_free_major_non_pinned_object_callback (GCObject *obj, size_t size, MonoDomain *domain) +{ + if (need_remove_object_for_domain (obj, domain)) + major_collector.free_non_pinned_object (obj, size); +} + +static void +clear_domain_free_major_pinned_object_callback (GCObject *obj, size_t size, MonoDomain *domain) +{ + if (need_remove_object_for_domain (obj, domain)) + major_collector.free_pinned_object (obj, size); +} + +/* + * When appdomains are unloaded we can easily remove objects that have finalizers, + * but all the others could still be present in random places on the heap. + * We need a sweep to get rid of them even though it's going to be costly + * with big heaps. + * The reason we need to remove them is because we access the vtable and class + * structures to know the object size and the reference bitmap: once the domain is + * unloaded the point to random memory. + */ +void +mono_gc_clear_domain (MonoDomain * domain) +{ + LOSObject *bigobj, *prev; + int i; + + LOCK_GC; + + binary_protocol_domain_unload_begin (domain); + + sgen_stop_world (0); + + if (sgen_concurrent_collection_in_progress ()) + sgen_perform_collection (0, GENERATION_OLD, "clear domain", TRUE, FALSE); + SGEN_ASSERT (0, !sgen_concurrent_collection_in_progress (), "We just ordered a synchronous collection. Why are we collecting concurrently?"); + + major_collector.finish_sweeping (); + + sgen_process_fin_stage_entries (); + + sgen_clear_nursery_fragments (); + + FOREACH_THREAD (info) { + mono_handle_stack_free_domain ((HandleStack*)info->client_info.info.handle_stack, domain); + } FOREACH_THREAD_END + + if (sgen_mono_xdomain_checks && domain != mono_get_root_domain ()) { + sgen_scan_for_registered_roots_in_domain (domain, ROOT_TYPE_NORMAL); + sgen_scan_for_registered_roots_in_domain (domain, ROOT_TYPE_WBARRIER); + sgen_check_for_xdomain_refs (); + } + + /*Ephemerons and dislinks must be processed before LOS since they might end up pointing + to memory returned to the OS.*/ + null_ephemerons_for_domain (domain); + sgen_null_links_for_domain (domain); + + for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i) + sgen_remove_finalizers_if (object_in_domain_predicate, domain, i); + + sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, + (IterateObjectCallbackFunc)clear_domain_process_minor_object_callback, domain, FALSE, TRUE); + + /* We need two passes over major and large objects because + freeing such objects might give their memory back to the OS + (in the case of large objects) or obliterate its vtable + (pinned objects with major-copying or pinned and non-pinned + objects with major-mark&sweep), but we might need to + dereference a pointer from an object to another object if + the first object is a proxy. */ + major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)clear_domain_process_major_object_callback, domain); + for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) + clear_domain_process_object ((GCObject*)bigobj->data, domain); + + prev = NULL; + for (bigobj = los_object_list; bigobj;) { + if (need_remove_object_for_domain ((GCObject*)bigobj->data, domain)) { + LOSObject *to_free = bigobj; + if (prev) + prev->next = bigobj->next; + else + los_object_list = bigobj->next; + bigobj = bigobj->next; + SGEN_LOG (4, "Freeing large object %p", bigobj->data); + sgen_los_free_object (to_free); + continue; + } + prev = bigobj; + bigobj = bigobj->next; + } + major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_NON_PINNED, (IterateObjectCallbackFunc)clear_domain_free_major_non_pinned_object_callback, domain); + major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_PINNED, (IterateObjectCallbackFunc)clear_domain_free_major_pinned_object_callback, domain); + + if (domain == mono_get_root_domain ()) { + sgen_pin_stats_report (); + sgen_object_layout_dump (stdout); + } + + sgen_restart_world (0); + + binary_protocol_domain_unload_end (domain); + binary_protocol_flush_buffers (FALSE); + + UNLOCK_GC; +} + +/* + * Allocation + */ + +void* +mono_gc_alloc_obj (MonoVTable *vtable, size_t size) +{ + MonoObject *obj = sgen_alloc_obj (vtable, size); + + if (G_UNLIKELY (mono_profiler_allocations_enabled ()) && obj) + MONO_PROFILER_RAISE (gc_allocation, (obj)); + + return obj; +} + +void* +mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size) +{ + MonoObject *obj = sgen_alloc_obj_pinned (vtable, size); + + if (G_UNLIKELY (mono_profiler_allocations_enabled ()) && obj) + MONO_PROFILER_RAISE (gc_allocation, (obj)); + + return obj; +} + +void* +mono_gc_alloc_mature (MonoVTable *vtable, size_t size) +{ + MonoObject *obj = sgen_alloc_obj_mature (vtable, size); + + if (G_UNLIKELY (mono_profiler_allocations_enabled ()) && obj) + MONO_PROFILER_RAISE (gc_allocation, (obj)); + + return obj; +} + +/** + * mono_gc_alloc_fixed: + */ +void* +mono_gc_alloc_fixed (size_t size, MonoGCDescriptor descr, MonoGCRootSource source, void *key, const char *msg) +{ + /* FIXME: do a single allocation */ + void *res = g_calloc (1, size); + if (!res) + return NULL; + if (!mono_gc_register_root ((char *)res, size, descr, source, key, msg)) { + g_free (res); + res = NULL; + } + return res; +} + +/** + * mono_gc_free_fixed: + */ +void +mono_gc_free_fixed (void* addr) +{ + mono_gc_deregister_root ((char *)addr); + g_free (addr); +} + +/* + * Managed allocator + */ + +static MonoMethod* alloc_method_cache [ATYPE_NUM]; +static MonoMethod* slowpath_alloc_method_cache [ATYPE_NUM]; +static MonoMethod* profiler_alloc_method_cache [ATYPE_NUM]; +static gboolean use_managed_allocator = TRUE; + +#ifdef MANAGED_ALLOCATION +// Cache the SgenThreadInfo pointer in a local 'var'. +#define EMIT_TLS_ACCESS_VAR(mb, var) \ + do { \ + var = mono_mb_add_local ((mb), &mono_defaults.int_class->byval_arg); \ + mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \ + mono_mb_emit_byte ((mb), CEE_MONO_TLS); \ + mono_mb_emit_i4 ((mb), TLS_KEY_SGEN_THREAD_INFO); \ + mono_mb_emit_stloc ((mb), (var)); \ + } while (0) + +#define EMIT_TLS_ACCESS_IN_CRITICAL_REGION_ADDR(mb, var) \ + do { \ + mono_mb_emit_ldloc ((mb), (var)); \ + mono_mb_emit_icon ((mb), MONO_STRUCT_OFFSET (SgenClientThreadInfo, in_critical_region)); \ + mono_mb_emit_byte ((mb), CEE_ADD); \ + } while (0) + +#define EMIT_TLS_ACCESS_NEXT_ADDR(mb, var) do { \ + mono_mb_emit_ldloc ((mb), (var)); \ + mono_mb_emit_icon ((mb), MONO_STRUCT_OFFSET (SgenThreadInfo, tlab_next)); \ + mono_mb_emit_byte ((mb), CEE_ADD); \ + } while (0) + +#define EMIT_TLS_ACCESS_TEMP_END(mb, var) do { \ + mono_mb_emit_ldloc ((mb), (var)); \ + mono_mb_emit_icon ((mb), MONO_STRUCT_OFFSET (SgenThreadInfo, tlab_temp_end)); \ + mono_mb_emit_byte ((mb), CEE_ADD); \ + mono_mb_emit_byte ((mb), CEE_LDIND_I); \ + } while (0) + +/* FIXME: Do this in the JIT, where specialized allocation sequences can be created + * for each class. This is currently not easy to do, as it is hard to generate basic + * blocks + branches, but it is easy with the linear IL codebase. + * + * For this to work we'd need to solve the TLAB race, first. Now we + * require the allocator to be in a few known methods to make sure + * that they are executed atomically via the restart mechanism. + */ +static MonoMethod* +create_allocator (int atype, ManagedAllocatorVariant variant) +{ + int p_var, size_var, real_size_var, thread_var G_GNUC_UNUSED; + gboolean slowpath = variant == MANAGED_ALLOCATOR_SLOW_PATH; + gboolean profiler = variant == MANAGED_ALLOCATOR_PROFILER; + guint32 fastpath_branch, max_size_branch, no_oom_branch; + MonoMethodBuilder *mb; + MonoMethod *res; + MonoMethodSignature *csig; + static gboolean registered = FALSE; + int tlab_next_addr_var, new_next_var; + const char *name = NULL; + WrapperInfo *info; + int num_params, i; + + if (!registered) { + mono_register_jit_icall (mono_gc_alloc_obj, "mono_gc_alloc_obj", mono_create_icall_signature ("object ptr int"), FALSE); + mono_register_jit_icall (mono_gc_alloc_vector, "mono_gc_alloc_vector", mono_create_icall_signature ("object ptr int int"), FALSE); + mono_register_jit_icall (mono_gc_alloc_string, "mono_gc_alloc_string", mono_create_icall_signature ("object ptr int int32"), FALSE); + mono_register_jit_icall (mono_profiler_raise_gc_allocation, "mono_profiler_raise_gc_allocation", mono_create_icall_signature ("void object"), FALSE); + registered = TRUE; + } + + if (atype == ATYPE_SMALL) { + name = slowpath ? "SlowAllocSmall" : (profiler ? "ProfilerAllocSmall" : "AllocSmall"); + } else if (atype == ATYPE_NORMAL) { + name = slowpath ? "SlowAlloc" : (profiler ? "ProfilerAlloc" : "Alloc"); + } else if (atype == ATYPE_VECTOR) { + name = slowpath ? "SlowAllocVector" : (profiler ? "ProfilerAllocVector" : "AllocVector"); + } else if (atype == ATYPE_STRING) { + name = slowpath ? "SlowAllocString" : (profiler ? "ProfilerAllocString" : "AllocString"); + } else { + g_assert_not_reached (); + } + + if (atype == ATYPE_NORMAL) + num_params = 1; + else + num_params = 2; + + csig = mono_metadata_signature_alloc (mono_defaults.corlib, num_params); + if (atype == ATYPE_STRING) { + csig->ret = &mono_defaults.string_class->byval_arg; + csig->params [0] = &mono_defaults.int_class->byval_arg; + csig->params [1] = &mono_defaults.int32_class->byval_arg; + } else { + csig->ret = &mono_defaults.object_class->byval_arg; + for (i = 0; i < num_params; i++) + csig->params [i] = &mono_defaults.int_class->byval_arg; + } + + mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC); + +#ifdef ENABLE_ILGEN + if (slowpath) { + switch (atype) { + case ATYPE_NORMAL: + case ATYPE_SMALL: + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_icall (mb, ves_icall_object_new_specific); + break; + case ATYPE_VECTOR: + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_icall (mb, ves_icall_array_new_specific); + break; + case ATYPE_STRING: + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_icall (mb, ves_icall_string_alloc); + break; + default: + g_assert_not_reached (); + } + + goto done; + } + + /* + * Tls access might call foreign code or code without jinfo. This can + * only happen if we are outside of the critical region. + */ + EMIT_TLS_ACCESS_VAR (mb, thread_var); + + size_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + if (atype == ATYPE_SMALL) { + /* size_var = size_arg */ + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_stloc (mb, size_var); + } else if (atype == ATYPE_NORMAL) { + /* size = vtable->klass->instance_size; */ + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoVTable, klass)); + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoClass, instance_size)); + mono_mb_emit_byte (mb, CEE_ADD); + /* FIXME: assert instance_size stays a 4 byte integer */ + mono_mb_emit_byte (mb, CEE_LDIND_U4); + mono_mb_emit_byte (mb, CEE_CONV_I); + mono_mb_emit_stloc (mb, size_var); + } else if (atype == ATYPE_VECTOR) { + MonoExceptionClause *clause; + int pos, pos_leave, pos_error; + MonoClass *oom_exc_class; + MonoMethod *ctor; + + /* + * n > MONO_ARRAY_MAX_INDEX => OutOfMemoryException + * n < 0 => OverflowException + * + * We can do an unsigned comparison to catch both cases, then in the error + * case compare signed to distinguish between them. + */ + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_icon (mb, MONO_ARRAY_MAX_INDEX); + mono_mb_emit_byte (mb, CEE_CONV_U); + pos = mono_mb_emit_short_branch (mb, CEE_BLE_UN_S); + + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN); + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_icon (mb, 0); + pos_error = mono_mb_emit_short_branch (mb, CEE_BLT_S); + mono_mb_emit_exception (mb, "OutOfMemoryException", NULL); + mono_mb_patch_short_branch (mb, pos_error); + mono_mb_emit_exception (mb, "OverflowException", NULL); + + mono_mb_patch_short_branch (mb, pos); + + clause = (MonoExceptionClause *)mono_image_alloc0 (mono_defaults.corlib, sizeof (MonoExceptionClause)); + clause->try_offset = mono_mb_get_label (mb); + + /* vtable->klass->sizes.element_size */ + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoVTable, klass)); + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoClass, sizes)); + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_byte (mb, CEE_LDIND_U4); + mono_mb_emit_byte (mb, CEE_CONV_I); + + /* * n */ + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_byte (mb, CEE_MUL_OVF_UN); + /* + sizeof (MonoArray) */ + mono_mb_emit_icon (mb, MONO_SIZEOF_MONO_ARRAY); + mono_mb_emit_byte (mb, CEE_ADD_OVF_UN); + mono_mb_emit_stloc (mb, size_var); + + pos_leave = mono_mb_emit_branch (mb, CEE_LEAVE); + + /* catch */ + clause->flags = MONO_EXCEPTION_CLAUSE_NONE; + clause->try_len = mono_mb_get_pos (mb) - clause->try_offset; + clause->data.catch_class = mono_class_load_from_name (mono_defaults.corlib, + "System", "OverflowException"); + clause->handler_offset = mono_mb_get_label (mb); + + oom_exc_class = mono_class_load_from_name (mono_defaults.corlib, + "System", "OutOfMemoryException"); + ctor = mono_class_get_method_from_name (oom_exc_class, ".ctor", 0); + g_assert (ctor); + + mono_mb_emit_byte (mb, CEE_POP); + mono_mb_emit_op (mb, CEE_NEWOBJ, ctor); + mono_mb_emit_byte (mb, CEE_THROW); + + clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset; + mono_mb_set_clauses (mb, 1, clause); + mono_mb_patch_branch (mb, pos_leave); + /* end catch */ + } else if (atype == ATYPE_STRING) { + int pos; + + /* + * a string allocator method takes the args: (vtable, len) + * + * bytes = offsetof (MonoString, chars) + ((len + 1) * 2) + * + * condition: + * + * bytes <= INT32_MAX - (SGEN_ALLOC_ALIGN - 1) + * + * therefore: + * + * offsetof (MonoString, chars) + ((len + 1) * 2) <= INT32_MAX - (SGEN_ALLOC_ALIGN - 1) + * len <= (INT32_MAX - (SGEN_ALLOC_ALIGN - 1) - offsetof (MonoString, chars)) / 2 - 1 + */ + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_icon (mb, (INT32_MAX - (SGEN_ALLOC_ALIGN - 1) - MONO_STRUCT_OFFSET (MonoString, chars)) / 2 - 1); + pos = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S); + + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN); + mono_mb_emit_exception (mb, "OutOfMemoryException", NULL); + mono_mb_patch_short_branch (mb, pos); + + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_icon (mb, 1); + mono_mb_emit_byte (mb, MONO_CEE_SHL); + //WE manually fold the above + 2 here + mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoString, chars) + 2); + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_stloc (mb, size_var); + } else { + g_assert_not_reached (); + } + +#ifdef MANAGED_ALLOCATOR_CAN_USE_CRITICAL_REGION + EMIT_TLS_ACCESS_IN_CRITICAL_REGION_ADDR (mb, thread_var); + mono_mb_emit_byte (mb, CEE_LDC_I4_1); + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_ATOMIC_STORE_I4); + mono_mb_emit_i4 (mb, MONO_MEMORY_BARRIER_NONE); +#endif + + if (nursery_canaries_enabled ()) { + real_size_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + mono_mb_emit_ldloc (mb, size_var); + mono_mb_emit_stloc(mb, real_size_var); + } + else + real_size_var = size_var; + + /* size += ALLOC_ALIGN - 1; */ + mono_mb_emit_ldloc (mb, size_var); + mono_mb_emit_icon (mb, SGEN_ALLOC_ALIGN - 1); + mono_mb_emit_byte (mb, CEE_ADD); + /* size &= ~(ALLOC_ALIGN - 1); */ + mono_mb_emit_icon (mb, ~(SGEN_ALLOC_ALIGN - 1)); + mono_mb_emit_byte (mb, CEE_AND); + mono_mb_emit_stloc (mb, size_var); + + /* if (size > MAX_SMALL_OBJ_SIZE) goto slowpath */ + if (atype != ATYPE_SMALL) { + mono_mb_emit_ldloc (mb, size_var); + mono_mb_emit_icon (mb, SGEN_MAX_SMALL_OBJ_SIZE); + max_size_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S); + } + + /* + * We need to modify tlab_next, but the JIT only supports reading, so we read + * another tls var holding its address instead. + */ + + /* tlab_next_addr (local) = tlab_next_addr (TLS var) */ + tlab_next_addr_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + EMIT_TLS_ACCESS_NEXT_ADDR (mb, thread_var); + mono_mb_emit_stloc (mb, tlab_next_addr_var); + + /* p = (void**)tlab_next; */ + p_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + mono_mb_emit_ldloc (mb, tlab_next_addr_var); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_stloc (mb, p_var); + + /* new_next = (char*)p + size; */ + new_next_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + mono_mb_emit_ldloc (mb, p_var); + mono_mb_emit_ldloc (mb, size_var); + mono_mb_emit_byte (mb, CEE_CONV_I); + mono_mb_emit_byte (mb, CEE_ADD); + + if (nursery_canaries_enabled ()) { + mono_mb_emit_icon (mb, CANARY_SIZE); + mono_mb_emit_byte (mb, CEE_ADD); + } + mono_mb_emit_stloc (mb, new_next_var); + + /* if (G_LIKELY (new_next < tlab_temp_end)) */ + mono_mb_emit_ldloc (mb, new_next_var); + EMIT_TLS_ACCESS_TEMP_END (mb, thread_var); + fastpath_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S); + + /* Slowpath */ + if (atype != ATYPE_SMALL) + mono_mb_patch_short_branch (mb, max_size_branch); + + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN); + /* + * We are no longer in a critical section. We need to do this before calling + * to unmanaged land in order to avoid stw deadlocks since unmanaged code + * might take locks. + */ +#ifdef MANAGED_ALLOCATOR_CAN_USE_CRITICAL_REGION + EMIT_TLS_ACCESS_IN_CRITICAL_REGION_ADDR (mb, thread_var); + mono_mb_emit_byte (mb, CEE_LDC_I4_0); + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_ATOMIC_STORE_I4); + mono_mb_emit_i4 (mb, MONO_MEMORY_BARRIER_NONE); +#endif + + /* FIXME: mono_gc_alloc_obj takes a 'size_t' as an argument, not an int32 */ + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldloc (mb, real_size_var); + if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) { + mono_mb_emit_icall (mb, mono_gc_alloc_obj); + } else if (atype == ATYPE_VECTOR) { + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_icall (mb, mono_gc_alloc_vector); + } else if (atype == ATYPE_STRING) { + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_icall (mb, mono_gc_alloc_string); + } else { + g_assert_not_reached (); + } + + /* if (ret == NULL) throw OOM; */ + mono_mb_emit_byte (mb, CEE_DUP); + no_oom_branch = mono_mb_emit_branch (mb, CEE_BRTRUE); + mono_mb_emit_exception (mb, "OutOfMemoryException", NULL); + + mono_mb_patch_branch (mb, no_oom_branch); + mono_mb_emit_byte (mb, CEE_RET); + + /* Fastpath */ + mono_mb_patch_short_branch (mb, fastpath_branch); + + /* FIXME: Memory barrier */ + + /* tlab_next = new_next */ + mono_mb_emit_ldloc (mb, tlab_next_addr_var); + mono_mb_emit_ldloc (mb, new_next_var); + mono_mb_emit_byte (mb, CEE_STIND_I); + + /* *p = vtable; */ + mono_mb_emit_ldloc (mb, p_var); + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_byte (mb, CEE_STIND_I); + + /* mark object end with nursery word */ + if (nursery_canaries_enabled ()) { + mono_mb_emit_ldloc (mb, p_var); + mono_mb_emit_ldloc (mb, real_size_var); + mono_mb_emit_byte (mb, MONO_CEE_ADD); + mono_mb_emit_icon8 (mb, (mword) CANARY_STRING); + mono_mb_emit_icon (mb, CANARY_SIZE); + mono_mb_emit_byte (mb, MONO_CEE_PREFIX1); + mono_mb_emit_byte (mb, CEE_CPBLK); + } + + if (atype == ATYPE_VECTOR) { + /* arr->max_length = max_length; */ + mono_mb_emit_ldloc (mb, p_var); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoArray, max_length)); + mono_mb_emit_ldarg (mb, 1); +#ifdef MONO_BIG_ARRAYS + mono_mb_emit_byte (mb, CEE_STIND_I); +#else + mono_mb_emit_byte (mb, CEE_STIND_I4); +#endif + } else if (atype == ATYPE_STRING) { + /* need to set length and clear the last char */ + /* s->length = len; */ + mono_mb_emit_ldloc (mb, p_var); + mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoString, length)); + mono_mb_emit_byte (mb, MONO_CEE_ADD); + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_byte (mb, MONO_CEE_STIND_I4); + } + +#ifdef MANAGED_ALLOCATOR_CAN_USE_CRITICAL_REGION + EMIT_TLS_ACCESS_IN_CRITICAL_REGION_ADDR (mb, thread_var); + mono_mb_emit_byte (mb, CEE_LDC_I4_0); + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_ATOMIC_STORE_I4); +#else + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_MEMORY_BARRIER); +#endif + /* + We must make sure both vtable and max_length are globaly visible before returning to managed land. + */ + mono_mb_emit_i4 (mb, MONO_MEMORY_BARRIER_REL); + + /* return p */ + mono_mb_emit_ldloc (mb, p_var); + + done: + + /* + * It's important that we do this outside of the critical region as we + * will be invoking arbitrary code. + */ + if (profiler) { + /* + * if (G_UNLIKELY (*&mono_profiler_state.gc_allocation_count)) { + * mono_profiler_raise_gc_allocation (p); + * } + */ + + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_LDPTR_PROFILER_ALLOCATION_COUNT); + mono_mb_emit_byte (mb, CEE_LDIND_U4); + + int prof_br = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S); + + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN); + mono_mb_emit_byte (mb, CEE_DUP); + mono_mb_emit_icall (mb, mono_profiler_raise_gc_allocation); + + mono_mb_patch_short_branch (mb, prof_br); + } + + mono_mb_emit_byte (mb, CEE_RET); +#endif + + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); + info->d.alloc.gc_name = "sgen"; + info->d.alloc.alloc_type = atype; + +#ifdef ENABLE_ILGEN + mb->init_locals = FALSE; +#endif + + res = mono_mb_create (mb, csig, 8, info); + mono_mb_free (mb); + + + return res; +} +#endif + +int +mono_gc_get_aligned_size_for_allocator (int size) +{ + return SGEN_ALIGN_UP (size); +} + +/* + * Generate an allocator method implementing the fast path of mono_gc_alloc_obj (). + * The signature of the called method is: + * object allocate (MonoVTable *vtable) + */ +MonoMethod* +mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size) +{ +#ifdef MANAGED_ALLOCATION + ManagedAllocatorVariant variant = mono_profiler_allocations_enabled () ? + MANAGED_ALLOCATOR_PROFILER : MANAGED_ALLOCATOR_REGULAR; + + if (collect_before_allocs) + return NULL; + if (klass->instance_size > tlab_size) + return NULL; + if (known_instance_size && ALIGN_TO (klass->instance_size, SGEN_ALLOC_ALIGN) >= SGEN_MAX_SMALL_OBJ_SIZE) + return NULL; + if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass) || klass->has_weak_fields) + return NULL; + if (klass->rank) + return NULL; + if (klass->byval_arg.type == MONO_TYPE_STRING) + return mono_gc_get_managed_allocator_by_type (ATYPE_STRING, variant); + /* Generic classes have dynamic field and can go above MAX_SMALL_OBJ_SIZE. */ + if (known_instance_size) + return mono_gc_get_managed_allocator_by_type (ATYPE_SMALL, variant); + else + return mono_gc_get_managed_allocator_by_type (ATYPE_NORMAL, variant); +#else + return NULL; +#endif +} + +MonoMethod* +mono_gc_get_managed_array_allocator (MonoClass *klass) +{ +#ifdef MANAGED_ALLOCATION + if (klass->rank != 1) + return NULL; + if (has_per_allocation_action) + return NULL; + g_assert (!mono_class_has_finalizer (klass) && !mono_class_is_marshalbyref (klass)); + + return mono_gc_get_managed_allocator_by_type (ATYPE_VECTOR, mono_profiler_allocations_enabled () ? + MANAGED_ALLOCATOR_PROFILER : MANAGED_ALLOCATOR_REGULAR); +#else + return NULL; +#endif +} + +void +sgen_set_use_managed_allocator (gboolean flag) +{ + use_managed_allocator = flag; +} + +MonoMethod* +mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant) +{ +#ifdef MANAGED_ALLOCATION + MonoMethod *res; + MonoMethod **cache; + + if (variant != MANAGED_ALLOCATOR_SLOW_PATH && !use_managed_allocator) + return NULL; + + switch (variant) { + case MANAGED_ALLOCATOR_REGULAR: cache = alloc_method_cache; break; + case MANAGED_ALLOCATOR_SLOW_PATH: cache = slowpath_alloc_method_cache; break; + case MANAGED_ALLOCATOR_PROFILER: cache = profiler_alloc_method_cache; break; + default: g_assert_not_reached (); break; + } + + res = cache [atype]; + if (res) + return res; + + res = create_allocator (atype, variant); + LOCK_GC; + if (cache [atype]) { + mono_free_method (res); + res = cache [atype]; + } else { + mono_memory_barrier (); + cache [atype] = res; + } + UNLOCK_GC; + + return res; +#else + return NULL; +#endif +} + +guint32 +mono_gc_get_managed_allocator_types (void) +{ + return ATYPE_NUM; +} + +gboolean +sgen_is_managed_allocator (MonoMethod *method) +{ + int i; + + for (i = 0; i < ATYPE_NUM; ++i) + if (method == alloc_method_cache [i] || method == slowpath_alloc_method_cache [i] || method == profiler_alloc_method_cache [i]) + return TRUE; + return FALSE; +} + +gboolean +sgen_has_managed_allocator (void) +{ + int i; + + for (i = 0; i < ATYPE_NUM; ++i) + if (alloc_method_cache [i] || slowpath_alloc_method_cache [i] || profiler_alloc_method_cache [i]) + return TRUE; + return FALSE; +} + +#define ARRAY_OBJ_INDEX(ptr,array,elem_size) (((char*)(ptr) - ((char*)(array) + G_STRUCT_OFFSET (MonoArray, vector))) / (elem_size)) + +gboolean +sgen_client_cardtable_scan_object (GCObject *obj, guint8 *cards, ScanCopyContext ctx) +{ + MonoVTable *vt = SGEN_LOAD_VTABLE (obj); + MonoClass *klass = vt->klass; + + SGEN_ASSERT (0, SGEN_VTABLE_HAS_REFERENCES (vt), "Why would we ever call this on reference-free objects?"); + + if (vt->rank) { + MonoArray *arr = (MonoArray*)obj; + guint8 *card_data, *card_base; + guint8 *card_data_end; + char *obj_start = (char *)sgen_card_table_align_pointer (obj); + mword bounds_size; + mword obj_size = sgen_mono_array_size (vt, arr, &bounds_size, sgen_vtable_get_descriptor (vt)); + /* We don't want to scan the bounds entries at the end of multidimensional arrays */ + char *obj_end = (char*)obj + obj_size - bounds_size; + size_t card_count; + size_t extra_idx = 0; + + mword desc = (mword)klass->element_class->gc_descr; + int elem_size = mono_array_element_size (klass); + +#ifdef SGEN_HAVE_OVERLAPPING_CARDS + guint8 *overflow_scan_end = NULL; +#endif + +#ifdef SGEN_OBJECT_LAYOUT_STATISTICS + if (klass->element_class->valuetype) + sgen_object_layout_scanned_vtype_array (); + else + sgen_object_layout_scanned_ref_array (); +#endif + + if (cards) + card_data = cards; + else + card_data = sgen_card_table_get_card_scan_address ((mword)obj); + + card_base = card_data; + card_count = sgen_card_table_number_of_cards_in_range ((mword)obj, obj_size); + card_data_end = card_data + card_count; + + +#ifdef SGEN_HAVE_OVERLAPPING_CARDS + /*Check for overflow and if so, setup to scan in two steps*/ + if (!cards && card_data_end >= SGEN_SHADOW_CARDTABLE_END) { + overflow_scan_end = sgen_shadow_cardtable + (card_data_end - SGEN_SHADOW_CARDTABLE_END); + card_data_end = SGEN_SHADOW_CARDTABLE_END; + } + +LOOP_HEAD: +#endif + + card_data = sgen_find_next_card (card_data, card_data_end); + for (; card_data < card_data_end; card_data = sgen_find_next_card (card_data + 1, card_data_end)) { + size_t index; + size_t idx = (card_data - card_base) + extra_idx; + char *start = (char*)(obj_start + idx * CARD_SIZE_IN_BYTES); + char *card_end = start + CARD_SIZE_IN_BYTES; + char *first_elem, *elem; + + HEAVY_STAT (++los_marked_cards); + + if (!cards) + sgen_card_table_prepare_card_for_scanning (card_data); + + card_end = MIN (card_end, obj_end); + + if (start <= (char*)arr->vector) + index = 0; + else + index = ARRAY_OBJ_INDEX (start, obj, elem_size); + + elem = first_elem = (char*)mono_array_addr_with_size_fast ((MonoArray*)obj, elem_size, index); + if (klass->element_class->valuetype) { + ScanVTypeFunc scan_vtype_func = ctx.ops->scan_vtype; + + for (; elem < card_end; elem += elem_size) + scan_vtype_func (obj, elem, desc, ctx.queue BINARY_PROTOCOL_ARG (elem_size)); + } else { + ScanPtrFieldFunc scan_ptr_field_func = ctx.ops->scan_ptr_field; + + HEAVY_STAT (++los_array_cards); + for (; elem < card_end; elem += SIZEOF_VOID_P) + scan_ptr_field_func (obj, (GCObject**)elem, ctx.queue); + } + + binary_protocol_card_scan (first_elem, elem - first_elem); + } + +#ifdef SGEN_HAVE_OVERLAPPING_CARDS + if (overflow_scan_end) { + extra_idx = card_data - card_base; + card_base = card_data = sgen_shadow_cardtable; + card_data_end = overflow_scan_end; + overflow_scan_end = NULL; + goto LOOP_HEAD; + } +#endif + return TRUE; + } + + return FALSE; +} + +/* + * Array and string allocation + */ + +void* +mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length) +{ + MonoArray *arr; + TLAB_ACCESS_INIT; + + if (!SGEN_CAN_ALIGN_UP (size)) + return NULL; + +#ifndef DISABLE_CRITICAL_REGION + ENTER_CRITICAL_REGION; + arr = (MonoArray*)sgen_try_alloc_obj_nolock (vtable, size); + if (arr) { + /*This doesn't require fencing since EXIT_CRITICAL_REGION already does it for us*/ + arr->max_length = (mono_array_size_t)max_length; + EXIT_CRITICAL_REGION; + goto done; + } + EXIT_CRITICAL_REGION; +#endif + + LOCK_GC; + + arr = (MonoArray*)sgen_alloc_obj_nolock (vtable, size); + if (G_UNLIKELY (!arr)) { + UNLOCK_GC; + return NULL; + } + + arr->max_length = (mono_array_size_t)max_length; + + UNLOCK_GC; + + done: + if (G_UNLIKELY (mono_profiler_allocations_enabled ())) + MONO_PROFILER_RAISE (gc_allocation, (&arr->obj)); + + SGEN_ASSERT (6, SGEN_ALIGN_UP (size) == SGEN_ALIGN_UP (sgen_client_par_object_get_size (vtable, (GCObject*)arr)), "Vector has incorrect size."); + return arr; +} + +void* +mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size) +{ + MonoArray *arr; + MonoArrayBounds *bounds; + TLAB_ACCESS_INIT; + + if (!SGEN_CAN_ALIGN_UP (size)) + return NULL; + +#ifndef DISABLE_CRITICAL_REGION + ENTER_CRITICAL_REGION; + arr = (MonoArray*)sgen_try_alloc_obj_nolock (vtable, size); + if (arr) { + /*This doesn't require fencing since EXIT_CRITICAL_REGION already does it for us*/ + arr->max_length = (mono_array_size_t)max_length; + + bounds = (MonoArrayBounds*)((char*)arr + size - bounds_size); + arr->bounds = bounds; + EXIT_CRITICAL_REGION; + goto done; + } + EXIT_CRITICAL_REGION; +#endif + + LOCK_GC; + + arr = (MonoArray*)sgen_alloc_obj_nolock (vtable, size); + if (G_UNLIKELY (!arr)) { + UNLOCK_GC; + return NULL; + } + + arr->max_length = (mono_array_size_t)max_length; + + bounds = (MonoArrayBounds*)((char*)arr + size - bounds_size); + arr->bounds = bounds; + + UNLOCK_GC; + + done: + if (G_UNLIKELY (mono_profiler_allocations_enabled ())) + MONO_PROFILER_RAISE (gc_allocation, (&arr->obj)); + + SGEN_ASSERT (6, SGEN_ALIGN_UP (size) == SGEN_ALIGN_UP (sgen_client_par_object_get_size (vtable, (GCObject*)arr)), "Array has incorrect size."); + return arr; +} + +void* +mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len) +{ + MonoString *str; + TLAB_ACCESS_INIT; + + if (!SGEN_CAN_ALIGN_UP (size)) + return NULL; + +#ifndef DISABLE_CRITICAL_REGION + ENTER_CRITICAL_REGION; + str = (MonoString*)sgen_try_alloc_obj_nolock (vtable, size); + if (str) { + /*This doesn't require fencing since EXIT_CRITICAL_REGION already does it for us*/ + str->length = len; + EXIT_CRITICAL_REGION; + goto done; + } + EXIT_CRITICAL_REGION; +#endif + + LOCK_GC; + + str = (MonoString*)sgen_alloc_obj_nolock (vtable, size); + if (G_UNLIKELY (!str)) { + UNLOCK_GC; + return NULL; + } + + str->length = len; + + UNLOCK_GC; + + done: + if (G_UNLIKELY (mono_profiler_allocations_enabled ())) + MONO_PROFILER_RAISE (gc_allocation, (&str->object)); + + return str; +} + +/* + * Strings + */ + +void +mono_gc_set_string_length (MonoString *str, gint32 new_length) +{ + mono_unichar2 *new_end = str->chars + new_length; + + /* zero the discarded string. This null-delimits the string and allows + * the space to be reclaimed by SGen. */ + + if (nursery_canaries_enabled () && sgen_ptr_in_nursery (str)) { + CHECK_CANARY_FOR_OBJECT ((GCObject*)str, TRUE); + memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2) + CANARY_SIZE); + memcpy (new_end + 1 , CANARY_STRING, CANARY_SIZE); + } else { + memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2)); + } + + str->length = new_length; +} + +/* + * Profiling + */ + +#define GC_ROOT_NUM 32 +#define SPECIAL_ADDRESS_FIN_QUEUE ((void*)1) +#define SPECIAL_ADDRESS_CRIT_FIN_QUEUE ((void*)2) + +typedef struct { + int count; /* must be the first field */ + void *addresses [GC_ROOT_NUM]; + void *objects [GC_ROOT_NUM]; +} GCRootReport; + +static void +notify_gc_roots (GCRootReport *report) +{ + if (!report->count) + return; + MONO_PROFILER_RAISE (gc_roots, (report->count, (const mono_byte *const *)report->addresses, (MonoObject *const *) report->objects)); + report->count = 0; +} + +static void +report_gc_root (GCRootReport *report, void *address, void *object) +{ + if (report->count == GC_ROOT_NUM) + notify_gc_roots (report); + report->addresses [report->count] = address; + report->objects [report->count] = object; + report->count++; +} + +static void +single_arg_report_root (MonoObject **obj, void *gc_data) +{ + GCRootReport *report = gc_data; + if (*obj) + report_gc_root (report, obj, *obj); +} + +static void +two_args_report_root (void *address, MonoObject *obj, void *gc_data) +{ + GCRootReport *report = gc_data; + if (obj) + report_gc_root (report, address, obj); +} + +static void +precisely_report_roots_from (GCRootReport *report, void** start_root, void** end_root, mword desc) +{ + switch (desc & ROOT_DESC_TYPE_MASK) { + case ROOT_DESC_BITMAP: + desc >>= ROOT_DESC_TYPE_SHIFT; + while (desc) { + if ((desc & 1) && *start_root) + report_gc_root (report, start_root, *start_root); + desc >>= 1; + start_root++; + } + return; + case ROOT_DESC_COMPLEX: { + gsize *bitmap_data = (gsize *)sgen_get_complex_descriptor_bitmap (desc); + gsize bwords = (*bitmap_data) - 1; + void **start_run = start_root; + bitmap_data++; + while (bwords-- > 0) { + gsize bmap = *bitmap_data++; + void **objptr = start_run; + while (bmap) { + if ((bmap & 1) && *objptr) + report_gc_root (report, objptr, *objptr); + bmap >>= 1; + ++objptr; + } + start_run += GC_BITS_PER_WORD; + } + break; + } + case ROOT_DESC_VECTOR: { + void **p; + + for (p = start_root; p < end_root; p++) { + if (*p) + report_gc_root (report, p, *p); + } + break; + } + case ROOT_DESC_USER: { + MonoGCRootMarkFunc marker = (MonoGCRootMarkFunc)sgen_get_user_descriptor_func (desc); + + if ((void*)marker == (void*)sgen_mark_normal_gc_handles) + sgen_gc_handles_report_roots (two_args_report_root, report); + else + marker ((MonoObject**)start_root, single_arg_report_root, report); + break; + } + case ROOT_DESC_RUN_LEN: + g_assert_not_reached (); + default: + g_assert_not_reached (); + } +} + +static void +report_pinning_roots (GCRootReport *report, void **start, void **end) +{ + while (start < end) { + mword addr = (mword)*start; + addr &= ~(SGEN_ALLOC_ALIGN - 1); + if (addr) + report_gc_root (report, start, (void*)addr); + + start++; + } +} + +static SgenPointerQueue pinned_objects = SGEN_POINTER_QUEUE_INIT (INTERNAL_MEM_MOVED_OBJECT); +static mword lower_bound, upper_bound; + +static GCObject* +find_pinned_obj (char *addr) +{ + size_t idx = sgen_pointer_queue_search (&pinned_objects, addr); + + if (idx != pinned_objects.next_slot) { + if (pinned_objects.data [idx] == addr) + return pinned_objects.data [idx]; + if (idx == 0) + return NULL; + } + + GCObject *obj = pinned_objects.data [idx - 1]; + if (addr > (char*)obj && addr < ((char*)obj + sgen_safe_object_get_size (obj))) + return obj; + return NULL; +} + + +/* + * We pass @root_report_address so register are properly accounted towards their thread +*/ +static void +report_conservative_roots (GCRootReport *report, char *root_report_address, void **start, void **end) +{ + while (start < end) { + mword addr = (mword)*start; + addr &= ~(SGEN_ALLOC_ALIGN - 1); + + if (addr < lower_bound || addr > upper_bound) { + ++start; + continue; + } + + GCObject *obj = find_pinned_obj ((char*)addr); + if (obj) + report_gc_root (report, root_report_address, obj); + start++; + } +} + +typedef struct { + gboolean precise; + GCRootReport *report; + SgenThreadInfo *info; +} ReportHandleStackRoot; + +static void +report_handle_stack_root (gpointer *ptr, gpointer user_data) +{ + ReportHandleStackRoot *ud = user_data; + GCRootReport *report = ud->report; + gpointer addr = ud->info->client_info.info.handle_stack; + + // Note: We know that *ptr != NULL. + if (ud->precise) + report_gc_root (report, addr, *ptr); + else + report_conservative_roots (report, addr, ptr, ptr + 1); +} + +static void +report_handle_stack_roots (GCRootReport *report, SgenThreadInfo *info, gboolean precise) +{ + ReportHandleStackRoot ud = { + .precise = precise, + .report = report, + .info = info, + }; + + mono_handle_stack_scan ((HandleStack *) info->client_info.info.handle_stack, report_handle_stack_root, &ud, ud.precise, FALSE); +} + +static void +report_stack_roots (void) +{ + GCRootReport report = {0}; + FOREACH_THREAD (info) { + void *aligned_stack_start; + + if (info->client_info.skip) { + continue; + } else if (info->client_info.gc_disabled) { + continue; + } else if (!mono_thread_info_is_live (info)) { + continue; + } else if (!info->client_info.stack_start) { + continue; + } + + g_assert (info->client_info.stack_start); + g_assert (info->client_info.info.stack_end); + + aligned_stack_start = (void*)(mword) ALIGN_TO ((mword)info->client_info.stack_start, SIZEOF_VOID_P); +#ifdef HOST_WIN32 + /* Windows uses a guard page before the committed stack memory pages to detect when the + stack needs to be grown. If we suspend a thread just after a function prolog has + decremented the stack pointer to point into the guard page but before the thread has + been able to read or write to that page, starting the stack scan at aligned_stack_start + will raise a STATUS_GUARD_PAGE_VIOLATION and the process will crash. This code uses + VirtualQuery() to determine whether stack_start points into the guard page and then + updates aligned_stack_start to point at the next non-guard page. */ + MEMORY_BASIC_INFORMATION mem_info; + SIZE_T result = VirtualQuery (info->client_info.stack_start, &mem_info, sizeof(mem_info)); + g_assert (result != 0); + if (mem_info.Protect & PAGE_GUARD) { + aligned_stack_start = ((char*) mem_info.BaseAddress) + mem_info.RegionSize; + } +#endif + + g_assert (info->client_info.suspend_done); + + report_conservative_roots (&report, aligned_stack_start, (void **)aligned_stack_start, (void **)info->client_info.info.stack_end); + report_conservative_roots (&report, aligned_stack_start, (void**)&info->client_info.ctx, (void**)(&info->client_info.ctx + 1)); + + report_handle_stack_roots (&report, info, FALSE); + report_handle_stack_roots (&report, info, TRUE); + } FOREACH_THREAD_END + + notify_gc_roots (&report); +} + +static void +report_pin_queue (void) +{ + lower_bound = SIZE_MAX; + upper_bound = 0; + + //sort the addresses + sgen_pointer_queue_sort_uniq (&pinned_objects); + + for (int i = 0; i < pinned_objects.next_slot; ++i) { + GCObject *obj = pinned_objects.data [i]; + ssize_t size = sgen_safe_object_get_size (obj); + + ssize_t addr = (ssize_t)obj; + lower_bound = MIN (lower_bound, addr); + upper_bound = MAX (upper_bound, addr + size); + } + + report_stack_roots (); + sgen_pointer_queue_clear (&pinned_objects); +} + +static void +report_finalizer_roots_from_queue (SgenPointerQueue *queue, void* queue_address) +{ + GCRootReport report; + size_t i; + + report.count = 0; + for (i = 0; i < queue->next_slot; ++i) { + void *obj = queue->data [i]; + if (!obj) + continue; + report_gc_root (&report, queue_address, obj); + } + notify_gc_roots (&report); +} + +static void +report_registered_roots_by_type (int root_type) +{ + GCRootReport report = { 0 }; + void **start_root; + RootRecord *root; + report.count = 0; + SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], void **, start_root, RootRecord *, root) { + SGEN_LOG (6, "Profiler root scan %p-%p (desc: %p)", start_root, root->end_root, (void*)root->root_desc); + if (root_type == ROOT_TYPE_PINNED) + report_pinning_roots (&report, start_root, (void**)root->end_root); + else + precisely_report_roots_from (&report, start_root, (void**)root->end_root, root->root_desc); + } SGEN_HASH_TABLE_FOREACH_END; + notify_gc_roots (&report); +} + +static void +report_registered_roots (void) +{ + for (int i = 0; i < ROOT_TYPE_NUM; ++i) + report_registered_roots_by_type (i); +} + +static void +sgen_report_all_roots (SgenPointerQueue *fin_ready_queue, SgenPointerQueue *critical_fin_queue) +{ + if (!MONO_PROFILER_ENABLED (gc_roots)) + return; + + report_registered_roots (); + report_pin_queue (); + report_finalizer_roots_from_queue (fin_ready_queue, SPECIAL_ADDRESS_FIN_QUEUE); + report_finalizer_roots_from_queue (critical_fin_queue, SPECIAL_ADDRESS_CRIT_FIN_QUEUE); +} + +void +sgen_client_pinning_start (void) +{ + if (!MONO_PROFILER_ENABLED (gc_roots)) + return; + + sgen_pointer_queue_clear (&pinned_objects); +} + +void +sgen_client_pinning_end (void) +{ + if (!MONO_PROFILER_ENABLED (gc_roots)) + return; +} + +void +sgen_client_nursery_objects_pinned (void **definitely_pinned, int count) +{ + if (!MONO_PROFILER_ENABLED (gc_roots)) + return; + + for (int i = 0; i < count; ++i) + sgen_pointer_queue_add (&pinned_objects, definitely_pinned [i]); +} + +void +sgen_client_pinned_los_object (GCObject *obj) +{ + if (!MONO_PROFILER_ENABLED (gc_roots)) + return; + + sgen_pointer_queue_add (&pinned_objects, obj); +} + +void +sgen_client_pinned_cemented_object (GCObject *obj) +{ + if (!MONO_PROFILER_ENABLED (gc_roots)) + return; + + // TODO: How do we report this in a way that makes sense? +} + +void +sgen_client_pinned_major_heap_object (GCObject *obj) +{ + if (!MONO_PROFILER_ENABLED (gc_roots)) + return; + + sgen_pointer_queue_add (&pinned_objects, obj); +} + +void +sgen_client_collecting_minor_report_roots (SgenPointerQueue *fin_ready_queue, SgenPointerQueue *critical_fin_queue) +{ + sgen_report_all_roots (fin_ready_queue, critical_fin_queue); +} + +void +sgen_client_collecting_major_report_roots (SgenPointerQueue *fin_ready_queue, SgenPointerQueue *critical_fin_queue) +{ + sgen_report_all_roots (fin_ready_queue, critical_fin_queue); +} + +#define MOVED_OBJECTS_NUM 64 +static void *moved_objects [MOVED_OBJECTS_NUM]; +static int moved_objects_idx = 0; + +static SgenPointerQueue moved_objects_queue = SGEN_POINTER_QUEUE_INIT (INTERNAL_MEM_MOVED_OBJECT); + +void +mono_sgen_register_moved_object (void *obj, void *destination) +{ + /* + * This function can be called from SGen's worker threads. We want to try + * and avoid exposing those threads to the profiler API, so queue up move + * events and send them later when the main GC thread calls + * mono_sgen_gc_event_moves (). + * + * TODO: Once SGen has multiple worker threads, we need to switch to a + * lock-free data structure for the queue as multiple threads will be + * adding to it at the same time. + */ + if (sgen_workers_is_worker_thread (mono_native_thread_id_get ())) { + sgen_pointer_queue_add (&moved_objects_queue, obj); + sgen_pointer_queue_add (&moved_objects_queue, destination); + } else { + if (moved_objects_idx == MOVED_OBJECTS_NUM) { + MONO_PROFILER_RAISE (gc_moves, ((MonoObject **) moved_objects, moved_objects_idx)); + moved_objects_idx = 0; + } + + moved_objects [moved_objects_idx++] = obj; + moved_objects [moved_objects_idx++] = destination; + } +} + +void +mono_sgen_gc_event_moves (void) +{ + while (!sgen_pointer_queue_is_empty (&moved_objects_queue)) { + void *dst = sgen_pointer_queue_pop (&moved_objects_queue); + void *src = sgen_pointer_queue_pop (&moved_objects_queue); + + mono_sgen_register_moved_object (src, dst); + } + + if (moved_objects_idx) { + MONO_PROFILER_RAISE (gc_moves, ((MonoObject **) moved_objects, moved_objects_idx)); + moved_objects_idx = 0; + } +} + +/* + * Heap walking + */ + +#define REFS_SIZE 128 +typedef struct { + void *data; + MonoGCReferences callback; + int flags; + int count; + int called; + MonoObject *refs [REFS_SIZE]; + uintptr_t offsets [REFS_SIZE]; +} HeapWalkInfo; + +#undef HANDLE_PTR +#define HANDLE_PTR(ptr,obj) do { \ + if (*(ptr)) { \ + if (hwi->count == REFS_SIZE) { \ + hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data); \ + hwi->count = 0; \ + hwi->called = 1; \ + } \ + hwi->offsets [hwi->count] = (char*)(ptr)-(char*)start; \ + hwi->refs [hwi->count++] = *(ptr); \ + } \ + } while (0) + +static void +collect_references (HeapWalkInfo *hwi, GCObject *obj, size_t size) +{ + char *start = (char*)obj; + mword desc = sgen_obj_get_descriptor (obj); + +#include "sgen/sgen-scan-object.h" +} + +static void +walk_references (GCObject *start, size_t size, void *data) +{ + HeapWalkInfo *hwi = (HeapWalkInfo *)data; + hwi->called = 0; + hwi->count = 0; + collect_references (hwi, start, size); + if (hwi->count || !hwi->called) + hwi->callback (start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data); +} + +/** + * mono_gc_walk_heap: + * \param flags flags for future use + * \param callback a function pointer called for each object in the heap + * \param data a user data pointer that is passed to callback + * This function can be used to iterate over all the live objects in the heap; + * for each object, \p callback is invoked, providing info about the object's + * location in memory, its class, its size and the objects it references. + * For each referenced object its offset from the object address is + * reported in the offsets array. + * The object references may be buffered, so the callback may be invoked + * multiple times for the same object: in all but the first call, the size + * argument will be zero. + * Note that this function can be only called in the \c MONO_GC_EVENT_PRE_START_WORLD + * profiler event handler. + * \returns a non-zero value if the GC doesn't support heap walking + */ +int +mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data) +{ + HeapWalkInfo hwi; + + hwi.flags = flags; + hwi.callback = callback; + hwi.data = data; + + sgen_clear_nursery_fragments (); + sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, walk_references, &hwi, FALSE, TRUE); + + major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, walk_references, &hwi); + sgen_los_iterate_objects (walk_references, &hwi); + + return 0; +} + +/* + * Threads + */ + +void +mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks) +{ + gc_callbacks = *callbacks; +} + +MonoGCCallbacks * +mono_gc_get_gc_callbacks () +{ + return &gc_callbacks; +} + +gpointer +mono_gc_thread_attach (SgenThreadInfo *info) +{ + return sgen_thread_attach (info); +} + +void +sgen_client_thread_attach (SgenThreadInfo* info) +{ + mono_tls_set_sgen_thread_info (info); + + info->client_info.skip = 0; + + info->client_info.stack_start = NULL; + +#ifdef SGEN_POSIX_STW + info->client_info.stop_count = -1; + info->client_info.signal = 0; +#endif + + memset (&info->client_info.ctx, 0, sizeof (MonoContext)); + + if (mono_gc_get_gc_callbacks ()->thread_attach_func) + info->client_info.runtime_data = mono_gc_get_gc_callbacks ()->thread_attach_func (); + + binary_protocol_thread_register ((gpointer)mono_thread_info_get_tid (info)); + + SGEN_LOG (3, "registered thread %p (%p) stack end %p", info, (gpointer)mono_thread_info_get_tid (info), info->client_info.info.stack_end); + + 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) +{ + return sgen_thread_detach_with_lock (info); +} + +void +sgen_client_thread_detach_with_lock (SgenThreadInfo *p) +{ + MonoNativeThreadId tid; + + mono_tls_set_sgen_thread_info (NULL); + + tid = mono_thread_info_get_tid (p); + + mono_threads_add_joinable_runtime_thread (&p->client_info.info); + + if (mono_gc_get_gc_callbacks ()->thread_detach_func) { + mono_gc_get_gc_callbacks ()->thread_detach_func (p->client_info.runtime_data); + p->client_info.runtime_data = NULL; + } + + binary_protocol_thread_unregister ((gpointer)tid); + SGEN_LOG (3, "unregister thread %p (%p)", p, (gpointer)tid); + + HandleStack *handles = (HandleStack*) p->client_info.info.handle_stack; + p->client_info.info.handle_stack = NULL; + mono_handle_stack_free (handles); +} + +void +mono_gc_set_skip_thread (gboolean skip) +{ + SgenThreadInfo *info = mono_thread_info_current (); + + LOCK_GC; + info->client_info.gc_disabled = skip; + UNLOCK_GC; + + if (skip) { + /* If we skip scanning a thread with a non-empty handle stack, we may move an + * object but fail to update the reference in the handle. + */ + HandleStack *stack = info->client_info.info.handle_stack; + g_assert (stack == NULL || mono_handle_stack_is_empty (stack)); + } +} + +gboolean +mono_gc_thread_in_critical_region (SgenThreadInfo *info) +{ + return info->client_info.in_critical_region; +} + +/** + * mono_gc_is_gc_thread: + */ +gboolean +mono_gc_is_gc_thread (void) +{ + gboolean result; + LOCK_GC; + result = mono_thread_info_current () != NULL; + UNLOCK_GC; + return result; +} + +void +sgen_client_thread_register_worker (void) +{ + mono_thread_info_register_small_id (); + mono_native_thread_set_name (mono_native_thread_id_get (), "SGen worker"); +} + +/* Variables holding start/end nursery so it won't have to be passed at every call */ +static void *scan_area_arg_start, *scan_area_arg_end; + +void +mono_gc_conservatively_scan_area (void *start, void *end) +{ + sgen_conservatively_pin_objects_from ((void **)start, (void **)end, scan_area_arg_start, scan_area_arg_end, PIN_TYPE_STACK); +} + +void* +mono_gc_scan_object (void *obj, void *gc_data) +{ + ScanCopyContext *ctx = (ScanCopyContext *)gc_data; + ctx->ops->copy_or_mark_object ((GCObject**)&obj, ctx->queue); + return obj; +} + +typedef struct { + void **start_nursery; + void **end_nursery; +} PinHandleStackInteriorPtrData; + +/* Called when we're scanning the handle stack imprecisely and we encounter a pointer into the + middle of an object. + */ +static void +pin_handle_stack_interior_ptrs (void **ptr_slot, void *user_data) +{ + PinHandleStackInteriorPtrData *ud = (PinHandleStackInteriorPtrData *)user_data; + sgen_conservatively_pin_objects_from (ptr_slot, ptr_slot+1, ud->start_nursery, ud->end_nursery, PIN_TYPE_STACK); +} + + +/* + * Mark from thread stacks and registers. + */ +void +sgen_client_scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, ScanCopyContext ctx) +{ + scan_area_arg_start = start_nursery; + scan_area_arg_end = end_nursery; +#ifdef HOST_WASM + //Under WASM we don't scan thread stacks and we can't trust the values we find there either. + return; +#endif + + FOREACH_THREAD (info) { + int skip_reason = 0; + void *aligned_stack_start; + + if (info->client_info.skip) { + SGEN_LOG (3, "Skipping dead thread %p, range: %p-%p, size: %zd", info, info->client_info.stack_start, info->client_info.info.stack_end, (char*)info->client_info.info.stack_end - (char*)info->client_info.stack_start); + skip_reason = 1; + } else if (info->client_info.gc_disabled) { + SGEN_LOG (3, "GC disabled for thread %p, range: %p-%p, size: %zd", info, info->client_info.stack_start, info->client_info.info.stack_end, (char*)info->client_info.info.stack_end - (char*)info->client_info.stack_start); + skip_reason = 2; + } else if (!mono_thread_info_is_live (info)) { + SGEN_LOG (3, "Skipping non-running thread %p, range: %p-%p, size: %zd (state %x)", info, info->client_info.stack_start, info->client_info.info.stack_end, (char*)info->client_info.info.stack_end - (char*)info->client_info.stack_start, info->client_info.info.thread_state); + skip_reason = 3; + } else if (!info->client_info.stack_start) { + SGEN_LOG (3, "Skipping starting or detaching thread %p", info); + skip_reason = 4; + } + + binary_protocol_scan_stack ((gpointer)mono_thread_info_get_tid (info), info->client_info.stack_start, info->client_info.info.stack_end, skip_reason); + + if (skip_reason) { + if (precise) { + /* If we skip a thread with a non-empty handle stack and then it + * resumes running we may potentially move an object but fail to + * update the reference in the handle. + */ + HandleStack *stack = info->client_info.info.handle_stack; + g_assert (stack == NULL || mono_handle_stack_is_empty (stack)); + } + continue; + } + + g_assert (info->client_info.stack_start); + g_assert (info->client_info.info.stack_end); + + aligned_stack_start = (void*)(mword) ALIGN_TO ((mword)info->client_info.stack_start, SIZEOF_VOID_P); +#ifdef HOST_WIN32 + /* Windows uses a guard page before the committed stack memory pages to detect when the + stack needs to be grown. If we suspend a thread just after a function prolog has + decremented the stack pointer to point into the guard page but before the thread has + been able to read or write to that page, starting the stack scan at aligned_stack_start + will raise a STATUS_GUARD_PAGE_VIOLATION and the process will crash. This code uses + VirtualQuery() to determine whether stack_start points into the guard page and then + updates aligned_stack_start to point at the next non-guard page. */ + MEMORY_BASIC_INFORMATION mem_info; + SIZE_T result = VirtualQuery(info->client_info.stack_start, &mem_info, sizeof(mem_info)); + g_assert (result != 0); + if (mem_info.Protect & PAGE_GUARD) { + aligned_stack_start = ((char*) mem_info.BaseAddress) + mem_info.RegionSize; + } +#endif + + g_assert (info->client_info.suspend_done); + SGEN_LOG (3, "Scanning thread %p, range: %p-%p, size: %zd, pinned=%zd", info, info->client_info.stack_start, info->client_info.info.stack_end, (char*)info->client_info.info.stack_end - (char*)info->client_info.stack_start, sgen_get_pinned_count ()); + if (mono_gc_get_gc_callbacks ()->thread_mark_func && !conservative_stack_mark) { + mono_gc_get_gc_callbacks ()->thread_mark_func (info->client_info.runtime_data, (guint8 *)aligned_stack_start, (guint8 *)info->client_info.info.stack_end, precise, &ctx); + } else if (!precise) { + if (!conservative_stack_mark) { + fprintf (stderr, "Precise stack mark not supported - disabling.\n"); + conservative_stack_mark = TRUE; + } + //FIXME we should eventually use the new stack_mark from coop + sgen_conservatively_pin_objects_from ((void **)aligned_stack_start, (void **)info->client_info.info.stack_end, start_nursery, end_nursery, PIN_TYPE_STACK); + } + + if (!precise) { + sgen_conservatively_pin_objects_from ((void**)&info->client_info.ctx, (void**)(&info->client_info.ctx + 1), + start_nursery, end_nursery, PIN_TYPE_STACK); + + { + // This is used on Coop GC for platforms where we cannot get the data for individual registers. + // We force a spill of all registers into the stack and pass a chunk of data into sgen. + //FIXME under coop, for now, what we need to ensure is that we scan any extra memory from info->client_info.info.stack_end to stack_mark + MonoThreadUnwindState *state = &info->client_info.info.thread_saved_state [SELF_SUSPEND_STATE_INDEX]; + if (state && state->gc_stackdata) { + sgen_conservatively_pin_objects_from ((void **)state->gc_stackdata, (void**)((char*)state->gc_stackdata + state->gc_stackdata_size), + start_nursery, end_nursery, PIN_TYPE_STACK); + } + } + } + if (info->client_info.info.handle_stack) { + /* + Make two passes over the handle stack. On the imprecise pass, pin all + objects where the handle points into the interior of the object. On the + precise pass, copy or mark all the objects that have handles to the + beginning of the object. + */ + if (precise) + mono_handle_stack_scan ((HandleStack*)info->client_info.info.handle_stack, (GcScanFunc)ctx.ops->copy_or_mark_object, ctx.queue, precise, TRUE); + else { + PinHandleStackInteriorPtrData ud = { .start_nursery = start_nursery, + .end_nursery = end_nursery, + }; + mono_handle_stack_scan ((HandleStack*)info->client_info.info.handle_stack, pin_handle_stack_interior_ptrs, &ud, precise, FALSE); + } + } + } FOREACH_THREAD_END +} + +/* + * mono_gc_set_stack_end: + * + * Set the end of the current threads stack to STACK_END. The stack space between + * STACK_END and the real end of the threads stack will not be scanned during collections. + */ +void +mono_gc_set_stack_end (void *stack_end) +{ + SgenThreadInfo *info; + + LOCK_GC; + info = mono_thread_info_current (); + if (info) { + SGEN_ASSERT (0, stack_end < info->client_info.info.stack_end, "Can only lower stack end"); + info->client_info.info.stack_end = stack_end; + } + UNLOCK_GC; +} + +/* + * Roots + */ + +int +mono_gc_register_root (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, void *key, const char *msg) +{ + return sgen_register_root (start, size, descr, descr ? ROOT_TYPE_NORMAL : ROOT_TYPE_PINNED, source, key, msg); +} + +int +mono_gc_register_root_wbarrier (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, void *key, const char *msg) +{ + return sgen_register_root (start, size, descr, ROOT_TYPE_WBARRIER, source, key, msg); +} + +void +mono_gc_deregister_root (char* addr) +{ + sgen_deregister_root (addr); +} + +/* + * PThreads + */ + +#ifndef HOST_WIN32 +int +mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) +{ + int res; + + mono_threads_join_lock (); + res = pthread_create (new_thread, attr, start_routine, arg); + mono_threads_join_unlock (); + + return res; +} +#endif + +/* + * Miscellaneous + */ + +void +sgen_client_total_allocated_heap_changed (size_t allocated_heap) +{ + MONO_PROFILER_RAISE (gc_resize, (allocated_heap)); + + mono_runtime_resource_check_limit (MONO_RESOURCE_GC_HEAP, allocated_heap); +} + +gboolean +mono_gc_user_markers_supported (void) +{ + return TRUE; +} + +gboolean +mono_object_is_alive (MonoObject* o) +{ + return TRUE; +} + +int +mono_gc_get_generation (MonoObject *obj) +{ + if (sgen_ptr_in_nursery (obj)) + return 0; + return 1; +} + +const char * +mono_gc_get_gc_name (void) +{ + return "sgen"; +} + +char* +mono_gc_get_description (void) +{ +#ifdef HAVE_CONC_GC_AS_DEFAULT + return g_strdup ("sgen (concurrent by default)"); +#else + return g_strdup ("sgen"); +#endif +} + +void +mono_gc_set_desktop_mode (void) +{ +} + +gboolean +mono_gc_is_moving (void) +{ + return TRUE; +} + +gboolean +mono_gc_needs_write_barriers(void) +{ + return TRUE; +} + +gboolean +mono_gc_is_disabled (void) +{ + return FALSE; +} + +#ifdef HOST_WIN32 +BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved) +{ + return TRUE; +} +#endif + +int +mono_gc_max_generation (void) +{ + return 1; +} + +gboolean +mono_gc_precise_stack_mark_enabled (void) +{ + return !conservative_stack_mark; +} + +void +mono_gc_collect (int generation) +{ + sgen_gc_collect (generation); +} + +int +mono_gc_collection_count (int generation) +{ + return sgen_gc_collection_count (generation); +} + +int64_t +mono_gc_get_used_size (void) +{ + return (int64_t)sgen_gc_get_used_size (); +} + +int64_t +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) +{ + return sgen_make_user_root_descriptor (marker); +} + +MonoGCDescriptor +mono_gc_make_descr_for_string (gsize *bitmap, int numbits) +{ + return SGEN_DESC_STRING; +} + +void +mono_gc_register_obj_with_weak_fields (void *obj) +{ + return sgen_register_obj_with_weak_fields (obj); +} + +void* +mono_gc_get_nursery (int *shift_bits, size_t *size) +{ + *size = sgen_nursery_size; + *shift_bits = sgen_nursery_bits; + return sgen_get_nursery_start (); +} + +int +mono_gc_get_los_limit (void) +{ + return SGEN_MAX_SMALL_OBJ_SIZE; +} + +gpointer +sgen_client_default_metadata (void) +{ + return mono_domain_get (); +} + +gpointer +sgen_client_metadata_for_object (GCObject *obj) +{ + return mono_object_domain (obj); +} + +/** + * mono_gchandle_new: + * \param obj managed object to get a handle for + * \param pinned whether the object should be pinned + * This returns a handle that wraps the object, this is used to keep a + * reference to a managed object from the unmanaged world and preventing the + * object from being disposed. + * + * If \p pinned is false the address of the object can not be obtained, if it is + * true the address of the object can be obtained. This will also pin the + * object so it will not be possible by a moving garbage collector to move the + * object. + * + * \returns a handle that can be used to access the object from unmanaged code. + */ +guint32 +mono_gchandle_new (MonoObject *obj, gboolean pinned) +{ + return sgen_gchandle_new (obj, pinned); +} + +/** + * mono_gchandle_new_weakref: + * \param obj managed object to get a handle for + * \param track_resurrection Determines how long to track the object, if this is set to TRUE, the object is tracked after finalization, if FALSE, the object is only tracked up until the point of finalization. + * + * This returns a weak handle that wraps the object, this is used to + * keep a reference to a managed object from the unmanaged world. + * Unlike the \c mono_gchandle_new the object can be reclaimed by the + * garbage collector. In this case the value of the GCHandle will be + * set to zero. + * + * If \p track_resurrection is TRUE the object will be tracked through + * finalization and if the object is resurrected during the execution + * of the finalizer, then the returned weakref will continue to hold + * a reference to the object. If \p track_resurrection is FALSE, then + * the weak reference's target will become NULL as soon as the object + * is passed on to the finalizer. + * + * \returns a handle that can be used to access the object from + * unmanaged code. + */ +guint32 +mono_gchandle_new_weakref (GCObject *obj, gboolean track_resurrection) +{ + return sgen_gchandle_new_weakref (obj, track_resurrection); +} + +/** + * mono_gchandle_is_in_domain: + * \param gchandle a GCHandle's handle. + * \param domain An application domain. + * \returns TRUE if the object wrapped by the \p gchandle belongs to the specific \p domain. + */ +gboolean +mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain) +{ + MonoDomain *gchandle_domain = (MonoDomain *)sgen_gchandle_get_metadata (gchandle); + return domain->domain_id == gchandle_domain->domain_id; +} + +/** + * mono_gchandle_free: + * \param gchandle a GCHandle's handle. + * + * Frees the \p gchandle handle. If there are no outstanding + * references, the garbage collector can reclaim the memory of the + * object wrapped. + */ +void +mono_gchandle_free (guint32 gchandle) +{ + sgen_gchandle_free (gchandle); +} + +/** + * mono_gchandle_free_domain: + * \param unloading domain that is unloading + * + * Function used internally to cleanup any GC handle for objects belonging + * to the specified domain during appdomain unload. + */ +void +mono_gchandle_free_domain (MonoDomain *unloading) +{ +} + +/** + * mono_gchandle_get_target: + * \param gchandle a GCHandle's handle. + * + * The handle was previously created by calling \c mono_gchandle_new or + * \c mono_gchandle_new_weakref. + * + * \returns a pointer to the \c MonoObject* represented by the handle or + * NULL for a collected object if using a weakref handle. + */ +MonoObject* +mono_gchandle_get_target (guint32 gchandle) +{ + return sgen_gchandle_get_target (gchandle); +} + +static gpointer +null_link_if_in_domain (gpointer hidden, GCHandleType handle_type, int max_generation, gpointer user) +{ + MonoDomain *unloading_domain = (MonoDomain *)user; + MonoDomain *obj_domain; + gboolean is_weak = MONO_GC_HANDLE_TYPE_IS_WEAK (handle_type); + if (MONO_GC_HANDLE_IS_OBJECT_POINTER (hidden)) { + MonoObject *obj = (MonoObject *)MONO_GC_REVEAL_POINTER (hidden, is_weak); + obj_domain = mono_object_domain (obj); + } else { + obj_domain = (MonoDomain *)MONO_GC_REVEAL_POINTER (hidden, is_weak); + } + if (unloading_domain->domain_id == obj_domain->domain_id) + return NULL; + return hidden; +} + +void +sgen_null_links_for_domain (MonoDomain *domain) +{ + guint type; + for (type = HANDLE_TYPE_MIN; type < HANDLE_TYPE_MAX; ++type) + sgen_gchandle_iterate ((GCHandleType)type, GENERATION_OLD, null_link_if_in_domain, domain); +} + +void +mono_gchandle_set_target (guint32 gchandle, MonoObject *obj) +{ + sgen_gchandle_set_target (gchandle, obj); +} + +void +sgen_client_gchandle_created (int handle_type, GCObject *obj, guint32 handle) +{ +#ifndef DISABLE_PERFCOUNTERS + mono_atomic_inc_i32 (&mono_perfcounters->gc_num_handles); +#endif + + MONO_PROFILER_RAISE (gc_handle_created, (handle, handle_type, obj)); +} + +void +sgen_client_gchandle_destroyed (int handle_type, guint32 handle) +{ +#ifndef DISABLE_PERFCOUNTERS + mono_atomic_dec_i32 (&mono_perfcounters->gc_num_handles); +#endif + + MONO_PROFILER_RAISE (gc_handle_deleted, (handle, handle_type)); +} + +void +sgen_client_ensure_weak_gchandles_accessible (void) +{ + /* + * During the second bridge processing step the world is + * running again. That step processes all weak links once + * more to null those that refer to dead objects. Before that + * is completed, those links must not be followed, so we + * conservatively wait for bridge processing when any weak + * link is dereferenced. + */ + /* FIXME: A GC can occur after this check fails, in which case we + * should wait for bridge processing but would fail to do so. + */ + if (G_UNLIKELY (bridge_processing_in_progress)) + mono_gc_wait_for_bridge_processing (); +} + +void* +mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data) +{ + void *result; + LOCK_INTERRUPTION; + result = func (data); + UNLOCK_INTERRUPTION; + return result; +} + +void +mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size) +{ + // FIXME: +} + +guint8* +mono_gc_get_card_table (int *shift_bits, gpointer *mask) +{ + return sgen_get_card_table_configuration (shift_bits, mask); +} + +gboolean +mono_gc_card_table_nursery_check (void) +{ + return !sgen_get_major_collector ()->is_concurrent; +} + +/* Negative value to remove */ +void +mono_gc_add_memory_pressure (gint64 value) +{ + /* FIXME: Implement at some point? */ +} + +/* + * Logging + */ + +void +sgen_client_degraded_allocation (void) +{ + static gint32 last_major_gc_warned = -1; + static gint32 num_degraded = 0; + + gint32 major_gc_count = mono_atomic_load_i32 (&gc_stats.major_gc_count); + if (mono_atomic_load_i32 (&last_major_gc_warned) < major_gc_count) { + gint32 num = mono_atomic_inc_i32 (&num_degraded); + if (num == 1 || num == 3) + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "Warning: Degraded allocation. Consider increasing nursery-size if the warning persists."); + else if (num == 10) + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "Warning: Repeated degraded allocation. Consider increasing nursery-size."); + mono_atomic_store_i32 (&last_major_gc_warned, major_gc_count); + } +} + +/* + * Debugging + */ + +const char* +sgen_client_description_for_internal_mem_type (int type) +{ + switch (type) { + case INTERNAL_MEM_EPHEMERON_LINK: return "ephemeron-link"; + case INTERNAL_MEM_MOVED_OBJECT: return "moved-object"; + default: + return NULL; + } +} + +void +sgen_client_pre_collection_checks (void) +{ + if (sgen_mono_xdomain_checks) { + sgen_clear_nursery_fragments (); + sgen_check_for_xdomain_refs (); + } +} + +gboolean +sgen_client_vtable_is_inited (MonoVTable *vt) +{ + return vt->klass->inited; +} + +const char* +sgen_client_vtable_get_namespace (MonoVTable *vt) +{ + return vt->klass->name_space; +} + +const char* +sgen_client_vtable_get_name (MonoVTable *vt) +{ + return vt->klass->name; +} + +/* + * Initialization + */ + +void +sgen_client_init (void) +{ + mono_thread_callbacks_init (); + mono_thread_info_init (sizeof (SgenThreadInfo)); + + ///* Keep this the default for now */ + /* Precise marking is broken on all supported targets. Disable until fixed. */ + conservative_stack_mark = TRUE; + + sgen_register_fixed_internal_mem_type (INTERNAL_MEM_EPHEMERON_LINK, sizeof (EphemeronLinkNode)); + + mono_sgen_init_stw (); + + mono_tls_init_gc_keys (); + + mono_thread_info_attach (); +} + +gboolean +sgen_client_handle_gc_param (const char *opt) +{ + if (g_str_has_prefix (opt, "stack-mark=")) { + opt = strchr (opt, '=') + 1; + if (!strcmp (opt, "precise")) { + conservative_stack_mark = FALSE; + } else if (!strcmp (opt, "conservative")) { + conservative_stack_mark = TRUE; + } else { + sgen_env_var_error (MONO_GC_PARAMS_NAME, conservative_stack_mark ? "Using `conservative`." : "Using `precise`.", + "Invalid value `%s` for `stack-mark` option, possible values are: `precise`, `conservative`.", opt); + } + } else if (g_str_has_prefix (opt, "bridge-implementation=")) { + opt = strchr (opt, '=') + 1; + sgen_set_bridge_implementation (opt); + } else if (g_str_has_prefix (opt, "toggleref-test")) { + /* FIXME: This should probably in MONO_GC_DEBUG */ + sgen_register_test_toggleref_callback (); + } else if (!sgen_bridge_handle_gc_param (opt)) { + return FALSE; + } + return TRUE; +} + +void +sgen_client_print_gc_params_usage (void) +{ + fprintf (stderr, " stack-mark=MARK-METHOD (where MARK-METHOD is 'precise' or 'conservative')\n"); +} + +gboolean +sgen_client_handle_gc_debug (const char *opt) +{ + if (!strcmp (opt, "xdomain-checks")) { + sgen_mono_xdomain_checks = TRUE; + } else if (!strcmp (opt, "do-not-finalize")) { + mono_do_not_finalize = TRUE; + } else if (g_str_has_prefix (opt, "do-not-finalize=")) { + opt = strchr (opt, '=') + 1; + mono_do_not_finalize = TRUE; + mono_do_not_finalize_class_names = g_strsplit (opt, ",", 0); + } else if (!strcmp (opt, "log-finalizers")) { + log_finalizers = TRUE; + } else if (!strcmp (opt, "no-managed-allocator")) { + sgen_set_use_managed_allocator (FALSE); + } else if (!sgen_bridge_handle_gc_debug (opt)) { + return FALSE; + } + return TRUE; +} + +void +sgen_client_print_gc_debug_usage (void) +{ + fprintf (stderr, " xdomain-checks\n"); + fprintf (stderr, " do-not-finalize\n"); + fprintf (stderr, " log-finalizers\n"); + fprintf (stderr, " no-managed-allocator\n"); + sgen_bridge_print_gc_debug_usage (); +} + + +gpointer +sgen_client_get_provenance (void) +{ +#ifdef SGEN_OBJECT_PROVENANCE + MonoGCCallbacks *cb = mono_gc_get_gc_callbacks (); + gpointer (*get_provenance_func) (void); + if (!cb) + return NULL; + get_provenance_func = cb->get_provenance_func; + if (get_provenance_func) + return get_provenance_func (); + return NULL; +#else + return NULL; +#endif +} + +void +sgen_client_describe_invalid_pointer (GCObject *ptr) +{ + sgen_bridge_describe_pointer (ptr); +} + +static gboolean gc_inited; + +/** + * mono_gc_base_init: + */ +void +mono_gc_base_init (void) +{ + if (gc_inited) + return; + + mono_counters_init (); + +#ifndef HOST_WIN32 + mono_w32handle_init (); +#endif + +#ifdef HEAVY_STATISTICS + mono_counters_register ("los marked cards", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &los_marked_cards); + mono_counters_register ("los array cards scanned ", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &los_array_cards); + mono_counters_register ("los array remsets", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &los_array_remsets); + + mono_counters_register ("WBarrier set arrayref", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_wbarrier_set_arrayref); + mono_counters_register ("WBarrier value copy", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_wbarrier_value_copy); + mono_counters_register ("WBarrier object copy", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_wbarrier_object_copy); +#endif + + sgen_gc_init (); + + gc_inited = TRUE; +} + +void +mono_gc_base_cleanup (void) +{ + sgen_thread_pool_shutdown (); + + // We should have consumed any outstanding moves. + g_assert (sgen_pointer_queue_is_empty (&moved_objects_queue)); +} + +gboolean +mono_gc_is_null (void) +{ + return FALSE; +} + +gsize * +sgen_client_get_weak_bitmap (MonoVTable *vt, int *nbits) +{ + MonoClass *klass = vt->klass; + + return mono_class_get_weak_bitmap (klass, nbits); +} + +void +sgen_client_binary_protocol_collection_begin (int minor_gc_count, int generation) +{ + static gboolean pseudo_roots_registered; + + MONO_GC_BEGIN (generation); + + MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_START, generation)); + + if (!pseudo_roots_registered) { + pseudo_roots_registered = TRUE; + MONO_PROFILER_RAISE (gc_root_register, (SPECIAL_ADDRESS_FIN_QUEUE, 1, MONO_ROOT_SOURCE_FINALIZER_QUEUE, NULL, "Finalizer Queue")); + MONO_PROFILER_RAISE (gc_root_register, (SPECIAL_ADDRESS_CRIT_FIN_QUEUE, 1, MONO_ROOT_SOURCE_FINALIZER_QUEUE, NULL, "Finalizer Queue (Critical)")); + } + +#ifndef DISABLE_PERFCOUNTERS + if (generation == GENERATION_NURSERY) + mono_atomic_inc_i32 (&mono_perfcounters->gc_collections0); + else + mono_atomic_inc_i32 (&mono_perfcounters->gc_collections1); +#endif +} + +void +sgen_client_binary_protocol_collection_end (int minor_gc_count, int generation, long long num_objects_scanned, long long num_unique_objects_scanned) +{ + MONO_GC_END (generation); + + MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_END, generation)); +} + +#ifdef HOST_WASM +void +sgen_client_schedule_background_job (void (*cb)(void)) +{ + mono_threads_schedule_background_job (cb); +} + +#endif + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/sgen-new-bridge.c b/unity-2019.4.24f1-mbe/mono/metadata/sgen-new-bridge.c new file mode 100644 index 000000000..21a90e8b7 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/sgen-new-bridge.c @@ -0,0 +1,1095 @@ +/** + * \file + * Simple generational GC. + * + * Copyright 2011 Novell, Inc (http://www.novell.com) + * Copyright 2011 Xamarin Inc (http://www.xamarin.com) + * Copyright 2001-2003 Ximian, Inc + * Copyright 2003-2010 Novell, Inc. + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include "config.h" + +#ifdef HAVE_SGEN_GC + +#include +#include + +#include "sgen/sgen-gc.h" +#include "sgen-bridge-internals.h" +#include "sgen/sgen-hash-table.h" +#include "sgen/sgen-qsort.h" +#include "sgen/sgen-client.h" +#include "tabledefs.h" +#include "utils/mono-logger-internals.h" + +#define OPTIMIZATION_COPY +#define OPTIMIZATION_FORWARD +#define OPTIMIZATION_SINGLETON_DYN_ARRAY +#include "sgen-dynarray.h" + +//#define NEW_XREFS +#ifdef NEW_XREFS +//#define TEST_NEW_XREFS +#endif + +#if !defined(NEW_XREFS) || defined(TEST_NEW_XREFS) +#define OLD_XREFS +#endif + +#ifdef NEW_XREFS +#define XREFS new_xrefs +#else +#define XREFS old_xrefs +#endif + +/* + * Bridge data for a single managed object + * + * FIXME: Optimizations: + * + * Don't allocate a srcs array for just one source. Most objects have + * just one source, so use the srcs pointer itself. + */ +typedef struct _HashEntry { + gboolean is_bridge; + + union { + struct { + guint32 is_visited : 1; + guint32 finishing_time : 31; + struct _HashEntry *forwarded_to; + } dfs1; + struct { + // Index in sccs array of SCC this object was folded into + int scc_index; + } dfs2; + } v; + + // "Source" managed objects pointing at this destination + DynPtrArray srcs; +} HashEntry; + +typedef struct { + HashEntry entry; + double weight; +} HashEntryWithAccounting; + +// The graph of managed objects/HashEntries is reduced to a graph of strongly connected components +typedef struct _SCC { + int index; + int api_index; + + // How many bridged objects does this SCC hold references to? + int num_bridge_entries; + + gboolean flag; + + /* + * Index in global sccs array of SCCs holding pointers to this SCC + * + * New and old xrefs are typically mutually exclusive. Only when TEST_NEW_XREFS is + * enabled we do both, and compare the results. This should only be done for + * debugging, obviously. + */ +#ifdef OLD_XREFS + DynIntArray old_xrefs; /* these are incoming, not outgoing */ +#endif +#ifdef NEW_XREFS + DynIntArray new_xrefs; +#endif +} SCC; + +static char *dump_prefix = NULL; + +// Maps managed objects to corresponding HashEntry stricts +static SgenHashTable hash_table = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_HASH_TABLE, INTERNAL_MEM_BRIDGE_HASH_TABLE_ENTRY, sizeof (HashEntry), mono_aligned_addr_hash, NULL); + +static guint32 current_time; + +static gboolean bridge_accounting_enabled = FALSE; + +static SgenBridgeProcessor *bridge_processor; + +/* Core functions */ + +/*SCC */ + +static void +dyn_array_scc_init (DynSCCArray *da) +{ + dyn_array_init (&da->array); +} + +static void +dyn_array_scc_uninit (DynSCCArray *da) +{ + dyn_array_uninit (&da->array, sizeof (SCC)); +} + +static int +dyn_array_scc_size (DynSCCArray *da) +{ + return da->array.size; +} + +static SCC* +dyn_array_scc_add (DynSCCArray *da) +{ + return (SCC *)dyn_array_add (&da->array, sizeof (SCC)); +} + +static SCC* +dyn_array_scc_get_ptr (DynSCCArray *da, int x) +{ + return &((SCC*)da->array.data)[x]; +} + +/* Merge code*/ + +static DynIntArray merge_array; + +#ifdef NEW_XREFS +static gboolean +dyn_array_int_contains (DynIntArray *da, int x) +{ + int i; + for (i = 0; i < dyn_array_int_size (da); ++i) + if (dyn_array_int_get (da, i) == x) + return TRUE; + return FALSE; +} +#endif + +static void +set_config (const SgenBridgeProcessorConfig *config) +{ + if (config->accounting) { + SgenHashTable table = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_HASH_TABLE, INTERNAL_MEM_BRIDGE_HASH_TABLE_ENTRY, sizeof (HashEntryWithAccounting), mono_aligned_addr_hash, NULL); + bridge_accounting_enabled = TRUE; + hash_table = table; + } + if (config->dump_prefix) { + dump_prefix = strdup (config->dump_prefix); + } +} + +static MonoGCBridgeObjectKind +class_kind (MonoClass *klass) +{ + MonoGCBridgeObjectKind res = bridge_callbacks.bridge_class_kind (klass); + + /* If it's a bridge, nothing we can do about it. */ + if (res == GC_BRIDGE_TRANSPARENT_BRIDGE_CLASS || res == GC_BRIDGE_OPAQUE_BRIDGE_CLASS) + return res; + + /* Non bridge classes with no pointers will never point to a bridge, so we can savely ignore them. */ + if (!klass->has_references) { + SGEN_LOG (6, "class %s is opaque\n", klass->name); + return GC_BRIDGE_OPAQUE_CLASS; + } + + /* Some arrays can be ignored */ + if (klass->rank == 1) { + MonoClass *elem_class = klass->element_class; + + /* FIXME the bridge check can be quite expensive, cache it at the class level. */ + /* An array of a sealed type that is not a bridge will never get to a bridge */ + if ((mono_class_get_flags (elem_class) & TYPE_ATTRIBUTE_SEALED) && !elem_class->has_references && !bridge_callbacks.bridge_class_kind (elem_class)) { + SGEN_LOG (6, "class %s is opaque\n", klass->name); + return GC_BRIDGE_OPAQUE_CLASS; + } + } + + return GC_BRIDGE_TRANSPARENT_CLASS; +} + +static HashEntry* +get_hash_entry (MonoObject *obj, gboolean *existing) +{ + HashEntry *entry = (HashEntry *)sgen_hash_table_lookup (&hash_table, obj); + HashEntry new_entry; + + if (entry) { + if (existing) + *existing = TRUE; + return entry; + } + if (existing) + *existing = FALSE; + + memset (&new_entry, 0, sizeof (HashEntry)); + + dyn_array_ptr_init (&new_entry.srcs); + new_entry.v.dfs1.finishing_time = 0; + + sgen_hash_table_replace (&hash_table, obj, &new_entry, NULL); + + return (HashEntry *)sgen_hash_table_lookup (&hash_table, obj); +} + +static void +add_source (HashEntry *entry, HashEntry *src) +{ + dyn_array_ptr_add (&entry->srcs, src); +} + +static void +free_data (void) +{ + MonoObject *obj G_GNUC_UNUSED; + HashEntry *entry; + int total_srcs = 0; + int max_srcs = 0; + + SGEN_HASH_TABLE_FOREACH (&hash_table, MonoObject *, obj, HashEntry *, entry) { + int entry_size = dyn_array_ptr_size (&entry->srcs); + total_srcs += entry_size; + if (entry_size > max_srcs) + max_srcs = entry_size; + dyn_array_ptr_uninit (&entry->srcs); + } SGEN_HASH_TABLE_FOREACH_END; + + sgen_hash_table_clean (&hash_table); + + dyn_array_int_uninit (&merge_array); + //g_print ("total srcs %d - max %d\n", total_srcs, max_srcs); +} + +static HashEntry* +register_bridge_object (MonoObject *obj) +{ + HashEntry *entry = get_hash_entry (obj, NULL); + entry->is_bridge = TRUE; + return entry; +} + +static void +register_finishing_time (HashEntry *entry, guint32 t) +{ + g_assert (entry->v.dfs1.finishing_time == 0); + /* finishing_time has 31 bits, so it must be within signed int32 range. */ + g_assert (t > 0 && t <= G_MAXINT32); + entry->v.dfs1.finishing_time = t; +} + +static int ignored_objects; + +static gboolean +is_opaque_object (MonoObject *obj) +{ + if ((obj->vtable->gc_bits & SGEN_GC_BIT_BRIDGE_OPAQUE_OBJECT) == SGEN_GC_BIT_BRIDGE_OPAQUE_OBJECT) { + SGEN_LOG (6, "ignoring %s\n", obj->vtable->klass->name); + ++ignored_objects; + return TRUE; + } + return FALSE; +} + +static gboolean +object_needs_expansion (MonoObject **objp) +{ + MonoObject *obj = *objp; + MonoObject *fwd = SGEN_OBJECT_IS_FORWARDED (obj); + if (fwd) { + *objp = fwd; + if (is_opaque_object (fwd)) + return FALSE; + return sgen_hash_table_lookup (&hash_table, fwd) != NULL; + } + if (is_opaque_object (obj)) + return FALSE; + if (!sgen_object_is_live (obj)) + return TRUE; + return sgen_hash_table_lookup (&hash_table, obj) != NULL; +} + +static HashEntry* +follow_forward (HashEntry *entry) +{ +#ifdef OPTIMIZATION_FORWARD + while (entry->v.dfs1.forwarded_to) { + HashEntry *next = entry->v.dfs1.forwarded_to; + if (next->v.dfs1.forwarded_to) + entry->v.dfs1.forwarded_to = next->v.dfs1.forwarded_to; + entry = next; + } +#else + g_assert (!entry->v.dfs1.forwarded_to); +#endif + return entry; +} + +static DynPtrArray registered_bridges; +static DynPtrArray dfs_stack; + +static int dfs1_passes, dfs2_passes; + +/* + * DFS1 maintains a stack, where each two entries are effectively one entry. (FIXME: + * Optimize this via pointer tagging.) There are two different types of entries: + * + * entry, src: entry needs to be expanded via scanning, and linked to from src + * NULL, entry: entry has already been expanded and needs to be finished + */ + +#undef HANDLE_PTR +#define HANDLE_PTR(ptr,obj) do { \ + GCObject *dst = (GCObject*)*(ptr); \ + if (dst && object_needs_expansion (&dst)) { \ + ++num_links; \ + dyn_array_ptr_push (&dfs_stack, obj_entry); \ + dyn_array_ptr_push (&dfs_stack, follow_forward (get_hash_entry (dst, NULL))); \ + } \ + } while (0) + +static void +dfs1 (HashEntry *obj_entry) +{ + HashEntry *src; + g_assert (dyn_array_ptr_size (&dfs_stack) == 0); + + dyn_array_ptr_push (&dfs_stack, NULL); + dyn_array_ptr_push (&dfs_stack, obj_entry); + + do { + MonoObject *obj; + char *start; + ++dfs1_passes; + + obj_entry = (HashEntry *)dyn_array_ptr_pop (&dfs_stack); + if (obj_entry) { + /* obj_entry needs to be expanded */ + src = (HashEntry *)dyn_array_ptr_pop (&dfs_stack); + + if (src) + g_assert (!src->v.dfs1.forwarded_to); + + obj_entry = follow_forward (obj_entry); + + again: + g_assert (!obj_entry->v.dfs1.forwarded_to); + obj = sgen_hash_table_key_for_value_pointer (obj_entry); + start = (char*)obj; + + if (!obj_entry->v.dfs1.is_visited) { + int num_links = 0; + mword desc = sgen_obj_get_descriptor_safe (obj); + + obj_entry->v.dfs1.is_visited = 1; + + /* push the finishing entry on the stack */ + dyn_array_ptr_push (&dfs_stack, obj_entry); + dyn_array_ptr_push (&dfs_stack, NULL); + +#include "sgen/sgen-scan-object.h" + + /* + * We can remove non-bridge objects with a single outgoing + * link by forwarding links going to it. + * + * This is the first time we've encountered this object, so + * no links to it have yet been added. We'll keep it that + * way by setting the forward pointer, and instead of + * continuing processing this object, we start over with the + * object it points to. + */ +#ifdef OPTIMIZATION_FORWARD + if (!obj_entry->is_bridge && num_links == 1) { + HashEntry *dst_entry = (HashEntry *)dyn_array_ptr_pop (&dfs_stack); + HashEntry *obj_entry_again = (HashEntry *)dyn_array_ptr_pop (&dfs_stack); + g_assert (obj_entry_again == obj_entry); + g_assert (!dst_entry->v.dfs1.forwarded_to); + if (obj_entry != dst_entry) { + obj_entry->v.dfs1.forwarded_to = dst_entry; + obj_entry = dst_entry; + } + goto again; + } +#endif + } + + if (src) { + //g_print ("link %s -> %s\n", sgen_safe_name (src->obj), sgen_safe_name (obj)); + g_assert (!obj_entry->v.dfs1.forwarded_to); + add_source (obj_entry, src); + } else { + //g_print ("starting with %s\n", sgen_safe_name (obj)); + } + } else { + /* obj_entry needs to be finished */ + + obj_entry = (HashEntry *)dyn_array_ptr_pop (&dfs_stack); + + //g_print ("finish %s\n", sgen_safe_name (obj_entry->obj)); + register_finishing_time (obj_entry, ++current_time); + } + } while (dyn_array_ptr_size (&dfs_stack) > 0); +} + +static DynSCCArray sccs; +static SCC *current_scc; + +/* + * At the end of bridge processing we need to end up with an (acyclyc) graph of bridge + * object SCCs, where the links between the nodes (each one an SCC) in that graph represent + * the presence of a direct or indirect link between those SCCs. An example: + * + * D + * | + * v + * A -> B -> c -> e -> F + * + * A, B, D and F are SCCs that contain bridge objects, c and e don't contain bridge objects. + * The graph we need to produce from this is: + * + * D + * | + * v + * A -> B -> F + * + * Note that we don't need to produce an edge from A to F. It's sufficient that F is + * indirectly reachable from A. + * + * The old algorithm would create a set, for each SCC, of bridge SCCs that can reach it, + * directly or indirectly, by merging the ones sets for those that reach it directly. The + * sets it would build up are these: + * + * A: {} + * B: {A} + * c: {B} + * D: {} + * e: {B,D} + * F: {B,D} + * + * The merge operations on these sets turned out to be huge time sinks. + * + * The new algorithm proceeds in two passes: During DFS2, it only builds up the sets of SCCs + * that directly point to each SCC: + * + * A: {} + * B: {A} + * c: {B} + * D: {} + * e: {c,D} + * F: {e} + * + * This is the adjacency list for the SCC graph, in other words. In a separate step + * afterwards, it does a depth-first traversal of that graph, for each bridge node, to get + * to the final list. It uses a flag to avoid traversing any node twice. + */ +static void +scc_add_xref (SCC *src, SCC *dst) +{ + g_assert (src != dst); + g_assert (src->index != dst->index); + +#ifdef NEW_XREFS + /* + * FIXME: Right now we don't even unique the direct ancestors, but just add to the + * list. Doing a containment check slows this algorithm down to almost the speed of + * the old one. Use the flag instead! + */ + dyn_array_int_add (&dst->new_xrefs, src->index); +#endif + +#ifdef OLD_XREFS + if (dyn_array_int_is_copy (&dst->old_xrefs)) { + int i; + dyn_array_int_ensure_independent (&dst->old_xrefs); + for (i = 0; i < dyn_array_int_size (&dst->old_xrefs); ++i) { + int j = dyn_array_int_get (&dst->old_xrefs, i); + SCC *bridge_scc = dyn_array_scc_get_ptr (&sccs, j); + g_assert (!bridge_scc->flag); + bridge_scc->flag = TRUE; + } + } + + if (src->num_bridge_entries) { + if (src->flag) + return; + src->flag = TRUE; + dyn_array_int_add (&dst->old_xrefs, src->index); +#ifdef OPTIMIZATION_COPY + } else if (dyn_array_int_size (&dst->old_xrefs) == 0) { + dyn_array_int_copy (&dst->old_xrefs, &src->old_xrefs); +#endif + } else { + int i; + for (i = 0; i < dyn_array_int_size (&src->old_xrefs); ++i) { + int j = dyn_array_int_get (&src->old_xrefs, i); + SCC *bridge_scc = dyn_array_scc_get_ptr (&sccs, j); + g_assert (bridge_scc->num_bridge_entries); + if (!bridge_scc->flag) { + bridge_scc->flag = TRUE; + dyn_array_int_add (&dst->old_xrefs, j); + } + } + } +#endif +} + +static void +scc_add_entry (SCC *scc, HashEntry *entry) +{ + g_assert (entry->v.dfs2.scc_index < 0); + entry->v.dfs2.scc_index = scc->index; + if (entry->is_bridge) + ++scc->num_bridge_entries; +} + +static void +dfs2 (HashEntry *entry) +{ + int i; + + g_assert (dyn_array_ptr_size (&dfs_stack) == 0); + + dyn_array_ptr_push (&dfs_stack, entry); + + do { + entry = (HashEntry *)dyn_array_ptr_pop (&dfs_stack); + ++dfs2_passes; + + if (entry->v.dfs2.scc_index >= 0) { + if (entry->v.dfs2.scc_index != current_scc->index) + scc_add_xref (dyn_array_scc_get_ptr (&sccs, entry->v.dfs2.scc_index), current_scc); + continue; + } + + scc_add_entry (current_scc, entry); + + for (i = 0; i < dyn_array_ptr_size (&entry->srcs); ++i) + dyn_array_ptr_push (&dfs_stack, dyn_array_ptr_get (&entry->srcs, i)); + } while (dyn_array_ptr_size (&dfs_stack) > 0); + +#ifdef OLD_XREFS + /* If xrefs is a copy then we haven't set a single flag. */ + if (dyn_array_int_is_copy (¤t_scc->old_xrefs)) + return; + for (i = 0; i < dyn_array_int_size (¤t_scc->old_xrefs); ++i) { + int j = dyn_array_int_get (¤t_scc->old_xrefs, i); + SCC *bridge_scc = dyn_array_scc_get_ptr (&sccs, j); + g_assert (bridge_scc->flag); + bridge_scc->flag = FALSE; + } +#endif +} + +#ifdef NEW_XREFS +static void +gather_xrefs (SCC *scc) +{ + int i; + for (i = 0; i < dyn_array_int_size (&scc->new_xrefs); ++i) { + int index = dyn_array_int_get (&scc->new_xrefs, i); + SCC *src = dyn_array_scc_get_ptr (&sccs, index); + if (src->flag) + continue; + src->flag = TRUE; + if (src->num_bridge_entries) + dyn_array_int_add (&merge_array, index); + else + gather_xrefs (src); + } +} + +static void +reset_flags (SCC *scc) +{ + int i; + for (i = 0; i < dyn_array_int_size (&scc->new_xrefs); ++i) { + int index = dyn_array_int_get (&scc->new_xrefs, i); + SCC *src = dyn_array_scc_get_ptr (&sccs, index); + if (!src->flag) + continue; + src->flag = FALSE; + if (!src->num_bridge_entries) + reset_flags (src); + } +} +#endif + +static void +dump_graph (void) +{ + static int counter = 0; + + MonoObject *obj; + HashEntry *entry; + size_t prefix_len = strlen (dump_prefix); + char *filename = (char *)alloca (prefix_len + 64); + FILE *file; + int edge_id = 0; + + sprintf (filename, "%s.%d.gexf", dump_prefix, counter++); + file = fopen (filename, "w"); + + if (file == NULL) { + fprintf (stderr, "Warning: Could not open bridge dump file `%s` for writing: %s\n", filename, strerror (errno)); + return; + } + + fprintf (file, "\n"); + + fprintf (file, "\n" + "\n" + "\n" + "\n" + "\n"); + + fprintf (file, "\n"); + SGEN_HASH_TABLE_FOREACH (&hash_table, MonoObject *, obj, HashEntry *, entry) { + MonoVTable *vt = SGEN_LOAD_VTABLE (obj); + fprintf (file, "\n", + obj, vt->klass->name_space, vt->klass->name, entry->is_bridge ? "true" : "false"); + } SGEN_HASH_TABLE_FOREACH_END; + fprintf (file, "\n"); + + fprintf (file, "\n"); + SGEN_HASH_TABLE_FOREACH (&hash_table, MonoObject *, obj, HashEntry *, entry) { + int i; + for (i = 0; i < dyn_array_ptr_size (&entry->srcs); ++i) { + HashEntry *src = (HashEntry *)dyn_array_ptr_get (&entry->srcs, i); + fprintf (file, "\n", edge_id++, sgen_hash_table_key_for_value_pointer (src), obj); + } + } SGEN_HASH_TABLE_FOREACH_END; + fprintf (file, "\n"); + + fprintf (file, "\n"); + + fclose (file); +} + +static int +compare_hash_entries (const HashEntry *e1, const HashEntry *e2) +{ + /* We can cast to signed int here because finishing_time has only 31 bits. */ + return (gint32)e2->v.dfs1.finishing_time - (gint32)e1->v.dfs1.finishing_time; +} + +DEF_QSORT_INLINE(hash_entries, HashEntry*, compare_hash_entries) + +static gint64 step_1, step_2, step_3, step_4, step_5, step_6; +static int fist_pass_links, second_pass_links, sccs_links; +static int max_sccs_links = 0; + +static void +register_finalized_object (GCObject *obj) +{ + g_assert (sgen_need_bridge_processing ()); + dyn_array_ptr_push (®istered_bridges, obj); +} + +static void +reset_data (void) +{ + dyn_array_ptr_empty (®istered_bridges); +} + +static void +processing_stw_step (void) +{ + int i; + int bridge_count; + MonoObject *obj G_GNUC_UNUSED; + HashEntry *entry; + SGEN_TV_DECLARE (atv); + SGEN_TV_DECLARE (btv); + + if (!dyn_array_ptr_size (®istered_bridges)) + return; + + SGEN_TV_GETTIME (btv); + + /* first DFS pass */ + + dyn_array_ptr_init (&dfs_stack); + dyn_array_int_init (&merge_array); + + current_time = 0; + /* + First we insert all bridges into the hash table and then we do dfs1. + + It must be done in 2 steps since the bridge arrays doesn't come in reverse topological order, + which means that we can have entry N pointing to entry N + 1. + + If we dfs1 entry N before N + 1 is registered we'll not consider N + 1 for this bridge + pass and not create the required xref between the two. + */ + bridge_count = dyn_array_ptr_size (®istered_bridges); + for (i = 0; i < bridge_count ; ++i) + register_bridge_object ((MonoObject *)dyn_array_ptr_get (®istered_bridges, i)); + + for (i = 0; i < bridge_count; ++i) + dfs1 (get_hash_entry ((MonoObject *)dyn_array_ptr_get (®istered_bridges, i), NULL)); + + /* Remove all forwarded objects. */ + SGEN_HASH_TABLE_FOREACH (&hash_table, MonoObject *, obj, HashEntry *, entry) { + if (entry->v.dfs1.forwarded_to) { + g_assert (dyn_array_ptr_size (&entry->srcs) == 0); + SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE); + continue; + } + } SGEN_HASH_TABLE_FOREACH_END; + + SGEN_TV_GETTIME (atv); + step_2 = SGEN_TV_ELAPSED (btv, atv); + + if (dump_prefix) + dump_graph (); +} + +static int num_registered_bridges, hash_table_size; + +static void +processing_build_callback_data (int generation) +{ + int i, j; + int num_sccs, num_xrefs; + int max_entries, max_xrefs; + MonoObject *obj G_GNUC_UNUSED; + HashEntry *entry; + HashEntry **all_entries; + MonoGCBridgeSCC **api_sccs; + MonoGCBridgeXRef *api_xrefs; + SGEN_TV_DECLARE (atv); + SGEN_TV_DECLARE (btv); + + g_assert (bridge_processor->num_sccs == 0 && bridge_processor->num_xrefs == 0); + g_assert (!bridge_processor->api_sccs && !bridge_processor->api_xrefs); + + if (!dyn_array_ptr_size (®istered_bridges)) + return; + + g_assert (bridge_processing_in_progress); + + SGEN_TV_GETTIME (atv); + + /* alloc and fill array of all entries */ + + all_entries = (HashEntry **)sgen_alloc_internal_dynamic (sizeof (HashEntry*) * hash_table.num_entries, INTERNAL_MEM_BRIDGE_DATA, TRUE); + + j = 0; + SGEN_HASH_TABLE_FOREACH (&hash_table, MonoObject *, obj, HashEntry *, entry) { + g_assert (entry->v.dfs1.finishing_time > 0); + all_entries [j++] = entry; + fist_pass_links += dyn_array_ptr_size (&entry->srcs); + } SGEN_HASH_TABLE_FOREACH_END; + g_assert (j == hash_table.num_entries); + hash_table_size = hash_table.num_entries; + + /* sort array according to decreasing finishing time */ + qsort_hash_entries (all_entries, hash_table.num_entries); + + SGEN_HASH_TABLE_FOREACH (&hash_table, MonoObject *, obj, HashEntry *, entry) { + entry->v.dfs2.scc_index = -1; + } SGEN_HASH_TABLE_FOREACH_END; + + SGEN_TV_GETTIME (btv); + step_3 = SGEN_TV_ELAPSED (atv, btv); + + /* second DFS pass */ + + dyn_array_scc_init (&sccs); + for (i = 0; i < hash_table.num_entries; ++i) { + HashEntry *entry = all_entries [i]; + if (entry->v.dfs2.scc_index < 0) { + int index = dyn_array_scc_size (&sccs); + current_scc = dyn_array_scc_add (&sccs); + current_scc->index = index; + current_scc->num_bridge_entries = 0; +#ifdef NEW_XREFS + current_scc->flag = FALSE; + dyn_array_int_init (¤t_scc->new_xrefs); +#endif +#ifdef OLD_XREFS + dyn_array_int_init (¤t_scc->old_xrefs); +#endif + current_scc->api_index = -1; + + dfs2 (entry); + +#ifdef NEW_XREFS + /* + * If a node has only one incoming edge, we just copy the source's + * xrefs array, effectively removing the source from the graph. + * This takes care of long linked lists. + */ + if (!current_scc->num_bridge_entries && dyn_array_int_size (¤t_scc->new_xrefs) == 1) { + SCC *src; + j = dyn_array_int_get (¤t_scc->new_xrefs, 0); + src = dyn_array_scc_get_ptr (&sccs, j); + if (src->num_bridge_entries) + dyn_array_int_set (¤t_scc->new_xrefs, 0, j); + else + dyn_array_int_copy (¤t_scc->new_xrefs, &src->new_xrefs); + } +#endif + } + } + +#ifdef NEW_XREFS +#ifdef TEST_NEW_XREFS + for (j = 0; j < dyn_array_scc_size (&sccs); ++j) { + SCC *scc = dyn_array_scc_get_ptr (&sccs, j); + g_assert (!scc->flag); + } +#endif + + for (i = 0; i < dyn_array_scc_size (&sccs); ++i) { + SCC *scc = dyn_array_scc_get_ptr (&sccs, i); + g_assert (scc->index == i); + if (!scc->num_bridge_entries) + continue; + + dyn_array_int_empty (&merge_array); + gather_xrefs (scc); + reset_flags (scc); + dyn_array_int_copy (&scc->new_xrefs, &merge_array); + dyn_array_int_ensure_independent (&scc->new_xrefs); + +#ifdef TEST_NEW_XREFS + for (j = 0; j < dyn_array_scc_size (&sccs); ++j) { + SCC *scc = dyn_array_scc_get_ptr (&sccs, j); + g_assert (!scc->flag); + } +#endif + } + +#ifdef TEST_NEW_XREFS + for (i = 0; i < dyn_array_scc_size (&sccs); ++i) { + SCC *scc = dyn_array_scc_get_ptr (&sccs, i); + g_assert (scc->index == i); + if (!scc->num_bridge_entries) + continue; + + g_assert (dyn_array_int_size (&scc->new_xrefs) == dyn_array_int_size (&scc->old_xrefs)); + for (j = 0; j < dyn_array_int_size (&scc->new_xrefs); ++j) + g_assert (dyn_array_int_contains (&scc->old_xrefs, dyn_array_int_get (&scc->new_xrefs, j))); + } +#endif +#endif + + /* + * Compute the weight of each object. The weight of an object is its size plus the size of all + * objects it points do. When the an object is pointed by multiple objects we distribute it's weight + * equally among them. This distribution gives a rough estimate of the real impact of making the object + * go away. + * + * The reasoning for this model is that complex graphs with single roots will have a bridge with very high + * value in comparison to others. + * + * The all_entries array has all objects topologically sorted. To correctly propagate the weights it must be + * done in reverse topological order - so we calculate the weight of the pointed-to objects before processing + * pointer-from objects. + * + * We log those objects in the opposite order for no particular reason. The other constrain is that it should use the same + * direction as the other logging loop that records live/dead information. + */ + if (bridge_accounting_enabled) { + for (i = hash_table.num_entries - 1; i >= 0; --i) { + double w; + HashEntryWithAccounting *entry = (HashEntryWithAccounting*)all_entries [i]; + + entry->weight += (double)sgen_safe_object_get_size (sgen_hash_table_key_for_value_pointer (entry)); + w = entry->weight / dyn_array_ptr_size (&entry->entry.srcs); + for (j = 0; j < dyn_array_ptr_size (&entry->entry.srcs); ++j) { + HashEntryWithAccounting *other = (HashEntryWithAccounting *)dyn_array_ptr_get (&entry->entry.srcs, j); + other->weight += w; + } + } + for (i = 0; i < hash_table.num_entries; ++i) { + HashEntryWithAccounting *entry = (HashEntryWithAccounting*)all_entries [i]; + if (entry->entry.is_bridge) { + MonoObject *obj = sgen_hash_table_key_for_value_pointer (entry); + MonoClass *klass = SGEN_LOAD_VTABLE (obj)->klass; + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "OBJECT %s::%s (%p) weight %f", klass->name_space, klass->name, obj, entry->weight); + } + } + } + + for (i = 0; i < hash_table.num_entries; ++i) { + HashEntry *entry = all_entries [i]; + second_pass_links += dyn_array_ptr_size (&entry->srcs); + } + + SGEN_TV_GETTIME (atv); + step_4 = SGEN_TV_ELAPSED (btv, atv); + + //g_print ("%d sccs\n", sccs.size); + + dyn_array_ptr_uninit (&dfs_stack); + + /* init data for callback */ + + num_sccs = 0; + for (i = 0; i < dyn_array_scc_size (&sccs); ++i) { + SCC *scc = dyn_array_scc_get_ptr (&sccs, i); + g_assert (scc->index == i); + if (scc->num_bridge_entries) + ++num_sccs; + sccs_links += dyn_array_int_size (&scc->XREFS); + max_sccs_links = MAX (max_sccs_links, dyn_array_int_size (&scc->XREFS)); + } + + api_sccs = (MonoGCBridgeSCC **)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeSCC*) * num_sccs, INTERNAL_MEM_BRIDGE_DATA, TRUE); + num_xrefs = 0; + j = 0; + for (i = 0; i < dyn_array_scc_size (&sccs); ++i) { + SCC *scc = dyn_array_scc_get_ptr (&sccs, i); + if (!scc->num_bridge_entries) + continue; + + api_sccs [j] = (MonoGCBridgeSCC *)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeSCC) + sizeof (MonoObject*) * scc->num_bridge_entries, INTERNAL_MEM_BRIDGE_DATA, TRUE); + api_sccs [j]->is_alive = FALSE; + api_sccs [j]->num_objs = scc->num_bridge_entries; + scc->num_bridge_entries = 0; + scc->api_index = j++; + + num_xrefs += dyn_array_int_size (&scc->XREFS); + } + + SGEN_HASH_TABLE_FOREACH (&hash_table, MonoObject *, obj, HashEntry *, entry) { + if (entry->is_bridge) { + SCC *scc = dyn_array_scc_get_ptr (&sccs, entry->v.dfs2.scc_index); + api_sccs [scc->api_index]->objs [scc->num_bridge_entries++] = sgen_hash_table_key_for_value_pointer (entry); + } + } SGEN_HASH_TABLE_FOREACH_END; + + api_xrefs = (MonoGCBridgeXRef *)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeXRef) * num_xrefs, INTERNAL_MEM_BRIDGE_DATA, TRUE); + j = 0; + for (i = 0; i < dyn_array_scc_size (&sccs); ++i) { + int k; + SCC *scc = dyn_array_scc_get_ptr (&sccs, i); + if (!scc->num_bridge_entries) + continue; + for (k = 0; k < dyn_array_int_size (&scc->XREFS); ++k) { + SCC *src_scc = dyn_array_scc_get_ptr (&sccs, dyn_array_int_get (&scc->XREFS, k)); + if (!src_scc->num_bridge_entries) + continue; + api_xrefs [j].src_scc_index = src_scc->api_index; + api_xrefs [j].dst_scc_index = scc->api_index; + ++j; + } + } + + SGEN_TV_GETTIME (btv); + step_5 = SGEN_TV_ELAPSED (atv, btv); + + /* free data */ + + j = 0; + max_entries = max_xrefs = 0; + for (i = 0; i < dyn_array_scc_size (&sccs); ++i) { + SCC *scc = dyn_array_scc_get_ptr (&sccs, i); + if (scc->num_bridge_entries) + ++j; + if (scc->num_bridge_entries > max_entries) + max_entries = scc->num_bridge_entries; + if (dyn_array_int_size (&scc->XREFS) > max_xrefs) + max_xrefs = dyn_array_int_size (&scc->XREFS); +#ifdef NEW_XREFS + dyn_array_int_uninit (&scc->new_xrefs); +#endif +#ifdef OLD_XREFS + dyn_array_int_uninit (&scc->old_xrefs); +#endif + + } + dyn_array_scc_uninit (&sccs); + + sgen_free_internal_dynamic (all_entries, sizeof (HashEntry*) * hash_table.num_entries, INTERNAL_MEM_BRIDGE_DATA); + + free_data (); + /* Empty the registered bridges array */ + num_registered_bridges = dyn_array_ptr_size (®istered_bridges); + dyn_array_ptr_empty (®istered_bridges); + + SGEN_TV_GETTIME (atv); + step_6 = SGEN_TV_ELAPSED (btv, atv); + + //g_print ("%d sccs containing bridges - %d max bridge objects - %d max xrefs\n", j, max_entries, max_xrefs); + + bridge_processor->num_sccs = num_sccs; + bridge_processor->api_sccs = api_sccs; + bridge_processor->num_xrefs = num_xrefs; + bridge_processor->api_xrefs = api_xrefs; +} + +static void +processing_after_callback (int generation) +{ + int i, j; + int num_sccs = bridge_processor->num_sccs; + MonoGCBridgeSCC **api_sccs = bridge_processor->api_sccs; + + if (bridge_accounting_enabled) { + for (i = 0; i < num_sccs; ++i) { + for (j = 0; j < api_sccs [i]->num_objs; ++j) { + GCVTable vtable = SGEN_LOAD_VTABLE (api_sccs [i]->objs [j]); + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, + "OBJECT %s.%s (%p) SCC [%d] %s", + sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable), api_sccs [i]->objs [j], + i, + api_sccs [i]->is_alive ? "ALIVE" : "DEAD"); + } + } + } + + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_NEW_BRIDGE num-objects %d num_hash_entries %d sccs size %d init %.2fms df1 %.2fms sort %.2fms dfs2 %.2fms setup-cb %.2fms free-data %.2fms links %d/%d/%d/%d dfs passes %d/%d ignored %d", + num_registered_bridges, hash_table_size, dyn_array_scc_size (&sccs), + step_1 / 10000.0f, + step_2 / 10000.0f, + step_3 / 10000.0f, + step_4 / 10000.0f, + step_5 / 10000.0f, + step_6 / 10000.0f, + fist_pass_links, second_pass_links, sccs_links, max_sccs_links, + dfs1_passes, dfs2_passes, ignored_objects); + + step_1 = 0; /* We must cleanup since this value is used as an accumulator. */ + fist_pass_links = second_pass_links = sccs_links = max_sccs_links = 0; + dfs1_passes = dfs2_passes = ignored_objects = 0; +} + +static void +describe_pointer (GCObject *obj) +{ + HashEntry *entry; + int i; + + for (i = 0; i < dyn_array_ptr_size (®istered_bridges); ++i) { + if (obj == dyn_array_ptr_get (®istered_bridges, i)) { + printf ("Pointer is a registered bridge object.\n"); + break; + } + } + + entry = (HashEntry *)sgen_hash_table_lookup (&hash_table, obj); + if (!entry) + return; + + printf ("Bridge hash table entry %p:\n", entry); + printf (" is bridge: %d\n", (int)entry->is_bridge); + printf (" is visited: %d\n", (int)entry->v.dfs1.is_visited); +} + +void +sgen_new_bridge_init (SgenBridgeProcessor *collector) +{ + collector->reset_data = reset_data; + collector->processing_stw_step = processing_stw_step; + collector->processing_build_callback_data = processing_build_callback_data; + collector->processing_after_callback = processing_after_callback; + collector->class_kind = class_kind; + collector->register_finalized_object = register_finalized_object; + collector->describe_pointer = describe_pointer; + collector->set_config = set_config; + + bridge_processor = collector; +} + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/sgen-old-bridge.c b/unity-2019.4.24f1-mbe/mono/metadata/sgen-old-bridge.c new file mode 100644 index 000000000..12f82b07f --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/sgen-old-bridge.c @@ -0,0 +1,933 @@ +/** + * \file + * Simple generational GC. + * + * Copyright 2011 Novell, Inc (http://www.novell.com) + * Copyright 2011 Xamarin Inc (http://www.xamarin.com) + * Copyright 2001-2003 Ximian, Inc + * Copyright 2003-2010 Novell, Inc. + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include "config.h" + +#ifdef HAVE_SGEN_GC + +#include + +#include "sgen/sgen-gc.h" +#include "sgen-bridge-internals.h" +#include "sgen/sgen-hash-table.h" +#include "sgen/sgen-qsort.h" +#include "sgen/sgen-client.h" +#include "utils/mono-logger-internals.h" + +typedef struct { + int size; + int capacity; + char *data; +} DynArray; + +/*Specializations*/ + +typedef struct { + DynArray array; +} DynIntArray; + +typedef struct { + DynArray array; +} DynPtrArray; + +typedef struct { + DynArray array; +} DynSCCArray; + +/* + * Bridge data for a single managed object + * + * FIXME: Optimizations: + * + * Don't allocate a srcs array for just one source. Most objects have + * just one source, so use the srcs pointer itself. + */ +typedef struct _HashEntry { + GCObject *obj; /* This is a duplicate - it's already stored in the hash table */ + + gboolean is_bridge; + gboolean is_visited; + + int finishing_time; + + // "Source" managed objects pointing at this destination + DynPtrArray srcs; + + // Index in sccs array of SCC this object was folded into + int scc_index; +} HashEntry; + +typedef struct { + HashEntry entry; + double weight; +} HashEntryWithAccounting; + +// The graph of managed objects/HashEntries is reduced to a graph of strongly connected components +typedef struct _SCC { + int index; + int api_index; + + // How many bridged objects does this SCC hold references to? + int num_bridge_entries; + + // Index in global sccs array of SCCs holding pointers to this SCC + DynIntArray xrefs; /* these are incoming, not outgoing */ +} SCC; + +// Maps managed objects to corresponding HashEntry stricts +static SgenHashTable hash_table = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_OLD_BRIDGE_HASH_TABLE, INTERNAL_MEM_OLD_BRIDGE_HASH_TABLE_ENTRY, sizeof (HashEntry), mono_aligned_addr_hash, NULL); + +static int current_time; + +static gboolean bridge_accounting_enabled = FALSE; + +static SgenBridgeProcessor *bridge_processor; + +/* Core functions */ +/* public */ + +/* private */ + +static void +dyn_array_init (DynArray *da) +{ + da->size = 0; + da->capacity = 0; + da->data = NULL; +} + +static void +dyn_array_uninit (DynArray *da, int elem_size) +{ + if (da->capacity <= 0) + return; + + sgen_free_internal_dynamic (da->data, elem_size * da->capacity, INTERNAL_MEM_BRIDGE_DATA); + da->data = NULL; +} + +static void +dyn_array_ensure_capacity (DynArray *da, int capacity, int elem_size) +{ + int old_capacity = da->capacity; + char *new_data; + + if (capacity <= old_capacity) + return; + + if (da->capacity == 0) + da->capacity = 2; + while (capacity > da->capacity) + da->capacity *= 2; + + new_data = (char *)sgen_alloc_internal_dynamic (elem_size * da->capacity, INTERNAL_MEM_BRIDGE_DATA, TRUE); + memcpy (new_data, da->data, elem_size * da->size); + sgen_free_internal_dynamic (da->data, elem_size * old_capacity, INTERNAL_MEM_BRIDGE_DATA); + da->data = new_data; +} + +static void* +dyn_array_add (DynArray *da, int elem_size) +{ + void *p; + + dyn_array_ensure_capacity (da, da->size + 1, elem_size); + + p = da->data + da->size * elem_size; + ++da->size; + return p; +} + +/* int */ +static void +dyn_array_int_init (DynIntArray *da) +{ + dyn_array_init (&da->array); +} + +static void +dyn_array_int_uninit (DynIntArray *da) +{ + dyn_array_uninit (&da->array, sizeof (int)); +} + +static int +dyn_array_int_size (DynIntArray *da) +{ + return da->array.size; +} + +static void +dyn_array_int_set_size (DynIntArray *da, int size) +{ + da->array.size = size; +} + +static void +dyn_array_int_add (DynIntArray *da, int x) +{ + int *p = (int *)dyn_array_add (&da->array, sizeof (int)); + *p = x; +} + +static int +dyn_array_int_get (DynIntArray *da, int x) +{ + return ((int*)da->array.data)[x]; +} + +static void +dyn_array_int_set (DynIntArray *da, int idx, int val) +{ + ((int*)da->array.data)[idx] = val; +} + +static void +dyn_array_int_ensure_capacity (DynIntArray *da, int capacity) +{ + dyn_array_ensure_capacity (&da->array, capacity, sizeof (int)); +} + +static void +dyn_array_int_set_all (DynIntArray *dst, DynIntArray *src) +{ + dyn_array_int_ensure_capacity (dst, src->array.size); + memcpy (dst->array.data, src->array.data, src->array.size * sizeof (int)); + dst->array.size = src->array.size; +} + +/* ptr */ + +static void +dyn_array_ptr_init (DynPtrArray *da) +{ + dyn_array_init (&da->array); +} + +static void +dyn_array_ptr_uninit (DynPtrArray *da) +{ + dyn_array_uninit (&da->array, sizeof (void*)); +} + +static int +dyn_array_ptr_size (DynPtrArray *da) +{ + return da->array.size; +} + +static void +dyn_array_ptr_set_size (DynPtrArray *da, int size) +{ + da->array.size = size; +} + +static void* +dyn_array_ptr_get (DynPtrArray *da, int x) +{ + return ((void**)da->array.data)[x]; +} + +static void +dyn_array_ptr_add (DynPtrArray *da, void *ptr) +{ + void **p = (void **)dyn_array_add (&da->array, sizeof (void*)); + *p = ptr; +} + +#define dyn_array_ptr_push dyn_array_ptr_add + +static void* +dyn_array_ptr_pop (DynPtrArray *da) +{ + void *p; + int size = da->array.size; + g_assert (size > 0); + p = dyn_array_ptr_get (da, size - 1); + --da->array.size; + return p; +} + +/*SCC */ + +static void +dyn_array_scc_init (DynSCCArray *da) +{ + dyn_array_init (&da->array); +} + +static void +dyn_array_scc_uninit (DynSCCArray *da) +{ + dyn_array_uninit (&da->array, sizeof (SCC)); +} + +static int +dyn_array_scc_size (DynSCCArray *da) +{ + return da->array.size; +} + +static SCC* +dyn_array_scc_add (DynSCCArray *da) +{ + return (SCC *)dyn_array_add (&da->array, sizeof (SCC)); +} + +static SCC* +dyn_array_scc_get_ptr (DynSCCArray *da, int x) +{ + return &((SCC*)da->array.data)[x]; +} + +/* Merge code*/ + +static DynIntArray merge_array; + +static gboolean +dyn_array_int_contains (DynIntArray *da, int x) +{ + int i; + for (i = 0; i < dyn_array_int_size (da); ++i) + if (dyn_array_int_get (da, i) == x) + return TRUE; + return FALSE; +} + + +static void +dyn_array_int_merge (DynIntArray *dst, DynIntArray *src) +{ + int i, j; + + dyn_array_int_ensure_capacity (&merge_array, dyn_array_int_size (dst) + dyn_array_int_size (src)); + dyn_array_int_set_size (&merge_array, 0); + + for (i = j = 0; i < dyn_array_int_size (dst) || j < dyn_array_int_size (src); ) { + if (i < dyn_array_int_size (dst) && j < dyn_array_int_size (src)) { + int a = dyn_array_int_get (dst, i); + int b = dyn_array_int_get (src, j); + if (a < b) { + dyn_array_int_add (&merge_array, a); + ++i; + } else if (a == b) { + dyn_array_int_add (&merge_array, a); + ++i; + ++j; + } else { + dyn_array_int_add (&merge_array, b); + ++j; + } + } else if (i < dyn_array_int_size (dst)) { + dyn_array_int_add (&merge_array, dyn_array_int_get (dst, i)); + ++i; + } else { + dyn_array_int_add (&merge_array, dyn_array_int_get (src, j)); + ++j; + } + } + + if (dyn_array_int_size (&merge_array) > dyn_array_int_size (dst)) { + dyn_array_int_set_all (dst, &merge_array); + } +} + +static void +dyn_array_int_merge_one (DynIntArray *array, int value) +{ + int i; + int tmp; + int size = dyn_array_int_size (array); + + for (i = 0; i < size; ++i) { + if (dyn_array_int_get (array, i) == value) + return; + else if (dyn_array_int_get (array, i) > value) + break; + } + + dyn_array_int_ensure_capacity (array, size + 1); + + if (i < size) { + tmp = dyn_array_int_get (array, i); + for (; i < size; ++i) { + dyn_array_int_set (array, i, value); + value = tmp; + tmp = dyn_array_int_get (array, i + 1); + } + dyn_array_int_set (array, size, value); + } else { + dyn_array_int_set (array, size, value); + } + + dyn_array_int_set_size (array, size + 1); +} + + +static void +set_config (const SgenBridgeProcessorConfig *config) +{ + if (config->accounting) { + SgenHashTable table = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_HASH_TABLE, INTERNAL_MEM_BRIDGE_HASH_TABLE_ENTRY, sizeof (HashEntryWithAccounting), mono_aligned_addr_hash, NULL); + bridge_accounting_enabled = TRUE; + hash_table = table; + } +} + +static MonoGCBridgeObjectKind +class_kind (MonoClass *klass) +{ + return bridge_callbacks.bridge_class_kind (klass); +} + +static HashEntry* +get_hash_entry (GCObject *obj, gboolean *existing) +{ + HashEntry *entry = (HashEntry *)sgen_hash_table_lookup (&hash_table, obj); + HashEntry new_entry; + + if (entry) { + if (existing) + *existing = TRUE; + return entry; + } + if (existing) + *existing = FALSE; + + memset (&new_entry, 0, sizeof (HashEntry)); + + new_entry.obj = obj; + dyn_array_ptr_init (&new_entry.srcs); + new_entry.finishing_time = -1; + new_entry.scc_index = -1; + + sgen_hash_table_replace (&hash_table, obj, &new_entry, NULL); + + return (HashEntry *)sgen_hash_table_lookup (&hash_table, obj); +} + +static void +add_source (HashEntry *entry, HashEntry *src) +{ + dyn_array_ptr_add (&entry->srcs, src); +} + +static void +free_data (void) +{ + GCObject *obj G_GNUC_UNUSED; + HashEntry *entry; + int total_srcs = 0; + int max_srcs = 0; + + SGEN_HASH_TABLE_FOREACH (&hash_table, GCObject *, obj, HashEntry *, entry) { + int entry_size = dyn_array_ptr_size (&entry->srcs); + total_srcs += entry_size; + if (entry_size > max_srcs) + max_srcs = entry_size; + dyn_array_ptr_uninit (&entry->srcs); + } SGEN_HASH_TABLE_FOREACH_END; + + sgen_hash_table_clean (&hash_table); + + dyn_array_int_uninit (&merge_array); + //g_print ("total srcs %d - max %d\n", total_srcs, max_srcs); +} + +static HashEntry* +register_bridge_object (GCObject *obj) +{ + HashEntry *entry = get_hash_entry (obj, NULL); + entry->is_bridge = TRUE; + return entry; +} + +static void +register_finishing_time (HashEntry *entry, int t) +{ + g_assert (entry->finishing_time < 0); + entry->finishing_time = t; +} + +static gboolean +object_is_live (GCObject **objp) +{ + GCObject *obj = *objp; + GCObject *fwd = SGEN_OBJECT_IS_FORWARDED (obj); + if (fwd) { + *objp = fwd; + return sgen_hash_table_lookup (&hash_table, fwd) == NULL; + } + if (!sgen_object_is_live (obj)) + return FALSE; + return sgen_hash_table_lookup (&hash_table, obj) == NULL; +} + +static DynPtrArray registered_bridges; +static DynPtrArray dfs_stack; + +static int dfs1_passes, dfs2_passes; + + +#undef HANDLE_PTR +#define HANDLE_PTR(ptr,obj) do { \ + GCObject *dst = (GCObject*)*(ptr); \ + if (dst && !object_is_live (&dst)) { \ + dyn_array_ptr_push (&dfs_stack, obj_entry); \ + dyn_array_ptr_push (&dfs_stack, get_hash_entry (dst, NULL)); \ + } \ + } while (0) + +static void +dfs1 (HashEntry *obj_entry) +{ + HashEntry *src; + g_assert (dyn_array_ptr_size (&dfs_stack) == 0); + + dyn_array_ptr_push (&dfs_stack, NULL); + dyn_array_ptr_push (&dfs_stack, obj_entry); + + do { + GCObject *obj; + ++dfs1_passes; + + obj_entry = (HashEntry *)dyn_array_ptr_pop (&dfs_stack); + if (obj_entry) { + char *start; + mword desc; + src = (HashEntry *)dyn_array_ptr_pop (&dfs_stack); + + obj = obj_entry->obj; + desc = sgen_obj_get_descriptor_safe (obj); + + if (src) { + //g_print ("link %s -> %s\n", sgen_safe_name (src->obj), sgen_safe_name (obj)); + add_source (obj_entry, src); + } else { + //g_print ("starting with %s\n", sgen_safe_name (obj)); + } + + if (obj_entry->is_visited) + continue; + + obj_entry->is_visited = TRUE; + + dyn_array_ptr_push (&dfs_stack, obj_entry); + /* NULL marks that the next entry is to be finished */ + dyn_array_ptr_push (&dfs_stack, NULL); + + start = (char*)obj; +#include "sgen/sgen-scan-object.h" + } else { + obj_entry = (HashEntry *)dyn_array_ptr_pop (&dfs_stack); + + //g_print ("finish %s\n", sgen_safe_name (obj_entry->obj)); + register_finishing_time (obj_entry, current_time++); + } + } while (dyn_array_ptr_size (&dfs_stack) > 0); +} + +static void +scc_add_xref (SCC *src, SCC *dst) +{ + g_assert (src != dst); + g_assert (src->index != dst->index); + + if (dyn_array_int_contains (&dst->xrefs, src->index)) + return; + if (src->num_bridge_entries) { + dyn_array_int_merge_one (&dst->xrefs, src->index); + } else { + int i; + dyn_array_int_merge (&dst->xrefs, &src->xrefs); + for (i = 0; i < dyn_array_int_size (&dst->xrefs); ++i) + g_assert (dyn_array_int_get (&dst->xrefs, i) != dst->index); + } +} + +static void +scc_add_entry (SCC *scc, HashEntry *entry) +{ + g_assert (entry->scc_index < 0); + entry->scc_index = scc->index; + if (entry->is_bridge) + ++scc->num_bridge_entries; +} + +static DynSCCArray sccs; +static SCC *current_scc; + +static void +dfs2 (HashEntry *entry) +{ + int i; + + g_assert (dyn_array_ptr_size (&dfs_stack) == 0); + + dyn_array_ptr_push (&dfs_stack, entry); + + do { + entry = (HashEntry *)dyn_array_ptr_pop (&dfs_stack); + ++dfs2_passes; + + if (entry->scc_index >= 0) { + if (entry->scc_index != current_scc->index) + scc_add_xref (dyn_array_scc_get_ptr (&sccs, entry->scc_index), current_scc); + continue; + } + + scc_add_entry (current_scc, entry); + + for (i = 0; i < dyn_array_ptr_size (&entry->srcs); ++i) + dyn_array_ptr_push (&dfs_stack, dyn_array_ptr_get (&entry->srcs, i)); + } while (dyn_array_ptr_size (&dfs_stack) > 0); +} + +static int +compare_hash_entries (const HashEntry *e1, const HashEntry *e2) +{ + return e2->finishing_time - e1->finishing_time; +} + +DEF_QSORT_INLINE(hash_entries, HashEntry*, compare_hash_entries) + +static gint64 step_1, step_2, step_3, step_4, step_5, step_6; +static int fist_pass_links, second_pass_links, sccs_links; +static int max_sccs_links = 0; + +static void +register_finalized_object (GCObject *obj) +{ + g_assert (sgen_need_bridge_processing ()); + dyn_array_ptr_push (®istered_bridges, obj); +} + +static void +reset_data (void) +{ + dyn_array_ptr_set_size (®istered_bridges, 0); +} + +static void +processing_stw_step (void) +{ + int i; + int bridge_count; + SGEN_TV_DECLARE (atv); + SGEN_TV_DECLARE (btv); + + if (!dyn_array_ptr_size (®istered_bridges)) + return; + + SGEN_TV_GETTIME (btv); + + /* first DFS pass */ + + dyn_array_ptr_init (&dfs_stack); + dyn_array_int_init (&merge_array); + + current_time = 0; + /* + First we insert all bridges into the hash table and then we do dfs1. + + It must be done in 2 steps since the bridge arrays doesn't come in reverse topological order, + which means that we can have entry N pointing to entry N + 1. + + If we dfs1 entry N before N + 1 is registered we'll not consider N + 1 for this bridge + pass and not create the required xref between the two. + */ + bridge_count = dyn_array_ptr_size (®istered_bridges); + for (i = 0; i < bridge_count ; ++i) + register_bridge_object ((GCObject *)dyn_array_ptr_get (®istered_bridges, i)); + + for (i = 0; i < bridge_count; ++i) + dfs1 (get_hash_entry ((GCObject *)dyn_array_ptr_get (®istered_bridges, i), NULL)); + + SGEN_TV_GETTIME (atv); + step_2 = SGEN_TV_ELAPSED (btv, atv); +} + +static int num_registered_bridges, hash_table_size; + +static void +processing_build_callback_data (int generation) +{ + int i, j; + int num_sccs, num_xrefs; + int max_entries, max_xrefs; + GCObject *obj G_GNUC_UNUSED; + HashEntry *entry; + HashEntry **all_entries; + MonoGCBridgeSCC **api_sccs; + MonoGCBridgeXRef *api_xrefs; + SGEN_TV_DECLARE (atv); + SGEN_TV_DECLARE (btv); + + g_assert (bridge_processor->num_sccs == 0 && bridge_processor->num_xrefs == 0); + g_assert (!bridge_processor->api_sccs && !bridge_processor->api_xrefs); + + if (!dyn_array_ptr_size (®istered_bridges)) + return; + + g_assert (bridge_processing_in_progress); + + SGEN_TV_GETTIME (atv); + + /* alloc and fill array of all entries */ + + all_entries = (HashEntry **)sgen_alloc_internal_dynamic (sizeof (HashEntry*) * hash_table.num_entries, INTERNAL_MEM_BRIDGE_DATA, TRUE); + + j = 0; + SGEN_HASH_TABLE_FOREACH (&hash_table, GCObject *, obj, HashEntry *, entry) { + g_assert (entry->finishing_time >= 0); + all_entries [j++] = entry; + fist_pass_links += dyn_array_ptr_size (&entry->srcs); + } SGEN_HASH_TABLE_FOREACH_END; + g_assert (j == hash_table.num_entries); + hash_table_size = hash_table.num_entries; + + /* sort array according to decreasing finishing time */ + qsort_hash_entries (all_entries, hash_table.num_entries); + + SGEN_TV_GETTIME (btv); + step_3 = SGEN_TV_ELAPSED (atv, btv); + + /* second DFS pass */ + + dyn_array_scc_init (&sccs); + for (i = 0; i < hash_table.num_entries; ++i) { + HashEntry *entry = all_entries [i]; + if (entry->scc_index < 0) { + int index = dyn_array_scc_size (&sccs); + current_scc = dyn_array_scc_add (&sccs); + current_scc->index = index; + current_scc->num_bridge_entries = 0; + current_scc->api_index = -1; + dyn_array_int_init (¤t_scc->xrefs); + + dfs2 (entry); + } + } + + /* + * Compute the weight of each object. The weight of an object is its size plus the size of all + * objects it points do. When the an object is pointed by multiple objects we distribute it's weight + * equally among them. This distribution gives a rough estimate of the real impact of making the object + * go away. + * + * The reasoning for this model is that complex graphs with single roots will have a bridge with very high + * value in comparison to others. + * + * The all_entries array has all objects topologically sorted. To correctly propagate the weights it must be + * done in reverse topological order - so we calculate the weight of the pointed-to objects before processing + * pointer-from objects. + * + * We log those objects in the opposite order for no particular reason. The other constrain is that it should use the same + * direction as the other logging loop that records live/dead information. + */ + if (bridge_accounting_enabled) { + for (i = hash_table.num_entries - 1; i >= 0; --i) { + double w; + HashEntryWithAccounting *entry = (HashEntryWithAccounting*)all_entries [i]; + + entry->weight += (double)sgen_safe_object_get_size (entry->entry.obj); + w = entry->weight / dyn_array_ptr_size (&entry->entry.srcs); + for (j = 0; j < dyn_array_ptr_size (&entry->entry.srcs); ++j) { + HashEntryWithAccounting *other = (HashEntryWithAccounting *)dyn_array_ptr_get (&entry->entry.srcs, j); + other->weight += w; + } + } + for (i = 0; i < hash_table.num_entries; ++i) { + HashEntryWithAccounting *entry = (HashEntryWithAccounting*)all_entries [i]; + if (entry->entry.is_bridge) { + MonoClass *klass = SGEN_LOAD_VTABLE (entry->entry.obj)->klass; + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "OBJECT %s::%s (%p) weight %f", klass->name_space, klass->name, entry->entry.obj, entry->weight); + } + } + } + + for (i = 0; i < hash_table.num_entries; ++i) { + HashEntry *entry = all_entries [i]; + second_pass_links += dyn_array_ptr_size (&entry->srcs); + } + + SGEN_TV_GETTIME (atv); + step_4 = SGEN_TV_ELAPSED (btv, atv); + + //g_print ("%d sccs\n", sccs.size); + + dyn_array_ptr_uninit (&dfs_stack); + + /* init data for callback */ + + num_sccs = 0; + for (i = 0; i < dyn_array_scc_size (&sccs); ++i) { + SCC *scc = dyn_array_scc_get_ptr (&sccs, i); + g_assert (scc->index == i); + if (scc->num_bridge_entries) + ++num_sccs; + sccs_links += dyn_array_int_size (&scc->xrefs); + max_sccs_links = MAX (max_sccs_links, dyn_array_int_size (&scc->xrefs)); + } + + api_sccs = (MonoGCBridgeSCC **)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeSCC*) * num_sccs, INTERNAL_MEM_BRIDGE_DATA, TRUE); + num_xrefs = 0; + j = 0; + for (i = 0; i < dyn_array_scc_size (&sccs); ++i) { + SCC *scc = dyn_array_scc_get_ptr (&sccs, i); + if (!scc->num_bridge_entries) + continue; + + api_sccs [j] = (MonoGCBridgeSCC *)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeSCC) + sizeof (MonoObject*) * scc->num_bridge_entries, INTERNAL_MEM_BRIDGE_DATA, TRUE); + api_sccs [j]->is_alive = FALSE; + api_sccs [j]->num_objs = scc->num_bridge_entries; + scc->num_bridge_entries = 0; + scc->api_index = j++; + + num_xrefs += dyn_array_int_size (&scc->xrefs); + } + + SGEN_HASH_TABLE_FOREACH (&hash_table, GCObject *, obj, HashEntry *, entry) { + if (entry->is_bridge) { + SCC *scc = dyn_array_scc_get_ptr (&sccs, entry->scc_index); + api_sccs [scc->api_index]->objs [scc->num_bridge_entries++] = (MonoObject*)entry->obj; + } + } SGEN_HASH_TABLE_FOREACH_END; + + api_xrefs = (MonoGCBridgeXRef *)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeXRef) * num_xrefs, INTERNAL_MEM_BRIDGE_DATA, TRUE); + j = 0; + for (i = 0; i < dyn_array_scc_size (&sccs); ++i) { + int k; + SCC *scc = dyn_array_scc_get_ptr (&sccs, i); + if (!scc->num_bridge_entries) + continue; + for (k = 0; k < dyn_array_int_size (&scc->xrefs); ++k) { + SCC *src_scc = dyn_array_scc_get_ptr (&sccs, dyn_array_int_get (&scc->xrefs, k)); + if (!src_scc->num_bridge_entries) + continue; + api_xrefs [j].src_scc_index = src_scc->api_index; + api_xrefs [j].dst_scc_index = scc->api_index; + ++j; + } + } + + SGEN_TV_GETTIME (btv); + step_5 = SGEN_TV_ELAPSED (atv, btv); + + /* free data */ + + j = 0; + max_entries = max_xrefs = 0; + for (i = 0; i < dyn_array_scc_size (&sccs); ++i) { + SCC *scc = dyn_array_scc_get_ptr (&sccs, i); + if (scc->num_bridge_entries) + ++j; + if (scc->num_bridge_entries > max_entries) + max_entries = scc->num_bridge_entries; + if (dyn_array_int_size (&scc->xrefs) > max_xrefs) + max_xrefs = dyn_array_int_size (&scc->xrefs); + dyn_array_int_uninit (&scc->xrefs); + + } + dyn_array_scc_uninit (&sccs); + + sgen_free_internal_dynamic (all_entries, sizeof (HashEntry*) * hash_table.num_entries, INTERNAL_MEM_BRIDGE_DATA); + + free_data (); + /* Empty the registered bridges array */ + num_registered_bridges = dyn_array_ptr_size (®istered_bridges); + dyn_array_ptr_set_size (®istered_bridges, 0); + + SGEN_TV_GETTIME (atv); + step_6 = SGEN_TV_ELAPSED (btv, atv); + + //g_print ("%d sccs containing bridges - %d max bridge objects - %d max xrefs\n", j, max_entries, max_xrefs); + + bridge_processor->num_sccs = num_sccs; + bridge_processor->api_sccs = api_sccs; + bridge_processor->num_xrefs = num_xrefs; + bridge_processor->api_xrefs = api_xrefs; +} + +static void +processing_after_callback (int generation) +{ + int i, j; + int num_sccs = bridge_processor->num_sccs; + MonoGCBridgeSCC **api_sccs = bridge_processor->api_sccs; + + if (bridge_accounting_enabled) { + for (i = 0; i < num_sccs; ++i) { + for (j = 0; j < api_sccs [i]->num_objs; ++j) { + GCVTable vtable = SGEN_LOAD_VTABLE (api_sccs [i]->objs [j]); + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, + "OBJECT %s.%s (%p) SCC [%d] %s", + sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable), api_sccs [i]->objs [j], + i, + api_sccs [i]->is_alive ? "ALIVE" : "DEAD"); + } + } + } + + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_OLD_BRIDGE num-objects %d num_hash_entries %d sccs size %d init %.2fms df1 %.2fms sort %.2fms dfs2 %.2fms setup-cb %.2fms free-data %.2fms links %d/%d/%d/%d dfs passes %d/%d", + num_registered_bridges, hash_table_size, dyn_array_scc_size (&sccs), + step_1 / 10000.0f, + step_2 / 10000.0f, + step_3 / 10000.0f, + step_4 / 10000.0f, + step_5 / 10000.0f, + step_6 / 10000.0f, + fist_pass_links, second_pass_links, sccs_links, max_sccs_links, + dfs1_passes, dfs2_passes); + + step_1 = 0; /* We must cleanup since this value is used as an accumulator. */ + fist_pass_links = second_pass_links = sccs_links = max_sccs_links = 0; + dfs1_passes = dfs2_passes = 0; +} + +static void +describe_pointer (GCObject *obj) +{ + HashEntry *entry; + int i; + + for (i = 0; i < dyn_array_ptr_size (®istered_bridges); ++i) { + if (obj == dyn_array_ptr_get (®istered_bridges, i)) { + printf ("Pointer is a registered bridge object.\n"); + break; + } + } + + entry = (HashEntry *)sgen_hash_table_lookup (&hash_table, obj); + if (!entry) + return; + + printf ("Bridge hash table entry %p:\n", entry); + printf (" is bridge: %d\n", (int)entry->is_bridge); + printf (" is visited: %d\n", (int)entry->is_visited); +} + +void +sgen_old_bridge_init (SgenBridgeProcessor *collector) +{ + collector->reset_data = reset_data; + collector->processing_stw_step = processing_stw_step; + collector->processing_build_callback_data = processing_build_callback_data; + collector->processing_after_callback = processing_after_callback; + collector->class_kind = class_kind; + collector->register_finalized_object = register_finalized_object; + collector->describe_pointer = describe_pointer; + collector->set_config = set_config; + + bridge_processor = collector; +} + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/sgen-stw.c b/unity-2019.4.24f1-mbe/mono/metadata/sgen-stw.c new file mode 100644 index 000000000..a8a1b9bf3 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/sgen-stw.c @@ -0,0 +1,405 @@ +/** + * \file + * Stop the world functionality + * + * Author: + * Paolo Molaro (lupus@ximian.com) + * Rodrigo Kumpera (kumpera@gmail.com) + * + * Copyright 2005-2011 Novell, Inc (http://www.novell.com) + * Copyright 2011 Xamarin Inc (http://www.xamarin.com) + * Copyright 2011 Xamarin, Inc. + * Copyright (C) 2012 Xamarin Inc + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include "config.h" +#ifdef HAVE_SGEN_GC + +#include "sgen/sgen-gc.h" +#include "sgen/sgen-protocol.h" +#include "sgen/sgen-memory-governor.h" +#include "sgen/sgen-workers.h" +#include "metadata/profiler-private.h" +#include "sgen/sgen-client.h" +#include "metadata/sgen-bridge-internals.h" +#include "metadata/gc-internals.h" +#include "utils/mono-threads.h" +#include "utils/mono-threads-debug.h" + +#define TV_DECLARE SGEN_TV_DECLARE +#define TV_GETTIME SGEN_TV_GETTIME +#define TV_ELAPSED SGEN_TV_ELAPSED + +static void sgen_unified_suspend_restart_world (void); +static void sgen_unified_suspend_stop_world (void); + +static TV_DECLARE (end_of_last_stw); + +guint64 mono_time_since_last_stw () +{ + if (end_of_last_stw == 0) + return 0; + + TV_DECLARE (current_time); + TV_GETTIME (current_time); + return TV_ELAPSED (end_of_last_stw, current_time); +} + +unsigned int sgen_global_stop_count = 0; + +inline static void* +align_pointer (void *ptr) +{ + mword p = (mword)ptr; + p += sizeof (gpointer) - 1; + p &= ~ (sizeof (gpointer) - 1); + return (void*)p; +} + +static void +update_current_thread_stack (void *start) +{ + int stack_guard = 0; + SgenThreadInfo *info = mono_thread_info_current (); + + info->client_info.stack_start = align_pointer (&stack_guard); + g_assert (info->client_info.stack_start); + g_assert (info->client_info.stack_start >= info->client_info.info.stack_start_limit && info->client_info.stack_start < info->client_info.info.stack_end); + +#if !defined(MONO_CROSS_COMPILE) && MONO_ARCH_HAS_MONO_CONTEXT + MONO_CONTEXT_GET_CURRENT (info->client_info.ctx); +#elif defined (HOST_WASM) + //nothing +#else + g_error ("Sgen STW requires a working mono-context"); +#endif + + if (mono_gc_get_gc_callbacks ()->thread_suspend_func) + mono_gc_get_gc_callbacks ()->thread_suspend_func (info->client_info.runtime_data, NULL, &info->client_info.ctx); +} + +static void +acquire_gc_locks (void) +{ + LOCK_INTERRUPTION; + mono_thread_info_suspend_lock (); +} + +static void +release_gc_locks (void) +{ + mono_thread_info_suspend_unlock (); + UNLOCK_INTERRUPTION; +} + +static TV_DECLARE (stop_world_time); +static unsigned long max_pause_usec = 0; + +static guint64 time_stop_world; +static guint64 time_restart_world; + +/* LOCKING: assumes the GC lock is held */ +void +sgen_client_stop_world (int generation) +{ + TV_DECLARE (end_handshake); + + MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_PRE_STOP_WORLD, generation)); + + acquire_gc_locks (); + + MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED, generation)); + + /* We start to scan after locks are taking, this ensures we won't be interrupted. */ + sgen_process_togglerefs (); + + update_current_thread_stack (&generation); + + sgen_global_stop_count++; + SGEN_LOG (3, "stopping world n %d from %p %p", sgen_global_stop_count, mono_thread_info_current (), (gpointer) (gsize) mono_native_thread_id_get ()); + TV_GETTIME (stop_world_time); + + sgen_unified_suspend_stop_world (); + + SGEN_LOG (3, "world stopped"); + + MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_POST_STOP_WORLD, generation)); + + TV_GETTIME (end_handshake); + time_stop_world += TV_ELAPSED (stop_world_time, end_handshake); + + sgen_memgov_collection_start (generation); + if (sgen_need_bridge_processing ()) + sgen_bridge_reset_data (); +} + +/* LOCKING: assumes the GC lock is held */ +void +sgen_client_restart_world (int generation, gint64 *stw_time) +{ + TV_DECLARE (end_sw); + TV_DECLARE (start_handshake); + unsigned long usec; + + /* notify the profiler of the leftovers */ + /* FIXME this is the wrong spot at we can STW for non collection reasons. */ + if (MONO_PROFILER_ENABLED (gc_moves)) + mono_sgen_gc_event_moves (); + + MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_PRE_START_WORLD, generation)); + + FOREACH_THREAD (info) { + info->client_info.stack_start = NULL; + memset (&info->client_info.ctx, 0, sizeof (MonoContext)); + } FOREACH_THREAD_END + + TV_GETTIME (start_handshake); + + sgen_unified_suspend_restart_world (); + + TV_GETTIME (end_sw); + time_restart_world += TV_ELAPSED (start_handshake, end_sw); + usec = TV_ELAPSED (stop_world_time, end_sw); + max_pause_usec = MAX (usec, max_pause_usec); + end_of_last_stw = end_sw; + + SGEN_LOG (2, "restarted (pause time: %d usec, max: %d)", (int)usec, (int)max_pause_usec); + + MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_POST_START_WORLD, generation)); + + /* + * We must release the thread info suspend lock after doing + * the thread handshake. Otherwise, if the GC stops the world + * and a thread is in the process of starting up, but has not + * yet registered (it's not in the thread_list), it is + * possible that the thread does register while the world is + * stopped. When restarting the GC will then try to restart + * said thread, but since it never got the suspend signal, it + * cannot answer the restart signal, so a deadlock results. + */ + release_gc_locks (); + + MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_POST_START_WORLD_UNLOCKED, generation)); + + *stw_time = usec; +} + +void +mono_sgen_init_stw (void) +{ + mono_counters_register ("World stop", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_stop_world); + mono_counters_register ("World restart", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_restart_world); +} + +/* Unified suspend code */ + +static gboolean +sgen_is_thread_in_current_stw (SgenThreadInfo *info, int *reason) +{ + /* + A thread explicitly asked to be skiped because it holds no managed state. + This is used by TP and finalizer threads. + FIXME Use an atomic variable for this to avoid everyone taking the GC LOCK. + */ + if (info->client_info.gc_disabled) { + if (reason) + *reason = 1; + return FALSE; + } + + /* + We have detected that this thread is failing/dying, ignore it. + FIXME: can't we merge this with thread_is_dying? + */ + if (info->client_info.skip) { + if (reason) + *reason = 2; + return FALSE; + } + + /* + Suspending the current thread will deadlock us, bad idea. + */ + if (info == mono_thread_info_current ()) { + if (reason) + *reason = 3; + return FALSE; + } + + /* + We can't suspend the workers that will do all the heavy lifting. + FIXME Use some state bit in SgenThreadInfo for this. + */ + if (sgen_thread_pool_is_thread_pool_thread (mono_thread_info_get_tid (info))) { + if (reason) + *reason = 4; + return FALSE; + } + + /* + The thread has signaled that it started to detach, ignore it. + FIXME: can't we merge this with skip + */ + if (!mono_thread_info_is_live (info)) { + if (reason) + *reason = 5; + return FALSE; + } + + return TRUE; +} + +static void +sgen_unified_suspend_stop_world (void) +{ + int sleep_duration = -1; + + mono_threads_begin_global_suspend (); + THREADS_STW_DEBUG ("[GC-STW-BEGIN][%p] *** BEGIN SUSPEND *** \n", mono_thread_info_get_tid (mono_thread_info_current ())); + + FOREACH_THREAD (info) { + info->client_info.skip = FALSE; + info->client_info.suspend_done = FALSE; + + int reason; + if (!sgen_is_thread_in_current_stw (info, &reason)) { + THREADS_STW_DEBUG ("[GC-STW-BEGIN-SUSPEND] IGNORE thread %p skip %s reason %d\n", mono_thread_info_get_tid (info), info->client_info.skip ? "true" : "false", reason); + continue; + } + + info->client_info.skip = !mono_thread_info_begin_suspend (info); + + THREADS_STW_DEBUG ("[GC-STW-BEGIN-SUSPEND] SUSPEND thread %p skip %s\n", mono_thread_info_get_tid (info), info->client_info.skip ? "true" : "false"); + } FOREACH_THREAD_END + + mono_thread_info_current ()->client_info.suspend_done = TRUE; + mono_threads_wait_pending_operations (); + + for (;;) { + gint restart_counter = 0; + + FOREACH_THREAD (info) { + gint suspend_count; + + int reason = 0; + if (info->client_info.suspend_done || !sgen_is_thread_in_current_stw (info, &reason)) { + THREADS_STW_DEBUG ("[GC-STW-RESTART] IGNORE RESUME thread %p not been processed done %d current %d reason %d\n", mono_thread_info_get_tid (info), info->client_info.suspend_done, !sgen_is_thread_in_current_stw (info, NULL), reason); + continue; + } + + /* + All threads that reach here are pristine suspended. This means the following: + + - We haven't accepted the previous suspend as good. + - We haven't gave up on it for this STW (it's either bad or asked not to) + */ + if (!mono_thread_info_in_critical_location (info)) { + info->client_info.suspend_done = TRUE; + + THREADS_STW_DEBUG ("[GC-STW-RESTART] DONE thread %p deemed fully suspended\n", mono_thread_info_get_tid (info)); + continue; + } + + suspend_count = mono_thread_info_suspend_count (info); + if (!(suspend_count == 1)) + g_error ("[%p] suspend_count = %d, but should be 1", mono_thread_info_get_tid (info), suspend_count); + + info->client_info.skip = !mono_thread_info_begin_resume (info); + if (!info->client_info.skip) + restart_counter += 1; + + THREADS_STW_DEBUG ("[GC-STW-RESTART] RESTART thread %p skip %s\n", mono_thread_info_get_tid (info), info->client_info.skip ? "true" : "false"); + } FOREACH_THREAD_END + + mono_threads_wait_pending_operations (); + + if (restart_counter == 0) + break; + + if (sleep_duration < 0) { + mono_thread_info_yield (); + sleep_duration = 0; + } else { + g_usleep (sleep_duration); + sleep_duration += 10; + } + + FOREACH_THREAD (info) { + int reason = 0; + if (info->client_info.suspend_done || !sgen_is_thread_in_current_stw (info, &reason)) { + THREADS_STW_DEBUG ("[GC-STW-RESTART] IGNORE SUSPEND thread %p not been processed done %d current %d reason %d\n", mono_thread_info_get_tid (info), info->client_info.suspend_done, !sgen_is_thread_in_current_stw (info, NULL), reason); + continue; + } + + if (!mono_thread_info_is_running (info)) { + THREADS_STW_DEBUG ("[GC-STW-RESTART] IGNORE SUSPEND thread %p not running\n", mono_thread_info_get_tid (info)); + continue; + } + + info->client_info.skip = !mono_thread_info_begin_suspend (info); + + THREADS_STW_DEBUG ("[GC-STW-RESTART] SUSPEND thread %p skip %s\n", mono_thread_info_get_tid (info), info->client_info.skip ? "true" : "false"); + } FOREACH_THREAD_END + + mono_threads_wait_pending_operations (); + } + + FOREACH_THREAD (info) { + gpointer stopped_ip; + + int reason = 0; + if (!sgen_is_thread_in_current_stw (info, &reason)) { + g_assert (!info->client_info.suspend_done || info == mono_thread_info_current ()); + + THREADS_STW_DEBUG ("[GC-STW-SUSPEND-END] thread %p is NOT suspended, reason %d\n", mono_thread_info_get_tid (info), reason); + continue; + } + + g_assert (info->client_info.suspend_done); + + info->client_info.ctx = mono_thread_info_get_suspend_state (info)->ctx; + + /* Once we remove the old suspend code, we should move sgen to directly access the state in MonoThread */ + info->client_info.stack_start = (gpointer) ((char*)MONO_CONTEXT_GET_SP (&info->client_info.ctx) - REDZONE_SIZE); + + if (info->client_info.stack_start < info->client_info.info.stack_start_limit + || info->client_info.stack_start >= info->client_info.info.stack_end) { + /* + * Thread context is in unhandled state, most likely because it is + * dying. We don't scan it. + * FIXME We should probably rework and check the valid flag instead. + */ + info->client_info.stack_start = NULL; + } + + stopped_ip = (gpointer) (MONO_CONTEXT_GET_IP (&info->client_info.ctx)); + + binary_protocol_thread_suspend ((gpointer) mono_thread_info_get_tid (info), stopped_ip); + + THREADS_STW_DEBUG ("[GC-STW-SUSPEND-END] thread %p is suspended, stopped_ip = %p, stack = %p -> %p\n", + mono_thread_info_get_tid (info), stopped_ip, info->client_info.stack_start, info->client_info.stack_start ? info->client_info.info.stack_end : NULL); + } FOREACH_THREAD_END +} + +static void +sgen_unified_suspend_restart_world (void) +{ + THREADS_STW_DEBUG ("[GC-STW-END] *** BEGIN RESUME ***\n"); + FOREACH_THREAD (info) { + int reason = 0; + if (sgen_is_thread_in_current_stw (info, &reason)) { + g_assert (mono_thread_info_begin_resume (info)); + THREADS_STW_DEBUG ("[GC-STW-RESUME-WORLD] RESUME thread %p\n", mono_thread_info_get_tid (info)); + + binary_protocol_thread_restart ((gpointer) mono_thread_info_get_tid (info)); + } else { + THREADS_STW_DEBUG ("[GC-STW-RESUME-WORLD] IGNORE thread %p, reason %d\n", mono_thread_info_get_tid (info), reason); + } + } FOREACH_THREAD_END + + mono_threads_wait_pending_operations (); + mono_threads_end_global_suspend (); +} +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/sgen-tarjan-bridge.c b/unity-2019.4.24f1-mbe/mono/metadata/sgen-tarjan-bridge.c new file mode 100644 index 000000000..77ea9f8a7 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/sgen-tarjan-bridge.c @@ -0,0 +1,1237 @@ +/** + * \file + * Tarjan-based bridge implementation. + * + * Copyright 2011 Novell, Inc (http://www.novell.com) + * Copyright 2014 Xamarin Inc (http://www.xamarin.com) + * + * + * Copyright 2001-2003 Ximian, Inc + * Copyright 2003-2010 Novell, Inc. + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include "config.h" + +#ifdef HAVE_SGEN_GC + +#include + +#include "sgen/sgen-gc.h" +#include "sgen-bridge-internals.h" +#include "tabledefs.h" +#include "utils/mono-logger-internals.h" + +#include "sgen-dynarray.h" + +/* + * See comments in sgen-bridge.h + * + * This bridge implementation is based on the tarjan algorithm for strongly + * connected components. It has two elements: + * + * - Standard tarjan SCC algorithm to convert graph to SCC forest + * + * - "Colors": We reduce the SCC forest to bridged-SCCs-only by using a + * "color" algorithm devised by Kumpera. Consider the set of bridged SCCs + * which is reachable from a given object. We call each such unique set a + * "color". We compute the set of colors and which colors contain links to + * which colors. The color graph then becomes the reduced SCC graph. + */ + +// Is this class bridged or not, and should its dependencies be scanned or not? +// The result of this callback will be cached for use by is_opaque_object later. +static MonoGCBridgeObjectKind +class_kind (MonoClass *klass) +{ + MonoGCBridgeObjectKind res = bridge_callbacks.bridge_class_kind (klass); + + /* If it's a bridge, nothing we can do about it. */ + if (res == GC_BRIDGE_TRANSPARENT_BRIDGE_CLASS || res == GC_BRIDGE_OPAQUE_BRIDGE_CLASS) + return res; + + /* Non bridge classes with no pointers will never point to a bridge, so we can savely ignore them. */ + if (!klass->has_references) { + SGEN_LOG (6, "class %s is opaque\n", klass->name); + return GC_BRIDGE_OPAQUE_CLASS; + } + + /* Some arrays can be ignored */ + if (klass->rank == 1) { + MonoClass *elem_class = klass->element_class; + + /* FIXME the bridge check can be quite expensive, cache it at the class level. */ + /* An array of a sealed type that is not a bridge will never get to a bridge */ + if ((mono_class_get_flags (elem_class) & TYPE_ATTRIBUTE_SEALED) && !elem_class->has_references && !bridge_callbacks.bridge_class_kind (elem_class)) { + SGEN_LOG (6, "class %s is opaque\n", klass->name); + return GC_BRIDGE_OPAQUE_CLASS; + } + } + + return GC_BRIDGE_TRANSPARENT_CLASS; +} + +//enable usage logging +// #define DUMP_GRAPH 1 + +/* Used in bridgeless_color_is_heavy: + * The idea here is as long as the reference fanin and fanout on a node are both 2 or greater, then + * removing that node will result in a net increase in edge count. So the question is which node + * removals are counterproductive (i.e., how many edges saved balances out one node added). + * The number of edges saved by preserving a node is (fanin*fanout - fanin - fanout). + * + * With all that in mind: + * + * - HEAVY_REFS_MIN is the number that *both* fanin and fanout must meet to preserve the node. + * - HEAVY_COMBINED_REFS_MIN is the number (fanin*fanout) must meet to preserve the node. + * + * Note HEAVY_COMBINED_REFS_MIN must be <= 2*INCOMING_COLORS_MAX, or we won't know the true fanin. + */ + +#define HEAVY_REFS_MIN 2 +#define HEAVY_COMBINED_REFS_MIN 60 + +/* Used in ColorData: + * The higher INCOMING_COLORS_BITS is the higher HEAVY_COMBINED_REFS_MIN can be (see above). + * However, API_INDEX_BITS + INCOMING_COLORS_BITS must be equal to 31, and if API_INDEX_BITS is too + * low then terrible things will happen if too many colors are generated. (The number of colors we + * will ever attempt to generate is currently naturally limited by the JNI GREF limit.) + */ + +#define API_INDEX_BITS 26 +#define INCOMING_COLORS_BITS 5 + +#define API_INDEX_MAX ((1<incoming_colors; + int fanout = dyn_array_ptr_size (&data->other_colors); + return fanin > HEAVY_REFS_MIN && fanout > HEAVY_REFS_MIN + && fanin*fanout >= HEAVY_COMBINED_REFS_MIN; +} + +// Should color be made visible to client? +static inline gboolean +color_visible_to_client (ColorData *data) { + return dyn_array_ptr_size (&data->bridges) || bridgeless_color_is_heavy (data); +} + +// Stacks of ScanData objects used for tarjan algorithm. +// The Tarjan algorithm is normally defined recursively; here scan_stack simulates the call stack of a recursive algorithm, +// and loop_stack is the stack structure used by the algorithm itself. +static DynPtrArray scan_stack, loop_stack; + +// GCObjects on which register_finalized_object has been called +static DynPtrArray registered_bridges; + +// As we traverse the graph, which ColorData objects are accessible from our current position? +static DynPtrArray color_merge_array; +// Running hash of the contents of the color_merge_array. +static unsigned int color_merge_array_hash; + +static void color_merge_array_empty (void) +{ + dyn_array_ptr_empty (&color_merge_array); + color_merge_array_hash = 0; +} + +static int ignored_objects; +static int object_index; +static int num_colors_with_bridges; +static int num_sccs; +static int xref_count; + +static size_t setup_time, tarjan_time, scc_setup_time, gather_xref_time, xref_setup_time, cleanup_time; +static SgenBridgeProcessor *bridge_processor; + +#define BUCKET_SIZE 8184 + +//ScanData buckets +#define NUM_SCAN_ENTRIES ((BUCKET_SIZE - SIZEOF_VOID_P * 2) / sizeof (ScanData)) + +typedef struct _ObjectBucket ObjectBucket; +struct _ObjectBucket { + ObjectBucket *next; + ScanData *next_data; + ScanData data [NUM_SCAN_ENTRIES]; +}; + +static ObjectBucket *root_object_bucket; +static ObjectBucket *cur_object_bucket; +static int object_data_count; + +// Arenas to allocate ScanData from +static ObjectBucket* +new_object_bucket (void) +{ + ObjectBucket *res = (ObjectBucket *)sgen_alloc_internal (INTERNAL_MEM_TARJAN_OBJ_BUCKET); + res->next_data = &res->data [0]; + return res; +} + +static void +object_alloc_init (void) +{ + root_object_bucket = cur_object_bucket = new_object_bucket (); +} + +static ScanData* +alloc_object_data (void) +{ + ScanData *res; +retry: + + /* next_data points to the first free entry */ + res = cur_object_bucket->next_data; + if (res >= &cur_object_bucket->data [NUM_SCAN_ENTRIES]) { + ObjectBucket *b = new_object_bucket (); + cur_object_bucket->next = b; + cur_object_bucket = b; + goto retry; + } + cur_object_bucket->next_data = res + 1; + object_data_count++; + return res; +} + +static void +free_object_buckets (void) +{ + ObjectBucket *cur = root_object_bucket; + + object_data_count = 0; + + while (cur) { + ObjectBucket *tmp = cur->next; + sgen_free_internal (cur, INTERNAL_MEM_TARJAN_OBJ_BUCKET); + cur = tmp; + } + + root_object_bucket = cur_object_bucket = NULL; +} + +//ColorData buckets +#define NUM_COLOR_ENTRIES ((BUCKET_SIZE - SIZEOF_VOID_P * 2) / sizeof (ColorData)) + +// Arenas for ColorDatas, same as ObjectBucket except items-per-bucket differs +typedef struct _ColorBucket ColorBucket; +struct _ColorBucket { + ColorBucket *next; + ColorData *next_data; + ColorData data [NUM_COLOR_ENTRIES]; +}; + +static ColorBucket *root_color_bucket; +static ColorBucket *cur_color_bucket; +static int color_data_count; + + +static ColorBucket* +new_color_bucket (void) +{ + ColorBucket *res = (ColorBucket *)sgen_alloc_internal (INTERNAL_MEM_TARJAN_OBJ_BUCKET); + res->next_data = &res->data [0]; + return res; +} + +static void +color_alloc_init (void) +{ + root_color_bucket = cur_color_bucket = new_color_bucket (); +} + +static ColorData* +alloc_color_data (void) +{ + ColorData *res; +retry: + + /* next_data points to the first free entry */ + res = cur_color_bucket->next_data; + if (res >= &cur_color_bucket->data [NUM_COLOR_ENTRIES]) { + ColorBucket *b = new_color_bucket (); + cur_color_bucket->next = b; + cur_color_bucket = b; + goto retry; + } + cur_color_bucket->next_data = res + 1; + color_data_count++; + return res; +} + +static void +free_color_buckets (void) +{ + ColorBucket *cur, *tmp; + + color_data_count = 0; + + for (cur = root_color_bucket; cur; cur = tmp) { + ColorData *cd; + for (cd = &cur->data [0]; cd < cur->next_data; ++cd) { + dyn_array_ptr_uninit (&cd->other_colors); + dyn_array_ptr_uninit (&cd->bridges); + } + tmp = cur->next; + sgen_free_internal (cur, INTERNAL_MEM_TARJAN_OBJ_BUCKET); + } + root_color_bucket = cur_color_bucket = NULL; +} + + +static ScanData* +create_data (GCObject *obj) +{ + mword *o = (mword*)obj; + ScanData *res = alloc_object_data (); + res->obj = obj; + res->color = NULL; + res->index = res->low_index = -1; + res->state = INITIAL; + res->is_bridge = FALSE; + res->obj_state = o [0] & SGEN_VTABLE_BITS_MASK; + res->lock_word = o [1]; + + o [0] |= SGEN_VTABLE_BITS_MASK; + o [1] = (mword)res; + return res; +} + +static ScanData* +find_data (GCObject *obj) +{ + ScanData *a = NULL; + mword *o = (mword*)obj; + if ((o [0] & SGEN_VTABLE_BITS_MASK) == SGEN_VTABLE_BITS_MASK) + a = (ScanData*)o [1]; + return a; +} + +static void +clear_after_processing (void) +{ + ObjectBucket *cur; + + for (cur = root_object_bucket; cur; cur = cur->next) { + ScanData *sd; + for (sd = &cur->data [0]; sd < cur->next_data; ++sd) { + mword *o = (mword*)sd->obj; + o [0] &= ~SGEN_VTABLE_BITS_MASK; + o [0] |= sd->obj_state; + o [1] = sd->lock_word; + } + } +} + +static GCObject* +bridge_object_forward (GCObject *obj) +{ + GCObject *fwd; + mword *o = (mword*)obj; + if ((o [0] & SGEN_VTABLE_BITS_MASK) == SGEN_VTABLE_BITS_MASK) + return obj; + + fwd = SGEN_OBJECT_IS_FORWARDED (obj); + return fwd ? fwd : obj; +} + +#ifdef DUMP_GRAPH +static const char* +safe_name_bridge (GCObject *obj) +{ + GCVTable vt = SGEN_LOAD_VTABLE (obj); + return vt->klass->name; +} + +static ScanData* +find_or_create_data (GCObject *obj) +{ + ScanData *entry = find_data (obj); + if (!entry) + entry = create_data (obj); + return entry; +} +#endif + +//---------- +typedef struct { + ColorData *color; + unsigned int hash; +} HashEntry; + +/* +The merge cache maps an ordered list of ColorDatas [the color_merge_array] to a single ColorData. + +About cache bucket tuning: We tried 2/32, 2/128, 4/32, 4/128, 6/128 and 8/128. + +The performance cost between 4/128 and 8/128 is so small since cache movement happens completely in the same cacheline, +making the extra space pretty much free. + +The cost between 32 and 128 itens is minimal too, it's mostly a fixed, setup cost. + +Memory wise, 4/32 takes 512 and 8/128 takes 8k, so it's quite reasonable. +*/ + +#define ELEMENTS_PER_BUCKET 8 +#define COLOR_CACHE_SIZE 128 +static HashEntry merge_cache [COLOR_CACHE_SIZE][ELEMENTS_PER_BUCKET]; +static unsigned int hash_perturb; + +// If true, disable an optimization where sometimes SCC nodes are merged without a perfect check +static gboolean scc_precise_merge; + +static unsigned int +mix_hash (uintptr_t source) +{ + unsigned int hash = source; + + // The full hash determines whether two colors can be merged-- sometimes exclusively. + // This value changes every GC, so XORing it in before performing the hash will make the + // chance that two different colors will produce the same hash on successive GCs very low. + hash = hash ^ hash_perturb; + + // Actual hash + hash = (((hash * 215497) >> 16) ^ ((hash * 1823231) + hash)); + + // Mix in highest bits on 64-bit systems only + if (sizeof (source) > 4) + hash = hash ^ (source >> 32); + + return hash; +} + +static void +reset_cache (void) +{ + memset (merge_cache, 0, sizeof (merge_cache)); + + // When using the precise merge debug option, we do not want the inconsistency caused by hash_perturb. + if (!scc_precise_merge) + ++hash_perturb; +} + + +static gboolean +dyn_array_ptr_contains (DynPtrArray *da, void *x) +{ + int i; + for (i = 0; i < dyn_array_ptr_size (da); ++i) + if (dyn_array_ptr_get (da, i) == x) + return TRUE; + return FALSE; +} + +static gboolean +match_colors_estimate (DynPtrArray *a, DynPtrArray *b) +{ + return dyn_array_ptr_size (a) == dyn_array_ptr_size (b); +} + + +static gboolean +match_colors (DynPtrArray *a, DynPtrArray *b) +{ + int i; + if (dyn_array_ptr_size (a) != dyn_array_ptr_size (b)) + return FALSE; + + for (i = 0; i < dyn_array_ptr_size (a); ++i) { + if (!dyn_array_ptr_contains (b, dyn_array_ptr_get (a, i))) + return FALSE; + } + return TRUE; +} + +// If scc_precise_merge, "semihits" refers to find_in_cache calls aborted because the merge array was too large. +// Otherwise "semihits" refers to cache hits where the match was only estimated. +static int cache_hits, cache_semihits, cache_misses; + +// The cache contains only non-bridged colors. +static ColorData* +find_in_cache (int *insert_index) +{ + HashEntry *bucket; + int i, size, index; + + size = dyn_array_ptr_size (&color_merge_array); + + /* Color equality checking is very expensive with a lot of elements, so when there are many + * elements we switch to a cheap comparison method which allows false positives. When false + * positives occur the worst that can happen is two items will be inappropriately merged + * and memory will be retained longer than it should be. We assume that will correct itself + * on the next GC (the hash_perturb mechanism increases the probability of this). + * + * Because this has *some* potential to create problems, if the user set the debug option + * 'enable-tarjan-precise-merge' we bail out early (and never merge SCCs with >3 colors). + */ + gboolean color_merge_array_large = size > 3; + if (scc_precise_merge && color_merge_array_large) { + ++cache_semihits; + return NULL; + } + + unsigned int hash = color_merge_array_hash; + if (!hash) // 0 is used to indicate an empty bucket entry + hash = 1; + + index = hash & (COLOR_CACHE_SIZE - 1); + bucket = merge_cache [index]; + for (i = 0; i < ELEMENTS_PER_BUCKET; ++i) { + if (bucket [i].hash != hash) + continue; + + if (color_merge_array_large) { + if (match_colors_estimate (&bucket [i].color->other_colors, &color_merge_array)) { + ++cache_semihits; + return bucket [i].color; + } + } else { + if (match_colors (&bucket [i].color->other_colors, &color_merge_array)) { + ++cache_hits; + return bucket [i].color; + } + } + } + + //move elements to the back + for (i = ELEMENTS_PER_BUCKET - 1; i > 0; --i) + bucket [i] = bucket [i - 1]; + ++cache_misses; + *insert_index = index; + bucket [0].hash = hash; + return NULL; +} + +// A color is needed for an SCC. If the SCC has bridges, the color MUST be newly allocated. +// If the SCC lacks bridges, the allocator MAY use the cache to merge it with an existing one. +static ColorData* +new_color (gboolean has_bridges) +{ + int cacheSlot = -1; + ColorData *cd; + /* XXX Try to find an equal one and return it */ + if (!has_bridges) { + cd = find_in_cache (&cacheSlot); + if (cd) + return cd; + } + + cd = alloc_color_data (); + cd->api_index = -1; + dyn_array_ptr_set_all (&cd->other_colors, &color_merge_array); + + // Inform targets + for (int i = 0; i < dyn_array_ptr_size (&color_merge_array); ++i) { + ColorData *points_to = (ColorData *)dyn_array_ptr_get (&color_merge_array, i); + points_to->incoming_colors = MIN (points_to->incoming_colors + 1, INCOMING_COLORS_MAX); + } + + /* if cacheSlot >= 0, it means we prepared a given slot to receive the new color */ + if (cacheSlot >= 0) + merge_cache [cacheSlot][0].color = cd; + + return cd; +} + + +static void +register_bridge_object (GCObject *obj) +{ + create_data (obj)->is_bridge = TRUE; +} + +static gboolean +is_opaque_object (GCObject *obj) +{ + MonoVTable *vt = SGEN_LOAD_VTABLE (obj); + if ((vt->gc_bits & SGEN_GC_BIT_BRIDGE_OPAQUE_OBJECT) == SGEN_GC_BIT_BRIDGE_OPAQUE_OBJECT) { + SGEN_LOG (6, "ignoring %s\n", vt->klass->name); + ++ignored_objects; + return TRUE; + } + return FALSE; +} + +// Called during DFS; visits one child. If it is a candidate to be scanned, pushes it to the stacks. +static void +push_object (GCObject *obj) +{ + ScanData *data; + obj = bridge_object_forward (obj); + +#if DUMP_GRAPH + printf ("\t= pushing %p %s -> ", obj, safe_name_bridge (obj)); +#endif + + /* Object types we can ignore */ + if (is_opaque_object (obj)) { +#if DUMP_GRAPH + printf ("opaque\n"); +#endif + return; + } + + data = find_data (obj); + + /* Already marked - XXX must be done this way as the bridge themselves are alive. */ + if (data && data->state != INITIAL) { +#if DUMP_GRAPH + printf ("already marked\n"); +#endif + return; + } + + /* We only care about dead objects */ + if (!data && sgen_object_is_live (obj)) { +#if DUMP_GRAPH + printf ("alive\n"); +#endif + return; + } + +#if DUMP_GRAPH + printf ("pushed!\n"); +#endif + + if (!data) + data = create_data (obj); + g_assert (data->state == INITIAL); + g_assert (data->index == -1); + dyn_array_ptr_push (&scan_stack, data); +} + +#undef HANDLE_PTR +#define HANDLE_PTR(ptr,obj) do { \ + GCObject *dst = (GCObject*)*(ptr); \ + if (dst) push_object (dst); \ + } while (0) + +// dfs () function's queue-children-of-object operation. +static void +push_all (ScanData *data) +{ + GCObject *obj = data->obj; + char *start = (char*)obj; + mword desc = sgen_obj_get_descriptor_safe (obj); + +#if DUMP_GRAPH + printf ("+scanning %s (%p) index %d color %p\n", safe_name_bridge (data->obj), data->obj, data->index, data->color); +#endif + + #include "sgen/sgen-scan-object.h" +} + + +static void +compute_low_index (ScanData *data, GCObject *obj) +{ + ScanData *other; + ColorData *cd; + + obj = bridge_object_forward (obj); + other = find_data (obj); + +#if DUMP_GRAPH + printf ("\tcompute low %p ->%p (%s) %p (%d / %d)\n", data->obj, obj, safe_name_bridge (obj), other, other ? other->index : -2, other ? other->low_index : -2); +#endif + if (!other) + return; + + g_assert (other->state != INITIAL); + + if ((other->state == SCANNED || other->state == FINISHED_ON_STACK) && data->low_index > other->low_index) + data->low_index = other->low_index; + + /* Compute the low color */ + if (other->color == NULL) + return; + + cd = other->color; + if (!cd->visited) { + color_merge_array_hash += mix_hash ((uintptr_t) other->color); + dyn_array_ptr_add (&color_merge_array, other->color); + cd->visited = TRUE; + } +} + +#undef HANDLE_PTR +#define HANDLE_PTR(ptr,obj) do { \ + GCObject *dst = (GCObject*)*(ptr); \ + if (dst) compute_low_index (data, dst); \ + } while (0) + +static void +compute_low (ScanData *data) +{ + GCObject *obj = data->obj; + char *start = (char*)obj; + mword desc = sgen_obj_get_descriptor_safe (obj); + + #include "sgen/sgen-scan-object.h" +} + +// A non-bridged object needs a single color describing the current merge array. +static ColorData* +reduce_color (void) +{ + ColorData *color = NULL; + int size = dyn_array_ptr_size (&color_merge_array); + + // Merge array is empty-- this SCC points to no bridged colors. + // This SCC can be ignored completely. + if (size == 0) + color = NULL; + + // Merge array has one item-- this SCC points to a single bridged color. + // This SCC can be forwarded to the pointed-to color. + else if (size == 1) { + color = (ColorData *)dyn_array_ptr_get (&color_merge_array, 0); + + // This SCC gets to talk to the color allocator. + } else + color = new_color (FALSE); + + return color; +} + +static void +create_scc (ScanData *data) +{ + int i; + gboolean found = FALSE; + gboolean found_bridge = FALSE; + ColorData *color_data = NULL; + + for (i = dyn_array_ptr_size (&loop_stack) - 1; i >= 0; --i) { + ScanData *other = (ScanData *)dyn_array_ptr_get (&loop_stack, i); + found_bridge |= other->is_bridge; + if (found_bridge || other == data) + break; + } + +#if DUMP_GRAPH + printf ("|SCC rooted in %s (%p) has bridge %d\n", safe_name_bridge (data->obj), data->obj, found_bridge); + printf ("\tpoints-to-colors: "); + for (int i = 0; i < dyn_array_ptr_size (&color_merge_array); ++i) + printf ("%p ", dyn_array_ptr_get (&color_merge_array, i)); + printf ("\n"); + + printf ("loop stack: "); + for (int i = 0; i < dyn_array_ptr_size (&loop_stack); ++i) { + ScanData *other = dyn_array_ptr_get (&loop_stack, i); + printf ("(%d/%d)", other->index, other->low_index); + } + printf ("\n"); +#endif + + if (found_bridge) { + color_data = new_color (TRUE); + ++num_colors_with_bridges; + } else { + color_data = reduce_color (); + } + + while (dyn_array_ptr_size (&loop_stack) > 0) { + ScanData *other = (ScanData *)dyn_array_ptr_pop (&loop_stack); + +#if DUMP_GRAPH + printf ("\tmember %s (%p) index %d low-index %d color %p state %d\n", safe_name_bridge (other->obj), other->obj, other->index, other->low_index, other->color, other->state); +#endif + + other->color = color_data; + switch (other->state) { + case FINISHED_ON_STACK: + other->state = FINISHED_OFF_STACK; + break; + case FINISHED_OFF_STACK: + break; + default: + g_error ("Invalid state when building SCC %d", other->state); + } + + if (other->is_bridge) + dyn_array_ptr_add (&color_data->bridges, other->obj); + + if (other == data) { + found = TRUE; + break; + } + } + g_assert (found); + + for (i = 0; i < dyn_array_ptr_size (&color_merge_array); ++i) { + ColorData *cd = (ColorData *)dyn_array_ptr_get (&color_merge_array, i); + g_assert (cd->visited); + cd->visited = FALSE; + } + color_merge_array_empty (); + found_bridge = FALSE; +} + +static void +dfs (void) +{ + g_assert (dyn_array_ptr_size (&scan_stack) == 1); + g_assert (dyn_array_ptr_size (&loop_stack) == 0); + + color_merge_array_empty (); + + while (dyn_array_ptr_size (&scan_stack) > 0) { + ScanData *data = (ScanData *)dyn_array_ptr_pop (&scan_stack); + + /** + * Ignore finished objects on stack, they happen due to loops. For example: + * A -> C + * A -> B + * B -> C + * C -> A + * + * We start scanning from A and push C before B. So, after the first iteration, the scan stack will have: A C B. + * We then visit B, which will find C in its initial state and push again. + * Finally after finish with C and B, the stack will be left with "A C" and at this point C should be ignored. + * + * The above explains FINISHED_ON_STACK, to explain FINISHED_OFF_STACK, consider if the root was D, which pointed + * to A and C. A is processed first, leaving C on stack after that in the mentioned state. + */ + if (data->state == FINISHED_ON_STACK || data->state == FINISHED_OFF_STACK) + continue; + + if (data->state == INITIAL) { + g_assert (data->index == -1); + g_assert (data->low_index == -1); + + data->state = SCANNED; + data->low_index = data->index = object_index++; + dyn_array_ptr_push (&scan_stack, data); + dyn_array_ptr_push (&loop_stack, data); + +#if DUMP_GRAPH + printf ("+scanning %s (%p) index %d color %p\n", safe_name_bridge (data->obj), data->obj, data->index, data->color); +#endif + /*push all refs */ + push_all (data); + } else { + g_assert (data->state == SCANNED); + data->state = FINISHED_ON_STACK; + +#if DUMP_GRAPH + printf ("-finishing %s (%p) index %d low-index %d color %p\n", safe_name_bridge (data->obj), data->obj, data->index, data->low_index, data->color); +#endif + + /* Compute low index */ + compute_low (data); + +#if DUMP_GRAPH + printf ("-finished %s (%p) index %d low-index %d color %p\n", safe_name_bridge (data->obj), data->obj, data->index, data->low_index, data->color); +#endif + //SCC root + if (data->index == data->low_index) + create_scc (data); + } + } +} + +static void +register_finalized_object (GCObject *obj) +{ + g_assert (sgen_need_bridge_processing ()); + dyn_array_ptr_push (®istered_bridges, obj); +} + +static void +reset_data (void) +{ + dyn_array_ptr_empty (®istered_bridges); +} + +static void +cleanup (void) +{ + dyn_array_ptr_empty (&scan_stack); + dyn_array_ptr_empty (&loop_stack); + dyn_array_ptr_empty (®istered_bridges); + free_object_buckets (); + free_color_buckets (); + reset_cache (); + object_index = 0; + num_colors_with_bridges = 0; +} + +#ifdef DUMP_GRAPH +static void +dump_color_table (const char *why, gboolean do_index) +{ + ColorBucket *cur; + int i = 0, j; + printf ("colors%s:\n", why); + + for (cur = root_color_bucket; cur; cur = cur->next, ++i) { + ColorData *cd; + for (cd = &cur->data [0]; cd < cur->next_data; ++cd) { + if (do_index) + printf ("\t%d(%d):", i, cd->api_index); + else + printf ("\t%d: ", i); + for (j = 0; j < dyn_array_ptr_size (&cd->other_colors); ++j) { + printf ("%p ", dyn_array_ptr_get (&cd->other_colors, j)); + } + if (dyn_array_ptr_size (&cd->bridges)) { + printf (" bridges: "); + for (j = 0; j < dyn_array_ptr_size (&cd->bridges); ++j) { + GCObject *obj = dyn_array_ptr_get (&cd->bridges, j); + ScanData *data = find_or_create_data (obj); + printf ("%d ", data->index); + } + } + printf ("\n"); + } + } + +} +#endif + +static gint64 +step_timer (gint64 *timer) +{ + gint64 curtime, diff; + + SGEN_TV_GETTIME (curtime); + diff = SGEN_TV_ELAPSED (*timer, curtime); + *timer = curtime; + return diff; +} +static void +processing_stw_step (void) +{ + int i; + int bridge_count; + gint64 curtime; + + if (!dyn_array_ptr_size (®istered_bridges)) + return; + +#if defined (DUMP_GRAPH) + printf ("-----------------\n"); +#endif + + SGEN_TV_GETTIME (curtime); + + object_alloc_init (); + color_alloc_init (); + + bridge_count = dyn_array_ptr_size (®istered_bridges); + for (i = 0; i < bridge_count ; ++i) + register_bridge_object ((GCObject *)dyn_array_ptr_get (®istered_bridges, i)); + + setup_time = step_timer (&curtime); + + for (i = 0; i < bridge_count; ++i) { + ScanData *sd = find_data ((GCObject *)dyn_array_ptr_get (®istered_bridges, i)); + if (sd->state == INITIAL) { + dyn_array_ptr_push (&scan_stack, sd); + dfs (); + } else { + g_assert (sd->state == FINISHED_OFF_STACK); + } + } + + tarjan_time = step_timer (&curtime); + +#if defined (DUMP_GRAPH) + printf ("----summary----\n"); + printf ("bridges:\n"); + for (int i = 0; i < bridge_count; ++i) { + ScanData *sd = find_data (dyn_array_ptr_get (®istered_bridges, i)); + printf ("\t%s (%p) index %d color %p\n", safe_name_bridge (sd->obj), sd->obj, sd->index, sd->color); + } + + dump_color_table (" after tarjan", FALSE); +#endif + + clear_after_processing (); +} + + +static void +gather_xrefs (ColorData *color) +{ + int i; + for (i = 0; i < dyn_array_ptr_size (&color->other_colors); ++i) { + ColorData *src = (ColorData *)dyn_array_ptr_get (&color->other_colors, i); + if (src->visited) + continue; + src->visited = TRUE; + if (color_visible_to_client (src)) + dyn_array_ptr_add (&color_merge_array, src); + else + gather_xrefs (src); + } +} + +static void +reset_xrefs (ColorData *color) +{ + int i; + for (i = 0; i < dyn_array_ptr_size (&color->other_colors); ++i) { + ColorData *src = (ColorData *)dyn_array_ptr_get (&color->other_colors, i); + if (!src->visited) + continue; + src->visited = FALSE; + if (!color_visible_to_client (src)) + reset_xrefs (src); + } +} + +static void +processing_build_callback_data (int generation) +{ + int j; + gint64 curtime; + ColorBucket *cur; + + g_assert (bridge_processor->num_sccs == 0 && bridge_processor->num_xrefs == 0); + g_assert (!bridge_processor->api_sccs && !bridge_processor->api_xrefs); + + if (!dyn_array_ptr_size (®istered_bridges)) + return; + + SGEN_TV_GETTIME (curtime); + + /*create API objects */ + +#if defined (DUMP_GRAPH) + printf ("***** API *****\n"); + printf ("number of SCCs %d\n", num_colors_with_bridges); +#endif + + // Count the number of SCCs visible to the client + num_sccs = 0; + for (cur = root_color_bucket; cur; cur = cur->next) { + ColorData *cd; + for (cd = &cur->data [0]; cd < cur->next_data; ++cd) { + if (color_visible_to_client (cd)) + num_sccs++; + } + } + + /* This is a straightforward translation from colors to the bridge callback format. */ + MonoGCBridgeSCC **api_sccs = (MonoGCBridgeSCC **)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeSCC*) * num_sccs, INTERNAL_MEM_BRIDGE_DATA, TRUE); + int api_index = 0; + xref_count = 0; + + // Convert visible SCCs, along with their bridged object list, to MonoGCBridgeSCCs in the client's SCC list + for (cur = root_color_bucket; cur; cur = cur->next) { + ColorData *cd; + for (cd = &cur->data [0]; cd < cur->next_data; ++cd) { + int bridges = dyn_array_ptr_size (&cd->bridges); + if (!(bridges || bridgeless_color_is_heavy (cd))) + continue; + + api_sccs [api_index] = (MonoGCBridgeSCC *)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeSCC) + sizeof (MonoObject*) * bridges, INTERNAL_MEM_BRIDGE_DATA, TRUE); + api_sccs [api_index]->is_alive = FALSE; + api_sccs [api_index]->num_objs = bridges; + + cd->api_index = api_index; + + for (j = 0; j < bridges; ++j) + api_sccs [api_index]->objs [j] = (MonoObject *)dyn_array_ptr_get (&cd->bridges, j); + + g_assert(api_index < API_INDEX_MAX); + api_index++; + } + } + + scc_setup_time = step_timer (&curtime); + + // Eliminate non-visible SCCs from the SCC list and redistribute xrefs + for (cur = root_color_bucket; cur; cur = cur->next) { + ColorData *cd; + for (cd = &cur->data [0]; cd < cur->next_data; ++cd) { + if (!color_visible_to_client (cd)) + continue; + + color_merge_array_empty (); + gather_xrefs (cd); + reset_xrefs (cd); + dyn_array_ptr_set_all (&cd->other_colors, &color_merge_array); + xref_count += dyn_array_ptr_size (&cd->other_colors); + } + } + + gather_xref_time = step_timer (&curtime); + +#if defined (DUMP_GRAPH) + printf ("TOTAL XREFS %d\n", xref_count); + dump_color_table (" after xref pass", TRUE); +#endif + + // Write out xrefs array + MonoGCBridgeXRef *api_xrefs = (MonoGCBridgeXRef *)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeXRef) * xref_count, INTERNAL_MEM_BRIDGE_DATA, TRUE); + int xref_index = 0; + for (cur = root_color_bucket; cur; cur = cur->next) { + ColorData *src; + for (src = &cur->data [0]; src < cur->next_data; ++src) { + if (!color_visible_to_client (src)) + continue; + + for (j = 0; j < dyn_array_ptr_size (&src->other_colors); ++j) { + ColorData *dest = (ColorData *)dyn_array_ptr_get (&src->other_colors, j); + g_assert (color_visible_to_client (dest)); /* Supposedly we already eliminated all xrefs to non-visible objects. */ + + api_xrefs [xref_index].src_scc_index = src->api_index; + api_xrefs [xref_index].dst_scc_index = dest->api_index; + + ++xref_index; + } + } + } + + g_assert (xref_count == xref_index); + xref_setup_time = step_timer (&curtime); + +#if defined (DUMP_GRAPH) + printf ("---xrefs:\n"); + for (int i = 0; i < xref_count; ++i) + printf ("\t%d -> %d\n", api_xrefs [i].src_scc_index, api_xrefs [i].dst_scc_index); +#endif + + //FIXME move half of the cleanup to before the bridge callback? + bridge_processor->num_sccs = num_sccs; + bridge_processor->api_sccs = api_sccs; + bridge_processor->num_xrefs = xref_count; + bridge_processor->api_xrefs = api_xrefs; +} + +static void +processing_after_callback (int generation) +{ + gint64 curtime; + int bridge_count = dyn_array_ptr_size (®istered_bridges); + int object_count = object_data_count; + int color_count = color_data_count; + int colors_with_bridges_count = num_colors_with_bridges; + + SGEN_TV_GETTIME (curtime); + + /* cleanup */ + cleanup (); + + cleanup_time = step_timer (&curtime); + + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_TAR_BRIDGE bridges %d objects %d opaque %d colors %d colors-bridged %d colors-visible %d xref %d cache-hit %d cache-%s %d cache-miss %d setup %.2fms tarjan %.2fms scc-setup %.2fms gather-xref %.2fms xref-setup %.2fms cleanup %.2fms", + bridge_count, object_count, ignored_objects, + color_count, colors_with_bridges_count, num_sccs, xref_count, + cache_hits, (scc_precise_merge ? "abstain" : "semihit"), cache_semihits, cache_misses, + setup_time / 10000.0f, + tarjan_time / 10000.0f, + scc_setup_time / 10000.0f, + gather_xref_time / 10000.0f, + xref_setup_time / 10000.0f, + cleanup_time / 10000.0f); + + cache_hits = cache_semihits = cache_misses = 0; + ignored_objects = 0; +} + +static void +describe_pointer (GCObject *obj) +{ + // HashEntry *entry; + int i; + + for (i = 0; i < dyn_array_ptr_size (®istered_bridges); ++i) { + if (obj == dyn_array_ptr_get (®istered_bridges, i)) { + printf ("Pointer is a registered bridge object.\n"); + break; + } + } + + // entry = sgen_hash_table_lookup (&hash_table, obj); + // if (!entry) + // return; + // + // printf ("Bridge hash table entry %p:\n", entry); + // printf (" is bridge: %d\n", (int)entry->is_bridge); + // printf (" is visited: %d\n", (int)entry->is_visited); +} + +static void +set_config (const SgenBridgeProcessorConfig *config) +{ + if (config->scc_precise_merge) { + hash_perturb = 0; + scc_precise_merge = TRUE; + } +} + +void +sgen_tarjan_bridge_init (SgenBridgeProcessor *collector) +{ + collector->reset_data = reset_data; + collector->processing_stw_step = processing_stw_step; + collector->processing_build_callback_data = processing_build_callback_data; + collector->processing_after_callback = processing_after_callback; + collector->class_kind = class_kind; + collector->register_finalized_object = register_finalized_object; + collector->describe_pointer = describe_pointer; + collector->set_config = set_config; + + sgen_register_fixed_internal_mem_type (INTERNAL_MEM_TARJAN_OBJ_BUCKET, BUCKET_SIZE); + g_assert (sizeof (ObjectBucket) <= BUCKET_SIZE); + g_assert (sizeof (ColorBucket) <= BUCKET_SIZE); + g_assert (API_INDEX_BITS + INCOMING_COLORS_BITS <= 31); + bridge_processor = collector; +} + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/sgen-toggleref.c b/unity-2019.4.24f1-mbe/mono/metadata/sgen-toggleref.c new file mode 100644 index 000000000..1f4e1c64d --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/sgen-toggleref.c @@ -0,0 +1,221 @@ +/** + * \file + * toggleref support for sgen + * + * Author: + * Rodrigo Kumpera (kumpera@gmail.com) + * + * Copyright 2011 Xamarin, Inc. + * Copyright (C) 2012 Xamarin Inc + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include "config.h" + +#ifdef HAVE_SGEN_GC + +#include "sgen/sgen-gc.h" +#include "sgen-toggleref.h" +#include "sgen/sgen-client.h" + + +/*only one of the two can be non null at a given time*/ +typedef struct { + GCObject *strong_ref; + GCObject *weak_ref; +} MonoGCToggleRef; + +static MonoToggleRefStatus (*toggleref_callback) (MonoObject *obj); +static MonoGCToggleRef *toggleref_array; +static int toggleref_array_size; +static int toggleref_array_capacity; + +void +sgen_process_togglerefs (void) +{ + int i, w; + int toggle_ref_counts [3] = { 0, 0, 0 }; + + SGEN_LOG (4, "Proccessing ToggleRefs %d", toggleref_array_size); + + for (i = w = 0; i < toggleref_array_size; ++i) { + int res; + MonoGCToggleRef r = toggleref_array [i]; + + MonoObject *obj; + + if (r.strong_ref) + obj = r.strong_ref; + else if (r.weak_ref) + obj = r.weak_ref; + else + continue; + + res = toggleref_callback (obj); + ++toggle_ref_counts [res]; + switch (res) { + case MONO_TOGGLE_REF_DROP: + break; + case MONO_TOGGLE_REF_STRONG: + toggleref_array [w].strong_ref = obj; + toggleref_array [w].weak_ref = NULL; + ++w; + break; + case MONO_TOGGLE_REF_WEAK: + toggleref_array [w].strong_ref = NULL; + toggleref_array [w].weak_ref = obj; + ++w; + break; + default: + g_assert_not_reached (); + } + } + + toggleref_array_size = w; + + SGEN_LOG (4, "Done Proccessing ToggleRefs dropped %d strong %d weak %d final size %d", + toggle_ref_counts [MONO_TOGGLE_REF_DROP], + toggle_ref_counts [MONO_TOGGLE_REF_STRONG], + toggle_ref_counts [MONO_TOGGLE_REF_WEAK], + w); +} + +void sgen_client_mark_togglerefs (char *start, char *end, ScanCopyContext ctx) +{ + CopyOrMarkObjectFunc copy_func = ctx.ops->copy_or_mark_object; + SgenGrayQueue *queue = ctx.queue; + int i; + + SGEN_LOG (4, "Marking ToggleRefs %d", toggleref_array_size); + + for (i = 0; i < toggleref_array_size; ++i) { + if (toggleref_array [i].strong_ref) { + GCObject *object = toggleref_array [i].strong_ref; + if ((char*)object >= start && (char*)object < end) { + SGEN_LOG (6, "\tcopying strong slot %d", i); + copy_func (&toggleref_array [i].strong_ref, queue); + } + } + } + sgen_drain_gray_stack (ctx); +} + +void sgen_client_clear_togglerefs (char *start, char *end, ScanCopyContext ctx) +{ + CopyOrMarkObjectFunc copy_func = ctx.ops->copy_or_mark_object; + SgenGrayQueue *queue = ctx.queue; + int i; + + SGEN_LOG (4, "Clearing ToggleRefs %d", toggleref_array_size); + + for (i = 0; i < toggleref_array_size; ++i) { + if (toggleref_array [i].weak_ref) { + GCObject *object = toggleref_array [i].weak_ref; + + if ((char*)object >= start && (char*)object < end) { + if (sgen_gc_is_object_ready_for_finalization (object)) { + SGEN_LOG (6, "\tcleaning weak slot %d", i); + toggleref_array [i].weak_ref = NULL; /* We defer compaction to only happen on the callback step. */ + } else { + SGEN_LOG (6, "\tkeeping weak slot %d", i); + copy_func (&toggleref_array [i].weak_ref, queue); + } + } + } + } + sgen_drain_gray_stack (ctx); +} + +static void +ensure_toggleref_capacity (int capacity) +{ + if (!toggleref_array) { + toggleref_array_capacity = 32; + toggleref_array = (MonoGCToggleRef *)sgen_alloc_internal_dynamic ( + toggleref_array_capacity * sizeof (MonoGCToggleRef), + INTERNAL_MEM_TOGGLEREF_DATA, + TRUE); + } + if (toggleref_array_size + capacity >= toggleref_array_capacity) { + MonoGCToggleRef *tmp; + int old_capacity = toggleref_array_capacity; + while (toggleref_array_capacity < toggleref_array_size + capacity) + toggleref_array_capacity *= 2; + + tmp = (MonoGCToggleRef *)sgen_alloc_internal_dynamic ( + toggleref_array_capacity * sizeof (MonoGCToggleRef), + INTERNAL_MEM_TOGGLEREF_DATA, + TRUE); + + memcpy (tmp, toggleref_array, toggleref_array_size * sizeof (MonoGCToggleRef)); + + sgen_free_internal_dynamic (toggleref_array, old_capacity * sizeof (MonoGCToggleRef), INTERNAL_MEM_TOGGLEREF_DATA); + toggleref_array = tmp; + } +} + +/** + * mono_gc_toggleref_add: + * @object object to register for toggleref processing + * @strong_ref if true the object is registered with a strong ref, a weak one otherwise + * + * Register a given object for toggleref processing. It will be stored internally and the toggleref callback will be called + * on it until it returns MONO_TOGGLE_REF_DROP or is collected. +*/ +void +mono_gc_toggleref_add (MonoObject *object, mono_bool strong_ref) +{ + if (!toggleref_callback) + return; + + SGEN_LOG (4, "Adding toggleref %p %d", object, strong_ref); + + sgen_gc_lock (); + + ensure_toggleref_capacity (1); + toggleref_array [toggleref_array_size].strong_ref = strong_ref ? object : NULL; + toggleref_array [toggleref_array_size].weak_ref = strong_ref ? NULL : object; + ++toggleref_array_size; + + sgen_gc_unlock (); +} + +/** + * mono_gc_toggleref_register_callback: + * \param callback callback used to determine the new state of the given object. + * + * The callback must decide the status of a given object. It must return one of the values in the \c MONO_TOGGLE_REF_ enum. + * This function is called with the world running but with the GC locked. This means that you can do everything that doesn't + * require GC interaction. This includes, but not limited to, allocating objects, (de)registering for finalization, manipulating + * gchandles, storing to reference fields or interacting with other threads that might perform such operations. + */ +void +mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref) (MonoObject *obj)) +{ + toggleref_callback = proccess_toggleref; +} + +static MonoToggleRefStatus +test_toggleref_callback (MonoObject *obj) +{ + static MonoClassField *mono_toggleref_test_field; + MonoToggleRefStatus status = MONO_TOGGLE_REF_DROP; + + if (!mono_toggleref_test_field) { + mono_toggleref_test_field = mono_class_get_field_from_name (mono_object_get_class (obj), "__test"); + g_assert (mono_toggleref_test_field); + } + + mono_field_get_value (obj, mono_toggleref_test_field, &status); + printf ("toggleref-cb obj %d\n", status); + return status; +} + +void +sgen_register_test_toggleref_callback (void) +{ + toggleref_callback = test_toggleref_callback; +} + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/sgen-toggleref.h b/unity-2019.4.24f1-mbe/mono/metadata/sgen-toggleref.h new file mode 100644 index 000000000..d968b6dc3 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/sgen-toggleref.h @@ -0,0 +1,33 @@ +/** + * \file + * toggleref support for sgen + * + * Copyright 2011 Xamarin, Inc. + * + * Author: + * Rodrigo Kumpera (kumpera@gmail.com) + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#ifndef _MONO_SGEN_TOGGLEREF_H_ +#define _MONO_SGEN_TOGGLEREF_H_ + +#include + +MONO_BEGIN_DECLS + +/* GC toggle ref support */ + +typedef enum { + MONO_TOGGLE_REF_DROP, + MONO_TOGGLE_REF_STRONG, + MONO_TOGGLE_REF_WEAK +} MonoToggleRefStatus; + +MONO_API void mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref) (MonoObject *obj)); +MONO_API void mono_gc_toggleref_add (MonoObject *object, mono_bool strong_ref); + +MONO_END_DECLS + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/sre-encode.c b/unity-2019.4.24f1-mbe/mono/metadata/sre-encode.c new file mode 100644 index 000000000..e7a0e0a69 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/sre-encode.c @@ -0,0 +1,1246 @@ +/** + * \file + * Routines for encoding SRE builders into a + * MonoDynamicImage and generating tokens. + * + * + * Author: + * Paolo Molaro (lupus@ximian.com) + * + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Copyright 2011 Rodrigo Kumpera + * Copyright 2016 Microsoft + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#include +#include + +#include "mono/metadata/dynamic-image-internals.h" +#include "mono/metadata/dynamic-stream-internals.h" +#include "mono/metadata/object-internals.h" +#include "mono/metadata/reflection-internals.h" +#include "mono/metadata/sre-internals.h" +#include "mono/metadata/tabledefs.h" +#include "mono/metadata/tokentype.h" +#include "mono/utils/checked-build.h" + +typedef struct { + char *p; + char *buf; + char *end; +} SigBuffer; + +static guint32 create_typespec (MonoDynamicImage *assembly, MonoType *type); +static void encode_type (MonoDynamicImage *assembly, MonoType *type, SigBuffer *buf); +static guint32 mono_image_typedef_or_ref (MonoDynamicImage *assembly, MonoType *type); + +static guint32 +mono_image_add_stream_data (MonoDynamicStream *stream, const char *data, guint32 len) +{ + return mono_dynstream_add_data (stream, data, len); +} + +static void +alloc_table (MonoDynamicTable *table, guint nrows) +{ + mono_dynimage_alloc_table (table, nrows); +} + +static void +sigbuffer_init (SigBuffer *buf, int size) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + buf->buf = (char *)g_malloc (size); + buf->p = buf->buf; + buf->end = buf->buf + size; +} + +static void +sigbuffer_make_room (SigBuffer *buf, int size) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + if (buf->end - buf->p < size) { + int new_size = buf->end - buf->buf + size + 32; + char *p = (char *)g_realloc (buf->buf, new_size); + size = buf->p - buf->buf; + buf->buf = p; + buf->p = p + size; + buf->end = buf->buf + new_size; + } +} + +static void +sigbuffer_add_value (SigBuffer *buf, guint32 val) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + sigbuffer_make_room (buf, 6); + mono_metadata_encode_value (val, buf->p, &buf->p); +} + +static void +sigbuffer_add_byte (SigBuffer *buf, guint8 val) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + sigbuffer_make_room (buf, 1); + buf->p [0] = val; + buf->p++; +} + +static void +sigbuffer_add_mem (SigBuffer *buf, char *p, guint32 size) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + sigbuffer_make_room (buf, size); + memcpy (buf->p, p, size); + buf->p += size; +} + +static void +sigbuffer_free (SigBuffer *buf) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + g_free (buf->buf); +} + +static guint32 +sigbuffer_add_to_blob_cached (MonoDynamicImage *assembly, SigBuffer *buf) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + char blob_size [8]; + char *b = blob_size; + guint32 size = buf->p - buf->buf; + /* store length */ + g_assert (size <= (buf->end - buf->buf)); + mono_metadata_encode_value (size, b, &b); + return mono_dynamic_image_add_to_blob_cached (assembly, blob_size, b-blob_size, buf->buf, size); +} + + +static void +encode_generic_class (MonoDynamicImage *assembly, MonoGenericClass *gclass, SigBuffer *buf) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + int i; + MonoGenericInst *class_inst; + MonoClass *klass; + + g_assert (gclass); + + class_inst = gclass->context.class_inst; + + sigbuffer_add_value (buf, MONO_TYPE_GENERICINST); + klass = gclass->container_class; + sigbuffer_add_value (buf, klass->byval_arg.type); + sigbuffer_add_value (buf, mono_dynimage_encode_typedef_or_ref_full (assembly, &klass->byval_arg, FALSE)); + + sigbuffer_add_value (buf, class_inst->type_argc); + for (i = 0; i < class_inst->type_argc; ++i) + encode_type (assembly, class_inst->type_argv [i], buf); + +} + +static void +encode_type (MonoDynamicImage *assembly, MonoType *type, SigBuffer *buf) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + if (!type) { + g_assert_not_reached (); + return; + } + + if (type->byref) + sigbuffer_add_value (buf, MONO_TYPE_BYREF); + + switch (type->type){ + case MONO_TYPE_VOID: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_CHAR: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_STRING: + case MONO_TYPE_OBJECT: + case MONO_TYPE_TYPEDBYREF: + sigbuffer_add_value (buf, type->type); + break; + case MONO_TYPE_PTR: + sigbuffer_add_value (buf, type->type); + encode_type (assembly, type->data.type, buf); + break; + case MONO_TYPE_SZARRAY: + sigbuffer_add_value (buf, type->type); + encode_type (assembly, &type->data.klass->byval_arg, buf); + break; + case MONO_TYPE_VALUETYPE: + case MONO_TYPE_CLASS: { + MonoClass *k = mono_class_from_mono_type (type); + + if (mono_class_is_gtd (k)) { + MonoGenericClass *gclass = mono_metadata_lookup_generic_class (k, mono_class_get_generic_container (k)->context.class_inst, TRUE); + encode_generic_class (assembly, gclass, buf); + } else { + /* + * Make sure we use the correct type. + */ + sigbuffer_add_value (buf, k->byval_arg.type); + /* + * ensure only non-byref gets passed to mono_image_typedef_or_ref(), + * otherwise two typerefs could point to the same type, leading to + * verification errors. + */ + sigbuffer_add_value (buf, mono_image_typedef_or_ref (assembly, &k->byval_arg)); + } + break; + } + case MONO_TYPE_ARRAY: + sigbuffer_add_value (buf, type->type); + encode_type (assembly, &type->data.array->eklass->byval_arg, buf); + sigbuffer_add_value (buf, type->data.array->rank); + sigbuffer_add_value (buf, 0); /* FIXME: set to 0 for now */ + sigbuffer_add_value (buf, 0); + break; + case MONO_TYPE_GENERICINST: + encode_generic_class (assembly, type->data.generic_class, buf); + break; + case MONO_TYPE_VAR: + case MONO_TYPE_MVAR: + sigbuffer_add_value (buf, type->type); + sigbuffer_add_value (buf, mono_type_get_generic_param_num (type)); + break; + default: + g_error ("need to encode type %x", type->type); + } +} + +static void +encode_reflection_type (MonoDynamicImage *assembly, MonoReflectionTypeHandle type, SigBuffer *buf, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + + if (!type) { + sigbuffer_add_value (buf, MONO_TYPE_VOID); + return; + } + + MonoType *t = mono_reflection_type_handle_mono_type (type, error); + return_if_nok (error); + encode_type (assembly, t, buf); +} + +static void +encode_reflection_type_raw (MonoDynamicImage *assembly, MonoReflectionType* type_raw, SigBuffer *buf, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); /* FIXME callers of encode_reflection_type_raw should use handles */ + error_init (error); + MONO_HANDLE_DCL (MonoReflectionType, type); + encode_reflection_type (assembly, type, buf, error); + HANDLE_FUNCTION_RETURN (); +} + + +static void +encode_custom_modifiers (MonoDynamicImage *assembly, MonoArrayHandle modreq, MonoArrayHandle modopt, SigBuffer *buf, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + MONO_REQ_GC_UNSAFE_MODE; + + int i; + + error_init (error); + + if (!MONO_HANDLE_IS_NULL (modreq)) { + for (i = 0; i < mono_array_handle_length (modreq); ++i) { + MonoType *mod = mono_type_array_get_and_resolve (modreq, i, error); + goto_if_nok (error, leave); + sigbuffer_add_byte (buf, MONO_TYPE_CMOD_REQD); + sigbuffer_add_value (buf, mono_image_typedef_or_ref (assembly, mod)); + } + } + if (!MONO_HANDLE_IS_NULL (modopt)) { + for (i = 0; i < mono_array_handle_length (modopt); ++i) { + MonoType *mod = mono_type_array_get_and_resolve (modopt, i, error); + goto_if_nok (error, leave); + sigbuffer_add_byte (buf, MONO_TYPE_CMOD_OPT); + sigbuffer_add_value (buf, mono_image_typedef_or_ref (assembly, mod)); + } + } +leave: + HANDLE_FUNCTION_RETURN (); +} + +static void +encode_custom_modifiers_raw (MonoDynamicImage *assembly, MonoArray *modreq_raw, MonoArray *modopt_raw, SigBuffer *buf, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); /* FIXME callers of encode_custom_modifiers_raw should use handles */ + error_init (error); + MONO_HANDLE_DCL (MonoArray, modreq); + MONO_HANDLE_DCL (MonoArray, modopt); + encode_custom_modifiers (assembly, modreq, modopt, buf, error); + HANDLE_FUNCTION_RETURN (); +} + + +#ifndef DISABLE_REFLECTION_EMIT +guint32 +mono_dynimage_encode_method_signature (MonoDynamicImage *assembly, MonoMethodSignature *sig) +{ + MONO_REQ_GC_UNSAFE_MODE; + + SigBuffer buf; + int i; + guint32 nparams = sig->param_count; + guint32 idx; + + if (!assembly->save) + return 0; + + sigbuffer_init (&buf, 32); + /* + * FIXME: vararg, explicit_this, differenc call_conv values... + */ + idx = sig->call_convention; + if (sig->hasthis) + idx |= 0x20; /* hasthis */ + if (sig->generic_param_count) + idx |= 0x10; /* generic */ + sigbuffer_add_byte (&buf, idx); + if (sig->generic_param_count) + sigbuffer_add_value (&buf, sig->generic_param_count); + sigbuffer_add_value (&buf, nparams); + encode_type (assembly, sig->ret, &buf); + for (i = 0; i < nparams; ++i) { + if (i == sig->sentinelpos) + sigbuffer_add_byte (&buf, MONO_TYPE_SENTINEL); + encode_type (assembly, sig->params [i], &buf); + } + idx = sigbuffer_add_to_blob_cached (assembly, &buf); + sigbuffer_free (&buf); + return idx; +} +#else /* DISABLE_REFLECTION_EMIT */ +guint32 +mono_dynimage_encode_method_signature (MonoDynamicImage *assembly, MonoMethodSignature *sig) +{ + g_assert_not_reached (); + return 0; +} +#endif + +guint32 +mono_dynimage_encode_method_builder_signature (MonoDynamicImage *assembly, ReflectionMethodBuilder *mb, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + + /* + * FIXME: reuse code from method_encode_signature(). + */ + SigBuffer buf; + int i; + guint32 nparams = mb->parameters ? mono_array_length (mb->parameters): 0; + guint32 ngparams = mb->generic_params ? mono_array_length (mb->generic_params): 0; + guint32 notypes = mb->opt_types ? mono_array_length (mb->opt_types): 0; + guint32 idx; + + sigbuffer_init (&buf, 32); + /* LAMESPEC: all the call conv spec is foobared */ + idx = mb->call_conv & 0x60; /* has-this, explicit-this */ + if (mb->call_conv & 2) + idx |= 0x5; /* vararg */ + if (!(mb->attrs & METHOD_ATTRIBUTE_STATIC)) + idx |= 0x20; /* hasthis */ + if (ngparams) + idx |= 0x10; /* generic */ + sigbuffer_add_byte (&buf, idx); + if (ngparams) + sigbuffer_add_value (&buf, ngparams); + sigbuffer_add_value (&buf, nparams + notypes); + encode_custom_modifiers_raw (assembly, mb->return_modreq, mb->return_modopt, &buf, error); + goto_if_nok (error, leave); + encode_reflection_type_raw (assembly, mb->rtype, &buf, error); + goto_if_nok (error, leave); + for (i = 0; i < nparams; ++i) { + MonoArray *modreq = NULL; + MonoArray *modopt = NULL; + MonoReflectionType *pt; + + if (mb->param_modreq && (i < mono_array_length (mb->param_modreq))) + modreq = mono_array_get (mb->param_modreq, MonoArray*, i); + if (mb->param_modopt && (i < mono_array_length (mb->param_modopt))) + modopt = mono_array_get (mb->param_modopt, MonoArray*, i); + encode_custom_modifiers_raw (assembly, modreq, modopt, &buf, error); + goto_if_nok (error, leave); + pt = mono_array_get (mb->parameters, MonoReflectionType*, i); + encode_reflection_type_raw (assembly, pt, &buf, error); + goto_if_nok (error, leave); + } + if (notypes) + sigbuffer_add_byte (&buf, MONO_TYPE_SENTINEL); + for (i = 0; i < notypes; ++i) { + MonoReflectionType *pt; + + pt = mono_array_get (mb->opt_types, MonoReflectionType*, i); + encode_reflection_type_raw (assembly, pt, &buf, error); + goto_if_nok (error, leave); + } + + idx = sigbuffer_add_to_blob_cached (assembly, &buf); +leave: + sigbuffer_free (&buf); + return idx; +} + +guint32 +mono_dynimage_encode_locals (MonoDynamicImage *assembly, MonoReflectionILGen *ilgen, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + + MonoDynamicTable *table; + guint32 *values; + guint32 idx, sig_idx; + guint nl = mono_array_length (ilgen->locals); + SigBuffer buf; + int i; + + sigbuffer_init (&buf, 32); + sigbuffer_add_value (&buf, 0x07); + sigbuffer_add_value (&buf, nl); + for (i = 0; i < nl; ++i) { + MonoReflectionLocalBuilder *lb = mono_array_get (ilgen->locals, MonoReflectionLocalBuilder*, i); + + if (lb->is_pinned) + sigbuffer_add_value (&buf, MONO_TYPE_PINNED); + + encode_reflection_type_raw (assembly, (MonoReflectionType*)lb->type, &buf, error); + if (!is_ok (error)) { + sigbuffer_free (&buf); + return 0; + } + } + sig_idx = sigbuffer_add_to_blob_cached (assembly, &buf); + sigbuffer_free (&buf); + + if (assembly->standalonesig_cache == NULL) + assembly->standalonesig_cache = g_hash_table_new (NULL, NULL); + idx = GPOINTER_TO_UINT (g_hash_table_lookup (assembly->standalonesig_cache, GUINT_TO_POINTER (sig_idx))); + if (idx) + return idx; + + table = &assembly->tables [MONO_TABLE_STANDALONESIG]; + idx = table->next_idx ++; + table->rows ++; + alloc_table (table, table->rows); + values = table->values + idx * MONO_STAND_ALONE_SIGNATURE_SIZE; + + values [MONO_STAND_ALONE_SIGNATURE] = sig_idx; + + g_hash_table_insert (assembly->standalonesig_cache, GUINT_TO_POINTER (sig_idx), GUINT_TO_POINTER (idx)); + + return idx; +} + + +/* + * Copy len * nelem bytes from val to dest, swapping bytes to LE if necessary. + * dest may be misaligned. + */ +static void +swap_with_size (char *dest, const char* val, int len, int nelem) { + MONO_REQ_GC_NEUTRAL_MODE; +#if G_BYTE_ORDER != G_LITTLE_ENDIAN + int elem; + + for (elem = 0; elem < nelem; ++elem) { + switch (len) { + case 1: + *dest = *val; + break; + case 2: + dest [0] = val [1]; + dest [1] = val [0]; + break; + case 4: + dest [0] = val [3]; + dest [1] = val [2]; + dest [2] = val [1]; + dest [3] = val [0]; + break; + case 8: + dest [0] = val [7]; + dest [1] = val [6]; + dest [2] = val [5]; + dest [3] = val [4]; + dest [4] = val [3]; + dest [5] = val [2]; + dest [6] = val [1]; + dest [7] = val [0]; + break; + default: + g_assert_not_reached (); + } + dest += len; + val += len; + } +#else + memcpy (dest, val, len * nelem); +#endif +} + + +guint32 +mono_dynimage_encode_constant (MonoDynamicImage *assembly, MonoObject *val, MonoTypeEnum *ret_type) +{ + MONO_REQ_GC_UNSAFE_MODE; + + char blob_size [64]; + char *b = blob_size; + char *box_val; + char* buf; + guint32 idx = 0, len = 0, dummy = 0; + + buf = (char *)g_malloc (64); + if (!val) { + *ret_type = MONO_TYPE_CLASS; + len = 4; + box_val = (char*)&dummy; + } else { + box_val = ((char*)val) + sizeof (MonoObject); + *ret_type = val->vtable->klass->byval_arg.type; + } +handle_enum: + switch (*ret_type) { + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_U1: + case MONO_TYPE_I1: + len = 1; + break; + case MONO_TYPE_CHAR: + case MONO_TYPE_U2: + case MONO_TYPE_I2: + len = 2; + break; + case MONO_TYPE_U4: + case MONO_TYPE_I4: + case MONO_TYPE_R4: + len = 4; + break; + case MONO_TYPE_U8: + case MONO_TYPE_I8: + len = 8; + break; + case MONO_TYPE_R8: + len = 8; + break; + case MONO_TYPE_VALUETYPE: { + MonoClass *klass = val->vtable->klass; + + if (klass->enumtype) { + *ret_type = mono_class_enum_basetype (klass)->type; + goto handle_enum; + } else if (mono_is_corlib_image (klass->image) && strcmp (klass->name_space, "System") == 0 && strcmp (klass->name, "DateTime") == 0) { + len = 8; + } else + g_error ("we can't encode valuetypes, we should have never reached this line"); + break; + } + case MONO_TYPE_CLASS: + break; + case MONO_TYPE_STRING: { + MonoString *str = (MonoString*)val; + /* there is no signature */ + len = str->length * 2; + mono_metadata_encode_value (len, b, &b); +#if G_BYTE_ORDER != G_LITTLE_ENDIAN + { + char *swapped = g_malloc (2 * mono_string_length (str)); + const char *p = (const char*)mono_string_chars (str); + + swap_with_size (swapped, p, 2, mono_string_length (str)); + idx = mono_dynamic_image_add_to_blob_cached (assembly, blob_size, b-blob_size, swapped, len); + g_free (swapped); + } +#else + idx = mono_dynamic_image_add_to_blob_cached (assembly, blob_size, b-blob_size, (char*)mono_string_chars (str), len); +#endif + + g_free (buf); + return idx; + } + case MONO_TYPE_GENERICINST: + *ret_type = mono_class_get_generic_class (val->vtable->klass)->container_class->byval_arg.type; + goto handle_enum; + default: + g_error ("we don't encode constant type 0x%02x yet", *ret_type); + } + + /* there is no signature */ + mono_metadata_encode_value (len, b, &b); +#if G_BYTE_ORDER != G_LITTLE_ENDIAN + idx = mono_image_add_stream_data (&assembly->blob, blob_size, b-blob_size); + swap_with_size (blob_size, box_val, len, 1); + mono_image_add_stream_data (&assembly->blob, blob_size, len); +#else + idx = mono_dynamic_image_add_to_blob_cached (assembly, blob_size, b-blob_size, box_val, len); +#endif + + g_free (buf); + return idx; +} + +guint32 +mono_dynimage_encode_field_signature (MonoDynamicImage *assembly, MonoReflectionFieldBuilder *fb, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + + SigBuffer buf; + guint32 idx; + guint32 typespec = 0; + MonoType *type; + MonoClass *klass; + + type = mono_reflection_type_get_handle ((MonoReflectionType*)fb->type, error); + return_val_if_nok (error, 0); + klass = mono_class_from_mono_type (type); + + sigbuffer_init (&buf, 32); + + sigbuffer_add_value (&buf, 0x06); + encode_custom_modifiers_raw (assembly, fb->modreq, fb->modopt, &buf, error); + goto_if_nok (error, fail); + /* encode custom attributes before the type */ + + if (mono_class_is_gtd (klass)) + typespec = create_typespec (assembly, type); + + if (typespec) { + MonoGenericClass *gclass; + gclass = mono_metadata_lookup_generic_class (klass, mono_class_get_generic_container (klass)->context.class_inst, TRUE); + encode_generic_class (assembly, gclass, &buf); + } else { + encode_type (assembly, type, &buf); + } + idx = sigbuffer_add_to_blob_cached (assembly, &buf); + sigbuffer_free (&buf); + return idx; +fail: + sigbuffer_free (&buf); + return 0; +} + +#ifndef DISABLE_REFLECTION_EMIT +/*field_image is the image to which the eventual custom mods have been encoded against*/ +guint32 +mono_dynimage_encode_fieldref_signature (MonoDynamicImage *assembly, MonoImage *field_image, MonoType *type) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + SigBuffer buf; + guint32 idx, i, token; + + if (!assembly->save) + return 0; + + sigbuffer_init (&buf, 32); + + sigbuffer_add_value (&buf, 0x06); + /* encode custom attributes before the type */ + if (type->num_mods) { + for (i = 0; i < type->num_mods; ++i) { + if (field_image) { + MonoError error; + MonoClass *klass = mono_class_get_checked (field_image, type->modifiers [i].token, &error); + g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */ + + token = mono_image_typedef_or_ref (assembly, &klass->byval_arg); + } else { + token = type->modifiers [i].token; + } + + if (type->modifiers [i].required) + sigbuffer_add_byte (&buf, MONO_TYPE_CMOD_REQD); + else + sigbuffer_add_byte (&buf, MONO_TYPE_CMOD_OPT); + + sigbuffer_add_value (&buf, token); + } + } + encode_type (assembly, type, &buf); + idx = sigbuffer_add_to_blob_cached (assembly, &buf); + sigbuffer_free (&buf); + return idx; +} +#else /* DISABLE_REFLECTION_EMIT */ +guint32 +mono_dynimage_encode_fieldref_signature (MonoDynamicImage *assembly, MonoImage *field_image, MonoType *type) +{ + g_assert_not_reached (); + return 0; +} +#endif /* DISABLE_REFLECTION_EMIT */ + +static guint32 +create_typespec (MonoDynamicImage *assembly, MonoType *type) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + MonoDynamicTable *table; + guint32 *values; + guint32 token; + SigBuffer buf; + + if ((token = GPOINTER_TO_UINT (g_hash_table_lookup (assembly->typespec, type)))) + return token; + + sigbuffer_init (&buf, 32); + switch (type->type) { + case MONO_TYPE_FNPTR: + case MONO_TYPE_PTR: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_ARRAY: + case MONO_TYPE_VAR: + case MONO_TYPE_MVAR: + case MONO_TYPE_GENERICINST: + encode_type (assembly, type, &buf); + break; + case MONO_TYPE_CLASS: + case MONO_TYPE_VALUETYPE: { + MonoClass *k = mono_class_from_mono_type (type); + if (!k || !mono_class_is_gtd (k)) { + sigbuffer_free (&buf); + return 0; + } + encode_type (assembly, type, &buf); + break; + } + default: + sigbuffer_free (&buf); + return 0; + } + + table = &assembly->tables [MONO_TABLE_TYPESPEC]; + if (assembly->save) { + token = sigbuffer_add_to_blob_cached (assembly, &buf); + alloc_table (table, table->rows + 1); + values = table->values + table->next_idx * MONO_TYPESPEC_SIZE; + values [MONO_TYPESPEC_SIGNATURE] = token; + } + sigbuffer_free (&buf); + + token = MONO_TYPEDEFORREF_TYPESPEC | (table->next_idx << MONO_TYPEDEFORREF_BITS); + g_hash_table_insert (assembly->typespec, type, GUINT_TO_POINTER(token)); + table->next_idx ++; + return token; +} + +guint32 +mono_dynimage_encode_typedef_or_ref_full (MonoDynamicImage *assembly, MonoType *type, gboolean try_typespec) +{ + MONO_REQ_GC_UNSAFE_MODE; + HANDLE_FUNCTION_ENTER (); + + MonoDynamicTable *table; + guint32 *values; + guint32 token, scope, enclosing; + MonoClass *klass; + + /* if the type requires a typespec, we must try that first*/ + if (try_typespec && (token = create_typespec (assembly, type))) + goto leave; + token = GPOINTER_TO_UINT (g_hash_table_lookup (assembly->typeref, type)); + if (token) + goto leave; + klass = mono_class_from_mono_type (type); + + MonoReflectionTypeBuilderHandle tb = MONO_HANDLE_CAST (MonoReflectionTypeBuilder, mono_class_get_ref_info (klass)); + /* + * If it's in the same module and not a generic type parameter: + */ + if ((klass->image == &assembly->image) && (type->type != MONO_TYPE_VAR) && + (type->type != MONO_TYPE_MVAR)) { + token = MONO_TYPEDEFORREF_TYPEDEF | (MONO_HANDLE_GETVAL (tb, table_idx) << MONO_TYPEDEFORREF_BITS); + /* This function is called multiple times from sre and sre-save, so same object is okay */ + mono_dynamic_image_register_token (assembly, token, MONO_HANDLE_CAST (MonoObject, tb), MONO_DYN_IMAGE_TOK_SAME_OK); + goto leave; + } + + if (klass->nested_in) { + enclosing = mono_dynimage_encode_typedef_or_ref_full (assembly, &klass->nested_in->byval_arg, FALSE); + /* get the typeref idx of the enclosing type */ + enclosing >>= MONO_TYPEDEFORREF_BITS; + scope = (enclosing << MONO_RESOLUTION_SCOPE_BITS) | MONO_RESOLUTION_SCOPE_TYPEREF; + } else { + scope = mono_reflection_resolution_scope_from_image (assembly, klass->image); + } + table = &assembly->tables [MONO_TABLE_TYPEREF]; + if (assembly->save) { + alloc_table (table, table->rows + 1); + values = table->values + table->next_idx * MONO_TYPEREF_SIZE; + values [MONO_TYPEREF_SCOPE] = scope; + values [MONO_TYPEREF_NAME] = mono_dynstream_insert_string (&assembly->sheap, klass->name); + values [MONO_TYPEREF_NAMESPACE] = mono_dynstream_insert_string (&assembly->sheap, klass->name_space); + } + token = MONO_TYPEDEFORREF_TYPEREF | (table->next_idx << MONO_TYPEDEFORREF_BITS); /* typeref */ + g_hash_table_insert (assembly->typeref, type, GUINT_TO_POINTER(token)); + table->next_idx ++; + + + if (!MONO_HANDLE_IS_NULL (tb)) { + /* This function is called multiple times from sre and sre-save, so same object is okay */ + mono_dynamic_image_register_token (assembly, token, MONO_HANDLE_CAST (MonoObject, tb), MONO_DYN_IMAGE_TOK_SAME_OK); + } + +leave: + HANDLE_FUNCTION_RETURN_VAL (token); +} + +/* + * Despite the name, we handle also TypeSpec (with the above helper). + */ +static guint32 +mono_image_typedef_or_ref (MonoDynamicImage *assembly, MonoType *type) +{ + return mono_dynimage_encode_typedef_or_ref_full (assembly, type, TRUE); +} + +guint32 +mono_dynimage_encode_generic_method_sig (MonoDynamicImage *assembly, MonoGenericContext *context) +{ + SigBuffer buf; + int i; + guint32 nparams = context->method_inst->type_argc; + guint32 idx; + + if (!assembly->save) + return 0; + + sigbuffer_init (&buf, 32); + /* + * FIXME: vararg, explicit_this, differenc call_conv values... + */ + sigbuffer_add_value (&buf, 0xa); /* FIXME FIXME FIXME */ + sigbuffer_add_value (&buf, nparams); + + for (i = 0; i < nparams; i++) + encode_type (assembly, context->method_inst->type_argv [i], &buf); + + idx = sigbuffer_add_to_blob_cached (assembly, &buf); + sigbuffer_free (&buf); + return idx; +} + +#ifndef DISABLE_REFLECTION_EMIT +static gboolean +encode_sighelper_arg (MonoDynamicImage *assembly, int i, MonoArrayHandle helper_arguments, MonoArrayHandle helper_modreqs, MonoArrayHandle helper_modopts, SigBuffer* buf, MonoError *error) +{ + HANDLE_FUNCTION_ENTER(); + error_init (error); + MonoArrayHandle modreqs = MONO_HANDLE_NEW (MonoArray, NULL); + MonoArrayHandle modopts = MONO_HANDLE_NEW (MonoArray, NULL); + + if (!MONO_HANDLE_IS_NULL (helper_modreqs) && (i < mono_array_handle_length (helper_modreqs))) + MONO_HANDLE_ARRAY_GETREF (modreqs, helper_modreqs, i); + if (!MONO_HANDLE_IS_NULL (helper_modopts) && (i < mono_array_handle_length (helper_modopts))) + MONO_HANDLE_ARRAY_GETREF (modopts, helper_modopts, i); + + encode_custom_modifiers (assembly, modreqs, modopts, buf, error); + goto_if_nok (error, leave); + MonoReflectionTypeHandle pt = MONO_HANDLE_NEW (MonoReflectionType, NULL); + MONO_HANDLE_ARRAY_GETREF (pt, helper_arguments, i); + encode_reflection_type (assembly, pt, buf, error); + goto_if_nok (error, leave); +leave: + HANDLE_FUNCTION_RETURN_VAL (is_ok (error)); +} + +guint32 +mono_dynimage_encode_reflection_sighelper (MonoDynamicImage *assembly, MonoReflectionSigHelperHandle helper, MonoError *error) +{ + SigBuffer buf; + guint32 nargs; + guint32 i, idx; + + error_init (error); + + if (!assembly->save) + return 0; + + /* FIXME: this means SignatureHelper.SignatureHelpType.HELPER_METHOD */ + g_assert (MONO_HANDLE_GETVAL (helper, type) == 2); + + MonoArrayHandle arguments = MONO_HANDLE_NEW_GET (MonoArray, helper, arguments); + if (!MONO_HANDLE_IS_NULL (arguments)) + nargs = mono_array_handle_length (arguments); + else + nargs = 0; + + sigbuffer_init (&buf, 32); + + /* Encode calling convention */ + /* Change Any to Standard */ + if ((MONO_HANDLE_GETVAL (helper, call_conv) & 0x03) == 0x03) + MONO_HANDLE_SETVAL (helper, call_conv, guint32, 0x01); + /* explicit_this implies has_this */ + if (MONO_HANDLE_GETVAL (helper, call_conv) & 0x40) + MONO_HANDLE_SETVAL (helper, call_conv, guint32, MONO_HANDLE_GETVAL (helper, call_conv) & 0x20); + + if (MONO_HANDLE_GETVAL (helper, call_conv) == 0) { /* Unmanaged */ + idx = MONO_HANDLE_GETVAL (helper, unmanaged_call_conv) - 1; + } else { + /* Managed */ + idx = MONO_HANDLE_GETVAL (helper, call_conv) & 0x60; /* has_this + explicit_this */ + if (MONO_HANDLE_GETVAL (helper, call_conv) & 0x02) /* varargs */ + idx += 0x05; + } + + sigbuffer_add_byte (&buf, idx); + sigbuffer_add_value (&buf, nargs); + encode_reflection_type (assembly, MONO_HANDLE_NEW_GET (MonoReflectionType, helper, return_type), &buf, error); + goto_if_nok (error, fail); + MonoArrayHandle modreqs = MONO_HANDLE_NEW_GET (MonoArray, helper, modreqs); + MonoArrayHandle modopts = MONO_HANDLE_NEW_GET (MonoArray, helper, modopts); + for (i = 0; i < nargs; ++i) { + if (!encode_sighelper_arg (assembly, i, arguments, modreqs, modopts, &buf, error)) + goto fail; + } + idx = sigbuffer_add_to_blob_cached (assembly, &buf); + sigbuffer_free (&buf); + + return idx; +fail: + sigbuffer_free (&buf); + return 0; +} +#else /* DISABLE_REFLECTION_EMIT */ +guint32 +mono_dynimage_encode_reflection_sighelper (MonoDynamicImage *assembly, MonoReflectionSigHelperHandle helper, MonoError *error) +{ + g_assert_not_reached (); + return 0; +} +#endif /* DISABLE_REFLECTION_EMIT */ + +static gboolean +encode_reflection_types (MonoDynamicImage *assembly, MonoArrayHandle sig_arguments, int i, SigBuffer *buf, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + error_init (error); + MonoReflectionTypeHandle type = MONO_HANDLE_NEW (MonoReflectionType, NULL); + MONO_HANDLE_ARRAY_GETREF (type, sig_arguments, i); + encode_reflection_type (assembly, type, buf, error); + HANDLE_FUNCTION_RETURN_VAL (is_ok (error)); +} + +static MonoArrayHandle +reflection_sighelper_get_signature_local (MonoReflectionSigHelperHandle sig, MonoError *error) +{ + MonoReflectionModuleBuilderHandle module = MONO_HANDLE_NEW_GET (MonoReflectionModuleBuilder, sig, module); + MonoDynamicImage *assembly = MONO_HANDLE_IS_NULL (module) ? NULL : MONO_HANDLE_GETVAL (module, dynamic_image); + MonoArrayHandle sig_arguments = MONO_HANDLE_NEW_GET (MonoArray, sig, arguments); + guint32 na = MONO_HANDLE_IS_NULL (sig_arguments) ? 0 : mono_array_handle_length (sig_arguments); + guint32 buflen, i; + SigBuffer buf; + + error_init (error); + + sigbuffer_init (&buf, 32); + + sigbuffer_add_value (&buf, 0x07); + sigbuffer_add_value (&buf, na); + if (assembly != NULL){ + for (i = 0; i < na; ++i) { + if (!encode_reflection_types (assembly, sig_arguments, i, &buf, error)) + goto fail; + } + } + + buflen = buf.p - buf.buf; + MonoArrayHandle result = mono_array_new_handle (mono_domain_get (), mono_defaults.byte_class, buflen, error); + goto_if_nok (error, fail); + uint32_t gchandle; + void *base = MONO_ARRAY_HANDLE_PIN (result, char, 0, &gchandle); + memcpy (base, buf.buf, buflen); + sigbuffer_free (&buf); + mono_gchandle_free (gchandle); + return result; +fail: + sigbuffer_free (&buf); + return MONO_HANDLE_CAST (MonoArray, NULL_HANDLE); +} + +static MonoArrayHandle +reflection_sighelper_get_signature_field (MonoReflectionSigHelperHandle sig, MonoError *error) +{ + MonoReflectionModuleBuilderHandle module = MONO_HANDLE_NEW_GET (MonoReflectionModuleBuilder, sig, module); + MonoDynamicImage *assembly = MONO_HANDLE_GETVAL (module, dynamic_image); + MonoArrayHandle sig_arguments = MONO_HANDLE_NEW_GET (MonoArray, sig, arguments); + guint32 na = MONO_HANDLE_IS_NULL (sig_arguments) ? 0 : mono_array_handle_length (sig_arguments); + guint32 buflen, i; + SigBuffer buf; + + error_init (error); + + sigbuffer_init (&buf, 32); + + sigbuffer_add_value (&buf, 0x06); + for (i = 0; i < na; ++i) { + if (! encode_reflection_types (assembly, sig_arguments, i, &buf, error)) + goto fail; + } + + buflen = buf.p - buf.buf; + MonoArrayHandle result = mono_array_new_handle (mono_domain_get (), mono_defaults.byte_class, buflen, error); + goto_if_nok (error, fail); + uint32_t gchandle; + void *base = MONO_ARRAY_HANDLE_PIN (result, char, 0, &gchandle); + memcpy (base, buf.buf, buflen); + sigbuffer_free (&buf); + mono_gchandle_free (gchandle); + + return result; +fail: + sigbuffer_free (&buf); + return MONO_HANDLE_CAST (MonoArray, NULL_HANDLE); +} + +static char* +type_get_fully_qualified_name (MonoType *type) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + return mono_type_get_name_full (type, MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED); +} + +#ifndef DISABLE_REFLECTION_EMIT_SAVE +guint32 +mono_dynimage_save_encode_marshal_blob (MonoDynamicImage *assembly, MonoReflectionMarshal *minfo, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + + char *str; + SigBuffer buf; + guint32 idx, len; + + sigbuffer_init (&buf, 32); + + sigbuffer_add_value (&buf, minfo->type); + + switch (minfo->type) { + case MONO_NATIVE_BYVALTSTR: + case MONO_NATIVE_BYVALARRAY: + sigbuffer_add_value (&buf, minfo->count); + break; + case MONO_NATIVE_LPARRAY: + if (minfo->eltype || minfo->has_size) { + sigbuffer_add_value (&buf, minfo->eltype); + if (minfo->has_size) { + sigbuffer_add_value (&buf, minfo->param_num != -1? minfo->param_num: 0); + sigbuffer_add_value (&buf, minfo->count != -1? minfo->count: 0); + + /* LAMESPEC: ElemMult is undocumented */ + sigbuffer_add_value (&buf, minfo->param_num != -1? 1: 0); + } + } + break; + case MONO_NATIVE_SAFEARRAY: + if (minfo->eltype) + sigbuffer_add_value (&buf, minfo->eltype); + break; + case MONO_NATIVE_CUSTOM: + if (minfo->guid) { + str = mono_string_to_utf8_checked (minfo->guid, error); + if (!is_ok (error)) { + sigbuffer_free (&buf); + return 0; + } + len = strlen (str); + sigbuffer_add_value (&buf, len); + sigbuffer_add_mem (&buf, str, len); + g_free (str); + } else { + sigbuffer_add_value (&buf, 0); + } + /* native type name */ + sigbuffer_add_value (&buf, 0); + /* custom marshaler type name */ + if (minfo->marshaltype || minfo->marshaltyperef) { + if (minfo->marshaltyperef) { + MonoType *marshaltype = mono_reflection_type_get_handle ((MonoReflectionType*)minfo->marshaltyperef, error); + if (!is_ok (error)) { + sigbuffer_free (&buf); + return 0; + } + str = type_get_fully_qualified_name (marshaltype); + } else { + str = mono_string_to_utf8_checked (minfo->marshaltype, error); + if (!is_ok (error)) { + sigbuffer_free (&buf); + return 0; + } + } + len = strlen (str); + sigbuffer_add_value (&buf, len); + sigbuffer_add_mem (&buf, str, len); + g_free (str); + } else { + /* FIXME: Actually a bug, since this field is required. Punting for now ... */ + sigbuffer_add_value (&buf, 0); + } + if (minfo->mcookie) { + str = mono_string_to_utf8_checked (minfo->mcookie, error); + if (!is_ok (error)) { + sigbuffer_free (&buf); + return 0; + } + len = strlen (str); + sigbuffer_add_value (&buf, len); + sigbuffer_add_mem (&buf, str, len); + g_free (str); + } else { + sigbuffer_add_value (&buf, 0); + } + break; + default: + break; + } + idx = sigbuffer_add_to_blob_cached (assembly, &buf); + sigbuffer_free (&buf); + return idx; +} + +guint32 +mono_dynimage_save_encode_property_signature (MonoDynamicImage *assembly, MonoReflectionPropertyBuilder *fb, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + + SigBuffer buf; + guint32 nparams = 0; + MonoReflectionMethodBuilder *mb = fb->get_method; + MonoReflectionMethodBuilder *smb = fb->set_method; + guint32 idx, i; + + if (mb && mb->parameters) + nparams = mono_array_length (mb->parameters); + if (!mb && smb && smb->parameters) + nparams = mono_array_length (smb->parameters) - 1; + sigbuffer_init (&buf, 32); + if (fb->call_conv & 0x20) + sigbuffer_add_byte (&buf, 0x28); + else + sigbuffer_add_byte (&buf, 0x08); + sigbuffer_add_value (&buf, nparams); + if (mb) { + encode_reflection_type_raw (assembly, (MonoReflectionType*)mb->rtype, &buf, error); + if (!is_ok (error)) + goto fail; + for (i = 0; i < nparams; ++i) { + MonoReflectionType *pt = mono_array_get (mb->parameters, MonoReflectionType*, i); + encode_reflection_type_raw (assembly, pt, &buf, error); + if (!is_ok (error)) + goto fail; + } + } else if (smb && smb->parameters) { + /* the property type is the last param */ + encode_reflection_type_raw (assembly, mono_array_get (smb->parameters, MonoReflectionType*, nparams), &buf, error); + if (!is_ok (error)) + goto fail; + + for (i = 0; i < nparams; ++i) { + MonoReflectionType *pt = mono_array_get (smb->parameters, MonoReflectionType*, i); + encode_reflection_type_raw (assembly, pt, &buf, error); + if (!is_ok (error)) + goto fail; + } + } else { + encode_reflection_type_raw (assembly, (MonoReflectionType*)fb->type, &buf, error); + if (!is_ok (error)) + goto fail; + } + + idx = sigbuffer_add_to_blob_cached (assembly, &buf); + sigbuffer_free (&buf); + return idx; +fail: + sigbuffer_free (&buf); + return 0; +} + + +#else /*DISABLE_REFLECTION_EMIT_SAVE*/ +guint32 +mono_dynimage_save_encode_marshal_blob (MonoDynamicImage *assembly, MonoReflectionMarshal *minfo, MonoError *error) +{ + g_assert_not_reached (); + return 0; +} + +guint32 +mono_dynimage_save_encode_property_signature (MonoDynamicImage *assembly, MonoReflectionPropertyBuilder *fb, MonoError *error) +{ + g_assert_not_reached (); + return 0; +} +#endif /*DISABLE_REFLECTION_EMIT_SAVE*/ + +#ifndef DISABLE_REFLECTION_EMIT +MonoArrayHandle +ves_icall_SignatureHelper_get_signature_local (MonoReflectionSigHelperHandle sig, MonoError *error) +{ + error_init (error); + return reflection_sighelper_get_signature_local (sig, error); +} + +MonoArrayHandle +ves_icall_SignatureHelper_get_signature_field (MonoReflectionSigHelperHandle sig, MonoError *error) +{ + error_init (error); + return reflection_sighelper_get_signature_field (sig, error); +} +#else /* DISABLE_REFLECTION_EMIT */ +MonoArrayHandle +ves_icall_SignatureHelper_get_signature_local (MonoReflectionSigHelperHandle sig, MonoError *error) +{ + error_init (error); + g_assert_not_reached (); + return MONO_HANDLE_CAST (MonoArray, NULL_HANDLE); +} + +MonoArrayHandle +ves_icall_SignatureHelper_get_signature_field (MonoReflectionSigHelperHandle sig, MonoError *error) +{ + error_init (error); + g_assert_not_reached (); + return MONO_HANDLE_CAST (MonoArray, NULL_HANDLE); +} + +#endif /* DISABLE_REFLECTION_EMIT */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/sre-internals.h b/unity-2019.4.24f1-mbe/mono/metadata/sre-internals.h new file mode 100644 index 000000000..058092b6f --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/sre-internals.h @@ -0,0 +1,157 @@ +/** + * \file + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#ifndef __MONO_METADATA_SRE_INTERNALS_H__ +#define __MONO_METADATA_SRE_INTERNALS_H__ + +#include + +/* Keep in sync with System.Reflection.Emit.AssemblyBuilderAccess */ +enum MonoAssemblyBuilderAccess { + MonoAssemblyBuilderAccess_Run = 1, /* 0b0001 */ + MonoAssemblyBuilderAccess_Save = 2, /* 0b0010 */ + MonoAssemblyBuilderAccess_RunAndSave = 3, /* Run | Save */ + MonoAssemblyBuilderAccess_ReflectionOnly = 6, /* Refonly | Save */ + MonoAssemblyBuilderAccess_RunAndCollect = 9, /* Collect | Run */ +}; + +typedef struct _ArrayMethod ArrayMethod; + +typedef struct { + guint32 owner; + MonoReflectionGenericParam *gparam; +} GenericParamTableEntry; + +typedef struct { + MonoReflectionILGen *ilgen; + MonoReflectionType *rtype; + MonoArray *parameters; + MonoArray *generic_params; + MonoGenericContainer *generic_container; + MonoArray *pinfo; + MonoArray *opt_types; + guint32 attrs; + guint32 iattrs; + guint32 call_conv; + guint32 *table_idx; /* note: it's a pointer */ + MonoArray *code; + MonoObject *type; + MonoString *name; + MonoBoolean init_locals; + MonoBoolean skip_visibility; + MonoArray *return_modreq; + MonoArray *return_modopt; + MonoArray *param_modreq; + MonoArray *param_modopt; + MonoArray *permissions; + MonoMethod *mhandle; + guint32 nrefs; + gpointer *refs; + /* for PInvoke */ + int charset, extra_flags, native_cc; + MonoString *dll, *dllentry; +} ReflectionMethodBuilder; /* FIXME raw pointers to managed objects */ + +void +mono_reflection_emit_init (void); + +void +mono_reflection_dynimage_basic_init (MonoReflectionAssemblyBuilder *assemblyb); + +gpointer +mono_image_g_malloc0 (MonoImage *image, guint size); + +gboolean +mono_is_sre_type_builder (MonoClass *klass); + +gboolean +mono_is_sre_generic_instance (MonoClass *klass); + +gboolean +mono_is_sre_method_on_tb_inst (MonoClass *klass); + +gboolean +mono_is_sre_ctor_builder (MonoClass *klass); + +gboolean +mono_is_sre_ctor_on_tb_inst (MonoClass *klass); + +gboolean +mono_is_sr_mono_cmethod (MonoClass *klass); + +gboolean +mono_is_sr_mono_property (MonoClass *klass); + +MonoType* +mono_reflection_type_get_handle (MonoReflectionType *ref, MonoError *error); + +gpointer +mono_reflection_resolve_object (MonoImage *image, MonoObject *obj, MonoClass **handle_class, MonoGenericContext *context, MonoError *error); + +MonoType* mono_type_array_get_and_resolve (MonoArrayHandle array, int idx, MonoError* error); + +void +mono_sre_array_method_free (ArrayMethod *am); + +void +mono_sre_generic_param_table_entry_free (GenericParamTableEntry *entry); + +gboolean +mono_reflection_methodbuilder_from_method_builder (ReflectionMethodBuilder *rmb, MonoReflectionMethodBuilder *mb, + MonoError *error); +gboolean +mono_reflection_methodbuilder_from_ctor_builder (ReflectionMethodBuilder *rmb, MonoReflectionCtorBuilder *mb, + MonoError *error); + +guint32 +mono_reflection_resolution_scope_from_image (MonoDynamicImage *assembly, MonoImage *image); + +guint32 mono_reflection_method_count_clauses (MonoReflectionILGen *ilgen); + + +/* sre-encode */ + +guint32 +mono_dynimage_encode_field_signature (MonoDynamicImage *assembly, MonoReflectionFieldBuilder *fb, MonoError *error); + +guint32 +mono_dynimage_encode_constant (MonoDynamicImage *assembly, MonoObject *val, MonoTypeEnum *ret_type); + +guint32 +mono_dynimage_encode_locals (MonoDynamicImage *assembly, MonoReflectionILGen *ilgen, MonoError *error); + +guint32 +mono_dynimage_encode_fieldref_signature (MonoDynamicImage *assembly, MonoImage *field_image, MonoType *type); + +guint32 +mono_dynimage_encode_method_signature (MonoDynamicImage *assembly, MonoMethodSignature *sig); + +guint32 +mono_dynimage_encode_method_builder_signature (MonoDynamicImage *assembly, ReflectionMethodBuilder *mb, + MonoError *error); + +guint32 +mono_dynimage_encode_generic_method_sig (MonoDynamicImage *assembly, MonoGenericContext *context); + +guint32 +mono_dynimage_encode_typedef_or_ref_full (MonoDynamicImage *assembly, MonoType *type, gboolean try_typespec); + +guint32 +mono_dynimage_encode_reflection_sighelper (MonoDynamicImage *assembly, MonoReflectionSigHelperHandle helper, + MonoError *error); + +/* sre-encode, without DISABLE_REFLECTION_EMIT_SAVE (o.w. g_assert_not_reached ()) */ + +guint32 +mono_dynimage_save_encode_marshal_blob (MonoDynamicImage *assembly, MonoReflectionMarshal *minfo, MonoError *error); + +guint32 +mono_dynimage_save_encode_property_signature (MonoDynamicImage *assembly, MonoReflectionPropertyBuilder *fb, MonoError *error); + +guint32 +mono_image_get_methodref_token (MonoDynamicImage *assembly, MonoMethod *method, gboolean create_typespec); + +#endif /* __MONO_METADATA_SRE_INTERNALS_H__ */ + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/sre-save.c b/unity-2019.4.24f1-mbe/mono/metadata/sre-save.c new file mode 100644 index 000000000..227fc504b --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/sre-save.c @@ -0,0 +1,3138 @@ +/** + * \file + * Routine for saving an image to a file. + * + * + * Author: + * Paolo Molaro (lupus@ximian.com) + * + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Copyright 2011 Rodrigo Kumpera + * Copyright 2016 Microsoft + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#include + +#include "mono/metadata/dynamic-image-internals.h" +#include "mono/metadata/dynamic-stream-internals.h" +#include "mono/metadata/mono-ptr-array.h" +#include "mono/metadata/object-internals.h" +#include "mono/metadata/sre-internals.h" +#include "mono/metadata/security-manager.h" +#include "mono/metadata/tabledefs.h" +#include "mono/metadata/tokentype.h" +#include "mono/metadata/w32file.h" +#include "mono/metadata/w32error.h" + +#include "mono/utils/checked-build.h" +#include "mono/utils/mono-digest.h" +#include "mono/utils/mono-error-internals.h" +#include "mono/utils/w32api.h" + +#define TEXT_OFFSET 512 +#define CLI_H_SIZE 136 +#define FILE_ALIGN 512 +#define VIRT_ALIGN 8192 +#define START_TEXT_RVA 0x00002000 + +static void mono_image_get_generic_param_info (MonoReflectionGenericParam *gparam, guint32 owner, MonoDynamicImage *assembly); + +static void +alloc_table (MonoDynamicTable *table, guint nrows) +{ + mono_dynimage_alloc_table (table, nrows); +} + +static guint32 +string_heap_insert (MonoDynamicStream *sh, const char *str) +{ + return mono_dynstream_insert_string (sh, str); +} + +static guint32 +string_heap_insert_mstring (MonoDynamicStream *sh, MonoString *str, MonoError *error) +{ + return mono_dynstream_insert_mstring (sh, str, error); +} + +static guint32 +mono_image_add_stream_data (MonoDynamicStream *stream, const char *data, guint32 len) +{ + return mono_dynstream_add_data (stream, data, len); +} + +static guint32 +mono_image_add_stream_zero (MonoDynamicStream *stream, guint32 len) +{ + return mono_dynstream_add_zero (stream, len); +} + +static void +stream_data_align (MonoDynamicStream *stream) +{ + mono_dynstream_data_align (stream); +} + +static guint32 +mono_image_typedef_or_ref (MonoDynamicImage *assembly, MonoType *type) +{ + return mono_dynimage_encode_typedef_or_ref_full (assembly, type, TRUE); +} + +static guint32 +find_index_in_table (MonoDynamicImage *assembly, int table_idx, int col, guint32 token) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + int i; + MonoDynamicTable *table; + guint32 *values; + + table = &assembly->tables [table_idx]; + + g_assert (col < table->columns); + + values = table->values + table->columns; + for (i = 1; i <= table->rows; ++i) { + if (values [col] == token) + return i; + values += table->columns; + } + return 0; +} + +/* + * Copy len * nelem bytes from val to dest, swapping bytes to LE if necessary. + * dest may be misaligned. + */ +static void +swap_with_size (char *dest, const char* val, int len, int nelem) { + MONO_REQ_GC_NEUTRAL_MODE; +#if G_BYTE_ORDER != G_LITTLE_ENDIAN + int elem; + + for (elem = 0; elem < nelem; ++elem) { + switch (len) { + case 1: + *dest = *val; + break; + case 2: + dest [0] = val [1]; + dest [1] = val [0]; + break; + case 4: + dest [0] = val [3]; + dest [1] = val [2]; + dest [2] = val [1]; + dest [3] = val [0]; + break; + case 8: + dest [0] = val [7]; + dest [1] = val [6]; + dest [2] = val [5]; + dest [3] = val [4]; + dest [4] = val [3]; + dest [5] = val [2]; + dest [6] = val [1]; + dest [7] = val [0]; + break; + default: + g_assert_not_reached (); + } + dest += len; + val += len; + } +#else + memcpy (dest, val, len * nelem); +#endif +} + +static guint32 +add_mono_string_to_blob_cached (MonoDynamicImage *assembly, MonoString *str) +{ + MONO_REQ_GC_UNSAFE_MODE; + + char blob_size [64]; + char *b = blob_size; + guint32 idx = 0, len; + + len = str->length * 2; + mono_metadata_encode_value (len, b, &b); +#if G_BYTE_ORDER != G_LITTLE_ENDIAN + { + char *swapped = g_malloc (2 * mono_string_length (str)); + const char *p = (const char*)mono_string_chars (str); + + swap_with_size (swapped, p, 2, mono_string_length (str)); + idx = mono_dynamic_image_add_to_blob_cached (assembly, blob_size, b-blob_size, swapped, len); + g_free (swapped); + } +#else + idx = mono_dynamic_image_add_to_blob_cached (assembly, blob_size, b-blob_size, (char*)mono_string_chars (str), len); +#endif + return idx; +} + +static guint32 +image_create_token_raw (MonoDynamicImage *assembly, MonoObject* obj_raw, gboolean create_methodspec, gboolean register_token, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); /* FIXME callers of image_create_token_raw should use handles */ + error_init (error); + MONO_HANDLE_DCL (MonoObject, obj); + guint32 result = mono_image_create_token (assembly, obj, create_methodspec, register_token, error); + HANDLE_FUNCTION_RETURN_VAL (result); +} + + +/* + * idx is the table index of the object + * type is one of MONO_CUSTOM_ATTR_* + */ +static gboolean +mono_image_add_cattrs (MonoDynamicImage *assembly, guint32 idx, guint32 type, MonoArray *cattrs, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoDynamicTable *table; + MonoReflectionCustomAttr *cattr; + guint32 *values; + guint32 count, i, token; + char blob_size [6]; + char *p = blob_size; + + error_init (error); + + /* it is legal to pass a NULL cattrs: we avoid to use the if in a lot of places */ + if (!cattrs) + return TRUE; + count = mono_array_length (cattrs); + table = &assembly->tables [MONO_TABLE_CUSTOMATTRIBUTE]; + table->rows += count; + alloc_table (table, table->rows); + values = table->values + table->next_idx * MONO_CUSTOM_ATTR_SIZE; + idx <<= MONO_CUSTOM_ATTR_BITS; + idx |= type; + for (i = 0; i < count; ++i) { + cattr = (MonoReflectionCustomAttr*)mono_array_get (cattrs, gpointer, i); + values [MONO_CUSTOM_ATTR_PARENT] = idx; + g_assert (cattr->ctor != NULL); + if (mono_is_sre_ctor_builder (mono_object_class (cattr->ctor))) { + MonoReflectionCtorBuilder *ctor = (MonoReflectionCtorBuilder*)cattr->ctor; + MonoMethod *method = ctor->mhandle; + if (method->klass->image == &assembly->image) + token = MONO_TOKEN_METHOD_DEF | ((MonoReflectionCtorBuilder*)cattr->ctor)->table_idx; + else + token = mono_image_get_methodref_token (assembly, method, FALSE); + } else { + token = image_create_token_raw (assembly, (MonoObject*)cattr->ctor, FALSE, FALSE, error); /* FIXME use handles */ + if (!mono_error_ok (error)) goto fail; + } + type = mono_metadata_token_index (token); + type <<= MONO_CUSTOM_ATTR_TYPE_BITS; + switch (mono_metadata_token_table (token)) { + case MONO_TABLE_METHOD: + type |= MONO_CUSTOM_ATTR_TYPE_METHODDEF; + /* + * fixup_cattrs () needs to fix this up. We can't use image->tokens, since it contains the old token for the + * method, not the one returned by mono_image_create_token (). + */ + mono_g_hash_table_insert (assembly->remapped_tokens, GUINT_TO_POINTER (token), cattr->ctor); + break; + case MONO_TABLE_MEMBERREF: + type |= MONO_CUSTOM_ATTR_TYPE_MEMBERREF; + break; + default: + g_warning ("got wrong token in custom attr"); + continue; + } + values [MONO_CUSTOM_ATTR_TYPE] = type; + p = blob_size; + mono_metadata_encode_value (mono_array_length (cattr->data), p, &p); + values [MONO_CUSTOM_ATTR_VALUE] = mono_dynamic_image_add_to_blob_cached (assembly, blob_size, p - blob_size, + mono_array_addr (cattr->data, char, 0), mono_array_length (cattr->data)); + values += MONO_CUSTOM_ATTR_SIZE; + ++table->next_idx; + } + + return TRUE; + +fail: + return FALSE; +} + +static void +mono_image_add_decl_security (MonoDynamicImage *assembly, guint32 parent_token, MonoArray *permissions) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoDynamicTable *table; + guint32 *values; + guint32 count, i, idx; + MonoReflectionPermissionSet *perm; + + if (!permissions) + return; + + count = mono_array_length (permissions); + table = &assembly->tables [MONO_TABLE_DECLSECURITY]; + table->rows += count; + alloc_table (table, table->rows); + + for (i = 0; i < mono_array_length (permissions); ++i) { + perm = (MonoReflectionPermissionSet*)mono_array_addr (permissions, MonoReflectionPermissionSet, i); + + values = table->values + table->next_idx * MONO_DECL_SECURITY_SIZE; + + idx = mono_metadata_token_index (parent_token); + idx <<= MONO_HAS_DECL_SECURITY_BITS; + switch (mono_metadata_token_table (parent_token)) { + case MONO_TABLE_TYPEDEF: + idx |= MONO_HAS_DECL_SECURITY_TYPEDEF; + break; + case MONO_TABLE_METHOD: + idx |= MONO_HAS_DECL_SECURITY_METHODDEF; + break; + case MONO_TABLE_ASSEMBLY: + idx |= MONO_HAS_DECL_SECURITY_ASSEMBLY; + break; + default: + g_assert_not_reached (); + } + + values [MONO_DECL_SECURITY_ACTION] = perm->action; + values [MONO_DECL_SECURITY_PARENT] = idx; + values [MONO_DECL_SECURITY_PERMISSIONSET] = add_mono_string_to_blob_cached (assembly, perm->pset); + + ++table->next_idx; + } +} + +/** + * method_encode_code: + * + * @assembly the assembly + * @mb the managed MethodBuilder + * @error set on error + * + * Note that the return value is not sensible if @error is set. + */ +static guint32 +method_encode_code (MonoDynamicImage *assembly, ReflectionMethodBuilder *mb, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + char flags = 0; + guint32 idx; + guint32 code_size; + gint32 max_stack, i; + gint32 num_locals = 0; + gint32 num_exception = 0; + gint maybe_small; + guint32 fat_flags; + char fat_header [12]; + guint32 int_value; + guint16 short_value; + guint32 local_sig = 0; + guint32 header_size = 12; + MonoArray *code; + + error_init (error); + + if ((mb->attrs & (METHOD_ATTRIBUTE_PINVOKE_IMPL | METHOD_ATTRIBUTE_ABSTRACT)) || + (mb->iattrs & (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL | METHOD_IMPL_ATTRIBUTE_RUNTIME))) + return 0; + + /*if (mb->name) + g_print ("Encode method %s\n", mono_string_to_utf8 (mb->name));*/ + if (mb->ilgen) { + code = mb->ilgen->code; + code_size = mb->ilgen->code_len; + max_stack = mb->ilgen->max_stack; + num_locals = mb->ilgen->locals ? mono_array_length (mb->ilgen->locals) : 0; + if (mb->ilgen->ex_handlers) + num_exception = mono_reflection_method_count_clauses (mb->ilgen); + } else { + code = mb->code; + if (code == NULL){ + MonoError inner_error; + char *name = mono_string_to_utf8_checked (mb->name, &inner_error); + if (!is_ok (&inner_error)) { + name = g_strdup (""); + mono_error_cleanup (&inner_error); + } + char *str = g_strdup_printf ("Method %s does not have any IL associated", name); + mono_error_set_argument (error, NULL, "a method does not have any IL associated"); + g_free (str); + g_free (name); + return 0; + } + + code_size = mono_array_length (code); + max_stack = 8; /* we probably need to run a verifier on the code... */ + } + + stream_data_align (&assembly->code); + + /* check for exceptions, maxstack, locals */ + maybe_small = (max_stack <= 8) && (!num_locals) && (!num_exception); + if (maybe_small) { + if (code_size < 64 && !(code_size & 1)) { + flags = (code_size << 2) | 0x2; + } else if (code_size < 32 && (code_size & 1)) { + flags = (code_size << 2) | 0x6; /* LAMESPEC: see metadata.c */ + } else { + goto fat_header; + } + idx = mono_image_add_stream_data (&assembly->code, &flags, 1); + /* add to the fixup todo list */ + if (mb->ilgen && mb->ilgen->num_token_fixups) + mono_g_hash_table_insert (assembly->token_fixups, mb->ilgen, GUINT_TO_POINTER (idx + 1)); + mono_image_add_stream_data (&assembly->code, mono_array_addr (code, char, 0), code_size); + return assembly->text_rva + idx; + } +fat_header: + if (num_locals) { + local_sig = MONO_TOKEN_SIGNATURE | mono_dynimage_encode_locals (assembly, mb->ilgen, error); + return_val_if_nok (error, 0); + } + /* + * FIXME: need to set also the header size in fat_flags. + * (and more sects and init locals flags) + */ + fat_flags = 0x03; + if (num_exception) + fat_flags |= METHOD_HEADER_MORE_SECTS; + if (mb->init_locals) + fat_flags |= METHOD_HEADER_INIT_LOCALS; + fat_header [0] = fat_flags; + fat_header [1] = (header_size / 4 ) << 4; + short_value = GUINT16_TO_LE (max_stack); + memcpy (fat_header + 2, &short_value, 2); + int_value = GUINT32_TO_LE (code_size); + memcpy (fat_header + 4, &int_value, 4); + int_value = GUINT32_TO_LE (local_sig); + memcpy (fat_header + 8, &int_value, 4); + idx = mono_image_add_stream_data (&assembly->code, fat_header, 12); + /* add to the fixup todo list */ + if (mb->ilgen && mb->ilgen->num_token_fixups) + mono_g_hash_table_insert (assembly->token_fixups, mb->ilgen, GUINT_TO_POINTER (idx + 12)); + + mono_image_add_stream_data (&assembly->code, mono_array_addr (code, char, 0), code_size); + if (num_exception) { + unsigned char sheader [4]; + MonoILExceptionInfo * ex_info; + MonoILExceptionBlock * ex_block; + int j; + + stream_data_align (&assembly->code); + /* always use fat format for now */ + sheader [0] = METHOD_HEADER_SECTION_FAT_FORMAT | METHOD_HEADER_SECTION_EHTABLE; + num_exception *= 6 * sizeof (guint32); + num_exception += 4; /* include the size of the header */ + sheader [1] = num_exception & 0xff; + sheader [2] = (num_exception >> 8) & 0xff; + sheader [3] = (num_exception >> 16) & 0xff; + mono_image_add_stream_data (&assembly->code, (char*)sheader, 4); + /* fat header, so we are already aligned */ + /* reverse order */ + for (i = mono_array_length (mb->ilgen->ex_handlers) - 1; i >= 0; --i) { + ex_info = (MonoILExceptionInfo *)mono_array_addr (mb->ilgen->ex_handlers, MonoILExceptionInfo, i); + if (ex_info->handlers) { + int finally_start = ex_info->start + ex_info->len; + for (j = 0; j < mono_array_length (ex_info->handlers); ++j) { + guint32 val; + ex_block = (MonoILExceptionBlock*)mono_array_addr (ex_info->handlers, MonoILExceptionBlock, j); + /* the flags */ + val = GUINT32_TO_LE (ex_block->type); + mono_image_add_stream_data (&assembly->code, (char*)&val, sizeof (guint32)); + /* try offset */ + val = GUINT32_TO_LE (ex_info->start); + mono_image_add_stream_data (&assembly->code, (char*)&val, sizeof (guint32)); + /* need fault, too, probably */ + if (ex_block->type == MONO_EXCEPTION_CLAUSE_FINALLY) + val = GUINT32_TO_LE (finally_start - ex_info->start); + else + val = GUINT32_TO_LE (ex_info->len); + mono_image_add_stream_data (&assembly->code, (char*)&val, sizeof (guint32)); + /* handler offset */ + val = GUINT32_TO_LE (ex_block->start); + mono_image_add_stream_data (&assembly->code, (char*)&val, sizeof (guint32)); + /* handler len */ + val = GUINT32_TO_LE (ex_block->len); + mono_image_add_stream_data (&assembly->code, (char*)&val, sizeof (guint32)); + finally_start = ex_block->start + ex_block->len; + if (ex_block->extype) { + MonoType *extype = mono_reflection_type_get_handle ((MonoReflectionType*)ex_block->extype, error); + return_val_if_nok (error, 0); + + val = mono_metadata_token_from_dor (mono_image_typedef_or_ref (assembly, extype)); + } else { + if (ex_block->type == MONO_EXCEPTION_CLAUSE_FILTER) + val = ex_block->filter_offset; + else + val = 0; + } + val = GUINT32_TO_LE (val); + mono_image_add_stream_data (&assembly->code, (char*)&val, sizeof (guint32)); + /*g_print ("out clause %d: from %d len=%d, handler at %d, %d, finally_start=%d, ex_info->start=%d, ex_info->len=%d, ex_block->type=%d, j=%d, i=%d\n", + clause.flags, clause.try_offset, clause.try_len, clause.handler_offset, clause.handler_len, finally_start, ex_info->start, ex_info->len, ex_block->type, j, i);*/ + } + } else { + g_error ("No clauses for ex info block %d", i); + } + } + } + return assembly->text_rva + idx; +} + +/* + * Fill in the MethodDef and ParamDef tables for a method. + * This is used for both normal methods and constructors. + */ +static gboolean +mono_image_basic_method (ReflectionMethodBuilder *mb, MonoDynamicImage *assembly, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoDynamicTable *table; + guint32 *values; + guint i, count; + + error_init (error); + + /* room in this table is already allocated */ + table = &assembly->tables [MONO_TABLE_METHOD]; + *mb->table_idx = table->next_idx ++; + g_hash_table_insert (assembly->method_to_table_idx, mb->mhandle, GUINT_TO_POINTER ((*mb->table_idx))); + values = table->values + *mb->table_idx * MONO_METHOD_SIZE; + values [MONO_METHOD_NAME] = string_heap_insert_mstring (&assembly->sheap, mb->name, error); + return_val_if_nok (error, FALSE); + values [MONO_METHOD_FLAGS] = mb->attrs; + values [MONO_METHOD_IMPLFLAGS] = mb->iattrs; + values [MONO_METHOD_SIGNATURE] = mono_dynimage_encode_method_builder_signature (assembly, mb, error); + return_val_if_nok (error, FALSE); + values [MONO_METHOD_RVA] = method_encode_code (assembly, mb, error); + return_val_if_nok (error, FALSE); + + table = &assembly->tables [MONO_TABLE_PARAM]; + values [MONO_METHOD_PARAMLIST] = table->next_idx; + + mono_image_add_decl_security (assembly, + mono_metadata_make_token (MONO_TABLE_METHOD, *mb->table_idx), mb->permissions); + + if (mb->pinfo) { + MonoDynamicTable *mtable; + guint32 *mvalues; + + mtable = &assembly->tables [MONO_TABLE_FIELDMARSHAL]; + mvalues = mtable->values + mtable->next_idx * MONO_FIELD_MARSHAL_SIZE; + + count = 0; + for (i = 0; i < mono_array_length (mb->pinfo); ++i) { + if (mono_array_get (mb->pinfo, gpointer, i)) + count++; + } + table->rows += count; + alloc_table (table, table->rows); + values = table->values + table->next_idx * MONO_PARAM_SIZE; + for (i = 0; i < mono_array_length (mb->pinfo); ++i) { + MonoReflectionParamBuilder *pb; + if ((pb = mono_array_get (mb->pinfo, MonoReflectionParamBuilder*, i))) { + values [MONO_PARAM_FLAGS] = pb->attrs; + values [MONO_PARAM_SEQUENCE] = i; + if (pb->name != NULL) { + values [MONO_PARAM_NAME] = string_heap_insert_mstring (&assembly->sheap, pb->name, error); + return_val_if_nok (error, FALSE); + } else { + values [MONO_PARAM_NAME] = 0; + } + values += MONO_PARAM_SIZE; + if (pb->marshal_info) { + mtable->rows++; + alloc_table (mtable, mtable->rows); + mvalues = mtable->values + mtable->rows * MONO_FIELD_MARSHAL_SIZE; + mvalues [MONO_FIELD_MARSHAL_PARENT] = (table->next_idx << MONO_HAS_FIELD_MARSHAL_BITS) | MONO_HAS_FIELD_MARSHAL_PARAMDEF; + mvalues [MONO_FIELD_MARSHAL_NATIVE_TYPE] = mono_dynimage_save_encode_marshal_blob (assembly, pb->marshal_info, error); + return_val_if_nok (error, FALSE); + } + pb->table_idx = table->next_idx++; + if (pb->attrs & PARAM_ATTRIBUTE_HAS_DEFAULT) { + guint32 field_type = 0; + mtable = &assembly->tables [MONO_TABLE_CONSTANT]; + mtable->rows ++; + alloc_table (mtable, mtable->rows); + mvalues = mtable->values + mtable->rows * MONO_CONSTANT_SIZE; + mvalues [MONO_CONSTANT_PARENT] = MONO_HASCONSTANT_PARAM | (pb->table_idx << MONO_HASCONSTANT_BITS); + mvalues [MONO_CONSTANT_VALUE] = mono_dynimage_encode_constant (assembly, pb->def_value, &field_type); + mvalues [MONO_CONSTANT_TYPE] = field_type; + mvalues [MONO_CONSTANT_PADDING] = 0; + } + } + } + } + + return TRUE; +} + +static gboolean +mono_image_add_methodimpl (MonoDynamicImage *assembly, MonoReflectionMethodBuilder *mb, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoReflectionTypeBuilder *tb = (MonoReflectionTypeBuilder *)mb->type; + MonoDynamicTable *table; + guint32 *values; + guint32 tok; + MonoReflectionMethod *m; + int i; + + error_init (error); + + if (!mb->override_methods) + return TRUE; + + for (i = 0; i < mono_array_length (mb->override_methods); ++i) { + m = mono_array_get (mb->override_methods, MonoReflectionMethod*, i); + + table = &assembly->tables [MONO_TABLE_METHODIMPL]; + table->rows ++; + alloc_table (table, table->rows); + values = table->values + table->rows * MONO_METHODIMPL_SIZE; + values [MONO_METHODIMPL_CLASS] = tb->table_idx; + values [MONO_METHODIMPL_BODY] = MONO_METHODDEFORREF_METHODDEF | (mb->table_idx << MONO_METHODDEFORREF_BITS); + + tok = image_create_token_raw (assembly, (MonoObject*)m, FALSE, FALSE, error); /* FIXME use handles */ + return_val_if_nok (error, FALSE); + + switch (mono_metadata_token_table (tok)) { + case MONO_TABLE_MEMBERREF: + tok = (mono_metadata_token_index (tok) << MONO_METHODDEFORREF_BITS ) | MONO_METHODDEFORREF_METHODREF; + break; + case MONO_TABLE_METHOD: + tok = (mono_metadata_token_index (tok) << MONO_METHODDEFORREF_BITS ) | MONO_METHODDEFORREF_METHODDEF; + break; + default: + g_assert_not_reached (); + } + values [MONO_METHODIMPL_DECLARATION] = tok; + } + + return TRUE; +} + +#ifndef DISABLE_REFLECTION_EMIT +static gboolean +mono_image_get_method_info (MonoReflectionMethodBuilder *mb, MonoDynamicImage *assembly, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoDynamicTable *table; + guint32 *values; + ReflectionMethodBuilder rmb; + int i; + + error_init (error); + + if (!mono_reflection_methodbuilder_from_method_builder (&rmb, mb, error) || + !mono_image_basic_method (&rmb, assembly, error)) + return FALSE; + + mb->table_idx = *rmb.table_idx; + + if (mb->dll) { /* It's a P/Invoke method */ + guint32 moduleref; + /* map CharSet values to on-disk values */ + int ncharset = (mb->charset ? (mb->charset - 1) * 2 : 0); + int extra_flags = mb->extra_flags; + table = &assembly->tables [MONO_TABLE_IMPLMAP]; + table->rows ++; + alloc_table (table, table->rows); + values = table->values + table->rows * MONO_IMPLMAP_SIZE; + + values [MONO_IMPLMAP_FLAGS] = (mb->native_cc << 8) | ncharset | extra_flags; + values [MONO_IMPLMAP_MEMBER] = (mb->table_idx << 1) | 1; /* memberforwarded: method */ + if (mb->dllentry) { + values [MONO_IMPLMAP_NAME] = string_heap_insert_mstring (&assembly->sheap, mb->dllentry, error); + return_val_if_nok (error, FALSE); + } else { + values [MONO_IMPLMAP_NAME] = string_heap_insert_mstring (&assembly->sheap, mb->name, error); + return_val_if_nok (error, FALSE); + } + moduleref = string_heap_insert_mstring (&assembly->sheap, mb->dll, error); + return_val_if_nok (error, FALSE); + if (!(values [MONO_IMPLMAP_SCOPE] = find_index_in_table (assembly, MONO_TABLE_MODULEREF, MONO_MODULEREF_NAME, moduleref))) { + table = &assembly->tables [MONO_TABLE_MODULEREF]; + table->rows ++; + alloc_table (table, table->rows); + table->values [table->rows * MONO_MODULEREF_SIZE + MONO_MODULEREF_NAME] = moduleref; + values [MONO_IMPLMAP_SCOPE] = table->rows; + } + } + + if (mb->generic_params) { + table = &assembly->tables [MONO_TABLE_GENERICPARAM]; + table->rows += mono_array_length (mb->generic_params); + alloc_table (table, table->rows); + for (i = 0; i < mono_array_length (mb->generic_params); ++i) { + guint32 owner = MONO_TYPEORMETHOD_METHOD | (mb->table_idx << MONO_TYPEORMETHOD_BITS); + + mono_image_get_generic_param_info ( + (MonoReflectionGenericParam *)mono_array_get (mb->generic_params, gpointer, i), owner, assembly); + } + } + + return TRUE; +} + +static gboolean +mono_image_get_ctor_info (MonoDomain *domain, MonoReflectionCtorBuilder *mb, MonoDynamicImage *assembly, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + ReflectionMethodBuilder rmb; + + if (!mono_reflection_methodbuilder_from_ctor_builder (&rmb, mb, error)) + return FALSE; + + if (!mono_image_basic_method (&rmb, assembly, error)) + return FALSE; + + mb->table_idx = *rmb.table_idx; + + return TRUE; +} +#endif + +static void +mono_image_get_field_info (MonoReflectionFieldBuilder *fb, MonoDynamicImage *assembly, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + + MonoDynamicTable *table; + guint32 *values; + + /* maybe this fixup should be done in the C# code */ + if (fb->attrs & FIELD_ATTRIBUTE_LITERAL) + fb->attrs |= FIELD_ATTRIBUTE_HAS_DEFAULT; + table = &assembly->tables [MONO_TABLE_FIELD]; + fb->table_idx = table->next_idx ++; + g_hash_table_insert (assembly->field_to_table_idx, fb->handle, GUINT_TO_POINTER (fb->table_idx)); + values = table->values + fb->table_idx * MONO_FIELD_SIZE; + values [MONO_FIELD_NAME] = string_heap_insert_mstring (&assembly->sheap, fb->name, error); + return_if_nok (error); + values [MONO_FIELD_FLAGS] = fb->attrs; + values [MONO_FIELD_SIGNATURE] = mono_dynimage_encode_field_signature (assembly, fb, error); + return_if_nok (error); + + if (fb->offset != -1) { + table = &assembly->tables [MONO_TABLE_FIELDLAYOUT]; + table->rows ++; + alloc_table (table, table->rows); + values = table->values + table->rows * MONO_FIELD_LAYOUT_SIZE; + values [MONO_FIELD_LAYOUT_FIELD] = fb->table_idx; + values [MONO_FIELD_LAYOUT_OFFSET] = fb->offset; + } + if (fb->attrs & FIELD_ATTRIBUTE_LITERAL) { + MonoTypeEnum field_type = (MonoTypeEnum)0; + table = &assembly->tables [MONO_TABLE_CONSTANT]; + table->rows ++; + alloc_table (table, table->rows); + values = table->values + table->rows * MONO_CONSTANT_SIZE; + values [MONO_CONSTANT_PARENT] = MONO_HASCONSTANT_FIEDDEF | (fb->table_idx << MONO_HASCONSTANT_BITS); + values [MONO_CONSTANT_VALUE] = mono_dynimage_encode_constant (assembly, fb->def_value, &field_type); + values [MONO_CONSTANT_TYPE] = field_type; + values [MONO_CONSTANT_PADDING] = 0; + } + if (fb->attrs & FIELD_ATTRIBUTE_HAS_FIELD_RVA) { + guint32 rva_idx; + table = &assembly->tables [MONO_TABLE_FIELDRVA]; + table->rows ++; + alloc_table (table, table->rows); + values = table->values + table->rows * MONO_FIELD_RVA_SIZE; + values [MONO_FIELD_RVA_FIELD] = fb->table_idx; + /* + * We store it in the code section because it's simpler for now. + */ + if (fb->rva_data) { + if (mono_array_length (fb->rva_data) >= 10) + stream_data_align (&assembly->code); + rva_idx = mono_image_add_stream_data (&assembly->code, mono_array_addr (fb->rva_data, char, 0), mono_array_length (fb->rva_data)); + } else + rva_idx = mono_image_add_stream_zero (&assembly->code, mono_class_value_size (fb->handle->parent, NULL)); + values [MONO_FIELD_RVA_RVA] = rva_idx + assembly->text_rva; + } + if (fb->marshal_info) { + table = &assembly->tables [MONO_TABLE_FIELDMARSHAL]; + table->rows ++; + alloc_table (table, table->rows); + values = table->values + table->rows * MONO_FIELD_MARSHAL_SIZE; + values [MONO_FIELD_MARSHAL_PARENT] = (fb->table_idx << MONO_HAS_FIELD_MARSHAL_BITS) | MONO_HAS_FIELD_MARSHAL_FIELDSREF; + values [MONO_FIELD_MARSHAL_NATIVE_TYPE] = mono_dynimage_save_encode_marshal_blob (assembly, fb->marshal_info, error); + return_if_nok (error); + } +} + +static void +mono_image_get_property_info (MonoReflectionPropertyBuilder *pb, MonoDynamicImage *assembly, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + + MonoDynamicTable *table; + guint32 *values; + guint num_methods = 0; + guint32 semaidx; + + /* + * we need to set things in the following tables: + * PROPERTYMAP (info already filled in _get_type_info ()) + * PROPERTY (rows already preallocated in _get_type_info ()) + * METHOD (method info already done with the generic method code) + * METHODSEMANTICS + * CONSTANT + */ + table = &assembly->tables [MONO_TABLE_PROPERTY]; + pb->table_idx = table->next_idx ++; + values = table->values + pb->table_idx * MONO_PROPERTY_SIZE; + values [MONO_PROPERTY_NAME] = string_heap_insert_mstring (&assembly->sheap, pb->name, error); + return_if_nok (error); + values [MONO_PROPERTY_FLAGS] = pb->attrs; + values [MONO_PROPERTY_TYPE] = mono_dynimage_save_encode_property_signature (assembly, pb, error); + return_if_nok (error); + + + /* FIXME: we still don't handle 'other' methods */ + if (pb->get_method) num_methods ++; + if (pb->set_method) num_methods ++; + + table = &assembly->tables [MONO_TABLE_METHODSEMANTICS]; + table->rows += num_methods; + alloc_table (table, table->rows); + + if (pb->get_method) { + semaidx = table->next_idx ++; + values = table->values + semaidx * MONO_METHOD_SEMA_SIZE; + values [MONO_METHOD_SEMA_SEMANTICS] = METHOD_SEMANTIC_GETTER; + values [MONO_METHOD_SEMA_METHOD] = pb->get_method->table_idx; + values [MONO_METHOD_SEMA_ASSOCIATION] = (pb->table_idx << MONO_HAS_SEMANTICS_BITS) | MONO_HAS_SEMANTICS_PROPERTY; + } + if (pb->set_method) { + semaidx = table->next_idx ++; + values = table->values + semaidx * MONO_METHOD_SEMA_SIZE; + values [MONO_METHOD_SEMA_SEMANTICS] = METHOD_SEMANTIC_SETTER; + values [MONO_METHOD_SEMA_METHOD] = pb->set_method->table_idx; + values [MONO_METHOD_SEMA_ASSOCIATION] = (pb->table_idx << MONO_HAS_SEMANTICS_BITS) | MONO_HAS_SEMANTICS_PROPERTY; + } + if (pb->attrs & PROPERTY_ATTRIBUTE_HAS_DEFAULT) { + MonoTypeEnum field_type = (MonoTypeEnum)0; + table = &assembly->tables [MONO_TABLE_CONSTANT]; + table->rows ++; + alloc_table (table, table->rows); + values = table->values + table->rows * MONO_CONSTANT_SIZE; + values [MONO_CONSTANT_PARENT] = MONO_HASCONSTANT_PROPERTY | (pb->table_idx << MONO_HASCONSTANT_BITS); + values [MONO_CONSTANT_VALUE] = mono_dynimage_encode_constant (assembly, pb->def_value, &field_type); + values [MONO_CONSTANT_TYPE] = field_type; + values [MONO_CONSTANT_PADDING] = 0; + } +} + +static void +mono_image_get_event_info (MonoReflectionEventBuilder *eb, MonoDynamicImage *assembly, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoDynamicTable *table; + guint32 *values; + guint num_methods = 0; + guint32 semaidx; + + /* + * we need to set things in the following tables: + * EVENTMAP (info already filled in _get_type_info ()) + * EVENT (rows already preallocated in _get_type_info ()) + * METHOD (method info already done with the generic method code) + * METHODSEMANTICS + */ + table = &assembly->tables [MONO_TABLE_EVENT]; + eb->table_idx = table->next_idx ++; + values = table->values + eb->table_idx * MONO_EVENT_SIZE; + values [MONO_EVENT_NAME] = string_heap_insert_mstring (&assembly->sheap, eb->name, error); + return_if_nok (error); + values [MONO_EVENT_FLAGS] = eb->attrs; + MonoType *ebtype = mono_reflection_type_get_handle (eb->type, error); + return_if_nok (error); + values [MONO_EVENT_TYPE] = mono_image_typedef_or_ref (assembly, ebtype); + + /* + * FIXME: we still don't handle 'other' methods + */ + if (eb->add_method) num_methods ++; + if (eb->remove_method) num_methods ++; + if (eb->raise_method) num_methods ++; + + table = &assembly->tables [MONO_TABLE_METHODSEMANTICS]; + table->rows += num_methods; + alloc_table (table, table->rows); + + if (eb->add_method) { + semaidx = table->next_idx ++; + values = table->values + semaidx * MONO_METHOD_SEMA_SIZE; + values [MONO_METHOD_SEMA_SEMANTICS] = METHOD_SEMANTIC_ADD_ON; + values [MONO_METHOD_SEMA_METHOD] = eb->add_method->table_idx; + values [MONO_METHOD_SEMA_ASSOCIATION] = (eb->table_idx << MONO_HAS_SEMANTICS_BITS) | MONO_HAS_SEMANTICS_EVENT; + } + if (eb->remove_method) { + semaidx = table->next_idx ++; + values = table->values + semaidx * MONO_METHOD_SEMA_SIZE; + values [MONO_METHOD_SEMA_SEMANTICS] = METHOD_SEMANTIC_REMOVE_ON; + values [MONO_METHOD_SEMA_METHOD] = eb->remove_method->table_idx; + values [MONO_METHOD_SEMA_ASSOCIATION] = (eb->table_idx << MONO_HAS_SEMANTICS_BITS) | MONO_HAS_SEMANTICS_EVENT; + } + if (eb->raise_method) { + semaidx = table->next_idx ++; + values = table->values + semaidx * MONO_METHOD_SEMA_SIZE; + values [MONO_METHOD_SEMA_SEMANTICS] = METHOD_SEMANTIC_FIRE; + values [MONO_METHOD_SEMA_METHOD] = eb->raise_method->table_idx; + values [MONO_METHOD_SEMA_ASSOCIATION] = (eb->table_idx << MONO_HAS_SEMANTICS_BITS) | MONO_HAS_SEMANTICS_EVENT; + } +} + +static void +encode_constraints (MonoReflectionGenericParam *gparam, guint32 owner, MonoDynamicImage *assembly, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + + MonoDynamicTable *table; + guint32 num_constraints, i; + guint32 *values; + guint32 table_idx; + + table = &assembly->tables [MONO_TABLE_GENERICPARAMCONSTRAINT]; + num_constraints = gparam->iface_constraints ? + mono_array_length (gparam->iface_constraints) : 0; + table->rows += num_constraints; + if (gparam->base_type) + table->rows++; + alloc_table (table, table->rows); + + if (gparam->base_type) { + table_idx = table->next_idx ++; + values = table->values + table_idx * MONO_GENPARCONSTRAINT_SIZE; + + MonoType *gpbasetype = mono_reflection_type_get_handle (gparam->base_type, error); + return_if_nok (error); + values [MONO_GENPARCONSTRAINT_GENERICPAR] = owner; + values [MONO_GENPARCONSTRAINT_CONSTRAINT] = mono_image_typedef_or_ref (assembly, gpbasetype); + } + + for (i = 0; i < num_constraints; i++) { + MonoReflectionType *constraint = (MonoReflectionType *)mono_array_get ( + gparam->iface_constraints, gpointer, i); + + table_idx = table->next_idx ++; + values = table->values + table_idx * MONO_GENPARCONSTRAINT_SIZE; + + MonoType *constraint_type = mono_reflection_type_get_handle (constraint, error); + return_if_nok (error); + + values [MONO_GENPARCONSTRAINT_GENERICPAR] = owner; + values [MONO_GENPARCONSTRAINT_CONSTRAINT] = mono_image_typedef_or_ref (assembly, constraint_type); + } +} + +static void +mono_image_get_generic_param_info (MonoReflectionGenericParam *gparam, guint32 owner, MonoDynamicImage *assembly) +{ + MONO_REQ_GC_UNSAFE_MODE; + + GenericParamTableEntry *entry; + + /* + * The GenericParam table must be sorted according to the `owner' field. + * We need to do this sorting prior to writing the GenericParamConstraint + * table, since we have to use the final GenericParam table indices there + * and they must also be sorted. + */ + + entry = g_new0 (GenericParamTableEntry, 1); + entry->owner = owner; + /* FIXME: track where gen_params should be freed and remove the GC root as well */ + MONO_GC_REGISTER_ROOT_IF_MOVING (entry->gparam, MONO_ROOT_SOURCE_REFLECTION, NULL, "Reflection Generic Parameter"); + entry->gparam = gparam; + + g_ptr_array_add (assembly->gen_params, entry); +} + +static gboolean +write_generic_param_entry (MonoDynamicImage *assembly, GenericParamTableEntry *entry, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoDynamicTable *table; + MonoGenericParam *param; + guint32 *values; + guint32 table_idx; + + error_init (error); + + table = &assembly->tables [MONO_TABLE_GENERICPARAM]; + table_idx = table->next_idx ++; + values = table->values + table_idx * MONO_GENERICPARAM_SIZE; + + MonoType *gparam_type = mono_reflection_type_get_handle ((MonoReflectionType*)entry->gparam, error); + return_val_if_nok (error, FALSE); + + param = gparam_type->data.generic_param; + + values [MONO_GENERICPARAM_OWNER] = entry->owner; + values [MONO_GENERICPARAM_FLAGS] = entry->gparam->attrs; + values [MONO_GENERICPARAM_NUMBER] = mono_generic_param_num (param); + values [MONO_GENERICPARAM_NAME] = string_heap_insert (&assembly->sheap, mono_generic_param_info (param)->name); + + if (!mono_image_add_cattrs (assembly, table_idx, MONO_CUSTOM_ATTR_GENERICPAR, entry->gparam->cattrs, error)) + return FALSE; + + encode_constraints (entry->gparam, table_idx, assembly, error); + return_val_if_nok (error, FALSE); + + return TRUE; +} + +static void +collect_types (MonoPtrArray *types, MonoReflectionTypeBuilder *type) +{ + int i; + + mono_ptr_array_append (*types, type); + + if (!type->subtypes) + return; + + for (i = 0; i < mono_array_length (type->subtypes); ++i) { + MonoReflectionTypeBuilder *subtype = mono_array_get (type->subtypes, MonoReflectionTypeBuilder*, i); + collect_types (types, subtype); + } +} + +static gint +compare_types_by_table_idx (MonoReflectionTypeBuilder **type1, MonoReflectionTypeBuilder **type2) +{ + if ((*type1)->table_idx < (*type2)->table_idx) + return -1; + else + if ((*type1)->table_idx > (*type2)->table_idx) + return 1; + else + return 0; +} + +static gboolean +params_add_cattrs (MonoDynamicImage *assembly, MonoArray *pinfo, MonoError *error) { + int i; + + error_init (error); + if (!pinfo) + return TRUE; + for (i = 0; i < mono_array_length (pinfo); ++i) { + MonoReflectionParamBuilder *pb; + pb = mono_array_get (pinfo, MonoReflectionParamBuilder *, i); + if (!pb) + continue; + if (!mono_image_add_cattrs (assembly, pb->table_idx, MONO_CUSTOM_ATTR_PARAMDEF, pb->cattrs, error)) + return FALSE; + } + + return TRUE; +} + +static gboolean +type_add_cattrs (MonoDynamicImage *assembly, MonoReflectionTypeBuilder *tb, MonoError *error) { + int i; + + error_init (error); + + if (!mono_image_add_cattrs (assembly, tb->table_idx, MONO_CUSTOM_ATTR_TYPEDEF, tb->cattrs, error)) + return FALSE; + if (tb->fields) { + for (i = 0; i < tb->num_fields; ++i) { + MonoReflectionFieldBuilder* fb; + fb = mono_array_get (tb->fields, MonoReflectionFieldBuilder*, i); + if (!mono_image_add_cattrs (assembly, fb->table_idx, MONO_CUSTOM_ATTR_FIELDDEF, fb->cattrs, error)) + return FALSE; + } + } + if (tb->events) { + for (i = 0; i < mono_array_length (tb->events); ++i) { + MonoReflectionEventBuilder* eb; + eb = mono_array_get (tb->events, MonoReflectionEventBuilder*, i); + if (!mono_image_add_cattrs (assembly, eb->table_idx, MONO_CUSTOM_ATTR_EVENT, eb->cattrs, error)) + return FALSE; + } + } + if (tb->properties) { + for (i = 0; i < mono_array_length (tb->properties); ++i) { + MonoReflectionPropertyBuilder* pb; + pb = mono_array_get (tb->properties, MonoReflectionPropertyBuilder*, i); + if (!mono_image_add_cattrs (assembly, pb->table_idx, MONO_CUSTOM_ATTR_PROPERTY, pb->cattrs, error)) + return FALSE; + } + } + if (tb->ctors) { + for (i = 0; i < mono_array_length (tb->ctors); ++i) { + MonoReflectionCtorBuilder* cb; + cb = mono_array_get (tb->ctors, MonoReflectionCtorBuilder*, i); + if (!mono_image_add_cattrs (assembly, cb->table_idx, MONO_CUSTOM_ATTR_METHODDEF, cb->cattrs, error) || + !params_add_cattrs (assembly, cb->pinfo, error)) + return FALSE; + } + } + + if (tb->methods) { + for (i = 0; i < tb->num_methods; ++i) { + MonoReflectionMethodBuilder* mb; + mb = mono_array_get (tb->methods, MonoReflectionMethodBuilder*, i); + if (!mono_image_add_cattrs (assembly, mb->table_idx, MONO_CUSTOM_ATTR_METHODDEF, mb->cattrs, error) || + !params_add_cattrs (assembly, mb->pinfo, error)) + return FALSE; + } + } + + if (tb->subtypes) { + for (i = 0; i < mono_array_length (tb->subtypes); ++i) { + if (!type_add_cattrs (assembly, mono_array_get (tb->subtypes, MonoReflectionTypeBuilder*, i), error)) + return FALSE; + } + } + + return TRUE; +} + +static gboolean +module_add_cattrs (MonoDynamicImage *assembly, MonoReflectionModuleBuilder *moduleb, MonoError *error) +{ + int i; + + error_init (error); + + if (!mono_image_add_cattrs (assembly, moduleb->table_idx, MONO_CUSTOM_ATTR_MODULE, moduleb->cattrs, error)) + return FALSE; + + if (moduleb->global_methods) { + for (i = 0; i < mono_array_length (moduleb->global_methods); ++i) { + MonoReflectionMethodBuilder* mb = mono_array_get (moduleb->global_methods, MonoReflectionMethodBuilder*, i); + if (!mono_image_add_cattrs (assembly, mb->table_idx, MONO_CUSTOM_ATTR_METHODDEF, mb->cattrs, error) || + !params_add_cattrs (assembly, mb->pinfo, error)) + return FALSE; + } + } + + if (moduleb->global_fields) { + for (i = 0; i < mono_array_length (moduleb->global_fields); ++i) { + MonoReflectionFieldBuilder *fb = mono_array_get (moduleb->global_fields, MonoReflectionFieldBuilder*, i); + if (!mono_image_add_cattrs (assembly, fb->table_idx, MONO_CUSTOM_ATTR_FIELDDEF, fb->cattrs, error)) + return FALSE; + } + } + + if (moduleb->types) { + for (i = 0; i < moduleb->num_types; ++i) { + if (!type_add_cattrs (assembly, mono_array_get (moduleb->types, MonoReflectionTypeBuilder*, i), error)) + return FALSE; + } + } + + return TRUE; +} + +static gboolean +mono_image_fill_file_table (MonoDomain *domain, MonoReflectionModule *module, MonoDynamicImage *assembly, MonoError *error) +{ + MonoDynamicTable *table; + guint32 *values; + char blob_size [6]; + guchar hash [20]; + char *b = blob_size; + char *dir, *path; + + error_init (error); + + table = &assembly->tables [MONO_TABLE_FILE]; + table->rows++; + alloc_table (table, table->rows); + values = table->values + table->next_idx * MONO_FILE_SIZE; + values [MONO_FILE_FLAGS] = FILE_CONTAINS_METADATA; + values [MONO_FILE_NAME] = string_heap_insert (&assembly->sheap, module->image->module_name); + if (image_is_dynamic (module->image)) { + /* This depends on the fact that the main module is emitted last */ + dir = mono_string_to_utf8_checked (((MonoReflectionModuleBuilder*)module)->assemblyb->dir, error); + return_val_if_nok (error, FALSE); + path = g_strdup_printf ("%s%c%s", dir, G_DIR_SEPARATOR, module->image->module_name); + } else { + dir = NULL; + path = g_strdup (module->image->name); + } + mono_sha1_get_digest_from_file (path, hash); + g_free (dir); + g_free (path); + mono_metadata_encode_value (20, b, &b); + values [MONO_FILE_HASH_VALUE] = mono_image_add_stream_data (&assembly->blob, blob_size, b-blob_size); + mono_image_add_stream_data (&assembly->blob, (char*)hash, 20); + table->next_idx ++; + return TRUE; +} + +static void +mono_image_fill_module_table (MonoDomain *domain, MonoReflectionModuleBuilder *mb, MonoDynamicImage *assembly, MonoError *error) +{ + MonoDynamicTable *table; + int i; + + error_init (error); + + table = &assembly->tables [MONO_TABLE_MODULE]; + mb->table_idx = table->next_idx ++; + table->values [mb->table_idx * MONO_MODULE_SIZE + MONO_MODULE_NAME] = string_heap_insert_mstring (&assembly->sheap, mb->module.name, error); + return_if_nok (error); + i = mono_image_add_stream_data (&assembly->guid, mono_array_addr (mb->guid, char, 0), 16); + i /= 16; + ++i; + table->values [mb->table_idx * MONO_MODULE_SIZE + MONO_MODULE_GENERATION] = 0; + table->values [mb->table_idx * MONO_MODULE_SIZE + MONO_MODULE_MVID] = i; + table->values [mb->table_idx * MONO_MODULE_SIZE + MONO_MODULE_ENC] = 0; + table->values [mb->table_idx * MONO_MODULE_SIZE + MONO_MODULE_ENCBASE] = 0; +} + +static guint32 +mono_image_fill_export_table_from_class (MonoDomain *domain, MonoClass *klass, + guint32 module_index, guint32 parent_index, MonoDynamicImage *assembly) +{ + MonoDynamicTable *table; + guint32 *values; + guint32 visib, res; + + visib = mono_class_get_flags (klass) & TYPE_ATTRIBUTE_VISIBILITY_MASK; + if (! ((visib & TYPE_ATTRIBUTE_PUBLIC) || (visib & TYPE_ATTRIBUTE_NESTED_PUBLIC))) + return 0; + + table = &assembly->tables [MONO_TABLE_EXPORTEDTYPE]; + table->rows++; + alloc_table (table, table->rows); + values = table->values + table->next_idx * MONO_EXP_TYPE_SIZE; + + values [MONO_EXP_TYPE_FLAGS] = mono_class_get_flags (klass); + values [MONO_EXP_TYPE_TYPEDEF] = klass->type_token; + if (klass->nested_in) + values [MONO_EXP_TYPE_IMPLEMENTATION] = (parent_index << MONO_IMPLEMENTATION_BITS) + MONO_IMPLEMENTATION_EXP_TYPE; + else + values [MONO_EXP_TYPE_IMPLEMENTATION] = (module_index << MONO_IMPLEMENTATION_BITS) + MONO_IMPLEMENTATION_FILE; + values [MONO_EXP_TYPE_NAME] = string_heap_insert (&assembly->sheap, klass->name); + values [MONO_EXP_TYPE_NAMESPACE] = string_heap_insert (&assembly->sheap, klass->name_space); + + res = table->next_idx; + + table->next_idx ++; + + /* Emit nested types */ + GList *nested_classes = mono_class_get_nested_classes_property (klass); + GList *tmp; + for (tmp = nested_classes; tmp; tmp = tmp->next) + mono_image_fill_export_table_from_class (domain, (MonoClass *)tmp->data, module_index, table->next_idx - 1, assembly); + + return res; +} + +static void +mono_image_fill_export_table (MonoDomain *domain, MonoReflectionTypeBuilder *tb, + guint32 module_index, guint32 parent_index, MonoDynamicImage *assembly, + MonoError *error) +{ + MonoClass *klass; + guint32 idx, i; + + error_init (error); + + MonoType *t = mono_reflection_type_get_handle ((MonoReflectionType*)tb, error); + return_if_nok (error); + + klass = mono_class_from_mono_type (t); + + klass->type_token = mono_metadata_make_token (MONO_TABLE_TYPEDEF, tb->table_idx); + + idx = mono_image_fill_export_table_from_class (domain, klass, module_index, + parent_index, assembly); + + /* + * Emit nested types + * We need to do this ourselves since klass->nested_classes is not set up. + */ + if (tb->subtypes) { + for (i = 0; i < mono_array_length (tb->subtypes); ++i) { + mono_image_fill_export_table (domain, mono_array_get (tb->subtypes, MonoReflectionTypeBuilder*, i), module_index, idx, assembly, error); + return_if_nok (error); + } + } +} + +static void +mono_image_fill_export_table_from_module (MonoDomain *domain, MonoReflectionModule *module, + guint32 module_index, MonoDynamicImage *assembly) +{ + MonoImage *image = module->image; + MonoTableInfo *t; + guint32 i; + + t = &image->tables [MONO_TABLE_TYPEDEF]; + + for (i = 0; i < t->rows; ++i) { + MonoError error; + MonoClass *klass = mono_class_get_checked (image, mono_metadata_make_token (MONO_TABLE_TYPEDEF, i + 1), &error); + g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */ + + if (mono_class_is_public (klass)) + mono_image_fill_export_table_from_class (domain, klass, module_index, 0, assembly); + } +} + +static void +add_exported_type (MonoReflectionAssemblyBuilder *assemblyb, MonoDynamicImage *assembly, MonoClass *klass, guint32 parent_index) +{ + MonoDynamicTable *table; + guint32 *values; + guint32 scope, scope_idx, impl, current_idx; + gboolean forwarder = TRUE; + gpointer iter = NULL; + MonoClass *nested; + + if (klass->nested_in) { + impl = (parent_index << MONO_IMPLEMENTATION_BITS) + MONO_IMPLEMENTATION_EXP_TYPE; + forwarder = FALSE; + } else { + scope = mono_reflection_resolution_scope_from_image (assembly, klass->image); + g_assert ((scope & MONO_RESOLUTION_SCOPE_MASK) == MONO_RESOLUTION_SCOPE_ASSEMBLYREF); + scope_idx = scope >> MONO_RESOLUTION_SCOPE_BITS; + impl = (scope_idx << MONO_IMPLEMENTATION_BITS) + MONO_IMPLEMENTATION_ASSEMBLYREF; + } + + table = &assembly->tables [MONO_TABLE_EXPORTEDTYPE]; + + table->rows++; + alloc_table (table, table->rows); + current_idx = table->next_idx; + values = table->values + current_idx * MONO_EXP_TYPE_SIZE; + + values [MONO_EXP_TYPE_FLAGS] = forwarder ? TYPE_ATTRIBUTE_FORWARDER : 0; + values [MONO_EXP_TYPE_TYPEDEF] = 0; + values [MONO_EXP_TYPE_IMPLEMENTATION] = impl; + values [MONO_EXP_TYPE_NAME] = string_heap_insert (&assembly->sheap, klass->name); + values [MONO_EXP_TYPE_NAMESPACE] = string_heap_insert (&assembly->sheap, klass->name_space); + + table->next_idx++; + + while ((nested = mono_class_get_nested_types (klass, &iter))) + add_exported_type (assemblyb, assembly, nested, current_idx); +} + +static void +mono_image_fill_export_table_from_type_forwarders (MonoReflectionAssemblyBuilder *assemblyb, MonoDynamicImage *assembly) +{ + MonoError error; + MonoClass *klass; + int i; + + if (!assemblyb->type_forwarders) + return; + + for (i = 0; i < mono_array_length (assemblyb->type_forwarders); ++i) { + MonoReflectionType *t = mono_array_get (assemblyb->type_forwarders, MonoReflectionType *, i); + MonoType *type; + if (!t) + continue; + + type = mono_reflection_type_get_handle (t, &error); + mono_error_assert_ok (&error); + g_assert (type); + + klass = mono_class_from_mono_type (type); + + add_exported_type (assemblyb, assembly, klass, 0); + } +} + +#define align_pointer(base,p)\ + do {\ + guint32 __diff = (unsigned char*)(p)-(unsigned char*)(base);\ + if (__diff & 3)\ + (p) += 4 - (__diff & 3);\ + } while (0) + +static int +compare_constants (const void *a, const void *b) +{ + const guint32 *a_values = (const guint32 *)a; + const guint32 *b_values = (const guint32 *)b; + return a_values [MONO_CONSTANT_PARENT] - b_values [MONO_CONSTANT_PARENT]; +} + +static int +compare_semantics (const void *a, const void *b) +{ + const guint32 *a_values = (const guint32 *)a; + const guint32 *b_values = (const guint32 *)b; + int assoc = a_values [MONO_METHOD_SEMA_ASSOCIATION] - b_values [MONO_METHOD_SEMA_ASSOCIATION]; + if (assoc) + return assoc; + return a_values [MONO_METHOD_SEMA_SEMANTICS] - b_values [MONO_METHOD_SEMA_SEMANTICS]; +} + +static int +compare_custom_attrs (const void *a, const void *b) +{ + const guint32 *a_values = (const guint32 *)a; + const guint32 *b_values = (const guint32 *)b; + + return a_values [MONO_CUSTOM_ATTR_PARENT] - b_values [MONO_CUSTOM_ATTR_PARENT]; +} + +static int +compare_field_marshal (const void *a, const void *b) +{ + const guint32 *a_values = (const guint32 *)a; + const guint32 *b_values = (const guint32 *)b; + + return a_values [MONO_FIELD_MARSHAL_PARENT] - b_values [MONO_FIELD_MARSHAL_PARENT]; +} + +static int +compare_nested (const void *a, const void *b) +{ + const guint32 *a_values = (const guint32 *)a; + const guint32 *b_values = (const guint32 *)b; + + return a_values [MONO_NESTED_CLASS_NESTED] - b_values [MONO_NESTED_CLASS_NESTED]; +} + +static int +compare_genericparam (const void *a, const void *b) +{ + MonoError error; + const GenericParamTableEntry **a_entry = (const GenericParamTableEntry **) a; + const GenericParamTableEntry **b_entry = (const GenericParamTableEntry **) b; + + if ((*b_entry)->owner == (*a_entry)->owner) { + MonoType *a_type = mono_reflection_type_get_handle ((MonoReflectionType*)(*a_entry)->gparam, &error); + mono_error_assert_ok (&error); + MonoType *b_type = mono_reflection_type_get_handle ((MonoReflectionType*)(*b_entry)->gparam, &error); + mono_error_assert_ok (&error); + return + mono_type_get_generic_param_num (a_type) - + mono_type_get_generic_param_num (b_type); + } else + return (*a_entry)->owner - (*b_entry)->owner; +} + +static int +compare_declsecurity_attrs (const void *a, const void *b) +{ + const guint32 *a_values = (const guint32 *)a; + const guint32 *b_values = (const guint32 *)b; + + return a_values [MONO_DECL_SECURITY_PARENT] - b_values [MONO_DECL_SECURITY_PARENT]; +} + +static int +compare_interface_impl (const void *a, const void *b) +{ + const guint32 *a_values = (const guint32 *)a; + const guint32 *b_values = (const guint32 *)b; + + int klass = a_values [MONO_INTERFACEIMPL_CLASS] - b_values [MONO_INTERFACEIMPL_CLASS]; + if (klass) + return klass; + + return a_values [MONO_INTERFACEIMPL_INTERFACE] - b_values [MONO_INTERFACEIMPL_INTERFACE]; +} + +struct StreamDesc { + const char *name; + MonoDynamicStream *stream; +}; + +/* + * build_compressed_metadata() fills in the blob of data that represents the + * raw metadata as it will be saved in the PE file. The five streams are output + * and the metadata tables are comnpressed from the guint32 array representation, + * to the compressed on-disk format. + */ +static gboolean +build_compressed_metadata (MonoDynamicImage *assembly, MonoError *error) +{ + MonoDynamicTable *table; + int i; + guint64 valid_mask = 0; + guint64 sorted_mask; + guint32 heapt_size = 0; + guint32 meta_size = 256; /* allow for header and other stuff */ + guint32 table_offset; + guint32 ntables = 0; + guint64 *int64val; + guint32 *int32val; + guint16 *int16val; + MonoImage *meta; + unsigned char *p; + struct StreamDesc stream_desc [5]; + + error_init (error); + + qsort (assembly->gen_params->pdata, assembly->gen_params->len, sizeof (gpointer), compare_genericparam); + for (i = 0; i < assembly->gen_params->len; i++) { + GenericParamTableEntry *entry = (GenericParamTableEntry *)g_ptr_array_index (assembly->gen_params, i); + if (!write_generic_param_entry (assembly, entry, error)) + return FALSE; + } + + stream_desc [0].name = "#~"; + stream_desc [0].stream = &assembly->tstream; + stream_desc [1].name = "#Strings"; + stream_desc [1].stream = &assembly->sheap; + stream_desc [2].name = "#US"; + stream_desc [2].stream = &assembly->us; + stream_desc [3].name = "#Blob"; + stream_desc [3].stream = &assembly->blob; + stream_desc [4].name = "#GUID"; + stream_desc [4].stream = &assembly->guid; + + /* tables that are sorted */ + sorted_mask = ((guint64)1 << MONO_TABLE_CONSTANT) | ((guint64)1 << MONO_TABLE_FIELDMARSHAL) + | ((guint64)1 << MONO_TABLE_METHODSEMANTICS) | ((guint64)1 << MONO_TABLE_CLASSLAYOUT) + | ((guint64)1 << MONO_TABLE_FIELDLAYOUT) | ((guint64)1 << MONO_TABLE_FIELDRVA) + | ((guint64)1 << MONO_TABLE_IMPLMAP) | ((guint64)1 << MONO_TABLE_NESTEDCLASS) + | ((guint64)1 << MONO_TABLE_METHODIMPL) | ((guint64)1 << MONO_TABLE_CUSTOMATTRIBUTE) + | ((guint64)1 << MONO_TABLE_DECLSECURITY) | ((guint64)1 << MONO_TABLE_GENERICPARAM) + | ((guint64)1 << MONO_TABLE_INTERFACEIMPL); + + /* Compute table sizes */ + /* the MonoImage has already been created in mono_reflection_dynimage_basic_init() */ + meta = &assembly->image; + + /* sizes should be multiple of 4 */ + mono_dynstream_data_align (&assembly->blob); + mono_dynstream_data_align (&assembly->guid); + mono_dynstream_data_align (&assembly->sheap); + mono_dynstream_data_align (&assembly->us); + + /* Setup the info used by compute_sizes () */ + meta->idx_blob_wide = assembly->blob.index >= 65536 ? 1 : 0; + meta->idx_guid_wide = assembly->guid.index >= 65536 ? 1 : 0; + meta->idx_string_wide = assembly->sheap.index >= 65536 ? 1 : 0; + + meta_size += assembly->blob.index; + meta_size += assembly->guid.index; + meta_size += assembly->sheap.index; + meta_size += assembly->us.index; + + for (i=0; i < MONO_TABLE_NUM; ++i) + meta->tables [i].rows = assembly->tables [i].rows; + + for (i = 0; i < MONO_TABLE_NUM; i++){ + if (meta->tables [i].rows == 0) + continue; + valid_mask |= (guint64)1 << i; + ntables ++; + meta->tables [i].row_size = mono_metadata_compute_size ( + meta, i, &meta->tables [i].size_bitfield); + heapt_size += meta->tables [i].row_size * meta->tables [i].rows; + } + heapt_size += 24; /* #~ header size */ + heapt_size += ntables * 4; + /* make multiple of 4 */ + heapt_size += 3; + heapt_size &= ~3; + meta_size += heapt_size; + meta->raw_metadata = (char *)g_malloc0 (meta_size); + p = (unsigned char*)meta->raw_metadata; + /* the metadata signature */ + *p++ = 'B'; *p++ = 'S'; *p++ = 'J'; *p++ = 'B'; + /* version numbers and 4 bytes reserved */ + int16val = (guint16*)p; + *int16val++ = GUINT16_TO_LE (meta->md_version_major); + *int16val = GUINT16_TO_LE (meta->md_version_minor); + p += 8; + /* version string */ + int32val = (guint32*)p; + *int32val = GUINT32_TO_LE ((strlen (meta->version) + 3) & (~3)); /* needs to be multiple of 4 */ + p += 4; + memcpy (p, meta->version, strlen (meta->version)); + p += GUINT32_FROM_LE (*int32val); + align_pointer (meta->raw_metadata, p); + int16val = (guint16*)p; + *int16val++ = GUINT16_TO_LE (0); /* flags must be 0 */ + *int16val = GUINT16_TO_LE (5); /* number of streams */ + p += 4; + + /* + * write the stream info. + */ + table_offset = (p - (unsigned char*)meta->raw_metadata) + 5 * 8 + 40; /* room needed for stream headers */ + table_offset += 3; table_offset &= ~3; + + assembly->tstream.index = heapt_size; + for (i = 0; i < 5; ++i) { + int32val = (guint32*)p; + stream_desc [i].stream->offset = table_offset; + *int32val++ = GUINT32_TO_LE (table_offset); + *int32val = GUINT32_TO_LE (stream_desc [i].stream->index); + table_offset += GUINT32_FROM_LE (*int32val); + table_offset += 3; table_offset &= ~3; + p += 8; + strcpy ((char*)p, stream_desc [i].name); + p += strlen (stream_desc [i].name) + 1; + align_pointer (meta->raw_metadata, p); + } + /* + * now copy the data, the table stream header and contents goes first. + */ + g_assert ((p - (unsigned char*)meta->raw_metadata) < assembly->tstream.offset); + p = (guchar*)meta->raw_metadata + assembly->tstream.offset; + int32val = (guint32*)p; + *int32val = GUINT32_TO_LE (0); /* reserved */ + p += 4; + + *p++ = 2; /* version */ + *p++ = 0; + + if (meta->idx_string_wide) + *p |= 0x01; + if (meta->idx_guid_wide) + *p |= 0x02; + if (meta->idx_blob_wide) + *p |= 0x04; + ++p; + *p++ = 1; /* reserved */ + int64val = (guint64*)p; + *int64val++ = GUINT64_TO_LE (valid_mask); + *int64val++ = GUINT64_TO_LE (valid_mask & sorted_mask); /* bitvector of sorted tables */ + p += 16; + int32val = (guint32*)p; + for (i = 0; i < MONO_TABLE_NUM; i++){ + if (meta->tables [i].rows == 0) + continue; + *int32val++ = GUINT32_TO_LE (meta->tables [i].rows); + } + p = (unsigned char*)int32val; + + /* sort the tables that still need sorting */ + table = &assembly->tables [MONO_TABLE_CONSTANT]; + if (table->rows) + qsort (table->values + MONO_CONSTANT_SIZE, table->rows, sizeof (guint32) * MONO_CONSTANT_SIZE, compare_constants); + table = &assembly->tables [MONO_TABLE_METHODSEMANTICS]; + if (table->rows) + qsort (table->values + MONO_METHOD_SEMA_SIZE, table->rows, sizeof (guint32) * MONO_METHOD_SEMA_SIZE, compare_semantics); + table = &assembly->tables [MONO_TABLE_CUSTOMATTRIBUTE]; + if (table->rows) + qsort (table->values + MONO_CUSTOM_ATTR_SIZE, table->rows, sizeof (guint32) * MONO_CUSTOM_ATTR_SIZE, compare_custom_attrs); + table = &assembly->tables [MONO_TABLE_FIELDMARSHAL]; + if (table->rows) + qsort (table->values + MONO_FIELD_MARSHAL_SIZE, table->rows, sizeof (guint32) * MONO_FIELD_MARSHAL_SIZE, compare_field_marshal); + table = &assembly->tables [MONO_TABLE_NESTEDCLASS]; + if (table->rows) + qsort (table->values + MONO_NESTED_CLASS_SIZE, table->rows, sizeof (guint32) * MONO_NESTED_CLASS_SIZE, compare_nested); + /* Section 21.11 DeclSecurity in Partition II doesn't specify this to be sorted by MS implementation requires it */ + table = &assembly->tables [MONO_TABLE_DECLSECURITY]; + if (table->rows) + qsort (table->values + MONO_DECL_SECURITY_SIZE, table->rows, sizeof (guint32) * MONO_DECL_SECURITY_SIZE, compare_declsecurity_attrs); + table = &assembly->tables [MONO_TABLE_INTERFACEIMPL]; + if (table->rows) + qsort (table->values + MONO_INTERFACEIMPL_SIZE, table->rows, sizeof (guint32) * MONO_INTERFACEIMPL_SIZE, compare_interface_impl); + + /* compress the tables */ + for (i = 0; i < MONO_TABLE_NUM; i++){ + int row, col; + guint32 *values; + guint32 bitfield = meta->tables [i].size_bitfield; + if (!meta->tables [i].rows) + continue; + if (assembly->tables [i].columns != mono_metadata_table_count (bitfield)) + g_error ("col count mismatch in %d: %d %d", i, assembly->tables [i].columns, mono_metadata_table_count (bitfield)); + meta->tables [i].base = (char*)p; + for (row = 1; row <= meta->tables [i].rows; ++row) { + values = assembly->tables [i].values + row * assembly->tables [i].columns; + for (col = 0; col < assembly->tables [i].columns; ++col) { + switch (mono_metadata_table_size (bitfield, col)) { + case 1: + *p++ = values [col]; + break; + case 2: + *p++ = values [col] & 0xff; + *p++ = (values [col] >> 8) & 0xff; + break; + case 4: + *p++ = values [col] & 0xff; + *p++ = (values [col] >> 8) & 0xff; + *p++ = (values [col] >> 16) & 0xff; + *p++ = (values [col] >> 24) & 0xff; + break; + default: + g_assert_not_reached (); + } + } + } + g_assert ((p - (const unsigned char*)meta->tables [i].base) == (meta->tables [i].rows * meta->tables [i].row_size)); + } + + g_assert (assembly->guid.offset + assembly->guid.index < meta_size); + memcpy (meta->raw_metadata + assembly->sheap.offset, assembly->sheap.data, assembly->sheap.index); + memcpy (meta->raw_metadata + assembly->us.offset, assembly->us.data, assembly->us.index); + memcpy (meta->raw_metadata + assembly->blob.offset, assembly->blob.data, assembly->blob.index); + memcpy (meta->raw_metadata + assembly->guid.offset, assembly->guid.data, assembly->guid.index); + + assembly->meta_size = assembly->guid.offset + assembly->guid.index; + + return TRUE; +} + +/* + * Some tables in metadata need to be sorted according to some criteria, but + * when methods and fields are first created with reflection, they may be assigned a token + * that doesn't correspond to the final token they will get assigned after the sorting. + * ILGenerator.cs keeps a fixup table that maps the position of tokens in the IL code stream + * with the reflection objects that represent them. Once all the tables are set up, the + * reflection objects will contains the correct table index. fixup_method() will fixup the + * tokens for the method with ILGenerator @ilgen. + */ +static void +fixup_method (MonoReflectionILGen *ilgen, gpointer value, MonoDynamicImage *assembly) +{ + guint32 code_idx = GPOINTER_TO_UINT (value); + MonoReflectionILTokenInfo *iltoken; + MonoReflectionTypeBuilder *tb; + MonoReflectionArrayMethod *am; + guint32 i, idx = 0; + unsigned char *target; + + for (i = 0; i < ilgen->num_token_fixups; ++i) { + iltoken = (MonoReflectionILTokenInfo *)mono_array_addr_with_size (ilgen->token_fixups, sizeof (MonoReflectionILTokenInfo), i); + target = (guchar*)assembly->code.data + code_idx + iltoken->code_pos; + switch (target [3]) { + case MONO_TABLE_FIELD: + if (!strcmp (iltoken->member->vtable->klass->name, "FieldBuilder")) { + g_assert_not_reached (); + } else if (!strcmp (iltoken->member->vtable->klass->name, "MonoField")) { + MonoClassField *f = ((MonoReflectionField*)iltoken->member)->field; + idx = GPOINTER_TO_UINT (g_hash_table_lookup (assembly->field_to_table_idx, f)); + } else { + g_assert_not_reached (); + } + break; + case MONO_TABLE_METHOD: + if (!strcmp (iltoken->member->vtable->klass->name, "MethodBuilder")) { + g_assert_not_reached (); + } else if (!strcmp (iltoken->member->vtable->klass->name, "ConstructorBuilder")) { + g_assert_not_reached (); + } else if (!strcmp (iltoken->member->vtable->klass->name, "MonoMethod") || + !strcmp (iltoken->member->vtable->klass->name, "MonoCMethod")) { + MonoMethod *m = ((MonoReflectionMethod*)iltoken->member)->method; + idx = GPOINTER_TO_UINT (g_hash_table_lookup (assembly->method_to_table_idx, m)); + } else { + g_assert_not_reached (); + } + break; + case MONO_TABLE_TYPEDEF: + if (!strcmp (iltoken->member->vtable->klass->name, "TypeBuilder")) { + g_assert_not_reached (); + } else if (!strcmp (iltoken->member->vtable->klass->name, "RuntimeType")) { + MonoClass *k = mono_class_from_mono_type (((MonoReflectionType*)iltoken->member)->type); + MonoObject *obj = mono_class_get_ref_info_raw (k); /* FIXME use handles */ + g_assert (obj); + g_assert (!strcmp (mono_object_class (obj)->name, "TypeBuilder")); + tb = (MonoReflectionTypeBuilder*)obj; + idx = tb->table_idx; + } else { + g_assert_not_reached (); + } + break; + case MONO_TABLE_TYPEREF: + g_assert (!strcmp (iltoken->member->vtable->klass->name, "RuntimeType")); + MonoClass *k = mono_class_from_mono_type (((MonoReflectionType*)iltoken->member)->type); + MonoObject *obj = mono_class_get_ref_info_raw (k); /* FIXME use handles */ + g_assert (obj); + g_assert (!strcmp (mono_object_class (obj)->name, "TypeBuilder")); + g_assert (((MonoReflectionTypeBuilder*)obj)->module->dynamic_image != assembly); + continue; + case MONO_TABLE_MEMBERREF: + if (!strcmp (iltoken->member->vtable->klass->name, "MonoArrayMethod")) { + am = (MonoReflectionArrayMethod*)iltoken->member; + idx = am->table_idx; + } else if (!strcmp (iltoken->member->vtable->klass->name, "MonoMethod") || + !strcmp (iltoken->member->vtable->klass->name, "MonoCMethod")) { + MonoMethod *m = ((MonoReflectionMethod*)iltoken->member)->method; + g_assert (mono_class_is_ginst (m->klass) || mono_class_is_gtd (m->klass)); + continue; + } else if (!strcmp (iltoken->member->vtable->klass->name, "FieldBuilder")) { + g_assert_not_reached (); + continue; + } else if (!strcmp (iltoken->member->vtable->klass->name, "MonoField")) { + continue; + } else if (!strcmp (iltoken->member->vtable->klass->name, "MethodBuilder") || + !strcmp (iltoken->member->vtable->klass->name, "ConstructorBuilder")) { + g_assert_not_reached (); + continue; + } else if (!strcmp (iltoken->member->vtable->klass->name, "FieldOnTypeBuilderInst")) { + g_assert_not_reached (); + continue; + } else if (!strcmp (iltoken->member->vtable->klass->name, "MethodOnTypeBuilderInst")) { + g_assert_not_reached (); + continue; + } else if (!strcmp (iltoken->member->vtable->klass->name, "ConstructorOnTypeBuilderInst")) { + g_assert_not_reached (); + continue; + } else { + g_assert_not_reached (); + } + break; + case MONO_TABLE_METHODSPEC: + if (!strcmp (iltoken->member->vtable->klass->name, "MonoMethod")) { + MonoMethod *m = ((MonoReflectionMethod*)iltoken->member)->method; + g_assert (mono_method_signature (m)->generic_param_count); + continue; + } else if (!strcmp (iltoken->member->vtable->klass->name, "MethodBuilder")) { + g_assert_not_reached (); + continue; + } else if (!strcmp (iltoken->member->vtable->klass->name, "MethodOnTypeBuilderInst")) { + g_assert_not_reached (); + continue; + } else { + g_assert_not_reached (); + } + break; + case MONO_TABLE_TYPESPEC: + if (!strcmp (iltoken->member->vtable->klass->name, "RuntimeType")) { + continue; + } else { + g_assert_not_reached (); + } + break; + default: + g_error ("got unexpected table 0x%02x in fixup", target [3]); + } + target [0] = idx & 0xff; + target [1] = (idx >> 8) & 0xff; + target [2] = (idx >> 16) & 0xff; + } +} + +/* + * fixup_cattrs: + * + * The CUSTOM_ATTRIBUTE table might contain METHODDEF tokens whose final + * value is not known when the table is emitted. + */ +static void +fixup_cattrs (MonoDynamicImage *assembly) +{ + MonoDynamicTable *table; + guint32 *values; + guint32 type, i, idx, token; + MonoObject *ctor; + + table = &assembly->tables [MONO_TABLE_CUSTOMATTRIBUTE]; + + for (i = 0; i < table->rows; ++i) { + values = table->values + ((i + 1) * MONO_CUSTOM_ATTR_SIZE); + + type = values [MONO_CUSTOM_ATTR_TYPE]; + if ((type & MONO_CUSTOM_ATTR_TYPE_MASK) == MONO_CUSTOM_ATTR_TYPE_METHODDEF) { + idx = type >> MONO_CUSTOM_ATTR_TYPE_BITS; + token = mono_metadata_make_token (MONO_TABLE_METHOD, idx); + ctor = (MonoObject *)mono_g_hash_table_lookup (assembly->remapped_tokens, GUINT_TO_POINTER (token)); + g_assert (ctor); + + if (!strcmp (ctor->vtable->klass->name, "MonoCMethod")) { + MonoMethod *m = ((MonoReflectionMethod*)ctor)->method; + idx = GPOINTER_TO_UINT (g_hash_table_lookup (assembly->method_to_table_idx, m)); + values [MONO_CUSTOM_ATTR_TYPE] = (idx << MONO_CUSTOM_ATTR_TYPE_BITS) | MONO_CUSTOM_ATTR_TYPE_METHODDEF; + } else if (!strcmp (ctor->vtable->klass->name, "ConstructorBuilder")) { + MonoMethod *m = ((MonoReflectionCtorBuilder*)ctor)->mhandle; + idx = GPOINTER_TO_UINT (g_hash_table_lookup (assembly->method_to_table_idx, m)); + values [MONO_CUSTOM_ATTR_TYPE] = (idx << MONO_CUSTOM_ATTR_TYPE_BITS) | MONO_CUSTOM_ATTR_TYPE_METHODDEF; + } + } + } +} + +static gboolean +assembly_add_resource_manifest (MonoReflectionModuleBuilder *mb, MonoDynamicImage *assembly, MonoReflectionResource *rsrc, guint32 implementation, MonoError *error) +{ + MonoDynamicTable *table; + guint32 *values; + + error_init (error); + + table = &assembly->tables [MONO_TABLE_MANIFESTRESOURCE]; + table->rows++; + alloc_table (table, table->rows); + values = table->values + table->next_idx * MONO_MANIFEST_SIZE; + values [MONO_MANIFEST_OFFSET] = rsrc->offset; + values [MONO_MANIFEST_FLAGS] = rsrc->attrs; + values [MONO_MANIFEST_NAME] = string_heap_insert_mstring (&assembly->sheap, rsrc->name, error); + return_val_if_nok (error, FALSE); + values [MONO_MANIFEST_IMPLEMENTATION] = implementation; + table->next_idx++; + return TRUE; +} + +static gboolean +assembly_add_resource (MonoReflectionModuleBuilder *mb, MonoDynamicImage *assembly, MonoReflectionResource *rsrc, MonoError *error) +{ + MonoDynamicTable *table; + guint32 *values; + char blob_size [6]; + guchar hash [20]; + char *b = blob_size; + char *name, *sname; + guint32 idx, offset; + + error_init (error); + + if (rsrc->filename) { + name = mono_string_to_utf8_checked (rsrc->filename, error); + return_val_if_nok (error, FALSE); + sname = g_path_get_basename (name); + + table = &assembly->tables [MONO_TABLE_FILE]; + table->rows++; + alloc_table (table, table->rows); + values = table->values + table->next_idx * MONO_FILE_SIZE; + values [MONO_FILE_FLAGS] = FILE_CONTAINS_NO_METADATA; + values [MONO_FILE_NAME] = string_heap_insert (&assembly->sheap, sname); + g_free (sname); + + mono_sha1_get_digest_from_file (name, hash); + mono_metadata_encode_value (20, b, &b); + values [MONO_FILE_HASH_VALUE] = mono_image_add_stream_data (&assembly->blob, blob_size, b-blob_size); + mono_image_add_stream_data (&assembly->blob, (char*)hash, 20); + g_free (name); + idx = table->next_idx++; + rsrc->offset = 0; + idx = MONO_IMPLEMENTATION_FILE | (idx << MONO_IMPLEMENTATION_BITS); + } else { + char sizebuf [4]; + char *data; + guint len; + if (rsrc->data) { + data = mono_array_addr (rsrc->data, char, 0); + len = mono_array_length (rsrc->data); + } else { + data = NULL; + len = 0; + } + offset = len; + sizebuf [0] = offset; sizebuf [1] = offset >> 8; + sizebuf [2] = offset >> 16; sizebuf [3] = offset >> 24; + rsrc->offset = mono_image_add_stream_data (&assembly->resources, sizebuf, 4); + mono_image_add_stream_data (&assembly->resources, data, len); + + if (!mb->is_main) + /* + * The entry should be emitted into the MANIFESTRESOURCE table of + * the main module, but that needs to reference the FILE table + * which isn't emitted yet. + */ + return TRUE; + else + idx = 0; + } + + return assembly_add_resource_manifest (mb, assembly, rsrc, idx, error); +} + +static gboolean +set_version_from_string (MonoString *version, guint32 *values, MonoError *error) +{ + gchar *ver, *p, *str; + guint32 i; + + error_init (error); + + values [MONO_ASSEMBLY_MAJOR_VERSION] = 0; + values [MONO_ASSEMBLY_MINOR_VERSION] = 0; + values [MONO_ASSEMBLY_REV_NUMBER] = 0; + values [MONO_ASSEMBLY_BUILD_NUMBER] = 0; + if (!version) + return TRUE; + ver = str = mono_string_to_utf8_checked (version, error); + return_val_if_nok (error, FALSE); + for (i = 0; i < 4; ++i) { + values [MONO_ASSEMBLY_MAJOR_VERSION + i] = strtol (ver, &p, 10); + switch (*p) { + case '.': + p++; + break; + case '*': + /* handle Revision and Build */ + p++; + break; + } + ver = p; + } + g_free (str); + return TRUE; +} + +static guint32 +load_public_key (MonoArray *pkey, MonoDynamicImage *assembly) { + gsize len; + guint32 token = 0; + char blob_size [6]; + char *b = blob_size; + + if (!pkey) + return token; + + len = mono_array_length (pkey); + mono_metadata_encode_value (len, b, &b); + token = mono_image_add_stream_data (&assembly->blob, blob_size, b - blob_size); + mono_image_add_stream_data (&assembly->blob, mono_array_addr (pkey, char, 0), len); + + assembly->public_key = (guint8 *)g_malloc (len); + memcpy (assembly->public_key, mono_array_addr (pkey, char, 0), len); + assembly->public_key_len = len; + + /* Special case: check for ECMA key (16 bytes) */ + if ((len == MONO_ECMA_KEY_LENGTH) && mono_is_ecma_key (mono_array_addr (pkey, char, 0), len)) { + /* In this case we must reserve 128 bytes (1024 bits) for the signature */ + assembly->strong_name_size = MONO_DEFAULT_PUBLIC_KEY_LENGTH; + } else if (len >= MONO_PUBLIC_KEY_HEADER_LENGTH + MONO_MINIMUM_PUBLIC_KEY_LENGTH) { + /* minimum key size (in 2.0) is 384 bits */ + assembly->strong_name_size = len - MONO_PUBLIC_KEY_HEADER_LENGTH; + } else { + /* FIXME - verifier */ + g_warning ("Invalid public key length: %d bits (total: %d)", (int)MONO_PUBLIC_KEY_BIT_SIZE (len), (int)len); + assembly->strong_name_size = MONO_DEFAULT_PUBLIC_KEY_LENGTH; /* to be safe */ + } + assembly->strong_name = (char *)g_malloc0 (assembly->strong_name_size); + + return token; +} + +static gboolean +mono_image_emit_manifest (MonoReflectionModuleBuilder *moduleb, MonoError *error) +{ + MonoDynamicTable *table; + MonoDynamicImage *assembly; + MonoReflectionAssemblyBuilder *assemblyb; + MonoDomain *domain; + guint32 *values; + int i; + guint32 module_index; + + error_init (error); + + assemblyb = moduleb->assemblyb; + assembly = moduleb->dynamic_image; + domain = mono_object_domain (assemblyb); + + /* Emit ASSEMBLY table */ + table = &assembly->tables [MONO_TABLE_ASSEMBLY]; + alloc_table (table, 1); + values = table->values + MONO_ASSEMBLY_SIZE; + values [MONO_ASSEMBLY_HASH_ALG] = assemblyb->algid? assemblyb->algid: ASSEMBLY_HASH_SHA1; + values [MONO_ASSEMBLY_NAME] = string_heap_insert_mstring (&assembly->sheap, assemblyb->name, error); + return_val_if_nok (error, FALSE); + if (assemblyb->culture) { + values [MONO_ASSEMBLY_CULTURE] = string_heap_insert_mstring (&assembly->sheap, assemblyb->culture, error); + return_val_if_nok (error, FALSE); + } else { + values [MONO_ASSEMBLY_CULTURE] = string_heap_insert (&assembly->sheap, ""); + } + values [MONO_ASSEMBLY_PUBLIC_KEY] = load_public_key (assemblyb->public_key, assembly); + values [MONO_ASSEMBLY_FLAGS] = assemblyb->flags; + if (!set_version_from_string (assemblyb->version, values, error)) + return FALSE; + + /* Emit FILE + EXPORTED_TYPE table */ + module_index = 0; + for (i = 0; i < mono_array_length (assemblyb->modules); ++i) { + int j; + MonoReflectionModuleBuilder *file_module = + mono_array_get (assemblyb->modules, MonoReflectionModuleBuilder*, i); + if (file_module != moduleb) { + if (!mono_image_fill_file_table (domain, (MonoReflectionModule*)file_module, assembly, error)) + return FALSE; + module_index ++; + if (file_module->types) { + for (j = 0; j < file_module->num_types; ++j) { + MonoReflectionTypeBuilder *tb = mono_array_get (file_module->types, MonoReflectionTypeBuilder*, j); + mono_image_fill_export_table (domain, tb, module_index, 0, assembly, error); + return_val_if_nok (error, FALSE); + } + } + } + } + if (assemblyb->loaded_modules) { + for (i = 0; i < mono_array_length (assemblyb->loaded_modules); ++i) { + MonoReflectionModule *file_module = + mono_array_get (assemblyb->loaded_modules, MonoReflectionModule*, i); + if (!mono_image_fill_file_table (domain, file_module, assembly, error)) + return FALSE; + module_index ++; + mono_image_fill_export_table_from_module (domain, file_module, module_index, assembly); + } + } + if (assemblyb->type_forwarders) + mono_image_fill_export_table_from_type_forwarders (assemblyb, assembly); + + /* Emit MANIFESTRESOURCE table */ + module_index = 0; + for (i = 0; i < mono_array_length (assemblyb->modules); ++i) { + int j; + MonoReflectionModuleBuilder *file_module = + mono_array_get (assemblyb->modules, MonoReflectionModuleBuilder*, i); + /* The table for the main module is emitted later */ + if (file_module != moduleb) { + module_index ++; + if (file_module->resources) { + int len = mono_array_length (file_module->resources); + for (j = 0; j < len; ++j) { + MonoReflectionResource* res = (MonoReflectionResource*)mono_array_addr (file_module->resources, MonoReflectionResource, j); + if (!assembly_add_resource_manifest (file_module, assembly, res, MONO_IMPLEMENTATION_FILE | (module_index << MONO_IMPLEMENTATION_BITS), error)) + return FALSE; + } + } + } + } + return TRUE; +} + +#ifndef DISABLE_REFLECTION_EMIT_SAVE + +/* + * Insert into the metadata tables all the info about the TypeBuilder tb. + * Data in the tables is inserted in a predefined order, since some tables need to be sorted. + */ +static gboolean +mono_image_get_type_info (MonoDomain *domain, MonoReflectionTypeBuilder *tb, MonoDynamicImage *assembly, MonoError *error) +{ + MonoDynamicTable *table; + guint *values; + int i, is_object = 0, is_system = 0; + char *n; + + error_init (error); + + table = &assembly->tables [MONO_TABLE_TYPEDEF]; + values = table->values + tb->table_idx * MONO_TYPEDEF_SIZE; + values [MONO_TYPEDEF_FLAGS] = tb->attrs; + n = mono_string_to_utf8_checked (tb->name, error); + return_val_if_nok (error, FALSE); + if (strcmp (n, "Object") == 0) + is_object++; + values [MONO_TYPEDEF_NAME] = string_heap_insert (&assembly->sheap, n); + g_free (n); + n = mono_string_to_utf8_checked (tb->nspace, error); + return_val_if_nok (error, FALSE); + if (strcmp (n, "System") == 0) + is_system++; + values [MONO_TYPEDEF_NAMESPACE] = string_heap_insert (&assembly->sheap, n); + g_free (n); + if (tb->parent && !(is_system && is_object) && + !(tb->attrs & TYPE_ATTRIBUTE_INTERFACE)) { /* interfaces don't have a parent */ + MonoType *parent_type = mono_reflection_type_get_handle ((MonoReflectionType*)tb->parent, error); + return_val_if_nok (error, FALSE); + values [MONO_TYPEDEF_EXTENDS] = mono_image_typedef_or_ref (assembly, parent_type); + } else { + values [MONO_TYPEDEF_EXTENDS] = 0; + } + values [MONO_TYPEDEF_FIELD_LIST] = assembly->tables [MONO_TABLE_FIELD].next_idx; + values [MONO_TYPEDEF_METHOD_LIST] = assembly->tables [MONO_TABLE_METHOD].next_idx; + + /* + * if we have explicitlayout or sequentiallayouts, output data in the + * ClassLayout table. + */ + if (((tb->attrs & TYPE_ATTRIBUTE_LAYOUT_MASK) != TYPE_ATTRIBUTE_AUTO_LAYOUT) && + ((tb->class_size > 0) || (tb->packing_size > 0))) { + table = &assembly->tables [MONO_TABLE_CLASSLAYOUT]; + table->rows++; + alloc_table (table, table->rows); + values = table->values + table->rows * MONO_CLASS_LAYOUT_SIZE; + values [MONO_CLASS_LAYOUT_PARENT] = tb->table_idx; + values [MONO_CLASS_LAYOUT_CLASS_SIZE] = tb->class_size; + values [MONO_CLASS_LAYOUT_PACKING_SIZE] = tb->packing_size; + } + + /* handle interfaces */ + if (tb->interfaces) { + table = &assembly->tables [MONO_TABLE_INTERFACEIMPL]; + i = table->rows; + table->rows += mono_array_length (tb->interfaces); + alloc_table (table, table->rows); + values = table->values + (i + 1) * MONO_INTERFACEIMPL_SIZE; + for (i = 0; i < mono_array_length (tb->interfaces); ++i) { + MonoReflectionType* iface = (MonoReflectionType*) mono_array_get (tb->interfaces, gpointer, i); + MonoType *iface_type = mono_reflection_type_get_handle (iface, error); + return_val_if_nok (error, FALSE); + values [MONO_INTERFACEIMPL_CLASS] = tb->table_idx; + values [MONO_INTERFACEIMPL_INTERFACE] = mono_image_typedef_or_ref (assembly, iface_type); + values += MONO_INTERFACEIMPL_SIZE; + } + } + + /* handle fields */ + if (tb->fields) { + table = &assembly->tables [MONO_TABLE_FIELD]; + table->rows += tb->num_fields; + alloc_table (table, table->rows); + for (i = 0; i < tb->num_fields; ++i) { + mono_image_get_field_info ( + mono_array_get (tb->fields, MonoReflectionFieldBuilder*, i), assembly, error); + return_val_if_nok (error, FALSE); + } + } + + /* handle constructors */ + if (tb->ctors) { + table = &assembly->tables [MONO_TABLE_METHOD]; + table->rows += mono_array_length (tb->ctors); + alloc_table (table, table->rows); + for (i = 0; i < mono_array_length (tb->ctors); ++i) { + if (!mono_image_get_ctor_info (domain, + mono_array_get (tb->ctors, MonoReflectionCtorBuilder*, i), + assembly, error)) + return FALSE; + } + } + + /* handle methods */ + if (tb->methods) { + table = &assembly->tables [MONO_TABLE_METHOD]; + table->rows += tb->num_methods; + alloc_table (table, table->rows); + for (i = 0; i < tb->num_methods; ++i) { + if (!mono_image_get_method_info ( + mono_array_get (tb->methods, MonoReflectionMethodBuilder*, i), assembly, error)) + return FALSE; + } + } + + /* Do the same with properties etc.. */ + if (tb->events && mono_array_length (tb->events)) { + table = &assembly->tables [MONO_TABLE_EVENT]; + table->rows += mono_array_length (tb->events); + alloc_table (table, table->rows); + table = &assembly->tables [MONO_TABLE_EVENTMAP]; + table->rows ++; + alloc_table (table, table->rows); + values = table->values + table->rows * MONO_EVENT_MAP_SIZE; + values [MONO_EVENT_MAP_PARENT] = tb->table_idx; + values [MONO_EVENT_MAP_EVENTLIST] = assembly->tables [MONO_TABLE_EVENT].next_idx; + for (i = 0; i < mono_array_length (tb->events); ++i) { + mono_image_get_event_info ( + mono_array_get (tb->events, MonoReflectionEventBuilder*, i), assembly, error); + return_val_if_nok (error, FALSE); + } + } + if (tb->properties && mono_array_length (tb->properties)) { + table = &assembly->tables [MONO_TABLE_PROPERTY]; + table->rows += mono_array_length (tb->properties); + alloc_table (table, table->rows); + table = &assembly->tables [MONO_TABLE_PROPERTYMAP]; + table->rows ++; + alloc_table (table, table->rows); + values = table->values + table->rows * MONO_PROPERTY_MAP_SIZE; + values [MONO_PROPERTY_MAP_PARENT] = tb->table_idx; + values [MONO_PROPERTY_MAP_PROPERTY_LIST] = assembly->tables [MONO_TABLE_PROPERTY].next_idx; + for (i = 0; i < mono_array_length (tb->properties); ++i) { + mono_image_get_property_info ( + mono_array_get (tb->properties, MonoReflectionPropertyBuilder*, i), assembly, error); + return_val_if_nok (error, FALSE); + } + } + + /* handle generic parameters */ + if (tb->generic_params) { + table = &assembly->tables [MONO_TABLE_GENERICPARAM]; + table->rows += mono_array_length (tb->generic_params); + alloc_table (table, table->rows); + for (i = 0; i < mono_array_length (tb->generic_params); ++i) { + guint32 owner = MONO_TYPEORMETHOD_TYPE | (tb->table_idx << MONO_TYPEORMETHOD_BITS); + + mono_image_get_generic_param_info ( + mono_array_get (tb->generic_params, MonoReflectionGenericParam*, i), owner, assembly); + } + } + + mono_image_add_decl_security (assembly, + mono_metadata_make_token (MONO_TABLE_TYPEDEF, tb->table_idx), tb->permissions); + + if (tb->subtypes) { + MonoDynamicTable *ntable; + + ntable = &assembly->tables [MONO_TABLE_NESTEDCLASS]; + ntable->rows += mono_array_length (tb->subtypes); + alloc_table (ntable, ntable->rows); + values = ntable->values + ntable->next_idx * MONO_NESTED_CLASS_SIZE; + + for (i = 0; i < mono_array_length (tb->subtypes); ++i) { + MonoReflectionTypeBuilder *subtype = mono_array_get (tb->subtypes, MonoReflectionTypeBuilder*, i); + + values [MONO_NESTED_CLASS_NESTED] = subtype->table_idx; + values [MONO_NESTED_CLASS_ENCLOSING] = tb->table_idx; + /*g_print ("nesting %s (%d) in %s (%d) (rows %d/%d)\n", + mono_string_to_utf8 (subtype->name), subtype->table_idx, + mono_string_to_utf8 (tb->name), tb->table_idx, + ntable->next_idx, ntable->rows);*/ + values += MONO_NESTED_CLASS_SIZE; + ntable->next_idx++; + } + } + + return TRUE; +} + + +/* + * mono_image_build_metadata() will fill the info in all the needed metadata tables + * for the modulebuilder @moduleb. + * At the end of the process, method and field tokens are fixed up and the + * on-disk compressed metadata representation is created. + * Return TRUE on success, or FALSE on failure and sets @error + */ +gboolean +mono_image_build_metadata (MonoReflectionModuleBuilder *moduleb, MonoError *error) +{ + MonoDynamicTable *table; + MonoDynamicImage *assembly; + MonoReflectionAssemblyBuilder *assemblyb; + MonoDomain *domain; + MonoPtrArray types; + guint32 *values; + int i, j; + + error_init (error); + + assemblyb = moduleb->assemblyb; + assembly = moduleb->dynamic_image; + domain = mono_object_domain (assemblyb); + + if (assembly->text_rva) + return TRUE; + + assembly->text_rva = START_TEXT_RVA; + + if (moduleb->is_main) { + mono_image_emit_manifest (moduleb, error); + return_val_if_nok (error, FALSE); + } + + table = &assembly->tables [MONO_TABLE_TYPEDEF]; + table->rows = 1; /* . */ + table->next_idx++; + alloc_table (table, table->rows); + /* + * Set the first entry. + */ + values = table->values + table->columns; + values [MONO_TYPEDEF_FLAGS] = 0; + values [MONO_TYPEDEF_NAME] = string_heap_insert (&assembly->sheap, "") ; + values [MONO_TYPEDEF_NAMESPACE] = string_heap_insert (&assembly->sheap, "") ; + values [MONO_TYPEDEF_EXTENDS] = 0; + values [MONO_TYPEDEF_FIELD_LIST] = 1; + values [MONO_TYPEDEF_METHOD_LIST] = 1; + + /* + * handle global methods + * FIXME: test what to do when global methods are defined in multiple modules. + */ + if (moduleb->global_methods) { + table = &assembly->tables [MONO_TABLE_METHOD]; + table->rows += mono_array_length (moduleb->global_methods); + alloc_table (table, table->rows); + for (i = 0; i < mono_array_length (moduleb->global_methods); ++i) { + if (!mono_image_get_method_info ( + mono_array_get (moduleb->global_methods, MonoReflectionMethodBuilder*, i), assembly, error)) + goto leave; + } + } + if (moduleb->global_fields) { + table = &assembly->tables [MONO_TABLE_FIELD]; + table->rows += mono_array_length (moduleb->global_fields); + alloc_table (table, table->rows); + for (i = 0; i < mono_array_length (moduleb->global_fields); ++i) { + mono_image_get_field_info ( + mono_array_get (moduleb->global_fields, MonoReflectionFieldBuilder*, i), assembly, + error); + goto_if_nok (error, leave); + } + } + + table = &assembly->tables [MONO_TABLE_MODULE]; + alloc_table (table, 1); + mono_image_fill_module_table (domain, moduleb, assembly, error); + goto_if_nok (error, leave); + + /* Collect all types into a list sorted by their table_idx */ + mono_ptr_array_init (types, moduleb->num_types, MONO_ROOT_SOURCE_REFLECTION, NULL, "Reflection Dynamic Image Type List"); + + if (moduleb->types) + for (i = 0; i < moduleb->num_types; ++i) { + MonoReflectionTypeBuilder *type = mono_array_get (moduleb->types, MonoReflectionTypeBuilder*, i); + collect_types (&types, type); + } + + mono_ptr_array_sort (types, (int (*)(const void *, const void *))compare_types_by_table_idx); + table = &assembly->tables [MONO_TABLE_TYPEDEF]; + table->rows += mono_ptr_array_size (types); + alloc_table (table, table->rows); + + /* + * Emit type names + namespaces at one place inside the string heap, + * so load_class_names () needs to touch fewer pages. + */ + for (i = 0; i < mono_ptr_array_size (types); ++i) { + MonoReflectionTypeBuilder *tb = (MonoReflectionTypeBuilder *)mono_ptr_array_get (types, i); + string_heap_insert_mstring (&assembly->sheap, tb->nspace, error); + goto_if_nok (error, leave_types); + } + for (i = 0; i < mono_ptr_array_size (types); ++i) { + MonoReflectionTypeBuilder *tb = (MonoReflectionTypeBuilder *)mono_ptr_array_get (types, i); + string_heap_insert_mstring (&assembly->sheap, tb->name, error); + goto_if_nok (error, leave_types); + } + + for (i = 0; i < mono_ptr_array_size (types); ++i) { + MonoReflectionTypeBuilder *type = (MonoReflectionTypeBuilder *)mono_ptr_array_get (types, i); + if (!mono_image_get_type_info (domain, type, assembly, error)) + goto leave_types; + } + + /* + * table->rows is already set above and in mono_image_fill_module_table. + */ + /* add all the custom attributes at the end, once all the indexes are stable */ + if (!mono_image_add_cattrs (assembly, 1, MONO_CUSTOM_ATTR_ASSEMBLY, assemblyb->cattrs, error)) + goto leave_types; + + /* CAS assembly permissions */ + if (assemblyb->permissions_minimum) + mono_image_add_decl_security (assembly, mono_metadata_make_token (MONO_TABLE_ASSEMBLY, 1), assemblyb->permissions_minimum); + if (assemblyb->permissions_optional) + mono_image_add_decl_security (assembly, mono_metadata_make_token (MONO_TABLE_ASSEMBLY, 1), assemblyb->permissions_optional); + if (assemblyb->permissions_refused) + mono_image_add_decl_security (assembly, mono_metadata_make_token (MONO_TABLE_ASSEMBLY, 1), assemblyb->permissions_refused); + + if (!module_add_cattrs (assembly, moduleb, error)) + goto leave_types; + + /* fixup tokens */ + mono_g_hash_table_foreach (assembly->token_fixups, (GHFunc)fixup_method, assembly); + + /* Create the MethodImpl table. We do this after emitting all methods so we already know + * the final tokens and don't need another fixup pass. */ + + if (moduleb->global_methods) { + for (i = 0; i < mono_array_length (moduleb->global_methods); ++i) { + MonoReflectionMethodBuilder *mb = mono_array_get ( + moduleb->global_methods, MonoReflectionMethodBuilder*, i); + if (!mono_image_add_methodimpl (assembly, mb, error)) + goto leave_types; + } + } + + for (i = 0; i < mono_ptr_array_size (types); ++i) { + MonoReflectionTypeBuilder *type = (MonoReflectionTypeBuilder *)mono_ptr_array_get (types, i); + if (type->methods) { + for (j = 0; j < type->num_methods; ++j) { + MonoReflectionMethodBuilder *mb = mono_array_get ( + type->methods, MonoReflectionMethodBuilder*, j); + + if (!mono_image_add_methodimpl (assembly, mb, error)) + goto leave_types; + } + } + } + + fixup_cattrs (assembly); + +leave_types: + mono_ptr_array_destroy (types); +leave: + + return mono_error_ok (error); +} + +#else /* DISABLE_REFLECTION_EMIT_SAVE */ + +gboolean +mono_image_build_metadata (MonoReflectionModuleBuilder *moduleb, MonoError *error) +{ + g_error ("This mono runtime was configured with --enable-minimal=reflection_emit_save, so saving of dynamic assemblies is not supported."); +} + +#endif /* DISABLE_REFLECTION_EMIT_SAVE */ + +#ifndef DISABLE_REFLECTION_EMIT_SAVE + +static int +calc_section_size (MonoDynamicImage *assembly) +{ + int nsections = 0; + + /* alignment constraints */ + mono_image_add_stream_zero (&assembly->code, 4 - (assembly->code.index % 4)); + g_assert ((assembly->code.index % 4) == 0); + assembly->meta_size += 3; + assembly->meta_size &= ~3; + mono_image_add_stream_zero (&assembly->resources, 4 - (assembly->resources.index % 4)); + g_assert ((assembly->resources.index % 4) == 0); + + assembly->sections [MONO_SECTION_TEXT].size = assembly->meta_size + assembly->code.index + assembly->resources.index + assembly->strong_name_size; + assembly->sections [MONO_SECTION_TEXT].attrs = SECT_FLAGS_HAS_CODE | SECT_FLAGS_MEM_EXECUTE | SECT_FLAGS_MEM_READ; + nsections++; + + if (assembly->win32_res) { + guint32 res_size = (assembly->win32_res_size + 3) & ~3; + + assembly->sections [MONO_SECTION_RSRC].size = res_size; + assembly->sections [MONO_SECTION_RSRC].attrs = SECT_FLAGS_HAS_INITIALIZED_DATA | SECT_FLAGS_MEM_READ; + nsections++; + } + + assembly->sections [MONO_SECTION_RELOC].size = 12; + assembly->sections [MONO_SECTION_RELOC].attrs = SECT_FLAGS_MEM_READ | SECT_FLAGS_MEM_DISCARDABLE | SECT_FLAGS_HAS_INITIALIZED_DATA; + nsections++; + + return nsections; +} + +typedef struct { + guint32 id; + guint32 offset; + GSList *children; + MonoReflectionWin32Resource *win32_res; /* Only for leaf nodes */ +} ResTreeNode; + +static int +resource_tree_compare_by_id (gconstpointer a, gconstpointer b) +{ + ResTreeNode *t1 = (ResTreeNode*)a; + ResTreeNode *t2 = (ResTreeNode*)b; + + return t1->id - t2->id; +} + +/* + * resource_tree_create: + * + * Organize the resources into a resource tree. + */ +static ResTreeNode * +resource_tree_create (MonoArray *win32_resources) +{ + ResTreeNode *tree, *res_node, *type_node, *lang_node; + GSList *l; + int i; + + tree = g_new0 (ResTreeNode, 1); + + for (i = 0; i < mono_array_length (win32_resources); ++i) { + MonoReflectionWin32Resource *win32_res = + (MonoReflectionWin32Resource*)mono_array_addr (win32_resources, MonoReflectionWin32Resource, i); + + /* Create node */ + + /* FIXME: BUG: this stores managed references in unmanaged memory */ + lang_node = g_new0 (ResTreeNode, 1); + lang_node->id = win32_res->lang_id; + lang_node->win32_res = win32_res; + + /* Create type node if neccesary */ + type_node = NULL; + for (l = tree->children; l; l = l->next) + if (((ResTreeNode*)(l->data))->id == win32_res->res_type) { + type_node = (ResTreeNode*)l->data; + break; + } + + if (!type_node) { + type_node = g_new0 (ResTreeNode, 1); + type_node->id = win32_res->res_type; + + /* + * The resource types have to be sorted otherwise + * Windows Explorer can't display the version information. + */ + tree->children = g_slist_insert_sorted (tree->children, + type_node, resource_tree_compare_by_id); + } + + /* Create res node if neccesary */ + res_node = NULL; + for (l = type_node->children; l; l = l->next) + if (((ResTreeNode*)(l->data))->id == win32_res->res_id) { + res_node = (ResTreeNode*)l->data; + break; + } + + if (!res_node) { + res_node = g_new0 (ResTreeNode, 1); + res_node->id = win32_res->res_id; + type_node->children = g_slist_append (type_node->children, res_node); + } + + res_node->children = g_slist_append (res_node->children, lang_node); + } + + return tree; +} + +/* + * resource_tree_encode: + * + * Encode the resource tree into the format used in the PE file. + */ +static void +resource_tree_encode (ResTreeNode *node, char *begin, char *p, char **endbuf) +{ + char *entries; + MonoPEResourceDir dir; + MonoPEResourceDirEntry dir_entry; + MonoPEResourceDataEntry data_entry; + GSList *l; + guint32 res_id_entries; + + /* + * For the format of the resource directory, see the article + * "An In-Depth Look into the Win32 Portable Executable File Format" by + * Matt Pietrek + */ + + memset (&dir, 0, sizeof (dir)); + memset (&dir_entry, 0, sizeof (dir_entry)); + memset (&data_entry, 0, sizeof (data_entry)); + + g_assert (sizeof (dir) == 16); + g_assert (sizeof (dir_entry) == 8); + g_assert (sizeof (data_entry) == 16); + + node->offset = p - begin; + + /* IMAGE_RESOURCE_DIRECTORY */ + res_id_entries = g_slist_length (node->children); + dir.res_id_entries = GUINT16_TO_LE (res_id_entries); + + memcpy (p, &dir, sizeof (dir)); + p += sizeof (dir); + + /* Reserve space for entries */ + entries = p; + p += sizeof (dir_entry) * res_id_entries; + + /* Write children */ + for (l = node->children; l; l = l->next) { + ResTreeNode *child = (ResTreeNode*)l->data; + + if (child->win32_res) { + guint32 size; + + child->offset = p - begin; + + /* IMAGE_RESOURCE_DATA_ENTRY */ + data_entry.rde_data_offset = GUINT32_TO_LE (p - begin + sizeof (data_entry)); + size = mono_array_length (child->win32_res->res_data); + data_entry.rde_size = GUINT32_TO_LE (size); + + memcpy (p, &data_entry, sizeof (data_entry)); + p += sizeof (data_entry); + + memcpy (p, mono_array_addr (child->win32_res->res_data, char, 0), size); + p += size; + } else { + resource_tree_encode (child, begin, p, &p); + } + } + + /* IMAGE_RESOURCE_ENTRY */ + for (l = node->children; l; l = l->next) { + ResTreeNode *child = (ResTreeNode*)l->data; + + MONO_PE_RES_DIR_ENTRY_SET_NAME (dir_entry, FALSE, child->id); + MONO_PE_RES_DIR_ENTRY_SET_DIR (dir_entry, !child->win32_res, child->offset); + + memcpy (entries, &dir_entry, sizeof (dir_entry)); + entries += sizeof (dir_entry); + } + + *endbuf = p; +} + +static void +resource_tree_free (ResTreeNode * node) +{ + GSList * list; + for (list = node->children; list; list = list->next) + resource_tree_free ((ResTreeNode*)list->data); + g_slist_free(node->children); + g_free (node); +} + +static void +assembly_add_win32_resources (MonoDynamicImage *assembly, MonoReflectionAssemblyBuilder *assemblyb) +{ + char *buf; + char *p; + guint32 size, i; + MonoReflectionWin32Resource *win32_res; + ResTreeNode *tree; + + if (!assemblyb->win32_resources) + return; + + /* + * Resources are stored in a three level tree inside the PE file. + * - level one contains a node for each type of resource + * - level two contains a node for each resource + * - level three contains a node for each instance of a resource for a + * specific language. + */ + + tree = resource_tree_create (assemblyb->win32_resources); + + /* Estimate the size of the encoded tree */ + size = 0; + for (i = 0; i < mono_array_length (assemblyb->win32_resources); ++i) { + win32_res = (MonoReflectionWin32Resource*)mono_array_addr (assemblyb->win32_resources, MonoReflectionWin32Resource, i); + size += mono_array_length (win32_res->res_data); + } + /* Directory structure */ + size += mono_array_length (assemblyb->win32_resources) * 256; + p = buf = (char *)g_malloc (size); + + resource_tree_encode (tree, p, p, &p); + + g_assert (p - buf <= size); + + assembly->win32_res = (char *)g_malloc (p - buf); + assembly->win32_res_size = p - buf; + memcpy (assembly->win32_res, buf, p - buf); + + g_free (buf); + resource_tree_free (tree); +} + +static void +fixup_resource_directory (char *res_section, char *p, guint32 rva) +{ + MonoPEResourceDir *dir = (MonoPEResourceDir*)p; + int i; + + p += sizeof (MonoPEResourceDir); + for (i = 0; i < GUINT16_FROM_LE (dir->res_named_entries) + GUINT16_FROM_LE (dir->res_id_entries); ++i) { + MonoPEResourceDirEntry *dir_entry = (MonoPEResourceDirEntry*)p; + char *child = res_section + MONO_PE_RES_DIR_ENTRY_DIR_OFFSET (*dir_entry); + if (MONO_PE_RES_DIR_ENTRY_IS_DIR (*dir_entry)) { + fixup_resource_directory (res_section, child, rva); + } else { + MonoPEResourceDataEntry *data_entry = (MonoPEResourceDataEntry*)child; + data_entry->rde_data_offset = GUINT32_TO_LE (GUINT32_FROM_LE (data_entry->rde_data_offset) + rva); + } + + p += sizeof (MonoPEResourceDirEntry); + } +} + +static void +checked_write_file (HANDLE f, gconstpointer buffer, guint32 numbytes) +{ + guint32 dummy; + if (!mono_w32file_write (f, buffer, numbytes, &dummy)) + g_error ("mono_w32file_write returned %d\n", mono_w32error_get_last ()); +} + +/* + * mono_image_create_pefile: + * @mb: a module builder object + * + * This function creates the PE-COFF header, the image sections, the CLI header * etc. all the data is written in + * assembly->pefile where it can be easily retrieved later in chunks. + */ +gboolean +mono_image_create_pefile (MonoReflectionModuleBuilder *mb, HANDLE file, MonoError *error) +{ + MonoMSDOSHeader *msdos; + MonoDotNetHeader *header; + MonoSectionTable *section; + MonoCLIHeader *cli_header; + guint32 size, image_size, virtual_base, text_offset; + guint32 header_start, section_start, file_offset, virtual_offset; + MonoDynamicImage *assembly; + MonoReflectionAssemblyBuilder *assemblyb; + MonoDynamicStream pefile_stream = {0}; + MonoDynamicStream *pefile = &pefile_stream; + int i, nsections; + guint32 *rva, value; + guchar *p; + static const unsigned char msheader[] = { + 0x4d, 0x5a, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd, 0x21, 0xb8, 0x01, 0x4c, 0xcd, 0x21, 0x54, 0x68, + 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, + 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20, + 0x6d, 0x6f, 0x64, 0x65, 0x2e, 0x0d, 0x0d, 0x0a, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + error_init (error); + + assemblyb = mb->assemblyb; + + mono_reflection_dynimage_basic_init (assemblyb); + assembly = mb->dynamic_image; + + assembly->pe_kind = assemblyb->pe_kind; + assembly->machine = assemblyb->machine; + ((MonoDynamicImage*)assemblyb->dynamic_assembly->assembly.image)->pe_kind = assemblyb->pe_kind; + ((MonoDynamicImage*)assemblyb->dynamic_assembly->assembly.image)->machine = assemblyb->machine; + + if (!mono_image_build_metadata (mb, error)) + return FALSE; + + + if (mb->is_main && assemblyb->resources) { + int len = mono_array_length (assemblyb->resources); + for (i = 0; i < len; ++i) { + if (!assembly_add_resource (mb, assembly, (MonoReflectionResource*)mono_array_addr (assemblyb->resources, MonoReflectionResource, i), error)) + return FALSE; + } + } + + if (mb->resources) { + int len = mono_array_length (mb->resources); + for (i = 0; i < len; ++i) { + if (!assembly_add_resource (mb, assembly, (MonoReflectionResource*)mono_array_addr (mb->resources, MonoReflectionResource, i), error)) + return FALSE; + } + } + + if (!build_compressed_metadata (assembly, error)) + return FALSE; + + if (mb->is_main) + assembly_add_win32_resources (assembly, assemblyb); + + nsections = calc_section_size (assembly); + + /* The DOS header and stub */ + g_assert (sizeof (MonoMSDOSHeader) == sizeof (msheader)); + mono_image_add_stream_data (pefile, (char*)msheader, sizeof (msheader)); + + /* the dotnet header */ + header_start = mono_image_add_stream_zero (pefile, sizeof (MonoDotNetHeader)); + + /* the section tables */ + section_start = mono_image_add_stream_zero (pefile, sizeof (MonoSectionTable) * nsections); + + file_offset = section_start + sizeof (MonoSectionTable) * nsections; + virtual_offset = VIRT_ALIGN; + image_size = 0; + + for (i = 0; i < MONO_SECTION_MAX; ++i) { + if (!assembly->sections [i].size) + continue; + /* align offsets */ + file_offset += FILE_ALIGN - 1; + file_offset &= ~(FILE_ALIGN - 1); + virtual_offset += VIRT_ALIGN - 1; + virtual_offset &= ~(VIRT_ALIGN - 1); + + assembly->sections [i].offset = file_offset; + assembly->sections [i].rva = virtual_offset; + + file_offset += assembly->sections [i].size; + virtual_offset += assembly->sections [i].size; + image_size += (assembly->sections [i].size + VIRT_ALIGN - 1) & ~(VIRT_ALIGN - 1); + } + + file_offset += FILE_ALIGN - 1; + file_offset &= ~(FILE_ALIGN - 1); + + image_size += section_start + sizeof (MonoSectionTable) * nsections; + + /* back-patch info */ + msdos = (MonoMSDOSHeader*)pefile->data; + msdos->pe_offset = GUINT32_FROM_LE (sizeof (MonoMSDOSHeader)); + + header = (MonoDotNetHeader*)(pefile->data + header_start); + header->pesig [0] = 'P'; + header->pesig [1] = 'E'; + + header->coff.coff_machine = GUINT16_FROM_LE (assemblyb->machine); + header->coff.coff_sections = GUINT16_FROM_LE (nsections); + header->coff.coff_time = GUINT32_FROM_LE (time (NULL)); + header->coff.coff_opt_header_size = GUINT16_FROM_LE (sizeof (MonoDotNetHeader) - sizeof (MonoCOFFHeader) - 4); + if (assemblyb->pekind == 1) { + /* it's a dll */ + header->coff.coff_attributes = GUINT16_FROM_LE (0x210e); + } else { + /* it's an exe */ + header->coff.coff_attributes = GUINT16_FROM_LE (0x010e); + } + + virtual_base = 0x400000; /* FIXME: 0x10000000 if a DLL */ + + header->pe.pe_magic = GUINT16_FROM_LE (0x10B); + header->pe.pe_major = 6; + header->pe.pe_minor = 0; + size = assembly->sections [MONO_SECTION_TEXT].size; + size += FILE_ALIGN - 1; + size &= ~(FILE_ALIGN - 1); + header->pe.pe_code_size = GUINT32_FROM_LE(size); + size = assembly->sections [MONO_SECTION_RSRC].size; + size += FILE_ALIGN - 1; + size &= ~(FILE_ALIGN - 1); + header->pe.pe_data_size = GUINT32_FROM_LE(size); + g_assert (START_TEXT_RVA == assembly->sections [MONO_SECTION_TEXT].rva); + header->pe.pe_rva_code_base = GUINT32_FROM_LE (assembly->sections [MONO_SECTION_TEXT].rva); + header->pe.pe_rva_data_base = GUINT32_FROM_LE (assembly->sections [MONO_SECTION_RSRC].rva); + /* pe_rva_entry_point always at the beginning of the text section */ + header->pe.pe_rva_entry_point = GUINT32_FROM_LE (assembly->sections [MONO_SECTION_TEXT].rva); + + header->nt.pe_image_base = GUINT32_FROM_LE (virtual_base); + header->nt.pe_section_align = GUINT32_FROM_LE (VIRT_ALIGN); + header->nt.pe_file_alignment = GUINT32_FROM_LE (FILE_ALIGN); + header->nt.pe_os_major = GUINT16_FROM_LE (4); + header->nt.pe_os_minor = GUINT16_FROM_LE (0); + header->nt.pe_subsys_major = GUINT16_FROM_LE (4); + size = section_start; + size += FILE_ALIGN - 1; + size &= ~(FILE_ALIGN - 1); + header->nt.pe_header_size = GUINT32_FROM_LE (size); + size = image_size; + size += VIRT_ALIGN - 1; + size &= ~(VIRT_ALIGN - 1); + header->nt.pe_image_size = GUINT32_FROM_LE (size); + + /* + // Translate the PEFileKind value to the value expected by the Windows loader + */ + { + short kind; + + /* + // PEFileKinds.Dll == 1 + // PEFileKinds.ConsoleApplication == 2 + // PEFileKinds.WindowApplication == 3 + // + // need to get: + // IMAGE_SUBSYSTEM_WINDOWS_GUI 2 // Image runs in the Windows GUI subsystem. + // IMAGE_SUBSYSTEM_WINDOWS_CUI 3 // Image runs in the Windows character subsystem. + */ + if (assemblyb->pekind == 3) + kind = 2; + else + kind = 3; + + header->nt.pe_subsys_required = GUINT16_FROM_LE (kind); + } + header->nt.pe_stack_reserve = GUINT32_FROM_LE (0x00100000); + header->nt.pe_stack_commit = GUINT32_FROM_LE (0x00001000); + header->nt.pe_heap_reserve = GUINT32_FROM_LE (0x00100000); + header->nt.pe_heap_commit = GUINT32_FROM_LE (0x00001000); + header->nt.pe_loader_flags = GUINT32_FROM_LE (0); + header->nt.pe_data_dir_count = GUINT32_FROM_LE (16); + + /* fill data directory entries */ + + header->datadir.pe_resource_table.size = GUINT32_FROM_LE (assembly->sections [MONO_SECTION_RSRC].size); + header->datadir.pe_resource_table.rva = GUINT32_FROM_LE (assembly->sections [MONO_SECTION_RSRC].rva); + + header->datadir.pe_reloc_table.size = GUINT32_FROM_LE (assembly->sections [MONO_SECTION_RELOC].size); + header->datadir.pe_reloc_table.rva = GUINT32_FROM_LE (assembly->sections [MONO_SECTION_RELOC].rva); + + header->datadir.pe_cli_header.size = GUINT32_FROM_LE (72); + header->datadir.pe_cli_header.rva = GUINT32_FROM_LE (assembly->text_rva + assembly->cli_header_offset); + header->datadir.pe_iat.size = GUINT32_FROM_LE (8); + header->datadir.pe_iat.rva = GUINT32_FROM_LE (assembly->text_rva + assembly->iat_offset); + /* patch entrypoint name */ + if (assemblyb->pekind == 1) + memcpy (assembly->code.data + assembly->imp_names_offset + 2, "_CorDllMain", 12); + else + memcpy (assembly->code.data + assembly->imp_names_offset + 2, "_CorExeMain", 12); + /* patch imported function RVA name */ + rva = (guint32*)(assembly->code.data + assembly->iat_offset); + *rva = GUINT32_FROM_LE (assembly->text_rva + assembly->imp_names_offset); + + /* the import table */ + header->datadir.pe_import_table.size = GUINT32_FROM_LE (79); /* FIXME: magic number? */ + header->datadir.pe_import_table.rva = GUINT32_FROM_LE (assembly->text_rva + assembly->idt_offset); + /* patch imported dll RVA name and other entries in the dir */ + rva = (guint32*)(assembly->code.data + assembly->idt_offset + G_STRUCT_OFFSET (MonoIDT, name_rva)); + *rva = GUINT32_FROM_LE (assembly->text_rva + assembly->imp_names_offset + 14); /* 14 is hint+strlen+1 of func name */ + rva = (guint32*)(assembly->code.data + assembly->idt_offset + G_STRUCT_OFFSET (MonoIDT, import_address_table_rva)); + *rva = GUINT32_FROM_LE (assembly->text_rva + assembly->iat_offset); + rva = (guint32*)(assembly->code.data + assembly->idt_offset + G_STRUCT_OFFSET (MonoIDT, import_lookup_table)); + *rva = GUINT32_FROM_LE (assembly->text_rva + assembly->ilt_offset); + + p = (guchar*)(assembly->code.data + assembly->ilt_offset); + value = (assembly->text_rva + assembly->imp_names_offset); + *p++ = (value) & 0xff; + *p++ = (value >> 8) & (0xff); + *p++ = (value >> 16) & (0xff); + *p++ = (value >> 24) & (0xff); + + /* the CLI header info */ + cli_header = (MonoCLIHeader*)(assembly->code.data + assembly->cli_header_offset); + cli_header->ch_size = GUINT32_FROM_LE (72); + cli_header->ch_runtime_major = GUINT16_FROM_LE (2); + cli_header->ch_runtime_minor = GUINT16_FROM_LE (5); + cli_header->ch_flags = GUINT32_FROM_LE (assemblyb->pe_kind); + if (assemblyb->entry_point) { + guint32 table_idx = 0; + if (!strcmp (assemblyb->entry_point->object.vtable->klass->name, "MethodBuilder")) { + MonoReflectionMethodBuilder *methodb = (MonoReflectionMethodBuilder*)assemblyb->entry_point; + table_idx = methodb->table_idx; + } else { + table_idx = GPOINTER_TO_UINT (g_hash_table_lookup (assembly->method_to_table_idx, assemblyb->entry_point->method)); + } + cli_header->ch_entry_point = GUINT32_FROM_LE (table_idx | MONO_TOKEN_METHOD_DEF); + } else { + cli_header->ch_entry_point = GUINT32_FROM_LE (0); + } + /* The embedded managed resources */ + text_offset = assembly->text_rva + assembly->code.index; + cli_header->ch_resources.rva = GUINT32_FROM_LE (text_offset); + cli_header->ch_resources.size = GUINT32_FROM_LE (assembly->resources.index); + text_offset += assembly->resources.index; + cli_header->ch_metadata.rva = GUINT32_FROM_LE (text_offset); + cli_header->ch_metadata.size = GUINT32_FROM_LE (assembly->meta_size); + text_offset += assembly->meta_size; + if (assembly->strong_name_size) { + cli_header->ch_strong_name.rva = GUINT32_FROM_LE (text_offset); + cli_header->ch_strong_name.size = GUINT32_FROM_LE (assembly->strong_name_size); + text_offset += assembly->strong_name_size; + } + + /* write the section tables and section content */ + section = (MonoSectionTable*)(pefile->data + section_start); + for (i = 0; i < MONO_SECTION_MAX; ++i) { + static const char section_names [][7] = { + ".text", ".rsrc", ".reloc" + }; + if (!assembly->sections [i].size) + continue; + strcpy (section->st_name, section_names [i]); + /*g_print ("output section %s (%d), size: %d\n", section->st_name, i, assembly->sections [i].size);*/ + section->st_virtual_address = GUINT32_FROM_LE (assembly->sections [i].rva); + section->st_virtual_size = GUINT32_FROM_LE (assembly->sections [i].size); + section->st_raw_data_size = GUINT32_FROM_LE (GUINT32_TO_LE (section->st_virtual_size) + (FILE_ALIGN - 1)); + section->st_raw_data_size &= GUINT32_FROM_LE (~(FILE_ALIGN - 1)); + section->st_raw_data_ptr = GUINT32_FROM_LE (assembly->sections [i].offset); + section->st_flags = GUINT32_FROM_LE (assembly->sections [i].attrs); + section ++; + } + + checked_write_file (file, pefile->data, pefile->index); + + mono_dynamic_stream_reset (pefile); + + for (i = 0; i < MONO_SECTION_MAX; ++i) { + if (!assembly->sections [i].size) + continue; + + if (mono_w32file_seek (file, assembly->sections [i].offset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) + g_error ("mono_w32file_seek returned %d\n", mono_w32error_get_last ()); + + switch (i) { + case MONO_SECTION_TEXT: + /* patch entry point */ + p = (guchar*)(assembly->code.data + 2); + value = (virtual_base + assembly->text_rva + assembly->iat_offset); + *p++ = (value) & 0xff; + *p++ = (value >> 8) & 0xff; + *p++ = (value >> 16) & 0xff; + *p++ = (value >> 24) & 0xff; + + checked_write_file (file, assembly->code.data, assembly->code.index); + checked_write_file (file, assembly->resources.data, assembly->resources.index); + checked_write_file (file, assembly->image.raw_metadata, assembly->meta_size); + checked_write_file (file, assembly->strong_name, assembly->strong_name_size); + + + g_free (assembly->image.raw_metadata); + break; + case MONO_SECTION_RELOC: { + struct { + guint32 page_rva; + guint32 block_size; + guint16 type_and_offset; + guint16 term; + } reloc; + + g_assert (sizeof (reloc) == 12); + + reloc.page_rva = GUINT32_FROM_LE (assembly->text_rva); + reloc.block_size = GUINT32_FROM_LE (12); + + /* + * the entrypoint is always at the start of the text section + * 3 is IMAGE_REL_BASED_HIGHLOW + * 2 is patch_size_rva - text_rva + */ + reloc.type_and_offset = GUINT16_FROM_LE ((3 << 12) + (2)); + reloc.term = 0; + + checked_write_file (file, &reloc, sizeof (reloc)); + + break; + } + case MONO_SECTION_RSRC: + if (assembly->win32_res) { + + /* Fixup the offsets in the IMAGE_RESOURCE_DATA_ENTRY structures */ + fixup_resource_directory (assembly->win32_res, assembly->win32_res, assembly->sections [i].rva); + checked_write_file (file, assembly->win32_res, assembly->win32_res_size); + } + break; + default: + g_assert_not_reached (); + } + } + + /* check that the file is properly padded */ + if (mono_w32file_seek (file, file_offset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) + g_error ("mono_w32file_seek returned %d\n", mono_w32error_get_last ()); + if (! mono_w32file_truncate (file)) + g_error ("mono_w32file_truncate returned %d\n", mono_w32error_get_last ()); + + mono_dynamic_stream_reset (&assembly->code); + mono_dynamic_stream_reset (&assembly->us); + mono_dynamic_stream_reset (&assembly->blob); + mono_dynamic_stream_reset (&assembly->guid); + mono_dynamic_stream_reset (&assembly->sheap); + + g_hash_table_foreach (assembly->blob_cache, (GHFunc)g_free, NULL); + g_hash_table_destroy (assembly->blob_cache); + assembly->blob_cache = NULL; + + return TRUE; +} + +#else /* DISABLE_REFLECTION_EMIT_SAVE */ + +gboolean +mono_image_create_pefile (MonoReflectionModuleBuilder *mb, HANDLE file, MonoError *error) +{ + g_assert_not_reached (); +} + +#endif /* DISABLE_REFLECTION_EMIT_SAVE */ + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/sre.c b/unity-2019.4.24f1-mbe/mono/metadata/sre.c new file mode 100644 index 000000000..076e12e8a --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/sre.c @@ -0,0 +1,4435 @@ +/** + * \file + * Routines for creating an image at runtime + * and related System.Reflection.Emit icalls + * + * + * Author: + * Paolo Molaro (lupus@ximian.com) + * + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Copyright 2011 Rodrigo Kumpera + * Copyright 2016 Microsoft + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#include +#include +#include "mono/metadata/assembly.h" +#include "mono/metadata/debug-helpers.h" +#include "mono/metadata/dynamic-image-internals.h" +#include "mono/metadata/dynamic-stream-internals.h" +#include "mono/metadata/exception.h" +#include "mono/metadata/gc-internals.h" +#include "mono/metadata/mono-ptr-array.h" +#include "mono/metadata/object-internals.h" +#include "mono/metadata/profiler-private.h" +#include "mono/metadata/reflection-internals.h" +#include "mono/metadata/reflection-cache.h" +#include "mono/metadata/sre-internals.h" +#include "mono/metadata/custom-attrs-internals.h" +#include "mono/metadata/security-manager.h" +#include "mono/metadata/security-core-clr.h" +#include "mono/metadata/tabledefs.h" +#include "mono/metadata/tokentype.h" +#include "mono/utils/checked-build.h" +#include "mono/utils/mono-digest.h" +#include "mono/utils/w32api.h" + +static GENERATE_GET_CLASS_WITH_CACHE (marshal_as_attribute, "System.Runtime.InteropServices", "MarshalAsAttribute"); +static GENERATE_GET_CLASS_WITH_CACHE (module_builder, "System.Reflection.Emit", "ModuleBuilder"); + +static char* string_to_utf8_image_raw (MonoImage *image, MonoString *s, MonoError *error); + +#ifndef DISABLE_REFLECTION_EMIT +static guint32 mono_image_get_sighelper_token (MonoDynamicImage *assembly, MonoReflectionSigHelperHandle helper, MonoError *error); +static gboolean ensure_runtime_vtable (MonoClass *klass, MonoError *error); +static void reflection_methodbuilder_from_dynamic_method (ReflectionMethodBuilder *rmb, MonoReflectionDynamicMethod *mb); +static gboolean reflection_setup_internal_class (MonoReflectionTypeBuilderHandle tb, MonoError *error); +static gboolean reflection_init_generic_class (MonoReflectionTypeBuilderHandle tb, MonoError *error); +static gboolean reflection_setup_class_hierarchy (GHashTable *unparented, MonoError *error); + + +static gpointer register_assembly (MonoDomain *domain, MonoReflectionAssembly *res, MonoAssembly *assembly); +#endif + +static char* type_get_qualified_name (MonoType *type, MonoAssembly *ass); +static MonoReflectionTypeHandle mono_reflection_type_get_underlying_system_type (MonoReflectionTypeHandle t, MonoError *error); +static gboolean is_sre_array (MonoClass *klass); +static gboolean is_sre_byref (MonoClass *klass); +static gboolean is_sre_pointer (MonoClass *klass); +static gboolean is_sre_generic_instance (MonoClass *klass); +static gboolean is_sre_type_builder (MonoClass *klass); +static gboolean is_sre_method_builder (MonoClass *klass); +static gboolean is_sre_field_builder (MonoClass *klass); +static gboolean is_sre_gparam_builder (MonoClass *klass); +static gboolean is_sre_enum_builder (MonoClass *klass); +static gboolean is_sr_mono_method (MonoClass *klass); +static gboolean is_sr_mono_field (MonoClass *klass); + +static guint32 mono_image_get_methodspec_token (MonoDynamicImage *assembly, MonoMethod *method); +static guint32 mono_image_get_inflated_method_token (MonoDynamicImage *assembly, MonoMethod *m); +static guint32 mono_image_create_method_token (MonoDynamicImage *assembly, MonoObjectHandle obj, MonoArrayHandle opt_param_types, MonoError *error); + + +#ifndef DISABLE_REFLECTION_EMIT +static MonoType* mono_type_array_get_and_resolve_raw (MonoArray* array, int idx, MonoError* error); +#endif + +static gboolean mono_image_module_basic_init (MonoReflectionModuleBuilderHandle module, MonoError *error); + +void +mono_reflection_emit_init (void) +{ + mono_dynamic_images_init (); +} + +char* +string_to_utf8_image_raw (MonoImage *image, MonoString *s_raw, MonoError *error) +{ + /* FIXME all callers to string_to_utf8_image_raw should use handles */ + HANDLE_FUNCTION_ENTER (); + char* result = NULL; + error_init (error); + MONO_HANDLE_DCL (MonoString, s); + result = mono_string_to_utf8_image (image, s, error); + HANDLE_FUNCTION_RETURN_VAL (result); +} + +static char* +type_get_fully_qualified_name (MonoType *type) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + return mono_type_get_name_full (type, MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED); +} + +static char* +type_get_qualified_name (MonoType *type, MonoAssembly *ass) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoClass *klass; + MonoAssembly *ta; + + klass = mono_class_from_mono_type (type); + if (!klass) + return mono_type_get_name_full (type, MONO_TYPE_NAME_FORMAT_REFLECTION); + ta = klass->image->assembly; + if (assembly_is_dynamic (ta) || (ta == ass)) { + if (mono_class_is_ginst (klass) || mono_class_is_gtd (klass)) + /* For generic type definitions, we want T, while REFLECTION returns T */ + return mono_type_get_name_full (type, MONO_TYPE_NAME_FORMAT_FULL_NAME); + else + return mono_type_get_name_full (type, MONO_TYPE_NAME_FORMAT_REFLECTION); + } + + return mono_type_get_name_full (type, MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED); +} + +#ifndef DISABLE_REFLECTION_EMIT +/** + * mp_g_alloc: + * + * Allocate memory from the @image mempool if it is non-NULL. Otherwise, allocate memory + * from the C heap. + */ +static gpointer +image_g_malloc (MonoImage *image, guint size) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + if (image) + return mono_image_alloc (image, size); + else + return g_malloc (size); +} +#endif /* !DISABLE_REFLECTION_EMIT */ + +/** + * image_g_alloc0: + * + * Allocate memory from the @image mempool if it is non-NULL. Otherwise, allocate memory + * from the C heap. + */ +gpointer +mono_image_g_malloc0 (MonoImage *image, guint size) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + if (image) + return mono_image_alloc0 (image, size); + else + return g_malloc0 (size); +} + +/** + * image_g_free: + * @image: a MonoImage + * @ptr: pointer + * + * If @image is NULL, free @ptr, otherwise do nothing. + */ +static void +image_g_free (MonoImage *image, gpointer ptr) +{ + if (image == NULL) + g_free (ptr); +} + +#ifndef DISABLE_REFLECTION_EMIT +static char* +image_strdup (MonoImage *image, const char *s) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + if (image) + return mono_image_strdup (image, s); + else + return g_strdup (s); +} +#endif + +#define image_g_new(image,struct_type, n_structs) \ + ((struct_type *) image_g_malloc (image, ((gsize) sizeof (struct_type)) * ((gsize) (n_structs)))) + +#define image_g_new0(image,struct_type, n_structs) \ + ((struct_type *) mono_image_g_malloc0 (image, ((gsize) sizeof (struct_type)) * ((gsize) (n_structs)))) + + +static void +alloc_table (MonoDynamicTable *table, guint nrows) +{ + mono_dynimage_alloc_table (table, nrows); +} + +static guint32 +string_heap_insert (MonoDynamicStream *sh, const char *str) +{ + return mono_dynstream_insert_string (sh, str); +} + +static guint32 +mono_image_add_stream_data (MonoDynamicStream *stream, const char *data, guint32 len) +{ + return mono_dynstream_add_data (stream, data, len); +} + +/* + * Despite the name, we handle also TypeSpec (with the above helper). + */ +static guint32 +mono_image_typedef_or_ref (MonoDynamicImage *assembly, MonoType *type) +{ + return mono_dynimage_encode_typedef_or_ref_full (assembly, type, TRUE); +} + +/* + * Copy len * nelem bytes from val to dest, swapping bytes to LE if necessary. + * dest may be misaligned. + */ +static void +swap_with_size (char *dest, const char* val, int len, int nelem) { + MONO_REQ_GC_NEUTRAL_MODE; +#if G_BYTE_ORDER != G_LITTLE_ENDIAN + int elem; + + for (elem = 0; elem < nelem; ++elem) { + switch (len) { + case 1: + *dest = *val; + break; + case 2: + dest [0] = val [1]; + dest [1] = val [0]; + break; + case 4: + dest [0] = val [3]; + dest [1] = val [2]; + dest [2] = val [1]; + dest [3] = val [0]; + break; + case 8: + dest [0] = val [7]; + dest [1] = val [6]; + dest [2] = val [5]; + dest [3] = val [4]; + dest [4] = val [3]; + dest [5] = val [2]; + dest [6] = val [1]; + dest [7] = val [0]; + break; + default: + g_assert_not_reached (); + } + dest += len; + val += len; + } +#else + memcpy (dest, val, len * nelem); +#endif +} + +guint32 +mono_reflection_method_count_clauses (MonoReflectionILGen *ilgen) +{ + MONO_REQ_GC_UNSAFE_MODE; + + guint32 num_clauses = 0; + int i; + + MonoILExceptionInfo *ex_info; + for (i = 0; i < mono_array_length (ilgen->ex_handlers); ++i) { + ex_info = (MonoILExceptionInfo*)mono_array_addr (ilgen->ex_handlers, MonoILExceptionInfo, i); + if (ex_info->handlers) + num_clauses += mono_array_length (ex_info->handlers); + else + num_clauses++; + } + + return num_clauses; +} + +#ifndef DISABLE_REFLECTION_EMIT +static MonoExceptionClause* +method_encode_clauses (MonoImage *image, MonoDynamicImage *assembly, MonoReflectionILGen *ilgen, guint32 num_clauses, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + + MonoExceptionClause *clauses; + MonoExceptionClause *clause; + MonoILExceptionInfo *ex_info; + MonoILExceptionBlock *ex_block; + guint32 finally_start; + int i, j, clause_index;; + + clauses = image_g_new0 (image, MonoExceptionClause, num_clauses); + + clause_index = 0; + for (i = mono_array_length (ilgen->ex_handlers) - 1; i >= 0; --i) { + ex_info = (MonoILExceptionInfo*)mono_array_addr (ilgen->ex_handlers, MonoILExceptionInfo, i); + finally_start = ex_info->start + ex_info->len; + if (!ex_info->handlers) + continue; + for (j = 0; j < mono_array_length (ex_info->handlers); ++j) { + ex_block = (MonoILExceptionBlock*)mono_array_addr (ex_info->handlers, MonoILExceptionBlock, j); + clause = &(clauses [clause_index]); + + clause->flags = ex_block->type; + clause->try_offset = ex_info->start; + + if (ex_block->type == MONO_EXCEPTION_CLAUSE_FINALLY) + clause->try_len = finally_start - ex_info->start; + else + clause->try_len = ex_info->len; + clause->handler_offset = ex_block->start; + clause->handler_len = ex_block->len; + if (ex_block->extype) { + MonoType *extype = mono_reflection_type_get_handle ((MonoReflectionType*)ex_block->extype, error); + + if (!is_ok (error)) { + image_g_free (image, clauses); + return NULL; + } + clause->data.catch_class = mono_class_from_mono_type (extype); + } else { + if (ex_block->type == MONO_EXCEPTION_CLAUSE_FILTER) + clause->data.filter_offset = ex_block->filter_offset; + else + clause->data.filter_offset = 0; + } + finally_start = ex_block->start + ex_block->len; + + clause_index ++; + } + } + + return clauses; +} +#endif /* !DISABLE_REFLECTION_EMIT */ + +#ifndef DISABLE_REFLECTION_EMIT +/* + * LOCKING: Acquires the loader lock. + */ +static void +mono_save_custom_attrs (MonoImage *image, void *obj, MonoArray *cattrs) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoCustomAttrInfo *ainfo, *tmp; + + if (!cattrs || !mono_array_length (cattrs)) + return; + + ainfo = mono_custom_attrs_from_builders (image, image, cattrs); + + mono_loader_lock (); + tmp = (MonoCustomAttrInfo *)mono_image_property_lookup (image, obj, MONO_PROP_DYNAMIC_CATTR); + if (tmp) + mono_custom_attrs_free (tmp); + mono_image_property_insert (image, obj, MONO_PROP_DYNAMIC_CATTR, ainfo); + mono_loader_unlock (); + +} +#else +//FIXME some code compiled under DISABLE_REFLECTION_EMIT depends on this function, we should be more aggressively disabling things +static void +mono_save_custom_attrs (MonoImage *image, void *obj, MonoArray *cattrs) +{ +} +#endif + +guint32 +mono_reflection_resolution_scope_from_image (MonoDynamicImage *assembly, MonoImage *image) +{ + MONO_REQ_GC_UNSAFE_MODE; + + MonoDynamicTable *table; + guint32 token; + guint32 *values; + guint32 cols [MONO_ASSEMBLY_SIZE]; + const char *pubkey; + guint32 publen; + + if ((token = GPOINTER_TO_UINT (g_hash_table_lookup (assembly->handleref, image)))) + return token; + + if (assembly_is_dynamic (image->assembly) && (image->assembly == assembly->image.assembly)) { + table = &assembly->tables [MONO_TABLE_MODULEREF]; + token = table->next_idx ++; + table->rows ++; + alloc_table (table, table->rows); + values = table->values + token * MONO_MODULEREF_SIZE; + values [MONO_MODULEREF_NAME] = string_heap_insert (&assembly->sheap, image->module_name); + + token <<= MONO_RESOLUTION_SCOPE_BITS; + token |= MONO_RESOLUTION_SCOPE_MODULEREF; + g_hash_table_insert (assembly->handleref, image, GUINT_TO_POINTER (token)); + + return token; + } + + if (assembly_is_dynamic (image->assembly)) + /* FIXME: */ + memset (cols, 0, sizeof (cols)); + else { + /* image->assembly->image is the manifest module */ + image = image->assembly->image; + mono_metadata_decode_row (&image->tables [MONO_TABLE_ASSEMBLY], 0, cols, MONO_ASSEMBLY_SIZE); + } + + table = &assembly->tables [MONO_TABLE_ASSEMBLYREF]; + token = table->next_idx ++; + table->rows ++; + alloc_table (table, table->rows); + values = table->values + token * MONO_ASSEMBLYREF_SIZE; + values [MONO_ASSEMBLYREF_NAME] = string_heap_insert (&assembly->sheap, image->assembly_name); + values [MONO_ASSEMBLYREF_MAJOR_VERSION] = cols [MONO_ASSEMBLY_MAJOR_VERSION]; + values [MONO_ASSEMBLYREF_MINOR_VERSION] = cols [MONO_ASSEMBLY_MINOR_VERSION]; + values [MONO_ASSEMBLYREF_BUILD_NUMBER] = cols [MONO_ASSEMBLY_BUILD_NUMBER]; + values [MONO_ASSEMBLYREF_REV_NUMBER] = cols [MONO_ASSEMBLY_REV_NUMBER]; + values [MONO_ASSEMBLYREF_FLAGS] = 0; + values [MONO_ASSEMBLYREF_CULTURE] = 0; + values [MONO_ASSEMBLYREF_HASH_VALUE] = 0; + + if (strcmp ("", image->assembly->aname.culture)) { + values [MONO_ASSEMBLYREF_CULTURE] = string_heap_insert (&assembly->sheap, + image->assembly->aname.culture); + } + + if ((pubkey = mono_image_get_public_key (image, &publen))) { + guchar pubtoken [9]; + pubtoken [0] = 8; + mono_digest_get_public_token (pubtoken + 1, (guchar*)pubkey, publen); + values [MONO_ASSEMBLYREF_PUBLIC_KEY] = mono_image_add_stream_data (&assembly->blob, (char*)pubtoken, 9); + } else { + values [MONO_ASSEMBLYREF_PUBLIC_KEY] = 0; + } + token <<= MONO_RESOLUTION_SCOPE_BITS; + token |= MONO_RESOLUTION_SCOPE_ASSEMBLYREF; + g_hash_table_insert (assembly->handleref, image, GUINT_TO_POINTER (token)); + return token; +} + +#ifndef DISABLE_REFLECTION_EMIT +gboolean +mono_reflection_methodbuilder_from_method_builder (ReflectionMethodBuilder *rmb, MonoReflectionMethodBuilder *mb, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + error_init (error); + memset (rmb, 0, sizeof (ReflectionMethodBuilder)); + + rmb->ilgen = mb->ilgen; + rmb->rtype = (MonoReflectionType*)mb->rtype; + return_val_if_nok (error, FALSE); + rmb->parameters = mb->parameters; + rmb->generic_params = mb->generic_params; + rmb->generic_container = mb->generic_container; + rmb->opt_types = NULL; + rmb->pinfo = mb->pinfo; + rmb->attrs = mb->attrs; + rmb->iattrs = mb->iattrs; + rmb->call_conv = mb->call_conv; + rmb->code = mb->code; + rmb->type = mb->type; + rmb->name = mb->name; + rmb->table_idx = &mb->table_idx; + rmb->init_locals = mb->init_locals; + rmb->skip_visibility = FALSE; + rmb->return_modreq = mb->return_modreq; + rmb->return_modopt = mb->return_modopt; + rmb->param_modreq = mb->param_modreq; + rmb->param_modopt = mb->param_modopt; + rmb->permissions = mb->permissions; + rmb->mhandle = mb->mhandle; + rmb->nrefs = 0; + rmb->refs = NULL; + + if (mb->dll) { + rmb->charset = mb->charset; + rmb->extra_flags = mb->extra_flags; + rmb->native_cc = mb->native_cc; + rmb->dllentry = mb->dllentry; + rmb->dll = mb->dll; + } + + return TRUE; +} + +gboolean +mono_reflection_methodbuilder_from_ctor_builder (ReflectionMethodBuilder *rmb, MonoReflectionCtorBuilder *mb, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + + const char *name = mb->attrs & METHOD_ATTRIBUTE_STATIC ? ".cctor": ".ctor"; + + error_init (error); + + memset (rmb, 0, sizeof (ReflectionMethodBuilder)); + + rmb->ilgen = mb->ilgen; + rmb->rtype = mono_type_get_object_checked (mono_domain_get (), &mono_defaults.void_class->byval_arg, error); + return_val_if_nok (error, FALSE); + rmb->parameters = mb->parameters; + rmb->generic_params = NULL; + rmb->generic_container = NULL; + rmb->opt_types = NULL; + rmb->pinfo = mb->pinfo; + rmb->attrs = mb->attrs; + rmb->iattrs = mb->iattrs; + rmb->call_conv = mb->call_conv; + rmb->code = NULL; + rmb->type = mb->type; + rmb->name = mono_string_new_checked (mono_domain_get (), name, error); + return_val_if_nok (error, FALSE); + rmb->table_idx = &mb->table_idx; + rmb->init_locals = mb->init_locals; + rmb->skip_visibility = FALSE; + rmb->return_modreq = NULL; + rmb->return_modopt = NULL; + rmb->param_modreq = mb->param_modreq; + rmb->param_modopt = mb->param_modopt; + rmb->permissions = mb->permissions; + rmb->mhandle = mb->mhandle; + rmb->nrefs = 0; + rmb->refs = NULL; + + return TRUE; +} + +static void +reflection_methodbuilder_from_dynamic_method (ReflectionMethodBuilder *rmb, MonoReflectionDynamicMethod *mb) +{ + MONO_REQ_GC_UNSAFE_MODE; + + memset (rmb, 0, sizeof (ReflectionMethodBuilder)); + + rmb->ilgen = mb->ilgen; + rmb->rtype = mb->rtype; + rmb->parameters = mb->parameters; + rmb->generic_params = NULL; + rmb->generic_container = NULL; + rmb->opt_types = NULL; + rmb->pinfo = NULL; + rmb->attrs = mb->attrs; + rmb->iattrs = 0; + rmb->call_conv = mb->call_conv; + rmb->code = NULL; + rmb->type = (MonoObject *) mb->owner; + rmb->name = mb->name; + rmb->table_idx = NULL; + rmb->init_locals = mb->init_locals; + rmb->skip_visibility = mb->skip_visibility; + rmb->return_modreq = NULL; + rmb->return_modopt = NULL; + rmb->param_modreq = NULL; + rmb->param_modopt = NULL; + rmb->permissions = NULL; + rmb->mhandle = mb->mhandle; + rmb->nrefs = 0; + rmb->refs = NULL; +} +#else /* DISABLE_REFLECTION_EMIT */ +gboolean +mono_reflection_methodbuilder_from_method_builder (ReflectionMethodBuilder *rmb, MonoReflectionMethodBuilder *mb, MonoError *error) { + g_assert_not_reached (); + return FALSE; +} +gboolean +mono_reflection_methodbuilder_from_ctor_builder (ReflectionMethodBuilder *rmb, MonoReflectionCtorBuilder *mb, MonoError *error) +{ + g_assert_not_reached (); + return FALSE; +} +#endif /* DISABLE_REFLECTION_EMIT */ + +#ifndef DISABLE_REFLECTION_EMIT +static guint32 +mono_image_add_memberef_row (MonoDynamicImage *assembly, guint32 parent, const char *name, guint32 sig) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + MonoDynamicTable *table; + guint32 *values; + guint32 token, pclass; + + switch (parent & MONO_TYPEDEFORREF_MASK) { + case MONO_TYPEDEFORREF_TYPEREF: + pclass = MONO_MEMBERREF_PARENT_TYPEREF; + break; + case MONO_TYPEDEFORREF_TYPESPEC: + pclass = MONO_MEMBERREF_PARENT_TYPESPEC; + break; + case MONO_TYPEDEFORREF_TYPEDEF: + pclass = MONO_MEMBERREF_PARENT_TYPEDEF; + break; + default: + g_warning ("unknown typeref or def token 0x%08x for %s", parent, name); + return 0; + } + /* extract the index */ + parent >>= MONO_TYPEDEFORREF_BITS; + + table = &assembly->tables [MONO_TABLE_MEMBERREF]; + + if (assembly->save) { + alloc_table (table, table->rows + 1); + values = table->values + table->next_idx * MONO_MEMBERREF_SIZE; + values [MONO_MEMBERREF_CLASS] = pclass | (parent << MONO_MEMBERREF_PARENT_BITS); + values [MONO_MEMBERREF_NAME] = string_heap_insert (&assembly->sheap, name); + values [MONO_MEMBERREF_SIGNATURE] = sig; + } + + token = MONO_TOKEN_MEMBER_REF | table->next_idx; + table->next_idx ++; + + return token; +} + +/* + * Insert a memberef row into the metadata: the token that point to the memberref + * is returned. Caching is done in the caller (mono_image_get_methodref_token() or + * mono_image_get_fieldref_token()). + * The sig param is an index to an already built signature. + */ +static guint32 +mono_image_get_memberref_token (MonoDynamicImage *assembly, MonoType *type, const char *name, guint32 sig) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + guint32 parent = mono_image_typedef_or_ref (assembly, type); + return mono_image_add_memberef_row (assembly, parent, name, sig); +} + + +guint32 +mono_image_get_methodref_token (MonoDynamicImage *assembly, MonoMethod *method, gboolean create_typespec) +{ + MONO_REQ_GC_NEUTRAL_MODE; + + guint32 token; + MonoMethodSignature *sig; + + create_typespec = create_typespec && method->is_generic && method->klass->image != &assembly->image; + + if (create_typespec) { + token = GPOINTER_TO_UINT (g_hash_table_lookup (assembly->handleref, GUINT_TO_POINTER (GPOINTER_TO_UINT (method) + 1))); + if (token) + return token; + } + + token = GPOINTER_TO_UINT (g_hash_table_lookup (assembly->handleref, method)); + if (token && !create_typespec) + return token; + + g_assert (!method->is_inflated); + if (!token) { + /* + * A methodref signature can't contain an unmanaged calling convention. + */ + sig = mono_metadata_signature_dup (mono_method_signature (method)); + if ((sig->call_convention != MONO_CALL_DEFAULT) && (sig->call_convention != MONO_CALL_VARARG)) + sig->call_convention = MONO_CALL_DEFAULT; + token = mono_image_get_memberref_token (assembly, &method->klass->byval_arg, + method->name, mono_dynimage_encode_method_signature (assembly, sig)); + g_free (sig); + g_hash_table_insert (assembly->handleref, method, GUINT_TO_POINTER(token)); + } + + if (create_typespec) { + MonoDynamicTable *table = &assembly->tables [MONO_TABLE_METHODSPEC]; + g_assert (mono_metadata_token_table (token) == MONO_TABLE_MEMBERREF); + token = (mono_metadata_token_index (token) << MONO_METHODDEFORREF_BITS) | MONO_METHODDEFORREF_METHODREF; + + if (assembly->save) { + guint32 *values; + + alloc_table (table, table->rows + 1); + values = table->values + table->next_idx * MONO_METHODSPEC_SIZE; + values [MONO_METHODSPEC_METHOD] = token; + values [MONO_METHODSPEC_SIGNATURE] = mono_dynimage_encode_generic_method_sig (assembly, &mono_method_get_generic_container (method)->context); + } + + token = MONO_TOKEN_METHOD_SPEC | table->next_idx; + table->next_idx ++; + /*methodspec and memberef tokens are diferent, */ + g_hash_table_insert (assembly->handleref, GUINT_TO_POINTER (GPOINTER_TO_UINT (method) + 1), GUINT_TO_POINTER (token)); + return token; + } + return token; +} + +static guint32 +mono_image_get_varargs_method_token (MonoDynamicImage *assembly, guint32 original, + const gchar *name, guint32 sig) +{ + MonoDynamicTable *table; + guint32 token; + guint32 *values; + + table = &assembly->tables [MONO_TABLE_MEMBERREF]; + + if (assembly->save) { + alloc_table (table, table->rows + 1); + values = table->values + table->next_idx * MONO_MEMBERREF_SIZE; + values [MONO_MEMBERREF_CLASS] = original; + values [MONO_MEMBERREF_NAME] = string_heap_insert (&assembly->sheap, name); + values [MONO_MEMBERREF_SIGNATURE] = sig; + } + + token = MONO_TOKEN_MEMBER_REF | table->next_idx; + table->next_idx ++; + + return token; +} + +#else /* DISABLE_REFLECTION_EMIT */ + +guint32 +mono_image_get_methodref_token (MonoDynamicImage *assembly, MonoMethod *method, gboolean create_typespec) +{ + g_assert_not_reached (); + return -1; +} +#endif + +static gboolean +is_field_on_inst (MonoClassField *field) +{ + return mono_class_is_ginst (field->parent) && mono_class_get_generic_class (field->parent)->is_dynamic; +} + +#ifndef DISABLE_REFLECTION_EMIT +static guint32 +mono_image_get_fieldref_token (MonoDynamicImage *assembly, MonoClassField *field) +{ + MonoType *type; + guint32 token; + + g_assert (field); + g_assert (field->parent); + + token = GPOINTER_TO_UINT (g_hash_table_lookup (assembly->handleref, field)); + if (token) + return token; + + if (mono_class_is_ginst (field->parent) && mono_class_get_generic_class (field->parent)->container_class && mono_class_get_generic_class (field->parent)->container_class->fields) { + int index = field - field->parent->fields; + type = mono_field_get_type (&mono_class_get_generic_class (field->parent)->container_class->fields [index]); + } else { + type = mono_field_get_type (field); + } + token = mono_image_get_memberref_token (assembly, &field->parent->byval_arg, + mono_field_get_name (field), + mono_dynimage_encode_fieldref_signature (assembly, field->parent->image, type)); + g_hash_table_insert (assembly->handleref, field, GUINT_TO_POINTER(token)); + return token; +} + +static guint32 +method_encode_methodspec (MonoDynamicImage *assembly, MonoMethod *method) +{ + MonoDynamicTable *table; + guint32 *values; + guint32 token, mtoken = 0, sig; + MonoMethodInflated *imethod; + MonoMethod *declaring; + + table = &assembly->tables [MONO_TABLE_METHODSPEC]; + + g_assert (method->is_inflated); + imethod = (MonoMethodInflated *) method; + declaring = imethod->declaring; + + sig = mono_dynimage_encode_method_signature (assembly, mono_method_signature (declaring)); + mtoken = mono_image_get_memberref_token (assembly, &method->klass->byval_arg, declaring->name, sig); + + if (!mono_method_signature (declaring)->generic_param_count) + return mtoken; + + switch (mono_metadata_token_table (mtoken)) { + case MONO_TABLE_MEMBERREF: + mtoken = (mono_metadata_token_index (mtoken) << MONO_METHODDEFORREF_BITS) | MONO_METHODDEFORREF_METHODREF; + break; + case MONO_TABLE_METHOD: + mtoken = (mono_metadata_token_index (mtoken) << MONO_METHODDEFORREF_BITS) | MONO_METHODDEFORREF_METHODDEF; + break; + default: + g_assert_not_reached (); + } + + sig = mono_dynimage_encode_generic_method_sig (assembly, mono_method_get_context (method)); + + if (assembly->save) { + alloc_table (table, table->rows + 1); + values = table->values + table->next_idx * MONO_METHODSPEC_SIZE; + values [MONO_METHODSPEC_METHOD] = mtoken; + values [MONO_METHODSPEC_SIGNATURE] = sig; + } + + token = MONO_TOKEN_METHOD_SPEC | table->next_idx; + table->next_idx ++; + + return token; +} + +static guint32 +mono_image_get_methodspec_token (MonoDynamicImage *assembly, MonoMethod *method) +{ + MonoMethodInflated *imethod; + guint32 token; + + token = GPOINTER_TO_UINT (g_hash_table_lookup (assembly->handleref, method)); + if (token) + return token; + + g_assert (method->is_inflated); + imethod = (MonoMethodInflated *) method; + + if (mono_method_signature (imethod->declaring)->generic_param_count) { + token = method_encode_methodspec (assembly, method); + } else { + guint32 sig = mono_dynimage_encode_method_signature ( + assembly, mono_method_signature (imethod->declaring)); + token = mono_image_get_memberref_token ( + assembly, &method->klass->byval_arg, method->name, sig); + } + + g_hash_table_insert (assembly->handleref, method, GUINT_TO_POINTER(token)); + return token; +} + +static guint32 +mono_image_get_inflated_method_token (MonoDynamicImage *assembly, MonoMethod *m) +{ + MonoMethodInflated *imethod = (MonoMethodInflated *) m; + guint32 sig, token; + + sig = mono_dynimage_encode_method_signature (assembly, mono_method_signature (imethod->declaring)); + token = mono_image_get_memberref_token ( + assembly, &m->klass->byval_arg, m->name, sig); + + return token; +} + +static guint32 +mono_image_get_sighelper_token (MonoDynamicImage *assembly, MonoReflectionSigHelperHandle helper, MonoError *error) +{ + guint32 idx; + MonoDynamicTable *table; + guint32 *values; + + error_init (error); + + table = &assembly->tables [MONO_TABLE_STANDALONESIG]; + idx = table->next_idx ++; + table->rows ++; + alloc_table (table, table->rows); + values = table->values + idx * MONO_STAND_ALONE_SIGNATURE_SIZE; + + values [MONO_STAND_ALONE_SIGNATURE] = + mono_dynimage_encode_reflection_sighelper (assembly, helper, error); + return_val_if_nok (error, 0); + + return idx; +} + +static int +reflection_cc_to_file (int call_conv) { + switch (call_conv & 0x3) { + case 0: + case 1: return MONO_CALL_DEFAULT; + case 2: return MONO_CALL_VARARG; + default: + g_assert_not_reached (); + } + return 0; +} +#endif /* !DISABLE_REFLECTION_EMIT */ + +struct _ArrayMethod { + MonoType *parent; + MonoMethodSignature *sig; + char *name; + guint32 token; +}; + +void +mono_sre_array_method_free (ArrayMethod *am) +{ + g_free (am->sig); + g_free (am->name); + g_free (am); +} + +#ifndef DISABLE_REFLECTION_EMIT +static guint32 +mono_image_get_array_token (MonoDynamicImage *assembly, MonoReflectionArrayMethodHandle m, MonoError *error) +{ + MonoMethodSignature *sig = NULL; + char *name = NULL; + + error_init (error); + + MonoArrayHandle parameters = MONO_HANDLE_NEW_GET (MonoArray, m, parameters); + guint32 nparams = mono_array_handle_length (parameters); + sig = (MonoMethodSignature *)g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + sizeof (MonoType*) * nparams); + sig->hasthis = 1; + sig->sentinelpos = -1; + sig->call_convention = reflection_cc_to_file (MONO_HANDLE_GETVAL (m, call_conv)); + sig->param_count = nparams; + MonoReflectionTypeHandle ret = MONO_HANDLE_NEW_GET (MonoReflectionType, m, ret); + if (!MONO_HANDLE_IS_NULL (ret)) { + sig->ret = mono_reflection_type_handle_mono_type (ret, error); + goto_if_nok (error, fail); + } else + sig->ret = &mono_defaults.void_class->byval_arg; + + MonoReflectionTypeHandle parent = MONO_HANDLE_NEW_GET (MonoReflectionType, m, parent); + MonoType *mtype = mono_reflection_type_handle_mono_type (parent, error); + goto_if_nok (error, fail); + + for (int i = 0; i < nparams; ++i) { + sig->params [i] = mono_type_array_get_and_resolve (parameters, i, error); + goto_if_nok (error, fail); + } + + MonoStringHandle mname = MONO_HANDLE_NEW_GET (MonoString, m, name); + name = mono_string_handle_to_utf8 (mname, error); + goto_if_nok (error, fail); + + ArrayMethod *am = NULL; + for (GList *tmp = assembly->array_methods; tmp; tmp = tmp->next) { + am = (ArrayMethod *)tmp->data; + if (strcmp (name, am->name) == 0 && + mono_metadata_type_equal (am->parent, mtype) && + mono_metadata_signature_equal (am->sig, sig)) { + g_free (name); + g_free (sig); + MONO_HANDLE_SETVAL (m, table_idx, guint32, am->token & 0xffffff); + return am->token; + } + } + am = g_new0 (ArrayMethod, 1); + am->name = name; + am->sig = sig; + am->parent = mtype; + am->token = mono_image_get_memberref_token (assembly, am->parent, name, + mono_dynimage_encode_method_signature (assembly, sig)); + assembly->array_methods = g_list_prepend (assembly->array_methods, am); + MONO_HANDLE_SETVAL (m, table_idx, guint32, am->token & 0xffffff); + return am->token; +fail: + g_free (name); + g_free (sig); + return 0; + +} +#endif + +#ifndef DISABLE_REFLECTION_EMIT + +/* + * mono_image_insert_string: + * @module: module builder object + * @str: a string + * + * Insert @str into the user string stream of @module. + */ +guint32 +mono_image_insert_string (MonoReflectionModuleBuilderHandle ref_module, MonoStringHandle str, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + guint32 idx; + char buf [16]; + char *b = buf; + guint32 token = 0; + + MonoDynamicImage *assembly = MONO_HANDLE_GETVAL (ref_module, dynamic_image); + if (!assembly) { + if (!mono_image_module_basic_init (ref_module, error)) + goto leave; + + assembly = MONO_HANDLE_GETVAL (ref_module, dynamic_image); + } + g_assert (assembly != NULL); + + if (assembly->save) { + int32_t length = mono_string_length (MONO_HANDLE_RAW (str)); + mono_metadata_encode_value (1 | (length * 2), b, &b); + idx = mono_image_add_stream_data (&assembly->us, buf, b-buf); + /* pinned */ + uint32_t gchandle = mono_gchandle_from_handle (MONO_HANDLE_CAST (MonoObject, str), TRUE); + const char *p = (const char*)mono_string_chars (MONO_HANDLE_RAW (str)); +#if G_BYTE_ORDER != G_LITTLE_ENDIAN + { + char *swapped = g_malloc (2 * length); + + swap_with_size (swapped, p, 2, length); + mono_image_add_stream_data (&assembly->us, swapped, length * 2); + g_free (swapped); + } +#else + mono_image_add_stream_data (&assembly->us, p, length * 2); +#endif + mono_gchandle_free (gchandle); + mono_image_add_stream_data (&assembly->us, "", 1); + } else { + idx = assembly->us.index ++; + } + + token = MONO_TOKEN_STRING | idx; + mono_dynamic_image_register_token (assembly, token, MONO_HANDLE_CAST (MonoObject, str), MONO_DYN_IMAGE_TOK_NEW); + +leave: + HANDLE_FUNCTION_RETURN_VAL (token); +} + +static guint32 +create_method_token (MonoDynamicImage *assembly, MonoMethod *method, MonoArrayHandle opt_param_types, MonoError *error) +{ + guint32 sig_token, parent; + + + int nargs = mono_array_handle_length (opt_param_types); + MonoMethodSignature *old = mono_method_signature (method); + MonoMethodSignature *sig = mono_metadata_signature_alloc ( &assembly->image, old->param_count + nargs); + + sig->hasthis = old->hasthis; + sig->explicit_this = old->explicit_this; + sig->call_convention = old->call_convention; + sig->generic_param_count = old->generic_param_count; + sig->param_count = old->param_count + nargs; + sig->sentinelpos = old->param_count; + sig->ret = old->ret; + + for (int i = 0; i < old->param_count; i++) + sig->params [i] = old->params [i]; + + MonoReflectionTypeHandle rt = MONO_HANDLE_NEW (MonoReflectionType, NULL); + for (int i = 0; i < nargs; i++) { + MONO_HANDLE_ARRAY_GETREF (rt, opt_param_types, i); + sig->params [old->param_count + i] = mono_reflection_type_handle_mono_type (rt, error); + goto_if_nok (error, fail); + } + + parent = mono_image_typedef_or_ref (assembly, &method->klass->byval_arg); + g_assert ((parent & MONO_TYPEDEFORREF_MASK) == MONO_MEMBERREF_PARENT_TYPEREF); + parent >>= MONO_TYPEDEFORREF_BITS; + + parent <<= MONO_MEMBERREF_PARENT_BITS; + parent |= MONO_MEMBERREF_PARENT_TYPEREF; + + sig_token = mono_dynimage_encode_method_signature (assembly, sig); + guint32 token = mono_image_get_varargs_method_token (assembly, parent, method->name, sig_token); + g_hash_table_insert (assembly->vararg_aux_hash, GUINT_TO_POINTER (token), sig); + return token; +fail: + return 0; +} + +guint32 +mono_image_create_method_token (MonoDynamicImage *assembly, MonoObjectHandle obj, MonoArrayHandle opt_param_types, MonoError *error) +{ + guint32 token = 0; + + error_init (error); + + MonoClass *klass = mono_handle_class (obj); + if (strcmp (klass->name, "MonoMethod") == 0 || strcmp (klass->name, "MonoCMethod") == 0) { + MonoReflectionMethodHandle ref_method = MONO_HANDLE_CAST (MonoReflectionMethod, obj); + MonoMethod *method = MONO_HANDLE_GETVAL (ref_method, method); + g_assert (!MONO_HANDLE_IS_NULL (opt_param_types) && (mono_method_signature (method)->sentinelpos >= 0)); + token = create_method_token (assembly, method, opt_param_types, error); + goto_if_nok (error, fail); + } else if (strcmp (klass->name, "MethodBuilder") == 0) { + g_assert_not_reached (); + } else { + g_error ("requested method token for %s\n", klass->name); + } + + mono_dynamic_image_register_token (assembly, token, obj, MONO_DYN_IMAGE_TOK_NEW); + return token; +fail: + g_assert (!mono_error_ok (error)); + return 0; +} + +/* + * mono_image_create_token: + * @assembly: a dynamic assembly + * @obj: + * @register_token: Whenever to register the token in the assembly->tokens hash. + * + * Get a token to insert in the IL code stream for the given MemberInfo. + * The metadata emission routines need to pass FALSE as REGISTER_TOKEN, since by that time, + * the table_idx-es were recomputed, so registering the token would overwrite an existing + * entry. + */ +guint32 +mono_image_create_token (MonoDynamicImage *assembly, MonoObjectHandle obj, + gboolean create_open_instance, gboolean register_token, + MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + guint32 token = 0; + + error_init (error); + + MonoClass *klass = mono_handle_class (obj); + MonoObjectHandle register_obj = MONO_HANDLE_NEW (MonoObject, NULL); + MONO_HANDLE_ASSIGN (register_obj, obj); + + /* Check for user defined reflection objects */ + /* TypeDelegator is the only corlib type which doesn't look like a MonoReflectionType */ + if (klass->image != mono_defaults.corlib || (strcmp (klass->name, "TypeDelegator") == 0)) { + mono_error_set_not_supported (error, "User defined subclasses of System.Type are not yet supported"); + goto leave; + } + + /* This function is called from ModuleBuilder:getToken multiple times for the same objects */ + int how_collide = MONO_DYN_IMAGE_TOK_SAME_OK; + + if (strcmp (klass->name, "RuntimeType") == 0) { + MonoType *type = mono_reflection_type_handle_mono_type (MONO_HANDLE_CAST (MonoReflectionType, obj), error); + goto_if_nok (error, leave); + MonoClass *mc = mono_class_from_mono_type (type); + token = mono_metadata_token_from_dor ( + mono_dynimage_encode_typedef_or_ref_full (assembly, type, !mono_class_is_gtd (mc) || create_open_instance)); + /* If it's a RuntimeType now, we could have registered a + * TypeBuilder for it before, so replacing is okay. */ + how_collide = MONO_DYN_IMAGE_TOK_REPLACE; + } else if (strcmp (klass->name, "MonoCMethod") == 0 || + strcmp (klass->name, "MonoMethod") == 0) { + MonoReflectionMethodHandle m = MONO_HANDLE_CAST (MonoReflectionMethod, obj); + MonoMethod *method = MONO_HANDLE_GETVAL (m, method); + if (method->is_inflated) { + if (create_open_instance) { + guint32 methodspec_token = mono_image_get_methodspec_token (assembly, method); + MonoReflectionMethodHandle canonical_obj = + mono_method_get_object_handle (MONO_HANDLE_DOMAIN (obj), method, NULL, error); + goto_if_nok (error, leave); + MONO_HANDLE_ASSIGN (register_obj, canonical_obj); + token = methodspec_token; + } else + token = mono_image_get_inflated_method_token (assembly, method); + } else if ((method->klass->image == &assembly->image) && + !mono_class_is_ginst (method->klass)) { + static guint32 method_table_idx = 0xffffff; + if (method->klass->wastypebuilder) { + /* we use the same token as the one that was assigned + * to the Methodbuilder. + * FIXME: do the equivalent for Fields. + */ + token = method->token; + how_collide = MONO_DYN_IMAGE_TOK_REPLACE; + } else { + /* + * Each token should have a unique index, but the indexes are + * assigned by managed code, so we don't know about them. An + * easy solution is to count backwards... + */ + method_table_idx --; + token = MONO_TOKEN_METHOD_DEF | method_table_idx; + how_collide = MONO_DYN_IMAGE_TOK_NEW; + } + } else { + guint32 methodref_token = mono_image_get_methodref_token (assembly, method, create_open_instance); + /* We need to register a 'canonical' object. The same + * MonoMethod could have been reflected via different + * classes so the MonoReflectionMethod:reftype could be + * different, and the object lookup in + * dynamic_image_register_token would assert assert. So + * we pick the MonoReflectionMethod object that has the + * reflected type as NULL (ie, take the declaring type + * of the method) */ + MonoReflectionMethodHandle canonical_obj = + mono_method_get_object_handle (MONO_HANDLE_DOMAIN (obj), method, NULL, error); + goto_if_nok (error, leave); + MONO_HANDLE_ASSIGN (register_obj, canonical_obj); + token = methodref_token; + } + /*g_print ("got token 0x%08x for %s\n", token, m->method->name);*/ + } else if (strcmp (klass->name, "MonoField") == 0) { + MonoReflectionFieldHandle f = MONO_HANDLE_CAST (MonoReflectionField, obj); + MonoClassField *field = MONO_HANDLE_GETVAL (f, field); + if ((field->parent->image == &assembly->image) && !is_field_on_inst (field)) { + static guint32 field_table_idx = 0xffffff; + field_table_idx --; + token = MONO_TOKEN_FIELD_DEF | field_table_idx; + how_collide = MONO_DYN_IMAGE_TOK_NEW; + } else { + guint32 fieldref_token = mono_image_get_fieldref_token (assembly, field); + /* Same as methodref: get a canonical object to + * register with the token. */ + MonoReflectionFieldHandle canonical_obj = + mono_field_get_object_handle (MONO_HANDLE_DOMAIN (obj), field->parent, field, error); + goto_if_nok (error, leave); + MONO_HANDLE_ASSIGN (register_obj, canonical_obj); + token = fieldref_token; + } + /*g_print ("got token 0x%08x for %s\n", token, f->field->name);*/ + } else if (strcmp (klass->name, "MonoArrayMethod") == 0) { + MonoReflectionArrayMethodHandle m = MONO_HANDLE_CAST (MonoReflectionArrayMethod, obj); + /* mono_image_get_array_token caches tokens by signature */ + guint32 array_token = mono_image_get_array_token (assembly, m, error); + goto_if_nok (error, leave); + token = array_token; + /* ModuleBuilder:GetArrayMethod() always returns a fresh + * MonoArrayMethod instance even given the same method name and + * signature. But they're all interchangeable, so it's okay to + * replace. + */ + how_collide = MONO_DYN_IMAGE_TOK_REPLACE; + } else if (strcmp (klass->name, "SignatureHelper") == 0) { + MonoReflectionSigHelperHandle s = MONO_HANDLE_CAST (MonoReflectionSigHelper, obj); + /* always returns a fresh token */ + guint32 sig_token = MONO_TOKEN_SIGNATURE | mono_image_get_sighelper_token (assembly, s, error); + goto_if_nok (error, leave); + token = sig_token; + how_collide = MONO_DYN_IMAGE_TOK_NEW; + } else { + g_error ("requested token for %s\n", klass->name); + } + + if (register_token) + mono_dynamic_image_register_token (assembly, token, register_obj, how_collide); + +leave: + HANDLE_FUNCTION_RETURN_VAL (token); +} + + +#endif + +#ifndef DISABLE_REFLECTION_EMIT + +static gboolean +assemblybuilderaccess_can_refonlyload (guint32 access) +{ + return (access & 0x4) != 0; +} + +static gboolean +assemblybuilderaccess_can_run (guint32 access) +{ + return (access & MonoAssemblyBuilderAccess_Run) != 0; +} + +static gboolean +assemblybuilderaccess_can_save (guint32 access) +{ + return (access & MonoAssemblyBuilderAccess_Save) != 0; +} + + +/* + * mono_reflection_dynimage_basic_init: + * @assembly: an assembly builder object + * + * Create the MonoImage that represents the assembly builder and setup some + * of the helper hash table and the basic metadata streams. + */ +void +mono_reflection_dynimage_basic_init (MonoReflectionAssemblyBuilder *assemblyb) +{ + MonoError error; + MonoDynamicAssembly *assembly; + MonoDynamicImage *image; + MonoDomain *domain = mono_object_domain (assemblyb); + + if (assemblyb->dynamic_assembly) + return; + + assembly = assemblyb->dynamic_assembly = g_new0 (MonoDynamicAssembly, 1); + + MONO_PROFILER_RAISE (assembly_loading, (&assembly->assembly)); + + assembly->assembly.ref_count = 1; + assembly->assembly.dynamic = TRUE; + assembly->assembly.corlib_internal = assemblyb->corlib_internal; + assemblyb->assembly.assembly = (MonoAssembly*)assembly; + assembly->assembly.basedir = mono_string_to_utf8_checked (assemblyb->dir, &error); + if (mono_error_set_pending_exception (&error)) + return; + if (assemblyb->culture) { + assembly->assembly.aname.culture = mono_string_to_utf8_checked (assemblyb->culture, &error); + if (mono_error_set_pending_exception (&error)) + return; + } else + assembly->assembly.aname.culture = g_strdup (""); + + if (assemblyb->version) { + char *vstr = mono_string_to_utf8_checked (assemblyb->version, &error); + if (mono_error_set_pending_exception (&error)) + return; + char **version = g_strsplit (vstr, ".", 4); + char **parts = version; + assembly->assembly.aname.major = atoi (*parts++); + assembly->assembly.aname.minor = atoi (*parts++); + assembly->assembly.aname.build = *parts != NULL ? atoi (*parts++) : 0; + assembly->assembly.aname.revision = *parts != NULL ? atoi (*parts) : 0; + + g_strfreev (version); + g_free (vstr); + } else { + assembly->assembly.aname.major = 0; + assembly->assembly.aname.minor = 0; + assembly->assembly.aname.build = 0; + assembly->assembly.aname.revision = 0; + } + + assembly->assembly.ref_only = assemblybuilderaccess_can_refonlyload (assemblyb->access); + assembly->run = assemblybuilderaccess_can_run (assemblyb->access); + assembly->save = assemblybuilderaccess_can_save (assemblyb->access); + assembly->domain = domain; + + char *assembly_name = mono_string_to_utf8_checked (assemblyb->name, &error); + if (mono_error_set_pending_exception (&error)) + return; + image = mono_dynamic_image_create (assembly, assembly_name, g_strdup ("RefEmit_YouForgotToDefineAModule")); + image->initial_image = TRUE; + assembly->assembly.aname.name = image->image.name; + assembly->assembly.image = &image->image; + if (assemblyb->pktoken && assemblyb->pktoken->max_length) { + /* -1 to correct for the trailing NULL byte */ + if (assemblyb->pktoken->max_length != MONO_PUBLIC_KEY_TOKEN_LENGTH - 1) { + g_error ("Public key token length invalid for assembly %s: %i", assembly->assembly.aname.name, assemblyb->pktoken->max_length); + } + memcpy (&assembly->assembly.aname.public_key_token, mono_array_addr (assemblyb->pktoken, guint8, 0), assemblyb->pktoken->max_length); + } + + mono_domain_assemblies_lock (domain); + domain->domain_assemblies = g_slist_append (domain->domain_assemblies, assembly); + mono_domain_assemblies_unlock (domain); + + register_assembly (mono_object_domain (assemblyb), &assemblyb->assembly, &assembly->assembly); + + MONO_PROFILER_RAISE (assembly_loaded, (&assembly->assembly)); + + mono_assembly_invoke_load_hook ((MonoAssembly*)assembly); +} + +#endif /* !DISABLE_REFLECTION_EMIT */ + +#ifndef DISABLE_REFLECTION_EMIT +static gpointer +register_assembly (MonoDomain *domain, MonoReflectionAssembly *res, MonoAssembly *assembly) +{ + return CACHE_OBJECT (MonoReflectionAssembly *, assembly, &res->object, NULL); +} + +static MonoReflectionModuleBuilderHandle +register_module (MonoDomain *domain, MonoReflectionModuleBuilderHandle res, MonoDynamicImage *module) +{ + return CACHE_OBJECT_HANDLE (MonoReflectionModuleBuilderHandle, module, MONO_HANDLE_CAST (MonoObject, res), NULL); +} + +static gboolean +image_module_basic_init (MonoReflectionModuleBuilderHandle moduleb, MonoError *error) +{ + error_init (error); + MonoDomain *domain = MONO_HANDLE_DOMAIN (moduleb); + MonoDynamicImage *image = MONO_HANDLE_GETVAL (moduleb, dynamic_image); + MonoReflectionAssemblyBuilderHandle ab = MONO_HANDLE_NEW (MonoReflectionAssemblyBuilder, NULL); + MONO_HANDLE_GET (ab, moduleb, assemblyb); + if (!image) { + /* + * FIXME: we already created an image in mono_reflection_dynimage_basic_init (), but + * we don't know which module it belongs to, since that is only + * determined at assembly save time. + */ + /*image = (MonoDynamicImage*)ab->dynamic_assembly->assembly.image; */ + MonoStringHandle abname = MONO_HANDLE_NEW_GET (MonoString, ab, name); + char *name = mono_string_handle_to_utf8 (abname, error); + return_val_if_nok (error, FALSE); + MonoStringHandle modfqname = MONO_HANDLE_NEW_GET (MonoString, MONO_HANDLE_CAST (MonoReflectionModule, moduleb), fqname); + char *fqname = mono_string_handle_to_utf8 (modfqname, error); + if (!is_ok (error)) { + g_free (name); + return FALSE; + } + MonoDynamicAssembly *dynamic_assembly = MONO_HANDLE_GETVAL (ab, dynamic_assembly); + image = mono_dynamic_image_create (dynamic_assembly, name, fqname); + + MONO_HANDLE_SETVAL (MONO_HANDLE_CAST (MonoReflectionModule, moduleb), image, MonoImage*, &image->image); + MONO_HANDLE_SETVAL (moduleb, dynamic_image, MonoDynamicImage*, image); + register_module (domain, moduleb, image); + + /* register the module with the assembly */ + MonoImage *ass = dynamic_assembly->assembly.image; + int module_count = ass->module_count; + MonoImage **new_modules = g_new0 (MonoImage *, module_count + 1); + + if (ass->modules) + memcpy (new_modules, ass->modules, module_count * sizeof (MonoImage *)); + new_modules [module_count] = &image->image; + mono_image_addref (&image->image); + + g_free (ass->modules); + ass->modules = new_modules; + ass->module_count ++; + } + return TRUE; +} + +static gboolean +mono_image_module_basic_init (MonoReflectionModuleBuilderHandle moduleb, MonoError *error) +{ + error_init (error); + return image_module_basic_init (moduleb, error); +} + +#endif + +static gboolean +is_corlib_type (MonoClass *klass) +{ + return klass->image == mono_defaults.corlib; +} + +#define check_corlib_type_cached(_class, _namespace, _name) do { \ + static MonoClass *cached_class; \ + if (cached_class) \ + return cached_class == _class; \ + if (is_corlib_type (_class) && !strcmp (_name, _class->name) && !strcmp (_namespace, _class->name_space)) { \ + cached_class = _class; \ + return TRUE; \ + } \ + return FALSE; \ +} while (0) \ + + +MonoType* +mono_type_array_get_and_resolve (MonoArrayHandle array, int idx, MonoError *error) +{ + HANDLE_FUNCTION_ENTER(); + error_init (error); + MonoReflectionTypeHandle t = MONO_HANDLE_NEW (MonoReflectionType, NULL); + MONO_HANDLE_ARRAY_GETREF (t, array, idx); + MonoType *result = mono_reflection_type_handle_mono_type (t, error); + HANDLE_FUNCTION_RETURN_VAL (result); +} + + +#ifndef DISABLE_REFLECTION_EMIT +static gboolean +is_sre_array (MonoClass *klass) +{ + check_corlib_type_cached (klass, "System.Reflection.Emit", "ArrayType"); +} + +static gboolean +is_sre_byref (MonoClass *klass) +{ + check_corlib_type_cached (klass, "System.Reflection.Emit", "ByRefType"); +} + +static gboolean +is_sre_pointer (MonoClass *klass) +{ + check_corlib_type_cached (klass, "System.Reflection.Emit", "PointerType"); +} + +static gboolean +is_sre_generic_instance (MonoClass *klass) +{ + check_corlib_type_cached (klass, "System.Reflection.Emit", "TypeBuilderInstantiation"); +} + +static gboolean +is_sre_type_builder (MonoClass *klass) +{ + check_corlib_type_cached (klass, "System.Reflection.Emit", "TypeBuilder"); +} + +static gboolean +is_sre_method_builder (MonoClass *klass) +{ + check_corlib_type_cached (klass, "System.Reflection.Emit", "MethodBuilder"); +} + +gboolean +mono_is_sre_ctor_builder (MonoClass *klass) +{ + check_corlib_type_cached (klass, "System.Reflection.Emit", "ConstructorBuilder"); +} + +static gboolean +is_sre_field_builder (MonoClass *klass) +{ + check_corlib_type_cached (klass, "System.Reflection.Emit", "FieldBuilder"); +} + +static gboolean +is_sre_gparam_builder (MonoClass *klass) +{ + check_corlib_type_cached (klass, "System.Reflection.Emit", "GenericTypeParameterBuilder"); +} + +static gboolean +is_sre_enum_builder (MonoClass *klass) +{ + check_corlib_type_cached (klass, "System.Reflection.Emit", "EnumBuilder"); +} + +gboolean +mono_is_sre_method_on_tb_inst (MonoClass *klass) +{ + check_corlib_type_cached (klass, "System.Reflection.Emit", "MethodOnTypeBuilderInst"); +} + +gboolean +mono_is_sre_ctor_on_tb_inst (MonoClass *klass) +{ + check_corlib_type_cached (klass, "System.Reflection.Emit", "ConstructorOnTypeBuilderInst"); +} + +static MonoReflectionTypeHandle +mono_reflection_type_get_underlying_system_type (MonoReflectionTypeHandle t, MonoError *error) +{ + static MonoMethod *method_get_underlying_system_type = NULL; + HANDLE_FUNCTION_ENTER (); + + error_init (error); + + if (!method_get_underlying_system_type) + method_get_underlying_system_type = mono_class_get_method_from_name (mono_defaults.systemtype_class, "get_UnderlyingSystemType", 0); + + MonoReflectionTypeHandle rt = MONO_HANDLE_NEW (MonoReflectionType, NULL); + + MonoMethod *usertype_method = mono_object_handle_get_virtual_method (MONO_HANDLE_CAST (MonoObject, t), method_get_underlying_system_type, error); + goto_if_nok (error, leave); + + MONO_HANDLE_ASSIGN (rt, MONO_HANDLE_NEW (MonoReflectionType, mono_runtime_invoke_checked (usertype_method, MONO_HANDLE_RAW (t), NULL, error))); + +leave: + HANDLE_FUNCTION_RETURN_REF (MonoReflectionType, rt); +} + +MonoType* +mono_reflection_type_get_handle (MonoReflectionType* ref_raw, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + error_init (error); + MONO_HANDLE_DCL (MonoReflectionType, ref); + MonoType *result = mono_reflection_type_handle_mono_type (ref, error); + HANDLE_FUNCTION_RETURN_VAL (result); +} + +static MonoType* +reflection_instance_handle_mono_type (MonoReflectionGenericClassHandle ref_gclass, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + MonoType *result = NULL; + MonoType **types = NULL; + + MonoArrayHandle typeargs = MONO_HANDLE_NEW_GET (MonoArray, ref_gclass, type_arguments); + int count = mono_array_handle_length (typeargs); + types = g_new0 (MonoType*, count); + MonoReflectionTypeHandle t = MONO_HANDLE_NEW (MonoReflectionType, NULL); + for (int i = 0; i < count; ++i) { + MONO_HANDLE_ARRAY_GETREF (t, typeargs, i); + types [i] = mono_reflection_type_handle_mono_type (t, error); + if (!types[i] || !is_ok (error)) { + goto leave; + } + } + /* Need to resolve the generic_type in order for it to create its generic context. */ + MonoReflectionTypeHandle ref_gtd = MONO_HANDLE_NEW_GET (MonoReflectionType, ref_gclass, generic_type); + MonoType *gtd = mono_reflection_type_handle_mono_type (ref_gtd, error); + goto_if_nok (error, leave); + MonoClass *gtd_klass = mono_class_from_mono_type (gtd); + if (is_sre_type_builder (mono_handle_class (ref_gtd))) { + reflection_setup_internal_class (MONO_HANDLE_CAST (MonoReflectionTypeBuilder, ref_gtd), error); + goto_if_nok (error, leave); + } + g_assert (count == 0 || mono_class_is_gtd (gtd_klass)); + result = mono_reflection_bind_generic_parameters (ref_gtd, count, types, error); + goto_if_nok (error, leave); + g_assert (result); + MONO_HANDLE_SETVAL (MONO_HANDLE_CAST (MonoReflectionType, ref_gclass), type, MonoType*, result); +leave: + g_free (types); + HANDLE_FUNCTION_RETURN_VAL (result); +} + +static MonoType* +reflection_param_handle_mono_type (MonoReflectionGenericParamHandle ref_gparam, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + error_init (error); + MonoType *result = NULL; + + + MonoReflectionTypeBuilderHandle ref_tbuilder = MONO_HANDLE_NEW_GET (MonoReflectionTypeBuilder, ref_gparam, tbuilder); + MonoReflectionModuleBuilderHandle ref_module = MONO_HANDLE_NEW_GET (MonoReflectionModuleBuilder, ref_tbuilder, module); + MonoDynamicImage *dynamic_image = MONO_HANDLE_GETVAL (ref_module, dynamic_image); + MonoImage *image = &dynamic_image->image; + + MonoGenericParamFull *param = mono_image_new0 (image, MonoGenericParamFull, 1); + + MonoStringHandle ref_name = MONO_HANDLE_NEW_GET (MonoString, ref_gparam, name); + param->info.name = mono_string_to_utf8_image (image, ref_name, error); + mono_error_assert_ok (error); + param->param.num = MONO_HANDLE_GETVAL (ref_gparam, index); + + MonoReflectionMethodBuilderHandle ref_mbuilder = MONO_HANDLE_NEW_GET (MonoReflectionMethodBuilder, ref_gparam, mbuilder); + if (!MONO_HANDLE_IS_NULL (ref_mbuilder)) { + MonoGenericContainer *generic_container = MONO_HANDLE_GETVAL (ref_mbuilder, generic_container); + if (!generic_container) { + generic_container = (MonoGenericContainer *)mono_image_alloc0 (image, sizeof (MonoGenericContainer)); + generic_container->is_method = TRUE; + /* + * Cannot set owner.method, since the MonoMethod is not created yet. + * Set the image field instead, so type_in_image () works. + */ + generic_container->is_anonymous = TRUE; + generic_container->owner.image = image; + MONO_HANDLE_SETVAL (ref_mbuilder, generic_container, MonoGenericContainer*, generic_container); + } + param->param.owner = generic_container; + } else { + MonoType *type = mono_reflection_type_handle_mono_type (MONO_HANDLE_CAST (MonoReflectionType, ref_tbuilder), error); + goto_if_nok (error, leave); + MonoClass *owner = mono_class_from_mono_type (type); + g_assert (mono_class_is_gtd (owner)); + param->param.owner = mono_class_get_generic_container (owner); + } + + MonoClass *pklass = mono_class_from_generic_parameter_internal ((MonoGenericParam *) param); + + result = &pklass->byval_arg; + + mono_class_set_ref_info (pklass, MONO_HANDLE_CAST (MonoObject, ref_gparam)); + mono_image_append_class_to_reflection_info_set (pklass); + + MONO_HANDLE_SETVAL (MONO_HANDLE_CAST (MonoReflectionType, ref_gparam), type, MonoType*, result); + +leave: + HANDLE_FUNCTION_RETURN_VAL (result); +} + +static MonoType* +mono_type_array_get_and_resolve_raw (MonoArray* array_raw, int idx, MonoError *error) +{ + HANDLE_FUNCTION_ENTER(); /* FIXME callers of mono_type_array_get_and_resolve_raw should use handles */ + error_init (error); + MONO_HANDLE_DCL (MonoArray, array); + MonoType *result = mono_type_array_get_and_resolve (array, idx, error); + HANDLE_FUNCTION_RETURN_VAL (result); +} + +MonoType* +mono_reflection_type_handle_mono_type (MonoReflectionTypeHandle ref, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + error_init (error); + + MonoType* result = NULL; + + g_assert (ref); + if (MONO_HANDLE_IS_NULL (ref)) + goto leave; + MonoType *t = MONO_HANDLE_GETVAL (ref, type); + if (t) { + result = t; + goto leave; + } + + if (mono_reflection_is_usertype (ref)) { + MONO_HANDLE_ASSIGN (ref, mono_reflection_type_get_underlying_system_type (ref, error)); + if (!is_ok (error) || MONO_HANDLE_IS_NULL (ref) || mono_reflection_is_usertype (ref)) + goto leave; + t = MONO_HANDLE_GETVAL (ref, type); + if (t) { + result = t; + goto leave; + } + } + + MonoClass *klass = mono_handle_class (ref); + + if (is_sre_array (klass)) { + MonoReflectionArrayTypeHandle sre_array = MONO_HANDLE_CAST (MonoReflectionArrayType, ref); + MonoReflectionTypeHandle ref_element = MONO_HANDLE_NEW_GET (MonoReflectionType, sre_array, element_type); + MonoType *base = mono_reflection_type_handle_mono_type (ref_element, error); + goto_if_nok (error, leave); + g_assert (base); + gint32 rank = MONO_HANDLE_GETVAL (sre_array, rank); + MonoClass *eclass = mono_class_from_mono_type (base); + result = mono_image_new0 (eclass->image, MonoType, 1); + if (rank == 0) { + result->type = MONO_TYPE_SZARRAY; + result->data.klass = eclass; + } else { + MonoArrayType *at = (MonoArrayType *)mono_image_alloc0 (eclass->image, sizeof (MonoArrayType)); + result->type = MONO_TYPE_ARRAY; + result->data.array = at; + at->eklass = eclass; + at->rank = rank; + } + MONO_HANDLE_SETVAL (ref, type, MonoType*, result); + } else if (is_sre_byref (klass)) { + MonoReflectionDerivedTypeHandle sre_byref = MONO_HANDLE_CAST (MonoReflectionDerivedType, ref); + MonoReflectionTypeHandle ref_element = MONO_HANDLE_NEW_GET (MonoReflectionType, sre_byref, element_type); + MonoType *base = mono_reflection_type_handle_mono_type (ref_element, error); + goto_if_nok (error, leave); + g_assert (base); + result = &mono_class_from_mono_type (base)->this_arg; + MONO_HANDLE_SETVAL (ref, type, MonoType*, result); + } else if (is_sre_pointer (klass)) { + MonoReflectionDerivedTypeHandle sre_pointer = MONO_HANDLE_CAST (MonoReflectionDerivedType, ref); + MonoReflectionTypeHandle ref_element = MONO_HANDLE_NEW_GET (MonoReflectionType, sre_pointer, element_type); + MonoType *base = mono_reflection_type_handle_mono_type (ref_element, error); + goto_if_nok (error, leave); + g_assert (base); + result = &mono_ptr_class_get (base)->byval_arg; + MONO_HANDLE_SETVAL (ref, type, MonoType*, result); + } else if (is_sre_generic_instance (klass)) { + result = reflection_instance_handle_mono_type (MONO_HANDLE_CAST (MonoReflectionGenericClass, ref), error); + } else if (is_sre_gparam_builder (klass)) { + result = reflection_param_handle_mono_type (MONO_HANDLE_CAST (MonoReflectionGenericParam, ref), error); + } else if (is_sre_enum_builder (klass)) { + MonoReflectionEnumBuilderHandle ref_ebuilder = MONO_HANDLE_CAST (MonoReflectionEnumBuilder, ref); + + MonoReflectionTypeHandle ref_tb = MONO_HANDLE_NEW_GET (MonoReflectionType, ref_ebuilder, tb); + result = mono_reflection_type_handle_mono_type (ref_tb, error); + } else if (is_sre_type_builder (klass)) { + MonoReflectionTypeBuilderHandle ref_tb = MONO_HANDLE_CAST (MonoReflectionTypeBuilder, ref); + + /* This happens when a finished type references an unfinished one. Have to create the minimal type */ + reflection_setup_internal_class (ref_tb, error); + mono_error_assert_ok (error); + result = MONO_HANDLE_GETVAL (ref, type); + } else { + g_error ("Cannot handle corlib user type %s", mono_type_full_name (&mono_object_class(ref)->byval_arg)); + } +leave: + HANDLE_FUNCTION_RETURN_VAL (result); +} + +/** + * LOCKING: Assumes the loader lock is held. + */ +static MonoMethodSignature* +parameters_to_signature (MonoImage *image, MonoArrayHandle parameters, MonoError *error) { + MonoMethodSignature *sig; + int count, i; + + error_init (error); + + count = MONO_HANDLE_IS_NULL (parameters) ? 0 : mono_array_handle_length (parameters); + + sig = (MonoMethodSignature *)mono_image_g_malloc0 (image, MONO_SIZEOF_METHOD_SIGNATURE + sizeof (MonoType*) * count); + sig->param_count = count; + sig->sentinelpos = -1; /* FIXME */ + for (i = 0; i < count; ++i) { + sig->params [i] = mono_type_array_get_and_resolve (parameters, i, error); + if (!is_ok (error)) { + image_g_free (image, sig); + return NULL; + } + } + return sig; +} + +/** + * LOCKING: Assumes the loader lock is held. + */ +static MonoMethodSignature* +ctor_builder_to_signature (MonoImage *image, MonoReflectionCtorBuilderHandle ctor, MonoError *error) { + MonoMethodSignature *sig; + + error_init (error); + + sig = parameters_to_signature (image, MONO_HANDLE_NEW_GET (MonoArray, ctor, parameters), error); + return_val_if_nok (error, NULL); + sig->hasthis = MONO_HANDLE_GETVAL (ctor, attrs) & METHOD_ATTRIBUTE_STATIC? 0: 1; + sig->ret = &mono_defaults.void_class->byval_arg; + return sig; +} + +static MonoMethodSignature* +ctor_builder_to_signature_raw (MonoImage *image, MonoReflectionCtorBuilder* ctor_raw, MonoError *error) { + HANDLE_FUNCTION_ENTER(); + MONO_HANDLE_DCL (MonoReflectionCtorBuilder, ctor); + MonoMethodSignature *sig = ctor_builder_to_signature (image, ctor, error); + HANDLE_FUNCTION_RETURN_VAL (sig); +} +/** + * LOCKING: Assumes the loader lock is held. + */ +static MonoMethodSignature* +method_builder_to_signature (MonoImage *image, MonoReflectionMethodBuilderHandle method, MonoError *error) { + MonoMethodSignature *sig; + + error_init (error); + + sig = parameters_to_signature (image, MONO_HANDLE_NEW_GET(MonoArray, method, parameters), error); + return_val_if_nok (error, NULL); + sig->hasthis = MONO_HANDLE_GETVAL (method, attrs) & METHOD_ATTRIBUTE_STATIC? 0: 1; + MonoReflectionTypeHandle rtype = MONO_HANDLE_NEW_GET (MonoReflectionType, method, rtype); + if (!MONO_HANDLE_IS_NULL (rtype)) { + sig->ret = mono_reflection_type_handle_mono_type (rtype, error); + if (!is_ok (error)) { + image_g_free (image, sig); + return NULL; + } + } else { + sig->ret = &mono_defaults.void_class->byval_arg; + } + MonoArrayHandle generic_params = MONO_HANDLE_NEW_GET (MonoArray, method, generic_params); + sig->generic_param_count = MONO_HANDLE_IS_NULL (generic_params) ? 0 : mono_array_handle_length (generic_params); + return sig; +} + +static MonoMethodSignature* +dynamic_method_to_signature (MonoReflectionDynamicMethodHandle method, MonoError *error) { + HANDLE_FUNCTION_ENTER (); + MonoMethodSignature *sig = NULL; + + error_init (error); + + sig = parameters_to_signature (NULL, MONO_HANDLE_NEW_GET (MonoArray, method, parameters), error); + goto_if_nok (error, leave); + sig->hasthis = MONO_HANDLE_GETVAL (method, attrs) & METHOD_ATTRIBUTE_STATIC? 0: 1; + MonoReflectionTypeHandle rtype = MONO_HANDLE_NEW_GET (MonoReflectionType, method, rtype); + if (!MONO_HANDLE_IS_NULL (rtype)) { + sig->ret = mono_reflection_type_handle_mono_type (rtype, error); + if (!is_ok (error)) { + g_free (sig); + sig = NULL; + goto leave; + } + } else { + sig->ret = &mono_defaults.void_class->byval_arg; + } + sig->generic_param_count = 0; +leave: + HANDLE_FUNCTION_RETURN_VAL (sig); +} + +static void +get_prop_name_and_type (MonoObject *prop, char **name, MonoType **type, MonoError *error) +{ + error_init (error); + MonoClass *klass = mono_object_class (prop); + if (strcmp (klass->name, "PropertyBuilder") == 0) { + MonoReflectionPropertyBuilder *pb = (MonoReflectionPropertyBuilder *)prop; + *name = mono_string_to_utf8_checked (pb->name, error); + return_if_nok (error); + *type = mono_reflection_type_get_handle ((MonoReflectionType*)pb->type, error); + } else { + MonoReflectionProperty *p = (MonoReflectionProperty *)prop; + *name = g_strdup (p->property->name); + if (p->property->get) + *type = mono_method_signature (p->property->get)->ret; + else + *type = mono_method_signature (p->property->set)->params [mono_method_signature (p->property->set)->param_count - 1]; + } +} + +static void +get_field_name_and_type (MonoObject *field, char **name, MonoType **type, MonoError *error) +{ + error_init (error); + MonoClass *klass = mono_object_class (field); + if (strcmp (klass->name, "FieldBuilder") == 0) { + MonoReflectionFieldBuilder *fb = (MonoReflectionFieldBuilder *)field; + *name = mono_string_to_utf8_checked (fb->name, error); + return_if_nok (error); + *type = mono_reflection_type_get_handle ((MonoReflectionType*)fb->type, error); + } else { + MonoReflectionField *f = (MonoReflectionField *)field; + *name = g_strdup (mono_field_get_name (f->field)); + *type = f->field->type; + } +} + +#else /* DISABLE_REFLECTION_EMIT */ + +static gboolean +is_sre_type_builder (MonoClass *klass) +{ + return FALSE; +} + +static gboolean +is_sre_generic_instance (MonoClass *klass) +{ + return FALSE; +} + +gboolean +mono_is_sre_ctor_builder (MonoClass *klass) +{ + return FALSE; +} + +gboolean +mono_is_sre_method_on_tb_inst (MonoClass *klass) +{ + return FALSE; +} + +gboolean +mono_is_sre_ctor_on_tb_inst (MonoClass *klass) +{ + return FALSE; +} + +#endif /* !DISABLE_REFLECTION_EMIT */ + + +static gboolean +is_sr_mono_field (MonoClass *klass) +{ + check_corlib_type_cached (klass, "System.Reflection", "MonoField"); +} + +gboolean +mono_is_sr_mono_property (MonoClass *klass) +{ + check_corlib_type_cached (klass, "System.Reflection", "MonoProperty"); +} + +static gboolean +is_sr_mono_method (MonoClass *klass) +{ + check_corlib_type_cached (klass, "System.Reflection", "MonoMethod"); +} + +gboolean +mono_is_sr_mono_cmethod (MonoClass *klass) +{ + check_corlib_type_cached (klass, "System.Reflection", "MonoCMethod"); +} + +gboolean +mono_class_is_reflection_method_or_constructor (MonoClass *klass) +{ + return is_sr_mono_method (klass) || mono_is_sr_mono_cmethod (klass); +} + +gboolean +mono_is_sre_type_builder (MonoClass *klass) +{ + return is_sre_type_builder (klass); +} + +gboolean +mono_is_sre_generic_instance (MonoClass *klass) +{ + return is_sre_generic_instance (klass); +} + + + +/** + * encode_cattr_value: + * Encode a value in a custom attribute stream of bytes. + * The value to encode is either supplied as an object in argument val + * (valuetypes are boxed), or as a pointer to the data in the + * argument argval. + * @type represents the type of the value + * @buffer is the start of the buffer + * @p the current position in the buffer + * @buflen contains the size of the buffer and is used to return the new buffer size + * if this needs to be realloced. + * @retbuffer and @retp return the start and the position of the buffer + * @error set on error. + */ +static void +encode_cattr_value (MonoAssembly *assembly, char *buffer, char *p, char **retbuffer, char **retp, guint32 *buflen, MonoType *type, MonoObject *arg, char *argval, MonoError *error) +{ + MonoTypeEnum simple_type; + + error_init (error); + if ((p-buffer) + 10 >= *buflen) { + char *newbuf; + *buflen *= 2; + newbuf = (char *)g_realloc (buffer, *buflen); + p = newbuf + (p-buffer); + buffer = newbuf; + } + if (!argval) + argval = ((char*)arg + sizeof (MonoObject)); + simple_type = type->type; +handle_enum: + switch (simple_type) { + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_U1: + case MONO_TYPE_I1: + *p++ = *argval; + break; + case MONO_TYPE_CHAR: + case MONO_TYPE_U2: + case MONO_TYPE_I2: + swap_with_size (p, argval, 2, 1); + p += 2; + break; + case MONO_TYPE_U4: + case MONO_TYPE_I4: + case MONO_TYPE_R4: + swap_with_size (p, argval, 4, 1); + p += 4; + break; + case MONO_TYPE_R8: + swap_with_size (p, argval, 8, 1); + p += 8; + break; + case MONO_TYPE_U8: + case MONO_TYPE_I8: + swap_with_size (p, argval, 8, 1); + p += 8; + break; + case MONO_TYPE_VALUETYPE: + if (type->data.klass->enumtype) { + simple_type = mono_class_enum_basetype (type->data.klass)->type; + goto handle_enum; + } else { + g_warning ("generic valutype %s not handled in custom attr value decoding", type->data.klass->name); + } + break; + case MONO_TYPE_STRING: { + char *str; + guint32 slen; + if (!arg) { + *p++ = 0xFF; + break; + } + str = mono_string_to_utf8_checked ((MonoString*)arg, error); + return_if_nok (error); + slen = strlen (str); + if ((p-buffer) + 10 + slen >= *buflen) { + char *newbuf; + *buflen *= 2; + *buflen += slen; + newbuf = (char *)g_realloc (buffer, *buflen); + p = newbuf + (p-buffer); + buffer = newbuf; + } + mono_metadata_encode_value (slen, p, &p); + memcpy (p, str, slen); + p += slen; + g_free (str); + break; + } + case MONO_TYPE_CLASS: { + char *str; + guint32 slen; + MonoType *arg_type; + if (!arg) { + *p++ = 0xFF; + break; + } +handle_type: + arg_type = mono_reflection_type_get_handle ((MonoReflectionType*)arg, error); + return_if_nok (error); + + str = type_get_qualified_name (arg_type, NULL); + slen = strlen (str); + if ((p-buffer) + 10 + slen >= *buflen) { + char *newbuf; + *buflen *= 2; + *buflen += slen; + newbuf = (char *)g_realloc (buffer, *buflen); + p = newbuf + (p-buffer); + buffer = newbuf; + } + mono_metadata_encode_value (slen, p, &p); + memcpy (p, str, slen); + p += slen; + g_free (str); + break; + } + case MONO_TYPE_SZARRAY: { + int len, i; + MonoClass *eclass, *arg_eclass; + + if (!arg) { + *p++ = 0xff; *p++ = 0xff; *p++ = 0xff; *p++ = 0xff; + break; + } + len = mono_array_length ((MonoArray*)arg); + *p++ = len & 0xff; + *p++ = (len >> 8) & 0xff; + *p++ = (len >> 16) & 0xff; + *p++ = (len >> 24) & 0xff; + *retp = p; + *retbuffer = buffer; + eclass = type->data.klass; + arg_eclass = mono_object_class (arg)->element_class; + + if (!eclass) { + /* Happens when we are called from the MONO_TYPE_OBJECT case below */ + eclass = mono_defaults.object_class; + } + if (eclass == mono_defaults.object_class && arg_eclass->valuetype) { + char *elptr = mono_array_addr ((MonoArray*)arg, char, 0); + int elsize = mono_class_array_element_size (arg_eclass); + for (i = 0; i < len; ++i) { + encode_cattr_value (assembly, buffer, p, &buffer, &p, buflen, &arg_eclass->byval_arg, NULL, elptr, error); + return_if_nok (error); + elptr += elsize; + } + } else if (eclass->valuetype && arg_eclass->valuetype) { + char *elptr = mono_array_addr ((MonoArray*)arg, char, 0); + int elsize = mono_class_array_element_size (eclass); + for (i = 0; i < len; ++i) { + encode_cattr_value (assembly, buffer, p, &buffer, &p, buflen, &eclass->byval_arg, NULL, elptr, error); + return_if_nok (error); + elptr += elsize; + } + } else { + for (i = 0; i < len; ++i) { + encode_cattr_value (assembly, buffer, p, &buffer, &p, buflen, &eclass->byval_arg, mono_array_get ((MonoArray*)arg, MonoObject*, i), NULL, error); + return_if_nok (error); + } + } + break; + } + case MONO_TYPE_OBJECT: { + MonoClass *klass; + char *str; + guint32 slen; + + /* + * The parameter type is 'object' but the type of the actual + * argument is not. So we have to add type information to the blob + * too. This is completely undocumented in the spec. + */ + + if (arg == NULL) { + *p++ = MONO_TYPE_STRING; // It's same hack as MS uses + *p++ = 0xFF; + break; + } + + klass = mono_object_class (arg); + + if (mono_object_isinst_checked (arg, mono_defaults.systemtype_class, error)) { + *p++ = 0x50; + goto handle_type; + } else { + return_if_nok (error); + } + + if (klass->enumtype) { + *p++ = 0x55; + } else if (klass == mono_defaults.string_class) { + simple_type = MONO_TYPE_STRING; + *p++ = 0x0E; + goto handle_enum; + } else if (klass->rank == 1) { + *p++ = 0x1D; + if (klass->element_class->byval_arg.type == MONO_TYPE_OBJECT) + /* See Partition II, Appendix B3 */ + *p++ = 0x51; + else + *p++ = klass->element_class->byval_arg.type; + encode_cattr_value (assembly, buffer, p, &buffer, &p, buflen, &klass->byval_arg, arg, NULL, error); + return_if_nok (error); + break; + } else if (klass->byval_arg.type >= MONO_TYPE_BOOLEAN && klass->byval_arg.type <= MONO_TYPE_R8) { + *p++ = simple_type = klass->byval_arg.type; + goto handle_enum; + } else { + g_error ("unhandled type in custom attr"); + } + str = type_get_qualified_name (mono_class_get_type(klass), NULL); + slen = strlen (str); + if ((p-buffer) + 10 + slen >= *buflen) { + char *newbuf; + *buflen *= 2; + *buflen += slen; + newbuf = (char *)g_realloc (buffer, *buflen); + p = newbuf + (p-buffer); + buffer = newbuf; + } + mono_metadata_encode_value (slen, p, &p); + memcpy (p, str, slen); + p += slen; + g_free (str); + simple_type = mono_class_enum_basetype (klass)->type; + goto handle_enum; + } + default: + g_error ("type 0x%02x not yet supported in custom attr encoder", simple_type); + } + *retp = p; + *retbuffer = buffer; +} + +static void +encode_field_or_prop_type (MonoType *type, char *p, char **retp) +{ + if (type->type == MONO_TYPE_VALUETYPE && type->data.klass->enumtype) { + char *str = type_get_qualified_name (type, NULL); + int slen = strlen (str); + + *p++ = 0x55; + /* + * This seems to be optional... + * *p++ = 0x80; + */ + mono_metadata_encode_value (slen, p, &p); + memcpy (p, str, slen); + p += slen; + g_free (str); + } else if (type->type == MONO_TYPE_OBJECT) { + *p++ = 0x51; + } else if (type->type == MONO_TYPE_CLASS) { + /* it should be a type: encode_cattr_value () has the check */ + *p++ = 0x50; + } else { + mono_metadata_encode_value (type->type, p, &p); + if (type->type == MONO_TYPE_SZARRAY) + /* See the examples in Partition VI, Annex B */ + encode_field_or_prop_type (&type->data.klass->byval_arg, p, &p); + } + + *retp = p; +} + +#ifndef DISABLE_REFLECTION_EMIT +static void +encode_named_val (MonoReflectionAssembly *assembly, char *buffer, char *p, char **retbuffer, char **retp, guint32 *buflen, MonoType *type, char *name, MonoObject *value, MonoError *error) +{ + int len; + + error_init (error); + + /* Preallocate a large enough buffer */ + if (type->type == MONO_TYPE_VALUETYPE && type->data.klass->enumtype) { + char *str = type_get_qualified_name (type, NULL); + len = strlen (str); + g_free (str); + } else if (type->type == MONO_TYPE_SZARRAY && type->data.klass->enumtype) { + char *str = type_get_qualified_name (&type->data.klass->byval_arg, NULL); + len = strlen (str); + g_free (str); + } else { + len = 0; + } + len += strlen (name); + + if ((p-buffer) + 20 + len >= *buflen) { + char *newbuf; + *buflen *= 2; + *buflen += len; + newbuf = (char *)g_realloc (buffer, *buflen); + p = newbuf + (p-buffer); + buffer = newbuf; + } + + encode_field_or_prop_type (type, p, &p); + + len = strlen (name); + mono_metadata_encode_value (len, p, &p); + memcpy (p, name, len); + p += len; + encode_cattr_value (assembly->assembly, buffer, p, &buffer, &p, buflen, type, value, NULL, error); + return_if_nok (error); + *retp = p; + *retbuffer = buffer; +} + +/** + * mono_reflection_get_custom_attrs_blob: + * \param ctor custom attribute constructor + * \param ctorArgs arguments o the constructor + * \param properties + * \param propValues + * \param fields + * \param fieldValues + * Creates the blob of data that needs to be saved in the metadata and that represents + * the custom attributed described by \p ctor, \p ctorArgs etc. + * \returns a \c Byte array representing the blob of data. + */ +MonoArray* +mono_reflection_get_custom_attrs_blob (MonoReflectionAssembly *assembly, MonoObject *ctor, MonoArray *ctorArgs, MonoArray *properties, MonoArray *propValues, MonoArray *fields, MonoArray* fieldValues) +{ + MonoError error; + MonoArray *result = mono_reflection_get_custom_attrs_blob_checked (assembly, ctor, ctorArgs, properties, propValues, fields, fieldValues, &error); + mono_error_cleanup (&error); + return result; +} + +/** + * mono_reflection_get_custom_attrs_blob_checked: + * \param ctor custom attribute constructor + * \param ctorArgs arguments o the constructor + * \param properties + * \param propValues + * \param fields + * \param fieldValues + * \param error set on error + * Creates the blob of data that needs to be saved in the metadata and that represents + * the custom attributed described by \p ctor, \p ctorArgs etc. + * \returns a \c Byte array representing the blob of data. On failure returns NULL and sets \p error. + */ +MonoArray* +mono_reflection_get_custom_attrs_blob_checked (MonoReflectionAssembly *assembly, MonoObject *ctor, MonoArray *ctorArgs, MonoArray *properties, MonoArray *propValues, MonoArray *fields, MonoArray* fieldValues, MonoError *error) +{ + MonoArray *result = NULL; + MonoMethodSignature *sig; + MonoObject *arg; + char *buffer, *p; + guint32 buflen, i; + + error_init (error); + + if (strcmp (ctor->vtable->klass->name, "MonoCMethod")) { + /* sig is freed later so allocate it in the heap */ + sig = ctor_builder_to_signature_raw (NULL, (MonoReflectionCtorBuilder*)ctor, error); /* FIXME use handles */ + if (!is_ok (error)) { + g_free (sig); + return NULL; + } + } else { + sig = mono_method_signature (((MonoReflectionMethod*)ctor)->method); + } + + g_assert (mono_array_length (ctorArgs) == sig->param_count); + buflen = 256; + p = buffer = (char *)g_malloc (buflen); + /* write the prolog */ + *p++ = 1; + *p++ = 0; + for (i = 0; i < sig->param_count; ++i) { + arg = mono_array_get (ctorArgs, MonoObject*, i); + encode_cattr_value (assembly->assembly, buffer, p, &buffer, &p, &buflen, sig->params [i], arg, NULL, error); + goto_if_nok (error, leave); + } + i = 0; + if (properties) + i += mono_array_length (properties); + if (fields) + i += mono_array_length (fields); + *p++ = i & 0xff; + *p++ = (i >> 8) & 0xff; + if (properties) { + MonoObject *prop; + for (i = 0; i < mono_array_length (properties); ++i) { + MonoType *ptype; + char *pname; + + prop = (MonoObject *)mono_array_get (properties, gpointer, i); + get_prop_name_and_type (prop, &pname, &ptype, error); + goto_if_nok (error, leave); + *p++ = 0x54; /* PROPERTY signature */ + encode_named_val (assembly, buffer, p, &buffer, &p, &buflen, ptype, pname, (MonoObject*)mono_array_get (propValues, gpointer, i), error); + g_free (pname); + goto_if_nok (error, leave); + } + } + + if (fields) { + MonoObject *field; + for (i = 0; i < mono_array_length (fields); ++i) { + MonoType *ftype; + char *fname; + + field = (MonoObject *)mono_array_get (fields, gpointer, i); + get_field_name_and_type (field, &fname, &ftype, error); + goto_if_nok (error, leave); + *p++ = 0x53; /* FIELD signature */ + encode_named_val (assembly, buffer, p, &buffer, &p, &buflen, ftype, fname, (MonoObject*)mono_array_get (fieldValues, gpointer, i), error); + g_free (fname); + goto_if_nok (error, leave); + } + } + + g_assert (p - buffer <= buflen); + buflen = p - buffer; + result = mono_array_new_checked (mono_domain_get (), mono_defaults.byte_class, buflen, error); + goto_if_nok (error, leave); + p = mono_array_addr (result, char, 0); + memcpy (p, buffer, buflen); +leave: + g_free (buffer); + if (strcmp (ctor->vtable->klass->name, "MonoCMethod")) + g_free (sig); + return result; +} + +static gboolean +reflection_setup_class_hierarchy (GHashTable *unparented, MonoError *error) +{ + error_init (error); + + mono_loader_lock (); + + MonoType *parent_type; + MonoType *child_type; + GHashTableIter iter; + + g_hash_table_iter_init (&iter, unparented); + + while (g_hash_table_iter_next (&iter, (gpointer *) &child_type, (gpointer *) &parent_type)) { + MonoClass *child_class = mono_class_from_mono_type (child_type); + if (parent_type != NULL) { + MonoClass *parent_class = mono_class_from_mono_type (parent_type); + child_class->parent = NULL; + /* fool mono_class_setup_parent */ + child_class->supertypes = NULL; + mono_class_setup_parent (child_class, parent_class); + } else if (strcmp (child_class->name, "Object") == 0 && strcmp (child_class->name_space, "System") == 0) { + const char *old_n = child_class->name; + /* trick to get relative numbering right when compiling corlib */ + child_class->name = "BuildingObject"; + mono_class_setup_parent (child_class, mono_defaults.object_class); + child_class->name = old_n; + } + mono_class_setup_mono_type (child_class); + mono_class_setup_supertypes (child_class); + } + + mono_loader_unlock (); + return is_ok (error); +} + +static gboolean +reflection_setup_internal_class_internal (MonoReflectionTypeBuilderHandle ref_tb, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + error_init (error); + + mono_loader_lock (); + + gint32 entering_state = MONO_HANDLE_GETVAL (MONO_HANDLE_CAST (MonoReflectionTypeBuilder, ref_tb), state); + if (entering_state != MonoTypeBuilderNew) { + g_assert (MONO_HANDLE_GETVAL (MONO_HANDLE_CAST (MonoReflectionType, ref_tb), type)); + goto leave; + } + + MONO_HANDLE_SETVAL (ref_tb, state, MonoTypeBuilderState, MonoTypeBuilderEntered); + MonoReflectionModuleBuilderHandle module_ref = MONO_HANDLE_NEW_GET (MonoReflectionModuleBuilder, ref_tb, module); + GHashTable *unparented_classes = MONO_HANDLE_GETVAL(module_ref, unparented_classes); + + // If this type is already setup, exit. We'll fix the parenting later + MonoType *type = MONO_HANDLE_GETVAL (MONO_HANDLE_CAST (MonoReflectionType, ref_tb), type); + if (type) + goto leave; + + MonoReflectionModuleBuilderHandle ref_module = MONO_HANDLE_NEW_GET (MonoReflectionModuleBuilder, ref_tb, module); + MonoDynamicImage *dynamic_image = MONO_HANDLE_GETVAL (ref_module, dynamic_image); + + MonoStringHandle ref_name = MONO_HANDLE_NEW_GET (MonoString, ref_tb, name); + MonoStringHandle ref_nspace = MONO_HANDLE_NEW_GET (MonoString, ref_tb, nspace); + + guint32 table_idx = MONO_HANDLE_GETVAL (ref_tb, table_idx); + /* + * The size calculation here warrants some explaining. + * reflection_setup_internal_class is called too early, well before we know whether the type will be a GTD or DEF, + * meaning we need to alloc enough space to morth a def into a gtd. + */ + MonoClass *klass = (MonoClass *)mono_image_alloc0 (&dynamic_image->image, MAX (sizeof (MonoClassDef), sizeof (MonoClassGtd))); + klass->class_kind = MONO_CLASS_DEF; + + klass->image = &dynamic_image->image; + + klass->inited = 1; /* we lie to the runtime */ + klass->name = mono_string_to_utf8_image (klass->image, ref_name, error); + goto_if_nok (error, leave); + klass->name_space = mono_string_to_utf8_image (klass->image, ref_nspace, error); + goto_if_nok (error, leave); + klass->type_token = MONO_TOKEN_TYPE_DEF | table_idx; + mono_class_set_flags (klass, MONO_HANDLE_GETVAL (ref_tb, attrs)); + + MONO_PROFILER_RAISE (class_loading, (klass)); + + klass->element_class = klass; + + g_assert (!mono_class_has_ref_info (klass)); + mono_class_set_ref_info (klass, MONO_HANDLE_CAST (MonoObject, ref_tb)); + + MonoReflectionTypeHandle ref_nesting_type = MONO_HANDLE_NEW_GET (MonoReflectionType, ref_tb, nesting_type); + /* Put into cache so mono_class_get_checked () will find it. + Skip nested types as those should not be available on the global scope. */ + if (MONO_HANDLE_IS_NULL (ref_nesting_type)) + mono_image_add_to_name_cache (klass->image, klass->name_space, klass->name, table_idx); + + /* + We must register all types as we cannot rely on the name_cache hashtable since we find the class + by performing a mono_class_get which does the full resolution. + + Working around this semantics would require us to write a lot of code for no clear advantage. + */ + mono_image_append_class_to_reflection_info_set (klass); + + mono_dynamic_image_register_token (dynamic_image, MONO_TOKEN_TYPE_DEF | table_idx, MONO_HANDLE_CAST (MonoObject, ref_tb), MONO_DYN_IMAGE_TOK_NEW); + + if ((!strcmp (klass->name, "ValueType") && !strcmp (klass->name_space, "System")) || + (!strcmp (klass->name, "Object") && !strcmp (klass->name_space, "System")) || + (!strcmp (klass->name, "Enum") && !strcmp (klass->name_space, "System"))) { + klass->instance_size = sizeof (MonoObject); + klass->size_inited = 1; + mono_class_setup_vtable_general (klass, NULL, 0, NULL); + } + + mono_class_setup_mono_type (klass); + + /* + * FIXME: handle interfaces. + */ + MonoReflectionTypeHandle ref_tb_type = MONO_HANDLE_CAST (MonoReflectionType, ref_tb); + MONO_HANDLE_SETVAL (ref_tb_type, type, MonoType*, &klass->byval_arg); + MONO_HANDLE_SETVAL (ref_tb, state, gint32, MonoTypeBuilderFinished); + + reflection_init_generic_class (ref_tb, error); + goto_if_nok (error, leave); + + // Do here so that the search inside of the parent can see the above type that's been set. + MonoReflectionTypeHandle ref_parent = MONO_HANDLE_NEW_GET (MonoReflectionType, ref_tb, parent); + MonoType *parent_type = NULL; + if (!MONO_HANDLE_IS_NULL (ref_parent)) { + MonoClass *parent_klass = mono_handle_class (ref_parent); + gboolean recursive_init = TRUE; + + if (is_sre_type_builder (parent_klass)) { + MonoTypeBuilderState parent_state = MONO_HANDLE_GETVAL (MONO_HANDLE_CAST (MonoReflectionTypeBuilder, ref_parent), state); + + if (parent_state != MonoTypeBuilderNew) { + // Initialize types reachable from parent recursively + // We'll fix the type hierarchy later + recursive_init = FALSE; + } + } + + if (recursive_init) { + // If we haven't encountered a cycle, force the creation of ref_parent's type + mono_reflection_type_handle_mono_type (ref_parent, error); + goto_if_nok (error, leave); + } + + parent_type = MONO_HANDLE_GETVAL (ref_parent, type); + + // If we failed to create the parent, fail the child + if (!parent_type) + goto leave; + } + + // Push the child type and parent type to process later + // Note: parent_type may be null. + g_assert (!g_hash_table_lookup (unparented_classes, &klass->byval_arg)); + g_hash_table_insert (unparented_classes, &klass->byval_arg, parent_type); + + if (!MONO_HANDLE_IS_NULL (ref_nesting_type)) { + if (!reflection_setup_internal_class (MONO_HANDLE_CAST (MonoReflectionTypeBuilder, ref_nesting_type), error)) + goto leave; + + MonoType *nesting_type = mono_reflection_type_handle_mono_type (ref_nesting_type, error); + goto_if_nok (error, leave); + klass->nested_in = mono_class_from_mono_type (nesting_type); + } + + /*g_print ("setup %s as %s (%p)\n", klass->name, ((MonoObject*)tb)->vtable->klass->name, tb);*/ + + MONO_PROFILER_RAISE (class_loaded, (klass)); + +leave: + mono_loader_unlock (); + HANDLE_FUNCTION_RETURN_VAL (is_ok (error)); +} + +/** + * reflection_init_generic_class: + * @tb: a TypeBuilder object + * @error: set on error + * + * Creates the generic class after all generic parameters have been added. + * On success returns TRUE, on failure returns FALSE and sets @error. + * + * This assumes that reflection_setup_internal_class has already set up + * ref_tb + */ +static gboolean +reflection_init_generic_class (MonoReflectionTypeBuilderHandle ref_tb, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + + error_init (error); + + MonoTypeBuilderState ref_state = MONO_HANDLE_GETVAL (ref_tb, state); + g_assert (ref_state == MonoTypeBuilderFinished); + + MonoType *type = MONO_HANDLE_GETVAL (MONO_HANDLE_CAST (MonoReflectionType, ref_tb), type); + MonoClass *klass = mono_class_from_mono_type (type); + + MonoArrayHandle generic_params = MONO_HANDLE_NEW_GET (MonoArray, ref_tb, generic_params); + int count = MONO_HANDLE_IS_NULL (generic_params) ? 0 : mono_array_handle_length (generic_params); + + if (count == 0) + goto leave; + + if (mono_class_try_get_generic_container (klass) != NULL) + goto leave; /* already setup */ + + MonoGenericContainer *generic_container = (MonoGenericContainer *)mono_image_alloc0 (klass->image, sizeof (MonoGenericContainer)); + + generic_container->owner.klass = klass; + generic_container->type_argc = count; + generic_container->type_params = (MonoGenericParamFull *)mono_image_alloc0 (klass->image, sizeof (MonoGenericParamFull) * count); + + klass->class_kind = MONO_CLASS_GTD; + mono_class_set_generic_container (klass, generic_container); + + + MonoReflectionGenericParamHandle ref_gparam = MONO_HANDLE_NEW (MonoReflectionGenericParam, NULL); + for (int i = 0; i < count; i++) { + MONO_HANDLE_ARRAY_GETREF (ref_gparam, generic_params, i); + MonoType *param_type = mono_reflection_type_handle_mono_type (MONO_HANDLE_CAST (MonoReflectionType, ref_gparam), error); + goto_if_nok (error, leave); + MonoGenericParamFull *param = (MonoGenericParamFull *) param_type->data.generic_param; + generic_container->type_params [i] = *param; + /*Make sure we are a diferent type instance */ + generic_container->type_params [i].param.owner = generic_container; + generic_container->type_params [i].info.pklass = NULL; + generic_container->type_params [i].info.flags = MONO_HANDLE_GETVAL (ref_gparam, attrs); + + g_assert (generic_container->type_params [i].param.owner); + } + + generic_container->context.class_inst = mono_get_shared_generic_inst (generic_container); + MonoGenericContext* context = &generic_container->context; + MonoType *canonical_inst = &((MonoClassGtd*)klass)->canonical_inst; + canonical_inst->type = MONO_TYPE_GENERICINST; + canonical_inst->data.generic_class = mono_metadata_lookup_generic_class (klass, context->class_inst, FALSE); + +leave: + HANDLE_FUNCTION_RETURN_VAL (is_ok (error)); +} + +static MonoMarshalSpec* +mono_marshal_spec_from_builder (MonoImage *image, MonoAssembly *assembly, + MonoReflectionMarshal *minfo, MonoError *error) +{ + MonoMarshalSpec *res; + + error_init (error); + + res = image_g_new0 (image, MonoMarshalSpec, 1); + res->native = (MonoMarshalNative)minfo->type; + + switch (minfo->type) { + case MONO_NATIVE_LPARRAY: + res->data.array_data.elem_type = (MonoMarshalNative)minfo->eltype; + if (minfo->has_size) { + res->data.array_data.param_num = minfo->param_num; + res->data.array_data.num_elem = minfo->count; + res->data.array_data.elem_mult = minfo->param_num == -1 ? 0 : 1; + } + else { + res->data.array_data.param_num = -1; + res->data.array_data.num_elem = -1; + res->data.array_data.elem_mult = -1; + } + break; + + case MONO_NATIVE_BYVALTSTR: + case MONO_NATIVE_BYVALARRAY: + res->data.array_data.num_elem = minfo->count; + break; + + case MONO_NATIVE_CUSTOM: + if (minfo->marshaltyperef) { + MonoType *marshaltyperef = mono_reflection_type_get_handle ((MonoReflectionType*)minfo->marshaltyperef, error); + if (!is_ok (error)) { + image_g_free (image, res); + return NULL; + } + res->data.custom_data.custom_name = + type_get_fully_qualified_name (marshaltyperef); + } + if (minfo->mcookie) { + res->data.custom_data.cookie = mono_string_to_utf8_checked (minfo->mcookie, error); + if (!is_ok (error)) { + image_g_free (image, res); + return NULL; + } + } + break; + + default: + break; + } + + return res; +} +#endif /* !DISABLE_REFLECTION_EMIT */ + +MonoReflectionMarshalAsAttributeHandle +mono_reflection_marshal_as_attribute_from_marshal_spec (MonoDomain *domain, MonoClass *klass, + MonoMarshalSpec *spec, MonoError *error) +{ + error_init (error); + + MonoReflectionMarshalAsAttributeHandle minfo = MONO_HANDLE_NEW (MonoReflectionMarshalAsAttribute, mono_object_new_checked (domain, mono_class_get_marshal_as_attribute_class (), error)); + goto_if_nok (error, fail); + guint32 utype = spec->native; + MONO_HANDLE_SETVAL (minfo, utype, guint32, utype); + + switch (utype) { + case MONO_NATIVE_LPARRAY: + MONO_HANDLE_SETVAL (minfo, array_subtype, guint32, spec->data.array_data.elem_type); + MONO_HANDLE_SETVAL (minfo, size_const, gint32, spec->data.array_data.num_elem); + if (spec->data.array_data.param_num != -1) + MONO_HANDLE_SETVAL (minfo, size_param_index, gint16, spec->data.array_data.param_num); + break; + + case MONO_NATIVE_BYVALTSTR: + case MONO_NATIVE_BYVALARRAY: + MONO_HANDLE_SETVAL (minfo, size_const, gint32, spec->data.array_data.num_elem); + break; + + case MONO_NATIVE_CUSTOM: + if (spec->data.custom_data.custom_name) { + MonoType *mtype = mono_reflection_type_from_name_checked (spec->data.custom_data.custom_name, klass->image, error); + goto_if_nok (error, fail); + + if (mtype) { + MonoReflectionTypeHandle rt = mono_type_get_object_handle (domain, mtype, error); + goto_if_nok (error, fail); + + MONO_HANDLE_SET (minfo, marshal_type_ref, rt); + } + + MonoStringHandle custom_name = mono_string_new_handle (domain, spec->data.custom_data.custom_name, error); + goto_if_nok (error, fail); + MONO_HANDLE_SET (minfo, marshal_type, custom_name); + } + if (spec->data.custom_data.cookie) { + MonoStringHandle cookie = mono_string_new_handle (domain, spec->data.custom_data.cookie, error); + goto_if_nok (error, fail); + MONO_HANDLE_SET (minfo, marshal_cookie, cookie); + } + break; + + default: + break; + } + + return minfo; +fail: + return MONO_HANDLE_NEW (MonoReflectionMarshalAsAttribute, NULL); +} + +#ifndef DISABLE_REFLECTION_EMIT +static MonoMethod* +reflection_methodbuilder_to_mono_method (MonoClass *klass, + ReflectionMethodBuilder *rmb, + MonoMethodSignature *sig, + MonoError *error) +{ + MonoMethod *m; + MonoMethodWrapper *wrapperm; + MonoMarshalSpec **specs; + MonoReflectionMethodAux *method_aux; + MonoImage *image; + gboolean dynamic; + int i; + + error_init (error); + /* + * Methods created using a MethodBuilder should have their memory allocated + * inside the image mempool, while dynamic methods should have their memory + * malloc'd. + */ + dynamic = rmb->refs != NULL; + image = dynamic ? NULL : klass->image; + + if (!dynamic) + g_assert (!mono_class_is_ginst (klass)); + + mono_loader_lock (); + + if ((rmb->attrs & METHOD_ATTRIBUTE_PINVOKE_IMPL) || + (rmb->iattrs & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)) + m = (MonoMethod *)image_g_new0 (image, MonoMethodPInvoke, 1); + else + m = (MonoMethod *)image_g_new0 (image, MonoDynamicMethod, 1); + + wrapperm = (MonoMethodWrapper*)m; + + m->dynamic = dynamic; + m->slot = -1; + m->flags = rmb->attrs; + m->iflags = rmb->iattrs; + m->name = mono_string_to_utf8_image_ignore (image, rmb->name); + m->klass = klass; + m->signature = sig; + m->sre_method = TRUE; + m->skip_visibility = rmb->skip_visibility; + if (rmb->table_idx) + m->token = MONO_TOKEN_METHOD_DEF | (*rmb->table_idx); + + if (m->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) { + if (klass == mono_defaults.string_class && !strcmp (m->name, ".ctor")) + m->string_ctor = 1; + + m->signature->pinvoke = 1; + } else if (m->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) { + m->signature->pinvoke = 1; + + method_aux = image_g_new0 (image, MonoReflectionMethodAux, 1); + + method_aux->dllentry = rmb->dllentry ? string_to_utf8_image_raw (image, rmb->dllentry, error) : image_strdup (image, m->name); + mono_error_assert_ok (error); + method_aux->dll = string_to_utf8_image_raw (image, rmb->dll, error); + mono_error_assert_ok (error); + + ((MonoMethodPInvoke*)m)->piflags = (rmb->native_cc << 8) | (rmb->charset ? (rmb->charset - 1) * 2 : 0) | rmb->extra_flags; + + if (image_is_dynamic (klass->image)) + g_hash_table_insert (((MonoDynamicImage*)klass->image)->method_aux_hash, m, method_aux); + + mono_loader_unlock (); + + return m; + } else if (!(m->flags & METHOD_ATTRIBUTE_ABSTRACT) && + !(m->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME)) { + MonoMethodHeader *header; + guint32 code_size; + gint32 max_stack, i; + gint32 num_locals = 0; + gint32 num_clauses = 0; + guint8 *code; + + if (rmb->ilgen) { + code = mono_array_addr (rmb->ilgen->code, guint8, 0); + code_size = rmb->ilgen->code_len; + max_stack = rmb->ilgen->max_stack; + num_locals = rmb->ilgen->locals ? mono_array_length (rmb->ilgen->locals) : 0; + if (rmb->ilgen->ex_handlers) + num_clauses = mono_reflection_method_count_clauses (rmb->ilgen); + } else { + if (rmb->code) { + code = mono_array_addr (rmb->code, guint8, 0); + code_size = mono_array_length (rmb->code); + /* we probably need to run a verifier on the code... */ + max_stack = 8; + } + else { + code = NULL; + code_size = 0; + max_stack = 8; + } + } + + header = (MonoMethodHeader *)mono_image_g_malloc0 (image, MONO_SIZEOF_METHOD_HEADER + num_locals * sizeof (MonoType*)); + header->code_size = code_size; + header->code = (const unsigned char *)image_g_malloc (image, code_size); + memcpy ((char*)header->code, code, code_size); + header->max_stack = max_stack; + header->init_locals = rmb->init_locals; + header->num_locals = num_locals; + + for (i = 0; i < num_locals; ++i) { + MonoReflectionLocalBuilder *lb = + mono_array_get (rmb->ilgen->locals, MonoReflectionLocalBuilder*, i); + + header->locals [i] = image_g_new0 (image, MonoType, 1); + MonoType *type = mono_reflection_type_get_handle ((MonoReflectionType*)lb->type, error); + mono_error_assert_ok (error); + memcpy (header->locals [i], type, MONO_SIZEOF_TYPE); + } + + header->num_clauses = num_clauses; + if (num_clauses) { + header->clauses = method_encode_clauses (image, (MonoDynamicImage*)klass->image, + rmb->ilgen, num_clauses, error); + mono_error_assert_ok (error); + } + + wrapperm->header = header; + MonoDynamicMethod *dm = (MonoDynamicMethod*)wrapperm; + dm->assembly = klass->image->assembly; + } + + if (rmb->generic_params) { + int count = mono_array_length (rmb->generic_params); + MonoGenericContainer *container; + + container = (MonoGenericContainer *)mono_image_alloc0 (klass->image, sizeof (MonoGenericContainer)); + container->is_method = TRUE; + container->is_anonymous = FALSE; + container->type_argc = count; + container->type_params = image_g_new0 (image, MonoGenericParamFull, count); + container->owner.method = m; + + m->is_generic = TRUE; + mono_method_set_generic_container (m, container); + + for (i = 0; i < count; i++) { + MonoReflectionGenericParam *gp = + mono_array_get (rmb->generic_params, MonoReflectionGenericParam*, i); + MonoType *gp_type = mono_reflection_type_get_handle ((MonoReflectionType*)gp, error); + mono_error_assert_ok (error); + MonoGenericParamFull *param = (MonoGenericParamFull *) gp_type->data.generic_param; + container->type_params [i] = *param; + container->type_params [i].param.owner = container; + + gp->type.type->data.generic_param = (MonoGenericParam*)&container->type_params [i]; + + MonoClass *gklass = mono_class_from_mono_type (gp_type); + gklass->wastypebuilder = TRUE; + } + + /* + * The method signature might have pointers to generic parameters that belong to other methods. + * This is a valid SRE case, but the resulting method signature must be encoded using the proper + * generic parameters. + */ + for (i = 0; i < m->signature->param_count; ++i) { + MonoType *t = m->signature->params [i]; + if (t->type == MONO_TYPE_MVAR) { + MonoGenericParam *gparam = t->data.generic_param; + if (gparam->num < count) { + m->signature->params [i] = mono_metadata_type_dup (image, m->signature->params [i]); + m->signature->params [i]->data.generic_param = mono_generic_container_get_param (container, gparam->num); + } + + } + } + + if (mono_class_is_gtd (klass)) { + container->parent = mono_class_get_generic_container (klass); + container->context.class_inst = mono_class_get_generic_container (klass)->context.class_inst; + } + container->context.method_inst = mono_get_shared_generic_inst (container); + } + + if (rmb->refs) { + MonoMethodWrapper *mw = (MonoMethodWrapper*)m; + int i; + void **data; + + m->wrapper_type = MONO_WRAPPER_DYNAMIC_METHOD; + + mw->method_data = data = image_g_new (image, gpointer, rmb->nrefs + 1); + data [0] = GUINT_TO_POINTER (rmb->nrefs); + for (i = 0; i < rmb->nrefs; ++i) + data [i + 1] = rmb->refs [i]; + } + + method_aux = NULL; + + /* Parameter info */ + if (rmb->pinfo) { + if (!method_aux) + method_aux = image_g_new0 (image, MonoReflectionMethodAux, 1); + method_aux->param_names = image_g_new0 (image, char *, mono_method_signature (m)->param_count + 1); + for (i = 0; i <= m->signature->param_count; ++i) { + MonoReflectionParamBuilder *pb; + if ((pb = mono_array_get (rmb->pinfo, MonoReflectionParamBuilder*, i))) { + if ((i > 0) && (pb->attrs)) { + /* Make a copy since it might point to a shared type structure */ + m->signature->params [i - 1] = mono_metadata_type_dup (klass->image, m->signature->params [i - 1]); + m->signature->params [i - 1]->attrs = pb->attrs; + } + + if (pb->attrs & PARAM_ATTRIBUTE_HAS_DEFAULT) { + MonoDynamicImage *assembly; + guint32 idx, len; + MonoTypeEnum def_type; + char *p; + const char *p2; + + if (!method_aux->param_defaults) { + method_aux->param_defaults = image_g_new0 (image, guint8*, m->signature->param_count + 1); + method_aux->param_default_types = image_g_new0 (image, guint32, m->signature->param_count + 1); + } + assembly = (MonoDynamicImage*)klass->image; + idx = mono_dynimage_encode_constant (assembly, pb->def_value, &def_type); + /* Copy the data from the blob since it might get realloc-ed */ + p = assembly->blob.data + idx; + len = mono_metadata_decode_blob_size (p, &p2); + len += p2 - p; + method_aux->param_defaults [i] = (uint8_t *)image_g_malloc (image, len); + method_aux->param_default_types [i] = def_type; + memcpy ((gpointer)method_aux->param_defaults [i], p, len); + } + + if (pb->name) { + method_aux->param_names [i] = string_to_utf8_image_raw (image, pb->name, error); + mono_error_assert_ok (error); + } + if (pb->cattrs) { + if (!method_aux->param_cattr) + method_aux->param_cattr = image_g_new0 (image, MonoCustomAttrInfo*, m->signature->param_count + 1); + method_aux->param_cattr [i] = mono_custom_attrs_from_builders (image, klass->image, pb->cattrs); + } + } + } + } + + /* Parameter marshalling */ + specs = NULL; + if (rmb->pinfo) + for (i = 0; i < mono_array_length (rmb->pinfo); ++i) { + MonoReflectionParamBuilder *pb; + if ((pb = mono_array_get (rmb->pinfo, MonoReflectionParamBuilder*, i))) { + if (pb->marshal_info) { + if (specs == NULL) + specs = image_g_new0 (image, MonoMarshalSpec*, sig->param_count + 1); + specs [pb->position] = + mono_marshal_spec_from_builder (image, klass->image->assembly, pb->marshal_info, error); + if (!is_ok (error)) { + mono_loader_unlock (); + image_g_free (image, specs); + /* FIXME: if image is NULL, this leaks all the other stuff we alloc'd in this function */ + return NULL; + } + } + } + } + if (specs != NULL) { + if (!method_aux) + method_aux = image_g_new0 (image, MonoReflectionMethodAux, 1); + method_aux->param_marshall = specs; + } + + if (image_is_dynamic (klass->image) && method_aux) + g_hash_table_insert (((MonoDynamicImage*)klass->image)->method_aux_hash, m, method_aux); + + mono_loader_unlock (); + + return m; +} + +static MonoMethod* +ctorbuilder_to_mono_method (MonoClass *klass, MonoReflectionCtorBuilder* mb, MonoError *error) +{ + ReflectionMethodBuilder rmb; + MonoMethodSignature *sig; + + mono_loader_lock (); + + if (!mono_reflection_methodbuilder_from_ctor_builder (&rmb, mb, error)) + return NULL; + + g_assert (klass->image != NULL); + sig = ctor_builder_to_signature_raw (klass->image, mb, error); /* FIXME use handles */ + mono_loader_unlock (); + return_val_if_nok (error, NULL); + + mb->mhandle = reflection_methodbuilder_to_mono_method (klass, &rmb, sig, error); + return_val_if_nok (error, NULL); + mono_save_custom_attrs (klass->image, mb->mhandle, mb->cattrs); + + if (!((MonoDynamicImage*)(MonoDynamicImage*)klass->image)->save) { + /* ilgen is no longer needed */ + mb->ilgen = NULL; + } + + return mb->mhandle; +} + +static MonoMethod* +methodbuilder_to_mono_method (MonoClass *klass, MonoReflectionMethodBuilderHandle ref_mb, MonoError *error) +{ + ReflectionMethodBuilder rmb; + MonoMethodSignature *sig; + + error_init (error); + + mono_loader_lock (); + + MonoReflectionMethodBuilder *mb = MONO_HANDLE_RAW (ref_mb); /* FIXME use handles */ + if (!mono_reflection_methodbuilder_from_method_builder (&rmb, mb, error)) + return NULL; + + g_assert (klass->image != NULL); + sig = method_builder_to_signature (klass->image, ref_mb, error); + mono_loader_unlock (); + return_val_if_nok (error, NULL); + + MonoMethod *method = reflection_methodbuilder_to_mono_method (klass, &rmb, sig, error); + return_val_if_nok (error, NULL); + MONO_HANDLE_SETVAL (ref_mb, mhandle, MonoMethod*, method); + mono_save_custom_attrs (klass->image, method, mb->cattrs); + + if (!((MonoDynamicImage*)(MonoDynamicImage*)klass->image)->save) + /* ilgen is no longer needed */ + mb->ilgen = NULL; + return method; +} + +static MonoMethod* +methodbuilder_to_mono_method_raw (MonoClass *klass, MonoReflectionMethodBuilder* mb_raw, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); /* FIXME change callers of methodbuilder_to_mono_method_raw to use handles */ + error_init (error); + MONO_HANDLE_DCL (MonoReflectionMethodBuilder, mb); + MonoMethod *result = methodbuilder_to_mono_method (klass, mb, error); + HANDLE_FUNCTION_RETURN_VAL (result); +} + +#endif + +#ifndef DISABLE_REFLECTION_EMIT + +/** + * fix_partial_generic_class: + * @klass: a generic instantiation MonoClass + * @error: set on error + * + * Assumes that the generic container of @klass has its vtable + * initialized, and updates the parent class, interfaces, methods and + * fields of @klass by inflating the types using the generic context. + * + * On success returns TRUE, on failure returns FALSE and sets @error. + * + */ +static gboolean +fix_partial_generic_class (MonoClass *klass, MonoError *error) +{ + MonoClass *gklass = mono_class_get_generic_class (klass)->container_class; + int i; + + error_init (error); + + if (klass->wastypebuilder) + return TRUE; + + if (klass->parent != gklass->parent) { + MonoType *parent_type = mono_class_inflate_generic_type_checked (&gklass->parent->byval_arg, &mono_class_get_generic_class (klass)->context, error); + if (mono_error_ok (error)) { + MonoClass *parent = mono_class_from_mono_type (parent_type); + mono_metadata_free_type (parent_type); + if (parent != klass->parent) { + /*fool mono_class_setup_parent*/ + klass->supertypes = NULL; + mono_class_setup_parent (klass, parent); + } + } else { + if (gklass->wastypebuilder) + klass->wastypebuilder = TRUE; + return FALSE; + } + } + + if (!mono_class_get_generic_class (klass)->need_sync) + return TRUE; + + int mcount = mono_class_get_method_count (klass); + int gmcount = mono_class_get_method_count (gklass); + if (mcount != gmcount) { + mono_class_set_method_count (klass, gmcount); + klass->methods = (MonoMethod **)mono_image_alloc (klass->image, sizeof (MonoMethod*) * (gmcount + 1)); + + for (i = 0; i < gmcount; i++) { + klass->methods [i] = mono_class_inflate_generic_method_full_checked ( + gklass->methods [i], klass, mono_class_get_context (klass), error); + mono_error_assert_ok (error); + } + } + + if (klass->interface_count && klass->interface_count != gklass->interface_count) { + klass->interface_count = gklass->interface_count; + klass->interfaces = (MonoClass **)mono_image_alloc (klass->image, sizeof (MonoClass*) * gklass->interface_count); + klass->interfaces_packed = NULL; /*make setup_interface_offsets happy*/ + + for (i = 0; i < gklass->interface_count; ++i) { + MonoType *iface_type = mono_class_inflate_generic_type_checked (&gklass->interfaces [i]->byval_arg, mono_class_get_context (klass), error); + return_val_if_nok (error, FALSE); + + klass->interfaces [i] = mono_class_from_mono_type (iface_type); + mono_metadata_free_type (iface_type); + + if (!ensure_runtime_vtable (klass->interfaces [i], error)) + return FALSE; + } + klass->interfaces_inited = 1; + } + + int fcount = mono_class_get_field_count (klass); + int gfcount = mono_class_get_field_count (gklass); + if (fcount != gfcount) { + mono_class_set_field_count (klass, gfcount); + klass->fields = image_g_new0 (klass->image, MonoClassField, gfcount); + + for (i = 0; i < gfcount; i++) { + klass->fields [i] = gklass->fields [i]; + klass->fields [i].parent = klass; + klass->fields [i].type = mono_class_inflate_generic_type_checked (gklass->fields [i].type, mono_class_get_context (klass), error); + return_val_if_nok (error, FALSE); + } + } + + /*We can only finish with this klass once it's parent has as well*/ + if (gklass->wastypebuilder) + klass->wastypebuilder = TRUE; + return TRUE; +} + +/** + * ensure_generic_class_runtime_vtable: + * @klass a generic class + * @error set on error + * + * Ensures that the generic container of @klass has a vtable and + * returns TRUE on success. On error returns FALSE and sets @error. + */ +static gboolean +ensure_generic_class_runtime_vtable (MonoClass *klass, MonoError *error) +{ + MonoClass *gklass = mono_class_get_generic_class (klass)->container_class; + + error_init (error); + + if (!ensure_runtime_vtable (gklass, error)) + return FALSE; + + return fix_partial_generic_class (klass, error); +} + +/** + * ensure_runtime_vtable: + * @klass the class + * @error set on error + * + * Ensures that @klass has a vtable and returns TRUE on success. On + * error returns FALSE and sets @error. + */ +static gboolean +ensure_runtime_vtable (MonoClass *klass, MonoError *error) +{ + MonoReflectionTypeBuilder *tb = (MonoReflectionTypeBuilder *)mono_class_get_ref_info_raw (klass); /* FIXME use handles */ + int i, num, j; + + error_init (error); + + if (!image_is_dynamic (klass->image) || (!tb && !mono_class_is_ginst (klass)) || klass->wastypebuilder) + return TRUE; + if (klass->parent) + if (!ensure_runtime_vtable (klass->parent, error)) + return FALSE; + + if (tb) { + num = tb->ctors? mono_array_length (tb->ctors): 0; + num += tb->num_methods; + mono_class_set_method_count (klass, num); + klass->methods = (MonoMethod **)mono_image_alloc (klass->image, sizeof (MonoMethod*) * num); + num = tb->ctors? mono_array_length (tb->ctors): 0; + for (i = 0; i < num; ++i) { + MonoMethod *ctor = ctorbuilder_to_mono_method (klass, mono_array_get (tb->ctors, MonoReflectionCtorBuilder*, i), error); + if (!ctor) + return FALSE; + klass->methods [i] = ctor; + } + num = tb->num_methods; + j = i; + for (i = 0; i < num; ++i) { + MonoMethod *meth = methodbuilder_to_mono_method_raw (klass, mono_array_get (tb->methods, MonoReflectionMethodBuilder*, i), error); /* FIXME use handles */ + if (!meth) + return FALSE; + klass->methods [j++] = meth; + } + + if (tb->interfaces) { + klass->interface_count = mono_array_length (tb->interfaces); + klass->interfaces = (MonoClass **)mono_image_alloc (klass->image, sizeof (MonoClass*) * klass->interface_count); + for (i = 0; i < klass->interface_count; ++i) { + MonoType *iface = mono_type_array_get_and_resolve_raw (tb->interfaces, i, error); /* FIXME use handles */ + return_val_if_nok (error, FALSE); + klass->interfaces [i] = mono_class_from_mono_type (iface); + if (!ensure_runtime_vtable (klass->interfaces [i], error)) + return FALSE; + } + klass->interfaces_inited = 1; + } + } else if (mono_class_is_ginst (klass)){ + if (!ensure_generic_class_runtime_vtable (klass, error)) { + mono_class_set_type_load_failure (klass, "Could not initialize vtable for generic class due to: %s", mono_error_get_message (error)); + return FALSE; + } + } + + if (mono_class_is_interface (klass) && !mono_class_is_ginst (klass)) { + int slot_num = 0; + int mcount = mono_class_get_method_count (klass); + for (i = 0; i < mcount; ++i) { + MonoMethod *im = klass->methods [i]; + if (!(im->flags & METHOD_ATTRIBUTE_STATIC)) + im->slot = slot_num++; + } + + klass->interfaces_packed = NULL; /*make setup_interface_offsets happy*/ + mono_class_setup_interface_offsets (klass); + mono_class_setup_interface_id (klass); + } + + /* + * The generic vtable is needed even if image->run is not set since some + * runtime code like ves_icall_Type_GetMethodsByName depends on + * method->slot being defined. + */ + + /* + * tb->methods could not be freed since it is used for determining + * overrides during dynamic vtable construction. + */ + + return TRUE; +} + +static MonoMethod* +mono_reflection_method_get_handle (MonoObject *method, MonoError *error) +{ + error_init (error); + MonoClass *klass = mono_object_class (method); + if (is_sr_mono_method (klass)) { + MonoReflectionMethod *sr_method = (MonoReflectionMethod*)method; + return sr_method->method; + } + if (is_sre_method_builder (klass)) { + MonoReflectionMethodBuilder *mb = (MonoReflectionMethodBuilder*)method; + return mb->mhandle; + } + if (mono_is_sre_method_on_tb_inst (klass)) { + MonoClass *handle_class; + + MonoMethod *result = mono_reflection_resolve_object (NULL, method, &handle_class, NULL, error); + return_val_if_nok (error, NULL); + + return result; + } + + g_error ("Can't handle methods of type %s:%s", klass->name_space, klass->name); + return NULL; +} + +void +mono_reflection_get_dynamic_overrides (MonoClass *klass, MonoMethod ***overrides, int *num_overrides, MonoError *error) +{ + MonoReflectionTypeBuilder *tb; + int i, j, onum; + MonoReflectionMethod *m; + + error_init (error); + *overrides = NULL; + *num_overrides = 0; + + g_assert (image_is_dynamic (klass->image)); + + if (!mono_class_has_ref_info (klass)) + return; + + tb = (MonoReflectionTypeBuilder*)mono_class_get_ref_info_raw (klass); /* FIXME use handles */ + g_assert (strcmp (mono_object_class (tb)->name, "TypeBuilder") == 0); + + onum = 0; + if (tb->methods) { + for (i = 0; i < tb->num_methods; ++i) { + MonoReflectionMethodBuilder *mb = + mono_array_get (tb->methods, MonoReflectionMethodBuilder*, i); + if (mb->override_methods) + onum += mono_array_length (mb->override_methods); + } + } + + if (onum) { + *overrides = g_new0 (MonoMethod*, onum * 2); + + onum = 0; + for (i = 0; i < tb->num_methods; ++i) { + MonoReflectionMethodBuilder *mb = + mono_array_get (tb->methods, MonoReflectionMethodBuilder*, i); + if (mb->override_methods) { + for (j = 0; j < mono_array_length (mb->override_methods); ++j) { + m = mono_array_get (mb->override_methods, MonoReflectionMethod*, j); + + (*overrides) [onum * 2] = mono_reflection_method_get_handle ((MonoObject*)m, error); + return_if_nok (error); + (*overrides) [onum * 2 + 1] = mb->mhandle; + + g_assert (mb->mhandle); + + onum ++; + } + } + } + } + + *num_overrides = onum; +} + +/* This initializes the same data as mono_class_setup_fields () */ +static void +typebuilder_setup_fields (MonoClass *klass, MonoError *error) +{ + MonoReflectionTypeBuilder *tb = (MonoReflectionTypeBuilder *)mono_class_get_ref_info_raw (klass); /* FIXME use handles */ + MonoReflectionFieldBuilder *fb; + MonoClassField *field; + MonoFieldDefaultValue *def_values; + MonoImage *image = klass->image; + const char *p, *p2; + int i, instance_size, packing_size = 0; + guint32 len, idx; + + if (klass->parent) { + if (!klass->parent->size_inited) + mono_class_init (klass->parent); + instance_size = klass->parent->instance_size; + } else { + instance_size = sizeof (MonoObject); + } + + int fcount = tb->num_fields; + mono_class_set_field_count (klass, fcount); + + error_init (error); + + if (tb->class_size) { + packing_size = tb->packing_size; + instance_size += tb->class_size; + } + + klass->fields = image_g_new0 (image, MonoClassField, fcount); + def_values = image_g_new0 (image, MonoFieldDefaultValue, fcount); + mono_class_set_field_def_values (klass, def_values); + /* + This is, guess what, a hack. + The issue is that the runtime doesn't know how to setup the fields of a typebuider and crash. + On the static path no field class is resolved, only types are built. This is the right thing to do + but we suck. + Setting size_inited is harmless because we're doing the same job as mono_class_setup_fields anyway. + */ + klass->size_inited = 1; + + for (i = 0; i < fcount; ++i) { + MonoArray *rva_data; + fb = (MonoReflectionFieldBuilder *)mono_array_get (tb->fields, gpointer, i); + field = &klass->fields [i]; + field->parent = klass; + field->name = string_to_utf8_image_raw (image, fb->name, error); /* FIXME use handles */ + if (!mono_error_ok (error)) + return; + if (fb->attrs) { + MonoType *type = mono_reflection_type_get_handle ((MonoReflectionType*)fb->type, error); + return_if_nok (error); + field->type = mono_metadata_type_dup (klass->image, type); + field->type->attrs = fb->attrs; + } else { + field->type = mono_reflection_type_get_handle ((MonoReflectionType*)fb->type, error); + return_if_nok (error); + } + + if (!klass->enumtype && !mono_type_get_underlying_type (field->type)) { + mono_class_set_type_load_failure (klass, "Field '%s' is an enum type with a bad underlying type", field->name); + continue; + } + + if ((fb->attrs & FIELD_ATTRIBUTE_HAS_FIELD_RVA) && (rva_data = fb->rva_data)) { + char *base = mono_array_addr (rva_data, char, 0); + size_t size = mono_array_length (rva_data); + char *data = (char *)mono_image_alloc (klass->image, size); + memcpy (data, base, size); + def_values [i].data = data; + } + if (fb->offset != -1) + field->offset = fb->offset; + fb->handle = field; + mono_save_custom_attrs (klass->image, field, fb->cattrs); + + if (fb->def_value) { + MonoDynamicImage *assembly = (MonoDynamicImage*)klass->image; + field->type->attrs |= FIELD_ATTRIBUTE_HAS_DEFAULT; + idx = mono_dynimage_encode_constant (assembly, fb->def_value, &def_values [i].def_type); + /* Copy the data from the blob since it might get realloc-ed */ + p = assembly->blob.data + idx; + len = mono_metadata_decode_blob_size (p, &p2); + len += p2 - p; + def_values [i].data = (const char *)mono_image_alloc (image, len); + memcpy ((gpointer)def_values [i].data, p, len); + } + } + + if (!mono_class_has_failure (klass)) { + mono_class_layout_fields (klass, instance_size, packing_size, TRUE); + } +} + +static void +typebuilder_setup_properties (MonoClass *klass, MonoError *error) +{ + MonoReflectionTypeBuilder *tb = (MonoReflectionTypeBuilder *)mono_class_get_ref_info_raw (klass); /* FIXME use handles */ + MonoReflectionPropertyBuilder *pb; + MonoImage *image = klass->image; + MonoProperty *properties; + MonoClassPropertyInfo *info; + int i; + + error_init (error); + + info = mono_class_get_property_info (klass); + if (!info) { + info = mono_class_alloc0 (klass, sizeof (MonoClassPropertyInfo)); + mono_class_set_property_info (klass, info); + } + + info->count = tb->properties ? mono_array_length (tb->properties) : 0; + info->first = 0; + + properties = image_g_new0 (image, MonoProperty, info->count); + info->properties = properties; + for (i = 0; i < info->count; ++i) { + pb = mono_array_get (tb->properties, MonoReflectionPropertyBuilder*, i); + properties [i].parent = klass; + properties [i].attrs = pb->attrs; + properties [i].name = string_to_utf8_image_raw (image, pb->name, error); /* FIXME use handles */ + if (!mono_error_ok (error)) + return; + if (pb->get_method) + properties [i].get = pb->get_method->mhandle; + if (pb->set_method) + properties [i].set = pb->set_method->mhandle; + + mono_save_custom_attrs (klass->image, &properties [i], pb->cattrs); + if (pb->def_value) { + guint32 len, idx; + const char *p, *p2; + MonoDynamicImage *assembly = (MonoDynamicImage*)klass->image; + if (!info->def_values) + info->def_values = image_g_new0 (image, MonoFieldDefaultValue, info->count); + properties [i].attrs |= PROPERTY_ATTRIBUTE_HAS_DEFAULT; + idx = mono_dynimage_encode_constant (assembly, pb->def_value, &info->def_values [i].def_type); + /* Copy the data from the blob since it might get realloc-ed */ + p = assembly->blob.data + idx; + len = mono_metadata_decode_blob_size (p, &p2); + len += p2 - p; + info->def_values [i].data = (const char *)mono_image_alloc (image, len); + memcpy ((gpointer)info->def_values [i].data, p, len); + } + } +} + +static void +typebuilder_setup_events (MonoClass *klass, MonoError *error) +{ + MonoReflectionTypeBuilder *tb = (MonoReflectionTypeBuilder *)mono_class_get_ref_info_raw (klass); /* FIXME use handles */ + MonoReflectionEventBuilder *eb; + MonoImage *image = klass->image; + MonoEvent *events; + MonoClassEventInfo *info; + int i; + + error_init (error); + + info = mono_class_alloc0 (klass, sizeof (MonoClassEventInfo)); + mono_class_set_event_info (klass, info); + + info->count = tb->events ? mono_array_length (tb->events) : 0; + info->first = 0; + + events = image_g_new0 (image, MonoEvent, info->count); + info->events = events; + for (i = 0; i < info->count; ++i) { + eb = mono_array_get (tb->events, MonoReflectionEventBuilder*, i); + events [i].parent = klass; + events [i].attrs = eb->attrs; + events [i].name = string_to_utf8_image_raw (image, eb->name, error); /* FIXME use handles */ + if (!mono_error_ok (error)) + return; + if (eb->add_method) + events [i].add = eb->add_method->mhandle; + if (eb->remove_method) + events [i].remove = eb->remove_method->mhandle; + if (eb->raise_method) + events [i].raise = eb->raise_method->mhandle; + +#ifndef MONO_SMALL_CONFIG + if (eb->other_methods) { + int j; + events [i].other = image_g_new0 (image, MonoMethod*, mono_array_length (eb->other_methods) + 1); + for (j = 0; j < mono_array_length (eb->other_methods); ++j) { + MonoReflectionMethodBuilder *mb = + mono_array_get (eb->other_methods, + MonoReflectionMethodBuilder*, j); + events [i].other [j] = mb->mhandle; + } + } +#endif + mono_save_custom_attrs (klass->image, &events [i], eb->cattrs); + } +} + +struct remove_instantiations_user_data +{ + MonoClass *klass; + MonoError *error; +}; + +static gboolean +remove_instantiations_of_and_ensure_contents (gpointer key, + gpointer value, + gpointer user_data) +{ + struct remove_instantiations_user_data *data = (struct remove_instantiations_user_data*)user_data; + MonoType *type = (MonoType*)key; + MonoClass *klass = data->klass; + gboolean already_failed = !is_ok (data->error); + MonoError lerror; + MonoError *error = already_failed ? &lerror : data->error; + + if ((type->type == MONO_TYPE_GENERICINST) && (type->data.generic_class->container_class == klass)) { + MonoClass *inst_klass = mono_class_from_mono_type (type); + //Ensure it's safe to use it. + if (!fix_partial_generic_class (inst_klass, error)) { + mono_class_set_type_load_failure (inst_klass, "Could not initialized generic type instance due to: %s", mono_error_get_message (error)); + // Marked the class with failure, but since some other instantiation already failed, + // just report that one, and swallow the error from this one. + if (already_failed) + mono_error_cleanup (error); + } + return TRUE; + } else + return FALSE; +} + +/** + * reflection_setup_internal_class: + * @tb: a TypeBuilder object + * @error: set on error + * + * Creates a MonoClass that represents the TypeBuilder. + * This is a trick that lets us simplify a lot of reflection code + * (and will allow us to support Build and Run assemblies easier). + * + * Returns TRUE on success. On failure, returns FALSE and sets @error. + */ +static gboolean +reflection_setup_internal_class (MonoReflectionTypeBuilderHandle ref_tb, MonoError *error) +{ + MonoReflectionModuleBuilderHandle module_ref = MONO_HANDLE_NEW_GET (MonoReflectionModuleBuilder, ref_tb, module); + GHashTable *unparented_classes = MONO_HANDLE_GETVAL(module_ref, unparented_classes); + + if (unparented_classes) { + return reflection_setup_internal_class_internal (ref_tb, error); + } else { + // If we're not being called recursively + unparented_classes = g_hash_table_new (NULL, NULL); + MONO_HANDLE_SETVAL (module_ref, unparented_classes, GHashTable *, unparented_classes); + + gboolean ret_val = reflection_setup_internal_class_internal (ref_tb, error); + mono_error_assert_ok (error); + + // Fix the relationship between the created classes and their parents + reflection_setup_class_hierarchy (unparented_classes, error); + mono_error_assert_ok (error); + + g_hash_table_destroy (unparented_classes); + MONO_HANDLE_SETVAL (module_ref, unparented_classes, GHashTable *, NULL); + + return ret_val; + } +} + + +MonoReflectionTypeHandle +ves_icall_TypeBuilder_create_runtime_class (MonoReflectionTypeBuilderHandle ref_tb, MonoError *error) +{ + error_init (error); + + reflection_setup_internal_class (ref_tb, error); + mono_error_assert_ok (error); + + MonoDomain *domain = MONO_HANDLE_DOMAIN (ref_tb); + MonoType *type = MONO_HANDLE_GETVAL (MONO_HANDLE_CAST (MonoReflectionType, ref_tb), type); + MonoClass *klass = mono_class_from_mono_type (type); + + MonoArrayHandle cattrs = MONO_HANDLE_NEW_GET (MonoArray, ref_tb, cattrs); + mono_save_custom_attrs (klass->image, klass, MONO_HANDLE_RAW (cattrs)); /* FIXME use handles */ + + /* + * we need to lock the domain because the lock will be taken inside + * So, we need to keep the locking order correct. + */ + mono_loader_lock (); + mono_domain_lock (domain); + if (klass->wastypebuilder) { + mono_domain_unlock (domain); + mono_loader_unlock (); + + return mono_type_get_object_handle (domain, &klass->byval_arg, error); + } + /* + * Fields to set in klass: + * the various flags: delegate/unicode/contextbound etc. + */ + mono_class_set_flags (klass, MONO_HANDLE_GETVAL (ref_tb, attrs)); + klass->has_cctor = 1; + + mono_class_setup_parent (klass, klass->parent); + /* fool mono_class_setup_supertypes */ + klass->supertypes = NULL; + mono_class_setup_supertypes (klass); + mono_class_setup_mono_type (klass); + + /* enums are done right away */ + if (!klass->enumtype) + if (!ensure_runtime_vtable (klass, error)) + goto failure; + + MonoArrayHandle nested_types = MONO_HANDLE_NEW_GET (MonoArray, ref_tb, subtypes); + if (!MONO_HANDLE_IS_NULL (nested_types)) { + GList *nested = NULL; + int num_nested = mono_array_handle_length (nested_types); + MonoReflectionTypeHandle nested_tb = MONO_HANDLE_NEW (MonoReflectionType, NULL); + for (int i = 0; i < num_nested; ++i) { + MONO_HANDLE_ARRAY_GETREF (nested_tb, nested_types, i); + + if (MONO_HANDLE_GETVAL (nested_tb, type) == NULL) { + reflection_setup_internal_class (MONO_HANDLE_CAST (MonoReflectionTypeBuilder, nested_tb), error); + mono_error_assert_ok (error); + } + + MonoType *subtype = mono_reflection_type_handle_mono_type (nested_tb, error); + goto_if_nok (error, failure); + nested = g_list_prepend_image (klass->image, nested, mono_class_from_mono_type (subtype)); + } + mono_class_set_nested_classes_property (klass, nested); + } + + klass->nested_classes_inited = TRUE; + + typebuilder_setup_fields (klass, error); + goto_if_nok (error, failure); + typebuilder_setup_properties (klass, error); + goto_if_nok (error, failure); + + typebuilder_setup_events (klass, error); + goto_if_nok (error, failure); + + klass->wastypebuilder = TRUE; + + MonoArrayHandle generic_params = MONO_HANDLE_NEW_GET (MonoArray, ref_tb, generic_params); + if (!MONO_HANDLE_IS_NULL (generic_params)) { + int num_params = mono_array_handle_length (generic_params); + MonoReflectionTypeHandle ref_gparam = MONO_HANDLE_NEW (MonoReflectionType, NULL); + for (int i = 0; i < num_params; i++) { + MONO_HANDLE_ARRAY_GETREF (ref_gparam, generic_params, i); + MonoType *param_type = mono_reflection_type_handle_mono_type (ref_gparam, error); + goto_if_nok (error, failure); + MonoClass *gklass = mono_class_from_mono_type (param_type); + + gklass->wastypebuilder = TRUE; + } + } + + /* + * If we are a generic TypeBuilder, there might be instantiations in the type cache + * which have type System.Reflection.MonoGenericClass, but after the type is created, + * we want to return normal System.MonoType objects, so clear these out from the cache. + * + * Together with this we must ensure the contents of all instances to match the created type. + */ + if (domain->type_hash && mono_class_is_gtd (klass)) { + struct remove_instantiations_user_data data; + data.klass = klass; + data.error = error; + mono_error_assert_ok (error); + mono_g_hash_table_foreach_remove (domain->type_hash, remove_instantiations_of_and_ensure_contents, &data); + goto_if_nok (error, failure); + } + + mono_domain_unlock (domain); + mono_loader_unlock (); + + if (klass->enumtype && !mono_class_is_valid_enum (klass)) { + mono_class_set_type_load_failure (klass, "Not a valid enumeration"); + mono_error_set_type_load_class (error, klass, "Not a valid enumeration"); + goto failure_unlocked; + } + + MonoReflectionTypeHandle res = mono_type_get_object_handle (domain, &klass->byval_arg, error); + goto_if_nok (error, failure_unlocked); + + return res; + +failure: + mono_class_set_type_load_failure (klass, "TypeBuilder could not create runtime class due to: %s", mono_error_get_message (error)); + klass->wastypebuilder = TRUE; + mono_domain_unlock (domain); + mono_loader_unlock (); +failure_unlocked: + return NULL; +} + +typedef struct { + MonoMethod *handle; + MonoDomain *domain; +} DynamicMethodReleaseData; + +/* + * The runtime automatically clean up those after finalization. +*/ +static MonoReferenceQueue *dynamic_method_queue; + +static void +free_dynamic_method (void *dynamic_method) +{ + DynamicMethodReleaseData *data = (DynamicMethodReleaseData *)dynamic_method; + MonoDomain *domain = data->domain; + MonoMethod *method = data->handle; + guint32 dis_link; + + mono_domain_lock (domain); + dis_link = (guint32)(size_t)g_hash_table_lookup (domain->method_to_dyn_method, method); + g_hash_table_remove (domain->method_to_dyn_method, method); + mono_domain_unlock (domain); + g_assert (dis_link); + mono_gchandle_free (dis_link); + + mono_runtime_free_method (domain, method); + g_free (data); +} + +static gboolean +reflection_create_dynamic_method (MonoReflectionDynamicMethodHandle ref_mb, MonoError *error) +{ + MonoReferenceQueue *queue; + MonoMethod *handle; + DynamicMethodReleaseData *release_data; + ReflectionMethodBuilder rmb; + MonoMethodSignature *sig; + MonoClass *klass; + MonoDomain *domain; + GSList *l; + int i; + + error_init (error); + + if (mono_runtime_is_shutting_down ()) { + mono_error_set_generic_error (error, "System", "InvalidOperationException", ""); + return FALSE; + } + + if (!(queue = dynamic_method_queue)) { + mono_loader_lock (); + if (!(queue = dynamic_method_queue)) + queue = dynamic_method_queue = mono_gc_reference_queue_new (free_dynamic_method); + mono_loader_unlock (); + } + + sig = dynamic_method_to_signature (ref_mb, error); + return_val_if_nok (error, FALSE); + + MonoReflectionDynamicMethod *mb = MONO_HANDLE_RAW (ref_mb); /* FIXME convert reflection_create_dynamic_method to use handles */ + reflection_methodbuilder_from_dynamic_method (&rmb, mb); + + /* + * Resolve references. + */ + /* + * Every second entry in the refs array is reserved for storing handle_class, + * which is needed by the ldtoken implementation in the JIT. + */ + rmb.nrefs = mb->nrefs; + rmb.refs = g_new0 (gpointer, mb->nrefs + 1); + for (i = 0; i < mb->nrefs; i += 2) { + MonoClass *handle_class; + gpointer ref; + MonoObject *obj = mono_array_get (mb->refs, MonoObject*, i); + + if (strcmp (obj->vtable->klass->name, "DynamicMethod") == 0) { + MonoReflectionDynamicMethod *method = (MonoReflectionDynamicMethod*)obj; + /* + * The referenced DynamicMethod should already be created by the managed + * code, except in the case of circular references. In that case, we store + * method in the refs array, and fix it up later when the referenced + * DynamicMethod is created. + */ + if (method->mhandle) { + ref = method->mhandle; + } else { + /* FIXME: GC object stored in unmanaged memory */ + ref = method; + + /* FIXME: GC object stored in unmanaged memory */ + method->referenced_by = g_slist_append (method->referenced_by, mb); + } + handle_class = mono_defaults.methodhandle_class; + } else { + MonoException *ex = NULL; + ref = mono_reflection_resolve_object (mb->module->image, obj, &handle_class, NULL, error); + if (!is_ok (error)) { + g_free (rmb.refs); + return FALSE; + } + if (!ref) + ex = mono_get_exception_type_load (NULL, NULL); + else if (mono_security_core_clr_enabled ()) + ex = mono_security_core_clr_ensure_dynamic_method_resolved_object (ref, handle_class); + + if (ex) { + g_free (rmb.refs); + mono_error_set_exception_instance (error, ex); + return FALSE; + } + } + + rmb.refs [i] = ref; /* FIXME: GC object stored in unmanaged memory (change also resolve_object() signature) */ + rmb.refs [i + 1] = handle_class; + } + + MonoAssembly *ass = NULL; + if (mb->owner) { + MonoType *owner_type = mono_reflection_type_get_handle ((MonoReflectionType*)mb->owner, error); + if (!is_ok (error)) { + g_free (rmb.refs); + return FALSE; + } + klass = mono_class_from_mono_type (owner_type); + ass = klass->image->assembly; + } else { + klass = mono_defaults.object_class; + ass = (mb->module && mb->module->image) ? mb->module->image->assembly : NULL; + } + + mb->mhandle = handle = reflection_methodbuilder_to_mono_method (klass, &rmb, sig, error); + ((MonoDynamicMethod*)handle)->assembly = ass; + g_free (rmb.refs); + return_val_if_nok (error, FALSE); + + release_data = g_new (DynamicMethodReleaseData, 1); + release_data->handle = handle; + release_data->domain = mono_object_get_domain ((MonoObject*)mb); + if (!mono_gc_reference_queue_add (queue, (MonoObject*)mb, release_data)) + g_free (release_data); + + /* Fix up refs entries pointing at us */ + for (l = mb->referenced_by; l; l = l->next) { + MonoReflectionDynamicMethod *method = (MonoReflectionDynamicMethod*)l->data; + MonoMethodWrapper *wrapper = (MonoMethodWrapper*)method->mhandle; + gpointer *data; + + g_assert (method->mhandle); + + data = (gpointer*)wrapper->method_data; + for (i = 0; i < GPOINTER_TO_UINT (data [0]); i += 2) { + if ((data [i + 1] == mb) && (data [i + 1 + 1] == mono_defaults.methodhandle_class)) + data [i + 1] = mb->mhandle; + } + } + g_slist_free (mb->referenced_by); + + /* ilgen is no longer needed */ + mb->ilgen = NULL; + + domain = mono_domain_get (); + mono_domain_lock (domain); + if (!domain->method_to_dyn_method) + domain->method_to_dyn_method = g_hash_table_new (NULL, NULL); + g_hash_table_insert (domain->method_to_dyn_method, handle, (gpointer)(size_t)mono_gchandle_new_weakref ((MonoObject *)mb, TRUE)); + mono_domain_unlock (domain); + + return TRUE; +} + +void +ves_icall_DynamicMethod_create_dynamic_method (MonoReflectionDynamicMethodHandle mb, MonoError *error) +{ + (void) reflection_create_dynamic_method (mb, error); +} + +#endif /* DISABLE_REFLECTION_EMIT */ + +MonoMethodSignature * +mono_reflection_lookup_signature (MonoImage *image, MonoMethod *method, guint32 token, MonoError *error) +{ + MonoMethodSignature *sig; + g_assert (image_is_dynamic (image)); + + error_init (error); + + sig = (MonoMethodSignature *)g_hash_table_lookup (((MonoDynamicImage*)image)->vararg_aux_hash, GUINT_TO_POINTER (token)); + if (sig) + return sig; + + return mono_method_signature_checked (method, error); +} + +#ifndef DISABLE_REFLECTION_EMIT + +/* + * ensure_complete_type: + * + * Ensure that KLASS is completed if it is a dynamic type, or references + * dynamic types. + */ +static void +ensure_complete_type (MonoClass *klass, MonoError *error) +{ + error_init (error); + + if (image_is_dynamic (klass->image) && !klass->wastypebuilder && mono_class_has_ref_info (klass)) { + MonoReflectionTypeBuilder *tb = (MonoReflectionTypeBuilder *)mono_class_get_ref_info_raw (klass); /* FIXME use handles */ + + mono_domain_try_type_resolve_checked (mono_domain_get (), NULL, (MonoObject*)tb, error); + return_if_nok (error); + + // Asserting here could break a lot of code + //g_assert (klass->wastypebuilder); + } + + if (mono_class_is_ginst (klass)) { + MonoGenericInst *inst = mono_class_get_generic_class (klass)->context.class_inst; + int i; + + for (i = 0; i < inst->type_argc; ++i) { + ensure_complete_type (mono_class_from_mono_type (inst->type_argv [i]), error); + return_if_nok (error); + } + } +} + +gpointer +mono_reflection_resolve_object (MonoImage *image, MonoObject *obj, MonoClass **handle_class, MonoGenericContext *context, MonoError *error) +{ + MonoClass *oklass = obj->vtable->klass; + gpointer result = NULL; + + error_init (error); + + if (strcmp (oklass->name, "String") == 0) { + result = mono_string_intern_checked ((MonoString*)obj, error); + return_val_if_nok (error, NULL); + *handle_class = mono_defaults.string_class; + g_assert (result); + } else if (strcmp (oklass->name, "RuntimeType") == 0) { + MonoType *type = mono_reflection_type_get_handle ((MonoReflectionType*)obj, error); + return_val_if_nok (error, NULL); + MonoClass *mc = mono_class_from_mono_type (type); + if (!mono_class_init (mc)) { + mono_error_set_for_class_failure (error, mc); + return NULL; + } + + if (context) { + MonoType *inflated = mono_class_inflate_generic_type_checked (type, context, error); + return_val_if_nok (error, NULL); + + result = mono_class_from_mono_type (inflated); + mono_metadata_free_type (inflated); + } else { + result = mono_class_from_mono_type (type); + } + *handle_class = mono_defaults.typehandle_class; + g_assert (result); + } else if (strcmp (oklass->name, "MonoMethod") == 0 || + strcmp (oklass->name, "MonoCMethod") == 0) { + result = ((MonoReflectionMethod*)obj)->method; + if (context) { + result = mono_class_inflate_generic_method_checked ((MonoMethod *)result, context, error); + mono_error_assert_ok (error); + } + *handle_class = mono_defaults.methodhandle_class; + g_assert (result); + } else if (strcmp (oklass->name, "MonoField") == 0) { + MonoClassField *field = ((MonoReflectionField*)obj)->field; + + ensure_complete_type (field->parent, error); + return_val_if_nok (error, NULL); + + if (context) { + MonoType *inflated = mono_class_inflate_generic_type_checked (&field->parent->byval_arg, context, error); + return_val_if_nok (error, NULL); + + MonoClass *klass = mono_class_from_mono_type (inflated); + MonoClassField *inflated_field; + gpointer iter = NULL; + mono_metadata_free_type (inflated); + while ((inflated_field = mono_class_get_fields (klass, &iter))) { + if (!strcmp (field->name, inflated_field->name)) + break; + } + g_assert (inflated_field && !strcmp (field->name, inflated_field->name)); + result = inflated_field; + } else { + result = field; + } + *handle_class = mono_defaults.fieldhandle_class; + g_assert (result); + } else if (strcmp (oklass->name, "TypeBuilder") == 0) { + MonoReflectionTypeBuilder *tb = (MonoReflectionTypeBuilder*)obj; + MonoType *type = mono_reflection_type_get_handle ((MonoReflectionType*)tb, error); + return_val_if_nok (error, NULL); + MonoClass *klass; + + klass = type->data.klass; + if (klass->wastypebuilder) { + /* Already created */ + result = klass; + } + else { + mono_domain_try_type_resolve_checked (mono_domain_get (), NULL, (MonoObject*)tb, error); + return_val_if_nok (error, NULL); + result = type->data.klass; + g_assert (result); + } + *handle_class = mono_defaults.typehandle_class; + } else if (strcmp (oklass->name, "SignatureHelper") == 0) { + MonoReflectionSigHelper *helper = (MonoReflectionSigHelper*)obj; + MonoMethodSignature *sig; + int nargs, i; + + if (helper->arguments) + nargs = mono_array_length (helper->arguments); + else + nargs = 0; + + sig = mono_metadata_signature_alloc (image, nargs); + sig->explicit_this = helper->call_conv & 64 ? 1 : 0; + sig->hasthis = helper->call_conv & 32 ? 1 : 0; + + if (helper->unmanaged_call_conv) { /* unmanaged */ + sig->call_convention = helper->unmanaged_call_conv - 1; + sig->pinvoke = TRUE; + } else if (helper->call_conv & 0x02) { + sig->call_convention = MONO_CALL_VARARG; + } else { + sig->call_convention = MONO_CALL_DEFAULT; + } + + sig->param_count = nargs; + /* TODO: Copy type ? */ + sig->ret = helper->return_type->type; + for (i = 0; i < nargs; ++i) { + sig->params [i] = mono_type_array_get_and_resolve_raw (helper->arguments, i, error); /* FIXME use handles */ + if (!is_ok (error)) { + image_g_free (image, sig); + return NULL; + } + } + + result = sig; + *handle_class = NULL; + } else if (strcmp (oklass->name, "DynamicMethod") == 0) { + MonoReflectionDynamicMethod *method = (MonoReflectionDynamicMethod*)obj; + /* Already created by the managed code */ + g_assert (method->mhandle); + result = method->mhandle; + *handle_class = mono_defaults.methodhandle_class; + } else if (strcmp (oklass->name, "MonoArrayMethod") == 0) { + MonoReflectionArrayMethod *m = (MonoReflectionArrayMethod*)obj; + MonoType *mtype; + MonoClass *klass; + MonoMethod *method; + gpointer iter; + char *name; + + mtype = mono_reflection_type_get_handle (m->parent, error); + return_val_if_nok (error, NULL); + klass = mono_class_from_mono_type (mtype); + + /* Find the method */ + + name = mono_string_to_utf8_checked (m->name, error); + return_val_if_nok (error, NULL); + iter = NULL; + while ((method = mono_class_get_methods (klass, &iter))) { + if (!strcmp (method->name, name)) + break; + } + g_free (name); + + // FIXME: + g_assert (method); + // FIXME: Check parameters/return value etc. match + + result = method; + *handle_class = mono_defaults.methodhandle_class; + } else if (is_sre_method_builder (oklass) || + mono_is_sre_ctor_builder (oklass) || + is_sre_field_builder (oklass) || + is_sre_gparam_builder (oklass) || + is_sre_generic_instance (oklass) || + is_sre_array (oklass) || + is_sre_byref (oklass) || + is_sre_pointer (oklass) || + !strcmp (oklass->name, "FieldOnTypeBuilderInst") || + !strcmp (oklass->name, "MethodOnTypeBuilderInst") || + !strcmp (oklass->name, "ConstructorOnTypeBuilderInst")) { + static MonoMethod *resolve_method; + if (!resolve_method) { + MonoMethod *m = mono_class_get_method_from_name_flags (mono_class_get_module_builder_class (), "RuntimeResolve", 1, 0); + g_assert (m); + mono_memory_barrier (); + resolve_method = m; + } + void *args [16]; + args [0] = obj; + obj = mono_runtime_invoke_checked (resolve_method, NULL, args, error); + mono_error_assert_ok (error); + g_assert (obj); + return mono_reflection_resolve_object (image, obj, handle_class, context, error); + } else { + g_print ("%s\n", obj->vtable->klass->name); + g_assert_not_reached (); + } + return result; +} + +#else /* DISABLE_REFLECTION_EMIT */ + +MonoArray* +mono_reflection_get_custom_attrs_blob (MonoReflectionAssembly *assembly, MonoObject *ctor, MonoArray *ctorArgs, MonoArray *properties, MonoArray *propValues, MonoArray *fields, MonoArray* fieldValues) +{ + g_assert_not_reached (); + return NULL; +} + +void +mono_reflection_dynimage_basic_init (MonoReflectionAssemblyBuilder *assemblyb) +{ + g_error ("This mono runtime was configured with --enable-minimal=reflection_emit, so System.Reflection.Emit is not supported."); +} + +static gboolean +mono_image_module_basic_init (MonoReflectionModuleBuilderHandle moduleb, MonoError *error) +{ + g_assert_not_reached (); + return FALSE; +} + +guint32 +mono_image_insert_string (MonoReflectionModuleBuilderHandle module, MonoStringHandle str, MonoError *error) +{ + g_assert_not_reached (); + return 0; +} + +guint32 +mono_image_create_method_token (MonoDynamicImage *assembly, MonoObjectHandle obj, MonoArrayHandle opt_param_types, MonoError *error) +{ + g_assert_not_reached (); + return 0; +} + +guint32 +mono_image_create_token (MonoDynamicImage *assembly, MonoObjectHandle obj, + gboolean create_open_instance, gboolean register_token, MonoError *error) +{ + g_assert_not_reached (); + return 0; +} + +void +mono_reflection_get_dynamic_overrides (MonoClass *klass, MonoMethod ***overrides, int *num_overrides, MonoError *error) +{ + error_init (error); + *overrides = NULL; + *num_overrides = 0; +} + +MonoReflectionTypeHandle +ves_icall_TypeBuilder_create_runtime_class (MonoReflectionTypeBuilderHandle tb, MonoError *error) +{ + g_assert_not_reached (); + return NULL; +} + +void +ves_icall_DynamicMethod_create_dynamic_method (MonoReflectionDynamicMethodHandle mb, MonoError *error) +{ + error_init (error); +} + +MonoType* +mono_reflection_type_get_handle (MonoReflectionType* ref, MonoError *error) +{ + error_init (error); + if (!ref) + return NULL; + return ref->type; +} + +MonoType* +mono_reflection_type_handle_mono_type (MonoReflectionTypeHandle ref, MonoError *error) +{ + error_init (error); + if (MONO_HANDLE_IS_NULL (ref)) + return NULL; + return MONO_HANDLE_GETVAL (ref, type); +} + + +#endif /* DISABLE_REFLECTION_EMIT */ + +void +mono_sre_generic_param_table_entry_free (GenericParamTableEntry *entry) +{ + MONO_GC_UNREGISTER_ROOT_IF_MOVING (entry->gparam); + g_free (entry); +} + +gint32 +ves_icall_ModuleBuilder_getToken (MonoReflectionModuleBuilderHandle mb, MonoObjectHandle obj, gboolean create_open_instance, MonoError *error) +{ + error_init (error); + if (MONO_HANDLE_IS_NULL (obj)) { + mono_error_set_argument_null (error, "obj", ""); + return 0; + } + return mono_image_create_token (MONO_HANDLE_GETVAL (mb, dynamic_image), obj, create_open_instance, TRUE, error); +} + +gint32 +ves_icall_ModuleBuilder_getMethodToken (MonoReflectionModuleBuilderHandle mb, + MonoReflectionMethodHandle method, + MonoArrayHandle opt_param_types, + MonoError *error) +{ + error_init (error); + if (MONO_HANDLE_IS_NULL (method)) { + mono_error_set_argument_null (error, "method", ""); + return 0; + } + + return mono_image_create_method_token (MONO_HANDLE_GETVAL (mb, dynamic_image), MONO_HANDLE_CAST (MonoObject, method), opt_param_types, error); +} + +void +ves_icall_ModuleBuilder_WriteToFile (MonoReflectionModuleBuilder *mb, HANDLE file) +{ + MonoError error; + mono_image_create_pefile (mb, file, &error); + mono_error_set_pending_exception (&error); +} + +void +ves_icall_ModuleBuilder_build_metadata (MonoReflectionModuleBuilder *mb) +{ + MonoError error; + mono_image_build_metadata (mb, &error); + mono_error_set_pending_exception (&error); +} + +void +ves_icall_ModuleBuilder_RegisterToken (MonoReflectionModuleBuilderHandle mb, MonoObjectHandle obj, guint32 token, MonoError *error) +{ + error_init (error); + /* This function may be called by ModuleBuilder.FixupTokens to update + * an existing token, so replace is okay here. */ + mono_dynamic_image_register_token (MONO_HANDLE_GETVAL (mb, dynamic_image), token, obj, MONO_DYN_IMAGE_TOK_REPLACE); +} + +MonoObjectHandle +ves_icall_ModuleBuilder_GetRegisteredToken (MonoReflectionModuleBuilderHandle mb, guint32 token, MonoError *error) +{ + error_init (error); + MonoDynamicImage *dynamic_image = MONO_HANDLE_GETVAL (mb, dynamic_image); + return mono_dynamic_image_get_registered_token (dynamic_image, token, error); +} + +#ifndef DISABLE_REFLECTION_EMIT +MonoArray* +ves_icall_CustomAttributeBuilder_GetBlob (MonoReflectionAssembly *assembly, MonoObject *ctor, MonoArray *ctorArgs, MonoArray *properties, MonoArray *propValues, MonoArray *fields, MonoArray* fieldValues) +{ + MonoError error; + MonoArray *result = mono_reflection_get_custom_attrs_blob_checked (assembly, ctor, ctorArgs, properties, propValues, fields, fieldValues, &error); + mono_error_set_pending_exception (&error); + return result; +} +#endif + +void +ves_icall_AssemblyBuilder_basic_init (MonoReflectionAssemblyBuilder *assemblyb) +{ + mono_reflection_dynimage_basic_init (assemblyb); +} + +void +ves_icall_AssemblyBuilder_UpdateNativeCustomAttributes (MonoReflectionAssemblyBuilderHandle assemblyb, MonoError *error) +{ + MonoArrayHandle cattrs = MONO_HANDLE_NEW_GET (MonoArray, assemblyb, cattrs); + + MonoReflectionAssemblyHandle assembly_handle = MONO_HANDLE_CAST (MonoReflectionAssembly, assemblyb); + MonoAssembly *assembly = MONO_HANDLE_GETVAL (assembly_handle, assembly); + g_assert (assembly); + + mono_save_custom_attrs (assembly->image, assembly, MONO_HANDLE_RAW (cattrs)); +} + +void +ves_icall_EnumBuilder_setup_enum_type (MonoReflectionTypeHandle enumtype, + MonoReflectionTypeHandle t, + MonoError *error) +{ + error_init (error); + MONO_HANDLE_SETVAL (enumtype, type, MonoType*, MONO_HANDLE_GETVAL (t, type)); +} + +void +ves_icall_ModuleBuilder_basic_init (MonoReflectionModuleBuilderHandle moduleb, MonoError *error) +{ + error_init (error); + mono_image_module_basic_init (moduleb, error); +} + +guint32 +ves_icall_ModuleBuilder_getUSIndex (MonoReflectionModuleBuilderHandle module, MonoStringHandle str, MonoError *error) +{ + return mono_image_insert_string (module, str, error); +} + +void +ves_icall_ModuleBuilder_set_wrappers_type (MonoReflectionModuleBuilderHandle moduleb, MonoReflectionTypeHandle ref_type, MonoError *error) +{ + error_init (error); + MonoDynamicImage *image = MONO_HANDLE_GETVAL (moduleb, dynamic_image); + MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); + + g_assert (type); + image->wrappers_type = mono_class_from_mono_type (type); +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/string-icalls.c b/unity-2019.4.24f1-mbe/mono/metadata/string-icalls.c new file mode 100644 index 000000000..e6cbc3dbf --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/string-icalls.c @@ -0,0 +1,73 @@ +/** + * \file + * String internal calls for the corlib + * + * Author: + * Patrik Torstensson (patrik.torstensson@labs2.com) + * Duncan Mak (duncan@ximian.com) + * + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#include +#include +#include +#include +#include "mono/utils/mono-membar.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* This function is redirected to String.CreateString () + by mono_marshal_get_native_wrapper () */ +void +ves_icall_System_String_ctor_RedirectToCreateString (void) +{ + g_assert_not_reached (); +} + +MonoString * +ves_icall_System_String_InternalAllocateStr (gint32 length) +{ + MonoError error; + MonoString *str = mono_string_new_size_checked (mono_domain_get (), length, &error); + mono_error_set_pending_exception (&error); + + return str; +} + +MonoString * +ves_icall_System_String_InternalIntern (MonoString *str) +{ + MonoError error; + MonoString *res; + + res = mono_string_intern_checked (str, &error); + if (!res) { + mono_error_set_pending_exception (&error); + return NULL; + } + return res; +} + +MonoString * +ves_icall_System_String_InternalIsInterned (MonoString *str) +{ + return mono_string_is_interned (str); +} + +int +ves_icall_System_String_GetLOSLimit (void) +{ + int limit = mono_gc_get_los_limit (); + + return (limit - 2 - G_STRUCT_OFFSET (MonoString, chars)) / 2; +} + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/string-icalls.h b/unity-2019.4.24f1-mbe/mono/metadata/string-icalls.h new file mode 100644 index 000000000..84be0998e --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/string-icalls.h @@ -0,0 +1,37 @@ +/** + * \file + */ + +#ifndef _MONO_CLI_STRING_ICALLS_H_ +#define _MONO_CLI_STRING_ICALLS_H_ + +/* + * string-icalls.h: String internal calls for the corlib + * + * Author: + * Patrik Torstensson (patrik.torstensson@labs2.com) + * + * (C) 2001 Ximian, Inc. + */ + +#include +#include +#include +#include "mono/utils/mono-compiler.h" + +void +ves_icall_System_String_ctor_RedirectToCreateString (void); + +MonoString * +ves_icall_System_String_InternalAllocateStr (gint32 length); + +MonoString * +ves_icall_System_String_InternalIntern (MonoString *str); + +MonoString * +ves_icall_System_String_InternalIsInterned (MonoString *str); + +int +ves_icall_System_String_GetLOSLimit (void); + +#endif /* _MONO_CLI_STRING_ICALLS_H_ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/sysmath.c b/unity-2019.4.24f1-mbe/mono/metadata/sysmath.c new file mode 100644 index 000000000..386092de3 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/sysmath.c @@ -0,0 +1,318 @@ +/** + * \file + * these are based on bob smith's csharp routines + * + * Author: + * Mono Project (http://www.mono-project.com) + * Ludovic Henry (ludovic@xamarin.com) + * + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Copyright 2015 Xamarin, Inc (https://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// Files: +// - src/classlibnative/float/floatnative.cpp +// - src/pal/src/cruntime/floatnative.cpp +// +// Ported from C++ to C and adjusted to Mono runtime + +#define __USE_ISOC99 + +#include +#include + +#include "number-ms.h" +#include "utils/mono-compiler.h" + +static const MonoDouble_double NaN = { .s = { .sign = 0x0, .exp = 0x7FF, .mantHi = 0x80000, .mantLo = 0x0 } }; + +/* +Infinity */ +static const MonoDouble_double PInfinity = { .s = { .sign = 0x0, .exp = 0x7FF, .mantHi = 0x0, .mantLo = 0x0 } }; + +/* -Infinity */ +static const MonoDouble_double MInfinity = { .s = { .sign = 0x1, .exp = 0x7FF, .mantHi = 0x0, .mantLo = 0x0 } }; + +/* +1 */ +static const MonoDouble_double POne = { .s = { .sign = 0x0, .exp = 0x3FF, .mantHi = 0x0, .mantLo = 0x0 } }; + +/* -1 */ +static const MonoDouble_double MOne = { .s = { .sign = 0x1, .exp = 0x3FF, .mantHi = 0x0, .mantLo = 0x0 } }; + +static MONO_ALWAYS_INLINE gboolean +isplusinfinity (gdouble d) +{ + return d == PInfinity.d; +} + +static MONO_ALWAYS_INLINE gboolean +isminusinfinity (gdouble d) +{ + return d == MInfinity.d; +} + +static MONO_ALWAYS_INLINE gboolean +isinfinity (gdouble d) +{ + return isplusinfinity (d) || isminusinfinity (d); +} + +static MONO_ALWAYS_INLINE gboolean +isplusone (gdouble d) +{ + return d == POne.d; +} + +static MONO_ALWAYS_INLINE gboolean +isminusone (gdouble d) +{ + return d == MOne.d; +} + +gdouble +ves_icall_System_Math_Floor (gdouble x) +{ + return floor(x); +} + +gdouble +ves_icall_System_Math_Round (gdouble x) +{ + gdouble tmp, floor_tmp; + + /* If the number has no fractional part do nothing This shortcut is necessary + * to workaround precision loss in borderline cases on some platforms */ + if (x == (gdouble)(gint64) x) + return x; + + tmp = x + 0.5; + floor_tmp = floor (tmp); + + if (floor_tmp == tmp) { + if (fmod (tmp, 2.0) != 0) + floor_tmp -= 1.0; + } + + return copysign (floor_tmp, x); +} + +gdouble +ves_icall_System_Math_Sin (gdouble x) +{ + return sin (x); +} + +gdouble +ves_icall_System_Math_Cos (gdouble x) +{ + return cos (x); +} + +gdouble +ves_icall_System_Math_Tan (gdouble x) +{ + return tan (x); +} + +gdouble +ves_icall_System_Math_Sinh (gdouble x) +{ + return sinh (x); +} + +gdouble +ves_icall_System_Math_Cosh (gdouble x) +{ + return cosh (x); +} + +gdouble +ves_icall_System_Math_Tanh (gdouble x) +{ + return tanh (x); +} + +gdouble +ves_icall_System_Math_Acos (gdouble x) +{ + if (x < -1 || x > 1) + return NaN.d; + + return acos (x); +} + +gdouble +ves_icall_System_Math_Asin (gdouble x) +{ + if (x < -1 || x > 1) + return NaN.d; + + return asin (x); +} + +gdouble +ves_icall_System_Math_Atan (gdouble x) +{ + return atan (x); +} + +gdouble +ves_icall_System_Math_Atan2 (gdouble y, gdouble x) +{ + gdouble result; + + if (isinfinity (x) && isinfinity (y)) + return NaN.d; + + result = atan2 (y, x); + return result == -0.0 ? 0.0: result; +} + +gdouble +ves_icall_System_Math_Exp (gdouble x) +{ + if (isinfinity (x)) + return x < 0 ? 0.0 : x; + + return exp (x); +} + +gdouble +ves_icall_System_Math_Log (gdouble x) +{ + if (x == 0) + return MInfinity.d; + else if (x < 0) + return NaN.d; + + return log (x); +} + +gdouble +ves_icall_System_Math_Log10 (gdouble x) +{ + if (x == 0) + return MInfinity.d; + else if (x < 0) + return NaN.d; + + return log10 (x); +} + +gdouble +ves_icall_System_Math_Pow (gdouble x, gdouble y) +{ + gdouble result; + + if (isnan (y)) + return y; + if (isnan (x)) + return x; + + if (isinfinity (y)) { + if (isplusone (x)) + return x; + if (isminusone (x)) + return NaN.d; + } + + /* following are cases from PAL_pow which abstract the implementation of pow for posix and win32 platforms + * (https://github.com/dotnet/coreclr/blob/master/src/pal/src/cruntime/finite.cpp#L331) */ + + if (isplusinfinity (y) && !isnan (x)) { + if (isplusone (x) || isminusone (x)) + result = NaN.d; + else if (x > MOne.d && x < POne.d) + result = 0.0; + else + result = PInfinity.d; + } else if (isminusinfinity (y) && !isnan (x)) { + if (isplusone (x) || isminusone (x)) + result = NaN.d; + if (x > MOne.d && x < POne.d) + result = PInfinity.d; + else + result = 0.0; + } else if (x == 0.0 && y < 0.0) { + result = PInfinity.d; + } else if (y == 0.0 && isnan (x)) { + /* Windows returns NaN for pow(NaN, 0), but POSIX specifies + * a return value of 1 for that case. We need to return + * the same result as Windows. */ + result = NaN.d; + } else { + result = pow (x, y); + } + + if (result == PInfinity.d && x < 0.0 && isfinite (x) && ceil (y / 2) != floor (y / 2)) + result = MInfinity.d; + + /* + * The even/odd test in the if (this one and the one above) used to be ((long long) y % 2 == 0) + * on SPARC (long long) y for large y (>2**63) is always 0x7fffffff7fffffff, which + * is an odd number, so the test ((long long) y % 2 == 0) will always fail for + * large y. Since large double numbers are always even (e.g., the representation of + * 1E20+1 is the same as that of 1E20, the last .+1. is too insignificant to be part + * of the representation), this test will always return the wrong result for large y. + * + * The (ceil(y/2) == floor(y/2)) test is slower, but more robust. + */ + if (result == MInfinity.d && x < 0.0 && isfinite (x) && ceil (y / 2) == floor (y / 2)) + result = PInfinity.d; + +#if defined (__linux__) && SIZEOF_VOID_P == 4 + /* On Linux 32bits, some tests erroneously return NaN */ + if (isnan (result)) { + if (isminusone (x) && (y > 9007199254740991.0 || y < -9007199254740991.0)) { + /* Math.Pow (-1, Double.MaxValue) and Math.Pow (-1, Double.MinValue) should return 1 */ + result = POne.d; + } else if (x < -9007199254740991.0 && y < -9007199254740991.0) { + /* Math.Pow (Double.MinValue, Double.MinValue) should return 0 */ + result = 0.0; + } else if (x < -9007199254740991.0 && y > 9007199254740991.0) { + /* Math.Pow (Double.MinValue, Double.MaxValue) should return Double.PositiveInfinity */ + result = PInfinity.d; + } + } +#endif + + return result == -0.0 ? 0 : result; +} + +gdouble +ves_icall_System_Math_Sqrt (gdouble x) +{ + if (x < 0) + return NaN.d; + + return sqrt (x); +} + +gdouble +ves_icall_System_Math_Abs_double (gdouble v) +{ + return fabs (v); +} + +gfloat +ves_icall_System_Math_Abs_single (gfloat v) +{ + return fabsf (v); +} + +gdouble +ves_icall_System_Math_Ceiling (gdouble v) +{ + return ceil (v); +} + +gdouble +ves_icall_System_Math_SplitFractionDouble (gdouble *v) +{ + return modf (*v, v); +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/sysmath.h b/unity-2019.4.24f1-mbe/mono/metadata/sysmath.h new file mode 100644 index 000000000..52a4f7a97 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/sysmath.h @@ -0,0 +1,82 @@ +/** + * \file + * + * Author: + * Dan Lewis (dihlewis@yahoo.co.uk) + * Ludovic Henry (ludovic@xamarin.com) + * + * (C) Ximian, Inc. 2002 + * Copyright 2015 Xamarin, Inc (https://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#ifndef __METADATA_SYSMATH_H__ +#define __METADATA_SYSMATH_H__ + +#include +#include + +gdouble +ves_icall_System_Math_Floor (gdouble x); + +gdouble +ves_icall_System_Math_Round (gdouble x); + +gdouble +ves_icall_System_Math_Sin (gdouble x); + +gdouble +ves_icall_System_Math_Cos (gdouble x); + +gdouble +ves_icall_System_Math_Tan (gdouble x); + +gdouble +ves_icall_System_Math_Sinh (gdouble x); + +gdouble +ves_icall_System_Math_Cosh (gdouble x); + +gdouble +ves_icall_System_Math_Tanh (gdouble x); + +gdouble +ves_icall_System_Math_Acos (gdouble x); + +gdouble +ves_icall_System_Math_Asin (gdouble x); + +gdouble +ves_icall_System_Math_Atan (gdouble x); + +gdouble +ves_icall_System_Math_Atan2 (gdouble y, gdouble x); + +gdouble +ves_icall_System_Math_Exp (gdouble x); + +gdouble +ves_icall_System_Math_Log (gdouble x); + +gdouble +ves_icall_System_Math_Log10 (gdouble x); + +gdouble +ves_icall_System_Math_Pow (gdouble x, gdouble y); + +gdouble +ves_icall_System_Math_Sqrt (gdouble x); + +gdouble +ves_icall_System_Math_Abs_double (gdouble v); + +gfloat +ves_icall_System_Math_Abs_single (gfloat v); + +gdouble +ves_icall_System_Math_SplitFractionDouble (gdouble *v); + +gdouble +ves_icall_System_Math_Ceiling (gdouble v); + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/tabledefs.h b/unity-2019.4.24f1-mbe/mono/metadata/tabledefs.h new file mode 100644 index 000000000..e3aa51b4c --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/tabledefs.h @@ -0,0 +1,266 @@ +/** + * \file + * This file contains the various definitions for constants + * found on the metadata tables + * + * Author: + * Miguel de Icaza (miguel@ximian.com) + * + * (C) 2001 Ximian, Inc. + * + * From the ECMA documentation + */ + +#ifndef _MONO_METADATA_TABLEDEFS_H_ +#define _MONO_METADATA_TABLEDEFS_H_ + +/* + * 22.1.1 Values for AssemblyHashAlgorithm + */ + +enum { + ASSEMBLY_HASH_NONE, + ASSEMBLY_HASH_MD5 = 0x8003, + ASSEMBLY_HASH_SHA1 = 0x8004 +}; + +/* + * 22.1.4 Flags for Event.EventAttributes + */ + +enum { + EVENT_SPECIALNAME = 0x0200, + EVENT_RTSPECIALNAME = 0x0400 +}; + +/* + * 22.1.6 Flags for FileAttributes + */ + +enum { + FILE_CONTAINS_METADATA = 0, + FILE_CONTAINS_NO_METADATA = 1 +}; + +/* keep in synch with System.Security.Permissions.SecurityAction enum + (except for the special non-CAS cases) */ +enum { + SECURITY_ACTION_DEMAND = 2, + SECURITY_ACTION_ASSERT = 3, + SECURITY_ACTION_DENY = 4, + SECURITY_ACTION_PERMITONLY = 5, + SECURITY_ACTION_LINKDEMAND = 6, + SECURITY_ACTION_INHERITDEMAND = 7, + SECURITY_ACTION_REQMIN = 8, + SECURITY_ACTION_REQOPT = 9, + SECURITY_ACTION_REQREFUSE = 10, + /* Special cases (non CAS permissions) */ + SECURITY_ACTION_NONCASDEMAND = 13, + SECURITY_ACTION_NONCASLINKDEMAND = 14, + SECURITY_ACTION_NONCASINHERITANCE = 15, + /* Fx 2.0 actions (for both CAS and non-CAS permissions) */ + SECURITY_ACTION_LINKDEMANDCHOICE = 16, + SECURITY_ACTION_INHERITDEMANDCHOICE = 17, + SECURITY_ACTION_DEMANDCHOICE = 18 +}; + +/* + * + * 22.1.8 Flags for ManifestResource + */ +#define MANIFEST_RESOURCE_VISIBILITY_MASK 0x00000007 +#define MANIFEST_RESOURCE_PUBLIC 0x00000001 +#define MANIFEST_RESOURCE_PRIVATE 0x00000002 + +/* + * Field Attributes (21.1.5). + */ + +#define FIELD_ATTRIBUTE_FIELD_ACCESS_MASK 0x0007 +#define FIELD_ATTRIBUTE_COMPILER_CONTROLLED 0x0000 +#define FIELD_ATTRIBUTE_PRIVATE 0x0001 +#define FIELD_ATTRIBUTE_FAM_AND_ASSEM 0x0002 +#define FIELD_ATTRIBUTE_ASSEMBLY 0x0003 +#define FIELD_ATTRIBUTE_FAMILY 0x0004 +#define FIELD_ATTRIBUTE_FAM_OR_ASSEM 0x0005 +#define FIELD_ATTRIBUTE_PUBLIC 0x0006 + +#define FIELD_ATTRIBUTE_STATIC 0x0010 +#define FIELD_ATTRIBUTE_INIT_ONLY 0x0020 +#define FIELD_ATTRIBUTE_LITERAL 0x0040 +#define FIELD_ATTRIBUTE_NOT_SERIALIZED 0x0080 +#define FIELD_ATTRIBUTE_SPECIAL_NAME 0x0200 +#define FIELD_ATTRIBUTE_PINVOKE_IMPL 0x2000 + +/* For runtime use only */ +#define FIELD_ATTRIBUTE_RESERVED_MASK 0x9500 +#define FIELD_ATTRIBUTE_RT_SPECIAL_NAME 0x0400 +#define FIELD_ATTRIBUTE_HAS_FIELD_MARSHAL 0x1000 +#define FIELD_ATTRIBUTE_HAS_DEFAULT 0x8000 +#define FIELD_ATTRIBUTE_HAS_FIELD_RVA 0x0100 + +/* + * Type Attributes (21.1.13). + */ +#define TYPE_ATTRIBUTE_VISIBILITY_MASK 0x00000007 +#define TYPE_ATTRIBUTE_NOT_PUBLIC 0x00000000 +#define TYPE_ATTRIBUTE_PUBLIC 0x00000001 +#define TYPE_ATTRIBUTE_NESTED_PUBLIC 0x00000002 +#define TYPE_ATTRIBUTE_NESTED_PRIVATE 0x00000003 +#define TYPE_ATTRIBUTE_NESTED_FAMILY 0x00000004 +#define TYPE_ATTRIBUTE_NESTED_ASSEMBLY 0x00000005 +#define TYPE_ATTRIBUTE_NESTED_FAM_AND_ASSEM 0x00000006 +#define TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM 0x00000007 + +#define TYPE_ATTRIBUTE_LAYOUT_MASK 0x00000018 +#define TYPE_ATTRIBUTE_AUTO_LAYOUT 0x00000000 +#define TYPE_ATTRIBUTE_SEQUENTIAL_LAYOUT 0x00000008 +#define TYPE_ATTRIBUTE_EXPLICIT_LAYOUT 0x00000010 + +#define TYPE_ATTRIBUTE_CLASS_SEMANTIC_MASK 0x00000020 +#define TYPE_ATTRIBUTE_CLASS 0x00000000 +#define TYPE_ATTRIBUTE_INTERFACE 0x00000020 + +#define TYPE_ATTRIBUTE_ABSTRACT 0x00000080 +#define TYPE_ATTRIBUTE_SEALED 0x00000100 +#define TYPE_ATTRIBUTE_SPECIAL_NAME 0x00000400 + +#define TYPE_ATTRIBUTE_IMPORT 0x00001000 +#define TYPE_ATTRIBUTE_SERIALIZABLE 0x00002000 +#define TYPE_ATTRIBUTE_WINDOWS_RUNTIME 0x00004000 + + +#define TYPE_ATTRIBUTE_STRING_FORMAT_MASK 0x00030000 +#define TYPE_ATTRIBUTE_ANSI_CLASS 0x00000000 +#define TYPE_ATTRIBUTE_UNICODE_CLASS 0x00010000 +#define TYPE_ATTRIBUTE_AUTO_CLASS 0x00020000 + +#define TYPE_ATTRIBUTE_BEFORE_FIELD_INIT 0x00100000 +#define TYPE_ATTRIBUTE_FORWARDER 0x00200000 + +#define TYPE_ATTRIBUTE_RESERVED_MASK 0x00040800 +#define TYPE_ATTRIBUTE_RT_SPECIAL_NAME 0x00000800 +#define TYPE_ATTRIBUTE_HAS_SECURITY 0x00040000 + +/* + * Method Attributes (22.1.9) + */ + +#define METHOD_IMPL_ATTRIBUTE_CODE_TYPE_MASK 0x0003 +#define METHOD_IMPL_ATTRIBUTE_IL 0x0000 +#define METHOD_IMPL_ATTRIBUTE_NATIVE 0x0001 +#define METHOD_IMPL_ATTRIBUTE_OPTIL 0x0002 +#define METHOD_IMPL_ATTRIBUTE_RUNTIME 0x0003 + +#define METHOD_IMPL_ATTRIBUTE_MANAGED_MASK 0x0004 +#define METHOD_IMPL_ATTRIBUTE_UNMANAGED 0x0004 +#define METHOD_IMPL_ATTRIBUTE_MANAGED 0x0000 + +#define METHOD_IMPL_ATTRIBUTE_FORWARD_REF 0x0010 +#define METHOD_IMPL_ATTRIBUTE_PRESERVE_SIG 0x0080 +#define METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL 0x1000 +#define METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED 0x0020 +#define METHOD_IMPL_ATTRIBUTE_NOINLINING 0x0008 +#define METHOD_IMPL_ATTRIBUTE_NOOPTIMIZATION 0x0040 +#define METHOD_IMPL_ATTRIBUTE_MAX_METHOD_IMPL_VAL 0xffff +#define METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING 0x0100 + +#define METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK 0x0007 +#define METHOD_ATTRIBUTE_COMPILER_CONTROLLED 0x0000 +#define METHOD_ATTRIBUTE_PRIVATE 0x0001 +#define METHOD_ATTRIBUTE_FAM_AND_ASSEM 0x0002 +#define METHOD_ATTRIBUTE_ASSEM 0x0003 +#define METHOD_ATTRIBUTE_FAMILY 0x0004 +#define METHOD_ATTRIBUTE_FAM_OR_ASSEM 0x0005 +#define METHOD_ATTRIBUTE_PUBLIC 0x0006 + +#define METHOD_ATTRIBUTE_STATIC 0x0010 +#define METHOD_ATTRIBUTE_FINAL 0x0020 +#define METHOD_ATTRIBUTE_VIRTUAL 0x0040 +#define METHOD_ATTRIBUTE_HIDE_BY_SIG 0x0080 + +#define METHOD_ATTRIBUTE_VTABLE_LAYOUT_MASK 0x0100 +#define METHOD_ATTRIBUTE_REUSE_SLOT 0x0000 +#define METHOD_ATTRIBUTE_NEW_SLOT 0x0100 + +#define METHOD_ATTRIBUTE_STRICT 0x0200 +#define METHOD_ATTRIBUTE_ABSTRACT 0x0400 +#define METHOD_ATTRIBUTE_SPECIAL_NAME 0x0800 + +#define METHOD_ATTRIBUTE_PINVOKE_IMPL 0x2000 +#define METHOD_ATTRIBUTE_UNMANAGED_EXPORT 0x0008 + +/* + * For runtime use only + */ +#define METHOD_ATTRIBUTE_RESERVED_MASK 0xd000 +#define METHOD_ATTRIBUTE_RT_SPECIAL_NAME 0x1000 +#define METHOD_ATTRIBUTE_HAS_SECURITY 0x4000 +#define METHOD_ATTRIBUTE_REQUIRE_SEC_OBJECT 0x8000 + + +/* + * Method Semantics ([MethodSemanticAttributes]) 22.1.10 + */ + +#define METHOD_SEMANTIC_SETTER 0x0001 +#define METHOD_SEMANTIC_GETTER 0x0002 +#define METHOD_SEMANTIC_OTHER 0x0004 +#define METHOD_SEMANTIC_ADD_ON 0x0008 +#define METHOD_SEMANTIC_REMOVE_ON 0x0010 +#define METHOD_SEMANTIC_FIRE 0x0020 + +/* + * Flags for Params (22.1.12) + */ +#define PARAM_ATTRIBUTE_IN 0x0001 +#define PARAM_ATTRIBUTE_OUT 0x0002 +#define PARAM_ATTRIBUTE_OPTIONAL 0x0010 +#define PARAM_ATTRIBUTE_RESERVED_MASK 0xf000 +#define PARAM_ATTRIBUTE_HAS_DEFAULT 0x1000 +#define PARAM_ATTRIBUTE_HAS_FIELD_MARSHAL 0x2000 +#define PARAM_ATTRIBUTE_UNUSED 0xcfe0 + +/* + * 22.1.12 PropertyAttributes + */ +#define PROPERTY_ATTRIBUTE_SPECIAL_NAME 0x0200 +#define PROPERTY_ATTRIBUTE_RESERVED_MASK 0xf400 +#define PROPERTY_ATTRIBUTE_RT_SPECIAL_NAME 0x0400 +#define PROPERTY_ATTRIBUTE_HAS_DEFAULT 0x1000 +#define PROPERTY_ATTRIBUTE_UNUSED 0xe9ff + +/* + * 22.1.7 Flags for ImplMap [PInvokeAttributes] + */ +#define PINVOKE_ATTRIBUTE_NO_MANGLE 0x0001 +#define PINVOKE_ATTRIBUTE_CHAR_SET_MASK 0x0006 +#define PINVOKE_ATTRIBUTE_CHAR_SET_NOT_SPEC 0x0000 +#define PINVOKE_ATTRIBUTE_CHAR_SET_ANSI 0x0002 +#define PINVOKE_ATTRIBUTE_CHAR_SET_UNICODE 0x0004 +#define PINVOKE_ATTRIBUTE_CHAR_SET_AUTO 0x0006 +#define PINVOKE_ATTRIBUTE_BEST_FIT_ENABLED 0x0010 +#define PINVOKE_ATTRIBUTE_BEST_FIT_DISABLED 0x0020 +#define PINVOKE_ATTRIBUTE_BEST_FIT_MASK 0x0030 +#define PINVOKE_ATTRIBUTE_SUPPORTS_LAST_ERROR 0x0040 +#define PINVOKE_ATTRIBUTE_CALL_CONV_MASK 0x0700 +#define PINVOKE_ATTRIBUTE_CALL_CONV_WINAPI 0x0100 +#define PINVOKE_ATTRIBUTE_CALL_CONV_CDECL 0x0200 +#define PINVOKE_ATTRIBUTE_CALL_CONV_STDCALL 0x0300 +#define PINVOKE_ATTRIBUTE_CALL_CONV_THISCALL 0x0400 +#define PINVOKE_ATTRIBUTE_CALL_CONV_FASTCALL 0x0500 +#define PINVOKE_ATTRIBUTE_THROW_ON_UNMAPPABLE_ENABLED 0x1000 +#define PINVOKE_ATTRIBUTE_THROW_ON_UNMAPPABLE_DISABLED 0x2000 +#define PINVOKE_ATTRIBUTE_THROW_ON_UNMAPPABLE_MASK 0x3000 +#define PINVOKE_ATTRIBUTE_BEST_FIT_MASK 0x0030 +#define PINVOKE_ATTRIBUTE_CALL_CONV_GENERIC 0x0010 +#define PINVOKE_ATTRIBUTE_CALL_CONV_GENERICINST 0x000a + +/** + * 21.5 AssemblyRefs + */ +#define ASSEMBLYREF_FULL_PUBLIC_KEY_FLAG 0x00000001 +#define ASSEMBLYREF_RETARGETABLE_FLAG 0x00000100 +#define ASSEMBLYREF_ENABLEJITCOMPILE_TRACKING_FLAG 0x00008000 +#define ASSEMBLYREF_DISABLEJITCOMPILE_OPTIMIZER_FLAG 0x00004000 +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/threadpool-io-epoll.c b/unity-2019.4.24f1-mbe/mono/metadata/threadpool-io-epoll.c new file mode 100644 index 000000000..67d555707 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/threadpool-io-epoll.c @@ -0,0 +1,132 @@ +/** + * \file + */ + +#if defined(HAVE_EPOLL) + +#include + +#if defined(HOST_WIN32) +/* We assume that epoll is not available on windows */ +#error +#endif + +#define EPOLL_NEVENTS 128 + +static gint epoll_fd; +static struct epoll_event *epoll_events; + +static gboolean +epoll_init (gint wakeup_pipe_fd) +{ + struct epoll_event event; + +#ifdef EPOOL_CLOEXEC + epoll_fd = epoll_create1 (EPOLL_CLOEXEC); +#else + epoll_fd = epoll_create (256); + fcntl (epoll_fd, F_SETFD, FD_CLOEXEC); +#endif + + if (epoll_fd == -1) { +#ifdef EPOOL_CLOEXEC + g_error ("epoll_init: epoll (EPOLL_CLOEXEC) failed, error (%d) %s\n", errno, g_strerror (errno)); +#else + g_error ("epoll_init: epoll (256) failed, error (%d) %s\n", errno, g_strerror (errno)); +#endif + return FALSE; + } + + event.events = EPOLLIN; + event.data.fd = wakeup_pipe_fd; + if (epoll_ctl (epoll_fd, EPOLL_CTL_ADD, event.data.fd, &event) == -1) { + g_error ("epoll_init: epoll_ctl () failed, error (%d) %s", errno, g_strerror (errno)); + close (epoll_fd); + return FALSE; + } + + epoll_events = g_new0 (struct epoll_event, EPOLL_NEVENTS); + + return TRUE; +} + +static void +epoll_register_fd (gint fd, gint events, gboolean is_new) +{ + struct epoll_event event; + +#ifndef EPOLLONESHOT +/* it was only defined on android in May 2013 */ +#define EPOLLONESHOT 0x40000000 +#endif + + event.data.fd = fd; + event.events = EPOLLONESHOT; + if ((events & EVENT_IN) != 0) + event.events |= EPOLLIN; + if ((events & EVENT_OUT) != 0) + event.events |= EPOLLOUT; + + if (epoll_ctl (epoll_fd, is_new ? EPOLL_CTL_ADD : EPOLL_CTL_MOD, event.data.fd, &event) == -1) + g_error ("epoll_register_fd: epoll_ctl(%s) failed, error (%d) %s", is_new ? "EPOLL_CTL_ADD" : "EPOLL_CTL_MOD", errno, g_strerror (errno)); +} + +static void +epoll_remove_fd (gint fd) +{ + if (epoll_ctl (epoll_fd, EPOLL_CTL_DEL, fd, NULL) == -1) + g_error ("epoll_remove_fd: epoll_ctl (EPOLL_CTL_DEL) failed, error (%d) %s", errno, g_strerror (errno)); +} + +static gint +epoll_event_wait (void (*callback) (gint fd, gint events, gpointer user_data), gpointer user_data) +{ + gint i, ready; + + memset (epoll_events, 0, sizeof (struct epoll_event) * EPOLL_NEVENTS); + + mono_gc_set_skip_thread (TRUE); + + MONO_ENTER_GC_SAFE; + ready = epoll_wait (epoll_fd, epoll_events, EPOLL_NEVENTS, -1); + MONO_EXIT_GC_SAFE; + + mono_gc_set_skip_thread (FALSE); + + if (ready == -1) { + switch (errno) { + case EINTR: + ready = 0; + break; + default: + g_error ("epoll_event_wait: epoll_wait () failed, error (%d) %s", errno, g_strerror (errno)); + break; + } + } + + if (ready == -1) + return -1; + + for (i = 0; i < ready; ++i) { + gint fd, events = 0; + + fd = epoll_events [i].data.fd; + if (epoll_events [i].events & (EPOLLIN | EPOLLERR | EPOLLHUP)) + events |= EVENT_IN; + if (epoll_events [i].events & (EPOLLOUT | EPOLLERR | EPOLLHUP)) + events |= EVENT_OUT; + + callback (fd, events, user_data); + } + + return 0; +} + +static ThreadPoolIOBackend backend_epoll = { + .init = epoll_init, + .register_fd = epoll_register_fd, + .remove_fd = epoll_remove_fd, + .event_wait = epoll_event_wait, +}; + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/threadpool-io-kqueue.c b/unity-2019.4.24f1-mbe/mono/metadata/threadpool-io-kqueue.c new file mode 100644 index 000000000..657fad7e3 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/threadpool-io-kqueue.c @@ -0,0 +1,129 @@ +/** + * \file + */ + +#if defined(HAVE_KQUEUE) + +#include +#include +#include + +#if defined(HOST_WIN32) +/* We assume that kqueue is not available on windows */ +#error +#endif + +#define KQUEUE_NEVENTS 128 + +static gint kqueue_fd; +static struct kevent *kqueue_events; + +static gint +KQUEUE_INIT_FD (gint fd, gint events, gint flags) +{ + struct kevent event; + EV_SET (&event, fd, events, flags, 0, 0, 0); + return kevent (kqueue_fd, &event, 1, NULL, 0, NULL); +} + +static gboolean +kqueue_init (gint wakeup_pipe_fd) +{ + kqueue_fd = kqueue (); + if (kqueue_fd == -1) { + g_error ("kqueue_init: kqueue () failed, error (%d) %s", errno, g_strerror (errno)); + return FALSE; + } + + if (KQUEUE_INIT_FD (wakeup_pipe_fd, EVFILT_READ, EV_ADD | EV_ENABLE) == -1) { + g_error ("kqueue_init: kevent () failed, error (%d) %s", errno, g_strerror (errno)); + close (kqueue_fd); + return FALSE; + } + + kqueue_events = g_new0 (struct kevent, KQUEUE_NEVENTS); + + return TRUE; +} + +static void +kqueue_register_fd (gint fd, gint events, gboolean is_new) +{ + if (events & EVENT_IN) { + if (KQUEUE_INIT_FD (fd, EVFILT_READ, EV_ADD | EV_ENABLE) == -1) + g_error ("kqueue_register_fd: kevent(read,enable) failed, error (%d) %s", errno, g_strerror (errno)); + } else { + if (KQUEUE_INIT_FD (fd, EVFILT_READ, EV_ADD | EV_DISABLE) == -1) + g_error ("kqueue_register_fd: kevent(read,disable) failed, error (%d) %s", errno, g_strerror (errno)); + } + if (events & EVENT_OUT) { + if (KQUEUE_INIT_FD (fd, EVFILT_WRITE, EV_ADD | EV_ENABLE) == -1) + g_error ("kqueue_register_fd: kevent(write,enable) failed, error (%d) %s", errno, g_strerror (errno)); + } else { + if (KQUEUE_INIT_FD (fd, EVFILT_WRITE, EV_ADD | EV_DISABLE) == -1) + g_error ("kqueue_register_fd: kevent(write,disable) failed, error (%d) %s", errno, g_strerror (errno)); + } +} + +static void +kqueue_remove_fd (gint fd) +{ + /* FIXME: a race between closing and adding operation in the Socket managed code trigger a ENOENT error */ + if (KQUEUE_INIT_FD (fd, EVFILT_READ, EV_DELETE) == -1) + g_error ("kqueue_register_fd: kevent(read,delete) failed, error (%d) %s", errno, g_strerror (errno)); + if (KQUEUE_INIT_FD (fd, EVFILT_WRITE, EV_DELETE) == -1) + g_error ("kqueue_register_fd: kevent(write,delete) failed, error (%d) %s", errno, g_strerror (errno)); +} + +static gint +kqueue_event_wait (void (*callback) (gint fd, gint events, gpointer user_data), gpointer user_data) +{ + gint i, ready; + + memset (kqueue_events, 0, sizeof (struct kevent) * KQUEUE_NEVENTS); + + mono_gc_set_skip_thread (TRUE); + + MONO_ENTER_GC_SAFE; + ready = kevent (kqueue_fd, NULL, 0, kqueue_events, KQUEUE_NEVENTS, NULL); + MONO_EXIT_GC_SAFE; + + mono_gc_set_skip_thread (FALSE); + + if (ready == -1) { + switch (errno) { + case EINTR: + ready = 0; + break; + default: + g_error ("kqueue_event_wait: kevent () failed, error (%d) %s", errno, g_strerror (errno)); + break; + } + } + + if (ready == -1) + return -1; + + for (i = 0; i < ready; ++i) { + gint fd, events = 0; + + fd = kqueue_events [i].ident; + if (kqueue_events [i].filter == EVFILT_READ || (kqueue_events [i].flags & EV_ERROR) != 0) + events |= EVENT_IN; + if (kqueue_events [i].filter == EVFILT_WRITE || (kqueue_events [i].flags & EV_ERROR) != 0) + events |= EVENT_OUT; + + callback (fd, events, user_data); + } + + return 0; +} + +static ThreadPoolIOBackend backend_kqueue = { + .init = kqueue_init, + .register_fd = kqueue_register_fd, + .remove_fd = kqueue_remove_fd, + .event_wait = kqueue_event_wait, +}; + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/threadpool-io-poll.c b/unity-2019.4.24f1-mbe/mono/metadata/threadpool-io-poll.c new file mode 100644 index 000000000..8ff6b13c6 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/threadpool-io-poll.c @@ -0,0 +1,220 @@ +/** + * \file + */ + +#include "utils/mono-poll.h" + +static mono_pollfd *poll_fds; +static guint poll_fds_capacity; +static guint poll_fds_size; + +static inline void +POLL_INIT_FD (mono_pollfd *poll_fd, gint fd, gint events) +{ + poll_fd->fd = fd; + poll_fd->events = events; + poll_fd->revents = 0; +} + +static gboolean +poll_init (gint wakeup_pipe_fd) +{ + g_assert (wakeup_pipe_fd >= 0); + + poll_fds_size = 1; + poll_fds_capacity = 64; + + poll_fds = g_new0 (mono_pollfd, poll_fds_capacity); + + POLL_INIT_FD (&poll_fds [0], wakeup_pipe_fd, MONO_POLLIN); + + return TRUE; +} + +static void +poll_register_fd (gint fd, gint events, gboolean is_new) +{ + gint i; + gint poll_event; + + g_assert (fd >= 0); + g_assert (poll_fds_size <= poll_fds_capacity); + + g_assert ((events & ~(EVENT_IN | EVENT_OUT)) == 0); + + poll_event = 0; + if (events & EVENT_IN) + poll_event |= MONO_POLLIN; + if (events & EVENT_OUT) + poll_event |= MONO_POLLOUT; + + for (i = 0; i < poll_fds_size; ++i) { + if (poll_fds [i].fd == fd) { + g_assert (!is_new); + POLL_INIT_FD (&poll_fds [i], fd, poll_event); + return; + } + } + + g_assert (is_new); + + for (i = 0; i < poll_fds_size; ++i) { + if (poll_fds [i].fd == -1) { + POLL_INIT_FD (&poll_fds [i], fd, poll_event); + return; + } + } + + poll_fds_size += 1; + + if (poll_fds_size > poll_fds_capacity) { + poll_fds_capacity *= 2; + g_assert (poll_fds_size <= poll_fds_capacity); + + poll_fds = (mono_pollfd *)g_renew (mono_pollfd, poll_fds, poll_fds_capacity); + } + + POLL_INIT_FD (&poll_fds [poll_fds_size - 1], fd, poll_event); +} + +static void +poll_remove_fd (gint fd) +{ + gint i; + + g_assert (fd >= 0); + + for (i = 0; i < poll_fds_size; ++i) { + if (poll_fds [i].fd == fd) { + POLL_INIT_FD (&poll_fds [i], -1, 0); + break; + } + } + + /* if we don't find the fd in poll_fds, + * it means we try to delete it twice */ + g_assert (i < poll_fds_size); + + /* if we find it again, it means we added + * it twice */ + for (; i < poll_fds_size; ++i) + g_assert (poll_fds [i].fd != fd); + + /* reduce the value of poll_fds_size so we + * do not keep it too big */ + while (poll_fds_size > 1 && poll_fds [poll_fds_size - 1].fd == -1) + poll_fds_size -= 1; +} + +static inline gint +poll_mark_bad_fds (mono_pollfd *poll_fds, gint poll_fds_size) +{ + gint i, ready = 0; + + for (i = 0; i < poll_fds_size; i++) { + if (poll_fds [i].fd == -1) + continue; + + switch (mono_poll (&poll_fds [i], 1, 0)) { + case 1: + ready++; + break; + case -1: + if (errno == EBADF) + { + poll_fds [i].revents |= MONO_POLLNVAL; + ready++; + } + break; + } + } + + return ready; +} + +static gint +poll_event_wait (void (*callback) (gint fd, gint events, gpointer user_data), gpointer user_data) +{ + gint i, ready; + + for (i = 0; i < poll_fds_size; ++i) + poll_fds [i].revents = 0; + + mono_gc_set_skip_thread (TRUE); + + MONO_ENTER_GC_SAFE; + ready = mono_poll (poll_fds, poll_fds_size, -1); + MONO_EXIT_GC_SAFE; + + mono_gc_set_skip_thread (FALSE); + + if (ready == -1) { + /* + * Apart from EINTR, we only check EBADF, for the rest: + * EINVAL: mono_poll() 'protects' us from descriptor + * numbers above the limit if using select() by marking + * then as POLLERR. If a system poll() is being + * used, the number of descriptor we're passing will not + * be over sysconf(_SC_OPEN_MAX), as the error would have + * happened when opening. + * + * EFAULT: we own the memory pointed by pfds. + * ENOMEM: we're doomed anyway + * + */ + switch (errno) + { + case EINTR: + { + ready = 0; + break; + } + case EBADF: + { + ready = poll_mark_bad_fds (poll_fds, poll_fds_size); + break; + } + default: + g_error ("poll_event_wait: mono_poll () failed, error (%d) %s", errno, g_strerror (errno)); + break; + } + } + + if (ready == -1) + return -1; + if (ready == 0) + return 0; + + g_assert (ready > 0); + + for (i = 0; i < poll_fds_size; ++i) { + gint fd, events = 0; + + if (poll_fds [i].fd == -1) + continue; + if (poll_fds [i].revents == 0) + continue; + + fd = poll_fds [i].fd; + if (poll_fds [i].revents & (MONO_POLLIN | MONO_POLLERR | MONO_POLLHUP | MONO_POLLNVAL)) + events |= EVENT_IN; + if (poll_fds [i].revents & (MONO_POLLOUT | MONO_POLLERR | MONO_POLLHUP | MONO_POLLNVAL)) + events |= EVENT_OUT; + if (poll_fds [i].revents & (MONO_POLLERR | MONO_POLLHUP | MONO_POLLNVAL)) + events |= EVENT_ERR; + + callback (fd, events, user_data); + + if (--ready == 0) + break; + } + + return 0; +} + +static ThreadPoolIOBackend backend_poll = { + .init = poll_init, + .register_fd = poll_register_fd, + .remove_fd = poll_remove_fd, + .event_wait = poll_event_wait, +}; diff --git a/unity-2019.4.24f1-mbe/mono/metadata/threadpool-io.c b/unity-2019.4.24f1-mbe/mono/metadata/threadpool-io.c new file mode 100644 index 000000000..4f338bdde --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/threadpool-io.c @@ -0,0 +1,728 @@ +/** + * \file + * Microsoft IO threadpool runtime support + * + * Author: + * Ludovic Henry (ludovic.henry@xamarin.com) + * + * Copyright 2015 Xamarin, Inc (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include + +#ifndef DISABLE_SOCKETS + +#include + +#if defined(HOST_WIN32) +#include +#else +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + gboolean (*init) (gint wakeup_pipe_fd); + void (*register_fd) (gint fd, gint events, gboolean is_new); + void (*remove_fd) (gint fd); + gint (*event_wait) (void (*callback) (gint fd, gint events, gpointer user_data), gpointer user_data); +} ThreadPoolIOBackend; + +/* Keep in sync with System.IOOperation in mcs/class/System/System/IOSelector.cs */ +enum MonoIOOperation { + EVENT_IN = 1 << 0, + EVENT_OUT = 1 << 1, + EVENT_ERR = 1 << 2, /* not in managed */ +}; + +#include "threadpool-io-epoll.c" +#include "threadpool-io-kqueue.c" +#include "threadpool-io-poll.c" + +#define UPDATES_CAPACITY 128 + +/* Keep in sync with System.IOSelectorJob in mcs/class/System/System/IOSelector.cs */ +struct _MonoIOSelectorJob { + MonoObject object; + gint32 operation; + MonoObject *callback; + MonoObject *state; +}; + +typedef enum { + UPDATE_EMPTY = 0, + UPDATE_ADD, + UPDATE_REMOVE_SOCKET, + UPDATE_REMOVE_DOMAIN, +} ThreadPoolIOUpdateType; + +typedef struct { + gint fd; + MonoIOSelectorJob *job; +} ThreadPoolIOUpdate_Add; + +typedef struct { + gint fd; +} ThreadPoolIOUpdate_RemoveSocket; + +typedef struct { + MonoDomain *domain; +} ThreadPoolIOUpdate_RemoveDomain; + +typedef struct { + ThreadPoolIOUpdateType type; + union { + ThreadPoolIOUpdate_Add add; + ThreadPoolIOUpdate_RemoveSocket remove_socket; + ThreadPoolIOUpdate_RemoveDomain remove_domain; + } data; +} ThreadPoolIOUpdate; + +typedef struct { + ThreadPoolIOBackend backend; + + ThreadPoolIOUpdate updates [UPDATES_CAPACITY]; + gint updates_size; + MonoCoopMutex updates_lock; + MonoCoopCond updates_cond; + +#if !defined(HOST_WIN32) + gint wakeup_pipes [2]; +#else + SOCKET wakeup_pipes [2]; +#endif +} ThreadPoolIO; + +static mono_lazy_init_t io_status = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED; + +static gboolean io_selector_running = FALSE; + +static ThreadPoolIO* threadpool_io; + +static MonoIOSelectorJob* +get_job_for_event (MonoMList **list, gint32 event) +{ + MonoMList *current; + + g_assert (list); + + for (current = *list; current; current = mono_mlist_next (current)) { + MonoIOSelectorJob *job = (MonoIOSelectorJob*) mono_mlist_get_data (current); + if (job->operation == event) { + *list = mono_mlist_remove_item (*list, current); + return job; + } + } + + return NULL; +} + +static gint +get_operations_for_jobs (MonoMList *list) +{ + MonoMList *current; + gint operations = 0; + + for (current = list; current; current = mono_mlist_next (current)) + operations |= ((MonoIOSelectorJob*) mono_mlist_get_data (current))->operation; + + return operations; +} + +static void +selector_thread_wakeup (void) +{ + gchar msg = 'c'; + gint written; + + for (;;) { +#if !defined(HOST_WIN32) + written = write (threadpool_io->wakeup_pipes [1], &msg, 1); + if (written == 1) + break; + if (written == -1) { + g_warning ("selector_thread_wakeup: write () failed, error (%d) %s\n", errno, g_strerror (errno)); + break; + } +#else + written = send (threadpool_io->wakeup_pipes [1], &msg, 1, 0); + if (written == 1) + break; + if (written == SOCKET_ERROR) { + g_warning ("selector_thread_wakeup: write () failed, error (%d)\n", WSAGetLastError ()); + break; + } +#endif + } +} + +static void +selector_thread_wakeup_drain_pipes (void) +{ + gchar buffer [128]; + gint received; + static gint warnings_issued = 0; + + for (;;) { +#if !defined(HOST_WIN32) + received = read (threadpool_io->wakeup_pipes [0], buffer, sizeof (buffer)); + if (received == 0) + break; + if (received == -1) { + 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 + received = recv (threadpool_io->wakeup_pipes [0], buffer, sizeof (buffer), 0); + if (received == 0) + break; + if (received == SOCKET_ERROR) { + 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 + } +} + +typedef struct { + MonoDomain *domain; + MonoGHashTable *states; +} FilterSockaresForDomainData; + +static void +filter_jobs_for_domain (gpointer key, gpointer value, gpointer user_data) +{ + FilterSockaresForDomainData *data; + MonoMList *list = (MonoMList *)value, *element; + MonoDomain *domain; + MonoGHashTable *states; + + g_assert (user_data); + data = (FilterSockaresForDomainData *)user_data; + domain = data->domain; + states = data->states; + + for (element = list; element; element = mono_mlist_next (element)) { + MonoIOSelectorJob *job = (MonoIOSelectorJob*) mono_mlist_get_data (element); + if (mono_object_domain (job) == domain) + mono_mlist_set_data (element, NULL); + } + + /* we skip all the first elements which are NULL */ + for (; list; list = mono_mlist_next (list)) { + if (mono_mlist_get_data (list)) + break; + } + + if (list) { + g_assert (mono_mlist_get_data (list)); + + /* we delete all the NULL elements after the first one */ + for (element = list; element;) { + MonoMList *next; + if (!(next = mono_mlist_next (element))) + break; + if (mono_mlist_get_data (next)) + element = next; + else + mono_mlist_set_next (element, mono_mlist_next (next)); + } + } + + mono_g_hash_table_replace (states, key, list); +} + +static void +wait_callback (gint fd, gint events, gpointer user_data) +{ + MonoError error; + + if (mono_runtime_is_shutting_down ()) + return; + + if (fd == threadpool_io->wakeup_pipes [0]) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_SELECTOR, "io threadpool: wke"); + selector_thread_wakeup_drain_pipes (); + } else { + MonoGHashTable *states; + MonoMList *list = NULL; + gpointer k; + gboolean remove_fd = FALSE; + gint operations; + + g_assert (user_data); + states = (MonoGHashTable *)user_data; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_SELECTOR, "io threadpool: cal fd %3d, events = %2s | %2s | %3s", + fd, (events & EVENT_IN) ? "RD" : "..", (events & EVENT_OUT) ? "WR" : "..", (events & EVENT_ERR) ? "ERR" : "..."); + + if (!mono_g_hash_table_lookup_extended (states, GINT_TO_POINTER (fd), &k, (gpointer*) &list)) + g_error ("wait_callback: fd %d not found in states table", fd); + + if (list && (events & EVENT_IN) != 0) { + MonoIOSelectorJob *job = get_job_for_event (&list, EVENT_IN); + if (job) { + mono_threadpool_enqueue_work_item (((MonoObject*) job)->vtable->domain, (MonoObject*) job, &error); + mono_error_assert_ok (&error); + } + + } + if (list && (events & EVENT_OUT) != 0) { + MonoIOSelectorJob *job = get_job_for_event (&list, EVENT_OUT); + if (job) { + mono_threadpool_enqueue_work_item (((MonoObject*) job)->vtable->domain, (MonoObject*) job, &error); + mono_error_assert_ok (&error); + } + } + + remove_fd = (events & EVENT_ERR) == EVENT_ERR; + if (!remove_fd) { + mono_g_hash_table_replace (states, GINT_TO_POINTER (fd), list); + + operations = get_operations_for_jobs (list); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_SELECTOR, "io threadpool: res fd %3d, events = %2s | %2s | %3s", + fd, (operations & EVENT_IN) ? "RD" : "..", (operations & EVENT_OUT) ? "WR" : "..", (operations & EVENT_ERR) ? "ERR" : "..."); + + threadpool_io->backend.register_fd (fd, operations, FALSE); + } else { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_SELECTOR, "io threadpool: err fd %d", fd); + + mono_g_hash_table_remove (states, GINT_TO_POINTER (fd)); + + threadpool_io->backend.remove_fd (fd); + } + } +} + +static void +selector_thread_interrupt (gpointer unused) +{ + selector_thread_wakeup (); +} + +static gsize WINAPI +selector_thread (gpointer data) +{ + MonoError error; + MonoGHashTable *states; + + MonoString *thread_name = mono_string_new_checked (mono_get_root_domain (), "Thread Pool I/O Selector", &error); + mono_error_assert_ok (&error); + mono_thread_set_name_internal (mono_thread_internal_current (), thread_name, FALSE, TRUE, &error); + mono_error_assert_ok (&error); + + if (mono_runtime_is_shutting_down ()) { + io_selector_running = FALSE; + return 0; + } + + states = mono_g_hash_table_new_type (g_direct_hash, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_THREAD_POOL, NULL, "Thread Pool I/O State Table"); + + while (!mono_runtime_is_shutting_down ()) { + gint i, j; + gint res; + gboolean interrupted = FALSE; + + if (mono_thread_interruption_checkpoint ()) + continue; + + mono_coop_mutex_lock (&threadpool_io->updates_lock); + + for (i = 0; i < threadpool_io->updates_size; ++i) { + ThreadPoolIOUpdate *update = &threadpool_io->updates [i]; + + switch (update->type) { + case UPDATE_EMPTY: + break; + case UPDATE_ADD: { + gint fd; + gint operations; + gpointer k; + gboolean exists; + MonoMList *list = NULL; + MonoIOSelectorJob *job; + + fd = update->data.add.fd; + g_assert (fd >= 0); + + job = update->data.add.job; + g_assert (job); + + exists = mono_g_hash_table_lookup_extended (states, GINT_TO_POINTER (fd), &k, (gpointer*) &list); + list = mono_mlist_append_checked (list, (MonoObject*) job, &error); + mono_error_assert_ok (&error); + mono_g_hash_table_replace (states, GINT_TO_POINTER (fd), list); + + operations = get_operations_for_jobs (list); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_SELECTOR, "io threadpool: %3s fd %3d, operations = %2s | %2s | %3s", + exists ? "mod" : "add", fd, (operations & EVENT_IN) ? "RD" : "..", (operations & EVENT_OUT) ? "WR" : "..", (operations & EVENT_ERR) ? "ERR" : "..."); + + threadpool_io->backend.register_fd (fd, operations, !exists); + + break; + } + case UPDATE_REMOVE_SOCKET: { + gint fd; + gpointer k; + MonoMList *list = NULL; + + fd = update->data.remove_socket.fd; + g_assert (fd >= 0); + + if (mono_g_hash_table_lookup_extended (states, GINT_TO_POINTER (fd), &k, (gpointer*) &list)) { + mono_g_hash_table_remove (states, GINT_TO_POINTER (fd)); + + for (j = i + 1; j < threadpool_io->updates_size; ++j) { + ThreadPoolIOUpdate *update = &threadpool_io->updates [j]; + if (update->type == UPDATE_ADD && update->data.add.fd == fd) + memset (update, 0, sizeof (ThreadPoolIOUpdate)); + } + + for (; list; list = mono_mlist_remove_item (list, list)) { + mono_threadpool_enqueue_work_item (mono_object_domain (mono_mlist_get_data (list)), mono_mlist_get_data (list), &error); + mono_error_assert_ok (&error); + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_SELECTOR, "io threadpool: del fd %3d", fd); + threadpool_io->backend.remove_fd (fd); + } + + break; + } + case UPDATE_REMOVE_DOMAIN: { + MonoDomain *domain; + + domain = update->data.remove_domain.domain; + g_assert (domain); + + FilterSockaresForDomainData user_data = { .domain = domain, .states = states }; + mono_g_hash_table_foreach (states, filter_jobs_for_domain, &user_data); + + for (j = i + 1; j < threadpool_io->updates_size; ++j) { + ThreadPoolIOUpdate *update = &threadpool_io->updates [j]; + if (update->type == UPDATE_ADD && mono_object_domain (update->data.add.job) == domain) + memset (update, 0, sizeof (ThreadPoolIOUpdate)); + } + + break; + } + default: + g_assert_not_reached (); + } + } + + mono_coop_cond_broadcast (&threadpool_io->updates_cond); + + if (threadpool_io->updates_size > 0) { + threadpool_io->updates_size = 0; + memset (&threadpool_io->updates, 0, UPDATES_CAPACITY * sizeof (ThreadPoolIOUpdate)); + } + + mono_coop_mutex_unlock (&threadpool_io->updates_lock); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_SELECTOR, "io threadpool: wai"); + + mono_thread_info_install_interrupt (selector_thread_interrupt, NULL, &interrupted); + if (interrupted) + continue; + + res = threadpool_io->backend.event_wait (wait_callback, states); + if (res == -1) + break; + + mono_thread_info_uninstall_interrupt (&interrupted); + } + + mono_g_hash_table_destroy (states); + + mono_coop_mutex_lock (&threadpool_io->updates_lock); + + io_selector_running = FALSE; + mono_coop_cond_broadcast (&threadpool_io->updates_cond); + + mono_coop_mutex_unlock (&threadpool_io->updates_lock); + + return 0; +} + +/* Locking: threadpool_io->updates_lock must be held */ +static ThreadPoolIOUpdate* +update_get_new (void) +{ + ThreadPoolIOUpdate *update = NULL; + g_assert (threadpool_io->updates_size <= UPDATES_CAPACITY); + + while (threadpool_io->updates_size == UPDATES_CAPACITY) { + /* we wait for updates to be applied in the selector_thread and we loop + * as long as none are available. if it happends too much, then we need + * to increase UPDATES_CAPACITY */ + mono_coop_cond_wait (&threadpool_io->updates_cond, &threadpool_io->updates_lock); + } + + g_assert (threadpool_io->updates_size < UPDATES_CAPACITY); + + update = &threadpool_io->updates [threadpool_io->updates_size ++]; + + return update; +} + +static void +wakeup_pipes_init (void) +{ +#if !defined(HOST_WIN32) + if (pipe (threadpool_io->wakeup_pipes) == -1) + g_error ("wakeup_pipes_init: pipe () failed, error (%d) %s\n", errno, g_strerror (errno)); + if (fcntl (threadpool_io->wakeup_pipes [0], F_SETFL, O_NONBLOCK) == -1) + g_error ("wakeup_pipes_init: fcntl () failed, error (%d) %s\n", errno, g_strerror (errno)); +#else + struct sockaddr_in client; + struct sockaddr_in server; + SOCKET server_sock; + gulong arg; + gint size; + + server_sock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + g_assert (server_sock != INVALID_SOCKET); + threadpool_io->wakeup_pipes [1] = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + g_assert (threadpool_io->wakeup_pipes [1] != INVALID_SOCKET); + + server.sin_family = AF_INET; + server.sin_addr.s_addr = inet_addr ("127.0.0.1"); + server.sin_port = 0; + if (bind (server_sock, (SOCKADDR*) &server, sizeof (server)) == SOCKET_ERROR) { + closesocket (server_sock); + g_error ("wakeup_pipes_init: bind () failed, error (%d)\n", WSAGetLastError ()); + } + + size = sizeof (server); + if (getsockname (server_sock, (SOCKADDR*) &server, &size) == SOCKET_ERROR) { + closesocket (server_sock); + g_error ("wakeup_pipes_init: getsockname () failed, error (%d)\n", WSAGetLastError ()); + } + if (listen (server_sock, 1024) == SOCKET_ERROR) { + closesocket (server_sock); + g_error ("wakeup_pipes_init: listen () failed, error (%d)\n", WSAGetLastError ()); + } + if (connect ((SOCKET) threadpool_io->wakeup_pipes [1], (SOCKADDR*) &server, sizeof (server)) == SOCKET_ERROR) { + closesocket (server_sock); + g_error ("wakeup_pipes_init: connect () failed, error (%d)\n", WSAGetLastError ()); + } + + size = sizeof (client); + threadpool_io->wakeup_pipes [0] = accept (server_sock, (SOCKADDR *) &client, &size); + g_assert (threadpool_io->wakeup_pipes [0] != INVALID_SOCKET); + + arg = 1; + if (ioctlsocket (threadpool_io->wakeup_pipes [0], FIONBIO, &arg) == SOCKET_ERROR) { + closesocket (threadpool_io->wakeup_pipes [0]); + closesocket (server_sock); + g_error ("wakeup_pipes_init: ioctlsocket () failed, error (%d)\n", WSAGetLastError ()); + } + + closesocket (server_sock); +#endif +} + +static void +initialize (void) +{ + g_assert (!threadpool_io); + threadpool_io = g_new0 (ThreadPoolIO, 1); + g_assert (threadpool_io); + + mono_coop_mutex_init (&threadpool_io->updates_lock); + mono_coop_cond_init (&threadpool_io->updates_cond); + mono_gc_register_root ((char *)&threadpool_io->updates [0], sizeof (threadpool_io->updates), MONO_GC_DESCRIPTOR_NULL, MONO_ROOT_SOURCE_THREAD_POOL, NULL, "Thread Pool I/O Update List"); + + threadpool_io->updates_size = 0; + + threadpool_io->backend = backend_poll; + if (g_hasenv ("MONO_ENABLE_AIO")) { +#if defined(HAVE_EPOLL) + threadpool_io->backend = backend_epoll; +#elif defined(HAVE_KQUEUE) + threadpool_io->backend = backend_kqueue; +#endif + } + + wakeup_pipes_init (); + + if (!threadpool_io->backend.init (threadpool_io->wakeup_pipes [0])) + g_error ("initialize: backend->init () failed"); + + mono_coop_mutex_lock (&threadpool_io->updates_lock); + + io_selector_running = TRUE; + + MonoError error; + if (!mono_thread_create_internal (mono_get_root_domain (), selector_thread, NULL, MONO_THREAD_CREATE_FLAGS_THREADPOOL | MONO_THREAD_CREATE_FLAGS_SMALL_STACK, &error)) + g_error ("initialize: mono_thread_create_internal () failed due to %s", mono_error_get_message (&error)); + + mono_coop_mutex_unlock (&threadpool_io->updates_lock); +} + +static void +cleanup (void) +{ + // FIXME destroy everything +} + +void +mono_threadpool_io_cleanup (void) +{ + mono_lazy_cleanup (&io_status, cleanup); +} + +void +ves_icall_System_IOSelector_Add (gpointer handle, MonoIOSelectorJob *job) +{ + ThreadPoolIOUpdate *update; + + g_assert (handle); + + g_assert ((job->operation == EVENT_IN) ^ (job->operation == EVENT_OUT)); + g_assert (job->callback); + + if (mono_runtime_is_shutting_down ()) + return; + if (mono_domain_is_unloading (mono_object_domain (job))) + return; + + mono_lazy_initialize (&io_status, initialize); + + mono_coop_mutex_lock (&threadpool_io->updates_lock); + + if (!io_selector_running) { + mono_coop_mutex_unlock (&threadpool_io->updates_lock); + return; + } + + update = update_get_new (); + update->type = UPDATE_ADD; + update->data.add.fd = GPOINTER_TO_INT (handle); + update->data.add.job = job; + mono_memory_barrier (); /* Ensure this is safely published before we wake up the selector */ + + selector_thread_wakeup (); + + mono_coop_mutex_unlock (&threadpool_io->updates_lock); +} + +void +ves_icall_System_IOSelector_Remove (gpointer handle) +{ + mono_threadpool_io_remove_socket (GPOINTER_TO_INT (handle)); +} + +void +mono_threadpool_io_remove_socket (int fd) +{ + ThreadPoolIOUpdate *update; + + if (!mono_lazy_is_initialized (&io_status)) + return; + + mono_coop_mutex_lock (&threadpool_io->updates_lock); + + if (!io_selector_running) { + mono_coop_mutex_unlock (&threadpool_io->updates_lock); + return; + } + + update = update_get_new (); + update->type = UPDATE_REMOVE_SOCKET; + update->data.add.fd = fd; + mono_memory_barrier (); /* Ensure this is safely published before we wake up the selector */ + + selector_thread_wakeup (); + + mono_coop_cond_wait (&threadpool_io->updates_cond, &threadpool_io->updates_lock); + + mono_coop_mutex_unlock (&threadpool_io->updates_lock); +} + +void +mono_threadpool_io_remove_domain_jobs (MonoDomain *domain) +{ + ThreadPoolIOUpdate *update; + + if (!mono_lazy_is_initialized (&io_status)) + return; + + mono_coop_mutex_lock (&threadpool_io->updates_lock); + + if (!io_selector_running) { + mono_coop_mutex_unlock (&threadpool_io->updates_lock); + return; + } + + update = update_get_new (); + update->type = UPDATE_REMOVE_DOMAIN; + update->data.remove_domain.domain = domain; + mono_memory_barrier (); /* Ensure this is safely published before we wake up the selector */ + + selector_thread_wakeup (); + + mono_coop_cond_wait (&threadpool_io->updates_cond, &threadpool_io->updates_lock); + + mono_coop_mutex_unlock (&threadpool_io->updates_lock); +} + +#else + +void +ves_icall_System_IOSelector_Add (gpointer handle, MonoIOSelectorJob *job) +{ + g_assert_not_reached (); +} + +void +ves_icall_System_IOSelector_Remove (gpointer handle) +{ + g_assert_not_reached (); +} + +void +mono_threadpool_io_cleanup (void) +{ + g_assert_not_reached (); +} + +void +mono_threadpool_io_remove_socket (int fd) +{ + g_assert_not_reached (); +} + +void +mono_threadpool_io_remove_domain_jobs (MonoDomain *domain) +{ + g_assert_not_reached (); +} + +#endif \ No newline at end of file diff --git a/unity-2019.4.24f1-mbe/mono/metadata/threadpool-io.h b/unity-2019.4.24f1-mbe/mono/metadata/threadpool-io.h new file mode 100644 index 000000000..c3f62f049 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/threadpool-io.h @@ -0,0 +1,28 @@ +/** + * \file + */ + +#ifndef _MONO_METADATA_THREADPOOL_IO_H_ +#define _MONO_METADATA_THREADPOOL_IO_H_ + +#include +#include + +#include + +typedef struct _MonoIOSelectorJob MonoIOSelectorJob; + +void +ves_icall_System_IOSelector_Add (gpointer handle, MonoIOSelectorJob *job); + +void +ves_icall_System_IOSelector_Remove (gpointer handle); + +void +mono_threadpool_io_remove_socket (int fd); +void +mono_threadpool_io_remove_domain_jobs (MonoDomain *domain); +void +mono_threadpool_io_cleanup (void); + +#endif /* _MONO_METADATA_THREADPOOL_IO_H_ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/threadpool-worker-default.c b/unity-2019.4.24f1-mbe/mono/metadata/threadpool-worker-default.c new file mode 100644 index 000000000..8b340c34a --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/threadpool-worker-default.c @@ -0,0 +1,1201 @@ +/** + * \file + * native threadpool worker + * + * Author: + * Ludovic Henry (ludovic.henry@xamarin.com) + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#define _USE_MATH_DEFINES // needed by MSVC to define math constants +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CPU_USAGE_LOW 80 +#define CPU_USAGE_HIGH 95 + +#define MONITOR_INTERVAL 500 // ms +#define MONITOR_MINIMAL_LIFETIME 60 * 1000 // ms + +#define WORKER_CREATION_MAX_PER_SEC 10 + +/* The exponent to apply to the gain. 1.0 means to use linear gain, + * higher values will enhance large moves and damp small ones. + * default: 2.0 */ +#define HILL_CLIMBING_GAIN_EXPONENT 2.0 + +/* The 'cost' of a thread. 0 means drive for increased throughput regardless + * of thread count, higher values bias more against higher thread counts. + * default: 0.15 */ +#define HILL_CLIMBING_BIAS 0.15 + +#define HILL_CLIMBING_WAVE_PERIOD 4 +#define HILL_CLIMBING_MAX_WAVE_MAGNITUDE 20 +#define HILL_CLIMBING_WAVE_MAGNITUDE_MULTIPLIER 1.0 +#define HILL_CLIMBING_WAVE_HISTORY_SIZE 8 +#define HILL_CLIMBING_TARGET_SIGNAL_TO_NOISE_RATIO 3.0 +#define HILL_CLIMBING_MAX_CHANGE_PER_SECOND 4 +#define HILL_CLIMBING_MAX_CHANGE_PER_SAMPLE 20 +#define HILL_CLIMBING_SAMPLE_INTERVAL_LOW 10 +#define HILL_CLIMBING_SAMPLE_INTERVAL_HIGH 200 +#define HILL_CLIMBING_ERROR_SMOOTHING_FACTOR 0.01 +#define HILL_CLIMBING_MAX_SAMPLE_ERROR_PERCENT 0.15 + +typedef enum { + TRANSITION_WARMUP, + TRANSITION_INITIALIZING, + TRANSITION_RANDOM_MOVE, + TRANSITION_CLIMBING_MOVE, + TRANSITION_CHANGE_POINT, + TRANSITION_STABILIZING, + TRANSITION_STARVATION, + TRANSITION_THREAD_TIMED_OUT, + TRANSITION_UNDEFINED, +} ThreadPoolHeuristicStateTransition; + +typedef struct { + gint32 wave_period; + gint32 samples_to_measure; + gdouble target_throughput_ratio; + gdouble target_signal_to_noise_ratio; + gdouble max_change_per_second; + gdouble max_change_per_sample; + gint32 max_thread_wave_magnitude; + gint32 sample_interval_low; + gdouble thread_magnitude_multiplier; + gint32 sample_interval_high; + gdouble throughput_error_smoothing_factor; + gdouble gain_exponent; + gdouble max_sample_error; + + gdouble current_control_setting; + gint64 total_samples; + gint16 last_thread_count; + gdouble elapsed_since_last_change; + gdouble completions_since_last_change; + + gdouble average_throughput_noise; + + gdouble *samples; + gdouble *thread_counts; + + guint32 current_sample_interval; + gpointer random_interval_generator; + + gint32 accumulated_completion_count; + gdouble accumulated_sample_duration; +} ThreadPoolHillClimbing; + +typedef union { + struct { + gint16 max_working; /* determined by heuristic */ + gint16 starting; /* starting, but not yet in worker_thread */ + gint16 working; /* executing worker_thread */ + gint16 parked; /* parked */ + } _; + gint64 as_gint64; +} ThreadPoolWorkerCounter +#ifdef __GNUC__ +__attribute__((aligned(64))) +#endif +; + +typedef struct { + MonoRefCount ref; + + MonoThreadPoolWorkerCallback callback; + + ThreadPoolWorkerCounter counters; + + MonoCoopMutex parked_threads_lock; + gint32 parked_threads_count; + MonoCoopCond parked_threads_cond; + + volatile gint32 work_items_count; + + guint32 worker_creation_current_second; + guint32 worker_creation_current_count; + MonoCoopMutex worker_creation_lock; + + gint32 heuristic_completions; + gint64 heuristic_sample_start; + gint64 heuristic_last_dequeue; // ms + gint64 heuristic_last_adjustment; // ms + gint64 heuristic_adjustment_interval; // ms + ThreadPoolHillClimbing heuristic_hill_climbing; + MonoCoopMutex heuristic_lock; + + gint32 limit_worker_min; + gint32 limit_worker_max; + + MonoCpuUsageState *cpu_usage_state; + gint32 cpu_usage; + + /* suspended by the debugger */ + gboolean suspended; + + gint32 monitor_status; +} ThreadPoolWorker; + +enum { + MONITOR_STATUS_REQUESTED, + MONITOR_STATUS_WAITING_FOR_REQUEST, + MONITOR_STATUS_NOT_RUNNING, +}; + +static ThreadPoolWorker worker; + +#define COUNTER_CHECK(counter) \ + do { \ + g_assert (counter._.max_working > 0); \ + g_assert (counter._.starting >= 0); \ + g_assert (counter._.working >= 0); \ + } while (0) + +#define COUNTER_ATOMIC(var,block) \ + do { \ + ThreadPoolWorkerCounter __old; \ + do { \ + __old = COUNTER_READ (); \ + (var) = __old; \ + { block; } \ + COUNTER_CHECK (var); \ + } while (mono_atomic_cas_i64 (&worker.counters.as_gint64, (var).as_gint64, __old.as_gint64) != __old.as_gint64); \ + } while (0) + +static inline ThreadPoolWorkerCounter +COUNTER_READ (void) +{ + ThreadPoolWorkerCounter counter; + counter.as_gint64 = mono_atomic_load_i64 (&worker.counters.as_gint64); + return counter; +} + +static gpointer +rand_create (void) +{ + mono_rand_open (); + return mono_rand_init (NULL, 0); +} + +static guint32 +rand_next (gpointer *handle, guint32 min, guint32 max) +{ + MonoError error; + guint32 val; + mono_rand_try_get_uint32 (handle, &val, min, max, &error); + // FIXME handle error + mono_error_assert_ok (&error); + return val; +} + +static void +destroy (gpointer data) +{ + mono_coop_mutex_destroy (&worker.parked_threads_lock); + mono_coop_cond_destroy (&worker.parked_threads_cond); + + mono_coop_mutex_destroy (&worker.worker_creation_lock); + + mono_coop_mutex_destroy (&worker.heuristic_lock); + + g_free (worker.cpu_usage_state); +} + +void +mono_threadpool_worker_init (MonoThreadPoolWorkerCallback callback) +{ + ThreadPoolHillClimbing *hc; + const char *threads_per_cpu_env; + gint threads_per_cpu; + gint threads_count; + + mono_refcount_init (&worker, destroy); + + worker.callback = callback; + + mono_coop_mutex_init (&worker.parked_threads_lock); + worker.parked_threads_count = 0; + mono_coop_cond_init (&worker.parked_threads_cond); + + worker.worker_creation_current_second = -1; + mono_coop_mutex_init (&worker.worker_creation_lock); + + worker.heuristic_adjustment_interval = 10; + mono_coop_mutex_init (&worker.heuristic_lock); + + mono_rand_open (); + + hc = &worker.heuristic_hill_climbing; + + hc->wave_period = HILL_CLIMBING_WAVE_PERIOD; + hc->max_thread_wave_magnitude = HILL_CLIMBING_MAX_WAVE_MAGNITUDE; + hc->thread_magnitude_multiplier = (gdouble) HILL_CLIMBING_WAVE_MAGNITUDE_MULTIPLIER; + hc->samples_to_measure = hc->wave_period * HILL_CLIMBING_WAVE_HISTORY_SIZE; + hc->target_throughput_ratio = (gdouble) HILL_CLIMBING_BIAS; + hc->target_signal_to_noise_ratio = (gdouble) HILL_CLIMBING_TARGET_SIGNAL_TO_NOISE_RATIO; + hc->max_change_per_second = (gdouble) HILL_CLIMBING_MAX_CHANGE_PER_SECOND; + hc->max_change_per_sample = (gdouble) HILL_CLIMBING_MAX_CHANGE_PER_SAMPLE; + hc->sample_interval_low = HILL_CLIMBING_SAMPLE_INTERVAL_LOW; + hc->sample_interval_high = HILL_CLIMBING_SAMPLE_INTERVAL_HIGH; + hc->throughput_error_smoothing_factor = (gdouble) HILL_CLIMBING_ERROR_SMOOTHING_FACTOR; + hc->gain_exponent = (gdouble) HILL_CLIMBING_GAIN_EXPONENT; + hc->max_sample_error = (gdouble) HILL_CLIMBING_MAX_SAMPLE_ERROR_PERCENT; + hc->current_control_setting = 0; + hc->total_samples = 0; + hc->last_thread_count = 0; + hc->average_throughput_noise = 0; + hc->elapsed_since_last_change = 0; + hc->accumulated_completion_count = 0; + hc->accumulated_sample_duration = 0; + hc->samples = g_new0 (gdouble, hc->samples_to_measure); + hc->thread_counts = g_new0 (gdouble, hc->samples_to_measure); + hc->random_interval_generator = rand_create (); + hc->current_sample_interval = rand_next (&hc->random_interval_generator, hc->sample_interval_low, hc->sample_interval_high); + + if (!(threads_per_cpu_env = g_getenv ("MONO_THREADS_PER_CPU"))) + threads_per_cpu = 1; + else + threads_per_cpu = CLAMP (atoi (threads_per_cpu_env), 1, 50); + + threads_count = mono_cpu_count () * threads_per_cpu; + + worker.limit_worker_min = threads_count; + +#if defined (HOST_ANDROID) || defined (HOST_IOS) + worker.limit_worker_max = CLAMP (threads_count * 100, MIN (threads_count, 200), MAX (threads_count, 200)); +#else + worker.limit_worker_max = threads_count * 100; +#endif + + worker.counters._.max_working = worker.limit_worker_min; + + worker.cpu_usage_state = g_new0 (MonoCpuUsageState, 1); + + worker.suspended = FALSE; + + worker.monitor_status = MONITOR_STATUS_NOT_RUNNING; +} + +void +mono_threadpool_worker_cleanup (void) +{ + mono_refcount_dec (&worker); +} + +static void +work_item_push (void) +{ + gint32 old, new; + + do { + old = mono_atomic_load_i32 (&worker.work_items_count); + g_assert (old >= 0); + + new = old + 1; + } while (mono_atomic_cas_i32 (&worker.work_items_count, new, old) != old); +} + +static gboolean +work_item_try_pop (void) +{ + gint32 old, new; + + do { + old = mono_atomic_load_i32 (&worker.work_items_count); + g_assert (old >= 0); + + if (old == 0) + return FALSE; + + new = old - 1; + } while (mono_atomic_cas_i32 (&worker.work_items_count, new, old) != old); + + return TRUE; +} + +static gint32 +work_item_count (void) +{ + return mono_atomic_load_i32 (&worker.work_items_count); +} + +static void worker_request (void); + +void +mono_threadpool_worker_request (void) +{ + if (!mono_refcount_tryinc (&worker)) + return; + + work_item_push (); + + worker_request (); + + mono_refcount_dec (&worker); +} + +static void +worker_wait_interrupt (gpointer unused) +{ + /* If the runtime is not shutting down, we are not using this mechanism to wake up a unparked thread, and if the + * runtime is shutting down, then we need to wake up ALL the threads. + * It might be a bit wasteful, but I witnessed shutdown hang where the main thread would abort and then wait for all + * background threads to exit (see mono_thread_manage). This would go wrong because not all threadpool threads would + * be unparked. It would end up getting unstucked because of the timeout, but that would delay shutdown by 5-60s. */ + if (!mono_runtime_is_shutting_down ()) + return; + + if (!mono_refcount_tryinc (&worker)) + return; + + mono_coop_mutex_lock (&worker.parked_threads_lock); + mono_coop_cond_broadcast (&worker.parked_threads_cond); + mono_coop_mutex_unlock (&worker.parked_threads_lock); + + mono_refcount_dec (&worker); +} + +/* return TRUE if timeout, FALSE otherwise (worker unpark or interrupt) */ +static gboolean +worker_park (void) +{ + gboolean timeout = FALSE; + gboolean interrupted = FALSE; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_THREADPOOL, "[%p] worker parking", + GUINT_TO_POINTER (MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ()))); + + mono_coop_mutex_lock (&worker.parked_threads_lock); + + if (!mono_runtime_is_shutting_down ()) { + static gpointer rand_handle = NULL; + MonoInternalThread *thread; + ThreadPoolWorkerCounter counter; + + if (!rand_handle) + rand_handle = rand_create (); + g_assert (rand_handle); + + thread = mono_thread_internal_current (); + g_assert (thread); + + COUNTER_ATOMIC (counter, { + counter._.working --; + counter._.parked ++; + }); + + worker.parked_threads_count += 1; + + mono_thread_info_install_interrupt (worker_wait_interrupt, NULL, &interrupted); + if (interrupted) + goto done; + + if (mono_coop_cond_timedwait (&worker.parked_threads_cond, &worker.parked_threads_lock, rand_next (&rand_handle, 5 * 1000, 60 * 1000)) != 0) + timeout = TRUE; + + mono_thread_info_uninstall_interrupt (&interrupted); + +done: + worker.parked_threads_count -= 1; + + COUNTER_ATOMIC (counter, { + counter._.working ++; + counter._.parked --; + }); + } + + mono_coop_mutex_unlock (&worker.parked_threads_lock); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_THREADPOOL, "[%p] worker unparking, timeout? %s interrupted? %s", + GUINT_TO_POINTER (MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ())), timeout ? "yes" : "no", interrupted ? "yes" : "no"); + + return timeout; +} + +static gboolean +worker_try_unpark (void) +{ + gboolean res = FALSE; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_THREADPOOL, "[%p] try unpark worker", + GUINT_TO_POINTER (MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ()))); + + mono_coop_mutex_lock (&worker.parked_threads_lock); + if (worker.parked_threads_count > 0) { + mono_coop_cond_signal (&worker.parked_threads_cond); + res = TRUE; + } + mono_coop_mutex_unlock (&worker.parked_threads_lock); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_THREADPOOL, "[%p] try unpark worker, success? %s", + GUINT_TO_POINTER (MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ())), res ? "yes" : "no"); + + return res; +} + +static gsize WINAPI +worker_thread (gpointer unused) +{ + MonoInternalThread *thread; + ThreadPoolWorkerCounter counter; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_THREADPOOL, "[%p] worker starting", + GUINT_TO_POINTER (MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ()))); + + if (!mono_refcount_tryinc (&worker)) + return 0; + + COUNTER_ATOMIC (counter, { + counter._.starting --; + counter._.working ++; + }); + + thread = mono_thread_internal_current (); + g_assert (thread); + + while (!mono_runtime_is_shutting_down ()) { + if (mono_thread_interruption_checkpoint ()) + continue; + + if (!work_item_try_pop ()) { + gboolean timeout; + + timeout = worker_park (); + if (timeout) + break; + + continue; + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_THREADPOOL, "[%p] worker executing", + GUINT_TO_POINTER (MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ()))); + + worker.callback (); + } + + COUNTER_ATOMIC (counter, { + counter._.working --; + }); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_THREADPOOL, "[%p] worker finishing", + GUINT_TO_POINTER (MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ()))); + + mono_refcount_dec (&worker); + + return 0; +} + +static gboolean +worker_try_create (void) +{ + MonoError error; + MonoInternalThread *thread; + gint64 current_ticks; + gint32 now; + ThreadPoolWorkerCounter counter; + + if (mono_runtime_is_shutting_down ()) + return FALSE; + + mono_coop_mutex_lock (&worker.worker_creation_lock); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_THREADPOOL, "[%p] try create worker", + GUINT_TO_POINTER (MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ()))); + + current_ticks = mono_100ns_ticks (); + if (0 == current_ticks) { + g_warning ("failed to get 100ns ticks"); + } else { + now = current_ticks / (10 * 1000 * 1000); + if (worker.worker_creation_current_second != now) { + worker.worker_creation_current_second = now; + worker.worker_creation_current_count = 0; + } else { + g_assert (worker.worker_creation_current_count <= WORKER_CREATION_MAX_PER_SEC); + if (worker.worker_creation_current_count == WORKER_CREATION_MAX_PER_SEC) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_THREADPOOL, "[%p] try create worker, failed: maximum number of worker created per second reached, current count = %d", + GUINT_TO_POINTER (MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ())), worker.worker_creation_current_count); + mono_coop_mutex_unlock (&worker.worker_creation_lock); + return FALSE; + } + } + } + + COUNTER_ATOMIC (counter, { + if (counter._.working >= counter._.max_working) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_THREADPOOL, "[%p] try create worker, failed: maximum number of working threads reached", + GUINT_TO_POINTER (MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ()))); + mono_coop_mutex_unlock (&worker.worker_creation_lock); + return FALSE; + } + counter._.starting ++; + }); + + thread = mono_thread_create_internal (mono_get_root_domain (), worker_thread, NULL, MONO_THREAD_CREATE_FLAGS_THREADPOOL, &error); + if (!thread) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_THREADPOOL, "[%p] try create worker, failed: could not create thread due to %s", + GUINT_TO_POINTER (MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ())), mono_error_get_message (&error)); + mono_error_cleanup (&error); + + COUNTER_ATOMIC (counter, { + counter._.starting --; + }); + + mono_coop_mutex_unlock (&worker.worker_creation_lock); + + return FALSE; + } + + worker.worker_creation_current_count += 1; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_THREADPOOL, "[%p] try create worker, created %p, now = %d count = %d", + GUINT_TO_POINTER (MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ())), (gpointer) thread->tid, now, worker.worker_creation_current_count); + + mono_coop_mutex_unlock (&worker.worker_creation_lock); + return TRUE; +} + +static void monitor_ensure_running (void); + +static void +worker_request (void) +{ + if (worker.suspended) + return; + + monitor_ensure_running (); + + if (worker_try_unpark ()) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_THREADPOOL, "[%p] request worker, unparked", + GUINT_TO_POINTER (MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ()))); + return; + } + + if (worker_try_create ()) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_THREADPOOL, "[%p] request worker, created", + GUINT_TO_POINTER (MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ()))); + return; + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_THREADPOOL, "[%p] request worker, failed", + GUINT_TO_POINTER (MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ()))); +} + +static gboolean +monitor_should_keep_running (void) +{ + static gint64 last_should_keep_running = -1; + + g_assert (worker.monitor_status == MONITOR_STATUS_WAITING_FOR_REQUEST || worker.monitor_status == MONITOR_STATUS_REQUESTED); + + if (mono_atomic_xchg_i32 (&worker.monitor_status, MONITOR_STATUS_WAITING_FOR_REQUEST) == MONITOR_STATUS_WAITING_FOR_REQUEST) { + gboolean should_keep_running = TRUE, force_should_keep_running = FALSE; + + if (mono_runtime_is_shutting_down ()) { + should_keep_running = FALSE; + } else { + if (work_item_count () == 0) + should_keep_running = FALSE; + + if (!should_keep_running) { + if (last_should_keep_running == -1 || mono_100ns_ticks () - last_should_keep_running < MONITOR_MINIMAL_LIFETIME * 1000 * 10) { + should_keep_running = force_should_keep_running = TRUE; + } + } + } + + if (should_keep_running) { + if (last_should_keep_running == -1 || !force_should_keep_running) + last_should_keep_running = mono_100ns_ticks (); + } else { + last_should_keep_running = -1; + if (mono_atomic_cas_i32 (&worker.monitor_status, MONITOR_STATUS_NOT_RUNNING, MONITOR_STATUS_WAITING_FOR_REQUEST) == MONITOR_STATUS_WAITING_FOR_REQUEST) + return FALSE; + } + } + + g_assert (worker.monitor_status == MONITOR_STATUS_WAITING_FOR_REQUEST || worker.monitor_status == MONITOR_STATUS_REQUESTED); + + return TRUE; +} + +static gboolean +monitor_sufficient_delay_since_last_dequeue (void) +{ + gint64 threshold; + + if (worker.cpu_usage < CPU_USAGE_LOW) { + threshold = MONITOR_INTERVAL; + } else { + ThreadPoolWorkerCounter counter; + counter = COUNTER_READ (); + threshold = counter._.max_working * MONITOR_INTERVAL * 2; + } + + return mono_msec_ticks () >= worker.heuristic_last_dequeue + threshold; +} + +static void hill_climbing_force_change (gint16 new_thread_count, ThreadPoolHeuristicStateTransition transition); + +static gsize WINAPI +monitor_thread (gpointer unused) +{ + MonoInternalThread *internal; + guint i; + + if (!mono_refcount_tryinc (&worker)) + return 0; + + internal = mono_thread_internal_current (); + g_assert (internal); + + mono_cpu_usage (worker.cpu_usage_state); + + // printf ("monitor_thread: start\n"); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_THREADPOOL, "[%p] monitor thread, started", + GUINT_TO_POINTER (MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ()))); + + do { + ThreadPoolWorkerCounter counter; + gboolean limit_worker_max_reached; + gint32 interval_left = MONITOR_INTERVAL; + gint32 awake = 0; /* number of spurious awakes we tolerate before doing a round of rebalancing */ + + g_assert (worker.monitor_status != MONITOR_STATUS_NOT_RUNNING); + + // counter = COUNTER_READ (); + // printf ("monitor_thread: starting = %d working = %d parked = %d max_working = %d\n", + // counter._.starting, counter._.working, counter._.parked, counter._.max_working); + + do { + gint64 ts; + gboolean alerted = FALSE; + + if (mono_runtime_is_shutting_down ()) + break; + + ts = mono_msec_ticks (); + if (mono_thread_info_sleep (interval_left, &alerted) == 0) + break; + interval_left -= mono_msec_ticks () - ts; + + mono_thread_interruption_checkpoint (); + } while (interval_left > 0 && ++awake < 10); + + if (mono_runtime_is_shutting_down ()) + continue; + + if (worker.suspended) + continue; + + if (work_item_count () == 0) + continue; + + worker.cpu_usage = mono_cpu_usage (worker.cpu_usage_state); + + if (!monitor_sufficient_delay_since_last_dequeue ()) + continue; + + limit_worker_max_reached = FALSE; + + COUNTER_ATOMIC (counter, { + if (counter._.max_working >= worker.limit_worker_max) { + limit_worker_max_reached = TRUE; + break; + } + counter._.max_working ++; + }); + + if (limit_worker_max_reached) + continue; + + hill_climbing_force_change (counter._.max_working, TRANSITION_STARVATION); + + for (i = 0; i < 5; ++i) { + if (mono_runtime_is_shutting_down ()) + break; + + if (worker_try_unpark ()) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_THREADPOOL, "[%p] monitor thread, unparked", + GUINT_TO_POINTER (MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ()))); + break; + } + + if (worker_try_create ()) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_THREADPOOL, "[%p] monitor thread, created", + GUINT_TO_POINTER (MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ()))); + break; + } + } + } while (monitor_should_keep_running ()); + + // printf ("monitor_thread: stop\n"); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_THREADPOOL, "[%p] monitor thread, finished", + GUINT_TO_POINTER (MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ()))); + + mono_refcount_dec (&worker); + return 0; +} + +static void +monitor_ensure_running (void) +{ + MonoError error; + for (;;) { + switch (worker.monitor_status) { + case MONITOR_STATUS_REQUESTED: + // printf ("monitor_thread: requested\n"); + return; + case MONITOR_STATUS_WAITING_FOR_REQUEST: + // printf ("monitor_thread: waiting for request\n"); + mono_atomic_cas_i32 (&worker.monitor_status, MONITOR_STATUS_REQUESTED, MONITOR_STATUS_WAITING_FOR_REQUEST); + break; + case MONITOR_STATUS_NOT_RUNNING: + // printf ("monitor_thread: not running\n"); + if (mono_runtime_is_shutting_down ()) + return; + if (mono_atomic_cas_i32 (&worker.monitor_status, MONITOR_STATUS_REQUESTED, MONITOR_STATUS_NOT_RUNNING) == MONITOR_STATUS_NOT_RUNNING) { + // printf ("monitor_thread: creating\n"); + if (!mono_thread_create_internal (mono_get_root_domain (), monitor_thread, NULL, MONO_THREAD_CREATE_FLAGS_THREADPOOL | MONO_THREAD_CREATE_FLAGS_SMALL_STACK, &error)) { + // printf ("monitor_thread: creating failed\n"); + worker.monitor_status = MONITOR_STATUS_NOT_RUNNING; + mono_error_cleanup (&error); + mono_refcount_dec (&worker); + } + return; + } + break; + default: g_assert_not_reached (); + } + } +} + +static void +hill_climbing_change_thread_count (gint16 new_thread_count, ThreadPoolHeuristicStateTransition transition) +{ + ThreadPoolHillClimbing *hc; + + hc = &worker.heuristic_hill_climbing; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_THREADPOOL, "[%p] hill climbing, change max number of threads %d", + GUINT_TO_POINTER (MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ())), new_thread_count); + + hc->last_thread_count = new_thread_count; + hc->current_sample_interval = rand_next (&hc->random_interval_generator, hc->sample_interval_low, hc->sample_interval_high); + hc->elapsed_since_last_change = 0; + hc->completions_since_last_change = 0; +} + +static void +hill_climbing_force_change (gint16 new_thread_count, ThreadPoolHeuristicStateTransition transition) +{ + ThreadPoolHillClimbing *hc; + + hc = &worker.heuristic_hill_climbing; + + if (new_thread_count != hc->last_thread_count) { + hc->current_control_setting += new_thread_count - hc->last_thread_count; + hill_climbing_change_thread_count (new_thread_count, transition); + } +} + +static double_complex +hill_climbing_get_wave_component (gdouble *samples, guint sample_count, gdouble period) +{ + ThreadPoolHillClimbing *hc; + gdouble w, cosine, sine, coeff, q0, q1, q2; + guint i; + + g_assert (sample_count >= period); + g_assert (period >= 2); + + hc = &worker.heuristic_hill_climbing; + + w = 2.0 * M_PI / period; + cosine = cos (w); + sine = sin (w); + coeff = 2.0 * cosine; + q0 = q1 = q2 = 0; + + for (i = 0; i < sample_count; ++i) { + q0 = coeff * q1 - q2 + samples [(hc->total_samples - sample_count + i) % hc->samples_to_measure]; + q2 = q1; + q1 = q0; + } + + return mono_double_complex_scalar_div (mono_double_complex_make (q1 - q2 * cosine, (q2 * sine)), ((gdouble)sample_count)); +} + +static gint16 +hill_climbing_update (gint16 current_thread_count, guint32 sample_duration, gint32 completions, gint64 *adjustment_interval) +{ + ThreadPoolHillClimbing *hc; + ThreadPoolHeuristicStateTransition transition; + gdouble throughput; + gdouble throughput_error_estimate; + gdouble confidence; + gdouble move; + gdouble gain; + gint sample_index; + gint sample_count; + gint new_thread_wave_magnitude; + gint new_thread_count; + double_complex thread_wave_component; + double_complex throughput_wave_component; + double_complex ratio; + + g_assert (adjustment_interval); + + hc = &worker.heuristic_hill_climbing; + + /* If someone changed the thread count without telling us, update our records accordingly. */ + if (current_thread_count != hc->last_thread_count) + hill_climbing_force_change (current_thread_count, TRANSITION_INITIALIZING); + + /* Update the cumulative stats for this thread count */ + hc->elapsed_since_last_change += sample_duration; + hc->completions_since_last_change += completions; + + /* Add in any data we've already collected about this sample */ + sample_duration += hc->accumulated_sample_duration; + completions += hc->accumulated_completion_count; + + /* We need to make sure we're collecting reasonably accurate data. Since we're just counting the end + * of each work item, we are goinng to be missing some data about what really happened during the + * sample interval. The count produced by each thread includes an initial work item that may have + * started well before the start of the interval, and each thread may have been running some new + * work item for some time before the end of the interval, which did not yet get counted. So + * our count is going to be off by +/- threadCount workitems. + * + * The exception is that the thread that reported to us last time definitely wasn't running any work + * at that time, and the thread that's reporting now definitely isn't running a work item now. So + * we really only need to consider threadCount-1 threads. + * + * Thus the percent error in our count is +/- (threadCount-1)/numCompletions. + * + * We cannot rely on the frequency-domain analysis we'll be doing later to filter out this error, because + * of the way it accumulates over time. If this sample is off by, say, 33% in the negative direction, + * then the next one likely will be too. The one after that will include the sum of the completions + * we missed in the previous samples, and so will be 33% positive. So every three samples we'll have + * two "low" samples and one "high" sample. This will appear as periodic variation right in the frequency + * range we're targeting, which will not be filtered by the frequency-domain translation. */ + if (hc->total_samples > 0 && ((current_thread_count - 1.0) / completions) >= hc->max_sample_error) { + /* Not accurate enough yet. Let's accumulate the data so + * far, and tell the ThreadPoolWorker to collect a little more. */ + hc->accumulated_sample_duration = sample_duration; + hc->accumulated_completion_count = completions; + *adjustment_interval = 10; + return current_thread_count; + } + + /* We've got enouugh data for our sample; reset our accumulators for next time. */ + hc->accumulated_sample_duration = 0; + hc->accumulated_completion_count = 0; + + /* Add the current thread count and throughput sample to our history. */ + throughput = ((gdouble) completions) / sample_duration; + + sample_index = hc->total_samples % hc->samples_to_measure; + hc->samples [sample_index] = throughput; + hc->thread_counts [sample_index] = current_thread_count; + hc->total_samples ++; + + /* Set up defaults for our metrics. */ + thread_wave_component = mono_double_complex_make(0, 0); + throughput_wave_component = mono_double_complex_make(0, 0); + throughput_error_estimate = 0; + ratio = mono_double_complex_make(0, 0); + confidence = 0; + + transition = TRANSITION_WARMUP; + + /* How many samples will we use? It must be at least the three wave periods we're looking for, and it must also + * be a whole multiple of the primary wave's period; otherwise the frequency we're looking for will fall between + * two frequency bands in the Fourier analysis, and we won't be able to measure it accurately. */ + sample_count = ((gint) MIN (hc->total_samples - 1, hc->samples_to_measure) / hc->wave_period) * hc->wave_period; + + if (sample_count > hc->wave_period) { + guint i; + gdouble average_throughput; + gdouble average_thread_count; + gdouble sample_sum = 0; + gdouble thread_sum = 0; + + /* Average the throughput and thread count samples, so we can scale the wave magnitudes later. */ + for (i = 0; i < sample_count; ++i) { + guint j = (hc->total_samples - sample_count + i) % hc->samples_to_measure; + sample_sum += hc->samples [j]; + thread_sum += hc->thread_counts [j]; + } + + average_throughput = sample_sum / sample_count; + average_thread_count = thread_sum / sample_count; + + if (average_throughput > 0 && average_thread_count > 0) { + gdouble noise_for_confidence, adjacent_period_1, adjacent_period_2; + + /* Calculate the periods of the adjacent frequency bands we'll be using to + * measure noise levels. We want the two adjacent Fourier frequency bands. */ + adjacent_period_1 = sample_count / (((gdouble) sample_count) / ((gdouble) hc->wave_period) + 1); + adjacent_period_2 = sample_count / (((gdouble) sample_count) / ((gdouble) hc->wave_period) - 1); + + /* Get the the three different frequency components of the throughput (scaled by average + * throughput). Our "error" estimate (the amount of noise that might be present in the + * frequency band we're really interested in) is the average of the adjacent bands. */ + throughput_wave_component = mono_double_complex_scalar_div (hill_climbing_get_wave_component (hc->samples, sample_count, hc->wave_period), average_throughput); + throughput_error_estimate = cabs (mono_double_complex_scalar_div (hill_climbing_get_wave_component (hc->samples, sample_count, adjacent_period_1), average_throughput)); + + if (adjacent_period_2 <= sample_count) { + throughput_error_estimate = MAX (throughput_error_estimate, cabs (mono_double_complex_scalar_div (hill_climbing_get_wave_component ( + hc->samples, sample_count, adjacent_period_2), average_throughput))); + } + + /* Do the same for the thread counts, so we have something to compare to. We don't + * measure thread count noise, because there is none; these are exact measurements. */ + thread_wave_component = mono_double_complex_scalar_div (hill_climbing_get_wave_component (hc->thread_counts, sample_count, hc->wave_period), average_thread_count); + + /* Update our moving average of the throughput noise. We'll use this + * later as feedback to determine the new size of the thread wave. */ + if (hc->average_throughput_noise == 0) { + hc->average_throughput_noise = throughput_error_estimate; + } else { + hc->average_throughput_noise = (hc->throughput_error_smoothing_factor * throughput_error_estimate) + + ((1.0 + hc->throughput_error_smoothing_factor) * hc->average_throughput_noise); + } + + if (cabs (thread_wave_component) > 0) { + /* Adjust the throughput wave so it's centered around the target wave, + * and then calculate the adjusted throughput/thread ratio. */ + ratio = mono_double_complex_div (mono_double_complex_sub (throughput_wave_component, mono_double_complex_scalar_mul(thread_wave_component, hc->target_throughput_ratio)), thread_wave_component); + transition = TRANSITION_CLIMBING_MOVE; + } else { + ratio = mono_double_complex_make (0, 0); + transition = TRANSITION_STABILIZING; + } + + noise_for_confidence = MAX (hc->average_throughput_noise, throughput_error_estimate); + if (noise_for_confidence > 0) { + confidence = cabs (thread_wave_component) / noise_for_confidence / hc->target_signal_to_noise_ratio; + } else { + /* there is no noise! */ + confidence = 1.0; + } + } + } + + /* We use just the real part of the complex ratio we just calculated. If the throughput signal + * is exactly in phase with the thread signal, this will be the same as taking the magnitude of + * the complex move and moving that far up. If they're 180 degrees out of phase, we'll move + * backward (because this indicates that our changes are having the opposite of the intended effect). + * If they're 90 degrees out of phase, we won't move at all, because we can't tell wether we're + * having a negative or positive effect on throughput. */ + move = creal (ratio); + move = CLAMP (move, -1.0, 1.0); + + /* Apply our confidence multiplier. */ + move *= CLAMP (confidence, -1.0, 1.0); + + /* Now apply non-linear gain, such that values around zero are attenuated, while higher values + * are enhanced. This allows us to move quickly if we're far away from the target, but more slowly + * if we're getting close, giving us rapid ramp-up without wild oscillations around the target. */ + gain = hc->max_change_per_second * sample_duration; + move = pow (fabs (move), hc->gain_exponent) * (move >= 0.0 ? 1 : -1) * gain; + move = MIN (move, hc->max_change_per_sample); + + /* If the result was positive, and CPU is > 95%, refuse the move. */ + if (move > 0.0 && worker.cpu_usage > CPU_USAGE_HIGH) + move = 0.0; + + /* Apply the move to our control setting. */ + hc->current_control_setting += move; + + /* Calculate the new thread wave magnitude, which is based on the moving average we've been keeping of the + * throughput error. This average starts at zero, so we'll start with a nice safe little wave at first. */ + new_thread_wave_magnitude = (gint)(0.5 + (hc->current_control_setting * hc->average_throughput_noise + * hc->target_signal_to_noise_ratio * hc->thread_magnitude_multiplier * 2.0)); + new_thread_wave_magnitude = CLAMP (new_thread_wave_magnitude, 1, hc->max_thread_wave_magnitude); + + /* Make sure our control setting is within the ThreadPoolWorker's limits. */ + hc->current_control_setting = CLAMP (hc->current_control_setting, worker.limit_worker_min, worker.limit_worker_max - new_thread_wave_magnitude); + + /* Calculate the new thread count (control setting + square wave). */ + new_thread_count = (gint)(hc->current_control_setting + new_thread_wave_magnitude * ((hc->total_samples / (hc->wave_period / 2)) % 2)); + + /* Make sure the new thread count doesn't exceed the ThreadPoolWorker's limits. */ + new_thread_count = CLAMP (new_thread_count, worker.limit_worker_min, worker.limit_worker_max); + + if (new_thread_count != current_thread_count) + hill_climbing_change_thread_count (new_thread_count, transition); + + if (creal (ratio) < 0.0 && new_thread_count == worker.limit_worker_min) + *adjustment_interval = (gint)(0.5 + hc->current_sample_interval * (10.0 * MAX (-1.0 * creal (ratio), 1.0))); + else + *adjustment_interval = hc->current_sample_interval; + + return new_thread_count; +} + +static gboolean +heuristic_should_adjust (void) +{ + if (worker.heuristic_last_dequeue > worker.heuristic_last_adjustment + worker.heuristic_adjustment_interval) { + ThreadPoolWorkerCounter counter; + counter = COUNTER_READ (); + if (counter._.working <= counter._.max_working) + return TRUE; + } + + return FALSE; +} + +static void +heuristic_adjust (void) +{ + if (mono_coop_mutex_trylock (&worker.heuristic_lock) == 0) { + gint32 completions = mono_atomic_xchg_i32 (&worker.heuristic_completions, 0); + gint64 sample_end = mono_msec_ticks (); + gint64 sample_duration = sample_end - worker.heuristic_sample_start; + + if (sample_duration >= worker.heuristic_adjustment_interval / 2) { + ThreadPoolWorkerCounter counter; + gint16 new_thread_count; + + counter = COUNTER_READ (); + new_thread_count = hill_climbing_update (counter._.max_working, sample_duration, completions, &worker.heuristic_adjustment_interval); + + COUNTER_ATOMIC (counter, { + counter._.max_working = new_thread_count; + }); + + if (new_thread_count > counter._.max_working) + worker_request (); + + worker.heuristic_sample_start = sample_end; + worker.heuristic_last_adjustment = mono_msec_ticks (); + } + + mono_coop_mutex_unlock (&worker.heuristic_lock); + } +} + +static void +heuristic_notify_work_completed (void) +{ + mono_atomic_inc_i32 (&worker.heuristic_completions); + worker.heuristic_last_dequeue = mono_msec_ticks (); + + if (heuristic_should_adjust ()) + heuristic_adjust (); +} + +gboolean +mono_threadpool_worker_notify_completed (void) +{ + ThreadPoolWorkerCounter counter; + + heuristic_notify_work_completed (); + + counter = COUNTER_READ (); + return counter._.working <= counter._.max_working; +} + +gint32 +mono_threadpool_worker_get_min (void) +{ + gint32 ret; + + if (!mono_refcount_tryinc (&worker)) + return 0; + + ret = worker.limit_worker_min; + + mono_refcount_dec (&worker); + return ret; +} + +gboolean +mono_threadpool_worker_set_min (gint32 value) +{ + if (value <= 0 || value > worker.limit_worker_max) + return FALSE; + + if (!mono_refcount_tryinc (&worker)) + return FALSE; + + worker.limit_worker_min = value; + + mono_refcount_dec (&worker); + return TRUE; +} + +gint32 +mono_threadpool_worker_get_max (void) +{ + gint32 ret; + + if (!mono_refcount_tryinc (&worker)) + return 0; + + ret = worker.limit_worker_max; + + mono_refcount_dec (&worker); + return ret; +} + +gboolean +mono_threadpool_worker_set_max (gint32 value) +{ + gint32 cpu_count; + + cpu_count = mono_cpu_count (); + if (value < worker.limit_worker_min || value < cpu_count) + return FALSE; + + if (!mono_refcount_tryinc (&worker)) + return FALSE; + + worker.limit_worker_max = value; + + mono_refcount_dec (&worker); + return TRUE; +} + +void +mono_threadpool_worker_set_suspended (gboolean suspended) +{ + if (!mono_refcount_tryinc (&worker)) + return; + + worker.suspended = suspended; + if (!suspended) + worker_request (); + + mono_refcount_dec (&worker); +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/threadpool-worker-wasm.c b/unity-2019.4.24f1-mbe/mono/metadata/threadpool-worker-wasm.c new file mode 100644 index 000000000..8877df960 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/threadpool-worker-wasm.c @@ -0,0 +1,77 @@ +/** + * \file + * native threadpool worker + * + * Author: + * Ludovic Henry (ludovic.henry@xamarin.com) + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#define _USE_MATH_DEFINES // needed by MSVC to define math constants +#include +#include +#include + +#include +#include + +static MonoThreadPoolWorkerCallback tp_cb; +static gboolean cb_scheduled; + +void +mono_threadpool_worker_init (MonoThreadPoolWorkerCallback callback) +{ + tp_cb = callback; +} + +void +mono_threadpool_worker_cleanup (void) +{ +} + +gint32 +mono_threadpool_worker_get_min (void) +{ + return 1; +} + +gboolean +mono_threadpool_worker_set_min (gint32 value) +{ + return value == 1; +} + +gint32 +mono_threadpool_worker_get_max (void) +{ + return 1; +} + +gboolean +mono_threadpool_worker_set_max (gint32 value) +{ + return value == 1; +} + +static void +fire_tp_callback (void) +{ + cb_scheduled = FALSE; + tp_cb (); +} + +void +mono_threadpool_worker_request (void) +{ + if (!cb_scheduled) + mono_threads_schedule_background_job (fire_tp_callback); + cb_scheduled = TRUE; +} + +gboolean +mono_threadpool_worker_notify_completed (void) +{ + return FALSE; +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/threadpool-worker.h b/unity-2019.4.24f1-mbe/mono/metadata/threadpool-worker.h new file mode 100644 index 000000000..9b2c51d7f --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/threadpool-worker.h @@ -0,0 +1,37 @@ +/** + * \file + */ + +#ifndef _MONO_METADATA_THREADPOOL_WORKER_H +#define _MONO_METADATA_THREADPOOL_WORKER_H + +#include + +typedef void (*MonoThreadPoolWorkerCallback)(void); + +void +mono_threadpool_worker_init (MonoThreadPoolWorkerCallback callback); + +void +mono_threadpool_worker_cleanup (void); + +void +mono_threadpool_worker_request (void); + +gboolean +mono_threadpool_worker_notify_completed (void); + +gint32 +mono_threadpool_worker_get_min (void); +gboolean +mono_threadpool_worker_set_min (gint32 value); + +gint32 +mono_threadpool_worker_get_max (void); +gboolean +mono_threadpool_worker_set_max (gint32 value); + +void +mono_threadpool_worker_set_suspended (gboolean suspended); + +#endif /* _MONO_METADATA_THREADPOOL_WORKER_H */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/threadpool.c b/unity-2019.4.24f1-mbe/mono/metadata/threadpool.c new file mode 100644 index 000000000..ebc915b84 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/threadpool.c @@ -0,0 +1,825 @@ +/** + * \file + * Microsoft threadpool runtime support + * + * Author: + * Ludovic Henry (ludovic.henry@xamarin.com) + * + * Copyright 2015 Xamarin, Inc (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// Files: +// - src/vm/comthreadpool.cpp +// - src/vm/win32threadpoolcpp +// - src/vm/threadpoolrequest.cpp +// - src/vm/hillclimbing.cpp +// +// Ported from C++ to C and adjusted to Mono runtime + +#include +#define _USE_MATH_DEFINES // needed by MSVC to define math constants +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// consistency with coreclr https://github.com/dotnet/coreclr/blob/643b09f966e68e06d5f0930755985a01a2a2b096/src/vm/win32threadpool.h#L111 +#define MAX_POSSIBLE_THREADS 0x7fff + +typedef struct { + MonoDomain *domain; + /* Number of outstanding jobs */ + gint32 outstanding_request; + /* Number of currently executing jobs */ + gint32 threadpool_jobs; + /* Signalled when threadpool_jobs + outstanding_request is 0 */ + /* Protected by threadpool.domains_lock */ + MonoCoopCond cleanup_cond; +} ThreadPoolDomain; + +typedef union { + struct { + gint16 starting; /* starting, but not yet in worker_callback */ + gint16 working; /* executing worker_callback */ + } _; + gint32 as_gint32; +} ThreadPoolCounter; + +typedef struct { + MonoRefCount ref; + + GPtrArray *domains; // ThreadPoolDomain* [] + MonoCoopMutex domains_lock; + + ThreadPoolCounter counters; + + gint32 limit_io_min; + gint32 limit_io_max; +} ThreadPool; + +static mono_lazy_init_t status = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED; + +static ThreadPool threadpool; + +#define COUNTER_ATOMIC(var,block) \ + do { \ + ThreadPoolCounter __old; \ + do { \ + (var) = __old = COUNTER_READ (); \ + { block; } \ + if (!(counter._.starting >= 0)) \ + g_error ("%s: counter._.starting = %d, but should be >= 0", __func__, counter._.starting); \ + if (!(counter._.working >= 0)) \ + g_error ("%s: counter._.working = %d, but should be >= 0", __func__, counter._.working); \ + } while (mono_atomic_cas_i32 (&threadpool.counters.as_gint32, (var).as_gint32, __old.as_gint32) != __old.as_gint32); \ + } while (0) + +static inline ThreadPoolCounter +COUNTER_READ (void) +{ + ThreadPoolCounter counter; + counter.as_gint32 = mono_atomic_load_i32 (&threadpool.counters.as_gint32); + return counter; +} + +static inline void +domains_lock (void) +{ + mono_coop_mutex_lock (&threadpool.domains_lock); +} + +static inline void +domains_unlock (void) +{ + mono_coop_mutex_unlock (&threadpool.domains_lock); +} + +static void +destroy (gpointer unused) +{ + g_ptr_array_free (threadpool.domains, TRUE); + mono_coop_mutex_destroy (&threadpool.domains_lock); +} + +static void +worker_callback (void); + +static void +initialize (void) +{ + g_assert (sizeof (ThreadPoolCounter) == sizeof (gint32)); + + mono_refcount_init (&threadpool, destroy); + + threadpool.domains = g_ptr_array_new (); + mono_coop_mutex_init (&threadpool.domains_lock); + + threadpool.limit_io_min = mono_cpu_count (); + threadpool.limit_io_max = CLAMP (threadpool.limit_io_min * 100, MIN (threadpool.limit_io_min, 200), MAX (threadpool.limit_io_min, 200)); + + mono_threadpool_worker_init (worker_callback); +} + +static void +cleanup (void) +{ + mono_threadpool_worker_cleanup (); + + mono_refcount_dec (&threadpool); +} + +gboolean +mono_threadpool_enqueue_work_item (MonoDomain *domain, MonoObject *work_item, MonoError *error) +{ + static MonoClass *threadpool_class = NULL; + static MonoMethod *unsafe_queue_custom_work_item_method = NULL; + MonoDomain *current_domain; + MonoBoolean f; + gpointer args [2]; + + error_init (error); + g_assert (work_item); + + if (!threadpool_class) + threadpool_class = mono_class_load_from_name (mono_defaults.corlib, "System.Threading", "ThreadPool"); + + if (!unsafe_queue_custom_work_item_method) + unsafe_queue_custom_work_item_method = mono_class_get_method_from_name (threadpool_class, "UnsafeQueueCustomWorkItem", 2); + g_assert (unsafe_queue_custom_work_item_method); + + f = FALSE; + + args [0] = (gpointer) work_item; + args [1] = (gpointer) &f; + + current_domain = mono_domain_get (); + if (current_domain == domain) { + mono_runtime_invoke_checked (unsafe_queue_custom_work_item_method, NULL, args, error); + return_val_if_nok (error, FALSE); + } else { + mono_thread_push_appdomain_ref (domain); + if (mono_domain_set (domain, FALSE)) { + mono_runtime_invoke_checked (unsafe_queue_custom_work_item_method, NULL, args, error); + if (!is_ok (error)) { + mono_thread_pop_appdomain_ref (); + return FALSE; + } + mono_domain_set (current_domain, TRUE); + } + mono_thread_pop_appdomain_ref (); + } + return TRUE; +} + +/* LOCKING: domains_lock must be held. */ +static ThreadPoolDomain * +tpdomain_create (MonoDomain *domain) +{ + ThreadPoolDomain *tpdomain; + + tpdomain = g_new0 (ThreadPoolDomain, 1); + tpdomain->domain = domain; + mono_coop_cond_init (&tpdomain->cleanup_cond); + + g_ptr_array_add (threadpool.domains, tpdomain); + + return tpdomain; +} + +/* LOCKING: domains_lock must be held. */ +static gboolean +tpdomain_remove (ThreadPoolDomain *tpdomain) +{ + g_assert (tpdomain); + return g_ptr_array_remove (threadpool.domains, tpdomain); +} + +/* LOCKING: domains_lock must be held */ +static ThreadPoolDomain * +tpdomain_get (MonoDomain *domain) +{ + gint i; + + g_assert (domain); + + for (i = 0; i < threadpool.domains->len; ++i) { + ThreadPoolDomain *tpdomain; + + tpdomain = (ThreadPoolDomain *)g_ptr_array_index (threadpool.domains, i); + if (tpdomain->domain == domain) + return tpdomain; + } + + return NULL; +} + +static void +tpdomain_free (ThreadPoolDomain *tpdomain) +{ + g_free (tpdomain); +} + +/* LOCKING: domains_lock must be held */ +static ThreadPoolDomain * +tpdomain_get_next (ThreadPoolDomain *current) +{ + ThreadPoolDomain *tpdomain = NULL; + gint len; + + len = threadpool.domains->len; + if (len > 0) { + gint i, current_idx = -1; + if (current) { + for (i = 0; i < len; ++i) { + if (current == g_ptr_array_index (threadpool.domains, i)) { + current_idx = i; + break; + } + } + } + for (i = current_idx + 1; i < len + current_idx + 1; ++i) { + ThreadPoolDomain *tmp = (ThreadPoolDomain *)g_ptr_array_index (threadpool.domains, i % len); + if (tmp->outstanding_request > 0) { + tpdomain = tmp; + break; + } + } + } + + return tpdomain; +} + +static MonoObject* +try_invoke_perform_wait_callback (MonoObject** exc, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + error_init (error); + MonoObject *res = mono_runtime_try_invoke (mono_defaults.threadpool_perform_wait_callback_method, NULL, NULL, exc, error); + HANDLE_FUNCTION_RETURN_VAL (res); +} + +static void +worker_callback (void) +{ + MonoError error; + ThreadPoolDomain *tpdomain, *previous_tpdomain; + ThreadPoolCounter counter; + MonoInternalThread *thread; + + if (!mono_refcount_tryinc (&threadpool)) + return; + + thread = mono_thread_internal_current (); + + COUNTER_ATOMIC (counter, { + if (!(counter._.working < 32767 /* G_MAXINT16 */)) + g_error ("%s: counter._.working = %d, but should be < 32767", __func__, counter._.working); + + counter._.starting --; + counter._.working ++; + }); + + if (mono_runtime_is_shutting_down ()) { + COUNTER_ATOMIC (counter, { + counter._.working --; + }); + + mono_refcount_dec (&threadpool); + return; + } + + /* + * This is needed so there is always an lmf frame in the runtime invoke call below, + * so ThreadAbortExceptions are caught even if the thread is in native code. + */ + mono_defaults.threadpool_perform_wait_callback_method->save_lmf = TRUE; + + domains_lock (); + + previous_tpdomain = NULL; + + while (!mono_runtime_is_shutting_down ()) { + gboolean retire = FALSE; + + if (thread->state & (ThreadState_AbortRequested | ThreadState_SuspendRequested)) { + domains_unlock (); + if (mono_thread_interruption_checkpoint ()) { + domains_lock (); + continue; + } + domains_lock (); + } + + tpdomain = tpdomain_get_next (previous_tpdomain); + if (!tpdomain) + break; + + tpdomain->outstanding_request --; + g_assert (tpdomain->outstanding_request >= 0); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_THREADPOOL, "[%p] worker running in domain %p (outstanding requests %d)", + GUINT_TO_POINTER (MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ())), tpdomain->domain, tpdomain->outstanding_request); + + g_assert (tpdomain->threadpool_jobs >= 0); + tpdomain->threadpool_jobs ++; + + domains_unlock (); + + MonoString *thread_name = mono_string_new_checked (mono_get_root_domain (), "Thread Pool Worker", &error); + mono_error_assert_ok (&error); + mono_thread_set_name_internal (thread, thread_name, FALSE, TRUE, &error); + mono_error_assert_ok (&error); + + mono_thread_clr_state (thread, (MonoThreadState)~ThreadState_Background); + if (!mono_thread_test_state (thread , ThreadState_Background)) + ves_icall_System_Threading_Thread_SetState (thread, ThreadState_Background); + + mono_thread_push_appdomain_ref (tpdomain->domain); + if (mono_domain_set (tpdomain->domain, FALSE)) { + MonoObject *exc = NULL, *res; + + res = try_invoke_perform_wait_callback (&exc, &error); + if (exc || !mono_error_ok(&error)) { + if (exc == NULL) + exc = (MonoObject *) mono_error_convert_to_exception (&error); + else + mono_error_cleanup (&error); + mono_thread_internal_unhandled_exception (exc); + } else if (res && *(MonoBoolean*) mono_object_unbox (res) == FALSE) { + retire = TRUE; + } + + mono_domain_set (mono_get_root_domain (), TRUE); + } + mono_thread_pop_appdomain_ref (); + + domains_lock (); + + tpdomain->threadpool_jobs --; + g_assert (tpdomain->threadpool_jobs >= 0); + + if (tpdomain->outstanding_request + tpdomain->threadpool_jobs == 0 && mono_domain_is_unloading (tpdomain->domain)) { + gboolean removed; + + removed = tpdomain_remove (tpdomain); + g_assert (removed); + + mono_coop_cond_signal (&tpdomain->cleanup_cond); + tpdomain = NULL; + } + + if (retire) + break; + + previous_tpdomain = tpdomain; + } + + domains_unlock (); + + COUNTER_ATOMIC (counter, { + counter._.working --; + }); + + mono_refcount_dec (&threadpool); +} + +void +mono_threadpool_cleanup (void) +{ +#ifndef DISABLE_SOCKETS + mono_threadpool_io_cleanup (); +#endif + mono_lazy_cleanup (&status, cleanup); +} + +MonoAsyncResult * +mono_threadpool_begin_invoke (MonoDomain *domain, MonoObject *target, MonoMethod *method, gpointer *params, MonoError *error) +{ + static MonoClass *async_call_klass = NULL; + MonoMethodMessage *message; + MonoAsyncResult *async_result; + MonoAsyncCall *async_call; + MonoDelegate *async_callback = NULL; + MonoObject *state = NULL; + + if (!async_call_klass) + async_call_klass = mono_class_load_from_name (mono_defaults.corlib, "System", "MonoAsyncCall"); + + error_init (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); + return_val_if_nok (error, NULL); + + MONO_OBJECT_SETREF (async_call, msg, message); + MONO_OBJECT_SETREF (async_call, state, state); + + if (async_callback) { + MONO_OBJECT_SETREF (async_call, cb_method, mono_get_delegate_invoke (((MonoObject*) async_callback)->vtable->klass)); + MONO_OBJECT_SETREF (async_call, cb_target, async_callback); + } + + async_result = mono_async_result_new (domain, NULL, async_call->state, NULL, (MonoObject*) async_call, error); + return_val_if_nok (error, NULL); + MONO_OBJECT_SETREF (async_result, async_delegate, target); + + mono_threadpool_enqueue_work_item (domain, (MonoObject*) async_result, error); + return_val_if_nok (error, NULL); + + return async_result; +} + +MonoObject * +mono_threadpool_end_invoke (MonoAsyncResult *ares, MonoArray **out_args, MonoObject **exc, MonoError *error) +{ + MonoAsyncCall *ac; + + error_init (error); + g_assert (exc); + g_assert (out_args); + + *exc = NULL; + *out_args = NULL; + + /* check if already finished */ + mono_monitor_enter ((MonoObject*) ares); + + if (ares->endinvoke_called) { + mono_error_set_invalid_operation(error, "Delegate EndInvoke method called more than once"); + mono_monitor_exit ((MonoObject*) ares); + return NULL; + } + + ares->endinvoke_called = 1; + + /* wait until we are really finished */ + if (ares->completed) { + mono_monitor_exit ((MonoObject *) ares); + } else { + gpointer wait_event; + if (ares->handle) { + wait_event = mono_wait_handle_get_handle ((MonoWaitHandle*) ares->handle); + } else { + wait_event = mono_w32event_create (TRUE, FALSE); + g_assert(wait_event); + MonoWaitHandle *wait_handle = mono_wait_handle_new (mono_object_domain (ares), wait_event, error); + if (!is_ok (error)) { + mono_w32event_close (wait_event); + return NULL; + } + MONO_OBJECT_SETREF (ares, handle, (MonoObject*) wait_handle); + } + mono_monitor_exit ((MonoObject*) ares); +#ifdef HOST_WIN32 + MONO_ENTER_GC_SAFE; + mono_win32_wait_for_single_object_ex (wait_event, INFINITE, TRUE); + MONO_EXIT_GC_SAFE; +#else + mono_w32handle_wait_one (wait_event, MONO_INFINITE_WAIT, TRUE); +#endif + } + + ac = (MonoAsyncCall*) ares->object_data; + g_assert (ac); + + *exc = ac->msg->exc; /* FIXME: GC add write barrier */ + *out_args = ac->out_args; + return ac->res; +} + +gboolean +mono_threadpool_remove_domain_jobs (MonoDomain *domain, int timeout) +{ + gint64 end; + ThreadPoolDomain *tpdomain; + gboolean ret; + + g_assert (domain); + g_assert (timeout >= -1); + + g_assert (mono_domain_is_unloading (domain)); + + if (timeout != -1) + end = mono_msec_ticks () + timeout; + +#ifndef DISABLE_SOCKETS + mono_threadpool_io_remove_domain_jobs (domain); + if (timeout != -1) { + if (mono_msec_ticks () > end) + return FALSE; + } +#endif + + /* + * Wait for all threads which execute jobs in the domain to exit. + * The is_unloading () check in worker_request () ensures that + * no new jobs are added after we enter the lock below. + */ + + if (!mono_lazy_is_initialized (&status)) + return TRUE; + + mono_refcount_inc (&threadpool); + + domains_lock (); + + tpdomain = tpdomain_get (domain); + if (!tpdomain) { + domains_unlock (); + mono_refcount_dec (&threadpool); + return TRUE; + } + + ret = TRUE; + + while (tpdomain->outstanding_request + tpdomain->threadpool_jobs > 0) { + if (timeout == -1) { + mono_coop_cond_wait (&tpdomain->cleanup_cond, &threadpool.domains_lock); + } else { + gint64 now; + gint res; + + now = mono_msec_ticks(); + if (now > end) { + ret = FALSE; + break; + } + + res = mono_coop_cond_timedwait (&tpdomain->cleanup_cond, &threadpool.domains_lock, end - now); + if (res != 0) { + ret = FALSE; + break; + } + } + } + + /* Remove from the list the worker threads look at */ + tpdomain_remove (tpdomain); + + domains_unlock (); + + mono_coop_cond_destroy (&tpdomain->cleanup_cond); + tpdomain_free (tpdomain); + + mono_refcount_dec (&threadpool); + + return ret; +} + +void +mono_threadpool_suspend (void) +{ + if (mono_lazy_is_initialized (&status)) + mono_threadpool_worker_set_suspended (TRUE); +} + +void +mono_threadpool_resume (void) +{ + if (mono_lazy_is_initialized (&status)) + mono_threadpool_worker_set_suspended (FALSE); +} + +void +ves_icall_System_Threading_ThreadPool_GetAvailableThreadsNative (gint32 *worker_threads, gint32 *completion_port_threads) +{ + ThreadPoolCounter counter; + + if (!worker_threads || !completion_port_threads) + return; + + if (!mono_lazy_initialize (&status, initialize) || !mono_refcount_tryinc (&threadpool)) { + *worker_threads = 0; + *completion_port_threads = 0; + return; + } + + counter = COUNTER_READ (); + + *worker_threads = MAX (0, mono_threadpool_worker_get_max () - counter._.working); + *completion_port_threads = threadpool.limit_io_max; + + mono_refcount_dec (&threadpool); +} + +void +ves_icall_System_Threading_ThreadPool_GetMinThreadsNative (gint32 *worker_threads, gint32 *completion_port_threads) +{ + if (!worker_threads || !completion_port_threads) + return; + + if (!mono_lazy_initialize (&status, initialize) || !mono_refcount_tryinc (&threadpool)) { + *worker_threads = 0; + *completion_port_threads = 0; + return; + } + + *worker_threads = mono_threadpool_worker_get_min (); + *completion_port_threads = threadpool.limit_io_min; + + mono_refcount_dec (&threadpool); +} + +void +ves_icall_System_Threading_ThreadPool_GetMaxThreadsNative (gint32 *worker_threads, gint32 *completion_port_threads) +{ + if (!worker_threads || !completion_port_threads) + return; + + if (!mono_lazy_initialize (&status, initialize) || !mono_refcount_tryinc (&threadpool)) { + *worker_threads = 0; + *completion_port_threads = 0; + return; + } + + *worker_threads = mono_threadpool_worker_get_max (); + *completion_port_threads = threadpool.limit_io_max; + + mono_refcount_dec (&threadpool); +} + +MonoBoolean +ves_icall_System_Threading_ThreadPool_SetMinThreadsNative (gint32 worker_threads, gint32 completion_port_threads) +{ + if (completion_port_threads <= 0 || completion_port_threads > threadpool.limit_io_max) + return FALSE; + + if (!mono_lazy_initialize (&status, initialize) || !mono_refcount_tryinc (&threadpool)) + return FALSE; + + if (!mono_threadpool_worker_set_min (worker_threads)) { + mono_refcount_dec (&threadpool); + return FALSE; + } + + threadpool.limit_io_min = completion_port_threads; + + mono_refcount_dec (&threadpool); + return TRUE; +} + +MonoBoolean +ves_icall_System_Threading_ThreadPool_SetMaxThreadsNative (gint32 worker_threads, gint32 completion_port_threads) +{ + worker_threads = MIN (worker_threads, MAX_POSSIBLE_THREADS); + completion_port_threads = MIN (completion_port_threads, MAX_POSSIBLE_THREADS); + + gint cpu_count = mono_cpu_count (); + + if (completion_port_threads < threadpool.limit_io_min || completion_port_threads < cpu_count) + return FALSE; + + if (!mono_lazy_initialize (&status, initialize) || !mono_refcount_tryinc (&threadpool)) + return FALSE; + + if (!mono_threadpool_worker_set_max (worker_threads)) { + mono_refcount_dec (&threadpool); + return FALSE; + } + + threadpool.limit_io_max = completion_port_threads; + + mono_refcount_dec (&threadpool); + return TRUE; +} + +void +ves_icall_System_Threading_ThreadPool_InitializeVMTp (MonoBoolean *enable_worker_tracking) +{ + if (enable_worker_tracking) { + // TODO implement some kind of switch to have the possibily to use it + *enable_worker_tracking = FALSE; + } + + mono_lazy_initialize (&status, initialize); +} + +MonoBoolean +ves_icall_System_Threading_ThreadPool_NotifyWorkItemComplete (void) +{ + if (mono_domain_is_unloading (mono_domain_get ()) || mono_runtime_is_shutting_down ()) + return FALSE; + + return mono_threadpool_worker_notify_completed (); +} + +void +ves_icall_System_Threading_ThreadPool_NotifyWorkItemProgressNative (void) +{ + mono_threadpool_worker_notify_completed (); +} + +void +ves_icall_System_Threading_ThreadPool_ReportThreadStatus (MonoBoolean is_working) +{ + // TODO + MonoError error; + mono_error_set_not_implemented (&error, ""); + mono_error_set_pending_exception (&error); +} + +MonoBoolean +ves_icall_System_Threading_ThreadPool_RequestWorkerThread (void) +{ + MonoDomain *domain; + ThreadPoolDomain *tpdomain; + ThreadPoolCounter counter; + + domain = mono_domain_get (); + if (mono_domain_is_unloading (domain)) + return FALSE; + + if (!mono_lazy_initialize (&status, initialize) || !mono_refcount_tryinc (&threadpool)) { + /* threadpool has been destroyed, we are shutting down */ + return FALSE; + } + + domains_lock (); + + tpdomain = tpdomain_get (domain); + if (!tpdomain) { + /* synchronize with mono_threadpool_remove_domain_jobs */ + if (mono_domain_is_unloading (domain)) { + domains_unlock (); + mono_refcount_dec (&threadpool); + return FALSE; + } + + tpdomain = tpdomain_create (domain); + } + + g_assert (tpdomain); + + tpdomain->outstanding_request ++; + g_assert (tpdomain->outstanding_request >= 1); + + domains_unlock (); + + COUNTER_ATOMIC (counter, { + if (counter._.starting == 16) { + mono_refcount_dec (&threadpool); + return TRUE; + } + + counter._.starting ++; + }); + + mono_threadpool_worker_request (); + + mono_refcount_dec (&threadpool); + return TRUE; +} + +MonoBoolean G_GNUC_UNUSED +ves_icall_System_Threading_ThreadPool_PostQueuedCompletionStatus (MonoNativeOverlapped *native_overlapped) +{ + /* This copy the behavior of the current Mono implementation */ + MonoError error; + mono_error_set_not_implemented (&error, ""); + mono_error_set_pending_exception (&error); + return FALSE; +} + +MonoBoolean G_GNUC_UNUSED +ves_icall_System_Threading_ThreadPool_BindIOCompletionCallbackNative (gpointer file_handle) +{ + /* This copy the behavior of the current Mono implementation */ + return TRUE; +} + +MonoBoolean G_GNUC_UNUSED +ves_icall_System_Threading_ThreadPool_IsThreadPoolHosted (void) +{ + return FALSE; +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/threadpool.h b/unity-2019.4.24f1-mbe/mono/metadata/threadpool.h new file mode 100644 index 000000000..91b404fe2 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/threadpool.h @@ -0,0 +1,67 @@ +/** + * \file + */ + +#ifndef _MONO_METADATA_THREADPOOL_H_ +#define _MONO_METADATA_THREADPOOL_H_ + +#include +#include + +#include +#include + +typedef struct _MonoNativeOverlapped MonoNativeOverlapped; + +void +mono_threadpool_cleanup (void); + +MonoAsyncResult * +mono_threadpool_begin_invoke (MonoDomain *domain, MonoObject *target, MonoMethod *method, gpointer *params, MonoError *error); +MonoObject * +mono_threadpool_end_invoke (MonoAsyncResult *ares, MonoArray **out_args, MonoObject **exc, MonoError *error); + +gboolean +mono_threadpool_remove_domain_jobs (MonoDomain *domain, int timeout); + +void +mono_threadpool_suspend (void); +void +mono_threadpool_resume (void); + +void +ves_icall_System_Threading_ThreadPool_GetAvailableThreadsNative (gint32 *worker_threads, gint32 *completion_port_threads); +void +ves_icall_System_Threading_ThreadPool_GetMinThreadsNative (gint32 *worker_threads, gint32 *completion_port_threads); +void +ves_icall_System_Threading_ThreadPool_GetMaxThreadsNative (gint32 *worker_threads, gint32 *completion_port_threads); +MonoBoolean +ves_icall_System_Threading_ThreadPool_SetMinThreadsNative (gint32 worker_threads, gint32 completion_port_threads); +MonoBoolean +ves_icall_System_Threading_ThreadPool_SetMaxThreadsNative (gint32 worker_threads, gint32 completion_port_threads); +void +ves_icall_System_Threading_ThreadPool_InitializeVMTp (MonoBoolean *enable_worker_tracking); +MonoBoolean +ves_icall_System_Threading_ThreadPool_NotifyWorkItemComplete (void); +void +ves_icall_System_Threading_ThreadPool_NotifyWorkItemProgressNative (void); +void +ves_icall_System_Threading_ThreadPool_ReportThreadStatus (MonoBoolean is_working); +MonoBoolean +ves_icall_System_Threading_ThreadPool_RequestWorkerThread (void); + +MonoBoolean +ves_icall_System_Threading_ThreadPool_PostQueuedCompletionStatus (MonoNativeOverlapped *native_overlapped); + +MonoBoolean +ves_icall_System_Threading_ThreadPool_BindIOCompletionCallbackNative (gpointer file_handle); + +MonoBoolean +ves_icall_System_Threading_ThreadPool_IsThreadPoolHosted (void); + +/* Internals */ + +gboolean +mono_threadpool_enqueue_work_item (MonoDomain *domain, MonoObject *work_item, MonoError *error); + +#endif // _MONO_METADATA_THREADPOOL_H_ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/threads-types.h b/unity-2019.4.24f1-mbe/mono/metadata/threads-types.h new file mode 100644 index 000000000..2fcf55787 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/threads-types.h @@ -0,0 +1,276 @@ +/** + * \file + * Generic thread typedef support (includes system-specific files) + * + * Author: + * Dick Porter (dick@ximian.com) + * + * (C) 2001 Ximian, Inc + * (C) Copyright 2002-2006 Novell, Inc + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#ifndef _MONO_METADATA_THREADS_TYPES_H_ +#define _MONO_METADATA_THREADS_TYPES_H_ + +#include + +#include +#include "mono/metadata/handle.h" +#include "mono/utils/mono-compiler.h" +#include "mono/utils/mono-membar.h" +#include "mono/utils/mono-threads.h" + +/* This is a copy of System.Threading.ThreadState */ +typedef enum { + ThreadState_Running = 0x00000000, + ThreadState_SuspendRequested = 0x00000002, + ThreadState_Background = 0x00000004, + ThreadState_Unstarted = 0x00000008, + ThreadState_Stopped = 0x00000010, + ThreadState_WaitSleepJoin = 0x00000020, + ThreadState_Suspended = 0x00000040, + ThreadState_AbortRequested = 0x00000080, + ThreadState_Aborted = 0x00000100 +} MonoThreadState; + +/* This is a copy of System.Threading.ApartmentState */ +typedef enum { + ThreadApartmentState_STA = 0x00000000, + ThreadApartmentState_MTA = 0x00000001, + ThreadApartmentState_Unknown = 0x00000002 +} MonoThreadApartmentState; + +typedef enum { + MONO_THREAD_PRIORITY_LOWEST = 0, + MONO_THREAD_PRIORITY_BELOW_NORMAL = 1, + MONO_THREAD_PRIORITY_NORMAL = 2, + MONO_THREAD_PRIORITY_ABOVE_NORMAL = 3, + MONO_THREAD_PRIORITY_HIGHEST = 4, +} MonoThreadPriority; + +#define SPECIAL_STATIC_NONE 0 +#define SPECIAL_STATIC_THREAD 1 +#define SPECIAL_STATIC_CONTEXT 2 + +typedef struct _MonoInternalThread MonoInternalThread; + +typedef void (*MonoThreadCleanupFunc) (MonoNativeThreadId tid); +/* INFO has type MonoThreadInfo* */ +typedef void (*MonoThreadNotifyPendingExcFunc) (gpointer info); + +void +mono_thread_callbacks_init (void); + +typedef enum { + MONO_THREAD_CREATE_FLAGS_NONE = 0x0, + MONO_THREAD_CREATE_FLAGS_THREADPOOL = 0x1, + MONO_THREAD_CREATE_FLAGS_DEBUGGER = 0x2, + MONO_THREAD_CREATE_FLAGS_FORCE_CREATE = 0x4, + MONO_THREAD_CREATE_FLAGS_SMALL_STACK = 0x8, +} MonoThreadCreateFlags; + +MonoInternalThread* +mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, MonoThreadCreateFlags flags, MonoError *error); + +void mono_threads_install_cleanup (MonoThreadCleanupFunc func); + +void ves_icall_System_Threading_Thread_ConstructInternalThread (MonoThread *this_obj); +gpointer ves_icall_System_Threading_Thread_Thread_internal(MonoThread *this_obj, MonoObject *start); +void ves_icall_System_Threading_InternalThread_Thread_free_internal(MonoInternalThread *this_obj); +void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms); +gboolean ves_icall_System_Threading_Thread_Join_internal(MonoThread *this_obj, int ms); +gint32 ves_icall_System_Threading_Thread_GetDomainID (void); +gboolean ves_icall_System_Threading_Thread_Yield (void); +MonoString* ves_icall_System_Threading_Thread_GetName_internal (MonoInternalThread *this_obj); +void ves_icall_System_Threading_Thread_SetName_internal (MonoInternalThread *this_obj, MonoString *name); +int ves_icall_System_Threading_Thread_GetPriority (MonoThread *this_obj); +void ves_icall_System_Threading_Thread_SetPriority (MonoThread *this_obj, int priority); +MonoObject* ves_icall_System_Threading_Thread_GetCachedCurrentCulture (MonoInternalThread *this_obj); +void ves_icall_System_Threading_Thread_SetCachedCurrentCulture (MonoThread *this_obj, MonoObject *culture); +MonoObject* ves_icall_System_Threading_Thread_GetCachedCurrentUICulture (MonoInternalThread *this_obj); +void ves_icall_System_Threading_Thread_SetCachedCurrentUICulture (MonoThread *this_obj, MonoObject *culture); +MonoThread *ves_icall_System_Threading_Thread_GetCurrentThread (void); + +gint32 ves_icall_System_Threading_WaitHandle_Wait_internal(gpointer *handles, gint32 numhandles, MonoBoolean waitall, gint32 ms, MonoError *error); +gint32 ves_icall_System_Threading_WaitHandle_SignalAndWait_Internal (gpointer toSignal, gpointer toWait, gint32 ms, MonoError *error); + +MonoArray* ves_icall_System_Threading_Thread_ByteArrayToRootDomain (MonoArray *arr); +MonoArray* ves_icall_System_Threading_Thread_ByteArrayToCurrentDomain (MonoArray *arr); + +gint32 ves_icall_System_Threading_Interlocked_Increment_Int(gint32 *location); +gint64 ves_icall_System_Threading_Interlocked_Increment_Long(gint64 *location); +gint32 ves_icall_System_Threading_Interlocked_Decrement_Int(gint32 *location); +gint64 ves_icall_System_Threading_Interlocked_Decrement_Long(gint64 * location); + +gint32 ves_icall_System_Threading_Interlocked_Exchange_Int(gint32 *location, gint32 value); +gint64 ves_icall_System_Threading_Interlocked_Exchange_Long(gint64 *location, gint64 value); +MonoObject *ves_icall_System_Threading_Interlocked_Exchange_Object(MonoObject **location, MonoObject *value); +gpointer ves_icall_System_Threading_Interlocked_Exchange_IntPtr(gpointer *location, gpointer value); +gfloat ves_icall_System_Threading_Interlocked_Exchange_Single(gfloat *location, gfloat value); +gdouble ves_icall_System_Threading_Interlocked_Exchange_Double(gdouble *location, gdouble value); + +gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *location, gint32 value, gint32 comparand); +gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int_Success(gint32 *location, gint32 value, gint32 comparand, MonoBoolean *success); +gint64 ves_icall_System_Threading_Interlocked_CompareExchange_Long(gint64 *location, gint64 value, gint64 comparand); +MonoObject *ves_icall_System_Threading_Interlocked_CompareExchange_Object(MonoObject **location, MonoObject *value, MonoObject *comparand); +gpointer ves_icall_System_Threading_Interlocked_CompareExchange_IntPtr(gpointer *location, gpointer value, gpointer comparand); +gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single(gfloat *location, gfloat value, gfloat comparand); +gdouble ves_icall_System_Threading_Interlocked_CompareExchange_Double(gdouble *location, gdouble value, gdouble comparand); +MonoObject* ves_icall_System_Threading_Interlocked_CompareExchange_T(MonoObject **location, MonoObject *value, MonoObject *comparand); +MonoObject* ves_icall_System_Threading_Interlocked_Exchange_T(MonoObject **location, MonoObject *value); + +gint32 ves_icall_System_Threading_Interlocked_Add_Int(gint32 *location, gint32 value); +gint64 ves_icall_System_Threading_Interlocked_Add_Long(gint64 *location, gint64 value); +gint64 ves_icall_System_Threading_Interlocked_Read_Long(gint64 *location); + +gint32 ves_icall_System_Threading_Interlocked_Increment_Int(gint32 *location); +gint64 ves_icall_System_Threading_Interlocked_Increment_Long(gint64 *location); + +gint32 ves_icall_System_Threading_Interlocked_Decrement_Int(gint32 *location); +gint64 ves_icall_System_Threading_Interlocked_Decrement_Long(gint64 * location); + +void ves_icall_System_Threading_Thread_Abort (MonoInternalThread *thread, MonoObject *state); +void ves_icall_System_Threading_Thread_ResetAbort (MonoThread *this_obj); +MonoObject* ves_icall_System_Threading_Thread_GetAbortExceptionState (MonoThread *thread); +void ves_icall_System_Threading_Thread_Suspend (MonoThread *this_obj); +void ves_icall_System_Threading_Thread_Resume (MonoThread *thread); +void ves_icall_System_Threading_Thread_ClrState (MonoInternalThread *thread, guint32 state); +void ves_icall_System_Threading_Thread_SetState (MonoInternalThread *thread, guint32 state); +guint32 ves_icall_System_Threading_Thread_GetState (MonoInternalThread *thread); + +gint8 ves_icall_System_Threading_Thread_VolatileRead1 (void *ptr); +gint16 ves_icall_System_Threading_Thread_VolatileRead2 (void *ptr); +gint32 ves_icall_System_Threading_Thread_VolatileRead4 (void *ptr); +gint64 ves_icall_System_Threading_Thread_VolatileRead8 (void *ptr); +void * ves_icall_System_Threading_Thread_VolatileReadIntPtr (void *ptr); +void * ves_icall_System_Threading_Thread_VolatileReadObject (void *ptr); +double ves_icall_System_Threading_Thread_VolatileReadDouble (void *ptr); +float ves_icall_System_Threading_Thread_VolatileReadFloat (void *ptr); + +void ves_icall_System_Threading_Thread_VolatileWrite1 (void *ptr, gint8); +void ves_icall_System_Threading_Thread_VolatileWrite2 (void *ptr, gint16); +void ves_icall_System_Threading_Thread_VolatileWrite4 (void *ptr, gint32); +void ves_icall_System_Threading_Thread_VolatileWrite8 (void *ptr, gint64); +void ves_icall_System_Threading_Thread_VolatileWriteIntPtr (void *ptr, void *); +void ves_icall_System_Threading_Thread_VolatileWriteObject (void *ptr, MonoObject *); +void ves_icall_System_Threading_Thread_VolatileWriteFloat (void *ptr, float); +void ves_icall_System_Threading_Thread_VolatileWriteDouble (void *ptr, double); + +gint8 ves_icall_System_Threading_Volatile_Read1 (void *ptr); +gint16 ves_icall_System_Threading_Volatile_Read2 (void *ptr); +gint32 ves_icall_System_Threading_Volatile_Read4 (void *ptr); +gint64 ves_icall_System_Threading_Volatile_Read8 (void *ptr); +void * ves_icall_System_Threading_Volatile_ReadIntPtr (void *ptr); +double ves_icall_System_Threading_Volatile_ReadDouble (void *ptr); +float ves_icall_System_Threading_Volatile_ReadFloat (void *ptr); +MonoObject* ves_icall_System_Threading_Volatile_Read_T (void *ptr); + +void ves_icall_System_Threading_Volatile_Write1 (void *ptr, gint8); +void ves_icall_System_Threading_Volatile_Write2 (void *ptr, gint16); +void ves_icall_System_Threading_Volatile_Write4 (void *ptr, gint32); +void ves_icall_System_Threading_Volatile_Write8 (void *ptr, gint64); +void ves_icall_System_Threading_Volatile_WriteIntPtr (void *ptr, void *); +void ves_icall_System_Threading_Volatile_WriteFloat (void *ptr, float); +void ves_icall_System_Threading_Volatile_WriteDouble (void *ptr, double); +void ves_icall_System_Threading_Volatile_Write_T (void *ptr, MonoObject *value); + +void ves_icall_System_Threading_Thread_MemoryBarrier (void); +void ves_icall_System_Threading_Thread_Interrupt_internal (MonoThread *this_obj); +void ves_icall_System_Threading_Thread_SpinWait_nop (void); + +void +mono_threads_register_app_context (MonoAppContext* ctx, MonoError *error); +void +mono_threads_release_app_context (MonoAppContext* ctx, MonoError *error); + +void ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext (MonoAppContextHandle ctx, MonoError *error); +void ves_icall_System_Runtime_Remoting_Contexts_Context_ReleaseContext (MonoAppContextHandle ctx, MonoError *error); + +MonoInternalThread *mono_thread_internal_current (void); + +void mono_thread_internal_abort (MonoInternalThread *thread, gboolean appdomain_unload); +void mono_thread_internal_suspend_for_shutdown (MonoInternalThread *thread); + +gboolean mono_thread_internal_has_appdomain_ref (MonoInternalThread *thread, MonoDomain *domain); + +void mono_thread_internal_reset_abort (MonoInternalThread *thread); + +void mono_thread_internal_unhandled_exception (MonoObject* exc); + +void mono_alloc_special_static_data_free (GHashTable *special_static_fields); +gboolean mono_thread_current_check_pending_interrupt (void); + +void mono_thread_set_state (MonoInternalThread *thread, MonoThreadState state); +void mono_thread_clr_state (MonoInternalThread *thread, MonoThreadState state); +gboolean mono_thread_test_state (MonoInternalThread *thread, MonoThreadState test); +gboolean mono_thread_test_and_set_state (MonoInternalThread *thread, MonoThreadState test, MonoThreadState set); + +void mono_thread_init_apartment_state (void); +void mono_thread_cleanup_apartment_state (void); + +UNITY_MONO_API void mono_threads_set_shutting_down (void); + +gunichar2* mono_thread_get_name (MonoInternalThread *this_obj, guint32 *name_len); + +MONO_API MonoException* mono_thread_get_undeniable_exception (void); +void mono_thread_self_abort (void); + +void mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, gboolean permanent, gboolean reset, MonoError *error); + +UNITY_MONO_API void mono_thread_suspend_all_other_threads (void); +gboolean mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout); + +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); +gboolean mono_thread_interruption_requested (void); +MonoException* mono_thread_interruption_checkpoint (void); +MonoException* mono_thread_force_interruption_checkpoint_noraise (void); +gint32* mono_thread_interruption_request_flag (void); + +uint32_t mono_alloc_special_static_data (uint32_t static_type, uint32_t size, uint32_t align, uintptr_t *bitmap, int numbits); +void* mono_get_special_static_data (uint32_t offset); +gpointer mono_get_special_static_data_for_thread (MonoInternalThread *thread, guint32 offset); + +MonoException* mono_thread_resume_interruption (gboolean exec); +void mono_threads_perform_thread_dump (void); + +gboolean +mono_thread_create_checked (MonoDomain *domain, gpointer func, gpointer arg, MonoError *error); + +/* Can't include utils/mono-threads.h because of the THREAD_INFO_TYPE wizardry */ +void mono_threads_add_joinable_runtime_thread (gpointer thread_info); +void mono_threads_add_joinable_thread (gpointer tid); +void mono_threads_join_threads (void); +void mono_thread_join (gpointer tid); + +void ves_icall_System_Threading_Thread_GetStackTraces (MonoArray **out_threads, MonoArray **out_stack_traces); + +MONO_API gpointer +mono_threads_attach_coop (MonoDomain *domain, gpointer *dummy); + +MONO_API void +mono_threads_detach_coop (gpointer cookie, gpointer *dummy); + +void mono_threads_begin_abort_protected_block (void); +gboolean mono_threads_end_abort_protected_block (void); + +gboolean +mono_thread_internal_current_is_attached (void); + +void +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/unity-2019.4.24f1-mbe/mono/metadata/threads.c b/unity-2019.4.24f1-mbe/mono/metadata/threads.c new file mode 100644 index 000000000..379c7a7c6 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/threads.c @@ -0,0 +1,5680 @@ +/** + * \file + * Thread support internal calls + * + * Author: + * Dick Porter (dick@ximian.com) + * Paolo Molaro (lupus@ximian.com) + * Patrik Torstensson (patrik.torstensson@labs2.com) + * + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Copyright 2011 Xamarin, Inc (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#ifdef HAVE_SIGNAL_H +#include +#endif + +#if defined(HOST_WIN32) +#include + +extern gboolean +mono_native_thread_join_handle (HANDLE thread_handle, gboolean close_handle); +#endif + +#if defined(HOST_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64) +#define USE_TKILL_ON_ANDROID 1 +#endif + +#ifdef HOST_ANDROID +#include + +#ifdef USE_TKILL_ON_ANDROID +extern int tkill (pid_t tid, int signal); +#endif +#endif + +/*#define THREAD_DEBUG(a) do { a; } while (0)*/ +#define THREAD_DEBUG(a) +/*#define THREAD_WAIT_DEBUG(a) do { a; } while (0)*/ +#define THREAD_WAIT_DEBUG(a) +/*#define LIBGC_DEBUG(a) do { a; } while (0)*/ +#define LIBGC_DEBUG(a) + +#define SPIN_TRYLOCK(i) (mono_atomic_cas_i32 (&(i), 1, 0) == 0) +#define SPIN_LOCK(i) do { \ + if (SPIN_TRYLOCK (i)) \ + break; \ + } while (1) + +#define SPIN_UNLOCK(i) i = 0 + +#define LOCK_THREAD(thread) lock_thread((thread)) +#define UNLOCK_THREAD(thread) unlock_thread((thread)) + +typedef union { + gint32 ival; + gfloat fval; +} IntFloatUnion; + +typedef union { + gint64 ival; + gdouble fval; +} LongDoubleUnion; + +typedef struct _StaticDataFreeList StaticDataFreeList; +struct _StaticDataFreeList { + StaticDataFreeList *next; + guint32 offset; + guint32 size; + gint32 align; +}; + +typedef struct { + int idx; + int offset; + StaticDataFreeList *freelist; +} StaticDataInfo; + +/* Controls access to the 'threads' hash table */ +static void mono_threads_lock (void); +static void mono_threads_unlock (void); +static MonoCoopMutex threads_mutex; + +/* Controls access to the 'joinable_threads' hash table */ +#define joinable_threads_lock() mono_coop_mutex_lock (&joinable_threads_mutex) +#define joinable_threads_unlock() mono_coop_mutex_unlock (&joinable_threads_mutex) +static MonoCoopMutex joinable_threads_mutex; + +/* Holds current status of static data heap */ +static StaticDataInfo thread_static_info; +static StaticDataInfo context_static_info; + +/* The hash of existing threads (key is thread ID, value is + * MonoInternalThread*) that need joining before exit + */ +static MonoGHashTable *threads=NULL; + +/* List of app context GC handles. + * Added to from mono_threads_register_app_context (). + */ +static GHashTable *contexts = NULL; + +/* Cleanup queue for contexts. */ +static MonoReferenceQueue *context_queue; + +/* + * Threads which are starting up and they are not in the 'threads' hash yet. + * When mono_thread_attach_internal is called for a thread, it will be removed from this hash table. + * Protected by mono_threads_lock (). + */ +static MonoGHashTable *threads_starting_up = NULL; + +/* Contains tids */ +/* Protected by the threads lock */ +static GHashTable *joinable_threads; +static gint32 joinable_thread_count; + +/* mono_threads_join_threads will take threads from joinable_threads list and wait for them. */ +/* When this happens, the tid is not on the list anymore so mono_thread_join assumes the thread has complete */ +/* and will return back to the caller. This could cause a race since caller of join assumes thread has completed */ +/* and on some OS it could cause errors. Keeping the tid's currently pending a native thread join call */ +/* in a separate table (only affecting callers interested in this internal join detail) and look at that table in mono_thread_join */ +/* will close this race. */ +static GHashTable *pending_native_thread_join_calls; +static MonoCoopCond pending_native_thread_join_calls_event; + +static GHashTable *pending_joinable_threads; +static gint32 pending_joinable_thread_count; + +static MonoCoopCond zero_pending_joinable_thread_event; + +static void threads_add_pending_joinable_runtime_thread (MonoThreadInfo *mono_thread_info); +static gboolean threads_wait_pending_joinable_threads (uint32_t timeout); + +#define SET_CURRENT_OBJECT(x) mono_tls_set_thread (x) +#define GET_CURRENT_OBJECT() (MonoInternalThread*) mono_tls_get_thread () + +/* function called at thread start */ +static MonoThreadStartCB mono_thread_start_cb = NULL; + +/* function called at thread attach */ +static MonoThreadAttachCB mono_thread_attach_cb = NULL; + +/* function called at thread cleanup */ +static MonoThreadCleanupFunc mono_thread_cleanup_fn = NULL; + +/* The default stack size for each thread */ +static guint32 default_stacksize = 0; +#define default_stacksize_for_thread(thread) ((thread)->stack_size? (thread)->stack_size: default_stacksize) + +static void context_adjust_static_data (MonoAppContext *ctx); +static void mono_free_static_data (gpointer* static_data); +static void mono_init_static_data_info (StaticDataInfo *static_data); +static guint32 mono_alloc_static_data_slot (StaticDataInfo *static_data, guint32 size, guint32 align); +static gboolean mono_thread_resume (MonoInternalThread* thread); +static void async_abort_internal (MonoInternalThread *thread, gboolean install_async_abort); +static void self_abort_internal (MonoError *error); +static void async_suspend_internal (MonoInternalThread *thread, gboolean interrupt); +static void self_suspend_internal (void); + +static MonoException* mono_thread_execute_interruption (void); +static void ref_stack_destroy (gpointer rs); + +/* Spin lock for InterlockedXXX 64 bit functions */ +#define mono_interlocked_lock() mono_os_mutex_lock (&interlocked_mutex) +#define mono_interlocked_unlock() mono_os_mutex_unlock (&interlocked_mutex) +static mono_mutex_t interlocked_mutex; + +/* global count of thread interruptions requested */ +static gint32 thread_interruption_requested = 0; + +/* Event signaled when a thread changes its background mode */ +static MonoOSEvent background_change_event; + +static gboolean shutting_down = FALSE; +gboolean unity_shutting_down = FALSE; + +static gint32 managed_thread_id_counter = 0; + +static void +mono_threads_lock (void) +{ + mono_locks_coop_acquire (&threads_mutex, ThreadsLock); +} + +static void +mono_threads_unlock (void) +{ + mono_locks_coop_release (&threads_mutex, ThreadsLock); +} + + +static guint32 +get_next_managed_thread_id (void) +{ + return mono_atomic_inc_i32 (&managed_thread_id_counter); +} + +/* + * We separate interruptions/exceptions into either sync (they can be processed anytime, + * normally as soon as they are set, and are set by the same thread) and async (they can't + * be processed inside abort protected blocks and are normally set by other threads). We + * can have both a pending sync and async interruption. In this case, the sync exception is + * processed first. Since we clean sync flag first, mono_thread_execute_interruption must + * also handle all sync type exceptions before the async type exceptions. + */ +enum { + INTERRUPT_SYNC_REQUESTED_BIT = 0x1, + INTERRUPT_ASYNC_REQUESTED_BIT = 0x2, + INTERRUPT_REQUESTED_MASK = 0x3, + ABORT_PROT_BLOCK_SHIFT = 2, + ABORT_PROT_BLOCK_BITS = 8, + ABORT_PROT_BLOCK_MASK = (((1 << ABORT_PROT_BLOCK_BITS) - 1) << ABORT_PROT_BLOCK_SHIFT) +}; + +static int +mono_thread_get_abort_prot_block_count (MonoInternalThread *thread) +{ + gsize state = thread->thread_state; + return (state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT; +} + +void +mono_threads_begin_abort_protected_block (void) +{ + MonoInternalThread *thread = mono_thread_internal_current (); + gsize old_state, new_state; + int new_val; + do { + old_state = thread->thread_state; + + new_val = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT) + 1; + //bounds check abort_prot_count + g_assert (new_val > 0); + g_assert (new_val < (1 << ABORT_PROT_BLOCK_BITS)); + + new_state = old_state + (1 << ABORT_PROT_BLOCK_SHIFT); + } while (mono_atomic_cas_ptr ((volatile gpointer *)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state); + + /* Defer async request since we won't be able to process until exiting the block */ + if (new_val == 1 && (new_state & INTERRUPT_ASYNC_REQUESTED_BIT)) { + mono_atomic_dec_i32 (&thread_interruption_requested); + THREADS_INTERRUPT_DEBUG ("[%d] begin abort protected block old_state %ld new_state %ld, defer tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested); + if (thread_interruption_requested < 0) + g_warning ("bad thread_interruption_requested state"); + } else { + THREADS_INTERRUPT_DEBUG ("[%d] begin abort protected block old_state %ld new_state %ld, tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested); + } +} + +static gboolean +mono_thread_state_has_interruption (gsize state) +{ + /* pending exception, self abort */ + if (state & INTERRUPT_SYNC_REQUESTED_BIT) + return TRUE; + + /* abort, interruption, suspend */ + if ((state & INTERRUPT_ASYNC_REQUESTED_BIT) && !(state & ABORT_PROT_BLOCK_MASK)) + return TRUE; + + return FALSE; +} + +gboolean +mono_threads_end_abort_protected_block (void) +{ + MonoInternalThread *thread = mono_thread_internal_current (); + gsize old_state, new_state; + int new_val; + do { + old_state = thread->thread_state; + + //bounds check abort_prot_count + new_val = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT) - 1; + g_assert (new_val >= 0); + g_assert (new_val < (1 << ABORT_PROT_BLOCK_BITS)); + + new_state = old_state - (1 << ABORT_PROT_BLOCK_SHIFT); + } while (mono_atomic_cas_ptr ((volatile gpointer *)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state); + + if (new_val == 0 && (new_state & INTERRUPT_ASYNC_REQUESTED_BIT)) { + mono_atomic_inc_i32 (&thread_interruption_requested); + THREADS_INTERRUPT_DEBUG ("[%d] end abort protected block old_state %ld new_state %ld, restore tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested); + } else { + THREADS_INTERRUPT_DEBUG ("[%d] end abort protected block old_state %ld new_state %ld, tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested); + } + + return mono_thread_state_has_interruption (new_state); +} + +static gboolean +mono_thread_get_interruption_requested (MonoInternalThread *thread) +{ + gsize state = thread->thread_state; + + return mono_thread_state_has_interruption (state); +} + +/* + * Returns TRUE is there was a state change + * We clear a single interruption request, sync has priority. + */ +static gboolean +mono_thread_clear_interruption_requested (MonoInternalThread *thread) +{ + gsize old_state, new_state; + do { + old_state = thread->thread_state; + + // no interruption to process + if (!(old_state & INTERRUPT_SYNC_REQUESTED_BIT) && + (!(old_state & INTERRUPT_ASYNC_REQUESTED_BIT) || (old_state & ABORT_PROT_BLOCK_MASK))) + return FALSE; + + if (old_state & INTERRUPT_SYNC_REQUESTED_BIT) + new_state = old_state & ~INTERRUPT_SYNC_REQUESTED_BIT; + else + new_state = old_state & ~INTERRUPT_ASYNC_REQUESTED_BIT; + } while (mono_atomic_cas_ptr ((volatile gpointer *)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state); + + mono_atomic_dec_i32 (&thread_interruption_requested); + THREADS_INTERRUPT_DEBUG ("[%d] clear interruption old_state %ld new_state %ld, tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested); + if (thread_interruption_requested < 0) + g_warning ("bad thread_interruption_requested state"); + return TRUE; +} + +/* Returns TRUE is there was a state change and the interruption can be processed */ +static gboolean +mono_thread_set_interruption_requested (MonoInternalThread *thread) +{ + //always force when the current thread is doing it to itself. + gboolean sync = thread == mono_thread_internal_current (); + gsize old_state, new_state; + do { + old_state = thread->thread_state; + + //Already set + if ((sync && (old_state & INTERRUPT_SYNC_REQUESTED_BIT)) || + (!sync && (old_state & INTERRUPT_ASYNC_REQUESTED_BIT))) + return FALSE; + + if (sync) + new_state = old_state | INTERRUPT_SYNC_REQUESTED_BIT; + else + new_state = old_state | INTERRUPT_ASYNC_REQUESTED_BIT; + } while (mono_atomic_cas_ptr ((volatile gpointer *)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state); + + if (sync || !(new_state & ABORT_PROT_BLOCK_MASK)) { + mono_atomic_inc_i32 (&thread_interruption_requested); + THREADS_INTERRUPT_DEBUG ("[%d] set interruption on [%d] old_state %ld new_state %ld, tir %d\n", mono_thread_internal_current ()->small_id, thread->small_id, old_state, new_state, thread_interruption_requested); + } else { + THREADS_INTERRUPT_DEBUG ("[%d] set interruption on [%d] old_state %ld new_state %ld, tir deferred %d\n", mono_thread_internal_current ()->small_id, thread->small_id, old_state, new_state, thread_interruption_requested); + } + + return sync || !(new_state & ABORT_PROT_BLOCK_MASK); +} + +static inline MonoNativeThreadId +thread_get_tid (MonoInternalThread *thread) +{ + /* We store the tid as a guint64 to keep the object layout constant between platforms */ + return MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid); +} + +static void ensure_synch_cs_set (MonoInternalThread *thread) +{ + MonoCoopMutex *synch_cs; + + if (thread->synch_cs != NULL) { + return; + } + + synch_cs = g_new0 (MonoCoopMutex, 1); + mono_coop_mutex_init_recursive (synch_cs); + + if (mono_atomic_cas_ptr ((gpointer *)&thread->synch_cs, + synch_cs, NULL) != NULL) { + /* Another thread must have installed this CS */ + mono_coop_mutex_destroy (synch_cs); + g_free (synch_cs); + } +} + +static inline void +lock_thread (MonoInternalThread *thread) +{ + if (!thread->synch_cs) + ensure_synch_cs_set (thread); + + g_assert (thread->synch_cs); + + mono_coop_mutex_lock (thread->synch_cs); +} + +static inline void +unlock_thread (MonoInternalThread *thread) +{ + mono_coop_mutex_unlock (thread->synch_cs); +} + +static inline gboolean +is_appdomainunloaded_exception (MonoClass *klass) +{ + return klass == mono_class_get_appdomain_unloaded_exception_class (); +} + +static inline gboolean +is_threadabort_exception (MonoClass *klass) +{ + return klass == mono_defaults.threadabortexception_class; +} + +/* + * A special static data offset (guint32) consists of 3 parts: + * + * [0] 6-bit index into the array of chunks. + * [6] 25-bit offset into the array. + * [31] Bit indicating thread or context static. + */ + +typedef union { + struct { +#if G_BYTE_ORDER != G_LITTLE_ENDIAN + guint32 type : 1; + guint32 offset : 25; + guint32 index : 6; +#else + guint32 index : 6; + guint32 offset : 25; + guint32 type : 1; +#endif + } fields; + guint32 raw; +} SpecialStaticOffset; + +#define SPECIAL_STATIC_OFFSET_TYPE_THREAD 0 +#define SPECIAL_STATIC_OFFSET_TYPE_CONTEXT 1 + +#define MAKE_SPECIAL_STATIC_OFFSET(idx, off, ty) \ + ((SpecialStaticOffset) { .fields = { .index = (idx), .offset = (off), .type = (ty) } }.raw) +#define ACCESS_SPECIAL_STATIC_OFFSET(x,f) \ + (((SpecialStaticOffset *) &(x))->fields.f) + +static gpointer +get_thread_static_data (MonoInternalThread *thread, guint32 offset) +{ + g_assert (ACCESS_SPECIAL_STATIC_OFFSET (offset, type) == SPECIAL_STATIC_OFFSET_TYPE_THREAD); + + int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index); + int off = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset); + + return ((char *) thread->static_data [idx]) + off; +} + +static gpointer +get_context_static_data (MonoAppContext *ctx, guint32 offset) +{ + g_assert (ACCESS_SPECIAL_STATIC_OFFSET (offset, type) == SPECIAL_STATIC_OFFSET_TYPE_CONTEXT); + + int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index); + int off = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset); + + return ((char *) ctx->static_data [idx]) + off; +} + +static MonoThread** +get_current_thread_ptr_for_domain (MonoDomain *domain, MonoInternalThread *thread) +{ + static MonoClassField *current_thread_field = NULL; + + guint32 offset; + + if (!current_thread_field) { + current_thread_field = mono_class_get_field_from_name (mono_defaults.thread_class, "current_thread"); + g_assert (current_thread_field); + } + + mono_class_vtable (domain, mono_defaults.thread_class); + mono_domain_lock (domain); + offset = GPOINTER_TO_UINT (g_hash_table_lookup (domain->special_static_fields, current_thread_field)); + mono_domain_unlock (domain); + g_assert (offset); + + return (MonoThread **)get_thread_static_data (thread, offset); +} + +static void +set_current_thread_for_domain (MonoDomain *domain, MonoInternalThread *thread, MonoThread *current) +{ + MonoThread **current_thread_ptr = get_current_thread_ptr_for_domain (domain, thread); + + g_assert (current->obj.vtable->domain == domain); + + g_assert (!*current_thread_ptr); + *current_thread_ptr = current; + mono_gc_wbarrier_generic_nostore (current_thread_ptr); +} + +static MonoThread* +create_thread_object (MonoDomain *domain, MonoInternalThread *internal) +{ + MonoThread *thread; + MonoVTable *vtable; + MonoError error; + + vtable = mono_class_vtable (domain, mono_defaults.thread_class); + g_assert (vtable); + + thread = (MonoThread*)mono_object_new_mature (vtable, &error); + /* only possible failure mode is OOM, from which we don't expect to recover. */ + mono_error_assert_ok (&error); + + MONO_OBJECT_SETREF (thread, internal_thread, internal); + + return thread; +} + +static MonoInternalThread* +create_internal_thread_object (void) +{ + MonoError error; + MonoInternalThread *thread; + MonoVTable *vt; + + vt = mono_class_vtable (mono_get_root_domain (), mono_defaults.internal_thread_class); + thread = (MonoInternalThread*) mono_object_new_mature (vt, &error); + /* only possible failure mode is OOM, from which we don't exect to recover */ + mono_error_assert_ok (&error); + + thread->synch_cs = g_new0 (MonoCoopMutex, 1); + mono_coop_mutex_init_recursive (thread->synch_cs); + + 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"); + } + + thread->priority = MONO_THREAD_PRIORITY_NORMAL; + + thread->suspended = g_new0 (MonoOSEvent, 1); + mono_os_event_init (thread->suspended, TRUE); + + return thread; +} + +static void +mono_thread_internal_set_priority (MonoInternalThread *internal, MonoThreadPriority priority) +{ + g_assert (internal); + + g_assert (priority >= MONO_THREAD_PRIORITY_LOWEST); + g_assert (priority <= MONO_THREAD_PRIORITY_HIGHEST); + g_assert (MONO_THREAD_PRIORITY_LOWEST < MONO_THREAD_PRIORITY_HIGHEST); + +#ifdef HOST_WIN32 + BOOL res; + + g_assert (internal->native_handle); + + res = SetThreadPriority (internal->native_handle, priority - 2); + if (!res) + g_error ("%s: SetThreadPriority failed, error %d", __func__, GetLastError ()); +#else /* HOST_WIN32 */ + pthread_t tid; + int policy; + struct sched_param param; + gint res; + + tid = thread_get_tid (internal); + + res = pthread_getschedparam (tid, &policy, ¶m); + if (res != 0) + g_error ("%s: pthread_getschedparam failed, error: \"%s\" (%d)", __func__, g_strerror (res), res); + +#ifdef _POSIX_PRIORITY_SCHEDULING + int max, min; + + /* Necessary to get valid priority range */ + + min = sched_get_priority_min (policy); + max = sched_get_priority_max (policy); + + if (max > 0 && min >= 0 && max > min) { + double srange, drange, sposition, dposition; + srange = MONO_THREAD_PRIORITY_HIGHEST - MONO_THREAD_PRIORITY_LOWEST; + drange = max - min; + sposition = priority - MONO_THREAD_PRIORITY_LOWEST; + dposition = (sposition / srange) * drange; + param.sched_priority = (int)(dposition + min); + } else +#endif + { + switch (policy) { + case SCHED_FIFO: + case SCHED_RR: + param.sched_priority = 50; + break; +#ifdef SCHED_BATCH + case SCHED_BATCH: +#endif + case SCHED_OTHER: + param.sched_priority = 0; + break; + default: + g_warning ("%s: unknown policy %d", __func__, policy); + return; + } + } + + res = pthread_setschedparam (tid, policy, ¶m); + if (res != 0) { + if (res == EPERM) { + g_warning ("%s: pthread_setschedparam failed, error: \"%s\" (%d)", __func__, g_strerror (res), res); + return; + } + g_error ("%s: pthread_setschedparam failed, error: \"%s\" (%d)", __func__, g_strerror (res), res); + } +#endif /* HOST_WIN32 */ +} + +static void +mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, void *alloc_key, gboolean threadlocal); + +static gboolean +mono_thread_attach_internal (MonoThread *thread, gboolean force_attach, gboolean force_domain) +{ + MonoThreadInfo *info; + MonoInternalThread *internal; + MonoDomain *domain, *root_domain; + + g_assert (thread); + + info = mono_thread_info_current (); + g_assert (info); + + internal = thread->internal_thread; + g_assert (internal); + + /* It is needed to store the MonoInternalThread on the MonoThreadInfo, because of the following case: + * - the MonoInternalThread TLS key is destroyed: set it to NULL + * - the MonoThreadInfo TLS key is destroyed: calls mono_thread_info_detach + * - it calls MonoThreadInfoCallbacks.thread_detach + * - mono_thread_internal_current returns NULL -> fails to detach the MonoInternalThread. */ + mono_thread_info_set_internal_thread_gchandle (info, mono_gchandle_new ((MonoObject*) internal, FALSE)); + + internal->handle = mono_threads_open_thread_handle (info->handle); +#ifdef HOST_WIN32 + internal->native_handle = OpenThread (THREAD_ALL_ACCESS, FALSE, GetCurrentThreadId ()); +#endif + internal->tid = MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ()); + internal->thread_info = info; + internal->small_id = info->small_id; + + THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Setting current_object_key to %p", __func__, mono_native_thread_id_get (), internal)); + + SET_CURRENT_OBJECT (internal); + + domain = mono_object_domain (thread); + + mono_thread_push_appdomain_ref (domain); + if (!mono_domain_set (domain, force_domain)) { + mono_thread_pop_appdomain_ref (); + return FALSE; + } + + mono_threads_lock (); + + if (threads_starting_up) + mono_g_hash_table_remove (threads_starting_up, thread); + + if (shutting_down && !force_attach) { + mono_threads_unlock (); + mono_thread_pop_appdomain_ref (); + return FALSE; + } + + if (!threads) { + threads = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_THREADING, NULL, "Thread Table"); + } + + /* We don't need to duplicate thread->handle, because it is + * only closed when the thread object is finalized by the GC. */ + mono_g_hash_table_insert (threads, (gpointer)(gsize)(internal->tid), internal); + + /* We have to do this here because mono_thread_start_cb + * requires that root_domain_thread is set up. */ + if (thread_static_info.offset || thread_static_info.idx > 0) { + /* get the current allocated size */ + guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (thread_static_info.idx, thread_static_info.offset, 0); + mono_alloc_static_data (&internal->static_data, offset, (void *) MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid), TRUE); + } + + mono_threads_unlock (); + + root_domain = mono_get_root_domain (); + + g_assert (!internal->root_domain_thread); + if (domain != root_domain) + MONO_OBJECT_SETREF (internal, root_domain_thread, create_thread_object (root_domain, internal)); + else + MONO_OBJECT_SETREF (internal, root_domain_thread, thread); + + if (domain != root_domain) + set_current_thread_for_domain (root_domain, internal, internal->root_domain_thread); + + set_current_thread_for_domain (domain, internal, thread); + + THREAD_DEBUG (g_message ("%s: Attached thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, internal->tid, internal->handle)); + + return TRUE; +} + +static void +mono_thread_detach_internal (MonoInternalThread *thread) +{ + MonoThreadInfo *info; + gboolean removed; + guint32 gchandle; + + g_assert (thread != NULL); + SET_CURRENT_OBJECT (thread); + + info = (MonoThreadInfo*) thread->thread_info; + g_assert (info); + + THREAD_DEBUG (g_message ("%s: mono_thread_detach for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->tid)); + + MONO_PROFILER_RAISE (thread_stopping, (thread->tid)); + + /* + * Prevent race condition between thread shutdown and runtime shutdown. + * Including all runtime threads in the pending joinable count will make + * sure shutdown will wait for it to get onto the joinable thread list before + * critical resources have been cleanup (like GC memory). Threads getting onto + * the joinable thread list should just about to exit and not blocking a potential + * join call. Owner of threads attached to the runtime but not identified as runtime + * threads needs to make sure thread detach calls won't race with runtime shutdown. + */ + threads_add_pending_joinable_runtime_thread (info); + +#ifndef HOST_WIN32 + mono_w32mutex_abandon (); +#endif + + if (thread->abort_state_handle) { + mono_gchandle_free (thread->abort_state_handle); + thread->abort_state_handle = 0; + } + + thread->abort_exc = NULL; + thread->current_appcontext = NULL; + + /* + * thread->synch_cs can be NULL if this was called after + * ves_icall_System_Threading_InternalThread_Thread_free_internal. + * This can happen only during shutdown. + * The shutting_down flag is not always set, so we can't assert on it. + */ + if (thread->synch_cs) + LOCK_THREAD (thread); + + thread->state |= ThreadState_Stopped; + thread->state &= ~ThreadState_Background; + + if (thread->synch_cs) + UNLOCK_THREAD (thread); + + /* + An interruption request has leaked to cleanup. Adjust the global counter. + + This can happen is the abort source thread finds the abortee (this) thread + in unmanaged code. If this thread never trips back to managed code or check + the local flag it will be left set and positively unbalance the global counter. + + Leaving the counter unbalanced will cause a performance degradation since all threads + will now keep checking their local flags all the time. + */ + mono_thread_clear_interruption_requested (thread); + + mono_threads_lock (); + + if (!threads) { + removed = FALSE; + } else if (mono_g_hash_table_lookup (threads, (gpointer)thread->tid) != thread) { + /* We have to check whether the thread object for the + * tid is still the same in the table because the + * thread might have been destroyed and the tid reused + * in the meantime, in which case the tid would be in + * the table, but with another thread object. + */ + removed = FALSE; + } else { + mono_g_hash_table_remove (threads, (gpointer)thread->tid); + removed = TRUE; + } + + mono_threads_unlock (); + + /* Don't close the handle here, wait for the object finalizer + * to do it. Otherwise, the following race condition applies: + * + * 1) Thread exits (and mono_thread_detach_internal() closes the handle) + * + * 2) Some other handle is reassigned the same slot + * + * 3) Another thread tries to join the first thread, and + * blocks waiting for the reassigned handle to be signalled + * (which might never happen). This is possible, because the + * thread calling Join() still has a reference to the first + * thread's object. + */ + + /* if the thread is not in the hash it has been removed already */ + if (!removed) { + mono_domain_unset (); + mono_memory_barrier (); + + if (mono_thread_cleanup_fn) + mono_thread_cleanup_fn (thread_get_tid (thread)); + + goto done; + } + + mono_release_type_locks (thread); + + MONO_PROFILER_RAISE (thread_stopped, (thread->tid)); + MONO_PROFILER_RAISE (gc_root_unregister, (info->stack_start_limit)); + MONO_PROFILER_RAISE (gc_root_unregister, (info->handle_stack)); + + /* + * This will signal async signal handlers that the thread has exited. + * The profiler callback needs this to be set, so it cannot be done earlier. + */ + mono_domain_unset (); + mono_memory_barrier (); + + if (thread == mono_thread_internal_current ()) + mono_thread_pop_appdomain_ref (); + + mono_free_static_data (thread->static_data); + thread->static_data = NULL; + ref_stack_destroy (thread->appdomain_refs); + thread->appdomain_refs = NULL; + + g_assert (thread->suspended); + mono_os_event_destroy (thread->suspended); + g_free (thread->suspended); + thread->suspended = NULL; + + if (mono_thread_cleanup_fn) + 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; + } + +done: + SET_CURRENT_OBJECT (NULL); + mono_domain_unset (); + + if (!mono_thread_info_try_get_internal_thread_gchandle (info, &gchandle)) + g_error ("%s: failed to get gchandle, info = %p", __func__, info); + + mono_gchandle_free (gchandle); + + mono_thread_info_unset_internal_thread_gchandle (info); + + MONO_PROFILER_RAISE (thread_exited, (thread->tid)); + + /* Don't need to close the handle to this thread, even though we took a + * reference in mono_thread_attach (), because the GC will do it + * when the Thread object is finalised. + */ +} + +typedef struct { + gint32 ref; + MonoThread *thread; + MonoObject *start_delegate; + MonoObject *start_delegate_arg; + MonoThreadStart start_func; + gpointer start_func_arg; + gboolean force_attach; + gboolean failed; + MonoCoopSem registered; +} StartInfo; + +static void +fire_attach_profiler_events (MonoNativeThreadId tid) +{ + MONO_PROFILER_RAISE (thread_started, ((uintptr_t) tid)); + + MonoThreadInfo *info = mono_thread_info_current (); + + MONO_PROFILER_RAISE (gc_root_register, ( + info->stack_start_limit, + (char *) info->stack_end - (char *) info->stack_start_limit, + MONO_ROOT_SOURCE_STACK, + (void *) tid, + "Thread Stack")); + + // The handle stack is a pseudo-root similar to the finalizer queues. + MONO_PROFILER_RAISE (gc_root_register, ( + info->handle_stack, + 1, + MONO_ROOT_SOURCE_HANDLE, + (void *) tid, + "Handle Stack")); +} + +static guint32 WINAPI start_wrapper_internal(StartInfo *start_info, gsize *stack_ptr) +{ + MonoError error; + MonoThreadStart start_func; + void *start_func_arg; + gsize tid; + /* + * We don't create a local to hold start_info->thread, so hopefully it won't get pinned during a + * GC stack walk. + */ + MonoThread *thread; + MonoInternalThread *internal; + MonoObject *start_delegate; + MonoObject *start_delegate_arg; + MonoDomain *domain; + + thread = start_info->thread; + internal = thread->internal_thread; + domain = mono_object_domain (start_info->thread); + + THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Start wrapper", __func__, mono_native_thread_id_get ())); + + if (!mono_thread_attach_internal (thread, start_info->force_attach, FALSE)) { + start_info->failed = TRUE; + + mono_coop_sem_post (&start_info->registered); + + if (mono_atomic_dec_i32 (&start_info->ref) == 0) { + mono_coop_sem_destroy (&start_info->registered); + g_free (start_info); + } + + return 0; + } + + mono_thread_internal_set_priority (internal, internal->priority); + + tid = internal->tid; + + start_delegate = start_info->start_delegate; + start_delegate_arg = start_info->start_delegate_arg; + start_func = start_info->start_func; + start_func_arg = start_info->start_func_arg; + + /* This MUST be called before any managed code can be + * executed, as it calls the callback function that (for the + * jit) sets the lmf marker. + */ + + if (mono_thread_start_cb) + mono_thread_start_cb (tid, stack_ptr, start_func); + + /* On 2.0 profile (and higher), set explicitly since state might have been + Unknown */ + if (internal->apartment_state == ThreadApartmentState_Unknown) + internal->apartment_state = ThreadApartmentState_MTA; + + mono_thread_init_apartment_state (); + + /* Let the thread that called Start() know we're ready */ + mono_coop_sem_post (&start_info->registered); + + if (mono_atomic_dec_i32 (&start_info->ref) == 0) { + mono_coop_sem_destroy (&start_info->registered); + g_free (start_info); + } + + /* start_info is not valid anymore */ + start_info = NULL; + + /* + * Call this after calling start_notify, since the profiler callback might want + * to lock the thread, and the lock is held by thread_start () which waits for + * start_notify. + */ + fire_attach_profiler_events ((MonoNativeThreadId) tid); + + /* if the name was set before starting, we didn't invoke the profiler callback */ + if (internal->name) { + char *tname = g_utf16_to_utf8 (internal->name, internal->name_len, NULL, NULL, NULL); + MONO_PROFILER_RAISE (thread_name, (internal->tid, tname)); + mono_native_thread_set_name (MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid), tname); + g_free (tname); + } + + /* start_func is set only for unmanaged start functions */ + if (start_func) { + start_func (start_func_arg); + } else { + void *args [1]; + + g_assert (start_delegate != NULL); + + /* we may want to handle the exception here. See comment below on unhandled exceptions */ + args [0] = (gpointer) start_delegate_arg; + mono_runtime_delegate_invoke_checked (start_delegate, args, &error); + + if (!mono_error_ok (&error)) { + MonoException *ex = mono_error_convert_to_exception (&error); + + g_assert (ex != NULL); + MonoClass *klass = mono_object_get_class (&ex->object); + if ((mono_runtime_unhandled_exception_policy_get () != MONO_UNHANDLED_POLICY_LEGACY) && + !is_threadabort_exception (klass)) { + mono_unhandled_exception (&ex->object); + mono_invoke_unhandled_exception_hook (&ex->object); + g_assert_not_reached (); + } + } else { + mono_error_cleanup (&error); + } + } + + /* If the thread calls ExitThread at all, this remaining code + * will not be executed, but the main thread will eventually + * call mono_thread_detach_internal() on this thread's behalf. + */ + + THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Start wrapper terminating", __func__, mono_native_thread_id_get ())); + + /* Do any cleanup needed for apartment state. This + * cannot be done in mono_thread_detach_internal since + * mono_thread_detach_internal could be called for a + * thread other than the current thread. + * mono_thread_cleanup_apartment_state cleans up apartment + * for the current thead */ + mono_thread_cleanup_apartment_state (); + + mono_thread_detach_internal (internal); + + return 0; +} + +static gsize WINAPI +start_wrapper (gpointer data) +{ + StartInfo *start_info; + MonoThreadInfo *info; + gsize res; + + start_info = (StartInfo*) data; + g_assert (start_info); + + info = mono_thread_info_attach (); + info->runtime_thread = TRUE; + + /* Run the actual main function of the thread */ + res = start_wrapper_internal (start_info, info->stack_end); + + mono_thread_info_exit (res); + + g_assert_not_reached (); +} + +/* + * create_thread: + * + * Common thread creation code. + * LOCKING: Acquires the threads lock. + */ +static gboolean +create_thread (MonoThread *thread, MonoInternalThread *internal, MonoObject *start_delegate, MonoThreadStart start_func, gpointer start_func_arg, + MonoThreadCreateFlags flags, MonoError *error) +{ + StartInfo *start_info = NULL; + MonoNativeThreadId tid; + gboolean ret; + gsize stack_set_size; + + if (start_delegate) + g_assert (!start_func && !start_func_arg); + if (start_func) + g_assert (!start_delegate); + + if (flags & MONO_THREAD_CREATE_FLAGS_THREADPOOL) { + g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_DEBUGGER)); + g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE)); + } + if (flags & MONO_THREAD_CREATE_FLAGS_DEBUGGER) { + g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_THREADPOOL)); + g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE)); + } + + /* + * Join joinable threads to prevent running out of threads since the finalizer + * thread might be blocked/backlogged. + */ + mono_threads_join_threads (); + + error_init (error); + + mono_threads_lock (); + if (shutting_down && !(flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE)) { + mono_threads_unlock (); + return FALSE; + } + if (threads_starting_up == NULL) { + threads_starting_up = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_VALUE_GC, MONO_ROOT_SOURCE_THREADING, NULL, "Thread Starting Table"); + } + mono_g_hash_table_insert (threads_starting_up, thread, thread); + mono_threads_unlock (); + + internal->threadpool_thread = flags & MONO_THREAD_CREATE_FLAGS_THREADPOOL; + if (internal->threadpool_thread) + mono_thread_set_state (internal, ThreadState_Background); + + internal->debugger_thread = flags & MONO_THREAD_CREATE_FLAGS_DEBUGGER; + + start_info = g_new0 (StartInfo, 1); + start_info->ref = 2; + start_info->thread = thread; + start_info->start_delegate = start_delegate; + start_info->start_delegate_arg = thread->start_obj; + start_info->start_func = start_func; + start_info->start_func_arg = start_func_arg; + start_info->force_attach = flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE; + start_info->failed = FALSE; + mono_coop_sem_init (&start_info->registered, 0); + + if (flags != MONO_THREAD_CREATE_FLAGS_SMALL_STACK) + stack_set_size = default_stacksize_for_thread (internal); + else + stack_set_size = 0; + + if (!mono_thread_platform_create_thread ((MonoThreadStart)start_wrapper, start_info, &stack_set_size, &tid)) { + /* The thread couldn't be created, so set an exception */ + mono_threads_lock (); + mono_g_hash_table_remove (threads_starting_up, thread); + mono_threads_unlock (); + mono_error_set_execution_engine (error, "Couldn't create thread. Error 0x%x", mono_w32error_get_last()); + /* ref is not going to be decremented in start_wrapper_internal */ + mono_atomic_dec_i32 (&start_info->ref); + ret = FALSE; + goto done; + } + + internal->stack_size = (int) stack_set_size; + + THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Launching thread %p (%"G_GSIZE_FORMAT")", __func__, mono_native_thread_id_get (), internal, (gsize)internal->tid)); + + /* + * Wait for the thread to set up its TLS data etc, so + * theres no potential race condition if someone tries + * to look up the data believing the thread has + * started + */ + + mono_coop_sem_wait (&start_info->registered, MONO_SEM_FLAGS_NONE); + + THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Done launching thread %p (%"G_GSIZE_FORMAT")", __func__, mono_native_thread_id_get (), internal, (gsize)internal->tid)); + + ret = !start_info->failed; + +done: + if (mono_atomic_dec_i32 (&start_info->ref) == 0) { + mono_coop_sem_destroy (&start_info->registered); + g_free (start_info); + } + + return ret; +} + +/** + * mono_thread_new_init: + */ +void +mono_thread_new_init (intptr_t tid, gpointer stack_start, gpointer func) +{ + if (mono_thread_start_cb) { + mono_thread_start_cb (tid, stack_start, func); + } +} + +/** + * mono_threads_set_default_stacksize: + */ +void +mono_threads_set_default_stacksize (guint32 stacksize) +{ + default_stacksize = stacksize; +} + +/** + * mono_threads_get_default_stacksize: + */ +guint32 +mono_threads_get_default_stacksize (void) +{ + return default_stacksize; +} + +/* + * mono_thread_create_internal: + * + * ARG should not be a GC reference. + */ +MonoInternalThread* +mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, MonoThreadCreateFlags flags, MonoError *error) +{ + MonoThread *thread; + MonoInternalThread *internal; + gboolean res; + + error_init (error); + + internal = create_internal_thread_object (); + + thread = create_thread_object (domain, internal); + + LOCK_THREAD (internal); + + res = create_thread (thread, internal, NULL, (MonoThreadStart) func, arg, flags, error); + + UNLOCK_THREAD (internal); + + return_val_if_nok (error, NULL); + return internal; +} + +/** + * mono_thread_create: + */ +void +mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg) +{ + MonoError error; + if (!mono_thread_create_checked (domain, func, arg, &error)) + mono_error_cleanup (&error); +} + +gboolean +mono_thread_create_checked (MonoDomain *domain, gpointer func, gpointer arg, MonoError *error) +{ + return (NULL != mono_thread_create_internal (domain, func, arg, MONO_THREAD_CREATE_FLAGS_NONE, error)); +} + +static MonoThread * +mono_thread_attach_full (MonoDomain *domain, gboolean force_attach) +{ + MonoInternalThread *internal; + MonoThread *thread; + MonoThreadInfo *info; + MonoNativeThreadId tid; + + if (mono_thread_internal_current_is_attached ()) { + if (domain != mono_domain_get ()) + mono_domain_set (domain, TRUE); + /* Already attached */ + return mono_thread_current (); + } + + info = mono_thread_info_attach (); + g_assert (info); + + tid=mono_native_thread_id_get (); + + internal = create_internal_thread_object (); + + thread = create_thread_object (domain, internal); + + if (!mono_thread_attach_internal (thread, force_attach, TRUE)) { + /* Mono is shutting down, so just wait for the end */ + for (;;) + mono_thread_info_sleep (10000, NULL); + } + + THREAD_DEBUG (g_message ("%s: Attached thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, tid, internal->handle)); + + if (mono_thread_attach_cb) + mono_thread_attach_cb (MONO_NATIVE_THREAD_ID_TO_UINT (tid), info->stack_end); + + fire_attach_profiler_events (tid); + + return thread; +} + +/** + * mono_thread_attach: + */ +MonoThread * +mono_thread_attach (MonoDomain *domain) +{ + return mono_thread_attach_full (domain, FALSE); +} + +/** + * mono_thread_detach: + */ +void +mono_thread_detach (MonoThread *thread) +{ + if (thread) + mono_thread_detach_internal (thread->internal_thread); +} + +/** + * mono_thread_detach_if_exiting: + * + * Detach the current thread from the runtime if it is exiting, i.e. it is running pthread dtors. + * This should be used at the end of embedding code which calls into managed code, and which + * can be called from pthread dtors, like dealloc: implementations in Objective-C. + */ +mono_bool +mono_thread_detach_if_exiting (void) +{ + if (mono_thread_info_is_exiting ()) { + MonoInternalThread *thread; + + thread = mono_thread_internal_current (); + if (thread) { + mono_thread_detach_internal (thread); + mono_thread_info_detach (); + return TRUE; + } + } + return FALSE; +} + +gboolean +mono_thread_internal_current_is_attached (void) +{ + MonoInternalThread *internal; + + internal = GET_CURRENT_OBJECT (); + if (!internal) + return FALSE; + + return TRUE; +} + +/** + * mono_thread_exit: + */ +void +mono_thread_exit (void) +{ + MonoInternalThread *thread = mono_thread_internal_current (); + + THREAD_DEBUG (g_message ("%s: mono_thread_exit for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->tid)); + + mono_thread_detach_internal (thread); + + /* we could add a callback here for embedders to use. */ + if (mono_thread_get_main () && (thread == mono_thread_get_main ()->internal_thread)) + exit (mono_environment_exitcode_get ()); + + mono_thread_info_exit (0); +} + +void +ves_icall_System_Threading_Thread_ConstructInternalThread (MonoThread *this_obj) +{ + MonoInternalThread *internal; + + internal = create_internal_thread_object (); + + 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 * +ves_icall_System_Threading_Thread_GetCurrentThread (void) +{ + return mono_thread_current (); +} + +HANDLE +ves_icall_System_Threading_Thread_Thread_internal (MonoThread *this_obj, + MonoObject *start) +{ + MonoError error; + MonoInternalThread *internal; + gboolean res; + + THREAD_DEBUG (g_message("%s: Trying to start a new thread: this (%p) start (%p)", __func__, this_obj, start)); + + if (!this_obj->internal_thread) + ves_icall_System_Threading_Thread_ConstructInternalThread (this_obj); + internal = this_obj->internal_thread; + + LOCK_THREAD (internal); + + if ((internal->state & ThreadState_Unstarted) == 0) { + UNLOCK_THREAD (internal); + mono_set_pending_exception (mono_get_exception_thread_state ("Thread has already been started.")); + return NULL; + } + + if ((internal->state & ThreadState_Aborted) != 0) { + UNLOCK_THREAD (internal); + return this_obj; + } + + res = create_thread (this_obj, internal, start, NULL, NULL, MONO_THREAD_CREATE_FLAGS_NONE, &error); + if (!res) { + mono_error_cleanup (&error); + UNLOCK_THREAD (internal); + return NULL; + } + + internal->state &= ~ThreadState_Unstarted; + + THREAD_DEBUG (g_message ("%s: Started thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, tid, thread)); + + UNLOCK_THREAD (internal); + return internal->handle; +} + +/* + * This is called from the finalizer of the internal thread object. + */ +void +ves_icall_System_Threading_InternalThread_Thread_free_internal (MonoInternalThread *this_obj) +{ + THREAD_DEBUG (g_message ("%s: Closing thread %p, handle %p", __func__, this, this_obj->handle)); + + /* + * Since threads keep a reference to their thread object while running, by + * the time this function is called, the thread has already exited/detached, + * i.e. mono_thread_detach_internal () has ran. The exception is during + * shutdown, when mono_thread_detach_internal () can be called after this. + */ + if (this_obj->handle) { + mono_threads_close_thread_handle (this_obj->handle); + this_obj->handle = NULL; + } + +#if HOST_WIN32 + CloseHandle (this_obj->native_handle); +#endif + + if (this_obj->synch_cs) { + MonoCoopMutex *synch_cs = this_obj->synch_cs; + this_obj->synch_cs = NULL; + mono_coop_mutex_destroy (synch_cs); + g_free (synch_cs); + } + + if (this_obj->name) { + void *name = this_obj->name; + this_obj->name = NULL; + g_free (name); + } +} + +void +ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms) +{ + guint32 res; + MonoInternalThread *thread = mono_thread_internal_current (); + + THREAD_DEBUG (g_message ("%s: Sleeping for %d ms", __func__, ms)); + + if (mono_thread_current_check_pending_interrupt ()) + return; + + while (TRUE) { + gboolean alerted = FALSE; + + mono_thread_set_state (thread, ThreadState_WaitSleepJoin); + + res = mono_thread_info_sleep (ms, &alerted); + + mono_thread_clr_state (thread, ThreadState_WaitSleepJoin); + + if (alerted) { + MonoException* exc = mono_thread_execute_interruption (); + if (exc) { + mono_set_pending_exception (exc); + return; + } else { + // FIXME: !MONO_INFINITE_WAIT + if (ms != MONO_INFINITE_WAIT) + break; + } + } else { + break; + } + } +} + +void ves_icall_System_Threading_Thread_SpinWait_nop (void) +{ +} + +gint32 +ves_icall_System_Threading_Thread_GetDomainID (void) +{ + return mono_domain_get()->domain_id; +} + +gboolean +ves_icall_System_Threading_Thread_Yield (void) +{ + return mono_thread_info_yield (); +} + +/* + * mono_thread_get_name: + * + * Return the name of the thread. NAME_LEN is set to the length of the name. + * Return NULL if the thread has no name. The returned memory is owned by the + * caller. + */ +gunichar2* +mono_thread_get_name (MonoInternalThread *this_obj, guint32 *name_len) +{ + gunichar2 *res; + + LOCK_THREAD (this_obj); + + if (!this_obj->name) { + *name_len = 0; + res = NULL; + } else { + *name_len = this_obj->name_len; + res = g_new (gunichar2, this_obj->name_len); + memcpy (res, this_obj->name, sizeof (gunichar2) * this_obj->name_len); + } + + UNLOCK_THREAD (this_obj); + + return res; +} + +/** + * mono_thread_get_name_utf8: + * \returns the name of the thread in UTF-8. + * Return NULL if the thread has no name. + * The returned memory is owned by the caller. + */ +char * +mono_thread_get_name_utf8 (MonoThread *thread) +{ + if (thread == NULL) + return NULL; + + MonoInternalThread *internal = thread->internal_thread; + if (internal == NULL) + return NULL; + + LOCK_THREAD (internal); + + char *tname = g_utf16_to_utf8 (internal->name, internal->name_len, NULL, NULL, NULL); + + UNLOCK_THREAD (internal); + + return tname; +} + +/** + * mono_thread_get_managed_id: + * \returns the \c Thread.ManagedThreadId value of \p thread. + * Returns \c -1 if \p thread is NULL. + */ +int32_t +mono_thread_get_managed_id (MonoThread *thread) +{ + if (thread == NULL) + return -1; + + MonoInternalThread *internal = thread->internal_thread; + if (internal == NULL) + return -1; + + int32_t id = internal->managed_id; + + return id; +} + +MonoString* +ves_icall_System_Threading_Thread_GetName_internal (MonoInternalThread *this_obj) +{ + MonoError error; + MonoString* str; + + error_init (&error); + + LOCK_THREAD (this_obj); + + if (!this_obj->name) + str = NULL; + else + str = mono_string_new_utf16_checked (mono_domain_get (), this_obj->name, this_obj->name_len, &error); + + UNLOCK_THREAD (this_obj); + + if (mono_error_set_pending_exception (&error)) + return NULL; + + return str; +} + +void +mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, gboolean permanent, gboolean reset, MonoError *error) +{ + MonoNativeThreadId tid = 0; + + LOCK_THREAD (this_obj); + + error_init (error); + + if (reset) { + this_obj->flags &= ~MONO_THREAD_FLAG_NAME_SET; + } else if (this_obj->flags & MONO_THREAD_FLAG_NAME_SET) { + UNLOCK_THREAD (this_obj); + + mono_error_set_invalid_operation (error, "Thread.Name can only be set once."); + return; + } + if (this_obj->name) { + g_free (this_obj->name); + this_obj->name_len = 0; + } + if (name) { + this_obj->name = g_memdup (mono_string_chars (name), mono_string_length (name) * sizeof (gunichar2)); + this_obj->name_len = mono_string_length (name); + + if (permanent) + this_obj->flags |= MONO_THREAD_FLAG_NAME_SET; + } + else + this_obj->name = NULL; + + if (!(this_obj->state & ThreadState_Stopped)) + tid = thread_get_tid (this_obj); + + UNLOCK_THREAD (this_obj); + + if (this_obj->name && tid) { + char *tname = mono_string_to_utf8_checked (name, error); + return_if_nok (error); + MONO_PROFILER_RAISE (thread_name, ((uintptr_t)tid, tname)); + mono_native_thread_set_name (tid, tname); + mono_free (tname); + } +} + +void +ves_icall_System_Threading_Thread_SetName_internal (MonoInternalThread *this_obj, MonoString *name) +{ + MonoError error; + mono_thread_set_name_internal (this_obj, name, TRUE, FALSE, &error); + mono_error_set_pending_exception (&error); +} + +/* + * ves_icall_System_Threading_Thread_GetPriority_internal: + * @param this_obj: The MonoInternalThread on which to operate. + * + * Gets the priority of the given thread. + * @return: The priority of the given thread. + */ +int +ves_icall_System_Threading_Thread_GetPriority (MonoThread *this_obj) +{ + gint32 priority; + MonoInternalThread *internal = this_obj->internal_thread; + + LOCK_THREAD (internal); + priority = internal->priority; + UNLOCK_THREAD (internal); + + return priority; +} + +/* + * ves_icall_System_Threading_Thread_SetPriority_internal: + * @param this_obj: The MonoInternalThread on which to operate. + * @param priority: The priority to set. + * + * Sets the priority of the given thread. + */ +void +ves_icall_System_Threading_Thread_SetPriority (MonoThread *this_obj, int priority) +{ + MonoInternalThread *internal = this_obj->internal_thread; + + LOCK_THREAD (internal); + internal->priority = priority; + if (internal->thread_info != NULL) + mono_thread_internal_set_priority (internal, priority); + UNLOCK_THREAD (internal); +} + +/* If the array is already in the requested domain, we just return it, + otherwise we return a copy in that domain. */ +static MonoArray* +byte_array_to_domain (MonoArray *arr, MonoDomain *domain, MonoError *error) +{ + MonoArray *copy; + + error_init (error); + if (!arr) + return NULL; + + if (mono_object_domain (arr) == domain) + return arr; + + copy = mono_array_new_checked (domain, mono_defaults.byte_class, arr->max_length, error); + memmove (mono_array_addr (copy, guint8, 0), mono_array_addr (arr, guint8, 0), arr->max_length); + return copy; +} + +MonoArray* +ves_icall_System_Threading_Thread_ByteArrayToRootDomain (MonoArray *arr) +{ + MonoError error; + MonoArray *result = byte_array_to_domain (arr, mono_get_root_domain (), &error); + mono_error_set_pending_exception (&error); + return result; +} + +MonoArray* +ves_icall_System_Threading_Thread_ByteArrayToCurrentDomain (MonoArray *arr) +{ + MonoError error; + MonoArray *result = byte_array_to_domain (arr, mono_domain_get (), &error); + mono_error_set_pending_exception (&error); + return result; +} + +/** + * mono_thread_current: + */ +MonoThread * +mono_thread_current (void) +{ + MonoDomain *domain = mono_domain_get (); + MonoInternalThread *internal = mono_thread_internal_current (); + MonoThread **current_thread_ptr; + + g_assert (internal); + current_thread_ptr = get_current_thread_ptr_for_domain (domain, 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; +} + +/* Return the thread object belonging to INTERNAL in the current domain */ +static MonoThread * +mono_thread_current_for_thread (MonoInternalThread *internal) +{ + MonoDomain *domain = mono_domain_get (); + MonoThread **current_thread_ptr; + + g_assert (internal); + current_thread_ptr = get_current_thread_ptr_for_domain (domain, 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; +} + +MonoInternalThread* +mono_thread_internal_current (void) +{ + MonoInternalThread *res = GET_CURRENT_OBJECT (); + THREAD_DEBUG (g_message ("%s: returning %p", __func__, res)); + return res; +} + +static MonoThreadInfoWaitRet +mono_join_uninterrupted (MonoThreadHandle* thread_to_join, gint32 ms, MonoError *error) +{ + MonoException *exc; + MonoThreadInfoWaitRet ret; + gint64 start; + gint32 diff_ms; + gint32 wait = ms; + + error_init (error); + + start = (ms == -1) ? 0 : mono_msec_ticks (); + for (;;) { + MONO_ENTER_GC_SAFE; + ret = mono_thread_info_wait_one_handle (thread_to_join, ms, TRUE); + MONO_EXIT_GC_SAFE; + + if (ret != MONO_THREAD_INFO_WAIT_RET_ALERTED) + return ret; + + exc = mono_thread_execute_interruption (); + if (exc) { + mono_error_set_exception_instance (error, exc); + return ret; + } + + if (ms == -1) + continue; + + /* Re-calculate ms according to the time passed */ + diff_ms = (gint32)(mono_msec_ticks () - start); + if (diff_ms >= ms) { + ret = MONO_THREAD_INFO_WAIT_RET_TIMEOUT; + return ret; + } + wait = ms - diff_ms; + } + + return ret; +} + +gboolean +ves_icall_System_Threading_Thread_Join_internal (MonoThread *this_obj, int ms) +{ + MonoInternalThread *thread = this_obj->internal_thread; + MonoThreadHandle *handle = thread->handle; + MonoInternalThread *cur_thread = mono_thread_internal_current (); + gboolean ret; + MonoError error; + + if (mono_thread_current_check_pending_interrupt ()) + return FALSE; + + LOCK_THREAD (thread); + + if ((thread->state & ThreadState_Unstarted) != 0) { + UNLOCK_THREAD (thread); + + mono_set_pending_exception (mono_get_exception_thread_state ("Thread has not been started.")); + return FALSE; + } + + UNLOCK_THREAD (thread); + + if (ms == -1) + ms = MONO_INFINITE_WAIT; + THREAD_DEBUG (g_message ("%s: joining thread handle %p, %d ms", __func__, handle, ms)); + + mono_thread_set_state (cur_thread, ThreadState_WaitSleepJoin); + + ret = mono_join_uninterrupted (handle, ms, &error); + + mono_thread_clr_state (cur_thread, ThreadState_WaitSleepJoin); + + mono_error_set_pending_exception (&error); + + if (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0) { + THREAD_DEBUG (g_message ("%s: join successful", __func__)); + + /* Wait for the thread to really exit */ + MonoNativeThreadId tid = thread_get_tid (thread); + mono_thread_join (tid); + + return TRUE; + } + + THREAD_DEBUG (g_message ("%s: join failed", __func__)); + + return FALSE; +} + +#define MANAGED_WAIT_FAILED 0x7fffffff + +static gint32 +map_native_wait_result_to_managed (MonoW32HandleWaitRet val, gsize numobjects) +{ + if (val >= MONO_W32HANDLE_WAIT_RET_SUCCESS_0 && val < MONO_W32HANDLE_WAIT_RET_SUCCESS_0 + numobjects) { + return WAIT_OBJECT_0 + (val - MONO_W32HANDLE_WAIT_RET_SUCCESS_0); + } else if (val >= MONO_W32HANDLE_WAIT_RET_ABANDONED_0 && val < MONO_W32HANDLE_WAIT_RET_ABANDONED_0 + numobjects) { + return WAIT_ABANDONED_0 + (val - MONO_W32HANDLE_WAIT_RET_ABANDONED_0); + } else if (val == MONO_W32HANDLE_WAIT_RET_ALERTED) { + return WAIT_IO_COMPLETION; + } else if (val == MONO_W32HANDLE_WAIT_RET_TIMEOUT) { + return WAIT_TIMEOUT; + } else if (val == MONO_W32HANDLE_WAIT_RET_FAILED) { + /* WAIT_FAILED in waithandle.cs is different from WAIT_FAILED in Win32 API */ + return MANAGED_WAIT_FAILED; + } else { + g_error ("%s: unknown val value %d", __func__, val); + } +} + +gint32 +ves_icall_System_Threading_WaitHandle_Wait_internal (gpointer *handles, gint32 numhandles, MonoBoolean waitall, gint32 timeout, MonoError *error) +{ + MonoW32HandleWaitRet ret; + MonoInternalThread *thread; + MonoException *exc; + gint64 start; + guint32 timeoutLeft; + + /* Do this WaitSleepJoin check before creating objects */ + if (mono_thread_current_check_pending_interrupt ()) + return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0); + + thread = mono_thread_internal_current (); + + mono_thread_set_state (thread, ThreadState_WaitSleepJoin); + + if (timeout == -1) + timeout = MONO_INFINITE_WAIT; + if (timeout != MONO_INFINITE_WAIT) + start = mono_msec_ticks (); + + timeoutLeft = timeout; + + for (;;) { +#ifdef HOST_WIN32 + MONO_ENTER_GC_SAFE; + if (numhandles != 1) + ret = mono_w32handle_convert_wait_ret (mono_win32_wait_for_multiple_objects_ex(numhandles, handles, waitall, timeoutLeft, TRUE), numhandles); + else + ret = mono_w32handle_convert_wait_ret (mono_win32_wait_for_single_object_ex (handles [0], timeoutLeft, TRUE), 1); + MONO_EXIT_GC_SAFE; +#else + /* mono_w32handle_wait_multiple optimizes the case for numhandles == 1 */ + ret = mono_w32handle_wait_multiple (handles, numhandles, waitall, timeoutLeft, TRUE); +#endif /* HOST_WIN32 */ + + if (ret != MONO_W32HANDLE_WAIT_RET_ALERTED) + break; + + exc = mono_thread_execute_interruption (); + if (exc) { + mono_error_set_exception_instance (error, exc); + break; + } + + if (timeout != MONO_INFINITE_WAIT) { + gint64 elapsed; + + elapsed = mono_msec_ticks () - start; + if (elapsed >= timeout) { + ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT; + break; + } + + timeoutLeft = timeout - elapsed; + } + } + + mono_thread_clr_state (thread, ThreadState_WaitSleepJoin); + + 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) +{ + MonoW32HandleWaitRet ret; + MonoInternalThread *thread = mono_thread_internal_current (); + + if (ms == -1) + ms = MONO_INFINITE_WAIT; + + if (mono_thread_current_check_pending_interrupt ()) + return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0); + + mono_thread_set_state (thread, ThreadState_WaitSleepJoin); + + MONO_ENTER_GC_SAFE; +#ifdef HOST_WIN32 + ret = mono_w32handle_convert_wait_ret (mono_win32_signal_object_and_wait (toSignal, toWait, ms, TRUE), 1); +#else + ret = mono_w32handle_signal_and_wait (toSignal, toWait, ms, TRUE); +#endif + MONO_EXIT_GC_SAFE; + + mono_thread_clr_state (thread, ThreadState_WaitSleepJoin); + + 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); +} + +gint64 ves_icall_System_Threading_Interlocked_Increment_Long (gint64 *location) +{ +#if SIZEOF_VOID_P == 4 + if (G_UNLIKELY ((size_t)location & 0x7)) { + gint64 ret; + mono_interlocked_lock (); + (*location)++; + ret = *location; + mono_interlocked_unlock (); + return ret; + } +#endif + return mono_atomic_inc_i64 (location); +} + +gint32 ves_icall_System_Threading_Interlocked_Decrement_Int (gint32 *location) +{ + return mono_atomic_dec_i32(location); +} + +gint64 ves_icall_System_Threading_Interlocked_Decrement_Long (gint64 * location) +{ +#if SIZEOF_VOID_P == 4 + if (G_UNLIKELY ((size_t)location & 0x7)) { + gint64 ret; + mono_interlocked_lock (); + (*location)--; + ret = *location; + mono_interlocked_unlock (); + return ret; + } +#endif + return mono_atomic_dec_i64 (location); +} + +gint32 ves_icall_System_Threading_Interlocked_Exchange_Int (gint32 *location, gint32 value) +{ + return mono_atomic_xchg_i32(location, value); +} + +MonoObject * ves_icall_System_Threading_Interlocked_Exchange_Object (MonoObject **location, MonoObject *value) +{ + MonoObject *res; + res = (MonoObject *) mono_atomic_xchg_ptr((gpointer *) location, value); + mono_gc_wbarrier_generic_nostore (location); + return res; +} + +gpointer ves_icall_System_Threading_Interlocked_Exchange_IntPtr (gpointer *location, gpointer value) +{ + return mono_atomic_xchg_ptr(location, value); +} + +gfloat ves_icall_System_Threading_Interlocked_Exchange_Single (gfloat *location, gfloat value) +{ + IntFloatUnion val, ret; + + val.fval = value; + ret.ival = mono_atomic_xchg_i32((gint32 *) location, val.ival); + + return ret.fval; +} + +gint64 +ves_icall_System_Threading_Interlocked_Exchange_Long (gint64 *location, gint64 value) +{ +#if SIZEOF_VOID_P == 4 + if (G_UNLIKELY ((size_t)location & 0x7)) { + gint64 ret; + mono_interlocked_lock (); + ret = *location; + *location = value; + mono_interlocked_unlock (); + return ret; + } +#endif + return mono_atomic_xchg_i64 (location, value); +} + +gdouble +ves_icall_System_Threading_Interlocked_Exchange_Double (gdouble *location, gdouble value) +{ + LongDoubleUnion val, ret; + + val.fval = value; + ret.ival = (gint64)mono_atomic_xchg_i64((gint64 *) location, val.ival); + + return ret.fval; +} + +gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *location, gint32 value, gint32 comparand) +{ + return mono_atomic_cas_i32(location, value, comparand); +} + +gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int_Success(gint32 *location, gint32 value, gint32 comparand, MonoBoolean *success) +{ + gint32 r = mono_atomic_cas_i32(location, value, comparand); + *success = r == comparand; + return r; +} + +MonoObject * ves_icall_System_Threading_Interlocked_CompareExchange_Object (MonoObject **location, MonoObject *value, MonoObject *comparand) +{ + MonoObject *res; + res = (MonoObject *) mono_atomic_cas_ptr((gpointer *) location, value, comparand); + mono_gc_wbarrier_generic_nostore (location); + return res; +} + +gpointer ves_icall_System_Threading_Interlocked_CompareExchange_IntPtr(gpointer *location, gpointer value, gpointer comparand) +{ + return mono_atomic_cas_ptr(location, value, comparand); +} + +gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single (gfloat *location, gfloat value, gfloat comparand) +{ + IntFloatUnion val, ret, cmp; + + val.fval = value; + cmp.fval = comparand; + ret.ival = mono_atomic_cas_i32((gint32 *) location, val.ival, cmp.ival); + + return ret.fval; +} + +gdouble +ves_icall_System_Threading_Interlocked_CompareExchange_Double (gdouble *location, gdouble value, gdouble comparand) +{ +#if SIZEOF_VOID_P == 8 + LongDoubleUnion val, comp, ret; + + val.fval = value; + comp.fval = comparand; + ret.ival = (gint64)mono_atomic_cas_ptr((gpointer *) location, (gpointer)val.ival, (gpointer)comp.ival); + + return ret.fval; +#else + gdouble old; + + mono_interlocked_lock (); + old = *location; + if (old == comparand) + *location = value; + mono_interlocked_unlock (); + + return old; +#endif +} + +gint64 +ves_icall_System_Threading_Interlocked_CompareExchange_Long (gint64 *location, gint64 value, gint64 comparand) +{ +#if SIZEOF_VOID_P == 4 + if (G_UNLIKELY ((size_t)location & 0x7)) { + gint64 old; + mono_interlocked_lock (); + old = *location; + if (old == comparand) + *location = value; + mono_interlocked_unlock (); + return old; + } +#endif + return mono_atomic_cas_i64 (location, value, comparand); +} + +MonoObject* +ves_icall_System_Threading_Interlocked_CompareExchange_T (MonoObject **location, MonoObject *value, MonoObject *comparand) +{ + MonoObject *res; + res = (MonoObject *)mono_atomic_cas_ptr ((volatile gpointer *)location, value, comparand); + mono_gc_wbarrier_generic_nostore (location); + return res; +} + +MonoObject* +ves_icall_System_Threading_Interlocked_Exchange_T (MonoObject **location, MonoObject *value) +{ + MonoObject *res; + MONO_CHECK_NULL (location, NULL); + res = (MonoObject *)mono_atomic_xchg_ptr ((volatile gpointer *)location, value); + mono_gc_wbarrier_generic_nostore (location); + return res; +} + +gint32 +ves_icall_System_Threading_Interlocked_Add_Int (gint32 *location, gint32 value) +{ + return mono_atomic_add_i32 (location, value); +} + +gint64 +ves_icall_System_Threading_Interlocked_Add_Long (gint64 *location, gint64 value) +{ +#if SIZEOF_VOID_P == 4 + if (G_UNLIKELY ((size_t)location & 0x7)) { + gint64 ret; + mono_interlocked_lock (); + *location += value; + ret = *location; + mono_interlocked_unlock (); + return ret; + } +#endif + return mono_atomic_add_i64 (location, value); +} + +gint64 +ves_icall_System_Threading_Interlocked_Read_Long (gint64 *location) +{ +#if SIZEOF_VOID_P == 4 + if (G_UNLIKELY ((size_t)location & 0x7)) { + gint64 ret; + mono_interlocked_lock (); + ret = *location; + mono_interlocked_unlock (); + return ret; + } +#endif + return mono_atomic_load_i64 (location); +} + +void +ves_icall_System_Threading_Thread_MemoryBarrier (void) +{ + mono_memory_barrier (); +} + +void +ves_icall_System_Threading_Thread_ClrState (MonoInternalThread* this_obj, guint32 state) +{ + mono_thread_clr_state (this_obj, (MonoThreadState)state); + + if (state & ThreadState_Background) { + /* If the thread changes the background mode, the main thread has to + * be notified, since it has to rebuild the list of threads to + * wait for. + */ + mono_os_event_set (&background_change_event); + } +} + +void +ves_icall_System_Threading_Thread_SetState (MonoInternalThread* this_obj, guint32 state) +{ + mono_thread_set_state (this_obj, (MonoThreadState)state); + + if (state & ThreadState_Background) { + /* If the thread changes the background mode, the main thread has to + * be notified, since it has to rebuild the list of threads to + * wait for. + */ + mono_os_event_set (&background_change_event); + } +} + +guint32 +ves_icall_System_Threading_Thread_GetState (MonoInternalThread* this_obj) +{ + guint32 state; + + LOCK_THREAD (this_obj); + + state = this_obj->state; + + UNLOCK_THREAD (this_obj); + + return state; +} + +void ves_icall_System_Threading_Thread_Interrupt_internal (MonoThread *this_obj) +{ + MonoInternalThread *current; + gboolean throw_; + MonoInternalThread *thread = this_obj->internal_thread; + + LOCK_THREAD (thread); + + current = mono_thread_internal_current (); + + thread->thread_interrupt_requested = TRUE; + throw_ = current != thread && (thread->state & ThreadState_WaitSleepJoin); + + UNLOCK_THREAD (thread); + + if (throw_) { + async_abort_internal (thread, FALSE); + } +} + +/** + * mono_thread_current_check_pending_interrupt: + * Checks if there's a interruption request and set the pending exception if so. + * \returns true if a pending exception was set + */ +gboolean +mono_thread_current_check_pending_interrupt (void) +{ + MonoInternalThread *thread = mono_thread_internal_current (); + gboolean throw_ = FALSE; + + LOCK_THREAD (thread); + + if (thread->thread_interrupt_requested) { + throw_ = TRUE; + thread->thread_interrupt_requested = FALSE; + } + + UNLOCK_THREAD (thread); + + if (throw_) + mono_set_pending_exception (mono_get_exception_thread_interrupted ()); + return throw_; +} + +static gboolean +request_thread_abort (MonoInternalThread *thread, MonoObject *state, gboolean appdomain_unload) +{ + LOCK_THREAD (thread); + + if (thread->state & (ThreadState_AbortRequested | ThreadState_Stopped)) + { + UNLOCK_THREAD (thread); + return FALSE; + } + + if ((thread->state & ThreadState_Unstarted) != 0) { + thread->state |= ThreadState_Aborted; + UNLOCK_THREAD (thread); + return FALSE; + } + + thread->state |= ThreadState_AbortRequested; + if (appdomain_unload) + thread->flags |= MONO_THREAD_FLAG_APPDOMAIN_ABORT; + else + thread->flags &= ~MONO_THREAD_FLAG_APPDOMAIN_ABORT; + + if (thread->abort_state_handle) + mono_gchandle_free (thread->abort_state_handle); + if (state) { + thread->abort_state_handle = mono_gchandle_new (state, FALSE); + g_assert (thread->abort_state_handle); + } else { + thread->abort_state_handle = 0; + } + thread->abort_exc = NULL; + + THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Abort requested for %p (%"G_GSIZE_FORMAT")", __func__, mono_native_thread_id_get (), thread, (gsize)thread->tid)); + + /* During shutdown, we can't wait for other threads */ + if (!shutting_down) + /* Make sure the thread is awake */ + mono_thread_resume (thread); + + UNLOCK_THREAD (thread); + return TRUE; +} + +void +ves_icall_System_Threading_Thread_Abort (MonoInternalThread *thread, MonoObject *state) +{ + if (!request_thread_abort (thread, state, FALSE)) + return; + + if (thread == mono_thread_internal_current ()) { + MonoError error; + self_abort_internal (&error); + mono_error_set_pending_exception (&error); + } else { + async_abort_internal (thread, TRUE); + } +} + +/** + * mono_thread_internal_abort: + * Request thread \p thread to be aborted. + * \p thread MUST NOT be the current thread. + */ +void +mono_thread_internal_abort (MonoInternalThread *thread, gboolean appdomain_unload) +{ + g_assert (thread != mono_thread_internal_current ()); + + if (!request_thread_abort (thread, NULL, appdomain_unload)) + return; + async_abort_internal (thread, TRUE); +} + +void +ves_icall_System_Threading_Thread_ResetAbort (MonoThread *this_obj) +{ + MonoInternalThread *thread = mono_thread_internal_current (); + gboolean was_aborting, is_domain_abort; + + LOCK_THREAD (thread); + was_aborting = thread->state & ThreadState_AbortRequested; + is_domain_abort = thread->flags & MONO_THREAD_FLAG_APPDOMAIN_ABORT; + + if (was_aborting && !is_domain_abort) + thread->state &= ~ThreadState_AbortRequested; + UNLOCK_THREAD (thread); + + if (!was_aborting) { + const char *msg = "Unable to reset abort because no abort was requested"; + mono_set_pending_exception (mono_get_exception_thread_state (msg)); + return; + } else if (is_domain_abort) { + /* Silently ignore abort resets in unloading appdomains */ + return; + } + + mono_get_eh_callbacks ()->mono_clear_abort_threshold (); + thread->abort_exc = NULL; + if (thread->abort_state_handle) { + mono_gchandle_free (thread->abort_state_handle); + /* This is actually not necessary - the handle + only counts if the exception is set */ + thread->abort_state_handle = 0; + } +} + +void +mono_thread_internal_reset_abort (MonoInternalThread *thread) +{ + LOCK_THREAD (thread); + + thread->state &= ~ThreadState_AbortRequested; + + if (thread->abort_exc) { + mono_get_eh_callbacks ()->mono_clear_abort_threshold (); + thread->abort_exc = NULL; + if (thread->abort_state_handle) { + mono_gchandle_free (thread->abort_state_handle); + /* This is actually not necessary - the handle + only counts if the exception is set */ + thread->abort_state_handle = 0; + } + } + + UNLOCK_THREAD (thread); +} + +MonoObject* +ves_icall_System_Threading_Thread_GetAbortExceptionState (MonoThread *this_obj) +{ + MonoError error; + MonoInternalThread *thread = this_obj->internal_thread; + MonoObject *state, *deserialized = NULL; + MonoDomain *domain; + + if (!thread->abort_state_handle) + return NULL; + + state = mono_gchandle_get_target (thread->abort_state_handle); + g_assert (state); + + domain = mono_domain_get (); + if (mono_object_domain (state) == domain) + return state; + + deserialized = mono_object_xdomain_representation (state, domain, &error); + + if (!deserialized) { + MonoException *invalid_op_exc = mono_get_exception_invalid_operation ("Thread.ExceptionState cannot access an ExceptionState from a different AppDomain"); + if (!is_ok (&error)) { + MonoObject *exc = (MonoObject*)mono_error_convert_to_exception (&error); + MONO_OBJECT_SETREF (invalid_op_exc, inner_ex, exc); + } + mono_set_pending_exception (invalid_op_exc); + return NULL; + } + + return deserialized; +} + +static gboolean +mono_thread_suspend (MonoInternalThread *thread) +{ + LOCK_THREAD (thread); + + if (thread->state & (ThreadState_Unstarted | ThreadState_Aborted | ThreadState_Stopped)) + { + UNLOCK_THREAD (thread); + return FALSE; + } + + if (thread->state & (ThreadState_Suspended | ThreadState_SuspendRequested | ThreadState_AbortRequested)) + { + UNLOCK_THREAD (thread); + return TRUE; + } + + thread->state |= ThreadState_SuspendRequested; + mono_os_event_reset (thread->suspended); + + if (thread == mono_thread_internal_current ()) { + /* calls UNLOCK_THREAD (thread) */ + self_suspend_internal (); + } else { + /* calls UNLOCK_THREAD (thread) */ + async_suspend_internal (thread, FALSE); + } + + return TRUE; +} + +void +ves_icall_System_Threading_Thread_Suspend (MonoThread *this_obj) +{ + if (!mono_thread_suspend (this_obj->internal_thread)) { + mono_set_pending_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead.")); + return; + } +} + +/* LOCKING: LOCK_THREAD(thread) must be held */ +static gboolean +mono_thread_resume (MonoInternalThread *thread) +{ + if ((thread->state & ThreadState_SuspendRequested) != 0) { + // MOSTLY_ASYNC_SAFE_PRINTF ("RESUME (1) thread %p\n", thread_get_tid (thread)); + thread->state &= ~ThreadState_SuspendRequested; + mono_os_event_set (thread->suspended); + return TRUE; + } + + if ((thread->state & ThreadState_Suspended) == 0 || + (thread->state & ThreadState_Unstarted) != 0 || + (thread->state & ThreadState_Aborted) != 0 || + (thread->state & ThreadState_Stopped) != 0) + { + // MOSTLY_ASYNC_SAFE_PRINTF ("RESUME (2) thread %p\n", thread_get_tid (thread)); + return FALSE; + } + + // MOSTLY_ASYNC_SAFE_PRINTF ("RESUME (3) thread %p\n", thread_get_tid (thread)); + + mono_os_event_set (thread->suspended); + + if (!thread->self_suspended) { + UNLOCK_THREAD (thread); + + /* Awake the thread */ + if (!mono_thread_info_resume (thread_get_tid (thread))) + return FALSE; + + LOCK_THREAD (thread); + } + + thread->state &= ~ThreadState_Suspended; + + return TRUE; +} + +void +ves_icall_System_Threading_Thread_Resume (MonoThread *thread) +{ + if (!thread->internal_thread) { + mono_set_pending_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead.")); + } else { + LOCK_THREAD (thread->internal_thread); + if (!mono_thread_resume (thread->internal_thread)) + mono_set_pending_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead.")); + UNLOCK_THREAD (thread->internal_thread); + } +} + +static gboolean +mono_threads_is_critical_method (MonoMethod *method) +{ + switch (method->wrapper_type) { + case MONO_WRAPPER_RUNTIME_INVOKE: + case MONO_WRAPPER_XDOMAIN_INVOKE: + case MONO_WRAPPER_XDOMAIN_DISPATCH: + return TRUE; + } + return FALSE; +} + +static gboolean +find_wrapper (MonoMethod *m, gint no, gint ilo, gboolean managed, gpointer data) +{ + if (managed) + return TRUE; + + if (mono_threads_is_critical_method (m)) { + *((gboolean*)data) = TRUE; + return TRUE; + } + return FALSE; +} + +static gboolean +is_running_protected_wrapper (void) +{ + gboolean found = FALSE; + mono_stack_walk (find_wrapper, &found); + return found; +} + +/** + * mono_thread_stop: + */ +void +mono_thread_stop (MonoThread *thread) +{ + MonoInternalThread *internal = thread->internal_thread; + + if (!request_thread_abort (internal, NULL, FALSE)) + return; + + if (internal == mono_thread_internal_current ()) { + MonoError error; + self_abort_internal (&error); + /* + This function is part of the embeding API and has no way to return the exception + to be thrown. So what we do is keep the old behavior and raise the exception. + */ + mono_error_raise_exception_deprecated (&error); /* OK to throw, see note */ + } else { + async_abort_internal (internal, TRUE); + } +} + +gint8 +ves_icall_System_Threading_Thread_VolatileRead1 (void *ptr) +{ + gint8 tmp = *(volatile gint8 *)ptr; + mono_memory_barrier (); + return tmp; +} + +gint16 +ves_icall_System_Threading_Thread_VolatileRead2 (void *ptr) +{ + gint16 tmp = *(volatile gint16 *)ptr; + mono_memory_barrier (); + return tmp; +} + +gint32 +ves_icall_System_Threading_Thread_VolatileRead4 (void *ptr) +{ + gint32 tmp = *(volatile gint32 *)ptr; + mono_memory_barrier (); + return tmp; +} + +gint64 +ves_icall_System_Threading_Thread_VolatileRead8 (void *ptr) +{ + gint64 tmp = *(volatile gint64 *)ptr; + mono_memory_barrier (); + return tmp; +} + +void * +ves_icall_System_Threading_Thread_VolatileReadIntPtr (void *ptr) +{ + volatile void *tmp = *(volatile void **)ptr; + mono_memory_barrier (); + return (void *) tmp; +} + +void * +ves_icall_System_Threading_Thread_VolatileReadObject (void *ptr) +{ + volatile MonoObject *tmp = *(volatile MonoObject **)ptr; + mono_memory_barrier (); + return (MonoObject *) tmp; +} + +double +ves_icall_System_Threading_Thread_VolatileReadDouble (void *ptr) +{ + double tmp = *(volatile double *)ptr; + mono_memory_barrier (); + return tmp; +} + +float +ves_icall_System_Threading_Thread_VolatileReadFloat (void *ptr) +{ + float tmp = *(volatile float *)ptr; + mono_memory_barrier (); + return tmp; +} + +gint8 +ves_icall_System_Threading_Volatile_Read1 (void *ptr) +{ + return mono_atomic_load_i8 ((volatile gint8 *)ptr); +} + +gint16 +ves_icall_System_Threading_Volatile_Read2 (void *ptr) +{ + return mono_atomic_load_i16 ((volatile gint16 *)ptr); +} + +gint32 +ves_icall_System_Threading_Volatile_Read4 (void *ptr) +{ + return mono_atomic_load_i32 ((volatile gint32 *)ptr); +} + +gint64 +ves_icall_System_Threading_Volatile_Read8 (void *ptr) +{ +#if SIZEOF_VOID_P == 4 + if (G_UNLIKELY ((size_t)ptr & 0x7)) { + gint64 val; + mono_interlocked_lock (); + val = *(gint64*)ptr; + mono_interlocked_unlock (); + return val; + } +#endif + return mono_atomic_load_i64 ((volatile gint64 *)ptr); +} + +void * +ves_icall_System_Threading_Volatile_ReadIntPtr (void *ptr) +{ + return mono_atomic_load_ptr ((volatile gpointer *)ptr); +} + +double +ves_icall_System_Threading_Volatile_ReadDouble (void *ptr) +{ + LongDoubleUnion u; + +#if SIZEOF_VOID_P == 4 + if (G_UNLIKELY ((size_t)ptr & 0x7)) { + double val; + mono_interlocked_lock (); + val = *(double*)ptr; + mono_interlocked_unlock (); + return val; + } +#endif + + u.ival = mono_atomic_load_i64 ((volatile gint64 *)ptr); + + return u.fval; +} + +float +ves_icall_System_Threading_Volatile_ReadFloat (void *ptr) +{ + IntFloatUnion u; + + u.ival = mono_atomic_load_i32 ((volatile gint32 *)ptr); + + return u.fval; +} + +MonoObject* +ves_icall_System_Threading_Volatile_Read_T (void *ptr) +{ + return (MonoObject *)mono_atomic_load_ptr ((volatile gpointer *)ptr); +} + +void +ves_icall_System_Threading_Thread_VolatileWrite1 (void *ptr, gint8 value) +{ + mono_memory_barrier (); + *(volatile gint8 *)ptr = value; +} + +void +ves_icall_System_Threading_Thread_VolatileWrite2 (void *ptr, gint16 value) +{ + mono_memory_barrier (); + *(volatile gint16 *)ptr = value; +} + +void +ves_icall_System_Threading_Thread_VolatileWrite4 (void *ptr, gint32 value) +{ + mono_memory_barrier (); + *(volatile gint32 *)ptr = value; +} + +void +ves_icall_System_Threading_Thread_VolatileWrite8 (void *ptr, gint64 value) +{ + mono_memory_barrier (); + *(volatile gint64 *)ptr = value; +} + +void +ves_icall_System_Threading_Thread_VolatileWriteIntPtr (void *ptr, void *value) +{ + mono_memory_barrier (); + *(volatile void **)ptr = value; +} + +void +ves_icall_System_Threading_Thread_VolatileWriteObject (void *ptr, MonoObject *value) +{ + mono_memory_barrier (); + mono_gc_wbarrier_generic_store (ptr, value); +} + +void +ves_icall_System_Threading_Thread_VolatileWriteDouble (void *ptr, double value) +{ + mono_memory_barrier (); + *(volatile double *)ptr = value; +} + +void +ves_icall_System_Threading_Thread_VolatileWriteFloat (void *ptr, float value) +{ + mono_memory_barrier (); + *(volatile float *)ptr = value; +} + +void +ves_icall_System_Threading_Volatile_Write1 (void *ptr, gint8 value) +{ + mono_atomic_store_i8 ((volatile gint8 *)ptr, value); +} + +void +ves_icall_System_Threading_Volatile_Write2 (void *ptr, gint16 value) +{ + mono_atomic_store_i16 ((volatile gint16 *)ptr, value); +} + +void +ves_icall_System_Threading_Volatile_Write4 (void *ptr, gint32 value) +{ + mono_atomic_store_i32 ((volatile gint32 *)ptr, value); +} + +void +ves_icall_System_Threading_Volatile_Write8 (void *ptr, gint64 value) +{ +#if SIZEOF_VOID_P == 4 + if (G_UNLIKELY ((size_t)ptr & 0x7)) { + mono_interlocked_lock (); + *(gint64*)ptr = value; + mono_interlocked_unlock (); + return; + } +#endif + + mono_atomic_store_i64 ((volatile gint64 *)ptr, value); +} + +void +ves_icall_System_Threading_Volatile_WriteIntPtr (void *ptr, void *value) +{ + mono_atomic_store_ptr ((volatile gpointer *)ptr, value); +} + +void +ves_icall_System_Threading_Volatile_WriteDouble (void *ptr, double value) +{ + LongDoubleUnion u; + +#if SIZEOF_VOID_P == 4 + if (G_UNLIKELY ((size_t)ptr & 0x7)) { + mono_interlocked_lock (); + *(double*)ptr = value; + mono_interlocked_unlock (); + return; + } +#endif + + u.fval = value; + + mono_atomic_store_i64 ((volatile gint64 *)ptr, u.ival); +} + +void +ves_icall_System_Threading_Volatile_WriteFloat (void *ptr, float value) +{ + IntFloatUnion u; + + u.fval = value; + + mono_atomic_store_i32 ((volatile gint32 *)ptr, u.ival); +} + +void +ves_icall_System_Threading_Volatile_Write_T (void *ptr, MonoObject *value) +{ + mono_gc_wbarrier_generic_store_atomic (ptr, value); +} + +static void +free_context (void *user_data) +{ + ContextStaticData *data = user_data; + + mono_threads_lock (); + + /* + * There is no guarantee that, by the point this reference queue callback + * has been invoked, the GC handle associated with the object will fail to + * resolve as one might expect. So if we don't free and remove the GC + * handle here, free_context_static_data_helper () could end up resolving + * a GC handle to an actually-dead context which would contain a pointer + * to an already-freed static data segment, resulting in a crash when + * accessing it. + */ + g_hash_table_remove (contexts, GUINT_TO_POINTER (data->gc_handle)); + + mono_threads_unlock (); + + mono_gchandle_free (data->gc_handle); + mono_free_static_data (data->static_data); + g_free (data); +} + +void +mono_threads_register_app_context (MonoAppContext *ctx, MonoError *error) +{ + error_init (error); + mono_threads_lock (); + + //g_print ("Registering context %d in domain %d\n", ctx->context_id, ctx->domain_id); + + if (!contexts) + contexts = g_hash_table_new (NULL, NULL); + + if (!context_queue) + context_queue = mono_gc_reference_queue_new (free_context); + + gpointer gch = GUINT_TO_POINTER (mono_gchandle_new_weakref (&ctx->obj, FALSE)); + g_hash_table_insert (contexts, gch, gch); + + /* + * We use this intermediate structure to contain a duplicate pointer to + * the static data because we can't rely on being able to resolve the GC + * handle in the reference queue callback. + */ + ContextStaticData *data = g_new0 (ContextStaticData, 1); + data->gc_handle = GPOINTER_TO_UINT (gch); + ctx->data = data; + + context_adjust_static_data (ctx); + mono_gc_reference_queue_add (context_queue, &ctx->obj, data); + + mono_threads_unlock (); + + MONO_PROFILER_RAISE (context_loaded, (ctx)); +} + +void +ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext (MonoAppContextHandle ctx, MonoError *error) +{ + error_init (error); + mono_threads_register_app_context (MONO_HANDLE_RAW (ctx), error); /* FIXME use handles in mono_threads_register_app_context */ +} + +void +mono_threads_release_app_context (MonoAppContext* ctx, MonoError *error) +{ + /* + * NOTE: Since finalizers are unreliable for the purposes of ensuring + * cleanup in exceptional circumstances, we don't actually do any + * cleanup work here. We instead do this via a reference queue. + */ + + //g_print ("Releasing context %d in domain %d\n", ctx->context_id, ctx->domain_id); + + MONO_PROFILER_RAISE (context_unloaded, (ctx)); +} + +void +ves_icall_System_Runtime_Remoting_Contexts_Context_ReleaseContext (MonoAppContextHandle ctx, MonoError *error) +{ + error_init (error); + mono_threads_release_app_context (MONO_HANDLE_RAW (ctx), error); /* FIXME use handles in mono_threads_release_app_context */ +} + +void mono_thread_init (MonoThreadStartCB start_cb, + MonoThreadAttachCB attach_cb) +{ + mono_coop_mutex_init_recursive (&threads_mutex); + + mono_os_mutex_init_recursive(&interlocked_mutex); + mono_coop_mutex_init_recursive(&joinable_threads_mutex); + + mono_os_event_init (&background_change_event, FALSE); + + mono_coop_cond_init (&pending_native_thread_join_calls_event); + mono_coop_cond_init (&zero_pending_joinable_thread_event); + + mono_init_static_data_info (&thread_static_info); + mono_init_static_data_info (&context_static_info); + + mono_thread_start_cb = start_cb; + mono_thread_attach_cb = attach_cb; +} + +static gpointer +thread_attach (MonoThreadInfo *info) +{ + return mono_gc_thread_attach (info); +} + +static void +thread_detach (MonoThreadInfo *info) +{ + MonoInternalThread *internal; + guint32 gchandle; + + /* If a delegate is passed to native code and invoked on a thread we dont + * know about, marshal will register it with mono_threads_attach_coop, but + * we have no way of knowing when that thread goes away. SGen has a TSD + * so we assume that if the domain is still registered, we can detach + * the thread */ + + 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_thread_detach_internal (internal); +} + +static void +thread_detach_with_lock (MonoThreadInfo *info) +{ + mono_gc_thread_detach_with_lock (info); +} + +static gboolean +thread_in_critical_region (MonoThreadInfo *info) +{ + return mono_gc_thread_in_critical_region (info); +} + +static gboolean +ip_in_critical_region (MonoDomain *domain, gpointer ip) +{ + MonoJitInfo *ji; + MonoMethod *method; + + /* + * We pass false for 'try_aot' so this becomes async safe. + * It won't find aot methods whose jit info is not yet loaded, + * so we preload their jit info in the JIT. + */ + ji = mono_jit_info_table_find_internal (domain, ip, FALSE, FALSE); + if (!ji) + return FALSE; + + method = mono_jit_info_get_method (ji); + g_assert (method); + + return mono_gc_is_critical_method (method); +} + +void +mono_thread_callbacks_init (void) +{ + MonoThreadInfoCallbacks cb; + + memset (&cb, 0, sizeof(cb)); + cb.thread_attach = thread_attach; + cb.thread_detach = thread_detach; + cb.thread_detach_with_lock = thread_detach_with_lock; + cb.ip_in_critical_region = ip_in_critical_region; + cb.thread_in_critical_region = thread_in_critical_region; + mono_thread_info_callbacks_init (&cb); +} + +/** + * mono_thread_cleanup: + */ +void +mono_thread_cleanup (void) +{ + /* Wait for pending threads to park on joinable threads list */ + /* NOTE, waiting on this should be extremely rare and will only happen */ + /* under certain specific conditions. */ + gboolean wait_result = threads_wait_pending_joinable_threads (2000); + if (!wait_result) + g_warning ("Waiting on threads to park on joinable thread list timed out."); + + mono_threads_join_threads (); + +#if !defined(RUN_IN_SUBTHREAD) && !defined(HOST_WIN32) + /* The main thread must abandon any held mutexes (particularly + * important for named mutexes as they are shared across + * processes, see bug 74680.) This will happen when the + * thread exits, but if it's not running in a subthread it + * won't exit in time. + */ + mono_w32mutex_abandon (); +#endif + +#if 0 + /* This stuff needs more testing, it seems one of these + * critical sections can be locked when mono_thread_cleanup is + * called. + */ + mono_coop_mutex_destroy (&threads_mutex); + mono_os_mutex_destroy (&interlocked_mutex); + mono_os_mutex_destroy (&delayed_free_table_mutex); + mono_os_mutex_destroy (&small_id_mutex); + mono_coop_cond_destroy (&zero_pending_joinable_thread_event); + mono_coop_cond_destroy (&pending_native_thread_join_calls_event); + mono_os_event_destroy (&background_change_event); +#endif +} + +void +mono_threads_install_cleanup (MonoThreadCleanupFunc func) +{ + mono_thread_cleanup_fn = func; +} + +/** + * mono_thread_set_manage_callback: + */ +void +mono_thread_set_manage_callback (MonoThread *thread, MonoThreadManageCallback func) +{ + thread->internal_thread->manage_callback = func; +} + +G_GNUC_UNUSED +static void print_tids (gpointer key, gpointer value, gpointer user) +{ + /* GPOINTER_TO_UINT breaks horribly if sizeof(void *) > + * sizeof(uint) and a cast to uint would overflow + */ + /* Older versions of glib don't have G_GSIZE_FORMAT, so just + * print this as a pointer. + */ + g_message ("Waiting for: %p", key); +} + +struct wait_data +{ + MonoThreadHandle *handles[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS]; + MonoInternalThread *threads[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS]; + guint32 num; +}; + +static void +wait_for_tids (struct wait_data *wait, guint32 timeout, gboolean check_state_change) +{ + guint32 i; + MonoThreadInfoWaitRet ret; + + THREAD_DEBUG (g_message("%s: %d threads to wait for in this batch", __func__, wait->num)); + + /* Add the thread state change event, so it wakes + * up if a thread changes to background mode. */ + + MONO_ENTER_GC_SAFE; + if (check_state_change) + ret = mono_thread_info_wait_multiple_handle (wait->handles, wait->num, &background_change_event, FALSE, timeout, TRUE); + else + ret = mono_thread_info_wait_multiple_handle (wait->handles, wait->num, NULL, TRUE, timeout, TRUE); + MONO_EXIT_GC_SAFE; + + if (ret == MONO_THREAD_INFO_WAIT_RET_FAILED) { + /* See the comment in build_wait_tids() */ + THREAD_DEBUG (g_message ("%s: Wait failed", __func__)); + return; + } + + for( i = 0; i < wait->num; i++) + mono_threads_close_thread_handle (wait->handles [i]); + + if (ret == MONO_THREAD_INFO_WAIT_RET_TIMEOUT) + return; + + if (ret < wait->num) { + MonoInternalThread *internal; + + internal = wait->threads [ret]; + + mono_threads_lock (); + if (mono_g_hash_table_lookup (threads, (gpointer) internal->tid) == internal) + g_error ("%s: failed to call mono_thread_detach_internal on thread %p, InternalThread: %p", __func__, internal->tid, internal); + mono_threads_unlock (); + } +} + +static void build_wait_tids (gpointer key, gpointer value, gpointer user) +{ + struct wait_data *wait=(struct wait_data *)user; + + if(wait->numstate & ThreadState_Background || unity_shutting_down) { + THREAD_DEBUG (g_message ("%s: ignoring background thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid)); + return; /* just leave, ignore */ + } + + if (mono_gc_is_finalizer_internal_thread (thread)) { + THREAD_DEBUG (g_message ("%s: ignoring finalizer thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid)); + return; + } + + if (thread == mono_thread_internal_current ()) { + THREAD_DEBUG (g_message ("%s: ignoring current thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid)); + return; + } + + if (mono_thread_get_main () && (thread == mono_thread_get_main ()->internal_thread)) { + THREAD_DEBUG (g_message ("%s: ignoring main thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid)); + return; + } + + if (thread->flags & MONO_THREAD_FLAG_DONT_MANAGE) { + THREAD_DEBUG (g_message ("%s: ignoring thread %" G_GSIZE_FORMAT "with DONT_MANAGE flag set.", __func__, (gsize)thread->tid)); + return; + } + + THREAD_DEBUG (g_message ("%s: Invoking mono_thread_manage callback on thread %p", __func__, thread)); + if ((thread->manage_callback == NULL) || (thread->manage_callback (thread->root_domain_thread) == TRUE)) { + wait->handles[wait->num]=mono_threads_open_thread_handle (thread->handle); + wait->threads[wait->num]=thread; + wait->num++; + + THREAD_DEBUG (g_message ("%s: adding thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid)); + } else { + THREAD_DEBUG (g_message ("%s: ignoring (because of callback) thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid)); + } + + + } else { + /* Just ignore the rest, we can't do anything with + * them yet + */ + } +} + +static void +abort_threads (gpointer key, gpointer value, gpointer user) +{ + struct wait_data *wait=(struct wait_data *)user; + MonoNativeThreadId self = mono_native_thread_id_get (); + MonoInternalThread *thread = (MonoInternalThread *)value; + + if (wait->num >= MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) + return; + + if (mono_native_thread_id_equals (thread_get_tid (thread), self)) + return; + if (mono_gc_is_finalizer_internal_thread (thread)) + return; + + if ((thread->flags & MONO_THREAD_FLAG_DONT_MANAGE)) + return; + + wait->handles[wait->num] = mono_threads_open_thread_handle (thread->handle); + wait->threads[wait->num] = thread; + wait->num++; + + THREAD_DEBUG (g_print ("%s: Aborting id: %"G_GSIZE_FORMAT"\n", __func__, (gsize)thread->tid)); + mono_thread_internal_abort (thread, FALSE); +} + +/** + * mono_threads_set_shutting_down: + * + * Is called by a thread that wants to shut down Mono. If the runtime is already + * shutting down, the calling thread is suspended/stopped, and this function never + * returns. + */ +void +mono_threads_set_shutting_down (void) +{ + MonoInternalThread *current_thread = mono_thread_internal_current (); + + mono_threads_lock (); + + if (shutting_down) { + mono_threads_unlock (); + + /* Make sure we're properly suspended/stopped */ + + LOCK_THREAD (current_thread); + + if (current_thread->state & (ThreadState_SuspendRequested | ThreadState_AbortRequested)) { + UNLOCK_THREAD (current_thread); + mono_thread_execute_interruption (); + } else { + UNLOCK_THREAD (current_thread); + } + + /*since we're killing the thread, detach it.*/ + mono_thread_detach_internal (current_thread); + + /* Wake up other threads potentially waiting for us */ + mono_thread_info_exit (0); + } else { + shutting_down = TRUE; + + /* Not really a background state change, but this will + * interrupt the main thread if it is waiting for all + * the other threads. + */ + mono_os_event_set (&background_change_event); + + mono_threads_unlock (); + } +} + +/** + * mono_thread_manage: + */ +void +mono_thread_manage (void) +{ + struct wait_data wait_data; + struct wait_data *wait = &wait_data; + + memset (wait, 0, sizeof (struct wait_data)); + /* join each thread that's still running */ + THREAD_DEBUG (g_message ("%s: Joining each running thread...", __func__)); + + mono_threads_lock (); + if(threads==NULL) { + THREAD_DEBUG (g_message("%s: No threads", __func__)); + mono_threads_unlock (); + return; + } + + mono_threads_unlock (); + + do { + mono_threads_lock (); + if (shutting_down) { + /* somebody else is shutting down */ + mono_threads_unlock (); + break; + } + THREAD_DEBUG (g_message ("%s: There are %d threads to join", __func__, mono_g_hash_table_size (threads)); + mono_g_hash_table_foreach (threads, print_tids, NULL)); + + mono_os_event_reset (&background_change_event); + wait->num=0; + /* We must zero all InternalThread pointers to avoid making the GC unhappy. */ + memset (wait->threads, 0, MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS * SIZEOF_VOID_P); + mono_g_hash_table_foreach (threads, build_wait_tids, wait); + mono_threads_unlock (); + if (wait->num > 0) + /* Something to wait for */ + wait_for_tids (wait, MONO_INFINITE_WAIT, TRUE); + THREAD_DEBUG (g_message ("%s: I have %d threads after waiting.", __func__, wait->num)); + } while(wait->num>0); + + /* Mono is shutting down, so just wait for the end */ + if (!mono_runtime_try_shutdown ()) { + /*FIXME mono_thread_suspend probably should call mono_thread_execute_interruption when self interrupting. */ + mono_thread_suspend (mono_thread_internal_current ()); + mono_thread_execute_interruption (); + } + + /* + * Remove everything but the finalizer thread and self. + * Also abort all the background threads + * */ + do { + mono_threads_lock (); + + wait->num = 0; + /*We must zero all InternalThread pointers to avoid making the GC unhappy.*/ + memset (wait->threads, 0, MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS * SIZEOF_VOID_P); + mono_g_hash_table_foreach (threads, abort_threads, wait); + + mono_threads_unlock (); + + THREAD_DEBUG (g_message ("%s: wait->num is now %d", __func__, wait->num)); + if (wait->num > 0) { + /* Something to wait for */ + wait_for_tids (wait, MONO_INFINITE_WAIT, FALSE); + } + } while (wait->num > 0); + + /* + * give the subthreads a chance to really quit (this is mainly needed + * to get correct user and system times from getrusage/wait/time(1)). + * This could be removed if we avoid pthread_detach() and use pthread_join(). + */ + mono_thread_info_yield (); +} + +static void +collect_threads_for_suspend (gpointer key, gpointer value, gpointer user_data) +{ + MonoInternalThread *thread = (MonoInternalThread*)value; + struct wait_data *wait = (struct wait_data*)user_data; + + /* + * We try to exclude threads early, to avoid running into the MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS + * limitation. + * This needs no locking. + */ + if ((thread->state & ThreadState_Suspended) != 0 || + (thread->state & ThreadState_Stopped) != 0) + return; + + if (wait->numhandles [wait->num] = mono_threads_open_thread_handle (thread->handle); + wait->threads [wait->num] = thread; + wait->num++; + } +} + +/* + * mono_thread_suspend_all_other_threads: + * + * Suspend all managed threads except the finalizer thread and this thread. It is + * not possible to resume them later. + */ +void mono_thread_suspend_all_other_threads (void) +{ + struct wait_data wait_data; + struct wait_data *wait = &wait_data; + int i; + MonoNativeThreadId self = mono_native_thread_id_get (); + guint32 eventidx = 0; + gboolean starting, finished; + + memset (wait, 0, sizeof (struct wait_data)); + /* + * The other threads could be in an arbitrary state at this point, i.e. + * they could be starting up, shutting down etc. This means that there could be + * threads which are not even in the threads hash table yet. + */ + + /* + * First we set a barrier which will be checked by all threads before they + * are added to the threads hash table, and they will exit if the flag is set. + * This ensures that no threads could be added to the hash later. + * We will use shutting_down as the barrier for now. + */ + g_assert (shutting_down); + + /* + * We make multiple calls to WaitForMultipleObjects since: + * - we can only wait for MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS threads + * - some threads could exit without becoming suspended + */ + finished = FALSE; + while (!finished) { + /* + * Make a copy of the hashtable since we can't do anything with + * threads while threads_mutex is held. + */ + wait->num = 0; + /*We must zero all InternalThread pointers to avoid making the GC unhappy.*/ + memset (wait->threads, 0, MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS * SIZEOF_VOID_P); + mono_threads_lock (); + mono_g_hash_table_foreach (threads, collect_threads_for_suspend, wait); + mono_threads_unlock (); + + eventidx = 0; + /* Get the suspended events that we'll be waiting for */ + for (i = 0; i < wait->num; ++i) { + MonoInternalThread *thread = wait->threads [i]; + + if (mono_native_thread_id_equals (thread_get_tid (thread), self) + || mono_gc_is_finalizer_internal_thread (thread) + || (thread->flags & MONO_THREAD_FLAG_DONT_MANAGE) + ) { + mono_threads_close_thread_handle (wait->handles [i]); + wait->threads [i] = NULL; + continue; + } + + LOCK_THREAD (thread); + + if (thread->state & (ThreadState_Suspended | ThreadState_Stopped)) { + UNLOCK_THREAD (thread); + mono_threads_close_thread_handle (wait->handles [i]); + wait->threads [i] = NULL; + continue; + } + + ++eventidx; + + /* Convert abort requests into suspend requests */ + if ((thread->state & ThreadState_AbortRequested) != 0) + thread->state &= ~ThreadState_AbortRequested; + + thread->state |= ThreadState_SuspendRequested; + mono_os_event_reset (thread->suspended); + + /* Signal the thread to suspend + calls UNLOCK_THREAD (thread) */ + async_suspend_internal (thread, TRUE); + + mono_threads_close_thread_handle (wait->handles [i]); + wait->threads [i] = NULL; + } + if (eventidx <= 0) { + /* + * If there are threads which are starting up, we wait until they + * are suspended when they try to register in the threads hash. + * This is guaranteed to finish, since the threads which can create new + * threads get suspended after a while. + * FIXME: The finalizer thread can still create new threads. + */ + mono_threads_lock (); + if (threads_starting_up) + starting = mono_g_hash_table_size (threads_starting_up) > 0; + else + starting = FALSE; + mono_threads_unlock (); + if (starting) + mono_thread_info_sleep (100, NULL); + else + finished = TRUE; + } + } +} + +typedef struct { + MonoInternalThread *thread; + MonoStackFrameInfo *frames; + int nframes, max_frames; + int nthreads, max_threads; + MonoInternalThread **threads; +} ThreadDumpUserData; + +static gboolean thread_dump_requested; + +/* This needs to be async safe */ +static gboolean +collect_frame (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data) +{ + ThreadDumpUserData *ud = (ThreadDumpUserData *)data; + + if (ud->nframes < ud->max_frames) { + memcpy (&ud->frames [ud->nframes], frame, sizeof (MonoStackFrameInfo)); + ud->nframes ++; + } + + return FALSE; +} + +/* This needs to be async safe */ +static SuspendThreadResult +get_thread_dump (MonoThreadInfo *info, gpointer ud) +{ + ThreadDumpUserData *user_data = (ThreadDumpUserData *)ud; + MonoInternalThread *thread = user_data->thread; + +#if 0 +/* This no longer works with remote unwinding */ + g_string_append_printf (text, " tid=0x%p this=0x%p ", (gpointer)(gsize)thread->tid, thread); + mono_thread_internal_describe (thread, text); + g_string_append (text, "\n"); +#endif + + if (thread == mono_thread_internal_current ()) + mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (collect_frame, NULL, MONO_UNWIND_SIGNAL_SAFE, ud); + else + mono_get_eh_callbacks ()->mono_walk_stack_with_state (collect_frame, mono_thread_info_get_suspend_state (info), MONO_UNWIND_SIGNAL_SAFE, 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; +} CollectThreadsUserData; + +static void +collect_thread (gpointer key, gpointer value, gpointer user) +{ + CollectThreadsUserData *ud = (CollectThreadsUserData *)user; + MonoInternalThread *thread = (MonoInternalThread *)value; + + if (ud->nthreads < ud->max_threads) + ud->threads [ud->nthreads ++] = thread; +} + +/* + * Collect running threads into the THREADS array. + * THREADS should be an array allocated on the stack. + */ +static int +collect_threads (MonoInternalThread **thread_array, int max_threads) +{ + CollectThreadsUserData ud; + + memset (&ud, 0, sizeof (ud)); + /* This array contains refs, but its on the stack, so its ok */ + ud.threads = thread_array; + ud.max_threads = max_threads; + + mono_threads_lock (); + mono_g_hash_table_foreach (threads, collect_thread, &ud); + mono_threads_unlock (); + + return ud.nthreads; +} + +static void +dump_thread (MonoInternalThread *thread, ThreadDumpUserData *ud) +{ + GString* text = g_string_new (0); + char *name; + GError *error = NULL; + int i; + + ud->thread = thread; + ud->nframes = 0; + + /* Collect frames for the thread */ + if (thread == mono_thread_internal_current ()) { + get_thread_dump (mono_thread_info_current (), ud); + } else { + mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), FALSE, get_thread_dump, ud); + } + + /* + * Do all the non async-safe work outside of get_thread_dump. + */ + if (thread->name) { + name = g_utf16_to_utf8 (thread->name, thread->name_len, NULL, NULL, &error); + g_assert (!error); + g_string_append_printf (text, "\n\"%s\"", name); + g_free (name); + } + else if (thread->threadpool_thread) { + g_string_append (text, "\n\"\""); + } else { + g_string_append (text, "\n\"\""); + } + + for (i = 0; i < ud->nframes; ++i) { + MonoStackFrameInfo *frame = &ud->frames [i]; + MonoMethod *method = NULL; + + if (frame->type == FRAME_TYPE_MANAGED) + method = mono_jit_info_get_method (frame->ji); + + if (method) { + gchar *location = mono_debug_print_stack_frame (method, frame->native_offset, frame->domain); + g_string_append_printf (text, " %s\n", location); + g_free (location); + } else { + g_string_append_printf (text, " at <0x%05x>\n", frame->native_offset); + } + } + + fprintf (stdout, "%s", text->str); + +#if PLATFORM_WIN32 && TARGET_WIN32 && _DEBUG + OutputDebugStringA(text->str); +#endif + + g_string_free (text, TRUE); + fflush (stdout); +} + +void +mono_threads_perform_thread_dump (void) +{ + ThreadDumpUserData ud; + MonoInternalThread *thread_array [128]; + int tindex, nthreads; + + if (!thread_dump_requested) + return; + + printf ("Full thread dump:\n"); + + /* Make a copy of the threads hash to avoid doing work inside threads_lock () */ + nthreads = collect_threads (thread_array, 128); + + memset (&ud, 0, sizeof (ud)); + ud.frames = g_new0 (MonoStackFrameInfo, 256); + ud.max_frames = 256; + + for (tindex = 0; tindex < nthreads; ++tindex) + dump_thread (thread_array [tindex], &ud); + + g_free (ud.frames); + + thread_dump_requested = FALSE; +} + +/* Obtain the thread dump of all threads */ +static gboolean +mono_threads_get_thread_dump (MonoArray **out_threads, MonoArray **out_stack_frames, MonoError *error) +{ + + ThreadDumpUserData ud; + MonoInternalThread *thread_array [128]; + MonoDomain *domain = mono_domain_get (); + MonoDebugSourceLocation *location; + int tindex, nthreads; + + error_init (error); + + *out_threads = NULL; + *out_stack_frames = NULL; + + /* Make a copy of the threads hash to avoid doing work inside threads_lock () */ + nthreads = collect_threads (thread_array, 128); + + memset (&ud, 0, sizeof (ud)); + ud.frames = g_new0 (MonoStackFrameInfo, 256); + ud.max_frames = 256; + + *out_threads = mono_array_new_checked (domain, mono_defaults.thread_class, nthreads, error); + goto_if_nok (error, leave); + *out_stack_frames = mono_array_new_checked (domain, mono_defaults.array_class, nthreads, error); + goto_if_nok (error, leave); + + for (tindex = 0; tindex < nthreads; ++tindex) { + MonoInternalThread *thread = thread_array [tindex]; + MonoArray *thread_frames; + int i; + + ud.thread = thread; + ud.nframes = 0; + + /* Collect frames for the thread */ + if (thread == mono_thread_internal_current ()) { + get_thread_dump (mono_thread_info_current (), &ud); + } else { + mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), FALSE, get_thread_dump, &ud); + } + + mono_array_setref_fast (*out_threads, tindex, mono_thread_current_for_thread (thread)); + + thread_frames = mono_array_new_checked (domain, mono_defaults.stack_frame_class, ud.nframes, error); + goto_if_nok (error, leave); + mono_array_setref_fast (*out_stack_frames, tindex, thread_frames); + + for (i = 0; i < ud.nframes; ++i) { + MonoStackFrameInfo *frame = &ud.frames [i]; + MonoMethod *method = NULL; + MonoStackFrame *sf = (MonoStackFrame *)mono_object_new_checked (domain, mono_defaults.stack_frame_class, error); + goto_if_nok (error, leave); + + sf->native_offset = frame->native_offset; + + if (frame->type == FRAME_TYPE_MANAGED) + method = mono_jit_info_get_method (frame->ji); + + if (method) { + sf->method_address = (gsize) frame->ji->code_start; + + MonoReflectionMethod *rm = mono_method_get_object_checked (domain, method, NULL, error); + goto_if_nok (error, leave); + MONO_OBJECT_SETREF (sf, method, rm); + + location = mono_debug_lookup_source_location (method, frame->native_offset, domain); + if (location) { + sf->il_offset = location->il_offset; + + if (location && location->source_file) { + MonoString *filename = mono_string_new_checked (domain, location->source_file, error); + goto_if_nok (error, leave); + MONO_OBJECT_SETREF (sf, filename, filename); + sf->line = location->row; + sf->column = location->column; + } + mono_debug_free_source_location (location); + } else { + sf->il_offset = -1; + } + } + mono_array_setref (thread_frames, i, sf); + } + } + +leave: + g_free (ud.frames); + return is_ok (error); +} + +/** + * mono_threads_request_thread_dump: + * + * Ask all threads except the current to print their stacktrace to stdout. + */ +void +mono_threads_request_thread_dump (void) +{ + /*The new thread dump code runs out of the finalizer thread. */ + thread_dump_requested = TRUE; + mono_gc_finalize_notify (); +} + +struct ref_stack { + gpointer *refs; + gint allocated; /* +1 so that refs [allocated] == NULL */ + gint bottom; +}; + +typedef struct ref_stack RefStack; + +static RefStack * +ref_stack_new (gint initial_size) +{ + RefStack *rs; + + initial_size = MAX (initial_size, 16) + 1; + rs = g_new0 (RefStack, 1); + rs->refs = g_new0 (gpointer, initial_size); + rs->allocated = initial_size; + return rs; +} + +static void +ref_stack_destroy (gpointer ptr) +{ + RefStack *rs = (RefStack *)ptr; + + if (rs != NULL) { + g_free (rs->refs); + g_free (rs); + } +} + +static void +ref_stack_push (RefStack *rs, gpointer ptr) +{ + g_assert (rs != NULL); + + if (rs->bottom >= rs->allocated) { + rs->refs = (void **)g_realloc (rs->refs, rs->allocated * 2 * sizeof (gpointer) + 1); + rs->allocated <<= 1; + rs->refs [rs->allocated] = NULL; + } + rs->refs [rs->bottom++] = ptr; +} + +static void +ref_stack_pop (RefStack *rs) +{ + if (rs == NULL || rs->bottom == 0) + return; + + rs->bottom--; + rs->refs [rs->bottom] = NULL; +} + +static gboolean +ref_stack_find (RefStack *rs, gpointer ptr) +{ + gpointer *refs; + + if (rs == NULL) + return FALSE; + + for (refs = rs->refs; refs && *refs; refs++) { + if (*refs == ptr) + return TRUE; + } + return FALSE; +} + +/* + * mono_thread_push_appdomain_ref: + * + * Register that the current thread may have references to objects in domain + * @domain on its stack. Each call to this function should be paired with a + * call to pop_appdomain_ref. + */ +void +mono_thread_push_appdomain_ref (MonoDomain *domain) +{ + MonoInternalThread *thread = mono_thread_internal_current (); + + if (thread) { + /* printf ("PUSH REF: %"G_GSIZE_FORMAT" -> %s.\n", (gsize)thread->tid, domain->friendly_name); */ + SPIN_LOCK (thread->lock_thread_id); + if (thread->appdomain_refs == NULL) + thread->appdomain_refs = ref_stack_new (16); + ref_stack_push ((RefStack *)thread->appdomain_refs, domain); + SPIN_UNLOCK (thread->lock_thread_id); + } +} + +void +mono_thread_pop_appdomain_ref (void) +{ + MonoInternalThread *thread = mono_thread_internal_current (); + + if (thread) { + /* printf ("POP REF: %"G_GSIZE_FORMAT" -> %s.\n", (gsize)thread->tid, ((MonoDomain*)(thread->appdomain_refs->data))->friendly_name); */ + SPIN_LOCK (thread->lock_thread_id); + ref_stack_pop ((RefStack *)thread->appdomain_refs); + SPIN_UNLOCK (thread->lock_thread_id); + } +} + +gboolean +mono_thread_internal_has_appdomain_ref (MonoInternalThread *thread, MonoDomain *domain) +{ + gboolean res; + SPIN_LOCK (thread->lock_thread_id); + res = ref_stack_find ((RefStack *)thread->appdomain_refs, domain); + SPIN_UNLOCK (thread->lock_thread_id); + return res; +} + +gboolean +mono_thread_has_appdomain_ref (MonoThread *thread, MonoDomain *domain) +{ + return mono_thread_internal_has_appdomain_ref (thread->internal_thread, domain); +} + +typedef struct abort_appdomain_data { + struct wait_data wait; + MonoDomain *domain; +} abort_appdomain_data; + +static void +collect_appdomain_thread (gpointer key, gpointer value, gpointer user_data) +{ + MonoInternalThread *thread = (MonoInternalThread*)value; + abort_appdomain_data *data = (abort_appdomain_data*)user_data; + MonoDomain *domain = data->domain; + + if (mono_thread_internal_has_appdomain_ref (thread, domain)) { + /* printf ("ABORTING THREAD %p BECAUSE IT REFERENCES DOMAIN %s.\n", thread->tid, domain->friendly_name); */ + + if(data->wait.numwait.handles [data->wait.num] = mono_threads_open_thread_handle (thread->handle); + data->wait.threads [data->wait.num] = thread; + data->wait.num++; + } else { + /* Just ignore the rest, we can't do anything with + * them yet + */ + } + } +} + +/* + * mono_threads_abort_appdomain_threads: + * + * Abort threads which has references to the given appdomain. + */ +gboolean +mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout) +{ + abort_appdomain_data user_data; + gint64 start_time; + int orig_timeout = timeout; + int i; + + THREAD_DEBUG (g_message ("%s: starting abort", __func__)); + + start_time = mono_msec_ticks (); + do { + mono_threads_lock (); + + user_data.domain = domain; + user_data.wait.num = 0; + /* This shouldn't take any locks */ + mono_g_hash_table_foreach (threads, collect_appdomain_thread, &user_data); + mono_threads_unlock (); + + if (user_data.wait.num > 0) { + /* Abort the threads outside the threads lock */ + for (i = 0; i < user_data.wait.num; ++i) + mono_thread_internal_abort (user_data.wait.threads [i], TRUE); + + /* + * We should wait for the threads either to abort, or to leave the + * domain. We can't do the latter, so we wait with a timeout. + */ + wait_for_tids (&user_data.wait, 100, FALSE); + } + + /* Update remaining time */ + timeout -= mono_msec_ticks () - start_time; + start_time = mono_msec_ticks (); + + if (orig_timeout != -1 && timeout < 0) + return FALSE; + } + while (user_data.wait.num > 0); + + THREAD_DEBUG (g_message ("%s: abort done", __func__)); + + return TRUE; +} + +void +mono_thread_self_abort (void) +{ + MonoError error; + self_abort_internal (&error); + mono_error_set_pending_exception (&error); +} + +/* + * mono_thread_get_undeniable_exception: + * + * Return an exception which needs to be raised when leaving a catch clause. + * This is used for undeniable exception propagation. + */ +MonoException* +mono_thread_get_undeniable_exception (void) +{ + MonoInternalThread *thread = mono_thread_internal_current (); + + if (!(thread && thread->abort_exc && !is_running_protected_wrapper ())) + return NULL; + + // We don't want to have our exception effect calls made by + // the catching block + + if (!mono_get_eh_callbacks ()->mono_above_abort_threshold ()) + return NULL; + + /* + * FIXME: Clear the abort exception and return an AppDomainUnloaded + * exception if the thread no longer references a dying appdomain. + */ + thread->abort_exc->trace_ips = NULL; + thread->abort_exc->stack_trace = NULL; + return thread->abort_exc; +} + +#if MONO_SMALL_CONFIG +#define NUM_STATIC_DATA_IDX 4 +static const int static_data_size [NUM_STATIC_DATA_IDX] = { + 64, 256, 1024, 4096 +}; +#else +#define NUM_STATIC_DATA_IDX 8 +static const int static_data_size [NUM_STATIC_DATA_IDX] = { + 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216 +}; +#endif + +static MonoBitSet *thread_reference_bitmaps [NUM_STATIC_DATA_IDX]; +static MonoBitSet *context_reference_bitmaps [NUM_STATIC_DATA_IDX]; + +static void +mark_slots (void *addr, MonoBitSet **bitmaps, MonoGCMarkFunc mark_func, void *gc_data) +{ + gpointer *static_data = (gpointer *)addr; + + for (int i = 0; i < NUM_STATIC_DATA_IDX; ++i) { + void **ptr = (void **)static_data [i]; + + if (!ptr) + continue; + + MONO_BITSET_FOREACH (bitmaps [i], idx, { + void **p = ptr + idx; + + if (*p) + mark_func ((MonoObject**)p, gc_data); + }); + } +} + +static void +mark_tls_slots (void *addr, MonoGCMarkFunc mark_func, void *gc_data) +{ + mark_slots (addr, thread_reference_bitmaps, mark_func, gc_data); +} + +static void +mark_ctx_slots (void *addr, MonoGCMarkFunc mark_func, void *gc_data) +{ + mark_slots (addr, context_reference_bitmaps, mark_func, gc_data); +} + +/* + * mono_alloc_static_data + * + * Allocate memory blocks for storing threads or context static data + */ +static void +mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, void *alloc_key, gboolean threadlocal) +{ + guint idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index); + int i; + + gpointer* static_data = *static_data_ptr; + if (!static_data) { + static MonoGCDescriptor tls_desc = MONO_GC_DESCRIPTOR_NULL; + static MonoGCDescriptor ctx_desc = MONO_GC_DESCRIPTOR_NULL; + + if (mono_gc_user_markers_supported ()) { + if (tls_desc == MONO_GC_DESCRIPTOR_NULL) + tls_desc = mono_gc_make_root_descr_user (mark_tls_slots); + + if (ctx_desc == MONO_GC_DESCRIPTOR_NULL) + ctx_desc = mono_gc_make_root_descr_user (mark_ctx_slots); + } + + static_data = (void **)mono_gc_alloc_fixed (static_data_size [0], threadlocal ? tls_desc : ctx_desc, + threadlocal ? MONO_ROOT_SOURCE_THREAD_STATIC : MONO_ROOT_SOURCE_CONTEXT_STATIC, + alloc_key, + threadlocal ? "ThreadStatic Fields" : "ContextStatic Fields"); + *static_data_ptr = static_data; + static_data [0] = static_data; + } + + for (i = 1; i <= idx; ++i) { + if (static_data [i]) + continue; + + 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); + } + } +} + +static void +mono_free_static_data (gpointer* static_data) +{ + int i; + for (i = 1; i < NUM_STATIC_DATA_IDX; ++i) { + gpointer p = static_data [i]; + if (!p) + continue; + /* + * At this point, the static data pointer array is still registered with the + * GC, so must ensure that mark_tls_slots() will not encounter any invalid + * data. Freeing the individual arrays without first nulling their slots + * would make it possible for mark_tls/ctx_slots() to encounter a pointer to + * such an already freed array. See bug #13813. + */ + static_data [i] = NULL; + mono_memory_write_barrier (); + if (mono_gc_user_markers_supported ()) + g_free (p); + else + mono_gc_free_fixed (p); + } + mono_gc_free_fixed (static_data); +} + +/* + * mono_init_static_data_info + * + * Initializes static data counters + */ +static void mono_init_static_data_info (StaticDataInfo *static_data) +{ + static_data->idx = 0; + static_data->offset = 0; + static_data->freelist = NULL; +} + +/* + * mono_alloc_static_data_slot + * + * Generates an offset for static data. static_data contains the counters + * used to generate it. + */ +static guint32 +mono_alloc_static_data_slot (StaticDataInfo *static_data, guint32 size, guint32 align) +{ + if (!static_data->idx && !static_data->offset) { + /* + * we use the first chunk of the first allocation also as + * an array for the rest of the data + */ + static_data->offset = sizeof (gpointer) * NUM_STATIC_DATA_IDX; + } + static_data->offset += align - 1; + static_data->offset &= ~(align - 1); + if (static_data->offset + size >= static_data_size [static_data->idx]) { + static_data->idx ++; + g_assert (size <= static_data_size [static_data->idx]); + g_assert (static_data->idx < NUM_STATIC_DATA_IDX); + static_data->offset = 0; + } + guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (static_data->idx, static_data->offset, 0); + static_data->offset += size; + return offset; +} + +/* + * LOCKING: requires that threads_mutex is held + */ +static void +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; + } +} + +/* + * LOCKING: requires that threads_mutex is held + */ +static void +alloc_thread_static_data_helper (gpointer key, gpointer value, gpointer user) +{ + MonoInternalThread *thread = (MonoInternalThread *)value; + guint32 offset = GPOINTER_TO_UINT (user); + + mono_alloc_static_data (&(thread->static_data), offset, (void *) MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid), TRUE); +} + +/* + * LOCKING: requires that threads_mutex is held + */ +static void +alloc_context_static_data_helper (gpointer key, gpointer value, gpointer user) +{ + MonoAppContext *ctx = (MonoAppContext *) mono_gchandle_get_target (GPOINTER_TO_INT (key)); + + if (!ctx) + return; + + 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; +} + +static StaticDataFreeList* +search_slot_in_freelist (StaticDataInfo *static_data, guint32 size, gint32 align) +{ + StaticDataFreeList* prev = NULL; + StaticDataFreeList* tmp = static_data->freelist; + while (tmp) { + if (tmp->size == size && tmp->align == align) { + if (prev) + prev->next = tmp->next; + else + static_data->freelist = tmp->next; + return tmp; + } + prev = tmp; + tmp = tmp->next; + } + return NULL; +} + +#if SIZEOF_VOID_P == 4 +#define ONE_P 1 +#else +#define ONE_P 1ll +#endif + +static void +update_reference_bitmap (MonoBitSet **sets, guint32 offset, uintptr_t *bitmap, int numbits) +{ + int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index); + if (!sets [idx]) + sets [idx] = mono_bitset_new (static_data_size [idx] / sizeof (uintptr_t), 0); + MonoBitSet *rb = sets [idx]; + offset = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset); + offset /= sizeof (uintptr_t); + /* offset is now the bitmap offset */ + for (int i = 0; i < numbits; ++i) { + if (bitmap [i / sizeof (uintptr_t)] & (ONE_P << (i & (sizeof (uintptr_t) * 8 -1)))) + mono_bitset_set_fast (rb, offset + i); + } +} + +static void +clear_reference_bitmap (MonoBitSet **sets, guint32 offset, guint32 size) +{ + int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index); + MonoBitSet *rb = sets [idx]; + offset = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset); + offset /= sizeof (uintptr_t); + /* offset is now the bitmap offset */ + for (int i = 0; i < size / sizeof (uintptr_t); i++) + mono_bitset_clear_fast (rb, offset + i); +} + +guint32 +mono_alloc_special_static_data (guint32 static_type, guint32 size, guint32 align, uintptr_t *bitmap, int numbits) +{ + g_assert (static_type == SPECIAL_STATIC_THREAD || static_type == SPECIAL_STATIC_CONTEXT); + + StaticDataInfo *info; + MonoBitSet **sets; + + if (static_type == SPECIAL_STATIC_THREAD) { + info = &thread_static_info; + sets = thread_reference_bitmaps; + } else { + info = &context_static_info; + sets = context_reference_bitmaps; + } + + mono_threads_lock (); + + StaticDataFreeList *item = search_slot_in_freelist (info, size, align); + guint32 offset; + + if (item) { + offset = item->offset; + g_free (item); + } else { + offset = mono_alloc_static_data_slot (info, size, align); + } + + update_reference_bitmap (sets, offset, bitmap, numbits); + + if (static_type == SPECIAL_STATIC_THREAD) { + /* This can be called during startup */ + if (threads != NULL) + mono_g_hash_table_foreach (threads, alloc_thread_static_data_helper, GUINT_TO_POINTER (offset)); + } else { + if (contexts != NULL) + g_hash_table_foreach (contexts, alloc_context_static_data_helper, GUINT_TO_POINTER (offset)); + + ACCESS_SPECIAL_STATIC_OFFSET (offset, type) = SPECIAL_STATIC_OFFSET_TYPE_CONTEXT; + } + + mono_threads_unlock (); + + return offset; +} + +gpointer +mono_get_special_static_data_for_thread (MonoInternalThread *thread, guint32 offset) +{ + guint32 static_type = ACCESS_SPECIAL_STATIC_OFFSET (offset, type); + + if (static_type == SPECIAL_STATIC_OFFSET_TYPE_THREAD) { + return get_thread_static_data (thread, offset); + } else { + return get_context_static_data (thread->current_appcontext, offset); + } +} + +gpointer +mono_get_special_static_data (guint32 offset) +{ + return mono_get_special_static_data_for_thread (mono_thread_internal_current (), offset); +} + +typedef struct { + guint32 offset; + guint32 size; +} OffsetSize; + +/* + * LOCKING: requires that threads_mutex is held + */ +static void +free_thread_static_data_helper (gpointer key, gpointer value, gpointer user) +{ + MonoInternalThread *thread = (MonoInternalThread *)value; + OffsetSize *data = (OffsetSize *)user; + int idx = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, index); + int off = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, offset); + char *ptr; + + if (!thread->static_data || !thread->static_data [idx]) + return; + ptr = ((char*) thread->static_data [idx]) + off; + mono_gc_bzero_atomic (ptr, data->size); +} + +/* + * LOCKING: requires that threads_mutex is held + */ +static void +free_context_static_data_helper (gpointer key, gpointer value, gpointer user) +{ + MonoAppContext *ctx = (MonoAppContext *) mono_gchandle_get_target (GPOINTER_TO_INT (key)); + + if (!ctx) + return; + + OffsetSize *data = (OffsetSize *)user; + int idx = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, index); + int off = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, offset); + char *ptr; + + if (!ctx->static_data || !ctx->static_data [idx]) + return; + + ptr = ((char*) ctx->static_data [idx]) + off; + mono_gc_bzero_atomic (ptr, data->size); +} + +static void +do_free_special_slot (guint32 offset, guint32 size, gint32 align) +{ + guint32 static_type = ACCESS_SPECIAL_STATIC_OFFSET (offset, type); + MonoBitSet **sets; + StaticDataInfo *info; + + if (static_type == SPECIAL_STATIC_OFFSET_TYPE_THREAD) { + info = &thread_static_info; + sets = thread_reference_bitmaps; + } else { + info = &context_static_info; + sets = context_reference_bitmaps; + } + + guint32 data_offset = offset; + ACCESS_SPECIAL_STATIC_OFFSET (data_offset, type) = 0; + OffsetSize data = { data_offset, size }; + + clear_reference_bitmap (sets, data.offset, data.size); + + if (static_type == SPECIAL_STATIC_OFFSET_TYPE_THREAD) { + if (threads != NULL) + mono_g_hash_table_foreach (threads, free_thread_static_data_helper, &data); + } else { + if (contexts != NULL) + g_hash_table_foreach (contexts, free_context_static_data_helper, &data); + } + + if (!mono_runtime_is_shutting_down ()) { + StaticDataFreeList *item = g_new0 (StaticDataFreeList, 1); + + item->offset = offset; + item->size = size; + item->align = align; + + item->next = info->freelist; + info->freelist = item; + } +} + +static void +do_free_special (gpointer key, gpointer value, gpointer data) +{ + MonoClassField *field = (MonoClassField *)key; + guint32 offset = GPOINTER_TO_UINT (value); + gint32 align; + guint32 size; + size = mono_type_size (field->type, &align); + do_free_special_slot (offset, size, align); +} + +void +mono_alloc_special_static_data_free (GHashTable *special_static_fields) +{ + mono_threads_lock (); + + g_hash_table_foreach (special_static_fields, do_free_special, NULL); + + mono_threads_unlock (); +} + +#ifdef HOST_WIN32 +static void CALLBACK dummy_apc (ULONG_PTR param) +{ +} +#endif + +/* + * mono_thread_execute_interruption + * + * Performs the operation that the requested thread state requires (abort, + * suspend or stop) + */ +static MonoException* +mono_thread_execute_interruption (void) +{ + MonoInternalThread *thread = mono_thread_internal_current (); + MonoThread *sys_thread = mono_thread_current (); + + LOCK_THREAD (thread); + + /* MonoThread::interruption_requested can only be changed with atomics */ + if (!mono_thread_clear_interruption_requested (thread)) { + UNLOCK_THREAD (thread); + return NULL; + } + + /* this will consume pending APC calls */ +#ifdef HOST_WIN32 + MONO_ENTER_GC_SAFE; + mono_win32_wait_for_single_object_ex (GetCurrentThread (), 0, TRUE); + MONO_EXIT_GC_SAFE; +#endif + + /* Clear the interrupted flag of the thread so it can wait again */ + mono_thread_info_clear_self_interrupt (); + + /* If there's a pending exception and an AbortRequested, the pending exception takes precedence */ + if (sys_thread->pending_exception) { + MonoException *exc; + + exc = sys_thread->pending_exception; + sys_thread->pending_exception = NULL; + + UNLOCK_THREAD (thread); + return exc; + } else if (thread->state & (ThreadState_AbortRequested)) { + UNLOCK_THREAD (thread); + g_assert (sys_thread->pending_exception == NULL); + if (thread->abort_exc == NULL) { + /* + * This might be racy, but it has to be called outside the lock + * since it calls managed code. + */ + MONO_OBJECT_SETREF (thread, abort_exc, mono_get_exception_thread_abort ()); + } + return thread->abort_exc; + } else if (thread->state & (ThreadState_SuspendRequested)) { + /* calls UNLOCK_THREAD (thread) */ + self_suspend_internal (); + return NULL; + } else if (thread->thread_interrupt_requested) { + + thread->thread_interrupt_requested = FALSE; + UNLOCK_THREAD (thread); + + return(mono_get_exception_thread_interrupted ()); + } + + UNLOCK_THREAD (thread); + + return NULL; +} + +/* + * mono_thread_request_interruption + * + * A signal handler can call this method to request the interruption of a + * thread. The result of the interruption will depend on the current state of + * the thread. If the result is an exception that needs to be throw, it is + * provided as return value. + */ +MonoException* +mono_thread_request_interruption (gboolean running_managed) +{ + MonoInternalThread *thread = mono_thread_internal_current (); + + /* The thread may already be stopping */ + if (thread == NULL) + return NULL; + + if (!mono_thread_set_interruption_requested (thread)) + return NULL; + + if (!running_managed || is_running_protected_wrapper ()) { + /* Can't stop while in unmanaged code. Increase the global interruption + request count. When exiting the unmanaged method the count will be + checked and the thread will be interrupted. */ + + /* this will awake the thread if it is in WaitForSingleObject + or similar */ +#ifdef HOST_WIN32 + mono_win32_interrupt_wait (thread->thread_info, thread->native_handle, (DWORD)thread->tid); +#else + mono_thread_info_self_interrupt (); +#endif + return NULL; + } + else { + return mono_thread_execute_interruption (); + } +} + +/*This function should be called by a thread after it has exited all of + * its handle blocks at interruption time.*/ +MonoException* +mono_thread_resume_interruption (gboolean exec) +{ + MonoInternalThread *thread = mono_thread_internal_current (); + gboolean still_aborting; + + /* The thread may already be stopping */ + if (thread == NULL) + return NULL; + + LOCK_THREAD (thread); + still_aborting = (thread->state & (ThreadState_AbortRequested)) != 0; + UNLOCK_THREAD (thread); + + /*This can happen if the protected block called Thread::ResetAbort*/ + if (!still_aborting) + return NULL; + + if (!mono_thread_set_interruption_requested (thread)) + return NULL; + + mono_thread_info_self_interrupt (); + + if (exec) + return mono_thread_execute_interruption (); + else + return NULL; +} + +gboolean mono_thread_interruption_requested () +{ + if (thread_interruption_requested) { + MonoInternalThread *thread = mono_thread_internal_current (); + /* The thread may already be stopping */ + if (thread != NULL) + return mono_thread_get_interruption_requested (thread); + } + return FALSE; +} + +static MonoException* +mono_thread_interruption_checkpoint_request (gboolean bypass_abort_protection) +{ + MonoInternalThread *thread = mono_thread_internal_current (); + + /* The thread may already be stopping */ + if (!thread) + return NULL; + if (!mono_thread_get_interruption_requested (thread)) + return NULL; + if (!bypass_abort_protection && !mono_thread_current ()->pending_exception && is_running_protected_wrapper ()) + return NULL; + + return mono_thread_execute_interruption (); +} + +/* + * Performs the interruption of the current thread, if one has been requested, + * and the thread is not running a protected wrapper. + * Return the exception which needs to be thrown, if any. + */ +MonoException* +mono_thread_interruption_checkpoint (void) +{ + return mono_thread_interruption_checkpoint_request (FALSE); +} + +/* + * Performs the interruption of the current thread, if one has been requested. + * Return the exception which needs to be thrown, if any. + */ +MonoException* +mono_thread_force_interruption_checkpoint_noraise (void) +{ + return mono_thread_interruption_checkpoint_request (TRUE); +} + +/* + * mono_set_pending_exception: + * + * Set the pending exception of the current thread to EXC. + * The exception will be thrown when execution returns to managed code. + */ +void +mono_set_pending_exception (MonoException *exc) +{ + MonoThread *thread = mono_thread_current (); + + /* The thread may already be stopping */ + if (thread == NULL) + return; + + MONO_OBJECT_SETREF (thread, pending_exception, exc); + + mono_thread_request_interruption (FALSE); +} + +/** + * mono_thread_interruption_request_flag: + * + * Returns the address of a flag that will be non-zero if an interruption has + * been requested for a thread. The thread to interrupt may not be the current + * thread, so an additional call to mono_thread_interruption_requested() or + * mono_thread_interruption_checkpoint() is allways needed if the flag is not + * zero. + */ +gint32* mono_thread_interruption_request_flag () +{ + return &thread_interruption_requested; +} + +void +mono_thread_init_apartment_state (void) +{ +#ifdef HOST_WIN32 + MonoInternalThread* thread = mono_thread_internal_current (); + + /* Positive return value indicates success, either + * S_OK if this is first CoInitialize call, or + * S_FALSE if CoInitialize already called, but with same + * threading model. A negative value indicates failure, + * probably due to trying to change the threading model. + */ + if (CoInitializeEx(NULL, (thread->apartment_state == ThreadApartmentState_STA) + ? COINIT_APARTMENTTHREADED + : COINIT_MULTITHREADED) < 0) { + thread->apartment_state = ThreadApartmentState_Unknown; + } +#endif +} + +void +mono_thread_cleanup_apartment_state (void) +{ +#ifdef HOST_WIN32 + MonoInternalThread* thread = mono_thread_internal_current (); + + if (thread && thread->apartment_state != ThreadApartmentState_Unknown) { + CoUninitialize (); + } +#endif +} + +void +mono_thread_set_state (MonoInternalThread *thread, MonoThreadState state) +{ + LOCK_THREAD (thread); + thread->state |= state; + UNLOCK_THREAD (thread); +} + +/** + * mono_thread_test_and_set_state: + * Test if current state of \p thread include \p test. If it does not, OR \p set into the state. + * \returns TRUE if \p set was OR'd in. + */ +gboolean +mono_thread_test_and_set_state (MonoInternalThread *thread, MonoThreadState test, MonoThreadState set) +{ + LOCK_THREAD (thread); + + if ((thread->state & test) != 0) { + UNLOCK_THREAD (thread); + return FALSE; + } + + thread->state |= set; + UNLOCK_THREAD (thread); + + return TRUE; +} + +void +mono_thread_clr_state (MonoInternalThread *thread, MonoThreadState state) +{ + LOCK_THREAD (thread); + thread->state &= ~state; + UNLOCK_THREAD (thread); +} + +gboolean +mono_thread_test_state (MonoInternalThread *thread, MonoThreadState test) +{ + gboolean ret = FALSE; + + LOCK_THREAD (thread); + + if ((thread->state & test) != 0) { + ret = TRUE; + } + + UNLOCK_THREAD (thread); + + return ret; +} + +static void +self_interrupt_thread (void *_unused) +{ + MonoException *exc; + MonoThreadInfo *info; + + exc = mono_thread_execute_interruption (); + if (!exc) { + if (mono_threads_is_coop_enabled ()) { + /* We can return from an async call in coop, as + * it's simply called when exiting the safepoint */ + return; + } + + g_error ("%s: we can't resume from an async call", __func__); + } + + info = mono_thread_info_current (); + + mono_raise_exception_with_context (exc, &info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx); /* FIXME using thread_saved_state [ASYNC_SUSPEND_STATE_INDEX] can race with another suspend coming in. */ +} + +static gboolean +mono_jit_info_match (MonoJitInfo *ji, gpointer ip) +{ + if (!ji) + return FALSE; + return ji->code_start <= ip && (char*)ip < (char*)ji->code_start + ji->code_size; +} + +static gboolean +last_managed (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data) +{ + MonoJitInfo **dest = (MonoJitInfo **)data; + *dest = frame->ji; + return TRUE; +} + +static MonoJitInfo* +mono_thread_info_get_last_managed (MonoThreadInfo *info) +{ + MonoJitInfo *ji = NULL; + if (!info) + return NULL; + + /* + * The suspended thread might be holding runtime locks. Make sure we don't try taking + * any runtime locks while unwinding. In coop case we shouldn't safepoint in regions + * where we hold runtime locks. + */ + if (!mono_threads_is_coop_enabled ()) + mono_thread_info_set_is_async_context (TRUE); + mono_get_eh_callbacks ()->mono_walk_stack_with_state (last_managed, mono_thread_info_get_suspend_state (info), MONO_UNWIND_SIGNAL_SAFE, &ji); + if (!mono_threads_is_coop_enabled ()) + mono_thread_info_set_is_async_context (FALSE); + return ji; +} + +typedef struct { + MonoInternalThread *thread; + gboolean install_async_abort; + MonoThreadInfoInterruptToken *interrupt_token; +} AbortThreadData; + +static SuspendThreadResult +async_abort_critical (MonoThreadInfo *info, gpointer ud) +{ + AbortThreadData *data = (AbortThreadData *)ud; + MonoInternalThread *thread = data->thread; + MonoJitInfo *ji = NULL; + gboolean protected_wrapper; + gboolean running_managed; + + 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*/ + if (!mono_thread_set_interruption_requested (thread)) + return MonoResumeThread; + + ji = mono_thread_info_get_last_managed (info); + protected_wrapper = ji && !ji->is_trampoline && !ji->async && mono_threads_is_critical_method (mono_jit_info_get_method (ji)); + running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx)); + + if (!protected_wrapper && running_managed) { + /*We are in managed code*/ + /*Set the thread to call */ + if (data->install_async_abort) + mono_thread_info_setup_async_call (info, self_interrupt_thread, NULL); + return MonoResumeThread; + } else { + /* + * This will cause waits to be broken. + * It will also prevent the thread from entering a wait, so if the thread returns + * from the wait before it receives the abort signal, it will just spin in the wait + * functions in the io-layer until the signal handler calls QueueUserAPC which will + * make it return. + */ + data->interrupt_token = mono_thread_info_prepare_interrupt (info); + + return MonoResumeThread; + } +} + +static void +async_abort_internal (MonoInternalThread *thread, gboolean install_async_abort) +{ + AbortThreadData data; + + g_assert (thread != mono_thread_internal_current ()); + + data.thread = thread; + data.install_async_abort = install_async_abort; + data.interrupt_token = NULL; + + mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), TRUE, async_abort_critical, &data); + if (data.interrupt_token) + mono_thread_info_finish_interrupt (data.interrupt_token); + /*FIXME we need to wait for interruption to complete -- figure out how much into interruption we should wait for here*/ +} + +static void +self_abort_internal (MonoError *error) +{ + MonoException *exc; + + error_init (error); + + /* FIXME this is insanely broken, it doesn't cause interruption to happen synchronously + * since passing FALSE to mono_thread_request_interruption makes sure it returns NULL */ + + /* + Self aborts ignore the protected block logic and raise the TAE regardless. This is verified by one of the tests in mono/tests/abort-cctor.cs. + */ + exc = mono_thread_request_interruption (TRUE); + if (exc) + mono_error_set_exception_instance (error, exc); + else + mono_thread_info_self_interrupt (); +} + +typedef struct { + MonoInternalThread *thread; + gboolean interrupt; + MonoThreadInfoInterruptToken *interrupt_token; +} SuspendThreadData; + +static SuspendThreadResult +async_suspend_critical (MonoThreadInfo *info, gpointer ud) +{ + SuspendThreadData *data = (SuspendThreadData *)ud; + MonoInternalThread *thread = data->thread; + MonoJitInfo *ji = NULL; + gboolean protected_wrapper; + gboolean running_managed; + + ji = mono_thread_info_get_last_managed (info); + protected_wrapper = ji && !ji->is_trampoline && !ji->async && mono_threads_is_critical_method (mono_jit_info_get_method (ji)); + running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx)); + + if (running_managed && !protected_wrapper) { + if (mono_threads_is_coop_enabled ()) { + mono_thread_info_setup_async_call (info, self_interrupt_thread, NULL); + return MonoResumeThread; + } else { + thread->state &= ~ThreadState_SuspendRequested; + thread->state |= ThreadState_Suspended; + return KeepSuspended; + } + } else { + mono_thread_set_interruption_requested (thread); + if (data->interrupt) + data->interrupt_token = mono_thread_info_prepare_interrupt ((MonoThreadInfo *)thread->thread_info); + + return MonoResumeThread; + } +} + +/* LOCKING: called with @thread synch_cs held, and releases it */ +static void +async_suspend_internal (MonoInternalThread *thread, gboolean interrupt) +{ + SuspendThreadData data; + + g_assert (thread != mono_thread_internal_current ()); + + // MOSTLY_ASYNC_SAFE_PRINTF ("ASYNC SUSPEND thread %p\n", thread_get_tid (thread)); + + thread->self_suspended = FALSE; + + data.thread = thread; + data.interrupt = interrupt; + data.interrupt_token = NULL; + + mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), interrupt, async_suspend_critical, &data); + if (data.interrupt_token) + mono_thread_info_finish_interrupt (data.interrupt_token); + + UNLOCK_THREAD (thread); +} + +/* LOCKING: called with @thread synch_cs held, and releases it */ +static void +self_suspend_internal (void) +{ + MonoInternalThread *thread; + MonoOSEvent *event; + MonoOSEventWaitRet res; + + thread = mono_thread_internal_current (); + + // MOSTLY_ASYNC_SAFE_PRINTF ("SELF SUSPEND thread %p\n", thread_get_tid (thread)); + + thread->self_suspended = TRUE; + + thread->state &= ~ThreadState_SuspendRequested; + thread->state |= ThreadState_Suspended; + + UNLOCK_THREAD (thread); + + event = thread->suspended; + + MONO_ENTER_GC_SAFE; + res = mono_os_event_wait_one (event, MONO_INFINITE_WAIT, TRUE); + g_assert (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0 || res == MONO_OS_EVENT_WAIT_RET_ALERTED); + MONO_EXIT_GC_SAFE; +} + +static void +suspend_for_shutdown_async_call (gpointer unused) +{ + for (;;) + mono_thread_info_yield (); +} + +static SuspendThreadResult +suspend_for_shutdown_critical (MonoThreadInfo *info, gpointer unused) +{ + mono_thread_info_setup_async_call (info, suspend_for_shutdown_async_call, NULL); + return MonoResumeThread; +} + +void +mono_thread_internal_suspend_for_shutdown (MonoInternalThread *thread) +{ + g_assert (thread != mono_thread_internal_current ()); + + mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), FALSE, suspend_for_shutdown_critical, NULL); +} + +/** + * mono_thread_is_foreign: + * \param thread the thread to query + * + * This function allows one to determine if a thread was created by the mono runtime and has + * a well defined lifecycle or it's a foreign one, created by the native environment. + * + * \returns TRUE if \p thread was not created by the runtime. + */ +mono_bool +mono_thread_is_foreign (MonoThread *thread) +{ + MonoThreadInfo *info = (MonoThreadInfo *)thread->internal_thread->thread_info; + return info->runtime_thread == FALSE; +} + +#ifndef HOST_WIN32 +static void +threads_native_thread_join_lock (gpointer tid, gpointer value) +{ + pthread_t thread = (pthread_t)tid; + if (thread != pthread_self ()) { + MONO_ENTER_GC_SAFE; + /* This shouldn't block */ + mono_threads_join_lock (); + mono_native_thread_join (thread); + mono_threads_join_unlock (); + MONO_EXIT_GC_SAFE; + } +} +static void +threads_native_thread_join_nolock (gpointer tid, gpointer value) +{ + pthread_t thread = (pthread_t)tid; + MONO_ENTER_GC_SAFE; + mono_native_thread_join (thread); + MONO_EXIT_GC_SAFE; +} + +static void +threads_add_joinable_thread_nolock (gpointer tid) +{ + g_hash_table_insert (joinable_threads, tid, tid); +} +#else +static void +threads_native_thread_join_lock (gpointer tid, gpointer value) +{ + MonoNativeThreadId thread_id = (MonoNativeThreadId)(guint64)tid; + HANDLE thread_handle = (HANDLE)value; + if (thread_id != GetCurrentThreadId () && thread_handle != NULL && thread_handle != INVALID_HANDLE_VALUE) { + MONO_ENTER_GC_SAFE; + /* This shouldn't block */ + mono_threads_join_lock (); + mono_native_thread_join_handle (thread_handle, TRUE); + mono_threads_join_unlock (); + MONO_EXIT_GC_SAFE; + } +} + +static void +threads_native_thread_join_nolock (gpointer tid, gpointer value) +{ + HANDLE thread_handle = (HANDLE)value; + MONO_ENTER_GC_SAFE; + mono_native_thread_join_handle (thread_handle, TRUE); + MONO_EXIT_GC_SAFE; +} + +static void +threads_add_joinable_thread_nolock (gpointer tid) +{ + g_hash_table_insert (joinable_threads, tid, (gpointer)OpenThread (SYNCHRONIZE, TRUE, (MonoNativeThreadId)(guint64)tid)); +} +#endif + +static void +threads_add_pending_joinable_thread (gpointer tid) +{ + joinable_threads_lock (); + + if (!pending_joinable_threads) + pending_joinable_threads = g_hash_table_new (NULL, NULL); + + gpointer orig_key; + gpointer value; + + if (!g_hash_table_lookup_extended (pending_joinable_threads, tid, &orig_key, &value)) { + g_hash_table_insert (pending_joinable_threads, tid, tid); + UnlockedIncrement (&pending_joinable_thread_count); + } + + joinable_threads_unlock (); +} + +static void +threads_add_pending_joinable_runtime_thread (MonoThreadInfo *mono_thread_info) +{ + g_assert (mono_thread_info); + + if (mono_thread_info->runtime_thread) { + threads_add_pending_joinable_thread ((gpointer)(MONO_UINT_TO_NATIVE_THREAD_ID (mono_thread_info_get_tid (mono_thread_info)))); + } +} + +static void +threads_remove_pending_joinable_thread_nolock (gpointer tid) +{ + gpointer orig_key; + gpointer value; + + if (pending_joinable_threads && g_hash_table_lookup_extended (pending_joinable_threads, tid, &orig_key, &value)) { + g_hash_table_remove (pending_joinable_threads, tid); + if (UnlockedDecrement (&pending_joinable_thread_count) == 0) + mono_coop_cond_broadcast (&zero_pending_joinable_thread_event); + } +} + +static gboolean +threads_wait_pending_joinable_threads (uint32_t timeout) +{ + if (UnlockedRead (&pending_joinable_thread_count) > 0) { + joinable_threads_lock (); + if (timeout == MONO_INFINITE_WAIT) { + while (UnlockedRead (&pending_joinable_thread_count) > 0) + mono_coop_cond_wait (&zero_pending_joinable_thread_event, &joinable_threads_mutex); + } else { + gint64 start = mono_msec_ticks (); + gint64 elapsed = 0; + while (UnlockedRead (&pending_joinable_thread_count) > 0 && elapsed < timeout) { + mono_coop_cond_timedwait (&zero_pending_joinable_thread_event, &joinable_threads_mutex, timeout - (uint32_t)elapsed); + elapsed = mono_msec_ticks () - start; + } + } + joinable_threads_unlock (); + } + + return UnlockedRead (&pending_joinable_thread_count) == 0; +} + +static void +threads_add_unique_joinable_thread_nolock (gpointer tid) +{ + if (!joinable_threads) + joinable_threads = g_hash_table_new (NULL, NULL); + + gpointer orig_key; + gpointer value; + + if (!g_hash_table_lookup_extended (joinable_threads, tid, &orig_key, &value)) { + threads_add_joinable_thread_nolock (tid); + UnlockedIncrement (&joinable_thread_count); + } +} + +void +mono_threads_add_joinable_runtime_thread (gpointer thread_info) +{ + g_assert (thread_info); + MonoThreadInfo *mono_thread_info = (MonoThreadInfo*)thread_info; + + if (mono_thread_info->runtime_thread) { + gpointer tid = (gpointer)(MONO_UINT_TO_NATIVE_THREAD_ID (mono_thread_info_get_tid (mono_thread_info))); + + joinable_threads_lock (); + + // Add to joinable thread list, if not already included. + threads_add_unique_joinable_thread_nolock (tid); + + // Remove thread from pending joinable list, if present. + threads_remove_pending_joinable_thread_nolock (tid); + + joinable_threads_unlock (); + + mono_gc_finalize_notify (); + } +} + +static void +threads_add_pending_native_thread_join_call_nolock (gpointer tid) +{ + if (!pending_native_thread_join_calls) + pending_native_thread_join_calls = g_hash_table_new (NULL, NULL); + + gpointer orig_key; + gpointer value; + + if (!g_hash_table_lookup_extended (pending_native_thread_join_calls, tid, &orig_key, &value)) + g_hash_table_insert (pending_native_thread_join_calls, tid, tid); +} + +static void +threads_remove_pending_native_thread_join_call_nolock (gpointer tid) +{ + if (pending_native_thread_join_calls) + g_hash_table_remove (pending_native_thread_join_calls, tid); + + mono_coop_cond_broadcast (&pending_native_thread_join_calls_event); +} + +static void +threads_wait_pending_native_thread_join_call_nolock (gpointer tid) +{ + gpointer orig_key; + gpointer value; + + while (g_hash_table_lookup_extended (pending_native_thread_join_calls, tid, &orig_key, &value)) { + mono_coop_cond_wait (&pending_native_thread_join_calls_event, &joinable_threads_mutex); + } +} + +/* + * mono_add_joinable_thread: + * + * Add TID to the list of joinable threads. + * LOCKING: Acquires the threads lock. + */ +void +mono_threads_add_joinable_thread (gpointer tid) +{ + /* + * We cannot detach from threads because it causes problems like + * 2fd16f60/r114307. So we collect them and join them when + * we have time (in the finalizer thread). + */ + joinable_threads_lock (); + threads_add_unique_joinable_thread_nolock (tid); + joinable_threads_unlock (); + + mono_gc_finalize_notify (); +} + +/* + * mono_threads_join_threads: + * + * Join all joinable threads. This is called from the finalizer thread. + * LOCKING: Acquires the threads lock. + */ +void +mono_threads_join_threads (void) +{ + GHashTableIter iter; + gpointer key = NULL; + gpointer value = NULL; + gboolean found = FALSE; + + /* Fastpath */ + if (!UnlockedRead (&joinable_thread_count)) + return; + + while (TRUE) { + joinable_threads_lock (); + if (found) { + // Previous native thread join call completed. + threads_remove_pending_native_thread_join_call_nolock (key); + } + found = FALSE; + if (g_hash_table_size (joinable_threads)) { + g_hash_table_iter_init (&iter, joinable_threads); + g_hash_table_iter_next (&iter, &key, (void**)&value); + g_hash_table_remove (joinable_threads, key); + UnlockedDecrement (&joinable_thread_count); + found = TRUE; + + // Add to table of tid's with pending native thread join call. + threads_add_pending_native_thread_join_call_nolock (key); + } + joinable_threads_unlock (); + if (found) + threads_native_thread_join_lock (key, value); + else + break; + } +} + +/* + * mono_thread_join: + * + * Wait for thread TID to exit. + * LOCKING: Acquires the threads lock. + */ +void +mono_thread_join (gpointer tid) +{ + gboolean found = FALSE; + gpointer orig_key; + gpointer value; + + joinable_threads_lock (); + if (!joinable_threads) + joinable_threads = g_hash_table_new (NULL, NULL); + + if (g_hash_table_lookup_extended (joinable_threads, tid, &orig_key, &value)) { + g_hash_table_remove (joinable_threads, tid); + UnlockedDecrement (&joinable_thread_count); + found = TRUE; + + // Add to table of tid's with pending native join call. + threads_add_pending_native_thread_join_call_nolock (tid); + } + + if (!found) { + // Wait for any pending native thread join call not yet completed for this tid. + threads_wait_pending_native_thread_join_call_nolock (tid); + } + + joinable_threads_unlock (); + + if (!found) + return; + + threads_native_thread_join_nolock (tid, value); + + joinable_threads_lock (); + // Native thread join call completed for this tid. + threads_remove_pending_native_thread_join_call_nolock (tid); + joinable_threads_unlock (); +} + +void +mono_thread_internal_unhandled_exception (MonoObject* exc) +{ + MonoClass *klass = exc->vtable->klass; + if (is_threadabort_exception (klass)) { + mono_thread_internal_reset_abort (mono_thread_internal_current ()); + } else if (!is_appdomainunloaded_exception (klass) + && mono_runtime_unhandled_exception_policy_get () == MONO_UNHANDLED_POLICY_CURRENT) { + mono_unhandled_exception (exc); + if (mono_environment_exitcode_get () == 1) { + mono_environment_exitcode_set (255); + mono_invoke_unhandled_exception_hook (exc); + g_assert_not_reached (); + } + } +} + +void +ves_icall_System_Threading_Thread_GetStackTraces (MonoArray **out_threads, MonoArray **out_stack_traces) +{ + MonoError error; + mono_threads_get_thread_dump (out_threads, out_stack_traces, &error); + mono_error_set_pending_exception (&error); +} + +/* + * mono_threads_attach_coop: called by native->managed wrappers + * + * - @dummy: + * - blocking mode: contains gc unsafe transition cookie + * - non-blocking mode: contains random data + * - @return: the original domain which needs to be restored, or NULL. + */ +gpointer +mono_threads_attach_coop (MonoDomain *domain, gpointer *dummy) +{ + MonoDomain *orig; + MonoThreadInfo *info; + gboolean external; + + orig = mono_domain_get (); + + if (!domain) { + /* Happens when called from AOTed code which is only used in the root domain. */ + domain = mono_get_root_domain (); + g_assert (domain); + } + + /* On coop, when we detached, we moved the thread from RUNNING->BLOCKING. + * If we try to reattach we do a BLOCKING->RUNNING transition. If the thread + * is fresh, mono_thread_attach() will do a STARTING->RUNNING transition so + * we're only responsible for making the cookie. */ + if (mono_threads_is_blocking_transition_enabled ()) + external = !(info = mono_thread_info_current_unchecked ()) || !mono_thread_info_is_live (info); + + if (!mono_thread_internal_current ()) { + mono_thread_attach_full (domain, FALSE); + + // #678164 + mono_thread_set_state (mono_thread_internal_current (), ThreadState_Background); + } + + if (orig != domain) + mono_domain_set (domain, TRUE); + + if (mono_threads_is_blocking_transition_enabled ()) { + if (external) { + /* mono_thread_attach put the thread in RUNNING mode from STARTING, but we need to + * return the right cookie. */ + *dummy = mono_threads_enter_gc_unsafe_region_cookie (); + } else { + /* thread state (BLOCKING|RUNNING) -> RUNNING */ + *dummy = mono_threads_enter_gc_unsafe_region (dummy); + } + } + + return orig; +} + +/* + * mono_threads_detach_coop: called by native->managed wrappers + * + * - @cookie: the original domain which needs to be restored, or NULL. + * - @dummy: + * - blocking mode: contains gc unsafe transition cookie + * - non-blocking mode: contains random data + */ +void +mono_threads_detach_coop (gpointer cookie, gpointer *dummy) +{ + MonoDomain *domain, *orig; + + orig = (MonoDomain*) cookie; + + domain = mono_domain_get (); + g_assert (domain); + + if (mono_threads_is_blocking_transition_enabled ()) { + /* it won't do anything if cookie is NULL + * thread state RUNNING -> (RUNNING|BLOCKING) */ + mono_threads_exit_gc_unsafe_region (*dummy, dummy); + } + + if (orig != domain) { + if (!orig) + mono_domain_unset (); + else + mono_domain_set (orig, TRUE); + } +} + +#if 0 +/* Returns TRUE if the current thread is ready to be interrupted. */ +gboolean +mono_threads_is_ready_to_be_interrupted (void) +{ + MonoInternalThread *thread; + + thread = mono_thread_internal_current (); + LOCK_THREAD (thread); + if (thread->state & (ThreadState_SuspendRequested | ThreadState_AbortRequested)) { + UNLOCK_THREAD (thread); + return FALSE; + } + + if (mono_thread_get_abort_prot_block_count (thread) || mono_get_eh_callbacks ()->mono_current_thread_has_handle_block_guard ()) { + UNLOCK_THREAD (thread); + return FALSE; + } + + UNLOCK_THREAD (thread); + return TRUE; +} +#endif + +void +mono_thread_internal_describe (MonoInternalThread *internal, GString *text) +{ + g_string_append_printf (text, ", thread handle : %p", internal->handle); + + if (internal->thread_info) { + g_string_append (text, ", state : "); + mono_thread_info_describe_interrupt_token ((MonoThreadInfo*) internal->thread_info, text); + } + + if (internal->owned_mutexes) { + int i; + + g_string_append (text, ", owns : ["); + for (i = 0; i < internal->owned_mutexes->len; i++) + g_string_append_printf (text, i == 0 ? "%p" : ", %p", g_ptr_array_index (internal->owned_mutexes, i)); + g_string_append (text, "]"); + } +} + +gboolean +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/unity-2019.4.24f1-mbe/mono/metadata/threads.h b/unity-2019.4.24f1-mbe/mono/metadata/threads.h new file mode 100644 index 000000000..bad65287b --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/threads.h @@ -0,0 +1,62 @@ +/** + * \file + * Threading API + * + * Author: + * Dick Porter (dick@ximian.com) + * Patrik Torstensson (patrik.torstensson@labs2.com) + * + * (C) 2001 Ximian, Inc + */ + +#ifndef _MONO_METADATA_THREADS_H_ +#define _MONO_METADATA_THREADS_H_ + +#include +#include +#include + +MONO_BEGIN_DECLS + +/* This callback should return TRUE if the runtime must wait for the thread, FALSE otherwise */ +typedef mono_bool (*MonoThreadManageCallback) (MonoThread* thread); + +extern MONO_API void mono_thread_init (MonoThreadStartCB start_cb, + MonoThreadAttachCB attach_cb); +extern MONO_API void mono_thread_cleanup (void); +extern MONO_API void mono_thread_manage(void); + +extern MONO_API MonoThread *mono_thread_current (void); + +extern MONO_API void mono_thread_set_main (MonoThread *thread); +extern MONO_API MonoThread *mono_thread_get_main (void); + +extern MONO_RT_EXTERNAL_ONLY MONO_API void mono_thread_stop (MonoThread *thread); + +extern MONO_API void mono_thread_new_init (intptr_t tid, void* stack_start, + void* func); + +extern MONO_RT_EXTERNAL_ONLY MONO_API void +mono_thread_create (MonoDomain *domain, void* func, void* arg); + +extern MONO_API MonoThread *mono_thread_attach (MonoDomain *domain); +extern MONO_API void mono_thread_detach (MonoThread *thread); +extern MONO_API void mono_thread_exit (void); + +extern MONO_API char *mono_thread_get_name_utf8 (MonoThread *thread); +extern MONO_API int32_t mono_thread_get_managed_id (MonoThread *thread); + +MONO_API void mono_thread_set_manage_callback (MonoThread *thread, MonoThreadManageCallback func); + +extern MONO_API void mono_threads_set_default_stacksize (uint32_t stacksize); +extern MONO_API uint32_t mono_threads_get_default_stacksize (void); + +MONO_API void mono_threads_request_thread_dump (void); + +MONO_API mono_bool mono_thread_is_foreign (MonoThread *thread); + +extern MONO_API mono_bool mono_thread_detach_if_exiting (void); + +MONO_END_DECLS + +#endif /* _MONO_METADATA_THREADS_H_ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/tokentype.h b/unity-2019.4.24f1-mbe/mono/metadata/tokentype.h new file mode 100644 index 000000000..a1c58944d --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/tokentype.h @@ -0,0 +1,45 @@ +/** + * \file + */ + +#ifndef _MONO_METADATA_TOKENTYPE_H_ +#define _MONO_METADATA_TOKENTYPE_H_ + +/* + * These tokens match the table ID except for the last + * three (string, name and base type which are special) + */ + +typedef enum { + MONO_TOKEN_MODULE = 0x00000000, + MONO_TOKEN_TYPE_REF = 0x01000000, + MONO_TOKEN_TYPE_DEF = 0x02000000, + MONO_TOKEN_FIELD_DEF = 0x04000000, + MONO_TOKEN_METHOD_DEF = 0x06000000, + MONO_TOKEN_PARAM_DEF = 0x08000000, + MONO_TOKEN_INTERFACE_IMPL = 0x09000000, + MONO_TOKEN_MEMBER_REF = 0x0a000000, + MONO_TOKEN_CUSTOM_ATTRIBUTE = 0x0c000000, + MONO_TOKEN_PERMISSION = 0x0e000000, + MONO_TOKEN_SIGNATURE = 0x11000000, + MONO_TOKEN_EVENT = 0x14000000, + MONO_TOKEN_PROPERTY = 0x17000000, + MONO_TOKEN_MODULE_REF = 0x1a000000, + MONO_TOKEN_TYPE_SPEC = 0x1b000000, + MONO_TOKEN_ASSEMBLY = 0x20000000, + MONO_TOKEN_ASSEMBLY_REF = 0x23000000, + MONO_TOKEN_FILE = 0x26000000, + MONO_TOKEN_EXPORTED_TYPE = 0x27000000, + MONO_TOKEN_MANIFEST_RESOURCE = 0x28000000, + MONO_TOKEN_GENERIC_PARAM = 0x2a000000, + MONO_TOKEN_METHOD_SPEC = 0x2b000000, + + /* + * These do not match metadata tables directly + */ + MONO_TOKEN_STRING = 0x70000000, + MONO_TOKEN_NAME = 0x71000000, + MONO_TOKEN_BASE_TYPE = 0x72000000 +} MonoTokenType; + +#endif /* _MONO_METADATA_TOKENTYPE_H_ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/unity-icall.c b/unity-2019.4.24f1-mbe/mono/metadata/unity-icall.c new file mode 100644 index 000000000..03ec5b6dc --- /dev/null +++ b/unity-2019.4.24f1-mbe/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/unity-2019.4.24f1-mbe/mono/metadata/unity-liveness.c b/unity-2019.4.24f1-mbe/mono/metadata/unity-liveness.c new file mode 100644 index 000000000..7f6c3df8f --- /dev/null +++ b/unity-2019.4.24f1-mbe/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/unity-2019.4.24f1-mbe/mono/metadata/unity-memory-info.c b/unity-2019.4.24f1-mbe/mono/metadata/unity-memory-info.c new file mode 100644 index 000000000..ee1bb3c84 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/unity-memory-info.c @@ -0,0 +1,810 @@ +#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, GList** managedObjects) +{ + *managedObjects = g_list_append(*managedObjects, managedObject); +} + +static inline void CaptureGCHandleTargets(MonoGCHandles* gcHandles) +{ + uint32_t i; + GList* trackedObjects, *trackedObject; + + trackedObjects = NULL; + + mono_gc_strong_handle_foreach((GFunc)GCHandleIterationCallback, &trackedObjects); + + gcHandles->trackedObjectCount = (uint32_t)g_list_length(trackedObjects); + gcHandles->pointersToObjects = (uint64_t*)g_new0(uint64_t, gcHandles->trackedObjectCount); + + trackedObject = trackedObjects; + + for (i = 0; i < gcHandles->trackedObjectCount; i++) + { + gcHandles->pointersToObjects[i] = (uint64_t)trackedObject->data; + trackedObject = g_list_next(trackedObject); + } + + g_list_free(trackedObjects); +} + +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/unity-2019.4.24f1-mbe/mono/metadata/unity-memory-info.h b/unity-2019.4.24f1-mbe/mono/metadata/unity-memory-info.h new file mode 100644 index 000000000..e21c8f254 --- /dev/null +++ b/unity-2019.4.24f1-mbe/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/unity-2019.4.24f1-mbe/mono/metadata/unity-utils.c b/unity-2019.4.24f1-mbe/mono/metadata/unity-utils.c new file mode 100644 index 000000000..633edb286 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/unity-utils.c @@ -0,0 +1,1910 @@ +#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; + MonoClass* klass; + klass = mono_class_inflate_generic_class_checked(gklass, context, &error); + mono_error_cleanup (&error); + return klass; +} + +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_enable() +{ +#if HAVE_BDWGC_GC + GC_enable(); +#else + g_assert_not_reached (); +#endif +} + +MONO_API void mono_unity_gc_disable() +{ +#if HAVE_BDWGC_GC + GC_disable(); +#else + g_assert_not_reached (); +#endif +} + +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 *)); +} + +MONO_API gboolean +mono_unity_class_is_open_constructed_type (MonoClass *klass) +{ + return mono_class_is_open_constructed_type (&klass->byval_arg); +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/unity-utils.h b/unity-2019.4.24f1-mbe/mono/metadata/unity-utils.h new file mode 100644 index 000000000..0f15173a2 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/unity-utils.h @@ -0,0 +1,207 @@ +#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); +MONO_API 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 +MONO_API void mono_unity_gc_enable(); +MONO_API void mono_unity_gc_disable(); +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); + +MONO_API gboolean mono_unity_class_is_open_constructed_type (MonoClass *klass); + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/verify-internals.h b/unity-2019.4.24f1-mbe/mono/metadata/verify-internals.h new file mode 100644 index 000000000..51a996c76 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/verify-internals.h @@ -0,0 +1,63 @@ +/** + * \file + */ + +#ifndef __MONO_METADATA_VERIFY_INTERNAL_H__ +#define __MONO_METADATA_VERIFY_INTERNAL_H__ + +#include +#include +#include +#include + +G_BEGIN_DECLS + +typedef enum { + MONO_VERIFIER_MODE_OFF, + MONO_VERIFIER_MODE_VALID, + MONO_VERIFIER_MODE_VERIFIABLE, + MONO_VERIFIER_MODE_STRICT +} MiniVerifierMode; + +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_method_full_trust (MonoMethod *method); +gboolean mono_verifier_is_class_full_trust (MonoClass *klass); +gboolean mono_verifier_class_is_valid_generic_instantiation (MonoClass *klass); +gboolean mono_verifier_is_method_valid_generic_instantiation (MonoMethod *method); + +gboolean mono_verifier_verify_class (MonoClass *klass); + +GSList* mono_method_verify_with_current_settings (MonoMethod *method, gboolean skip_visibility, gboolean is_fulltrust); + +gboolean mono_verifier_verify_pe_data (MonoImage *image, GSList **error_list); +gboolean mono_verifier_verify_cli_data (MonoImage *image, GSList **error_list); +gboolean mono_verifier_verify_table_data (MonoImage *image, GSList **error_list); + +gboolean mono_verifier_verify_full_table_data (MonoImage *image, GSList **error_list); + +gboolean mono_verifier_verify_field_signature (MonoImage *image, guint32 offset, GSList **error_list); +gboolean mono_verifier_verify_method_header (MonoImage *image, guint32 offset, GSList **error_list); +gboolean mono_verifier_verify_method_signature (MonoImage *image, guint32 offset, MonoError *error); +gboolean mono_verifier_verify_standalone_signature (MonoImage *image, guint32 offset, GSList **error_list); +gboolean mono_verifier_verify_typespec_signature (MonoImage *image, guint32 offset, guint32 token, GSList **error_list); +gboolean mono_verifier_verify_methodspec_signature (MonoImage *image, guint32 offset, GSList **error_list); +gboolean mono_verifier_verify_string_signature (MonoImage *image, guint32 offset, GSList **error_list); +gboolean mono_verifier_verify_cattr_blob (MonoImage *image, guint32 offset, GSList **error_list); +gboolean mono_verifier_verify_cattr_content (MonoImage *image, MonoMethod *ctor, const guchar *data, guint32 size, GSList **error_list); +gboolean mono_verifier_is_sig_compatible (MonoImage *image, MonoMethod *method, MonoMethodSignature *signature); +gboolean mono_verifier_verify_memberref_method_signature (MonoImage *image, guint32 offset, GSList **error_list); +gboolean mono_verifier_verify_memberref_field_signature (MonoImage *image, guint32 offset, GSList **error_list); + +gboolean mono_verifier_verify_typeref_row (MonoImage *image, guint32 row, MonoError *error); +gboolean mono_verifier_verify_methodimpl_row (MonoImage *image, guint32 row, MonoError *error); +gboolean mono_verifier_is_signature_compatible (MonoMethodSignature *target, MonoMethodSignature *candidate); +G_END_DECLS + +#endif /* __MONO_METADATA_VERIFY_INTERNAL_H__ */ + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/verify.c b/unity-2019.4.24f1-mbe/mono/metadata/verify.c new file mode 100644 index 000000000..e509ed680 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/verify.c @@ -0,0 +1,6539 @@ +/** + * \file + * + * Author: + * Mono Project (http://www.mono-project.com) + * + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Copyright 2011 Rodrigo Kumpera + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static MiniVerifierMode verifier_mode = MONO_VERIFIER_MODE_OFF; +static gboolean verify_all = FALSE; + +/* + * Set the desired level of checks for the verfier. + * + */ +void +mono_verifier_set_mode (MiniVerifierMode mode) +{ + verifier_mode = mode; +} + +void +mono_verifier_enable_verify_all () +{ + verify_all = TRUE; +} + +#ifndef DISABLE_VERIFIER +/* + * Pull the list of opcodes + */ +#define OPDEF(a,b,c,d,e,f,g,h,i,j) \ + a = i, + +enum { +#include "mono/cil/opcode.def" + LAST = 0xff +}; +#undef OPDEF + +#ifdef MONO_VERIFIER_DEBUG +#define VERIFIER_DEBUG(code) do { code } while (0) +#else +#define VERIFIER_DEBUG(code) +#endif + +////////////////////////////////////////////////////////////////// +#define IS_STRICT_MODE(ctx) (((ctx)->level & MONO_VERIFY_NON_STRICT) == 0) +#define IS_FAIL_FAST_MODE(ctx) (((ctx)->level & MONO_VERIFY_FAIL_FAST) == MONO_VERIFY_FAIL_FAST) +#define IS_SKIP_VISIBILITY(ctx) (((ctx)->level & MONO_VERIFY_SKIP_VISIBILITY) == MONO_VERIFY_SKIP_VISIBILITY) +#define IS_REPORT_ALL_ERRORS(ctx) (((ctx)->level & MONO_VERIFY_REPORT_ALL_ERRORS) == MONO_VERIFY_REPORT_ALL_ERRORS) +#define CLEAR_PREFIX(ctx, prefix) do { (ctx)->prefix_set &= ~(prefix); } while (0) +#define ADD_VERIFY_INFO(__ctx, __msg, __status, __exception) \ + do { \ + MonoVerifyInfoExtended *vinfo = g_new (MonoVerifyInfoExtended, 1); \ + vinfo->info.status = __status; \ + vinfo->info.message = ( __msg ); \ + vinfo->exception_type = (__exception); \ + (__ctx)->list = g_slist_prepend ((__ctx)->list, vinfo); \ + } while (0) + +//TODO support MONO_VERIFY_REPORT_ALL_ERRORS +#define ADD_VERIFY_ERROR(__ctx, __msg) \ + do { \ + ADD_VERIFY_INFO(__ctx, __msg, MONO_VERIFY_ERROR, MONO_EXCEPTION_INVALID_PROGRAM); \ + (__ctx)->valid = 0; \ + } while (0) + +#define CODE_NOT_VERIFIABLE(__ctx, __msg) \ + do { \ + if ((__ctx)->verifiable || IS_REPORT_ALL_ERRORS (__ctx)) { \ + ADD_VERIFY_INFO(__ctx, __msg, MONO_VERIFY_NOT_VERIFIABLE, MONO_EXCEPTION_UNVERIFIABLE_IL); \ + (__ctx)->verifiable = 0; \ + if (IS_FAIL_FAST_MODE (__ctx)) \ + (__ctx)->valid = 0; \ + } \ + } while (0) + +#define ADD_VERIFY_ERROR2(__ctx, __msg, __exception) \ + do { \ + ADD_VERIFY_INFO(__ctx, __msg, MONO_VERIFY_ERROR, __exception); \ + (__ctx)->valid = 0; \ + } while (0) + +#define CODE_NOT_VERIFIABLE2(__ctx, __msg, __exception) \ + do { \ + if ((__ctx)->verifiable || IS_REPORT_ALL_ERRORS (__ctx)) { \ + ADD_VERIFY_INFO(__ctx, __msg, MONO_VERIFY_NOT_VERIFIABLE, __exception); \ + (__ctx)->verifiable = 0; \ + if (IS_FAIL_FAST_MODE (__ctx)) \ + (__ctx)->valid = 0; \ + } \ + } while (0) + +#define CHECK_ADD4_OVERFLOW_UN(a, b) ((guint32)(0xFFFFFFFFU) - (guint32)(b) < (guint32)(a)) +#define CHECK_ADD8_OVERFLOW_UN(a, b) ((guint64)(0xFFFFFFFFFFFFFFFFUL) - (guint64)(b) < (guint64)(a)) + +#if SIZEOF_VOID_P == 4 +#define CHECK_ADDP_OVERFLOW_UN(a,b) CHECK_ADD4_OVERFLOW_UN(a, b) +#else +#define CHECK_ADDP_OVERFLOW_UN(a,b) CHECK_ADD8_OVERFLOW_UN(a, b) +#endif + +#define ADDP_IS_GREATER_OR_OVF(a, b, c) (((a) + (b) > (c)) || CHECK_ADDP_OVERFLOW_UN (a, b)) +#define ADD_IS_GREATER_OR_OVF(a, b, c) (((a) + (b) > (c)) || CHECK_ADD4_OVERFLOW_UN (a, b)) + +/*Flags to be used with ILCodeDesc::flags */ +enum { + /*Instruction has not been processed.*/ + IL_CODE_FLAG_NOT_PROCESSED = 0, + /*Instruction was decoded by mono_method_verify loop.*/ + IL_CODE_FLAG_SEEN = 1, + /*Instruction was target of a branch or is at a protected block boundary.*/ + IL_CODE_FLAG_WAS_TARGET = 2, + /*Used by stack_init to avoid double initialize each entry.*/ + IL_CODE_FLAG_STACK_INITED = 4, + /*Used by merge_stacks to decide if it should just copy the eval stack.*/ + IL_CODE_STACK_MERGED = 8, + /*This instruction is part of the delegate construction sequence, it cannot be target of a branch.*/ + IL_CODE_DELEGATE_SEQUENCE = 0x10, + /*This is a delegate created from a ldftn to a non final virtual method*/ + IL_CODE_LDFTN_DELEGATE_NONFINAL_VIRTUAL = 0x20, + /*This is a call to a non final virtual method*/ + IL_CODE_CALL_NONFINAL_VIRTUAL = 0x40, +}; + +typedef enum { + RESULT_VALID, + RESULT_UNVERIFIABLE, + RESULT_INVALID +} verify_result_t; + +typedef struct { + MonoType *type; + int stype; + MonoMethod *method; +} ILStackDesc; + + +typedef struct { + ILStackDesc *stack; + guint16 size, max_size; + guint16 flags; +} ILCodeDesc; + +typedef struct { + int max_args; + int max_stack; + int verifiable; + int valid; + int level; + + int code_size; + ILCodeDesc *code; + ILCodeDesc eval; + + MonoType **params; + GSList *list; + /*Allocated fnptr MonoType that should be freed by us.*/ + GSList *funptrs; + /*Type dup'ed exception types from catch blocks.*/ + GSList *exception_types; + + int num_locals; + MonoType **locals; + char *locals_verification_state; + + /*TODO get rid of target here, need_merge in mono_method_verify and hoist the merging code in the branching code*/ + int target; + + guint32 ip_offset; + MonoMethodSignature *signature; + MonoMethodHeader *header; + + MonoGenericContext *generic_context; + MonoImage *image; + MonoMethod *method; + + /*This flag helps solving a corner case of delegate verification in that you cannot have a "starg 0" + *on a method that creates a delegate for a non-final virtual method using ldftn*/ + gboolean has_this_store; + + /*This flag is used to control if the contructor of the parent class has been called. + *If the this pointer is pushed on the eval stack and it's a reference type constructor and + * super_ctor_called is false, the uninitialized flag is set on the pushed value. + * + * Poping an uninitialized this ptr from the eval stack is an unverifiable operation unless + * the safe variant is used. Only a few opcodes can use it : dup, pop, ldfld, stfld and call to a constructor. + */ + gboolean super_ctor_called; + + guint32 prefix_set; + gboolean has_flags; + MonoType *constrained_type; +} VerifyContext; + +static void +merge_stacks (VerifyContext *ctx, ILCodeDesc *from, ILCodeDesc *to, gboolean start, gboolean external); + +static int +get_stack_type (MonoType *type); + +static gboolean +mono_delegate_signature_equal (MonoMethodSignature *delegate_sig, MonoMethodSignature *method_sig, gboolean is_static_ldftn); + +static gboolean +mono_class_is_valid_generic_instantiation (VerifyContext *ctx, MonoClass *klass); + +static gboolean +mono_method_is_valid_generic_instantiation (VerifyContext *ctx, MonoMethod *method); + +static MonoGenericParam* +verifier_get_generic_param_from_type (VerifyContext *ctx, MonoType *type); + +static gboolean +verifier_class_is_assignable_from (MonoClass *target, MonoClass *candidate); +////////////////////////////////////////////////////////////////// + + + +enum { + TYPE_INV = 0, /* leave at 0. */ + TYPE_I4 = 1, + TYPE_I8 = 2, + TYPE_NATIVE_INT = 3, + TYPE_R8 = 4, + /* Used by operator tables to resolve pointer types (managed & unmanaged) and by unmanaged pointer types*/ + TYPE_PTR = 5, + /* value types and classes */ + TYPE_COMPLEX = 6, + /* Number of types, used to define the size of the tables*/ + TYPE_MAX = 6, + + /* Used by tables to signal that a result is not verifiable*/ + NON_VERIFIABLE_RESULT = 0x80, + + /*Mask used to extract just the type, excluding flags */ + TYPE_MASK = 0x0F, + + /* The stack type is a managed pointer, unmask the value to res */ + POINTER_MASK = 0x100, + + /*Stack type with the pointer mask*/ + RAW_TYPE_MASK = 0x10F, + + /* Controlled Mutability Manager Pointer */ + CMMP_MASK = 0x200, + + /* The stack type is a null literal*/ + NULL_LITERAL_MASK = 0x400, + + /**Used by ldarg.0 and family to let delegate verification happens.*/ + THIS_POINTER_MASK = 0x800, + + /**Signals that this is a boxed value type*/ + BOXED_MASK = 0x1000, + + /*This is an unitialized this ref*/ + UNINIT_THIS_MASK = 0x2000, + + /* This is a safe to return byref */ + SAFE_BYREF_MASK = 0x4000, +}; + +static const char* const +type_names [TYPE_MAX + 1] = { + "Invalid", + "Int32", + "Int64", + "Native Int", + "Float64", + "Native Pointer", + "Complex" +}; + +enum { + PREFIX_UNALIGNED = 1, + PREFIX_VOLATILE = 2, + PREFIX_TAIL = 4, + PREFIX_CONSTRAINED = 8, + PREFIX_READONLY = 16 +}; +////////////////////////////////////////////////////////////////// + +#ifdef ENABLE_VERIFIER_STATS + +#define _MEM_ALLOC(amt) do { allocated_memory += (amt); working_set += (amt); } while (0) +#define _MEM_FREE(amt) do { working_set -= (amt); } while (0) + +static int allocated_memory; +static int working_set; +static int max_allocated_memory; +static int max_working_set; +static int total_allocated_memory; + +static void +finish_collect_stats (void) +{ + max_allocated_memory = MAX (max_allocated_memory, allocated_memory); + max_working_set = MAX (max_working_set, working_set); + total_allocated_memory += allocated_memory; + allocated_memory = working_set = 0; +} + +static void +init_verifier_stats (void) +{ + static gboolean inited; + if (!inited) { + inited = TRUE; + mono_counters_register ("Maximum memory allocated during verification", MONO_COUNTER_METADATA | MONO_COUNTER_INT, &max_allocated_memory); + mono_counters_register ("Maximum memory used during verification", MONO_COUNTER_METADATA | MONO_COUNTER_INT, &max_working_set); + mono_counters_register ("Total memory allocated for verification", MONO_COUNTER_METADATA | MONO_COUNTER_INT, &total_allocated_memory); + } +} + +#else + +#define _MEM_ALLOC(amt) do {} while (0) +#define _MEM_FREE(amt) do { } while (0) + +#define finish_collect_stats() +#define init_verifier_stats() + +#endif + + +////////////////////////////////////////////////////////////////// + + +/*Token validation macros and functions */ +#define IS_MEMBER_REF(token) (mono_metadata_token_table (token) == MONO_TABLE_MEMBERREF) +#define IS_METHOD_DEF(token) (mono_metadata_token_table (token) == MONO_TABLE_METHOD) +#define IS_METHOD_SPEC(token) (mono_metadata_token_table (token) == MONO_TABLE_METHODSPEC) +#define IS_FIELD_DEF(token) (mono_metadata_token_table (token) == MONO_TABLE_FIELD) + +#define IS_TYPE_REF(token) (mono_metadata_token_table (token) == MONO_TABLE_TYPEREF) +#define IS_TYPE_DEF(token) (mono_metadata_token_table (token) == MONO_TABLE_TYPEDEF) +#define IS_TYPE_SPEC(token) (mono_metadata_token_table (token) == MONO_TABLE_TYPESPEC) +#define IS_METHOD_DEF_OR_REF_OR_SPEC(token) (IS_METHOD_DEF (token) || IS_MEMBER_REF (token) || IS_METHOD_SPEC (token)) +#define IS_TYPE_DEF_OR_REF_OR_SPEC(token) (IS_TYPE_DEF (token) || IS_TYPE_REF (token) || IS_TYPE_SPEC (token)) +#define IS_FIELD_DEF_OR_REF(token) (IS_FIELD_DEF (token) || IS_MEMBER_REF (token)) + +/* + * Verify if @token refers to a valid row on int's table. + */ +static gboolean +token_bounds_check (MonoImage *image, guint32 token) +{ + if (image_is_dynamic (image)) + return mono_dynamic_image_is_valid_token ((MonoDynamicImage*)image, token); + return image->tables [mono_metadata_token_table (token)].rows >= mono_metadata_token_index (token) && mono_metadata_token_index (token) > 0; +} + +static MonoType * +mono_type_create_fnptr_from_mono_method (VerifyContext *ctx, MonoMethod *method) +{ + MonoType *res = g_new0 (MonoType, 1); + _MEM_ALLOC (sizeof (MonoType)); + + //FIXME use mono_method_get_signature_full + res->data.method = mono_method_signature (method); + res->type = MONO_TYPE_FNPTR; + ctx->funptrs = g_slist_prepend (ctx->funptrs, res); + return res; +} + +/* + * mono_type_is_enum_type: + * + * Returns: TRUE if @type is an enum type. + */ +static gboolean +mono_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; +} + +/* + * mono_type_is_value_type: + * + * Returns: TRUE if @type is named after @namespace.@name. + * + */ +static gboolean +mono_type_is_value_type (MonoType *type, const char *namespace_, const char *name) +{ + return type->type == MONO_TYPE_VALUETYPE && + !strcmp (namespace_, type->data.klass->name_space) && + !strcmp (name, type->data.klass->name); +} + +/* + * Returns TURE if @type is VAR or MVAR + */ +static gboolean +mono_type_is_generic_argument (MonoType *type) +{ + return type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR; +} + +/* + * mono_type_get_underlying_type_any: + * + * This functions is just like mono_type_get_underlying_type but it doesn't care if the type is byref. + * + * Returns the underlying type of @type regardless if it is byref or not. + */ +static MonoType* +mono_type_get_underlying_type_any (MonoType *type) +{ + if (type->type == MONO_TYPE_VALUETYPE && type->data.klass->enumtype) + return mono_class_enum_basetype (type->data.klass); + if (type->type == MONO_TYPE_GENERICINST && type->data.generic_class->container_class->enumtype) + return mono_class_enum_basetype (type->data.generic_class->container_class); + return type; +} + +static G_GNUC_UNUSED const char* +mono_type_get_stack_name (MonoType *type) +{ + return type_names [get_stack_type (type) & TYPE_MASK]; +} + +#define CTOR_REQUIRED_FLAGS (METHOD_ATTRIBUTE_SPECIAL_NAME | METHOD_ATTRIBUTE_RT_SPECIAL_NAME) +#define CTOR_INVALID_FLAGS (METHOD_ATTRIBUTE_STATIC) + +static gboolean +mono_method_is_constructor (MonoMethod *method) +{ + return ((method->flags & CTOR_REQUIRED_FLAGS) == CTOR_REQUIRED_FLAGS && + !(method->flags & CTOR_INVALID_FLAGS) && + !strcmp (".ctor", method->name)); +} + +static gboolean +mono_class_has_default_constructor (MonoClass *klass) +{ + MonoMethod *method; + int i; + + mono_class_setup_methods (klass); + if (mono_class_has_failure (klass)) + return FALSE; + + int mcount = mono_class_get_method_count (klass); + for (i = 0; i < mcount; ++i) { + method = klass->methods [i]; + if (mono_method_is_constructor (method) && + mono_method_signature (method) && + mono_method_signature (method)->param_count == 0 && + (method->flags & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK) == METHOD_ATTRIBUTE_PUBLIC) + return TRUE; + } + return FALSE; +} + +/* + * Verify if @type is valid for the given @ctx verification context. + * this function checks for VAR and MVAR types that are invalid under the current verifier, + */ +static gboolean +mono_type_is_valid_type_in_context_full (MonoType *type, MonoGenericContext *context, gboolean check_gtd) +{ + int i; + MonoGenericInst *inst; + + switch (type->type) { + case MONO_TYPE_VAR: + case MONO_TYPE_MVAR: + if (!context) + return FALSE; + inst = type->type == MONO_TYPE_VAR ? context->class_inst : context->method_inst; + if (!inst || mono_type_get_generic_param_num (type) >= inst->type_argc) + return FALSE; + break; + case MONO_TYPE_SZARRAY: + return mono_type_is_valid_type_in_context_full (&type->data.klass->byval_arg, context, check_gtd); + case MONO_TYPE_ARRAY: + return mono_type_is_valid_type_in_context_full (&type->data.array->eklass->byval_arg, context, check_gtd); + case MONO_TYPE_PTR: + return mono_type_is_valid_type_in_context_full (type->data.type, context, check_gtd); + case MONO_TYPE_GENERICINST: + inst = type->data.generic_class->context.class_inst; + if (!inst->is_open) + break; + for (i = 0; i < inst->type_argc; ++i) + if (!mono_type_is_valid_type_in_context_full (inst->type_argv [i], context, check_gtd)) + return FALSE; + break; + case MONO_TYPE_CLASS: + case MONO_TYPE_VALUETYPE: { + MonoClass *klass = type->data.klass; + /* + * It's possible to encode generic'sh types in such a way that they disguise themselves as class or valuetype. + * Fixing the type decoding is really tricky since under some cases this behavior is needed, for example, to + * have a 'class' type pointing to a 'genericinst' class. + * + * For the runtime these non canonical (weird) encodings work fine, the worst they can cause is some + * reflection oddities which are harmless - to security at least. + */ + if (klass->byval_arg.type != type->type) + return mono_type_is_valid_type_in_context_full (&klass->byval_arg, context, check_gtd); + + if (check_gtd && mono_class_is_gtd (klass)) + return FALSE; + break; + } + default: + break; + } + return TRUE; +} + +static gboolean +mono_type_is_valid_type_in_context (MonoType *type, MonoGenericContext *context) +{ + return mono_type_is_valid_type_in_context_full (type, context, FALSE); +} + +/*This function returns NULL if the type is not instantiatable*/ +static MonoType* +verifier_inflate_type (VerifyContext *ctx, MonoType *type, MonoGenericContext *context) +{ + MonoError error; + MonoType *result; + + result = mono_class_inflate_generic_type_checked (type, context, &error); + if (!mono_error_ok (&error)) { + mono_error_cleanup (&error); + return NULL; + } + return result; +} + +/*A side note here. We don't need to check if arguments are broken since this +is only need to be done by the runtime before realizing the type. +*/ +static gboolean +is_valid_generic_instantiation (MonoGenericContainer *gc, MonoGenericContext *context, MonoGenericInst *ginst) +{ + MonoError error; + int i; + + if (ginst->type_argc != gc->type_argc) + return FALSE; + + for (i = 0; i < gc->type_argc; ++i) { + MonoGenericParamInfo *param_info = mono_generic_container_get_param_info (gc, i); + MonoClass *paramClass; + MonoClass **constraints; + MonoType *param_type = ginst->type_argv [i]; + + /*it's not our job to validate type variables*/ + if (mono_type_is_generic_argument (param_type)) + continue; + + paramClass = mono_class_from_mono_type (param_type); + + + /* A GTD can't be a generic argument. + * + * Due to how types are encoded we must check for the case of a genericinst MonoType and GTD MonoClass. + * This happens in cases such as: class Foo { void X() { new Bar (); } } + * + * Open instantiations can have GTDs as this happens when one type is instantiated with others params + * and the former has an expansion into the later. For example: + * class B {} + * class A: B {} + * The type A has a parent B, that is inflated into the GTD B<>. + * Since A is open, thus not instantiatable, this is valid. + */ + if (mono_class_is_gtd (paramClass) && param_type->type != MONO_TYPE_GENERICINST && !ginst->is_open) + return FALSE; + + /*it's not safe to call mono_class_init from here*/ + if (mono_class_is_ginst (paramClass) && !paramClass->inited) { + if (!mono_class_is_valid_generic_instantiation (NULL, paramClass)) + return FALSE; + } + + if (!param_info->constraints && !(param_info->flags & GENERIC_PARAMETER_ATTRIBUTE_SPECIAL_CONSTRAINTS_MASK)) + continue; + + if ((param_info->flags & GENERIC_PARAMETER_ATTRIBUTE_VALUE_TYPE_CONSTRAINT) && (!paramClass->valuetype || mono_class_is_nullable (paramClass))) + return FALSE; + + if ((param_info->flags & GENERIC_PARAMETER_ATTRIBUTE_REFERENCE_TYPE_CONSTRAINT) && paramClass->valuetype) + return FALSE; + + if ((param_info->flags & GENERIC_PARAMETER_ATTRIBUTE_CONSTRUCTOR_CONSTRAINT) && !paramClass->valuetype && !mono_class_has_default_constructor (paramClass)) + return FALSE; + + if (!param_info->constraints) + continue; + + for (constraints = param_info->constraints; *constraints; ++constraints) { + MonoClass *ctr = *constraints; + MonoType *inflated; + + inflated = mono_class_inflate_generic_type_checked (&ctr->byval_arg, context, &error); + if (!mono_error_ok (&error)) { + mono_error_cleanup (&error); + return FALSE; + } + ctr = mono_class_from_mono_type (inflated); + mono_metadata_free_type (inflated); + + /*FIXME maybe we need the same this as verifier_class_is_assignable_from*/ + if (!mono_class_is_assignable_from_slow (ctr, paramClass)) + return FALSE; + } + } + return TRUE; +} + +/** + * mono_generic_param_is_constraint_compatible: + * + * \returns TRUE if \p candidate is constraint compatible with \p target. + * + * This means that \p candidate constraints are a super set of \p target constaints + */ +static gboolean +mono_generic_param_is_constraint_compatible (VerifyContext *ctx, MonoGenericParam *target, MonoGenericParam *candidate, MonoClass *candidate_param_class, MonoGenericContext *context) +{ + MonoGenericParamInfo *tinfo = mono_generic_param_info (target); + MonoGenericParamInfo *cinfo = mono_generic_param_info (candidate); + MonoClass **candidate_class; + gboolean class_constraint_satisfied = FALSE; + gboolean valuetype_constraint_satisfied = FALSE; + + int tmask = tinfo->flags & GENERIC_PARAMETER_ATTRIBUTE_SPECIAL_CONSTRAINTS_MASK; + int cmask = cinfo->flags & GENERIC_PARAMETER_ATTRIBUTE_SPECIAL_CONSTRAINTS_MASK; + + if (cinfo->constraints) { + for (candidate_class = cinfo->constraints; *candidate_class; ++candidate_class) { + MonoClass *cc; + MonoType *inflated = verifier_inflate_type (ctx, &(*candidate_class)->byval_arg, ctx->generic_context); + if (!inflated) + return FALSE; + cc = mono_class_from_mono_type (inflated); + mono_metadata_free_type (inflated); + + if (mono_type_is_reference (&cc->byval_arg) && !MONO_CLASS_IS_INTERFACE (cc)) + class_constraint_satisfied = TRUE; + else if (!mono_type_is_reference (&cc->byval_arg) && !MONO_CLASS_IS_INTERFACE (cc)) + valuetype_constraint_satisfied = TRUE; + } + } + class_constraint_satisfied |= (cmask & GENERIC_PARAMETER_ATTRIBUTE_REFERENCE_TYPE_CONSTRAINT) != 0; + valuetype_constraint_satisfied |= (cmask & GENERIC_PARAMETER_ATTRIBUTE_VALUE_TYPE_CONSTRAINT) != 0; + + if ((tmask & GENERIC_PARAMETER_ATTRIBUTE_REFERENCE_TYPE_CONSTRAINT) && !class_constraint_satisfied) + return FALSE; + if ((tmask & GENERIC_PARAMETER_ATTRIBUTE_VALUE_TYPE_CONSTRAINT) && !valuetype_constraint_satisfied) + return FALSE; + if ((tmask & GENERIC_PARAMETER_ATTRIBUTE_CONSTRUCTOR_CONSTRAINT) && !((cmask & GENERIC_PARAMETER_ATTRIBUTE_CONSTRUCTOR_CONSTRAINT) || + valuetype_constraint_satisfied)) { + return FALSE; + } + + + if (tinfo->constraints) { + MonoClass **target_class; + for (target_class = tinfo->constraints; *target_class; ++target_class) { + MonoClass *tc; + MonoType *inflated = verifier_inflate_type (ctx, &(*target_class)->byval_arg, context); + if (!inflated) + return FALSE; + tc = mono_class_from_mono_type (inflated); + mono_metadata_free_type (inflated); + + /* + * A constraint from @target might inflate into @candidate itself and in that case we don't need + * check it's constraints since it satisfy the constraint by itself. + */ + if (mono_metadata_type_equal (&tc->byval_arg, &candidate_param_class->byval_arg)) + continue; + + if (!cinfo->constraints) + return FALSE; + + for (candidate_class = cinfo->constraints; *candidate_class; ++candidate_class) { + MonoClass *cc; + inflated = verifier_inflate_type (ctx, &(*candidate_class)->byval_arg, ctx->generic_context); + if (!inflated) + return FALSE; + cc = mono_class_from_mono_type (inflated); + mono_metadata_free_type (inflated); + + if (verifier_class_is_assignable_from (tc, cc)) + break; + + /* + * This happens when we have the following: + * + * Bar where K : IFace + * Foo where T : U where U : IFace + * ... + * Bar <- T here satisfy K constraint transitively through to U's constraint + * + */ + if (mono_type_is_generic_argument (&cc->byval_arg)) { + MonoGenericParam *other_candidate = verifier_get_generic_param_from_type (ctx, &cc->byval_arg); + + if (mono_generic_param_is_constraint_compatible (ctx, target, other_candidate, cc, context)) { + break; + } + } + } + if (!*candidate_class) + return FALSE; + } + } + return TRUE; +} + +static MonoGenericParam* +verifier_get_generic_param_from_type (VerifyContext *ctx, MonoType *type) +{ + MonoGenericContainer *gc; + MonoMethod *method = ctx->method; + int num; + + num = mono_type_get_generic_param_num (type); + + if (type->type == MONO_TYPE_VAR) { + MonoClass *gtd = method->klass; + if (mono_class_is_ginst (gtd)) + gtd = mono_class_get_generic_class (gtd)->container_class; + gc = mono_class_try_get_generic_container (gtd); + } else { //MVAR + MonoMethod *gmd = method; + if (method->is_inflated) + gmd = ((MonoMethodInflated*)method)->declaring; + gc = mono_method_get_generic_container (gmd); + } + if (!gc) + return NULL; + return mono_generic_container_get_param (gc, num); +} + + + +/* + * Verify if @type is valid for the given @ctx verification context. + * this function checks for VAR and MVAR types that are invalid under the current verifier, + * This means that it either + */ +static gboolean +is_valid_type_in_context (VerifyContext *ctx, MonoType *type) +{ + return mono_type_is_valid_type_in_context (type, ctx->generic_context); +} + +static gboolean +is_valid_generic_instantiation_in_context (VerifyContext *ctx, MonoGenericInst *ginst, gboolean check_gtd) +{ + int i; + for (i = 0; i < ginst->type_argc; ++i) { + MonoType *type = ginst->type_argv [i]; + + if (!mono_type_is_valid_type_in_context_full (type, ctx->generic_context, TRUE)) + return FALSE; + } + return TRUE; +} + +static gboolean +generic_arguments_respect_constraints (VerifyContext *ctx, MonoGenericContainer *gc, MonoGenericContext *context, MonoGenericInst *ginst) +{ + int i; + for (i = 0; i < ginst->type_argc; ++i) { + MonoType *type = ginst->type_argv [i]; + MonoGenericParam *target = mono_generic_container_get_param (gc, i); + MonoGenericParam *candidate; + MonoClass *candidate_class; + + if (!mono_type_is_generic_argument (type)) + continue; + + if (!is_valid_type_in_context (ctx, type)) + return FALSE; + + candidate = verifier_get_generic_param_from_type (ctx, type); + candidate_class = mono_class_from_mono_type (type); + + if (!mono_generic_param_is_constraint_compatible (ctx, target, candidate, candidate_class, context)) + return FALSE; + } + return TRUE; +} + +static gboolean +mono_method_repect_method_constraints (VerifyContext *ctx, MonoMethod *method) +{ + MonoMethodInflated *gmethod = (MonoMethodInflated *)method; + MonoGenericInst *ginst = gmethod->context.method_inst; + MonoGenericContainer *gc = mono_method_get_generic_container (gmethod->declaring); + return !gc || generic_arguments_respect_constraints (ctx, gc, &gmethod->context, ginst); +} + +static gboolean +mono_class_repect_method_constraints (VerifyContext *ctx, MonoClass *klass) +{ + MonoGenericClass *gklass = mono_class_get_generic_class (klass); + MonoGenericInst *ginst = gklass->context.class_inst; + MonoGenericContainer *gc = mono_class_get_generic_container (gklass->container_class); + return !gc || generic_arguments_respect_constraints (ctx, gc, &gklass->context, ginst); +} + +static gboolean +mono_method_is_valid_generic_instantiation (VerifyContext *ctx, MonoMethod *method) +{ + MonoMethodInflated *gmethod = (MonoMethodInflated *)method; + MonoGenericInst *ginst = gmethod->context.method_inst; + MonoGenericContainer *gc = mono_method_get_generic_container (gmethod->declaring); + if (!gc) /*non-generic inflated method - it's part of a generic type */ + return TRUE; + if (ctx && !is_valid_generic_instantiation_in_context (ctx, ginst, TRUE)) + return FALSE; + return is_valid_generic_instantiation (gc, &gmethod->context, ginst); + +} + +static gboolean +mono_class_is_valid_generic_instantiation (VerifyContext *ctx, MonoClass *klass) +{ + MonoGenericClass *gklass = mono_class_get_generic_class (klass); + MonoGenericInst *ginst = gklass->context.class_inst; + MonoGenericContainer *gc = mono_class_get_generic_container (gklass->container_class); + if (ctx && !is_valid_generic_instantiation_in_context (ctx, ginst, TRUE)) + return FALSE; + return is_valid_generic_instantiation (gc, &gklass->context, ginst); +} + +static gboolean +mono_type_is_valid_in_context (VerifyContext *ctx, MonoType *type) +{ + MonoClass *klass; + + if (type == NULL) { + ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Invalid null type at 0x%04x", ctx->ip_offset), MONO_EXCEPTION_BAD_IMAGE); + return FALSE; + } + + if (!is_valid_type_in_context (ctx, type)) { + char *str = mono_type_full_name (type); + ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Invalid generic type (%s%s) (argument out of range or %s is not generic) at 0x%04x", + str [0] == '!' ? "" : type->type == MONO_TYPE_VAR ? "!" : "!!", + str, + type->type == MONO_TYPE_VAR ? "class" : "method", + ctx->ip_offset), + MONO_EXCEPTION_BAD_IMAGE); + g_free (str); + return FALSE; + } + + klass = mono_class_from_mono_type (type); + mono_class_init (klass); + if (mono_class_has_failure (klass)) { + if (mono_class_is_ginst (klass) && !mono_class_is_valid_generic_instantiation (NULL, klass)) + ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Invalid generic instantiation of type %s.%s at 0x%04x", klass->name_space, klass->name, ctx->ip_offset), MONO_EXCEPTION_TYPE_LOAD); + else + ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Could not load type %s.%s at 0x%04x", klass->name_space, klass->name, ctx->ip_offset), MONO_EXCEPTION_TYPE_LOAD); + return FALSE; + } + + if (mono_class_is_ginst (klass) && mono_class_has_failure (mono_class_get_generic_class (klass)->container_class)) { + ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Could not load type %s.%s at 0x%04x", klass->name_space, klass->name, ctx->ip_offset), MONO_EXCEPTION_TYPE_LOAD); + return FALSE; + } + + if (!mono_class_is_ginst (klass)) + return TRUE; + + if (!mono_class_is_valid_generic_instantiation (ctx, klass)) { + ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Invalid generic type instantiation of type %s.%s at 0x%04x", klass->name_space, klass->name, ctx->ip_offset), MONO_EXCEPTION_TYPE_LOAD); + return FALSE; + } + + if (!mono_class_repect_method_constraints (ctx, klass)) { + ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Invalid generic type instantiation of type %s.%s (generic args don't respect target's constraints) at 0x%04x", klass->name_space, klass->name, ctx->ip_offset), MONO_EXCEPTION_TYPE_LOAD); + return FALSE; + } + + return TRUE; +} + +static verify_result_t +mono_method_is_valid_in_context (VerifyContext *ctx, MonoMethod *method) +{ + if (!mono_type_is_valid_in_context (ctx, &method->klass->byval_arg)) + return RESULT_INVALID; + + if (!method->is_inflated) + return RESULT_VALID; + + if (!mono_method_is_valid_generic_instantiation (ctx, method)) { + ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Invalid generic method instantiation of method %s.%s::%s at 0x%04x", method->klass->name_space, method->klass->name, method->name, ctx->ip_offset), MONO_EXCEPTION_UNVERIFIABLE_IL); + return RESULT_INVALID; + } + + if (!mono_method_repect_method_constraints (ctx, method)) { + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid generic method instantiation of method %s.%s::%s (generic args don't respect target's constraints) at 0x%04x", method->klass->name_space, method->klass->name, method->name, ctx->ip_offset)); + return RESULT_UNVERIFIABLE; + } + return RESULT_VALID; +} + + +static MonoClassField* +verifier_load_field (VerifyContext *ctx, int token, MonoClass **out_klass, const char *opcode) { + MonoError error; + MonoClassField *field; + MonoClass *klass = NULL; + + if (ctx->method->wrapper_type != MONO_WRAPPER_NONE) { + field = (MonoClassField *)mono_method_get_wrapper_data (ctx->method, (guint32)token); + klass = field ? field->parent : NULL; + } else { + if (!IS_FIELD_DEF_OR_REF (token) || !token_bounds_check (ctx->image, token)) { + ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Invalid field token 0x%08x for %s at 0x%04x", token, opcode, ctx->ip_offset), MONO_EXCEPTION_BAD_IMAGE); + return NULL; + } + + field = mono_field_from_token_checked (ctx->image, token, &klass, ctx->generic_context, &error); + mono_error_cleanup (&error); /*FIXME don't swallow the error */ + } + + if (!field || !field->parent || !klass) { + ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Cannot load field from token 0x%08x for %s at 0x%04x", token, opcode, ctx->ip_offset), MONO_EXCEPTION_BAD_IMAGE); + return NULL; + } + + if (!mono_type_is_valid_in_context (ctx, &klass->byval_arg)) + return NULL; + + if (mono_field_get_flags (field) & FIELD_ATTRIBUTE_LITERAL) { + char *type_name = mono_type_get_full_name (field->parent); + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Cannot reference literal field %s::%s at 0x%04x", type_name, field->name, ctx->ip_offset)); + g_free (type_name); + return NULL; + } + + *out_klass = klass; + return field; +} + +static MonoMethod* +verifier_load_method (VerifyContext *ctx, int token, const char *opcode) { + MonoMethod* method; + + + if (ctx->method->wrapper_type != MONO_WRAPPER_NONE) { + method = (MonoMethod *)mono_method_get_wrapper_data (ctx->method, (guint32)token); + } else { + MonoError error; + if (!IS_METHOD_DEF_OR_REF_OR_SPEC (token) || !token_bounds_check (ctx->image, token)) { + ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Invalid method token 0x%08x for %s at 0x%04x", token, opcode, ctx->ip_offset), MONO_EXCEPTION_BAD_IMAGE); + return NULL; + } + + method = mono_get_method_checked (ctx->image, token, NULL, ctx->generic_context, &error); + mono_error_cleanup (&error); /* FIXME don't swallow this error */ + } + + if (!method) { + ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Cannot load method from token 0x%08x for %s at 0x%04x", token, opcode, ctx->ip_offset), MONO_EXCEPTION_BAD_IMAGE); + return NULL; + } + + if (mono_method_is_valid_in_context (ctx, method) == RESULT_INVALID) + return NULL; + + return method; +} + +static MonoType* +verifier_load_type (VerifyContext *ctx, int token, const char *opcode) { + MonoType* type; + + if (ctx->method->wrapper_type != MONO_WRAPPER_NONE) { + MonoClass *klass = (MonoClass *)mono_method_get_wrapper_data (ctx->method, (guint32)token); + type = klass ? &klass->byval_arg : NULL; + } else { + MonoError error; + if (!IS_TYPE_DEF_OR_REF_OR_SPEC (token) || !token_bounds_check (ctx->image, token)) { + ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Invalid type token 0x%08x at 0x%04x", token, ctx->ip_offset), MONO_EXCEPTION_BAD_IMAGE); + return NULL; + } + type = mono_type_get_checked (ctx->image, token, ctx->generic_context, &error); + mono_error_cleanup (&error); /*FIXME don't swallow the error */ + } + + if (!type) { + ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Cannot load type from token 0x%08x for %s at 0x%04x", token, opcode, ctx->ip_offset), MONO_EXCEPTION_BAD_IMAGE); + return NULL; + } + + if (!mono_type_is_valid_in_context (ctx, type)) + return NULL; + + return type; +} + + +/* stack_slot_get_type: + * + * Returns the stack type of @value. This value includes POINTER_MASK. + * + * Use this function to checks that account for a managed pointer. + */ +static gint32 +stack_slot_get_type (ILStackDesc *value) +{ + return value->stype & RAW_TYPE_MASK; +} + +/* stack_slot_get_underlying_type: + * + * Returns the stack type of @value. This value does not include POINTER_MASK. + * + * Use this function is cases where the fact that the value could be a managed pointer is + * irrelevant. For example, field load doesn't care about this fact of type on stack. + */ +static gint32 +stack_slot_get_underlying_type (ILStackDesc *value) +{ + return value->stype & TYPE_MASK; +} + +/* stack_slot_is_managed_pointer: + * + * Returns TRUE is @value is a managed pointer. + */ +static gboolean +stack_slot_is_managed_pointer (ILStackDesc *value) +{ + return (value->stype & POINTER_MASK) == POINTER_MASK; +} + +/* stack_slot_is_managed_mutability_pointer: + * + * Returns TRUE is @value is a managed mutability pointer. + */ +static G_GNUC_UNUSED gboolean +stack_slot_is_managed_mutability_pointer (ILStackDesc *value) +{ + return (value->stype & CMMP_MASK) == CMMP_MASK; +} + +/* stack_slot_is_null_literal: + * + * Returns TRUE is @value is the null literal. + */ +static gboolean +stack_slot_is_null_literal (ILStackDesc *value) +{ + return (value->stype & NULL_LITERAL_MASK) == NULL_LITERAL_MASK; +} + + +/* stack_slot_is_this_pointer: + * + * Returns TRUE is @value is the this literal + */ +static gboolean +stack_slot_is_this_pointer (ILStackDesc *value) +{ + return (value->stype & THIS_POINTER_MASK) == THIS_POINTER_MASK; +} + +/* stack_slot_is_boxed_value: + * + * Returns TRUE is @value is a boxed value + */ +static gboolean +stack_slot_is_boxed_value (ILStackDesc *value) +{ + return (value->stype & BOXED_MASK) == BOXED_MASK; +} + +/* stack_slot_is_safe_byref: + * + * Returns TRUE is @value is a safe byref + */ +static gboolean +stack_slot_is_safe_byref (ILStackDesc *value) +{ + return (value->stype & SAFE_BYREF_MASK) == SAFE_BYREF_MASK; +} + +static const char * +stack_slot_get_name (ILStackDesc *value) +{ + return type_names [value->stype & TYPE_MASK]; +} + +enum { + SAFE_BYREF_LOCAL = 1, + UNSAFE_BYREF_LOCAL = 2 +}; +static gboolean +local_is_safe_byref (VerifyContext *ctx, unsigned int arg) +{ + return ctx->locals_verification_state [arg] == SAFE_BYREF_LOCAL; +} + +static gboolean +local_is_unsafe_byref (VerifyContext *ctx, unsigned int arg) +{ + return ctx->locals_verification_state [arg] == UNSAFE_BYREF_LOCAL; +} + +#define APPEND_WITH_PREDICATE(PRED,NAME) do {\ + if (PRED (value)) { \ + if (!first) \ + g_string_append (str, ", "); \ + g_string_append (str, NAME); \ + first = FALSE; \ + } } while (0) + +static char* +stack_slot_stack_type_full_name (ILStackDesc *value) +{ + GString *str = g_string_new (""); + char *result; + gboolean has_pred = FALSE, first = TRUE; + + if ((value->stype & TYPE_MASK) != value->stype) { + g_string_append(str, "["); + APPEND_WITH_PREDICATE (stack_slot_is_this_pointer, "this"); + APPEND_WITH_PREDICATE (stack_slot_is_boxed_value, "boxed"); + APPEND_WITH_PREDICATE (stack_slot_is_null_literal, "null"); + APPEND_WITH_PREDICATE (stack_slot_is_managed_mutability_pointer, "cmmp"); + APPEND_WITH_PREDICATE (stack_slot_is_managed_pointer, "mp"); + APPEND_WITH_PREDICATE (stack_slot_is_safe_byref, "safe-byref"); + has_pred = TRUE; + } + + if (mono_type_is_generic_argument (value->type) && !stack_slot_is_boxed_value (value)) { + if (!has_pred) + g_string_append(str, "["); + if (!first) + g_string_append (str, ", "); + g_string_append (str, "unboxed"); + has_pred = TRUE; + } + + if (has_pred) + g_string_append(str, "] "); + + g_string_append (str, stack_slot_get_name (value)); + result = str->str; + g_string_free (str, FALSE); + return result; +} + +static char* +stack_slot_full_name (ILStackDesc *value) +{ + char *type_name = mono_type_full_name (value->type); + char *stack_name = stack_slot_stack_type_full_name (value); + char *res = g_strdup_printf ("%s (%s)", type_name, stack_name); + g_free (type_name); + g_free (stack_name); + return res; +} + +////////////////////////////////////////////////////////////////// + +/** + * mono_free_verify_list: + */ +void +mono_free_verify_list (GSList *list) +{ + MonoVerifyInfoExtended *info; + GSList *tmp; + + for (tmp = list; tmp; tmp = tmp->next) { + info = (MonoVerifyInfoExtended *)tmp->data; + g_free (info->info.message); + g_free (info); + } + g_slist_free (list); +} + +#define ADD_ERROR(list,msg) \ + do { \ + MonoVerifyInfoExtended *vinfo = g_new (MonoVerifyInfoExtended, 1); \ + vinfo->info.status = MONO_VERIFY_ERROR; \ + vinfo->info.message = (msg); \ + (list) = g_slist_prepend ((list), vinfo); \ + } while (0) + +#define ADD_WARN(list,code,msg) \ + do { \ + MonoVerifyInfoExtended *vinfo = g_new (MonoVerifyInfoExtended, 1); \ + vinfo->info.status = (code); \ + vinfo->info.message = (msg); \ + (list) = g_slist_prepend ((list), vinfo); \ + } while (0) + +#define ADD_INVALID(list,msg) \ + do { \ + MonoVerifyInfoExtended *vinfo = g_new (MonoVerifyInfoExtended, 1); \ + vinfo->status = MONO_VERIFY_ERROR; \ + vinfo->message = (msg); \ + (list) = g_slist_prepend ((list), vinfo); \ + /*G_BREAKPOINT ();*/ \ + goto invalid_cil; \ + } while (0) + +#define CHECK_STACK_UNDERFLOW(num) \ + do { \ + if (cur_stack < (num)) \ + ADD_INVALID (list, g_strdup_printf ("Stack underflow at 0x%04x (%d items instead of %d)", ip_offset, cur_stack, (num))); \ + } while (0) + +#define CHECK_STACK_OVERFLOW() \ + do { \ + if (cur_stack >= max_stack) \ + ADD_INVALID (list, g_strdup_printf ("Maxstack exceeded at 0x%04x", ip_offset)); \ + } while (0) + + +static int +in_any_block (MonoMethodHeader *header, guint offset) +{ + int i; + MonoExceptionClause *clause; + + for (i = 0; i < header->num_clauses; ++i) { + clause = &header->clauses [i]; + if (MONO_OFFSET_IN_CLAUSE (clause, offset)) + return 1; + if (MONO_OFFSET_IN_HANDLER (clause, offset)) + return 1; + if (MONO_OFFSET_IN_FILTER (clause, offset)) + return 1; + } + return 0; +} + +/* + * in_any_exception_block: + * + * Returns TRUE is @offset is part of any exception clause (filter, handler, catch, finally or fault). + */ +static gboolean +in_any_exception_block (MonoMethodHeader *header, guint offset) +{ + int i; + MonoExceptionClause *clause; + + for (i = 0; i < header->num_clauses; ++i) { + clause = &header->clauses [i]; + if (MONO_OFFSET_IN_HANDLER (clause, offset)) + return TRUE; + if (MONO_OFFSET_IN_FILTER (clause, offset)) + return TRUE; + } + return FALSE; +} + +/* + * is_valid_branch_instruction: + * + * Verify if it's valid to perform a branch from @offset to @target. + * This should be used with br and brtrue/false. + * It returns 0 if valid, 1 for unverifiable and 2 for invalid. + * The major difference from other similiar functions is that branching into a + * finally/fault block is invalid instead of just unverifiable. + */ +static int +is_valid_branch_instruction (MonoMethodHeader *header, guint offset, guint target) +{ + int i; + MonoExceptionClause *clause; + + for (i = 0; i < header->num_clauses; ++i) { + clause = &header->clauses [i]; + /*branching into a finally block is invalid*/ + if ((clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY || clause->flags == MONO_EXCEPTION_CLAUSE_FAULT) && + !MONO_OFFSET_IN_HANDLER (clause, offset) && + MONO_OFFSET_IN_HANDLER (clause, target)) + return 2; + + if (clause->try_offset != target && (MONO_OFFSET_IN_CLAUSE (clause, offset) ^ MONO_OFFSET_IN_CLAUSE (clause, target))) + return 1; + if (MONO_OFFSET_IN_HANDLER (clause, offset) ^ MONO_OFFSET_IN_HANDLER (clause, target)) + return 1; + if (MONO_OFFSET_IN_FILTER (clause, offset) ^ MONO_OFFSET_IN_FILTER (clause, target)) + return 1; + } + return 0; +} + +/* + * is_valid_cmp_branch_instruction: + * + * Verify if it's valid to perform a branch from @offset to @target. + * This should be used with binary comparison branching instruction, like beq, bge and similars. + * It returns 0 if valid, 1 for unverifiable and 2 for invalid. + * + * The major differences from other similar functions are that most errors lead to invalid + * code and only branching out of finally, filter or fault clauses is unverifiable. + */ +static int +is_valid_cmp_branch_instruction (MonoMethodHeader *header, guint offset, guint target) +{ + int i; + MonoExceptionClause *clause; + + for (i = 0; i < header->num_clauses; ++i) { + clause = &header->clauses [i]; + /*branching out of a handler or finally*/ + if (clause->flags != MONO_EXCEPTION_CLAUSE_NONE && + MONO_OFFSET_IN_HANDLER (clause, offset) && + !MONO_OFFSET_IN_HANDLER (clause, target)) + return 1; + + if (clause->try_offset != target && (MONO_OFFSET_IN_CLAUSE (clause, offset) ^ MONO_OFFSET_IN_CLAUSE (clause, target))) + return 2; + if (MONO_OFFSET_IN_HANDLER (clause, offset) ^ MONO_OFFSET_IN_HANDLER (clause, target)) + return 2; + if (MONO_OFFSET_IN_FILTER (clause, offset) ^ MONO_OFFSET_IN_FILTER (clause, target)) + return 2; + } + return 0; +} + +/* + * A leave can't escape a finally block + */ +static int +is_correct_leave (MonoMethodHeader *header, guint offset, guint target) +{ + int i; + MonoExceptionClause *clause; + + for (i = 0; i < header->num_clauses; ++i) { + clause = &header->clauses [i]; + if (clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY && MONO_OFFSET_IN_HANDLER (clause, offset) && !MONO_OFFSET_IN_HANDLER (clause, target)) + return 0; + if (MONO_OFFSET_IN_FILTER (clause, offset)) + return 0; + } + return 1; +} + +/* + * A rethrow can't happen outside of a catch handler. + */ +static int +is_correct_rethrow (MonoMethodHeader *header, guint offset) +{ + int i; + MonoExceptionClause *clause; + + for (i = 0; i < header->num_clauses; ++i) { + clause = &header->clauses [i]; + if (MONO_OFFSET_IN_HANDLER (clause, offset)) + return 1; + } + return 0; +} + +/* + * An endfinally can't happen outside of a finally/fault handler. + */ +static int +is_correct_endfinally (MonoMethodHeader *header, guint offset) +{ + int i; + MonoExceptionClause *clause; + + for (i = 0; i < header->num_clauses; ++i) { + clause = &header->clauses [i]; + if (MONO_OFFSET_IN_HANDLER (clause, offset) && (clause->flags == MONO_EXCEPTION_CLAUSE_FAULT || clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY)) + return 1; + } + return 0; +} + + +/* + * An endfilter can only happens inside a filter clause. + * In non-strict mode filter is allowed inside the handler clause too + */ +static MonoExceptionClause * +is_correct_endfilter (VerifyContext *ctx, guint offset) +{ + int i; + MonoExceptionClause *clause; + + for (i = 0; i < ctx->header->num_clauses; ++i) { + clause = &ctx->header->clauses [i]; + if (clause->flags != MONO_EXCEPTION_CLAUSE_FILTER) + continue; + if (MONO_OFFSET_IN_FILTER (clause, offset)) + return clause; + if (!IS_STRICT_MODE (ctx) && MONO_OFFSET_IN_HANDLER (clause, offset)) + return clause; + } + return NULL; +} + + +/* + * Non-strict endfilter can happens inside a try block or any handler block + */ +static int +is_unverifiable_endfilter (VerifyContext *ctx, guint offset) +{ + int i; + MonoExceptionClause *clause; + + for (i = 0; i < ctx->header->num_clauses; ++i) { + clause = &ctx->header->clauses [i]; + if (MONO_OFFSET_IN_CLAUSE (clause, offset)) + return 1; + } + return 0; +} + +static gboolean +is_valid_bool_arg (ILStackDesc *arg) +{ + if (stack_slot_is_managed_pointer (arg) || stack_slot_is_boxed_value (arg) || stack_slot_is_null_literal (arg)) + return TRUE; + + + switch (stack_slot_get_underlying_type (arg)) { + case TYPE_I4: + case TYPE_I8: + case TYPE_NATIVE_INT: + case TYPE_PTR: + return TRUE; + case TYPE_COMPLEX: + g_assert (arg->type); + switch (arg->type->type) { + case MONO_TYPE_CLASS: + case MONO_TYPE_STRING: + case MONO_TYPE_OBJECT: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_ARRAY: + case MONO_TYPE_FNPTR: + case MONO_TYPE_PTR: + return TRUE; + case MONO_TYPE_GENERICINST: + /*We need to check if the container class + * of the generic type is a valuetype, iow: + * is it a "class Foo" or a "struct Foo"? + */ + return !arg->type->data.generic_class->container_class->valuetype; + default: + return FALSE; + } + default: + return FALSE; + } +} + + +/*Type manipulation helper*/ + +/*Returns the byref version of the supplied MonoType*/ +static MonoType* +mono_type_get_type_byref (MonoType *type) +{ + if (type->byref) + return type; + return &mono_class_from_mono_type (type)->this_arg; +} + + +/*Returns the byval version of the supplied MonoType*/ +static MonoType* +mono_type_get_type_byval (MonoType *type) +{ + if (!type->byref) + return type; + return &mono_class_from_mono_type (type)->byval_arg; +} + +static MonoType* +mono_type_from_stack_slot (ILStackDesc *slot) +{ + if (stack_slot_is_managed_pointer (slot)) + return mono_type_get_type_byref (slot->type); + return slot->type; +} + +/*Stack manipulation code*/ + +static void +ensure_stack_size (ILCodeDesc *stack, int required) +{ + int new_size = 8; + ILStackDesc *tmp; + + if (required < stack->max_size) + return; + + /* We don't have to worry about the exponential growth since stack_copy prune unused space */ + new_size = MAX (8, MAX (required, stack->max_size * 2)); + + g_assert (new_size >= stack->size); + g_assert (new_size >= required); + + tmp = g_new0 (ILStackDesc, new_size); + _MEM_ALLOC (sizeof (ILStackDesc) * new_size); + + if (stack->stack) { + if (stack->size) + memcpy (tmp, stack->stack, stack->size * sizeof (ILStackDesc)); + g_free (stack->stack); + _MEM_FREE (sizeof (ILStackDesc) * stack->max_size); + } + + stack->stack = tmp; + stack->max_size = new_size; +} + +static void +stack_init (VerifyContext *ctx, ILCodeDesc *state) +{ + if (state->flags & IL_CODE_FLAG_STACK_INITED) + return; + state->size = state->max_size = 0; + state->flags |= IL_CODE_FLAG_STACK_INITED; +} + +static void +stack_copy (ILCodeDesc *to, ILCodeDesc *from) +{ + ensure_stack_size (to, from->size); + to->size = from->size; + + /*stack copy happens at merge points, which have small stacks*/ + if (from->size) + memcpy (to->stack, from->stack, sizeof (ILStackDesc) * from->size); +} + +static void +copy_stack_value (ILStackDesc *to, ILStackDesc *from) +{ + to->stype = from->stype; + to->type = from->type; + to->method = from->method; +} + +static int +check_underflow (VerifyContext *ctx, int size) +{ + if (ctx->eval.size < size) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Stack underflow, required %d, but have %d at 0x%04x", size, ctx->eval.size, ctx->ip_offset)); + return 0; + } + return 1; +} + +static int +check_overflow (VerifyContext *ctx) +{ + if (ctx->eval.size >= ctx->max_stack) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Method doesn't have stack-depth %d at 0x%04x", ctx->eval.size + 1, ctx->ip_offset)); + return 0; + } + return 1; +} + +/*This reject out PTR, FNPTR and TYPEDBYREF*/ +static gboolean +check_unmanaged_pointer (VerifyContext *ctx, ILStackDesc *value) +{ + if (stack_slot_get_type (value) == TYPE_PTR) { + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Unmanaged pointer is not a verifiable type at 0x%04x", ctx->ip_offset)); + return 0; + } + return 1; +} + +/*TODO verify if MONO_TYPE_TYPEDBYREF is not allowed here as well.*/ +static gboolean +check_unverifiable_type (VerifyContext *ctx, MonoType *type) +{ + if (type->type == MONO_TYPE_PTR || type->type == MONO_TYPE_FNPTR) { + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Unmanaged pointer is not a verifiable type at 0x%04x", ctx->ip_offset)); + return 0; + } + return 1; +} + +static ILStackDesc * +stack_push (VerifyContext *ctx) +{ + g_assert (ctx->eval.size < ctx->max_stack); + g_assert (ctx->eval.size <= ctx->eval.max_size); + + ensure_stack_size (&ctx->eval, ctx->eval.size + 1); + + return & ctx->eval.stack [ctx->eval.size++]; +} + +static ILStackDesc * +stack_push_val (VerifyContext *ctx, int stype, MonoType *type) +{ + ILStackDesc *top = stack_push (ctx); + top->stype = stype; + top->type = type; + return top; +} + +static ILStackDesc * +stack_pop (VerifyContext *ctx) +{ + ILStackDesc *ret; + g_assert (ctx->eval.size > 0); + ret = ctx->eval.stack + --ctx->eval.size; + if ((ret->stype & UNINIT_THIS_MASK) == UNINIT_THIS_MASK) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Found use of uninitialized 'this ptr' ref at 0x%04x", ctx->ip_offset)); + return ret; +} + +/* This function allows to safely pop an unititialized this ptr from + * the eval stack without marking the method as unverifiable. + */ +static ILStackDesc * +stack_pop_safe (VerifyContext *ctx) +{ + g_assert (ctx->eval.size > 0); + return ctx->eval.stack + --ctx->eval.size; +} + +/*Positive number distance from stack top. [0] is stack top, [1] is the one below*/ +static ILStackDesc* +stack_peek (VerifyContext *ctx, int distance) +{ + g_assert (ctx->eval.size - distance > 0); + return ctx->eval.stack + (ctx->eval.size - 1 - distance); +} + +static ILStackDesc * +stack_push_stack_val (VerifyContext *ctx, ILStackDesc *value) +{ + ILStackDesc *top = stack_push (ctx); + copy_stack_value (top, value); + return top; +} + +/* Returns the MonoType associated with the token, or NULL if it is invalid. + * + * A boxable type can be either a reference or value type, but cannot be a byref type or an unmanaged pointer + * */ +static MonoType* +get_boxable_mono_type (VerifyContext* ctx, int token, const char *opcode) +{ + MonoType *type; + MonoClass *klass; + + if (!(type = verifier_load_type (ctx, token, opcode))) + return NULL; + + if (type->byref && type->type != MONO_TYPE_TYPEDBYREF) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Invalid use of byref type for %s at 0x%04x", opcode, ctx->ip_offset)); + return NULL; + } + + if (type->type == MONO_TYPE_VOID) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Invalid use of void type for %s at 0x%04x", opcode, ctx->ip_offset)); + return NULL; + } + + if (type->type == MONO_TYPE_TYPEDBYREF) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid use of typedbyref for %s at 0x%04x", opcode, ctx->ip_offset)); + + if (!(klass = mono_class_from_mono_type (type))) + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Could not retrieve type token for %s at 0x%04x", opcode, ctx->ip_offset)); + + if (mono_class_is_gtd (klass) && type->type != MONO_TYPE_GENERICINST) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Cannot use the generic type definition in a boxable type position for %s at 0x%04x", opcode, ctx->ip_offset)); + + check_unverifiable_type (ctx, type); + return type; +} + + +/*operation result tables */ + +static const unsigned char bin_op_table [TYPE_MAX][TYPE_MAX] = { + {TYPE_I4, TYPE_INV, TYPE_NATIVE_INT, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_INV, TYPE_I8, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_NATIVE_INT, TYPE_INV, TYPE_NATIVE_INT, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_R8, TYPE_INV, TYPE_INV}, + {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, +}; + +static const unsigned char add_table [TYPE_MAX][TYPE_MAX] = { + {TYPE_I4, TYPE_INV, TYPE_NATIVE_INT, TYPE_INV, TYPE_PTR | NON_VERIFIABLE_RESULT, TYPE_INV}, + {TYPE_INV, TYPE_I8, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_NATIVE_INT, TYPE_INV, TYPE_NATIVE_INT, TYPE_INV, TYPE_PTR | NON_VERIFIABLE_RESULT, TYPE_INV}, + {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_R8, TYPE_INV, TYPE_INV}, + {TYPE_PTR | NON_VERIFIABLE_RESULT, TYPE_INV, TYPE_PTR | NON_VERIFIABLE_RESULT, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, +}; + +static const unsigned char sub_table [TYPE_MAX][TYPE_MAX] = { + {TYPE_I4, TYPE_INV, TYPE_NATIVE_INT, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_INV, TYPE_I8, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_NATIVE_INT, TYPE_INV, TYPE_NATIVE_INT, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_R8, TYPE_INV, TYPE_INV}, + {TYPE_PTR | NON_VERIFIABLE_RESULT, TYPE_INV, TYPE_PTR | NON_VERIFIABLE_RESULT, TYPE_INV, TYPE_NATIVE_INT | NON_VERIFIABLE_RESULT, TYPE_INV}, + {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, +}; + +static const unsigned char int_bin_op_table [TYPE_MAX][TYPE_MAX] = { + {TYPE_I4, TYPE_INV, TYPE_NATIVE_INT, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_INV, TYPE_I8, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_NATIVE_INT, TYPE_INV, TYPE_NATIVE_INT, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, +}; + +static const unsigned char shift_op_table [TYPE_MAX][TYPE_MAX] = { + {TYPE_I4, TYPE_INV, TYPE_I4, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_I8, TYPE_INV, TYPE_I8, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_NATIVE_INT, TYPE_INV, TYPE_NATIVE_INT, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, +}; + +static const unsigned char cmp_br_op [TYPE_MAX][TYPE_MAX] = { + {TYPE_I4, TYPE_INV, TYPE_I4, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_INV, TYPE_I4, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_I4, TYPE_INV, TYPE_I4, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_I4, TYPE_INV, TYPE_INV}, + {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_I4, TYPE_INV}, + {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, +}; + +static const unsigned char cmp_br_eq_op [TYPE_MAX][TYPE_MAX] = { + {TYPE_I4, TYPE_INV, TYPE_I4, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_INV, TYPE_I4, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_I4, TYPE_INV, TYPE_I4, TYPE_INV, TYPE_I4 | NON_VERIFIABLE_RESULT, TYPE_INV}, + {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_I4, TYPE_INV, TYPE_INV}, + {TYPE_INV, TYPE_INV, TYPE_I4 | NON_VERIFIABLE_RESULT, TYPE_INV, TYPE_I4, TYPE_INV}, + {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_I4}, +}; + +static const unsigned char add_ovf_un_table [TYPE_MAX][TYPE_MAX] = { + {TYPE_I4, TYPE_INV, TYPE_NATIVE_INT, TYPE_INV, TYPE_PTR | NON_VERIFIABLE_RESULT, TYPE_INV}, + {TYPE_INV, TYPE_I8, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_NATIVE_INT, TYPE_INV, TYPE_NATIVE_INT, TYPE_INV, TYPE_PTR | NON_VERIFIABLE_RESULT, TYPE_INV}, + {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_PTR | NON_VERIFIABLE_RESULT, TYPE_INV, TYPE_PTR | NON_VERIFIABLE_RESULT, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, +}; + +static const unsigned char sub_ovf_un_table [TYPE_MAX][TYPE_MAX] = { + {TYPE_I4, TYPE_INV, TYPE_NATIVE_INT, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_INV, TYPE_I8, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_NATIVE_INT, TYPE_INV, TYPE_NATIVE_INT, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_PTR | NON_VERIFIABLE_RESULT, TYPE_INV, TYPE_PTR | NON_VERIFIABLE_RESULT, TYPE_INV, TYPE_NATIVE_INT | NON_VERIFIABLE_RESULT, TYPE_INV}, + {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, +}; + +static const unsigned char bin_ovf_table [TYPE_MAX][TYPE_MAX] = { + {TYPE_I4, TYPE_INV, TYPE_NATIVE_INT, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_INV, TYPE_I8, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_NATIVE_INT, TYPE_INV, TYPE_NATIVE_INT, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, + {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, +}; + +#ifdef MONO_VERIFIER_DEBUG + +/*debug helpers */ +static void +dump_stack_value (ILStackDesc *value) +{ + printf ("[(%x)(%x)", value->type->type, value->stype); + + if (stack_slot_is_this_pointer (value)) + printf ("[this] "); + + if (stack_slot_is_boxed_value (value)) + printf ("[boxed] "); + + if (stack_slot_is_null_literal (value)) + printf ("[null] "); + + if (stack_slot_is_managed_mutability_pointer (value)) + printf ("Controled Mutability MP: "); + + if (stack_slot_is_managed_pointer (value)) + printf ("Managed Pointer to: "); + + if (stack_slot_is_safe_byref (value)) + printf ("Safe ByRef to: "); + + switch (stack_slot_get_underlying_type (value)) { + case TYPE_INV: + printf ("invalid type]"); + return; + case TYPE_I4: + printf ("int32]"); + return; + case TYPE_I8: + printf ("int64]"); + return; + case TYPE_NATIVE_INT: + printf ("native int]"); + return; + case TYPE_R8: + printf ("float64]"); + return; + case TYPE_PTR: + printf ("unmanaged pointer]"); + return; + case TYPE_COMPLEX: + switch (value->type->type) { + case MONO_TYPE_CLASS: + case MONO_TYPE_VALUETYPE: + printf ("complex] (%s)", value->type->data.klass->name); + return; + case MONO_TYPE_STRING: + printf ("complex] (string)"); + return; + case MONO_TYPE_OBJECT: + printf ("complex] (object)"); + return; + case MONO_TYPE_SZARRAY: + printf ("complex] (%s [])", value->type->data.klass->name); + return; + case MONO_TYPE_ARRAY: + printf ("complex] (%s [%d %d %d])", + value->type->data.array->eklass->name, + value->type->data.array->rank, + value->type->data.array->numsizes, + value->type->data.array->numlobounds); + return; + case MONO_TYPE_GENERICINST: + printf ("complex] (inst of %s )", value->type->data.generic_class->container_class->name); + return; + case MONO_TYPE_VAR: + printf ("complex] (type generic param !%d - %s) ", value->type->data.generic_param->num, mono_generic_param_info (value->type->data.generic_param)->name); + return; + case MONO_TYPE_MVAR: + printf ("complex] (method generic param !!%d - %s) ", value->type->data.generic_param->num, mono_generic_param_info (value->type->data.generic_param)->name); + return; + default: { + //should be a boxed value + char * name = mono_type_full_name (value->type); + printf ("complex] %s", name); + g_free (name); + return; + } + } + default: + printf ("unknown stack %x type]\n", value->stype); + g_assert_not_reached (); + } +} + +static void +dump_stack_state (ILCodeDesc *state) +{ + int i; + + printf ("(%d) ", state->size); + for (i = 0; i < state->size; ++i) + dump_stack_value (state->stack + i); + printf ("\n"); +} +#endif + +/** + * is_array_type_compatible: + * + * Returns TRUE if candidate array type can be assigned to target. + * + * Both parameters MUST be of type MONO_TYPE_ARRAY (target->type == MONO_TYPE_ARRAY) + */ +static gboolean +is_array_type_compatible (MonoType *target, MonoType *candidate) +{ + MonoArrayType *left = target->data.array; + MonoArrayType *right = candidate->data.array; + + g_assert (target->type == MONO_TYPE_ARRAY); + g_assert (candidate->type == MONO_TYPE_ARRAY); + + if (left->rank != right->rank) + return FALSE; + + return verifier_class_is_assignable_from (left->eklass, right->eklass); +} + +static int +get_stack_type (MonoType *type) +{ + int mask = 0; + int type_kind = type->type; + if (type->byref) + mask = POINTER_MASK; + /*TODO handle CMMP_MASK */ + +handle_enum: + switch (type_kind) { + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_CHAR: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + return TYPE_I4 | mask; + + case MONO_TYPE_I: + case MONO_TYPE_U: + return TYPE_NATIVE_INT | mask; + + /* FIXME: the spec says that you cannot have a pointer to method pointer, do we need to check this here? */ + case MONO_TYPE_FNPTR: + case MONO_TYPE_PTR: + case MONO_TYPE_TYPEDBYREF: + return TYPE_PTR | mask; + + case MONO_TYPE_VAR: + case MONO_TYPE_MVAR: + + case MONO_TYPE_CLASS: + case MONO_TYPE_STRING: + case MONO_TYPE_OBJECT: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_ARRAY: + return TYPE_COMPLEX | mask; + + case MONO_TYPE_I8: + case MONO_TYPE_U8: + return TYPE_I8 | mask; + + case MONO_TYPE_R4: + case MONO_TYPE_R8: + return TYPE_R8 | mask; + + case MONO_TYPE_GENERICINST: + case MONO_TYPE_VALUETYPE: + if (mono_type_is_enum_type (type)) { + type = mono_type_get_underlying_type_any (type); + if (!type) + return FALSE; + type_kind = type->type; + goto handle_enum; + } else { + return TYPE_COMPLEX | mask; + } + + default: + return TYPE_INV; + } +} + +/* convert MonoType to ILStackDesc format (stype) */ +static gboolean +set_stack_value (VerifyContext *ctx, ILStackDesc *stack, MonoType *type, int take_addr) +{ + int mask = 0; + int type_kind = type->type; + + if (type->byref || take_addr) + mask = POINTER_MASK; + /* TODO handle CMMP_MASK */ + +handle_enum: + stack->type = type; + + switch (type_kind) { + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_CHAR: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + stack->stype = TYPE_I4 | mask; + break; + case MONO_TYPE_I: + case MONO_TYPE_U: + stack->stype = TYPE_NATIVE_INT | mask; + break; + + /*FIXME: Do we need to check if it's a pointer to the method pointer? The spec says it' illegal to have that.*/ + case MONO_TYPE_FNPTR: + case MONO_TYPE_PTR: + case MONO_TYPE_TYPEDBYREF: + stack->stype = TYPE_PTR | mask; + break; + + case MONO_TYPE_CLASS: + case MONO_TYPE_STRING: + case MONO_TYPE_OBJECT: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_ARRAY: + + case MONO_TYPE_VAR: + case MONO_TYPE_MVAR: + stack->stype = TYPE_COMPLEX | mask; + break; + + case MONO_TYPE_I8: + case MONO_TYPE_U8: + stack->stype = TYPE_I8 | mask; + break; + case MONO_TYPE_R4: + case MONO_TYPE_R8: + stack->stype = TYPE_R8 | mask; + break; + case MONO_TYPE_GENERICINST: + case MONO_TYPE_VALUETYPE: + if (mono_type_is_enum_type (type)) { + MonoType *utype = mono_type_get_underlying_type_any (type); + if (!utype) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Could not resolve underlying type of %x at %d", type->type, ctx->ip_offset)); + return FALSE; + } + type = utype; + type_kind = type->type; + goto handle_enum; + } else { + stack->stype = TYPE_COMPLEX | mask; + break; + } + default: + VERIFIER_DEBUG ( printf ("unknown type 0x%02x in eval stack type\n", type->type); ); + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Illegal value set on stack 0x%02x at %d", type->type, ctx->ip_offset)); + return FALSE; + } + return TRUE; +} + +/* + * init_stack_with_value_at_exception_boundary: + * + * Initialize the stack and push a given type. + * The instruction is marked as been on the exception boundary. + */ +static void +init_stack_with_value_at_exception_boundary (VerifyContext *ctx, ILCodeDesc *code, MonoClass *klass) +{ + MonoError error; + MonoType *type = mono_class_inflate_generic_type_checked (&klass->byval_arg, ctx->generic_context, &error); + + if (!mono_error_ok (&error)) { + char *name = mono_type_get_full_name (klass); + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Invalid class %s used for exception", name)); + g_free (name); + mono_error_cleanup (&error); + return; + } + + if (!ctx->max_stack) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Stack overflow at 0x%04x", ctx->ip_offset)); + return; + } + + stack_init (ctx, code); + ensure_stack_size (code, 1); + set_stack_value (ctx, code->stack, type, FALSE); + ctx->exception_types = g_slist_prepend (ctx->exception_types, type); + code->size = 1; + code->flags |= IL_CODE_FLAG_WAS_TARGET; + if (mono_type_is_generic_argument (type)) + code->stack->stype |= BOXED_MASK; +} +/* Class lazy loading functions */ +static GENERATE_GET_CLASS_WITH_CACHE (ienumerable, "System.Collections.Generic", "IEnumerable`1") +static GENERATE_GET_CLASS_WITH_CACHE (icollection, "System.Collections.Generic", "ICollection`1") +static GENERATE_GET_CLASS_WITH_CACHE (ireadonly_list, "System.Collections.Generic", "IReadOnlyList`1") +static GENERATE_GET_CLASS_WITH_CACHE (ireadonly_collection, "System.Collections.Generic", "IReadOnlyCollection`1") + + +static MonoClass* +get_ienumerable_class (void) +{ + return mono_class_get_ienumerable_class (); +} + +static MonoClass* +get_icollection_class (void) +{ + return mono_class_get_icollection_class (); +} + +static MonoClass* +get_ireadonlylist_class (void) +{ + return mono_class_get_ireadonly_list_class (); +} + +static MonoClass* +get_ireadonlycollection_class (void) +{ + return mono_class_get_ireadonly_collection_class (); +} + +static MonoClass* +inflate_class_one_arg (MonoClass *gtype, MonoClass *arg0) +{ + MonoType *args [1]; + args [0] = &arg0->byval_arg; + + return mono_class_bind_generic_parameters (gtype, 1, args, FALSE); +} + +static gboolean +verifier_inflate_and_check_compat (MonoClass *target, MonoClass *gtd, MonoClass *arg) +{ + MonoClass *tmp; + if (!(tmp = inflate_class_one_arg (gtd, arg))) + return FALSE; + if (mono_class_is_variant_compatible (target, tmp, TRUE)) + return TRUE; + return FALSE; +} + +static gboolean +verifier_class_is_assignable_from (MonoClass *target, MonoClass *candidate) +{ + MonoClass *iface_gtd; + + if (target == candidate) + return TRUE; + + if (mono_class_has_variant_generic_params (target)) { + if (MONO_CLASS_IS_INTERFACE (target)) { + if (MONO_CLASS_IS_INTERFACE (candidate) && mono_class_is_variant_compatible (target, candidate, TRUE)) + return TRUE; + + if (candidate->rank == 1) { + if (verifier_inflate_and_check_compat (target, mono_defaults.generic_ilist_class, candidate->element_class)) + return TRUE; + if (verifier_inflate_and_check_compat (target, get_icollection_class (), candidate->element_class)) + return TRUE; + if (verifier_inflate_and_check_compat (target, get_ienumerable_class (), candidate->element_class)) + return TRUE; + if (verifier_inflate_and_check_compat (target, get_ireadonlylist_class (), candidate->element_class)) + return TRUE; + if (verifier_inflate_and_check_compat (target, get_ireadonlycollection_class (), candidate->element_class)) + return TRUE; + } else { + MonoError error; + int i; + while (candidate && candidate != mono_defaults.object_class) { + mono_class_setup_interfaces (candidate, &error); + if (!mono_error_ok (&error)) { + mono_error_cleanup (&error); + return FALSE; + } + + /*klass is a generic variant interface, We need to extract from oklass a list of ifaces which are viable candidates.*/ + for (i = 0; i < candidate->interface_offsets_count; ++i) { + MonoClass *iface = candidate->interfaces_packed [i]; + if (mono_class_is_variant_compatible (target, iface, TRUE)) + return TRUE; + } + + for (i = 0; i < candidate->interface_count; ++i) { + MonoClass *iface = candidate->interfaces [i]; + if (mono_class_is_variant_compatible (target, iface, TRUE)) + return TRUE; + } + candidate = candidate->parent; + } + } + } else if (target->delegate) { + if (mono_class_is_variant_compatible (target, candidate, TRUE)) + return TRUE; + } + return FALSE; + } + + if (mono_class_is_assignable_from (target, candidate)) + return TRUE; + + if (!MONO_CLASS_IS_INTERFACE (target) || !mono_class_is_ginst (target) || candidate->rank != 1) + return FALSE; + + iface_gtd = mono_class_get_generic_class (target)->container_class; + if (iface_gtd != mono_defaults.generic_ilist_class && iface_gtd != get_icollection_class () && iface_gtd != get_ienumerable_class ()) + return FALSE; + + target = mono_class_from_mono_type (mono_class_get_generic_class (target)->context.class_inst->type_argv [0]); + candidate = candidate->element_class; + + return TRUE; +} + +/*Verify if type 'candidate' can be stored in type 'target'. + * + * If strict, check for the underlying type and not the verification stack types + */ +static gboolean +verify_type_compatibility_full (VerifyContext *ctx, MonoType *target, MonoType *candidate, gboolean strict) +{ +#define IS_ONE_OF3(T, A, B, C) (T == A || T == B || T == C) +#define IS_ONE_OF2(T, A, B) (T == A || T == B) + + MonoType *original_candidate = candidate; + VERIFIER_DEBUG ( printf ("checking type compatibility %s x %s strict %d\n", mono_type_full_name (target), mono_type_full_name (candidate), strict); ); + + /*only one is byref */ + if (candidate->byref ^ target->byref) { + /* converting from native int to byref*/ + if (get_stack_type (candidate) == TYPE_NATIVE_INT && target->byref) { + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("using byref native int at 0x%04x", ctx->ip_offset)); + return TRUE; + } + return FALSE; + } + strict |= target->byref; + /*From now on we don't care about byref anymore, so it's ok to discard it here*/ + candidate = mono_type_get_underlying_type_any (candidate); + +handle_enum: + switch (target->type) { + case MONO_TYPE_VOID: + return candidate->type == MONO_TYPE_VOID; + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_BOOLEAN: + if (strict) + return IS_ONE_OF3 (candidate->type, MONO_TYPE_I1, MONO_TYPE_U1, MONO_TYPE_BOOLEAN); + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_CHAR: + if (strict) + return IS_ONE_OF3 (candidate->type, MONO_TYPE_I2, MONO_TYPE_U2, MONO_TYPE_CHAR); + case MONO_TYPE_I4: + case MONO_TYPE_U4: { + gboolean is_native_int = IS_ONE_OF2 (candidate->type, MONO_TYPE_I, MONO_TYPE_U); + gboolean is_int4 = IS_ONE_OF2 (candidate->type, MONO_TYPE_I4, MONO_TYPE_U4); + if (strict) + return is_native_int || is_int4; + return is_native_int || get_stack_type (candidate) == TYPE_I4; + } + + case MONO_TYPE_I8: + case MONO_TYPE_U8: + return IS_ONE_OF2 (candidate->type, MONO_TYPE_I8, MONO_TYPE_U8); + + case MONO_TYPE_R4: + case MONO_TYPE_R8: + if (strict) + return candidate->type == target->type; + return IS_ONE_OF2 (candidate->type, MONO_TYPE_R4, MONO_TYPE_R8); + + case MONO_TYPE_I: + case MONO_TYPE_U: { + gboolean is_native_int = IS_ONE_OF2 (candidate->type, MONO_TYPE_I, MONO_TYPE_U); + gboolean is_int4 = IS_ONE_OF2 (candidate->type, MONO_TYPE_I4, MONO_TYPE_U4); + if (strict) + return is_native_int || is_int4; + return is_native_int || get_stack_type (candidate) == TYPE_I4; + } + + case MONO_TYPE_PTR: + if (candidate->type != MONO_TYPE_PTR) + return FALSE; + /* check the underlying type */ + return verify_type_compatibility_full (ctx, target->data.type, candidate->data.type, TRUE); + + case MONO_TYPE_FNPTR: { + MonoMethodSignature *left, *right; + if (candidate->type != MONO_TYPE_FNPTR) + return FALSE; + + left = mono_type_get_signature (target); + right = mono_type_get_signature (candidate); + return mono_metadata_signature_equal (left, right) && left->call_convention == right->call_convention; + } + + case MONO_TYPE_GENERICINST: { + MonoClass *target_klass; + MonoClass *candidate_klass; + if (mono_type_is_enum_type (target)) { + target = mono_type_get_underlying_type_any (target); + if (!target) + return FALSE; + goto handle_enum; + } + /* + * VAR / MVAR compatibility must be checked by verify_stack_type_compatibility + * to take boxing status into account. + */ + if (mono_type_is_generic_argument (original_candidate)) + return FALSE; + + target_klass = mono_class_from_mono_type (target); + candidate_klass = mono_class_from_mono_type (candidate); + if (mono_class_is_nullable (target_klass)) { + if (!mono_class_is_nullable (candidate_klass)) + return FALSE; + return target_klass == candidate_klass; + } + return verifier_class_is_assignable_from (target_klass, candidate_klass); + } + + case MONO_TYPE_STRING: + return candidate->type == MONO_TYPE_STRING; + + case MONO_TYPE_CLASS: + /* + * VAR / MVAR compatibility must be checked by verify_stack_type_compatibility + * to take boxing status into account. + */ + if (mono_type_is_generic_argument (original_candidate)) + return FALSE; + + if (candidate->type == MONO_TYPE_VALUETYPE) + return FALSE; + + /* If candidate is an enum it should return true for System.Enum and supertypes. + * That's why here we use the original type and not the underlying type. + */ + return verifier_class_is_assignable_from (target->data.klass, mono_class_from_mono_type (original_candidate)); + + case MONO_TYPE_OBJECT: + return MONO_TYPE_IS_REFERENCE (candidate); + + case MONO_TYPE_SZARRAY: { + MonoClass *left; + MonoClass *right; + if (candidate->type != MONO_TYPE_SZARRAY) + return FALSE; + + left = mono_class_from_mono_type (target); + right = mono_class_from_mono_type (candidate); + + return verifier_class_is_assignable_from (left, right); + } + + case MONO_TYPE_ARRAY: + if (candidate->type != MONO_TYPE_ARRAY) + return FALSE; + return is_array_type_compatible (target, candidate); + + case MONO_TYPE_TYPEDBYREF: + return candidate->type == MONO_TYPE_TYPEDBYREF; + + case MONO_TYPE_VALUETYPE: { + MonoClass *target_klass; + MonoClass *candidate_klass; + + if (candidate->type == MONO_TYPE_CLASS) + return FALSE; + + target_klass = mono_class_from_mono_type (target); + candidate_klass = mono_class_from_mono_type (candidate); + if (target_klass == candidate_klass) + return TRUE; + if (mono_type_is_enum_type (target)) { + target = mono_type_get_underlying_type_any (target); + if (!target) + return FALSE; + goto handle_enum; + } + return FALSE; + } + + case MONO_TYPE_VAR: + if (candidate->type != MONO_TYPE_VAR) + return FALSE; + return mono_type_get_generic_param_num (candidate) == mono_type_get_generic_param_num (target); + + case MONO_TYPE_MVAR: + if (candidate->type != MONO_TYPE_MVAR) + return FALSE; + return mono_type_get_generic_param_num (candidate) == mono_type_get_generic_param_num (target); + + default: + VERIFIER_DEBUG ( printf ("unknown store type %d\n", target->type); ); + g_assert_not_reached (); + return FALSE; + } + return 1; +#undef IS_ONE_OF3 +#undef IS_ONE_OF2 +} + +static gboolean +verify_type_compatibility (VerifyContext *ctx, MonoType *target, MonoType *candidate) +{ + return verify_type_compatibility_full (ctx, target, candidate, FALSE); +} + +/* + * Returns the generic param bound to the context been verified. + * + */ +static MonoGenericParam* +get_generic_param (VerifyContext *ctx, MonoType *param) +{ + guint16 param_num = mono_type_get_generic_param_num (param); + if (param->type == MONO_TYPE_VAR) { + if (!ctx->generic_context->class_inst || ctx->generic_context->class_inst->type_argc <= param_num) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Invalid generic type argument %d", param_num)); + return NULL; + } + return ctx->generic_context->class_inst->type_argv [param_num]->data.generic_param; + } + + /*param must be a MVAR */ + if (!ctx->generic_context->method_inst || ctx->generic_context->method_inst->type_argc <= param_num) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Invalid generic method argument %d", param_num)); + return NULL; + } + return ctx->generic_context->method_inst->type_argv [param_num]->data.generic_param; + +} + +static gboolean +recursive_boxed_constraint_type_check (VerifyContext *ctx, MonoType *type, MonoClass *constraint_class, int recursion_level) +{ + MonoType *constraint_type = &constraint_class->byval_arg; + if (recursion_level <= 0) + return FALSE; + + if (verify_type_compatibility_full (ctx, type, mono_type_get_type_byval (constraint_type), FALSE)) + return TRUE; + + if (mono_type_is_generic_argument (constraint_type)) { + MonoGenericParam *param = get_generic_param (ctx, constraint_type); + MonoClass **klass; + if (!param) + return FALSE; + for (klass = mono_generic_param_info (param)->constraints; klass && *klass; ++klass) { + if (recursive_boxed_constraint_type_check (ctx, type, *klass, recursion_level - 1)) + return TRUE; + } + } + return FALSE; +} + +/** + * is_compatible_boxed_valuetype: + * + * Returns: TRUE if @candidate / @stack is a valid boxed valuetype. + * + * @type The source type. It it tested to be of the proper type. + * @candidate type of the boxed valuetype. + * @stack stack slot of the boxed valuetype, separate from @candidade since one could be changed before calling this function + * @strict if TRUE candidate must be boxed compatible to the target type + * + */ +static gboolean +is_compatible_boxed_valuetype (VerifyContext *ctx, MonoType *type, MonoType *candidate, ILStackDesc *stack, gboolean strict) +{ + if (!stack_slot_is_boxed_value (stack)) + return FALSE; + if (type->byref || candidate->byref) + return FALSE; + + if (mono_type_is_generic_argument (candidate)) { + MonoGenericParam *param = get_generic_param (ctx, candidate); + MonoClass **klass; + if (!param) + return FALSE; + + for (klass = mono_generic_param_info (param)->constraints; klass && *klass; ++klass) { + /*256 should be enough since there can't be more than 255 generic arguments.*/ + if (recursive_boxed_constraint_type_check (ctx, type, *klass, 256)) + return TRUE; + } + } + + if (mono_type_is_generic_argument (type)) + return FALSE; + + if (!strict) + return TRUE; + + return MONO_TYPE_IS_REFERENCE (type) && verifier_class_is_assignable_from (mono_class_from_mono_type (type), mono_class_from_mono_type (candidate)); +} + +static int +verify_stack_type_compatibility_full (VerifyContext *ctx, MonoType *type, ILStackDesc *stack, gboolean drop_byref, gboolean valuetype_must_be_boxed) +{ + MonoType *candidate = mono_type_from_stack_slot (stack); + if (MONO_TYPE_IS_REFERENCE (type) && !type->byref && stack_slot_is_null_literal (stack)) + return TRUE; + + if (is_compatible_boxed_valuetype (ctx, type, candidate, stack, TRUE)) + return TRUE; + + if (valuetype_must_be_boxed && !stack_slot_is_boxed_value (stack) && !MONO_TYPE_IS_REFERENCE (candidate)) + return FALSE; + + if (!valuetype_must_be_boxed && stack_slot_is_boxed_value (stack)) + return FALSE; + + if (drop_byref) + return verify_type_compatibility_full (ctx, type, mono_type_get_type_byval (candidate), FALSE); + + /* Handle how Roslyn emit fixed statements by encoding it as byref */ + if (type->byref && candidate->byref && (type->type == MONO_TYPE_I) && !mono_type_is_reference (candidate)) { + if (!IS_STRICT_MODE (ctx)) + return TRUE; + } + + return verify_type_compatibility_full (ctx, type, candidate, FALSE); +} + +static int +verify_stack_type_compatibility (VerifyContext *ctx, MonoType *type, ILStackDesc *stack) +{ + return verify_stack_type_compatibility_full (ctx, type, stack, FALSE, FALSE); +} + +static gboolean +mono_delegate_type_equal (MonoType *target, MonoType *candidate) +{ + if (candidate->byref ^ target->byref) + return FALSE; + + switch (target->type) { + case MONO_TYPE_VOID: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_CHAR: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_STRING: + case MONO_TYPE_TYPEDBYREF: + return candidate->type == target->type; + + case MONO_TYPE_PTR: + if (candidate->type != MONO_TYPE_PTR) + return FALSE; + return mono_delegate_type_equal (target->data.type, candidate->data.type); + + case MONO_TYPE_FNPTR: + if (candidate->type != MONO_TYPE_FNPTR) + return FALSE; + return mono_delegate_signature_equal (mono_type_get_signature (target), mono_type_get_signature (candidate), FALSE); + + case MONO_TYPE_GENERICINST: { + MonoClass *target_klass; + MonoClass *candidate_klass; + target_klass = mono_class_from_mono_type (target); + candidate_klass = mono_class_from_mono_type (candidate); + /*FIXME handle nullables and enum*/ + return verifier_class_is_assignable_from (target_klass, candidate_klass); + } + case MONO_TYPE_OBJECT: + return MONO_TYPE_IS_REFERENCE (candidate); + + case MONO_TYPE_CLASS: + return verifier_class_is_assignable_from(target->data.klass, mono_class_from_mono_type (candidate)); + + case MONO_TYPE_SZARRAY: + if (candidate->type != MONO_TYPE_SZARRAY) + return FALSE; + return verifier_class_is_assignable_from (mono_class_from_mono_type (target)->element_class, mono_class_from_mono_type (candidate)->element_class); + + case MONO_TYPE_ARRAY: + if (candidate->type != MONO_TYPE_ARRAY) + return FALSE; + return is_array_type_compatible (target, candidate); + + case MONO_TYPE_VALUETYPE: + /*FIXME handle nullables and enum*/ + return mono_class_from_mono_type (candidate) == mono_class_from_mono_type (target); + + case MONO_TYPE_VAR: + return candidate->type == MONO_TYPE_VAR && mono_type_get_generic_param_num (target) == mono_type_get_generic_param_num (candidate); + return FALSE; + + case MONO_TYPE_MVAR: + return candidate->type == MONO_TYPE_MVAR && mono_type_get_generic_param_num (target) == mono_type_get_generic_param_num (candidate); + return FALSE; + + default: + VERIFIER_DEBUG ( printf ("Unknown type %d. Implement me!\n", target->type); ); + g_assert_not_reached (); + return FALSE; + } +} + +static gboolean +mono_delegate_param_equal (MonoType *delegate, MonoType *method) +{ + if (mono_metadata_type_equal_full (delegate, method, TRUE)) + return TRUE; + + return mono_delegate_type_equal (method, delegate); +} + +static gboolean +mono_delegate_ret_equal (MonoType *delegate, MonoType *method) +{ + if (mono_metadata_type_equal_full (delegate, method, TRUE)) + return TRUE; + + return mono_delegate_type_equal (delegate, method); +} + +/* + * mono_delegate_signature_equal: + * + * Compare two signatures in the way expected by delegates. + * + * This function only exists due to the fact that it should ignore the 'has_this' part of the signature. + * + * FIXME can this function be eliminated and proper metadata functionality be used? + */ +static gboolean +mono_delegate_signature_equal (MonoMethodSignature *delegate_sig, MonoMethodSignature *method_sig, gboolean is_static_ldftn) +{ + int i; + int method_offset = is_static_ldftn ? 1 : 0; + + if (delegate_sig->param_count + method_offset != method_sig->param_count) + return FALSE; + + if (delegate_sig->call_convention != method_sig->call_convention) + return FALSE; + + for (i = 0; i < delegate_sig->param_count; i++) { + MonoType *p1 = delegate_sig->params [i]; + MonoType *p2 = method_sig->params [i + method_offset]; + + if (!mono_delegate_param_equal (p1, p2)) + return FALSE; + } + + if (!mono_delegate_ret_equal (delegate_sig->ret, method_sig->ret)) + return FALSE; + + return TRUE; +} + +gboolean +mono_verifier_is_signature_compatible (MonoMethodSignature *target, MonoMethodSignature *candidate) +{ + return mono_delegate_signature_equal (target, candidate, FALSE); +} + +/* + * verify_ldftn_delegate: + * + * Verify properties of ldftn based delegates. + */ +static void +verify_ldftn_delegate (VerifyContext *ctx, MonoClass *delegate, ILStackDesc *value, ILStackDesc *funptr) +{ + MonoMethod *method = funptr->method; + + /*ldftn non-final virtuals only allowed if method is not static, + * the object is a this arg (comes from a ldarg.0), and there is no starg.0. + * This rules doesn't apply if the object on stack is a boxed valuetype. + */ + if ((method->flags & METHOD_ATTRIBUTE_VIRTUAL) && !(method->flags & METHOD_ATTRIBUTE_FINAL) && !mono_class_is_sealed (method->klass) && !stack_slot_is_boxed_value (value)) { + /*A stdarg 0 must not happen, we fail here only in fail fast mode to avoid double error reports*/ + if (IS_FAIL_FAST_MODE (ctx) && ctx->has_this_store) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid ldftn with virtual function in method with stdarg 0 at 0x%04x", ctx->ip_offset)); + + /*current method must not be static*/ + if (ctx->method->flags & METHOD_ATTRIBUTE_STATIC) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid ldftn with virtual function at 0x%04x", ctx->ip_offset)); + + /*value is the this pointer, loaded using ldarg.0 */ + if (!stack_slot_is_this_pointer (value)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid object argument, it is not the this pointer, to ldftn with virtual method at 0x%04x", ctx->ip_offset)); + + ctx->code [ctx->ip_offset].flags |= IL_CODE_LDFTN_DELEGATE_NONFINAL_VIRTUAL; + } +} + +/* + * verify_delegate_compatibility: + * + * Verify delegate creation sequence. + * + */ +static void +verify_delegate_compatibility (VerifyContext *ctx, MonoClass *delegate, ILStackDesc *value, ILStackDesc *funptr) +{ +#define IS_VALID_OPCODE(offset, opcode) (ip [ip_offset - offset] == opcode && (ctx->code [ip_offset - offset].flags & IL_CODE_FLAG_SEEN)) +#define IS_LOAD_FUN_PTR(kind) (IS_VALID_OPCODE (6, CEE_PREFIX1) && ip [ip_offset - 5] == kind) + + MonoMethod *invoke, *method; + const guint8 *ip = ctx->header->code; + guint32 ip_offset = ctx->ip_offset; + gboolean is_static_ldftn = FALSE, is_first_arg_bound = FALSE; + + if (stack_slot_get_type (funptr) != TYPE_PTR || !funptr->method) { + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid function pointer parameter for delegate constructor at 0x%04x", ctx->ip_offset)); + return; + } + + invoke = mono_get_delegate_invoke (delegate); + method = funptr->method; + + if (!method || !mono_method_signature (method)) { + char *name = mono_type_get_full_name (delegate); + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Invalid method on stack to create delegate %s construction at 0x%04x", name, ctx->ip_offset)); + g_free (name); + return; + } + + if (!invoke || !mono_method_signature (invoke)) { + char *name = mono_type_get_full_name (delegate); + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Delegate type %s with bad Invoke method at 0x%04x", name, ctx->ip_offset)); + g_free (name); + return; + } + + is_static_ldftn = (ip_offset > 5 && IS_LOAD_FUN_PTR (CEE_LDFTN)) && method->flags & METHOD_ATTRIBUTE_STATIC; + + if (is_static_ldftn) + is_first_arg_bound = mono_method_signature (invoke)->param_count + 1 == mono_method_signature (method)->param_count; + + if (!mono_delegate_signature_equal (mono_method_signature (invoke), mono_method_signature (method), is_first_arg_bound)) { + char *fun_sig = mono_signature_get_desc (mono_method_signature (method), FALSE); + char *invoke_sig = mono_signature_get_desc (mono_method_signature (invoke), FALSE); + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Function pointer signature '%s' doesn't match delegate's signature '%s' at 0x%04x", fun_sig, invoke_sig, ctx->ip_offset)); + g_free (fun_sig); + g_free (invoke_sig); + } + + /* + * Delegate code sequences: + * [-6] ldftn token + * newobj ... + * + * + * [-7] dup + * [-6] ldvirtftn token + * newobj ... + * + * ldftn sequence:*/ + if (ip_offset > 5 && IS_LOAD_FUN_PTR (CEE_LDFTN)) { + verify_ldftn_delegate (ctx, delegate, value, funptr); + } else if (ip_offset > 6 && IS_VALID_OPCODE (7, CEE_DUP) && IS_LOAD_FUN_PTR (CEE_LDVIRTFTN)) { + ctx->code [ip_offset - 6].flags |= IL_CODE_DELEGATE_SEQUENCE; + }else { + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid code sequence for delegate creation at 0x%04x", ctx->ip_offset)); + } + ctx->code [ip_offset].flags |= IL_CODE_DELEGATE_SEQUENCE; + + //general tests + if (is_first_arg_bound) { + if (mono_method_signature (method)->param_count == 0 || !verify_stack_type_compatibility_full (ctx, mono_method_signature (method)->params [0], value, FALSE, TRUE)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("This object not compatible with function pointer for delegate creation at 0x%04x", ctx->ip_offset)); + } else { + if (method->flags & METHOD_ATTRIBUTE_STATIC) { + if (!stack_slot_is_null_literal (value)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Non-null this args used with static function for delegate creation at 0x%04x", ctx->ip_offset)); + } else { + if (!verify_stack_type_compatibility_full (ctx, &method->klass->byval_arg, value, FALSE, TRUE) && !stack_slot_is_null_literal (value)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("This object not compatible with function pointer for delegate creation at 0x%04x", ctx->ip_offset)); + } + } + + if (stack_slot_get_type (value) != TYPE_COMPLEX) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid first parameter for delegate creation at 0x%04x", ctx->ip_offset)); + +#undef IS_VALID_OPCODE +#undef IS_LOAD_FUN_PTR +} + +static gboolean +is_this_arg_of_struct_instance_method (unsigned int arg, VerifyContext *ctx) +{ + if (arg != 0) + return FALSE; + if (ctx->method->flags & METHOD_ATTRIBUTE_STATIC) + return FALSE; + if (!ctx->method->klass->valuetype) + return FALSE; + return TRUE; +} + +/* implement the opcode checks*/ +static void +push_arg (VerifyContext *ctx, unsigned int arg, int take_addr) +{ + ILStackDesc *top; + + if (arg >= ctx->max_args) { + if (take_addr) + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Method doesn't have argument %d", arg + 1)); + else { + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Method doesn't have argument %d", arg + 1)); + if (check_overflow (ctx)) //FIXME: what sane value could we ever push? + stack_push_val (ctx, TYPE_I4, &mono_defaults.int32_class->byval_arg); + } + } else if (check_overflow (ctx)) { + /*We must let the value be pushed, otherwise we would get an underflow error*/ + check_unverifiable_type (ctx, ctx->params [arg]); + if (ctx->params [arg]->byref && take_addr) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("ByRef of ByRef at 0x%04x", ctx->ip_offset)); + top = stack_push (ctx); + if (!set_stack_value (ctx, top, ctx->params [arg], take_addr)) + return; + + if (arg == 0 && !(ctx->method->flags & METHOD_ATTRIBUTE_STATIC)) { + if (take_addr) + ctx->has_this_store = TRUE; + else + top->stype |= THIS_POINTER_MASK; + if (mono_method_is_constructor (ctx->method) && !ctx->super_ctor_called && !ctx->method->klass->valuetype) + top->stype |= UNINIT_THIS_MASK; + } + if (!take_addr && ctx->params [arg]->byref && !is_this_arg_of_struct_instance_method (arg, ctx)) + top->stype |= SAFE_BYREF_MASK; + } +} + +static void +push_local (VerifyContext *ctx, guint32 arg, int take_addr) +{ + if (arg >= ctx->num_locals) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Method doesn't have local %d", arg + 1)); + } else if (check_overflow (ctx)) { + /*We must let the value be pushed, otherwise we would get an underflow error*/ + check_unverifiable_type (ctx, ctx->locals [arg]); + if (ctx->locals [arg]->byref && take_addr) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("ByRef of ByRef at 0x%04x", ctx->ip_offset)); + + ILStackDesc *value = stack_push (ctx); + set_stack_value (ctx, value, ctx->locals [arg], take_addr); + if (local_is_safe_byref (ctx, arg)) + value->stype |= SAFE_BYREF_MASK; + } +} + +static void +store_arg (VerifyContext *ctx, guint32 arg) +{ + ILStackDesc *value; + + if (arg >= ctx->max_args) { + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Method doesn't have argument %d at 0x%04x", arg + 1, ctx->ip_offset)); + if (check_underflow (ctx, 1)) + stack_pop (ctx); + return; + } + + if (check_underflow (ctx, 1)) { + value = stack_pop (ctx); + if (!verify_stack_type_compatibility (ctx, ctx->params [arg], value)) { + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Incompatible type %s in argument store at 0x%04x", stack_slot_get_name (value), ctx->ip_offset)); + } + } + if (arg == 0 && !(ctx->method->flags & METHOD_ATTRIBUTE_STATIC)) + ctx->has_this_store = 1; +} + +static void +store_local (VerifyContext *ctx, guint32 arg) +{ + ILStackDesc *value; + if (arg >= ctx->num_locals) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Method doesn't have local var %d at 0x%04x", arg + 1, ctx->ip_offset)); + return; + } + + /*TODO verify definite assigment */ + if (!check_underflow (ctx, 1)) + return; + + value = stack_pop (ctx); + if (ctx->locals [arg]->byref) { + if (stack_slot_is_managed_mutability_pointer (value)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Cannot use a readonly managed reference when storing on a local variable at 0x%04x", ctx->ip_offset)); + + if (local_is_safe_byref (ctx, arg) && !stack_slot_is_safe_byref (value)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Cannot store an unsafe ret byref to a local that was previously stored a save ret byref value at 0x%04x", ctx->ip_offset)); + + if (stack_slot_is_safe_byref (value) && !local_is_unsafe_byref (ctx, arg)) + ctx->locals_verification_state [arg] |= SAFE_BYREF_LOCAL; + + if (!stack_slot_is_safe_byref (value)) + ctx->locals_verification_state [arg] |= UNSAFE_BYREF_LOCAL; + + } + if (!verify_stack_type_compatibility (ctx, ctx->locals [arg], value)) { + char *expected = mono_type_full_name (ctx->locals [arg]); + char *found = stack_slot_full_name (value); + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Incompatible type '%s' on stack cannot be stored to local %d with type '%s' at 0x%04x", + found, + arg, + expected, + ctx->ip_offset)); + g_free (expected); + g_free (found); + } +} + +/*FIXME add and sub needs special care here*/ +static void +do_binop (VerifyContext *ctx, unsigned int opcode, const unsigned char table [TYPE_MAX][TYPE_MAX]) +{ + ILStackDesc *a, *b, *top; + int idxa, idxb, complexMerge = 0; + unsigned char res; + + if (!check_underflow (ctx, 2)) + return; + b = stack_pop (ctx); + a = stack_pop (ctx); + + idxa = stack_slot_get_underlying_type (a); + if (stack_slot_is_managed_pointer (a)) { + idxa = TYPE_PTR; + complexMerge = 1; + } + + idxb = stack_slot_get_underlying_type (b); + if (stack_slot_is_managed_pointer (b)) { + idxb = TYPE_PTR; + complexMerge = 2; + } + + --idxa; + --idxb; + res = table [idxa][idxb]; + + VERIFIER_DEBUG ( printf ("binop res %d\n", res); ); + VERIFIER_DEBUG ( printf ("idxa %d idxb %d\n", idxa, idxb); ); + + top = stack_push (ctx); + if (res == TYPE_INV) { + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Binary instruction applyed to ill formed stack (%s x %s)", stack_slot_get_name (a), stack_slot_get_name (b))); + copy_stack_value (top, a); + return; + } + + if (res & NON_VERIFIABLE_RESULT) { + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Binary instruction is not verifiable (%s x %s)", stack_slot_get_name (a), stack_slot_get_name (b))); + + res = res & ~NON_VERIFIABLE_RESULT; + } + + if (complexMerge && res == TYPE_PTR) { + if (complexMerge == 1) + copy_stack_value (top, a); + else if (complexMerge == 2) + copy_stack_value (top, b); + /* + * There is no need to merge the type of two pointers. + * The only valid operation is subtraction, that returns a native + * int as result and can be used with any 2 pointer kinds. + * This is valid acording to Patition III 1.1.4 + */ + } else + top->stype = res; + +} + + +static void +do_boolean_branch_op (VerifyContext *ctx, int delta) +{ + int target = ctx->ip_offset + delta; + ILStackDesc *top; + + VERIFIER_DEBUG ( printf ("boolean branch offset %d delta %d target %d\n", ctx->ip_offset, delta, target); ); + + if (target < 0 || target >= ctx->code_size) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Boolean branch target out of code at 0x%04x", ctx->ip_offset)); + return; + } + + switch (is_valid_branch_instruction (ctx->header, ctx->ip_offset, target)) { + case 1: + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Branch target escapes out of exception block at 0x%04x", ctx->ip_offset)); + break; + case 2: + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Branch target escapes out of exception block at 0x%04x", ctx->ip_offset)); + return; + } + + ctx->target = target; + + if (!check_underflow (ctx, 1)) + return; + + top = stack_pop (ctx); + if (!is_valid_bool_arg (top)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Argument type %s not valid for brtrue/brfalse at 0x%04x", stack_slot_get_name (top), ctx->ip_offset)); + + check_unmanaged_pointer (ctx, top); +} + +static gboolean +stack_slot_is_complex_type_not_reference_type (ILStackDesc *slot) +{ + return stack_slot_get_type (slot) == TYPE_COMPLEX && !MONO_TYPE_IS_REFERENCE (slot->type) && !stack_slot_is_boxed_value (slot); +} + +static gboolean +stack_slot_is_reference_value (ILStackDesc *slot) +{ + return stack_slot_get_type (slot) == TYPE_COMPLEX && (MONO_TYPE_IS_REFERENCE (slot->type) || stack_slot_is_boxed_value (slot)); +} + +static void +do_branch_op (VerifyContext *ctx, signed int delta, const unsigned char table [TYPE_MAX][TYPE_MAX]) +{ + ILStackDesc *a, *b; + int idxa, idxb; + unsigned char res; + int target = ctx->ip_offset + delta; + + VERIFIER_DEBUG ( printf ("branch offset %d delta %d target %d\n", ctx->ip_offset, delta, target); ); + + if (target < 0 || target >= ctx->code_size) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Branch target out of code at 0x%04x", ctx->ip_offset)); + return; + } + + switch (is_valid_cmp_branch_instruction (ctx->header, ctx->ip_offset, target)) { + case 1: /*FIXME use constants and not magic numbers.*/ + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Branch target escapes out of exception block at 0x%04x", ctx->ip_offset)); + break; + case 2: + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Branch target escapes out of exception block at 0x%04x", ctx->ip_offset)); + return; + } + + ctx->target = target; + + if (!check_underflow (ctx, 2)) + return; + + b = stack_pop (ctx); + a = stack_pop (ctx); + + idxa = stack_slot_get_underlying_type (a); + if (stack_slot_is_managed_pointer (a)) + idxa = TYPE_PTR; + + idxb = stack_slot_get_underlying_type (b); + if (stack_slot_is_managed_pointer (b)) + idxb = TYPE_PTR; + + if (stack_slot_is_complex_type_not_reference_type (a) || stack_slot_is_complex_type_not_reference_type (b)) { + res = TYPE_INV; + } else { + --idxa; + --idxb; + res = table [idxa][idxb]; + } + + VERIFIER_DEBUG ( printf ("branch res %d\n", res); ); + VERIFIER_DEBUG ( printf ("idxa %d idxb %d\n", idxa, idxb); ); + + if (res == TYPE_INV) { + CODE_NOT_VERIFIABLE (ctx, + g_strdup_printf ("Compare and Branch instruction applyed to ill formed stack (%s x %s) at 0x%04x", stack_slot_get_name (a), stack_slot_get_name (b), ctx->ip_offset)); + } else if (res & NON_VERIFIABLE_RESULT) { + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Compare and Branch instruction is not verifiable (%s x %s) at 0x%04x", stack_slot_get_name (a), stack_slot_get_name (b), ctx->ip_offset)); + res = res & ~NON_VERIFIABLE_RESULT; + } +} + +static void +do_cmp_op (VerifyContext *ctx, const unsigned char table [TYPE_MAX][TYPE_MAX], guint32 opcode) +{ + ILStackDesc *a, *b; + int idxa, idxb; + unsigned char res; + + if (!check_underflow (ctx, 2)) + return; + b = stack_pop (ctx); + a = stack_pop (ctx); + + if (opcode == CEE_CGT_UN) { + if ((stack_slot_is_reference_value (a) && stack_slot_is_null_literal (b)) || + (stack_slot_is_reference_value (b) && stack_slot_is_null_literal (a))) { + stack_push_val (ctx, TYPE_I4, &mono_defaults.int32_class->byval_arg); + return; + } + } + + idxa = stack_slot_get_underlying_type (a); + if (stack_slot_is_managed_pointer (a)) + idxa = TYPE_PTR; + + idxb = stack_slot_get_underlying_type (b); + if (stack_slot_is_managed_pointer (b)) + idxb = TYPE_PTR; + + if (stack_slot_is_complex_type_not_reference_type (a) || stack_slot_is_complex_type_not_reference_type (b)) { + res = TYPE_INV; + } else { + --idxa; + --idxb; + res = table [idxa][idxb]; + } + + if(res == TYPE_INV) { + char *left_type = stack_slot_full_name (a); + char *right_type = stack_slot_full_name (b); + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf("Compare instruction applyed to ill formed stack (%s x %s) at 0x%04x", left_type, right_type, ctx->ip_offset)); + g_free (left_type); + g_free (right_type); + } else if (res & NON_VERIFIABLE_RESULT) { + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Compare instruction is not verifiable (%s x %s) at 0x%04x", stack_slot_get_name (a), stack_slot_get_name (b), ctx->ip_offset)); + res = res & ~NON_VERIFIABLE_RESULT; + } + stack_push_val (ctx, TYPE_I4, &mono_defaults.int32_class->byval_arg); +} + +static void +do_ret (VerifyContext *ctx) +{ + MonoType *ret = ctx->signature->ret; + VERIFIER_DEBUG ( printf ("checking ret\n"); ); + if (ret->type != MONO_TYPE_VOID) { + ILStackDesc *top; + if (!check_underflow (ctx, 1)) + return; + + top = stack_pop(ctx); + + if (!verify_stack_type_compatibility (ctx, ctx->signature->ret, top)) { + char *ret_type = mono_type_full_name (ctx->signature->ret); + char *stack_type = stack_slot_full_name (top); + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Incompatible return value on stack with method signature, expected '%s' but got '%s' at 0x%04x", ret_type, stack_type, ctx->ip_offset)); + g_free (stack_type); + g_free (ret_type); + return; + } + + if (ret->byref && !stack_slot_is_safe_byref (top)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Method returns byref and return value is not a safe-to-return-byref at 0x%04x", ctx->ip_offset)); + + if (ret->type == MONO_TYPE_TYPEDBYREF || mono_type_is_value_type (ret, "System", "ArgIterator") || mono_type_is_value_type (ret, "System", "RuntimeArgumentHandle")) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Method returns byref, TypedReference, ArgIterator or RuntimeArgumentHandle at 0x%04x", ctx->ip_offset)); + } + + if (ctx->eval.size > 0) { + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Stack not empty (%d) after ret at 0x%04x", ctx->eval.size, ctx->ip_offset)); + } + if (in_any_block (ctx->header, ctx->ip_offset)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("ret cannot escape exception blocks at 0x%04x", ctx->ip_offset)); +} + +/* + * FIXME we need to fix the case of a non-virtual instance method defined in the parent but call using a token pointing to a subclass. + * This is illegal but mono_get_method_full decoded it. + * TODO handle calling .ctor outside one or calling the .ctor for other class but super + */ +static void +do_invoke_method (VerifyContext *ctx, int method_token, gboolean virtual_) +{ + MonoError error; + int param_count, i; + MonoMethodSignature *sig; + ILStackDesc *value; + MonoMethod *method; + gboolean virt_check_this = FALSE; + gboolean constrained = ctx->prefix_set & PREFIX_CONSTRAINED; + + if (!(method = verifier_load_method (ctx, method_token, virtual_ ? "callvirt" : "call"))) + return; + + if (virtual_) { + CLEAR_PREFIX (ctx, PREFIX_CONSTRAINED); + + if (method->klass->valuetype) // && !constrained ??? + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Cannot use callvirtual with valuetype method at 0x%04x", ctx->ip_offset)); + + if ((method->flags & METHOD_ATTRIBUTE_STATIC)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Cannot use callvirtual with static method at 0x%04x", ctx->ip_offset)); + + } else { + if (method->flags & METHOD_ATTRIBUTE_ABSTRACT) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Cannot use call with an abstract method at 0x%04x", ctx->ip_offset)); + + if ((method->flags & METHOD_ATTRIBUTE_VIRTUAL) && !(method->flags & METHOD_ATTRIBUTE_FINAL) && !mono_class_is_sealed (method->klass)) { + virt_check_this = TRUE; + ctx->code [ctx->ip_offset].flags |= IL_CODE_CALL_NONFINAL_VIRTUAL; + } + } + + if (!(sig = mono_method_get_signature_checked (method, ctx->image, method_token, ctx->generic_context, &error))) { + mono_error_cleanup (&error); + sig = mono_method_get_signature_checked (method, ctx->image, method_token, NULL, &error); + } + + if (!sig) { + char *name = mono_type_get_full_name (method->klass); + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Could not resolve signature of %s:%s at 0x%04x due to: %s", name, method->name, ctx->ip_offset, mono_error_get_message (&error))); + mono_error_cleanup (&error); + g_free (name); + return; + } + + param_count = sig->param_count + sig->hasthis; + if (!check_underflow (ctx, param_count)) + return; + + gboolean is_safe_byref_call = TRUE; + + for (i = sig->param_count - 1; i >= 0; --i) { + VERIFIER_DEBUG ( printf ("verifying argument %d\n", i); ); + value = stack_pop (ctx); + if (!verify_stack_type_compatibility (ctx, sig->params[i], value)) { + char *stack_name = stack_slot_full_name (value); + char *sig_name = mono_type_full_name (sig->params [i]); + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Incompatible parameter with function signature: Calling method with signature (%s) but for argument %d there is a (%s) on stack at 0x%04x", sig_name, i, stack_name, ctx->ip_offset)); + g_free (stack_name); + g_free (sig_name); + } + + if (stack_slot_is_managed_mutability_pointer (value)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Cannot use a readonly pointer as argument of %s at 0x%04x", virtual_ ? "callvirt" : "call", ctx->ip_offset)); + + if ((ctx->prefix_set & PREFIX_TAIL) && stack_slot_is_managed_pointer (value)) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Cannot pass a byref argument to a tail %s at 0x%04x", virtual_ ? "callvirt" : "call", ctx->ip_offset)); + return; + } + if (stack_slot_is_managed_pointer (value) && !stack_slot_is_safe_byref (value)) + is_safe_byref_call = FALSE; + } + + if (sig->hasthis) { + MonoType *type = &method->klass->byval_arg; + ILStackDesc copy; + + if (mono_method_is_constructor (method) && !method->klass->valuetype) { + if (IS_STRICT_MODE (ctx) && !mono_method_is_constructor (ctx->method)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Cannot call a constructor outside one at 0x%04x", ctx->ip_offset)); + if (IS_STRICT_MODE (ctx) && method->klass != ctx->method->klass->parent && method->klass != ctx->method->klass) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Cannot call a constructor of a type different from this or super at 0x%04x", ctx->ip_offset)); + + ctx->super_ctor_called = TRUE; + value = stack_pop_safe (ctx); + if (IS_STRICT_MODE (ctx) && (value->stype & THIS_POINTER_MASK) != THIS_POINTER_MASK) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid 'this ptr' argument for constructor at 0x%04x", ctx->ip_offset)); + if (!(value->stype & UNINIT_THIS_MASK)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Calling the base constructor on an initialized this pointer at 0x%04x", ctx->ip_offset)); + } else { + value = stack_pop (ctx); + } + + copy_stack_value (©, value); + //TODO we should extract this to a 'drop_byref_argument' and use everywhere + //Other parts of the code suffer from the same issue of + copy.type = mono_type_get_type_byval (copy.type); + copy.stype &= ~POINTER_MASK; + + if (virt_check_this && !stack_slot_is_this_pointer (value) && !(method->klass->valuetype || stack_slot_is_boxed_value (value))) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Cannot use the call opcode with a non-final virtual method on an object different than the 'this' pointer at 0x%04x", ctx->ip_offset)); + + if (constrained && virtual_) { + if (!stack_slot_is_managed_pointer (value)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Object is not a managed pointer for a constrained call at 0x%04x", ctx->ip_offset)); + if (!mono_metadata_type_equal_full (mono_type_get_type_byval (value->type), mono_type_get_underlying_type (ctx->constrained_type), TRUE)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Object not compatible with constrained type at 0x%04x", ctx->ip_offset)); + copy.stype |= BOXED_MASK; + copy.type = ctx->constrained_type; + } else { + if (stack_slot_is_managed_pointer (value) && !mono_class_from_mono_type (value->type)->valuetype) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Cannot call a reference type using a managed pointer to the this arg at 0x%04x", ctx->ip_offset)); + + if (!virtual_ && mono_class_from_mono_type (value->type)->valuetype && !method->klass->valuetype && !stack_slot_is_boxed_value (value)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Cannot call a valuetype baseclass at 0x%04x", ctx->ip_offset)); + + if (virtual_ && mono_class_from_mono_type (value->type)->valuetype && !stack_slot_is_boxed_value (value)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Cannot use a valuetype with callvirt at 0x%04x", ctx->ip_offset)); + + if (method->klass->valuetype && (stack_slot_is_boxed_value (value) || !stack_slot_is_managed_pointer (value))) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Cannot use a boxed or literal valuetype to call a valuetype method at 0x%04x", ctx->ip_offset)); + } + if (!verify_stack_type_compatibility (ctx, type, ©)) { + char *expected = mono_type_full_name (type); + char *effective = stack_slot_full_name (©); + char *method_name = mono_method_full_name (method, TRUE); + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Incompatible this argument on stack with method signature expected '%s' but got '%s' for a call to '%s' at 0x%04x", + expected, effective, method_name, ctx->ip_offset)); + g_free (method_name); + g_free (effective); + g_free (expected); + } + + if (!IS_SKIP_VISIBILITY (ctx) && !mono_method_can_access_method_full (ctx->method, method, mono_class_from_mono_type (value->type))) { + char *name = mono_method_full_name (method, TRUE); + CODE_NOT_VERIFIABLE2 (ctx, g_strdup_printf ("Method %s is not accessible at 0x%04x", name, ctx->ip_offset), MONO_EXCEPTION_METHOD_ACCESS); + g_free (name); + } + + } else if (!IS_SKIP_VISIBILITY (ctx) && !mono_method_can_access_method_full (ctx->method, method, NULL)) { + char *name = mono_method_full_name (method, TRUE); + CODE_NOT_VERIFIABLE2 (ctx, g_strdup_printf ("Method %s is not accessible at 0x%04x", name, ctx->ip_offset), MONO_EXCEPTION_METHOD_ACCESS); + g_free (name); + } + + if (sig->ret->type != MONO_TYPE_VOID) { + if (!mono_type_is_valid_in_context (ctx, sig->ret)) + return; + + if (check_overflow (ctx)) { + value = stack_push (ctx); + set_stack_value (ctx, value, sig->ret, FALSE); + if ((ctx->prefix_set & PREFIX_READONLY) && method->klass->rank && !strcmp (method->name, "Address")) { + ctx->prefix_set &= ~PREFIX_READONLY; + value->stype |= CMMP_MASK; + } + if (sig->ret->byref && is_safe_byref_call) + value->stype |= SAFE_BYREF_MASK; + } + } + + if ((ctx->prefix_set & PREFIX_TAIL)) { + if (!mono_delegate_ret_equal (mono_method_signature (ctx->method)->ret, sig->ret)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Tail call with incompatible return type at 0x%04x", ctx->ip_offset)); + if (ctx->header->code [ctx->ip_offset + 5] != CEE_RET) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Tail call not followed by ret at 0x%04x", ctx->ip_offset)); + } + +} + +static void +do_push_static_field (VerifyContext *ctx, int token, gboolean take_addr) +{ + MonoClassField *field; + MonoClass *klass; + if (!check_overflow (ctx)) + return; + if (!take_addr) + CLEAR_PREFIX (ctx, PREFIX_VOLATILE); + + if (!(field = verifier_load_field (ctx, token, &klass, take_addr ? "ldsflda" : "ldsfld"))) + return; + + if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC)) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Cannot load non static field at 0x%04x", ctx->ip_offset)); + return; + } + /*taking the address of initonly field only works from the static constructor */ + if (take_addr && (field->type->attrs & FIELD_ATTRIBUTE_INIT_ONLY) && + !(field->parent == ctx->method->klass && (ctx->method->flags & (METHOD_ATTRIBUTE_SPECIAL_NAME | METHOD_ATTRIBUTE_STATIC)) && !strcmp (".cctor", ctx->method->name))) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Cannot take the address of a init-only field at 0x%04x", ctx->ip_offset)); + + if (!IS_SKIP_VISIBILITY (ctx) && !mono_method_can_access_field_full (ctx->method, field, NULL)) + CODE_NOT_VERIFIABLE2 (ctx, g_strdup_printf ("Type at stack is not accessible at 0x%04x", ctx->ip_offset), MONO_EXCEPTION_FIELD_ACCESS); + + ILStackDesc *value = stack_push (ctx); + set_stack_value (ctx, value, field->type, take_addr); + if (take_addr) + value->stype |= SAFE_BYREF_MASK; +} + +static void +do_store_static_field (VerifyContext *ctx, int token) { + MonoClassField *field; + MonoClass *klass; + ILStackDesc *value; + CLEAR_PREFIX (ctx, PREFIX_VOLATILE); + + if (!check_underflow (ctx, 1)) + return; + + value = stack_pop (ctx); + + if (!(field = verifier_load_field (ctx, token, &klass, "stsfld"))) + return; + + if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC)) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Cannot store non static field at 0x%04x", ctx->ip_offset)); + return; + } + + if (field->type->type == MONO_TYPE_TYPEDBYREF) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Typedbyref field is an unverfiable type in store static field at 0x%04x", ctx->ip_offset)); + return; + } + + if (!IS_SKIP_VISIBILITY (ctx) && !mono_method_can_access_field_full (ctx->method, field, NULL)) + CODE_NOT_VERIFIABLE2 (ctx, g_strdup_printf ("Type at stack is not accessible at 0x%04x", ctx->ip_offset), MONO_EXCEPTION_FIELD_ACCESS); + + if (!verify_stack_type_compatibility (ctx, field->type, value)) { + char *stack_name = stack_slot_full_name (value); + char *field_name = mono_type_full_name (field->type); + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Incompatible type in static field store expected '%s' but found '%s' at 0x%04x", + field_name, stack_name, ctx->ip_offset)); + g_free (field_name); + g_free (stack_name); + } +} + +static gboolean +check_is_valid_type_for_field_ops (VerifyContext *ctx, int token, ILStackDesc *obj, MonoClassField **ret_field, const char *opcode) +{ + MonoClassField *field; + MonoClass *klass; + gboolean is_pointer; + + /*must be a reference type, a managed pointer, an unamanaged pointer, or a valuetype*/ + if (!(field = verifier_load_field (ctx, token, &klass, opcode))) + return FALSE; + + *ret_field = field; + //the value on stack is going to be used as a pointer + is_pointer = stack_slot_get_type (obj) == TYPE_PTR || (stack_slot_get_type (obj) == TYPE_NATIVE_INT && !get_stack_type (&field->parent->byval_arg)); + + if (field->type->type == MONO_TYPE_TYPEDBYREF) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Typedbyref field is an unverfiable type at 0x%04x", ctx->ip_offset)); + return FALSE; + } + g_assert (obj->type); + + /*The value on the stack must be a subclass of the defining type of the field*/ + /* we need to check if we can load the field from the stack value*/ + if (is_pointer) { + if (stack_slot_get_underlying_type (obj) == TYPE_NATIVE_INT) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Native int is not a verifiable type to reference a field at 0x%04x", ctx->ip_offset)); + + if (!IS_SKIP_VISIBILITY (ctx) && !mono_method_can_access_field_full (ctx->method, field, NULL)) + CODE_NOT_VERIFIABLE2 (ctx, g_strdup_printf ("Type at stack is not accessible at 0x%04x", ctx->ip_offset), MONO_EXCEPTION_FIELD_ACCESS); + } else { + if (!field->parent->valuetype && stack_slot_is_managed_pointer (obj)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Type at stack is a managed pointer to a reference type and is not compatible to reference the field at 0x%04x", ctx->ip_offset)); + + /*a value type can be loaded from a value or a managed pointer, but not a boxed object*/ + if (field->parent->valuetype && stack_slot_is_boxed_value (obj)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Type at stack is a boxed valuetype and is not compatible to reference the field at 0x%04x", ctx->ip_offset)); + + if (!stack_slot_is_null_literal (obj) && !verify_stack_type_compatibility_full (ctx, &field->parent->byval_arg, obj, TRUE, FALSE)) { + char *found = stack_slot_full_name (obj); + char *expected = mono_type_full_name (&field->parent->byval_arg); + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Expected type '%s' but found '%s' referencing the 'this' argument at 0x%04x", expected, found, ctx->ip_offset)); + g_free (found); + g_free (expected); + } + + if (!IS_SKIP_VISIBILITY (ctx) && !mono_method_can_access_field_full (ctx->method, field, mono_class_from_mono_type (obj->type))) + CODE_NOT_VERIFIABLE2 (ctx, g_strdup_printf ("Type at stack is not accessible at 0x%04x", ctx->ip_offset), MONO_EXCEPTION_FIELD_ACCESS); + } + + check_unmanaged_pointer (ctx, obj); + return TRUE; +} + +static void +do_push_field (VerifyContext *ctx, int token, gboolean take_addr) +{ + ILStackDesc *obj; + MonoClassField *field; + gboolean is_safe_byref = FALSE; + + if (!take_addr) + CLEAR_PREFIX (ctx, PREFIX_UNALIGNED | PREFIX_VOLATILE); + + if (!check_underflow (ctx, 1)) + return; + obj = stack_pop_safe (ctx); + + if (!check_is_valid_type_for_field_ops (ctx, token, obj, &field, take_addr ? "ldflda" : "ldfld")) + return; + + if (take_addr && field->parent->valuetype && !stack_slot_is_managed_pointer (obj)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Cannot take the address of a temporary value-type at 0x%04x", ctx->ip_offset)); + + if (take_addr && (field->type->attrs & FIELD_ATTRIBUTE_INIT_ONLY) && + !(field->parent == ctx->method->klass && mono_method_is_constructor (ctx->method))) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Cannot take the address of a init-only field at 0x%04x", ctx->ip_offset)); + + //must do it here cuz stack_push will return the same slot as obj above + is_safe_byref = take_addr && (stack_slot_is_reference_value (obj) || stack_slot_is_safe_byref (obj)); + + ILStackDesc *value = stack_push (ctx); + set_stack_value (ctx, value, field->type, take_addr); + + if (is_safe_byref) + value->stype |= SAFE_BYREF_MASK; +} + +static void +do_store_field (VerifyContext *ctx, int token) +{ + ILStackDesc *value, *obj; + MonoClassField *field; + CLEAR_PREFIX (ctx, PREFIX_UNALIGNED | PREFIX_VOLATILE); + + if (!check_underflow (ctx, 2)) + return; + + value = stack_pop (ctx); + obj = stack_pop_safe (ctx); + + if (!check_is_valid_type_for_field_ops (ctx, token, obj, &field, "stfld")) + return; + + if (!verify_stack_type_compatibility (ctx, field->type, value)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Incompatible type %s in field store at 0x%04x", stack_slot_get_name (value), ctx->ip_offset)); +} + +/*TODO proper handle for Nullable*/ +static void +do_box_value (VerifyContext *ctx, int klass_token) +{ + ILStackDesc *value; + MonoType *type = get_boxable_mono_type (ctx, klass_token, "box"); + MonoClass *klass; + + if (!type) + return; + + if (!check_underflow (ctx, 1)) + return; + + value = stack_pop (ctx); + /*box is a nop for reference types*/ + + if (stack_slot_get_underlying_type (value) == TYPE_COMPLEX && MONO_TYPE_IS_REFERENCE (value->type) && MONO_TYPE_IS_REFERENCE (type)) { + stack_push_stack_val (ctx, value)->stype |= BOXED_MASK; + return; + } + + + if (!verify_stack_type_compatibility (ctx, type, value)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid type at stack for boxing operation at 0x%04x", ctx->ip_offset)); + + klass = mono_class_from_mono_type (type); + if (mono_class_is_nullable (klass)) + type = &mono_class_get_nullable_param (klass)->byval_arg; + stack_push_val (ctx, TYPE_COMPLEX | BOXED_MASK, type); +} + +static void +do_unbox_value (VerifyContext *ctx, int klass_token) +{ + ILStackDesc *value; + MonoType *type = get_boxable_mono_type (ctx, klass_token, "unbox"); + + if (!type) + return; + + if (!check_underflow (ctx, 1)) + return; + + if (!mono_class_from_mono_type (type)->valuetype) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid reference type for unbox at 0x%04x", ctx->ip_offset)); + + value = stack_pop (ctx); + + /*Value should be: a boxed valuetype or a reference type*/ + if (!(stack_slot_get_type (value) == TYPE_COMPLEX && + (stack_slot_is_boxed_value (value) || !mono_class_from_mono_type (value->type)->valuetype))) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid type %s at stack for unbox operation at 0x%04x", stack_slot_get_name (value), ctx->ip_offset)); + + set_stack_value (ctx, value = stack_push (ctx), mono_type_get_type_byref (type), FALSE); + value->stype |= CMMP_MASK; +} + +static void +do_unbox_any (VerifyContext *ctx, int klass_token) +{ + ILStackDesc *value; + MonoType *type = get_boxable_mono_type (ctx, klass_token, "unbox.any"); + + if (!type) + return; + + if (!check_underflow (ctx, 1)) + return; + + value = stack_pop (ctx); + + /*Value should be: a boxed valuetype or a reference type*/ + if (!(stack_slot_get_type (value) == TYPE_COMPLEX && + (stack_slot_is_boxed_value (value) || !mono_class_from_mono_type (value->type)->valuetype))) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid type %s at stack for unbox.any operation at 0x%04x", stack_slot_get_name (value), ctx->ip_offset)); + + set_stack_value (ctx, stack_push (ctx), type, FALSE); +} + +static void +do_unary_math_op (VerifyContext *ctx, int op) +{ + ILStackDesc *value; + if (!check_underflow (ctx, 1)) + return; + value = stack_pop (ctx); + switch (stack_slot_get_type (value)) { + case TYPE_I4: + case TYPE_I8: + case TYPE_NATIVE_INT: + break; + case TYPE_R8: + if (op == CEE_NEG) + break; + case TYPE_COMPLEX: /*only enums are ok*/ + if (mono_type_is_enum_type (value->type)) + break; + default: + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid type at stack for unary not at 0x%04x", ctx->ip_offset)); + } + stack_push_stack_val (ctx, value); +} + +static void +do_conversion (VerifyContext *ctx, int kind) +{ + ILStackDesc *value; + if (!check_underflow (ctx, 1)) + return; + value = stack_pop (ctx); + + switch (stack_slot_get_type (value)) { + case TYPE_I4: + case TYPE_I8: + case TYPE_NATIVE_INT: + case TYPE_R8: + break; + default: + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid type (%s) at stack for conversion operation. Numeric type expected at 0x%04x", stack_slot_get_name (value), ctx->ip_offset)); + } + + switch (kind) { + case TYPE_I4: + stack_push_val (ctx, TYPE_I4, &mono_defaults.int32_class->byval_arg); + break; + case TYPE_I8: + stack_push_val (ctx,TYPE_I8, &mono_defaults.int64_class->byval_arg); + break; + case TYPE_R8: + stack_push_val (ctx, TYPE_R8, &mono_defaults.double_class->byval_arg); + break; + case TYPE_NATIVE_INT: + stack_push_val (ctx, TYPE_NATIVE_INT, &mono_defaults.int_class->byval_arg); + break; + default: + g_error ("unknown type %02x in conversion", kind); + + } +} + +static void +do_load_token (VerifyContext *ctx, int token) +{ + MonoError error; + gpointer handle; + MonoClass *handle_class; + if (!check_overflow (ctx)) + return; + + if (ctx->method->wrapper_type != MONO_WRAPPER_NONE) { + handle = mono_method_get_wrapper_data (ctx->method, token); + handle_class = (MonoClass *)mono_method_get_wrapper_data (ctx->method, token + 1); + if (handle_class == mono_defaults.typehandle_class) + handle = &((MonoClass*)handle)->byval_arg; + } else { + switch (token & 0xff000000) { + case MONO_TOKEN_TYPE_DEF: + case MONO_TOKEN_TYPE_REF: + case MONO_TOKEN_TYPE_SPEC: + case MONO_TOKEN_FIELD_DEF: + case MONO_TOKEN_METHOD_DEF: + case MONO_TOKEN_METHOD_SPEC: + case MONO_TOKEN_MEMBER_REF: + if (!token_bounds_check (ctx->image, token)) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Table index out of range 0x%x for token %x for ldtoken at 0x%04x", mono_metadata_token_index (token), token, ctx->ip_offset)); + return; + } + break; + default: + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Invalid table 0x%x for token 0x%x for ldtoken at 0x%04x", mono_metadata_token_table (token), token, ctx->ip_offset)); + return; + } + + handle = mono_ldtoken_checked (ctx->image, token, &handle_class, ctx->generic_context, &error); + } + + if (!handle) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Invalid token 0x%x for ldtoken at 0x%04x due to %s", token, ctx->ip_offset, mono_error_get_message (&error))); + mono_error_cleanup (&error); + return; + } + if (handle_class == mono_defaults.typehandle_class) { + mono_type_is_valid_in_context (ctx, (MonoType*)handle); + } else if (handle_class == mono_defaults.methodhandle_class) { + mono_method_is_valid_in_context (ctx, (MonoMethod*)handle); + } else if (handle_class == mono_defaults.fieldhandle_class) { + mono_type_is_valid_in_context (ctx, &((MonoClassField*)handle)->parent->byval_arg); + } else { + ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Invalid ldtoken type %x at 0x%04x", token, ctx->ip_offset), MONO_EXCEPTION_BAD_IMAGE); + } + stack_push_val (ctx, TYPE_COMPLEX, mono_class_get_type (handle_class)); +} + +static void +do_ldobj_value (VerifyContext *ctx, int token) +{ + ILStackDesc *value; + MonoType *type = get_boxable_mono_type (ctx, token, "ldobj"); + CLEAR_PREFIX (ctx, PREFIX_UNALIGNED | PREFIX_VOLATILE); + + if (!type) + return; + + if (!check_underflow (ctx, 1)) + return; + + value = stack_pop (ctx); + if (!stack_slot_is_managed_pointer (value) + && stack_slot_get_type (value) != TYPE_NATIVE_INT + && !(stack_slot_get_type (value) == TYPE_PTR && value->type->type != MONO_TYPE_FNPTR)) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Invalid argument %s to ldobj at 0x%04x", stack_slot_get_name (value), ctx->ip_offset)); + return; + } + + if (stack_slot_get_type (value) == TYPE_NATIVE_INT) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Using native pointer to ldobj at 0x%04x", ctx->ip_offset)); + + /*We have a byval on the stack, but the comparison must be strict. */ + if (!verify_type_compatibility_full (ctx, type, mono_type_get_type_byval (value->type), TRUE)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid type at stack for ldojb operation at 0x%04x", ctx->ip_offset)); + + set_stack_value (ctx, stack_push (ctx), type, FALSE); +} + +static void +do_stobj (VerifyContext *ctx, int token) +{ + ILStackDesc *dest, *src; + MonoType *type = get_boxable_mono_type (ctx, token, "stobj"); + CLEAR_PREFIX (ctx, PREFIX_UNALIGNED | PREFIX_VOLATILE); + + if (!type) + return; + + if (!check_underflow (ctx, 2)) + return; + + src = stack_pop (ctx); + dest = stack_pop (ctx); + + if (stack_slot_is_managed_mutability_pointer (dest)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Cannot use a readonly pointer with stobj at 0x%04x", ctx->ip_offset)); + + if (!stack_slot_is_managed_pointer (dest)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid destination of stobj operation at 0x%04x", ctx->ip_offset)); + + if (stack_slot_is_boxed_value (src) && !MONO_TYPE_IS_REFERENCE (src->type) && !MONO_TYPE_IS_REFERENCE (type)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Cannot use stobj with a boxed source value that is not a reference type at 0x%04x", ctx->ip_offset)); + + if (!verify_stack_type_compatibility (ctx, type, src)) { + char *type_name = mono_type_full_name (type); + char *src_name = stack_slot_full_name (src); + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Token '%s' and source '%s' of stobj don't match ' at 0x%04x", type_name, src_name, ctx->ip_offset)); + g_free (type_name); + g_free (src_name); + } + + if (!verify_type_compatibility (ctx, mono_type_get_type_byval (dest->type), type)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Destination and token types of stobj don't match at 0x%04x", ctx->ip_offset)); +} + +static void +do_cpobj (VerifyContext *ctx, int token) +{ + ILStackDesc *dest, *src; + MonoType *type = get_boxable_mono_type (ctx, token, "cpobj"); + if (!type) + return; + + if (!check_underflow (ctx, 2)) + return; + + src = stack_pop (ctx); + dest = stack_pop (ctx); + + if (!stack_slot_is_managed_pointer (src)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid source of cpobj operation at 0x%04x", ctx->ip_offset)); + + if (!stack_slot_is_managed_pointer (dest)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid destination of cpobj operation at 0x%04x", ctx->ip_offset)); + + if (stack_slot_is_managed_mutability_pointer (dest)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Cannot use a readonly pointer with cpobj at 0x%04x", ctx->ip_offset)); + + if (!verify_type_compatibility (ctx, type, mono_type_get_type_byval (src->type))) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Token and source types of cpobj don't match at 0x%04x", ctx->ip_offset)); + + if (!verify_type_compatibility (ctx, mono_type_get_type_byval (dest->type), type)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Destination and token types of cpobj don't match at 0x%04x", ctx->ip_offset)); +} + +static void +do_initobj (VerifyContext *ctx, int token) +{ + ILStackDesc *obj; + MonoType *stack, *type = get_boxable_mono_type (ctx, token, "initobj"); + if (!type) + return; + + if (!check_underflow (ctx, 1)) + return; + + obj = stack_pop (ctx); + + if (!stack_slot_is_managed_pointer (obj)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid object address for initobj at 0x%04x", ctx->ip_offset)); + + if (stack_slot_is_managed_mutability_pointer (obj)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Cannot use a readonly pointer with initobj at 0x%04x", ctx->ip_offset)); + + stack = mono_type_get_type_byval (obj->type); + if (MONO_TYPE_IS_REFERENCE (stack)) { + if (!verify_type_compatibility (ctx, stack, type)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Type token of initobj not compatible with value on stack at 0x%04x", ctx->ip_offset)); + else if (IS_STRICT_MODE (ctx) && !mono_metadata_type_equal (type, stack)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Type token of initobj not compatible with value on stack at 0x%04x", ctx->ip_offset)); + } else if (!verify_type_compatibility (ctx, stack, type)) { + char *expected_name = mono_type_full_name (type); + char *stack_name = mono_type_full_name (stack); + + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Initobj %s not compatible with value on stack %s at 0x%04x", expected_name, stack_name, ctx->ip_offset)); + g_free (expected_name); + g_free (stack_name); + } +} + +static void +do_newobj (VerifyContext *ctx, int token) +{ + ILStackDesc *value; + int i; + MonoMethodSignature *sig; + MonoMethod *method; + gboolean is_delegate = FALSE; + + if (!(method = verifier_load_method (ctx, token, "newobj"))) + return; + + if (!mono_method_is_constructor (method)) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Method from token 0x%08x not a constructor at 0x%04x", token, ctx->ip_offset)); + return; + } + + if (mono_class_get_flags (method->klass) & (TYPE_ATTRIBUTE_ABSTRACT | TYPE_ATTRIBUTE_INTERFACE)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Trying to instantiate an abstract or interface type at 0x%04x", ctx->ip_offset)); + + if (!IS_SKIP_VISIBILITY (ctx) && !mono_method_can_access_method_full (ctx->method, method, NULL)) { + char *from = mono_method_full_name (ctx->method, TRUE); + char *to = mono_method_full_name (method, TRUE); + CODE_NOT_VERIFIABLE2 (ctx, g_strdup_printf ("Constructor %s not visible from %s at 0x%04x", to, from, ctx->ip_offset), MONO_EXCEPTION_METHOD_ACCESS); + g_free (from); + g_free (to); + } + + //FIXME use mono_method_get_signature_full + sig = mono_method_signature (method); + if (!sig) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Invalid constructor signature to newobj at 0x%04x", ctx->ip_offset)); + return; + } + + if (!sig->hasthis) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Invalid constructor signature missing hasthis at 0x%04x", ctx->ip_offset)); + return; + } + + if (!check_underflow (ctx, sig->param_count)) + return; + + is_delegate = method->klass->parent == mono_defaults.multicastdelegate_class; + + if (is_delegate) { + ILStackDesc *funptr; + //first arg is object, second arg is fun ptr + if (sig->param_count != 2) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Invalid delegate constructor at 0x%04x", ctx->ip_offset)); + return; + } + funptr = stack_pop (ctx); + value = stack_pop (ctx); + verify_delegate_compatibility (ctx, method->klass, value, funptr); + } else { + for (i = sig->param_count - 1; i >= 0; --i) { + VERIFIER_DEBUG ( printf ("verifying constructor argument %d\n", i); ); + value = stack_pop (ctx); + if (!verify_stack_type_compatibility (ctx, sig->params [i], value)) { + char *stack_name = stack_slot_full_name (value); + char *sig_name = mono_type_full_name (sig->params [i]); + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Incompatible parameter value with constructor signature: %s X %s at 0x%04x", sig_name, stack_name, ctx->ip_offset)); + g_free (stack_name); + g_free (sig_name); + } + + if (stack_slot_is_managed_mutability_pointer (value)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Cannot use a readonly pointer as argument of newobj at 0x%04x", ctx->ip_offset)); + } + } + + if (check_overflow (ctx)) + set_stack_value (ctx, stack_push (ctx), &method->klass->byval_arg, FALSE); +} + +static void +do_cast (VerifyContext *ctx, int token, const char *opcode) { + ILStackDesc *value; + MonoType *type; + gboolean is_boxed; + gboolean do_box; + + if (!check_underflow (ctx, 1)) + return; + + if (!(type = get_boxable_mono_type (ctx, token, opcode))) + return; + + if (type->byref) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Invalid %s type at 0x%04x", opcode, ctx->ip_offset)); + return; + } + + value = stack_pop (ctx); + is_boxed = stack_slot_is_boxed_value (value); + + if (stack_slot_is_managed_pointer (value)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid value for %s at 0x%04x", opcode, ctx->ip_offset)); + else if (!MONO_TYPE_IS_REFERENCE (value->type) && !is_boxed) { + char *name = stack_slot_full_name (value); + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Expected a reference type on stack for %s but found %s at 0x%04x", opcode, name, ctx->ip_offset)); + g_free (name); + } + + switch (value->type->type) { + case MONO_TYPE_FNPTR: + case MONO_TYPE_PTR: + case MONO_TYPE_TYPEDBYREF: + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid value for %s at 0x%04x", opcode, ctx->ip_offset)); + default: + break; + } + + do_box = is_boxed || mono_type_is_generic_argument(type) || mono_class_from_mono_type (type)->valuetype; + stack_push_val (ctx, TYPE_COMPLEX | (do_box ? BOXED_MASK : 0), type); +} + +static MonoType * +mono_type_from_opcode (int opcode) { + switch (opcode) { + case CEE_LDIND_I1: + case CEE_LDIND_U1: + case CEE_STIND_I1: + case CEE_LDELEM_I1: + case CEE_LDELEM_U1: + case CEE_STELEM_I1: + return &mono_defaults.sbyte_class->byval_arg; + + case CEE_LDIND_I2: + case CEE_LDIND_U2: + case CEE_STIND_I2: + case CEE_LDELEM_I2: + case CEE_LDELEM_U2: + case CEE_STELEM_I2: + return &mono_defaults.int16_class->byval_arg; + + case CEE_LDIND_I4: + case CEE_LDIND_U4: + case CEE_STIND_I4: + case CEE_LDELEM_I4: + case CEE_LDELEM_U4: + case CEE_STELEM_I4: + return &mono_defaults.int32_class->byval_arg; + + case CEE_LDIND_I8: + case CEE_STIND_I8: + case CEE_LDELEM_I8: + case CEE_STELEM_I8: + return &mono_defaults.int64_class->byval_arg; + + case CEE_LDIND_R4: + case CEE_STIND_R4: + case CEE_LDELEM_R4: + case CEE_STELEM_R4: + return &mono_defaults.single_class->byval_arg; + + case CEE_LDIND_R8: + case CEE_STIND_R8: + case CEE_LDELEM_R8: + case CEE_STELEM_R8: + return &mono_defaults.double_class->byval_arg; + + case CEE_LDIND_I: + case CEE_STIND_I: + case CEE_LDELEM_I: + case CEE_STELEM_I: + return &mono_defaults.int_class->byval_arg; + + case CEE_LDIND_REF: + case CEE_STIND_REF: + case CEE_LDELEM_REF: + case CEE_STELEM_REF: + return &mono_defaults.object_class->byval_arg; + + default: + g_error ("unknown opcode %02x in mono_type_from_opcode ", opcode); + return NULL; + } +} + +static void +do_load_indirect (VerifyContext *ctx, int opcode) +{ + ILStackDesc *value; + CLEAR_PREFIX (ctx, PREFIX_UNALIGNED | PREFIX_VOLATILE); + + if (!check_underflow (ctx, 1)) + return; + + value = stack_pop (ctx); + if (!stack_slot_is_managed_pointer (value)) { + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Load indirect not using a manager pointer at 0x%04x", ctx->ip_offset)); + set_stack_value (ctx, stack_push (ctx), mono_type_from_opcode (opcode), FALSE); + return; + } + + if (opcode == CEE_LDIND_REF) { + if (stack_slot_get_underlying_type (value) != TYPE_COMPLEX || mono_class_from_mono_type (value->type)->valuetype) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid type at stack for ldind_ref expected object byref operation at 0x%04x", ctx->ip_offset)); + set_stack_value (ctx, stack_push (ctx), mono_type_get_type_byval (value->type), FALSE); + } else { + if (!verify_type_compatibility_full (ctx, mono_type_from_opcode (opcode), mono_type_get_type_byval (value->type), TRUE)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid type at stack for ldind 0x%x operation at 0x%04x", opcode, ctx->ip_offset)); + set_stack_value (ctx, stack_push (ctx), mono_type_from_opcode (opcode), FALSE); + } +} + +static void +do_store_indirect (VerifyContext *ctx, int opcode) +{ + ILStackDesc *addr, *val; + CLEAR_PREFIX (ctx, PREFIX_UNALIGNED | PREFIX_VOLATILE); + + if (!check_underflow (ctx, 2)) + return; + + val = stack_pop (ctx); + addr = stack_pop (ctx); + + check_unmanaged_pointer (ctx, addr); + + if (!stack_slot_is_managed_pointer (addr) && stack_slot_get_type (addr) != TYPE_PTR) { + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid non-pointer argument to stind at 0x%04x", ctx->ip_offset)); + return; + } + + if (stack_slot_is_managed_mutability_pointer (addr)) { + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Cannot use a readonly pointer with stind at 0x%04x", ctx->ip_offset)); + return; + } + + if (!verify_type_compatibility_full (ctx, mono_type_from_opcode (opcode), mono_type_get_type_byval (addr->type), TRUE)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid addr type at stack for stind 0x%x operation at 0x%04x", opcode, ctx->ip_offset)); + + if (!verify_stack_type_compatibility (ctx, mono_type_from_opcode (opcode), val)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid value type at stack for stind 0x%x operation at 0x%04x", opcode, ctx->ip_offset)); +} + +static void +do_newarr (VerifyContext *ctx, int token) +{ + ILStackDesc *value; + MonoType *type = get_boxable_mono_type (ctx, token, "newarr"); + + if (!type) + return; + + if (!check_underflow (ctx, 1)) + return; + + value = stack_pop (ctx); + if (stack_slot_get_type (value) != TYPE_I4 && stack_slot_get_type (value) != TYPE_NATIVE_INT) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Array size type on stack (%s) is not a verifiable type at 0x%04x", stack_slot_get_name (value), ctx->ip_offset)); + + set_stack_value (ctx, stack_push (ctx), mono_class_get_type (mono_array_class_get (mono_class_from_mono_type (type), 1)), FALSE); +} + +/*FIXME handle arrays that are not 0-indexed*/ +static void +do_ldlen (VerifyContext *ctx) +{ + ILStackDesc *value; + + if (!check_underflow (ctx, 1)) + return; + + value = stack_pop (ctx); + + if (stack_slot_get_type (value) != TYPE_COMPLEX || value->type->type != MONO_TYPE_SZARRAY) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid array type for ldlen at 0x%04x", ctx->ip_offset)); + + stack_push_val (ctx, TYPE_NATIVE_INT, &mono_defaults.int_class->byval_arg); +} + +/*FIXME handle arrays that are not 0-indexed*/ +/*FIXME handle readonly prefix and CMMP*/ +static void +do_ldelema (VerifyContext *ctx, int klass_token) +{ + ILStackDesc *index, *array, *res; + MonoType *type = get_boxable_mono_type (ctx, klass_token, "ldelema"); + gboolean valid; + + if (!type) + return; + + if (!check_underflow (ctx, 2)) + return; + + index = stack_pop (ctx); + array = stack_pop (ctx); + + if (stack_slot_get_type (index) != TYPE_I4 && stack_slot_get_type (index) != TYPE_NATIVE_INT) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Index type(%s) for ldelema is not an int or a native int at 0x%04x", stack_slot_get_name (index), ctx->ip_offset)); + + if (!stack_slot_is_null_literal (array)) { + if (stack_slot_get_type (array) != TYPE_COMPLEX || array->type->type != MONO_TYPE_SZARRAY) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid array type(%s) for ldelema at 0x%04x", stack_slot_get_name (array), ctx->ip_offset)); + else { + if (get_stack_type (type) == TYPE_I4 || get_stack_type (type) == TYPE_NATIVE_INT) { + valid = verify_type_compatibility_full (ctx, type, &array->type->data.klass->byval_arg, TRUE); + } else { + valid = mono_metadata_type_equal (type, &array->type->data.klass->byval_arg); + } + if (!valid) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid array type on stack for ldelema at 0x%04x", ctx->ip_offset)); + } + } + + res = stack_push (ctx); + set_stack_value (ctx, res, type, TRUE); + if (ctx->prefix_set & PREFIX_READONLY) { + ctx->prefix_set &= ~PREFIX_READONLY; + res->stype |= CMMP_MASK; + } + + res->stype |= SAFE_BYREF_MASK; +} + +/* + * FIXME handle arrays that are not 0-indexed + * FIXME handle readonly prefix and CMMP + */ +static void +do_ldelem (VerifyContext *ctx, int opcode, int token) +{ +#define IS_ONE_OF2(T, A, B) (T == A || T == B) + ILStackDesc *index, *array; + MonoType *type; + if (!check_underflow (ctx, 2)) + return; + + if (opcode == CEE_LDELEM) { + if (!(type = verifier_load_type (ctx, token, "ldelem.any"))) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Type (0x%08x) not found at 0x%04x", token, ctx->ip_offset)); + return; + } + } else { + type = mono_type_from_opcode (opcode); + } + + index = stack_pop (ctx); + array = stack_pop (ctx); + + if (stack_slot_get_type (index) != TYPE_I4 && stack_slot_get_type (index) != TYPE_NATIVE_INT) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Index type(%s) for ldelem.X is not an int or a native int at 0x%04x", stack_slot_get_name (index), ctx->ip_offset)); + + if (!stack_slot_is_null_literal (array)) { + if (stack_slot_get_type (array) != TYPE_COMPLEX || array->type->type != MONO_TYPE_SZARRAY) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid array type(%s) for ldelem.X at 0x%04x", stack_slot_get_name (array), ctx->ip_offset)); + else { + if (opcode == CEE_LDELEM_REF) { + if (array->type->data.klass->valuetype) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid array type is not a reference type for ldelem.ref 0x%04x", ctx->ip_offset)); + type = &array->type->data.klass->byval_arg; + } else { + MonoType *candidate = &array->type->data.klass->byval_arg; + if (IS_STRICT_MODE (ctx)) { + MonoType *underlying_type = mono_type_get_underlying_type_any (type); + MonoType *underlying_candidate = mono_type_get_underlying_type_any (candidate); + if ((IS_ONE_OF2 (underlying_type->type, MONO_TYPE_I4, MONO_TYPE_U4) && IS_ONE_OF2 (underlying_candidate->type, MONO_TYPE_I, MONO_TYPE_U)) || + (IS_ONE_OF2 (underlying_candidate->type, MONO_TYPE_I4, MONO_TYPE_U4) && IS_ONE_OF2 (underlying_type->type, MONO_TYPE_I, MONO_TYPE_U))) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid array type on stack for ldelem.X at 0x%04x", ctx->ip_offset)); + } + if (!verify_type_compatibility_full (ctx, type, candidate, TRUE)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid array type on stack for ldelem.X at 0x%04x", ctx->ip_offset)); + } + } + } + + set_stack_value (ctx, stack_push (ctx), type, FALSE); +#undef IS_ONE_OF2 +} + +/* + * FIXME handle arrays that are not 0-indexed + */ +static void +do_stelem (VerifyContext *ctx, int opcode, int token) +{ + ILStackDesc *index, *array, *value; + MonoType *type; + if (!check_underflow (ctx, 3)) + return; + + if (opcode == CEE_STELEM) { + if (!(type = verifier_load_type (ctx, token, "stelem.any"))) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Type (0x%08x) not found at 0x%04x", token, ctx->ip_offset)); + return; + } + } else { + type = mono_type_from_opcode (opcode); + } + + value = stack_pop (ctx); + index = stack_pop (ctx); + array = stack_pop (ctx); + + if (stack_slot_get_type (index) != TYPE_I4 && stack_slot_get_type (index) != TYPE_NATIVE_INT) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Index type(%s) for stdelem.X is not an int or a native int at 0x%04x", stack_slot_get_name (index), ctx->ip_offset)); + + if (!stack_slot_is_null_literal (array)) { + if (stack_slot_get_type (array) != TYPE_COMPLEX || array->type->type != MONO_TYPE_SZARRAY) { + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid array type(%s) for stelem.X at 0x%04x", stack_slot_get_name (array), ctx->ip_offset)); + } else { + if (opcode == CEE_STELEM_REF) { + if (array->type->data.klass->valuetype) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid array type is not a reference type for stelem.ref 0x%04x", ctx->ip_offset)); + } else if (!verify_type_compatibility_full (ctx, &array->type->data.klass->byval_arg, type, TRUE)) { + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid array type on stack for stdelem.X at 0x%04x", ctx->ip_offset)); + } + } + } + if (opcode == CEE_STELEM_REF) { + if (!stack_slot_is_boxed_value (value) && mono_class_from_mono_type (value->type)->valuetype) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid value is not a reference type for stelem.ref 0x%04x", ctx->ip_offset)); + } else if (opcode != CEE_STELEM_REF) { + if (!verify_stack_type_compatibility (ctx, type, value)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid value on stack for stdelem.X at 0x%04x", ctx->ip_offset)); + + if (stack_slot_is_boxed_value (value) && !MONO_TYPE_IS_REFERENCE (value->type) && !MONO_TYPE_IS_REFERENCE (type)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Cannot use stobj with a boxed source value that is not a reference type at 0x%04x", ctx->ip_offset)); + + } +} + +static void +do_throw (VerifyContext *ctx) +{ + ILStackDesc *exception; + if (!check_underflow (ctx, 1)) + return; + exception = stack_pop (ctx); + + if (!stack_slot_is_null_literal (exception) && !(stack_slot_get_type (exception) == TYPE_COMPLEX && !mono_class_from_mono_type (exception->type)->valuetype)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid type on stack for throw, expected reference type at 0x%04x", ctx->ip_offset)); + + if (mono_type_is_generic_argument (exception->type) && !stack_slot_is_boxed_value (exception)) { + char *name = mono_type_full_name (exception->type); + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid type on stack for throw, expected reference type but found unboxed %s at 0x%04x ", name, ctx->ip_offset)); + g_free (name); + } + /*The stack is left empty after a throw*/ + ctx->eval.size = 0; +} + + +static void +do_endfilter (VerifyContext *ctx) +{ + MonoExceptionClause *clause; + + if (IS_STRICT_MODE (ctx)) { + if (ctx->eval.size != 1) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Stack size must have one item for endfilter at 0x%04x", ctx->ip_offset)); + + if (ctx->eval.size >= 1 && stack_slot_get_type (stack_pop (ctx)) != TYPE_I4) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Stack item type is not an int32 for endfilter at 0x%04x", ctx->ip_offset)); + } + + if ((clause = is_correct_endfilter (ctx, ctx->ip_offset))) { + if (IS_STRICT_MODE (ctx)) { + if (ctx->ip_offset != clause->handler_offset - 2) + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("endfilter is not the last instruction of the filter clause at 0x%04x", ctx->ip_offset)); + } else { + if ((ctx->ip_offset != clause->handler_offset - 2) && !MONO_OFFSET_IN_HANDLER (clause, ctx->ip_offset)) + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("endfilter is not the last instruction of the filter clause at 0x%04x", ctx->ip_offset)); + } + } else { + if (IS_STRICT_MODE (ctx) && !is_unverifiable_endfilter (ctx, ctx->ip_offset)) + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("endfilter outside filter clause at 0x%04x", ctx->ip_offset)); + else + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("endfilter outside filter clause at 0x%04x", ctx->ip_offset)); + } + + ctx->eval.size = 0; +} + +static void +do_leave (VerifyContext *ctx, int delta) +{ + int target = ((gint32)ctx->ip_offset) + delta; + if (target >= ctx->code_size || target < 0) + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Branch target out of code at 0x%04x", ctx->ip_offset)); + + if (!is_correct_leave (ctx->header, ctx->ip_offset, target)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Leave not allowed in finally block at 0x%04x", ctx->ip_offset)); + ctx->eval.size = 0; + ctx->target = target; +} + +/* + * do_static_branch: + * + * Verify br and br.s opcodes. + */ +static void +do_static_branch (VerifyContext *ctx, int delta) +{ + int target = ctx->ip_offset + delta; + if (target < 0 || target >= ctx->code_size) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("branch target out of code at 0x%04x", ctx->ip_offset)); + return; + } + + switch (is_valid_branch_instruction (ctx->header, ctx->ip_offset, target)) { + case 1: + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Branch target escapes out of exception block at 0x%04x", ctx->ip_offset)); + break; + case 2: + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Branch target escapes out of exception block at 0x%04x", ctx->ip_offset)); + break; + } + + ctx->target = target; +} + +static void +do_switch (VerifyContext *ctx, int count, const unsigned char *data) +{ + int i, base = ctx->ip_offset + 5 + count * 4; + ILStackDesc *value; + + if (!check_underflow (ctx, 1)) + return; + + value = stack_pop (ctx); + + if (stack_slot_get_type (value) != TYPE_I4 && stack_slot_get_type (value) != TYPE_NATIVE_INT) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid argument to switch at 0x%04x", ctx->ip_offset)); + + for (i = 0; i < count; ++i) { + int target = base + read32 (data + i * 4); + + if (target < 0 || target >= ctx->code_size) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Switch target %x out of code at 0x%04x", i, ctx->ip_offset)); + return; + } + + switch (is_valid_branch_instruction (ctx->header, ctx->ip_offset, target)) { + case 1: + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Switch target %x escapes out of exception block at 0x%04x", i, ctx->ip_offset)); + break; + case 2: + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Switch target %x escapes out of exception block at 0x%04x", i, ctx->ip_offset)); + return; + } + merge_stacks (ctx, &ctx->eval, &ctx->code [target], FALSE, TRUE); + } +} + +static void +do_load_function_ptr (VerifyContext *ctx, guint32 token, gboolean virtual_) +{ + ILStackDesc *top; + MonoMethod *method; + + if (virtual_ && !check_underflow (ctx, 1)) + return; + + if (!virtual_ && !check_overflow (ctx)) + return; + + if (ctx->method->wrapper_type != MONO_WRAPPER_NONE) { + method = (MonoMethod *)mono_method_get_wrapper_data (ctx->method, (guint32)token); + if (!method) { + ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Invalid token %x for ldftn at 0x%04x", token, ctx->ip_offset), MONO_EXCEPTION_BAD_IMAGE); + return; + } + } else { + if (!IS_METHOD_DEF_OR_REF_OR_SPEC (token) || !token_bounds_check (ctx->image, token)) { + ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Invalid token %x for ldftn at 0x%04x", token, ctx->ip_offset), MONO_EXCEPTION_BAD_IMAGE); + return; + } + + if (!(method = verifier_load_method (ctx, token, virtual_ ? "ldvirtfrn" : "ldftn"))) + return; + } + + if (mono_method_is_constructor (method)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Cannot use ldftn with a constructor at 0x%04x", ctx->ip_offset)); + + if (virtual_) { + ILStackDesc *top = stack_pop (ctx); + + if (stack_slot_get_type (top) != TYPE_COMPLEX || top->type->type == MONO_TYPE_VALUETYPE) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid argument to ldvirtftn at 0x%04x", ctx->ip_offset)); + + if (method->flags & METHOD_ATTRIBUTE_STATIC) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Cannot use ldvirtftn with a constructor at 0x%04x", ctx->ip_offset)); + + if (!verify_stack_type_compatibility (ctx, &method->klass->byval_arg, top)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Unexpected object for ldvirtftn at 0x%04x", ctx->ip_offset)); + } + + if (!IS_SKIP_VISIBILITY (ctx) && !mono_method_can_access_method_full (ctx->method, method, NULL)) + CODE_NOT_VERIFIABLE2 (ctx, g_strdup_printf ("Loaded method is not visible for ldftn/ldvirtftn at 0x%04x", ctx->ip_offset), MONO_EXCEPTION_METHOD_ACCESS); + + top = stack_push_val(ctx, TYPE_PTR, mono_type_create_fnptr_from_mono_method (ctx, method)); + top->method = method; +} + +static void +do_sizeof (VerifyContext *ctx, int token) +{ + MonoType *type; + + if (!(type = verifier_load_type (ctx, token, "sizeof"))) + return; + + if (type->byref && type->type != MONO_TYPE_TYPEDBYREF) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Invalid use of byref type at 0x%04x", ctx->ip_offset)); + return; + } + + if (type->type == MONO_TYPE_VOID) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Invalid use of void type at 0x%04x", ctx->ip_offset)); + return; + } + + if (check_overflow (ctx)) + set_stack_value (ctx, stack_push (ctx), &mono_defaults.uint32_class->byval_arg, FALSE); +} + +/* Stack top can be of any type, the runtime doesn't care and treat everything as an int. */ +static void +do_localloc (VerifyContext *ctx) +{ + if (ctx->eval.size != 1) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Stack must have only size item in localloc at 0x%04x", ctx->ip_offset)); + return; + } + + if (in_any_exception_block (ctx->header, ctx->ip_offset)) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Stack must have only size item in localloc at 0x%04x", ctx->ip_offset)); + return; + } + + /*TODO verify top type*/ + /* top = */ stack_pop (ctx); + + set_stack_value (ctx, stack_push (ctx), &mono_defaults.int_class->byval_arg, FALSE); + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Instruction localloc in never verifiable at 0x%04x", ctx->ip_offset)); +} + +static void +do_ldstr (VerifyContext *ctx, guint32 token) +{ + GSList *error = NULL; + if (ctx->method->wrapper_type == MONO_WRAPPER_NONE && !image_is_dynamic (ctx->image)) { + if (mono_metadata_token_code (token) != MONO_TOKEN_STRING) { + ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Invalid string token %x at 0x%04x", token, ctx->ip_offset), MONO_EXCEPTION_BAD_IMAGE); + return; + } + + if (!mono_verifier_verify_string_signature (ctx->image, mono_metadata_token_index (token), &error)) { + if (error) + ctx->list = g_slist_concat (ctx->list, error); + ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Invalid string index %x at 0x%04x", token, ctx->ip_offset), MONO_EXCEPTION_BAD_IMAGE); + return; + } + } + + if (check_overflow (ctx)) + stack_push_val (ctx, TYPE_COMPLEX, &mono_defaults.string_class->byval_arg); +} + +static void +do_refanyval (VerifyContext *ctx, int token) +{ + ILStackDesc *top; + MonoType *type; + if (!check_underflow (ctx, 1)) + return; + + if (!(type = get_boxable_mono_type (ctx, token, "refanyval"))) + return; + + top = stack_pop (ctx); + + if (top->stype != TYPE_PTR || top->type->type != MONO_TYPE_TYPEDBYREF) + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Expected a typedref as argument for refanyval, but found %s at 0x%04x", stack_slot_get_name (top), ctx->ip_offset)); + + set_stack_value (ctx, stack_push (ctx), type, TRUE); +} + +static void +do_refanytype (VerifyContext *ctx) +{ + ILStackDesc *top; + + if (!check_underflow (ctx, 1)) + return; + + top = stack_pop (ctx); + + if (top->stype != TYPE_PTR || top->type->type != MONO_TYPE_TYPEDBYREF) + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Expected a typedref as argument for refanytype, but found %s at 0x%04x", stack_slot_get_name (top), ctx->ip_offset)); + + set_stack_value (ctx, stack_push (ctx), &mono_defaults.typehandle_class->byval_arg, FALSE); + +} + +static void +do_mkrefany (VerifyContext *ctx, int token) +{ + ILStackDesc *top; + MonoType *type; + if (!check_underflow (ctx, 1)) + return; + + if (!(type = get_boxable_mono_type (ctx, token, "refanyval"))) + return; + + top = stack_pop (ctx); + + if (stack_slot_is_managed_mutability_pointer (top)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Cannot use a readonly pointer with mkrefany at 0x%04x", ctx->ip_offset)); + + if (!stack_slot_is_managed_pointer (top)) { + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Expected a managed pointer for mkrefany, but found %s at 0x%04x", stack_slot_get_name (top), ctx->ip_offset)); + }else { + MonoType *stack_type = mono_type_get_type_byval (top->type); + if (MONO_TYPE_IS_REFERENCE (type) && !mono_metadata_type_equal (type, stack_type)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Type not compatible for mkrefany at 0x%04x", ctx->ip_offset)); + + if (!MONO_TYPE_IS_REFERENCE (type) && !verify_type_compatibility_full (ctx, type, stack_type, TRUE)) + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Type not compatible for mkrefany at 0x%04x", ctx->ip_offset)); + } + + set_stack_value (ctx, stack_push (ctx), &mono_defaults.typed_reference_class->byval_arg, FALSE); +} + +static void +do_ckfinite (VerifyContext *ctx) +{ + ILStackDesc *top; + if (!check_underflow (ctx, 1)) + return; + + top = stack_pop (ctx); + + if (stack_slot_get_underlying_type (top) != TYPE_R8) + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Expected float32 or float64 on stack for ckfinit but found %s at 0x%04x", stack_slot_get_name (top), ctx->ip_offset)); + stack_push_stack_val (ctx, top); +} +/* + * merge_stacks: + * Merge the stacks and perform compat checks. The merge check if types of @from are mergeable with type of @to + * + * @from holds new values for a given control path + * @to holds the current values of a given control path + * + * TODO we can eliminate the from argument as all callers pass &ctx->eval + */ +static void +merge_stacks (VerifyContext *ctx, ILCodeDesc *from, ILCodeDesc *to, gboolean start, gboolean external) +{ + MonoError error; + int i, j; + stack_init (ctx, to); + + if (start) { + if (to->flags == IL_CODE_FLAG_NOT_PROCESSED) + from->size = 0; + else + stack_copy (&ctx->eval, to); + goto end_verify; + } else if (!(to->flags & IL_CODE_STACK_MERGED)) { + stack_copy (to, &ctx->eval); + goto end_verify; + } + VERIFIER_DEBUG ( printf ("performing stack merge %d x %d\n", from->size, to->size); ); + + if (from->size != to->size) { + VERIFIER_DEBUG ( printf ("different stack sizes %d x %d at 0x%04x\n", from->size, to->size, ctx->ip_offset); ); + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Could not merge stacks, different sizes (%d x %d) at 0x%04x", from->size, to->size, ctx->ip_offset)); + goto end_verify; + } + + //FIXME we need to preserve CMMP attributes + //FIXME we must take null literals into consideration. + for (i = 0; i < from->size; ++i) { + ILStackDesc *new_slot = from->stack + i; + ILStackDesc *old_slot = to->stack + i; + MonoType *new_type = mono_type_from_stack_slot (new_slot); + MonoType *old_type = mono_type_from_stack_slot (old_slot); + MonoClass *old_class = mono_class_from_mono_type (old_type); + MonoClass *new_class = mono_class_from_mono_type (new_type); + MonoClass *match_class = NULL; + + // check for safe byref before the next steps override new_slot + if (stack_slot_is_safe_byref (old_slot) ^ stack_slot_is_safe_byref (new_slot)) { + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Cannot merge stack at depth %d byref types are safe byref incompatible at %0x04x ", i, ctx->ip_offset)); + goto end_verify; + } + + // S := T then U = S (new value is compatible with current value, keep current) + if (verify_stack_type_compatibility (ctx, old_type, new_slot)) { + copy_stack_value (new_slot, old_slot); + continue; + } + + // T := S then U = T (old value is compatible with current value, use new) + if (verify_stack_type_compatibility (ctx, new_type, old_slot)) { + copy_stack_value (old_slot, new_slot); + continue; + } + + /*Both slots are the same boxed valuetype. Simply copy it.*/ + if (stack_slot_is_boxed_value (old_slot) && + stack_slot_is_boxed_value (new_slot) && + mono_metadata_type_equal (old_type, new_type)) { + copy_stack_value (new_slot, old_slot); + continue; + } + + if (mono_type_is_generic_argument (old_type) || mono_type_is_generic_argument (new_type)) { + char *old_name = stack_slot_full_name (old_slot); + char *new_name = stack_slot_full_name (new_slot); + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Could not merge stack at depth %d, types not compatible: %s X %s at 0x%04x", i, old_name, new_name, ctx->ip_offset)); + g_free (old_name); + g_free (new_name); + goto end_verify; + } + + //both are reference types, use closest common super type + if (!mono_class_from_mono_type (old_type)->valuetype + && !mono_class_from_mono_type (new_type)->valuetype + && !stack_slot_is_managed_pointer (old_slot) + && !stack_slot_is_managed_pointer (new_slot)) { + + mono_class_setup_supertypes (old_class); + mono_class_setup_supertypes (new_class); + + for (j = MIN (old_class->idepth, new_class->idepth) - 1; j > 0; --j) { + if (mono_metadata_type_equal (&old_class->supertypes [j]->byval_arg, &new_class->supertypes [j]->byval_arg)) { + match_class = old_class->supertypes [j]; + goto match_found; + } + } + + mono_class_setup_interfaces (old_class, &error); + if (!mono_error_ok (&error)) { + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Cannot merge stacks due to a TypeLoadException %s at 0x%04x", mono_error_get_message (&error), ctx->ip_offset)); + mono_error_cleanup (&error); + goto end_verify; + } + mono_class_setup_interfaces (new_class, &error); + if (!mono_error_ok (&error)) { + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Cannot merge stacks due to a TypeLoadException %s at 0x%04x", mono_error_get_message (&error), ctx->ip_offset)); + mono_error_cleanup (&error); + goto end_verify; + } + + /* if old class is an interface that new class implements */ + if (mono_class_is_interface (old_class)) { + if (verifier_class_is_assignable_from (old_class, new_class)) { + match_class = old_class; + goto match_found; + } + for (j = 0; j < old_class->interface_count; ++j) { + if (verifier_class_is_assignable_from (old_class->interfaces [j], new_class)) { + match_class = old_class->interfaces [j]; + goto match_found; + } + } + } + + if (mono_class_is_interface (new_class)) { + if (verifier_class_is_assignable_from (new_class, old_class)) { + match_class = new_class; + goto match_found; + } + for (j = 0; j < new_class->interface_count; ++j) { + if (verifier_class_is_assignable_from (new_class->interfaces [j], old_class)) { + match_class = new_class->interfaces [j]; + goto match_found; + } + } + } + + //No decent super type found, use object + match_class = mono_defaults.object_class; + goto match_found; + } else if (is_compatible_boxed_valuetype (ctx,old_type, new_type, new_slot, FALSE) || is_compatible_boxed_valuetype (ctx, new_type, old_type, old_slot, FALSE)) { + match_class = mono_defaults.object_class; + goto match_found; + } + + { + char *old_name = stack_slot_full_name (old_slot); + char *new_name = stack_slot_full_name (new_slot); + CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Could not merge stack at depth %d, types not compatible: %s X %s at 0x%04x", i, old_name, new_name, ctx->ip_offset)); + g_free (old_name); + g_free (new_name); + } + set_stack_value (ctx, old_slot, &new_class->byval_arg, stack_slot_is_managed_pointer (old_slot)); + goto end_verify; + +match_found: + g_assert (match_class); + set_stack_value (ctx, old_slot, &match_class->byval_arg, stack_slot_is_managed_pointer (old_slot)); + set_stack_value (ctx, new_slot, &match_class->byval_arg, stack_slot_is_managed_pointer (old_slot)); + continue; + } + +end_verify: + if (external) + to->flags |= IL_CODE_FLAG_WAS_TARGET; + to->flags |= IL_CODE_STACK_MERGED; +} + +#define HANDLER_START(clause) ((clause)->flags == MONO_EXCEPTION_CLAUSE_FILTER ? (clause)->data.filter_offset : clause->handler_offset) +#define IS_CATCH_OR_FILTER(clause) ((clause)->flags == MONO_EXCEPTION_CLAUSE_FILTER || (clause)->flags == MONO_EXCEPTION_CLAUSE_NONE) + +/** + * is_clause_in_range : + * + * Returns TRUE if either the protected block or the handler of @clause is in the @start - @end range. + */ +static gboolean +is_clause_in_range (MonoExceptionClause *clause, guint32 start, guint32 end) +{ + if (clause->try_offset >= start && clause->try_offset < end) + return TRUE; + if (HANDLER_START (clause) >= start && HANDLER_START (clause) < end) + return TRUE; + return FALSE; +} + +/** + * is_clause_inside_range : + * + * Returns TRUE if @clause lies completely inside the @start - @end range. + */ +static gboolean +is_clause_inside_range (MonoExceptionClause *clause, guint32 start, guint32 end) +{ + if (clause->try_offset < start || (clause->try_offset + clause->try_len) > end) + return FALSE; + if (HANDLER_START (clause) < start || (clause->handler_offset + clause->handler_len) > end) + return FALSE; + return TRUE; +} + +/** + * is_clause_nested : + * + * Returns TRUE if @nested is nested in @clause. + */ +static gboolean +is_clause_nested (MonoExceptionClause *clause, MonoExceptionClause *nested) +{ + if (clause->flags == MONO_EXCEPTION_CLAUSE_FILTER && is_clause_inside_range (nested, clause->data.filter_offset, clause->handler_offset)) + return TRUE; + return is_clause_inside_range (nested, clause->try_offset, clause->try_offset + clause->try_len) || + is_clause_inside_range (nested, clause->handler_offset, clause->handler_offset + clause->handler_len); +} + +/* Test the relationship between 2 exception clauses. Follow P.1 12.4.2.7 of ECMA + * the each pair of exception must have the following properties: + * - one is fully nested on another (the outer must not be a filter clause) (the nested one must come earlier) + * - completely disjoin (none of the 3 regions of each entry overlap with the other 3) + * - mutual protection (protected block is EXACT the same, handlers are disjoin and all handler are catch or all handler are filter) + */ +static void +verify_clause_relationship (VerifyContext *ctx, MonoExceptionClause *clause, MonoExceptionClause *to_test) +{ + /*clause is nested*/ + if (to_test->flags == MONO_EXCEPTION_CLAUSE_FILTER && is_clause_inside_range (clause, to_test->data.filter_offset, to_test->handler_offset)) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Exception clause inside filter")); + return; + } + + /*wrong nesting order.*/ + if (is_clause_nested (clause, to_test)) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Nested exception clause appears after enclosing clause")); + return; + } + + /*mutual protection*/ + if (clause->try_offset == to_test->try_offset && clause->try_len == to_test->try_len) { + /*handlers are not disjoint*/ + if (is_clause_in_range (to_test, HANDLER_START (clause), clause->handler_offset + clause->handler_len)) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Exception handlers overlap")); + return; + } + /* handlers are not catch or filter */ + if (!IS_CATCH_OR_FILTER (clause) || !IS_CATCH_OR_FILTER (to_test)) { + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Exception clauses with shared protected block are neither catch or filter")); + return; + } + /*OK*/ + return; + } + + /*not completelly disjoint*/ + if ((is_clause_in_range (to_test, clause->try_offset, clause->try_offset + clause->try_len) || + is_clause_in_range (to_test, HANDLER_START (clause), clause->handler_offset + clause->handler_len)) && !is_clause_nested (to_test, clause)) + ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Exception clauses overlap")); +} + +#define code_bounds_check(size) \ + if (ADDP_IS_GREATER_OR_OVF (ip, size, end)) {\ + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Code overrun starting with 0x%x at 0x%04x", *ip, ctx.ip_offset)); \ + break; \ + } \ + +static gboolean +mono_opcode_is_prefix (int op) +{ + switch (op) { + case MONO_CEE_UNALIGNED_: + case MONO_CEE_VOLATILE_: + case MONO_CEE_TAIL_: + case MONO_CEE_CONSTRAINED_: + case MONO_CEE_READONLY_: + return TRUE; + } + return FALSE; +} + +/* + * FIXME: need to distinguish between valid and verifiable. + * Need to keep track of types on the stack. + */ + +/** + * mono_method_verify: + * Verify types for opcodes. + */ +GSList* +mono_method_verify (MonoMethod *method, int level) +{ + MonoError error; + const unsigned char *ip, *code_start; + const unsigned char *end; + MonoSimpleBasicBlock *bb = NULL, *original_bb = NULL; + + int i, n, need_merge = 0, start = 0; + guint ip_offset = 0, prefix = 0; + MonoGenericContext *generic_context = NULL; + MonoImage *image; + VerifyContext ctx; + GSList *tmp; + VERIFIER_DEBUG ( printf ("Verify IL for method %s %s %s\n", method->klass->name_space, method->klass->name, method->name); ); + + init_verifier_stats (); + + if (method->iflags & (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL | METHOD_IMPL_ATTRIBUTE_RUNTIME) || + (method->flags & (METHOD_ATTRIBUTE_PINVOKE_IMPL | METHOD_ATTRIBUTE_ABSTRACT))) { + return NULL; + } + + memset (&ctx, 0, sizeof (VerifyContext)); + + //FIXME use mono_method_get_signature_full + ctx.signature = mono_method_signature (method); + if (!ctx.signature) { + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Could not decode method signature")); + + finish_collect_stats (); + return ctx.list; + } + if (!method->is_generic && !mono_class_is_gtd (method->klass) && ctx.signature->has_type_parameters) { + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Method and signature don't match in terms of genericity")); + finish_collect_stats (); + return ctx.list; + } + + ctx.header = mono_method_get_header_checked (method, &error); + if (!ctx.header) { + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Could not decode method header due to %s", mono_error_get_message (&error))); + mono_error_cleanup (&error); + finish_collect_stats (); + return ctx.list; + } + ctx.method = method; + code_start = ip = ctx.header->code; + end = ip + ctx.header->code_size; + ctx.image = image = method->klass->image; + + + ctx.max_args = ctx.signature->param_count + ctx.signature->hasthis; + ctx.max_stack = ctx.header->max_stack; + ctx.verifiable = ctx.valid = 1; + ctx.level = level; + + ctx.code = g_new (ILCodeDesc, ctx.header->code_size); + ctx.code_size = ctx.header->code_size; + _MEM_ALLOC (sizeof (ILCodeDesc) * ctx.header->code_size); + + memset(ctx.code, 0, sizeof (ILCodeDesc) * ctx.header->code_size); + + ctx.num_locals = ctx.header->num_locals; + ctx.locals = (MonoType **)g_memdup (ctx.header->locals, sizeof (MonoType*) * ctx.header->num_locals); + _MEM_ALLOC (sizeof (MonoType*) * ctx.header->num_locals); + ctx.locals_verification_state = g_new0 (char, ctx.num_locals); + + if (ctx.num_locals > 0 && !ctx.header->init_locals) + CODE_NOT_VERIFIABLE (&ctx, g_strdup_printf ("Method with locals variable but without init locals set")); + + ctx.params = g_new (MonoType*, ctx.max_args); + _MEM_ALLOC (sizeof (MonoType*) * ctx.max_args); + + if (ctx.signature->hasthis) + ctx.params [0] = method->klass->valuetype ? &method->klass->this_arg : &method->klass->byval_arg; + memcpy (ctx.params + ctx.signature->hasthis, ctx.signature->params, sizeof (MonoType *) * ctx.signature->param_count); + + if (ctx.signature->is_inflated) + ctx.generic_context = generic_context = mono_method_get_context (method); + + if (!generic_context && (mono_class_is_gtd (method->klass) || method->is_generic)) { + if (method->is_generic) + ctx.generic_context = generic_context = &(mono_method_get_generic_container (method)->context); + else + ctx.generic_context = generic_context = &mono_class_get_generic_container (method->klass)->context; + } + + for (i = 0; i < ctx.num_locals; ++i) { + MonoType *uninflated = ctx.locals [i]; + ctx.locals [i] = mono_class_inflate_generic_type_checked (ctx.locals [i], ctx.generic_context, &error); + if (!mono_error_ok (&error)) { + char *name = mono_type_full_name (ctx.locals [i] ? ctx.locals [i] : uninflated); + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Invalid local %d of type %s", i, name)); + g_free (name); + mono_error_cleanup (&error); + /* we must not free (in cleanup) what was not yet allocated (but only copied) */ + ctx.num_locals = i; + ctx.max_args = 0; + goto cleanup; + } + } + for (i = 0; i < ctx.max_args; ++i) { + MonoType *uninflated = ctx.params [i]; + ctx.params [i] = mono_class_inflate_generic_type_checked (ctx.params [i], ctx.generic_context, &error); + if (!mono_error_ok (&error)) { + char *name = mono_type_full_name (ctx.params [i] ? ctx.params [i] : uninflated); + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Invalid parameter %d of type %s", i, name)); + g_free (name); + mono_error_cleanup (&error); + /* we must not free (in cleanup) what was not yet allocated (but only copied) */ + ctx.max_args = i; + goto cleanup; + } + } + stack_init (&ctx, &ctx.eval); + + for (i = 0; i < ctx.num_locals; ++i) { + if (!mono_type_is_valid_in_context (&ctx, ctx.locals [i])) + break; + if (get_stack_type (ctx.locals [i]) == TYPE_INV) { + char *name = mono_type_full_name (ctx.locals [i]); + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Invalid local %i of type %s", i, name)); + g_free (name); + break; + } + + } + + for (i = 0; i < ctx.max_args; ++i) { + if (!mono_type_is_valid_in_context (&ctx, ctx.params [i])) + break; + + if (get_stack_type (ctx.params [i]) == TYPE_INV) { + char *name = mono_type_full_name (ctx.params [i]); + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Invalid parameter %i of type %s", i, name)); + g_free (name); + break; + } + } + + if (!ctx.valid) + goto cleanup; + + for (i = 0; i < ctx.header->num_clauses && ctx.valid; ++i) { + MonoExceptionClause *clause = ctx.header->clauses + i; + VERIFIER_DEBUG (printf ("clause try %x len %x filter at %x handler at %x len %x\n", clause->try_offset, clause->try_len, clause->data.filter_offset, clause->handler_offset, clause->handler_len); ); + + if (clause->try_offset > ctx.code_size || ADD_IS_GREATER_OR_OVF (clause->try_offset, clause->try_len, ctx.code_size)) + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("try clause out of bounds at 0x%04x", clause->try_offset)); + + if (clause->try_len <= 0) + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("try clause len <= 0 at 0x%04x", clause->try_offset)); + + if (clause->handler_offset > ctx.code_size || ADD_IS_GREATER_OR_OVF (clause->handler_offset, clause->handler_len, ctx.code_size)) + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("handler clause out of bounds at 0x%04x", clause->try_offset)); + + if (clause->handler_len <= 0) + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("handler clause len <= 0 at 0x%04x", clause->try_offset)); + + if (clause->try_offset < clause->handler_offset && ADD_IS_GREATER_OR_OVF (clause->try_offset, clause->try_len, HANDLER_START (clause))) + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("try block (at 0x%04x) includes handler block (at 0x%04x)", clause->try_offset, clause->handler_offset)); + + if (clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) { + if (clause->data.filter_offset > ctx.code_size) + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("filter clause out of bounds at 0x%04x", clause->try_offset)); + + if (clause->data.filter_offset >= clause->handler_offset) + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("filter clause must come before the handler clause at 0x%04x", clause->data.filter_offset)); + } + + for (n = i + 1; n < ctx.header->num_clauses && ctx.valid; ++n) + verify_clause_relationship (&ctx, clause, ctx.header->clauses + n); + + if (!ctx.valid) + break; + + ctx.code [clause->try_offset].flags |= IL_CODE_FLAG_WAS_TARGET; + if (clause->try_offset + clause->try_len < ctx.code_size) + ctx.code [clause->try_offset + clause->try_len].flags |= IL_CODE_FLAG_WAS_TARGET; + if (clause->handler_offset + clause->handler_len < ctx.code_size) + ctx.code [clause->handler_offset + clause->handler_len].flags |= IL_CODE_FLAG_WAS_TARGET; + + if (clause->flags == MONO_EXCEPTION_CLAUSE_NONE) { + if (!clause->data.catch_class) { + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Catch clause %d with invalid type", i)); + break; + } + if (!mono_type_is_valid_in_context (&ctx, &clause->data.catch_class->byval_arg)) + break; + + init_stack_with_value_at_exception_boundary (&ctx, ctx.code + clause->handler_offset, clause->data.catch_class); + } + else if (clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) { + init_stack_with_value_at_exception_boundary (&ctx, ctx.code + clause->data.filter_offset, mono_defaults.exception_class); + init_stack_with_value_at_exception_boundary (&ctx, ctx.code + clause->handler_offset, mono_defaults.exception_class); + } + } + + if (!ctx.valid) + goto cleanup; + + original_bb = bb = mono_basic_block_split (method, &error, ctx.header); + if (!mono_error_ok (&error)) { + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Invalid branch target: %s", mono_error_get_message (&error))); + mono_error_cleanup (&error); + goto cleanup; + } + g_assert (bb); + + while (ip < end && ctx.valid) { + int op_size; + ip_offset = (guint) (ip - code_start); + { + const unsigned char *ip_copy = ip; + int op; + + if (ip_offset > bb->end) { + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Branch or EH block at [0x%04x] targets middle instruction at 0x%04x", bb->end, ip_offset)); + goto cleanup; + } + + if (ip_offset == bb->end) + bb = bb->next; + + op_size = mono_opcode_value_and_size (&ip_copy, end, &op); + if (op_size == -1) { + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Invalid instruction %x at 0x%04x", *ip, ip_offset)); + goto cleanup; + } + + if (ADD_IS_GREATER_OR_OVF (ip_offset, op_size, bb->end)) { + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Branch or EH block targets middle of instruction at 0x%04x", ip_offset)); + goto cleanup; + } + + /*Last Instruction*/ + if (ip_offset + op_size == bb->end && mono_opcode_is_prefix (op)) { + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Branch or EH block targets between prefix '%s' and instruction at 0x%04x", mono_opcode_name (op), ip_offset)); + goto cleanup; + } + } + + ctx.ip_offset = ip_offset = (guint) (ip - code_start); + + /*We need to check against fallthrou in and out of protected blocks. + * For fallout we check the once a protected block ends, if the start flag is not set. + * Likewise for fallthru in, we check if ip is the start of a protected block and start is not set + * TODO convert these checks to be done using flags and not this loop + */ + for (i = 0; i < ctx.header->num_clauses && ctx.valid; ++i) { + MonoExceptionClause *clause = ctx.header->clauses + i; + + if ((clause->try_offset + clause->try_len == ip_offset) && start == 0) { + CODE_NOT_VERIFIABLE (&ctx, g_strdup_printf ("fallthru off try block at 0x%04x", ip_offset)); + start = 1; + } + + if ((clause->handler_offset + clause->handler_len == ip_offset) && start == 0) { + if (clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("fallout of handler block at 0x%04x", ip_offset)); + else + CODE_NOT_VERIFIABLE (&ctx, g_strdup_printf ("fallout of handler block at 0x%04x", ip_offset)); + start = 1; + } + + if (clause->flags == MONO_EXCEPTION_CLAUSE_FILTER && clause->handler_offset == ip_offset && start == 0) { + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("fallout of filter block at 0x%04x", ip_offset)); + start = 1; + } + + if (clause->handler_offset == ip_offset && start == 0) { + CODE_NOT_VERIFIABLE (&ctx, g_strdup_printf ("fallthru handler block at 0x%04x", ip_offset)); + start = 1; + } + + if (clause->try_offset == ip_offset && ctx.eval.size > 0 && start == 0) { + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Try to enter try block with a non-empty stack at 0x%04x", ip_offset)); + start = 1; + } + } + + /*This must be done after fallthru detection otherwise it won't happen.*/ + if (bb->dead) { + /*FIXME remove this once we move all bad branch checking code to use BB only*/ + ctx.code [ip_offset].flags |= IL_CODE_FLAG_SEEN; + ip += op_size; + continue; + } + + if (!ctx.valid) + break; + + if (need_merge) { + VERIFIER_DEBUG ( printf ("extra merge needed! 0x%04x \n", ctx.target); ); + merge_stacks (&ctx, &ctx.eval, &ctx.code [ctx.target], FALSE, TRUE); + need_merge = 0; + } + merge_stacks (&ctx, &ctx.eval, &ctx.code[ip_offset], start, FALSE); + start = 0; + + /*TODO we can fast detect a forward branch or exception block targeting code after prefix, we should fail fast*/ +#ifdef MONO_VERIFIER_DEBUG + { + char *discode; + discode = mono_disasm_code_one (NULL, method, ip, NULL); + discode [strlen (discode) - 1] = 0; /* no \n */ + g_print ("[%d] %-29s (%d)\n", ip_offset, discode, ctx.eval.size); + g_free (discode); + } + dump_stack_state (&ctx.code [ip_offset]); + dump_stack_state (&ctx.eval); +#endif + + switch (*ip) { + case CEE_NOP: + case CEE_BREAK: + ++ip; + break; + + case CEE_LDARG_0: + case CEE_LDARG_1: + case CEE_LDARG_2: + case CEE_LDARG_3: + push_arg (&ctx, *ip - CEE_LDARG_0, FALSE); + ++ip; + break; + + case CEE_LDARG_S: + case CEE_LDARGA_S: + code_bounds_check (2); + push_arg (&ctx, ip [1], *ip == CEE_LDARGA_S); + ip += 2; + break; + + case CEE_ADD_OVF_UN: + do_binop (&ctx, *ip, add_ovf_un_table); + ++ip; + break; + + case CEE_SUB_OVF_UN: + do_binop (&ctx, *ip, sub_ovf_un_table); + ++ip; + break; + + case CEE_ADD_OVF: + case CEE_SUB_OVF: + case CEE_MUL_OVF: + case CEE_MUL_OVF_UN: + do_binop (&ctx, *ip, bin_ovf_table); + ++ip; + break; + + case CEE_ADD: + do_binop (&ctx, *ip, add_table); + ++ip; + break; + + case CEE_SUB: + do_binop (&ctx, *ip, sub_table); + ++ip; + break; + + case CEE_MUL: + case CEE_DIV: + case CEE_REM: + do_binop (&ctx, *ip, bin_op_table); + ++ip; + break; + + case CEE_AND: + case CEE_DIV_UN: + case CEE_OR: + case CEE_REM_UN: + case CEE_XOR: + do_binop (&ctx, *ip, int_bin_op_table); + ++ip; + break; + + case CEE_SHL: + case CEE_SHR: + case CEE_SHR_UN: + do_binop (&ctx, *ip, shift_op_table); + ++ip; + break; + + case CEE_POP: + if (!check_underflow (&ctx, 1)) + break; + stack_pop_safe (&ctx); + ++ip; + break; + + case CEE_RET: + do_ret (&ctx); + ++ip; + start = 1; + break; + + case CEE_LDLOC_0: + case CEE_LDLOC_1: + case CEE_LDLOC_2: + case CEE_LDLOC_3: + /*TODO support definite assignment verification? */ + push_local (&ctx, *ip - CEE_LDLOC_0, FALSE); + ++ip; + break; + + case CEE_STLOC_0: + case CEE_STLOC_1: + case CEE_STLOC_2: + case CEE_STLOC_3: + store_local (&ctx, *ip - CEE_STLOC_0); + ++ip; + break; + + case CEE_STLOC_S: + code_bounds_check (2); + store_local (&ctx, ip [1]); + ip += 2; + break; + + case CEE_STARG_S: + code_bounds_check (2); + store_arg (&ctx, ip [1]); + ip += 2; + break; + + case CEE_LDC_I4_M1: + case CEE_LDC_I4_0: + case CEE_LDC_I4_1: + case CEE_LDC_I4_2: + case CEE_LDC_I4_3: + case CEE_LDC_I4_4: + case CEE_LDC_I4_5: + case CEE_LDC_I4_6: + case CEE_LDC_I4_7: + case CEE_LDC_I4_8: + if (check_overflow (&ctx)) + stack_push_val (&ctx, TYPE_I4, &mono_defaults.int32_class->byval_arg); + ++ip; + break; + + case CEE_LDC_I4_S: + code_bounds_check (2); + if (check_overflow (&ctx)) + stack_push_val (&ctx, TYPE_I4, &mono_defaults.int32_class->byval_arg); + ip += 2; + break; + + case CEE_LDC_I4: + code_bounds_check (5); + if (check_overflow (&ctx)) + stack_push_val (&ctx,TYPE_I4, &mono_defaults.int32_class->byval_arg); + ip += 5; + break; + + case CEE_LDC_I8: + code_bounds_check (9); + if (check_overflow (&ctx)) + stack_push_val (&ctx,TYPE_I8, &mono_defaults.int64_class->byval_arg); + ip += 9; + break; + + case CEE_LDC_R4: + code_bounds_check (5); + if (check_overflow (&ctx)) + stack_push_val (&ctx, TYPE_R8, &mono_defaults.double_class->byval_arg); + ip += 5; + break; + + case CEE_LDC_R8: + code_bounds_check (9); + if (check_overflow (&ctx)) + stack_push_val (&ctx, TYPE_R8, &mono_defaults.double_class->byval_arg); + ip += 9; + break; + + case CEE_LDNULL: + if (check_overflow (&ctx)) + stack_push_val (&ctx, TYPE_COMPLEX | NULL_LITERAL_MASK, &mono_defaults.object_class->byval_arg); + ++ip; + break; + + case CEE_BEQ_S: + case CEE_BNE_UN_S: + code_bounds_check (2); + do_branch_op (&ctx, (signed char)ip [1] + 2, cmp_br_eq_op); + ip += 2; + need_merge = 1; + break; + + case CEE_BGE_S: + case CEE_BGT_S: + case CEE_BLE_S: + case CEE_BLT_S: + case CEE_BGE_UN_S: + case CEE_BGT_UN_S: + case CEE_BLE_UN_S: + case CEE_BLT_UN_S: + code_bounds_check (2); + do_branch_op (&ctx, (signed char)ip [1] + 2, cmp_br_op); + ip += 2; + need_merge = 1; + break; + + case CEE_BEQ: + case CEE_BNE_UN: + code_bounds_check (5); + do_branch_op (&ctx, (gint32)read32 (ip + 1) + 5, cmp_br_eq_op); + ip += 5; + need_merge = 1; + break; + + case CEE_BGE: + case CEE_BGT: + case CEE_BLE: + case CEE_BLT: + case CEE_BGE_UN: + case CEE_BGT_UN: + case CEE_BLE_UN: + case CEE_BLT_UN: + code_bounds_check (5); + do_branch_op (&ctx, (gint32)read32 (ip + 1) + 5, cmp_br_op); + ip += 5; + need_merge = 1; + break; + + case CEE_LDLOC_S: + case CEE_LDLOCA_S: + code_bounds_check (2); + push_local (&ctx, ip[1], *ip == CEE_LDLOCA_S); + ip += 2; + break; + + case CEE_UNUSED99: + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Use of the `unused' opcode")); + ++ip; + break; + + case CEE_DUP: { + ILStackDesc *top; + if (!check_underflow (&ctx, 1)) + break; + if (!check_overflow (&ctx)) + break; + top = stack_push (&ctx); + copy_stack_value (top, stack_peek (&ctx, 1)); + ++ip; + break; + } + + case CEE_JMP: + code_bounds_check (5); + if (ctx.eval.size) + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Eval stack must be empty in jmp at 0x%04x", ip_offset)); + /* token = read32 (ip + 1); */ + if (in_any_block (ctx.header, ip_offset)) + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("jmp cannot escape exception blocks at 0x%04x", ip_offset)); + + CODE_NOT_VERIFIABLE (&ctx, g_strdup_printf ("Intruction jmp is not verifiable at 0x%04x", ctx.ip_offset)); + /* + * FIXME: check signature, retval, arguments etc. + */ + ip += 5; + break; + case CEE_CALL: + case CEE_CALLVIRT: + code_bounds_check (5); + do_invoke_method (&ctx, read32 (ip + 1), *ip == CEE_CALLVIRT); + ip += 5; + break; + + case CEE_CALLI: + code_bounds_check (5); + /* token = read32 (ip + 1); */ + /* + * FIXME: check signature, retval, arguments etc. + * FIXME: check requirements for tail call + */ + CODE_NOT_VERIFIABLE (&ctx, g_strdup_printf ("Intruction calli is not verifiable at 0x%04x", ctx.ip_offset)); + ip += 5; + break; + case CEE_BR_S: + code_bounds_check (2); + do_static_branch (&ctx, (signed char)ip [1] + 2); + need_merge = 1; + ip += 2; + start = 1; + break; + + case CEE_BRFALSE_S: + case CEE_BRTRUE_S: + code_bounds_check (2); + do_boolean_branch_op (&ctx, (signed char)ip [1] + 2); + ip += 2; + need_merge = 1; + break; + + case CEE_BR: + code_bounds_check (5); + do_static_branch (&ctx, (gint32)read32 (ip + 1) + 5); + need_merge = 1; + ip += 5; + start = 1; + break; + + case CEE_BRFALSE: + case CEE_BRTRUE: + code_bounds_check (5); + do_boolean_branch_op (&ctx, (gint32)read32 (ip + 1) + 5); + ip += 5; + need_merge = 1; + break; + + case CEE_SWITCH: { + guint32 entries; + code_bounds_check (5); + entries = read32 (ip + 1); + + if (entries > 0xFFFFFFFFU / sizeof (guint32)) + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Too many switch entries %x at 0x%04x", entries, ctx.ip_offset)); + + ip += 5; + code_bounds_check (sizeof (guint32) * entries); + + do_switch (&ctx, entries, ip); + ip += sizeof (guint32) * entries; + break; + } + case CEE_LDIND_I1: + case CEE_LDIND_U1: + case CEE_LDIND_I2: + case CEE_LDIND_U2: + case CEE_LDIND_I4: + case CEE_LDIND_U4: + case CEE_LDIND_I8: + case CEE_LDIND_I: + case CEE_LDIND_R4: + case CEE_LDIND_R8: + case CEE_LDIND_REF: + do_load_indirect (&ctx, *ip); + ++ip; + break; + + case CEE_STIND_REF: + case CEE_STIND_I1: + case CEE_STIND_I2: + case CEE_STIND_I4: + case CEE_STIND_I8: + case CEE_STIND_R4: + case CEE_STIND_R8: + case CEE_STIND_I: + do_store_indirect (&ctx, *ip); + ++ip; + break; + + case CEE_NOT: + case CEE_NEG: + do_unary_math_op (&ctx, *ip); + ++ip; + break; + + case CEE_CONV_I1: + case CEE_CONV_I2: + case CEE_CONV_I4: + case CEE_CONV_U1: + case CEE_CONV_U2: + case CEE_CONV_U4: + do_conversion (&ctx, TYPE_I4); + ++ip; + break; + + case CEE_CONV_I8: + case CEE_CONV_U8: + do_conversion (&ctx, TYPE_I8); + ++ip; + break; + + case CEE_CONV_R4: + case CEE_CONV_R8: + case CEE_CONV_R_UN: + do_conversion (&ctx, TYPE_R8); + ++ip; + break; + + case CEE_CONV_I: + case CEE_CONV_U: + do_conversion (&ctx, TYPE_NATIVE_INT); + ++ip; + break; + + case CEE_CPOBJ: + code_bounds_check (5); + do_cpobj (&ctx, read32 (ip + 1)); + ip += 5; + break; + + case CEE_LDOBJ: + code_bounds_check (5); + do_ldobj_value (&ctx, read32 (ip + 1)); + ip += 5; + break; + + case CEE_LDSTR: + code_bounds_check (5); + do_ldstr (&ctx, read32 (ip + 1)); + ip += 5; + break; + + case CEE_NEWOBJ: + code_bounds_check (5); + do_newobj (&ctx, read32 (ip + 1)); + ip += 5; + break; + + case CEE_CASTCLASS: + case CEE_ISINST: + code_bounds_check (5); + do_cast (&ctx, read32 (ip + 1), *ip == CEE_CASTCLASS ? "castclass" : "isinst"); + ip += 5; + break; + + case CEE_UNUSED58: + case CEE_UNUSED1: + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Use of the `unused' opcode")); + ++ip; + break; + + case CEE_UNBOX: + code_bounds_check (5); + do_unbox_value (&ctx, read32 (ip + 1)); + ip += 5; + break; + + case CEE_THROW: + do_throw (&ctx); + start = 1; + ++ip; + break; + + case CEE_LDFLD: + case CEE_LDFLDA: + code_bounds_check (5); + do_push_field (&ctx, read32 (ip + 1), *ip == CEE_LDFLDA); + ip += 5; + break; + + case CEE_LDSFLD: + case CEE_LDSFLDA: + code_bounds_check (5); + do_push_static_field (&ctx, read32 (ip + 1), *ip == CEE_LDSFLDA); + ip += 5; + break; + + case CEE_STFLD: + code_bounds_check (5); + do_store_field (&ctx, read32 (ip + 1)); + ip += 5; + break; + + case CEE_STSFLD: + code_bounds_check (5); + do_store_static_field (&ctx, read32 (ip + 1)); + ip += 5; + break; + + case CEE_STOBJ: + code_bounds_check (5); + do_stobj (&ctx, read32 (ip + 1)); + ip += 5; + break; + + case CEE_CONV_OVF_I1_UN: + case CEE_CONV_OVF_I2_UN: + case CEE_CONV_OVF_I4_UN: + case CEE_CONV_OVF_U1_UN: + case CEE_CONV_OVF_U2_UN: + case CEE_CONV_OVF_U4_UN: + do_conversion (&ctx, TYPE_I4); + ++ip; + break; + + case CEE_CONV_OVF_I8_UN: + case CEE_CONV_OVF_U8_UN: + do_conversion (&ctx, TYPE_I8); + ++ip; + break; + + case CEE_CONV_OVF_I_UN: + case CEE_CONV_OVF_U_UN: + do_conversion (&ctx, TYPE_NATIVE_INT); + ++ip; + break; + + case CEE_BOX: + code_bounds_check (5); + do_box_value (&ctx, read32 (ip + 1)); + ip += 5; + break; + + case CEE_NEWARR: + code_bounds_check (5); + do_newarr (&ctx, read32 (ip + 1)); + ip += 5; + break; + + case CEE_LDLEN: + do_ldlen (&ctx); + ++ip; + break; + + case CEE_LDELEMA: + code_bounds_check (5); + do_ldelema (&ctx, read32 (ip + 1)); + ip += 5; + break; + + case CEE_LDELEM_I1: + case CEE_LDELEM_U1: + case CEE_LDELEM_I2: + case CEE_LDELEM_U2: + case CEE_LDELEM_I4: + case CEE_LDELEM_U4: + case CEE_LDELEM_I8: + case CEE_LDELEM_I: + case CEE_LDELEM_R4: + case CEE_LDELEM_R8: + case CEE_LDELEM_REF: + do_ldelem (&ctx, *ip, 0); + ++ip; + break; + + case CEE_STELEM_I: + case CEE_STELEM_I1: + case CEE_STELEM_I2: + case CEE_STELEM_I4: + case CEE_STELEM_I8: + case CEE_STELEM_R4: + case CEE_STELEM_R8: + case CEE_STELEM_REF: + do_stelem (&ctx, *ip, 0); + ++ip; + break; + + case CEE_LDELEM: + code_bounds_check (5); + do_ldelem (&ctx, *ip, read32 (ip + 1)); + ip += 5; + break; + + case CEE_STELEM: + code_bounds_check (5); + do_stelem (&ctx, *ip, read32 (ip + 1)); + ip += 5; + break; + + case CEE_UNBOX_ANY: + code_bounds_check (5); + do_unbox_any (&ctx, read32 (ip + 1)); + ip += 5; + break; + + case CEE_CONV_OVF_I1: + case CEE_CONV_OVF_U1: + case CEE_CONV_OVF_I2: + case CEE_CONV_OVF_U2: + case CEE_CONV_OVF_I4: + case CEE_CONV_OVF_U4: + do_conversion (&ctx, TYPE_I4); + ++ip; + break; + + case CEE_CONV_OVF_I8: + case CEE_CONV_OVF_U8: + do_conversion (&ctx, TYPE_I8); + ++ip; + break; + + case CEE_CONV_OVF_I: + case CEE_CONV_OVF_U: + do_conversion (&ctx, TYPE_NATIVE_INT); + ++ip; + break; + + case CEE_REFANYVAL: + code_bounds_check (5); + do_refanyval (&ctx, read32 (ip + 1)); + ip += 5; + break; + + case CEE_CKFINITE: + do_ckfinite (&ctx); + ++ip; + break; + + case CEE_MKREFANY: + code_bounds_check (5); + do_mkrefany (&ctx, read32 (ip + 1)); + ip += 5; + break; + + case CEE_LDTOKEN: + code_bounds_check (5); + do_load_token (&ctx, read32 (ip + 1)); + ip += 5; + break; + + case CEE_ENDFINALLY: + if (!is_correct_endfinally (ctx.header, ip_offset)) + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("endfinally must be used inside a finally/fault handler at 0x%04x", ctx.ip_offset)); + ctx.eval.size = 0; + start = 1; + ++ip; + break; + + case CEE_LEAVE: + code_bounds_check (5); + do_leave (&ctx, read32 (ip + 1) + 5); + ip += 5; + start = 1; + need_merge = 1; + break; + + case CEE_LEAVE_S: + code_bounds_check (2); + do_leave (&ctx, (signed char)ip [1] + 2); + ip += 2; + start = 1; + need_merge = 1; + break; + + case CEE_PREFIX1: + code_bounds_check (2); + ++ip; + switch (*ip) { + case CEE_STLOC: + code_bounds_check (3); + store_local (&ctx, read16 (ip + 1)); + ip += 3; + break; + + case CEE_CEQ: + do_cmp_op (&ctx, cmp_br_eq_op, *ip); + ++ip; + break; + + case CEE_CGT: + case CEE_CGT_UN: + case CEE_CLT: + case CEE_CLT_UN: + do_cmp_op (&ctx, cmp_br_op, *ip); + ++ip; + break; + + case CEE_STARG: + code_bounds_check (3); + store_arg (&ctx, read16 (ip + 1) ); + ip += 3; + break; + + + case CEE_ARGLIST: + if (!check_overflow (&ctx)) + break; + if (ctx.signature->call_convention != MONO_CALL_VARARG) + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Cannot use arglist on method without VARGARG calling convention at 0x%04x", ctx.ip_offset)); + set_stack_value (&ctx, stack_push (&ctx), &mono_defaults.argumenthandle_class->byval_arg, FALSE); + ++ip; + break; + + case CEE_LDFTN: + code_bounds_check (5); + do_load_function_ptr (&ctx, read32 (ip + 1), FALSE); + ip += 5; + break; + + case CEE_LDVIRTFTN: + code_bounds_check (5); + do_load_function_ptr (&ctx, read32 (ip + 1), TRUE); + ip += 5; + break; + + case CEE_LDARG: + case CEE_LDARGA: + code_bounds_check (3); + push_arg (&ctx, read16 (ip + 1), *ip == CEE_LDARGA); + ip += 3; + break; + + case CEE_LDLOC: + case CEE_LDLOCA: + code_bounds_check (3); + push_local (&ctx, read16 (ip + 1), *ip == CEE_LDLOCA); + ip += 3; + break; + + case CEE_LOCALLOC: + do_localloc (&ctx); + ++ip; + break; + + case CEE_UNUSED56: + case CEE_UNUSED57: + case CEE_UNUSED70: + case CEE_UNUSED: + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Use of the `unused' opcode")); + ++ip; + break; + case CEE_ENDFILTER: + do_endfilter (&ctx); + start = 1; + ++ip; + break; + case CEE_UNALIGNED_: + code_bounds_check (2); + prefix |= PREFIX_UNALIGNED; + ip += 2; + break; + case CEE_VOLATILE_: + prefix |= PREFIX_VOLATILE; + ++ip; + break; + case CEE_TAIL_: + prefix |= PREFIX_TAIL; + ++ip; + if (ip < end && (*ip != CEE_CALL && *ip != CEE_CALLI && *ip != CEE_CALLVIRT)) + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("tail prefix must be used only with call opcodes at 0x%04x", ip_offset)); + break; + + case CEE_INITOBJ: + code_bounds_check (5); + do_initobj (&ctx, read32 (ip + 1)); + ip += 5; + break; + + case CEE_CONSTRAINED_: + code_bounds_check (5); + ctx.constrained_type = get_boxable_mono_type (&ctx, read32 (ip + 1), "constrained."); + prefix |= PREFIX_CONSTRAINED; + ip += 5; + break; + + case CEE_READONLY_: + prefix |= PREFIX_READONLY; + ip++; + break; + + case CEE_CPBLK: + CLEAR_PREFIX (&ctx, PREFIX_UNALIGNED | PREFIX_VOLATILE); + if (!check_underflow (&ctx, 3)) + break; + CODE_NOT_VERIFIABLE (&ctx, g_strdup_printf ("Instruction cpblk is not verifiable at 0x%04x", ctx.ip_offset)); + ip++; + break; + + case CEE_INITBLK: + CLEAR_PREFIX (&ctx, PREFIX_UNALIGNED | PREFIX_VOLATILE); + if (!check_underflow (&ctx, 3)) + break; + CODE_NOT_VERIFIABLE (&ctx, g_strdup_printf ("Instruction initblk is not verifiable at 0x%04x", ctx.ip_offset)); + ip++; + break; + + case CEE_NO_: + ip += 2; + break; + case CEE_RETHROW: + if (!is_correct_rethrow (ctx.header, ip_offset)) + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("rethrow must be used inside a catch handler at 0x%04x", ctx.ip_offset)); + ctx.eval.size = 0; + start = 1; + ++ip; + break; + + case CEE_SIZEOF: + code_bounds_check (5); + do_sizeof (&ctx, read32 (ip + 1)); + ip += 5; + break; + + case CEE_REFANYTYPE: + do_refanytype (&ctx); + ++ip; + break; + + default: + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Invalid instruction FE %x at 0x%04x", *ip, ctx.ip_offset)); + ++ip; + } + break; + + default: + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Invalid instruction %x at 0x%04x", *ip, ctx.ip_offset)); + ++ip; + } + + /*TODO we can fast detect a forward branch or exception block targeting code after prefix, we should fail fast*/ + if (prefix) { + if (!ctx.prefix_set) //first prefix + ctx.code [ctx.ip_offset].flags |= IL_CODE_FLAG_SEEN; + ctx.prefix_set |= prefix; + ctx.has_flags = TRUE; + prefix = 0; + } else { + if (!ctx.has_flags) + ctx.code [ctx.ip_offset].flags |= IL_CODE_FLAG_SEEN; + + if (ctx.prefix_set & PREFIX_CONSTRAINED) + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Invalid instruction after constrained prefix at 0x%04x", ctx.ip_offset)); + if (ctx.prefix_set & PREFIX_READONLY) + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Invalid instruction after readonly prefix at 0x%04x", ctx.ip_offset)); + if (ctx.prefix_set & PREFIX_VOLATILE) + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Invalid instruction after volatile prefix at 0x%04x", ctx.ip_offset)); + if (ctx.prefix_set & PREFIX_UNALIGNED) + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Invalid instruction after unaligned prefix at 0x%04x", ctx.ip_offset)); + ctx.prefix_set = prefix = 0; + ctx.has_flags = FALSE; + } + } + /* + * if ip != end we overflowed: mark as error. + */ + if ((ip != end || !start) && ctx.verifiable && !ctx.list) { + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Run ahead of method code at 0x%04x", ip_offset)); + } + + /*We should guard against the last decoded opcode, otherwise we might add errors that doesn't make sense.*/ + for (i = 0; i < ctx.code_size && i < ip_offset; ++i) { + if (ctx.code [i].flags & IL_CODE_FLAG_WAS_TARGET) { + if (!(ctx.code [i].flags & IL_CODE_FLAG_SEEN)) + ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Branch or exception block target middle of instruction at 0x%04x", i)); + + if (ctx.code [i].flags & IL_CODE_DELEGATE_SEQUENCE) + CODE_NOT_VERIFIABLE (&ctx, g_strdup_printf ("Branch to delegate code sequence at 0x%04x", i)); + } + if ((ctx.code [i].flags & IL_CODE_LDFTN_DELEGATE_NONFINAL_VIRTUAL) && ctx.has_this_store) + CODE_NOT_VERIFIABLE (&ctx, g_strdup_printf ("Invalid ldftn with virtual function in method with stdarg 0 at 0x%04x", i)); + + if ((ctx.code [i].flags & IL_CODE_CALL_NONFINAL_VIRTUAL) && ctx.has_this_store) + CODE_NOT_VERIFIABLE (&ctx, g_strdup_printf ("Invalid call to a non-final virtual function in method with stdarg.0 or ldarga.0 at 0x%04x", i)); + } + + if (mono_method_is_constructor (ctx.method) && !ctx.super_ctor_called && !ctx.method->klass->valuetype && ctx.method->klass != mono_defaults.object_class) { + char *method_name = mono_method_full_name (ctx.method, TRUE); + char *type = mono_type_get_full_name (ctx.method->klass); + if (ctx.method->klass->parent && mono_class_has_failure (ctx.method->klass->parent)) + CODE_NOT_VERIFIABLE (&ctx, g_strdup_printf ("Constructor %s for type %s not calling base type ctor due to a TypeLoadException on base type.", method_name, type)); + else + CODE_NOT_VERIFIABLE (&ctx, g_strdup_printf ("Constructor %s for type %s not calling base type ctor.", method_name, type)); + g_free (method_name); + g_free (type); + } + +cleanup: + if (ctx.code) { + for (i = 0; i < ctx.header->code_size; ++i) { + if (ctx.code [i].stack) + g_free (ctx.code [i].stack); + } + } + + for (tmp = ctx.funptrs; tmp; tmp = tmp->next) + g_free (tmp->data); + g_slist_free (ctx.funptrs); + + for (tmp = ctx.exception_types; tmp; tmp = tmp->next) + mono_metadata_free_type ((MonoType *)tmp->data); + g_slist_free (ctx.exception_types); + + for (i = 0; i < ctx.num_locals; ++i) { + if (ctx.locals [i]) + mono_metadata_free_type (ctx.locals [i]); + } + for (i = 0; i < ctx.max_args; ++i) { + if (ctx.params [i]) + mono_metadata_free_type (ctx.params [i]); + } + + if (ctx.eval.stack) + g_free (ctx.eval.stack); + if (ctx.code) + g_free (ctx.code); + g_free (ctx.locals); + g_free (ctx.locals_verification_state); + g_free (ctx.params); + mono_basic_block_free (original_bb); + mono_metadata_free_mh (ctx.header); + + finish_collect_stats (); + return ctx.list; +} + +char* +mono_verify_corlib () +{ + /* This is a public API function so cannot be removed */ + return NULL; +} + +/** + * mono_verifier_is_enabled_for_method: + * \param method the method to probe + * \returns TRUE if \p method needs to be verified. + */ +gboolean +mono_verifier_is_enabled_for_method (MonoMethod *method) +{ + return mono_verifier_is_enabled_for_class (method->klass) && (method->wrapper_type == MONO_WRAPPER_NONE || method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD); +} + +/** + * mono_verifier_is_enabled_for_class: + * \param klass The \c MonoClass to probe + * \returns TRUE if \p klass need to be verified. + */ +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); +} + +gboolean +mono_verifier_is_enabled_for_image (MonoImage *image) +{ + return verify_all || verifier_mode > MONO_VERIFIER_MODE_OFF; +} + +/* + * Dynamic methods are not considered full trust since if the user is trusted and need to + * generate unsafe code, make the method skip verification - this is a known good way to do it. + */ +gboolean +mono_verifier_is_method_full_trust (MonoMethod *method) +{ + return mono_verifier_is_class_full_trust (method->klass) && !method_is_dynamic (method); +} + +/* + * Returns if @klass is under full trust or not. + * + * TODO This code doesn't take CAS into account. + * + * Under verify_all all user code must be verifiable if no security option was set + * + */ +gboolean +mono_verifier_is_class_full_trust (MonoClass *klass) +{ + /* under CoreCLR code is trusted if it is part of the "platform" otherwise all code inside the GAC is trusted */ + gboolean trusted_location = !mono_security_core_clr_enabled () ? + (klass->image->assembly && klass->image->assembly->in_gac) : mono_security_core_clr_is_platform_image (klass->image); + + if (verify_all && verifier_mode == MONO_VERIFIER_MODE_OFF) + return trusted_location || klass->image == mono_defaults.corlib; + return verifier_mode < MONO_VERIFIER_MODE_VERIFIABLE || trusted_location || klass->image == mono_defaults.corlib; +} + +GSList* +mono_method_verify_with_current_settings (MonoMethod *method, gboolean skip_visibility, gboolean is_fulltrust) +{ + return mono_method_verify (method, + (verifier_mode != MONO_VERIFIER_MODE_STRICT ? MONO_VERIFY_NON_STRICT: 0) + | (!is_fulltrust && !mono_verifier_is_method_full_trust (method) ? MONO_VERIFY_FAIL_FAST : 0) + | (skip_visibility ? MONO_VERIFY_SKIP_VISIBILITY : 0)); +} + +static int +get_field_end (MonoClassField *field) +{ + int align; + int size = mono_type_size (field->type, &align); + if (size == 0) + size = 4; /*FIXME Is this a safe bet?*/ + return size + field->offset; +} + +static gboolean +verify_class_for_overlapping_reference_fields (MonoClass *klass) +{ + int i = 0, j; + gpointer iter = NULL; + MonoClassField *field; + gboolean is_fulltrust = mono_verifier_is_class_full_trust (klass); + /*We can't skip types with !has_references since this is calculated after we have run.*/ + if (!mono_class_is_explicit_layout (klass)) + return TRUE; + + + /*We must check for stuff overlapping reference fields. + The outer loop uses mono_class_get_fields to ensure that MonoClass:fields get inited. + */ + while ((field = mono_class_get_fields (klass, &iter))) { + int fieldEnd = get_field_end (field); + gboolean is_valuetype = !MONO_TYPE_IS_REFERENCE (field->type); + ++i; + + if (mono_field_is_deleted (field) || (field->type->attrs & FIELD_ATTRIBUTE_STATIC)) + continue; + + int fcount = mono_class_get_field_count (klass); + for (j = i; j < fcount; ++j) { + MonoClassField *other = &klass->fields [j]; + int otherEnd = get_field_end (other); + if (mono_field_is_deleted (other) || (is_valuetype && !MONO_TYPE_IS_REFERENCE (other->type)) || (other->type->attrs & FIELD_ATTRIBUTE_STATIC)) + continue; + + if (!is_valuetype && MONO_TYPE_IS_REFERENCE (other->type) && field->offset == other->offset && is_fulltrust) + continue; + + if ((otherEnd > field->offset && otherEnd <= fieldEnd) || (other->offset >= field->offset && other->offset < fieldEnd)) + return FALSE; + } + } + return TRUE; +} + +static guint +field_hash (gconstpointer key) +{ + const MonoClassField *field = (const MonoClassField *)key; + return g_str_hash (field->name) ^ mono_metadata_type_hash (field->type); /**/ +} + +static gboolean +field_equals (gconstpointer _a, gconstpointer _b) +{ + const MonoClassField *a = (const MonoClassField *)_a; + const MonoClassField *b = (const MonoClassField *)_b; + return !strcmp (a->name, b->name) && mono_metadata_type_equal (a->type, b->type); +} + + +static gboolean +verify_class_fields (MonoClass *klass) +{ + gpointer iter = NULL; + MonoClassField *field; + MonoGenericContext *context = mono_class_get_context (klass); + GHashTable *unique_fields = g_hash_table_new_full (&field_hash, &field_equals, NULL, NULL); + if (mono_class_is_gtd (klass)) + context = &mono_class_get_generic_container (klass)->context; + + while ((field = mono_class_get_fields (klass, &iter)) != NULL) { + if (!mono_type_is_valid_type_in_context (field->type, context)) { + g_hash_table_destroy (unique_fields); + return FALSE; + } + if (g_hash_table_lookup (unique_fields, field)) { + g_hash_table_destroy (unique_fields); + return FALSE; + } + g_hash_table_insert (unique_fields, field, field); + } + g_hash_table_destroy (unique_fields); + return TRUE; +} + +static gboolean +verify_interfaces (MonoClass *klass) +{ + int i; + for (i = 0; i < klass->interface_count; ++i) { + MonoClass *iface = klass->interfaces [i]; + if (!mono_class_get_flags (iface)) + return FALSE; + } + return TRUE; +} + +static gboolean +verify_valuetype_layout_with_target (MonoClass *klass, MonoClass *target_class) +{ + int type; + gpointer iter = NULL; + MonoClassField *field; + MonoClass *field_class; + + if (!klass->valuetype) + return TRUE; + + type = klass->byval_arg.type; + /*primitive type fields are not properly decoded*/ + if ((type >= MONO_TYPE_BOOLEAN && type <= MONO_TYPE_R8) || (type >= MONO_TYPE_I && type <= MONO_TYPE_U)) + return TRUE; + + while ((field = mono_class_get_fields (klass, &iter)) != NULL) { + if (!field->type) + return FALSE; + + if (field->type->attrs & (FIELD_ATTRIBUTE_STATIC | FIELD_ATTRIBUTE_HAS_FIELD_RVA)) + continue; + + field_class = mono_class_get_generic_type_definition (mono_class_from_mono_type (field->type)); + + if (field_class == target_class || klass == field_class || !verify_valuetype_layout_with_target (field_class, target_class)) + return FALSE; + } + + return TRUE; +} + +static gboolean +verify_valuetype_layout (MonoClass *klass) +{ + gboolean res; + res = verify_valuetype_layout_with_target (klass, klass); + return res; +} + +static gboolean +recursive_mark_constraint_args (MonoBitSet *used_args, MonoGenericContainer *gc, MonoType *type) +{ + int idx; + MonoClass **constraints; + MonoGenericParamInfo *param_info; + + g_assert (mono_type_is_generic_argument (type)); + + idx = mono_type_get_generic_param_num (type); + if (mono_bitset_test_fast (used_args, idx)) + return FALSE; + + mono_bitset_set_fast (used_args, idx); + param_info = mono_generic_container_get_param_info (gc, idx); + + if (!param_info->constraints) + return TRUE; + + for (constraints = param_info->constraints; *constraints; ++constraints) { + MonoClass *ctr = *constraints; + MonoType *constraint_type = &ctr->byval_arg; + + if (mono_type_is_generic_argument (constraint_type) && !recursive_mark_constraint_args (used_args, gc, constraint_type)) + return FALSE; + } + return TRUE; +} + +static gboolean +verify_generic_parameters (MonoClass *klass) +{ + int i; + MonoGenericContainer *gc = mono_class_get_generic_container (klass); + MonoBitSet *used_args = mono_bitset_new (gc->type_argc, 0); + + for (i = 0; i < gc->type_argc; ++i) { + MonoGenericParamInfo *param_info = mono_generic_container_get_param_info (gc, i); + MonoClass **constraints; + + if (!param_info->constraints) + continue; + + mono_bitset_clear_all (used_args); + mono_bitset_set_fast (used_args, i); + + for (constraints = param_info->constraints; *constraints; ++constraints) { + MonoClass *ctr = *constraints; + MonoType *constraint_type = &ctr->byval_arg; + + if (!mono_class_can_access_class (klass, ctr)) + goto fail; + + if (!mono_type_is_valid_type_in_context (constraint_type, &gc->context)) + goto fail; + + if (mono_type_is_generic_argument (constraint_type) && !recursive_mark_constraint_args (used_args, gc, constraint_type)) + goto fail; + if (mono_class_is_ginst (ctr) && !mono_class_is_valid_generic_instantiation (NULL, ctr)) + goto fail; + } + } + mono_bitset_free (used_args); + return TRUE; + +fail: + mono_bitset_free (used_args); + return FALSE; +} + +/* + * Check if the class is verifiable. + * + * Right now there are no conditions that make a class a valid but not verifiable. Both overlapping reference + * field and invalid generic instantiation are fatal errors. + * + * This method must be safe to be called from mono_class_init and all code must be carefull about that. + * + */ +gboolean +mono_verifier_verify_class (MonoClass *klass) +{ + /*Neither , object or ifaces have parent.*/ + if (!klass->parent && + klass != mono_defaults.object_class && + !MONO_CLASS_IS_INTERFACE (klass) && + (!image_is_dynamic (klass->image) && klass->type_token != 0x2000001)) /* is the first type in the assembly*/ + return FALSE; + if (klass->parent) { + if (MONO_CLASS_IS_INTERFACE (klass->parent)) + return FALSE; + if (!mono_class_is_ginst (klass) && mono_class_is_gtd (klass->parent)) + return FALSE; + if (mono_class_is_ginst (klass->parent) && !mono_class_is_ginst (klass)) { + MonoGenericContext *context = mono_class_get_context (klass); + if (mono_class_is_gtd (klass)) + context = &mono_class_get_generic_container (klass)->context; + if (!mono_type_is_valid_type_in_context (&klass->parent->byval_arg, context)) + return FALSE; + } + } + if (mono_class_is_gtd (klass) && (mono_class_is_explicit_layout (klass))) + return FALSE; + if (mono_class_is_gtd (klass) && !verify_generic_parameters (klass)) + return FALSE; + if (!verify_class_for_overlapping_reference_fields (klass)) + return FALSE; + if (mono_class_is_ginst (klass) && !mono_class_is_valid_generic_instantiation (NULL, klass)) + return FALSE; + if (!mono_class_is_ginst (klass) && !verify_class_fields (klass)) + return FALSE; + if (klass->valuetype && !verify_valuetype_layout (klass)) + return FALSE; + if (!verify_interfaces (klass)) + return FALSE; + return TRUE; +} + +gboolean +mono_verifier_class_is_valid_generic_instantiation (MonoClass *klass) +{ + return mono_class_is_valid_generic_instantiation (NULL, klass); +} + +gboolean +mono_verifier_is_method_valid_generic_instantiation (MonoMethod *method) +{ + if (!method->is_inflated) + return TRUE; + return mono_method_is_valid_generic_instantiation (NULL, method); +} + +#else + +gboolean +mono_verifier_verify_class (MonoClass *klass) +{ + /* The verifier was disabled at compile time */ + return TRUE; +} + +GSList* +mono_method_verify_with_current_settings (MonoMethod *method, gboolean skip_visibility, gboolean is_fulltrust) +{ + /* The verifier was disabled at compile time */ + return NULL; +} + +gboolean +mono_verifier_is_class_full_trust (MonoClass *klass) +{ + /* The verifier was disabled at compile time */ + return TRUE; +} + +gboolean +mono_verifier_is_method_full_trust (MonoMethod *method) +{ + /* The verifier was disabled at compile time */ + return TRUE; +} + +gboolean +mono_verifier_is_enabled_for_image (MonoImage *image) +{ + /* The verifier was disabled at compile time */ + return FALSE; +} + +gboolean +mono_verifier_is_enabled_for_class (MonoClass *klass) +{ + /* The verifier was disabled at compile time */ + return FALSE; +} + +gboolean +mono_verifier_is_enabled_for_method (MonoMethod *method) +{ + /* The verifier was disabled at compile time */ + return FALSE; +} + +GSList* +mono_method_verify (MonoMethod *method, int level) +{ + /* The verifier was disabled at compile time */ + return NULL; +} + +void +mono_free_verify_list (GSList *list) +{ + /* The verifier was disabled at compile time */ + /* will always be null if verifier is disabled */ +} + +gboolean +mono_verifier_class_is_valid_generic_instantiation (MonoClass *klass) +{ + return TRUE; +} + +gboolean +mono_verifier_is_method_valid_generic_instantiation (MonoMethod *method) +{ + return TRUE; +} + + + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/verify.h b/unity-2019.4.24f1-mbe/mono/metadata/verify.h new file mode 100644 index 000000000..162ef4434 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/verify.h @@ -0,0 +1,66 @@ +/** + * \file + */ + +#ifndef __MONO_METADATA_VERIFY_H__ +#define __MONO_METADATA_VERIFY_H__ + +#include +#include +#include +#include /* GSList dep */ + +MONO_BEGIN_DECLS + +typedef enum { + MONO_VERIFY_OK, + MONO_VERIFY_ERROR, + MONO_VERIFY_WARNING, + MONO_VERIFY_CLS = 4, + MONO_VERIFY_ALL = 7, + + /* Status signaling code that is not verifiable.*/ + MONO_VERIFY_NOT_VERIFIABLE = 8, + + /*OR it with other flags*/ + + /* Abort the verification if the code is not verifiable. + * The standard behavior is to abort if the code is not valid. + * */ + MONO_VERIFY_FAIL_FAST = 16, + + + /* Perform less verification of the code. This flag should be used + * if one wants the verifier to be more compatible to the MS runtime. + * Mind that this is not to be more compatible with MS peverify, but + * with the runtime itself, that has a less strict verifier. + */ + MONO_VERIFY_NON_STRICT = 32, + + /*Skip all visibility related checks*/ + MONO_VERIFY_SKIP_VISIBILITY = 64, + + /*Skip all visibility related checks*/ + MONO_VERIFY_REPORT_ALL_ERRORS = 128 + +} MonoVerifyStatus; + +typedef struct { + char *message; + MonoVerifyStatus status; +} MonoVerifyInfo; + +typedef struct { + MonoVerifyInfo info; + int8_t exception_type; /*should be one of MONO_EXCEPTION_* */ +} MonoVerifyInfoExtended; + + +MONO_API GSList* mono_method_verify (MonoMethod *method, int level); +MONO_API void mono_free_verify_list (GSList *list); +MONO_API char* mono_verify_corlib (void); + +MONO_END_DECLS + +#endif /* __MONO_METADATA_VERIFY_H__ */ + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32error-unity.c b/unity-2019.4.24f1-mbe/mono/metadata/w32error-unity.c new file mode 100644 index 000000000..5617f52f5 --- /dev/null +++ b/unity-2019.4.24f1-mbe/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/unity-2019.4.24f1-mbe/mono/metadata/w32error-unix.c b/unity-2019.4.24f1-mbe/mono/metadata/w32error-unix.c new file mode 100644 index 000000000..d6cc76bc5 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32error-unix.c @@ -0,0 +1,86 @@ +/** + * \file + */ + +#include "w32error.h" + +#include "utils/mono-lazy-init.h" + +static mono_lazy_init_t error_key_once = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED; + +static pthread_key_t error_key; + +static void +error_key_init (void) +{ + gint ret; + ret = pthread_key_create (&error_key, NULL); + g_assert (ret == 0); +} + +guint32 +mono_w32error_get_last (void) +{ + mono_lazy_initialize (&error_key_once, error_key_init); + return GPOINTER_TO_UINT (pthread_getspecific (error_key)); +} + +void +mono_w32error_set_last (guint32 error) +{ + gint ret; + mono_lazy_initialize (&error_key_once, error_key_init); + ret = pthread_setspecific (error_key, GUINT_TO_POINTER (error)); + g_assert (ret == 0); +} + +guint32 +mono_w32error_unix_to_win32 (guint32 error) +{ + /* mapping ideas borrowed from wine. they may need some work */ + + switch (error) { + case EACCES: + case EPERM: + case EROFS: return ERROR_ACCESS_DENIED; + case EAGAIN: return ERROR_SHARING_VIOLATION; + case EBUSY: return ERROR_LOCK_VIOLATION; + case EEXIST: return ERROR_FILE_EXISTS; + case EINVAL: + case ESPIPE: return ERROR_SEEK; + case EISDIR: return ERROR_CANNOT_MAKE; + case ENFILE: + case EMFILE: return ERROR_TOO_MANY_OPEN_FILES; + case ENOENT: + case ENOTDIR: return ERROR_FILE_NOT_FOUND; + case ENOSPC: return ERROR_HANDLE_DISK_FULL; + case ENOTEMPTY: return ERROR_DIR_NOT_EMPTY; + case ENOEXEC: return ERROR_BAD_FORMAT; + case ENAMETOOLONG: return ERROR_FILENAME_EXCED_RANGE; +#ifdef EINPROGRESS + case EINPROGRESS: return ERROR_IO_PENDING; +#endif + case ENOSYS: return ERROR_NOT_SUPPORTED; + case EBADF: return ERROR_INVALID_HANDLE; + case EIO: return ERROR_INVALID_HANDLE; + case EINTR: return ERROR_IO_PENDING; /* best match I could find */ + case EPIPE: return ERROR_WRITE_FAULT; + case ELOOP: return ERROR_CANT_RESOLVE_FILENAME; +#ifdef ENODEV + case ENODEV: return ERROR_DEV_NOT_EXIST; +#endif +#ifdef ENXIO + case ENXIO: return ERROR_DEV_NOT_EXIST; +#endif +#ifdef ENOTCONN + case ENOTCONN: return ERROR_DEV_NOT_EXIST; +#endif +#ifdef EHOSTDOWN + case EHOSTDOWN: return ERROR_DEV_NOT_EXIST; +#endif + + default: + g_warning ("%s: unknown error (%d) \"%s\"", __FILE__, error, g_strerror (error)); + return ERROR_NOT_SUPPORTED; + } +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32error-win32.c b/unity-2019.4.24f1-mbe/mono/metadata/w32error-win32.c new file mode 100644 index 000000000..a5253c15c --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32error-win32.c @@ -0,0 +1,25 @@ +/** + * \file + */ + +#include + +#include "w32error.h" + +guint32 +mono_w32error_get_last (void) +{ + return GetLastError (); +} + +void +mono_w32error_set_last (guint32 error) +{ + SetLastError (error); +} + +guint32 +mono_w32error_unix_to_win32 (guint32 error) +{ + g_assert_not_reached (); +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32error.h b/unity-2019.4.24f1-mbe/mono/metadata/w32error.h new file mode 100644 index 000000000..e66f920b4 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32error.h @@ -0,0 +1,93 @@ +/** + * \file + */ + +#ifndef _MONO_METADATA_W32ERROR_H_ +#define _MONO_METADATA_W32ERROR_H_ + +#include +#include + +#if !defined(HOST_WIN32) + +#define ERROR_SUCCESS 0 +#define ERROR_FILE_NOT_FOUND 2 +#define ERROR_PATH_NOT_FOUND 3 +#define ERROR_TOO_MANY_OPEN_FILES 4 +#define ERROR_ACCESS_DENIED 5 +#define ERROR_INVALID_HANDLE 6 +#define ERROR_NOT_ENOUGH_MEMORY 8 +#define ERROR_BAD_FORMAT 11 +#define ERROR_INVALID_ACCESS 12 +#define ERROR_INVALID_DATA 13 +#define ERROR_OUTOFMEMORY 14 +#define ERROR_NOT_SAME_DEVICE 17 +#define ERROR_NO_MORE_FILES 18 +#define ERROR_BAD_LENGTH 24 +#define ERROR_SEEK 25 +#define ERROR_WRITE_FAULT 29 +#define ERROR_GEN_FAILURE 31 +#define ERROR_SHARING_VIOLATION 32 +#define ERROR_LOCK_VIOLATION 33 +#define ERROR_HANDLE_DISK_FULL 39 +#define ERROR_NOT_SUPPORTED 50 +#define ERROR_DEV_NOT_EXIST 55 +#define ERROR_FILE_EXISTS 80 +#define ERROR_CANNOT_MAKE 82 +#define ERROR_INVALID_PARAMETER 87 +#define ERROR_INVALID_NAME 123 +#define ERROR_PROC_NOT_FOUND 127 +#define ERROR_DIR_NOT_EMPTY 145 +#define ERROR_ALREADY_EXISTS 183 +#define ERROR_BAD_EXE_FORMAT 193 +#define ERROR_FILENAME_EXCED_RANGE 206 +#define ERROR_DIRECTORY 267 +#define ERROR_IO_PENDING 997 +#define ERROR_CANT_RESOLVE_FILENAME 1921 +#define ERROR_ENCRYPTION_FAILED 6000 +#define WSAEINTR 10004 +#define WSAEBADF 10009 +#define WSAEACCES 10013 +#define WSAEFAULT 10014 +#define WSAEINVAL 10022 +#define WSAEMFILE 10024 +#define WSAEWOULDBLOCK 10035 +#define WSAEINPROGRESS 10036 +#define WSAEALREADY 10037 +#define WSAENOTSOCK 10038 +#define WSAEDESTADDRREQ 10039 +#define WSAEMSGSIZE 10040 +#define WSAEPROTOTYPE 10041 +#define WSAENOPROTOOPT 10042 +#define WSAEPROTONOSUPPORT 10043 +#define WSAESOCKTNOSUPPORT 10044 +#define WSAEOPNOTSUPP 10045 +#define WSAEAFNOSUPPORT 10047 +#define WSAEADDRINUSE 10048 +#define WSAEADDRNOTAVAIL 10049 +#define WSAENETDOWN 10050 +#define WSAENETUNREACH 10051 +#define WSAECONNRESET 10054 +#define WSAENOBUFS 10055 +#define WSAEISCONN 10056 +#define WSAENOTCONN 10057 +#define WSAESHUTDOWN 10058 +#define WSAETIMEDOUT 10060 +#define WSAECONNREFUSED 10061 +#define WSAEHOSTDOWN 10064 +#define WSAEHOSTUNREACH 10065 +#define WSASYSCALLFAILURE 10107 +#define WSAENXIO 100001 + +#endif + +guint32 +mono_w32error_get_last (void); + +void +mono_w32error_set_last (guint32 error); + +guint32 +mono_w32error_unix_to_win32 (guint32 error); + +#endif /* _MONO_METADATA_W32ERROR_H_ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32event-unity.c b/unity-2019.4.24f1-mbe/mono/metadata/w32event-unity.c new file mode 100644 index 000000000..e1a570ba0 --- /dev/null +++ b/unity-2019.4.24f1-mbe/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/unity-2019.4.24f1-mbe/mono/metadata/w32event-unix.c b/unity-2019.4.24f1-mbe/mono/metadata/w32event-unix.c new file mode 100644 index 000000000..93c595abf --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32event-unix.c @@ -0,0 +1,426 @@ +/** + * \file + * Runtime support for managed Event on Unix + * + * Author: + * Ludovic Henry (luhenry@microsoft.com) + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include "w32event.h" + +#include "w32error.h" +#include "w32handle-namespace.h" +#include "mono/utils/mono-error-internals.h" +#include "mono/utils/mono-logger-internals.h" +#include "mono/metadata/handle.h" +#include "mono/metadata/object-internals.h" +#include "mono/metadata/w32handle.h" + +#define MAX_PATH 260 + +static gpointer +mono_w32event_create_full (MonoBoolean manual, MonoBoolean initial, const gchar *name, gint32 *err); + +static gpointer +mono_w32event_open (const gchar *utf8_name, gint32 rights G_GNUC_UNUSED, gint32 *error); + +typedef struct { + gboolean manual; + guint32 set_count; +} MonoW32HandleEvent; + +struct MonoW32HandleNamedEvent { + MonoW32HandleEvent e; + MonoW32HandleNamespace sharedns; +}; + +static void event_handle_signal (MonoW32Handle *handle_data) +{ + MonoW32HandleEvent *event_handle; + + event_handle = (MonoW32HandleEvent*) handle_data->specific; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_EVENT, "%s: signalling %s handle %p", + __func__, mono_w32handle_get_typename (handle_data->type), handle_data); + + if (!event_handle->manual) { + event_handle->set_count = 1; + mono_w32handle_set_signal_state (handle_data, TRUE, FALSE); + } else { + mono_w32handle_set_signal_state (handle_data, TRUE, TRUE); + } +} + +static gboolean event_handle_own (MonoW32Handle *handle_data, gboolean *abandoned) +{ + MonoW32HandleEvent *event_handle; + + *abandoned = FALSE; + + event_handle = (MonoW32HandleEvent*) handle_data->specific; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_EVENT, "%s: owning %s handle %p", + __func__, mono_w32handle_get_typename (handle_data->type), handle_data); + + if (!event_handle->manual) { + g_assert (event_handle->set_count > 0); + event_handle->set_count --; + + if (event_handle->set_count == 0) + mono_w32handle_set_signal_state (handle_data, FALSE, FALSE); + } + + return TRUE; +} + +static void event_details (MonoW32Handle *handle_data) +{ + MonoW32HandleEvent *event = (MonoW32HandleEvent *)handle_data->specific; + g_print ("manual: %s, set_count: %d", + event->manual ? "TRUE" : "FALSE", event->set_count); +} + +static void namedevent_details (MonoW32Handle *handle_data) +{ + MonoW32HandleNamedEvent *namedevent = (MonoW32HandleNamedEvent *)handle_data->specific; + g_print ("manual: %s, set_count: %d, name: \"%s\"", + namedevent->e.manual ? "TRUE" : "FALSE", namedevent->e.set_count, namedevent->sharedns.name); +} + +static const gchar* event_typename (void) +{ + return "Event"; +} + +static gsize event_typesize (void) +{ + return sizeof (MonoW32HandleEvent); +} + +static const gchar* namedevent_typename (void) +{ + return "N.Event"; +} + +static gsize namedevent_typesize (void) +{ + return sizeof (MonoW32HandleNamedEvent); +} + +void +mono_w32event_init (void) +{ + static MonoW32HandleOps event_ops = { + NULL, /* close */ + event_handle_signal, /* signal */ + event_handle_own, /* own */ + NULL, /* is_owned */ + NULL, /* special_wait */ + NULL, /* prewait */ + event_details, /* details */ + event_typename, /* typename */ + event_typesize, /* typesize */ + }; + + static MonoW32HandleOps namedevent_ops = { + NULL, /* close */ + event_handle_signal, /* signal */ + event_handle_own, /* own */ + NULL, /* is_owned */ + NULL, /* special_wait */ + NULL, /* prewait */ + namedevent_details, /* details */ + namedevent_typename, /* typename */ + namedevent_typesize, /* typesize */ + }; + + mono_w32handle_register_ops (MONO_W32TYPE_EVENT, &event_ops); + mono_w32handle_register_ops (MONO_W32TYPE_NAMEDEVENT, &namedevent_ops); + + mono_w32handle_register_capabilities (MONO_W32TYPE_EVENT, + (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL)); + mono_w32handle_register_capabilities (MONO_W32TYPE_NAMEDEVENT, + (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL)); +} + +gpointer +mono_w32event_create (gboolean manual, gboolean initial) +{ + gpointer handle; + gint32 error; + + handle = mono_w32event_create_full (manual, initial, NULL, &error); + if (error != ERROR_SUCCESS) + g_assert (!handle); + + return handle; +} + +gboolean +mono_w32event_close (gpointer handle) +{ + return mono_w32handle_close (handle); +} + +void +mono_w32event_set (gpointer handle) +{ + ves_icall_System_Threading_Events_SetEvent_internal (handle); +} + +void +mono_w32event_reset (gpointer handle) +{ + ves_icall_System_Threading_Events_ResetEvent_internal (handle); +} + +static gpointer event_handle_create (MonoW32HandleEvent *event_handle, MonoW32Type type, gboolean manual, gboolean initial) +{ + MonoW32Handle *handle_data; + gpointer handle; + + event_handle->manual = manual; + event_handle->set_count = (initial && !manual) ? 1 : 0; + + handle = mono_w32handle_new (type, event_handle); + if (handle == INVALID_HANDLE_VALUE) { + g_warning ("%s: error creating %s handle", + __func__, mono_w32handle_get_typename (type)); + mono_w32error_set_last (ERROR_GEN_FAILURE); + return NULL; + } + + if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) + g_error ("%s: unkown handle %p", __func__, handle); + + if (handle_data->type != type) + g_error ("%s: unknown event handle %p", __func__, handle); + + mono_w32handle_lock (handle_data); + + if (initial) + mono_w32handle_set_signal_state (handle_data, TRUE, FALSE); + + mono_w32handle_unlock (handle_data); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_EVENT, "%s: created %s handle %p", + __func__, mono_w32handle_get_typename (type), handle); + + mono_w32handle_unref (handle_data); + + return handle; +} + +static gpointer event_create (gboolean manual, gboolean initial) +{ + MonoW32HandleEvent event_handle; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_EVENT, "%s: creating %s handle", + __func__, mono_w32handle_get_typename (MONO_W32TYPE_EVENT)); + return event_handle_create (&event_handle, MONO_W32TYPE_EVENT, manual, initial); +} + +static gpointer namedevent_create (gboolean manual, gboolean initial, const gchar *utf8_name G_GNUC_UNUSED) +{ + gpointer handle; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_EVENT, "%s: creating %s handle", + __func__, mono_w32handle_get_typename (MONO_W32TYPE_NAMEDEVENT)); + + /* w32 seems to guarantee that opening named objects can't race each other */ + mono_w32handle_namespace_lock (); + + glong utf8_len = strlen (utf8_name); + + handle = mono_w32handle_namespace_search_handle (MONO_W32TYPE_NAMEDEVENT, utf8_name); + if (handle == INVALID_HANDLE_VALUE) { + /* The name has already been used for a different object. */ + handle = NULL; + mono_w32error_set_last (ERROR_INVALID_HANDLE); + } else if (handle) { + /* Not an error, but this is how the caller is informed that the event wasn't freshly created */ + mono_w32error_set_last (ERROR_ALREADY_EXISTS); + + /* mono_w32handle_namespace_search_handle already adds a ref to the handle */ + } else { + /* A new named event */ + MonoW32HandleNamedEvent namedevent_handle; + + size_t len = utf8_len < MAX_PATH ? utf8_len : MAX_PATH; + memcpy (&namedevent_handle.sharedns.name [0], utf8_name, len); + namedevent_handle.sharedns.name [len] = '\0'; + + handle = event_handle_create ((MonoW32HandleEvent*) &namedevent_handle, MONO_W32TYPE_NAMEDEVENT, manual, initial); + } + + mono_w32handle_namespace_unlock (); + + return handle; +} + +gpointer +mono_w32event_create_full (MonoBoolean manual, MonoBoolean initial, const gchar *name, gint32 *error) +{ + gpointer event; + + /* Need to blow away any old errors here, because code tests + * for ERROR_ALREADY_EXISTS on success (!) to see if an event + * was freshly created */ + mono_w32error_set_last (ERROR_SUCCESS); + + event = name ? namedevent_create (manual, initial, name) : event_create (manual, initial); + + *error = mono_w32error_get_last (); + + return event; +} + +gpointer +ves_icall_System_Threading_Events_CreateEvent_internal (MonoBoolean manual, MonoBoolean initial, MonoStringHandle name, gint32 *err, MonoError *error) +{ + error_init (error); + gchar *utf8_name = mono_string_handle_to_utf8 (name, error); + return_val_if_nok (error, NULL); + gpointer result = mono_w32event_create_full (manual, initial, utf8_name, err); + g_free (utf8_name); + return result; +} + +gboolean +ves_icall_System_Threading_Events_SetEvent_internal (gpointer handle) +{ + MonoW32Handle *handle_data; + MonoW32HandleEvent *event_handle; + + if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) { + g_warning ("%s: unkown handle %p", __func__, handle); + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return FALSE; + } + + if (handle_data->type != MONO_W32TYPE_EVENT && handle_data->type != MONO_W32TYPE_NAMEDEVENT) { + g_warning ("%s: unkown event handle %p", __func__, handle); + mono_w32error_set_last (ERROR_INVALID_HANDLE); + mono_w32handle_unref (handle_data); + return FALSE; + } + + event_handle = (MonoW32HandleEvent*) handle_data->specific; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_EVENT, "%s: setting %s handle %p", + __func__, mono_w32handle_get_typename (handle_data->type), handle); + + mono_w32handle_lock (handle_data); + + if (!event_handle->manual) { + event_handle->set_count = 1; + mono_w32handle_set_signal_state (handle_data, TRUE, FALSE); + } else { + mono_w32handle_set_signal_state (handle_data, TRUE, TRUE); + } + + mono_w32handle_unlock (handle_data); + + mono_w32handle_unref (handle_data); + return TRUE; +} + +gboolean +ves_icall_System_Threading_Events_ResetEvent_internal (gpointer handle) +{ + MonoW32Handle *handle_data; + MonoW32HandleEvent *event_handle; + + mono_w32error_set_last (ERROR_SUCCESS); + + if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) { + g_warning ("%s: unkown handle %p", __func__, handle); + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return FALSE; + } + + if (handle_data->type != MONO_W32TYPE_EVENT && handle_data->type != MONO_W32TYPE_NAMEDEVENT) { + g_warning ("%s: unkown event handle %p", __func__, handle); + mono_w32error_set_last (ERROR_INVALID_HANDLE); + mono_w32handle_unref (handle_data); + return FALSE; + } + + event_handle = (MonoW32HandleEvent*) handle_data->specific; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_EVENT, "%s: resetting %s handle %p", + __func__, mono_w32handle_get_typename (handle_data->type), handle); + + mono_w32handle_lock (handle_data); + + if (!mono_w32handle_issignalled (handle_data)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_EVENT, "%s: no need to reset %s handle %p", + __func__, mono_w32handle_get_typename (handle_data->type), handle); + } else { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_EVENT, "%s: obtained write lock on %s handle %p", + __func__, mono_w32handle_get_typename (handle_data->type), handle); + + mono_w32handle_set_signal_state (handle_data, FALSE, FALSE); + } + + event_handle->set_count = 0; + + mono_w32handle_unlock (handle_data); + + mono_w32handle_unref (handle_data); + return TRUE; +} + +void +ves_icall_System_Threading_Events_CloseEvent_internal (gpointer handle) +{ + mono_w32handle_close (handle); +} + +gpointer +ves_icall_System_Threading_Events_OpenEvent_internal (MonoStringHandle name, gint32 rights G_GNUC_UNUSED, gint32 *err, MonoError *error) +{ + error_init (error); + gchar *utf8_name = mono_string_handle_to_utf8 (name, error); + return_val_if_nok (error, NULL); + gpointer handle = mono_w32event_open (utf8_name, rights, err); + g_free (utf8_name); + return handle; +} + +gpointer +mono_w32event_open (const gchar *utf8_name, gint32 rights G_GNUC_UNUSED, gint32 *error) +{ + gpointer handle; + *error = ERROR_SUCCESS; + + /* w32 seems to guarantee that opening named objects can't race each other */ + mono_w32handle_namespace_lock (); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_EVENT, "%s: Opening named event [%s]", __func__, utf8_name); + + handle = mono_w32handle_namespace_search_handle (MONO_W32TYPE_NAMEDEVENT, utf8_name); + if (handle == INVALID_HANDLE_VALUE) { + /* The name has already been used for a different object. */ + *error = ERROR_INVALID_HANDLE; + goto cleanup; + } else if (!handle) { + /* This name doesn't exist */ + *error = ERROR_FILE_NOT_FOUND; + goto cleanup; + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_EVENT, "%s: returning named event handle %p", __func__, handle); + +cleanup: + mono_w32handle_namespace_unlock (); + + return handle; +} + +MonoW32HandleNamespace* +mono_w32event_get_namespace (MonoW32HandleNamedEvent *event) +{ + return &event->sharedns; +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32event-win32.c b/unity-2019.4.24f1-mbe/mono/metadata/w32event-win32.c new file mode 100644 index 000000000..9894053b6 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32event-win32.c @@ -0,0 +1,111 @@ +/** + * \file + * Runtime support for managed Event on Win32 + * + * Author: + * Ludovic Henry (luhenry@microsoft.com) + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include "w32event.h" + +#include +#include +#include +#include + +void +mono_w32event_init (void) +{ +} + +gpointer +mono_w32event_create (gboolean manual, gboolean initial) +{ + return CreateEvent (NULL, manual, initial, NULL); +} + +gboolean +mono_w32event_close (gpointer handle) +{ + return CloseHandle (handle); +} + +void +mono_w32event_set (gpointer handle) +{ + SetEvent (handle); +} + +void +mono_w32event_reset (gpointer handle) +{ + ResetEvent (handle); +} + +gpointer +ves_icall_System_Threading_Events_CreateEvent_internal (MonoBoolean manual, MonoBoolean initial, MonoStringHandle name, gint32 *err, MonoError *error) +{ + gpointer event; + + error_init (error); + + uint32_t gchandle = 0; + gunichar2 *uniname = NULL; + if (!MONO_HANDLE_IS_NULL (name)) + uniname = mono_string_handle_pin_chars (name, &gchandle); + MONO_ENTER_GC_SAFE; + event = CreateEvent (NULL, manual, initial, uniname); + *err = GetLastError (); + MONO_EXIT_GC_SAFE; + if (gchandle) + mono_gchandle_free (gchandle); + + return event; +} + +gboolean +ves_icall_System_Threading_Events_SetEvent_internal (gpointer handle) +{ + return SetEvent (handle); +} + +gboolean +ves_icall_System_Threading_Events_ResetEvent_internal (gpointer handle) +{ + return ResetEvent (handle); +} + +void +ves_icall_System_Threading_Events_CloseEvent_internal (gpointer handle) +{ + CloseHandle (handle); +} + +gpointer +ves_icall_System_Threading_Events_OpenEvent_internal (MonoStringHandle name, gint32 rights, gint32 *err, MonoError *error) +{ + gpointer handle; + + error_init (error); + + *err = ERROR_SUCCESS; + + uint32_t gchandle = 0; + gunichar2 *uniname = NULL; + + if (!MONO_HANDLE_IS_NULL (name)) + uniname = mono_string_handle_pin_chars (name, &gchandle); + + MONO_ENTER_GC_SAFE; + handle = OpenEvent (rights, FALSE, uniname); + if (!handle) + *err = GetLastError (); + MONO_EXIT_GC_SAFE; + + if (gchandle) + mono_gchandle_free (gchandle); + + return handle; +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32event.h b/unity-2019.4.24f1-mbe/mono/metadata/w32event.h new file mode 100644 index 000000000..779407de8 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32event.h @@ -0,0 +1,50 @@ +/** + * \file + */ + +#ifndef _MONO_METADATA_W32EVENT_H_ +#define _MONO_METADATA_W32EVENT_H_ + +#include +#include + +#include "object.h" +#include "object-internals.h" +#include "w32handle-namespace.h" + +void +mono_w32event_init (void); + +gpointer +mono_w32event_create (gboolean manual, gboolean initial); + +gboolean +mono_w32event_close (gpointer handle); + +void +mono_w32event_set (gpointer handle); + +void +mono_w32event_reset (gpointer handle); + +gpointer +ves_icall_System_Threading_Events_CreateEvent_internal (MonoBoolean manual, MonoBoolean initial, MonoStringHandle name, gint32 *err, MonoError *error); + +gboolean +ves_icall_System_Threading_Events_SetEvent_internal (gpointer handle); + +gboolean +ves_icall_System_Threading_Events_ResetEvent_internal (gpointer handle); + +void +ves_icall_System_Threading_Events_CloseEvent_internal (gpointer handle); + +gpointer +ves_icall_System_Threading_Events_OpenEvent_internal (MonoStringHandle name, gint32 rights, gint32 *err, MonoError *error); + +typedef struct MonoW32HandleNamedEvent MonoW32HandleNamedEvent; + +MonoW32HandleNamespace* +mono_w32event_get_namespace (MonoW32HandleNamedEvent *event); + +#endif /* _MONO_METADATA_W32EVENT_H_ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32file-internals.h b/unity-2019.4.24f1-mbe/mono/metadata/w32file-internals.h new file mode 100644 index 000000000..0f7876c9d --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32file-internals.h @@ -0,0 +1,12 @@ +/** + * \file + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#ifndef _MONO_METADATA_W32FILE_INTERNALS_H_ +#define _MONO_METADATA_W32FILE_INTERNALS_H_ + +#include +#include + +#endif /* _MONO_METADATA_W32FILE_INTERNALS_H_ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32file-unity.c b/unity-2019.4.24f1-mbe/mono/metadata/w32file-unity.c new file mode 100644 index 000000000..786c41111 --- /dev/null +++ b/unity-2019.4.24f1-mbe/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/unity-2019.4.24f1-mbe/mono/metadata/w32file-unix-glob.c b/unity-2019.4.24f1-mbe/mono/metadata/w32file-unix-glob.c new file mode 100644 index 000000000..62d44889b --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32file-unix-glob.c @@ -0,0 +1,407 @@ +/* $OpenBSD: glob.c,v 1.26 2005/11/28 17:50:12 deraadt Exp $ */ +/** + * \file + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Guido van Rossum. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * mono_w32file_unix_glob(3) -- a subset of the one defined in POSIX 1003.2. + * + * Optional extra services, controlled by flags not defined by POSIX: + * + * GLOB_MAGCHAR: + * Set in gl_flags if pattern contained a globbing character. + */ +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "w32file-unix-glob.h" + +#define EOS '\0' +#define NOT '!' +#define QUESTION '?' +#define QUOTE '\\' +#define STAR '*' + +#ifndef DEBUG + +#define M_QUOTE 0x8000 +#define M_PROTECT 0x4000 +#define M_MASK 0xffff +#define M_ASCII 0x00ff + +typedef unsigned short Char; + +#else + +#define M_QUOTE 0x80 +#define M_PROTECT 0x40 +#define M_MASK 0xff +#define M_ASCII 0x7f + +typedef char Char; + +#endif + + +#define CHAR(c) ((gchar)((c)&M_ASCII)) +#define META(c) ((gchar)((c)|M_QUOTE)) +#define M_ALL META('*') +#define M_ONE META('?') +#define ismeta(c) (((c)&M_QUOTE) != 0) + + +static int +g_Ctoc(const gchar *, char *, unsigned int); + +static int +glob0(GDir *dir, const gchar *, mono_w32file_unix_glob_t *, gboolean, gboolean); +static int +glob1(GDir *dir, gchar *, gchar *, mono_w32file_unix_glob_t *, size_t *, gboolean, gboolean); + +static int +glob3(GDir *dir, gchar *, gchar *, mono_w32file_unix_glob_t *, size_t *, gboolean, gboolean); + +static int +globextend(const gchar *, mono_w32file_unix_glob_t *, size_t *); + +static int +match(const gchar *, gchar *, gchar *, gboolean); + +#ifdef DEBUG_ENABLED +static void qprintf(const char *, Char *); +#endif + +int +mono_w32file_unix_glob(GDir *dir, const char *pattern, int flags, mono_w32file_unix_glob_t *pglob) +{ + const unsigned char *patnext; + int c; + gchar *bufnext, *bufend, patbuf[PATH_MAX]; + + patnext = (unsigned char *) pattern; + if (!(flags & W32FILE_UNIX_GLOB_APPEND)) { + pglob->gl_pathc = 0; + pglob->gl_pathv = NULL; + pglob->gl_offs = 0; + } + pglob->gl_flags = flags & ~W32FILE_UNIX_GLOB_MAGCHAR; + + bufnext = patbuf; + bufend = bufnext + PATH_MAX - 1; + + /* Protect the quoted characters. */ + while (bufnext < bufend && (c = *patnext++) != EOS) + if (c == QUOTE) { + if ((c = *patnext++) == EOS) { + c = QUOTE; + --patnext; + } + *bufnext++ = c | M_PROTECT; + } else + *bufnext++ = c; + + *bufnext = EOS; + + return glob0(dir, patbuf, pglob, flags & W32FILE_UNIX_GLOB_IGNORECASE, + flags & W32FILE_UNIX_GLOB_UNIQUE); +} + +/* + * The main glob() routine: compiles the pattern (optionally processing + * quotes), calls glob1() to do the real pattern matching, and finally + * sorts the list (unless unsorted operation is requested). Returns 0 + * if things went well, nonzero if errors occurred. It is not an error + * to find no matches. + */ +static int +glob0(GDir *dir, const gchar *pattern, mono_w32file_unix_glob_t *pglob, gboolean ignorecase, + gboolean unique) +{ + const gchar *qpatnext; + int c, err, oldpathc; + gchar *bufnext, patbuf[PATH_MAX]; + size_t limit = 0; + + qpatnext = pattern; + oldpathc = pglob->gl_pathc; + bufnext = patbuf; + + /* We don't need to check for buffer overflow any more. */ + while ((c = *qpatnext++) != EOS) { + switch (c) { + case QUESTION: + pglob->gl_flags |= W32FILE_UNIX_GLOB_MAGCHAR; + *bufnext++ = M_ONE; + break; + case STAR: + pglob->gl_flags |= W32FILE_UNIX_GLOB_MAGCHAR; + /* collapse adjacent stars to one, + * to avoid exponential behavior + */ + if (bufnext == patbuf || bufnext[-1] != M_ALL) + *bufnext++ = M_ALL; + break; + default: + *bufnext++ = CHAR(c); + break; + } + } + *bufnext = EOS; +#ifdef DEBUG_ENABLED + qprintf("glob0:", patbuf); +#endif + + if ((err = glob1(dir, patbuf, patbuf+PATH_MAX-1, pglob, &limit, + ignorecase, unique)) != 0) + return(err); + + if (pglob->gl_pathc == oldpathc) { + return(W32FILE_UNIX_GLOB_NOMATCH); + } + + return(0); +} + +static int +glob1(GDir *dir, gchar *pattern, gchar *pattern_last, mono_w32file_unix_glob_t *pglob, + size_t *limitp, gboolean ignorecase, gboolean unique) +{ + /* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */ + if (*pattern == EOS) + return(0); + return(glob3(dir, pattern, pattern_last, pglob, limitp, ignorecase, + unique)); +} + +static gboolean contains (mono_w32file_unix_glob_t *pglob, const gchar *name) +{ + int i; + char **pp; + + if (pglob->gl_pathv != NULL) { + pp = pglob->gl_pathv + pglob->gl_offs; + for (i = pglob->gl_pathc; i--; ++pp) { + if (*pp) { + if (!strcmp (*pp, name)) { + return(TRUE); + } + } + } + } + + return(FALSE); +} + +static int +glob3(GDir *dir, gchar *pattern, gchar *pattern_last, mono_w32file_unix_glob_t *pglob, + size_t *limitp, gboolean ignorecase, gboolean unique) +{ + const gchar *name; + + /* Search directory for matching names. */ + while ((name = g_dir_read_name(dir))) { + if (!match(name, pattern, pattern + strlen (pattern), + ignorecase)) { + continue; + } + if (!unique || + !contains (pglob, name)) { + globextend (name, pglob, limitp); + } + } + + return(0); +} + + +/* + * Extend the gl_pathv member of a mono_w32file_unix_glob_t structure to accommodate a new item, + * add the new item, and update gl_pathc. + * + * This assumes the BSD realloc, which only copies the block when its size + * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic + * behavior. + * + * Return 0 if new item added, error code if memory couldn't be allocated. + * + * Invariant of the mono_w32file_unix_glob_t structure: + * Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and + * gl_pathv points to (gl_offs + gl_pathc + 1) items. + */ +static int +globextend(const gchar *path, mono_w32file_unix_glob_t *pglob, size_t *limitp) +{ + char **pathv; + int i; + unsigned int newsize, len; + char *copy; + const gchar *p; + + newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs); + /* FIXME: Can just use realloc(). */ + pathv = (char **)(pglob->gl_pathv ? g_realloc ((char *)pglob->gl_pathv, newsize) : + g_malloc (newsize)); + if (pathv == NULL) { + if (pglob->gl_pathv) { + g_free (pglob->gl_pathv); + pglob->gl_pathv = NULL; + } + return(W32FILE_UNIX_GLOB_NOSPACE); + } + + if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) { + /* first time around -- clear initial gl_offs items */ + pathv += pglob->gl_offs; + for (i = pglob->gl_offs; --i >= 0; ) + *--pathv = NULL; + } + pglob->gl_pathv = pathv; + + for (p = path; *p++;) + ; + len = (size_t)(p - path); + *limitp += len; + if ((copy = (char *)malloc(len)) != NULL) { + if (g_Ctoc(path, copy, len)) { + g_free (copy); + return(W32FILE_UNIX_GLOB_NOSPACE); + } + pathv[pglob->gl_offs + pglob->gl_pathc++] = copy; + } + pathv[pglob->gl_offs + pglob->gl_pathc] = NULL; + +#if 0 + /* Broken on opensuse 11 */ + if ((pglob->gl_flags & W32FILE_UNIX_GLOB_LIMIT) && + newsize + *limitp >= ARG_MAX) { + errno = 0; + return(W32FILE_UNIX_GLOB_NOSPACE); + } +#endif + + return(copy == NULL ? W32FILE_UNIX_GLOB_NOSPACE : 0); +} + + +/* + * pattern matching function for filenames. Each occurrence of the * + * pattern causes a recursion level. + */ +static int +match(const gchar *name, gchar *pat, gchar *patend, gboolean ignorecase) +{ + gchar c; + + while (pat < patend) { + c = *pat++; + switch (c & M_MASK) { + case M_ALL: + if (pat == patend) + return(1); + do { + if (match(name, pat, patend, ignorecase)) + return(1); + } while (*name++ != EOS); + return(0); + case M_ONE: + if (*name++ == EOS) + return(0); + break; + default: + if (ignorecase) { + if (g_ascii_tolower (*name++) != g_ascii_tolower (c)) + return(0); + } else { + if (*name++ != c) + return(0); + } + + break; + } + } + return(*name == EOS); +} + +/* Free allocated data belonging to a mono_w32file_unix_glob_t structure. */ +void +mono_w32file_unix_globfree(mono_w32file_unix_glob_t *pglob) +{ + int i; + char **pp; + + if (pglob->gl_pathv != NULL) { + pp = pglob->gl_pathv + pglob->gl_offs; + for (i = pglob->gl_pathc; i--; ++pp) + if (*pp) + g_free (*pp); + g_free (pglob->gl_pathv); + pglob->gl_pathv = NULL; + } +} + +static int +g_Ctoc(const gchar *str, char *buf, unsigned int len) +{ + + while (len--) { + if ((*buf++ = *str++) == EOS) + return (0); + } + return (1); +} + +#ifdef DEBUG_ENABLED +static void +qprintf(const char *str, Char *s) +{ + Char *p; + + (void)printf("%s:\n", str); + for (p = s; *p; p++) + (void)printf("%c", CHAR(*p)); + (void)printf("\n"); + for (p = s; *p; p++) + (void)printf("%c", *p & M_PROTECT ? '"' : ' '); + (void)printf("\n"); + for (p = s; *p; p++) + (void)printf("%c", ismeta(*p) ? '_' : ' '); + (void)printf("\n"); +} +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32file-unix-glob.h b/unity-2019.4.24f1-mbe/mono/metadata/w32file-unix-glob.h new file mode 100644 index 000000000..81f604ca4 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32file-unix-glob.h @@ -0,0 +1,74 @@ +/* $OpenBSD: glob.h,v 1.10 2005/12/13 00:35:22 millert Exp $ */ +/* $NetBSD: glob.h,v 1.5 1994/10/26 00:55:56 cgd Exp $ */ + +/** + * \file + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Guido van Rossum. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)glob.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef __MONO_METADATA_W32FILE_UNIX_GLOB_H__ +#define __MONO_METADATA_W32FILE_UNIX_GLOB_H__ + +#include + +struct stat; +typedef struct { + int gl_pathc; /* Count of total paths so far. */ + int gl_offs; /* Reserved at beginning of gl_pathv. */ + int gl_flags; /* Copy of flags parameter to glob. */ + char **gl_pathv; /* List of paths matching pattern. */ +} mono_w32file_unix_glob_t; + +#define W32FILE_UNIX_GLOB_APPEND 0x0001 /* Append to output from previous call. */ +#define W32FILE_UNIX_GLOB_UNIQUE 0x0040 /* When appending only add items that aren't already in the list */ +#define W32FILE_UNIX_GLOB_NOSPACE (-1) /* Malloc call failed. */ +#define W32FILE_UNIX_GLOB_ABORTED (-2) /* Unignored error. */ +#define W32FILE_UNIX_GLOB_NOMATCH (-3) /* No match and W32FILE_UNIX_GLOB_NOCHECK not set. */ +#define W32FILE_UNIX_GLOB_NOSYS (-4) /* Function not supported. */ + +#define W32FILE_UNIX_GLOB_MAGCHAR 0x0100 /* Pattern had globbing characters. */ +#define W32FILE_UNIX_GLOB_LIMIT 0x2000 /* Limit pattern match output to ARG_MAX */ +#define W32FILE_UNIX_GLOB_IGNORECASE 0x4000 /* Ignore case when matching */ +#define W32FILE_UNIX_GLOB_ABEND W32FILE_UNIX_GLOB_ABORTED /* backward compatibility */ + +G_BEGIN_DECLS + +int +mono_w32file_unix_glob (GDir *dir, const char *, int, mono_w32file_unix_glob_t *); + +void +mono_w32file_unix_globfree (mono_w32file_unix_glob_t *); + +G_END_DECLS + +#endif /* !__MONO_METADATA_W32FILE_UNIX_GLOB_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32file-unix.c b/unity-2019.4.24f1-mbe/mono/metadata/w32file-unix.c new file mode 100644 index 000000000..e5a52b66c --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32file-unix.c @@ -0,0 +1,4829 @@ +/** + * \file + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_STATVFS_H +#include +#endif +#if defined(HAVE_SYS_STATFS_H) +#include +#endif +#if defined(HAVE_SYS_PARAM_H) && defined(HAVE_SYS_MOUNT_H) +#include +#include +#endif +#include +#include +#include +#ifdef __linux__ +#include +#include +#include +#endif +#include +#ifdef HAVE_DIRENT_H +# include +#endif + +#include "w32file.h" +#include "w32file-internals.h" + +#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" +#include "utils/mono-threads.h" +#include "utils/mono-threads-api.h" +#include "utils/strenc.h" +#include "utils/refcount.h" + +#define INVALID_HANDLE_VALUE (GINT_TO_POINTER (-1)) + +typedef struct { + guint64 device; + guint64 inode; + guint32 sharemode; + guint32 access; + guint32 handle_refs; + guint32 timestamp; +} FileShare; + +/* Currently used for both FILE, CONSOLE and PIPE handle types. + * This may have to change in future. */ +typedef struct { + MonoFDHandle fdhandle; + gchar *filename; + FileShare *share_info; /* Pointer into shared mem */ + guint32 security_attributes; + guint32 fileaccess; + guint32 sharemode; + guint32 attrs; +} FileHandle; + +typedef struct { + MonoRefCount ref; + MonoCoopMutex mutex; + gchar **namelist; + gchar *dir_part; + gint num; + gsize count; +} FindHandle; + +/* + * If SHM is disabled, this will point to a hash of FileShare structures, otherwise + * it will be NULL. We use this instead of _wapi_fileshare_layout to avoid allocating a + * 4MB array. + */ +static GHashTable *file_share_table; +static MonoCoopMutex file_share_mutex; + +static GHashTable *finds; +static MonoCoopMutex finds_mutex; + +static void +time_t_to_filetime (time_t timeval, FILETIME *filetime) +{ + guint64 ticks; + + ticks = ((guint64)timeval * 10000000) + 116444736000000000ULL; + filetime->dwLowDateTime = ticks & 0xFFFFFFFF; + filetime->dwHighDateTime = ticks >> 32; +} + +static FileHandle* +file_data_create (MonoFDType type, gint fd) +{ + FileHandle *filehandle; + + filehandle = g_new0 (FileHandle, 1); + mono_fdhandle_init ((MonoFDHandle*) filehandle, type, fd); + + return filehandle; +} + +static gint +_wapi_unlink (const gchar *pathname); + +static void +file_share_release (FileShare *share_info); + +static void +file_data_close (MonoFDHandle *fdhandle) +{ + FileHandle* filehandle; + + filehandle = (FileHandle*) fdhandle; + g_assert (filehandle); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: closing fd %d", __func__, ((MonoFDHandle*) filehandle)->fd); + + if (((MonoFDHandle*) filehandle)->type == MONO_FDTYPE_FILE && (filehandle->attrs & FILE_FLAG_DELETE_ON_CLOSE)) { + _wapi_unlink (filehandle->filename); + } + + if (((MonoFDHandle*) filehandle)->type != MONO_FDTYPE_CONSOLE || ((MonoFDHandle*) filehandle)->fd > 2) { + if (filehandle->share_info) { + file_share_release (filehandle->share_info); + filehandle->share_info = NULL; + } + + MONO_ENTER_GC_SAFE; + close (((MonoFDHandle*) filehandle)->fd); + MONO_EXIT_GC_SAFE; + } +} + +static void +file_data_destroy (MonoFDHandle *fdhandle) +{ + FileHandle *filehandle; + + filehandle = (FileHandle*) fdhandle; + g_assert (filehandle); + + if (filehandle->filename) + g_free (filehandle->filename); + + g_free (filehandle); +} + +static void +file_share_release (FileShare *share_info) +{ + /* Prevent new entries racing with us */ + mono_coop_mutex_lock (&file_share_mutex); + + g_assert (share_info->handle_refs > 0); + share_info->handle_refs -= 1; + + if (share_info->handle_refs == 0) { + g_hash_table_remove (file_share_table, share_info); + // g_free (share_info); + } + + mono_coop_mutex_unlock (&file_share_mutex); +} + +static gint +file_share_equal (gconstpointer ka, gconstpointer kb) +{ + const FileShare *s1 = (const FileShare *)ka; + const FileShare *s2 = (const FileShare *)kb; + + return (s1->device == s2->device && s1->inode == s2->inode) ? 1 : 0; +} + +static guint +file_share_hash (gconstpointer data) +{ + const FileShare *s = (const FileShare *)data; + + return s->inode; +} + +static gboolean +file_share_get (guint64 device, guint64 inode, guint32 new_sharemode, guint32 new_access, + guint32 *old_sharemode, guint32 *old_access, FileShare **share_info) +{ + FileShare *file_share; + gboolean exists = FALSE; + + /* Prevent new entries racing with us */ + mono_coop_mutex_lock (&file_share_mutex); + + FileShare tmp; + + /* + * Instead of allocating a 4MB array, we use a hash table to keep track of this + * info. This is needed even if SHM is disabled, to track sharing inside + * the current process. + */ + if (!file_share_table) + file_share_table = g_hash_table_new_full (file_share_hash, file_share_equal, NULL, g_free); + + tmp.device = device; + tmp.inode = inode; + + file_share = (FileShare *)g_hash_table_lookup (file_share_table, &tmp); + if (file_share) { + *old_sharemode = file_share->sharemode; + *old_access = file_share->access; + *share_info = file_share; + + g_assert (file_share->handle_refs > 0); + file_share->handle_refs += 1; + + exists = TRUE; + } else { + file_share = g_new0 (FileShare, 1); + + file_share->device = device; + file_share->inode = inode; + file_share->sharemode = new_sharemode; + file_share->access = new_access; + file_share->handle_refs = 1; + *share_info = file_share; + + g_hash_table_insert (file_share_table, file_share, file_share); + } + + mono_coop_mutex_unlock (&file_share_mutex); + + return(exists); +} + +static gint +_wapi_open (const gchar *pathname, gint flags, mode_t mode) +{ + gint fd; + gchar *located_filename; + + if (flags & O_CREAT) { + located_filename = mono_portability_find_file (pathname, FALSE); + if (located_filename == NULL) { + MONO_ENTER_GC_SAFE; + fd = open (pathname, flags, mode); + MONO_EXIT_GC_SAFE; + } else { + MONO_ENTER_GC_SAFE; + fd = open (located_filename, flags, mode); + MONO_EXIT_GC_SAFE; + g_free (located_filename); + } + } else { + MONO_ENTER_GC_SAFE; + fd = open (pathname, flags, mode); + MONO_EXIT_GC_SAFE; + if (fd == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) { + gint saved_errno = errno; + located_filename = mono_portability_find_file (pathname, TRUE); + + if (located_filename == NULL) { + errno = saved_errno; + return -1; + } + + MONO_ENTER_GC_SAFE; + fd = open (located_filename, flags, mode); + MONO_EXIT_GC_SAFE; + g_free (located_filename); + } + } + + return(fd); +} + +static gint +_wapi_access (const gchar *pathname, gint mode) +{ + gint ret; + + MONO_ENTER_GC_SAFE; + ret = access (pathname, mode); + MONO_EXIT_GC_SAFE; + if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) { + gint saved_errno = errno; + gchar *located_filename = mono_portability_find_file (pathname, TRUE); + + if (located_filename == NULL) { + errno = saved_errno; + return -1; + } + + MONO_ENTER_GC_SAFE; + ret = access (located_filename, mode); + MONO_EXIT_GC_SAFE; + g_free (located_filename); + } + + return ret; +} + +static gint +_wapi_chmod (const gchar *pathname, mode_t mode) +{ + gint ret; + + MONO_ENTER_GC_SAFE; + ret = chmod (pathname, mode); + MONO_EXIT_GC_SAFE; + if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) { + gint saved_errno = errno; + gchar *located_filename = mono_portability_find_file (pathname, TRUE); + + if (located_filename == NULL) { + errno = saved_errno; + return -1; + } + + MONO_ENTER_GC_SAFE; + ret = chmod (located_filename, mode); + MONO_EXIT_GC_SAFE; + g_free (located_filename); + } + + return ret; +} + +static gint +_wapi_utime (const gchar *filename, const struct utimbuf *buf) +{ + gint ret; + + MONO_ENTER_GC_SAFE; + ret = utime (filename, buf); + MONO_EXIT_GC_SAFE; + if (ret == -1 && errno == ENOENT && IS_PORTABILITY_SET) { + gint saved_errno = errno; + gchar *located_filename = mono_portability_find_file (filename, TRUE); + + if (located_filename == NULL) { + errno = saved_errno; + return -1; + } + + MONO_ENTER_GC_SAFE; + ret = utime (located_filename, buf); + MONO_EXIT_GC_SAFE; + g_free (located_filename); + } + + return ret; +} + +static gint +_wapi_unlink (const gchar *pathname) +{ + gint ret; + + MONO_ENTER_GC_SAFE; + ret = unlink (pathname); + MONO_EXIT_GC_SAFE; + if (ret == -1 && (errno == ENOENT || errno == ENOTDIR || errno == EISDIR) && IS_PORTABILITY_SET) { + gint saved_errno = errno; + gchar *located_filename = mono_portability_find_file (pathname, TRUE); + + if (located_filename == NULL) { + errno = saved_errno; + return -1; + } + + MONO_ENTER_GC_SAFE; + ret = unlink (located_filename); + MONO_EXIT_GC_SAFE; + g_free (located_filename); + } + + return ret; +} + +static gint +_wapi_rename (const gchar *oldpath, const gchar *newpath) +{ + gint ret; + gchar *located_newpath = mono_portability_find_file (newpath, FALSE); + + if (located_newpath == NULL) { + MONO_ENTER_GC_SAFE; + ret = rename (oldpath, newpath); + MONO_EXIT_GC_SAFE; + } else { + MONO_ENTER_GC_SAFE; + ret = rename (oldpath, located_newpath); + MONO_EXIT_GC_SAFE; + + if (ret == -1 && (errno == EISDIR || errno == ENAMETOOLONG || errno == ENOENT || errno == ENOTDIR || errno == EXDEV) && IS_PORTABILITY_SET) { + gint saved_errno = errno; + gchar *located_oldpath = mono_portability_find_file (oldpath, TRUE); + + if (located_oldpath == NULL) { + g_free (located_oldpath); + g_free (located_newpath); + + errno = saved_errno; + return -1; + } + + MONO_ENTER_GC_SAFE; + ret = rename (located_oldpath, located_newpath); + MONO_EXIT_GC_SAFE; + g_free (located_oldpath); + } + g_free (located_newpath); + } + + return ret; +} + +static gint +_wapi_stat (const gchar *path, struct stat *buf) +{ + gint ret; + + MONO_ENTER_GC_SAFE; + ret = stat (path, buf); + MONO_EXIT_GC_SAFE; + if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) { + gint saved_errno = errno; + gchar *located_filename = mono_portability_find_file (path, TRUE); + + if (located_filename == NULL) { + errno = saved_errno; + return -1; + } + + MONO_ENTER_GC_SAFE; + ret = stat (located_filename, buf); + MONO_EXIT_GC_SAFE; + g_free (located_filename); + } + + return ret; +} + +static gint +_wapi_lstat (const gchar *path, struct stat *buf) +{ + gint ret; + + MONO_ENTER_GC_SAFE; + ret = lstat (path, buf); + MONO_EXIT_GC_SAFE; + if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) { + gint saved_errno = errno; + gchar *located_filename = mono_portability_find_file (path, TRUE); + + if (located_filename == NULL) { + errno = saved_errno; + return -1; + } + + ret = lstat (located_filename, buf); + g_free (located_filename); + } + + return ret; +} + +static gint +_wapi_mkdir (const gchar *pathname, mode_t mode) +{ + gint ret; + gchar *located_filename = mono_portability_find_file (pathname, FALSE); + + if (located_filename == NULL) { + MONO_ENTER_GC_SAFE; + ret = mkdir (pathname, mode); + MONO_EXIT_GC_SAFE; + } else { + MONO_ENTER_GC_SAFE; + ret = mkdir (located_filename, mode); + MONO_EXIT_GC_SAFE; + g_free (located_filename); + } + + return ret; +} + +static gint +_wapi_rmdir (const gchar *pathname) +{ + gint ret; + + MONO_ENTER_GC_SAFE; + ret = rmdir (pathname); + MONO_EXIT_GC_SAFE; + if (ret == -1 && (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) && IS_PORTABILITY_SET) { + gint saved_errno = errno; + gchar *located_filename = mono_portability_find_file (pathname, TRUE); + + if (located_filename == NULL) { + errno = saved_errno; + return -1; + } + + MONO_ENTER_GC_SAFE; + ret = rmdir (located_filename); + MONO_EXIT_GC_SAFE; + g_free (located_filename); + } + + return ret; +} + +static gint +_wapi_chdir (const gchar *path) +{ + gint ret; + + MONO_ENTER_GC_SAFE; + ret = chdir (path); + MONO_EXIT_GC_SAFE; + if (ret == -1 && (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) && IS_PORTABILITY_SET) { + gint saved_errno = errno; + gchar *located_filename = mono_portability_find_file (path, TRUE); + + if (located_filename == NULL) { + errno = saved_errno; + return -1; + } + + MONO_ENTER_GC_SAFE; + ret = chdir (located_filename); + MONO_EXIT_GC_SAFE; + g_free (located_filename); + } + + return ret; +} + +static gchar* +_wapi_basename (const gchar *filename) +{ + gchar *new_filename = g_strdup (filename), *ret; + + if (IS_PORTABILITY_SET) { + g_strdelimit (new_filename, "\\", '/'); + } + + if (IS_PORTABILITY_DRIVE && g_ascii_isalpha (new_filename[0]) && (new_filename[1] == ':')) { + gint len = strlen (new_filename); + + g_memmove (new_filename, new_filename + 2, len - 2); + new_filename[len - 2] = '\0'; + } + + ret = g_path_get_basename (new_filename); + g_free (new_filename); + + return ret; +} + +static gchar* +_wapi_dirname (const gchar *filename) +{ + gchar *new_filename = g_strdup (filename), *ret; + + if (IS_PORTABILITY_SET) { + g_strdelimit (new_filename, "\\", '/'); + } + + if (IS_PORTABILITY_DRIVE && g_ascii_isalpha (new_filename[0]) && (new_filename[1] == ':')) { + gint len = strlen (new_filename); + + g_memmove (new_filename, new_filename + 2, len - 2); + new_filename[len - 2] = '\0'; + } + + ret = g_path_get_dirname (new_filename); + g_free (new_filename); + + return ret; +} + +static GDir* +_wapi_g_dir_open (const gchar *path, guint flags, GError **error) +{ + GDir *ret; + + MONO_ENTER_GC_SAFE; + ret = g_dir_open (path, flags, error); + MONO_EXIT_GC_SAFE; + if (ret == NULL && ((*error)->code == G_FILE_ERROR_NOENT || (*error)->code == G_FILE_ERROR_NOTDIR || (*error)->code == G_FILE_ERROR_NAMETOOLONG) && IS_PORTABILITY_SET) { + gchar *located_filename = mono_portability_find_file (path, TRUE); + GError *tmp_error = NULL; + + if (located_filename == NULL) { + return(NULL); + } + + MONO_ENTER_GC_SAFE; + ret = g_dir_open (located_filename, flags, &tmp_error); + MONO_EXIT_GC_SAFE; + g_free (located_filename); + if (tmp_error == NULL) { + g_clear_error (error); + } + } + + return ret; +} + +static gint +get_errno_from_g_file_error (gint error) +{ + switch (error) { +#ifdef EACCES + case G_FILE_ERROR_ACCES: return EACCES; +#endif +#ifdef ENAMETOOLONG + case G_FILE_ERROR_NAMETOOLONG: return ENAMETOOLONG; +#endif +#ifdef ENOENT + case G_FILE_ERROR_NOENT: return ENOENT; +#endif +#ifdef ENOTDIR + case G_FILE_ERROR_NOTDIR: return ENOTDIR; +#endif +#ifdef ENXIO + case G_FILE_ERROR_NXIO: return ENXIO; +#endif +#ifdef ENODEV + case G_FILE_ERROR_NODEV: return ENODEV; +#endif +#ifdef EROFS + case G_FILE_ERROR_ROFS: return EROFS; +#endif +#ifdef ETXTBSY + case G_FILE_ERROR_TXTBSY: return ETXTBSY; +#endif +#ifdef EFAULT + case G_FILE_ERROR_FAULT: return EFAULT; +#endif +#ifdef ELOOP + case G_FILE_ERROR_LOOP: return ELOOP; +#endif +#ifdef ENOSPC + case G_FILE_ERROR_NOSPC: return ENOSPC; +#endif +#ifdef ENOMEM + case G_FILE_ERROR_NOMEM: return ENOMEM; +#endif +#ifdef EMFILE + case G_FILE_ERROR_MFILE: return EMFILE; +#endif +#ifdef ENFILE + case G_FILE_ERROR_NFILE: return ENFILE; +#endif +#ifdef EBADF + case G_FILE_ERROR_BADF: return EBADF; +#endif +#ifdef EINVAL + case G_FILE_ERROR_INVAL: return EINVAL; +#endif +#ifdef EPIPE + case G_FILE_ERROR_PIPE: return EPIPE; +#endif +#ifdef EAGAIN + case G_FILE_ERROR_AGAIN: return EAGAIN; +#endif +#ifdef EINTR + case G_FILE_ERROR_INTR: return EINTR; +#endif +#ifdef EIO + case G_FILE_ERROR_IO: return EIO; +#endif +#ifdef EPERM + case G_FILE_ERROR_PERM: return EPERM; +#endif + case G_FILE_ERROR_FAILED: return ERROR_INVALID_PARAMETER; + default: + g_assert_not_reached (); + } +} + +static gint +file_compare (gconstpointer a, gconstpointer b) +{ + gchar *astr = *(gchar **) a; + gchar *bstr = *(gchar **) b; + + return strcmp (astr, bstr); +} + +/* scandir using glib */ +static gint +_wapi_io_scandir (const gchar *dirname, const gchar *pattern, gchar ***namelist) +{ + GError *error = NULL; + GDir *dir; + GPtrArray *names; + gint result; + mono_w32file_unix_glob_t glob_buf; + gint flags = 0, i; + + dir = _wapi_g_dir_open (dirname, 0, &error); + if (dir == NULL) { + /* g_dir_open returns ENOENT on directories on which we don't + * have read/x permission */ + gint errnum = get_errno_from_g_file_error (error->code); + g_error_free (error); + if (errnum == ENOENT && + !_wapi_access (dirname, F_OK) && + _wapi_access (dirname, R_OK|X_OK)) { + errnum = EACCES; + } + + errno = errnum; + return -1; + } + + if (IS_PORTABILITY_CASE) { + flags = W32FILE_UNIX_GLOB_IGNORECASE; + } + + result = mono_w32file_unix_glob (dir, pattern, flags, &glob_buf); + if (g_str_has_suffix (pattern, ".*")) { + /* Special-case the patterns ending in '.*', as + * windows also matches entries with no extension with + * this pattern. + * + * TODO: should this be a MONO_IOMAP option? + */ + gchar *pattern2 = g_strndup (pattern, strlen (pattern) - 2); + gint result2; + + MONO_ENTER_GC_SAFE; + g_dir_rewind (dir); + MONO_EXIT_GC_SAFE; + result2 = mono_w32file_unix_glob (dir, pattern2, flags | W32FILE_UNIX_GLOB_APPEND | W32FILE_UNIX_GLOB_UNIQUE, &glob_buf); + + g_free (pattern2); + + if (result != 0) { + result = result2; + } + } + + MONO_ENTER_GC_SAFE; + g_dir_close (dir); + MONO_EXIT_GC_SAFE; + if (glob_buf.gl_pathc == 0) { + return(0); + } else if (result != 0) { + return -1; + } + + names = g_ptr_array_new (); + for (i = 0; i < glob_buf.gl_pathc; i++) { + g_ptr_array_add (names, g_strdup (glob_buf.gl_pathv[i])); + } + + mono_w32file_unix_globfree (&glob_buf); + + result = names->len; + if (result > 0) { + g_ptr_array_sort (names, file_compare); + g_ptr_array_set_size (names, result + 1); + + *namelist = (gchar **) g_ptr_array_free (names, FALSE); + } else { + g_ptr_array_free (names, TRUE); + } + + return result; +} + +static gboolean +_wapi_lock_file_region (gint fd, off_t offset, off_t length) +{ + struct flock lock_data; + gint ret; + + if (offset < 0 || length < 0) { + mono_w32error_set_last (ERROR_INVALID_PARAMETER); + return FALSE; + } + + lock_data.l_type = F_WRLCK; + lock_data.l_whence = SEEK_SET; + lock_data.l_start = offset; + lock_data.l_len = length; + + do { + ret = fcntl (fd, F_SETLK, &lock_data); + } while(ret == -1 && errno == EINTR); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fcntl returns %d", __func__, ret); + + if (ret == -1) { + /* + * if locks are not available (NFS for example), + * ignore the error + */ + if (errno == ENOLCK +#ifdef EOPNOTSUPP + || errno == EOPNOTSUPP +#endif +#ifdef ENOTSUP + || errno == ENOTSUP +#endif + ) { + return TRUE; + } + + mono_w32error_set_last (ERROR_LOCK_VIOLATION); + return FALSE; + } + + return TRUE; +} + +static gboolean +_wapi_unlock_file_region (gint fd, off_t offset, off_t length) +{ + struct flock lock_data; + gint ret; + + lock_data.l_type = F_UNLCK; + lock_data.l_whence = SEEK_SET; + lock_data.l_start = offset; + lock_data.l_len = length; + + do { + ret = fcntl (fd, F_SETLK, &lock_data); + } while(ret == -1 && errno == EINTR); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fcntl returns %d", __func__, ret); + + if (ret == -1) { + /* + * if locks are not available (NFS for example), + * ignore the error + */ + if (errno == ENOLCK +#ifdef EOPNOTSUPP + || errno == EOPNOTSUPP +#endif +#ifdef ENOTSUP + || errno == ENOTSUP +#endif + ) { + return TRUE; + } + + mono_w32error_set_last (ERROR_LOCK_VIOLATION); + return FALSE; + } + + return TRUE; +} + +static gboolean lock_while_writing = FALSE; + +/* Some utility functions. + */ + +/* + * Check if a file is writable by the current user. + * + * This is is a best effort kind of thing. It assumes a reasonable sane set + * of permissions by the underlying OS. + * + * We generally assume that basic unix permission bits are authoritative. Which might not + * be the case under systems with extended permissions systems (posix ACLs, SELinux, OSX/iOS sandboxing, etc) + * + * The choice of access as the fallback is due to the expected lower overhead compared to trying to open the file. + * + * The only expected problem with using access are for root, setuid or setgid programs as access is not consistent + * under those situations. It's to be expected that this should not happen in practice as those bits are very dangerous + * and should not be used with a dynamic runtime. + */ +static gboolean +is_file_writable (struct stat *st, const gchar *path) +{ +#if __APPLE__ + // OS X Finder "locked" or `ls -lO` "uchg". + // This only covers one of several cases where an OS X file could be unwritable through special flags. + if (st->st_flags & (UF_IMMUTABLE|SF_IMMUTABLE)) + return 0; +#endif + + /* Is it globally writable? */ + if (st->st_mode & S_IWOTH) + return 1; + + /* Am I the owner? */ + if ((st->st_uid == geteuid ()) && (st->st_mode & S_IWUSR)) + return 1; + + /* Am I in the same group? */ + if ((st->st_gid == getegid ()) && (st->st_mode & S_IWGRP)) + return 1; + + /* Fallback to using access(2). It's not ideal as it might not take into consideration euid/egid + * but it's the only sane option we have on unix. + */ + return access (path, W_OK) == 0; +} + + +static guint32 _wapi_stat_to_file_attributes (const gchar *pathname, + struct stat *buf, + struct stat *lbuf) +{ + guint32 attrs = 0; + gchar *filename; + + /* FIXME: this could definitely be better, but there seems to + * be no pattern to the attributes that are set + */ + + /* Sockets (0140000) != Directory (040000) + Regular file (0100000) */ + if (S_ISSOCK (buf->st_mode)) + buf->st_mode &= ~S_IFSOCK; /* don't consider socket protection */ + + filename = _wapi_basename (pathname); + + if (S_ISDIR (buf->st_mode)) { + attrs = FILE_ATTRIBUTE_DIRECTORY; + if (!is_file_writable (buf, pathname)) { + attrs |= FILE_ATTRIBUTE_READONLY; + } + if (filename[0] == '.') { + attrs |= FILE_ATTRIBUTE_HIDDEN; + } + } else { + if (!is_file_writable (buf, pathname)) { + attrs = FILE_ATTRIBUTE_READONLY; + + if (filename[0] == '.') { + attrs |= FILE_ATTRIBUTE_HIDDEN; + } + } else if (filename[0] == '.') { + attrs = FILE_ATTRIBUTE_HIDDEN; + } else { + attrs = FILE_ATTRIBUTE_NORMAL; + } + } + + if (lbuf != NULL) { + if (S_ISLNK (lbuf->st_mode)) { + attrs |= FILE_ATTRIBUTE_REPARSE_POINT; + } + } + + g_free (filename); + + return attrs; +} + +static void +_wapi_set_last_error_from_errno (void) +{ + mono_w32error_set_last (mono_w32error_unix_to_win32 (errno)); +} + +static void _wapi_set_last_path_error_from_errno (const gchar *dir, + const gchar *path) +{ + if (errno == ENOENT) { + /* Check the path - if it's a missing directory then + * we need to set PATH_NOT_FOUND not FILE_NOT_FOUND + */ + gchar *dirname; + + + if (dir == NULL) { + dirname = _wapi_dirname (path); + } else { + dirname = g_strdup (dir); + } + + if (_wapi_access (dirname, F_OK) == 0) { + mono_w32error_set_last (ERROR_FILE_NOT_FOUND); + } else { + mono_w32error_set_last (ERROR_PATH_NOT_FOUND); + } + + g_free (dirname); + } else { + _wapi_set_last_error_from_errno (); + } +} + +static gboolean +file_read(FileHandle *filehandle, gpointer buffer, guint32 numbytes, guint32 *bytesread) +{ + gint ret; + MonoThreadInfo *info = mono_thread_info_current (); + + if(bytesread!=NULL) { + *bytesread=0; + } + + if(!(filehandle->fileaccess & (GENERIC_READ | GENERIC_ALL))) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d doesn't have GENERIC_READ access: %u", __func__, ((MonoFDHandle*) filehandle)->fd, filehandle->fileaccess); + + mono_w32error_set_last (ERROR_ACCESS_DENIED); + return(FALSE); + } + + do { + MONO_ENTER_GC_SAFE; + ret = read (((MonoFDHandle*) filehandle)->fd, buffer, numbytes); + MONO_EXIT_GC_SAFE; + } while (ret == -1 && errno == EINTR && + !mono_thread_info_is_interrupt_state (info)); + + if(ret==-1) { + gint err = errno; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: read of fd %d error: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(err)); + mono_w32error_set_last (mono_w32error_unix_to_win32 (err)); + return(FALSE); + } + + if (bytesread != NULL) { + *bytesread = ret; + MONO_PROFILER_RAISE (fileio, (1, *bytesread)); + } + + return(TRUE); +} + +static gboolean +file_write(FileHandle *filehandle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten) +{ + gint ret; + off_t current_pos = 0; + MonoThreadInfo *info = mono_thread_info_current (); + + if(byteswritten!=NULL) { + *byteswritten=0; + } + + if(!(filehandle->fileaccess & GENERIC_WRITE) && !(filehandle->fileaccess & GENERIC_ALL)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d doesn't have GENERIC_WRITE access: %u", __func__, ((MonoFDHandle*) filehandle)->fd, filehandle->fileaccess); + + mono_w32error_set_last (ERROR_ACCESS_DENIED); + return(FALSE); + } + + if (lock_while_writing) { + /* Need to lock the region we're about to write to, + * because we only do advisory locking on POSIX + * systems + */ + MONO_ENTER_GC_SAFE; + current_pos = lseek (((MonoFDHandle*) filehandle)->fd, (off_t)0, SEEK_CUR); + MONO_EXIT_GC_SAFE; + if (current_pos == -1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d lseek failed: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror (errno)); + _wapi_set_last_error_from_errno (); + return(FALSE); + } + + if (_wapi_lock_file_region (((MonoFDHandle*) filehandle)->fd, current_pos, numbytes) == FALSE) { + /* The error has already been set */ + return(FALSE); + } + } + + do { + MONO_ENTER_GC_SAFE; + ret = write (((MonoFDHandle*) filehandle)->fd, buffer, numbytes); + MONO_EXIT_GC_SAFE; + } while (ret == -1 && errno == EINTR && + !mono_thread_info_is_interrupt_state (info)); + + if (lock_while_writing) { + _wapi_unlock_file_region (((MonoFDHandle*) filehandle)->fd, current_pos, numbytes); + } + + if (ret == -1) { + if (errno == EINTR) { + ret = 0; + } else { + _wapi_set_last_error_from_errno (); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: write of fd %d error: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(errno)); + + return(FALSE); + } + } + if (byteswritten != NULL) { + *byteswritten = ret; + MONO_PROFILER_RAISE (fileio, (0, *byteswritten)); + } + return(TRUE); +} + +static gboolean file_flush(FileHandle *filehandle) +{ + gint ret; + + if(!(filehandle->fileaccess & (GENERIC_WRITE | GENERIC_ALL))) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d doesn't have GENERIC_WRITE access: %u", __func__, ((MonoFDHandle*) filehandle)->fd, filehandle->fileaccess); + + mono_w32error_set_last (ERROR_ACCESS_DENIED); + return(FALSE); + } + + MONO_ENTER_GC_SAFE; + ret=fsync(((MonoFDHandle*) filehandle)->fd); + MONO_EXIT_GC_SAFE; + if (ret==-1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fsync of fd %d error: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(errno)); + + _wapi_set_last_error_from_errno (); + return(FALSE); + } + + return(TRUE); +} + +static guint32 file_seek(FileHandle *filehandle, gint32 movedistance, + gint32 *highmovedistance, gint method) +{ + gint64 offset, newpos; + gint whence; + guint32 ret; + + if(!(filehandle->fileaccess & (GENERIC_READ | GENERIC_WRITE | GENERIC_ALL))) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, ((MonoFDHandle*) filehandle)->fd, filehandle->fileaccess); + + mono_w32error_set_last (ERROR_ACCESS_DENIED); + return(INVALID_SET_FILE_POINTER); + } + + switch(method) { + case FILE_BEGIN: + whence=SEEK_SET; + break; + case FILE_CURRENT: + whence=SEEK_CUR; + break; + case FILE_END: + whence=SEEK_END; + break; + default: + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: invalid seek type %d", __func__, method); + + mono_w32error_set_last (ERROR_INVALID_PARAMETER); + return(INVALID_SET_FILE_POINTER); + } + +#ifdef HAVE_LARGE_FILE_SUPPORT + if(highmovedistance==NULL) { + offset=movedistance; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: setting offset to %" G_GINT64_FORMAT " (low %" G_GINT32_FORMAT ")", __func__, + offset, movedistance); + } else { + offset=((gint64) *highmovedistance << 32) | (guint32)movedistance; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: setting offset to %" G_GINT64_FORMAT " 0x%" PRIx64 " (high %" G_GINT32_FORMAT " 0x%" PRIx32 ", low %" G_GINT32_FORMAT " 0x%" PRIx32 ")", + __func__, offset, offset, *highmovedistance, *highmovedistance, movedistance, movedistance); + } +#else + offset=movedistance; +#endif + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: moving fd %d by %" G_GINT64_FORMAT " bytes from %d", __func__, ((MonoFDHandle*) filehandle)->fd, offset, whence); + +#ifdef HOST_ANDROID + /* bionic doesn't support -D_FILE_OFFSET_BITS=64 */ + MONO_ENTER_GC_SAFE; + newpos=lseek64(((MonoFDHandle*) filehandle)->fd, offset, whence); + MONO_EXIT_GC_SAFE; +#else + MONO_ENTER_GC_SAFE; + newpos=lseek(((MonoFDHandle*) filehandle)->fd, offset, whence); + MONO_EXIT_GC_SAFE; +#endif + if(newpos==-1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: lseek on fd %d returned error %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(errno)); + + _wapi_set_last_error_from_errno (); + return(INVALID_SET_FILE_POINTER); + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: lseek returns %" G_GINT64_FORMAT, __func__, newpos); + +#ifdef HAVE_LARGE_FILE_SUPPORT + ret=newpos & 0xFFFFFFFF; + if(highmovedistance!=NULL) { + *highmovedistance=newpos>>32; + } +#else + ret=newpos; + if(highmovedistance!=NULL) { + /* Accurate, but potentially dodgy :-) */ + *highmovedistance=0; + } +#endif + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: move of fd %d returning %" G_GUINT32_FORMAT "/%" G_GINT32_FORMAT, __func__, ((MonoFDHandle*) filehandle)->fd, ret, highmovedistance==NULL?0:*highmovedistance); + + return(ret); +} + +static gboolean file_setendoffile(FileHandle *filehandle) +{ + struct stat statbuf; + off_t pos; + gint ret; + MonoThreadInfo *info = mono_thread_info_current (); + + if(!(filehandle->fileaccess & (GENERIC_WRITE | GENERIC_ALL))) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d doesn't have GENERIC_WRITE access: %u", __func__, ((MonoFDHandle*) filehandle)->fd, filehandle->fileaccess); + + mono_w32error_set_last (ERROR_ACCESS_DENIED); + return(FALSE); + } + + /* Find the current file position, and the file length. If + * the file position is greater than the length, write to + * extend the file with a hole. If the file position is less + * than the length, truncate the file. + */ + + MONO_ENTER_GC_SAFE; + ret=fstat(((MonoFDHandle*) filehandle)->fd, &statbuf); + MONO_EXIT_GC_SAFE; + if(ret==-1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d fstat failed: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(errno)); + + _wapi_set_last_error_from_errno (); + return(FALSE); + } + + MONO_ENTER_GC_SAFE; + pos=lseek(((MonoFDHandle*) filehandle)->fd, (off_t)0, SEEK_CUR); + MONO_EXIT_GC_SAFE; + if(pos==-1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d lseek failed: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(errno)); + + _wapi_set_last_error_from_errno (); + return(FALSE); + } + +#ifdef FTRUNCATE_DOESNT_EXTEND + off_t size = statbuf.st_size; + /* I haven't bothered to write the configure.ac stuff for this + * because I don't know if any platform needs it. I'm leaving + * this code just in case though + */ + if(pos>size) { + /* Extend the file. Use write() here, because some + * manuals say that ftruncate() behaviour is undefined + * when the file needs extending. The POSIX spec says + * that on XSI-conformant systems it extends, so if + * every system we care about conforms, then we can + * drop this write. + */ + do { + MONO_ENTER_GC_SAFE; + ret = write (((MonoFDHandle*) filehandle)->fd, "", 1); + MONO_EXIT_GC_SAFE; + } while (ret == -1 && errno == EINTR && + !mono_thread_info_is_interrupt_state (info)); + + if(ret==-1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d extend write failed: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(errno)); + + _wapi_set_last_error_from_errno (); + return(FALSE); + } + + /* And put the file position back after the write */ + MONO_ENTER_GC_SAFE; + ret = lseek (((MonoFDHandle*) filehandle)->fd, pos, SEEK_SET); + MONO_EXIT_GC_SAFE; + if (ret == -1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d second lseek failed: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(errno)); + + _wapi_set_last_error_from_errno (); + return(FALSE); + } + } +#endif + + /* always truncate, because the extend write() adds an extra + * byte to the end of the file + */ + do { + MONO_ENTER_GC_SAFE; + ret=ftruncate(((MonoFDHandle*) filehandle)->fd, pos); + MONO_EXIT_GC_SAFE; + } + while (ret==-1 && errno==EINTR && !mono_thread_info_is_interrupt_state (info)); + if(ret==-1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d ftruncate failed: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(errno)); + + _wapi_set_last_error_from_errno (); + return(FALSE); + } + + return(TRUE); +} + +static guint32 file_getfilesize(FileHandle *filehandle, guint32 *highsize) +{ + struct stat statbuf; + guint32 size; + gint ret; + + if(!(filehandle->fileaccess & (GENERIC_READ | GENERIC_WRITE | GENERIC_ALL))) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, ((MonoFDHandle*) filehandle)->fd, filehandle->fileaccess); + + mono_w32error_set_last (ERROR_ACCESS_DENIED); + return(INVALID_FILE_SIZE); + } + + /* If the file has a size with the low bits 0xFFFFFFFF the + * caller can't tell if this is an error, so clear the error + * value + */ + mono_w32error_set_last (ERROR_SUCCESS); + + MONO_ENTER_GC_SAFE; + ret = fstat(((MonoFDHandle*) filehandle)->fd, &statbuf); + MONO_EXIT_GC_SAFE; + if (ret == -1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d fstat failed: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(errno)); + + _wapi_set_last_error_from_errno (); + return(INVALID_FILE_SIZE); + } + + /* fstat indicates block devices as zero-length, so go a different path */ +#ifdef BLKGETSIZE64 + if (S_ISBLK(statbuf.st_mode)) { + guint64 bigsize; + gint res; + MONO_ENTER_GC_SAFE; + res = ioctl (((MonoFDHandle*) filehandle)->fd, BLKGETSIZE64, &bigsize); + MONO_EXIT_GC_SAFE; + if (res < 0) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d ioctl BLKGETSIZE64 failed: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(errno)); + + _wapi_set_last_error_from_errno (); + return(INVALID_FILE_SIZE); + } + + size = bigsize & 0xFFFFFFFF; + if (highsize != NULL) { + *highsize = bigsize>>32; + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Returning block device size %" G_GUINT32_FORMAT "/%" G_GUINT32_FORMAT, + __func__, size, *highsize); + + return(size); + } +#endif + +#ifdef HAVE_LARGE_FILE_SUPPORT + size = statbuf.st_size & 0xFFFFFFFF; + if (highsize != NULL) { + *highsize = statbuf.st_size>>32; + } +#else + if (highsize != NULL) { + /* Accurate, but potentially dodgy :-) */ + *highsize = 0; + } + size = statbuf.st_size; +#endif + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Returning size %" G_GUINT32_FORMAT "/%" G_GUINT32_FORMAT, __func__, size, *highsize); + + return(size); +} + +static gboolean file_getfiletime(FileHandle *filehandle, FILETIME *create_time, + FILETIME *access_time, + FILETIME *write_time) +{ + struct stat statbuf; + guint64 create_ticks, access_ticks, write_ticks; + gint ret; + + if(!(filehandle->fileaccess & (GENERIC_READ | GENERIC_ALL))) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d doesn't have GENERIC_READ access: %u", __func__, ((MonoFDHandle*) filehandle)->fd, filehandle->fileaccess); + + mono_w32error_set_last (ERROR_ACCESS_DENIED); + return(FALSE); + } + + MONO_ENTER_GC_SAFE; + ret=fstat(((MonoFDHandle*) filehandle)->fd, &statbuf); + MONO_EXIT_GC_SAFE; + if(ret==-1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d fstat failed: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(errno)); + + _wapi_set_last_error_from_errno (); + return(FALSE); + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: atime: %ld ctime: %ld mtime: %ld", __func__, + statbuf.st_atime, statbuf.st_ctime, + statbuf.st_mtime); + + /* Try and guess a meaningful create time by using the older + * of atime or ctime + */ + /* The magic constant comes from msdn documentation + * "Converting a time_t Value to a File Time" + */ + if(statbuf.st_atime < statbuf.st_ctime) { + create_ticks=((guint64)statbuf.st_atime*10000000) + + 116444736000000000ULL; + } else { + create_ticks=((guint64)statbuf.st_ctime*10000000) + + 116444736000000000ULL; + } + + access_ticks=((guint64)statbuf.st_atime*10000000)+116444736000000000ULL; + write_ticks=((guint64)statbuf.st_mtime*10000000)+116444736000000000ULL; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: aticks: %" G_GUINT64_FORMAT " cticks: %" G_GUINT64_FORMAT " wticks: %" G_GUINT64_FORMAT, __func__, + access_ticks, create_ticks, write_ticks); + + if(create_time!=NULL) { + create_time->dwLowDateTime = create_ticks & 0xFFFFFFFF; + create_time->dwHighDateTime = create_ticks >> 32; + } + + if(access_time!=NULL) { + access_time->dwLowDateTime = access_ticks & 0xFFFFFFFF; + access_time->dwHighDateTime = access_ticks >> 32; + } + + if(write_time!=NULL) { + write_time->dwLowDateTime = write_ticks & 0xFFFFFFFF; + write_time->dwHighDateTime = write_ticks >> 32; + } + + return(TRUE); +} + +static gboolean file_setfiletime(FileHandle *filehandle, + const FILETIME *create_time G_GNUC_UNUSED, + const FILETIME *access_time, + const FILETIME *write_time) +{ + struct utimbuf utbuf; + struct stat statbuf; + guint64 access_ticks, write_ticks; + gint ret; + + + if(!(filehandle->fileaccess & (GENERIC_WRITE | GENERIC_ALL))) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d doesn't have GENERIC_WRITE access: %u", __func__, ((MonoFDHandle*) filehandle)->fd, filehandle->fileaccess); + + mono_w32error_set_last (ERROR_ACCESS_DENIED); + return(FALSE); + } + + if(filehandle->filename == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d unknown filename", __func__, ((MonoFDHandle*) filehandle)->fd); + + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return(FALSE); + } + + /* Get the current times, so we can put the same times back in + * the event that one of the FileTime structs is NULL + */ + MONO_ENTER_GC_SAFE; + ret=fstat (((MonoFDHandle*) filehandle)->fd, &statbuf); + MONO_EXIT_GC_SAFE; + if(ret==-1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d fstat failed: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(errno)); + + mono_w32error_set_last (ERROR_INVALID_PARAMETER); + return(FALSE); + } + + if(access_time!=NULL) { + access_ticks=((guint64)access_time->dwHighDateTime << 32) + + access_time->dwLowDateTime; + /* This is (time_t)0. We can actually go to INT_MIN, + * but this will do for now. + */ + if (access_ticks < 116444736000000000ULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: attempt to set access time too early", + __func__); + mono_w32error_set_last (ERROR_INVALID_PARAMETER); + return(FALSE); + } + + if (sizeof (utbuf.actime) == 4 && ((access_ticks - 116444736000000000ULL) / 10000000) > INT_MAX) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: attempt to set write time that is too big for a 32bits time_t", + __func__); + mono_w32error_set_last (ERROR_INVALID_PARAMETER); + return(FALSE); + } + + utbuf.actime=(access_ticks - 116444736000000000ULL) / 10000000; + } else { + utbuf.actime=statbuf.st_atime; + } + + if(write_time!=NULL) { + write_ticks=((guint64)write_time->dwHighDateTime << 32) + + write_time->dwLowDateTime; + /* This is (time_t)0. We can actually go to INT_MIN, + * but this will do for now. + */ + if (write_ticks < 116444736000000000ULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: attempt to set write time too early", + __func__); + mono_w32error_set_last (ERROR_INVALID_PARAMETER); + return(FALSE); + } + if (sizeof (utbuf.modtime) == 4 && ((write_ticks - 116444736000000000ULL) / 10000000) > INT_MAX) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: attempt to set write time that is too big for a 32bits time_t", + __func__); + mono_w32error_set_last (ERROR_INVALID_PARAMETER); + return(FALSE); + } + + utbuf.modtime=(write_ticks - 116444736000000000ULL) / 10000000; + } else { + utbuf.modtime=statbuf.st_mtime; + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: setting fd %d access %ld write %ld", __func__, + ((MonoFDHandle*) filehandle)->fd, utbuf.actime, utbuf.modtime); + + ret = _wapi_utime (filehandle->filename, &utbuf); + if (ret == -1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d [%s] utime failed: %s", __func__, ((MonoFDHandle*) filehandle)->fd, filehandle->filename, g_strerror(errno)); + + mono_w32error_set_last (ERROR_INVALID_PARAMETER); + return(FALSE); + } + + return(TRUE); +} + +static gboolean +console_read(FileHandle *filehandle, gpointer buffer, guint32 numbytes, guint32 *bytesread) +{ + gint ret; + MonoThreadInfo *info = mono_thread_info_current (); + + if(bytesread!=NULL) { + *bytesread=0; + } + + if(!(filehandle->fileaccess & (GENERIC_READ | GENERIC_ALL))) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d doesn't have GENERIC_READ access: %u", __func__, ((MonoFDHandle*) filehandle)->fd, filehandle->fileaccess); + + mono_w32error_set_last (ERROR_ACCESS_DENIED); + return(FALSE); + } + + do { + MONO_ENTER_GC_SAFE; + ret=read(((MonoFDHandle*) filehandle)->fd, buffer, numbytes); + MONO_EXIT_GC_SAFE; + } while (ret==-1 && errno==EINTR && !mono_thread_info_is_interrupt_state (info)); + + if(ret==-1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: read of fd %d error: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(errno)); + + _wapi_set_last_error_from_errno (); + return(FALSE); + } + + if(bytesread!=NULL) { + *bytesread=ret; + } + + return(TRUE); +} + +static gboolean +console_write(FileHandle *filehandle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten) +{ + gint ret; + MonoThreadInfo *info = mono_thread_info_current (); + + if(byteswritten!=NULL) { + *byteswritten=0; + } + + if(!(filehandle->fileaccess & (GENERIC_WRITE | GENERIC_ALL))) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d doesn't have GENERIC_WRITE access: %u", __func__, ((MonoFDHandle*) filehandle)->fd, filehandle->fileaccess); + + mono_w32error_set_last (ERROR_ACCESS_DENIED); + return(FALSE); + } + + do { + MONO_ENTER_GC_SAFE; + ret = write(((MonoFDHandle*) filehandle)->fd, buffer, numbytes); + MONO_EXIT_GC_SAFE; + } while (ret == -1 && errno == EINTR && + !mono_thread_info_is_interrupt_state (info)); + + if (ret == -1) { + if (errno == EINTR) { + ret = 0; + } else { + _wapi_set_last_error_from_errno (); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: write of fd %d error: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(errno)); + + return(FALSE); + } + } + if(byteswritten!=NULL) { + *byteswritten=ret; + } + + return(TRUE); +} + +static gboolean +pipe_read (FileHandle *filehandle, gpointer buffer, guint32 numbytes, guint32 *bytesread) +{ + gint ret; + MonoThreadInfo *info = mono_thread_info_current (); + + if(bytesread!=NULL) { + *bytesread=0; + } + + if(!(filehandle->fileaccess & (GENERIC_READ | GENERIC_ALL))) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d doesn't have GENERIC_READ access: %u", __func__, ((MonoFDHandle*) filehandle)->fd, filehandle->fileaccess); + + mono_w32error_set_last (ERROR_ACCESS_DENIED); + return(FALSE); + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: reading up to %" G_GUINT32_FORMAT " bytes from pipe %d", __func__, numbytes, ((MonoFDHandle*) filehandle)->fd); + + do { + MONO_ENTER_GC_SAFE; + ret=read(((MonoFDHandle*) filehandle)->fd, buffer, numbytes); + MONO_EXIT_GC_SAFE; + } while (ret==-1 && errno==EINTR && !mono_thread_info_is_interrupt_state (info)); + + if (ret == -1) { + if (errno == EINTR) { + ret = 0; + } else { + _wapi_set_last_error_from_errno (); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: read of fd %d error: %s", __func__,((MonoFDHandle*) filehandle)->fd, g_strerror(errno)); + + return(FALSE); + } + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: read %d bytes from pipe %d", __func__, ret, ((MonoFDHandle*) filehandle)->fd); + + if(bytesread!=NULL) { + *bytesread=ret; + } + + return(TRUE); +} + +static gboolean +pipe_write(FileHandle *filehandle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten) +{ + gint ret; + MonoThreadInfo *info = mono_thread_info_current (); + + if(byteswritten!=NULL) { + *byteswritten=0; + } + + if(!(filehandle->fileaccess & (GENERIC_WRITE | GENERIC_ALL))) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d doesn't have GENERIC_WRITE access: %u", __func__, ((MonoFDHandle*) filehandle)->fd, filehandle->fileaccess); + + mono_w32error_set_last (ERROR_ACCESS_DENIED); + return(FALSE); + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: writing up to %" G_GUINT32_FORMAT " bytes to pipe %d", __func__, numbytes, ((MonoFDHandle*) filehandle)->fd); + + do { + MONO_ENTER_GC_SAFE; + ret = write (((MonoFDHandle*) filehandle)->fd, buffer, numbytes); + MONO_EXIT_GC_SAFE; + } while (ret == -1 && errno == EINTR && + !mono_thread_info_is_interrupt_state (info)); + + if (ret == -1) { + if (errno == EINTR) { + ret = 0; + } else { + _wapi_set_last_error_from_errno (); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: write of fd %d error: %s", __func__,((MonoFDHandle*) filehandle)->fd, g_strerror(errno)); + + return(FALSE); + } + } + if(byteswritten!=NULL) { + *byteswritten=ret; + } + + return(TRUE); +} + +static gint convert_flags(guint32 fileaccess, guint32 createmode) +{ + gint flags=0; + + switch(fileaccess) { + case GENERIC_READ: + flags=O_RDONLY; + break; + case GENERIC_WRITE: + flags=O_WRONLY; + break; + case GENERIC_READ|GENERIC_WRITE: + flags=O_RDWR; + break; + default: + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Unknown access type 0x%" PRIx32, __func__, + fileaccess); + break; + } + + switch(createmode) { + case CREATE_NEW: + flags|=O_CREAT|O_EXCL; + break; + case CREATE_ALWAYS: + flags|=O_CREAT|O_TRUNC; + break; + case OPEN_EXISTING: + break; + case OPEN_ALWAYS: + flags|=O_CREAT; + break; + case TRUNCATE_EXISTING: + flags|=O_TRUNC; + break; + default: + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Unknown create mode 0x%" PRIx32, __func__, + createmode); + break; + } + + return(flags); +} + +#if 0 /* unused */ +static mode_t convert_perms(guint32 sharemode) +{ + mode_t perms=0600; + + if(sharemode&FILE_SHARE_READ) { + perms|=044; + } + if(sharemode&FILE_SHARE_WRITE) { + perms|=022; + } + + return(perms); +} +#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) +{ + gboolean file_already_shared; + guint32 file_existing_share, file_existing_access; + + file_already_shared = file_share_get (statbuf->st_dev, statbuf->st_ino, sharemode, fileaccess, &file_existing_share, &file_existing_access, share_info); + + 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. + */ + if (file_existing_share == 0) { + /* Quick and easy, no possibility to share */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Share mode prevents open: requested access: 0x%" PRIx32 ", file has sharing = NONE", __func__, fileaccess); + + file_share_release (*share_info); + *share_info = NULL; + + return(FALSE); + } + + if (((file_existing_share == FILE_SHARE_READ) && + (fileaccess != GENERIC_READ)) || + ((file_existing_share == FILE_SHARE_WRITE) && + (fileaccess != GENERIC_WRITE))) { + /* New access mode doesn't match up */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Share mode prevents open: requested access: 0x%" PRIx32 ", file has sharing: 0x%" PRIx32, __func__, fileaccess, file_existing_share); + + file_share_release (*share_info); + *share_info = NULL; + + return(FALSE); + } + + if (((file_existing_access & GENERIC_READ) && + !(sharemode & FILE_SHARE_READ)) || + ((file_existing_access & GENERIC_WRITE) && + !(sharemode & FILE_SHARE_WRITE))) { + /* New share mode doesn't match up */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Access mode prevents open: requested share: 0x%" PRIx32 ", file has access: 0x%" PRIx32, __func__, sharemode, file_existing_access); + + file_share_release (*share_info); + *share_info = NULL; + + return(FALSE); + } + } else { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: New file!", __func__); + } + + return(TRUE); +} + + +static gboolean +share_allows_delete (struct stat *statbuf, FileShare **share_info) +{ + gboolean file_already_shared; + guint32 file_existing_share, file_existing_access; + + file_already_shared = file_share_get (statbuf->st_dev, statbuf->st_ino, FILE_SHARE_DELETE, GENERIC_READ, &file_existing_share, &file_existing_access, share_info); + + if (file_already_shared) { + /* 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. + */ + if (file_existing_share == 0) { + /* Quick and easy, no possibility to share */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Share mode prevents open: requested access: 0x%" PRIx32 ", file has sharing = NONE", __func__, (*share_info)->access); + + file_share_release (*share_info); + *share_info = NULL; + + return(FALSE); + } + + if (!(file_existing_share & FILE_SHARE_DELETE)) { + /* New access mode doesn't match up */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Share mode prevents open: requested access: 0x%" PRIx32 ", file has sharing: 0x%" PRIx32, __func__, (*share_info)->access, file_existing_share); + + file_share_release (*share_info); + *share_info = NULL; + + return(FALSE); + } + } else { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: New file!", __func__); + } + + return(TRUE); +} + +gpointer +mono_w32file_create(const gunichar2 *name, guint32 fileaccess, guint32 sharemode, guint32 createmode, guint32 attrs) +{ + FileHandle *filehandle; + MonoFDType type; + gint flags=convert_flags(fileaccess, createmode); + /*mode_t perms=convert_perms(sharemode);*/ + /* we don't use sharemode, because that relates to sharing of + * the file when the file is open and is already handled by + * other code, perms instead are the on-disk permissions and + * this is a sane default. + */ + mode_t perms=0666; + gchar *filename; + gint fd, ret; + struct stat statbuf; + + if (attrs & FILE_ATTRIBUTE_TEMPORARY) + perms = 0600; + + if (attrs & FILE_ATTRIBUTE_ENCRYPTED){ + mono_w32error_set_last (ERROR_ENCRYPTION_FAILED); + return INVALID_HANDLE_VALUE; + } + + if (name == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__); + + mono_w32error_set_last (ERROR_INVALID_NAME); + return(INVALID_HANDLE_VALUE); + } + + filename = mono_unicode_to_external (name); + if (filename == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL", __func__); + + mono_w32error_set_last (ERROR_INVALID_NAME); + return(INVALID_HANDLE_VALUE); + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Opening %s with share 0x%" PRIx32 " and access 0x%" PRIx32, __func__, + filename, sharemode, fileaccess); + + fd = _wapi_open (filename, flags, perms); + + /* If we were trying to open a directory with write permissions + * (e.g. O_WRONLY or O_RDWR), this call will fail with + * EISDIR. However, this is a bit bogus because calls to + * manipulate the directory (e.g. mono_w32file_set_times) will still work on + * the directory because they use other API calls + * (e.g. utime()). Hence, if we failed with the EISDIR error, try + * to open the directory again without write permission. + */ + if (fd == -1 && errno == EISDIR) + { + /* Try again but don't try to make it writable */ + fd = _wapi_open (filename, flags & ~(O_RDWR|O_WRONLY), perms); + } + + if (fd == -1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Error opening file %s: %s", __func__, filename, g_strerror(errno)); + _wapi_set_last_path_error_from_errno (NULL, filename); + g_free (filename); + + return(INVALID_HANDLE_VALUE); + } + + MONO_ENTER_GC_SAFE; + ret = fstat (fd, &statbuf); + MONO_EXIT_GC_SAFE; + if (ret == -1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fstat error of file %s: %s", __func__, filename, g_strerror (errno)); + _wapi_set_last_error_from_errno (); + MONO_ENTER_GC_SAFE; + close (fd); + MONO_EXIT_GC_SAFE; + + return(INVALID_HANDLE_VALUE); + } + +#ifndef S_ISFIFO +#define S_ISFIFO(m) ((m & S_IFIFO) != 0) +#endif + if (S_ISFIFO (statbuf.st_mode)) { + type = MONO_FDTYPE_PIPE; + /* maintain invariant that pipes have no filename */ + g_free (filename); + filename = NULL; + } else if (S_ISCHR (statbuf.st_mode)) { + type = MONO_FDTYPE_CONSOLE; + } else { + type = MONO_FDTYPE_FILE; + } + + filehandle = file_data_create (type, fd); + filehandle->filename = filename; + filehandle->fileaccess = fileaccess; + filehandle->sharemode = sharemode; + filehandle->attrs = attrs; + + if (!share_allows_open (&statbuf, filehandle->sharemode, filehandle->fileaccess, &filehandle->share_info)) { + mono_w32error_set_last (ERROR_SHARING_VIOLATION); + MONO_ENTER_GC_SAFE; + close (((MonoFDHandle*) filehandle)->fd); + MONO_EXIT_GC_SAFE; + + mono_fdhandle_unref ((MonoFDHandle*) filehandle); + return (INVALID_HANDLE_VALUE); + } + if (!filehandle->share_info) { + /* No space, so no more files can be opened */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: No space in the share table", __func__); + + mono_w32error_set_last (ERROR_TOO_MANY_OPEN_FILES); + MONO_ENTER_GC_SAFE; + close (((MonoFDHandle*) filehandle)->fd); + MONO_EXIT_GC_SAFE; + + mono_fdhandle_unref ((MonoFDHandle*) filehandle); + return(INVALID_HANDLE_VALUE); + } + +#ifdef HAVE_POSIX_FADVISE + if (attrs & FILE_FLAG_SEQUENTIAL_SCAN) { + MONO_ENTER_GC_SAFE; + posix_fadvise (((MonoFDHandle*) filehandle)->fd, 0, 0, POSIX_FADV_SEQUENTIAL); + MONO_EXIT_GC_SAFE; + } + if (attrs & FILE_FLAG_RANDOM_ACCESS) { + MONO_ENTER_GC_SAFE; + posix_fadvise (((MonoFDHandle*) filehandle)->fd, 0, 0, POSIX_FADV_RANDOM); + MONO_EXIT_GC_SAFE; + } +#endif + +#ifdef F_RDAHEAD + if (attrs & FILE_FLAG_SEQUENTIAL_SCAN) { + MONO_ENTER_GC_SAFE; + fcntl(((MonoFDHandle*) filehandle)->fd, F_RDAHEAD, 1); + MONO_EXIT_GC_SAFE; + } +#endif + + mono_fdhandle_insert ((MonoFDHandle*) filehandle); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: returning handle %p", __func__, GINT_TO_POINTER(((MonoFDHandle*) filehandle)->fd)); + + return GINT_TO_POINTER(((MonoFDHandle*) filehandle)->fd); +} + +gboolean +mono_w32file_close (gpointer handle) +{ + if (!mono_fdhandle_close (GPOINTER_TO_INT (handle))) { + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return FALSE; + } + + return TRUE; +} + +gboolean mono_w32file_delete(const gunichar2 *name) +{ + gchar *filename; + gint retval; + gboolean ret = FALSE; +#if 0 + struct stat statbuf; + FileShare *shareinfo; +#endif + + if(name==NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__); + + mono_w32error_set_last (ERROR_INVALID_NAME); + return(FALSE); + } + + filename=mono_unicode_to_external(name); + if(filename==NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL", __func__); + + mono_w32error_set_last (ERROR_INVALID_NAME); + return(FALSE); + } + +#if 0 + /* Check to make sure sharing allows us to open the file for + * writing. See bug 323389. + * + * Do the checks that don't need an open file descriptor, for + * simplicity's sake. If we really have to do the full checks + * then we can implement that later. + */ + if (_wapi_stat (filename, &statbuf) < 0) { + _wapi_set_last_path_error_from_errno (NULL, filename); + g_free (filename); + return(FALSE); + } + + if (share_allows_open (&statbuf, 0, GENERIC_WRITE, + &shareinfo) == FALSE) { + mono_w32error_set_last (ERROR_SHARING_VIOLATION); + g_free (filename); + return FALSE; + } + if (shareinfo) + file_share_release (shareinfo); +#endif + + retval = _wapi_unlink (filename); + + if (retval == -1) { + /* On linux, calling unlink on an non-existing file in a read-only mount will fail with EROFS. + * The expected behavior is for this function to return FALSE and not trigger an exception. + * To work around this behavior, we stat the file on failure. + * + * This was supposedly fixed on kernel 3.0 [1] but we could reproduce it with Ubuntu 16.04 which has kernel 4.4. + * We can't remove this workaround until the early 2020's when most Android deviced will have a fix. + * [1] https://github.com/torvalds/linux/commit/50338b889dc504c69e0cb316ac92d1b9e51f3c8a + */ + if (errno == EROFS) { + MonoIOStat stat; + if (mono_w32file_get_attributes_ex (name, &stat)) //The file exists, so must be due the RO file system + errno = EROFS; + } + _wapi_set_last_path_error_from_errno (NULL, filename); + } else { + ret = TRUE; + } + + g_free(filename); + + return(ret); +} + +static gboolean +MoveFile (const gunichar2 *name, const gunichar2 *dest_name) +{ + gchar *utf8_name, *utf8_dest_name; + gint result, errno_copy; + struct stat stat_src, stat_dest; + gboolean ret = FALSE; + FileShare *shareinfo; + + if(name==NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__); + + mono_w32error_set_last (ERROR_INVALID_NAME); + return(FALSE); + } + + utf8_name = mono_unicode_to_external (name); + if (utf8_name == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL", __func__); + + mono_w32error_set_last (ERROR_INVALID_NAME); + return FALSE; + } + + if(dest_name==NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__); + + g_free (utf8_name); + mono_w32error_set_last (ERROR_INVALID_NAME); + return(FALSE); + } + + utf8_dest_name = mono_unicode_to_external (dest_name); + if (utf8_dest_name == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL", __func__); + + g_free (utf8_name); + mono_w32error_set_last (ERROR_INVALID_NAME); + return FALSE; + } + + /* + * In C# land we check for the existence of src, but not for dest. + * We check it here and return the failure if dest exists and is not + * the same file as src. + */ + if (_wapi_stat (utf8_name, &stat_src) < 0) { + if (errno != ENOENT || _wapi_lstat (utf8_name, &stat_src) < 0) { + _wapi_set_last_path_error_from_errno (NULL, utf8_name); + g_free (utf8_name); + g_free (utf8_dest_name); + return FALSE; + } + } + + if (!_wapi_stat (utf8_dest_name, &stat_dest)) { + if (stat_dest.st_dev != stat_src.st_dev || + stat_dest.st_ino != stat_src.st_ino) { + g_free (utf8_name); + g_free (utf8_dest_name); + mono_w32error_set_last (ERROR_ALREADY_EXISTS); + return FALSE; + } + } + + /* Check to make that we have delete sharing permission. + * See https://bugzilla.xamarin.com/show_bug.cgi?id=17009 + * + * Do the checks that don't need an open file descriptor, for + * simplicity's sake. If we really have to do the full checks + * then we can implement that later. + */ + if (share_allows_delete (&stat_src, &shareinfo) == FALSE) { + mono_w32error_set_last (ERROR_SHARING_VIOLATION); + return FALSE; + } + if (shareinfo) { + file_share_release (shareinfo); + shareinfo = NULL; + } + + result = _wapi_rename (utf8_name, utf8_dest_name); + errno_copy = errno; + + if (result == -1) { + switch(errno_copy) { + case EEXIST: + mono_w32error_set_last (ERROR_ALREADY_EXISTS); + break; + + case EXDEV: + /* Ignore here, it is dealt with below */ + break; + + case ENOENT: + /* We already know src exists. Must be dest that doesn't exist. */ + _wapi_set_last_path_error_from_errno (NULL, utf8_dest_name); + break; + + default: + _wapi_set_last_error_from_errno (); + } + } + + g_free (utf8_name); + g_free (utf8_dest_name); + + if (result != 0 && errno_copy == EXDEV) { + gint32 copy_error; + + if (S_ISDIR (stat_src.st_mode)) { + mono_w32error_set_last (ERROR_NOT_SAME_DEVICE); + return FALSE; + } + /* Try a copy to the new location, and delete the source */ + if (!mono_w32file_copy (name, dest_name, FALSE, ©_error)) { + /* mono_w32file_copy will set the error */ + return(FALSE); + } + + return(mono_w32file_delete (name)); + } + + if (result == 0) { + ret = TRUE; + } + + return(ret); +} + +static gboolean +write_file (gint src_fd, gint dest_fd, struct stat *st_src, gboolean report_errors) +{ + gint remain, n; + gchar *buf, *wbuf; + gint buf_size = st_src->st_blksize; + MonoThreadInfo *info = mono_thread_info_current (); + + buf_size = buf_size < 8192 ? 8192 : (buf_size > 65536 ? 65536 : buf_size); + buf = (gchar *) g_malloc (buf_size); + + for (;;) { + MONO_ENTER_GC_SAFE; + remain = read (src_fd, buf, buf_size); + MONO_EXIT_GC_SAFE; + if (remain < 0) { + if (errno == EINTR && !mono_thread_info_is_interrupt_state (info)) + continue; + + if (report_errors) + _wapi_set_last_error_from_errno (); + + g_free (buf); + return FALSE; + } + if (remain == 0) { + break; + } + + wbuf = buf; + while (remain > 0) { + MONO_ENTER_GC_SAFE; + n = write (dest_fd, wbuf, remain); + MONO_EXIT_GC_SAFE; + if (n < 0) { + if (errno == EINTR && !mono_thread_info_is_interrupt_state (info)) + continue; + + if (report_errors) + _wapi_set_last_error_from_errno (); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: write failed.", __func__); + g_free (buf); + return FALSE; + } + + remain -= n; + wbuf += n; + } + } + + g_free (buf); + return TRUE ; +} + +static gboolean +CopyFile (const gunichar2 *name, const gunichar2 *dest_name, gboolean fail_if_exists) +{ + gchar *utf8_src, *utf8_dest; + gint src_fd, dest_fd; + struct stat st, dest_st; + struct utimbuf dest_time; + gboolean ret = TRUE; + gint ret_utime; + gint syscall_res; + + if(name==NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__); + + mono_w32error_set_last (ERROR_INVALID_NAME); + return(FALSE); + } + + utf8_src = mono_unicode_to_external (name); + if (utf8_src == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion of source returned NULL", + __func__); + + mono_w32error_set_last (ERROR_INVALID_PARAMETER); + return(FALSE); + } + + if(dest_name==NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: dest is NULL", __func__); + + g_free (utf8_src); + mono_w32error_set_last (ERROR_INVALID_NAME); + return(FALSE); + } + + utf8_dest = mono_unicode_to_external (dest_name); + if (utf8_dest == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion of dest returned NULL", + __func__); + + mono_w32error_set_last (ERROR_INVALID_PARAMETER); + + g_free (utf8_src); + + return(FALSE); + } + + src_fd = _wapi_open (utf8_src, O_RDONLY, 0); + if (src_fd < 0) { + _wapi_set_last_path_error_from_errno (NULL, utf8_src); + + g_free (utf8_src); + g_free (utf8_dest); + + return(FALSE); + } + + MONO_ENTER_GC_SAFE; + syscall_res = fstat (src_fd, &st); + MONO_EXIT_GC_SAFE; + if (syscall_res < 0) { + _wapi_set_last_error_from_errno (); + + g_free (utf8_src); + g_free (utf8_dest); + MONO_ENTER_GC_SAFE; + close (src_fd); + MONO_EXIT_GC_SAFE; + + return(FALSE); + } + + /* Before trying to open/create the dest, we need to report a 'file busy' + * error if src and dest are actually the same file. We do the check here to take + * advantage of the IOMAP capability */ + if (!_wapi_stat (utf8_dest, &dest_st) && st.st_dev == dest_st.st_dev && + st.st_ino == dest_st.st_ino) { + + g_free (utf8_src); + g_free (utf8_dest); + MONO_ENTER_GC_SAFE; + close (src_fd); + MONO_EXIT_GC_SAFE; + + mono_w32error_set_last (ERROR_SHARING_VIOLATION); + return (FALSE); + } + + if (fail_if_exists) { + dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_CREAT | O_EXCL, st.st_mode); + } else { + /* FIXME: it kinda sucks that this code path potentially scans + * the directory twice due to the weird mono_w32error_set_last() + * behavior. */ + dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_TRUNC, st.st_mode); + if (dest_fd < 0) { + /* The file does not exist, try creating it */ + dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_CREAT | O_TRUNC, st.st_mode); + } else { + /* Apparently this error is set if we + * overwrite the dest file + */ + mono_w32error_set_last (ERROR_ALREADY_EXISTS); + } + } + if (dest_fd < 0) { + _wapi_set_last_error_from_errno (); + + g_free (utf8_src); + g_free (utf8_dest); + MONO_ENTER_GC_SAFE; + close (src_fd); + MONO_EXIT_GC_SAFE; + + return(FALSE); + } + + if (!write_file (src_fd, dest_fd, &st, TRUE)) + ret = FALSE; + + close (src_fd); + close (dest_fd); + + dest_time.modtime = st.st_mtime; + dest_time.actime = st.st_atime; + MONO_ENTER_GC_SAFE; + ret_utime = utime (utf8_dest, &dest_time); + MONO_EXIT_GC_SAFE; + if (ret_utime == -1) + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: file [%s] utime failed: %s", __func__, utf8_dest, g_strerror(errno)); + + g_free (utf8_src); + g_free (utf8_dest); + + return ret; +} + +static gchar* +convert_arg_to_utf8 (const gunichar2 *arg, const gchar *arg_name) +{ + gchar *utf8_ret; + + if (arg == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: %s is NULL", __func__, arg_name); + mono_w32error_set_last (ERROR_INVALID_NAME); + return NULL; + } + + utf8_ret = mono_unicode_to_external (arg); + if (utf8_ret == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion of %s returned NULL", + __func__, arg_name); + mono_w32error_set_last (ERROR_INVALID_PARAMETER); + return NULL; + } + + return utf8_ret; +} + +static gboolean +ReplaceFile (const gunichar2 *replacedFileName, const gunichar2 *replacementFileName, const gunichar2 *backupFileName, guint32 replaceFlags, gpointer exclude, gpointer reserved) +{ + gint result, backup_fd = -1,replaced_fd = -1; + gchar *utf8_replacedFileName, *utf8_replacementFileName = NULL, *utf8_backupFileName = NULL; + struct stat stBackup; + gboolean ret = FALSE; + + if (!(utf8_replacedFileName = convert_arg_to_utf8 (replacedFileName, "replacedFileName"))) + return FALSE; + if (!(utf8_replacementFileName = convert_arg_to_utf8 (replacementFileName, "replacementFileName"))) + goto replace_cleanup; + if (backupFileName != NULL) { + if (!(utf8_backupFileName = convert_arg_to_utf8 (backupFileName, "backupFileName"))) + goto replace_cleanup; + } + + if (utf8_backupFileName) { + // Open the backup file for read so we can restore the file if an error occurs. + backup_fd = _wapi_open (utf8_backupFileName, O_RDONLY, 0); + result = _wapi_rename (utf8_replacedFileName, utf8_backupFileName); + if (result == -1) + goto replace_cleanup; + } + + result = _wapi_rename (utf8_replacementFileName, utf8_replacedFileName); + if (result == -1) { + _wapi_set_last_path_error_from_errno (NULL, utf8_replacementFileName); + _wapi_rename (utf8_backupFileName, utf8_replacedFileName); + if (backup_fd != -1 && !fstat (backup_fd, &stBackup)) { + replaced_fd = _wapi_open (utf8_backupFileName, O_WRONLY | O_CREAT | O_TRUNC, + stBackup.st_mode); + + if (replaced_fd == -1) + goto replace_cleanup; + + write_file (backup_fd, replaced_fd, &stBackup, FALSE); + } + + goto replace_cleanup; + } + + ret = TRUE; + +replace_cleanup: + g_free (utf8_replacedFileName); + g_free (utf8_replacementFileName); + g_free (utf8_backupFileName); + if (backup_fd != -1) { + MONO_ENTER_GC_SAFE; + close (backup_fd); + MONO_EXIT_GC_SAFE; + } + if (replaced_fd != -1) { + MONO_ENTER_GC_SAFE; + close (replaced_fd); + MONO_EXIT_GC_SAFE; + } + return ret; +} + +static gpointer +_wapi_stdhandle_create (gint fd, const gchar *name) +{ + gint flags; + FileHandle *filehandle; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: creating standard handle type %s, fd %d", __func__, name, fd); + + /* Check if fd is valid */ + do { + flags = fcntl(fd, F_GETFL); + } while (flags == -1 && errno == EINTR); + + if (flags == -1) { + /* Invalid fd. Not really much point checking for EBADF + * specifically + */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fcntl error on fd %d: %s", __func__, fd, g_strerror(errno)); + + mono_w32error_set_last (mono_w32error_unix_to_win32 (errno)); + return INVALID_HANDLE_VALUE; + } + + filehandle = file_data_create (MONO_FDTYPE_CONSOLE, fd); + filehandle->filename = g_strdup(name); + + switch (flags & (O_RDONLY|O_WRONLY|O_RDWR)) { + case O_RDONLY: + filehandle->fileaccess = GENERIC_READ; + break; + case O_WRONLY: + filehandle->fileaccess = GENERIC_WRITE; + break; + case O_RDWR: + filehandle->fileaccess = GENERIC_READ | GENERIC_WRITE; + break; + default: + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Can't figure out flags 0x%x", __func__, flags); + filehandle->fileaccess = 0; + break; + } + + /* some default security attributes might be needed */ + filehandle->security_attributes = 0; + + /* Apparently input handles can't be written to. (I don't + * know if output or error handles can't be read from.) + */ + if (fd == 0) + filehandle->fileaccess &= ~GENERIC_WRITE; + + filehandle->sharemode = 0; + filehandle->attrs = 0; + + if (!mono_fdhandle_try_insert ((MonoFDHandle*) filehandle)) { + /* we raced between 2 invocations of _wapi_stdhandle_create */ + mono_fdhandle_unref ((MonoFDHandle*) filehandle); + return GINT_TO_POINTER(fd); + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: returning handle %p", __func__, GINT_TO_POINTER(((MonoFDHandle*) filehandle)->fd)); + + return GINT_TO_POINTER(((MonoFDHandle*) filehandle)->fd); +} + +enum { + STD_INPUT_HANDLE = -10, + STD_OUTPUT_HANDLE = -11, + STD_ERROR_HANDLE = -12, +}; + +static gpointer +mono_w32file_get_std_handle (gint stdhandle) +{ + FileHandle **filehandle; + gint fd; + const gchar *name; + + switch(stdhandle) { + case STD_INPUT_HANDLE: + fd = 0; + name = ""; + break; + + case STD_OUTPUT_HANDLE: + fd = 1; + name = ""; + break; + + case STD_ERROR_HANDLE: + fd = 2; + name = ""; + break; + + default: + g_assert_not_reached (); + } + + if (!mono_fdhandle_lookup_and_ref(fd, (MonoFDHandle**) &filehandle)) { + gpointer handle; + + handle = _wapi_stdhandle_create (fd, name); + if (handle == INVALID_HANDLE_VALUE) { + mono_w32error_set_last (ERROR_NO_MORE_FILES); + return INVALID_HANDLE_VALUE; + } + } + + return GINT_TO_POINTER (fd); +} + +gboolean +mono_w32file_read (gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread) +{ + FileHandle *filehandle; + gboolean ret; + + if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) { + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return FALSE; + } + + switch (((MonoFDHandle*) filehandle)->type) { + case MONO_FDTYPE_FILE: + ret = file_read(filehandle, buffer, numbytes, bytesread); + break; + case MONO_FDTYPE_CONSOLE: + ret = console_read(filehandle, buffer, numbytes, bytesread); + break; + case MONO_FDTYPE_PIPE: + ret = pipe_read(filehandle, buffer, numbytes, bytesread); + break; + default: + mono_w32error_set_last (ERROR_INVALID_HANDLE); + mono_fdhandle_unref ((MonoFDHandle*) filehandle); + return FALSE; + } + + mono_fdhandle_unref ((MonoFDHandle*) filehandle); + return ret; +} + +gboolean +mono_w32file_write (gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten) +{ + FileHandle *filehandle; + gboolean ret; + + if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) { + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return FALSE; + } + + switch (((MonoFDHandle*) filehandle)->type) { + case MONO_FDTYPE_FILE: + ret = file_write(filehandle, buffer, numbytes, byteswritten); + break; + case MONO_FDTYPE_CONSOLE: + ret = console_write(filehandle, buffer, numbytes, byteswritten); + break; + case MONO_FDTYPE_PIPE: + ret = pipe_write(filehandle, buffer, numbytes, byteswritten); + break; + default: + mono_w32error_set_last (ERROR_INVALID_HANDLE); + mono_fdhandle_unref ((MonoFDHandle*) filehandle); + return FALSE; + } + + mono_fdhandle_unref ((MonoFDHandle*) filehandle); + return ret; +} + +gboolean +mono_w32file_flush (gpointer handle) +{ + FileHandle *filehandle; + gboolean ret; + + if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) { + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return FALSE; + } + + switch (((MonoFDHandle*) filehandle)->type) { + case MONO_FDTYPE_FILE: + ret = file_flush(filehandle); + break; + default: + mono_w32error_set_last (ERROR_INVALID_HANDLE); + mono_fdhandle_unref ((MonoFDHandle*) filehandle); + return FALSE; + } + + mono_fdhandle_unref ((MonoFDHandle*) filehandle); + return ret; +} + +gboolean +mono_w32file_truncate (gpointer handle) +{ + FileHandle *filehandle; + gboolean ret; + + if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) { + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return FALSE; + } + + switch (((MonoFDHandle*) filehandle)->type) { + case MONO_FDTYPE_FILE: + ret = file_setendoffile(filehandle); + break; + default: + mono_w32error_set_last (ERROR_INVALID_HANDLE); + mono_fdhandle_unref ((MonoFDHandle*) filehandle); + return FALSE; + } + + mono_fdhandle_unref ((MonoFDHandle*) filehandle); + return ret; +} + +guint32 +mono_w32file_seek (gpointer handle, gint32 movedistance, gint32 *highmovedistance, guint32 method) +{ + FileHandle *filehandle; + guint32 ret; + + if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) { + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return INVALID_SET_FILE_POINTER; + } + + switch (((MonoFDHandle*) filehandle)->type) { + case MONO_FDTYPE_FILE: + ret = file_seek(filehandle, movedistance, highmovedistance, method); + break; + default: + mono_w32error_set_last (ERROR_INVALID_HANDLE); + mono_fdhandle_unref ((MonoFDHandle*) filehandle); + return INVALID_SET_FILE_POINTER; + } + + mono_fdhandle_unref ((MonoFDHandle*) filehandle); + return ret; +} + +gint +mono_w32file_get_type(gpointer handle) +{ + FileHandle *filehandle; + gint ret; + + if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) { + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return FILE_TYPE_UNKNOWN; + } + + switch (((MonoFDHandle*) filehandle)->type) { + case MONO_FDTYPE_FILE: + ret = FILE_TYPE_DISK; + break; + case MONO_FDTYPE_CONSOLE: + ret = FILE_TYPE_CHAR; + break; + case MONO_FDTYPE_PIPE: + ret = FILE_TYPE_PIPE; + break; + default: + mono_w32error_set_last (ERROR_INVALID_HANDLE); + mono_fdhandle_unref ((MonoFDHandle*) filehandle); + return FILE_TYPE_UNKNOWN; + } + + mono_fdhandle_unref ((MonoFDHandle*) filehandle); + return ret; +} + +static guint32 +GetFileSize(gpointer handle, guint32 *highsize) +{ + FileHandle *filehandle; + guint32 ret; + + if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) { + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return INVALID_FILE_SIZE; + } + + switch (((MonoFDHandle*) filehandle)->type) { + case MONO_FDTYPE_FILE: + ret = file_getfilesize(filehandle, highsize); + break; + default: + mono_w32error_set_last (ERROR_INVALID_HANDLE); + mono_fdhandle_unref ((MonoFDHandle*) filehandle); + return INVALID_FILE_SIZE; + } + + mono_fdhandle_unref ((MonoFDHandle*) filehandle); + return ret; +} + +gboolean +mono_w32file_get_times(gpointer handle, FILETIME *create_time, FILETIME *access_time, FILETIME *write_time) +{ + FileHandle *filehandle; + gboolean ret; + + if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) { + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return FALSE; + } + + switch (((MonoFDHandle*) filehandle)->type) { + case MONO_FDTYPE_FILE: + ret = file_getfiletime(filehandle, create_time, access_time, write_time); + break; + default: + mono_w32error_set_last (ERROR_INVALID_HANDLE); + mono_fdhandle_unref ((MonoFDHandle*) filehandle); + return FALSE; + } + + mono_fdhandle_unref ((MonoFDHandle*) filehandle); + return ret; +} + +gboolean +mono_w32file_set_times(gpointer handle, const FILETIME *create_time, const FILETIME *access_time, const FILETIME *write_time) +{ + FileHandle *filehandle; + gboolean ret; + + if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) { + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return FALSE; + } + + switch (((MonoFDHandle*) filehandle)->type) { + case MONO_FDTYPE_FILE: + ret = file_setfiletime(filehandle, create_time, access_time, write_time); + break; + default: + mono_w32error_set_last (ERROR_INVALID_HANDLE); + mono_fdhandle_unref ((MonoFDHandle*) filehandle); + return FALSE; + } + + mono_fdhandle_unref ((MonoFDHandle*) filehandle); + return ret; +} + +/* A tick is a 100-nanosecond interval. File time epoch is Midnight, + * January 1 1601 GMT + */ + +#define TICKS_PER_MILLISECOND 10000L +#define TICKS_PER_SECOND 10000000L +#define TICKS_PER_MINUTE 600000000L +#define TICKS_PER_HOUR 36000000000LL +#define TICKS_PER_DAY 864000000000LL + +#define isleap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0)) + +static const guint16 mon_yday[2][13]={ + {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, + {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}, +}; + +gboolean +mono_w32file_filetime_to_systemtime(const FILETIME *file_time, SYSTEMTIME *system_time) +{ + gint64 file_ticks, totaldays, rem, y; + const guint16 *ip; + + if(system_time==NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: system_time NULL", __func__); + + mono_w32error_set_last (ERROR_INVALID_PARAMETER); + return(FALSE); + } + + file_ticks=((gint64)file_time->dwHighDateTime << 32) + + file_time->dwLowDateTime; + + /* Really compares if file_ticks>=0x8000000000000000 + * (LLONG_MAX+1) but we're working with a signed value for the + * year and day calculation to work later + */ + if(file_ticks<0) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: file_time too big", __func__); + + mono_w32error_set_last (ERROR_INVALID_PARAMETER); + return(FALSE); + } + + totaldays=(file_ticks / TICKS_PER_DAY); + rem = file_ticks % TICKS_PER_DAY; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: totaldays: %" G_GINT64_FORMAT " rem: %" G_GINT64_FORMAT, __func__, + totaldays, rem); + + system_time->wHour=rem/TICKS_PER_HOUR; + rem %= TICKS_PER_HOUR; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Hour: %d rem: %" G_GINT64_FORMAT, __func__, + system_time->wHour, rem); + + system_time->wMinute = rem / TICKS_PER_MINUTE; + rem %= TICKS_PER_MINUTE; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Minute: %d rem: %" G_GINT64_FORMAT, __func__, + system_time->wMinute, rem); + + system_time->wSecond = rem / TICKS_PER_SECOND; + rem %= TICKS_PER_SECOND; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Second: %d rem: %" G_GINT64_FORMAT, __func__, + system_time->wSecond, rem); + + system_time->wMilliseconds = rem / TICKS_PER_MILLISECOND; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Milliseconds: %d", __func__, + system_time->wMilliseconds); + + /* January 1, 1601 was a Monday, according to Emacs calendar */ + system_time->wDayOfWeek = ((1 + totaldays) % 7) + 1; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Day of week: %d", __func__, system_time->wDayOfWeek); + + /* This algorithm to find year and month given days from epoch + * from glibc + */ + y=1601; + +#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0)) +#define LEAPS_THRU_END_OF(y) (DIV(y, 4) - DIV (y, 100) + DIV (y, 400)) + + while(totaldays < 0 || totaldays >= (isleap(y)?366:365)) { + /* Guess a corrected year, assuming 365 days per year */ + gint64 yg = y + totaldays / 365 - (totaldays % 365 < 0); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: totaldays: %" G_GINT64_FORMAT " yg: %" G_GINT64_FORMAT " y: %" G_GINT64_FORMAT, __func__, + totaldays, yg, y); + g_message("%s: LEAPS(yg): %li LEAPS(y): %li", __func__, + LEAPS_THRU_END_OF(yg-1), LEAPS_THRU_END_OF(y-1)); + + /* Adjust days and y to match the guessed year. */ + totaldays -= ((yg - y) * 365 + + LEAPS_THRU_END_OF (yg - 1) + - LEAPS_THRU_END_OF (y - 1)); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: totaldays: %" G_GINT64_FORMAT, + __func__, totaldays); + y = yg; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: y: %" G_GINT64_FORMAT, __func__, y); + } + + system_time->wYear = y; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Year: %d", __func__, system_time->wYear); + + ip = mon_yday[isleap(y)]; + + for(y=11; totaldays < ip[y]; --y) { + continue; + } + totaldays-=ip[y]; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: totaldays: %" G_GINT64_FORMAT, __func__, totaldays); + + system_time->wMonth = y + 1; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Month: %d", __func__, system_time->wMonth); + + system_time->wDay = totaldays + 1; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Day: %d", __func__, system_time->wDay); + + return(TRUE); +} + +static void +findhandle_destroy (gpointer data) +{ + FindHandle *findhandle; + + findhandle = (FindHandle*) data; + g_assert (findhandle); + + mono_coop_mutex_destroy (&findhandle->mutex); + + if (findhandle->namelist) + g_strfreev (findhandle->namelist); + if (findhandle->dir_part) + g_free (findhandle->dir_part); + + g_free (findhandle); +} + +static FindHandle* +findhandle_create (void) +{ + FindHandle* findhandle; + + findhandle = g_new0 (FindHandle, 1); + mono_refcount_init (findhandle, findhandle_destroy); + + mono_coop_mutex_init (&findhandle->mutex); + + return findhandle; +} + +static void +findhandle_insert (FindHandle *findhandle) +{ + mono_coop_mutex_lock (&finds_mutex); + + if (g_hash_table_lookup_extended (finds, (gpointer) findhandle, NULL, NULL)) + g_error("%s: duplicate Find handle %p", __func__, (gpointer) findhandle); + + g_hash_table_insert (finds, (gpointer) findhandle, findhandle); + + mono_coop_mutex_unlock (&finds_mutex); +} + +static gboolean +findhandle_lookup_and_ref (gpointer handle, FindHandle **findhandle) +{ + mono_coop_mutex_lock (&finds_mutex); + + if (!g_hash_table_lookup_extended (finds, handle, NULL, (gpointer*) findhandle)) { + mono_coop_mutex_unlock (&finds_mutex); + return FALSE; + } + + mono_refcount_inc (*findhandle); + + mono_coop_mutex_unlock (&finds_mutex); + + return TRUE; +} + +static void +findhandle_unref (FindHandle *findhandle) +{ + mono_refcount_dec (findhandle); +} + +static gboolean +findhandle_close (gpointer handle) +{ + FindHandle *findhandle; + gboolean removed; + + mono_coop_mutex_lock (&finds_mutex); + + if (!g_hash_table_lookup_extended (finds, handle, NULL, (gpointer*) &findhandle)) { + mono_coop_mutex_unlock (&finds_mutex); + + return FALSE; + } + + removed = g_hash_table_remove (finds, (gpointer) findhandle); + g_assert (removed); + + mono_coop_mutex_unlock (&finds_mutex); + + return TRUE; +} + +gpointer +mono_w32file_find_first (const gunichar2 *pattern, WIN32_FIND_DATA *find_data) +{ + FindHandle *findhandle; + gchar *utf8_pattern = NULL, *dir_part, *entry_part, **namelist; + gint result; + + if (pattern == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: pattern is NULL", __func__); + + mono_w32error_set_last (ERROR_PATH_NOT_FOUND); + return(INVALID_HANDLE_VALUE); + } + + utf8_pattern = mono_unicode_to_external (pattern); + if (utf8_pattern == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL", __func__); + + mono_w32error_set_last (ERROR_INVALID_NAME); + return(INVALID_HANDLE_VALUE); + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: looking for [%s]", __func__, utf8_pattern); + + /* Figure out which bit of the pattern is the directory */ + dir_part = _wapi_dirname (utf8_pattern); + entry_part = _wapi_basename (utf8_pattern); + +#if 0 + /* Don't do this check for now, it breaks if directories + * really do have metachars in their names (see bug 58116). + * FIXME: Figure out a better solution to keep some checks... + */ + if (strchr (dir_part, '*') || strchr (dir_part, '?')) { + mono_w32error_set_last (ERROR_INVALID_NAME); + g_free (dir_part); + g_free (entry_part); + g_free (utf8_pattern); + return(INVALID_HANDLE_VALUE); + } +#endif + + /* The pattern can specify a directory or a set of files. + * + * The pattern can have wildcard characters ? and *, but only + * in the section after the last directory delimiter. (Return + * ERROR_INVALID_NAME if there are wildcards in earlier path + * sections.) "*" has the usual 0-or-more chars meaning. "?" + * means "match one character", "??" seems to mean "match one + * or two characters", "???" seems to mean "match one, two or + * three characters", etc. Windows will also try and match + * the mangled "short name" of files, so 8 character patterns + * with wildcards will show some surprising results. + * + * All the written documentation I can find says that '?' + * should only match one character, and doesn't mention '??', + * '???' etc. I'm going to assume that the strict behaviour + * (ie '???' means three and only three characters) is the + * correct one, because that lets me use fnmatch(3) rather + * than mess around with regexes. + */ + + namelist = NULL; + result = _wapi_io_scandir (dir_part, entry_part, + &namelist); + + if (result == 0) { + /* No files, which windows seems to call + * FILE_NOT_FOUND + */ + mono_w32error_set_last (ERROR_FILE_NOT_FOUND); + g_free (utf8_pattern); + g_free (entry_part); + g_free (dir_part); + g_strfreev (namelist); + return (INVALID_HANDLE_VALUE); + } + + if (result < 0) { + _wapi_set_last_path_error_from_errno (dir_part, NULL); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: scandir error: %s", __func__, g_strerror (errno)); + g_free (utf8_pattern); + g_free (entry_part); + g_free (dir_part); + g_strfreev (namelist); + return (INVALID_HANDLE_VALUE); + } + + g_free (utf8_pattern); + g_free (entry_part); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Got %d matches", __func__, result); + + findhandle = findhandle_create (); + findhandle->namelist = namelist; + findhandle->dir_part = dir_part; + findhandle->num = result; + findhandle->count = 0; + + findhandle_insert (findhandle); + + if (!mono_w32file_find_next ((gpointer) findhandle, find_data)) { + mono_w32file_find_close ((gpointer) findhandle); + mono_w32error_set_last (ERROR_NO_MORE_FILES); + return INVALID_HANDLE_VALUE; + } + + return (gpointer) findhandle; +} + +gboolean +mono_w32file_find_next (gpointer handle, WIN32_FIND_DATA *find_data) +{ + FindHandle *findhandle; + struct stat buf, linkbuf; + gint result; + gchar *filename; + gchar *utf8_filename, *utf8_basename; + gunichar2 *utf16_basename; + time_t create_time; + glong bytes; + gboolean ret = FALSE; + + if (!findhandle_lookup_and_ref (handle, &findhandle)) { + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return FALSE; + } + + mono_coop_mutex_lock (&findhandle->mutex); + +retry: + if (findhandle->count >= findhandle->num) { + mono_w32error_set_last (ERROR_NO_MORE_FILES); + goto cleanup; + } + + /* stat next match */ + + filename = g_build_filename (findhandle->dir_part, findhandle->namelist[findhandle->count ++], NULL); + + result = _wapi_stat (filename, &buf); + if (result == -1 && errno == ENOENT) { + /* Might be a dangling symlink */ + result = _wapi_lstat (filename, &buf); + } + + if (result != 0) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: stat failed: %s", __func__, filename); + + g_free (filename); + goto retry; + } + + result = _wapi_lstat (filename, &linkbuf); + if (result != 0) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: lstat failed: %s", __func__, filename); + + g_free (filename); + goto retry; + } + + utf8_filename = mono_utf8_from_external (filename); + if (utf8_filename == NULL) { + /* We couldn't turn this filename into utf8 (eg the + * encoding of the name wasn't convertible), so just + * ignore it. + */ + g_warning ("%s: Bad encoding for '%s'\nConsider using MONO_EXTERNAL_ENCODINGS\n", __func__, filename); + + g_free (filename); + goto retry; + } + g_free (filename); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Found [%s]", __func__, utf8_filename); + + /* fill data block */ + + if (buf.st_mtime < buf.st_ctime) + create_time = buf.st_mtime; + else + create_time = buf.st_ctime; + + find_data->dwFileAttributes = _wapi_stat_to_file_attributes (utf8_filename, &buf, &linkbuf); + + time_t_to_filetime (create_time, &find_data->ftCreationTime); + time_t_to_filetime (buf.st_atime, &find_data->ftLastAccessTime); + time_t_to_filetime (buf.st_mtime, &find_data->ftLastWriteTime); + + if (find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + find_data->nFileSizeHigh = 0; + find_data->nFileSizeLow = 0; + } else { + find_data->nFileSizeHigh = buf.st_size >> 32; + find_data->nFileSizeLow = buf.st_size & 0xFFFFFFFF; + } + + find_data->dwReserved0 = 0; + find_data->dwReserved1 = 0; + + utf8_basename = _wapi_basename (utf8_filename); + utf16_basename = g_utf8_to_utf16 (utf8_basename, -1, NULL, &bytes, + NULL); + if(utf16_basename==NULL) { + g_free (utf8_basename); + g_free (utf8_filename); + goto retry; + } + ret = TRUE; + + /* utf16 is 2 * utf8 */ + bytes *= 2; + + memset (find_data->cFileName, '\0', (MAX_PATH*2)); + + /* Truncating a utf16 string like this might leave the last + * gchar incomplete + */ + memcpy (find_data->cFileName, utf16_basename, + bytes<(MAX_PATH*2)-2?bytes:(MAX_PATH*2)-2); + + find_data->cAlternateFileName [0] = 0; /* not used */ + + g_free (utf8_basename); + g_free (utf8_filename); + g_free (utf16_basename); + +cleanup: + mono_coop_mutex_unlock (&findhandle->mutex); + + findhandle_unref (findhandle); + + return(ret); +} + +gboolean +mono_w32file_find_close (gpointer handle) +{ + if (!findhandle_close (handle)) { + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return FALSE; + } + + return TRUE; +} + +gboolean +mono_w32file_create_directory (const gunichar2 *name) +{ + gchar *utf8_name; + gint result; + + if (name == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__); + + mono_w32error_set_last (ERROR_INVALID_NAME); + return(FALSE); + } + + utf8_name = mono_unicode_to_external (name); + if (utf8_name == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL", __func__); + + mono_w32error_set_last (ERROR_INVALID_NAME); + return FALSE; + } + + result = _wapi_mkdir (utf8_name, 0777); + + if (result == 0) { + g_free (utf8_name); + return TRUE; + } + + _wapi_set_last_path_error_from_errno (NULL, utf8_name); + g_free (utf8_name); + return FALSE; +} + +gboolean +mono_w32file_remove_directory (const gunichar2 *name) +{ + gchar *utf8_name; + gint result; + + if (name == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__); + + mono_w32error_set_last (ERROR_INVALID_NAME); + return(FALSE); + } + + utf8_name = mono_unicode_to_external (name); + if (utf8_name == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL", __func__); + + mono_w32error_set_last (ERROR_INVALID_NAME); + return FALSE; + } + + result = _wapi_rmdir (utf8_name); + if (result == -1) { + _wapi_set_last_path_error_from_errno (NULL, utf8_name); + g_free (utf8_name); + + return(FALSE); + } + g_free (utf8_name); + + return(TRUE); +} + +guint32 +mono_w32file_get_attributes (const gunichar2 *name) +{ + gchar *utf8_name; + struct stat buf, linkbuf; + gint result; + guint32 ret; + + if (name == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__); + + mono_w32error_set_last (ERROR_INVALID_NAME); + return(FALSE); + } + + utf8_name = mono_unicode_to_external (name); + if (utf8_name == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL", __func__); + + mono_w32error_set_last (ERROR_INVALID_PARAMETER); + return (INVALID_FILE_ATTRIBUTES); + } + + result = _wapi_stat (utf8_name, &buf); + if (result == -1 && (errno == ENOENT || errno == ELOOP)) { + /* Might be a dangling symlink... */ + result = _wapi_lstat (utf8_name, &buf); + } + + if (result != 0) { + _wapi_set_last_path_error_from_errno (NULL, utf8_name); + g_free (utf8_name); + return (INVALID_FILE_ATTRIBUTES); + } + + result = _wapi_lstat (utf8_name, &linkbuf); + if (result != 0) { + _wapi_set_last_path_error_from_errno (NULL, utf8_name); + g_free (utf8_name); + return (INVALID_FILE_ATTRIBUTES); + } + + ret = _wapi_stat_to_file_attributes (utf8_name, &buf, &linkbuf); + + g_free (utf8_name); + + return(ret); +} + +gboolean +mono_w32file_get_attributes_ex (const gunichar2 *name, MonoIOStat *stat) +{ + gchar *utf8_name; + + struct stat buf, linkbuf; + gint result; + + if (name == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__); + + mono_w32error_set_last (ERROR_INVALID_NAME); + return(FALSE); + } + + utf8_name = mono_unicode_to_external (name); + if (utf8_name == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL", __func__); + + mono_w32error_set_last (ERROR_INVALID_PARAMETER); + return FALSE; + } + + result = _wapi_stat (utf8_name, &buf); + if (result == -1 && errno == ENOENT) { + /* Might be a dangling symlink... */ + result = _wapi_lstat (utf8_name, &buf); + } + + if (result != 0) { + _wapi_set_last_path_error_from_errno (NULL, utf8_name); + g_free (utf8_name); + return FALSE; + } + + result = _wapi_lstat (utf8_name, &linkbuf); + if (result != 0) { + _wapi_set_last_path_error_from_errno (NULL, utf8_name); + g_free (utf8_name); + return(FALSE); + } + + /* fill stat block */ + + stat->attributes = _wapi_stat_to_file_attributes (utf8_name, &buf, &linkbuf); + stat->creation_time = (((guint64) (buf.st_mtime < buf.st_ctime ? buf.st_mtime : buf.st_ctime)) * 10 * 1000 * 1000) + 116444736000000000ULL; + stat->last_access_time = (((guint64) (buf.st_atime)) * 10 * 1000 * 1000) + 116444736000000000ULL; + stat->last_write_time = (((guint64) (buf.st_mtime)) * 10 * 1000 * 1000) + 116444736000000000ULL; + stat->length = (stat->attributes & FILE_ATTRIBUTE_DIRECTORY) ? 0 : buf.st_size; + + g_free (utf8_name); + return TRUE; +} + +gboolean +mono_w32file_set_attributes (const gunichar2 *name, guint32 attrs) +{ + /* FIXME: think of something clever to do on unix */ + gchar *utf8_name; + struct stat buf; + gint result; + + /* + * Currently we only handle one *internal* case, with a value that is + * not standard: 0x80000000, which means `set executable bit' + */ + + if (name == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__); + + mono_w32error_set_last (ERROR_INVALID_NAME); + return(FALSE); + } + + utf8_name = mono_unicode_to_external (name); + if (utf8_name == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL", __func__); + + mono_w32error_set_last (ERROR_INVALID_NAME); + return FALSE; + } + + result = _wapi_stat (utf8_name, &buf); + if (result == -1 && errno == ENOENT) { + /* Might be a dangling symlink... */ + result = _wapi_lstat (utf8_name, &buf); + } + + if (result != 0) { + _wapi_set_last_path_error_from_errno (NULL, utf8_name); + g_free (utf8_name); + return FALSE; + } + + /* Contrary to the documentation, ms allows NORMAL to be + * specified along with other attributes, so dont bother to + * catch that case here. + */ + if (attrs & FILE_ATTRIBUTE_READONLY) { + result = _wapi_chmod (utf8_name, buf.st_mode & ~(S_IWUSR | S_IWOTH | S_IWGRP)); + } else { + result = _wapi_chmod (utf8_name, buf.st_mode | S_IWUSR); + } + + /* Ignore the other attributes for now */ + + if (attrs & 0x80000000){ + mode_t exec_mask = 0; + + if ((buf.st_mode & S_IRUSR) != 0) + exec_mask |= S_IXUSR; + + if ((buf.st_mode & S_IRGRP) != 0) + exec_mask |= S_IXGRP; + + if ((buf.st_mode & S_IROTH) != 0) + exec_mask |= S_IXOTH; + + MONO_ENTER_GC_SAFE; + result = chmod (utf8_name, buf.st_mode | exec_mask); + MONO_EXIT_GC_SAFE; + } + /* Don't bother to reset executable (might need to change this + * policy) + */ + + g_free (utf8_name); + + return(TRUE); +} + +guint32 +mono_w32file_get_cwd (guint32 length, gunichar2 *buffer) +{ + gunichar2 *utf16_path; + glong count; + gsize bytes; + + if (getcwd ((gchar*)buffer, length) == NULL) { + if (errno == ERANGE) { /*buffer length is not big enough */ + gchar *path = g_get_current_dir (); /*FIXME g_get_current_dir doesn't work with broken paths and calling it just to know the path length is silly*/ + if (path == NULL) + return 0; + utf16_path = mono_unicode_from_external (path, &bytes); + g_free (utf16_path); + g_free (path); + return (bytes/2)+1; + } + _wapi_set_last_error_from_errno (); + return 0; + } + + utf16_path = mono_unicode_from_external ((gchar*)buffer, &bytes); + count = (bytes/2)+1; + g_assert (count <= length); /*getcwd must have failed before with ERANGE*/ + + /* Add the terminator */ + memset (buffer, '\0', bytes+2); + memcpy (buffer, utf16_path, bytes); + + g_free (utf16_path); + + return count; +} + +gboolean +mono_w32file_set_cwd (const gunichar2 *path) +{ + gchar *utf8_path; + gboolean result; + + if (path == NULL) { + mono_w32error_set_last (ERROR_INVALID_PARAMETER); + return(FALSE); + } + + utf8_path = mono_unicode_to_external (path); + if (_wapi_chdir (utf8_path) != 0) { + _wapi_set_last_error_from_errno (); + result = FALSE; + } + else + result = TRUE; + + g_free (utf8_path); + return result; +} + +gboolean +mono_w32file_create_pipe (gpointer *readpipe, gpointer *writepipe, guint32 size) +{ + FileHandle *read_filehandle, *write_filehandle; + gint filedes[2]; + gint ret; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Creating pipe", __func__); + + MONO_ENTER_GC_SAFE; + ret=pipe (filedes); + MONO_EXIT_GC_SAFE; + if (ret==-1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Error creating pipe: (%d) %s", + __func__, errno, g_strerror (errno)); + + _wapi_set_last_error_from_errno (); + return FALSE; + } + + /* filedes[0] is open for reading, filedes[1] for writing */ + + read_filehandle = file_data_create (MONO_FDTYPE_PIPE, filedes[0]); + read_filehandle->fileaccess = GENERIC_READ; + + write_filehandle = file_data_create (MONO_FDTYPE_PIPE, filedes[1]); + write_filehandle->fileaccess = GENERIC_WRITE; + + mono_fdhandle_insert ((MonoFDHandle*) read_filehandle); + mono_fdhandle_insert ((MonoFDHandle*) write_filehandle); + + *readpipe = GINT_TO_POINTER(((MonoFDHandle*) read_filehandle)->fd); + *writepipe = GINT_TO_POINTER(((MonoFDHandle*) write_filehandle)->fd); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Returning pipe: read handle %p, write handle %p", + __func__, GINT_TO_POINTER(((MonoFDHandle*) read_filehandle)->fd), GINT_TO_POINTER(((MonoFDHandle*) write_filehandle)->fd)); + + return(TRUE); +} + +#ifdef HAVE_GETFSSTAT +/* Darwin has getfsstat */ +gint32 +mono_w32file_get_logical_drive (guint32 len, gunichar2 *buf) +{ + struct statfs *stats; + gint size, n, i; + gunichar2 *dir; + glong length, total = 0; + gint syscall_res; + + MONO_ENTER_GC_SAFE; + n = getfsstat (NULL, 0, MNT_NOWAIT); + MONO_EXIT_GC_SAFE; + if (n == -1) + return 0; + size = n * sizeof (struct statfs); + stats = (struct statfs *) g_malloc (size); + if (stats == NULL) + return 0; + MONO_ENTER_GC_SAFE; + syscall_res = getfsstat (stats, size, MNT_NOWAIT); + MONO_EXIT_GC_SAFE; + if (syscall_res == -1){ + g_free (stats); + return 0; + } + for (i = 0; i < n; i++){ + dir = g_utf8_to_utf16 (stats [i].f_mntonname, -1, NULL, &length, NULL); + if (total + length < len){ + memcpy (buf + total, dir, sizeof (gunichar2) * length); + buf [total+length] = 0; + } + g_free (dir); + total += length + 1; + } + if (total < len) + buf [total] = 0; + total++; + g_free (stats); + return total; +} +#else +/* In-place octal sequence replacement */ +static void +unescape_octal (gchar *str) +{ + gchar *rptr; + gchar *wptr; + + if (str == NULL) + return; + + rptr = wptr = str; + while (*rptr != '\0') { + if (*rptr == '\\') { + gchar c; + rptr++; + c = (*(rptr++) - '0') << 6; + c += (*(rptr++) - '0') << 3; + c += *(rptr++) - '0'; + *wptr++ = c; + } else if (wptr != rptr) { + *wptr++ = *rptr++; + } else { + rptr++; wptr++; + } + } + *wptr = '\0'; +} +static gint32 GetLogicalDriveStrings_Mtab (guint32 len, gunichar2 *buf); + +#if __linux__ +#define GET_LOGICAL_DRIVE_STRINGS_BUFFER 512 +#define GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER 512 +#define GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER 64 + +typedef struct +{ + glong total; + guint32 buffer_index; + guint32 mountpoint_index; + guint32 field_number; + guint32 allocated_size; + guint32 fsname_index; + guint32 fstype_index; + gchar mountpoint [GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER + 1]; + gchar *mountpoint_allocated; + gchar buffer [GET_LOGICAL_DRIVE_STRINGS_BUFFER]; + gchar fsname [GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER + 1]; + gchar fstype [GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER + 1]; + ssize_t nbytes; + gchar delimiter; + gboolean check_mount_source; +} LinuxMountInfoParseState; + +static gboolean GetLogicalDriveStrings_Mounts (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state); +static gboolean GetLogicalDriveStrings_MountInfo (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state); +static void append_to_mountpoint (LinuxMountInfoParseState *state); +static gboolean add_drive_string (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state); + +gint32 +mono_w32file_get_logical_drive (guint32 len, gunichar2 *buf) +{ + gint fd; + gint32 ret = 0; + LinuxMountInfoParseState state; + gboolean (*parser)(guint32, gunichar2*, LinuxMountInfoParseState*) = NULL; + + memset (buf, 0, len * sizeof (gunichar2)); + MONO_ENTER_GC_SAFE; + fd = open ("/proc/self/mountinfo", O_RDONLY); + MONO_EXIT_GC_SAFE; + if (fd != -1) + parser = GetLogicalDriveStrings_MountInfo; + else { + MONO_ENTER_GC_SAFE; + fd = open ("/proc/mounts", O_RDONLY); + MONO_EXIT_GC_SAFE; + if (fd != -1) + parser = GetLogicalDriveStrings_Mounts; + } + + if (!parser) { + ret = GetLogicalDriveStrings_Mtab (len, buf); + goto done_and_out; + } + + memset (&state, 0, sizeof (LinuxMountInfoParseState)); + state.field_number = 1; + state.delimiter = ' '; + + while (1) { + MONO_ENTER_GC_SAFE; + state.nbytes = read (fd, state.buffer, GET_LOGICAL_DRIVE_STRINGS_BUFFER); + MONO_EXIT_GC_SAFE; + if (!(state.nbytes > 0)) + break; + state.buffer_index = 0; + + while ((*parser)(len, buf, &state)) { + if (state.buffer [state.buffer_index] == '\n') { + gboolean quit = add_drive_string (len, buf, &state); + state.field_number = 1; + state.buffer_index++; + if (state.mountpoint_allocated) { + g_free (state.mountpoint_allocated); + state.mountpoint_allocated = NULL; + } + if (quit) { + ret = state.total; + goto done_and_out; + } + } + } + }; + ret = state.total; + + done_and_out: + if (fd != -1) { + MONO_ENTER_GC_SAFE; + close (fd); + MONO_EXIT_GC_SAFE; + } + return ret; +} + +static gboolean GetLogicalDriveStrings_Mounts (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state) +{ + gchar *ptr; + + if (state->field_number == 1) + state->check_mount_source = TRUE; + + while (state->buffer_index < (guint32)state->nbytes) { + if (state->buffer [state->buffer_index] == state->delimiter) { + state->field_number++; + switch (state->field_number) { + case 2: + state->mountpoint_index = 0; + break; + + case 3: + if (state->mountpoint_allocated) + state->mountpoint_allocated [state->mountpoint_index] = 0; + else + state->mountpoint [state->mountpoint_index] = 0; + break; + + default: + ptr = (gchar*)memchr (state->buffer + state->buffer_index, '\n', GET_LOGICAL_DRIVE_STRINGS_BUFFER - state->buffer_index); + if (ptr) + state->buffer_index = (ptr - (gchar*)state->buffer) - 1; + else + state->buffer_index = state->nbytes; + return TRUE; + } + state->buffer_index++; + continue; + } else if (state->buffer [state->buffer_index] == '\n') + return TRUE; + + switch (state->field_number) { + case 1: + if (state->check_mount_source) { + if (state->fsname_index == 0 && state->buffer [state->buffer_index] == '/') { + /* We can ignore the rest, it's a device + * path */ + state->check_mount_source = FALSE; + state->fsname [state->fsname_index++] = '/'; + break; + } + if (state->fsname_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER) + state->fsname [state->fsname_index++] = state->buffer [state->buffer_index]; + } + break; + + case 2: + append_to_mountpoint (state); + break; + + case 3: + if (state->fstype_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER) + state->fstype [state->fstype_index++] = state->buffer [state->buffer_index]; + break; + } + + state->buffer_index++; + } + + return FALSE; +} + +static gboolean GetLogicalDriveStrings_MountInfo (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state) +{ + while (state->buffer_index < (guint32)state->nbytes) { + if (state->buffer [state->buffer_index] == state->delimiter) { + state->field_number++; + switch (state->field_number) { + case 5: + state->mountpoint_index = 0; + break; + + case 6: + if (state->mountpoint_allocated) + state->mountpoint_allocated [state->mountpoint_index] = 0; + else + state->mountpoint [state->mountpoint_index] = 0; + break; + + case 7: + state->delimiter = '-'; + break; + + case 8: + state->delimiter = ' '; + break; + + case 10: + state->check_mount_source = TRUE; + break; + } + state->buffer_index++; + continue; + } else if (state->buffer [state->buffer_index] == '\n') + return TRUE; + + switch (state->field_number) { + case 5: + append_to_mountpoint (state); + break; + + case 9: + if (state->fstype_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER) + state->fstype [state->fstype_index++] = state->buffer [state->buffer_index]; + break; + + case 10: + if (state->check_mount_source) { + if (state->fsname_index == 0 && state->buffer [state->buffer_index] == '/') { + /* We can ignore the rest, it's a device + * path */ + state->check_mount_source = FALSE; + state->fsname [state->fsname_index++] = '/'; + break; + } + if (state->fsname_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER) + state->fsname [state->fsname_index++] = state->buffer [state->buffer_index]; + } + break; + } + + state->buffer_index++; + } + + return FALSE; +} + +static void +append_to_mountpoint (LinuxMountInfoParseState *state) +{ + gchar ch = state->buffer [state->buffer_index]; + if (state->mountpoint_allocated) { + if (state->mountpoint_index >= state->allocated_size) { + guint32 newsize = (state->allocated_size << 1) + 1; + gchar *newbuf = (gchar *)g_malloc0 (newsize * sizeof (gchar)); + + memcpy (newbuf, state->mountpoint_allocated, state->mountpoint_index); + g_free (state->mountpoint_allocated); + state->mountpoint_allocated = newbuf; + state->allocated_size = newsize; + } + state->mountpoint_allocated [state->mountpoint_index++] = ch; + } else { + if (state->mountpoint_index >= GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER) { + state->allocated_size = (state->mountpoint_index << 1) + 1; + state->mountpoint_allocated = (gchar *)g_malloc0 (state->allocated_size * sizeof (gchar)); + memcpy (state->mountpoint_allocated, state->mountpoint, state->mountpoint_index); + state->mountpoint_allocated [state->mountpoint_index++] = ch; + } else + state->mountpoint [state->mountpoint_index++] = ch; + } +} + +static gboolean +add_drive_string (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state) +{ + gboolean quit = FALSE; + gboolean ignore_entry; + + if (state->fsname_index == 1 && state->fsname [0] == '/') + ignore_entry = FALSE; + else if (memcmp ("overlay", state->fsname, state->fsname_index) == 0 || + memcmp ("aufs", state->fstype, state->fstype_index) == 0) { + /* Don't ignore overlayfs and aufs - these might be used on Docker + * (https://bugzilla.xamarin.com/show_bug.cgi?id=31021) */ + ignore_entry = FALSE; + } else if (state->fsname_index == 0 || memcmp ("none", state->fsname, state->fsname_index) == 0) { + ignore_entry = TRUE; + } else if (state->fstype_index >= 5 && memcmp ("fuse.", state->fstype, 5) == 0) { + /* Ignore GNOME's gvfs */ + if (state->fstype_index == 21 && memcmp ("fuse.gvfs-fuse-daemon", state->fstype, state->fstype_index) == 0) + ignore_entry = TRUE; + else + ignore_entry = FALSE; + } else if (state->fstype_index == 3 && memcmp ("nfs", state->fstype, state->fstype_index) == 0) + ignore_entry = FALSE; + else + ignore_entry = TRUE; + + if (!ignore_entry) { + gunichar2 *dir; + glong length; + gchar *mountpoint = state->mountpoint_allocated ? state->mountpoint_allocated : state->mountpoint; + + unescape_octal (mountpoint); + dir = g_utf8_to_utf16 (mountpoint, -1, NULL, &length, NULL); + if (state->total + length + 1 > len) { + quit = TRUE; + state->total = len * 2; + } else { + length++; + memcpy (buf + state->total, dir, sizeof (gunichar2) * length); + state->total += length; + } + g_free (dir); + } + state->fsname_index = 0; + state->fstype_index = 0; + + return quit; +} +#else +gint32 +mono_w32file_get_logical_drive (guint32 len, gunichar2 *buf) +{ + return GetLogicalDriveStrings_Mtab (len, buf); +} +#endif +static gint32 +GetLogicalDriveStrings_Mtab (guint32 len, gunichar2 *buf) +{ + FILE *fp; + gunichar2 *ptr, *dir; + glong length, total = 0; + gchar buffer [512]; + gchar **splitted; + + memset (buf, 0, sizeof (gunichar2) * (len + 1)); + buf [0] = '/'; + buf [1] = 0; + buf [2] = 0; + + /* Sigh, mntent and friends don't work well. + * It stops on the first line that doesn't begin with a '/'. + * (linux 2.6.5, libc 2.3.2.ds1-12) - Gonz */ + MONO_ENTER_GC_SAFE; + fp = fopen ("/etc/mtab", "rt"); + MONO_EXIT_GC_SAFE; + if (fp == NULL) { + MONO_ENTER_GC_SAFE; + fp = fopen ("/etc/mnttab", "rt"); + MONO_EXIT_GC_SAFE; + if (fp == NULL) + return 1; + } + + ptr = buf; + while (1) { + gchar *fgets_res; + MONO_ENTER_GC_SAFE; + fgets_res = fgets (buffer, 512, fp); + MONO_EXIT_GC_SAFE; + if (!fgets_res) + break; + if (*buffer != '/') + continue; + + splitted = g_strsplit (buffer, " ", 0); + if (!*splitted || !*(splitted + 1)) { + g_strfreev (splitted); + continue; + } + + unescape_octal (*(splitted + 1)); + dir = g_utf8_to_utf16 (*(splitted + 1), -1, NULL, &length, NULL); + g_strfreev (splitted); + if (total + length + 1 > len) { + MONO_ENTER_GC_SAFE; + fclose (fp); + MONO_EXIT_GC_SAFE; + g_free (dir); + return len * 2; /* guess */ + } + + memcpy (ptr + total, dir, sizeof (gunichar2) * length); + g_free (dir); + total += length + 1; + } + + MONO_ENTER_GC_SAFE; + fclose (fp); + MONO_EXIT_GC_SAFE; + return total; +/* Commented out, does not work with my mtab!!! - Gonz */ +#ifdef NOTENABLED /* HAVE_MNTENT_H */ +{ + FILE *fp; + struct mntent *mnt; + gunichar2 *ptr, *dir; + glong len, total = 0; + + + MONO_ENTER_GC_SAFE; + fp = setmntent ("/etc/mtab", "rt"); + MONO_EXIT_GC_SAFE; + if (fp == NULL) { + MONO_ENTER_GC_SAFE; + fp = setmntent ("/etc/mnttab", "rt"); + MONO_EXIT_GC_SAFE; + if (fp == NULL) + return; + } + + ptr = buf; + while (1) { + MONO_ENTER_GC_SAFE; + mnt = getmntent (fp); + MONO_EXIT_GC_SAFE; + if (mnt == NULL) + break; + g_print ("GOT %s\n", mnt->mnt_dir); + dir = g_utf8_to_utf16 (mnt->mnt_dir, &len, NULL, NULL, NULL); + if (total + len + 1 > len) { + MONO_ENTER_GC_SAFE; + endmntent (fp); + MONO_EXIT_GC_SAFE; + return len * 2; /* guess */ + } + + memcpy (ptr + total, dir, sizeof (gunichar2) * len); + g_free (dir); + total += len + 1; + } + + MONO_ENTER_GC_SAFE; + endmntent (fp); + MONO_EXIT_GC_SAFE; + return total; +} +#endif +} +#endif + +#if defined(HAVE_STATVFS) || defined(HAVE_STATFS) +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) +{ +#ifdef HAVE_STATVFS + struct statvfs fsstat; +#elif defined(HAVE_STATFS) + struct statfs fsstat; +#endif + gchar *utf8_path_name; + gint ret; + unsigned long block_size; + + if (path_name == NULL) { + utf8_path_name = g_strdup (g_get_current_dir()); + if (utf8_path_name == NULL) { + mono_w32error_set_last (ERROR_DIRECTORY); + return(FALSE); + } + } + else { + utf8_path_name = mono_unicode_to_external (path_name); + if (utf8_path_name == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL", __func__); + + mono_w32error_set_last (ERROR_INVALID_NAME); + return(FALSE); + } + } + + do { +#ifdef HAVE_STATVFS + MONO_ENTER_GC_SAFE; + ret = statvfs (utf8_path_name, &fsstat); + MONO_EXIT_GC_SAFE; + block_size = fsstat.f_frsize; +#elif defined(HAVE_STATFS) + MONO_ENTER_GC_SAFE; + ret = statfs (utf8_path_name, &fsstat); + MONO_EXIT_GC_SAFE; + block_size = fsstat.f_bsize; +#endif + } while(ret == -1 && errno == EINTR); + + g_free(utf8_path_name); + + if (ret == -1) { + _wapi_set_last_error_from_errno (); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: statvfs failed: %s", __func__, g_strerror (errno)); + return(FALSE); + } + + /* total number of free bytes for non-root */ + + if (free_bytes_avail != NULL) { + *free_bytes_avail = block_size * (guint64)fsstat.f_bavail; + } + + /* total number of bytes available for non-root */ + if (total_number_of_bytes != NULL) { + *total_number_of_bytes = block_size * (guint64)fsstat.f_blocks; + } + + /* total number of bytes available for root */ + + if (total_number_of_free_bytes != NULL) { + *total_number_of_free_bytes = block_size * (guint64)fsstat.f_bfree; + } + + return(TRUE); +} +#else +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) +{ + if (free_bytes_avail != NULL) { + *free_bytes_avail = (guint64) -1; + } + + if (total_number_of_bytes != NULL) { + *total_number_of_bytes = (guint64) -1; + } + + if (total_number_of_free_bytes != NULL) { + *total_number_of_free_bytes = (guint64) -1; + } + + return(TRUE); +} +#endif + +/* + * General Unix support + */ +typedef struct { + guint32 drive_type; +#if __linux__ + const long fstypeid; +#endif + const gchar* fstype; +} _wapi_drive_type; + +static _wapi_drive_type _wapi_drive_types[] = { +#if HOST_DARWIN + { DRIVE_REMOTE, "afp" }, + { DRIVE_REMOTE, "autofs" }, + { DRIVE_CDROM, "cddafs" }, + { DRIVE_CDROM, "cd9660" }, + { DRIVE_RAMDISK, "devfs" }, + { DRIVE_FIXED, "exfat" }, + { DRIVE_RAMDISK, "fdesc" }, + { DRIVE_REMOTE, "ftp" }, + { DRIVE_FIXED, "hfs" }, + { DRIVE_FIXED, "apfs" }, + { DRIVE_REMOTE, "kbfuse" }, + { DRIVE_FIXED, "msdos" }, + { DRIVE_REMOTE, "nfs" }, + { DRIVE_FIXED, "ntfs" }, + { DRIVE_REMOTE, "smbfs" }, + { DRIVE_FIXED, "udf" }, + { DRIVE_REMOTE, "webdav" }, + { DRIVE_UNKNOWN, NULL } +#elif __linux__ + { DRIVE_FIXED, ADFS_SUPER_MAGIC, "adfs"}, + { DRIVE_FIXED, AFFS_SUPER_MAGIC, "affs"}, + { DRIVE_REMOTE, AFS_SUPER_MAGIC, "afs"}, + { DRIVE_RAMDISK, AUTOFS_SUPER_MAGIC, "autofs"}, + { DRIVE_RAMDISK, AUTOFS_SBI_MAGIC, "autofs4"}, + { DRIVE_REMOTE, CODA_SUPER_MAGIC, "coda" }, + { DRIVE_RAMDISK, CRAMFS_MAGIC, "cramfs"}, + { DRIVE_RAMDISK, CRAMFS_MAGIC_WEND, "cramfs"}, + { DRIVE_REMOTE, CIFS_MAGIC_NUMBER, "cifs"}, + { DRIVE_RAMDISK, DEBUGFS_MAGIC, "debugfs"}, + { DRIVE_RAMDISK, SYSFS_MAGIC, "sysfs"}, + { DRIVE_RAMDISK, SECURITYFS_MAGIC, "securityfs"}, + { DRIVE_RAMDISK, SELINUX_MAGIC, "selinuxfs"}, + { DRIVE_RAMDISK, RAMFS_MAGIC, "ramfs"}, + { DRIVE_FIXED, SQUASHFS_MAGIC, "squashfs"}, + { DRIVE_FIXED, EFS_SUPER_MAGIC, "efs"}, + { DRIVE_FIXED, EXT2_SUPER_MAGIC, "ext"}, + { DRIVE_FIXED, EXT3_SUPER_MAGIC, "ext"}, + { DRIVE_FIXED, EXT4_SUPER_MAGIC, "ext"}, + { DRIVE_REMOTE, XENFS_SUPER_MAGIC, "xenfs"}, + { DRIVE_FIXED, BTRFS_SUPER_MAGIC, "btrfs"}, + { DRIVE_FIXED, HFS_SUPER_MAGIC, "hfs"}, + { DRIVE_FIXED, HFSPLUS_SUPER_MAGIC, "hfsplus"}, + { DRIVE_FIXED, HPFS_SUPER_MAGIC, "hpfs"}, + { DRIVE_RAMDISK, HUGETLBFS_MAGIC, "hugetlbfs"}, + { DRIVE_CDROM, ISOFS_SUPER_MAGIC, "iso"}, + { DRIVE_FIXED, JFFS2_SUPER_MAGIC, "jffs2"}, + { DRIVE_RAMDISK, ANON_INODE_FS_MAGIC, "anon_inode"}, + { DRIVE_FIXED, JFS_SUPER_MAGIC, "jfs"}, + { DRIVE_FIXED, MINIX_SUPER_MAGIC, "minix"}, + { DRIVE_FIXED, MINIX_SUPER_MAGIC2, "minix v2"}, + { DRIVE_FIXED, MINIX2_SUPER_MAGIC, "minix2"}, + { DRIVE_FIXED, MINIX2_SUPER_MAGIC2, "minix2 v2"}, + { DRIVE_FIXED, MINIX3_SUPER_MAGIC, "minix3"}, + { DRIVE_FIXED, MSDOS_SUPER_MAGIC, "msdos"}, + { DRIVE_REMOTE, NCP_SUPER_MAGIC, "ncp"}, + { DRIVE_REMOTE, NFS_SUPER_MAGIC, "nfs"}, + { DRIVE_FIXED, NTFS_SB_MAGIC, "ntfs"}, + { DRIVE_RAMDISK, OPENPROM_SUPER_MAGIC, "openpromfs"}, + { DRIVE_RAMDISK, PROC_SUPER_MAGIC, "proc"}, + { DRIVE_FIXED, QNX4_SUPER_MAGIC, "qnx4"}, + { DRIVE_FIXED, REISERFS_SUPER_MAGIC, "reiserfs"}, + { DRIVE_RAMDISK, ROMFS_MAGIC, "romfs"}, + { DRIVE_REMOTE, SMB_SUPER_MAGIC, "samba"}, + { DRIVE_RAMDISK, CGROUP_SUPER_MAGIC, "cgroupfs"}, + { DRIVE_RAMDISK, FUTEXFS_SUPER_MAGIC, "futexfs"}, + { DRIVE_FIXED, SYSV2_SUPER_MAGIC, "sysv2"}, + { DRIVE_FIXED, SYSV4_SUPER_MAGIC, "sysv4"}, + { DRIVE_RAMDISK, TMPFS_MAGIC, "tmpfs"}, + { DRIVE_RAMDISK, DEVPTS_SUPER_MAGIC, "devpts"}, + { DRIVE_CDROM, UDF_SUPER_MAGIC, "udf"}, + { DRIVE_FIXED, UFS_MAGIC, "ufs"}, + { DRIVE_FIXED, UFS_MAGIC_BW, "ufs"}, + { DRIVE_FIXED, UFS2_MAGIC, "ufs2"}, + { DRIVE_FIXED, UFS_CIGAM, "ufs"}, + { DRIVE_RAMDISK, USBDEVICE_SUPER_MAGIC, "usbdev"}, + { DRIVE_FIXED, XENIX_SUPER_MAGIC, "xenix"}, + { DRIVE_FIXED, XFS_SB_MAGIC, "xfs"}, + { DRIVE_RAMDISK, FUSE_SUPER_MAGIC, "fuse"}, + { DRIVE_FIXED, V9FS_MAGIC, "9p"}, + { DRIVE_REMOTE, CEPH_SUPER_MAGIC, "ceph"}, + { DRIVE_RAMDISK, CONFIGFS_MAGIC, "configfs"}, + { DRIVE_RAMDISK, ECRYPTFS_SUPER_MAGIC, "eCryptfs"}, + { DRIVE_FIXED, EXOFS_SUPER_MAGIC, "exofs"}, + { DRIVE_FIXED, VXFS_SUPER_MAGIC, "vxfs"}, + { DRIVE_FIXED, VXFS_OLT_MAGIC, "vxfs_olt"}, + { DRIVE_REMOTE, GFS2_MAGIC, "gfs2"}, + { DRIVE_FIXED, LOGFS_MAGIC_U32, "logfs"}, + { DRIVE_FIXED, OCFS2_SUPER_MAGIC, "ocfs2"}, + { DRIVE_FIXED, OMFS_MAGIC, "omfs"}, + { DRIVE_FIXED, UBIFS_SUPER_MAGIC, "ubifs"}, + { DRIVE_UNKNOWN, 0, NULL} +#else + { DRIVE_RAMDISK, "ramfs" }, + { DRIVE_RAMDISK, "tmpfs" }, + { DRIVE_RAMDISK, "proc" }, + { DRIVE_RAMDISK, "sysfs" }, + { DRIVE_RAMDISK, "debugfs" }, + { DRIVE_RAMDISK, "devpts" }, + { DRIVE_RAMDISK, "securityfs" }, + { DRIVE_CDROM, "iso9660" }, + { DRIVE_FIXED, "ext2" }, + { DRIVE_FIXED, "ext3" }, + { DRIVE_FIXED, "ext4" }, + { DRIVE_FIXED, "sysv" }, + { DRIVE_FIXED, "reiserfs" }, + { DRIVE_FIXED, "ufs" }, + { DRIVE_FIXED, "vfat" }, + { DRIVE_FIXED, "msdos" }, + { DRIVE_FIXED, "udf" }, + { DRIVE_FIXED, "hfs" }, + { DRIVE_FIXED, "hpfs" }, + { DRIVE_FIXED, "qnx4" }, + { DRIVE_FIXED, "ntfs" }, + { DRIVE_FIXED, "ntfs-3g" }, + { DRIVE_REMOTE, "smbfs" }, + { DRIVE_REMOTE, "fuse" }, + { DRIVE_REMOTE, "nfs" }, + { DRIVE_REMOTE, "nfs4" }, + { DRIVE_REMOTE, "cifs" }, + { DRIVE_REMOTE, "ncpfs" }, + { DRIVE_REMOTE, "coda" }, + { DRIVE_REMOTE, "afs" }, + { DRIVE_UNKNOWN, NULL } +#endif +}; + +#if __linux__ +static guint32 _wapi_get_drive_type(long f_type) +{ + _wapi_drive_type *current; + + current = &_wapi_drive_types[0]; + while (current->drive_type != DRIVE_UNKNOWN) { + if (current->fstypeid == f_type) + return current->drive_type; + current++; + } + + return DRIVE_UNKNOWN; +} +#else +static guint32 _wapi_get_drive_type(const gchar* fstype) +{ + _wapi_drive_type *current; + + current = &_wapi_drive_types[0]; + while (current->drive_type != DRIVE_UNKNOWN) { + if (strcmp (current->fstype, fstype) == 0) + break; + + current++; + } + + return current->drive_type; +} +#endif + +#if defined (HOST_DARWIN) || defined (__linux__) +static guint32 +GetDriveTypeFromPath (const gchar *utf8_root_path_name) +{ + struct statfs buf; + gint res; + + MONO_ENTER_GC_SAFE; + res = statfs (utf8_root_path_name, &buf); + MONO_EXIT_GC_SAFE; + if (res == -1) + return DRIVE_UNKNOWN; +#if HOST_DARWIN + return _wapi_get_drive_type (buf.f_fstypename); +#else + return _wapi_get_drive_type (buf.f_type); +#endif +} +#else +static guint32 +GetDriveTypeFromPath (const gchar *utf8_root_path_name) +{ + guint32 drive_type; + FILE *fp; + gchar buffer [512]; + gchar **splitted; + + MONO_ENTER_GC_SAFE; + fp = fopen ("/etc/mtab", "rt"); + MONO_EXIT_GC_SAFE; + if (fp == NULL) { + MONO_ENTER_GC_SAFE; + fp = fopen ("/etc/mnttab", "rt"); + MONO_EXIT_GC_SAFE; + if (fp == NULL) + return(DRIVE_UNKNOWN); + } + + drive_type = DRIVE_NO_ROOT_DIR; + while (1) { + gchar *fgets_res; + MONO_ENTER_GC_SAFE; + fgets_res = fgets (buffer, 512, fp); + MONO_EXIT_GC_SAFE; + if (fgets_res == NULL) + break; + splitted = g_strsplit (buffer, " ", 0); + if (!*splitted || !*(splitted + 1) || !*(splitted + 2)) { + g_strfreev (splitted); + continue; + } + + /* compare given root_path_name with the one from mtab, + if length of utf8_root_path_name is zero it must be the root dir */ + if (strcmp (*(splitted + 1), utf8_root_path_name) == 0 || + (strcmp (*(splitted + 1), "/") == 0 && strlen (utf8_root_path_name) == 0)) { + drive_type = _wapi_get_drive_type (*(splitted + 2)); + /* it is possible this path might be mounted again with + a known type...keep looking */ + if (drive_type != DRIVE_UNKNOWN) { + g_strfreev (splitted); + break; + } + } + + g_strfreev (splitted); + } + + MONO_ENTER_GC_SAFE; + fclose (fp); + MONO_EXIT_GC_SAFE; + return drive_type; +} +#endif + +guint32 +mono_w32file_get_drive_type(const gunichar2 *root_path_name) +{ + gchar *utf8_root_path_name; + guint32 drive_type; + + if (root_path_name == NULL) { + utf8_root_path_name = g_strdup (g_get_current_dir()); + if (utf8_root_path_name == NULL) { + return(DRIVE_NO_ROOT_DIR); + } + } + else { + utf8_root_path_name = mono_unicode_to_external (root_path_name); + if (utf8_root_path_name == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL", __func__); + return(DRIVE_NO_ROOT_DIR); + } + + /* strip trailing slash for compare below */ + if (g_str_has_suffix(utf8_root_path_name, "/") && utf8_root_path_name [1] != 0) { + utf8_root_path_name[strlen(utf8_root_path_name) - 1] = 0; + } + } + drive_type = GetDriveTypeFromPath (utf8_root_path_name); + g_free (utf8_root_path_name); + + return (drive_type); +} + +#if defined (HOST_DARWIN) || defined (__linux__) || defined(HOST_BSD) || defined(__FreeBSD_kernel__) || defined(__HAIKU__) +static gchar* +get_fstypename (gchar *utfpath) +{ +#if defined (HOST_DARWIN) || defined (__linux__) + struct statfs stat; +#if __linux__ + _wapi_drive_type *current; +#endif + gint statfs_res; + MONO_ENTER_GC_SAFE; + statfs_res = statfs (utfpath, &stat); + MONO_EXIT_GC_SAFE; + if (statfs_res == -1) + return NULL; +#if HOST_DARWIN + return g_strdup (stat.f_fstypename); +#else + current = &_wapi_drive_types[0]; + while (current->drive_type != DRIVE_UNKNOWN) { + if (stat.f_type == current->fstypeid) + return g_strdup (current->fstype); + current++; + } + return NULL; +#endif +#else + return NULL; +#endif +} + +/* Linux has struct statfs which has a different layout */ +gboolean +mono_w32file_get_volume_information (const gunichar2 *path, gunichar2 *volumename, gint volumesize, gint *outserial, gint *maxcomp, gint *fsflags, gunichar2 *fsbuffer, gint fsbuffersize) +{ + gchar *utfpath; + gchar *fstypename; + gboolean status = FALSE; + glong len; + + // We only support getting the file system type + if (fsbuffer == NULL) + return 0; + + utfpath = mono_unicode_to_external (path); + if ((fstypename = get_fstypename (utfpath)) != NULL){ + gunichar2 *ret = g_utf8_to_utf16 (fstypename, -1, NULL, &len, NULL); + if (ret != NULL && len < fsbuffersize){ + memcpy (fsbuffer, ret, len * sizeof (gunichar2)); + fsbuffer [len] = 0; + status = TRUE; + } + if (ret != NULL) + g_free (ret); + g_free (fstypename); + } + g_free (utfpath); + return status; +} +#endif + +static gboolean +LockFile (gpointer handle, guint32 offset_low, guint32 offset_high, guint32 length_low, guint32 length_high) +{ + FileHandle *filehandle; + gboolean ret; + off_t offset, length; + + if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) { + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return FALSE; + } + + if (((MonoFDHandle*) filehandle)->type != MONO_FDTYPE_FILE) { + mono_w32error_set_last (ERROR_INVALID_HANDLE); + mono_fdhandle_unref ((MonoFDHandle*) filehandle); + return FALSE; + } + + if (!(filehandle->fileaccess & (GENERIC_READ | GENERIC_WRITE | GENERIC_ALL))) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, ((MonoFDHandle*) filehandle)->fd, filehandle->fileaccess); + mono_w32error_set_last (ERROR_ACCESS_DENIED); + mono_fdhandle_unref ((MonoFDHandle*) filehandle); + return FALSE; + } + +#ifdef HAVE_LARGE_FILE_SUPPORT + offset = ((gint64)offset_high << 32) | offset_low; + length = ((gint64)length_high << 32) | length_low; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Locking fd %d, offset %" G_GINT64_FORMAT ", length %" G_GINT64_FORMAT, __func__, ((MonoFDHandle*) filehandle)->fd, (gint64) offset, (gint64) length); +#else + if (offset_high > 0 || length_high > 0) { + mono_w32error_set_last (ERROR_INVALID_PARAMETER); + mono_fdhandle_unref ((MonoFDHandle*) filehandle); + return FALSE; + } + + offset = offset_low; + length = length_low; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Locking fd %d, offset %" G_GINT64_FORMAT ", length %" G_GINT64_FORMAT, __func__, ((MonoFDHandle*) filehandle)->fd, (gint64) offset, (gint64) length); +#endif + + ret = _wapi_lock_file_region (((MonoFDHandle*) filehandle)->fd, offset, length); + + mono_fdhandle_unref ((MonoFDHandle*) filehandle); + return ret; +} + +static gboolean +UnlockFile (gpointer handle, guint32 offset_low, guint32 offset_high, guint32 length_low, guint32 length_high) +{ + FileHandle *filehandle; + gboolean ret; + off_t offset, length; + + if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) { + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return FALSE; + } + + if (((MonoFDHandle*) filehandle)->type != MONO_FDTYPE_FILE) { + mono_w32error_set_last (ERROR_INVALID_HANDLE); + mono_fdhandle_unref ((MonoFDHandle*) filehandle); + return FALSE; + } + + if (!(filehandle->fileaccess & (GENERIC_READ | GENERIC_WRITE | GENERIC_ALL))) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, ((MonoFDHandle*) filehandle)->fd, filehandle->fileaccess); + mono_w32error_set_last (ERROR_ACCESS_DENIED); + mono_fdhandle_unref ((MonoFDHandle*) filehandle); + return FALSE; + } + +#ifdef HAVE_LARGE_FILE_SUPPORT + offset = ((gint64)offset_high << 32) | offset_low; + length = ((gint64)length_high << 32) | length_low; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Unlocking fd %d, offset %" G_GINT64_FORMAT ", length %" G_GINT64_FORMAT, __func__, ((MonoFDHandle*) filehandle)->fd, (gint64) offset, (gint64) length); +#else + offset = offset_low; + length = length_low; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Unlocking fd %p, offset %" G_GINT64_FORMAT ", length %" G_GINT64_FORMAT, __func__, ((MonoFDHandle*) filehandle)->fd, (gint64) offset, (gint64) length); +#endif + + ret = _wapi_unlock_file_region (((MonoFDHandle*) filehandle)->fd, offset, length); + + mono_fdhandle_unref ((MonoFDHandle*) filehandle); + return ret; +} + +void +mono_w32file_init (void) +{ + MonoFDHandleCallback file_data_callbacks = { + .close = file_data_close, + .destroy = file_data_destroy + }; + + mono_fdhandle_register (MONO_FDTYPE_FILE, &file_data_callbacks); + mono_fdhandle_register (MONO_FDTYPE_CONSOLE, &file_data_callbacks); + mono_fdhandle_register (MONO_FDTYPE_PIPE, &file_data_callbacks); + + mono_coop_mutex_init (&file_share_mutex); + + finds = g_hash_table_new (g_direct_hash, g_direct_equal); + mono_coop_mutex_init (&finds_mutex); + + if (g_hasenv ("MONO_STRICT_IO_EMULATION")) + lock_while_writing = TRUE; +} + +void +mono_w32file_cleanup (void) +{ + mono_coop_mutex_destroy (&file_share_mutex); + + if (file_share_table) + g_hash_table_destroy (file_share_table); + + g_hash_table_destroy (finds); + mono_coop_mutex_destroy (&finds_mutex); +} + +gboolean +mono_w32file_move (const gunichar2 *path, const gunichar2 *dest, gint32 *error) +{ + gboolean result; + + result = MoveFile (path, dest); + if (!result) + *error = mono_w32error_get_last (); + return result; +} + +gboolean +mono_w32file_copy (const gunichar2 *path, const gunichar2 *dest, gboolean overwrite, gint32 *error) +{ + gboolean result; + + result = CopyFile (path, dest, !overwrite); + if (!result) + *error = mono_w32error_get_last (); + + 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; + + result = ReplaceFile (destination_file_name, source_file_name, destination_backup_file_name, flags, NULL, NULL); + if (!result) + *error = mono_w32error_get_last (); + return result; +} + +gint64 +mono_w32file_get_file_size (gpointer handle, gint32 *error) +{ + gint64 length; + guint32 length_hi; + + length = GetFileSize (handle, &length_hi); + if(length==INVALID_FILE_SIZE) { + *error=mono_w32error_get_last (); + } + + return length | ((gint64)length_hi << 32); +} + +gboolean +mono_w32file_lock (gpointer handle, gint64 position, gint64 length, gint32 *error) +{ + gboolean result; + + result = LockFile (handle, position & 0xFFFFFFFF, position >> 32, length & 0xFFFFFFFF, length >> 32); + if (!result) + *error = mono_w32error_get_last (); + return result; +} + +gboolean +mono_w32file_unlock (gpointer handle, gint64 position, gint64 length, gint32 *error) +{ + gboolean result; + + result = UnlockFile (handle, position & 0xFFFFFFFF, position >> 32, length & 0xFFFFFFFF, length >> 32); + if (!result) + *error = mono_w32error_get_last (); + return result; +} + +gpointer +mono_w32file_get_console_input (void) +{ + return mono_w32file_get_std_handle (STD_INPUT_HANDLE); +} + +gpointer +mono_w32file_get_console_output (void) +{ + return mono_w32file_get_std_handle (STD_OUTPUT_HANDLE); +} + +gpointer +mono_w32file_get_console_error (void) +{ + return mono_w32file_get_std_handle (STD_ERROR_HANDLE); +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32file-win32-internals.h b/unity-2019.4.24f1-mbe/mono/metadata/w32file-win32-internals.h new file mode 100644 index 000000000..a60b4fa4f --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32file-win32-internals.h @@ -0,0 +1,16 @@ +/** + * \file + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#ifndef _MONO_METADATA_W32FILE_WIN32_INTERNALS_H_ +#define _MONO_METADATA_W32FILE_WIN32_INTERNALS_H_ + +#include +#include + +#ifdef HOST_WIN32 +#include "mono/metadata/w32file.h" +#include "mono/metadata/w32file-internals.h" +#endif /* HOST_WIN32 */ +#endif /* _MONO_METADATA_W32FILE_WIN32_INTERNALS_H_ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32file-win32-uwp.c b/unity-2019.4.24f1-mbe/mono/metadata/w32file-win32-uwp.c new file mode 100644 index 000000000..e703deabe --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32file-win32-uwp.c @@ -0,0 +1,168 @@ +/** + * \file + * UWP w32file support for Mono. + * + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. +*/ +#include +#include +#include "mono/utils/mono-compiler.h" + +#if G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) +#include +#include "mono/metadata/w32file-win32-internals.h" + +gboolean +mono_w32file_move (const gunichar2 *path, const gunichar2 *dest, gint32 *error) +{ + gboolean result = FALSE; + MONO_ENTER_GC_SAFE; + + result = MoveFileEx (path, dest, MOVEFILE_COPY_ALLOWED); + if (result == FALSE) { + *error=GetLastError (); + } + + MONO_EXIT_GC_SAFE; + return result; +} + +gboolean +mono_w32file_replace (const gunichar2 *destinationFileName, const gunichar2 *sourceFileName, + const gunichar2 *destinationBackupFileName, guint32 flags, gint32 *error) +{ + gboolean result = FALSE; + MONO_ENTER_GC_SAFE; + + result = ReplaceFile (destinationFileName, sourceFileName, destinationBackupFileName, flags, NULL, NULL); + if (result == FALSE) { + *error=GetLastError (); + } + + MONO_EXIT_GC_SAFE; + return result; +} + +gboolean +mono_w32file_copy (const gunichar2 *path, const gunichar2 *dest, gboolean overwrite, gint32 *error) +{ + gboolean result = FALSE; + COPYFILE2_EXTENDED_PARAMETERS copy_param = {0}; + + copy_param.dwSize = sizeof (COPYFILE2_EXTENDED_PARAMETERS); + copy_param.dwCopyFlags = (!overwrite) ? COPY_FILE_FAIL_IF_EXISTS : 0; + + MONO_ENTER_GC_SAFE; + + result = SUCCEEDED (CopyFile2 (path, dest, ©_param)); + if (result == FALSE) { + *error=GetLastError (); + } + + MONO_EXIT_GC_SAFE; + return result; +} + +gint64 +mono_w32file_get_file_size (HANDLE handle, gint32 *error) +{ + LARGE_INTEGER length; + + MONO_ENTER_GC_SAFE; + + if (!GetFileSizeEx (handle, &length)) { + *error=GetLastError (); + length.QuadPart = INVALID_FILE_SIZE; + } + + MONO_EXIT_GC_SAFE; + return length.QuadPart; +} + +gboolean +mono_w32file_lock (HANDLE handle, gint64 position, gint64 length, gint32 *error) +{ + gboolean result = FALSE; + MONO_ENTER_GC_SAFE; + + result = LockFile (handle, position & 0xFFFFFFFF, position >> 32, + length & 0xFFFFFFFF, length >> 32); + + if (result == FALSE) { + *error = GetLastError (); + } + + MONO_EXIT_GC_SAFE; + return result; +} + +gboolean +mono_w32file_unlock (HANDLE handle, gint64 position, gint64 length, gint32 *error) +{ + gboolean result = FALSE; + MONO_ENTER_GC_SAFE; + + result = UnlockFile (handle, position & 0xFFFFFFFF, position >> 32, + length & 0xFFFFFFFF, length >> 32); + + if (result == FALSE) { + *error = GetLastError (); + } + + MONO_EXIT_GC_SAFE; + return result; +} + +HANDLE +mono_w32file_get_console_output (void) +{ + MonoError mono_error; + error_init (&mono_error); + + g_unsupported_api ("GetStdHandle (STD_OUTPUT_HANDLE)"); + + mono_error_set_not_supported (&mono_error, G_UNSUPPORTED_API, "GetStdHandle (STD_OUTPUT_HANDLE)"); + mono_error_set_pending_exception (&mono_error); + + SetLastError (ERROR_NOT_SUPPORTED); + + return INVALID_HANDLE_VALUE; +} + +HANDLE +mono_w32file_get_console_input (void) +{ + MonoError mono_error; + error_init (&mono_error); + + g_unsupported_api ("GetStdHandle (STD_INPUT_HANDLE)"); + + mono_error_set_not_supported (&mono_error, G_UNSUPPORTED_API, "GetStdHandle (STD_INPUT_HANDLE)"); + mono_error_set_pending_exception (&mono_error); + + SetLastError (ERROR_NOT_SUPPORTED); + + return INVALID_HANDLE_VALUE; +} + +HANDLE +mono_w32file_get_console_error (void) +{ + MonoError mono_error; + error_init (&mono_error); + + g_unsupported_api ("GetStdHandle (STD_ERROR_HANDLE)"); + + mono_error_set_not_supported (&mono_error, G_UNSUPPORTED_API, "GetStdHandle (STD_ERROR_HANDLE)"); + mono_error_set_pending_exception (&mono_error); + + SetLastError (ERROR_NOT_SUPPORTED); + + return INVALID_HANDLE_VALUE; +} + +#else /* G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) */ + +MONO_EMPTY_SOURCE_FILE (file_io_windows_uwp); +#endif /* G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32file-win32.c b/unity-2019.4.24f1-mbe/mono/metadata/w32file-win32.c new file mode 100644 index 000000000..249dc5ace --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32file-win32.c @@ -0,0 +1,575 @@ +/** + * \file + * Windows File IO internal calls. + * + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#include +#include + +#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) +{ +} + +void +mono_w32file_cleanup (void) +{ +} + +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; +} + +gpointer +mono_w32file_create(const gunichar2 *name, guint32 fileaccess, guint32 sharemode, guint32 createmode, guint32 attrs) +{ + gpointer res; + MONO_ENTER_GC_SAFE; + res = CreateFile (name, fileaccess, sharemode, NULL, createmode, attrs, NULL); + MONO_EXIT_GC_SAFE; + return res; +} + +gboolean +mono_w32file_close (gpointer handle) +{ + gboolean res; + MONO_ENTER_GC_SAFE; + res = CloseHandle (handle); + MONO_EXIT_GC_SAFE; + return res; +} + +gboolean +mono_w32file_delete (const gunichar2 *name) +{ + gboolean res; + MONO_ENTER_GC_SAFE; + res = DeleteFile (name); + MONO_EXIT_GC_SAFE; + 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) +{ + + 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)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 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)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; +} + +gboolean +mono_w32file_flush (gpointer handle) +{ + gboolean res; + MONO_ENTER_GC_SAFE; + res = FlushFileBuffers (handle); + MONO_EXIT_GC_SAFE; + return res; +} + +gboolean +mono_w32file_truncate (gpointer handle) +{ + gboolean res; + MONO_ENTER_GC_SAFE; + res = SetEndOfFile (handle); + MONO_EXIT_GC_SAFE; + return res; +} + +guint32 +mono_w32file_seek (gpointer handle, gint32 movedistance, gint32 *highmovedistance, guint32 method) +{ + guint32 res; + MONO_ENTER_GC_SAFE; + res = SetFilePointer (handle, movedistance, highmovedistance, method); + MONO_EXIT_GC_SAFE; + return res; +} + +gint +mono_w32file_get_type (gpointer handle) +{ + gint res; + MONO_ENTER_GC_SAFE; + res = GetFileType (handle); + MONO_EXIT_GC_SAFE; + return res; +} + +gboolean +mono_w32file_get_times (gpointer handle, FILETIME *create_time, FILETIME *access_time, FILETIME *write_time) +{ + gboolean res; + MONO_ENTER_GC_SAFE; + res = GetFileTime (handle, create_time, access_time, write_time); + MONO_EXIT_GC_SAFE; + return res; +} + +gboolean +mono_w32file_set_times (gpointer handle, const FILETIME *create_time, const FILETIME *access_time, const FILETIME *write_time) +{ + gboolean res; + MONO_ENTER_GC_SAFE; + res = SetFileTime (handle, create_time, access_time, write_time); + MONO_EXIT_GC_SAFE; + return res; +} + +gboolean +mono_w32file_filetime_to_systemtime (const FILETIME *file_time, SYSTEMTIME *system_time) +{ + gboolean res; + MONO_ENTER_GC_SAFE; + res = FileTimeToSystemTime (file_time, system_time); + MONO_EXIT_GC_SAFE; + return res; +} + +gpointer +mono_w32file_find_first (const gunichar2 *pattern, WIN32_FIND_DATA *find_data) +{ + gpointer res; + MONO_ENTER_GC_SAFE; + res = FindFirstFile (pattern, find_data); + MONO_EXIT_GC_SAFE; + return res; +} + +gboolean +mono_w32file_find_next (gpointer handle, WIN32_FIND_DATA *find_data) +{ + gboolean res; + MONO_ENTER_GC_SAFE; + res = FindNextFile (handle, find_data); + MONO_EXIT_GC_SAFE; + return res; +} + +gboolean +mono_w32file_find_close (gpointer handle) +{ + gboolean res; + MONO_ENTER_GC_SAFE; + res = FindClose (handle); + MONO_EXIT_GC_SAFE; + return res; +} + +gboolean +mono_w32file_create_directory (const gunichar2 *name) +{ + gboolean res; + MONO_ENTER_GC_SAFE; + res = CreateDirectory (name, NULL); + MONO_EXIT_GC_SAFE; + return res; +} + +gboolean +mono_w32file_remove_directory (const gunichar2 *name) +{ + gboolean res; + MONO_ENTER_GC_SAFE; + res = RemoveDirectory (name); + MONO_EXIT_GC_SAFE; + return res; +} + +/* + * GetFileAttributes|Ex () seems to try opening the file, which might lead to sharing violation errors, whereas + * FindFirstFile always succeeds. + */ +guint32 +mono_w32file_get_attributes (const gunichar2 *name) +{ + guint32 res; + guint32 error; + HANDLE find_handle; + WIN32_FIND_DATA find_data; + + MONO_ENTER_GC_SAFE; + res = GetFileAttributes (name); + MONO_EXIT_GC_SAFE; + + if (res != INVALID_FILE_ATTRIBUTES) + return res; + + error = GetLastError (); + if (error != ERROR_SHARING_VIOLATION) + return INVALID_FILE_ATTRIBUTES; + + MONO_ENTER_GC_SAFE; + find_handle = FindFirstFile (name, &find_data); + MONO_EXIT_GC_SAFE; + + if (find_handle == INVALID_HANDLE_VALUE) + return INVALID_FILE_ATTRIBUTES; + + MONO_ENTER_GC_SAFE; + FindClose (find_handle); + MONO_EXIT_GC_SAFE; + + return find_data.dwFileAttributes; +} + +static gint64 +convert_filetime (const FILETIME *filetime) +{ + return (gint64) ((((guint64) filetime->dwHighDateTime) << 32) + filetime->dwLowDateTime); +} + +gboolean +mono_w32file_get_attributes_ex (const gunichar2 *name, MonoIOStat *stat) +{ + gboolean res; + guint32 error; + HANDLE find_handle; + WIN32_FIND_DATA find_data; + WIN32_FILE_ATTRIBUTE_DATA file_attribute_data; + + MONO_ENTER_GC_SAFE; + res = GetFileAttributesEx (name, GetFileExInfoStandard, &file_attribute_data); + MONO_EXIT_GC_SAFE; + if (res) { + stat->attributes = file_attribute_data.dwFileAttributes; + stat->creation_time = convert_filetime (&file_attribute_data.ftCreationTime); + stat->last_access_time = convert_filetime (&file_attribute_data.ftLastAccessTime); + stat->last_write_time = convert_filetime (&file_attribute_data.ftLastWriteTime); + stat->length = ((gint64)file_attribute_data.nFileSizeHigh << 32) | file_attribute_data.nFileSizeLow; + return TRUE; + } + + error = GetLastError (); + if (error != ERROR_SHARING_VIOLATION) + return FALSE; + + MONO_ENTER_GC_SAFE; + find_handle = FindFirstFile (name, &find_data); + MONO_EXIT_GC_SAFE; + + if (find_handle == INVALID_HANDLE_VALUE) + return FALSE; + + MONO_ENTER_GC_SAFE; + FindClose (find_handle); + MONO_EXIT_GC_SAFE; + + stat->attributes = find_data.dwFileAttributes; + stat->creation_time = convert_filetime (&find_data.ftCreationTime); + stat->last_access_time = convert_filetime (&find_data.ftLastAccessTime); + stat->last_write_time = convert_filetime (&find_data.ftLastWriteTime); + stat->length = ((gint64)find_data.nFileSizeHigh << 32) | find_data.nFileSizeLow; + return TRUE; +} + +gboolean +mono_w32file_set_attributes (const gunichar2 *name, guint32 attrs) +{ + gboolean res; + MONO_ENTER_GC_SAFE; + res = SetFileAttributes (name, attrs); + MONO_EXIT_GC_SAFE; + return res; +} + +guint32 +mono_w32file_get_cwd (guint32 length, gunichar2 *buffer) +{ + guint32 res; + MONO_ENTER_GC_SAFE; + res = GetCurrentDirectory (length, buffer); + MONO_EXIT_GC_SAFE; + return res; +} + +gboolean +mono_w32file_set_cwd (const gunichar2 *path) +{ + gboolean res; + MONO_ENTER_GC_SAFE; + res = SetCurrentDirectory (path); + MONO_EXIT_GC_SAFE; + return res; +} + +gboolean +mono_w32file_create_pipe (gpointer *readpipe, gpointer *writepipe, guint32 size) +{ + gboolean res; + SECURITY_ATTRIBUTES attr; + attr.nLength = sizeof(SECURITY_ATTRIBUTES); + attr.bInheritHandle = TRUE; + attr.lpSecurityDescriptor = NULL; + MONO_ENTER_GC_SAFE; + res = CreatePipe (readpipe, writepipe, &attr, size); + MONO_EXIT_GC_SAFE; + return res; +} + +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) +{ + gboolean result; + ULARGE_INTEGER wapi_free_bytes_avail; + ULARGE_INTEGER wapi_total_number_of_bytes; + ULARGE_INTEGER wapi_total_number_of_free_bytes; + + MONO_ENTER_GC_SAFE; + result = GetDiskFreeSpaceEx (path_name, &wapi_free_bytes_avail, &wapi_total_number_of_bytes, &wapi_total_number_of_free_bytes); + MONO_EXIT_GC_SAFE; + if (result) { + if (free_bytes_avail) + *free_bytes_avail = wapi_free_bytes_avail.QuadPart; + if (total_number_of_bytes) + *total_number_of_bytes = wapi_total_number_of_bytes.QuadPart; + if (total_number_of_free_bytes) + *total_number_of_free_bytes = wapi_total_number_of_free_bytes.QuadPart; + } + + return result; +} + +gboolean +mono_w32file_get_volume_information (const gunichar2 *path, gunichar2 *volumename, gint volumesize, gint *outserial, gint *maxcomp, gint *fsflags, gunichar2 *fsbuffer, gint fsbuffersize) +{ + gboolean res; + MONO_ENTER_GC_SAFE; + res = GetVolumeInformation (path, volumename, volumesize, outserial, maxcomp, fsflags, fsbuffer, fsbuffersize); + MONO_EXIT_GC_SAFE; + return res; +} + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) + +gboolean +mono_w32file_move (const gunichar2 *path, const gunichar2 *dest, gint32 *error) +{ + gboolean result; + + MONO_ENTER_GC_SAFE; + + result = MoveFile (path, dest); + if (!result) + *error = GetLastError (); + + MONO_EXIT_GC_SAFE; + + return result; +} + +gboolean +mono_w32file_replace (const gunichar2 *destinationFileName, const gunichar2 *sourceFileName, const gunichar2 *destinationBackupFileName, guint32 flags, gint32 *error) +{ + gboolean result; + + MONO_ENTER_GC_SAFE; + + result = ReplaceFile (destinationFileName, sourceFileName, destinationBackupFileName, flags, NULL, NULL); + if (!result) + *error = GetLastError (); + + MONO_EXIT_GC_SAFE; + + return result; +} + +gboolean +mono_w32file_copy (const gunichar2 *path, const gunichar2 *dest, gboolean overwrite, gint32 *error) +{ + gboolean result; + + MONO_ENTER_GC_SAFE; + + result = CopyFile (path, dest, !overwrite); + if (!result) + *error = GetLastError (); + + MONO_EXIT_GC_SAFE; + + return result; +} + +gboolean +mono_w32file_lock (gpointer handle, gint64 position, gint64 length, gint32 *error) +{ + gboolean result; + + MONO_ENTER_GC_SAFE; + + result = LockFile (handle, position & 0xFFFFFFFF, position >> 32, length & 0xFFFFFFFF, length >> 32); + if (!result) + *error = GetLastError (); + + MONO_EXIT_GC_SAFE; + + return result; +} + +gboolean +mono_w32file_unlock (gpointer handle, gint64 position, gint64 length, gint32 *error) +{ + gboolean result; + + MONO_ENTER_GC_SAFE; + + result = UnlockFile (handle, position & 0xFFFFFFFF, position >> 32, length & 0xFFFFFFFF, length >> 32); + if (!result) + *error = GetLastError (); + + MONO_EXIT_GC_SAFE; + + return result; +} + +HANDLE +mono_w32file_get_console_input (void) +{ + HANDLE res; + MONO_ENTER_GC_SAFE; + res = GetStdHandle (STD_INPUT_HANDLE); + MONO_EXIT_GC_SAFE; + return res; +} + +HANDLE +mono_w32file_get_console_output (void) +{ + HANDLE res; + MONO_ENTER_GC_SAFE; + res = GetStdHandle (STD_OUTPUT_HANDLE); + MONO_EXIT_GC_SAFE; + return res; +} + +HANDLE +mono_w32file_get_console_error (void) +{ + HANDLE res; + MONO_ENTER_GC_SAFE; + res = GetStdHandle (STD_ERROR_HANDLE); + MONO_EXIT_GC_SAFE; + return res; +} + +gint64 +mono_w32file_get_file_size (gpointer handle, gint32 *error) +{ + gint64 length; + guint32 length_hi; + + MONO_ENTER_GC_SAFE; + + length = GetFileSize (handle, &length_hi); + if(length==INVALID_FILE_SIZE) { + *error=GetLastError (); + } + + MONO_EXIT_GC_SAFE; + + return length | ((gint64)length_hi << 32); +} + +guint32 +mono_w32file_get_drive_type (const gunichar2 *root_path_name) +{ + guint32 res; + MONO_ENTER_GC_SAFE; + res = GetDriveType (root_path_name); + MONO_EXIT_GC_SAFE; + return res; +} + +gint32 +mono_w32file_get_logical_drive (guint32 len, gunichar2 *buf) +{ + gint32 res; + MONO_ENTER_GC_SAFE; + res = GetLogicalDriveStrings (len, buf); + MONO_EXIT_GC_SAFE; + return res; +} + +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32file.c b/unity-2019.4.24f1-mbe/mono/metadata/w32file.c new file mode 100644 index 000000000..88f9664bb --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32file.c @@ -0,0 +1,998 @@ +/** + * \file + * File IO internal calls + * + * Author: + * Dick Porter (dick@ximian.com) + * Gonzalo Paniagua Javier (gonzalo@ximian.com) + * + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Copyright 2012 Xamarin Inc (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include + +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG + +/* conversion functions */ + +static guint32 convert_mode(MonoFileMode mono_mode) +{ + guint32 mode; + + switch(mono_mode) { + case FileMode_CreateNew: + mode=CREATE_NEW; + break; + case FileMode_Create: + mode=CREATE_ALWAYS; + break; + case FileMode_Open: + mode=OPEN_EXISTING; + break; + case FileMode_OpenOrCreate: + mode=OPEN_ALWAYS; + break; + case FileMode_Truncate: + mode=TRUNCATE_EXISTING; + break; + case FileMode_Append: + mode=OPEN_ALWAYS; + break; + default: + g_warning("System.IO.FileMode has unknown value 0x%x", + mono_mode); + /* Safe fallback */ + mode=OPEN_EXISTING; + } + + return(mode); +} + +static guint32 convert_access(MonoFileAccess mono_access) +{ + guint32 access; + + switch(mono_access) { + case FileAccess_Read: + access=GENERIC_READ; + break; + case FileAccess_Write: + access=GENERIC_WRITE; + break; + case FileAccess_ReadWrite: + access=GENERIC_READ|GENERIC_WRITE; + break; + default: + g_warning("System.IO.FileAccess has unknown value 0x%x", + mono_access); + /* Safe fallback */ + access=GENERIC_READ; + } + + return(access); +} + +static guint32 convert_share(MonoFileShare mono_share) +{ + guint32 share = 0; + + if (mono_share & FileShare_Read) { + share |= FILE_SHARE_READ; + } + if (mono_share & FileShare_Write) { + share |= FILE_SHARE_WRITE; + } + if (mono_share & FileShare_Delete) { + share |= FILE_SHARE_DELETE; + } + + if (mono_share & ~(FileShare_Read|FileShare_Write|FileShare_Delete)) { + g_warning("System.IO.FileShare has unknown value 0x%x", + mono_share); + /* Safe fallback */ + share=0; + } + + return(share); +} + +#if 0 +static guint32 convert_stdhandle(guint32 fd) +{ + guint32 stdhandle; + + switch(fd) { + case 0: + stdhandle=STD_INPUT_HANDLE; + break; + case 1: + stdhandle=STD_OUTPUT_HANDLE; + break; + case 2: + stdhandle=STD_ERROR_HANDLE; + break; + default: + g_warning("unknown standard file descriptor %d", fd); + stdhandle=STD_INPUT_HANDLE; + } + + return(stdhandle); +} +#endif + +static guint32 convert_seekorigin(MonoSeekOrigin origin) +{ + guint32 w32origin; + + switch(origin) { + case SeekOrigin_Begin: + w32origin=FILE_BEGIN; + break; + case SeekOrigin_Current: + w32origin=FILE_CURRENT; + break; + case SeekOrigin_End: + w32origin=FILE_END; + break; + default: + g_warning("System.IO.SeekOrigin has unknown value 0x%x", + origin); + /* Safe fallback */ + w32origin=FILE_CURRENT; + } + + return(w32origin); +} + +/* Managed file attributes have nearly but not quite the same values + * as the w32 equivalents. + */ +static guint32 convert_attrs(MonoFileAttributes attrs) +{ + if(attrs & FileAttributes_Encrypted) { + attrs = (MonoFileAttributes)(attrs | FILE_ATTRIBUTE_ENCRYPTED); + } + + return(attrs); +} + +/* System.IO.MonoIO internal calls */ + +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); + if(ret==FALSE) { + *error=mono_w32error_get_last (); + } + + g_free (path_remapped); + + return(ret); +} + +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; + + ret=mono_w32file_remove_directory (path); + if(ret==FALSE) { + *error=mono_w32error_get_last (); + } + + g_free (path_remapped); + + return(ret); +} + +HANDLE +ves_icall_System_IO_MonoIO_FindFirstFile (const gunichar2 *path_with_pattern, MonoStringHandleOut file_name, gint32 *file_attr, gint32 *ioerror, MonoError *error) +{ + 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); + + if (hnd == INVALID_HANDLE_VALUE) { + MONO_HANDLE_ASSIGN (file_name, NULL_HANDLE_STRING); + *file_attr = 0; + *ioerror = mono_w32error_get_last (); + g_free (path_with_pattern_remapped); + return hnd; + } + + int len = 0; + while (data.cFileName [len]) len++; + MONO_HANDLE_ASSIGN (file_name, mono_string_new_utf16_handle (mono_domain_get (), data.cFileName, len, error)); + return_val_if_nok (error, INVALID_HANDLE_VALUE); + + *file_attr = data.dwFileAttributes; + *ioerror = ERROR_SUCCESS; + + g_free (path_with_pattern_remapped); + return hnd; +} + +MonoBoolean +ves_icall_System_IO_MonoIO_FindNextFile (HANDLE hnd, MonoStringHandleOut file_name, gint32 *file_attr, gint32 *ioerror, MonoError *error) +{ + MonoBoolean res; + WIN32_FIND_DATA data; + + res = mono_w32file_find_next (hnd, &data); + + if (res == FALSE) { + MONO_HANDLE_ASSIGN (file_name, NULL_HANDLE_STRING); + *file_attr = 0; + *ioerror = mono_w32error_get_last (); + return res; + } + + int len = 0; + while (data.cFileName [len]) len++; + MONO_HANDLE_ASSIGN (file_name, mono_string_new_utf16_handle (mono_domain_get (), data.cFileName, len, error)); + return_val_if_nok (error, FALSE); + + *file_attr = data.dwFileAttributes; + *ioerror = ERROR_SUCCESS; + + return res; +} + +MonoBoolean +ves_icall_System_IO_MonoIO_FindCloseFile (HANDLE hnd) +{ + return mono_w32file_find_close (hnd); +} + +MonoStringHandle +ves_icall_System_IO_MonoIO_GetCurrentDirectory (gint32 *io_error, MonoError *error) +{ + MonoStringHandle result; + gunichar2 *buf; + int len, res_len; + + len = MAX_PATH + 1; /*FIXME this is too smal under most unix systems.*/ + buf = g_new (gunichar2, len); + + *io_error = ERROR_SUCCESS; + result = MONO_HANDLE_NEW (MonoString, NULL); + + res_len = mono_w32file_get_cwd (len, buf); + if (res_len > len) { /*buf is too small.*/ + int old_res_len = res_len; + g_free (buf); + buf = g_new (gunichar2, res_len); + res_len = mono_w32file_get_cwd (res_len, buf) == old_res_len; + } + + if (res_len) { + len = 0; + while (buf [len]) + ++ len; + + MONO_HANDLE_ASSIGN (result, mono_string_new_utf16_handle (mono_domain_get (), buf, len, error)); + } else { + *io_error=mono_w32error_get_last (); + } + + g_free (buf); + return_val_if_nok (error, NULL_HANDLE_STRING); + return result; +} + +MonoBoolean +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; + + ret=mono_w32file_set_cwd (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; + ret = mono_w32file_move (path, dest, error); + + g_free (path_remapped); + g_free (dest_remapped); + + return ret; +} + +MonoBoolean +ves_icall_System_IO_MonoIO_ReplaceFile (const gunichar2 *source_file_name, const gunichar2 *destination_file_name, + 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! */ + 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; + 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; + + ret=mono_w32file_delete (path); + if(ret==FALSE) { + *error=mono_w32error_get_last (); + } + + g_free (path_remapped); + + return(ret); +} + +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); + + /* + * The definition of INVALID_FILE_ATTRIBUTES in the cygwin win32 + * headers is wrong, hence this temporary workaround. + * See + * http://cygwin.com/ml/cygwin/2003-09/msg01771.html + */ + if (ret==-1) { + /* if(ret==INVALID_FILE_ATTRIBUTES) { */ + *error=mono_w32error_get_last (); + } + + g_free (path_remapped); + + return(ret); +} + +MonoBoolean +ves_icall_System_IO_MonoIO_SetFileAttributes (const gunichar2 *path, gint32 attrs, + 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, + convert_attrs ((MonoFileAttributes)attrs)); + if(ret==FALSE) { + *error=mono_w32error_get_last (); + } + + g_free (path_remapped); + + return(ret); +} + +gint32 +ves_icall_System_IO_MonoIO_GetFileType (HANDLE handle, gint32 *error) +{ + gboolean ret; + + *error=ERROR_SUCCESS; + + ret=mono_w32file_get_type (handle); + if(ret==FILE_TYPE_UNKNOWN) { + /* Not necessarily an error, but the caller will have + * to decide based on the error value. + */ + *error=mono_w32error_get_last (); + } + + return(ret); +} + +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; + + result = mono_w32file_get_attributes_ex (path, stat); + + if (!result) { + *error=mono_w32error_get_last (); + memset (stat, 0, sizeof (MonoIOStat)); + } + + g_free (path_remapped); + + return result; +} + +HANDLE +ves_icall_System_IO_MonoIO_Open (const gunichar2 *filename, gint32 mode, + gint32 access_mode, gint32 share, gint32 options, + gint32 *error) +{ + HANDLE ret; + int attributes, attrs; + const gunichar2 *filename_remapped; + + if (filename_remapped = mono_unity_remap_path_utf16 (filename)) + filename = filename_remapped; + + *error=ERROR_SUCCESS; + + if (options != 0){ + if (options & FileOptions_Encrypted) + attributes = FILE_ATTRIBUTE_ENCRYPTED; + else + attributes = FILE_ATTRIBUTE_NORMAL; + if (options & FileOptions_DeleteOnClose) + attributes |= FILE_FLAG_DELETE_ON_CLOSE; + if (options & FileOptions_SequentialScan) + attributes |= FILE_FLAG_SEQUENTIAL_SCAN; + if (options & FileOptions_RandomAccess) + attributes |= FILE_FLAG_RANDOM_ACCESS; + + if (options & FileOptions_Temporary) + attributes |= FILE_ATTRIBUTE_TEMPORARY; + + if (options & FileOptions_WriteThrough) + attributes |= FILE_FLAG_WRITE_THROUGH; + } else + attributes = FILE_ATTRIBUTE_NORMAL; + + /* If we're opening a directory we need to set the extra flag + */ + attrs = mono_w32file_get_attributes (filename); + if (attrs != INVALID_FILE_ATTRIBUTES) { + if (attrs & FILE_ATTRIBUTE_DIRECTORY) { + attributes |= FILE_FLAG_BACKUP_SEMANTICS; + } + } + + 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); +} + +MonoBoolean +ves_icall_System_IO_MonoIO_Close (HANDLE handle, gint32 *error) +{ + gboolean ret; + *error=ERROR_SUCCESS; + + ret=mono_w32file_close (handle); + if(ret==FALSE) { + *error=mono_w32error_get_last (); + } + return(ret); +} + +gint32 +ves_icall_System_IO_MonoIO_Read (HANDLE handle, MonoArrayHandle dest, + gint32 dest_offset, gint32 count, + gint32 *io_error, + MonoError *error) +{ + guchar *buffer; + gboolean result; + guint32 n; + + *io_error=ERROR_SUCCESS; + + MONO_CHECK_ARG_NULL (dest, 0); + + if (dest_offset > mono_array_handle_length (dest) - count) { + mono_error_set_argument (error, "array", "array too small. numBytes/offset wrong."); + return 0; + } + + guint32 buffer_handle = 0; + buffer = MONO_ARRAY_HANDLE_PIN (dest, guchar, dest_offset, &buffer_handle); + result = mono_w32file_read (handle, buffer, count, &n); + mono_gchandle_free (buffer_handle); + + if (!result) { + *io_error=mono_w32error_get_last (); + return -1; + } + + return (gint32)n; +} + +gint32 +ves_icall_System_IO_MonoIO_Write (HANDLE handle, MonoArrayHandle src, + gint32 src_offset, gint32 count, + gint32 *io_error, + MonoError *error) +{ + guchar *buffer; + gboolean result; + guint32 n; + + *io_error=ERROR_SUCCESS; + + MONO_CHECK_ARG_NULL (src, 0); + + if (src_offset > mono_array_handle_length (src) - count) { + mono_error_set_argument (error, "array", "array too small. numBytes/offset wrong."); + return 0; + } + + guint32 src_handle = 0; + buffer = MONO_ARRAY_HANDLE_PIN (src, guchar, src_offset, &src_handle); + result = mono_w32file_write (handle, buffer, count, &n); + mono_gchandle_free (src_handle); + + if (!result) { + *io_error=mono_w32error_get_last (); + return -1; + } + + return (gint32)n; +} + +gint64 +ves_icall_System_IO_MonoIO_Seek (HANDLE handle, gint64 offset, gint32 origin, + gint32 *error) +{ + gint32 offset_hi; + + *error=ERROR_SUCCESS; + + offset_hi = offset >> 32; + offset = mono_w32file_seek (handle, (gint32) (offset & 0xFFFFFFFF), &offset_hi, + convert_seekorigin ((MonoSeekOrigin)origin)); + + if(offset==INVALID_SET_FILE_POINTER) { + *error=mono_w32error_get_last (); + } + + return offset | ((gint64)offset_hi << 32); +} + +MonoBoolean +ves_icall_System_IO_MonoIO_Flush (HANDLE handle, gint32 *error) +{ + gboolean ret; + + *error=ERROR_SUCCESS; + + ret=mono_w32file_flush (handle); + if(ret==FALSE) { + *error=mono_w32error_get_last (); + } + + return(ret); +} + +gint64 +ves_icall_System_IO_MonoIO_GetLength (HANDLE handle, gint32 *error) +{ + *error=ERROR_SUCCESS; + return mono_w32file_get_file_size (handle, error); +} + +/* FIXME make gc suspendable */ +MonoBoolean +ves_icall_System_IO_MonoIO_SetLength (HANDLE handle, gint64 length, + gint32 *error) +{ + gint64 offset, offset_set; + gint32 offset_hi; + gint32 length_hi; + gboolean result; + + *error=ERROR_SUCCESS; + + /* save file pointer */ + + offset_hi = 0; + offset = mono_w32file_seek (handle, 0, &offset_hi, FILE_CURRENT); + if(offset==INVALID_SET_FILE_POINTER) { + *error=mono_w32error_get_last (); + return(FALSE); + } + + /* extend or truncate */ + + length_hi = length >> 32; + offset_set=mono_w32file_seek (handle, length & 0xFFFFFFFF, &length_hi, + FILE_BEGIN); + if(offset_set==INVALID_SET_FILE_POINTER) { + *error=mono_w32error_get_last (); + return(FALSE); + } + + result = mono_w32file_truncate (handle); + if(result==FALSE) { + *error=mono_w32error_get_last (); + return(FALSE); + } + + /* restore file pointer */ + + offset_set=mono_w32file_seek (handle, offset & 0xFFFFFFFF, &offset_hi, + FILE_BEGIN); + if(offset_set==INVALID_SET_FILE_POINTER) { + *error=mono_w32error_get_last (); + return(FALSE); + } + + return result; +} + +MonoBoolean +ves_icall_System_IO_MonoIO_SetFileTime (HANDLE handle, gint64 creation_time, + gint64 last_access_time, + gint64 last_write_time, gint32 *error) +{ + gboolean ret; + const FILETIME *creation_filetime; + const FILETIME *access_filetime; + const FILETIME *write_filetime; + + *error=ERROR_SUCCESS; + + if (creation_time < 0) + creation_filetime = NULL; + else + creation_filetime = (FILETIME *)&creation_time; + + if (last_access_time < 0) + access_filetime = NULL; + else + access_filetime = (FILETIME *)&last_access_time; + + if (last_write_time < 0) + write_filetime = NULL; + else + write_filetime = (FILETIME *)&last_write_time; + + ret=mono_w32file_set_times (handle, creation_filetime, access_filetime, write_filetime); + if(ret==FALSE) { + *error=mono_w32error_get_last (); + } + + return(ret); +} + +HANDLE +ves_icall_System_IO_MonoIO_get_ConsoleOutput () +{ + return mono_w32file_get_console_output (); +} + +HANDLE +ves_icall_System_IO_MonoIO_get_ConsoleInput () +{ + return mono_w32file_get_console_input (); +} + +HANDLE +ves_icall_System_IO_MonoIO_get_ConsoleError () +{ + return mono_w32file_get_console_error (); +} + +MonoBoolean +ves_icall_System_IO_MonoIO_CreatePipe (HANDLE *read_handle, HANDLE *write_handle, gint32 *error) +{ + gboolean ret; + + ret=mono_w32file_create_pipe (read_handle, write_handle, 0); + + if(ret==FALSE) { + *error = mono_w32error_get_last (); + /* FIXME: throw an exception? */ + return(FALSE); + } + + return(TRUE); +} + +MonoBoolean +ves_icall_System_IO_MonoIO_DuplicateHandle (HANDLE source_process_handle, HANDLE source_handle, + HANDLE target_process_handle, HANDLE *target_handle, gint32 access, gint32 inherit, gint32 options, gint32 *error) +{ +#ifndef HOST_WIN32 + MonoW32Handle *source_handle_data; + + if (!mono_w32handle_lookup_and_ref (source_handle, &source_handle_data)) { + *error = ERROR_INVALID_HANDLE; + return FALSE; + } + + *target_handle = mono_w32handle_duplicate (source_handle_data); + + mono_w32handle_unref (source_handle); +#else + gboolean ret; + + MONO_ENTER_GC_SAFE; + ret=DuplicateHandle (source_process_handle, source_handle, target_process_handle, target_handle, access, inherit, options); + MONO_EXIT_GC_SAFE; + + if (!ret) { + *error = mono_w32error_get_last (); + /* FIXME: throw an exception? */ + return(FALSE); + } +#endif + + return(TRUE); +} + +#ifndef HOST_WIN32 +gunichar2 +ves_icall_System_IO_MonoIO_get_VolumeSeparatorChar () +{ + return (gunichar2) '/'; /* forward slash */ +} + +gunichar2 +ves_icall_System_IO_MonoIO_get_DirectorySeparatorChar () +{ + return (gunichar2) '/'; /* forward slash */ +} + +gunichar2 +ves_icall_System_IO_MonoIO_get_AltDirectorySeparatorChar () +{ + if (IS_PORTABILITY_SET) + return (gunichar2) '\\'; /* backslash */ + else + return (gunichar2) '/'; /* forward slash */ +} + +gunichar2 +ves_icall_System_IO_MonoIO_get_PathSeparator () +{ + return (gunichar2) ':'; /* colon */ +} +#endif /* !HOST_WIN32 */ + +static const gunichar2 +invalid_path_chars [] = { +#if defined (TARGET_WIN32) + 0x0022, /* double quote, which seems allowed in MS.NET but should be rejected */ + 0x003c, /* less than */ + 0x003e, /* greater than */ + 0x007c, /* pipe */ + 0x0008, + 0x0010, + 0x0011, + 0x0012, + 0x0014, + 0x0015, + 0x0016, + 0x0017, + 0x0018, + 0x0019, +#endif + 0x0000 /* null */ +}; + +MonoArrayHandle +ves_icall_System_IO_MonoIO_get_InvalidPathChars (MonoError *error) +{ + MonoArrayHandle chars = MONO_HANDLE_NEW (MonoArray, NULL); + MonoDomain *domain; + int i, n; + + domain = mono_domain_get (); + n = sizeof (invalid_path_chars) / sizeof (gunichar2); + MONO_HANDLE_ASSIGN (chars, mono_array_new_handle (domain, mono_defaults.char_class, n, error)); + return_val_if_nok (error, NULL); + + for (i = 0; i < n; ++ i) + MONO_HANDLE_ARRAY_SETVAL (chars, gunichar2, i, invalid_path_chars [i]); + + return chars; +} + +void ves_icall_System_IO_MonoIO_Lock (HANDLE handle, gint64 position, + gint64 length, gint32 *error) +{ + *error=ERROR_SUCCESS; + mono_w32file_lock (handle, position, length, error); +} + +void ves_icall_System_IO_MonoIO_Unlock (HANDLE handle, gint64 position, + gint64 length, gint32 *error) +{ + *error=ERROR_SUCCESS; + mono_w32file_unlock (handle, position, length, error); +} + +//Support for io-layer free mmap'd files. + +#if defined (TARGET_IOS) || defined (TARGET_ANDROID) + +gint64 +mono_filesize_from_path (MonoString *string) +{ + MonoError error; + struct stat buf; + gint64 res; + char *path = mono_string_to_utf8_checked (string, &error); + mono_error_raise_exception_deprecated (&error); /* OK to throw, external only without a good alternative */ + + gint stat_res; + MONO_ENTER_GC_SAFE; + stat_res = stat (path, &buf); + MONO_EXIT_GC_SAFE; + if (stat_res == -1) + res = -1; + else + res = (gint64)buf.st_size; + + g_free (path); + + return res; +} + +gint64 +mono_filesize_from_fd (int fd) +{ + struct stat buf; + int res; + + MONO_ENTER_GC_SAFE; + res = fstat (fd, &buf); + MONO_EXIT_GC_SAFE; + + if (res == -1) + return (gint64)-1; + + return (gint64)buf.st_size; +} + +#endif + +#ifndef HOST_WIN32 +void mono_w32handle_dump (void); + +void ves_icall_System_IO_MonoIO_DumpHandles (void) +{ + mono_w32handle_dump (); +} +#endif /* !HOST_WIN32 */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32file.h b/unity-2019.4.24f1-mbe/mono/metadata/w32file.h new file mode 100644 index 000000000..73673495a --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32file.h @@ -0,0 +1,486 @@ +/** + * \file + * File IO internal calls + * + * Authors: + * Dick Porter (dick@ximian.com) + * Dan Lewis (dihlewis@yahoo.co.uk) + * + * (C) 2001 Ximian, Inc. + * Copyright 2012 Xamarin Inc (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#ifndef _MONO_METADATA_W32FILE_H_ +#define _MONO_METADATA_W32FILE_H_ + +#include +#include + +#include +#include + +G_BEGIN_DECLS + +/* This is a copy of System.IO.FileAccess */ +typedef enum { + FileAccess_Read=0x01, + FileAccess_Write=0x02, + FileAccess_ReadWrite=FileAccess_Read|FileAccess_Write +} MonoFileAccess; + +/* This is a copy of System.IO.FileMode */ +typedef enum { + FileMode_CreateNew=1, + FileMode_Create=2, + FileMode_Open=3, + FileMode_OpenOrCreate=4, + FileMode_Truncate=5, + FileMode_Append=6 +} MonoFileMode; + +/* This is a copy of System.IO.FileShare */ +typedef enum { + FileShare_None=0x0, + FileShare_Read=0x01, + FileShare_Write=0x02, + FileShare_ReadWrite=FileShare_Read|FileShare_Write, + FileShare_Delete=0x04 +} MonoFileShare; + +/* This is a copy of System.IO.FileOptions */ +typedef enum { + FileOptions_None = 0, + FileOptions_Temporary = 1, // Internal. See note in System.IO.FileOptions + FileOptions_Encrypted = 0x4000, + FileOptions_DeleteOnClose = 0x4000000, + FileOptions_SequentialScan = 0x8000000, + FileOptions_RandomAccess = 0x10000000, + FileOptions_Asynchronous = 0x40000000, + FileOptions_WriteThrough = 0x80000000 +} MonoFileOptions; + +/* This is a copy of System.IO.SeekOrigin */ +typedef enum { + SeekOrigin_Begin=0, + SeekOrigin_Current=1, + SeekOrigin_End=2 +} MonoSeekOrigin; + +/* This is a copy of System.IO.MonoIOStat */ +typedef struct _MonoIOStat { + gint32 attributes; + gint64 length; + gint64 creation_time; + gint64 last_access_time; + gint64 last_write_time; +} MonoIOStat; + +/* This is a copy of System.IO.FileAttributes */ +typedef enum { + FileAttributes_ReadOnly=0x00001, + FileAttributes_Hidden=0x00002, + FileAttributes_System=0x00004, + FileAttributes_Directory=0x00010, + FileAttributes_Archive=0x00020, + FileAttributes_Device=0x00040, + FileAttributes_Normal=0x00080, + FileAttributes_Temporary=0x00100, + FileAttributes_SparseFile=0x00200, + FileAttributes_ReparsePoint=0x00400, + FileAttributes_Compressed=0x00800, + FileAttributes_Offline=0x01000, + FileAttributes_NotContentIndexed=0x02000, + FileAttributes_Encrypted=0x04000, + FileAttributes_MonoExecutable= (int) 0x80000000 +} MonoFileAttributes; +/* This is not used anymore +typedef struct _MonoFSAsyncResult { + MonoObject obj; + MonoObject *state; + MonoBoolean completed; + MonoBoolean done; + MonoException *exc; + MonoWaitHandle *wait_handle; + MonoDelegate *async_callback; + MonoBoolean completed_synch; + MonoArray *buffer; + gint offset; + gint count; + gint original_count; + gint bytes_read; + MonoDelegate *real_cb; +} MonoFSAsyncResult; +*/ +/* System.IO.MonoIO */ + +extern MonoBoolean +ves_icall_System_IO_MonoIO_CreateDirectory (const gunichar2 *path, gint32 *error); + +extern MonoBoolean +ves_icall_System_IO_MonoIO_RemoveDirectory (const gunichar2 *path, gint32 *error); + +extern gpointer +ves_icall_System_IO_MonoIO_FindFirstFile (const gunichar2 *path_with_pattern, + MonoStringHandleOut file_name, + gint32 *file_attr, + gint32 *ioerror, + MonoError *error); + +extern MonoBoolean +ves_icall_System_IO_MonoIO_FindNextFile (gpointer hnd, + MonoStringHandleOut file_name, + gint32 *file_attr, + gint32 *ioerror, + MonoError *error); + +extern MonoBoolean +ves_icall_System_IO_MonoIO_FindCloseFile (gpointer hnd); + +extern MonoStringHandle +ves_icall_System_IO_MonoIO_GetCurrentDirectory (gint32 *io_error, MonoError *error); + +extern MonoBoolean +ves_icall_System_IO_MonoIO_SetCurrentDirectory (const gunichar2 *path, + gint32 *error); + +extern MonoBoolean +ves_icall_System_IO_MonoIO_MoveFile (const gunichar2 *path, const gunichar2 *dest, + gint32 *error); + +extern MonoBoolean +ves_icall_System_IO_MonoIO_CopyFile (const gunichar2 *path, const gunichar2 *dest, + MonoBoolean overwrite, gint32 *error); + +extern MonoBoolean +ves_icall_System_IO_MonoIO_DeleteFile (const gunichar2 *path, gint32 *error); + +extern gint32 +ves_icall_System_IO_MonoIO_GetFileAttributes (const gunichar2 *path, gint32 *error); + +extern MonoBoolean +ves_icall_System_IO_MonoIO_SetFileAttributes (const gunichar2 *path, gint32 attrs, + gint32 *error); + +extern gint32 +ves_icall_System_IO_MonoIO_GetFileType (gpointer handle, gint32 *error); + +extern MonoBoolean +ves_icall_System_IO_MonoIO_GetFileStat (const gunichar2 *path, MonoIOStat *stat, + gint32 *error); + +extern gpointer +ves_icall_System_IO_MonoIO_Open (const gunichar2 *filename, gint32 mode, + gint32 access_mode, gint32 share, gint32 options, + gint32 *error); + +extern MonoBoolean +ves_icall_System_IO_MonoIO_Close (gpointer handle, gint32 *error); + +extern gint32 +ves_icall_System_IO_MonoIO_Read (gpointer handle, MonoArrayHandle dest, + gint32 dest_offset, gint32 count, + gint32 *io_error, + MonoError *error); + +extern gint32 +ves_icall_System_IO_MonoIO_Write (gpointer handle, MonoArrayHandle src, + gint32 src_offset, gint32 count, + gint32 *io_error, + MonoError *error); + +extern gint64 +ves_icall_System_IO_MonoIO_Seek (gpointer handle, gint64 offset, gint32 origin, + gint32 *error); + +extern MonoBoolean +ves_icall_System_IO_MonoIO_Flush (gpointer handle, gint32 *error); + +extern gint64 +ves_icall_System_IO_MonoIO_GetLength (gpointer handle, gint32 *error); + +extern MonoBoolean +ves_icall_System_IO_MonoIO_SetLength (gpointer handle, gint64 length, + gint32 *error); + +extern MonoBoolean +ves_icall_System_IO_MonoIO_SetFileTime (gpointer handle, gint64 creation_time, + gint64 last_access_time, + gint64 last_write_time, gint32 *error); + +extern gpointer +ves_icall_System_IO_MonoIO_get_ConsoleOutput (void); + +extern gpointer +ves_icall_System_IO_MonoIO_get_ConsoleInput (void); + +extern gpointer +ves_icall_System_IO_MonoIO_get_ConsoleError (void); + +extern MonoBoolean +ves_icall_System_IO_MonoIO_CreatePipe (gpointer *read_handle, gpointer *write_handle, gint32 *error); + +extern MonoBoolean +ves_icall_System_IO_MonoIO_DuplicateHandle (gpointer source_process_handle, gpointer source_handle, + gpointer target_process_handle, gpointer *target_handle, gint32 access, gint32 inherit, gint32 options, gint32 *error); + +extern gunichar2 +ves_icall_System_IO_MonoIO_get_VolumeSeparatorChar (void); + +extern gunichar2 +ves_icall_System_IO_MonoIO_get_DirectorySeparatorChar (void); + +extern gunichar2 +ves_icall_System_IO_MonoIO_get_AltDirectorySeparatorChar (void); + +extern gunichar2 +ves_icall_System_IO_MonoIO_get_PathSeparator (void); + +extern MonoArrayHandle +ves_icall_System_IO_MonoIO_get_InvalidPathChars (MonoError *error); + +extern void ves_icall_System_IO_MonoIO_Lock (gpointer handle, gint64 position, + gint64 length, gint32 *error); +extern void ves_icall_System_IO_MonoIO_Unlock (gpointer handle, gint64 position, + gint64 length, gint32 *error); + +extern MonoBoolean +ves_icall_System_IO_MonoIO_ReplaceFile (const gunichar2 *source_file_name, const gunichar2 *destination_file_name, + const gunichar2 *destination_backup_file_name, MonoBoolean ignore_metadata_errors, + gint32 *error); + +#if defined (TARGET_IOS) || defined (TARGET_ANDROID) + +MONO_RT_EXTERNAL_ONLY +extern gint64 +mono_filesize_from_path (MonoString *path); + +extern gint64 +mono_filesize_from_fd (int fd); + +#endif + +void +ves_icall_System_IO_MonoIO_DumpHandles (void); + +#if !defined(HOST_WIN32) + +#define GENERIC_READ 0x80000000 +#define GENERIC_WRITE 0x40000000 +#define GENERIC_EXECUTE 0x20000000 +#define GENERIC_ALL 0x10000000 + +#define FILE_SHARE_READ 0x00000001 +#define FILE_SHARE_WRITE 0x00000002 +#define FILE_SHARE_DELETE 0x00000004 + +#define CREATE_NEW 1 +#define CREATE_ALWAYS 2 +#define OPEN_EXISTING 3 +#define OPEN_ALWAYS 4 +#define TRUNCATE_EXISTING 5 + +#define FILE_ATTRIBUTE_READONLY 0x00000001 +#define FILE_ATTRIBUTE_HIDDEN 0x00000002 +#define FILE_ATTRIBUTE_SYSTEM 0x00000004 +#define FILE_ATTRIBUTE_DIRECTORY 0x00000010 +#define FILE_ATTRIBUTE_ARCHIVE 0x00000020 +#define FILE_ATTRIBUTE_ENCRYPTED 0x00000040 +#define FILE_ATTRIBUTE_NORMAL 0x00000080 +#define FILE_ATTRIBUTE_TEMPORARY 0x00000100 +#define FILE_ATTRIBUTE_SPARSE_FILE 0x00000200 +#define FILE_ATTRIBUTE_REPARSE_POINT 0x00000400 +#define FILE_ATTRIBUTE_COMPRESSED 0x00000800 +#define FILE_ATTRIBUTE_OFFLINE 0x00001000 +#define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x00002000 +#define FILE_FLAG_OPEN_NO_RECALL 0x00100000 +#define FILE_FLAG_OPEN_REPARSE_POINT 0x00200000 +#define FILE_FLAG_POSIX_SEMANTICS 0x01000000 +#define FILE_FLAG_BACKUP_SEMANTICS 0x02000000 +#define FILE_FLAG_DELETE_ON_CLOSE 0x04000000 +#define FILE_FLAG_SEQUENTIAL_SCAN 0x08000000 +#define FILE_FLAG_RANDOM_ACCESS 0x10000000 +#define FILE_FLAG_NO_BUFFERING 0x20000000 +#define FILE_FLAG_OVERLAPPED 0x40000000 +#define FILE_FLAG_WRITE_THROUGH 0x80000000 + +#define REPLACEFILE_WRITE_THROUGH 0x00000001 +#define REPLACEFILE_IGNORE_MERGE_ERRORS 0x00000002 + +#define MAX_PATH 260 + +#define INVALID_SET_FILE_POINTER ((guint32) 0xFFFFFFFF) +#define INVALID_FILE_SIZE ((guint32) 0xFFFFFFFF) +#define INVALID_FILE_ATTRIBUTES ((guint32) 0xFFFFFFFF) + +#define FILE_TYPE_UNKNOWN 0x0000 +#define FILE_TYPE_DISK 0x0001 +#define FILE_TYPE_CHAR 0x0002 +#define FILE_TYPE_PIPE 0x0003 +#define FILE_TYPE_REMOTE 0x8000 + +#define FILE_BEGIN 0 +#define FILE_CURRENT 1 +#define FILE_END 2 + +#define DRIVE_UNKNOWN 0 +#define DRIVE_NO_ROOT_DIR 1 +#define DRIVE_REMOVABLE 2 +#define DRIVE_FIXED 3 +#define DRIVE_REMOTE 4 +#define DRIVE_CDROM 5 +#define DRIVE_RAMDISK 6 + +typedef struct { + guint16 wYear; + guint16 wMonth; + guint16 wDayOfWeek; + guint16 wDay; + guint16 wHour; + guint16 wMinute; + guint16 wSecond; + guint16 wMilliseconds; +} SYSTEMTIME; + +typedef struct { +#if G_BYTE_ORDER == G_BIG_ENDIAN + guint32 dwHighDateTime; + guint32 dwLowDateTime; +#else + guint32 dwLowDateTime; + guint32 dwHighDateTime; +#endif +} FILETIME; + +typedef struct { + guint32 dwFileAttributes; + FILETIME ftCreationTime; + FILETIME ftLastAccessTime; + FILETIME ftLastWriteTime; + guint32 nFileSizeHigh; + guint32 nFileSizeLow; + guint32 dwReserved0; + guint32 dwReserved1; + gunichar2 cFileName [MAX_PATH]; + gunichar2 cAlternateFileName [14]; +} WIN32_FIND_DATA; + +#endif /* !defined(HOST_WIN32) */ + +void +mono_w32file_init (void); + +void +mono_w32file_cleanup (void); + +gpointer +mono_w32file_create(const gunichar2 *name, guint32 fileaccess, guint32 sharemode, guint32 createmode, guint32 attrs); + +gboolean +mono_w32file_close (gpointer handle); + +gboolean +mono_w32file_delete (const gunichar2 *name); + +gboolean +mono_w32file_read (gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread); + +gboolean +mono_w32file_write (gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten); + +gboolean +mono_w32file_flush (gpointer handle); + +gboolean +mono_w32file_truncate (gpointer handle); + +guint32 +mono_w32file_seek (gpointer handle, gint32 movedistance, gint32 *highmovedistance, guint32 method); + +gboolean +mono_w32file_move (const gunichar2 *path, const gunichar2 *dest, gint32 *error); + +gboolean +mono_w32file_copy (const gunichar2 *path, const gunichar2 *dest, gboolean overwrite, gint32 *error); + +gboolean +mono_w32file_lock (gpointer handle, gint64 position, gint64 length, gint32 *error); + +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 +mono_w32file_unlock (gpointer handle, gint64 position, gint64 length, gint32 *error); + +gpointer +mono_w32file_get_console_output (void); + +gpointer +mono_w32file_get_console_error (void); + +gpointer +mono_w32file_get_console_input (void); + +gint64 +mono_w32file_get_file_size (gpointer handle, gint32 *error); + +gint +mono_w32file_get_type (gpointer handle); + +gboolean +mono_w32file_get_times (gpointer handle, FILETIME *create_time, FILETIME *access_time, FILETIME *write_time); + +gboolean +mono_w32file_set_times (gpointer handle, const FILETIME *create_time, const FILETIME *access_time, const FILETIME *write_time); + +gboolean +mono_w32file_filetime_to_systemtime (const FILETIME *file_time, SYSTEMTIME *system_time); + +gpointer +mono_w32file_find_first (const gunichar2 *pattern, WIN32_FIND_DATA *find_data); + +gboolean +mono_w32file_find_next (gpointer handle, WIN32_FIND_DATA *find_data); + +gboolean +mono_w32file_find_close (gpointer handle); + +gboolean +mono_w32file_create_directory (const gunichar2 *name); + +gboolean +mono_w32file_remove_directory (const gunichar2 *name); + +guint32 +mono_w32file_get_attributes (const gunichar2 *name); + +gboolean +mono_w32file_get_attributes_ex (const gunichar2 *name, MonoIOStat *stat); + +gboolean +mono_w32file_set_attributes (const gunichar2 *name, guint32 attrs); + +guint32 +mono_w32file_get_cwd (guint32 length, gunichar2 *buffer); + +gboolean +mono_w32file_set_cwd (const gunichar2 *path); + +gboolean +mono_w32file_create_pipe (gpointer *readpipe, gpointer *writepipe, guint32 size); + +gint32 +mono_w32file_get_logical_drive (guint32 len, gunichar2 *buf); + +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); + +guint32 +mono_w32file_get_drive_type (const gunichar2 *root_path_name); + +gboolean +mono_w32file_get_volume_information (const gunichar2 *path, gunichar2 *volumename, gint volumesize, gint *outserial, gint *maxcomp, gint *fsflags, gunichar2 *fsbuffer, gint fsbuffersize); + +G_END_DECLS + +#endif /* _MONO_METADATA_W32FILE_H_ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32handle-namespace.c b/unity-2019.4.24f1-mbe/mono/metadata/w32handle-namespace.c new file mode 100644 index 000000000..ab24689c3 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32handle-namespace.c @@ -0,0 +1,119 @@ +/** + * \file + * namespace for w32handles + * + * Author: + * Ludovic Henry (luhenry@microsoft.com) + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include + +#ifndef HOST_WIN32 + +#include "w32handle-namespace.h" + +#include "w32mutex.h" +#include "w32semaphore.h" +#include "w32event.h" +#include "mono/utils/mono-logger-internals.h" +#include "mono/utils/mono-coop-mutex.h" + +static MonoCoopMutex lock; + +void +mono_w32handle_namespace_init (void) +{ + mono_coop_mutex_init (&lock); +} + +void +mono_w32handle_namespace_lock (void) +{ + mono_coop_mutex_lock (&lock); +} + +void +mono_w32handle_namespace_unlock (void) +{ + mono_coop_mutex_unlock (&lock); +} + +static gboolean +has_namespace (MonoW32Type type) +{ + switch (type) { + case MONO_W32TYPE_NAMEDMUTEX: + case MONO_W32TYPE_NAMEDSEM: + case MONO_W32TYPE_NAMEDEVENT: + return TRUE; + default: + return FALSE; + } +} + +typedef struct { + gpointer ret; + MonoW32Type type; + const gchar *name; +} NamespaceSearchHandleData; + +static gboolean +mono_w32handle_namespace_search_handle_callback (MonoW32Handle *handle_data, gpointer user_data) +{ + NamespaceSearchHandleData *search_data; + MonoW32HandleNamespace *sharedns; + + if (!has_namespace (handle_data->type)) + return FALSE; + + search_data = (NamespaceSearchHandleData*) user_data; + + switch (handle_data->type) { + case MONO_W32TYPE_NAMEDMUTEX: sharedns = mono_w32mutex_get_namespace ((MonoW32HandleNamedMutex*) handle_data->specific); break; + case MONO_W32TYPE_NAMEDSEM: sharedns = mono_w32semaphore_get_namespace ((MonoW32HandleNamedSemaphore*) handle_data->specific); break; + case MONO_W32TYPE_NAMEDEVENT: sharedns = mono_w32event_get_namespace ((MonoW32HandleNamedEvent*) handle_data->specific); break; + default: + g_assert_not_reached (); + } + + if (strcmp (sharedns->name, search_data->name) == 0) { + if (handle_data->type != search_data->type) { + /* Its the wrong type, so fail now */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p matches name but is wrong type: %s", + __func__, handle_data, mono_w32handle_get_typename (handle_data->type)); + search_data->ret = INVALID_HANDLE_VALUE; + } else { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p matches name and type", + __func__, handle_data); + + /* we do not want the handle to be destroyed before we return it */ + search_data->ret = mono_w32handle_duplicate (handle_data); + } + + return TRUE; + } + + return FALSE; +} + +gpointer +mono_w32handle_namespace_search_handle (MonoW32Type type, const gchar *name) +{ + NamespaceSearchHandleData search_data; + + if (!has_namespace (type)) + g_error ("%s: type %s does not have a namespace", __func__, type); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: Lookup for handle named [%s] type %s", + __func__, name, mono_w32handle_get_typename (type)); + + search_data.ret = NULL; + search_data.type = type; + search_data.name = name; + mono_w32handle_foreach (mono_w32handle_namespace_search_handle_callback, &search_data); + return search_data.ret; +} + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32handle-namespace.h b/unity-2019.4.24f1-mbe/mono/metadata/w32handle-namespace.h new file mode 100644 index 000000000..db79fa593 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32handle-namespace.h @@ -0,0 +1,31 @@ +/** + * \file + */ + +#ifndef _MONO_METADATA_W32HANDLE_NAMESPACE_H_ +#define _MONO_METADATA_W32HANDLE_NAMESPACE_H_ + +#include +#include + +#include "mono/metadata/w32handle.h" + +#define MONO_W32HANDLE_NAMESPACE_MAX_PATH 260 + +typedef struct { + gchar name [MONO_W32HANDLE_NAMESPACE_MAX_PATH + 1]; +} MonoW32HandleNamespace; + +void +mono_w32handle_namespace_init (void); + +void +mono_w32handle_namespace_lock (void); + +void +mono_w32handle_namespace_unlock (void); + +gpointer +mono_w32handle_namespace_search_handle (MonoW32Type type, const gchar *name); + +#endif /* _MONO_METADATA_W32HANDLE_NAMESPACE_H_ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32handle.c b/unity-2019.4.24f1-mbe/mono/metadata/w32handle.c new file mode 100644 index 000000000..7ba74ef23 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32handle.c @@ -0,0 +1,1201 @@ +/** + * \file + * Generic and internal operations on handles + * + * Author: + * Dick Porter (dick@ximian.com) + * Ludovic Henry (luhenry@microsoft.com) + * + * (C) 2002-2011 Novell, Inc. + * Copyright 2011 Xamarin Inc + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#include + +#include "w32handle.h" + +#include "utils/atomic.h" +#include "utils/mono-logger-internals.h" +#include "utils/mono-proclib.h" +#include "utils/mono-threads.h" +#include "utils/mono-time.h" + +#undef DEBUG_REFS + +#define SLOT_MAX (1024 * 32) + +/* must be a power of 2 */ +#define HANDLE_PER_SLOT (256) + +static MonoW32HandleCapability handle_caps [MONO_W32TYPE_COUNT]; +static MonoW32HandleOps *handle_ops [MONO_W32TYPE_COUNT]; + +/* + * We can hold SLOT_MAX * HANDLE_PER_SLOT handles. + * If 4M handles are not enough... Oh, well... we will crash. + */ +#define SLOT_INDEX(x) (x / HANDLE_PER_SLOT) +#define SLOT_OFFSET(x) (x % HANDLE_PER_SLOT) + +static MonoW32Handle **private_handles; +static guint32 private_handles_size = 0; + +/* + * This is an internal handle which is used for handling waiting for multiple handles. + * Threads which wait for multiple handles wait on this one handle, and when a handle + * is signalled, this handle is signalled too. + */ +static MonoCoopMutex global_signal_mutex; +static MonoCoopCond global_signal_cond; + +static MonoCoopMutex scan_mutex; + +static gboolean shutting_down = FALSE; + +static const gchar* +mono_w32handle_ops_typename (MonoW32Type type); + +const gchar* +mono_w32handle_get_typename (MonoW32Type type) +{ + return mono_w32handle_ops_typename (type); +} + +void +mono_w32handle_set_signal_state (MonoW32Handle *handle_data, gboolean state, gboolean broadcast) +{ +#ifdef DEBUG + g_message ("%s: setting state of %p to %s (broadcast %s)", __func__, + handle_data, state?"TRUE":"FALSE", broadcast?"TRUE":"FALSE"); +#endif + + if (state) { + /* Tell everyone blocking on a single handle */ + + /* The condition the global signal cond is waiting on is the signalling of + * _any_ handle. So lock it before setting the signalled state. + */ + mono_coop_mutex_lock (&global_signal_mutex); + + /* This function _must_ be called with + * handle->signal_mutex locked + */ + handle_data->signalled = TRUE; + + if (broadcast) + mono_coop_cond_broadcast (&handle_data->signal_cond); + else + mono_coop_cond_signal (&handle_data->signal_cond); + + /* Tell everyone blocking on multiple handles that something + * was signalled + */ + mono_coop_cond_broadcast (&global_signal_cond); + + mono_coop_mutex_unlock (&global_signal_mutex); + } else { + handle_data->signalled = FALSE; + } +} + +gboolean +mono_w32handle_issignalled (MonoW32Handle *handle_data) +{ + return handle_data->signalled; +} + +static void +mono_w32handle_set_in_use (MonoW32Handle *handle_data, gboolean in_use) +{ + handle_data->in_use = in_use; +} + +static void +mono_w32handle_lock_signal_mutex (void) +{ +#ifdef DEBUG + g_message ("%s: lock global signal mutex", __func__); +#endif + + mono_coop_mutex_lock (&global_signal_mutex); +} + +static void +mono_w32handle_unlock_signal_mutex (void) +{ +#ifdef DEBUG + g_message ("%s: unlock global signal mutex", __func__); +#endif + + mono_coop_mutex_unlock (&global_signal_mutex); +} + +void +mono_w32handle_lock (MonoW32Handle *handle_data) +{ + mono_coop_mutex_lock (&handle_data->signal_mutex); +} + +gboolean +mono_w32handle_trylock (MonoW32Handle *handle_data) +{ + return mono_coop_mutex_trylock (&handle_data->signal_mutex) == 0; +} + +void +mono_w32handle_unlock (MonoW32Handle *handle_data) +{ + mono_coop_mutex_unlock (&handle_data->signal_mutex); +} + +void +mono_w32handle_init (void) +{ + static gboolean initialized = FALSE; + + if (initialized) + return; + + mono_coop_mutex_init (&scan_mutex); + + mono_coop_cond_init (&global_signal_cond); + mono_coop_mutex_init (&global_signal_mutex); + + private_handles = g_new0 (MonoW32Handle*, SLOT_MAX); + + initialized = TRUE; +} + +void +mono_w32handle_cleanup (void) +{ + int i; + + g_assert (!shutting_down); + shutting_down = TRUE; + + for (i = 0; i < SLOT_MAX; ++i) + g_free (private_handles [i]); + + g_free (private_handles); +} + +static gsize +mono_w32handle_ops_typesize (MonoW32Type type); + +/* + * mono_w32handle_new_internal: + * @type: Init handle to this type + * + * Search for a free handle and initialize it. Return the handle on + * success and 0 on failure. This is only called from + * mono_w32handle_new, and scan_mutex must be held. + */ +static MonoW32Handle* +mono_w32handle_new_internal (MonoW32Type type, gpointer handle_specific) +{ + guint32 i, k, count; + static guint32 last = 0; + gboolean retry = FALSE; + + /* A linear scan should be fast enough. Start from the last + * allocation, assuming that handles are allocated more often + * than they're freed. Leave the space reserved for file + * descriptors + */ + + if (last == 0) { + /* We need to go from 1 since a handle of value 0 can be considered invalid in managed code */ + last = 1; + } else { + retry = TRUE; + } + +again: + count = last; + for(i = SLOT_INDEX (count); i < private_handles_size; i++) { + if (private_handles [i]) { + for (k = SLOT_OFFSET (count); k < HANDLE_PER_SLOT; k++) { + MonoW32Handle *handle_data = &private_handles [i][k]; + + if (handle_data->type == MONO_W32TYPE_UNUSED) { + last = count + 1; + + g_assert (handle_data->ref == 0); + + handle_data->type = type; + handle_data->signalled = FALSE; + handle_data->ref = 1; + + mono_coop_cond_init (&handle_data->signal_cond); + mono_coop_mutex_init (&handle_data->signal_mutex); + + if (handle_specific) + handle_data->specific = g_memdup (handle_specific, mono_w32handle_ops_typesize (type)); + + return handle_data; + } + count++; + } + } + } + + if (retry) { + /* Try again from the beginning */ + last = 1; + retry = FALSE; + goto again; + } + + /* Will need to expand the array. The caller will sort it out */ + + return GINT_TO_POINTER (-1); +} + +gpointer +mono_w32handle_new (MonoW32Type type, gpointer handle_specific) +{ + MonoW32Handle *handle_data; + + g_assert (!shutting_down); + + mono_coop_mutex_lock (&scan_mutex); + + while ((handle_data = mono_w32handle_new_internal (type, handle_specific)) == GINT_TO_POINTER (-1)) { + /* Try and expand the array, and have another go */ + if (private_handles_size >= SLOT_MAX) { + mono_coop_mutex_unlock (&scan_mutex); + + /* We ran out of slots */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: failed to create %s handle", __func__, mono_w32handle_ops_typename (type)); + return INVALID_HANDLE_VALUE; + } + + private_handles [private_handles_size ++] = g_new0 (MonoW32Handle, HANDLE_PER_SLOT); + } + + mono_coop_mutex_unlock (&scan_mutex); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: create %s handle %p", __func__, mono_w32handle_ops_typename (type), handle_data); + + return (gpointer) handle_data; +} + +static gboolean +mono_w32handle_ref_core (MonoW32Handle *handle_data); + +static gboolean +mono_w32handle_unref_core (MonoW32Handle *handle_data); + +static void +w32handle_destroy (MonoW32Handle *handle_data); + +gpointer +mono_w32handle_duplicate (MonoW32Handle *handle_data) +{ + if (!mono_w32handle_ref_core (handle_data)) + g_error ("%s: unknown handle %p", __func__, handle_data); + + return (gpointer) handle_data; +} + +gboolean +mono_w32handle_close (gpointer handle) +{ + MonoW32Handle *handle_data; + gboolean destroy; + + if (handle == INVALID_HANDLE_VALUE) + return FALSE; + + handle_data = (MonoW32Handle*) handle; + + if (handle_data->type == MONO_W32TYPE_UNUSED) + return FALSE; + + destroy = mono_w32handle_unref_core (handle_data); + if (destroy) + w32handle_destroy (handle_data); + + return TRUE; +} + +gboolean +mono_w32handle_lookup_and_ref (gpointer handle, MonoW32Handle **handle_data) +{ + g_assert (handle_data); + + if (handle == INVALID_HANDLE_VALUE) + return FALSE; + + *handle_data = (MonoW32Handle*) handle; + + if (!mono_w32handle_ref_core (*handle_data)) + return FALSE; + + if ((*handle_data)->type == MONO_W32TYPE_UNUSED) { + mono_w32handle_unref_core (*handle_data); + return FALSE; + } + + return TRUE; +} + +void +mono_w32handle_foreach (gboolean (*on_each)(MonoW32Handle *handle_data, gpointer user_data), gpointer user_data) +{ + GPtrArray *handles_to_destroy; + guint32 i, k; + + handles_to_destroy = NULL; + + mono_coop_mutex_lock (&scan_mutex); + + for (i = SLOT_INDEX (0); i < private_handles_size; i++) { + if (!private_handles [i]) + continue; + for (k = SLOT_OFFSET (0); k < HANDLE_PER_SLOT; k++) { + MonoW32Handle *handle_data; + gboolean destroy, finished; + + handle_data = &private_handles [i][k]; + if (handle_data->type == MONO_W32TYPE_UNUSED) + continue; + + if (!mono_w32handle_ref_core (handle_data)) { + /* we are racing with mono_w32handle_unref: + * the handle ref has been decremented, but it + * hasn't yet been destroyed. */ + continue; + } + + finished = on_each (handle_data, user_data); + + /* we might have to destroy the handle here, as + * it could have been unrefed in another thread */ + destroy = mono_w32handle_unref_core (handle_data); + if (destroy) { + /* we do not destroy it while holding the scan_mutex + * lock, because w32handle_destroy also needs to take + * the lock, and it calls user code which might lead + * to a deadlock */ + if (!handles_to_destroy) + handles_to_destroy = g_ptr_array_sized_new (4); + g_ptr_array_add (handles_to_destroy, (gpointer) handle_data); + } + + if (finished) + goto done; + } + } + +done: + mono_coop_mutex_unlock (&scan_mutex); + + if (handles_to_destroy) { + for (i = 0; i < handles_to_destroy->len; ++i) + w32handle_destroy ((MonoW32Handle*) handles_to_destroy->pdata [i]); + + g_ptr_array_free (handles_to_destroy, TRUE); + } +} + +static gboolean +mono_w32handle_ref_core (MonoW32Handle *handle_data) +{ + guint old, new; + + do { + old = handle_data->ref; + if (old == 0) + return FALSE; + + new = old + 1; + } while (mono_atomic_cas_i32 ((gint32*) &handle_data->ref, (gint32)new, (gint32)old) != (gint32)old); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: ref %s handle %p, ref: %d -> %d", + __func__, mono_w32handle_ops_typename (handle_data->type), handle_data, old, new); + + return TRUE; +} + +static gboolean +mono_w32handle_unref_core (MonoW32Handle *handle_data) +{ + MonoW32Type type; + guint old, new; + + type = handle_data->type; + + do { + old = handle_data->ref; + if (!(old >= 1)) + g_error ("%s: handle %p has ref %d, it should be >= 1", __func__, handle_data, old); + + new = old - 1; + } while (mono_atomic_cas_i32 ((gint32*) &handle_data->ref, (gint32)new, (gint32)old) != (gint32)old); + + /* handle_data might contain invalid data from now on, if + * another thread is unref'ing this handle at the same time */ + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: unref %s handle %p, ref: %d -> %d destroy: %s", + __func__, mono_w32handle_ops_typename (type), handle_data, old, new, new == 0 ? "true" : "false"); + + return new == 0; +} + +static void (*_wapi_handle_ops_get_close_func (MonoW32Type type))(gpointer, gpointer); + +static void +w32handle_destroy (MonoW32Handle *handle_data) +{ + /* Need to copy the handle info, reset the slot in the + * array, and _only then_ call the close function to + * avoid race conditions (eg file descriptors being + * closed, and another file being opened getting the + * same fd racing the memset()) + */ + MonoW32Type type; + gpointer handle_specific; + void (*close_func)(gpointer, gpointer); + + g_assert (!handle_data->in_use); + + type = handle_data->type; + handle_specific = handle_data->specific; + + mono_coop_mutex_lock (&scan_mutex); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: destroy %s handle %p", __func__, mono_w32handle_ops_typename (type), handle_data); + + mono_coop_mutex_destroy (&handle_data->signal_mutex); + mono_coop_cond_destroy (&handle_data->signal_cond); + + memset (handle_data, 0, sizeof (MonoW32Handle)); + + mono_coop_mutex_unlock (&scan_mutex); + + close_func = _wapi_handle_ops_get_close_func (type); + if (close_func != NULL) { + close_func (handle_data, handle_specific); + } + + memset (handle_specific, 0, mono_w32handle_ops_typesize (type)); + + g_free (handle_specific); +} + +/* The handle must not be locked on entry to this function */ +void +mono_w32handle_unref (MonoW32Handle *handle_data) +{ + gboolean destroy; + + destroy = mono_w32handle_unref_core (handle_data); + if (destroy) + w32handle_destroy (handle_data); +} + +void +mono_w32handle_register_ops (MonoW32Type type, MonoW32HandleOps *ops) +{ + handle_ops [type] = ops; +} + +void +mono_w32handle_register_capabilities (MonoW32Type type, MonoW32HandleCapability caps) +{ + handle_caps[type] = caps; +} + +static gboolean +mono_w32handle_test_capabilities (MonoW32Handle *handle_data, MonoW32HandleCapability caps) +{ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: testing 0x%x against 0x%x (%d)", __func__, + handle_caps[handle_data->type], caps, handle_caps[handle_data->type] & caps); + + return (handle_caps [handle_data->type] & caps) != 0; +} + +static void (*_wapi_handle_ops_get_close_func (MonoW32Type type))(gpointer, gpointer) +{ + if (handle_ops[type] != NULL && + handle_ops[type]->close != NULL) { + return (handle_ops[type]->close); + } + + return (NULL); +} + +static void +mono_w32handle_ops_details (MonoW32Handle *handle_data) +{ + if (handle_ops [handle_data->type] && handle_ops [handle_data->type]->details != NULL) + handle_ops [handle_data->type]->details (handle_data); +} + +static const gchar* +mono_w32handle_ops_typename (MonoW32Type type) +{ + g_assert (handle_ops [type]); + g_assert (handle_ops [type]->typename); + return handle_ops [type]->typename (); +} + +static gsize +mono_w32handle_ops_typesize (MonoW32Type type) +{ + g_assert (handle_ops [type]); + g_assert (handle_ops [type]->typesize); + return handle_ops [type]->typesize (); +} + +static void +mono_w32handle_ops_signal (MonoW32Handle *handle_data) +{ + if (handle_ops [handle_data->type] && handle_ops [handle_data->type]->signal) + handle_ops [handle_data->type]->signal (handle_data); +} + +static gboolean +mono_w32handle_ops_own (MonoW32Handle *handle_data, gboolean *abandoned) +{ + if (handle_ops [handle_data->type] && handle_ops [handle_data->type]->own_handle) + return handle_ops [handle_data->type]->own_handle (handle_data, abandoned); + + return FALSE; +} + +static gboolean +mono_w32handle_ops_isowned (MonoW32Handle *handle_data) +{ + if (handle_ops [handle_data->type] && handle_ops [handle_data->type]->is_owned) + return handle_ops [handle_data->type]->is_owned (handle_data); + + return FALSE; +} + +static MonoW32HandleWaitRet +mono_w32handle_ops_specialwait (MonoW32Handle *handle_data, guint32 timeout, gboolean *alerted) +{ + if (handle_ops [handle_data->type] && handle_ops [handle_data->type]->special_wait) + return handle_ops [handle_data->type]->special_wait (handle_data, timeout, alerted); + + return MONO_W32HANDLE_WAIT_RET_FAILED; +} + +static void +mono_w32handle_ops_prewait (MonoW32Handle *handle_data) +{ + if (handle_ops [handle_data->type] && handle_ops [handle_data->type]->prewait) + handle_ops [handle_data->type]->prewait (handle_data); +} + +static void +mono_w32handle_lock_handles (MonoW32Handle **handles_data, gsize nhandles) +{ + gint i, j, iter = 0; +#ifndef HOST_WIN32 + struct timespec sleepytime; +#endif + + /* Lock all the handles, with backoff */ +again: + for (i = 0; i < nhandles; i++) { + if (!mono_w32handle_trylock (handles_data [i])) { + /* Bummer */ + + for (j = i - 1; j >= 0; j--) + mono_w32handle_unlock (handles_data [j]); + + iter += 10; + if (iter == 1000) + iter = 10; + +#ifdef HOST_WIN32 + SleepEx (iter, TRUE); +#else + /* If iter ever reaches 1000 the nanosleep will + * return EINVAL immediately, but we have a + * design flaw if that happens. */ + g_assert (iter < 1000); + + sleepytime.tv_sec = 0; + sleepytime.tv_nsec = iter * 1000000; + nanosleep (&sleepytime, NULL); +#endif /* HOST_WIN32 */ + + goto again; + } + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: Locked all handles", __func__); +} + +static void +mono_w32handle_unlock_handles (MonoW32Handle **handles_data, gsize nhandles) +{ + gint i; + + for (i = nhandles - 1; i >= 0; i--) + mono_w32handle_unlock (handles_data [i]); +} + +static int +mono_w32handle_timedwait_signal_naked (MonoCoopCond *cond, MonoCoopMutex *mutex, guint32 timeout, gboolean poll, gboolean *alerted) +{ + int res; + + if (!poll) { + res = mono_coop_cond_timedwait (cond, mutex, timeout); + } else { + /* This is needed when waiting for process handles */ + if (!alerted) { + /* + * pthread_cond_(timed)wait() can return 0 even if the condition was not + * signalled. This happens at least on Darwin. We surface this, i.e., we + * get spurious wake-ups. + * + * http://pubs.opengroup.org/onlinepubs/007908775/xsh/pthread_cond_wait.html + */ + res = mono_coop_cond_timedwait (cond, mutex, timeout); + } else { + if (timeout < 100) { + /* Real timeout is less than 100ms time */ + res = mono_coop_cond_timedwait (cond, mutex, timeout); + } else { + res = mono_coop_cond_timedwait (cond, mutex, 100); + + /* Mask the fake timeout, this will cause + * another poll if the cond was not really signaled + */ + if (res == -1) + res = 0; + } + } + } + + return res; +} + +static void +signal_global (gpointer unused) +{ + /* If we reach here, then interrupt token is set to the flag value, which + * means that the target thread is either + * - before the first CAS in timedwait, which means it won't enter the wait. + * - it is after the first CAS, so it is already waiting, or it will enter + * the wait, and it will be interrupted by the broadcast. */ + mono_coop_mutex_lock (&global_signal_mutex); + mono_coop_cond_broadcast (&global_signal_cond); + mono_coop_mutex_unlock (&global_signal_mutex); +} + +static int +mono_w32handle_timedwait_signal (guint32 timeout, gboolean poll, gboolean *alerted) +{ + int res; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: waiting for global", __func__); + + if (alerted) + *alerted = FALSE; + + if (alerted) { + mono_thread_info_install_interrupt (signal_global, NULL, alerted); + if (*alerted) + return 0; + } + + res = mono_w32handle_timedwait_signal_naked (&global_signal_cond, &global_signal_mutex, timeout, poll, alerted); + + if (alerted) + mono_thread_info_uninstall_interrupt (alerted); + + return res; +} + +static void +signal_handle_and_unref (gpointer handle_duplicate) +{ + MonoW32Handle *handle_data; + MonoCoopCond *cond; + MonoCoopMutex *mutex; + + if (!mono_w32handle_lookup_and_ref (handle_duplicate, &handle_data)) + g_error ("%s: unknown handle %p", __func__, handle_duplicate); + + /* If we reach here, then interrupt token is set to the flag value, which + * means that the target thread is either + * - before the first CAS in timedwait, which means it won't enter the wait. + * - it is after the first CAS, so it is already waiting, or it will enter + * the wait, and it will be interrupted by the broadcast. */ + cond = &handle_data->signal_cond; + mutex = &handle_data->signal_mutex; + + mono_coop_mutex_lock (mutex); + mono_coop_cond_broadcast (cond); + mono_coop_mutex_unlock (mutex); + + mono_w32handle_unref (handle_data); + + mono_w32handle_close (handle_duplicate); +} + +static int +mono_w32handle_timedwait_signal_handle (MonoW32Handle *handle_data, guint32 timeout, gboolean poll, gboolean *alerted) +{ + gpointer handle_duplicate; + int res; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: waiting for %p (type %s)", __func__, handle_data, + mono_w32handle_ops_typename (handle_data->type)); + + if (alerted) + *alerted = FALSE; + + if (alerted) { + mono_thread_info_install_interrupt (signal_handle_and_unref, handle_duplicate = mono_w32handle_duplicate (handle_data), alerted); + if (*alerted) { + mono_w32handle_close (handle_duplicate); + return 0; + } + } + + res = mono_w32handle_timedwait_signal_naked (&handle_data->signal_cond, &handle_data->signal_mutex, timeout, poll, alerted); + + if (alerted) { + mono_thread_info_uninstall_interrupt (alerted); + if (!*alerted) { + /* if it is alerted, then the handle_duplicate is closed in the interrupt callback */ + mono_w32handle_close (handle_duplicate); + } + } + + return res; +} + +static gboolean +dump_callback (MonoW32Handle *handle_data, gpointer user_data) +{ + g_print ("%p [%7s] signalled: %5s ref: %3d ", + handle_data, mono_w32handle_ops_typename (handle_data->type), handle_data->signalled ? "true" : "false", handle_data->ref - 1 /* foreach increase ref by 1 */); + mono_w32handle_ops_details (handle_data); + g_print ("\n"); + + return FALSE; +} + +void mono_w32handle_dump (void) +{ + mono_w32handle_foreach (dump_callback, NULL); +} + +static gboolean +own_if_signalled (MonoW32Handle *handle_data, gboolean *abandoned) +{ + if (!mono_w32handle_issignalled (handle_data)) + return FALSE; + + *abandoned = FALSE; + mono_w32handle_ops_own (handle_data, abandoned); + return TRUE; +} + +static gboolean +own_if_owned (MonoW32Handle *handle_data, gboolean *abandoned) +{ + if (!mono_w32handle_ops_isowned (handle_data)) + return FALSE; + + *abandoned = FALSE; + mono_w32handle_ops_own (handle_data, abandoned); + return TRUE; +} + +MonoW32HandleWaitRet +mono_w32handle_wait_one (gpointer handle, guint32 timeout, gboolean alertable) +{ + MonoW32Handle *handle_data; + MonoW32HandleWaitRet ret; + gboolean alerted; + gint64 start; + gboolean abandoned = FALSE; + + alerted = FALSE; + + if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) + return MONO_W32HANDLE_WAIT_RET_FAILED; + + if (mono_w32handle_test_capabilities (handle_data, MONO_W32HANDLE_CAP_SPECIAL_WAIT)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p has special wait", __func__, handle_data); + + mono_w32handle_unref (handle_data); + return mono_w32handle_ops_specialwait (handle_data, timeout, alertable ? &alerted : NULL); + } + + if (!mono_w32handle_test_capabilities (handle_data, MONO_W32HANDLE_CAP_WAIT)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p can't be waited for", __func__, handle_data); + + mono_w32handle_unref (handle_data); + return MONO_W32HANDLE_WAIT_RET_FAILED; + } + + mono_w32handle_lock (handle_data); + + if (mono_w32handle_test_capabilities (handle_data, MONO_W32HANDLE_CAP_OWN)) { + if (own_if_owned (handle_data, &abandoned)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p already owned", __func__, handle_data); + + ret = abandoned ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0; + goto done; + } + } + + if (timeout != MONO_INFINITE_WAIT) + start = mono_msec_ticks (); + + mono_w32handle_set_in_use (handle_data, TRUE); + + for (;;) { + gint waited; + + if (own_if_signalled (handle_data, &abandoned)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p signalled", __func__, handle_data); + + ret = abandoned ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0; + goto done; + } + + mono_w32handle_ops_prewait (handle_data); + + if (timeout == MONO_INFINITE_WAIT) { + waited = mono_w32handle_timedwait_signal_handle (handle_data, MONO_INFINITE_WAIT, FALSE, alertable ? &alerted : NULL); + } else { + gint64 elapsed; + + elapsed = mono_msec_ticks () - start; + if (elapsed > timeout) { + ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT; + goto done; + } + + waited = mono_w32handle_timedwait_signal_handle (handle_data, timeout - elapsed, FALSE, alertable ? &alerted : NULL); + } + + if (alerted) { + ret = MONO_W32HANDLE_WAIT_RET_ALERTED; + goto done; + } + + if (waited != 0) { + ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT; + goto done; + } + } + +done: + mono_w32handle_set_in_use (handle_data, FALSE); + + mono_w32handle_unlock (handle_data); + + mono_w32handle_unref (handle_data); + + return ret; +} + +MonoW32HandleWaitRet +mono_w32handle_wait_multiple (gpointer *handles, gsize nhandles, gboolean waitall, guint32 timeout, gboolean alertable) +{ + MonoW32HandleWaitRet ret; + gboolean alerted, poll; + gint i; + gint64 start; + MonoW32Handle *handles_data [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS], *handles_data_sorted [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS]; + gboolean abandoned [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS] = {0}; + + if (nhandles == 0) + return MONO_W32HANDLE_WAIT_RET_FAILED; + + if (nhandles == 1) + return mono_w32handle_wait_one (handles [0], timeout, alertable); + + alerted = FALSE; + + if (nhandles > MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: too many handles: %zd", + __func__, nhandles); + + return MONO_W32HANDLE_WAIT_RET_FAILED; + } + + for (i = 0; i < nhandles; ++i) { + if (!mono_w32handle_lookup_and_ref (handles [i], &handles_data [i])) { + for (; i >= 0; --i) + mono_w32handle_unref (handles_data [i]); + return MONO_W32HANDLE_WAIT_RET_FAILED; + } + } + + for (i = 0; i < nhandles; ++i) { + if (!mono_w32handle_test_capabilities (handles_data[i], MONO_W32HANDLE_CAP_WAIT) + && !mono_w32handle_test_capabilities (handles_data[i], MONO_W32HANDLE_CAP_SPECIAL_WAIT)) + { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p can't be waited for", __func__, handles_data [i]); + + for (i = nhandles - 1; i >= 0; --i) + mono_w32handle_unref (handles_data [i]); + + return MONO_W32HANDLE_WAIT_RET_FAILED; + } + + handles_data_sorted [i] = handles_data [i]; + } + + qsort (handles_data_sorted, nhandles, sizeof (gpointer), g_direct_equal); + for (i = 1; i < nhandles; ++i) { + if (handles_data_sorted [i - 1] == handles_data_sorted [i]) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p is duplicated", __func__, handles_data_sorted [i]); + + for (i = nhandles - 1; i >= 0; --i) + mono_w32handle_unref (handles_data [i]); + + return MONO_W32HANDLE_WAIT_RET_FAILED; + } + } + + poll = FALSE; + for (i = 0; i < nhandles; ++i) { + if (handles_data [i]->type == MONO_W32TYPE_PROCESS) { + /* Can't wait for a process handle + another handle without polling */ + poll = TRUE; + } + } + + if (timeout != MONO_INFINITE_WAIT) + start = mono_msec_ticks (); + + for (;;) { + gsize count, lowest; + gboolean signalled; + gint waited; + + count = 0; + lowest = nhandles; + + mono_w32handle_lock_handles (handles_data, nhandles); + + for (i = 0; i < nhandles; i++) { + if ((mono_w32handle_test_capabilities (handles_data [i], MONO_W32HANDLE_CAP_OWN) && mono_w32handle_ops_isowned (handles_data [i])) + || mono_w32handle_issignalled (handles_data [i])) + { + count ++; + + if (i < lowest) + lowest = i; + } + } + + signalled = (waitall && count == nhandles) || (!waitall && count > 0); + + if (signalled) { + for (i = 0; i < nhandles; i++) { + if (own_if_signalled (handles_data [i], &abandoned [i]) && !waitall) { + /* if we are calling WaitHandle.WaitAny, .NET only owns the first one; it matters for Mutex which + * throw AbandonedMutexException in case we owned it but didn't release it */ + break; + } + } + } + + mono_w32handle_unlock_handles (handles_data, nhandles); + + if (signalled) { + ret = MONO_W32HANDLE_WAIT_RET_SUCCESS_0 + lowest; + for (i = lowest; i < nhandles; i++) { + if (abandoned [i]) { + ret = MONO_W32HANDLE_WAIT_RET_ABANDONED_0 + lowest; + break; + } + } + goto done; + } + + for (i = 0; i < nhandles; i++) { + mono_w32handle_ops_prewait (handles_data [i]); + + if (mono_w32handle_test_capabilities (handles_data [i], MONO_W32HANDLE_CAP_SPECIAL_WAIT) + && !mono_w32handle_issignalled (handles_data [i])) + { + mono_w32handle_ops_specialwait (handles_data [i], 0, alertable ? &alerted : NULL); + } + } + + mono_w32handle_lock_signal_mutex (); + + if (waitall) { + signalled = TRUE; + for (i = 0; i < nhandles; ++i) { + if (!mono_w32handle_issignalled (handles_data [i])) { + signalled = FALSE; + break; + } + } + } else { + signalled = FALSE; + for (i = 0; i < nhandles; ++i) { + if (mono_w32handle_issignalled (handles_data [i])) { + signalled = TRUE; + break; + } + } + } + + waited = 0; + + if (!signalled) { + if (timeout == MONO_INFINITE_WAIT) { + waited = mono_w32handle_timedwait_signal (MONO_INFINITE_WAIT, poll, alertable ? &alerted : NULL); + } else { + gint64 elapsed; + + elapsed = mono_msec_ticks () - start; + if (elapsed > timeout) { + ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT; + + mono_w32handle_unlock_signal_mutex (); + + goto done; + } + + waited = mono_w32handle_timedwait_signal (timeout - elapsed, poll, alertable ? &alerted : NULL); + } + } + + mono_w32handle_unlock_signal_mutex (); + + if (alerted) { + ret = MONO_W32HANDLE_WAIT_RET_ALERTED; + goto done; + } + + if (waited != 0) { + ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT; + goto done; + } + } + +done: + for (i = nhandles - 1; i >= 0; i--) { + /* Unref everything we reffed above */ + mono_w32handle_unref (handles_data [i]); + } + + return ret; +} + +MonoW32HandleWaitRet +mono_w32handle_signal_and_wait (gpointer signal_handle, gpointer wait_handle, guint32 timeout, gboolean alertable) +{ + MonoW32Handle *signal_handle_data, *wait_handle_data, *handles_data [2]; + MonoW32HandleWaitRet ret; + gint64 start; + gboolean alerted; + gboolean abandoned = FALSE; + + alerted = FALSE; + + if (!mono_w32handle_lookup_and_ref (signal_handle, &signal_handle_data)) { + return MONO_W32HANDLE_WAIT_RET_FAILED; + } + if (!mono_w32handle_lookup_and_ref (wait_handle, &wait_handle_data)) { + mono_w32handle_unref (signal_handle_data); + return MONO_W32HANDLE_WAIT_RET_FAILED; + } + + if (!mono_w32handle_test_capabilities (signal_handle_data, MONO_W32HANDLE_CAP_SIGNAL)) { + mono_w32handle_unref (wait_handle_data); + mono_w32handle_unref (signal_handle_data); + return MONO_W32HANDLE_WAIT_RET_FAILED; + } + if (!mono_w32handle_test_capabilities (wait_handle_data, MONO_W32HANDLE_CAP_WAIT)) { + mono_w32handle_unref (wait_handle_data); + mono_w32handle_unref (signal_handle_data); + return MONO_W32HANDLE_WAIT_RET_FAILED; + } + + if (mono_w32handle_test_capabilities (wait_handle_data, MONO_W32HANDLE_CAP_SPECIAL_WAIT)) { + g_warning ("%s: handle %p has special wait, implement me!!", __func__, wait_handle_data); + mono_w32handle_unref (wait_handle_data); + mono_w32handle_unref (signal_handle_data); + return MONO_W32HANDLE_WAIT_RET_FAILED; + } + + handles_data [0] = wait_handle_data; + handles_data [1] = signal_handle_data; + + mono_w32handle_lock_handles (handles_data, 2); + + mono_w32handle_ops_signal (signal_handle_data); + + mono_w32handle_unlock (signal_handle_data); + + if (mono_w32handle_test_capabilities (wait_handle_data, MONO_W32HANDLE_CAP_OWN)) { + if (own_if_owned (wait_handle_data, &abandoned)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p already owned", __func__, wait_handle_data); + + ret = abandoned ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0; + goto done; + } + } + + if (timeout != MONO_INFINITE_WAIT) + start = mono_msec_ticks (); + + for (;;) { + gint waited; + + if (own_if_signalled (wait_handle_data, &abandoned)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p signalled", __func__, wait_handle_data); + + ret = abandoned ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0; + goto done; + } + + mono_w32handle_ops_prewait (wait_handle_data); + + if (timeout == MONO_INFINITE_WAIT) { + waited = mono_w32handle_timedwait_signal_handle (wait_handle_data, MONO_INFINITE_WAIT, FALSE, alertable ? &alerted : NULL); + } else { + gint64 elapsed; + + elapsed = mono_msec_ticks () - start; + if (elapsed > timeout) { + ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT; + goto done; + } + + waited = mono_w32handle_timedwait_signal_handle (wait_handle_data, timeout - elapsed, FALSE, alertable ? &alerted : NULL); + } + + if (alerted) { + ret = MONO_W32HANDLE_WAIT_RET_ALERTED; + goto done; + } + + if (waited != 0) { + ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT; + goto done; + } + } + +done: + mono_w32handle_unlock (wait_handle_data); + + mono_w32handle_unref (wait_handle_data); + mono_w32handle_unref (signal_handle_data); + + return ret; +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32handle.h b/unity-2019.4.24f1-mbe/mono/metadata/w32handle.h new file mode 100644 index 000000000..13a1cb26b --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32handle.h @@ -0,0 +1,188 @@ +/** + * \file + */ + +#ifndef _MONO_METADATA_W32HANDLE_H_ +#define _MONO_METADATA_W32HANDLE_H_ + +#include +#include + +#ifdef HOST_WIN32 +#include +#endif + +#include "mono/utils/mono-coop-mutex.h" + +#ifndef INVALID_HANDLE_VALUE +#define INVALID_HANDLE_VALUE (gpointer)-1 +#endif + +#define MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS 64 + +#ifndef MONO_INFINITE_WAIT +#define MONO_INFINITE_WAIT ((guint32) 0xFFFFFFFF) +#endif + +typedef enum { + MONO_W32TYPE_UNUSED = 0, + MONO_W32TYPE_SEM, + MONO_W32TYPE_MUTEX, + MONO_W32TYPE_EVENT, + MONO_W32TYPE_PROCESS, + MONO_W32TYPE_NAMEDMUTEX, + MONO_W32TYPE_NAMEDSEM, + MONO_W32TYPE_NAMEDEVENT, + MONO_W32TYPE_COUNT +} MonoW32Type; + +typedef struct { + MonoW32Type type; + guint ref; + gboolean signalled; + gboolean in_use; + MonoCoopMutex signal_mutex; + MonoCoopCond signal_cond; + gpointer specific; +} MonoW32Handle; + +typedef enum { + MONO_W32HANDLE_WAIT_RET_SUCCESS_0 = 0, + MONO_W32HANDLE_WAIT_RET_ABANDONED_0 = MONO_W32HANDLE_WAIT_RET_SUCCESS_0 + MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS, + MONO_W32HANDLE_WAIT_RET_ALERTED = -1, + MONO_W32HANDLE_WAIT_RET_TIMEOUT = -2, + MONO_W32HANDLE_WAIT_RET_FAILED = -3, +} MonoW32HandleWaitRet; + +typedef struct +{ + void (*close)(gpointer handle, gpointer data); + + /* mono_w32handle_signal_and_wait */ + void (*signal)(MonoW32Handle *handle_data); + + /* Called by mono_w32handle_wait_one and mono_w32handle_wait_multiple, + * with the handle locked (shared handles aren't locked.) + * Returns TRUE if ownership was established, false otherwise. + * If TRUE, *abandoned contains a status code such as + * WAIT_OBJECT_0 or WAIT_ABANDONED_0. + */ + gboolean (*own_handle)(MonoW32Handle *handle_data, gboolean *abandoned); + + /* Called by mono_w32handle_wait_one and mono_w32handle_wait_multiple, if the + * handle in question is "ownable" (ie mutexes), to see if the current + * thread already owns this handle + */ + gboolean (*is_owned)(MonoW32Handle *handle_data); + + /* Called by mono_w32handle_wait_one and mono_w32handle_wait_multiple, + * if the handle in question needs a special wait function + * instead of using the normal handle signal mechanism. + * Returns the mono_w32handle_wait_one return code. + */ + MonoW32HandleWaitRet (*special_wait)(MonoW32Handle *handle_data, guint32 timeout, gboolean *alerted); + + /* Called by mono_w32handle_wait_one and mono_w32handle_wait_multiple, + * if the handle in question needs some preprocessing before the + * signal wait. + */ + void (*prewait)(MonoW32Handle *handle_data); + + /* Called when dumping the handles */ + void (*details)(MonoW32Handle *handle_data); + + /* Called to get the name of the handle type */ + const gchar* (*typename) (void); + + /* Called to get the size of the handle type */ + gsize (*typesize) (void); +} MonoW32HandleOps; + +typedef enum { + MONO_W32HANDLE_CAP_WAIT = 0x01, + MONO_W32HANDLE_CAP_SIGNAL = 0x02, + MONO_W32HANDLE_CAP_OWN = 0x04, + MONO_W32HANDLE_CAP_SPECIAL_WAIT = 0x08, +} MonoW32HandleCapability; + +void +mono_w32handle_init (void); + +void +mono_w32handle_cleanup (void); + +void +mono_w32handle_register_ops (MonoW32Type type, MonoW32HandleOps *ops); + +gpointer +mono_w32handle_new (MonoW32Type type, gpointer handle_specific); + +gpointer +mono_w32handle_duplicate (MonoW32Handle *handle_data); + +gboolean +mono_w32handle_close (gpointer handle); + +const gchar* +mono_w32handle_get_typename (MonoW32Type type); + +gboolean +mono_w32handle_lookup_and_ref (gpointer handle, MonoW32Handle **handle_data); + +void +mono_w32handle_unref (MonoW32Handle *handle_data); + +void +mono_w32handle_foreach (gboolean (*on_each)(MonoW32Handle *handle_data, gpointer user_data), gpointer user_data); + +void +mono_w32handle_dump (void); + +void +mono_w32handle_register_capabilities (MonoW32Type type, MonoW32HandleCapability caps); + +void +mono_w32handle_set_signal_state (MonoW32Handle *handle_data, gboolean state, gboolean broadcast); + +gboolean +mono_w32handle_issignalled (MonoW32Handle *handle_data); + +void +mono_w32handle_lock (MonoW32Handle *handle_data); + +gboolean +mono_w32handle_trylock (MonoW32Handle *handle_data); + +void +mono_w32handle_unlock (MonoW32Handle *handle_data); + +MonoW32HandleWaitRet +mono_w32handle_wait_one (gpointer handle, guint32 timeout, gboolean alertable); + +MonoW32HandleWaitRet +mono_w32handle_wait_multiple (gpointer *handles, gsize nhandles, gboolean waitall, guint32 timeout, gboolean alertable); + +MonoW32HandleWaitRet +mono_w32handle_signal_and_wait (gpointer signal_handle, gpointer wait_handle, guint32 timeout, gboolean alertable); + +#ifdef HOST_WIN32 +static inline MonoW32HandleWaitRet +mono_w32handle_convert_wait_ret (guint32 res, guint32 numobjects) +{ + if (res >= WAIT_OBJECT_0 && res <= WAIT_OBJECT_0 + numobjects - 1) + return MONO_W32HANDLE_WAIT_RET_SUCCESS_0 + (res - WAIT_OBJECT_0); + else if (res >= WAIT_ABANDONED_0 && res <= WAIT_ABANDONED_0 + numobjects - 1) + return MONO_W32HANDLE_WAIT_RET_ABANDONED_0 + (res - WAIT_ABANDONED_0); + else if (res == WAIT_IO_COMPLETION) + return MONO_W32HANDLE_WAIT_RET_ALERTED; + else if (res == WAIT_TIMEOUT) + return MONO_W32HANDLE_WAIT_RET_TIMEOUT; + else if (res == WAIT_FAILED) + return MONO_W32HANDLE_WAIT_RET_FAILED; + else + g_error ("%s: unknown res value %d", __func__, res); +} +#endif + + +#endif /* _MONO_METADATA_W32HANDLE_H_ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32mutex-unity.c b/unity-2019.4.24f1-mbe/mono/metadata/w32mutex-unity.c new file mode 100644 index 000000000..f58205fca --- /dev/null +++ b/unity-2019.4.24f1-mbe/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/unity-2019.4.24f1-mbe/mono/metadata/w32mutex-unix.c b/unity-2019.4.24f1-mbe/mono/metadata/w32mutex-unix.c new file mode 100644 index 000000000..d0ede1ccb --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32mutex-unix.c @@ -0,0 +1,534 @@ +/** + * \file + * Runtime support for managed Mutex on Unix + * + * Author: + * Ludovic Henry (luhenry@microsoft.com) + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include "w32mutex.h" + +#include + +#include "w32error.h" +#include "w32handle-namespace.h" +#include "mono/metadata/object-internals.h" +#include "mono/utils/mono-logger-internals.h" +#include "mono/utils/mono-threads.h" +#include "mono/metadata/w32handle.h" + +#define MAX_PATH 260 + +typedef struct { + MonoNativeThreadId tid; + guint32 recursion; + gboolean abandoned; +} MonoW32HandleMutex; + +struct MonoW32HandleNamedMutex { + MonoW32HandleMutex m; + MonoW32HandleNamespace sharedns; +}; + +gpointer +mono_w32mutex_open (const gchar* utf8_name, gint32 right G_GNUC_UNUSED, gint32 *error); + +static void +thread_own_mutex (MonoInternalThread *internal, gpointer handle, MonoW32Handle *handle_data) +{ + /* if we are not on the current thread, there is a + * race condition when allocating internal->owned_mutexes */ + g_assert (mono_thread_internal_is_current (internal)); + + if (!internal->owned_mutexes) + internal->owned_mutexes = g_ptr_array_new (); + + g_ptr_array_add (internal->owned_mutexes, mono_w32handle_duplicate (handle_data)); +} + +static void +thread_disown_mutex (MonoInternalThread *internal, gpointer handle) +{ + gboolean removed; + + g_assert (mono_thread_internal_is_current (internal)); + + g_assert (internal->owned_mutexes); + removed = g_ptr_array_remove (internal->owned_mutexes, handle); + g_assert (removed); + + mono_w32handle_close (handle); +} + +static void +mutex_handle_signal (MonoW32Handle *handle_data) +{ + MonoW32HandleMutex *mutex_handle; + pthread_t tid; + + mutex_handle = (MonoW32HandleMutex*) handle_data->specific; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: signalling %s handle %p, tid: %p recursion: %d", + __func__, mono_w32handle_get_typename (handle_data->type), handle_data, (gpointer) mutex_handle->tid, mutex_handle->recursion); + + tid = pthread_self (); + + if (mutex_handle->abandoned) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: %s handle %p is abandoned", + __func__, mono_w32handle_get_typename (handle_data->type), handle_data); + } else if (!pthread_equal (mutex_handle->tid, tid)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: we don't own %s handle %p (owned by %ld, me %ld)", + __func__, mono_w32handle_get_typename (handle_data->type), handle_data, (long)mutex_handle->tid, (long)tid); + } else { + /* OK, we own this mutex */ + mutex_handle->recursion--; + + if (mutex_handle->recursion == 0) { + thread_disown_mutex (mono_thread_internal_current (), handle_data); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: unlocking %s handle %p, tid: %p recusion : %d", + __func__, mono_w32handle_get_typename (handle_data->type), handle_data, (gpointer) mutex_handle->tid, mutex_handle->recursion); + + mutex_handle->tid = 0; + mono_w32handle_set_signal_state (handle_data, TRUE, FALSE); + } + } +} + +static gboolean +mutex_handle_own (MonoW32Handle *handle_data, gboolean *abandoned) +{ + MonoW32HandleMutex *mutex_handle; + + *abandoned = FALSE; + + mutex_handle = (MonoW32HandleMutex*) handle_data->specific; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: owning %s handle %p, before: [tid: %p, recursion: %d], after: [tid: %p, recursion: %d], abandoned: %s", + __func__, mono_w32handle_get_typename (handle_data->type), handle_data, (gpointer) mutex_handle->tid, mutex_handle->recursion, (gpointer) pthread_self (), mutex_handle->recursion + 1, mutex_handle->abandoned ? "true" : "false"); + + if (mutex_handle->recursion != 0) { + g_assert (pthread_equal (pthread_self (), mutex_handle->tid)); + mutex_handle->recursion++; + } else { + mutex_handle->tid = pthread_self (); + mutex_handle->recursion = 1; + + thread_own_mutex (mono_thread_internal_current (), handle_data, handle_data); + } + + if (mutex_handle->abandoned) { + mutex_handle->abandoned = FALSE; + *abandoned = TRUE; + } + + mono_w32handle_set_signal_state (handle_data, FALSE, FALSE); + + return TRUE; +} + +static gboolean +mutex_handle_is_owned (MonoW32Handle *handle_data) +{ + MonoW32HandleMutex *mutex_handle; + + mutex_handle = (MonoW32HandleMutex*) handle_data->specific; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: testing ownership %s handle %p", + __func__, mono_w32handle_get_typename (handle_data->type), handle_data); + + if (mutex_handle->recursion > 0 && pthread_equal (mutex_handle->tid, pthread_self ())) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: %s handle %p owned by %p", + __func__, mono_w32handle_get_typename (handle_data->type), handle_data, (gpointer) pthread_self ()); + return TRUE; + } else { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: %s handle %p not owned by %p, tid: %p recursion: %d", + __func__, mono_w32handle_get_typename (handle_data->type), handle_data, (gpointer) pthread_self (), (gpointer) mutex_handle->tid, mutex_handle->recursion); + return FALSE; + } +} + +static void mutex_handle_prewait (MonoW32Handle *handle_data) +{ + /* If the mutex is not currently owned, do nothing and let the + * usual wait carry on. If it is owned, check that the owner + * is still alive; if it isn't we override the previous owner + * and assume that process exited abnormally and failed to + * clean up. + */ + MonoW32HandleMutex *mutex_handle; + + mutex_handle = (MonoW32HandleMutex*) handle_data->specific; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: pre-waiting %s handle %p, owned? %s", + __func__, mono_w32handle_get_typename (handle_data->type), handle_data, mutex_handle->recursion != 0 ? "true" : "false"); +} + +static void mutex_details (MonoW32Handle *handle_data) +{ + MonoW32HandleMutex *mut = (MonoW32HandleMutex *)handle_data->specific; + +#ifdef PTHREAD_POINTER_ID + g_print ("own: %5p, count: %5u", mut->tid, mut->recursion); +#else + g_print ("own: %5ld, count: %5u", mut->tid, mut->recursion); +#endif +} + +static void namedmutex_details (MonoW32Handle *handle_data) +{ + MonoW32HandleNamedMutex *namedmut = (MonoW32HandleNamedMutex *)handle_data->specific; + +#ifdef PTHREAD_POINTER_ID + g_print ("own: %5p, count: %5u, name: \"%s\"", + namedmut->m.tid, namedmut->m.recursion, namedmut->sharedns.name); +#else + g_print ("own: %5ld, count: %5u, name: \"%s\"", + namedmut->m.tid, namedmut->m.recursion, namedmut->sharedns.name); +#endif +} + +static const gchar* mutex_typename (void) +{ + return "Mutex"; +} + +static gsize mutex_typesize (void) +{ + return sizeof (MonoW32HandleMutex); +} + +static const gchar* namedmutex_typename (void) +{ + return "N.Mutex"; +} + +static gsize namedmutex_typesize (void) +{ + return sizeof (MonoW32HandleNamedMutex); +} + +void +mono_w32mutex_init (void) +{ + static MonoW32HandleOps mutex_ops = { + NULL, /* close */ + mutex_handle_signal, /* signal */ + mutex_handle_own, /* own */ + mutex_handle_is_owned, /* is_owned */ + NULL, /* special_wait */ + mutex_handle_prewait, /* prewait */ + mutex_details, /* details */ + mutex_typename, /* typename */ + mutex_typesize, /* typesize */ + }; + + static MonoW32HandleOps namedmutex_ops = { + NULL, /* close */ + mutex_handle_signal, /* signal */ + mutex_handle_own, /* own */ + mutex_handle_is_owned, /* is_owned */ + NULL, /* special_wait */ + mutex_handle_prewait, /* prewait */ + namedmutex_details, /* details */ + namedmutex_typename, /* typename */ + namedmutex_typesize, /* typesize */ + }; + + mono_w32handle_register_ops (MONO_W32TYPE_MUTEX, &mutex_ops); + mono_w32handle_register_ops (MONO_W32TYPE_NAMEDMUTEX, &namedmutex_ops); + + mono_w32handle_register_capabilities (MONO_W32TYPE_MUTEX, + (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL | MONO_W32HANDLE_CAP_OWN)); + mono_w32handle_register_capabilities (MONO_W32TYPE_NAMEDMUTEX, + (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL | MONO_W32HANDLE_CAP_OWN)); +} + +static gpointer mutex_handle_create (MonoW32HandleMutex *mutex_handle, MonoW32Type type, gboolean owned) +{ + MonoW32Handle *handle_data; + gpointer handle; + gboolean abandoned; + + mutex_handle->tid = 0; + mutex_handle->recursion = 0; + mutex_handle->abandoned = FALSE; + + handle = mono_w32handle_new (type, mutex_handle); + if (handle == INVALID_HANDLE_VALUE) { + g_warning ("%s: error creating %s handle", + __func__, mono_w32handle_get_typename (type)); + mono_w32error_set_last (ERROR_GEN_FAILURE); + return NULL; + } + + if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) + g_error ("%s: unkown handle %p", __func__, handle); + + if (handle_data->type != type) + g_error ("%s: unknown mutex handle %p", __func__, handle); + + mono_w32handle_lock (handle_data); + + if (owned) + mutex_handle_own (handle_data, &abandoned); + else + mono_w32handle_set_signal_state (handle_data, TRUE, FALSE); + + mono_w32handle_unlock (handle_data); + + /* Balance mono_w32handle_lookup_and_ref */ + mono_w32handle_unref (handle_data); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: created %s handle %p", + __func__, mono_w32handle_get_typename (type), handle); + + return handle; +} + +static gpointer mutex_create (gboolean owned) +{ + MonoW32HandleMutex mutex_handle; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: creating %s handle", + __func__, mono_w32handle_get_typename (MONO_W32TYPE_MUTEX)); + return mutex_handle_create (&mutex_handle, MONO_W32TYPE_MUTEX, owned); +} + +static gpointer namedmutex_create (gboolean owned, const gchar *utf8_name) +{ + gpointer handle; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: creating %s handle", + __func__, mono_w32handle_get_typename (MONO_W32TYPE_NAMEDMUTEX)); + + /* w32 seems to guarantee that opening named objects can't race each other */ + mono_w32handle_namespace_lock (); + + glong utf8_len = strlen (utf8_name); + + handle = mono_w32handle_namespace_search_handle (MONO_W32TYPE_NAMEDMUTEX, utf8_name); + if (handle == INVALID_HANDLE_VALUE) { + /* The name has already been used for a different object. */ + handle = NULL; + mono_w32error_set_last (ERROR_INVALID_HANDLE); + } else if (handle) { + /* Not an error, but this is how the caller is informed that the mutex wasn't freshly created */ + mono_w32error_set_last (ERROR_ALREADY_EXISTS); + + /* mono_w32handle_namespace_search_handle already adds a ref to the handle */ + } else { + /* A new named mutex */ + MonoW32HandleNamedMutex namedmutex_handle; + + size_t len = utf8_len < MAX_PATH ? utf8_len : MAX_PATH; + memcpy (&namedmutex_handle.sharedns.name [0], utf8_name, len); + namedmutex_handle.sharedns.name [len] = '\0'; + + handle = mutex_handle_create ((MonoW32HandleMutex*) &namedmutex_handle, MONO_W32TYPE_NAMEDMUTEX, owned); + } + + mono_w32handle_namespace_unlock (); + + return handle; +} + +gpointer +ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned, MonoStringHandle name, MonoBoolean *created, MonoError *error) +{ + gpointer mutex; + + error_init (error); + *created = TRUE; + + /* Need to blow away any old errors here, because code tests + * for ERROR_ALREADY_EXISTS on success (!) to see if a mutex + * was freshly created */ + mono_w32error_set_last (ERROR_SUCCESS); + + if (MONO_HANDLE_IS_NULL (name)) { + mutex = mutex_create (owned); + } else { + gchar *utf8_name = mono_string_handle_to_utf8 (name, error); + return_val_if_nok (error, NULL); + + mutex = namedmutex_create (owned, utf8_name); + + if (mono_w32error_get_last () == ERROR_ALREADY_EXISTS) + *created = FALSE; + g_free (utf8_name); + } + + return mutex; +} + +MonoBoolean +ves_icall_System_Threading_Mutex_ReleaseMutex_internal (gpointer handle) +{ + MonoW32Handle *handle_data; + MonoW32HandleMutex *mutex_handle; + pthread_t tid; + gboolean ret; + + if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) { + g_warning ("%s: unkown handle %p", __func__, handle); + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return FALSE; + } + + if (handle_data->type != MONO_W32TYPE_MUTEX && handle_data->type != MONO_W32TYPE_NAMEDMUTEX) { + g_warning ("%s: unknown mutex handle %p", __func__, handle); + mono_w32error_set_last (ERROR_INVALID_HANDLE); + mono_w32handle_unref (handle_data); + return FALSE; + } + + mutex_handle = (MonoW32HandleMutex*) handle_data->specific; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: releasing %s handle %p, tid: %p recursion: %d", + __func__, mono_w32handle_get_typename (handle_data->type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion); + + mono_w32handle_lock (handle_data); + + tid = pthread_self (); + + if (mutex_handle->abandoned) { + // The Win32 ReleaseMutex() function returns TRUE for abandoned mutexes + ret = TRUE; + } else if (!pthread_equal (mutex_handle->tid, tid)) { + ret = FALSE; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: we don't own %s handle %p (owned by %ld, me %ld)", + __func__, mono_w32handle_get_typename (handle_data->type), handle, (long)mutex_handle->tid, (long)tid); + } else { + ret = TRUE; + + /* OK, we own this mutex */ + mutex_handle->recursion--; + + if (mutex_handle->recursion == 0) { + thread_disown_mutex (mono_thread_internal_current (), handle); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: unlocking %s handle %p, tid: %p recusion : %d", + __func__, mono_w32handle_get_typename (handle_data->type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion); + + mutex_handle->tid = 0; + mono_w32handle_set_signal_state (handle_data, TRUE, FALSE); + } + } + + mono_w32handle_unlock (handle_data); + mono_w32handle_unref (handle_data); + + return ret; +} + +gpointer +ves_icall_System_Threading_Mutex_OpenMutex_internal (MonoStringHandle name, gint32 rights G_GNUC_UNUSED, gint32 *err, MonoError *error) +{ + error_init (error); + gchar *utf8_name = mono_string_handle_to_utf8 (name, error); + return_val_if_nok (error, NULL); + gpointer handle = mono_w32mutex_open (utf8_name, rights, err); + g_free (utf8_name); + return handle; +} + +gpointer +mono_w32mutex_open (const gchar* utf8_name, gint32 right G_GNUC_UNUSED, gint32 *error) +{ + gpointer handle; + + *error = ERROR_SUCCESS; + + /* w32 seems to guarantee that opening named objects can't race each other */ + mono_w32handle_namespace_lock (); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: Opening named mutex [%s]", + __func__, utf8_name); + + handle = mono_w32handle_namespace_search_handle (MONO_W32TYPE_NAMEDMUTEX, utf8_name); + if (handle == INVALID_HANDLE_VALUE) { + /* The name has already been used for a different object. */ + *error = ERROR_INVALID_HANDLE; + goto cleanup; + } else if (!handle) { + /* This name doesn't exist */ + *error = ERROR_FILE_NOT_FOUND; + goto cleanup; + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: returning named mutex handle %p", + __func__, handle); + +cleanup: + mono_w32handle_namespace_unlock (); + + return handle; +} + +void +mono_w32mutex_abandon (void) +{ + MonoInternalThread *internal; + + g_assert (mono_thread_internal_current_is_attached ()); + + internal = mono_thread_internal_current (); + g_assert (internal); + + if (!internal->owned_mutexes) + return; + + while (internal->owned_mutexes->len) { + MonoW32Handle *handle_data; + MonoW32HandleMutex *mutex_handle; + MonoNativeThreadId tid; + gpointer handle; + + handle = g_ptr_array_index (internal->owned_mutexes, 0); + + if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) + g_error ("%s: unkown handle %p", __func__, handle); + + if (handle_data->type != MONO_W32TYPE_MUTEX && handle_data->type != MONO_W32TYPE_NAMEDMUTEX) + g_error ("%s: unkown mutex handle %p", __func__, handle); + + mutex_handle = (MonoW32HandleMutex*) handle_data->specific; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: abandoning %s handle %p", + __func__, mono_w32handle_get_typename (handle_data->type), handle); + + tid = MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid); + + if (!pthread_equal (mutex_handle->tid, tid)) + g_error ("%s: trying to release mutex %p acquired by thread %p from thread %p", + __func__, handle, (gpointer) mutex_handle->tid, (gpointer) tid); + + mono_w32handle_lock (handle_data); + + mutex_handle->recursion = 0; + mutex_handle->tid = 0; + mutex_handle->abandoned = TRUE; + + mono_w32handle_set_signal_state (handle_data, TRUE, FALSE); + + thread_disown_mutex (internal, handle); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: abandoned %s handle %p", + __func__, mono_w32handle_get_typename (handle_data->type), handle); + + mono_w32handle_unlock (handle_data); + mono_w32handle_unref (handle_data); + } + + g_ptr_array_free (internal->owned_mutexes, TRUE); + internal->owned_mutexes = NULL; +} + +MonoW32HandleNamespace* +mono_w32mutex_get_namespace (MonoW32HandleNamedMutex *mutex) +{ + return &mutex->sharedns; +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32mutex-win32.c b/unity-2019.4.24f1-mbe/mono/metadata/w32mutex-win32.c new file mode 100644 index 000000000..f7a68c5c7 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32mutex-win32.c @@ -0,0 +1,79 @@ +/** + * \file + * Runtime support for managed Mutex on Win32 + * + * Author: + * Ludovic Henry (luhenry@microsoft.com) + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include "w32mutex.h" + +#include +#include +#include +#include + + +void +mono_w32mutex_init (void) +{ +} + +gpointer +ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned, MonoStringHandle name, MonoBoolean *created, MonoError *error) +{ + HANDLE mutex; + + error_init (error); + + *created = TRUE; + + if (MONO_HANDLE_IS_NULL (name)) { + MONO_ENTER_GC_SAFE; + mutex = CreateMutex (NULL, owned, NULL); + MONO_EXIT_GC_SAFE; + } else { + uint32_t gchandle; + gunichar2 *uniname = mono_string_handle_pin_chars (name, &gchandle); + MONO_ENTER_GC_SAFE; + mutex = CreateMutex (NULL, owned, uniname); + + if (GetLastError () == ERROR_ALREADY_EXISTS) + *created = FALSE; + MONO_EXIT_GC_SAFE; + mono_gchandle_free (gchandle); + } + + return mutex; +} + +MonoBoolean +ves_icall_System_Threading_Mutex_ReleaseMutex_internal (gpointer handle) +{ + return ReleaseMutex (handle); +} + +gpointer +ves_icall_System_Threading_Mutex_OpenMutex_internal (MonoStringHandle name, gint32 rights, gint32 *err, MonoError *error) +{ + HANDLE ret; + + error_init (error); + *err = ERROR_SUCCESS; + + uint32_t gchandle = 0; + gunichar2 *uniname = NULL; + if (!MONO_HANDLE_IS_NULL (name)) + uniname = mono_string_handle_pin_chars (name, &gchandle); + MONO_ENTER_GC_SAFE; + ret = OpenMutex (rights, FALSE, uniname); + if (!ret) + *err = GetLastError (); + MONO_EXIT_GC_SAFE; + if (gchandle != 0) + mono_gchandle_free (gchandle); + + return ret; +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32mutex.h b/unity-2019.4.24f1-mbe/mono/metadata/w32mutex.h new file mode 100644 index 000000000..375731591 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32mutex.h @@ -0,0 +1,37 @@ +/** + * \file + */ + +#ifndef _MONO_METADATA_W32MUTEX_H_ +#define _MONO_METADATA_W32MUTEX_H_ + +#include +#include + +#include "object.h" +#include "object-internals.h" +#include "w32handle-namespace.h" + +void +mono_w32mutex_init (void); + +gpointer +ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned, MonoStringHandle name, MonoBoolean *created, MonoError *error); + +MonoBoolean +ves_icall_System_Threading_Mutex_ReleaseMutex_internal (gpointer handle); + +gpointer +ves_icall_System_Threading_Mutex_OpenMutex_internal (MonoStringHandle name, gint32 rights, gint32 *err, MonoError *error); + +typedef struct MonoW32HandleNamedMutex MonoW32HandleNamedMutex; + +MonoW32HandleNamespace* +mono_w32mutex_get_namespace (MonoW32HandleNamedMutex *mutex); + +#ifndef HOST_WIN32 +void +mono_w32mutex_abandon (void); +#endif + +#endif /* _MONO_METADATA_W32MUTEX_H_ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32process-internals.h b/unity-2019.4.24f1-mbe/mono/metadata/w32process-internals.h new file mode 100644 index 000000000..c8e3dd014 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32process-internals.h @@ -0,0 +1,68 @@ +/** + * \file + */ + +#ifndef _MONO_METADATA_W32PROCESS_INTERNALS_H_ +#define _MONO_METADATA_W32PROCESS_INTERNALS_H_ + +#include +#include + +#ifndef HOST_WIN32 + +typedef struct { + guint32 dwSignature; /* Should contain 0xFEEF04BD on le machines */ + guint32 dwStrucVersion; + guint32 dwFileVersionMS; + guint32 dwFileVersionLS; + guint32 dwProductVersionMS; + guint32 dwProductVersionLS; + guint32 dwFileFlagsMask; + guint32 dwFileFlags; + guint32 dwFileOS; + guint32 dwFileType; + guint32 dwFileSubtype; + guint32 dwFileDateMS; + guint32 dwFileDateLS; +} VS_FIXEDFILEINFO; + +typedef struct { + gpointer lpBaseOfDll; + guint32 SizeOfImage; + gpointer EntryPoint; +} MODULEINFO; + +#define VS_FF_DEBUG 0x0001 +#define VS_FF_PRERELEASE 0x0002 +#define VS_FF_PATCHED 0x0004 +#define VS_FF_PRIVATEBUILD 0x0008 +#define VS_FF_INFOINFERRED 0x0010 +#define VS_FF_SPECIALBUILD 0x0020 + +guint32 +mono_w32process_get_pid (gpointer handle); + +gboolean +mono_w32process_try_get_modules (gpointer process, gpointer *modules, guint32 size, guint32 *needed); + +guint32 +mono_w32process_module_get_name (gpointer process, gpointer module, gunichar2 *basename, guint32 size); + +guint32 +mono_w32process_module_get_filename (gpointer process, gpointer module, gunichar2 *basename, guint32 size); + +gboolean +mono_w32process_module_get_information (gpointer process, gpointer module, MODULEINFO *modinfo, guint32 size); + +gboolean +mono_w32process_get_fileversion_info (gunichar2 *filename, gpointer *data); + +gboolean +mono_w32process_ver_query_value (gconstpointer datablock, const gunichar2 *subblock, gpointer *buffer, guint32 *len); + +guint32 +mono_w32process_ver_language_name (guint32 lang, gunichar2 *lang_out, guint32 lang_len); + +#endif /* HOST_WIN32 */ + +#endif /* _MONO_METADATA_W32PROCESS_INTERNALS_H_ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32process-unity.c b/unity-2019.4.24f1-mbe/mono/metadata/w32process-unity.c new file mode 100644 index 000000000..0b79fe252 --- /dev/null +++ b/unity-2019.4.24f1-mbe/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/unity-2019.4.24f1-mbe/mono/metadata/w32process-unix-bsd.c b/unity-2019.4.24f1-mbe/mono/metadata/w32process-unix-bsd.c new file mode 100644 index 000000000..34eac0459 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32process-unix-bsd.c @@ -0,0 +1,154 @@ +/** + * \file + */ + +#include "w32process.h" +#include "w32process-unix-internals.h" + +#ifdef USE_BSD_BACKEND + +#include +#include +#include +#if !defined(__OpenBSD__) +#include +#endif +#if defined(__FreeBSD__) +#include /* struct kinfo_proc */ +#endif + +#include + +#include "utils/mono-logger-internals.h" + +gchar* +mono_w32process_get_name (pid_t pid) +{ + gint mib [6]; + gsize size; + struct kinfo_proc *pi; + gchar *ret = NULL; + +#if defined(__FreeBSD__) + mib [0] = CTL_KERN; + mib [1] = KERN_PROC; + mib [2] = KERN_PROC_PID; + mib [3] = pid; + if (sysctl(mib, 4, NULL, &size, NULL, 0) < 0) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: sysctl() failed: %d", __func__, errno); + return NULL; + } + + if ((pi = g_malloc (size)) == NULL) + return NULL; + + if (sysctl (mib, 4, pi, &size, NULL, 0) < 0) { + if (errno == ENOMEM) { + g_free (pi); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Didn't allocate enough memory for kproc info", __func__); + } + return NULL; + } + + ret = strlen (pi->ki_comm) > 0 ? g_strdup (pi->ki_comm) : NULL; + + g_free (pi); +#elif defined(__OpenBSD__) + mib [0] = CTL_KERN; + mib [1] = KERN_PROC; + mib [2] = KERN_PROC_PID; + mib [3] = pid; + mib [4] = sizeof(struct kinfo_proc); + mib [5] = 0; + +retry: + if (sysctl(mib, 6, NULL, &size, NULL, 0) < 0) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: sysctl() failed: %d", __func__, errno); + return NULL; + } + + if ((pi = g_malloc (size)) == NULL) + return NULL; + + mib[5] = (int)(size / sizeof(struct kinfo_proc)); + + if ((sysctl (mib, 6, pi, &size, NULL, 0) < 0) || + (size != sizeof (struct kinfo_proc))) { + if (errno == ENOMEM) { + g_free (pi); + goto retry; + } + return NULL; + } + + ret = strlen (pi->p_comm) > 0 ? g_strdup (pi->p_comm) : NULL; + + g_free (pi); +#endif + + return ret; +} + +gchar* +mono_w32process_get_path (pid_t pid) +{ + return mono_w32process_get_name (pid); +} + +static gint +mono_w32process_get_modules_callback (struct dl_phdr_info *info, gsize size, gpointer ptr) +{ + if (size < offsetof (struct dl_phdr_info, dlpi_phnum) + sizeof (info->dlpi_phnum)) + return (-1); + + struct dl_phdr_info *cpy = g_calloc (1, sizeof(struct dl_phdr_info)); + if (!cpy) + return (-1); + + memcpy(cpy, info, sizeof(*info)); + + g_ptr_array_add ((GPtrArray *)ptr, cpy); + + return (0); +} + +GSList* +mono_w32process_get_modules (pid_t pid) +{ + GSList *ret = NULL; + MonoW32ProcessModule *mod; + GPtrArray *dlarray = g_ptr_array_new(); + gint i; + + if (dl_iterate_phdr (mono_w32process_get_modules_callback, dlarray) < 0) + return NULL; + + for (i = 0; i < dlarray->len; i++) { + struct dl_phdr_info *info = g_ptr_array_index (dlarray, i); + + mod = g_new0 (MonoW32ProcessModule, 1); + mod->address_start = (gpointer)(info->dlpi_addr + info->dlpi_phdr[0].p_vaddr); + mod->address_end = (gpointer)(info->dlpi_addr + info->dlpi_phdr[info->dlpi_phnum - 1].p_vaddr); + mod->perms = g_strdup ("r--p"); + mod->address_offset = 0; + mod->inode = i; + mod->filename = g_strdup (info->dlpi_name); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: inode=%d, filename=%s, address_start=%p, address_end=%p", + __func__, mod->inode, mod->filename, mod->address_start, mod->address_end); + + g_free (info); + + if (g_slist_find_custom (ret, mod, mono_w32process_module_equals) == NULL) { + ret = g_slist_prepend (ret, mod); + } else { + mono_w32process_module_free (mod); + } + } + + g_ptr_array_free (dlarray, TRUE); + + return g_slist_reverse (ret); +} + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32process-unix-default.c b/unity-2019.4.24f1-mbe/mono/metadata/w32process-unix-default.c new file mode 100644 index 000000000..0ea952a0c --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32process-unix-default.c @@ -0,0 +1,268 @@ +/** + * \file + */ + +#include "w32process.h" +#include "w32process-unix-internals.h" + +#ifdef USE_DEFAULT_BACKEND + +#include + +#ifdef HOST_SOLARIS +/* procfs.h cannot be included if this define is set, but it seems to work fine if it is undefined */ +#if _FILE_OFFSET_BITS == 64 +#undef _FILE_OFFSET_BITS +#include +#define _FILE_OFFSET_BITS 64 +#else +#include +#endif +#endif + +/* makedev() macro */ +#ifdef MAJOR_IN_MKDEV +#include +#elif defined MAJOR_IN_SYSMACROS +#include +#endif + +#include "utils/mono-logger-internals.h" + +#ifndef MAXPATHLEN +#define MAXPATHLEN 242 +#endif + +gchar* +mono_w32process_get_name (pid_t pid) +{ + FILE *fp; + gchar *filename; + gchar buf[256]; + gchar *ret = NULL; + +#if defined(HOST_SOLARIS) + filename = g_strdup_printf ("/proc/%d/psinfo", pid); + if ((fp = fopen (filename, "r")) != NULL) { + struct psinfo info; + int nread; + + nread = fread (&info, sizeof (info), 1, fp); + if (nread == 1) { + ret = g_strdup (info.pr_fname); + } + + fclose (fp); + } + g_free (filename); +#else + memset (buf, '\0', sizeof(buf)); + filename = g_strdup_printf ("/proc/%d/exe", pid); + if (readlink (filename, buf, 255) > 0) { + ret = g_strdup (buf); + } + g_free (filename); + + if (ret != NULL) { + return(ret); + } + + filename = g_strdup_printf ("/proc/%d/cmdline", pid); + if ((fp = fopen (filename, "r")) != NULL) { + if (fgets (buf, 256, fp) != NULL) { + ret = g_strdup (buf); + } + + fclose (fp); + } + g_free (filename); + + if (ret != NULL) { + return(ret); + } + + filename = g_strdup_printf ("/proc/%d/stat", pid); + if ((fp = fopen (filename, "r")) != NULL) { + if (fgets (buf, 256, fp) != NULL) { + char *start, *end; + + start = strchr (buf, '('); + if (start != NULL) { + end = strchr (start + 1, ')'); + + if (end != NULL) { + ret = g_strndup (start + 1, + end - start - 1); + } + } + } + + fclose (fp); + } + g_free (filename); +#endif + + return ret; +} + +gchar* +mono_w32process_get_path (pid_t pid) +{ + return mono_w32process_get_name (pid); +} + +static FILE * +open_process_map (int pid, const char *mode) +{ + gint i; + const gchar *proc_path[] = { + "/proc/%d/maps", /* GNU/Linux */ + "/proc/%d/map", /* FreeBSD */ + NULL + }; + + for (i = 0; proc_path [i]; i++) { + gchar *filename; + FILE *fp; + + filename = g_strdup_printf (proc_path[i], pid); + fp = fopen (filename, mode); + g_free (filename); + + if (fp) + return fp; + } + + return NULL; +} + + +GSList* +mono_w32process_get_modules (pid_t pid) +{ + GSList *ret = NULL; + FILE *fp; + MonoW32ProcessModule *mod; + gchar buf[MAXPATHLEN + 1], *p, *endp; + gchar *start_start, *end_start, *prot_start, *offset_start; + gchar *maj_dev_start, *min_dev_start, *inode_start, prot_buf[5]; + gpointer address_start, address_end, address_offset; + guint32 maj_dev, min_dev; + guint64 inode; + guint64 device; + + fp = open_process_map (pid, "r"); + if (!fp) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Can't open process map file for pid %d", __func__, pid); + return NULL; + } + + while (fgets (buf, sizeof(buf), fp)) { + p = buf; + while (g_ascii_isspace (*p)) ++p; + start_start = p; + if (!g_ascii_isxdigit (*start_start)) { + continue; + } + address_start = (gpointer)strtoul (start_start, &endp, 16); + p = endp; + if (*p != '-') { + continue; + } + + ++p; + end_start = p; + if (!g_ascii_isxdigit (*end_start)) { + continue; + } + address_end = (gpointer)strtoul (end_start, &endp, 16); + p = endp; + if (!g_ascii_isspace (*p)) { + continue; + } + + while (g_ascii_isspace (*p)) ++p; + prot_start = p; + if (*prot_start != 'r' && *prot_start != '-') { + continue; + } + memcpy (prot_buf, prot_start, 4); + prot_buf[4] = '\0'; + while (!g_ascii_isspace (*p)) ++p; + + while (g_ascii_isspace (*p)) ++p; + offset_start = p; + if (!g_ascii_isxdigit (*offset_start)) { + continue; + } + address_offset = (gpointer)strtoul (offset_start, &endp, 16); + p = endp; + if (!g_ascii_isspace (*p)) { + continue; + } + + while(g_ascii_isspace (*p)) ++p; + maj_dev_start = p; + if (!g_ascii_isxdigit (*maj_dev_start)) { + continue; + } + maj_dev = strtoul (maj_dev_start, &endp, 16); + p = endp; + if (*p != ':') { + continue; + } + + ++p; + min_dev_start = p; + if (!g_ascii_isxdigit (*min_dev_start)) { + continue; + } + min_dev = strtoul (min_dev_start, &endp, 16); + p = endp; + if (!g_ascii_isspace (*p)) { + continue; + } + + while (g_ascii_isspace (*p)) ++p; + inode_start = p; + if (!g_ascii_isxdigit (*inode_start)) { + continue; + } + inode = (guint64)strtol (inode_start, &endp, 10); + p = endp; + if (!g_ascii_isspace (*p)) { + continue; + } + + device = makedev ((int)maj_dev, (int)min_dev); + if ((device == 0) && (inode == 0)) { + continue; + } + + while(g_ascii_isspace (*p)) ++p; + /* p now points to the filename */ + + mod = g_new0 (MonoW32ProcessModule, 1); + mod->address_start = address_start; + mod->address_end = address_end; + mod->perms = g_strdup (prot_buf); + mod->address_offset = address_offset; + mod->device = device; + mod->inode = inode; + mod->filename = g_strdup (g_strstrip (p)); + + if (g_slist_find_custom (ret, mod, mono_w32process_module_equals) == NULL) { + ret = g_slist_prepend (ret, mod); + } else { + mono_w32process_module_free (mod); + } + } + + ret = g_slist_reverse (ret); + + fclose (fp); + + return(ret); +} + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32process-unix-haiku.c b/unity-2019.4.24f1-mbe/mono/metadata/w32process-unix-haiku.c new file mode 100644 index 000000000..ec028ad7b --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32process-unix-haiku.c @@ -0,0 +1,60 @@ +/** + * \file + */ + +#include "w32process.h" +#include "w32process-unix-internals.h" + +#ifdef USE_HAIKU_BACKEND + +/* KernelKit.h doesn't include the right headers? */ +#include + +gchar* +mono_w32process_get_name (pid_t pid) +{ + image_info imageInfo; + int32 cookie = 0; + + if (get_next_image_info ((team_id) pid, &cookie, &imageInfo) != B_OK) + return NULL; + + return g_strdup (imageInfo.name); +} + +gchar* +mono_w32process_get_path (pid_t pid) +{ + return mono_w32process_get_name (pid); +} + +GSList* +mono_w32process_get_modules (pid_t pid) +{ + GSList *ret = NULL; + MonoW32ProcessModule *mod; + gint32 cookie = 0; + image_info imageInfo; + + while (get_next_image_info (B_CURRENT_TEAM, &cookie, &imageInfo) == B_OK) { + mod = g_new0 (MonoW32ProcessModule, 1); + mod->device = imageInfo.device; + mod->inode = imageInfo.node; + mod->filename = g_strdup (imageInfo.name); + mod->address_start = MIN (imageInfo.text, imageInfo.data); + mod->address_end = MAX ((uint8_t*)imageInfo.text + imageInfo.text_size, + (uint8_t*)imageInfo.data + imageInfo.data_size); + mod->perms = g_strdup ("r--p"); + mod->address_offset = 0; + + if (g_slist_find_custom (ret, mod, mono_w32process_module_equals) == NULL) { + ret = g_slist_prepend (ret, mod); + } else { + mono_w32process_module_free (mod); + } + } + + return g_slist_reverse (ret); +} + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32process-unix-internals.h b/unity-2019.4.24f1-mbe/mono/metadata/w32process-unix-internals.h new file mode 100644 index 000000000..ecab6c5ae --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32process-unix-internals.h @@ -0,0 +1,62 @@ +/** + * \file + */ + +#ifndef _MONO_METADATA_W32PROCESS_UNIX_INTERNALS_H_ +#define _MONO_METADATA_W32PROCESS_UNIX_INTERNALS_H_ + +#include +#include + +/* + * FOR EXCLUSIVE USE BY w32process-unix.c + */ + +#if defined(HOST_DARWIN) +#define USE_OSX_BACKEND +#elif (defined(__OpenBSD__) || defined(__FreeBSD__)) && defined(HAVE_LINK_H) +#define USE_BSD_BACKEND +#elif defined(__HAIKU__) +#define USE_HAIKU_BACKEND +/* Define header for team_info */ +#include +#else +#define USE_DEFAULT_BACKEND +#endif + +typedef struct { + gpointer address_start; + gpointer address_end; + gchar *perms; + gpointer address_offset; + guint64 device; + guint64 inode; + gchar *filename; +} MonoW32ProcessModule; + +gchar* +mono_w32process_get_name (pid_t pid); + +GSList* +mono_w32process_get_modules (pid_t pid); + +static void +mono_w32process_module_free (MonoW32ProcessModule *module) +{ + g_free (module->perms); + g_free (module->filename); + g_free (module); +} + +/* + * Used to look through the GSList* returned by mono_w32process_get_modules + */ +static gint +mono_w32process_module_equals (gconstpointer a, gconstpointer b) +{ + MonoW32ProcessModule *want = (MonoW32ProcessModule *)a; + MonoW32ProcessModule *compare = (MonoW32ProcessModule *)b; + return (want->device == compare->device && want->inode == compare->inode) ? 0 : 1; +} + +#endif /* _MONO_METADATA_W32PROCESS_UNIX_INTERNALS_H_ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32process-unix-osx.c b/unity-2019.4.24f1-mbe/mono/metadata/w32process-unix-osx.c new file mode 100644 index 000000000..790fd0283 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32process-unix-osx.c @@ -0,0 +1,173 @@ +/** + * \file + */ + +#include "w32process.h" +#include "w32process-unix-internals.h" + +#ifdef USE_OSX_BACKEND + +#include +#include +#include +#include +#include +#include +#include +#include + +/* sys/resource.h (for rusage) is required when using osx 10.3 (but not 10.4) */ +#ifdef __APPLE__ +#include +#include +#ifdef HAVE_LIBPROC_H +/* proc_name */ +#include +#endif +#endif + +#include "utils/mono-logger-internals.h" + +gchar* +mono_w32process_get_name (pid_t pid) +{ + gchar *ret = NULL; + +#if defined (__mono_ppc__) || !defined (TARGET_OSX) + size_t size; + struct kinfo_proc *pi; + gint mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid }; + + if (sysctl(mib, 4, NULL, &size, NULL, 0) < 0) + return(ret); + + if ((pi = g_malloc (size)) == NULL) + return(ret); + + if (sysctl (mib, 4, pi, &size, NULL, 0) < 0) { + if (errno == ENOMEM) { + g_free (pi); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Didn't allocate enough memory for kproc info", __func__); + } + return(ret); + } + + if (strlen (pi->kp_proc.p_comm) > 0) + ret = g_strdup (pi->kp_proc.p_comm); + + g_free (pi); +#else + gchar buf[256]; + gint res; + + /* No proc name on OSX < 10.5 nor ppc nor iOS */ + memset (buf, '\0', sizeof(buf)); + res = proc_name (pid, buf, sizeof(buf)); + if (res == 0) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: proc_name failed, error (%d) \"%s\"", __func__, errno, g_strerror (errno)); + return NULL; + } + + // Fixes proc_name triming values to 15 characters #32539 + if (strlen (buf) >= MAXCOMLEN - 1) { + gchar path_buf [PROC_PIDPATHINFO_MAXSIZE]; + gchar *name_buf; + gint path_len; + + memset (path_buf, '\0', sizeof(path_buf)); + path_len = proc_pidpath (pid, path_buf, sizeof(path_buf)); + + if (path_len > 0 && path_len < sizeof(path_buf)) { + name_buf = path_buf + path_len; + for(;name_buf > path_buf; name_buf--) { + if (name_buf [0] == '/') { + name_buf++; + break; + } + } + + if (memcmp (buf, name_buf, MAXCOMLEN - 1) == 0) + ret = g_strdup (name_buf); + } + } + + if (ret == NULL && strlen (buf) > 0) + ret = g_strdup (buf); +#endif + + return ret; +} + +gchar* +mono_w32process_get_path (pid_t pid) +{ +#if defined(__mono_ppc__) || !defined(TARGET_OSX) + return mono_w32process_get_name (pid); +#else + gchar buf [PROC_PIDPATHINFO_MAXSIZE]; + gint res; + + res = proc_pidpath (pid, buf, sizeof (buf)); + if (res <= 0) + return NULL; + if (buf [0] == '\0') + return NULL; + return g_strdup (buf); +#endif +} + +GSList* +mono_w32process_get_modules (pid_t pid) +{ + GSList *ret = NULL; + MonoW32ProcessModule *mod; + guint32 count; + int i = 0; + + if (pid != getpid ()) + return NULL; + + count = _dyld_image_count (); + for (i = 0; i < count; i++) { +#if SIZEOF_VOID_P == 8 + const struct mach_header_64 *hdr; + const struct section_64 *sec; +#else + const struct mach_header *hdr; + const struct section *sec; +#endif + const char *name; + + name = _dyld_get_image_name (i); +#if SIZEOF_VOID_P == 8 + hdr = (const struct mach_header_64*)_dyld_get_image_header (i); + sec = getsectbynamefromheader_64 (hdr, SEG_DATA, SECT_DATA); +#else + hdr = _dyld_get_image_header (i); + sec = getsectbynamefromheader (hdr, SEG_DATA, SECT_DATA); +#endif + + /* Some dynlibs do not have data sections on osx (#533893) */ + if (sec == 0) + continue; + + mod = g_new0 (MonoW32ProcessModule, 1); + mod->address_start = GINT_TO_POINTER (sec->addr); + mod->address_end = GINT_TO_POINTER (sec->addr+sec->size); + mod->perms = g_strdup ("r--p"); + mod->address_offset = 0; + mod->device = makedev (0, 0); + mod->inode = i; + mod->filename = g_strdup (name); + + if (g_slist_find_custom (ret, mod, mono_w32process_module_equals) == NULL) { + ret = g_slist_prepend (ret, mod); + } else { + mono_w32process_module_free (mod); + } + } + + return g_slist_reverse (ret); +} + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32process-unix.c b/unity-2019.4.24f1-mbe/mono/metadata/w32process-unix.c new file mode 100644 index 000000000..e2100d266 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32process-unix.c @@ -0,0 +1,4463 @@ +/** + * \file + * System.Diagnostics.Process support + * + * Author: + * Dick Porter (dick@ximian.com) + * + * Copyright 2002 Ximian, Inc. + * Copyright 2002-2006 Novell, Inc. + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SIGNAL_H +#include +#endif +#include +#include +#ifdef HAVE_SYS_PARAM_H +#include +#endif +#include + +#ifdef HAVE_SYS_WAIT_H +#include +#endif +#ifdef HAVE_SYS_RESOURCE_H +#include +#endif + +#ifdef HAVE_SYS_MKDEV_H +#include +#endif + +#ifdef HAVE_UTIME_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef MAXPATHLEN +#define MAXPATHLEN 242 +#endif + +#define STILL_ACTIVE ((int) 0x00000103) + +#define LOGDEBUG(...) +/* define LOGDEBUG(...) g_message(__VA_ARGS__) */ + +/* The process' environment strings */ +#if defined(__APPLE__) +#if defined (TARGET_OSX) +/* Apple defines this in crt_externs.h but doesn't provide that header for + * arm-apple-darwin9. We'll manually define the symbol on Apple as it does + * in fact exist on all implementations (so far) + */ +gchar ***_NSGetEnviron(void); +#define environ (*_NSGetEnviron()) +#else +static char *mono_environ[1] = { NULL }; +#define environ mono_environ +#endif /* defined (TARGET_OSX) */ +#else +extern char **environ; +#endif + +typedef enum { + STARTF_USESHOWWINDOW=0x001, + STARTF_USESIZE=0x002, + STARTF_USEPOSITION=0x004, + STARTF_USECOUNTCHARS=0x008, + STARTF_USEFILLATTRIBUTE=0x010, + STARTF_RUNFULLSCREEN=0x020, + STARTF_FORCEONFEEDBACK=0x040, + STARTF_FORCEOFFFEEDBACK=0x080, + STARTF_USESTDHANDLES=0x100 +} StartupFlags; + +typedef struct { + gpointer input; + gpointer output; + gpointer error; +} StartupHandles; + +typedef struct { +#if G_BYTE_ORDER == G_BIG_ENDIAN + guint32 highDateTime; + guint32 lowDateTime; +#else + guint32 lowDateTime; + guint32 highDateTime; +#endif +} ProcessTime; + +/* + * Process describes processes we create. + * It contains a semaphore that can be waited on in order to wait + * for process termination. + */ +typedef struct _Process { + pid_t pid; /* the pid of the process. This value is only valid until the process has exited. */ + MonoCoopSem exit_sem; /* this semaphore will be released when the process exits */ + int status; /* the exit status */ + gint32 handle_count; /* the number of handles to this process instance */ + /* we keep a ref to the creating _WapiHandle_process handle until + * the process has exited, so that the information there isn't lost. + */ + gpointer handle; + gboolean signalled; + struct _Process *next; +} Process; + +/* MonoW32HandleProcess is a structure containing all the required information for process handling. */ +typedef struct { + pid_t pid; + gboolean child; + guint32 exitstatus; + gpointer main_thread; + guint64 create_time; + guint64 exit_time; + char *pname; + size_t min_working_set; + size_t max_working_set; + gboolean exited; + Process *process; +} MonoW32HandleProcess; + +/* + * VS_VERSIONINFO: + * + * 2 bytes: Length in bytes (this block, and all child blocks. does _not_ include alignment padding between blocks) + * 2 bytes: Length in bytes of VS_FIXEDFILEINFO struct + * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data) + * Variable length unicode string (null terminated): Key (currently "VS_VERSION_INFO") + * Variable length padding to align VS_FIXEDFILEINFO on a 32-bit boundary + * VS_FIXEDFILEINFO struct + * Variable length padding to align Child struct on a 32-bit boundary + * Child struct (zero or one StringFileInfo structs, zero or one VarFileInfo structs) + */ + +/* + * StringFileInfo: + * + * 2 bytes: Length in bytes (includes this block, as well as all Child blocks) + * 2 bytes: Value length (always zero) + * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data) + * Variable length unicode string: Key (currently "StringFileInfo") + * Variable length padding to align Child struct on a 32-bit boundary + * Child structs ( one or more StringTable structs. Each StringTable struct's Key member indicates the appropriate language and code page for displaying the text in that StringTable struct.) + */ + +/* + * StringTable: + * + * 2 bytes: Length in bytes (includes this block as well as all Child blocks, but excludes any padding between String blocks) + * 2 bytes: Value length (always zero) + * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data) + * Variable length unicode string: Key. An 8-digit hex number stored as a unicode string. The four most significant digits represent the language identifier. The four least significant digits represent the code page for which the data is formatted. + * Variable length padding to align Child struct on a 32-bit boundary + * Child structs (an array of one or more String structs (each aligned on a 32-bit boundary) + */ + +/* + * String: + * + * 2 bytes: Length in bytes (of this block) + * 2 bytes: Value length (the length in words of the Value member) + * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data) + * Variable length unicode string: Key. arbitrary string, identifies data. + * Variable length padding to align Value on a 32-bit boundary + * Value: Variable length unicode string, holding data. + */ + +/* + * VarFileInfo: + * + * 2 bytes: Length in bytes (includes this block, as well as all Child blocks) + * 2 bytes: Value length (always zero) + * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data) + * Variable length unicode string: Key (currently "VarFileInfo") + * Variable length padding to align Child struct on a 32-bit boundary + * Child structs (a Var struct) + */ + +/* + * Var: + * + * 2 bytes: Length in bytes of this block + * 2 bytes: Value length in bytes of the Value + * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data) + * Variable length unicode string: Key ("Translation") + * Variable length padding to align Value on a 32-bit boundary + * Value: an array of one or more 4 byte values that are language and code page identifier pairs, low-order word containing a language identifier, and the high-order word containing a code page number. Either word can be zero, indicating that the file is language or code page independent. + */ + +#if G_BYTE_ORDER == G_BIG_ENDIAN +#define VS_FFI_SIGNATURE 0xbd04effe +#define VS_FFI_STRUCVERSION 0x00000100 +#else +#define VS_FFI_SIGNATURE 0xfeef04bd +#define VS_FFI_STRUCVERSION 0x00010000 +#endif + +#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 + +#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 +#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 +#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 + +#define IMAGE_SIZEOF_SHORT_NAME 8 + +#if G_BYTE_ORDER != G_LITTLE_ENDIAN +#define IMAGE_DOS_SIGNATURE 0x4d5a +#define IMAGE_NT_SIGNATURE 0x50450000 +#define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0xb10 +#define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0xb20 +#else +#define IMAGE_DOS_SIGNATURE 0x5a4d +#define IMAGE_NT_SIGNATURE 0x00004550 +#define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b +#define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b +#endif + +typedef struct { + guint16 e_magic; + guint16 e_cblp; + guint16 e_cp; + guint16 e_crlc; + guint16 e_cparhdr; + guint16 e_minalloc; + guint16 e_maxalloc; + guint16 e_ss; + guint16 e_sp; + guint16 e_csum; + guint16 e_ip; + guint16 e_cs; + guint16 e_lfarlc; + guint16 e_ovno; + guint16 e_res[4]; + guint16 e_oemid; + guint16 e_oeminfo; + guint16 e_res2[10]; + guint32 e_lfanew; +} IMAGE_DOS_HEADER; + +typedef struct { + guint16 Machine; + guint16 NumberOfSections; + guint32 TimeDateStamp; + guint32 PointerToSymbolTable; + guint32 NumberOfSymbols; + guint16 SizeOfOptionalHeader; + guint16 Characteristics; +} IMAGE_FILE_HEADER; + +typedef struct { + guint32 VirtualAddress; + guint32 Size; +} IMAGE_DATA_DIRECTORY; + +typedef struct { + guint16 Magic; + guint8 MajorLinkerVersion; + guint8 MinorLinkerVersion; + guint32 SizeOfCode; + guint32 SizeOfInitializedData; + guint32 SizeOfUninitializedData; + guint32 AddressOfEntryPoint; + guint32 BaseOfCode; + guint32 BaseOfData; + guint32 ImageBase; + guint32 SectionAlignment; + guint32 FileAlignment; + guint16 MajorOperatingSystemVersion; + guint16 MinorOperatingSystemVersion; + guint16 MajorImageVersion; + guint16 MinorImageVersion; + guint16 MajorSubsystemVersion; + guint16 MinorSubsystemVersion; + guint32 Win32VersionValue; + guint32 SizeOfImage; + guint32 SizeOfHeaders; + guint32 CheckSum; + guint16 Subsystem; + guint16 DllCharacteristics; + guint32 SizeOfStackReserve; + guint32 SizeOfStackCommit; + guint32 SizeOfHeapReserve; + guint32 SizeOfHeapCommit; + guint32 LoaderFlags; + guint32 NumberOfRvaAndSizes; + IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; +} IMAGE_OPTIONAL_HEADER32; + +typedef struct { + guint16 Magic; + guint8 MajorLinkerVersion; + guint8 MinorLinkerVersion; + guint32 SizeOfCode; + guint32 SizeOfInitializedData; + guint32 SizeOfUninitializedData; + guint32 AddressOfEntryPoint; + guint32 BaseOfCode; + guint64 ImageBase; + guint32 SectionAlignment; + guint32 FileAlignment; + guint16 MajorOperatingSystemVersion; + guint16 MinorOperatingSystemVersion; + guint16 MajorImageVersion; + guint16 MinorImageVersion; + guint16 MajorSubsystemVersion; + guint16 MinorSubsystemVersion; + guint32 Win32VersionValue; + guint32 SizeOfImage; + guint32 SizeOfHeaders; + guint32 CheckSum; + guint16 Subsystem; + guint16 DllCharacteristics; + guint64 SizeOfStackReserve; + guint64 SizeOfStackCommit; + guint64 SizeOfHeapReserve; + guint64 SizeOfHeapCommit; + guint32 LoaderFlags; + guint32 NumberOfRvaAndSizes; + IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; +} IMAGE_OPTIONAL_HEADER64; + +#if SIZEOF_VOID_P == 8 +typedef IMAGE_OPTIONAL_HEADER64 IMAGE_OPTIONAL_HEADER; +#else +typedef IMAGE_OPTIONAL_HEADER32 IMAGE_OPTIONAL_HEADER; +#endif + +typedef struct { + guint32 Signature; + IMAGE_FILE_HEADER FileHeader; + IMAGE_OPTIONAL_HEADER32 OptionalHeader; +} IMAGE_NT_HEADERS32; + +typedef struct { + guint32 Signature; + IMAGE_FILE_HEADER FileHeader; + IMAGE_OPTIONAL_HEADER64 OptionalHeader; +} IMAGE_NT_HEADERS64; + +#if SIZEOF_VOID_P == 8 +typedef IMAGE_NT_HEADERS64 IMAGE_NT_HEADERS; +#else +typedef IMAGE_NT_HEADERS32 IMAGE_NT_HEADERS; +#endif + +typedef struct { + guint8 Name[IMAGE_SIZEOF_SHORT_NAME]; + union { + guint32 PhysicalAddress; + guint32 VirtualSize; + } Misc; + guint32 VirtualAddress; + guint32 SizeOfRawData; + guint32 PointerToRawData; + guint32 PointerToRelocations; + guint32 PointerToLinenumbers; + guint16 NumberOfRelocations; + guint16 NumberOfLinenumbers; + guint32 Characteristics; +} IMAGE_SECTION_HEADER; + +#define IMAGE_FIRST_SECTION32(header) ((IMAGE_SECTION_HEADER *)((gsize)(header) + G_STRUCT_OFFSET (IMAGE_NT_HEADERS32, OptionalHeader) + GUINT16_FROM_LE (((IMAGE_NT_HEADERS32 *)(header))->FileHeader.SizeOfOptionalHeader))) + +#define RT_CURSOR 0x01 +#define RT_BITMAP 0x02 +#define RT_ICON 0x03 +#define RT_MENU 0x04 +#define RT_DIALOG 0x05 +#define RT_STRING 0x06 +#define RT_FONTDIR 0x07 +#define RT_FONT 0x08 +#define RT_ACCELERATOR 0x09 +#define RT_RCDATA 0x0a +#define RT_MESSAGETABLE 0x0b +#define RT_GROUP_CURSOR 0x0c +#define RT_GROUP_ICON 0x0e +#define RT_VERSION 0x10 +#define RT_DLGINCLUDE 0x11 +#define RT_PLUGPLAY 0x13 +#define RT_VXD 0x14 +#define RT_ANICURSOR 0x15 +#define RT_ANIICON 0x16 +#define RT_HTML 0x17 +#define RT_MANIFEST 0x18 + +typedef struct { + guint32 Characteristics; + guint32 TimeDateStamp; + guint16 MajorVersion; + guint16 MinorVersion; + guint16 NumberOfNamedEntries; + guint16 NumberOfIdEntries; +} IMAGE_RESOURCE_DIRECTORY; + +typedef struct { + union { + struct { +#if G_BYTE_ORDER == G_BIG_ENDIAN + guint32 NameIsString:1; + guint32 NameOffset:31; +#else + guint32 NameOffset:31; + guint32 NameIsString:1; +#endif + }; + guint32 Name; +#if G_BYTE_ORDER == G_BIG_ENDIAN + struct { + guint16 __wapi_big_endian_padding; + guint16 Id; + }; +#else + guint16 Id; +#endif + }; + union { + guint32 OffsetToData; + struct { +#if G_BYTE_ORDER == G_BIG_ENDIAN + guint32 DataIsDirectory:1; + guint32 OffsetToDirectory:31; +#else + guint32 OffsetToDirectory:31; + guint32 DataIsDirectory:1; +#endif + }; + }; +} IMAGE_RESOURCE_DIRECTORY_ENTRY; + +typedef struct { + guint32 OffsetToData; + guint32 Size; + guint32 CodePage; + guint32 Reserved; +} IMAGE_RESOURCE_DATA_ENTRY; + +#define VOS_UNKNOWN 0x00000000 +#define VOS_DOS 0x00010000 +#define VOS_OS216 0x00020000 +#define VOS_OS232 0x00030000 +#define VOS_NT 0x00040000 +#define VOS__BASE 0x00000000 +#define VOS__WINDOWS16 0x00000001 +#define VOS__PM16 0x00000002 +#define VOS__PM32 0x00000003 +#define VOS__WINDOWS32 0x00000004 +/* Should "embrace and extend" here with some entries for linux etc */ + +#define VOS_DOS_WINDOWS16 0x00010001 +#define VOS_DOS_WINDOWS32 0x00010004 +#define VOS_OS216_PM16 0x00020002 +#define VOS_OS232_PM32 0x00030003 +#define VOS_NT_WINDOWS32 0x00040004 + +#define VFT_UNKNOWN 0x0000 +#define VFT_APP 0x0001 +#define VFT_DLL 0x0002 +#define VFT_DRV 0x0003 +#define VFT_FONT 0x0004 +#define VFT_VXD 0x0005 +#define VFT_STATIC_LIB 0x0007 + +#define VFT2_UNKNOWN 0x0000 +#define VFT2_DRV_PRINTER 0x0001 +#define VFT2_DRV_KEYBOARD 0x0002 +#define VFT2_DRV_LANGUAGE 0x0003 +#define VFT2_DRV_DISPLAY 0x0004 +#define VFT2_DRV_MOUSE 0x0005 +#define VFT2_DRV_NETWORK 0x0006 +#define VFT2_DRV_SYSTEM 0x0007 +#define VFT2_DRV_INSTALLABLE 0x0008 +#define VFT2_DRV_SOUND 0x0009 +#define VFT2_DRV_COMM 0x000a +#define VFT2_DRV_INPUTMETHOD 0x000b +#define VFT2_FONT_RASTER 0x0001 +#define VFT2_FONT_VECTOR 0x0002 +#define VFT2_FONT_TRUETYPE 0x0003 + +#define MAKELANGID(primary,secondary) ((guint16)((secondary << 10) | (primary))) + +#define ALIGN32(ptr) ptr = (gpointer)((char *)ptr + 3); ptr = (gpointer)((char *)ptr - ((gsize)ptr & 3)); + +#if HAVE_SIGACTION +static mono_lazy_init_t process_sig_chld_once = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED; +#endif + +static gchar *cli_launcher; + +static Process *processes; +static MonoCoopMutex processes_mutex; + +static pid_t current_pid; +static gpointer current_process; + +static const gunichar2 utf16_space_bytes [2] = { 0x20, 0 }; +static const gunichar2 *utf16_space = utf16_space_bytes; +static const gunichar2 utf16_quote_bytes [2] = { 0x22, 0 }; +static const gunichar2 *utf16_quote = utf16_quote_bytes; + +/* Check if a pid is valid - i.e. if a process exists with this pid. */ +static gboolean +process_is_alive (pid_t pid) +{ +#if defined(HOST_WATCHOS) + return TRUE; // TODO: Rewrite using sysctl +#elif defined(HOST_DARWIN) || defined(__OpenBSD__) || defined(__FreeBSD__) + if (pid == 0) + return FALSE; + if (kill (pid, 0) == 0) + return TRUE; + if (errno == EPERM) + return TRUE; + return FALSE; +#elif defined(__HAIKU__) + team_info teamInfo; + if (get_team_info ((team_id)pid, &teamInfo) == B_OK) + return TRUE; + return FALSE; +#else + gchar *dir = g_strdup_printf ("/proc/%d", pid); + gboolean result = access (dir, F_OK) == 0; + g_free (dir); + return result; +#endif +} + +static void +process_details (MonoW32Handle *handle_data) +{ + MonoW32HandleProcess *process_handle = (MonoW32HandleProcess *) handle_data->specific; + g_print ("pid: %d, exited: %s, exitstatus: %d", + process_handle->pid, process_handle->exited ? "true" : "false", process_handle->exitstatus); +} + +static const gchar* +process_typename (void) +{ + return "Process"; +} + +static gsize +process_typesize (void) +{ + return sizeof (MonoW32HandleProcess); +} + +static MonoW32HandleWaitRet +process_wait (MonoW32Handle *handle_data, guint32 timeout, gboolean *alerted) +{ + MonoW32HandleProcess *process_handle; + pid_t pid G_GNUC_UNUSED, ret; + int status; + gint64 start, now; + Process *process; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s (%p, %" G_GUINT32_FORMAT ")", __func__, handle_data, timeout); + + if (alerted) + *alerted = FALSE; + + process_handle = (MonoW32HandleProcess*) handle_data->specific; + + if (process_handle->exited) { + /* We've already done this one */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s (%p, %" G_GUINT32_FORMAT "): Process already exited", __func__, handle_data, timeout); + return MONO_W32HANDLE_WAIT_RET_SUCCESS_0; + } + + pid = process_handle->pid; + + if (pid == mono_process_current_pid ()) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s (%p, %" G_GUINT32_FORMAT "): waiting on current process", __func__, handle_data, timeout); + return MONO_W32HANDLE_WAIT_RET_TIMEOUT; + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s (%p, %" G_GUINT32_FORMAT "): PID: %d", __func__, handle_data, timeout, pid); + + if (!process_handle->child) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s (%p, %" G_GUINT32_FORMAT "): waiting on non-child process", __func__, handle_data, timeout); + + if (!process_is_alive (pid)) { + /* assume the process has exited */ + process_handle->exited = TRUE; + process_handle->exitstatus = -1; + mono_w32handle_set_signal_state (handle_data, TRUE, TRUE); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s (%p, %" G_GUINT32_FORMAT "): non-child process is not alive anymore (2)", __func__, handle_data, timeout); + return MONO_W32HANDLE_WAIT_RET_SUCCESS_0; + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s (%p, %" G_GUINT32_FORMAT "): non-child process wait failed, error : %s (%d))", __func__, handle_data, timeout, g_strerror (errno), errno); + return MONO_W32HANDLE_WAIT_RET_FAILED; + } + + /* We don't need to lock processes here, the entry + * has a handle_count > 0 which means it will not be freed. */ + process = process_handle->process; + g_assert (process); + + start = mono_msec_ticks (); + now = start; + + while (1) { + if (timeout != MONO_INFINITE_WAIT) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s (%p, %" G_GUINT32_FORMAT "): waiting on semaphore for %" G_GINT64_FORMAT " ms...", + __func__, handle_data, timeout, timeout - (now - start)); + ret = mono_coop_sem_timedwait (&process->exit_sem, (timeout - (now - start)), alerted ? MONO_SEM_FLAGS_ALERTABLE : MONO_SEM_FLAGS_NONE); + } else { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s (%p, %" G_GUINT32_FORMAT "): waiting on semaphore forever...", + __func__, handle_data, timeout); + ret = mono_coop_sem_wait (&process->exit_sem, alerted ? MONO_SEM_FLAGS_ALERTABLE : MONO_SEM_FLAGS_NONE); + } + + if (ret == MONO_SEM_TIMEDWAIT_RET_SUCCESS) { + /* Success, process has exited */ + mono_coop_sem_post (&process->exit_sem); + break; + } + + if (ret == MONO_SEM_TIMEDWAIT_RET_TIMEDOUT) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s (%p, %" G_GUINT32_FORMAT "): wait timeout (timeout = 0)", __func__, handle_data, timeout); + return MONO_W32HANDLE_WAIT_RET_TIMEOUT; + } + + now = mono_msec_ticks (); + if (now - start >= timeout) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s (%p, %" G_GUINT32_FORMAT "): wait timeout", __func__, handle_data, timeout); + return MONO_W32HANDLE_WAIT_RET_TIMEOUT; + } + + if (alerted && ret == MONO_SEM_TIMEDWAIT_RET_ALERTED) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s (%p, %" G_GUINT32_FORMAT "): wait alerted", __func__, handle_data, timeout); + *alerted = TRUE; + return MONO_W32HANDLE_WAIT_RET_ALERTED; + } + } + + /* Process must have exited */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s (%p, %" G_GUINT32_FORMAT "): Waited successfully", __func__, handle_data, timeout); + + status = process->status; + if (WIFSIGNALED (status)) + process_handle->exitstatus = 128 + WTERMSIG (status); + else + process_handle->exitstatus = WEXITSTATUS (status); + + process_handle->exit_time = mono_100ns_datetime (); + + process_handle->exited = TRUE; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s (%p, %" G_GUINT32_FORMAT "): Setting pid %d signalled, exit status %d", + __func__, handle_data, timeout, process_handle->pid, process_handle->exitstatus); + + mono_w32handle_set_signal_state (handle_data, TRUE, TRUE); + + return MONO_W32HANDLE_WAIT_RET_SUCCESS_0; +} + +static void +processes_cleanup (void) +{ + static gint32 cleaning_up; + Process *process; + Process *prev = NULL; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s", __func__); + + /* Ensure we're not in here in multiple threads at once, nor recursive. */ + if (mono_atomic_cas_i32 (&cleaning_up, 1, 0) != 0) + return; + + /* + * This needs to be done outside the lock but atomically, hence the CAS above. + */ + for (process = processes; process; process = process->next) { + if (process->signalled && process->handle) { + /* This process has exited and we need to remove the artifical ref + * on the handle */ + mono_w32handle_close (process->handle); + process->handle = NULL; + } + } + + mono_coop_mutex_lock (&processes_mutex); + + for (process = processes; process;) { + Process *next = process->next; + if (process->handle_count == 0 && process->signalled) { + /* + * Unlink the entry. + */ + if (process == processes) + processes = process->next; + else + prev->next = process->next; + + mono_coop_sem_destroy (&process->exit_sem); + g_free (process); + } else { + prev = process; + } + process = next; + } + + mono_coop_mutex_unlock (&processes_mutex); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s done", __func__); + + mono_atomic_xchg_i32 (&cleaning_up, 0); +} + +static void +process_close (gpointer handle, gpointer data) +{ + MonoW32HandleProcess *process_handle; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s", __func__); + + process_handle = (MonoW32HandleProcess *) data; + g_free (process_handle->pname); + process_handle->pname = NULL; + if (process_handle->process) + mono_atomic_dec_i32 (&process_handle->process->handle_count); + processes_cleanup (); +} + +static MonoW32HandleOps process_ops = { + process_close, /* close_shared */ + NULL, /* signal */ + NULL, /* own */ + NULL, /* is_owned */ + process_wait, /* special_wait */ + NULL, /* prewait */ + process_details, /* details */ + process_typename, /* typename */ + process_typesize, /* typesize */ +}; + +static void +process_set_defaults (MonoW32HandleProcess *process_handle) +{ + /* These seem to be the defaults on w2k */ + process_handle->min_working_set = 204800; + process_handle->max_working_set = 1413120; + + process_handle->create_time = mono_100ns_datetime (); +} + +static void +process_set_name (MonoW32HandleProcess *process_handle) +{ + char *progname, *utf8_progname, *slash; + + progname = g_get_prgname (); + utf8_progname = mono_utf8_from_external (progname); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: using [%s] as prog name", __func__, progname); + + if (utf8_progname) { + slash = strrchr (utf8_progname, '/'); + if (slash) + process_handle->pname = g_strdup (slash+1); + else + process_handle->pname = g_strdup (utf8_progname); + g_free (utf8_progname); + } +} + +void +mono_w32process_init (void) +{ + MonoW32HandleProcess process_handle; + + mono_w32handle_register_ops (MONO_W32TYPE_PROCESS, &process_ops); + + mono_w32handle_register_capabilities (MONO_W32TYPE_PROCESS, + (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SPECIAL_WAIT)); + + current_pid = getpid (); + + memset (&process_handle, 0, sizeof (process_handle)); + process_handle.pid = current_pid; + process_set_defaults (&process_handle); + process_set_name (&process_handle); + + current_process = mono_w32handle_new (MONO_W32TYPE_PROCESS, &process_handle); + g_assert (current_process != INVALID_HANDLE_VALUE); + + mono_coop_mutex_init (&processes_mutex); +} + +void +mono_w32process_cleanup (void) +{ + g_free (cli_launcher); +} + +static int +len16 (const gunichar2 *str) +{ + int len = 0; + + while (*str++ != 0) + len++; + + return len; +} + +static gunichar2 * +utf16_concat (const gunichar2 *first, ...) +{ + va_list args; + int total = 0, i; + const gunichar2 *s; + const gunichar2 *p; + gunichar2 *ret; + + va_start (args, first); + total += len16 (first); + for (s = va_arg (args, gunichar2 *); s != NULL; s = va_arg(args, gunichar2 *)) + total += len16 (s); + va_end (args); + + ret = g_new (gunichar2, total + 1); + if (ret == NULL) + return NULL; + + ret [total] = 0; + i = 0; + for (s = first; *s != 0; s++) + ret [i++] = *s; + va_start (args, first); + for (s = va_arg (args, gunichar2 *); s != NULL; s = va_arg (args, gunichar2 *)){ + for (p = s; *p != 0; p++) + ret [i++] = *p; + } + va_end (args); + + return ret; +} + +guint32 +mono_w32process_get_pid (gpointer handle) +{ + MonoW32Handle *handle_data; + guint32 ret; + + if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown handle %p", __func__, handle); + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return 0; + } + + if (handle_data->type != MONO_W32TYPE_PROCESS) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown process handle %p", __func__, handle); + mono_w32error_set_last (ERROR_INVALID_HANDLE); + mono_w32handle_unref (handle_data); + return 0; + } + + ret = ((MonoW32HandleProcess*) handle_data->specific)->pid; + + mono_w32handle_unref (handle_data); + + return ret; +} + +typedef struct { + guint32 pid; + gpointer handle; +} GetProcessForeachData; + +static gboolean +get_process_foreach_callback (MonoW32Handle *handle_data, gpointer user_data) +{ + GetProcessForeachData *foreach_data; + MonoW32HandleProcess *process_handle; + pid_t pid; + + if (handle_data->type != MONO_W32TYPE_PROCESS) + return FALSE; + + process_handle = (MonoW32HandleProcess*) handle_data->specific; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: looking at process %d", __func__, process_handle->pid); + + pid = process_handle->pid; + if (pid == 0) + return FALSE; + + foreach_data = (GetProcessForeachData*) user_data; + + /* It's possible to have more than one process handle with the + * same pid, but only the one running process can be + * unsignalled. */ + if (foreach_data->pid != pid) + return FALSE; + if (mono_w32handle_issignalled (handle_data)) + return FALSE; + + foreach_data->handle = mono_w32handle_duplicate (handle_data); + return TRUE; +} + +HANDLE +ves_icall_System_Diagnostics_Process_GetProcess_internal (guint32 pid) +{ + GetProcessForeachData foreach_data; + gpointer handle; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: looking for process %d", __func__, pid); + + memset (&foreach_data, 0, sizeof (foreach_data)); + foreach_data.pid = pid; + mono_w32handle_foreach (get_process_foreach_callback, &foreach_data); + handle = foreach_data.handle; + if (handle) { + /* get_process_foreach_callback already added a ref */ + return handle; + } + + if (process_is_alive (pid)) { + /* non-child process */ + MonoW32HandleProcess process_handle; + + memset (&process_handle, 0, sizeof (process_handle)); + process_handle.pid = pid; + process_handle.pname = mono_w32process_get_name (pid); + + handle = mono_w32handle_new (MONO_W32TYPE_PROCESS, &process_handle); + if (handle == INVALID_HANDLE_VALUE) { + g_warning ("%s: error creating process handle", __func__); + + mono_w32error_set_last (ERROR_OUTOFMEMORY); + return NULL; + } + + return handle; + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Can't find pid %d", __func__, pid); + + mono_w32error_set_last (ERROR_PROC_NOT_FOUND); + return NULL; +} + +static gboolean +match_procname_to_modulename (char *procname, char *modulename) +{ + char* lastsep = NULL; + char* lastsep2 = NULL; + char* pname = NULL; + char* mname = NULL; + gboolean result = FALSE; + + if (procname == NULL || modulename == NULL) + return (FALSE); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: procname=\"%s\", modulename=\"%s\"", __func__, procname, modulename); + pname = mono_path_resolve_symlinks (procname); + mname = mono_path_resolve_symlinks (modulename); + + if (!strcmp (pname, mname)) + result = TRUE; + + if (!result) { + lastsep = strrchr (mname, '/'); + if (lastsep) + if (!strcmp (lastsep+1, pname)) + result = TRUE; + if (!result) { + lastsep2 = strrchr (pname, '/'); + if (lastsep2){ + if (lastsep) { + if (!strcmp (lastsep+1, lastsep2+1)) + result = TRUE; + } else { + if (!strcmp (mname, lastsep2+1)) + result = TRUE; + } + } + } + } + + g_free (pname); + g_free (mname); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: result is %" G_GINT32_FORMAT, __func__, result); + return result; +} + +gboolean +mono_w32process_try_get_modules (gpointer handle, gpointer *modules, guint32 size, guint32 *needed) +{ + MonoW32Handle *handle_data; + MonoW32HandleProcess *process_handle; + GSList *mods = NULL, *mods_iter; + MonoW32ProcessModule *module; + guint32 count, avail = size / sizeof(gpointer); + int i; + pid_t pid; + char *pname = NULL; + + /* Store modules in an array of pointers (main module as + * modules[0]), using the load address for each module as a + * token. (Use 'NULL' as an alternative for the main module + * so that the simple implementation can just return one item + * for now.) Get the info from /proc//maps on linux, + * /proc//map on FreeBSD, other systems will have to + * implement /dev/kmem reading or whatever other horrid + * technique is needed. + */ + if (size < sizeof(gpointer)) + return FALSE; + + if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown handle %p", __func__, handle); + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return FALSE; + } + + if (handle_data->type != MONO_W32TYPE_PROCESS) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown process handle %p", __func__, handle); + mono_w32error_set_last (ERROR_INVALID_HANDLE); + mono_w32handle_unref (handle_data); + return FALSE; + } + + process_handle = (MonoW32HandleProcess*) handle_data->specific; + + pid = process_handle->pid; + pname = g_strdup (process_handle->pname); + + if (!pname) { + modules[0] = NULL; + *needed = sizeof(gpointer); + mono_w32handle_unref (handle_data); + return TRUE; + } + + mods = mono_w32process_get_modules (pid); + if (!mods) { + modules[0] = NULL; + *needed = sizeof(gpointer); + g_free (pname); + mono_w32handle_unref (handle_data); + return TRUE; + } + + count = 0; + + /* + * Use the NULL shortcut, as the first line in + * /proc//maps isn't the executable, and we need + * that first in the returned list. Check the module name + * to see if it ends with the proc name and substitute + * the first entry with it. FIXME if this turns out to + * be a problem. + */ + modules[0] = NULL; + mods_iter = mods; + for (i = 0; mods_iter; i++) { + if (i < avail - 1) { + module = (MonoW32ProcessModule *)mods_iter->data; + if (modules[0] != NULL) + modules[i] = module->address_start; + else if (match_procname_to_modulename (pname, module->filename)) + modules[0] = module->address_start; + else + modules[i + 1] = module->address_start; + } + mono_w32process_module_free ((MonoW32ProcessModule *)mods_iter->data); + mods_iter = g_slist_next (mods_iter); + count++; + } + + /* count + 1 to leave slot 0 for the main module */ + *needed = sizeof(gpointer) * (count + 1); + + g_slist_free (mods); + g_free (pname); + mono_w32handle_unref (handle_data); + return TRUE; +} + +guint32 +mono_w32process_module_get_filename (gpointer handle, gpointer module, gunichar2 *basename, guint32 size) +{ + gint pid, len; + gsize bytes; + gchar *path; + gunichar2 *proc_path; + + size *= sizeof (gunichar2); /* adjust for unicode characters */ + + if (basename == NULL || size == 0) + return 0; + + pid = mono_w32process_get_pid (handle); + + path = mono_w32process_get_path (pid); + if (path == NULL) + return 0; + + proc_path = mono_unicode_from_external (path, &bytes); + g_free (path); + + if (proc_path == NULL) + return 0; + + len = (bytes / 2); + + /* Add the terminator */ + bytes += 2; + + if (size < bytes) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Size %" G_GUINT32_FORMAT " smaller than needed (%zd); truncating", __func__, size, bytes); + memcpy (basename, proc_path, size); + } else { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Size %" G_GUINT32_FORMAT " larger than needed (%zd)", __func__, size, bytes); + memcpy (basename, proc_path, bytes); + } + + g_free (proc_path); + + return len; +} + +guint32 +mono_w32process_module_get_name (gpointer handle, gpointer module, gunichar2 *basename, guint32 size) +{ + MonoW32Handle *handle_data; + MonoW32HandleProcess *process_handle; + pid_t pid; + gunichar2 *procname; + char *procname_ext = NULL; + glong len; + gsize bytes; + GSList *mods = NULL, *mods_iter; + MonoW32ProcessModule *found_module; + char *pname = NULL; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Getting module base name, process handle %p module %p basename %p size %" G_GUINT32_FORMAT, + __func__, handle, module, basename, size); + + size = size * sizeof (gunichar2); /* adjust for unicode characters */ + + if (basename == NULL || size == 0) + return 0; + + if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown handle %p", __func__, handle); + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return 0; + } + + if (handle_data->type != MONO_W32TYPE_PROCESS) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown process handle %p", __func__, handle); + mono_w32error_set_last (ERROR_INVALID_HANDLE); + mono_w32handle_unref (handle_data); + return 0; + } + + process_handle = (MonoW32HandleProcess*) handle_data->specific; + + pid = process_handle->pid; + pname = g_strdup (process_handle->pname); + + mods = mono_w32process_get_modules (pid); + if (!mods && module != NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Can't get modules %p", __func__, handle); + g_free (pname); + mono_w32handle_unref (handle_data); + return 0; + } + + /* If module != NULL compare the address. + * If module == NULL we are looking for the main module. + * The best we can do for now check it the module name end with the process name. + */ + for (mods_iter = mods; mods_iter; mods_iter = g_slist_next (mods_iter)) { + found_module = (MonoW32ProcessModule *)mods_iter->data; + if (procname_ext == NULL && + ((module == NULL && match_procname_to_modulename (pname, found_module->filename)) || + (module != NULL && found_module->address_start == module))) { + procname_ext = g_path_get_basename (found_module->filename); + } + + mono_w32process_module_free (found_module); + } + + if (procname_ext == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Can't find procname_ext from procmods %p", __func__, handle); + /* If it's *still* null, we might have hit the + * case where reading /proc/$pid/maps gives an + * empty file for this user. + */ + procname_ext = mono_w32process_get_name (pid); + if (!procname_ext) + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Can't find procname_ext from proc_get_name %p pid %d", __func__, handle, pid); + } + + g_slist_free (mods); + g_free (pname); + + if (procname_ext) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Process name is [%s]", __func__, + procname_ext); + + procname = mono_unicode_from_external (procname_ext, &bytes); + if (procname == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Can't get procname %p", __func__, handle); + /* bugger */ + g_free (procname_ext); + mono_w32handle_unref (handle_data); + return 0; + } + + len = (bytes / 2); + + /* Add the terminator */ + bytes += 2; + + if (size < bytes) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Size %" G_GUINT32_FORMAT " smaller than needed (%zd); truncating", __func__, size, bytes); + + memcpy (basename, procname, size); + } else { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Size %" G_GUINT32_FORMAT " larger than needed (%zd)", + __func__, size, bytes); + + memcpy (basename, procname, bytes); + } + + g_free (procname); + g_free (procname_ext); + + mono_w32handle_unref (handle_data); + return len; + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Can't find procname_ext %p", __func__, handle); + mono_w32handle_unref (handle_data); + return 0; +} + +gboolean +mono_w32process_module_get_information (gpointer handle, gpointer module, MODULEINFO *modinfo, guint32 size) +{ + MonoW32Handle *handle_data; + MonoW32HandleProcess *process_handle; + pid_t pid; + GSList *mods = NULL, *mods_iter; + MonoW32ProcessModule *found_module; + gboolean ret = FALSE; + char *pname = NULL; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Getting module info, process handle %p module %p", + __func__, handle, module); + + if (modinfo == NULL || size < sizeof (MODULEINFO)) + return FALSE; + + if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown handle %p", __func__, handle); + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return FALSE; + } + + if (handle_data->type != MONO_W32TYPE_PROCESS) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown process handle %p", __func__, handle); + mono_w32error_set_last (ERROR_INVALID_HANDLE); + mono_w32handle_unref (handle_data); + return FALSE; + } + + process_handle = (MonoW32HandleProcess*) handle_data->specific; + + pid = process_handle->pid; + pname = g_strdup (process_handle->pname); + + mods = mono_w32process_get_modules (pid); + if (!mods) { + g_free (pname); + mono_w32handle_unref (handle_data); + return FALSE; + } + + /* If module != NULL compare the address. + * If module == NULL we are looking for the main module. + * The best we can do for now check it the module name end with the process name. + */ + for (mods_iter = mods; mods_iter; mods_iter = g_slist_next (mods_iter)) { + found_module = (MonoW32ProcessModule *)mods_iter->data; + if (ret == FALSE && + ((module == NULL && match_procname_to_modulename (pname, found_module->filename)) || + (module != NULL && found_module->address_start == module))) { + modinfo->lpBaseOfDll = found_module->address_start; + modinfo->SizeOfImage = (gsize)(found_module->address_end) - (gsize)(found_module->address_start); + modinfo->EntryPoint = found_module->address_offset; + ret = TRUE; + } + + mono_w32process_module_free (found_module); + } + + g_slist_free (mods); + g_free (pname); + mono_w32handle_unref (handle_data); + return ret; +} + +static void +switch_dir_separators (char *path) +{ + size_t i, pathLength = strlen(path); + + /* Turn all the slashes round the right way, except for \' */ + /* There are probably other characters that need to be excluded as well. */ + for (i = 0; i < pathLength; i++) { + if (path[i] == '\\' && i < pathLength - 1 && path[i+1] != '\'' ) + path[i] = '/'; + } +} + +#if HAVE_SIGACTION + +MONO_SIGNAL_HANDLER_FUNC (static, mono_sigchld_signal_handler, (int _dummy, siginfo_t *info, void *context)) +{ + /* + * Don't want to do any complicated processing here so just wake up the finalizer thread which will call + * mono_w32process_signal_finished (). + */ + int old_errno = errno; + + mono_gc_finalize_notify (); + + errno = old_errno; +} + +static void +process_add_sigchld_handler (void) +{ + struct sigaction sa; + + sa.sa_sigaction = mono_sigchld_signal_handler; + sigemptyset (&sa.sa_mask); + sa.sa_flags = SA_NOCLDSTOP | SA_SIGINFO | SA_RESTART; + g_assert (sigaction (SIGCHLD, &sa, NULL) != -1); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "Added SIGCHLD handler"); +} + +#endif + +/* + * mono_w32process_signal_finished: + * + * Signal the exit semaphore for processes which have finished. + */ +void +mono_w32process_signal_finished (void) +{ + mono_coop_mutex_lock (&processes_mutex); + + for (Process* process = processes; process; process = process->next) { + int status = -1; + int pid; + + do { + pid = waitpid (process->pid, &status, WNOHANG); + } while (pid == -1 && errno == EINTR); + + // 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 + + // 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); + } + + mono_coop_mutex_unlock (&processes_mutex); +} + +static gboolean +is_readable_or_executable (const char *prog) +{ + struct stat buf; + int a = access (prog, R_OK); + int b = access (prog, X_OK); + if (a != 0 && b != 0) + return FALSE; + if (stat (prog, &buf)) + return FALSE; + if (S_ISREG (buf.st_mode)) + return TRUE; + return FALSE; +} + +static gboolean +is_executable (const char *prog) +{ + struct stat buf; + if (access (prog, X_OK) != 0) + return FALSE; + if (stat (prog, &buf)) + return FALSE; + if (S_ISREG (buf.st_mode)) + return TRUE; + return FALSE; +} + +static gboolean +is_managed_binary (const char *filename) +{ + int original_errno = errno; +#if defined(HAVE_LARGE_FILE_SUPPORT) && defined(O_LARGEFILE) + int file = open (filename, O_RDONLY | O_LARGEFILE); +#else + int file = open (filename, O_RDONLY); +#endif + off_t new_offset; + unsigned char buffer[8]; + off_t file_size, optional_header_offset; + off_t pe_header_offset, clr_header_offset; + gboolean managed = FALSE; + int num_read; + guint32 first_word, second_word, magic_number; + + /* If we are unable to open the file, then we definitely + * can't say that it is managed. The child mono process + * probably wouldn't be able to open it anyway. + */ + if (file < 0) { + errno = original_errno; + return FALSE; + } + + /* Retrieve the length of the file for future sanity checks. */ + file_size = lseek (file, 0, SEEK_END); + lseek (file, 0, SEEK_SET); + + /* We know we need to read a header field at offset 60. */ + if (file_size < 64) + goto leave; + + num_read = read (file, buffer, 2); + + if ((num_read != 2) || (buffer[0] != 'M') || (buffer[1] != 'Z')) + goto leave; + + new_offset = lseek (file, 60, SEEK_SET); + + if (new_offset != 60) + goto leave; + + num_read = read (file, buffer, 4); + + if (num_read != 4) + goto leave; + pe_header_offset = buffer[0] + | (buffer[1] << 8) + | (buffer[2] << 16) + | (buffer[3] << 24); + + if (pe_header_offset + 24 > file_size) + goto leave; + + new_offset = lseek (file, pe_header_offset, SEEK_SET); + + if (new_offset != pe_header_offset) + goto leave; + + num_read = read (file, buffer, 4); + + if ((num_read != 4) || (buffer[0] != 'P') || (buffer[1] != 'E') || (buffer[2] != 0) || (buffer[3] != 0)) + goto leave; + + /* + * Verify that the header we want in the optional header data + * is present in this binary. + */ + new_offset = lseek (file, pe_header_offset + 20, SEEK_SET); + + if (new_offset != pe_header_offset + 20) + goto leave; + + num_read = read (file, buffer, 2); + + if ((num_read != 2) || ((buffer[0] | (buffer[1] << 8)) < 216)) + goto leave; + + optional_header_offset = pe_header_offset + 24; + + /* Read the PE magic number */ + new_offset = lseek (file, optional_header_offset, SEEK_SET); + + if (new_offset != optional_header_offset) + goto leave; + + num_read = read (file, buffer, 2); + + if (num_read != 2) + goto leave; + + magic_number = (buffer[0] | (buffer[1] << 8)); + + if (magic_number == 0x10B) // PE32 + clr_header_offset = 208; + else if (magic_number == 0x20B) // PE32+ + clr_header_offset = 224; + else + goto leave; + + /* Read the CLR header address and size fields. These will be + * zero if the binary is not managed. + */ + new_offset = lseek (file, optional_header_offset + clr_header_offset, SEEK_SET); + + if (new_offset != optional_header_offset + clr_header_offset) + goto leave; + + num_read = read (file, buffer, 8); + + /* We are not concerned with endianness, only with + * whether it is zero or not. + */ + first_word = *(guint32 *)&buffer[0]; + second_word = *(guint32 *)&buffer[4]; + + if ((num_read != 8) || (first_word == 0) || (second_word == 0)) + goto leave; + + managed = TRUE; + +leave: + close (file); + errno = original_errno; + return managed; +} + +static gboolean +process_create (const gunichar2 *appname, const gunichar2 *cmdline, + const gunichar2 *cwd, StartupHandles *startup_handles, MonoW32ProcessInfo *process_info) +{ +#if defined (HAVE_FORK) && defined (HAVE_EXECVE) + char *cmd = NULL, *prog = NULL, *full_prog = NULL, *args = NULL, *args_after_prog = NULL; + char *dir = NULL, **env_strings = NULL, **argv = NULL; + guint32 i; + gboolean ret = FALSE; + gpointer handle = NULL; + GError *gerr = NULL; + int in_fd, out_fd, err_fd; + pid_t pid = 0; + int startup_pipe [2] = {-1, -1}; + int dummy; + Process *process; + +#if HAVE_SIGACTION + mono_lazy_initialize (&process_sig_chld_once, process_add_sigchld_handler); +#endif + + /* appname and cmdline specify the executable and its args: + * + * If appname is not NULL, it is the name of the executable. + * Otherwise the executable is the first token in cmdline. + * + * Executable searching: + * + * If appname is not NULL, it can specify the full path and + * file name, or else a partial name and the current directory + * will be used. There is no additional searching. + * + * If appname is NULL, the first whitespace-delimited token in + * cmdline is used. If the name does not contain a full + * directory path, the search sequence is: + * + * 1) The directory containing the current process + * 2) The current working directory + * 3) The windows system directory (Ignored) + * 4) The windows directory (Ignored) + * 5) $PATH + * + * Just to make things more interesting, tokens can contain + * white space if they are surrounded by quotation marks. I'm + * beginning to understand just why windows apps are generally + * so crap, with an API like this :-( + */ + if (appname != NULL) { + cmd = mono_unicode_to_external (appname); + if (cmd == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unicode conversion returned NULL", + __func__); + + mono_w32error_set_last (ERROR_PATH_NOT_FOUND); + goto free_strings; + } + + switch_dir_separators(cmd); + } + + if (cmdline != NULL) { + args = mono_unicode_to_external (cmdline); + if (args == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unicode conversion returned NULL", __func__); + + mono_w32error_set_last (ERROR_PATH_NOT_FOUND); + goto free_strings; + } + } + + if (cwd != NULL) { + dir = mono_unicode_to_external (cwd); + if (dir == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unicode conversion returned NULL", __func__); + + mono_w32error_set_last (ERROR_PATH_NOT_FOUND); + goto free_strings; + } + + /* Turn all the slashes round the right way */ + switch_dir_separators(dir); + } + + + /* We can't put off locating the executable any longer :-( */ + if (cmd != NULL) { + char *unquoted; + if (g_ascii_isalpha (cmd[0]) && (cmd[1] == ':')) { + /* Strip off the drive letter. I can't + * believe that CP/M holdover is still + * visible... + */ + g_memmove (cmd, cmd+2, strlen (cmd)-2); + cmd[strlen (cmd)-2] = '\0'; + } + + unquoted = g_shell_unquote (cmd, NULL); + if (unquoted[0] == '/') { + /* Assume full path given */ + prog = g_strdup (unquoted); + + /* Executable existing ? */ + if (!is_readable_or_executable (prog)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Couldn't find executable %s", + __func__, prog); + g_free (unquoted); + mono_w32error_set_last (ERROR_FILE_NOT_FOUND); + goto free_strings; + } + } else { + /* Search for file named by cmd in the current + * directory + */ + char *curdir = g_get_current_dir (); + + prog = g_strdup_printf ("%s/%s", curdir, unquoted); + g_free (curdir); + + /* And make sure it's readable */ + if (!is_readable_or_executable (prog)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Couldn't find executable %s", + __func__, prog); + g_free (unquoted); + mono_w32error_set_last (ERROR_FILE_NOT_FOUND); + goto free_strings; + } + } + g_free (unquoted); + + args_after_prog = args; + } else { + char *token = NULL; + char quote; + + /* Dig out the first token from args, taking quotation + * marks into account + */ + + /* First, strip off all leading whitespace */ + args = g_strchug (args); + + /* args_after_prog points to the contents of args + * after token has been set (otherwise argv[0] is + * duplicated) + */ + args_after_prog = args; + + /* Assume the opening quote will always be the first + * character + */ + if (args[0] == '\"' || args [0] == '\'') { + quote = args [0]; + for (i = 1; args[i] != '\0' && args[i] != quote; i++); + if (args [i + 1] == '\0' || g_ascii_isspace (args[i+1])) { + /* We found the first token */ + token = g_strndup (args+1, i-1); + args_after_prog = g_strchug (args + i + 1); + } else { + /* Quotation mark appeared in the + * middle of the token. Just give the + * whole first token, quotes and all, + * to exec. + */ + } + } + + if (token == NULL) { + /* No quote mark, or malformed */ + for (i = 0; args[i] != '\0'; i++) { + if (g_ascii_isspace (args[i])) { + token = g_strndup (args, i); + args_after_prog = args + i + 1; + break; + } + } + } + + if (token == NULL && args[0] != '\0') { + /* Must be just one token in the string */ + token = g_strdup (args); + args_after_prog = NULL; + } + + if (token == NULL) { + /* Give up */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Couldn't find what to exec", __func__); + + mono_w32error_set_last (ERROR_PATH_NOT_FOUND); + goto free_strings; + } + + /* Turn all the slashes round the right way. Only for + * the prg. name + */ + switch_dir_separators(token); + + if (g_ascii_isalpha (token[0]) && (token[1] == ':')) { + /* Strip off the drive letter. I can't + * believe that CP/M holdover is still + * visible... + */ + g_memmove (token, token+2, strlen (token)-2); + token[strlen (token)-2] = '\0'; + } + + if (token[0] == '/') { + /* Assume full path given */ + prog = g_strdup (token); + + /* Executable existing ? */ + if (!is_readable_or_executable (prog)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Couldn't find executable %s", + __func__, token); + g_free (token); + mono_w32error_set_last (ERROR_FILE_NOT_FOUND); + goto free_strings; + } + } else { + char *curdir = g_get_current_dir (); + + /* FIXME: Need to record the directory + * containing the current process, and check + * that for the new executable as the first + * place to look + */ + + prog = g_strdup_printf ("%s/%s", curdir, token); + g_free (curdir); + + /* I assume X_OK is the criterion to use, + * rather than F_OK + * + * X_OK is too strict *if* the target is a CLR binary + */ + if (!is_readable_or_executable (prog)) { + g_free (prog); + prog = g_find_program_in_path (token); + if (prog == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Couldn't find executable %s", __func__, token); + + g_free (token); + mono_w32error_set_last (ERROR_FILE_NOT_FOUND); + goto free_strings; + } + } + } + + g_free (token); + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Exec prog [%s] args [%s]", + __func__, prog, args_after_prog); + + /* Check for CLR binaries; if found, we will try to invoke + * them using the same mono binary that started us. + */ + if (is_managed_binary (prog)) { + gunichar2 *newapp, *newcmd; + gsize bytes_ignored; + + newapp = mono_unicode_from_external (cli_launcher ? cli_launcher : "mono", &bytes_ignored); + if (newapp) { + if (appname) + newcmd = utf16_concat (utf16_quote, newapp, utf16_quote, utf16_space, appname, utf16_space, cmdline, NULL); + else + newcmd = utf16_concat (utf16_quote, newapp, utf16_quote, utf16_space, cmdline, NULL); + + g_free (newapp); + + if (newcmd) { + ret = process_create (NULL, newcmd, cwd, startup_handles, process_info); + + g_free (newcmd); + + goto free_strings; + } + } + } else { + if (!is_executable (prog)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Executable permisson not set on %s", __func__, prog); + mono_w32error_set_last (ERROR_ACCESS_DENIED); + goto free_strings; + } + } + + if (args_after_prog != NULL && *args_after_prog) { + char *qprog; + + qprog = g_shell_quote (prog); + full_prog = g_strconcat (qprog, " ", args_after_prog, NULL); + g_free (qprog); + } else { + full_prog = g_shell_quote (prog); + } + + ret = g_shell_parse_argv (full_prog, NULL, &argv, &gerr); + if (ret == FALSE) { + g_message ("process_create: %s\n", gerr->message); + g_error_free (gerr); + gerr = NULL; + goto free_strings; + } + + if (startup_handles) { + in_fd = GPOINTER_TO_UINT (startup_handles->input); + out_fd = GPOINTER_TO_UINT (startup_handles->output); + err_fd = GPOINTER_TO_UINT (startup_handles->error); + } else { + in_fd = GPOINTER_TO_UINT (mono_w32file_get_console_input ()); + out_fd = GPOINTER_TO_UINT (mono_w32file_get_console_output ()); + err_fd = GPOINTER_TO_UINT (mono_w32file_get_console_error ()); + } + + /* + * process->env_variables is a an array of MonoString* + * + * If new_environ is not NULL it specifies the entire set of + * environment variables in the new process. Otherwise the + * new process inherits the same environment. + */ + if (process_info->env_variables) { + gint i, str_length, var_length; + MonoString *var; + gunichar2 *str; + + /* +2: one for the process handle value, and the last one is NULL */ + env_strings = g_new0 (gchar*, mono_array_length (process_info->env_variables) + 2); + + str = NULL; + str_length = 0; + + /* Copy each environ string into 'strings' turning it into utf8 (or the requested encoding) at the same time */ + for (i = 0; i < mono_array_length (process_info->env_variables); ++i) { + var = mono_array_get (process_info->env_variables, MonoString*, i); + var_length = mono_string_length (var); + + /* str is a null-terminated copy of var */ + + if (var_length + 1 > str_length) { + str_length = var_length + 1; + str = g_renew (gunichar2, str, str_length); + } + + memcpy (str, mono_string_chars (var), var_length * sizeof (gunichar2)); + str [var_length] = '\0'; + + env_strings [i] = mono_unicode_to_external (str); + } + + g_free (str); + } else { + guint32 env_count; + + env_count = 0; + for (i = 0; environ[i] != NULL; i++) + env_count++; + + /* +2: one for the process handle value, and the last one is NULL */ + env_strings = g_new0 (gchar*, env_count + 2); + + /* Copy each environ string into 'strings' turning it into utf8 (or the requested encoding) at the same time */ + for (i = 0; i < env_count; i++) + env_strings [i] = g_strdup (environ[i]); + } + + /* Create a pipe to make sure the child doesn't exit before + * we can add the process to the linked list of processes */ + if (pipe (startup_pipe) == -1) { + /* Could not create the pipe to synchroniz process startup. We'll just not synchronize. + * This is just for a very hard to hit race condition in the first place */ + startup_pipe [0] = startup_pipe [1] = -1; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: new process startup not synchronized. We may not notice if the newly created process exits immediately.", __func__); + } + + switch (pid = fork ()) { + case -1: /* Error */ { + mono_w32error_set_last (ERROR_OUTOFMEMORY); + ret = FALSE; + break; + } + case 0: /* Child */ { + if (startup_pipe [0] != -1) { + /* Wait until the parent has updated it's internal data */ + ssize_t _i G_GNUC_UNUSED = read (startup_pipe [0], &dummy, 1); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: child: parent has completed its setup", __func__); + close (startup_pipe [0]); + close (startup_pipe [1]); + } + + /* should we detach from the process group? */ + + /* Connect stdin, stdout and stderr */ + dup2 (in_fd, 0); + dup2 (out_fd, 1); + dup2 (err_fd, 2); + + /* Close all file descriptors */ + for (i = eg_getdtablesize() - 1; i > 2; i--) + close (i); + +#ifdef DEBUG_ENABLED + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: exec()ing [%s] in dir [%s]", __func__, cmd, + dir == NULL?".":dir); + for (i = 0; argv[i] != NULL; i++) + g_message ("arg %" G_GUINT32_FORMAT ": [%s]", i, argv[i]); + + for (i = 0; env_strings[i] != NULL; i++) + g_message ("env %" G_GUINT32_FORMAT ": [%s]", i, env_strings[i]); +#endif + + /* set cwd */ + if (dir != NULL && chdir (dir) == -1) { + /* set error */ + _exit (-1); + } + + /* exec */ + execve (argv[0], argv, env_strings); + + /* set error */ + _exit (-1); + + break; + } + default: /* Parent */ { + MonoW32Handle *handle_data; + MonoW32HandleProcess process_handle; + + memset (&process_handle, 0, sizeof (process_handle)); + process_handle.pid = pid; + process_handle.child = TRUE; + process_handle.pname = g_strdup (prog); + process_set_defaults (&process_handle); + + /* Add our process into the linked list of processes */ + process = (Process *) g_malloc0 (sizeof (Process)); + process->pid = pid; + process->handle_count = 1; + mono_coop_sem_init (&process->exit_sem, 0); + + process_handle.process = process; + + handle = mono_w32handle_new (MONO_W32TYPE_PROCESS, &process_handle); + if (handle == INVALID_HANDLE_VALUE) { + g_warning ("%s: error creating process handle", __func__); + + mono_coop_sem_destroy (&process->exit_sem); + g_free (process); + + mono_w32error_set_last (ERROR_OUTOFMEMORY); + ret = FALSE; + break; + } + + if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) + g_error ("%s: unknown handle %p", __func__, handle); + + if (handle_data->type != MONO_W32TYPE_PROCESS) + g_error ("%s: unknown process handle %p", __func__, handle); + + /* Keep the process handle artificially alive until the process + * exits so that the information in the handle isn't lost. */ + process->handle = mono_w32handle_duplicate (handle_data); + + mono_coop_mutex_lock (&processes_mutex); + process->next = processes; + mono_memory_barrier (); + processes = process; + mono_coop_mutex_unlock (&processes_mutex); + + if (process_info != NULL) { + process_info->process_handle = handle; + process_info->pid = pid; + } + + mono_w32handle_unref (handle_data); + + break; + } + } + + if (startup_pipe [1] != -1) { + /* Write 1 byte, doesn't matter what */ + ssize_t _i G_GNUC_UNUSED = write (startup_pipe [1], startup_pipe, 1); + close (startup_pipe [0]); + close (startup_pipe [1]); + } + +free_strings: + if (cmd) + g_free (cmd); + if (full_prog) + g_free (full_prog); + if (prog) + g_free (prog); + if (args) + g_free (args); + if (dir) + g_free (dir); + if (env_strings) + g_strfreev (env_strings); + if (argv) + g_strfreev (argv); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: returning handle %p for pid %d", __func__, handle, pid); + + /* Check if something needs to be cleaned up. */ + processes_cleanup (); + + return ret; +#else + mono_w32error_set_last (ERROR_NOT_SUPPORTED); + return FALSE; +#endif // defined (HAVE_FORK) && defined (HAVE_EXECVE) +} + +MonoBoolean +ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoW32ProcessStartInfo *proc_start_info, MonoW32ProcessInfo *process_info) +{ + const gunichar2 *lpFile; + const gunichar2 *lpParameters; + const gunichar2 *lpDirectory; + gunichar2 *args; + gboolean ret; + gboolean handler_needswait = FALSE; + + if (!proc_start_info->filename) { + /* w2k returns TRUE for this, for some reason. */ + ret = TRUE; + goto done; + } + + lpFile = proc_start_info->filename ? mono_string_chars (proc_start_info->filename) : NULL; + lpParameters = proc_start_info->arguments ? mono_string_chars (proc_start_info->arguments) : NULL; + lpDirectory = proc_start_info->working_directory && mono_string_length (proc_start_info->working_directory) != 0 ? + mono_string_chars (proc_start_info->working_directory) : NULL; + + /* Put both executable and parameters into the second argument + * to process_create (), so it searches $PATH. The conversion + * into and back out of utf8 is because there is no + * g_strdup_printf () equivalent for gunichar2 :-( + */ + args = utf16_concat (utf16_quote, lpFile, utf16_quote, lpParameters == NULL ? NULL : utf16_space, lpParameters, NULL); + if (args == NULL) { + mono_w32error_set_last (ERROR_INVALID_DATA); + ret = FALSE; + goto done; + } + ret = process_create (NULL, args, lpDirectory, NULL, process_info); + g_free (args); + + if (!ret && mono_w32error_get_last () == ERROR_OUTOFMEMORY) + goto done; + + if (!ret) { + static char *handler; + static gunichar2 *handler_utf16; + + if (handler_utf16 == (gunichar2 *)-1) { + ret = FALSE; + goto done; + } + +#ifdef HOST_DARWIN + handler = g_strdup ("/usr/bin/open"); + handler_needswait = TRUE; +#else + /* + * On Linux, try: xdg-open, the FreeDesktop standard way of doing it, + * if that fails, try to use gnome-open, then kfmclient + */ + handler = g_find_program_in_path ("xdg-open"); + if (handler == NULL){ + handler = g_find_program_in_path ("gnome-open"); + if (handler == NULL){ + handler = g_find_program_in_path ("kfmclient"); + if (handler == NULL){ + handler_utf16 = (gunichar2 *) -1; + ret = FALSE; + goto done; + } else { + /* kfmclient needs exec argument */ + char *old = handler; + handler = g_strconcat (old, " exec", + NULL); + g_free (old); + } + } + } +#endif + handler_utf16 = g_utf8_to_utf16 (handler, -1, NULL, NULL, NULL); + g_free (handler); + + /* Put quotes around the filename, in case it's a url + * that contains #'s (process_create() calls + * g_shell_parse_argv(), which deliberately throws + * away anything after an unquoted #). Fixes bug + * 371567. + */ + args = utf16_concat (handler_utf16, utf16_space, utf16_quote, lpFile, utf16_quote, + lpParameters == NULL ? NULL : utf16_space, lpParameters, NULL); + if (args == NULL) { + mono_w32error_set_last (ERROR_INVALID_DATA); + ret = FALSE; + goto done; + } + ret = process_create (NULL, args, lpDirectory, NULL, process_info); + g_free (args); + if (!ret) { + if (mono_w32error_get_last () != ERROR_OUTOFMEMORY) + mono_w32error_set_last (ERROR_INVALID_DATA); + ret = FALSE; + goto done; + } + + if (handler_needswait) { + gint32 exitcode; + MonoW32HandleWaitRet waitret; + waitret = process_wait (process_info->process_handle, MONO_INFINITE_WAIT, NULL); + ves_icall_Microsoft_Win32_NativeMethods_GetExitCodeProcess (process_info->process_handle, &exitcode); + if (exitcode != 0) + ret = FALSE; + } + /* 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 = INVALID_HANDLE_VALUE; + } + +done: + if (ret == FALSE) { + process_info->pid = -mono_w32error_get_last (); + } else { +#if !defined(MONO_CROSS_COMPILE) + process_info->pid = mono_w32process_get_pid (process_info->process_handle); +#else + process_info->pid = 0; +#endif + } + + return ret; +} + +/* Only used when UseShellExecute is false */ +static gboolean +process_get_complete_path (const gunichar2 *appname, gchar **completed) +{ + gchar *utf8app; + gchar *found; + + utf8app = g_utf16_to_utf8 (appname, -1, NULL, NULL, NULL); + + if (g_path_is_absolute (utf8app)) { + *completed = g_shell_quote (utf8app); + g_free (utf8app); + return TRUE; + } + + if (g_file_test (utf8app, G_FILE_TEST_IS_EXECUTABLE) && !g_file_test (utf8app, G_FILE_TEST_IS_DIR)) { + *completed = g_shell_quote (utf8app); + g_free (utf8app); + return TRUE; + } + + found = g_find_program_in_path (utf8app); + if (found == NULL) { + *completed = NULL; + g_free (utf8app); + return FALSE; + } + + *completed = g_shell_quote (found); + g_free (found); + g_free (utf8app); + return TRUE; +} + +static gboolean +process_get_shell_arguments (MonoW32ProcessStartInfo *proc_start_info, gunichar2 **shell_path) +{ + gchar *complete_path = NULL; + + *shell_path = NULL; + + if (process_get_complete_path (mono_string_chars (proc_start_info->filename), &complete_path)) { + *shell_path = g_utf8_to_utf16 (complete_path, -1, NULL, NULL, NULL); + g_free (complete_path); + } + + return *shell_path != NULL; +} + +MonoBoolean +ves_icall_System_Diagnostics_Process_CreateProcess_internal (MonoW32ProcessStartInfo *proc_start_info, + HANDLE stdin_handle, HANDLE stdout_handle, HANDLE stderr_handle, MonoW32ProcessInfo *process_info) +{ + gboolean ret; + gunichar2 *dir; + StartupHandles startup_handles; + gunichar2 *shell_path = NULL; + gunichar2 *args = NULL; + + memset (&startup_handles, 0, sizeof (startup_handles)); + startup_handles.input = stdin_handle; + startup_handles.output = stdout_handle; + startup_handles.error = stderr_handle; + + if (!process_get_shell_arguments (proc_start_info, &shell_path)) { + process_info->pid = -ERROR_FILE_NOT_FOUND; + return FALSE; + } + + args = proc_start_info->arguments && mono_string_length (proc_start_info->arguments) > 0 ? + mono_string_chars (proc_start_info->arguments): NULL; + + /* The default dir name is "". Turn that into NULL to mean "current directory" */ + dir = proc_start_info->working_directory && mono_string_length (proc_start_info->working_directory) > 0 ? + mono_string_chars (proc_start_info->working_directory) : NULL; + + ret = process_create (shell_path, args, dir, &startup_handles, process_info); + + if (shell_path != NULL) + g_free (shell_path); + + if (!ret) + process_info->pid = -mono_w32error_get_last (); + + return ret; +} + +/* Returns an array of pids */ +MonoArray * +ves_icall_System_Diagnostics_Process_GetProcesses_internal (void) +{ + MonoError error; + MonoArray *procs; + gpointer *pidarray; + int i, count; + + pidarray = mono_process_list (&count); + if (!pidarray) { + mono_set_pending_exception (mono_get_exception_not_supported ("This system does not support EnumProcesses")); + return NULL; + } + procs = mono_array_new_checked (mono_domain_get (), mono_get_int32_class (), count, &error); + if (mono_error_set_pending_exception (&error)) { + g_free (pidarray); + return NULL; + } + if (sizeof (guint32) == sizeof (gpointer)) { + memcpy (mono_array_addr (procs, guint32, 0), pidarray, count * sizeof (gint32)); + } else { + for (i = 0; i < count; ++i) + *(mono_array_addr (procs, guint32, i)) = GPOINTER_TO_UINT (pidarray [i]); + } + g_free (pidarray); + + return procs; +} + +void +mono_w32process_set_cli_launcher (gchar *path) +{ + g_free (cli_launcher); + cli_launcher = g_strdup (path); +} + +gpointer +ves_icall_Microsoft_Win32_NativeMethods_GetCurrentProcess (void) +{ + return current_process; +} + +MonoBoolean +ves_icall_Microsoft_Win32_NativeMethods_GetExitCodeProcess (gpointer handle, gint32 *exitcode) +{ + MonoW32Handle *handle_data; + MonoW32HandleProcess *process_handle; + + if (!exitcode) + return FALSE; + + if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown handle %p", __func__, handle); + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return FALSE; + } + + if (handle_data->type != MONO_W32TYPE_PROCESS) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown process handle %p", __func__, handle); + mono_w32error_set_last (ERROR_INVALID_HANDLE); + mono_w32handle_unref (handle_data); + return FALSE; + } + + process_handle = (MonoW32HandleProcess*) handle_data->specific; + + if (process_handle->pid == current_pid) { + *exitcode = STILL_ACTIVE; + mono_w32handle_unref (handle_data); + return TRUE; + } + + /* A process handle is only signalled if the process has exited + * and has been waited for. Make sure any process exit has been + * noticed before checking if the process is signalled. + * Fixes bug 325463. */ + mono_w32handle_wait_one (handle, 0, TRUE); + + *exitcode = mono_w32handle_issignalled (handle_data) ? process_handle->exitstatus : STILL_ACTIVE; + + mono_w32handle_unref (handle_data); + + return TRUE; +} + +MonoBoolean +ves_icall_Microsoft_Win32_NativeMethods_CloseProcess (gpointer handle) +{ + return mono_w32handle_close (handle); +} + +MonoBoolean +ves_icall_Microsoft_Win32_NativeMethods_TerminateProcess (gpointer handle, gint32 exitcode) +{ +#ifdef HAVE_KILL + MonoW32Handle *handle_data; + int ret; + pid_t pid; + + if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown handle %p", __func__, handle); + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return FALSE; + } + + if (handle_data->type != MONO_W32TYPE_PROCESS) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown process handle %p", __func__, handle); + mono_w32error_set_last (ERROR_INVALID_HANDLE); + mono_w32handle_unref (handle_data); + return FALSE; + } + + pid = ((MonoW32HandleProcess*) handle_data->specific)->pid; + + ret = kill (pid, exitcode == -1 ? SIGKILL : SIGTERM); + if (ret == 0) { + mono_w32handle_unref (handle_data); + return TRUE; + } + + switch (errno) { + case EINVAL: mono_w32error_set_last (ERROR_INVALID_PARAMETER); break; + case EPERM: mono_w32error_set_last (ERROR_ACCESS_DENIED); break; + case ESRCH: mono_w32error_set_last (ERROR_PROC_NOT_FOUND); break; + default: mono_w32error_set_last (ERROR_GEN_FAILURE); break; + } + + mono_w32handle_unref (handle_data); + return FALSE; +#else + g_error ("kill() is not supported by this platform"); +#endif +} + +MonoBoolean +ves_icall_Microsoft_Win32_NativeMethods_GetProcessWorkingSetSize (gpointer handle, gsize *min, gsize *max) +{ + MonoW32Handle *handle_data; + MonoW32HandleProcess *process_handle; + + if (!min || !max) + return FALSE; + + if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown handle %p", __func__, handle); + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return FALSE; + } + + if (handle_data->type != MONO_W32TYPE_PROCESS) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown process handle %p", __func__, handle); + mono_w32error_set_last (ERROR_INVALID_HANDLE); + mono_w32handle_unref (handle_data); + return FALSE; + } + + process_handle = (MonoW32HandleProcess*) handle_data->specific; + + if (!process_handle->child) { + mono_w32handle_unref (handle_data); + return FALSE; + } + + *min = process_handle->min_working_set; + *max = process_handle->max_working_set; + + mono_w32handle_unref (handle_data); + return TRUE; +} + +MonoBoolean +ves_icall_Microsoft_Win32_NativeMethods_SetProcessWorkingSetSize (gpointer handle, gsize min, gsize max) +{ + MonoW32Handle *handle_data; + MonoW32HandleProcess *process_handle; + + if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown handle %p", __func__, handle); + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return FALSE; + } + + if (handle_data->type != MONO_W32TYPE_PROCESS) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown process handle %p", __func__, handle); + mono_w32error_set_last (ERROR_INVALID_HANDLE); + mono_w32handle_unref (handle_data); + return FALSE; + } + + process_handle = (MonoW32HandleProcess*) handle_data->specific; + + if (!process_handle->child) { + mono_w32handle_unref (handle_data); + return FALSE; + } + + process_handle->min_working_set = min; + process_handle->max_working_set = max; + + mono_w32handle_unref (handle_data); + return TRUE; +} + +gint32 +ves_icall_Microsoft_Win32_NativeMethods_GetPriorityClass (gpointer handle) +{ +#ifdef HAVE_GETPRIORITY + MonoW32Handle *handle_data; + gint res; + gint32 ret; + pid_t pid; + + if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown handle %p", __func__, handle); + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return 0; + } + + if (handle_data->type != MONO_W32TYPE_PROCESS) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown process handle %p", __func__, handle); + mono_w32error_set_last (ERROR_INVALID_HANDLE); + mono_w32handle_unref (handle_data); + return 0; + } + + pid = ((MonoW32HandleProcess*) handle_data->specific)->pid; + + errno = 0; + res = getpriority (PRIO_PROCESS, pid); + if (res == -1 && errno != 0) { + switch (errno) { + case EPERM: + case EACCES: + mono_w32error_set_last (ERROR_ACCESS_DENIED); + break; + case ESRCH: + mono_w32error_set_last (ERROR_PROC_NOT_FOUND); + break; + default: + mono_w32error_set_last (ERROR_GEN_FAILURE); + } + + mono_w32handle_unref (handle_data); + return 0; + } + + if (res == 0) + ret = MONO_W32PROCESS_PRIORITY_CLASS_NORMAL; + else if (res < -15) + ret = MONO_W32PROCESS_PRIORITY_CLASS_REALTIME; + else if (res < -10) + ret = MONO_W32PROCESS_PRIORITY_CLASS_HIGH; + else if (res < 0) + ret = MONO_W32PROCESS_PRIORITY_CLASS_ABOVE_NORMAL; + else if (res > 10) + ret = MONO_W32PROCESS_PRIORITY_CLASS_IDLE; + else if (res > 0) + ret = MONO_W32PROCESS_PRIORITY_CLASS_BELOW_NORMAL; + else + ret = MONO_W32PROCESS_PRIORITY_CLASS_NORMAL; + + mono_w32handle_unref (handle_data); + return ret; +#else + mono_w32error_set_last (ERROR_NOT_SUPPORTED); + return 0; +#endif +} + +MonoBoolean +ves_icall_Microsoft_Win32_NativeMethods_SetPriorityClass (gpointer handle, gint32 priorityClass) +{ +#ifdef HAVE_SETPRIORITY + MonoW32Handle *handle_data; + int ret; + int prio; + pid_t pid; + + if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown handle %p", __func__, handle); + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return FALSE; + } + + if (handle_data->type != MONO_W32TYPE_PROCESS) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown process handle %p", __func__, handle); + mono_w32error_set_last (ERROR_INVALID_HANDLE); + mono_w32handle_unref (handle_data); + return FALSE; + } + + pid = ((MonoW32HandleProcess*) handle_data->specific)->pid; + + switch (priorityClass) { + case MONO_W32PROCESS_PRIORITY_CLASS_IDLE: + prio = 19; + break; + case MONO_W32PROCESS_PRIORITY_CLASS_BELOW_NORMAL: + prio = 10; + break; + case MONO_W32PROCESS_PRIORITY_CLASS_NORMAL: + prio = 0; + break; + case MONO_W32PROCESS_PRIORITY_CLASS_ABOVE_NORMAL: + prio = -5; + break; + case MONO_W32PROCESS_PRIORITY_CLASS_HIGH: + prio = -11; + break; + case MONO_W32PROCESS_PRIORITY_CLASS_REALTIME: + prio = -20; + break; + default: + mono_w32error_set_last (ERROR_INVALID_PARAMETER); + mono_w32handle_unref (handle_data); + return FALSE; + } + + ret = setpriority (PRIO_PROCESS, pid, prio); + if (ret == -1) { + switch (errno) { + case EPERM: + case EACCES: + mono_w32error_set_last (ERROR_ACCESS_DENIED); + break; + case ESRCH: + mono_w32error_set_last (ERROR_PROC_NOT_FOUND); + break; + default: + mono_w32error_set_last (ERROR_GEN_FAILURE); + } + } + + mono_w32handle_unref (handle_data); + return ret == 0; +#else + mono_w32error_set_last (ERROR_NOT_SUPPORTED); + return FALSE; +#endif +} + +static void +ticks_to_processtime (guint64 ticks, ProcessTime *processtime) +{ + processtime->lowDateTime = ticks & 0xFFFFFFFF; + processtime->highDateTime = ticks >> 32; +} + +MonoBoolean +ves_icall_Microsoft_Win32_NativeMethods_GetProcessTimes (gpointer handle, gint64 *creation_time, gint64 *exit_time, gint64 *kernel_time, gint64 *user_time) +{ + MonoW32Handle *handle_data; + MonoW32HandleProcess *process_handle; + ProcessTime *creation_processtime, *exit_processtime, *kernel_processtime, *user_processtime; + + if (!creation_time || !exit_time || !kernel_time || !user_time) { + /* Not sure if w32 allows NULLs here or not */ + return FALSE; + } + + creation_processtime = (ProcessTime*) creation_time; + exit_processtime = (ProcessTime*) exit_time; + kernel_processtime = (ProcessTime*) kernel_time; + user_processtime = (ProcessTime*) user_time; + + memset (creation_processtime, 0, sizeof (ProcessTime)); + memset (exit_processtime, 0, sizeof (ProcessTime)); + memset (kernel_processtime, 0, sizeof (ProcessTime)); + memset (user_processtime, 0, sizeof (ProcessTime)); + + if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown handle %p", __func__, handle); + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return FALSE; + } + + if (handle_data->type != MONO_W32TYPE_PROCESS) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown process handle %p", __func__, handle); + mono_w32error_set_last (ERROR_INVALID_HANDLE); + mono_w32handle_unref (handle_data); + return FALSE; + } + + process_handle = (MonoW32HandleProcess*) handle_data->specific; + + if (!process_handle->child) { + gint64 start_ticks, user_ticks, kernel_ticks; + + mono_process_get_times (GINT_TO_POINTER (process_handle->pid), + &start_ticks, &user_ticks, &kernel_ticks); + + ticks_to_processtime (start_ticks, creation_processtime); + ticks_to_processtime (kernel_ticks, kernel_processtime); + ticks_to_processtime (user_ticks, user_processtime); + + mono_w32handle_unref (handle_data); + return TRUE; + } + + ticks_to_processtime (process_handle->create_time, creation_processtime); + + /* A process handle is only signalled if the process has + * exited, otherwise exit_processtime isn't set */ + if (mono_w32handle_issignalled (handle_data)) + ticks_to_processtime (process_handle->exit_time, exit_processtime); + +#ifdef HAVE_GETRUSAGE + if (process_handle->pid == getpid ()) { + struct rusage time_data; + if (getrusage (RUSAGE_SELF, &time_data) == 0) { + ticks_to_processtime ((guint64)time_data.ru_utime.tv_sec * 10000000 + (guint64)time_data.ru_utime.tv_usec * 10, user_processtime); + ticks_to_processtime ((guint64)time_data.ru_stime.tv_sec * 10000000 + (guint64)time_data.ru_stime.tv_usec * 10, kernel_processtime); + } + } +#endif + + mono_w32handle_unref (handle_data); + return TRUE; +} + +static IMAGE_SECTION_HEADER * +get_enclosing_section_header (guint32 rva, IMAGE_NT_HEADERS32 *nt_headers) +{ + IMAGE_SECTION_HEADER *section = IMAGE_FIRST_SECTION32 (nt_headers); + guint32 i; + + for (i = 0; i < GUINT16_FROM_LE (nt_headers->FileHeader.NumberOfSections); i++, section++) { + guint32 size = GUINT32_FROM_LE (section->Misc.VirtualSize); + if (size == 0) { + size = GUINT32_FROM_LE (section->SizeOfRawData); + } + + if ((rva >= GUINT32_FROM_LE (section->VirtualAddress)) && + (rva < (GUINT32_FROM_LE (section->VirtualAddress) + size))) { + return(section); + } + } + + return(NULL); +} + +/* This works for both 32bit and 64bit files, as the differences are + * all after the section header block + */ +static gpointer +get_ptr_from_rva (guint32 rva, IMAGE_NT_HEADERS32 *ntheaders, gpointer file_map) +{ + IMAGE_SECTION_HEADER *section_header; + guint32 delta; + + section_header = get_enclosing_section_header (rva, ntheaders); + if (section_header == NULL) { + return(NULL); + } + + delta = (guint32)(GUINT32_FROM_LE (section_header->VirtualAddress) - + GUINT32_FROM_LE (section_header->PointerToRawData)); + + return((guint8 *)file_map + rva - delta); +} + +static gpointer +scan_resource_dir (IMAGE_RESOURCE_DIRECTORY *root, IMAGE_NT_HEADERS32 *nt_headers, gpointer file_map, + IMAGE_RESOURCE_DIRECTORY_ENTRY *entry, int level, guint32 res_id, guint32 lang_id, gsize *size) +{ + IMAGE_RESOURCE_DIRECTORY_ENTRY swapped_entry; + gboolean is_string, is_dir; + guint32 name_offset, dir_offset, data_offset; + + swapped_entry.Name = GUINT32_FROM_LE (entry->Name); + swapped_entry.OffsetToData = GUINT32_FROM_LE (entry->OffsetToData); + + is_string = swapped_entry.NameIsString; + is_dir = swapped_entry.DataIsDirectory; + name_offset = swapped_entry.NameOffset; + dir_offset = swapped_entry.OffsetToDirectory; + data_offset = swapped_entry.OffsetToData; + + if (level == 0) { + /* Normally holds a directory entry for each type of + * resource + */ + if ((is_string == FALSE && + name_offset != res_id) || + (is_string == TRUE)) { + return(NULL); + } + } else if (level == 1) { + /* Normally holds a directory entry for each resource + * item + */ + } else if (level == 2) { + /* Normally holds a directory entry for each language + */ + if ((is_string == FALSE && + name_offset != lang_id && + lang_id != 0) || + (is_string == TRUE)) { + return(NULL); + } + } else { + g_assert_not_reached (); + } + + if (is_dir == TRUE) { + IMAGE_RESOURCE_DIRECTORY *res_dir = (IMAGE_RESOURCE_DIRECTORY *)((guint8 *)root + dir_offset); + IMAGE_RESOURCE_DIRECTORY_ENTRY *sub_entries = (IMAGE_RESOURCE_DIRECTORY_ENTRY *)(res_dir + 1); + guint32 entries, i; + + entries = GUINT16_FROM_LE (res_dir->NumberOfNamedEntries) + GUINT16_FROM_LE (res_dir->NumberOfIdEntries); + + for (i = 0; i < entries; i++) { + IMAGE_RESOURCE_DIRECTORY_ENTRY *sub_entry = &sub_entries[i]; + gpointer ret; + + ret = scan_resource_dir (root, nt_headers, file_map, + sub_entry, level + 1, res_id, + lang_id, size); + if (ret != NULL) { + return(ret); + } + } + + return(NULL); + } else { + IMAGE_RESOURCE_DATA_ENTRY *data_entry = (IMAGE_RESOURCE_DATA_ENTRY *)((guint8 *)root + data_offset); + *size = GUINT32_FROM_LE (data_entry->Size); + + return(get_ptr_from_rva (GUINT32_FROM_LE (data_entry->OffsetToData), nt_headers, file_map)); + } +} + +static gpointer +find_pe_file_resources32 (gpointer file_map, guint32 map_size, guint32 res_id, guint32 lang_id, gsize *size) +{ + IMAGE_DOS_HEADER *dos_header; + IMAGE_NT_HEADERS32 *nt_headers; + IMAGE_RESOURCE_DIRECTORY *resource_dir; + IMAGE_RESOURCE_DIRECTORY_ENTRY *resource_dir_entry; + guint32 resource_rva, entries, i; + gpointer ret = NULL; + + dos_header = (IMAGE_DOS_HEADER *)file_map; + if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Bad dos signature 0x%x", __func__, dos_header->e_magic); + + mono_w32error_set_last (ERROR_INVALID_DATA); + return(NULL); + } + + if (map_size < sizeof(IMAGE_NT_HEADERS32) + GUINT32_FROM_LE (dos_header->e_lfanew)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: File is too small: %" G_GUINT32_FORMAT, __func__, map_size); + + mono_w32error_set_last (ERROR_BAD_LENGTH); + return(NULL); + } + + nt_headers = (IMAGE_NT_HEADERS32 *)((guint8 *)file_map + GUINT32_FROM_LE (dos_header->e_lfanew)); + if (nt_headers->Signature != IMAGE_NT_SIGNATURE) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Bad NT signature 0x%x", __func__, nt_headers->Signature); + + mono_w32error_set_last (ERROR_INVALID_DATA); + return(NULL); + } + + if (nt_headers->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { + /* Do 64-bit stuff */ + resource_rva = GUINT32_FROM_LE (((IMAGE_NT_HEADERS64 *)nt_headers)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress); + } else { + resource_rva = GUINT32_FROM_LE (nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress); + } + + if (resource_rva == 0) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: No resources in file!", __func__); + + mono_w32error_set_last (ERROR_INVALID_DATA); + return(NULL); + } + + resource_dir = (IMAGE_RESOURCE_DIRECTORY *)get_ptr_from_rva (resource_rva, (IMAGE_NT_HEADERS32 *)nt_headers, file_map); + if (resource_dir == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Can't find resource directory", __func__); + + mono_w32error_set_last (ERROR_INVALID_DATA); + return(NULL); + } + + entries = GUINT16_FROM_LE (resource_dir->NumberOfNamedEntries) + GUINT16_FROM_LE (resource_dir->NumberOfIdEntries); + resource_dir_entry = (IMAGE_RESOURCE_DIRECTORY_ENTRY *)(resource_dir + 1); + + for (i = 0; i < entries; i++) { + IMAGE_RESOURCE_DIRECTORY_ENTRY *direntry = &resource_dir_entry[i]; + ret = scan_resource_dir (resource_dir, + (IMAGE_NT_HEADERS32 *)nt_headers, + file_map, direntry, 0, res_id, + lang_id, size); + if (ret != NULL) { + return(ret); + } + } + + return(NULL); +} + +static gpointer +find_pe_file_resources64 (gpointer file_map, guint32 map_size, guint32 res_id, guint32 lang_id, gsize *size) +{ + IMAGE_DOS_HEADER *dos_header; + IMAGE_NT_HEADERS64 *nt_headers; + IMAGE_RESOURCE_DIRECTORY *resource_dir; + IMAGE_RESOURCE_DIRECTORY_ENTRY *resource_dir_entry; + guint32 resource_rva, entries, i; + gpointer ret = NULL; + + dos_header = (IMAGE_DOS_HEADER *)file_map; + if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Bad dos signature 0x%x", __func__, dos_header->e_magic); + + mono_w32error_set_last (ERROR_INVALID_DATA); + return(NULL); + } + + if (map_size < sizeof(IMAGE_NT_HEADERS64) + GUINT32_FROM_LE (dos_header->e_lfanew)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: File is too small: %" G_GUINT32_FORMAT, __func__, map_size); + + mono_w32error_set_last (ERROR_BAD_LENGTH); + return(NULL); + } + + nt_headers = (IMAGE_NT_HEADERS64 *)((guint8 *)file_map + GUINT32_FROM_LE (dos_header->e_lfanew)); + if (nt_headers->Signature != IMAGE_NT_SIGNATURE) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Bad NT signature 0x%x", __func__, + nt_headers->Signature); + + mono_w32error_set_last (ERROR_INVALID_DATA); + return(NULL); + } + + if (nt_headers->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { + /* Do 64-bit stuff */ + resource_rva = GUINT32_FROM_LE (((IMAGE_NT_HEADERS64 *)nt_headers)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress); + } else { + resource_rva = GUINT32_FROM_LE (nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress); + } + + if (resource_rva == 0) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: No resources in file!", __func__); + + mono_w32error_set_last (ERROR_INVALID_DATA); + return(NULL); + } + + resource_dir = (IMAGE_RESOURCE_DIRECTORY *)get_ptr_from_rva (resource_rva, (IMAGE_NT_HEADERS32 *)nt_headers, file_map); + if (resource_dir == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Can't find resource directory", __func__); + + mono_w32error_set_last (ERROR_INVALID_DATA); + return(NULL); + } + + entries = GUINT16_FROM_LE (resource_dir->NumberOfNamedEntries) + GUINT16_FROM_LE (resource_dir->NumberOfIdEntries); + resource_dir_entry = (IMAGE_RESOURCE_DIRECTORY_ENTRY *)(resource_dir + 1); + + for (i = 0; i < entries; i++) { + IMAGE_RESOURCE_DIRECTORY_ENTRY *direntry = &resource_dir_entry[i]; + ret = scan_resource_dir (resource_dir, + (IMAGE_NT_HEADERS32 *)nt_headers, + file_map, direntry, 0, res_id, + lang_id, size); + if (ret != NULL) { + return(ret); + } + } + + return(NULL); +} + +static gpointer +find_pe_file_resources (gpointer file_map, guint32 map_size, guint32 res_id, guint32 lang_id, gsize *size) +{ + /* Figure this out when we support 64bit PE files */ + if (1) { + return find_pe_file_resources32 (file_map, map_size, res_id, + lang_id, size); + } else { + return find_pe_file_resources64 (file_map, map_size, res_id, + lang_id, size); + } +} + +static gpointer +map_pe_file (gunichar2 *filename, gint32 *map_size, void **handle) +{ + gchar *filename_ext; + int fd; + struct stat statbuf; + gpointer file_map; + + /* According to the MSDN docs, a search path is applied to + * filename. FIXME: implement this, for now just pass it + * straight to fopen + */ + + filename_ext = mono_unicode_to_external (filename); + if (filename_ext == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unicode conversion returned NULL", __func__); + + mono_w32error_set_last (ERROR_INVALID_NAME); + return(NULL); + } + + fd = open (filename_ext, O_RDONLY, 0); + if (fd == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) { + gint saved_errno; + gchar *located_filename; + + saved_errno = errno; + + located_filename = mono_portability_find_file (filename_ext, TRUE); + if (!located_filename) { + errno = saved_errno; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Error opening file %s (1): %s", __func__, filename_ext, strerror (errno)); + + g_free (filename_ext); + + mono_w32error_set_last (mono_w32error_unix_to_win32 (errno)); + return NULL; + } + + fd = open (located_filename, O_RDONLY, 0); + if (fd == -1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Error opening file %s (2): %s", __func__, filename_ext, strerror (errno)); + + g_free (filename_ext); + g_free (located_filename); + + mono_w32error_set_last (mono_w32error_unix_to_win32 (errno)); + return NULL; + } + + g_free (located_filename); + } + + if (fstat (fd, &statbuf) == -1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Error stat()ing file %s: %s", __func__, filename_ext, strerror (errno)); + + mono_w32error_set_last (mono_w32error_unix_to_win32 (errno)); + g_free (filename_ext); + close (fd); + return(NULL); + } + *map_size = statbuf.st_size; + + /* Check basic file size */ + if (statbuf.st_size < sizeof(IMAGE_DOS_HEADER)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: File %s is too small: %lld", __func__, filename_ext, (long long) statbuf.st_size); + + mono_w32error_set_last (ERROR_BAD_LENGTH); + g_free (filename_ext); + close (fd); + return(NULL); + } + + file_map = mono_file_map (statbuf.st_size, MONO_MMAP_READ | MONO_MMAP_PRIVATE, fd, 0, handle); + if (file_map == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Error mmap()int file %s: %s", __func__, filename_ext, strerror (errno)); + + mono_w32error_set_last (mono_w32error_unix_to_win32 (errno)); + g_free (filename_ext); + close (fd); + return(NULL); + } + + /* Don't need the fd any more */ + close (fd); + g_free (filename_ext); + + return(file_map); +} + +static void +unmap_pe_file (gpointer file_map, void *handle) +{ + gint res; + + res = mono_file_unmap (file_map, handle); + if (G_UNLIKELY (res != 0)) + g_error ("%s: mono_file_unmap failed, error: \"%s\" (%d)", __func__, g_strerror (errno), errno); +} + +static guint32 +unicode_chars (const gunichar2 *str) +{ + guint32 len = 0; + + do { + if (str[len] == '\0') { + return(len); + } + len++; + } while(1); +} + +static gboolean +unicode_compare (const gunichar2 *str1, const gunichar2 *str2) +{ + while (*str1 && *str2) { + if (*str1 != *str2) { + return(FALSE); + } + ++str1; + ++str2; + } + + return(*str1 == *str2); +} + +/* compare a little-endian null-terminated utf16 string and a normal string. + * Can be used only for ascii or latin1 chars. + */ +static gboolean +unicode_string_equals (const gunichar2 *str1, const gchar *str2) +{ + while (*str1 && *str2) { + if (GUINT16_TO_LE (*str1) != *str2) { + return(FALSE); + } + ++str1; + ++str2; + } + + return(*str1 == *str2); +} + +typedef struct { + guint16 data_len; + guint16 value_len; + guint16 type; + gunichar2 *key; +} version_data; + +/* Returns a pointer to the value data, because there's no way to know + * how big that data is (value_len is set to zero for most blocks :-( ) + */ +static gconstpointer +get_versioninfo_block (gconstpointer data, version_data *block) +{ + block->data_len = GUINT16_FROM_LE (*((guint16 *)data)); + data = (char *)data + sizeof(guint16); + block->value_len = GUINT16_FROM_LE (*((guint16 *)data)); + data = (char *)data + sizeof(guint16); + + /* No idea what the type is supposed to indicate */ + block->type = GUINT16_FROM_LE (*((guint16 *)data)); + data = (char *)data + sizeof(guint16); + block->key = ((gunichar2 *)data); + + /* Skip over the key (including the terminator) */ + data = ((gunichar2 *)data) + (unicode_chars (block->key) + 1); + + /* align on a 32-bit boundary */ + ALIGN32 (data); + + return(data); +} + +static gconstpointer +get_fixedfileinfo_block (gconstpointer data, version_data *block) +{ + gconstpointer data_ptr; + VS_FIXEDFILEINFO *ffi; + + data_ptr = get_versioninfo_block (data, block); + + if (block->value_len != sizeof(VS_FIXEDFILEINFO)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: FIXEDFILEINFO size mismatch", __func__); + return(NULL); + } + + if (!unicode_string_equals (block->key, "VS_VERSION_INFO")) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: VS_VERSION_INFO mismatch", __func__); + + return(NULL); + } + + ffi = ((VS_FIXEDFILEINFO *)data_ptr); + if ((ffi->dwSignature != VS_FFI_SIGNATURE) || + (ffi->dwStrucVersion != VS_FFI_STRUCVERSION)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: FIXEDFILEINFO bad signature", __func__); + + return(NULL); + } + + return(data_ptr); +} + +static gconstpointer +get_varfileinfo_block (gconstpointer data_ptr, version_data *block) +{ + /* data is pointing at a Var block + */ + data_ptr = get_versioninfo_block (data_ptr, block); + + return(data_ptr); +} + +static gconstpointer +get_string_block (gconstpointer data_ptr, const gunichar2 *string_key, gpointer *string_value, + guint32 *string_value_len, version_data *block) +{ + guint16 data_len = block->data_len; + guint16 string_len = 28; /* Length of the StringTable block */ + char *orig_data_ptr = (char *)data_ptr - 28; + + /* data_ptr is pointing at an array of one or more String blocks + * with total length (not including alignment padding) of + * data_len + */ + while (((char *)data_ptr - (char *)orig_data_ptr) < data_len) { + /* align on a 32-bit boundary */ + ALIGN32 (data_ptr); + + data_ptr = get_versioninfo_block (data_ptr, block); + if (block->data_len == 0) { + /* We must have hit padding, so give up + * processing now + */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Hit 0-length block, giving up", __func__); + + return(NULL); + } + + string_len = string_len + block->data_len; + + if (string_key != NULL && + string_value != NULL && + string_value_len != NULL && + unicode_compare (string_key, block->key) == TRUE) { + *string_value = (gpointer)data_ptr; + *string_value_len = block->value_len; + } + + /* Skip over the value */ + data_ptr = ((gunichar2 *)data_ptr) + block->value_len; + } + + return(data_ptr); +} + +/* Returns a pointer to the byte following the Stringtable block, or + * NULL if the data read hits padding. We can't recover from this + * because the data length does not include padding bytes, so it's not + * possible to just return the start position + length + * + * If lang == NULL it means we're just stepping through this block + */ +static gconstpointer +get_stringtable_block (gconstpointer data_ptr, gchar *lang, const gunichar2 *string_key, gpointer *string_value, + guint32 *string_value_len, version_data *block) +{ + guint16 data_len = block->data_len; + guint16 string_len = 36; /* length of the StringFileInfo block */ + gchar *found_lang; + gchar *lowercase_lang; + + /* data_ptr is pointing at an array of StringTable blocks, + * with total length (not including alignment padding) of + * data_len + */ + + while(string_len < data_len) { + /* align on a 32-bit boundary */ + ALIGN32 (data_ptr); + + data_ptr = get_versioninfo_block (data_ptr, block); + if (block->data_len == 0) { + /* We must have hit padding, so give up + * processing now + */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Hit 0-length block, giving up", __func__); + return(NULL); + } + + string_len = string_len + block->data_len; + + found_lang = g_utf16_to_utf8 (block->key, 8, NULL, NULL, NULL); + if (found_lang == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Didn't find a valid language key, giving up", __func__); + return(NULL); + } + + lowercase_lang = g_utf8_strdown (found_lang, -1); + g_free (found_lang); + found_lang = lowercase_lang; + lowercase_lang = NULL; + + if (lang != NULL && !strcmp (found_lang, lang)) { + /* Got the one we're interested in */ + data_ptr = get_string_block (data_ptr, string_key, + string_value, + string_value_len, block); + } else { + data_ptr = get_string_block (data_ptr, NULL, NULL, + NULL, block); + } + + g_free (found_lang); + + if (data_ptr == NULL) { + /* Child block hit padding */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Child block hit 0-length block, giving up", __func__); + return(NULL); + } + } + + return(data_ptr); +} + +#if G_BYTE_ORDER == G_BIG_ENDIAN +static gconstpointer +big_up_string_block (gconstpointer data_ptr, version_data *block) +{ + guint16 data_len = block->data_len; + guint16 string_len = 28; /* Length of the StringTable block */ + gchar *big_value; + char *orig_data_ptr = (char *)data_ptr - 28; + + /* data_ptr is pointing at an array of one or more String + * blocks with total length (not including alignment padding) + * of data_len + */ + while (((char *)data_ptr - (char *)orig_data_ptr) < data_len) { + /* align on a 32-bit boundary */ + ALIGN32 (data_ptr); + + data_ptr = get_versioninfo_block (data_ptr, block); + if (block->data_len == 0) { + /* We must have hit padding, so give up + * processing now + */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Hit 0-length block, giving up", __func__); + return(NULL); + } + + string_len = string_len + block->data_len; + + big_value = g_convert ((gchar *)block->key, + unicode_chars (block->key) * 2, + "UTF-16BE", "UTF-16LE", NULL, NULL, + NULL); + if (big_value == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Didn't find a valid string, giving up", __func__); + return(NULL); + } + + /* The swapped string should be exactly the same + * length as the original little-endian one, but only + * copy the number of original chars just to be on the + * safe side + */ + memcpy (block->key, big_value, unicode_chars (block->key) * 2); + g_free (big_value); + + big_value = g_convert ((gchar *)data_ptr, + unicode_chars (data_ptr) * 2, + "UTF-16BE", "UTF-16LE", NULL, NULL, + NULL); + if (big_value == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Didn't find a valid data string, giving up", __func__); + return(NULL); + } + memcpy ((gpointer)data_ptr, big_value, + unicode_chars (data_ptr) * 2); + g_free (big_value); + + data_ptr = ((gunichar2 *)data_ptr) + block->value_len; + } + + return(data_ptr); +} + +/* Returns a pointer to the byte following the Stringtable block, or + * NULL if the data read hits padding. We can't recover from this + * because the data length does not include padding bytes, so it's not + * possible to just return the start position + length + */ +static gconstpointer +big_up_stringtable_block (gconstpointer data_ptr, version_data *block) +{ + guint16 data_len = block->data_len; + guint16 string_len = 36; /* length of the StringFileInfo block */ + gchar *big_value; + + /* data_ptr is pointing at an array of StringTable blocks, + * with total length (not including alignment padding) of + * data_len + */ + + while(string_len < data_len) { + /* align on a 32-bit boundary */ + ALIGN32 (data_ptr); + + data_ptr = get_versioninfo_block (data_ptr, block); + if (block->data_len == 0) { + /* We must have hit padding, so give up + * processing now + */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Hit 0-length block, giving up", __func__); + return(NULL); + } + + string_len = string_len + block->data_len; + + big_value = g_convert ((gchar *)block->key, 16, "UTF-16BE", + "UTF-16LE", NULL, NULL, NULL); + if (big_value == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Didn't find a valid string, giving up", __func__); + return(NULL); + } + + memcpy (block->key, big_value, 16); + g_free (big_value); + + data_ptr = big_up_string_block (data_ptr, block); + + if (data_ptr == NULL) { + /* Child block hit padding */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Child block hit 0-length block, giving up", __func__); + return(NULL); + } + } + + return(data_ptr); +} + +/* Follows the data structures and turns all UTF-16 strings from the + * LE found in the resource section into UTF-16BE + */ +static void +big_up (gconstpointer datablock, guint32 size) +{ + gconstpointer data_ptr; + gint32 data_len; /* signed to guard against underflow */ + version_data block; + + data_ptr = get_fixedfileinfo_block (datablock, &block); + if (data_ptr != NULL) { + VS_FIXEDFILEINFO *ffi = (VS_FIXEDFILEINFO *)data_ptr; + + /* Byteswap all the fields */ + ffi->dwFileVersionMS = GUINT32_SWAP_LE_BE (ffi->dwFileVersionMS); + ffi->dwFileVersionLS = GUINT32_SWAP_LE_BE (ffi->dwFileVersionLS); + ffi->dwProductVersionMS = GUINT32_SWAP_LE_BE (ffi->dwProductVersionMS); + ffi->dwProductVersionLS = GUINT32_SWAP_LE_BE (ffi->dwProductVersionLS); + ffi->dwFileFlagsMask = GUINT32_SWAP_LE_BE (ffi->dwFileFlagsMask); + ffi->dwFileFlags = GUINT32_SWAP_LE_BE (ffi->dwFileFlags); + ffi->dwFileOS = GUINT32_SWAP_LE_BE (ffi->dwFileOS); + ffi->dwFileType = GUINT32_SWAP_LE_BE (ffi->dwFileType); + ffi->dwFileSubtype = GUINT32_SWAP_LE_BE (ffi->dwFileSubtype); + ffi->dwFileDateMS = GUINT32_SWAP_LE_BE (ffi->dwFileDateMS); + ffi->dwFileDateLS = GUINT32_SWAP_LE_BE (ffi->dwFileDateLS); + + /* The FFI and header occupies the first 92 bytes + */ + data_ptr = (char *)data_ptr + sizeof(VS_FIXEDFILEINFO); + data_len = block.data_len - 92; + + /* There now follow zero or one StringFileInfo blocks + * and zero or one VarFileInfo blocks + */ + while (data_len > 0) { + /* align on a 32-bit boundary */ + ALIGN32 (data_ptr); + + data_ptr = get_versioninfo_block (data_ptr, &block); + if (block.data_len == 0) { + /* We must have hit padding, so give + * up processing now + */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Hit 0-length block, giving up", __func__); + return; + } + + data_len = data_len - block.data_len; + + if (unicode_string_equals (block.key, "VarFileInfo")) { + data_ptr = get_varfileinfo_block (data_ptr, + &block); + data_ptr = ((guchar *)data_ptr) + block.value_len; + } else if (unicode_string_equals (block.key, + "StringFileInfo")) { + data_ptr = big_up_stringtable_block (data_ptr, + &block); + } else { + /* Bogus data */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Not a valid VERSIONINFO child block", __func__); + return; + } + + if (data_ptr == NULL) { + /* Child block hit padding */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Child block hit 0-length block, giving up", __func__); + return; + } + } + } +} +#endif + +gboolean +mono_w32process_get_fileversion_info (gunichar2 *filename, gpointer *data) +{ + gpointer file_map; + gpointer versioninfo; + void *map_handle; + gint32 map_size; + gsize datasize; + + g_assert (data); + *data = NULL; + + file_map = map_pe_file (filename, &map_size, &map_handle); + if (!file_map) + return FALSE; + + versioninfo = find_pe_file_resources (file_map, map_size, RT_VERSION, 0, &datasize); + if (!versioninfo) { + unmap_pe_file (file_map, map_handle); + return FALSE; + } + + *data = g_malloc0 (datasize); + + /* This could probably process the data so that mono_w32process_ver_query_value() doesn't have to follow the + * data blocks every time. But hey, these functions aren't likely to appear in many profiles. */ + memcpy (*data, versioninfo, datasize); + +#if G_BYTE_ORDER == G_BIG_ENDIAN + big_up (*data, datasize); +#endif + + unmap_pe_file (file_map, map_handle); + + return TRUE; +} + +gboolean +mono_w32process_ver_query_value (gconstpointer datablock, const gunichar2 *subblock, gpointer *buffer, guint32 *len) +{ + gchar *subblock_utf8, *lang_utf8 = NULL; + gboolean ret = FALSE; + version_data block; + gconstpointer data_ptr; + gint32 data_len; /* signed to guard against underflow */ + gboolean want_var = FALSE; + gboolean want_string = FALSE; + gunichar2 lang[8]; + const gunichar2 *string_key = NULL; + gpointer string_value = NULL; + guint32 string_value_len = 0; + gchar *lowercase_lang; + + subblock_utf8 = g_utf16_to_utf8 (subblock, -1, NULL, NULL, NULL); + if (subblock_utf8 == NULL) { + return(FALSE); + } + + if (!strcmp (subblock_utf8, "\\VarFileInfo\\Translation")) { + want_var = TRUE; + } else if (!strncmp (subblock_utf8, "\\StringFileInfo\\", 16)) { + want_string = TRUE; + memcpy (lang, subblock + 16, 8 * sizeof(gunichar2)); + lang_utf8 = g_utf16_to_utf8 (lang, 8, NULL, NULL, NULL); + lowercase_lang = g_utf8_strdown (lang_utf8, -1); + g_free (lang_utf8); + lang_utf8 = lowercase_lang; + lowercase_lang = NULL; + string_key = subblock + 25; + } + + if (!strcmp (subblock_utf8, "\\")) { + data_ptr = get_fixedfileinfo_block (datablock, &block); + if (data_ptr != NULL) { + *buffer = (gpointer)data_ptr; + *len = block.value_len; + + ret = TRUE; + } + } else if (want_var || want_string) { + data_ptr = get_fixedfileinfo_block (datablock, &block); + if (data_ptr != NULL) { + /* The FFI and header occupies the first 92 + * bytes + */ + data_ptr = (char *)data_ptr + sizeof(VS_FIXEDFILEINFO); + data_len = block.data_len - 92; + + /* There now follow zero or one StringFileInfo + * blocks and zero or one VarFileInfo blocks + */ + while (data_len > 0) { + /* align on a 32-bit boundary */ + ALIGN32 (data_ptr); + + data_ptr = get_versioninfo_block (data_ptr, + &block); + if (block.data_len == 0) { + /* We must have hit padding, + * so give up processing now + */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Hit 0-length block, giving up", __func__); + goto done; + } + + data_len = data_len - block.data_len; + + if (unicode_string_equals (block.key, "VarFileInfo")) { + data_ptr = get_varfileinfo_block (data_ptr, &block); + if (want_var) { + *buffer = (gpointer)data_ptr; + *len = block.value_len; + ret = TRUE; + goto done; + } else { + /* Skip over the Var block */ + data_ptr = ((guchar *)data_ptr) + block.value_len; + } + } else if (unicode_string_equals (block.key, "StringFileInfo")) { + data_ptr = get_stringtable_block (data_ptr, lang_utf8, string_key, &string_value, &string_value_len, &block); + if (want_string && + string_value != NULL && + string_value_len != 0) { + *buffer = string_value; + *len = unicode_chars ((const gunichar2 *)string_value) + 1; /* Include trailing null */ + ret = TRUE; + goto done; + } + } else { + /* Bogus data */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Not a valid VERSIONINFO child block", __func__); + goto done; + } + + if (data_ptr == NULL) { + /* Child block hit padding */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Child block hit 0-length block, giving up", __func__); + goto done; + } + } + } + } + + done: + if (lang_utf8) { + g_free (lang_utf8); + } + + g_free (subblock_utf8); + return(ret); +} + +static guint32 +copy_lang (gunichar2 *lang_out, guint32 lang_len, const gchar *text) +{ + gunichar2 *unitext; + int chars = strlen (text); + int ret; + + unitext = g_utf8_to_utf16 (text, -1, NULL, NULL, NULL); + g_assert (unitext != NULL); + + if (chars < (lang_len - 1)) { + memcpy (lang_out, (gpointer)unitext, chars * 2); + lang_out[chars] = '\0'; + ret = chars; + } else { + memcpy (lang_out, (gpointer)unitext, (lang_len - 1) * 2); + lang_out[lang_len] = '\0'; + ret = lang_len; + } + + g_free (unitext); + + return(ret); +} + +guint32 +mono_w32process_ver_language_name (guint32 lang, gunichar2 *lang_out, guint32 lang_len) +{ + int primary, secondary; + const char *name = NULL; + + primary = lang & 0x3FF; + secondary = (lang >> 10) & 0x3F; + + switch(primary) { + case 0x00: + switch (secondary) { + case 0x01: name = "Process Default Language"; break; + } + break; + case 0x01: + switch (secondary) { + case 0x00: + case 0x01: name = "Arabic (Saudi Arabia)"; break; + case 0x02: name = "Arabic (Iraq)"; break; + case 0x03: name = "Arabic (Egypt)"; break; + case 0x04: name = "Arabic (Libya)"; break; + case 0x05: name = "Arabic (Algeria)"; break; + case 0x06: name = "Arabic (Morocco)"; break; + case 0x07: name = "Arabic (Tunisia)"; break; + case 0x08: name = "Arabic (Oman)"; break; + case 0x09: name = "Arabic (Yemen)"; break; + case 0x0a: name = "Arabic (Syria)"; break; + case 0x0b: name = "Arabic (Jordan)"; break; + case 0x0c: name = "Arabic (Lebanon)"; break; + case 0x0d: name = "Arabic (Kuwait)"; break; + case 0x0e: name = "Arabic (U.A.E.)"; break; + case 0x0f: name = "Arabic (Bahrain)"; break; + case 0x10: name = "Arabic (Qatar)"; break; + } + break; + case 0x02: + switch (secondary) { + case 0x00: name = "Bulgarian (Bulgaria)"; break; + case 0x01: name = "Bulgarian"; break; + } + break; + case 0x03: + switch (secondary) { + case 0x00: name = "Catalan (Spain)"; break; + case 0x01: name = "Catalan"; break; + } + break; + case 0x04: + switch (secondary) { + case 0x00: + case 0x01: name = "Chinese (Taiwan)"; break; + case 0x02: name = "Chinese (PRC)"; break; + case 0x03: name = "Chinese (Hong Kong S.A.R.)"; break; + case 0x04: name = "Chinese (Singapore)"; break; + case 0x05: name = "Chinese (Macau S.A.R.)"; break; + } + break; + case 0x05: + switch (secondary) { + case 0x00: name = "Czech (Czech Republic)"; break; + case 0x01: name = "Czech"; break; + } + break; + case 0x06: + switch (secondary) { + case 0x00: name = "Danish (Denmark)"; break; + case 0x01: name = "Danish"; break; + } + break; + case 0x07: + switch (secondary) { + case 0x00: + case 0x01: name = "German (Germany)"; break; + case 0x02: name = "German (Switzerland)"; break; + case 0x03: name = "German (Austria)"; break; + case 0x04: name = "German (Luxembourg)"; break; + case 0x05: name = "German (Liechtenstein)"; break; + } + break; + case 0x08: + switch (secondary) { + case 0x00: name = "Greek (Greece)"; break; + case 0x01: name = "Greek"; break; + } + break; + case 0x09: + switch (secondary) { + case 0x00: + case 0x01: name = "English (United States)"; break; + case 0x02: name = "English (United Kingdom)"; break; + case 0x03: name = "English (Australia)"; break; + case 0x04: name = "English (Canada)"; break; + case 0x05: name = "English (New Zealand)"; break; + case 0x06: name = "English (Ireland)"; break; + case 0x07: name = "English (South Africa)"; break; + case 0x08: name = "English (Jamaica)"; break; + case 0x09: name = "English (Caribbean)"; break; + case 0x0a: name = "English (Belize)"; break; + case 0x0b: name = "English (Trinidad and Tobago)"; break; + case 0x0c: name = "English (Zimbabwe)"; break; + case 0x0d: name = "English (Philippines)"; break; + case 0x10: name = "English (India)"; break; + case 0x11: name = "English (Malaysia)"; break; + case 0x12: name = "English (Singapore)"; break; + } + break; + case 0x0a: + switch (secondary) { + case 0x00: name = "Spanish (Spain)"; break; + case 0x01: name = "Spanish (Traditional Sort)"; break; + case 0x02: name = "Spanish (Mexico)"; break; + case 0x03: name = "Spanish (International Sort)"; break; + case 0x04: name = "Spanish (Guatemala)"; break; + case 0x05: name = "Spanish (Costa Rica)"; break; + case 0x06: name = "Spanish (Panama)"; break; + case 0x07: name = "Spanish (Dominican Republic)"; break; + case 0x08: name = "Spanish (Venezuela)"; break; + case 0x09: name = "Spanish (Colombia)"; break; + case 0x0a: name = "Spanish (Peru)"; break; + case 0x0b: name = "Spanish (Argentina)"; break; + case 0x0c: name = "Spanish (Ecuador)"; break; + case 0x0d: name = "Spanish (Chile)"; break; + case 0x0e: name = "Spanish (Uruguay)"; break; + case 0x0f: name = "Spanish (Paraguay)"; break; + case 0x10: name = "Spanish (Bolivia)"; break; + case 0x11: name = "Spanish (El Salvador)"; break; + case 0x12: name = "Spanish (Honduras)"; break; + case 0x13: name = "Spanish (Nicaragua)"; break; + case 0x14: name = "Spanish (Puerto Rico)"; break; + case 0x15: name = "Spanish (United States)"; break; + } + break; + case 0x0b: + switch (secondary) { + case 0x00: name = "Finnish (Finland)"; break; + case 0x01: name = "Finnish"; break; + } + break; + case 0x0c: + switch (secondary) { + case 0x00: + case 0x01: name = "French (France)"; break; + case 0x02: name = "French (Belgium)"; break; + case 0x03: name = "French (Canada)"; break; + case 0x04: name = "French (Switzerland)"; break; + case 0x05: name = "French (Luxembourg)"; break; + case 0x06: name = "French (Monaco)"; break; + } + break; + case 0x0d: + switch (secondary) { + case 0x00: name = "Hebrew (Israel)"; break; + case 0x01: name = "Hebrew"; break; + } + break; + case 0x0e: + switch (secondary) { + case 0x00: name = "Hungarian (Hungary)"; break; + case 0x01: name = "Hungarian"; break; + } + break; + case 0x0f: + switch (secondary) { + case 0x00: name = "Icelandic (Iceland)"; break; + case 0x01: name = "Icelandic"; break; + } + break; + case 0x10: + switch (secondary) { + case 0x00: + case 0x01: name = "Italian (Italy)"; break; + case 0x02: name = "Italian (Switzerland)"; break; + } + break; + case 0x11: + switch (secondary) { + case 0x00: name = "Japanese (Japan)"; break; + case 0x01: name = "Japanese"; break; + } + break; + case 0x12: + switch (secondary) { + case 0x00: name = "Korean (Korea)"; break; + case 0x01: name = "Korean"; break; + } + break; + case 0x13: + switch (secondary) { + case 0x00: + case 0x01: name = "Dutch (Netherlands)"; break; + case 0x02: name = "Dutch (Belgium)"; break; + } + break; + case 0x14: + switch (secondary) { + case 0x00: + case 0x01: name = "Norwegian (Bokmal)"; break; + case 0x02: name = "Norwegian (Nynorsk)"; break; + } + break; + case 0x15: + switch (secondary) { + case 0x00: name = "Polish (Poland)"; break; + case 0x01: name = "Polish"; break; + } + break; + case 0x16: + switch (secondary) { + case 0x00: + case 0x01: name = "Portuguese (Brazil)"; break; + case 0x02: name = "Portuguese (Portugal)"; break; + } + break; + case 0x17: + switch (secondary) { + case 0x01: name = "Romansh (Switzerland)"; break; + } + break; + case 0x18: + switch (secondary) { + case 0x00: name = "Romanian (Romania)"; break; + case 0x01: name = "Romanian"; break; + } + break; + case 0x19: + switch (secondary) { + case 0x00: name = "Russian (Russia)"; break; + case 0x01: name = "Russian"; break; + } + break; + case 0x1a: + switch (secondary) { + case 0x00: name = "Croatian (Croatia)"; break; + case 0x01: name = "Croatian"; break; + case 0x02: name = "Serbian (Latin)"; break; + case 0x03: name = "Serbian (Cyrillic)"; break; + case 0x04: name = "Croatian (Bosnia and Herzegovina)"; break; + case 0x05: name = "Bosnian (Latin, Bosnia and Herzegovina)"; break; + case 0x06: name = "Serbian (Latin, Bosnia and Herzegovina)"; break; + case 0x07: name = "Serbian (Cyrillic, Bosnia and Herzegovina)"; break; + case 0x08: name = "Bosnian (Cyrillic, Bosnia and Herzegovina)"; break; + } + break; + case 0x1b: + switch (secondary) { + case 0x00: name = "Slovak (Slovakia)"; break; + case 0x01: name = "Slovak"; break; + } + break; + case 0x1c: + switch (secondary) { + case 0x00: name = "Albanian (Albania)"; break; + case 0x01: name = "Albanian"; break; + } + break; + case 0x1d: + switch (secondary) { + case 0x00: name = "Swedish (Sweden)"; break; + case 0x01: name = "Swedish"; break; + case 0x02: name = "Swedish (Finland)"; break; + } + break; + case 0x1e: + switch (secondary) { + case 0x00: name = "Thai (Thailand)"; break; + case 0x01: name = "Thai"; break; + } + break; + case 0x1f: + switch (secondary) { + case 0x00: name = "Turkish (Turkey)"; break; + case 0x01: name = "Turkish"; break; + } + break; + case 0x20: + switch (secondary) { + case 0x00: name = "Urdu (Islamic Republic of Pakistan)"; break; + case 0x01: name = "Urdu"; break; + } + break; + case 0x21: + switch (secondary) { + case 0x00: name = "Indonesian (Indonesia)"; break; + case 0x01: name = "Indonesian"; break; + } + break; + case 0x22: + switch (secondary) { + case 0x00: name = "Ukrainian (Ukraine)"; break; + case 0x01: name = "Ukrainian"; break; + } + break; + case 0x23: + switch (secondary) { + case 0x00: name = "Belarusian (Belarus)"; break; + case 0x01: name = "Belarusian"; break; + } + break; + case 0x24: + switch (secondary) { + case 0x00: name = "Slovenian (Slovenia)"; break; + case 0x01: name = "Slovenian"; break; + } + break; + case 0x25: + switch (secondary) { + case 0x00: name = "Estonian (Estonia)"; break; + case 0x01: name = "Estonian"; break; + } + break; + case 0x26: + switch (secondary) { + case 0x00: name = "Latvian (Latvia)"; break; + case 0x01: name = "Latvian"; break; + } + break; + case 0x27: + switch (secondary) { + case 0x00: name = "Lithuanian (Lithuania)"; break; + case 0x01: name = "Lithuanian"; break; + } + break; + case 0x28: + switch (secondary) { + case 0x01: name = "Tajik (Tajikistan)"; break; + } + break; + case 0x29: + switch (secondary) { + case 0x00: name = "Farsi (Iran)"; break; + case 0x01: name = "Farsi"; break; + } + break; + case 0x2a: + switch (secondary) { + case 0x00: name = "Vietnamese (Viet Nam)"; break; + case 0x01: name = "Vietnamese"; break; + } + break; + case 0x2b: + switch (secondary) { + case 0x00: name = "Armenian (Armenia)"; break; + case 0x01: name = "Armenian"; break; + } + break; + case 0x2c: + switch (secondary) { + case 0x00: name = "Azeri (Latin) (Azerbaijan)"; break; + case 0x01: name = "Azeri (Latin)"; break; + case 0x02: name = "Azeri (Cyrillic)"; break; + } + break; + case 0x2d: + switch (secondary) { + case 0x00: name = "Basque (Spain)"; break; + case 0x01: name = "Basque"; break; + } + break; + case 0x2e: + switch (secondary) { + case 0x01: name = "Upper Sorbian (Germany)"; break; + case 0x02: name = "Lower Sorbian (Germany)"; break; + } + break; + case 0x2f: + switch (secondary) { + case 0x00: name = "FYRO Macedonian (Former Yugoslav Republic of Macedonia)"; break; + case 0x01: name = "FYRO Macedonian"; break; + } + break; + case 0x32: + switch (secondary) { + case 0x00: name = "Tswana (South Africa)"; break; + case 0x01: name = "Tswana"; break; + } + break; + case 0x34: + switch (secondary) { + case 0x00: name = "Xhosa (South Africa)"; break; + case 0x01: name = "Xhosa"; break; + } + break; + case 0x35: + switch (secondary) { + case 0x00: name = "Zulu (South Africa)"; break; + case 0x01: name = "Zulu"; break; + } + break; + case 0x36: + switch (secondary) { + case 0x00: name = "Afrikaans (South Africa)"; break; + case 0x01: name = "Afrikaans"; break; + } + break; + case 0x37: + switch (secondary) { + case 0x00: name = "Georgian (Georgia)"; break; + case 0x01: name = "Georgian"; break; + } + break; + case 0x38: + switch (secondary) { + case 0x00: name = "Faroese (Faroe Islands)"; break; + case 0x01: name = "Faroese"; break; + } + break; + case 0x39: + switch (secondary) { + case 0x00: name = "Hindi (India)"; break; + case 0x01: name = "Hindi"; break; + } + break; + case 0x3a: + switch (secondary) { + case 0x00: name = "Maltese (Malta)"; break; + case 0x01: name = "Maltese"; break; + } + break; + case 0x3b: + switch (secondary) { + case 0x00: name = "Sami (Northern) (Norway)"; break; + case 0x01: name = "Sami, Northern (Norway)"; break; + case 0x02: name = "Sami, Northern (Sweden)"; break; + case 0x03: name = "Sami, Northern (Finland)"; break; + case 0x04: name = "Sami, Lule (Norway)"; break; + case 0x05: name = "Sami, Lule (Sweden)"; break; + case 0x06: name = "Sami, Southern (Norway)"; break; + case 0x07: name = "Sami, Southern (Sweden)"; break; + case 0x08: name = "Sami, Skolt (Finland)"; break; + case 0x09: name = "Sami, Inari (Finland)"; break; + } + break; + case 0x3c: + switch (secondary) { + case 0x02: name = "Irish (Ireland)"; break; + } + break; + case 0x3e: + switch (secondary) { + case 0x00: + case 0x01: name = "Malay (Malaysia)"; break; + case 0x02: name = "Malay (Brunei Darussalam)"; break; + } + break; + case 0x3f: + switch (secondary) { + case 0x00: name = "Kazakh (Kazakhstan)"; break; + case 0x01: name = "Kazakh"; break; + } + break; + case 0x40: + switch (secondary) { + case 0x00: name = "Kyrgyz (Kyrgyzstan)"; break; + case 0x01: name = "Kyrgyz (Cyrillic)"; break; + } + break; + case 0x41: + switch (secondary) { + case 0x00: name = "Swahili (Kenya)"; break; + case 0x01: name = "Swahili"; break; + } + break; + case 0x42: + switch (secondary) { + case 0x01: name = "Turkmen (Turkmenistan)"; break; + } + break; + case 0x43: + switch (secondary) { + case 0x00: name = "Uzbek (Latin) (Uzbekistan)"; break; + case 0x01: name = "Uzbek (Latin)"; break; + case 0x02: name = "Uzbek (Cyrillic)"; break; + } + break; + case 0x44: + switch (secondary) { + case 0x00: name = "Tatar (Russia)"; break; + case 0x01: name = "Tatar"; break; + } + break; + case 0x45: + switch (secondary) { + case 0x00: + case 0x01: name = "Bengali (India)"; break; + } + break; + case 0x46: + switch (secondary) { + case 0x00: name = "Punjabi (India)"; break; + case 0x01: name = "Punjabi"; break; + } + break; + case 0x47: + switch (secondary) { + case 0x00: name = "Gujarati (India)"; break; + case 0x01: name = "Gujarati"; break; + } + break; + case 0x49: + switch (secondary) { + case 0x00: name = "Tamil (India)"; break; + case 0x01: name = "Tamil"; break; + } + break; + case 0x4a: + switch (secondary) { + case 0x00: name = "Telugu (India)"; break; + case 0x01: name = "Telugu"; break; + } + break; + case 0x4b: + switch (secondary) { + case 0x00: name = "Kannada (India)"; break; + case 0x01: name = "Kannada"; break; + } + break; + case 0x4c: + switch (secondary) { + case 0x00: + case 0x01: name = "Malayalam (India)"; break; + } + break; + case 0x4d: + switch (secondary) { + case 0x01: name = "Assamese (India)"; break; + } + break; + case 0x4e: + switch (secondary) { + case 0x00: name = "Marathi (India)"; break; + case 0x01: name = "Marathi"; break; + } + break; + case 0x4f: + switch (secondary) { + case 0x00: name = "Sanskrit (India)"; break; + case 0x01: name = "Sanskrit"; break; + } + break; + case 0x50: + switch (secondary) { + case 0x00: name = "Mongolian (Mongolia)"; break; + case 0x01: name = "Mongolian (Cyrillic)"; break; + case 0x02: name = "Mongolian (PRC)"; break; + } + break; + case 0x51: + switch (secondary) { + case 0x01: name = "Tibetan (PRC)"; break; + case 0x02: name = "Tibetan (Bhutan)"; break; + } + break; + case 0x52: + switch (secondary) { + case 0x00: name = "Welsh (United Kingdom)"; break; + case 0x01: name = "Welsh"; break; + } + break; + case 0x53: + switch (secondary) { + case 0x01: name = "Khmer (Cambodia)"; break; + } + break; + case 0x54: + switch (secondary) { + case 0x01: name = "Lao (Lao PDR)"; break; + } + break; + case 0x56: + switch (secondary) { + case 0x00: name = "Galician (Spain)"; break; + case 0x01: name = "Galician"; break; + } + break; + case 0x57: + switch (secondary) { + case 0x00: name = "Konkani (India)"; break; + case 0x01: name = "Konkani"; break; + } + break; + case 0x5a: + switch (secondary) { + case 0x00: name = "Syriac (Syria)"; break; + case 0x01: name = "Syriac"; break; + } + break; + case 0x5b: + switch (secondary) { + case 0x01: name = "Sinhala (Sri Lanka)"; break; + } + break; + case 0x5d: + switch (secondary) { + case 0x01: name = "Inuktitut (Syllabics, Canada)"; break; + case 0x02: name = "Inuktitut (Latin, Canada)"; break; + } + break; + case 0x5e: + switch (secondary) { + case 0x01: name = "Amharic (Ethiopia)"; break; + } + break; + case 0x5f: + switch (secondary) { + case 0x02: name = "Tamazight (Algeria, Latin)"; break; + } + break; + case 0x61: + switch (secondary) { + case 0x01: name = "Nepali (Nepal)"; break; + } + break; + case 0x62: + switch (secondary) { + case 0x01: name = "Frisian (Netherlands)"; break; + } + break; + case 0x63: + switch (secondary) { + case 0x01: name = "Pashto (Afghanistan)"; break; + } + break; + case 0x64: + switch (secondary) { + case 0x01: name = "Filipino (Philippines)"; break; + } + break; + case 0x65: + switch (secondary) { + case 0x00: name = "Divehi (Maldives)"; break; + case 0x01: name = "Divehi"; break; + } + break; + case 0x68: + switch (secondary) { + case 0x01: name = "Hausa (Nigeria, Latin)"; break; + } + break; + case 0x6a: + switch (secondary) { + case 0x01: name = "Yoruba (Nigeria)"; break; + } + break; + case 0x6b: + switch (secondary) { + case 0x00: + case 0x01: name = "Quechua (Bolivia)"; break; + case 0x02: name = "Quechua (Ecuador)"; break; + case 0x03: name = "Quechua (Peru)"; break; + } + break; + case 0x6c: + switch (secondary) { + case 0x00: name = "Northern Sotho (South Africa)"; break; + case 0x01: name = "Northern Sotho"; break; + } + break; + case 0x6d: + switch (secondary) { + case 0x01: name = "Bashkir (Russia)"; break; + } + break; + case 0x6e: + switch (secondary) { + case 0x01: name = "Luxembourgish (Luxembourg)"; break; + } + break; + case 0x6f: + switch (secondary) { + case 0x01: name = "Greenlandic (Greenland)"; break; + } + break; + case 0x78: + switch (secondary) { + case 0x01: name = "Yi (PRC)"; break; + } + break; + case 0x7a: + switch (secondary) { + case 0x01: name = "Mapudungun (Chile)"; break; + } + break; + case 0x7c: + switch (secondary) { + case 0x01: name = "Mohawk (Mohawk)"; break; + } + break; + case 0x7e: + switch (secondary) { + case 0x01: name = "Breton (France)"; break; + } + break; + case 0x7f: + switch (secondary) { + case 0x00: name = "Invariant Language (Invariant Country)"; break; + } + break; + case 0x80: + switch (secondary) { + case 0x01: name = "Uighur (PRC)"; break; + } + break; + case 0x81: + switch (secondary) { + case 0x00: name = "Maori (New Zealand)"; break; + case 0x01: name = "Maori"; break; + } + break; + case 0x83: + switch (secondary) { + case 0x01: name = "Corsican (France)"; break; + } + break; + case 0x84: + switch (secondary) { + case 0x01: name = "Alsatian (France)"; break; + } + break; + case 0x85: + switch (secondary) { + case 0x01: name = "Yakut (Russia)"; break; + } + break; + case 0x86: + switch (secondary) { + case 0x01: name = "K'iche (Guatemala)"; break; + } + break; + case 0x87: + switch (secondary) { + case 0x01: name = "Kinyarwanda (Rwanda)"; break; + } + break; + case 0x88: + switch (secondary) { + case 0x01: name = "Wolof (Senegal)"; break; + } + break; + case 0x8c: + switch (secondary) { + case 0x01: name = "Dari (Afghanistan)"; break; + } + break; + + default: + name = "Language Neutral"; + + } + + if (!name) + name = "Language Neutral"; + + return copy_lang (lang_out, lang_len, name); +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32process-win32-internals.h b/unity-2019.4.24f1-mbe/mono/metadata/w32process-win32-internals.h new file mode 100644 index 000000000..147920248 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32process-win32-internals.h @@ -0,0 +1,43 @@ +/** + * \file + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#ifndef __MONO_METADATA_PROCESS_INTERNALS_H__ +#define __MONO_METADATA_PROCESS_INTERNALS_H__ + +#include +#include + +// On platforms not using classic WIN API support the implementation of bellow methods are hosted in separate source file +// process-windows-*.c. On platforms using classic WIN API the implementation is still keept in process.c and still declared +// static and in some places even inlined. +#if !G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +void +mono_w32process_get_fileversion (MonoObject *filever, gunichar2 *filename, MonoError *error); + +void +mono_process_init_startup_info (HANDLE stdin_handle, HANDLE stdout_handle, + HANDLE stderr_handle,STARTUPINFO *startinfo); + +gboolean +mono_process_create_process (MonoW32ProcessInfo *mono_process_info, MonoString *cmd, guint32 creation_flags, + gunichar2 *env_vars, gunichar2 *dir, STARTUPINFO *start_info, PROCESS_INFORMATION *process_info); + +MonoBoolean +mono_icall_get_process_working_set_size (gpointer handle, gsize *min, gsize *max); + +MonoBoolean +mono_icall_set_process_working_set_size (gpointer handle, gsize min, gsize max); + +gint32 +mono_icall_get_priority_class (gpointer handle); + +MonoBoolean +mono_icall_set_priority_class (gpointer handle, gint32 priorityClass); + +gboolean +mono_process_win_enum_processes (DWORD *pids, DWORD count, DWORD *needed); +#endif /* !G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ + +#endif /* __MONO_METADATA_PROCESS_INTERNALS_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32process-win32-uwp.c b/unity-2019.4.24f1-mbe/mono/metadata/w32process-win32-uwp.c new file mode 100644 index 000000000..be769f4b3 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32process-win32-uwp.c @@ -0,0 +1,224 @@ +/** + * \file + * UWP process support for Mono. + * + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. +*/ +#include +#include +#include "mono/utils/mono-compiler.h" + +#if G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) +#include +#include +#include "mono/metadata/w32process.h" +#include "mono/metadata/w32process-internals.h" +#include "mono/metadata/w32process-win32-internals.h" + +gboolean +mono_process_win_enum_processes (DWORD *pids, DWORD count, DWORD *needed) +{ + g_unsupported_api ("EnumProcesses"); + *needed = 0; + SetLastError (ERROR_NOT_SUPPORTED); + + return FALSE; +} + +HANDLE +ves_icall_System_Diagnostics_Process_GetProcess_internal (guint32 pid) +{ + MonoError mono_error; + error_init (&mono_error); + + g_unsupported_api ("OpenProcess"); + + mono_error_set_not_supported (&mono_error, G_UNSUPPORTED_API, "OpenProcess"); + mono_error_set_pending_exception (&mono_error); + + SetLastError (ERROR_NOT_SUPPORTED); + + return NULL; +} + +void +mono_w32process_get_fileversion (MonoObject *filever, gunichar2 *filename, MonoError *error) +{ + g_unsupported_api ("GetFileVersionInfoSize, GetFileVersionInfo, VerQueryValue, VerLanguageName"); + + error_init (error); + mono_error_set_not_supported (error, G_UNSUPPORTED_API, "GetFileVersionInfoSize, GetFileVersionInfo, VerQueryValue, VerLanguageName"); + + SetLastError (ERROR_NOT_SUPPORTED); +} + +MonoObject* +process_add_module (HANDLE process, HMODULE mod, gunichar2 *filename, gunichar2 *modulename, MonoClass *proc_class, MonoError *error) +{ + g_unsupported_api ("GetModuleInformation"); + + error_init (error); + mono_error_set_not_supported (error, G_UNSUPPORTED_API, "GetModuleInformation"); + + SetLastError (ERROR_NOT_SUPPORTED); + + return NULL; +} + +MonoArray * +ves_icall_System_Diagnostics_Process_GetModules_internal (MonoObject *this_obj, HANDLE process) +{ + MonoError mono_error; + error_init (&mono_error); + + g_unsupported_api ("EnumProcessModules, GetModuleBaseName, GetModuleFileNameEx"); + + mono_error_set_not_supported (&mono_error, G_UNSUPPORTED_API, "EnumProcessModules, GetModuleBaseName, GetModuleFileNameEx"); + mono_error_set_pending_exception (&mono_error); + + SetLastError (ERROR_NOT_SUPPORTED); + + return NULL; +} + +MonoBoolean +ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoW32ProcessStartInfo *proc_start_info, MonoW32ProcessInfo *process_info) +{ + MonoError mono_error; + error_init (&mono_error); + + g_unsupported_api ("ShellExecuteEx"); + + mono_error_set_not_supported (&mono_error, G_UNSUPPORTED_API, "ShellExecuteEx"); + mono_error_set_pending_exception (&mono_error); + + process_info->pid = (guint32)(-ERROR_NOT_SUPPORTED); + SetLastError (ERROR_NOT_SUPPORTED); + + return FALSE; +} + +MonoString * +ves_icall_System_Diagnostics_Process_ProcessName_internal (HANDLE process) +{ + MonoError error; + MonoString *string; + gunichar2 name[MAX_PATH]; + guint32 len; + + len = GetModuleFileName (NULL, name, G_N_ELEMENTS (name)); + if (len == 0) + return NULL; + + string = mono_string_new_utf16_checked (mono_domain_get (), name, len, &error); + if (!mono_error_ok (&error)) + mono_error_set_pending_exception (&error); + + return string; +} + +void +mono_process_init_startup_info (HANDLE stdin_handle, HANDLE stdout_handle, HANDLE stderr_handle, STARTUPINFO *startinfo) +{ + startinfo->cb = sizeof(STARTUPINFO); + startinfo->dwFlags = 0; + startinfo->hStdInput = INVALID_HANDLE_VALUE; + startinfo->hStdOutput = INVALID_HANDLE_VALUE; + startinfo->hStdError = INVALID_HANDLE_VALUE; + return; +} + +gboolean +mono_process_create_process (MonoW32ProcessInfo *mono_process_info, MonoString *cmd, guint32 creation_flags, + gunichar2 *env_vars, gunichar2 *dir, STARTUPINFO *start_info, PROCESS_INFORMATION *process_info) +{ + MonoError mono_error; + gchar *api_name = ""; + + if (mono_process_info->username) { + api_name = "CreateProcessWithLogonW"; + } else { + api_name = "CreateProcess"; + } + + memset (&process_info, 0, sizeof (PROCESS_INFORMATION)); + g_unsupported_api (api_name); + + error_init (&mono_error); + mono_error_set_not_supported (&mono_error, G_UNSUPPORTED_API, api_name); + mono_error_set_pending_exception (&mono_error); + + SetLastError (ERROR_NOT_SUPPORTED); + + return FALSE; +} + +MonoBoolean +mono_icall_get_process_working_set_size (gpointer handle, gsize *min, gsize *max) +{ + MonoError mono_error; + error_init (&mono_error); + + g_unsupported_api ("GetProcessWorkingSetSize"); + + mono_error_set_not_supported(&mono_error, G_UNSUPPORTED_API, "GetProcessWorkingSetSize"); + mono_error_set_pending_exception (&mono_error); + + SetLastError (ERROR_NOT_SUPPORTED); + + return FALSE; +} + +MonoBoolean +mono_icall_set_process_working_set_size (gpointer handle, gsize min, gsize max) +{ + MonoError mono_error; + error_init (&mono_error); + + g_unsupported_api ("SetProcessWorkingSetSize"); + + mono_error_set_not_supported (&mono_error, G_UNSUPPORTED_API, "SetProcessWorkingSetSize"); + mono_error_set_pending_exception (&mono_error); + + SetLastError (ERROR_NOT_SUPPORTED); + + return FALSE; +} + +gint32 +mono_icall_get_priority_class (gpointer handle) +{ + MonoError mono_error; + error_init (&mono_error); + + g_unsupported_api ("GetPriorityClass"); + + mono_error_set_not_supported (&mono_error, G_UNSUPPORTED_API, "GetPriorityClass"); + mono_error_set_pending_exception (&mono_error); + + SetLastError (ERROR_NOT_SUPPORTED); + + return FALSE; +} + +MonoBoolean +mono_icall_set_priority_class (gpointer handle, gint32 priorityClass) +{ + MonoError mono_error; + error_init (&mono_error); + + g_unsupported_api ("SetPriorityClass"); + + mono_error_set_not_supported(&mono_error, G_UNSUPPORTED_API, "SetPriorityClass"); + mono_error_set_pending_exception (&mono_error); + + SetLastError (ERROR_NOT_SUPPORTED); + + return FALSE; +} + +#else /* G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) */ + +MONO_EMPTY_SOURCE_FILE (process_windows_uwp); +#endif /* G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32process-win32.c b/unity-2019.4.24f1-mbe/mono/metadata/w32process-win32.c new file mode 100644 index 000000000..63d02a703 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32process-win32.c @@ -0,0 +1,480 @@ +/** + * \file + * System.Diagnostics.Process support + * + * Author: + * Dick Porter (dick@ximian.com) + * + * Copyright 2002 Ximian, Inc. + * Copyright 2002-2006 Novell, Inc. + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* FIXME: fix this code to not depend so much on the internals */ +#include +#include +#include + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +#include +#endif + +void +mono_w32process_init (void) +{ +} + +void +mono_w32process_cleanup (void) +{ +} + +void +mono_w32process_signal_finished (void) +{ +} + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +HANDLE +ves_icall_System_Diagnostics_Process_GetProcess_internal (guint32 pid) +{ + HANDLE handle; + + /* GetCurrentProcess returns a pseudo-handle, so use + * OpenProcess instead + */ + handle = OpenProcess (PROCESS_ALL_ACCESS, TRUE, pid); + if (handle == NULL) + /* FIXME: Throw an exception */ + return NULL; + return handle; +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */ + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +MonoBoolean +ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoW32ProcessStartInfo *proc_start_info, MonoW32ProcessInfo *process_info) +{ + SHELLEXECUTEINFO shellex = {0}; + gboolean ret; + + shellex.cbSize = sizeof(SHELLEXECUTEINFO); + shellex.fMask = (gulong)(SEE_MASK_FLAG_DDEWAIT | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_UNICODE); + shellex.nShow = (gulong)proc_start_info->window_style; + shellex.nShow = (gulong)((shellex.nShow == 0) ? 1 : (shellex.nShow == 1 ? 0 : shellex.nShow)); + + if (proc_start_info->filename != NULL) { + shellex.lpFile = mono_string_chars (proc_start_info->filename); + } + + if (proc_start_info->arguments != NULL) { + shellex.lpParameters = mono_string_chars (proc_start_info->arguments); + } + + if (proc_start_info->verb != NULL && + mono_string_length (proc_start_info->verb) != 0) { + shellex.lpVerb = mono_string_chars (proc_start_info->verb); + } + + if (proc_start_info->working_directory != NULL && + mono_string_length (proc_start_info->working_directory) != 0) { + shellex.lpDirectory = mono_string_chars (proc_start_info->working_directory); + } + + if (proc_start_info->error_dialog) { + shellex.hwnd = proc_start_info->error_dialog_parent_handle; + } else { + shellex.fMask = (gulong)(shellex.fMask | SEE_MASK_FLAG_NO_UI); + } + + ret = ShellExecuteEx (&shellex); + if (ret == FALSE) { + process_info->pid = -GetLastError (); + } else { + process_info->process_handle = shellex.hProcess; +#if !defined(MONO_CROSS_COMPILE) + process_info->pid = GetProcessId (shellex.hProcess); +#else + process_info->pid = 0; +#endif + } + + return ret; +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +static inline void +mono_process_init_startup_info (HANDLE stdin_handle, HANDLE stdout_handle, HANDLE stderr_handle, STARTUPINFO *startinfo) +{ + startinfo->cb = sizeof(STARTUPINFO); + startinfo->dwFlags = STARTF_USESTDHANDLES; + startinfo->hStdInput = stdin_handle; + startinfo->hStdOutput = stdout_handle; + startinfo->hStdError = stderr_handle; + return; +} + +static gboolean +mono_process_create_process (MonoW32ProcessInfo *mono_process_info, MonoString *cmd, guint32 creation_flags, + gunichar2 *env_vars, gunichar2 *dir, STARTUPINFO *start_info, PROCESS_INFORMATION *process_info) +{ + gboolean result = FALSE; + + if (mono_process_info->username) { + guint32 logon_flags = mono_process_info->load_user_profile ? LOGON_WITH_PROFILE : 0; + + result = CreateProcessWithLogonW (mono_string_chars (mono_process_info->username), + mono_process_info->domain ? mono_string_chars (mono_process_info->domain) : NULL, + (const gunichar2 *)mono_process_info->password, + logon_flags, + NULL, + cmd ? mono_string_chars (cmd) : NULL, + creation_flags, + env_vars, dir, start_info, process_info); + + } else { + + result = CreateProcessW (NULL, + cmd ? mono_string_chars (cmd): NULL, + NULL, + NULL, + TRUE, + creation_flags, + env_vars, + dir, + start_info, + process_info); + + } + + return result; +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ + +static gchar* +process_unquote_application_name (gchar *appname) +{ + size_t len = strlen (appname); + if (len) { + if (appname[len-1] == '\"') + appname[len-1] = '\0'; + if (appname[0] == '\"') + appname++; + } + + return appname; +} + +static gchar* +process_quote_path (const gchar *path) +{ + 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 */ +static gboolean +process_complete_path (const gunichar2 *appname, gchar **completed) +{ + gchar *utf8app, *utf8appmemory; + gchar *found; + + utf8appmemory = g_utf16_to_utf8 (appname, -1, NULL, NULL, NULL); + utf8app = process_unquote_application_name (utf8appmemory); + + if (g_path_is_absolute (utf8app)) { + *completed = process_quote_path (utf8app); + g_free (utf8appmemory); + return TRUE; + } + + if (g_file_test (utf8app, G_FILE_TEST_IS_EXECUTABLE) && !g_file_test (utf8app, G_FILE_TEST_IS_DIR)) { + *completed = process_quote_path (utf8app); + g_free (utf8appmemory); + return TRUE; + } + + found = g_find_program_in_path (utf8app); + if (found == NULL) { + *completed = NULL; + g_free (utf8appmemory); + return FALSE; + } + + *completed = process_quote_path (found); + g_free (found); + g_free (utf8appmemory); + return TRUE; +} + +static gboolean +process_get_shell_arguments (MonoW32ProcessStartInfo *proc_start_info, MonoString **cmd) +{ + gchar *spath = NULL; + gchar *new_cmd, *cmd_utf8; + MonoError mono_error; + + *cmd = proc_start_info->arguments; + + if (process_complete_path (mono_string_chars (proc_start_info->filename), &spath)) { + /* Seems like our CreateProcess does not work as the windows one. + * This hack is needed to deal with paths containing spaces */ + if (*cmd) { + cmd_utf8 = mono_string_to_utf8_checked (*cmd, &mono_error); + if (!mono_error_set_pending_exception (&mono_error)) { + new_cmd = g_strdup_printf ("%s %s", spath, cmd_utf8); + *cmd = mono_string_new_wrapper (new_cmd); + g_free (cmd_utf8); + g_free (new_cmd); + } else { + *cmd = NULL; + } + } + else { + *cmd = mono_string_new_wrapper (spath); + } + + g_free (spath); + } + + return (*cmd != NULL) ? TRUE : FALSE; +} + +MonoBoolean +ves_icall_System_Diagnostics_Process_CreateProcess_internal (MonoW32ProcessStartInfo *proc_start_info, HANDLE stdin_handle, + HANDLE stdout_handle, HANDLE stderr_handle, MonoW32ProcessInfo *process_info) +{ + gboolean ret; + gunichar2 *dir; + STARTUPINFO startinfo={0}; + PROCESS_INFORMATION procinfo; + gunichar2 *env_vars = NULL; + MonoString *cmd = NULL; + guint32 creation_flags; + + mono_process_init_startup_info (stdin_handle, stdout_handle, stderr_handle, &startinfo); + + creation_flags = CREATE_UNICODE_ENVIRONMENT; + if (proc_start_info->create_no_window) + creation_flags |= CREATE_NO_WINDOW; + + if (process_get_shell_arguments (proc_start_info, &cmd) == FALSE) { + process_info->pid = -ERROR_FILE_NOT_FOUND; + return FALSE; + } + + if (process_info->env_variables) { + gint i, len; + MonoString *var; + gunichar2 *str, *ptr; + + len = 0; + + for (i = 0; i < mono_array_length (process_info->env_variables); i++) { + var = mono_array_get (process_info->env_variables, MonoString*, i); + + len += mono_string_length (var) * sizeof (gunichar2); + + /* null-separated */ + len += sizeof (gunichar2); + } + /* null-terminated */ + len += sizeof (gunichar2); + + env_vars = ptr = g_new0 (gunichar2, len); + + for (i = 0; i < mono_array_length (process_info->env_variables); i++) { + var = mono_array_get (process_info->env_variables, MonoString*, i); + + memcpy (ptr, mono_string_chars (var), mono_string_length (var) * sizeof (gunichar2)); + ptr += mono_string_length (var); + ptr += 1; // Skip over the null-separator + } + } + + /* The default dir name is "". Turn that into NULL to mean + * "current directory" + */ + if (proc_start_info->working_directory == NULL || mono_string_length (proc_start_info->working_directory) == 0) + dir = NULL; + else + dir = mono_string_chars (proc_start_info->working_directory); + + ret = mono_process_create_process (process_info, cmd, creation_flags, env_vars, dir, &startinfo, &procinfo); + + g_free (env_vars); + + if (ret) { + process_info->process_handle = procinfo.hProcess; + /*process_info->thread_handle=procinfo.hThread;*/ + if (procinfo.hThread != NULL && procinfo.hThread != INVALID_HANDLE_VALUE) + CloseHandle (procinfo.hThread); + process_info->pid = procinfo.dwProcessId; + } else { + process_info->pid = -GetLastError (); + } + + return ret; +} + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +static inline gboolean +mono_process_win_enum_processes (DWORD *pids, DWORD count, DWORD *needed) +{ + return EnumProcesses (pids, count, needed); +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ + +MonoArray * +ves_icall_System_Diagnostics_Process_GetProcesses_internal (void) +{ + MonoError error; + MonoArray *procs; + gboolean ret; + DWORD needed; + int count; + DWORD *pids; + + count = 512; + do { + pids = g_new0 (DWORD, count); + ret = mono_process_win_enum_processes (pids, count * sizeof (guint32), &needed); + if (ret == FALSE) { + MonoException *exc; + + g_free (pids); + pids = NULL; + exc = mono_get_exception_not_supported ("This system does not support EnumProcesses"); + mono_set_pending_exception (exc); + return NULL; + } + if (needed < (count * sizeof (guint32))) + break; + g_free (pids); + pids = NULL; + count = (count * 3) / 2; + } while (TRUE); + + count = needed / sizeof (guint32); + procs = mono_array_new_checked (mono_domain_get (), mono_get_int32_class (), count, &error); + if (mono_error_set_pending_exception (&error)) { + g_free (pids); + return NULL; + } + + memcpy (mono_array_addr (procs, guint32, 0), pids, needed); + g_free (pids); + pids = NULL; + + return procs; +} + +MonoBoolean +ves_icall_Microsoft_Win32_NativeMethods_CloseProcess (gpointer handle) +{ + return CloseHandle (handle); +} + +MonoBoolean +ves_icall_Microsoft_Win32_NativeMethods_TerminateProcess (gpointer handle, gint32 exitcode) +{ + return TerminateProcess (handle, exitcode); +} + +MonoBoolean +ves_icall_Microsoft_Win32_NativeMethods_GetExitCodeProcess (gpointer handle, gint32 *exitcode) +{ + return GetExitCodeProcess (handle, exitcode); +} + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +static inline MonoBoolean +mono_icall_get_process_working_set_size (gpointer handle, gsize *min, gsize *max) +{ + return GetProcessWorkingSetSize (handle, min, max); +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ + +MonoBoolean +ves_icall_Microsoft_Win32_NativeMethods_GetProcessWorkingSetSize (gpointer handle, gsize *min, gsize *max) +{ + return mono_icall_get_process_working_set_size (handle, min, max); +} + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +static inline MonoBoolean +mono_icall_set_process_working_set_size (gpointer handle, gsize min, gsize max) +{ + return SetProcessWorkingSetSize (handle, min, max); +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ + +MonoBoolean +ves_icall_Microsoft_Win32_NativeMethods_SetProcessWorkingSetSize (gpointer handle, gsize min, gsize max) +{ + return mono_icall_set_process_working_set_size (handle, min, max); +} + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +static inline gint32 +mono_icall_get_priority_class (gpointer handle) +{ + return GetPriorityClass (handle); +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ + +gint32 +ves_icall_Microsoft_Win32_NativeMethods_GetPriorityClass (gpointer handle) +{ + return mono_icall_get_priority_class (handle); +} + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +static inline MonoBoolean +mono_icall_set_priority_class (gpointer handle, gint32 priorityClass) +{ + return SetPriorityClass (handle, (guint32) priorityClass); +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ + +MonoBoolean +ves_icall_Microsoft_Win32_NativeMethods_SetPriorityClass (gpointer handle, gint32 priorityClass) +{ + return mono_icall_set_priority_class (handle, priorityClass); +} + +MonoBoolean +ves_icall_Microsoft_Win32_NativeMethods_GetProcessTimes (gpointer handle, gint64 *creationtime, gint64 *exittime, gint64 *kerneltime, gint64 *usertime) +{ + return GetProcessTimes (handle, (LPFILETIME) creationtime, (LPFILETIME) exittime, (LPFILETIME) kerneltime, (LPFILETIME) usertime); +} + +gpointer +ves_icall_Microsoft_Win32_NativeMethods_GetCurrentProcess (void) +{ + return GetCurrentProcess (); +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32process.c b/unity-2019.4.24f1-mbe/mono/metadata/w32process.c new file mode 100644 index 000000000..abfcfe600 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32process.c @@ -0,0 +1,655 @@ +/** + * \file + */ + +#include + +#include "w32process.h" +#include "w32process-internals.h" +#include "w32process-win32-internals.h" +#include "w32file.h" +#include "object.h" +#include "object-internals.h" +#include "class.h" +#include "class-internals.h" +#include "image.h" +#include "utils/mono-proclib.h" +#include "utils/w32api.h" + +#define LOGDEBUG(...) +/* define LOGDEBUG(...) g_message(__VA_ARGS__) */ + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) && defined(HOST_WIN32) + +static guint32 +mono_w32process_get_pid (gpointer handle) +{ + return GetProcessId (handle); +} + +static gboolean +mono_w32process_try_get_modules (gpointer process, gpointer *modules, guint32 size, guint32 *needed) +{ + return EnumProcessModules (process, (HMODULE *) modules, size, (LPDWORD) needed); +} + +static guint32 +mono_w32process_module_get_name (gpointer process, gpointer module, gunichar2 *basename, guint32 size) +{ + return GetModuleBaseName (process, module, basename, size); +} + +static guint32 +mono_w32process_module_get_filename (gpointer process, gpointer module, gunichar2 *basename, guint32 size) +{ + return GetModuleFileNameEx (process, module, basename, size); +} + +static gboolean +mono_w32process_module_get_information (gpointer process, gpointer module, MODULEINFO *modinfo, guint32 size) +{ + return GetModuleInformation (process, module, modinfo, size); +} + +static gboolean +mono_w32process_get_fileversion_info (gunichar2 *filename, gpointer *data) +{ + guint32 handle; + gsize datasize; + + g_assert (data); + *data = NULL; + + datasize = GetFileVersionInfoSize (filename, &handle); + if (datasize <= 0) + return FALSE; + + *data = g_malloc0 (datasize); + if (!GetFileVersionInfo (filename, handle, datasize, *data)) { + g_free (*data); + return FALSE; + } + + return TRUE; +} + +static gboolean +mono_w32process_ver_query_value (gconstpointer datablock, const gunichar2 *subblock, gpointer *buffer, guint32 *len) +{ + return VerQueryValue (datablock, subblock, buffer, len); +} + +static guint32 +mono_w32process_ver_language_name (guint32 lang, gunichar2 *lang_out, guint32 lang_len) +{ + return VerLanguageName (lang, lang_out, lang_len); +} + +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) && defined(HOST_WIN32) */ + +static MonoClass* +get_file_version_info_class (MonoImage *system_image) +{ + g_assert (system_image); + + return mono_class_load_from_name ( + system_image, "System.Diagnostics", "FileVersionInfo"); +} + +static MonoClass* +get_process_module_class (MonoImage *system_image) +{ + g_assert (system_image); + + return mono_class_load_from_name ( + system_image, "System.Diagnostics", "ProcessModule"); +} + +static guint32 +unicode_chars (const gunichar2 *str) +{ + guint32 len; + for (len = 0; str [len] != '\0'; ++len) {} + return len; +} + +static void +process_set_field_object (MonoObject *obj, const gchar *fieldname, MonoObject *data) +{ + MonoClass *klass; + MonoClassField *field; + + LOGDEBUG (g_message ("%s: Setting field %s to object at %p", __func__, fieldname, data)); + + klass = mono_object_class (obj); + g_assert (klass); + + field = mono_class_get_field_from_name (klass, fieldname); + g_assert (field); + + mono_gc_wbarrier_generic_store (((char *)obj) + field->offset, data); +} + +static void +process_set_field_string (MonoObject *obj, const gchar *fieldname, const gunichar2 *val, guint32 len, MonoError *error) +{ + MonoDomain *domain; + MonoClass *klass; + MonoClassField *field; + MonoString *string; + + error_init (error); + + LOGDEBUG (g_message ("%s: Setting field %s to [%s]", __func__, fieldname, g_utf16_to_utf8 (val, len, NULL, NULL, NULL))); + + domain = mono_object_domain (obj); + g_assert (domain); + + klass = mono_object_class (obj); + g_assert (klass); + + field = mono_class_get_field_from_name (klass, fieldname); + g_assert (field); + + string = mono_string_new_utf16_checked (domain, val, len, error); + return_if_nok (error); + + mono_gc_wbarrier_generic_store (((char *)obj) + field->offset, (MonoObject*)string); +} + +static void +process_set_field_string_char (MonoObject *obj, const gchar *fieldname, const gchar *val, MonoError *error) +{ + MonoDomain *domain; + MonoClass *klass; + MonoClassField *field; + MonoString *string; + + error_init (error); + LOGDEBUG (g_message ("%s: Setting field %s to [%s]", __func__, fieldname, val)); + + domain = mono_object_domain (obj); + g_assert (domain); + + klass = mono_object_class (obj); + g_assert (klass); + + field = mono_class_get_field_from_name (klass, fieldname); + g_assert (field); + + string = mono_string_new_checked (domain, val, error); + return_if_nok (error); + + mono_gc_wbarrier_generic_store (((char *)obj) + field->offset, (MonoObject*)string); +} + +static void +process_set_field_int (MonoObject *obj, const gchar *fieldname, guint32 val) +{ + MonoClass *klass; + MonoClassField *field; + + LOGDEBUG (g_message ("%s: Setting field %s to %d", __func__,fieldname, val)); + + klass = mono_object_class (obj); + g_assert (klass); + + field = mono_class_get_field_from_name (klass, fieldname); + g_assert (field); + + *(guint32 *)(((char *)obj) + field->offset)=val; +} + +static void +process_set_field_intptr (MonoObject *obj, const gchar *fieldname, gpointer val) +{ + MonoClass *klass; + MonoClassField *field; + + LOGDEBUG (g_message ("%s: Setting field %s to %p", __func__, fieldname, val)); + + klass = mono_object_class (obj); + g_assert (klass); + + field = mono_class_get_field_from_name (klass, fieldname); + g_assert (field); + + *(gpointer *)(((char *)obj) + field->offset) = val; +} + +static void +process_set_field_bool (MonoObject *obj, const gchar *fieldname, gboolean val) +{ + MonoClass *klass; + MonoClassField *field; + + LOGDEBUG (g_message ("%s: Setting field %s to %s", __func__, fieldname, val ? "TRUE":"FALSE")); + + klass = mono_object_class (obj); + g_assert (klass); + + field = mono_class_get_field_from_name (klass, fieldname); + g_assert (field); + + *(guint8 *)(((char *)obj) + field->offset) = val; +} + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) + +#define SFI_COMMENTS "\\StringFileInfo\\%02X%02X%02X%02X\\Comments" +#define SFI_COMPANYNAME "\\StringFileInfo\\%02X%02X%02X%02X\\CompanyName" +#define SFI_FILEDESCRIPTION "\\StringFileInfo\\%02X%02X%02X%02X\\FileDescription" +#define SFI_FILEVERSION "\\StringFileInfo\\%02X%02X%02X%02X\\FileVersion" +#define SFI_INTERNALNAME "\\StringFileInfo\\%02X%02X%02X%02X\\InternalName" +#define SFI_LEGALCOPYRIGHT "\\StringFileInfo\\%02X%02X%02X%02X\\LegalCopyright" +#define SFI_LEGALTRADEMARKS "\\StringFileInfo\\%02X%02X%02X%02X\\LegalTrademarks" +#define SFI_ORIGINALFILENAME "\\StringFileInfo\\%02X%02X%02X%02X\\OriginalFilename" +#define SFI_PRIVATEBUILD "\\StringFileInfo\\%02X%02X%02X%02X\\PrivateBuild" +#define SFI_PRODUCTNAME "\\StringFileInfo\\%02X%02X%02X%02X\\ProductName" +#define SFI_PRODUCTVERSION "\\StringFileInfo\\%02X%02X%02X%02X\\ProductVersion" +#define SFI_SPECIALBUILD "\\StringFileInfo\\%02X%02X%02X%02X\\SpecialBuild" +#define EMPTY_STRING (gunichar2*)"\000\000" + +typedef struct { + const char *name; + const char *id; +} StringTableEntry; + +static StringTableEntry stringtable_entries [] = { + { "comments", SFI_COMMENTS }, + { "companyname", SFI_COMPANYNAME }, + { "filedescription", SFI_FILEDESCRIPTION }, + { "fileversion", SFI_FILEVERSION }, + { "internalname", SFI_INTERNALNAME }, + { "legalcopyright", SFI_LEGALCOPYRIGHT }, + { "legaltrademarks", SFI_LEGALTRADEMARKS }, + { "originalfilename", SFI_ORIGINALFILENAME }, + { "privatebuild", SFI_PRIVATEBUILD }, + { "productname", SFI_PRODUCTNAME }, + { "productversion", SFI_PRODUCTVERSION }, + { "specialbuild", SFI_SPECIALBUILD } +}; + +static void +process_module_string_read (MonoObject *filever, gpointer data, const gchar *fieldname, + guchar lang_hi, guchar lang_lo, const gchar *key, MonoError *error) +{ + gchar *lang_key_utf8; + gunichar2 *lang_key, *buffer; + UINT chars; + + error_init (error); + + lang_key_utf8 = g_strdup_printf (key, lang_lo, lang_hi, 0x04, 0xb0); + + LOGDEBUG (g_message ("%s: asking for [%s]", __func__, lang_key_utf8)); + + lang_key = g_utf8_to_utf16 (lang_key_utf8, -1, NULL, NULL, NULL); + + if (mono_w32process_ver_query_value (data, lang_key, (gpointer *)&buffer, &chars) && chars > 0) { + LOGDEBUG (g_message ("%s: found %d chars of [%s]", __func__, chars, g_utf16_to_utf8 (buffer, chars, NULL, NULL, NULL))); + /* chars includes trailing null */ + process_set_field_string (filever, fieldname, buffer, chars - 1, error); + } else { + process_set_field_string (filever, fieldname, EMPTY_STRING, 0, error); + } + + g_free (lang_key); + g_free (lang_key_utf8); +} + +static void +process_module_stringtable (MonoObject *filever, gpointer data, guchar lang_hi, guchar lang_lo, MonoError *error) +{ + for (int i = 0; i < G_N_ELEMENTS (stringtable_entries); ++i) { + process_module_string_read (filever, data, stringtable_entries [i].name, + lang_hi, lang_lo, stringtable_entries [i].id, error); + return_if_nok (error); + } +} + +static void +mono_w32process_get_fileversion (MonoObject *filever, gunichar2 *filename, MonoError *error) +{ + VS_FIXEDFILEINFO *ffi; + gpointer data; + guchar *trans_data; + gunichar2 *query; + UINT ffi_size, trans_size; + gunichar2 lang_buf[128]; + guint32 lang, lang_count; + + error_init (error); + + if (!mono_w32process_get_fileversion_info (filename, &data)) + return; + + query = g_utf8_to_utf16 ("\\", -1, NULL, NULL, NULL); + if (query == NULL) { + g_free (data); + return; + } + + if (mono_w32process_ver_query_value (data, query, (gpointer *)&ffi, &ffi_size)) { + #define LOWORD(i32) ((guint16)((i32) & 0xFFFF)) + #define HIWORD(i32) ((guint16)(((guint32)(i32) >> 16) & 0xFFFF)) + + LOGDEBUG (g_message ("%s: recording assembly: FileName [%s] FileVersionInfo [%d.%d.%d.%d]", + __func__, g_utf16_to_utf8 (filename, -1, NULL, NULL, NULL), HIWORD (ffi->dwFileVersionMS), + LOWORD (ffi->dwFileVersionMS), HIWORD (ffi->dwFileVersionLS), LOWORD (ffi->dwFileVersionLS))); + + process_set_field_int (filever, "filemajorpart", HIWORD (ffi->dwFileVersionMS)); + process_set_field_int (filever, "fileminorpart", LOWORD (ffi->dwFileVersionMS)); + process_set_field_int (filever, "filebuildpart", HIWORD (ffi->dwFileVersionLS)); + process_set_field_int (filever, "fileprivatepart", LOWORD (ffi->dwFileVersionLS)); + + process_set_field_int (filever, "productmajorpart", HIWORD (ffi->dwProductVersionMS)); + process_set_field_int (filever, "productminorpart", LOWORD (ffi->dwProductVersionMS)); + process_set_field_int (filever, "productbuildpart", HIWORD (ffi->dwProductVersionLS)); + process_set_field_int (filever, "productprivatepart", LOWORD (ffi->dwProductVersionLS)); + + process_set_field_bool (filever, "isdebug", ((ffi->dwFileFlags & ffi->dwFileFlagsMask) & VS_FF_DEBUG) != 0); + process_set_field_bool (filever, "isprerelease", ((ffi->dwFileFlags & ffi->dwFileFlagsMask) & VS_FF_PRERELEASE) != 0); + process_set_field_bool (filever, "ispatched", ((ffi->dwFileFlags & ffi->dwFileFlagsMask) & VS_FF_PATCHED) != 0); + process_set_field_bool (filever, "isprivatebuild", ((ffi->dwFileFlags & ffi->dwFileFlagsMask) & VS_FF_PRIVATEBUILD) != 0); + process_set_field_bool (filever, "isspecialbuild", ((ffi->dwFileFlags & ffi->dwFileFlagsMask) & VS_FF_SPECIALBUILD) != 0); + + #undef LOWORD + #undef HIWORD + } + g_free (query); + + query = g_utf8_to_utf16 ("\\VarFileInfo\\Translation", -1, NULL, NULL, NULL); + if (query == NULL) { + g_free (data); + return; + } + + if (mono_w32process_ver_query_value (data, query, (gpointer *)&trans_data, &trans_size)) { + /* use the first language ID we see */ + if (trans_size >= 4) { + LOGDEBUG (g_message("%s: %s has 0x%0x 0x%0x 0x%0x 0x%0x", __func__, g_utf16_to_utf8 (filename, -1, NULL, NULL, NULL), trans_data[0], trans_data[1], trans_data[2], trans_data[3])); + lang = (trans_data[0]) | (trans_data[1] << 8) | (trans_data[2] << 16) | (trans_data[3] << 24); + /* Only give the lower 16 bits to mono_w32process_ver_language_name, as Windows gets confused otherwise */ + lang_count = mono_w32process_ver_language_name (lang & 0xFFFF, lang_buf, 128); + if (lang_count) { + process_set_field_string (filever, "language", lang_buf, lang_count, error); + if (!is_ok (error)) + goto cleanup; + } + process_module_stringtable (filever, data, trans_data[0], trans_data[1], error); + if (!is_ok (error)) + goto cleanup; + } + } else { + int i; + + for (i = 0; i < G_N_ELEMENTS (stringtable_entries); ++i) { + /* No strings, so set every field to the empty string */ + process_set_field_string (filever, stringtable_entries [i].name, EMPTY_STRING, 0, error); + if (!is_ok (error)) + goto cleanup; + } + + /* And language seems to be set to en_US according to bug 374600 */ + lang_count = mono_w32process_ver_language_name (0x0409, lang_buf, 128); + if (lang_count) { + process_set_field_string (filever, "language", lang_buf, lang_count, error); + if (!is_ok (error)) + goto cleanup; + } + } + +cleanup: + g_free (query); + g_free (data); +} + +#endif /* #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ + +void +ves_icall_System_Diagnostics_FileVersionInfo_GetVersionInfo_internal (MonoObject *this_obj, MonoString *filename) +{ + MonoError error; + + mono_w32process_get_fileversion (this_obj, mono_string_chars (filename), &error); + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + return; + } + + process_set_field_string (this_obj, "filename", mono_string_chars (filename), mono_string_length (filename), &error); + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + } +} + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) + +static GPtrArray* +get_domain_assemblies (MonoDomain *domain) +{ + GSList *tmp; + GPtrArray *assemblies; + + /* + * Make a copy of the list of assemblies because we can't hold the assemblies + * lock while creating objects etc. + */ + assemblies = g_ptr_array_new (); + mono_domain_assemblies_lock (domain); + for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) { + MonoAssembly *ass = (MonoAssembly *)tmp->data; + if (ass->image->fileio_used) + continue; + g_ptr_array_add (assemblies, ass); + } + mono_domain_assemblies_unlock (domain); + + return assemblies; +} + +static MonoObject* +process_add_module (HANDLE process, HMODULE mod, gunichar2 *filename, gunichar2 *modulename, MonoClass *proc_class, MonoError *error) +{ + MonoObject *item, *filever; + MonoDomain *domain = mono_domain_get (); + MODULEINFO modinfo; + BOOL ok; + + error_init (error); + + /* Build a System.Diagnostics.ProcessModule with the data. */ + 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 (proc_class->image), error); + return_val_if_nok (error, NULL); + + mono_w32process_get_fileversion (filever, filename, error); + return_val_if_nok (error, NULL); + + process_set_field_string (filever, "filename", filename, unicode_chars (filename), error); + return_val_if_nok (error, NULL); + + ok = mono_w32process_module_get_information (process, mod, &modinfo, sizeof(MODULEINFO)); + if (ok) { + process_set_field_intptr (item, "baseaddr", modinfo.lpBaseOfDll); + process_set_field_intptr (item, "entryaddr", modinfo.EntryPoint); + process_set_field_int (item, "memory_size", modinfo.SizeOfImage); + } + + process_set_field_string (item, "filename", filename, unicode_chars (filename), error); + return_val_if_nok (error, NULL); + + process_set_field_string (item, "modulename", modulename, unicode_chars (modulename), error); + return_val_if_nok (error, NULL); + + process_set_field_object (item, "version_info", filever); + + return item; +} + +static void +process_get_assembly_fileversion (MonoObject *filever, MonoAssembly *assembly) +{ + process_set_field_int (filever, "filemajorpart", assembly->aname.major); + process_set_field_int (filever, "fileminorpart", assembly->aname.minor); + process_set_field_int (filever, "filebuildpart", assembly->aname.build); +} + +static MonoObject* +process_get_module (MonoAssembly *assembly, MonoClass *proc_class, MonoError *error) +{ + MonoObject *item, *filever; + MonoDomain *domain; + gchar *filename; + const gchar *modulename; + + error_init (error); + + domain = mono_domain_get (); + + modulename = assembly->aname.name; + + /* Build a System.Diagnostics.ProcessModule with the data. */ + 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 (proc_class->image), error); + return_val_if_nok (error, NULL); + + filename = g_strdup_printf ("[In Memory] %s", modulename); + + process_get_assembly_fileversion (filever, assembly); + process_set_field_string_char (filever, "filename", filename, error); + return_val_if_nok (error, NULL); + process_set_field_object (item, "version_info", filever); + + process_set_field_intptr (item, "baseaddr", assembly->image->raw_data); + process_set_field_int (item, "memory_size", assembly->image->raw_data_len); + process_set_field_string_char (item, "filename", filename, error); + return_val_if_nok (error, NULL); + process_set_field_string_char (item, "modulename", modulename, error); + return_val_if_nok (error, NULL); + + g_free (filename); + + return item; +} + +/* Returns an array of System.Diagnostics.ProcessModule */ +MonoArray * +ves_icall_System_Diagnostics_Process_GetModules_internal (MonoObject *this_obj, HANDLE process) +{ + MonoError error; + MonoArray *temp_arr = NULL; + MonoArray *arr; + HMODULE mods[1024]; + gunichar2 filename[MAX_PATH]; + gunichar2 modname[MAX_PATH]; + DWORD needed; + guint32 count = 0, module_count = 0, assembly_count = 0; + guint32 i, num_added = 0; + GPtrArray *assemblies = NULL; + MonoClass *process_module_class; + + 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 ()); + assembly_count = assemblies->len; + } + + if (mono_w32process_try_get_modules (process, mods, sizeof(mods), &needed)) + module_count += needed / sizeof(HMODULE); + + count = module_count + assembly_count; + temp_arr = mono_array_new_checked (mono_domain_get (), process_module_class, count, &error); + if (mono_error_set_pending_exception (&error)) + return NULL; + + for (i = 0; i < module_count; i++) { + 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, process_module_class, &error); + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + return NULL; + } + mono_array_setref (temp_arr, num_added++, module); + } + } + + if (assemblies) { + for (i = 0; i < assembly_count; i++) { + MonoAssembly *ass = (MonoAssembly *)g_ptr_array_index (assemblies, i); + MonoObject *module = process_get_module (ass, process_module_class, &error); + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + return NULL; + } + mono_array_setref (temp_arr, num_added++, module); + } + g_ptr_array_free (assemblies, TRUE); + } + + if (count == num_added) { + arr = temp_arr; + } else { + /* shorter version of the array */ + arr = mono_array_new_checked (mono_domain_get (), process_module_class, num_added, &error); + if (mono_error_set_pending_exception (&error)) + return NULL; + + for (i = 0; i < num_added; i++) + mono_array_setref (arr, i, mono_array_get (temp_arr, MonoObject*, i)); + } + + return arr; +} + +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) + +MonoString * +ves_icall_System_Diagnostics_Process_ProcessName_internal (HANDLE process) +{ + MonoError error; + MonoString *string; + gunichar2 name[MAX_PATH]; + guint32 len; + gboolean ok; + HMODULE mod; + DWORD needed; + + ok = mono_w32process_try_get_modules (process, &mod, sizeof(mod), &needed); + if (!ok) + return NULL; + + len = mono_w32process_module_get_name (process, mod, name, MAX_PATH); + if (len == 0) + return NULL; + + string = mono_string_new_utf16_checked (mono_domain_get (), name, len, &error); + if (!mono_error_ok (&error)) + mono_error_set_pending_exception (&error); + + return string; +} + +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ + +gint64 +ves_icall_System_Diagnostics_Process_GetProcessData (int pid, gint32 data_type, gint32 *error) +{ + MonoProcessError perror; + guint64 res; + + res = mono_process_get_data_with_error (GINT_TO_POINTER (pid), (MonoProcessData)data_type, &perror); + if (error) + *error = perror; + return res; +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32process.h b/unity-2019.4.24f1-mbe/mono/metadata/w32process.h new file mode 100644 index 000000000..41c3a3154 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32process.h @@ -0,0 +1,143 @@ +/** + * \file + * System.Diagnostics.Process support + * + * Author: + * Dick Porter (dick@ximian.com) + * + * (C) 2002 Ximian, Inc. + */ + +#ifndef _MONO_METADATA_W32PROCESS_H_ +#define _MONO_METADATA_W32PROCESS_H_ + +#include +#include + +#if HAVE_SYS_TYPES_H +#include +#endif + +#include + +G_BEGIN_DECLS + +typedef enum { + MONO_W32PROCESS_PRIORITY_CLASS_NORMAL = 0x0020, + MONO_W32PROCESS_PRIORITY_CLASS_IDLE = 0x0040, + MONO_W32PROCESS_PRIORITY_CLASS_HIGH = 0x0080, + MONO_W32PROCESS_PRIORITY_CLASS_REALTIME = 0x0100, + MONO_W32PROCESS_PRIORITY_CLASS_BELOW_NORMAL = 0x4000, + MONO_W32PROCESS_PRIORITY_CLASS_ABOVE_NORMAL = 0x8000, +} MonoW32ProcessPriorityClass; + +typedef struct +{ + gpointer process_handle; + guint32 pid; /* Contains mono_w32error_get_last () on failure */ + MonoArray *env_variables; + MonoString *username; + MonoString *domain; + gpointer password; /* BSTR from SecureString in 2.0 profile */ + MonoBoolean load_user_profile; +} MonoW32ProcessInfo; + +typedef struct +{ + MonoObject object; + MonoString *filename; + MonoString *arguments; + MonoString *working_directory; + MonoString *verb; + guint32 window_style; + MonoBoolean error_dialog; + gpointer error_dialog_parent_handle; + MonoBoolean use_shell_execute; + MonoString *username; + MonoString *domain; + MonoObject *password; /* SecureString in 2.0 profile, dummy in 1.x */ + MonoString *password_in_clear_text; + MonoBoolean load_user_profile; + MonoBoolean redirect_standard_input; + MonoBoolean redirect_standard_output; + MonoBoolean redirect_standard_error; + MonoObject *encoding_stdout; + MonoObject *encoding_stderr; + MonoBoolean create_no_window; + MonoObject *weak_parent_process; + MonoObject *envVars; +} MonoW32ProcessStartInfo; + +void +mono_w32process_init (void); + +void +mono_w32process_cleanup (void); + +void +mono_w32process_signal_finished (void); + +#ifndef HOST_WIN32 + +void +mono_w32process_set_cli_launcher (gchar *path); + +gchar* +mono_w32process_get_path (pid_t pid); + +#endif + +gpointer +ves_icall_System_Diagnostics_Process_GetProcess_internal (guint32 pid); + +MonoArray* +ves_icall_System_Diagnostics_Process_GetProcesses_internal (void); + +MonoArray* +ves_icall_System_Diagnostics_Process_GetModules_internal (MonoObject *this_obj, gpointer process); + +void +ves_icall_System_Diagnostics_FileVersionInfo_GetVersionInfo_internal (MonoObject *this_obj, MonoString *filename); + +MonoBoolean +ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoW32ProcessStartInfo *proc_start_info, MonoW32ProcessInfo *process_handle); + +MonoBoolean +ves_icall_System_Diagnostics_Process_CreateProcess_internal (MonoW32ProcessStartInfo *proc_start_info, gpointer stdin_handle, + gpointer stdout_handle, gpointer stderr_handle, MonoW32ProcessInfo *process_handle); + +MonoString* +ves_icall_System_Diagnostics_Process_ProcessName_internal (gpointer process); + +gint64 +ves_icall_System_Diagnostics_Process_GetProcessData (int pid, gint32 data_type, gint32 *error); + +gpointer +ves_icall_Microsoft_Win32_NativeMethods_GetCurrentProcess (void); + +MonoBoolean +ves_icall_Microsoft_Win32_NativeMethods_GetExitCodeProcess (gpointer handle, gint32 *exitcode); + +MonoBoolean +ves_icall_Microsoft_Win32_NativeMethods_CloseProcess (gpointer handle); + +MonoBoolean +ves_icall_Microsoft_Win32_NativeMethods_TerminateProcess (gpointer handle, gint32 exitcode); + +MonoBoolean +ves_icall_Microsoft_Win32_NativeMethods_GetProcessWorkingSetSize (gpointer handle, gsize *min, gsize *max); +MonoBoolean +ves_icall_Microsoft_Win32_NativeMethods_SetProcessWorkingSetSize (gpointer handle, gsize min, gsize max); + +gint32 +ves_icall_Microsoft_Win32_NativeMethods_GetPriorityClass (gpointer handle); +MonoBoolean +ves_icall_Microsoft_Win32_NativeMethods_SetPriorityClass (gpointer handle, gint32 priorityClass); + +MonoBoolean +ves_icall_Microsoft_Win32_NativeMethods_GetProcessTimes (gpointer handle, gint64 *creationtime, gint64 *exittime, gint64 *kerneltime, gint64 *usertime); + +G_END_DECLS + +#endif /* _MONO_METADATA_W32PROCESS_H_ */ + diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32semaphore-unity.c b/unity-2019.4.24f1-mbe/mono/metadata/w32semaphore-unity.c new file mode 100644 index 000000000..778cb1693 --- /dev/null +++ b/unity-2019.4.24f1-mbe/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/unity-2019.4.24f1-mbe/mono/metadata/w32semaphore-unix.c b/unity-2019.4.24f1-mbe/mono/metadata/w32semaphore-unix.c new file mode 100644 index 000000000..4a13b5694 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32semaphore-unix.c @@ -0,0 +1,360 @@ +/** + * \file + * Runtime support for managed Semaphore on Unix + * + * Author: + * Ludovic Henry (luhenry@microsoft.com) + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include "w32semaphore.h" + +#include "w32error.h" +#include "w32handle-namespace.h" +#include "mono/utils/mono-logger-internals.h" +#include "mono/metadata/w32handle.h" + +#define MAX_PATH 260 + +typedef struct { + guint32 val; + gint32 max; +} MonoW32HandleSemaphore; + +struct MonoW32HandleNamedSemaphore { + MonoW32HandleSemaphore s; + MonoW32HandleNamespace sharedns; +}; + +static void sem_handle_signal (MonoW32Handle *handle_data) +{ + MonoW32HandleSemaphore *sem_handle; + + sem_handle = (MonoW32HandleSemaphore*) handle_data->specific; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SEMAPHORE, "%s: signalling %s handle %p", + __func__, mono_w32handle_get_typename (handle_data->type), handle_data); + + /* No idea why max is signed, but thats the spec :-( */ + if (sem_handle->val + 1 > (guint32)sem_handle->max) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SEMAPHORE, "%s: %s handle %p val %d count %d max %d, max value would be exceeded", + __func__, mono_w32handle_get_typename (handle_data->type), handle_data, sem_handle->val, 1, sem_handle->max); + } else { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SEMAPHORE, "%s: %s handle %p val %d count %d max %d", + __func__, mono_w32handle_get_typename (handle_data->type), handle_data, sem_handle->val, 1, sem_handle->max); + + sem_handle->val += 1; + mono_w32handle_set_signal_state (handle_data, TRUE, TRUE); + } +} + +static gboolean sem_handle_own (MonoW32Handle *handle_data, gboolean *abandoned) +{ + MonoW32HandleSemaphore *sem_handle; + + *abandoned = FALSE; + + sem_handle = (MonoW32HandleSemaphore*) handle_data->specific; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SEMAPHORE, "%s: owning %s handle %p", + __func__, mono_w32handle_get_typename (handle_data->type), handle_data); + + sem_handle->val--; + + if (sem_handle->val == 0) + mono_w32handle_set_signal_state (handle_data, FALSE, FALSE); + + return TRUE; +} + +static void sema_details (MonoW32Handle *handle_data) +{ + MonoW32HandleSemaphore *sem = (MonoW32HandleSemaphore *)handle_data->specific; + g_print ("val: %5u, max: %5d", sem->val, sem->max); +} + +static void namedsema_details (MonoW32Handle *handle_data) +{ + MonoW32HandleNamedSemaphore *namedsem = (MonoW32HandleNamedSemaphore *)handle_data->specific; + g_print ("val: %5u, max: %5d, name: \"%s\"", namedsem->s.val, namedsem->s.max, namedsem->sharedns.name); +} + +static const gchar* sema_typename (void) +{ + return "Semaphore"; +} + +static gsize sema_typesize (void) +{ + return sizeof (MonoW32HandleSemaphore); +} + +static const gchar* namedsema_typename (void) +{ + return "N.Semaphore"; +} + +static gsize namedsema_typesize (void) +{ + return sizeof (MonoW32HandleNamedSemaphore); +} + +void +mono_w32semaphore_init (void) +{ + static MonoW32HandleOps sem_ops = { + NULL, /* close */ + sem_handle_signal, /* signal */ + sem_handle_own, /* own */ + NULL, /* is_owned */ + NULL, /* special_wait */ + NULL, /* prewait */ + sema_details, /* details */ + sema_typename, /* typename */ + sema_typesize, /* typesize */ + }; + + static MonoW32HandleOps namedsem_ops = { + NULL, /* close */ + sem_handle_signal, /* signal */ + sem_handle_own, /* own */ + NULL, /* is_owned */ + NULL, /* special_wait */ + NULL, /* prewait */ + namedsema_details, /* details */ + namedsema_typename, /* typename */ + namedsema_typesize, /* typesize */ + }; + + mono_w32handle_register_ops (MONO_W32TYPE_SEM, &sem_ops); + mono_w32handle_register_ops (MONO_W32TYPE_NAMEDSEM, &namedsem_ops); + + mono_w32handle_register_capabilities (MONO_W32TYPE_SEM, + (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL)); + mono_w32handle_register_capabilities (MONO_W32TYPE_NAMEDSEM, + (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL)); +} + +static gpointer +sem_handle_create (MonoW32HandleSemaphore *sem_handle, MonoW32Type type, gint32 initial, gint32 max) +{ + MonoW32Handle *handle_data; + gpointer handle; + + sem_handle->val = initial; + sem_handle->max = max; + + handle = mono_w32handle_new (type, sem_handle); + if (handle == INVALID_HANDLE_VALUE) { + g_warning ("%s: error creating %s handle", + __func__, mono_w32handle_get_typename (type)); + mono_w32error_set_last (ERROR_GEN_FAILURE); + return NULL; + } + + if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) + g_error ("%s: unkown handle %p", __func__, handle); + + if (handle_data->type != type) + g_error ("%s: unknown semaphore handle %p", __func__, handle); + + mono_w32handle_lock (handle_data); + + if (initial != 0) + mono_w32handle_set_signal_state (handle_data, TRUE, FALSE); + + mono_w32handle_unlock (handle_data); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SEMAPHORE, "%s: created %s handle %p", + __func__, mono_w32handle_get_typename (type), handle); + + mono_w32handle_unref (handle_data); + + return handle; +} + +static gpointer +sem_create (gint32 initial, gint32 max) +{ + MonoW32HandleSemaphore sem_handle; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SEMAPHORE, "%s: creating %s handle, initial %d max %d", + __func__, mono_w32handle_get_typename (MONO_W32TYPE_SEM), initial, max); + return sem_handle_create (&sem_handle, MONO_W32TYPE_SEM, initial, max); +} + +static gpointer +namedsem_create (gint32 initial, gint32 max, const gunichar2 *name) +{ + gpointer handle; + gchar *utf8_name; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SEMAPHORE, "%s: creating %s handle, initial %d max %d name \"%s\"", + __func__, mono_w32handle_get_typename (MONO_W32TYPE_NAMEDSEM), initial, max, (const char*)name); + + /* w32 seems to guarantee that opening named objects can't race each other */ + mono_w32handle_namespace_lock (); + + glong utf8_len = 0; + utf8_name = g_utf16_to_utf8 (name, -1, NULL, &utf8_len, NULL); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SEMAPHORE, "%s: Creating named sem name [%s] initial %d max %d", __func__, utf8_name, initial, max); + + handle = mono_w32handle_namespace_search_handle (MONO_W32TYPE_NAMEDSEM, utf8_name); + if (handle == INVALID_HANDLE_VALUE) { + /* The name has already been used for a different object. */ + handle = NULL; + mono_w32error_set_last (ERROR_INVALID_HANDLE); + } else if (handle) { + /* Not an error, but this is how the caller is informed that the semaphore wasn't freshly created */ + mono_w32error_set_last (ERROR_ALREADY_EXISTS); + + /* mono_w32handle_namespace_search_handle already adds a ref to the handle */ + } else { + /* A new named semaphore */ + MonoW32HandleNamedSemaphore namedsem_handle; + + size_t len = utf8_len < MAX_PATH ? utf8_len : MAX_PATH; + memcpy (&namedsem_handle.sharedns.name [0], utf8_name, len); + namedsem_handle.sharedns.name [len] = '\0'; + + handle = sem_handle_create ((MonoW32HandleSemaphore*) &namedsem_handle, MONO_W32TYPE_NAMEDSEM, initial, max); + } + + g_free (utf8_name); + + mono_w32handle_namespace_unlock (); + + return handle; +} + +gpointer +ves_icall_System_Threading_Semaphore_CreateSemaphore_internal (gint32 initialCount, gint32 maximumCount, MonoString *name, gint32 *error) +{ + gpointer sem; + + if (maximumCount <= 0) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SEMAPHORE, "%s: maximumCount <= 0", __func__); + + *error = ERROR_INVALID_PARAMETER; + return NULL; + } + + if (initialCount > maximumCount || initialCount < 0) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SEMAPHORE, "%s: initialCount > maximumCount or < 0", __func__); + + *error = ERROR_INVALID_PARAMETER; + return NULL; + } + + /* Need to blow away any old errors here, because code tests + * for ERROR_ALREADY_EXISTS on success (!) to see if a + * semaphore was freshly created + */ + mono_w32error_set_last (ERROR_SUCCESS); + + if (!name) + sem = sem_create (initialCount, maximumCount); + else + sem = namedsem_create (initialCount, maximumCount, mono_string_chars (name)); + + *error = mono_w32error_get_last (); + + return sem; +} + +MonoBoolean +ves_icall_System_Threading_Semaphore_ReleaseSemaphore_internal (gpointer handle, gint32 releaseCount, gint32 *prevcount) +{ + MonoW32Handle *handle_data; + MonoW32HandleSemaphore *sem_handle; + MonoBoolean ret; + + if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) { + g_warning ("%s: unkown handle %p", __func__, handle); + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return FALSE; + } + + if (handle_data->type != MONO_W32TYPE_SEM && handle_data->type != MONO_W32TYPE_NAMEDSEM) { + g_warning ("%s: unknown sem handle %p", __func__, handle); + mono_w32error_set_last (ERROR_INVALID_HANDLE); + mono_w32handle_unref (handle_data); + return FALSE; + } + + sem_handle = (MonoW32HandleSemaphore*) handle_data->specific; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SEMAPHORE, "%s: releasing %s handle %p", + __func__, mono_w32handle_get_typename (handle_data->type), handle); + + mono_w32handle_lock (handle_data); + + /* Do this before checking for count overflow, because overflowing + * max is a listed technique for finding the current value */ + if (prevcount) + *prevcount = sem_handle->val; + + /* No idea why max is signed, but thats the spec :-( */ + if (sem_handle->val + releaseCount > (guint32)sem_handle->max) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SEMAPHORE, "%s: %s handle %p val %d count %d max %d, max value would be exceeded", + __func__, mono_w32handle_get_typename (handle_data->type), handle, sem_handle->val, releaseCount, sem_handle->max); + + ret = FALSE; + } else { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SEMAPHORE, "%s: %s handle %p val %d count %d max %d", + __func__, mono_w32handle_get_typename (handle_data->type), handle, sem_handle->val, releaseCount, sem_handle->max); + + sem_handle->val += releaseCount; + mono_w32handle_set_signal_state (handle_data, TRUE, TRUE); + + ret = TRUE; + } + + mono_w32handle_unlock (handle_data); + mono_w32handle_unref (handle_data); + + return ret; +} + +gpointer +ves_icall_System_Threading_Semaphore_OpenSemaphore_internal (MonoString *name, gint32 rights, gint32 *error) +{ + gpointer handle; + gchar *utf8_name; + + *error = ERROR_SUCCESS; + + /* w32 seems to guarantee that opening named objects can't race each other */ + mono_w32handle_namespace_lock (); + + utf8_name = g_utf16_to_utf8 (mono_string_chars (name), -1, NULL, NULL, NULL); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SEMAPHORE, "%s: Opening named sem [%s]", __func__, utf8_name); + + handle = mono_w32handle_namespace_search_handle (MONO_W32TYPE_NAMEDSEM, utf8_name); + if (handle == INVALID_HANDLE_VALUE) { + /* The name has already been used for a different object. */ + *error = ERROR_INVALID_HANDLE; + goto cleanup; + } else if (!handle) { + /* This name doesn't exist */ + *error = ERROR_FILE_NOT_FOUND; + goto cleanup; + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SEMAPHORE, "%s: returning named sem handle %p", __func__, handle); + +cleanup: + g_free (utf8_name); + + mono_w32handle_namespace_unlock (); + + return handle; +} + +MonoW32HandleNamespace* +mono_w32semaphore_get_namespace (MonoW32HandleNamedSemaphore *semaphore) +{ + return &semaphore->sharedns; +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32semaphore-win32.c b/unity-2019.4.24f1-mbe/mono/metadata/w32semaphore-win32.c new file mode 100644 index 000000000..ca4ec59b4 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32semaphore-win32.c @@ -0,0 +1,51 @@ +/** + * \file + * Runtime support for managed Semaphore on Win32 + * + * Author: + * Ludovic Henry (luhenry@microsoft.com) + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include "w32semaphore.h" + +#include +#include + +void +mono_w32semaphore_init (void) +{ +} + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) +gpointer +ves_icall_System_Threading_Semaphore_CreateSemaphore_internal (gint32 initialCount, gint32 maximumCount, MonoString *name, gint32 *error) +{ + HANDLE sem; + + sem = CreateSemaphore (NULL, initialCount, maximumCount, name ? mono_string_chars (name) : NULL); + + *error = GetLastError (); + + return sem; +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */ + +MonoBoolean +ves_icall_System_Threading_Semaphore_ReleaseSemaphore_internal (gpointer handle, gint32 releaseCount, gint32 *prevcount) +{ + return ReleaseSemaphore (handle, releaseCount, prevcount); +} + +gpointer +ves_icall_System_Threading_Semaphore_OpenSemaphore_internal (MonoString *name, gint32 rights, gint32 *error) +{ + HANDLE sem; + + sem = OpenSemaphore (rights, FALSE, mono_string_chars (name)); + + *error = GetLastError (); + + return sem; +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32semaphore.h b/unity-2019.4.24f1-mbe/mono/metadata/w32semaphore.h new file mode 100644 index 000000000..13c963a10 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32semaphore.h @@ -0,0 +1,31 @@ +/** + * \file + */ + +#ifndef _MONO_METADATA_W32SEMAPHORE_H_ +#define _MONO_METADATA_W32SEMAPHORE_H_ + +#include +#include + +#include "object.h" +#include "w32handle-namespace.h" + +void +mono_w32semaphore_init (void); + +gpointer +ves_icall_System_Threading_Semaphore_CreateSemaphore_internal (gint32 initialCount, gint32 maximumCount, MonoString *name, gint32 *error); + +MonoBoolean +ves_icall_System_Threading_Semaphore_ReleaseSemaphore_internal (gpointer handle, gint32 releaseCount, gint32 *prevcount); + +gpointer +ves_icall_System_Threading_Semaphore_OpenSemaphore_internal (MonoString *name, gint32 rights, gint32 *error); + +typedef struct MonoW32HandleNamedSemaphore MonoW32HandleNamedSemaphore; + +MonoW32HandleNamespace* +mono_w32semaphore_get_namespace (MonoW32HandleNamedSemaphore *semaphore); + +#endif /* _MONO_METADATA_W32SEMAPHORE_H_ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32socket-internals.h b/unity-2019.4.24f1-mbe/mono/metadata/w32socket-internals.h new file mode 100644 index 000000000..7557818a6 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32socket-internals.h @@ -0,0 +1,152 @@ +/** + * \file + * + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#ifndef __MONO_METADATA_W32SOCKET_INTERNALS_H__ +#define __MONO_METADATA_W32SOCKET_INTERNALS_H__ + +#include +#include + +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +#include + +#ifndef HAVE_SOCKLEN_T +#define socklen_t int +#endif + +#ifndef HOST_WIN32 + +#define TF_DISCONNECT 0x01 +#define TF_REUSE_SOCKET 0x02 + +typedef struct { + gpointer Head; + guint32 HeadLength; + gpointer Tail; + guint32 TailLength; +} TRANSMIT_FILE_BUFFERS, *LPTRANSMIT_FILE_BUFFERS; + +typedef struct { + guint32 Data1; + guint16 Data2; + guint16 Data3; + guint8 Data4[8]; +} GUID; + +typedef struct { + guint32 Internal; + guint32 InternalHigh; + guint32 Offset; + guint32 OffsetHigh; + gpointer hEvent; + gpointer handle1; + gpointer handle2; +} OVERLAPPED; + +#endif + +void +mono_w32socket_initialize (void); + +void +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); + +int +mono_w32socket_recv (SOCKET s, char *buf, int len, int flags, gboolean blocking); + +int +mono_w32socket_recvfrom (SOCKET s, char *buf, int len, int flags, struct sockaddr *from, socklen_t *fromlen, gboolean blocking); + +int +mono_w32socket_recvbuffers (SOCKET s, LPWSABUF lpBuffers, guint32 dwBufferCount, guint32 *lpNumberOfBytesRecvd, guint32 *lpFlags, gpointer lpOverlapped, gpointer lpCompletionRoutine, gboolean blocking); + +int +mono_w32socket_send (SOCKET s, char *buf, int len, int flags, gboolean blocking); + +int +mono_w32socket_sendto (SOCKET s, const char *buf, int len, int flags, const struct sockaddr *to, int tolen, gboolean blocking); + +int +mono_w32socket_sendbuffers (SOCKET s, LPWSABUF lpBuffers, guint32 dwBufferCount, guint32 *lpNumberOfBytesRecvd, guint32 lpFlags, gpointer lpOverlapped, gpointer lpCompletionRoutine, gboolean blocking); + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) + +BOOL +mono_w32socket_transmit_file (SOCKET hSocket, gpointer hFile, LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers, guint32 dwReserved, gboolean blocking); + +#endif + +#ifndef HOST_WIN32 + +SOCKET +mono_w32socket_socket (int domain, int type, int protocol); + +gint +mono_w32socket_bind (SOCKET sock, struct sockaddr *addr, socklen_t addrlen); + +gint +mono_w32socket_getpeername (SOCKET sock, struct sockaddr *name, socklen_t *namelen); + +gint +mono_w32socket_getsockname (SOCKET sock, struct sockaddr *name, socklen_t *namelen); + +gint +mono_w32socket_getsockopt (SOCKET sock, gint level, gint optname, gpointer optval, socklen_t *optlen); + +gint +mono_w32socket_setsockopt (SOCKET sock, gint level, gint optname, const gpointer optval, socklen_t optlen); + +gint +mono_w32socket_listen (SOCKET sock, gint backlog); + +gint +mono_w32socket_shutdown (SOCKET sock, gint how); + +gint +mono_w32socket_ioctl (SOCKET sock, gint32 command, gchar *input, gint inputlen, gchar *output, gint outputlen, glong *written); + +gboolean +mono_w32socket_close (SOCKET sock); + +#endif /* HOST_WIN32 */ + +gint +mono_w32socket_disconnect (SOCKET sock, gboolean reuse); + +gint +mono_w32socket_set_blocking (SOCKET socket, gboolean blocking); + +gint +mono_w32socket_get_available (SOCKET socket, guint64 *amount); + +void +mono_w32socket_set_last_error (gint32 error); + +gint32 +mono_w32socket_get_last_error (void); + +gint32 +mono_w32socket_convert_error (gint error); + +gboolean +mono_w32socket_duplicate (gpointer handle, gint32 targetProcessId, gpointer *duplicate_handle); + +#endif // __MONO_METADATA_W32SOCKET_INTERNALS_H__ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32socket-unity.c b/unity-2019.4.24f1-mbe/mono/metadata/w32socket-unity.c new file mode 100644 index 000000000..38729c773 --- /dev/null +++ b/unity-2019.4.24f1-mbe/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/unity-2019.4.24f1-mbe/mono/metadata/w32socket-unix.c b/unity-2019.4.24f1-mbe/mono/metadata/w32socket-unix.c new file mode 100644 index 000000000..d675b53e6 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32socket-unix.c @@ -0,0 +1,1578 @@ +/** + * \file + * Unix specific socket code. + * + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#include + +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#include +#include +#ifdef HAVE_NETDB_H +#include +#endif +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#ifdef HAVE_SYS_UIO_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_SYS_FILIO_H +#include /* defines FIONBIO and FIONREAD */ +#endif +#ifdef HAVE_SYS_SOCKIO_H +#include /* defines SIOCATMARK */ +#endif +#ifndef HAVE_MSG_NOSIGNAL +#include +#endif +#ifdef HAVE_SYS_SENDFILE_H +#include +#endif +#include + +#include "w32socket.h" +#include "w32socket-internals.h" +#include "w32error.h" +#include "fdhandle.h" +#include "utils/mono-logger-internals.h" +#include "utils/mono-poll.h" +#include "utils/mono-compiler.h" + +typedef struct { + MonoFDHandle fdhandle; + gint domain; + gint type; + gint protocol; + gint saved_error; + gint still_readable; +} SocketHandle; + +static SocketHandle* +socket_data_create (MonoFDType type, gint fd) +{ + SocketHandle *sockethandle; + + sockethandle = g_new0 (SocketHandle, 1); + mono_fdhandle_init ((MonoFDHandle*) sockethandle, type, fd); + + return sockethandle; +} + +static void +socket_data_close (MonoFDHandle *fdhandle) +{ + MonoThreadInfo *info; + SocketHandle* sockethandle; + gint ret; + + sockethandle = (SocketHandle*) fdhandle; + g_assert (sockethandle); + + info = mono_thread_info_current (); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SOCKET, "%s: closing fd %d", __func__, ((MonoFDHandle*) sockethandle)->fd); + + /* Shutdown the socket for reading, to interrupt any potential + * receives that may be blocking for data. See bug 75705. */ + MONO_ENTER_GC_SAFE; + shutdown (((MonoFDHandle*) sockethandle)->fd, SHUT_RD); + MONO_EXIT_GC_SAFE; + +retry_close: + MONO_ENTER_GC_SAFE; + ret = close (((MonoFDHandle*) sockethandle)->fd); + MONO_EXIT_GC_SAFE; + if (ret == -1) { + if (errno == EINTR && !mono_thread_info_is_interrupt_state (info)) + goto retry_close; + } + + sockethandle->saved_error = 0; +} + +static void +socket_data_destroy (MonoFDHandle *fdhandle) +{ + SocketHandle *sockethandle; + + sockethandle = (SocketHandle*) fdhandle; + g_assert (sockethandle); + + g_free (sockethandle); +} + +void +mono_w32socket_initialize (void) +{ + MonoFDHandleCallback socket_data_callbacks = { + .close = socket_data_close, + .destroy = socket_data_destroy + }; + + mono_fdhandle_register (MONO_FDTYPE_SOCKET, &socket_data_callbacks); +} + +void +mono_w32socket_cleanup (void) +{ +} + +SOCKET +mono_w32socket_accept (SOCKET sock, struct sockaddr *addr, socklen_t *addrlen, gboolean blocking) +{ + SocketHandle *sockethandle, *accepted_socket_data; + MonoThreadInfo *info; + gint accepted_fd; + + if (addr != NULL && *addrlen < sizeof(struct sockaddr)) { + mono_w32socket_set_last_error (WSAEFAULT); + return INVALID_SOCKET; + } + + if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { + mono_w32error_set_last (WSAENOTSOCK); + return INVALID_SOCKET; + } + + if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + mono_w32error_set_last (WSAENOTSOCK); + return INVALID_SOCKET; + } + + info = mono_thread_info_current (); + + do { + MONO_ENTER_GC_SAFE; + accepted_fd = accept (((MonoFDHandle*) sockethandle)->fd, addr, addrlen); + MONO_EXIT_GC_SAFE; + } while (accepted_fd == -1 && errno == EINTR && !mono_thread_info_is_interrupt_state (info)); + + if (accepted_fd == -1) { + gint error = mono_w32socket_convert_error (errno); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SOCKET, "%s: accept error: %s", __func__, g_strerror(errno)); + mono_w32socket_set_last_error (error); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return INVALID_SOCKET; + } + + accepted_socket_data = socket_data_create (MONO_FDTYPE_SOCKET, accepted_fd); + accepted_socket_data->domain = sockethandle->domain; + accepted_socket_data->type = sockethandle->type; + accepted_socket_data->protocol = sockethandle->protocol; + accepted_socket_data->still_readable = 1; + + mono_fdhandle_insert ((MonoFDHandle*) accepted_socket_data); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SOCKET, "%s: returning accepted handle %p", __func__, GINT_TO_POINTER(((MonoFDHandle*) accepted_socket_data)->fd)); + + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return ((MonoFDHandle*) accepted_socket_data)->fd; +} + +int +mono_w32socket_connect (SOCKET sock, const struct sockaddr *addr, int addrlen, gboolean blocking) +{ + SocketHandle *sockethandle; + gint ret; + + if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + MONO_ENTER_GC_SAFE; + ret = connect (((MonoFDHandle*) sockethandle)->fd, addr, addrlen); + MONO_EXIT_GC_SAFE; + if (ret == -1) { + MonoThreadInfo *info; + mono_pollfd fds; + gint errnum, so_error; + socklen_t len; + + errnum = errno; + + if (errno != EINTR) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SOCKET, "%s: connect error: %s", __func__, + g_strerror (errnum)); + + errnum = mono_w32socket_convert_error (errnum); + if (errnum == WSAEINPROGRESS) + errnum = WSAEWOULDBLOCK; /* see bug #73053 */ + + mono_w32socket_set_last_error (errnum); + + /* + * On solaris x86 getsockopt (SO_ERROR) is not set after + * connect () fails so we need to save this error. + * + * But don't do this for EWOULDBLOCK (bug 317315) + */ + if (errnum != WSAEWOULDBLOCK) { + /* ECONNRESET means the socket was closed by another thread */ + /* Async close on mac raises ECONNABORTED. */ + sockethandle->saved_error = errnum; + } + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return SOCKET_ERROR; + } + + info = mono_thread_info_current (); + + fds.fd = ((MonoFDHandle*) sockethandle)->fd; + fds.events = MONO_POLLOUT; + for (;;) { + MONO_ENTER_GC_SAFE; + ret = mono_poll (&fds, 1, -1); + MONO_EXIT_GC_SAFE; + if (ret != -1 || mono_thread_info_is_interrupt_state (info)) + break; + + if (errno != EINTR) { + gint errnum = errno; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SOCKET, "%s: connect poll error: %s", __func__, g_strerror (errno)); + mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return SOCKET_ERROR; + } + } + + len = sizeof(so_error); + MONO_ENTER_GC_SAFE; + ret = getsockopt (((MonoFDHandle*) sockethandle)->fd, SOL_SOCKET, SO_ERROR, &so_error, &len); + MONO_EXIT_GC_SAFE; + if (ret == -1) { + gint errnum = errno; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SOCKET, "%s: connect getsockopt error: %s", __func__, g_strerror (errno)); + mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return SOCKET_ERROR; + } + + if (so_error != 0) { + gint errnum = mono_w32socket_convert_error (so_error); + + /* Need to save this socket error */ + sockethandle->saved_error = errnum; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SOCKET, "%s: connect getsockopt returned error: %s", + __func__, g_strerror (so_error)); + + mono_w32socket_set_last_error (errnum); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return SOCKET_ERROR; + } + } + + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return 0; +} + +int +mono_w32socket_recv (SOCKET sock, char *buf, int len, int flags, gboolean blocking) +{ + return mono_w32socket_recvfrom (sock, buf, len, flags, NULL, 0, blocking); +} + +int +mono_w32socket_recvfrom (SOCKET sock, char *buf, int len, int flags, struct sockaddr *from, socklen_t *fromlen, gboolean blocking) +{ + SocketHandle *sockethandle; + int ret; + MonoThreadInfo *info; + + if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + info = mono_thread_info_current (); + + do { + MONO_ENTER_GC_SAFE; + ret = recvfrom (((MonoFDHandle*) sockethandle)->fd, buf, len, flags, from, fromlen); + MONO_EXIT_GC_SAFE; + } while (ret == -1 && errno == EINTR && !mono_thread_info_is_interrupt_state (info)); + + if (ret == 0 && len > 0) { + /* According to the Linux man page, recvfrom only + * returns 0 when the socket has been shut down + * cleanly. Turn this into an EINTR to simulate win32 + * behaviour of returning EINTR when a socket is + * closed while the recvfrom is blocking (we use a + * shutdown() in socket_close() to trigger this.) See + * bug 75705. + */ + /* Distinguish between the socket being shut down at + * the local or remote ends, and reads that request 0 + * bytes to be read + */ + + /* If this returns FALSE, it means the socket has been + * closed locally. If it returns TRUE, but + * still_readable != 1 then shutdown + * (SHUT_RD|SHUT_RDWR) has been called locally. + */ + if (sockethandle->still_readable != 1) { + ret = -1; + errno = EINTR; + } + } + + if (ret == -1) { + gint errnum = errno; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SOCKET, "%s: recv error: %s", __func__, g_strerror(errno)); + mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return SOCKET_ERROR; + } + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return ret; +} + +static void +wsabuf_to_msghdr (WSABUF *buffers, guint32 count, struct msghdr *hdr) +{ + guint32 i; + + memset (hdr, 0, sizeof (struct msghdr)); + hdr->msg_iovlen = count; + hdr->msg_iov = g_new0 (struct iovec, count); + for (i = 0; i < count; i++) { + hdr->msg_iov [i].iov_base = buffers [i].buf; + hdr->msg_iov [i].iov_len = buffers [i].len; + } +} + +static void +msghdr_iov_free (struct msghdr *hdr) +{ + g_free (hdr->msg_iov); +} + +int +mono_w32socket_recvbuffers (SOCKET sock, WSABUF *buffers, guint32 count, guint32 *received, guint32 *flags, gpointer overlapped, gpointer complete, gboolean blocking) +{ + SocketHandle *sockethandle; + MonoThreadInfo *info; + gint ret; + struct msghdr hdr; + + g_assert (overlapped == NULL); + g_assert (complete == NULL); + + if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + info = mono_thread_info_current (); + + wsabuf_to_msghdr (buffers, count, &hdr); + + do { + MONO_ENTER_GC_SAFE; + ret = recvmsg (((MonoFDHandle*) sockethandle)->fd, &hdr, *flags); + MONO_EXIT_GC_SAFE; + } while (ret == -1 && errno == EINTR && !mono_thread_info_is_interrupt_state (info)); + + msghdr_iov_free (&hdr); + + if (ret == 0) { + /* see mono_w32socket_recvfrom */ + if (sockethandle->still_readable != 1) { + ret = -1; + errno = EINTR; + } + } + + if (ret == -1) { + gint errnum = errno; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SOCKET, "%s: recvmsg error: %s", __func__, g_strerror(errno)); + mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return SOCKET_ERROR; + } + + *received = ret; + *flags = hdr.msg_flags; + + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return 0; +} + +int +mono_w32socket_send (SOCKET sock, char *buf, int len, int flags, gboolean blocking) +{ + SocketHandle *sockethandle; + int ret; + MonoThreadInfo *info; + + if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + info = mono_thread_info_current (); + + do { + MONO_ENTER_GC_SAFE; + ret = send (((MonoFDHandle*) sockethandle)->fd, buf, len, flags); + MONO_EXIT_GC_SAFE; + } while (ret == -1 && errno == EINTR && !mono_thread_info_is_interrupt_state (info)); + + if (ret == -1) { + gint errnum = errno; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SOCKET, "%s: send error: %s", __func__, g_strerror (errno)); + +#ifdef O_NONBLOCK + /* At least linux returns EAGAIN/EWOULDBLOCK when the timeout has been set on + * a blocking socket. See bug #599488 */ + if (errnum == EAGAIN) { + MONO_ENTER_GC_SAFE; + ret = fcntl (((MonoFDHandle*) sockethandle)->fd, F_GETFL, 0); + MONO_EXIT_GC_SAFE; + if (ret != -1 && (ret & O_NONBLOCK) == 0) + errnum = ETIMEDOUT; + } +#endif /* O_NONBLOCK */ + mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return SOCKET_ERROR; + } + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return ret; +} + +int +mono_w32socket_sendto (SOCKET sock, const char *buf, int len, int flags, const struct sockaddr *to, int tolen, gboolean blocking) +{ + SocketHandle *sockethandle; + int ret; + MonoThreadInfo *info; + + if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + info = mono_thread_info_current (); + + do { + MONO_ENTER_GC_SAFE; + ret = sendto (((MonoFDHandle*) sockethandle)->fd, buf, len, flags, to, tolen); + MONO_EXIT_GC_SAFE; + } while (ret == -1 && errno == EINTR && !mono_thread_info_is_interrupt_state (info)); + + if (ret == -1) { + gint errnum = errno; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SOCKET, "%s: send error: %s", __func__, g_strerror (errno)); + mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return SOCKET_ERROR; + } + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return ret; +} + +int +mono_w32socket_sendbuffers (SOCKET sock, WSABUF *buffers, guint32 count, guint32 *sent, guint32 flags, gpointer overlapped, gpointer complete, gboolean blocking) +{ + struct msghdr hdr; + MonoThreadInfo *info; + SocketHandle *sockethandle; + gint ret; + + g_assert (overlapped == NULL); + g_assert (complete == NULL); + + if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + info = mono_thread_info_current (); + + wsabuf_to_msghdr (buffers, count, &hdr); + + do { + MONO_ENTER_GC_SAFE; + ret = sendmsg (((MonoFDHandle*) sockethandle)->fd, &hdr, flags); + MONO_EXIT_GC_SAFE; + } while (ret == -1 && errno == EINTR && !mono_thread_info_is_interrupt_state (info)); + + msghdr_iov_free (&hdr); + + if (ret == -1) { + gint errnum = errno; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SOCKET, "%s: sendmsg error: %s", __func__, g_strerror (errno)); + mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return SOCKET_ERROR; + } + + *sent = ret; + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return 0; +} + +#define SF_BUFFER_SIZE 16384 + +BOOL +mono_w32socket_transmit_file (SOCKET sock, gpointer file_handle, TRANSMIT_FILE_BUFFERS *buffers, guint32 flags, gboolean blocking) +{ + MonoThreadInfo *info; + SocketHandle *sockethandle; + gint file; + gssize ret; +#if defined(HAVE_SENDFILE) && (defined(__linux__) || defined(DARWIN)) + struct stat statbuf; +#else + gchar *buffer; +#endif + + if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + /* Write the header */ + if (buffers != NULL && buffers->Head != NULL && buffers->HeadLength > 0) { + ret = mono_w32socket_send (((MonoFDHandle*) sockethandle)->fd, buffers->Head, buffers->HeadLength, 0, FALSE); + if (ret == SOCKET_ERROR) { + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return FALSE; + } + } + + info = mono_thread_info_current (); + + file = GPOINTER_TO_INT (file_handle); + +#if defined(HAVE_SENDFILE) && (defined(__linux__) || defined(DARWIN)) + MONO_ENTER_GC_SAFE; + ret = fstat (file, &statbuf); + MONO_EXIT_GC_SAFE; + if (ret == -1) { + gint errnum = errno; + mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return SOCKET_ERROR; + } + + do { + MONO_ENTER_GC_SAFE; +#ifdef __linux__ + ret = sendfile (((MonoFDHandle*) sockethandle)->fd, file, NULL, statbuf.st_size); +#elif defined(DARWIN) + /* TODO: header/tail could be sent in the 5th argument */ + /* TODO: Might not send the entire file for non-blocking sockets */ + ret = sendfile (file, ((MonoFDHandle*) sockethandle)->fd, 0, &statbuf.st_size, NULL, 0); +#endif + MONO_EXIT_GC_SAFE; + } while (ret != -1 && errno == EINTR && !mono_thread_info_is_interrupt_state (info)); +#else + buffer = g_malloc (SF_BUFFER_SIZE); + + do { + do { + MONO_ENTER_GC_SAFE; + ret = read (file, buffer, SF_BUFFER_SIZE); + MONO_EXIT_GC_SAFE; + } while (ret == -1 && errno == EINTR && !mono_thread_info_is_interrupt_state (info)); + + if (ret == -1 || ret == 0) + break; + + do { + MONO_ENTER_GC_SAFE; + ret = send (((MonoFDHandle*) sockethandle)->fd, buffer, ret, 0); /* short sends? enclose this in a loop? */ + MONO_EXIT_GC_SAFE; + } while (ret == -1 && errno == EINTR && !mono_thread_info_is_interrupt_state (info)); + } while (ret != -1 && errno == EINTR && !mono_thread_info_is_interrupt_state (info)); + + g_free (buffer); +#endif + + if (ret == -1) { + gint errnum = errno; + mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return FALSE; + } + + /* Write the tail */ + if (buffers != NULL && buffers->Tail != NULL && buffers->TailLength > 0) { + ret = mono_w32socket_send (((MonoFDHandle*) sockethandle)->fd, buffers->Tail, buffers->TailLength, 0, FALSE); + if (ret == SOCKET_ERROR) { + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return FALSE; + } + } + + if ((flags & TF_DISCONNECT) == TF_DISCONNECT) + mono_w32socket_close (((MonoFDHandle*) sockethandle)->fd); + + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return TRUE; +} + +SOCKET +mono_w32socket_socket (int domain, int type, int protocol) +{ + SocketHandle *sockethandle; + gint fd; + +retry_socket: + MONO_ENTER_GC_SAFE; + fd = socket (domain, type, protocol); + MONO_EXIT_GC_SAFE; + if (fd == -1) { + if (domain == AF_INET && type == SOCK_RAW && protocol == 0) { + /* Retry with protocol == 4 (see bug #54565) */ + // https://bugzilla.novell.com/show_bug.cgi?id=MONO54565 + protocol = 4; + goto retry_socket; + } + + gint errnum = errno; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SOCKET, "%s: socket error: %s", __func__, g_strerror (errno)); + mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); + return INVALID_SOCKET; + } + + sockethandle = socket_data_create(MONO_FDTYPE_SOCKET, fd); + sockethandle->domain = domain; + sockethandle->type = type; + sockethandle->protocol = protocol; + sockethandle->still_readable = 1; + + /* .net seems to set this by default for SOCK_STREAM, not for + * SOCK_DGRAM (see bug #36322) + * https://bugzilla.novell.com/show_bug.cgi?id=MONO36322 + * + * It seems winsock has a rather different idea of what + * SO_REUSEADDR means. If it's set, then a new socket can be + * bound over an existing listening socket. There's a new + * windows-specific option called SO_EXCLUSIVEADDRUSE but + * using that means the socket MUST be closed properly, or a + * denial of service can occur. Luckily for us, winsock + * behaves as though any other system would when SO_REUSEADDR + * is true, so we don't need to do anything else here. See + * bug 53992. + * https://bugzilla.novell.com/show_bug.cgi?id=MONO53992 + */ + { + int ret, true_ = 1; + + MONO_ENTER_GC_SAFE; + ret = setsockopt (((MonoFDHandle*) sockethandle)->fd, SOL_SOCKET, SO_REUSEADDR, &true_, sizeof (true_)); + MONO_EXIT_GC_SAFE; + if (ret == -1) { + gint errnum = errno; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SOCKET, "%s: Error setting SO_REUSEADDR", __func__); + mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); + + MONO_ENTER_GC_SAFE; + close (((MonoFDHandle*) sockethandle)->fd); + MONO_EXIT_GC_SAFE; + + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return INVALID_SOCKET; + } + } + + mono_fdhandle_insert ((MonoFDHandle*) sockethandle); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SOCKET, "%s: returning socket handle %p", __func__, GINT_TO_POINTER(((MonoFDHandle*) sockethandle)->fd)); + + return ((MonoFDHandle*) sockethandle)->fd; +} + +gint +mono_w32socket_bind (SOCKET sock, struct sockaddr *addr, socklen_t addrlen) +{ + SocketHandle *sockethandle; + int ret; + + if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + MONO_ENTER_GC_SAFE; + ret = bind (((MonoFDHandle*) sockethandle)->fd, addr, addrlen); + MONO_EXIT_GC_SAFE; + if (ret == -1) { + gint errnum = errno; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SOCKET, "%s: bind error: %s", __func__, g_strerror(errno)); + mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return SOCKET_ERROR; + } + + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return 0; +} + +gint +mono_w32socket_getpeername (SOCKET sock, struct sockaddr *name, socklen_t *namelen) +{ + SocketHandle *sockethandle; + gint ret; + + if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + MONO_ENTER_GC_SAFE; + ret = getpeername (((MonoFDHandle*) sockethandle)->fd, name, namelen); + MONO_EXIT_GC_SAFE; + if (ret == -1) { + gint errnum = errno; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SOCKET, "%s: getpeername error: %s", __func__, g_strerror (errno)); + mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return SOCKET_ERROR; + } + + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return 0; +} + +gint +mono_w32socket_getsockname (SOCKET sock, struct sockaddr *name, socklen_t *namelen) +{ + SocketHandle *sockethandle; + gint ret; + + if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + MONO_ENTER_GC_SAFE; + ret = getsockname (((MonoFDHandle*) sockethandle)->fd, name, namelen); + MONO_EXIT_GC_SAFE; + if (ret == -1) { + gint errnum = errno; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SOCKET, "%s: getsockname error: %s", __func__, g_strerror (errno)); + mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return SOCKET_ERROR; + } + + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return 0; +} + +gint +mono_w32socket_getsockopt (SOCKET sock, gint level, gint optname, gpointer optval, socklen_t *optlen) +{ + SocketHandle *sockethandle; + gint ret; + struct timeval tv; + gpointer tmp_val; + + if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + tmp_val = optval; + if (level == SOL_SOCKET && + (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO)) { + tmp_val = &tv; + *optlen = sizeof (tv); + } + + MONO_ENTER_GC_SAFE; + ret = getsockopt (((MonoFDHandle*) sockethandle)->fd, level, optname, tmp_val, optlen); + MONO_EXIT_GC_SAFE; + if (ret == -1) { + gint errnum = errno; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SOCKET, "%s: getsockopt error: %s", __func__, g_strerror (errno)); + mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return SOCKET_ERROR; + } + + if (level == SOL_SOCKET && (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO)) { + *((int *) optval) = tv.tv_sec * 1000 + (tv.tv_usec / 1000); // milli from micro + *optlen = sizeof (int); + } + + if (optname == SO_ERROR) { + if (*((int *)optval) != 0) { + *((int *) optval) = mono_w32socket_convert_error (*((int *)optval)); + sockethandle->saved_error = *((int *)optval); + } else { + *((int *)optval) = sockethandle->saved_error; + } + } + + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return 0; +} + +gint +mono_w32socket_setsockopt (SOCKET sock, gint level, gint optname, const gpointer optval, socklen_t optlen) +{ + SocketHandle *sockethandle; + gint ret; + gpointer tmp_val; +#if defined (__linux__) + /* This has its address taken so it cannot be moved to the if block which uses it */ + gint bufsize = 0; +#endif + struct timeval tv; + + if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + tmp_val = optval; + if (level == SOL_SOCKET && + (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO)) { + int ms = *((int *) optval); + tv.tv_sec = ms / 1000; + tv.tv_usec = (ms % 1000) * 1000; // micro from milli + tmp_val = &tv; + optlen = sizeof (tv); + } +#if defined (__linux__) + else if (level == SOL_SOCKET && + (optname == SO_SNDBUF || optname == SO_RCVBUF)) { + /* According to socket(7) the Linux kernel doubles the + * buffer sizes "to allow space for bookkeeping + * overhead." + */ + bufsize = *((int *) optval); + + bufsize /= 2; + tmp_val = &bufsize; + } +#endif + + MONO_ENTER_GC_SAFE; + ret = setsockopt (((MonoFDHandle*) sockethandle)->fd, level, optname, tmp_val, optlen); + MONO_EXIT_GC_SAFE; + if (ret == -1) { + gint errnum = errno; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SOCKET, "%s: setsockopt error: %s", __func__, g_strerror (errno)); + mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return SOCKET_ERROR; + } + +#if defined (SO_REUSEPORT) + /* BSD's and MacOS X multicast sockets also need SO_REUSEPORT when SO_REUSEADDR is requested. */ + if (level == SOL_SOCKET && optname == SO_REUSEADDR) { + int type; + socklen_t type_len = sizeof (type); + + MONO_ENTER_GC_SAFE; + ret = getsockopt (((MonoFDHandle*) sockethandle)->fd, level, SO_TYPE, &type, &type_len); + MONO_EXIT_GC_SAFE; + if (!ret) { + if (type == SOCK_DGRAM || type == SOCK_STREAM) { + MONO_ENTER_GC_SAFE; + setsockopt (((MonoFDHandle*) sockethandle)->fd, level, SO_REUSEPORT, tmp_val, optlen); + MONO_EXIT_GC_SAFE; + } + } + } +#endif + + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return ret; +} + +gint +mono_w32socket_listen (SOCKET sock, gint backlog) +{ + SocketHandle *sockethandle; + gint ret; + + if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + MONO_ENTER_GC_SAFE; + ret = listen (((MonoFDHandle*) sockethandle)->fd, backlog); + MONO_EXIT_GC_SAFE; + if (ret == -1) { + gint errnum = errno; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SOCKET, "%s: listen error: %s", __func__, g_strerror (errno)); + mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return SOCKET_ERROR; + } + + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return 0; +} + +gint +mono_w32socket_shutdown (SOCKET sock, gint how) +{ + SocketHandle *sockethandle; + gint ret; + + if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + if (how == SHUT_RD || how == SHUT_RDWR) + sockethandle->still_readable = 0; + + MONO_ENTER_GC_SAFE; + ret = shutdown (((MonoFDHandle*) sockethandle)->fd, how); + MONO_EXIT_GC_SAFE; + if (ret == -1) { + gint errnum = errno; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SOCKET, "%s: shutdown error: %s", __func__, g_strerror (errno)); + mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return SOCKET_ERROR; + } + + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return ret; +} + +gint +mono_w32socket_disconnect (SOCKET sock, gboolean reuse) +{ + SocketHandle *sockethandle; + SOCKET newsock; + gint ret; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SOCKET, "%s: called on socket %d!", __func__, sock); + + /* We could check the socket type here and fail unless its + * SOCK_STREAM, SOCK_SEQPACKET or SOCK_RDM (according to msdn) + * if we really wanted to */ + + if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + MONO_ENTER_GC_SAFE; + newsock = socket (sockethandle->domain, sockethandle->type, sockethandle->protocol); + MONO_EXIT_GC_SAFE; + if (newsock == -1) { + gint errnum = errno; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SOCKET, "%s: socket error: %s", __func__, g_strerror (errnum)); + mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return SOCKET_ERROR; + } + + /* According to Stevens "Advanced Programming in the UNIX + * Environment: UNIX File I/O" dup2() is atomic so there + * should not be a race condition between the old fd being + * closed and the new socket fd being copied over */ + do { + MONO_ENTER_GC_SAFE; + ret = dup2 (newsock, ((MonoFDHandle*) sockethandle)->fd); + MONO_EXIT_GC_SAFE; + } while (ret == -1 && errno == EAGAIN); + + if (ret == -1) { + gint errnum = errno; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SOCKET, "%s: dup2 error: %s", __func__, g_strerror (errnum)); + mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return SOCKET_ERROR; + } + + MONO_ENTER_GC_SAFE; + close (newsock); + MONO_EXIT_GC_SAFE; + + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return 0; +} + +static gboolean +extension_disconect (SOCKET sock, OVERLAPPED *overlapped, guint32 flags, guint32 reserved) +{ + gboolean ret; + MONO_ENTER_GC_UNSAFE; + ret = mono_w32socket_disconnect (sock, flags & TF_REUSE_SOCKET) == 0; + MONO_EXIT_GC_UNSAFE; + return ret; +} + +static gboolean +extension_transmit_file (SOCKET sock, gpointer file_handle, guint32 bytes_to_write, guint32 bytes_per_send, + OVERLAPPED *ol, TRANSMIT_FILE_BUFFERS *buffers, guint32 flags) +{ + gboolean ret; + MONO_ENTER_GC_UNSAFE; + ret = mono_w32socket_transmit_file (sock, file_handle, buffers, flags, FALSE); + MONO_EXIT_GC_UNSAFE; + return ret; +} + +static struct { + GUID guid; + gpointer func; +} extension_functions[] = { + { {0x7fda2e11,0x8630,0x436f,{0xa0,0x31,0xf5,0x36,0xa6,0xee,0xc1,0x57}} /* WSAID_DISCONNECTEX */, extension_disconect }, + { {0xb5367df0,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}} /* WSAID_TRANSMITFILE */, extension_transmit_file }, + { {0} , NULL }, +}; + +gint +mono_w32socket_ioctl (SOCKET sock, gint32 command, gchar *input, gint inputlen, gchar *output, gint outputlen, glong *written) +{ + SocketHandle *sockethandle; + gint ret; + gchar *buffer; + + if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + if (command == 0xC8000006 /* SIO_GET_EXTENSION_FUNCTION_POINTER */) { + gint i; + GUID *guid; + + if (inputlen < sizeof(GUID)) { + /* As far as I can tell, windows doesn't + * actually set an error here... + */ + mono_w32socket_set_last_error (WSAEINVAL); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return SOCKET_ERROR; + } + + if (outputlen < sizeof(gpointer)) { + /* Or here... */ + mono_w32socket_set_last_error (WSAEINVAL); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return SOCKET_ERROR; + } + + if (output == NULL) { + /* Or here */ + mono_w32socket_set_last_error (WSAEINVAL); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return SOCKET_ERROR; + } + + guid = (GUID*) input; + for (i = 0; extension_functions[i].func; i++) { + if (memcmp (guid, &extension_functions[i].guid, sizeof(GUID)) == 0) { + memcpy (output, &extension_functions[i].func, sizeof(gpointer)); + *written = sizeof(gpointer); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return 0; + } + } + + mono_w32socket_set_last_error (WSAEINVAL); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return SOCKET_ERROR; + } + + if (command == 0x98000004 /* SIO_KEEPALIVE_VALS */) { + guint32 onoff; + + if (inputlen < 3 * sizeof (guint32)) { + mono_w32socket_set_last_error (WSAEINVAL); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return SOCKET_ERROR; + } + + onoff = *((guint32*) input); + + MONO_ENTER_GC_SAFE; + ret = setsockopt (((MonoFDHandle*) sockethandle)->fd, SOL_SOCKET, SO_KEEPALIVE, &onoff, sizeof (guint32)); + MONO_EXIT_GC_SAFE; + if (ret < 0) { + mono_w32socket_set_last_error (mono_w32socket_convert_error (errno)); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return SOCKET_ERROR; + } + +#if defined(TCP_KEEPIDLE) && defined(TCP_KEEPINTVL) + if (onoff != 0) { + /* Values are in ms, but we need s */ + guint32 keepalivetime, keepaliveinterval, rem; + + keepalivetime = *(((guint32*) input) + 1); + keepaliveinterval = *(((guint32*) input) + 2); + + /* keepalivetime and keepaliveinterval are > 0 (checked in managed code) */ + rem = keepalivetime % 1000; + keepalivetime /= 1000; + if (keepalivetime == 0 || rem >= 500) + keepalivetime++; + MONO_ENTER_GC_SAFE; + ret = setsockopt (((MonoFDHandle*) sockethandle)->fd, IPPROTO_TCP, TCP_KEEPIDLE, &keepalivetime, sizeof (guint32)); + MONO_EXIT_GC_SAFE; + if (ret == 0) { + rem = keepaliveinterval % 1000; + keepaliveinterval /= 1000; + if (keepaliveinterval == 0 || rem >= 500) + keepaliveinterval++; + MONO_ENTER_GC_SAFE; + ret = setsockopt (((MonoFDHandle*) sockethandle)->fd, IPPROTO_TCP, TCP_KEEPINTVL, &keepaliveinterval, sizeof (guint32)); + MONO_EXIT_GC_SAFE; + } + if (ret != 0) { + mono_w32socket_set_last_error (mono_w32socket_convert_error (errno)); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return SOCKET_ERROR; + } + + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return 0; + } +#endif + + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return 0; + } + + buffer = inputlen > 0 ? (gchar*) g_memdup (input, inputlen) : NULL; + + MONO_ENTER_GC_SAFE; + ret = ioctl (((MonoFDHandle*) sockethandle)->fd, command, buffer); + MONO_EXIT_GC_SAFE; + if (ret == -1) { + g_free (buffer); + + gint errnum = errno; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SOCKET, "%s: WSAIoctl error: %s", __func__, g_strerror (errno)); + mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return SOCKET_ERROR; + } + + if (!buffer) { + *written = 0; + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return 0; + } + + /* We just copy the buffer to the output. Some ioctls + * don't even output any data, but, well... + * + * NB windows returns WSAEFAULT if outputlen is too small */ + inputlen = (inputlen > outputlen) ? outputlen : inputlen; + + if (inputlen > 0 && output != NULL) + memcpy (output, buffer, inputlen); + + g_free (buffer); + *written = inputlen; + + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return 0; +} + +gboolean +mono_w32socket_close (SOCKET sock) +{ + if (!mono_fdhandle_close (sock)) { + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return FALSE; + } + + return TRUE; +} + +gint +mono_w32socket_set_blocking (SOCKET sock, gboolean blocking) +{ +#ifdef O_NONBLOCK + SocketHandle *sockethandle; + gint ret; + + if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + /* This works better than ioctl(...FIONBIO...) + * on Linux (it causes connect to return + * EINPROGRESS, but the ioctl doesn't seem to) */ + MONO_ENTER_GC_SAFE; + ret = fcntl (((MonoFDHandle*) sockethandle)->fd, F_GETFL, 0); + MONO_EXIT_GC_SAFE; + if (ret == -1) { + gint errnum = mono_w32socket_convert_error (errno); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SOCKET, "%s: fcntl(F_GETFL) error: %s", __func__, g_strerror (errno)); + mono_w32socket_set_last_error (errnum); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return SOCKET_ERROR; + } + + MONO_ENTER_GC_SAFE; + ret = fcntl (((MonoFDHandle*) sockethandle)->fd, F_SETFL, blocking ? (ret & (~O_NONBLOCK)) : (ret | (O_NONBLOCK))); + MONO_EXIT_GC_SAFE; + if (ret == -1) { + gint errnum = mono_w32socket_convert_error (errno); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SOCKET, "%s: fcntl(F_SETFL) error: %s", __func__, g_strerror (errno)); + mono_w32socket_set_last_error (errnum); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return SOCKET_ERROR; + } + + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return 0; +#else + mono_w32socket_set_last_error (ERROR_NOT_SUPPORTED); + return SOCKET_ERROR; +#endif /* O_NONBLOCK */ +} + +gint +mono_w32socket_get_available (SOCKET sock, guint64 *amount) +{ + SocketHandle *sockethandle; + gint ret; + + if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + + if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + mono_w32error_set_last (WSAENOTSOCK); + return SOCKET_ERROR; + } + +#if defined (HOST_DARWIN) + // ioctl (socket, FIONREAD, XXX) returns the size of + // the UDP header as well on Darwin. + // + // Use getsockopt SO_NREAD instead to get the + // right values for TCP and UDP. + // + // ai_canonname can be null in some cases on darwin, + // where the runtime assumes it will be the value of + // the ip buffer. + + socklen_t optlen = sizeof (int); + MONO_ENTER_GC_SAFE; + ret = getsockopt (((MonoFDHandle*) sockethandle)->fd, SOL_SOCKET, SO_NREAD, (gulong*) amount, &optlen); + MONO_EXIT_GC_SAFE; + if (ret == -1) { + gint errnum = mono_w32socket_convert_error (errno); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SOCKET, "%s: getsockopt error: %s", __func__, g_strerror (errno)); + mono_w32socket_set_last_error (errnum); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return SOCKET_ERROR; + } +#else + MONO_ENTER_GC_SAFE; + ret = ioctl (((MonoFDHandle*) sockethandle)->fd, FIONREAD, (gulong*) amount); + MONO_EXIT_GC_SAFE; + if (ret == -1) { + gint errnum = mono_w32socket_convert_error (errno); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_SOCKET, "%s: ioctl error: %s", __func__, g_strerror (errno)); + mono_w32socket_set_last_error (errnum); + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return SOCKET_ERROR; + } +#endif + + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + return 0; +} + +void +mono_w32socket_set_last_error (gint32 error) +{ + mono_w32error_set_last (error); +} + +gint32 +mono_w32socket_get_last_error (void) +{ + return mono_w32error_get_last (); +} + +gint32 +mono_w32socket_convert_error (gint error) +{ + switch (error) { + case 0: return ERROR_SUCCESS; + case EACCES: return WSAEACCES; +#ifdef EADDRINUSE + case EADDRINUSE: return WSAEADDRINUSE; +#endif +#ifdef EAFNOSUPPORT + case EAFNOSUPPORT: return WSAEAFNOSUPPORT; +#endif +#if EAGAIN != EWOULDBLOCK + case EAGAIN: return WSAEWOULDBLOCK; +#endif +#ifdef EALREADY + case EALREADY: return WSAEALREADY; +#endif + case EBADF: return WSAENOTSOCK; +#ifdef ECONNABORTED + case ECONNABORTED: return WSAENETDOWN; +#endif +#ifdef ECONNREFUSED + case ECONNREFUSED: return WSAECONNREFUSED; +#endif +#ifdef ECONNRESET + case ECONNRESET: return WSAECONNRESET; +#endif + case EFAULT: return WSAEFAULT; +#ifdef EHOSTUNREACH + case EHOSTUNREACH: return WSAEHOSTUNREACH; +#endif +#ifdef EINPROGRESS + case EINPROGRESS: return WSAEINPROGRESS; +#endif + case EINTR: return WSAEINTR; + case EINVAL: return WSAEINVAL; + /*FIXME: case EIO: return WSAE????; */ +#ifdef EISCONN + case EISCONN: return WSAEISCONN; +#endif + /* FIXME: case ELOOP: return WSA????; */ + case EMFILE: return WSAEMFILE; +#ifdef EMSGSIZE + case EMSGSIZE: return WSAEMSGSIZE; +#endif + /* FIXME: case ENAMETOOLONG: return WSAEACCES; */ +#ifdef ENETUNREACH + case ENETUNREACH: return WSAENETUNREACH; +#endif +#ifdef ENOBUFS + case ENOBUFS: return WSAENOBUFS; /* not documented */ +#endif + /* case ENOENT: return WSAE????; */ + case ENOMEM: return WSAENOBUFS; +#ifdef ENOPROTOOPT + case ENOPROTOOPT: return WSAENOPROTOOPT; +#endif +#ifdef ENOSR + case ENOSR: return WSAENETDOWN; +#endif +#ifdef ENOTCONN + case ENOTCONN: return WSAENOTCONN; +#endif + /*FIXME: case ENOTDIR: return WSAE????; */ +#ifdef ENOTSOCK + case ENOTSOCK: return WSAENOTSOCK; +#endif + case ENOTTY: return WSAENOTSOCK; +#ifdef EOPNOTSUPP + case EOPNOTSUPP: return WSAEOPNOTSUPP; +#endif + case EPERM: return WSAEACCES; + case EPIPE: return WSAESHUTDOWN; +#ifdef EPROTONOSUPPORT + case EPROTONOSUPPORT: return WSAEPROTONOSUPPORT; +#endif +#if ERESTARTSYS + case ERESTARTSYS: return WSAENETDOWN; +#endif + /*FIXME: case EROFS: return WSAE????; */ +#ifdef ESOCKTNOSUPPORT + case ESOCKTNOSUPPORT: return WSAESOCKTNOSUPPORT; +#endif +#ifdef ETIMEDOUT + case ETIMEDOUT: return WSAETIMEDOUT; +#endif +#ifdef EWOULDBLOCK + case EWOULDBLOCK: return WSAEWOULDBLOCK; +#endif +#ifdef EADDRNOTAVAIL + case EADDRNOTAVAIL: return WSAEADDRNOTAVAIL; +#endif + /* This might happen with unix sockets */ + case ENOENT: return WSAECONNREFUSED; +#ifdef EDESTADDRREQ + case EDESTADDRREQ: return WSAEDESTADDRREQ; +#endif +#ifdef EHOSTDOWN + case EHOSTDOWN: return WSAEHOSTDOWN; +#endif +#ifdef ENETDOWN + case ENETDOWN: return WSAENETDOWN; +#endif + case ENODEV: return WSAENETDOWN; +#ifdef EPROTOTYPE + case EPROTOTYPE: return WSAEPROTOTYPE; +#endif +#ifdef ENXIO + case ENXIO: return WSAENXIO; +#endif + default: + g_error ("%s: no translation into winsock error for (%d) \"%s\"", __func__, error, g_strerror (error)); + } +} + +gboolean +ves_icall_System_Net_Sockets_Socket_SupportPortReuse (MonoProtocolType proto, MonoError *error) +{ + error_init (error); +#if defined (SO_REUSEPORT) + return TRUE; +#else +#ifdef __linux__ + /* Linux always supports double binding for UDP, even on older kernels. */ + if (proto == ProtocolType_Udp) + return TRUE; +#endif + return FALSE; +#endif +} + +gboolean +mono_w32socket_duplicate (gpointer handle, gint32 targetProcessId, gpointer *duplicate_handle) +{ + SocketHandle *sockethandle; + + if (!mono_fdhandle_lookup_and_ref (GPOINTER_TO_INT(handle), (MonoFDHandle**) &sockethandle)) { + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return FALSE; + } + + if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { + mono_fdhandle_unref ((MonoFDHandle*) sockethandle); + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return FALSE; + } + + *duplicate_handle = handle; + return TRUE; +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32socket-win32.c b/unity-2019.4.24f1-mbe/mono/metadata/w32socket-win32.c new file mode 100644 index 000000000..8c4b55c3a --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32socket-win32.c @@ -0,0 +1,240 @@ +/** + * \file + * Windows specific socket code. + * + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#include + +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include + +#include + +#include "w32socket.h" +#include "w32socket-internals.h" + +#include "utils/w32api.h" +#include "utils/mono-os-wait.h" + +#define LOGDEBUG(...) + +void +mono_w32socket_initialize (void) +{ +} + +void +mono_w32socket_cleanup (void) +{ +} + +#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) +{ + 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; + 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) +{ + int ret = SOCKET_ERROR; + 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; + 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; + 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; + 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; + 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; + 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) +static gint +internal_w32socket_transmit_file (SOCKET sock, gpointer file, TRANSMIT_FILE_BUFFERS *lpTransmitBuffers, guint32 dwReserved, gboolean blocking) +{ + gint ret = ERROR_NOT_SUPPORTED; + LPFN_TRANSMITFILE transmit_file; + GUID transmit_file_guid = WSAID_TRANSMITFILE; + DWORD output_bytes; + + 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 (); + + if (blocking) + mono_win32_enter_blocking_io_call (info, (HANDLE)sock); + + MONO_ENTER_GC_SAFE; + if (transmit_file (sock, file, 0, 0, NULL, lpTransmitBuffers, dwReserved)) + ret = 0; + MONO_EXIT_GC_SAFE; + + if (blocking) + mono_win32_leave_blocking_io_call (info, (HANDLE)sock); + } + + if (ret != 0) + ret = WSAGetLastError (); + + return ret; +} + +static gint +internal_w32socket_disconnect (SOCKET sock, gboolean reuse, gboolean blocking) +{ + gint ret = ERROR_NOT_SUPPORTED; + LPFN_DISCONNECTEX disconnect; + GUID disconnect_guid = WSAID_DISCONNECTEX; + DWORD output_bytes; + + 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 (); + + 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; + + if (blocking) + mono_win32_leave_blocking_io_call (info, (HANDLE)sock); + } + + if (ret != 0) + ret = WSAGetLastError (); + + 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); + + return ret; +} +#endif /* #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */ + +gint +mono_w32socket_set_blocking (SOCKET sock, gboolean blocking) +{ + gint ret; + gulong nonblocking_long = !blocking; + MONO_ENTER_GC_SAFE; + ret = ioctlsocket (sock, FIONBIO, &nonblocking_long); + MONO_EXIT_GC_SAFE; + return ret; +} + +gint +mono_w32socket_get_available (SOCKET sock, guint64 *amount) +{ + gint ret; + u_long amount_long = 0; + MONO_ENTER_GC_SAFE; + ret = ioctlsocket (sock, FIONREAD, &amount_long); + *amount = amount_long; + MONO_EXIT_GC_SAFE; + return ret; +} + +void +mono_w32socket_set_last_error (gint32 error) +{ + WSASetLastError (error); +} + +gint32 +mono_w32socket_get_last_error (void) +{ + return WSAGetLastError (); +} + +gint32 +mono_w32socket_convert_error (gint error) +{ + return (error > 0 && error < WSABASEERR) ? error + WSABASEERR : error; +} + +gboolean +ves_icall_System_Net_Sockets_Socket_SupportPortReuse (MonoProtocolType proto, MonoError *error) +{ + error_init (error); + return TRUE; +} + +gboolean +mono_w32socket_duplicate (gpointer handle, gint32 targetProcessId, gpointer *duplicate_handle) +{ + gboolean ret; + + MONO_ENTER_GC_SAFE; + ret = DuplicateHandle (GetCurrentProcess(), handle, GINT_TO_POINTER(targetProcessId), duplicate_handle, 0, 0, 0x00000002 /* DUPLICATE_SAME_ACCESS */); + MONO_EXIT_GC_SAFE; + + return ret; +} diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32socket.c b/unity-2019.4.24f1-mbe/mono/metadata/w32socket.c new file mode 100644 index 000000000..4e9121f0e --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32socket.c @@ -0,0 +1,2832 @@ +/** + * \file + * Socket IO internal calls + * + * Authors: + * Dick Porter (dick@ximian.com) + * Gonzalo Paniagua Javier (gonzalo@ximian.com) + * + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * + * This file has been re-licensed under the MIT License: + * http://opensource.org/licenses/MIT + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include + +#ifndef DISABLE_SOCKETS + +#if defined(__APPLE__) || defined(__FreeBSD__) +#define __APPLE_USE_RFC_3542 +#endif + +#include +#include +#include +#ifdef HOST_WIN32 +#include +#else +#include +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#include +#include +#ifdef HAVE_NETDB_H +#include +#endif +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* FIXME change this code to not mess so much with the internals */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_NET_IF_H +#include +#endif + +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_SYS_FILIO_H +#include /* defines FIONBIO and FIONREAD */ +#endif +#ifdef HAVE_SYS_SOCKIO_H +#include /* defines SIOCATMARK */ +#endif +#ifdef HAVE_SYS_UN_H +#include +#endif + +#ifdef HAVE_GETIFADDRS +// must be included before +#include +#endif + +#if defined(_MSC_VER) && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) +#include +#endif + +#define LOGDEBUG(...) +/* define LOGDEBUG(...) g_message(__VA_ARGS__) */ + +static gboolean +addrinfo_to_IPHostEntry_handles (MonoAddressInfo *info, MonoStringHandleOut h_name, MonoArrayHandleOut h_aliases, MonoArrayHandleOut h_addr_list, gboolean add_local_ips, MonoError *error); + +static MonoObjectHandle +create_object_handle_from_sockaddr (struct sockaddr *saddr, int sa_size, gint32 *werror, MonoError *error); + +static struct sockaddr* +create_sockaddr_from_handle (MonoObjectHandle saddr_obj, socklen_t *sa_size, gint32 *werror, MonoError *error); + +#ifdef HOST_WIN32 + +static SOCKET +mono_w32socket_socket (int domain, int type, int protocol) +{ + SOCKET ret; + MONO_ENTER_GC_SAFE; + ret = WSASocket (domain, type, protocol, NULL, 0, WSA_FLAG_OVERLAPPED); + MONO_EXIT_GC_SAFE; + return ret; +} + +static gint +mono_w32socket_bind (SOCKET sock, struct sockaddr *addr, socklen_t addrlen) +{ + gint ret; + MONO_ENTER_GC_SAFE; + ret = bind (sock, addr, addrlen); + MONO_EXIT_GC_SAFE; + return ret; +} + +static gint +mono_w32socket_getpeername (SOCKET sock, struct sockaddr *name, socklen_t *namelen) +{ + gint ret; + MONO_ENTER_GC_SAFE; + ret = getpeername (sock, name, namelen); + MONO_EXIT_GC_SAFE; + return ret; +} + +static gint +mono_w32socket_getsockname (SOCKET sock, struct sockaddr *name, socklen_t *namelen) +{ + gint ret; + MONO_ENTER_GC_SAFE; + ret = getsockname (sock, name, namelen); + MONO_EXIT_GC_SAFE; + return ret; +} + +static gint +mono_w32socket_getsockopt (SOCKET sock, gint level, gint optname, gpointer optval, socklen_t *optlen) +{ + gint ret; + MONO_ENTER_GC_SAFE; + ret = getsockopt (sock, level, optname, optval, optlen); + MONO_EXIT_GC_SAFE; + return ret; +} + +static gint +mono_w32socket_setsockopt (SOCKET sock, gint level, gint optname, const gpointer optval, socklen_t optlen) +{ + gint ret; + MONO_ENTER_GC_SAFE; + ret = setsockopt (sock, level, optname, optval, optlen); + MONO_EXIT_GC_SAFE; + return ret; +} + +static gint +mono_w32socket_listen (SOCKET sock, gint backlog) +{ + gint ret; + MONO_ENTER_GC_SAFE; + ret = listen (sock, backlog); + MONO_EXIT_GC_SAFE; + return ret; +} + +static gint +mono_w32socket_shutdown (SOCKET sock, gint how) +{ + gint ret; + MONO_ENTER_GC_SAFE; + ret = shutdown (sock, how); + MONO_EXIT_GC_SAFE; + return ret; +} + +static gint +mono_w32socket_ioctl (SOCKET sock, gint32 command, gchar *input, gint inputlen, gchar *output, gint outputlen, glong *written) +{ + gint ret; + MONO_ENTER_GC_SAFE; + ret = WSAIoctl (sock, command, input, inputlen, output, outputlen, written, NULL, NULL); + MONO_EXIT_GC_SAFE; + return ret; +} + +static gboolean +mono_w32socket_close (SOCKET sock) +{ + gboolean ret; + MONO_ENTER_GC_SAFE; + ret = closesocket (sock); + MONO_EXIT_GC_SAFE; + return ret; +} + +#endif /* HOST_WIN32 */ + +static void +abort_syscall (gpointer data) +{ + mono_thread_info_abort_socket_syscall_for_close ((MonoNativeThreadId) (gsize) data); +} + +static gint32 +convert_family (MonoAddressFamily mono_family) +{ + switch (mono_family) { + case AddressFamily_Unknown: + case AddressFamily_ImpLink: + case AddressFamily_Pup: + case AddressFamily_Chaos: + case AddressFamily_Iso: + case AddressFamily_Ecma: + case AddressFamily_DataKit: + case AddressFamily_Ccitt: + case AddressFamily_DataLink: + case AddressFamily_Lat: + case AddressFamily_HyperChannel: + case AddressFamily_NetBios: + case AddressFamily_VoiceView: + case AddressFamily_FireFox: + case AddressFamily_Banyan: + case AddressFamily_Atm: + case AddressFamily_Cluster: + case AddressFamily_Ieee12844: + case AddressFamily_NetworkDesigners: + g_warning ("System.Net.Sockets.AddressFamily has unsupported value 0x%x", mono_family); + return -1; + case AddressFamily_Unspecified: + return AF_UNSPEC; + case AddressFamily_Unix: + return AF_UNIX; + case AddressFamily_InterNetwork: + return AF_INET; + case AddressFamily_AppleTalk: +#ifdef AF_APPLETALK + return AF_APPLETALK; +#else + return -1; +#endif + case AddressFamily_InterNetworkV6: +#ifdef HAVE_STRUCT_SOCKADDR_IN6 + return AF_INET6; +#else + return -1; +#endif + case AddressFamily_DecNet: +#ifdef AF_DECnet + return AF_DECnet; +#else + return -1; +#endif + case AddressFamily_Ipx: +#ifdef AF_IPX + return AF_IPX; +#else + return -1; +#endif + case AddressFamily_Sna: +#ifdef AF_SNA + return AF_SNA; +#else + return -1; +#endif + case AddressFamily_Irda: +#ifdef AF_IRDA + return AF_IRDA; +#else + return -1; +#endif + default: + g_warning ("System.Net.Sockets.AddressFamily has unknown value 0x%x", mono_family); + return -1; + } +} + +static MonoAddressFamily +convert_to_mono_family (guint16 af_family) +{ + switch (af_family) { + case AF_UNSPEC: + return AddressFamily_Unspecified; + case AF_UNIX: + return AddressFamily_Unix; + case AF_INET: + return AddressFamily_InterNetwork; +#ifdef AF_IPX + case AF_IPX: + return AddressFamily_Ipx; +#endif +#ifdef AF_SNA + case AF_SNA: + return AddressFamily_Sna; +#endif +#ifdef AF_DECnet + case AF_DECnet: + return AddressFamily_DecNet; +#endif +#ifdef AF_APPLETALK + case AF_APPLETALK: + return AddressFamily_AppleTalk; +#endif +#ifdef HAVE_STRUCT_SOCKADDR_IN6 + case AF_INET6: + return AddressFamily_InterNetworkV6; +#endif +#ifdef AF_IRDA + case AF_IRDA: + return AddressFamily_Irda; +#endif + default: + g_warning ("unknown address family 0x%x", af_family); + return AddressFamily_Unknown; + } +} + +static gint32 +convert_type (MonoSocketType mono_type) +{ + switch (mono_type) { + case SocketType_Stream: + return SOCK_STREAM; + case SocketType_Dgram: + return SOCK_DGRAM; + case SocketType_Raw: + return SOCK_RAW; + case SocketType_Rdm: +#ifdef SOCK_RDM + return SOCK_RDM; +#else + return -1; +#endif + case SocketType_Seqpacket: +#ifdef SOCK_SEQPACKET + return SOCK_SEQPACKET; +#else + return -1; +#endif + case SocketType_Unknown: + g_warning ("System.Net.Sockets.SocketType has unsupported value 0x%x", mono_type); + return -1; + default: + g_warning ("System.Net.Sockets.SocketType has unknown value 0x%x", mono_type); + return -1; + } +} + +static gint32 +convert_proto (MonoProtocolType mono_proto) +{ + switch (mono_proto) { + case ProtocolType_IP: + case ProtocolType_IPv6: + case ProtocolType_Icmp: + case ProtocolType_Igmp: + case ProtocolType_Ggp: + case ProtocolType_Tcp: + case ProtocolType_Pup: + case ProtocolType_Udp: + case ProtocolType_Idp: + /* These protocols are known (on my system at least) */ + return mono_proto; + case ProtocolType_ND: + case ProtocolType_Raw: + case ProtocolType_Ipx: + case ProtocolType_Spx: + case ProtocolType_SpxII: + case ProtocolType_Unknown: + /* These protocols arent */ + g_warning ("System.Net.Sockets.ProtocolType has unsupported value 0x%x", mono_proto); + return -1; + default: + return -1; + } +} + +/* Convert MonoSocketFlags */ +static gint32 +convert_socketflags (gint32 sflags) +{ + gint32 flags = 0; + + if (!sflags) + /* SocketFlags.None */ + return 0; + + if (sflags & ~(SocketFlags_OutOfBand | SocketFlags_MaxIOVectorLength | SocketFlags_Peek | + SocketFlags_DontRoute | SocketFlags_Partial)) + /* Contains invalid flag values */ + return -1; + +#ifdef MSG_OOB + if (sflags & SocketFlags_OutOfBand) + flags |= MSG_OOB; +#endif + if (sflags & SocketFlags_Peek) + flags |= MSG_PEEK; + if (sflags & SocketFlags_DontRoute) + flags |= MSG_DONTROUTE; + + /* Ignore Partial - see bug 349688. Don't return -1, because + * according to the comment in that bug ms runtime doesn't for + * UDP sockets (this means we will silently ignore it for TCP + * too) + */ +#ifdef MSG_MORE + if (sflags & SocketFlags_Partial) + flags |= MSG_MORE; +#endif +#if 0 + /* Don't do anything for MaxIOVectorLength */ + if (sflags & SocketFlags_MaxIOVectorLength) + return -1; +#endif + return flags; +} + +/* + * Returns: + * 0 on success (mapped mono_level and mono_name to system_level and system_name + * -1 on error + * -2 on non-fatal error (ie, must ignore) + */ +static gint32 +convert_sockopt_level_and_name (MonoSocketOptionLevel mono_level, MonoSocketOptionName mono_name, int *system_level, int *system_name) +{ + switch (mono_level) { + case SocketOptionLevel_Socket: + *system_level = SOL_SOCKET; + + switch (mono_name) { + case SocketOptionName_DontLinger: + /* This is SO_LINGER, because the setsockopt + * internal call maps DontLinger to SO_LINGER + * with l_onoff=0 + */ + *system_name = SO_LINGER; + break; +#ifdef SO_DEBUG + case SocketOptionName_Debug: + *system_name = SO_DEBUG; + break; +#endif +#ifdef SO_ACCEPTCONN + case SocketOptionName_AcceptConnection: + *system_name = SO_ACCEPTCONN; + break; +#endif + case SocketOptionName_ReuseAddress: + *system_name = SO_REUSEADDR; + break; + case SocketOptionName_KeepAlive: + *system_name = SO_KEEPALIVE; + break; +#ifdef SO_DONTROUTE + case SocketOptionName_DontRoute: + *system_name = SO_DONTROUTE; + break; +#endif + case SocketOptionName_Broadcast: + *system_name = SO_BROADCAST; + break; + case SocketOptionName_Linger: + *system_name = SO_LINGER; + break; +#ifdef SO_OOBINLINE + case SocketOptionName_OutOfBandInline: + *system_name = SO_OOBINLINE; + break; +#endif + case SocketOptionName_SendBuffer: + *system_name = SO_SNDBUF; + break; + case SocketOptionName_ReceiveBuffer: + *system_name = SO_RCVBUF; + break; + case SocketOptionName_SendLowWater: + *system_name = SO_SNDLOWAT; + break; + case SocketOptionName_ReceiveLowWater: + *system_name = SO_RCVLOWAT; + break; + case SocketOptionName_SendTimeout: + *system_name = SO_SNDTIMEO; + break; + case SocketOptionName_ReceiveTimeout: + *system_name = SO_RCVTIMEO; + break; + case SocketOptionName_Error: + *system_name = SO_ERROR; + break; + case SocketOptionName_Type: + *system_name = SO_TYPE; + break; +#ifdef SO_PEERCRED + case SocketOptionName_PeerCred: + *system_name = SO_PEERCRED; + break; +#endif + case SocketOptionName_ExclusiveAddressUse: +#ifdef SO_EXCLUSIVEADDRUSE + *system_name = SO_EXCLUSIVEADDRUSE; + break; +#endif + case SocketOptionName_UseLoopback: +#ifdef SO_USELOOPBACK + *system_name = SO_USELOOPBACK; + break; +#endif + case SocketOptionName_MaxConnections: +#ifdef SO_MAXCONN + *system_name = SO_MAXCONN; + break; +#elif defined(SOMAXCONN) + *system_name = SOMAXCONN; + break; +#endif + default: + g_warning ("System.Net.Sockets.SocketOptionName 0x%x is not supported at Socket level", mono_name); + return -1; + } + break; + + case SocketOptionLevel_IP: + *system_level = mono_networking_get_ip_protocol (); + + switch (mono_name) { +#ifdef IP_OPTIONS + case SocketOptionName_IPOptions: + *system_name = IP_OPTIONS; + break; +#endif +#ifdef IP_HDRINCL + case SocketOptionName_HeaderIncluded: + *system_name = IP_HDRINCL; + break; +#endif +#ifdef IP_TOS + case SocketOptionName_TypeOfService: + *system_name = IP_TOS; + break; +#endif +#ifdef IP_TTL + case SocketOptionName_IpTimeToLive: + *system_name = IP_TTL; + break; +#endif + case SocketOptionName_MulticastInterface: + *system_name = IP_MULTICAST_IF; + break; + case SocketOptionName_MulticastTimeToLive: + *system_name = IP_MULTICAST_TTL; + break; + case SocketOptionName_MulticastLoopback: + *system_name = IP_MULTICAST_LOOP; + break; + case SocketOptionName_AddMembership: + *system_name = IP_ADD_MEMBERSHIP; + break; + case SocketOptionName_DropMembership: + *system_name = IP_DROP_MEMBERSHIP; + break; +#ifdef HAVE_IP_PKTINFO + case SocketOptionName_PacketInformation: + *system_name = IP_PKTINFO; + break; +#endif /* HAVE_IP_PKTINFO */ + + case SocketOptionName_DontFragment: +#ifdef HAVE_IP_DONTFRAGMENT + *system_name = IP_DONTFRAGMENT; + break; +#elif defined HAVE_IP_MTU_DISCOVER + /* Not quite the same */ + *system_name = IP_MTU_DISCOVER; + break; +#else + /* If the flag is not available on this system, we can ignore this error */ + return -2; +#endif /* HAVE_IP_DONTFRAGMENT */ + case SocketOptionName_AddSourceMembership: + case SocketOptionName_DropSourceMembership: + case SocketOptionName_BlockSource: + case SocketOptionName_UnblockSource: + /* Can't figure out how to map these, so fall + * through + */ + default: + g_warning ("System.Net.Sockets.SocketOptionName 0x%x is not supported at IP level", mono_name); + return -1; + } + break; + + case SocketOptionLevel_IPv6: + *system_level = mono_networking_get_ipv6_protocol (); + + switch (mono_name) { + case SocketOptionName_IpTimeToLive: + case SocketOptionName_HopLimit: + *system_name = IPV6_UNICAST_HOPS; + break; + case SocketOptionName_MulticastInterface: + *system_name = IPV6_MULTICAST_IF; + break; + case SocketOptionName_MulticastTimeToLive: + *system_name = IPV6_MULTICAST_HOPS; + break; + case SocketOptionName_MulticastLoopback: + *system_name = IPV6_MULTICAST_LOOP; + break; + case SocketOptionName_AddMembership: + *system_name = IPV6_JOIN_GROUP; + break; + case SocketOptionName_DropMembership: + *system_name = IPV6_LEAVE_GROUP; + break; + case SocketOptionName_IPv6Only: +#ifdef IPV6_V6ONLY + *system_name = IPV6_V6ONLY; +#else + return -1; +#endif + break; + case SocketOptionName_PacketInformation: +#ifdef HAVE_IPV6_PKTINFO + *system_name = IPV6_PKTINFO; +#endif + break; + case SocketOptionName_HeaderIncluded: + case SocketOptionName_IPOptions: + case SocketOptionName_TypeOfService: + case SocketOptionName_DontFragment: + case SocketOptionName_AddSourceMembership: + case SocketOptionName_DropSourceMembership: + case SocketOptionName_BlockSource: + case SocketOptionName_UnblockSource: + /* Can't figure out how to map these, so fall + * through + */ + default: + g_warning ("System.Net.Sockets.SocketOptionName 0x%x is not supported at IPv6 level", mono_name); + return -1; + } + break; /* SocketOptionLevel_IPv6 */ + + case SocketOptionLevel_Tcp: + *system_level = mono_networking_get_tcp_protocol (); + + switch (mono_name) { + case SocketOptionName_NoDelay: + *system_name = TCP_NODELAY; + break; +#if 0 + /* The documentation is talking complete + * bollocks here: rfc-1222 is titled + * 'Advancing the NSFNET Routing Architecture' + * and doesn't mention either of the words + * "expedite" or "urgent". + */ + case SocketOptionName_BsdUrgent: + case SocketOptionName_Expedited: +#endif + default: + g_warning ("System.Net.Sockets.SocketOptionName 0x%x is not supported at TCP level", mono_name); + return -1; + } + break; + + case SocketOptionLevel_Udp: + g_warning ("System.Net.Sockets.SocketOptionLevel has unsupported value 0x%x", mono_level); + + switch(mono_name) { + case SocketOptionName_NoChecksum: + case SocketOptionName_ChecksumCoverage: + default: + g_warning ("System.Net.Sockets.SocketOptionName 0x%x is not supported at UDP level", mono_name); + return -1; + } + return -1; + break; + + default: + g_warning ("System.Net.Sockets.SocketOptionLevel has unknown value 0x%x", mono_level); + return -1; + } + + return 0; +} + +static MonoImage* +get_socket_assembly (void) +{ + MonoDomain *domain = mono_domain_get (); + + if (domain->socket_assembly == NULL) { + MonoImage *socket_assembly; + + socket_assembly = mono_image_loaded ("System"); + if (!socket_assembly) { + MonoAssembly *sa = mono_assembly_open_predicate ("System.dll", FALSE, FALSE, NULL, NULL, NULL); + + if (!sa) { + g_assert_not_reached (); + } else { + socket_assembly = mono_assembly_get_image (sa); + } + } + mono_atomic_store_release (&domain->socket_assembly, socket_assembly); + } + + return domain->socket_assembly; +} + +gpointer +ves_icall_System_Net_Sockets_Socket_Socket_internal (MonoObjectHandle this_obj, gint32 family, gint32 type, gint32 proto, gint32 *werror, MonoError *error) +{ + SOCKET sock; + gint32 sock_family; + gint32 sock_proto; + gint32 sock_type; + + error_init (error); + *werror = 0; + + sock_family = convert_family ((MonoAddressFamily)family); + if (sock_family == -1) { + *werror = WSAEAFNOSUPPORT; + return NULL; + } + + sock_proto = convert_proto ((MonoProtocolType)proto); + if (sock_proto == -1) { + *werror = WSAEPROTONOSUPPORT; + return NULL; + } + + sock_type = convert_type ((MonoSocketType)type); + if (sock_type == -1) { + *werror = WSAESOCKTNOSUPPORT; + return NULL; + } + + sock = mono_w32socket_socket (sock_family, sock_type, sock_proto); + + if (sock == INVALID_SOCKET) { + *werror = mono_w32socket_get_last_error (); + return NULL; + } + + return GUINT_TO_POINTER (sock); +} + +/* FIXME: the SOCKET parameter (here and in other functions in this + * file) is really an IntPtr which needs to be converted to a guint32. + */ +void +ves_icall_System_Net_Sockets_Socket_Close_internal (gsize sock, gint32 *werror, MonoError *error) +{ + LOGDEBUG (g_message ("%s: closing 0x%x", __func__, sock)); + + error_init (error); + *werror = 0; + + /* Clear any pending work item from this socket if the underlying + * polling system does not notify when the socket is closed */ + mono_threadpool_io_remove_socket (GPOINTER_TO_INT (sock)); + + mono_w32socket_close ((SOCKET) sock); +} + +gint32 +ves_icall_System_Net_Sockets_SocketException_WSAGetLastError_internal (void) +{ + LOGDEBUG (g_message("%s: returning %d", __func__, mono_w32socket_get_last_error ())); + + return mono_w32socket_get_last_error (); +} + +gint32 +ves_icall_System_Net_Sockets_Socket_Available_internal (gsize sock, gint32 *werror, MonoError *error) +{ + int ret; + guint64 amount; + + error_init (error); + *werror = 0; + + /* FIXME: this might require amount to be unsigned long. */ + ret = mono_w32socket_get_available (sock, &amount); + if (ret == SOCKET_ERROR) { + *werror = mono_w32socket_get_last_error (); + return 0; + } + + return amount; +} + +void +ves_icall_System_Net_Sockets_Socket_Blocking_internal (gsize sock, gboolean block, gint32 *werror, MonoError *error) +{ + int ret; + + error_init (error); + *werror = 0; + + ret = mono_w32socket_set_blocking (sock, block); + if (ret == SOCKET_ERROR) + *werror = mono_w32socket_get_last_error (); +} + +gpointer +ves_icall_System_Net_Sockets_Socket_Accept_internal (gsize sock, gint32 *werror, gboolean blocking, MonoError *error) +{ + gboolean interrupted; + SOCKET newsock; + + error_init (error); + *werror = 0; + + mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); + if (interrupted) { + *werror = WSAEINTR; + return NULL; + } + + newsock = mono_w32socket_accept (sock, NULL, 0, blocking); + if (newsock == INVALID_SOCKET) + *werror = mono_w32socket_get_last_error (); + + mono_thread_info_uninstall_interrupt (&interrupted); + if (interrupted) + *werror = WSAEINTR; + + if (*werror) + return NULL; + + return GUINT_TO_POINTER (newsock); +} + +void +ves_icall_System_Net_Sockets_Socket_Listen_internal(gsize sock, guint32 backlog, gint32 *werror, MonoError *error) +{ + int ret; + + error_init (error); + *werror = 0; + + ret = mono_w32socket_listen (sock, backlog); + if (ret == SOCKET_ERROR) + *werror = mono_w32socket_get_last_error (); +} + +#ifdef HAVE_STRUCT_SOCKADDR_IN6 +// Check whether it's ::ffff::0:0. +static gboolean +is_ipv4_mapped_any (const struct in6_addr *addr) +{ + int i; + + for (i = 0; i < 10; i++) { + if (addr->s6_addr [i]) + return FALSE; + } + if ((addr->s6_addr [10] != 0xff) || (addr->s6_addr [11] != 0xff)) + return FALSE; + for (i = 12; i < 16; i++) { + if (addr->s6_addr [i]) + return FALSE; + } + return TRUE; +} +#endif + +static MonoObjectHandle +create_object_handle_from_sockaddr (struct sockaddr *saddr, int sa_size, gint32 *werror, MonoError *error) +{ + MonoDomain *domain = mono_domain_get (); + MonoAddressFamily family; + + error_init (error); + + /* Build a System.Net.SocketAddress object instance */ + if (!domain->sockaddr_class) + domain->sockaddr_class = mono_class_load_from_name (get_socket_assembly (), "System.Net", "SocketAddress"); + MonoObjectHandle sockaddr_obj = MONO_HANDLE_NEW (MonoObject, mono_object_new_checked (domain, domain->sockaddr_class, error)); + return_val_if_nok (error, MONO_HANDLE_NEW (MonoObject, NULL)); + + /* Locate the SocketAddress data buffer in the object */ + if (!domain->sockaddr_data_field) { + domain->sockaddr_data_field = mono_class_get_field_from_name (domain->sockaddr_class, "m_Buffer"); + g_assert (domain->sockaddr_data_field); + } + + /* Locate the SocketAddress data buffer length in the object */ + if (!domain->sockaddr_data_length_field) { + domain->sockaddr_data_length_field = mono_class_get_field_from_name (domain->sockaddr_class, "m_Size"); + g_assert (domain->sockaddr_data_length_field); + } + + /* May be the +2 here is too conservative, as sa_len returns + * the length of the entire sockaddr_in/in6, including + * sizeof (unsigned short) of the family */ + /* We can't really avoid the +2 as all code below depends on this size - INCLUDING unix domain sockets.*/ + MonoArrayHandle data = mono_array_new_handle (domain, mono_get_byte_class (), sa_size + 2, error); + return_val_if_nok (error, MONO_HANDLE_NEW (MonoObject, NULL)); + + /* The data buffer is laid out as follows: + * bytes 0 and 1 are the address family + * bytes 2 and 3 are the port info + * the rest is the address info + */ + + family = convert_to_mono_family (saddr->sa_family); + if (family == AddressFamily_Unknown) { + *werror = WSAEAFNOSUPPORT; + return MONO_HANDLE_NEW (MonoObject, NULL); + } + + MONO_HANDLE_ARRAY_SETVAL (data, guint8, 0, family & 0x0FF); + MONO_HANDLE_ARRAY_SETVAL (data, guint8, 1, (family >> 8) & 0x0FF); + + if (saddr->sa_family == AF_INET) { + struct sockaddr_in *sa_in = (struct sockaddr_in *)saddr; + guint16 port = ntohs (sa_in->sin_port); + guint32 address = ntohl (sa_in->sin_addr.s_addr); + int buffer_size = 8; + + if (sa_size < buffer_size) { + mono_error_set_generic_error (error, "System", "SystemException", ""); + return MONO_HANDLE_NEW (MonoObject, NULL); + } + + MONO_HANDLE_ARRAY_SETVAL (data, guint8, 2, (port>>8) & 0xff); + MONO_HANDLE_ARRAY_SETVAL (data, guint8, 3, (port) & 0xff); + MONO_HANDLE_ARRAY_SETVAL (data, guint8, 4, (address>>24) & 0xff); + MONO_HANDLE_ARRAY_SETVAL (data, guint8, 5, (address>>16) & 0xff); + MONO_HANDLE_ARRAY_SETVAL (data, guint8, 6, (address>>8) & 0xff); + MONO_HANDLE_ARRAY_SETVAL (data, guint8, 7, (address) & 0xff); + + mono_field_set_value (MONO_HANDLE_RAW (sockaddr_obj), domain->sockaddr_data_field, MONO_HANDLE_RAW (data)); /* FIXME: use handles for mono_field_set_value */ + mono_field_set_value (MONO_HANDLE_RAW (sockaddr_obj), domain->sockaddr_data_length_field, &buffer_size); /* FIXME: use handles for mono_field_set_value */ + + return sockaddr_obj; + } +#ifdef HAVE_STRUCT_SOCKADDR_IN6 + else if (saddr->sa_family == AF_INET6) { + struct sockaddr_in6 *sa_in = (struct sockaddr_in6 *)saddr; + int i; + int buffer_size = 28; + + guint16 port = ntohs (sa_in->sin6_port); + + if (sa_size < buffer_size) { + mono_error_set_generic_error (error, "System", "SystemException", ""); + return MONO_HANDLE_NEW (MonoObject, NULL); + } + + MONO_HANDLE_ARRAY_SETVAL (data, guint8, 2, (port>>8) & 0xff); + MONO_HANDLE_ARRAY_SETVAL (data, guint8, 3, (port) & 0xff); + + if (is_ipv4_mapped_any (&sa_in->sin6_addr)) { + // Map ::ffff:0:0 to :: (bug #5502) + for (i = 0; i < 16; i++) + MONO_HANDLE_ARRAY_SETVAL (data, guint8, 8 + i, 0); + } else { + for (i = 0; i < 16; i++) { + MONO_HANDLE_ARRAY_SETVAL (data, guint8, 8 + i, + sa_in->sin6_addr.s6_addr [i]); + } + } + + MONO_HANDLE_ARRAY_SETVAL (data, guint8, 24, sa_in->sin6_scope_id & 0xff); + MONO_HANDLE_ARRAY_SETVAL (data, guint8, 25, + (sa_in->sin6_scope_id >> 8) & 0xff); + MONO_HANDLE_ARRAY_SETVAL (data, guint8, 26, + (sa_in->sin6_scope_id >> 16) & 0xff); + MONO_HANDLE_ARRAY_SETVAL (data, guint8, 27, + (sa_in->sin6_scope_id >> 24) & 0xff); + + mono_field_set_value (MONO_HANDLE_RAW (sockaddr_obj), domain->sockaddr_data_field, MONO_HANDLE_RAW (data)); /* FIXME: use handles for mono_field_set_value */ + mono_field_set_value (MONO_HANDLE_RAW (sockaddr_obj), domain->sockaddr_data_length_field, &buffer_size); /* FIXME: use handles for mono_field_set_value */ + + return sockaddr_obj; + } +#endif +#ifdef HAVE_SYS_UN_H + else if (saddr->sa_family == AF_UNIX) { + int i; + int buffer_size = sa_size + 2; + + for (i = 0; i < sa_size; i++) + MONO_HANDLE_ARRAY_SETVAL (data, guint8, i + 2, saddr->sa_data [i]); + + mono_field_set_value (MONO_HANDLE_RAW (sockaddr_obj), domain->sockaddr_data_field, MONO_HANDLE_RAW (data)); /* FIXME: use handles for mono_field_set_value */ + mono_field_set_value (MONO_HANDLE_RAW (sockaddr_obj), domain->sockaddr_data_length_field, &buffer_size); /* FIXME: use handles for mono_field_set_value */ + + return sockaddr_obj; + } +#endif + else { + *werror = WSAEAFNOSUPPORT; + return MONO_HANDLE_NEW (MonoObject, NULL); + } +} + +static int +get_sockaddr_size (int family) +{ + int size; + + size = 0; + if (family == AF_INET) { + size = sizeof (struct sockaddr_in); + } +#ifdef HAVE_STRUCT_SOCKADDR_IN6 + else if (family == AF_INET6) { + size = sizeof (struct sockaddr_in6); + } +#endif +#ifdef HAVE_SYS_UN_H + else if (family == AF_UNIX) { + size = sizeof (struct sockaddr_un); + } +#endif + return size; +} + +MonoObjectHandle +ves_icall_System_Net_Sockets_Socket_LocalEndPoint_internal (gsize sock, gint32 af, gint32 *werror, MonoError *error) +{ + gchar *sa; + socklen_t salen; + int ret; + + *werror = 0; + + salen = get_sockaddr_size (convert_family ((MonoAddressFamily)af)); + if (salen == 0) { + *werror = WSAEAFNOSUPPORT; + return NULL; + } + sa = (salen <= 128) ? (gchar *)alloca (salen) : (gchar *)g_malloc0 (salen); + + ret = mono_w32socket_getsockname (sock, (struct sockaddr *)sa, &salen); + if (ret == SOCKET_ERROR) { + *werror = mono_w32socket_get_last_error (); + if (salen > 128) + g_free (sa); + return NULL_HANDLE; + } + + LOGDEBUG (g_message("%s: bound to %s port %d", __func__, inet_ntoa (((struct sockaddr_in *)&sa)->sin_addr), ntohs (((struct sockaddr_in *)&sa)->sin_port))); + + MonoObjectHandle result = create_object_handle_from_sockaddr ((struct sockaddr *)sa, salen, werror, error); + if (salen > 128) + g_free (sa); + return result; +} + +MonoObjectHandle +ves_icall_System_Net_Sockets_Socket_RemoteEndPoint_internal (gsize sock, gint32 af, gint32 *werror, MonoError *error) +{ + gchar *sa; + socklen_t salen; + int ret; + + error_init (error); + *werror = 0; + + salen = get_sockaddr_size (convert_family ((MonoAddressFamily)af)); + if (salen == 0) { + *werror = WSAEAFNOSUPPORT; + return MONO_HANDLE_NEW (MonoObject, NULL); + } + sa = (salen <= 128) ? (gchar *)alloca (salen) : (gchar *)g_malloc0 (salen); + /* Note: linux returns just 2 for AF_UNIX. Always. */ + + ret = mono_w32socket_getpeername (sock, (struct sockaddr *)sa, &salen); + if (ret == SOCKET_ERROR) { + *werror = mono_w32socket_get_last_error (); + if (salen > 128) + g_free (sa); + return MONO_HANDLE_NEW (MonoObject, NULL); + } + + LOGDEBUG (g_message("%s: connected to %s port %d", __func__, inet_ntoa (((struct sockaddr_in *)&sa)->sin_addr), ntohs (((struct sockaddr_in *)&sa)->sin_port))); + + MonoObjectHandle result = create_object_handle_from_sockaddr ((struct sockaddr *)sa, salen, werror, error); + if (salen > 128) + g_free (sa); + return result; +} + +static struct sockaddr* +create_sockaddr_from_handle (MonoObjectHandle saddr_obj, socklen_t *sa_size, gint32 *werror, MonoError *error) +{ + MonoDomain *domain = mono_domain_get (); + gint32 family; + int len; + + error_init (error); + + if (!domain->sockaddr_class) + domain->sockaddr_class = mono_class_load_from_name (get_socket_assembly (), "System.Net", "SocketAddress"); + + /* Locate the SocketAddress data buffer in the object */ + if (!domain->sockaddr_data_field) { + domain->sockaddr_data_field = mono_class_get_field_from_name (domain->sockaddr_class, "m_Buffer"); + g_assert (domain->sockaddr_data_field); + } + + /* Locate the SocketAddress data buffer length in the object */ + if (!domain->sockaddr_data_length_field) { + domain->sockaddr_data_length_field = mono_class_get_field_from_name (domain->sockaddr_class, "m_Size"); + g_assert (domain->sockaddr_data_length_field); + } + + MonoArrayHandle data = MONO_HANDLE_NEW_GET_FIELD (saddr_obj, MonoArray, domain->sockaddr_data_field); + + /* The data buffer is laid out as follows: + * byte 0 is the address family low byte + * byte 1 is the address family high byte + * INET: + * bytes 2 and 3 are the port info + * the rest is the address info + * UNIX: + * the rest is the file name + */ + len = MONO_HANDLE_GET_FIELD_VAL (saddr_obj, int, domain->sockaddr_data_length_field); + g_assert (len >= 2); + + uint32_t gchandle; + guint8 *buf = MONO_ARRAY_HANDLE_PIN (data, guint8, 0, &gchandle); + family = convert_family ((MonoAddressFamily)(buf[0] + (buf[1] << 8))); + if (family == AF_INET) { + struct sockaddr_in *sa; + guint16 port; + guint32 address; + + if (len < 8) { + mono_error_set_generic_error (error, "System", "SystemException", ""); + mono_gchandle_free (gchandle); + return NULL; + } + + sa = g_new0 (struct sockaddr_in, 1); + port = (buf[2] << 8) + buf[3]; + address = (buf[4] << 24) + (buf[5] << 16) + (buf[6] << 8) + buf[7]; + + sa->sin_family = family; + sa->sin_addr.s_addr = htonl (address); + sa->sin_port = htons (port); + + *sa_size = sizeof (struct sockaddr_in); + mono_gchandle_free (gchandle); + return (struct sockaddr *)sa; + } +#ifdef HAVE_STRUCT_SOCKADDR_IN6 + else if (family == AF_INET6) { + struct sockaddr_in6 *sa; + int i; + guint16 port; + guint32 scopeid; + + if (len < 28) { + mono_error_set_generic_error (error, "System", "SystemException", ""); + mono_gchandle_free (gchandle); + return NULL; + } + + sa = g_new0 (struct sockaddr_in6, 1); + port = buf[3] + (buf[2] << 8); + scopeid = buf[24] + (buf[25] << 8) + (buf[26] << 16) + (buf[27] << 24); + + sa->sin6_family = family; + sa->sin6_port = htons (port); + sa->sin6_scope_id = scopeid; + + for (i = 0; i < 16; i++) + sa->sin6_addr.s6_addr [i] = buf[8 + i]; + + *sa_size = sizeof (struct sockaddr_in6); + mono_gchandle_free (gchandle); + return (struct sockaddr *)sa; + } +#endif +#ifdef HAVE_SYS_UN_H + else if (family == AF_UNIX) { + struct sockaddr_un *sock_un; + int i; + + /* Need a byte for the '\0' terminator/prefix, and the first + * two bytes hold the SocketAddress family + */ + if (len - 2 >= sizeof (sock_un->sun_path)) { + mono_error_set_exception_instance (error, mono_get_exception_index_out_of_range ()); + mono_gchandle_free (gchandle); + return NULL; + } + + sock_un = g_new0 (struct sockaddr_un, 1); + + sock_un->sun_family = family; + for (i = 0; i < len - 2; i++) + sock_un->sun_path [i] = buf[i + 2]; + + *sa_size = len; + mono_gchandle_free (gchandle); + return (struct sockaddr *)sock_un; + } +#endif + else { + *werror = WSAEAFNOSUPPORT; + mono_gchandle_free (gchandle); + return 0; + } +} + +void +ves_icall_System_Net_Sockets_Socket_Bind_internal (gsize sock, MonoObjectHandle sockaddr, gint32 *werror, MonoError *error) +{ + struct sockaddr *sa; + socklen_t sa_size; + int ret; + + error_init (error); + *werror = 0; + + sa = create_sockaddr_from_handle (sockaddr, &sa_size, werror, error); + if (*werror != 0) + return; + return_if_nok (error); + + LOGDEBUG (g_message("%s: binding to %s port %d", __func__, inet_ntoa (((struct sockaddr_in *)sa)->sin_addr), ntohs (((struct sockaddr_in *)sa)->sin_port))); + + ret = mono_w32socket_bind (sock, sa, sa_size); + + if (ret == SOCKET_ERROR) + *werror = mono_w32socket_get_last_error (); + + g_free (sa); +} + +enum { + SelectModeRead, + SelectModeWrite, + SelectModeError +}; + +MonoBoolean +ves_icall_System_Net_Sockets_Socket_Poll_internal (gsize sock, gint mode, + gint timeout, gint32 *werror, MonoError *error) +{ + MonoInternalThread *thread = mono_thread_internal_current (); + mono_pollfd *pfds; + int ret; + gboolean interrupted; + time_t start; + + error_init (error); + *werror = 0; + + pfds = g_new0 (mono_pollfd, 1); + pfds->fd = GPOINTER_TO_INT (sock); + + switch (mode) { + case SelectModeRead: + pfds->events = MONO_POLLIN; + break; + case SelectModeWrite: + pfds->events = MONO_POLLOUT; + break; + default: + pfds->events = MONO_POLLERR | MONO_POLLHUP | MONO_POLLNVAL; + break; + } + + timeout = (timeout >= 0) ? (timeout / 1000) : -1; + start = time (NULL); + + do { + mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); + if (interrupted) { + g_free (pfds); + *werror = WSAEINTR; + return FALSE; + } + + MONO_ENTER_GC_SAFE; + + ret = mono_poll (pfds, 1, timeout); + + MONO_EXIT_GC_SAFE; + + mono_thread_info_uninstall_interrupt (&interrupted); + if (interrupted) { + g_free (pfds); + *werror = WSAEINTR; + return FALSE; + } + + if (timeout > 0 && ret < 0) { + int err = errno; + int sec = time (NULL) - start; + + timeout -= sec * 1000; + if (timeout < 0) { + timeout = 0; + } + + errno = err; + } + + if (ret == -1 && errno == EINTR) { + if (mono_thread_test_state (thread, ThreadState_AbortRequested)) { + g_free (pfds); + return FALSE; + } + + /* Suspend requested? */ + mono_thread_interruption_checkpoint (); + + errno = EINTR; + } + } while (ret == -1 && errno == EINTR); + + if (ret == -1) { + *werror = mono_w32socket_convert_error (errno); + g_free (pfds); + return FALSE; + } + + g_free (pfds); + return ret != 0; +} + +void +ves_icall_System_Net_Sockets_Socket_Connect_internal (gsize sock, MonoObjectHandle sockaddr, gint32 *werror, gboolean blocking, MonoError *error) +{ + struct sockaddr *sa; + socklen_t sa_size; + int ret; + gboolean interrupted; + + error_init (error); + *werror = 0; + + sa = create_sockaddr_from_handle (sockaddr, &sa_size, werror, error); + if (*werror != 0) + return; + return_if_nok (error); + + LOGDEBUG (g_message("%s: connecting to %s port %d", __func__, inet_ntoa (((struct sockaddr_in *)sa)->sin_addr), ntohs (((struct sockaddr_in *)sa)->sin_port))); + + mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); + if (interrupted) { + *werror = WSAEINTR; + return; + } + + ret = mono_w32socket_connect (sock, sa, sa_size, blocking); + if (ret == SOCKET_ERROR) + *werror = mono_w32socket_get_last_error (); + + mono_thread_info_uninstall_interrupt (&interrupted); + if (interrupted) + *werror = WSAEINTR; + + g_free (sa); +} + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) + +void +ves_icall_System_Net_Sockets_Socket_Disconnect_internal (gsize sock, MonoBoolean reuse, gint32 *werror, MonoError *error) +{ + gboolean interrupted; + + error_init (error); + + LOGDEBUG (g_message("%s: disconnecting from socket %p (reuse %d)", __func__, sock, reuse)); + + mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); + if (interrupted) { + *werror = WSAEINTR; + return; + } + + *werror = mono_w32socket_disconnect (sock, reuse); + + mono_thread_info_uninstall_interrupt (&interrupted); + if (interrupted) + *werror = WSAEINTR; +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */ + +MonoBoolean +ves_icall_System_Net_Sockets_Socket_Duplicate_internal (gpointer handle, gint32 targetProcessId, gpointer *duplicate_handle, gint32 *werror, MonoError *error) +{ + error_init (error); + + *werror = 0; + if (!mono_w32socket_duplicate (handle, targetProcessId, duplicate_handle)) { + *werror = mono_w32error_get_last (); + return FALSE; + } + + return TRUE; +} + +gint32 +ves_icall_System_Net_Sockets_Socket_Receive_internal (gsize sock, gchar *buffer, gint32 count, gint32 flags, gint32 *werror, gboolean blocking, MonoError *error) +{ + int ret; + int recvflags = 0; + gboolean interrupted; + + error_init (error); + *werror = 0; + + recvflags = convert_socketflags (flags); + if (recvflags == -1) { + *werror = WSAEOPNOTSUPP; + return 0; + } + + mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); + if (interrupted) + return 0; + + ret = mono_w32socket_recv (sock, buffer, count, recvflags, blocking); + + if (ret == SOCKET_ERROR) + *werror = mono_w32socket_get_last_error (); + + mono_thread_info_uninstall_interrupt (&interrupted); + if (interrupted) + *werror = WSAEINTR; + + if (*werror) + return 0; + + return ret; +} + +gint32 +ves_icall_System_Net_Sockets_Socket_Receive_array_internal (gsize sock, WSABUF *buffers, gint32 count, gint32 flags, gint32 *werror, gboolean blocking, MonoError *error) +{ + int ret; + gboolean interrupted; + guint32 recv; + guint32 recvflags = 0; + + error_init (error); + *werror = 0; + + recvflags = convert_socketflags (flags); + if (recvflags == -1) { + *werror = WSAEOPNOTSUPP; + return 0; + } + + mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); + if (interrupted) { + *werror = WSAEINTR; + return 0; + } + + ret = mono_w32socket_recvbuffers (sock, buffers, count, &recv, &recvflags, NULL, NULL, blocking); + + if (ret == SOCKET_ERROR) + *werror = mono_w32socket_get_last_error (); + + mono_thread_info_uninstall_interrupt (&interrupted); + if (interrupted) + *werror = WSAEINTR; + + if (*werror) + return 0; + + return recv; +} + +gint32 +ves_icall_System_Net_Sockets_Socket_ReceiveFrom_internal (gsize sock, gchar *buffer, gint32 count, gint32 flags, MonoObjectHandle sockaddr, gint32 *werror, gboolean blocking, MonoError *error) +{ + int ret; + int recvflags = 0; + struct sockaddr *sa; + socklen_t sa_size; + gboolean interrupted; + + error_init (error); + *werror = 0; + + sa = create_sockaddr_from_handle (sockaddr, &sa_size, werror, error); + if (*werror != 0) + return 0; + if (!is_ok (error)) + return 0; + + recvflags = convert_socketflags (flags); + if (recvflags == -1) { + *werror = WSAEOPNOTSUPP; + return 0; + } + + mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); + if (interrupted) { + g_free (sa); + *werror = WSAEINTR; + return 0; + } + + ret = mono_w32socket_recvfrom (sock, buffer, count, recvflags, sa, &sa_size, blocking); + + if (ret == SOCKET_ERROR) + *werror = mono_w32socket_get_last_error (); + + mono_thread_info_uninstall_interrupt (&interrupted); + + if (interrupted) + *werror = WSAEINTR; + + if (*werror) { + g_free(sa); + return 0; + } + + /* If we didn't get a socket size, then we're probably a + * connected connection-oriented socket and the stack hasn't + * returned the remote address. All we can do is return null. + */ + if (sa_size) { + MONO_HANDLE_ASSIGN (sockaddr, create_object_handle_from_sockaddr (sa, sa_size, werror, error)); + if (!is_ok (error)) { + g_free (sa); + return 0; + } + } else { + MONO_HANDLE_ASSIGN (sockaddr, MONO_HANDLE_NEW (MonoObject, NULL)); + } + + g_free (sa); + + return ret; +} + +gint32 +ves_icall_System_Net_Sockets_Socket_Send_internal (gsize sock, gchar *buffer, gint32 count, gint32 flags, gint32 *werror, gboolean blocking, MonoError *error) +{ + int ret; + int sendflags = 0; + gboolean interrupted; + + error_init (error); + *werror = 0; + + LOGDEBUG (g_message("%s: Sending %d bytes", __func__, count)); + + sendflags = convert_socketflags (flags); + if (sendflags == -1) { + *werror = WSAEOPNOTSUPP; + return 0; + } + + mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); + if (interrupted) { + *werror = WSAEINTR; + return 0; + } + + ret = mono_w32socket_send (sock, buffer, count, sendflags, blocking); + + if (ret == SOCKET_ERROR) + *werror = mono_w32socket_get_last_error (); + + mono_thread_info_uninstall_interrupt (&interrupted); + if (interrupted) + *werror = WSAEINTR; + + if (*werror) + return 0; + + return ret; +} + +gint32 +ves_icall_System_Net_Sockets_Socket_Send_array_internal (gsize sock, WSABUF *buffers, gint32 count, gint32 flags, gint32 *werror, gboolean blocking, MonoError *error) +{ + int ret; + guint32 sent; + guint32 sendflags = 0; + gboolean interrupted; + + error_init (error); + *werror = 0; + + sendflags = convert_socketflags (flags); + if (sendflags == -1) { + *werror = WSAEOPNOTSUPP; + return 0; + } + + mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); + if (interrupted) { + *werror = WSAEINTR; + return 0; + } + + ret = mono_w32socket_sendbuffers (sock, buffers, count, &sent, sendflags, NULL, NULL, blocking); + + if (ret == SOCKET_ERROR) + *werror = mono_w32socket_get_last_error (); + + mono_thread_info_uninstall_interrupt (&interrupted); + if (interrupted) + *werror = WSAEINTR; + + if (*werror) + return 0; + + return sent; +} + +gint32 +ves_icall_System_Net_Sockets_Socket_SendTo_internal (gsize sock, gchar *buffer, gint32 count, gint32 flags, MonoObjectHandle sockaddr, gint32 *werror, gboolean blocking, MonoError *error) +{ + int ret; + int sendflags = 0; + struct sockaddr *sa; + socklen_t sa_size; + gboolean interrupted; + + *werror = 0; + + sa = create_sockaddr_from_handle (sockaddr, &sa_size, werror, error); + if (*werror != 0) + return 0; + return_val_if_nok (error, 0); + + LOGDEBUG (g_message("%s: Sending %d bytes", __func__, count)); + + sendflags = convert_socketflags (flags); + if (sendflags == -1) { + g_free (sa); + *werror = WSAEOPNOTSUPP; + return 0; + } + + mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); + if (interrupted) { + g_free (sa); + *werror = WSAEINTR; + return 0; + } + + ret = mono_w32socket_sendto (sock, buffer, count, sendflags, sa, sa_size, blocking); + + if (ret == SOCKET_ERROR) + *werror = mono_w32socket_get_last_error (); + + mono_thread_info_uninstall_interrupt (&interrupted); + if (interrupted) + *werror = WSAEINTR; + + g_free(sa); + + if (*werror) + return 0; + + return ret; +} + +static SOCKET +Socket_to_SOCKET (MonoObjectHandle sockobj) +{ + MonoClassField *field; + + field = mono_class_get_field_from_name (mono_handle_class (sockobj), "m_Handle"); + MonoSafeHandleHandle safe_handle = MONO_HANDLE_NEW_GET_FIELD(sockobj, MonoSafeHandle, field); + + if (MONO_HANDLE_IS_NULL (safe_handle)) + return -1; + + return (SOCKET)MONO_HANDLE_GETVAL (safe_handle, handle); +} + +#define POLL_ERRORS (MONO_POLLERR | MONO_POLLHUP | MONO_POLLNVAL) + +static gboolean +collect_pollfds_from_array (MonoArrayHandle sockets, int i, int nfds, mono_pollfd *pfds, int *idx, int *mode) +{ + HANDLE_FUNCTION_ENTER (); + gboolean result = TRUE; + MonoObjectHandle obj = MONO_HANDLE_NEW (MonoObject, NULL); + MONO_HANDLE_ARRAY_GETREF (obj, sockets, i); + if (MONO_HANDLE_IS_NULL (obj)) { + (*mode)++; + goto leave; + } + + if (*idx >= nfds) { + result = FALSE; + goto leave; + } + + pfds [*idx].fd = Socket_to_SOCKET (obj); + pfds [*idx].events = (*mode == 0) ? MONO_POLLIN : (*mode == 1) ? MONO_POLLOUT : POLL_ERRORS; + (*idx)++; +leave: + HANDLE_FUNCTION_RETURN_VAL (result); +} + +static void +set_socks_array_from_pollfds (MonoArrayHandle sockets, int i, mono_pollfd *pfds, int *ret, int *mode, MonoArrayHandle socks, int *idx) +{ + HANDLE_FUNCTION_ENTER (); + mono_pollfd *pfd; + + MonoObjectHandle obj = MONO_HANDLE_NEW (MonoObject, NULL); + MONO_HANDLE_ARRAY_GETREF (obj, sockets, i); + if (MONO_HANDLE_IS_NULL (obj)) { + (*mode)++; + (*idx)++; + goto leave; + } + + pfd = &pfds [i - *mode]; + if (pfd->revents == 0) + goto leave; + + (*ret)--; + if (((*mode == 0 && (pfd->revents & (MONO_POLLIN | POLL_ERRORS)) != 0)) || + ((*mode == 1 && (pfd->revents & (MONO_POLLOUT | POLL_ERRORS)) != 0)) || + ((pfd->revents & POLL_ERRORS) != 0)) { + MONO_HANDLE_ARRAY_SETREF (socks, *idx, obj); + (*idx)++; + } +leave: + HANDLE_FUNCTION_RETURN (); +} + +void +ves_icall_System_Net_Sockets_Socket_Select_internal (MonoArrayHandle sockets, gint32 timeout, gint32 *werror, MonoError *error) +{ + MonoInternalThread *thread = mono_thread_internal_current (); + mono_pollfd *pfds; + int nfds, idx; + int ret; + int i, count; + int mode; + MonoClass *sock_arr_class; + time_t start; + uintptr_t socks_size; + gboolean interrupted; + + error_init (error); + *werror = 0; + + /* *sockets -> READ, null, WRITE, null, ERROR, null */ + count = mono_array_handle_length (sockets); + nfds = count - 3; /* NULL separators */ + pfds = g_new0 (mono_pollfd, nfds); + mode = idx = 0; + for (i = 0; i < count; i++) { + if (!collect_pollfds_from_array (sockets, i, nfds, pfds, &idx, &mode)) { + /* The socket array was bogus */ + g_free (pfds); + *werror = WSAEFAULT; + return; + } + } + + timeout = (timeout >= 0) ? (timeout / 1000) : -1; + start = time (NULL); + do { + mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); + if (interrupted) { + g_free (pfds); + *werror = WSAEINTR; + return; + } + + MONO_ENTER_GC_SAFE; + + ret = mono_poll (pfds, nfds, timeout); + + MONO_EXIT_GC_SAFE; + + mono_thread_info_uninstall_interrupt (&interrupted); + if (interrupted) { + g_free (pfds); + *werror = WSAEINTR; + return; + } + + if (timeout > 0 && ret < 0) { + int err = errno; + int sec = time (NULL) - start; + + timeout -= sec * 1000; + if (timeout < 0) + timeout = 0; + errno = err; + } + + if (ret == -1 && errno == EINTR) { + if (mono_thread_test_state (thread, ThreadState_AbortRequested)) { + g_free (pfds); + MONO_HANDLE_ASSIGN (sockets, MONO_HANDLE_NEW (MonoObject, NULL)); + return; + } + + /* Suspend requested? */ + mono_thread_interruption_checkpoint (); + + errno = EINTR; + } + } while (ret == -1 && errno == EINTR); + + if (ret == -1) { + *werror = mono_w32socket_convert_error (errno); + g_free (pfds); + return; + } + + if (ret == 0) { + g_free (pfds); + MONO_HANDLE_ASSIGN (sockets, MONO_HANDLE_NEW (MonoObject, NULL)); + return; + } + + sock_arr_class = mono_handle_class (sockets); + socks_size = ((uintptr_t)ret) + 3; /* space for the NULL delimiters */ + MonoArrayHandle socks = MONO_HANDLE_NEW (MonoArray, mono_array_new_full_checked (mono_domain_get (), sock_arr_class, &socks_size, NULL, error)); + if (!is_ok (error)) { + g_free (pfds); + return; + } + + mode = idx = 0; + for (i = 0; i < count && ret > 0; i++) { + set_socks_array_from_pollfds (sockets, i, pfds, &ret, &mode, socks, &idx); + } + + MONO_HANDLE_ASSIGN (sockets, socks); + g_free (pfds); +} + +static MonoObjectHandle +int_to_object_handle (MonoDomain *domain, int val, MonoError *error) +{ + return MONO_HANDLE_NEW (MonoObject, mono_value_box_checked (domain, mono_get_int32_class (), &val, error)); +} + +void +ves_icall_System_Net_Sockets_Socket_GetSocketOption_obj_internal (gsize sock, gint32 level, gint32 name, MonoObjectHandle obj_val, gint32 *werror, MonoError *error) +{ + int system_level = 0; + int system_name = 0; + int ret; + int val = 0; + socklen_t valsize = sizeof (val); + struct linger linger; + socklen_t lingersize = sizeof (linger); + int time_ms = 0; + socklen_t time_ms_size = sizeof (time_ms); +#ifdef SO_PEERCRED +# if defined(__OpenBSD__) + struct sockpeercred cred; +# else + struct ucred cred; +# endif + socklen_t credsize = sizeof (cred); +#endif + MonoDomain *domain = mono_domain_get (); + MonoClass *obj_class; + MonoClassField *field; + + error_init (error); + *werror = 0; + +#if !defined(SO_EXCLUSIVEADDRUSE) && defined(SO_REUSEADDR) + if (level == SocketOptionLevel_Socket && name == SocketOptionName_ExclusiveAddressUse) { + system_level = SOL_SOCKET; + system_name = SO_REUSEADDR; + ret = 0; + } else +#endif + { + ret = convert_sockopt_level_and_name ((MonoSocketOptionLevel)level, (MonoSocketOptionName)name, &system_level, &system_name); + } + + if (ret == -1) { + *werror = WSAENOPROTOOPT; + return; + } + if (ret == -2) { + MONO_HANDLE_ASSIGN (obj_val, int_to_object_handle (domain, 0, error)); + return; + } + + /* No need to deal with MulticastOption names here, because + * you cant getsockopt AddMembership or DropMembership (the + * int getsockopt will error, causing an exception) + */ + switch (name) { + case SocketOptionName_Linger: + case SocketOptionName_DontLinger: + ret = mono_w32socket_getsockopt (sock, system_level, system_name, &linger, &lingersize); + break; + + case SocketOptionName_SendTimeout: + case SocketOptionName_ReceiveTimeout: + ret = mono_w32socket_getsockopt (sock, system_level, system_name, (char *)&time_ms, &time_ms_size); + break; + +#ifdef SO_PEERCRED + case SocketOptionName_PeerCred: + ret = mono_w32socket_getsockopt (sock, system_level, system_name, &cred, &credsize); + break; +#endif + + default: + ret = mono_w32socket_getsockopt (sock, system_level, system_name, &val, &valsize); + } + + if (ret == SOCKET_ERROR) { + *werror = mono_w32socket_get_last_error (); + return; + } + + switch (name) { + case SocketOptionName_Linger: { + /* build a System.Net.Sockets.LingerOption */ + obj_class = mono_class_load_from_name (get_socket_assembly (), + "System.Net.Sockets", + "LingerOption"); + MonoObjectHandle obj = MONO_HANDLE_NEW (MonoObject, mono_object_new_checked (domain, obj_class, error)); + return_if_nok (error); + + /* Locate and set the fields "bool enabled" and "int + * lingerTime" + */ + field = mono_class_get_field_from_name(obj_class, "enabled"); + MONO_HANDLE_SET_FIELD_VAL (obj, guint8, field, linger.l_onoff); + + field = mono_class_get_field_from_name(obj_class, "lingerTime"); + MONO_HANDLE_SET_FIELD_VAL (obj, guint32, field, linger.l_linger); + + MONO_HANDLE_ASSIGN (obj_val, obj); + break; + } + case SocketOptionName_DontLinger: { + /* construct a bool int in val - true if linger is off */ + MonoObjectHandle obj = int_to_object_handle (domain, !linger.l_onoff, error); + return_if_nok (error); + + MONO_HANDLE_ASSIGN (obj_val, obj); + break; + } + case SocketOptionName_SendTimeout: + case SocketOptionName_ReceiveTimeout: { + MonoObjectHandle obj = int_to_object_handle (domain, time_ms, error); + return_if_nok (error); + + MONO_HANDLE_ASSIGN (obj_val, obj); + break; + } + +#ifdef SO_PEERCRED + case SocketOptionName_PeerCred: { + /* + * build a Mono.Posix.PeerCred+PeerCredData if + * possible + */ + static MonoImage *mono_posix_image = NULL; + + if (mono_posix_image == NULL) { + mono_posix_image = mono_image_loaded ("Mono.Posix"); + if (!mono_posix_image) { + MonoAssembly *sa = mono_assembly_open_predicate ("Mono.Posix.dll", FALSE, FALSE, NULL, NULL, NULL); + if (!sa) { + *werror = WSAENOPROTOOPT; + return; + } else { + mono_posix_image = mono_assembly_get_image (sa); + } + } + } + + obj_class = mono_class_load_from_name (mono_posix_image, + "Mono.Posix", + "PeerCredData"); + MonoPeerCredDataHandle cred_data = MONO_HANDLE_NEW (MonoPeerCredData, mono_object_new_checked (domain, obj_class, error)); + return_if_nok (error); + + MONO_HANDLE_SETVAL (cred_data, pid, gint, cred.pid); + MONO_HANDLE_SETVAL (cred_data, uid, gint, cred.uid); + MONO_HANDLE_SETVAL (cred_data, gid, gint, cred.gid); + + MONO_HANDLE_ASSIGN (obj_val, cred_data); + break; + } +#endif + + default: { +#if !defined(SO_EXCLUSIVEADDRUSE) && defined(SO_REUSEADDR) + if (level == SocketOptionLevel_Socket && name == SocketOptionName_ExclusiveAddressUse) + val = val ? 0 : 1; +#endif + MonoObjectHandle obj = int_to_object_handle (domain, val, error); + return_if_nok (error); + + MONO_HANDLE_ASSIGN (obj_val, obj); + } + } +} + +void +ves_icall_System_Net_Sockets_Socket_GetSocketOption_arr_internal (gsize sock, gint32 level, gint32 name, MonoArrayHandle byte_val, gint32 *werror, MonoError *error) +{ + int system_level = 0; + int system_name = 0; + int ret; + socklen_t valsize; + + error_init (error); + *werror = 0; + + ret = convert_sockopt_level_and_name((MonoSocketOptionLevel)level, (MonoSocketOptionName)name, &system_level, + &system_name); + if (ret == -1) { + *werror = WSAENOPROTOOPT; + return; + } + if (ret == -2) + return; + + valsize = mono_array_handle_length (byte_val); + + uint32_t gchandle; + guchar *buf = MONO_ARRAY_HANDLE_PIN (byte_val, guchar, 0, &gchandle); + + ret = mono_w32socket_getsockopt (sock, system_level, system_name, buf, &valsize); + + mono_gchandle_free (gchandle); + + if (ret == SOCKET_ERROR) + *werror = mono_w32socket_get_last_error (); +} + +#if defined(HAVE_STRUCT_IP_MREQN) || defined(HAVE_STRUCT_IP_MREQ) +static struct in_addr +ipaddress_handle_to_struct_in_addr (MonoObjectHandle ipaddr) +{ + struct in_addr inaddr; + MonoClassField *field; + + field = mono_class_get_field_from_name (mono_handle_class (ipaddr), "m_Address"); + g_assert (field); + + /* No idea why .net uses a 64bit type to hold a 32bit value... + * + * Internal value of IPAddess is in little-endian order + */ + inaddr.s_addr = GUINT_FROM_LE ((guint32)MONO_HANDLE_GET_FIELD_VAL (ipaddr, guint64, field)); + + return inaddr; +} + +#ifdef HAVE_STRUCT_SOCKADDR_IN6 +static struct in6_addr +ipaddress_handle_to_struct_in6_addr (MonoObjectHandle ipaddr) +{ + struct in6_addr in6addr; + MonoClassField *field; + int i; + + field = mono_class_get_field_from_name (mono_handle_class (ipaddr), "m_Numbers"); + g_assert (field); + MonoArrayHandle data = MONO_HANDLE_NEW_GET_FIELD (ipaddr, MonoArray, field); + + for (i = 0; i < 8; i++) { + guint16 v; + MONO_HANDLE_ARRAY_GETVAL (v, data, guint16, i); + const guint16 s = GUINT16_TO_BE (v); + +/* Solaris/MacOS have only the 8 bit version. */ +#ifndef s6_addr16 + in6addr.s6_addr[2 * i + 1] = (s >> 8) & 0xff; + in6addr.s6_addr[2 * i] = s & 0xff; +#else + in6addr.s6_addr16[i] = s; +#endif + } + return in6addr; +} +#endif +#endif + +#if defined(__APPLE__) || defined(__FreeBSD__) + +static int +get_local_interface_id (int family) +{ +#if !defined(HAVE_GETIFADDRS) || !defined(HAVE_IF_NAMETOINDEX) + return 0; +#else + struct ifaddrs *ifap = NULL, *ptr; + int idx = 0; + + if (getifaddrs (&ifap)) + return 0; + + for (ptr = ifap; ptr; ptr = ptr->ifa_next) { + if (!ptr->ifa_addr || !ptr->ifa_name) + continue; + if (ptr->ifa_addr->sa_family != family) + continue; + if ((ptr->ifa_flags & IFF_LOOPBACK) != 0) + continue; + if ((ptr->ifa_flags & IFF_MULTICAST) == 0) + continue; + + idx = if_nametoindex (ptr->ifa_name); + break; + } + + freeifaddrs (ifap); + return idx; +#endif +} + +#endif /* defined(__APPLE__) || defined(__FreeBSD__) */ + +void +ves_icall_System_Net_Sockets_Socket_SetSocketOption_internal (gsize sock, gint32 level, gint32 name, MonoObjectHandle obj_val, MonoArrayHandle byte_val, gint32 int_val, gint32 *werror, MonoError *error) +{ + struct linger linger; + int system_level = 0; + int system_name = 0; + int ret; + int sol_ip; + int sol_ipv6; + + error_init (error); + *werror = 0; + + sol_ipv6 = mono_networking_get_ipv6_protocol (); + sol_ip = mono_networking_get_ip_protocol (); + + ret = convert_sockopt_level_and_name ((MonoSocketOptionLevel)level, (MonoSocketOptionName)name, &system_level, + &system_name); + +#if !defined(SO_EXCLUSIVEADDRUSE) && defined(SO_REUSEADDR) + if (level == SocketOptionLevel_Socket && name == SocketOptionName_ExclusiveAddressUse) { + system_name = SO_REUSEADDR; + int_val = int_val ? 0 : 1; + ret = 0; + } +#endif + + if (ret == -1) { + *werror = WSAENOPROTOOPT; + return; + } + if (ret == -2) + return; + + /* Only one of obj_val, byte_val or int_val has data */ + if (!MONO_HANDLE_IS_NULL (obj_val)) { + MonoClass *obj_class = mono_handle_class (obj_val); + MonoClassField *field; + int valsize; + + switch (name) { + case SocketOptionName_Linger: + /* Dig out "bool enabled" and "int lingerTime" + * fields + */ + field = mono_class_get_field_from_name (obj_class, "enabled"); + linger.l_onoff = MONO_HANDLE_GET_FIELD_VAL (obj_val, guint8, field); + field = mono_class_get_field_from_name (obj_class, "lingerTime"); + linger.l_linger = MONO_HANDLE_GET_FIELD_VAL (obj_val, guint32, field); + + valsize = sizeof (linger); + ret = mono_w32socket_setsockopt (sock, system_level, system_name, &linger, valsize); + break; + case SocketOptionName_AddMembership: + case SocketOptionName_DropMembership: +#if defined(HAVE_STRUCT_IP_MREQN) || defined(HAVE_STRUCT_IP_MREQ) + { + MonoObjectHandle address = MONO_HANDLE_NEW (MonoObject, NULL); +#ifdef HAVE_STRUCT_SOCKADDR_IN6 + if (system_level == sol_ipv6) { + struct ipv6_mreq mreq6; + + /* + * Get group address + */ + field = mono_class_get_field_from_name (obj_class, "m_Group"); + g_assert (field); + MONO_HANDLE_ASSIGN (address, MONO_HANDLE_NEW_GET_FIELD (obj_val, MonoObject, field)); + + if (!MONO_HANDLE_IS_NULL (address)) + mreq6.ipv6mr_multiaddr = ipaddress_handle_to_struct_in6_addr (address); + + field = mono_class_get_field_from_name (obj_class, "m_Interface"); + mreq6.ipv6mr_interface = MONO_HANDLE_GET_FIELD_VAL (obj_val, guint64, field); + +#if defined(__APPLE__) || defined(__FreeBSD__) + /* + * Bug #5504: + * + * Mac OS Lion doesn't allow ipv6mr_interface = 0. + * + * Tests on Windows and Linux show that the multicast group is only + * joined on one NIC when interface = 0, so we simply use the interface + * id from the first non-loopback interface (this is also what + * Dns.GetHostName (string.Empty) would return). + */ + if (!mreq6.ipv6mr_interface) + mreq6.ipv6mr_interface = get_local_interface_id (AF_INET6); +#endif + + ret = mono_w32socket_setsockopt (sock, system_level, system_name, &mreq6, sizeof (mreq6)); + + break; // Don't check sol_ip + } +#endif + if (system_level == sol_ip) { +#ifdef HAVE_STRUCT_IP_MREQN + struct ip_mreqn mreq = {{0}}; +#else + struct ip_mreq mreq = {{0}}; +#endif /* HAVE_STRUCT_IP_MREQN */ + + /* + * pain! MulticastOption holds two IPAddress + * members, so I have to dig the value out of + * those :-( + */ + field = mono_class_get_field_from_name (obj_class, "group"); + MONO_HANDLE_ASSIGN (address, MONO_HANDLE_NEW_GET_FIELD (obj_val, MonoObject, field)); + + /* address might not be defined and if so, set the address to ADDR_ANY. + */ + if (!MONO_HANDLE_IS_NULL (address)) + mreq.imr_multiaddr = ipaddress_handle_to_struct_in_addr (address); + + field = mono_class_get_field_from_name (obj_class, "localAddress"); + MONO_HANDLE_ASSIGN (address, MONO_HANDLE_NEW_GET_FIELD (obj_val, MonoObject, field)); + +#ifdef HAVE_STRUCT_IP_MREQN + if (!MONO_HANDLE_IS_NULL (address)) + mreq.imr_address = ipaddress_handle_to_struct_in_addr (address); + + field = mono_class_get_field_from_name (obj_class, "ifIndex"); + mreq.imr_ifindex = MONO_HANDLE_GET_FIELD_VAL (obj_val, gint32, field); +#else + if (!MONO_HANDLE_IS_NULL (address)) + mreq.imr_interface = ipaddress_handle_to_struct_in_addr (address); +#endif /* HAVE_STRUCT_IP_MREQN */ + + ret = mono_w32socket_setsockopt (sock, system_level, system_name, &mreq, sizeof (mreq)); + } + break; + } +#endif /* HAVE_STRUCT_IP_MREQN || HAVE_STRUCT_IP_MREQ */ + default: + /* Cause an exception to be thrown */ + *werror = WSAEINVAL; + return; + } + } else if (!MONO_HANDLE_IS_NULL (byte_val)) { + int valsize = mono_array_handle_length (byte_val); + uint32_t gchandle; + guchar *buf = MONO_ARRAY_HANDLE_PIN (byte_val, guchar, 0, &gchandle); + + switch(name) { + case SocketOptionName_DontLinger: + if (valsize == 1) { + linger.l_onoff = (*buf) ? 0 : 1; + linger.l_linger = 0; + ret = mono_w32socket_setsockopt (sock, system_level, system_name, &linger, sizeof (linger)); + } else { + *werror = WSAEINVAL; + } + break; + default: + ret = mono_w32socket_setsockopt (sock, system_level, system_name, buf, valsize); + break; + } + mono_gchandle_free (gchandle); + } else { + /* ReceiveTimeout/SendTimeout get here */ + switch (name) { + case SocketOptionName_DontLinger: + linger.l_onoff = !int_val; + linger.l_linger = 0; + ret = mono_w32socket_setsockopt (sock, system_level, system_name, &linger, sizeof (linger)); + break; + case SocketOptionName_MulticastInterface: +#ifndef HOST_WIN32 +#ifdef HAVE_STRUCT_IP_MREQN + int_val = GUINT32_FROM_BE (int_val); + if ((int_val & 0xff000000) == 0) { + /* int_val is interface index */ + struct ip_mreqn mreq = {{0}}; + mreq.imr_ifindex = int_val; + ret = mono_w32socket_setsockopt (sock, system_level, system_name, (char *) &mreq, sizeof (mreq)); + break; + } + int_val = GUINT32_TO_BE (int_val); +#endif /* HAVE_STRUCT_IP_MREQN */ +#endif /* HOST_WIN32 */ + /* int_val is in_addr */ + ret = mono_w32socket_setsockopt (sock, system_level, system_name, (char *) &int_val, sizeof (int_val)); + break; + case SocketOptionName_DontFragment: +#ifdef HAVE_IP_MTU_DISCOVER + /* Fiddle with the value slightly if we're + * turning DF on + */ + if (int_val == 1) + int_val = IP_PMTUDISC_DO; + /* Fall through */ +#endif + + default: + ret = mono_w32socket_setsockopt (sock, system_level, system_name, (char *) &int_val, sizeof (int_val)); + } + } + + if (ret == SOCKET_ERROR) { + *werror = mono_w32socket_get_last_error (); + +#ifdef HAVE_IP_MTU_DISCOVER + if (system_name == IP_MTU_DISCOVER) { + switch (system_level) { + case IP_PMTUDISC_DONT: + case IP_PMTUDISC_WANT: + case IP_PMTUDISC_DO: +#ifdef IP_PMTUDISC_PROBE + case IP_PMTUDISC_PROBE: +#endif +#ifdef IP_PMTUDISC_INTERFACE + case IP_PMTUDISC_INTERFACE: +#endif +#ifdef IP_PMTUDISC_OMIT + case IP_PMTUDISC_OMIT: +#endif + /* + * This happens if HAVE_IP_MTU_DISCOVER is set but the OS + * doesn't actually understand it. The only OS that this is + * known to happen on currently is Windows Subsystem for Linux + * (newer versions have been fixed to recognize it). Just + * pretend everything is fine. + */ + ret = 0; + *werror = 0; + break; + default: + break; + } + } +#endif + } +} + +void +ves_icall_System_Net_Sockets_Socket_Shutdown_internal (gsize sock, gint32 how, gint32 *werror, MonoError *error) +{ + int ret; + gboolean interrupted; + + error_init (error); + *werror = 0; + + mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); + if (interrupted) { + *werror = WSAEINTR; + return; + } + + /* Currently, the values for how (recv=0, send=1, both=2) match the BSD API */ + ret = mono_w32socket_shutdown (sock, how); + if (ret == SOCKET_ERROR) + *werror = mono_w32socket_get_last_error (); + + mono_thread_info_uninstall_interrupt (&interrupted); + if (interrupted) { + *werror = WSAEINTR; + } + +} + +gint +ves_icall_System_Net_Sockets_Socket_IOControl_internal (gsize sock, gint32 code, MonoArrayHandle input, MonoArrayHandle output, gint32 *werror, MonoError *error) +{ + glong output_bytes = 0; + gchar *i_buffer, *o_buffer; + gint i_len, o_len; + uint32_t i_gchandle, o_gchandle; + gint ret; + + error_init (error); + *werror = 0; + + if ((guint32)code == FIONBIO) + /* Invalid command. Must use Socket.Blocking */ + return -1; + + if (MONO_HANDLE_IS_NULL (input)) { + i_buffer = NULL; + i_len = 0; + i_gchandle = 0; + } else { + i_len = mono_array_handle_length (input); + i_buffer = MONO_ARRAY_HANDLE_PIN (input, gchar, 0, &i_gchandle); + } + + if (MONO_HANDLE_IS_NULL (output)) { + o_buffer = NULL; + o_len = 0; + o_gchandle = 0; + } else { + o_len = mono_array_handle_length (output); + o_buffer = MONO_ARRAY_HANDLE_PIN (output, gchar, 0, &o_gchandle); + } + + ret = mono_w32socket_ioctl (sock, code, i_buffer, i_len, o_buffer, o_len, &output_bytes); + + if (i_gchandle) + mono_gchandle_free (i_gchandle); + if (o_gchandle) + mono_gchandle_free (o_gchandle); + + if (ret == SOCKET_ERROR) { + *werror = mono_w32socket_get_last_error (); + return -1; + } + + 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) +{ + HANDLE_FUNCTION_ENTER (); + error_init (error); + MonoStringHandle str = mono_string_new_handle (domain, s, error); + goto_if_nok (error, leave); + MONO_HANDLE_ARRAY_SETREF (arr, index, str); +leave: + HANDLE_FUNCTION_RETURN_VAL (is_ok (error)); + +} + +static int +addrinfo_add_local_ips (MonoDomain *domain, MonoArrayHandleOut h_addr_list, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + struct in_addr *local_in = NULL; + int nlocal_in = 0; + struct in6_addr *local_in6 = NULL; + int nlocal_in6 = 0; + int addr_index = 0; + + error_init (error); + local_in = (struct in_addr *) mono_get_local_interfaces (AF_INET, &nlocal_in); + local_in6 = (struct in6_addr *) mono_get_local_interfaces (AF_INET6, &nlocal_in6); + if (nlocal_in || nlocal_in6) { + char addr [INET6_ADDRSTRLEN]; + MONO_HANDLE_ASSIGN (h_addr_list, mono_array_new_handle (domain, mono_get_string_class (), nlocal_in + nlocal_in6, error)); + goto_if_nok (error, leave); + + if (nlocal_in) { + int i; + + for (i = 0; i < nlocal_in; i++) { + MonoAddress maddr; + mono_address_init (&maddr, AF_INET, &local_in [i]); + if (mono_networking_addr_to_str (&maddr, addr, sizeof (addr))) { + if (!addrinfo_add_string (domain, addr, h_addr_list, addr_index, error)) + goto leave; + addr_index++; + } + } + } +#ifdef HAVE_STRUCT_SOCKADDR_IN6 + if (nlocal_in6) { + int i; + + for (i = 0; i < nlocal_in6; i++) { + MonoAddress maddr; + mono_address_init (&maddr, AF_INET6, &local_in6 [i]); + if (mono_networking_addr_to_str (&maddr, addr, sizeof (addr))) { + if (!addrinfo_add_string (domain, addr, h_addr_list, addr_index, error)) + goto leave; + addr_index++; + } + } + } +#endif + } + +leave: + g_free (local_in); + g_free (local_in6); + HANDLE_FUNCTION_RETURN_VAL (addr_index); +} + +static gboolean +addrinfo_to_IPHostEntry_handles (MonoAddressInfo *info, MonoStringHandleOut h_name, MonoArrayHandleOut h_aliases, MonoArrayHandleOut h_addr_list, gboolean add_local_ips, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + MonoAddressEntry *ai = NULL; + MonoDomain *domain = mono_domain_get (); + + error_init (error); + MONO_HANDLE_ASSIGN (h_aliases, mono_array_new_handle (domain, mono_get_string_class (), 0, error)); + goto_if_nok (error, leave); + if (add_local_ips) { + int addr_index = addrinfo_add_local_ips (domain, h_addr_list, error); + goto_if_nok (error, leave); + if (addr_index > 0) + goto leave; + } + + gint32 count = 0; + for (ai = info->entries; ai != NULL; ai = ai->next) { + if (ai->family != AF_INET && ai->family != AF_INET6) + continue; + count++; + } + + int addr_index = 0; + MONO_HANDLE_ASSIGN (h_addr_list, mono_array_new_handle (domain, mono_get_string_class (), count, error)); + goto_if_nok (error, leave); + + gboolean name_assigned = FALSE; + for (ai = info->entries; ai != NULL; ai = ai->next) { + MonoAddress maddr; + char buffer [INET6_ADDRSTRLEN]; /* Max. size for IPv6 */ + + if ((ai->family != PF_INET) && (ai->family != PF_INET6)) + continue; + + mono_address_init (&maddr, ai->family, &ai->address); + const char *addr = NULL; + if (mono_networking_addr_to_str (&maddr, buffer, sizeof (buffer))) + addr = buffer; + else + addr = ""; + if (!addrinfo_add_string (domain, addr, h_addr_list, addr_index, error)) + goto leave; + + if (!name_assigned) { + name_assigned = TRUE; + const char *name = ai->canonical_name != NULL ? ai->canonical_name : buffer; + MONO_HANDLE_ASSIGN (h_name, mono_string_new_handle (domain, name, error)); + goto_if_nok (error, leave); + } + + addr_index++; + } + +leave: + if (info) + mono_free_address_info (info); + + HANDLE_FUNCTION_RETURN_VAL (is_ok (error)); +} + +MonoBoolean +ves_icall_System_Net_Dns_GetHostByName_internal (MonoStringHandle host, MonoStringHandleOut h_name, MonoArrayHandleOut h_aliases, MonoArrayHandleOut h_addr_list, gint32 hint, MonoError *error) +{ + gboolean add_local_ips = FALSE, add_info_ok = TRUE; + gchar this_hostname [256]; + MonoAddressInfo *info = NULL; + + error_init (error); + + char *hostname = mono_string_handle_to_utf8 (host, error); + return_val_if_nok (error, FALSE); + + if (*hostname == '\0') { + add_local_ips = TRUE; + MONO_HANDLE_ASSIGN (h_name, host); + } + + if (!add_local_ips && gethostname (this_hostname, sizeof (this_hostname)) != -1) { + if (!strcmp (hostname, this_hostname)) { + add_local_ips = TRUE; + MONO_HANDLE_ASSIGN (h_name, host); + } + } + +#ifdef HOST_WIN32 + // Win32 APIs already returns local interface addresses for empty hostname ("") + // so we never want to add them manually. + add_local_ips = FALSE; + if (mono_get_address_info(hostname, 0, MONO_HINT_CANONICAL_NAME | hint, &info)) + add_info_ok = FALSE; +#else + if (*hostname && mono_get_address_info (hostname, 0, MONO_HINT_CANONICAL_NAME | hint, &info)) + add_info_ok = FALSE; +#endif + + g_free(hostname); + + if (add_info_ok) { + MonoBoolean result = addrinfo_to_IPHostEntry_handles (info, h_name, h_aliases, h_addr_list, add_local_ips, error); + return result; + } + return FALSE; +} + +MonoBoolean +ves_icall_System_Net_Dns_GetHostByAddr_internal (MonoStringHandle addr, MonoStringHandleOut h_name, MonoArrayHandleOut h_aliases, MonoArrayHandleOut h_addr_list, gint32 hint, MonoError *error) +{ + char *address; + struct sockaddr_in saddr; +#ifdef HAVE_STRUCT_SOCKADDR_IN6 + struct sockaddr_in6 saddr6; +#endif + MonoAddressInfo *info = NULL; + gint32 family; + gchar hostname [NI_MAXHOST] = { 0 }; + gboolean ret; + + error_init (error); + + address = mono_string_handle_to_utf8 (addr, error); + return_val_if_nok (error, FALSE); + + if (inet_pton (AF_INET, address, &saddr.sin_addr ) == 1) { + family = AF_INET; + saddr.sin_family = AF_INET; + } +#ifdef HAVE_STRUCT_SOCKADDR_IN6 + else if (inet_pton (AF_INET6, address, &saddr6.sin6_addr) == 1) { + family = AF_INET6; + saddr6.sin6_family = AF_INET6; + } +#endif + else { + g_free (address); + return FALSE; + } + + g_free (address); + + switch (family) { + case AF_INET: { +#if HAVE_SOCKADDR_IN_SIN_LEN + saddr.sin_len = sizeof (saddr); +#endif + MONO_ENTER_GC_SAFE; + ret = getnameinfo ((struct sockaddr*)&saddr, sizeof (saddr), hostname, sizeof (hostname), NULL, 0, 0) == 0; + MONO_EXIT_GC_SAFE; + break; + } +#ifdef HAVE_STRUCT_SOCKADDR_IN6 + case AF_INET6: { +#if HAVE_SOCKADDR_IN6_SIN_LEN + saddr6.sin6_len = sizeof (saddr6); +#endif + MONO_ENTER_GC_SAFE; + ret = getnameinfo ((struct sockaddr*)&saddr6, sizeof (saddr6), hostname, sizeof (hostname), NULL, 0, 0) == 0; + MONO_EXIT_GC_SAFE; + break; + } +#endif + default: + g_assert_not_reached (); + } + + if (!ret) + return FALSE; + + if (mono_get_address_info (hostname, 0, hint | MONO_HINT_CANONICAL_NAME | MONO_HINT_CONFIGURED_ONLY, &info) != 0) + return FALSE; + + MonoBoolean result = addrinfo_to_IPHostEntry_handles (info, h_name, h_aliases, h_addr_list, FALSE, error); + return result; +} + +MonoBoolean +ves_icall_System_Net_Dns_GetHostName_internal (MonoStringHandleOut h_name, MonoError *error) +{ + gchar hostname [NI_MAXHOST] = { 0 }; + int ret; + + error_init (error); + MONO_ENTER_GC_SAFE; + ret = gethostname (hostname, sizeof (hostname)); + MONO_EXIT_GC_SAFE; + if (ret == -1) + return FALSE; + + MONO_HANDLE_ASSIGN (h_name, mono_string_new_handle (mono_domain_get (), hostname, error)); + return TRUE; +} + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) +gboolean +ves_icall_System_Net_Sockets_Socket_SendFile_internal (gsize sock, MonoStringHandle filename, MonoArrayHandle pre_buffer, MonoArrayHandle post_buffer, gint flags, gint32 *werror, gboolean blocking, MonoError *error) +{ + HANDLE file; + gboolean ret; + gboolean interrupted; + TRANSMIT_FILE_BUFFERS buffers; + uint32_t pre_buffer_gchandle = 0; + uint32_t post_buffer_gchandle = 0; + + error_init (error); + *werror = 0; + + if (MONO_HANDLE_IS_NULL (filename)) + return FALSE; + + /* FIXME: replace file by a proper fd that we can call open and close on, as they are interruptible */ + + uint32_t filename_gchandle; + gunichar2 *filename_chars = mono_string_handle_pin_chars (filename, &filename_gchandle); + file = mono_w32file_create (filename_chars, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, 0); + mono_gchandle_free (filename_gchandle); + if (file == INVALID_HANDLE_VALUE) { + *werror = mono_w32error_get_last (); + return FALSE; + } + + mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); + if (interrupted) { + mono_w32file_close (file); + mono_w32error_set_last (WSAEINTR); + return FALSE; + } + + + memset (&buffers, 0, sizeof (buffers)); + if (!MONO_HANDLE_IS_NULL (pre_buffer)) { + buffers.Head = MONO_ARRAY_HANDLE_PIN (pre_buffer, guchar, 0, &pre_buffer_gchandle); + buffers.HeadLength = mono_array_handle_length (pre_buffer); + } + if (!MONO_HANDLE_IS_NULL (post_buffer)) { + buffers.Tail = MONO_ARRAY_HANDLE_PIN (post_buffer, guchar, 0, &post_buffer_gchandle); + buffers.TailLength = mono_array_handle_length (post_buffer); + } + + ret = mono_w32socket_transmit_file (sock, file, &buffers, flags, blocking); + + if (pre_buffer_gchandle) + mono_gchandle_free (pre_buffer_gchandle); + if (post_buffer_gchandle) + mono_gchandle_free (post_buffer_gchandle); + + if (!ret) + *werror = mono_w32socket_get_last_error (); + + mono_thread_info_uninstall_interrupt (&interrupted); + if (interrupted) { + mono_w32file_close (file); + *werror = WSAEINTR; + return FALSE; + } + + mono_w32file_close (file); + + if (*werror) + return FALSE; + + return ret; +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */ + +void +mono_network_init (void) +{ + mono_networking_init (); + mono_w32socket_initialize (); +} + +void +mono_network_cleanup (void) +{ + mono_w32socket_cleanup (); + mono_networking_shutdown (); +} + +void +icall_cancel_blocking_socket_operation (MonoThreadObjectHandle thread, MonoError *error) +{ + error_init (error); + MonoInternalThreadHandle internal = MONO_HANDLE_NEW_GET (MonoInternalThread, thread, internal_thread); + g_assert (!MONO_HANDLE_IS_NULL (internal)); + + guint64 tid = mono_internal_thread_handle_ptr (internal)->tid; + mono_thread_info_abort_socket_syscall_for_close (MONO_UINT_TO_NATIVE_THREAD_ID (tid)); +} + +#endif /* #ifndef DISABLE_SOCKETS */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/w32socket.h b/unity-2019.4.24f1-mbe/mono/metadata/w32socket.h new file mode 100644 index 000000000..4208591c4 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/w32socket.h @@ -0,0 +1,305 @@ +/** + * \file + * System.Net.Sockets.Socket support + * + * Author: + * Dick Porter (dick@ximian.com) + * + * (C) 2001 Ximian, Inc. + */ + +#ifndef _MONO_METADATA_W32SOCKET_H_ +#define _MONO_METADATA_W32SOCKET_H_ + +#include +#include + +#include + +#ifndef HOST_WIN32 +#define INVALID_SOCKET ((SOCKET)(guint32)(~0)) +#define SOCKET_ERROR (-1) + +typedef gint SOCKET; + +typedef struct { + guint32 len; + gpointer buf; +} WSABUF, *LPWSABUF; +#endif + +/* This is a copy of System.Net.Sockets.SocketType */ +typedef enum { + SocketType_Stream=1, + SocketType_Dgram=2, + SocketType_Raw=3, + SocketType_Rdm=4, + SocketType_Seqpacket=5, + SocketType_Unknown=-1 +} MonoSocketType; + +/* This is a copy of System.Net.Sockets.AddressFamily */ +typedef enum { + AddressFamily_Unknown=-1, + AddressFamily_Unspecified=0, + AddressFamily_Unix=1, + AddressFamily_InterNetwork=2, + AddressFamily_ImpLink=3, + AddressFamily_Pup=4, + AddressFamily_Chaos=5, + AddressFamily_NS=6, + AddressFamily_Ipx=6, + AddressFamily_Iso=7, + AddressFamily_Osi=7, + AddressFamily_Ecma=8, + AddressFamily_DataKit=9, + AddressFamily_Ccitt=10, + AddressFamily_Sna=11, + AddressFamily_DecNet=12, + AddressFamily_DataLink=13, + AddressFamily_Lat=14, + AddressFamily_HyperChannel=15, + AddressFamily_AppleTalk=16, + AddressFamily_NetBios=17, + AddressFamily_VoiceView=18, + AddressFamily_FireFox=19, + AddressFamily_Banyan=21, + AddressFamily_Atm=22, + AddressFamily_InterNetworkV6=23, + AddressFamily_Cluster=24, + AddressFamily_Ieee12844=25, + AddressFamily_Irda=26, + AddressFamily_NetworkDesigners=28 +} MonoAddressFamily; + +/* This is a copy of System.Net.Sockets.ProtocolType */ +typedef enum { + ProtocolType_IP=0, + ProtocolType_Icmp=1, + ProtocolType_Igmp=2, + ProtocolType_Ggp=3, + ProtocolType_Tcp=6, + ProtocolType_Pup=12, + ProtocolType_Udp=17, + ProtocolType_Idp=22, + ProtocolType_IPv6=41, + ProtocolType_ND=77, + ProtocolType_Raw=255, + ProtocolType_Unspecified=0, + ProtocolType_Ipx=1000, + ProtocolType_Spx=1256, + ProtocolType_SpxII=1257, + ProtocolType_Unknown=-1 +} MonoProtocolType; + +/* This is a copy of System.Net.Sockets.SocketOptionLevel */ +typedef enum { + SocketOptionLevel_Socket=65535, + SocketOptionLevel_IP=0, + SocketOptionLevel_IPv6=41, + SocketOptionLevel_Tcp=6, + SocketOptionLevel_Udp=17 +} MonoSocketOptionLevel; + +/* This is a copy of System.Net.Sockets.SocketOptionName */ +typedef enum { + SocketOptionName_Debug=1, + SocketOptionName_AcceptConnection=2, + SocketOptionName_ReuseAddress=4, + SocketOptionName_KeepAlive=8, + SocketOptionName_DontRoute=16, + SocketOptionName_IPProtectionLevel = 23, + SocketOptionName_IPv6Only = 27, + SocketOptionName_Broadcast=32, + SocketOptionName_UseLoopback=64, + SocketOptionName_Linger=128, + SocketOptionName_OutOfBandInline=256, + SocketOptionName_DontLinger= -129, + SocketOptionName_ExclusiveAddressUse= -5, + SocketOptionName_SendBuffer= 4097, + SocketOptionName_ReceiveBuffer=4098, + SocketOptionName_SendLowWater=4099, + SocketOptionName_ReceiveLowWater=4100, + SocketOptionName_SendTimeout=4101, + SocketOptionName_ReceiveTimeout=4102, + SocketOptionName_Error=4103, + SocketOptionName_Type=4104, + SocketOptionName_MaxConnections=2147483647, + SocketOptionName_IPOptions=1, + SocketOptionName_HeaderIncluded=2, + SocketOptionName_TypeOfService=3, + SocketOptionName_IpTimeToLive=4, + SocketOptionName_MulticastInterface=9, + SocketOptionName_MulticastTimeToLive=10, + SocketOptionName_MulticastLoopback=11, + SocketOptionName_AddMembership=12, + SocketOptionName_DropMembership=13, + SocketOptionName_DontFragment=14, + SocketOptionName_AddSourceMembership=15, + SocketOptionName_DropSourceMembership=16, + SocketOptionName_BlockSource=17, + SocketOptionName_UnblockSource=18, + SocketOptionName_PacketInformation=19, + SocketOptionName_NoDelay=1, + SocketOptionName_BsdUrgent=2, + SocketOptionName_Expedited=2, + SocketOptionName_NoChecksum=1, + SocketOptionName_ChecksumCoverage=20, + SocketOptionName_HopLimit=21, + + /* This is Mono-specific, keep it in sync with + * Mono.Posix/PeerCred.cs + */ + SocketOptionName_PeerCred=10001 +} MonoSocketOptionName; + +/* This is a copy of System.Net.Sockets.SocketFlags */ +typedef enum { + SocketFlags_None = 0x0000, + SocketFlags_OutOfBand = 0x0001, + SocketFlags_MaxIOVectorLength = 0x0010, + SocketFlags_Peek = 0x0002, + SocketFlags_DontRoute = 0x0004, + SocketFlags_Partial = 0x8000 +} MonoSocketFlags; + +typedef struct +{ + MonoObject obj; + gint pid; + gint uid; + gint gid; +} MonoPeerCredData; + +/* Safely access Mono.Posix.PeerCredData from native code */ +TYPED_HANDLE_DECL (MonoPeerCredData); + +gpointer +ves_icall_System_Net_Sockets_Socket_Socket_internal (MonoObjectHandle this_obj, gint32 family, gint32 type, + gint32 proto, gint32 *werror, MonoError *error); + +void +ves_icall_System_Net_Sockets_Socket_Close_internal (gsize sock, gint32 *werror, MonoError *error); + +gint32 +ves_icall_System_Net_Sockets_SocketException_WSAGetLastError_internal (void); + +gint32 +ves_icall_System_Net_Sockets_Socket_Available_internal (gsize sock, gint32 *werror, MonoError *error); + +void +ves_icall_System_Net_Sockets_Socket_Blocking_internal (gsize sock, gboolean block, gint32 *werror, MonoError *error); + +gpointer +ves_icall_System_Net_Sockets_Socket_Accept_internal (gsize sock, gint32 *werror, gboolean blocking, MonoError *error); + +void +ves_icall_System_Net_Sockets_Socket_Listen_internal (gsize sock, guint32 backlog, gint32 *werror, MonoError *error); + +MonoObjectHandle +ves_icall_System_Net_Sockets_Socket_LocalEndPoint_internal (gsize sock, gint32 af, gint32 *werror, MonoError *error); + +MonoObjectHandle +ves_icall_System_Net_Sockets_Socket_RemoteEndPoint_internal (gsize sock, gint32 af, gint32 *werror, MonoError *error); + +void +ves_icall_System_Net_Sockets_Socket_Bind_internal (gsize sock, MonoObjectHandle sockaddr, gint32 *werror, MonoError *error); + +void +ves_icall_System_Net_Sockets_Socket_Connect_internal (gsize sock, MonoObjectHandle sockaddr, gint32 *werror, gboolean blocking, MonoError *error); + +gint32 +ves_icall_System_Net_Sockets_Socket_Receive_internal (gsize sock, gchar *buffer, gint32 count, + gint32 flags, gint32 *werror, gboolean blocking, MonoError *error); + +gint32 +ves_icall_System_Net_Sockets_Socket_Receive_array_internal (gsize sock, WSABUF *buffers, gint32 count, gint32 flags, + gint32 *werror, gboolean blocking, MonoError *error); + +gint32 +ves_icall_System_Net_Sockets_Socket_ReceiveFrom_internal (gsize sock, gchar *buffer, gint32 count, + gint32 flags, MonoObjectHandle sockaddr, gint32 *werror, gboolean blocking, MonoError *error); + + +gint32 +ves_icall_System_Net_Sockets_Socket_Send_internal (gsize sock, gchar *buffer, gint32 count, + gint32 flags, gint32 *werror, gboolean blocking, MonoError *error); + +gint32 +ves_icall_System_Net_Sockets_Socket_Send_array_internal (gsize sock, WSABUF *buffers, gint32 count, gint32 flags, + gint32 *werror, gboolean blocking, MonoError *error); + +gint32 +ves_icall_System_Net_Sockets_Socket_SendTo_internal (gsize sock, gchar *buffer, gint32 count, + gint32 flags, MonoObjectHandle sockaddr, gint32 *werror, + gboolean blocking, MonoError *error); + +void +ves_icall_System_Net_Sockets_Socket_Select_internal (MonoArrayHandle sockets, gint32 timeout, gint32 *werror, MonoError *error); + +void +ves_icall_System_Net_Sockets_Socket_Shutdown_internal (gsize sock, gint32 how, gint32 *werror, MonoError *error); + +void +ves_icall_System_Net_Sockets_Socket_GetSocketOption_obj_internal (gsize sock, gint32 level, gint32 name, + MonoObjectHandle obj_val, gint32 *werror, + MonoError *error); + +void +ves_icall_System_Net_Sockets_Socket_GetSocketOption_arr_internal (gsize sock, gint32 level, gint32 name, + MonoArrayHandle byte_val, gint32 *werror, + MonoError *error); + +void +ves_icall_System_Net_Sockets_Socket_SetSocketOption_internal (gsize sock, gint32 level, gint32 name, + MonoObjectHandle obj_val, MonoArrayHandle byte_val, + gint32 int_val, gint32 *werror, MonoError *error); + +int +ves_icall_System_Net_Sockets_Socket_IOControl_internal (gsize sock, gint32 code, + MonoArrayHandle input, MonoArrayHandle output, + gint32 *werror, MonoError *error); + +MonoBoolean +ves_icall_System_Net_Dns_GetHostByName_internal (MonoStringHandle host, MonoStringHandleOut h_name, + 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, + gint32 hint, MonoError *error); + +MonoBoolean +ves_icall_System_Net_Dns_GetHostName_internal (MonoStringHandleOut h_name, MonoError *error); + +MonoBoolean +ves_icall_System_Net_Sockets_Socket_Poll_internal (gsize sock, gint mode, gint timeout, gint32 *werror, MonoError *error); + +void +ves_icall_System_Net_Sockets_Socket_Disconnect_internal (gsize sock, MonoBoolean reuse, gint32 *werror, MonoError *error); + +MonoBoolean +ves_icall_System_Net_Sockets_Socket_Duplicate_internal (gpointer handle, gint32 targetProcessId, gpointer *duplicate_handle, gint32 *werror, MonoError *error); + +gboolean +ves_icall_System_Net_Sockets_Socket_SendFile_internal (gsize sock, MonoStringHandle filename, + MonoArrayHandle pre_buffer, MonoArrayHandle post_buffer, + gint flags, gint32 *werror, gboolean blocking, MonoError *error); + +void +icall_cancel_blocking_socket_operation (MonoThreadObjectHandle thread, MonoError *error); + +gboolean +ves_icall_System_Net_Sockets_Socket_SupportPortReuse (MonoProtocolType proto, MonoError *error); + +void +mono_network_init(void); + +void +mono_network_cleanup(void); + +#endif /* _MONO_METADATA_W32SOCKET_H_ */ diff --git a/unity-2019.4.24f1-mbe/mono/metadata/wrapper-types.h b/unity-2019.4.24f1-mbe/mono/metadata/wrapper-types.h new file mode 100644 index 000000000..90ac3d80a --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/metadata/wrapper-types.h @@ -0,0 +1,35 @@ +/** + * \file + * NOTE NOTE NOTE + * No additional wrapper types should be added. + * If a new wrapper is asolutely necessary, an existing one needs + * to be removed first (with all the change that implies). + */ +WRAPPER(NONE, "none") +WRAPPER(DELEGATE_INVOKE, "delegate-invoke") +WRAPPER(DELEGATE_BEGIN_INVOKE, "delegate-begin-invoke") +WRAPPER(DELEGATE_END_INVOKE, "delegate-end-invoke") +WRAPPER(RUNTIME_INVOKE, "runtime-invoke") +WRAPPER(NATIVE_TO_MANAGED, "native-to-managed") +WRAPPER(MANAGED_TO_NATIVE, "managed-to-native") +WRAPPER(MANAGED_TO_MANAGED, "managed-to-managed") +WRAPPER(REMOTING_INVOKE, "remoting-invoke") +WRAPPER(REMOTING_INVOKE_WITH_CHECK, "remoting-invoke-with-check") +WRAPPER(XDOMAIN_INVOKE, "xdomain-invoke") +WRAPPER(XDOMAIN_DISPATCH, "xdomain-dispatch") +WRAPPER(LDFLD, "ldfld") +WRAPPER(STFLD, "stfld") +WRAPPER(SYNCHRONIZED, "synchronized") +WRAPPER(DYNAMIC_METHOD, "dynamic-method") +WRAPPER(CASTCLASS, "castclass") +WRAPPER(PROXY_ISINST, "proxy_isinst") +WRAPPER(STELEMREF, "stelemref") +WRAPPER(UNBOX, "unbox") +WRAPPER(LDFLDA, "ldflda") +WRAPPER(WRITE_BARRIER, "write-barrier") +WRAPPER(UNKNOWN, "unknown") +WRAPPER(COMINTEROP_INVOKE, "cominterop-invoke") +WRAPPER(COMINTEROP, "cominterop") +WRAPPER(ALLOC, "alloc") + + diff --git a/unity-2019.4.24f1-mbe/mono/mini/.gitignore b/unity-2019.4.24f1-mbe/mono/mini/.gitignore new file mode 100644 index 000000000..0fb8914a9 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/.gitignore @@ -0,0 +1,37 @@ +/Makefile +/Makefile.in +/Makefile.am +/*.o +/*.g.c +/*.so +/*.exe +/*.dll +/*.mdb +/*.lo +/interp/*.lo +/*.loT +/*.la +/.libs +/.deps +/mini +/genmdesc +/inssel.c +/inssel.h +/mini.pc +/cpu-*.h +/mono +/monow +/.hidden +/semantic.cache +/.project +/.cproject +/iltests.il +/version.h +/buildver.h +/TAGS +/mono-sgen +/mono-boehm +/mono-bdwgc +/buildver-sgen.h +/buildver-boehm.h +/regressiontests.out diff --git a/unity-2019.4.24f1-mbe/mono/mini/Info.plist b/unity-2019.4.24f1-mbe/mono/mini/Info.plist new file mode 100644 index 000000000..4ae22bb51 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/Info.plist @@ -0,0 +1,16 @@ + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.novell.mono + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + mono + CFBundleVersion + 0.3 + SecTaskAccess + allowed + + diff --git a/unity-2019.4.24f1-mbe/mono/mini/Makefile.am.in b/unity-2019.4.24f1-mbe/mono/mini/Makefile.am.in new file mode 100644 index 000000000..535e2069d --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/Makefile.am.in @@ -0,0 +1,1021 @@ +count=100000 +mtest=for_loop +monodir=$(top_builddir) +mono=$(if $(MONO_EXECUTABLE),$(MONO_EXECUTABLE),mono) + +if HOST_WIN32 +PLATFORM_PATH_SEPARATOR=; +else +PLATFORM_PATH_SEPARATOR=: +endif + +# This is needed for automake dependency generation +if SUPPORT_NULLGC +libgc_libs= +libgc_static_libs= +else +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 \ + $(monodir)/mono/utils/libmonoutils.la \ + $(GLIB_LIBS) $(LIBICONV) + +boehm_static_libs= \ + $(monodir)/mono/metadata/libmonoruntime-static.la \ + $(monodir)/mono/utils/libmonoutils.la \ + $(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 \ + $(monodir)/mono/utils/libmonoutils.la \ + $(GLIB_LIBS) $(LIBICONV) + +if FULL_AOT_TESTS +# if the tests are going to run with framework assemblies compiled with +# -d:MOBILE, tell the runtime to remap framework assemblies using the mobile +# runtime info +MOBILE_RUNTIME_ARG=--runtime=mobile +else +MOBILE_RUNTIME_ARG= +endif + +CLASS=$(mcs_topdir)/class/lib/$(DEFAULT_PROFILE) + +RUNTIME_EXECUTABLE = $(if $(BOEHM),$(top_builddir)/mono/mini/mono-boehm,$(top_builddir)/runtime/mono-wrapper) + +MINI_RUNTIME = MONO_PATH=$(CLASS) $(RUNTIME_EXECUTABLE) $(MOBILE_RUNTIME_ARG) +TOOLS_RUNTIME = MONO_PATH=$(mcs_topdir)/class/lib/build $(top_builddir)/runtime/mono-wrapper +INTERP_RUNTIME = $(MINI_RUNTIME) --interpreter +RUNTIME_AOTCHECK = MONO_PATH="$(CLASS)$(PLATFORM_PATH_SEPARATOR)." $(RUNTIME_EXECUTABLE) + +MCS = CSC_SDK_PATH_DISABLED= $(TOOLS_RUNTIME) $(CSC) -unsafe -nowarn:0162 -nologo -noconfig -r:$(CLASS)/mscorlib.dll -r:$(CLASS)/System.dll -r:$(CLASS)/System.Core.dll +ILASM = $(TOOLS_RUNTIME) $(mcs_topdir)/class/lib/build/ilasm.exe + +AM_CFLAGS = \ + -I$(top_srcdir) \ + $(GLIB_CFLAGS) \ + $(LLVM_CFLAGS) \ + $(PLATFORM_CFLAGS) $(ARCH_CFLAGS) $(SHARED_CFLAGS) + +AM_CXXFLAGS = $(LLVM_CXXFLAGS) $(GLIB_CFLAGS) + +if HOST_WIN32 +export HOST_CC +# The mingw math.h has "extern inline" functions that dont appear in libs, so +# optimisation is required to actually inline them +PLATFORM_CFLAGS = -O +endif + +monoldflags=$(export_ldflags) +monobinldflags=$(export_ldflags) $(extra_runtime_ldflags) + +if HOST_WIN32 +libmonoldflags=-no-undefined -avoid-version -Wl,--kill-at $(monoldflags) +else +if HOST_ANDROID +libmonoldflags= -avoid-version $(monoldflags) +else +libmonoldflags=$(monoldflags) -version-info 1:0:0 +endif +endif + +if SUPPORT_SGEN +sgen_binaries = mono-sgen +sgen_libraries = libmonosgen-2.0.la +sgen_static_libraries = libmini-static.la $(sgen_static_libs) +endif + +if SUPPORT_BOEHM +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 +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) + ln -sf $< $@ + +mono.exe: mono-$(mono_bin_suffix).exe + ln -sf $< $@ + +install-exec-hook: + (cd $(DESTDIR)$(bindir) && ln -sf mono-$(mono_bin_suffix) mono) + (cd $(DESTDIR)$(libdir); shopt -s nullglob 2>/dev/null; for i in libmono$(libmono_suffix)*; do ln -sf $$i `echo $$i | sed s/$(libmono_suffix)//` ; done) +endif + +if DISABLE_EXECUTABLES +else +if HOST_WIN32 +bin_PROGRAMS = $(boehm_binaries) $(bdwgc_binaries) $(sgen_binaries) monow +else +bin_PROGRAMS = $(boehm_binaries) $(bdwgc_binaries) $(sgen_binaries) +endif +endif + +if DISABLE_EXECUTABLES +noinst_PROGRAMS = +else +noinst_PROGRAMS = mono +endif + +if DISABLE_EXECUTABLES +shared_libraries = $(boehm_libraries) $(bdwgc_libraries) $(sgen_libraries) +else +if SHARED_MONO +shared_libraries = $(boehm_libraries) $(bdwgc_libraries) $(sgen_libraries) +endif +endif + +lib_LTLIBRARIES = $(shared_libraries) + +if SHARED_MONO +mini_common_lib = libmini.la +else +mini_common_lib = +endif + +if DISABLE_EXECUTABLES +noinst_LTLIBRARIES = $(mini_common_lib) +else +noinst_LTLIBRARIES = $(mini_common_lib) libmini-static.la +endif + +if LOADED_LLVM +lib_LTLIBRARIES += libmono-llvm.la +libmono_llvm_la_SOURCES = mini-llvm.c mini-llvm-cpp.cpp llvm-jit.cpp +libmono_llvm_la_LIBADD = $(GLIB_LIBS) $(LLVM_LIBS) $(LLVM_LDFLAGS) +if HOST_DARWIN +libmono_llvm_la_LDFLAGS=-Wl,-undefined -Wl,suppress -Wl,-flat_namespace +else +libmono_llvm_la_LIBADD += $(top_builddir)/mono/mini/libmonoboehm-$(API_VER).la $(boehm_libs) +endif +endif + +mono_boehm_SOURCES = \ + main.c + +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 = \ + main-sgen.c + +mono_SOURCES = \ + main-sgen.c + +mono_sgen_CFLAGS = $(AM_CFLAGS) + +# We build this after libmono was built so it contains the date when the final +# link was done +if SUPPORT_BOEHM +if DISABLE_EXECUTABLES +buildver-boehm.h: libmini-static.la $(monodir)/mono/metadata/libmonoruntime.la +else +buildver-boehm.h: libmini-static.la $(monodir)/mono/metadata/libmonoruntime-static.la +endif + @echo "const char *build_date = \"`date`\";" > buildver-boehm.h +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 +buildver-sgen.h: libmini-static.la $(monodir)/mono/metadata/libmonoruntimesgen-static.la $(monodir)/mono/sgen/libmonosgen-static.la +endif + @echo "const char *build_date = \"`date`\";" > buildver-sgen.h +mono_sgen-main-sgen.$(OBJEXT): buildver-sgen.h +main-sgen.$(OBJEXT): buildver-sgen.h + +if DTRACE_G_REQUIRED +LIBMONO_DTRACE_OBJECT = .libs/mono-dtrace.$(OBJEXT) +if STATIC_MONO +MONO_DTRACE_OBJECT = mono-dtrace.$(OBJEXT) +else +MONO_DTRACE_OBJECT = +endif +else +MONO_DTRACE_OBJECT = +LIBMONO_DTRACE_OBJECT = +endif + +if STATIC_MONO +# Link libmono into mono statically +# 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 +LLVMMONOF= +else +LLVMMONOF=$(LLVM_LIBS) $(LLVM_LDFLAGS) +endif + +mono_boehm_LDADD = \ + $(MONO_LIB) \ + $(GLIB_LIBS) \ + $(LLVMMONOF) \ + $(LIBICONV) \ + -lm \ + $(MONO_DTRACE_OBJECT) + +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) \ + $(LLVMMONOF) \ + $(LIBICONV) \ + -lm \ + $(MONO_DTRACE_OBJECT) + +mono_sgen_LDFLAGS = $(static_flags) -export-dynamic $(monobinldflags) $(monobin_platform_ldflags) + +if BITCODE +libmonoldflags += -no-undefined +endif + +# if SUPPORT_SGEN +# +# mono_LDADD = $(mono_sgen_LDADD) +# mono_LDFLAGS = $(mono_sgen_LDFLAGS) +# +# endif + + +if DTRACE_G_REQUIRED + +mono-dtrace.$(OBJEXT): $(top_srcdir)/data/mono.d mini.lo $(monodir)/mono/metadata/libmonoruntime-static.la + DTRACE="$(DTRACE)" DTRACEFLAGS="$(DTRACEFLAGS)" AR="$(AR)" $(SHELL) $(top_srcdir)/data/dtrace-prelink.sh \ + $@ $(top_srcdir)/data/mono.d $(monodir)/mono/metadata/libmonoruntime-static.la mini.lo + +.libs/mono-dtrace.$(OBJEXT): $(top_srcdir)/data/mono.d mini.lo $(monodir)/mono/metadata/libmonoruntime.la + DTRACE="$(DTRACE)" DTRACEFLAGS="$(DTRACEFLAGS)" AR="$(AR)" $(SHELL) $(top_srcdir)/data/dtrace-prelink.sh \ + --pic $@ $(top_srcdir)/data/mono.d $(monodir)/mono/metadata/libmonoruntime.la mini.lo + +endif + +# Create monow.exe, linked for the 'windows' subsystem +if HOST_WIN32 +if SUPPORT_BOEHM +monow_LDADD = $(mono_boehm_LDADD) +monow_LDFLAGS = $(mono_boehm_LDFLAGS) -mwindows +monow_SOURCES = $(mono_boehm_SOURCES) +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 \ + exceptions-wasm.c \ + tramp-wasm.c + +x86_sources = \ + mini-x86.c \ + mini-x86.h \ + exceptions-x86.c \ + tramp-x86.c \ + mini-x86-gsharedvt.c \ + tramp-x86-gsharedvt.c + +amd64_sources = \ + mini-amd64.c \ + mini-amd64.h \ + exceptions-amd64.c \ + tramp-amd64.c \ + mini-amd64-gsharedvt.c \ + mini-amd64-gsharedvt.h \ + tramp-amd64-gsharedvt.c + +ppc_sources = \ + mini-ppc.c \ + mini-ppc.h \ + exceptions-ppc.c \ + tramp-ppc.c + +arm_sources = \ + mini-arm.c \ + mini-arm.h \ + exceptions-arm.c \ + tramp-arm.c \ + mini-arm-gsharedvt.c \ + tramp-arm-gsharedvt.c + +arm64_sources = \ + mini-arm64.c \ + mini-arm64.h \ + exceptions-arm64.c \ + tramp-arm64.c \ + mini-arm64-gsharedvt.c \ + mini-arm64-gsharedvt.h \ + tramp-arm64-gsharedvt.c + +mips_sources = \ + mini-mips.c \ + mini-mips.h \ + exceptions-mips.c \ + tramp-mips.c + +sparc_sources = \ + mini-sparc.c \ + mini-sparc.h \ + exceptions-sparc.c \ + tramp-sparc.c + +s390x_sources = \ + mini-s390x.c \ + mini-s390x.h \ + support-s390x.h \ + exceptions-s390x.c \ + tramp-s390x.c + +darwin_sources = \ + mini-darwin.c + +windows_sources = \ + mini-windows.c \ + mini-windows.h \ + mini-windows-dllmain.c \ + mini-windows-dlldac.c + +posix_sources = \ + mini-posix.c + +if ENABLE_LLVM +if LOADED_LLVM +llvm_sources = \ + mini-llvm-loaded.c +else +llvm_sources = \ + mini-llvm.c \ + mini-llvm-loaded.c \ + mini-llvm-cpp.cpp \ + llvm-jit.cpp +endif +endif + +if DISABLE_INTERPRETER +interp_sources = \ + interp/interp.h +else +interp_sources = \ + interp/hacks.h \ + interp/interp.h \ + interp/interp-internals.h \ + interp/interp.c \ + interp/mintops.h \ + interp/mintops.def \ + interp/mintops.c \ + interp/transform.c +endif + +if ENABLE_LLVM +llvm_runtime_sources = \ + llvm-runtime.cpp +else +if ENABLE_LLVM_RUNTIME +llvm_runtime_sources = \ + llvm-runtime.cpp +endif +endif + +common_sources = \ + mini.c \ + mini-runtime.c \ + seq-points.c \ + seq-points.h \ + ir-emit.h \ + method-to-ir.c \ + cfgdump.h \ + cfgdump.c \ + decompose.c \ + mini.h \ + version.h \ + optflags-def.h \ + jit-icalls.h \ + jit-icalls.c \ + trace.c \ + trace.h \ + patch-info.h \ + mini-ops.h \ + mini-arch.h \ + dominators.c \ + cfold.c \ + regalloc.h \ + helpers.c \ + liveness.c \ + ssa.c \ + abcremoval.c \ + abcremoval.h \ + local-propagation.c \ + driver.c \ + debug-mini.c \ + linear-scan.c \ + aot-compiler.h \ + aot-compiler.c \ + aot-runtime.c \ + aot-runtime-wasm.c \ + wasm_m2n_invoke.g.h \ + graph.c \ + mini-codegen.c \ + mini-exceptions.c \ + mini-trampolines.c \ + branch-opts.c \ + mini-generic-sharing.c \ + simd-methods.h \ + tasklets.c \ + tasklets.h \ + simd-intrinsics.c \ + mini-native-types.c \ + mini-unwind.h \ + unwind.c \ + image-writer.h \ + image-writer.c \ + dwarfwriter.h \ + dwarfwriter.c \ + mini-gc.h \ + mini-gc.c \ + debugger-agent.h \ + debugger-agent.c \ + xdebug.c \ + mini-llvm.h \ + mini-llvm-cpp.h \ + llvm-jit.h \ + alias-analysis.c \ + mini-cross-helpers.c \ + arch-stubs.c \ + llvm-runtime.h \ + type-checking.c \ + lldb.h \ + lldb.c \ + mixed_callstack_plugin.h \ + mixed_callstack_plugin.c \ + memory-access.c \ + mini-profiler.c \ + interp-stubs.c \ + aot-runtime.h \ + mini-runtime.h + +test_sources = \ + basic-calls.cs \ + basic-long.cs \ + bench.cs \ + builtin-types.cs \ + objects.cs \ + arrays.cs \ + basic-float.cs \ + basic-math.cs \ + basic.cs \ + exceptions.cs \ + devirtualization.cs \ + iltests.il \ + test.cs \ + generics.cs \ + generics-variant-types.il\ + basic-simd.cs \ + basic-vectors.cs \ + aot-tests.cs \ + gc-test.cs \ + gshared.cs \ + unaligned.cs \ + MemoryIntrinsics.il \ + mixed.cs \ + ratests.cs + +regtests_UNIVERSAL = \ + aot-tests.exe \ + basic.exe \ + basic-float.exe \ + basic-long.exe \ + basic-calls.exe \ + builtin-types.exe \ + gshared.exe \ + objects.exe \ + arrays.exe \ + basic-math.exe \ + exceptions.exe \ + iltests.exe \ + devirtualization.exe \ + generics.exe \ + basic-simd.exe \ + unaligned.exe \ + basic-vectors.exe \ + ratests.exe + +regtests_DISABLED = + +if FULL_AOT_TESTS +regtests_DISABLED += builtin-types.exe +endif + +regtests = $(filter-out $(regtests_DISABLED),$(regtests_UNIVERSAL)) + +if WASM +arch_sources = $(wasm_sources) +arch_built=cpu-wasm.h +arch_define=__wasm__ +target_define=TARGET_WASM +endif + +if X86 +arch_sources = $(x86_sources) +arch_built=cpu-x86.h +arch_define=__i386__ +target_define=TARGET_X86 +endif + +if AMD64 +arch_sources = $(amd64_sources) +arch_built=cpu-amd64.h +arch_define=__x86_64__ +target_define=TARGET_AMD64 +ARCH_FULLAOT_EXCLUDE= +endif + +if POWERPC +arch_sources = $(ppc_sources) +arch_built=cpu-ppc.h +arch_define=__ppc__ +target_define=TARGET_POWERPC +endif + +if POWERPC64 +arch_sources = $(ppc_sources) +arch_built=cpu-ppc64.h +arch_define=__ppc64__ +target_define=TARGET_POWERPC +endif + +if MIPS +arch_sources = $(mips_sources) +arch_built=cpu-mips.h +arch_define=__mips__ +target_define=TARGET_MIPS +endif + +if ARM +# pick up arm_dpimacros.h +ARCH_CFLAGS = -I../arch/arm +arch_sources = $(arm_sources) +arch_built=cpu-arm.h +arch_define=__arm__ +target_define=TARGET_ARM +endif + +if ARM64 +arch_sources = $(arm64_sources) +arch_built=cpu-arm64.h +arch_define=__aarch64__ +target_define=TARGET_ARM64 +endif + +if SPARC +arch_sources = $(sparc_sources) +arch_built=cpu-sparc.h +arch_define=__sparc__ +target_define=TARGET_SPARC +endif + +if SPARC64 +arch_sources = $(sparc_sources) +arch_built=cpu-sparc.h +arch_define=__sparc__ +target_define=TARGET_SPARC +endif + +if S390X +arch_sources = $(s390x_sources) +arch_built=cpu-s390x.h +arch_define=__s390__ +target_define=TARGET_S390X +endif + +if HOST_WIN32 +os_sources = $(windows_sources) +monobin_platform_ldflags= +endif + +if HOST_SIGPOSIX +os_sources = $(posix_sources) +monobin_platform_ldflags= +endif + +if HOST_DARWIN +os_sources = $(darwin_sources) $(posix_sources) +#monobin_platform_ldflags=-sectcreate __TEXT __info_plist $(top_srcdir)/mono/mini/Info.plist -framework CoreFoundation -framework Foundation +monobin_platform_ldflags=-framework CoreFoundation -framework Foundation +endif + +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 = +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) +libmonosgen_2_0_la_LDFLAGS = $(libmonoldflags) $(monobin_platform_ldflags) + +# +# This library is shared between mono and mono-sgen, since the code in mini/ doesn't contain +# compile time dependencies on boehm/sgen. +# +libmini_static_la_SOURCES = $(libmini_la_SOURCES) +libmini_static_la_CFLAGS = $(AM_CFLAGS) +libmini_static_la_LDFLAGS = -static +libmini_static_la_LIBADD = $(MONO_DTRACE_OBJECT) + +libmonoincludedir = $(includedir)/mono-$(API_VER)/mono/jit + +libmonoinclude_HEADERS = jit.h + +CSFLAGS = -unsafe -nowarn:0219,0169,0414,0649,0618 + +basic-simd.exe: basic-simd.cs TestDriver.dll + $(MCS) -out:$@ $(CSFLAGS) $< -r:TestDriver.dll -r:$(CLASS)/Mono.Simd.dll + +basic-vectors.exe: basic-vectors.cs TestDriver.dll + $(MCS) -out:$@ $(CSFLAGS) $< -r:TestDriver.dll -r:$(CLASS)/System.Numerics.dll -r:$(CLASS)/System.Numerics.Vectors.dll + +builtin-types.exe: builtin-types.cs TestDriver.dll + $(MCS) -out:$@ $(CSFLAGS) -define:ARCH_$(shell echo $$((8 * $(SIZEOF_VOID_P)))) $< -r:TestDriver.dll + +nacl.exe: nacl.cs TestDriver.dll + $(MCS) -out:$@ $(CSFLAGS) $< -r:TestDriver.dll -r:$(CLASS)/Mono.Simd.dll + +generics.exe: generics.cs TestDriver.dll generics-variant-types.dll + $(MCS) -out:$@ $(CSFLAGS) $< -r:TestDriver.dll -r:generics-variant-types.dll -r:$(CLASS)/System.Core.dll + +unaligned.exe: unaligned.cs TestDriver.dll MemoryIntrinsics.dll + $(MCS) -out:$@ $(CSFLAGS) $< -r:TestDriver.dll -r:MemoryIntrinsics.dll + +%.exe: %.cs TestDriver.dll + $(MCS) -out:$@ $(CSFLAGS) $< -r:TestDriver.dll + +%.exe: %.il + $(ILASM) -output=$@ $< + +TestDriver.dll: $(srcdir)/TestDriver.cs $(srcdir)/TestHelpers.cs + $(MCS) -out:$@ -target:library $^ + +generics-variant-types.dll: generics-variant-types.il + $(ILASM) -dll -output=$@ $< + +MemoryIntrinsics.dll: MemoryIntrinsics.il + $(ILASM) -dll -output=$@ $< + +GENMDESC_OPTS= + +GENMDESC_PRG=python $(srcdir)/genmdesc.py $(target_define) $(srcdir) + +cpu-wasm.h: cpu-wasm.md + $(GENMDESC_PRG) cpu-wasm.h wasm_desc $(srcdir)/cpu-wasm.md + +cpu-x86.h: cpu-x86.md + $(GENMDESC_PRG) cpu-x86.h x86_desc $(srcdir)/cpu-x86.md + +cpu-amd64.h: cpu-amd64.md + $(GENMDESC_PRG) cpu-amd64.h amd64_desc $(srcdir)/cpu-amd64.md + +cpu-ppc.h: cpu-ppc.md + $(GENMDESC_PRG) cpu-ppc.h ppcg4 $(srcdir)/cpu-ppc.md + +cpu-ppc64.h: cpu-ppc64.md + $(GENMDESC_PRG) cpu-ppc64.h ppc64_cpu_desc $(srcdir)/cpu-ppc64.md + +cpu-arm.h: cpu-arm.md + $(GENMDESC_PRG) cpu-arm.h arm_cpu_desc $(srcdir)/cpu-arm.md + +cpu-arm64.h: cpu-arm64.md + $(GENMDESC_PRG) cpu-arm64.h arm64_cpu_desc $(srcdir)/cpu-arm64.md + +cpu-sparc.h: cpu-sparc.md + $(GENMDESC_PRG) cpu-sparc.h sparc_desc $(srcdir)/cpu-sparc.md + +cpu-s390x.h: cpu-s390x.md + $(GENMDESC_PRG) cpu-s390x.h s390x_cpu_desc $(srcdir)/cpu-s390x.md + +cpu-mips.h: cpu-mips.md + $(GENMDESC_PRG) cpu-mips.h mips_desc $(srcdir)/cpu-mips.md + +testi: mono test.exe + $(MINI_RUNTIME) -v -v --ncompile 1 --compile Test:$(mtest) test.exe + +# ensure the tests are actually correct +checktests: $(regtests) + for i in $(regtests); do $(MINI_RUNTIME) $$i; done + +rcheck-nunit: mono $(regtests) + $(MINI_RUNTIME) --regression $(regtests) > regressiontests.out 2>&1; cat regressiontests.out; \ + if grep -q "100% pass" regressiontests.out; then successbool=True; failurescount=0; else successbool=False; failurescount=1; fi; \ + echo "\ + \ + \ + " > TestResult-regression.xml; \ + if [ "$$successbool" = "False" ]; then echo "" >> TestResult-regression.xml; fi; \ + echo "\ + \ + " >> TestResult-regression.xml; exit $$failurescount + +rcheck: mono $(regtests) + $(MINI_RUNTIME) --regression $(regtests) + +richeck: mono $(regtests) + $(INTERP_RUNTIME) --regression $(regtests) + +mixedcheck: mono mixed.exe + $(MINI_RUNTIME) --interp=jit=JitClass mixed.exe + +if ARM +check-seq-points: +else +check-seq-points: mono $(regtests) + rm -f TestResult-op_il_seq_point.xml + for i in $(regtests); do $(srcdir)/test_op_il_seq_point.sh $$i || ($(srcdir)/test_op_il_seq_point_headerfooter.sh; exit 1) || exit 1; done + for i in $(regtests); do $(srcdir)/test_op_il_seq_point.sh $$i --aot || ($(srcdir)/test_op_il_seq_point_headerfooter.sh; exit 1) || exit 1; done + $(srcdir)/test_op_il_seq_point_headerfooter.sh +endif + +gctest: mono gc-test.exe + MONO_DEBUG_OPTIONS=clear-nursery-at-gc $(MINI_RUNTIME) --regression gc-test.exe + +LLVM_AOT_RUNTIME_OPTS=$(if $(LLVM),--llvm,) +GSHAREDVT_RUNTIME_OPTS=$(if $(GSHAREDVT),-O=gsharedvt,) + +aotcheck: mono $(regtests) + rm -rf *.exe.so *.exe.dylib *.exe.dylib.dSYM + $(MINI_RUNTIME) $(LLVM_AOT_RUNTIME_OPTS) --aot $(regtests) || exit 1 + for i in $(regtests); do $(RUNTIME_AOTCHECK) --regression $$i || exit 1; done + rm -rf *.exe.so *.exe.dylib *.exe.dylib.dSYM + +llvmaotcheck: + $(MAKE) aotcheck LLVM=1 + +gsharedvtcheck: + $(MAKE) fullaotcheck GSHAREDVT=1 + +fullaot_regtests = $(regtests) +fullaot_testing_deps = generics-variant-types.dll TestDriver.dll MemoryIntrinsics.dll + +FULLAOT_LIBS_UNIVERSAL = \ + mscorlib.dll \ + System.Core.dll \ + System.dll \ + System.Xml.dll \ + System.Security.dll \ + Mono.Simd.dll \ + Mono.Security.dll \ + System.Numerics.dll \ + System.Numerics.Vectors.dll \ + Mono.Posix.dll \ + System.Configuration.dll + +FULLAOT_LIBS_DISABLED = + +if FULL_AOT_TESTS +# Skip aoting the tests that aren't compiled +# on the full aot profiles because they're skipped +# on mobile profiles +FULLAOT_LIBS_DISABLED += \ + Mono.Posix.dll \ + System.Configuration.dll +endif + + +FULLAOT_LIBS = $(filter-out $(FULLAOT_LIBS_DISABLED),$(FULLAOT_LIBS_UNIVERSAL)) + +FULLAOT_TMP_DIR=$(top_builddir)/mono/mini/fullaot-tmp + +# This currently only works on amd64/arm +fullaotcheck: $(mono) $(fullaot_regtests) $(fullaot_testing_deps) + rm -rf $(FULLAOT_TMP_DIR) + mkdir $(FULLAOT_TMP_DIR) + $(MAKE) fullaot-libs AOT_FLAGS="full,$(MONO_FULLAOT_ADDITIONAL_ARGS)$(INVARIANT_AOT_OPTIONS)" GSHAREDVT=$(GSHAREDVT) + cp $(regtests) $(fullaot_regtests) $(fullaot_testing_deps) $(FULLAOT_TMP_DIR)/ + MONO_PATH=$(FULLAOT_TMP_DIR) $(top_builddir)/runtime/mono-wrapper $(MOBILE_RUNTIME_ARG) $(LLVM_AOT_RUNTIME_OPTS) $(GSHAREDVT_RUNTIME_OPTS) --aot="full,$(MONO_FULLAOT_ADDITIONAL_ARGS)$(INVARIANT_AOT_OPTIONS)" $(FULLAOT_TMP_DIR)/{*.dll,*.exe} || exit 1 + ln -s $(if $(MONO_EXECUTABLE),$(MONO_EXECUTABLE),$$PWD/mono) $(FULLAOT_TMP_DIR)/ + for i in $(fullaot_regtests); do echo $$i; MONO_PATH=$(FULLAOT_TMP_DIR) $(top_builddir)/runtime/mono-wrapper $(MOBILE_RUNTIME_ARG) --full-aot $(FULLAOT_TMP_DIR)/$$i --exclude '!FULLAOT' $(ARCH_FULLAOT_EXCLUDE) || exit 1; done + +# This can run in parallel +fullaot-libs: $(patsubst %,fullaot-tmp/%.dylib,$(FULLAOT_LIBS)) + +fullaot-tmp/%.dylib: $(CLASS)/% + cp $(CLASS)/$* fullaot-tmp/ + mkdir fullaot-tmp/$*-tmp + MONO_PATH="fullaot-tmp/$(PLATFORM_PATH_SEPARATOR)$(CLASS)" $(top_builddir)/runtime/mono-wrapper $(MOBILE_RUNTIME_ARG) $(if $(GSHAREDVT),-O=gsharedvt) --aot=$(AOT_FLAGS),temp-path=fullaot-tmp/$*-tmp fullaot-tmp/$* + rm -rf fullaot-tmp/$*-tmp + +llvmfullaotcheck: + $(MAKE) fullaotcheck LLVM=1 + +llvmonly_regtests = $(fullaot_regtests) gshared.exe + +llvmonlycheck: mono $(llvmonly_regtests) + rm -rf fullaot-tmp + mkdir fullaot-tmp + $(MAKE) fullaot-libs AOT_FLAGS="llvmonly,$(MONO_FULLAOT_ADDITIONAL_ARGS)$(INVARIANT_AOT_OPTIONS)" + cp $(llvmonly_regtests) $(fullaot_testing_deps) fullaot-tmp/ + MONO_PATH=fullaot-tmp $(top_builddir)/runtime/mono-wrapper $(MOBILE_RUNTIME_ARG) --aot=llvmonly fullaot-tmp/{*.dll,*.exe} || exit 1 + ln -s $$PWD/mono fullaot-tmp/ + for i in $(llvmonly_regtests); do echo $$i; MONO_PATH=fullaot-tmp $(top_builddir)/runtime/mono-wrapper $(MOBILE_RUNTIME_ARG) --llvmonly fullaot-tmp/$$i --exclude '!BITCODE' || exit 1; done + +gccheck: gc-test.exe + MONO_GC_PARAMS=stack-mark=precise MONO_GC_DEBUG=clear-at-gc ./mono-sgen gc-test.exe + +bench: mono test.exe + time env $(MINI_RUNTIME) --ncompile $(count) --compile Test:$(mtest) test.exe + +mbench: test.exe + time $(monodir)/mono/jit/mono --ncompile $(count) --compile Test:$(mtest) test.exe + +stat1: mono bench.exe + $(MINI_RUNTIME) --verbose --statfile stats.pl --regression bench.exe + perl viewstat.pl stats.pl + +stat2: mono basic.exe + $(MINI_RUNTIME) --verbose --statfile stats.pl --regression basic.exe + perl viewstat.pl -e stats.pl + +stat3: mono bench.exe + $(MINI_RUNTIME) --statfile stats.pl --ncompile 1000 --compile Tests:test_0_many_nested_loops bench.exe + perl viewstat.pl stats.pl + +docu: mini.sgm + docbook2txt mini.sgm + +# We need these because automake can't process normal make conditionals +check_local_targets = $(if $(EMIT_NUNIT), rcheck-nunit, rcheck) + +check-local: $(check_local_targets) + +clean-local: + rm -f mono a.out gmon.out *.o buildver-boehm.h buildver-sgen.h test.exe regressionexitcode.out TestResult-op_il_seq_point.xml* + +pkgconfigdir = $(libdir)/pkgconfig + +BUILT_SOURCES = version.h $(arch_built) + +CLEANFILES= $(BUILT_SOURCES) *.exe *.dll +EXTRA_DIST = TestDriver.cs \ + TestHelpers.cs \ + genmdesc.py \ + $(test_sources) \ + $(wasm_sources) cpu-wasm.md \ + $(x86_sources) cpu-x86.md \ + $(amd64_sources) cpu-amd64.md \ + $(ppc_sources) cpu-ppc.md cpu-ppc64.md \ + $(arm_sources) cpu-arm.md \ + $(arm64_sources) cpu-arm64.md \ + $(mips_sources) cpu-mips.md \ + $(sparc_sources) cpu-sparc.md \ + $(s390x_sources) cpu-s390x.md \ + $(windows_sources) \ + $(darwin_sources) Info.plist \ + $(posix_sources) \ + test_op_il_seq_point.sh \ + test_op_il_seq_point_headerfooter.sh \ + Makefile.am.in + +version.h: Makefile + if test -d $(top_srcdir)/.git; then \ + (cd $(top_srcdir); \ + LANG=C; export LANG; \ + if test -z "$$ghprbPullId"; then \ + branch=`git branch | grep '^\*' | sed 's/(detached from .*/explicit/' | cut -d ' ' -f 2`; \ + else \ + branch="pull-request-$$ghprbPullId"; \ + fi; \ + version=`git log --no-color --first-parent -n1 --pretty=format:%h`; \ + echo "#define FULL_VERSION \"$$branch/$$version\""; \ + ); \ + else \ + echo "#define FULL_VERSION \"tarball\""; \ + fi > version.h + +# Utility target for patching libtool to speed up linking +patch-libtool: + sed -e 's,if (for obj in $$oldobjs,if (for obj in "",g' < ../../libtool > 2; mv 2 ../../libtool + chmod a+x ../../libtool + +# Utility target for patching libtool to get rid of the 'ranlib: file has no symbols" warnings +patch-osx-libtool: + sed -e 's/old_archive_cmds=.*/old_archive_cmds="libtool -no_warning_for_no_symbols -static -o \\$$oldlib \\$$oldobjs"/g' < ../../libtool > 2; mv 2 ../../libtool + chmod a+x ../../libtool + +# Utility target to patch automake to generate the same format silent output as the old mono silent build did +patch-automake: + src="@echo \" '. \$$name . ' ' x (8 - length (\$$name)) . '\""; dst="@echo \"'. \$$name . ' ' x (7 - length (\$$name)) .'\""; sed -e "s/$$src/$$dst/g" < $$EXE > 2 && cp 2 $$EXE && rm -f 2 + +tags: + etags -o TAGS `find .. -name "*.h" -o -name "*.c"` + +if HAS_EXTENSION_MODULE +else +Makefile.am: Makefile.am.in + cp $< $@ +endif diff --git a/unity-2019.4.24f1-mbe/mono/mini/MemoryIntrinsics.il b/unity-2019.4.24f1-mbe/mono/mini/MemoryIntrinsics.il new file mode 100644 index 000000000..dd4c9b5d4 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/MemoryIntrinsics.il @@ -0,0 +1,180 @@ +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) + .ver 4:0:0:0 +} + +.assembly 'MemoryIntrinsics' +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module 'instrics-lib.dll' +.imagebase 0x00400000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 +.corflags 0x00000001 + +.namespace Mono { + .class public abstract auto ansi sealed beforefieldinit Intrinsics extends [mscorlib]System.Object + { + .method public hidebysig static void Cpobj(void* 'to', void* from) cil managed + { + ldarg.0 + ldarg.1 + cpobj !!T + ret + } + + .method public hidebysig static !!T Ldobj(void* 'from') cil managed + { + ldarg.0 + ldobj !!T + ret + } + + .method public hidebysig static void Stobj(void* 'to', !!T 'value') cil managed + { + ldarg.0 + ldarg.1 + stobj !!T + ret + } + + .method public hidebysig static void LdobjStObjPair(void* 'to', void* 'from') cil managed + { + ldarg.0 + ldarg.1 + ldobj !!T + stobj !!T + ret + } + + .method public hidebysig static void Cpblk(void* 'to', void* 'from', int32 size) cil managed + { + ldarg.0 + ldarg.1 + ldarg.2 + cpblk + ret + } + + .method public hidebysig static void Initblk(void* 'to', int32 'value', int32 'size') cil managed + { + ldarg.0 + ldarg.1 + ldarg.2 + initblk + ret + } + + //Unaligned intrinsics + .method public hidebysig static void UnalignedCpobj(void* 'to', void* from) cil managed + { + ldarg.0 + ldarg.1 + unaligned. 1 + cpobj !!T + ret + } + + .method public hidebysig static !!T UnalignedLdobj(void* 'from') cil managed + { + ldarg.0 + unaligned. 1 + ldobj !!T + ret + } + + .method public hidebysig static void UnalignedStobj(void* 'to', !!T 'value') cil managed + { + ldarg.0 + ldarg.1 + unaligned. 1 + stobj !!T + ret + } + + .method public hidebysig static void UnalignedLdobjStObjPair(void* 'to', void* 'from') cil managed + { + ldarg.0 + ldarg.1 + unaligned. 1 + ldobj !!T + stobj !!T + ret + } + + .method public hidebysig static void UnalignedCpblk(void* 'to', void* 'from', int32 size) cil managed + { + ldarg.0 + ldarg.1 + ldarg.2 + unaligned. 1 + cpblk + ret + } + + .method public hidebysig static void UnalignedInit(void* 'to', int32 'value', int32 'size') cil managed + { + ldarg.0 + ldarg.1 + ldarg.2 + unaligned. 1 + initblk + ret + } + + //Unaligned ldind + .method public hidebysig static int16 UnalignedLdInd2(void* 'from') cil managed + { + ldarg.0 + unaligned. 1 + ldind.i2 + ret + } + + .method public hidebysig static int32 UnalignedLdInd4(void* 'from') cil managed + { + ldarg.0 + unaligned. 1 + ldind.i4 + ret + } + + .method public hidebysig static int64 UnalignedLdInd8(void* 'from') cil managed + { + ldarg.0 + unaligned. 1 + ldind.i8 + ret + } + + .method public hidebysig static float32 UnalignedLdIndR4(void* 'from') cil managed + { + ldarg.0 + unaligned. 1 + ldind.r4 + ret + } + + .method public hidebysig static float64 UnalignedLdIndR8(void* 'from') cil managed + { + ldarg.0 + unaligned. 1 + ldind.r8 + ret + } + + .method public hidebysig static native int UnalignedLdIndI(void* 'from') cil managed + { + ldarg.0 + unaligned. 1 + ldind.i + ret + } + + } +} diff --git a/unity-2019.4.24f1-mbe/mono/mini/TestDriver.cs b/unity-2019.4.24f1-mbe/mono/mini/TestDriver.cs new file mode 100644 index 000000000..b17cb654f --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/TestDriver.cs @@ -0,0 +1,193 @@ +using System; +using System.Reflection; +using System.Collections.Generic; + +[AttributeUsageAttribute(AttributeTargets.All, Inherited = true, AllowMultiple = true)] +public class CategoryAttribute : Attribute +{ + public CategoryAttribute (string category) { + Category = category; + } + + public string Category { + get; set; + } +} +public class TestDriverReporter +{ + public int FailedTests { get; private set; } + public int SkippedTests { get; private set; } + public int ExecutedTests { get; private set; } + + public void ReportResults (int executed, int skipped, int failed) { + ExecutedTests = executed; + SkippedTests = skipped; + FailedTests = failed; + } +}; + +public class TestDriver { + + static public int RunTests(Type type, string[] args, TestDriverReporter reporter) { + int failed = 0, ran = 0; + int result, expected; + int i, j, iterations; + string name; + MethodInfo[] methods; + bool do_timings = false; + bool verbose = false; + bool quiet = false; + int tms = 0; + DateTime start, end = DateTime.Now; + + iterations = 1; + + var exclude = new Dictionary (); + List run_only = new List (); + List exclude_test = new List (); + if (args != null && args.Length > 0) { + for (j = 0; j < args.Length;) { + if (args [j] == "--time") { + do_timings = !quiet; + j ++; + } else if (args [j] == "--iter") { + iterations = Int32.Parse (args [j + 1]); + j += 2; + } else if ((args [j] == "-v") || (args [j] == "--verbose")) { + verbose = !quiet; + j += 1; + } else if ((args [j] == "-q") || (args [j] == "--quiet")) { + quiet = true; + verbose = false; + do_timings = false; + j += 1; + } else if (args [j] == "--exclude") { + exclude [args [j + 1]] = args [j + 1]; + j += 2; + } else if (args [j] == "--exclude-test") { + exclude_test.Add (args [j + 1]); + j += 2; + } else if (args [j] == "--run-only") { + run_only.Add (args [j + 1]); + j += 2; + } else { + Console.WriteLine ("Unknown argument: " + args [j]); + return 1; + } + } + } + int nskipped = 0; + methods = type.GetMethods (BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Static); + for (int iter = 0; iter < iterations; ++iter) { + for (i = 0; i < methods.Length; ++i) { + name = methods [i].Name; + if (!name.StartsWith ("test_", StringComparison.Ordinal)) + continue; + if (run_only.Count > 0) { + bool found = false; + for (j = 0; j < run_only.Count; j++) { + if (name.EndsWith (run_only [j])) { + found = true; + break; + } + } + if (!found) + continue; + } + if (exclude.Count > 0 || exclude_test.Count > 0) { + var attrs = methods [i].GetCustomAttributes (typeof (CategoryAttribute), false); + bool skip = false; + for (j = 0; j < exclude_test.Count; j++) { + if (name.EndsWith (exclude_test [j])) { + skip = true; + break; + } + } + foreach (CategoryAttribute attr in attrs) { + if (exclude.ContainsKey (attr.Category)) + skip = true; + } + if (skip) { + if (verbose) + Console.WriteLine ("Skipping '{0}'.", name); + nskipped ++; + continue; + } + } + for (j = 5; j < name.Length; ++j) + if (!Char.IsDigit (name [j])) + break; + if (verbose) + Console.WriteLine ("Running '{0}' ...", name); + expected = Int32.Parse (name.Substring (5, j - 5)); + start = DateTime.Now; + result = (int)methods [i].Invoke (null, null); + if (do_timings) { + end = DateTime.Now; + long tdiff = end.Ticks - start.Ticks; + int mdiff = (int)tdiff/10000; + tms += mdiff; + Console.WriteLine ("{0} took {1} ms", name, mdiff); + } + ran++; + if (result != expected) { + failed++; + Console.WriteLine ("{0} failed: got {1}, expected {2}", name, result, expected); + } + } + + if (!quiet) { + if (do_timings) { + Console.WriteLine ("Total ms: {0}", tms); + } + if (nskipped > 0) + Console.WriteLine ("Regression tests: {0} ran, {1} skipped, {2} failed in {3}", ran, nskipped, failed, type); + else + Console.WriteLine ("Regression tests: {0} ran, {1} failed in {2}", ran, failed, type); + } + } + + if (reporter != null) { + reporter.ReportResults (ran, nskipped, failed); + } + + //Console.WriteLine ("Regression tests: {0} ran, {1} failed in [{2}]{3}", ran, failed, type.Assembly.GetName().Name, type); + return failed; + } + + static public int RunTests (Type type, string[] args) { + return RunTests (type, args, null); + } + + static public int RunTests (Type type) { + return RunTests (type, null, null); + } +} + +/// Provide tests with the ability to find out how much time they have to run before being timed out. +public class TestTimeout +{ + private TimeSpan Timeout { get; } + + private DateTime StartTime { get; } + + public bool HaveTimeLeft { get { return DateTime.UtcNow - StartTime < Timeout; } } + + public static bool IsStressTest { get { return Environment.GetEnvironmentVariable("MONO_TESTS_STRESS") == "1"; } } + + private TestTimeout (TimeSpan timeout) + { + Timeout = timeout; + StartTime = DateTime.UtcNow; + } + + public static TestTimeout Start(TimeSpan timeout) + { + if (timeout.Ticks < 0) + { + throw new ArgumentException("timeout"); + } + + return new TestTimeout(timeout); + } +} diff --git a/unity-2019.4.24f1-mbe/mono/mini/TestHelpers.cs b/unity-2019.4.24f1-mbe/mono/mini/TestHelpers.cs new file mode 100644 index 000000000..26f8d1276 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/TestHelpers.cs @@ -0,0 +1,50 @@ +using System; +using System.Threading; +using System.Reflection; + +namespace MonoTests.Helpers { + + public static class FinalizerHelpers { + private static IntPtr aptr; + + private static unsafe void NoPinActionHelper (int depth, Action act) + { + // Avoid tail calls + int* values = stackalloc int [20]; + aptr = new IntPtr (values); + + if (depth <= 0) + act (); + else + NoPinActionHelper (depth - 1, act); + } + + public static void PerformNoPinAction (Action act) + { + Thread thr = new Thread (() => NoPinActionHelper (1024, act)); + thr.Start (); + thr.Join (); + } + } + + public static class OOMHelpers { + public static void RunTest (string test) + { + Assembly asm = Assembly.Load (test); + + try { + // Support both (void) and (string[]) signatures + if (asm.EntryPoint.GetParameters ().Length == 1) + asm.EntryPoint.Invoke (null, new string[] { null }); + else + asm.EntryPoint.Invoke (null, null); + } catch (TargetInvocationException e) { + if (e.InnerException is OutOfMemoryException) + Console.WriteLine ("Catched oom"); + else + throw; + } + } + } +} + diff --git a/unity-2019.4.24f1-mbe/mono/mini/abcremoval.c b/unity-2019.4.24f1-mbe/mono/mini/abcremoval.c new file mode 100644 index 000000000..803d1f6dc --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/abcremoval.c @@ -0,0 +1,1402 @@ +/** + * \file + * Array bounds check removal + * + * Author: + * Massimiliano Mantione (massi@ximian.com) + * + * (C) 2004 Ximian, Inc. http://www.ximian.com + */ +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#ifndef DISABLE_JIT + +#include "abcremoval.h" + +#if SIZEOF_VOID_P == 8 +#define OP_PCONST OP_I8CONST +#else +#define OP_PCONST OP_ICONST +#endif + + +#define TRACE_ABC_REMOVAL (verbose_level > 2) +#define REPORT_ABC_REMOVAL (verbose_level > 1) + +/* + * A little hack for the verbosity level. + * The verbosity level is stored in the cfg, but not all functions that must + * print something see the cfg, so we store the verbosity level here at the + * beginning of the algorithm. + * This is not thread safe (does not handle correctly different verbosity + * levels in different threads), and is not exact in case of dynamic changes + * of the verbosity level... + * Anyway, this is not needed, all that can happen is that something more + * (or less) is logged, the result is in any case correct. + */ +static int verbose_level; + + +#define RELATION_BETWEEN_VALUES(value,related_value) (\ + ((value) > (related_value))? MONO_GT_RELATION :\ + (((value) < (related_value))? MONO_LT_RELATION : MONO_EQ_RELATION)) + +#define MAKE_VALUE_ANY(v) do{\ + (v).type = MONO_ANY_SUMMARIZED_VALUE;\ + } while (0) + +#define MAKE_VALUE_RELATION_ANY(r) do{\ + (r)->relation = MONO_ANY_RELATION;\ + MAKE_VALUE_ANY((r)->related_value);\ + } while (0) + +#define INITIALIZE_VALUE_RELATION(r) do{\ + MAKE_VALUE_RELATION_ANY((r));\ + (r)->next = NULL;\ + } while (0) + +#define MONO_NEGATED_RELATION(r) ((MonoValueRelation)((~(r))&MONO_ANY_RELATION)) +#define MONO_SYMMETRIC_RELATION(r) ((MonoValueRelation)(((r)&MONO_EQ_RELATION)|(((r)&MONO_LT_RELATION)<<1)|((r&MONO_GT_RELATION)>>1))) + + + +static void +print_relation (int relation) { + int print_or = 0; + printf ("("); + if (relation & MONO_LT_RELATION) { + printf ("LT"); + print_or = 1; + } + if (relation & MONO_EQ_RELATION) { + if (print_or) { + printf ("|"); + } + printf ("EQ"); + print_or = 1; + } + if (relation & MONO_GT_RELATION) { + if (print_or) { + printf ("|"); + } + printf ("GT"); + print_or = 1; + } + printf (")"); +} + +static void +print_summarized_value (MonoSummarizedValue *value) { + switch (value->type) { + case MONO_ANY_SUMMARIZED_VALUE: + printf ("ANY"); + break; + case MONO_CONSTANT_SUMMARIZED_VALUE: + printf ("CONSTANT %d", value->value.constant.value); + break; + case MONO_VARIABLE_SUMMARIZED_VALUE: + printf ("VARIABLE %d, delta %d", value->value.variable.variable, value->value.variable.delta); + break; + case MONO_PHI_SUMMARIZED_VALUE: { + int phi; + printf ("PHI ("); + for (phi = 0; phi < value->value.phi.number_of_alternatives; phi++) { + if (phi) printf (","); + printf ("%d", value->value.phi.phi_alternatives [phi]); + } + printf (")"); + break; + } + default: + g_assert_not_reached (); + } +} + +static void +print_summarized_value_relation (MonoSummarizedValueRelation *relation) { + printf ("Relation "); + print_relation (relation->relation); + printf (" with value "); + print_summarized_value (&(relation->related_value)); +} + +#if 0 +static void +print_summarized_value_relation_chain (MonoSummarizedValueRelation *relation) { + printf ("Relations:\n"); + while (relation) { + print_summarized_value_relation (relation); + printf ("\n"); + relation = relation->next; + } +} +#endif + +static void +print_evaluation_context_status (MonoRelationsEvaluationStatus status) { + if (status == MONO_RELATIONS_EVALUATION_NOT_STARTED) { + printf ("EVALUATION_NOT_STARTED"); + } else { + gboolean print_or = FALSE; + + printf ("("); + if (status & MONO_RELATIONS_EVALUATION_IN_PROGRESS) { + if (print_or) printf ("|"); + printf ("EVALUATION_IN_PROGRESS"); + print_or = TRUE; + } + if (status & MONO_RELATIONS_EVALUATION_COMPLETED) { + if (print_or) printf ("|"); + printf ("EVALUATION_COMPLETED"); + print_or = TRUE; + } + if (status & MONO_RELATIONS_EVALUATION_IS_RECURSIVELY_ASCENDING) { + if (print_or) printf ("|"); + printf ("RECURSIVELY_ASCENDING"); + print_or = TRUE; + } + if (status & MONO_RELATIONS_EVALUATION_IS_RECURSIVELY_DESCENDING) { + if (print_or) printf ("|"); + printf ("RECURSIVELY_DESCENDING"); + print_or = TRUE; + } + if (status & MONO_RELATIONS_EVALUATION_IS_RECURSIVELY_INDEFINITE) { + if (print_or) printf ("|"); + printf ("RECURSIVELY_INDEFINITE"); + print_or = TRUE; + } + printf (")"); + } +} + + +static void +print_evaluation_context_ranges (MonoRelationsEvaluationRanges *ranges) { + printf ("(ranges: zero [%d,%d], variable [%d,%d])", ranges->zero.lower, ranges->zero.upper, ranges->variable.lower, ranges->variable.upper); +} + +static void +print_evaluation_context (MonoRelationsEvaluationContext *context, MonoRelationsEvaluationStatus status) { + print_evaluation_context_status (status); + if (status & (MONO_RELATIONS_EVALUATION_IN_PROGRESS|MONO_RELATIONS_EVALUATION_COMPLETED)) { + print_evaluation_context_ranges (&(context->ranges)); + } + printf ("\n"); +} + +#if 0 +static void +print_evaluation_area (MonoVariableRelationsEvaluationArea *area) { + int i; + printf ("Dump of evaluation area (%d variables):\n", area->cfg->num_varinfo); + for (i = 0; i < area->cfg->num_varinfo; i++) { + printf ("Variable %d: ", i); + print_evaluation_context (&(area->contexts [i])); + print_summarized_value_relation_chain (&(area->relations [i])); + } +} + +static void +print_evaluation_area_contexts (MonoVariableRelationsEvaluationArea *area) { + int i; + printf ("Dump of evaluation area contexts (%d variables):\n", area->cfg->num_varinfo); + for (i = 0; i < area->cfg->num_varinfo; i++) { + printf ("Variable %d: ", i); + print_evaluation_context (&(area->contexts [i])); + } +} +#endif + +/* + * Check if the delta of an integer variable value is safe with respect + * to the variable size in bytes and its kind (signed or unsigned). + * If the delta is not safe, make the value an "any". + */ +static G_GNUC_UNUSED void +check_delta_safety (MonoVariableRelationsEvaluationArea *area, MonoSummarizedValue *value) { + if (value->type == MONO_VARIABLE_SUMMARIZED_VALUE) { + int variable = value->value.variable.variable; + int delta = value->value.variable.delta; + if ((area->variable_value_kind [variable]) & MONO_UNSIGNED_VALUE_FLAG) { + if (delta < 0) { + MAKE_VALUE_ANY (*value); + } + } else { + if (((area->variable_value_kind [variable]) & MONO_INTEGER_VALUE_SIZE_BITMASK) < 4) { + MAKE_VALUE_ANY (*value); + } else if (delta > 16) { + MAKE_VALUE_ANY (*value); + } + } + } +} + +/* + * get_relation_from_ins: + * + * Obtain relations from a MonoInst. + * + * result_value_kind: the "expected" kind of result; + * result: the "summarized" value + * returns the "actual" kind of result, if guessable (otherwise MONO_UNKNOWN_INTEGER_VALUE) + */ +static MonoIntegerValueKind +get_relation_from_ins (MonoVariableRelationsEvaluationArea *area, MonoInst *ins, MonoSummarizedValueRelation *result, MonoIntegerValueKind result_value_kind) +{ + MonoIntegerValueKind value_kind; + MonoSummarizedValue *value = &result->related_value; + + if (ins->type == STACK_I8) { + value_kind = MONO_INTEGER_VALUE_SIZE_8; + } else if (ins->type == STACK_I4) { + value_kind = MONO_INTEGER_VALUE_SIZE_4; + } else { + value_kind = MONO_UNKNOWN_INTEGER_VALUE; + } + + result->relation = MONO_EQ_RELATION; + MAKE_VALUE_ANY (*value); + + switch (ins->opcode) { + case OP_ICONST: + value->type = MONO_CONSTANT_SUMMARIZED_VALUE; + value->value.constant.value = ins->inst_c0; + break; + case OP_MOVE: + value->type = MONO_VARIABLE_SUMMARIZED_VALUE; + value->value.variable.variable = ins->sreg1; + value->value.variable.delta = 0; + break; + case OP_SEXT_I4: + value->type = MONO_VARIABLE_SUMMARIZED_VALUE; + value->value.variable.variable = ins->sreg1; + value->value.variable.delta = 0; + value_kind = MONO_INTEGER_VALUE_SIZE_8; + break; + case OP_PHI: + value->type = MONO_PHI_SUMMARIZED_VALUE; + value->value.phi.number_of_alternatives = *(ins->inst_phi_args); + value->value.phi.phi_alternatives = ins->inst_phi_args + 1; + break; + case OP_IADD_IMM: + value->type = MONO_VARIABLE_SUMMARIZED_VALUE; + value->value.variable.variable = ins->sreg1; + value->value.variable.delta = ins->inst_imm; + /* FIXME: */ + //check_delta_safety (area, result); + break; + case OP_ISUB_IMM: + value->type = MONO_VARIABLE_SUMMARIZED_VALUE; + value->value.variable.variable = ins->sreg1; + value->value.variable.delta = -ins->inst_imm; + /* FIXME: */ + //check_delta_safety (area, result); + break; + case OP_IREM_UN: + /* The result of an unsigned remainder is 0 < x < the divisor */ + result->relation = MONO_LT_RELATION; + value->type = MONO_VARIABLE_SUMMARIZED_VALUE; + value->value.variable.variable = ins->sreg2; + value->value.variable.delta = 0; + value_kind = MONO_UNSIGNED_INTEGER_VALUE_SIZE_4; + break; + case OP_LDLEN: + /* + * We represent arrays by their length, so r1<-ldlen r2 is stored + * as r1 == r2 in the evaluation graph. + */ + value->type = MONO_VARIABLE_SUMMARIZED_VALUE; + value->value.variable.variable = ins->sreg1; + value->value.variable.delta = 0; + value_kind = MONO_UNSIGNED_INTEGER_VALUE_SIZE_4; + break; + case OP_NEWARR: + value->type = MONO_VARIABLE_SUMMARIZED_VALUE; + value->value.variable.variable = ins->sreg1; + value->value.variable.delta = 0; + area->defs [ins->dreg] = ins; + break; + case OP_LDADDR: + /* The result is non-null */ + result->relation = MONO_GT_RELATION; + value->type = MONO_CONSTANT_SUMMARIZED_VALUE; + value->value.constant.value = 0; + break; + + /* FIXME: Add more opcodes */ + default: + /* These opcodes are not currently handled while running SciMark, first + * column is the number of times the warning was shown: + * + * 1 add_imm + * 1 float_conv_to_i8 + * 1 int_mul_ovf_un + * 1 int_neg + * 1 int_or + * 1 int_shr_un + * 1 localloc + * 1 long_ceq + * 1 long_rem + * 1 long_sub + * 2 int_ceq + * 2 int_conv_to_i2 + * 2 int_min + * 2 lcall + * 2 long_div + * 3 int_conv_to_u2 + * 3 long_shr_imm + * 4 int_rem + * 4 int_rem_imm + * 4 loadi1_membase + * 4 loadu4_membase + * 5 int_div + * 5 shl_imm + * 6 int_div_imm + * 6 int_mul + * 9 int_mul_imm + * 9 zext_i4 + * 10 int_shr_imm + * 12 int_shr_un_imm + * 12 long_add_imm + * 12 outarg_vtretaddr + * 12 strlen + * 13 int_or_imm + * 23 call_membase + * 23 int_conv_to_u1 + * 23 long_add + * 24 int_and_imm + * 24 int_shl_imm + * 24 loadu2_membase + * 29 loadi8_membase + * 31 llvm_outarg_vt + * 34 int_sub + * 34 loadu1_membase + * 42 int_add + * 85 ldaddr + * 116 loadi4_membase + * 159 x86_lea + * 179 sext_i4 + * 291 load_membase + * 462 i8const + * 472 call + */ + + break; + } + return value_kind; +} + +static MonoValueRelation +get_relation_from_branch_instruction (MonoInst *ins) +{ + if (MONO_IS_COND_BRANCH_OP (ins)) { + CompRelation rel = mono_opcode_to_cond (ins->opcode); + + switch (rel) { + case CMP_EQ: + return MONO_EQ_RELATION; + case CMP_NE: + return MONO_NE_RELATION; + case CMP_LE: + case CMP_LE_UN: + return MONO_LE_RELATION; + case CMP_GE: + case CMP_GE_UN: + return MONO_GE_RELATION; + case CMP_LT: + case CMP_LT_UN: + return MONO_LT_RELATION; + case CMP_GT: + case CMP_GT_UN: + return MONO_GT_RELATION; + default: + g_assert_not_reached (); + return MONO_ANY_RELATION; + } + } else { + return MONO_ANY_RELATION; + } +} + +/* + * Given a BB, find its entry condition and put its relations in a + * "MonoAdditionalVariableRelationsForBB" structure. + * bb: the BB + * relations: the resulting relations (entry condition of the given BB) + */ +static void +get_relations_from_previous_bb (MonoVariableRelationsEvaluationArea *area, MonoBasicBlock *bb, MonoAdditionalVariableRelationsForBB *relations) +{ + MonoBasicBlock *in_bb; + MonoInst *ins, *compare, *branch; + MonoValueRelation branch_relation; + MonoValueRelation symmetric_relation; + gboolean code_path; + + INITIALIZE_VALUE_RELATION (&(relations->relation1.relation)); + relations->relation1.relation.relation_is_static_definition = FALSE; + relations->relation1.relation.next = NULL; + relations->relation1.insertion_point = NULL; + relations->relation1.variable = -1; + INITIALIZE_VALUE_RELATION (&(relations->relation2.relation)); + relations->relation2.relation.relation_is_static_definition = FALSE; + relations->relation2.relation.next = NULL; + relations->relation2.insertion_point = NULL; + relations->relation2.variable = -1; + + if (bb->in_count == 1) { /* Should write the code to "sum" conditions... */ + in_bb = bb->in_bb [0]; + + if ((in_bb->last_ins == NULL) || (in_bb->code == in_bb->last_ins)) + return; + + for (ins = in_bb->code; ins->next != in_bb->last_ins; ins = ins->next) + ; + + compare = ins; + branch = ins->next; + branch_relation = get_relation_from_branch_instruction (branch); + + if (branch_relation != MONO_ANY_RELATION) { + if (branch->inst_true_bb == bb) { + code_path = TRUE; + } else if (branch->inst_false_bb == bb) { + code_path = FALSE; + } else { + code_path = TRUE; + g_assert_not_reached (); + } + if (!code_path) + branch_relation = MONO_NEGATED_RELATION (branch_relation); + symmetric_relation = MONO_SYMMETRIC_RELATION (branch_relation); + + /* FIXME: Other compare opcodes */ + if (compare->opcode == OP_ICOMPARE) { + relations->relation1.variable = compare->sreg1; + relations->relation1.relation.relation = branch_relation; + relations->relation1.relation.related_value.type = MONO_VARIABLE_SUMMARIZED_VALUE; + relations->relation1.relation.related_value.value.variable.variable = compare->sreg2; + relations->relation1.relation.related_value.value.variable.delta = 0; + + relations->relation2.variable = compare->sreg2; + relations->relation2.relation.relation = symmetric_relation; + relations->relation2.relation.related_value.type = MONO_VARIABLE_SUMMARIZED_VALUE; + relations->relation2.relation.related_value.value.variable.variable = compare->sreg1; + relations->relation2.relation.related_value.value.variable.delta = 0; + } else if (compare->opcode == OP_ICOMPARE_IMM) { + relations->relation1.variable = compare->sreg1; + relations->relation1.relation.relation = branch_relation; + relations->relation1.relation.related_value.type = MONO_CONSTANT_SUMMARIZED_VALUE; + relations->relation1.relation.related_value.value.constant.value = compare->inst_imm; + } + } + } +} + +/* + * Add the given relations to the evaluation area. + * area: the evaluation area + * change: the relations that must be added + */ +static void +apply_change_to_evaluation_area (MonoVariableRelationsEvaluationArea *area, MonoAdditionalVariableRelation *change) +{ + MonoSummarizedValueRelation *base_relation; + + if (change->relation.relation != MONO_ANY_RELATION) { + base_relation = &(area->relations [change->variable]); + while ((base_relation->next != NULL) && (base_relation->next->relation_is_static_definition)) { + base_relation = base_relation->next; + } + change->insertion_point = base_relation; + change->relation.next = base_relation->next; + base_relation->next = &(change->relation); + } +} + +/* + * Remove the given relation from the evaluation area. + * change: the relation that must be removed + */ +static void +remove_change_from_evaluation_area (MonoAdditionalVariableRelation *change) +{ + if (change->insertion_point != NULL) { + change->insertion_point->next = change->relation.next; + change->relation.next = NULL; + } +} + + +static void +clean_contexts (MonoVariableRelationsEvaluationArea *area, int number) +{ + memset(area->statuses, MONO_RELATIONS_EVALUATION_NOT_STARTED, number * sizeof(MonoRelationsEvaluationStatus)); +} + + +/* + * Perform the intersection of a range and a constant value (taking into + * account the relation that the value has with the range). + * range: the range that will be intersected with the value + * value: the value that will be intersected with the range + * relation: the relation between the range and the value + */ +static void +intersect_value( MonoRelationsEvaluationRange *range, int value, MonoValueRelation relation ) +{ + switch (relation) { + case MONO_NO_RELATION: + MONO_MAKE_RELATIONS_EVALUATION_RANGE_IMPOSSIBLE (*range); + break; + case MONO_ANY_RELATION: + break; + case MONO_EQ_RELATION: + MONO_UPPER_EVALUATION_RANGE_INTERSECTION (range->upper, value); + MONO_LOWER_EVALUATION_RANGE_INTERSECTION (range->lower, value); + break; + case MONO_NE_RELATION: { + /* IMPROVEMENT Figure this out! (ignoring it is safe anyway) */ + break; + } + case MONO_LT_RELATION: + MONO_UPPER_EVALUATION_RANGE_INTERSECTION (range->upper, MONO_UPPER_EVALUATION_RANGE_NOT_EQUAL (value)); + break; + case MONO_LE_RELATION: + MONO_UPPER_EVALUATION_RANGE_INTERSECTION (range->upper, value); + break; + case MONO_GT_RELATION: + MONO_LOWER_EVALUATION_RANGE_INTERSECTION (range->lower, MONO_LOWER_EVALUATION_RANGE_NOT_EQUAL (value)); + break; + case MONO_GE_RELATION: + MONO_LOWER_EVALUATION_RANGE_INTERSECTION (range->lower, value); + break; + default: + g_assert_not_reached(); + } +} + + +/* + * Perform the intersection of two pairs of ranges (taking into account the + * relation between the ranges and a given delta). + * ranges: the ranges that will be intersected + * other_ranges the other ranges that will be intersected + * delta: the delta between the pairs of ranges + * relation: the relation between the pairs of ranges + */ +static void +intersect_ranges( MonoRelationsEvaluationRanges *ranges, MonoRelationsEvaluationRanges *other_ranges, int delta, MonoValueRelation relation ) +{ + if (delta == 0) { + switch (relation) { + case MONO_NO_RELATION: + MONO_MAKE_RELATIONS_EVALUATION_RANGES_IMPOSSIBLE (*ranges); + break; + case MONO_ANY_RELATION: + break; + case MONO_EQ_RELATION: + MONO_RELATIONS_EVALUATION_RANGES_INTERSECTION (*ranges, *other_ranges); + break; + case MONO_NE_RELATION: { + /* FIXME Figure this out! (ignoring it is safe anyway) */ + break; + } + case MONO_LT_RELATION: + MONO_UPPER_EVALUATION_RANGE_INTERSECTION (ranges->zero.upper, MONO_UPPER_EVALUATION_RANGE_NOT_EQUAL (other_ranges->zero.upper)); + MONO_UPPER_EVALUATION_RANGE_INTERSECTION (ranges->variable.upper, MONO_UPPER_EVALUATION_RANGE_NOT_EQUAL (other_ranges->variable.upper)); + break; + case MONO_LE_RELATION: + MONO_UPPER_EVALUATION_RANGE_INTERSECTION (ranges->zero.upper, other_ranges->zero.upper); + MONO_UPPER_EVALUATION_RANGE_INTERSECTION (ranges->variable.upper, other_ranges->variable.upper); + break; + case MONO_GT_RELATION: + MONO_LOWER_EVALUATION_RANGE_INTERSECTION (ranges->zero.lower, MONO_LOWER_EVALUATION_RANGE_NOT_EQUAL (other_ranges->zero.lower)); + MONO_LOWER_EVALUATION_RANGE_INTERSECTION (ranges->variable.lower, MONO_LOWER_EVALUATION_RANGE_NOT_EQUAL (other_ranges->variable.lower)); + break; + case MONO_GE_RELATION: + MONO_LOWER_EVALUATION_RANGE_INTERSECTION (ranges->zero.lower, other_ranges->zero.lower); + MONO_LOWER_EVALUATION_RANGE_INTERSECTION (ranges->variable.lower, other_ranges->variable.lower); + break; + default: + g_assert_not_reached(); + } + } else { + MonoRelationsEvaluationRanges translated_ranges = *other_ranges; + MONO_ADD_DELTA_SAFELY_TO_RANGES (translated_ranges, delta); + intersect_ranges( ranges, &translated_ranges, FALSE, relation ); + } +} + +/* + * Recursive method that traverses the relation graph to evaluate the + * relation between two variables. + * At the end of the execution, the resulting ranges are in the context of + * the "starting" variable. + * area: the current evaluation area (it contains the relation graph and + * memory for all the evaluation contexts is already allocated) + * variable: starting variable (the value ranges in its context are the result + * of the execution of this procedure) + * target_variable: the variable with respect to which the starting variable + * is evaluated (tipically the starting variable is the index + * and the target one is the array (which means its length)) + * father_context: the previous evaluation context in recursive invocations + * (or NULL for the first invocation) + */ +static void +evaluate_relation_with_target_variable (MonoVariableRelationsEvaluationArea *area, const int variable, const int target_variable, MonoRelationsEvaluationContext *father_context) +{ + MonoRelationsEvaluationContext * const context = &(area->contexts [variable]); + MonoRelationsEvaluationStatus * const status = &(area->statuses [variable]); + + // First of all, we check the evaluation status + // (what must be done is *very* different in each case) + switch (*status) { + case MONO_RELATIONS_EVALUATION_NOT_STARTED: { + MonoSummarizedValueRelation *relation = &(area->relations [variable]); + + if (TRACE_ABC_REMOVAL) { + printf ("Evaluating variable %d (target variable %d)\n", variable, target_variable); + print_summarized_value_relation (relation); + printf ("\n"); + } + + // We properly inizialize the context + *status = MONO_RELATIONS_EVALUATION_IN_PROGRESS; + context->father = father_context; + MONO_MAKE_RELATIONS_EVALUATION_RANGES_WEAK (context->ranges); + + // If we have found the target variable, we can set the range + // related to it in the context to "equal" (which is [0,0]) + if (variable == target_variable) { + if (TRACE_ABC_REMOVAL) { + printf ("Target variable reached (%d), continuing to evaluate relations with constants\n", variable); + } + context->ranges.variable.lower = 0; + context->ranges.variable.upper = 0; + } + + // Examine all relations for this variable (scan the list) + // The contribute of each relation will be intersected (logical and) + while (relation != NULL) { + context->current_relation = relation; + + if (TRACE_ABC_REMOVAL) { + printf ("Processing (%d): ", variable); + print_summarized_value_relation (relation); + printf ("\n"); + } + + // We decie what to do according the the type of the related value + switch (relation->related_value.type) { + case MONO_ANY_SUMMARIZED_VALUE: + // No added information, skip it + break; + case MONO_CONSTANT_SUMMARIZED_VALUE: + // Intersect range with constant (taking into account the relation) + intersect_value (&(context->ranges.zero), relation->related_value.value.constant.value, relation->relation); + break; + case MONO_VARIABLE_SUMMARIZED_VALUE: + // Generally, evaluate related variable and intersect ranges. + // However, some check is necessary... + + // If the relation is "ANY", nothing to do (no added information) + if (relation->relation != MONO_ANY_RELATION) { + int related_variable = relation->related_value.value.variable.variable; + MonoRelationsEvaluationContext *related_context = &(area->contexts [related_variable]); + MonoRelationsEvaluationStatus related_status = area->statuses [related_variable]; + + // The second condition in the "or" avoids messing with "back edges" in the graph traversal + // (they are simply ignored instead of triggering the handling of recursion) + if ( (related_status == MONO_RELATIONS_EVALUATION_NOT_STARTED) || ! + ((related_context->current_relation->related_value.type == MONO_VARIABLE_SUMMARIZED_VALUE) && + (related_context->current_relation->related_value.value.variable.variable == variable))) { + // Evaluate the related variable + evaluate_relation_with_target_variable (area, related_variable, target_variable, context); + + // Check if we are part of a recursive loop + if (*status & MONO_RELATIONS_EVALUATION_IS_RECURSIVE) { + if (TRACE_ABC_REMOVAL) { + printf ("Recursivity detected for variable %d (target variable %d), status ", variable, target_variable); + print_evaluation_context_status (*status); + } + + // If we are, check if the evaluation of the related variable is complete + if (related_status == MONO_RELATIONS_EVALUATION_COMPLETED) { + // If it is complete, we are part of a recursive definition. + // Since it is a *definition* (and definitions are evaluated *before* + // conditions because they are first in the list), intersection is not + // strictly necessary, we simply copy the ranges and apply the delta + context->ranges = related_context->ranges; + /* Delta has already been checked for over/under-flow when evaluating values */ + MONO_ADD_DELTA_SAFELY_TO_RANGES (context->ranges, relation->related_value.value.variable.delta); + *status = MONO_RELATIONS_EVALUATION_COMPLETED; + if (TRACE_ABC_REMOVAL) { + printf (", ranges already computed, result: \n"); + print_evaluation_context_ranges (&(context->ranges)); + printf (" (delta is %d)\n", relation->related_value.value.variable.delta); + } + } else { + // If it is not complete, do nothing (we do not have enough information) + if (TRACE_ABC_REMOVAL) { + printf (", ranges not computed\n"); + } + } + } else { + // If we are not (the common case) intersect the result + intersect_ranges( &(context->ranges), &(related_context->ranges), relation->related_value.value.variable.delta, relation->relation ); + } + } else { + if (TRACE_ABC_REMOVAL) { + printf ("Relation is a back-edge in this traversal, skipping\n"); + } + } + } + break; + case MONO_PHI_SUMMARIZED_VALUE: { + // We must compute all PHI alternatives, combining the results (with a union, which is a logical "or"), + // and intersect this result with the ranges in the context; we must also take into account recursions + // (with loops that can be ascending, descending, or indefinite) + MonoRelationsEvaluationRanges phi_ranges; + int phi; + gboolean is_ascending = FALSE; + gboolean is_descending = FALSE; + + MONO_MAKE_RELATIONS_EVALUATION_RANGES_IMPOSSIBLE (phi_ranges); + for (phi = 0; phi < relation->related_value.value.phi.number_of_alternatives; phi++) { + int phi_alternative = relation->related_value.value.phi.phi_alternatives [phi]; + evaluate_relation_with_target_variable (area, phi_alternative, target_variable, context); + + // This means we are part of a recursive loop + if (*status & MONO_RELATIONS_EVALUATION_IS_RECURSIVE) { + if (TRACE_ABC_REMOVAL) { + printf ("Recursivity detected for variable %d (target variable %d), status ", variable, target_variable); + print_evaluation_context_status (*status); + printf ("\n"); + } + if (*status & MONO_RELATIONS_EVALUATION_IS_RECURSIVELY_ASCENDING) { + is_ascending = TRUE; + } + if (*status & MONO_RELATIONS_EVALUATION_IS_RECURSIVELY_DESCENDING) { + is_descending = TRUE; + } + if (*status & MONO_RELATIONS_EVALUATION_IS_RECURSIVELY_INDEFINITE) { + is_ascending = TRUE; + is_descending = TRUE; + } + + // Clear "recursivity" bits in the status (recursion has been handled) + *status = MONO_RELATIONS_EVALUATION_IN_PROGRESS; + } else { + MONO_RELATIONS_EVALUATION_RANGES_UNION (phi_ranges, area->contexts [phi_alternative].ranges); + } + } + + // Apply the effects of all recursive loops + if (is_ascending) { + phi_ranges.zero.upper = INT_MAX; + phi_ranges.variable.upper = INT_MAX; + } + if (is_descending) { + phi_ranges.zero.lower = INT_MIN; + phi_ranges.variable.lower = INT_MIN; + } + + // Intersect final result + MONO_RELATIONS_EVALUATION_RANGES_INTERSECTION (context->ranges, phi_ranges); + break; + } + default: + g_assert_not_reached(); + } + + // Pass to next relation + relation = relation->next; + } + + // Check if any recursivity bits are still in the status, and in any case clear them + if (*status & MONO_RELATIONS_EVALUATION_IS_RECURSIVE) { + if (TRACE_ABC_REMOVAL) { + printf ("Recursivity for variable %d (target variable %d) discards computation, status ", variable, target_variable); + print_evaluation_context_status (*status); + printf ("\n"); + } + // If yes, we did not have enough information (most likely we were evaluated inside a PHI, but we also + // depended on the same PHI, which was still in evaluation...), so clear the status to "NOT_STARTED" + // (if we will be evaluated again, the PHI will be already done, so our evaluation will succeed) + *status = MONO_RELATIONS_EVALUATION_NOT_STARTED; + } else { + if (TRACE_ABC_REMOVAL) { + printf ("Ranges for variable %d (target variable %d) computed: ", variable, target_variable); + print_evaluation_context_ranges (&(context->ranges)); + printf ("\n"); + } + // If not (the common case) the evaluation is complete, and the result is in the context + *status = MONO_RELATIONS_EVALUATION_COMPLETED; + } + break; + } + case MONO_RELATIONS_EVALUATION_IN_PROGRESS: { + // This means we are in a recursive loop + MonoRelationsEvaluationContext *current_context = father_context; + MonoRelationsEvaluationContext *last_context = context->father; + gboolean evaluation_can_be_recursive = TRUE; + gboolean evaluation_is_definition = TRUE; + int path_value = 0; + + if (TRACE_ABC_REMOVAL) { + printf ("Evaluation of variable %d (target variable %d) already in progress\n", variable, target_variable); + print_evaluation_context (context, *status); + print_summarized_value_relation (context->current_relation); + printf ("\n"); + } + + // We must check if the loop can be a recursive definition (we scan the whole loop) + while (current_context != last_context) { + if (current_context == NULL) { + printf ("Broken recursive ring in ABC removal\n"); + g_assert_not_reached (); + } + + if (current_context->current_relation->relation_is_static_definition) { + if (current_context->current_relation->related_value.type == MONO_VARIABLE_SUMMARIZED_VALUE) { + /* No need to check path_value for over/under-flow, since delta should be safe */ + path_value += current_context->current_relation->related_value.value.variable.delta; + } else if (current_context->current_relation->related_value.type != MONO_PHI_SUMMARIZED_VALUE) { + evaluation_can_be_recursive = FALSE; + } + } else { + evaluation_is_definition = FALSE; + evaluation_can_be_recursive = FALSE; + } + + current_context = current_context->father; + } + + // If this is a recursive definition, we properly flag the status in all the involved contexts + if (evaluation_is_definition) { + MonoRelationsEvaluationStatus recursive_status; + if (evaluation_can_be_recursive) { + if (path_value > 0) { + recursive_status = MONO_RELATIONS_EVALUATION_IS_RECURSIVELY_ASCENDING; + } else if (path_value < 0) { + recursive_status = MONO_RELATIONS_EVALUATION_IS_RECURSIVELY_DESCENDING; + } else { + recursive_status = MONO_RELATIONS_EVALUATION_IS_RECURSIVELY_INDEFINITE; + } + } else { + recursive_status = MONO_RELATIONS_EVALUATION_IS_RECURSIVELY_INDEFINITE; + } + + if (TRACE_ABC_REMOVAL) { + printf ("Recursivity accepted ("); + print_evaluation_context_status (recursive_status); + printf (")\n"); + } + + current_context = father_context; + while (current_context != last_context) { + int index = current_context - area->contexts; + MonoRelationsEvaluationStatus *current_status = &(area->statuses [index]); + *current_status = (MonoRelationsEvaluationStatus)(*current_status | recursive_status); + current_context = current_context->father; + } + } else { + if (TRACE_ABC_REMOVAL) { + printf ("Recursivity rejected (some relation in the cycle is not a defintion)\n"); + } + } + break; + } + case MONO_RELATIONS_EVALUATION_COMPLETED: { + return; + } + default: + if (TRACE_ABC_REMOVAL) { + printf ("Variable %d (target variable %d) already in a recursive ring, skipping\n", variable, target_variable); + print_evaluation_context (context, *status); + print_summarized_value_relation (context->current_relation); + printf ("\n"); + } + break; + } + +} + +/* + * Apply the given value kind to the given range + */ +static void +apply_value_kind_to_range (MonoRelationsEvaluationRange *range, MonoIntegerValueKind value_kind) +{ + if (value_kind != MONO_UNKNOWN_INTEGER_VALUE) { + if (value_kind & MONO_UNSIGNED_VALUE_FLAG) { + if (range->lower < 0) { + range->lower = 0; + } + if ((value_kind & MONO_INTEGER_VALUE_SIZE_BITMASK) == 1) { + if (range->upper > 0xff) { + range->upper = 0xff; + } + } else if ((value_kind & MONO_INTEGER_VALUE_SIZE_BITMASK) == 2) { + if (range->upper > 0xffff) { + range->upper = 0xffff; + } + } + } else { + if ((value_kind & MONO_INTEGER_VALUE_SIZE_BITMASK) == 1) { + if (range->lower < -0x80) { + range->lower = -0x80; + } + if (range->upper > 0x7f) { + range->upper = 0x7f; + } + } else if ((value_kind & MONO_INTEGER_VALUE_SIZE_BITMASK) == 2) { + if (range->lower < -0x8000) { + range->lower = -0x8000; + } + if (range->upper > 0x7fff) { + range->upper = 0x7fff; + } + } + } + } +} + +/* + * Attempt the removal of bounds checks from a MonoInst. + * inst: the MonoInst + * area: the current evaluation area (it contains the relation graph and + * memory for all the evaluation contexts is already allocated) + */ +static void +remove_abc_from_inst (MonoInst *ins, MonoVariableRelationsEvaluationArea *area) +{ + /* FIXME: Add support for 'constant' arrays and constant indexes */ + + int array_variable = ins->sreg1; + int index_variable = ins->sreg2; + MonoRelationsEvaluationContext *array_context = &(area->contexts [array_variable]); + MonoRelationsEvaluationContext *index_context = &(area->contexts [index_variable]); + + clean_contexts (area, area->cfg->next_vreg); + + evaluate_relation_with_target_variable (area, index_variable, array_variable, NULL); + evaluate_relation_with_target_variable (area, array_variable, array_variable, NULL); + + if ((index_context->ranges.zero.lower >=0) && ((index_context->ranges.variable.upper < 0)||(index_context->ranges.zero.upper < array_context->ranges.zero.lower))) { + if (REPORT_ABC_REMOVAL) { + printf ("ARRAY-ACCESS: removed bounds check on array %d with index %d\n", + array_variable, index_variable); + } + NULLIFY_INS (ins); + } else { + if (TRACE_ABC_REMOVAL) { + if (index_context->ranges.zero.lower >= 0) { + printf ("ARRAY-ACCESS: Removed lower bound check on array %d with index %d\n", array_variable, index_variable); + } + if (index_context->ranges.variable.upper < 0) { + printf ("ARRAY-ACCESS: Removed upper bound check (through variable) on array %d with index %d\n", array_variable, index_variable); + } + if (index_context->ranges.zero.upper < array_context->ranges.zero.lower) { + printf ("ARRAY-ACCESS: Removed upper bound check (through constant) on array %d with index %d\n", array_variable, index_variable); + } + } + } +} + +static gboolean +eval_non_null (MonoVariableRelationsEvaluationArea *area, int reg) +{ + MonoRelationsEvaluationContext *context = &(area->contexts [reg]); + + clean_contexts (area, area->cfg->next_vreg); + evaluate_relation_with_target_variable (area, reg, reg, NULL); + + return context->ranges.zero.lower > 0; +} + +static void +add_non_null (MonoVariableRelationsEvaluationArea *area, MonoCompile *cfg, int reg, + GSList **check_relations) +{ + MonoAdditionalVariableRelation *rel; + + rel = (MonoAdditionalVariableRelation *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoAdditionalVariableRelation)); + rel->variable = reg; + rel->relation.relation = MONO_GT_RELATION; + rel->relation.related_value.type = MONO_CONSTANT_SUMMARIZED_VALUE; + rel->relation.related_value.value.constant.value = 0; + + apply_change_to_evaluation_area (area, rel); + + *check_relations = g_slist_append_mempool (cfg->mempool, *check_relations, rel); +} + +/* + * Process a BB removing bounds checks from array accesses. + * It does the following (in sequence): + * - Get the BB entry condition + * - Add its relations to the relation graph in the evaluation area + * - Process all the MonoInst trees in the BB + * - Recursively process all the children BBs in the dominator tree + * - Remove the relations previously added to the relation graph + * + * bb: the BB that must be processed + * area: the current evaluation area (it contains the relation graph and + * memory for all the evaluation contexts is already allocated) + */ +static void +process_block (MonoCompile *cfg, MonoBasicBlock *bb, MonoVariableRelationsEvaluationArea *area) { + int inst_index; + MonoInst *ins; + MonoAdditionalVariableRelationsForBB additional_relations; + GSList *dominated_bb, *l; + GSList *check_relations = NULL; + + if (TRACE_ABC_REMOVAL) { + printf ("\nProcessing block %d [dfn %d]...\n", bb->block_num, bb->dfn); + } + + if (bb->region != -1) + return; + + get_relations_from_previous_bb (area, bb, &additional_relations); + if (TRACE_ABC_REMOVAL) { + if (additional_relations.relation1.relation.relation != MONO_ANY_RELATION) { + printf ("Adding relation 1 on variable %d: ", additional_relations.relation1.variable); + print_summarized_value_relation (&(additional_relations.relation1.relation)); + printf ("\n"); + } + if (additional_relations.relation2.relation.relation != MONO_ANY_RELATION) { + printf ("Adding relation 2 on variable %d: ", additional_relations.relation2.variable); + print_summarized_value_relation (&(additional_relations.relation2.relation)); + printf ("\n"); + } + } + apply_change_to_evaluation_area (area, &(additional_relations.relation1)); + apply_change_to_evaluation_area (area, &(additional_relations.relation2)); + + inst_index = 0; + for (ins = bb->code; ins; ins = ins->next) { + MonoAdditionalVariableRelation *rel; + int array_var, index_var; + + if (TRACE_ABC_REMOVAL) { + printf ("Processing instruction %d\n", inst_index); + inst_index++; + } + + if (ins->opcode == OP_BOUNDS_CHECK) { /* Handle OP_LDELEMA2D, too */ + if (TRACE_ABC_REMOVAL) { + printf ("Attempting check removal...\n"); + } + + array_var = ins->sreg1; + index_var = ins->sreg2; + + remove_abc_from_inst (ins, area); + + /* We can derive additional relations from the bounds check */ + if (ins->opcode != OP_NOP) { + rel = (MonoAdditionalVariableRelation *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoAdditionalVariableRelation)); + rel->variable = index_var; + rel->relation.relation = MONO_LT_RELATION; + rel->relation.related_value.type = MONO_VARIABLE_SUMMARIZED_VALUE; + rel->relation.related_value.value.variable.variable = array_var; + rel->relation.related_value.value.variable.delta = 0; + + apply_change_to_evaluation_area (area, rel); + + check_relations = g_slist_append_mempool (cfg->mempool, check_relations, rel); + + rel = (MonoAdditionalVariableRelation *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoAdditionalVariableRelation)); + rel->variable = index_var; + rel->relation.relation = MONO_GE_RELATION; + rel->relation.related_value.type = MONO_CONSTANT_SUMMARIZED_VALUE; + rel->relation.related_value.value.constant.value = 0; + + apply_change_to_evaluation_area (area, rel); + + check_relations = g_slist_append_mempool (cfg->mempool, check_relations, rel); + } + } + + if (ins->opcode == OP_CHECK_THIS) { + if (eval_non_null (area, ins->sreg1)) { + if (REPORT_ABC_REMOVAL) + printf ("ARRAY-ACCESS: removed check_this instruction.\n"); + NULLIFY_INS (ins); + } + } + + if (ins->opcode == OP_NOT_NULL) + add_non_null (area, cfg, ins->sreg1, &check_relations); + + /* + * FIXME: abcrem equates an array with its length, + * so a = new int [100] implies a != null, but a = new int [0] doesn't. + */ + /* + * Eliminate MONO_INST_FAULT flags if possible. + */ + if (COMPILE_LLVM (cfg) && (ins->opcode == OP_LDLEN || + ins->opcode == OP_BOUNDS_CHECK || + ins->opcode == OP_STRLEN || + (MONO_IS_LOAD_MEMBASE (ins) && (ins->flags & MONO_INST_FAULT)) || + (MONO_IS_STORE_MEMBASE (ins) && (ins->flags & MONO_INST_FAULT)))) { + int reg; + + if (MONO_IS_STORE_MEMBASE (ins)) + reg = ins->inst_destbasereg; + else if (MONO_IS_LOAD_MEMBASE (ins)) + reg = ins->inst_basereg; + else + reg = ins->sreg1; + + /* + * This doesn't work because LLVM can move the non-faulting loads before the faulting + * ones (test_0_llvm_moving_faulting_loads ()). + * So only do it if we know the load cannot be moved before the instruction which ensures it is not + * null (i.e. the def of its sreg). + */ + if (area->defs [reg] && area->defs [reg]->opcode == OP_NEWARR) { + if (REPORT_ABC_REMOVAL) + printf ("ARRAY-ACCESS: removed MONO_INST_FAULT flag.\n"); + ins->flags &= ~MONO_INST_FAULT; + } + /* + if (eval_non_null (area, reg)) { + if (REPORT_ABC_REMOVAL) + printf ("ARRAY-ACCESS: removed MONO_INST_FAULT flag.\n"); + ins->flags &= ~MONO_INST_FAULT; + } else { + add_non_null (area, cfg, reg, &check_relations); + } + */ + } + } + + if (TRACE_ABC_REMOVAL) { + printf ("Processing block %d [dfn %d] done.\n", bb->block_num, bb->dfn); + } + + for (dominated_bb = bb->dominated; dominated_bb != NULL; dominated_bb = dominated_bb->next) { + process_block (cfg, (MonoBasicBlock*) (dominated_bb->data), area); + } + + for (l = check_relations; l; l = l->next) + remove_change_from_evaluation_area ((MonoAdditionalVariableRelation *)l->data); + + remove_change_from_evaluation_area (&(additional_relations.relation1)); + remove_change_from_evaluation_area (&(additional_relations.relation2)); +} + +static MonoIntegerValueKind +type_to_value_kind (MonoType *type) +{ + if (type->byref) + return MONO_UNKNOWN_INTEGER_VALUE; + switch (type->type) { + case MONO_TYPE_I1: + return MONO_INTEGER_VALUE_SIZE_1; + break; + case MONO_TYPE_U1: + return MONO_UNSIGNED_INTEGER_VALUE_SIZE_1; + break; + case MONO_TYPE_I2: + return MONO_INTEGER_VALUE_SIZE_2; + break; + case MONO_TYPE_U2: + return MONO_UNSIGNED_INTEGER_VALUE_SIZE_2; + break; + case MONO_TYPE_I4: + return MONO_INTEGER_VALUE_SIZE_4; + break; + case MONO_TYPE_U4: + return MONO_UNSIGNED_INTEGER_VALUE_SIZE_4; + break; + case MONO_TYPE_I: + return (MonoIntegerValueKind)SIZEOF_VOID_P; + break; + case MONO_TYPE_U: + return (MonoIntegerValueKind)(MONO_UNSIGNED_VALUE_FLAG | SIZEOF_VOID_P); + break; + case MONO_TYPE_I8: + return MONO_INTEGER_VALUE_SIZE_8; + break; + case MONO_TYPE_U8: + return MONO_UNSIGNED_INTEGER_VALUE_SIZE_8; + default: + return MONO_UNKNOWN_INTEGER_VALUE; + } +} + +/** + * mono_perform_abc_removal: + * \param cfg Control Flow Graph + * + * Performs the ABC removal from a cfg in SSA form. + * It does the following: + * - Prepare the evaluation area + * - Allocate memory for the relation graph in the evaluation area + * (of course, only for variable definitions) and summarize there all + * variable definitions + * - Allocate memory for the evaluation contexts in the evaluation area + * - Recursively process all the BBs in the dominator tree (it is enough + * to invoke the processing on the entry BB) + * + * cfg: the method code + */ +void +mono_perform_abc_removal (MonoCompile *cfg) +{ + MonoVariableRelationsEvaluationArea area; + MonoBasicBlock *bb; + int i; + + verbose_level = cfg->verbose_level; + + if (TRACE_ABC_REMOVAL) { + printf ("\nRemoving array bound checks in %s\n", mono_method_full_name (cfg->method, TRUE)); + } + + area.cfg = cfg; + area.relations = (MonoSummarizedValueRelation *) + mono_mempool_alloc (cfg->mempool, sizeof (MonoSummarizedValueRelation) * (cfg->next_vreg) * 2); + + area.contexts = (MonoRelationsEvaluationContext *) + mono_mempool_alloc0 (cfg->mempool, sizeof (MonoRelationsEvaluationContext) * (cfg->next_vreg)); + + area.statuses = (MonoRelationsEvaluationStatus *) + mono_mempool_alloc0 (cfg->mempool, sizeof (MonoRelationsEvaluationStatus) * (cfg->next_vreg)); + + area.variable_value_kind = (MonoIntegerValueKind *) + mono_mempool_alloc (cfg->mempool, sizeof (MonoIntegerValueKind) * (cfg->next_vreg)); + area.defs = (MonoInst **)mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * cfg->next_vreg); + for (i = 0; i < cfg->next_vreg; i++) { + area.variable_value_kind [i] = MONO_UNKNOWN_INTEGER_VALUE; + area.relations [i].relation = MONO_EQ_RELATION; + area.relations [i].relation_is_static_definition = TRUE; + MAKE_VALUE_ANY (area.relations [i].related_value); + area.relations [i].next = NULL; + area.defs [i] = NULL; + } + + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { + MonoInst *ins; + + if (TRACE_ABC_REMOVAL) + printf ("\nABCREM BLOCK %d:\n", bb->block_num); + + for (ins = bb->code; ins; ins = ins->next) { + const char *spec = INS_INFO (ins->opcode); + gint32 idx, *reg; + + if (spec [MONO_INST_DEST] == ' ' || MONO_IS_STORE_MEMBASE (ins)) + continue; + + MONO_INS_FOR_EACH_REG (ins, idx, reg) { + MonoInst *var = get_vreg_to_inst (cfg, *reg); + if (var && (!MONO_VARINFO (cfg, var->inst_c0)->def)) + break; + } + if (idx < MONO_INST_LEN) { + if (TRACE_ABC_REMOVAL) + printf ("Global register %d is not in the SSA form, skipping.\n", *reg); + continue; + } + + if (spec [MONO_INST_DEST] == 'i') { + MonoIntegerValueKind effective_value_kind; + MonoRelationsEvaluationRange range; + MonoSummarizedValueRelation *type_relation; + MonoInst *var; + + if (TRACE_ABC_REMOVAL) + mono_print_ins (ins); + + var = get_vreg_to_inst (cfg, ins->dreg); + if (var) + area.variable_value_kind [ins->dreg] = type_to_value_kind (var->inst_vtype); + + effective_value_kind = get_relation_from_ins (&area, ins, &area.relations [ins->dreg], area.variable_value_kind [ins->dreg]); + + MONO_MAKE_RELATIONS_EVALUATION_RANGE_WEAK (range); + apply_value_kind_to_range (&range, area.variable_value_kind [ins->dreg]); + apply_value_kind_to_range (&range, effective_value_kind); + + if (range.upper < INT_MAX) { + type_relation = (MonoSummarizedValueRelation *) mono_mempool_alloc (cfg->mempool, sizeof (MonoSummarizedValueRelation)); + type_relation->relation = MONO_LE_RELATION; + type_relation->related_value.type = MONO_CONSTANT_SUMMARIZED_VALUE; + type_relation->related_value.value.constant.value = range.upper; + type_relation->relation_is_static_definition = TRUE; + type_relation->next = area.relations [ins->dreg].next; + area.relations [ins->dreg].next = type_relation; + if (TRACE_ABC_REMOVAL) { + printf ("[var%d <= %d]", ins->dreg, range.upper); + } + } + if (range.lower > INT_MIN) { + type_relation = (MonoSummarizedValueRelation *) mono_mempool_alloc (cfg->mempool, sizeof (MonoSummarizedValueRelation)); + type_relation->relation = MONO_GE_RELATION; + type_relation->related_value.type = MONO_CONSTANT_SUMMARIZED_VALUE; + type_relation->related_value.value.constant.value = range.lower; + type_relation->relation_is_static_definition = TRUE; + type_relation->next = area.relations [ins->dreg].next; + area.relations [ins->dreg].next = type_relation; + if (TRACE_ABC_REMOVAL) { + printf ("[var%d >= %d]", ins->dreg, range.lower); + } + } + if (TRACE_ABC_REMOVAL) { + printf ("Summarized variable %d: ", ins->dreg); + print_summarized_value (&(area.relations [ins->dreg].related_value)); + printf ("\n"); + } + } + } + } + + /* Add symmetric relations */ + for (i = 0; i < cfg->next_vreg; i++) { + if (area.relations [i].related_value.type == MONO_VARIABLE_SUMMARIZED_VALUE) { + int related_index = cfg->next_vreg + i; + int related_variable = area.relations [i].related_value.value.variable.variable; + + area.relations [related_index].relation = MONO_EQ_RELATION; + area.relations [related_index].relation_is_static_definition = TRUE; + area.relations [related_index].related_value.type = MONO_VARIABLE_SUMMARIZED_VALUE; + area.relations [related_index].related_value.value.variable.variable = i; + area.relations [related_index].related_value.value.variable.delta = - area.relations [i].related_value.value.variable.delta; + + area.relations [related_index].next = area.relations [related_variable].next; + area.relations [related_variable].next = &(area.relations [related_index]); + + if (TRACE_ABC_REMOVAL) { + printf ("Added symmetric summarized value for variable variable %d (to %d): ", i, related_variable); + print_summarized_value (&(area.relations [related_index].related_value)); + printf ("\n"); + } + } + } + + process_block (cfg, cfg->bblocks [0], &area); +} + +#else /* !DISABLE_JIT */ + +MONO_EMPTY_SOURCE_FILE (abcremoval); + +#endif /* !DISABLE_JIT */ diff --git a/unity-2019.4.24f1-mbe/mono/mini/abcremoval.h b/unity-2019.4.24f1-mbe/mono/mini/abcremoval.h new file mode 100644 index 000000000..ac52857ea --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/abcremoval.h @@ -0,0 +1,352 @@ +/** + * \file + * Array bounds check removal + * + * Author: + * Massimiliano Mantione (massi@ximian.com) + * + * (C) 2004 Ximian, Inc. http://www.ximian.com + */ + +#ifndef __MONO_ABCREMOVAL_H__ +#define __MONO_ABCREMOVAL_H__ + +#include + +#include "mini.h" + + +/** + * All handled value types (expressions) in variable definitions and branch + * contitions: + * ANY: not handled + * CONSTANT: an integer constant + * VARIABLE: a reference to a variable, with an optional delta (can be zero) + * PHI: a PHI definition of the SSA representation + */ +typedef enum { + MONO_ANY_SUMMARIZED_VALUE, + MONO_CONSTANT_SUMMARIZED_VALUE, + MONO_VARIABLE_SUMMARIZED_VALUE, + MONO_PHI_SUMMARIZED_VALUE +} MonoSummarizedValueType; + +/** + * A MONO_CONSTANT_SUMMARIZED_VALUE value. + * value: the value + */ +typedef struct MonoSummarizedConstantValue { + int value; +} MonoSummarizedConstantValue; + +/** + * A MONO_VARIABLE_SUMMARIZED_VALUE value + * variable: the variable index in the cfg + * delta: the delta (can be zero) + */ +typedef struct MonoSummarizedVariableValue { + int variable; + int delta; +} MonoSummarizedVariableValue; + +/** + * A MONO_PHI_SUMMARIZED_VALUE value. + * number_of_alternatives: the number of alternatives in the PHI definition + * phi_alternatives: an array of integers with the indexes of the variables + * which are the alternatives in this PHI definition + */ +typedef struct MonoSummarizedPhiValue { + int number_of_alternatives; + int *phi_alternatives; +} MonoSummarizedPhiValue; + +/** + * A summarized value. + * In practice it is a "tagged union". + */ +typedef struct MonoSummarizedValue { + MonoSummarizedValueType type; + union { + MonoSummarizedConstantValue constant; + MonoSummarizedVariableValue variable; + MonoSummarizedPhiValue phi; + } value; +} MonoSummarizedValue; + +/** + * A "relation" between two values. + * The enumeration is used as a bit field, with three significant bits. + * The degenerated cases are meaningful: + * MONO_ANY_RELATION: we know nothing of this relation + * MONO_NO_RELATION: no relation is possible (this code is unreachable) + */ +typedef enum { + MONO_EQ_RELATION = 1, + MONO_LT_RELATION = 2, + MONO_GT_RELATION = 4, + MONO_NE_RELATION = (MONO_LT_RELATION|MONO_GT_RELATION), + MONO_LE_RELATION = (MONO_LT_RELATION|MONO_EQ_RELATION), + MONO_GE_RELATION = (MONO_GT_RELATION|MONO_EQ_RELATION), + MONO_ANY_RELATION = (MONO_EQ_RELATION|MONO_LT_RELATION|MONO_GT_RELATION), + MONO_NO_RELATION = 0 +} MonoValueRelation; + +/** + * A "kind" of integer value. + * The enumeration is used as a bit field, with two fields. + * The first, four bits wide, is the "sizeof" in bytes. + * The second is a flag that is true if the value is unsigned. + */ +typedef enum { + MONO_INTEGER_VALUE_SIZE_1 = 1, + MONO_INTEGER_VALUE_SIZE_2 = 2, + MONO_INTEGER_VALUE_SIZE_4 = 4, + MONO_INTEGER_VALUE_SIZE_8 = 8, + MONO_INTEGER_VALUE_SIZE_BITMASK = 15, + MONO_UNSIGNED_VALUE_FLAG = 16, + MONO_UNSIGNED_INTEGER_VALUE_SIZE_1 = MONO_UNSIGNED_VALUE_FLAG|MONO_INTEGER_VALUE_SIZE_1, + MONO_UNSIGNED_INTEGER_VALUE_SIZE_2 = MONO_UNSIGNED_VALUE_FLAG|MONO_INTEGER_VALUE_SIZE_2, + MONO_UNSIGNED_INTEGER_VALUE_SIZE_4 = MONO_UNSIGNED_VALUE_FLAG|MONO_INTEGER_VALUE_SIZE_4, + MONO_UNSIGNED_INTEGER_VALUE_SIZE_8 = MONO_UNSIGNED_VALUE_FLAG|MONO_INTEGER_VALUE_SIZE_8, + MONO_UNKNOWN_INTEGER_VALUE = 0 +} MonoIntegerValueKind; + +/** + * A relation between variables (or a variable and a constant). + * The first variable (the one "on the left of the expression") is implicit. + * relation: the relation between the variable and the value + * related_value: the related value + * relation_is_static_definition: TRUE if the relation comes from a veriable + * definition, FALSE if it comes from a branch + * condition + * next: pointer to the next relation of this variable in the evaluation area + * (relations are always kept in the evaluation area, one list for each + * variable) + */ +typedef struct MonoSummarizedValueRelation { + MonoValueRelation relation; + MonoSummarizedValue related_value; + gboolean relation_is_static_definition; + struct MonoSummarizedValueRelation *next; +} MonoSummarizedValueRelation; + +/** + * The evaluation status for one variable. + * The enumeration is used as a bit field, because the status has two + * distinct sections. + * The first is the "main" one (bits 0, 1 and 2), which is actually a proper + * enumeration (the bits are mutually exclusive, and their meaning is obvious). + * The other section (the bits in the MONO_RELATIONS_EVALUATION_IS_RECURSIVE + * set) is used to mark an evaluation as recursive (while backtracking through + * the evaluation contexts), to state if the graph loop gives a value that is + * ascending, descending or indefinite. + * The bits are handled separately because the same evaluation context could + * belong to more than one loop, so that each loop would set its bits. + * After the backtracking, the bits are examined and a decision is taken. + * + */ +typedef enum { + MONO_RELATIONS_EVALUATION_NOT_STARTED = 0, + MONO_RELATIONS_EVALUATION_IN_PROGRESS = 1, + MONO_RELATIONS_EVALUATION_COMPLETED = 2, + MONO_RELATIONS_EVALUATION_IS_RECURSIVELY_ASCENDING = 4, + MONO_RELATIONS_EVALUATION_IS_RECURSIVELY_DESCENDING = 8, + MONO_RELATIONS_EVALUATION_IS_RECURSIVELY_INDEFINITE = 16, + MONO_RELATIONS_EVALUATION_IS_RECURSIVE = (MONO_RELATIONS_EVALUATION_IS_RECURSIVELY_ASCENDING|MONO_RELATIONS_EVALUATION_IS_RECURSIVELY_DESCENDING|MONO_RELATIONS_EVALUATION_IS_RECURSIVELY_INDEFINITE) +} MonoRelationsEvaluationStatus; + +/** + * A range of values (ranges include their limits). + * A range from MIN_INT to MAX_INT is "indefinite" (any value). + * A range where upper < lower means unreachable code (some of the relations + * that generated the range is incompatible, like x = 0 and x > 0). + * lower: the lower limit + * upper: the upper limit + */ +typedef struct MonoRelationsEvaluationRange { + int lower; + int upper; +} MonoRelationsEvaluationRange; + +/** + * The two ranges that contain the result of a variable evaluation. + * zero: the range with respect to zero + * variable: the range with respect to the target variable in this evaluation + */ +typedef struct MonoRelationsEvaluationRanges { + MonoRelationsEvaluationRange zero; + MonoRelationsEvaluationRange variable; +} MonoRelationsEvaluationRanges; + +/** + * The context of a variable evaluation. + * current_relation: the relation that is currently evaluated. + * ranges: the result of the evaluation. + * father: the context of the evaluation that invoked this one (used to + * perform the backtracking when loops are detected. + */ +typedef struct MonoRelationsEvaluationContext { + MonoSummarizedValueRelation *current_relation; + MonoRelationsEvaluationRanges ranges; + struct MonoRelationsEvaluationContext *father; +} MonoRelationsEvaluationContext; + +/* + * Basic macros to initialize and check ranges. + */ +#define MONO_MAKE_RELATIONS_EVALUATION_RANGE_WEAK(r) do{\ + (r).lower = INT_MIN;\ + (r).upper = INT_MAX;\ + } while (0) +#define MONO_MAKE_RELATIONS_EVALUATION_RANGES_WEAK(rs) do{\ + MONO_MAKE_RELATIONS_EVALUATION_RANGE_WEAK ((rs).zero); \ + MONO_MAKE_RELATIONS_EVALUATION_RANGE_WEAK ((rs).variable); \ + } while (0) +#define MONO_MAKE_RELATIONS_EVALUATION_RANGE_IMPOSSIBLE(r) do{\ + (r).lower = INT_MAX;\ + (r).upper = INT_MIN;\ + } while (0) +#define MONO_MAKE_RELATIONS_EVALUATION_RANGES_IMPOSSIBLE(rs) do{\ + MONO_MAKE_RELATIONS_EVALUATION_RANGE_IMPOSSIBLE ((rs).zero); \ + MONO_MAKE_RELATIONS_EVALUATION_RANGE_IMPOSSIBLE ((rs).variable); \ + } while (0) +#define MONO_RELATIONS_EVALUATION_RANGE_IS_WEAK(r) (((r).lower==INT_MIN)&&((r).upper==INT_MAX)) +#define MONO_RELATIONS_EVALUATION_RANGES_ARE_WEAK(rs) \ + (MONO_RELATIONS_EVALUATION_RANGE_IS_WEAK((rs).zero) && \ + MONO_RELATIONS_EVALUATION_RANGE_IS_WEAK((rs).variable)) +#define MONO_RELATIONS_EVALUATION_RANGE_IS_IMPOSSIBLE(r) (((r).lower)>((r).upper)) +#define MONO_RELATIONS_EVALUATION_RANGES_ARE_IMPOSSIBLE(rs) \ + (MONO_RELATIONS_EVALUATION_RANGE_IS_IMPOSSIBLE((rs).zero) || \ + MONO_RELATIONS_EVALUATION_RANGE_IS_IMPOSSIBLE((rs).variable)) + +/* + * The following macros are needed because ranges include theit limits, but + * some relations explicitly exclude them (GT and LT). + */ +#define MONO_UPPER_EVALUATION_RANGE_NOT_EQUAL(ur) ((((ur)==INT_MIN)||((ur)==INT_MAX))?(ur):((ur)-1)) +#define MONO_LOWER_EVALUATION_RANGE_NOT_EQUAL(lr) ((((lr)==INT_MIN)||((lr)==INT_MAX))?(lr):((lr)+1)) +#define MONO_APPLY_INEQUALITY_TO_EVALUATION_RANGE(r) do{\ + (r).lower = MONO_LOWER_EVALUATION_RANGE_NOT_EQUAL ((r).lower);\ + (r).upper = MONO_UPPER_EVALUATION_RANGE_NOT_EQUAL ((r).upper);\ + } while (0) +#define MONO_APPLY_INEQUALITY_TO_EVALUATION_RANGES(rs) do{\ + MONO_APPLY_INEQUALITY_TO_EVALUATION_RANGE ((rs).zero); \ + MONO_APPLY_INEQUALITY_TO_EVALUATION_RANGE ((rs).variable); \ + } while (0) + +/* + * The following macros perform union and intersection operations on ranges. + */ +#define MONO_LOWER_EVALUATION_RANGE_UNION(lr,other_lr) ((lr)=MIN(lr,other_lr)) +#define MONO_UPPER_EVALUATION_RANGE_UNION(ur,other_ur) ((ur)=MAX(ur,other_ur)) +#define MONO_LOWER_EVALUATION_RANGE_INTERSECTION(lr,other_lr) ((lr)=MAX(lr,other_lr)) +#define MONO_UPPER_EVALUATION_RANGE_INTERSECTION(ur,other_ur) ((ur)=MIN(ur,other_ur)) +#define MONO_RELATIONS_EVALUATION_RANGE_UNION(r,other_r) do{\ + MONO_LOWER_EVALUATION_RANGE_UNION((r).lower,(other_r).lower);\ + MONO_UPPER_EVALUATION_RANGE_UNION((r).upper,(other_r).upper);\ + } while (0) +#define MONO_RELATIONS_EVALUATION_RANGE_INTERSECTION(r,other_r) do{\ + MONO_LOWER_EVALUATION_RANGE_INTERSECTION((r).lower,(other_r).lower);\ + MONO_UPPER_EVALUATION_RANGE_INTERSECTION((r).upper,(other_r).upper);\ + } while (0) +#define MONO_RELATIONS_EVALUATION_RANGES_UNION(rs,other_rs) do{\ + MONO_RELATIONS_EVALUATION_RANGE_UNION ((rs).zero,(other_rs).zero); \ + MONO_RELATIONS_EVALUATION_RANGE_UNION ((rs).variable,(other_rs).variable); \ + } while (0) +#define MONO_RELATIONS_EVALUATION_RANGES_INTERSECTION(rs,other_rs) do{\ + MONO_RELATIONS_EVALUATION_RANGE_INTERSECTION ((rs).zero,(other_rs).zero); \ + MONO_RELATIONS_EVALUATION_RANGE_INTERSECTION ((rs).variable,(other_rs).variable); \ + } while (0) + +/* + * The following macros add or subtract "safely" (without over/under-flow) a + * delta (constant) value to a range. + */ +#define MONO_ADD_DELTA_SAFELY(v,d) do{\ + if (((d) > 0) && ((v) != INT_MIN)) {\ + (v) = (((v)+(d))>(v))?((v)+(d)):INT_MAX;\ + } else if (((d) < 0) && ((v) != INT_MAX)) {\ + (v) = (((v)+(d))<(v))?((v)+(d)):INT_MIN;\ + }\ + } while (0) +#define MONO_SUB_DELTA_SAFELY(v,d) do{\ + if (((d) < 0) && ((v) != INT_MIN)) {\ + (v) = (((v)-(d))>(v))?((v)-(d)):INT_MAX;\ + } else if (((d) > 0) && ((v) != INT_MAX)) {\ + (v) = (((v)-(d))<(v))?((v)-(d)):INT_MIN;\ + }\ + } while (0) +#define MONO_ADD_DELTA_SAFELY_TO_RANGE(r,d) do{\ + MONO_ADD_DELTA_SAFELY((r).lower,(d));\ + MONO_ADD_DELTA_SAFELY((r).upper,(d));\ + } while (0) +#define MONO_SUB_DELTA_SAFELY_FROM_RANGE(r,d) do{\ + MONO_SUB_DELTA_SAFELY((r).lower,(d));\ + MONO_SUB_DELTA_SAFELY((r).upper,(d));\ + } while (0) +#define MONO_ADD_DELTA_SAFELY_TO_RANGES(rs,d) do{\ + MONO_ADD_DELTA_SAFELY_TO_RANGE((rs).zero,(d));\ + MONO_ADD_DELTA_SAFELY_TO_RANGE((rs).variable,(d));\ + } while (0) +#define MONO_SUB_DELTA_SAFELY_FROM_RANGES(rs,d) do{\ + MONO_SUB_DELTA_SAFELY_FROM_RANGE((rs).zero,(d));\ + MONO_SUB_DELTA_SAFELY_FROM_RANGE((rs).variable,(d));\ + } while (0) + + +/** + * The main evaluation area. + * cfg: the cfg of the method that is being examined. + * relations: and array of relations, one for each method variable (each + * relation is the head of a list); this is the evaluation graph + * contexts: an array of evaluation contexts (one for each method variable) + * variable_value_kind: an array of MonoIntegerValueKind, one for each local + * variable (or argument) + * defs: maps vregs to the instruction which defines it. + */ +typedef struct MonoVariableRelationsEvaluationArea { + MonoCompile *cfg; + MonoSummarizedValueRelation *relations; + +/** + * statuses and contexts are parallel arrays. A given index into each refers to + * the same context. This is a performance optimization. Clean_context was + * coming to dominate the running time of abcremoval. By + * storing the statuses together, we can memset the entire + * region. + */ + MonoRelationsEvaluationStatus *statuses; + MonoRelationsEvaluationContext *contexts; + + MonoIntegerValueKind *variable_value_kind; + MonoInst **defs; +} MonoVariableRelationsEvaluationArea; + +/** + * Convenience structure to define an "additional" relation for the main + * evaluation graph. + * variable: the variable to which the relation is applied + * relation: the relation + * insertion_point: the point in the graph where the relation is inserted + * (useful for removing it from the list when backtracking + * in the traversal of the dominator tree) + */ +typedef struct MonoAdditionalVariableRelation { + int variable; + MonoSummarizedValueRelation relation; + MonoSummarizedValueRelation *insertion_point; +} MonoAdditionalVariableRelation; + +/** + * Convenience structure that stores two additional relations. + * In the current code, each BB can add at most two relations to the main + * evaluation graph, so one of these structures is enough to hold all the + * modifications to the graph made examining one BB. + */ +typedef struct MonoAdditionalVariableRelationsForBB { + MonoAdditionalVariableRelation relation1; + MonoAdditionalVariableRelation relation2; +} MonoAdditionalVariableRelationsForBB; + + +#endif /* __MONO_ABCREMOVAL_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/mini/alias-analysis.c b/unity-2019.4.24f1-mbe/mono/mini/alias-analysis.c new file mode 100644 index 000000000..89c1c8595 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/alias-analysis.c @@ -0,0 +1,378 @@ +/** + * \file + * Implement simple alias analysis for local variables. + * + * Author: + * Rodrigo Kumpera (kumpera@gmail.com) + * + * (C) 2013 Xamarin + */ + +#include +#include + +#include "mini.h" +#include "ir-emit.h" +#include "glib.h" +#include + +#ifndef DISABLE_JIT + +static gboolean +is_int_stack_size (int type) +{ +#if SIZEOF_VOID_P == 4 + return type == STACK_I4 || type == STACK_MP; +#else + return type == STACK_I4; +#endif +} + +static gboolean +is_long_stack_size (int type) +{ +#if SIZEOF_VOID_P == 8 + return type == STACK_I8 || type == STACK_MP; +#else + return type == STACK_I8; +#endif +} + + +static gboolean +lower_load (MonoCompile *cfg, MonoInst *load, MonoInst *ldaddr) +{ + MonoInst *var = (MonoInst *)ldaddr->inst_p0; + MonoType *type = &var->klass->byval_arg; + int replaced_op = mono_type_to_load_membase (cfg, type); + + if (load->opcode == OP_LOADV_MEMBASE && load->klass != var->klass) { + if (cfg->verbose_level > 2) + printf ("Incompatible load_vtype classes %s x %s\n", load->klass->name, var->klass->name); + return FALSE; + } + + if (replaced_op != load->opcode) { + if (cfg->verbose_level > 2) + printf ("Incompatible load type: expected %s but got %s\n", + mono_inst_name (replaced_op), + mono_inst_name (load->opcode)); + return FALSE; + } else { + if (cfg->verbose_level > 2) { printf ("mem2reg replacing: "); mono_print_ins (load); } + } + + load->opcode = mono_type_to_regmove (cfg, type); + type_to_eval_stack_type (cfg, type, load); + load->sreg1 = var->dreg; + mono_atomic_inc_i32 (&mono_jit_stats.loads_eliminated); + return TRUE; +} + +static gboolean +lower_store (MonoCompile *cfg, MonoInst *store, MonoInst *ldaddr) +{ + MonoInst *var = (MonoInst *)ldaddr->inst_p0; + MonoType *type = &var->klass->byval_arg; + int replaced_op = mono_type_to_store_membase (cfg, type); + + if (store->opcode == OP_STOREV_MEMBASE && store->klass != var->klass) { + if (cfg->verbose_level > 2) + printf ("Incompatible store_vtype classes %s x %s\n", store->klass->name, store->klass->name); + return FALSE; + } + + + if (replaced_op != store->opcode) { + if (cfg->verbose_level > 2) + printf ("Incompatible store_reg type: expected %s but got %s\n", + mono_inst_name (replaced_op), + mono_inst_name (store->opcode)); + return FALSE; + } else { + if (cfg->verbose_level > 2) { printf ("mem2reg replacing: "); mono_print_ins (store); } + } + + store->opcode = mono_type_to_regmove (cfg, type); + type_to_eval_stack_type (cfg, type, store); + store->dreg = var->dreg; + mono_atomic_inc_i32 (&mono_jit_stats.stores_eliminated); + return TRUE; +} + +static gboolean +lower_store_imm (MonoCompile *cfg, MonoInst *store, MonoInst *ldaddr) +{ + MonoInst *var = (MonoInst *)ldaddr->inst_p0; + MonoType *type = &var->klass->byval_arg; + int store_op = mono_type_to_store_membase (cfg, type); + if (store_op == OP_STOREV_MEMBASE || store_op == OP_STOREX_MEMBASE) + return FALSE; + + switch (store->opcode) { +#if SIZEOF_VOID_P == 4 + case OP_STORE_MEMBASE_IMM: +#endif + case OP_STOREI4_MEMBASE_IMM: + if (!is_int_stack_size (var->type)) { + if (cfg->verbose_level > 2) printf ("Incompatible variable of size != 4\n"); + return FALSE; + } + if (cfg->verbose_level > 2) { printf ("mem2reg replacing: "); mono_print_ins (store); } + store->opcode = OP_ICONST; + store->type = STACK_I4; + store->dreg = var->dreg; + store->inst_c0 = store->inst_imm; + break; + +#if SIZEOF_VOID_P == 8 + case OP_STORE_MEMBASE_IMM: +#endif + case OP_STOREI8_MEMBASE_IMM: + if (!is_long_stack_size (var->type)) { + if (cfg->verbose_level > 2) printf ("Incompatible variable of size != 8\n"); + return FALSE; + } + if (cfg->verbose_level > 2) { printf ("mem2reg replacing: "); mono_print_ins (store); } + store->opcode = OP_I8CONST; + store->type = STACK_I8; + store->dreg = var->dreg; + store->inst_l = store->inst_imm; + break; + default: + return FALSE; + } + mono_atomic_inc_i32 (&mono_jit_stats.stores_eliminated); + return TRUE; +} + +static gboolean +lower_memory_access (MonoCompile *cfg) +{ + MonoBasicBlock *bb; + MonoInst *ins, *tmp; + gboolean needs_dce = FALSE; + GHashTable *addr_loads = g_hash_table_new (NULL, NULL); + //FIXME optimize + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { + g_hash_table_remove_all (addr_loads); + + for (ins = bb->code; ins; ins = ins->next) { +handle_instruction: + switch (ins->opcode) { + case OP_LDADDR: { + MonoInst *var = (MonoInst*)ins->inst_p0; + if (var->flags & MONO_INST_VOLATILE) { + if (cfg->verbose_level > 2) { printf ("Found address to volatile var, can't take it: "); mono_print_ins (ins); } + } else { + g_hash_table_insert (addr_loads, GINT_TO_POINTER (ins->dreg), ins); + if (cfg->verbose_level > 2) { printf ("New address: "); mono_print_ins (ins); } + } + break; + } + + case OP_MOVE: + tmp = (MonoInst*)g_hash_table_lookup (addr_loads, GINT_TO_POINTER (ins->sreg1)); + /* + Forward propagate known aliases + ldaddr R10 <- R8 + mov R11 <- R10 + */ + if (tmp) { + g_hash_table_insert (addr_loads, GINT_TO_POINTER (ins->dreg), tmp); + if (cfg->verbose_level > 2) { printf ("New alias: "); mono_print_ins (ins); } + } else { + /* + Source value is not a know address, kill the variable. + */ + if (g_hash_table_remove (addr_loads, GINT_TO_POINTER (ins->dreg))) { + if (cfg->verbose_level > 2) { printf ("Killed alias: "); mono_print_ins (ins); } + } + } + break; + + case OP_LOADV_MEMBASE: + case OP_LOAD_MEMBASE: + case OP_LOADU1_MEMBASE: + case OP_LOADI2_MEMBASE: + case OP_LOADU2_MEMBASE: + case OP_LOADI4_MEMBASE: + case OP_LOADU4_MEMBASE: + case OP_LOADI1_MEMBASE: + case OP_LOADI8_MEMBASE: +#ifndef MONO_ARCH_SOFT_FLOAT_FALLBACK + case OP_LOADR4_MEMBASE: +#endif + case OP_LOADR8_MEMBASE: + if (ins->inst_offset != 0) + continue; + tmp = (MonoInst *)g_hash_table_lookup (addr_loads, GINT_TO_POINTER (ins->sreg1)); + if (tmp) { + if (cfg->verbose_level > 2) { printf ("Found candidate load:"); mono_print_ins (ins); } + if (lower_load (cfg, ins, tmp)) { + needs_dce = TRUE; + /* Try to propagate known aliases if an OP_MOVE was inserted */ + goto handle_instruction; + } + } + break; + + case OP_STORE_MEMBASE_REG: + case OP_STOREI1_MEMBASE_REG: + case OP_STOREI2_MEMBASE_REG: + case OP_STOREI4_MEMBASE_REG: + case OP_STOREI8_MEMBASE_REG: +#ifndef MONO_ARCH_SOFT_FLOAT_FALLBACK + case OP_STORER4_MEMBASE_REG: +#endif + case OP_STORER8_MEMBASE_REG: + case OP_STOREV_MEMBASE: + if (ins->inst_offset != 0) + continue; + tmp = (MonoInst *)g_hash_table_lookup (addr_loads, GINT_TO_POINTER (ins->dreg)); + if (tmp) { + if (cfg->verbose_level > 2) { printf ("Found candidate store:"); mono_print_ins (ins); } + if (lower_store (cfg, ins, tmp)) { + needs_dce = TRUE; + /* Try to propagate known aliases if an OP_MOVE was inserted */ + goto handle_instruction; + } + } + break; + //FIXME missing storei1_membase_imm and storei2_membase_imm + case OP_STORE_MEMBASE_IMM: + case OP_STOREI4_MEMBASE_IMM: + case OP_STOREI8_MEMBASE_IMM: + if (ins->inst_offset != 0) + continue; + tmp = (MonoInst *)g_hash_table_lookup (addr_loads, GINT_TO_POINTER (ins->dreg)); + if (tmp) { + if (cfg->verbose_level > 2) { printf ("Found candidate store-imm:"); mono_print_ins (ins); } + needs_dce |= lower_store_imm (cfg, ins, tmp); + } + break; + case OP_CHECK_THIS: + case OP_NOT_NULL: + tmp = (MonoInst *)g_hash_table_lookup (addr_loads, GINT_TO_POINTER (ins->sreg1)); + if (tmp) { + if (cfg->verbose_level > 2) { printf ("Found null check over local: "); mono_print_ins (ins); } + NULLIFY_INS (ins); + needs_dce = TRUE; + } + break; + } + } + } + g_hash_table_destroy (addr_loads); + return needs_dce; +} + +static gboolean +recompute_aliased_variables (MonoCompile *cfg, int *restored_vars) +{ + int i; + MonoBasicBlock *bb; + MonoInst *ins; + int kills = 0; + int adds = 0; + *restored_vars = 0; + + for (i = 0; i < cfg->num_varinfo; i++) { + MonoInst *var = cfg->varinfo [i]; + if (var->flags & MONO_INST_INDIRECT) { + if (cfg->verbose_level > 2) { + printf ("Killing :"); mono_print_ins (var); + } + ++kills; + } + var->flags &= ~MONO_INST_INDIRECT; + } + + if (!kills) + return FALSE; + + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { + for (ins = bb->code; ins; ins = ins->next) { + if (ins->opcode == OP_LDADDR) { + MonoInst *var; + + if (cfg->verbose_level > 2) { printf ("Found op :"); mono_print_ins (ins); } + + var = (MonoInst*)ins->inst_p0; + if (!(var->flags & MONO_INST_INDIRECT)) { + if (cfg->verbose_level > 1) { printf ("Restoring :"); mono_print_ins (var); } + ++adds; + } + var->flags |= MONO_INST_INDIRECT; + } + } + } + *restored_vars = adds; + + mono_atomic_fetch_add_i32 (&mono_jit_stats.alias_found, kills); + mono_atomic_fetch_add_i32 (&mono_jit_stats.alias_removed, kills - adds); + if (kills > adds) { + if (cfg->verbose_level > 2) { + printf ("Method: %s\n", mono_method_full_name (cfg->method, 1)); + printf ("Kills %d Adds %d\n", kills, adds); + } + return TRUE; + } + return FALSE; +} + +/* +FIXME: + Don't DCE on the whole CFG, only the BBs that have changed. + +TODO: + SRVT of small types can fix cases of mismatch for fields of a different type than the component. + Handle aliasing of byrefs in call conventions. +*/ +void +mono_local_alias_analysis (MonoCompile *cfg) +{ + int i, restored_vars = 1; + if (!cfg->has_indirection) + return; + + if (cfg->verbose_level > 2) + mono_print_code (cfg, "BEFORE ALIAS_ANALYSIS"); + + /* + Remove indirection and memory access of known variables. + */ + if (!lower_memory_access (cfg)) + goto done; + + /* + By replacing indirect access with direct operations, some LDADDR ops become dead. Kill them. + */ + if (cfg->opt & MONO_OPT_DEADCE) + mono_local_deadce (cfg); + + /* + Some variables no longer need to be flagged as indirect, find them. + Since indirect vars are converted into global vregs, each pass eliminates only one level of indirection. + Most cases only need one pass and some 2. + */ + for (i = 0; i < 3 && restored_vars > 0 && recompute_aliased_variables (cfg, &restored_vars); ++i) { + /* + A lot of simplification just took place, we recompute local variables and do DCE to + really profit from the previous gains + */ + mono_handle_global_vregs (cfg); + if (cfg->opt & MONO_OPT_DEADCE) + mono_local_deadce (cfg); + } + +done: + if (cfg->verbose_level > 2) + mono_print_code (cfg, "AFTER ALIAS_ANALYSIS"); +} + +#else /* !DISABLE_JIT */ + +MONO_EMPTY_SOURCE_FILE (alias_analysis); + +#endif /* !DISABLE_JIT */ diff --git a/unity-2019.4.24f1-mbe/mono/mini/aot-compiler.c b/unity-2019.4.24f1-mbe/mono/mini/aot-compiler.c new file mode 100644 index 000000000..07ef2b70b --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/aot-compiler.c @@ -0,0 +1,12951 @@ +/** + * \file + * mono Ahead of Time compiler + * + * Author: + * Dietmar Maurer (dietmar@ximian.com) + * Zoltan Varga (vargaz@gmail.com) + * Johan Lorensson (lateralusx.github@gmail.com) + * + * (C) 2002 Ximian, Inc. + * Copyright 2003-2011 Novell, Inc + * Copyright 2011 Xamarin Inc (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include "config.h" +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_STDINT_H +#include +#endif +#include +#include +#include +#ifndef HOST_WIN32 +#include +#else +#include +#include +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "aot-compiler.h" +#include "aot-runtime.h" +#include "seq-points.h" +#include "image-writer.h" +#include "dwarfwriter.h" +#include "mini-gc.h" +#include "mini-llvm.h" +#include "mini-runtime.h" + +#if !defined(DISABLE_AOT) && !defined(DISABLE_JIT) + +// Use MSVC toolchain, Clang for MSVC using MSVC codegen and linker, when compiling for AMD64 +// targeting WIN32 platforms running AOT compiler on WIN32 platform with VS installation. +#if defined(TARGET_AMD64) && defined(TARGET_WIN32) && defined(HOST_WIN32) && defined(_MSC_VER) +#define TARGET_X86_64_WIN32_MSVC +#endif + +#if defined(TARGET_X86_64_WIN32_MSVC) +#define TARGET_WIN32_MSVC +#endif + +// Emit native unwind info on Windows platforms (different from DWARF). Emitted unwind info +// works when using the MSVC toolchain using Clang for MSVC codegen and linker. Only supported when +// compiling for AMD64 (Windows x64 platforms). +#if defined(TARGET_WIN32_MSVC) && defined(MONO_ARCH_HAVE_UNWIND_TABLE) +#define EMIT_WIN32_UNWIND_INFO +#endif + +#if defined(__linux__) +#define RODATA_SECT ".rodata" +#elif defined(TARGET_MACH) +#define RODATA_SECT ".section __TEXT, __const" +#elif defined(TARGET_WIN32_MSVC) +#define RODATA_SECT ".rdata" +#else +#define RODATA_SECT ".text" +#endif + +#define TV_DECLARE(name) gint64 name +#define TV_GETTIME(tv) tv = mono_100ns_ticks () +#define TV_ELAPSED(start,end) (((end) - (start)) / 10) + +#define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1)) +#define ALIGN_PTR_TO(ptr,align) (gpointer)((((gssize)(ptr)) + (align - 1)) & (~(align - 1))) +#define ROUND_DOWN(VALUE,SIZE) ((VALUE) & ~((SIZE) - 1)) + +typedef struct { + char *name; + MonoImage *image; +} ImageProfileData; + +typedef struct ClassProfileData ClassProfileData; + +typedef struct { + int argc; + ClassProfileData **argv; + MonoGenericInst *inst; +} GInstProfileData; + +struct ClassProfileData { + ImageProfileData *image; + char *ns, *name; + GInstProfileData *inst; + MonoClass *klass; +}; + +typedef struct { + ClassProfileData *klass; + int id; + char *name; + int param_count; + char *signature; + GInstProfileData *inst; + MonoMethod *method; +} MethodProfileData; + +typedef struct { + GHashTable *images, *classes, *ginsts, *methods; +} ProfileData; + +/* predefined values for static readonly fields without needed to run the .cctor */ +typedef struct _ReadOnlyValue ReadOnlyValue; +struct _ReadOnlyValue { + ReadOnlyValue *next; + char *name; + int type; /* to be used later for typechecking to prevent user errors */ + union { + guint8 i1; + guint16 i2; + guint32 i4; + guint64 i8; + gpointer ptr; + } value; +}; +static ReadOnlyValue *readonly_values; + +typedef struct MonoAotOptions { + char *outfile; + char *llvm_outfile; + char *data_outfile; + GList *profile_files; + gboolean save_temps; + gboolean write_symbols; + gboolean metadata_only; + gboolean bind_to_runtime_version; + MonoAotMode mode; + gboolean no_dlsym; + gboolean static_link; + gboolean asm_only; + gboolean asm_writer; + gboolean nodebug; + gboolean dwarf_debug; + gboolean soft_debug; + gboolean log_generics; + gboolean log_instances; + gboolean gen_msym_dir; + char *gen_msym_dir_path; + gboolean direct_pinvoke; + gboolean direct_icalls; + gboolean no_direct_calls; + gboolean use_trampolines_page; + gboolean no_instances; + // We are collecting inflated methods and emitting non-inflated + gboolean dedup; + // The name of the assembly for which the AOT module is going to have all deduped methods moved to. + // When set, we are emitting inflated methods only + char *dedup_include; + gboolean gnu_asm; + gboolean llvm; + gboolean llvm_only; + int nthreads; + int ntrampolines; + int nrgctx_trampolines; + int nimt_trampolines; + int ngsharedvt_arg_trampolines; + int nrgctx_fetch_trampolines; + gboolean print_skipped_methods; + gboolean stats; + gboolean verbose; + char *tool_prefix; + char *ld_flags; + char *mtriple; + char *llvm_path; + char *temp_path; + char *instances_logfile_path; + char *logfile; + gboolean dump_json; + gboolean profile_only; +} MonoAotOptions; + +typedef enum { + METHOD_CAT_NORMAL, + METHOD_CAT_GSHAREDVT, + METHOD_CAT_INST, + METHOD_CAT_WRAPPER, + METHOD_CAT_NUM +} MethodCategory; + +typedef struct MonoAotStats { + int ccount, mcount, lmfcount, abscount, gcount, ocount, genericcount; + gint64 code_size, info_size, ex_info_size, unwind_info_size, got_size, class_info_size, got_info_size, plt_size; + int methods_without_got_slots, direct_calls, all_calls, llvm_count; + int got_slots, offsets_size; + int method_categories [METHOD_CAT_NUM]; + int got_slot_types [MONO_PATCH_INFO_NUM]; + int got_slot_info_sizes [MONO_PATCH_INFO_NUM]; + int jit_time, gen_time, link_time; +} MonoAotStats; + +typedef struct GotInfo { + GHashTable *patch_to_got_offset; + GHashTable **patch_to_got_offset_by_type; + GPtrArray *got_patches; +} GotInfo; + +#ifdef EMIT_WIN32_UNWIND_INFO +typedef struct _UnwindInfoSectionCacheItem { + char *xdata_section_label; + PUNWIND_INFO unwind_info; + gboolean xdata_section_emitted; +} UnwindInfoSectionCacheItem; +#endif + +typedef struct MonoAotCompile { + MonoImage *image; + GPtrArray *methods; + GHashTable *method_indexes; + GHashTable *method_depth; + MonoCompile **cfgs; + int cfgs_size; + GHashTable **patch_to_plt_entry; + GHashTable *plt_offset_to_entry; + //GHashTable *patch_to_got_offset; + //GHashTable **patch_to_got_offset_by_type; + //GPtrArray *got_patches; + GotInfo got_info, llvm_got_info; + GHashTable *image_hash; + GHashTable *method_to_cfg; + GHashTable *token_info_hash; + GHashTable *method_to_pinvoke_import; + GPtrArray *extra_methods; + GPtrArray *image_table; + GPtrArray *globals; + GPtrArray *method_order; + GHashTable *dedup_stats; + GHashTable *dedup_cache; + gboolean dedup_cache_changed; + GHashTable *export_names; + /* Maps MonoClass* -> blob offset */ + GHashTable *klass_blob_hash; + /* Maps MonoMethod* -> blob offset */ + GHashTable *method_blob_hash; + GHashTable *gsharedvt_in_signatures; + GHashTable *gsharedvt_out_signatures; + guint32 *plt_got_info_offsets; + guint32 got_offset, llvm_got_offset, plt_offset, plt_got_offset_base, nshared_got_entries; + /* Number of GOT entries reserved for trampolines */ + guint32 num_trampoline_got_entries; + guint32 tramp_page_size; + + guint32 table_offsets [MONO_AOT_TABLE_NUM]; + guint32 num_trampolines [MONO_AOT_TRAMP_NUM]; + guint32 trampoline_got_offset_base [MONO_AOT_TRAMP_NUM]; + guint32 trampoline_size [MONO_AOT_TRAMP_NUM]; + guint32 tramp_page_code_offsets [MONO_AOT_TRAMP_NUM]; + + MonoAotOptions aot_opts; + guint32 nmethods; + guint32 opts; + guint32 simd_opts; + MonoMemPool *mempool; + MonoAotStats stats; + int method_index; + char *static_linking_symbol; + mono_mutex_t mutex; + gboolean gas_line_numbers; + /* Whenever to emit an object file directly from llc */ + gboolean llvm_owriter; + gboolean llvm_owriter_supported; + MonoImageWriter *w; + MonoDwarfWriter *dwarf; + FILE *fp; + char *tmpbasename; + char *tmpfname; + char *llvm_sfile; + char *llvm_ofile; + GSList *cie_program; + GHashTable *unwind_info_offsets; + GPtrArray *unwind_ops; + guint32 unwind_info_offset; + char *global_prefix; + char *got_symbol; + char *llvm_got_symbol; + char *plt_symbol; + char *llvm_eh_frame_symbol; + GHashTable *method_label_hash; + const char *temp_prefix; + const char *user_symbol_prefix; + const char *llvm_label_prefix; + const char *inst_directive; + int align_pad_value; + guint32 label_generator; + gboolean llvm; + gboolean has_jitted_code; + gboolean is_full_aot; + MonoAotFileFlags flags; + MonoDynamicStream blob; + gboolean blob_closed; + GHashTable *typespec_classes; + GString *llc_args; + GString *as_args; + char *assembly_name_sym; + GHashTable *plt_entry_debug_sym_cache; + gboolean thumb_mixed, need_no_dead_strip, need_pt_gnu_stack; + GHashTable *ginst_hash; + GHashTable *dwarf_ln_filenames; + gboolean global_symbols; + int objc_selector_index, objc_selector_index_2; + GPtrArray *objc_selectors; + GHashTable *objc_selector_to_index; + GList *profile_data; + GHashTable *profile_methods; +#ifdef EMIT_WIN32_UNWIND_INFO + GList *unwind_info_section_cache; +#endif + FILE *logfile; + FILE *instances_logfile; + FILE *data_outfile; + int datafile_offset; + int gc_name_offset; + // In this mode, we are emitting dedupable methods that we encounter + gboolean dedup_emit_mode; +} MonoAotCompile; + +typedef struct { + int plt_offset; + char *symbol, *llvm_symbol, *debug_sym; + MonoJumpInfo *ji; + gboolean jit_used, llvm_used; +} MonoPltEntry; + +#define mono_acfg_lock(acfg) mono_os_mutex_lock (&((acfg)->mutex)) +#define mono_acfg_unlock(acfg) mono_os_mutex_unlock (&((acfg)->mutex)) + +/* This points to the current acfg in LLVM mode */ +static MonoAotCompile *llvm_acfg; + +#ifdef HAVE_ARRAY_ELEM_INIT +#define MSGSTRFIELD(line) MSGSTRFIELD1(line) +#define MSGSTRFIELD1(line) str##line +static const struct msgstr_t { +#define PATCH_INFO(a,b) char MSGSTRFIELD(__LINE__) [sizeof (b)]; +#include "patch-info.h" +#undef PATCH_INFO +} opstr = { +#define PATCH_INFO(a,b) b, +#include "patch-info.h" +#undef PATCH_INFO +}; +static const gint16 opidx [] = { +#define PATCH_INFO(a,b) [MONO_PATCH_INFO_ ## a] = offsetof (struct msgstr_t, MSGSTRFIELD(__LINE__)), +#include "patch-info.h" +#undef PATCH_INFO +}; + +static G_GNUC_UNUSED const char* +get_patch_name (int info) +{ + return (const char*)&opstr + opidx [info]; +} + +#else +#define PATCH_INFO(a,b) b, +static const char* const +patch_types [MONO_PATCH_INFO_NUM + 1] = { +#include "patch-info.h" + NULL +}; + +static G_GNUC_UNUSED const char* +get_patch_name (int info) +{ + return patch_types [info]; +} + +#endif + +static void +mono_flush_method_cache (MonoAotCompile *acfg); + +static void +mono_read_method_cache (MonoAotCompile *acfg); + +static guint32 +get_unwind_info_offset (MonoAotCompile *acfg, guint8 *encoded, guint32 encoded_len); + +static char* +get_plt_entry_debug_sym (MonoAotCompile *acfg, MonoJumpInfo *ji, GHashTable *cache); + +static void +add_gsharedvt_wrappers (MonoAotCompile *acfg, MonoMethodSignature *sig, gboolean gsharedvt_in, gboolean gsharedvt_out); + +static void +add_profile_instances (MonoAotCompile *acfg, ProfileData *data); + +static inline gboolean +ignore_cfg (MonoCompile *cfg) +{ + return !cfg || cfg->skip; +} + +static void +aot_printf (MonoAotCompile *acfg, const gchar *format, ...) +{ + FILE *output; + va_list args; + + if (acfg->logfile) + output = acfg->logfile; + else + output = stdout; + + va_start (args, format); + vfprintf (output, format, args); + va_end (args); +} + +static void +aot_printerrf (MonoAotCompile *acfg, const gchar *format, ...) +{ + FILE *output; + va_list args; + + if (acfg->logfile) + output = acfg->logfile; + else + output = stderr; + + va_start (args, format); + vfprintf (output, format, args); + va_end (args); +} + +static void +report_loader_error (MonoAotCompile *acfg, MonoError *error, gboolean fatal, const char *format, ...) +{ + FILE *output; + va_list args; + + if (mono_error_ok (error)) + return; + + if (acfg->logfile) + output = acfg->logfile; + else + output = stderr; + + va_start (args, format); + vfprintf (output, format, args); + va_end (args); + mono_error_cleanup (error); + + if (acfg->is_full_aot && fatal) { + fprintf (output, "FullAOT cannot continue if there are loader errors.\n"); + exit (1); + } +} + +/* Wrappers around the image writer functions */ + +#define MAX_SYMBOL_SIZE 256 + +static inline const char * +mangle_symbol (const char * symbol, char * mangled_symbol, gsize length) +{ + gsize needed_size = length; + + g_assert (NULL != symbol); + g_assert (NULL != mangled_symbol); + g_assert (0 != length); + +#if defined(TARGET_WIN32) && defined(TARGET_X86) + if (symbol && '_' != symbol [0]) { + needed_size = g_snprintf (mangled_symbol, length, "_%s", symbol); + } else { + needed_size = g_snprintf (mangled_symbol, length, "%s", symbol); + } +#else + needed_size = g_snprintf (mangled_symbol, length, "%s", symbol); +#endif + + g_assert (0 <= needed_size && needed_size < length); + return mangled_symbol; +} + +static inline char * +mangle_symbol_alloc (const char * symbol) +{ + g_assert (NULL != symbol); + +#if defined(TARGET_WIN32) && defined(TARGET_X86) + if (symbol && '_' != symbol [0]) { + return g_strdup_printf ("_%s", symbol); + } + else { + return g_strdup_printf ("%s", symbol); + } +#else + return g_strdup_printf ("%s", symbol); +#endif +} + +static inline void +emit_section_change (MonoAotCompile *acfg, const char *section_name, int subsection_index) +{ + mono_img_writer_emit_section_change (acfg->w, section_name, subsection_index); +} + +#if defined(TARGET_WIN32) && defined(TARGET_X86) + +static inline void +emit_local_symbol (MonoAotCompile *acfg, const char *name, const char *end_label, gboolean func) +{ + const char * mangled_symbol_name = name; + char * mangled_symbol_name_alloc = NULL; + + if (TRUE == func) { + mangled_symbol_name_alloc = mangle_symbol_alloc (name); + mangled_symbol_name = mangled_symbol_name_alloc; + } + + if (name != mangled_symbol_name && 0 != g_strcasecmp (name, mangled_symbol_name)) { + mono_img_writer_emit_label (acfg->w, mangled_symbol_name); + } + mono_img_writer_emit_local_symbol (acfg->w, mangled_symbol_name, end_label, func); + + if (NULL != mangled_symbol_name_alloc) { + g_free (mangled_symbol_name_alloc); + } +} + +#else + +static inline void +emit_local_symbol (MonoAotCompile *acfg, const char *name, const char *end_label, gboolean func) +{ + mono_img_writer_emit_local_symbol (acfg->w, name, end_label, func); +} + +#endif + +static inline void +emit_label (MonoAotCompile *acfg, const char *name) +{ + mono_img_writer_emit_label (acfg->w, name); +} + +static inline void +emit_bytes (MonoAotCompile *acfg, const guint8* buf, int size) +{ + mono_img_writer_emit_bytes (acfg->w, buf, size); +} + +static inline void +emit_string (MonoAotCompile *acfg, const char *value) +{ + mono_img_writer_emit_string (acfg->w, value); +} + +static inline void +emit_line (MonoAotCompile *acfg) +{ + mono_img_writer_emit_line (acfg->w); +} + +static inline void +emit_alignment (MonoAotCompile *acfg, int size) +{ + mono_img_writer_emit_alignment (acfg->w, size); +} + +static inline void +emit_alignment_code (MonoAotCompile *acfg, int size) +{ + if (acfg->align_pad_value) + mono_img_writer_emit_alignment_fill (acfg->w, size, acfg->align_pad_value); + else + mono_img_writer_emit_alignment (acfg->w, size); +} + +static inline void +emit_padding (MonoAotCompile *acfg, int size) +{ + int i; + guint8 buf [16]; + + if (acfg->align_pad_value) { + for (i = 0; i < 16; ++i) + buf [i] = acfg->align_pad_value; + } else { + memset (buf, 0, sizeof (buf)); + } + + for (i = 0; i < size; i += 16) { + if (size - i < 16) + emit_bytes (acfg, buf, size - i); + else + emit_bytes (acfg, buf, 16); + } +} + +static inline void +emit_pointer (MonoAotCompile *acfg, const char *target) +{ + mono_img_writer_emit_pointer (acfg->w, target); +} + +static inline void +emit_pointer_2 (MonoAotCompile *acfg, const char *prefix, const char *target) +{ + if (prefix [0] != '\0') { + char *s = g_strdup_printf ("%s%s", prefix, target); + mono_img_writer_emit_pointer (acfg->w, s); + g_free (s); + } else { + mono_img_writer_emit_pointer (acfg->w, target); + } +} + +static inline void +emit_int16 (MonoAotCompile *acfg, int value) +{ + mono_img_writer_emit_int16 (acfg->w, value); +} + +static inline void +emit_int32 (MonoAotCompile *acfg, int value) +{ + mono_img_writer_emit_int32 (acfg->w, value); +} + +static inline void +emit_symbol_diff (MonoAotCompile *acfg, const char *end, const char* start, int offset) +{ + mono_img_writer_emit_symbol_diff (acfg->w, end, start, offset); +} + +static inline void +emit_zero_bytes (MonoAotCompile *acfg, int num) +{ + mono_img_writer_emit_zero_bytes (acfg->w, num); +} + +static inline void +emit_byte (MonoAotCompile *acfg, guint8 val) +{ + mono_img_writer_emit_byte (acfg->w, val); +} + +#if defined(TARGET_WIN32) && defined(TARGET_X86) + +static G_GNUC_UNUSED void +emit_global_inner (MonoAotCompile *acfg, const char *name, gboolean func) +{ + const char * mangled_symbol_name = name; + char * mangled_symbol_name_alloc = NULL; + + mangled_symbol_name_alloc = mangle_symbol_alloc (name); + mangled_symbol_name = mangled_symbol_name_alloc; + + if (0 != g_strcasecmp (name, mangled_symbol_name)) { + mono_img_writer_emit_label (acfg->w, mangled_symbol_name); + } + mono_img_writer_emit_global (acfg->w, mangled_symbol_name, func); + + if (NULL != mangled_symbol_name_alloc) { + g_free (mangled_symbol_name_alloc); + } +} + +#else + +static G_GNUC_UNUSED void +emit_global_inner (MonoAotCompile *acfg, const char *name, gboolean func) +{ + mono_img_writer_emit_global (acfg->w, name, func); +} + +#endif + +static inline gboolean +link_shared_library (MonoAotCompile *acfg) +{ + return !acfg->aot_opts.static_link && !acfg->aot_opts.asm_only; +} + +static inline gboolean +add_to_global_symbol_table (MonoAotCompile *acfg) +{ +#ifdef TARGET_WIN32_MSVC + return acfg->aot_opts.no_dlsym || link_shared_library (acfg); +#else + return acfg->aot_opts.no_dlsym; +#endif +} + +static void +emit_global (MonoAotCompile *acfg, const char *name, gboolean func) +{ + if (add_to_global_symbol_table (acfg)) + g_ptr_array_add (acfg->globals, g_strdup (name)); + + if (acfg->aot_opts.no_dlsym) { + mono_img_writer_emit_local_symbol (acfg->w, name, NULL, func); + } else { + emit_global_inner (acfg, name, func); + } +} + +static void +emit_symbol_size (MonoAotCompile *acfg, const char *name, const char *end_label) +{ + mono_img_writer_emit_symbol_size (acfg->w, name, end_label); +} + +/* Emit a symbol which is referenced by the MonoAotFileInfo structure */ +static void +emit_info_symbol (MonoAotCompile *acfg, const char *name) +{ + char symbol [MAX_SYMBOL_SIZE]; + + if (acfg->llvm) { + emit_label (acfg, name); + /* LLVM generated code references this */ + sprintf (symbol, "%s%s%s", acfg->user_symbol_prefix, acfg->global_prefix, name); + emit_label (acfg, symbol); + emit_global_inner (acfg, symbol, FALSE); + } else { + emit_label (acfg, name); + } +} + +static void +emit_string_symbol (MonoAotCompile *acfg, const char *name, const char *value) +{ + if (acfg->llvm) { + mono_llvm_emit_aot_data (name, (guint8*)value, strlen (value) + 1); + return; + } + + mono_img_writer_emit_section_change (acfg->w, RODATA_SECT, 1); +#ifdef TARGET_MACH + /* On apple, all symbols need to be aligned to avoid warnings from ld */ + emit_alignment (acfg, 4); +#endif + mono_img_writer_emit_label (acfg->w, name); + mono_img_writer_emit_string (acfg->w, value); +} + +static G_GNUC_UNUSED void +emit_uleb128 (MonoAotCompile *acfg, guint32 value) +{ + do { + guint8 b = value & 0x7f; + value >>= 7; + if (value != 0) /* more bytes to come */ + b |= 0x80; + emit_byte (acfg, b); + } while (value); +} + +static G_GNUC_UNUSED void +emit_sleb128 (MonoAotCompile *acfg, gint64 value) +{ + gboolean more = 1; + gboolean negative = (value < 0); + guint32 size = 64; + guint8 byte; + + while (more) { + byte = value & 0x7f; + value >>= 7; + /* the following is unnecessary if the + * implementation of >>= uses an arithmetic rather + * than logical shift for a signed left operand + */ + if (negative) + /* sign extend */ + value |= - ((gint64)1 <<(size - 7)); + /* sign bit of byte is second high order bit (0x40) */ + if ((value == 0 && !(byte & 0x40)) || + (value == -1 && (byte & 0x40))) + more = 0; + else + byte |= 0x80; + emit_byte (acfg, byte); + } +} + +static G_GNUC_UNUSED void +encode_uleb128 (guint32 value, guint8 *buf, guint8 **endbuf) +{ + guint8 *p = buf; + + do { + guint8 b = value & 0x7f; + value >>= 7; + if (value != 0) /* more bytes to come */ + b |= 0x80; + *p ++ = b; + } while (value); + + *endbuf = p; +} + +static G_GNUC_UNUSED void +encode_sleb128 (gint32 value, guint8 *buf, guint8 **endbuf) +{ + gboolean more = 1; + gboolean negative = (value < 0); + guint32 size = 32; + guint8 byte; + guint8 *p = buf; + + while (more) { + byte = value & 0x7f; + value >>= 7; + /* the following is unnecessary if the + * implementation of >>= uses an arithmetic rather + * than logical shift for a signed left operand + */ + if (negative) + /* sign extend */ + value |= - (1 <<(size - 7)); + /* sign bit of byte is second high order bit (0x40) */ + if ((value == 0 && !(byte & 0x40)) || + (value == -1 && (byte & 0x40))) + more = 0; + else + byte |= 0x80; + *p ++= byte; + } + + *endbuf = p; +} + +static void +encode_int (gint32 val, guint8 *buf, guint8 **endbuf) +{ + // FIXME: Big-endian + buf [0] = (val >> 0) & 0xff; + buf [1] = (val >> 8) & 0xff; + buf [2] = (val >> 16) & 0xff; + buf [3] = (val >> 24) & 0xff; + + *endbuf = buf + 4; +} + +static void +encode_int16 (guint16 val, guint8 *buf, guint8 **endbuf) +{ + buf [0] = (val >> 0) & 0xff; + buf [1] = (val >> 8) & 0xff; + + *endbuf = buf + 2; +} + +static void +encode_string (const char *s, guint8 *buf, guint8 **endbuf) +{ + int len = strlen (s); + + memcpy (buf, s, len + 1); + *endbuf = buf + len + 1; +} + +static void +emit_unset_mode (MonoAotCompile *acfg) +{ + mono_img_writer_emit_unset_mode (acfg->w); +} + +static G_GNUC_UNUSED void +emit_set_thumb_mode (MonoAotCompile *acfg) +{ + emit_unset_mode (acfg); + fprintf (acfg->fp, ".code 16\n"); +} + +static G_GNUC_UNUSED void +emit_set_arm_mode (MonoAotCompile *acfg) +{ + emit_unset_mode (acfg); + fprintf (acfg->fp, ".code 32\n"); +} + +static inline void +emit_code_bytes (MonoAotCompile *acfg, const guint8* buf, int size) +{ +#ifdef TARGET_ARM64 + int i; + + g_assert (size % 4 == 0); + emit_unset_mode (acfg); + for (i = 0; i < size; i += 4) + fprintf (acfg->fp, "%s 0x%x\n", acfg->inst_directive, *(guint32*)(buf + i)); +#else + emit_bytes (acfg, buf, size); +#endif +} + +/* ARCHITECTURE SPECIFIC CODE */ + +#if defined(TARGET_X86) || defined(TARGET_AMD64) || defined(TARGET_ARM) || defined(TARGET_POWERPC) || defined(TARGET_ARM64) +#define EMIT_DWARF_INFO 1 +#endif + +#ifdef TARGET_WIN32_MSVC +#undef EMIT_DWARF_INFO +#define EMIT_WIN32_CODEVIEW_INFO +#endif + +#ifdef EMIT_WIN32_UNWIND_INFO +static UnwindInfoSectionCacheItem * +get_cached_unwind_info_section_item_win32 (MonoAotCompile *acfg, const char *function_start, const char *function_end, GSList *unwind_ops); + +static void +free_unwind_info_section_cache_win32 (MonoAotCompile *acfg); + +static void +emit_unwind_info_data_win32 (MonoAotCompile *acfg, PUNWIND_INFO unwind_info); + +static void +emit_unwind_info_sections_win32 (MonoAotCompile *acfg, const char *function_start, const char *function_end, GSList *unwind_ops); +#endif + +static void +arch_free_unwind_info_section_cache (MonoAotCompile *acfg) +{ +#ifdef EMIT_WIN32_UNWIND_INFO + free_unwind_info_section_cache_win32 (acfg); +#endif +} + +static void +arch_emit_unwind_info_sections (MonoAotCompile *acfg, const char *function_start, const char *function_end, GSList *unwind_ops) +{ +#ifdef EMIT_WIN32_UNWIND_INFO + gboolean own_unwind_ops = FALSE; + if (!unwind_ops) { + unwind_ops = mono_unwind_get_cie_program (); + own_unwind_ops = TRUE; + } + + emit_unwind_info_sections_win32 (acfg, function_start, function_end, unwind_ops); + + if (own_unwind_ops) + mono_free_unwind_info (unwind_ops); +#endif +} + +#if defined(TARGET_ARM) +#define AOT_FUNC_ALIGNMENT 4 +#else +#define AOT_FUNC_ALIGNMENT 16 +#endif + +#if defined(TARGET_POWERPC64) && !defined(__mono_ilp32__) +#define PPC_LD_OP "ld" +#define PPC_LDX_OP "ldx" +#else +#define PPC_LD_OP "lwz" +#define PPC_LDX_OP "lwzx" +#endif + +#ifdef TARGET_X86_64_WIN32_MSVC +#define AOT_TARGET_STR "AMD64 (WIN32) (MSVC codegen)" +#elif TARGET_AMD64 +#define AOT_TARGET_STR "AMD64" +#endif + +#ifdef TARGET_ARM +#ifdef TARGET_MACH +#define AOT_TARGET_STR "ARM (MACH)" +#else +#define AOT_TARGET_STR "ARM (!MACH)" +#endif +#endif + +#ifdef TARGET_ARM64 +#ifdef TARGET_MACH +#define AOT_TARGET_STR "ARM64 (MACH)" +#else +#define AOT_TARGET_STR "ARM64 (!MACH)" +#endif +#endif + +#ifdef TARGET_POWERPC64 +#ifdef __mono_ilp32__ +#define AOT_TARGET_STR "POWERPC64 (mono ilp32)" +#else +#define AOT_TARGET_STR "POWERPC64 (!mono ilp32)" +#endif +#else +#ifdef TARGET_POWERPC +#ifdef __mono_ilp32__ +#define AOT_TARGET_STR "POWERPC (mono ilp32)" +#else +#define AOT_TARGET_STR "POWERPC (!mono ilp32)" +#endif +#endif +#endif + +#ifdef TARGET_X86 +#ifdef TARGET_WIN32 +#define AOT_TARGET_STR "X86 (WIN32)" +#else +#define AOT_TARGET_STR "X86" +#endif +#endif + +#ifndef AOT_TARGET_STR +#define AOT_TARGET_STR "" +#endif + +static void +arch_init (MonoAotCompile *acfg) +{ + acfg->llc_args = g_string_new (""); + acfg->as_args = g_string_new (""); + acfg->llvm_owriter_supported = TRUE; + + /* + * The prefix LLVM likes to put in front of symbol names on darwin. + * The mach-os specs require this for globals, but LLVM puts them in front of all + * symbols. We need to handle this, since we need to refer to LLVM generated + * symbols. + */ + acfg->llvm_label_prefix = ""; + acfg->user_symbol_prefix = ""; + +#if defined(TARGET_X86) + g_string_append (acfg->llc_args, " -march=x86 -mattr=sse4.1"); +#endif + +#if defined(TARGET_AMD64) + g_string_append (acfg->llc_args, " -march=x86-64 -mattr=sse4.1"); + /* NOP */ + acfg->align_pad_value = 0x90; +#endif + +#ifdef TARGET_ARM + if (acfg->aot_opts.mtriple && strstr (acfg->aot_opts.mtriple, "darwin")) { + g_string_append (acfg->llc_args, "-mattr=+v6"); + } else { +#if defined(ARM_FPU_VFP_HARD) + g_string_append (acfg->llc_args, " -mattr=+vfp2,-neon,+d16 -float-abi=hard"); + g_string_append (acfg->as_args, " -mfpu=vfp3"); +#elif defined(ARM_FPU_VFP) + g_string_append (acfg->llc_args, " -mattr=+vfp2,-neon,+d16"); + g_string_append (acfg->as_args, " -mfpu=vfp3"); +#else + g_string_append (acfg->llc_args, " -soft-float"); +#endif + } + if (acfg->aot_opts.mtriple && strstr (acfg->aot_opts.mtriple, "thumb")) + acfg->thumb_mixed = TRUE; + + if (acfg->aot_opts.mtriple) + mono_arch_set_target (acfg->aot_opts.mtriple); +#endif + +#ifdef TARGET_ARM64 + acfg->inst_directive = ".inst"; + if (acfg->aot_opts.mtriple) + mono_arch_set_target (acfg->aot_opts.mtriple); +#endif + +#ifdef TARGET_MACH + acfg->user_symbol_prefix = "_"; + acfg->llvm_label_prefix = "_"; + acfg->inst_directive = ".word"; + acfg->need_no_dead_strip = TRUE; + acfg->aot_opts.gnu_asm = TRUE; +#endif + +#if defined(__linux__) && !defined(TARGET_ARM) + acfg->need_pt_gnu_stack = TRUE; +#endif + +#ifdef MONOTOUCH + acfg->global_symbols = TRUE; +#endif + +#ifdef TARGET_ANDROID + acfg->llvm_owriter_supported = FALSE; +#endif +} + +#ifdef TARGET_ARM64 + + +/* Load the contents of GOT_SLOT into dreg, clobbering ip0 */ +static void +arm64_emit_load_got_slot (MonoAotCompile *acfg, int dreg, int got_slot) +{ + int offset; + + g_assert (acfg->fp); + emit_unset_mode (acfg); + /* r16==ip0 */ + offset = (int)(got_slot * sizeof (gpointer)); +#ifdef TARGET_MACH + /* clang's integrated assembler */ + fprintf (acfg->fp, "adrp x16, %s@PAGE+%d\n", acfg->got_symbol, offset & 0xfffff000); + fprintf (acfg->fp, "add x16, x16, %s@PAGEOFF\n", acfg->got_symbol); + fprintf (acfg->fp, "ldr x%d, [x16, #%d]\n", dreg, offset & 0xfff); +#else + /* Linux GAS */ + fprintf (acfg->fp, "adrp x16, %s+%d\n", acfg->got_symbol, offset & 0xfffff000); + fprintf (acfg->fp, "add x16, x16, :lo12:%s\n", acfg->got_symbol); + fprintf (acfg->fp, "ldr x%d, [x16, %d]\n", dreg, offset & 0xfff); +#endif +} + +static void +arm64_emit_objc_selector_ref (MonoAotCompile *acfg, guint8 *code, int index, int *code_size) +{ + int reg; + + g_assert (acfg->fp); + emit_unset_mode (acfg); + + /* ldr rt, target */ + reg = arm_get_ldr_lit_reg (code); + + fprintf (acfg->fp, "adrp x%d, L_OBJC_SELECTOR_REFERENCES_%d@PAGE\n", reg, index); + fprintf (acfg->fp, "add x%d, x%d, L_OBJC_SELECTOR_REFERENCES_%d@PAGEOFF\n", reg, reg, index); + fprintf (acfg->fp, "ldr x%d, [x%d]\n", reg, reg); + + *code_size = 12; +} + +static void +arm64_emit_direct_call (MonoAotCompile *acfg, const char *target, gboolean external, gboolean thumb, MonoJumpInfo *ji, int *call_size) +{ + g_assert (acfg->fp); + emit_unset_mode (acfg); + if (ji && ji->relocation == MONO_R_ARM64_B) { + fprintf (acfg->fp, "b %s\n", target); + } else { + if (ji) + g_assert (ji->relocation == MONO_R_ARM64_BL); + fprintf (acfg->fp, "bl %s\n", target); + } + *call_size = 4; +} + +static void +arm64_emit_got_access (MonoAotCompile *acfg, guint8 *code, int got_slot, int *code_size) +{ + int reg; + + /* ldr rt, target */ + reg = arm_get_ldr_lit_reg (code); + arm64_emit_load_got_slot (acfg, reg, got_slot); + *code_size = 12; +} + +static void +arm64_emit_plt_entry (MonoAotCompile *acfg, const char *got_symbol, int offset, int info_offset) +{ + arm64_emit_load_got_slot (acfg, ARMREG_R16, offset / sizeof (gpointer)); + fprintf (acfg->fp, "br x16\n"); + /* Used by mono_aot_get_plt_info_offset () */ + fprintf (acfg->fp, "%s %d\n", acfg->inst_directive, info_offset); +} + +static void +arm64_emit_tramp_page_common_code (MonoAotCompile *acfg, int pagesize, int arg_reg, int *size) +{ + guint8 buf [256]; + guint8 *code; + int imm; + + /* The common code */ + code = buf; + imm = pagesize; + /* The trampoline address is in IP0 */ + arm_movzx (code, ARMREG_IP1, imm & 0xffff, 0); + arm_movkx (code, ARMREG_IP1, (imm >> 16) & 0xffff, 16); + /* Compute the data slot address */ + arm_subx (code, ARMREG_IP0, ARMREG_IP0, ARMREG_IP1); + /* Trampoline argument */ + arm_ldrx (code, arg_reg, ARMREG_IP0, 0); + /* Address */ + arm_ldrx (code, ARMREG_IP0, ARMREG_IP0, 8); + arm_brx (code, ARMREG_IP0); + + /* Emit it */ + emit_code_bytes (acfg, buf, code - buf); + + *size = code - buf; +} + +static void +arm64_emit_tramp_page_specific_code (MonoAotCompile *acfg, int pagesize, int common_tramp_size, int specific_tramp_size) +{ + guint8 buf [256]; + guint8 *code; + int i, count; + + count = (pagesize - common_tramp_size) / specific_tramp_size; + for (i = 0; i < count; ++i) { + code = buf; + arm_adrx (code, ARMREG_IP0, code); + /* Branch to the generic code */ + arm_b (code, code - 4 - (i * specific_tramp_size) - common_tramp_size); + /* This has to be 2 pointers long */ + arm_nop (code); + arm_nop (code); + g_assert (code - buf == specific_tramp_size); + emit_code_bytes (acfg, buf, code - buf); + } +} + +static void +arm64_emit_specific_trampoline_pages (MonoAotCompile *acfg) +{ + guint8 buf [128]; + guint8 *code; + guint8 *labels [16]; + int common_tramp_size; + int specific_tramp_size = 2 * 8; + int imm, pagesize; + char symbol [128]; + + if (!acfg->aot_opts.use_trampolines_page) + return; + +#ifdef TARGET_MACH + /* Have to match the target pagesize */ + pagesize = 16384; +#else + pagesize = mono_pagesize (); +#endif + acfg->tramp_page_size = pagesize; + + /* The specific trampolines */ + sprintf (symbol, "%sspecific_trampolines_page", acfg->user_symbol_prefix); + emit_alignment (acfg, pagesize); + emit_global (acfg, symbol, TRUE); + emit_label (acfg, symbol); + + /* The common code */ + arm64_emit_tramp_page_common_code (acfg, pagesize, ARMREG_IP1, &common_tramp_size); + acfg->tramp_page_code_offsets [MONO_AOT_TRAMP_SPECIFIC] = common_tramp_size; + + arm64_emit_tramp_page_specific_code (acfg, pagesize, common_tramp_size, specific_tramp_size); + + /* The rgctx trampolines */ + /* These are the same as the specific trampolines, but they load the argument into MONO_ARCH_RGCTX_REG */ + sprintf (symbol, "%srgctx_trampolines_page", acfg->user_symbol_prefix); + emit_alignment (acfg, pagesize); + emit_global (acfg, symbol, TRUE); + emit_label (acfg, symbol); + + /* The common code */ + arm64_emit_tramp_page_common_code (acfg, pagesize, MONO_ARCH_RGCTX_REG, &common_tramp_size); + acfg->tramp_page_code_offsets [MONO_AOT_TRAMP_STATIC_RGCTX] = common_tramp_size; + + arm64_emit_tramp_page_specific_code (acfg, pagesize, common_tramp_size, specific_tramp_size); + + /* The gsharedvt arg trampolines */ + /* These are the same as the specific trampolines */ + sprintf (symbol, "%sgsharedvt_arg_trampolines_page", acfg->user_symbol_prefix); + emit_alignment (acfg, pagesize); + emit_global (acfg, symbol, TRUE); + emit_label (acfg, symbol); + + arm64_emit_tramp_page_common_code (acfg, pagesize, ARMREG_IP1, &common_tramp_size); + acfg->tramp_page_code_offsets [MONO_AOT_TRAMP_GSHAREDVT_ARG] = common_tramp_size; + + arm64_emit_tramp_page_specific_code (acfg, pagesize, common_tramp_size, specific_tramp_size); + + /* The IMT trampolines */ + sprintf (symbol, "%simt_trampolines_page", acfg->user_symbol_prefix); + emit_alignment (acfg, pagesize); + emit_global (acfg, symbol, TRUE); + emit_label (acfg, symbol); + + code = buf; + imm = pagesize; + /* The trampoline address is in IP0 */ + arm_movzx (code, ARMREG_IP1, imm & 0xffff, 0); + arm_movkx (code, ARMREG_IP1, (imm >> 16) & 0xffff, 16); + /* Compute the data slot address */ + arm_subx (code, ARMREG_IP0, ARMREG_IP0, ARMREG_IP1); + /* Trampoline argument */ + arm_ldrx (code, ARMREG_IP1, ARMREG_IP0, 0); + + /* Same as arch_emit_imt_trampoline () */ + labels [0] = code; + arm_ldrx (code, ARMREG_IP0, ARMREG_IP1, 0); + arm_cmpx (code, ARMREG_IP0, MONO_ARCH_RGCTX_REG); + labels [1] = code; + arm_bcc (code, ARMCOND_EQ, 0); + + /* End-of-loop check */ + labels [2] = code; + arm_cbzx (code, ARMREG_IP0, 0); + + /* Loop footer */ + arm_addx_imm (code, ARMREG_IP1, ARMREG_IP1, 2 * 8); + arm_b (code, labels [0]); + + /* Match */ + mono_arm_patch (labels [1], code, MONO_R_ARM64_BCC); + /* Load vtable slot addr */ + arm_ldrx (code, ARMREG_IP0, ARMREG_IP1, 8); + /* Load vtable slot */ + arm_ldrx (code, ARMREG_IP0, ARMREG_IP0, 0); + arm_brx (code, ARMREG_IP0); + + /* No match */ + mono_arm_patch (labels [2], code, MONO_R_ARM64_CBZ); + /* Load fail addr */ + arm_ldrx (code, ARMREG_IP0, ARMREG_IP1, 8); + arm_brx (code, ARMREG_IP0); + + emit_code_bytes (acfg, buf, code - buf); + + common_tramp_size = code - buf; + acfg->tramp_page_code_offsets [MONO_AOT_TRAMP_IMT] = common_tramp_size; + + arm64_emit_tramp_page_specific_code (acfg, pagesize, common_tramp_size, specific_tramp_size); +} + +static void +arm64_emit_specific_trampoline (MonoAotCompile *acfg, int offset, int *tramp_size) +{ + /* Load argument from second GOT slot */ + arm64_emit_load_got_slot (acfg, ARMREG_R17, offset + 1); + /* Load generic trampoline address from first GOT slot */ + arm64_emit_load_got_slot (acfg, ARMREG_R16, offset); + fprintf (acfg->fp, "br x16\n"); + *tramp_size = 7 * 4; +} + +static void +arm64_emit_unbox_trampoline (MonoAotCompile *acfg, MonoCompile *cfg, MonoMethod *method, const char *call_target) +{ + emit_unset_mode (acfg); + fprintf (acfg->fp, "add x0, x0, %d\n", (int)(sizeof (MonoObject))); + fprintf (acfg->fp, "b %s\n", call_target); +} + +static void +arm64_emit_static_rgctx_trampoline (MonoAotCompile *acfg, int offset, int *tramp_size) +{ + /* Similar to the specific trampolines, but use the rgctx reg instead of ip1 */ + + /* Load argument from first GOT slot */ + arm64_emit_load_got_slot (acfg, MONO_ARCH_RGCTX_REG, offset); + /* Load generic trampoline address from second GOT slot */ + arm64_emit_load_got_slot (acfg, ARMREG_R16, offset + 1); + fprintf (acfg->fp, "br x16\n"); + *tramp_size = 7 * 4; +} + +static void +arm64_emit_imt_trampoline (MonoAotCompile *acfg, int offset, int *tramp_size) +{ + guint8 buf [128]; + guint8 *code, *labels [16]; + + /* Load parameter from GOT slot into ip1 */ + arm64_emit_load_got_slot (acfg, ARMREG_R17, offset); + + code = buf; + labels [0] = code; + arm_ldrx (code, ARMREG_IP0, ARMREG_IP1, 0); + arm_cmpx (code, ARMREG_IP0, MONO_ARCH_RGCTX_REG); + labels [1] = code; + arm_bcc (code, ARMCOND_EQ, 0); + + /* End-of-loop check */ + labels [2] = code; + arm_cbzx (code, ARMREG_IP0, 0); + + /* Loop footer */ + arm_addx_imm (code, ARMREG_IP1, ARMREG_IP1, 2 * 8); + arm_b (code, labels [0]); + + /* Match */ + mono_arm_patch (labels [1], code, MONO_R_ARM64_BCC); + /* Load vtable slot addr */ + arm_ldrx (code, ARMREG_IP0, ARMREG_IP1, 8); + /* Load vtable slot */ + arm_ldrx (code, ARMREG_IP0, ARMREG_IP0, 0); + arm_brx (code, ARMREG_IP0); + + /* No match */ + mono_arm_patch (labels [2], code, MONO_R_ARM64_CBZ); + /* Load fail addr */ + arm_ldrx (code, ARMREG_IP0, ARMREG_IP1, 8); + arm_brx (code, ARMREG_IP0); + + emit_code_bytes (acfg, buf, code - buf); + + *tramp_size = code - buf + (3 * 4); +} + +static void +arm64_emit_gsharedvt_arg_trampoline (MonoAotCompile *acfg, int offset, int *tramp_size) +{ + /* Similar to the specific trampolines, but the address is in the second slot */ + /* Load argument from first GOT slot */ + arm64_emit_load_got_slot (acfg, ARMREG_R17, offset); + /* Load generic trampoline address from second GOT slot */ + arm64_emit_load_got_slot (acfg, ARMREG_R16, offset + 1); + fprintf (acfg->fp, "br x16\n"); + *tramp_size = 7 * 4; +} + + +#endif + +#ifdef MONO_ARCH_AOT_SUPPORTED +/* + * arch_emit_direct_call: + * + * Emit a direct call to the symbol TARGET. CALL_SIZE is set to the size of the + * calling code. + */ +static void +arch_emit_direct_call (MonoAotCompile *acfg, const char *target, gboolean external, gboolean thumb, MonoJumpInfo *ji, int *call_size) +{ +#if defined(TARGET_X86) || defined(TARGET_AMD64) + /* Need to make sure this is exactly 5 bytes long */ + emit_unset_mode (acfg); + fprintf (acfg->fp, "call %s\n", target); + *call_size = 5; +#elif defined(TARGET_ARM) + emit_unset_mode (acfg); + if (thumb) + fprintf (acfg->fp, "blx %s\n", target); + else + fprintf (acfg->fp, "bl %s\n", target); + *call_size = 4; +#elif defined(TARGET_ARM64) + arm64_emit_direct_call (acfg, target, external, thumb, ji, call_size); +#elif defined(TARGET_POWERPC) + emit_unset_mode (acfg); + fprintf (acfg->fp, "bl %s\n", target); + *call_size = 4; +#else + g_assert_not_reached (); +#endif +} +#endif + +/* + * PPC32 design: + * - we use an approach similar to the x86 abi: reserve a register (r30) to hold + * the GOT pointer. + * - The full-aot trampolines need access to the GOT of mscorlib, so we store + * in in the 2. slot of every GOT, and require every method to place the GOT + * address in r30, even when it doesn't access the GOT otherwise. This way, + * the trampolines can compute the mscorlib GOT address by loading 4(r30). + */ + +/* + * PPC64 design: + * PPC64 uses function descriptors which greatly complicate all code, since + * these are used very inconsistently in the runtime. Some functions like + * mono_compile_method () return ftn descriptors, while others like the + * trampoline creation functions do not. + * We assume that all GOT slots contain function descriptors, and create + * descriptors in aot-runtime.c when needed. + * The ppc64 abi uses r2 to hold the address of the TOC/GOT, which is loaded + * from function descriptors, we could do the same, but it would require + * rewriting all the ppc/aot code to handle function descriptors properly. + * So instead, we use the same approach as on PPC32. + * This is a horrible mess, but fixing it would probably lead to an even bigger + * one. + */ + +/* + * X86 design: + * - similar to the PPC32 design, we reserve EBX to hold the GOT pointer. + */ + +#ifdef MONO_ARCH_AOT_SUPPORTED +/* + * arch_emit_got_offset: + * + * The memory pointed to by CODE should hold native code for computing the GOT + * address (OP_LOAD_GOTADDR). Emit this code while patching it with the offset + * between code and the GOT. CODE_SIZE is set to the number of bytes emitted. + */ +static void +arch_emit_got_offset (MonoAotCompile *acfg, guint8 *code, int *code_size) +{ +#if defined(TARGET_POWERPC64) + emit_unset_mode (acfg); + /* + * The ppc32 code doesn't seem to work on ppc64, the assembler complains about + * unsupported relocations. So we store the got address into the .Lgot_addr + * symbol which is in the text segment, compute its address, and load it. + */ + fprintf (acfg->fp, ".L%d:\n", acfg->label_generator); + fprintf (acfg->fp, "lis 0, (.Lgot_addr + 4 - .L%d)@h\n", acfg->label_generator); + fprintf (acfg->fp, "ori 0, 0, (.Lgot_addr + 4 - .L%d)@l\n", acfg->label_generator); + fprintf (acfg->fp, "add 30, 30, 0\n"); + fprintf (acfg->fp, "%s 30, 0(30)\n", PPC_LD_OP); + acfg->label_generator ++; + *code_size = 16; +#elif defined(TARGET_POWERPC) + emit_unset_mode (acfg); + fprintf (acfg->fp, ".L%d:\n", acfg->label_generator); + fprintf (acfg->fp, "lis 0, (%s + 4 - .L%d)@h\n", acfg->got_symbol, acfg->label_generator); + fprintf (acfg->fp, "ori 0, 0, (%s + 4 - .L%d)@l\n", acfg->got_symbol, acfg->label_generator); + acfg->label_generator ++; + *code_size = 8; +#else + guint32 offset = mono_arch_get_patch_offset (code); + emit_bytes (acfg, code, offset); + emit_symbol_diff (acfg, acfg->got_symbol, ".", offset); + + *code_size = offset + 4; +#endif +} + +/* + * arch_emit_got_access: + * + * The memory pointed to by CODE should hold native code for loading a GOT + * slot (OP_AOTCONST/OP_GOT_ENTRY). Emit this code while patching it so it accesses the + * GOT slot GOT_SLOT. CODE_SIZE is set to the number of bytes emitted. + */ +static void +arch_emit_got_access (MonoAotCompile *acfg, const char *got_symbol, guint8 *code, int got_slot, int *code_size) +{ +#ifdef TARGET_AMD64 + /* mov reg, got+offset(%rip) */ + if (acfg->llvm) { + /* The GOT symbol is in the LLVM module, the clang assembler has problems emitting symbol diffs for it */ + int dreg; + int rex_r; + + /* Decode reg, see amd64_mov_reg_membase () */ + rex_r = code [0] & AMD64_REX_R; + g_assert (code [0] == 0x49 + rex_r); + g_assert (code [1] == 0x8b); + dreg = ((code [2] >> 3) & 0x7) + (rex_r ? 8 : 0); + + emit_unset_mode (acfg); + fprintf (acfg->fp, "mov %s+%d(%%rip), %s\n", got_symbol, (unsigned int) ((got_slot * sizeof (gpointer))), mono_arch_regname (dreg)); + *code_size = 7; + } else { + emit_bytes (acfg, code, mono_arch_get_patch_offset (code)); + emit_symbol_diff (acfg, got_symbol, ".", (unsigned int) ((got_slot * sizeof (gpointer)) - 4)); + *code_size = mono_arch_get_patch_offset (code) + 4; + } +#elif defined(TARGET_X86) + emit_bytes (acfg, code, mono_arch_get_patch_offset (code)); + emit_int32 (acfg, (unsigned int) ((got_slot * sizeof (gpointer)))); + *code_size = mono_arch_get_patch_offset (code) + 4; +#elif defined(TARGET_ARM) + emit_bytes (acfg, code, mono_arch_get_patch_offset (code)); + emit_symbol_diff (acfg, got_symbol, ".", (unsigned int) ((got_slot * sizeof (gpointer))) - 12); + *code_size = mono_arch_get_patch_offset (code) + 4; +#elif defined(TARGET_ARM64) + emit_bytes (acfg, code, mono_arch_get_patch_offset (code)); + arm64_emit_got_access (acfg, code, got_slot, code_size); +#elif defined(TARGET_POWERPC) + { + guint8 buf [32]; + + emit_bytes (acfg, code, mono_arch_get_patch_offset (code)); + code = buf; + ppc_load32 (code, ppc_r0, got_slot * sizeof (gpointer)); + g_assert (code - buf == 8); + emit_bytes (acfg, buf, code - buf); + *code_size = code - buf; + } +#else + g_assert_not_reached (); +#endif +} + +#endif + +#ifdef MONO_ARCH_AOT_SUPPORTED +/* + * arch_emit_objc_selector_ref: + * + * Emit the implementation of OP_OBJC_GET_SELECTOR, which itself implements @selector(foo:) in objective-c. + */ +static void +arch_emit_objc_selector_ref (MonoAotCompile *acfg, guint8 *code, int index, int *code_size) +{ +#if defined(TARGET_ARM) + char symbol1 [MAX_SYMBOL_SIZE]; + char symbol2 [MAX_SYMBOL_SIZE]; + int lindex = acfg->objc_selector_index_2 ++; + + /* Emit ldr.imm/b */ + emit_bytes (acfg, code, 8); + + sprintf (symbol1, "L_OBJC_SELECTOR_%d", lindex); + sprintf (symbol2, "L_OBJC_SELECTOR_REFERENCES_%d", index); + + emit_label (acfg, symbol1); + mono_img_writer_emit_unset_mode (acfg->w); + fprintf (acfg->fp, ".long %s-(%s+12)", symbol2, symbol1); + + *code_size = 12; +#elif defined(TARGET_ARM64) + arm64_emit_objc_selector_ref (acfg, code, index, code_size); +#else + g_assert_not_reached (); +#endif +} +#endif + +/* + * arch_emit_plt_entry: + * + * Emit code for the PLT entry. + * The plt entry should look like this: + * + * + */ +static void +arch_emit_plt_entry (MonoAotCompile *acfg, const char *got_symbol, int offset, int info_offset) +{ +#if defined(TARGET_X86) + /* jmp *(%ebx) */ + emit_byte (acfg, 0xff); + emit_byte (acfg, 0xa3); + emit_int32 (acfg, offset); + /* Used by mono_aot_get_plt_info_offset */ + emit_int32 (acfg, info_offset); +#elif defined(TARGET_AMD64) + emit_unset_mode (acfg); + fprintf (acfg->fp, "jmp *%s+%d(%%rip)\n", got_symbol, offset); + /* Used by mono_aot_get_plt_info_offset */ + emit_int32 (acfg, info_offset); + acfg->stats.plt_size += 10; +#elif defined(TARGET_ARM) + guint8 buf [256]; + guint8 *code; + + code = buf; + ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0); + ARM_LDR_REG_REG (code, ARMREG_PC, ARMREG_PC, ARMREG_IP); + emit_bytes (acfg, buf, code - buf); + emit_symbol_diff (acfg, got_symbol, ".", offset - 4); + /* Used by mono_aot_get_plt_info_offset */ + emit_int32 (acfg, info_offset); +#elif defined(TARGET_ARM64) + arm64_emit_plt_entry (acfg, got_symbol, offset, info_offset); +#elif defined(TARGET_POWERPC) + /* The GOT address is guaranteed to be in r30 by OP_LOAD_GOTADDR */ + emit_unset_mode (acfg); + fprintf (acfg->fp, "lis 11, %d@h\n", offset); + fprintf (acfg->fp, "ori 11, 11, %d@l\n", offset); + fprintf (acfg->fp, "add 11, 11, 30\n"); + fprintf (acfg->fp, "%s 11, 0(11)\n", PPC_LD_OP); +#ifdef PPC_USES_FUNCTION_DESCRIPTOR + fprintf (acfg->fp, "%s 2, %d(11)\n", PPC_LD_OP, (int)sizeof (gpointer)); + fprintf (acfg->fp, "%s 11, 0(11)\n", PPC_LD_OP); +#endif + fprintf (acfg->fp, "mtctr 11\n"); + fprintf (acfg->fp, "bctr\n"); + emit_int32 (acfg, info_offset); +#else + g_assert_not_reached (); +#endif +} + +/* + * arch_emit_llvm_plt_entry: + * + * Same as arch_emit_plt_entry, but handles calls from LLVM generated code. + * This is only needed on arm to handle thumb interop. + */ +static void +arch_emit_llvm_plt_entry (MonoAotCompile *acfg, const char *got_symbol, int offset, int info_offset) +{ +#if defined(TARGET_ARM) + /* LLVM calls the PLT entries using bl, so these have to be thumb2 */ + /* The caller already transitioned to thumb */ + /* The code below should be 12 bytes long */ + /* clang has trouble encoding these instructions, so emit the binary */ +#if 0 + fprintf (acfg->fp, "ldr ip, [pc, #8]\n"); + /* thumb can't encode ld pc, [pc, ip] */ + fprintf (acfg->fp, "add ip, pc, ip\n"); + fprintf (acfg->fp, "ldr ip, [ip, #0]\n"); + fprintf (acfg->fp, "bx ip\n"); +#endif + emit_set_thumb_mode (acfg); + fprintf (acfg->fp, ".4byte 0xc008f8df\n"); + fprintf (acfg->fp, ".2byte 0x44fc\n"); + fprintf (acfg->fp, ".4byte 0xc000f8dc\n"); + fprintf (acfg->fp, ".2byte 0x4760\n"); + emit_symbol_diff (acfg, got_symbol, ".", offset + 4); + emit_int32 (acfg, info_offset); + emit_unset_mode (acfg); + emit_set_arm_mode (acfg); +#else + g_assert_not_reached (); +#endif +} + +/* Save unwind_info in the module and emit the offset to the information at symbol */ +static void save_unwind_info (MonoAotCompile *acfg, char *symbol, GSList *unwind_ops) +{ + guint32 uw_offset, encoded_len; + guint8 *encoded; + + emit_section_change (acfg, RODATA_SECT, 0); + emit_global (acfg, symbol, FALSE); + emit_label (acfg, symbol); + + encoded = mono_unwind_ops_encode (unwind_ops, &encoded_len); + uw_offset = get_unwind_info_offset (acfg, encoded, encoded_len); + g_free (encoded); + emit_int32 (acfg, uw_offset); +} + +/* + * arch_emit_specific_trampoline_pages: + * + * Emits a page full of trampolines: each trampoline uses its own address to + * lookup both the generic trampoline code and the data argument. + * This page can be remapped in process multiple times so we can get an + * unlimited number of trampolines. + * Specifically this implementation uses the following trick: two memory pages + * are allocated, with the first containing the data and the second containing the trampolines. + * To reduce trampoline size, each trampoline jumps at the start of the page where a common + * implementation does all the lifting. + * Note that the ARM single trampoline size is 8 bytes, exactly like the data that needs to be stored + * on the arm 32 bit system. + */ +static void +arch_emit_specific_trampoline_pages (MonoAotCompile *acfg) +{ +#if defined(TARGET_ARM) + guint8 buf [128]; + guint8 *code; + guint8 *loop_start, *loop_branch_back, *loop_end_check, *imt_found_check; + int i; + int pagesize = MONO_AOT_TRAMP_PAGE_SIZE; + GSList *unwind_ops = NULL; +#define COMMON_TRAMP_SIZE 16 + int count = (pagesize - COMMON_TRAMP_SIZE) / 8; + int imm8, rot_amount; + char symbol [128]; + + if (!acfg->aot_opts.use_trampolines_page) + return; + + acfg->tramp_page_size = pagesize; + + sprintf (symbol, "%sspecific_trampolines_page", acfg->user_symbol_prefix); + emit_alignment (acfg, pagesize); + emit_global (acfg, symbol, TRUE); + emit_label (acfg, symbol); + + /* emit the generic code first, the trampoline address + 8 is in the lr register */ + code = buf; + imm8 = mono_arm_is_rotated_imm8 (pagesize, &rot_amount); + ARM_SUB_REG_IMM (code, ARMREG_LR, ARMREG_LR, imm8, rot_amount); + ARM_LDR_IMM (code, ARMREG_R1, ARMREG_LR, -8); + ARM_LDR_IMM (code, ARMREG_PC, ARMREG_LR, -4); + ARM_NOP (code); + g_assert (code - buf == COMMON_TRAMP_SIZE); + + /* Emit it */ + emit_bytes (acfg, buf, code - buf); + + for (i = 0; i < count; ++i) { + code = buf; + ARM_PUSH (code, 0x5fff); + ARM_BL (code, 0); + arm_patch (code - 4, code - COMMON_TRAMP_SIZE - 8 * (i + 1)); + g_assert (code - buf == 8); + emit_bytes (acfg, buf, code - buf); + } + + /* now the rgctx trampolines: each specific trampolines puts in the ip register + * the instruction pointer address, so the generic trampoline at the start of the page + * subtracts 4096 to get to the data page and loads the values + * We again fit the generic trampiline in 16 bytes. + */ + sprintf (symbol, "%srgctx_trampolines_page", acfg->user_symbol_prefix); + emit_global (acfg, symbol, TRUE); + emit_label (acfg, symbol); + code = buf; + imm8 = mono_arm_is_rotated_imm8 (pagesize, &rot_amount); + ARM_SUB_REG_IMM (code, ARMREG_IP, ARMREG_IP, imm8, rot_amount); + ARM_LDR_IMM (code, MONO_ARCH_RGCTX_REG, ARMREG_IP, -8); + ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, -4); + ARM_NOP (code); + g_assert (code - buf == COMMON_TRAMP_SIZE); + + /* Emit it */ + emit_bytes (acfg, buf, code - buf); + + for (i = 0; i < count; ++i) { + code = buf; + ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_PC); + ARM_B (code, 0); + arm_patch (code - 4, code - COMMON_TRAMP_SIZE - 8 * (i + 1)); + g_assert (code - buf == 8); + emit_bytes (acfg, buf, code - buf); + } + + /* + * gsharedvt arg trampolines: see arch_emit_gsharedvt_arg_trampoline () + */ + sprintf (symbol, "%sgsharedvt_arg_trampolines_page", acfg->user_symbol_prefix); + emit_global (acfg, symbol, TRUE); + emit_label (acfg, symbol); + code = buf; + ARM_PUSH (code, (1 << ARMREG_R0) | (1 << ARMREG_R1) | (1 << ARMREG_R2) | (1 << ARMREG_R3)); + imm8 = mono_arm_is_rotated_imm8 (pagesize, &rot_amount); + ARM_SUB_REG_IMM (code, ARMREG_IP, ARMREG_IP, imm8, rot_amount); + ARM_LDR_IMM (code, ARMREG_R0, ARMREG_IP, -8); + ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, -4); + g_assert (code - buf == COMMON_TRAMP_SIZE); + /* Emit it */ + emit_bytes (acfg, buf, code - buf); + + for (i = 0; i < count; ++i) { + code = buf; + ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_PC); + ARM_B (code, 0); + arm_patch (code - 4, code - COMMON_TRAMP_SIZE - 8 * (i + 1)); + g_assert (code - buf == 8); + emit_bytes (acfg, buf, code - buf); + } + + /* now the imt trampolines: each specific trampolines puts in the ip register + * the instruction pointer address, so the generic trampoline at the start of the page + * subtracts 4096 to get to the data page and loads the values + */ +#define IMT_TRAMP_SIZE 72 + sprintf (symbol, "%simt_trampolines_page", acfg->user_symbol_prefix); + emit_global (acfg, symbol, TRUE); + emit_label (acfg, symbol); + code = buf; + /* Need at least two free registers, plus a slot for storing the pc */ + ARM_PUSH (code, (1 << ARMREG_R0)|(1 << ARMREG_R1)|(1 << ARMREG_R2)); + + imm8 = mono_arm_is_rotated_imm8 (pagesize, &rot_amount); + ARM_SUB_REG_IMM (code, ARMREG_IP, ARMREG_IP, imm8, rot_amount); + ARM_LDR_IMM (code, ARMREG_R0, ARMREG_IP, -8); + + /* The IMT method is in v5, r0 has the imt array address */ + + loop_start = code; + ARM_LDR_IMM (code, ARMREG_R1, ARMREG_R0, 0); + ARM_CMP_REG_REG (code, ARMREG_R1, ARMREG_V5); + imt_found_check = code; + ARM_B_COND (code, ARMCOND_EQ, 0); + + /* End-of-loop check */ + ARM_CMP_REG_IMM (code, ARMREG_R1, 0, 0); + loop_end_check = code; + ARM_B_COND (code, ARMCOND_EQ, 0); + + /* Loop footer */ + ARM_ADD_REG_IMM8 (code, ARMREG_R0, ARMREG_R0, sizeof (gpointer) * 2); + loop_branch_back = code; + ARM_B (code, 0); + arm_patch (loop_branch_back, loop_start); + + /* Match */ + arm_patch (imt_found_check, code); + ARM_LDR_IMM (code, ARMREG_R0, ARMREG_R0, 4); + ARM_LDR_IMM (code, ARMREG_R0, ARMREG_R0, 0); + /* Save it to the third stack slot */ + ARM_STR_IMM (code, ARMREG_R0, ARMREG_SP, 8); + /* Restore the registers and branch */ + ARM_POP (code, (1 << ARMREG_R0)|(1 << ARMREG_R1)|(1 << ARMREG_PC)); + + /* No match */ + arm_patch (loop_end_check, code); + ARM_LDR_IMM (code, ARMREG_R0, ARMREG_R0, 4); + ARM_STR_IMM (code, ARMREG_R0, ARMREG_SP, 8); + ARM_POP (code, (1 << ARMREG_R0)|(1 << ARMREG_R1)|(1 << ARMREG_PC)); + ARM_NOP (code); + + /* Emit it */ + g_assert (code - buf == IMT_TRAMP_SIZE); + emit_bytes (acfg, buf, code - buf); + + for (i = 0; i < count; ++i) { + code = buf; + ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_PC); + ARM_B (code, 0); + arm_patch (code - 4, code - IMT_TRAMP_SIZE - 8 * (i + 1)); + g_assert (code - buf == 8); + emit_bytes (acfg, buf, code - buf); + } + + acfg->tramp_page_code_offsets [MONO_AOT_TRAMP_SPECIFIC] = 16; + acfg->tramp_page_code_offsets [MONO_AOT_TRAMP_STATIC_RGCTX] = 16; + acfg->tramp_page_code_offsets [MONO_AOT_TRAMP_IMT] = 72; + acfg->tramp_page_code_offsets [MONO_AOT_TRAMP_GSHAREDVT_ARG] = 16; + + /* Unwind info for specifc trampolines */ + sprintf (symbol, "%sspecific_trampolines_page_gen_p", acfg->user_symbol_prefix); + /* We unwind to the original caller, from the stack, since lr is clobbered */ + mono_add_unwind_op_def_cfa (unwind_ops, 0, 0, ARMREG_SP, 14 * sizeof (mgreg_t)); + mono_add_unwind_op_offset (unwind_ops, 0, 0, ARMREG_LR, -4); + save_unwind_info (acfg, symbol, unwind_ops); + mono_free_unwind_info (unwind_ops); + + sprintf (symbol, "%sspecific_trampolines_page_sp_p", acfg->user_symbol_prefix); + mono_add_unwind_op_def_cfa (unwind_ops, 0, 0, ARMREG_SP, 0); + mono_add_unwind_op_def_cfa_offset (unwind_ops, 4, 0, 14 * sizeof (mgreg_t)); + save_unwind_info (acfg, symbol, unwind_ops); + mono_free_unwind_info (unwind_ops); + + /* Unwind info for rgctx trampolines */ + sprintf (symbol, "%srgctx_trampolines_page_gen_p", acfg->user_symbol_prefix); + mono_add_unwind_op_def_cfa (unwind_ops, 0, 0, ARMREG_SP, 0); + save_unwind_info (acfg, symbol, unwind_ops); + + sprintf (symbol, "%srgctx_trampolines_page_sp_p", acfg->user_symbol_prefix); + save_unwind_info (acfg, symbol, unwind_ops); + mono_free_unwind_info (unwind_ops); + + /* Unwind info for gsharedvt trampolines */ + sprintf (symbol, "%sgsharedvt_trampolines_page_gen_p", acfg->user_symbol_prefix); + mono_add_unwind_op_def_cfa (unwind_ops, 0, 0, ARMREG_SP, 0); + mono_add_unwind_op_def_cfa_offset (unwind_ops, 4, 0, 4 * sizeof (mgreg_t)); + save_unwind_info (acfg, symbol, unwind_ops); + mono_free_unwind_info (unwind_ops); + + sprintf (symbol, "%sgsharedvt_trampolines_page_sp_p", acfg->user_symbol_prefix); + mono_add_unwind_op_def_cfa (unwind_ops, 0, 0, ARMREG_SP, 0); + save_unwind_info (acfg, symbol, unwind_ops); + mono_free_unwind_info (unwind_ops); + + /* Unwind info for imt trampolines */ + sprintf (symbol, "%simt_trampolines_page_gen_p", acfg->user_symbol_prefix); + mono_add_unwind_op_def_cfa (unwind_ops, 0, 0, ARMREG_SP, 0); + mono_add_unwind_op_def_cfa_offset (unwind_ops, 4, 0, 3 * sizeof (mgreg_t)); + save_unwind_info (acfg, symbol, unwind_ops); + mono_free_unwind_info (unwind_ops); + + sprintf (symbol, "%simt_trampolines_page_sp_p", acfg->user_symbol_prefix); + mono_add_unwind_op_def_cfa (unwind_ops, 0, 0, ARMREG_SP, 0); + save_unwind_info (acfg, symbol, unwind_ops); + mono_free_unwind_info (unwind_ops); +#elif defined(TARGET_ARM64) + arm64_emit_specific_trampoline_pages (acfg); +#endif +} + +/* + * arch_emit_specific_trampoline: + * + * Emit code for a specific trampoline. OFFSET is the offset of the first of + * two GOT slots which contain the generic trampoline address and the trampoline + * argument. TRAMP_SIZE is set to the size of the emitted trampoline. + */ +static void +arch_emit_specific_trampoline (MonoAotCompile *acfg, int offset, int *tramp_size) +{ + /* + * The trampolines created here are variations of the specific + * trampolines created in mono_arch_create_specific_trampoline (). The + * differences are: + * - the generic trampoline address is taken from a got slot. + * - the offset of the got slot where the trampoline argument is stored + * is embedded in the instruction stream, and the generic trampoline + * can load the argument by loading the offset, adding it to the + * address of the trampoline to get the address of the got slot, and + * loading the argument from there. + * - all the trampolines should be of the same length. + */ +#if defined(TARGET_AMD64) + /* This should be exactly 8 bytes long */ + *tramp_size = 8; + /* call *(%rip) */ + if (acfg->llvm) { + emit_unset_mode (acfg); + fprintf (acfg->fp, "call *%s+%d(%%rip)\n", acfg->got_symbol, (int)(offset * sizeof (gpointer))); + emit_zero_bytes (acfg, 2); + } else { + emit_byte (acfg, '\x41'); + emit_byte (acfg, '\xff'); + emit_byte (acfg, '\x15'); + emit_symbol_diff (acfg, acfg->got_symbol, ".", (offset * sizeof (gpointer)) - 4); + emit_zero_bytes (acfg, 1); + } +#elif defined(TARGET_ARM) + guint8 buf [128]; + guint8 *code; + + /* This should be exactly 20 bytes long */ + *tramp_size = 20; + code = buf; + ARM_PUSH (code, 0x5fff); + ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 4); + /* Load the value from the GOT */ + ARM_LDR_REG_REG (code, ARMREG_R1, ARMREG_PC, ARMREG_R1); + /* Branch to it */ + ARM_BLX_REG (code, ARMREG_R1); + + g_assert (code - buf == 16); + + /* Emit it */ + emit_bytes (acfg, buf, code - buf); + /* + * Only one offset is needed, since the second one would be equal to the + * first one. + */ + emit_symbol_diff (acfg, acfg->got_symbol, ".", (offset * sizeof (gpointer)) - 4 + 4); + //emit_symbol_diff (acfg, acfg->got_symbol, ".", ((offset + 1) * sizeof (gpointer)) - 4 + 8); +#elif defined(TARGET_ARM64) + arm64_emit_specific_trampoline (acfg, offset, tramp_size); +#elif defined(TARGET_POWERPC) + guint8 buf [128]; + guint8 *code; + + *tramp_size = 4; + code = buf; + + /* + * PPC has no ip relative addressing, so we need to compute the address + * of the mscorlib got. That is slow and complex, so instead, we store it + * in the second got slot of every aot image. The caller already computed + * the address of its got and placed it into r30. + */ + emit_unset_mode (acfg); + /* Load mscorlib got address */ + fprintf (acfg->fp, "%s 0, %d(30)\n", PPC_LD_OP, (int)sizeof (gpointer)); + /* Load generic trampoline address */ + fprintf (acfg->fp, "lis 11, %d@h\n", (int)(offset * sizeof (gpointer))); + fprintf (acfg->fp, "ori 11, 11, %d@l\n", (int)(offset * sizeof (gpointer))); + fprintf (acfg->fp, "%s 11, 11, 0\n", PPC_LDX_OP); +#ifdef PPC_USES_FUNCTION_DESCRIPTOR + fprintf (acfg->fp, "%s 11, 0(11)\n", PPC_LD_OP); +#endif + fprintf (acfg->fp, "mtctr 11\n"); + /* Load trampoline argument */ + /* On ppc, we pass it normally to the generic trampoline */ + fprintf (acfg->fp, "lis 11, %d@h\n", (int)((offset + 1) * sizeof (gpointer))); + fprintf (acfg->fp, "ori 11, 11, %d@l\n", (int)((offset + 1) * sizeof (gpointer))); + fprintf (acfg->fp, "%s 0, 11, 0\n", PPC_LDX_OP); + /* Branch to generic trampoline */ + fprintf (acfg->fp, "bctr\n"); + +#ifdef PPC_USES_FUNCTION_DESCRIPTOR + *tramp_size = 10 * 4; +#else + *tramp_size = 9 * 4; +#endif +#elif defined(TARGET_X86) + guint8 buf [128]; + guint8 *code; + + /* Similar to the PPC code above */ + + /* FIXME: Could this clobber the register needed by get_vcall_slot () ? */ + + code = buf; + /* Load mscorlib got address */ + x86_mov_reg_membase (code, X86_ECX, MONO_ARCH_GOT_REG, sizeof (gpointer), 4); + /* Push trampoline argument */ + x86_push_membase (code, X86_ECX, (offset + 1) * sizeof (gpointer)); + /* Load generic trampoline address */ + x86_mov_reg_membase (code, X86_ECX, X86_ECX, offset * sizeof (gpointer), 4); + /* Branch to generic trampoline */ + x86_jump_reg (code, X86_ECX); + + emit_bytes (acfg, buf, code - buf); + + *tramp_size = 17; + g_assert (code - buf == *tramp_size); +#else + g_assert_not_reached (); +#endif +} + +/* + * arch_emit_unbox_trampoline: + * + * Emit code for the unbox trampoline for METHOD used in the full-aot case. + * CALL_TARGET is the symbol pointing to the native code of METHOD. + */ +static void +arch_emit_unbox_trampoline (MonoAotCompile *acfg, MonoCompile *cfg, MonoMethod *method, const char *call_target) +{ +#if defined(TARGET_AMD64) + guint8 buf [32]; + guint8 *code; + int this_reg; + + this_reg = mono_arch_get_this_arg_reg (NULL); + code = buf; + amd64_alu_reg_imm (code, X86_ADD, this_reg, sizeof (MonoObject)); + + emit_bytes (acfg, buf, code - buf); + /* jump */ + if (acfg->llvm) { + emit_unset_mode (acfg); + fprintf (acfg->fp, "jmp %s\n", call_target); + } else { + emit_byte (acfg, '\xe9'); + emit_symbol_diff (acfg, call_target, ".", -4); + } +#elif defined(TARGET_X86) + guint8 buf [32]; + guint8 *code; + int this_pos = 4; + + code = buf; + + x86_alu_membase_imm (code, X86_ADD, X86_ESP, this_pos, sizeof (MonoObject)); + + emit_bytes (acfg, buf, code - buf); + + /* jump */ + emit_byte (acfg, '\xe9'); + emit_symbol_diff (acfg, call_target, ".", -4); +#elif defined(TARGET_ARM) + guint8 buf [128]; + guint8 *code; + + if (acfg->thumb_mixed && cfg->compile_llvm) { + fprintf (acfg->fp, "add r0, r0, #%d\n", (int)sizeof (MonoObject)); + fprintf (acfg->fp, "b %s\n", call_target); + fprintf (acfg->fp, ".arm\n"); + fprintf (acfg->fp, ".align 2\n"); + return; + } + + code = buf; + + ARM_ADD_REG_IMM8 (code, ARMREG_R0, ARMREG_R0, sizeof (MonoObject)); + + emit_bytes (acfg, buf, code - buf); + /* jump to method */ + if (acfg->thumb_mixed && cfg->compile_llvm) + fprintf (acfg->fp, "\n\tbx %s\n", call_target); + else + fprintf (acfg->fp, "\n\tb %s\n", call_target); +#elif defined(TARGET_ARM64) + arm64_emit_unbox_trampoline (acfg, cfg, method, call_target); +#elif defined(TARGET_POWERPC) + int this_pos = 3; + + fprintf (acfg->fp, "\n\taddi %d, %d, %d\n", this_pos, this_pos, (int)sizeof (MonoObject)); + fprintf (acfg->fp, "\n\tb %s\n", call_target); +#else + g_assert_not_reached (); +#endif +} + +/* + * arch_emit_static_rgctx_trampoline: + * + * Emit code for a static rgctx trampoline. OFFSET is the offset of the first of + * two GOT slots which contain the rgctx argument, and the method to jump to. + * TRAMP_SIZE is set to the size of the emitted trampoline. + * These kinds of trampolines cannot be enumerated statically, since there could + * be one trampoline per method instantiation, so we emit the same code for all + * trampolines, and parameterize them using two GOT slots. + */ +static void +arch_emit_static_rgctx_trampoline (MonoAotCompile *acfg, int offset, int *tramp_size) +{ +#if defined(TARGET_AMD64) + /* This should be exactly 13 bytes long */ + *tramp_size = 13; + + if (acfg->llvm) { + emit_unset_mode (acfg); + fprintf (acfg->fp, "mov %s+%d(%%rip), %%r10\n", acfg->got_symbol, (int)(offset * sizeof (gpointer))); + fprintf (acfg->fp, "jmp *%s+%d(%%rip)\n", acfg->got_symbol, (int)((offset + 1) * sizeof (gpointer))); + } else { + /* mov (%rip), %r10 */ + emit_byte (acfg, '\x4d'); + emit_byte (acfg, '\x8b'); + emit_byte (acfg, '\x15'); + emit_symbol_diff (acfg, acfg->got_symbol, ".", (offset * sizeof (gpointer)) - 4); + + /* jmp *(%rip) */ + emit_byte (acfg, '\xff'); + emit_byte (acfg, '\x25'); + emit_symbol_diff (acfg, acfg->got_symbol, ".", ((offset + 1) * sizeof (gpointer)) - 4); + } +#elif defined(TARGET_ARM) + guint8 buf [128]; + guint8 *code; + + /* This should be exactly 24 bytes long */ + *tramp_size = 24; + code = buf; + /* Load rgctx value */ + ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 8); + ARM_LDR_REG_REG (code, MONO_ARCH_RGCTX_REG, ARMREG_PC, ARMREG_IP); + /* Load branch addr + branch */ + ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 4); + ARM_LDR_REG_REG (code, ARMREG_PC, ARMREG_PC, ARMREG_IP); + + g_assert (code - buf == 16); + + /* Emit it */ + emit_bytes (acfg, buf, code - buf); + emit_symbol_diff (acfg, acfg->got_symbol, ".", (offset * sizeof (gpointer)) - 4 + 8); + emit_symbol_diff (acfg, acfg->got_symbol, ".", ((offset + 1) * sizeof (gpointer)) - 4 + 4); +#elif defined(TARGET_ARM64) + arm64_emit_static_rgctx_trampoline (acfg, offset, tramp_size); +#elif defined(TARGET_POWERPC) + guint8 buf [128]; + guint8 *code; + + *tramp_size = 4; + code = buf; + + /* + * PPC has no ip relative addressing, so we need to compute the address + * of the mscorlib got. That is slow and complex, so instead, we store it + * in the second got slot of every aot image. The caller already computed + * the address of its got and placed it into r30. + */ + emit_unset_mode (acfg); + /* Load mscorlib got address */ + fprintf (acfg->fp, "%s 0, %d(30)\n", PPC_LD_OP, (int)sizeof (gpointer)); + /* Load rgctx */ + fprintf (acfg->fp, "lis 11, %d@h\n", (int)(offset * sizeof (gpointer))); + fprintf (acfg->fp, "ori 11, 11, %d@l\n", (int)(offset * sizeof (gpointer))); + fprintf (acfg->fp, "%s %d, 11, 0\n", PPC_LDX_OP, MONO_ARCH_RGCTX_REG); + /* Load target address */ + fprintf (acfg->fp, "lis 11, %d@h\n", (int)((offset + 1) * sizeof (gpointer))); + fprintf (acfg->fp, "ori 11, 11, %d@l\n", (int)((offset + 1) * sizeof (gpointer))); + fprintf (acfg->fp, "%s 11, 11, 0\n", PPC_LDX_OP); +#ifdef PPC_USES_FUNCTION_DESCRIPTOR + fprintf (acfg->fp, "%s 2, %d(11)\n", PPC_LD_OP, (int)sizeof (gpointer)); + fprintf (acfg->fp, "%s 11, 0(11)\n", PPC_LD_OP); +#endif + fprintf (acfg->fp, "mtctr 11\n"); + /* Branch to the target address */ + fprintf (acfg->fp, "bctr\n"); + +#ifdef PPC_USES_FUNCTION_DESCRIPTOR + *tramp_size = 11 * 4; +#else + *tramp_size = 9 * 4; +#endif + +#elif defined(TARGET_X86) + guint8 buf [128]; + guint8 *code; + + /* Similar to the PPC code above */ + + g_assert (MONO_ARCH_RGCTX_REG != X86_ECX); + + code = buf; + /* Load mscorlib got address */ + x86_mov_reg_membase (code, X86_ECX, MONO_ARCH_GOT_REG, sizeof (gpointer), 4); + /* Load arg */ + x86_mov_reg_membase (code, MONO_ARCH_RGCTX_REG, X86_ECX, offset * sizeof (gpointer), 4); + /* Branch to the target address */ + x86_jump_membase (code, X86_ECX, (offset + 1) * sizeof (gpointer)); + + emit_bytes (acfg, buf, code - buf); + + *tramp_size = 15; + g_assert (code - buf == *tramp_size); +#else + g_assert_not_reached (); +#endif +} + +/* + * arch_emit_imt_trampoline: + * + * Emit an IMT trampoline usable in full-aot mode. The trampoline uses 1 got slot which + * points to an array of pointer pairs. The pairs of the form [key, ptr], where + * key is the IMT key, and ptr holds the address of a memory location holding + * the address to branch to if the IMT arg matches the key. The array is + * terminated by a pair whose key is NULL, and whose ptr is the address of the + * fail_tramp. + * TRAMP_SIZE is set to the size of the emitted trampoline. + */ +static void +arch_emit_imt_trampoline (MonoAotCompile *acfg, int offset, int *tramp_size) +{ +#if defined(TARGET_AMD64) + guint8 *buf, *code; + guint8 *labels [16]; + guint8 mov_buf[3]; + guint8 *mov_buf_ptr = mov_buf; + + const int kSizeOfMove = 7; + + code = buf = (guint8 *)g_malloc (256); + + /* FIXME: Optimize this, i.e. use binary search etc. */ + /* Maybe move the body into a separate function (slower, but much smaller) */ + + /* MONO_ARCH_IMT_SCRATCH_REG is a free register */ + + if (acfg->llvm) { + emit_unset_mode (acfg); + fprintf (acfg->fp, "mov %s+%d(%%rip), %s\n", acfg->got_symbol, (int)(offset * sizeof (gpointer)), mono_arch_regname (MONO_ARCH_IMT_SCRATCH_REG)); + } + + labels [0] = code; + amd64_alu_membase_imm (code, X86_CMP, MONO_ARCH_IMT_SCRATCH_REG, 0, 0); + labels [1] = code; + amd64_branch8 (code, X86_CC_Z, 0, FALSE); + + /* Check key */ + amd64_alu_membase_reg_size (code, X86_CMP, MONO_ARCH_IMT_SCRATCH_REG, 0, MONO_ARCH_IMT_REG, sizeof (gpointer)); + labels [2] = code; + amd64_branch8 (code, X86_CC_Z, 0, FALSE); + + /* Loop footer */ + amd64_alu_reg_imm (code, X86_ADD, MONO_ARCH_IMT_SCRATCH_REG, 2 * sizeof (gpointer)); + amd64_jump_code (code, labels [0]); + + /* Match */ + mono_amd64_patch (labels [2], code); + amd64_mov_reg_membase (code, MONO_ARCH_IMT_SCRATCH_REG, MONO_ARCH_IMT_SCRATCH_REG, sizeof (gpointer), sizeof (gpointer)); + amd64_jump_membase (code, MONO_ARCH_IMT_SCRATCH_REG, 0); + + /* No match */ + mono_amd64_patch (labels [1], code); + /* Load fail tramp */ + amd64_alu_reg_imm (code, X86_ADD, MONO_ARCH_IMT_SCRATCH_REG, sizeof (gpointer)); + /* Check if there is a fail tramp */ + amd64_alu_membase_imm (code, X86_CMP, MONO_ARCH_IMT_SCRATCH_REG, 0, 0); + labels [3] = code; + amd64_branch8 (code, X86_CC_Z, 0, FALSE); + /* Jump to fail tramp */ + amd64_jump_membase (code, MONO_ARCH_IMT_SCRATCH_REG, 0); + + /* Fail */ + mono_amd64_patch (labels [3], code); + x86_breakpoint (code); + + if (!acfg->llvm) { + /* mov (%rip), MONO_ARCH_IMT_SCRATCH_REG */ + amd64_emit_rex (mov_buf_ptr, sizeof(gpointer), MONO_ARCH_IMT_SCRATCH_REG, 0, AMD64_RIP); + *(mov_buf_ptr)++ = (unsigned char)0x8b; /* mov opcode */ + x86_address_byte (mov_buf_ptr, 0, MONO_ARCH_IMT_SCRATCH_REG & 0x7, 5); + emit_bytes (acfg, mov_buf, mov_buf_ptr - mov_buf); + emit_symbol_diff (acfg, acfg->got_symbol, ".", (offset * sizeof (gpointer)) - 4); + } + emit_bytes (acfg, buf, code - buf); + + *tramp_size = code - buf + kSizeOfMove; + + g_free (buf); + +#elif defined(TARGET_X86) + guint8 *buf, *code; + guint8 *labels [16]; + + code = buf = g_malloc (256); + + /* Allocate a temporary stack slot */ + x86_push_reg (code, X86_EAX); + /* Save EAX */ + x86_push_reg (code, X86_EAX); + + /* Load mscorlib got address */ + x86_mov_reg_membase (code, X86_EAX, MONO_ARCH_GOT_REG, sizeof (gpointer), 4); + /* Load arg */ + x86_mov_reg_membase (code, X86_EAX, X86_EAX, offset * sizeof (gpointer), 4); + + labels [0] = code; + x86_alu_membase_imm (code, X86_CMP, X86_EAX, 0, 0); + labels [1] = code; + x86_branch8 (code, X86_CC_Z, FALSE, 0); + + /* Check key */ + x86_alu_membase_reg (code, X86_CMP, X86_EAX, 0, MONO_ARCH_IMT_REG); + labels [2] = code; + x86_branch8 (code, X86_CC_Z, FALSE, 0); + + /* Loop footer */ + x86_alu_reg_imm (code, X86_ADD, X86_EAX, 2 * sizeof (gpointer)); + x86_jump_code (code, labels [0]); + + /* Match */ + mono_x86_patch (labels [2], code); + x86_mov_reg_membase (code, X86_EAX, X86_EAX, sizeof (gpointer), 4); + x86_mov_reg_membase (code, X86_EAX, X86_EAX, 0, 4); + /* Save the target address to the temporary stack location */ + x86_mov_membase_reg (code, X86_ESP, 4, X86_EAX, 4); + /* Restore EAX */ + x86_pop_reg (code, X86_EAX); + /* Jump to the target address */ + x86_ret (code); + + /* No match */ + mono_x86_patch (labels [1], code); + /* Load fail tramp */ + x86_mov_reg_membase (code, X86_EAX, X86_EAX, sizeof (gpointer), 4); + x86_alu_membase_imm (code, X86_CMP, X86_EAX, 0, 0); + labels [3] = code; + x86_branch8 (code, X86_CC_Z, FALSE, 0); + /* Jump to fail tramp */ + x86_mov_membase_reg (code, X86_ESP, 4, X86_EAX, 4); + x86_pop_reg (code, X86_EAX); + x86_ret (code); + + /* Fail */ + mono_x86_patch (labels [3], code); + x86_breakpoint (code); + + emit_bytes (acfg, buf, code - buf); + + *tramp_size = code - buf; + + g_free (buf); + +#elif defined(TARGET_ARM) + guint8 buf [128]; + guint8 *code, *code2, *labels [16]; + + code = buf; + + /* The IMT method is in v5 */ + + /* Need at least two free registers, plus a slot for storing the pc */ + ARM_PUSH (code, (1 << ARMREG_R0)|(1 << ARMREG_R1)|(1 << ARMREG_R2)); + labels [0] = code; + /* Load the parameter from the GOT */ + ARM_LDR_IMM (code, ARMREG_R0, ARMREG_PC, 0); + ARM_LDR_REG_REG (code, ARMREG_R0, ARMREG_PC, ARMREG_R0); + + labels [1] = code; + ARM_LDR_IMM (code, ARMREG_R1, ARMREG_R0, 0); + ARM_CMP_REG_REG (code, ARMREG_R1, ARMREG_V5); + labels [2] = code; + ARM_B_COND (code, ARMCOND_EQ, 0); + + /* End-of-loop check */ + ARM_CMP_REG_IMM (code, ARMREG_R1, 0, 0); + labels [3] = code; + ARM_B_COND (code, ARMCOND_EQ, 0); + + /* Loop footer */ + ARM_ADD_REG_IMM8 (code, ARMREG_R0, ARMREG_R0, sizeof (gpointer) * 2); + labels [4] = code; + ARM_B (code, 0); + arm_patch (labels [4], labels [1]); + + /* Match */ + arm_patch (labels [2], code); + ARM_LDR_IMM (code, ARMREG_R0, ARMREG_R0, 4); + ARM_LDR_IMM (code, ARMREG_R0, ARMREG_R0, 0); + /* Save it to the third stack slot */ + ARM_STR_IMM (code, ARMREG_R0, ARMREG_SP, 8); + /* Restore the registers and branch */ + ARM_POP (code, (1 << ARMREG_R0)|(1 << ARMREG_R1)|(1 << ARMREG_PC)); + + /* No match */ + arm_patch (labels [3], code); + ARM_LDR_IMM (code, ARMREG_R0, ARMREG_R0, 4); + ARM_STR_IMM (code, ARMREG_R0, ARMREG_SP, 8); + ARM_POP (code, (1 << ARMREG_R0)|(1 << ARMREG_R1)|(1 << ARMREG_PC)); + + /* Fixup offset */ + code2 = labels [0]; + ARM_LDR_IMM (code2, ARMREG_R0, ARMREG_PC, (code - (labels [0] + 8))); + + emit_bytes (acfg, buf, code - buf); + emit_symbol_diff (acfg, acfg->got_symbol, ".", (offset * sizeof (gpointer)) + (code - (labels [0] + 8)) - 4); + + *tramp_size = code - buf + 4; +#elif defined(TARGET_ARM64) + arm64_emit_imt_trampoline (acfg, offset, tramp_size); +#elif defined(TARGET_POWERPC) + guint8 buf [128]; + guint8 *code, *labels [16]; + + code = buf; + + /* Load the mscorlib got address */ + ppc_ldptr (code, ppc_r12, sizeof (gpointer), ppc_r30); + /* Load the parameter from the GOT */ + ppc_load (code, ppc_r0, offset * sizeof (gpointer)); + ppc_ldptr_indexed (code, ppc_r12, ppc_r12, ppc_r0); + + /* Load and check key */ + labels [1] = code; + ppc_ldptr (code, ppc_r0, 0, ppc_r12); + ppc_cmp (code, 0, sizeof (gpointer) == 8 ? 1 : 0, ppc_r0, MONO_ARCH_IMT_REG); + labels [2] = code; + ppc_bc (code, PPC_BR_TRUE, PPC_BR_EQ, 0); + + /* End-of-loop check */ + ppc_cmpi (code, 0, sizeof (gpointer) == 8 ? 1 : 0, ppc_r0, 0); + labels [3] = code; + ppc_bc (code, PPC_BR_TRUE, PPC_BR_EQ, 0); + + /* Loop footer */ + ppc_addi (code, ppc_r12, ppc_r12, 2 * sizeof (gpointer)); + labels [4] = code; + ppc_b (code, 0); + mono_ppc_patch (labels [4], labels [1]); + + /* Match */ + mono_ppc_patch (labels [2], code); + ppc_ldptr (code, ppc_r12, sizeof (gpointer), ppc_r12); + /* r12 now contains the value of the vtable slot */ + /* this is not a function descriptor on ppc64 */ + ppc_ldptr (code, ppc_r12, 0, ppc_r12); + ppc_mtctr (code, ppc_r12); + ppc_bcctr (code, PPC_BR_ALWAYS, 0); + + /* Fail */ + mono_ppc_patch (labels [3], code); + /* FIXME: */ + ppc_break (code); + + *tramp_size = code - buf; + + emit_bytes (acfg, buf, code - buf); +#else + g_assert_not_reached (); +#endif +} + + +#if defined (TARGET_AMD64) + +static void +amd64_emit_load_got_slot (MonoAotCompile *acfg, int dreg, int got_slot) +{ + + g_assert (acfg->fp); + emit_unset_mode (acfg); + + fprintf (acfg->fp, "mov %s+%d(%%rip), %s\n", acfg->got_symbol, (unsigned int) ((got_slot * sizeof (gpointer))), mono_arch_regname (dreg)); +} + +#endif + + +/* + * arch_emit_gsharedvt_arg_trampoline: + * + * Emit code for a gsharedvt arg trampoline. OFFSET is the offset of the first of + * two GOT slots which contain the argument, and the code to jump to. + * TRAMP_SIZE is set to the size of the emitted trampoline. + * These kinds of trampolines cannot be enumerated statically, since there could + * be one trampoline per method instantiation, so we emit the same code for all + * trampolines, and parameterize them using two GOT slots. + */ +static void +arch_emit_gsharedvt_arg_trampoline (MonoAotCompile *acfg, int offset, int *tramp_size) +{ +#if defined(TARGET_X86) + guint8 buf [128]; + guint8 *code; + + /* Similar to the PPC code above */ + + g_assert (MONO_ARCH_RGCTX_REG != X86_ECX); + + code = buf; + /* Load mscorlib got address */ + x86_mov_reg_membase (code, X86_ECX, MONO_ARCH_GOT_REG, sizeof (gpointer), 4); + /* Load arg */ + x86_mov_reg_membase (code, X86_EAX, X86_ECX, offset * sizeof (gpointer), 4); + /* Branch to the target address */ + x86_jump_membase (code, X86_ECX, (offset + 1) * sizeof (gpointer)); + + emit_bytes (acfg, buf, code - buf); + + *tramp_size = 15; + g_assert (code - buf == *tramp_size); +#elif defined(TARGET_ARM) + guint8 buf [128]; + guint8 *code; + + /* The same as mono_arch_get_gsharedvt_arg_trampoline (), but for AOT */ + /* Similar to arch_emit_specific_trampoline () */ + *tramp_size = 24; + code = buf; + ARM_PUSH (code, (1 << ARMREG_R0) | (1 << ARMREG_R1) | (1 << ARMREG_R2) | (1 << ARMREG_R3)); + ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 8); + /* Load the arg value from the GOT */ + ARM_LDR_REG_REG (code, ARMREG_R0, ARMREG_PC, ARMREG_R1); + /* Load the addr from the GOT */ + ARM_LDR_REG_REG (code, ARMREG_R1, ARMREG_PC, ARMREG_R1); + /* Branch to it */ + ARM_BX (code, ARMREG_R1); + + g_assert (code - buf == 20); + + /* Emit it */ + emit_bytes (acfg, buf, code - buf); + emit_symbol_diff (acfg, acfg->got_symbol, ".", (offset * sizeof (gpointer)) + 4); +#elif defined(TARGET_ARM64) + arm64_emit_gsharedvt_arg_trampoline (acfg, offset, tramp_size); +#elif defined (TARGET_AMD64) + + amd64_emit_load_got_slot (acfg, AMD64_RAX, offset); + amd64_emit_load_got_slot (acfg, MONO_ARCH_IMT_SCRATCH_REG, offset + 1); + g_assert (AMD64_R11 == MONO_ARCH_IMT_SCRATCH_REG); + fprintf (acfg->fp, "jmp *%%r11\n"); + + *tramp_size = 0x11; +#else + g_assert_not_reached (); +#endif +} + +/* END OF ARCH SPECIFIC CODE */ + +static guint32 +mono_get_field_token (MonoClassField *field) +{ + MonoClass *klass = field->parent; + int i; + + int fcount = mono_class_get_field_count (klass); + for (i = 0; i < fcount; ++i) { + if (field == &klass->fields [i]) + return MONO_TOKEN_FIELD_DEF | (mono_class_get_first_field_idx (klass) + 1 + i); + } + + g_assert_not_reached (); + return 0; +} + +static inline void +encode_value (gint32 value, guint8 *buf, guint8 **endbuf) +{ + guint8 *p = buf; + + //printf ("ENCODE: %d 0x%x.\n", value, value); + + /* + * Same encoding as the one used in the metadata, extended to handle values + * greater than 0x1fffffff. + */ + if ((value >= 0) && (value <= 127)) + *p++ = value; + else if ((value >= 0) && (value <= 16383)) { + p [0] = 0x80 | (value >> 8); + p [1] = value & 0xff; + p += 2; + } else if ((value >= 0) && (value <= 0x1fffffff)) { + p [0] = (value >> 24) | 0xc0; + p [1] = (value >> 16) & 0xff; + p [2] = (value >> 8) & 0xff; + p [3] = value & 0xff; + p += 4; + } + else { + p [0] = 0xff; + p [1] = (value >> 24) & 0xff; + p [2] = (value >> 16) & 0xff; + p [3] = (value >> 8) & 0xff; + p [4] = value & 0xff; + p += 5; + } + if (endbuf) + *endbuf = p; +} + +static void +stream_init (MonoDynamicStream *sh) +{ + sh->index = 0; + sh->alloc_size = 4096; + sh->data = (char *)g_malloc (4096); + + /* So offsets are > 0 */ + sh->data [0] = 0; + sh->index ++; +} + +static void +make_room_in_stream (MonoDynamicStream *stream, int size) +{ + if (size <= stream->alloc_size) + return; + + while (stream->alloc_size <= size) { + if (stream->alloc_size < 4096) + stream->alloc_size = 4096; + else + stream->alloc_size *= 2; + } + + stream->data = (char *)g_realloc (stream->data, stream->alloc_size); +} + +static guint32 +add_stream_data (MonoDynamicStream *stream, const char *data, guint32 len) +{ + guint32 idx; + + make_room_in_stream (stream, stream->index + len); + memcpy (stream->data + stream->index, data, len); + idx = stream->index; + stream->index += len; + return idx; +} + +/* + * add_to_blob: + * + * Add data to the binary blob inside the aot image. Returns the offset inside the + * blob where the data was stored. + */ +static guint32 +add_to_blob (MonoAotCompile *acfg, const guint8 *data, guint32 data_len) +{ + g_assert (!acfg->blob_closed); + + if (acfg->blob.alloc_size == 0) + stream_init (&acfg->blob); + + return add_stream_data (&acfg->blob, (char*)data, data_len); +} + +static guint32 +add_to_blob_aligned (MonoAotCompile *acfg, const guint8 *data, guint32 data_len, guint32 align) +{ + char buf [4] = {0}; + guint32 count; + + if (acfg->blob.alloc_size == 0) + stream_init (&acfg->blob); + + count = acfg->blob.index % align; + + /* we assume the stream data will be aligned */ + if (count) + add_stream_data (&acfg->blob, buf, 4 - count); + + return add_stream_data (&acfg->blob, (char*)data, data_len); +} + +/* Emit a table of data into the aot image */ +static void +emit_aot_data (MonoAotCompile *acfg, MonoAotFileTable table, const char *symbol, guint8 *data, int size) +{ + if (acfg->data_outfile) { + acfg->table_offsets [(int)table] = acfg->datafile_offset; + fwrite (data,1, size, acfg->data_outfile); + acfg->datafile_offset += size; + // align the data to 8 bytes. Put zeros in the file (so that every build results in consistent output). + int align = 8 - size % 8; + acfg->datafile_offset += align; + guint8 align_buf [16]; + memset (&align_buf, 0, sizeof (align_buf)); + fwrite (align_buf, align, 1, acfg->data_outfile); + } else if (acfg->llvm) { + mono_llvm_emit_aot_data (symbol, data, size); + } else { + emit_section_change (acfg, RODATA_SECT, 0); + emit_alignment (acfg, 8); + emit_label (acfg, symbol); + emit_bytes (acfg, data, size); + } +} + +/* + * emit_offset_table: + * + * Emit a table of increasing offsets in a compact form using differential encoding. + * There is an index entry for each GROUP_SIZE number of entries. The greater the + * group size, the more compact the table becomes, but the slower it becomes to compute + * a given entry. Returns the size of the table. + */ +static guint32 +emit_offset_table (MonoAotCompile *acfg, const char *symbol, MonoAotFileTable table, int noffsets, int group_size, gint32 *offsets) +{ + gint32 current_offset; + int i, buf_size, ngroups, index_entry_size; + guint8 *p, *buf; + guint8 *data_p, *data_buf; + guint32 *index_offsets; + + ngroups = (noffsets + (group_size - 1)) / group_size; + + index_offsets = g_new0 (guint32, ngroups); + + buf_size = noffsets * 4; + p = buf = (guint8 *)g_malloc0 (buf_size); + + current_offset = 0; + for (i = 0; i < noffsets; ++i) { + //printf ("D: %d -> %d\n", i, offsets [i]); + if ((i % group_size) == 0) { + index_offsets [i / group_size] = p - buf; + /* Emit the full value for these entries */ + encode_value (offsets [i], p, &p); + } else { + /* The offsets are allowed to be non-increasing */ + //g_assert (offsets [i] >= current_offset); + encode_value (offsets [i] - current_offset, p, &p); + } + current_offset = offsets [i]; + } + data_buf = buf; + data_p = p; + + if (ngroups && index_offsets [ngroups - 1] < 65000) + index_entry_size = 2; + else + index_entry_size = 4; + + buf_size = (data_p - data_buf) + (ngroups * 4) + 16; + p = buf = (guint8 *)g_malloc0 (buf_size); + + /* Emit the header */ + encode_int (noffsets, p, &p); + encode_int (group_size, p, &p); + encode_int (ngroups, p, &p); + encode_int (index_entry_size, p, &p); + + /* Emit the index */ + for (i = 0; i < ngroups; ++i) { + if (index_entry_size == 2) + encode_int16 (index_offsets [i], p, &p); + else + encode_int (index_offsets [i], p, &p); + } + /* Emit the data */ + memcpy (p, data_buf, data_p - data_buf); + p += data_p - data_buf; + + g_assert (p - buf <= buf_size); + + emit_aot_data (acfg, table, symbol, buf, p - buf); + + g_free (buf); + g_free (data_buf); + + return (int)(p - buf); +} + +static guint32 +get_image_index (MonoAotCompile *cfg, MonoImage *image) +{ + guint32 index; + + index = GPOINTER_TO_UINT (g_hash_table_lookup (cfg->image_hash, image)); + if (index) + return index - 1; + else { + index = g_hash_table_size (cfg->image_hash); + g_hash_table_insert (cfg->image_hash, image, GUINT_TO_POINTER (index + 1)); + g_ptr_array_add (cfg->image_table, image); + return index; + } +} + +static guint32 +find_typespec_for_class (MonoAotCompile *acfg, MonoClass *klass) +{ + int i; + int len = acfg->image->tables [MONO_TABLE_TYPESPEC].rows; + + /* FIXME: Search referenced images as well */ + if (!acfg->typespec_classes) { + acfg->typespec_classes = g_hash_table_new (NULL, NULL); + for (i = 0; i < len; i++) { + MonoError error; + int typespec = MONO_TOKEN_TYPE_SPEC | (i + 1); + MonoClass *klass_key = mono_class_get_and_inflate_typespec_checked (acfg->image, typespec, NULL, &error); + if (!is_ok (&error)) { + mono_error_cleanup (&error); + continue; + } + g_hash_table_insert (acfg->typespec_classes, klass_key, GINT_TO_POINTER (typespec)); + } + } + return GPOINTER_TO_INT (g_hash_table_lookup (acfg->typespec_classes, klass)); +} + +static void +encode_method_ref (MonoAotCompile *acfg, MonoMethod *method, guint8 *buf, guint8 **endbuf); + +static void +encode_klass_ref (MonoAotCompile *acfg, MonoClass *klass, guint8 *buf, guint8 **endbuf); + +static void +encode_ginst (MonoAotCompile *acfg, MonoGenericInst *inst, guint8 *buf, guint8 **endbuf); + +static void +encode_type (MonoAotCompile *acfg, MonoType *t, guint8 *buf, guint8 **endbuf); + +static void +encode_klass_ref_inner (MonoAotCompile *acfg, MonoClass *klass, guint8 *buf, guint8 **endbuf) +{ + guint8 *p = buf; + + /* + * The encoding begins with one of the MONO_AOT_TYPEREF values, followed by additional + * information. + */ + + if (mono_class_is_ginst (klass)) { + guint32 token; + g_assert (klass->type_token); + + /* Find a typespec for a class if possible */ + token = find_typespec_for_class (acfg, klass); + if (token) { + encode_value (MONO_AOT_TYPEREF_TYPESPEC_TOKEN, p, &p); + encode_value (token, p, &p); + } else { + MonoClass *gclass = mono_class_get_generic_class (klass)->container_class; + MonoGenericInst *inst = mono_class_get_generic_class (klass)->context.class_inst; + static int count = 0; + guint8 *p1 = p; + + encode_value (MONO_AOT_TYPEREF_GINST, p, &p); + encode_klass_ref (acfg, gclass, p, &p); + encode_ginst (acfg, inst, p, &p); + + count += p - p1; + } + } else if (klass->type_token) { + int iindex = get_image_index (acfg, klass->image); + + g_assert (mono_metadata_token_code (klass->type_token) == MONO_TOKEN_TYPE_DEF); + if (iindex == 0) { + encode_value (MONO_AOT_TYPEREF_TYPEDEF_INDEX, p, &p); + encode_value (klass->type_token - MONO_TOKEN_TYPE_DEF, p, &p); + } else { + encode_value (MONO_AOT_TYPEREF_TYPEDEF_INDEX_IMAGE, p, &p); + encode_value (klass->type_token - MONO_TOKEN_TYPE_DEF, p, &p); + encode_value (get_image_index (acfg, klass->image), p, &p); + } + } else if ((klass->byval_arg.type == MONO_TYPE_VAR) || (klass->byval_arg.type == MONO_TYPE_MVAR)) { + MonoGenericContainer *container = mono_type_get_generic_param_owner (&klass->byval_arg); + MonoGenericParam *par = klass->byval_arg.data.generic_param; + + encode_value (MONO_AOT_TYPEREF_VAR, p, &p); + + encode_value (par->gshared_constraint ? 1 : 0, p, &p); + if (par->gshared_constraint) { + MonoGSharedGenericParam *gpar = (MonoGSharedGenericParam*)par; + encode_type (acfg, par->gshared_constraint, p, &p); + encode_klass_ref (acfg, mono_class_from_generic_parameter_internal (gpar->parent), p, &p); + } else { + encode_value (klass->byval_arg.type, p, &p); + encode_value (mono_type_get_generic_param_num (&klass->byval_arg), p, &p); + + encode_value (container->is_anonymous ? 0 : 1, p, &p); + + if (!container->is_anonymous) { + encode_value (container->is_method, p, &p); + if (container->is_method) + encode_method_ref (acfg, container->owner.method, p, &p); + else + encode_klass_ref (acfg, container->owner.klass, p, &p); + } + } + } else if (klass->byval_arg.type == MONO_TYPE_PTR) { + encode_value (MONO_AOT_TYPEREF_PTR, p, &p); + encode_type (acfg, &klass->byval_arg, p, &p); + } else { + /* Array class */ + g_assert (klass->rank > 0); + encode_value (MONO_AOT_TYPEREF_ARRAY, p, &p); + encode_value (klass->rank, p, &p); + encode_klass_ref (acfg, klass->element_class, p, &p); + } + *endbuf = p; +} + +/* + * encode_klass_ref: + * + * Encode a reference to KLASS. We use our home-grown encoding instead of the + * standard metadata encoding. + */ +static void +encode_klass_ref (MonoAotCompile *acfg, MonoClass *klass, guint8 *buf, guint8 **endbuf) +{ + gboolean shared = FALSE; + + /* + * The encoding of generic instances is large so emit them only once. + */ + if (mono_class_is_ginst (klass)) { + guint32 token; + g_assert (klass->type_token); + + /* Find a typespec for a class if possible */ + token = find_typespec_for_class (acfg, klass); + if (!token) + shared = TRUE; + } else if ((klass->byval_arg.type == MONO_TYPE_VAR) || (klass->byval_arg.type == MONO_TYPE_MVAR)) { + shared = TRUE; + } + + if (shared) { + guint offset = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->klass_blob_hash, klass)); + guint8 *buf2, *p; + + if (!offset) { + buf2 = (guint8 *)g_malloc (1024); + p = buf2; + + encode_klass_ref_inner (acfg, klass, p, &p); + g_assert (p - buf2 < 1024); + + offset = add_to_blob (acfg, buf2, p - buf2); + g_free (buf2); + + g_hash_table_insert (acfg->klass_blob_hash, klass, GUINT_TO_POINTER (offset + 1)); + } else { + offset --; + } + + p = buf; + encode_value (MONO_AOT_TYPEREF_BLOB_INDEX, p, &p); + encode_value (offset, p, &p); + *endbuf = p; + return; + } + + encode_klass_ref_inner (acfg, klass, buf, endbuf); +} + +static void +encode_field_info (MonoAotCompile *cfg, MonoClassField *field, guint8 *buf, guint8 **endbuf) +{ + guint32 token = mono_get_field_token (field); + guint8 *p = buf; + + encode_klass_ref (cfg, field->parent, p, &p); + g_assert (mono_metadata_token_code (token) == MONO_TOKEN_FIELD_DEF); + encode_value (token - MONO_TOKEN_FIELD_DEF, p, &p); + *endbuf = p; +} + +static void +encode_ginst (MonoAotCompile *acfg, MonoGenericInst *inst, guint8 *buf, guint8 **endbuf) +{ + guint8 *p = buf; + int i; + + encode_value (inst->type_argc, p, &p); + for (i = 0; i < inst->type_argc; ++i) + encode_klass_ref (acfg, mono_class_from_mono_type (inst->type_argv [i]), p, &p); + *endbuf = p; +} + +static void +encode_generic_context (MonoAotCompile *acfg, MonoGenericContext *context, guint8 *buf, guint8 **endbuf) +{ + guint8 *p = buf; + MonoGenericInst *inst; + + inst = context->class_inst; + if (inst) { + g_assert (inst->type_argc); + encode_ginst (acfg, inst, p, &p); + } else { + encode_value (0, p, &p); + } + inst = context->method_inst; + if (inst) { + g_assert (inst->type_argc); + encode_ginst (acfg, inst, p, &p); + } else { + encode_value (0, p, &p); + } + *endbuf = p; +} + +static void +encode_type (MonoAotCompile *acfg, MonoType *t, guint8 *buf, guint8 **endbuf) +{ + guint8 *p = buf; + + g_assert (t->num_mods == 0); + /* t->attrs can be ignored */ + //g_assert (t->attrs == 0); + + if (t->pinned) { + *p = MONO_TYPE_PINNED; + ++p; + } + if (t->byref) { + *p = MONO_TYPE_BYREF; + ++p; + } + + *p = t->type; + p ++; + + switch (t->type) { + case MONO_TYPE_VOID: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_CHAR: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_STRING: + case MONO_TYPE_OBJECT: + case MONO_TYPE_TYPEDBYREF: + break; + case MONO_TYPE_VALUETYPE: + case MONO_TYPE_CLASS: + encode_klass_ref (acfg, mono_class_from_mono_type (t), p, &p); + break; + case MONO_TYPE_SZARRAY: + encode_klass_ref (acfg, t->data.klass, p, &p); + break; + case MONO_TYPE_PTR: + encode_type (acfg, t->data.type, p, &p); + break; + case MONO_TYPE_GENERICINST: { + MonoClass *gclass = t->data.generic_class->container_class; + MonoGenericInst *inst = t->data.generic_class->context.class_inst; + + encode_klass_ref (acfg, gclass, p, &p); + encode_ginst (acfg, inst, p, &p); + break; + } + case MONO_TYPE_ARRAY: { + MonoArrayType *array = t->data.array; + int i; + + encode_klass_ref (acfg, array->eklass, p, &p); + encode_value (array->rank, p, &p); + encode_value (array->numsizes, p, &p); + for (i = 0; i < array->numsizes; ++i) + encode_value (array->sizes [i], p, &p); + encode_value (array->numlobounds, p, &p); + for (i = 0; i < array->numlobounds; ++i) + encode_value (array->lobounds [i], p, &p); + break; + } + case MONO_TYPE_VAR: + case MONO_TYPE_MVAR: + encode_klass_ref (acfg, mono_class_from_mono_type (t), p, &p); + break; + default: + g_assert_not_reached (); + } + + *endbuf = p; +} + +static void +encode_signature (MonoAotCompile *acfg, MonoMethodSignature *sig, guint8 *buf, guint8 **endbuf) +{ + guint8 *p = buf; + guint32 flags = 0; + int i; + + /* Similar to the metadata encoding */ + if (sig->generic_param_count) + flags |= 0x10; + if (sig->hasthis) + flags |= 0x20; + if (sig->explicit_this) + flags |= 0x40; + flags |= (sig->call_convention & 0x0F); + + *p = flags; + ++p; + if (sig->generic_param_count) + encode_value (sig->generic_param_count, p, &p); + encode_value (sig->param_count, p, &p); + + encode_type (acfg, sig->ret, p, &p); + for (i = 0; i < sig->param_count; ++i) { + if (sig->sentinelpos == i) { + *p = MONO_TYPE_SENTINEL; + ++p; + } + encode_type (acfg, sig->params [i], p, &p); + } + + *endbuf = p; +} + +#define MAX_IMAGE_INDEX 250 + +static void +encode_method_ref (MonoAotCompile *acfg, MonoMethod *method, guint8 *buf, guint8 **endbuf) +{ + guint32 image_index = get_image_index (acfg, method->klass->image); + guint32 token = method->token; + MonoJumpInfoToken *ji; + guint8 *p = buf; + + /* + * The encoding for most methods is as follows: + * - image index encoded as a leb128 + * - token index encoded as a leb128 + * Values of image index >= MONO_AOT_METHODREF_MIN are used to mark additional + * types of method encodings. + */ + + /* Mark methods which can't use aot trampolines because they need the further + * processing in mono_magic_trampoline () which requires a MonoMethod*. + */ + if ((method->is_generic && (method->flags & METHOD_ATTRIBUTE_VIRTUAL)) || + (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)) + encode_value ((MONO_AOT_METHODREF_NO_AOT_TRAMPOLINE << 24), p, &p); + + if (method->wrapper_type) { + WrapperInfo *info = mono_marshal_get_wrapper_info (method); + + encode_value ((MONO_AOT_METHODREF_WRAPPER << 24), p, &p); + + encode_value (method->wrapper_type, p, &p); + + switch (method->wrapper_type) { + case MONO_WRAPPER_REMOTING_INVOKE: + case MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK: + case MONO_WRAPPER_XDOMAIN_INVOKE: { + MonoMethod *m; + + m = mono_marshal_method_from_wrapper (method); + g_assert (m); + encode_method_ref (acfg, m, p, &p); + break; + } + case MONO_WRAPPER_PROXY_ISINST: + case MONO_WRAPPER_LDFLD: + case MONO_WRAPPER_LDFLDA: + case MONO_WRAPPER_STFLD: { + g_assert (info); + encode_klass_ref (acfg, info->d.proxy.klass, p, &p); + break; + } + case MONO_WRAPPER_ALLOC: { + /* The GC name is saved once in MonoAotFileInfo */ + g_assert (info->d.alloc.alloc_type != -1); + encode_value (info->d.alloc.alloc_type, p, &p); + break; + } + case MONO_WRAPPER_WRITE_BARRIER: { + g_assert (info); + break; + } + case MONO_WRAPPER_STELEMREF: { + g_assert (info); + encode_value (info->subtype, p, &p); + if (info->subtype == WRAPPER_SUBTYPE_VIRTUAL_STELEMREF) + encode_value (info->d.virtual_stelemref.kind, p, &p); + break; + } + case MONO_WRAPPER_UNKNOWN: { + g_assert (info); + encode_value (info->subtype, p, &p); + if (info->subtype == WRAPPER_SUBTYPE_PTR_TO_STRUCTURE || + info->subtype == WRAPPER_SUBTYPE_STRUCTURE_TO_PTR) + encode_klass_ref (acfg, method->klass, p, &p); + else if (info->subtype == WRAPPER_SUBTYPE_SYNCHRONIZED_INNER) + encode_method_ref (acfg, info->d.synchronized_inner.method, p, &p); + else if (info->subtype == WRAPPER_SUBTYPE_ARRAY_ACCESSOR) + encode_method_ref (acfg, info->d.array_accessor.method, p, &p); + else if (info->subtype == WRAPPER_SUBTYPE_INTERP_IN) + encode_signature (acfg, info->d.interp_in.sig, p, &p); + else if (info->subtype == WRAPPER_SUBTYPE_GSHAREDVT_IN_SIG) + encode_signature (acfg, info->d.gsharedvt.sig, p, &p); + else if (info->subtype == WRAPPER_SUBTYPE_GSHAREDVT_OUT_SIG) + encode_signature (acfg, info->d.gsharedvt.sig, p, &p); + break; + } + case MONO_WRAPPER_MANAGED_TO_NATIVE: { + g_assert (info); + encode_value (info->subtype, p, &p); + if (info->subtype == WRAPPER_SUBTYPE_ICALL_WRAPPER) { + strcpy ((char*)p, method->name); + p += strlen (method->name) + 1; + } else if (info->subtype == WRAPPER_SUBTYPE_NATIVE_FUNC_AOT) { + encode_method_ref (acfg, info->d.managed_to_native.method, p, &p); + } else { + g_assert (info->subtype == WRAPPER_SUBTYPE_NONE || info->subtype == WRAPPER_SUBTYPE_PINVOKE); + encode_method_ref (acfg, info->d.managed_to_native.method, p, &p); + } + break; + } + case MONO_WRAPPER_SYNCHRONIZED: { + MonoMethod *m; + + m = mono_marshal_method_from_wrapper (method); + g_assert (m); + g_assert (m != method); + encode_method_ref (acfg, m, p, &p); + break; + } + case MONO_WRAPPER_MANAGED_TO_MANAGED: { + g_assert (info); + encode_value (info->subtype, p, &p); + + if (info->subtype == WRAPPER_SUBTYPE_ELEMENT_ADDR) { + encode_value (info->d.element_addr.rank, p, &p); + encode_value (info->d.element_addr.elem_size, p, &p); + } else if (info->subtype == WRAPPER_SUBTYPE_STRING_CTOR) { + encode_method_ref (acfg, info->d.string_ctor.method, p, &p); + } else { + g_assert_not_reached (); + } + break; + } + case MONO_WRAPPER_CASTCLASS: { + g_assert (info); + encode_value (info->subtype, p, &p); + break; + } + case MONO_WRAPPER_RUNTIME_INVOKE: { + g_assert (info); + encode_value (info->subtype, p, &p); + if (info->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_DIRECT || info->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_VIRTUAL) + encode_method_ref (acfg, info->d.runtime_invoke.method, p, &p); + else if (info->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_NORMAL) + encode_signature (acfg, info->d.runtime_invoke.sig, p, &p); + break; + } + case MONO_WRAPPER_DELEGATE_INVOKE: + case MONO_WRAPPER_DELEGATE_BEGIN_INVOKE: + case MONO_WRAPPER_DELEGATE_END_INVOKE: { + if (method->is_inflated) { + /* These wrappers are identified by their class */ + encode_value (1, p, &p); + encode_klass_ref (acfg, method->klass, p, &p); + } else { + MonoMethodSignature *sig = mono_method_signature (method); + WrapperInfo *info = mono_marshal_get_wrapper_info (method); + + encode_value (0, p, &p); + if (method->wrapper_type == MONO_WRAPPER_DELEGATE_INVOKE) + encode_value (info ? info->subtype : 0, p, &p); + encode_signature (acfg, sig, p, &p); + } + break; + } + case MONO_WRAPPER_NATIVE_TO_MANAGED: { + g_assert (info); + encode_method_ref (acfg, info->d.native_to_managed.method, p, &p); + encode_klass_ref (acfg, info->d.native_to_managed.klass, p, &p); + break; + } + default: + g_assert_not_reached (); + } + } else if (mono_method_signature (method)->is_inflated) { + /* + * This is a generic method, find the original token which referenced it and + * encode that. + * Obtain the token from information recorded by the JIT. + */ + ji = (MonoJumpInfoToken *)g_hash_table_lookup (acfg->token_info_hash, method); + if (ji) { + image_index = get_image_index (acfg, ji->image); + g_assert (image_index < MAX_IMAGE_INDEX); + token = ji->token; + + encode_value ((MONO_AOT_METHODREF_METHODSPEC << 24), p, &p); + encode_value (image_index, p, &p); + encode_value (token, p, &p); + } else { + MonoMethod *declaring; + MonoGenericContext *context = mono_method_get_context (method); + + g_assert (method->is_inflated); + declaring = ((MonoMethodInflated*)method)->declaring; + + /* + * This might be a non-generic method of a generic instance, which + * doesn't have a token since the reference is generated by the JIT + * like Nullable:Box/Unbox, or by generic sharing. + */ + encode_value ((MONO_AOT_METHODREF_GINST << 24), p, &p); + /* Encode the klass */ + encode_klass_ref (acfg, method->klass, p, &p); + /* Encode the method */ + image_index = get_image_index (acfg, method->klass->image); + g_assert (image_index < MAX_IMAGE_INDEX); + g_assert (declaring->token); + token = declaring->token; + g_assert (mono_metadata_token_table (token) == MONO_TABLE_METHOD); + encode_value (image_index, p, &p); + encode_value (token, p, &p); + encode_generic_context (acfg, context, p, &p); + } + } else if (token == 0) { + /* This might be a method of a constructed type like int[,].Set */ + /* Obtain the token from information recorded by the JIT */ + ji = (MonoJumpInfoToken *)g_hash_table_lookup (acfg->token_info_hash, method); + if (ji) { + image_index = get_image_index (acfg, ji->image); + g_assert (image_index < MAX_IMAGE_INDEX); + token = ji->token; + + encode_value ((MONO_AOT_METHODREF_METHODSPEC << 24), p, &p); + encode_value (image_index, p, &p); + encode_value (token, p, &p); + } else { + /* Array methods */ + g_assert (method->klass->rank); + + /* Encode directly */ + encode_value ((MONO_AOT_METHODREF_ARRAY << 24), p, &p); + encode_klass_ref (acfg, method->klass, p, &p); + if (!strcmp (method->name, ".ctor") && mono_method_signature (method)->param_count == method->klass->rank) + encode_value (0, p, &p); + else if (!strcmp (method->name, ".ctor") && mono_method_signature (method)->param_count == method->klass->rank * 2) + encode_value (1, p, &p); + else if (!strcmp (method->name, "Get")) + encode_value (2, p, &p); + else if (!strcmp (method->name, "Address")) + encode_value (3, p, &p); + else if (!strcmp (method->name, "Set")) + encode_value (4, p, &p); + else + g_assert_not_reached (); + } + } else { + g_assert (mono_metadata_token_table (token) == MONO_TABLE_METHOD); + + if (image_index >= MONO_AOT_METHODREF_MIN) { + encode_value ((MONO_AOT_METHODREF_LARGE_IMAGE_INDEX << 24), p, &p); + encode_value (image_index, p, &p); + encode_value (mono_metadata_token_index (token), p, &p); + } else { + encode_value ((image_index << 24) | mono_metadata_token_index (token), p, &p); + } + } + *endbuf = p; +} + +static gint +compare_patches (gconstpointer a, gconstpointer b) +{ + int i, j; + + i = (*(MonoJumpInfo**)a)->ip.i; + j = (*(MonoJumpInfo**)b)->ip.i; + + if (i < j) + return -1; + else + if (i > j) + return 1; + else + return 0; +} + +static G_GNUC_UNUSED char* +patch_to_string (MonoJumpInfo *patch_info) +{ + GString *str; + + str = g_string_new (""); + + g_string_append_printf (str, "%s(", get_patch_name (patch_info->type)); + + switch (patch_info->type) { + case MONO_PATCH_INFO_VTABLE: + mono_type_get_desc (str, &patch_info->data.klass->byval_arg, TRUE); + break; + default: + break; + } + g_string_append_printf (str, ")"); + return g_string_free (str, FALSE); +} + +/* + * is_plt_patch: + * + * Return whenever PATCH_INFO refers to a direct call, and thus requires a + * PLT entry. + */ +static inline gboolean +is_plt_patch (MonoJumpInfo *patch_info) +{ + switch (patch_info->type) { + case MONO_PATCH_INFO_METHOD: + case MONO_PATCH_INFO_INTERNAL_METHOD: + case MONO_PATCH_INFO_JIT_ICALL_ADDR: + case MONO_PATCH_INFO_ICALL_ADDR_CALL: + case MONO_PATCH_INFO_RGCTX_FETCH: + return TRUE; + default: + return FALSE; + } +} + +/* + * get_plt_symbol: + * + * Return the symbol identifying the plt entry PLT_OFFSET. + */ +static char* +get_plt_symbol (MonoAotCompile *acfg, int plt_offset, MonoJumpInfo *patch_info) +{ +#ifdef TARGET_MACH + /* + * The Apple linker reorganizes object files, so it doesn't like branches to local + * labels, since those have no relocations. + */ + return g_strdup_printf ("%sp_%d", acfg->llvm_label_prefix, plt_offset); +#else + return g_strdup_printf ("%sp_%d", acfg->temp_prefix, plt_offset); +#endif +} + +/* + * get_plt_entry: + * + * Return a PLT entry which belongs to the method identified by PATCH_INFO. + */ +static MonoPltEntry* +get_plt_entry (MonoAotCompile *acfg, MonoJumpInfo *patch_info) +{ + MonoPltEntry *res; + gboolean synchronized = FALSE; + static int synchronized_symbol_idx; + + if (!is_plt_patch (patch_info)) + return NULL; + + if (!acfg->patch_to_plt_entry [patch_info->type]) + acfg->patch_to_plt_entry [patch_info->type] = g_hash_table_new (mono_patch_info_hash, mono_patch_info_equal); + res = (MonoPltEntry *)g_hash_table_lookup (acfg->patch_to_plt_entry [patch_info->type], patch_info); + + if (!acfg->llvm && patch_info->type == MONO_PATCH_INFO_METHOD && (patch_info->data.method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)) { + /* + * Allocate a separate PLT slot for each such patch, since some plt + * entries will refer to the method itself, and some will refer to the + * wrapper. + */ + res = NULL; + synchronized = TRUE; + } + + if (!res) { + MonoJumpInfo *new_ji; + + new_ji = mono_patch_info_dup_mp (acfg->mempool, patch_info); + + res = (MonoPltEntry *)mono_mempool_alloc0 (acfg->mempool, sizeof (MonoPltEntry)); + res->plt_offset = acfg->plt_offset; + res->ji = new_ji; + res->symbol = get_plt_symbol (acfg, res->plt_offset, patch_info); + if (acfg->aot_opts.write_symbols) + res->debug_sym = get_plt_entry_debug_sym (acfg, res->ji, acfg->plt_entry_debug_sym_cache); + if (synchronized) { + /* Avoid duplicate symbols because we don't cache */ + res->symbol = g_strdup_printf ("%s_%d", res->symbol, synchronized_symbol_idx); + if (res->debug_sym) + res->debug_sym = g_strdup_printf ("%s_%d", res->debug_sym, synchronized_symbol_idx); + synchronized_symbol_idx ++; + } + if (res->debug_sym) + res->llvm_symbol = g_strdup_printf ("%s_%s_llvm", res->symbol, res->debug_sym); + else + res->llvm_symbol = g_strdup_printf ("%s_llvm", res->symbol); + + g_hash_table_insert (acfg->patch_to_plt_entry [new_ji->type], new_ji, res); + + g_hash_table_insert (acfg->plt_offset_to_entry, GUINT_TO_POINTER (res->plt_offset), res); + + //g_assert (mono_patch_info_equal (patch_info, new_ji)); + //mono_print_ji (patch_info); printf ("\n"); + //g_hash_table_print_stats (acfg->patch_to_plt_entry); + + acfg->plt_offset ++; + } + + return res; +} + +/** + * get_got_offset: + * + * Returns the offset of the GOT slot where the runtime object resulting from resolving + * JI could be found if it exists, otherwise allocates a new one. + */ +static guint32 +get_got_offset (MonoAotCompile *acfg, gboolean llvm, MonoJumpInfo *ji) +{ + guint32 got_offset; + GotInfo *info = llvm ? &acfg->llvm_got_info : &acfg->got_info; + + got_offset = GPOINTER_TO_UINT (g_hash_table_lookup (info->patch_to_got_offset_by_type [ji->type], ji)); + if (got_offset) + return got_offset - 1; + + if (llvm) { + got_offset = acfg->llvm_got_offset; + acfg->llvm_got_offset ++; + } else { + got_offset = acfg->got_offset; + acfg->got_offset ++; + } + + acfg->stats.got_slots ++; + acfg->stats.got_slot_types [ji->type] ++; + + g_hash_table_insert (info->patch_to_got_offset, ji, GUINT_TO_POINTER (got_offset + 1)); + g_hash_table_insert (info->patch_to_got_offset_by_type [ji->type], ji, GUINT_TO_POINTER (got_offset + 1)); + g_ptr_array_add (info->got_patches, ji); + + return got_offset; +} + +/* Add a method to the list of methods which need to be emitted */ +static void +add_method_with_index (MonoAotCompile *acfg, MonoMethod *method, int index, gboolean extra) +{ + g_assert (method); + if (!g_hash_table_lookup (acfg->method_indexes, method)) { + g_ptr_array_add (acfg->methods, method); + g_hash_table_insert (acfg->method_indexes, method, GUINT_TO_POINTER (index + 1)); + acfg->nmethods = acfg->methods->len + 1; + } + + if (method->wrapper_type || extra) + g_ptr_array_add (acfg->extra_methods, method); +} + +static gboolean +prefer_gsharedvt_method (MonoAotCompile *acfg, MonoMethod *method) +{ + /* One instantiation with valuetypes is generated for each async method */ + if (method->klass->image == mono_defaults.corlib && (!strcmp (method->klass->name, "AsyncMethodBuilderCore") || !strcmp (method->klass->name, "AsyncVoidMethodBuilder"))) + return TRUE; + else + return FALSE; +} + +static guint32 +get_method_index (MonoAotCompile *acfg, MonoMethod *method) +{ + int index = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->method_indexes, method)); + + g_assert (index); + + return index - 1; +} + +static int +add_method_full (MonoAotCompile *acfg, MonoMethod *method, gboolean extra, int depth) +{ + int index; + + index = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->method_indexes, method)); + if (index) + return index - 1; + + index = acfg->method_index; + add_method_with_index (acfg, method, index, extra); + + g_ptr_array_add (acfg->method_order, GUINT_TO_POINTER (index)); + + g_hash_table_insert (acfg->method_depth, method, GUINT_TO_POINTER (depth)); + + acfg->method_index ++; + + return index; +} + +static int +add_method (MonoAotCompile *acfg, MonoMethod *method) +{ + return add_method_full (acfg, method, FALSE, 0); +} + +static void +mono_dedup_cache_method (MonoAotCompile *acfg, MonoMethod *method) +{ + g_assert (acfg->dedup_stats); + + char *name = mono_aot_get_mangled_method_name (method); + g_assert (name); + + // For stats + char *stats_name = g_strdup (name); + + g_assert (acfg->dedup_cache); + + if (!g_hash_table_lookup (acfg->dedup_cache, name)) { + // This AOTCompile owns this method + // We do this to decide whether to write it to disk + // during a dedup run (first phase, where we skip). + // + // If never changed, then maybe can avoid a recompile + // of the cache. + // + // Files not read in during last phase. + acfg->dedup_cache_changed = TRUE; + + // owns name + g_hash_table_insert (acfg->dedup_cache, name, method); + } else { + // owns name + g_free (name); + } + + guint count = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->dedup_stats, stats_name)); + count++; + g_hash_table_insert (acfg->dedup_stats, stats_name, GUINT_TO_POINTER (count)); +} + +static void +add_extra_method_with_depth (MonoAotCompile *acfg, MonoMethod *method, int depth) +{ + if (mono_method_is_generic_sharable_full (method, TRUE, TRUE, FALSE)) + method = mini_get_shared_method (method); + else if ((acfg->opts & MONO_OPT_GSHAREDVT) && prefer_gsharedvt_method (acfg, method) && mono_method_is_generic_sharable_full (method, FALSE, FALSE, TRUE)) + /* Use the gsharedvt version */ + method = mini_get_shared_method_full (method, TRUE, TRUE); + + if ((acfg->aot_opts.dedup || acfg->aot_opts.dedup_include) && mono_aot_can_dedup (method)) { + mono_dedup_cache_method (acfg, method); + + if (!acfg->dedup_emit_mode) + return; + } + + if (acfg->aot_opts.log_generics) + aot_printf (acfg, "%*sAdding method %s.\n", depth, "", mono_method_get_full_name (method)); + + add_method_full (acfg, method, TRUE, depth); +} + +static void +add_extra_method (MonoAotCompile *acfg, MonoMethod *method) +{ + add_extra_method_with_depth (acfg, method, 0); +} + +static void +add_jit_icall_wrapper (gpointer key, gpointer value, gpointer user_data) +{ + MonoAotCompile *acfg = (MonoAotCompile *)user_data; + MonoJitICallInfo *callinfo = (MonoJitICallInfo *)value; + MonoMethod *wrapper; + char *name; + + if (!callinfo->sig) + return; + + name = g_strdup_printf ("__icall_wrapper_%s", callinfo->name); + wrapper = mono_marshal_get_icall_wrapper (callinfo->sig, name, callinfo->func, TRUE); + g_free (name); + + add_method (acfg, wrapper); +} + +static MonoMethod* +get_runtime_invoke_sig (MonoMethodSignature *sig) +{ + MonoMethodBuilder *mb; + MonoMethod *m; + + mb = mono_mb_new (mono_defaults.object_class, "FOO", MONO_WRAPPER_NONE); + m = mono_mb_create_method (mb, sig, 16); + MonoMethod *invoke = mono_marshal_get_runtime_invoke (m, FALSE); + mono_mb_free (mb); + return invoke; +} + +static MonoMethod* +get_runtime_invoke (MonoAotCompile *acfg, MonoMethod *method, gboolean virtual_) +{ + return mono_marshal_get_runtime_invoke (method, virtual_); +} + +static gboolean +can_marshal_struct (MonoClass *klass) +{ + MonoClassField *field; + gboolean can_marshal = TRUE; + gpointer iter = NULL; + MonoMarshalType *info; + int i; + + if (mono_class_is_auto_layout (klass)) + return FALSE; + + info = mono_marshal_load_type_info (klass); + + /* Only allow a few field types to avoid asserts in the marshalling code */ + while ((field = mono_class_get_fields (klass, &iter))) { + if ((field->type->attrs & FIELD_ATTRIBUTE_STATIC)) + continue; + + switch (field->type->type) { + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_CHAR: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_PTR: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + case MONO_TYPE_STRING: + break; + case MONO_TYPE_VALUETYPE: + if (!mono_class_from_mono_type (field->type)->enumtype && !can_marshal_struct (mono_class_from_mono_type (field->type))) + can_marshal = FALSE; + break; + case MONO_TYPE_SZARRAY: { + gboolean has_mspec = FALSE; + + if (info) { + for (i = 0; i < info->num_fields; ++i) { + if (info->fields [i].field == field && info->fields [i].mspec) + has_mspec = TRUE; + } + } + if (!has_mspec) + can_marshal = FALSE; + break; + } + default: + can_marshal = FALSE; + break; + } + } + + /* Special cases */ + /* Its hard to compute whenever these can be marshalled or not */ + if (!strcmp (klass->name_space, "System.Net.NetworkInformation.MacOsStructs") && strcmp (klass->name, "sockaddr_dl")) + return TRUE; + + return can_marshal; +} + +static void +create_gsharedvt_inst (MonoAotCompile *acfg, MonoMethod *method, MonoGenericContext *ctx) +{ + /* Create a vtype instantiation */ + MonoGenericContext shared_context; + MonoType **args; + MonoGenericInst *inst; + MonoGenericContainer *container; + MonoClass **constraints; + int i; + + memset (ctx, 0, sizeof (MonoGenericContext)); + + if (mono_class_is_gtd (method->klass)) { + shared_context = mono_class_get_generic_container (method->klass)->context; + inst = shared_context.class_inst; + + args = g_new0 (MonoType*, inst->type_argc); + for (i = 0; i < inst->type_argc; ++i) { + args [i] = &mono_defaults.int_class->byval_arg; + } + ctx->class_inst = mono_metadata_get_generic_inst (inst->type_argc, args); + } + if (method->is_generic) { + container = mono_method_get_generic_container (method); + shared_context = container->context; + inst = shared_context.method_inst; + + args = g_new0 (MonoType*, inst->type_argc); + for (i = 0; i < container->type_argc; ++i) { + MonoGenericParamInfo *info = &container->type_params [i].info; + gboolean ref_only = FALSE; + + if (info && info->constraints) { + constraints = info->constraints; + + while (*constraints) { + MonoClass *cklass = *constraints; + if (!(cklass == mono_defaults.object_class || (cklass->image == mono_defaults.corlib && !strcmp (cklass->name, "ValueType")))) + /* Inflaring the method with our vtype would not be valid */ + ref_only = TRUE; + constraints ++; + } + } + + if (ref_only) + args [i] = &mono_defaults.object_class->byval_arg; + else + args [i] = &mono_defaults.int_class->byval_arg; + } + ctx->method_inst = mono_metadata_get_generic_inst (inst->type_argc, args); + } +} + +static void +add_wrappers (MonoAotCompile *acfg) +{ + MonoMethod *method, *m; + int i, j; + MonoMethodSignature *sig, *csig; + guint32 token; + + /* + * FIXME: Instead of AOTing all the wrappers, it might be better to redesign them + * so there is only one wrapper of a given type, or inlining their contents into their + * callers. + */ + for (i = 0; i < acfg->image->tables [MONO_TABLE_METHOD].rows; ++i) { + MonoError error; + MonoMethod *method; + guint32 token = MONO_TOKEN_METHOD_DEF | (i + 1); + gboolean skip = FALSE; + + method = mono_get_method_checked (acfg->image, token, NULL, NULL, &error); + report_loader_error (acfg, &error, TRUE, "Failed to load method token 0x%x due to %s\n", i, mono_error_get_message (&error)); + + if ((method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) || + (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) || + (method->flags & METHOD_ATTRIBUTE_ABSTRACT)) + skip = TRUE; + + /* Skip methods which can not be handled by get_runtime_invoke () */ + sig = mono_method_signature (method); + if (!sig) + continue; + if ((sig->ret->type == MONO_TYPE_PTR) || + (sig->ret->type == MONO_TYPE_TYPEDBYREF)) + skip = TRUE; + if (mono_class_is_open_constructed_type (sig->ret)) + skip = TRUE; + + for (j = 0; j < sig->param_count; j++) { + if (sig->params [j]->type == MONO_TYPE_TYPEDBYREF) + skip = TRUE; + if (mono_class_is_open_constructed_type (sig->params [j])) + skip = TRUE; + } + +#ifdef MONO_ARCH_DYN_CALL_SUPPORTED + if (!mono_class_is_contextbound (method->klass)) { + MonoDynCallInfo *info = mono_arch_dyn_call_prepare (sig); + gboolean has_nullable = FALSE; + + for (j = 0; j < sig->param_count; j++) { + if (sig->params [j]->type == MONO_TYPE_GENERICINST && mono_class_is_nullable (mono_class_from_mono_type (sig->params [j]))) + has_nullable = TRUE; + } + + if (info && !has_nullable && !acfg->aot_opts.llvm_only) { + /* Supported by the dynamic runtime-invoke wrapper */ + skip = TRUE; + } + if (info) + mono_arch_dyn_call_free (info); + } +#endif + + if (acfg->aot_opts.llvm_only) + /* Supported by the gsharedvt based runtime-invoke wrapper */ + skip = TRUE; + + if (!skip) { + //printf ("%s\n", mono_method_full_name (method, TRUE)); + add_method (acfg, get_runtime_invoke (acfg, method, FALSE)); + } + } + + if (strcmp (acfg->image->assembly->aname.name, "mscorlib") == 0) { + int nallocators; + + /* Runtime invoke wrappers */ + + /* void runtime-invoke () [.cctor] */ + csig = mono_metadata_signature_alloc (mono_defaults.corlib, 0); + csig->ret = &mono_defaults.void_class->byval_arg; + add_method (acfg, get_runtime_invoke_sig (csig)); + + /* void runtime-invoke () [Finalize] */ + csig = mono_metadata_signature_alloc (mono_defaults.corlib, 0); + csig->hasthis = 1; + csig->ret = &mono_defaults.void_class->byval_arg; + add_method (acfg, get_runtime_invoke_sig (csig)); + + /* void runtime-invoke (string) [exception ctor] */ + csig = mono_metadata_signature_alloc (mono_defaults.corlib, 1); + csig->hasthis = 1; + csig->ret = &mono_defaults.void_class->byval_arg; + csig->params [0] = &mono_defaults.string_class->byval_arg; + add_method (acfg, get_runtime_invoke_sig (csig)); + + /* void runtime-invoke (string, string) [exception ctor] */ + csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2); + csig->hasthis = 1; + csig->ret = &mono_defaults.void_class->byval_arg; + csig->params [0] = &mono_defaults.string_class->byval_arg; + csig->params [1] = &mono_defaults.string_class->byval_arg; + add_method (acfg, get_runtime_invoke_sig (csig)); + + /* string runtime-invoke () [Exception.ToString ()] */ + csig = mono_metadata_signature_alloc (mono_defaults.corlib, 0); + csig->hasthis = 1; + csig->ret = &mono_defaults.string_class->byval_arg; + add_method (acfg, get_runtime_invoke_sig (csig)); + + /* void runtime-invoke (string, Exception) [exception ctor] */ + csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2); + csig->hasthis = 1; + csig->ret = &mono_defaults.void_class->byval_arg; + csig->params [0] = &mono_defaults.string_class->byval_arg; + csig->params [1] = &mono_defaults.exception_class->byval_arg; + add_method (acfg, get_runtime_invoke_sig (csig)); + + /* Assembly runtime-invoke (string, Assembly, bool) [DoAssemblyResolve] */ + csig = mono_metadata_signature_alloc (mono_defaults.corlib, 3); + csig->hasthis = 1; + csig->ret = &(mono_class_load_from_name ( + mono_defaults.corlib, "System.Reflection", "Assembly"))->byval_arg; + csig->params [0] = &mono_defaults.string_class->byval_arg; + csig->params [1] = &(mono_class_load_from_name (mono_defaults.corlib, "System.Reflection", "Assembly"))->byval_arg; + csig->params [2] = &mono_defaults.boolean_class->byval_arg; + add_method (acfg, get_runtime_invoke_sig (csig)); + + /* runtime-invoke used by finalizers */ + add_method (acfg, get_runtime_invoke (acfg, mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE)); + + /* This is used by mono_runtime_capture_context () */ + method = mono_get_context_capture_method (); + if (method) + add_method (acfg, get_runtime_invoke (acfg, method, FALSE)); + +#ifdef MONO_ARCH_DYN_CALL_SUPPORTED + if (!acfg->aot_opts.llvm_only) + add_method (acfg, mono_marshal_get_runtime_invoke_dynamic ()); +#endif + + /* These are used by mono_jit_runtime_invoke () to calls gsharedvt out wrappers */ + if (acfg->aot_opts.llvm_only) { + int variants; + + /* Create simplified signatures which match the signature used by the gsharedvt out wrappers */ + for (variants = 0; variants < 4; ++variants) { + for (i = 0; i < 16; ++i) { + sig = mini_get_gsharedvt_out_sig_wrapper_signature ((variants & 1) > 0, (variants & 2) > 0, i); + add_extra_method (acfg, mono_marshal_get_runtime_invoke_for_sig (sig)); + + g_free (sig); + } + } + } + + /* stelemref */ + add_method (acfg, mono_marshal_get_stelemref ()); + + /* Managed Allocators */ + nallocators = mono_gc_get_managed_allocator_types (); + for (i = 0; i < nallocators; ++i) { + if ((m = mono_gc_get_managed_allocator_by_type (i, MANAGED_ALLOCATOR_REGULAR))) + add_method (acfg, m); + if ((m = mono_gc_get_managed_allocator_by_type (i, MANAGED_ALLOCATOR_SLOW_PATH))) + add_method (acfg, m); + if ((m = mono_gc_get_managed_allocator_by_type (i, MANAGED_ALLOCATOR_PROFILER))) + add_method (acfg, m); + } + + /* write barriers */ + 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)); + } + + /* Stelemref wrappers */ + { + MonoMethod **wrappers; + int nwrappers; + + wrappers = mono_marshal_get_virtual_stelemref_wrappers (&nwrappers); + for (i = 0; i < nwrappers; ++i) + add_method (acfg, wrappers [i]); + g_free (wrappers); + } + + /* castclass_with_check wrapper */ + add_method (acfg, mono_marshal_get_castclass_with_cache ()); + /* isinst_with_check wrapper */ + add_method (acfg, mono_marshal_get_isinst_with_cache ()); + + /* JIT icall wrappers */ + /* FIXME: locking - this is "safe" as full-AOT threads don't mutate the icall hash*/ + g_hash_table_foreach (mono_get_jit_icall_info (), add_jit_icall_wrapper, acfg); + } + + /* + * remoting-invoke-with-check wrappers are very frequent, so avoid emitting them, + * we use the original method instead at runtime. + * Since full-aot doesn't support remoting, this is not a problem. + */ +#if 0 + /* remoting-invoke wrappers */ + for (i = 0; i < acfg->image->tables [MONO_TABLE_METHOD].rows; ++i) { + MonoError error; + MonoMethodSignature *sig; + + token = MONO_TOKEN_METHOD_DEF | (i + 1); + method = mono_get_method_checked (acfg->image, token, NULL, NULL, &error); + g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */ + + sig = mono_method_signature (method); + + if (sig->hasthis && (method->klass->marshalbyref || method->klass == mono_defaults.object_class)) { + m = mono_marshal_get_remoting_invoke_with_check (method); + + add_method (acfg, m); + } + } +#endif + + /* delegate-invoke wrappers */ + for (i = 0; i < acfg->image->tables [MONO_TABLE_TYPEDEF].rows; ++i) { + MonoError error; + MonoClass *klass; + MonoCustomAttrInfo *cattr; + + token = MONO_TOKEN_TYPE_DEF | (i + 1); + klass = mono_class_get_checked (acfg->image, token, &error); + + if (!klass) { + mono_error_cleanup (&error); + continue; + } + + if (!klass->delegate || klass == mono_defaults.delegate_class || klass == mono_defaults.multicastdelegate_class) + continue; + + if (!mono_class_is_gtd (klass)) { + method = mono_get_delegate_invoke (klass); + + m = mono_marshal_get_delegate_invoke (method, NULL); + + add_method (acfg, m); + + method = mono_class_get_method_from_name_flags (klass, "BeginInvoke", -1, 0); + if (method) + add_method (acfg, mono_marshal_get_delegate_begin_invoke (method)); + + method = mono_class_get_method_from_name_flags (klass, "EndInvoke", -1, 0); + if (method) + add_method (acfg, mono_marshal_get_delegate_end_invoke (method)); + + cattr = mono_custom_attrs_from_class_checked (klass, &error); + if (!is_ok (&error)) { + mono_error_cleanup (&error); + continue; + } + + if (cattr) { + int j; + + for (j = 0; j < cattr->num_attrs; ++j) + if (cattr->attrs [j].ctor && (!strcmp (cattr->attrs [j].ctor->klass->name, "MonoNativeFunctionWrapperAttribute") || !strcmp (cattr->attrs [j].ctor->klass->name, "UnmanagedFunctionPointerAttribute"))) + break; + if (j < cattr->num_attrs) { + MonoMethod *invoke; + MonoMethod *wrapper; + MonoMethod *del_invoke; + + /* Add wrappers needed by mono_ftnptr_to_delegate () */ + invoke = mono_get_delegate_invoke (klass); + wrapper = mono_marshal_get_native_func_wrapper_aot (klass); + del_invoke = mono_marshal_get_delegate_invoke_internal (invoke, FALSE, TRUE, wrapper); + add_method (acfg, wrapper); + add_method (acfg, del_invoke); + } + } + } else if ((acfg->opts & MONO_OPT_GSHAREDVT) && mono_class_is_gtd (klass)) { + MonoError error; + MonoGenericContext ctx; + MonoMethod *inst, *gshared; + + /* + * Emit gsharedvt versions of the generic delegate-invoke wrappers + */ + /* Invoke */ + method = mono_get_delegate_invoke (klass); + create_gsharedvt_inst (acfg, method, &ctx); + + inst = mono_class_inflate_generic_method_checked (method, &ctx, &error); + g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */ + + m = mono_marshal_get_delegate_invoke (inst, NULL); + g_assert (m->is_inflated); + + gshared = mini_get_shared_method_full (m, FALSE, TRUE); + add_extra_method (acfg, gshared); + + /* begin-invoke */ + method = mono_get_delegate_begin_invoke (klass); + if (method) { + create_gsharedvt_inst (acfg, method, &ctx); + + inst = mono_class_inflate_generic_method_checked (method, &ctx, &error); + g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */ + + m = mono_marshal_get_delegate_begin_invoke (inst); + g_assert (m->is_inflated); + + gshared = mini_get_shared_method_full (m, FALSE, TRUE); + add_extra_method (acfg, gshared); + } + + /* end-invoke */ + method = mono_get_delegate_end_invoke (klass); + if (method) { + create_gsharedvt_inst (acfg, method, &ctx); + + inst = mono_class_inflate_generic_method_checked (method, &ctx, &error); + g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */ + + m = mono_marshal_get_delegate_end_invoke (inst); + g_assert (m->is_inflated); + + gshared = mini_get_shared_method_full (m, FALSE, TRUE); + add_extra_method (acfg, gshared); + } + } + } + + /* array access wrappers */ + for (i = 0; i < acfg->image->tables [MONO_TABLE_TYPESPEC].rows; ++i) { + MonoError error; + MonoClass *klass; + + token = MONO_TOKEN_TYPE_SPEC | (i + 1); + klass = mono_class_get_checked (acfg->image, token, &error); + + if (!klass) { + mono_error_cleanup (&error); + continue; + } + + if (klass->rank && MONO_TYPE_IS_PRIMITIVE (&klass->element_class->byval_arg)) { + MonoMethod *m, *wrapper; + + /* Add runtime-invoke wrappers too */ + + m = mono_class_get_method_from_name (klass, "Get", -1); + g_assert (m); + wrapper = mono_marshal_get_array_accessor_wrapper (m); + add_extra_method (acfg, wrapper); + if (!acfg->aot_opts.llvm_only) + add_extra_method (acfg, get_runtime_invoke (acfg, wrapper, FALSE)); + + m = mono_class_get_method_from_name (klass, "Set", -1); + g_assert (m); + wrapper = mono_marshal_get_array_accessor_wrapper (m); + add_extra_method (acfg, wrapper); + if (!acfg->aot_opts.llvm_only) + add_extra_method (acfg, get_runtime_invoke (acfg, wrapper, FALSE)); + } + } + + /* Synchronized wrappers */ + for (i = 0; i < acfg->image->tables [MONO_TABLE_METHOD].rows; ++i) { + MonoError error; + token = MONO_TOKEN_METHOD_DEF | (i + 1); + method = mono_get_method_checked (acfg->image, token, NULL, NULL, &error); + report_loader_error (acfg, &error, TRUE, "Failed to load method token 0x%x due to %s\n", i, mono_error_get_message (&error)); + + if (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) { + if (method->is_generic) { + // FIXME: + } else if ((acfg->opts & MONO_OPT_GSHAREDVT) && mono_class_is_gtd (method->klass)) { + MonoError error; + MonoGenericContext ctx; + MonoMethod *inst, *gshared, *m; + + /* + * Create a generic wrapper for a generic instance, and AOT that. + */ + create_gsharedvt_inst (acfg, method, &ctx); + inst = mono_class_inflate_generic_method_checked (method, &ctx, &error); + g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */ + m = mono_marshal_get_synchronized_wrapper (inst); + g_assert (m->is_inflated); + gshared = mini_get_shared_method_full (m, FALSE, TRUE); + add_method (acfg, gshared); + } else { + add_method (acfg, mono_marshal_get_synchronized_wrapper (method)); + } + } + } + + /* pinvoke wrappers */ + for (i = 0; i < acfg->image->tables [MONO_TABLE_METHOD].rows; ++i) { + MonoError error; + MonoMethod *method; + guint32 token = MONO_TOKEN_METHOD_DEF | (i + 1); + + method = mono_get_method_checked (acfg->image, token, NULL, NULL, &error); + report_loader_error (acfg, &error, TRUE, "Failed to load method token 0x%x due to %s\n", i, mono_error_get_message (&error)); + + if ((method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) || + (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)) { + add_method (acfg, mono_marshal_get_native_wrapper (method, TRUE, TRUE)); + } + + if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) { + if (acfg->aot_opts.llvm_only) { + /* The wrappers have a different signature (hasthis is not set) so need to add this too */ + add_gsharedvt_wrappers (acfg, mono_method_signature (method), FALSE, TRUE); + } + } + } + + /* native-to-managed wrappers */ + for (i = 0; i < acfg->image->tables [MONO_TABLE_METHOD].rows; ++i) { + MonoError error; + MonoMethod *method; + guint32 token = MONO_TOKEN_METHOD_DEF | (i + 1); + MonoCustomAttrInfo *cattr; + int j; + + method = mono_get_method_checked (acfg->image, token, NULL, NULL, &error); + report_loader_error (acfg, &error, TRUE, "Failed to load method token 0x%x due to %s\n", i, mono_error_get_message (&error)); + + /* + * Only generate native-to-managed wrappers for methods which have an + * attribute named MonoPInvokeCallbackAttribute. We search for the attribute by + * name to avoid defining a new assembly to contain it. + */ + cattr = mono_custom_attrs_from_method_checked (method, &error); + if (!is_ok (&error)) { + char *name = mono_method_get_full_name (method); + report_loader_error (acfg, &error, TRUE, "Failed to load custom attributes from method %s due to %s\n", name, mono_error_get_message (&error)); + g_free (name); + } + + if (cattr) { + for (j = 0; j < cattr->num_attrs; ++j) + if (cattr->attrs [j].ctor && !strcmp (cattr->attrs [j].ctor->klass->name, "MonoPInvokeCallbackAttribute")) + break; + if (j < cattr->num_attrs) { + MonoCustomAttrEntry *e = &cattr->attrs [j]; + MonoMethodSignature *sig = mono_method_signature (e->ctor); + const char *p = (const char*)e->data; + const char *named; + int slen, num_named, named_type; + char *n; + MonoType *t; + MonoClass *klass; + char *export_name = NULL; + MonoMethod *wrapper; + + /* this cannot be enforced by the C# compiler so we must give the user some warning before aborting */ + if (!(method->flags & METHOD_ATTRIBUTE_STATIC)) { + g_warning ("AOT restriction: Method '%s' must be static since it is decorated with [MonoPInvokeCallback]. See http://ios.xamarin.com/Documentation/Limitations#Reverse_Callbacks", + mono_method_full_name (method, TRUE)); + exit (1); + } + + g_assert (sig->param_count == 1); + g_assert (sig->params [0]->type == MONO_TYPE_CLASS && !strcmp (mono_class_from_mono_type (sig->params [0])->name, "Type")); + + /* + * Decode the cattr manually since we can't create objects + * during aot compilation. + */ + + /* Skip prolog */ + p += 2; + + /* From load_cattr_value () in reflection.c */ + slen = mono_metadata_decode_value (p, &p); + n = (char *)g_memdup (p, slen + 1); + n [slen] = 0; + t = mono_reflection_type_from_name_checked (n, acfg->image, &error); + g_assert (t); + mono_error_assert_ok (&error); + g_free (n); + + klass = mono_class_from_mono_type (t); + g_assert (klass->parent == mono_defaults.multicastdelegate_class); + + p += slen; + + num_named = read16 (p); + p += 2; + + g_assert (num_named < 2); + if (num_named == 1) { + int name_len; + char *name; + + /* parse ExportSymbol attribute */ + named = p; + named_type = *named; + named += 1; + /* data_type = *named; */ + named += 1; + + name_len = mono_metadata_decode_blob_size (named, &named); + name = (char *)g_malloc (name_len + 1); + memcpy (name, named, name_len); + name [name_len] = 0; + named += name_len; + + g_assert (named_type == 0x54); + g_assert (!strcmp (name, "ExportSymbol")); + + /* load_cattr_value (), string case */ + g_assert (*named != (char)0xff); + slen = mono_metadata_decode_value (named, &named); + export_name = (char *)g_malloc (slen + 1); + memcpy (export_name, named, slen); + export_name [slen] = 0; + named += slen; + } + + wrapper = mono_marshal_get_managed_wrapper (method, klass, 0, &error); + mono_error_assert_ok (&error); + + add_method (acfg, wrapper); + if (export_name) + g_hash_table_insert (acfg->export_names, wrapper, export_name); + } + g_free (cattr); + } + + if ((method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) || + (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)) { + add_method (acfg, mono_marshal_get_native_wrapper (method, TRUE, TRUE)); + } + } + + /* StructureToPtr/PtrToStructure wrappers */ + for (i = 0; i < acfg->image->tables [MONO_TABLE_TYPEDEF].rows; ++i) { + MonoError error; + MonoClass *klass; + + token = MONO_TOKEN_TYPE_DEF | (i + 1); + klass = mono_class_get_checked (acfg->image, token, &error); + + if (!klass) { + mono_error_cleanup (&error); + continue; + } + + if (klass->valuetype && !mono_class_is_gtd (klass) && can_marshal_struct (klass) && + !(klass->nested_in && strstr (klass->nested_in->name, "") == klass->nested_in->name)) { + add_method (acfg, mono_marshal_get_struct_to_ptr (klass)); + add_method (acfg, mono_marshal_get_ptr_to_struct (klass)); + } + } +} + +static gboolean +has_type_vars (MonoClass *klass) +{ + if ((klass->byval_arg.type == MONO_TYPE_VAR) || (klass->byval_arg.type == MONO_TYPE_MVAR)) + return TRUE; + if (klass->rank) + return has_type_vars (klass->element_class); + if (mono_class_is_ginst (klass)) { + MonoGenericContext *context = &mono_class_get_generic_class (klass)->context; + if (context->class_inst) { + int i; + + for (i = 0; i < context->class_inst->type_argc; ++i) + if (has_type_vars (mono_class_from_mono_type (context->class_inst->type_argv [i]))) + return TRUE; + } + } + if (mono_class_is_gtd (klass)) + return TRUE; + return FALSE; +} + +static gboolean +is_vt_inst (MonoGenericInst *inst) +{ + int i; + + for (i = 0; i < inst->type_argc; ++i) { + MonoType *t = inst->type_argv [i]; + if (MONO_TYPE_ISSTRUCT (t) || t->type == MONO_TYPE_VALUETYPE) + return TRUE; + } + return FALSE; +} + +static gboolean +method_has_type_vars (MonoMethod *method) +{ + if (has_type_vars (method->klass)) + return TRUE; + + if (method->is_inflated) { + MonoGenericContext *context = mono_method_get_context (method); + if (context->method_inst) { + int i; + + for (i = 0; i < context->method_inst->type_argc; ++i) + if (has_type_vars (mono_class_from_mono_type (context->method_inst->type_argv [i]))) + return TRUE; + } + } + return FALSE; +} + +static +gboolean mono_aot_mode_is_full (MonoAotOptions *opts) +{ + return opts->mode == MONO_AOT_MODE_FULL || opts->mode == MONO_AOT_MODE_INTERP; +} + +static +gboolean mono_aot_mode_is_interp (MonoAotOptions *opts) +{ + return opts->mode == MONO_AOT_MODE_INTERP; +} + +static +gboolean mono_aot_mode_is_hybrid (MonoAotOptions *opts) +{ + return opts->mode == MONO_AOT_MODE_HYBRID; +} + +static void add_generic_class_with_depth (MonoAotCompile *acfg, MonoClass *klass, int depth, const char *ref); + +static void +add_generic_class (MonoAotCompile *acfg, MonoClass *klass, gboolean force, const char *ref) +{ + /* This might lead to a huge code blowup so only do it if neccesary */ + if (!mono_aot_mode_is_full (&acfg->aot_opts) && !mono_aot_mode_is_hybrid (&acfg->aot_opts) && !force) + return; + + add_generic_class_with_depth (acfg, klass, 0, ref); +} + +static gboolean +check_type_depth (MonoType *t, int depth) +{ + int i; + + if (depth > 8) + return TRUE; + + switch (t->type) { + case MONO_TYPE_GENERICINST: { + MonoGenericClass *gklass = t->data.generic_class; + MonoGenericInst *ginst = gklass->context.class_inst; + + if (ginst) { + for (i = 0; i < ginst->type_argc; ++i) { + if (check_type_depth (ginst->type_argv [i], depth + 1)) + return TRUE; + } + } + break; + } + default: + break; + } + + return FALSE; +} + +static void +add_types_from_method_header (MonoAotCompile *acfg, MonoMethod *method); + +/* + * add_generic_class: + * + * Add all methods of a generic class. + */ +static void +add_generic_class_with_depth (MonoAotCompile *acfg, MonoClass *klass, int depth, const char *ref) +{ + MonoMethod *method; + MonoClassField *field; + gpointer iter; + gboolean use_gsharedvt = FALSE; + + if (!acfg->ginst_hash) + acfg->ginst_hash = g_hash_table_new (NULL, NULL); + + mono_class_init (klass); + + if (mono_class_is_ginst (klass) && mono_class_get_generic_class (klass)->context.class_inst->is_open) + return; + + if (has_type_vars (klass)) + return; + + if (!mono_class_is_ginst (klass) && !klass->rank) + return; + + if (mono_class_has_failure (klass)) + return; + + if (!acfg->ginst_hash) + acfg->ginst_hash = g_hash_table_new (NULL, NULL); + + if (g_hash_table_lookup (acfg->ginst_hash, klass)) + return; + + if (check_type_depth (&klass->byval_arg, 0)) + return; + + if (acfg->aot_opts.log_generics) + aot_printf (acfg, "%*sAdding generic instance %s [%s].\n", depth, "", mono_type_full_name (&klass->byval_arg), ref); + + g_hash_table_insert (acfg->ginst_hash, klass, klass); + + /* + * Use gsharedvt for generic collections with vtype arguments to avoid code blowup. + * Enable this only for some classes since gsharedvt might not support all methods. + */ + if ((acfg->opts & MONO_OPT_GSHAREDVT) && klass->image == mono_defaults.corlib && mono_class_is_ginst (klass) && mono_class_get_generic_class (klass)->context.class_inst && is_vt_inst (mono_class_get_generic_class (klass)->context.class_inst) && + (!strcmp (klass->name, "Dictionary`2") || !strcmp (klass->name, "List`1") || !strcmp (klass->name, "ReadOnlyCollection`1"))) + use_gsharedvt = TRUE; + + iter = NULL; + while ((method = mono_class_get_methods (klass, &iter))) { + if ((acfg->opts & MONO_OPT_GSHAREDVT) && method->is_inflated && mono_method_get_context (method)->method_inst) { + /* + * This is partial sharing, and we can't handle it yet + */ + continue; + } + + if (mono_method_is_generic_sharable_full (method, FALSE, FALSE, use_gsharedvt)) { + /* Already added */ + add_types_from_method_header (acfg, method); + continue; + } + + if (method->is_generic) + /* FIXME: */ + continue; + + /* + * FIXME: Instances which are referenced by these methods are not added, + * for example Array.Resize for List.Add (). + */ + add_extra_method_with_depth (acfg, method, depth + 1); + } + + iter = NULL; + while ((field = mono_class_get_fields (klass, &iter))) { + if (field->type->type == MONO_TYPE_GENERICINST) + add_generic_class_with_depth (acfg, mono_class_from_mono_type (field->type), depth + 1, "field"); + } + + if (klass->delegate) { + method = mono_get_delegate_invoke (klass); + + method = mono_marshal_get_delegate_invoke (method, NULL); + + if (acfg->aot_opts.log_generics) + aot_printf (acfg, "%*sAdding method %s.\n", depth, "", mono_method_get_full_name (method)); + + add_method (acfg, method); + } + + /* Add superclasses */ + if (klass->parent) + add_generic_class_with_depth (acfg, klass->parent, depth, "parent"); + + /* + * For ICollection, add instances of the helper methods + * in Array, since a T[] could be cast to ICollection. + */ + if (klass->image == mono_defaults.corlib && !strcmp (klass->name_space, "System.Collections.Generic") && + (!strcmp(klass->name, "ICollection`1") || !strcmp (klass->name, "IEnumerable`1") || !strcmp (klass->name, "IList`1") || !strcmp (klass->name, "IEnumerator`1") || !strcmp (klass->name, "IReadOnlyList`1"))) { + MonoClass *tclass = mono_class_from_mono_type (mono_class_get_generic_class (klass)->context.class_inst->type_argv [0]); + MonoClass *array_class = mono_bounded_array_class_get (tclass, 1, FALSE); + gpointer iter; + char *name_prefix; + + if (!strcmp (klass->name, "IEnumerator`1")) + name_prefix = g_strdup_printf ("%s.%s", klass->name_space, "IEnumerable`1"); + else + name_prefix = g_strdup_printf ("%s.%s", klass->name_space, klass->name); + + /* Add the T[]/InternalEnumerator class */ + if (!strcmp (klass->name, "IEnumerable`1") || !strcmp (klass->name, "IEnumerator`1")) { + MonoError error; + MonoClass *nclass; + + iter = NULL; + while ((nclass = mono_class_get_nested_types (array_class->parent, &iter))) { + if (!strcmp (nclass->name, "InternalEnumerator`1")) + break; + } + g_assert (nclass); + nclass = mono_class_inflate_generic_class_checked (nclass, mono_generic_class_get_context (mono_class_get_generic_class (klass)), &error); + mono_error_assert_ok (&error); /* FIXME don't swallow the error */ + add_generic_class (acfg, nclass, FALSE, "ICollection"); + } + + iter = NULL; + while ((method = mono_class_get_methods (array_class, &iter))) { + if (strstr (method->name, name_prefix)) { + MonoMethod *m = mono_aot_get_array_helper_from_wrapper (method); + + add_extra_method_with_depth (acfg, m, depth); + } + } + + g_free (name_prefix); + } + + /* Add an instance of GenericComparer which is created dynamically by Comparer */ + if (klass->image == mono_defaults.corlib && !strcmp (klass->name_space, "System.Collections.Generic") && !strcmp (klass->name, "Comparer`1")) { + MonoError error; + MonoClass *tclass = mono_class_from_mono_type (mono_class_get_generic_class (klass)->context.class_inst->type_argv [0]); + MonoClass *icomparable, *gcomparer, *icomparable_inst; + MonoGenericContext ctx; + MonoType *args [16]; + + memset (&ctx, 0, sizeof (ctx)); + + icomparable = mono_class_load_from_name (mono_defaults.corlib, "System", "IComparable`1"); + + args [0] = &tclass->byval_arg; + ctx.class_inst = mono_metadata_get_generic_inst (1, args); + + icomparable_inst = mono_class_inflate_generic_class_checked (icomparable, &ctx, &error); + mono_error_assert_ok (&error); /* FIXME don't swallow the error */ + + if (mono_class_is_assignable_from (icomparable_inst, tclass)) { + MonoClass *gcomparer_inst; + gcomparer = mono_class_load_from_name (mono_defaults.corlib, "System.Collections.Generic", "GenericComparer`1"); + gcomparer_inst = mono_class_inflate_generic_class_checked (gcomparer, &ctx, &error); + mono_error_assert_ok (&error); /* FIXME don't swallow the error */ + + add_generic_class (acfg, gcomparer_inst, FALSE, "Comparer"); + } + } + + /* Add an instance of GenericEqualityComparer which is created dynamically by EqualityComparer */ + if (klass->image == mono_defaults.corlib && !strcmp (klass->name_space, "System.Collections.Generic") && !strcmp (klass->name, "EqualityComparer`1")) { + MonoError error; + MonoClass *tclass = mono_class_from_mono_type (mono_class_get_generic_class (klass)->context.class_inst->type_argv [0]); + MonoClass *iface, *gcomparer, *iface_inst; + MonoGenericContext ctx; + MonoType *args [16]; + + memset (&ctx, 0, sizeof (ctx)); + + iface = mono_class_load_from_name (mono_defaults.corlib, "System", "IEquatable`1"); + g_assert (iface); + args [0] = &tclass->byval_arg; + ctx.class_inst = mono_metadata_get_generic_inst (1, args); + + iface_inst = mono_class_inflate_generic_class_checked (iface, &ctx, &error); + mono_error_assert_ok (&error); /* FIXME don't swallow the error */ + + if (mono_class_is_assignable_from (iface_inst, tclass)) { + MonoClass *gcomparer_inst; + MonoError error; + + gcomparer = mono_class_load_from_name (mono_defaults.corlib, "System.Collections.Generic", "GenericEqualityComparer`1"); + gcomparer_inst = mono_class_inflate_generic_class_checked (gcomparer, &ctx, &error); + mono_error_assert_ok (&error); /* FIXME don't swallow the error */ + add_generic_class (acfg, gcomparer_inst, FALSE, "EqualityComparer"); + } + } + + /* Add an instance of EnumComparer which is created dynamically by EqualityComparer for enums */ + if (klass->image == mono_defaults.corlib && !strcmp (klass->name_space, "System.Collections.Generic") && !strcmp (klass->name, "EqualityComparer`1")) { + MonoClass *enum_comparer; + MonoClass *tclass = mono_class_from_mono_type (mono_class_get_generic_class (klass)->context.class_inst->type_argv [0]); + MonoGenericContext ctx; + MonoType *args [16]; + + if (mono_class_is_enum (tclass)) { + MonoClass *enum_comparer_inst; + MonoError error; + + memset (&ctx, 0, sizeof (ctx)); + args [0] = &tclass->byval_arg; + ctx.class_inst = mono_metadata_get_generic_inst (1, args); + + enum_comparer = mono_class_load_from_name (mono_defaults.corlib, "System.Collections.Generic", "EnumEqualityComparer`1"); + enum_comparer_inst = mono_class_inflate_generic_class_checked (enum_comparer, &ctx, &error); + mono_error_assert_ok (&error); /* FIXME don't swallow the error */ + add_generic_class (acfg, enum_comparer_inst, FALSE, "EqualityComparer"); + } + } + + /* Add an instance of ObjectComparer which is created dynamically by Comparer for enums */ + if (klass->image == mono_defaults.corlib && !strcmp (klass->name_space, "System.Collections.Generic") && !strcmp (klass->name, "Comparer`1")) { + MonoClass *comparer; + MonoClass *tclass = mono_class_from_mono_type (mono_class_get_generic_class (klass)->context.class_inst->type_argv [0]); + MonoGenericContext ctx; + MonoType *args [16]; + + if (mono_class_is_enum (tclass)) { + MonoClass *comparer_inst; + MonoError error; + + memset (&ctx, 0, sizeof (ctx)); + args [0] = &tclass->byval_arg; + ctx.class_inst = mono_metadata_get_generic_inst (1, args); + + comparer = mono_class_load_from_name (mono_defaults.corlib, "System.Collections.Generic", "ObjectComparer`1"); + comparer_inst = mono_class_inflate_generic_class_checked (comparer, &ctx, &error); + mono_error_assert_ok (&error); /* FIXME don't swallow the error */ + add_generic_class (acfg, comparer_inst, FALSE, "Comparer"); + } + } +} + +static void +add_instances_of (MonoAotCompile *acfg, MonoClass *klass, MonoType **insts, int ninsts, gboolean force) +{ + int i; + MonoGenericContext ctx; + MonoType *args [16]; + + if (acfg->aot_opts.no_instances) + return; + + memset (&ctx, 0, sizeof (ctx)); + + for (i = 0; i < ninsts; ++i) { + MonoError error; + MonoClass *generic_inst; + args [0] = insts [i]; + ctx.class_inst = mono_metadata_get_generic_inst (1, args); + generic_inst = mono_class_inflate_generic_class_checked (klass, &ctx, &error); + mono_error_assert_ok (&error); /* FIXME don't swallow the error */ + add_generic_class (acfg, generic_inst, force, ""); + } +} + +static void +add_types_from_method_header (MonoAotCompile *acfg, MonoMethod *method) +{ + MonoError error; + MonoMethodHeader *header; + MonoMethodSignature *sig; + int j, depth; + + depth = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->method_depth, method)); + + sig = mono_method_signature (method); + + if (sig) { + for (j = 0; j < sig->param_count; ++j) + if (sig->params [j]->type == MONO_TYPE_GENERICINST) + add_generic_class_with_depth (acfg, mono_class_from_mono_type (sig->params [j]), depth + 1, "arg"); + } + + header = mono_method_get_header_checked (method, &error); + + if (header) { + for (j = 0; j < header->num_locals; ++j) + if (header->locals [j]->type == MONO_TYPE_GENERICINST) + add_generic_class_with_depth (acfg, mono_class_from_mono_type (header->locals [j]), depth + 1, "local"); + mono_metadata_free_mh (header); + } else { + mono_error_cleanup (&error); /* FIXME report the error */ + } + +} + +/* + * add_generic_instances: + * + * Add instances referenced by the METHODSPEC/TYPESPEC table. + */ +static void +add_generic_instances (MonoAotCompile *acfg) +{ + int i; + guint32 token; + MonoMethod *method; + MonoGenericContext *context; + + if (acfg->aot_opts.no_instances) + return; + + for (i = 0; i < acfg->image->tables [MONO_TABLE_METHODSPEC].rows; ++i) { + MonoError error; + token = MONO_TOKEN_METHOD_SPEC | (i + 1); + method = mono_get_method_checked (acfg->image, token, NULL, NULL, &error); + + if (!method) { + aot_printerrf (acfg, "Failed to load methodspec 0x%x due to %s.\n", token, mono_error_get_message (&error)); + aot_printerrf (acfg, "Run with MONO_LOG_LEVEL=debug for more information.\n"); + mono_error_cleanup (&error); + continue; + } + + if (method->klass->image != acfg->image) + continue; + + context = mono_method_get_context (method); + + if (context && ((context->class_inst && context->class_inst->is_open))) + continue; + + /* + * For open methods, create an instantiation which can be passed to the JIT. + * FIXME: Handle class_inst as well. + */ + if (context && context->method_inst && context->method_inst->is_open) { + MonoError error; + MonoGenericContext shared_context; + MonoGenericInst *inst; + MonoType **type_argv; + int i; + MonoMethod *declaring_method; + gboolean supported = TRUE; + + /* Check that the context doesn't contain open constructed types */ + if (context->class_inst) { + inst = context->class_inst; + for (i = 0; i < inst->type_argc; ++i) { + if (MONO_TYPE_IS_REFERENCE (inst->type_argv [i]) || inst->type_argv [i]->type == MONO_TYPE_VAR || inst->type_argv [i]->type == MONO_TYPE_MVAR) + continue; + if (mono_class_is_open_constructed_type (inst->type_argv [i])) + supported = FALSE; + } + } + if (context->method_inst) { + inst = context->method_inst; + for (i = 0; i < inst->type_argc; ++i) { + if (MONO_TYPE_IS_REFERENCE (inst->type_argv [i]) || inst->type_argv [i]->type == MONO_TYPE_VAR || inst->type_argv [i]->type == MONO_TYPE_MVAR) + continue; + if (mono_class_is_open_constructed_type (inst->type_argv [i])) + supported = FALSE; + } + } + + if (!supported) + continue; + + memset (&shared_context, 0, sizeof (MonoGenericContext)); + + inst = context->class_inst; + if (inst) { + type_argv = g_new0 (MonoType*, inst->type_argc); + for (i = 0; i < inst->type_argc; ++i) { + if (MONO_TYPE_IS_REFERENCE (inst->type_argv [i]) || inst->type_argv [i]->type == MONO_TYPE_VAR || inst->type_argv [i]->type == MONO_TYPE_MVAR) + type_argv [i] = &mono_defaults.object_class->byval_arg; + else + type_argv [i] = inst->type_argv [i]; + } + + shared_context.class_inst = mono_metadata_get_generic_inst (inst->type_argc, type_argv); + g_free (type_argv); + } + + inst = context->method_inst; + if (inst) { + type_argv = g_new0 (MonoType*, inst->type_argc); + for (i = 0; i < inst->type_argc; ++i) { + if (MONO_TYPE_IS_REFERENCE (inst->type_argv [i]) || inst->type_argv [i]->type == MONO_TYPE_VAR || inst->type_argv [i]->type == MONO_TYPE_MVAR) + type_argv [i] = &mono_defaults.object_class->byval_arg; + else + type_argv [i] = inst->type_argv [i]; + } + + shared_context.method_inst = mono_metadata_get_generic_inst (inst->type_argc, type_argv); + g_free (type_argv); + } + + if (method->is_generic || mono_class_is_gtd (method->klass)) + declaring_method = method; + else + declaring_method = mono_method_get_declaring_generic_method (method); + + method = mono_class_inflate_generic_method_checked (declaring_method, &shared_context, &error); + g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */ + } + + /* + * If the method is fully sharable, it was already added in place of its + * generic definition. + */ + if (mono_method_is_generic_sharable_full (method, FALSE, FALSE, FALSE)) + continue; + + /* + * FIXME: Partially shared methods are not shared here, so we end up with + * many identical methods. + */ + add_extra_method (acfg, method); + } + + for (i = 0; i < acfg->image->tables [MONO_TABLE_TYPESPEC].rows; ++i) { + MonoError error; + MonoClass *klass; + + token = MONO_TOKEN_TYPE_SPEC | (i + 1); + + klass = mono_class_get_checked (acfg->image, token, &error); + if (!klass || klass->rank) { + mono_error_cleanup (&error); + continue; + } + + add_generic_class (acfg, klass, FALSE, "typespec"); + } + + /* Add types of args/locals */ + for (i = 0; i < acfg->methods->len; ++i) { + method = (MonoMethod *)g_ptr_array_index (acfg->methods, i); + add_types_from_method_header (acfg, method); + } + + if (acfg->image == mono_defaults.corlib) { + MonoClass *klass; + MonoType *insts [256]; + int ninsts = 0; + + insts [ninsts ++] = &mono_defaults.byte_class->byval_arg; + insts [ninsts ++] = &mono_defaults.sbyte_class->byval_arg; + insts [ninsts ++] = &mono_defaults.int16_class->byval_arg; + insts [ninsts ++] = &mono_defaults.uint16_class->byval_arg; + insts [ninsts ++] = &mono_defaults.int32_class->byval_arg; + insts [ninsts ++] = &mono_defaults.uint32_class->byval_arg; + insts [ninsts ++] = &mono_defaults.int64_class->byval_arg; + insts [ninsts ++] = &mono_defaults.uint64_class->byval_arg; + insts [ninsts ++] = &mono_defaults.single_class->byval_arg; + insts [ninsts ++] = &mono_defaults.double_class->byval_arg; + insts [ninsts ++] = &mono_defaults.char_class->byval_arg; + insts [ninsts ++] = &mono_defaults.boolean_class->byval_arg; + + /* Add GenericComparer instances for primitive types for Enum.ToString () */ + klass = mono_class_try_load_from_name (acfg->image, "System.Collections.Generic", "GenericComparer`1"); + if (klass) + add_instances_of (acfg, klass, insts, ninsts, TRUE); + klass = mono_class_try_load_from_name (acfg->image, "System.Collections.Generic", "GenericEqualityComparer`1"); + if (klass) + add_instances_of (acfg, klass, insts, ninsts, TRUE); + + /* Add instances of EnumEqualityComparer which are created by EqualityComparer for enums */ + { + MonoClass *enum_comparer; + MonoType *insts [16]; + int ninsts; + + ninsts = 0; + insts [ninsts ++] = &mono_defaults.int32_class->byval_arg; + insts [ninsts ++] = &mono_defaults.uint32_class->byval_arg; + insts [ninsts ++] = &mono_defaults.uint16_class->byval_arg; + insts [ninsts ++] = &mono_defaults.byte_class->byval_arg; + enum_comparer = mono_class_load_from_name (mono_defaults.corlib, "System.Collections.Generic", "EnumEqualityComparer`1"); + add_instances_of (acfg, enum_comparer, insts, ninsts, FALSE); + + ninsts = 0; + insts [ninsts ++] = &mono_defaults.int16_class->byval_arg; + enum_comparer = mono_class_load_from_name (mono_defaults.corlib, "System.Collections.Generic", "ShortEnumEqualityComparer`1"); + add_instances_of (acfg, enum_comparer, insts, ninsts, FALSE); + + ninsts = 0; + insts [ninsts ++] = &mono_defaults.sbyte_class->byval_arg; + enum_comparer = mono_class_load_from_name (mono_defaults.corlib, "System.Collections.Generic", "SByteEnumEqualityComparer`1"); + add_instances_of (acfg, enum_comparer, insts, ninsts, FALSE); + + enum_comparer = mono_class_load_from_name (mono_defaults.corlib, "System.Collections.Generic", "LongEnumEqualityComparer`1"); + ninsts = 0; + insts [ninsts ++] = &mono_defaults.int64_class->byval_arg; + insts [ninsts ++] = &mono_defaults.uint64_class->byval_arg; + add_instances_of (acfg, enum_comparer, insts, ninsts, FALSE); + } + + /* Add instances of the array generic interfaces for primitive types */ + /* This will add instances of the InternalArray_ helper methods in Array too */ + klass = mono_class_try_load_from_name (acfg->image, "System.Collections.Generic", "ICollection`1"); + if (klass) + add_instances_of (acfg, klass, insts, ninsts, TRUE); + + klass = mono_class_try_load_from_name (acfg->image, "System.Collections.Generic", "IList`1"); + if (klass) + add_instances_of (acfg, klass, insts, ninsts, TRUE); + + klass = mono_class_try_load_from_name (acfg->image, "System.Collections.Generic", "IEnumerable`1"); + if (klass) + add_instances_of (acfg, klass, insts, ninsts, TRUE); + + /* + * Add a managed-to-native wrapper of Array.GetGenericValueImpl, which is + * used for all instances of GetGenericValueImpl by the AOT runtime. + */ + { + MonoGenericContext ctx; + MonoType *args [16]; + MonoMethod *get_method; + MonoClass *array_klass = mono_array_class_get (mono_defaults.object_class, 1)->parent; + + get_method = mono_class_get_method_from_name (array_klass, "GetGenericValueImpl", 2); + + if (get_method) { + MonoError error; + memset (&ctx, 0, sizeof (ctx)); + args [0] = &mono_defaults.object_class->byval_arg; + ctx.method_inst = mono_metadata_get_generic_inst (1, args); + add_extra_method (acfg, mono_marshal_get_native_wrapper (mono_class_inflate_generic_method_checked (get_method, &ctx, &error), TRUE, TRUE)); + g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */ + } + } + + /* Same for CompareExchange/Exchange */ + { + MonoGenericContext ctx; + MonoType *args [16]; + MonoMethod *m; + MonoClass *interlocked_klass = mono_class_load_from_name (mono_defaults.corlib, "System.Threading", "Interlocked"); + gpointer iter = NULL; + + while ((m = mono_class_get_methods (interlocked_klass, &iter))) { + if ((!strcmp (m->name, "CompareExchange") || !strcmp (m->name, "Exchange")) && m->is_generic) { + MonoError error; + memset (&ctx, 0, sizeof (ctx)); + args [0] = &mono_defaults.object_class->byval_arg; + ctx.method_inst = mono_metadata_get_generic_inst (1, args); + add_extra_method (acfg, mono_marshal_get_native_wrapper (mono_class_inflate_generic_method_checked (m, &ctx, &error), TRUE, TRUE)); + g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */ + } + } + } + + /* Same for Volatile.Read/Write */ + { + MonoGenericContext ctx; + MonoType *args [16]; + MonoMethod *m; + MonoClass *volatile_klass = mono_class_try_load_from_name (mono_defaults.corlib, "System.Threading", "Volatile"); + gpointer iter = NULL; + + if (volatile_klass) { + while ((m = mono_class_get_methods (volatile_klass, &iter))) { + if ((!strcmp (m->name, "Read") || !strcmp (m->name, "Write")) && m->is_generic) { + MonoError error; + memset (&ctx, 0, sizeof (ctx)); + args [0] = &mono_defaults.object_class->byval_arg; + ctx.method_inst = mono_metadata_get_generic_inst (1, args); + add_extra_method (acfg, mono_marshal_get_native_wrapper (mono_class_inflate_generic_method_checked (m, &ctx, &error), TRUE, TRUE)); + g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */ + } + } + } + } + + /* object[] accessor wrappers. */ + for (i = 1; i < 4; ++i) { + MonoClass *obj_array_class = mono_array_class_get (mono_defaults.object_class, i); + MonoMethod *m; + + m = mono_class_get_method_from_name (obj_array_class, "Get", i); + g_assert (m); + + m = mono_marshal_get_array_accessor_wrapper (m); + add_extra_method (acfg, m); + + m = mono_class_get_method_from_name (obj_array_class, "Address", i); + g_assert (m); + + m = mono_marshal_get_array_accessor_wrapper (m); + add_extra_method (acfg, m); + + m = mono_class_get_method_from_name (obj_array_class, "Set", i + 1); + g_assert (m); + + m = mono_marshal_get_array_accessor_wrapper (m); + add_extra_method (acfg, m); + } + } +} + +/* + * is_direct_callable: + * + * Return whenever the method identified by JI is directly callable without + * going through the PLT. + */ +static gboolean +is_direct_callable (MonoAotCompile *acfg, MonoMethod *method, MonoJumpInfo *patch_info) +{ + if ((patch_info->type == MONO_PATCH_INFO_METHOD) && (patch_info->data.method->klass->image == acfg->image)) { + MonoCompile *callee_cfg = (MonoCompile *)g_hash_table_lookup (acfg->method_to_cfg, patch_info->data.method); + if (callee_cfg) { + gboolean direct_callable = TRUE; + + if (direct_callable && (acfg->aot_opts.dedup || acfg->aot_opts.dedup_include) && mono_aot_can_dedup (patch_info->data.method)) + direct_callable = FALSE; + + if (direct_callable && !(!callee_cfg->has_got_slots && mono_class_is_before_field_init (callee_cfg->method->klass))) + direct_callable = FALSE; + if ((callee_cfg->method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) && (!method || method->wrapper_type != MONO_WRAPPER_SYNCHRONIZED)) + // FIXME: Maybe call the wrapper directly ? + direct_callable = FALSE; + + if (acfg->aot_opts.soft_debug || acfg->aot_opts.no_direct_calls) { + /* Disable this so all calls go through load_method (), see the + * mini_get_debug_options ()->load_aot_jit_info_eagerly = TRUE; line in + * mono_debugger_agent_init (). + */ + direct_callable = FALSE; + } + + if (callee_cfg->method->wrapper_type == MONO_WRAPPER_ALLOC) + /* sgen does some initialization when the allocator method is created */ + direct_callable = FALSE; + if (callee_cfg->method->wrapper_type == MONO_WRAPPER_WRITE_BARRIER) + /* we don't know at compile time whether sgen is concurrent or not */ + direct_callable = FALSE; + + if (direct_callable) + return TRUE; + } + } else if ((patch_info->type == MONO_PATCH_INFO_ICALL_ADDR_CALL && patch_info->data.method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) { + if (acfg->aot_opts.direct_pinvoke) + return TRUE; + } else if (patch_info->type == MONO_PATCH_INFO_ICALL_ADDR_CALL) { + if (acfg->aot_opts.direct_icalls) + return TRUE; + return FALSE; + } + + return FALSE; +} + +#ifdef MONO_ARCH_AOT_SUPPORTED +static const char * +get_pinvoke_import (MonoAotCompile *acfg, MonoMethod *method) +{ + MonoImage *image = method->klass->image; + MonoMethodPInvoke *piinfo = (MonoMethodPInvoke *) method; + MonoTableInfo *tables = image->tables; + MonoTableInfo *im = &tables [MONO_TABLE_IMPLMAP]; + MonoTableInfo *mr = &tables [MONO_TABLE_MODULEREF]; + guint32 im_cols [MONO_IMPLMAP_SIZE]; + char *import; + + import = (char *)g_hash_table_lookup (acfg->method_to_pinvoke_import, method); + if (import != NULL) + return import; + + if (!piinfo->implmap_idx || piinfo->implmap_idx > im->rows) + return NULL; + + mono_metadata_decode_row (im, piinfo->implmap_idx - 1, im_cols, MONO_IMPLMAP_SIZE); + + if (!im_cols [MONO_IMPLMAP_SCOPE] || im_cols [MONO_IMPLMAP_SCOPE] > mr->rows) + return NULL; + + import = g_strdup_printf ("%s", mono_metadata_string_heap (image, im_cols [MONO_IMPLMAP_NAME])); + + g_hash_table_insert (acfg->method_to_pinvoke_import, method, import); + + return import; +} +#else +static const char * +get_pinvoke_import (MonoAotCompile *acfg, MonoMethod *method) +{ + return NULL; +} +#endif + +static gint +compare_lne (MonoDebugLineNumberEntry *a, MonoDebugLineNumberEntry *b) +{ + if (a->native_offset == b->native_offset) + return a->il_offset - b->il_offset; + else + return a->native_offset - b->native_offset; +} + +/* + * compute_line_numbers: + * + * Returns a sparse array of size CODE_SIZE containing MonoDebugSourceLocation* entries for the native offsets which have a corresponding line number + * entry. + */ +static MonoDebugSourceLocation** +compute_line_numbers (MonoMethod *method, int code_size, MonoDebugMethodJitInfo *debug_info) +{ + MonoDebugMethodInfo *minfo; + MonoDebugLineNumberEntry *ln_array; + MonoDebugSourceLocation *loc; + int i, prev_line, prev_il_offset; + int *native_to_il_offset = NULL; + MonoDebugSourceLocation **res; + gboolean first; + + minfo = mono_debug_lookup_method (method); + if (!minfo) + return NULL; + // FIXME: This seems to happen when two methods have the same cfg->method_to_register + if (debug_info->code_size != code_size) + return NULL; + + g_assert (code_size); + + /* Compute the native->IL offset mapping */ + + ln_array = g_new0 (MonoDebugLineNumberEntry, debug_info->num_line_numbers); + memcpy (ln_array, debug_info->line_numbers, debug_info->num_line_numbers * sizeof (MonoDebugLineNumberEntry)); + + qsort (ln_array, debug_info->num_line_numbers, sizeof (MonoDebugLineNumberEntry), (int (*)(const void *, const void *))compare_lne); + + native_to_il_offset = g_new0 (int, code_size + 1); + + for (i = 0; i < debug_info->num_line_numbers; ++i) { + int j; + MonoDebugLineNumberEntry *lne = &ln_array [i]; + + if (i == 0) { + for (j = 0; j < lne->native_offset; ++j) + native_to_il_offset [j] = -1; + } + + if (i < debug_info->num_line_numbers - 1) { + MonoDebugLineNumberEntry *lne_next = &ln_array [i + 1]; + + for (j = lne->native_offset; j < lne_next->native_offset; ++j) + native_to_il_offset [j] = lne->il_offset; + } else { + for (j = lne->native_offset; j < code_size; ++j) + native_to_il_offset [j] = lne->il_offset; + } + } + g_free (ln_array); + + /* Compute the native->line number mapping */ + res = g_new0 (MonoDebugSourceLocation*, code_size); + prev_il_offset = -1; + prev_line = -1; + first = TRUE; + for (i = 0; i < code_size; ++i) { + int il_offset = native_to_il_offset [i]; + + if (il_offset == -1 || il_offset == prev_il_offset) + continue; + prev_il_offset = il_offset; + loc = mono_debug_method_lookup_location (minfo, il_offset); + if (!(loc && loc->source_file)) + continue; + if (loc->row == prev_line) { + mono_debug_free_source_location (loc); + continue; + } + prev_line = loc->row; + //printf ("D: %s:%d il=%x native=%x\n", loc->source_file, loc->row, il_offset, i); + if (first) + /* This will cover the prolog too */ + res [0] = loc; + else + res [i] = loc; + first = FALSE; + } + return res; +} + +static int +get_file_index (MonoAotCompile *acfg, const char *source_file) +{ + int findex; + + // FIXME: Free these + if (!acfg->dwarf_ln_filenames) + acfg->dwarf_ln_filenames = g_hash_table_new (g_str_hash, g_str_equal); + findex = GPOINTER_TO_INT (g_hash_table_lookup (acfg->dwarf_ln_filenames, source_file)); + if (!findex) { + findex = g_hash_table_size (acfg->dwarf_ln_filenames) + 1; + g_hash_table_insert (acfg->dwarf_ln_filenames, g_strdup (source_file), GINT_TO_POINTER (findex)); + emit_unset_mode (acfg); + fprintf (acfg->fp, ".file %d \"%s\"\n", findex, mono_dwarf_escape_path (source_file)); + } + return findex; +} + +#ifdef TARGET_ARM64 +#define INST_LEN 4 +#else +#define INST_LEN 1 +#endif + +/* + * emit_and_reloc_code: + * + * Emit the native code in CODE, handling relocations along the way. If GOT_ONLY + * is true, calls are made through the GOT too. This is used for emitting trampolines + * in full-aot mode, since calls made from trampolines couldn't go through the PLT, + * since trampolines are needed to make PTL work. + */ +static void +emit_and_reloc_code (MonoAotCompile *acfg, MonoMethod *method, guint8 *code, guint32 code_len, MonoJumpInfo *relocs, gboolean got_only, MonoDebugMethodJitInfo *debug_info) +{ + int i, pindex, start_index; + GPtrArray *patches; + MonoJumpInfo *patch_info; + MonoDebugSourceLocation **locs = NULL; + gboolean skip, prologue_end = FALSE; +#ifdef MONO_ARCH_AOT_SUPPORTED + gboolean direct_call, external_call; + guint32 got_slot; + const char *direct_call_target = 0; + const char *direct_pinvoke; +#endif + + if (acfg->gas_line_numbers && method && debug_info) { + locs = compute_line_numbers (method, code_len, debug_info); + if (!locs) { + int findex = get_file_index (acfg, ""); + emit_unset_mode (acfg); + fprintf (acfg->fp, ".loc %d %d 0\n", findex, 1); + } + } + + /* Collect and sort relocations */ + patches = g_ptr_array_new (); + for (patch_info = relocs; patch_info; patch_info = patch_info->next) + g_ptr_array_add (patches, patch_info); + g_ptr_array_sort (patches, compare_patches); + + start_index = 0; + for (i = 0; i < code_len; i += INST_LEN) { + patch_info = NULL; + for (pindex = start_index; pindex < patches->len; ++pindex) { + patch_info = (MonoJumpInfo *)g_ptr_array_index (patches, pindex); + if (patch_info->ip.i >= i) + break; + } + + if (locs && locs [i]) { + MonoDebugSourceLocation *loc = locs [i]; + int findex; + const char *options; + + findex = get_file_index (acfg, loc->source_file); + emit_unset_mode (acfg); + if (!prologue_end) + options = " prologue_end"; + else + options = ""; + prologue_end = TRUE; + fprintf (acfg->fp, ".loc %d %d 0%s\n", findex, loc->row, options); + mono_debug_free_source_location (loc); + } + + skip = FALSE; +#ifdef MONO_ARCH_AOT_SUPPORTED + if (patch_info && (patch_info->ip.i == i) && (pindex < patches->len)) { + start_index = pindex; + + switch (patch_info->type) { + case MONO_PATCH_INFO_NONE: + break; + case MONO_PATCH_INFO_GOT_OFFSET: { + int code_size; + + arch_emit_got_offset (acfg, code + i, &code_size); + i += code_size - INST_LEN; + skip = TRUE; + patch_info->type = MONO_PATCH_INFO_NONE; + break; + } + case MONO_PATCH_INFO_OBJC_SELECTOR_REF: { + int code_size, index; + char *selector = (char *)patch_info->data.target; + + if (!acfg->objc_selector_to_index) + acfg->objc_selector_to_index = g_hash_table_new (g_str_hash, g_str_equal); + if (!acfg->objc_selectors) + acfg->objc_selectors = g_ptr_array_new (); + index = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->objc_selector_to_index, selector)); + if (index) + index --; + else { + index = acfg->objc_selector_index; + g_ptr_array_add (acfg->objc_selectors, (void*)patch_info->data.target); + g_hash_table_insert (acfg->objc_selector_to_index, selector, GUINT_TO_POINTER (index + 1)); + acfg->objc_selector_index ++; + } + + arch_emit_objc_selector_ref (acfg, code + i, index, &code_size); + i += code_size - INST_LEN; + skip = TRUE; + patch_info->type = MONO_PATCH_INFO_NONE; + break; + } + default: { + /* + * If this patch is a call, try emitting a direct call instead of + * through a PLT entry. This is possible if the called method is in + * the same assembly and requires no initialization. + */ + direct_call = FALSE; + external_call = FALSE; + if ((patch_info->type == MONO_PATCH_INFO_METHOD) && (patch_info->data.method->klass->image == acfg->image)) { + if (!got_only && is_direct_callable (acfg, method, patch_info)) { + MonoCompile *callee_cfg = (MonoCompile *)g_hash_table_lookup (acfg->method_to_cfg, patch_info->data.method); + + // Don't compile inflated methods if we're doing dedup + if (acfg->aot_opts.dedup && !mono_aot_can_dedup (patch_info->data.method)) { + char *name = mono_aot_get_mangled_method_name (patch_info->data.method); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_AOT, "DIRECT CALL: %s by %s", name, method ? mono_method_full_name (method, TRUE) : ""); + g_free (name); + + direct_call = TRUE; + direct_call_target = callee_cfg->asm_symbol; + patch_info->type = MONO_PATCH_INFO_NONE; + acfg->stats.direct_calls ++; + } + } + + acfg->stats.all_calls ++; + } else if (patch_info->type == MONO_PATCH_INFO_ICALL_ADDR_CALL) { + if (!got_only && is_direct_callable (acfg, method, patch_info)) { + if (!(patch_info->data.method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) + direct_pinvoke = mono_lookup_icall_symbol (patch_info->data.method); + else + direct_pinvoke = get_pinvoke_import (acfg, patch_info->data.method); + if (direct_pinvoke) { + direct_call = TRUE; + g_assert (strlen (direct_pinvoke) < 1000); + direct_call_target = g_strdup_printf ("%s%s", acfg->user_symbol_prefix, direct_pinvoke); + } + } + } else if (patch_info->type == MONO_PATCH_INFO_JIT_ICALL_ADDR) { + const char *sym = mono_lookup_jit_icall_symbol (patch_info->data.name); + if (!got_only && sym && acfg->aot_opts.direct_icalls) { + /* Call to a C function implementing a jit icall */ + direct_call = TRUE; + external_call = TRUE; + g_assert (strlen (sym) < 1000); + direct_call_target = g_strdup_printf ("%s%s", acfg->user_symbol_prefix, sym); + } + } else if (patch_info->type == MONO_PATCH_INFO_INTERNAL_METHOD) { + MonoJitICallInfo *info = mono_find_jit_icall_by_name (patch_info->data.name); + const char *sym = mono_lookup_jit_icall_symbol (patch_info->data.name); + if (!got_only && sym && acfg->aot_opts.direct_icalls && info->func == info->wrapper) { + /* Call to a jit icall without a wrapper */ + direct_call = TRUE; + external_call = TRUE; + g_assert (strlen (sym) < 1000); + direct_call_target = g_strdup_printf ("%s%s", acfg->user_symbol_prefix, sym); + } + } + + if (direct_call) { + patch_info->type = MONO_PATCH_INFO_NONE; + acfg->stats.direct_calls ++; + } + + if (!got_only && !direct_call) { + MonoPltEntry *plt_entry = get_plt_entry (acfg, patch_info); + if (plt_entry) { + /* This patch has a PLT entry, so we must emit a call to the PLT entry */ + direct_call = TRUE; + direct_call_target = plt_entry->symbol; + + /* Nullify the patch */ + patch_info->type = MONO_PATCH_INFO_NONE; + plt_entry->jit_used = TRUE; + } + } + + if (direct_call) { + int call_size; + + arch_emit_direct_call (acfg, direct_call_target, external_call, FALSE, patch_info, &call_size); + i += call_size - INST_LEN; + } else { + int code_size; + + got_slot = get_got_offset (acfg, FALSE, patch_info); + + arch_emit_got_access (acfg, acfg->got_symbol, code + i, got_slot, &code_size); + i += code_size - INST_LEN; + } + skip = TRUE; + } + } + } +#endif /* MONO_ARCH_AOT_SUPPORTED */ + + if (!skip) { + /* Find next patch */ + patch_info = NULL; + for (pindex = start_index; pindex < patches->len; ++pindex) { + patch_info = (MonoJumpInfo *)g_ptr_array_index (patches, pindex); + if (patch_info->ip.i >= i) + break; + } + + /* Try to emit multiple bytes at once */ + if (pindex < patches->len && patch_info->ip.i > i) { + int limit; + + for (limit = i + INST_LEN; limit < patch_info->ip.i; limit += INST_LEN) { + if (locs && locs [limit]) + break; + } + + emit_code_bytes (acfg, code + i, limit - i); + i = limit - INST_LEN; + } else { + emit_code_bytes (acfg, code + i, INST_LEN); + } + } + } + + g_ptr_array_free (patches, TRUE); + g_free (locs); +} + +/* + * sanitize_symbol: + * + * Return a modified version of S which only includes characters permissible in symbols. + */ +static char* +sanitize_symbol (MonoAotCompile *acfg, char *s) +{ + gboolean process = FALSE; + int i, len; + GString *gs; + char *res; + + if (!s) + return s; + + len = strlen (s); + for (i = 0; i < len; ++i) + if (!(s [i] <= 0x7f && (isalnum (s [i]) || s [i] == '_'))) + process = TRUE; + if (!process) + return s; + + gs = g_string_sized_new (len); + for (i = 0; i < len; ++i) { + guint8 c = s [i]; + if (c <= 0x7f && (isalnum (c) || c == '_')) { + g_string_append_c (gs, c); + } else if (c > 0x7f) { + /* multi-byte utf8 */ + g_string_append_printf (gs, "_0x%x", c); + i ++; + c = s [i]; + while (c >> 6 == 0x2) { + g_string_append_printf (gs, "%x", c); + i ++; + c = s [i]; + } + g_string_append_printf (gs, "_"); + i --; + } else { + g_string_append_c (gs, '_'); + } + } + + res = mono_mempool_strdup (acfg->mempool, gs->str); + g_string_free (gs, TRUE); + return res; +} + +static char* +get_debug_sym (MonoMethod *method, const char *prefix, GHashTable *cache) +{ + char *name1, *name2, *cached; + int i, j, len, count; + MonoMethod *cached_method; + + name1 = mono_method_full_name (method, TRUE); + +#ifdef TARGET_MACH + // This is so that we don't accidentally create a local symbol (which starts with 'L') + if ((!prefix || !*prefix) && name1 [0] == 'L') + prefix = "_"; +#endif + +#if defined(TARGET_WIN32) && defined(TARGET_X86) + char adjustedPrefix [MAX_SYMBOL_SIZE]; + prefix = mangle_symbol (prefix, adjustedPrefix, G_N_ELEMENTS (adjustedPrefix)); +#endif + + len = strlen (name1); + name2 = (char *)malloc (strlen (prefix) + len + 16); + memcpy (name2, prefix, strlen (prefix)); + j = strlen (prefix); + for (i = 0; i < len; ++i) { + if (i == 0 && name1 [0] >= '0' && name1 [0] <= '9') { + name2 [j ++] = '_'; + } else if (isalnum (name1 [i])) { + name2 [j ++] = name1 [i]; + } else if (name1 [i] == ' ' && name1 [i + 1] == '(' && name1 [i + 2] == ')') { + i += 2; + } else if (name1 [i] == ',' && name1 [i + 1] == ' ') { + name2 [j ++] = '_'; + i++; + } else if (name1 [i] == '(' || name1 [i] == ')' || name1 [i] == '>') { + } else + name2 [j ++] = '_'; + } + name2 [j] = '\0'; + + g_free (name1); + + count = 0; + while (TRUE) { + cached_method = (MonoMethod *)g_hash_table_lookup (cache, name2); + if (!(cached_method && cached_method != method)) + break; + sprintf (name2 + j, "_%d", count); + count ++; + } + + cached = g_strdup (name2); + g_hash_table_insert (cache, cached, method); + + return name2; +} + +static void +emit_method_code (MonoAotCompile *acfg, MonoCompile *cfg) +{ + MonoMethod *method; + int method_index; + guint8 *code; + char *debug_sym = NULL; + char *symbol = NULL; + int func_alignment = AOT_FUNC_ALIGNMENT; + char *export_name; + + g_assert (!ignore_cfg (cfg)); + + method = cfg->orig_method; + code = cfg->native_code; + + method_index = get_method_index (acfg, method); + symbol = g_strdup_printf ("%sme_%x", acfg->temp_prefix, method_index); + + /* Make the labels local */ + emit_section_change (acfg, ".text", 0); + emit_alignment_code (acfg, func_alignment); + + if (acfg->global_symbols && acfg->need_no_dead_strip) + fprintf (acfg->fp, " .no_dead_strip %s\n", cfg->asm_symbol); + + emit_label (acfg, cfg->asm_symbol); + + if (acfg->aot_opts.write_symbols && !acfg->global_symbols && !acfg->llvm) { + /* + * Write a C style symbol for every method, this has two uses: + * - it works on platforms where the dwarf debugging info is not + * yet supported. + * - it allows the setting of breakpoints of aot-ed methods. + */ + + // Comment out to force dedup to link these symbols and forbid compiling + // in duplicated code. This is an "assert when linking if broken" trick. + /*if (mono_aot_can_dedup (method) && (acfg->aot_opts.dedup || acfg->aot_opts.dedup_include))*/ + /*debug_sym = mono_aot_get_mangled_method_name (method);*/ + /*else*/ + debug_sym = get_debug_sym (method, "", acfg->method_label_hash); + + cfg->asm_debug_symbol = g_strdup (debug_sym); + + if (acfg->need_no_dead_strip) + fprintf (acfg->fp, " .no_dead_strip %s\n", debug_sym); + + // Comment out to force dedup to link these symbols and forbid compiling + // in duplicated code. This is an "assert when linking if broken" trick. + /*if (mono_aot_can_dedup (method) && (acfg->aot_opts.dedup || acfg->aot_opts.dedup_include))*/ + /*emit_global_inner (acfg, debug_sym, TRUE);*/ + /*else*/ + emit_local_symbol (acfg, debug_sym, symbol, TRUE); + + emit_label (acfg, debug_sym); + } + + export_name = (char *)g_hash_table_lookup (acfg->export_names, method); + if (export_name) { + /* Emit a global symbol for the method */ + emit_global_inner (acfg, export_name, TRUE); + emit_label (acfg, export_name); + } + + if (cfg->verbose_level > 0 && !ignore_cfg (cfg)) + g_print ("Method %s emitted as %s\n", mono_method_get_full_name (method), cfg->asm_symbol); + + acfg->stats.code_size += cfg->code_len; + + acfg->cfgs [method_index]->got_offset = acfg->got_offset; + + emit_and_reloc_code (acfg, method, code, cfg->code_len, cfg->patch_info, FALSE, mono_debug_find_method (cfg->jit_info->d.method, mono_domain_get ())); + + emit_line (acfg); + + if (acfg->aot_opts.write_symbols) { + if (debug_sym) + emit_symbol_size (acfg, debug_sym, "."); + else + emit_symbol_size (acfg, cfg->asm_symbol, "."); + g_free (debug_sym); + } + + emit_label (acfg, symbol); + + arch_emit_unwind_info_sections (acfg, cfg->asm_symbol, symbol, cfg->unwind_ops); + + g_free (symbol); +} + +/** + * encode_patch: + * + * Encode PATCH_INFO into its disk representation. + */ +static void +encode_patch (MonoAotCompile *acfg, MonoJumpInfo *patch_info, guint8 *buf, guint8 **endbuf) +{ + guint8 *p = buf; + + switch (patch_info->type) { + case MONO_PATCH_INFO_NONE: + break; + case MONO_PATCH_INFO_IMAGE: + encode_value (get_image_index (acfg, patch_info->data.image), p, &p); + break; + case MONO_PATCH_INFO_MSCORLIB_GOT_ADDR: + case MONO_PATCH_INFO_GC_CARD_TABLE_ADDR: + case MONO_PATCH_INFO_GC_NURSERY_START: + case MONO_PATCH_INFO_GC_NURSERY_BITS: + break; + case MONO_PATCH_INFO_CASTCLASS_CACHE: + encode_value (patch_info->data.index, p, &p); + break; + case MONO_PATCH_INFO_METHOD_REL: + encode_value ((gint)patch_info->data.offset, p, &p); + break; + case MONO_PATCH_INFO_SWITCH: { + gpointer *table = (gpointer *)patch_info->data.table->table; + int k; + + encode_value (patch_info->data.table->table_size, p, &p); + for (k = 0; k < patch_info->data.table->table_size; k++) + encode_value ((int)(gssize)table [k], p, &p); + break; + } + case MONO_PATCH_INFO_METHODCONST: + case MONO_PATCH_INFO_METHOD: + case MONO_PATCH_INFO_METHOD_JUMP: + case MONO_PATCH_INFO_ICALL_ADDR: + case MONO_PATCH_INFO_ICALL_ADDR_CALL: + case MONO_PATCH_INFO_METHOD_RGCTX: + case MONO_PATCH_INFO_METHOD_CODE_SLOT: + encode_method_ref (acfg, patch_info->data.method, p, &p); + break; + case MONO_PATCH_INFO_AOT_JIT_INFO: + case MONO_PATCH_INFO_GET_TLS_TRAMP: + case MONO_PATCH_INFO_SET_TLS_TRAMP: + encode_value (patch_info->data.index, p, &p); + break; + case MONO_PATCH_INFO_INTERNAL_METHOD: + case MONO_PATCH_INFO_JIT_ICALL_ADDR: + case MONO_PATCH_INFO_JIT_ICALL_ADDR_NOCALL: { + guint32 len = strlen (patch_info->data.name); + + encode_value (len, p, &p); + + memcpy (p, patch_info->data.name, len); + p += len; + *p++ = '\0'; + break; + } + case MONO_PATCH_INFO_LDSTR: { + guint32 image_index = get_image_index (acfg, patch_info->data.token->image); + guint32 token = patch_info->data.token->token; + g_assert (mono_metadata_token_code (token) == MONO_TOKEN_STRING); + encode_value (image_index, p, &p); + encode_value (patch_info->data.token->token - MONO_TOKEN_STRING, p, &p); + break; + } + case MONO_PATCH_INFO_RVA: + case MONO_PATCH_INFO_DECLSEC: + case MONO_PATCH_INFO_LDTOKEN: + case MONO_PATCH_INFO_TYPE_FROM_HANDLE: + encode_value (get_image_index (acfg, patch_info->data.token->image), p, &p); + encode_value (patch_info->data.token->token, p, &p); + encode_value (patch_info->data.token->has_context, p, &p); + if (patch_info->data.token->has_context) + encode_generic_context (acfg, &patch_info->data.token->context, p, &p); + break; + case MONO_PATCH_INFO_EXC_NAME: { + MonoClass *ex_class; + + ex_class = + mono_class_load_from_name (mono_defaults.exception_class->image, + "System", (const char *)patch_info->data.target); + encode_klass_ref (acfg, ex_class, p, &p); + break; + } + case MONO_PATCH_INFO_R4: + encode_value (*((guint32 *)patch_info->data.target), p, &p); + break; + case MONO_PATCH_INFO_R8: + encode_value (((guint32 *)patch_info->data.target) [MINI_LS_WORD_IDX], p, &p); + encode_value (((guint32 *)patch_info->data.target) [MINI_MS_WORD_IDX], p, &p); + break; + case MONO_PATCH_INFO_VTABLE: + case MONO_PATCH_INFO_CLASS: + case MONO_PATCH_INFO_IID: + case MONO_PATCH_INFO_ADJUSTED_IID: + encode_klass_ref (acfg, patch_info->data.klass, p, &p); + break; + case MONO_PATCH_INFO_DELEGATE_TRAMPOLINE: + encode_klass_ref (acfg, patch_info->data.del_tramp->klass, p, &p); + if (patch_info->data.del_tramp->method) { + encode_value (1, p, &p); + encode_method_ref (acfg, patch_info->data.del_tramp->method, p, &p); + } else { + encode_value (0, p, &p); + } + encode_value (patch_info->data.del_tramp->is_virtual, p, &p); + break; + case MONO_PATCH_INFO_FIELD: + case MONO_PATCH_INFO_SFLDA: + encode_field_info (acfg, patch_info->data.field, p, &p); + break; + case MONO_PATCH_INFO_INTERRUPTION_REQUEST_FLAG: + break; + case MONO_PATCH_INFO_PROFILER_ALLOCATION_COUNT: + break; + case MONO_PATCH_INFO_RGCTX_FETCH: + case MONO_PATCH_INFO_RGCTX_SLOT_INDEX: { + MonoJumpInfoRgctxEntry *entry = patch_info->data.rgctx_entry; + guint32 offset; + guint8 *buf2, *p2; + + /* + * entry->method has a lenghtly encoding and multiple rgctx_fetch entries + * reference the same method, so encode the method only once. + */ + offset = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->method_blob_hash, entry->method)); + if (!offset) { + buf2 = (guint8 *)g_malloc (1024); + p2 = buf2; + + encode_method_ref (acfg, entry->method, p2, &p2); + g_assert (p2 - buf2 < 1024); + + offset = add_to_blob (acfg, buf2, p2 - buf2); + g_free (buf2); + + g_hash_table_insert (acfg->method_blob_hash, entry->method, GUINT_TO_POINTER (offset + 1)); + } else { + offset --; + } + + encode_value (offset, p, &p); + g_assert ((int)entry->info_type < 256); + g_assert (entry->data->type < 256); + encode_value ((entry->in_mrgctx ? 1 : 0) | (entry->info_type << 1) | (entry->data->type << 9), p, &p); + encode_patch (acfg, entry->data, p, &p); + break; + } + case MONO_PATCH_INFO_SEQ_POINT_INFO: + case MONO_PATCH_INFO_AOT_MODULE: + break; + case MONO_PATCH_INFO_SIGNATURE: + case MONO_PATCH_INFO_GSHAREDVT_IN_WRAPPER: + encode_signature (acfg, (MonoMethodSignature*)patch_info->data.target, p, &p); + break; + case MONO_PATCH_INFO_GSHAREDVT_CALL: + encode_signature (acfg, (MonoMethodSignature*)patch_info->data.gsharedvt->sig, p, &p); + encode_method_ref (acfg, patch_info->data.gsharedvt->method, p, &p); + break; + case MONO_PATCH_INFO_GSHAREDVT_METHOD: { + MonoGSharedVtMethodInfo *info = patch_info->data.gsharedvt_method; + int i; + + encode_method_ref (acfg, info->method, p, &p); + encode_value (info->num_entries, p, &p); + for (i = 0; i < info->num_entries; ++i) { + MonoRuntimeGenericContextInfoTemplate *template_ = &info->entries [i]; + + encode_value (template_->info_type, p, &p); + switch (mini_rgctx_info_type_to_patch_info_type (template_->info_type)) { + case MONO_PATCH_INFO_CLASS: + encode_klass_ref (acfg, mono_class_from_mono_type ((MonoType *)template_->data), p, &p); + break; + case MONO_PATCH_INFO_FIELD: + encode_field_info (acfg, (MonoClassField *)template_->data, p, &p); + break; + default: + g_assert_not_reached (); + break; + } + } + break; + } + case MONO_PATCH_INFO_LDSTR_LIT: { + const char *s = (const char *)patch_info->data.target; + int len = strlen (s); + + encode_value (len, p, &p); + memcpy (p, s, len + 1); + p += len + 1; + break; + } + case MONO_PATCH_INFO_VIRT_METHOD: + encode_klass_ref (acfg, patch_info->data.virt_method->klass, p, &p); + encode_method_ref (acfg, patch_info->data.virt_method->method, p, &p); + break; + case MONO_PATCH_INFO_GC_SAFE_POINT_FLAG: + case MONO_PATCH_INFO_JIT_THREAD_ATTACH: + break; + default: + g_warning ("unable to handle jump info %d", patch_info->type); + g_assert_not_reached (); + } + + *endbuf = p; +} + +static void +encode_patch_list (MonoAotCompile *acfg, GPtrArray *patches, int n_patches, gboolean llvm, int first_got_offset, guint8 *buf, guint8 **endbuf) +{ + guint8 *p = buf; + guint32 pindex, offset; + MonoJumpInfo *patch_info; + + encode_value (n_patches, p, &p); + + for (pindex = 0; pindex < patches->len; ++pindex) { + patch_info = (MonoJumpInfo *)g_ptr_array_index (patches, pindex); + + if (patch_info->type == MONO_PATCH_INFO_NONE || patch_info->type == MONO_PATCH_INFO_BB) + /* Nothing to do */ + continue; + + offset = get_got_offset (acfg, llvm, patch_info); + encode_value (offset, p, &p); + } + + *endbuf = p; +} + +static void +emit_method_info (MonoAotCompile *acfg, MonoCompile *cfg) +{ + MonoMethod *method; + int pindex, buf_size, n_patches; + GPtrArray *patches; + MonoJumpInfo *patch_info; + guint32 method_index; + guint8 *p, *buf; + guint32 first_got_offset; + + method = cfg->orig_method; + + method_index = get_method_index (acfg, method); + + /* Sort relocations */ + patches = g_ptr_array_new (); + for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) + g_ptr_array_add (patches, patch_info); + g_ptr_array_sort (patches, compare_patches); + + first_got_offset = acfg->cfgs [method_index]->got_offset; + + /**********************/ + /* Encode method info */ + /**********************/ + + buf_size = (patches->len < 1000) ? 40960 : 40960 + (patches->len * 64); + p = buf = (guint8 *)g_malloc (buf_size); + + if (mono_class_get_cctor (method->klass)) { + encode_value (1, p, &p); + encode_klass_ref (acfg, method->klass, p, &p); + } else { + /* Not needed when loading the method */ + encode_value (0, p, &p); + } + + g_assert (!(cfg->opt & MONO_OPT_SHARED)); + + n_patches = 0; + for (pindex = 0; pindex < patches->len; ++pindex) { + patch_info = (MonoJumpInfo *)g_ptr_array_index (patches, pindex); + + if ((patch_info->type == MONO_PATCH_INFO_GOT_OFFSET) || + (patch_info->type == MONO_PATCH_INFO_NONE)) { + patch_info->type = MONO_PATCH_INFO_NONE; + /* Nothing to do */ + continue; + } + + if ((patch_info->type == MONO_PATCH_INFO_IMAGE) && (patch_info->data.image == acfg->image)) { + /* Stored in a GOT slot initialized at module load time */ + patch_info->type = MONO_PATCH_INFO_NONE; + continue; + } + + if (patch_info->type == MONO_PATCH_INFO_GC_CARD_TABLE_ADDR || + patch_info->type == MONO_PATCH_INFO_GC_NURSERY_START || + patch_info->type == MONO_PATCH_INFO_GC_NURSERY_BITS || + patch_info->type == MONO_PATCH_INFO_AOT_MODULE) { + /* Stored in a GOT slot initialized at module load time */ + patch_info->type = MONO_PATCH_INFO_NONE; + continue; + } + + if (is_plt_patch (patch_info) && !(cfg->compile_llvm && acfg->aot_opts.llvm_only)) { + /* Calls are made through the PLT */ + patch_info->type = MONO_PATCH_INFO_NONE; + continue; + } + + n_patches ++; + } + + if (n_patches) + g_assert (cfg->has_got_slots); + + encode_patch_list (acfg, patches, n_patches, cfg->compile_llvm, first_got_offset, p, &p); + + g_ptr_array_free (patches, TRUE); + + acfg->stats.info_size += p - buf; + + g_assert (p - buf < buf_size); + + cfg->method_info_offset = add_to_blob (acfg, buf, p - buf); + g_free (buf); +} + +static guint32 +get_unwind_info_offset (MonoAotCompile *acfg, guint8 *encoded, guint32 encoded_len) +{ + guint32 cache_index; + guint32 offset; + + /* Reuse the unwind module to canonize and store unwind info entries */ + cache_index = mono_cache_unwind_info (encoded, encoded_len); + + /* Use +/- 1 to distinguish 0s from missing entries */ + offset = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->unwind_info_offsets, GUINT_TO_POINTER (cache_index + 1))); + if (offset) + return offset - 1; + else { + guint8 buf [16]; + guint8 *p; + + /* + * It would be easier to use assembler symbols, but the caller needs an + * offset now. + */ + offset = acfg->unwind_info_offset; + g_hash_table_insert (acfg->unwind_info_offsets, GUINT_TO_POINTER (cache_index + 1), GUINT_TO_POINTER (offset + 1)); + g_ptr_array_add (acfg->unwind_ops, GUINT_TO_POINTER (cache_index)); + + p = buf; + encode_value (encoded_len, p, &p); + + acfg->unwind_info_offset += encoded_len + (p - buf); + return offset; + } +} + +static void +emit_exception_debug_info (MonoAotCompile *acfg, MonoCompile *cfg, gboolean store_seq_points) +{ + int i, k, buf_size; + guint32 debug_info_size, seq_points_size; + guint8 *code; + MonoMethodHeader *header; + guint8 *p, *buf, *debug_info; + MonoJitInfo *jinfo = cfg->jit_info; + guint32 flags; + gboolean use_unwind_ops = FALSE; + MonoSeqPointInfo *seq_points; + + code = cfg->native_code; + header = cfg->header; + + if (!acfg->aot_opts.nodebug) { + mono_debug_serialize_debug_info (cfg, &debug_info, &debug_info_size); + } else { + debug_info = NULL; + debug_info_size = 0; + } + + seq_points = cfg->seq_point_info; + seq_points_size = (store_seq_points)? mono_seq_point_info_get_write_size (seq_points) : 0; + + buf_size = header->num_clauses * 256 + debug_info_size + 2048 + seq_points_size + cfg->gc_map_size; + + p = buf = (guint8 *)g_malloc (buf_size); + + use_unwind_ops = cfg->unwind_ops != NULL; + + flags = (jinfo->has_generic_jit_info ? 1 : 0) | (use_unwind_ops ? 2 : 0) | (header->num_clauses ? 4 : 0) | (seq_points_size ? 8 : 0) | (cfg->compile_llvm ? 16 : 0) | (jinfo->has_try_block_holes ? 32 : 0) | (cfg->gc_map ? 64 : 0) | (jinfo->has_arch_eh_info ? 128 : 0); + + encode_value (flags, p, &p); + + if (use_unwind_ops) { + guint32 encoded_len; + guint8 *encoded; + guint32 unwind_desc; + + encoded = mono_unwind_ops_encode (cfg->unwind_ops, &encoded_len); + + unwind_desc = get_unwind_info_offset (acfg, encoded, encoded_len); + encode_value (unwind_desc, p, &p); + + g_free (encoded); + } else { + encode_value (jinfo->unwind_info, p, &p); + } + + /*Encode the number of holes before the number of clauses to make decoding easier*/ + if (jinfo->has_try_block_holes) { + MonoTryBlockHoleTableJitInfo *table = mono_jit_info_get_try_block_hole_table_info (jinfo); + encode_value (table->num_holes, p, &p); + } + + if (jinfo->has_arch_eh_info) { + /* + * In AOT mode, the code length is calculated from the address of the previous method, + * which could include alignment padding, so calculating the start of the epilog as + * code_len - epilog_size is correct any more. Save the real code len as a workaround. + */ + encode_value (jinfo->code_size, p, &p); + } + + /* Exception table */ + if (cfg->compile_llvm) { + /* + * When using LLVM, we can't emit some data, like pc offsets, this reg/offset etc., + * since the information is only available to llc. Instead, we let llc save the data + * into the LSDA, and read it from there at runtime. + */ + /* The assembly might be CIL stripped so emit the data ourselves */ + if (header->num_clauses) + encode_value (header->num_clauses, p, &p); + + for (k = 0; k < header->num_clauses; ++k) { + MonoExceptionClause *clause; + + clause = &header->clauses [k]; + + encode_value (clause->flags, p, &p); + if (!(clause->flags == MONO_EXCEPTION_CLAUSE_FILTER || clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY)) { + if (clause->data.catch_class) { + guint8 *buf2, *p2; + int len; + + buf2 = (guint8 *)g_malloc (4096); + p2 = buf2; + encode_klass_ref (acfg, clause->data.catch_class, p2, &p2); + len = p2 - buf2; + g_assert (len < 4096); + encode_value (len, p, &p); + memcpy (p, buf2, len); + p += p2 - buf2; + g_free (buf2); + } else { + encode_value (0, p, &p); + } + } + + /* Emit the IL ranges too, since they might not be available at runtime */ + encode_value (clause->try_offset, p, &p); + encode_value (clause->try_len, p, &p); + encode_value (clause->handler_offset, p, &p); + encode_value (clause->handler_len, p, &p); + + /* Emit a list of nesting clauses */ + for (i = 0; i < header->num_clauses; ++i) { + gint32 cindex1 = k; + MonoExceptionClause *clause1 = &header->clauses [cindex1]; + gint32 cindex2 = i; + MonoExceptionClause *clause2 = &header->clauses [cindex2]; + + if (cindex1 != cindex2 && clause1->try_offset >= clause2->try_offset && clause1->handler_offset <= clause2->handler_offset) + encode_value (i, p, &p); + } + encode_value (-1, p, &p); + } + } else { + if (jinfo->num_clauses) + encode_value (jinfo->num_clauses, p, &p); + + for (k = 0; k < jinfo->num_clauses; ++k) { + MonoJitExceptionInfo *ei = &jinfo->clauses [k]; + + encode_value (ei->flags, p, &p); +#ifdef MONO_CONTEXT_SET_LLVM_EXC_REG + /* Not used for catch clauses */ + if (ei->flags != MONO_EXCEPTION_CLAUSE_NONE) + encode_value (ei->exvar_offset, p, &p); +#else + encode_value (ei->exvar_offset, p, &p); +#endif + + if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER || ei->flags == MONO_EXCEPTION_CLAUSE_FINALLY) + encode_value ((gint)((guint8*)ei->data.filter - code), p, &p); + else { + if (ei->data.catch_class) { + guint8 *buf2, *p2; + int len; + + buf2 = (guint8 *)g_malloc (4096); + p2 = buf2; + encode_klass_ref (acfg, ei->data.catch_class, p2, &p2); + len = p2 - buf2; + g_assert (len < 4096); + encode_value (len, p, &p); + memcpy (p, buf2, len); + p += p2 - buf2; + g_free (buf2); + } else { + encode_value (0, p, &p); + } + } + + encode_value ((gint)((guint8*)ei->try_start - code), p, &p); + encode_value ((gint)((guint8*)ei->try_end - code), p, &p); + encode_value ((gint)((guint8*)ei->handler_start - code), p, &p); + } + } + + if (jinfo->has_try_block_holes) { + MonoTryBlockHoleTableJitInfo *table = mono_jit_info_get_try_block_hole_table_info (jinfo); + for (i = 0; i < table->num_holes; ++i) { + MonoTryBlockHoleJitInfo *hole = &table->holes [i]; + encode_value (hole->clause, p, &p); + encode_value (hole->length, p, &p); + encode_value (hole->offset, p, &p); + } + } + + if (jinfo->has_arch_eh_info) { + MonoArchEHJitInfo *eh_info; + + eh_info = mono_jit_info_get_arch_eh_info (jinfo); + encode_value (eh_info->stack_size, p, &p); + encode_value (eh_info->epilog_size, p, &p); + } + + if (jinfo->has_generic_jit_info) { + MonoGenericJitInfo *gi = mono_jit_info_get_generic_jit_info (jinfo); + MonoGenericSharingContext* gsctx = gi->generic_sharing_context; + guint8 *buf2, *p2; + int len; + + encode_value (gi->nlocs, p, &p); + if (gi->nlocs) { + for (i = 0; i < gi->nlocs; ++i) { + MonoDwarfLocListEntry *entry = &gi->locations [i]; + + encode_value (entry->is_reg ? 1 : 0, p, &p); + encode_value (entry->reg, p, &p); + if (!entry->is_reg) + encode_value (entry->offset, p, &p); + if (i == 0) + g_assert (entry->from == 0); + else + encode_value (entry->from, p, &p); + encode_value (entry->to, p, &p); + } + } else { + if (!cfg->compile_llvm) { + encode_value (gi->has_this ? 1 : 0, p, &p); + encode_value (gi->this_reg, p, &p); + encode_value (gi->this_offset, p, &p); + } + } + + /* + * Need to encode jinfo->method too, since it is not equal to 'method' + * when using generic sharing. + */ + buf2 = (guint8 *)g_malloc (4096); + p2 = buf2; + encode_method_ref (acfg, jinfo->d.method, p2, &p2); + len = p2 - buf2; + g_assert (len < 4096); + encode_value (len, p, &p); + memcpy (p, buf2, len); + p += p2 - buf2; + g_free (buf2); + + if (gsctx && gsctx->is_gsharedvt) { + encode_value (1, p, &p); + } else { + encode_value (0, p, &p); + } + } + + if (seq_points_size) + p += mono_seq_point_info_write (seq_points, p); + + g_assert (debug_info_size < buf_size); + + encode_value (debug_info_size, p, &p); + if (debug_info_size) { + memcpy (p, debug_info, debug_info_size); + p += debug_info_size; + g_free (debug_info); + } + + /* GC Map */ + if (cfg->gc_map) { + encode_value (cfg->gc_map_size, p, &p); + /* The GC map requires 4 bytes of alignment */ + while ((gsize)p % 4) + p ++; + memcpy (p, cfg->gc_map, cfg->gc_map_size); + p += cfg->gc_map_size; + } + + acfg->stats.ex_info_size += p - buf; + + g_assert (p - buf < buf_size); + + /* Emit info */ + /* The GC Map requires 4 byte alignment */ + cfg->ex_info_offset = add_to_blob_aligned (acfg, buf, p - buf, cfg->gc_map ? 4 : 1); + g_free (buf); +} + +static guint32 +emit_klass_info (MonoAotCompile *acfg, guint32 token) +{ + MonoError error; + MonoClass *klass = mono_class_get_checked (acfg->image, token, &error); + guint8 *p, *buf; + int i, buf_size, res; + gboolean no_special_static, cant_encode; + gpointer iter = NULL; + + if (!klass) { + mono_error_cleanup (&error); + + buf_size = 16; + + p = buf = (guint8 *)g_malloc (buf_size); + + /* Mark as unusable */ + encode_value (-1, p, &p); + + res = add_to_blob (acfg, buf, p - buf); + g_free (buf); + + return res; + } + + buf_size = 10240 + (klass->vtable_size * 16); + p = buf = (guint8 *)g_malloc (buf_size); + + g_assert (klass); + + mono_class_init (klass); + + mono_class_get_nested_types (klass, &iter); + g_assert (klass->nested_classes_inited); + + mono_class_setup_vtable (klass); + + /* + * Emit all the information which is required for creating vtables so + * the runtime does not need to create the MonoMethod structures which + * take up a lot of space. + */ + + no_special_static = !mono_class_has_special_static_fields (klass); + + /* Check whenever we have enough info to encode the vtable */ + cant_encode = FALSE; + for (i = 0; i < klass->vtable_size; ++i) { + MonoMethod *cm = klass->vtable [i]; + + if (cm && mono_method_signature (cm)->is_inflated && !g_hash_table_lookup (acfg->token_info_hash, cm)) + cant_encode = TRUE; + } + + mono_class_has_finalizer (klass); + if (mono_class_has_failure (klass)) + cant_encode = TRUE; + + if (mono_class_is_gtd (klass) || cant_encode) { + encode_value (-1, p, &p); + } else { + gboolean has_nested = mono_class_get_nested_classes_property (klass) != NULL; + encode_value (klass->vtable_size, p, &p); + encode_value ((klass->has_weak_fields << 9) | (mono_class_is_gtd (klass) ? (1 << 8) : 0) | (no_special_static << 7) | (klass->has_static_refs << 6) | (klass->has_references << 5) | ((klass->blittable << 4) | (has_nested ? 1 : 0) << 3) | (klass->has_cctor << 2) | (klass->has_finalize << 1) | klass->ghcimpl, p, &p); + if (klass->has_cctor) + encode_method_ref (acfg, mono_class_get_cctor (klass), p, &p); + if (klass->has_finalize) + encode_method_ref (acfg, mono_class_get_finalizer (klass), p, &p); + + encode_value (klass->instance_size, p, &p); + encode_value (mono_class_data_size (klass), p, &p); + encode_value (klass->packing_size, p, &p); + encode_value (klass->min_align, p, &p); + + for (i = 0; i < klass->vtable_size; ++i) { + MonoMethod *cm = klass->vtable [i]; + + if (cm) + encode_method_ref (acfg, cm, p, &p); + else + encode_value (0, p, &p); + } + } + + acfg->stats.class_info_size += p - buf; + + g_assert (p - buf < buf_size); + res = add_to_blob (acfg, buf, p - buf); + g_free (buf); + + return res; +} + +static char* +get_plt_entry_debug_sym (MonoAotCompile *acfg, MonoJumpInfo *ji, GHashTable *cache) +{ + char *debug_sym = NULL; + char *prefix; + + if (acfg->llvm && llvm_acfg->aot_opts.static_link) { + /* Need to add a prefix to create unique symbols */ + prefix = g_strdup_printf ("plt_%s_", acfg->assembly_name_sym); + } else { +#if defined(TARGET_WIN32) && defined(TARGET_X86) + prefix = mangle_symbol_alloc ("plt_"); +#else + prefix = g_strdup ("plt_"); +#endif + } + + switch (ji->type) { + case MONO_PATCH_INFO_METHOD: + debug_sym = get_debug_sym (ji->data.method, prefix, cache); + break; + case MONO_PATCH_INFO_INTERNAL_METHOD: + debug_sym = g_strdup_printf ("%s_jit_icall_%s", prefix, ji->data.name); + break; + case MONO_PATCH_INFO_RGCTX_FETCH: + debug_sym = g_strdup_printf ("%s_rgctx_fetch_%d", prefix, acfg->label_generator ++); + break; + case MONO_PATCH_INFO_ICALL_ADDR: + case MONO_PATCH_INFO_ICALL_ADDR_CALL: { + char *s = get_debug_sym (ji->data.method, "", cache); + + debug_sym = g_strdup_printf ("%s_icall_native_%s", prefix, s); + g_free (s); + break; + } + case MONO_PATCH_INFO_JIT_ICALL_ADDR: + debug_sym = g_strdup_printf ("%s_jit_icall_native_%s", prefix, ji->data.name); + break; + default: + break; + } + + g_free (prefix); + + return sanitize_symbol (acfg, debug_sym); +} + +/* + * Calls made from AOTed code are routed through a table of jumps similar to the + * ELF PLT (Program Linkage Table). Initially the PLT entries jump to code which transfers + * control to the AOT runtime through a trampoline. + */ +static void +emit_plt (MonoAotCompile *acfg) +{ + int i; + + if (acfg->aot_opts.llvm_only) { + g_assert (acfg->plt_offset == 1); + return; + } + + emit_line (acfg); + + emit_section_change (acfg, ".text", 0); + emit_alignment_code (acfg, 16); + emit_info_symbol (acfg, "plt"); + emit_label (acfg, acfg->plt_symbol); + + for (i = 0; i < acfg->plt_offset; ++i) { + char *debug_sym = NULL; + MonoPltEntry *plt_entry = NULL; + + if (i == 0) + /* + * The first plt entry is unused. + */ + continue; + + plt_entry = (MonoPltEntry *)g_hash_table_lookup (acfg->plt_offset_to_entry, GUINT_TO_POINTER (i)); + + debug_sym = plt_entry->debug_sym; + + if (acfg->thumb_mixed && !plt_entry->jit_used) + /* Emit only a thumb version */ + continue; + + /* Skip plt entries not actually called */ + if (!plt_entry->jit_used && !plt_entry->llvm_used) + continue; + + if (acfg->llvm && !acfg->thumb_mixed) { + emit_label (acfg, plt_entry->llvm_symbol); + if (acfg->llvm) { + emit_global_inner (acfg, plt_entry->llvm_symbol, TRUE); +#if defined(TARGET_MACH) + fprintf (acfg->fp, ".private_extern %s\n", plt_entry->llvm_symbol); +#endif + } + } + + if (debug_sym) { + if (acfg->need_no_dead_strip) { + emit_unset_mode (acfg); + fprintf (acfg->fp, " .no_dead_strip %s\n", debug_sym); + } + emit_local_symbol (acfg, debug_sym, NULL, TRUE); + emit_label (acfg, debug_sym); + } + + emit_label (acfg, plt_entry->symbol); + + arch_emit_plt_entry (acfg, acfg->got_symbol, (acfg->plt_got_offset_base + i) * sizeof (gpointer), acfg->plt_got_info_offsets [i]); + + if (debug_sym) + emit_symbol_size (acfg, debug_sym, "."); + } + + if (acfg->thumb_mixed) { + /* Make sure the ARM symbols don't alias the thumb ones */ + emit_zero_bytes (acfg, 16); + + /* + * Emit a separate set of PLT entries using thumb2 which is called by LLVM generated + * code. + */ + for (i = 0; i < acfg->plt_offset; ++i) { + char *debug_sym = NULL; + MonoPltEntry *plt_entry = NULL; + + if (i == 0) + continue; + + plt_entry = (MonoPltEntry *)g_hash_table_lookup (acfg->plt_offset_to_entry, GUINT_TO_POINTER (i)); + + /* Skip plt entries not actually called by LLVM code */ + if (!plt_entry->llvm_used) + continue; + + if (acfg->aot_opts.write_symbols) { + if (plt_entry->debug_sym) + debug_sym = g_strdup_printf ("%s_thumb", plt_entry->debug_sym); + } + + if (debug_sym) { +#if defined(TARGET_MACH) + fprintf (acfg->fp, " .thumb_func %s\n", debug_sym); + fprintf (acfg->fp, " .no_dead_strip %s\n", debug_sym); +#endif + emit_local_symbol (acfg, debug_sym, NULL, TRUE); + emit_label (acfg, debug_sym); + } + fprintf (acfg->fp, "\n.thumb_func\n"); + + emit_label (acfg, plt_entry->llvm_symbol); + + if (acfg->llvm) + emit_global_inner (acfg, plt_entry->llvm_symbol, TRUE); + + arch_emit_llvm_plt_entry (acfg, acfg->got_symbol, (acfg->plt_got_offset_base + i) * sizeof (gpointer), acfg->plt_got_info_offsets [i]); + + if (debug_sym) { + emit_symbol_size (acfg, debug_sym, "."); + g_free (debug_sym); + } + } + } + + emit_symbol_size (acfg, acfg->plt_symbol, "."); + + emit_info_symbol (acfg, "plt_end"); + + arch_emit_unwind_info_sections (acfg, "plt", "plt_end", NULL); +} + +/* + * emit_trampoline_full: + * + * If EMIT_TINFO is TRUE, emit additional information which can be used to create a MonoJitInfo for this trampoline by + * create_jit_info_for_trampoline (). + */ +static G_GNUC_UNUSED void +emit_trampoline_full (MonoAotCompile *acfg, int got_offset, MonoTrampInfo *info, gboolean emit_tinfo) +{ + char start_symbol [MAX_SYMBOL_SIZE]; + char end_symbol [MAX_SYMBOL_SIZE]; + char symbol [MAX_SYMBOL_SIZE]; + guint32 buf_size, info_offset; + MonoJumpInfo *patch_info; + guint8 *buf, *p; + GPtrArray *patches; + char *name; + guint8 *code; + guint32 code_size; + MonoJumpInfo *ji; + GSList *unwind_ops; + + g_assert (info); + + name = info->name; + code = info->code; + code_size = info->code_size; + ji = info->ji; + unwind_ops = info->unwind_ops; + + /* Emit code */ + + sprintf (start_symbol, "%s%s", acfg->user_symbol_prefix, name); + + emit_section_change (acfg, ".text", 0); + emit_global (acfg, start_symbol, TRUE); + emit_alignment_code (acfg, AOT_FUNC_ALIGNMENT); + emit_label (acfg, start_symbol); + + sprintf (symbol, "%snamed_%s", acfg->temp_prefix, name); + emit_label (acfg, symbol); + + /* + * The code should access everything through the GOT, so we pass + * TRUE here. + */ + emit_and_reloc_code (acfg, NULL, code, code_size, ji, TRUE, NULL); + + emit_symbol_size (acfg, start_symbol, "."); + + if (emit_tinfo) { + sprintf (end_symbol, "%snamede_%s", acfg->temp_prefix, name); + emit_label (acfg, end_symbol); + } + + /* Emit info */ + + /* Sort relocations */ + patches = g_ptr_array_new (); + for (patch_info = ji; patch_info; patch_info = patch_info->next) + if (patch_info->type != MONO_PATCH_INFO_NONE) + g_ptr_array_add (patches, patch_info); + g_ptr_array_sort (patches, compare_patches); + + buf_size = patches->len * 128 + 128; + buf = (guint8 *)g_malloc (buf_size); + p = buf; + + encode_patch_list (acfg, patches, patches->len, FALSE, got_offset, p, &p); + g_assert (p - buf < buf_size); + g_ptr_array_free (patches, TRUE); + + sprintf (symbol, "%s%s_p", acfg->user_symbol_prefix, name); + + info_offset = add_to_blob (acfg, buf, p - buf); + + emit_section_change (acfg, RODATA_SECT, 0); + emit_global (acfg, symbol, FALSE); + emit_label (acfg, symbol); + + emit_int32 (acfg, info_offset); + + if (emit_tinfo) { + guint8 *encoded; + guint32 encoded_len; + guint32 uw_offset; + + /* + * Emit additional information which can be used to reconstruct a partial MonoTrampInfo. + */ + encoded = mono_unwind_ops_encode (info->unwind_ops, &encoded_len); + uw_offset = get_unwind_info_offset (acfg, encoded, encoded_len); + g_free (encoded); + + emit_symbol_diff (acfg, end_symbol, start_symbol, 0); + emit_int32 (acfg, uw_offset); + } + + /* Emit debug info */ + if (unwind_ops) { + char symbol2 [MAX_SYMBOL_SIZE]; + + sprintf (symbol, "%s", name); + sprintf (symbol2, "%snamed_%s", acfg->temp_prefix, name); + + arch_emit_unwind_info_sections (acfg, start_symbol, end_symbol, unwind_ops); + + if (acfg->dwarf) + mono_dwarf_writer_emit_trampoline (acfg->dwarf, symbol, symbol2, NULL, NULL, code_size, unwind_ops); + } + + g_free (buf); +} + +static G_GNUC_UNUSED void +emit_trampoline (MonoAotCompile *acfg, int got_offset, MonoTrampInfo *info) +{ + emit_trampoline_full (acfg, got_offset, info, TRUE); +} + +static void +emit_trampolines (MonoAotCompile *acfg) +{ + char symbol [MAX_SYMBOL_SIZE]; + char end_symbol [MAX_SYMBOL_SIZE]; + int i, tramp_got_offset; + int ntype; +#ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES + int tramp_type; +#endif + + if (!mono_aot_mode_is_full (&acfg->aot_opts) || acfg->aot_opts.llvm_only) + return; + + g_assert (acfg->image->assembly); + + /* Currently, we emit most trampolines into the mscorlib AOT image. */ + if (strcmp (acfg->image->assembly->aname.name, "mscorlib") == 0) { +#ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES + MonoTrampInfo *info; + + /* + * Emit the generic trampolines. + * + * We could save some code by treating the generic trampolines as a wrapper + * method, but that approach has its own complexities, so we choose the simpler + * method. + */ + for (tramp_type = 0; tramp_type < MONO_TRAMPOLINE_NUM; ++tramp_type) { + /* we overload the boolean here to indicate the slightly different trampoline needed, see mono_arch_create_generic_trampoline() */ +#ifdef DISABLE_REMOTING + if (tramp_type == MONO_TRAMPOLINE_GENERIC_VIRTUAL_REMOTING) + continue; +#endif + mono_arch_create_generic_trampoline ((MonoTrampolineType)tramp_type, &info, acfg->aot_opts.use_trampolines_page? 2: TRUE); + emit_trampoline (acfg, acfg->got_offset, info); + mono_tramp_info_free (info); + } + + /* Emit the exception related code pieces */ + mono_arch_get_restore_context (&info, TRUE); + emit_trampoline (acfg, acfg->got_offset, info); + mono_tramp_info_free (info); + + mono_arch_get_call_filter (&info, TRUE); + emit_trampoline (acfg, acfg->got_offset, info); + mono_tramp_info_free (info); + + mono_arch_get_throw_exception (&info, TRUE); + emit_trampoline (acfg, acfg->got_offset, info); + mono_tramp_info_free (info); + + mono_arch_get_rethrow_exception (&info, TRUE); + emit_trampoline (acfg, acfg->got_offset, info); + mono_tramp_info_free (info); + + mono_arch_get_throw_corlib_exception (&info, TRUE); + emit_trampoline (acfg, acfg->got_offset, info); + mono_tramp_info_free (info); + +#ifdef MONO_ARCH_HAVE_SDB_TRAMPOLINES + mono_arch_create_sdb_trampoline (TRUE, &info, TRUE); + emit_trampoline (acfg, acfg->got_offset, info); + mono_tramp_info_free (info); + + mono_arch_create_sdb_trampoline (FALSE, &info, TRUE); + emit_trampoline (acfg, acfg->got_offset, info); + mono_tramp_info_free (info); +#endif + +#ifdef MONO_ARCH_GSHAREDVT_SUPPORTED + mono_arch_get_gsharedvt_trampoline (&info, TRUE); + if (info) { + emit_trampoline_full (acfg, acfg->got_offset, info, TRUE); + + /* Create a separate out trampoline for more information in stack traces */ + info->name = g_strdup ("gsharedvt_out_trampoline"); + emit_trampoline_full (acfg, acfg->got_offset, info, TRUE); + mono_tramp_info_free (info); + } +#endif + +#if defined(MONO_ARCH_HAVE_GET_TRAMPOLINES) + { + GSList *l = mono_arch_get_trampolines (TRUE); + + while (l) { + MonoTrampInfo *info = (MonoTrampInfo *)l->data; + + emit_trampoline (acfg, acfg->got_offset, info); + l = l->next; + } + } +#endif + + for (i = 0; i < acfg->aot_opts.nrgctx_fetch_trampolines; ++i) { + int offset; + + offset = MONO_RGCTX_SLOT_MAKE_RGCTX (i); + mono_arch_create_rgctx_lazy_fetch_trampoline (offset, &info, TRUE); + emit_trampoline (acfg, acfg->got_offset, info); + mono_tramp_info_free (info); + + offset = MONO_RGCTX_SLOT_MAKE_MRGCTX (i); + mono_arch_create_rgctx_lazy_fetch_trampoline (offset, &info, TRUE); + emit_trampoline (acfg, acfg->got_offset, info); + mono_tramp_info_free (info); + } + +#ifdef MONO_ARCH_HAVE_GENERAL_RGCTX_LAZY_FETCH_TRAMPOLINE + mono_arch_create_general_rgctx_lazy_fetch_trampoline (&info, TRUE); + emit_trampoline (acfg, acfg->got_offset, info); + mono_tramp_info_free (info); +#endif + + { + GSList *l; + + /* delegate_invoke_impl trampolines */ + l = mono_arch_get_delegate_invoke_impls (); + while (l) { + MonoTrampInfo *info = (MonoTrampInfo *)l->data; + + emit_trampoline (acfg, acfg->got_offset, info); + l = l->next; + } + } + + if (mono_aot_mode_is_interp (&acfg->aot_opts)) { + mono_arch_get_enter_icall_trampoline (&info); + emit_trampoline (acfg, acfg->got_offset, info); + } + +#endif /* #ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES */ + + /* Emit trampolines which are numerous */ + + /* + * These include the following: + * - specific trampolines + * - static rgctx invoke trampolines + * - imt trampolines + * These trampolines have the same code, they are parameterized by GOT + * slots. + * They are defined in this file, in the arch_... routines instead of + * in tramp-.c, since it is easier to do it this way. + */ + + /* + * When running in aot-only mode, we can't create specific trampolines at + * runtime, so we create a few, and save them in the AOT file. + * Normal trampolines embed their argument as a literal inside the + * trampoline code, we can't do that here, so instead we embed an offset + * which needs to be added to the trampoline address to get the address of + * the GOT slot which contains the argument value. + * The generated trampolines jump to the generic trampolines using another + * GOT slot, which will be setup by the AOT loader to point to the + * generic trampoline code of the given type. + */ + + /* + * FIXME: Maybe we should use more specific trampolines (i.e. one class init for + * each class). + */ + + emit_section_change (acfg, ".text", 0); + + tramp_got_offset = acfg->got_offset; + + for (ntype = 0; ntype < MONO_AOT_TRAMP_NUM; ++ntype) { + switch (ntype) { + case MONO_AOT_TRAMP_SPECIFIC: + sprintf (symbol, "specific_trampolines"); + break; + case MONO_AOT_TRAMP_STATIC_RGCTX: + sprintf (symbol, "static_rgctx_trampolines"); + break; + case MONO_AOT_TRAMP_IMT: + sprintf (symbol, "imt_trampolines"); + break; + case MONO_AOT_TRAMP_GSHAREDVT_ARG: + sprintf (symbol, "gsharedvt_arg_trampolines"); + break; + default: + g_assert_not_reached (); + } + + sprintf (end_symbol, "%s_e", symbol); + + if (acfg->aot_opts.write_symbols) + emit_local_symbol (acfg, symbol, end_symbol, TRUE); + + emit_alignment_code (acfg, AOT_FUNC_ALIGNMENT); + emit_info_symbol (acfg, symbol); + + acfg->trampoline_got_offset_base [ntype] = tramp_got_offset; + + for (i = 0; i < acfg->num_trampolines [ntype]; ++i) { + int tramp_size = 0; + + switch (ntype) { + case MONO_AOT_TRAMP_SPECIFIC: + arch_emit_specific_trampoline (acfg, tramp_got_offset, &tramp_size); + tramp_got_offset += 2; + break; + case MONO_AOT_TRAMP_STATIC_RGCTX: + arch_emit_static_rgctx_trampoline (acfg, tramp_got_offset, &tramp_size); + tramp_got_offset += 2; + break; + case MONO_AOT_TRAMP_IMT: + arch_emit_imt_trampoline (acfg, tramp_got_offset, &tramp_size); + tramp_got_offset += 1; + break; + case MONO_AOT_TRAMP_GSHAREDVT_ARG: + arch_emit_gsharedvt_arg_trampoline (acfg, tramp_got_offset, &tramp_size); + tramp_got_offset += 2; + break; + default: + g_assert_not_reached (); + } + if (!acfg->trampoline_size [ntype]) { + g_assert (tramp_size); + acfg->trampoline_size [ntype] = tramp_size; + } + } + + emit_label (acfg, end_symbol); + emit_int32 (acfg, 0); + } + + arch_emit_specific_trampoline_pages (acfg); + + /* Reserve some entries at the end of the GOT for our use */ + acfg->num_trampoline_got_entries = tramp_got_offset - acfg->got_offset; + } + + acfg->got_offset += acfg->num_trampoline_got_entries; +} + +static gboolean +str_begins_with (const char *str1, const char *str2) +{ + int len = strlen (str2); + return strncmp (str1, str2, len) == 0; +} + +void* +mono_aot_readonly_field_override (MonoClassField *field) +{ + ReadOnlyValue *rdv; + for (rdv = readonly_values; rdv; rdv = rdv->next) { + char *p = rdv->name; + int len; + len = strlen (field->parent->name_space); + if (strncmp (p, field->parent->name_space, len)) + continue; + p += len; + if (*p++ != '.') + continue; + len = strlen (field->parent->name); + if (strncmp (p, field->parent->name, len)) + continue; + p += len; + if (*p++ != '.') + continue; + if (strcmp (p, field->name)) + continue; + switch (rdv->type) { + case MONO_TYPE_I1: + return &rdv->value.i1; + case MONO_TYPE_I2: + return &rdv->value.i2; + case MONO_TYPE_I4: + return &rdv->value.i4; + default: + break; + } + } + return NULL; +} + +static void +add_readonly_value (MonoAotOptions *opts, const char *val) +{ + ReadOnlyValue *rdv; + const char *fval; + const char *tval; + /* the format of val is: + * namespace.typename.fieldname=type/value + * type can be i1 for uint8/int8/boolean, i2 for uint16/int16/char, i4 for uint32/int32 + */ + fval = strrchr (val, '/'); + if (!fval) { + fprintf (stderr, "AOT : invalid format for readonly field '%s', missing /.\n", val); + exit (1); + } + tval = strrchr (val, '='); + if (!tval) { + fprintf (stderr, "AOT : invalid format for readonly field '%s', missing =.\n", val); + exit (1); + } + rdv = g_new0 (ReadOnlyValue, 1); + rdv->name = (char *)g_malloc0 (tval - val + 1); + memcpy (rdv->name, val, tval - val); + tval++; + fval++; + if (strncmp (tval, "i1", 2) == 0) { + rdv->value.i1 = atoi (fval); + rdv->type = MONO_TYPE_I1; + } else if (strncmp (tval, "i2", 2) == 0) { + rdv->value.i2 = atoi (fval); + rdv->type = MONO_TYPE_I2; + } else if (strncmp (tval, "i4", 2) == 0) { + rdv->value.i4 = atoi (fval); + rdv->type = MONO_TYPE_I4; + } else { + fprintf (stderr, "AOT : unsupported type for readonly field '%s'.\n", tval); + exit (1); + } + rdv->next = readonly_values; + readonly_values = rdv; +} + +static gchar * +clean_path (gchar * path) +{ + if (!path) + return NULL; + + if (g_str_has_suffix (path, G_DIR_SEPARATOR_S)) + return path; + + gchar *clean = g_strconcat (path, G_DIR_SEPARATOR_S, NULL); + g_free (path); + + return clean; +} + +static gchar * +wrap_path (gchar * path) +{ + int len; + if (!path) + return NULL; + + // If the string contains no spaces, just return the original string. + if (strstr (path, " ") == NULL) + return path; + + // If the string is already wrapped in quotes, return it. + len = strlen (path); + if (len >= 2 && path[0] == '\"' && path[len-1] == '\"') + return path; + + // If the string contains spaces, then wrap it in quotes. + gchar *clean = g_strdup_printf ("\"%s\"", path); + + return clean; +} + +// Duplicate a char range and add it to a ptrarray, but only if it is nonempty +static void +ptr_array_add_range_if_nonempty(GPtrArray *args, gchar const *start, gchar const *end) +{ + ptrdiff_t len = end-start; + if (len > 0) + g_ptr_array_add (args, g_strndup (start, len)); +} + +static GPtrArray * +mono_aot_split_options (const char *aot_options) +{ + enum MonoAotOptionState { + MONO_AOT_OPTION_STATE_DEFAULT, + MONO_AOT_OPTION_STATE_STRING, + MONO_AOT_OPTION_STATE_ESCAPE, + }; + + GPtrArray *args = g_ptr_array_new (); + enum MonoAotOptionState state = MONO_AOT_OPTION_STATE_DEFAULT; + gchar const *opt_start = aot_options; + gboolean end_of_string = FALSE; + gchar cur; + + g_return_val_if_fail (aot_options != NULL, NULL); + + while ((cur = *aot_options) != '\0') { + if (state == MONO_AOT_OPTION_STATE_ESCAPE) + goto next; + + switch (cur) { + case '"': + // If we find a quote, then if we're in the default case then + // it means we've found the start of a string, if not then it + // means we've found the end of the string and should switch + // back to the default case. + switch (state) { + case MONO_AOT_OPTION_STATE_DEFAULT: + state = MONO_AOT_OPTION_STATE_STRING; + break; + case MONO_AOT_OPTION_STATE_STRING: + state = MONO_AOT_OPTION_STATE_DEFAULT; + break; + case MONO_AOT_OPTION_STATE_ESCAPE: + g_assert_not_reached (); + break; + } + break; + case '\\': + // If we've found an escaping operator, then this means we + // should not process the next character if inside a string. + if (state == MONO_AOT_OPTION_STATE_STRING) + state = MONO_AOT_OPTION_STATE_ESCAPE; + break; + case ',': + // If we're in the default state then this means we've found + // an option, store it for later processing. + if (state == MONO_AOT_OPTION_STATE_DEFAULT) + goto new_opt; + break; + } + + next: + aot_options++; + restart: + // If the next character is end of string, then process the last option. + if (*(aot_options) == '\0') { + end_of_string = TRUE; + goto new_opt; + } + continue; + + new_opt: + ptr_array_add_range_if_nonempty (args, opt_start, aot_options); + opt_start = ++aot_options; + if (end_of_string) + break; + goto restart; // Check for null and continue loop + } + + return args; +} + +static void +mono_aot_parse_options (const char *aot_options, MonoAotOptions *opts) +{ + GPtrArray* args; + + args = mono_aot_split_options (aot_options ? aot_options : ""); + for (int i = 0; i < args->len; ++i) { + const char *arg = (const char *)g_ptr_array_index (args, i); + + if (str_begins_with (arg, "outfile=")) { + opts->outfile = g_strdup (arg + strlen ("outfile=")); + } else if (str_begins_with (arg, "llvm-outfile=")) { + opts->llvm_outfile = g_strdup (arg + strlen ("llvm-outfile=")); + } else if (str_begins_with (arg, "temp-path=")) { + opts->temp_path = clean_path (g_strdup (arg + strlen ("temp-path="))); + } else if (str_begins_with (arg, "save-temps")) { + opts->save_temps = TRUE; + } else if (str_begins_with (arg, "keep-temps")) { + opts->save_temps = TRUE; + } else if (str_begins_with (arg, "write-symbols")) { + opts->write_symbols = TRUE; + } else if (str_begins_with (arg, "no-write-symbols")) { + opts->write_symbols = FALSE; + // Intentionally undocumented -- one-off experiment + } else if (str_begins_with (arg, "metadata-only")) { + opts->metadata_only = TRUE; + } else if (str_begins_with (arg, "bind-to-runtime-version")) { + opts->bind_to_runtime_version = TRUE; + } else if (str_begins_with (arg, "full")) { + opts->mode = MONO_AOT_MODE_FULL; + } else if (str_begins_with (arg, "hybrid")) { + opts->mode = MONO_AOT_MODE_HYBRID; + } else if (str_begins_with (arg, "interp")) { + opts->mode = MONO_AOT_MODE_INTERP; + } else if (str_begins_with (arg, "threads=")) { + opts->nthreads = atoi (arg + strlen ("threads=")); + } else if (str_begins_with (arg, "static")) { + opts->static_link = TRUE; + opts->no_dlsym = TRUE; + } else if (str_begins_with (arg, "asmonly")) { + opts->asm_only = TRUE; + } else if (str_begins_with (arg, "asmwriter")) { + opts->asm_writer = TRUE; + } else if (str_begins_with (arg, "nodebug")) { + opts->nodebug = TRUE; + } else if (str_begins_with (arg, "dwarfdebug")) { + opts->dwarf_debug = TRUE; + // Intentionally undocumented -- No one remembers what this does. It appears to be ARM-only + } else if (str_begins_with (arg, "nopagetrampolines")) { + opts->use_trampolines_page = FALSE; + } else if (str_begins_with (arg, "ntrampolines=")) { + opts->ntrampolines = atoi (arg + strlen ("ntrampolines=")); + } else if (str_begins_with (arg, "nrgctx-trampolines=")) { + opts->nrgctx_trampolines = atoi (arg + strlen ("nrgctx-trampolines=")); + } else if (str_begins_with (arg, "nrgctx-fetch-trampolines=")) { + opts->nrgctx_fetch_trampolines = atoi (arg + strlen ("nrgctx-fetch-trampolines=")); + } else if (str_begins_with (arg, "nimt-trampolines=")) { + opts->nimt_trampolines = atoi (arg + strlen ("nimt-trampolines=")); + } else if (str_begins_with (arg, "ngsharedvt-trampolines=")) { + opts->ngsharedvt_arg_trampolines = atoi (arg + strlen ("ngsharedvt-trampolines=")); + } else if (str_begins_with (arg, "tool-prefix=")) { + opts->tool_prefix = g_strdup (arg + strlen ("tool-prefix=")); + } else if (str_begins_with (arg, "ld-flags=")) { + opts->ld_flags = g_strdup (arg + strlen ("ld-flags=")); + } else if (str_begins_with (arg, "soft-debug")) { + opts->soft_debug = TRUE; + // Intentionally undocumented x2-- deprecated + } else if (str_begins_with (arg, "gen-seq-points-file=")) { + fprintf (stderr, "Mono Warning: aot option gen-seq-points-file= is deprecated.\n"); + } else if (str_begins_with (arg, "gen-seq-points-file")) { + fprintf (stderr, "Mono Warning: aot option gen-seq-points-file is deprecated.\n"); + } else if (str_begins_with (arg, "msym-dir=")) { + debug_options.no_seq_points_compact_data = FALSE; + opts->gen_msym_dir = TRUE; + opts->gen_msym_dir_path = g_strdup (arg + strlen ("msym_dir="));; + } else if (str_begins_with (arg, "direct-pinvoke")) { + opts->direct_pinvoke = TRUE; + } else if (str_begins_with (arg, "direct-icalls")) { + opts->direct_icalls = TRUE; + } else if (str_begins_with (arg, "no-direct-calls")) { + opts->no_direct_calls = TRUE; + } else if (str_begins_with (arg, "print-skipped")) { + opts->print_skipped_methods = TRUE; + } else if (str_begins_with (arg, "stats")) { + opts->stats = TRUE; + // Intentionally undocumented-- has no known function other than to debug the compiler + } else if (str_begins_with (arg, "no-instances")) { + opts->no_instances = TRUE; + // Intentionally undocumented x4-- Used for internal debugging of compiler + } else if (str_begins_with (arg, "log-generics")) { + opts->log_generics = TRUE; + } else if (str_begins_with (arg, "log-instances=")) { + opts->log_instances = TRUE; + opts->instances_logfile_path = g_strdup (arg + strlen ("log-instances=")); + } else if (str_begins_with (arg, "log-instances")) { + opts->log_instances = TRUE; + } else if (str_begins_with (arg, "internal-logfile=")) { + opts->logfile = g_strdup (arg + strlen ("internal-logfile=")); + } else if (str_begins_with (arg, "dedup-skip")) { + opts->dedup = TRUE; + } else if (str_begins_with (arg, "dedup-include=")) { + opts->dedup_include = g_strdup (arg + strlen ("dedup-include=")); + } else if (str_begins_with (arg, "mtriple=")) { + opts->mtriple = g_strdup (arg + strlen ("mtriple=")); + } else if (str_begins_with (arg, "llvm-path=")) { + opts->llvm_path = clean_path (g_strdup (arg + strlen ("llvm-path="))); + } else if (!strcmp (arg, "llvm")) { + opts->llvm = TRUE; + } else if (str_begins_with (arg, "readonly-value=")) { + add_readonly_value (opts, arg + strlen ("readonly-value=")); + } else if (str_begins_with (arg, "info")) { + printf ("AOT target setup: %s.\n", AOT_TARGET_STR); + exit (0); + // Intentionally undocumented: Used for precise stack maps, which are not available yet + } else if (str_begins_with (arg, "gc-maps")) { + mini_gc_enable_gc_maps_for_aot (); + // Intentionally undocumented: Used for internal debugging + } else if (str_begins_with (arg, "dump")) { + opts->dump_json = TRUE; + } else if (str_begins_with (arg, "llvmonly")) { + opts->mode = MONO_AOT_MODE_FULL; + opts->llvm = TRUE; + opts->llvm_only = TRUE; + } else if (str_begins_with (arg, "data-outfile=")) { + opts->data_outfile = g_strdup (arg + strlen ("data-outfile=")); + } else if (str_begins_with (arg, "profile=")) { + opts->profile_files = g_list_append (opts->profile_files, g_strdup (arg + strlen ("profile="))); + } else if (!strcmp (arg, "profile-only")) { + opts->profile_only = TRUE; + } else if (!strcmp (arg, "verbose")) { + opts->verbose = TRUE; + } else if (str_begins_with (arg, "help") || str_begins_with (arg, "?")) { + printf ("Supported options for --aot:\n"); + printf (" asmonly\n"); + printf (" bind-to-runtime-version\n"); + printf (" bitcode\n"); + printf (" data-outfile=\n"); + printf (" direct-icalls\n"); + printf (" direct-pinvoke\n"); + printf (" dwarfdebug\n"); + printf (" full\n"); + printf (" hybrid\n"); + printf (" info\n"); + printf (" keep-temps\n"); + printf (" llvm\n"); + printf (" llvmonly\n"); + printf (" llvm-outfile=\n"); + printf (" llvm-path=\n"); + printf (" msym-dir=\n"); + printf (" mtriple\n"); + printf (" nimt-trampolines=\n"); + printf (" nodebug\n"); + printf (" no-direct-calls\n"); + printf (" no-write-symbols\n"); + printf (" nrgctx-trampolines=\n"); + printf (" nrgctx-fetch-trampolines=\n"); + printf (" ngsharedvt-trampolines=\n"); + printf (" ntrampolines=\n"); + printf (" outfile=\n"); + printf (" profile=\n"); + printf (" profile-only\n"); + printf (" print-skipped-methods\n"); + printf (" readonly-value=\n"); + printf (" save-temps\n"); + printf (" soft-debug\n"); + printf (" static\n"); + printf (" stats\n"); + printf (" temp-path=\n"); + printf (" tool-prefix=\n"); + printf (" threads=\n"); + printf (" write-symbols\n"); + printf (" verbose\n"); + printf (" help/?\n"); + exit (0); + } else { + fprintf (stderr, "AOT : Unknown argument '%s'.\n", arg); + exit (1); + } + + g_free ((gpointer) arg); + } + + if (opts->use_trampolines_page) { + opts->ntrampolines = 0; + opts->nrgctx_trampolines = 0; + opts->nimt_trampolines = 0; + opts->ngsharedvt_arg_trampolines = 0; + } + + g_ptr_array_free (args, /*free_seg=*/TRUE); +} + +static void +add_token_info_hash (gpointer key, gpointer value, gpointer user_data) +{ + MonoMethod *method = (MonoMethod*)key; + MonoJumpInfoToken *ji = (MonoJumpInfoToken*)value; + MonoAotCompile *acfg = (MonoAotCompile *)user_data; + MonoJumpInfoToken *new_ji; + + new_ji = (MonoJumpInfoToken *)mono_mempool_alloc0 (acfg->mempool, sizeof (MonoJumpInfoToken)); + new_ji->image = ji->image; + new_ji->token = ji->token; + g_hash_table_insert (acfg->token_info_hash, method, new_ji); +} + +static gboolean +can_encode_class (MonoAotCompile *acfg, MonoClass *klass) +{ + if (klass->type_token) + return TRUE; + if ((klass->byval_arg.type == MONO_TYPE_VAR) || (klass->byval_arg.type == MONO_TYPE_MVAR) || (klass->byval_arg.type == MONO_TYPE_PTR)) + return TRUE; + if (klass->rank) + return can_encode_class (acfg, klass->element_class); + return FALSE; +} + +static gboolean +can_encode_method (MonoAotCompile *acfg, MonoMethod *method) +{ + if (method->wrapper_type) { + switch (method->wrapper_type) { + case MONO_WRAPPER_NONE: + case MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK: + case MONO_WRAPPER_XDOMAIN_INVOKE: + case MONO_WRAPPER_STFLD: + case MONO_WRAPPER_LDFLD: + case MONO_WRAPPER_LDFLDA: + case MONO_WRAPPER_STELEMREF: + case MONO_WRAPPER_PROXY_ISINST: + case MONO_WRAPPER_ALLOC: + case MONO_WRAPPER_REMOTING_INVOKE: + case MONO_WRAPPER_UNKNOWN: + case MONO_WRAPPER_WRITE_BARRIER: + case MONO_WRAPPER_DELEGATE_INVOKE: + case MONO_WRAPPER_DELEGATE_BEGIN_INVOKE: + case MONO_WRAPPER_DELEGATE_END_INVOKE: + case MONO_WRAPPER_SYNCHRONIZED: + break; + case MONO_WRAPPER_MANAGED_TO_MANAGED: + case MONO_WRAPPER_CASTCLASS: { + WrapperInfo *info = mono_marshal_get_wrapper_info (method); + + if (info) + return TRUE; + else + return FALSE; + break; + } + default: + //printf ("Skip (wrapper call): %d -> %s\n", patch_info->type, mono_method_full_name (patch_info->data.method, TRUE)); + return FALSE; + } + } else { + if (!method->token) { + /* The method is part of a constructed type like Int[,].Set (). */ + if (!g_hash_table_lookup (acfg->token_info_hash, method)) { + if (method->klass->rank) + return TRUE; + return FALSE; + } + } + } + return TRUE; +} + +static gboolean +can_encode_patch (MonoAotCompile *acfg, MonoJumpInfo *patch_info) +{ + switch (patch_info->type) { + case MONO_PATCH_INFO_METHOD: + case MONO_PATCH_INFO_METHODCONST: + case MONO_PATCH_INFO_METHOD_CODE_SLOT: { + MonoMethod *method = patch_info->data.method; + + return can_encode_method (acfg, method); + } + case MONO_PATCH_INFO_VTABLE: + case MONO_PATCH_INFO_CLASS: + case MONO_PATCH_INFO_IID: + case MONO_PATCH_INFO_ADJUSTED_IID: + if (!can_encode_class (acfg, patch_info->data.klass)) { + //printf ("Skip: %s\n", mono_type_full_name (&patch_info->data.klass->byval_arg)); + return FALSE; + } + break; + case MONO_PATCH_INFO_DELEGATE_TRAMPOLINE: { + if (!can_encode_class (acfg, patch_info->data.del_tramp->klass)) { + //printf ("Skip: %s\n", mono_type_full_name (&patch_info->data.klass->byval_arg)); + return FALSE; + } + break; + } + case MONO_PATCH_INFO_RGCTX_FETCH: + case MONO_PATCH_INFO_RGCTX_SLOT_INDEX: { + MonoJumpInfoRgctxEntry *entry = patch_info->data.rgctx_entry; + + if (!can_encode_method (acfg, entry->method)) + return FALSE; + if (!can_encode_patch (acfg, entry->data)) + return FALSE; + break; + } + default: + break; + } + + return TRUE; +} + +static gboolean +is_concrete_type (MonoType *t) +{ + MonoClass *klass; + int i; + + if (t->type == MONO_TYPE_VAR || t->type == MONO_TYPE_MVAR) + return FALSE; + if (t->type == MONO_TYPE_GENERICINST) { + MonoGenericContext *orig_ctx; + MonoGenericInst *inst; + MonoType *arg; + + if (!MONO_TYPE_ISSTRUCT (t)) + return TRUE; + klass = mono_class_from_mono_type (t); + orig_ctx = &mono_class_get_generic_class (klass)->context; + + inst = orig_ctx->class_inst; + if (inst) { + for (i = 0; i < inst->type_argc; ++i) { + arg = mini_get_underlying_type (inst->type_argv [i]); + if (!is_concrete_type (arg)) + return FALSE; + } + } + inst = orig_ctx->method_inst; + if (inst) { + for (i = 0; i < inst->type_argc; ++i) { + arg = mini_get_underlying_type (inst->type_argv [i]); + if (!is_concrete_type (arg)) + return FALSE; + } + } + } + return TRUE; +} + +/* LOCKING: Assumes the loader lock is held */ +static void +add_gsharedvt_wrappers (MonoAotCompile *acfg, MonoMethodSignature *sig, gboolean gsharedvt_in, gboolean gsharedvt_out) +{ + MonoMethod *wrapper; + gboolean concrete = TRUE; + gboolean add_in = gsharedvt_in; + gboolean add_out = gsharedvt_out; + + if (gsharedvt_in && g_hash_table_lookup (acfg->gsharedvt_in_signatures, sig)) + add_in = FALSE; + if (gsharedvt_out && g_hash_table_lookup (acfg->gsharedvt_out_signatures, sig)) + add_out = FALSE; + + if (!add_in && !add_out) + return; + + if (mini_is_gsharedvt_variable_signature (sig)) + return; + + if (add_in) + g_hash_table_insert (acfg->gsharedvt_in_signatures, sig, sig); + if (add_out) + g_hash_table_insert (acfg->gsharedvt_out_signatures, sig, sig); + + if (!sig->has_type_parameters) { + //printf ("%s\n", mono_signature_full_name (sig)); + + if (gsharedvt_in) { + wrapper = mini_get_gsharedvt_in_sig_wrapper (sig); + add_extra_method (acfg, wrapper); + } + if (gsharedvt_out) { + wrapper = mini_get_gsharedvt_out_sig_wrapper (sig); + add_extra_method (acfg, wrapper); + } + } else { + /* For signatures creared during generic sharing, convert them to a concrete signature if possible */ + MonoMethodSignature *copy = mono_metadata_signature_dup (sig); + int i; + + //printf ("%s\n", mono_signature_full_name (sig)); + + copy->ret = mini_get_underlying_type (sig->ret); + if (!is_concrete_type (copy->ret)) + concrete = FALSE; + for (i = 0; i < sig->param_count; ++i) { + copy->params [i] = mini_get_underlying_type (sig->params [i]); + if (!is_concrete_type (copy->params [i])) + concrete = FALSE; + } + if (concrete) { + copy->has_type_parameters = 0; + + if (gsharedvt_in) { + wrapper = mini_get_gsharedvt_in_sig_wrapper (copy); + add_extra_method (acfg, wrapper); + } + + if (gsharedvt_out) { + wrapper = mini_get_gsharedvt_out_sig_wrapper (copy); + add_extra_method (acfg, wrapper); + } + + //printf ("%s\n", mono_method_full_name (wrapper, 1)); + } + } +} + +/* + * compile_method: + * + * AOT compile a given method. + * This function might be called by multiple threads, so it must be thread-safe. + */ +static void +compile_method (MonoAotCompile *acfg, MonoMethod *method) +{ + MonoCompile *cfg; + MonoJumpInfo *patch_info; + gboolean skip; + int index, depth; + MonoMethod *wrapped; + GTimer *jit_timer; + JitFlags flags; + + if (acfg->aot_opts.metadata_only) + return; + + mono_acfg_lock (acfg); + index = get_method_index (acfg, method); + mono_acfg_unlock (acfg); + + /* fixme: maybe we can also precompile wrapper methods */ + if ((method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) || + (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) || + (method->flags & METHOD_ATTRIBUTE_ABSTRACT)) { + //printf ("Skip (impossible): %s\n", mono_method_full_name (method, TRUE)); + return; + } + + if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) + return; + + wrapped = mono_marshal_method_from_wrapper (method); + if (wrapped && (wrapped->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) && wrapped->is_generic) + // FIXME: The wrapper should be generic too, but it is not + return; + + if (method->wrapper_type == MONO_WRAPPER_COMINTEROP) + return; + + if (acfg->aot_opts.profile_only && !method->is_inflated && !g_hash_table_lookup (acfg->profile_methods, method)) + return; + + mono_atomic_inc_i32 (&acfg->stats.mcount); + +#if 0 + if (method->is_generic || mono_class_is_gtd (method->klass)) { + mono_atomic_inc_i32 (&acfg->stats.genericcount); + return; + } +#endif + + //acfg->aot_opts.print_skipped_methods = TRUE; + + /* + * Since these methods are the only ones which are compiled with + * AOT support, and they are not used by runtime startup/shutdown code, + * the runtime will not see AOT methods during AOT compilation,so it + * does not need to support them by creating a fake GOT etc. + */ + flags = JIT_FLAG_AOT; + if (mono_aot_mode_is_full (&acfg->aot_opts)) + flags = (JitFlags)(flags | JIT_FLAG_FULL_AOT); + if (acfg->llvm) + flags = (JitFlags)(flags | JIT_FLAG_LLVM); + if (acfg->aot_opts.llvm_only) + flags = (JitFlags)(flags | JIT_FLAG_LLVM_ONLY | JIT_FLAG_EXPLICIT_NULL_CHECKS); + if (acfg->aot_opts.no_direct_calls) + flags = (JitFlags)(flags | JIT_FLAG_NO_DIRECT_ICALLS); + if (acfg->aot_opts.direct_pinvoke) + flags = (JitFlags)(flags | JIT_FLAG_DIRECT_PINVOKE); + + jit_timer = mono_time_track_start (); + cfg = mini_method_compile (method, acfg->opts, mono_get_root_domain (), flags, 0, index); + mono_time_track_end (&mono_jit_stats.jit_time, jit_timer); + + if (cfg->exception_type == MONO_EXCEPTION_GENERIC_SHARING_FAILED) { + if (acfg->aot_opts.print_skipped_methods) + printf ("Skip (gshared failure): %s (%s)\n", mono_method_get_full_name (method), cfg->exception_message); + mono_atomic_inc_i32 (&acfg->stats.genericcount); + return; + } + if (cfg->exception_type != MONO_EXCEPTION_NONE) { + /* Some instances cannot be JITted due to constraints etc. */ + if (!method->is_inflated) + report_loader_error (acfg, &cfg->error, FALSE, "Unable to compile method '%s' due to: '%s'.\n", mono_method_get_full_name (method), mono_error_get_message (&cfg->error)); + /* Let the exception happen at runtime */ + return; + } + + if (cfg->disable_aot) { + if (acfg->aot_opts.print_skipped_methods) + printf ("Skip (disabled): %s\n", mono_method_get_full_name (method)); + mono_atomic_inc_i32 (&acfg->stats.ocount); + return; + } + cfg->method_index = index; + + /* Nullify patches which need no aot processing */ + for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) { + switch (patch_info->type) { + case MONO_PATCH_INFO_LABEL: + case MONO_PATCH_INFO_BB: + patch_info->type = MONO_PATCH_INFO_NONE; + break; + default: + break; + } + } + + /* Collect method->token associations from the cfg */ + mono_acfg_lock (acfg); + g_hash_table_foreach (cfg->token_info_hash, add_token_info_hash, acfg); + mono_acfg_unlock (acfg); + g_hash_table_destroy (cfg->token_info_hash); + cfg->token_info_hash = NULL; + + /* + * Check for absolute addresses. + */ + skip = FALSE; + for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) { + switch (patch_info->type) { + case MONO_PATCH_INFO_ABS: + /* unable to handle this */ + skip = TRUE; + break; + default: + break; + } + } + + if (skip) { + if (acfg->aot_opts.print_skipped_methods) + printf ("Skip (abs call): %s\n", mono_method_get_full_name (method)); + mono_atomic_inc_i32 (&acfg->stats.abscount); + return; + } + + /* Lock for the rest of the code */ + mono_acfg_lock (acfg); + + if (cfg->gsharedvt) + acfg->stats.method_categories [METHOD_CAT_GSHAREDVT] ++; + else if (cfg->gshared) + acfg->stats.method_categories [METHOD_CAT_INST] ++; + else if (cfg->method->wrapper_type) + acfg->stats.method_categories [METHOD_CAT_WRAPPER] ++; + else + acfg->stats.method_categories [METHOD_CAT_NORMAL] ++; + + /* + * Check for methods/klasses we can't encode. + */ + skip = FALSE; + for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) { + if (!can_encode_patch (acfg, patch_info)) + skip = TRUE; + } + + if (skip) { + if (acfg->aot_opts.print_skipped_methods) + printf ("Skip (patches): %s\n", mono_method_get_full_name (method)); + acfg->stats.ocount++; + mono_acfg_unlock (acfg); + return; + } + + if (!cfg->compile_llvm) + acfg->has_jitted_code = TRUE; + + if (method->is_inflated && acfg->aot_opts.log_instances) { + if (acfg->instances_logfile) + fprintf (acfg->instances_logfile, "%s ### %d\n", mono_method_get_full_name (method), cfg->code_size); + else + printf ("%s ### %d\n", mono_method_get_full_name (method), cfg->code_size); + } + + /* Adds generic instances referenced by this method */ + /* + * The depth is used to avoid infinite loops when generic virtual recursion is + * encountered. + */ + depth = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->method_depth, method)); + if (!acfg->aot_opts.no_instances && depth < 32 && mono_aot_mode_is_full (&acfg->aot_opts)) { + for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) { + switch (patch_info->type) { + case MONO_PATCH_INFO_RGCTX_FETCH: + case MONO_PATCH_INFO_RGCTX_SLOT_INDEX: + case MONO_PATCH_INFO_METHOD: + case MONO_PATCH_INFO_METHOD_RGCTX: { + MonoMethod *m = NULL; + + if (patch_info->type == MONO_PATCH_INFO_RGCTX_FETCH || patch_info->type == MONO_PATCH_INFO_RGCTX_SLOT_INDEX) { + MonoJumpInfoRgctxEntry *e = patch_info->data.rgctx_entry; + + if (e->info_type == MONO_RGCTX_INFO_GENERIC_METHOD_CODE) + m = e->data->data.method; + } else { + m = patch_info->data.method; + } + + if (!m) + break; + if (m->is_inflated && mono_aot_mode_is_full (&acfg->aot_opts)) { + if (!(mono_class_generic_sharing_enabled (m->klass) && + mono_method_is_generic_sharable_full (m, FALSE, FALSE, FALSE)) && + (!method_has_type_vars (m) || mono_method_is_generic_sharable_full (m, TRUE, TRUE, FALSE))) { + if (m->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) { + if (mono_aot_mode_is_full (&acfg->aot_opts) && !method_has_type_vars (m)) + add_extra_method_with_depth (acfg, mono_marshal_get_native_wrapper (m, TRUE, TRUE), depth + 1); + } else { + add_extra_method_with_depth (acfg, m, depth + 1); + add_types_from_method_header (acfg, m); + } + } + add_generic_class_with_depth (acfg, m->klass, depth + 5, "method"); + } + if (m->wrapper_type == MONO_WRAPPER_MANAGED_TO_MANAGED) { + WrapperInfo *info = mono_marshal_get_wrapper_info (m); + + if (info && info->subtype == WRAPPER_SUBTYPE_ELEMENT_ADDR) + add_extra_method_with_depth (acfg, m, depth + 1); + } + break; + } + case MONO_PATCH_INFO_VTABLE: { + MonoClass *klass = patch_info->data.klass; + + if (mono_class_is_ginst (klass) && !mini_class_is_generic_sharable (klass)) + add_generic_class_with_depth (acfg, klass, depth + 5, "vtable"); + break; + } + case MONO_PATCH_INFO_SFLDA: { + MonoClass *klass = patch_info->data.field->parent; + + /* The .cctor needs to run at runtime. */ + if (mono_class_is_ginst (klass) && !mono_generic_context_is_sharable_full (&mono_class_get_generic_class (klass)->context, FALSE, FALSE) && mono_class_get_cctor (klass)) + add_extra_method_with_depth (acfg, mono_class_get_cctor (klass), depth + 1); + break; + } + default: + break; + } + } + } + + /* Determine whenever the method has GOT slots */ + for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) { + switch (patch_info->type) { + case MONO_PATCH_INFO_GOT_OFFSET: + case MONO_PATCH_INFO_NONE: + case MONO_PATCH_INFO_GC_CARD_TABLE_ADDR: + case MONO_PATCH_INFO_GC_NURSERY_START: + case MONO_PATCH_INFO_GC_NURSERY_BITS: + break; + case MONO_PATCH_INFO_IMAGE: + /* The assembly is stored in GOT slot 0 */ + if (patch_info->data.image != acfg->image) + cfg->has_got_slots = TRUE; + break; + default: + if (!is_plt_patch (patch_info) || (cfg->compile_llvm && acfg->aot_opts.llvm_only)) + cfg->has_got_slots = TRUE; + break; + } + } + + if (!cfg->has_got_slots) + mono_atomic_inc_i32 (&acfg->stats.methods_without_got_slots); + + /* Add gsharedvt wrappers for signatures used by the method */ + if (acfg->aot_opts.llvm_only) { + GSList *l; + + if (!cfg->method->wrapper_type || cfg->method->wrapper_type == MONO_WRAPPER_DELEGATE_INVOKE) + /* These only need out wrappers */ + add_gsharedvt_wrappers (acfg, mono_method_signature (cfg->method), FALSE, TRUE); + + for (l = cfg->signatures; l; l = l->next) { + MonoMethodSignature *sig = mono_metadata_signature_dup ((MonoMethodSignature*)l->data); + + /* These only need in wrappers */ + add_gsharedvt_wrappers (acfg, sig, TRUE, FALSE); + } + } + + /* + * FIXME: Instead of this mess, allocate the patches from the aot mempool. + */ + /* Make a copy of the patch info which is in the mempool */ + { + MonoJumpInfo *patches = NULL, *patches_end = NULL; + + for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) { + MonoJumpInfo *new_patch_info = mono_patch_info_dup_mp (acfg->mempool, patch_info); + + if (!patches) + patches = new_patch_info; + else + patches_end->next = new_patch_info; + patches_end = new_patch_info; + } + cfg->patch_info = patches; + } + /* Make a copy of the unwind info */ + { + GSList *l, *unwind_ops; + MonoUnwindOp *op; + + unwind_ops = NULL; + for (l = cfg->unwind_ops; l; l = l->next) { + op = (MonoUnwindOp *)mono_mempool_alloc (acfg->mempool, sizeof (MonoUnwindOp)); + memcpy (op, l->data, sizeof (MonoUnwindOp)); + unwind_ops = g_slist_prepend_mempool (acfg->mempool, unwind_ops, op); + } + cfg->unwind_ops = g_slist_reverse (unwind_ops); + } + /* Make a copy of the argument/local info */ + { + MonoError error; + MonoInst **args, **locals; + MonoMethodSignature *sig; + MonoMethodHeader *header; + int i; + + sig = mono_method_signature (method); + args = (MonoInst **)mono_mempool_alloc (acfg->mempool, sizeof (MonoInst*) * (sig->param_count + sig->hasthis)); + for (i = 0; i < sig->param_count + sig->hasthis; ++i) { + args [i] = (MonoInst *)mono_mempool_alloc (acfg->mempool, sizeof (MonoInst)); + memcpy (args [i], cfg->args [i], sizeof (MonoInst)); + } + cfg->args = args; + + header = mono_method_get_header_checked (method, &error); + mono_error_assert_ok (&error); /* FIXME don't swallow the error */ + locals = (MonoInst **)mono_mempool_alloc (acfg->mempool, sizeof (MonoInst*) * header->num_locals); + for (i = 0; i < header->num_locals; ++i) { + locals [i] = (MonoInst *)mono_mempool_alloc (acfg->mempool, sizeof (MonoInst)); + memcpy (locals [i], cfg->locals [i], sizeof (MonoInst)); + } + mono_metadata_free_mh (header); + cfg->locals = locals; + } + + /* Free some fields used by cfg to conserve memory */ + mono_empty_compile (cfg); + + //printf ("Compile: %s\n", mono_method_full_name (method, TRUE)); + + while (index >= acfg->cfgs_size) { + MonoCompile **new_cfgs; + int new_size; + + new_size = acfg->cfgs_size * 2; + new_cfgs = g_new0 (MonoCompile*, new_size); + memcpy (new_cfgs, acfg->cfgs, sizeof (MonoCompile*) * acfg->cfgs_size); + g_free (acfg->cfgs); + acfg->cfgs = new_cfgs; + acfg->cfgs_size = new_size; + } + acfg->cfgs [index] = cfg; + + g_hash_table_insert (acfg->method_to_cfg, cfg->orig_method, cfg); + + /* Update global stats while holding a lock. */ + mono_update_jit_stats (cfg); + + /* + if (cfg->orig_method->wrapper_type) + g_ptr_array_add (acfg->extra_methods, cfg->orig_method); + */ + + mono_acfg_unlock (acfg); + + mono_atomic_inc_i32 (&acfg->stats.ccount); +} + +static mono_thread_start_return_t WINAPI +compile_thread_main (gpointer user_data) +{ + MonoAotCompile *acfg = ((MonoAotCompile **)user_data) [0]; + GPtrArray *methods = ((GPtrArray **)user_data) [1]; + int i; + + MonoError error; + MonoInternalThread *internal = mono_thread_internal_current (); + MonoString *str = mono_string_new_checked (mono_domain_get (), "AOT compiler", &error); + mono_error_assert_ok (&error); + mono_thread_set_name_internal (internal, str, TRUE, FALSE, &error); + mono_error_assert_ok (&error); + + for (i = 0; i < methods->len; ++i) + compile_method (acfg, (MonoMethod *)g_ptr_array_index (methods, i)); + + return 0; +} + +/* Used by the LLVM backend */ +guint32 +mono_aot_get_got_offset (MonoJumpInfo *ji) +{ + return get_got_offset (llvm_acfg, TRUE, ji); +} + +/* + * mono_aot_is_shared_got_offset: + * + * Return whenever OFFSET refers to a GOT slot which is preinitialized + * when the AOT image is loaded. + */ +gboolean +mono_aot_is_shared_got_offset (int offset) +{ + return offset < llvm_acfg->nshared_got_entries; +} + +char* +mono_aot_get_method_name (MonoCompile *cfg) +{ + if (llvm_acfg->aot_opts.static_link) + /* Include the assembly name too to avoid duplicate symbol errors */ + return g_strdup_printf ("%s_%s", llvm_acfg->assembly_name_sym, get_debug_sym (cfg->orig_method, "", llvm_acfg->method_label_hash)); + else + return get_debug_sym (cfg->orig_method, "", llvm_acfg->method_label_hash); +} + +/* + * mono_aot_is_linkonce_method: + * + * Return whenever METHOD should be emitted with linkonce linkage, + * eliminating duplicate copies when compiling in static mode. + */ +gboolean +mono_aot_is_linkonce_method (MonoMethod *method) +{ + return FALSE; +#if 0 + WrapperInfo *info; + + // FIXME: Add more cases + if (method->wrapper_type != MONO_WRAPPER_UNKNOWN) + return FALSE; + info = mono_marshal_get_wrapper_info (method); + if ((info && (info->subtype == WRAPPER_SUBTYPE_GSHAREDVT_IN_SIG || info->subtype == WRAPPER_SUBTYPE_GSHAREDVT_OUT_SIG))) + return TRUE; + return FALSE; +#endif +} + +static gboolean +append_mangled_type (GString *s, MonoType *t) +{ + if (t->byref) + g_string_append_printf (s, "b"); + switch (t->type) { + case MONO_TYPE_VOID: + g_string_append_printf (s, "void_"); + break; + case MONO_TYPE_I1: + g_string_append_printf (s, "i1"); + break; + case MONO_TYPE_U1: + g_string_append_printf (s, "u1"); + break; + case MONO_TYPE_I2: + g_string_append_printf (s, "i2"); + break; + case MONO_TYPE_U2: + g_string_append_printf (s, "u2"); + break; + case MONO_TYPE_I4: + g_string_append_printf (s, "i4"); + break; + case MONO_TYPE_U4: + g_string_append_printf (s, "u4"); + break; + case MONO_TYPE_I8: + g_string_append_printf (s, "i8"); + break; + case MONO_TYPE_U8: + g_string_append_printf (s, "u8"); + break; + case MONO_TYPE_I: + g_string_append_printf (s, "ii"); + break; + case MONO_TYPE_U: + g_string_append_printf (s, "ui"); + break; + case MONO_TYPE_R4: + g_string_append_printf (s, "fl"); + break; + case MONO_TYPE_R8: + g_string_append_printf (s, "do"); + break; + default: { + char *fullname = mono_type_full_name (t); + GString *temp; + char *temps; + int i, len; + + /* + * Have to create a mangled name which is: + * - a valid symbol + * - unique + */ + temp = g_string_new (""); + len = strlen (fullname); + for (i = 0; i < len; ++i) { + char c = fullname [i]; + if (isalnum (c)) { + g_string_append_c (temp, c); + } else if (c == '_') { + g_string_append_c (temp, '_'); + g_string_append_c (temp, '_'); + } else { + g_string_append_c (temp, '_'); + g_string_append_printf (temp, "%x", (int)c); + } + } + temps = g_string_free (temp, FALSE); + /* Include the length to avoid different length type names aliasing each other */ + g_string_append_printf (s, "cl%x_%s_", strlen (temps), temps); + g_free (temps); + } + } + if (t->attrs) + g_string_append_printf (s, "_attrs_%d", t->attrs); + return TRUE; +} + +static gboolean +append_mangled_signature (GString *s, MonoMethodSignature *sig) +{ + int i; + gboolean supported; + + supported = append_mangled_type (s, sig->ret); + if (!supported) + return FALSE; + if (sig->hasthis) + g_string_append_printf (s, "this_"); + if (sig->pinvoke) + g_string_append_printf (s, "pinvoke_"); + for (i = 0; i < sig->param_count; ++i) { + supported = append_mangled_type (s, sig->params [i]); + if (!supported) + return FALSE; + } + + return TRUE; +} + +static void +append_mangled_wrapper_type (GString *s, guint32 wrapper_type) +{ + const char *label; + + switch (wrapper_type) { + case MONO_WRAPPER_REMOTING_INVOKE: + label = "remoting_invoke"; + break; + case MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK: + label = "remoting_invoke_check"; + break; + case MONO_WRAPPER_XDOMAIN_INVOKE: + label = "remoting_invoke_xdomain"; + break; + case MONO_WRAPPER_PROXY_ISINST: + label = "proxy_isinst"; + break; + case MONO_WRAPPER_LDFLD: + label = "ldfld"; + break; + case MONO_WRAPPER_LDFLDA: + label = "ldflda"; + break; + case MONO_WRAPPER_STFLD: + label = "stfld"; + break; + case MONO_WRAPPER_ALLOC: + label = "alloc"; + break; + case MONO_WRAPPER_WRITE_BARRIER: + label = "write_barrier"; + break; + case MONO_WRAPPER_STELEMREF: + label = "stelemref"; + break; + case MONO_WRAPPER_UNKNOWN: + label = "unknown"; + break; + case MONO_WRAPPER_MANAGED_TO_NATIVE: + label = "man2native"; + break; + case MONO_WRAPPER_SYNCHRONIZED: + label = "synch"; + break; + case MONO_WRAPPER_MANAGED_TO_MANAGED: + label = "man2man"; + break; + case MONO_WRAPPER_CASTCLASS: + label = "castclass"; + break; + case MONO_WRAPPER_RUNTIME_INVOKE: + label = "run_invoke"; + break; + case MONO_WRAPPER_DELEGATE_INVOKE: + label = "del_inv"; + break; + case MONO_WRAPPER_DELEGATE_BEGIN_INVOKE: + label = "del_beg_inv"; + break; + case MONO_WRAPPER_DELEGATE_END_INVOKE: + label = "del_end_inv"; + break; + case MONO_WRAPPER_NATIVE_TO_MANAGED: + label = "native2man"; + break; + default: + g_assert_not_reached (); + } + + g_string_append_printf (s, "%s_", label); +} + +static void +append_mangled_wrapper_subtype (GString *s, WrapperSubtype subtype) +{ + const char *label; + + switch (subtype) + { + case WRAPPER_SUBTYPE_NONE: + return; + case WRAPPER_SUBTYPE_ELEMENT_ADDR: + label = "elem_addr"; + break; + case WRAPPER_SUBTYPE_STRING_CTOR: + label = "str_ctor"; + break; + case WRAPPER_SUBTYPE_VIRTUAL_STELEMREF: + label = "virt_stelem"; + break; + case WRAPPER_SUBTYPE_FAST_MONITOR_ENTER: + label = "fast_mon_enter"; + break; + case WRAPPER_SUBTYPE_FAST_MONITOR_ENTER_V4: + label = "fast_mon_enter_4"; + break; + case WRAPPER_SUBTYPE_FAST_MONITOR_EXIT: + label = "fast_monitor_exit"; + break; + case WRAPPER_SUBTYPE_PTR_TO_STRUCTURE: + label = "ptr2struct"; + break; + case WRAPPER_SUBTYPE_STRUCTURE_TO_PTR: + label = "struct2ptr"; + break; + case WRAPPER_SUBTYPE_CASTCLASS_WITH_CACHE: + label = "castclass_w_cache"; + break; + case WRAPPER_SUBTYPE_ISINST_WITH_CACHE: + label = "isinst_w_cache"; + break; + case WRAPPER_SUBTYPE_RUNTIME_INVOKE_NORMAL: + label = "run_inv_norm"; + break; + case WRAPPER_SUBTYPE_RUNTIME_INVOKE_DYNAMIC: + label = "run_inv_dyn"; + break; + case WRAPPER_SUBTYPE_RUNTIME_INVOKE_DIRECT: + label = "run_inv_dir"; + break; + case WRAPPER_SUBTYPE_RUNTIME_INVOKE_VIRTUAL: + label = "run_inv_vir"; + break; + case WRAPPER_SUBTYPE_ICALL_WRAPPER: + label = "icall"; + break; + case WRAPPER_SUBTYPE_NATIVE_FUNC_AOT: + label = "native_func_aot"; + break; + case WRAPPER_SUBTYPE_PINVOKE: + label = "pinvoke"; + break; + case WRAPPER_SUBTYPE_SYNCHRONIZED_INNER: + label = "synch_inner"; + break; + case WRAPPER_SUBTYPE_GSHAREDVT_IN: + label = "gshared_in"; + break; + case WRAPPER_SUBTYPE_GSHAREDVT_OUT: + label = "gshared_out"; + break; + case WRAPPER_SUBTYPE_ARRAY_ACCESSOR: + label = "array_acc"; + break; + case WRAPPER_SUBTYPE_GENERIC_ARRAY_HELPER: + label = "generic_arry_help"; + break; + case WRAPPER_SUBTYPE_DELEGATE_INVOKE_VIRTUAL: + label = "del_inv_virt"; + break; + case WRAPPER_SUBTYPE_DELEGATE_INVOKE_BOUND: + label = "del_inv_bound"; + break; + case WRAPPER_SUBTYPE_INTERP_IN: + label = "interp_in"; + break; + case WRAPPER_SUBTYPE_GSHAREDVT_IN_SIG: + label = "gsharedvt_in_sig"; + break; + case WRAPPER_SUBTYPE_GSHAREDVT_OUT_SIG: + label = "gsharedvt_out_sig"; + break; + default: + g_assert_not_reached (); + } + + g_string_append_printf (s, "%s_", label); +} + +static char * +sanitize_mangled_string (const char *input) +{ + GString *s = g_string_new (""); + + for (int i=0; input [i] != '\0'; i++) { + char c = input [i]; + switch (c) { + case '.': + g_string_append (s, "_dot_"); + break; + case ' ': + g_string_append (s, "_"); + break; + case '`': + g_string_append (s, "_bt_"); + break; + case '<': + g_string_append (s, "_le_"); + break; + case '>': + g_string_append (s, "_gt_"); + break; + case '/': + g_string_append (s, "_sl_"); + break; + case '[': + g_string_append (s, "_lbrack_"); + break; + case ']': + g_string_append (s, "_rbrack_"); + break; + case '(': + g_string_append (s, "_lparen_"); + break; + case '-': + g_string_append (s, "_dash_"); + break; + case ')': + g_string_append (s, "_rparen_"); + break; + case ',': + g_string_append (s, "_comma_"); + break; + case ':': + g_string_append (s, "_colon_"); + break; + default: + g_string_append_c (s, c); + } + } + + return g_string_free (s, FALSE); +} + +static gboolean +append_mangled_klass (GString *s, MonoClass *klass) +{ + char *klass_desc = mono_class_full_name (klass); + g_string_append_printf (s, "_%s_%s_", klass->name_space, klass_desc); + g_free (klass_desc); + + // Success + return TRUE; +} + +static gboolean +append_mangled_method (GString *s, MonoMethod *method); + +static gboolean +append_mangled_wrapper (GString *s, MonoMethod *method) +{ + gboolean success = TRUE; + WrapperInfo *info = mono_marshal_get_wrapper_info (method); + g_string_append_printf (s, "wrapper_"); + g_string_append_printf (s, "%s_", method->klass->image->assembly->aname.name); + + append_mangled_wrapper_type (s, method->wrapper_type); + + switch (method->wrapper_type) { + case MONO_WRAPPER_REMOTING_INVOKE: + case MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK: + case MONO_WRAPPER_XDOMAIN_INVOKE: { + MonoMethod *m = mono_marshal_method_from_wrapper (method); + g_assert (m); + success = success && append_mangled_method (s, m); + break; + } + case MONO_WRAPPER_PROXY_ISINST: + case MONO_WRAPPER_LDFLD: + case MONO_WRAPPER_LDFLDA: + case MONO_WRAPPER_STFLD: { + g_assert (info); + success = success && append_mangled_klass (s, info->d.proxy.klass); + break; + } + case MONO_WRAPPER_ALLOC: { + /* The GC name is saved once in MonoAotFileInfo */ + g_assert (info->d.alloc.alloc_type != -1); + g_string_append_printf (s, "%d_", info->d.alloc.alloc_type); + // SlowAlloc, etc + g_string_append_printf (s, "%s_", method->name); + break; + } + case MONO_WRAPPER_WRITE_BARRIER: { + g_string_append_printf (s, "%s_", method->name); + break; + } + case MONO_WRAPPER_STELEMREF: { + append_mangled_wrapper_subtype (s, info->subtype); + if (info->subtype == WRAPPER_SUBTYPE_VIRTUAL_STELEMREF) + g_string_append_printf (s, "%d", info->d.virtual_stelemref.kind); + break; + } + case MONO_WRAPPER_UNKNOWN: { + append_mangled_wrapper_subtype (s, info->subtype); + if (info->subtype == WRAPPER_SUBTYPE_PTR_TO_STRUCTURE || + info->subtype == WRAPPER_SUBTYPE_STRUCTURE_TO_PTR) + success = success && append_mangled_klass (s, method->klass); + else if (info->subtype == WRAPPER_SUBTYPE_SYNCHRONIZED_INNER) + success = success && append_mangled_method (s, info->d.synchronized_inner.method); + else if (info->subtype == WRAPPER_SUBTYPE_ARRAY_ACCESSOR) + success = success && append_mangled_method (s, info->d.array_accessor.method); + else if (info->subtype == WRAPPER_SUBTYPE_INTERP_IN) + append_mangled_signature (s, info->d.interp_in.sig); + else if (info->subtype == WRAPPER_SUBTYPE_GSHAREDVT_IN_SIG) + append_mangled_signature (s, info->d.gsharedvt.sig); + else if (info->subtype == WRAPPER_SUBTYPE_GSHAREDVT_OUT_SIG) + append_mangled_signature (s, info->d.gsharedvt.sig); + break; + } + case MONO_WRAPPER_MANAGED_TO_NATIVE: { + append_mangled_wrapper_subtype (s, info->subtype); + if (info->subtype == WRAPPER_SUBTYPE_ICALL_WRAPPER) { + g_string_append_printf (s, "%s", method->name); + } else if (info->subtype == WRAPPER_SUBTYPE_NATIVE_FUNC_AOT) { + success = success && append_mangled_method (s, info->d.managed_to_native.method); + } else { + g_assert (info->subtype == WRAPPER_SUBTYPE_NONE || info->subtype == WRAPPER_SUBTYPE_PINVOKE); + success = success && append_mangled_method (s, info->d.managed_to_native.method); + } + break; + } + case MONO_WRAPPER_SYNCHRONIZED: { + MonoMethod *m; + + m = mono_marshal_method_from_wrapper (method); + g_assert (m); + g_assert (m != method); + success = success && append_mangled_method (s, m); + break; + } + case MONO_WRAPPER_MANAGED_TO_MANAGED: { + append_mangled_wrapper_subtype (s, info->subtype); + + if (info->subtype == WRAPPER_SUBTYPE_ELEMENT_ADDR) { + g_string_append_printf (s, "%d_", info->d.element_addr.rank); + g_string_append_printf (s, "%d_", info->d.element_addr.elem_size); + } else if (info->subtype == WRAPPER_SUBTYPE_STRING_CTOR) { + success = success && append_mangled_method (s, info->d.string_ctor.method); + } else if (info->subtype == WRAPPER_SUBTYPE_GENERIC_ARRAY_HELPER) { + success = success && append_mangled_method (s, info->d.generic_array_helper.method); + } else { + success = FALSE; + } + break; + } + case MONO_WRAPPER_CASTCLASS: { + append_mangled_wrapper_subtype (s, info->subtype); + break; + } + case MONO_WRAPPER_RUNTIME_INVOKE: { + append_mangled_wrapper_subtype (s, info->subtype); + if (info->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_DIRECT || info->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_VIRTUAL) + success = success && append_mangled_method (s, info->d.runtime_invoke.method); + else if (info->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_NORMAL) + success = success && append_mangled_signature (s, info->d.runtime_invoke.sig); + break; + } + case MONO_WRAPPER_DELEGATE_INVOKE: + case MONO_WRAPPER_DELEGATE_BEGIN_INVOKE: + case MONO_WRAPPER_DELEGATE_END_INVOKE: { + if (method->is_inflated) { + /* These wrappers are identified by their class */ + g_string_append_printf (s, "i_"); + success = success && append_mangled_klass (s, method->klass); + } else { + WrapperInfo *info = mono_marshal_get_wrapper_info (method); + + g_string_append_printf (s, "u_"); + if (method->wrapper_type == MONO_WRAPPER_DELEGATE_INVOKE) + append_mangled_wrapper_subtype (s, info->subtype); + g_string_append_printf (s, "u_sigstart"); + } + break; + } + case MONO_WRAPPER_NATIVE_TO_MANAGED: { + g_assert (info); + success = success && append_mangled_method (s, info->d.native_to_managed.method); + success = success && append_mangled_klass (s, method->klass); + break; + } + default: + g_assert_not_reached (); + } + return success && append_mangled_signature (s, mono_method_signature (method)); +} + +static void +append_mangled_ginst (GString *str, MonoGenericInst *ginst) +{ + int i; + + for (i = 0; i < ginst->type_argc; ++i) { + if (i > 0) + g_string_append (str, ", "); + MonoType *type = ginst->type_argv [i]; + switch (type->type) { + case MONO_TYPE_VAR: + case MONO_TYPE_MVAR: { + MonoType *constraint = NULL; + if (type->data.generic_param) + constraint = type->data.generic_param->gshared_constraint; + if (constraint) { + g_assert (constraint->type != MONO_TYPE_VAR && constraint->type != MONO_TYPE_MVAR); + g_string_append (str, "gshared:"); + mono_type_get_desc (str, constraint, TRUE); + break; + } + // Else falls through to common case + } + default: + mono_type_get_desc (str, type, TRUE); + } + } +} + +static void +append_mangled_context (GString *str, MonoGenericContext *context) +{ + GString *res = g_string_new (""); + + g_string_append_printf (res, "gens_"); + g_string_append (res, "00"); + + gboolean good = context->class_inst && context->class_inst->type_argc > 0; + good = good || (context->method_inst && context->method_inst->type_argc > 0); + g_assert (good); + + if (context->class_inst) + append_mangled_ginst (res, context->class_inst); + if (context->method_inst) { + if (context->class_inst) + g_string_append (res, "11"); + append_mangled_ginst (res, context->method_inst); + } + g_string_append_printf (str, "gens_%s", res->str); + g_free (res); +} + +static gboolean +append_mangled_method (GString *s, MonoMethod *method) +{ + if (method->wrapper_type) + return append_mangled_wrapper (s, method); + + if (method->is_inflated) { + g_string_append_printf (s, "inflated_"); + MonoMethodInflated *imethod = (MonoMethodInflated*) method; + g_assert (imethod->context.class_inst != NULL || imethod->context.method_inst != NULL); + + append_mangled_context (s, &imethod->context); + g_string_append_printf (s, "_declared_by_"); + append_mangled_method (s, imethod->declaring); + } else if (method->is_generic) { + g_string_append_printf (s, "%s_", method->klass->image->assembly->aname.name); + + g_string_append_printf (s, "generic_"); + append_mangled_klass (s, method->klass); + g_string_append_printf (s, "_%s_", method->name); + + MonoGenericContainer *container = mono_method_get_generic_container (method); + g_string_append_printf (s, "_%s"); + append_mangled_context (s, &container->context); + + return append_mangled_signature (s, mono_method_signature (method)); + } else { + g_string_append_printf (s, "_"); + append_mangled_klass (s, method->klass); + g_string_append_printf (s, "_%s_", method->name); + if (!append_mangled_signature (s, mono_method_signature (method))) { + g_string_free (s, TRUE); + return FALSE; + } + } + + return TRUE; +} + +/* + * mono_aot_get_mangled_method_name: + * + * Return a unique mangled name for METHOD, or NULL. + */ +char* +mono_aot_get_mangled_method_name (MonoMethod *method) +{ + // FIXME: use static cache (mempool?) + // We call this a *lot* + + GString *s = g_string_new ("aot_"); + if (!append_mangled_method (s, method)) { + g_string_free (s, TRUE); + return NULL; + } else { + char *out = g_string_free (s, FALSE); + // Scrub method and class names + char *cleaned = sanitize_mangled_string (out); + g_free (out); + return cleaned; + } +} + +gboolean +mono_aot_is_direct_callable (MonoJumpInfo *patch_info) +{ + return is_direct_callable (llvm_acfg, NULL, patch_info); +} + +void +mono_aot_mark_unused_llvm_plt_entry (MonoJumpInfo *patch_info) +{ + MonoPltEntry *plt_entry; + + plt_entry = get_plt_entry (llvm_acfg, patch_info); + plt_entry->llvm_used = FALSE; +} + +char* +mono_aot_get_direct_call_symbol (MonoJumpInfoType type, gconstpointer data) +{ + const char *sym = NULL; + + if (llvm_acfg->aot_opts.direct_icalls) { + if (type == MONO_PATCH_INFO_JIT_ICALL_ADDR) { + /* Call to a C function implementing a jit icall */ + sym = mono_lookup_jit_icall_symbol ((const char *)data); + } else if (type == MONO_PATCH_INFO_ICALL_ADDR_CALL) { + MonoMethod *method = (MonoMethod *)data; + if (!(method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) + sym = mono_lookup_icall_symbol (method); + else if (llvm_acfg->aot_opts.direct_pinvoke) + sym = get_pinvoke_import (llvm_acfg, method); + } + if (sym) + return g_strdup (sym); + } + return NULL; +} + +char* +mono_aot_get_plt_symbol (MonoJumpInfoType type, gconstpointer data) +{ + MonoJumpInfo *ji = (MonoJumpInfo *)mono_mempool_alloc (llvm_acfg->mempool, sizeof (MonoJumpInfo)); + MonoPltEntry *plt_entry; + const char *sym = NULL; + + ji->type = type; + ji->data.target = data; + + if (!can_encode_patch (llvm_acfg, ji)) + return NULL; + + if (llvm_acfg->aot_opts.direct_icalls) { + if (type == MONO_PATCH_INFO_JIT_ICALL_ADDR) { + /* Call to a C function implementing a jit icall */ + sym = mono_lookup_jit_icall_symbol ((const char *)data); + } else if (type == MONO_PATCH_INFO_ICALL_ADDR_CALL) { + MonoMethod *method = (MonoMethod *)data; + if (!(method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) + sym = mono_lookup_icall_symbol (method); + } + if (sym) + return g_strdup (sym); + } + + plt_entry = get_plt_entry (llvm_acfg, ji); + plt_entry->llvm_used = TRUE; + +#if defined(TARGET_MACH) + return g_strdup_printf (plt_entry->llvm_symbol + strlen (llvm_acfg->llvm_label_prefix)); +#else + return g_strdup_printf (plt_entry->llvm_symbol); +#endif +} + +int +mono_aot_get_method_index (MonoMethod *method) +{ + g_assert (llvm_acfg); + return get_method_index (llvm_acfg, method); +} + +MonoJumpInfo* +mono_aot_patch_info_dup (MonoJumpInfo* ji) +{ + MonoJumpInfo *res; + + mono_acfg_lock (llvm_acfg); + res = mono_patch_info_dup_mp (llvm_acfg->mempool, ji); + mono_acfg_unlock (llvm_acfg); + + return res; +} + +static int +execute_system (const char * command) +{ + int status = 0; + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) && defined(HOST_WIN32) + // We need an extra set of quotes around the whole command to properly handle commands + // with spaces since internally the command is called through "cmd /c. + char * quoted_command = g_strdup_printf ("\"%s\"", command); + + int size = MultiByteToWideChar (CP_UTF8, 0 , quoted_command , -1, NULL , 0); + wchar_t* wstr = g_malloc (sizeof (wchar_t) * size); + MultiByteToWideChar (CP_UTF8, 0, quoted_command, -1, wstr , size); + status = _wsystem (wstr); + g_free (wstr); + + g_free (quoted_command); +#elif defined (HAVE_SYSTEM) + status = system (command); +#else + g_assert_not_reached (); +#endif + + return status; +} + +#ifdef ENABLE_LLVM + +/* + * emit_llvm_file: + * + * Emit the LLVM code into an LLVM bytecode file, and compile it using the LLVM + * tools. + */ +static gboolean +emit_llvm_file (MonoAotCompile *acfg) +{ + char *command, *opts, *tempbc, *optbc, *output_fname; + + if (acfg->aot_opts.llvm_only && acfg->aot_opts.asm_only) { + tempbc = g_strdup_printf ("%s.bc", acfg->tmpbasename); + optbc = g_strdup (acfg->aot_opts.llvm_outfile); + } else { + tempbc = g_strdup_printf ("%s.bc", acfg->tmpbasename); + optbc = g_strdup_printf ("%s.opt.bc", acfg->tmpbasename); + } + + mono_llvm_emit_aot_module (tempbc, g_path_get_basename (acfg->image->name)); + + /* + * FIXME: Experiment with adding optimizations, the -std-compile-opts set takes + * a lot of time, and doesn't seem to save much space. + * The following optimizations cannot be enabled: + * - 'tailcallelim' + * - 'jump-threading' changes our blockaddress references to int constants. + * - 'basiccg' fails because it contains: + * if (CS && !isa(II)) { + * and isa is false for invokes to intrinsics (iltests.exe). + * - 'prune-eh' and 'functionattrs' depend on 'basiccg'. + * The opt list below was produced by taking the output of: + * llvm-as < /dev/null | opt -O2 -disable-output -debug-pass=Arguments + * then removing tailcallelim + the global opts. + * strip-dead-prototypes deletes unused intrinsics definitions. + */ + /* The dse pass is disabled because of #13734 and #17616 */ + /* + * The dse bug is in DeadStoreElimination.cpp:isOverwrite (): + * // If we have no DataLayout information around, then the size of the store + * // is inferrable from the pointee type. If they are the same type, then + * // we know that the store is safe. + * if (AA.getDataLayout() == 0 && + * Later.Ptr->getType() == Earlier.Ptr->getType()) { + * return OverwriteComplete; + * Here, if 'Earlier' refers to a memset, and Later has no size info, it mistakenly thinks the memset is redundant. + */ + if (acfg->aot_opts.llvm_only) + // FIXME: This doesn't work yet + opts = g_strdup (""); + else +#if LLVM_API_VERSION > 100 + opts = g_strdup ("-O2 -disable-tail-calls"); +#else + opts = g_strdup ("-targetlibinfo -no-aa -basicaa -notti -instcombine -simplifycfg -inline-cost -inline -sroa -domtree -early-cse -lazy-value-info -correlated-propagation -simplifycfg -instcombine -simplifycfg -reassociate -domtree -loops -loop-simplify -lcssa -loop-rotate -licm -lcssa -loop-unswitch -instcombine -scalar-evolution -loop-simplify -lcssa -indvars -loop-idiom -loop-deletion -loop-unroll -memdep -gvn -memdep -memcpyopt -sccp -instcombine -lazy-value-info -correlated-propagation -domtree -memdep -adce -simplifycfg -instcombine -strip-dead-prototypes -domtree -verify"); +#endif + command = g_strdup_printf ("\"%sopt\" -f %s -o \"%s\" \"%s\"", acfg->aot_opts.llvm_path, opts, optbc, tempbc); + aot_printf (acfg, "Executing opt: %s\n", command); + if (execute_system (command) != 0) + return FALSE; + g_free (opts); + + if (acfg->aot_opts.llvm_only && acfg->aot_opts.asm_only) + /* Nothing else to do */ + return TRUE; + + if (acfg->aot_opts.llvm_only) { + /* Use the stock clang from xcode */ + // FIXME: arch + command = g_strdup_printf ("clang++ -fexceptions -march=x86-64 -fpic -msse -msse2 -msse3 -msse4 -O2 -fno-optimize-sibling-calls -Wno-override-module -c -o \"%s\" \"%s.opt.bc\"", acfg->llvm_ofile, acfg->tmpbasename); + + aot_printf (acfg, "Executing clang: %s\n", command); + if (execute_system (command) != 0) + return FALSE; + return TRUE; + } + + if (!acfg->llc_args) + acfg->llc_args = g_string_new (""); + + /* Verbose asm slows down llc greatly */ + g_string_append (acfg->llc_args, " -asm-verbose=false"); + + if (acfg->aot_opts.mtriple) + g_string_append_printf (acfg->llc_args, " -mtriple=%s", acfg->aot_opts.mtriple); + + g_string_append (acfg->llc_args, " -disable-gnu-eh-frame -enable-mono-eh-frame"); + + g_string_append_printf (acfg->llc_args, " -mono-eh-frame-symbol=%s%s", acfg->user_symbol_prefix, acfg->llvm_eh_frame_symbol); + +#if LLVM_API_VERSION > 100 + g_string_append_printf (acfg->llc_args, " -disable-tail-calls"); +#endif + +#if ( defined(TARGET_MACH) && defined(TARGET_ARM) ) || defined(TARGET_ORBIS) + /* ios requires PIC code now */ + g_string_append_printf (acfg->llc_args, " -relocation-model=pic"); +#else + if (llvm_acfg->aot_opts.static_link) + g_string_append_printf (acfg->llc_args, " -relocation-model=static"); + else + g_string_append_printf (acfg->llc_args, " -relocation-model=pic"); +#endif + + if (acfg->llvm_owriter) { + /* Emit an object file directly */ + output_fname = g_strdup_printf ("%s", acfg->llvm_ofile); + g_string_append_printf (acfg->llc_args, " -filetype=obj"); + } else { + output_fname = g_strdup_printf ("%s", acfg->llvm_sfile); + } + command = g_strdup_printf ("\"%sllc\" %s -o \"%s\" \"%s.opt.bc\"", acfg->aot_opts.llvm_path, acfg->llc_args->str, output_fname, acfg->tmpbasename); + g_free (output_fname); + + aot_printf (acfg, "Executing llc: %s\n", command); + + if (execute_system (command) != 0) + return FALSE; + return TRUE; +} +#endif + +static void +emit_code (MonoAotCompile *acfg) +{ + int oindex, i, prev_index; + gboolean saved_unbox_info = FALSE; + char symbol [MAX_SYMBOL_SIZE]; + + if (acfg->aot_opts.llvm_only) + return; + +#if defined(TARGET_POWERPC64) + sprintf (symbol, ".Lgot_addr"); + emit_section_change (acfg, ".text", 0); + emit_alignment (acfg, 8); + emit_label (acfg, symbol); + emit_pointer (acfg, acfg->got_symbol); +#endif + + /* + * This global symbol is used to compute the address of each method using the + * code_offsets array. It is also used to compute the memory ranges occupied by + * AOT code, so it must be equal to the address of the first emitted method. + */ + emit_section_change (acfg, ".text", 0); + emit_alignment_code (acfg, 8); + emit_info_symbol (acfg, "jit_code_start"); + + /* + * Emit some padding so the local symbol for the first method doesn't have the + * same address as 'methods'. + */ + emit_padding (acfg, 16); + + for (oindex = 0; oindex < acfg->method_order->len; ++oindex) { + MonoCompile *cfg; + MonoMethod *method; + + i = GPOINTER_TO_UINT (g_ptr_array_index (acfg->method_order, oindex)); + + cfg = acfg->cfgs [i]; + + if (!cfg) + continue; + + method = cfg->orig_method; + + gboolean dedup_collect = acfg->aot_opts.dedup || (acfg->aot_opts.dedup_include && !acfg->dedup_emit_mode); + gboolean dedupable = mono_aot_can_dedup (method); + + // cfg->skip is vital for LLVM to work, can't just continue in this loop + if (dedupable && strcmp (method->name, "wbarrier_conc") && dedup_collect) { + mono_dedup_cache_method (acfg, method); + + // Don't compile inflated methods if we're in first phase of + // dedup + // + // In second phase, we emit methods that + // are dedupable. We also emit later methods + // which are referenced by them and added later. + // For this reason, when in the dedup_include mode, + // we never set skip. + if (acfg->aot_opts.dedup) + cfg->skip = TRUE; + } + + // Don't compile anything in this mode + if (acfg->aot_opts.dedup_include && !acfg->dedup_emit_mode) + cfg->skip = TRUE; + + // Compile everything in this mode + if (acfg->aot_opts.dedup_include && acfg->dedup_emit_mode) + cfg->skip = FALSE; + + /*if (dedup_collect) {*/ + /*char *name = mono_aot_get_mangled_method_name (method);*/ + + /*if (ignore_cfg (cfg))*/ + /*aot_printf (acfg, "Dedup Skipping %s\n", acfg->image->name, name);*/ + /*else*/ + /*aot_printf (acfg, "Dedup Keeping %s\n", acfg->image->name, name);*/ + + /*g_free (name);*/ + /*}*/ + + if (ignore_cfg (cfg)) + continue; + + /* Emit unbox trampoline */ + if (mono_aot_mode_is_full (&acfg->aot_opts) && cfg->orig_method->klass->valuetype) { + sprintf (symbol, "ut_%d", get_method_index (acfg, method)); + + emit_section_change (acfg, ".text", 0); + + if (acfg->thumb_mixed && cfg->compile_llvm) { + emit_set_thumb_mode (acfg); + fprintf (acfg->fp, "\n.thumb_func\n"); + } + + emit_label (acfg, symbol); + + arch_emit_unbox_trampoline (acfg, cfg, cfg->orig_method, cfg->asm_symbol); + + if (acfg->thumb_mixed && cfg->compile_llvm) + emit_set_arm_mode (acfg); + + if (!saved_unbox_info) { + char user_symbol [128]; + GSList *unwind_ops; + sprintf (user_symbol, "%sunbox_trampoline_p", acfg->user_symbol_prefix); + + emit_label (acfg, "ut_end"); + + unwind_ops = mono_unwind_get_cie_program (); + save_unwind_info (acfg, user_symbol, unwind_ops); + mono_free_unwind_info (unwind_ops); + + /* Save the unbox trampoline size */ + emit_symbol_diff (acfg, "ut_end", symbol, 0); + + saved_unbox_info = TRUE; + } + } + + if (cfg->compile_llvm) { + acfg->stats.llvm_count ++; + } else { + emit_method_code (acfg, cfg); + } + } + + emit_section_change (acfg, ".text", 0); + emit_alignment_code (acfg, 8); + emit_info_symbol (acfg, "jit_code_end"); + + /* To distinguish it from the next symbol */ + emit_padding (acfg, 4); + + /* + * Add .no_dead_strip directives for all LLVM methods to prevent the OSX linker + * from optimizing them away, since it doesn't see that code_offsets references them. + * JITted methods don't need this since they are referenced using assembler local + * symbols. + * FIXME: This is why write-symbols doesn't work on OSX ? + */ + if (acfg->llvm && acfg->need_no_dead_strip) { + fprintf (acfg->fp, "\n"); + for (i = 0; i < acfg->nmethods; ++i) { + if (acfg->cfgs [i] && acfg->cfgs [i]->compile_llvm) + fprintf (acfg->fp, ".no_dead_strip %s\n", acfg->cfgs [i]->asm_symbol); + } + } + + /* + * To work around linker issues, we emit a table of branches, and disassemble them at runtime. + * This is PIE code, and the linker can update it if needed. + */ + + sprintf (symbol, "method_addresses"); + emit_section_change (acfg, ".text", 1); + emit_alignment_code (acfg, 8); + emit_info_symbol (acfg, symbol); + if (acfg->aot_opts.write_symbols) + emit_local_symbol (acfg, symbol, "method_addresses_end", TRUE); + emit_unset_mode (acfg); + if (acfg->need_no_dead_strip) + fprintf (acfg->fp, " .no_dead_strip %s\n", symbol); + + for (i = 0; i < acfg->nmethods; ++i) { +#ifdef MONO_ARCH_AOT_SUPPORTED + int call_size; + + if (!ignore_cfg (acfg->cfgs [i])) { + arch_emit_direct_call (acfg, acfg->cfgs [i]->asm_symbol, FALSE, acfg->thumb_mixed && acfg->cfgs [i]->compile_llvm, NULL, &call_size); + } else { + arch_emit_direct_call (acfg, symbol, FALSE, FALSE, NULL, &call_size); + } +#endif + } + + sprintf (symbol, "method_addresses_end"); + emit_label (acfg, symbol); + emit_line (acfg); + + /* Emit a sorted table mapping methods to the index of their unbox trampolines */ + sprintf (symbol, "unbox_trampolines"); + emit_section_change (acfg, RODATA_SECT, 0); + emit_alignment (acfg, 8); + emit_info_symbol (acfg, symbol); + + prev_index = -1; + for (i = 0; i < acfg->nmethods; ++i) { + MonoCompile *cfg; + MonoMethod *method; + int index; + + cfg = acfg->cfgs [i]; + if (ignore_cfg (cfg)) + continue; + + method = cfg->orig_method; + + if (mono_aot_mode_is_full (&acfg->aot_opts) && cfg->orig_method->klass->valuetype) { + index = get_method_index (acfg, method); + + emit_int32 (acfg, index); + /* Make sure the table is sorted by index */ + g_assert (index > prev_index); + prev_index = index; + } + } + sprintf (symbol, "unbox_trampolines_end"); + emit_info_symbol (acfg, symbol); + emit_int32 (acfg, 0); + + /* Emit a separate table with the trampoline addresses/offsets */ + sprintf (symbol, "unbox_trampoline_addresses"); + emit_section_change (acfg, ".text", 0); + emit_alignment_code (acfg, 8); + emit_info_symbol (acfg, symbol); + + for (i = 0; i < acfg->nmethods; ++i) { + MonoCompile *cfg; + MonoMethod *method; + int index; + + cfg = acfg->cfgs [i]; + if (ignore_cfg (cfg)) + continue; + + method = cfg->orig_method; + + if (mono_aot_mode_is_full (&acfg->aot_opts) && cfg->orig_method->klass->valuetype) { +#ifdef MONO_ARCH_AOT_SUPPORTED + int call_size; + + index = get_method_index (acfg, method); + sprintf (symbol, "ut_%d", index); + + arch_emit_direct_call (acfg, symbol, FALSE, acfg->thumb_mixed && cfg->compile_llvm, NULL, &call_size); +#endif + } + } + emit_int32 (acfg, 0); +} + +static void +emit_info (MonoAotCompile *acfg) +{ + int oindex, i; + gint32 *offsets; + + offsets = g_new0 (gint32, acfg->nmethods); + + for (oindex = 0; oindex < acfg->method_order->len; ++oindex) { + i = GPOINTER_TO_UINT (g_ptr_array_index (acfg->method_order, oindex)); + + if (acfg->cfgs [i]) { + emit_method_info (acfg, acfg->cfgs [i]); + offsets [i] = acfg->cfgs [i]->method_info_offset; + } else { + offsets [i] = 0; + } + } + + acfg->stats.offsets_size += emit_offset_table (acfg, "method_info_offsets", MONO_AOT_TABLE_METHOD_INFO_OFFSETS, acfg->nmethods, 10, offsets); + + g_free (offsets); +} + +#endif /* #if !defined(DISABLE_AOT) && !defined(DISABLE_JIT) */ + +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) +#define mix(a,b,c) { \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} +#define final(a,b,c) { \ + c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ +} + +static guint +mono_aot_type_hash (MonoType *t1) +{ + guint hash = t1->type; + + hash |= t1->byref << 6; /* do not collide with t1->type values */ + switch (t1->type) { + case MONO_TYPE_VALUETYPE: + case MONO_TYPE_CLASS: + case MONO_TYPE_SZARRAY: + /* check if the distribution is good enough */ + return ((hash << 5) - hash) ^ mono_metadata_str_hash (t1->data.klass->name); + case MONO_TYPE_PTR: + return ((hash << 5) - hash) ^ mono_metadata_type_hash (t1->data.type); + case MONO_TYPE_ARRAY: + return ((hash << 5) - hash) ^ mono_metadata_type_hash (&t1->data.array->eklass->byval_arg); + case MONO_TYPE_GENERICINST: + return ((hash << 5) - hash) ^ 0; + default: + return hash; + } +} + +/* + * mono_aot_method_hash: + * + * Return a hash code for methods which only depends on metadata. + */ +guint32 +mono_aot_method_hash (MonoMethod *method) +{ + MonoMethodSignature *sig; + MonoClass *klass; + int i, hindex; + int hashes_count; + guint32 *hashes_start, *hashes; + guint32 a, b, c; + MonoGenericInst *class_ginst = NULL; + MonoGenericInst *ginst = NULL; + + /* Similar to the hash in mono_method_get_imt_slot () */ + + sig = mono_method_signature (method); + + if (mono_class_is_ginst (method->klass)) + class_ginst = mono_class_get_generic_class (method->klass)->context.class_inst; + if (method->is_inflated) + ginst = ((MonoMethodInflated*)method)->context.method_inst; + + hashes_count = sig->param_count + 5 + (class_ginst ? class_ginst->type_argc : 0) + (ginst ? ginst->type_argc : 0); + hashes_start = (guint32 *)g_malloc0 (hashes_count * sizeof (guint32)); + hashes = hashes_start; + + /* Some wrappers are assigned to random classes */ + if (!method->wrapper_type || method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK) + klass = method->klass; + else + klass = mono_defaults.object_class; + + if (!method->wrapper_type) { + char *full_name; + + if (mono_class_is_ginst (klass)) + full_name = mono_type_full_name (&mono_class_get_generic_class (klass)->container_class->byval_arg); + else + full_name = mono_type_full_name (&klass->byval_arg); + + hashes [0] = mono_metadata_str_hash (full_name); + hashes [1] = 0; + g_free (full_name); + } else { + hashes [0] = mono_metadata_str_hash (klass->name); + hashes [1] = mono_metadata_str_hash (klass->name_space); + } + if (method->wrapper_type == MONO_WRAPPER_STFLD || method->wrapper_type == MONO_WRAPPER_LDFLD || method->wrapper_type == MONO_WRAPPER_LDFLDA) + /* The method name includes a stringified pointer */ + hashes [2] = 0; + else + hashes [2] = mono_metadata_str_hash (method->name); + hashes [3] = method->wrapper_type; + hashes [4] = mono_aot_type_hash (sig->ret); + hindex = 5; + for (i = 0; i < sig->param_count; i++) { + hashes [hindex ++] = mono_aot_type_hash (sig->params [i]); + } + if (class_ginst) { + for (i = 0; i < class_ginst->type_argc; ++i) + hashes [hindex ++] = mono_aot_type_hash (class_ginst->type_argv [i]); + } + if (ginst) { + for (i = 0; i < ginst->type_argc; ++i) + hashes [hindex ++] = mono_aot_type_hash (ginst->type_argv [i]); + } + g_assert (hindex == hashes_count); + + /* Setup internal state */ + a = b = c = 0xdeadbeef + (((guint32)hashes_count)<<2); + + /* Handle most of the hashes */ + while (hashes_count > 3) { + a += hashes [0]; + b += hashes [1]; + c += hashes [2]; + mix (a,b,c); + hashes_count -= 3; + hashes += 3; + } + + /* Handle the last 3 hashes (all the case statements fall through) */ + switch (hashes_count) { + case 3 : c += hashes [2]; + case 2 : b += hashes [1]; + case 1 : a += hashes [0]; + final (a,b,c); + case 0: /* nothing left to add */ + break; + } + + g_free (hashes_start); + + return c; +} +#undef rot +#undef mix +#undef final + +/* + * mono_aot_get_array_helper_from_wrapper; + * + * Get the helper method in Array called by an array wrapper method. + */ +MonoMethod* +mono_aot_get_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; +} + +#if !defined(DISABLE_AOT) && !defined(DISABLE_JIT) + +typedef struct HashEntry { + guint32 key, value, index; + struct HashEntry *next; +} HashEntry; + +/* + * emit_extra_methods: + * + * Emit methods which are not in the METHOD table, like wrappers. + */ +static void +emit_extra_methods (MonoAotCompile *acfg) +{ + int i, table_size, buf_size; + guint8 *p, *buf; + guint32 *info_offsets; + guint32 hash; + GPtrArray *table; + HashEntry *entry, *new_entry; + int nmethods, max_chain_length; + int *chain_lengths; + + info_offsets = g_new0 (guint32, acfg->extra_methods->len); + + /* Emit method info */ + nmethods = 0; + for (i = 0; i < acfg->extra_methods->len; ++i) { + MonoMethod *method = (MonoMethod *)g_ptr_array_index (acfg->extra_methods, i); + MonoCompile *cfg = (MonoCompile *)g_hash_table_lookup (acfg->method_to_cfg, method); + + if (ignore_cfg (cfg)) + continue; + + buf_size = 10240; + p = buf = (guint8 *)g_malloc (buf_size); + + nmethods ++; + + method = cfg->method_to_register; + + encode_method_ref (acfg, method, p, &p); + + g_assert ((p - buf) < buf_size); + + info_offsets [i] = add_to_blob (acfg, buf, p - buf); + g_free (buf); + } + + /* + * Construct a chained hash table for mapping indexes in extra_method_info to + * method indexes. + */ + table_size = g_spaced_primes_closest ((int)(nmethods * 1.5)); + table = g_ptr_array_sized_new (table_size); + for (i = 0; i < table_size; ++i) + g_ptr_array_add (table, NULL); + chain_lengths = g_new0 (int, table_size); + max_chain_length = 0; + for (i = 0; i < acfg->extra_methods->len; ++i) { + MonoMethod *method = (MonoMethod *)g_ptr_array_index (acfg->extra_methods, i); + MonoCompile *cfg = (MonoCompile *)g_hash_table_lookup (acfg->method_to_cfg, method); + guint32 key, value; + + if (ignore_cfg (cfg)) + continue; + + key = info_offsets [i]; + value = get_method_index (acfg, method); + + hash = mono_aot_method_hash (method) % table_size; + //printf ("X: %s %x\n", mono_method_get_full_name (method), mono_aot_method_hash (method)); + + chain_lengths [hash] ++; + max_chain_length = MAX (max_chain_length, chain_lengths [hash]); + + new_entry = (HashEntry *)mono_mempool_alloc0 (acfg->mempool, sizeof (HashEntry)); + new_entry->key = key; + new_entry->value = value; + + entry = (HashEntry *)g_ptr_array_index (table, hash); + if (entry == NULL) { + new_entry->index = hash; + g_ptr_array_index (table, hash) = new_entry; + } else { + while (entry->next) + entry = entry->next; + + entry->next = new_entry; + new_entry->index = table->len; + g_ptr_array_add (table, new_entry); + } + } + g_free (chain_lengths); + + //printf ("MAX: %d\n", max_chain_length); + + buf_size = table->len * 12 + 4; + p = buf = (guint8 *)g_malloc (buf_size); + encode_int (table_size, p, &p); + + for (i = 0; i < table->len; ++i) { + HashEntry *entry = (HashEntry *)g_ptr_array_index (table, i); + + if (entry == NULL) { + encode_int (0, p, &p); + encode_int (0, p, &p); + encode_int (0, p, &p); + } else { + //g_assert (entry->key > 0); + encode_int (entry->key, p, &p); + encode_int (entry->value, p, &p); + if (entry->next) + encode_int (entry->next->index, p, &p); + else + encode_int (0, p, &p); + } + } + g_assert (p - buf <= buf_size); + + /* Emit the table */ + emit_aot_data (acfg, MONO_AOT_TABLE_EXTRA_METHOD_TABLE, "extra_method_table", buf, p - buf); + + g_free (buf); + + /* + * Emit a table reverse mapping method indexes to their index in extra_method_info. + * This is used by mono_aot_find_jit_info (). + */ + buf_size = acfg->extra_methods->len * 8 + 4; + p = buf = (guint8 *)g_malloc (buf_size); + encode_int (acfg->extra_methods->len, p, &p); + for (i = 0; i < acfg->extra_methods->len; ++i) { + MonoMethod *method = (MonoMethod *)g_ptr_array_index (acfg->extra_methods, i); + + encode_int (get_method_index (acfg, method), p, &p); + encode_int (info_offsets [i], p, &p); + } + emit_aot_data (acfg, MONO_AOT_TABLE_EXTRA_METHOD_INFO_OFFSETS, "extra_method_info_offsets", buf, p - buf); + + g_free (buf); + g_free (info_offsets); + g_ptr_array_free (table, TRUE); +} + +static void +generate_aotid (guint8* aotid) +{ + gpointer rand_handle; + MonoError error; + + mono_rand_open (); + rand_handle = mono_rand_init (NULL, 0); + + mono_rand_try_get_bytes (&rand_handle, aotid, 16, &error); + mono_error_assert_ok (&error); + + mono_rand_close (rand_handle); +} + +static void +emit_exception_info (MonoAotCompile *acfg) +{ + int i; + gint32 *offsets; + SeqPointData sp_data; + gboolean seq_points_to_file = FALSE; + + offsets = g_new0 (gint32, acfg->nmethods); + for (i = 0; i < acfg->nmethods; ++i) { + if (acfg->cfgs [i]) { + MonoCompile *cfg = acfg->cfgs [i]; + + // By design aot-runtime decode_exception_debug_info is not able to load sequence point debug data from a file. + // As it is not possible to load debug data from a file its is also not possible to store it in a file. + gboolean method_seq_points_to_file = acfg->aot_opts.gen_msym_dir && + cfg->gen_seq_points && !cfg->gen_sdb_seq_points; + gboolean method_seq_points_to_binary = cfg->gen_seq_points && !method_seq_points_to_file; + + emit_exception_debug_info (acfg, cfg, method_seq_points_to_binary); + offsets [i] = cfg->ex_info_offset; + + if (method_seq_points_to_file) { + if (!seq_points_to_file) { + mono_seq_point_data_init (&sp_data, acfg->nmethods); + seq_points_to_file = TRUE; + } + mono_seq_point_data_add (&sp_data, cfg->method->token, cfg->method_index, cfg->seq_point_info); + } + } else { + offsets [i] = 0; + } + } + + if (seq_points_to_file) { + char *aotid = mono_guid_to_string_minimal (acfg->image->aotid); + char *dir = g_build_filename (acfg->aot_opts.gen_msym_dir_path, aotid, NULL); + char *image_basename = g_path_get_basename (acfg->image->name); + char *aot_file = g_strdup_printf("%s%s", image_basename, SEQ_POINT_AOT_EXT); + char *aot_file_path = g_build_filename (dir, aot_file, NULL); + + if (g_ensure_directory_exists (aot_file_path) == FALSE) { + fprintf (stderr, "AOT : failed to create msym directory: %s\n", aot_file_path); + exit (1); + } + + mono_seq_point_data_write (&sp_data, aot_file_path); + mono_seq_point_data_free (&sp_data); + + g_free (aotid); + g_free (dir); + g_free (image_basename); + g_free (aot_file); + g_free (aot_file_path); + } + + acfg->stats.offsets_size += emit_offset_table (acfg, "ex_info_offsets", MONO_AOT_TABLE_EX_INFO_OFFSETS, acfg->nmethods, 10, offsets); + g_free (offsets); +} + +static void +emit_unwind_info (MonoAotCompile *acfg) +{ + int i; + char symbol [128]; + + if (acfg->aot_opts.llvm_only) { + g_assert (acfg->unwind_ops->len == 0); + return; + } + + /* + * The unwind info contains a lot of duplicates so we emit each unique + * entry once, and only store the offset from the start of the table in the + * exception info. + */ + + sprintf (symbol, "unwind_info"); + emit_section_change (acfg, RODATA_SECT, 1); + emit_alignment (acfg, 8); + emit_info_symbol (acfg, symbol); + + for (i = 0; i < acfg->unwind_ops->len; ++i) { + guint32 index = GPOINTER_TO_UINT (g_ptr_array_index (acfg->unwind_ops, i)); + guint8 *unwind_info; + guint32 unwind_info_len; + guint8 buf [16]; + guint8 *p; + + unwind_info = mono_get_cached_unwind_info (index, &unwind_info_len); + + p = buf; + encode_value (unwind_info_len, p, &p); + emit_bytes (acfg, buf, p - buf); + emit_bytes (acfg, unwind_info, unwind_info_len); + + acfg->stats.unwind_info_size += (p - buf) + unwind_info_len; + } +} + +static void +emit_class_info (MonoAotCompile *acfg) +{ + int i; + gint32 *offsets; + + offsets = g_new0 (gint32, acfg->image->tables [MONO_TABLE_TYPEDEF].rows); + for (i = 0; i < acfg->image->tables [MONO_TABLE_TYPEDEF].rows; ++i) + offsets [i] = emit_klass_info (acfg, MONO_TOKEN_TYPE_DEF | (i + 1)); + + acfg->stats.offsets_size += emit_offset_table (acfg, "class_info_offsets", MONO_AOT_TABLE_CLASS_INFO_OFFSETS, acfg->image->tables [MONO_TABLE_TYPEDEF].rows, 10, offsets); + g_free (offsets); +} + +typedef struct ClassNameTableEntry { + guint32 token, index; + struct ClassNameTableEntry *next; +} ClassNameTableEntry; + +static void +emit_class_name_table (MonoAotCompile *acfg) +{ + int i, table_size, buf_size; + guint32 token, hash; + MonoClass *klass; + GPtrArray *table; + char *full_name; + guint8 *buf, *p; + ClassNameTableEntry *entry, *new_entry; + + /* + * Construct a chained hash table for mapping class names to typedef tokens. + */ + table_size = g_spaced_primes_closest ((int)(acfg->image->tables [MONO_TABLE_TYPEDEF].rows * 1.5)); + table = g_ptr_array_sized_new (table_size); + for (i = 0; i < table_size; ++i) + g_ptr_array_add (table, NULL); + for (i = 0; i < acfg->image->tables [MONO_TABLE_TYPEDEF].rows; ++i) { + MonoError error; + token = MONO_TOKEN_TYPE_DEF | (i + 1); + klass = mono_class_get_checked (acfg->image, token, &error); + if (!klass) { + mono_error_cleanup (&error); + continue; + } + full_name = mono_type_get_name_full (mono_class_get_type (klass), MONO_TYPE_NAME_FORMAT_FULL_NAME); + hash = mono_metadata_str_hash (full_name) % table_size; + g_free (full_name); + + /* FIXME: Allocate from the mempool */ + new_entry = g_new0 (ClassNameTableEntry, 1); + new_entry->token = token; + + entry = (ClassNameTableEntry *)g_ptr_array_index (table, hash); + if (entry == NULL) { + new_entry->index = hash; + g_ptr_array_index (table, hash) = new_entry; + } else { + while (entry->next) + entry = entry->next; + + entry->next = new_entry; + new_entry->index = table->len; + g_ptr_array_add (table, new_entry); + } + } + + /* Emit the table */ + buf_size = table->len * 4 + 4; + p = buf = (guint8 *)g_malloc0 (buf_size); + + /* FIXME: Optimize memory usage */ + g_assert (table_size < 65000); + encode_int16 (table_size, p, &p); + g_assert (table->len < 65000); + for (i = 0; i < table->len; ++i) { + ClassNameTableEntry *entry = (ClassNameTableEntry *)g_ptr_array_index (table, i); + + if (entry == NULL) { + encode_int16 (0, p, &p); + encode_int16 (0, p, &p); + } else { + encode_int16 (mono_metadata_token_index (entry->token), p, &p); + if (entry->next) + encode_int16 (entry->next->index, p, &p); + else + encode_int16 (0, p, &p); + } + g_free (entry); + } + g_assert (p - buf <= buf_size); + g_ptr_array_free (table, TRUE); + + emit_aot_data (acfg, MONO_AOT_TABLE_CLASS_NAME, "class_name_table", buf, p - buf); + + g_free (buf); +} + +static void +emit_image_table (MonoAotCompile *acfg) +{ + int i, buf_size; + guint8 *buf, *p; + + /* + * The image table is small but referenced in a lot of places. + * So we emit it at once, and reference its elements by an index. + */ + buf_size = acfg->image_table->len * 28 + 4; + for (i = 0; i < acfg->image_table->len; i++) { + MonoImage *image = (MonoImage*)g_ptr_array_index (acfg->image_table, i); + MonoAssemblyName *aname = &image->assembly->aname; + + buf_size += strlen (image->assembly_name) + strlen (image->guid) + (aname->culture ? strlen (aname->culture) : 1) + strlen ((char*)aname->public_key_token) + 4; + } + + buf = p = (guint8 *)g_malloc0 (buf_size); + encode_int (acfg->image_table->len, p, &p); + for (i = 0; i < acfg->image_table->len; i++) { + MonoImage *image = (MonoImage*)g_ptr_array_index (acfg->image_table, i); + MonoAssemblyName *aname = &image->assembly->aname; + + /* FIXME: Support multi-module assemblies */ + g_assert (image->assembly->image == image); + + encode_string (image->assembly_name, p, &p); + encode_string (image->guid, p, &p); + encode_string (aname->culture ? aname->culture : "", p, &p); + encode_string ((const char*)aname->public_key_token, p, &p); + + while (GPOINTER_TO_UINT (p) % 8 != 0) + p ++; + + encode_int (aname->flags, p, &p); + encode_int (aname->major, p, &p); + encode_int (aname->minor, p, &p); + encode_int (aname->build, p, &p); + encode_int (aname->revision, p, &p); + } + g_assert (p - buf <= buf_size); + + emit_aot_data (acfg, MONO_AOT_TABLE_IMAGE_TABLE, "image_table", buf, p - buf); + + g_free (buf); +} + +static void +emit_weak_field_indexes (MonoAotCompile *acfg) +{ + char symbol [128]; + GHashTable *indexes; + GHashTableIter iter; + gpointer key, value; + + /* Emit a table of weak field indexes, since computing these at runtime is expensive */ + sprintf (symbol, "weak_field_indexes"); + emit_section_change (acfg, ".text", 0); + emit_alignment_code (acfg, 8); + emit_info_symbol (acfg, symbol); + + mono_assembly_init_weak_fields (acfg->image); + indexes = acfg->image->weak_field_indexes; + g_assert (indexes); + + emit_int32 (acfg, g_hash_table_size (indexes)); + g_hash_table_iter_init (&iter, indexes); + while (g_hash_table_iter_next (&iter, &key, &value)) { + guint32 index = GPOINTER_TO_UINT (key); + emit_int32 (acfg, index); + } +} + +static void +emit_got_info (MonoAotCompile *acfg, gboolean llvm) +{ + int i, first_plt_got_patch = 0, buf_size; + guint8 *p, *buf; + guint32 *got_info_offsets; + GotInfo *info = llvm ? &acfg->llvm_got_info : &acfg->got_info; + + /* Add the patches needed by the PLT to the GOT */ + if (!llvm) { + acfg->plt_got_offset_base = acfg->got_offset; + first_plt_got_patch = info->got_patches->len; + for (i = 1; i < acfg->plt_offset; ++i) { + MonoPltEntry *plt_entry = (MonoPltEntry *)g_hash_table_lookup (acfg->plt_offset_to_entry, GUINT_TO_POINTER (i)); + + g_ptr_array_add (info->got_patches, plt_entry->ji); + + acfg->stats.got_slot_types [plt_entry->ji->type] ++; + } + + acfg->got_offset += acfg->plt_offset; + } + + /** + * FIXME: + * - optimize offsets table. + * - reduce number of exported symbols. + * - emit info for a klass only once. + * - determine when a method uses a GOT slot which is guaranteed to be already + * initialized. + * - clean up and document the code. + * - use String.Empty in class libs. + */ + + /* Encode info required to decode shared GOT entries */ + buf_size = info->got_patches->len * 128; + p = buf = (guint8 *)mono_mempool_alloc (acfg->mempool, buf_size); + got_info_offsets = (guint32 *)mono_mempool_alloc (acfg->mempool, info->got_patches->len * sizeof (guint32)); + if (!llvm) { + acfg->plt_got_info_offsets = (guint32 *)mono_mempool_alloc (acfg->mempool, acfg->plt_offset * sizeof (guint32)); + /* Unused */ + if (acfg->plt_offset) + acfg->plt_got_info_offsets [0] = 0; + } + for (i = 0; i < info->got_patches->len; ++i) { + MonoJumpInfo *ji = (MonoJumpInfo *)g_ptr_array_index (info->got_patches, i); + guint8 *p2; + + p = buf; + + encode_value (ji->type, p, &p); + p2 = p; + encode_patch (acfg, ji, p, &p); + acfg->stats.got_slot_info_sizes [ji->type] += p - p2; + g_assert (p - buf <= buf_size); + got_info_offsets [i] = add_to_blob (acfg, buf, p - buf); + + if (!llvm && i >= first_plt_got_patch) + acfg->plt_got_info_offsets [i - first_plt_got_patch + 1] = got_info_offsets [i]; + acfg->stats.got_info_size += p - buf; + } + + /* Emit got_info_offsets table */ + + /* No need to emit offsets for the got plt entries, the plt embeds them directly */ + acfg->stats.offsets_size += emit_offset_table (acfg, llvm ? "llvm_got_info_offsets" : "got_info_offsets", llvm ? MONO_AOT_TABLE_LLVM_GOT_INFO_OFFSETS : MONO_AOT_TABLE_GOT_INFO_OFFSETS, llvm ? acfg->llvm_got_offset : first_plt_got_patch, 10, (gint32*)got_info_offsets); +} + +static void +emit_got (MonoAotCompile *acfg) +{ + char symbol [MAX_SYMBOL_SIZE]; + + if (acfg->aot_opts.llvm_only) + return; + + /* Don't make GOT global so accesses to it don't need relocations */ + sprintf (symbol, "%s", acfg->got_symbol); + +#ifdef TARGET_MACH + emit_unset_mode (acfg); + fprintf (acfg->fp, ".section __DATA, __bss\n"); + emit_alignment (acfg, 8); + if (acfg->llvm) + emit_info_symbol (acfg, "jit_got"); + fprintf (acfg->fp, ".lcomm %s, %d\n", acfg->got_symbol, (int)(acfg->got_offset * sizeof (gpointer))); +#else + emit_section_change (acfg, ".bss", 0); + emit_alignment (acfg, 8); + if (acfg->aot_opts.write_symbols) + emit_local_symbol (acfg, symbol, "got_end", FALSE); + emit_label (acfg, symbol); + if (acfg->llvm) + emit_info_symbol (acfg, "jit_got"); + if (acfg->got_offset > 0) + emit_zero_bytes (acfg, (int)(acfg->got_offset * sizeof (gpointer))); +#endif + + sprintf (symbol, "got_end"); + emit_label (acfg, symbol); +} + +typedef struct GlobalsTableEntry { + guint32 value, index; + struct GlobalsTableEntry *next; +} GlobalsTableEntry; + +#ifdef TARGET_WIN32_MSVC +#define DLL_ENTRY_POINT "DllMain" + +static void +emit_library_info (MonoAotCompile *acfg) +{ + // Only include for shared libraries linked directly from generated object. + if (link_shared_library (acfg)) { + char *name = NULL; + char symbol [MAX_SYMBOL_SIZE]; + + // Ask linker to export all global symbols. + emit_section_change (acfg, ".drectve", 0); + for (guint i = 0; i < acfg->globals->len; ++i) { + name = (char *)g_ptr_array_index (acfg->globals, i); + g_assert (name != NULL); + sprintf_s (symbol, MAX_SYMBOL_SIZE, " /EXPORT:%s", name); + emit_string (acfg, symbol); + } + + // Emit DLLMain function, needed by MSVC linker for DLL's. + // NOTE, DllMain should not go into exports above. + emit_section_change (acfg, ".text", 0); + emit_global (acfg, DLL_ENTRY_POINT, TRUE); + emit_label (acfg, DLL_ENTRY_POINT); + + // Simple implementation of DLLMain, just returning TRUE. + // For more information about DLLMain: https://msdn.microsoft.com/en-us/library/windows/desktop/ms682583(v=vs.85).aspx + fprintf (acfg->fp, "movl $1, %%eax\n"); + fprintf (acfg->fp, "ret\n"); + + // Inform linker about our dll entry function. + emit_section_change (acfg, ".drectve", 0); + emit_string (acfg, "/ENTRY:" DLL_ENTRY_POINT); + return; + } +} + +#else + +static inline void +emit_library_info (MonoAotCompile *acfg) +{ + return; +} +#endif + +static void +emit_globals (MonoAotCompile *acfg) +{ + int i, table_size; + guint32 hash; + GPtrArray *table; + char symbol [1024]; + GlobalsTableEntry *entry, *new_entry; + + if (!acfg->aot_opts.static_link) + return; + + if (acfg->aot_opts.llvm_only) { + g_assert (acfg->globals->len == 0); + return; + } + + /* + * When static linking, we emit a table containing our globals. + */ + + /* + * Construct a chained hash table for mapping global names to their index in + * the globals table. + */ + table_size = g_spaced_primes_closest ((int)(acfg->globals->len * 1.5)); + table = g_ptr_array_sized_new (table_size); + for (i = 0; i < table_size; ++i) + g_ptr_array_add (table, NULL); + for (i = 0; i < acfg->globals->len; ++i) { + char *name = (char *)g_ptr_array_index (acfg->globals, i); + + hash = mono_metadata_str_hash (name) % table_size; + + /* FIXME: Allocate from the mempool */ + new_entry = g_new0 (GlobalsTableEntry, 1); + new_entry->value = i; + + entry = (GlobalsTableEntry *)g_ptr_array_index (table, hash); + if (entry == NULL) { + new_entry->index = hash; + g_ptr_array_index (table, hash) = new_entry; + } else { + while (entry->next) + entry = entry->next; + + entry->next = new_entry; + new_entry->index = table->len; + g_ptr_array_add (table, new_entry); + } + } + + /* Emit the table */ + sprintf (symbol, ".Lglobals_hash"); + emit_section_change (acfg, RODATA_SECT, 0); + emit_alignment (acfg, 8); + emit_label (acfg, symbol); + + /* FIXME: Optimize memory usage */ + g_assert (table_size < 65000); + emit_int16 (acfg, table_size); + for (i = 0; i < table->len; ++i) { + GlobalsTableEntry *entry = (GlobalsTableEntry *)g_ptr_array_index (table, i); + + if (entry == NULL) { + emit_int16 (acfg, 0); + emit_int16 (acfg, 0); + } else { + emit_int16 (acfg, entry->value + 1); + if (entry->next) + emit_int16 (acfg, entry->next->index); + else + emit_int16 (acfg, 0); + } + } + + /* Emit the names */ + for (i = 0; i < acfg->globals->len; ++i) { + char *name = (char *)g_ptr_array_index (acfg->globals, i); + + sprintf (symbol, "name_%d", i); + emit_section_change (acfg, RODATA_SECT, 1); +#ifdef TARGET_MACH + emit_alignment (acfg, 4); +#endif + emit_label (acfg, symbol); + emit_string (acfg, name); + } + + /* Emit the globals table */ + sprintf (symbol, "globals"); + emit_section_change (acfg, ".data", 0); + /* This is not a global, since it is accessed by the init function */ + emit_alignment (acfg, 8); + emit_info_symbol (acfg, symbol); + + sprintf (symbol, "%sglobals_hash", acfg->temp_prefix); + emit_pointer (acfg, symbol); + + for (i = 0; i < acfg->globals->len; ++i) { + char *name = (char *)g_ptr_array_index (acfg->globals, i); + + sprintf (symbol, "name_%d", i); + emit_pointer (acfg, symbol); + + g_assert (strlen (name) < sizeof (symbol)); + sprintf (symbol, "%s", name); + emit_pointer (acfg, symbol); + } + /* Null terminate the table */ + emit_int32 (acfg, 0); + emit_int32 (acfg, 0); +} + +static void +emit_mem_end (MonoAotCompile *acfg) +{ + char symbol [128]; + + if (acfg->aot_opts.llvm_only) + return; + + sprintf (symbol, "mem_end"); + emit_section_change (acfg, ".text", 1); + emit_alignment_code (acfg, 8); + emit_label (acfg, symbol); +} + +static void +init_aot_file_info (MonoAotCompile *acfg, MonoAotFileInfo *info) +{ + int i; + + info->version = MONO_AOT_FILE_VERSION; + info->plt_got_offset_base = acfg->plt_got_offset_base; + info->got_size = acfg->got_offset * sizeof (gpointer); + info->plt_size = acfg->plt_offset; + info->nmethods = acfg->nmethods; + info->flags = acfg->flags; + info->opts = acfg->opts; + info->simd_opts = acfg->simd_opts; + info->gc_name_index = acfg->gc_name_offset; + info->datafile_size = acfg->datafile_offset; + for (i = 0; i < MONO_AOT_TABLE_NUM; ++i) + info->table_offsets [i] = acfg->table_offsets [i]; + for (i = 0; i < MONO_AOT_TRAMP_NUM; ++i) + info->num_trampolines [i] = acfg->num_trampolines [i]; + for (i = 0; i < MONO_AOT_TRAMP_NUM; ++i) + info->trampoline_got_offset_base [i] = acfg->trampoline_got_offset_base [i]; + for (i = 0; i < MONO_AOT_TRAMP_NUM; ++i) + info->trampoline_size [i] = acfg->trampoline_size [i]; + info->num_rgctx_fetch_trampolines = acfg->aot_opts.nrgctx_fetch_trampolines; + + info->double_align = MONO_ABI_ALIGNOF (double); + info->long_align = MONO_ABI_ALIGNOF (gint64); + info->generic_tramp_num = MONO_TRAMPOLINE_NUM; + info->tramp_page_size = acfg->tramp_page_size; + info->nshared_got_entries = acfg->nshared_got_entries; + for (i = 0; i < MONO_AOT_TRAMP_NUM; ++i) + info->tramp_page_code_offsets [i] = acfg->tramp_page_code_offsets [i]; + + memcpy(&info->aotid, acfg->image->aotid, 16); +} + +static void +emit_aot_file_info (MonoAotCompile *acfg, MonoAotFileInfo *info) +{ + char symbol [MAX_SYMBOL_SIZE]; + int i, sindex; + const char **symbols; + + symbols = g_new0 (const char *, MONO_AOT_FILE_INFO_NUM_SYMBOLS); + sindex = 0; + symbols [sindex ++] = acfg->got_symbol; + if (acfg->llvm) { + symbols [sindex ++] = g_strdup_printf ("%s%s", acfg->user_symbol_prefix, acfg->llvm_got_symbol); + symbols [sindex ++] = acfg->llvm_eh_frame_symbol; + } else { + symbols [sindex ++] = NULL; + symbols [sindex ++] = NULL; + } + /* llvm_get_method */ + symbols [sindex ++] = NULL; + /* llvm_get_unbox_tramp */ + symbols [sindex ++] = NULL; + if (!acfg->aot_opts.llvm_only) { + symbols [sindex ++] = "jit_code_start"; + symbols [sindex ++] = "jit_code_end"; + symbols [sindex ++] = "method_addresses"; + } else { + symbols [sindex ++] = NULL; + symbols [sindex ++] = NULL; + symbols [sindex ++] = NULL; + } + if (acfg->data_outfile) { + for (i = 0; i < MONO_AOT_TABLE_NUM; ++i) + symbols [sindex ++] = NULL; + } else { + symbols [sindex ++] = "blob"; + symbols [sindex ++] = "class_name_table"; + symbols [sindex ++] = "class_info_offsets"; + symbols [sindex ++] = "method_info_offsets"; + symbols [sindex ++] = "ex_info_offsets"; + symbols [sindex ++] = "extra_method_info_offsets"; + symbols [sindex ++] = "extra_method_table"; + symbols [sindex ++] = "got_info_offsets"; + if (acfg->llvm) + symbols [sindex ++] = "llvm_got_info_offsets"; + else + symbols [sindex ++] = NULL; + symbols [sindex ++] = "image_table"; + } + symbols [sindex ++] = "mem_end"; + symbols [sindex ++] = "assembly_guid"; + symbols [sindex ++] = "runtime_version"; + if (acfg->num_trampoline_got_entries) { + symbols [sindex ++] = "specific_trampolines"; + symbols [sindex ++] = "static_rgctx_trampolines"; + symbols [sindex ++] = "imt_trampolines"; + symbols [sindex ++] = "gsharedvt_arg_trampolines"; + } else { + symbols [sindex ++] = NULL; + symbols [sindex ++] = NULL; + symbols [sindex ++] = NULL; + symbols [sindex ++] = NULL; + } + if (acfg->aot_opts.static_link) { + symbols [sindex ++] = "globals"; + } else { + symbols [sindex ++] = NULL; + } + symbols [sindex ++] = "assembly_name"; + symbols [sindex ++] = "plt"; + symbols [sindex ++] = "plt_end"; + symbols [sindex ++] = "unwind_info"; + if (!acfg->aot_opts.llvm_only) { + symbols [sindex ++] = "unbox_trampolines"; + symbols [sindex ++] = "unbox_trampolines_end"; + symbols [sindex ++] = "unbox_trampoline_addresses"; + } else { + symbols [sindex ++] = NULL; + symbols [sindex ++] = NULL; + symbols [sindex ++] = NULL; + } + symbols [sindex ++] = "weak_field_indexes"; + + g_assert (sindex == MONO_AOT_FILE_INFO_NUM_SYMBOLS); + + sprintf (symbol, "%smono_aot_file_info", acfg->user_symbol_prefix); + emit_section_change (acfg, ".data", 0); + emit_alignment (acfg, 8); + emit_label (acfg, symbol); + if (!acfg->aot_opts.static_link) + emit_global (acfg, symbol, FALSE); + + /* The data emitted here must match MonoAotFileInfo. */ + + emit_int32 (acfg, info->version); + emit_int32 (acfg, info->dummy); + + /* + * We emit pointers to our data structures instead of emitting global symbols which + * point to them, to reduce the number of globals, and because using globals leads to + * various problems (i.e. arm/thumb). + */ + for (i = 0; i < MONO_AOT_FILE_INFO_NUM_SYMBOLS; ++i) + emit_pointer (acfg, symbols [i]); + + emit_int32 (acfg, info->plt_got_offset_base); + emit_int32 (acfg, info->got_size); + emit_int32 (acfg, info->plt_size); + emit_int32 (acfg, info->nmethods); + emit_int32 (acfg, info->flags); + emit_int32 (acfg, info->opts); + emit_int32 (acfg, info->simd_opts); + emit_int32 (acfg, info->gc_name_index); + emit_int32 (acfg, info->num_rgctx_fetch_trampolines); + emit_int32 (acfg, info->double_align); + emit_int32 (acfg, info->long_align); + emit_int32 (acfg, info->generic_tramp_num); + emit_int32 (acfg, info->tramp_page_size); + emit_int32 (acfg, info->nshared_got_entries); + emit_int32 (acfg, info->datafile_size); + + for (i = 0; i < MONO_AOT_TABLE_NUM; ++i) + emit_int32 (acfg, info->table_offsets [i]); + for (i = 0; i < MONO_AOT_TRAMP_NUM; ++i) + emit_int32 (acfg, info->num_trampolines [i]); + for (i = 0; i < MONO_AOT_TRAMP_NUM; ++i) + emit_int32 (acfg, info->trampoline_got_offset_base [i]); + for (i = 0; i < MONO_AOT_TRAMP_NUM; ++i) + emit_int32 (acfg, info->trampoline_size [i]); + for (i = 0; i < MONO_AOT_TRAMP_NUM; ++i) + emit_int32 (acfg, info->tramp_page_code_offsets [i]); + + emit_bytes (acfg, info->aotid, 16); + + if (acfg->aot_opts.static_link) { + emit_global_inner (acfg, acfg->static_linking_symbol, FALSE); + emit_alignment (acfg, sizeof (gpointer)); + emit_label (acfg, acfg->static_linking_symbol); + emit_pointer_2 (acfg, acfg->user_symbol_prefix, "mono_aot_file_info"); + } +} + +/* + * Emit a structure containing all the information not stored elsewhere. + */ +static void +emit_file_info (MonoAotCompile *acfg) +{ + char *build_info; + MonoAotFileInfo *info; + + if (acfg->aot_opts.bind_to_runtime_version) { + build_info = mono_get_runtime_build_info (); + emit_string_symbol (acfg, "runtime_version", build_info); + g_free (build_info); + } else { + emit_string_symbol (acfg, "runtime_version", ""); + } + + emit_string_symbol (acfg, "assembly_guid" , acfg->image->guid); + + /* Emit a string holding the assembly name */ + emit_string_symbol (acfg, "assembly_name", acfg->image->assembly->aname.name); + + info = g_new0 (MonoAotFileInfo, 1); + init_aot_file_info (acfg, info); + + if (acfg->aot_opts.static_link) { + char symbol [MAX_SYMBOL_SIZE]; + char *p; + + /* + * Emit a global symbol which can be passed by an embedding app to + * mono_aot_register_module (). The symbol points to a pointer to the the file info + * structure. + */ + sprintf (symbol, "%smono_aot_module_%s_info", acfg->user_symbol_prefix, acfg->image->assembly->aname.name); + + /* Get rid of characters which cannot occur in symbols */ + p = symbol; + for (p = symbol; *p; ++p) { + if (!(isalnum (*p) || *p == '_')) + *p = '_'; + } + acfg->static_linking_symbol = g_strdup (symbol); + } + + if (acfg->llvm) + mono_llvm_emit_aot_file_info (info, acfg->has_jitted_code); + else + emit_aot_file_info (acfg, info); +} + +static void +emit_blob (MonoAotCompile *acfg) +{ + acfg->blob_closed = TRUE; + + emit_aot_data (acfg, MONO_AOT_TABLE_BLOB, "blob", (guint8*)acfg->blob.data, acfg->blob.index); +} + +static void +emit_objc_selectors (MonoAotCompile *acfg) +{ + int i; + char symbol [128]; + + if (!acfg->objc_selectors || acfg->objc_selectors->len == 0) + return; + + /* + * From + * cat > foo.m << EOF + * void *ret () + * { + * return @selector(print:); + * } + * EOF + */ + + mono_img_writer_emit_unset_mode (acfg->w); + g_assert (acfg->fp); + fprintf (acfg->fp, ".section __DATA,__objc_selrefs,literal_pointers,no_dead_strip\n"); + fprintf (acfg->fp, ".align 3\n"); + for (i = 0; i < acfg->objc_selectors->len; ++i) { + sprintf (symbol, "L_OBJC_SELECTOR_REFERENCES_%d", i); + emit_label (acfg, symbol); + sprintf (symbol, "L_OBJC_METH_VAR_NAME_%d", i); + emit_pointer (acfg, symbol); + + } + fprintf (acfg->fp, ".section __TEXT,__cstring,cstring_literals\n"); + for (i = 0; i < acfg->objc_selectors->len; ++i) { + fprintf (acfg->fp, "L_OBJC_METH_VAR_NAME_%d:\n", i); + fprintf (acfg->fp, ".asciz \"%s\"\n", (char*)g_ptr_array_index (acfg->objc_selectors, i)); + } + + fprintf (acfg->fp, ".section __DATA,__objc_imageinfo,regular,no_dead_strip\n"); + fprintf (acfg->fp, ".align 3\n"); + fprintf (acfg->fp, "L_OBJC_IMAGE_INFO:\n"); + fprintf (acfg->fp, ".long 0\n"); + fprintf (acfg->fp, ".long 16\n"); +} + +static void +emit_dwarf_info (MonoAotCompile *acfg) +{ +#ifdef EMIT_DWARF_INFO + int i; + char symbol2 [128]; + + /* DIEs for methods */ + for (i = 0; i < acfg->nmethods; ++i) { + MonoCompile *cfg = acfg->cfgs [i]; + + if (ignore_cfg (cfg)) + continue; + + // FIXME: LLVM doesn't define .Lme_... + if (cfg->compile_llvm) + continue; + + sprintf (symbol2, "%sme_%x", acfg->temp_prefix, i); + + mono_dwarf_writer_emit_method (acfg->dwarf, cfg, cfg->method, cfg->asm_symbol, symbol2, cfg->asm_debug_symbol, (guint8 *)cfg->jit_info->code_start, cfg->jit_info->code_size, cfg->args, cfg->locals, cfg->unwind_ops, mono_debug_find_method (cfg->jit_info->d.method, mono_domain_get ())); + } +#endif +} + +#ifdef EMIT_WIN32_CODEVIEW_INFO +typedef struct _CodeViewSubSectionData +{ + gchar *start_section; + gchar *end_section; + gchar *start_section_record; + gchar *end_section_record; + int section_type; + int section_record_type; + int section_id; +} CodeViewSubsectionData; + +typedef struct _CodeViewCompilerVersion +{ + gint major; + gint minor; + gint revision; + gint patch; +} CodeViewCompilerVersion; + +#define CODEVIEW_SUBSECTION_SYMBOL_TYPE 0xF1 +#define CODEVIEW_SUBSECTION_RECORD_COMPILER_TYPE 0x113c +#define CODEVIEW_SUBSECTION_RECORD_FUNCTION_START_TYPE 0x1147 +#define CODEVIEW_SUBSECTION_RECORD_FUNCTION_END_TYPE 0x114F +#define CODEVIEW_CSHARP_LANGUAGE_TYPE 0x0A +#define CODEVIEW_CPU_TYPE 0x0 +#define CODEVIEW_MAGIC_HEADER 0x4 + +static void +codeview_clear_subsection_data (CodeViewSubsectionData *section_data) +{ + g_free (section_data->start_section); + g_free (section_data->end_section); + g_free (section_data->start_section_record); + g_free (section_data->end_section_record); + + memset (section_data, 0, sizeof (CodeViewSubsectionData)); +} + +static void +codeview_parse_compiler_version (gchar *version, CodeViewCompilerVersion *data) +{ + gint values[4] = { 0 }; + gint *value = values; + + while (*version && (value < values + G_N_ELEMENTS (values))) { + if (isdigit (*version)) { + *value *= 10; + *value += *version - '0'; + } + else if (*version == '.') { + value++; + } + + version++; + } + + data->major = values[0]; + data->minor = values[1]; + data->revision = values[2]; + data->patch = values[3]; +} + +static void +emit_codeview_start_subsection (MonoAotCompile *acfg, int section_id, int section_type, int section_record_type, CodeViewSubsectionData *section_data) +{ + // Starting a new subsection, clear old data. + codeview_clear_subsection_data (section_data); + + // Keep subsection data. + section_data->section_id = section_id; + section_data->section_type = section_type; + section_data->section_record_type = section_record_type; + + // Allocate all labels used in subsection. + section_data->start_section = g_strdup_printf ("%scvs_%d", acfg->temp_prefix, section_data->section_id); + section_data->end_section = g_strdup_printf ("%scvse_%d", acfg->temp_prefix, section_data->section_id); + section_data->start_section_record = g_strdup_printf ("%scvsr_%d", acfg->temp_prefix, section_data->section_id); + section_data->end_section_record = g_strdup_printf ("%scvsre_%d", acfg->temp_prefix, section_data->section_id); + + // Subsection type, function symbol. + emit_int32 (acfg, section_data->section_type); + + // Subsection size. + emit_symbol_diff (acfg, section_data->end_section, section_data->start_section, 0); + emit_label (acfg, section_data->start_section); + + // Subsection record size. + fprintf (acfg->fp, "\t.word %s - %s\n", section_data->end_section_record, section_data->start_section_record); + emit_label (acfg, section_data->start_section_record); + + // Subsection record type. + emit_int16 (acfg, section_record_type); +} + +static void +emit_codeview_end_subsection (MonoAotCompile *acfg, CodeViewSubsectionData *section_data, int *section_id) +{ + g_assert (section_data->start_section); + g_assert (section_data->end_section); + g_assert (section_data->start_section_record); + g_assert (section_data->end_section_record); + + emit_label (acfg, section_data->end_section_record); + + if (section_data->section_record_type == CODEVIEW_SUBSECTION_RECORD_FUNCTION_START_TYPE) { + // Emit record length. + emit_int16 (acfg, 2); + + // Emit specific record type end. + emit_int16 (acfg, CODEVIEW_SUBSECTION_RECORD_FUNCTION_END_TYPE); + } + + emit_label (acfg, section_data->end_section); + + // Next subsection needs to be 4 byte aligned. + emit_alignment (acfg, 4); + + *section_id = section_data->section_id + 1; + codeview_clear_subsection_data (section_data); +} + +inline static void +emit_codeview_start_symbol_subsection (MonoAotCompile *acfg, int section_id, int section_record_type, CodeViewSubsectionData *section_data) +{ + emit_codeview_start_subsection (acfg, section_id, CODEVIEW_SUBSECTION_SYMBOL_TYPE, section_record_type, section_data); +} + +inline static void +emit_codeview_end_symbol_subsection (MonoAotCompile *acfg, CodeViewSubsectionData *section_data, int *section_id) +{ + emit_codeview_end_subsection (acfg, section_data, section_id); +} + +static void +emit_codeview_compiler_info (MonoAotCompile *acfg, int *section_id) +{ + CodeViewSubsectionData section_data = { 0 }; + CodeViewCompilerVersion compiler_version = { 0 }; + + // Start new compiler record subsection. + emit_codeview_start_symbol_subsection (acfg, *section_id, CODEVIEW_SUBSECTION_RECORD_COMPILER_TYPE, §ion_data); + + emit_int32 (acfg, CODEVIEW_CSHARP_LANGUAGE_TYPE); + emit_int16 (acfg, CODEVIEW_CPU_TYPE); + + // Get compiler version information. + codeview_parse_compiler_version (VERSION, &compiler_version); + + // Compiler frontend version, 4 digits. + emit_int16 (acfg, compiler_version.major); + emit_int16 (acfg, compiler_version.minor); + emit_int16 (acfg, compiler_version.revision); + emit_int16 (acfg, compiler_version.patch); + + // Compiler backend version, 4 digits (currently same as frontend). + emit_int16 (acfg, compiler_version.major); + emit_int16 (acfg, compiler_version.minor); + emit_int16 (acfg, compiler_version.revision); + emit_int16 (acfg, compiler_version.patch); + + // Compiler string. + emit_string (acfg, "Mono AOT compiler"); + + // Done with section. + emit_codeview_end_symbol_subsection (acfg, §ion_data, section_id); +} + +static void +emit_codeview_function_info (MonoAotCompile *acfg, MonoMethod *method, int *section_id, gchar *symbol, gchar *symbol_start, gchar *symbol_end) +{ + CodeViewSubsectionData section_data = { 0 }; + gchar *full_method_name = NULL; + + // Start new function record subsection. + emit_codeview_start_symbol_subsection (acfg, *section_id, CODEVIEW_SUBSECTION_RECORD_FUNCTION_START_TYPE, §ion_data); + + // Emit 3 int 0 byte padding, currently not used. + emit_zero_bytes (acfg, sizeof (int) * 3); + + // Emit size of function. + emit_symbol_diff (acfg, symbol_end, symbol_start, 0); + + // Emit 3 int 0 byte padding, currently not used. + emit_zero_bytes (acfg, sizeof (int) * 3); + + // Emit reallocation info. + fprintf (acfg->fp, "\t.secrel32 %s\n", symbol); + fprintf (acfg->fp, "\t.secidx %s\n", symbol); + + // Emit flag, currently not used. + emit_zero_bytes (acfg, 1); + + // Emit function name, exclude signature since it should be described by own metadata. + full_method_name = mono_method_full_name (method, FALSE); + emit_string (acfg, full_method_name ? full_method_name : ""); + g_free (full_method_name); + + // Done with section. + emit_codeview_end_symbol_subsection (acfg, §ion_data, section_id); +} + +static void +emit_codeview_info (MonoAotCompile *acfg) +{ + int i; + int section_id = 0; + gchar symbol_buffer[MAX_SYMBOL_SIZE]; + + // Emit codeview debug info section + emit_section_change (acfg, ".debug$S", 0); + + // Emit magic header. + emit_int32 (acfg, CODEVIEW_MAGIC_HEADER); + + emit_codeview_compiler_info (acfg, §ion_id); + + for (i = 0; i < acfg->nmethods; ++i) { + MonoCompile *cfg = acfg->cfgs[i]; + + if (!cfg) + continue; + + int ret = g_snprintf (symbol_buffer, G_N_ELEMENTS (symbol_buffer), "%sme_%x", acfg->temp_prefix, i); + if (ret > 0 && ret < G_N_ELEMENTS (symbol_buffer)) + emit_codeview_function_info (acfg, cfg->method, §ion_id, cfg->asm_debug_symbol, cfg->asm_symbol, symbol_buffer); + } +} +#else +static void +emit_codeview_info (MonoAotCompile *acfg) +{ +} +#endif /* EMIT_WIN32_CODEVIEW_INFO */ + +#ifdef EMIT_WIN32_UNWIND_INFO +static UnwindInfoSectionCacheItem * +get_cached_unwind_info_section_item_win32 (MonoAotCompile *acfg, const char *function_start, const char *function_end, GSList *unwind_ops) +{ + UnwindInfoSectionCacheItem *item = NULL; + + if (!acfg->unwind_info_section_cache) + acfg->unwind_info_section_cache = g_list_alloc (); + + PUNWIND_INFO unwind_info = mono_arch_unwindinfo_alloc_unwind_info (unwind_ops); + + // Search for unwind info in cache. + GList *list = acfg->unwind_info_section_cache; + int list_size = 0; + while (list && list->data) { + item = (UnwindInfoSectionCacheItem*)list->data; + if (!memcmp (unwind_info, item->unwind_info, sizeof (UNWIND_INFO))) { + // Cache hit, return cached item. + return item; + } + list = list->next; + list_size++; + } + + // Add to cache. + if (acfg->unwind_info_section_cache) { + item = g_new0 (UnwindInfoSectionCacheItem, 1); + if (item) { + // Format .xdata section label for function, used to get unwind info address RVA. + // Since the unwind info is similar for most functions, the symbol will be reused. + item->xdata_section_label = g_strdup_printf ("%sunwind_%d", acfg->temp_prefix, list_size); + + // Cache unwind info data, used when checking cache for matching unwind info. NOTE, cache takes + //over ownership of unwind info. + item->unwind_info = unwind_info; + + // Needs to be emitted once. + item->xdata_section_emitted = FALSE; + + // Prepend to beginning of list to speed up inserts. + acfg->unwind_info_section_cache = g_list_prepend (acfg->unwind_info_section_cache, (gpointer)item); + } + } + + return item; +} + +static void +free_unwind_info_section_cache_win32 (MonoAotCompile *acfg) +{ + GList *list = acfg->unwind_info_section_cache; + + while (list) { + UnwindInfoSectionCacheItem *item = (UnwindInfoSectionCacheItem *)list->data; + if (item) { + g_free (item->xdata_section_label); + mono_arch_unwindinfo_free_unwind_info (item->unwind_info); + + g_free (item); + list->data = NULL; + } + + list = list->next; + } + + g_list_free (acfg->unwind_info_section_cache); + acfg->unwind_info_section_cache = NULL; +} + +static void +emit_unwind_info_data_win32 (MonoAotCompile *acfg, PUNWIND_INFO unwind_info) +{ + // Emit the unwind info struct. + emit_bytes (acfg, (guint8*)unwind_info, sizeof (UNWIND_INFO) - (sizeof (UNWIND_CODE) * MONO_MAX_UNWIND_CODES)); + + // Emit all unwind codes encoded in unwind info struct. + PUNWIND_CODE current_unwind_node = &unwind_info->UnwindCode[MONO_MAX_UNWIND_CODES - unwind_info->CountOfCodes]; + PUNWIND_CODE last_unwind_node = &unwind_info->UnwindCode[MONO_MAX_UNWIND_CODES]; + + while (current_unwind_node < last_unwind_node) { + guint8 node_count = 0; + switch (current_unwind_node->UnwindOp) { + case UWOP_PUSH_NONVOL: + case UWOP_ALLOC_SMALL: + case UWOP_SET_FPREG: + case UWOP_PUSH_MACHFRAME: + node_count = 1; + break; + case UWOP_SAVE_NONVOL: + case UWOP_SAVE_XMM128: + node_count = 2; + break; + case UWOP_SAVE_NONVOL_FAR: + case UWOP_SAVE_XMM128_FAR: + node_count = 3; + break; + case UWOP_ALLOC_LARGE: + if (current_unwind_node->OpInfo == 0) + node_count = 2; + else + node_count = 3; + break; + default: + g_assert (!"Unknown unwind opcode."); + } + + while (node_count > 0) { + g_assert (current_unwind_node < last_unwind_node); + + //Emit current node. + emit_bytes (acfg, (guint8*)current_unwind_node, sizeof (UNWIND_CODE)); + + node_count--; + current_unwind_node++; + } + } +} + +// Emit unwind info sections for each function. Unwind info on Windows x64 is emitted into two different sections. +// .pdata includes the serialized DWORD aligned RVA's of function start, end and address of serialized +// UNWIND_INFO struct emitted into .xdata, see https://msdn.microsoft.com/en-us/library/ft9x1kdx.aspx. +// .xdata section includes DWORD aligned serialized version of UNWIND_INFO struct, https://msdn.microsoft.com/en-us/library/ddssxxy8.aspx. +static void +emit_unwind_info_sections_win32 (MonoAotCompile *acfg, const char *function_start, const char *function_end, GSList *unwind_ops) +{ + char *pdata_section_label = NULL; + + int temp_prefix_len = (acfg->temp_prefix != NULL) ? strlen (acfg->temp_prefix) : 0; + if (strncmp (function_start, acfg->temp_prefix, temp_prefix_len)) { + temp_prefix_len = 0; + } + + // Format .pdata section label for function. + pdata_section_label = g_strdup_printf ("%spdata_%s", acfg->temp_prefix, function_start + temp_prefix_len); + + UnwindInfoSectionCacheItem *cache_item = get_cached_unwind_info_section_item_win32 (acfg, function_start, function_end, unwind_ops); + g_assert (cache_item && cache_item->xdata_section_label && cache_item->unwind_info); + + // Emit .pdata section. + emit_section_change (acfg, ".pdata", 0); + emit_alignment (acfg, sizeof (DWORD)); + emit_label (acfg, pdata_section_label); + + // Emit function start address RVA. + fprintf (acfg->fp, "\t.long %s@IMGREL\n", function_start); + + // Emit function end address RVA. + fprintf (acfg->fp, "\t.long %s@IMGREL\n", function_end); + + // Emit unwind info address RVA. + fprintf (acfg->fp, "\t.long %s@IMGREL\n", cache_item->xdata_section_label); + + if (!cache_item->xdata_section_emitted) { + // Emit .xdata section. + emit_section_change (acfg, ".xdata", 0); + emit_alignment (acfg, sizeof (DWORD)); + emit_label (acfg, cache_item->xdata_section_label); + + // Emit unwind info into .xdata section. + emit_unwind_info_data_win32 (acfg, cache_item->unwind_info); + cache_item->xdata_section_emitted = TRUE; + } + + g_free (pdata_section_label); +} +#endif + +static gboolean +collect_methods (MonoAotCompile *acfg) +{ + int mindex, i; + MonoImage *image = acfg->image; + + /* Collect methods */ + for (i = 0; i < image->tables [MONO_TABLE_METHOD].rows; ++i) { + MonoError error; + MonoMethod *method; + guint32 token = MONO_TOKEN_METHOD_DEF | (i + 1); + + method = mono_get_method_checked (acfg->image, token, NULL, NULL, &error); + + if (!method) { + aot_printerrf (acfg, "Failed to load method 0x%x from '%s' due to %s.\n", token, image->name, mono_error_get_message (&error)); + aot_printerrf (acfg, "Run with MONO_LOG_LEVEL=debug for more information.\n"); + mono_error_cleanup (&error); + return FALSE; + } + + /* Load all methods eagerly to skip the slower lazy loading code */ + mono_class_setup_methods (method->klass); + + if (mono_aot_mode_is_full (&acfg->aot_opts) && method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) { + /* Compile the wrapper instead */ + /* We do this here instead of add_wrappers () because it is easy to do it here */ + MonoMethod *wrapper = mono_marshal_get_native_wrapper (method, TRUE, TRUE); + method = wrapper; + } + + /* FIXME: Some mscorlib methods don't have debug info */ + /* + if (acfg->aot_opts.soft_debug && !method->wrapper_type) { + if (!((method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) || + (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) || + (method->flags & METHOD_ATTRIBUTE_ABSTRACT) || + (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL))) { + if (!mono_debug_lookup_method (method)) { + fprintf (stderr, "Method %s has no debug info, probably the .mdb file for the assembly is missing.\n", mono_method_get_full_name (method)); + exit (1); + } + } + } + */ + + if (method->is_generic || mono_class_is_gtd (method->klass)) + /* Compile the ref shared version instead */ + method = mini_get_shared_method (method); + + /* Since we add the normal methods first, their index will be equal to their zero based token index */ + add_method_with_index (acfg, method, i, FALSE); + acfg->method_index ++; + } + + /* gsharedvt methods */ + for (mindex = 0; mindex < image->tables [MONO_TABLE_METHOD].rows; ++mindex) { + MonoError error; + MonoMethod *method; + guint32 token = MONO_TOKEN_METHOD_DEF | (mindex + 1); + + if (!(acfg->opts & MONO_OPT_GSHAREDVT)) + continue; + + method = mono_get_method_checked (acfg->image, token, NULL, NULL, &error); + report_loader_error (acfg, &error, TRUE, "Failed to load method token 0x%x due to %s\n", i, mono_error_get_message (&error)); + + if (method->is_generic || mono_class_is_gtd (method->klass)) { + MonoMethod *gshared; + + gshared = mini_get_shared_method_full (method, TRUE, TRUE); + add_extra_method (acfg, gshared); + } + } + + if (mono_aot_mode_is_full (&acfg->aot_opts)) + add_generic_instances (acfg); + + if (mono_aot_mode_is_full (&acfg->aot_opts)) + add_wrappers (acfg); + return TRUE; +} + +static void +compile_methods (MonoAotCompile *acfg) +{ + int i, methods_len; + + if (acfg->aot_opts.nthreads > 0) { + GPtrArray *frag; + int len, j; + GPtrArray *threads; + MonoThreadHandle *thread_handle; + gpointer *user_data; + MonoMethod **methods; + + methods_len = acfg->methods->len; + + len = acfg->methods->len / acfg->aot_opts.nthreads; + g_assert (len > 0); + /* + * Partition the list of methods into fragments, and hand it to threads to + * process. + */ + threads = g_ptr_array_new (); + /* Make a copy since acfg->methods is modified by compile_method () */ + methods = g_new0 (MonoMethod*, methods_len); + //memcpy (methods, g_ptr_array_index (acfg->methods, 0), sizeof (MonoMethod*) * methods_len); + for (i = 0; i < methods_len; ++i) + methods [i] = (MonoMethod *)g_ptr_array_index (acfg->methods, i); + i = 0; + while (i < methods_len) { + MonoError error; + MonoInternalThread *thread; + + frag = g_ptr_array_new (); + for (j = 0; j < len; ++j) { + if (i < methods_len) { + g_ptr_array_add (frag, methods [i]); + i ++; + } + } + + user_data = g_new0 (gpointer, 3); + user_data [0] = acfg; + user_data [1] = frag; + + thread = mono_thread_create_internal (mono_domain_get (), compile_thread_main, (gpointer) user_data, MONO_THREAD_CREATE_FLAGS_NONE, &error); + mono_error_assert_ok (&error); + + thread_handle = mono_threads_open_thread_handle (thread->handle); + g_ptr_array_add (threads, thread_handle); + } + g_free (methods); + + for (i = 0; i < threads->len; ++i) { + mono_thread_info_wait_one_handle (g_ptr_array_index (threads, i), MONO_INFINITE_WAIT, FALSE); + mono_threads_close_thread_handle (g_ptr_array_index (threads, i)); + } + } else { + methods_len = 0; + } + + /* Compile methods added by compile_method () or all methods if nthreads == 0 */ + for (i = methods_len; i < acfg->methods->len; ++i) { + /* This can add new methods to acfg->methods */ + compile_method (acfg, (MonoMethod *)g_ptr_array_index (acfg->methods, i)); + } +} + +static int +compile_asm (MonoAotCompile *acfg) +{ + char *command, *objfile; + char *outfile_name, *tmp_outfile_name, *llvm_ofile; + const char *tool_prefix = acfg->aot_opts.tool_prefix ? acfg->aot_opts.tool_prefix : ""; + char *ld_flags = acfg->aot_opts.ld_flags ? acfg->aot_opts.ld_flags : g_strdup(""); + +#ifdef TARGET_WIN32_MSVC +#define AS_OPTIONS "-c -x assembler" +#elif defined(TARGET_AMD64) && !defined(TARGET_MACH) +#define AS_OPTIONS "--64" +#elif defined(TARGET_POWERPC64) +#define AS_OPTIONS "-a64 -mppc64" +#elif defined(sparc) && SIZEOF_VOID_P == 8 +#define AS_OPTIONS "-xarch=v9" +#elif defined(TARGET_X86) && defined(TARGET_MACH) +#define AS_OPTIONS "-arch i386" +#else +#define AS_OPTIONS "" +#endif + +#if defined(TARGET_OSX) +#define AS_NAME "clang" +#elif defined(TARGET_WIN32_MSVC) +#define AS_NAME "clang.exe" +#else +#define AS_NAME "as" +#endif + +#ifdef TARGET_WIN32_MSVC +#define AS_OBJECT_FILE_SUFFIX "obj" +#else +#define AS_OBJECT_FILE_SUFFIX "o" +#endif + +#if defined(sparc) +#define LD_NAME "ld" +#define LD_OPTIONS "-shared -G" +#elif defined(__ppc__) && defined(TARGET_MACH) +#define LD_NAME "gcc" +#define LD_OPTIONS "-dynamiclib" +#elif defined(TARGET_AMD64) && defined(TARGET_MACH) +#define LD_NAME "clang" +#define LD_OPTIONS "--shared" +#elif defined(TARGET_WIN32_MSVC) +#define LD_NAME "link.exe" +#define LD_OPTIONS "/DLL /MACHINE:X64 /NOLOGO /INCREMENTAL:NO" +#define LD_DEBUG_OPTIONS LD_OPTIONS " /DEBUG" +#elif defined(TARGET_WIN32) && !defined(TARGET_ANDROID) +#define LD_NAME "gcc" +#define LD_OPTIONS "-shared" +#elif defined(TARGET_X86) && defined(TARGET_MACH) +#define LD_NAME "clang" +#define LD_OPTIONS "-m32 -dynamiclib" +#elif defined(TARGET_ARM) && !defined(TARGET_ANDROID) +#define LD_NAME "gcc" +#define LD_OPTIONS "--shared" +#elif defined(TARGET_POWERPC64) +#define LD_OPTIONS "-m elf64ppc" +#endif + +#ifndef LD_OPTIONS +#define LD_OPTIONS "" +#endif + + if (acfg->aot_opts.asm_only) { + aot_printf (acfg, "Output file: '%s'.\n", acfg->tmpfname); + if (acfg->aot_opts.static_link) + aot_printf (acfg, "Linking symbol: '%s'.\n", acfg->static_linking_symbol); + if (acfg->llvm) + aot_printf (acfg, "LLVM output file: '%s'.\n", acfg->llvm_sfile); + return 0; + } + + if (acfg->aot_opts.static_link) { + if (acfg->aot_opts.outfile) + objfile = g_strdup_printf ("%s", acfg->aot_opts.outfile); + else + objfile = g_strdup_printf ("%s." AS_OBJECT_FILE_SUFFIX, acfg->image->name); + } else { + objfile = g_strdup_printf ("%s." AS_OBJECT_FILE_SUFFIX, acfg->tmpfname); + } + +#ifdef TARGET_OSX + g_string_append (acfg->as_args, "-c -x assembler"); +#endif + + command = g_strdup_printf ("\"%s%s\" %s %s -o %s %s", tool_prefix, AS_NAME, AS_OPTIONS, + acfg->as_args ? acfg->as_args->str : "", + wrap_path (objfile), wrap_path (acfg->tmpfname)); + aot_printf (acfg, "Executing the native assembler: %s\n", command); + if (execute_system (command) != 0) { + g_free (command); + g_free (objfile); + return 1; + } + + if (acfg->llvm && !acfg->llvm_owriter) { + command = g_strdup_printf ("\"%s%s\" %s %s -o %s %s", tool_prefix, AS_NAME, AS_OPTIONS, + acfg->as_args ? acfg->as_args->str : "", + wrap_path (acfg->llvm_ofile), wrap_path (acfg->llvm_sfile)); + aot_printf (acfg, "Executing the native assembler: %s\n", command); + if (execute_system (command) != 0) { + g_free (command); + g_free (objfile); + return 1; + } + } + + g_free (command); + + if (acfg->aot_opts.static_link) { + aot_printf (acfg, "Output file: '%s'.\n", objfile); + aot_printf (acfg, "Linking symbol: '%s'.\n", acfg->static_linking_symbol); + g_free (objfile); + return 0; + } + + if (acfg->aot_opts.outfile) + outfile_name = g_strdup_printf ("%s", acfg->aot_opts.outfile); + else + outfile_name = g_strdup_printf ("%s%s", acfg->image->name, MONO_SOLIB_EXT); + + tmp_outfile_name = g_strdup_printf ("%s.tmp", outfile_name); + + if (acfg->llvm) { + llvm_ofile = g_strdup_printf ("\"%s\"", acfg->llvm_ofile); + } else { + llvm_ofile = g_strdup (""); + } + + /* replace the ; flags separators with spaces */ + g_strdelimit (ld_flags, ";", ' '); + + if (acfg->aot_opts.llvm_only) + ld_flags = g_strdup_printf ("%s %s", ld_flags, "-lstdc++"); + +#ifdef TARGET_WIN32_MSVC + g_assert (tmp_outfile_name != NULL); + g_assert (objfile != NULL); + command = g_strdup_printf ("\"%s%s\" %s %s /OUT:\"%s\" \"%s\"", tool_prefix, LD_NAME, + acfg->aot_opts.nodebug ? LD_OPTIONS : LD_DEBUG_OPTIONS, ld_flags, tmp_outfile_name, objfile); +#elif defined(LD_NAME) + command = g_strdup_printf ("%s%s %s -o %s %s %s %s", tool_prefix, LD_NAME, LD_OPTIONS, + wrap_path (tmp_outfile_name), wrap_path (llvm_ofile), + wrap_path (g_strdup_printf ("%s." AS_OBJECT_FILE_SUFFIX, acfg->tmpfname)), ld_flags); +#else + // Default (linux) + if (acfg->aot_opts.tool_prefix) { + /* Cross compiling */ + command = g_strdup_printf ("\"%sld\" %s -shared -o %s %s %s %s", tool_prefix, LD_OPTIONS, + wrap_path (tmp_outfile_name), wrap_path (llvm_ofile), + wrap_path (g_strdup_printf ("%s." AS_OBJECT_FILE_SUFFIX, acfg->tmpfname)), ld_flags); + } else { + char *args = g_strdup_printf ("%s -shared -o %s %s %s %s", LD_OPTIONS, + wrap_path (tmp_outfile_name), wrap_path (llvm_ofile), + wrap_path (g_strdup_printf ("%s." AS_OBJECT_FILE_SUFFIX, acfg->tmpfname)), ld_flags); + + if (acfg->aot_opts.llvm_only) { + command = g_strdup_printf ("clang++ %s", args); + } else { + command = g_strdup_printf ("\"%sld\" %s", tool_prefix, args); + } + g_free (args); + } +#endif + aot_printf (acfg, "Executing the native linker: %s\n", command); + if (execute_system (command) != 0) { + g_free (tmp_outfile_name); + g_free (outfile_name); + g_free (command); + g_free (objfile); + g_free (ld_flags); + return 1; + } + + g_free (command); + + /*com = g_strdup_printf ("strip --strip-unneeded %s%s", acfg->image->name, MONO_SOLIB_EXT); + printf ("Stripping the binary: %s\n", com); + execute_system (com); + g_free (com);*/ + +#if defined(TARGET_ARM) && !defined(TARGET_MACH) + /* + * gas generates 'mapping symbols' each time code and data is mixed, which + * happens a lot in emit_and_reloc_code (), so we need to get rid of them. + */ + command = g_strdup_printf ("\"%sstrip\" --strip-symbol=\\$a --strip-symbol=\\$d %s", wrap_path(tool_prefix), wrap_path(tmp_outfile_name)); + aot_printf (acfg, "Stripping the binary: %s\n", command); + if (execute_system (command) != 0) { + g_free (tmp_outfile_name); + g_free (outfile_name); + g_free (command); + g_free (objfile); + return 1; + } +#endif + + if (0 != rename (tmp_outfile_name, outfile_name)) { + if (G_FILE_ERROR_EXIST == g_file_error_from_errno (errno)) { + /* Since we are rebuilding the module we need to be able to replace any old copies. Remove old file and retry rename operation. */ + unlink (outfile_name); + rename (tmp_outfile_name, outfile_name); + } + } + +#if defined(TARGET_MACH) + command = g_strdup_printf ("dsymutil \"%s\"", outfile_name); + aot_printf (acfg, "Executing dsymutil: %s\n", command); + if (execute_system (command) != 0) { + return 1; + } +#endif + + if (!acfg->aot_opts.save_temps) + unlink (objfile); + + g_free (tmp_outfile_name); + g_free (outfile_name); + g_free (objfile); + + if (acfg->aot_opts.save_temps) + aot_printf (acfg, "Retained input file.\n"); + else + unlink (acfg->tmpfname); + + return 0; +} + +static guint8 +profread_byte (FILE *infile) +{ + guint8 i; + int res; + + res = fread (&i, 1, 1, infile); + g_assert (res == 1); + return i; +} + +static int +profread_int (FILE *infile) +{ + int i, res; + + res = fread (&i, 4, 1, infile); + g_assert (res == 1); + return i; +} + +static char* +profread_string (FILE *infile) +{ + int len, res; + char buf [1024]; + char *pbuf; + + len = profread_int (infile); + if (len + 1 > 1024) + pbuf = g_malloc (len + 1); + else + pbuf = buf; + res = fread (pbuf, 1, len, infile); + g_assert (res == len); + pbuf [len] = '\0'; + if (pbuf == buf) + return g_strdup (buf); + else + return pbuf; +} + +static void +load_profile_file (MonoAotCompile *acfg, char *filename) +{ + FILE *infile; + char buf [1024]; + int res, len, version; + char magic [32]; + + infile = fopen (filename, "r"); + if (!infile) { + fprintf (stderr, "Unable to open file '%s': %s.\n", filename, strerror (errno)); + exit (1); + } + + printf ("Using profile data file '%s'\n", filename); + + sprintf (magic, AOT_PROFILER_MAGIC); + len = strlen (magic); + res = fread (buf, 1, len, infile); + magic [len] = '\0'; + buf [len] = '\0'; + if ((res != len) || strcmp (buf, magic) != 0) { + printf ("Profile file has wrong header: '%s'.\n", buf); + fclose (infile); + exit (1); + } + guint32 expected_version = (AOT_PROFILER_MAJOR_VERSION << 16) | AOT_PROFILER_MINOR_VERSION; + version = profread_int (infile); + if (version != expected_version) { + printf ("Profile file has wrong version 0x%4x, expected 0x%4x.\n", version, expected_version); + fclose (infile); + exit (1); + } + + ProfileData *data = g_new0 (ProfileData, 1); + data->images = g_hash_table_new (NULL, NULL); + data->classes = g_hash_table_new (NULL, NULL); + data->ginsts = g_hash_table_new (NULL, NULL); + data->methods = g_hash_table_new (NULL, NULL); + + while (TRUE) { + int type = profread_byte (infile); + int id = profread_int (infile); + + if (type == AOTPROF_RECORD_NONE) + break; + + switch (type) { + case AOTPROF_RECORD_IMAGE: { + ImageProfileData *idata = g_new0 (ImageProfileData, 1); + idata->name = profread_string (infile); + char *mvid = profread_string (infile); + g_free (mvid); + g_hash_table_insert (data->images, GINT_TO_POINTER (id), idata); + break; + } + case AOTPROF_RECORD_GINST: { + int i; + int len = profread_int (infile); + + GInstProfileData *gdata = g_new0 (GInstProfileData, 1); + gdata->argc = len; + gdata->argv = g_new0 (ClassProfileData*, len); + + for (i = 0; i < len; ++i) { + int class_id = profread_int (infile); + + gdata->argv [i] = g_hash_table_lookup (data->classes, GINT_TO_POINTER (class_id)); + g_assert (gdata->argv [i]); + } + g_hash_table_insert (data->ginsts, GINT_TO_POINTER (id), gdata); + break; + } + case AOTPROF_RECORD_TYPE: { + int type = profread_byte (infile); + + switch (type) { + case MONO_TYPE_CLASS: { + int image_id = profread_int (infile); + int ginst_id = profread_int (infile); + char *class_name = profread_string (infile); + + ImageProfileData *image = g_hash_table_lookup (data->images, GINT_TO_POINTER (image_id)); + g_assert (image); + + char *p = strrchr (class_name, '.'); + g_assert (p); + *p = '\0'; + + ClassProfileData *cdata = g_new0 (ClassProfileData, 1); + cdata->image = image; + cdata->ns = g_strdup (class_name); + cdata->name = g_strdup (p + 1); + + if (ginst_id != -1) { + cdata->inst = g_hash_table_lookup (data->ginsts, GINT_TO_POINTER (ginst_id)); + g_assert (cdata->inst); + } + g_free (class_name); + + g_hash_table_insert (data->classes, GINT_TO_POINTER (id), cdata); + break; + } +#if 0 + case MONO_TYPE_SZARRAY: { + int elem_id = profread_int (infile); + // FIXME: + break; + } +#endif + default: + g_assert_not_reached (); + break; + } + break; + } + case AOTPROF_RECORD_METHOD: { + int class_id = profread_int (infile); + int ginst_id = profread_int (infile); + int param_count = profread_int (infile); + char *method_name = profread_string (infile); + char *sig = profread_string (infile); + + ClassProfileData *klass = g_hash_table_lookup (data->classes, GINT_TO_POINTER (class_id)); + g_assert (klass); + + MethodProfileData *mdata = g_new0 (MethodProfileData, 1); + mdata->id = id; + mdata->klass = klass; + mdata->name = method_name; + mdata->signature = sig; + mdata->param_count = param_count; + + if (ginst_id != -1) { + mdata->inst = g_hash_table_lookup (data->ginsts, GINT_TO_POINTER (ginst_id)); + g_assert (mdata->inst); + } + g_hash_table_insert (data->methods, GINT_TO_POINTER (id), mdata); + break; + } + default: + printf ("%d\n", type); + g_assert_not_reached (); + break; + } + } + + fclose (infile); + acfg->profile_data = g_list_append (acfg->profile_data, data); +} + +static void +resolve_class (ClassProfileData *cdata); + +static void +resolve_ginst (GInstProfileData *inst_data) +{ + int i; + + if (inst_data->inst) + return; + + for (i = 0; i < inst_data->argc; ++i) { + resolve_class (inst_data->argv [i]); + if (!inst_data->argv [i]->klass) + return; + } + MonoType **args = g_new0 (MonoType*, inst_data->argc); + for (i = 0; i < inst_data->argc; ++i) + args [i] = &inst_data->argv [i]->klass->byval_arg; + + inst_data->inst = mono_metadata_get_generic_inst (inst_data->argc, args); +} + +static void +resolve_class (ClassProfileData *cdata) +{ + MonoError error; + MonoClass *klass; + + if (!cdata->image->image) + return; + + klass = mono_class_from_name_checked (cdata->image->image, cdata->ns, cdata->name, &error); + if (!klass) { + //printf ("[%s] %s.%s\n", cdata->image->name, cdata->ns, cdata->name); + return; + } + if (cdata->inst) { + resolve_ginst (cdata->inst); + if (!cdata->inst->inst) + return; + MonoGenericContext ctx; + + memset (&ctx, 0, sizeof (ctx)); + ctx.class_inst = cdata->inst->inst; + cdata->klass = mono_class_inflate_generic_class_checked (klass, &ctx, &error); + } else { + cdata->klass = klass; + } +} + +/* + * Resolve the profile data to the corresponding loaded classes/methods etc. if possible. + */ +static void +resolve_profile_data (MonoAotCompile *acfg, ProfileData *data) +{ + GHashTableIter iter; + gpointer key, value; + int i; + + if (!data) + return; + + /* Images */ + GPtrArray *assemblies = mono_domain_get_assemblies (mono_get_root_domain (), FALSE); + g_hash_table_iter_init (&iter, data->images); + while (g_hash_table_iter_next (&iter, &key, &value)) { + ImageProfileData *idata = (ImageProfileData*)value; + + for (i = 0; i < assemblies->len; ++i) { + MonoAssembly *ass = g_ptr_array_index (assemblies, i); + + if (!strcmp (ass->aname.name, idata->name)) { + idata->image = ass->image; + break; + } + } + } + g_ptr_array_free (assemblies, TRUE); + + /* Classes */ + g_hash_table_iter_init (&iter, data->classes); + while (g_hash_table_iter_next (&iter, &key, &value)) { + ClassProfileData *cdata = (ClassProfileData*)value; + + if (!cdata->image->image) { + if (acfg->aot_opts.verbose) + printf ("Unable to load class '%s.%s' because its image '%s' is not loaded.\n", cdata->ns, cdata->name, cdata->image->name); + continue; + } + + resolve_class (cdata); + /* + if (cdata->klass) + printf ("%s %s %s\n", cdata->ns, cdata->name, mono_class_full_name (cdata->klass)); + */ + } + + /* Methods */ + g_hash_table_iter_init (&iter, data->methods); + while (g_hash_table_iter_next (&iter, &key, &value)) { + MethodProfileData *mdata = (MethodProfileData*)value; + MonoClass *klass; + MonoMethod *m; + gpointer miter; + + resolve_class (mdata->klass); + klass = mdata->klass->klass; + if (!klass) { + if (acfg->aot_opts.verbose) + printf ("Unable to load method '%s' because its class '%s.%s' is not loaded.\n", mdata->name, mdata->klass->ns, mdata->klass->name); + continue; + } + miter = NULL; + while ((m = mono_class_get_methods (klass, &miter))) { + MonoError error; + + if (strcmp (m->name, mdata->name)) + continue; + MonoMethodSignature *sig = mono_method_signature (m); + if (!sig) + continue; + if (sig->param_count != mdata->param_count) + continue; + if (mdata->inst) { + resolve_ginst (mdata->inst); + if (!mdata->inst->inst) + continue; + MonoGenericContext ctx; + + memset (&ctx, 0, sizeof (ctx)); + ctx.method_inst = mdata->inst->inst; + + m = mono_class_inflate_generic_method_checked (m, &ctx, &error); + if (!m) + continue; + sig = mono_method_signature_checked (m, &error); + if (!is_ok (&error)) { + mono_error_cleanup (&error); + continue; + } + } + char *sig_str = mono_signature_full_name (sig); + gboolean match = !strcmp (sig_str, mdata->signature); + g_free (sig_str); + if (!match) + + continue; + //printf ("%s\n", mono_method_full_name (m, 1)); + mdata->method = m; + break; + } + if (!mdata->method) { + if (acfg->aot_opts.verbose) + printf ("Unable to load method '%s' from class '%s', not found.\n", mdata->name, mono_class_full_name (klass)); + } + } +} + +static gboolean +inst_references_image (MonoGenericInst *inst, MonoImage *image) +{ + int i; + + for (i = 0; i < inst->type_argc; ++i) { + MonoClass *k = mono_class_from_mono_type (inst->type_argv [i]); + if (k->image == image) + return TRUE; + if (mono_class_is_ginst (k)) { + MonoGenericInst *kinst = mono_class_get_context (k)->class_inst; + if (inst_references_image (kinst, image)) + return TRUE; + } + } + return FALSE; +} + +static gboolean +is_local_inst (MonoGenericInst *inst, MonoImage *image) +{ + int i; + + for (i = 0; i < inst->type_argc; ++i) { + MonoClass *k = mono_class_from_mono_type (inst->type_argv [i]); + if (!MONO_TYPE_IS_PRIMITIVE (inst->type_argv [i]) && k->image != image) + return FALSE; + } + return TRUE; +} + +static void +add_profile_instances (MonoAotCompile *acfg, ProfileData *data) +{ + GHashTableIter iter; + gpointer key, value; + int count = 0; + + if (!data) + return; + + if (acfg->aot_opts.profile_only) { + /* Add methods referenced by the profile */ + g_hash_table_iter_init (&iter, data->methods); + while (g_hash_table_iter_next (&iter, &key, &value)) { + MethodProfileData *mdata = (MethodProfileData*)value; + MonoMethod *m = mdata->method; + + if (!m) + continue; + if (m->is_inflated) + continue; + add_extra_method (acfg, m); + g_hash_table_insert (acfg->profile_methods, m, m); + count ++; + } + } + + /* + * Add method instances 'related' to this assembly to the AOT image. + */ + g_hash_table_iter_init (&iter, data->methods); + while (g_hash_table_iter_next (&iter, &key, &value)) { + MethodProfileData *mdata = (MethodProfileData*)value; + MonoMethod *m = mdata->method; + MonoGenericContext *ctx; + + if (!m) + continue; + if (!m->is_inflated) + continue; + + ctx = mono_method_get_context (m); + /* For simplicity, add instances which reference the assembly we are compiling */ + if (((ctx->class_inst && inst_references_image (ctx->class_inst, acfg->image)) || + (ctx->method_inst && inst_references_image (ctx->method_inst, acfg->image))) && + !mono_method_is_generic_sharable_full (m, FALSE, FALSE, FALSE)) { + //printf ("%s\n", mono_method_full_name (m, TRUE)); + add_extra_method (acfg, m); + count ++; + } else if (m->klass->image == acfg->image && + ((ctx->class_inst && is_local_inst (ctx->class_inst, acfg->image)) || + (ctx->method_inst && is_local_inst (ctx->method_inst, acfg->image))) && + !mono_method_is_generic_sharable_full (m, FALSE, FALSE, FALSE)) { + /* Add instances where the gtd is in the assembly and its inflated with types from this assembly or corlib */ + //printf ("%s\n", mono_method_full_name (m, TRUE)); + add_extra_method (acfg, m); + count ++; + } + /* + * FIXME: We might skip some instances, for example: + * Foo won't be compiled when compiling Foo's assembly since it doesn't match the first case, + * and it won't be compiled when compiling Bar's assembly if Foo's assembly is not loaded. + */ + } + + printf ("Added %d methods from profile.\n", count); +} + +static void +init_got_info (GotInfo *info) +{ + int i; + + info->patch_to_got_offset = g_hash_table_new (mono_patch_info_hash, mono_patch_info_equal); + info->patch_to_got_offset_by_type = g_new0 (GHashTable*, MONO_PATCH_INFO_NUM); + for (i = 0; i < MONO_PATCH_INFO_NUM; ++i) + info->patch_to_got_offset_by_type [i] = g_hash_table_new (mono_patch_info_hash, mono_patch_info_equal); + info->got_patches = g_ptr_array_new (); +} + +static MonoAotCompile* +acfg_create (MonoAssembly *ass, guint32 opts) +{ + MonoImage *image = ass->image; + MonoAotCompile *acfg; + + acfg = g_new0 (MonoAotCompile, 1); + acfg->methods = g_ptr_array_new (); + acfg->method_indexes = g_hash_table_new (NULL, NULL); + acfg->method_depth = g_hash_table_new (NULL, NULL); + acfg->plt_offset_to_entry = g_hash_table_new (NULL, NULL); + acfg->patch_to_plt_entry = g_new0 (GHashTable*, MONO_PATCH_INFO_NUM); + acfg->method_to_cfg = g_hash_table_new (NULL, NULL); + acfg->token_info_hash = g_hash_table_new_full (NULL, NULL, NULL, NULL); + acfg->method_to_pinvoke_import = g_hash_table_new_full (NULL, NULL, NULL, g_free); + acfg->image_hash = g_hash_table_new (NULL, NULL); + acfg->image_table = g_ptr_array_new (); + acfg->globals = g_ptr_array_new (); + acfg->image = image; + acfg->opts = opts; + /* TODO: Write out set of SIMD instructions used, rather than just those available */ + acfg->simd_opts = mono_arch_cpu_enumerate_simd_versions (); + acfg->mempool = mono_mempool_new (); + acfg->extra_methods = g_ptr_array_new (); + acfg->unwind_info_offsets = g_hash_table_new (NULL, NULL); + acfg->unwind_ops = g_ptr_array_new (); + acfg->method_label_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + acfg->method_order = g_ptr_array_new (); + acfg->export_names = g_hash_table_new (NULL, NULL); + acfg->klass_blob_hash = g_hash_table_new (NULL, NULL); + acfg->method_blob_hash = g_hash_table_new (NULL, NULL); + acfg->plt_entry_debug_sym_cache = g_hash_table_new (g_str_hash, g_str_equal); + acfg->gsharedvt_in_signatures = g_hash_table_new ((GHashFunc)mono_signature_hash, (GEqualFunc)mono_metadata_signature_equal); + acfg->gsharedvt_out_signatures = g_hash_table_new ((GHashFunc)mono_signature_hash, (GEqualFunc)mono_metadata_signature_equal); + acfg->profile_methods = g_hash_table_new (NULL, NULL); + mono_os_mutex_init_recursive (&acfg->mutex); + + init_got_info (&acfg->got_info); + init_got_info (&acfg->llvm_got_info); + + return acfg; +} + +static void +got_info_free (GotInfo *info) +{ + int i; + + for (i = 0; i < MONO_PATCH_INFO_NUM; ++i) + g_hash_table_destroy (info->patch_to_got_offset_by_type [i]); + g_free (info->patch_to_got_offset_by_type); + g_hash_table_destroy (info->patch_to_got_offset); + g_ptr_array_free (info->got_patches, TRUE); +} + +static void +acfg_free (MonoAotCompile *acfg) +{ + int i; + + mono_img_writer_destroy (acfg->w); + for (i = 0; i < acfg->nmethods; ++i) + if (acfg->cfgs [i]) + mono_destroy_compile (acfg->cfgs [i]); + + g_free (acfg->cfgs); + + g_free (acfg->static_linking_symbol); + g_free (acfg->got_symbol); + g_free (acfg->plt_symbol); + g_ptr_array_free (acfg->methods, TRUE); + g_ptr_array_free (acfg->image_table, TRUE); + g_ptr_array_free (acfg->globals, TRUE); + g_ptr_array_free (acfg->unwind_ops, TRUE); + g_hash_table_destroy (acfg->method_indexes); + g_hash_table_destroy (acfg->method_depth); + g_hash_table_destroy (acfg->plt_offset_to_entry); + for (i = 0; i < MONO_PATCH_INFO_NUM; ++i) { + if (acfg->patch_to_plt_entry [i]) + g_hash_table_destroy (acfg->patch_to_plt_entry [i]); + } + g_free (acfg->patch_to_plt_entry); + g_hash_table_destroy (acfg->method_to_cfg); + g_hash_table_destroy (acfg->token_info_hash); + g_hash_table_destroy (acfg->method_to_pinvoke_import); + g_hash_table_destroy (acfg->image_hash); + g_hash_table_destroy (acfg->unwind_info_offsets); + g_hash_table_destroy (acfg->method_label_hash); + if (acfg->typespec_classes) + g_hash_table_destroy (acfg->typespec_classes); + g_hash_table_destroy (acfg->export_names); + g_hash_table_destroy (acfg->plt_entry_debug_sym_cache); + g_hash_table_destroy (acfg->klass_blob_hash); + g_hash_table_destroy (acfg->method_blob_hash); + got_info_free (&acfg->got_info); + got_info_free (&acfg->llvm_got_info); + arch_free_unwind_info_section_cache (acfg); + mono_mempool_destroy (acfg->mempool); + g_free (acfg); +} + +#define WRAPPER(e,n) n, +static const char* const +wrapper_type_names [MONO_WRAPPER_NUM + 1] = { +#include "mono/metadata/wrapper-types.h" + NULL +}; + +static G_GNUC_UNUSED const char* +get_wrapper_type_name (int type) +{ + return wrapper_type_names [type]; +} + +//#define DUMP_PLT +//#define DUMP_GOT + +static void aot_dump (MonoAotCompile *acfg) +{ + FILE *dumpfile; + char * dumpname; + + JsonWriter writer; + mono_json_writer_init (&writer); + + mono_json_writer_object_begin(&writer); + + // Methods + mono_json_writer_indent (&writer); + mono_json_writer_object_key(&writer, "methods"); + mono_json_writer_array_begin (&writer); + + int i; + for (i = 0; i < acfg->nmethods; ++i) { + MonoCompile *cfg; + MonoMethod *method; + MonoClass *klass; + + cfg = acfg->cfgs [i]; + if (ignore_cfg (cfg)) + continue; + + method = cfg->orig_method; + + mono_json_writer_indent (&writer); + mono_json_writer_object_begin(&writer); + + mono_json_writer_indent (&writer); + mono_json_writer_object_key(&writer, "name"); + mono_json_writer_printf (&writer, "\"%s\",\n", method->name); + + mono_json_writer_indent (&writer); + mono_json_writer_object_key(&writer, "signature"); + mono_json_writer_printf (&writer, "\"%s\",\n", mono_method_get_full_name (method)); + + mono_json_writer_indent (&writer); + mono_json_writer_object_key(&writer, "code_size"); + mono_json_writer_printf (&writer, "\"%d\",\n", cfg->code_size); + + klass = method->klass; + + mono_json_writer_indent (&writer); + mono_json_writer_object_key(&writer, "class"); + mono_json_writer_printf (&writer, "\"%s\",\n", klass->name); + + mono_json_writer_indent (&writer); + mono_json_writer_object_key(&writer, "namespace"); + mono_json_writer_printf (&writer, "\"%s\",\n", klass->name_space); + + mono_json_writer_indent (&writer); + mono_json_writer_object_key(&writer, "wrapper_type"); + mono_json_writer_printf (&writer, "\"%s\",\n", get_wrapper_type_name(method->wrapper_type)); + + mono_json_writer_indent_pop (&writer); + mono_json_writer_indent (&writer); + mono_json_writer_object_end (&writer); + mono_json_writer_printf (&writer, ",\n"); + } + + mono_json_writer_indent_pop (&writer); + mono_json_writer_indent (&writer); + mono_json_writer_array_end (&writer); + mono_json_writer_printf (&writer, ",\n"); + + // PLT entries +#ifdef DUMP_PLT + mono_json_writer_indent_push (&writer); + mono_json_writer_indent (&writer); + mono_json_writer_object_key(&writer, "plt"); + mono_json_writer_array_begin (&writer); + + for (i = 0; i < acfg->plt_offset; ++i) { + MonoPltEntry *plt_entry = NULL; + MonoJumpInfo *ji; + + if (i == 0) + /* + * The first plt entry is unused. + */ + continue; + + plt_entry = g_hash_table_lookup (acfg->plt_offset_to_entry, GUINT_TO_POINTER (i)); + ji = plt_entry->ji; + + mono_json_writer_indent (&writer); + mono_json_writer_printf (&writer, "{ "); + mono_json_writer_object_key(&writer, "symbol"); + mono_json_writer_printf (&writer, "\"%s\" },\n", plt_entry->symbol); + } + + mono_json_writer_indent_pop (&writer); + mono_json_writer_indent (&writer); + mono_json_writer_array_end (&writer); + mono_json_writer_printf (&writer, ",\n"); +#endif + + // GOT entries +#ifdef DUMP_GOT + mono_json_writer_indent_push (&writer); + mono_json_writer_indent (&writer); + mono_json_writer_object_key(&writer, "got"); + mono_json_writer_array_begin (&writer); + + mono_json_writer_indent_push (&writer); + for (i = 0; i < acfg->got_info.got_patches->len; ++i) { + MonoJumpInfo *ji = g_ptr_array_index (acfg->got_info.got_patches, i); + + mono_json_writer_indent (&writer); + mono_json_writer_printf (&writer, "{ "); + mono_json_writer_object_key(&writer, "patch_name"); + mono_json_writer_printf (&writer, "\"%s\" },\n", get_patch_name (ji->type)); + } + + mono_json_writer_indent_pop (&writer); + mono_json_writer_indent (&writer); + mono_json_writer_array_end (&writer); + mono_json_writer_printf (&writer, ",\n"); +#endif + + mono_json_writer_indent_pop (&writer); + mono_json_writer_indent (&writer); + mono_json_writer_object_end (&writer); + + dumpname = g_strdup_printf ("%s.json", g_path_get_basename (acfg->image->name)); + dumpfile = fopen (dumpname, "w+"); + g_free (dumpname); + + fprintf (dumpfile, "%s", writer.text->str); + fclose (dumpfile); + + mono_json_writer_destroy (&writer); +} + +static const char *preinited_jit_icalls[] = { + "mono_aot_init_llvm_method", + "mono_aot_init_gshared_method_this", + "mono_aot_init_gshared_method_mrgctx", + "mono_aot_init_gshared_method_vtable", + "mono_llvm_throw_corlib_exception", + "mono_init_vtable_slot", + "mono_helper_ldstr_mscorlib" +}; + +static void +add_preinit_got_slots (MonoAotCompile *acfg) +{ + MonoJumpInfo *ji; + int i; + + /* + * Allocate the first few GOT entries to information which is needed frequently, or it is needed + * during method initialization etc. + */ + + ji = (MonoJumpInfo *)mono_mempool_alloc0 (acfg->mempool, sizeof (MonoJumpInfo)); + ji->type = MONO_PATCH_INFO_IMAGE; + ji->data.image = acfg->image; + get_got_offset (acfg, FALSE, ji); + get_got_offset (acfg, TRUE, ji); + + ji = (MonoJumpInfo *)mono_mempool_alloc0 (acfg->mempool, sizeof (MonoJumpInfo)); + ji->type = MONO_PATCH_INFO_MSCORLIB_GOT_ADDR; + get_got_offset (acfg, FALSE, ji); + get_got_offset (acfg, TRUE, ji); + + ji = (MonoJumpInfo *)mono_mempool_alloc0 (acfg->mempool, sizeof (MonoJumpInfo)); + ji->type = MONO_PATCH_INFO_GC_CARD_TABLE_ADDR; + get_got_offset (acfg, FALSE, ji); + get_got_offset (acfg, TRUE, ji); + + ji = (MonoJumpInfo *)mono_mempool_alloc0 (acfg->mempool, sizeof (MonoJumpInfo)); + ji->type = MONO_PATCH_INFO_GC_NURSERY_START; + get_got_offset (acfg, FALSE, ji); + get_got_offset (acfg, TRUE, ji); + + ji = (MonoJumpInfo *)mono_mempool_alloc0 (acfg->mempool, sizeof (MonoJumpInfo)); + ji->type = MONO_PATCH_INFO_AOT_MODULE; + get_got_offset (acfg, FALSE, ji); + get_got_offset (acfg, TRUE, ji); + + ji = (MonoJumpInfo *)mono_mempool_alloc0 (acfg->mempool, sizeof (MonoJumpInfo)); + ji->type = MONO_PATCH_INFO_GC_NURSERY_BITS; + get_got_offset (acfg, FALSE, ji); + get_got_offset (acfg, TRUE, ji); + + for (i = 0; i < TLS_KEY_NUM; i++) { + ji = (MonoJumpInfo *)mono_mempool_alloc0 (acfg->mempool, sizeof (MonoJumpInfo)); + ji->type = MONO_PATCH_INFO_GET_TLS_TRAMP; + ji->data.index = i; + get_got_offset (acfg, FALSE, ji); + get_got_offset (acfg, TRUE, ji); + + ji = (MonoJumpInfo *)mono_mempool_alloc0 (acfg->mempool, sizeof (MonoJumpInfo)); + ji->type = MONO_PATCH_INFO_SET_TLS_TRAMP; + ji->data.index = i; + get_got_offset (acfg, FALSE, ji); + get_got_offset (acfg, TRUE, ji); + } + + ji = (MonoJumpInfo *)mono_mempool_alloc0 (acfg->mempool, sizeof (MonoJumpInfo)); + ji->type = MONO_PATCH_INFO_JIT_THREAD_ATTACH; + get_got_offset (acfg, FALSE, ji); + get_got_offset (acfg, TRUE, ji); + + /* Called by native-to-managed wrappers on possibly unattached threads */ + ji = (MonoJumpInfo *)mono_mempool_alloc0 (acfg->mempool, sizeof (MonoJumpInfo)); + ji->type = MONO_PATCH_INFO_JIT_ICALL_ADDR_NOCALL; + ji->data.name = "mono_threads_attach_coop"; + get_got_offset (acfg, FALSE, ji); + get_got_offset (acfg, TRUE, ji); + + for (i = 0; i < sizeof (preinited_jit_icalls) / sizeof (char*); ++i) { + ji = (MonoJumpInfo *)mono_mempool_alloc0 (acfg->mempool, sizeof (MonoAotCompile)); + ji->type = MONO_PATCH_INFO_INTERNAL_METHOD; + ji->data.name = preinited_jit_icalls [i]; + get_got_offset (acfg, FALSE, ji); + get_got_offset (acfg, TRUE, ji); + } + + acfg->nshared_got_entries = acfg->got_offset; +} + +static void +mono_dedup_log_stats (MonoAotCompile *acfg) +{ + GHashTableIter iter; + g_assert (acfg->dedup_stats); + + // If dedup_emit_mode, acfg is the dummy dedup module that consolidates + // deduped modules + g_hash_table_iter_init (&iter, acfg->method_to_cfg); + MonoCompile *dcfg = NULL; + MonoMethod *method = NULL; + + size_t wrappers_size_saved = 0; + size_t inflated_size_saved = 0; + size_t copied_singles = 0; + + while (g_hash_table_iter_next (&iter, (gpointer *) &method, (gpointer *)&dcfg)) { + gchar *dedup_name = mono_aot_get_mangled_method_name (method); + guint count = GPOINTER_TO_UINT(g_hash_table_lookup (acfg->dedup_stats, dedup_name)); + + if (count == 0) + continue; + + if (acfg->dedup_emit_mode) { + // Size *saved* is the size due to things not emitted. + if (count < 2) { + // Just moved, didn't save space / dedup + copied_singles += dcfg->code_len; + } else if (method->wrapper_type != MONO_WRAPPER_NONE) { + wrappers_size_saved += dcfg->code_len * (count - 1); + } else { + inflated_size_saved += dcfg->code_len * (count - 1); + } + } + if (acfg->aot_opts.dedup) { + if (method->wrapper_type != MONO_WRAPPER_NONE) { + wrappers_size_saved += dcfg->code_len * count; + } else { + inflated_size_saved += dcfg->code_len * count; + } + } + } + + aot_printf (acfg, "Dedup Pass: Size Saved From Deduped Wrappers:\t%zu bytes\n", wrappers_size_saved); + aot_printf (acfg, "Dedup Pass: Size Saved From Inflated Methods:\t%zu bytes\n", inflated_size_saved); + if (acfg->dedup_emit_mode) + aot_printf (acfg, "Dedup Pass: Size of Moved But Not Deduped (only 1 copy) Methods:\t%zu bytes\n", copied_singles); + + g_hash_table_destroy (acfg->dedup_stats); + acfg->dedup_stats = NULL; +} + +// Flush the cache to tell future calls what to skip +static void +mono_flush_method_cache (MonoAotCompile *acfg) +{ + GHashTable *method_cache = acfg->dedup_cache; + char *filename = g_strdup_printf ("%s.dedup", acfg->image->name); + if (!acfg->dedup_cache_changed || !acfg->aot_opts.dedup) { + g_free (filename); + return; + } + + acfg->dedup_cache = NULL; + + FILE *cache = fopen (filename, "w"); + + if (!cache) + g_error ("Could not create cache at %s because of error: %s\n", filename, strerror (errno)); + + GHashTableIter iter; + gchar *name = NULL; + g_hash_table_iter_init (&iter, method_cache); + gboolean cont = TRUE; + while (cont && g_hash_table_iter_next (&iter, (gpointer *) &name, NULL)) { + int res = fprintf (cache, "%s\n", name); + cont = res >= 0; + } + // FIXME: don't assert if error when flushing + g_assert (cont); + + fclose (cache); + g_free (filename); + + // The keys are all in the imageset, nothing to free + // Values are just pointers to memory owned elsewhere, or sentinels + g_hash_table_destroy (method_cache); +} + +// Read in what has been emitted by previous invocations, +// what can be skipped +static void +mono_read_method_cache (MonoAotCompile *acfg) +{ + char *filename = g_strdup_printf ("%s.dedup", acfg->image->name); + // Only do once, when dedup_cache is null + if (acfg->dedup_cache) + goto early_exit; + + if (acfg->aot_opts.dedup_include || acfg->aot_opts.dedup) + g_assert (acfg->dedup_stats); + + // only in skip mode + if (!acfg->aot_opts.dedup) + goto early_exit; + + g_assert (acfg->dedup_cache); + + FILE *cache = fopen (filename, "r"); + if (!cache) + goto early_exit; + + // Since we do pointer comparisons, and it can't be allocated at + // the address 0x1 due to alignment, we use this as a sentinel + gpointer other_acfg_sentinel = GINT_TO_POINTER (0x1); + + if (fseek (cache, 0L, SEEK_END)) + goto cleanup; + + size_t fileLength = ftell (cache); + g_assert (fileLength > 0); + + if (fseek (cache, 0L, SEEK_SET)) + goto cleanup; + + // Avoid thousands of new malloc entries + // FIXME: allocate into imageset, so we don't need to free. + // put the other mangled names there too. + char *bulk = g_malloc0 (fileLength * sizeof (char)); + size_t offset = 0; + + while (fgets (&bulk [offset], fileLength - offset, cache)) { + // strip newline + char *line = &bulk [offset]; + size_t len = strlen (line); + if (len == 0) + break; + + if (len >= 0 && line [len] == '\n') + line [len] = '\0'; + offset += strlen (line) + 1; + g_assert (fileLength >= offset); + + g_hash_table_insert (acfg->dedup_cache, line, other_acfg_sentinel); + } + +cleanup: + fclose (cache); + +early_exit: + g_free (filename); + return; +} + +typedef struct { + GHashTable *cache; + GHashTable *stats; + gboolean emit_inflated_methods; + MonoAssembly *inflated_assembly; +} MonoAotState; + +static MonoAotState * +alloc_aot_state (void) +{ + MonoAotState *state = g_malloc (sizeof (MonoAotState)); + // FIXME: Should this own the memory? + state->cache = g_hash_table_new (g_str_hash, g_str_equal); + state->stats = g_hash_table_new (g_str_hash, g_str_equal); + // Start in "collect mode" + state->emit_inflated_methods = FALSE; + state->inflated_assembly = NULL; + return state; +} + +static void +free_aot_state (MonoAotState *astate) +{ + g_hash_table_destroy (astate->cache); + g_free (astate); +} + +static void +mono_add_deferred_extra_methods (MonoAotCompile *acfg, MonoAotState *astate) +{ + GHashTableIter iter; + gchar *name = NULL; + MonoMethod *method = NULL; + + acfg->dedup_emit_mode = TRUE; + + g_hash_table_iter_init (&iter, astate->cache); + while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &method)) { + add_method_full (acfg, method, TRUE, 0); + } + return; +} + +static void +mono_setup_dedup_state (MonoAotCompile *acfg, MonoAotState **global_aot_state, MonoAssembly *ass, MonoAotState **astate, gboolean *is_dedup_dummy) +{ + if (!acfg->aot_opts.dedup_include && !acfg->aot_opts.dedup) + return; + + if (global_aot_state && *global_aot_state && acfg->aot_opts.dedup_include) { + // Thread the state through when making the inflate pass + *astate = *global_aot_state; + } + + if (!*astate) { + *astate = alloc_aot_state (); + *global_aot_state = *astate; + } + + acfg->dedup_cache = (*astate)->cache; + acfg->dedup_stats = (*astate)->stats; + + // fills out acfg->dedup_cache + if (acfg->aot_opts.dedup) + mono_read_method_cache (acfg); + + if (!(*astate)->inflated_assembly && acfg->aot_opts.dedup_include) { + gchar **asm_path = g_strsplit (ass->image->name, G_DIR_SEPARATOR_S, 0); + gchar *asm_file = NULL; + + // Get the last part of the path, the filename + for (int i=0; asm_path [i] != NULL; i++) + asm_file = asm_path [i]; + + if (!strcmp (acfg->aot_opts.dedup_include, asm_file)) { + // Save + *is_dedup_dummy = TRUE; + (*astate)->inflated_assembly = ass; + } + g_strfreev (asm_path); + } else if ((*astate)->inflated_assembly) { + *is_dedup_dummy = (ass == (*astate)->inflated_assembly); + } +} + +int +mono_compile_deferred_assemblies (guint32 opts, const char *aot_options, gpointer **aot_state) +{ + // create assembly, loop and add extra_methods + // in add_generic_instances , rip out what's in that for loop + // and apply that to this aot_state inside of mono_compile_assembly + MonoAotState *astate; + astate = *(MonoAotState **)aot_state; + g_assert (astate); + + // FIXME: allow suffixes? + if (!astate->inflated_assembly) { + char *inflate = strstr (aot_options, "dedup-inflate"); + if (!inflate) + return 0; + else + g_error ("Error: mono was not given an assembly with the provided inflate name\n"); + } + + // Switch modes + astate->emit_inflated_methods = TRUE; + + int res = mono_compile_assembly (astate->inflated_assembly, opts, aot_options, aot_state); + + *aot_state = NULL; + free_aot_state (astate); + + return res; +} + +static const char* interp_in_static_sigs[] = { + "bool ptr int32 ptr&", + "bool ptr ptr&", + "int32 int32 ptr&", + "int32 int32 ptr ptr&", + "int32 ptr int32 ptr", + "int32 ptr int32 ptr&", + "int32 ptr ptr&", + "object object ptr ptr ptr", + "ptr int32 ptr&", + "ptr ptr int32 ptr ptr ptr&", + "ptr ptr int32 ptr ptr&", + "ptr ptr int32 ptr&", + "ptr ptr ptr int32 ptr&", + "ptr ptr ptr ptr& ptr&", + "ptr ptr ptr ptr&", + "ptr ptr ptr&", + "ptr ptr uint32 ptr&", + "ptr uint32 ptr&", + "void object ptr ptr ptr", + "void ptr ptr int32 ptr ptr& ptr ptr&", + "void ptr ptr int32 ptr ptr&", + "void ptr ptr ptr&", + "void ptr ptr&", + "void ptr", + "void int32 ptr&", + "void uint32 ptr&", +}; + +int +mono_compile_assembly (MonoAssembly *ass, guint32 opts, const char *aot_options, gpointer **global_aot_state) +{ + MonoImage *image = ass->image; + int i, res; + gint64 all_sizes; + MonoAotCompile *acfg; + char *outfile_name, *tmp_outfile_name, *p; + char llvm_stats_msg [256]; + TV_DECLARE (atv); + TV_DECLARE (btv); + + acfg = acfg_create (ass, opts); + + memset (&acfg->aot_opts, 0, sizeof (acfg->aot_opts)); + acfg->aot_opts.write_symbols = TRUE; + acfg->aot_opts.ntrampolines = 4096; + acfg->aot_opts.nrgctx_trampolines = 4096; + acfg->aot_opts.nimt_trampolines = 512; + acfg->aot_opts.nrgctx_fetch_trampolines = 128; + acfg->aot_opts.ngsharedvt_arg_trampolines = 512; + acfg->aot_opts.llvm_path = g_strdup (""); + acfg->aot_opts.temp_path = g_strdup (""); +#ifdef MONOTOUCH + acfg->aot_opts.use_trampolines_page = TRUE; +#endif + + mono_aot_parse_options (aot_options, &acfg->aot_opts); + + // start dedup + MonoAotState *astate = NULL; + gboolean is_dedup_dummy = FALSE; + mono_setup_dedup_state (acfg, (MonoAotState **) global_aot_state, ass, &astate, &is_dedup_dummy); + + // Process later + if (is_dedup_dummy && astate && !astate->emit_inflated_methods) + return 0; + + // end dedup + + if (acfg->aot_opts.logfile) { + acfg->logfile = fopen (acfg->aot_opts.logfile, "a+"); + } + + if (acfg->aot_opts.data_outfile) { + acfg->data_outfile = fopen (acfg->aot_opts.data_outfile, "w+"); + if (!acfg->data_outfile) { + aot_printerrf (acfg, "Unable to create file '%s': %s\n", acfg->aot_opts.data_outfile, strerror (errno)); + return 1; + } + acfg->flags = (MonoAotFileFlags)(acfg->flags | MONO_AOT_FILE_FLAG_SEPARATE_DATA); + } + + //acfg->aot_opts.print_skipped_methods = TRUE; + +#if !defined(MONO_ARCH_GSHAREDVT_SUPPORTED) + if (acfg->opts & MONO_OPT_GSHAREDVT) { + aot_printerrf (acfg, "-O=gsharedvt not supported on this platform.\n"); + return 1; + } + if (acfg->aot_opts.llvm_only) { + aot_printerrf (acfg, "--aot=llvmonly requires a runtime that supports gsharedvt.\n"); + return 1; + } +#else + if (acfg->aot_opts.llvm_only || mono_aot_mode_is_full (&acfg->aot_opts) || mono_aot_mode_is_hybrid (&acfg->aot_opts)) + acfg->opts |= MONO_OPT_GSHAREDVT; +#endif + +#if !defined(ENABLE_LLVM) + if (acfg->aot_opts.llvm_only) { + aot_printerrf (acfg, "--aot=llvmonly requires a runtime compiled with llvm support.\n"); + return 1; + } +#endif + + if (acfg->opts & MONO_OPT_GSHAREDVT) + mono_set_generic_sharing_vt_supported (TRUE); + + aot_printf (acfg, "Mono Ahead of Time compiler - compiling assembly %s\n", image->name); + + generate_aotid ((guint8*) &acfg->image->aotid); + + char *aotid = mono_guid_to_string (acfg->image->aotid); + aot_printf (acfg, "AOTID %s\n", aotid); + g_free (aotid); + +#ifndef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES + if (mono_aot_mode_is_full (&acfg->aot_opts)) { + aot_printerrf (acfg, "--aot=full is not supported on this platform.\n"); + return 1; + } +#endif + + if (acfg->aot_opts.direct_pinvoke && !acfg->aot_opts.static_link) { + aot_printerrf (acfg, "The 'direct-pinvoke' AOT option also requires the 'static' AOT option.\n"); + return 1; + } + + if (acfg->aot_opts.static_link) + acfg->aot_opts.asm_writer = TRUE; + + if (acfg->aot_opts.soft_debug) { + MonoDebugOptions *opt = mini_get_debug_options (); + + opt->mdb_optimizations = TRUE; + opt->gen_sdb_seq_points = TRUE; + + if (!mono_debug_enabled ()) { + aot_printerrf (acfg, "The soft-debug AOT option requires the --debug option.\n"); + return 1; + } + acfg->flags = (MonoAotFileFlags)(acfg->flags | MONO_AOT_FILE_FLAG_DEBUG); + } + + if (mono_use_llvm || acfg->aot_opts.llvm) { + acfg->llvm = TRUE; + acfg->aot_opts.asm_writer = TRUE; + acfg->flags = (MonoAotFileFlags)(acfg->flags | MONO_AOT_FILE_FLAG_WITH_LLVM); + + if (acfg->aot_opts.soft_debug) { + aot_printerrf (acfg, "The 'soft-debug' option is not supported when compiling with LLVM.\n"); + return 1; + } + + mini_llvm_init (); + + if (acfg->aot_opts.asm_only && !acfg->aot_opts.llvm_outfile) { + aot_printerrf (acfg, "Compiling with LLVM and the asm-only option requires the llvm-outfile= option.\n"); + return 1; + } + } + + if (mono_aot_mode_is_full (&acfg->aot_opts)) { + acfg->flags = (MonoAotFileFlags)(acfg->flags | MONO_AOT_FILE_FLAG_FULL_AOT); + acfg->is_full_aot = TRUE; + } + + if (mono_threads_is_coop_enabled ()) + acfg->flags = (MonoAotFileFlags)(acfg->flags | MONO_AOT_FILE_FLAG_SAFEPOINTS); + + // The methods in dedup-emit amodules must be available on runtime startup + // Note: Only one such amodule can have this attribute + if (astate && astate->emit_inflated_methods) + acfg->flags = (MonoAotFileFlags)(acfg->flags | MONO_AOT_FILE_FLAG_EAGER_LOAD); + + + if (acfg->aot_opts.instances_logfile_path) { + acfg->instances_logfile = fopen (acfg->aot_opts.instances_logfile_path, "w"); + if (!acfg->instances_logfile) { + aot_printerrf (acfg, "Unable to create logfile: '%s'.\n", acfg->aot_opts.instances_logfile_path); + return 1; + } + } + + if (acfg->aot_opts.profile_files) { + GList *l; + + for (l = acfg->aot_opts.profile_files; l; l = l->next) { + load_profile_file (acfg, (char*)l->data); + } + } + + if (!mono_aot_mode_is_interp (&acfg->aot_opts)) { + int method_index; + + for (method_index = 0; method_index < acfg->image->tables [MONO_TABLE_METHOD].rows; ++method_index) { + g_ptr_array_add (acfg->method_order,GUINT_TO_POINTER (method_index)); + } + } + + acfg->num_trampolines [MONO_AOT_TRAMP_SPECIFIC] = mono_aot_mode_is_full (&acfg->aot_opts) ? acfg->aot_opts.ntrampolines : 0; +#ifdef MONO_ARCH_GSHARED_SUPPORTED + acfg->num_trampolines [MONO_AOT_TRAMP_STATIC_RGCTX] = mono_aot_mode_is_full (&acfg->aot_opts) ? acfg->aot_opts.nrgctx_trampolines : 0; +#endif + acfg->num_trampolines [MONO_AOT_TRAMP_IMT] = mono_aot_mode_is_full (&acfg->aot_opts) ? acfg->aot_opts.nimt_trampolines : 0; +#ifdef MONO_ARCH_GSHAREDVT_SUPPORTED + if (acfg->opts & MONO_OPT_GSHAREDVT) + acfg->num_trampolines [MONO_AOT_TRAMP_GSHAREDVT_ARG] = mono_aot_mode_is_full (&acfg->aot_opts) ? acfg->aot_opts.ngsharedvt_arg_trampolines : 0; +#endif + + acfg->temp_prefix = mono_img_writer_get_temp_label_prefix (NULL); + + arch_init (acfg); + + if (mono_use_llvm || acfg->aot_opts.llvm) { + + /* + * Emit all LLVM code into a separate assembly/object file and link with it + * normally. + */ + if (!acfg->aot_opts.asm_only && acfg->llvm_owriter_supported) { + acfg->llvm_owriter = TRUE; + } else if (acfg->aot_opts.llvm_outfile) { + int len = strlen (acfg->aot_opts.llvm_outfile); + + if (len >= 2 && acfg->aot_opts.llvm_outfile [len - 2] == '.' && acfg->aot_opts.llvm_outfile [len - 1] == 'o') + acfg->llvm_owriter = TRUE; + } + } + + if (acfg->llvm && acfg->thumb_mixed) + acfg->flags = (MonoAotFileFlags)(acfg->flags | MONO_AOT_FILE_FLAG_LLVM_THUMB); + if (acfg->aot_opts.llvm_only) + acfg->flags = (MonoAotFileFlags)(acfg->flags | MONO_AOT_FILE_FLAG_LLVM_ONLY); + + acfg->assembly_name_sym = g_strdup (acfg->image->assembly->aname.name); + /* Get rid of characters which cannot occur in symbols */ + for (p = acfg->assembly_name_sym; *p; ++p) { + if (!(isalnum (*p) || *p == '_')) + *p = '_'; + } + + acfg->global_prefix = g_strdup_printf ("mono_aot_%s", acfg->assembly_name_sym); + acfg->plt_symbol = g_strdup_printf ("%s_plt", acfg->global_prefix); + acfg->got_symbol = g_strdup_printf ("%s_got", acfg->global_prefix); + if (acfg->llvm) { + acfg->llvm_got_symbol = g_strdup_printf ("%s_llvm_got", acfg->global_prefix); + acfg->llvm_eh_frame_symbol = g_strdup_printf ("%s_eh_frame", acfg->global_prefix); + } + + acfg->method_index = 1; + + if (mono_aot_mode_is_full (&acfg->aot_opts) || mono_aot_mode_is_hybrid (&acfg->aot_opts)) + mono_set_partial_sharing_supported (TRUE); + + if (!mono_aot_mode_is_interp (&acfg->aot_opts)) { + res = collect_methods (acfg); + + if (!res) + return 1; + } + + // If we're emitting all of the inflated methods into a dummy + // Assembly, then after extra_methods is set up, we're done + // in this function. + if (astate && astate->emit_inflated_methods) + mono_add_deferred_extra_methods (acfg, astate); + + { + GList *l; + + for (l = acfg->profile_data; l; l = l->next) + resolve_profile_data (acfg, (ProfileData*)l->data); + for (l = acfg->profile_data; l; l = l->next) + add_profile_instances (acfg, (ProfileData*)l->data); + } + + acfg->cfgs_size = acfg->methods->len + 32; + acfg->cfgs = g_new0 (MonoCompile*, acfg->cfgs_size); + + /* PLT offset 0 is reserved for the PLT trampoline */ + acfg->plt_offset = 1; + add_preinit_got_slots (acfg); + +#ifdef ENABLE_LLVM + if (acfg->llvm) { + llvm_acfg = acfg; + mono_llvm_create_aot_module (acfg->image->assembly, acfg->global_prefix, acfg->nshared_got_entries, TRUE, acfg->aot_opts.static_link, acfg->aot_opts.llvm_only); + } +#endif + + if (mono_aot_mode_is_interp (&acfg->aot_opts)) { + for (int i = 0; i < sizeof (interp_in_static_sigs) / sizeof (const char *); i++) { + MonoMethodSignature *sig = mono_create_icall_signature (interp_in_static_sigs [i]); + MonoMethod *wrapper = mini_get_interp_in_wrapper (sig); + add_method (acfg, wrapper); + } + } + + TV_GETTIME (atv); + + compile_methods (acfg); + + TV_GETTIME (btv); + + acfg->stats.jit_time = TV_ELAPSED (atv, btv); + + TV_GETTIME (atv); + +#ifdef ENABLE_LLVM + if (acfg->llvm) { + if (acfg->aot_opts.asm_only) { + if (acfg->aot_opts.outfile) { + acfg->tmpfname = g_strdup_printf ("%s", acfg->aot_opts.outfile); + acfg->tmpbasename = g_strdup (acfg->tmpfname); + } else { + acfg->tmpbasename = g_strdup_printf ("%s", acfg->image->name); + acfg->tmpfname = g_strdup_printf ("%s.s", acfg->tmpbasename); + } + g_assert (acfg->aot_opts.llvm_outfile); + acfg->llvm_sfile = g_strdup (acfg->aot_opts.llvm_outfile); + if (acfg->llvm_owriter) + acfg->llvm_ofile = g_strdup (acfg->aot_opts.llvm_outfile); + else + acfg->llvm_sfile = g_strdup (acfg->aot_opts.llvm_outfile); + } else { + acfg->tmpbasename = (strcmp (acfg->aot_opts.temp_path, "") == 0) ? + g_strdup_printf ("%s", "temp") : + g_build_filename (acfg->aot_opts.temp_path, "temp", NULL); + + acfg->tmpfname = g_strdup_printf ("%s.s", acfg->tmpbasename); + acfg->llvm_sfile = g_strdup_printf ("%s-llvm.s", acfg->tmpbasename); + acfg->llvm_ofile = g_strdup_printf ("%s-llvm.o", acfg->tmpbasename); + } + } +#endif + + if (acfg->aot_opts.asm_only && !acfg->aot_opts.llvm_only) { + if (acfg->aot_opts.outfile) + acfg->tmpfname = g_strdup_printf ("%s", acfg->aot_opts.outfile); + else + acfg->tmpfname = g_strdup_printf ("%s.s", acfg->image->name); + acfg->fp = fopen (acfg->tmpfname, "w+"); + } else { + if (strcmp (acfg->aot_opts.temp_path, "") == 0) { + int i = g_file_open_tmp ("mono_aot_XXXXXX", &acfg->tmpfname, NULL); + acfg->fp = fdopen (i, "w+"); + } else { + acfg->tmpbasename = g_build_filename (acfg->aot_opts.temp_path, "temp", NULL); + acfg->tmpfname = g_strdup_printf ("%s.s", acfg->tmpbasename); + acfg->fp = fopen (acfg->tmpfname, "w+"); + } + } + if (acfg->fp == 0 && !acfg->aot_opts.llvm_only) { + aot_printerrf (acfg, "Unable to open file '%s': %s\n", acfg->tmpfname, strerror (errno)); + return 1; + } + if (acfg->fp) + acfg->w = mono_img_writer_create (acfg->fp, FALSE); + + tmp_outfile_name = NULL; + outfile_name = NULL; + + /* Compute symbols for methods */ + for (i = 0; i < acfg->nmethods; ++i) { + if (acfg->cfgs [i]) { + MonoCompile *cfg = acfg->cfgs [i]; + int method_index = get_method_index (acfg, cfg->orig_method); + + if (COMPILE_LLVM (cfg)) + cfg->asm_symbol = g_strdup_printf ("%s%s", acfg->llvm_label_prefix, cfg->llvm_method_name); + else if (acfg->global_symbols || acfg->llvm) + cfg->asm_symbol = get_debug_sym (cfg->orig_method, "", acfg->method_label_hash); + else + cfg->asm_symbol = g_strdup_printf ("%s%sm_%x", acfg->temp_prefix, acfg->llvm_label_prefix, method_index); + cfg->asm_debug_symbol = cfg->asm_symbol; + } + } + + if (acfg->aot_opts.dwarf_debug && acfg->aot_opts.gnu_asm) { + /* + * CLANG supports GAS .file/.loc directives, so emit line number information this way + */ + acfg->gas_line_numbers = TRUE; + } + +#ifdef EMIT_DWARF_INFO + if ((!acfg->aot_opts.nodebug || acfg->aot_opts.dwarf_debug) && acfg->has_jitted_code) { + if (acfg->aot_opts.dwarf_debug && !mono_debug_enabled ()) { + aot_printerrf (acfg, "The dwarf AOT option requires the --debug option.\n"); + return 1; + } + acfg->dwarf = mono_dwarf_writer_create (acfg->w, NULL, 0, !acfg->gas_line_numbers); + } +#endif /* EMIT_DWARF_INFO */ + + if (acfg->w) + mono_img_writer_emit_start (acfg->w); + + if (acfg->dwarf) + mono_dwarf_writer_emit_base_info (acfg->dwarf, g_path_get_basename (acfg->image->name), mono_unwind_get_cie_program ()); + + emit_code (acfg); + if (acfg->aot_opts.dedup) + mono_flush_method_cache (acfg); + if (acfg->aot_opts.dedup || acfg->dedup_emit_mode) + mono_dedup_log_stats (acfg); + + emit_info (acfg); + + emit_extra_methods (acfg); + + if (acfg->aot_opts.dedup_include && !is_dedup_dummy) + return 0; + + emit_trampolines (acfg); + + emit_class_name_table (acfg); + + emit_got_info (acfg, FALSE); + if (acfg->llvm) + emit_got_info (acfg, TRUE); + + emit_exception_info (acfg); + + emit_unwind_info (acfg); + + emit_class_info (acfg); + + emit_plt (acfg); + + emit_image_table (acfg); + + emit_weak_field_indexes (acfg); + + emit_got (acfg); + + { + /* + * The managed allocators are GC specific, so can't use an AOT image created by one GC + * in another. + */ + const char *gc_name = mono_gc_get_gc_name (); + acfg->gc_name_offset = add_to_blob (acfg, (guint8*)gc_name, strlen (gc_name) + 1); + } + + emit_blob (acfg); + + emit_objc_selectors (acfg); + + emit_globals (acfg); + + emit_file_info (acfg); + + emit_library_info (acfg); + + if (acfg->dwarf) { + emit_dwarf_info (acfg); + mono_dwarf_writer_close (acfg->dwarf); + } else { + if (!acfg->aot_opts.nodebug) + emit_codeview_info (acfg); + } + + emit_mem_end (acfg); + + if (acfg->need_pt_gnu_stack) { + /* This is required so the .so doesn't have an executable stack */ + /* The bin writer already emits this */ + fprintf (acfg->fp, "\n.section .note.GNU-stack,\"\",@progbits\n"); + } + + if (acfg->aot_opts.data_outfile) + fclose (acfg->data_outfile); + +#ifdef ENABLE_LLVM + if (acfg->llvm) { + gboolean res; + + res = emit_llvm_file (acfg); + if (!res) + return 1; + } +#endif + + TV_GETTIME (btv); + + acfg->stats.gen_time = TV_ELAPSED (atv, btv); + + if (acfg->llvm) + sprintf (llvm_stats_msg, ", LLVM: %d (%d%%)", acfg->stats.llvm_count, acfg->stats.mcount ? (acfg->stats.llvm_count * 100) / acfg->stats.mcount : 100); + else + strcpy (llvm_stats_msg, ""); + + all_sizes = acfg->stats.code_size + acfg->stats.info_size + acfg->stats.ex_info_size + acfg->stats.unwind_info_size + acfg->stats.class_info_size + acfg->stats.got_info_size + acfg->stats.offsets_size + acfg->stats.plt_size; + + aot_printf (acfg, "Code: %d(%d%%) Info: %d(%d%%) Ex Info: %d(%d%%) Unwind Info: %d(%d%%) Class Info: %d(%d%%) PLT: %d(%d%%) GOT Info: %d(%d%%) Offsets: %d(%d%%) GOT: %d\n", + (int)acfg->stats.code_size, (int)(acfg->stats.code_size * 100 / all_sizes), + (int)acfg->stats.info_size, (int)(acfg->stats.info_size * 100 / all_sizes), + (int)acfg->stats.ex_info_size, (int)(acfg->stats.ex_info_size * 100 / all_sizes), + (int)acfg->stats.unwind_info_size, (int)(acfg->stats.unwind_info_size * 100 / all_sizes), + (int)acfg->stats.class_info_size, (int)(acfg->stats.class_info_size * 100 / all_sizes), + acfg->stats.plt_size ? (int)acfg->stats.plt_size : (int)acfg->plt_offset, acfg->stats.plt_size ? (int)(acfg->stats.plt_size * 100 / all_sizes) : 0, + (int)acfg->stats.got_info_size, (int)(acfg->stats.got_info_size * 100 / all_sizes), + (int)acfg->stats.offsets_size, (int)(acfg->stats.offsets_size * 100 / all_sizes), + (int)(acfg->got_offset * sizeof (gpointer))); + aot_printf (acfg, "Compiled: %d/%d (%d%%)%s, No GOT slots: %d (%d%%), Direct calls: %d (%d%%)\n", + acfg->stats.ccount, acfg->stats.mcount, acfg->stats.mcount ? (acfg->stats.ccount * 100) / acfg->stats.mcount : 100, + llvm_stats_msg, + acfg->stats.methods_without_got_slots, acfg->stats.mcount ? (acfg->stats.methods_without_got_slots * 100) / acfg->stats.mcount : 100, + acfg->stats.direct_calls, acfg->stats.all_calls ? (acfg->stats.direct_calls * 100) / acfg->stats.all_calls : 100); + if (acfg->stats.genericcount) + aot_printf (acfg, "%d methods are generic (%d%%)\n", acfg->stats.genericcount, acfg->stats.mcount ? (acfg->stats.genericcount * 100) / acfg->stats.mcount : 100); + if (acfg->stats.abscount) + aot_printf (acfg, "%d methods contain absolute addresses (%d%%)\n", acfg->stats.abscount, acfg->stats.mcount ? (acfg->stats.abscount * 100) / acfg->stats.mcount : 100); + if (acfg->stats.lmfcount) + aot_printf (acfg, "%d methods contain lmf pointers (%d%%)\n", acfg->stats.lmfcount, acfg->stats.mcount ? (acfg->stats.lmfcount * 100) / acfg->stats.mcount : 100); + if (acfg->stats.ocount) + aot_printf (acfg, "%d methods have other problems (%d%%)\n", acfg->stats.ocount, acfg->stats.mcount ? (acfg->stats.ocount * 100) / acfg->stats.mcount : 100); + + TV_GETTIME (atv); + if (acfg->w) { + res = mono_img_writer_emit_writeout (acfg->w); + if (res != 0) { + acfg_free (acfg); + return res; + } + res = compile_asm (acfg); + if (res != 0) { + acfg_free (acfg); + return res; + } + } + TV_GETTIME (btv); + acfg->stats.link_time = TV_ELAPSED (atv, btv); + + if (acfg->aot_opts.stats) { + int i; + + aot_printf (acfg, "GOT slot distribution:\n"); + for (i = 0; i < MONO_PATCH_INFO_NUM; ++i) + if (acfg->stats.got_slot_types [i]) + aot_printf (acfg, "\t%s: %d (%d)\n", get_patch_name (i), acfg->stats.got_slot_types [i], acfg->stats.got_slot_info_sizes [i]); + aot_printf (acfg, "\nMethod stats:\n"); + aot_printf (acfg, "\tNormal: %d\n", acfg->stats.method_categories [METHOD_CAT_NORMAL]); + aot_printf (acfg, "\tInstance: %d\n", acfg->stats.method_categories [METHOD_CAT_INST]); + aot_printf (acfg, "\tGSharedvt: %d\n", acfg->stats.method_categories [METHOD_CAT_GSHAREDVT]); + aot_printf (acfg, "\tWrapper: %d\n", acfg->stats.method_categories [METHOD_CAT_WRAPPER]); + } + + aot_printf (acfg, "JIT time: %d ms, Generation time: %d ms, Assembly+Link time: %d ms.\n", acfg->stats.jit_time / 1000, acfg->stats.gen_time / 1000, acfg->stats.link_time / 1000); + + if (acfg->aot_opts.dump_json) + aot_dump (acfg); + + acfg_free (acfg); + + return 0; +} + +#else + +/* AOT disabled */ + +void* +mono_aot_readonly_field_override (MonoClassField *field) +{ + return NULL; +} + +int +mono_compile_assembly (MonoAssembly *ass, guint32 opts, const char *aot_options, gpointer **aot_state) +{ + return 0; +} + +gboolean +mono_aot_is_shared_got_offset (int offset) +{ + return FALSE; +} + +int +mono_compile_deferred_assemblies (guint32 opts, const char *aot_options, gpointer **aot_state) +{ + g_assert_not_reached (); + return 0; +} + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/mini/aot-compiler.h b/unity-2019.4.24f1-mbe/mono/mini/aot-compiler.h new file mode 100644 index 000000000..9de33cc9d --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/aot-compiler.h @@ -0,0 +1,30 @@ +/** + * \file + */ + +#ifndef __MONO_AOT_COMPILER_H__ +#define __MONO_AOT_COMPILER_H__ + +#include "mini.h" + +int mono_compile_assembly (MonoAssembly *ass, guint32 opts, const char *aot_options, gpointer **aot_state); +int mono_compile_deferred_assemblies (guint32 opts, const char *aot_options, gpointer **aot_state); +void* mono_aot_readonly_field_override (MonoClassField *field); +gboolean mono_aot_is_shared_got_offset (int offset) MONO_LLVM_INTERNAL; + +guint32 mono_aot_get_got_offset (MonoJumpInfo *ji) MONO_LLVM_INTERNAL; +char* mono_aot_get_method_name (MonoCompile *cfg) MONO_LLVM_INTERNAL; +char* mono_aot_get_mangled_method_name (MonoMethod *method) MONO_LLVM_INTERNAL; +gboolean mono_aot_is_linkonce_method (MonoMethod *method) MONO_LLVM_INTERNAL; +gboolean mono_aot_is_direct_callable (MonoJumpInfo *patch_info) MONO_LLVM_INTERNAL; +void mono_aot_mark_unused_llvm_plt_entry(MonoJumpInfo *patch_info) MONO_LLVM_INTERNAL; +char* mono_aot_get_plt_symbol (MonoJumpInfoType type, gconstpointer data) MONO_LLVM_INTERNAL; +char* mono_aot_get_direct_call_symbol (MonoJumpInfoType type, gconstpointer data) MONO_LLVM_INTERNAL; +int mono_aot_get_method_index (MonoMethod *method) MONO_LLVM_INTERNAL; +MonoJumpInfo* mono_aot_patch_info_dup (MonoJumpInfo* ji) MONO_LLVM_INTERNAL; + +#endif + + + + diff --git a/unity-2019.4.24f1-mbe/mono/mini/aot-runtime-wasm.c b/unity-2019.4.24f1-mbe/mono/mini/aot-runtime-wasm.c new file mode 100644 index 000000000..fa9b7ad0b --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/aot-runtime-wasm.c @@ -0,0 +1,182 @@ +/** + * \file + * WASM AOT runtime + */ + +#include "config.h" + +#include + +#include "mini.h" +#include "interp/interp.h" + +#ifdef TARGET_WASM + +static void +wasm_restore_context (void) +{ + g_error ("wasm_restore_context"); +} + +static void +wasm_call_filter (void) +{ + g_error ("wasm_call_filter"); +} + +static void +wasm_throw_exception (void) +{ + g_error ("wasm_throw_exception"); +} + +static void +wasm_rethrow_exception (void) +{ + g_error ("wasm_rethrow_exception"); +} + +static void +wasm_throw_corlib_exception (void) +{ + g_error ("wasm_throw_corlib_exception"); +} + +static char +type_to_c (MonoType *t) +{ + if (t->byref) + return 'I'; + +handle_enum: + switch (t->type) { + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_CHAR: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_PTR: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_CLASS: + case MONO_TYPE_OBJECT: + case MONO_TYPE_STRING: + return 'I'; + case MONO_TYPE_R4: + return 'F'; + case MONO_TYPE_R8: + return 'D'; + break; + case MONO_TYPE_I8: + case MONO_TYPE_U8: + return 'L'; + case MONO_TYPE_VOID: + return 'V'; + case MONO_TYPE_VALUETYPE: + if (t->data.klass->enumtype) { + t = mono_class_enum_basetype (t->data.klass); + goto handle_enum; + } + + return 'I'; + case MONO_TYPE_GENERICINST: + if (t->data.klass->valuetype) + return 'S'; + return 'I'; + default: + g_warning ("CANT TRANSLATE %s", mono_type_full_name (t)); + return 'X'; + } +} + +#if SIZEOF_VOID_P == 4 +#define FIDX(x) ((x) * 2) +#else +#define FIDX(x) (x) +#endif + + + +typedef union { + gint64 l; + struct { + gint32 lo; + gint32 hi; + } pair; +} interp_pair; + +static inline gint64 +get_long_arg (InterpMethodArguments *margs, int idx) +{ + interp_pair p; + p.pair.lo = (gint32)margs->iargs [idx]; + p.pair.hi = (gint32)margs->iargs [idx + 1]; + return p.l; +} + +#include "wasm_m2n_invoke.g.h" + +static void +wasm_enter_icall_trampoline (void *target_func, InterpMethodArguments *margs) +{ + static char cookie [8]; + static int c_count; + + MonoMethodSignature *sig = margs->sig; + + c_count = sig->param_count + sig->hasthis + 1; + cookie [0] = type_to_c (sig->ret); + if (sig->hasthis) + cookie [1] = 'I'; + for (int i = 0; i < sig->param_count; ++i) + cookie [1 + sig->hasthis + i ] = type_to_c (sig->params [i]); + cookie [c_count] = 0; + + icall_trampoline_dispatch (cookie, target_func, margs); +} + +gpointer +mono_aot_get_trampoline_full (const char *name, MonoTrampInfo **out_tinfo) +{ + gpointer code = NULL; + + if (!strcmp (name, "restore_context")) + code = wasm_restore_context; + else if (!strcmp (name, "call_filter")) + code = wasm_call_filter; + else if (!strcmp (name, "throw_exception")) + code = wasm_throw_exception; + else if (!strcmp (name, "rethrow_exception")) + code = wasm_rethrow_exception; + else if (!strcmp (name, "throw_corlib_exception")) + code = wasm_throw_corlib_exception; + else if (!strcmp (name, "enter_icall_trampoline")) + code = wasm_enter_icall_trampoline; + + g_assert (code); + + if (out_tinfo) { + MonoTrampInfo *tinfo = g_new0 (MonoTrampInfo, 1); + tinfo->code = code; + tinfo->code_size = 1; + tinfo->name = g_strdup (name); + tinfo->ji = NULL; + tinfo->unwind_ops = NULL; + tinfo->uw_info = NULL; + tinfo->uw_info_len = 0; + tinfo->owns_uw_info = FALSE; + + *out_tinfo = tinfo; + } + + return code; +} +#else /* TARGET_WASM */ + +MONO_EMPTY_SOURCE_FILE (aot_runtime_wasm); + +#endif /* TARGET_WASM */ diff --git a/unity-2019.4.24f1-mbe/mono/mini/aot-runtime.c b/unity-2019.4.24f1-mbe/mono/mini/aot-runtime.c new file mode 100644 index 000000000..baa8be474 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/aot-runtime.c @@ -0,0 +1,6116 @@ +/** + * \file + * mono Ahead of Time compiler + * + * Author: + * Dietmar Maurer (dietmar@ximian.com) + * Zoltan Varga (vargaz@gmail.com) + * + * (C) 2002 Ximian, Inc. + * Copyright 2003-2011 Novell, Inc. + * Copyright 2011 Xamarin, Inc. + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include "config.h" +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#ifdef HAVE_SYS_MMAN_H +#include +#endif + +#if HOST_WIN32 +#include +#include +#endif + +#ifdef HAVE_EXECINFO_H +#include +#endif + +#include +#include + +#ifdef HAVE_SYS_WAIT_H +#include /* for WIFEXITED, WEXITSTATUS */ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mini.h" +#include "seq-points.h" +#include "version.h" +#include "debugger-agent.h" +#include "aot-compiler.h" +#include "aot-runtime.h" +#include "jit-icalls.h" +#include "mini-runtime.h" + +#ifndef DISABLE_AOT + +#ifdef TARGET_OSX +#define ENABLE_AOT_CACHE +#endif + +/* Number of got entries shared between the JIT and LLVM GOT */ +#define N_COMMON_GOT_ENTRIES 10 + +#define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1)) +#define ALIGN_PTR_TO(ptr,align) (gpointer)((((gssize)(ptr)) + (align - 1)) & (~(align - 1))) +#define ROUND_DOWN(VALUE,SIZE) ((VALUE) & ~((SIZE) - 1)) + +typedef struct { + int method_index; + MonoJitInfo *jinfo; +} JitInfoMap; + +typedef struct MonoAotModule { + char *aot_name; + /* Pointer to the Global Offset Table */ + gpointer *got; + gpointer *llvm_got; + gpointer *shared_got; + GHashTable *name_cache; + GHashTable *extra_methods; + /* Maps methods to their code */ + GHashTable *method_to_code; + /* Maps pointers into the method info to the methods themselves */ + GHashTable *method_ref_to_method; + MonoAssemblyName *image_names; + char **image_guids; + MonoAssembly *assembly; + MonoImage **image_table; + guint32 image_table_len; + gboolean out_of_date; + gboolean plt_inited; + gboolean got_initializing; + guint8 *mem_begin; + guint8 *mem_end; + guint8 *jit_code_start; + guint8 *jit_code_end; + guint8 *llvm_code_start; + guint8 *llvm_code_end; + guint8 *plt; + guint8 *plt_end; + guint8 *blob; + /* Maps method indexes to their code */ + gpointer *methods; + /* Sorted array of method addresses */ + gpointer *sorted_methods; + /* Method indexes for each method in sorted_methods */ + int *sorted_method_indexes; + /* The length of the two tables above */ + int sorted_methods_len; + guint32 *method_info_offsets; + guint32 *ex_info_offsets; + guint32 *class_info_offsets; + guint32 *got_info_offsets; + guint32 *llvm_got_info_offsets; + guint32 *methods_loaded; + guint16 *class_name_table; + guint32 *extra_method_table; + guint32 *extra_method_info_offsets; + guint32 *unbox_trampolines; + guint32 *unbox_trampolines_end; + guint32 *unbox_trampoline_addresses; + guint8 *unwind_info; + + /* Points to the mono EH data created by LLVM */ + guint8 *mono_eh_frame; + + /* Points to the data tables if MONO_AOT_FILE_FLAG_SEPARATE_DATA is set */ + gpointer tables [MONO_AOT_TABLE_NUM]; + /* Points to the trampolines */ + guint8 *trampolines [MONO_AOT_TRAMP_NUM]; + /* The first unused trampoline of each kind */ + guint32 trampoline_index [MONO_AOT_TRAMP_NUM]; + + gboolean use_page_trampolines; + + MonoAotFileInfo info; + + gpointer *globals; + MonoDl *sofile; + + JitInfoMap *async_jit_info_table; + mono_mutex_t mutex; +} MonoAotModule; + +typedef struct { + void *next; + unsigned char *trampolines; + unsigned char *trampolines_end; +} TrampolinePage; + +static GHashTable *aot_modules; +#define mono_aot_lock() mono_os_mutex_lock (&aot_mutex) +#define mono_aot_unlock() mono_os_mutex_unlock (&aot_mutex) +static mono_mutex_t aot_mutex; + +/* + * Maps assembly names to the mono_aot_module__info symbols in the + * AOT modules registered by mono_aot_register_module (). + */ +static GHashTable *static_aot_modules; +/* + * Same as above, but tracks module that must be loaded before others are + * This allows us to have a "container" module which contains resources for + * other modules. Since it doesn't provide methods for a managed assembly, + * and it needs to be fully loaded by the time the other code needs it, it + * must be eagerly loaded before other modules. + */ +static char *container_assm_name = NULL; +static MonoAotModule *container_amodule = NULL; + +/* + * Maps MonoJitInfo* to the aot module they belong to, this can be different + * from ji->method->klass->image's aot module for generic instances. + */ +static GHashTable *ji_to_amodule; + +/* + * Whenever to AOT compile loaded assemblies on demand and store them in + * a cache. + */ +static gboolean enable_aot_cache = FALSE; + +static gboolean mscorlib_aot_loaded; + +/* For debugging */ +static gint32 mono_last_aot_method = -1; + +static gboolean make_unreadable = FALSE; +static guint32 name_table_accesses = 0; +static guint32 n_pagefaults = 0; + +/* Used to speed-up find_aot_module () */ +static gsize aot_code_low_addr = (gssize)-1; +static gsize aot_code_high_addr = 0; + +/* Stats */ +static gint32 async_jit_info_size; + +static GHashTable *aot_jit_icall_hash; + +#ifdef MONOTOUCH +#define USE_PAGE_TRAMPOLINES ((MonoAotModule*)mono_defaults.corlib->aot_module)->use_page_trampolines +#else +#define USE_PAGE_TRAMPOLINES 0 +#endif + +#define mono_aot_page_lock() mono_os_mutex_lock (&aot_page_mutex) +#define mono_aot_page_unlock() mono_os_mutex_unlock (&aot_page_mutex) +static mono_mutex_t aot_page_mutex; + +static MonoAotModule *mscorlib_aot_module; + +/* Embedding API hooks to load the AOT data for AOT images compiled with MONO_AOT_FILE_FLAG_SEPARATE_DATA */ +static MonoLoadAotDataFunc aot_data_load_func; +static MonoFreeAotDataFunc aot_data_free_func; +static gpointer aot_data_func_user_data; + +static void +init_plt (MonoAotModule *info); + +static void +compute_llvm_code_range (MonoAotModule *amodule, guint8 **code_start, guint8 **code_end); + +static gboolean +init_method (MonoAotModule *amodule, guint32 method_index, MonoMethod *method, MonoClass *init_class, MonoGenericContext *context, MonoError *error); + +static MonoJumpInfo* +decode_patches (MonoAotModule *amodule, MonoMemPool *mp, int n_patches, gboolean llvm, guint32 *got_offsets); + +static inline void +amodule_lock (MonoAotModule *amodule) +{ + mono_os_mutex_lock (&amodule->mutex); +} + +static inline void +amodule_unlock (MonoAotModule *amodule) +{ + mono_os_mutex_unlock (&amodule->mutex); +} + +/* + * load_image: + * + * Load one of the images referenced by AMODULE. Returns NULL if the image is not + * found, and sets @error for what happened + */ +static MonoImage * +load_image (MonoAotModule *amodule, int index, MonoError *error) +{ + MonoAssembly *assembly; + MonoImageOpenStatus status; + + g_assert (index < amodule->image_table_len); + + error_init (error); + + if (amodule->image_table [index]) + return amodule->image_table [index]; + if (amodule->out_of_date) { + mono_error_set_bad_image_name (error, amodule->aot_name, "Image out of date"); + return NULL; + } + + assembly = mono_assembly_load (&amodule->image_names [index], amodule->assembly->basedir, &status); + if (!assembly) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT: module %s is unusable because dependency %s is not found.", amodule->aot_name, amodule->image_names [index].name); + mono_error_set_bad_image_name (error, amodule->aot_name, "module is unusable because dependency %s is not found (error %d).\n", amodule->image_names [index].name, status); + amodule->out_of_date = TRUE; + return NULL; + } + + if (strcmp (assembly->image->guid, amodule->image_guids [index])) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT: module %s is unusable (GUID of dependent assembly %s doesn't match (expected '%s', got '%s').", amodule->aot_name, amodule->image_names [index].name, amodule->image_guids [index], assembly->image->guid); + mono_error_set_bad_image_name (error, amodule->aot_name, "module is unusable (GUID of dependent assembly %s doesn't match (expected '%s', got '%s').", amodule->image_names [index].name, amodule->image_guids [index], assembly->image->guid); + amodule->out_of_date = TRUE; + return NULL; + } + + amodule->image_table [index] = assembly->image; + return assembly->image; +} + +static inline gint32 +decode_value (guint8 *ptr, guint8 **rptr) +{ + guint8 b = *ptr; + gint32 len; + + if ((b & 0x80) == 0){ + len = b; + ++ptr; + } else if ((b & 0x40) == 0){ + len = ((b & 0x3f) << 8 | ptr [1]); + ptr += 2; + } else if (b != 0xff) { + len = ((b & 0x1f) << 24) | + (ptr [1] << 16) | + (ptr [2] << 8) | + ptr [3]; + ptr += 4; + } + else { + len = (ptr [1] << 24) | (ptr [2] << 16) | (ptr [3] << 8) | ptr [4]; + ptr += 5; + } + if (rptr) + *rptr = ptr; + + //printf ("DECODE: %d.\n", len); + return len; +} + +/* + * mono_aot_get_offset: + * + * Decode an offset table emitted by emit_offset_table (), returning the INDEXth + * entry. + */ +static guint32 +mono_aot_get_offset (guint32 *table, int index) +{ + int i, group, ngroups, index_entry_size; + int start_offset, offset, group_size; + guint8 *data_start, *p; + guint32 *index32 = NULL; + guint16 *index16 = NULL; + + /* noffsets = table [0]; */ + group_size = table [1]; + ngroups = table [2]; + index_entry_size = table [3]; + group = index / group_size; + + if (index_entry_size == 2) { + index16 = (guint16*)&table [4]; + data_start = (guint8*)&index16 [ngroups]; + p = data_start + index16 [group]; + } else { + index32 = (guint32*)&table [4]; + data_start = (guint8*)&index32 [ngroups]; + p = data_start + index32 [group]; + } + + /* offset will contain the value of offsets [group * group_size] */ + offset = start_offset = decode_value (p, &p); + for (i = group * group_size + 1; i <= index; ++i) { + offset += decode_value (p, &p); + } + + //printf ("Offset lookup: %d -> %d, start=%d, p=%d\n", index, offset, start_offset, table [3 + group]); + + return offset; +} + +static MonoMethod* +decode_resolve_method_ref (MonoAotModule *module, guint8 *buf, guint8 **endbuf, MonoError *error); + +static MonoClass* +decode_klass_ref (MonoAotModule *module, guint8 *buf, guint8 **endbuf, MonoError *error); + +static MonoType* +decode_type (MonoAotModule *module, guint8 *buf, guint8 **endbuf, MonoError *error); + +static MonoGenericInst* +decode_generic_inst (MonoAotModule *module, guint8 *buf, guint8 **endbuf, MonoError *error) +{ + int type_argc, i; + MonoType **type_argv; + MonoGenericInst *inst; + guint8 *p = buf; + + error_init (error); + type_argc = decode_value (p, &p); + type_argv = g_new0 (MonoType*, type_argc); + + for (i = 0; i < type_argc; ++i) { + MonoClass *pclass = decode_klass_ref (module, p, &p, error); + if (!pclass) { + g_free (type_argv); + return NULL; + } + type_argv [i] = &pclass->byval_arg; + } + + inst = mono_metadata_get_generic_inst (type_argc, type_argv); + g_free (type_argv); + + *endbuf = p; + + return inst; +} + +static gboolean +decode_generic_context (MonoAotModule *module, MonoGenericContext *ctx, guint8 *buf, guint8 **endbuf, MonoError *error) +{ + guint8 *p = buf; + guint8 *p2; + int argc; + error_init (error); + + p2 = p; + argc = decode_value (p, &p); + if (argc) { + p = p2; + ctx->class_inst = decode_generic_inst (module, p, &p, error); + if (!ctx->class_inst) + return FALSE; + } + p2 = p; + argc = decode_value (p, &p); + if (argc) { + p = p2; + ctx->method_inst = decode_generic_inst (module, p, &p, error); + if (!ctx->method_inst) + return FALSE; + } + + *endbuf = p; + return TRUE; +} + +static MonoClass* +decode_klass_ref (MonoAotModule *module, guint8 *buf, guint8 **endbuf, MonoError *error) +{ + MonoImage *image; + MonoClass *klass = NULL, *eklass; + guint32 token, rank, idx; + guint8 *p = buf; + int reftype; + + error_init (error); + reftype = decode_value (p, &p); + if (reftype == 0) { + *endbuf = p; + mono_error_set_bad_image_name (error, module->aot_name, "Decoding a null class ref"); + return NULL; + } + + switch (reftype) { + case MONO_AOT_TYPEREF_TYPEDEF_INDEX: + idx = decode_value (p, &p); + image = load_image (module, 0, error); + if (!image) + return NULL; + klass = mono_class_get_checked (image, MONO_TOKEN_TYPE_DEF + idx, error); + break; + case MONO_AOT_TYPEREF_TYPEDEF_INDEX_IMAGE: + idx = decode_value (p, &p); + image = load_image (module, decode_value (p, &p), error); + if (!image) + return NULL; + klass = mono_class_get_checked (image, MONO_TOKEN_TYPE_DEF + idx, error); + break; + case MONO_AOT_TYPEREF_TYPESPEC_TOKEN: + token = decode_value (p, &p); + image = module->assembly->image; + if (!image) { + mono_error_set_bad_image_name (error, module->aot_name, "No image associated with the aot module"); + return NULL; + } + klass = mono_class_get_checked (image, token, error); + break; + case MONO_AOT_TYPEREF_GINST: { + MonoClass *gclass; + MonoGenericContext ctx; + MonoType *type; + + gclass = decode_klass_ref (module, p, &p, error); + if (!gclass) + return NULL; + g_assert (mono_class_is_gtd (gclass)); + + memset (&ctx, 0, sizeof (ctx)); + ctx.class_inst = decode_generic_inst (module, p, &p, error); + if (!ctx.class_inst) + return NULL; + type = mono_class_inflate_generic_type_checked (&gclass->byval_arg, &ctx, error); + if (!type) + return NULL; + klass = mono_class_from_mono_type (type); + mono_metadata_free_type (type); + break; + } + case MONO_AOT_TYPEREF_VAR: { + MonoType *t = NULL; + MonoGenericContainer *container = NULL; + gboolean has_constraint = decode_value (p, &p); + + if (has_constraint) { + MonoClass *par_klass; + MonoType *gshared_constraint; + + gshared_constraint = decode_type (module, p, &p, error); + if (!gshared_constraint) + return NULL; + + par_klass = decode_klass_ref (module, p, &p, error); + if (!par_klass) + return NULL; + + t = mini_get_shared_gparam (&par_klass->byval_arg, gshared_constraint); + mono_metadata_free_type (gshared_constraint); + klass = mono_class_from_mono_type (t); + } else { + int type = decode_value (p, &p); + int num = decode_value (p, &p); + gboolean is_not_anonymous = decode_value (p, &p); + + if (is_not_anonymous) { + gboolean is_method = decode_value (p, &p); + + if (is_method) { + MonoMethod *method_def; + g_assert (type == MONO_TYPE_MVAR); + method_def = decode_resolve_method_ref (module, p, &p, error); + if (!method_def) + return NULL; + + container = mono_method_get_generic_container (method_def); + } else { + MonoClass *class_def; + g_assert (type == MONO_TYPE_VAR); + class_def = decode_klass_ref (module, p, &p, error); + if (!class_def) + return NULL; + + container = mono_class_try_get_generic_container (class_def); //FIXME is this a case for a try_get? + } + } else { + // We didn't decode is_method, so we have to infer it from type enum. + container = get_anonymous_container_for_image (module->assembly->image, type == MONO_TYPE_MVAR); + } + + t = g_new0 (MonoType, 1); + t->type = (MonoTypeEnum)type; + if (is_not_anonymous) { + t->data.generic_param = mono_generic_container_get_param (container, num); + } else { + /* Anonymous */ + MonoGenericParam *par = (MonoGenericParam*)mono_image_alloc0 (module->assembly->image, sizeof (MonoGenericParamFull)); + par->owner = container; + par->num = num; + t->data.generic_param = par; + ((MonoGenericParamFull*)par)->info.name = make_generic_name_string (module->assembly->image, num); + } + // FIXME: Maybe use types directly to avoid + // the overhead of creating MonoClass-es + klass = mono_class_from_mono_type (t); + + g_free (t); + } + break; + } + case MONO_AOT_TYPEREF_ARRAY: + /* Array */ + rank = decode_value (p, &p); + eklass = decode_klass_ref (module, p, &p, error); + if (!eklass) + return NULL; + klass = mono_array_class_get (eklass, rank); + break; + case MONO_AOT_TYPEREF_PTR: { + MonoType *t; + + t = decode_type (module, p, &p, error); + if (!t) + return NULL; + klass = mono_class_from_mono_type (t); + g_free (t); + break; + } + case MONO_AOT_TYPEREF_BLOB_INDEX: { + guint32 offset = decode_value (p, &p); + guint8 *p2; + + p2 = module->blob + offset; + klass = decode_klass_ref (module, p2, &p2, error); + break; + } + default: + mono_error_set_bad_image_name (error, module->aot_name, "Invalid klass reftype %d", reftype); + } + //g_assert (klass); + //printf ("BLA: %s\n", mono_type_full_name (&klass->byval_arg)); + *endbuf = p; + return klass; +} + +static MonoClassField* +decode_field_info (MonoAotModule *module, guint8 *buf, guint8 **endbuf) +{ + MonoError error; + MonoClass *klass = decode_klass_ref (module, buf, &buf, &error); + guint32 token; + guint8 *p = buf; + + if (!klass) { + mono_error_cleanup (&error); /* FIXME don't swallow the error */ + return NULL; + } + + token = MONO_TOKEN_FIELD_DEF + decode_value (p, &p); + + *endbuf = p; + + return mono_class_get_field (klass, token); +} + +/* + * Parse a MonoType encoded by encode_type () in aot-compiler.c. Return malloc-ed + * memory. + */ +static MonoType* +decode_type (MonoAotModule *module, guint8 *buf, guint8 **endbuf, MonoError *error) +{ + guint8 *p = buf; + MonoType *t; + + t = (MonoType *)g_malloc0 (sizeof (MonoType)); + error_init (error); + + while (TRUE) { + if (*p == MONO_TYPE_PINNED) { + t->pinned = TRUE; + ++p; + } else if (*p == MONO_TYPE_BYREF) { + t->byref = TRUE; + ++p; + } else { + break; + } + } + + t->type = (MonoTypeEnum)*p; + ++p; + + switch (t->type) { + case MONO_TYPE_VOID: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_CHAR: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_STRING: + case MONO_TYPE_OBJECT: + case MONO_TYPE_TYPEDBYREF: + break; + case MONO_TYPE_VALUETYPE: + case MONO_TYPE_CLASS: + t->data.klass = decode_klass_ref (module, p, &p, error); + if (!t->data.klass) + goto fail; + break; + case MONO_TYPE_SZARRAY: + t->data.klass = decode_klass_ref (module, p, &p, error); + + if (!t->data.klass) + goto fail; + break; + case MONO_TYPE_PTR: + t->data.type = decode_type (module, p, &p, error); + if (!t->data.type) + goto fail; + break; + case MONO_TYPE_GENERICINST: { + MonoClass *gclass; + MonoGenericContext ctx; + MonoType *type; + MonoClass *klass; + + gclass = decode_klass_ref (module, p, &p, error); + if (!gclass) + goto fail; + g_assert (mono_class_is_gtd (gclass)); + + memset (&ctx, 0, sizeof (ctx)); + ctx.class_inst = decode_generic_inst (module, p, &p, error); + if (!ctx.class_inst) + goto fail; + type = mono_class_inflate_generic_type_checked (&gclass->byval_arg, &ctx, error); + if (!type) + goto fail; + klass = mono_class_from_mono_type (type); + t->data.generic_class = mono_class_get_generic_class (klass); + break; + } + case MONO_TYPE_ARRAY: { + MonoArrayType *array; + int i; + + // FIXME: memory management + array = g_new0 (MonoArrayType, 1); + array->eklass = decode_klass_ref (module, p, &p, error); + if (!array->eklass) + goto fail; + array->rank = decode_value (p, &p); + array->numsizes = decode_value (p, &p); + + if (array->numsizes) + array->sizes = (int *)g_malloc0 (sizeof (int) * array->numsizes); + for (i = 0; i < array->numsizes; ++i) + array->sizes [i] = decode_value (p, &p); + + array->numlobounds = decode_value (p, &p); + if (array->numlobounds) + array->lobounds = (int *)g_malloc0 (sizeof (int) * array->numlobounds); + for (i = 0; i < array->numlobounds; ++i) + array->lobounds [i] = decode_value (p, &p); + t->data.array = array; + break; + } + case MONO_TYPE_VAR: + case MONO_TYPE_MVAR: { + MonoClass *klass = decode_klass_ref (module, p, &p, error); + if (!klass) + goto fail; + t->data.generic_param = klass->byval_arg.data.generic_param; + break; + } + default: + mono_error_set_bad_image_name (error, module->aot_name, "Invalid encoded type %d", t->type); + goto fail; + } + + *endbuf = p; + + return t; +fail: + g_free (t); + return NULL; +} + +// FIXME: Error handling, memory management + +static MonoMethodSignature* +decode_signature_with_target (MonoAotModule *module, MonoMethodSignature *target, guint8 *buf, guint8 **endbuf) +{ + MonoError error; + MonoMethodSignature *sig; + guint32 flags; + int i, gen_param_count = 0, param_count, call_conv; + guint8 *p = buf; + gboolean hasthis, explicit_this, has_gen_params; + + flags = *p; + p ++; + has_gen_params = (flags & 0x10) != 0; + hasthis = (flags & 0x20) != 0; + explicit_this = (flags & 0x40) != 0; + call_conv = flags & 0x0F; + + if (has_gen_params) + gen_param_count = decode_value (p, &p); + param_count = decode_value (p, &p); + if (target && param_count != target->param_count) + return NULL; + sig = (MonoMethodSignature *)g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + param_count * sizeof (MonoType *)); + sig->param_count = param_count; + sig->sentinelpos = -1; + sig->hasthis = hasthis; + sig->explicit_this = explicit_this; + sig->call_convention = call_conv; + sig->generic_param_count = gen_param_count; + sig->ret = decode_type (module, p, &p, &error); + if (!sig->ret) + goto fail; + for (i = 0; i < param_count; ++i) { + if (*p == MONO_TYPE_SENTINEL) { + g_assert (sig->call_convention == MONO_CALL_VARARG); + sig->sentinelpos = i; + p ++; + } + sig->params [i] = decode_type (module, p, &p, &error); + if (!sig->params [i]) + goto fail; + } + + if (sig->call_convention == MONO_CALL_VARARG && sig->sentinelpos == -1) + sig->sentinelpos = sig->param_count; + + *endbuf = p; + + return sig; +fail: + mono_error_cleanup (&error); /* FIXME don't swallow the error */ + g_free (sig); + return NULL; +} + +static MonoMethodSignature* +decode_signature (MonoAotModule *module, guint8 *buf, guint8 **endbuf) +{ + return decode_signature_with_target (module, NULL, buf, endbuf); +} + +static gboolean +sig_matches_target (MonoAotModule *module, MonoMethod *target, guint8 *buf, guint8 **endbuf) +{ + MonoMethodSignature *sig; + gboolean res; + guint8 *p = buf; + + sig = decode_signature_with_target (module, mono_method_signature (target), p, &p); + res = sig && mono_metadata_signature_equal (mono_method_signature (target), sig); + g_free (sig); + *endbuf = p; + return res; +} + +/* Stores information returned by decode_method_ref () */ +typedef struct { + MonoImage *image; + guint32 token; + MonoMethod *method; + gboolean no_aot_trampoline; +} MethodRef; + +/* + * decode_method_ref_with_target: + * + * Decode a method reference, storing the image/token into a MethodRef structure. + * This avoids loading metadata for the method if the caller does not need it. If the method has + * no token, then it is loaded from metadata and ref->method is set to the method instance. + * If TARGET is non-NULL, abort decoding if it can be determined that the decoded method + * couldn't resolve to TARGET, and return FALSE. + * There are some kinds of method references which only support a non-null TARGET. + * This means that its not possible to decode this into a method, only to check + * that the method reference matches a given method. This is normally not a problem + * as these wrappers only occur in the extra_methods table, where we already have + * a method we want to lookup. + * + * If there was a decoding error, we return FALSE and set @error + */ +static gboolean +decode_method_ref_with_target (MonoAotModule *module, MethodRef *ref, MonoMethod *target, guint8 *buf, guint8 **endbuf, MonoError *error) +{ + guint32 image_index, value; + MonoImage *image = NULL; + guint8 *p = buf; + + memset (ref, 0, sizeof (MethodRef)); + error_init (error); + + value = decode_value (p, &p); + image_index = value >> 24; + + if (image_index == MONO_AOT_METHODREF_NO_AOT_TRAMPOLINE) { + ref->no_aot_trampoline = TRUE; + value = decode_value (p, &p); + image_index = value >> 24; + } + + if (image_index < MONO_AOT_METHODREF_MIN || image_index == MONO_AOT_METHODREF_METHODSPEC || image_index == MONO_AOT_METHODREF_GINST) { + if (target && target->wrapper_type) { + return FALSE; + } + } + + if (image_index == MONO_AOT_METHODREF_WRAPPER) { + WrapperInfo *info; + guint32 wrapper_type; + + wrapper_type = decode_value (p, &p); + + if (target && target->wrapper_type != wrapper_type) + return FALSE; + + /* Doesn't matter */ + image = mono_defaults.corlib; + + switch (wrapper_type) { +#ifndef DISABLE_REMOTING + case MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK: { + MonoMethod *m = decode_resolve_method_ref (module, p, &p, error); + if (!m) + return FALSE; + mono_class_init (m->klass); + if (mono_aot_only) + ref->method = m; + else + ref->method = mono_marshal_get_remoting_invoke_with_check (m); + break; + } + case MONO_WRAPPER_PROXY_ISINST: { + MonoClass *klass = decode_klass_ref (module, p, &p, error); + if (!klass) + return FALSE; + ref->method = mono_marshal_get_proxy_cancast (klass); + break; + } + case MONO_WRAPPER_LDFLD: + case MONO_WRAPPER_LDFLDA: + case MONO_WRAPPER_STFLD: { + MonoClass *klass = decode_klass_ref (module, p, &p, error); + if (!klass) + return FALSE; + if (wrapper_type == MONO_WRAPPER_LDFLD) + ref->method = mono_marshal_get_ldfld_wrapper (&klass->byval_arg); + else if (wrapper_type == MONO_WRAPPER_LDFLDA) + ref->method = mono_marshal_get_ldflda_wrapper (&klass->byval_arg); + else if (wrapper_type == MONO_WRAPPER_STFLD) + ref->method = mono_marshal_get_stfld_wrapper (&klass->byval_arg); + else { + mono_error_set_bad_image_name (error, module->aot_name, "Unknown AOT wrapper type %d", wrapper_type); + return FALSE; + } + break; + } +#endif + case MONO_WRAPPER_ALLOC: { + int atype = decode_value (p, &p); + ManagedAllocatorVariant variant = + mono_profiler_allocations_enabled () ? + MANAGED_ALLOCATOR_PROFILER : MANAGED_ALLOCATOR_REGULAR; + + ref->method = mono_gc_get_managed_allocator_by_type (atype, variant); + /* Try to fallback to the slow path version */ + if (!ref->method) + ref->method = mono_gc_get_managed_allocator_by_type (atype, MANAGED_ALLOCATOR_SLOW_PATH); + if (!ref->method) { + mono_error_set_bad_image_name (error, module->aot_name, "Error: No managed allocator, but we need one for AOT.\nAre you using non-standard GC options?\n"); + return FALSE; + } + break; + } + case MONO_WRAPPER_WRITE_BARRIER: { + ref->method = mono_gc_get_write_barrier (); + break; + } + case MONO_WRAPPER_STELEMREF: { + int subtype = decode_value (p, &p); + + if (subtype == WRAPPER_SUBTYPE_NONE) { + ref->method = mono_marshal_get_stelemref (); + } else if (subtype == WRAPPER_SUBTYPE_VIRTUAL_STELEMREF) { + int kind; + + kind = decode_value (p, &p); + + /* Can't decode this */ + if (!target) + return FALSE; + if (target->wrapper_type == MONO_WRAPPER_STELEMREF) { + info = mono_marshal_get_wrapper_info (target); + + g_assert (info); + if (info->subtype == subtype && info->d.virtual_stelemref.kind == kind) + ref->method = target; + else + return FALSE; + } else { + return FALSE; + } + } else { + mono_error_set_bad_image_name (error, module->aot_name, "Invalid STELEMREF subtype %d", subtype); + return FALSE; + } + break; + } + case MONO_WRAPPER_SYNCHRONIZED: { + MonoMethod *m = decode_resolve_method_ref (module, p, &p, error); + if (!m) + return FALSE; + ref->method = mono_marshal_get_synchronized_wrapper (m); + break; + } + case MONO_WRAPPER_UNKNOWN: { + int subtype = decode_value (p, &p); + + if (subtype == WRAPPER_SUBTYPE_PTR_TO_STRUCTURE || subtype == WRAPPER_SUBTYPE_STRUCTURE_TO_PTR) { + MonoClass *klass = decode_klass_ref (module, p, &p, error); + if (!klass) + return FALSE; + + if (!target) + return FALSE; + if (klass != target->klass) + return FALSE; + + if (subtype == WRAPPER_SUBTYPE_PTR_TO_STRUCTURE) { + if (strcmp (target->name, "PtrToStructure")) + return FALSE; + ref->method = mono_marshal_get_ptr_to_struct (klass); + } else { + if (strcmp (target->name, "StructureToPtr")) + return FALSE; + ref->method = mono_marshal_get_struct_to_ptr (klass); + } + } else if (subtype == WRAPPER_SUBTYPE_SYNCHRONIZED_INNER) { + MonoMethod *m = decode_resolve_method_ref (module, p, &p, error); + if (!m) + return FALSE; + ref->method = mono_marshal_get_synchronized_inner_wrapper (m); + } else if (subtype == WRAPPER_SUBTYPE_ARRAY_ACCESSOR) { + MonoMethod *m = decode_resolve_method_ref (module, p, &p, error); + if (!m) + return FALSE; + ref->method = mono_marshal_get_array_accessor_wrapper (m); + } else if (subtype == WRAPPER_SUBTYPE_GSHAREDVT_IN) { + ref->method = mono_marshal_get_gsharedvt_in_wrapper (); + } else if (subtype == WRAPPER_SUBTYPE_GSHAREDVT_OUT) { + ref->method = mono_marshal_get_gsharedvt_out_wrapper (); + } else if (subtype == WRAPPER_SUBTYPE_INTERP_IN) { + MonoMethodSignature *sig = decode_signature (module, p, &p); + if (!sig) + return FALSE; + ref->method = mini_get_interp_in_wrapper (sig); + } else if (subtype == WRAPPER_SUBTYPE_GSHAREDVT_IN_SIG) { + MonoMethodSignature *sig = decode_signature (module, p, &p); + if (!sig) + return FALSE; + ref->method = mini_get_gsharedvt_in_sig_wrapper (sig); + } else if (subtype == WRAPPER_SUBTYPE_GSHAREDVT_OUT_SIG) { + MonoMethodSignature *sig = decode_signature (module, p, &p); + if (!sig) + return FALSE; + ref->method = mini_get_gsharedvt_out_sig_wrapper (sig); + } else { + mono_error_set_bad_image_name (error, module->aot_name, "Invalid UNKNOWN wrapper subtype %d", subtype); + return FALSE; + } + break; + } + case MONO_WRAPPER_MANAGED_TO_MANAGED: { + int subtype = decode_value (p, &p); + + if (subtype == WRAPPER_SUBTYPE_ELEMENT_ADDR) { + int rank = decode_value (p, &p); + int elem_size = decode_value (p, &p); + + ref->method = mono_marshal_get_array_address (rank, elem_size); + } else if (subtype == WRAPPER_SUBTYPE_STRING_CTOR) { + MonoMethod *m; + + m = decode_resolve_method_ref (module, p, &p, error); + if (!m) + return FALSE; + + if (!target) + return FALSE; + g_assert (target->wrapper_type == MONO_WRAPPER_MANAGED_TO_MANAGED); + + info = mono_marshal_get_wrapper_info (target); + if (info && info->subtype == subtype && info->d.string_ctor.method == m) + ref->method = target; + else + return FALSE; + } + break; + } + case MONO_WRAPPER_MANAGED_TO_NATIVE: { + MonoMethod *m; + int subtype = decode_value (p, &p); + char *name; + + if (subtype == WRAPPER_SUBTYPE_ICALL_WRAPPER) { + if (!target) + return FALSE; + + name = (char*)p; + if (strcmp (target->name, name) != 0) + return FALSE; + ref->method = target; + } else { + m = decode_resolve_method_ref (module, p, &p, error); + if (!m) + return FALSE; + + /* This should only happen when looking for an extra method */ + if (!target) + return FALSE; + if (mono_marshal_method_from_wrapper (target) == m) + ref->method = target; + else + return FALSE; + } + break; + } + case MONO_WRAPPER_CASTCLASS: { + int subtype = decode_value (p, &p); + + if (subtype == WRAPPER_SUBTYPE_CASTCLASS_WITH_CACHE) + ref->method = mono_marshal_get_castclass_with_cache (); + else if (subtype == WRAPPER_SUBTYPE_ISINST_WITH_CACHE) + ref->method = mono_marshal_get_isinst_with_cache (); + else { + mono_error_set_bad_image_name (error, module->aot_name, "Invalid CASTCLASS wrapper subtype %d", subtype); + return FALSE; + } + break; + } + case MONO_WRAPPER_RUNTIME_INVOKE: { + int subtype = decode_value (p, &p); + + if (!target) + return FALSE; + + if (subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_DYNAMIC) { + if (strcmp (target->name, "runtime_invoke_dynamic") != 0) + return FALSE; + ref->method = target; + } else if (subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_DIRECT) { + /* Direct wrapper */ + MonoMethod *m = decode_resolve_method_ref (module, p, &p, error); + if (!m) + return FALSE; + ref->method = mono_marshal_get_runtime_invoke (m, FALSE); + } else if (subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_VIRTUAL) { + /* Virtual direct wrapper */ + MonoMethod *m = decode_resolve_method_ref (module, p, &p, error); + if (!m) + return FALSE; + ref->method = mono_marshal_get_runtime_invoke (m, TRUE); + } else { + MonoMethodSignature *sig; + + sig = decode_signature_with_target (module, NULL, p, &p); + info = mono_marshal_get_wrapper_info (target); + g_assert (info); + + if (info->subtype != subtype) + return FALSE; + g_assert (info->d.runtime_invoke.sig); + if (mono_metadata_signature_equal (sig, info->d.runtime_invoke.sig)) + ref->method = target; + else + return FALSE; + } + break; + } + case MONO_WRAPPER_DELEGATE_INVOKE: + case MONO_WRAPPER_DELEGATE_BEGIN_INVOKE: + case MONO_WRAPPER_DELEGATE_END_INVOKE: { + gboolean is_inflated = decode_value (p, &p); + WrapperSubtype subtype; + + if (is_inflated) { + MonoClass *klass; + MonoMethod *invoke, *wrapper; + + klass = decode_klass_ref (module, p, &p, error); + if (!klass) + return FALSE; + + switch (wrapper_type) { + case MONO_WRAPPER_DELEGATE_INVOKE: + invoke = mono_get_delegate_invoke (klass); + wrapper = mono_marshal_get_delegate_invoke (invoke, NULL); + break; + case MONO_WRAPPER_DELEGATE_BEGIN_INVOKE: + invoke = mono_get_delegate_begin_invoke (klass); + wrapper = mono_marshal_get_delegate_begin_invoke (invoke); + break; + case MONO_WRAPPER_DELEGATE_END_INVOKE: + invoke = mono_get_delegate_end_invoke (klass); + wrapper = mono_marshal_get_delegate_end_invoke (invoke); + break; + default: + g_assert_not_reached (); + break; + } + if (target) { + /* + * Due to the way mini_get_shared_method () works, we could end up with + * multiple copies of the same wrapper. + */ + if (wrapper->klass != target->klass) + return FALSE; + ref->method = target; + } else { + ref->method = wrapper; + } + } else { + /* + * These wrappers are associated with a signature, not with a method. + * Since we can't decode them into methods, they need a target method. + */ + if (!target) + return FALSE; + + if (wrapper_type == MONO_WRAPPER_DELEGATE_INVOKE) { + subtype = (WrapperSubtype)decode_value (p, &p); + info = mono_marshal_get_wrapper_info (target); + if (info) { + if (info->subtype != subtype) + return FALSE; + } else { + if (subtype != WRAPPER_SUBTYPE_NONE) + return FALSE; + } + } + if (sig_matches_target (module, target, p, &p)) + ref->method = target; + else + return FALSE; + } + break; + } + case MONO_WRAPPER_NATIVE_TO_MANAGED: { + MonoMethod *m; + MonoClass *klass; + + m = decode_resolve_method_ref (module, p, &p, error); + if (!m) + return FALSE; + klass = decode_klass_ref (module, p, &p, error); + if (!klass) + return FALSE; + ref->method = mono_marshal_get_managed_wrapper (m, klass, 0, error); + if (!mono_error_ok (error)) + return FALSE; + break; + } + default: + g_assert_not_reached (); + } + } else if (image_index == MONO_AOT_METHODREF_METHODSPEC) { + image_index = decode_value (p, &p); + ref->token = decode_value (p, &p); + + image = load_image (module, image_index, error); + if (!image) + return FALSE; + } else if (image_index == MONO_AOT_METHODREF_GINST) { + MonoClass *klass; + MonoGenericContext ctx; + + /* + * These methods do not have a token which resolves them, so we + * resolve them immediately. + */ + klass = decode_klass_ref (module, p, &p, error); + if (!klass) + return FALSE; + + if (target && target->klass != klass) + return FALSE; + + image_index = decode_value (p, &p); + ref->token = decode_value (p, &p); + + image = load_image (module, image_index, error); + if (!image) + return FALSE; + + ref->method = mono_get_method_checked (image, ref->token, NULL, NULL, error); + if (!ref->method) + return FALSE; + + + memset (&ctx, 0, sizeof (ctx)); + + if (FALSE && mono_class_is_ginst (klass)) { + ctx.class_inst = mono_class_get_generic_class (klass)->context.class_inst; + ctx.method_inst = NULL; + + ref->method = mono_class_inflate_generic_method_full_checked (ref->method, klass, &ctx, error); + if (!ref->method) + return FALSE; + } + + memset (&ctx, 0, sizeof (ctx)); + + if (!decode_generic_context (module, &ctx, p, &p, error)) + return FALSE; + + ref->method = mono_class_inflate_generic_method_full_checked (ref->method, klass, &ctx, error); + if (!ref->method) + return FALSE; + + } else if (image_index == MONO_AOT_METHODREF_ARRAY) { + MonoClass *klass; + int method_type; + + klass = decode_klass_ref (module, p, &p, error); + if (!klass) + return FALSE; + method_type = decode_value (p, &p); + switch (method_type) { + case 0: + ref->method = mono_class_get_method_from_name (klass, ".ctor", klass->rank); + break; + case 1: + ref->method = mono_class_get_method_from_name (klass, ".ctor", klass->rank * 2); + break; + case 2: + ref->method = mono_class_get_method_from_name (klass, "Get", -1); + break; + case 3: + ref->method = mono_class_get_method_from_name (klass, "Address", -1); + break; + case 4: + ref->method = mono_class_get_method_from_name (klass, "Set", -1); + break; + default: + mono_error_set_bad_image_name (error, module->aot_name, "Invalid METHODREF_ARRAY method type %d", method_type); + return FALSE; + } + } else { + if (image_index == MONO_AOT_METHODREF_LARGE_IMAGE_INDEX) { + image_index = decode_value (p, &p); + value = decode_value (p, &p); + } + + ref->token = MONO_TOKEN_METHOD_DEF | (value & 0xffffff); + + image = load_image (module, image_index, error); + if (!image) + return FALSE; + } + + *endbuf = p; + + ref->image = image; + + return TRUE; +} + +static gboolean +decode_method_ref (MonoAotModule *module, MethodRef *ref, guint8 *buf, guint8 **endbuf, MonoError *error) +{ + return decode_method_ref_with_target (module, ref, NULL, buf, endbuf, error); +} + +/* + * decode_resolve_method_ref_with_target: + * + * Similar to decode_method_ref, but resolve and return the method itself. + */ +static MonoMethod* +decode_resolve_method_ref_with_target (MonoAotModule *module, MonoMethod *target, guint8 *buf, guint8 **endbuf, MonoError *error) +{ + MethodRef ref; + + error_init (error); + + if (!decode_method_ref_with_target (module, &ref, target, buf, endbuf, error)) + return NULL; + if (ref.method) + return ref.method; + if (!ref.image) { + mono_error_set_bad_image_name (error, module->aot_name, "No image found for methodref with target"); + return NULL; + } + return mono_get_method_checked (ref.image, ref.token, NULL, NULL, error); +} + +static MonoMethod* +decode_resolve_method_ref (MonoAotModule *module, guint8 *buf, guint8 **endbuf, MonoError *error) +{ + return decode_resolve_method_ref_with_target (module, NULL, buf, endbuf, error); +} + +#ifdef ENABLE_AOT_CACHE + +/* AOT CACHE */ + +/* + * FIXME: + * - Add options for controlling the cache size + * - Handle full cache by deleting old assemblies lru style + * - Maybe add a threshold after an assembly is AOT compiled + * - Add options for enabling this for specific main assemblies + */ + +/* The cache directory */ +static char *cache_dir; + +/* The number of assemblies AOTed in this run */ +static int cache_count; + +/* Whenever to AOT in-process */ +static gboolean in_process; + +static void +collect_assemblies (gpointer data, gpointer user_data) +{ + MonoAssembly *ass = data; + GSList **l = user_data; + + *l = g_slist_prepend (*l, ass); +} + +#define SHA1_DIGEST_LENGTH 20 + +/* + * get_aot_config_hash: + * + * Return a hash for all the version information an AOT module depends on. + */ +static G_GNUC_UNUSED char* +get_aot_config_hash (MonoAssembly *assembly) +{ + char *build_info; + GSList *l, *assembly_list = NULL; + GString *s; + int i; + guint8 digest [SHA1_DIGEST_LENGTH]; + char *digest_str; + + build_info = mono_get_runtime_build_info (); + + s = g_string_new (build_info); + + mono_assembly_foreach (collect_assemblies, &assembly_list); + + /* + * The assembly list includes the current assembly as well, no need + * to add it. + */ + for (l = assembly_list; l; l = l->next) { + MonoAssembly *ass = l->data; + + g_string_append (s, "_"); + g_string_append (s, ass->aname.name); + g_string_append (s, "_"); + g_string_append (s, ass->image->guid); + } + + for (i = 0; i < s->len; ++i) { + if (!isalnum (s->str [i]) && s->str [i] != '-') + s->str [i] = '_'; + } + + mono_sha1_get_digest ((guint8*)s->str, s->len, digest); + + digest_str = g_malloc0 ((SHA1_DIGEST_LENGTH * 2) + 1); + for (i = 0; i < SHA1_DIGEST_LENGTH; ++i) + sprintf (digest_str + (i * 2), "%02x", digest [i]); + + mono_trace (G_LOG_LEVEL_MESSAGE, MONO_TRACE_AOT, "AOT: file dependencies: %s, hash %s", s->str, digest_str); + + g_string_free (s, TRUE); + + return digest_str; +} + +static void +aot_cache_init (void) +{ + if (mono_aot_only) + return; + enable_aot_cache = TRUE; + in_process = TRUE; +} + +/* + * aot_cache_load_module: + * + * Load the AOT image corresponding to ASSEMBLY from the aot cache, AOTing it if neccessary. + */ +static MonoDl* +aot_cache_load_module (MonoAssembly *assembly, char **aot_name) +{ + MonoAotCacheConfig *config; + GSList *l; + char *fname, *tmp2, *aot_options, *failure_fname; + const char *home; + MonoDl *module; + gboolean res; + gint exit_status; + char *hash; + int pid; + gboolean enabled; + FILE *failure_file; + + *aot_name = NULL; + + if (image_is_dynamic (assembly->image)) + return NULL; + + /* Check in the list of assemblies enabled for aot caching */ + config = mono_get_aot_cache_config (); + + enabled = FALSE; + if (config->apps) { + MonoDomain *domain = mono_domain_get (); + MonoAssembly *entry_assembly = domain->entry_assembly; + + // FIXME: This cannot be used for mscorlib during startup, since entry_assembly is not set yet + for (l = config->apps; l; l = l->next) { + char *n = l->data; + + if ((entry_assembly && !strcmp (entry_assembly->aname.name, n)) || (!entry_assembly && !strcmp (assembly->aname.name, n))) + break; + } + if (l) + enabled = TRUE; + } + + if (!enabled) { + for (l = config->assemblies; l; l = l->next) { + char *n = l->data; + + if (!strcmp (assembly->aname.name, n)) + break; + } + if (l) + enabled = TRUE; + } + if (!enabled) + return NULL; + + if (!cache_dir) { + home = g_get_home_dir (); + if (!home) + return NULL; + cache_dir = g_strdup_printf ("%s/Library/Caches/mono/aot-cache", home); + if (!g_file_test (cache_dir, G_FILE_TEST_EXISTS|G_FILE_TEST_IS_DIR)) + g_mkdir_with_parents (cache_dir, 0777); + } + + /* + * The same assembly can be used in multiple configurations, i.e. multiple + * versions of the runtime, with multiple versions of dependent assemblies etc. + * To handle this, we compute a version string containing all this information, hash it, + * and use the hash as a filename suffix. + */ + hash = get_aot_config_hash (assembly); + + tmp2 = g_strdup_printf ("%s-%s%s", assembly->image->assembly_name, hash, MONO_SOLIB_EXT); + fname = g_build_filename (cache_dir, tmp2, NULL); + *aot_name = fname; + g_free (tmp2); + + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT: loading from cache: '%s'.", fname); + module = mono_dl_open (fname, MONO_DL_LAZY, NULL); + + if (module) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT: found in cache: '%s'.", fname); + return module; + } + + if (!strcmp (assembly->aname.name, "mscorlib") && !mscorlib_aot_loaded) + /* + * Can't AOT this during startup, so we AOT it when called later from + * mono_aot_get_method (). + */ + return NULL; + + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT: not found."); + + /* Only AOT one assembly per run to avoid slowing down execution too much */ + if (cache_count > 0) + return NULL; + cache_count ++; + + /* Check for previous failure */ + failure_fname = g_strdup_printf ("%s.failure", fname); + failure_file = fopen (failure_fname, "r"); + if (failure_file) { + mono_trace (G_LOG_LEVEL_MESSAGE, MONO_TRACE_AOT, "AOT: assembly '%s' previously failed to compile '%s' ('%s')... ", assembly->image->name, fname, failure_fname); + g_free (failure_fname); + return NULL; + } else { + g_free (failure_fname); + fclose (failure_file); + } + + mono_trace (G_LOG_LEVEL_MESSAGE, MONO_TRACE_AOT, "AOT: compiling assembly '%s', logfile: '%s.log'... ", assembly->image->name, fname); + + /* + * We need to invoke the AOT compiler here. There are multiple approaches: + * - spawn a new runtime process. This can be hard when running with mkbundle, and + * its hard to make the new process load the same set of assemblies. + * - doing it in-process. This exposes the current process to bugs/leaks/side effects of + * the AOT compiler. + * - fork a new process and do the work there. + */ + if (in_process) { + aot_options = g_strdup_printf ("outfile=%s,internal-logfile=%s.log%s%s", fname, fname, config->aot_options ? "," : "", config->aot_options ? config->aot_options : ""); + /* Maybe due this in another thread ? */ + res = mono_compile_assembly (assembly, mono_parse_default_optimizations (NULL), aot_options, NULL); + if (res) { + mono_trace (G_LOG_LEVEL_MESSAGE, MONO_TRACE_AOT, "AOT: compilation failed."); + failure_fname = g_strdup_printf ("%s.failure", fname); + failure_file = fopen (failure_fname, "a+"); + fclose (failure_file); + g_free (failure_fname); + } else { + mono_trace (G_LOG_LEVEL_MESSAGE, MONO_TRACE_AOT, "AOT: compilation succeeded."); + } + } else { + /* + * - Avoid waiting for the aot process to finish ? + * (less overhead, but multiple processes could aot the same assembly at the same time) + */ + pid = fork (); + if (pid == 0) { + FILE *logfile; + char *logfile_name; + + /* Child */ + + logfile_name = g_strdup_printf ("%s/aot.log", cache_dir); + logfile = fopen (logfile_name, "a+"); + g_free (logfile_name); + + dup2 (fileno (logfile), 1); + dup2 (fileno (logfile), 2); + + aot_options = g_strdup_printf ("outfile=%s", fname); + res = mono_compile_assembly (assembly, mono_parse_default_optimizations (NULL), aot_options, NULL); + if (!res) { + exit (1); + } else { + exit (0); + } + } else { + /* Parent */ + waitpid (pid, &exit_status, 0); + if (!WIFEXITED (exit_status) && (WEXITSTATUS (exit_status) == 0)) + mono_trace (G_LOG_LEVEL_MESSAGE, MONO_TRACE_AOT, "AOT: failed."); + else + mono_trace (G_LOG_LEVEL_MESSAGE, MONO_TRACE_AOT, "AOT: succeeded."); + } + } + + module = mono_dl_open (fname, MONO_DL_LAZY, NULL); + + return module; +} + +#else + +static void +aot_cache_init (void) +{ +} + +static MonoDl* +aot_cache_load_module (MonoAssembly *assembly, char **aot_name) +{ + return NULL; +} + +#endif + +static void +find_symbol (MonoDl *module, gpointer *globals, const char *name, gpointer *value) +{ + if (globals) { + int global_index; + guint16 *table, *entry; + guint16 table_size; + guint32 hash; + char *symbol = (char*)name; + +#ifdef TARGET_MACH + symbol = g_strdup_printf ("_%s", name); +#endif + + /* The first entry points to the hash */ + table = (guint16 *)globals [0]; + globals ++; + + table_size = table [0]; + table ++; + + hash = mono_metadata_str_hash (symbol) % table_size; + + entry = &table [hash * 2]; + + /* Search the hash for the index into the globals table */ + global_index = -1; + while (entry [0] != 0) { + guint32 index = entry [0] - 1; + guint32 next = entry [1]; + + //printf ("X: %s %s\n", (char*)globals [index * 2], name); + + if (!strcmp (globals [index * 2], symbol)) { + global_index = index; + break; + } + + if (next != 0) { + entry = &table [next * 2]; + } else { + break; + } + } + + if (global_index != -1) + *value = globals [global_index * 2 + 1]; + else + *value = NULL; + + if (symbol != name) + g_free (symbol); + } else { + char *err = mono_dl_symbol (module, name, value); + + if (err) + g_free (err); + } +} + +static void +find_amodule_symbol (MonoAotModule *amodule, const char *name, gpointer *value) +{ + g_assert (!(amodule->info.flags & MONO_AOT_FILE_FLAG_LLVM_ONLY)); + + find_symbol (amodule->sofile, amodule->globals, name, value); +} + +void +mono_install_load_aot_data_hook (MonoLoadAotDataFunc load_func, MonoFreeAotDataFunc free_func, gpointer user_data) +{ + aot_data_load_func = load_func; + aot_data_free_func = free_func; + aot_data_func_user_data = user_data; +} + +/* Load the separate aot data file for ASSEMBLY */ +static guint8* +open_aot_data (MonoAssembly *assembly, MonoAotFileInfo *info, void **ret_handle) +{ + MonoFileMap *map; + char *filename; + guint8 *data; + + if (aot_data_load_func) { + data = aot_data_load_func (assembly, info->datafile_size, aot_data_func_user_data, ret_handle); + g_assert (data); + return data; + } + + /* + * Use .aotdata as the default implementation if no callback is given + */ + filename = g_strdup_printf ("%s.aotdata", assembly->image->name); + map = mono_file_map_open (filename); + g_assert (map); + data = mono_file_map (info->datafile_size, MONO_MMAP_READ, mono_file_map_fd (map), 0, ret_handle); + g_assert (data); + + return data; +} + +static gboolean +check_usable (MonoAssembly *assembly, MonoAotFileInfo *info, guint8 *blob, char **out_msg) +{ + char *build_info; + char *msg = NULL; + gboolean usable = TRUE; + gboolean full_aot, safepoints; + guint32 excluded_cpu_optimizations; + + if (strcmp (assembly->image->guid, info->assembly_guid)) { + msg = g_strdup_printf ("doesn't match assembly"); + usable = FALSE; + } + + build_info = mono_get_runtime_build_info (); + if (strlen ((const char *)info->runtime_version) > 0 && strcmp (info->runtime_version, build_info)) { + msg = g_strdup_printf ("compiled against runtime version '%s' while this runtime has version '%s'", info->runtime_version, build_info); + usable = FALSE; + } + g_free (build_info); + + full_aot = info->flags & MONO_AOT_FILE_FLAG_FULL_AOT; + + if (mono_aot_only && !full_aot) { + msg = g_strdup_printf ("not compiled with --aot=full"); + usable = FALSE; + } + if (!mono_aot_only && full_aot) { + msg = g_strdup_printf ("compiled with --aot=full"); + usable = FALSE; + } + if (mono_llvm_only && !(info->flags & MONO_AOT_FILE_FLAG_LLVM_ONLY)) { + msg = g_strdup_printf ("not compiled with --aot=llvmonly"); + usable = FALSE; + } + if (mini_get_debug_options ()->mdb_optimizations && !(info->flags & MONO_AOT_FILE_FLAG_DEBUG) && !full_aot) { + msg = g_strdup_printf ("not compiled for debugging"); + usable = FALSE; + } + + mono_arch_cpu_optimizations (&excluded_cpu_optimizations); + if (info->opts & excluded_cpu_optimizations) { + msg = g_strdup_printf ("compiled with unsupported CPU optimizations"); + usable = FALSE; + } + + if (!mono_aot_only && (info->simd_opts & ~mono_arch_cpu_enumerate_simd_versions ())) { + msg = g_strdup_printf ("compiled with unsupported SIMD extensions"); + usable = FALSE; + } + + if (info->gc_name_index != -1) { + char *gc_name = (char*)&blob [info->gc_name_index]; + const char *current_gc_name = mono_gc_get_gc_name (); + + if (strcmp (current_gc_name, gc_name) != 0) { + msg = g_strdup_printf ("compiled against GC %s, while the current runtime uses GC %s.\n", gc_name, current_gc_name); + usable = FALSE; + } + } + + safepoints = info->flags & MONO_AOT_FILE_FLAG_SAFEPOINTS; + + if (!safepoints && mono_threads_is_coop_enabled ()) { + msg = g_strdup_printf ("not compiled with safepoints"); + usable = FALSE; + } + + *out_msg = msg; + return usable; +} + +/* + * TABLE should point to a table of call instructions. Return the address called by the INDEXth entry. + */ +static void* +get_call_table_entry (void *table, int index) +{ +#if defined(TARGET_ARM) + guint32 *ins_addr; + guint32 ins; + gint32 offset; + + ins_addr = (guint32*)table + index; + ins = *ins_addr; + if ((ins >> ARMCOND_SHIFT) == ARMCOND_NV) { + /* blx */ + offset = (((int)(((ins & 0xffffff) << 1) | ((ins >> 24) & 0x1))) << 7) >> 7; + return (char*)ins_addr + (offset * 2) + 8 + 1; + } else { + offset = (((int)ins & 0xffffff) << 8) >> 8; + return (char*)ins_addr + (offset * 4) + 8; + } +#elif defined(TARGET_ARM64) + return mono_arch_get_call_target ((guint8*)table + (index * 4) + 4); +#elif defined(TARGET_X86) || defined(TARGET_AMD64) + /* The callee expects an ip which points after the call */ + return mono_arch_get_call_target ((guint8*)table + (index * 5) + 5); +#else + g_assert_not_reached (); + return NULL; +#endif +} + +/* + * init_amodule_got: + * + * Initialize the shared got entries for AMODULE. + */ +static void +init_amodule_got (MonoAotModule *amodule) +{ + MonoJumpInfo *ji; + MonoMemPool *mp; + MonoJumpInfo *patches; + guint32 got_offsets [128]; + MonoError error; + int i, npatches; + + /* These can't be initialized in load_aot_module () */ + if (amodule->shared_got [0] || amodule->got_initializing) + return; + + amodule->got_initializing = TRUE; + + mp = mono_mempool_new (); + npatches = amodule->info.nshared_got_entries; + for (i = 0; i < npatches; ++i) + got_offsets [i] = i; + patches = decode_patches (amodule, mp, npatches, FALSE, got_offsets); + g_assert (patches); + for (i = 0; i < npatches; ++i) { + ji = &patches [i]; + + if (ji->type == MONO_PATCH_INFO_GC_CARD_TABLE_ADDR && !mono_gc_is_moving ()) { + amodule->shared_got [i] = NULL; + } else if (ji->type == MONO_PATCH_INFO_GC_NURSERY_START && !mono_gc_is_moving ()) { + amodule->shared_got [i] = NULL; + } else if (ji->type == MONO_PATCH_INFO_GC_NURSERY_BITS && !mono_gc_is_moving ()) { + amodule->shared_got [i] = NULL; + } else if (ji->type == MONO_PATCH_INFO_IMAGE) { + amodule->shared_got [i] = amodule->assembly->image; + } else if (ji->type == MONO_PATCH_INFO_MSCORLIB_GOT_ADDR) { + if (mono_defaults.corlib) { + MonoAotModule *mscorlib_amodule = (MonoAotModule *)mono_defaults.corlib->aot_module; + + if (mscorlib_amodule) + amodule->shared_got [i] = mscorlib_amodule->got; + } else { + amodule->shared_got [i] = amodule->got; + } + } else if (ji->type == MONO_PATCH_INFO_AOT_MODULE) { + amodule->shared_got [i] = amodule; + } else { + amodule->shared_got [i] = mono_resolve_patch_target (NULL, mono_get_root_domain (), NULL, ji, FALSE, &error); + mono_error_assert_ok (&error); + } + } + + if (amodule->got) { + for (i = 0; i < npatches; ++i) + amodule->got [i] = amodule->shared_got [i]; + } + if (amodule->llvm_got) { + for (i = 0; i < npatches; ++i) + amodule->llvm_got [i] = amodule->shared_got [i]; + } + + mono_mempool_destroy (mp); +} + +static void +load_aot_module (MonoAssembly *assembly, gpointer user_data) +{ + char *aot_name, *found_aot_name; + MonoAotModule *amodule; + MonoDl *sofile; + gboolean usable = TRUE; + char *version_symbol = NULL; + char *msg = NULL; + gpointer *globals = NULL; + MonoAotFileInfo *info = NULL; + int i, version; + gboolean do_load_image = TRUE; + int align_double, align_int64; + guint8 *aot_data = NULL; + + if (mono_compile_aot) + return; + + if (assembly->image->aot_module) + /* + * Already loaded. This can happen because the assembly loading code might invoke + * the assembly load hooks multiple times for the same assembly. + */ + return; + + if (image_is_dynamic (assembly->image) || assembly->ref_only || mono_domain_get () != mono_get_root_domain ()) + return; + + mono_aot_lock (); + +if (container_assm_name && !container_amodule) { + char *local_ref = container_assm_name; + container_assm_name = NULL; + MonoImageOpenStatus status = MONO_IMAGE_OK; + gchar *dll = g_strdup_printf ( "%s.dll", local_ref); + MonoAssembly *assm = mono_assembly_open_a_lot (dll, &status, FALSE, FALSE); + if (!assm) { + gchar *exe = g_strdup_printf ("%s.exe", local_ref); + assm = mono_assembly_open_a_lot (exe, &status, FALSE, FALSE); + } + g_assert (assm); + load_aot_module (assm, NULL); + container_amodule = assm->image->aot_module; +} + + if (static_aot_modules) + info = (MonoAotFileInfo *)g_hash_table_lookup (static_aot_modules, assembly->aname.name); + + mono_aot_unlock (); + + sofile = NULL; + + found_aot_name = NULL; + + if (info) { + /* Statically linked AOT module */ + aot_name = g_strdup_printf ("%s", assembly->aname.name); + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "Found statically linked AOT module '%s'.", aot_name); + if (!(info->flags & MONO_AOT_FILE_FLAG_LLVM_ONLY)) { + globals = (void **)info->globals; + g_assert (globals); + } + found_aot_name = g_strdup (aot_name); + } else { + char *err; + + if (enable_aot_cache) + sofile = aot_cache_load_module (assembly, &aot_name); + if (!sofile) { + aot_name = g_strdup_printf ("%s%s", assembly->image->name, MONO_SOLIB_EXT); + + sofile = mono_dl_open (aot_name, MONO_DL_LAZY, &err); + if (sofile) { + found_aot_name = g_strdup (aot_name); + } else { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT: image '%s' not found: %s", aot_name, err); + g_free (err); + } + g_free (aot_name); + } + if (!sofile) { + char *basename = g_path_get_basename (assembly->image->name); + aot_name = g_strdup_printf ("%s/mono/aot-cache/%s/%s%s", mono_assembly_getrootdir(), MONO_ARCHITECTURE, basename, MONO_SOLIB_EXT); + g_free (basename); + sofile = mono_dl_open (aot_name, MONO_DL_LAZY, &err); + if (!sofile) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT: image '%s' not found: %s", aot_name, err); + g_free (err); + } + g_free (aot_name); + } + if (!sofile) { + GList *l; + + for (l = mono_aot_paths; l; l = l->next) { + char *path = l->data; + + char *basename = g_path_get_basename (assembly->image->name); + aot_name = g_strdup_printf ("%s/%s%s", path, basename, MONO_SOLIB_EXT); + sofile = mono_dl_open (aot_name, MONO_DL_LAZY, &err); + if (sofile) { + found_aot_name = g_strdup (aot_name); + } else { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT: image '%s' not found: %s", aot_name, err); + g_free (err); + } + g_free (basename); + g_free (aot_name); + if (sofile) + break; + } + } + if (!sofile) { + if (mono_aot_only && !mono_use_interpreter && assembly->image->tables [MONO_TABLE_METHOD].rows) { + aot_name = g_strdup_printf ("%s%s", assembly->image->name, MONO_SOLIB_EXT); + g_error ("Failed to load AOT module '%s' in aot-only mode.\n", aot_name); + g_free (aot_name); + } + return; + } + } + + if (!info) { + find_symbol (sofile, globals, "mono_aot_version", (gpointer *) &version_symbol); + find_symbol (sofile, globals, "mono_aot_file_info", (gpointer*)&info); + } + + // Copy aotid to MonoImage + memcpy(&assembly->image->aotid, info->aotid, 16); + + if (version_symbol) { + /* Old file format */ + version = atoi (version_symbol); + } else { + g_assert (info); + version = info->version; + } + + if (version != MONO_AOT_FILE_VERSION) { + msg = g_strdup_printf ("wrong file format version (expected %d got %d)", MONO_AOT_FILE_VERSION, version); + usable = FALSE; + } else { + guint8 *blob; + void *handle; + + if (info->flags & MONO_AOT_FILE_FLAG_SEPARATE_DATA) { + aot_data = open_aot_data (assembly, info, &handle); + + blob = aot_data + info->table_offsets [MONO_AOT_TABLE_BLOB]; + } else { + blob = (guint8 *)info->blob; + } + + usable = check_usable (assembly, info, blob, &msg); + } + + if (!usable) { + if (mono_aot_only && !mono_use_interpreter) { + g_error ("Failed to load AOT module '%s' while running in aot-only mode: %s.\n", found_aot_name, msg); + } else { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT: module %s is unusable: %s.", found_aot_name, msg); + } + g_free (msg); + g_free (found_aot_name); + if (sofile) + mono_dl_close (sofile); + assembly->image->aot_module = NULL; + return; + } + + /* Sanity check */ + align_double = MONO_ABI_ALIGNOF (double); + align_int64 = MONO_ABI_ALIGNOF (gint64); + g_assert (info->double_align == align_double); + g_assert (info->long_align == align_int64); + g_assert (info->generic_tramp_num == MONO_TRAMPOLINE_NUM); + + amodule = g_new0 (MonoAotModule, 1); + amodule->aot_name = found_aot_name; + amodule->assembly = assembly; + + memcpy (&amodule->info, info, sizeof (*info)); + + amodule->got = (void **)amodule->info.jit_got; + amodule->llvm_got = (void **)amodule->info.llvm_got; + amodule->globals = globals; + amodule->sofile = sofile; + amodule->method_to_code = g_hash_table_new (mono_aligned_addr_hash, NULL); + amodule->extra_methods = g_hash_table_new (NULL, NULL); + amodule->shared_got = g_new0 (gpointer, info->nshared_got_entries); + + if (info->flags & MONO_AOT_FILE_FLAG_SEPARATE_DATA) { + for (i = 0; i < MONO_AOT_TABLE_NUM; ++i) + amodule->tables [i] = aot_data + info->table_offsets [i]; + } + + mono_os_mutex_init_recursive (&amodule->mutex); + + /* Read image table */ + { + guint32 table_len, i; + char *table = NULL; + + if (info->flags & MONO_AOT_FILE_FLAG_SEPARATE_DATA) + table = amodule->tables [MONO_AOT_TABLE_IMAGE_TABLE]; + else + table = (char *)info->image_table; + g_assert (table); + + table_len = *(guint32*)table; + table += sizeof (guint32); + amodule->image_table = g_new0 (MonoImage*, table_len); + amodule->image_names = g_new0 (MonoAssemblyName, table_len); + amodule->image_guids = g_new0 (char*, table_len); + amodule->image_table_len = table_len; + for (i = 0; i < table_len; ++i) { + MonoAssemblyName *aname = &(amodule->image_names [i]); + + aname->name = g_strdup (table); + table += strlen (table) + 1; + amodule->image_guids [i] = g_strdup (table); + table += strlen (table) + 1; + if (table [0] != 0) + aname->culture = g_strdup (table); + table += strlen (table) + 1; + memcpy (aname->public_key_token, table, strlen (table) + 1); + table += strlen (table) + 1; + + table = (char *)ALIGN_PTR_TO (table, 8); + aname->flags = *(guint32*)table; + table += 4; + aname->major = *(guint32*)table; + table += 4; + aname->minor = *(guint32*)table; + table += 4; + aname->build = *(guint32*)table; + table += 4; + aname->revision = *(guint32*)table; + table += 4; + } + } + + amodule->jit_code_start = (guint8 *)info->jit_code_start; + amodule->jit_code_end = (guint8 *)info->jit_code_end; + if (info->flags & MONO_AOT_FILE_FLAG_SEPARATE_DATA) { + amodule->blob = amodule->tables [MONO_AOT_TABLE_BLOB]; + amodule->method_info_offsets = amodule->tables [MONO_AOT_TABLE_METHOD_INFO_OFFSETS]; + amodule->ex_info_offsets = amodule->tables [MONO_AOT_TABLE_EX_INFO_OFFSETS]; + amodule->class_info_offsets = amodule->tables [MONO_AOT_TABLE_CLASS_INFO_OFFSETS]; + amodule->class_name_table = amodule->tables [MONO_AOT_TABLE_CLASS_NAME]; + amodule->extra_method_table = amodule->tables [MONO_AOT_TABLE_EXTRA_METHOD_TABLE]; + amodule->extra_method_info_offsets = amodule->tables [MONO_AOT_TABLE_EXTRA_METHOD_INFO_OFFSETS]; + amodule->got_info_offsets = amodule->tables [MONO_AOT_TABLE_GOT_INFO_OFFSETS]; + amodule->llvm_got_info_offsets = amodule->tables [MONO_AOT_TABLE_LLVM_GOT_INFO_OFFSETS]; + } else { + amodule->blob = info->blob; + amodule->method_info_offsets = (guint32 *)info->method_info_offsets; + amodule->ex_info_offsets = (guint32 *)info->ex_info_offsets; + amodule->class_info_offsets = (guint32 *)info->class_info_offsets; + amodule->class_name_table = (guint16 *)info->class_name_table; + amodule->extra_method_table = (guint32 *)info->extra_method_table; + amodule->extra_method_info_offsets = (guint32 *)info->extra_method_info_offsets; + amodule->got_info_offsets = info->got_info_offsets; + amodule->llvm_got_info_offsets = info->llvm_got_info_offsets; + } + amodule->unbox_trampolines = (guint32 *)info->unbox_trampolines; + amodule->unbox_trampolines_end = (guint32 *)info->unbox_trampolines_end; + amodule->unbox_trampoline_addresses = (guint32 *)info->unbox_trampoline_addresses; + amodule->unwind_info = (guint8 *)info->unwind_info; + amodule->mem_begin = amodule->jit_code_start; + amodule->mem_end = (guint8 *)info->mem_end; + amodule->plt = (guint8 *)info->plt; + amodule->plt_end = (guint8 *)info->plt_end; + amodule->mono_eh_frame = (guint8 *)info->mono_eh_frame; + amodule->trampolines [MONO_AOT_TRAMP_SPECIFIC] = (guint8 *)info->specific_trampolines; + amodule->trampolines [MONO_AOT_TRAMP_STATIC_RGCTX] = (guint8 *)info->static_rgctx_trampolines; + amodule->trampolines [MONO_AOT_TRAMP_IMT] = (guint8 *)info->imt_trampolines; + amodule->trampolines [MONO_AOT_TRAMP_GSHAREDVT_ARG] = (guint8 *)info->gsharedvt_arg_trampolines; + + if (!strcmp (assembly->aname.name, "mscorlib")) + mscorlib_aot_module = amodule; + + /* Compute method addresses */ + amodule->methods = (void **)g_malloc0 (amodule->info.nmethods * sizeof (gpointer)); + for (i = 0; i < amodule->info.nmethods; ++i) { + void *addr = NULL; + + if (amodule->info.llvm_get_method) { + gpointer (*get_method) (int) = (gpointer (*)(int))amodule->info.llvm_get_method; + + addr = get_method (i); + } + + /* method_addresses () contains a table of branches, since the ios linker can update those correctly */ + if (!addr && amodule->info.method_addresses) { + addr = get_call_table_entry (amodule->info.method_addresses, i); + g_assert (addr); + if (addr == amodule->info.method_addresses) + addr = NULL; + } + if (addr == NULL) + amodule->methods [i] = GINT_TO_POINTER (-1); + else + amodule->methods [i] = addr; + } + + if (make_unreadable) { +#ifndef TARGET_WIN32 + guint8 *addr; + guint8 *page_start, *page_end; + int err, len; + + addr = amodule->mem_begin; + g_assert (addr); + len = amodule->mem_end - amodule->mem_begin; + + /* Round down in both directions to avoid modifying data which is not ours */ + page_start = (guint8 *) (((gssize) (addr)) & ~ (mono_pagesize () - 1)) + mono_pagesize (); + page_end = (guint8 *) (((gssize) (addr + len)) & ~ (mono_pagesize () - 1)); + if (page_end > page_start) { + err = mono_mprotect (page_start, (page_end - page_start), MONO_MMAP_NONE); + g_assert (err == 0); + } +#endif + } + + /* Compute the boundaries of LLVM code */ + if (info->flags & MONO_AOT_FILE_FLAG_WITH_LLVM) + compute_llvm_code_range (amodule, &amodule->llvm_code_start, &amodule->llvm_code_end); + + mono_aot_lock (); + + if (amodule->jit_code_start) { + aot_code_low_addr = MIN (aot_code_low_addr, (gsize)amodule->jit_code_start); + aot_code_high_addr = MAX (aot_code_high_addr, (gsize)amodule->jit_code_end); + } + if (amodule->llvm_code_start) { + aot_code_low_addr = MIN (aot_code_low_addr, (gsize)amodule->llvm_code_start); + aot_code_high_addr = MAX (aot_code_high_addr, (gsize)amodule->llvm_code_end); + } + + g_hash_table_insert (aot_modules, assembly, amodule); + mono_aot_unlock (); + + if (amodule->jit_code_start) + mono_jit_info_add_aot_module (assembly->image, amodule->jit_code_start, amodule->jit_code_end); + if (amodule->llvm_code_start) + mono_jit_info_add_aot_module (assembly->image, amodule->llvm_code_start, amodule->llvm_code_end); + + assembly->image->aot_module = amodule; + + if (mono_aot_only && !mono_llvm_only) { + char *code; + find_amodule_symbol (amodule, "specific_trampolines_page", (gpointer *)&code); + amodule->use_page_trampolines = code != NULL; + /*g_warning ("using page trampolines: %d", amodule->use_page_trampolines);*/ + } + + /* + * Register the plt region as a single trampoline so we can unwind from this code + */ + mono_aot_tramp_info_register ( + mono_tramp_info_create ( + NULL, + amodule->plt, + amodule->plt_end - amodule->plt, + NULL, + mono_unwind_get_cie_program () + ), + NULL + ); + + /* + * Since we store methoddef and classdef tokens when referring to methods/classes in + * referenced assemblies, we depend on the exact versions of the referenced assemblies. + * MS calls this 'hard binding'. This means we have to load all referenced assemblies + * non-lazily, since we can't handle out-of-date errors later. + * The cached class info also depends on the exact assemblies. + */ + if (do_load_image) { + for (i = 0; i < amodule->image_table_len; ++i) { + MonoError error; + load_image (amodule, i, &error); + mono_error_cleanup (&error); /* FIXME don't swallow the error */ + } + } + + if (amodule->out_of_date) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT: Module %s is unusable because a dependency is out-of-date.", assembly->image->name); + if (mono_aot_only) + g_error ("Failed to load AOT module '%s' while running in aot-only mode because a dependency cannot be found or it is out of date.\n", found_aot_name); + } else { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT: image '%s' found.", found_aot_name); + } +} + +/* + * mono_aot_register_module: + * + * This should be called by embedding code to register normal AOT modules statically linked + * into the executable. + * + * \param aot_info the value of the 'mono_aot_module__info' global symbol from the AOT module. + */ +void +mono_aot_register_module (gpointer *aot_info) +{ + gpointer *globals; + char *aname; + MonoAotFileInfo *info = (MonoAotFileInfo *)aot_info; + + g_assert (info->version == MONO_AOT_FILE_VERSION); + + if (!(info->flags & MONO_AOT_FILE_FLAG_LLVM_ONLY)) { + globals = (void **)info->globals; + g_assert (globals); + } + + aname = (char *)info->assembly_name; + + /* This could be called before startup */ + if (aot_modules) + mono_aot_lock (); + + if (!static_aot_modules) + static_aot_modules = g_hash_table_new (g_str_hash, g_str_equal); + + g_hash_table_insert (static_aot_modules, aname, info); + + if (info->flags & MONO_AOT_FILE_FLAG_EAGER_LOAD) { + g_assert (!container_assm_name); + container_assm_name = aname; + } + + if (aot_modules) + mono_aot_unlock (); +} + +void +mono_aot_init (void) +{ + mono_os_mutex_init_recursive (&aot_mutex); + mono_os_mutex_init_recursive (&aot_page_mutex); + aot_modules = g_hash_table_new (NULL, NULL); + + mono_install_assembly_load_hook (load_aot_module, NULL); + mono_counters_register ("Async JIT info size", MONO_COUNTER_INT|MONO_COUNTER_JIT, &async_jit_info_size); + + char *lastaot = g_getenv ("MONO_LASTAOT"); + if (lastaot) { + mono_last_aot_method = atoi (lastaot); + g_free (lastaot); + } + aot_cache_init (); +} + +void +mono_aot_cleanup (void) +{ + if (aot_jit_icall_hash) + g_hash_table_destroy (aot_jit_icall_hash); + if (aot_modules) + g_hash_table_destroy (aot_modules); +} + +static gboolean +decode_cached_class_info (MonoAotModule *module, MonoCachedClassInfo *info, guint8 *buf, guint8 **endbuf) +{ + MonoError error; + guint32 flags; + MethodRef ref; + gboolean res; + + info->vtable_size = decode_value (buf, &buf); + if (info->vtable_size == -1) + /* Generic type */ + return FALSE; + flags = decode_value (buf, &buf); + info->ghcimpl = (flags >> 0) & 0x1; + info->has_finalize = (flags >> 1) & 0x1; + info->has_cctor = (flags >> 2) & 0x1; + info->has_nested_classes = (flags >> 3) & 0x1; + info->blittable = (flags >> 4) & 0x1; + info->has_references = (flags >> 5) & 0x1; + info->has_static_refs = (flags >> 6) & 0x1; + info->no_special_static_fields = (flags >> 7) & 0x1; + info->is_generic_container = (flags >> 8) & 0x1; + info->has_weak_fields = (flags >> 9) & 0x1; + + if (info->has_cctor) { + res = decode_method_ref (module, &ref, buf, &buf, &error); + mono_error_assert_ok (&error); /* FIXME don't swallow the error */ + if (!res) + return FALSE; + info->cctor_token = ref.token; + } + if (info->has_finalize) { + res = decode_method_ref (module, &ref, buf, &buf, &error); + mono_error_assert_ok (&error); /* FIXME don't swallow the error */ + if (!res) + return FALSE; + info->finalize_image = ref.image; + info->finalize_token = ref.token; + } + + info->instance_size = decode_value (buf, &buf); + info->class_size = decode_value (buf, &buf); + info->packing_size = decode_value (buf, &buf); + info->min_align = decode_value (buf, &buf); + + *endbuf = buf; + + return TRUE; +} + +gpointer +mono_aot_get_method_from_vt_slot (MonoDomain *domain, MonoVTable *vtable, int slot, MonoError *error) +{ + int i; + MonoClass *klass = vtable->klass; + MonoAotModule *amodule = (MonoAotModule *)klass->image->aot_module; + guint8 *info, *p; + MonoCachedClassInfo class_info; + gboolean err; + MethodRef ref; + gboolean res; + gpointer addr; + MonoError inner_error; + + error_init (error); + + if (MONO_CLASS_IS_INTERFACE (klass) || klass->rank || !amodule) + return NULL; + + info = &amodule->blob [mono_aot_get_offset (amodule->class_info_offsets, mono_metadata_token_index (klass->type_token) - 1)]; + p = info; + + err = decode_cached_class_info (amodule, &class_info, p, &p); + if (!err) + return NULL; + + for (i = 0; i < slot; ++i) { + decode_method_ref (amodule, &ref, p, &p, &inner_error); + mono_error_cleanup (&inner_error); /* FIXME don't swallow the error */ + } + + res = decode_method_ref (amodule, &ref, p, &p, &inner_error); + mono_error_cleanup (&inner_error); /* FIXME don't swallow the error */ + if (!res) + return NULL; + if (ref.no_aot_trampoline) + return NULL; + + if (mono_metadata_token_index (ref.token) == 0 || mono_metadata_token_table (ref.token) != MONO_TABLE_METHOD) + return NULL; + + addr = mono_aot_get_method_from_token (domain, ref.image, ref.token, error); + return addr; +} + +gboolean +mono_aot_get_cached_class_info (MonoClass *klass, MonoCachedClassInfo *res) +{ + MonoAotModule *amodule = (MonoAotModule *)klass->image->aot_module; + guint8 *p; + gboolean err; + + if (klass->rank || !klass->type_token || !amodule) + return FALSE; + + p = (guint8*)&amodule->blob [mono_aot_get_offset (amodule->class_info_offsets, mono_metadata_token_index (klass->type_token) - 1)]; + + err = decode_cached_class_info (amodule, res, p, &p); + if (!err) + return FALSE; + + return TRUE; +} + +/** + * mono_aot_get_class_from_name: + * + * Obtains a MonoClass with a given namespace and a given name which is located in IMAGE, + * using a cache stored in the AOT file. + * Stores the resulting class in *KLASS if found, stores NULL otherwise. + * + * Returns: TRUE if the klass was found/not found in the cache, FALSE if no aot file was + * found. + */ +gboolean +mono_aot_get_class_from_name (MonoImage *image, const char *name_space, const char *name, MonoClass **klass) +{ + MonoAotModule *amodule = (MonoAotModule *)image->aot_module; + guint16 *table, *entry; + guint16 table_size; + guint32 hash; + char full_name_buf [1024]; + char *full_name; + const char *name2, *name_space2; + MonoTableInfo *t; + guint32 cols [MONO_TYPEDEF_SIZE]; + GHashTable *nspace_table; + + if (!amodule || !amodule->class_name_table) + return FALSE; + + amodule_lock (amodule); + + *klass = NULL; + + /* First look in the cache */ + if (!amodule->name_cache) + amodule->name_cache = g_hash_table_new (g_str_hash, g_str_equal); + nspace_table = (GHashTable *)g_hash_table_lookup (amodule->name_cache, name_space); + if (nspace_table) { + *klass = (MonoClass *)g_hash_table_lookup (nspace_table, name); + if (*klass) { + amodule_unlock (amodule); + return TRUE; + } + } + + table_size = amodule->class_name_table [0]; + table = amodule->class_name_table + 1; + + if (name_space [0] == '\0') + full_name = g_strdup_printf ("%s", name); + else { + if (strlen (name_space) + strlen (name) < 1000) { + sprintf (full_name_buf, "%s.%s", name_space, name); + full_name = full_name_buf; + } else { + full_name = g_strdup_printf ("%s.%s", name_space, name); + } + } + hash = mono_metadata_str_hash (full_name) % table_size; + if (full_name != full_name_buf) + g_free (full_name); + + entry = &table [hash * 2]; + + if (entry [0] != 0) { + t = &image->tables [MONO_TABLE_TYPEDEF]; + + while (TRUE) { + guint32 index = entry [0]; + guint32 next = entry [1]; + guint32 token = mono_metadata_make_token (MONO_TABLE_TYPEDEF, index); + + name_table_accesses ++; + + mono_metadata_decode_row (t, index - 1, cols, MONO_TYPEDEF_SIZE); + + name2 = mono_metadata_string_heap (image, cols [MONO_TYPEDEF_NAME]); + name_space2 = mono_metadata_string_heap (image, cols [MONO_TYPEDEF_NAMESPACE]); + + if (!strcmp (name, name2) && !strcmp (name_space, name_space2)) { + MonoError error; + amodule_unlock (amodule); + *klass = mono_class_get_checked (image, token, &error); + if (!mono_error_ok (&error)) + mono_error_cleanup (&error); /* FIXME don't swallow the error */ + + /* Add to cache */ + if (*klass) { + amodule_lock (amodule); + nspace_table = (GHashTable *)g_hash_table_lookup (amodule->name_cache, name_space); + if (!nspace_table) { + nspace_table = g_hash_table_new (g_str_hash, g_str_equal); + g_hash_table_insert (amodule->name_cache, (char*)name_space2, nspace_table); + } + g_hash_table_insert (nspace_table, (char*)name2, *klass); + amodule_unlock (amodule); + } + return TRUE; + } + + if (next != 0) { + entry = &table [next * 2]; + } else { + break; + } + } + } + + amodule_unlock (amodule); + + return TRUE; +} + +GHashTable * +mono_aot_get_weak_field_indexes (MonoImage *image) +{ + MonoAotModule *amodule = (MonoAotModule *)image->aot_module; + + if (!amodule) + return NULL; + + /* Initialize weak field indexes from the cached copy */ + guint32 *indexes = amodule->info.weak_field_indexes; + int len = indexes [0]; + GHashTable *indexes_hash = g_hash_table_new (NULL, NULL); + for (int i = 0; i < len; ++i) + g_hash_table_insert (indexes_hash, GUINT_TO_POINTER (indexes [i + 1]), GUINT_TO_POINTER (1)); + return indexes_hash; +} + +/* Compute the boundaries of the LLVM code for AMODULE. */ +static void +compute_llvm_code_range (MonoAotModule *amodule, guint8 **code_start, guint8 **code_end) +{ + guint8 *p; + int version, fde_count; + gint32 *table; + + if (amodule->info.llvm_get_method) { + gpointer (*get_method) (int) = (gpointer (*)(int))amodule->info.llvm_get_method; + + *code_start = (guint8 *)get_method (-1); + *code_end = (guint8 *)get_method (-2); + + g_assert (*code_end > *code_start); + return; + } + + g_assert (amodule->mono_eh_frame); + + p = amodule->mono_eh_frame; + + /* p points to data emitted by LLVM in DwarfException::EmitMonoEHFrame () */ + + /* Header */ + version = *p; + g_assert (version == 3); + p ++; + p ++; + p = (guint8 *)ALIGN_PTR_TO (p, 4); + + fde_count = *(guint32*)p; + p += 4; + table = (gint32*)p; + + if (fde_count > 0) { + *code_start = (guint8 *)amodule->methods [table [0]]; + *code_end = (guint8*)amodule->methods [table [(fde_count - 1) * 2]] + table [fde_count * 2]; + } else { + *code_start = NULL; + *code_end = NULL; + } +} + +static gboolean +is_llvm_code (MonoAotModule *amodule, guint8 *code) +{ + if ((guint8*)code >= amodule->llvm_code_start && (guint8*)code < amodule->llvm_code_end) + return TRUE; + else + return FALSE; +} + +static gboolean +is_thumb_code (MonoAotModule *amodule, guint8 *code) +{ + if (is_llvm_code (amodule, code) && (amodule->info.flags & MONO_AOT_FILE_FLAG_LLVM_THUMB)) + return TRUE; + else + return FALSE; +} + +/* + * decode_llvm_mono_eh_frame: + * + * Decode the EH information emitted by our modified LLVM compiler and construct a + * MonoJitInfo structure from it. + * If JINFO is NULL, set OUT_LLVM_CLAUSES to the number of llvm level clauses. + * This function is async safe when called in async context. + */ +static void +decode_llvm_mono_eh_frame (MonoAotModule *amodule, MonoDomain *domain, MonoJitInfo *jinfo, + guint8 *code, guint32 code_len, + MonoJitExceptionInfo *clauses, int num_clauses, + GSList **nesting, + int *this_reg, int *this_offset, int *out_llvm_clauses) +{ + guint8 *p, *code1, *code2; + guint8 *fde, *cie, *code_start, *code_end; + int version, fde_count; + gint32 *table; + int i, pos, left, right; + MonoJitExceptionInfo *ei; + guint32 fde_len, ei_len, nested_len, nindex; + gpointer *type_info; + MonoLLVMFDEInfo info; + guint8 *unw_info; + gboolean async; + + async = mono_thread_info_is_async_context (); + + if (!amodule->mono_eh_frame) { + if (!jinfo) { + *out_llvm_clauses = num_clauses; + return; + } + memcpy (jinfo->clauses, clauses, num_clauses * sizeof (MonoJitExceptionInfo)); + return; + } + + g_assert (amodule->mono_eh_frame && code); + + p = amodule->mono_eh_frame; + + /* p points to data emitted by LLVM in DwarfMonoException::EmitMonoEHFrame () */ + + /* Header */ + version = *p; + g_assert (version == 3); + p ++; + /* func_encoding = *p; */ + p ++; + p = (guint8 *)ALIGN_PTR_TO (p, 4); + + fde_count = *(guint32*)p; + p += 4; + table = (gint32*)p; + + /* There is +1 entry in the table */ + cie = p + ((fde_count + 1) * 8); + + /* Binary search in the table to find the entry for code */ + left = 0; + right = fde_count; + while (TRUE) { + pos = (left + right) / 2; + + /* The table contains method index/fde offset pairs */ + g_assert (table [(pos * 2)] != -1); + code1 = (guint8 *)amodule->methods [table [(pos * 2)]]; + if (pos + 1 == fde_count) { + code2 = amodule->llvm_code_end; + } else { + g_assert (table [(pos + 1) * 2] != -1); + code2 = (guint8 *)amodule->methods [table [(pos + 1) * 2]]; + } + + if (code < code1) + right = pos; + else if (code >= code2) + left = pos + 1; + else + break; + } + + code_start = (guint8 *)amodule->methods [table [(pos * 2)]]; + if (pos + 1 == fde_count) { + /* The +1 entry in the table contains the length of the last method */ + int len = table [(pos + 1) * 2]; + code_end = code_start + len; + } else { + code_end = (guint8 *)amodule->methods [table [(pos + 1) * 2]]; + } + if (!code_len) + code_len = code_end - code_start; + + g_assert (code >= code_start && code < code_end); + + if (is_thumb_code (amodule, code_start)) + /* Clear thumb flag */ + code_start = (guint8*)(((mgreg_t)code_start) & ~1); + + fde = amodule->mono_eh_frame + table [(pos * 2) + 1]; + /* This won't overflow because there is +1 entry in the table */ + fde_len = table [(pos * 2) + 2 + 1] - table [(pos * 2) + 1]; + + /* Compute lengths */ + mono_unwind_decode_llvm_mono_fde (fde, fde_len, cie, code_start, &info, NULL, NULL, NULL); + + if (async) { + /* These are leaked, but the leak is bounded */ + ei = mono_domain_alloc0_lock_free (domain, info.ex_info_len * sizeof (MonoJitExceptionInfo)); + type_info = mono_domain_alloc0_lock_free (domain, info.ex_info_len * sizeof (gpointer)); + unw_info = mono_domain_alloc0_lock_free (domain, info.unw_info_len); + } else { + ei = (MonoJitExceptionInfo *)g_malloc0 (info.ex_info_len * sizeof (MonoJitExceptionInfo)); + type_info = (gpointer *)g_malloc0 (info.ex_info_len * sizeof (gpointer)); + unw_info = (guint8*)g_malloc0 (info.unw_info_len); + } + mono_unwind_decode_llvm_mono_fde (fde, fde_len, cie, code_start, &info, ei, type_info, unw_info); + + ei_len = info.ex_info_len; + *this_reg = info.this_reg; + *this_offset = info.this_offset; + + /* + * LLVM might represent one IL region with multiple regions. + */ + + /* Count number of nested clauses */ + nested_len = 0; + for (i = 0; i < ei_len; ++i) { + /* This might be unaligned */ + gint32 cindex1 = read32 (type_info [i]); + GSList *l; + + for (l = nesting [cindex1]; l; l = l->next) + nested_len ++; + } + + if (!jinfo) { + *out_llvm_clauses = ei_len + nested_len; + return; + } + + /* Store the unwind info addr/length in the MonoJitInfo structure itself so its async safe */ + MonoUnwindJitInfo *jinfo_unwind = mono_jit_info_get_unwind_info (jinfo); + g_assert (jinfo_unwind); + jinfo_unwind->unw_info = unw_info; + jinfo_unwind->unw_info_len = info.unw_info_len; + + for (i = 0; i < ei_len; ++i) { + /* + * clauses contains the original IL exception info saved by the AOT + * compiler, we have to combine that with the information produced by LLVM + */ + /* The type_info entries contain IL clause indexes */ + int clause_index = read32 (type_info [i]); + MonoJitExceptionInfo *jei = &jinfo->clauses [i]; + MonoJitExceptionInfo *orig_jei = &clauses [clause_index]; + + g_assert (clause_index < num_clauses); + jei->flags = orig_jei->flags; + jei->data.catch_class = orig_jei->data.catch_class; + + jei->try_start = ei [i].try_start; + jei->try_end = ei [i].try_end; + jei->handler_start = ei [i].handler_start; + jei->clause_index = clause_index; + + if (is_thumb_code (amodule, (guint8 *)jei->try_start)) { + jei->try_start = (void*)((mgreg_t)jei->try_start & ~1); + jei->try_end = (void*)((mgreg_t)jei->try_end & ~1); + /* Make sure we transition to thumb when a handler starts */ + jei->handler_start = (void*)((mgreg_t)jei->handler_start + 1); + } + } + + /* See exception_cb () in mini-llvm.c as to why this is needed */ + nindex = ei_len; + for (i = 0; i < ei_len; ++i) { + gint32 cindex1 = read32 (type_info [i]); + GSList *l; + + for (l = nesting [cindex1]; l; l = l->next) { + gint32 nesting_cindex = GPOINTER_TO_INT (l->data); + MonoJitExceptionInfo *nesting_ei; + MonoJitExceptionInfo *nesting_clause = &clauses [nesting_cindex]; + + nesting_ei = &jinfo->clauses [nindex]; + nindex ++; + + memcpy (nesting_ei, &jinfo->clauses [i], sizeof (MonoJitExceptionInfo)); + nesting_ei->flags = nesting_clause->flags; + nesting_ei->data.catch_class = nesting_clause->data.catch_class; + nesting_ei->clause_index = nesting_cindex; + } + } + g_assert (nindex == ei_len + nested_len); +} + +static gpointer +alloc0_jit_info_data (MonoDomain *domain, int size, gboolean async_context) +{ + gpointer res; + + if (async_context) { + res = mono_domain_alloc0_lock_free (domain, size); + mono_atomic_fetch_add_i32 (&async_jit_info_size, size); + } else { + res = mono_domain_alloc0 (domain, size); + } + return res; +} + +/* + * LOCKING: Acquires the domain lock. + * In async context, this is async safe. + */ +static MonoJitInfo* +decode_exception_debug_info (MonoAotModule *amodule, MonoDomain *domain, + MonoMethod *method, guint8* ex_info, + guint8 *code, guint32 code_len) +{ + MonoError error; + int i, buf_len, num_clauses, len; + MonoJitInfo *jinfo; + MonoJitInfoFlags flags = JIT_INFO_NONE; + guint unwind_info, eflags; + gboolean has_generic_jit_info, has_dwarf_unwind_info, has_clauses, has_seq_points, has_try_block_holes, has_arch_eh_jit_info; + gboolean from_llvm, has_gc_map; + guint8 *p; + int try_holes_info_size, num_holes; + int this_reg = 0, this_offset = 0; + gboolean async; + + /* Load the method info from the AOT file */ + async = mono_thread_info_is_async_context (); + + p = ex_info; + eflags = decode_value (p, &p); + has_generic_jit_info = (eflags & 1) != 0; + has_dwarf_unwind_info = (eflags & 2) != 0; + has_clauses = (eflags & 4) != 0; + has_seq_points = (eflags & 8) != 0; + from_llvm = (eflags & 16) != 0; + has_try_block_holes = (eflags & 32) != 0; + has_gc_map = (eflags & 64) != 0; + has_arch_eh_jit_info = (eflags & 128) != 0; + + if (has_dwarf_unwind_info) { + unwind_info = decode_value (p, &p); + g_assert (unwind_info < (1 << 30)); + } else { + unwind_info = decode_value (p, &p); + } + if (has_generic_jit_info) + flags = (MonoJitInfoFlags)(flags | JIT_INFO_HAS_GENERIC_JIT_INFO); + + if (has_try_block_holes) { + num_holes = decode_value (p, &p); + flags = (MonoJitInfoFlags)(flags | JIT_INFO_HAS_TRY_BLOCK_HOLES); + try_holes_info_size = sizeof (MonoTryBlockHoleTableJitInfo) + num_holes * sizeof (MonoTryBlockHoleJitInfo); + } else { + num_holes = try_holes_info_size = 0; + } + + if (has_arch_eh_jit_info) { + flags = (MonoJitInfoFlags)(flags | JIT_INFO_HAS_ARCH_EH_INFO); + /* Overwrite the original code_len which includes alignment padding */ + code_len = decode_value (p, &p); + } + + /* Exception table */ + if (has_clauses) + num_clauses = decode_value (p, &p); + else + num_clauses = 0; + + if (from_llvm) { + MonoJitExceptionInfo *clauses; + GSList **nesting; + + /* + * Part of the info is encoded by the AOT compiler, the rest is in the .eh_frame + * section. + */ + if (async) { + if (num_clauses < 16) { + clauses = g_newa (MonoJitExceptionInfo, num_clauses); + nesting = g_newa (GSList*, num_clauses); + } else { + clauses = alloc0_jit_info_data (domain, sizeof (MonoJitExceptionInfo) * num_clauses, TRUE); + nesting = alloc0_jit_info_data (domain, sizeof (GSList*) * num_clauses, TRUE); + } + memset (clauses, 0, sizeof (MonoJitExceptionInfo) * num_clauses); + memset (nesting, 0, sizeof (GSList*) * num_clauses); + } else { + clauses = g_new0 (MonoJitExceptionInfo, num_clauses); + nesting = g_new0 (GSList*, num_clauses); + } + + for (i = 0; i < num_clauses; ++i) { + MonoJitExceptionInfo *ei = &clauses [i]; + + ei->flags = decode_value (p, &p); + + if (!(ei->flags == MONO_EXCEPTION_CLAUSE_FILTER || ei->flags == MONO_EXCEPTION_CLAUSE_FINALLY)) { + int len = decode_value (p, &p); + + if (len > 0) { + if (async) { + p += len; + } else { + ei->data.catch_class = decode_klass_ref (amodule, p, &p, &error); + mono_error_cleanup (&error); /* FIXME don't swallow the error */ + } + } + } + + ei->clause_index = i; + + ei->try_offset = decode_value (p, &p); + ei->try_len = decode_value (p, &p); + ei->handler_offset = decode_value (p, &p); + ei->handler_len = decode_value (p, &p); + + /* Read the list of nesting clauses */ + while (TRUE) { + int nesting_index = decode_value (p, &p); + if (nesting_index == -1) + break; + // FIXME: async + g_assert (!async); + nesting [i] = g_slist_prepend (nesting [i], GINT_TO_POINTER (nesting_index)); + } + } + + flags |= JIT_INFO_HAS_UNWIND_INFO; + + int num_llvm_clauses; + /* Get the length first */ + decode_llvm_mono_eh_frame (amodule, domain, NULL, code, code_len, clauses, num_clauses, nesting, &this_reg, &this_offset, &num_llvm_clauses); + len = mono_jit_info_size (flags, num_llvm_clauses, num_holes); + jinfo = (MonoJitInfo *)alloc0_jit_info_data (domain, len, async); + mono_jit_info_init (jinfo, method, code, code_len, flags, num_llvm_clauses, num_holes); + + decode_llvm_mono_eh_frame (amodule, domain, jinfo, code, code_len, clauses, num_clauses, nesting, &this_reg, &this_offset, NULL); + + if (!async) { + g_free (clauses); + for (i = 0; i < num_clauses; ++i) + g_slist_free (nesting [i]); + g_free (nesting); + } + jinfo->from_llvm = 1; + } else { + len = mono_jit_info_size (flags, num_clauses, num_holes); + jinfo = (MonoJitInfo *)alloc0_jit_info_data (domain, len, async); + mono_jit_info_init (jinfo, method, code, code_len, flags, num_clauses, num_holes); + + for (i = 0; i < jinfo->num_clauses; ++i) { + MonoJitExceptionInfo *ei = &jinfo->clauses [i]; + + ei->flags = decode_value (p, &p); + +#ifdef MONO_CONTEXT_SET_LLVM_EXC_REG + /* Not used for catch clauses */ + if (ei->flags != MONO_EXCEPTION_CLAUSE_NONE) + ei->exvar_offset = decode_value (p, &p); +#else + ei->exvar_offset = decode_value (p, &p); +#endif + + if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER || ei->flags == MONO_EXCEPTION_CLAUSE_FINALLY) + ei->data.filter = code + decode_value (p, &p); + else { + int len = decode_value (p, &p); + + if (len > 0) { + if (async) { + p += len; + } else { + ei->data.catch_class = decode_klass_ref (amodule, p, &p, &error); + mono_error_cleanup (&error); /* FIXME don't swallow the error */ + } + } + } + + ei->try_start = code + decode_value (p, &p); + ei->try_end = code + decode_value (p, &p); + ei->handler_start = code + decode_value (p, &p); + } + + jinfo->unwind_info = unwind_info; + jinfo->domain_neutral = 0; + jinfo->from_aot = 1; + } + + if (has_try_block_holes) { + MonoTryBlockHoleTableJitInfo *table; + + g_assert (jinfo->has_try_block_holes); + + table = mono_jit_info_get_try_block_hole_table_info (jinfo); + g_assert (table); + + table->num_holes = (guint16)num_holes; + for (i = 0; i < num_holes; ++i) { + MonoTryBlockHoleJitInfo *hole = &table->holes [i]; + hole->clause = decode_value (p, &p); + hole->length = decode_value (p, &p); + hole->offset = decode_value (p, &p); + } + } + + if (has_arch_eh_jit_info) { + MonoArchEHJitInfo *eh_info; + + g_assert (jinfo->has_arch_eh_info); + + eh_info = mono_jit_info_get_arch_eh_info (jinfo); + eh_info->stack_size = decode_value (p, &p); + eh_info->epilog_size = decode_value (p, &p); + } + + if (async) { + /* The rest is not needed in async mode */ + jinfo->async = TRUE; + jinfo->d.aot_info = amodule; + // FIXME: Cache + return jinfo; + } + + if (has_generic_jit_info) { + MonoGenericJitInfo *gi; + int len; + + g_assert (jinfo->has_generic_jit_info); + + gi = mono_jit_info_get_generic_jit_info (jinfo); + g_assert (gi); + + gi->nlocs = decode_value (p, &p); + if (gi->nlocs) { + gi->locations = (MonoDwarfLocListEntry *)alloc0_jit_info_data (domain, gi->nlocs * sizeof (MonoDwarfLocListEntry), async); + for (i = 0; i < gi->nlocs; ++i) { + MonoDwarfLocListEntry *entry = &gi->locations [i]; + + entry->is_reg = decode_value (p, &p); + entry->reg = decode_value (p, &p); + if (!entry->is_reg) + entry->offset = decode_value (p, &p); + if (i > 0) + entry->from = decode_value (p, &p); + entry->to = decode_value (p, &p); + } + gi->has_this = 1; + } else { + if (from_llvm) { + gi->has_this = this_reg != -1; + gi->this_reg = this_reg; + gi->this_offset = this_offset; + } else { + gi->has_this = decode_value (p, &p); + gi->this_reg = decode_value (p, &p); + gi->this_offset = decode_value (p, &p); + } + } + + len = decode_value (p, &p); + if (async) { + p += len; + } else { + jinfo->d.method = decode_resolve_method_ref (amodule, p, &p, &error); + mono_error_cleanup (&error); /* FIXME don't swallow the error */ + } + + gi->generic_sharing_context = alloc0_jit_info_data (domain, sizeof (MonoGenericSharingContext), async); + if (decode_value (p, &p)) { + /* gsharedvt */ + MonoGenericSharingContext *gsctx = gi->generic_sharing_context; + + gsctx->is_gsharedvt = TRUE; + } + } + + if (method && has_seq_points) { + MonoSeqPointInfo *seq_points; + + p += mono_seq_point_info_read (&seq_points, p, FALSE); + + mono_domain_lock (domain); + /* This could be set already since this function can be called more than once for the same method */ + if (!g_hash_table_lookup (domain_jit_info (domain)->seq_points, method)) + g_hash_table_insert (domain_jit_info (domain)->seq_points, method, seq_points); + else + mono_seq_point_info_free (seq_points); + mono_domain_unlock (domain); + } + + /* Load debug info */ + buf_len = decode_value (p, &p); + if (!async) + mono_debug_add_aot_method (domain, method, code, p, buf_len); + p += buf_len; + + if (has_gc_map) { + int map_size = decode_value (p, &p); + /* The GC map requires 4 bytes of alignment */ + while ((guint64)(gsize)p % 4) + p ++; + jinfo->gc_info = p; + p += map_size; + } + + if (amodule != jinfo->d.method->klass->image->aot_module) { + mono_aot_lock (); + if (!ji_to_amodule) + ji_to_amodule = g_hash_table_new (NULL, NULL); + g_hash_table_insert (ji_to_amodule, jinfo, amodule); + mono_aot_unlock (); + } + + return jinfo; +} + +static gboolean +amodule_contains_code_addr (MonoAotModule *amodule, guint8 *code) +{ + return (code >= amodule->jit_code_start && code <= amodule->jit_code_end) || + (code >= amodule->llvm_code_start && code <= amodule->llvm_code_end); +} + +/* + * mono_aot_get_unwind_info: + * + * Return a pointer to the DWARF unwind info belonging to JI. + */ +guint8* +mono_aot_get_unwind_info (MonoJitInfo *ji, guint32 *unwind_info_len) +{ + MonoAotModule *amodule; + guint8 *p; + guint8 *code = (guint8 *)ji->code_start; + + if (ji->async) + amodule = (MonoAotModule *)ji->d.aot_info; + else + amodule = (MonoAotModule *)jinfo_get_method (ji)->klass->image->aot_module; + g_assert (amodule); + g_assert (ji->from_aot); + + if (!amodule_contains_code_addr (amodule, code)) { + /* ji belongs to a different aot module than amodule */ + mono_aot_lock (); + g_assert (ji_to_amodule); + amodule = (MonoAotModule *)g_hash_table_lookup (ji_to_amodule, ji); + g_assert (amodule); + g_assert (amodule_contains_code_addr (amodule, code)); + mono_aot_unlock (); + } + + p = amodule->unwind_info + ji->unwind_info; + *unwind_info_len = decode_value (p, &p); + return p; +} + +static void +msort_method_addresses_internal (gpointer *array, int *indexes, int lo, int hi, gpointer *scratch, int *scratch_indexes) +{ + int mid = (lo + hi) / 2; + int i, t_lo, t_hi; + + if (lo >= hi) + return; + + if (hi - lo < 32) { + for (i = lo; i < hi; ++i) + if (array [i] > array [i + 1]) + break; + if (i == hi) + /* Already sorted */ + return; + } + + msort_method_addresses_internal (array, indexes, lo, mid, scratch, scratch_indexes); + msort_method_addresses_internal (array, indexes, mid + 1, hi, scratch, scratch_indexes); + + if (array [mid] < array [mid + 1]) + return; + + /* Merge */ + t_lo = lo; + t_hi = mid + 1; + for (i = lo; i <= hi; i ++) { + if (t_lo <= mid && ((t_hi > hi) || array [t_lo] < array [t_hi])) { + scratch [i] = array [t_lo]; + scratch_indexes [i] = indexes [t_lo]; + t_lo ++; + } else { + scratch [i] = array [t_hi]; + scratch_indexes [i] = indexes [t_hi]; + t_hi ++; + } + } + for (i = lo; i <= hi; ++i) { + array [i] = scratch [i]; + indexes [i] = scratch_indexes [i]; + } +} + +static void +msort_method_addresses (gpointer *array, int *indexes, int len) +{ + gpointer *scratch; + int *scratch_indexes; + + scratch = g_new (gpointer, len); + scratch_indexes = g_new (int, len); + msort_method_addresses_internal (array, indexes, 0, len - 1, scratch, scratch_indexes); + g_free (scratch); + g_free (scratch_indexes); +} + +/* + * mono_aot_find_jit_info: + * + * In async context, the resulting MonoJitInfo will not have its method field set, and it will not be added + * to the jit info tables. + * FIXME: Large sizes in the lock free allocator + */ +MonoJitInfo * +mono_aot_find_jit_info (MonoDomain *domain, MonoImage *image, gpointer addr) +{ + MonoError error; + int pos, left, right, code_len; + int method_index, table_len; + guint32 token; + MonoAotModule *amodule = (MonoAotModule *)image->aot_module; + MonoMethod *method = NULL; + MonoJitInfo *jinfo; + guint8 *code, *ex_info, *p; + guint32 *table; + int nmethods; + gpointer *methods; + guint8 *code1, *code2; + int methods_len, i; + gboolean async; + + if (!amodule) + return NULL; + + nmethods = amodule->info.nmethods; + + if (domain != mono_get_root_domain ()) + /* FIXME: */ + return NULL; + + if (!amodule_contains_code_addr (amodule, (guint8 *)addr)) + return NULL; + + async = mono_thread_info_is_async_context (); + + /* Compute a sorted table mapping code to method indexes. */ + if (!amodule->sorted_methods) { + // FIXME: async + gpointer *methods = g_new0 (gpointer, nmethods); + int *method_indexes = g_new0 (int, nmethods); + int methods_len = 0; + + for (i = 0; i < nmethods; ++i) { + /* Skip the -1 entries to speed up sorting */ + if (amodule->methods [i] == GINT_TO_POINTER (-1)) + continue; + methods [methods_len] = amodule->methods [i]; + method_indexes [methods_len] = i; + methods_len ++; + } + /* Use a merge sort as this is mostly sorted */ + msort_method_addresses (methods, method_indexes, methods_len); + for (i = 0; i < methods_len -1; ++i) + g_assert (methods [i] <= methods [i + 1]); + amodule->sorted_methods_len = methods_len; + if (mono_atomic_cas_ptr ((gpointer*)&amodule->sorted_methods, methods, NULL) != NULL) + /* Somebody got in before us */ + g_free (methods); + if (mono_atomic_cas_ptr ((gpointer*)&amodule->sorted_method_indexes, method_indexes, NULL) != NULL) + /* Somebody got in before us */ + g_free (method_indexes); + } + + /* Binary search in the sorted_methods table */ + methods = amodule->sorted_methods; + methods_len = amodule->sorted_methods_len; + code = (guint8 *)addr; + left = 0; + right = methods_len; + while (TRUE) { + pos = (left + right) / 2; + + code1 = (guint8 *)methods [pos]; + if (pos + 1 == methods_len) { + if (code1 >= amodule->jit_code_start && code1 < amodule->jit_code_end) + code2 = amodule->jit_code_end; + else + code2 = amodule->llvm_code_end; + } else { + code2 = (guint8 *)methods [pos + 1]; + } + + if (code < code1) + right = pos; + else if (code >= code2) + left = pos + 1; + else + break; + } + + g_assert (addr >= methods [pos]); + if (pos + 1 < methods_len) + g_assert (addr < methods [pos + 1]); + method_index = amodule->sorted_method_indexes [pos]; + + /* In async mode, jinfo is not added to the normal jit info table, so have to cache it ourselves */ + if (async) { + JitInfoMap *table = amodule->async_jit_info_table; + int len; + + if (table) { + len = table [0].method_index; + for (i = 1; i < len; ++i) { + if (table [i].method_index == method_index) + return table [i].jinfo; + } + } + } + + code = (guint8 *)amodule->methods [method_index]; + ex_info = &amodule->blob [mono_aot_get_offset (amodule->ex_info_offsets, method_index)]; + + if (pos == methods_len - 1) { + if (code >= amodule->jit_code_start && code < amodule->jit_code_end) + code_len = amodule->jit_code_end - code; + else + code_len = amodule->llvm_code_end - code; + } else { + code_len = (guint8*)methods [pos + 1] - (guint8*)methods [pos]; + } + + g_assert ((guint8*)code <= (guint8*)addr && (guint8*)addr < (guint8*)code + code_len); + + /* Might be a wrapper/extra method */ + if (!async) { + if (amodule->extra_methods) { + amodule_lock (amodule); + method = (MonoMethod *)g_hash_table_lookup (amodule->extra_methods, GUINT_TO_POINTER (method_index)); + amodule_unlock (amodule); + } else { + method = NULL; + } + + if (!method) { + if (method_index >= image->tables [MONO_TABLE_METHOD].rows) { + /* + * This is hit for extra methods which are called directly, so they are + * not in amodule->extra_methods. + */ + table_len = amodule->extra_method_info_offsets [0]; + table = amodule->extra_method_info_offsets + 1; + left = 0; + right = table_len; + pos = 0; + + /* Binary search */ + while (TRUE) { + pos = ((left + right) / 2); + + g_assert (pos < table_len); + + if (table [pos * 2] < method_index) + left = pos + 1; + else if (table [pos * 2] > method_index) + right = pos; + else + break; + } + + p = amodule->blob + table [(pos * 2) + 1]; + method = decode_resolve_method_ref (amodule, p, &p, &error); + mono_error_cleanup (&error); /* FIXME don't swallow the error */ + if (!method) + /* Happens when a random address is passed in which matches a not-yey called wrapper encoded using its name */ + return NULL; + } else { + MonoError error; + token = mono_metadata_make_token (MONO_TABLE_METHOD, method_index + 1); + method = mono_get_method_checked (image, token, NULL, NULL, &error); + if (!method) + g_error ("AOT runtime could not load method due to %s", mono_error_get_message (&error)); /* FIXME don't swallow the error */ + } + } + /* FIXME: */ + g_assert (method); + } + + //printf ("F: %s\n", mono_method_full_name (method, TRUE)); + + jinfo = decode_exception_debug_info (amodule, domain, method, ex_info, code, code_len); + + g_assert ((guint8*)addr >= (guint8*)jinfo->code_start); + + /* Add it to the normal JitInfo tables */ + if (async) { + JitInfoMap *old_table, *new_table; + int len; + + /* + * Use a simple inmutable table with linear search to cache async jit info entries. + * This assumes that the number of entries is small. + */ + while (TRUE) { + /* Copy the table, adding a new entry at the end */ + old_table = amodule->async_jit_info_table; + if (old_table) + len = old_table[0].method_index; + else + len = 1; + new_table = (JitInfoMap *)alloc0_jit_info_data (domain, (len + 1) * sizeof (JitInfoMap), async); + if (old_table) + memcpy (new_table, old_table, len * sizeof (JitInfoMap)); + new_table [0].method_index = len + 1; + new_table [len].method_index = method_index; + new_table [len].jinfo = jinfo; + /* Publish it */ + mono_memory_barrier (); + if (mono_atomic_cas_ptr ((volatile gpointer *)&amodule->async_jit_info_table, new_table, old_table) == old_table) + break; + } + } else { + mono_jit_info_table_add (domain, jinfo); + } + + if ((guint8*)addr >= (guint8*)jinfo->code_start + jinfo->code_size) + /* addr is in the padding between methods, see the adjustment of code_size in decode_exception_debug_info () */ + return NULL; + + return jinfo; +} + +static gboolean +decode_patch (MonoAotModule *aot_module, MonoMemPool *mp, MonoJumpInfo *ji, guint8 *buf, guint8 **endbuf) +{ + MonoError error; + guint8 *p = buf; + gpointer *table; + MonoImage *image; + int i; + + switch (ji->type) { + case MONO_PATCH_INFO_METHOD: + case MONO_PATCH_INFO_METHOD_JUMP: + case MONO_PATCH_INFO_ICALL_ADDR: + case MONO_PATCH_INFO_ICALL_ADDR_CALL: + case MONO_PATCH_INFO_METHOD_RGCTX: + case MONO_PATCH_INFO_METHOD_CODE_SLOT: { + MethodRef ref; + gboolean res; + + res = decode_method_ref (aot_module, &ref, p, &p, &error); + mono_error_assert_ok (&error); /* FIXME don't swallow the error */ + if (!res) + goto cleanup; + + if (!ref.method && !mono_aot_only && !ref.no_aot_trampoline && (ji->type == MONO_PATCH_INFO_METHOD) && (mono_metadata_token_table (ref.token) == MONO_TABLE_METHOD)) { + ji->data.target = mono_create_ftnptr (mono_domain_get (), mono_create_jit_trampoline_from_token (ref.image, ref.token)); + ji->type = MONO_PATCH_INFO_ABS; + } + else { + if (ref.method) { + ji->data.method = ref.method; + }else { + MonoError error; + ji->data.method = mono_get_method_checked (ref.image, ref.token, NULL, NULL, &error); + if (!ji->data.method) + g_error ("AOT Runtime could not load method due to %s", mono_error_get_message (&error)); /* FIXME don't swallow the error */ + } + g_assert (ji->data.method); + mono_class_init (ji->data.method->klass); + } + break; + } + case MONO_PATCH_INFO_INTERNAL_METHOD: + case MONO_PATCH_INFO_JIT_ICALL_ADDR: + case MONO_PATCH_INFO_JIT_ICALL_ADDR_NOCALL: { + guint32 len = decode_value (p, &p); + + ji->data.name = (char*)p; + p += len + 1; + break; + } + case MONO_PATCH_INFO_METHODCONST: + /* Shared */ + ji->data.method = decode_resolve_method_ref (aot_module, p, &p, &error); + mono_error_cleanup (&error); /* FIXME don't swallow the error */ + if (!ji->data.method) + goto cleanup; + break; + case MONO_PATCH_INFO_VTABLE: + case MONO_PATCH_INFO_CLASS: + case MONO_PATCH_INFO_IID: + case MONO_PATCH_INFO_ADJUSTED_IID: + /* Shared */ + ji->data.klass = decode_klass_ref (aot_module, p, &p, &error); + mono_error_cleanup (&error); /* FIXME don't swallow the error */ + if (!ji->data.klass) + goto cleanup; + break; + case MONO_PATCH_INFO_DELEGATE_TRAMPOLINE: + ji->data.del_tramp = (MonoDelegateClassMethodPair *)mono_mempool_alloc0 (mp, sizeof (MonoDelegateClassMethodPair)); + ji->data.del_tramp->klass = decode_klass_ref (aot_module, p, &p, &error); + mono_error_cleanup (&error); /* FIXME don't swallow the error */ + if (!ji->data.del_tramp->klass) + goto cleanup; + if (decode_value (p, &p)) { + ji->data.del_tramp->method = decode_resolve_method_ref (aot_module, p, &p, &error); + mono_error_cleanup (&error); /* FIXME don't swallow the error */ + if (!ji->data.del_tramp->method) + goto cleanup; + } + ji->data.del_tramp->is_virtual = decode_value (p, &p) ? TRUE : FALSE; + break; + case MONO_PATCH_INFO_IMAGE: + ji->data.image = load_image (aot_module, decode_value (p, &p), &error); + mono_error_cleanup (&error); /* FIXME don't swallow the error */ + if (!ji->data.image) + goto cleanup; + break; + case MONO_PATCH_INFO_FIELD: + case MONO_PATCH_INFO_SFLDA: + /* Shared */ + ji->data.field = decode_field_info (aot_module, p, &p); + if (!ji->data.field) + goto cleanup; + break; + case MONO_PATCH_INFO_SWITCH: + ji->data.table = (MonoJumpInfoBBTable *)mono_mempool_alloc0 (mp, sizeof (MonoJumpInfoBBTable)); + ji->data.table->table_size = decode_value (p, &p); + table = (void **)mono_domain_alloc (mono_domain_get (), sizeof (gpointer) * ji->data.table->table_size); + ji->data.table->table = (MonoBasicBlock**)table; + for (i = 0; i < ji->data.table->table_size; i++) + table [i] = (gpointer)(gssize)decode_value (p, &p); + break; + case MONO_PATCH_INFO_R4: { + guint32 val; + + ji->data.target = mono_domain_alloc0 (mono_domain_get (), sizeof (float)); + val = decode_value (p, &p); + *(float*)ji->data.target = *(float*)&val; + break; + } + case MONO_PATCH_INFO_R8: { + guint32 val [2]; + guint64 v; + + ji->data.target = mono_domain_alloc0 (mono_domain_get (), sizeof (double)); + + val [0] = decode_value (p, &p); + val [1] = decode_value (p, &p); + v = ((guint64)val [1] << 32) | ((guint64)val [0]); + *(double*)ji->data.target = *(double*)&v; + break; + } + case MONO_PATCH_INFO_LDSTR: + image = load_image (aot_module, decode_value (p, &p), &error); + mono_error_cleanup (&error); /* FIXME don't swallow the error */ + if (!image) + goto cleanup; + ji->data.token = mono_jump_info_token_new (mp, image, MONO_TOKEN_STRING + decode_value (p, &p)); + break; + case MONO_PATCH_INFO_RVA: + case MONO_PATCH_INFO_DECLSEC: + case MONO_PATCH_INFO_LDTOKEN: + case MONO_PATCH_INFO_TYPE_FROM_HANDLE: + /* Shared */ + image = load_image (aot_module, decode_value (p, &p), &error); + mono_error_cleanup (&error); /* FIXME don't swallow the error */ + if (!image) + goto cleanup; + ji->data.token = mono_jump_info_token_new (mp, image, decode_value (p, &p)); + + ji->data.token->has_context = decode_value (p, &p); + if (ji->data.token->has_context) { + gboolean res = decode_generic_context (aot_module, &ji->data.token->context, p, &p, &error); + mono_error_cleanup (&error); /* FIXME don't swallow the error */ + if (!res) + goto cleanup; + } + break; + case MONO_PATCH_INFO_EXC_NAME: + ji->data.klass = decode_klass_ref (aot_module, p, &p, &error); + mono_error_cleanup (&error); /* FIXME don't swallow the error */ + if (!ji->data.klass) + goto cleanup; + ji->data.name = ji->data.klass->name; + break; + case MONO_PATCH_INFO_METHOD_REL: + ji->data.offset = decode_value (p, &p); + break; + case MONO_PATCH_INFO_INTERRUPTION_REQUEST_FLAG: + case MONO_PATCH_INFO_GC_CARD_TABLE_ADDR: + case MONO_PATCH_INFO_GC_NURSERY_START: + case MONO_PATCH_INFO_GC_NURSERY_BITS: + case MONO_PATCH_INFO_PROFILER_ALLOCATION_COUNT: + break; + case MONO_PATCH_INFO_CASTCLASS_CACHE: + ji->data.index = decode_value (p, &p); + break; + case MONO_PATCH_INFO_RGCTX_FETCH: + case MONO_PATCH_INFO_RGCTX_SLOT_INDEX: { + gboolean res; + MonoJumpInfoRgctxEntry *entry; + guint32 offset, val; + guint8 *p2; + + offset = decode_value (p, &p); + val = decode_value (p, &p); + + entry = (MonoJumpInfoRgctxEntry *)mono_mempool_alloc0 (mp, sizeof (MonoJumpInfoRgctxEntry)); + p2 = aot_module->blob + offset; + entry->method = decode_resolve_method_ref (aot_module, p2, &p2, &error); + entry->in_mrgctx = ((val & 1) > 0) ? TRUE : FALSE; + entry->info_type = (MonoRgctxInfoType)((val >> 1) & 0xff); + entry->data = (MonoJumpInfo *)mono_mempool_alloc0 (mp, sizeof (MonoJumpInfo)); + entry->data->type = (MonoJumpInfoType)((val >> 9) & 0xff); + mono_error_cleanup (&error); /* FIXME don't swallow the error */ + + res = decode_patch (aot_module, mp, entry->data, p, &p); + if (!res) + goto cleanup; + ji->data.rgctx_entry = entry; + break; + } + case MONO_PATCH_INFO_SEQ_POINT_INFO: + case MONO_PATCH_INFO_AOT_MODULE: + case MONO_PATCH_INFO_MSCORLIB_GOT_ADDR: + break; + case MONO_PATCH_INFO_SIGNATURE: + case MONO_PATCH_INFO_GSHAREDVT_IN_WRAPPER: + ji->data.target = decode_signature (aot_module, p, &p); + break; + case MONO_PATCH_INFO_GSHAREDVT_CALL: { + MonoJumpInfoGSharedVtCall *info = (MonoJumpInfoGSharedVtCall *)mono_mempool_alloc0 (mp, sizeof (MonoJumpInfoGSharedVtCall)); + info->sig = decode_signature (aot_module, p, &p); + g_assert (info->sig); + info->method = decode_resolve_method_ref (aot_module, p, &p, &error); + mono_error_assert_ok (&error); /* FIXME don't swallow the error */ + + ji->data.target = info; + break; + } + case MONO_PATCH_INFO_GSHAREDVT_METHOD: { + MonoGSharedVtMethodInfo *info = (MonoGSharedVtMethodInfo *)mono_mempool_alloc0 (mp, sizeof (MonoGSharedVtMethodInfo)); + int i; + + info->method = decode_resolve_method_ref (aot_module, p, &p, &error); + mono_error_assert_ok (&error); /* FIXME don't swallow the error */ + + info->num_entries = decode_value (p, &p); + info->count_entries = info->num_entries; + info->entries = (MonoRuntimeGenericContextInfoTemplate *)mono_mempool_alloc0 (mp, sizeof (MonoRuntimeGenericContextInfoTemplate) * info->num_entries); + for (i = 0; i < info->num_entries; ++i) { + MonoRuntimeGenericContextInfoTemplate *template_ = &info->entries [i]; + + template_->info_type = (MonoRgctxInfoType)decode_value (p, &p); + switch (mini_rgctx_info_type_to_patch_info_type (template_->info_type)) { + case MONO_PATCH_INFO_CLASS: { + MonoClass *klass = decode_klass_ref (aot_module, p, &p, &error); + mono_error_cleanup (&error); /* FIXME don't swallow the error */ + if (!klass) + goto cleanup; + template_->data = &klass->byval_arg; + break; + } + case MONO_PATCH_INFO_FIELD: + template_->data = decode_field_info (aot_module, p, &p); + if (!template_->data) + goto cleanup; + break; + default: + g_assert_not_reached (); + break; + } + } + ji->data.target = info; + break; + } + case MONO_PATCH_INFO_LDSTR_LIT: { + int len = decode_value (p, &p); + char *s; + + s = (char *)mono_mempool_alloc0 (mp, len + 1); + memcpy (s, p, len + 1); + p += len + 1; + + ji->data.target = s; + break; + } + case MONO_PATCH_INFO_VIRT_METHOD: { + MonoJumpInfoVirtMethod *info = (MonoJumpInfoVirtMethod *)mono_mempool_alloc0 (mp, sizeof (MonoJumpInfoVirtMethod)); + + info->klass = decode_klass_ref (aot_module, p, &p, &error); + mono_error_assert_ok (&error); /* FIXME don't swallow the error */ + + info->method = decode_resolve_method_ref (aot_module, p, &p, &error); + mono_error_assert_ok (&error); /* FIXME don't swallow the error */ + + ji->data.target = info; + break; + } + case MONO_PATCH_INFO_GC_SAFE_POINT_FLAG: + case MONO_PATCH_INFO_JIT_THREAD_ATTACH: + break; + case MONO_PATCH_INFO_GET_TLS_TRAMP: + case MONO_PATCH_INFO_SET_TLS_TRAMP: + case MONO_PATCH_INFO_AOT_JIT_INFO: + ji->data.index = decode_value (p, &p); + break; + default: + g_warning ("unhandled type %d", ji->type); + g_assert_not_reached (); + } + + *endbuf = p; + + return TRUE; + + cleanup: + return FALSE; +} + +/* + * decode_patches: + * + * Decode a list of patches identified by the got offsets in GOT_OFFSETS. Return an array of + * MonoJumpInfo structures allocated from MP. + */ +static MonoJumpInfo* +decode_patches (MonoAotModule *amodule, MonoMemPool *mp, int n_patches, gboolean llvm, guint32 *got_offsets) +{ + MonoJumpInfo *patches; + MonoJumpInfo *ji; + gpointer *got; + guint32 *got_info_offsets; + int i; + gboolean res; + + if (llvm) { + got = amodule->llvm_got; + got_info_offsets = (guint32 *)amodule->llvm_got_info_offsets; + } else { + got = amodule->got; + got_info_offsets = (guint32 *)amodule->got_info_offsets; + } + + patches = (MonoJumpInfo *)mono_mempool_alloc0 (mp, sizeof (MonoJumpInfo) * n_patches); + for (i = 0; i < n_patches; ++i) { + guint8 *p = amodule->blob + mono_aot_get_offset (got_info_offsets, got_offsets [i]); + + ji = &patches [i]; + ji->type = (MonoJumpInfoType)decode_value (p, &p); + + /* See load_method () for SFLDA */ + if (got && got [got_offsets [i]] && ji->type != MONO_PATCH_INFO_SFLDA) { + /* Already loaded */ + } else { + res = decode_patch (amodule, mp, ji, p, &p); + if (!res) + return NULL; + } + } + + return patches; +} + +static MonoJumpInfo* +load_patch_info (MonoAotModule *amodule, MonoMemPool *mp, int n_patches, + gboolean llvm, guint32 **got_slots, + guint8 *buf, guint8 **endbuf) +{ + MonoJumpInfo *patches; + int pindex; + guint8 *p; + + p = buf; + + *got_slots = (guint32 *)g_malloc (sizeof (guint32) * n_patches); + for (pindex = 0; pindex < n_patches; ++pindex) { + (*got_slots)[pindex] = decode_value (p, &p); + } + + patches = decode_patches (amodule, mp, n_patches, llvm, *got_slots); + if (!patches) { + g_free (*got_slots); + *got_slots = NULL; + return NULL; + } + + *endbuf = p; + return patches; +} + +static void +register_jump_target_got_slot (MonoDomain *domain, MonoMethod *method, gpointer *got_slot) +{ + /* + * Jump addresses cannot be patched by the trampoline code since it + * does not have access to the caller's address. Instead, we collect + * the addresses of the GOT slots pointing to a method, and patch + * them after the method has been compiled. + */ + MonoJitDomainInfo *info = domain_jit_info (domain); + GSList *list; + + mono_domain_lock (domain); + if (!info->jump_target_got_slot_hash) + info->jump_target_got_slot_hash = g_hash_table_new (NULL, NULL); + list = (GSList *)g_hash_table_lookup (info->jump_target_got_slot_hash, method); + list = g_slist_prepend (list, got_slot); + g_hash_table_insert (info->jump_target_got_slot_hash, method, list); + mono_domain_unlock (domain); +} + +/* + * load_method: + * + * Load the method identified by METHOD_INDEX from the AOT image. Return a + * pointer to the native code of the method, or NULL if not found. + * METHOD might not be set if the caller only has the image/token info. + */ +static gpointer +load_method (MonoDomain *domain, MonoAotModule *amodule, MonoImage *image, MonoMethod *method, guint32 token, int method_index, + MonoError *error) +{ + MonoJitInfo *jinfo = NULL; + guint8 *code = NULL, *info; + gboolean res; + + error_init (error); + + init_amodule_got (amodule); + + if (domain != mono_get_root_domain ()) + /* Non shared AOT code can't be used in other appdomains */ + return NULL; + + if (amodule->out_of_date) + return NULL; + + if (amodule->info.llvm_get_method) { + /* + * Obtain the method address by calling a generated function in the LLVM module. + */ + gpointer (*get_method) (int) = (gpointer (*)(int))amodule->info.llvm_get_method; + code = (guint8 *)get_method (method_index); + } + + if (!code) { + /* JITted method */ + if (amodule->methods [method_index] == GINT_TO_POINTER (-1)) { + if (mono_trace_is_traced (G_LOG_LEVEL_DEBUG, MONO_TRACE_AOT)) { + char *full_name; + + if (!method) { + method = mono_get_method_checked (image, token, NULL, NULL, error); + if (!method) + return NULL; + } + if (!(method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)) { + full_name = mono_method_full_name (method, TRUE); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_AOT, "AOT: NOT FOUND: %s.", full_name); + g_free (full_name); + } + } + return NULL; + } + code = (guint8 *)amodule->methods [method_index]; + } + + info = &amodule->blob [mono_aot_get_offset (amodule->method_info_offsets, method_index)]; + + if (!amodule->methods_loaded) { + amodule_lock (amodule); + if (!amodule->methods_loaded) { + guint32 *loaded; + + loaded = g_new0 (guint32, amodule->info.nmethods / 32 + 1); + mono_memory_barrier (); + amodule->methods_loaded = loaded; + } + amodule_unlock (amodule); + } + + if ((amodule->methods_loaded [method_index / 32] >> (method_index % 32)) & 0x1) + return code; + + if (mono_last_aot_method != -1) { + gint32 methods_aot = mono_atomic_load_i32 (&mono_jit_stats.methods_aot); + if (methods_aot >= mono_last_aot_method) + return NULL; + else if (methods_aot == mono_last_aot_method - 1) { + if (!method) { + method = mono_get_method_checked (image, token, NULL, NULL, error); + if (!method) + return NULL; + } + if (method) { + char *name = mono_method_full_name (method, TRUE); + g_print ("LAST AOT METHOD: %s.\n", name); + g_free (name); + } else { + g_print ("LAST AOT METHOD: %p %d\n", code, method_index); + } + } + } + + if (!(is_llvm_code (amodule, code) && (amodule->info.flags & MONO_AOT_FILE_FLAG_LLVM_ONLY)) || + (mono_llvm_only && method && method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED)) { + res = init_method (amodule, method_index, method, NULL, NULL, error); + if (!res) + goto cleanup; + } + + if (mono_trace_is_traced (G_LOG_LEVEL_DEBUG, MONO_TRACE_AOT)) { + char *full_name; + + if (!method) { + method = mono_get_method_checked (image, token, NULL, NULL, error); + if (!method) + return NULL; + } + + full_name = mono_method_full_name (method, TRUE); + + if (!jinfo) + jinfo = mono_aot_find_jit_info (domain, amodule->assembly->image, code); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_AOT, "AOT: FOUND method %s [%p - %p %p]", full_name, code, code + jinfo->code_size, info); + g_free (full_name); + } + + amodule_lock (amodule); + + init_plt (amodule); + + mono_atomic_inc_i32 (&mono_jit_stats.methods_aot); + + if (method && method->wrapper_type) + g_hash_table_insert (amodule->method_to_code, method, code); + + /* Commit changes since methods_loaded is accessed outside the lock */ + mono_memory_barrier (); + + amodule->methods_loaded [method_index / 32] |= 1 << (method_index % 32); + + amodule_unlock (amodule); + + if (MONO_PROFILER_ENABLED (jit_begin) || MONO_PROFILER_ENABLED (jit_done)) { + MonoJitInfo *jinfo; + + if (!method) { + method = mono_get_method_checked (amodule->assembly->image, token, NULL, NULL, error); + if (!method) + return NULL; + } + MONO_PROFILER_RAISE (jit_begin, (method)); + jinfo = mono_jit_info_table_find (domain, (char*)code); + g_assert (jinfo); + MONO_PROFILER_RAISE (jit_done, (method, jinfo)); + } + + return code; + + cleanup: + if (jinfo) + g_free (jinfo); + + return NULL; +} + +/** find_aot_method_in_amodule + * + * \param code_amodule The AOT module containing the code pointer + * \param method The method to find the code index for + * \param hash_full The hash for the method + */ +static guint32 +find_aot_method_in_amodule (MonoAotModule *code_amodule, MonoMethod *method, guint32 hash_full) +{ + MonoError error; + guint32 table_size, entry_size, hash; + guint32 *table, *entry; + guint32 index; + static guint32 n_extra_decodes; + + // The AOT module containing the MonoMethod + // The reference to the metadata amodule will differ among multiple dedup methods + // which mangle to the same name but live in different assemblies. This leads to + // the caching breaking. The solution seems to be to cache using the "metadata" amodule. + MonoAotModule *metadata_amodule = (MonoAotModule *)method->klass->image->aot_module; + + if (!metadata_amodule || metadata_amodule->out_of_date || !code_amodule || code_amodule->out_of_date) + return 0xffffff; + + table_size = code_amodule->extra_method_table [0]; + hash = hash_full % table_size; + table = code_amodule->extra_method_table + 1; + entry_size = 3; + + entry = &table [hash * entry_size]; + + if (entry [0] == 0) + return 0xffffff; + + index = 0xffffff; + while (TRUE) { + guint32 key = entry [0]; + guint32 value = entry [1]; + guint32 next = entry [entry_size - 1]; + MonoMethod *m; + guint8 *p, *orig_p; + + p = code_amodule->blob + key; + orig_p = p; + + amodule_lock (metadata_amodule); + if (!metadata_amodule->method_ref_to_method) + metadata_amodule->method_ref_to_method = g_hash_table_new (NULL, NULL); + m = (MonoMethod *)g_hash_table_lookup (metadata_amodule->method_ref_to_method, p); + amodule_unlock (metadata_amodule); + if (!m) { + m = decode_resolve_method_ref_with_target (code_amodule, method, p, &p, &error); + mono_error_cleanup (&error); /* FIXME don't swallow the error */ + /* + * Can't catche runtime invoke wrappers since it would break + * the check in decode_method_ref_with_target (). + */ + if (m && m->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE) { + amodule_lock (metadata_amodule); + g_hash_table_insert (metadata_amodule->method_ref_to_method, orig_p, m); + amodule_unlock (metadata_amodule); + } + } + if (m == method) { + index = value; + break; + } + + /* Methods decoded needlessly */ + if (m) { + //printf ("%d %s %s %p\n", n_extra_decodes, mono_method_full_name (method, TRUE), mono_method_full_name (m, TRUE), orig_p); + n_extra_decodes ++; + } + + if (next != 0) + entry = &table [next * entry_size]; + else + break; + } + + if (index != 0xffffff) + g_assert (index < code_amodule->info.nmethods); + + return index; +} + +static void +add_module_cb (gpointer key, gpointer value, gpointer user_data) +{ + g_ptr_array_add ((GPtrArray*)user_data, value); +} + +gboolean +mono_aot_can_dedup (MonoMethod *method) +{ + gboolean not_normal_gshared = method->is_inflated && !mono_method_is_generic_sharable_full (method, TRUE, FALSE, FALSE); + gboolean extra_method = (method->wrapper_type != MONO_WRAPPER_NONE) || not_normal_gshared; + + return extra_method; +} + + +/* + * find_aot_method: + * + * Try finding METHOD in the extra_method table in all AOT images. + * Return its method index, or 0xffffff if not found. Set OUT_AMODULE to the AOT + * module where the method was found. + */ +static guint32 +find_aot_method (MonoMethod *method, MonoAotModule **out_amodule) +{ + guint32 index; + GPtrArray *modules; + int i; + guint32 hash = mono_aot_method_hash (method); + + /* Try the place we expect to have moved the method only + * We don't probe, as that causes hard-to-debug issues when we fail + * to find the method */ + if (container_amodule && mono_aot_can_dedup (method)) { + *out_amodule = container_amodule; + index = find_aot_method_in_amodule (container_amodule, method, hash); + return index; + } + + /* Try the method's module first */ + *out_amodule = (MonoAotModule *)method->klass->image->aot_module; + index = find_aot_method_in_amodule ((MonoAotModule *)method->klass->image->aot_module, method, hash); + if (index != 0xffffff) + return index; + + /* + * Try all other modules. + * This is needed because generic instances klass->image points to the image + * containing the generic definition, but the native code is generated to the + * AOT image which contains the reference. + */ + + /* Make a copy to avoid doing the search inside the aot lock */ + modules = g_ptr_array_new (); + mono_aot_lock (); + g_hash_table_foreach (aot_modules, add_module_cb, modules); + mono_aot_unlock (); + + index = 0xffffff; + for (i = 0; i < modules->len; ++i) { + MonoAotModule *amodule = (MonoAotModule *)g_ptr_array_index (modules, i); + + if (amodule != method->klass->image->aot_module) + index = find_aot_method_in_amodule (amodule, method, hash); + if (index != 0xffffff) { + *out_amodule = amodule; + break; + } + } + + g_ptr_array_free (modules, TRUE); + + return index; +} + +guint32 +mono_aot_find_method_index (MonoMethod *method) +{ + MonoAotModule *out_amodule; + return find_aot_method (method, &out_amodule); +} + +static gboolean +init_method (MonoAotModule *amodule, guint32 method_index, MonoMethod *method, MonoClass *init_class, MonoGenericContext *context, MonoError *error) +{ + MonoDomain *domain = mono_domain_get (); + MonoMemPool *mp; + MonoClass *klass_to_run_ctor = NULL; + gboolean from_plt = method == NULL; + int pindex, n_patches; + guint8 *p; + MonoJitInfo *jinfo = NULL; + guint8 *code, *info; + + error_init (error); + + code = (guint8 *)amodule->methods [method_index]; + info = &amodule->blob [mono_aot_get_offset (amodule->method_info_offsets, method_index)]; + + p = info; + + //does the method's class has a cctor? + if (decode_value (p, &p) == 1) + klass_to_run_ctor = decode_klass_ref (amodule, p, &p, error); + if (!is_ok (error)) + return FALSE; + + //FIXME old code would use the class from @method if not null and ignore the one encoded. I don't know if we need to honor that -- @kumpera + if (method) + klass_to_run_ctor = method->klass; + + n_patches = decode_value (p, &p); + + if (n_patches) { + MonoJumpInfo *patches; + guint32 *got_slots; + gboolean llvm; + gpointer *got; + + mp = mono_mempool_new (); + + if ((gpointer)code >= amodule->info.jit_code_start && (gpointer)code <= amodule->info.jit_code_end) { + llvm = FALSE; + got = amodule->got; + } else { + llvm = TRUE; + got = amodule->llvm_got; + g_assert (got); + } + + patches = load_patch_info (amodule, mp, n_patches, llvm, &got_slots, p, &p); + if (patches == NULL) { + mono_mempool_destroy (mp); + goto cleanup; + } + + for (pindex = 0; pindex < n_patches; ++pindex) { + MonoJumpInfo *ji = &patches [pindex]; + gpointer addr; + + /* + * For SFLDA, we need to call resolve_patch_target () since the GOT slot could have + * been initialized by load_method () for a static cctor before the cctor has + * finished executing (#23242). + */ + if (!got [got_slots [pindex]] || ji->type == MONO_PATCH_INFO_SFLDA) { + /* In llvm-only made, we might encounter shared methods */ + if (mono_llvm_only && ji->type == MONO_PATCH_INFO_METHOD && mono_method_check_context_used (ji->data.method)) { + g_assert (context); + ji->data.method = mono_class_inflate_generic_method_checked (ji->data.method, context, error); + if (!mono_error_ok (error)) { + g_free (got_slots); + mono_mempool_destroy (mp); + return FALSE; + } + } + /* This cannot be resolved in mono_resolve_patch_target () */ + if (ji->type == MONO_PATCH_INFO_AOT_JIT_INFO) { + // FIXME: Lookup using the index + jinfo = mono_aot_find_jit_info (domain, amodule->assembly->image, code); + ji->type = MONO_PATCH_INFO_ABS; + ji->data.target = jinfo; + } + addr = mono_resolve_patch_target (method, domain, code, ji, TRUE, error); + if (!mono_error_ok (error)) { + g_free (got_slots); + mono_mempool_destroy (mp); + return FALSE; + } + if (ji->type == MONO_PATCH_INFO_METHOD_JUMP) + addr = mono_create_ftnptr (domain, addr); + mono_memory_barrier (); + got [got_slots [pindex]] = addr; + if (ji->type == MONO_PATCH_INFO_METHOD_JUMP) + register_jump_target_got_slot (domain, ji->data.method, &(got [got_slots [pindex]])); + } + ji->type = MONO_PATCH_INFO_NONE; + } + + g_free (got_slots); + + mono_mempool_destroy (mp); + } + + if (mini_get_debug_options ()->load_aot_jit_info_eagerly) + jinfo = mono_aot_find_jit_info (domain, amodule->assembly->image, code); + + gboolean inited_ok = TRUE; + if (init_class) + inited_ok = mono_runtime_class_init_full (mono_class_vtable (domain, init_class), error); + else if (from_plt && klass_to_run_ctor && !mono_class_is_gtd (klass_to_run_ctor)) + inited_ok = mono_runtime_class_init_full (mono_class_vtable (domain, klass_to_run_ctor), error); + if (!inited_ok) + return FALSE; + + return TRUE; + + cleanup: + if (jinfo) + g_free (jinfo); + + return FALSE; +} + +static void +init_llvmonly_method (MonoAotModule *amodule, guint32 method_index, MonoMethod *method, MonoClass *init_class, MonoGenericContext *context) +{ + gboolean res; + MonoError error; + + res = init_method (amodule, method_index, method, init_class, context, &error); + if (!is_ok (&error)) { + MonoException *ex = mono_error_convert_to_exception (&error); + /* Its okay to raise in llvmonly mode */ + if (ex) + mono_llvm_throw_exception ((MonoObject*)ex); + } +} + +void +mono_aot_init_llvm_method (gpointer aot_module, guint32 method_index) +{ + MonoAotModule *amodule = (MonoAotModule *)aot_module; + + init_llvmonly_method (amodule, method_index, NULL, NULL, NULL); +} + +void +mono_aot_init_gshared_method_this (gpointer aot_module, guint32 method_index, MonoObject *this_obj) +{ + MonoAotModule *amodule = (MonoAotModule *)aot_module; + MonoClass *klass; + MonoGenericContext *context; + MonoMethod *method; + + // FIXME: + g_assert (this_obj); + klass = this_obj->vtable->klass; + + amodule_lock (amodule); + method = (MonoMethod *)g_hash_table_lookup (amodule->extra_methods, GUINT_TO_POINTER (method_index)); + amodule_unlock (amodule); + + g_assert (method); + context = mono_method_get_context (method); + g_assert (context); + + init_llvmonly_method (amodule, method_index, NULL, klass, context); +} + +void +mono_aot_init_gshared_method_mrgctx (gpointer aot_module, guint32 method_index, MonoMethodRuntimeGenericContext *rgctx) +{ + MonoAotModule *amodule = (MonoAotModule *)aot_module; + MonoGenericContext context = { NULL, NULL }; + MonoClass *klass = rgctx->class_vtable->klass; + + if (mono_class_is_ginst (klass)) + context.class_inst = mono_class_get_generic_class (klass)->context.class_inst; + else if (mono_class_is_gtd (klass)) + context.class_inst = mono_class_get_generic_container (klass)->context.class_inst; + context.method_inst = rgctx->method_inst; + + init_llvmonly_method (amodule, method_index, NULL, rgctx->class_vtable->klass, &context); +} + +void +mono_aot_init_gshared_method_vtable (gpointer aot_module, guint32 method_index, MonoVTable *vtable) +{ + MonoAotModule *amodule = (MonoAotModule *)aot_module; + MonoClass *klass; + MonoGenericContext *context; + MonoMethod *method; + + klass = vtable->klass; + + amodule_lock (amodule); + method = (MonoMethod *)g_hash_table_lookup (amodule->extra_methods, GUINT_TO_POINTER (method_index)); + amodule_unlock (amodule); + + g_assert (method); + context = mono_method_get_context (method); + g_assert (context); + + init_llvmonly_method (amodule, method_index, NULL, klass, context); +} + +/* + * mono_aot_get_method_checked: + * + * Return a pointer to the AOTed native code for METHOD if it can be found, + * NULL otherwise. + * On platforms with function pointers, this doesn't return a function pointer. + */ +gpointer +mono_aot_get_method_checked (MonoDomain *domain, MonoMethod *method, MonoError *error) +{ + MonoClass *klass = method->klass; + MonoMethod *orig_method = method; + guint32 method_index; + MonoAotModule *amodule = (MonoAotModule *)klass->image->aot_module; + guint8 *code; + gboolean cache_result = FALSE; + MonoError inner_error; + + error_init (error); + + if (domain != mono_get_root_domain ()) + /* Non shared AOT code can't be used in other appdomains */ + return NULL; + + if (enable_aot_cache && !amodule && domain->entry_assembly && klass->image == mono_defaults.corlib) { + /* This cannot be AOTed during startup, so do it now */ + if (!mscorlib_aot_loaded) { + mscorlib_aot_loaded = TRUE; + load_aot_module (klass->image->assembly, NULL); + amodule = (MonoAotModule *)klass->image->aot_module; + } + } + + if (!amodule) + return NULL; + + if (amodule->out_of_date) + return NULL; + + if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) || + (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) || + (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) || + (method->flags & METHOD_ATTRIBUTE_ABSTRACT)) + return NULL; + + /* + * Use the original method instead of its invoke-with-check wrapper. + * This is not a problem when using full-aot, since it doesn't support + * remoting. + */ + if (mono_aot_only && method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK) + return mono_aot_get_method_checked (domain, mono_marshal_method_from_wrapper (method), error); + + g_assert (klass->inited); + + /* Find method index */ + method_index = 0xffffff; + + gboolean dedupable = mono_aot_can_dedup (method); + + if (method->is_inflated && !method->wrapper_type && mono_method_is_generic_sharable_full (method, TRUE, FALSE, FALSE) && !dedupable) { + MonoMethod *orig_method = method; + /* + * For generic methods, we store the fully shared instance in place of the + * original method. + */ + method = mono_method_get_declaring_generic_method (method); + method_index = mono_metadata_token_index (method->token) - 1; + + if (mono_llvm_only) { + /* Needed by mono_aot_init_gshared_method_this () */ + /* orig_method is a random instance but it is enough to make init_method () work */ + amodule_lock (amodule); + g_hash_table_insert (amodule->extra_methods, GUINT_TO_POINTER (method_index), orig_method); + amodule_unlock (amodule); + } + } + + if (method_index == 0xffffff && (method->is_inflated || !method->token)) { + /* This hash table is used to avoid the slower search in the extra_method_table in the AOT image */ + amodule_lock (amodule); + code = (guint8 *)g_hash_table_lookup (amodule->method_to_code, method); + amodule_unlock (amodule); + if (code) + return code; + + cache_result = TRUE; + if (method_index == 0xffffff) + method_index = find_aot_method (method, &amodule); + + /* + * Special case the ICollection wrappers for arrays, as they cannot + * be statically enumerated, and each wrapper ends up calling the same + * method in Array. + */ + if (method_index == 0xffffff && method->wrapper_type == MONO_WRAPPER_MANAGED_TO_MANAGED && method->klass->rank && strstr (method->name, "System.Collections.Generic")) { + MonoMethod *m = mono_aot_get_array_helper_from_wrapper (method); + + code = (guint8 *)mono_aot_get_method_checked (domain, m, &inner_error); + mono_error_cleanup (&inner_error); + if (code) + return code; + } + + /* + * Special case Array.GetGenericValueImpl which is a generic icall. + * Generic sharing currently can't handle it, but the icall returns data using + * an out parameter, so the managed-to-native wrappers can share the same code. + */ + if (method_index == 0xffffff && method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE && method->klass == mono_defaults.array_class && !strcmp (method->name, "GetGenericValueImpl")) { + MonoMethod *m; + MonoGenericContext ctx; + MonoType *args [16]; + + if (mono_method_signature (method)->params [1]->type == MONO_TYPE_OBJECT) + /* Avoid recursion */ + return NULL; + + m = mono_class_get_method_from_name (mono_defaults.array_class, "GetGenericValueImpl", 2); + g_assert (m); + + memset (&ctx, 0, sizeof (ctx)); + args [0] = &mono_defaults.object_class->byval_arg; + ctx.method_inst = mono_metadata_get_generic_inst (1, args); + + m = mono_marshal_get_native_wrapper (mono_class_inflate_generic_method_checked (m, &ctx, error), TRUE, TRUE); + if (!m) + g_error ("AOT runtime could not load method due to %s", mono_error_get_message (error)); /* FIXME don't swallow the error */ + + /* + * Get the code for the instantiation which should be emitted into + * the mscorlib aot image by the AOT compiler. + */ + code = (guint8 *)mono_aot_get_method_checked (domain, m, &inner_error); + mono_error_cleanup (&inner_error); + if (code) + return code; + } + + /* Same for CompareExchange and Exchange */ + /* Same for Volatile.Read/Write */ + if (method_index == 0xffffff && method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE && method->klass->image == mono_defaults.corlib && + ((!strcmp (method->klass->name_space, "System.Threading") && !strcmp (method->klass->name, "Interlocked") && (!strcmp (method->name, "CompareExchange") || !strcmp (method->name, "Exchange")) && MONO_TYPE_IS_REFERENCE (mini_type_get_underlying_type (mono_method_signature (method)->params [1]))) || + (!strcmp (method->klass->name_space, "System.Threading") && !strcmp (method->klass->name, "Volatile") && (!strcmp (method->name, "Read") && MONO_TYPE_IS_REFERENCE (mini_type_get_underlying_type (mono_method_signature (method)->ret)))) || + (!strcmp (method->klass->name_space, "System.Threading") && !strcmp (method->klass->name, "Volatile") && (!strcmp (method->name, "Write") && MONO_TYPE_IS_REFERENCE (mini_type_get_underlying_type (mono_method_signature (method)->params [1])))))) { + MonoMethod *m; + MonoGenericContext ctx; + MonoType *args [16]; + gpointer iter = NULL; + + while ((m = mono_class_get_methods (method->klass, &iter))) { + if (mono_method_signature (m)->generic_param_count && !strcmp (m->name, method->name)) + break; + } + g_assert (m); + + memset (&ctx, 0, sizeof (ctx)); + args [0] = &mono_defaults.object_class->byval_arg; + ctx.method_inst = mono_metadata_get_generic_inst (1, args); + + m = mono_marshal_get_native_wrapper (mono_class_inflate_generic_method_checked (m, &ctx, error), TRUE, TRUE); + if (!m) + g_error ("AOT runtime could not load method due to %s", mono_error_get_message (error)); /* FIXME don't swallow the error */ + + /* Avoid recursion */ + if (method == m) + return NULL; + + /* + * Get the code for the instantiation which should be emitted into + * the mscorlib aot image by the AOT compiler. + */ + code = (guint8 *)mono_aot_get_method_checked (domain, m, &inner_error); + mono_error_cleanup (&inner_error); + if (code) + return code; + } + + /* For ARRAY_ACCESSOR wrappers with reference types, use the instantiation saved in corlib */ + if (method_index == 0xffffff && method->wrapper_type == MONO_WRAPPER_UNKNOWN) { + WrapperInfo *info = mono_marshal_get_wrapper_info (method); + + if (info->subtype == WRAPPER_SUBTYPE_ARRAY_ACCESSOR) { + MonoMethod *array_method = info->d.array_accessor.method; + if (MONO_TYPE_IS_REFERENCE (&array_method->klass->element_class->byval_arg)) { + int rank; + + if (!strcmp (array_method->name, "Set")) + rank = mono_method_signature (array_method)->param_count - 1; + else if (!strcmp (array_method->name, "Get") || !strcmp (array_method->name, "Address")) + rank = mono_method_signature (array_method)->param_count; + else + g_assert_not_reached (); + MonoClass *obj_array_class = mono_array_class_get (mono_defaults.object_class, rank); + MonoMethod *m = mono_class_get_method_from_name (obj_array_class, array_method->name, mono_method_signature (array_method)->param_count); + g_assert (m); + + m = mono_marshal_get_array_accessor_wrapper (m); + if (m != method) { + code = (guint8 *)mono_aot_get_method_checked (domain, m, &inner_error); + mono_error_cleanup (&inner_error); + if (code) + return code; + } + } + } + } + + if (method_index == 0xffffff && method->is_inflated && mono_method_is_generic_sharable_full (method, FALSE, TRUE, FALSE)) { + /* Partial sharing */ + MonoMethod *shared; + + shared = mini_get_shared_method (method); + method_index = find_aot_method (shared, &amodule); + if (method_index != 0xffffff) + method = shared; + } + + if (method_index == 0xffffff && method->is_inflated && mono_method_is_generic_sharable_full (method, FALSE, FALSE, TRUE)) { + MonoMethod *shared; + /* gsharedvt */ + /* Use the all-vt shared method since this is what was AOTed */ + shared = mini_get_shared_method_full (method, TRUE, TRUE); + method_index = find_aot_method (shared, &amodule); + if (method_index != 0xffffff) + method = mini_get_shared_method_full (method, TRUE, FALSE); + } + + if (method_index == 0xffffff) { + if (mono_trace_is_traced (G_LOG_LEVEL_DEBUG, MONO_TRACE_AOT)) { + char *full_name; + + full_name = mono_method_full_name (method, TRUE); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_AOT, "AOT NOT FOUND: %s.", full_name); + g_free (full_name); + } + return NULL; + } + + if (method_index == 0xffffff) + return NULL; + + /* Needed by find_jit_info */ + amodule_lock (amodule); + g_hash_table_insert (amodule->extra_methods, GUINT_TO_POINTER (method_index), method); + amodule_unlock (amodule); + } else { + /* Common case */ + method_index = mono_metadata_token_index (method->token) - 1; + } + + code = (guint8 *)load_method (domain, amodule, klass->image, method, method->token, method_index, error); + if (!is_ok (error)) + return NULL; + if (code && cache_result) { + amodule_lock (amodule); + g_hash_table_insert (amodule->method_to_code, orig_method, code); + amodule_unlock (amodule); + } + return code; +} + +/** + * Same as mono_aot_get_method, but we try to avoid loading any metadata from the + * method. + */ +gpointer +mono_aot_get_method_from_token (MonoDomain *domain, MonoImage *image, guint32 token, MonoError *error) +{ + MonoAotModule *aot_module = (MonoAotModule *)image->aot_module; + int method_index; + gpointer res; + + error_init (error); + + if (!aot_module) + return NULL; + + method_index = mono_metadata_token_index (token) - 1; + + res = load_method (domain, aot_module, image, NULL, token, method_index, error); + return res; +} + +typedef struct { + guint8 *addr; + gboolean res; +} IsGotEntryUserData; + +static void +check_is_got_entry (gpointer key, gpointer value, gpointer user_data) +{ + IsGotEntryUserData *data = (IsGotEntryUserData*)user_data; + MonoAotModule *aot_module = (MonoAotModule*)value; + + if (aot_module->got && (data->addr >= (guint8*)(aot_module->got)) && (data->addr < (guint8*)(aot_module->got + aot_module->info.got_size))) + data->res = TRUE; +} + +gboolean +mono_aot_is_got_entry (guint8 *code, guint8 *addr) +{ + IsGotEntryUserData user_data; + + if (!aot_modules) + return FALSE; + + user_data.addr = addr; + user_data.res = FALSE; + mono_aot_lock (); + g_hash_table_foreach (aot_modules, check_is_got_entry, &user_data); + mono_aot_unlock (); + + return user_data.res; +} + +typedef struct { + guint8 *addr; + MonoAotModule *module; +} FindAotModuleUserData; + +static void +find_aot_module_cb (gpointer key, gpointer value, gpointer user_data) +{ + FindAotModuleUserData *data = (FindAotModuleUserData*)user_data; + MonoAotModule *aot_module = (MonoAotModule*)value; + + if (amodule_contains_code_addr (aot_module, data->addr)) + data->module = aot_module; +} + +static inline MonoAotModule* +find_aot_module (guint8 *code) +{ + FindAotModuleUserData user_data; + + if (!aot_modules) + return NULL; + + /* Reading these need no locking */ + if (((gsize)code < aot_code_low_addr) || ((gsize)code > aot_code_high_addr)) + return NULL; + + user_data.addr = code; + user_data.module = NULL; + + mono_aot_lock (); + g_hash_table_foreach (aot_modules, find_aot_module_cb, &user_data); + mono_aot_unlock (); + + return user_data.module; +} + +void +mono_aot_patch_plt_entry (guint8 *code, guint8 *plt_entry, gpointer *got, mgreg_t *regs, guint8 *addr) +{ + MonoAotModule *amodule; + + /* + * Since AOT code is only used in the root domain, + * mono_domain_get () != mono_get_root_domain () means the calling method + * is AppDomain:InvokeInDomain, so this is the same check as in + * mono_method_same_domain () but without loading the metadata for the method. + */ + if (mono_domain_get () == mono_get_root_domain ()) { + if (!got) { + amodule = find_aot_module (code); + if (amodule) + got = amodule->got; + } + mono_arch_patch_plt_entry (plt_entry, got, regs, addr); + } +} + +/* + * mono_aot_plt_resolve: + * + * This function is called by the entries in the PLT to resolve the actual method that + * needs to be called. It returns a trampoline to the method and patches the PLT entry. + * Returns NULL if the something cannot be loaded. + */ +gpointer +mono_aot_plt_resolve (gpointer aot_module, guint32 plt_info_offset, guint8 *code, MonoError *error) +{ +#ifdef MONO_ARCH_AOT_SUPPORTED + guint8 *p, *target, *plt_entry; + MonoJumpInfo ji; + MonoAotModule *module = (MonoAotModule*)aot_module; + gboolean res, no_ftnptr = FALSE; + MonoMemPool *mp; + gboolean using_gsharedvt = FALSE; + + error_init (error); + + //printf ("DYN: %p %d\n", aot_module, plt_info_offset); + + p = &module->blob [plt_info_offset]; + + ji.type = (MonoJumpInfoType)decode_value (p, &p); + + mp = mono_mempool_new (); + res = decode_patch (module, mp, &ji, p, &p); + + if (!res) { + mono_mempool_destroy (mp); + return NULL; + } + +#ifdef MONO_ARCH_GSHAREDVT_SUPPORTED + using_gsharedvt = TRUE; +#endif + + /* + * Avoid calling resolve_patch_target in the full-aot case if possible, since + * it would create a trampoline, and we don't need that. + * We could do this only if the method does not need the special handling + * in mono_magic_trampoline (). + */ + if (mono_aot_only && ji.type == MONO_PATCH_INFO_METHOD && !ji.data.method->is_generic && !mono_method_check_context_used (ji.data.method) && !(ji.data.method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) && + !mono_method_needs_static_rgctx_invoke (ji.data.method, FALSE) && !using_gsharedvt) { + target = (guint8 *)mono_jit_compile_method (ji.data.method, error); + if (!mono_error_ok (error)) { + mono_mempool_destroy (mp); + return NULL; + } + no_ftnptr = TRUE; + } else { + target = (guint8 *)mono_resolve_patch_target (NULL, mono_domain_get (), NULL, &ji, TRUE, error); + if (!mono_error_ok (error)) { + mono_mempool_destroy (mp); + return NULL; + } + } + + /* + * The trampoline expects us to return a function descriptor on platforms which use + * it, but resolve_patch_target returns a direct function pointer for some type of + * patches, so have to translate between the two. + * FIXME: Clean this up, but how ? + */ + if (ji.type == MONO_PATCH_INFO_ABS || ji.type == MONO_PATCH_INFO_INTERNAL_METHOD || ji.type == MONO_PATCH_INFO_ICALL_ADDR || ji.type == MONO_PATCH_INFO_JIT_ICALL_ADDR || ji.type == MONO_PATCH_INFO_RGCTX_FETCH) { + /* These should already have a function descriptor */ +#ifdef PPC_USES_FUNCTION_DESCRIPTOR + /* Our function descriptors have a 0 environment, gcc created ones don't */ + if (ji.type != MONO_PATCH_INFO_INTERNAL_METHOD && ji.type != MONO_PATCH_INFO_JIT_ICALL_ADDR && ji.type != MONO_PATCH_INFO_ICALL_ADDR) + g_assert (((gpointer*)target) [2] == 0); +#endif + /* Empty */ + } else if (!no_ftnptr) { +#ifdef PPC_USES_FUNCTION_DESCRIPTOR + g_assert (((gpointer*)target) [2] != 0); +#endif + target = (guint8 *)mono_create_ftnptr (mono_domain_get (), target); + } + + mono_mempool_destroy (mp); + + /* Patch the PLT entry with target which might be the actual method not a trampoline */ + plt_entry = mono_aot_get_plt_entry (code); + g_assert (plt_entry); + mono_aot_patch_plt_entry (code, plt_entry, module->got, NULL, target); + + return target; +#else + g_assert_not_reached (); + return NULL; +#endif +} + +/** + * init_plt: + * + * Initialize the PLT table of the AOT module. Called lazily when the first AOT + * method in the module is loaded to avoid committing memory by writing to it. + * LOCKING: Assumes the AMODULE lock is held. + */ +static void +init_plt (MonoAotModule *amodule) +{ + int i; + gpointer tramp; + + if (amodule->plt_inited) + return; + + if (amodule->info.plt_size <= 1) { + amodule->plt_inited = TRUE; + return; + } + + tramp = mono_create_specific_trampoline (amodule, MONO_TRAMPOLINE_AOT_PLT, mono_get_root_domain (), NULL); + + /* + * Initialize the PLT entries in the GOT to point to the default targets. + */ + + tramp = mono_create_ftnptr (mono_domain_get (), tramp); + for (i = 1; i < amodule->info.plt_size; ++i) + /* All the default entries point to the AOT trampoline */ + ((gpointer*)amodule->got)[amodule->info.plt_got_offset_base + i] = tramp; + + amodule->plt_inited = TRUE; +} + +/* + * mono_aot_get_plt_entry: + * + * Return the address of the PLT entry called by the code at CODE if exists. + */ +guint8* +mono_aot_get_plt_entry (guint8 *code) +{ + MonoAotModule *amodule = find_aot_module (code); + guint8 *target = NULL; + + if (!amodule) + return NULL; + +#ifdef TARGET_ARM + if (is_thumb_code (amodule, code - 4)) + return mono_arm_get_thumb_plt_entry (code); +#endif + +#ifdef MONO_ARCH_AOT_SUPPORTED + target = mono_arch_get_call_target (code); +#else + g_assert_not_reached (); +#endif + +#ifdef MONOTOUCH + while (target != NULL) { + if ((target >= (guint8*)(amodule->plt)) && (target < (guint8*)(amodule->plt_end))) + return target; + + // Add 4 since mono_arch_get_call_target assumes we're passing + // the instruction after the actual branch instruction. + target = mono_arch_get_call_target (target + 4); + } + + return NULL; +#else + if ((target >= (guint8*)(amodule->plt)) && (target < (guint8*)(amodule->plt_end))) + return target; + else + return NULL; +#endif +} + +/* + * mono_aot_get_plt_info_offset: + * + * Return the PLT info offset belonging to the plt entry called by CODE. + */ +guint32 +mono_aot_get_plt_info_offset (mgreg_t *regs, guint8 *code) +{ + guint8 *plt_entry = mono_aot_get_plt_entry (code); + + g_assert (plt_entry); + + /* The offset is embedded inside the code after the plt entry */ +#ifdef MONO_ARCH_AOT_SUPPORTED + return mono_arch_get_plt_info_offset (plt_entry, regs, code); +#else + g_assert_not_reached (); + return 0; +#endif +} + +static gpointer +mono_create_ftnptr_malloc (guint8 *code) +{ +#ifdef PPC_USES_FUNCTION_DESCRIPTOR + MonoPPCFunctionDescriptor *ftnptr = g_malloc0 (sizeof (MonoPPCFunctionDescriptor)); + + ftnptr->code = code; + ftnptr->toc = NULL; + ftnptr->env = NULL; + + return ftnptr; +#else + return code; +#endif +} + +/* + * mono_aot_register_jit_icall: + * + * Register a JIT icall which is called by trampolines in full-aot mode. This should + * be called from mono_arch_init () during startup. + */ +void +mono_aot_register_jit_icall (const char *name, gpointer addr) +{ + /* No need for locking */ + if (!aot_jit_icall_hash) + aot_jit_icall_hash = g_hash_table_new (g_str_hash, g_str_equal); + g_hash_table_insert (aot_jit_icall_hash, (char*)name, addr); +} + +/* + * load_function_full: + * + * Load the function named NAME from the aot image. + */ +static gpointer +load_function_full (MonoAotModule *amodule, const char *name, MonoTrampInfo **out_tinfo) +{ + char *symbol; + guint8 *p; + int n_patches, pindex; + MonoMemPool *mp; + gpointer code; + guint32 info_offset; + + /* Load the code */ + + symbol = g_strdup_printf ("%s", name); + find_amodule_symbol (amodule, symbol, (gpointer *)&code); + g_free (symbol); + if (!code) + g_error ("Symbol '%s' not found in AOT file '%s'.\n", name, amodule->aot_name); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_AOT, "AOT: FOUND function '%s' in AOT file '%s'.", name, amodule->aot_name); + + /* Load info */ + + symbol = g_strdup_printf ("%s_p", name); + find_amodule_symbol (amodule, symbol, (gpointer *)&p); + g_free (symbol); + if (!p) + /* Nothing to patch */ + return code; + + info_offset = *(guint32*)p; + if (out_tinfo) { + MonoTrampInfo *tinfo; + guint32 code_size, uw_info_len, uw_offset; + guint8 *uw_info; + /* Construct a MonoTrampInfo from the data in the AOT image */ + + p += sizeof (guint32); + code_size = *(guint32*)p; + p += sizeof (guint32); + uw_offset = *(guint32*)p; + uw_info = amodule->unwind_info + uw_offset; + uw_info_len = decode_value (uw_info, &uw_info); + + tinfo = g_new0 (MonoTrampInfo, 1); + tinfo->code = (guint8 *)code; + tinfo->code_size = code_size; + tinfo->uw_info_len = uw_info_len; + if (uw_info_len) + tinfo->uw_info = uw_info; + + *out_tinfo = tinfo; + } + + p = amodule->blob + info_offset; + + /* Similar to mono_aot_load_method () */ + + n_patches = decode_value (p, &p); + + if (n_patches) { + MonoJumpInfo *patches; + guint32 *got_slots; + + mp = mono_mempool_new (); + + patches = load_patch_info (amodule, mp, n_patches, FALSE, &got_slots, p, &p); + g_assert (patches); + + for (pindex = 0; pindex < n_patches; ++pindex) { + MonoJumpInfo *ji = &patches [pindex]; + MonoError error; + gpointer target; + + if (amodule->got [got_slots [pindex]]) + continue; + + /* + * When this code is executed, the runtime may not be initalized yet, so + * resolve the patch info by hand. + */ + if (ji->type == MONO_PATCH_INFO_JIT_ICALL_ADDR) { + if (!strcmp (ji->data.name, "mono_get_lmf_addr")) { + target = mono_get_lmf_addr; + } else if (!strcmp (ji->data.name, "mono_thread_force_interruption_checkpoint_noraise")) { + target = mono_thread_force_interruption_checkpoint_noraise; + } else if (!strcmp (ji->data.name, "mono_exception_from_token")) { + target = mono_exception_from_token; + } else if (!strcmp (ji->data.name, "mono_throw_exception")) { + target = mono_get_throw_exception (); + } else if (strstr (ji->data.name, "trampoline_func_") == ji->data.name) { + MonoTrampolineType tramp_type2 = (MonoTrampolineType)atoi (ji->data.name + strlen ("trampoline_func_")); + target = (gpointer)mono_get_trampoline_func (tramp_type2); + } else if (strstr (ji->data.name, "specific_trampoline_lazy_fetch_") == ji->data.name) { + /* atoll is needed because the the offset is unsigned */ + guint32 slot; + int res; + + res = sscanf (ji->data.name, "specific_trampoline_lazy_fetch_%u", &slot); + g_assert (res == 1); + target = mono_create_specific_trampoline (GUINT_TO_POINTER (slot), MONO_TRAMPOLINE_RGCTX_LAZY_FETCH, mono_get_root_domain (), NULL); + target = mono_create_ftnptr_malloc ((guint8 *)target); + } else if (!strcmp (ji->data.name, "debugger_agent_single_step_from_context")) { + target = debugger_agent_single_step_from_context; + } else if (!strcmp (ji->data.name, "debugger_agent_breakpoint_from_context")) { + target = debugger_agent_breakpoint_from_context; + } else if (!strcmp (ji->data.name, "throw_exception_addr")) { + target = mono_get_throw_exception_addr (); + } else if (strstr (ji->data.name, "generic_trampoline_")) { + target = mono_aot_get_trampoline (ji->data.name); + } else if (aot_jit_icall_hash && g_hash_table_lookup (aot_jit_icall_hash, ji->data.name)) { + /* Registered by mono_arch_init () */ + target = g_hash_table_lookup (aot_jit_icall_hash, ji->data.name); + } else { + fprintf (stderr, "Unknown relocation '%s'\n", ji->data.name); + g_assert_not_reached (); + target = NULL; + } + } else { + /* Hopefully the code doesn't have patches which need method or + * domain to be set. + */ + target = mono_resolve_patch_target (NULL, NULL, (guint8 *)code, ji, FALSE, &error); + mono_error_assert_ok (&error); + g_assert (target); + } + + amodule->got [got_slots [pindex]] = target; + } + + g_free (got_slots); + + mono_mempool_destroy (mp); + } + + return code; +} + +static gpointer +load_function (MonoAotModule *amodule, const char *name) +{ + return load_function_full (amodule, name, NULL); +} + +static MonoAotModule* +get_mscorlib_aot_module (void) +{ + MonoImage *image; + MonoAotModule *amodule; + + image = mono_defaults.corlib; + if (image) + amodule = (MonoAotModule *)image->aot_module; + else + amodule = mscorlib_aot_module; + g_assert (amodule); + return amodule; +} + +static void +no_trampolines (void) +{ + g_assert_not_reached (); +} + + +#ifndef TARGET_WASM +/* + * Return the trampoline identified by NAME from the mscorlib AOT file. + * On ppc64, this returns a function descriptor. + */ +gpointer +mono_aot_get_trampoline_full (const char *name, MonoTrampInfo **out_tinfo) +{ + MonoAotModule *amodule = get_mscorlib_aot_module (); + + if (mono_llvm_only) { + *out_tinfo = NULL; + return no_trampolines; + } + + return mono_create_ftnptr_malloc ((guint8 *)load_function_full (amodule, name, out_tinfo)); +} +#endif + + +gpointer +mono_aot_get_trampoline (const char *name) +{ + MonoTrampInfo *out_tinfo; + gpointer code; + + code = mono_aot_get_trampoline_full (name, &out_tinfo); + mono_aot_tramp_info_register (out_tinfo, NULL); + + return code; +} + +static gpointer +read_unwind_info (MonoAotModule *amodule, MonoTrampInfo *info, const char *symbol_name) +{ + gpointer symbol_addr; + guint32 uw_offset, uw_info_len; + guint8 *uw_info; + + find_amodule_symbol (amodule, symbol_name, &symbol_addr); + + if (!symbol_addr) + return NULL; + + uw_offset = *(guint32*)symbol_addr; + uw_info = amodule->unwind_info + uw_offset; + uw_info_len = decode_value (uw_info, &uw_info); + + info->uw_info_len = uw_info_len; + if (uw_info_len) + info->uw_info = uw_info; + else + info->uw_info = NULL; + + /* If successful return the address of the following data */ + return (guint32*)symbol_addr + 1; +} + +#ifdef MONOTOUCH +#include + +static TrampolinePage* trampoline_pages [MONO_AOT_TRAMP_NUM]; + +static void +read_page_trampoline_uwinfo (MonoTrampInfo *info, int tramp_type, gboolean is_generic) +{ + char symbol_name [128]; + + if (tramp_type == MONO_AOT_TRAMP_SPECIFIC) + sprintf (symbol_name, "specific_trampolines_page_%s_p", is_generic ? "gen" : "sp"); + else if (tramp_type == MONO_AOT_TRAMP_STATIC_RGCTX) + sprintf (symbol_name, "rgctx_trampolines_page_%s_p", is_generic ? "gen" : "sp"); + else if (tramp_type == MONO_AOT_TRAMP_IMT) + sprintf (symbol_name, "imt_trampolines_page_%s_p", is_generic ? "gen" : "sp"); + else if (tramp_type == MONO_AOT_TRAMP_GSHAREDVT_ARG) + sprintf (symbol_name, "gsharedvt_trampolines_page_%s_p", is_generic ? "gen" : "sp"); + else + g_assert_not_reached (); + + read_unwind_info (mono_defaults.corlib->aot_module, info, symbol_name); +} + +static unsigned char* +get_new_trampoline_from_page (int tramp_type) +{ + MonoAotModule *amodule; + MonoImage *image; + TrampolinePage *page; + int count; + void *tpage; + vm_address_t addr, taddr; + kern_return_t ret; + vm_prot_t prot, max_prot; + int psize, specific_trampoline_size; + unsigned char *code; + + specific_trampoline_size = 2 * sizeof (gpointer); + + mono_aot_page_lock (); + page = trampoline_pages [tramp_type]; + if (page && page->trampolines < page->trampolines_end) { + code = page->trampolines; + page->trampolines += specific_trampoline_size; + mono_aot_page_unlock (); + return code; + } + mono_aot_page_unlock (); + /* the trampoline template page is in the mscorlib module */ + image = mono_defaults.corlib; + g_assert (image); + + psize = MONO_AOT_TRAMP_PAGE_SIZE; + + amodule = image->aot_module; + g_assert (amodule); + + if (tramp_type == MONO_AOT_TRAMP_SPECIFIC) + tpage = load_function (amodule, "specific_trampolines_page"); + else if (tramp_type == MONO_AOT_TRAMP_STATIC_RGCTX) + tpage = load_function (amodule, "rgctx_trampolines_page"); + else if (tramp_type == MONO_AOT_TRAMP_IMT) + tpage = load_function (amodule, "imt_trampolines_page"); + else if (tramp_type == MONO_AOT_TRAMP_GSHAREDVT_ARG) + tpage = load_function (amodule, "gsharedvt_arg_trampolines_page"); + else + g_error ("Incorrect tramp type for trampolines page"); + g_assert (tpage); + /*g_warning ("loaded trampolines page at %x", tpage);*/ + + /* avoid the unlikely case of looping forever */ + count = 40; + page = NULL; + while (page == NULL && count-- > 0) { + MonoTrampInfo *gen_info, *sp_info; + + addr = 0; + /* allocate two contiguous pages of memory: the first page will contain the data (like a local constant pool) + * while the second will contain the trampolines. + */ + do { + ret = vm_allocate (mach_task_self (), &addr, psize * 2, VM_FLAGS_ANYWHERE); + } while (ret == KERN_ABORTED); + if (ret != KERN_SUCCESS) { + g_error ("Cannot allocate memory for trampolines: %d", ret); + break; + } + /*g_warning ("allocated trampoline double page at %x", addr);*/ + /* replace the second page with a remapped trampoline page */ + taddr = addr + psize; + vm_deallocate (mach_task_self (), taddr, psize); + ret = vm_remap (mach_task_self (), &taddr, psize, 0, FALSE, mach_task_self(), (vm_address_t)tpage, FALSE, &prot, &max_prot, VM_INHERIT_SHARE); + if (ret != KERN_SUCCESS) { + /* someone else got the page, try again */ + vm_deallocate (mach_task_self (), addr, psize); + continue; + } + /*g_warning ("remapped trampoline page at %x", taddr);*/ + + mono_aot_page_lock (); + page = trampoline_pages [tramp_type]; + /* some other thread already allocated, so use that to avoid wasting memory */ + if (page && page->trampolines < page->trampolines_end) { + code = page->trampolines; + page->trampolines += specific_trampoline_size; + mono_aot_page_unlock (); + vm_deallocate (mach_task_self (), addr, psize); + vm_deallocate (mach_task_self (), taddr, psize); + return code; + } + page = (TrampolinePage*)addr; + page->next = trampoline_pages [tramp_type]; + trampoline_pages [tramp_type] = page; + page->trampolines = (void*)(taddr + amodule->info.tramp_page_code_offsets [tramp_type]); + page->trampolines_end = (void*)(taddr + psize - 64); + code = page->trampolines; + page->trampolines += specific_trampoline_size; + mono_aot_page_unlock (); + + /* Register the generic part at the beggining of the trampoline page */ + gen_info = mono_tramp_info_create (NULL, (guint8*)taddr, amodule->info.tramp_page_code_offsets [tramp_type], NULL, NULL); + read_page_trampoline_uwinfo (gen_info, tramp_type, TRUE); + mono_aot_tramp_info_register (gen_info, NULL); + /* + * FIXME + * Registering each specific trampoline produces a lot of + * MonoJitInfo structures. Jump trampolines are also registered + * separately. + */ + if (tramp_type != MONO_AOT_TRAMP_SPECIFIC) { + /* Register the rest of the page as a single trampoline */ + sp_info = mono_tramp_info_create (NULL, code, page->trampolines_end - code, NULL, NULL); + read_page_trampoline_uwinfo (sp_info, tramp_type, FALSE); + mono_aot_tramp_info_register (sp_info, NULL); + } + return code; + } + g_error ("Cannot allocate more trampoline pages: %d", ret); + return NULL; +} + +#else +static unsigned char* +get_new_trampoline_from_page (int tramp_type) +{ + g_error ("Page trampolines not supported."); + return NULL; +} +#endif + + +static gpointer +get_new_specific_trampoline_from_page (gpointer tramp, gpointer arg) +{ + void *code; + gpointer *data; + + code = get_new_trampoline_from_page (MONO_AOT_TRAMP_SPECIFIC); + + data = (gpointer*)((char*)code - MONO_AOT_TRAMP_PAGE_SIZE); + data [0] = arg; + data [1] = tramp; + /*g_warning ("new trampoline at %p for data %p, tramp %p (stored at %p)", code, arg, tramp, data);*/ + return code; + +} + +static gpointer +get_new_rgctx_trampoline_from_page (gpointer tramp, gpointer arg) +{ + void *code; + gpointer *data; + + code = get_new_trampoline_from_page (MONO_AOT_TRAMP_STATIC_RGCTX); + + data = (gpointer*)((char*)code - MONO_AOT_TRAMP_PAGE_SIZE); + data [0] = arg; + data [1] = tramp; + /*g_warning ("new rgctx trampoline at %p for data %p, tramp %p (stored at %p)", code, arg, tramp, data);*/ + return code; + +} + +static gpointer +get_new_imt_trampoline_from_page (gpointer arg) +{ + void *code; + gpointer *data; + + code = get_new_trampoline_from_page (MONO_AOT_TRAMP_IMT); + + data = (gpointer*)((char*)code - MONO_AOT_TRAMP_PAGE_SIZE); + data [0] = arg; + /*g_warning ("new imt trampoline at %p for data %p, (stored at %p)", code, arg, data);*/ + return code; + +} + +static gpointer +get_new_gsharedvt_arg_trampoline_from_page (gpointer tramp, gpointer arg) +{ + void *code; + gpointer *data; + + code = get_new_trampoline_from_page (MONO_AOT_TRAMP_GSHAREDVT_ARG); + + data = (gpointer*)((char*)code - MONO_AOT_TRAMP_PAGE_SIZE); + data [0] = arg; + data [1] = tramp; + /*g_warning ("new rgctx trampoline at %p for data %p, tramp %p (stored at %p)", code, arg, tramp, data);*/ + return code; +} + +/* Return a given kind of trampoline */ +/* FIXME set unwind info for these trampolines */ +static gpointer +get_numerous_trampoline (MonoAotTrampoline tramp_type, int n_got_slots, MonoAotModule **out_amodule, guint32 *got_offset, guint32 *out_tramp_size) +{ + MonoImage *image; + MonoAotModule *amodule = get_mscorlib_aot_module (); + int index, tramp_size; + + /* Currently, we keep all trampolines in the mscorlib AOT image */ + image = mono_defaults.corlib; + + *out_amodule = amodule; + + mono_aot_lock (); + +#ifdef MONOTOUCH +#define MONOTOUCH_TRAMPOLINES_ERROR ". See http://docs.xamarin.com/ios/troubleshooting for instructions on how to fix this condition." +#else +#define MONOTOUCH_TRAMPOLINES_ERROR "" +#endif + if (amodule->trampoline_index [tramp_type] == amodule->info.num_trampolines [tramp_type]) { + g_error ("Ran out of trampolines of type %d in '%s' (limit %d)%s\n", + tramp_type, image ? image->name : "mscorlib", amodule->info.num_trampolines [tramp_type], MONOTOUCH_TRAMPOLINES_ERROR); + } + index = amodule->trampoline_index [tramp_type] ++; + + mono_aot_unlock (); + + *got_offset = amodule->info.trampoline_got_offset_base [tramp_type] + (index * n_got_slots); + + tramp_size = amodule->info.trampoline_size [tramp_type]; + + if (out_tramp_size) + *out_tramp_size = tramp_size; + + return amodule->trampolines [tramp_type] + (index * tramp_size); +} + +static void +no_specific_trampoline (void) +{ + g_assert_not_reached (); +} + +/* + * Return a specific trampoline from the AOT file. + */ +gpointer +mono_aot_create_specific_trampoline (MonoImage *image, gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len) +{ + MonoAotModule *amodule; + guint32 got_offset, tramp_size; + guint8 *code, *tramp; + static gpointer generic_trampolines [MONO_TRAMPOLINE_NUM]; + static gboolean inited; + static guint32 num_trampolines; + + if (mono_llvm_only) { + *code_len = 1; + return no_specific_trampoline; + } + + if (!inited) { + mono_aot_lock (); + + if (!inited) { + mono_counters_register ("Specific trampolines", MONO_COUNTER_JIT | MONO_COUNTER_INT, &num_trampolines); + inited = TRUE; + } + + mono_aot_unlock (); + } + + num_trampolines ++; + + if (!generic_trampolines [tramp_type]) { + char *symbol; + + symbol = mono_get_generic_trampoline_name (tramp_type); + generic_trampolines [tramp_type] = mono_aot_get_trampoline (symbol); + g_free (symbol); + } + + tramp = (guint8 *)generic_trampolines [tramp_type]; + g_assert (tramp); + + if (USE_PAGE_TRAMPOLINES) { + code = (guint8 *)get_new_specific_trampoline_from_page (tramp, arg1); + tramp_size = 8; + } else { + code = (guint8 *)get_numerous_trampoline (MONO_AOT_TRAMP_SPECIFIC, 2, &amodule, &got_offset, &tramp_size); + + amodule->got [got_offset] = tramp; + amodule->got [got_offset + 1] = arg1; + } + + if (code_len) + *code_len = tramp_size; + + return code; +} + +gpointer +mono_aot_get_static_rgctx_trampoline (gpointer ctx, gpointer addr) +{ + MonoAotModule *amodule; + guint8 *code; + guint32 got_offset; + + if (USE_PAGE_TRAMPOLINES) { + code = (guint8 *)get_new_rgctx_trampoline_from_page (addr, ctx); + } else { + code = (guint8 *)get_numerous_trampoline (MONO_AOT_TRAMP_STATIC_RGCTX, 2, &amodule, &got_offset, NULL); + + amodule->got [got_offset] = ctx; + amodule->got [got_offset + 1] = addr; + } + + /* The caller expects an ftnptr */ + return mono_create_ftnptr (mono_domain_get (), code); +} + +gpointer +mono_aot_get_unbox_trampoline (MonoMethod *method) +{ + guint32 method_index = mono_metadata_token_index (method->token) - 1; + MonoAotModule *amodule; + gpointer code; + guint32 *ut, *ut_end, *entry; + int low, high, entry_index = 0; + gpointer symbol_addr; + MonoTrampInfo *tinfo; + + if (method->is_inflated && !mono_method_is_generic_sharable_full (method, FALSE, FALSE, FALSE)) { + method_index = find_aot_method (method, &amodule); + if (method_index == 0xffffff && mono_method_is_generic_sharable_full (method, FALSE, TRUE, FALSE)) { + MonoMethod *shared = mini_get_shared_method_full (method, FALSE, FALSE); + method_index = find_aot_method (shared, &amodule); + } + if (method_index == 0xffffff && mono_method_is_generic_sharable_full (method, FALSE, TRUE, TRUE)) { + MonoMethod *shared = mini_get_shared_method_full (method, TRUE, TRUE); + method_index = find_aot_method (shared, &amodule); + } + g_assert (method_index != 0xffffff); + } else { + amodule = (MonoAotModule *)method->klass->image->aot_module; + g_assert (amodule); + } + + if (amodule->info.llvm_get_unbox_tramp) { + gpointer (*get_tramp) (int) = (gpointer (*)(int))amodule->info.llvm_get_unbox_tramp; + code = get_tramp (method_index); + + if (code) + return code; + } + + ut = amodule->unbox_trampolines; + ut_end = amodule->unbox_trampolines_end; + + /* Do a binary search in the sorted table */ + code = NULL; + low = 0; + high = (ut_end - ut); + while (low < high) { + entry_index = (low + high) / 2; + entry = &ut [entry_index]; + if (entry [0] < method_index) { + low = entry_index + 1; + } else if (entry [0] > method_index) { + high = entry_index; + } else { + break; + } + } + + code = get_call_table_entry (amodule->unbox_trampoline_addresses, entry_index); + g_assert (code); + + tinfo = mono_tramp_info_create (NULL, (guint8 *)code, 0, NULL, NULL); + + symbol_addr = read_unwind_info (amodule, tinfo, "unbox_trampoline_p"); + if (!symbol_addr) { + mono_tramp_info_free (tinfo); + return FALSE; + } + + tinfo->code_size = *(guint32*)symbol_addr; + mono_aot_tramp_info_register (tinfo, NULL); + + /* The caller expects an ftnptr */ + return mono_create_ftnptr (mono_domain_get (), code); +} + +gpointer +mono_aot_get_lazy_fetch_trampoline (guint32 slot) +{ + char *symbol; + gpointer code; + MonoAotModule *amodule = (MonoAotModule *)mono_defaults.corlib->aot_module; + guint32 index = MONO_RGCTX_SLOT_INDEX (slot); + static int count = 0; + + count ++; + if (index >= amodule->info.num_rgctx_fetch_trampolines) { + static gpointer addr; + gpointer *info; + + /* + * Use the general version of the rgctx fetch trampoline. It receives a pair of in the rgctx arg reg. + */ + if (!addr) + addr = load_function (amodule, "rgctx_fetch_trampoline_general"); + info = (void **)mono_domain_alloc0 (mono_get_root_domain (), sizeof (gpointer) * 2); + info [0] = GUINT_TO_POINTER (slot); + info [1] = mono_create_specific_trampoline (GUINT_TO_POINTER (slot), MONO_TRAMPOLINE_RGCTX_LAZY_FETCH, mono_get_root_domain (), NULL); + code = mono_aot_get_static_rgctx_trampoline (info, addr); + return mono_create_ftnptr (mono_domain_get (), code); + } + + symbol = mono_get_rgctx_fetch_trampoline_name (slot); + code = load_function ((MonoAotModule *)mono_defaults.corlib->aot_module, symbol); + g_free (symbol); + /* The caller expects an ftnptr */ + return mono_create_ftnptr (mono_domain_get (), code); +} + +static void +no_imt_trampoline (void) +{ + g_assert_not_reached (); +} + +gpointer +mono_aot_get_imt_trampoline (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count, gpointer fail_tramp) +{ + guint32 got_offset; + gpointer code; + gpointer *buf; + int i, index, real_count; + MonoAotModule *amodule; + + if (mono_llvm_only) + return no_imt_trampoline; + + real_count = 0; + for (i = 0; i < count; ++i) { + MonoIMTCheckItem *item = imt_entries [i]; + + if (item->is_equals) + real_count ++; + } + + /* Save the entries into an array */ + buf = (void **)mono_domain_alloc (domain, (real_count + 1) * 2 * sizeof (gpointer)); + index = 0; + for (i = 0; i < count; ++i) { + MonoIMTCheckItem *item = imt_entries [i]; + + if (!item->is_equals) + continue; + + g_assert (item->key); + + buf [(index * 2)] = item->key; + if (item->has_target_code) { + gpointer *p = (gpointer *)mono_domain_alloc (domain, sizeof (gpointer)); + *p = item->value.target_code; + buf [(index * 2) + 1] = p; + } else { + buf [(index * 2) + 1] = &(vtable->vtable [item->value.vtable_slot]); + } + index ++; + } + buf [(index * 2)] = NULL; + buf [(index * 2) + 1] = fail_tramp; + + if (USE_PAGE_TRAMPOLINES) { + code = get_new_imt_trampoline_from_page (buf); + } else { + code = get_numerous_trampoline (MONO_AOT_TRAMP_IMT, 1, &amodule, &got_offset, NULL); + + amodule->got [got_offset] = buf; + } + + return code; +} + +gpointer +mono_aot_get_gsharedvt_arg_trampoline (gpointer arg, gpointer addr) +{ + MonoAotModule *amodule; + guint8 *code; + guint32 got_offset; + + if (USE_PAGE_TRAMPOLINES) { + code = (guint8 *)get_new_gsharedvt_arg_trampoline_from_page (addr, arg); + } else { + code = (guint8 *)get_numerous_trampoline (MONO_AOT_TRAMP_GSHAREDVT_ARG, 2, &amodule, &got_offset, NULL); + + amodule->got [got_offset] = arg; + amodule->got [got_offset + 1] = addr; + } + + /* The caller expects an ftnptr */ + return mono_create_ftnptr (mono_domain_get (), code); +} + +/* + * mono_aot_set_make_unreadable: + * + * Set whenever to make all mmaped memory unreadable. In conjuction with a + * SIGSEGV handler, this is useful to find out which pages the runtime tries to read. + */ +void +mono_aot_set_make_unreadable (gboolean unreadable) +{ + static int inited; + + make_unreadable = unreadable; + + if (make_unreadable && !inited) { + mono_counters_register ("AOT: pagefaults", MONO_COUNTER_JIT | MONO_COUNTER_INT, &n_pagefaults); + } +} + +typedef struct { + MonoAotModule *module; + guint8 *ptr; +} FindMapUserData; + +static void +find_map (gpointer key, gpointer value, gpointer user_data) +{ + MonoAotModule *module = (MonoAotModule*)value; + FindMapUserData *data = (FindMapUserData*)user_data; + + if (!data->module) + if ((data->ptr >= module->mem_begin) && (data->ptr < module->mem_end)) + data->module = module; +} + +static MonoAotModule* +find_module_for_addr (void *ptr) +{ + FindMapUserData data; + + if (!make_unreadable) + return NULL; + + data.module = NULL; + data.ptr = (guint8*)ptr; + + mono_aot_lock (); + g_hash_table_foreach (aot_modules, (GHFunc)find_map, &data); + mono_aot_unlock (); + + return data.module; +} + +/* + * mono_aot_is_pagefault: + * + * Should be called from a SIGSEGV signal handler to find out whenever @ptr is + * within memory allocated by this module. + */ +gboolean +mono_aot_is_pagefault (void *ptr) +{ + if (!make_unreadable) + return FALSE; + + /* + * Not signal safe, but SIGSEGV's are synchronous, and + * this is only turned on by a MONO_DEBUG option. + */ + return find_module_for_addr (ptr) != NULL; +} + +/* + * mono_aot_handle_pagefault: + * + * Handle a pagefault caused by an unreadable page by making it readable again. + */ +void +mono_aot_handle_pagefault (void *ptr) +{ +#ifndef HOST_WIN32 + guint8* start = (guint8*)ROUND_DOWN (((gssize)ptr), mono_pagesize ()); + int res; + + mono_aot_lock (); + res = mono_mprotect (start, mono_pagesize (), MONO_MMAP_READ|MONO_MMAP_WRITE|MONO_MMAP_EXEC); + g_assert (res == 0); + + n_pagefaults ++; + mono_aot_unlock (); +#endif +} + +#else +/* AOT disabled */ + +void +mono_aot_init (void) +{ +} + +void +mono_aot_cleanup (void) +{ +} + +guint32 +mono_aot_find_method_index (MonoMethod *method) +{ + g_assert_not_reached (); + return 0; +} + +void +mono_aot_init_llvm_method (gpointer aot_module, guint32 method_index) +{ +} + +void +mono_aot_init_gshared_method_this (gpointer aot_module, guint32 method_index, MonoObject *this) +{ +} + +void +mono_aot_init_gshared_method_mrgctx (gpointer aot_module, guint32 method_index, MonoMethodRuntimeGenericContext *rgctx) +{ +} + +void +mono_aot_init_gshared_method_vtable (gpointer aot_module, guint32 method_index, MonoVTable *vtable) +{ +} + +gpointer +mono_aot_get_method (MonoDomain *domain, MonoMethod *method) +{ + return NULL; +} + +gpointer +mono_aot_get_method_checked (MonoDomain *domain, + MonoMethod *method, MonoError *error) +{ + error_init (error); + return NULL; +} + +gboolean +mono_aot_is_got_entry (guint8 *code, guint8 *addr) +{ + return FALSE; +} + +gboolean +mono_aot_get_cached_class_info (MonoClass *klass, MonoCachedClassInfo *res) +{ + return FALSE; +} + +gboolean +mono_aot_get_class_from_name (MonoImage *image, const char *name_space, const char *name, MonoClass **klass) +{ + return FALSE; +} + +MonoJitInfo * +mono_aot_find_jit_info (MonoDomain *domain, MonoImage *image, gpointer addr) +{ + return NULL; +} + +gpointer +mono_aot_get_method_from_token (MonoDomain *domain, MonoImage *image, guint32 token, MonoError *error) +{ + error_init (error); + return NULL; +} + +guint8* +mono_aot_get_plt_entry (guint8 *code) +{ + return NULL; +} + +gpointer +mono_aot_plt_resolve (gpointer aot_module, guint32 plt_info_offset, guint8 *code, MonoError *error) +{ + return NULL; +} + +void +mono_aot_patch_plt_entry (guint8 *code, guint8 *plt_entry, gpointer *got, mgreg_t *regs, guint8 *addr) +{ +} + +gpointer +mono_aot_get_method_from_vt_slot (MonoDomain *domain, MonoVTable *vtable, int slot, MonoError *error) +{ + error_init (error); + + return NULL; +} + +guint32 +mono_aot_get_plt_info_offset (mgreg_t *regs, guint8 *code) +{ + g_assert_not_reached (); + + return 0; +} + +gpointer +mono_aot_create_specific_trampoline (MonoImage *image, gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len) +{ + g_assert_not_reached (); + return NULL; +} + +gpointer +mono_aot_get_static_rgctx_trampoline (gpointer ctx, gpointer addr) +{ + g_assert_not_reached (); + return NULL; +} + +gpointer +mono_aot_get_trampoline_full (const char *name, MonoTrampInfo **out_tinfo) +{ + g_assert_not_reached (); + return NULL; +} + +gpointer +mono_aot_get_trampoline (const char *name) +{ + g_assert_not_reached (); + return NULL; +} + +gpointer +mono_aot_get_unbox_trampoline (MonoMethod *method) +{ + g_assert_not_reached (); + return NULL; +} + +gpointer +mono_aot_get_lazy_fetch_trampoline (guint32 slot) +{ + g_assert_not_reached (); + return NULL; +} + +gpointer +mono_aot_get_imt_trampoline (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count, gpointer fail_tramp) +{ + g_assert_not_reached (); + return NULL; +} + +gpointer +mono_aot_get_gsharedvt_arg_trampoline (gpointer arg, gpointer addr) +{ + g_assert_not_reached (); + return NULL; +} + +void +mono_aot_set_make_unreadable (gboolean unreadable) +{ +} + +gboolean +mono_aot_is_pagefault (void *ptr) +{ + return FALSE; +} + +void +mono_aot_handle_pagefault (void *ptr) +{ +} + +guint8* +mono_aot_get_unwind_info (MonoJitInfo *ji, guint32 *unwind_info_len) +{ + g_assert_not_reached (); + return NULL; +} + +void +mono_aot_register_jit_icall (const char *name, gpointer addr) +{ +} + +#endif diff --git a/unity-2019.4.24f1-mbe/mono/mini/aot-runtime.h b/unity-2019.4.24f1-mbe/mono/mini/aot-runtime.h new file mode 100644 index 000000000..8be886205 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/aot-runtime.h @@ -0,0 +1,254 @@ +/** + * \file + * Copyright 2002-2003 Ximian Inc + * Copyright 2003-2011 Novell Inc + * Copyright 2011 Xamarin Inc + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#ifndef __MONO_AOT_RUNTIME_H__ +#define __MONO_AOT_RUNTIME_H__ + +#include "mini.h" + +/* Version number of the AOT file format */ +#define MONO_AOT_FILE_VERSION 142 + +#define MONO_AOT_TRAMP_PAGE_SIZE 16384 + +/* Constants used to encode different types of methods in AOT */ +enum { + MONO_AOT_METHODREF_MIN = 240, + /* Image index bigger than METHODREF_MIN */ + MONO_AOT_METHODREF_LARGE_IMAGE_INDEX = 249, + /* Runtime provided methods on arrays */ + MONO_AOT_METHODREF_ARRAY = 250, + MONO_AOT_METHODREF_NO_AOT_TRAMPOLINE = 251, + /* Wrappers */ + MONO_AOT_METHODREF_WRAPPER = 252, + /* Methods on generic instances */ + MONO_AOT_METHODREF_GINST = 253, + /* Methods resolve using a METHODSPEC token */ + MONO_AOT_METHODREF_METHODSPEC = 254, +}; + +/* Constants used to encode different types of types in AOT */ +enum { + /* typedef index */ + MONO_AOT_TYPEREF_TYPEDEF_INDEX = 1, + /* typedef index + image index */ + MONO_AOT_TYPEREF_TYPEDEF_INDEX_IMAGE = 2, + /* typespec token */ + MONO_AOT_TYPEREF_TYPESPEC_TOKEN = 3, + /* generic inst */ + MONO_AOT_TYPEREF_GINST = 4, + /* type/method variable */ + MONO_AOT_TYPEREF_VAR = 5, + /* array */ + MONO_AOT_TYPEREF_ARRAY = 6, + /* blob index of the type encoding */ + MONO_AOT_TYPEREF_BLOB_INDEX = 7, + /* ptr */ + MONO_AOT_TYPEREF_PTR = 8 +}; + +/* Trampolines which we have a lot of */ +typedef enum { + MONO_AOT_TRAMP_SPECIFIC = 0, + MONO_AOT_TRAMP_STATIC_RGCTX = 1, + MONO_AOT_TRAMP_IMT = 2, + MONO_AOT_TRAMP_GSHAREDVT_ARG = 3, + MONO_AOT_TRAMP_NUM = 4 +} MonoAotTrampoline; + +typedef enum { + MONO_AOT_FILE_FLAG_WITH_LLVM = 1, + MONO_AOT_FILE_FLAG_FULL_AOT = 2, + MONO_AOT_FILE_FLAG_DEBUG = 4, + MONO_AOT_FILE_FLAG_LLVM_THUMB = 8, + MONO_AOT_FILE_FLAG_LLVM_ONLY = 16, + MONO_AOT_FILE_FLAG_SAFEPOINTS = 32, + MONO_AOT_FILE_FLAG_SEPARATE_DATA = 64, + MONO_AOT_FILE_FLAG_EAGER_LOAD = 128, +} MonoAotFileFlags; + +typedef enum { + MONO_AOT_TABLE_BLOB, + MONO_AOT_TABLE_IMAGE_TABLE, + MONO_AOT_TABLE_CLASS_NAME, + MONO_AOT_TABLE_METHOD_INFO_OFFSETS, + MONO_AOT_TABLE_EX_INFO_OFFSETS, + MONO_AOT_TABLE_CLASS_INFO_OFFSETS, + MONO_AOT_TABLE_GOT_INFO_OFFSETS, + MONO_AOT_TABLE_LLVM_GOT_INFO_OFFSETS, + MONO_AOT_TABLE_EXTRA_METHOD_INFO_OFFSETS, + MONO_AOT_TABLE_EXTRA_METHOD_TABLE, + MONO_AOT_TABLE_NUM +} MonoAotFileTable; + +/* This structure is stored in the AOT file */ +typedef struct MonoAotFileInfo +{ + /* The version number of the AOT file format, should match MONO_AOT_FILE_VERSION */ + guint32 version; + /* For alignment */ + guint32 dummy; + + /* All the pointers should be at the start to avoid alignment problems */ + /* Symbols */ +#define MONO_AOT_FILE_INFO_FIRST_SYMBOL jit_got + /* Global Offset Table for JITted code */ + gpointer jit_got; + /* Global Offset Table for LLVM code */ + gpointer llvm_got; + /* Mono EH Frame created by llc when using LLVM */ + gpointer mono_eh_frame; + /* Points to the get_method () function in the LLVM image or NULL */ + gpointer llvm_get_method; + /* Points to the get_unbox_tramp () function in the LLVM image or NULL */ + gpointer llvm_get_unbox_tramp; + gpointer jit_code_start; + gpointer jit_code_end; + gpointer method_addresses; + /* Data blob */ + gpointer blob; + gpointer class_name_table; + gpointer class_info_offsets; + gpointer method_info_offsets; + gpointer ex_info_offsets; + gpointer extra_method_info_offsets; + gpointer extra_method_table; + gpointer got_info_offsets; + gpointer llvm_got_info_offsets; + gpointer image_table; + gpointer mem_end; + /* The GUID of the assembly which the AOT image was generated from */ + gpointer assembly_guid; + /* + * The runtime version string for AOT images generated using 'bind-to-runtime-version', + * NULL otherwise. + */ + gpointer runtime_version; + /* Blocks of various kinds of trampolines */ + gpointer specific_trampolines; + gpointer static_rgctx_trampolines; + gpointer imt_trampolines; + gpointer gsharedvt_arg_trampolines; + /* In static mode, points to a table of global symbols for trampolines etc */ + gpointer globals; + /* Points to a string containing the assembly name*/ + gpointer assembly_name; + /* Start of Mono's Program Linkage Table */ + gpointer plt; + /* End of Mono's Program Linkage Table */ + gpointer plt_end; + gpointer unwind_info; + /* Points to a table mapping methods to their unbox trampolines */ + gpointer unbox_trampolines; + /* Points to the end of the previous table */ + gpointer unbox_trampolines_end; + /* Points to a table of unbox trampoline addresses/offsets */ + gpointer unbox_trampoline_addresses; + /* Points to an array of weak field indexes */ + gpointer weak_field_indexes; +#define MONO_AOT_FILE_INFO_LAST_SYMBOL weak_field_indexes + + /* Scalars */ + /* The index of the first GOT slot used by the PLT */ + guint32 plt_got_offset_base; + /* Number of entries in the GOT */ + guint32 got_size; + /* Number of entries in the PLT */ + guint32 plt_size; + /* Number of methods */ + guint32 nmethods; + /* A union of MonoAotFileFlags */ + guint32 flags; + /* Optimization flags used to compile the module */ + guint32 opts; + /* SIMD flags used to compile the module */ + guint32 simd_opts; + /* Index of the blob entry holding the GC used by this module */ + gint32 gc_name_index; + guint32 num_rgctx_fetch_trampolines; + /* These are used for sanity checking object layout problems when cross-compiling */ + guint32 double_align, long_align, generic_tramp_num; + /* The page size used by trampoline pages */ + guint32 tramp_page_size; + /* + * The number of GOT entries which need to be preinitialized when the + * module is loaded. + */ + guint32 nshared_got_entries; + /* The size of the data file, if MONO_AOT_FILE_FLAG_SEPARATE_DATA is set */ + guint32 datafile_size; + + /* Arrays */ + /* Offsets for tables inside the data file if MONO_AOT_FILE_FLAG_SEPARATE_DATA is set */ + // FIXME: Sync with AOT + guint32 table_offsets [MONO_AOT_TABLE_NUM]; + /* Number of trampolines */ + guint32 num_trampolines [MONO_AOT_TRAMP_NUM]; + /* The indexes of the first GOT slots used by the trampolines */ + guint32 trampoline_got_offset_base [MONO_AOT_TRAMP_NUM]; + /* The size of one trampoline */ + guint32 trampoline_size [MONO_AOT_TRAMP_NUM]; + /* The offset where the trampolines begin on a trampoline page */ + guint32 tramp_page_code_offsets [MONO_AOT_TRAMP_NUM]; + /* GUID of aot compilation */ + guint8 aotid[16]; +} MonoAotFileInfo; + +/* Number of symbols in the MonoAotFileInfo structure */ +#define MONO_AOT_FILE_INFO_NUM_SYMBOLS (((G_STRUCT_OFFSET (MonoAotFileInfo, MONO_AOT_FILE_INFO_LAST_SYMBOL) - G_STRUCT_OFFSET (MonoAotFileInfo, MONO_AOT_FILE_INFO_FIRST_SYMBOL)) / sizeof (gpointer)) + 1) + +void mono_aot_init (void); +void mono_aot_cleanup (void); +gpointer mono_aot_get_method_checked (MonoDomain *domain, + MonoMethod *method, MonoError *error); +gpointer mono_aot_get_method_from_token (MonoDomain *domain, MonoImage *image, guint32 token, MonoError *error); +gboolean mono_aot_is_got_entry (guint8 *code, guint8 *addr); +guint8* mono_aot_get_plt_entry (guint8 *code); +guint32 mono_aot_get_plt_info_offset (mgreg_t *regs, guint8 *code); +gboolean mono_aot_get_cached_class_info (MonoClass *klass, MonoCachedClassInfo *res); +gboolean mono_aot_get_class_from_name (MonoImage *image, const char *name_space, const char *name, MonoClass **klass); +MonoJitInfo* mono_aot_find_jit_info (MonoDomain *domain, MonoImage *image, gpointer addr); +gpointer mono_aot_plt_resolve (gpointer aot_module, guint32 plt_info_offset, guint8 *code, MonoError *error); +void mono_aot_patch_plt_entry (guint8 *code, guint8 *plt_entry, gpointer *got, mgreg_t *regs, guint8 *addr); +gpointer mono_aot_get_method_from_vt_slot (MonoDomain *domain, MonoVTable *vtable, int slot, MonoError *error); +gpointer mono_aot_create_specific_trampoline (MonoImage *image, gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len); +gpointer mono_aot_get_trampoline (const char *name); +gpointer mono_aot_get_trampoline_full (const char *name, MonoTrampInfo **out_tinfo); +gpointer mono_aot_get_unbox_trampoline (MonoMethod *method); +gpointer mono_aot_get_lazy_fetch_trampoline (guint32 slot); +gpointer mono_aot_get_static_rgctx_trampoline (gpointer ctx, gpointer addr); +gpointer mono_aot_get_imt_trampoline (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count, gpointer fail_tramp); +gpointer mono_aot_get_gsharedvt_arg_trampoline(gpointer arg, gpointer addr); +guint8* mono_aot_get_unwind_info (MonoJitInfo *ji, guint32 *unwind_info_len); +guint32 mono_aot_method_hash (MonoMethod *method); +gboolean mono_aot_can_dedup (MonoMethod *method); +MonoMethod* mono_aot_get_array_helper_from_wrapper (MonoMethod *method); +void mono_aot_set_make_unreadable (gboolean unreadable); +gboolean mono_aot_is_pagefault (void *ptr); +void mono_aot_handle_pagefault (void *ptr); +void mono_aot_register_jit_icall (const char *name, gpointer addr); +guint32 mono_aot_find_method_index (MonoMethod *method); +void mono_aot_init_llvm_method (gpointer aot_module, guint32 method_index); +void mono_aot_init_gshared_method_this (gpointer aot_module, guint32 method_index, MonoObject *this_ins); +void mono_aot_init_gshared_method_mrgctx (gpointer aot_module, guint32 method_index, MonoMethodRuntimeGenericContext *rgctx); +void mono_aot_init_gshared_method_vtable (gpointer aot_module, guint32 method_index, MonoVTable *vtable); +GHashTable *mono_aot_get_weak_field_indexes (MonoImage *image); + +/* This is an exported function */ +MONO_API void mono_aot_register_module (gpointer *aot_info); + +/* These are used to load the AOT data for aot images compiled with MONO_AOT_FILE_FLAG_SEPARATE_DATA */ +/* + * Return the AOT data for ASSEMBLY. SIZE is the size of the data. OUT_HANDLE should be set to a handle which is later + * passed to the free function. + */ +typedef unsigned char* (*MonoLoadAotDataFunc) (MonoAssembly *assembly, int size, gpointer user_data, void **out_handle); +/* Not yet used */ +typedef void (*MonoFreeAotDataFunc) (MonoAssembly *assembly, int size, gpointer user_data, void *handle); +MONO_API void mono_install_load_aot_data_hook (MonoLoadAotDataFunc load_func, MonoFreeAotDataFunc free_func, gpointer user_data); + +#endif /* __MONO_AOT_RUNTIME_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/mini/aot-tests.cs b/unity-2019.4.24f1-mbe/mono/mini/aot-tests.cs new file mode 100644 index 000000000..7e718784a --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/aot-tests.cs @@ -0,0 +1,503 @@ +using System; +using System.Text; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; + +/* + * Regression tests for the AOT/FULL-AOT code. + */ + +#if __MOBILE__ +class AotTests +#else +class Tests +#endif +{ +#if !__MOBILE__ + static int Main (String[] args) { + return TestDriver.RunTests (typeof (Tests), args); + } +#endif + + public delegate void ArrayDelegate (int[,] arr); + + [Category ("!WASM")] //Requires a working threadpool + static int test_0_array_delegate_full_aot () { + ArrayDelegate d = delegate (int[,] arr) { + }; + int[,] a = new int[5, 6]; + d.BeginInvoke (a, null, null); + return 0; + } + + struct Struct1 { + public double a, b; + } + + struct Struct2 { + public float a, b; + } + + class Foo { + /* The 'd' argument is used to shift the register indexes so 't' doesn't start at the first reg */ + public static T Get_T (double d, T t) { + return t; + } + } + + class Foo2 { + public static T Get_T (double d, T t) { + return t; + } + public static T Get_T2 (double d, int i1, int i2, int i3, int i4, int i5, int i6, int i7, int i8, T t) { + return t; + } + } + + class Foo3 { + public static T Get_T (double d, T t) { + return Foo2.Get_T (d, t); + } + } + + [Category ("DYNCALL")] + static int test_0_arm64_dyncall_double () { + double arg1 = 1.0f; + double s = 2.0f; + var res = (double)typeof (Foo).GetMethod ("Get_T").Invoke (null, new object [] { arg1, s }); + if (res != 2.0f) + return 1; + return 0; + } + + [Category ("DYNCALL")] + static int test_0_arm64_dyncall_float () { + double arg1 = 1.0f; + float s = 2.0f; + var res = (float)typeof (Foo).GetMethod ("Get_T").Invoke (null, new object [] { arg1, s }); + if (res != 2.0f) + return 1; + return 0; + } + + [Category ("DYNCALL")] + [Category ("!FULLAOT-AMD64")] + static int test_0_arm64_dyncall_hfa_double () { + double arg1 = 1.0f; + // HFA with double members + var s = new Struct1 (); + s.a = 1.0f; + s.b = 2.0f; + var s_res = (Struct1)typeof (Foo).GetMethod ("Get_T").Invoke (null, new object [] { arg1, s }); + if (s_res.a != 1.0f || s_res.b != 2.0f) + return 1; + return 0; + } + + [Category ("DYNCALL")] + [Category ("!FULLAOT-AMD64")] + static int test_0_arm64_dyncall_hfa_float () { + double arg1 = 1.0f; + var s = new Struct2 (); + s.a = 1.0f; + s.b = 2.0f; + var s_res = (Struct2)typeof (Foo).GetMethod ("Get_T").Invoke (null, new object [] { arg1, s }); + if (s_res.a != 1.0f || s_res.b != 2.0f) + return 1; + return 0; + } + + [Category ("DYNCALL")] + [Category ("GSHAREDVT")] + [Category ("!FULLAOT-AMD64")] + static int test_0_arm64_dyncall_gsharedvt_out_hfa_double () { + /* gsharedvt out trampoline with double hfa argument */ + double arg1 = 1.0f; + + var s = new Struct1 (); + s.a = 1.0f; + s.b = 2.0f; + // Call Foo2.Get_T directly, so its gets an instance + Foo2.Get_T (arg1, s); + Type t = typeof (Foo3<>).MakeGenericType (new Type [] { typeof (Struct1) }); + // Call Foo3.Get_T, this will call the gsharedvt instance, which will call the non-gsharedvt instance + var s_res = (Struct1)t.GetMethod ("Get_T").Invoke (null, new object [] { arg1, s }); + if (s_res.a != 1.0f || s_res.b != 2.0f) + return 1; + return 0; + } + + [Category ("DYNCALL")] + [Category ("GSHAREDVT")] + [Category ("!FULLAOT-AMD64")] + static int test_0_arm64_dyncall_gsharedvt_out_hfa_float () { + /* gsharedvt out trampoline with double hfa argument */ + double arg1 = 1.0f; + + var s = new Struct2 (); + s.a = 1.0f; + s.b = 2.0f; + // Call Foo2.Get_T directly, so its gets an instance + Foo2.Get_T (arg1, s); + Type t = typeof (Foo3<>).MakeGenericType (new Type [] { typeof (Struct2) }); + // Call Foo3.Get_T, this will call the gsharedvt instance, which will call the non-gsharedvt instance + var s_res = (Struct2)t.GetMethod ("Get_T").Invoke (null, new object [] { arg1, s }); + if (s_res.a != 1.0f || s_res.b != 2.0f) + return 1; + return 0; + } + + interface IFaceFoo4 { + T Get_T (double d, T t); + T Get_T2 (double d, T t); + } + + class Foo4 : IFaceFoo4 { + public T Get_T (double d, T t) { + return Foo2.Get_T (d, t); + } + public T Get_T2 (double d, T t) { + return Foo2.Get_T2 (d, 1, 2, 3, 4, 5, 6, 7, 8, t); + } + } + + struct VTypeByRefStruct { + public long o1, o2, o3; + } + + [Category ("GSHAREDVT")] + public static int test_0_arm64_gsharedvt_out_vtypebyref () { + /* gsharedvt out trampoline with vtypebyref argument */ + var s = new VTypeByRefStruct () { o1 = 1, o2 = 2, o3 = 3 }; + + // Call Foo2.Get_T directly, so its gets an instance + Foo2.Get_T (1.0f, s); + var o = (IFaceFoo4)Activator.CreateInstance (typeof (Foo4<>).MakeGenericType (new Type [] { typeof (VTypeByRefStruct) })); + // Call Foo4.Get_T, this will call the gsharedvt instance, which will call the non-gsharedvt instance + var s_res = o.Get_T (1.0f, s); + if (s_res.o1 != 1 || s_res.o2 != 2 || s_res.o3 != 3) + return 1; + // Same with the byref argument passed on the stack + s_res = o.Get_T2 (1.0f, s); + if (s_res.o1 != 1 || s_res.o2 != 2 || s_res.o3 != 3) + return 2; + return 0; + } + + class Foo5 { + public static T Get_T (object o) { + return (T)o; + } + } + + [Category ("DYNCALL")] + [Category ("GSHAREDVT")] + [Category ("!FULLAOT-AMD64")] + static int test_0_arm64_dyncall_vtypebyref_ret () { + var s = new VTypeByRefStruct () { o1 = 1, o2 = 2, o3 = 3 }; + Type t = typeof (Foo5<>).MakeGenericType (new Type [] { typeof (VTypeByRefStruct) }); + var o = Activator.CreateInstance (t); + try { + var s_res = (VTypeByRefStruct)t.GetMethod ("Get_T").Invoke (o, new object [] { s }); + if (s_res.o1 != 1 || s_res.o2 != 2 || s_res.o3 != 3) + return 1; + } catch (TargetInvocationException) { + return 2; + } + return 0; + } + + class Foo6 { + public T reg_stack_split_inner (int i, int j, T l) { + return l; + } + } + + [Category ("DYNCALL")] + [Category ("GSHAREDVT")] + static int test_0_arm_dyncall_reg_stack_split () { + var m = typeof (Foo6).GetMethod ("reg_stack_split_inner").MakeGenericMethod (new Type[] { typeof (long) }); + var o = new Foo6 (); + if ((long)m.Invoke (o, new object [] { 1, 2, 3 }) != 3) + return 1; + if ((long)m.Invoke (o, new object [] { 1, 2, Int64.MaxValue }) != Int64.MaxValue) + return 2; + return 0; + } + + static int test_0_partial_sharing_regress_30204 () { + var t = typeof (System.Collections.Generic.Comparer>); + var d = new SortedDictionary (); + d.Add ("key1", "banana"); + return d ["key1"] == "banana" ? 0 : 1; + } + + class NullableMethods { + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static bool GetHasValue(Nullable value) where T : struct { + return value.HasValue; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static T GetValue(Nullable value) where T : struct { + return value.Value; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Nullable Get(T t) where T : struct { + return t; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Nullable GetNull() where T : struct { + return null; + } + } + + [Category ("DYNCALL")] + [Category ("!FULLAOT-AMD64")] + public static int test_0_dyncall_nullable () { + int? v; + + v = 42; + NullableMethods.GetHasValue (v); + bool b = (bool)typeof (NullableMethods).GetMethod ("GetHasValue").MakeGenericMethod (new Type [] { typeof (int) }).Invoke (null, new object [] { v }); + if (!b) + return 1; + v = null; + b = (bool)typeof (NullableMethods).GetMethod ("GetHasValue").MakeGenericMethod (new Type [] { typeof (int) }).Invoke (null, new object [] { v }); + if (b) + return 2; + + v = 42; + NullableMethods.GetValue (v); + var res = (int)typeof (NullableMethods).GetMethod ("GetValue").MakeGenericMethod (new Type [] { typeof (int) }).Invoke (null, new object [] { v }); + if (res != 42) + return 3; + + NullableMethods.Get (42); + var res2 = (int?)typeof (NullableMethods).GetMethod ("Get").MakeGenericMethod (new Type [] { typeof (int) }).Invoke (null, new object [] { 42 }); + if (res2 != 42) + return 4; + res2 = (int?)typeof (NullableMethods).GetMethod ("GetNull").MakeGenericMethod (new Type [] { typeof (int) }).Invoke (null, new object [] { }); + if (res2.HasValue) + return 5; + return 0; + } + + enum AnEnum { + A = 0, + B = 1 + } + + public static int test_0_enum_eq_comparer () { + var c = EqualityComparer.Default; + return (!c.Equals (AnEnum.A, AnEnum.B) && c.Equals (AnEnum.A, AnEnum.A)) ? 0 : 1; + } + + public static int test_0_enum_comparer () { + var c = Comparer.Default; + return c.Compare (AnEnum.A, AnEnum.A); + } + + private static Dictionary ConvertDictionary(Dictionary> source) { + return source.ToDictionary(pair => pair.Key, pair => pair.Value[0]); + } + + [Category ("GSHAREDVT")] + public static int test_0_gsharedvt_non_variable_arg () { + Dictionary> data = new Dictionary> + { + {123L, new List {2}} + }; + Dictionary newDict = ConvertDictionary(data); + if (newDict.Count != 1) + return 1; + return 0; + } + + enum LongEnum : ulong { + A = 1 + } + + public static int test_0_long_enum_eq_comparer () { + var c = EqualityComparer.Default; + c.GetHashCode (LongEnum.A); + return 0; + } + + enum UInt32Enum : uint { + A = 1 + } + + enum Int32Enum : int { + A = 1 + } + + enum Int16Enum : short { + A = 1 + } + + enum UInt16Enum : ushort { + A = 1 + } + + enum Int8Enum : sbyte { + A = 1 + } + + enum UInt8Enum : byte { + A = 1 + } + + public static int test_0_int_enum_eq_comparer () { + var t1 = new Dictionary (); + t1 [Int32Enum.A] = "foo"; + + var t2 = new Dictionary (); + t2 [UInt32Enum.A] = "foo"; + + var t3 = new Dictionary (); + t3 [UInt16Enum.A] = "foo"; + + var t4 = new Dictionary (); + t4 [Int16Enum.A] = "foo"; + + var t5 = new Dictionary (); + t5 [Int8Enum.A] = "foo"; + + var t6 = new Dictionary (); + t6 [UInt8Enum.A] = "foo"; + + return 0; + } + + [Category ("DYNCALL")] + public static int test_0_array_accessor_runtime_invoke_ref () { + var t = typeof (string[]); + var arr = Array.CreateInstance (typeof (string), 1); + arr.GetType ().GetMethod ("Set").Invoke (arr, new object [] { 0, "A" }); + var res = (string)arr.GetType ().GetMethod ("Get").Invoke (arr, new object [] { 0 }); + if (res != "A") + return 1; + return 0; + } + + public static void SetArrayValue_ (T[] values) { + values.Select (x => x).ToArray (); + } + + [Category ("GSHAREDVT")] + public static int test_0_delegate_invoke_wrappers_gsharedvt () { + var enums = new LongEnum [] { LongEnum.A }; + SetArrayValue_ (enums); + return 0; + } + + struct LargeStruct { + public int a, b, c, d; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static bool GetHasValue(T? value) where T : struct + { + return value.HasValue; + } + + [Category ("DYNCALL")] + [Category ("!FULLAOT-AMD64")] + [Category ("!WASM")] //Interp fails + public static int test_0_large_nullable_invoke () { + var s = new LargeStruct () { a = 1, b = 2, c = 3, d = 4 }; + + GetHasValue (s); + +#if __MOBILE__ + var m = typeof(AotTests).GetMethod("GetHasValue", BindingFlags.Static | BindingFlags.Public); +#else + var m = typeof(Tests).GetMethod("GetHasValue", BindingFlags.Static | BindingFlags.Public); +#endif + + Type type = typeof (LargeStruct?).GetGenericArguments () [0]; + bool b1 = (bool)m.MakeGenericMethod (new Type[] {type}).Invoke (null, new object[] { s }); + if (!b1) + return 1; + bool b2 = (bool)m.MakeGenericMethod (new Type[] {type}).Invoke (null, new object[] { null }); + if (b2) + return 2; + return 0; + } + + struct FpStruct { + public float a, b, c; + } + + struct LargeStruct2 { + public FpStruct x; + public int a, b, c, d, e, f, g, h; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + static int pass_hfa_on_stack (FpStruct s1, FpStruct s2, FpStruct s3) { + return (int)s3.c; + } + + public static int test_10_arm64_hfa_on_stack_llvm () { + var arr = new LargeStruct2 [10, 10]; + for (int i = 0; i < 10; ++i) + for (int j = 0; j < 10; ++j) + arr [i, j].x = new FpStruct (); + + var s1 = new FpStruct () { a = 1, b = 1, c = 10 }; + return pass_hfa_on_stack (s1, s1, s1); + } + + public static int test_0_get_current_method () { + var m = MethodBase.GetCurrentMethod (); +#if __MOBILE__ + var m2 = typeof (AotTests).GetMethod ("test_0_get_current_method"); +#else + var m2 = typeof (Tests).GetMethod ("test_0_get_current_method"); +#endif + return m == m2 ? 0 : 1; + } + + class GetCurrentMethodClass { + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public MethodBase get_current () { + return MethodBase.GetCurrentMethod (); + } + } + + public static int test_0_get_current_method_generic () { + var c = new GetCurrentMethodClass (); + var m = c.get_current (); + var m2 = typeof (GetCurrentMethodClass<>).GetMethod ("get_current"); + return m == m2 ? 0 : 1; + } + + public static int test_0_array_wrappers_runtime_invoke () { + string[][] arr = new string [10][]; + IEnumerable iface = arr; + var m = typeof(IEnumerable).GetMethod ("GetEnumerator"); + m.Invoke (arr, null); + return 0; + } + + public static int test_0_fault_clauses () { + object [] data = { 1, 2, 3 }; + int [] expected = { 1, 2, 3 }; + + try { + Action d = delegate () { data.Cast ().GetEnumerator ().MoveNext (); }; + d (); + } catch (Exception ex) { + } + return 0; + } +} diff --git a/unity-2019.4.24f1-mbe/mono/mini/arch-stubs.c b/unity-2019.4.24f1-mbe/mono/mini/arch-stubs.c new file mode 100644 index 000000000..6f918c5d0 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/arch-stubs.c @@ -0,0 +1,68 @@ +/** + * \file + */ + +#include "mini.h" + +/* Dummy versions of some arch specific functions to avoid ifdefs at call sites */ + +#ifndef MONO_ARCH_GSHAREDVT_SUPPORTED + +gboolean +mono_arch_gsharedvt_sig_supported (MonoMethodSignature *sig) +{ + return FALSE; +} + +gpointer +mono_arch_get_gsharedvt_call_info (gpointer addr, MonoMethodSignature *normal_sig, MonoMethodSignature *gsharedvt_sig, gboolean gsharedvt_in, gint32 vcall_offset, gboolean calli) +{ + g_assert_not_reached (); + return NULL; +} + +gpointer +mono_arch_get_gsharedvt_arg_trampoline (MonoDomain *domain, gpointer arg, gpointer addr) +{ + g_assert_not_reached (); + return NULL; +} + +gpointer +mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot) +{ + g_assert_not_reached (); + return NULL; +} + +#endif + +#ifndef MONO_ARCH_HAVE_DECOMPOSE_OPTS +void +mono_arch_decompose_opts (MonoCompile *cfg, MonoInst *ins) +{ +} +#endif + +#ifndef MONO_ARCH_HAVE_OPCODE_NEEDS_EMULATION +gboolean +mono_arch_opcode_needs_emulation (MonoCompile *cfg, int opcode) +{ + return TRUE; +} +#endif + +#ifndef MONO_ARCH_HAVE_DECOMPOSE_LONG_OPTS +void +mono_arch_decompose_long_opts (MonoCompile *cfg, MonoInst *ins) +{ +} +#endif + +#ifndef MONO_ARCH_HAVE_OP_TAIL_CALL +gboolean +mono_arch_tail_call_supported (MonoCompile *cfg, MonoMethodSignature *caller_sig, MonoMethodSignature *callee_sig) +{ + return mono_metadata_signature_equal (caller_sig, callee_sig) && !MONO_TYPE_ISSTRUCT (callee_sig->ret); +} +#endif diff --git a/unity-2019.4.24f1-mbe/mono/mini/arrays.cs b/unity-2019.4.24f1-mbe/mono/mini/arrays.cs new file mode 100644 index 000000000..00a3c6b90 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/arrays.cs @@ -0,0 +1,811 @@ +using System; +using System.Reflection; + +/* + * Regression tests for the mono JIT. + * + * Each test needs to be of the form: + * + * static int test__ (); + * + * where is an integer (the value that needs to be returned by + * the method to make it pass. + * is a user-displayed name used to identify the test. + * + * The tests can be driven in two ways: + * *) running the program directly: Main() uses reflection to find and invoke + * the test methods (this is useful mostly to check that the tests are correct) + * *) with the --regression switch of the jit (this is the preferred way since + * all the tests will be run with optimizations on and off) + * + * The reflection logic could be moved to a .dll since we need at least another + * regression test file written in IL code to have better control on how + * the IL code looks. + */ + +#if __MOBILE__ +class ArrayTests +#else +class Tests +#endif +{ + +#if !__MOBILE__ + public static int Main (string[] args) { + return TestDriver.RunTests (typeof (Tests), args); + } +#endif + + public static int test_10_create () { + int[] a = new int [10]; + return a.Length; + } + + public static int test_0_unset_value () { + int[] a = new int [10]; + return a [5]; + } + + public static int test_3_set_value () { + int[] a = new int [10]; + a [5] = 3; + return a [5]; + } + + public static int test_0_char_array_1 () { + int value = -30; + char[] tmp = new char [20]; + char[] digitLowerTable = new char[16]; + tmp[0] = digitLowerTable[-(value % 10)]; + return 0; + } + + public static int test_0_char_array_2 () { + int value = 5; + char[] tmp = new char [20]; + char[] digitLowerTable = new char[16]; + tmp[0] = digitLowerTable[value % 10]; + return 0; + } + + public static int test_0_char_array_3 () { + int value = -1; + char[] tmp = new char [20]; + char[] digitLowerTable = new char[16]; + tmp [0] = digitLowerTable[value & 15]; + return 0; + } + + public unsafe static int test_0_byte_array () { + byte [] src = new byte [8]; + double ret; + byte *dst = (byte *)&ret; + int start = 0; + + dst[0] = src[4 + start]; + + return 0; + } + + public static int test_0_set_after_shift () { + int [] n = new int [1]; + int b = 16; + + n [0] = 100 + (1 << (16 - b)); + + if (n [0] != 101) + return 1; + + return 0; + } + + /* Regression test for #30073 */ + public static int test_0_newarr_emulation () { + double d = 500; + checked { + double [] arr = new double [(int)d]; + } + return 0; + } + + class BitClass { + private Int32[] m_array = new int [10]; + + public void setBit (int bitIndex, bool value) { + int index = bitIndex/32; + int shift = bitIndex%32; + + Int32 theBit = 1 << shift; + if (value) + m_array[index] |= theBit; + else + m_array[index] &= ~theBit; + } + + public bool getBit (int bitIndex) { + int index = bitIndex/32; + int shift = bitIndex%32; + + Int32 theBit = m_array[index] & (1 << shift); + return (theBit == 0) ? false : true; + } + } + + public static int test_1_bit_index () { + var t = new BitClass (); + t.setBit (0, true); + t.setBit (3, true); + if (t.getBit (1)) + return 4; + if (!t.getBit (0)) + return 5; + if (!t.getBit (3)) + return 6; + return 1; + } + + class helper1 { + + int [] ma = new int [56]; + const int MBIG = int.MaxValue; + + public helper1 () { + for (int k = 1; k < 5; k++) { + for (int i = 1; i < 56; i++) { + ma [i] -= ma [1 + (i + 30) % 55]; + if (ma [i] < 0) + ma [i] += MBIG; + } + } + } + } + + public static int test_2_regalloc () { + helper1 h = new helper1 (); + return 2; + } + + public static int test_0_stelemref_1 () { + object [] o = new object [1]; + o [0] = null; + + return 0; + } + + public static int test_0_stelemref_2 () { + object [] o = new object [1]; + o [0] = 1; + + return 0; + } + + interface IFace {} + class Face : IFace {} + + public static int test_0_stelemref_3 () { + object [] o = new IFace [1]; + o [0] = new Face (); + + return 0; + } + + public static int test_0_stelemref_4 () { + object [][] o = new object [5] []; + o [0] = new object [5]; + + return 0; + } + + struct FooStruct { + public int i; + + public FooStruct (int i) { + this.i = i; + } + } + + public static int test_0_arrays () { + + int sum; + + byte[] a1 = new byte [10]; + for (int i = 0; i < 10; ++i) + a1 [i] = (byte)i; + sum = 0; + for (int i = 0; i < 10; ++i) + sum += a1 [i]; + if (sum != 45) + return 1; + + sbyte[] a2 = new sbyte [10]; + for (int i = 0; i < 10; ++i) + a2 [i] = (sbyte)i; + sum = 0; + for (int i = 0; i < 10; ++i) + sum += a2 [i]; + if (sum != 45) + return 2; + + short[] a3 = new short [10]; + for (int i = 0; i < 10; ++i) + a3 [i] = (short)i; + sum = 0; + for (int i = 0; i < 10; ++i) + sum += a3 [i]; + if (sum != 45) + return 3; + + ushort[] a4 = new ushort [10]; + for (int i = 0; i < 10; ++i) + a4 [i] = (ushort)i; + sum = 0; + for (int i = 0; i < 10; ++i) + sum += a4 [i]; + if (sum != 45) + return 4; + + int[] a5 = new int [10]; + for (int i = 0; i < 10; ++i) + a5 [i] = (int)i; + sum = 0; + for (int i = 0; i < 10; ++i) + sum += a5 [i]; + if (sum != 45) + return 5; + + uint[] a6 = new uint [10]; + for (int i = 0; i < 10; ++i) + a6 [i] = (uint)i; + sum = 0; + for (int i = 0; i < 10; ++i) + sum += (int)a6 [i]; + if (sum != 45) + return 6; + + long[] a7 = new long [10]; + for (int i = 0; i < 10; ++i) + a7 [i] = i; + sum = 0; + for (int i = 0; i < 10; ++i) + sum += (int)a7 [i]; + if (sum != 45) + return 7; + + ulong[] a8 = new ulong [10]; + for (int i = 0; i < 10; ++i) + a8 [i] = (ulong)i; + sum = 0; + for (int i = 0; i < 10; ++i) + sum += (int)a8 [i]; + if (sum != 45) + return 8; + + float[] a9 = new float [10]; + for (int i = 0; i < 10; ++i) + a9 [i] = (float)i; + sum = 0; + for (int i = 0; i < 10; ++i) + sum += (int)a9 [i]; + if (sum != 45) + return 9; + + double[] a10 = new double [10]; + for (int i = 0; i < 10; ++i) + a10 [i] = i; + sum = 0; + for (int i = 0; i < 10; ++i) + sum += (int)a10 [i]; + if (sum != 45) + return 10; + + object[] a11 = new object [10]; + object o = new Object (); + for (int i = 0; i < 10; ++i) + a11 [i] = o; + for (int i = 0; i < 10; ++i) + if (a11 [i] != o) + return 11; + + FooStruct[] a12 = new FooStruct [10]; + for (int i = 0; i < 10; ++i) + a12 [i] = new FooStruct (i); + sum = 0; + for (int i = 0; i < 10; ++i) + sum += a12 [i].i; + if (sum != 45) + return 12; + + return 0; + } + + public static int test_0_multi_dimension_arrays () { + int sum; + + byte[,] a1 = new byte [10, 10]; + for (int i = 0; i < 10; ++i) + a1 [i, i] = (byte)i; + sum = 0; + for (int i = 0; i < 10; ++i) + sum += a1 [i, i]; + if (sum != 45) + return 1; + + sbyte[,] a2 = new sbyte [10, 10]; + for (int i = 0; i < 10; ++i) + a2 [i, i] = (sbyte)i; + sum = 0; + for (int i = 0; i < 10; ++i) + sum += a2 [i, i]; + if (sum != 45) + return 2; + + short[,] a3 = new short [10, 10]; + for (int i = 0; i < 10; ++i) + a3 [i, i] = (short)i; + sum = 0; + for (int i = 0; i < 10; ++i) + sum += a3 [i, i]; + if (sum != 45) + return 3; + + ushort[,] a4 = new ushort [10, 10]; + for (int i = 0; i < 10; ++i) + a4 [i, i] = (ushort)i; + sum = 0; + for (int i = 0; i < 10; ++i) + sum += a4 [i, i]; + if (sum != 45) + return 4; + + int[,] a5 = new int [10, 10]; + for (int i = 0; i < 10; ++i) + a5 [i, i] = (int)i; + sum = 0; + for (int i = 0; i < 10; ++i) + sum += a5 [i, i]; + if (sum != 45) + return 5; + + uint[,] a6 = new uint [10, 10]; + for (int i = 0; i < 10; ++i) + a6 [i, i] = (uint)i; + sum = 0; + for (int i = 0; i < 10; ++i) + sum += (int)a6 [i, i]; + if (sum != 45) + return 6; + + long[,] a7 = new long [10, 10]; + for (int i = 0; i < 10; ++i) + a7 [i, i] = i; + sum = 0; + for (int i = 0; i < 10; ++i) + sum += (int)a7 [i, i]; + if (sum != 45) + return 7; + + ulong[,] a8 = new ulong [10, 10]; + for (int i = 0; i < 10; ++i) + a8 [i, i] = (ulong)i; + sum = 0; + for (int i = 0; i < 10; ++i) + sum += (int)a8 [i, i]; + if (sum != 45) + return 8; + + float[,] a9 = new float [10, 10]; + for (int i = 0; i < 10; ++i) + a9 [i, i] = (float)i; + sum = 0; + for (int i = 0; i < 10; ++i) + sum += (int)a9 [i, i]; + if (sum != 45) + return 9; + + double[,] a10 = new double [10, 10]; + for (int i = 0; i < 10; ++i) + a10 [i, i] = i; + sum = 0; + for (int i = 0; i < 10; ++i) + sum += (int)a10 [i, i]; + if (sum != 45) + return 10; + + object[,] a11 = new object [10, 10]; + object o = new Object (); + for (int i = 0; i < 10; ++i) + a11 [i, i] = o; + for (int i = 0; i < 10; ++i) + if (a11 [i, i] != o) + return 11; + + FooStruct[,] a12 = new FooStruct [10, 10]; + for (int i = 0; i < 10; ++i) + for (int j = 0; j < 10; ++j) { + /* This one calls Address */ + a12 [i, j] = new FooStruct (i + j); + + /* Test Set as well */ + FooStruct s = new FooStruct (i + j); + a12 [i, j] = s; + } + sum = 0; + for (int i = 0; i < 10; ++i) + for (int j = 0; j < 10; ++j) { + /* This one calls Address */ + sum += a12 [i, j].i; + + /* Test Get as well */ + FooStruct s = a12 [i, j]; + sum += s.i; + } + if (sum != 1800) + return 12; + + /* Null check */ + object[,] a13 = null; + try { + a13 [0, 0] = new Object (); + return 13; + } catch (NullReferenceException) { + } + + return 0; + } + + public static int test_100_3_dimensional_arrays () { + int[,,] test = new int[10, 10, 10]; + + test [1, 1, 1] = 100; + return test [1, 1, 1]; + } + + public static int test_100_4_dimensional_arrays () { + int[,,,] test = new int[10, 10, 10, 10]; + + test [1, 1, 1, 1] = 100; + return test [1, 1, 1, 1]; + } + + public static int test_0_bug_71454 () { + int[,] a = new int[4,4]; + int[,] b = new int[4,4]; + for(int i = 0; i < 4; ++i) { + b[0,0] = a[0,i % 4]; + } + return 0; + } + + public static int test_0_interface_array_cast () { + try { + object [] a = new ICloneable [2]; + ICloneable [] b = (ICloneable [])a; + } catch { + return 1; + } + return 0; + } + + class Foo { + public static Foo[][] foo; + } + + public static int test_0_regress_74549 () { + new Foo (); + return 0; + } + + public static int test_0_regress_75832 () { + int[] table = new int[] { 0, 0 }; + + int x = 0; + + int temp = -1 ^ x; + temp = 2 + temp; + int y = table[temp]; + + return y; + } + + class RefClass { + } + + public static int test_0_stelem_ref_null_opt () { + object[] arr = new RefClass [1]; + + arr [0] = new RefClass (); + arr [0] = null; + + return arr [0] == null ? 0 : 1; + } + + public static int test_0_invalid_new_array_size () { + int size; + object res = null; + size = -1; + try { + res = new float [size]; + } catch (OverflowException e) { + + } catch (Exception) { + return 1; + } + if (res != null) + return 2; + + size = -2147483648; + try { + res = new float [size]; + } catch (OverflowException e) { + + } catch (Exception) { + return 3; + } + + if (res != null) + return 4; + + return 0; + } + + public static int test_0_multidym_array_with_negative_lower_bound () { + int[,] x = (int[,]) Array.CreateInstance(typeof (int), new int[] { 2, 2 }, new int[] { -2, -3 }); + + if(x.GetLowerBound (0) != -2) + return 1; + if (x.GetLowerBound (1) != -3) + return 2; + + x.SetValue (10, new int [] { -2, -3 }); + x.SetValue (20, new int [] { -2, -2 }); + x.SetValue (30, new int [] { -1, -3 }); + x.SetValue (40, new int [] { -1, -2 }); + + try { + x.SetValue (10, new int [] { -3, -3 }); + return 3; + } catch (IndexOutOfRangeException) { } + + try { + x.SetValue (10, new int [] { -2, -4 }); + return 4; + } catch (IndexOutOfRangeException) { } + + try { + x.SetValue (10, new int [] { 0, -3 }); + return 5; + } catch (IndexOutOfRangeException) { } + + try { + x.SetValue (10, new int [] { -1, -1 }); + return 6; + } catch (IndexOutOfRangeException) { } + + if ((int)x.GetValue (new int [] { -2, -3 }) != 10) + return 7; + if ((int)x.GetValue (new int [] { -2, -2 }) != 20) + return 8; + if ((int)x.GetValue (new int [] { -1, -3 }) != 30) + return 9; + if ((int)x.GetValue (new int [] { -1, -2 }) != 40) + return 10; + + try { + x.GetValue (new int [] { -3, -3 }); + return 11; + } catch (IndexOutOfRangeException) { } + + try { + x.GetValue ( new int [] { -2, -4 }); + return 12; + } catch (IndexOutOfRangeException) { } + + try { + x.GetValue (new int [] { 0, -3 }); + return 13; + } catch (IndexOutOfRangeException) { } + + try { + x.GetValue (new int [] { -1, -1 }); + return 14; + } catch (IndexOutOfRangeException) { } + return 0; + } + + + public static int test_0_invalid_new_multi_dym_array_size () { + int dym_size = 1; + int size; + object res = null; + size = -1; + try { + res = new float [dym_size, size]; + } catch (OverflowException e) { + + } catch (Exception) { + return 1; + } + if (res != null) + return 2; + + size = -2147483648; + try { + res = new float [size, dym_size]; + } catch (OverflowException e) { + + } catch (Exception) { + return 3; + } + + if (res != null) + return 4; + + return 0; + } + + public enum IntEnum { + A,B,C + } + + public enum UintEnum : uint { + A,B,C + } + + static bool TryCast (object o) { + return o is T[]; + } + + public static int test_0_primitive_array_cast () { + object a = new int[1]; + object b = new uint[1]; + object c = new IntEnum[1]; + object d = new UintEnum[1]; + + object[] arr = new object[] { a, b, c, d }; + int err = 1; + + foreach (var v in arr) { + if (!TryCast (v)) + return err; + if (!TryCast (v)) + return err + 1; + if (!TryCast (v)) + return err + 2; + if (!TryCast (v)) + return err + 3; + err += 4; + } + + foreach (var v in arr) { + if (!(v is int[])) + return err; + if (!(v is uint[])) + return err; + if (!(v is IntEnum[])) + return err; + if (!(v is UintEnum[])) + return err; + err += 4; + } + return 0; + } + + public static int test_0_intptr_array_cast () { + object[] a = new object[] { new int[1], new uint[1] }; + object[] b = new object[] { new long[1], new ulong[1] }; + object[] c = new object[] { new IntPtr[1], new UIntPtr[1] }; + + int err = 1; + if (IntPtr.Size == 4) { + foreach (var v in a) { + if (!(v is IntPtr[])) + return err; + if (!(v is IntPtr[])) + return err; + err += 2; + } + foreach (var v in b) { + if (v is IntPtr[]) + return err; + if (v is IntPtr[]) + return err; + err += 2; + } + + foreach (var v in c) { + if (!(v is int[])) + return err; + if (!(v is uint[])) + return err; + err += 2; + } + } else { + foreach (var v in a) { + if (v is IntPtr[]) + return err; + if (v is IntPtr[]) + return err; + err += 2; + } + foreach (var v in b) { + if (!(v is IntPtr[])) + return err; + if (!(v is IntPtr[])) + return err; + err += 2; + } + foreach (var v in c) { + if (!(v is long[])) + return err; + if (!(v is ulong[])) + return err; + err += 2; + } + } + return 0; + } + + public static int test_0_long_indices () { + int[] arr = new int [10]; + int[,] arr2 = new int [10, 10]; + long index = 1; + arr [index] = 5; + if (arr [index] != 5) + return 1; + arr2 [index, index] = 5; + if (arr2 [index, index] != 5) + return 2; + return 0; + } + + // #7438 + public static int test_0_ldelema_2_64bit () { + bool[,] test = new bool[201,201]; + int x,y; + for(x=-100;x<100;x++) for(y=-100;y<100;y++){ + test[x+100,y+100] = true; + } + return 0; + } + + static bool alloc_long (long l) { + try { + var arr = new byte[l]; + return false; + } catch (Exception e) { + return true; + } + } + + // #13544 + public static int test_0_newarr_ovf () { + if (!alloc_long (5000000000)) + return 1; + if (!alloc_long (4000000000)) + return 2; + if (!alloc_long (-1)) + return 3; + if (!alloc_long (-4000000000)) + return 4; + if (!alloc_long (-6000000000)) + return 5; + return 0; + } + + static int llvm_ldlen_licm (int[] arr) { + int sum = 0; + // The ldlen should be moved out of the loop + for (int i = 0; i < arr.Length; ++i) + sum += arr [i]; + return sum; + } + + public static int test_10_llvm_ldlen_licm () { + int[] arr = new int [10]; + for (int i = 0; i < 10; ++i) + arr [i] = 1; + return llvm_ldlen_licm (arr); + } +} + + diff --git a/unity-2019.4.24f1-mbe/mono/mini/basic-calls.cs b/unity-2019.4.24f1-mbe/mono/mini/basic-calls.cs new file mode 100644 index 000000000..ad3cdde59 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/basic-calls.cs @@ -0,0 +1,345 @@ +using System; +using System.Reflection; + +/* + * Regression tests for the mono JIT. + * + * Each test needs to be of the form: + * + * public static int test__ (); + * + * where is an integer (the value that needs to be returned by + * the method to make it pass. + * is a user-displayed name used to identify the test. + * + * The tests can be driven in two ways: + * *) running the program directly: Main() uses reflection to find and invoke + * the test methods (this is useful mostly to check that the tests are correct) + * *) with the --regression switch of the jit (this is the preferred way since + * all the tests will be run with optimizations on and off) + * + * The reflection logic could be moved to a .dll since we need at least another + * regression test file written in IL code to have better control on how + * the IL code looks. + */ + +#if __MOBILE__ +class CallsTests +#else +class Tests +#endif +{ + +#if !__MOBILE__ + public static int Main (string[] args) { + return TestDriver.RunTests (typeof (Tests), args); + } +#endif + + static void dummy () { + } + + public static int test_0_return () { + dummy (); + return 0; + } + + static int dummy1 () { + return 1; + } + + public static int test_2_int_return () { + int r = dummy1 (); + if (r == 1) + return 2; + return 0; + } + + static int add1 (int val) { + return val + 1; + } + + public static int test_1_int_pass () { + int r = add1 (5); + if (r == 6) + return 1; + return 0; + } + + static int add_many (int val, short t, byte b, int da) { + return val + t + b + da; + } + + public static int test_1_int_pass_many () { + byte b = 6; + int r = add_many (5, 2, b, 1); + if (r == 14) + return 1; + return 0; + } + + unsafe static float GetFloat (byte *ptr) { + return *(float*)ptr; + } + + unsafe public static float GetFloat(float value) + { + return GetFloat((byte *)&value); + } + + /* bug #42134 */ + public static int test_2_inline_saved_arg_type () { + float f = 100.0f; + return GetFloat (f) == f? 2: 1; + } + + static int pass_many_types (int a, long b, int c, long d) { + return a + (int)b + c + (int)d; + } + + public static int test_5_pass_longs () { + return pass_many_types (1, 2, -5, 7); + } + + static int overflow_registers (int a, int b, int c, int d, int e, int f, int g, int h, int i, int j) { + return a+b+c+d+e+f+g+h+i+j; + } + + public static int test_55_pass_even_more () { + return overflow_registers (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + } + + static int pass_ints_longs (int a, long b, long c, long d, long e, int f, long g) { + return (int)(a + b + c + d + e + f + g); + } + + public static int test_1_sparc_argument_passing () { + // The 4. argument tests split reg/mem argument passing + // The 5. argument tests mem argument passing + // The 7. argument tests passing longs in misaligned memory + // The MaxValues are needed so the MS word of the long is not 0 + return pass_ints_longs (1, 2, System.Int64.MaxValue, System.Int64.MinValue, System.Int64.MaxValue, 0, System.Int64.MinValue); + } + + static int pass_bytes (byte a, byte b, byte c, byte d, byte e, byte f, byte g) { + return (int)(a + b + c + d + e + f + g); + } + + public static int test_21_sparc_byte_argument_passing () { + return pass_bytes (0, 1, 2, 3, 4, 5, 6); + } + + static int pass_sbytes (sbyte a, sbyte b, sbyte c, sbyte d, sbyte e, sbyte f, sbyte g, sbyte h1, sbyte h2, sbyte h3, sbyte h4) { + return (int)(a + b + c + d + e + f + g + h1 + h2 + h3 + h4); + } + + public static int test_55_sparc_sbyte_argument_passing () { + return pass_sbytes (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + } + + static int pass_shorts (short a, short b, short c, short d, short e, short f, short g) { + return (int)(a + b + c + d + e + f + g); + } + + public static int test_21_sparc_short_argument_passing () { + return pass_shorts (0, 1, 2, 3, 4, 5, 6); + } + + static int pass_floats_doubles (float a, double b, double c, double d, double e, float f, double g) { + return (int)(a + b + c + d + e + f + g); + } + + public static int test_721_sparc_float_argument_passing () { + return pass_floats_doubles (100.0f, 101.0, 102.0, 103.0, 104.0, 105.0f, 106.0); + } + + static float pass_floats (float a, float b, float c, float d, float e, float f, float g, float h, float i, float j) { + return a + b + c + d + e + f + g + h + i + j; + } + + public static int test_55_sparc_float_argument_passing2 () { + return (int)pass_floats (1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f); + } + + public static bool is_small (float value) { + double d = (double)value; + double d2 = 7.183757E-41; + return d - d2 < 0.000001; + } + + public static int test_0_float_argument_passing_precision () { + float f = 7.183757E-41f; + return is_small (f) ? 0 : 1; + } + + // The first argument must be passed on a dword aligned stack location + static int pass_byref_ints_longs (ref long a, ref int b, ref byte c, ref short d, ref long e, ref int f, ref long g) { + return (int)(a + b + c + d + e + f + g); + } + + static int pass_takeaddr_ints_longs (long a, int b, byte c, short d, long e, int f, long g) { + return pass_byref_ints_longs (ref a, ref b, ref c, ref d, ref e, ref f, ref g); + } + + // Test that arguments are moved to the stack from incoming registers + // when the argument must reside in the stack because its address is taken + public static int test_2_sparc_takeaddr_argument_passing () { + return pass_takeaddr_ints_longs (1, 2, 253, -253, System.Int64.MaxValue, 0, System.Int64.MinValue); + } + + static int pass_byref_floats_doubles (ref float a, ref double b, ref double c, ref double d, ref double e, ref float f, ref double g) { + return (int)(a + b + c + d + e + f + g); + } + + static int pass_takeaddr_floats_doubles (float a, double b, double c, double d, double e, float f, double g) { + return pass_byref_floats_doubles (ref a, ref b, ref c, ref d, ref e, ref f, ref g); + } + + public static int test_721_sparc_takeaddr_argument_passing2 () { + return pass_takeaddr_floats_doubles (100.0f, 101.0, 102.0, 103.0, 104.0, 105.0f, 106.0); + } + + static void pass_byref_double (out double d) { + d = 5.0; + } + + // Test byref double argument passing + public static int test_0_sparc_byref_double_argument_passing () { + double d; + pass_byref_double (out d); + return (d == 5.0) ? 0 : 1; + } + + static void shift_un_arg (ulong value) { + do { + value = value >> 4; + } while (value != 0); + } + + // Test that assignment to long arguments work + public static int test_0_long_arg_assign () + { + ulong c = 0x800000ff00000000; + + shift_un_arg (c >> 4); + + return 0; + } + + static unsafe void* ptr_return (void *ptr) + { + return ptr; + } + + public static unsafe int test_0_ptr_return () + { + void *ptr = new IntPtr (55).ToPointer (); + + if (ptr_return (ptr) == ptr) + return 0; + else + return 1; + } + + static bool isnan (float f) { + return (f != f); + } + + public static int test_0_isnan () { + float f = 1.0f; + return isnan (f) ? 1 : 0; + } + + static int first_is_zero (int v1, int v2) { + if (v1 != 0) + return -1; + return v2; + } + public static int test_1_handle_dup_stloc () { + int index = 0; + int val = first_is_zero (index, ++index); + if (val != 1) + return 2; + return 1; + } + + static long return_5low () { + return 5; + } + + static long return_5high () { + return 0x500000000; + } + + public static int test_3_long_ret () { + long val = return_5low (); + return (int) (val - 2); + } + + public static int test_1_long_ret2 () { + long val = return_5high (); + if (val > 0xffffffff) + return 1; + return 0; + } + + public static void use_long_arg (ulong l) { + for (int i = 0; i < 10; ++i) + l ++; + } + + public static ulong return_long_arg (object o, ulong perm) { + use_long_arg (perm); + + perm = 0x8000000000000FFF; + + use_long_arg (perm); + + return perm; + } + + public static int test_0_sparc_long_ret_regress_541577 () { + ulong perm = 0x8000000000000FFF; + + ulong work = return_long_arg (null, perm); + + return work == perm ? 0 : 1; + } + + static void doit (double value, out long m) { + m = (long) value; + } + + public static int test_0_ftol_clobber () { + long m; + doit (1.3, out m); + if (m != 1) + return 2; + return 0; + } + + public static bool arm64_stack_arg_reg_bool (object o1, object o2, object o3, object o4, object o5, object o6, object o7, + bool foo, bool bar) { + bool res1 = bar || foo; + bool res2 = bar || foo; + return res1 | res2; + } + + public static int arm64_stack_arg_reg_sbyte (object o1, object o2, object o3, object o4, object o5, object o6, object o7, + sbyte foo, sbyte bar) { + int res1 = bar + foo; + int res2 = bar + foo; + return res1 + res2; + } + + // bool argument passed on the stack and promoted to a register + public static int test_0_arm64_stack_arg_reg_bool () { + bool res = arm64_stack_arg_reg_bool (null, null, null, null, null, null, null, false, false); + return res ? 1 : 0; + } + + public static int test_0_arm64_stack_arg_reg_sbyte () { + int res = arm64_stack_arg_reg_sbyte (null, null, null, null, null, null, null, -4, -7); + return res == -22 ? 0 : 1; + } +} diff --git a/unity-2019.4.24f1-mbe/mono/mini/basic-float.cs b/unity-2019.4.24f1-mbe/mono/mini/basic-float.cs new file mode 100644 index 000000000..e6a14456f --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/basic-float.cs @@ -0,0 +1,824 @@ +using System; +using System.Reflection; + +/* + * Regression tests for the mono JIT. + * + * Each test needs to be of the form: + * + * public static int test__ (); + * + * where is an integer (the value that needs to be returned by + * the method to make it pass. + * is a user-displayed name used to identify the test. + * + * The tests can be driven in two ways: + * *) running the program directly: Main() uses reflection to find and invoke + * the test methods (this is useful mostly to check that the tests are correct) + * *) with the --regression switch of the jit (this is the preferred way since + * all the tests will be run with optimizations on and off) + * + * The reflection logic could be moved to a .dll since we need at least another + * regression test file written in IL code to have better control on how + * the IL code looks. + */ + +/* A comparison made to same variable. */ +#pragma warning disable 1718 + +#if __MOBILE__ +class FloatTests +#else +class Tests +#endif +{ + +#if !__MOBILE__ + public static int Main (string[] args) { + return TestDriver.RunTests (typeof (Tests), args); + } +#endif + + public static int test_0_beq () { + double a = 2.0; + if (a != 2.0) + return 1; + return 0; + } + + public static int test_0_bne_un () { + double a = 2.0; + if (a == 1.0) + return 1; + return 0; + } + + public static int test_0_conv_r8 () { + double a = 2; + if (a != 2.0) + return 1; + return 0; + } + + public static int test_0_conv_i () { + double a = 2.0; + int i = (int)a; + if (i != 2) + return 1; + uint ui = (uint)a; + if (ui != 2) + return 2; + short s = (short)a; + if (s != 2) + return 3; + ushort us = (ushort)a; + if (us != 2) + return 4; + byte b = (byte)a; + if (b != 2) + return 5; + sbyte sb = (sbyte)a; + if (sb != 2) + return 6; + /* MS.NET special cases these */ + double d = Double.NaN; + ui = (uint)d; + if (ui != 0) + return 7; + d = Double.PositiveInfinity; + ui = (uint)d; + if (ui != 0) + return 8; + d = Double.NegativeInfinity; + ui = (uint)d; + if (ui != 0) + return 9; + /* FIXME: This fails with llvm and with gcc -O2 on osx/linux */ + /* + d = Double.MaxValue; + i = (int)d; + if (i != -2147483648) + return 10; + */ + + return 0; + } + + public static int test_5_conv_r4 () { + int i = 5; + float f = (float)i; + return (int)f; + } + + public static int test_0_conv_r4_m1 () { + int i = -1; + float f = (float)i; + return (int)f + 1; + } + + public static int test_5_double_conv_r4 () { + double d = 5.0; + float f = (float)d; + return (int)f; + } + + public static int test_5_float_conv_r8 () { + float f = 5.0F; + double d = (double)f; + return (int)d; + } + + public static int test_5_conv_r8 () { + int i = 5; + double f = (double)i; + return (int)f; + } + + public static int test_5_add () { + double a = 2.0; + double b = 3.0; + return (int)(a + b); + } + + public static int test_5_sub () { + double a = 8.0; + double b = 3.0; + return (int)(a - b); + } + + public static int test_24_mul () { + double a = 8.0; + double b = 3.0; + return (int)(a * b); + } + + public static int test_4_div () { + double a = 8.0; + double b = 2.0; + return (int)(a / b); + } + + public static int test_2_rem () { + double a = 8.0; + double b = 3.0; + return (int)(a % b); + } + + public static int test_2_neg () { + double a = -2.0; + return (int)(-a); + } + + public static int test_46_float_add_spill () { + // we overflow the FP stack + double a = 1; + double b = 2; + double c = 3; + double d = 4; + double e = 5; + double f = 6; + double g = 7; + double h = 8; + double i = 9; + + return (int)(1.0 + (a + (b + (c + (d + (e + (f + (g + (h + i))))))))); + } + + public static int test_4_float_sub_spill () { + // we overflow the FP stack + double a = 1; + double b = 2; + double c = 3; + double d = 4; + double e = 5; + double f = 6; + double g = 7; + double h = 8; + double i = 9; + + return -(int)(1.0 - (a - (b - (c - (d - (e - (f - (g - (h - i))))))))); + ////// -(int)(1.0 - (1 - (2 - (3 - (4 - (5 - (6 - (7 - (8 - 9))))))))); + } + + public static int test_362880_float_mul_spill () { + // we overflow the FP stack + double a = 1; + double b = 2; + double c = 3; + double d = 4; + double e = 5; + double f = 6; + double g = 7; + double h = 8; + double i = 9; + + return (int)(1.0 * (a * (b * (c * (d * (e * (f * (g * (h * i))))))))); + } + + public static int test_4_long_cast () { + long a = 1000; + double d = (double)a; + long b = (long)d; + if (b != 1000) + return 0; + a = -1; + d = (double)a; + b = (long)d; + if (b != -1) + return 1; + return 4; + } + + public static int test_4_ulong_cast () { + ulong a = 1000; + double d = (double)a; + ulong b = (ulong)d; + if (b != 1000) + return 0; + a = 0xffffffffffffffff; + float f = (float)a; + if (!(f > 0f)) + return 1; + return 4; + } + + public static int test_4_single_long_cast () { + long a = 1000; + float d = (float)a; + long b = (long)d; + if (b != 1000) + return 0; + a = -1; + d = (float)a; + b = (long)d; + if (b != -1) + return 1; + return 4; + } + + public static int test_0_lconv_to_r8 () { + long a = 150; + double b = (double) a; + + if (b != 150.0) + return 1; + return 0; + } + + public static int test_0_lconv_to_r4 () { + long a = 3000; + float b = (float) a; + + if (b != 3000.0F) + return 1; + return 0; + } + + static void doit (double value, out long m) { + m = (long) value; + } + + public static int test_0_ftol_clobber () { + long m; + doit (1.3, out m); + if (m != 1) + return 2; + return 0; + } + + public static int test_0_rounding () { + long ticks = 631502475130080000L; + long ticksperday = 864000000000L; + + double days = (double) ticks / ticksperday; + + if ((int)days != 730905) + return 1; + + return 0; + } + + /* FIXME: This only works on little-endian machines */ + /* + static unsafe int test_2_negative_zero () { + int result = 0; + double d = -0.0; + float f = -0.0f; + + byte *ptr = (byte*)&d; + if (ptr [7] == 0) + return result; + result ++; + + ptr = (byte*)&f; + if (ptr [3] == 0) + return result; + result ++; + + return result; + } + */ + + public static int test_16_float_cmp () { + double a = 2.0; + double b = 1.0; + int result = 0; + bool val; + + val = a == a; + if (!val) + return result; + result++; + + val = (a != a); + if (val) + return result; + result++; + + val = a < a; + if (val) + return result; + result++; + + val = a > a; + if (val) + return result; + result++; + + val = a <= a; + if (!val) + return result; + result++; + + val = a >= a; + if (!val) + return result; + result++; + + val = b == a; + if (val) + return result; + result++; + + val = b < a; + if (!val) + return result; + result++; + + val = b > a; + if (val) + return result; + result++; + + val = b <= a; + if (!val) + return result; + result++; + + val = b >= a; + if (val) + return result; + result++; + + val = a == b; + if (val) + return result; + result++; + + val = a < b; + if (val) + return result; + result++; + + val = a > b; + if (!val) + return result; + result++; + + val = a <= b; + if (val) + return result; + result++; + + val = a >= b; + if (!val) + return result; + result++; + + return result; + } + + public static int test_15_float_cmp_un () { + double a = Double.NaN; + double b = 1.0; + int result = 0; + bool val; + + val = a == a; + if (val) + return result; + result++; + + val = a < a; + if (val) + return result; + result++; + + val = a > a; + if (val) + return result; + result++; + + val = a <= a; + if (val) + return result; + result++; + + val = a >= a; + if (val) + return result; + result++; + + val = b == a; + if (val) + return result; + result++; + + val = b < a; + if (val) + return result; + result++; + + val = b > a; + if (val) + return result; + result++; + + val = b <= a; + if (val) + return result; + result++; + + val = b >= a; + if (val) + return result; + result++; + + val = a == b; + if (val) + return result; + result++; + + val = a < b; + if (val) + return result; + result++; + + val = a > b; + if (val) + return result; + result++; + + val = a <= b; + if (val) + return result; + result++; + + val = a >= b; + if (val) + return result; + result++; + + return result; + } + + public static int test_15_float_branch () { + double a = 2.0; + double b = 1.0; + int result = 0; + + if (!(a == a)) + return result; + result++; + + if (a < a) + return result; + result++; + + if (a > a) + return result; + result++; + + if (!(a <= a)) + return result; + result++; + + if (!(a >= a)) + return result; + result++; + + if (b == a) + return result; + result++; + + if (!(b < a)) + return result; + result++; + + if (b > a) + return result; + result++; + + if (!(b <= a)) + return result; + result++; + + if (b >= a) + return result; + result++; + + if (a == b) + return result; + result++; + + if (a < b) + return result; + result++; + + if (!(a > b)) + return result; + result++; + + if (a <= b) + return result; + result++; + + if (!(a >= b)) + return result; + result++; + + return result; + } + + public static int test_15_float_branch_un () { + double a = Double.NaN; + double b = 1.0; + int result = 0; + + if (a == a) + return result; + result++; + + if (a < a) + return result; + result++; + + if (a > a) + return result; + result++; + + if (a <= a) + return result; + result++; + + if (a >= a) + return result; + result++; + + if (b == a) + return result; + result++; + + if (b < a) + return result; + result++; + + if (b > a) + return result; + result++; + + if (b <= a) + return result; + result++; + + if (b >= a) + return result; + result++; + + if (a == b) + return result; + result++; + + if (a < b) + return result; + result++; + + if (a > b) + return result; + result++; + + if (a <= b) + return result; + result++; + + if (a >= b) + return result; + result++; + + return result; + } + + public static int test_0_float_precision () { + float f1 = 3.40282346638528859E+38f; + float f2 = 3.40282346638528859E+38f; + float PositiveInfinity = (float)(1.0f / 0.0f); + float f = (float)(f1 + f2); + + return f == PositiveInfinity ? 0 : 1; + } + + static double VALUE = 0.19975845134874831D; + + public static int test_0_float_conversion_reduces_double_precision () { + double d = (float)VALUE; + if (d != 0.19975845515727997d) + return 1; + + return 0; + } + + /* This doesn't work with llvm */ + /* + public static int test_0_long_to_double_conversion () + { + long l = 9223372036854775807L; + long conv = (long)((double)l); + if (conv != -9223372036854775808L) + return 1; + + return 0; + } + */ + + public static int INT_VAL = 0x13456799; + + public static int test_0_int4_to_float_convertion () + { + double d = (double)(float)INT_VAL; + + if (d != 323315616) + return 1; + return 0; + } + + public static int test_0_int8_to_float_convertion () + { + double d = (double)(float)(long)INT_VAL; + + if (d != 323315616) + return 1; + return 0; + } + + public static int test_5_r4_fadd () { + float f1 = 3.0f; + float f2 = 2.0f; + return (int)(f1 + f2); + } + + public static int test_1_r4_fsub () { + float f1 = 3.0f; + float f2 = 2.0f; + return (int)(f1 - f2); + } + + public static int test_6_fmul_r4 () { + float f1 = 2.0f; + float f2 = 3.0f; + return (int)(f1 * f2); + } + + public static int test_3_fdiv_r4 () { + float f1 = 6.0f; + float f2 = 2.0f; + return (int)(f1 / f2); + } + + public static int test_1_frem_r4 () { + float f1 = 7.0f; + float f2 = 2.0f; + return (int)(f1 % f2); + } + + public static int test_0_fcmp_eq_r4 () { + float f1 = 1.0f; + float f2 = 1.0f; + return f1 == f2 ? 0 : 1; + } + + public static int test_0_fcmp_eq_2_r4 () { + float f1 = 1.0f; + float f2 = 2.0f; + return f1 == f2 ? 1 : 0; + } + + public static int test_0_fcmp_eq_r4_mixed () { + float f1 = 1.0f; + double f2 = 1.0; + return f1 == f2 ? 0 : 1; + } + + public static int test_3_iconv_to_r4 () { + int i = 3; + float f = (float)i; + return (int)f; + } + + public static int test_2_neg_r4 () { + float a = -2.0f; + return (int)(-a); + } + + public static int test_0_fceq_r4 () { + float f1 = 1.0f; + float f2 = 1.0f; + bool res = f1 == f2; + return res ? 0 : 1; + } + + public static int test_0_fcgt_r4 () { + float f1 = 2.0f; + float f2 = 1.0f; + bool res = f1 > f2; + bool res2 = f2 > f1; + return res && !res2 ? 0 : 1; + } + + public static int test_0_fclt_r4 () { + float f1 = 1.0f; + float f2 = 2.0f; + bool res = f1 < f2; + bool res2 = f2 < f1; + return res && !res2 ? 0 : 1; + } + + public static int test_0_fclt_un_r4 () { + float f1 = 2.0f; + float f2 = 1.0f; + bool res = f1 >= f2; + bool res2 = f2 >= f1; + return res && !res2 ? 0 : 1; + } + + public static int test_0_fcgt_un_r4 () { + float f1 = 1.0f; + float f2 = 2.0f; + bool res = f1 <= f2; + bool res2 = f2 <= f1; + return res && !res2 ? 0 : 1; + } + + public static int test_0_fconv_to_u4_r4 () { + float a = 10.0f; + + uint b = (uint)a; + return b == 10 ? 0 : 1; + } + + public static int test_0_fconv_to_u1_r4 () { + float a = 10.0f; + + byte b = (byte)a; + return b == 10 ? 0 : 1; + } + + public static int test_0_fconv_to_i1_r4 () { + float a = 127.0f; + + sbyte b = (sbyte)a; + return b == 127 ? 0 : 1; + } + + public static int test_0_fconv_to_u2_r4 () { + float a = 10.0f; + + ushort b = (ushort)a; + return b == 10 ? 0 : 1; + } + + public static int test_0_fconv_to_i2_r4 () { + float a = 127.0f; + + short b = (short)a; + return b == 127 ? 0 : 1; + } + + public static int test_10_rconv_to_u8 () { + ulong l = 10; + float f = (float)l; + l = (ulong)f; + return (int)l; + } + +} + diff --git a/unity-2019.4.24f1-mbe/mono/mini/basic-long.cs b/unity-2019.4.24f1-mbe/mono/mini/basic-long.cs new file mode 100644 index 000000000..82eb5befe --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/basic-long.cs @@ -0,0 +1,1267 @@ +using System; +using System.Reflection; + +/* + * Regression tests for the mono JIT. + * + * Each test needs to be of the form: + * + * public static int test__ (); + * + * where is an integer (the value that needs to be returned by + * the method to make it pass. + * is a user-displayed name used to identify the test. + * + * The tests can be driven in two ways: + * *) running the program directly: Main() uses reflection to find and invoke + * the test methods (this is useful mostly to check that the tests are correct) + * *) with the --regression switch of the jit (this is the preferred way since + * all the tests will be run with optimizations on and off) + * + * The reflection logic could be moved to a .dll since we need at least another + * regression test file written in IL code to have better control on how + * the IL code looks. + */ + +#if __MOBILE__ +class LongTests +#else +class Tests +#endif +{ + +#if !__MOBILE__ + public static int Main (string[] args) { + return TestDriver.RunTests (typeof (Tests), args); + } +#endif + + public static int test_10_simple_cast () { + long a = 10; + return (int)a; + } + + public static int test_1_bigmul1 () { + int a; + int b; + long c; + a = 10; + b = 10; + c = (long)a * (long)b; + if (c == 100) + return 1; + return 0; + } + + public static int test_1_bigmul2 () { + int a = System.Int32.MaxValue, b = System.Int32.MaxValue; + long s = System.Int64.MinValue; + long c; + c = s + (long) a * (long) b; + if (c == -4611686022722355199) + return 1; + return 0; + } + + public static int test_1_bigmul3 () { + int a = 10, b = 10; + ulong c; + c = (ulong) a * (ulong) b; + if (c == 100) + return 1; + return 0; + } + + public static int test_1_bigmul4 () { + int a = System.Int32.MaxValue, b = System.Int32.MaxValue; + ulong c; + c = (ulong) a * (ulong) b; + if (c == 4611686014132420609) + return 1; + return 0; + } + + public static int test_1_bigmul5 () { + int a = System.Int32.MaxValue, b = System.Int32.MinValue; + long c; + c = (long) a * (long) b; + if (c == -4611686016279904256) + return 1; + return 0; + } + + public static int test_1_bigmul6 () { + uint a = System.UInt32.MaxValue, b = System.UInt32.MaxValue/(uint)2; + ulong c; + c = (ulong) a * (ulong) b; + if (c == 9223372030412324865) + return 1; + return 0; + } + + public static int test_0_beq () { + long a = 0xffffffffff; + if (a != 0xffffffffff) + return 1; + return 0; + } + + public static int test_0_bne_un () { + long a = 0xffffffffff; + if (a == 0xfffffffffe) + return 1; + if (a == 0xfeffffffff) + return 2; + return 0; + } + + public static int test_0_ble () { + long a = 0xffffffffff; + if (a > 0xffffffffff) + return 1; + + if (a > 0x1ffffffffff) + return 2; + + if (a > 0xff00000000) {} else + return 3; + + if (a > 0xfeffffffff) {} else + return 4; + + a = 0xff00000000; + if (a > 0xffffffffff) + return 5; + + return 0; + } + + public static int test_0_ble_un () { + ulong a = 0xffffffffff; + if (a > 0xffffffffff) + return 1; + + if (a > 0x1ffffffffff) + return 2; + + if (a > 0xff00000000) {} else + return 3; + + if (a > 0xfeffffffff) {} else + return 4; + + a = 0xff00000000; + if (a > 0xffffffffff) + return 5; + + return 0; + } + + public static int test_0_bge () { + long a = 0xffffffffff; + if (a < 0xffffffffff) + return 1; + + if (a < 0x1ffffffffff) {} else + return 2; + + if (a < 0xff00000000) + return 3; + + if (a < 0xfeffffffff) + return 4; + + a = 0xff00000000; + if (a < 0xffffffffff) {} else + return 5; + + return 0; + } + + public static int test_0_bge_un () { + ulong a = 0xffffffffff; + if (a < 0xffffffffff) + return 1; + + if (a < 0x1ffffffffff) {} else + return 2; + + if (a < 0xff00000000) + return 3; + + if (a < 0xfeffffffff) + return 4; + + a = 0xff00000000; + if (a < 0xffffffffff) {} else + return 5; + + return 0; + } + + public static int test_0_blt () { + long a = 0xfffffffffe; + if (a >= 0xffffffffff) + return 1; + + if (a >= 0x1fffffffffe) + return 2; + + if (a >= 0xff00000000) {} else + return 3; + + if (a >= 0xfefffffffe) {} else + return 4; + + a = 0xff00000000; + if (a >= 0xffffffffff) + return 5; + + return 0; + } + + public static int test_0_blt_un () { + ulong a = 0xfffffffffe; + if (a >= 0xffffffffff) + return 1; + + if (a >= 0x1fffffffffe) + return 2; + + if (a >= 0xff00000000) {} else + return 3; + + if (a >= 0xfefffffffe) {} else + return 4; + + a = 0xff00000000; + if (a >= 0xffffffffff) + return 5; + + return 0; + } + + public static int test_0_bgt () { + long a = 0xffffffffff; + if (a <= 0xfffffffffe) + return 1; + + if (a <= 0x1ffffffffff) {} else + return 2; + + if (a <= 0xff00000000) + return 3; + + if (a <= 0xfeffffffff) + return 4; + + a = 0xff00000000; + if (a <= 0xffffffffff) {} else + return 5; + + return 0; + } + + public static int test_0_bgt_un () { + ulong a = 0xffffffffff; + if (a <= 0xfffffffffe) + return 1; + + if (a <= 0x1ffffffffff) {} else + return 2; + + if (a <= 0xff00000000) + return 3; + + if (a <= 0xfeffffffff) + return 4; + + a = 0xff00000000; + if (a <= 0xffffffffff) {} else + return 5; + + return 0; + } + + public static int test_0_conv_to_i4 () { + long a = 0; + + return (int)a; + } + + public static int test_32_conv_to_u4 () { + long a = 32; + + return (int)(uint)a; + } + + public static int test_15_conv_to_u4_2 () { + long a = 0x10000000f; + + return (int)(uint)a; + } + + public static int test_0_conv_from_i4 () { + long a = 2; + if (a != 2) + return 1; + + int b = 2; + + if (a != b) + return 2; + return 0; + } + + public static int test_0_conv_from_i4_negative () { + long a = -2; + if (a != -2) + return 1; + + int b = -2; + + if (a != b) + return 2; + return 0; + } + + /* + public static int test_0_conv_from_r8 () { + double b = 2.0; + long a = (long)b; + + if (a != 2) + return 1; + return 0; + } + + public static int test_0_conv_from_r4 () { + float b = 2.0F; + long a = (long)b; + + if (a != 2) + return 1; + return 0; + } + */ + + public static int test_8_and () { + long a = 0xffffffffff; + long b = 8; + return (int)(a & b); + } + + public static int test_8_and_imm () { + long a = 0xffffffffff; + return (int)(a & 8); + } + + public static int get_high_bit (ulong a) { + if ((a & 0x8000000000000000) != 0) + return 1; + return 0; + } + + public static int test_1_and () { + ulong a = 0xabcd1234deadbeef; + return get_high_bit (a); + } + + public static int test_10_or () { + long a = 8; + long b = 2; + return (int)(a | b); + } + + public static int test_10_or_imm () { + long a = 8; + return (int)(a | 2); + } + + public static int test_5_xor () { + long a = 7; + long b = 2; + return (int)(a ^ b); + } + + public static int test_5_xor_imm () { + long a = 7; + return (int)(a ^ 2); + } + + public static int test_5_add () { + long a = 2; + long b = 3; + return (int)(a + b); + } + + public static int test_5_add_imm () { + long a = 2; + return (int)(a + 3); + } + + public static int test_0_add_imm_carry () { + long a = -1; + return (int)(a + 1); + } + + public static int test_0_add_imm_no_inc () { + // we can't blindly convert an add x, 1 to an inc x + long a = 0x1ffffffff; + long c; + c = a + 2; + if (c == ((a + 1) + 1)) + return 0; + return 1; + } + + public static int test_4_addcc_imm () { + long a = 3; + long b = 0; + return (int)(a - b + 1); + } + + public static int test_5_sub () { + long a = 8; + long b = 3; + return (int)(a - b); + } + + public static int test_5_sub_imm () { + long a = 8; + return (int)(a - 3); + } + + public static int test_0_sub_imm_carry () { + long a = 0; + return (int)((a - 1) + 1); + } + + public static int test_0_add_ovf () { + long i, j, k; + + checked { + i = System.Int64.MinValue; + j = 0; + k = i + j; + } + + if (k != System.Int64.MinValue) + return 1; + + checked { + i = System.Int64.MaxValue; + j = 0; + k = i + j; + } + + if (k != System.Int64.MaxValue) + return 2; + + checked { + i = System.Int64.MinValue; + j = System.Int64.MaxValue; + k = i + j; + } + + if (k != -1) + return 3; + + checked { + i = System.Int64.MaxValue; + j = System.Int64.MinValue; + k = i + j; + } + + if (k != -1) + return 4; + + checked { + i = System.Int64.MinValue + 1234; + j = -1234; + k = i + j; + } + + if (k != System.Int64.MinValue) + return 5; + + checked { + i = System.Int64.MaxValue - 1234; + j = 1234; + k = i + j; + } + + if (k != System.Int64.MaxValue) + return 6; + + return 0; + } + + public static int test_0_add_un_ovf () { + ulong n = (ulong)134217728 * 16; + ulong number = checked (n + (uint)0); + + return number == n ? 0 : 1; + } + + public static int test_0_sub_ovf () { + long i, j, k; + + checked { + i = System.Int64.MinValue; + j = 0; + k = i - j; + } + + if (k != System.Int64.MinValue) + return 1; + + checked { + i = System.Int64.MaxValue; + j = 0; + k = i - j; + } + + if (k != System.Int64.MaxValue) + return 2; + + checked { + i = System.Int64.MinValue; + j = System.Int64.MinValue + 1234; + k = i - j; + } + + if (k != -1234) + return 3; + + checked { + i = System.Int64.MaxValue; + j = 1234; + k = i - j; + } + + if (k != System.Int64.MaxValue - 1234) + return 4; + + checked { + i = System.Int64.MaxValue - 1234; + j = -1234; + k = i - j; + } + + if (k != System.Int64.MaxValue) + return 5; + + checked { + i = System.Int64.MinValue + 1234; + j = 1234; + k = i - j; + } + + if (k != System.Int64.MinValue) + return 6; + + return 0; + } + + public static int test_0_sub_ovf_un () { + ulong i, j, k; + + checked { + i = System.UInt64.MaxValue; + j = 0; + k = i - j; + } + + if (k != System.UInt64.MaxValue) + return 1; + + checked { + i = System.UInt64.MaxValue; + j = System.UInt64.MaxValue; + k = i - j; + } + + if (k != 0) + return 2; + + return 0; + } + + public static int test_2_neg () { + long a = -2; + return (int)(-a); + } + + public static int test_0_neg_large () { + long min = -9223372036854775808; + unchecked { + ulong ul = (ulong)min; + return (min == -(long)ul) ? 0 : 1; + } + } + + public static int test_5_shift () + { + long a = 9; + int b = 1; + int count = 0; + + if ((a >> b) != 4) + return count; + count++; + + if ((a >> 63) != 0) + return count; + count++; + + if ((a << 1) != 18) + return count; + count++; + + if ((a << b) != 18) + return count; + count++; + + a = -9; + if ((a >> b) != -5) + return count; + count++; + + return count; + } + + public static int test_1_shift_u () + { + ulong a; + int count = 0; + + // The JIT optimizes this + a = 8589934592UL; + if ((a >> 32) != 2) + return 0; + count ++; + + return count; + } + + public static int test_1_shift_u_32 () + { + ulong a; + int count = 0; + + a = UInt64.MaxValue; + // Avoid constant folding + for (int i = 0; i < 32; ++i) + count ++; + + if ((a >> count) != 0xFFFFFFFFUL) + return 0; + else + return 1; + } + + public static int test_1_simple_neg () { + long a = 9; + + if (-a != -9) + return 0; + return 1; + } + + public static int test_2_compare () { + long a = 1; + long b = 1; + + if (a != b) + return 0; + return 2; + } + + public static int test_9_alu () + { + long a = 9, b = 6; + int count = 0; + + if ((a + b) != 15) + return count; + count++; + + if ((a - b) != 3) + return count; + count++; + + if ((a & 8) != 8) + return count; + count++; + + if ((a | 2) != 11) + return count; + count++; + + if ((a * b) != 54) + return count; + count++; + + if ((a / 4) != 2) + return count; + count++; + + if ((a % 4) != 1) + return count; + count++; + + if (-a != -9) + return count; + count++; + + b = -1; + if (~b != 0) + return count; + count++; + + return count; + } + + public static int test_24_mul () { + long a = 8; + long b = 3; + return (int)(a * b); + } + + public static int test_24_mul_ovf () { + long a = 8; + long b = 3; + long res; + + checked { + res = a * b; + } + return (int)res; + } + + public static int test_24_mul_un () { + ulong a = 8; + ulong b = 3; + return (int)(a * b); + } + + public static int test_24_mul_ovf_un () { + ulong a = 8; + ulong b = 3; + ulong res; + + checked { + res = a * b; + } + return (int)res; + } + + public static int test_0_mul_imm () { + long i = 4; + + if ((i * 0) != 0) + return 1; + if ((i * 1) != 4) + return 2; + if ((i * 2) != 8) + return 3; + if ((i * 3) != 12) + return 4; + if ((i * 1234) != 4936) + return 5; + if ((i * -1) != -4) + return 6; + if ((i * -2) != -8) + return 7; + if ((i * -3) != -12) + return 8; + if ((i * -1234) != -4936) + return 9; + + return 0; + } + + public static int test_0_mul_imm_opt () + { + long i; + + i = 1; + if ((i * 2) != 2) + return 1; + i = -1; + if ((i * 2) != -2) + return 2; + i = 1; + if ((i * 3) != 3) + return 3; + i = -1; + if ((i * 3) != -3) + return 4; + i = 1; + if ((i * 5) != 5) + return 5; + i = -1; + if ((i * 5) != -5) + return 6; + i = 1; + if ((i * 6) != 6) + return 7; + i = -1; + if ((i * 6) != -6) + return 8; + i = 1; + if ((i * 9) != 9) + return 9; + i = -1; + if ((i * 9) != -9) + return 10; + i = 1; + if ((i * 10) != 10) + return 11; + i = -1; + if ((i * 10) != -10) + return 12; + i = 1; + if ((i * 12) != 12) + return 13; + i = -1; + if ((i * 12) != -12) + return 14; + i = 1; + if ((i * 25) != 25) + return 15; + i = -1; + if ((i * 25) != -25) + return 16; + i = 1; + if ((i * 100) != 100) + return 17; + i = -1; + if ((i * 100) != -100) + return 18; + + return 0; + } + + public static int test_4_divun () { + uint b = 12; + int a = 3; + return (int)(b / a); + } + + public static int test_1431655764_bigdivun_imm () { + unchecked { + uint b = (uint)-2; + return (int)(b / 3); + } + } + + public static int test_1431655764_bigdivun () { + unchecked { + uint b = (uint)-2; + int a = 3; + return (int)(b / a); + } + } + + public static int test_1_remun () { + uint b = 13; + int a = 3; + return (int)(b % a); + } + + public static int test_2_bigremun () { + unchecked { + uint b = (uint)-2; + int a = 3; + return (int)(b % a); + } + } + + public static int test_0_ceq () { + long a = 2; + long b = 2; + long c = 3; + long d = 0xff00000002; + + bool val = (a == b); // this should produce a ceq + if (!val) + return 1; + + val = (a == c); // this should produce a ceq + if (val) + return 2; + + val = (a == d); // this should produce a ceq + if (val) + return 3; + + return 0; + } + + public static int test_0_ceq_complex () { + long l = 1, ll = 2; + + if (l < 0 != ll < 0) + return 1; + + return 0; + } + + public static int test_0_clt () { + long a = 2; + long b = 2; + long c = 3; + long d = 0xff00000002L; + long e = -1; + + bool val = (a < b); // this should produce a clt + if (val) + return 1; + + val = (a < c); // this should produce a clt + if (!val) + return 2; + + val = (c < a); // this should produce a clt + if (val) + return 3; + + val = (e < d); // this should produce a clt + if (!val) + return 4; + + val = (d < e); // this should produce a clt + if (val) + return 5; + + return 0; + } + + public static int test_0_clt_un () { + ulong a = 2; + ulong b = 2; + ulong c = 3; + ulong d = 0xff00000002; + ulong e = 0xffffffffffffffff; + + bool val = (a < b); // this should produce a clt_un + if (val) + return 1; + + val = (a < c); // this should produce a clt_un + if (!val) + return 1; + + val = (d < e); // this should produce a clt_un + if (!val) + return 1; + + val = (e < d); // this should produce a clt_un + if (val) + return 1; + + return 0; + } + + public static int test_0_cgt () { + long a = 2; + long b = 2; + long c = 3; + long d = 0xff00000002L; + long e = -1; + + bool val = (a > b); // this should produce a cgt + if (val) + return 1; + + val = (a > c); // this should produce a cgt + if (val) + return 2; + + val = (c > a); // this should produce a cgt + if (!val) + return 3; + + val = (e > d); // this should produce a cgt + if (val) + return 4; + + val = (d > e); // this should produce a cgt + if (!val) + return 5; + + return 0; + } + + public static int test_0_cgt_un () { + ulong a = 2; + ulong b = 2; + ulong c = 3; + ulong d = 0xff00000002; + ulong e = 0xffffffffffffffff; + + bool val = (a > b); // this should produce a cgt_un + if (val) + return 1; + + val = (a > c); // this should produce a cgt_un + if (val) + return 1; + + val = (d > e); // this should produce a cgt_un + if (val) + return 1; + + val = (e > d); // this should produce a cgt_un + if (!val) + return 1; + + return 0; + } + + public static int test_3_byte_cast () { + ulong val = 0xff00ff00f0f0f0f0; + byte b; + b = (byte) (val & 0xFF); + if (b != 0xf0) + return 1; + + return 3; + } + + public static int test_4_ushort_cast () { + ulong val = 0xff00ff00f0f0f0f0; + ushort b; + b = (ushort) (val & 0xFFFF); + if (b != 0xf0f0) + return 1; + return 4; + } + + public static int test_500_mul_div () { + long val = 1000; + long exp = 10; + long maxexp = 20; + long res = val * exp / maxexp; + + return (int)res; + } + + public static int test_3_checked_cast_un () { + ulong i = 2; + long j; + + checked { j = (long)i; } + + if (j != 2) + return 0; + return 3; + } + + public static int test_4_checked_cast () { + long i = 3; + ulong j; + + checked { j = (ulong)i; } + + if (j != 3) + return 0; + return 4; + } + + public static int test_12_checked_i1_cast () { + long l = 12; + + checked { + return (sbyte)l; + } + } + + public static int test_127_checked_i1_cast_un () { + ulong l = 127; + + checked { + return (sbyte)l; + } + } + + public static int test_1234_checked_i2_cast () { + long l = 1234; + + checked { + return (short)l; + } + } + + public static int test_32767_checked_i2_cast_un () { + ulong l = 32767; + + checked { + return (ushort)l; + } + } + + public static int test_1234_checked_i4_cast () { + ulong ul = 1234; + + checked { + return (int)ul; + } + } + + public static int test_10_int_uint_compare () { + uint size = 10; + int j = 0; + for (int i = 0; i < size; ++i) { + j++; + } + return j; + } + + public static int test_0_ulong_regress () { + ulong u = 4257145737; + u --; + return (u == 4257145736) ? 0 : 1; + } + + public static int test_0_ulong_regress2 () { + int p2 = 31; + ulong sum_p = 2897079476 + (ulong) (1 << p2); + return (sum_p == 749595828) ? 0 : 1; + } + + public static int test_0_assemble_long () + { + uint a = 5; + ulong x = 0x12345678; + ulong y = 1; + + + ulong z = ((x - y) << 32) | a; + + if (z != 0x1234567700000005) + return 1; + + return 0; + } + + public static int test_0_hash () + { + ulong x = 0x1234567887654321; + int h = (int)(x & 0xffffffff) ^ (int)(x >> 32); + if (h != unchecked ((int)(0x87654321 ^ 0x12345678))) + return h; + return 0; + + } + + public static int test_0_shift_regress () { + long a = 0; + int b = 6; + UInt16 c = 3; + + return ((a >> (b - c)) == 0) ? 0 : 1; + } + + public static int test_1234_conv_ovf_u8 () { + int i = 1234; + + checked { + ulong l = (ulong)i; + return (int)l; + } + } + + public static int test_0_regress_cprop_80738 () { + int hours = Int32.MinValue; + int hrssec = (hours * 3600); + long t = ((long)(hrssec) * 1000L); + + return t == 0 ? 0 : 1; + } + + public static int test_0_conv_u () { + unsafe { + int** dead = (int**) 0xdeadbeaf; + long i = (long)dead; + return (i == 0xdeadbeaf) ? 0 : 1; + } + } + + public static int test_0_lconv_to_u2 () { + unchecked { + ulong value = (ulong)(short)-10; + value = (ushort)value; + return (value == 65526) ? 0 : 1; + } + } + + public static int test_0_lneg_regress_10320 () { + long a = 0x100000000; + ulong c = ((ulong) (-(-a))) >> 32; + return c == 1 ? 0 : 1; + } + + public static int test_6_lrem_un () { + ulong r2 = 4294967296; + uint d = 10; + ulong res = r2 % d; + + return (int)res; + } + + public static int test_0_lrem_imm_1 () { + long b = 12L; + return (int)(b % 1L); + } + + public static int test_0_lrem_imm_1_neg () { + long b = -2L; + return (int)(b % 1L); + } + + public static int test_0_lrem_imm_2 () + { + long x = 245345634L; + return (int)(x % 2L); + } + + public static int test_1_lrem_imm_2 () + { + long x = 24534553245L; + return (int)(x % 2L); + } + + public static int test_1_lrem_imm_2_neg () + { + long x = -24534553245L; + return -(int)(x % 2L); + } + + public static int test_13_lrem_imm_32 () + { + long x = 17389L; + return (int)(x % 32L); + } + + public static int test_27_lrem_imm_32_neg () + { + long x = -2435323L; + return -(int)(x % 32L); + } + + public static int test_5_lrem_imm_large () + { + long x = 0x1000000005L; + return (int)(x % 0x40000000L); + } + + public static int test_5_lrem_imm_too_large () + { + long x = 0x1000000005L; + return (int)(x % 0x80000000L); + } +} + diff --git a/unity-2019.4.24f1-mbe/mono/mini/basic-math.cs b/unity-2019.4.24f1-mbe/mono/mini/basic-math.cs new file mode 100644 index 000000000..a5aeb9cc8 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/basic-math.cs @@ -0,0 +1,304 @@ +using System; +using System.Reflection; + +/* + * Regression tests for the mono JIT. + * + * Each test needs to be of the form: + * + * public static int test__ (); + * + * where is an integer (the value that needs to be returned by + * the method to make it pass. + * is a user-displayed name used to identify the test. + * + * The tests can be driven in two ways: + * *) running the program directly: Main() uses reflection to find and invoke + * the test methods (this is useful mostly to check that the tests are correct) + * *) with the --regression switch of the jit (this is the preferred way since + * all the tests will be run with optimizations on and off) + * + * The reflection logic could be moved to a .dll since we need at least another + * regression test file written in IL code to have better control on how + * the IL code looks. + */ + +#if __MOBILE__ +class MathTests +#else +class Tests +#endif +{ + +#if !__MOBILE__ + public static int Main (string[] args) { + return TestDriver.RunTests (typeof (Tests), args); + } +#endif + + public static int test_0_sin_precision () { + double d1 = Math.Sin (1); + double d2 = Math.Sin (1) - d1; + return (d2 == 0) ? 0 : 1; + } + + public static int test_0_cos_precision () { + double d1 = Math.Cos (1); + double d2 = Math.Cos (1) - d1; + return (d2 == 0) ? 0 : 1; + } + + public static int test_0_tan_precision () { + double d1 = Math.Tan (1); + double d2 = Math.Tan (1) - d1; + return (d2 == 0) ? 0 : 1; + } + + public static int test_0_atan_precision () { + double d1 = Math.Atan (double.NegativeInfinity); + double d2 = Math.Atan (double.NegativeInfinity) - d1; + return (d2 == 0) ? 0 : 1; + } + + public static int test_0_sqrt_precision () { + double d1 = Math.Sqrt (2); + double d2 = Math.Sqrt (2) - d1; + return (d2 == 0) ? 0 : 1; + } + + public static int test_2_sqrt () { + return (int) Math.Sqrt (4); + } + public static int test_0_sqrt_precision_and_not_spill () { + double expected = 0; + double[] operands = new double[3]; + double[] temporaries = new double[3]; + for (int i = 0; i < 3; i++) { + operands [i] = (i+1) * (i+1) * (i+1); + if (i == 0) { + expected = operands [0]; + } else { + temporaries [i] = operands [i] / expected; + temporaries [i] = Math.Sqrt (temporaries [i]); + expected = temporaries [i]; + } + + //Console.Write( "{0}: {1}\n", i, temporaries [i] ); + } + expected = temporaries [2]; + + double result = Math.Sqrt (operands [2] / Math.Sqrt (operands [1] / operands [0])); + + //Console.Write( "result: {0,20:G}\n", result ); + + return (result == expected) ? 0 : 1; + } + + public static int test_0_sqrt_precision_and_spill () { + double expected = 0; + double[] operands = new double[9]; + double[] temporaries = new double[9]; + for (int i = 0; i < 9; i++) { + operands [i] = (i+1) * (i+1) * (i+1); + if (i == 0) { + expected = operands [0]; + } else { + temporaries [i] = operands [i] / expected; + temporaries [i] = Math.Sqrt (temporaries [i]); + expected = temporaries [i]; + } + + //Console.Write( "{0}: {1}\n", i, temporaries [i] ); + } + expected = temporaries [8]; + + double result = Math.Sqrt (operands [8] / Math.Sqrt (operands [7] / Math.Sqrt (operands [6] / Math.Sqrt (operands [5] / Math.Sqrt (operands [4] / Math.Sqrt (operands [3] / Math.Sqrt (operands [2] / Math.Sqrt (operands [1] / operands [0])))))))); + + //Console.Write( "result: {0,20:G}\n", result ); + + return (result == expected) ? 0 : 1; + } + + public static int test_0_div_precision_and_spill () { + double expected = 0; + double[] operands = new double[9]; + double[] temporaries = new double[9]; + for (int i = 0; i < 9; i++) { + operands [i] = (i+1) * (i+1); + if (i == 0) { + expected = operands [0]; + } else { + temporaries [i] = operands [i] / expected; + expected = temporaries [i]; + } + + //Console.Write( "{0}: {1}\n", i, temporaries [i] ); + } + expected = temporaries [8]; + + double result = (operands [8] / (operands [7] / (operands [6] / (operands [5] / (operands [4] / (operands [3] / (operands [2] / (operands [1] / operands [0])))))))); + + //Console.Write( "result: {0,20:G}\n", result ); + + return (result == expected) ? 0 : 1; + } + + public static int test_0_sqrt_nan () { + return Double.IsNaN (Math.Sqrt (Double.NaN)) ? 0 : 1; + } + + public static int test_0_sin_nan () { + return Double.IsNaN (Math.Sin (Double.NaN)) ? 0 : 1; + } + + public static int test_0_cos_nan () { + return Double.IsNaN (Math.Cos (Double.NaN)) ? 0 : 1; + } + + public static int test_0_tan_nan () { + return Double.IsNaN (Math.Tan (Double.NaN)) ? 0 : 1; + } + + public static int test_0_atan_nan () { + return Double.IsNaN (Math.Atan (Double.NaN)) ? 0 : 1; + } + + public static int test_0_min () { + if (Math.Min (5, 6) != 5) + return 1; + if (Math.Min (6, 5) != 5) + return 2; + if (Math.Min (-100, -101) != -101) + return 3; + if (Math.Min ((long)5, (long)6) != 5) + return 4; + if (Math.Min ((long)6, (long)5) != 5) + return 5; + if (Math.Min ((long)-100, (long)-101) != -101) + return 6; + return 0; + } + + public static int test_0_max () { + if (Math.Max (5, 6) != 6) + return 1; + if (Math.Max (6, 5) != 6) + return 2; + if (Math.Max (-100, -101) != -100) + return 3; + if (Math.Max ((long)5, (long)6) != 6) + return 4; + if (Math.Max ((long)6, (long)5) != 6) + return 5; + if (Math.Max ((long)-100, (long)-101) != -100) + return 6; + return 0; + } + + public static int test_0_min_un () { + uint a = (uint)int.MaxValue + 10; + + for (uint b = 7; b <= 10; ++b) { + if (Math.Min (a, b) != b) + return (int)b; + if (Math.Min (b, a) != b) + return (int)b; + } + + if (Math.Min ((ulong)5, (ulong)6) != 5) + return 4; + if (Math.Min ((ulong)6, (ulong)5) != 5) + return 5; + + ulong la = (ulong)long.MaxValue + 10; + + for (ulong b = 7; b <= 10; ++b) { + if (Math.Min (la, b) != b) + return (int)b; + if (Math.Min (b, la) != b) + return (int)b; + } + + return 0; + } + + public static int test_0_max_un () { + uint a = (uint)int.MaxValue + 10; + + for (uint b = 7; b <= 10; ++b) { + if (Math.Max (a, b) != a) + return (int)b; + if (Math.Max (b, a) != a) + return (int)b; + } + + if (Math.Max ((ulong)5, (ulong)6) != 6) + return 4; + if (Math.Max ((ulong)6, (ulong)5) != 6) + return 5; + + ulong la = (ulong)long.MaxValue + 10; + + for (ulong b = 7; b <= 10; ++b) { + if (Math.Max (la, b) != la) + return (int)b; + if (Math.Max (b, la) != la) + return (int)b; + } + + return 0; + } + + public static int test_0_abs () { + double d = -5.0; + + if (Math.Abs (d) != 5.0) + return 1; + return 0; + } + + public static int test_0_float_abs () { + float f = -1.0f; + + if (Math.Abs (f) != 1.0f) + return 1; + return 0; + } + + public static int test_0_round () { + if (Math.Round (5.0) != 5.0) + return 1; + + if (Math.Round (5.000000000000001) != 5.0) + return 2; + + if (Math.Round (5.499999999999999) != 5.0) + return 3; + + if (Math.Round (5.5) != 6.0) + return 4; + + if (Math.Round (5.999999999999999) != 6.0) + return 5; + + if (Math.Round (Double.Epsilon) != 0) + return 6; + + if (!Double.IsNaN (Math.Round (Double.NaN))) + return 7; + + if (!Double.IsPositiveInfinity (Math.Round (Double.PositiveInfinity))) + return 8; + + if (!Double.IsNegativeInfinity (Math.Round (Double.NegativeInfinity))) + return 9; + + if (Math.Round (Double.MinValue) != Double.MinValue) + return 10; + + if (Math.Round (Double.MaxValue) != Double.MaxValue) + return 11; + + return 0; + } +} diff --git a/unity-2019.4.24f1-mbe/mono/mini/basic-simd.cs b/unity-2019.4.24f1-mbe/mono/mini/basic-simd.cs new file mode 100644 index 000000000..f8fdcebaf --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/basic-simd.cs @@ -0,0 +1,3311 @@ +using System; +using Mono.Simd; + +public class SimdTests { + static int ddd; + static void InitByRef (out Vector4i v) { + v = new Vector4i (99); + if (ddd > 10) + throw new Exception ("ddd"); + } + + static int test_0_vector4i_one_element_ctor_with_byref () + { + Vector4i a; + InitByRef (out a); + if (a.X != 99) + return 1; + if (a.Y != 99) + return 2; + if (a.Z != 99) + return 3; + if (a.W != 99) + return 4; + return 0; + } + + static int test_0_vector2d_one_element_ctor () { + Vector2d a = new Vector2d (99); + if (a.X != 99) + return 1; + if (a.Y != 99) + return 2; + return 0; + } + + static int test_0_vector2ul_one_element_ctor () { + Vector2ul a = new Vector2ul (99); + + if (a.X != 99) + return 1; + if (a.Y != 99) + return 2; + return 0; + } + + static int test_0_vector2l_one_element_ctor () { + Vector2l a = new Vector2l (99); + + if (a.X != 99) + return 1; + if (a.Y != 99) + return 2; + return 0; + } + + static int test_0_vector4f_one_element_ctor () { + Vector4f a = new Vector4f (99); + + if (a.X != 99) + return 1; + if (a.Y != 99) + return 2; + if (a.Z != 99) + return 3; + if (a.W != 99) + return 4; + return 0; + } + + static int test_0_vector4ui_one_element_ctor () { + Vector4ui a = new Vector4ui (99); + + if (a.X != 99) + return 1; + if (a.Y != 99) + return 2; + if (a.Z != 99) + return 3; + if (a.W != 99) + return 4; + return 0; + } + + static int test_0_vector4i_one_element_ctor () { + Vector4i a = new Vector4i (99); + + if (a.X != 99) + return 1; + if (a.Y != 99) + return 2; + if (a.Z != 99) + return 3; + if (a.W != 99) + return 4; + return 0; + } + + static int test_0_vector8us_one_element_ctor () { + Vector8us a = new Vector8us (99); + + if (a.V0 != 99) + return 1; + if (a.V1 != 99) + return 2; + if (a.V2 != 99) + return 3; + if (a.V3 != 99) + return 4; + if (a.V4 != 99) + return 5; + if (a.V5 != 99) + return 6; + if (a.V6 != 99) + return 7; + if (a.V7 != 99) + return 8; + return 0; + } + + static int test_0_vector8s_one_element_ctor () { + Vector8s a = new Vector8s (99); + + if (a.V0 != 99) + return 1; + if (a.V1 != 99) + return 2; + if (a.V2 != 99) + return 3; + if (a.V3 != 99) + return 4; + if (a.V4 != 99) + return 5; + if (a.V5 != 99) + return 6; + if (a.V6 != 99) + return 7; + if (a.V7 != 99) + return 8; + return 0; + } + + static int test_0_vector16sb_one_element_ctor () { + Vector16sb a = new Vector16sb (99); + + if (a.V0 != 99) + return 1; + if (a.V1 != 99) + return 2; + if (a.V2 != 99) + return 3; + if (a.V3 != 99) + return 4; + if (a.V4 != 99) + return 5; + if (a.V5 != 99) + return 6; + if (a.V6 != 99) + return 7; + if (a.V7 != 99) + return 8; + if (a.V8 != 99) + return 9; + if (a.V9 != 99) + return 10; + if (a.V10 != 99) + return 11; + if (a.V11 != 99) + return 12; + if (a.V12 != 99) + return 13; + if (a.V13 != 99) + return 14; + if (a.V14 != 99) + return 15; + if (a.V15 != 99) + return 16; + return 0; + } + + static int test_0_vector16b_one_element_ctor () { + Vector16b a = new Vector16b (99); + + if (a.V0 != 99) + return 1; + if (a.V1 != 99) + return 2; + if (a.V2 != 99) + return 3; + if (a.V3 != 99) + return 4; + if (a.V4 != 99) + return 5; + if (a.V5 != 99) + return 6; + if (a.V6 != 99) + return 7; + if (a.V7 != 99) + return 8; + if (a.V8 != 99) + return 9; + if (a.V9 != 99) + return 10; + if (a.V10 != 99) + return 11; + if (a.V11 != 99) + return 12; + if (a.V12 != 99) + return 13; + if (a.V13 != 99) + return 14; + if (a.V14 != 99) + return 15; + if (a.V15 != 99) + return 16; + return 0; + } + + public static unsafe int test_0_sizeof_returns_16_2d () + { + double[] array = new double[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + fixed (double *ptr = &array [0]) { + Vector2d *f = (Vector2d*)ptr; + Vector2d a = *f++; + Vector2d b = *f++; + Vector2d c = *f++; + Vector2d d = *f++; + + if (a.X != 1 || a.Y != 2) + return 1; + if (b.X != 3 || b.Y != 4) + return 2; + if (c.X != 5 || c.Y != 6) + return 3; + if (d.X != 7 || d.Y != 8) + return 4; + } + return 0; + } + + public static unsafe int test_0_sizeof_returns_16_4f () + { + float[] array = new float[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + fixed (float *ptr = &array [0]) { + Vector4f *f = (Vector4f*)ptr; + Vector4f a = *f++; + Vector4f b = *f++; + Vector4f c = *f++; + Vector4f d = *f++; + + if (a.X != 1 || a.W != 4) + return 1; + if (b.X != 5 || b.W != 8) + return 2; + if (c.X != 9 || c.W != 12) + return 3; + if (d.X != 13 || d.W != 16) + return 4; + } + return 0; + } + + public static unsafe int test_0_sizeof_returns_16_8d () + { + short[] array = new short[40]; + for (int i = 0; i < 40; ++i) + array [i] = (short) (i + 1); + + fixed (short *ptr = &array [0]) { + Vector8s *f = (Vector8s*)ptr; + Vector8s a = *f++; + Vector8s b = *f++; + Vector8s c = *f++; + Vector8s d = *f++; + + if (a.V0 != 1 || a.V7 != 8) + return 1; + if (b.V0 != 9 || b.V7 != 16) + return 2; + if (c.V0 != 17 || c.V7 != 24) + return 3; + if (d.V0 != 25 || d.V7 != 32) + return 4; + } + return 0; + } + + public static unsafe int test_0_sizeof_returns_16_16b () + { + byte[] array = new byte[80]; + for (int i = 0; i < 80; ++i) + array [i] = (byte) (i + 1); + + fixed (byte *ptr = &array [0]) { + Vector16b *f = (Vector16b*)ptr; + Vector16b a = *f++; + Vector16b b = *f++; + Vector16b c = *f++; + Vector16b d = *f++; + + if (a.V0 != 1 || a.V15 != 16) + return 1; + if (b.V0 != 17 || b.V15 != 32) + return 2; + if (c.V0 != 33 || c.V15 != 48) + return 3; + if (d.V0 != 49 || d.V15 != 64) + return 4; + } + return 0; + } + public static int test_0_bug_462457 () + { + Vector4f sum = new Vector4f(0,0,0,0); + Vector4f add = new Vector4f(1.0F,1.0F,1.0F,1.0F); + + for (int i = 0; i < 10; ++i) + sum = sum + add; + + if (sum.X != 10f) + return 1; + return 0; + } + + public static int test_0_vector16b_operator_neq () { + Vector16b a = new Vector16b(1,2,3,5,5,6,7,8,1,2,3,5,5,6,7,8); + Vector16b b = new Vector16b(1,2,3,5,5,6,7,8,1,2,3,5,5,6,7,8); + if (a != b) + return 1; + b.V0 = 99; + if (!(a != b)) + return 2; + return 0; + } + + public static int test_0_vector16b_operator_eq () { + Vector16b a = new Vector16b(1,2,3,5,5,6,7,8,1,2,3,5,5,6,7,8); + Vector16b b = new Vector16b(1,2,3,5,5,6,7,8,1,2,3,5,5,6,7,8); + if (!(a == b)) + return 1; + b.V0 = 99; + if (a == b) + return 2; + return 0; + } + + public static int test_0_vector8us_operator_neq () { + Vector8us a = new Vector8us(1, 2, 3, 4, 5, 6, 7, 8); + Vector8us b = new Vector8us(1, 2, 3, 4, 5, 6, 7, 8); + if (a != b) + return 1; + b.V0 = 99; + if (!(a != b)) + return 2; + return 0; + } + + public static int test_0_vector8us_operator_eq () { + Vector8us a = new Vector8us(1, 2, 3, 4, 5, 6, 7, 8); + Vector8us b = new Vector8us(1, 2, 3, 4, 5, 6, 7, 8); + if (!(a == b)) + return 1; + b.V0 = 99; + if (a == b) + return 2; + return 0; + } + + public static int test_0_set_vector4f_operator_neq () { + Vector4f a = new Vector4f(1, 2, 3, 4); + Vector4f b = new Vector4f(1, 2, 3, 4); + if (a != b) + return 1; + + a = new Vector4f(1, 2, float.NaN, 4); + b = new Vector4f(1, 2, float.NaN, 4); + if (!(a != b)) //NaN is always != + return 2; + + a = new Vector4f(1, 2, float.NaN, 4); + b = new Vector4f(1, 2, 10, 4); + if (!(a != b)) + return 3; + + a = new Vector4f(1, 2, float.PositiveInfinity, 4); + b = new Vector4f(1, 2, float.PositiveInfinity, 4); + if (a != b) + return 4; + + a = new Vector4f(1, 2, 20, 4); + b = new Vector4f(1, 2, 30, 4); + if (!(a != b)) + return 5; + + return 0; + } + + public static int test_0_set_vector4f_operator_eq () { + Vector4f a = new Vector4f(1, 2, 3, 4); + Vector4f b = new Vector4f(1, 2, 3, 4); + if (!(a == b)) + return 1; + + a = new Vector4f(1, 2, float.NaN, 4); + b = new Vector4f(1, 2, float.NaN, 4); + if (a == b) + return 2; + + a = new Vector4f(1, 2, 10, 4); + b = new Vector4f(1, 2, float.NaN, 4); + if (a == b) + return 3; + + a = new Vector4f(1, 2, float.PositiveInfinity, 4); + b = new Vector4f(1, 2, float.PositiveInfinity, 4); + if (!(a == b)) + return 4; + return 0; + } + + public static int test_1_set_vector4ui_operator_neq () { + Vector4ui a = new Vector4ui(1, 2, 3, 4); + Vector4ui b = new Vector4ui(1, 2, 3, 4); + if (a != b) + return 0; + return 1; + } + + public static int test_0_set_vector4ui_operator_neq () { + Vector4ui a = new Vector4ui(1, 2, 3, 4); + Vector4ui b = new Vector4ui(111, 2, 3, 4); + if (a != b) + return 0; + return 1; + } + + public static int test_0_set_vector4ui_operator_eq () { + Vector4ui a = new Vector4ui(1, 2, 3, 4); + Vector4ui b = new Vector4ui(1, 2, 3, 4); + if (a == b) + return 0; + return 1; + } + + public static int test_1_set_vector4ui_operator_eq () { + Vector4ui a = new Vector4ui(1, 2, 3, 4); + Vector4ui b = new Vector4ui(111, 2, 3, 4); + if (a == b) + return 0; + return 1; + } + + public static int test_0_set_vector_small_array () { + uint[] array = new uint[3]; + + try { + array.SetVector (new Vector4ui (), 0); + return 1; + } catch (IndexOutOfRangeException) { + } + return 0; + } + + public static int test_0_set_vector_negative_index () { + uint[] array = new uint[4]; + + try { + array.SetVector (new Vector4ui (), -1); + return 1; + } catch (IndexOutOfRangeException) { + } + return 0; + } + + public static int test_0_set_vector_bounds_error () { + uint[] array = new uint[4]; + + try { + array.SetVector (new Vector4ui (), 1); + return 1; + } catch (IndexOutOfRangeException) { + } + return 0; + } + + public static int test_0_set_vector () { + uint[] array = new uint[10]; + Vector4ui a = new Vector4ui (11, 22, 33, 44); + + array.SetVector (a, 1); + + if (array [1] != 11) + return 1; + if (array [2] != 22) + return 2; + if (array [3] != 33) + return 3; + if (array [4] != 44) + return 4; + return 0; + } + + public static int test_0_get_vector_small_array () { + uint[] array = new uint[3]; + + try { + Vector4ui res = array.GetVector (0); + return 1; + } catch (IndexOutOfRangeException) { + } + return 0; + } + + public static int test_0_get_vector_negative_index () { + uint[] array = new uint[4]; + + try { + Vector4ui res = array.GetVector (-1); + return 1; + } catch (IndexOutOfRangeException) { + } + return 0; + } + + public static int test_0_get_vector_bounds_error () { + uint[] array = new uint[4]; + + try { + Vector4ui res = array.GetVector (1); + return 1; + } catch (IndexOutOfRangeException) { + } + return 0; + } + + public static int test_0_get_vector () { + uint[] array = new uint[] { 11, 22, 33, 44, 55, 66, 77, 88, 99, 111 }; + + Vector4ui res = array.GetVector (1); + + if (res.X != 22) + return 1; + if (res.Y != 33) + return 2; + if (res.Z != 44) + return 3; + if (res.W != 55) + return 4; + + return 0; + } + + public static int test_0_accessor_vecto2l () { + Vector2l a = new Vector2l (3, 2); + + if (a.X != 3) + return 1; + if (a.Y != 2) + return 2; + + a.X = 500000000000055L; + a.Y = -12345678900L; + + if (a.X != 500000000000055L) + return 3; + if (a.Y != -12345678900L) + return 4; + return 0; + } + + public static int test_0_accessor_vecto2d () { + Vector2d a = new Vector2d (3, 2); + + if (a.X != 3) + return 1; + if (a.Y != 2) + return 2; + + a.X = 5000000000000; + a.Y = -0.5; + + if (a.X != 5000000000000) + return 3; + if (a.Y != -0.5) + return 4; + return 0; + } + + public static int test_0_accessor_vecto4f () { + Vector4f a = new Vector4f (1,2,3,4); + + if (a.X != 1) + return 1; + if (a.Y != 2) + return 2; + if (a.Z != 3) + return 3; + if (a.W != 4) + return 4; + + a.X = 128f; + a.Y = 256f; + a.Z = -0.5f; + a.W = 0.125f; + + if (a.X != 128) + return 5; + if (a.Y != 256) + return 6; + if (a.Z != -0.5) + return 7; + if (a.W != 0.125) + return 8; + return 0; + } + + public static int test_0_accessor_vecto4i () { + Vector4i a = new Vector4i (0x70000000, -1, 3, 4); + + if (a.X != 0x70000000) + return 1; + if (a.Y != -1) + return 2; + if (a.Z != 3) + return 3; + if (a.W != 4) + return 4; + + a.X = 11; + a.Y = 22; + a.Z = 33333344; + a.W = -44444444; + + if (a.X != 11) + return 5; + if (a.Y != 22) + return 6; + if (a.Z != 33333344) + return 7; + if (a.W != -44444444) + return 8; + return 0; + } + + public static int test_0_accessor_vecto4ui () { + Vector4ui a = new Vector4ui (0xF0000000, 0xF0000, 3, 4); + + if (a.X != 0xF0000000) + return 1; + if (a.Y != 0xF0000) + return 2; + if (a.Z != 3) + return 3; + if (a.W != 4) + return 4; + + a.X = 11; + a.Y = 22; + a.Z = 33333344; + a.W = 44444444; + + if (a.X != 11) + return 5; + if (a.Y != 22) + return 6; + if (a.Z != 33333344) + return 7; + if (a.W != 44444444) + return 8; + return 0; + } + + static float use_getter_with_byref (ref Vector4f a) { + return a.W; + } + + public static int test_0_accessor_and_byref_var () { + Vector4f a = new Vector4f (1, 2, 3, 4); + if (use_getter_with_byref (ref a) != 4) + return 1; + return 0; + } + + public static unsafe int test_0_vector2ul_slr () { + Vector2ul a = new Vector2ul (1, 6); + + Vector2ul c = a >> 1; + if (c.X != 0) + return 1; + if (c.Y != 3) + return 2; + return 0; + } + + public static unsafe int test_0_vector2l_cmp_gt () { + Vector2l a = new Vector2l (10, 5); + Vector2l b = new Vector2l (-1, 5); + + Vector2l c = a.CompareGreaterThan (b); + + if (c.X != -1) + return 1; + if (c.Y != 0) + return 2; + return 0; + } + + public static unsafe int test_0_vector2l_cmp_eq () { + Vector2l a = new Vector2l (0xFF, 5); + Vector2l b = new Vector2l (0xFF000000FFL, 5); + + Vector2l c = a.CompareEqual (b); + + if (c.X != 0) + return 1; + if (c.Y != -1) + return 2; + return 0; + } + + public static unsafe int test_0_vector2l_srl () { + Vector2l a = new Vector2l (1, 6); + + Vector2l c = a.LogicalRightShift (1); + + if (c.X != 0) + return 1; + if (c.Y != 3) + return 2; + return 0; + } + + public static unsafe int test_0_vector2l_unpack_high () { + Vector2l a = new Vector2l (1, 6); + Vector2l b = new Vector2l (3, 4); + + Vector2l c = a.UnpackHigh (b); + + if (c.X != 6) + return 1; + if (c.Y != 4) + return 2; + return 0; + } + + public static unsafe int test_0_vector2l_unpack_low () { + Vector2l a = new Vector2l (1, 6); + Vector2l b = new Vector2l (3, 4); + + Vector2l c = a.UnpackLow (b); + + if (c.X != 1) + return 1; + if (c.Y != 3) + return 2; + return 0; + } + + public static unsafe int test_0_vector2l_xor () { + Vector2l a = new Vector2l (1, 6); + Vector2l b = new Vector2l (3, 4); + + Vector2l c = a ^ b; + + if (c.X != 2) + return 1; + if (c.Y != 2) + return 2; + return 0; + } + + public static unsafe int test_0_vector2l_or () { + Vector2l a = new Vector2l (1, 6); + Vector2l b = new Vector2l (3, 4); + + Vector2l c = a | b; + + if (c.X != 3) + return 1; + if (c.Y != 6) + return 2; + return 0; + } + + public static unsafe int test_0_vector2l_and () { + Vector2l a = new Vector2l (1, 6); + Vector2l b = new Vector2l (3, 4); + + Vector2l c = a & b; + + if (c.X != 1) + return 1; + if (c.Y != 4) + return 2; + return 0; + } + + public static unsafe int test_0_vector2l_shl() { + Vector2l a = new Vector2l (1, 6); + + Vector2l c = a << 3; + + if (c.X != 8) + return 1; + if (c.Y != 48) + return 2; + return 0; + } + public static unsafe int test_0_vector2l_sub() { + Vector2l a = new Vector2l (1, 6); + Vector2l b = new Vector2l (3, 4); + + Vector2l c = a - b; + + if (c.X != -2) + return 1; + if (c.Y != 2) + return 2; + return 0; + } + + public static unsafe int test_0_vector2l_add () { + Vector2l a = new Vector2l (1, 2); + Vector2l b = new Vector2l (3, 4); + + Vector2l c = a + b; + + if (c.X != 4) + return 1; + if (c.Y != 6) + return 2; + return 0; + } + + public static unsafe int test_0_vector2d_dup () { + Vector2d a = new Vector2d (3, 2); + + Vector2d c = a.Duplicate (); + + if (c.X != 3) + return 1; + if (c.Y != 3) + return 2; + return 0; + } + + public static unsafe int test_0_vector2d_cmp_eq () { + Vector2d a = new Vector2d (3, 2); + Vector2d b = new Vector2d (3, 4); + + Vector4ui c = (Vector4ui)a.CompareEqual (b); + + if (c.X != 0xFFFFFFFF) + return 1; + if (c.Y != 0xFFFFFFFF) + return 2; + if (c.Z != 0) + return 3; + if (c.W != 0) + return 4; + return 0; + } + + public static unsafe int test_0_vector2d_unpack_low () { + Vector2d a = new Vector2d (1, 2); + Vector2d b = new Vector2d (4, 5); + + Vector2d c = a.InterleaveLow (b); + + if (c.X != 1) + return 1; + if (c.Y != 4) + return 2; + return 0; + } + + public static unsafe int test_0_vector2d_unpack_high () { + Vector2d a = new Vector2d (1, 2); + Vector2d b = new Vector2d (4, 5); + + Vector2d c = a.InterleaveHigh (b); + + if (c.X != 2) + return 1; + if (c.Y != 5) + return 2; + return 0; + } + public static unsafe int test_0_vector2d_addsub () { + Vector2d a = new Vector2d (1, 2); + Vector2d b = new Vector2d (4, 1); + + Vector2d c = a.AddSub (b); + + if (c.X != -3) + return 1; + if (c.Y != 3) + return 2; + return 0; + } + public static unsafe int test_0_vector2d_hsub () { + Vector2d a = new Vector2d (1, 2); + Vector2d b = new Vector2d (4, 1); + + Vector2d c = a.HorizontalSub (b); + + if (c.X != -1) + return 1; + if (c.Y != 3) + return 2; + return 0; + } + + public static unsafe int test_0_vector2d_hadd () { + Vector2d a = new Vector2d (1, 2); + Vector2d b = new Vector2d (4, 0); + + Vector2d c = a.HorizontalAdd (b); + + if (c.X != 3) + return 1; + if (c.Y != 4) + return 2; + return 0; + } + + public static unsafe int test_0_vector2d_min () { + Vector2d a = new Vector2d (1, 2); + Vector2d b = new Vector2d (4, 0); + + Vector2d c = a.Min (b); + + if (c.X != 1) + return 1; + if (c.Y != 0) + return 2; + return 0; + } + + public static unsafe int test_0_vector2d_max () { + Vector2d a = new Vector2d (1, 2); + Vector2d b = new Vector2d (4, 0); + + Vector2d c = a.Max (b); + + if (c.X != 4) + return 1; + if (c.Y != 2) + return 2; + return 0; + } + + + public static unsafe int test_0_vector2d_andnot () { + Vector2d a = new Vector2d (1, 2); + Vector2d b = new Vector2d (3, 4); + + Vector4ui c = (Vector4ui)a.AndNot (b); + Vector4ui ta = (Vector4ui)a; + Vector4ui tb = (Vector4ui)b; + + if (c.X != (~ta.X & tb.X)) + return 1; + if (c.Y != (~ta.Y & tb.Y)) + return 2; + if (c.Z != (~ta.Z & tb.Z)) + return 3; + if (c.W != (~ta.W & tb.W)) + return 4; + return 0; + } + + public static unsafe int test_0_vector2d_div () { + Vector2d a = new Vector2d (1, 2); + Vector2d b = new Vector2d (4, 5); + + Vector2d c = a / b; + + if (c.X != 0.25) + return 1; + if (c.Y != 0.4) + return 2; + return 0; + } + + public static unsafe int test_0_vector2d_mul () { + Vector2d a = new Vector2d (1, 2); + Vector2d b = new Vector2d (3, 5); + + Vector2d c = a * b; + + if (c.X != 3) + return 1; + if (c.Y != 10) + return 2; + return 0; + } + public static unsafe int test_0_vector2d_sub () { + Vector2d a = new Vector2d (1, 2); + Vector2d b = new Vector2d (3, 5); + + Vector2d c = a - b; + + if (c.X != -2) + return 1; + if (c.Y != -3) + return 2; + return 0; + } + public static unsafe int test_0_vector2d_add () { + Vector2d a = new Vector2d (1, 2); + Vector2d b = new Vector2d (3, 4); + + Vector2d c = a + b; + + if (c.X != 4) + return 1; + if (c.Y != 6) + return 2; + return 0; + } + public static unsafe int test_0_vector2d_xor () { + Vector2d a = new Vector2d (1, 2); + Vector2d b = new Vector2d (3, 4); + + Vector4ui c = (Vector4ui)(a ^ b); + Vector4ui ta = (Vector4ui)a; + Vector4ui tb = (Vector4ui)b; + + if (c.X != (ta.X ^ tb.X)) + return 1; + if (c.Y != (ta.Y ^ tb.Y)) + return 2; + if (c.Z != (ta.Z ^ tb.Z)) + return 3; + if (c.W != (ta.W ^ tb.W)) + return 4; + return 0; + } + + public static unsafe int test_0_vector2d_or () { + Vector2d a = new Vector2d (1, 2); + Vector2d b = new Vector2d (3, 4); + + Vector4ui c = (Vector4ui)(a | b); + Vector4ui ta = (Vector4ui)a; + Vector4ui tb = (Vector4ui)b; + + if (c.X != (ta.X | tb.X)) + return 1; + if (c.Y != (ta.Y | tb.Y)) + return 2; + if (c.Z != (ta.Z | tb.Z)) + return 3; + if (c.W != (ta.W | tb.W)) + return 4; + return 0; + } + + public static unsafe int test_0_vector2d_and () { + Vector2d a = new Vector2d (1, 2); + Vector2d b = new Vector2d (3, 4); + + Vector4ui c = (Vector4ui)(a & b); + Vector4ui ta = (Vector4ui)a; + Vector4ui tb = (Vector4ui)b; + + if (c.X != (ta.X & tb.X)) + return 1; + if (c.Y != (ta.Y & tb.Y)) + return 2; + if (c.Z != (ta.Z & tb.Z)) + return 3; + if (c.W != (ta.W & tb.W)) + return 4; + return 0; + } + + public static unsafe int test_0_vector8s_pack_signed_sat () { + Vector8s a = new Vector8s (-200, 200, 3, 0, 5, 6, 5, 4); + Vector8s b = new Vector8s (9, 2, 1, 2, 3, 6, 5, 6); + + Vector16sb c = a.PackWithSignedSaturation (b); + + if (c.V0 != -128) + return 1; + if (c.V1 != 127) + return 2; + + return 0; + } + + public static unsafe int test_0_vector16sb_sub_sat () { + Vector16sb a = new Vector16sb (100,-100,11,12,13,14,15,0,1,2,3,4,5,6,7,8); + Vector16sb b = new Vector16sb (-100, 100,11,12,4,5,6,7,8,9,10,11,12,13,14,15); + + Vector16sb c = a.SubtractWithSaturation (b); + + if (c.V0 != 127) + return 1; + if (c.V1 != -128) + return 2; + if (c.V2 != 0) + return 3; + if (c.V3 != 0) + return 4; + if (c.V4 != 9) + return 5; + if (c.V5 != 9) + return 6; + if (c.V6 != 9) + return 7; + if (c.V7 != -7) + return 8; + return 0; + } + + public static unsafe int test_0_vector16sb_add_sat () { + Vector16sb a = new Vector16sb (100,-100,11,12,13,14,15,0,1,2,3,4,5,6,7,8); + Vector16sb b = new Vector16sb (100, -100,11,12,4,5,6,7,8,9,10,11,12,13,14,15); + + Vector16sb c = a.AddWithSaturation (b); + + if (c.V0 != 127) + return 1; + if (c.V1 != -128) + return 2; + if (c.V2 != 22) + return 3; + if (c.V3 != 24) + return 4; + if (c.V4 != 17) + return 5; + if (c.V5 != 19) + return 6; + if (c.V6 != 21) + return 7; + if (c.V7 != 7) + return 8; + return 0; + } + + public static unsafe int test_0_vector16sb_cmp_gt () { + Vector16sb a = new Vector16sb (100,-100,11,12,13,14,15,0,1,2,3,4,5,6,7,8); + Vector16sb b = new Vector16sb (-100, 100,11,12,4,5,6,7,8,9,10,11,12,13,14,15); + + Vector16sb c = a.CompareGreaterThan (b); + + if (c.V0 != -1) + return 1; + if (c.V1 != 0) + return 2; + if (c.V2 != 0) + return 3; + if (c.V3 != 0) + return 4; + if (c.V4 != -1) + return 5; + if (c.V5 != -1) + return 6; + if (c.V6 != -1) + return 7; + return 0; + } + + + public static int test_0_vector4ui_pack_with_sat () { + Vector4ui a = new Vector4ui (0xF0000000,0xF0000,3,4); + Vector4ui b = new Vector4ui (5,6,7,8); + + Vector8us c = a.SignedPackWithUnsignedSaturation (b); + + if (c.V0 != 0) + return 1; + if (c.V1 != 0xFFFF) + return 2; + if (c.V2 != 3) + return 3; + if (c.V3 != 4) + return 4; + if (c.V4 != 5) + return 5; + if (c.V5 != 6) + return 6; + if (c.V6 != 7) + return 7; + if (c.V7 != 8) + return 8; + return 0; + } + + public static int test_0_vector8us_pack_with_sat () { + Vector8us a = new Vector8us (0xFF00,1,2,3,4,5,6,7); + Vector8us b = new Vector8us (3,4,5,6,7,8,9,10); + Vector16b c = a.SignedPackWithUnsignedSaturation (b); + + if (c.V0 != 0) + return 1; + if (c.V1 != 1) + return 2; + if (c.V2 != 2) + return 3; + if (c.V8 != 3) + return 4; + if (c.V15 != 10) + return 5; + return 0; + } + + public static int test_0_vector8us_mul_high () { + Vector8us a = new Vector8us (0xFF00, 2, 3, 0, 5, 6, 5, 4); + Vector8us b = new Vector8us (0xFF00, 2, 1, 2, 3, 6, 5, 6); + Vector8us c = a.MultiplyStoreHigh (b); + + if (c.V0 != 0xFE01) + return 1; + if (c.V1 != 0) + return 2; + if (c.V2 != 0) + return 3; + if (c.V3 != 0) + return 4; + if (c.V4 != 0) + return 5; + if (c.V5 != 0) + return 6; + if (c.V6 != 0) + return 7; + if (c.V7 != 0) + return 8; + return 0; + } + + public static int test_0_vector8us_cmpeq () { + Vector8us a = new Vector8us (1, 2, 3, 0, 5, 6, 5, 4); + Vector8us b = new Vector8us (9, 2, 1, 2, 3, 6, 5, 6); + Vector8us c = a.CompareEqual (b); + + if (c.V0 != 0) + return 1; + if (c.V1 != 0xFFFF) + return 2; + if (c.V2 != 0) + return 3; + if (c.V3 != 0) + return 4; + if (c.V4 != 0) + return 5; + if (c.V5 != 0xFFFF) + return 6; + if (c.V6 != 0xFFFF) + return 7; + if (c.V7 != 0) + return 8; + return 0; + } + + + public static int test_0_vector4ui_cmpeq () { + Vector4ui a = new Vector4ui (6,1,6,3); + Vector4ui b = new Vector4ui (3,4,6,7); + Vector4ui c = a.CompareEqual (b); + + if (c.X != 0) + return 1; + if (c.Y != 0) + return 2; + if (c.Z != 0xFFFFFFFF) + return 3; + if (c.W != 0) + return 4; + return 0; + } + + public static int test_0_vector4ui_shuffle () { + Vector4ui a = new Vector4ui (1,2,3,4); + Vector4ui c = a.Shuffle (ShuffleSel.XFromY | ShuffleSel.YFromW | ShuffleSel.ZFromX | ShuffleSel.WFromZ); + + if (c.X != 2) + return 1; + if (c.Y != 4) + return 2; + if (c.Z != 1) + return 3; + if (c.W != 3) + return 4; + return 0; + } + + public static int test_0_vector4ui_min () { + Vector4ui a = new Vector4ui (6,1,6,3); + Vector4ui b = new Vector4ui (3,4,6,7); + Vector4ui c = a.Min (b); + + if (c.X != 3) + return 1; + if (c.Y != 1) + return 2; + if (c.Z != 6) + return 3; + if (c.W != 3) + return 4; + return 0; + } + + public static int test_0_vector4ui_max () { + Vector4ui a = new Vector4ui (6,1,6,3); + Vector4ui b = new Vector4ui (3,4,6,7); + Vector4ui c = a.Max (b); + + if (c.X != 6) + return 1; + if (c.Y != 4) + return 2; + if (c.Z != 6) + return 3; + if (c.W != 7) + return 4; + return 0; + } + + public static int vector16b_cmpeq () { + Vector16b a = new Vector16b (1,0,9,0,0,0,0,0,0,0,0,0,0,0,0,1); + Vector16b b = new Vector16b (0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0); + Vector16b c = a.CompareEqual (b); + + if (c.V0 != 0) + return 1; + if (c.V1 != 0) + return 2; + if (c.V2 != 0) + return 3; + if (c.V3 != 0xff) + return 4; + if (c.V4 != 0xff) + return 5; + if (c.V5 != 0xff) + return 6; + if (c.V6 != 0xff) + return 7; + if (c.V7 != 0xff) + return 8; + if (c.V8 != 0xff) + return 9; + if (c.V9 != 0xff) + return 10; + if (c.V10 != 0xff) + return 11; + if (c.V11 != 0xff) + return 12; + if (c.V12 != 0xff) + return 13; + if (c.V13 != 0xff) + return 14; + if (c.V14 != 0xff) + return 15; + if (c.V15 != 0) + return 16; + return 0; + } + + + public static int vector16b_sum_abs_diff () { + Vector16b a = new Vector16b (100,20,20,20,0,0,0,0,0,0,0,0,0,0, 0, 0); + Vector16sb b = new Vector16sb (0, 10,10,10,0,0,0,0,0,0,0,0,0,0,10,10); + Vector8us c = a.SumOfAbsoluteDifferences (b); + + if (c.V0 != 130) + return 1; + if (c.V1 != 0) + return 2; + if (c.V2 != 0) + return 3; + if (c.V3 != 0) + return 4; + if (c.V4 != 20) + return 5; + if (c.V5 != 0) + return 6; + if (c.V6 != 0) + return 7; + if (c.V7 != 0) + return 8; + return 0; + } + + + public static int test_0_vector16b_extract_mask () { + Vector16b a = new Vector16b (0xF0,0,0xF0,0,0,0,0xF0,0xAA,0x0F,0,0xFF,0,0,0,0,0); + int c = a.ExtractByteMask (); + + if (c != 0x4C5) + return 1; + return 0; + } + + public static int test_0_vector16b_min () { + Vector16b a = new Vector16b (0,12,20,12,4,5,6,7,8,9,10,11,12,13,14,15); + Vector16b b = new Vector16b (9,10,11,12,13,14,15,0,1,2,3,4,5,6,7,8); + Vector16b c = a.Min (b); + + if (c.V0 != 0) + return 1; + if (c.V1 != 10) + return 2; + if (c.V2 != 11) + return 3; + if (c.V3 != 12) + return 4; + if (c.V4 != 4) + return 5; + if (c.V5 != 5) + return 6; + if (c.V6 != 6) + return 7; + if (c.V7 != 0) + return 8; + if (c.V8 != 1) + return 9; + if (c.V9 != 2) + return 10; + if (c.V10 != 3) + return 11; + if (c.V11 != 4) + return 12; + if (c.V12 != 5) + return 13; + if (c.V13 != 6) + return 14; + if (c.V14 != 7) + return 15; + if (c.V15 != 8) + return 16; + return 0; + } + + public static int test_0_vector16b_max () { + Vector16b a = new Vector16b (0,12,20,12,4,5,6,7,8,9,10,11,12,13,14,15); + Vector16b b = new Vector16b (9,10,11,12,13,14,15,0,1,2,3,4,5,6,7,8); + Vector16b c = a.Max (b); + + if (c.V0 != 9) + return 1; + if (c.V1 != 12) + return 2; + if (c.V2 != 20) + return 3; + if (c.V3 != 12) + return 4; + if (c.V4 != 13) + return 5; + if (c.V5 != 14) + return 6; + if (c.V6 != 15) + return 7; + if (c.V7 != 7) + return 8; + if (c.V8 != 8) + return 9; + if (c.V9 != 9) + return 10; + if (c.V10 != 10) + return 11; + if (c.V11 != 11) + return 12; + if (c.V12 != 12) + return 13; + if (c.V13 != 13) + return 14; + if (c.V14 != 14) + return 15; + if (c.V15 != 15) + return 16; + return 0; + } + public static int test_0_vector16b_avg () { + Vector16b a = new Vector16b (0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15); + Vector16b b = new Vector16b (9,10,11,12,13,14,15,0,1,2,3,4,5,6,7,8); + Vector16b c = a.Average (b); + + if (c.V0 != 5) + return 1; + if (c.V1 != 6) + return 2; + if (c.V2 != 7) + return 3; + if (c.V3 != 8) + return 4; + if (c.V4 != 9) + return 5; + if (c.V5 != 10) + return 6; + if (c.V6 != 11) + return 7; + if (c.V7 != 4) + return 8; + if (c.V8 != 5) + return 9; + if (c.V9 != 6) + return 10; + if (c.V10 != 7) + return 11; + if (c.V11 != 8) + return 12; + if (c.V12 != 9) + return 13; + if (c.V13 != 10) + return 14; + if (c.V14 != 11) + return 15; + if (c.V15 != 12) + return 16; + return 0; + } + + + static unsafe Vector8us bad_method_regression_2 (Vector16b va, Vector16b vb) { + Vector8us res = new Vector8us (); + byte *a = (byte*)&va; + byte *b = (byte*)&vb; + + int tmp = 0; + for (int i = 0; i < 8; ++i) + tmp += System.Math.Abs ((int)*a++ - (int)*b++); + res.V0 = (ushort)tmp; + + tmp = 0; + for (int i = 0; i < 8; ++i) + tmp += System.Math.Abs ((int)*a++ - (int)*b++); + res.V4 = (ushort)tmp; + return res; + } + + /*This bug was caused the simplifier not taking notice of LDADDR on the remaining blocks.*/ + public static int test_2_local_simplifier_regression_other_blocks () { + Vector16b a = new Vector16b (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1); + Vector16b b = new Vector16b (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); + Vector8us res = bad_method_regression_2 (a,b); + return (int)res.V0 + res.V4; + } + + static unsafe Vector8us bad_method_regression (Vector16b va, Vector16b vb) { + Vector8us res = new Vector8us (); + byte *a = (byte*)&va; + byte *b = (byte*)&vb; + *((ushort*)&res) = 10; + + int tmp = 0; + if (*b != 0) + tmp++; + + Vector8us dd = res; + dd = dd + dd - dd; + return dd; + } + + /*This bug was caused the simplifier not taking notice of LDADDR on the first block.*/ + public static int test_10_local_simplifier_regression_first_block () { + Vector16b a = new Vector16b (); + Vector16b b = new Vector16b (); + Vector8us res = bad_method_regression (a,b); + return (int)res.V0; + } + + public static int test_0_vecto8us_shuffle_low () { + Vector8us a = new Vector8us (1, 2, 3, 4, 5, 6, 7, 8); + Vector8us c = a.ShuffleLow (ShuffleSel.XFromY | ShuffleSel.YFromW | ShuffleSel.ZFromX | ShuffleSel.WFromZ); + + if (c.V0 != 2) + return 1; + if (c.V1 != 4) + return 2; + if (c.V2 != 1) + return 3; + if (c.V3 != 3) + return 4; + if (c.V4 != 5) + return 5; + if (c.V5 != 6) + return 6; + if (c.V6 != 7) + return 7; + if (c.V7 != 8) + return 8; + return 0; + } + + public static int test_0_vecto8us_shuffle_high () { + Vector8us a = new Vector8us (1, 2, 3, 4, 5, 6, 7, 8); + Vector8us c = a.ShuffleHigh (ShuffleSel.XFromY | ShuffleSel.YFromW | ShuffleSel.ZFromX | ShuffleSel.WFromZ); + + if (c.V0 != 1) + return 1; + if (c.V1 != 2) + return 2; + if (c.V2 != 3) + return 3; + if (c.V3 != 4) + return 4; + if (c.V4 != 6) + return 5; + if (c.V5 != 8) + return 6; + if (c.V6 != 5) + return 7; + if (c.V7 != 7) + return 8; + + return 0; + } + + public static int test_0_vecto8us_max () { + Vector8us a = new Vector8us (1, 2, 3, 4, 5, 6, 7, 8); + Vector8us b = new Vector8us (9, 1, 1, 2, 9, 6, 5, 1000); + Vector8us c = a.Max (b); + + if (c.V0 != 9) + return 1; + if (c.V1 != 2) + return 2; + if (c.V2 != 3) + return 3; + if (c.V3 != 4) + return 4; + if (c.V4 != 9) + return 5; + if (c.V5 != 6) + return 6; + if (c.V6 != 7) + return 7; + if (c.V7 != 1000) + return 0; + + return 0; + } + + public static int test_0_vecto8us_min () { + Vector8us a = new Vector8us (1, 2, 3, 0, 5, 6, 5, 4); + Vector8us b = new Vector8us (9, 1, 1, 2, 3, 4, 5, 6); + Vector8us c = a.Min (b); + + if (c.V0 != 1) + return 1; + if (c.V1 != 1) + return 2; + if (c.V2 != 1) + return 3; + if (c.V3 != 0) + return 4; + if (c.V4 != 3) + return 5; + if (c.V5 != 4) + return 6; + if (c.V6 != 5) + return 7; + if (c.V7 != 4) + return 8; + return 0; + } + + public static int test_0_vecto8us_avg () { + Vector8us a = new Vector8us (1, 2, 3, 4, 5, 6, 7, 8); + Vector8us b = new Vector8us (9, 1, 1, 2, 3, 4, 5, 6); + Vector8us c = a.Average (b); + + if (c.V0 != 5) + return 1; + if (c.V1 != 2) + return 2; + if (c.V2 != 2) + return 3; + if (c.V3 != 3) + return 4; + if (c.V4 != 4) + return 5; + if (c.V5 != 5) + return 6; + if (c.V6 != 6) + return 7; + if (c.V7 != 7) + return 8; + return 0; + } + + static void store_helper (ref Vector4f x) { + Vector4f k; + k = new Vector4f(9,9,9,9); + x = k; + } + + public static int test_0_vector4f_byref_store () + { + Vector4f k; + k = new Vector4f(1,2,3,4); + store_helper (ref k); + if (k.X != 9) + return 1; + return 0; + } + + public static int test_0_vector4f_init_array_element () + { + Vector4f[] v = new Vector4f[1]; + v[0] = new Vector4f(9,9,9,9); + if (v [0].X != 9) + return 1; + return 0; + } + + public static int test_0_vector4f_dup_high () { + Vector4f a = new Vector4f (1, 2, 3, 4); + Vector4f c = a.DuplicateHigh(); + + if (c.X != 2) + return 1; + if (c.Y != 2) + return 2; + if (c.Z != 4) + return 3; + if (c.W != 4) + return 4; + return 0; + } + + public static int test_0_vector4f_dup_low () { + Vector4f a = new Vector4f (1, 2, 3, 4); + Vector4f c = a.DuplicateLow (); + + if (c.X != 1) + return 1; + if (c.Y != 1) + return 2; + if (c.Z != 3) + return 3; + if (c.W != 3) + return 4; + return 0; + } + + + public static int test_0_vector4f_interleave_high () { + Vector4f a = new Vector4f (1, 2, 3, 4); + Vector4f b = new Vector4f (5, 6, 7, 8); + Vector4f c = a.InterleaveHigh (b); + + if (c.X != 3) + return 1; + if (c.Y != 7) + return 2; + if (c.Z != 4) + return 3; + if (c.W != 8) + return 4; + return 0; + } + + public static int test_0_vector4f_interleave_low () { + Vector4f a = new Vector4f (1, 2, 3, 4); + Vector4f b = new Vector4f (5, 6, 7, 8); + Vector4f c = a.InterleaveLow (b); + + if (c.X != 1) + return 1; + if (c.Y != 5) + return 2; + if (c.Z != 2) + return 3; + if (c.W != 6) + return 4; + return 0; + } + + public static int test_0_vector4f_rcp () { + Vector4f a = new Vector4f (1, 2, 4, 8); + Vector4f c = a.Reciprocal (); + + //Test with ranges due to the terrible precision. + if (c.X < (1 - 0.01f) || c.X > (1 + 0.01f)) + return 1; + if (c.Y < (0.5 - 0.01f) || c.Y > (0.5 + 0.01f)) + return 2; + if (c.Z < (0.25 - 0.01f) || c.Z > (0.25 + 0.01f)) + return 3; + if (c.W < (0.125 - 0.01f) || c.W > (0.125 + 0.01f)) + return 4; + return 0; + } + + public static int test_0_vector4f_xor () { + Vector4f a = new Vector4f (1, 2, 3, 4); + Vector4f b = new Vector4f (1, 3, 3, 8); + Vector4f c = a ^ b; + + if (((Vector4ui)c).X != 0) + return 1; + if (((Vector4ui)c).Y != 0x400000) + return 2; + if (((Vector4ui)c).Z != 0) + return 3; + if (((Vector4ui)c).W != 0x1800000) + return 4; + return 0; + } + + public static int test_0_vector4f_or () { + Vector4f a = new Vector4f (1, 2, 3, 4); + Vector4f b = new Vector4f (1, 3, 3, 8); + Vector4f c = a | b; + + if (((Vector4ui)c).X != 0x3F800000) + return 1; + if (((Vector4ui)c).Y != 0x40400000) + return 2; + if (((Vector4ui)c).Z != 0x40400000) + return 3; + if (((Vector4ui)c).W != 0x41800000) + return 4; + return 0; + } + public static int test_0_vector4f_andn () { + Vector4f a = new Vector4f (1, 2, 3, 4); + Vector4f b = new Vector4f (1, 3, 3, 8); + Vector4f c = a.AndNot (b); + + if (((Vector4ui)c).X != 0) + return 1; + if (((Vector4ui)c).Y != 0x400000) + return 2; + if (((Vector4ui)c).Z != 0) + return 3; + if (((Vector4ui)c).W != 0x1000000) + return 4; + return 0; + } + + public static int test_0_vector4f_and () { + Vector4f a = new Vector4f (1, 2, 3, 4); + Vector4f b = new Vector4f (1, 3, 3, 8); + Vector4f c = a & b; + + if (((Vector4ui)c).X != 0x3F800000) + return 1; + if (((Vector4ui)c).Y != 0x40000000) + return 2; + if (((Vector4ui)c).Z != 0x40400000) + return 3; + if (((Vector4ui)c).W != 0x40000000) + return 4; + return 0; + } + + public static int test_0_vector4f_cmpord () { + Vector4f a = new Vector4f (float.NaN, 2, 3, 4); + Vector4f b = new Vector4f (1, float.NaN, 3, 6); + Vector4f c = a.CompareOrdered (b); + + if (((Vector4ui)c).X != 0) + return 1; + if (((Vector4ui)c).Y != 0) + return 2; + if (((Vector4ui)c).Z != 0xFFFFFFFF) + return 3; + if (((Vector4ui)c).W != 0xFFFFFFFF) + return 4; + return 0; + } + + public static int test_0_vector4f_cmpnle () { + Vector4f a = new Vector4f (float.NaN, 2, 3, 4); + Vector4f b = new Vector4f (1, float.NaN, 3, 6); + Vector4f c = a.CompareNotLessEqual (b); + + if (((Vector4ui)c).X != 0xFFFFFFFF) + return 1; + if (((Vector4ui)c).Y != 0xFFFFFFFF) + return 2; + if (((Vector4ui)c).Z != 0) + return 3; + if (((Vector4ui)c).W != 0) + return 4; + return 0; + } + + public static int test_0_vector4f_cmpnlt () { + Vector4f a = new Vector4f (float.NaN, 2, 3, 4); + Vector4f b = new Vector4f (1, float.NaN, 3, 6); + Vector4f c = a.CompareNotLessThan (b); + + if (((Vector4ui)c).X != 0xFFFFFFFF) + return 1; + if (((Vector4ui)c).Y != 0xFFFFFFFF) + return 2; + if (((Vector4ui)c).Z != 0xFFFFFFFF) + return 3; + if (((Vector4ui)c).W != 0) + return 4; + return 0; + } + + public static int test_0_vector4f_cmpneq () { + Vector4f a = new Vector4f (float.NaN, 2, 3, 4); + Vector4f b = new Vector4f (1, float.NaN, 3, 6); + Vector4f c = a.CompareNotEqual (b); + + if (((Vector4ui)c).X != 0xFFFFFFFF) + return 1; + if (((Vector4ui)c).Y != 0xFFFFFFFF) + return 2; + if (((Vector4ui)c).Z != 0) + return 3; + if (((Vector4ui)c).W != 0xFFFFFFFF) + return 4; + return 0; + } + + public static int test_0_vector4f_cmpunord () { + Vector4f a = new Vector4f (float.NaN, 2, 3, 4); + Vector4f b = new Vector4f (1, float.NaN, 3, 6); + Vector4f c = a.CompareUnordered (b); + + if (((Vector4ui)c).X != 0xFFFFFFFF) + return 1; + if (((Vector4ui)c).Y != 0xFFFFFFFF) + return 2; + if (((Vector4ui)c).Z != 0) + return 3; + if (((Vector4ui)c).W != 0) + return 4; + return 0; + } + + public static int test_0_vector4f_cmple () { + Vector4f a = new Vector4f (float.NaN, 2, 3, 4); + Vector4f b = new Vector4f (1, float.NaN, 3, 6); + Vector4f c = a.CompareLessEqual (b); + + if (((Vector4ui)c).X != 0) + return 1; + if (((Vector4ui)c).Y != 0) + return 2; + if (((Vector4ui)c).Z != 0xFFFFFFFF) + return 3; + if (((Vector4ui)c).W != 0xFFFFFFFF) + return 4; + return 0; + } + + public static int test_0_vector4f_cmplt () { + Vector4f a = new Vector4f (float.NaN, 2, 3, 4); + Vector4f b = new Vector4f (1, float.NaN, 3, 6); + Vector4f c = a.CompareLessThan (b); + + if (((Vector4ui)c).X != 0) + return 1; + if (((Vector4ui)c).Y != 0) + return 2; + if (((Vector4ui)c).Z != 0) + return 3; + if (((Vector4ui)c).W != 0xFFFFFFFF) + return 4; + return 0; + } + + public static int test_0_vector4f_cmpeq () { + Vector4f a = new Vector4f (float.NaN, 2, 3, 6); + Vector4f b = new Vector4f (1, float.NaN, 3, 4); + Vector4f c = a.CompareEqual (b); + + if (((Vector4ui)c).X != 0) + return 1; + if (((Vector4ui)c).Y != 0) + return 2; + if (((Vector4ui)c).Z != 0xFFFFFFFF) + return 3; + if (((Vector4ui)c).W != 0) + return 4; + return 0; + } + + public static int test_0_vector4ui_sar () { + Vector4ui a = new Vector4ui (0xF0000000u,20,3,40); + + Vector4ui c = a.ArithmeticRightShift (2); + + if (c.X != 0xFC000000) + return 1; + if (c.Y != 5) + return 2; + if (c.Z != 0) + return 3; + if (c.W != 10) + return 4; + return 0; + } + + public static int test_0_vector4ui_unpack_high () { + Vector4ui a = new Vector4ui (1,2,3,4); + Vector4ui b = new Vector4ui (5,6,7,8); + + Vector4ui c = a.UnpackHigh(b); + + if (c.X != 3) + return 1; + if (c.Y != 7) + return 2; + if (c.Z != 4) + return 3; + if (c.W != 8) + return 4; + return 0; + } + + public static int test_0_vector4ui_unpack_low () { + Vector4ui a = new Vector4ui (1,2,3,4); + Vector4ui b = new Vector4ui (5,6,7,8); + + Vector4ui c = a.UnpackLow (b); + + if (c.X != 1) + return 1; + if (c.Y != 5) + return 2; + if (c.Z != 2) + return 3; + if (c.W != 6) + return 4; + return 0; + } + + public static int test_0_vector4ui_xor () { + Vector4ui a = new Vector4ui (1,2,3,4); + Vector4ui b = new Vector4ui (7,5,3,1); + + Vector4ui c = a ^ b; + + if (c.X != 6) + return 1; + if (c.Y != 7) + return 2; + if (c.Z != 0) + return 3; + if (c.W != 5) + return 4; + return 0; + } + + public static int test_0_vector4ui_or () { + Vector4ui a = new Vector4ui (1,2,3,4); + Vector4ui b = new Vector4ui (7,5,3,1); + + Vector4ui c = a | b; + + if (c.X != 7) + return 1; + if (c.Y != 7) + return 2; + if (c.Z != 3) + return 3; + if (c.W != 5) + return 4; + return 0; + } + public static int test_0_vector4ui_and () { + Vector4ui a = new Vector4ui (1,2,3,4); + Vector4ui b = new Vector4ui (7,5,3,1); + + Vector4ui c = a & b; + + if (c.X != 1) + return 1; + if (c.Y != 0) + return 2; + if (c.Z != 3) + return 3; + if (c.W != 0) + return 4; + return 0; + } + + public static int test_0_vector4ui_shr () { + Vector4ui a = new Vector4ui (0xF0000000u,20,3,40); + + Vector4ui c = a >> 2; + + if (c.X != 0x3C000000) + return 1; + if (c.Y != 5) + return 2; + if (c.Z != 0) + return 3; + if (c.W != 10) + return 4; + return 0; + } + + public static int test_0_vector4ui_shl () { + Vector4ui a = new Vector4ui (10,20,3,40); + + Vector4ui c = a << 2; + + if (c.X != 40) + return 1; + if (c.Y != 80) + return 2; + if (c.Z != 12) + return 3; + if (c.W != 160) + return 4; + return 0; + } + + public static int test_0_vector4ui_mul () { + Vector4ui a = new Vector4ui (0x8888,20,3,40); + Vector4ui b = new Vector4ui (0xFF00FF00u,2,3,4); + + Vector4ui c = a * b; + + if (c.X != 0xffff7800) + return 1; + if (c.Y != 40) + return 2; + if (c.Z != 9) + return 3; + if (c.W != 160) + return 4; + return 0; + } + public static int test_0_vector4ui_sub () { + Vector4ui a = new Vector4ui (1,20,3,40); + Vector4ui b = new Vector4ui (0xFF00FF00u,2,3,4); + + Vector4ui c = a - b; + + if (c.X != 0xff0101) + return 1; + if (c.Y != 18) + return 2; + if (c.Z != 0) + return 3; + if (c.W != 36) + return 4; + return 0; + } + + public static int test_0_vector4ui_add () { + Vector4ui a = new Vector4ui (0xFF00FF00u,2,3,4); + Vector4ui b = new Vector4ui (0xFF00FF00u,2,3,4); + + Vector4ui c = a + b; + + if (c.X != 0xfe01fe00) + return 1; + if (c.Y != 4) + return 2; + if (c.Z != 6) + return 3; + if (c.W != 8) + return 4; + return 0; + } + + + static int test_0_vector4ui_accessors () { + Vector4ui a = new Vector4ui (1,2,3,4); + + if (a.X != 1) + return 1; + if (a.Y != 2) + return 2; + if (a.Z != 3) + return 3; + if (a.W != 4) + return 4; + a.X = 10; + a.Y = 20; + a.Z = 30; + a.W = 40; + + if (a.X != 10) + return 5; + if (a.Y != 20) + return 6; + if (a.Z != 30) + return 7; + if (a.W != 40) + return 8; + return 0; + } + + static int test_0_vector8us_sub_sat () { + Vector8us a = new Vector8us (0xF000,1,20,3,4,5,6,7); + Vector8us b = new Vector8us (0xFF00,4,5,6,7,8,9,10); + Vector8us c = a.SubtractWithSaturation (b); + + if (c.V0 != 0) + return 1; + if (c.V1 != 0) + return 2; + if (c.V2 != 15) + return 3; + if (c.V3 != 0) + return 4; + if (c.V4 != 0) + return 5; + if (c.V5 != 0) + return 6; + if (c.V6 != 0) + return 7; + if (c.V7 != 0) + return 8; + return 0; + } + + static int test_0_vector8us_add_sat () { + Vector8us a = new Vector8us (0xFF00,1,2,3,4,5,6,7); + Vector8us b = new Vector8us (0xFF00,4,5,6,7,8,9,10); + Vector8us c = a.AddWithSaturation (b); + + if (c.V0 != 0xFFFF) + return 1; + if (c.V1 != 5) + return 2; + if (c.V2 != 7) + return 3; + if (c.V3 != 9) + return 4; + if (c.V4 != 11) + return 5; + if (c.V5 != 13) + return 6; + if (c.V6 != 15) + return 7; + if (c.V7 != 17) + return 8; + return 0; + } + + static int test_0_vector8us_unpack_low () { + Vector8us a = new Vector8us (0,1,2,3,4,5,6,7); + Vector8us b = new Vector8us (3,4,5,6,7,8,9,10); + Vector8us c = a.UnpackLow (b); + + if (c.V0 != 0) + return 1; + if (c.V1 != 3) + return 2; + if (c.V2 != 1) + return 3; + if (c.V3 != 4) + return 4; + if (c.V4 != 2) + return 5; + if (c.V5 != 5) + return 6; + if (c.V6 != 3) + return 7; + if (c.V7 != 6) + return 8; + return 0; + } + + + static int test_0_vector8us_shift_left () { + Vector8us a = new Vector8us (0xFF00,1,2,3,4,5,6,7); + int amt = 2; + Vector8us c = a << amt; + + if (c.V0 != 0xFC00) + return 1; + if (c.V1 != 4) + return 2; + if (c.V7 != 28) + return 3; + return 0; + } + + static int test_0_vector8us_shift_right_arithmetic () { + Vector8us a = new Vector8us (0xFF00,1,2,3,4,5,6,7); + int amt = 2; + Vector8us c = a.ArithmeticRightShift (amt); + + if (c.V0 != 0xFFC0) + return 1; + if (c.V1 != 0) + return 2; + if (c.V7 != 1) + return 3; + return 0; + } + + static int test_0_vector8us_shift_variable_offset () { + int off = 2; + Vector8us a = new Vector8us (0xF000,1,2,3,4,5,6,7); + Vector8us b = a; + Vector8us c = b >> off; + a = b + b; + + if (c.V0 != 0x3C00) + return 1; + if (c.V1 != 0) + return 2; + if (c.V7 != 1) + return 3; + if (a.V1 != 2) + return 4; + if (a.V7 != 14) + return 5; + return 0; + } + + + static int test_0_vector8us_shift_operand_is_live_after_op () { + Vector8us a = new Vector8us (0xF000,1,2,3,4,5,6,7); + Vector8us b = a; + Vector8us c = b >> 2; + a = b + b; + + if (c.V0 != 0x3C00) + return 1; + if (c.V1 != 0) + return 2; + if (c.V7 != 1) + return 3; + if (a.V1 != 2) + return 4; + if (a.V7 != 14) + return 5; + return 0; + } + + static int test_0_vector8us_shr_constant () { + Vector8us a = new Vector8us (0xF000,1,2,3,4,5,6,7); + Vector8us c = a >> 2; + + if (c.V0 != 0x3C00) + return 1; + if (c.V1 != 0) + return 2; + if (c.V7 != 1) + return 3; + return 0; + } + + static int test_0_vector8us_mul () { + Vector8us a = new Vector8us (0x0F00,4,5,6,7,8,9,10); + Vector8us b = new Vector8us (0x0888,1,2,3,4,5,6,8); + + Vector8us c = a * b; + if (c.V0 != 63488) + return 1; + if (c.V1 != 4) + return 2; + if (c.V7 != 80) + return 3; + return 0; + } + + static int test_0_vector8us_add () { + Vector8us a = new Vector8us (0xFF00,4,5,6,7,8,9,10); + Vector8us b = new Vector8us (0x8888,1,2,3,4,5,6,8); + + Vector8us c = a + b; + if (c.V0 != 34696) + return 1; + if (c.V1 != 5) + return 2; + if (c.V7 != 18) + return 3; + return 0; + } + + + static int test_0_vector8us_sub () { + Vector8us a = new Vector8us (3,4,5,6,7,8,9,10); + Vector8us b = new Vector8us (10,1,2,3,4,5,6,8); + + Vector8us c = a - b; + + if (c.V0 != 65529) + return 1; + if (c.V1 != 3) + return 2; + if (c.V7 != 2) + return 3; + return 0; + } + + + static int test_0_vector8us_accessors () { + Vector8us a = new Vector8us (0,1,2,3,4,5,6,7); + + if (a.V0 != 0) + return 1; + if (a.V1 != 1) + return 2; + if (a.V2 != 2) + return 3; + if (a.V3 != 3) + return 4; + if (a.V4 != 4) + return 5; + if (a.V5 != 5) + return 6; + if (a.V6 != 6) + return 7; + if (a.V7 != 7) + return 8; + a.V0 = 10; + a.V1 = 20; + a.V2 = 30; + a.V3 = 40; + a.V4 = 50; + a.V5 = 60; + a.V6 = 70; + a.V7 = 80; + + if (a.V0 != 10) + return 17; + if (a.V1 != 20) + return 18; + if (a.V2 != 30) + return 19; + if (a.V3 != 40) + return 20; + if (a.V4 != 50) + return 21; + if (a.V5 != 60) + return 22; + if (a.V6 != 70) + return 23; + if (a.V7 != 80) + return 24; + + return 0; + } + + + static int test_0_vector16b_unpack_high () { + Vector16b a = new Vector16b (0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15); + Vector16b b = new Vector16b (9,10,11,12,13,14,15,0,1,2,3,4,5,6,7,8); + Vector16b c = a.UnpackHigh (b); + + if (c.V0 != 8) + return 1; + if (c.V1 != 1) + return 2; + if (c.V2 != 9) + return 3; + if (c.V3 != 2) + return 4; + if (c.V4 != 10) + return 5; + if (c.V5 != 3) + return 6; + if (c.V14 != 15) + return 7; + if (c.V15 != 8) + return 8; + return 0; + } + + static int test_0_vector16b_unpack_low () { + Vector16b a = new Vector16b (0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15); + Vector16b b = new Vector16b (9,10,11,12,13,14,15,0,1,2,3,4,5,6,7,8); + Vector16b c = a.UnpackLow (b); + + if (c.V0 != 0) + return 1; + if (c.V1 != 9) + return 2; + if (c.V2 != 1) + return 3; + if (c.V3 != 10) + return 4; + if (c.V4 != 2) + return 5; + if (c.V5 != 11) + return 6; + if (c.V14 != 7) + return 7; + if (c.V15 != 0) + return 8; + return 0; + } + + static int test_0_vector16b_sub_sat () { + Vector16b a = new Vector16b (100,10,11,12,13,14,15,0,1,2,3,4,5,6,7,8); + Vector16b b = new Vector16b (200,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15); + Vector16b c = a.SubtractWithSaturation (b); + + if (c.V0 != 0) + return 1; + if (c.V1 != 9) + return 2; + if (c.V15 != 0) + return 3; + return 0; + } + + static int test_0_vector16b_add_sat () { + Vector16b a = new Vector16b (200,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15); + Vector16b b = new Vector16b (200,10,11,12,13,14,15,0,1,2,3,4,5,6,7,8); + Vector16b c = a.AddWithSaturation (b); + + if (c.V0 != 255) + return 1; + if (c.V1 != 11) + return 2; + if (c.V15 != 23) + return 3; + return 0; + } + + static int test_0_vector16b_add_ovf () { + Vector16b a = new Vector16b (200,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15); + Vector16b b = new Vector16b (200,10,11,12,13,14,15,0,1,2,3,4,5,6,7,8); + Vector16b c = a + b; + + if (c.V0 != 144) + return 1; + if (c.V1 != 11) + return 2; + if (c.V15 != 23) + return 3; + return 0; + } + + static int test_0_vector16b_accessors () { + Vector16b a = new Vector16b (0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15); + + if (a.V0 != 0) + return 1; + if (a.V1 != 1) + return 2; + if (a.V2 != 2) + return 3; + if (a.V3 != 3) + return 4; + if (a.V4 != 4) + return 5; + if (a.V5 != 5) + return 6; + if (a.V6 != 6) + return 7; + if (a.V7 != 7) + return 8; + if (a.V8 != 8) + return 9; + if (a.V9 != 9) + return 10; + if (a.V10 != 10) + return 11; + if (a.V11 != 11) + return 12; + if (a.V12 != 12) + return 13; + if (a.V13 != 13) + return 14; + if (a.V14 != 14) + return 15; + if (a.V15 != 15) + return 16; + + a.V0 = 10; + a.V1 = 20; + a.V2 = 30; + a.V3 = 40; + a.V4 = 50; + a.V5 = 60; + a.V6 = 70; + a.V7 = 80; + a.V8 = 90; + a.V9 = 100; + a.V10 = 110; + a.V11 = 120; + a.V12 = 130; + a.V13 = 140; + a.V14 = 150; + a.V15 = 160; + + if (a.V0 != 10) + return 17; + if (a.V1 != 20) + return 18; + if (a.V2 != 30) + return 19; + if (a.V3 != 40) + return 20; + if (a.V4 != 50) + return 21; + if (a.V5 != 60) + return 22; + if (a.V6 != 70) + return 23; + if (a.V7 != 80) + return 24; + if (a.V8 != 90) + return 25; + if (a.V9 != 100) + return 26; + if (a.V10 != 110) + return 27; + if (a.V11 != 120) + return 28; + if (a.V12 != 130) + return 29; + if (a.V13 != 140) + return 30; + if (a.V14 != 150) + return 31; + if (a.V15 != 160) + return 32; + return 0; + } + + public static int test_0_accessors () { + Vector4f a = new Vector4f (1, 2, 3, 4); + if (a.X != 1f) + return 1; + if (a.Y != 2f) + return 2; + if (a.Z != 3f) + return 3; + if (a.W != 4f) + return 4; + return 0; + } + + public static int test_0_packed_add_with_stack_tmp () { + Vector4f a = new Vector4f (1, 2, 3, 4); + Vector4f b = new Vector4f (5, 6, 7, 8); + Vector4f c = new Vector4f (-1, -3, -4, -5); + Vector4f d = a + b + c; + if (d.X != 5f) + return 1; + if (d.Y != 5f) + return 2; + if (d.Z != 6f) + return 3; + if (d.W != 7f) + return 4; + return 0; + } + + public static int test_0_simple_packed_add () { + Vector4f a = new Vector4f (1, 2, 3, 4); + Vector4f b = new Vector4f (5, 6, 7, 8); + Vector4f c; + c = a + b; + if (c.X != 6f) + return 1; + if (c.Y != 8f) + return 2; + if (c.Z != 10f) + return 3; + if (c.W != 12f) + return 4; + return 0; + } + + public static int test_0_simple_packed_sub () { + Vector4f a = new Vector4f (1, 2, 3, 4); + Vector4f b = new Vector4f (5, 6, 7, 8); + Vector4f c = b - a; + if (c.X != 4f) + return 1; + if (c.Y != 4f) + return 2; + if (c.Z != 4f) + return 3; + if (c.W != 4f) + return 4; + return 0; + } + + public static int test_0_simple_packed_mul () { + Vector4f a = new Vector4f (1, 2, 3, 4); + Vector4f b = new Vector4f (5, 6, 7, 8); + Vector4f c = b * a; + if (c.X != 5f) + return 1; + if (c.Y != 12f) + return 2; + if (c.Z != 21f) + return 3; + if (c.W != 32f) + return 4; + return 0; + } + + public static int test_0_simple_packed_div () { + Vector4f a = new Vector4f (2, 2, 3, 4); + Vector4f b = new Vector4f (20, 10, 33, 12); + Vector4f c = b / a; + if (c.X != 10f) + return 1; + if (c.Y != 5f) + return 2; + if (c.Z != 11f) + return 3; + if (c.W != 3f) + return 4; + return 0; + } + + public static int test_0_simple_packed_sqrt () { + Vector4f a = new Vector4f (16, 4, 9, 25); + a = a.Sqrt (); + if (a.X != 4f) + return 1; + if (a.Y != 2f) + return 2; + if (a.Z != 3f) + return 3; + if (a.W != 5f) + return 4; + return 0; + } + + public static int test_0_simple_packed_invsqrt () { + Vector4f a = new Vector4f (16, 4, 100, 25); + //this function has VERY low precision + a = a.InvSqrt (); + if (a.X < (1/4f - 0.01f) || a.X > (1/4f + 0.01f)) + return 1; + if (a.Y < (1/2f - 0.01f) || a.Y > (1/2f + 0.01f)) + return 2; + if (a.Z < (1/10f - 0.01f) || a.Z > (1/10f + 0.01f)) + return 3; + if (a.W < (1/5f - 0.01f) || a.W > (1/5f + 0.01f)) + return 4; + return 0; + } + + public static int test_0_simple_packed_min () { + Vector4f a = new Vector4f (16, -4, 9, 25); + Vector4f b = new Vector4f (5, 3, 9, 0); + Vector4f c = a.Min (b); + if (c.X != 5f) + return 1; + if (c.Y != -4f) + return 2; + if (c.Z != 9f) + return 3; + if (c.W != 0f) + return 4; + return 0; + } + + public static int test_0_simple_packed_max () { + Vector4f a = new Vector4f (16, -4, 9, 25); + Vector4f b = new Vector4f (5, 3, 9, 0); + Vector4f c = a.Max (b); + if (c.X != 16f) + return 1; + if (c.Y != 3f) + return 2; + if (c.Z != 9f) + return 3; + if (c.W != 25f) + return 4; + return 0; + } + + public static int test_0_simple_packed_hadd () { + Vector4f a = new Vector4f (5, 5, 6, 6); + Vector4f b = new Vector4f (7, 7, 8, 8); + Vector4f c = a.HorizontalAdd (b); + if (c.X != 10f) + return 1; + if (c.Y != 12f) + return 2; + if (c.Z != 14f) + return 3; + if (c.W != 16f) + return 4; + return 0; + } + + public static int test_0_simple_packed_hsub () { + Vector4f a = new Vector4f (5, 2, 6, 1); + Vector4f b = new Vector4f (7, 0, 8, 3); + Vector4f c = a.HorizontalSub (b); + if (c.X != 3f) + return 1; + if (c.Y != 5f) + return 2; + if (c.Z != 7f) + return 3; + if (c.W != 5f) + return 4; + return 0; + } + + public static int test_0_simple_packed_addsub () { + Vector4f a = new Vector4f (5, 2, 6, 1); + Vector4f b = new Vector4f (7, 0, 8, 3); + Vector4f c = a.AddSub (b); + if (c.X != -2f) + return 1; + if (c.Y != 2f) + return 2; + if (c.Z != -2f) + return 3; + if (c.W != 4f) + return 4; + return 0; + } + + public static int test_0_simple_packed_shuffle () { + Vector4f a = new Vector4f (1, 2, 3, 4); + a = a.Shuffle(ShuffleSel.XFromY | ShuffleSel.YFromW | ShuffleSel.ZFromX | ShuffleSel.WFromZ); + if (a.X != 2f) + return 1; + if (a.Y != 4f) + return 2; + if (a.Z != 1f) + return 3; + if (a.W != 3f) + return 4; + return 0; + } + + public static int test_0_packed_shuffle_with_reg_pressure () { + Vector4f v = new Vector4f (1, 2, 3, 4); + Vector4f m0 = v + v, m1 = v - v, m2 = v * v, m3 = v + v + v; + if (ff) v = v + v -v ; + + Vector4f r0 = v.Shuffle (ShuffleSel.XFromY | ShuffleSel.YFromW | ShuffleSel.ZFromX | ShuffleSel.WFromZ); + Vector4f r1 = v.Shuffle (ShuffleSel.XFromY | ShuffleSel.YFromW | ShuffleSel.ZFromX | ShuffleSel.WFromZ); + Vector4f x = v.Shuffle (ShuffleSel.XFromY | ShuffleSel.YFromW | ShuffleSel.ZFromX | ShuffleSel.WFromZ); + Vector4f r2 = v.Shuffle (ShuffleSel.XFromY | ShuffleSel.YFromW | ShuffleSel.ZFromX | ShuffleSel.WFromZ); + Vector4f r3 = v.Shuffle (ShuffleSel.XFromY | ShuffleSel.YFromW | ShuffleSel.ZFromX | ShuffleSel.WFromZ); + Vector4f a = x; + + r0 = r0 * m0 + x; + r1 = r1 * m1 + x; + x = x - v + v; + r2 = r2 * m2 + x; + r3 = r3 * m3 + x; + Vector4f result = r0 + r1 + r2 + r3; + + if (a.X != 2f) + return 1; + if (a.Y != 4f) + return 2; + if (a.Z != 1f) + return 3; + if (a.W != 3f) + return 4; + if (result.Y != result.Y) + return 0; + return 0; + } + + public static int test_0_double_packed_sqrt () { + Vector2d a = new Vector2d (16, 4); + a = a.Sqrt (); + if (a.X != 4f) + return 1; + if (a.Y != 2f) + return 2; + return 0; + } + + public static int test_24_regs_pressure_a () { + Vector4f a = new Vector4f (1, 2, 3, 4); + Vector4f b = a + a; + Vector4f c = b * a; + Vector4f d = a - b; + c = a + b + c + d; + return (int)c.Z; + } + + public static int test_54_regs_pressure_b () { + Vector4f a = new Vector4f (1, 2, 3, 4); + Vector4f b = a + a; + Vector4f c = b - a; + Vector4f d = c - a; + Vector4f e = a + b + c; + Vector4f f = d - b + a - c; + Vector4f g = a - d * f - c + b; + Vector4f h = a * b - c + e; + Vector4f i = h - g - f - e - d - c - b - a; + Vector4f j = a + b + c + d + e + f + g + h + i; + return (int)j.Z; + } + + public static int test_8_regs_pressure_c () { + Vector4f a = new Vector4f (1, 2, 3, 4); + Vector4f b = a + a; + Vector4f c = b - a; + Vector4f d = c - a; + Vector4f e = a + b + c; + Vector4f f = d - b + a - c; + Vector4f g = a - d * f - c + b; + Vector4f h = a * b - c + e; + Vector4f i = h - g - f - e - d - c - b - a; + Vector4f j = a + b + c + d + e + f + g + h + i; + Vector4f k = j - i - h + e + d - a + b - f + g; + Vector4f l = k * c - j * b - i * e + f - g; + Vector4f m = l - k + j - i + e + f; + Vector4f n = m - j + g - i + e * b + a * d; + Vector4f o = k + j + i * b; + Vector4f p = m + j + i + e + l; + Vector4f q = l * m + j + k; + Vector4f r = p * a + o * b + j * c + m * d + l * e; + Vector4f s = a - b - c - d - e - f - g - h - i - j - k - l - m - p - o - q - r; + Vector4f t = a + b + c + d + e + f + g + h + i + j + k + l + m + p + o + q + r + s; + return (int)t.W; + } + + public static int test_0_regs_pressure_fp_and_simd_share_bank_1 () { + Vector4f a = new Vector4f (4, 3, 2, 1); + float aF = 10f; + Vector4f b = a + a; + float bF = aF + aF; + Vector4f c = b - a; + float cF = bF - aF; + Vector4f d = c - a; + float dF = cF - aF; + Vector4f e = a + b + c; + float eF = aF + bF + cF; + Vector4f f = d - b + a - c; + float fF = dF - bF + aF - cF; + Vector4f g = a - d * f - c + b; + float gF = aF - dF * fF - cF + bF; + Vector4f h = a * b - c + e; + float hF = aF * bF - cF + eF; + Vector4f i = h - g - f - e - d - c - b - a; + float iF = hF - gF - fF - eF - dF - cF - bF - aF; + Vector4f j = a + b + c + d + e + f + g + h + i; + float jF = aF + bF + cF + dF + eF + fF + gF + hF + iF; + + if (j.X != 88f) + return 1; + + if(jF != 460f) + return 2; + + return 0; + } + + public static int test_0_regs_pressure_fp_and_simd_share_bank_2 () { + Vector4f a = new Vector4f (4, 3, 2, 1); + float aF = 10f; + Vector4f b = a + a; + float bF = aF + aF; + Vector4f c = b - a; + float cF = bF - aF; + Vector4f d = c - a; + float dF = cF - aF; + Vector4f e = a + b + c; + float eF = aF + bF + cF; + Vector4f f = d - b + a - c; + float fF = dF - bF + aF - cF; + Vector4f g = a - d * f - c + b; + float gF = aF - dF * fF - cF + bF; + Vector4f h = a * b - c + e; + float hF = aF * bF - cF + eF; + Vector4f i = h - g - f - e - d - c - b - a; + float iF = hF - gF - fF - eF - dF - cF - bF - aF; + Vector4f j = a + b + c + d + e + f + g + h + i; + float jF = aF + bF + cF + dF + eF + fF + gF + hF + iF; + Vector4f k = j - i - h + e + d - a + b - f + g; + float kF = jF - iF - hF + eF + dF - aF + bF - fF + gF; + Vector4f l = k * c - j * b - i * e + f - g; + float lF = kF * cF - jF * bF - iF * eF + fF - gF; + Vector4f m = l - k + j - i + e + f; + float mF = lF - kF + jF - iF + eF + fF; + Vector4f n = m - j + g - i + e * b + a * d; + float nF = mF - jF + gF - iF + eF * bF + aF * dF; + Vector4f o = k + j + i * b; + float oF = kF + jF + iF * bF; + Vector4f p = m + j + i + e + l; + float pF = mF + jF + iF + eF + lF; + Vector4f q = l * m + j + k; + float qF = lF * mF + jF + kF; + Vector4f r = p * a + o * b + j * c + m * d + l * e; + float rF = pF * aF + oF * bF + jF * cF + mF * dF + lF * eF; + Vector4f s = a - b - c - d - e - f - g - h - i - j - k - l - m - p - o - q - r; + float sF = aF - bF - cF - dF - eF - fF - gF - hF - iF - jF - kF - lF - mF - pF - oF - qF - rF; + Vector4f t = a + b + c + d + e + f + g + h + i + j + k + l + m + p + o + q + r + s; + float tF = aF + bF + cF + dF + eF + fF + gF + hF + iF + jF + kF + lF + mF + pF + oF + qF + rF + sF; + + if (t.X != 8f) + return 1; + + if(tF != 14f) + return 2; + + return 0; + } + + + public static void call_simd_fp () { + Vector4f a = new Vector4f (20f, 22f, 23f, 24f); + float b = 25f; + Vector4f c = new Vector4f (26f, 27f, 28f, 29f); + float d = 30f; + + b += d; + a += c; + } + public static int test_0_call_fp_and_simd_share_bank () { + + float a = 1f; + Vector4f b = new Vector4f (2f, 3f, 4f, 5f); + float c = 6f; + Vector4f d = new Vector4f (7f, 8f, 9f, 10f); + + a += c; + + b += d; + + call_simd_fp (); + if (a != 7f) + return 1; + if (b.X != 9f) + return 2; + if (c != 6f) + return 3; + if (d.X != 7f) + return 4; + if (b.W != 15f) + return 5; + if (d.W != 10f) + return 6; + + + return 0; + } + + + static bool ff; + public static int test_3_single_block_var_is_properly_promoted () { + Vector4f a = new Vector4f (4, 5, 6, 7); + if (ff) + a = a - a; + else { + Vector4f b = new Vector4f (1, 2, 3, 4); + Vector4f c = b; + a = a - b; + if (ff) { + c = a; + a = c; + } + } + return (int)a.X; + } + + static float float_val = 45f; + + public static int test_0_sse2_opt_and_simd_intrinsic_proper_regalloc () { + Vector4f v = new Vector4f (1, 2, 3, 4); + float f = float_val; + int x = (int)f; + if (v.X != 1f) + return 1; + if (x != 45f) + return 2; + return 0; + } + + public static int test_0_sse41_vector8s_min () { + Vector8s v = new Vector8s(2); + Vector8s v2 = new Vector8s(1); + v = v.Min(v2); + if (v.V0 != 1 || v.V1 != 1 || v.V2 != 1 || v.V3 != 1 || v.V4 != 1 || v.V5 != 1 || v.V6 != 1 || v.V7 != 1) + return 1; + return 0; + } + + public static int test_0_simd_const_indexer_simple () { + Vector4f v = new Vector4f (1, 2, 3, 4); + + if (v[0] != 1) + return 1; + if (v[1] != 2) + return 2; + if (v[2] != 3) + return 3; + if (v[3] != 4) + return 4; + return 0; + } + + public static int test_0_simd_var_indexer_simple () { + Vector4f v = new Vector4f (1, 2, 3, 4); + + int index = 0; + + if (v[index++] != 1) + return 1; + if (v[index++] != 2) + return 2; + if (v[index++] != 3) + return 3; + if (v[index] != 4) + return 4; + return 0; + } + + public static int test_0_simd_const_indexer_double () { + Vector2d v = new Vector2d (1, 2); + + if (v[0] != 1) + return 1; + if (v[1] != 2) + return 2; + return 0; + } + + public static int test_0_simd_var_indexer_double () { + Vector2d v = new Vector2d (1, 2); + + int index = 0; + + if (v[index++] != 1) + return 1; + if (v[index] != 2) + return 2; + return 0; + } + + + public static int test_0_scala_vector4f_mul () { + Vector4f a = new Vector4f (1, 2, 3, 4); + Vector4f b = 2 * a; + Vector4f c = a * 3; + + if (b.X != 2f || b.Y != 4f || b.Z != 6f || b.W != 8f ) + return 1; + if (c.X != 3f || c.Y != 6f || c.Z != 9f || c.W != 12f ) + return 1; + + return 0; + } + + static void CallMethodThatClobbersRegs () { + Vector4f a = new Vector4f (9,9,9,9); + Vector4f b = new Vector4f (9,9,9,9); + a = a + b; + } + + public static int test_0_call_spills_regs_correctly () { + Vector4f a = new Vector4f (1,2,3,4); + Vector4f b = new Vector4f (5,6,7,8); + + CallMethodThatClobbersRegs (); + + bool b0 = a.X == 1f; + bool b1 = b.X == 5f; + if (!b0 || !b1) + return 1; + return 0; + } + + public static int test_0_shuffle_with_two_args_pd () { + Vector2d a = new Vector2d (1,2); + Vector2d b = new Vector2d (5,6); + + Vector2d c = a.Shuffle (b, 0x2); + if (c.X != 1) + return 1; + if (c.Y != 6) + return 2; + return 0; + } + + public static int test_0_shuffle_with_two_args_ps () { + Vector4f a = new Vector4f (1, 2, 3, 4); + Vector4f b = new Vector4f (5, 6, 7, 8); + + Vector4f c = a.Shuffle (b, ShuffleSel.ExpandY); + if (c.X != 2) + return 1; + if (c.Y != 2) + return 2; + if (c.Z != 6) + return 3; + if (c.W != 6) + return 4; + return 0; + } + + public static int test_0_i_to_d () { + var a = new Vector4i (1, 2, 3, 4); + var b = a.ConvertToDouble (); + if (b.X != 1) + return 1; + if (b.Y != 2) + return 2; + return 0; + } + + public static int test_0_i_to_f () { + var a = new Vector4i (1, 2, 3, 4); + var b = a.ConvertToFloat (); + if (b.X != 1) + return 1; + if (b.Y != 2) + return 2; + if (b.Z != 3) + return 3; + if (b.W != 4) + return 4; + return 0; + } + + public static int test_0_d_to_i () { + var a = new Vector2d (1.4, 2.6); + var b = a.ConvertToInt (); + if (b.X != 1) + return 1; + if (b.Y != 3) + return 2; + if (b.Z != 0) + return 3; + if (b.W != 0) + return 4; + return 0; + } + + public static int test_0_d_to_f () { + var a = new Vector2d (1, 2); + var b = a.ConvertToFloat (); + if (b.X != 1) + return 1; + if (b.Y != 2) + return 2; + if (b.Z != 0) + return 3; + if (b.W != 0) + return 4; + return 0; + } + + public static int test_0_f_to_i () { + var a = new Vector4f (1.1f, 2.2f, 3.5f, 4.6f); + var b = a.ConvertToInt (); + if (b.X != 1) + return 1; + if (b.Y != 2) + return 2; + if (b.Z != 4) + return 3; + if (b.W != 5) + return 4; + return 0; + } + + public static int test_0_f_to_d () { + var a = new Vector4f (1,2,3,4); + var b = a.ConvertToDouble (); + if (b.X != 1) + return 1; + if (b.Y != 2) + return 2; + return 0; + } + + public static int test_0_d_to_i_trunc () { + var a = new Vector2d (1.4, 2.6); + var b = a.ConvertToIntTruncated (); + if (b.X != 1) + return 1; + if (b.Y != 2) + return 2; + if (b.Z != 0) + return 3; + if (b.W != 0) + return 4; + return 0; + } + + public static int test_0_f_to_i_trunc () { + var a = new Vector4f (1.1f, 2.2f, 3.5f, 4.6f); + var b = a.ConvertToIntTruncated (); + if (b.X != 1) + return 1; + if (b.Y != 2) + return 2; + if (b.Z != 3) + return 3; + if (b.W != 4) + return 4; + return 0; + } + + class BoxedVector2d + { + public Vector2d v; + } + + public static int test_0_vector2d_set_x () { + var bv = new BoxedVector2d (); + var xy = new Vector2d (); + xy.X = bv.v.X; + + if (xy.X != 0) + return 1; + if (xy.Y != 0) + return 2; + return 0; + } + + public static int Main (String[] args) { + return TestDriver.RunTests (typeof (SimdTests), args); + } +} + diff --git a/unity-2019.4.24f1-mbe/mono/mini/basic-vectors.cs b/unity-2019.4.24f1-mbe/mono/mini/basic-vectors.cs new file mode 100644 index 000000000..a2cf10145 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/basic-vectors.cs @@ -0,0 +1,1423 @@ +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +/* + * Tests for the SIMD intrinsics in the System.Numerics.Vectors assembly. + */ +public class VectorTests { + +#if !MOBILE + public static int Main (string[] args) { + return TestDriver.RunTests (typeof (VectorTests), args); + } +#endif + + // + // Vector2 tests + // + + public static int test_0_vector2_ctor_1 () { + var v = new Vector2 (1.0f); + + if (v.X != 1.0f) + return 1; + if (v.Y != 1.0f) + return 2; + return 0; + } + + public static int test_0_vector2_ctor_2 () { + var v = new Vector2 (1.0f, 2.0f); + + if (v.X != 1.0f) + return 1; + if (v.Y != 2.0f) + return 2; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static bool vector2_equals (Vector2 v1, Vector2 v2) { + // cmpeqps+pmovmskb + return v1.Equals (v2); + } + + public static int test_0_vector2_equals () { + var v1 = new Vector2 (1.0f, 2.0f); + var v2 = new Vector2 (2.0f, 2.0f); + + if (vector2_equals (v1, v2)) + return 1; + if (!vector2_equals (v1, v1)) + return 2; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static float vector2_dot (Vector2 v1, Vector2 v2) { + return Vector2.Dot (v1, v2); + } + + public static int test_0_vector2_dot () { + var v1 = new Vector2 (1.0f, 1.0f); + var v2 = new Vector2 (2.0f, 2.0f); + + float f = vector2_dot (v1, v2); + if (f != 4.0f) + return 1; + f = vector2_dot (v1, v1); + if (f != 2.0f) + return 2; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector2 vector2_min (Vector2 v1, Vector2 v2) { + return Vector2.Min (v1, v2); + } + + public static int test_0_vector2_min () { + var v1 = new Vector2 (1.0f, 1.0f); + var v2 = new Vector2 (2.0f, 2.0f); + + var v3 = vector2_min (v1, v2); + if (v3.X != 1.0f || v3.Y != 1.0f) + return 1; + v3 = vector2_min (v2, v2); + if (v3.X != 2.0f || v3.Y != 2.0f) + return 2; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector2 vector2_max (Vector2 v1, Vector2 v2) { + return Vector2.Max (v1, v2); + } + + public static int test_0_vector2_max () { + var v1 = new Vector2 (1.0f, 1.0f); + var v2 = new Vector2 (2.0f, 2.0f); + + var v3 = vector2_max (v1, v2); + if (v3.X != 2.0f || v3.Y != 2.0f) + return 1; + v3 = vector2_min (v1, v1); + if (v3.X != 1.0f || v3.Y != 1.0f) + return 2; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector2 vector2_abs (Vector2 v1) { + return Vector2.Abs (v1); + } + + public static int test_0_vector2_abs () { + var v1 = new Vector2 (-1.0f, -2.0f); + var v2 = new Vector2 (1.0f, 2.0f); + + var v3 = vector2_abs (v1); + if (v3.X != 1.0f || v3.Y != 2.0f) + return 1; + v3 = vector2_abs (v2); + if (v3.X != 1.0f || v3.Y != 2.0f) + return 2; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector2 vector2_sqrt (Vector2 v1) { + return Vector2.SquareRoot (v1); + } + + public static int test_0_vector2_sqrt () { + var v1 = new Vector2 (1.0f, 0.0f); + + var v3 = vector2_sqrt (v1); + if (v3.X != 1.0f || v3.Y != 0.0f) + return 1; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector2 vector2_add (Vector2 v1, Vector2 v2) { + return v1 + v2; + } + + public static int test_0_vector2_add () { + var v1 = new Vector2 (1.0f, 2.0f); + var v2 = new Vector2 (3.0f, 4.0f); + + var v3 = vector2_add (v1, v2); + if (v3.X != 4.0f || v3.Y != 6.0f) + return 1; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector2 vector2_sub (Vector2 v1, Vector2 v2) { + return v1 - v2; + } + + public static int test_0_vector2_sub () { + var v1 = new Vector2 (1.0f, 2.0f); + var v2 = new Vector2 (3.0f, 5.0f); + + var v3 = vector2_sub (v2, v1); + if (v3.X != 2.0f || v3.Y != 3.0f) + return 1; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector2 vector2_mul (Vector2 v1, Vector2 v2) { + return v1 * v2; + } + + public static int test_0_vector2_mul () { + var v1 = new Vector2 (1.0f, 2.0f); + var v2 = new Vector2 (3.0f, 5.0f); + + var v3 = vector2_mul (v2, v1); + if (v3.X != 3.0f || v3.Y != 10.0f) + return 1; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector2 vector2_mul_left (float v1, Vector2 v2) { + return v1 * v2; + } + + public static int test_0_vector2_mul_left () { + var v1 = new Vector2 (3.0f, 5.0f); + + var v3 = vector2_mul_left (2.0f, v1); + if (v3.X != 6.0f || v3.Y != 10.0f) + return 1; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector2 vector2_mul_right (Vector2 v1, float v2) { + return v1 * v2; + } + + public static int test_0_vector2_mul_right () { + var v1 = new Vector2 (3.0f, 5.0f); + + var v3 = vector2_mul_right (v1, 2.0f); + if (v3.X != 6.0f || v3.Y != 10.0f) + return 1; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector2 vector2_div (Vector2 v1, Vector2 v2) { + return v1 / v2; + } + + public static int test_0_vector2_div () { + var v1 = new Vector2 (9.0f, 10.0f); + var v2 = new Vector2 (3.0f, 5.0f); + + var v3 = vector2_div (v1, v2); + if (v3.X != 3.0f || v3.Y != 2.0f) + return 1; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector2 vector2_div_right (Vector2 v1, float v2) { + return v1 / v2; + } + + public static int test_0_vector2_div_right () { + var v1 = new Vector2 (9.0f, 15.0f); + + var v3 = vector2_div_right (v1, 3.0f); + if (v3.X != 3.0f || v3.Y != 5.0f) + return 1; + return 0; + } + + // + // Vector4 tests + // + + public static int test_0_vector4_ctor_1 () { + var v = new Vector4 (1.0f); + + if (v.X != 1.0f) + return 1; + if (v.Y != 1.0f) + return 2; + if (v.Z != 1.0f) + return 3; + if (v.W != 1.0f) + return 4; + return 0; + } + + public static int test_0_vector4_ctor_2 () { + var v = new Vector4 (1.0f, 2.0f, 3.0f, 4.0f); + + if (v.X != 1.0f) + return 1; + if (v.Y != 2.0f) + return 2; + if (v.Z != 3.0f) + return 3; + if (v.W != 4.0f) + return 4; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static bool vector4_equals (Vector4 v1, Vector4 v2) { + // cmpeqps+pmovmskb + return v1.Equals (v2); + } + + public static int test_0_vector4_equals () { + var v1 = new Vector4 (1.0f, 2.0f, 3.0f, 4.0f); + var v2 = new Vector4 (2.0f, 2.0f, 2.0f, 2.0f); + + if (vector4_equals (v1, v2)) + return 1; + if (!vector4_equals (v1, v1)) + return 2; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static float vector4_dot (Vector4 v1, Vector4 v2) { + return Vector4.Dot (v1, v2); + } + + public static int test_0_vector4_dot () { + var v1 = new Vector4 (1.0f, 1.0f, 1.0f, 1.0f); + var v2 = new Vector4 (2.0f, 2.0f, 2.0f, 2.0f); + + float f = vector4_dot (v1, v2); + if (f != 8.0f) + return 1; + f = vector4_dot (v1, v1); + if (f != 4.0f) + return 2; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector4 vector4_min (Vector4 v1, Vector4 v2) { + return Vector4.Min (v1, v2); + } + + public static int test_0_vector4_min () { + var v1 = new Vector4 (1.0f, 2.0f, 3.0f, 4.0f); + var v2 = new Vector4 (5.0f, 6.0f, 7.0f, 8.0f); + + var v3 = vector4_min (v1, v2); + if (v3.X != 1.0f || v3.Y != 2.0f || v3.Z != 3.0f || v3.W != 4.0f) + return 1; + v3 = vector4_min (v2, v2); + if (v3.X != 5.0f || v3.Y != 6.0f || v3.Z != 7.0f || v3.W != 8.0f) + return 2; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector4 vector4_max (Vector4 v1, Vector4 v2) { + return Vector4.Max (v1, v2); + } + + public static int test_0_vector4_max () { + var v1 = new Vector4 (1.0f, 2.0f, 3.0f, 4.0f); + var v2 = new Vector4 (5.0f, 6.0f, 7.0f, 8.0f); + + var v3 = vector4_max (v1, v2); + if (v3.X != 5.0f || v3.Y != 6.0f || v3.Z != 7.0f || v3.W != 8.0f) + return 1; + v3 = vector4_max (v1, v1); + if (v3.X != 1.0f || v3.Y != 2.0f || v3.Z != 3.0f || v3.W != 4.0f) + return 2; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector4 vector4_abs (Vector4 v1) { + return Vector4.Abs (v1); + } + + public static int test_0_vector4_abs () { + var v1 = new Vector4 (-1.0f, -2.0f, -3.0f, -4.0f); + var v2 = new Vector4 (1.0f, 2.0f, 3.0f, 4.0f); + + var v3 = vector4_abs (v1); + if (v3.X != 1.0f || v3.Y != 2.0f || v3.Z != 3.0f || v3.W != 4.0f) + return 1; + v3 = vector4_abs (v2); + if (v3.X != 1.0f || v3.Y != 2.0f || v3.Z != 3.0f || v3.W != 4.0f) + return 2; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector4 vector4_sqrt (Vector4 v1) { + return Vector4.SquareRoot (v1); + } + + public static int test_0_vector4_sqrt () { + var v1 = new Vector4 (1.0f, 0.0f, 1.0f, 0.0f); + + var v3 = vector4_sqrt (v1); + if (v3.X != 1.0f || v3.Y != 0.0f || v3.Z != 1.0f || v3.W != 0.0f) + return 1; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector4 vector4_add (Vector4 v1, Vector4 v2) { + return v1 + v2; + } + + public static int test_0_vector4_add () { + var v1 = new Vector4 (1.0f, 2.0f, 3.0f, 4.0f); + var v2 = new Vector4 (5.0f, 6.0f, 7.0f, 8.0f); + + var v3 = vector4_add (v1, v2); + if (v3.X != 6.0f || v3.Y != 8.0f || v3.Z != 10.0f || v3.W != 12.0f) + return 1; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector4 vector4_sub (Vector4 v1, Vector4 v2) { + return v1 - v2; + } + + public static int test_0_vector4_sub () { + var v1 = new Vector4 (1.0f, 2.0f, 3.0f, 4.0f); + var v2 = new Vector4 (3.0f, 5.0f, 7.0f, 9.0f); + + var v3 = vector4_sub (v2, v1); + if (v3.X != 2.0f || v3.Y != 3.0f || v3.Z != 4.0f || v3.W != 5.0f) + return 1; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector4 vector4_mul (Vector4 v1, Vector4 v2) { + return v1 * v2; + } + + public static int test_0_vector4_mul () { + var v1 = new Vector4 (1.0f, 2.0f, 3.0f, 4.0f); + var v2 = new Vector4 (3.0f, 5.0f, 6.0f, 7.0f); + + var v3 = vector4_mul (v2, v1); + if (v3.X != 3.0f || v3.Y != 10.0f || v3.Z != 18.0f || v3.W != 28.0f) + return 1; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector4 vector4_mul_left (float v1, Vector4 v2) { + return v1 * v2; + } + + public static int test_0_vector4_mul_left () { + var v1 = new Vector4 (3.0f, 5.0f, 6.0f, 7.0f); + + var v3 = vector4_mul_left (2.0f, v1); + if (v3.X != 6.0f || v3.Y != 10.0f || v3.Z != 12.0f || v3.W != 14.0f) + return 1; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector4 vector4_mul_right (Vector4 v1, float v2) { + return v1 * v2; + } + + public static int test_0_vector4_mul_right () { + var v1 = new Vector4 (3.0f, 5.0f, 6.0f, 7.0f); + + var v3 = vector4_mul_right (v1, 2.0f); + if (v3.X != 6.0f || v3.Y != 10.0f || v3.Z != 12.0f || v3.W != 14.0f) + return 1; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector4 vector4_div (Vector4 v1, Vector4 v2) { + return v1 / v2; + } + + public static int test_0_vector4_div () { + var v1 = new Vector4 (9.0f, 10.0f, 12.0f, 21.0f); + var v2 = new Vector4 (3.0f, 5.0f, 6.0f, 7.0f); + + var v3 = vector4_div (v1, v2); + if (v3.X != 3.0f || v3.Y != 2.0f || v3.Z != 2.0f || v3.W != 3.0f) + return 1; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector4 vector4_div_right (Vector4 v1, float v2) { + return v1 / v2; + } + + public static int test_0_vector4_div_right () { + var v1 = new Vector4 (9.0f, 15.0f, 21.0f, 30.0f); + + var v3 = vector4_div_right (v1, 3.0f); + if (v3.X != 3.0f || v3.Y != 5.0f || v3.Z != 7.0f || v3.W != 10.0f) + return 1; + return 0; + } + + public static int test_0_vector4_length () { + var v = new Vector4 (2.0f, 2.0f, 2.0f, 2.0f); + return v.Length () == 4.0f ? 0 : 1; + } + + // + // Vector3 tests + // + + public static int test_0_vector3_ctor_1 () { + var v = new Vector3 (1.0f); + + if (v.X != 1.0f) + return 1; + if (v.Y != 1.0f) + return 2; + if (v.Z != 1.0f) + return 3; + return 0; + } + + public static int test_0_vector3_ctor_2 () { + var v = new Vector3 (1.0f, 2.0f, 3.0f); + + if (v.X != 1.0f) + return 1; + if (v.Y != 2.0f) + return 2; + if (v.Z != 3.0f) + return 3; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static bool vector3_equals (Vector3 v1, Vector3 v2) { + // cmpeqps+pmovmskb + return v1.Equals (v2); + } + + public static int test_0_vector3_equals () { + var v1 = new Vector3 (1.0f, 2.0f, 3.0f); + var v2 = new Vector3 (2.0f, 2.0f, 2.0f); + + if (vector3_equals (v1, v2)) + return 1; + if (!vector3_equals (v1, v1)) + return 2; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static float vector3_dot (Vector3 v1, Vector3 v2) { + return Vector3.Dot (v1, v2); + } + + public static int test_0_vector3_dot () { + var v1 = new Vector3 (1.0f, 1.0f, 1.0f); + var v2 = new Vector3 (2.0f, 2.0f, 2.0f); + + float f = vector3_dot (v1, v2); + if (f != 6.0f) + return 1; + f = vector3_dot (v1, v1); + if (f != 3.0f) + return 2; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector3 vector3_min (Vector3 v1, Vector3 v2) { + return Vector3.Min (v1, v2); + } + + public static int test_0_vector3_min () { + var v1 = new Vector3 (1.0f, 2.0f, 3.0f); + var v2 = new Vector3 (5.0f, 6.0f, 7.0f); + + var v3 = vector3_min (v1, v2); + if (v3.X != 1.0f || v3.Y != 2.0f || v3.Z != 3.0f) + return 1; + v3 = vector3_min (v2, v2); + if (v3.X != 5.0f || v3.Y != 6.0f || v3.Z != 7.0f) + return 2; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector3 vector3_max (Vector3 v1, Vector3 v2) { + return Vector3.Max (v1, v2); + } + + public static int test_0_vector3_max () { + var v1 = new Vector3 (1.0f, 2.0f, 3.0f); + var v2 = new Vector3 (5.0f, 6.0f, 7.0f); + + var v3 = vector3_max (v1, v2); + if (v3.X != 5.0f || v3.Y != 6.0f || v3.Z != 7.0f) + return 1; + v3 = vector3_max (v1, v1); + if (v3.X != 1.0f || v3.Y != 2.0f || v3.Z != 3.0f) + return 2; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector3 vector3_abs (Vector3 v1) { + return Vector3.Abs (v1); + } + + public static int test_0_vector3_abs () { + var v1 = new Vector3 (-1.0f, -2.0f, -3.0f); + var v2 = new Vector3 (1.0f, 2.0f, 3.0f); + + var v3 = vector3_abs (v1); + if (v3.X != 1.0f || v3.Y != 2.0f || v3.Z != 3.0f) + return 1; + v3 = vector3_abs (v2); + if (v3.X != 1.0f || v3.Y != 2.0f || v3.Z != 3.0f) + return 2; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector3 vector3_sqrt (Vector3 v1) { + return Vector3.SquareRoot (v1); + } + + public static int test_0_vector3_sqrt () { + var v1 = new Vector3 (1.0f, 0.0f, 1.0f); + + var v3 = vector3_sqrt (v1); + if (v3.X != 1.0f || v3.Y != 0.0f || v3.Z != 1.0f) + return 1; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector3 vector3_add (Vector3 v1, Vector3 v2) { + return v1 + v2; + } + + public static int test_0_vector3_add () { + var v1 = new Vector3 (1.0f, 2.0f, 3.0f); + var v2 = new Vector3 (5.0f, 6.0f, 7.0f); + + var v3 = vector3_add (v1, v2); + if (v3.X != 6.0f || v3.Y != 8.0f || v3.Z != 10.0f) + return 1; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector3 vector3_sub (Vector3 v1, Vector3 v2) { + return v1 - v2; + } + + public static int test_0_vector3_sub () { + var v1 = new Vector3 (1.0f, 2.0f, 3.0f); + var v2 = new Vector3 (3.0f, 5.0f, 7.0f); + + var v3 = vector3_sub (v2, v1); + if (v3.X != 2.0f || v3.Y != 3.0f || v3.Z != 4.0f) + return 1; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector3 vector3_mul (Vector3 v1, Vector3 v2) { + return v1 * v2; + } + + public static int test_0_vector3_mul () { + var v1 = new Vector3 (1.0f, 2.0f, 3.0f); + var v2 = new Vector3 (3.0f, 5.0f, 6.0f); + + var v3 = vector3_mul (v2, v1); + if (v3.X != 3.0f || v3.Y != 10.0f || v3.Z != 18.0f) + return 1; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector3 vector3_mul_left (float v1, Vector3 v2) { + return v1 * v2; + } + + public static int test_0_vector3_mul_left () { + var v1 = new Vector3 (3.0f, 5.0f, 6.0f); + + var v3 = vector3_mul_left (2.0f, v1); + if (v3.X != 6.0f || v3.Y != 10.0f || v3.Z != 12.0f) + return 1; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector3 vector3_mul_right (Vector3 v1, float v2) { + return v1 * v2; + } + + public static int test_0_vector3_mul_right () { + var v1 = new Vector3 (3.0f, 5.0f, 6.0f); + + var v3 = vector3_mul_right (v1, 2.0f); + if (v3.X != 6.0f || v3.Y != 10.0f || v3.Z != 12.0f) + return 1; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector3 vector3_div (Vector3 v1, Vector3 v2) { + return v1 / v2; + } + + public static int test_0_vector3_div () { + var v1 = new Vector3 (9.0f, 10.0f, 12.0f); + var v2 = new Vector3 (3.0f, 5.0f, 6.0f); + + var v3 = vector3_div (v1, v2); + if (v3.X != 3.0f || v3.Y != 2.0f || v3.Z != 2.0f) + return 1; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector3 vector3_div_right (Vector3 v1, float v2) { + return v1 / v2; + } + + public static int test_0_vector3_div_right () { + var v1 = new Vector3 (9.0f, 15.0f, 21.0f); + + var v3 = vector3_div_right (v1, 3.0f); + if (v3.X != 3.0f || v3.Y != 5.0f || v3.Z != 7.0f) + return 1; + return 0; + } + + // + // Vector + // + + public static int test_0_vector_t_count () { + // This assumes a 16 byte simd register size + if (Vector.Count != 16) + return 1; + if (Vector.Count != 8) + return 2; + if (Vector.Count != 4) + return 3; + if (Vector.Count != 2) + return 4; + return 0; + } + + public static int test_0_vector_t_zero () { + var v = Vector.Zero; + for (int i = 0; i < Vector.Count; ++i) + if (v [i] != 0) + return 1; + var v2 = Vector.Zero; + for (int i = 0; i < Vector.Count; ++i) + if (v2 [i] != 0.0) + return 2; + return 0; + } + + public static int test_0_vector_t_i1_accessor () { + var elems = new byte [] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; + var v = new Vector (elems, 0); + for (int i = 0; i < Vector.Count; ++i) + if (v [i] != i + 1) + return 1; + if (v [0] != 1) + return 2; + if (v [1] != 2) + return 2; + if (v [15] != 16) + return 2; + try { + int r = v [-1]; + return 3; + } catch (IndexOutOfRangeException) { + } + try { + int r = v [16]; + return 4; + } catch (IndexOutOfRangeException) { + } + return 0; + } + + public static int test_0_vector_t_i4_accessor () { + var elems = new int [] { 1, 2, 3, 4 }; + var v = new Vector (elems, 0); + for (int i = 0; i < Vector.Count; ++i) + if (v [i] != i + 1) + return 1; + if (v [0] != 1) + return 2; + if (v [1] != 2) + return 2; + if (v [3] != 4) + return 2; + try { + int r = v [-1]; + return 3; + } catch (IndexOutOfRangeException) { + } + try { + int r = v [Vector.Count]; + return 4; + } catch (IndexOutOfRangeException) { + } + return 0; + } + + public static int test_0_vector_t_i8_accessor () { + var elems = new long [] { 1, 2 }; + var v = new Vector (elems, 0); + for (int i = 0; i < Vector.Count; ++i) + if (v [i] != i + 1) + return 1; + if (v [0] != 1) + return 2; + if (v [1] != 2) + return 2; + try { + var r = v [-1]; + return 3; + } catch (IndexOutOfRangeException) { + } + try { + var r = v [Vector.Count]; + return 4; + } catch (IndexOutOfRangeException) { + } + return 0; + } + + public static int test_0_vector_t_r8_accessor () { + var elems = new double [] { 1.0, 2.0 }; + var v = new Vector (elems, 0); + for (int i = 0; i < Vector.Count; ++i) + if (v [i] != (double)i + 1.0) + return 1; + if (v [0] != 1.0) + return 2; + if (v [1] != 2.0) + return 2; + try { + var r = v [-1]; + return 3; + } catch (IndexOutOfRangeException) { + } + try { + var r = v [Vector.Count]; + return 4; + } catch (IndexOutOfRangeException) { + } + return 0; + } + + public static int test_0_vector_t_i1_ctor_3 () { + var v = new Vector (5); + for (int i = 0; i < 16; ++i) + if (v [i] != 5) + return 1; + return 0; + } + + public static int test_0_vector_t_i2_ctor_3 () { + var v = new Vector (5); + for (int i = 0; i < 8; ++i) + if (v [i] != 5) + return 1; + return 0; + } + + public static int test_0_vector_t_i4_ctor_3 () { + var v = new Vector (0xffffeee); + for (int i = 0; i < 4; ++i) + if (v [i] != 0xffffeee) + return 1; + return 0; + } + + public static int test_0_vector_t_i8_ctor_3 () { + var v = new Vector (0xffffeeeeabcdefL); + for (int i = 0; i < 2; ++i) + if (v [i] != 0xffffeeeeabcdefL) + return 1; + return 0; + } + + public static int test_0_vector_t_r4_ctor_3 () { + var v = new Vector (0.5f); + for (int i = 0; i < 4; ++i) { + if (v [i] != 0.5f) + return 1; + } + return 0; + } + + public static int test_0_vector_t_r8_ctor_3 () { + var v = new Vector (0.5f); + for (int i = 0; i < 2; ++i) { + if (v [i] != 0.5f) + return 1; + } + return 0; + } + + public static int test_0_vector_t_i1_ctor () { + var elems = new byte [] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }; + var v = new Vector (elems, 16); + for (int i = 0; i < 16; ++i) + if (v [i] != i) + return 1; + try { + var v2 = new Vector (elems, 16 + 4); + return 2; + } catch (IndexOutOfRangeException) { + } + return 0; + } + + public static int test_0_vector_t_i1_ctor_2 () { + var elems = new byte [] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; + var v = new Vector (elems); + for (int i = 0; i < 16; ++i) + if (v [i] != i + 1) + return 1; + try { + var elems2 = new byte [] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; + var v2 = new Vector (elems2); + return 2; + } catch (IndexOutOfRangeException) { + } + return 0; + } + + public static int test_0_vector_t_r4_equal () { + var elems1 = new float [4] { 1.0f, 1.0f, 1.0f, 1.0f }; + var v1 = new Vector (elems1); + var elems2 = new float [4] { 1.0f, 2.0f, 1.0f, 2.0f }; + var v2 = new Vector (elems2); + Vector v = Vector.Equals (v1, v2); + if (v [0] != -1 || v [1] != 0 || v [2] != -1 || v [3] != 0) + return 1; + return 0; + } + + public static int test_0_vector_t_r8_equal () { + var elems1 = new double [] { 1.0f, 1.0f }; + var v1 = new Vector (elems1); + var elems2 = new double [] { 1.0f, 2.0f }; + var v2 = new Vector (elems2); + Vector v = Vector.Equals (v1, v2); + if (v [0] != -1 || v [1] != 0) + return 1; + return 0; + } + + public static int test_0_vector_t_i8_equal () { + var elems1 = new long [] { 1, 1 }; + var v1 = new Vector (elems1); + var elems2 = new long [] { 1, 2 }; + var v2 = new Vector (elems2); + Vector v = Vector.Equals (v1, v2); + if (v [0] != -1 || v [1] != 0) + return 1; + return 0; + } + + public static int test_0_vector_t_i4_equal () { + var elems1 = new int [] { 1, 1, 1, 1 }; + var v1 = new Vector (elems1); + var elems2 = new int [] { 1, 2, 1, 2 }; + var v2 = new Vector (elems2); + Vector v = Vector.Equals (v1, v2); + if (v [0] != -1 || v [1] != 0 || v [2] != -1 || v[3] != 0) + return 1; + return 0; + } + + /* + public static int test_0_vector_t_u4_equal () { + var elems1 = new uint [] { 1, 1, 1, 1 }; + var v1 = new Vector (elems1); + var elems2 = new uint [] { 1, 2, 1, 2 }; + var v2 = new Vector (elems2); + Vector v = Vector.Equals (v1, v2); + if (v [0] != 0xffffffff || v [1] != 0 || v [2] != 0xffffffff || v[3] != 0) + return 1; + return 0; + } + */ + + public static int test_0_vector_t_i2_equal () { + var elems1 = new short [] { 1, 1, 1, 1, 1, 1, 1, 1 }; + var v1 = new Vector (elems1); + var elems2 = new short [] { 1, 2, 1, 2, 1, 2, 1, 2 }; + var v2 = new Vector (elems2); + Vector v = Vector.Equals (v1, v2); + for (int i = 0; i < Vector.Count; ++i) { + if (i % 2 == 0 && v [i] != -1) + return 1; + if (i % 2 == 1 && v [i] != 0) + return 1; + } + return 0; + } + + public static int test_0_vector_t_u2_equal () { + var elems1 = new ushort [] { 1, 1, 1, 1, 1, 1, 1, 1 }; + var v1 = new Vector (elems1); + var elems2 = new ushort [] { 1, 2, 1, 2, 1, 2, 1, 2 }; + var v2 = new Vector (elems2); + Vector v = Vector.Equals (v1, v2); + for (int i = 0; i < Vector.Count; ++i) { + if (i % 2 == 0 && v [i] != 0xffff) + return 1; + if (i % 2 == 1 && v [i] != 0) + return 1; + } + return 0; + } + + public static int test_0_vector_t_i1_equal () { + var elems1 = new sbyte [] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; + var v1 = new Vector (elems1); + var elems2 = new sbyte [] { 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2 }; + var v2 = new Vector (elems2); + Vector v = Vector.Equals (v1, v2); + for (int i = 0; i < Vector.Count; ++i) { + if (i % 2 == 0 && v [i] != -1) + return 1; + if (i % 2 == 1 && v [i] != 0) + return 1; + } + return 0; + } + + public static int test_0_vector_t_u1_equal () { + var elems1 = new byte [] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; + var v1 = new Vector (elems1); + var elems2 = new byte [] { 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2 }; + var v2 = new Vector (elems2); + Vector v = Vector.Equals (v1, v2); + for (int i = 0; i < Vector.Count; ++i) { + if (i % 2 == 0 && v [i] != 0xff) + return 1; + if (i % 2 == 1 && v [i] != 0) + return 1; + } + return 0; + } + + /* op_Explicit () -> Vector */ + + public static int test_0_vector_t_cast_vector_int32 () { + var v1 = new Vector (new long [] { 0x123456789abcdef0L, 0x23456789abcdef01L }); + var v = (Vector)v1; + if (BitConverter.IsLittleEndian) { + if ((uint)v [0] != 0x9abcdef0 || (uint)v [1] != 0x12345678) + return 1; + if ((uint)v [2] != 0xabcdef01 || (uint)v [3] != 0x23456789) + return 2; + } else { + if ((uint)v [1] != 0x9abcdef0 || (uint)v [0] != 0x12345678) + return 1; + if ((uint)v [3] != 0xabcdef01 || (uint)v [2] != 0x23456789) + return 2; + } + return 0; + } + + /* Vector.GreaterThanOrEqual */ + + public static int test_0_vector_t_i1_ge () { + var elems1 = new sbyte [] { 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1 }; + var v1 = new Vector (elems1); + var elems2 = new sbyte [] { 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2 }; + var v2 = new Vector (elems2); + Vector v = Vector.GreaterThanOrEqual (v1, v2); + for (int i = 0; i < Vector.Count; ++i) { + if (i % 2 == 0 && v [i] != -1) + return 1; + if (i % 2 == 1 && v [i] != 0) + return 1; + } + return 0; + } + + public static int test_0_vector_t_i2_ge () { + var elems1 = new short [] { 1, 1, 0, 1, 1, 1, 0, 1 }; + var v1 = new Vector (elems1); + var elems2 = new short [] { 0, 2, 0, 2, 0, 2, 0, 2 }; + var v2 = new Vector (elems2); + Vector v = Vector.GreaterThanOrEqual (v1, v2); + for (int i = 0; i < Vector.Count; ++i) { + if (i % 2 == 0 && v [i] != -1) + return 1; + if (i % 2 == 1 && v [i] != 0) + return 1; + } + return 0; + } + + public static int test_0_vector_t_i4_ge () { + var elems1 = new int [] { 1, 1, 0, 1 }; + var v1 = new Vector (elems1); + var elems2 = new int [] { 0, 2, 0, 2 }; + var v2 = new Vector (elems2); + Vector v = Vector.GreaterThanOrEqual (v1, v2); + if (v [0] != -1 || v [1] != 0 || v [2] != -1 || v[3] != 0) + return 1; + return 0; + } + + public static int test_0_vector_t_i8_ge () { + var elems1 = new long [] { 1, 1 }; + var v1 = new Vector (elems1); + var elems2 = new long [] { 0, 1 }; + var v2 = new Vector (elems2); + Vector v = Vector.GreaterThanOrEqual (v1, v2); + if (v [0] != -1 || v [1] != -1) + return 1; + return 0; + } + + /* Vector.LessThan */ + + public static int test_0_vector_t_i1_lt () { + var elems1 = new sbyte [] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; + var v1 = new Vector (elems1); + var elems2 = new sbyte [] { 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2 }; + var v2 = new Vector (elems2); + Vector v = Vector.LessThan (v2, v1); + for (int i = 0; i < Vector.Count; ++i) { + if (i % 2 == 0 && v [i] != -1) + return 1; + if (i % 2 == 1 && v [i] != 0) + return 1; + } + return 0; + } + + /* Vector.GreaterThan */ + + public static int test_0_vector_t_i1_gt () { + var elems1 = new sbyte [] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; + var v1 = new Vector (elems1); + var elems2 = new sbyte [] { 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2 }; + var v2 = new Vector (elems2); + Vector v = Vector.GreaterThan (v1, v2); + for (int i = 0; i < Vector.Count; ++i) { + if (i % 2 == 0 && v [i] != -1) + return 1; + if (i % 2 == 1 && v [i] != 0) + return 1; + } + return 0; + } + + /* Vector.LessThanOrEqual */ + public static int test_0_vector_t_i1_le () { + var elems1 = new sbyte [] { 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2 }; + var v1 = new Vector (elems1); + var elems2 = new sbyte [] { 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1 }; + var v2 = new Vector (elems2); + Vector v = Vector.LessThanOrEqual (v1, v2); + for (int i = 0; i < Vector.Count; ++i) { + if (i % 2 == 0 && v [i] != -1) + return 1; + if (i % 2 == 1 && v [i] != 0) + return 1; + } + return 0; + } + + /* Vector.Abs */ + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector vector_t_abs (Vector v1) where T: struct { + return Vector.Abs (v1); + } + + public static int test_0_vector_t_u1_abs () { + var elems1 = new byte [] { 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2 }; + var v1 = new Vector (elems1); + + if (vector_t_abs (v1) != v1) + return 1; + return 0; + } + + public static int test_0_vector_t_u2_abs () { + var elems1 = new ushort [] { 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2 }; + var v1 = new Vector (elems1); + + if (vector_t_abs (v1) != v1) + return 1; + return 0; + } + + public static int test_0_vector_t_u4_abs () { + var elems1 = new uint [] { 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2 }; + var v1 = new Vector (elems1); + + if (vector_t_abs (v1) != v1) + return 1; + return 0; + } + + public static int test_0_vector_t_u8_abs () { + var elems1 = new ulong [] { 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2 }; + var v1 = new Vector (elems1); + + if (vector_t_abs (v1) != v1) + return 1; + return 0; + } + + public static int test_0_vector_t_i1_abs () { + var elems1 = new sbyte [] { 1, -2, 1, -2, 1, -2, 1, -2, 1, -2, 1, -2, 1, -2, 1, -2 }; + var v1 = new Vector (elems1); + var elems2 = new sbyte [] { 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2 }; + var v2 = new Vector (elems2); + + if (vector_t_abs (v1) != v2) + return 1; + return 0; + } + + // Vector.Add + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector vector_add (Vector v1, Vector v2) where T: struct { + return v1 + v2; + } + + public static int test_0_vector_byte_add () { + var v1 = new Vector (new byte[] { 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2 }); + var v2 = new Vector (new byte[] { 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4 }); + + var res = vector_add (v1, v1); + if (res != v2) + return 1; + return 0; + } + + public static int test_0_vector_sbyte_add () { + var v1 = new Vector (new sbyte[] { 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1 }); + var v2 = new Vector (new sbyte[] { 2, -2, 2, -2, 2, -2, 2, -2, 2, -2, 2, -2, 2, -2, 2, -2 }); + + var res = vector_add (v1, v1); + if (res != v2) + return 1; + return 0; + } + + public static int test_0_vector_ushort_add () { + var v1 = new Vector (new ushort[] { 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2 }); + var v2 = new Vector (new ushort[] { 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4 }); + + var res = vector_add (v1, v1); + if (res != v2) + return 1; + return 0; + } + + public static int test_0_vector_short_add () { + var v1 = new Vector (new short[] { 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1 }); + var v2 = new Vector (new short[] { 2, -2, 2, -2, 2, -2, 2, -2, 2, -2, 2, -2, 2, -2, 2, -2 }); + + var res = vector_add (v1, v1); + if (res != v2) + return 1; + return 0; + } + + public static int test_0_vector_uint_add () { + var v1 = new Vector (new uint[] { 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2 }); + var v2 = new Vector (new uint[] { 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4 }); + + var res = vector_add (v1, v1); + if (res != v2) + return 1; + return 0; + } + + public static int test_0_vector_int_add () { + var v1 = new Vector (new int[] { 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1 }); + var v2 = new Vector (new int[] { 2, -2, 2, -2, 2, -2, 2, -2, 2, -2, 2, 2, 2, -2, 2, -2 }); + + var res = vector_add (v1, v1); + if (res != v2) + return 1; + return 0; + } + + public static int test_0_vector_double_add () { + var v1 = new Vector (new double[] { 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0 }); + var v2 = new Vector (new double[] { 2.0, -2.0, 2.0, -2.0, 2.0, -2.0, 2.0, -2.0, 2.0, -2.0, 2.0, 2.0, 2.0, -2.0, 2.0, -2.0 }); + + var res = vector_add (v1, v1); + if (res != v2) + return 1; + return 0; + } + + public static int test_0_vector_float_add () { + var v1 = new Vector (new float[] { 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f }); + var v2 = new Vector (new float[] { 2.0f, -2.0f, 2.0f, -2.0f, 2.0f, -2.0f, 2.0f, -2.0f, 2.0f, -2.0f, 2.0f, 2.0f, 2.0f, -2.0f, 2.0f, -2.0f }); + + var res = vector_add (v1, v1); + if (res != v2) + return 1; + return 0; + } + + // Vector.op_Subtraction + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector vector_sub (Vector v1, Vector v2) where T: struct { + return v1 - v2; + } + + public static int test_0_vector_byte_sub () { + var v1 = new Vector (new byte[] { 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2 }); + + var res = vector_sub (v1, v1); + if (res != Vector.Zero) + return 1; + return 0; + } + + public static int test_0_vector_double_sub () { + var v1 = new Vector (new double[] { 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0 }); + + var res = vector_sub (v1, v1); + if (res != Vector.Zero) + return 1; + return 0; + } + + public static int test_0_vector_float_sub () { + var v1 = new Vector (new float[] { 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f }); + + var res = vector_sub (v1, v1); + if (res != Vector.Zero) + return 1; + return 0; + } + + // Vector.op_Multiply + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector vector_mul (Vector v1, Vector v2) where T: struct { + return v1 * v2; + } + + public static int test_0_vector_int_mul () { + var v1 = new Vector (new int[] { 1, 2, -1, 2, 1, 2, -1, 2, 1, -2, 1, 2, 1, 2, -1, 2 }); + var v2 = new Vector (new int[] { 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4 }); + + var res = vector_mul (v1, v1); + if (res != v2) + return 1; + return 0; + } + + public static int test_0_vector_double_mul () { + var v1 = new Vector (new double[] { 2.0, -1.0, 2.0, -1.0 }); + var v2 = new Vector (new double[] { 4.0, 1.0, 4.0, 1.0 }); + + var res = vector_mul (v1, v1); + if (res != v2) + return 1; + return 0; + } + + public static int test_0_vector_float_mul () { + var v1 = new Vector (new float[] { 2.0f, -1.0f, 2.0f, -1.0f }); + var v2 = new Vector (new float[] { 4.0f, 1.0f, 4.0f, 1.0f }); + + var res = vector_mul (v1, v1); + if (res != v2) + return 1; + return 0; + } + + // Vector.op_Division + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static Vector vector_div (Vector v1, Vector v2) where T: struct { + return v1 / v2; + } + + public static int test_0_vector_double_div () { + var v1 = new Vector (new double[] { 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0 }); + var v2 = new Vector (new double[] { 1.0, 1.0, 1.0, 1.0 }); + + var res = vector_div (v1, v1); + if (res != v2) + return 1; + return 0; + } + + public static int test_0_vector_float_div () { + var v1 = new Vector (new float[] { 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f }); + var v2 = new Vector (new float[] { 1.0f, 1.0f, 1.0f, 1.0f }); + + var res = vector_div (v1, v1); + if (res != v2) + return 1; + return 0; + } + + // Vector.CopyTo + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static void vector_copyto (Vector v1, T[] array, int index) where T: struct { + v1.CopyTo (array, index); + } + + public static int test_0_vector_byte_copyto () { + var v1 = new Vector (new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }); + + byte[] arr = new byte [256]; + vector_copyto (v1, arr, 1); + for (int i = 0; i < 16; ++i) + if (arr [i + 1] != (i + 1)) + return 1; + vector_copyto (v1, arr, 240); + try { + vector_copyto (v1, arr, 241); + return 1; + } catch (ArgumentException) { + } + return 0; + } +} diff --git a/unity-2019.4.24f1-mbe/mono/mini/basic.cs b/unity-2019.4.24f1-mbe/mono/mini/basic.cs new file mode 100644 index 000000000..984f1ac27 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/basic.cs @@ -0,0 +1,1470 @@ +using System; +using System.Reflection; + +/* + * Regression tests for the mono JIT. + * + * Each test needs to be of the form: + * + * static int test__ (); + * + * where is an integer (the value that needs to be returned by + * the method to make it pass. + * is a user-displayed name used to identify the test. + * + * The tests can be driven in two ways: + * *) running the program directly: Main() uses reflection to find and invoke + * the test methods (this is useful mostly to check that the tests are correct) + * *) with the --regression switch of the jit (this is the preferred way since + * all the tests will be run with optimizations on and off) + * + * The reflection logic could be moved to a .dll since we need at least another + * regression test file written in IL code to have better control on how + * the IL code looks. + */ + +#if __MOBILE__ +class BasicTests +#else +class Tests +#endif +{ + +#if !__MOBILE__ + public static int Main (string[] args) { + return TestDriver.RunTests (typeof (Tests), args); + } +#endif + + public static int test_0_return () { + return 0; + } + + public static int test_100000_return_large () { + return 100000; + } + + public static int test_1_load_bool () { + bool a = true; + return a? 1: 0; + } + + public static int test_0_load_bool_false () { + bool a = false; + return a? 1: 0; + } + + public static int test_200_load_byte () { + byte a = 200; + return a; + } + + public static int test_100_load_sbyte () { + sbyte a = 100; + return a; + } + + public static int test_200_load_short () { + short a = 200; + return a; + } + + public static int test_100_load_ushort () { + ushort a = 100; + return a; + } + + public static int test_3_add_simple () { + int a = 1; + int b = 2; + return a + b; + } + + public static int test_3_add_imm () { + int a = 1; + return a + 2; + } + + public static int test_13407573_add_largeimm () { + int a = 1; + return a + 13407572; + } + + public static int test_1_sub_simple () { + int a = 1; + int b = 2; + return b - a; + } + + public static int test_1_sub_simple_un () { + uint a = 1; + uint b = 2; + return (int)(b - a); + } + + public static int test_1_sub_imm () { + int b = 2; + return b - 1; + } + + public static int test_2_sub_large_imm () { + int b = 0xff0f0f; + return b - 0xff0f0d; + } + + public static int test_0_sub_inv_imm () { + int b = 2; + return 2 - b; + } + + public static int test_2_and () { + int b = 2; + int a = 3; + return b & a; + } + + public static int test_0_and_imm () { + int b = 2; + return b & 0x10; + } + + public static int test_0_and_large_imm () { + int b = 2; + return b & 0x10000000; + } + + public static int test_0_and_large_imm2 () { + int b = 2; + return b & 0x100000f0; + } + + public static int test_2_div () { + int b = 6; + int a = 3; + return b / a; + } + + public static int test_4_div_imm () { + int b = 12; + return b / 3; + } + + public static int test_4_divun_imm () { + uint b = 12; + return (int)(b / 3); + } + + public static int test_0_div_fold () { + int b = -1; + return b / 2; + } + + public static int test_2_div_fold4 () { + int b = -8; + return -(b / 4); + } + + public static int test_2_div_fold16 () { + int b = 32; + return b / 16; + } + + public static int test_719177_div_destreg () { + int year = 1970; + return ((365* (year-1)) + ((year-1)/4)); + } + + public static int test_1_remun_imm () { + uint b = 13; + return (int)(b % 3); + } + + public static int test_2_bigremun_imm () { + unchecked { + uint b = (uint)-2; + return (int)(b % 3); + } + } + + public static int test_2_rem () { + int b = 5; + int a = 3; + return b % a; + } + + public static int test_4_rem_imm () { + int b = 12; + return b % 8; + } + + public static int test_0_rem_imm_0 () { + int b = 12; + return b % 1; + } + + public static int test_0_rem_imm_0_neg () { + int b = -2; + return b % 1; + } + + public static int test_4_rem_big_imm () { + int b = 10004; + return b % 10000; + } + + public static int test_9_mul () { + int b = 3; + int a = 3; + return b * a; + } + + public static int test_15_mul_imm () { + int b = 3; + return b * 5; + } + + public static int test_24_mul () { + int a = 3; + int b = 8; + int res; + + res = a * b; + + return res; + } + + public static int test_24_mul_ovf () { + int a = 3; + int b = 8; + int res; + + checked { + res = a * b; + } + + return res; + } + + public static int test_24_mul_un () { + uint a = 3; + uint b = 8; + uint res; + + res = a * b; + + return (int)res; + } + + public static int test_24_mul_ovf_un () { + uint a = 3; + uint b = 8; + uint res; + + checked { + res = a * b; + } + + return (int)res; + } + + public static int test_0_add_ovf1 () { + int i, j, k; + + checked { + i = System.Int32.MinValue; + j = 0; + k = i + j; + } + + if (k != System.Int32.MinValue) + return 1; + return 0; + } + + public static int test_0_add_ovf2 () { + int i, j, k; + + checked { + i = System.Int32.MaxValue; + j = 0; + k = i + j; + } + + if (k != System.Int32.MaxValue) + return 2; + return 0; + } + + public static int test_0_add_ovf3 () { + int i, j, k; + + checked { + i = System.Int32.MinValue; + j = System.Int32.MaxValue; + k = i + j; + } + + if (k != -1) + return 3; + return 0; + } + + public static int test_0_add_ovf4 () { + int i, j, k; + + checked { + i = System.Int32.MaxValue; + j = System.Int32.MinValue; + k = i + j; + } + + if (k != -1) + return 4; + return 0; + } + + public static int test_0_add_ovf5 () { + int i, j, k; + + checked { + i = System.Int32.MinValue + 1234; + j = -1234; + k = i + j; + } + + if (k != System.Int32.MinValue) + return 5; + return 0; + } + + public static int test_0_add_ovf6 () { + int i, j, k; + + checked { + i = System.Int32.MaxValue - 1234; + j = 1234; + k = i + j; + } + + if (k != System.Int32.MaxValue) + return 6; + + return 0; + } + + public static int test_0_add_un_ovf () { + uint n = (uint)134217728 * 16; + uint number = checked (n + (uint)0); + + return number == n ? 0 : 1; + } + + public static int test_0_sub_ovf1 () { + int i, j, k; + + checked { + i = System.Int32.MinValue; + j = 0; + k = i - j; + } + + if (k != System.Int32.MinValue) + return 1; + + return 0; + } + + public static int test_0_sub_ovf2 () { + int i, j, k; + + checked { + i = System.Int32.MaxValue; + j = 0; + k = i - j; + } + + if (k != System.Int32.MaxValue) + return 2; + + return 0; + } + + public static int test_0_sub_ovf3 () { + int i, j, k; + + checked { + i = System.Int32.MinValue; + j = System.Int32.MinValue + 1234; + k = i - j; + } + + if (k != -1234) + return 3; + + return 0; + } + + public static int test_0_sub_ovf4 () { + int i, j, k; + + checked { + i = System.Int32.MaxValue; + j = 1234; + k = i - j; + } + + if (k != System.Int32.MaxValue - 1234) + return 4; + + return 0; + } + + public static int test_0_sub_ovf5 () { + int i, j, k; + + checked { + i = System.Int32.MaxValue - 1234; + j = -1234; + k = i - j; + } + + if (k != System.Int32.MaxValue) + return 5; + + return 0; + } + + public static int test_0_sub_ovf6 () { + int i, j, k; + + checked { + i = System.Int32.MinValue + 1234; + j = 1234; + k = i - j; + } + + if (k != System.Int32.MinValue) + return 6; + + return 0; + } + + public static int test_0_sub_ovf_un () { + uint i, j, k; + + checked { + i = System.UInt32.MaxValue; + j = 0; + k = i - j; + } + + if (k != System.UInt32.MaxValue) + return 1; + + checked { + i = System.UInt32.MaxValue; + j = System.UInt32.MaxValue; + k = i - j; + } + + if (k != 0) + return 2; + + return 0; + } + + public static int test_3_or () { + int b = 2; + int a = 3; + return b | a; + } + + public static int test_3_or_un () { + uint b = 2; + uint a = 3; + return (int)(b | a); + } + + public static int test_3_or_short_un () { + ushort b = 2; + ushort a = 3; + return (int)(b | a); + } + + public static int test_18_or_imm () { + int b = 2; + return b | 0x10; + } + + public static int test_268435458_or_large_imm () { + int b = 2; + return b | 0x10000000; + } + + public static int test_268435459_or_large_imm2 () { + int b = 2; + return b | 0x10000001; + } + + public static int test_1_xor () { + int b = 2; + int a = 3; + return b ^ a; + } + + public static int test_1_xor_imm () { + int b = 2; + return b ^ 3; + } + + public static int test_983041_xor_imm_large () { + int b = 2; + return b ^ 0xf0003; + } + + public static int test_1_neg () { + int b = -2; + b++; + return -b; + } + + public static int test_2_not () { + int b = ~2; + b = ~b; + return b; + } + + public static int test_16_shift () { + int b = 2; + int a = 3; + return b << a; + } + + public static int test_16_shift_add () { + int b = 2; + int a = 3; + int c = 0; + return b << (a + c); + } + + public static int test_16_shift_add2 () { + int b = 2; + int a = 3; + int c = 0; + return (b + c) << a; + } + + public static int test_16_shift_imm () { + int b = 2; + return b << 3; + } + + public static int test_524288_shift_imm_large () { + int b = 2; + return b << 18; + } + + public static int test_12_shift_imm_inv () { + int b = 2; + return 3 << 2; + } + + public static int test_12_shift_imm_inv_sbyte () { + sbyte b = 2; + return 3 << 2; + } + + public static int test_1_rshift_imm () { + int b = 8; + return b >> 3; + } + + public static int test_2_unrshift_imm () { + uint b = 16; + return (int)(b >> 3); + } + + public static int test_0_bigunrshift_imm () { + unchecked { + uint b = (uint)-1; + b = b >> 1; + if (b != 0x7fffffff) + return 1; + return 0; + } + } + + public static int test_0_bigrshift_imm () { + int b = -1; + b = b >> 1; + if (b != -1) + return 1; + return 0; + } + + public static int test_1_rshift () { + int b = 8; + int a = 3; + return b >> a; + } + + public static int test_2_unrshift () { + uint b = 16; + int a = 3; + return (int)(b >> a); + } + + public static int test_0_bigunrshift () { + unchecked { + uint b = (uint)-1; + int a = 1; + b = b >> a; + if (b != 0x7fffffff) + return 1; + return 0; + } + } + + public static int test_0_bigrshift () { + int b = -1; + int a = 1; + b = b >> a; + if (b != -1) + return 1; + return 0; + } + + public static int test_2_cond () { + int b = 2, a = 3, c; + if (a == b) + return 0; + return 2; + } + + public static int test_2_cond_short () { + short b = 2, a = 3, c; + if (a == b) + return 0; + return 2; + } + + public static int test_2_cond_sbyte () { + sbyte b = 2, a = 3, c; + if (a == b) + return 0; + return 2; + } + + public static int test_6_cascade_cond () { + int b = 2, a = 3, c; + if (a == b) + return 0; + else if (b > a) + return 1; + else if (b != b) + return 2; + else { + c = 1; + } + return a + b + c; + } + + public static int test_6_cascade_short () { + short b = 2, a = 3, c; + if (a == b) + return 0; + else if (b > a) + return 1; + else if (b != b) + return 2; + else { + c = 1; + } + return a + b + c; + } + + public static int test_0_short_sign_extend () { + int t1 = 0xffeedd; + short s1 = (short)t1; + int t2 = s1; + + if ((uint)t2 != 0xffffeedd) + return 1; + else + return 0; + } + + public static int test_127_iconv_to_i1 () { + int i = 0x100017f; + sbyte s = (sbyte)i; + + return s; + } + + public static int test_384_iconv_to_i2 () { + int i = 0x1000180; + short s = (short)i; + + return s; + } + + public static int test_15_for_loop () { + int i; + for (i = 0; i < 15; ++i) { + } + return i; + } + + public static int test_11_nested_for_loop () { + int i, j = 0; /* mcs bug here if j not set */ + for (i = 0; i < 15; ++i) { + for (j = 200; j >= 5; --j) ; + } + return i - j; + } + + public static int test_11_several_nested_for_loops () { + int i, j = 0; /* mcs bug here if j not set */ + for (i = 0; i < 15; ++i) { + for (j = 200; j >= 5; --j) ; + } + i = j = 0; + for (i = 0; i < 15; ++i) { + for (j = 200; j >= 5; --j) ; + } + return i - j; + } + + public static int test_0_conv_ovf_i1 () { + int c; + + //for (int j = 0; j < 10000000; j++) + checked { + c = 127; + sbyte b = (sbyte)c; + c = -128; + b = (sbyte)c; + } + + return 0; + } + + public static int test_0_conv_ovf_i1_un () { + uint c; + + checked { + c = 127; + sbyte b = (sbyte)c; + } + + return 0; + } + + public static int test_0_conv_ovf_i2 () { + int c; + + checked { + c = 32767; + Int16 b = (Int16)c; + c = -32768; + b = (Int16)c; + unchecked { + uint u = 0xfffffffd; + c = (int)u; + } + b = (Int16)c; + } + + return 0; + } + + public static int test_0_conv_ovf_i2_un () { + uint c; + + checked { + c = 32767; + Int16 b = (Int16)c; + } + + return 0; + } + + public static int test_0_conv_ovf_u2 () { + int c; + + checked { + c = 65535; + UInt16 b = (UInt16)c; + } + + return 0; + } + + public static int test_0_conv_ovf_u2_un () { + uint c; + + checked { + c = 65535; + UInt16 b = (UInt16)c; + } + + return 0; + } + + public static int test_0_conv_ovf_u4 () { + int c; + + checked { + c = 0x7fffffff; + uint b = (uint)c; + } + + return 0; + } + + public static int test_0_conv_ovf_i4_un () { + uint c; + + checked { + c = 0x7fffffff; + int b = (int)c; + } + + return 0; + } + + public static int test_0_bool () { + bool val = true; + if (val) + return 0; + return 1; + } + + public static int test_1_bool_inverted () { + bool val = true; + if (!val) + return 0; + return 1; + } + + public static int test_1_bool_assign () { + bool val = true; + val = !val; // this should produce a ceq + if (val) + return 0; + return 1; + } + + public static int test_1_bool_multi () { + bool val = true; + bool val2 = true; + val = !val; + if ((val && !val2) && (!val2 && val)) + return 0; + return 1; + } + + public static int test_16_spill () { + int a = 1; + int b = 2; + int c = 3; + int d = 4; + int e = 5; + + return (1 + (a + (b + (c + (d + e))))); + } + + public static int test_1_switch () { + int n = 0; + + switch (n) { + case 0: return 1; + case 1: return 2; + case -1: return 3; + default: + return 4; + } + return 1; + } + + public static int test_0_switch_constprop () { + int n = -1; + + switch (n) { + case 0: return 2; + case 1: return 3; + case 2: return 3; + default: + return 0; + } + return 3; + } + + public static int test_0_switch_constprop2 () { + int n = 3; + + switch (n) { + case 0: return 2; + case 1: return 3; + case 2: return 3; + default: + return 0; + } + return 3; + } + + public static int test_0_while_loop_1 () { + + int value = 255; + + do { + value = value >> 4; + } while (value != 0); + + return 0; + } + + public static int test_0_while_loop_2 () { + int value = 255; + int position = 5; + + do { + value = value >> 4; + } while (value != 0 && position > 1); + + return 0; + } + + public static int test_0_char_conv () { + int i = 1; + + char tc = (char) ('0' + i); + + if (tc != '1') + return 1; + + return 0; + } + + public static int test_3_shift_regalloc () { + int shift = 8; + int orig = 1; + byte value = 0xfe; + + orig &= ~(0xff << shift); + orig |= value << shift; + + if (orig == 0xfe01) + return 3; + return 0; + } + + enum E {A, B}; + + public static int test_2_optimize_branches () { + switch (E.A) { + case E.A: + if (E.A == E.B) { + } + break; + } + return 2; + } + + public static int test_0_checked_byte_cast () { + int v = 250; + int b = checked ((byte) (v)); + + if (b != 250) + return 1; + return 0; + } + + public static int test_0_checked_byte_cast_un () { + uint v = 250; + uint b = checked ((byte) (v)); + + if (b != 250) + return 1; + return 0; + } + + public static int test_0_checked_short_cast () { + int v = 250; + int b = checked ((ushort) (v)); + + if (b != 250) + return 1; + return 0; + } + + public static int test_0_checked_short_cast_un () { + uint v = 250; + uint b = checked ((ushort) (v)); + + if (b != 250) + return 1; + return 0; + } + + public static int test_1_a_eq_b_plus_a () { + int a = 0, b = 1; + a = b + a; + return a; + } + + public static int test_0_comp () { + int a = 0; + int b = -1; + int error = 1; + bool val; + + val = a < b; + if (val) + return error; + error++; + + val = a > b; + if (!val) + return error; + error ++; + + val = a == b; + if (val) + return error; + error ++; + + val = a == a; + if (!val) + return error; + error ++; + + return 0; + } + + public static int test_0_comp_unsigned () { + uint a = 1; + uint b = 0xffffffff; + int error = 1; + bool val; + + val = a < b; + if (!val) + return error; + error++; + + val = a <= b; + if (!val) + return error; + error++; + + val = a == b; + if (val) + return error; + error++; + + val = a >= b; + if (val) + return error; + error++; + + val = a > b; + if (val) + return error; + error++; + + val = b < a; + if (val) + return error; + error++; + + val = b <= a; + if (val) + return error; + error++; + + val = b == a; + if (val) + return error; + error++; + + val = b > a; + if (!val) + return error; + error++; + + val = b >= a; + if (!val) + return error; + error++; + + return 0; + } + + public static int test_16_cmov () + { + int n = 0; + if (n == 0) + n = 16; + + return n; + } + + public static int test_0_and_cmp () + { + /* test esi, imm */ + int local = 0x01020304; + + if ((local & 0x01020304) == 0) + return 7; + + if ((local & 0x00000304) == 0) + return 8; + + if ((local & 0x00000004) == 0) + return 9; + + if ((local & 0x00000300) == 0) + return 10; + + if ((local & 0x00020000) == 0) + return 11; + + if ((local & 0x01000000) == 0) + return 12; + + return 0; + } + + public static int test_0_mul_imm_opt () + { + int i; + + i = 1; + if ((i * 2) != 2) + return 1; + i = -1; + if ((i * 2) != -2) + return 2; + i = 1; + if ((i * 3) != 3) + return 3; + i = -1; + if ((i * 3) != -3) + return 4; + i = 1; + if ((i * 5) != 5) + return 5; + i = -1; + if ((i * 5) != -5) + return 6; + i = 1; + if ((i * 6) != 6) + return 7; + i = -1; + if ((i * 6) != -6) + return 8; + i = 1; + if ((i * 9) != 9) + return 9; + i = -1; + if ((i * 9) != -9) + return 10; + i = 1; + if ((i * 10) != 10) + return 11; + i = -1; + if ((i * 10) != -10) + return 12; + i = 1; + if ((i * 12) != 12) + return 13; + i = -1; + if ((i * 12) != -12) + return 14; + i = 1; + if ((i * 25) != 25) + return 15; + i = -1; + if ((i * 25) != -25) + return 16; + i = 1; + if ((i * 100) != 100) + return 17; + i = -1; + if ((i * 100) != -100) + return 18; + + return 0; + } + + public static int test_0_cne () + { + int x = 0; + int y = 1; + + bool b = x != y; + bool bb = x != x; + + if (!b) + return 1; + if (bb) + return 2; + + return 0; + } + + public static int test_0_cmp_regvar_zero () + { + int n = 10; + + if (!(n > 0 && n >= 0 && n != 0)) + return 1; + if (n < 0 || n <= 0 || n == 0) + return 1; + + return 0; + } + + public static int test_5_div_un_cfold () + { + uint i = 10; + uint j = 2; + + return (int)(i / j); + } + + public static int test_1_rem_un_cfold () + { + uint i = 11; + uint j = 2; + + return (int)(i % j); + } + + public static int test_0_div_opt () { + int i; + + // Avoid cfolding this + i = 0; + for (int j = 0; j < 567; ++j) + i ++; + i += 1234000; + if ((i / 2) != 617283) + return 1; + if ((i / 4) != 308641) + return 2; + if ((i / 8) != 154320) + return 3; + if ((i / 16) != 77160) + return 4; + + // Avoid cfolding this + i = 0; + for (int j = 0; j < 567; ++j) + i --; + i -= 1234000; + if ((i / 2) != -617283) + return 5; + if ((i / 4) != -308641) + return 6; + if ((i / 8) != -154320) + return 7; + if ((i / 16) != -77160) + return 8; + + return 0; + } + + public static int test_0_udiv_opt () { + uint i; + + // Avoid cfolding this + i = 0; + for (int j = 0; j < 567; ++j) + i ++; + i += 1234000; + if ((i / 2) != 617283) + return 1; + if ((i / 4) != 308641) + return 2; + if ((i / 8) != 154320) + return 3; + if ((i / 16) != 77160) + return 4; + + return 0; + } + + public static int test_0_signed_ct_div () { + int n = 2147483647; + bool divide_by_zero = false; + bool overflow = false; + + if ((n / 2147483647) != 1) + return 1; + if ((n / -2147483647) != -1) + return 2; + n = -n; + if ((n / 2147483647) != -1) + return 3; + n--; /* MinValue */ + if ((n / -2147483648) != 1) + return 4; + if ((n / 2147483647) != -1) + return 5; + if ((n / 1) != n) + return 6; + + try { + int r = n / (-1); + } catch (OverflowException) { + overflow = true; + } + if (!overflow) + return 7; + + try { + int r = n / 0; + } catch (DivideByZeroException) { + divide_by_zero = true; + } + if (!divide_by_zero) + return 8; + + if ((n / 35) != -61356675) + return 9; + if ((n / -35) != 61356675) + return 10; + n = -(n + 1); /* MaxValue */ + if ((n / 35) != 61356675) + return 11; + if ((n / -35) != -61356675) + return 12; + + return 0; + } + + public static int test_0_unsigned_ct_div () { + uint n = 4294967295; + bool divide_by_zero = false; + + if ((n / 4294967295) != 1) + return 1; + n--; + if ((n / 4294967295) != 0) + return 2; + n++; + if ((n / 4294967294) != 1) + return 3; + if ((n / 1) != n) + return 4; + + try { + uint a = n / 0; + } catch (DivideByZeroException) { + divide_by_zero = true; + } + + if (!divide_by_zero) + return 5; + + if ((n / 35) != 122713351) + return 9; + + return 0; + } + + public static int test_0_rem_opt () { + int i; + + // Avoid cfolding this + i = 0; + for (int j = 0; j < 29; ++j) + i ++; + if ((i % 2) != 1) + return 1; + if ((i % 4) != 1) + return 2; + if ((i % 8) != 5) + return 3; + if ((i % 16) != 13) + return 4; + + // Avoid cfolding this + i = 0; + for (int j = 0; j < 29; ++j) + i --; + if ((i % 2) != -1) + return 5; + if ((i % 4) != -1) + return 6; + if ((i % 8) != -5) + return 7; + if ((i % 16) != -13) + return 8; + + return 0; + } + + public static int cmov (int i) { + int j = 0; + + if (i > 0) + j = 1; + + return j; + } + + public static int cmov2 (int i) { + int j = 0; + + if (i <= 0) + ; + else + j = 1; + + return j; + } + + public static int test_0_branch_to_cmov_opt () { + if (cmov (0) != 0) + return 1; + if (cmov (1) != 1) + return 2; + if (cmov2 (0) != 0) + return 1; + if (cmov2 (1) != 1) + return 2; + return 0; + } + + public static unsafe int test_0_ishr_sign_extend () { + // Check that ishr does sign extension from bit 31 on 64 bit platforms + uint val = 0xF0000000u; + + uint *a = &val; + uint ui = (uint)((int)(*a) >> 2); + + if (ui != 0xfc000000) + return 1; + + // Same with non-immediates + int amount = 2; + + ui = (uint)((int)(*a) >> amount); + + if (ui != 0xfc000000) + return 2; + + return 0; + } + + public static unsafe int test_0_ishr_sign_extend_cfold () { + int i = 32768; + int j = i << 16; + int k = j >> 16; + + return k == -32768 ? 0 : 1; + } +} diff --git a/unity-2019.4.24f1-mbe/mono/mini/bench.cs b/unity-2019.4.24f1-mbe/mono/mini/bench.cs new file mode 100644 index 000000000..15cd67d60 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/bench.cs @@ -0,0 +1,245 @@ +using System; +using System.Reflection; + +/* + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + * Regression tests for the mono JIT. + * + * Each test needs to be of the form: + * + * static int test__ (); + * + * where is an integer (the value that needs to be returned by + * the method to make it pass. + * is a user-displayed name used to identify the test. + * + * The tests can be driven in two ways: + * *) running the program directly: Main() uses reflection to find and invoke + * the test methods (this is useful mostly to check that the tests are correct) + * *) with the --regression switch of the jit (this is the preferred way since + * all the tests will be run with optimizations on and off) + * + * The reflection logic could be moved to a .dll since we need at least another + * regression test file written in IL code to have better control on how + * the IL code looks. + */ + +class Tests { + + static int Main (string[] args) { + return TestDriver.RunTests (typeof (Tests), args); + } + + static public int test_0_many_nested_loops () { + // we do the loop a few times otherwise it's too fast + for (int i = 0; i < 5; ++i) { + int n = 16; + int x = 0; + int a = n; + while (a-- != 0) { + int b = n; + while (b-- != 0) { + int c = n; + while (c-- != 0) { + int d = n; + while (d-- != 0) { + int e = n; + while (e-- != 0) { + int f = n; + while (f-- != 0) { + x++; + } + } + } + } + } + } + if (x != 16777216) + return 1; + } + return 0; + } + + public static int test_0_logic_run () + { + // GPL: Copyright (C) 2001 Southern Storm Software, Pty Ltd. + int iter, i = 0; + + while (i++ < 10) { + // Initialize. + bool flag1 = true; + bool flag2 = true; + bool flag3 = true; + bool flag4 = true; + bool flag5 = true; + bool flag6 = true; + bool flag7 = true; + bool flag8 = true; + bool flag9 = true; + bool flag10 = true; + bool flag11 = true; + bool flag12 = true; + bool flag13 = true; + + // First set of tests. + for(iter = 0; iter < 2000000; ++iter) { + if((flag1 || flag2) && (flag3 || flag4) && + (flag5 || flag6 || flag7)) + { + flag8 = !flag8; + flag9 = !flag9; + flag10 = !flag10; + flag11 = !flag11; + flag12 = !flag12; + flag13 = !flag13; + flag1 = !flag1; + flag2 = !flag2; + flag3 = !flag3; + flag4 = !flag4; + flag5 = !flag5; + flag6 = !flag6; + flag1 = !flag1; + flag2 = !flag2; + flag3 = !flag3; + flag4 = !flag4; + flag5 = !flag5; + flag6 = !flag6; + } + } + } + return 0; + } + static public int test_1028_sieve () { + //int NUM = ((argc == 2) ? atoi(argv[1]) : 1); + int NUM = 2000; + byte[] flags = new byte[8192 + 1]; + int i, k; + int count = 0; + + while (NUM-- != 0) { + count = 0; + for (i=2; i <= 8192; i++) { + flags[i] = 1; + } + for (i=2; i <= 8192; i++) { + if (flags[i] != 0) { + // remove all multiples of prime: i + for (k=i+i; k <= 8192; k+=i) { + flags[k] = 0; + } + count++; + } + } + } + //printf("Count: %d\n", count); + return(count); + } + + public static int fib (int n) { + if (n < 2) + return 1; + return fib(n-2)+fib(n-1); + } + + public static int test_3524578_fib () { + for (int i = 0; i < 10; i++) + fib (32); + + return fib (32); + } + + private static ulong numMoves; + + static void movetower (int disc, int from, int to, int use) { + if (disc > 0) { + numMoves++; + movetower (disc-1, from, use, to); + movetower (disc-1, use, to, from); + } + } + + public static int test_0_hanoi () { + int iterations = 5000; + int numdiscs = 12; + + numMoves = 0; + while (iterations > 0) { + iterations--; + movetower (numdiscs, 1, 3, 2); + } + if (numMoves != 20475000) + return 1; + return 0; + } + + public static int test_0_castclass () { + object a = "a"; + + for (int i = 0; i < 100000000; i++) { + string b = (string)a; + if ((object)a != (object)b) + return 1; + } + return 0; + } + + public static int test_23005000_float () { + double a, b, c, d; + bool val; + int loops = 0; + a = 0.0; + b = 0.0001; + c = 2300.5; + d = 1000.0; + + while (a < c) { + if (a == d) + b *= 2; + a += b; + val = b >= c; + if (val) break; + loops++; + } + return loops; + } + + /* + /// Gaussian blur of a generated grayscale picture + private int test_0_blur(int size) { + const int num = 5; // Number of time to blur + byte[,] arr1 = new byte[size, size]; + byte[,] arr2 = new byte[size, size]; + + int iterations = 1; + + while(iterations-- > 0) { + + // Draw fake picture + for(int i = 0; i < size; i++) { + for(int j = 0; j < size; j++) { + arr1[i, j] = (byte) (i%255); + } + } + + for(int n = 0; n < num; n++) { // num rounds of blurring + for(int i = 3; i < size-3; i++) // vertical blur arr1 -> arr2 + for(int j = 0; j < size; j++) + arr2[i, j] = (byte)((arr1[i-3, j] + arr1[i+3, j] + + 6*(arr1[i-2, j]+arr1[i+2, j]) + + 15*(arr1[i-1, j]+arr1[i+1, j]) + + 20*arr1[i, j] + 32)>>6); + + for(int j = 3; j < size-3; j++) // horizontal blur arr1 -> arr2 + for(int i = 0; i < size; i++) + arr1[i, j] = (byte)((arr2[i, j-3] + arr2[i, j+3] + + 6*(arr2[i, j-2]+arr2[i, j+2]) + + 15*(arr2[i, j-1]+arr2[i, j+1]) + + 20*arr2[i, j] + 32)>>6); + } + } + + return 0; + } + */ +} + diff --git a/unity-2019.4.24f1-mbe/mono/mini/branch-opts.c b/unity-2019.4.24f1-mbe/mono/mini/branch-opts.c new file mode 100644 index 000000000..0dab1240f --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/branch-opts.c @@ -0,0 +1,1481 @@ +/** + * \file + * Branch optimizations support + * + * Authors: + * Patrik Torstensson (Patrik.Torstesson at gmail.com) + * + * (C) 2005 Ximian, Inc. http://www.ximian.com + * Copyright 2011 Xamarin Inc. http://www.xamarin.com + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include "config.h" +#include +#ifndef DISABLE_JIT + +#include "mini.h" + +/* + * Returns true if @bb is a basic block which falls through the next block. + * TODO verify if it helps to check if the bb last ins is a branch to its successor. + */ +static gboolean +mono_bb_is_fall_through (MonoCompile *cfg, MonoBasicBlock *bb) +{ + return bb->next_bb && bb->next_bb->region == bb->region && /*fall throught between regions is not really interesting or useful*/ + (bb->last_ins == NULL || !MONO_IS_BRANCH_OP (bb->last_ins)); /*and the last op can't be a branch too*/ +} + +/* + * Used by the arch code to replace the exception handling + * with a direct branch. This is safe to do if the + * exception object isn't used, no rethrow statement and + * no filter statement (verify). + * + */ +MonoInst * +mono_branch_optimize_exception_target (MonoCompile *cfg, MonoBasicBlock *bb, const char * exname) +{ + MonoMethodHeader *header = cfg->header; + MonoExceptionClause *clause; + MonoClass *exclass; + int i; + + if (!(cfg->opt & MONO_OPT_EXCEPTION)) + return NULL; + + if (bb->region == -1 || !MONO_BBLOCK_IS_IN_REGION (bb, MONO_REGION_TRY)) + return NULL; + + exclass = mono_class_load_from_name (mono_get_corlib (), "System", exname); + /* search for the handler */ + for (i = 0; i < header->num_clauses; ++i) { + clause = &header->clauses [i]; + if (MONO_OFFSET_IN_CLAUSE (clause, bb->real_offset)) { + if (clause->flags == MONO_EXCEPTION_CLAUSE_NONE && clause->data.catch_class && mono_class_is_assignable_from (clause->data.catch_class, exclass)) { + MonoBasicBlock *tbb; + + /* get the basic block for the handler and + * check if the exception object is used. + * Flag is set during method_to_ir due to + * pop-op is optmized away in codegen (burg). + */ + tbb = cfg->cil_offset_to_bb [clause->handler_offset]; + if (tbb && tbb->flags & BB_EXCEPTION_DEAD_OBJ && !(tbb->flags & BB_EXCEPTION_UNSAFE)) { + MonoBasicBlock *targetbb = tbb; + gboolean unsafe = FALSE; + + /* Check if this catch clause is ok to optimize by + * looking for the BB_EXCEPTION_UNSAFE in every BB that + * belongs to the same region. + * + * UNSAFE flag is set during method_to_ir (OP_RETHROW) + */ + while (!unsafe && tbb->next_bb && tbb->region == tbb->next_bb->region) { + if (tbb->next_bb->flags & BB_EXCEPTION_UNSAFE) { + unsafe = TRUE; + break; + } + tbb = tbb->next_bb; + } + + if (!unsafe) { + MonoInst *jump; + + /* Create dummy inst to allow easier integration in + * arch dependent code (opcode ignored) + */ + MONO_INST_NEW (cfg, jump, OP_BR); + + /* Allocate memory for our branch target */ + jump->inst_i1 = (MonoInst *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst)); + jump->inst_true_bb = targetbb; + + if (cfg->verbose_level > 2) + g_print ("found exception to optimize - returning branch to BB%d (%s) (instead of throw) for method %s:%s\n", targetbb->block_num, clause->data.catch_class->name, cfg->method->klass->name, cfg->method->name); + + return jump; + } + + return NULL; + } else { + /* Branching to an outer clause could skip inner clauses */ + return NULL; + } + } else { + /* Branching to an outer clause could skip inner clauses */ + return NULL; + } + } + } + + return NULL; +} + +static const int int_cmov_opcodes [] = { + OP_CMOV_IEQ, + OP_CMOV_INE_UN, + OP_CMOV_ILE, + OP_CMOV_IGE, + OP_CMOV_ILT, + OP_CMOV_IGT, + OP_CMOV_ILE_UN, + OP_CMOV_IGE_UN, + OP_CMOV_ILT_UN, + OP_CMOV_IGT_UN +}; + +static const int long_cmov_opcodes [] = { + OP_CMOV_LEQ, + OP_CMOV_LNE_UN, + OP_CMOV_LLE, + OP_CMOV_LGE, + OP_CMOV_LLT, + OP_CMOV_LGT, + OP_CMOV_LLE_UN, + OP_CMOV_LGE_UN, + OP_CMOV_LLT_UN, + OP_CMOV_LGT_UN +}; + +static G_GNUC_UNUSED int +br_to_br_un (int opcode) +{ + switch (opcode) { + case OP_IBGT: + return OP_IBGT_UN; + break; + case OP_IBLE: + return OP_IBLE_UN; + break; + case OP_LBGT: + return OP_LBGT_UN; + break; + case OP_LBLE: + return OP_LBLE_UN; + break; + default: + g_assert_not_reached (); + return -1; + } +} + +/** + * mono_replace_ins: + * + * Replace INS with its decomposition which is stored in a series of bblocks starting + * at FIRST_BB and ending at LAST_BB. On enter, PREV points to the predecessor of INS. + * On return, it will be set to the last ins of the decomposition. + */ +void +mono_replace_ins (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *ins, MonoInst **prev, MonoBasicBlock *first_bb, MonoBasicBlock *last_bb) +{ + MonoInst *next = ins->next; + + if (next && next->opcode == OP_NOP) { + /* Avoid NOPs following branches */ + ins->next = next->next; + next = next->next; + } + + if (first_bb == last_bb) { + /* + * Only one replacement bb, merge the code into + * the current bb. + */ + + /* Delete links between the first_bb and its successors */ + while (first_bb->out_count) + mono_unlink_bblock (cfg, first_bb, first_bb->out_bb [0]); + + /* Head */ + if (*prev) { + (*prev)->next = first_bb->code; + first_bb->code->prev = (*prev); + } else { + bb->code = first_bb->code; + } + + /* Tail */ + last_bb->last_ins->next = next; + if (next) + next->prev = last_bb->last_ins; + else + bb->last_ins = last_bb->last_ins; + *prev = last_bb->last_ins; + bb->has_array_access |= first_bb->has_array_access; + } else { + int i, count; + MonoBasicBlock **tmp_bblocks, *tmp; + MonoInst *last; + + /* Multiple BBs */ + + /* Set region/real_offset */ + for (tmp = first_bb; tmp; tmp = tmp->next_bb) { + tmp->region = bb->region; + tmp->real_offset = bb->real_offset; + } + + /* Split the original bb */ + if (ins->next) + ins->next->prev = NULL; + ins->next = NULL; + bb->last_ins = ins; + + /* Merge the second part of the original bb into the last bb */ + if (last_bb->last_ins) { + last_bb->last_ins->next = next; + if (next) + next->prev = last_bb->last_ins; + } else { + last_bb->code = next; + } + last_bb->has_array_access |= bb->has_array_access; + + if (next) { + for (last = next; last->next != NULL; last = last->next) + ; + last_bb->last_ins = last; + } + + for (i = 0; i < bb->out_count; ++i) + mono_link_bblock (cfg, last_bb, bb->out_bb [i]); + + /* Merge the first (dummy) bb to the original bb */ + if (*prev) { + (*prev)->next = first_bb->code; + first_bb->code->prev = (*prev); + } else { + bb->code = first_bb->code; + } + bb->last_ins = first_bb->last_ins; + bb->has_array_access |= first_bb->has_array_access; + + /* Delete the links between the original bb and its successors */ + tmp_bblocks = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoBasicBlock*) * bb->out_count); + memcpy (tmp_bblocks, bb->out_bb, sizeof (MonoBasicBlock*) * bb->out_count); + count = bb->out_count; + for (i = 0; i < count; ++i) + mono_unlink_bblock (cfg, bb, tmp_bblocks [i]); + + /* Add links between the original bb and the first_bb's successors */ + for (i = 0; i < first_bb->out_count; ++i) { + MonoBasicBlock *out_bb = first_bb->out_bb [i]; + + mono_link_bblock (cfg, bb, out_bb); + } + /* Delete links between the first_bb and its successors */ + for (i = 0; i < bb->out_count; ++i) { + MonoBasicBlock *out_bb = bb->out_bb [i]; + + mono_unlink_bblock (cfg, first_bb, out_bb); + } + last_bb->next_bb = bb->next_bb; + bb->next_bb = first_bb->next_bb; + + *prev = NULL; + } +} + +void +mono_if_conversion (MonoCompile *cfg) +{ +#ifdef MONO_ARCH_HAVE_CMOV_OPS + MonoBasicBlock *bb; + gboolean changed = FALSE; + int filter = FILTER_NOP | FILTER_IL_SEQ_POINT; + + if (!(cfg->opt & MONO_OPT_CMOV)) + return; + + // FIXME: Make this work with extended bblocks + + /* + * This pass requires somewhat optimized IR code so it should be run after + * local cprop/deadce. Also, it should be run before dominator computation, since + * it changes control flow. + */ + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { + MonoBasicBlock *bb1, *bb2; + + restart: + /* Look for the IR code generated from cond ? a : b + * which is: + * BB: + * b [BB1BB2] + * BB1: + * <- + * br BB3 + * BB2: + * <- + * br BB3 + */ + if (!(bb->out_count == 2 && !bb->extended)) + continue; + + bb1 = bb->out_bb [0]; + bb2 = bb->out_bb [1]; + + if (bb1->in_count == 1 && bb2->in_count == 1 && bb1->out_count == 1 && bb2->out_count == 1 && bb1->out_bb [0] == bb2->out_bb [0]) { + MonoInst *compare, *branch, *ins1, *ins2, *cmov, *move, *tmp; + MonoBasicBlock *true_bb, *false_bb; + gboolean simple, ret; + int dreg, tmp_reg; + CompType comp_type; + + branch = mono_bb_last_inst (bb, filter); + + if (!branch || branch->opcode == OP_BR_REG || branch->opcode == OP_BR) + continue; + + /* Find the compare instruction */ + compare = mono_inst_prev (branch, filter); + if (!compare) + continue; + + if (!MONO_IS_COND_BRANCH_OP (branch)) + /* This can happen if a cond branch is optimized away */ + continue; + + true_bb = branch->inst_true_bb; + false_bb = branch->inst_false_bb; + + /* + * Check that bb1 and bb2 are 'simple' and both assign to the same + * variable. + */ + /* FIXME: Get rid of the nops earlier */ + ins1 = mono_bb_first_inst (true_bb, filter); + ins2 = mono_bb_first_inst (false_bb, filter); + + if (!(ins1 && ins2 && ins1->dreg == ins2->dreg && ins1->dreg != -1)) + continue; + + simple = TRUE; + for (tmp = ins1->next; tmp; tmp = tmp->next) + if (!((tmp->opcode == OP_NOP) || (tmp->opcode == OP_IL_SEQ_POINT) || (tmp->opcode == OP_BR))) + simple = FALSE; + + for (tmp = ins2->next; tmp; tmp = tmp->next) + if (!((tmp->opcode == OP_NOP) || (tmp->opcode == OP_IL_SEQ_POINT) || (tmp->opcode == OP_BR))) + simple = FALSE; + + if (!simple) + continue; + + /* We move ins1/ins2 before the compare so they should have no side effect */ + if (!(MONO_INS_HAS_NO_SIDE_EFFECT (ins1) && MONO_INS_HAS_NO_SIDE_EFFECT (ins2))) + continue; + + /* Moving ins1/ins2 could change the comparison */ + /* FIXME: */ + if (!((compare->sreg1 != ins1->dreg) && (compare->sreg2 != ins1->dreg))) + continue; + + /* FIXME: */ + comp_type = mono_opcode_to_type (branch->opcode, compare->opcode); + if (!((comp_type == CMP_TYPE_I) || (comp_type == CMP_TYPE_L))) + continue; + + /* FIXME: */ + /* ins->type might not be set */ + if (INS_INFO (ins1->opcode) [MONO_INST_DEST] != 'i') + continue; + + if (cfg->verbose_level > 2) { + printf ("\tBranch -> CMove optimization in BB%d on\n", bb->block_num); + printf ("\t\t"); mono_print_ins (compare); + printf ("\t\t"); mono_print_ins (mono_inst_next (compare, filter)); + printf ("\t\t"); mono_print_ins (ins1); + printf ("\t\t"); mono_print_ins (ins2); + } + + changed = TRUE; + + //printf ("HIT!\n"); + + /* Assignments to the return register must remain at the end of bbs */ + if (cfg->ret) + ret = ins1->dreg == cfg->ret->dreg; + else + ret = FALSE; + + tmp_reg = mono_alloc_dreg (cfg, STACK_I4); + dreg = ins1->dreg; + + /* Rewrite ins1 to emit to tmp_reg */ + ins1->dreg = tmp_reg; + + if (ret) { + dreg = mono_alloc_dreg (cfg, STACK_I4); + ins2->dreg = dreg; + } + + /* Remove ins1/ins2 from bb1/bb2 */ + MONO_REMOVE_INS (true_bb, ins1); + MONO_REMOVE_INS (false_bb, ins2); + + /* Move ins1 and ins2 before the comparison */ + /* ins1 comes first to avoid ins1 overwriting an argument of ins2 */ + mono_bblock_insert_before_ins (bb, compare, ins2); + mono_bblock_insert_before_ins (bb, ins2, ins1); + + /* Add cmov instruction */ + MONO_INST_NEW (cfg, cmov, OP_NOP); + cmov->dreg = dreg; + cmov->sreg1 = dreg; + cmov->sreg2 = tmp_reg; + switch (mono_opcode_to_type (branch->opcode, compare->opcode)) { + case CMP_TYPE_I: + cmov->opcode = int_cmov_opcodes [mono_opcode_to_cond (branch->opcode)]; + break; + case CMP_TYPE_L: + cmov->opcode = long_cmov_opcodes [mono_opcode_to_cond (branch->opcode)]; + break; + default: + g_assert_not_reached (); + } + mono_bblock_insert_after_ins (bb, compare, cmov); + + if (ret) { + /* Add an extra move */ + MONO_INST_NEW (cfg, move, OP_MOVE); + move->dreg = cfg->ret->dreg; + move->sreg1 = dreg; + mono_bblock_insert_after_ins (bb, cmov, move); + } + + /* Rewrite the branch */ + branch->opcode = OP_BR; + branch->inst_target_bb = true_bb->out_bb [0]; + mono_link_bblock (cfg, bb, branch->inst_target_bb); + + /* Reorder bblocks */ + mono_unlink_bblock (cfg, bb, true_bb); + mono_unlink_bblock (cfg, bb, false_bb); + mono_unlink_bblock (cfg, true_bb, true_bb->out_bb [0]); + mono_unlink_bblock (cfg, false_bb, false_bb->out_bb [0]); + mono_remove_bblock (cfg, true_bb); + mono_remove_bblock (cfg, false_bb); + + /* Merge bb and its successor if possible */ + if ((bb->out_bb [0]->in_count == 1) && (bb->out_bb [0] != cfg->bb_exit) && + (bb->region == bb->out_bb [0]->region)) { + mono_merge_basic_blocks (cfg, bb, bb->out_bb [0]); + goto restart; + } + } + + /* Look for the IR code generated from if (cond) <- + * which is: + * BB: + * b [BB1BB2] + * BB1: + * <- + * br BB2 + */ + + if ((bb2->in_count == 1 && bb2->out_count == 1 && bb2->out_bb [0] == bb1) || + (bb1->in_count == 1 && bb1->out_count == 1 && bb1->out_bb [0] == bb2)) { + MonoInst *compare, *branch, *ins1, *cmov, *tmp; + gboolean simple; + int dreg, tmp_reg; + CompType comp_type; + CompRelation cond; + MonoBasicBlock *next_bb, *code_bb; + + /* code_bb is the bblock containing code, next_bb is the successor bblock */ + if (bb2->in_count == 1 && bb2->out_count == 1 && bb2->out_bb [0] == bb1) { + code_bb = bb2; + next_bb = bb1; + } else { + code_bb = bb1; + next_bb = bb2; + } + + ins1 = mono_bb_first_inst (code_bb, filter); + + if (!ins1) + continue; + + /* Check that code_bb is simple */ + simple = TRUE; + for (tmp = ins1; tmp; tmp = tmp->next) + if (!((tmp->opcode == OP_NOP) || (tmp->opcode == OP_IL_SEQ_POINT) || (tmp->opcode == OP_BR))) + simple = FALSE; + + if (!simple) + continue; + + /* We move ins1 before the compare so it should have no side effect */ + if (!MONO_INS_HAS_NO_SIDE_EFFECT (ins1)) + continue; + + branch = mono_bb_last_inst (bb, filter); + + if (!branch || branch->opcode == OP_BR_REG) + continue; + + /* Find the compare instruction */ + compare = mono_inst_prev (branch, filter); + if (!compare) + continue; + + if (!MONO_IS_COND_BRANCH_OP (branch)) + /* This can happen if a cond branch is optimized away */ + continue; + + /* FIXME: */ + comp_type = mono_opcode_to_type (branch->opcode, compare->opcode); + if (!((comp_type == CMP_TYPE_I) || (comp_type == CMP_TYPE_L))) + continue; + + /* FIXME: */ + /* ins->type might not be set */ + if (INS_INFO (ins1->opcode) [MONO_INST_DEST] != 'i') + continue; + + /* FIXME: */ + if (cfg->ret && ins1->dreg == cfg->ret->dreg) + continue; + + if (!(cfg->opt & MONO_OPT_DEADCE)) + /* + * It is possible that dreg is never set before, so we can't use + * it as an sreg of the cmov instruction (#582322). + */ + continue; + + if (cfg->verbose_level > 2) { + printf ("\tBranch -> CMove optimization (2) in BB%d on\n", bb->block_num); + printf ("\t\t"); mono_print_ins (compare); + printf ("\t\t"); mono_print_ins (mono_inst_next (compare, filter)); + printf ("\t\t"); mono_print_ins (ins1); + } + + changed = TRUE; + + //printf ("HIT!\n"); + + tmp_reg = mono_alloc_dreg (cfg, STACK_I4); + dreg = ins1->dreg; + + /* Rewrite ins1 to emit to tmp_reg */ + ins1->dreg = tmp_reg; + + /* Remove ins1 from code_bb */ + MONO_REMOVE_INS (code_bb, ins1); + + /* Move ins1 before the comparison */ + mono_bblock_insert_before_ins (bb, compare, ins1); + + /* Add cmov instruction */ + MONO_INST_NEW (cfg, cmov, OP_NOP); + cmov->dreg = dreg; + cmov->sreg1 = dreg; + cmov->sreg2 = tmp_reg; + cond = mono_opcode_to_cond (branch->opcode); + if (branch->inst_false_bb == code_bb) + cond = mono_negate_cond (cond); + switch (mono_opcode_to_type (branch->opcode, compare->opcode)) { + case CMP_TYPE_I: + cmov->opcode = int_cmov_opcodes [cond]; + break; + case CMP_TYPE_L: + cmov->opcode = long_cmov_opcodes [cond]; + break; + default: + g_assert_not_reached (); + } + mono_bblock_insert_after_ins (bb, compare, cmov); + + /* Rewrite the branch */ + branch->opcode = OP_BR; + branch->inst_target_bb = next_bb; + mono_link_bblock (cfg, bb, branch->inst_target_bb); + + /* Nullify the branch at the end of code_bb */ + if (code_bb->code) { + branch = code_bb->code; + MONO_DELETE_INS (code_bb, branch); + } + + /* Reorder bblocks */ + mono_unlink_bblock (cfg, bb, code_bb); + mono_unlink_bblock (cfg, code_bb, next_bb); + + /* Merge bb and its successor if possible */ + if ((bb->out_bb [0]->in_count == 1) && (bb->out_bb [0] != cfg->bb_exit) && + (bb->region == bb->out_bb [0]->region)) { + mono_merge_basic_blocks (cfg, bb, bb->out_bb [0]); + + /* + * bbn might have fallen through to the next bb without a branch, + * have to add one now (#474718). + * FIXME: Maybe need to do this more generally in + * merge_basic_blocks () ? + */ + if (!(bb->last_ins && MONO_IS_BRANCH_OP (bb->last_ins)) && bb->out_count) { + MONO_INST_NEW (cfg, ins1, OP_BR); + ins1->inst_target_bb = bb->out_bb [0]; + MONO_ADD_INS (bb, ins1); + } + goto restart; + } + } + } + + /* + * Optimize checks like: if (v < 0 || v > limit) by changing then to unsigned + * compares. This isn't really if conversion, but it easier to do here than in + * optimize_branches () since the IR is already optimized. + */ + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { + MonoBasicBlock *bb1, *bb2, *next_bb; + MonoInst *branch1, *branch2, *compare1, *ins, *next; + + /* Look for the IR code generated from if ( < 0 || v > ) + * after branch opts which is: + * BB: + * icompare_imm R [0] + * int_blt [BB1BB2] + * BB2: + * icompare_imm R [] + * int_ble [BB3BB1] + */ + if (!(bb->out_count == 2 && !bb->extended)) + continue; + + bb1 = bb->out_bb [0]; + bb2 = bb->out_bb [1]; + + // FIXME: Add more cases + + /* Check structure */ + if (!(bb1->in_count == 2 && bb1->in_bb [0] == bb && bb1->in_bb [1] == bb2 && bb2->in_count == 1 && bb2->out_count == 2)) + continue; + + next_bb = bb2; + + /* Check first branch */ + branch1 = mono_bb_last_inst (bb, filter); + if (!(branch1 && ((branch1->opcode == OP_IBLT) || (branch1->opcode == OP_LBLT)) && (branch1->inst_false_bb == next_bb))) + continue; + + /* Check second branch */ + branch2 = mono_bb_last_inst (next_bb, filter); + if (!branch2) + continue; + + /* mcs sometimes generates inverted branches */ + if (((branch2->opcode == OP_IBGT) || (branch2->opcode == OP_LBGT)) && branch2->inst_true_bb == branch1->inst_true_bb) + ; + else if (((branch2->opcode == OP_IBLE) || (branch2->opcode == OP_LBLE)) && branch2->inst_false_bb == branch1->inst_true_bb) + ; + else + continue; + + /* Check first compare */ + compare1 = mono_inst_prev (mono_bb_last_inst (bb, filter), filter); + if (!(compare1 && ((compare1->opcode == OP_ICOMPARE_IMM) || (compare1->opcode == OP_LCOMPARE_IMM)) && compare1->inst_imm == 0)) + continue; + + /* Check second bblock */ + ins = mono_bb_first_inst (next_bb, filter); + if (!ins) + continue; + next = mono_inst_next (ins, filter); + if (((ins->opcode == OP_ICOMPARE_IMM) || (ins->opcode == OP_LCOMPARE_IMM)) && ins->sreg1 == compare1->sreg1 && next == branch2) { + /* The second arg must be positive */ + if (ins->inst_imm < 0) + continue; + } else if (((ins->opcode == OP_LDLEN) || (ins->opcode == OP_STRLEN)) && ins->dreg != compare1->sreg1 && next && next->opcode == OP_ICOMPARE && next->sreg1 == compare1->sreg1 && next->sreg2 == ins->dreg && mono_inst_next (next, filter) == branch2) { + /* Another common case: if (index < 0 || index > arr.Length) */ + } else { + continue; + } + + if (cfg->verbose_level > 2) { + printf ("\tSigned->unsigned compare optimization in BB%d on\n", bb->block_num); + printf ("\t\t"); mono_print_ins (compare1); + printf ("\t\t"); mono_print_ins (mono_inst_next (compare1, filter)); + printf ("\t\t"); mono_print_ins (ins); + } + + /* Rewrite the first compare+branch */ + MONO_DELETE_INS (bb, compare1); + branch1->opcode = OP_BR; + mono_unlink_bblock (cfg, bb, branch1->inst_true_bb); + mono_unlink_bblock (cfg, bb, branch1->inst_false_bb); + branch1->inst_target_bb = next_bb; + mono_link_bblock (cfg, bb, next_bb); + + /* Rewrite the second branch */ + branch2->opcode = br_to_br_un (branch2->opcode); + + mono_merge_basic_blocks (cfg, bb, next_bb); + } + +#if 0 + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { + MonoBasicBlock *bb1, *bb2; + MonoInst *prev, *compare, *branch, *ins1, *ins2, *cmov, *move, *tmp; + gboolean simple, ret; + int dreg, tmp_reg; + CompType comp_type; + + /* Look for the IR code generated from if (cond) <- + * after branch opts which is: + * BB: + * compare + * b [BB1] + * <- + * BB1: + */ + if (!(bb->out_count == 1 && bb->extended && bb->code && bb->code->next && bb->code->next->next)) + continue; + + mono_print_bb (bb, ""); + + /* Find the compare instruction */ + prev = NULL; + compare = bb->code; + g_assert (compare); + while (compare->next->next && compare->next->next != bb->last_ins) { + prev = compare; + compare = compare->next; + } + branch = compare->next; + if (!MONO_IS_COND_BRANCH_OP (branch)) + continue; + } +#endif + + if (changed) { + if (cfg->opt & MONO_OPT_BRANCH) + mono_optimize_branches (cfg); + /* Merging bblocks could make some variables local */ + mono_handle_global_vregs (cfg); + if (cfg->opt & (MONO_OPT_CONSPROP | MONO_OPT_COPYPROP)) + mono_local_cprop (cfg); + if (cfg->opt & MONO_OPT_DEADCE) + mono_local_deadce (cfg); + } +#endif +} + +void +mono_nullify_basic_block (MonoBasicBlock *bb) +{ + bb->in_count = 0; + bb->out_count = 0; + bb->in_bb = NULL; + bb->out_bb = NULL; + bb->next_bb = NULL; + bb->code = bb->last_ins = NULL; + bb->cil_code = NULL; +} + +static void +replace_out_block (MonoBasicBlock *bb, MonoBasicBlock *orig, MonoBasicBlock *repl) +{ + int i; + + for (i = 0; i < bb->out_count; i++) { + MonoBasicBlock *ob = bb->out_bb [i]; + if (ob == orig) { + if (!repl) { + if (bb->out_count > 1) { + bb->out_bb [i] = bb->out_bb [bb->out_count - 1]; + } + bb->out_count--; + } else { + bb->out_bb [i] = repl; + } + } + } +} + +static void +replace_in_block (MonoBasicBlock *bb, MonoBasicBlock *orig, MonoBasicBlock *repl) +{ + int i; + + for (i = 0; i < bb->in_count; i++) { + MonoBasicBlock *ib = bb->in_bb [i]; + if (ib == orig) { + if (!repl) { + if (bb->in_count > 1) { + bb->in_bb [i] = bb->in_bb [bb->in_count - 1]; + } + bb->in_count--; + } else { + bb->in_bb [i] = repl; + } + } + } +} + +static void +replace_out_block_in_code (MonoBasicBlock *bb, MonoBasicBlock *orig, MonoBasicBlock *repl) +{ + MonoInst *ins; + + for (ins = bb->code; ins != NULL; ins = ins->next) { + switch (ins->opcode) { + case OP_BR: + if (ins->inst_target_bb == orig) + ins->inst_target_bb = repl; + break; + case OP_CALL_HANDLER: + if (ins->inst_target_bb == orig) + ins->inst_target_bb = repl; + break; + case OP_SWITCH: { + int i; + int n = GPOINTER_TO_INT (ins->klass); + for (i = 0; i < n; i++ ) { + if (ins->inst_many_bb [i] == orig) + ins->inst_many_bb [i] = repl; + } + break; + } + default: + if (MONO_IS_COND_BRANCH_OP (ins)) { + if (ins->inst_true_bb == orig) + ins->inst_true_bb = repl; + if (ins->inst_false_bb == orig) + ins->inst_false_bb = repl; + } else if (MONO_IS_JUMP_TABLE (ins)) { + int i; + MonoJumpInfoBBTable *table = (MonoJumpInfoBBTable *)MONO_JUMP_TABLE_FROM_INS (ins); + for (i = 0; i < table->table_size; i++ ) { + if (table->table [i] == orig) + table->table [i] = repl; + } + } + + break; + } + } +} + +/** + * Check if a bb is useless (is just made of NOPs and ends with an + * unconditional branch, or nothing). + * If it is so, unlink it from the CFG and nullify it, and return TRUE. + * Otherwise, return FALSE; + */ +static gboolean +remove_block_if_useless (MonoCompile *cfg, MonoBasicBlock *bb, MonoBasicBlock *previous_bb) { + MonoBasicBlock *target_bb = NULL; + MonoInst *inst; + + /* Do not touch handlers */ + if (bb->region != -1) { + bb->not_useless = TRUE; + return FALSE; + } + + MONO_BB_FOR_EACH_INS (bb, inst) { + switch (inst->opcode) { + case OP_NOP: + case OP_IL_SEQ_POINT: + break; + case OP_BR: + target_bb = inst->inst_target_bb; + break; + default: + bb->not_useless = TRUE; + return FALSE; + } + } + + if (target_bb == NULL) { + if ((bb->out_count == 1) && (bb->out_bb [0] == bb->next_bb)) { + target_bb = bb->next_bb; + } else { + /* Do not touch empty BBs that do not "fall through" to their next BB (like the exit BB) */ + return FALSE; + } + } + + /* Do not touch BBs following a switch (they are the "default" branch) */ + if ((previous_bb->last_ins != NULL) && (previous_bb->last_ins->opcode == OP_SWITCH)) { + return FALSE; + } + + /* Do not touch BBs following the entry BB and jumping to something that is not */ + /* thiry "next" bb (the entry BB cannot contain the branch) */ + if ((previous_bb == cfg->bb_entry) && (bb->next_bb != target_bb)) { + return FALSE; + } + + /* + * Do not touch BBs following a try block as the code in + * mini_method_compile needs them to compute the length of the try block. + */ + if (MONO_BBLOCK_IS_IN_REGION (previous_bb, MONO_REGION_TRY)) + return FALSE; + + /* Check that there is a target BB, and that bb is not an empty loop (Bug 75061) */ + if ((target_bb != NULL) && (target_bb != bb)) { + int i; + + if (cfg->verbose_level > 1) { + printf ("remove_block_if_useless, removed BB%d\n", bb->block_num); + } + + /* unlink_bblock () modifies the bb->in_bb array so can't use a for loop here */ + while (bb->in_count) { + MonoBasicBlock *in_bb = bb->in_bb [0]; + mono_unlink_bblock (cfg, in_bb, bb); + mono_link_bblock (cfg, in_bb, target_bb); + replace_out_block_in_code (in_bb, bb, target_bb); + } + + mono_unlink_bblock (cfg, bb, target_bb); + if (previous_bb != cfg->bb_entry && mono_bb_is_fall_through (cfg, previous_bb)) { + for (i = 0; i < previous_bb->out_count; i++) { + if (previous_bb->out_bb [i] == target_bb) { + MonoInst *jump; + MONO_INST_NEW (cfg, jump, OP_BR); + MONO_ADD_INS (previous_bb, jump); + jump->cil_code = previous_bb->cil_code; + jump->inst_target_bb = target_bb; + break; + } + } + } + + previous_bb->next_bb = bb->next_bb; + mono_nullify_basic_block (bb); + + return TRUE; + } else { + return FALSE; + } +} + +void +mono_merge_basic_blocks (MonoCompile *cfg, MonoBasicBlock *bb, MonoBasicBlock *bbn) +{ + MonoInst *inst; + MonoBasicBlock *prev_bb; + int i; + + /* There may be only one control flow edge between two BBs that we merge, and it should connect these BBs together. */ + g_assert (bb->out_count == 1 && bbn->in_count == 1 && bb->out_bb [0] == bbn && bbn->in_bb [0] == bb); + + bb->has_array_access |= bbn->has_array_access; + bb->extended |= bbn->extended; + + mono_unlink_bblock (cfg, bb, bbn); + for (i = 0; i < bbn->out_count; ++i) + mono_link_bblock (cfg, bb, bbn->out_bb [i]); + while (bbn->out_count) + mono_unlink_bblock (cfg, bbn, bbn->out_bb [0]); + + /* Handle the branch at the end of the bb */ + if (bb->has_call_handler) { + for (inst = bb->code; inst != NULL; inst = inst->next) { + if (inst->opcode == OP_CALL_HANDLER) { + g_assert (inst->inst_target_bb == bbn); + NULLIFY_INS (inst); + } + } + } + if (bb->has_jump_table) { + for (inst = bb->code; inst != NULL; inst = inst->next) { + if (MONO_IS_JUMP_TABLE (inst)) { + int i; + MonoJumpInfoBBTable *table = (MonoJumpInfoBBTable *)MONO_JUMP_TABLE_FROM_INS (inst); + for (i = 0; i < table->table_size; i++ ) { + /* Might be already NULL from a previous merge */ + if (table->table [i]) + g_assert (table->table [i] == bbn); + table->table [i] = NULL; + } + /* Can't nullify this as later instructions depend on it */ + } + } + } + if (bb->last_ins && MONO_IS_COND_BRANCH_OP (bb->last_ins)) { + g_assert (bb->last_ins->inst_false_bb == bbn); + bb->last_ins->inst_false_bb = NULL; + bb->extended = TRUE; + } else if (bb->last_ins && MONO_IS_BRANCH_OP (bb->last_ins)) { + NULLIFY_INS (bb->last_ins); + } + + bb->has_call_handler |= bbn->has_call_handler; + bb->has_jump_table |= bbn->has_jump_table; + + if (bb->last_ins) { + if (bbn->code) { + bb->last_ins->next = bbn->code; + bbn->code->prev = bb->last_ins; + bb->last_ins = bbn->last_ins; + } + } else { + bb->code = bbn->code; + bb->last_ins = bbn->last_ins; + } + + + /* Check if the control flow predecessor is also the linear IL predecessor. */ + if (bbn->in_bb [0]->next_bb == bbn) + prev_bb = bbn->in_bb [0]; + else + /* If it isn't, look for one among all basic blocks. */ + for (prev_bb = cfg->bb_entry; prev_bb && prev_bb->next_bb != bbn; prev_bb = prev_bb->next_bb) + ; + if (prev_bb) { + prev_bb->next_bb = bbn->next_bb; + } else { + /* bbn might not be in the bb list yet */ + if (bb->next_bb == bbn) + bb->next_bb = bbn->next_bb; + } + mono_nullify_basic_block (bbn); + + /* + * If bbn fell through to its next bblock, have to add a branch, since bb + * will not fall though to the same bblock (#513931). + */ + if (bb->last_ins && bb->out_count == 1 && bb->out_bb [0] != bb->next_bb && !MONO_IS_BRANCH_OP (bb->last_ins)) { + MONO_INST_NEW (cfg, inst, OP_BR); + inst->inst_target_bb = bb->out_bb [0]; + MONO_ADD_INS (bb, inst); + } +} + +static void +move_basic_block_to_end (MonoCompile *cfg, MonoBasicBlock *bb) +{ + MonoBasicBlock *bbn, *next; + + next = bb->next_bb; + + /* Find the previous */ + for (bbn = cfg->bb_entry; bbn->next_bb && bbn->next_bb != bb; bbn = bbn->next_bb) + ; + if (bbn->next_bb) { + bbn->next_bb = bb->next_bb; + } + + /* Find the last */ + for (bbn = cfg->bb_entry; bbn->next_bb; bbn = bbn->next_bb) + ; + bbn->next_bb = bb; + bb->next_bb = NULL; + + /* Add a branch */ + if (next && (!bb->last_ins || ((bb->last_ins->opcode != OP_NOT_REACHED) && (bb->last_ins->opcode != OP_BR) && (bb->last_ins->opcode != OP_BR_REG) && (!MONO_IS_COND_BRANCH_OP (bb->last_ins))))) { + MonoInst *ins; + + MONO_INST_NEW (cfg, ins, OP_BR); + MONO_ADD_INS (bb, ins); + mono_link_bblock (cfg, bb, next); + ins->inst_target_bb = next; + } +} + +/* + * mono_remove_block: + * + * Remove BB from the control flow graph + */ +void +mono_remove_bblock (MonoCompile *cfg, MonoBasicBlock *bb) +{ + MonoBasicBlock *tmp_bb; + + for (tmp_bb = cfg->bb_entry; tmp_bb && tmp_bb->next_bb != bb; tmp_bb = tmp_bb->next_bb) + ; + + g_assert (tmp_bb); + tmp_bb->next_bb = bb->next_bb; +} + +void +mono_remove_critical_edges (MonoCompile *cfg) +{ + MonoBasicBlock *bb; + MonoBasicBlock *previous_bb; + + if (cfg->verbose_level > 3) { + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { + int i; + printf ("remove_critical_edges, BEFORE BB%d (in:", bb->block_num); + for (i = 0; i < bb->in_count; i++) { + printf (" %d", bb->in_bb [i]->block_num); + } + printf (") (out:"); + for (i = 0; i < bb->out_count; i++) { + printf (" %d", bb->out_bb [i]->block_num); + } + printf (")"); + if (bb->last_ins != NULL) { + printf (" "); + mono_print_ins (bb->last_ins); + } + printf ("\n"); + } + } + + for (previous_bb = cfg->bb_entry, bb = previous_bb->next_bb; bb != NULL; previous_bb = previous_bb->next_bb, bb = bb->next_bb) { + if (bb->in_count > 1) { + int in_bb_index; + for (in_bb_index = 0; in_bb_index < bb->in_count; in_bb_index++) { + MonoBasicBlock *in_bb = bb->in_bb [in_bb_index]; + /* + * Have to remove non-critical edges whose source ends with a BR_REG + * ins too, since inserting a computation before the BR_REG could + * overwrite the sreg1 of the ins. + */ + if ((in_bb->out_count > 1) || (in_bb->out_count == 1 && in_bb->last_ins && in_bb->last_ins->opcode == OP_BR_REG)) { + MonoBasicBlock *new_bb = (MonoBasicBlock *)mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoBasicBlock)); + new_bb->block_num = cfg->num_bblocks++; +// new_bb->real_offset = bb->real_offset; + new_bb->region = bb->region; + + /* Do not alter the CFG while altering the BB list */ + if (mono_bb_is_fall_through (cfg, previous_bb)) { + if (previous_bb != cfg->bb_entry) { + int i; + /* Make sure previous_bb really falls through bb */ + for (i = 0; i < previous_bb->out_count; i++) { + if (previous_bb->out_bb [i] == bb) { + MonoInst *jump; + MONO_INST_NEW (cfg, jump, OP_BR); + MONO_ADD_INS (previous_bb, jump); + jump->cil_code = previous_bb->cil_code; + jump->inst_target_bb = bb; + break; + } + } + } else { + /* We cannot add any inst to the entry BB, so we must */ + /* put a new BB in the middle to hold the OP_BR */ + MonoInst *jump; + MonoBasicBlock *new_bb_after_entry = (MonoBasicBlock *)mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoBasicBlock)); + new_bb_after_entry->block_num = cfg->num_bblocks++; +// new_bb_after_entry->real_offset = bb->real_offset; + new_bb_after_entry->region = bb->region; + + MONO_INST_NEW (cfg, jump, OP_BR); + MONO_ADD_INS (new_bb_after_entry, jump); + jump->cil_code = bb->cil_code; + jump->inst_target_bb = bb; + + mono_unlink_bblock (cfg, previous_bb, bb); + mono_link_bblock (cfg, new_bb_after_entry, bb); + mono_link_bblock (cfg, previous_bb, new_bb_after_entry); + + previous_bb->next_bb = new_bb_after_entry; + previous_bb = new_bb_after_entry; + + if (cfg->verbose_level > 2) { + printf ("remove_critical_edges, added helper BB%d jumping to BB%d\n", new_bb_after_entry->block_num, bb->block_num); + } + } + } + + /* Insert new_bb in the BB list */ + previous_bb->next_bb = new_bb; + new_bb->next_bb = bb; + previous_bb = new_bb; + + /* Setup in_bb and out_bb */ + new_bb->in_bb = (MonoBasicBlock **)mono_mempool_alloc ((cfg)->mempool, sizeof (MonoBasicBlock*)); + new_bb->in_bb [0] = in_bb; + new_bb->in_count = 1; + new_bb->out_bb = (MonoBasicBlock **)mono_mempool_alloc ((cfg)->mempool, sizeof (MonoBasicBlock*)); + new_bb->out_bb [0] = bb; + new_bb->out_count = 1; + + /* Relink in_bb and bb to (from) new_bb */ + replace_out_block (in_bb, bb, new_bb); + replace_out_block_in_code (in_bb, bb, new_bb); + replace_in_block (bb, in_bb, new_bb); + + if (cfg->verbose_level > 2) { + printf ("remove_critical_edges, removed critical edge from BB%d to BB%d (added BB%d)\n", in_bb->block_num, bb->block_num, new_bb->block_num); + } + } + } + } + } + + if (cfg->verbose_level > 3) { + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { + int i; + printf ("remove_critical_edges, AFTER BB%d (in:", bb->block_num); + for (i = 0; i < bb->in_count; i++) { + printf (" %d", bb->in_bb [i]->block_num); + } + printf (") (out:"); + for (i = 0; i < bb->out_count; i++) { + printf (" %d", bb->out_bb [i]->block_num); + } + printf (")"); + if (bb->last_ins != NULL) { + printf (" "); + mono_print_ins (bb->last_ins); + } + printf ("\n"); + } + } +} + +/* + * Optimizes the branches on the Control Flow Graph + * + */ +void +mono_optimize_branches (MonoCompile *cfg) +{ + int i, count = 0, changed = FALSE; + MonoBasicBlock *bb, *bbn; + guint32 niterations; + MonoInst *bbn_first_inst; + int filter = FILTER_IL_SEQ_POINT; + + /* + * Some crazy loops could cause the code below to go into an infinite + * loop, see bug #53003 for an example. To prevent this, we put an upper + * bound on the number of iterations. + */ + if (cfg->num_bblocks > 1000) + niterations = cfg->num_bblocks * 2; + else + niterations = 1000; + + do { + MonoBasicBlock *previous_bb; + changed = FALSE; + niterations --; + + /* we skip the entry block (exit is handled specially instead ) */ + for (previous_bb = cfg->bb_entry, bb = cfg->bb_entry->next_bb; bb; previous_bb = bb, bb = bb->next_bb) { + count ++; + if (count == 1000) { + mono_threads_safepoint (); + count = 0; + } + /* dont touch code inside exception clauses */ + if (bb->region != -1) + continue; + + if (!bb->not_useless && remove_block_if_useless (cfg, bb, previous_bb)) { + changed = TRUE; + continue; + } + + if ((bbn = bb->next_bb) && bbn->in_count == 0 && bbn != cfg->bb_exit && bb->region == bbn->region) { + if (cfg->verbose_level > 2) + g_print ("nullify block triggered %d\n", bbn->block_num); + + bb->next_bb = bbn->next_bb; + + for (i = 0; i < bbn->out_count; i++) + replace_in_block (bbn->out_bb [i], bbn, NULL); + + mono_nullify_basic_block (bbn); + changed = TRUE; + } + + if (bb->out_count == 1) { + bbn = bb->out_bb [0]; + + /* conditional branches where true and false targets are the same can be also replaced with OP_BR */ + if (bb->last_ins && (bb->last_ins->opcode != OP_BR) && MONO_IS_COND_BRANCH_OP (bb->last_ins)) { + bb->last_ins->opcode = OP_BR; + bb->last_ins->inst_target_bb = bb->last_ins->inst_true_bb; + changed = TRUE; + if (cfg->verbose_level > 2) + g_print ("cond branch removal triggered in %d %d\n", bb->block_num, bb->out_count); + } + + if (bb->region == bbn->region && bb->next_bb == bbn) { + /* the block are in sequence anyway ... */ + + /* branches to the following block can be removed */ + if (bb->last_ins && bb->last_ins->opcode == OP_BR && !bbn->out_of_line) { + NULLIFY_INS (bb->last_ins); + changed = TRUE; + if (cfg->verbose_level > 2) + g_print ("br removal triggered %d -> %d\n", bb->block_num, bbn->block_num); + } + + if (bbn->in_count == 1 && !bb->extended) { + if (bbn != cfg->bb_exit) { + if (cfg->verbose_level > 2) + g_print ("block merge triggered %d -> %d\n", bb->block_num, bbn->block_num); + mono_merge_basic_blocks (cfg, bb, bbn); + changed = TRUE; + continue; + } + + //mono_print_bb_code (bb); + } + } + } + + if ((bbn = bb->next_bb) && bbn->in_count == 0 && bbn != cfg->bb_exit && bb->region == bbn->region) { + if (cfg->verbose_level > 2) { + g_print ("nullify block triggered %d\n", bbn->block_num); + } + bb->next_bb = bbn->next_bb; + + for (i = 0; i < bbn->out_count; i++) + replace_in_block (bbn->out_bb [i], bbn, NULL); + + mono_nullify_basic_block (bbn); + changed = TRUE; + continue; + } + + if (bb->out_count == 1) { + bbn = bb->out_bb [0]; + + if (bb->last_ins && bb->last_ins->opcode == OP_BR) { + bbn = bb->last_ins->inst_target_bb; + bbn_first_inst = mono_bb_first_inst (bbn, filter); + if (bb->region == bbn->region && bbn_first_inst && bbn_first_inst->opcode == OP_BR && + bbn_first_inst->inst_target_bb != bbn && + bbn_first_inst->inst_target_bb->region == bb->region) { + + if (cfg->verbose_level > 2) + g_print ("branch to branch triggered %d -> %d -> %d\n", bb->block_num, bbn->block_num, bbn_first_inst->inst_target_bb->block_num); + + replace_in_block (bbn, bb, NULL); + replace_out_block (bb, bbn, bbn_first_inst->inst_target_bb); + mono_link_bblock (cfg, bb, bbn_first_inst->inst_target_bb); + bb->last_ins->inst_target_bb = bbn_first_inst->inst_target_bb; + changed = TRUE; + continue; + } + } + } else if (bb->out_count == 2) { + if (bb->last_ins && MONO_IS_COND_BRANCH_NOFP (bb->last_ins)) { + int branch_result; + MonoBasicBlock *taken_branch_target = NULL, *untaken_branch_target = NULL; + + if (bb->last_ins->flags & MONO_INST_CFOLD_TAKEN) + branch_result = BRANCH_TAKEN; + else if (bb->last_ins->flags & MONO_INST_CFOLD_NOT_TAKEN) + branch_result = BRANCH_NOT_TAKEN; + else + branch_result = BRANCH_UNDEF; + + if (branch_result == BRANCH_TAKEN) { + taken_branch_target = bb->last_ins->inst_true_bb; + untaken_branch_target = bb->last_ins->inst_false_bb; + } else if (branch_result == BRANCH_NOT_TAKEN) { + taken_branch_target = bb->last_ins->inst_false_bb; + untaken_branch_target = bb->last_ins->inst_true_bb; + } + if (taken_branch_target) { + /* if mono_eval_cond_branch () is ever taken to handle + * non-constant values to compare, issue a pop here. + */ + bb->last_ins->opcode = OP_BR; + bb->last_ins->inst_target_bb = taken_branch_target; + if (!bb->extended) + mono_unlink_bblock (cfg, bb, untaken_branch_target); + changed = TRUE; + continue; + } + bbn = bb->last_ins->inst_true_bb; + bbn_first_inst = mono_bb_first_inst (bbn, filter); + if (bb->region == bbn->region && bbn_first_inst && bbn_first_inst->opcode == OP_BR && + bbn_first_inst->inst_target_bb->region == bb->region) { + if (cfg->verbose_level > 2) + g_print ("cbranch1 to branch triggered %d -> (%d) %d (0x%02x)\n", + bb->block_num, bbn->block_num, bbn_first_inst->inst_target_bb->block_num, + bbn_first_inst->opcode); + + /* + * Unlink, then relink bblocks to avoid various + * tricky situations when the two targets of the branch + * are equal, or will become equal after the change. + */ + mono_unlink_bblock (cfg, bb, bb->last_ins->inst_true_bb); + mono_unlink_bblock (cfg, bb, bb->last_ins->inst_false_bb); + + bb->last_ins->inst_true_bb = bbn_first_inst->inst_target_bb; + + mono_link_bblock (cfg, bb, bb->last_ins->inst_true_bb); + mono_link_bblock (cfg, bb, bb->last_ins->inst_false_bb); + + changed = TRUE; + continue; + } + + bbn = bb->last_ins->inst_false_bb; + bbn_first_inst = mono_bb_first_inst (bbn, filter); + if (bbn && bb->region == bbn->region && bbn_first_inst && bbn_first_inst->opcode == OP_BR && + bbn_first_inst->inst_target_bb->region == bb->region) { + if (cfg->verbose_level > 2) + g_print ("cbranch2 to branch triggered %d -> (%d) %d (0x%02x)\n", + bb->block_num, bbn->block_num, bbn_first_inst->inst_target_bb->block_num, + bbn_first_inst->opcode); + + mono_unlink_bblock (cfg, bb, bb->last_ins->inst_true_bb); + mono_unlink_bblock (cfg, bb, bb->last_ins->inst_false_bb); + + bb->last_ins->inst_false_bb = bbn_first_inst->inst_target_bb; + + mono_link_bblock (cfg, bb, bb->last_ins->inst_true_bb); + mono_link_bblock (cfg, bb, bb->last_ins->inst_false_bb); + + changed = TRUE; + continue; + } + + bbn = bb->last_ins->inst_false_bb; + /* + * If bb is an extended bb, it could contain an inside branch to bbn. + * FIXME: Enable the optimization if that is not true. + * If bblocks_linked () is true, then merging bb and bbn + * would require addition of an extra branch at the end of bbn + * slowing down loops. + */ + if (bbn && bb->region == bbn->region && bbn->in_count == 1 && cfg->enable_extended_bblocks && bbn != cfg->bb_exit && !bb->extended && !bbn->out_of_line && !mono_bblocks_linked (bbn, bb)) { + g_assert (bbn->in_bb [0] == bb); + if (cfg->verbose_level > 2) + g_print ("merge false branch target triggered BB%d -> BB%d\n", bb->block_num, bbn->block_num); + mono_merge_basic_blocks (cfg, bb, bbn); + changed = TRUE; + continue; + } + } + + if (bb->last_ins && MONO_IS_COND_BRANCH_NOFP (bb->last_ins)) { + if (bb->last_ins->inst_false_bb && bb->last_ins->inst_false_bb->out_of_line && (bb->region == bb->last_ins->inst_false_bb->region) && !cfg->disable_out_of_line_bblocks) { + /* Reverse the branch */ + bb->last_ins->opcode = mono_reverse_branch_op (bb->last_ins->opcode); + bbn = bb->last_ins->inst_false_bb; + bb->last_ins->inst_false_bb = bb->last_ins->inst_true_bb; + bb->last_ins->inst_true_bb = bbn; + + move_basic_block_to_end (cfg, bb->last_ins->inst_true_bb); + if (cfg->verbose_level > 2) + g_print ("cbranch to throw block triggered %d.\n", + bb->block_num); + } + } + } + } + } while (changed && (niterations > 0)); +} + +#else /* !DISABLE_JIT */ + +MONO_EMPTY_SOURCE_FILE (branch_opts); + +#endif /* !DISABLE_JIT */ diff --git a/unity-2019.4.24f1-mbe/mono/mini/builtin-types.cs b/unity-2019.4.24f1-mbe/mono/mini/builtin-types.cs new file mode 100644 index 000000000..de06a0dc5 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/builtin-types.cs @@ -0,0 +1,2398 @@ +// #define ARCH_32 +#define NINT_JIT_OPTIMIZED + +using System; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + +public class BuiltinTests { + static int test_0_nint_ctor () + { + var x = new nint (10); + var y = new nint (x); + var z = new nint (new nint (20)); + if ((int)x != 10) + return 1; + if ((int)y != 10) + return 2; + if ((int)z != 20) + return 3; + return 0; + } + + static int test_0_nint_casts () + { + var x = (nint)10; + var y = (nint)20L; + + if ((int)x != 10) + return 1; + if ((long)x != 10L) + return 2; + if ((int)y != 20) + return 3; + if ((long)y != 20L) + return 4; + return 0; + } + + static int test_0_nint_plus () + { + var x = (nint)10; + var z = +x; + if ((int)z != 10) + return 1; + return 0; + } + + static int test_0_nint_neg () + { + var x = (nint)10; + var z = -x; + if ((int)z != -10) + return 1; + return 0; + } + + static int test_0_nint_comp () + { + var x = (nint)10; + var z = ~x; + if ((int)z != ~10) + return 1; + return 0; + } + +#if FALSE + static int test_0_nint_inc () + { + var x = (nint)10; + ++x; + if ((int)x != 11) + return 1; + return 0; + } + + static int test_0_nint_dec () + { + var x = (nint)10; + --x; + if ((int)x != 9) + return 1; + return 0; + } +#endif + + static int test_0_nint_add () + { + var x = (nint)10; + var y = (nint)20; + var z = x + y; + if ((int)z != 30) + return 1; + return 0; + } + + static int test_0_nint_sub () + { + var x = (nint)10; + var y = (nint)20; + var z = x - y; + if ((int)z != -10) + return 1; + return 0; + } + + static int test_0_nint_mul () + { + var x = (nint)10; + var y = (nint)20; + var z = x * y; + if ((int)z != 200) + return 1; + return 0; + } + + static int test_0_nint_div () + { + var x = (nint)30; + var y = (nint)3; + var z = x / y; + if ((int)z != 10) + return 1; + return 0; + } + + static int test_0_nint_rem () + { + var x = (nint)22; + var y = (nint)10; + var z = x % y; + if ((int)z != 2) + return 1; + return 0; + } + + static int test_0_nint_and () + { + var x = (nint)0x30; + var y = (nint)0x11; + var z = x & y; + if ((int)z != 0x10) + return 1; + return 0; + } + + static int test_0_nint_or () + { + var x = (nint)0x0F; + var y = (nint)0xF0; + var z = x | y; + if ((int)z != 0xFF) + return 1; + return 0; + } + + static int test_0_nint_xor () + { + var x = (nint)0xFF; + var y = (nint)0xF0; + var z = x ^ y; + if ((int)z != 0x0F) + return 1; + return 0; + } + + static int test_0_nint_shl () + { + var x = (nint)10; + var z = x << 2; + if ((int)z != 40) + return 1; + return 0; + } + + static int test_0_nint_shr () + { + var x = (nint)10; + var z = x >> 2; + if ((int)z != 2) + return 1; + return 0; + } + + static int test_0_nint_cmp_same_val () + { + var x = (nint)10; + var y = (nint)10; + if (!(x == y)) + return 1; + if (x != y) + return 2; + if (x < y) + return 3; + if (x > y) + return 4; + if (!(x <= y)) + return 5; + if (!(x >= y)) + return 6; + return 0; + } + + static int test_0_nint_cmp_small_val () + { + var x = (nint)5; + var y = (nint)10; + if (x == y) + return 1; + if (!(x != y)) + return 2; + if (!(x < y)) + return 3; + if (x > y) + return 4; + if (!(x <= y)) + return 5; + if (x >= y) + return 6; + return 0; + } + + static int test_0_nint_cmp_large_val () + { + var x = (nint)20; + var y = (nint)10; + if (x == y) + return 1; + if (!(x != y)) + return 2; + if (x < y) + return 3; + if (!(x > y)) + return 4; + if (x <= y) + return 1; + if (!(x >= y)) + return 1; + return 0; + } + + // static int test_0_nint_call_boxed_equals () + // { + // object x = new nint (10); + // object y = new nint (10); + // if (!x.Equals (y)) + // return 1; + // return 0; + // } + + static int test_0_nint_call_boxed_funs () + { + object x = new nint (10); + object y = new nint (10); + if (x.GetHashCode () == 0) + return 2; + if (x.ToString () != "10") + return 3; + return 0; + } + + public int test_0_nint_unboxed_member_calls () + { + var x = (nint)10; +#if FALSE + if (!x.Equals (x)) + return 1; +#endif + if (x != nint.Parse ("10")) + return 2; + return 0; + } + + static int test_0_nuint_ctor () + { + var x = new nuint (10u); + var y = new nuint (x); + var z = new nuint (new nuint (20u)); + if ((uint)x != 10) + return 1; + if ((uint)y != 10) + return 2; + if ((uint)z != 20) + return 3; + return 0; + } + + static int test_0_nuint_casts () + { + var x = (nuint)10; + var y = (nuint)20L; + + if ((uint)x != 10) + return 1; + if ((ulong)x != 10L) + return 2; + if ((uint)y != 20) + return 3; + if ((ulong)y != 20L) + return 4; + return 0; + } + + static int test_0_nuint_plus () + { + var x = (nuint)10; + var z = +x; + if ((uint)z != 10) + return 1; + return 0; + } + + // static int test_0_nuint_neg () + // { + // var x = (nuint)10; + // var z = -x; + // if ((uint)z != -10) + // return 1; + // return 0; + // } + + static int test_0_nuint_comp () + { + var x = (nuint)10; + var z = ~x; + if ((uint)z != ~10u) + return 1; + return 0; + } + +#if FALSE + static int test_0_nuint_inc () + { + var x = (nuint)10; + ++x; + if ((uint)x != 11) + return 1; + return 0; + } + + static int test_0_nuint_dec () + { + var x = (nuint)10; + --x; + if ((uint)x != 9) + return 1; + return 0; + } +#endif + + static int test_0_nuint_add () + { + var x = (nuint)10; + var y = (nuint)20; + var z = x + y; + if ((uint)z != 30) + return 1; + return 0; + } + + static int test_0_nuint_sub () + { + var x = (nuint)20; + var y = (nuint)5; + var z = x - y; + if ((uint)z != 15) + return 1; + return 0; + } + + static int test_0_nuint_mul () + { + var x = (nuint)10; + var y = (nuint)20; + var z = x * y; + if ((uint)z != 200) + return 1; + return 0; + } + + static int test_0_nuint_div () + { + var x = (nuint)30; + var y = (nuint)3; + var z = x / y; + if ((uint)z != 10) + return 1; + return 0; + } + + static int test_0_nuint_rem () + { + var x = (nuint)22; + var y = (nuint)10; + var z = x % y; + if ((uint)z != 2) + return 1; + return 0; + } + + static int test_0_nuint_and () + { + var x = (nuint)0x30; + var y = (nuint)0x11; + var z = x & y; + if ((uint)z != 0x10) + return 1; + return 0; + } + + static int test_0_nuint_or () + { + var x = (nuint)0x0F; + var y = (nuint)0xF0; + var z = x | y; + if ((uint)z != 0xFF) + return 1; + return 0; + } + + static int test_0_nuint_xor () + { + var x = (nuint)0xFF; + var y = (nuint)0xF0; + var z = x ^ y; + if ((uint)z != 0x0F) + return 1; + return 0; + } + + static int test_0_nuint_shl () + { + var x = (nuint)10; + var z = x << 2; + if ((uint)z != 40) + return 1; + return 0; + } + + static int test_0_nuint_shr () + { + var x = (nuint)10; + var z = x >> 2; + if ((uint)z != 2) + return 1; + return 0; + } + + static int test_0_nuint_cmp_same_val () + { + var x = (nuint)10; + var y = (nuint)10; + if (!(x == y)) + return 1; + if (x != y) + return 2; + if (x < y) + return 3; + if (x > y) + return 4; + if (!(x <= y)) + return 5; + if (!(x >= y)) + return 6; + return 0; + } + + static int test_0_nuint_cmp_small_val () + { + var x = (nuint)5; + var y = (nuint)10; + if (x == y) + return 1; + if (!(x != y)) + return 2; + if (!(x < y)) + return 3; + if (x > y) + return 4; + if (!(x <= y)) + return 5; + if (x >= y) + return 6; + return 0; + } + + static int test_0_nuint_cmp_large_val () + { + var x = (nuint)20; + var y = (nuint)10; + if (x == y) + return 1; + if (!(x != y)) + return 2; + if (x < y) + return 3; + if (!(x > y)) + return 4; + if (x <= y) + return 1; + if (!(x >= y)) + return 1; + return 0; + } + + // static int test_0_nuint_call_boxed_equals () + // { + // object x = new nuint (10); + // object y = new nuint (10); + // if (!x.Equals (y)) + // return 1; + // return 0; + // } + + static int test_0_nuint_call_boxed_funs () + { + object x = new nuint (10u); + object y = new nuint (10u); + if (x.GetHashCode () == 0) + return 2; + if (x.ToString () != "10") + return 3; + return 0; + } + + public int test_0_nuint_unboxed_member_calls () + { + var x = (nuint)10; +#if FALSE + if (!x.Equals (x)) + return 1; +#endif + if (x != nuint.Parse ("10")) + return 2; + return 0; + } + + static int test_0_nfloat_ctor () + { + var x = new nfloat (10.0f); + var y = new nfloat (x); + var z = new nfloat (new nfloat (20f)); + if ((float)x != 10f) + return 1; + if ((float)y != 10f) + return 2; + if ((float)z != 20f) + return 3; + return 0; + } + + static int test_0_nfloat_casts () + { + var x = (nfloat)10f; + + if ((float)x != 10f) + return 1; + if ((double)x != 10) + return 2; +#if FALSE + var y = (nfloat)20; + if ((float)y != 20f) + return 3; + if ((double)y != 20) + return 4; +#endif + return 0; + } + + static int test_0_nfloat_plus () + { + var x = (nfloat)10f; + var z = +x; + if ((float)z != 10f) + return 1; + return 0; + } + + static int test_0_nfloat_neg () + { + var x = (nfloat)10f; + var z = -x; + if ((float)z != -10f) + return 1; + return 0; + } + +#if FALSE + static int test_0_nfloat_inc () + { + var x = (nfloat)10f; + ++x; + if ((float)x != 11f) { + Console.WriteLine ((float)x); + return 1; + } + return 0; + } + + static int test_0_nfloat_dec () + { + var x = (nfloat)10f; + --x; + if ((float)x != 9f) { + Console.WriteLine ((float)x); + return 1; + } + return 0; + } +#endif + + static int test_0_nfloat_add () + { + var x = (nfloat)10f; + var y = (nfloat)20f; + var z = x + y; + if ((float)z != 30f) + return 1; + return 0; + } + + static int test_0_nfloat_sub () + { + var x = (nfloat)10f; + var y = (nfloat)20f; + var z = x - y; + if ((float)z != -10f) + return 1; + return 0; + } + + static int test_0_nfloat_mul () + { + var x = (nfloat)10f; + var y = (nfloat)20f; + var z = x * y; + if ((float)z != 200f) + return 1; + return 0; + } + + static int test_0_nfloat_div () + { + var x = (nfloat)30f; + var y = (nfloat)3f; + var z = x / y; + if ((float)z != 10f) + return 1; + return 0; + } + + static int test_0_nfloat_rem () + { + var x = (nfloat)22f; + var y = (nfloat)10f; + var z = x % y; + if ((float)z != 2f) + return 1; + return 0; + } + + static int test_0_nfloat_cmp_same_val () + { + var x = (nfloat)10f; + var y = (nfloat)10f; + if (!(x == y)) + return 1; + if (x != y) + return 2; + if (x < y) + return 3; + if (x > y) + return 4; + if (!(x <= y)) + return 5; + if (!(x >= y)) + return 6; + return 0; + } + + static int test_0_nfloat_cmp_small_val () + { + var x = (nfloat)5f; + var y = (nfloat)10f; + if (x == y) + return 1; + if (!(x != y)) + return 2; + if (!(x < y)) + return 3; + if (x > y) + return 4; + if (!(x <= y)) + return 5; + if (x >= y) + return 6; + return 0; + } + + static int test_0_nfloat_cmp_large_val () + { + var x = (nfloat)20f; + var y = (nfloat)10f; + if (x == y) + return 1; + if (!(x != y)) + return 2; + if (x < y) + return 3; + if (!(x > y)) + return 4; + if (x <= y) + return 1; + if (!(x >= y)) + return 1; + return 0; + } + + /* fails on arm64 */ +#if FALSE + static int test_0_nfloat_cmp_left_nan () + { + var x = (nfloat)float.NaN; + var y = (nfloat)10f; + if (x == y) + return 1; + if (!(x != y)) + return 2; + if (x < y) + return 3; + if (x > y) + return 4; + if (x <= y) + return 1; + if (x >= y) + return 1; + return 0; + } + + + static int test_0_nfloat_cmp_right_nan () + { + var x = (nfloat)10f; + var y = (nfloat)float.NaN; + if (x == y) + return 1; + if (!(x != y)) + return 2; + if (x < y) + return 3; + if (x > y) + return 4; + if (x <= y) + return 1; + if (x >= y) + return 1; + return 0; + } +#endif + + // static int test_0_nfloat_call_boxed_equals () + // { + // object x = new nfloat (10f); + // object y = new nfloat (10f); + // if (!x.Equals (y)) + // return 1; + // return 0; + // } + + static int test_0_nfloat_call_boxed_funs () + { + object x = new nfloat (10f); + object y = new nfloat (10f); + if (x.GetHashCode () == 0) + return 2; + if (x.ToString () != "10") + return 3; + return 0; + } + + public int test_0_nfloat_unboxed_member_calls () + { + var x = (nfloat)10f; +#if FALSE + if (!x.Equals (x)) + return 1; +#endif + if (x != nfloat.Parse ("10")) + return 2; + return 0; + } + +#if !__MOBILE__ + public static int Main (String[] args) { + return TestDriver.RunTests (typeof (BuiltinTests), args); + } +#endif +} + + +// !!! WARNING - GENERATED CODE - DO NOT EDIT !!! +// +// Generated by NativeTypes.tt, a T4 template. +// +// NativeTypes.cs: basic types with 32 or 64 bit sizes: +// +// - nint +// - nuint +// - nfloat +// +// Authors: +// Aaron Bockover +// +// Copyright 2013 Xamarin, Inc. All rights reserved. +// + +namespace System +{ + [Serializable] + [DebuggerDisplay ("{v,nq}")] + public unsafe struct nint : IFormattable, IConvertible, IComparable, IComparable, IEquatable + { + internal nint (nint v) { this.v = v.v; } + public nint (Int32 v) { this.v = v; } + +#if ARCH_32 + public static readonly int Size = 4; + + public static readonly nint MaxValue = Int32.MaxValue; + public static readonly nint MinValue = Int32.MinValue; + + [DebuggerBrowsable (DebuggerBrowsableState.Never)] + internal Int32 v; + + public nint (Int64 v) { this.v = (Int32)v; } +#else + public static readonly int Size = 8; + + public static readonly nint MaxValue = (nint) Int64.MaxValue; // 64-bit only codepath + public static readonly nint MinValue = (nint) Int64.MinValue; // 64-bit only codepath + + [DebuggerBrowsable (DebuggerBrowsableState.Never)] + internal Int64 v; + + public nint (Int64 v) { this.v = v; } +#endif + + public static explicit operator nint (nuint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nint ((int)v.v); +#else + return new nint ((long)v.v); +#endif + } + + public static explicit operator nuint (nint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nuint ((uint)v.v); +#else + return new nuint ((ulong)v.v); +#endif + } + + public static explicit operator nint (nfloat v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nint ((int)v.v); +#else + return new nint ((long)v.v); +#endif + } + + public static implicit operator nfloat (nint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nfloat ((float)v.v); +#else + return new nfloat ((double)v.v); +#endif + } + + public static explicit operator nint (IntPtr v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nint (*((int *)&v)); +#else + return new nint (*((long *)&v)); +#endif + } + + public static explicit operator IntPtr (nint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return *((IntPtr *)&v.v); +#else + return *((IntPtr *)&v.v); +#endif + } + + public static implicit operator nint (sbyte v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nint ((int)v); +#else + return new nint ((long)v); +#endif + } + + public static explicit operator sbyte (nint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (sbyte)v.v; +#else + return (sbyte)v.v; +#endif + } + + public static implicit operator nint (byte v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nint ((int)v); +#else + return new nint ((long)v); +#endif + } + + public static explicit operator byte (nint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (byte)v.v; +#else + return (byte)v.v; +#endif + } + + public static implicit operator nint (char v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nint ((int)v); +#else + return new nint ((long)v); +#endif + } + + public static explicit operator char (nint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (char)v.v; +#else + return (char)v.v; +#endif + } + + public static implicit operator nint (short v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nint ((int)v); +#else + return new nint ((long)v); +#endif + } + + public static explicit operator short (nint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (short)v.v; +#else + return (short)v.v; +#endif + } + + public static explicit operator nint (ushort v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nint ((int)v); +#else + return new nint ((long)v); +#endif + } + + public static explicit operator ushort (nint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (ushort)v.v; +#else + return (ushort)v.v; +#endif + } + + public static implicit operator nint (int v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nint ((int)v); +#else + return new nint ((long)v); +#endif + } + + public static explicit operator int (nint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (int)v.v; +#else + return (int)v.v; +#endif + } + + public static explicit operator nint (uint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nint ((int)v); +#else + return new nint ((long)v); +#endif + } + + public static explicit operator uint (nint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (uint)v.v; +#else + return (uint)v.v; +#endif + } + + public static explicit operator nint (long v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nint ((int)v); +#else + return new nint ((long)v); +#endif + } + + public static implicit operator long (nint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (long)v.v; +#else + return (long)v.v; +#endif + } + + public static explicit operator nint (ulong v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nint ((int)v); +#else + return new nint ((long)v); +#endif + } + + public static explicit operator ulong (nint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (ulong)v.v; +#else + return (ulong)v.v; +#endif + } + + public static explicit operator nint (float v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nint ((int)v); +#else + return new nint ((long)v); +#endif + } + + public static implicit operator float (nint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (float)v.v; +#else + return (float)v.v; +#endif + } + + public static explicit operator nint (double v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nint ((int)v); +#else + return new nint ((long)v); +#endif + } + + public static implicit operator double (nint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (double)v.v; +#else + return (double)v.v; +#endif + } + + public static explicit operator nint (decimal v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nint ((int)v); +#else + return new nint ((long)v); +#endif + } + + public static implicit operator decimal (nint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (decimal)v.v; +#else + return (decimal)v.v; +#endif + } + +#if NINT_JIT_OPTIMIZED + public static nint operator + (nint v) { throw new NotImplementedException (); } + public static nint operator - (nint v) { throw new NotImplementedException (); } + public static nint operator ~ (nint v) { throw new NotImplementedException (); } +#else + public static nint operator + (nint v) { return new nint (+v.v); } + public static nint operator - (nint v) { return new nint (-v.v); } + public static nint operator ~ (nint v) { return new nint (~v.v); } +#endif + +#if NINT_JIT_OPTIMIZED + public static nint operator ++ (nint v) { throw new NotImplementedException (); } + public static nint operator -- (nint v) { throw new NotImplementedException (); } +#else + public static nint operator ++ (nint v) { return new nint (v.v + 1); } + public static nint operator -- (nint v) { return new nint (v.v - 1); } +#endif + +#if NINT_JIT_OPTIMIZED + public static nint operator + (nint l, nint r) { throw new NotImplementedException (); } + public static nint operator - (nint l, nint r) { throw new NotImplementedException (); } + public static nint operator * (nint l, nint r) { throw new NotImplementedException (); } + public static nint operator / (nint l, nint r) { throw new NotImplementedException (); } + public static nint operator % (nint l, nint r) { throw new NotImplementedException (); } + public static nint operator & (nint l, nint r) { throw new NotImplementedException (); } + public static nint operator | (nint l, nint r) { throw new NotImplementedException (); } + public static nint operator ^ (nint l, nint r) { throw new NotImplementedException (); } + + public static nint operator << (nint l, int r) { throw new NotImplementedException (); } + public static nint operator >> (nint l, int r) { throw new NotImplementedException (); } +#else + public static nint operator + (nint l, nint r) { return new nint (l.v + r.v); } + public static nint operator - (nint l, nint r) { return new nint (l.v - r.v); } + public static nint operator * (nint l, nint r) { return new nint (l.v * r.v); } + public static nint operator / (nint l, nint r) { return new nint (l.v / r.v); } + public static nint operator % (nint l, nint r) { return new nint (l.v % r.v); } + public static nint operator & (nint l, nint r) { return new nint (l.v & r.v); } + public static nint operator | (nint l, nint r) { return new nint (l.v | r.v); } + public static nint operator ^ (nint l, nint r) { return new nint (l.v ^ r.v); } + + public static nint operator << (nint l, int r) { return new nint (l.v << r); } + public static nint operator >> (nint l, int r) { return new nint (l.v >> r); } +#endif + +#if NINT_JIT_OPTIMIZED + public static bool operator == (nint l, nint r) { throw new NotImplementedException (); } + public static bool operator != (nint l, nint r) { throw new NotImplementedException (); } + public static bool operator < (nint l, nint r) { throw new NotImplementedException (); } + public static bool operator > (nint l, nint r) { throw new NotImplementedException (); } + public static bool operator <= (nint l, nint r) { throw new NotImplementedException (); } + public static bool operator >= (nint l, nint r) { throw new NotImplementedException (); } +#else + public static bool operator == (nint l, nint r) { return l.v == r.v; } + public static bool operator != (nint l, nint r) { return l.v != r.v; } + public static bool operator < (nint l, nint r) { return l.v < r.v; } + public static bool operator > (nint l, nint r) { return l.v > r.v; } + public static bool operator <= (nint l, nint r) { return l.v <= r.v; } + public static bool operator >= (nint l, nint r) { return l.v >= r.v; } +#endif + + public int CompareTo (nint value) { return v.CompareTo (value.v); } + public int CompareTo (object value) + { + if (value is nint) + return v.CompareTo (((nint) value).v); + return v.CompareTo (value); + } + public bool Equals (nint obj) { return v.Equals (obj.v); } + public override bool Equals (object obj) + { + if (obj is nint) + return v.Equals (((nint) obj).v); + return v.Equals (obj); + } + public override int GetHashCode () { return v.GetHashCode (); } + +#if ARCH_32 + public static nint Parse (string s, IFormatProvider provider) { return (nint)Int32.Parse (s, provider); } + public static nint Parse (string s, NumberStyles style) { return (nint)Int32.Parse (s, style); } + public static nint Parse (string s) { return (nint)Int32.Parse (s); } + public static nint Parse (string s, NumberStyles style, IFormatProvider provider) { + return (nint)Int32.Parse (s, style, provider); + } + + public static bool TryParse (string s, out nint result) + { + Int32 v; + var r = Int32.TryParse (s, out v); + result = (nint)v; + return r; + } + + public static bool TryParse (string s, NumberStyles style, IFormatProvider provider, out nint result) + { + Int32 v; + var r = Int32.TryParse (s, style, provider, out v); + result = (nint)v; + return r; + } +#else + public static nint Parse (string s, IFormatProvider provider) { return (nint)Int64.Parse (s, provider); } + public static nint Parse (string s, NumberStyles style) { return (nint)Int64.Parse (s, style); } + public static nint Parse (string s) { return (nint)Int64.Parse (s); } + public static nint Parse (string s, NumberStyles style, IFormatProvider provider) { + return (nint)Int64.Parse (s, style, provider); + } + + public static bool TryParse (string s, out nint result) + { + Int64 v; + var r = Int64.TryParse (s, out v); + result = (nint)v; + return r; + } + + public static bool TryParse (string s, NumberStyles style, IFormatProvider provider, out nint result) + { + Int64 v; + var r = Int64.TryParse (s, style, provider, out v); + result = (nint)v; + return r; + } +#endif + + public override string ToString () { return v.ToString (); } + public string ToString (IFormatProvider provider) { return v.ToString (provider); } + public string ToString (string format) { return v.ToString (format); } + public string ToString (string format, IFormatProvider provider) { return v.ToString (format, provider); } + + public TypeCode GetTypeCode () { return v.GetTypeCode (); } + + bool IConvertible.ToBoolean (IFormatProvider provider) { return ((IConvertible)v).ToBoolean (provider); } + byte IConvertible.ToByte (IFormatProvider provider) { return ((IConvertible)v).ToByte (provider); } + char IConvertible.ToChar (IFormatProvider provider) { return ((IConvertible)v).ToChar (provider); } + DateTime IConvertible.ToDateTime (IFormatProvider provider) { return ((IConvertible)v).ToDateTime (provider); } + decimal IConvertible.ToDecimal (IFormatProvider provider) { return ((IConvertible)v).ToDecimal (provider); } + double IConvertible.ToDouble (IFormatProvider provider) { return ((IConvertible)v).ToDouble (provider); } + short IConvertible.ToInt16 (IFormatProvider provider) { return ((IConvertible)v).ToInt16 (provider); } + int IConvertible.ToInt32 (IFormatProvider provider) { return ((IConvertible)v).ToInt32 (provider); } + long IConvertible.ToInt64 (IFormatProvider provider) { return ((IConvertible)v).ToInt64 (provider); } + sbyte IConvertible.ToSByte (IFormatProvider provider) { return ((IConvertible)v).ToSByte (provider); } + float IConvertible.ToSingle (IFormatProvider provider) { return ((IConvertible)v).ToSingle (provider); } + ushort IConvertible.ToUInt16 (IFormatProvider provider) { return ((IConvertible)v).ToUInt16 (provider); } + uint IConvertible.ToUInt32 (IFormatProvider provider) { return ((IConvertible)v).ToUInt32 (provider); } + ulong IConvertible.ToUInt64 (IFormatProvider provider) { return ((IConvertible)v).ToUInt64 (provider); } + + object IConvertible.ToType (Type targetType, IFormatProvider provider) { + return ((IConvertible)v).ToType (targetType, provider); + } + + public static void CopyArray (IntPtr source, nint [] destination, int startIndex, int length) + { + if (source == IntPtr.Zero) + throw new ArgumentNullException ("source"); + if (destination == null) + throw new ArgumentNullException ("destination"); + if (destination.Rank != 1) + throw new ArgumentException ("destination", "array is multi-dimensional"); + if (startIndex < 0) + throw new ArgumentException ("startIndex", "must be >= 0"); + if (length < 0) + throw new ArgumentException ("length", "must be >= 0"); + if (startIndex + length > destination.Length) + throw new ArgumentException ("length", "startIndex + length > destination.Length"); + + for (int i = 0; i < length; i++) + destination [i + startIndex] = (nint)Marshal.ReadIntPtr (source, i * nint.Size); + } + + public static void CopyArray (nint [] source, int startIndex, IntPtr destination, int length) + { + if (source == null) + throw new ArgumentNullException ("source"); + if (destination == IntPtr.Zero) + throw new ArgumentNullException ("destination"); + if (source.Rank != 1) + throw new ArgumentException ("source", "array is multi-dimensional"); + if (startIndex < 0) + throw new ArgumentException ("startIndex", "must be >= 0"); + if (length < 0) + throw new ArgumentException ("length", "must be >= 0"); + if (startIndex + length > source.Length) + throw new ArgumentException ("length", "startIndex + length > source.Length"); + + for (int i = 0; i < length; i++) + Marshal.WriteIntPtr (destination, i * nint.Size, (IntPtr)source [i + startIndex]); + } + } + [Serializable] + [DebuggerDisplay ("{v,nq}")] + public unsafe struct nuint : IFormattable, IConvertible, IComparable, IComparable, IEquatable + { + internal nuint (nuint v) { this.v = v.v; } + public nuint (UInt32 v) { this.v = v; } + +#if ARCH_32 + public static readonly int Size = 4; + + public static readonly nuint MaxValue = UInt32.MaxValue; + public static readonly nuint MinValue = UInt32.MinValue; + + [DebuggerBrowsable (DebuggerBrowsableState.Never)] + internal UInt32 v; + + public nuint (UInt64 v) { this.v = (UInt32)v; } +#else + public static readonly int Size = 8; + + public static readonly nuint MaxValue = (nuint) UInt64.MaxValue; // 64-bit only codepath + public static readonly nuint MinValue = (nuint) UInt64.MinValue; // 64-bit only codepath + + [DebuggerBrowsable (DebuggerBrowsableState.Never)] + internal UInt64 v; + + public nuint (UInt64 v) { this.v = v; } +#endif + + public static explicit operator nuint (nfloat v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nuint ((uint)v.v); +#else + return new nuint ((ulong)v.v); +#endif + } + + public static implicit operator nfloat (nuint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nfloat ((float)v.v); +#else + return new nfloat ((double)v.v); +#endif + } + + public static explicit operator nuint (IntPtr v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nuint (*((uint *)&v)); +#else + return new nuint (*((ulong *)&v)); +#endif + } + + public static explicit operator IntPtr (nuint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return *((IntPtr *)&v.v); +#else + return *((IntPtr *)&v.v); +#endif + } + + public static explicit operator nuint (sbyte v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nuint ((uint)v); +#else + return new nuint ((ulong)v); +#endif + } + + public static explicit operator sbyte (nuint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (sbyte)v.v; +#else + return (sbyte)v.v; +#endif + } + + public static implicit operator nuint (byte v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nuint ((uint)v); +#else + return new nuint ((ulong)v); +#endif + } + + public static explicit operator byte (nuint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (byte)v.v; +#else + return (byte)v.v; +#endif + } + + public static implicit operator nuint (char v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nuint ((uint)v); +#else + return new nuint ((ulong)v); +#endif + } + + public static explicit operator char (nuint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (char)v.v; +#else + return (char)v.v; +#endif + } + + public static explicit operator nuint (short v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nuint ((uint)v); +#else + return new nuint ((ulong)v); +#endif + } + + public static explicit operator short (nuint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (short)v.v; +#else + return (short)v.v; +#endif + } + + public static implicit operator nuint (ushort v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nuint ((uint)v); +#else + return new nuint ((ulong)v); +#endif + } + + public static explicit operator ushort (nuint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (ushort)v.v; +#else + return (ushort)v.v; +#endif + } + + public static explicit operator nuint (int v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nuint ((uint)v); +#else + return new nuint ((ulong)v); +#endif + } + + public static explicit operator int (nuint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (int)v.v; +#else + return (int)v.v; +#endif + } + + public static implicit operator nuint (uint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nuint ((uint)v); +#else + return new nuint ((ulong)v); +#endif + } + + public static explicit operator uint (nuint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (uint)v.v; +#else + return (uint)v.v; +#endif + } + + public static explicit operator nuint (long v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nuint ((uint)v); +#else + return new nuint ((ulong)v); +#endif + } + + public static explicit operator long (nuint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (long)v.v; +#else + return (long)v.v; +#endif + } + + public static explicit operator nuint (ulong v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nuint ((uint)v); +#else + return new nuint ((ulong)v); +#endif + } + + public static implicit operator ulong (nuint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (ulong)v.v; +#else + return (ulong)v.v; +#endif + } + + public static explicit operator nuint (float v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nuint ((uint)v); +#else + return new nuint ((ulong)v); +#endif + } + + public static implicit operator float (nuint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (float)v.v; +#else + return (float)v.v; +#endif + } + + public static explicit operator nuint (double v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nuint ((uint)v); +#else + return new nuint ((ulong)v); +#endif + } + + public static implicit operator double (nuint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (double)v.v; +#else + return (double)v.v; +#endif + } + + public static explicit operator nuint (decimal v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nuint ((uint)v); +#else + return new nuint ((ulong)v); +#endif + } + + public static implicit operator decimal (nuint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (decimal)v.v; +#else + return (decimal)v.v; +#endif + } + +#if NINT_JIT_OPTIMIZED + public static nuint operator + (nuint v) { throw new NotImplementedException (); } + public static nuint operator ~ (nuint v) { throw new NotImplementedException (); } +#else + public static nuint operator + (nuint v) { return new nuint (+v.v); } + public static nuint operator ~ (nuint v) { return new nuint (~v.v); } +#endif + +#if NINT_JIT_OPTIMIZED + public static nuint operator ++ (nuint v) { throw new NotImplementedException (); } + public static nuint operator -- (nuint v) { throw new NotImplementedException (); } +#else + public static nuint operator ++ (nuint v) { return new nuint (v.v + 1); } + public static nuint operator -- (nuint v) { return new nuint (v.v - 1); } +#endif + +#if NINT_JIT_OPTIMIZED + public static nuint operator + (nuint l, nuint r) { throw new NotImplementedException (); } + public static nuint operator - (nuint l, nuint r) { throw new NotImplementedException (); } + public static nuint operator * (nuint l, nuint r) { throw new NotImplementedException (); } + public static nuint operator / (nuint l, nuint r) { throw new NotImplementedException (); } + public static nuint operator % (nuint l, nuint r) { throw new NotImplementedException (); } + public static nuint operator & (nuint l, nuint r) { throw new NotImplementedException (); } + public static nuint operator | (nuint l, nuint r) { throw new NotImplementedException (); } + public static nuint operator ^ (nuint l, nuint r) { throw new NotImplementedException (); } + + public static nuint operator << (nuint l, int r) { throw new NotImplementedException (); } + public static nuint operator >> (nuint l, int r) { throw new NotImplementedException (); } +#else + public static nuint operator + (nuint l, nuint r) { return new nuint (l.v + r.v); } + public static nuint operator - (nuint l, nuint r) { return new nuint (l.v - r.v); } + public static nuint operator * (nuint l, nuint r) { return new nuint (l.v * r.v); } + public static nuint operator / (nuint l, nuint r) { return new nuint (l.v / r.v); } + public static nuint operator % (nuint l, nuint r) { return new nuint (l.v % r.v); } + public static nuint operator & (nuint l, nuint r) { return new nuint (l.v & r.v); } + public static nuint operator | (nuint l, nuint r) { return new nuint (l.v | r.v); } + public static nuint operator ^ (nuint l, nuint r) { return new nuint (l.v ^ r.v); } + + public static nuint operator << (nuint l, int r) { return new nuint (l.v << r); } + public static nuint operator >> (nuint l, int r) { return new nuint (l.v >> r); } +#endif + +#if NINT_JIT_OPTIMIZED + public static bool operator == (nuint l, nuint r) { throw new NotImplementedException (); } + public static bool operator != (nuint l, nuint r) { throw new NotImplementedException (); } + public static bool operator < (nuint l, nuint r) { throw new NotImplementedException (); } + public static bool operator > (nuint l, nuint r) { throw new NotImplementedException (); } + public static bool operator <= (nuint l, nuint r) { throw new NotImplementedException (); } + public static bool operator >= (nuint l, nuint r) { throw new NotImplementedException (); } +#else + public static bool operator == (nuint l, nuint r) { return l.v == r.v; } + public static bool operator != (nuint l, nuint r) { return l.v != r.v; } + public static bool operator < (nuint l, nuint r) { return l.v < r.v; } + public static bool operator > (nuint l, nuint r) { return l.v > r.v; } + public static bool operator <= (nuint l, nuint r) { return l.v <= r.v; } + public static bool operator >= (nuint l, nuint r) { return l.v >= r.v; } +#endif + + public int CompareTo (nuint value) { return v.CompareTo (value.v); } + public int CompareTo (object value) + { + if (value is nuint) + return v.CompareTo (((nuint) value).v); + return v.CompareTo (value); + } + public bool Equals (nuint obj) { return v.Equals (obj.v); } + public override bool Equals (object obj) + { + if (obj is nuint) + return v.Equals (((nuint) obj).v); + return v.Equals (obj); + } + public override int GetHashCode () { return v.GetHashCode (); } + +#if ARCH_32 + public static nuint Parse (string s, IFormatProvider provider) { return (nuint)UInt32.Parse (s, provider); } + public static nuint Parse (string s, NumberStyles style) { return (nuint)UInt32.Parse (s, style); } + public static nuint Parse (string s) { return (nuint)UInt32.Parse (s); } + public static nuint Parse (string s, NumberStyles style, IFormatProvider provider) { + return (nuint)UInt32.Parse (s, style, provider); + } + + public static bool TryParse (string s, out nuint result) + { + UInt32 v; + var r = UInt32.TryParse (s, out v); + result = (nuint)v; + return r; + } + + public static bool TryParse (string s, NumberStyles style, IFormatProvider provider, out nuint result) + { + UInt32 v; + var r = UInt32.TryParse (s, style, provider, out v); + result = (nuint)v; + return r; + } +#else + public static nuint Parse (string s, IFormatProvider provider) { return (nuint)UInt64.Parse (s, provider); } + public static nuint Parse (string s, NumberStyles style) { return (nuint)UInt64.Parse (s, style); } + public static nuint Parse (string s) { return (nuint)UInt64.Parse (s); } + public static nuint Parse (string s, NumberStyles style, IFormatProvider provider) { + return (nuint)UInt64.Parse (s, style, provider); + } + + public static bool TryParse (string s, out nuint result) + { + UInt64 v; + var r = UInt64.TryParse (s, out v); + result = (nuint)v; + return r; + } + + public static bool TryParse (string s, NumberStyles style, IFormatProvider provider, out nuint result) + { + UInt64 v; + var r = UInt64.TryParse (s, style, provider, out v); + result = (nuint)v; + return r; + } +#endif + + public override string ToString () { return v.ToString (); } + public string ToString (IFormatProvider provider) { return v.ToString (provider); } + public string ToString (string format) { return v.ToString (format); } + public string ToString (string format, IFormatProvider provider) { return v.ToString (format, provider); } + + public TypeCode GetTypeCode () { return v.GetTypeCode (); } + + bool IConvertible.ToBoolean (IFormatProvider provider) { return ((IConvertible)v).ToBoolean (provider); } + byte IConvertible.ToByte (IFormatProvider provider) { return ((IConvertible)v).ToByte (provider); } + char IConvertible.ToChar (IFormatProvider provider) { return ((IConvertible)v).ToChar (provider); } + DateTime IConvertible.ToDateTime (IFormatProvider provider) { return ((IConvertible)v).ToDateTime (provider); } + decimal IConvertible.ToDecimal (IFormatProvider provider) { return ((IConvertible)v).ToDecimal (provider); } + double IConvertible.ToDouble (IFormatProvider provider) { return ((IConvertible)v).ToDouble (provider); } + short IConvertible.ToInt16 (IFormatProvider provider) { return ((IConvertible)v).ToInt16 (provider); } + int IConvertible.ToInt32 (IFormatProvider provider) { return ((IConvertible)v).ToInt32 (provider); } + long IConvertible.ToInt64 (IFormatProvider provider) { return ((IConvertible)v).ToInt64 (provider); } + sbyte IConvertible.ToSByte (IFormatProvider provider) { return ((IConvertible)v).ToSByte (provider); } + float IConvertible.ToSingle (IFormatProvider provider) { return ((IConvertible)v).ToSingle (provider); } + ushort IConvertible.ToUInt16 (IFormatProvider provider) { return ((IConvertible)v).ToUInt16 (provider); } + uint IConvertible.ToUInt32 (IFormatProvider provider) { return ((IConvertible)v).ToUInt32 (provider); } + ulong IConvertible.ToUInt64 (IFormatProvider provider) { return ((IConvertible)v).ToUInt64 (provider); } + + object IConvertible.ToType (Type targetType, IFormatProvider provider) { + return ((IConvertible)v).ToType (targetType, provider); + } + + public static void CopyArray (IntPtr source, nuint [] destination, int startIndex, int length) + { + if (source == IntPtr.Zero) + throw new ArgumentNullException ("source"); + if (destination == null) + throw new ArgumentNullException ("destination"); + if (destination.Rank != 1) + throw new ArgumentException ("destination", "array is multi-dimensional"); + if (startIndex < 0) + throw new ArgumentException ("startIndex", "must be >= 0"); + if (length < 0) + throw new ArgumentException ("length", "must be >= 0"); + if (startIndex + length > destination.Length) + throw new ArgumentException ("length", "startIndex + length > destination.Length"); + + for (int i = 0; i < length; i++) + destination [i + startIndex] = (nuint)Marshal.ReadIntPtr (source, i * nuint.Size); + } + + public static void CopyArray (nuint [] source, int startIndex, IntPtr destination, int length) + { + if (source == null) + throw new ArgumentNullException ("source"); + if (destination == IntPtr.Zero) + throw new ArgumentNullException ("destination"); + if (source.Rank != 1) + throw new ArgumentException ("source", "array is multi-dimensional"); + if (startIndex < 0) + throw new ArgumentException ("startIndex", "must be >= 0"); + if (length < 0) + throw new ArgumentException ("length", "must be >= 0"); + if (startIndex + length > source.Length) + throw new ArgumentException ("length", "startIndex + length > source.Length"); + + for (int i = 0; i < length; i++) + Marshal.WriteIntPtr (destination, i * nuint.Size, (IntPtr)source [i + startIndex]); + } + } + [Serializable] + [DebuggerDisplay ("{v,nq}")] + public unsafe struct nfloat : IFormattable, IConvertible, IComparable, IComparable, IEquatable + { + internal nfloat (nfloat v) { this.v = v.v; } + public nfloat (Single v) { this.v = v; } + +#if ARCH_32 + public static readonly int Size = 4; + + public static readonly nfloat MaxValue = Single.MaxValue; + public static readonly nfloat MinValue = Single.MinValue; + public static readonly nfloat Epsilon = (nfloat)Single.Epsilon; + public static readonly nfloat NaN = (nfloat)Single.NaN; + public static readonly nfloat NegativeInfinity = (nfloat)Single.NegativeInfinity; + public static readonly nfloat PositiveInfinity = (nfloat)Single.PositiveInfinity; + + [DebuggerBrowsable (DebuggerBrowsableState.Never)] + internal Single v; + + public nfloat (Double v) { this.v = (Single)v; } +#else + public static readonly int Size = 8; + + public static readonly nfloat MaxValue = (nfloat) Double.MaxValue; // 64-bit only codepath + public static readonly nfloat MinValue = (nfloat) Double.MinValue; // 64-bit only codepath + public static readonly nfloat Epsilon = (nfloat)Double.Epsilon; + public static readonly nfloat NaN = (nfloat)Double.NaN; + public static readonly nfloat NegativeInfinity = (nfloat)Double.NegativeInfinity; + public static readonly nfloat PositiveInfinity = (nfloat)Double.PositiveInfinity; + + [DebuggerBrowsable (DebuggerBrowsableState.Never)] + internal Double v; + + public nfloat (Double v) { this.v = v; } +#endif + + public static explicit operator nfloat (IntPtr v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nfloat (*((float *)&v)); +#else + return new nfloat (*((double *)&v)); +#endif + } + + public static explicit operator IntPtr (nfloat v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return *((IntPtr *)&v.v); +#else + return *((IntPtr *)&v.v); +#endif + } + + public static implicit operator nfloat (sbyte v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nfloat ((float)v); +#else + return new nfloat ((double)v); +#endif + } + + public static explicit operator sbyte (nfloat v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (sbyte)v.v; +#else + return (sbyte)v.v; +#endif + } + + public static implicit operator nfloat (byte v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nfloat ((float)v); +#else + return new nfloat ((double)v); +#endif + } + + public static explicit operator byte (nfloat v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (byte)v.v; +#else + return (byte)v.v; +#endif + } + + public static implicit operator nfloat (char v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nfloat ((float)v); +#else + return new nfloat ((double)v); +#endif + } + + public static explicit operator char (nfloat v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (char)v.v; +#else + return (char)v.v; +#endif + } + + public static implicit operator nfloat (short v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nfloat ((float)v); +#else + return new nfloat ((double)v); +#endif + } + + public static explicit operator short (nfloat v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (short)v.v; +#else + return (short)v.v; +#endif + } + + public static implicit operator nfloat (ushort v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nfloat ((float)v); +#else + return new nfloat ((double)v); +#endif + } + + public static explicit operator ushort (nfloat v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (ushort)v.v; +#else + return (ushort)v.v; +#endif + } + + public static implicit operator nfloat (int v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nfloat ((float)v); +#else + return new nfloat ((double)v); +#endif + } + + public static explicit operator int (nfloat v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (int)v.v; +#else + return (int)v.v; +#endif + } + + public static implicit operator nfloat (uint v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nfloat ((float)v); +#else + return new nfloat ((double)v); +#endif + } + + public static explicit operator uint (nfloat v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (uint)v.v; +#else + return (uint)v.v; +#endif + } + + public static implicit operator nfloat (long v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nfloat ((float)v); +#else + return new nfloat ((double)v); +#endif + } + + public static explicit operator long (nfloat v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (long)v.v; +#else + return (long)v.v; +#endif + } + + public static implicit operator nfloat (ulong v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nfloat ((float)v); +#else + return new nfloat ((double)v); +#endif + } + + public static explicit operator ulong (nfloat v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (ulong)v.v; +#else + return (ulong)v.v; +#endif + } + + public static implicit operator nfloat (float v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nfloat ((float)v); +#else + return new nfloat ((double)v); +#endif + } + + public static explicit operator float (nfloat v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (float)v.v; +#else + return (float)v.v; +#endif + } + + public static explicit operator nfloat (double v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nfloat ((float)v); +#else + return new nfloat ((double)v); +#endif + } + + public static implicit operator double (nfloat v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (double)v.v; +#else + return (double)v.v; +#endif + } + + public static explicit operator nfloat (decimal v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return new nfloat ((float)v); +#else + return new nfloat ((double)v); +#endif + } + + public static explicit operator decimal (nfloat v) + { +#if NINT_JIT_OPTIMIZED + throw new NotImplementedException (); +#elif ARCH_32 + return (decimal)v.v; +#else + return (decimal)v.v; +#endif + } + +#if NINT_JIT_OPTIMIZED + public static nfloat operator + (nfloat v) { throw new NotImplementedException (); } + public static nfloat operator - (nfloat v) { throw new NotImplementedException (); } +#else + public static nfloat operator + (nfloat v) { return new nfloat (+v.v); } + public static nfloat operator - (nfloat v) { return new nfloat (-v.v); } +#endif + +#if NINT_JIT_OPTIMIZED + public static nfloat operator ++ (nfloat v) { throw new NotImplementedException (); } + public static nfloat operator -- (nfloat v) { throw new NotImplementedException (); } +#else + public static nfloat operator ++ (nfloat v) { return new nfloat (v.v + 1); } + public static nfloat operator -- (nfloat v) { return new nfloat (v.v - 1); } +#endif + +#if NINT_JIT_OPTIMIZED + public static nfloat operator + (nfloat l, nfloat r) { throw new NotImplementedException (); } + public static nfloat operator - (nfloat l, nfloat r) { throw new NotImplementedException (); } + public static nfloat operator * (nfloat l, nfloat r) { throw new NotImplementedException (); } + public static nfloat operator / (nfloat l, nfloat r) { throw new NotImplementedException (); } + public static nfloat operator % (nfloat l, nfloat r) { throw new NotImplementedException (); } +#else + public static nfloat operator + (nfloat l, nfloat r) { return new nfloat (l.v + r.v); } + public static nfloat operator - (nfloat l, nfloat r) { return new nfloat (l.v - r.v); } + public static nfloat operator * (nfloat l, nfloat r) { return new nfloat (l.v * r.v); } + public static nfloat operator / (nfloat l, nfloat r) { return new nfloat (l.v / r.v); } + public static nfloat operator % (nfloat l, nfloat r) { return new nfloat (l.v % r.v); } +#endif + +#if NINT_JIT_OPTIMIZED + public static bool operator == (nfloat l, nfloat r) { throw new NotImplementedException (); } + public static bool operator != (nfloat l, nfloat r) { throw new NotImplementedException (); } + public static bool operator < (nfloat l, nfloat r) { throw new NotImplementedException (); } + public static bool operator > (nfloat l, nfloat r) { throw new NotImplementedException (); } + public static bool operator <= (nfloat l, nfloat r) { throw new NotImplementedException (); } + public static bool operator >= (nfloat l, nfloat r) { throw new NotImplementedException (); } +#else + public static bool operator == (nfloat l, nfloat r) { return l.v == r.v; } + public static bool operator != (nfloat l, nfloat r) { return l.v != r.v; } + public static bool operator < (nfloat l, nfloat r) { return l.v < r.v; } + public static bool operator > (nfloat l, nfloat r) { return l.v > r.v; } + public static bool operator <= (nfloat l, nfloat r) { return l.v <= r.v; } + public static bool operator >= (nfloat l, nfloat r) { return l.v >= r.v; } +#endif + + public int CompareTo (nfloat value) { return v.CompareTo (value.v); } + public int CompareTo (object value) + { + if (value is nfloat) + return v.CompareTo (((nfloat) value).v); + return v.CompareTo (value); + } + public bool Equals (nfloat obj) { return v.Equals (obj.v); } + public override bool Equals (object obj) + { + if (obj is nfloat) + return v.Equals (((nfloat) obj).v); + return v.Equals (obj); + } + public override int GetHashCode () { return v.GetHashCode (); } + +#if ARCH_32 + public static bool IsNaN (nfloat f) { return Single.IsNaN ((Single)f); } + public static bool IsInfinity (nfloat f) { return Single.IsInfinity ((Single)f); } + public static bool IsPositiveInfinity (nfloat f) { return Single.IsPositiveInfinity ((Single)f); } + public static bool IsNegativeInfinity (nfloat f) { return Single.IsNegativeInfinity ((Single)f); } + + public static nfloat Parse (string s, IFormatProvider provider) { return (nfloat)Single.Parse (s, provider); } + public static nfloat Parse (string s, NumberStyles style) { return (nfloat)Single.Parse (s, style); } + public static nfloat Parse (string s) { return (nfloat)Single.Parse (s); } + public static nfloat Parse (string s, NumberStyles style, IFormatProvider provider) { + return (nfloat)Single.Parse (s, style, provider); + } + + public static bool TryParse (string s, out nfloat result) + { + Single v; + var r = Single.TryParse (s, out v); + result = (nfloat)v; + return r; + } + + public static bool TryParse (string s, NumberStyles style, IFormatProvider provider, out nfloat result) + { + Single v; + var r = Single.TryParse (s, style, provider, out v); + result = (nfloat)v; + return r; + } +#else + public static bool IsNaN (nfloat f) { return Double.IsNaN ((Double)f); } + public static bool IsInfinity (nfloat f) { return Double.IsInfinity ((Double)f); } + public static bool IsPositiveInfinity (nfloat f) { return Double.IsPositiveInfinity ((Double)f); } + public static bool IsNegativeInfinity (nfloat f) { return Double.IsNegativeInfinity ((Double)f); } + + public static nfloat Parse (string s, IFormatProvider provider) { return (nfloat)Double.Parse (s, provider); } + public static nfloat Parse (string s, NumberStyles style) { return (nfloat)Double.Parse (s, style); } + public static nfloat Parse (string s) { return (nfloat)Double.Parse (s); } + public static nfloat Parse (string s, NumberStyles style, IFormatProvider provider) { + return (nfloat)Double.Parse (s, style, provider); + } + + public static bool TryParse (string s, out nfloat result) + { + Double v; + var r = Double.TryParse (s, out v); + result = (nfloat)v; + return r; + } + + public static bool TryParse (string s, NumberStyles style, IFormatProvider provider, out nfloat result) + { + Double v; + var r = Double.TryParse (s, style, provider, out v); + result = (nfloat)v; + return r; + } +#endif + + public override string ToString () { return v.ToString (); } + public string ToString (IFormatProvider provider) { return v.ToString (provider); } + public string ToString (string format) { return v.ToString (format); } + public string ToString (string format, IFormatProvider provider) { return v.ToString (format, provider); } + + public TypeCode GetTypeCode () { return v.GetTypeCode (); } + + bool IConvertible.ToBoolean (IFormatProvider provider) { return ((IConvertible)v).ToBoolean (provider); } + byte IConvertible.ToByte (IFormatProvider provider) { return ((IConvertible)v).ToByte (provider); } + char IConvertible.ToChar (IFormatProvider provider) { return ((IConvertible)v).ToChar (provider); } + DateTime IConvertible.ToDateTime (IFormatProvider provider) { return ((IConvertible)v).ToDateTime (provider); } + decimal IConvertible.ToDecimal (IFormatProvider provider) { return ((IConvertible)v).ToDecimal (provider); } + double IConvertible.ToDouble (IFormatProvider provider) { return ((IConvertible)v).ToDouble (provider); } + short IConvertible.ToInt16 (IFormatProvider provider) { return ((IConvertible)v).ToInt16 (provider); } + int IConvertible.ToInt32 (IFormatProvider provider) { return ((IConvertible)v).ToInt32 (provider); } + long IConvertible.ToInt64 (IFormatProvider provider) { return ((IConvertible)v).ToInt64 (provider); } + sbyte IConvertible.ToSByte (IFormatProvider provider) { return ((IConvertible)v).ToSByte (provider); } + float IConvertible.ToSingle (IFormatProvider provider) { return ((IConvertible)v).ToSingle (provider); } + ushort IConvertible.ToUInt16 (IFormatProvider provider) { return ((IConvertible)v).ToUInt16 (provider); } + uint IConvertible.ToUInt32 (IFormatProvider provider) { return ((IConvertible)v).ToUInt32 (provider); } + ulong IConvertible.ToUInt64 (IFormatProvider provider) { return ((IConvertible)v).ToUInt64 (provider); } + + object IConvertible.ToType (Type targetType, IFormatProvider provider) { + return ((IConvertible)v).ToType (targetType, provider); + } + + public static void CopyArray (IntPtr source, nfloat [] destination, int startIndex, int length) + { + if (source == IntPtr.Zero) + throw new ArgumentNullException ("source"); + if (destination == null) + throw new ArgumentNullException ("destination"); + if (destination.Rank != 1) + throw new ArgumentException ("destination", "array is multi-dimensional"); + if (startIndex < 0) + throw new ArgumentException ("startIndex", "must be >= 0"); + if (length < 0) + throw new ArgumentException ("length", "must be >= 0"); + if (startIndex + length > destination.Length) + throw new ArgumentException ("length", "startIndex + length > destination.Length"); + + for (int i = 0; i < length; i++) + destination [i + startIndex] = (nfloat)Marshal.ReadIntPtr (source, i * nfloat.Size); + } + + public static void CopyArray (nfloat [] source, int startIndex, IntPtr destination, int length) + { + if (source == null) + throw new ArgumentNullException ("source"); + if (destination == IntPtr.Zero) + throw new ArgumentNullException ("destination"); + if (source.Rank != 1) + throw new ArgumentException ("source", "array is multi-dimensional"); + if (startIndex < 0) + throw new ArgumentException ("startIndex", "must be >= 0"); + if (length < 0) + throw new ArgumentException ("length", "must be >= 0"); + if (startIndex + length > source.Length) + throw new ArgumentException ("length", "startIndex + length > source.Length"); + + for (int i = 0; i < length; i++) + Marshal.WriteIntPtr (destination, i * nfloat.Size, (IntPtr)source [i + startIndex]); + } + } +} diff --git a/unity-2019.4.24f1-mbe/mono/mini/cfgdump.c b/unity-2019.4.24f1-mbe/mono/mini/cfgdump.c new file mode 100644 index 000000000..36c9ffac8 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/cfgdump.c @@ -0,0 +1,556 @@ +/** + * \file + * Copyright (C) 2016 Xamarin Inc + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +/* inspired by BinaryGraphPrinter.java of Graal */ + +#include "mini.h" + +#if !defined(DISABLE_LOGGING) && !defined(DISABLE_JIT) && !defined(HOST_WIN32) + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 +#define CFG_DEBUG +#endif + + +#ifdef CFG_DEBUG + +#ifdef HAVE_C99_SUPPORT +#define cfg_debug(format, ...) g_debug(format, __VA_ARGS__) +#else +#define cfg_debug(...) g_debug(__VA_ARGS__) +#endif + +#else + +#ifdef HAVE_C99_SUPPORT +#define cfg_debug(format, ...) do {} while (0) +#else +#define cfg_debug(...) do {} while (0) +#endif + +#endif + +static ConstantPoolEntry* +create_cp_entry (MonoCompile *cfg, void *data, pool_type pt) +{ + ConstantPoolEntry *entry = (ConstantPoolEntry *) mono_mempool_alloc0 (cfg->mempool, sizeof (ConstantPoolEntry)); + entry->pt = pt; + entry->data = data; + return entry; +} + +static void write_pool (MonoCompile *cfg, ConstantPoolEntry *entry); + +static int +create_socket (const char *hostname, const int port) +{ + int sockfd = 0; + struct sockaddr_in serv_addr; + + if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0) { + g_warning ("cfg_dump: could not create socket"); + return -1; + } + + serv_addr.sin_family = AF_INET; + serv_addr.sin_port = htons (port); + serv_addr.sin_addr.s_addr = inet_addr (hostname); + + if (connect (sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { + g_warning ("cfg_dump: Connect Failed: %s", strerror (errno)); + return -2; + } + + return sockfd; +} + +static void +write_byte (MonoCompile *cfg, unsigned char b) +{ + write (cfg->gdump_ctx->fd, &b, 1); +} + +static void +write_short (MonoCompile *cfg, short s) +{ + short swap = htons (s); + write (cfg->gdump_ctx->fd, &swap, 2); +} + +static void +write_int (MonoCompile *cfg, int v) +{ + int swap = htonl (v); + write (cfg->gdump_ctx->fd, &swap, 4); +} + +static void +write_string (MonoCompile *cfg, const char *str) +{ + size_t len = strnlen (str, 0x2000); + write_int (cfg, (int) len); + + gunichar2 *u = u8to16 (str); + for (int i = 0; i < len; i++) + write_short (cfg, u[i]); +} + +static void +add_pool_entry (MonoCompile *cfg, ConstantPoolEntry *entry) +{ + int *cp_id= (int *) mono_mempool_alloc0 (cfg->mempool, sizeof (int)); + *cp_id = cfg->gdump_ctx->next_cp_id; + g_hash_table_insert (cfg->gdump_ctx->constant_pool, entry, cp_id); + write_byte (cfg, POOL_NEW); + write_short (cfg, cfg->gdump_ctx->next_cp_id++); + switch (entry->pt) { + case PT_STRING: + write_byte (cfg, POOL_STRING); + write_string (cfg, (char *) entry->data); + break; + case PT_METHOD: { + MonoMethod *method = (MonoMethod *) entry->data; + write_byte (cfg, POOL_METHOD); + write_pool (cfg, create_cp_entry (cfg, (void *) method->klass, PT_KLASS)); + write_pool (cfg, create_cp_entry (cfg, (void *) method->name, PT_STRING)); + write_pool (cfg, create_cp_entry (cfg, (void *) method->signature, PT_SIGNATURE)); + write_int (cfg, (int) method->flags); + write_int (cfg, -1); // don't transmit bytecode. + break; + } + case PT_KLASS: { + MonoClass *klass = (MonoClass *) entry->data; + write_byte (cfg, POOL_KLASS); + write_string (cfg, klass->name); + write_byte (cfg, KLASS); + break; + } + case PT_SIGNATURE: { + write_byte (cfg, POOL_SIGNATURE); + MonoMethodSignature *sig = (MonoMethodSignature *) entry->data; + write_short (cfg, sig->param_count); + for (int i = 0; i < sig->param_count; i++) { + GString *sbuf = g_string_new (NULL); + mono_type_get_desc (sbuf, sig->params [i], TRUE); + write_pool (cfg, create_cp_entry (cfg, (void *) sbuf->str, PT_STRING)); + g_string_free (sbuf, TRUE); + } + GString *sbuf = g_string_new (NULL); + mono_type_get_desc (sbuf, sig->ret, TRUE); + write_pool (cfg, create_cp_entry (cfg, (void *) sbuf->str, PT_STRING)); + g_string_free (sbuf, TRUE); + break; + } + case PT_OPTYPE: { + MonoInst *insn = (MonoInst *) entry->data; + write_byte (cfg, POOL_NODE_CLASS); + + write_string (cfg, mono_inst_name (insn->opcode)); + GString *insndesc = mono_print_ins_index_strbuf (-1, insn); + int len = strnlen (insndesc->str, 0x2000); +#define CUTOFF 40 + if (len > CUTOFF) { + insndesc->str[CUTOFF] = '\0'; + insndesc->str[CUTOFF - 1] = '.'; + insndesc->str[CUTOFF - 2] = '.'; + } + write_string (cfg, insndesc->str); + if (len > CUTOFF) + insndesc->str[CUTOFF] = ' '; + g_string_free (insndesc, TRUE); + + // one predecessor + write_short (cfg, 1); + write_byte (cfg, 0); + write_pool (cfg, create_cp_entry (cfg, (void *) "predecessor", PT_STRING)); + write_pool (cfg, create_cp_entry (cfg, (void *) NULL, PT_INPUTTYPE)); + + // make NUM_SUCCESSOR successor edges, not everyone will be used. +#define NUM_SUCCESSOR 5 + write_short (cfg, NUM_SUCCESSOR); + for (int i = 0; i < NUM_SUCCESSOR; i++) { + char *str = g_strdup ("successor1"); + str[9] = '0' + i; + write_byte (cfg, 0); + write_pool (cfg, create_cp_entry (cfg, (void *) str, PT_STRING)); + } + + break; + } + case PT_INPUTTYPE: { + write_byte (cfg, POOL_ENUM); + write_pool (cfg, create_cp_entry (cfg, (void *) NULL, PT_ENUMKLASS)); + write_int (cfg, 0); + break; + } + case PT_ENUMKLASS: { + write_byte (cfg, POOL_KLASS); + write_string (cfg, "InputType"); + write_byte (cfg, ENUM_KLASS); + write_int (cfg, 1); + write_pool (cfg, create_cp_entry (cfg, (void *) "fixed", PT_STRING)); + break; + } + } +} + +static void +write_pool (MonoCompile *cfg, ConstantPoolEntry *entry) +{ + if (!entry || !entry->data) { + write_byte (cfg, POOL_NULL); + return; + } + + short *cp_index = (short *) g_hash_table_lookup (cfg->gdump_ctx->constant_pool, entry); + if (cp_index == NULL) + add_pool_entry (cfg, entry); + else { + switch (entry->pt) { + case PT_STRING: write_byte (cfg, POOL_STRING); break; + case PT_METHOD: write_byte (cfg, POOL_METHOD); break; + case PT_ENUMKLASS: write_byte (cfg, POOL_KLASS); break; + case PT_KLASS: write_byte (cfg, POOL_KLASS); break; + case PT_SIGNATURE: write_byte (cfg, POOL_SIGNATURE); break; + case PT_OPTYPE: write_byte (cfg, POOL_NODE_CLASS); break; + case PT_INPUTTYPE: write_byte (cfg, POOL_ENUM); break; + } + write_short (cfg, *cp_index); + } +} + +void +mono_cfg_dump_begin_group (MonoCompile *cfg) +{ + if (cfg->gdump_ctx == NULL) + return; + write_byte (cfg, BEGIN_GROUP); + char *title = (char *) mono_mempool_alloc0 (cfg->mempool, 0x2000); + sprintf (title, "%s::%s", cfg->method->klass->name, cfg->method->name); + write_pool (cfg, create_cp_entry (cfg, (void *) title, PT_STRING)); + write_pool (cfg, create_cp_entry (cfg, (void *) cfg->method->name, PT_STRING)); + write_pool (cfg, create_cp_entry (cfg, (void *) cfg->method, PT_METHOD)); + write_int (cfg, 0); // TODO: real bytecode index. +} + +void +mono_cfg_dump_close_group (MonoCompile *cfg) +{ + if (cfg->gdump_ctx == NULL) + return; + write_byte (cfg, CLOSE_GROUP); + cfg->gdump_ctx = NULL; +} + +static int +label_instructions (MonoCompile *cfg) +{ + MonoBasicBlock *bb; + int instruction_count = 0; + + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { + cfg_debug ("bb: %d (in: %d, out: %d)", bb->block_num, bb->in_count, bb->out_count); + MonoInst *insn; + for (insn = bb->code; insn; insn = insn->next) { + instruction_count++; + void *id = g_hash_table_lookup (cfg->gdump_ctx->insn2id, insn); + if (id != NULL) // already in the table. + continue; + int *new_id = (int *) mono_mempool_alloc0 (cfg->mempool, sizeof (int)); + *new_id = cfg->gdump_ctx->next_insn_id++; + g_hash_table_insert (cfg->gdump_ctx->insn2id, insn, new_id); +#ifdef CFG_DEBUG + GString *insndesc = mono_print_ins_index_strbuf (-1, insn); + cfg_debug ("> insn%002d: %s", *new_id, insndesc->str); + g_string_free (insndesc, TRUE); +#endif + } + } + return instruction_count; +} + +static void +write_instructions (MonoCompile *cfg, int instruction_count) +{ + MonoBasicBlock *bb; + write_int (cfg, instruction_count); + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { + MonoInst *insn; + cfg_debug ("== bb: %d (in: %d, out: %d) ==", bb->block_num, bb->in_count, bb->out_count); + for (insn = bb->code; insn; insn = insn->next) { + int i; + int *id = (int *) g_hash_table_lookup (cfg->gdump_ctx->insn2id, insn); + g_assert (id); + write_int (cfg, *id); + + // hardcoded node class: only one input and NUM_SUCCESSOR successors + write_pool (cfg, create_cp_entry (cfg, (void *) insn, PT_OPTYPE)); + write_byte (cfg, cfg->bb_entry->code != insn); + + // properties + write_short (cfg, 2); + + // property #1 + GString *insndesc = mono_print_ins_index_strbuf (-1, insn); + cfg_debug ("dumping node [%2d]: %s", *id, insndesc->str); + write_pool (cfg, create_cp_entry (cfg, (void *) "fullname", PT_STRING)); + write_byte (cfg, PROPERTY_POOL); + write_pool (cfg, create_cp_entry (cfg, (void *) insndesc->str, PT_STRING)); + g_string_free (insndesc, TRUE); + + // property #2 + write_pool (cfg, create_cp_entry (cfg, (void *) "category", PT_STRING)); + write_byte (cfg, PROPERTY_POOL); + if (bb->in_count > 1 && bb->code == insn) + write_pool (cfg, create_cp_entry (cfg, (void *) "merge", PT_STRING)); + else if (bb->code == insn) + write_pool (cfg, create_cp_entry (cfg, (void *) "begin", PT_STRING)); + else if (MONO_IS_COND_BRANCH_OP (insn)) + write_pool (cfg, create_cp_entry (cfg, (void *) "controlSplit", PT_STRING)); + else if (MONO_IS_PHI (insn)) + write_pool (cfg, create_cp_entry (cfg, (void *) "phi", PT_STRING)); + else if (!MONO_INS_HAS_NO_SIDE_EFFECT (insn)) + write_pool (cfg, create_cp_entry (cfg, (void *) "state", PT_STRING)); + else + write_pool (cfg, create_cp_entry (cfg, (void *) "fixed", PT_STRING)); + // end of properties + write_int (cfg, -1); // never set predecessor. + + int *next_id; + if (insn->next != NULL) { + next_id = (int *) g_hash_table_lookup (cfg->gdump_ctx->insn2id, insn->next); + g_assert (next_id); + cfg_debug ("\tsuccessor' : [%2d]", *next_id); + write_int (cfg, *next_id); + for (i = 1; i < NUM_SUCCESSOR; i++) + write_int (cfg, -1); + } else { + g_assert (bb->out_count < NUM_SUCCESSOR); + for (i = 0; (i < bb->out_count) && (i < NUM_SUCCESSOR); i++) { + if (bb->out_bb[i]->code == NULL) + write_int (cfg, -1); + else { + next_id = (int *) g_hash_table_lookup (cfg->gdump_ctx->insn2id, bb->out_bb[i]->code); + if (next_id) + cfg_debug ("\tsuccessor'': [%2d]", *next_id); + write_int (cfg, next_id ? *next_id : -1); + } + } + for (; i < NUM_SUCCESSOR; i++) + write_int (cfg, -1); + } + } + } +} + +static void +write_blocks (MonoCompile *cfg) +{ + int block_size = 0; + MonoBasicBlock *bb; + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) + block_size++; + write_int (cfg, block_size); + + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { + int insn_size = 0; + MonoInst *insn = NULL; + + write_int (cfg, bb->block_num); + + for (insn = bb->code; insn; insn = insn->next) + insn_size++; + write_int (cfg, insn_size); + + for (insn = bb->code; insn; insn = insn->next) { + int *id = (int *) g_hash_table_lookup (cfg->gdump_ctx->insn2id, insn); + g_assert (id); + write_int (cfg, *id); + } + + write_int (cfg, bb->out_count); + for (int i = 0; i < bb->out_count; i++) + write_int (cfg, bb->out_bb[i]->block_num); + } +} + +static guint +instruction_hash (MonoInst *insn) +{ + guint res = 0; + res = insn->opcode << 0x00; + res ^= insn->type << 0x04; + res ^= insn->flags << 0x08; + res ^= insn->dreg << 0x0c; + res ^= insn->sreg1 << 0x10; + res ^= insn->sreg2 << 0x14; + res ^= insn->sreg3 << 0x18; + res ^= (gsize) insn->next; + res ^= (gsize) insn->prev; + res ^= (gsize) insn; + return res; +} + +static gboolean +instruction_equal (gconstpointer v1, gconstpointer v2) +{ + MonoInst *i1 = (MonoInst *) v1; + MonoInst *i2 = (MonoInst *) v2; + + if (i1->opcode != i2->opcode || i1->type != i2->type || i1->flags != i2->flags) + return FALSE; + if (i1->dreg != i2->dreg || i1->sreg1 != i2->sreg1 || i1->sreg2 != i2->sreg2 || i1->sreg3 != i2->sreg3) + return FALSE; + if (i1->next != i2->next || i1->prev != i2->prev) + return FALSE; + return TRUE; +} + +static guint +constant_pool_hash (ConstantPoolEntry *entry) +{ + switch (entry->pt) { + case PT_STRING: + return g_str_hash (entry->data); + case PT_METHOD: { + MonoMethod *method = (MonoMethod *) entry->data; + return g_str_hash (method->name) ^ g_str_hash (method->klass); + } + case PT_KLASS: + return g_str_hash (((MonoClass *) entry->data)->name); + case PT_OPTYPE: + return instruction_hash ((MonoInst *) entry->data); + case PT_SIGNATURE: { + MonoMethodSignature *sig = (MonoMethodSignature *) entry->data; + guint ret = GPOINTER_TO_UINT (sig->ret); + for (int i = 0; i < sig->param_count; i++) { + ret ^= GPOINTER_TO_UINT (sig->params [i]) << (i + 1); + } + return ret; + } + case PT_INPUTTYPE: // TODO: singleton. + case PT_ENUMKLASS: + return GPOINTER_TO_UINT (entry->data); + } + g_assert (FALSE); + return FALSE; +} + +static gboolean +constant_pool_equal (gconstpointer v1, gconstpointer v2) +{ + ConstantPoolEntry *e1 = (ConstantPoolEntry *) v1; + ConstantPoolEntry *e2 = (ConstantPoolEntry *) v2; + if (e1->pt != e2->pt) + return FALSE; + + switch (e1->pt) { + case PT_STRING: + return g_str_equal (e1->data, e2->data); + case PT_OPTYPE: + return instruction_equal (e1->data, e2->data); + case PT_METHOD: // TODO: implement proper equal. + case PT_KLASS: + case PT_SIGNATURE: + return constant_pool_hash (e1) == constant_pool_hash (e2); + case PT_INPUTTYPE: // TODO: singleton. + case PT_ENUMKLASS: + return TRUE; + } + g_assert (FALSE); + return FALSE; +} + + +static gboolean cfg_dump_method_inited = FALSE; +static const char *cfg_dump_method_name; + +void mono_cfg_dump_create_context (MonoCompile *cfg) +{ + cfg->gdump_ctx = NULL; + + if (!cfg_dump_method_inited) { + cfg_dump_method_name = g_getenv ("MONO_JIT_DUMP_METHOD"); + cfg_dump_method_inited = TRUE; + } + if (!cfg_dump_method_name) + return; + const char *name = cfg_dump_method_name; + + if ((strchr (name, '.') > name) || strchr (name, ':')) { + MonoMethodDesc *desc = mono_method_desc_new (name, TRUE); + gboolean failed = !mono_method_desc_full_match (desc, cfg->method); + mono_method_desc_free (desc); + if (failed) + return; + } else + if (strcmp (cfg->method->name, name) != 0) + return; + + g_debug ("cfg_dump: create context for \"%s::%s\"", cfg->method->klass->name, cfg->method->name); + int fd = create_socket (DEFAULT_HOST, DEFAULT_PORT); + if (fd < 0) { + g_warning ("cfg_dump: couldn't create socket: %s::%d", DEFAULT_HOST, DEFAULT_PORT); + return; + } + + MonoGraphDumper *ctx = (MonoGraphDumper *) mono_mempool_alloc0 (cfg->mempool, sizeof (MonoGraphDumper)); + ctx->fd = fd; + ctx->constant_pool = g_hash_table_new ((GHashFunc) constant_pool_hash, constant_pool_equal); + ctx->insn2id = g_hash_table_new ((GHashFunc) instruction_hash, instruction_equal); + ctx->next_cp_id = 1; + ctx->next_insn_id = 0; + + cfg->gdump_ctx = ctx; +} + +void +mono_cfg_dump_ir (MonoCompile *cfg, const char *phase_name) +{ + if (cfg->gdump_ctx == NULL) + return; + cfg_debug ("=== DUMPING PASS \"%s\" ===", phase_name); + write_byte (cfg, BEGIN_GRAPH); + write_pool (cfg, create_cp_entry (cfg, (void *) phase_name, PT_STRING)); + + int instruction_count = label_instructions (cfg); + write_instructions (cfg, instruction_count); + write_blocks (cfg); +} +#else /* !defined(DISABLE_LOGGING) && !defined(DISABLE_JIT) && !defined(HOST_WIN32) */ +void +mono_cfg_dump_create_context (MonoCompile *cfg) +{ +} + +void +mono_cfg_dump_begin_group (MonoCompile *cfg) +{ +} + +void +mono_cfg_dump_close_group (MonoCompile *cfg) +{ +} + +void +mono_cfg_dump_ir (MonoCompile *cfg, const char *phase_name) +{ +} +#endif /* !defined(DISABLE_LOGGING) && !defined(DISABLE_JIT) && !defined(HOST_WIN32) */ diff --git a/unity-2019.4.24f1-mbe/mono/mini/cfgdump.h b/unity-2019.4.24f1-mbe/mono/mini/cfgdump.h new file mode 100644 index 000000000..2a0161f38 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/cfgdump.h @@ -0,0 +1,70 @@ +/** + * \file + * Copyright (C) 2016 Xamarin Inc + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#ifndef __MINI_CFGDUMP_H__ +#define __MINI_CFGDUMP_H__ +#include + +#define CONSTANT_POOL_MAX_SIZE 8000 + +#define BEGIN_GROUP 0x00 +#define BEGIN_GRAPH 0x01 +#define CLOSE_GROUP 0x02 + +#define POOL_NEW 0x00 +#define POOL_STRING 0x01 +#define POOL_ENUM 0x02 +#define POOL_KLASS 0x03 +#define POOL_METHOD 0x04 +#define POOL_NULL 0x05 +#define POOL_NODE_CLASS 0x06 +#define POOL_FIELD 0x07 +#define POOL_SIGNATURE 0x08 + +#define PROPERTY_POOL 0x00 +#define PROPERTY_INT 0x01 +#define PROPERTY_LONG 0x02 +#define PROPERTY_DOUBLE 0x03 +#define PROPERTY_FLOAT 0x04 +#define PROPERTY_TRUE 0x05 +#define PROPERTY_FALSE 0x06 +#define PROPERTY_ARRAY 0x07 +#define PROPERTY_SUBGRAPH 0x08 + +#define KLASS 0x00 +#define ENUM_KLASS 0x01 + + +#define DEFAULT_PORT 4445 +#define DEFAULT_HOST "127.0.0.1" + +typedef enum { + PT_STRING, + PT_METHOD, + PT_KLASS, + PT_OPTYPE, + PT_INPUTTYPE, + PT_ENUMKLASS, + PT_SIGNATURE +} pool_type; + +struct _MonoGraphDumper { + int fd; + GHashTable *constant_pool; + short next_cp_id; + GHashTable *insn2id; + int next_insn_id; +}; + +typedef struct _MonoGraphDumper MonoGraphDumper; + +struct _ConstantPoolEntry { + pool_type pt; + void *data; +}; + +typedef struct _ConstantPoolEntry ConstantPoolEntry; +#endif /* __MINI_CFGDUMP_H__ */ diff --git a/unity-2019.4.24f1-mbe/mono/mini/cfold.c b/unity-2019.4.24f1-mbe/mono/mini/cfold.c new file mode 100644 index 000000000..940c07ac8 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/cfold.c @@ -0,0 +1,408 @@ +/** + * \file + * Constant folding support + * + * Author: + * Paolo Molaro (lupus@ximian.com) + * Dietmar Maurer (dietmar@ximian.com) + * + * (C) 2003 Ximian, Inc. http://www.ximian.com + */ +#include + +#include "mini.h" +#include "ir-emit.h" + +/* WTF is this doing here?!?!? */ +int +mono_is_power_of_two (guint32 val) +{ + int i, j, k; + + for (i = 0, j = 1, k = 0xfffffffe; i < 32; ++i, j = j << 1, k = k << 1) { + if (val & j) + break; + } + if (i == 32 || val & k) + return -1; + return i; +} + +#ifndef G_MININT32 +#define MYGINT32_MAX 2147483647 +#define G_MININT32 (-MYGINT32_MAX -1) +#endif + +#define FOLD_UNOP(name,op) \ + case name: \ + dest->inst_c0 = op arg1->inst_c0; \ + break; + +#define FOLD_BINOP(name, op) \ + case name: \ + dest->inst_c0 = arg1->inst_c0 op arg2->inst_c0; \ + break; + +#define FOLD_BINOPC(name,op,cast) \ + case name: \ + dest->inst_c0 = (cast)arg1->inst_c0 op (cast)arg2->inst_c0; \ + break; + +#define FOLD_BINOP2_IMM(name, op) \ + case name: \ + dest->inst_c0 = arg1->inst_c0 op ins->inst_imm; \ + break; + +#define FOLD_BINOPC2_IMM(name, op, cast) \ + case name: \ + dest->inst_c0 = (cast)arg1->inst_c0 op (cast)ins->inst_imm; \ + break; + +#define FOLD_BINOPCXX(name,op,cast) \ + case name: \ + res = (cast)arg1->inst_c0 op (cast)arg2->inst_c0; \ + break; \ + +#define ALLOC_DEST(cfg, dest, ins) do { \ + if (!(dest)) { \ + MONO_INST_NEW ((cfg), (dest), -1); \ + (dest)->dreg = (ins)->dreg; \ + } \ +} while (0) + +#ifndef DISABLE_JIT + +/** + * mono_constant_fold_ins: + * + * Perform constant folding on INS, using ARG1 and ARG2 as the arguments. If OVERWRITE is + * true, then store the result back into INS and return INS. Otherwise allocate a new ins, + * store the result into it and return it. If constant folding cannot be performed, return + * NULL. + */ +MonoInst* +mono_constant_fold_ins (MonoCompile *cfg, MonoInst *ins, MonoInst *arg1, MonoInst *arg2, gboolean overwrite) +{ + MonoInst *dest = NULL; + + if (overwrite) + dest = ins; + + switch (ins->opcode) { + case OP_IMUL: + case OP_IADD: + case OP_IAND: + case OP_IOR: + case OP_IXOR: + if (arg2->opcode == OP_ICONST) { + if (arg1->opcode == OP_ICONST) { + ALLOC_DEST (cfg, dest, ins); + switch (ins->opcode) { + FOLD_BINOP (OP_IMUL, *); + FOLD_BINOP (OP_IADD, +); + FOLD_BINOP (OP_IAND, &); + FOLD_BINOP (OP_IOR, |); + FOLD_BINOP (OP_IXOR, ^); + } + dest->opcode = OP_ICONST; + MONO_INST_NULLIFY_SREGS (dest); + } + } else if (arg1->opcode == OP_ICONST) { + /* + * This is commutative so swap the arguments, allowing the _imm variant + * to be used later. + */ + if (mono_op_to_op_imm (ins->opcode) != -1) { + ALLOC_DEST (cfg, dest, ins); + dest->opcode = mono_op_to_op_imm (ins->opcode); + dest->sreg1 = ins->sreg2; + dest->sreg2 = -1; + dest->inst_imm = arg1->inst_c0; + } + } + break; + case OP_IMUL_IMM: + case OP_IADD_IMM: + case OP_IAND_IMM: + case OP_IOR_IMM: + case OP_IXOR_IMM: + case OP_ISUB_IMM: + case OP_ISHL_IMM: + case OP_ISHR_IMM: + case OP_ISHR_UN_IMM: + case OP_SHL_IMM: + if (arg1->opcode == OP_ICONST) { + ALLOC_DEST (cfg, dest, ins); + switch (ins->opcode) { + FOLD_BINOP2_IMM (OP_IMUL_IMM, *); + FOLD_BINOP2_IMM (OP_IADD_IMM, +); + FOLD_BINOP2_IMM (OP_IAND_IMM, &); + FOLD_BINOP2_IMM (OP_IOR_IMM, |); + FOLD_BINOP2_IMM (OP_IXOR_IMM, ^); + FOLD_BINOP2_IMM (OP_ISUB_IMM, -); + FOLD_BINOPC2_IMM (OP_ISHL_IMM, <<, gint32); + FOLD_BINOPC2_IMM (OP_ISHR_IMM, >>, gint32); + FOLD_BINOPC2_IMM (OP_ISHR_UN_IMM, >>, guint32); + FOLD_BINOP2_IMM (OP_SHL_IMM, <<); + } + dest->opcode = OP_ICONST; + MONO_INST_NULLIFY_SREGS (dest); + } + break; + case OP_ISUB: + case OP_ISHL: + case OP_ISHR: + case OP_ISHR_UN: + if ((arg1->opcode == OP_ICONST) && (arg2->opcode == OP_ICONST)) { + ALLOC_DEST (cfg, dest, ins); + switch (ins->opcode) { + FOLD_BINOP (OP_ISUB, -); + FOLD_BINOP (OP_ISHL, <<); + FOLD_BINOP (OP_ISHR, >>); + FOLD_BINOPC (OP_ISHR_UN, >>, guint32); + } + dest->opcode = OP_ICONST; + MONO_INST_NULLIFY_SREGS (dest); + } + break; + case OP_IDIV: + case OP_IDIV_UN: + case OP_IREM: + case OP_IREM_UN: + if ((arg1->opcode == OP_ICONST) && (arg2->opcode == OP_ICONST)) { + if ((arg2->inst_c0 == 0) || ((arg1->inst_c0 == G_MININT32) && (arg2->inst_c0 == -1))) + return NULL; + ALLOC_DEST (cfg, dest, ins); + switch (ins->opcode) { + FOLD_BINOPC (OP_IDIV, /, gint32); + FOLD_BINOPC (OP_IDIV_UN, /, guint32); + FOLD_BINOPC (OP_IREM, %, gint32); + FOLD_BINOPC (OP_IREM_UN, %, guint32); + } + dest->opcode = OP_ICONST; + MONO_INST_NULLIFY_SREGS (dest); + } + break; + case OP_IDIV_IMM: + case OP_IDIV_UN_IMM: + case OP_IREM_IMM: + case OP_IREM_UN_IMM: + if (arg1->opcode == OP_ICONST) { + if ((ins->inst_imm == 0) || ((arg1->inst_c0 == G_MININT32) && (ins->inst_imm == -1))) + return NULL; + ALLOC_DEST (cfg, dest, ins); + switch (ins->opcode) { + FOLD_BINOPC2_IMM (OP_IDIV_IMM, /, gint32); + FOLD_BINOPC2_IMM (OP_IDIV_UN_IMM, /, guint32); + FOLD_BINOPC2_IMM (OP_IREM_IMM, %, gint32); + FOLD_BINOPC2_IMM (OP_IREM_UN_IMM, %, guint32); + default: + g_assert_not_reached (); + } + dest->opcode = OP_ICONST; + MONO_INST_NULLIFY_SREGS (dest); + } + break; + /* case OP_INEG: */ + case OP_INOT: + case OP_INEG: + if (arg1->opcode == OP_ICONST) { + /* INEG sets cflags on x86, and the LNEG decomposition depends on that */ +#if SIZEOF_REGISTER == 4 + if (ins->opcode == OP_INEG) + return NULL; +#endif + ALLOC_DEST (cfg, dest, ins); + switch (ins->opcode) { + FOLD_UNOP (OP_INEG,-); + FOLD_UNOP (OP_INOT,~); + } + dest->opcode = OP_ICONST; + MONO_INST_NULLIFY_SREGS (dest); + } + break; + case OP_MOVE: +#if SIZEOF_REGISTER == 8 + if ((arg1->opcode == OP_ICONST) || (arg1->opcode == OP_I8CONST)) { +#else + if (arg1->opcode == OP_ICONST) { +#endif + ALLOC_DEST (cfg, dest, ins); + dest->opcode = arg1->opcode; + MONO_INST_NULLIFY_SREGS (dest); + dest->inst_c0 = arg1->inst_c0; + } + break; + case OP_VMOVE: + if (arg1->opcode == OP_VZERO) { + ALLOC_DEST (cfg, dest, ins); + dest->opcode = OP_VZERO; + dest->sreg1 = -1; + } + break; + case OP_XMOVE: + if (arg1->opcode == OP_XZERO) { + ALLOC_DEST (cfg, dest, ins); + dest->opcode = OP_XZERO; + dest->sreg1 = -1; + } + break; + case OP_COMPARE: + case OP_ICOMPARE: + case OP_COMPARE_IMM: + case OP_ICOMPARE_IMM: { + MonoInst dummy_arg2; + if (ins->sreg2 == -1) { + arg2 = &dummy_arg2; + arg2->opcode = OP_ICONST; + arg2->inst_c0 = ins->inst_imm; + } + + if ((arg1->opcode == OP_ICONST) && (arg2->opcode == OP_ICONST) && ins->next) { + MonoInst *next = ins->next; + gboolean res = FALSE; + + switch (next->opcode) { + case OP_CEQ: + case OP_ICEQ: + case OP_CGT: + case OP_ICGT: + case OP_CGT_UN: + case OP_ICGT_UN: + case OP_CLT: + case OP_ICLT: + case OP_CLT_UN: + case OP_ICLT_UN: + switch (next->opcode) { + FOLD_BINOPCXX (OP_CEQ,==,gint32); + FOLD_BINOPCXX (OP_ICEQ,==,gint32); + FOLD_BINOPCXX (OP_CGT,>,gint32); + FOLD_BINOPCXX (OP_ICGT,>,gint32); + FOLD_BINOPCXX (OP_CGT_UN,>,guint32); + FOLD_BINOPCXX (OP_ICGT_UN,>,guint32); + FOLD_BINOPCXX (OP_CLT,<,gint32); + FOLD_BINOPCXX (OP_ICLT,<,gint32); + FOLD_BINOPCXX (OP_CLT_UN,<,guint32); + FOLD_BINOPCXX (OP_ICLT_UN,<,guint32); + } + + if (overwrite) { + NULLIFY_INS (ins); + next->opcode = OP_ICONST; + next->inst_c0 = res; + MONO_INST_NULLIFY_SREGS (next); + } else { + ALLOC_DEST (cfg, dest, ins); + dest->opcode = OP_ICONST; + dest->inst_c0 = res; + } + break; + case OP_IBEQ: + case OP_IBNE_UN: + case OP_IBGT: + case OP_IBGT_UN: + case OP_IBGE: + case OP_IBGE_UN: + case OP_IBLT: + case OP_IBLT_UN: + case OP_IBLE: + case OP_IBLE_UN: + switch (next->opcode) { + FOLD_BINOPCXX (OP_IBEQ,==,gint32); + FOLD_BINOPCXX (OP_IBNE_UN,!=,guint32); + FOLD_BINOPCXX (OP_IBGT,>,gint32); + FOLD_BINOPCXX (OP_IBGT_UN,>,guint32); + FOLD_BINOPCXX (OP_IBGE,>=,gint32); + FOLD_BINOPCXX (OP_IBGE_UN,>=,guint32); + FOLD_BINOPCXX (OP_IBLT,<,gint32); + FOLD_BINOPCXX (OP_IBLT_UN,<,guint32); + FOLD_BINOPCXX (OP_IBLE,<=,gint32); + FOLD_BINOPCXX (OP_IBLE_UN,<=,guint32); + } + + if (overwrite) { + /* + * Can't nullify OP_COMPARE here since the decompose long branch + * opcodes depend on it being executed. Also, the branch might not + * be eliminated after all if loop opts is disabled, for example. + */ + if (res) + next->flags |= MONO_INST_CFOLD_TAKEN; + else + next->flags |= MONO_INST_CFOLD_NOT_TAKEN; + } else { + ALLOC_DEST (cfg, dest, ins); + dest->opcode = OP_ICONST; + dest->inst_c0 = res; + } + break; + case OP_COND_EXC_EQ: + res = arg1->inst_c0 == arg2->inst_c0; + if (!res) { + if (overwrite) { + NULLIFY_INS (ins); + NULLIFY_INS (next); + } else { + ALLOC_DEST (cfg, dest, ins); + dest->opcode = OP_ICONST; + dest->inst_c0 = res; + } + } + break; + case OP_NOP: + case OP_BR: + /* This happens when a conditional branch is eliminated */ + if (next->next == NULL) { + /* Last ins */ + if (overwrite) + NULLIFY_INS (ins); + } + break; + default: + return NULL; + } + } + if ((arg1->opcode == OP_PCONST) && (arg2->opcode == OP_PCONST) && ins->next) { + MonoInst *next = ins->next; + + if (next->opcode == OP_LCEQ) { + gboolean res = arg1->inst_p0 == arg2->inst_p0; + if (overwrite) { + NULLIFY_INS (ins); + next->opcode = OP_ICONST; + next->inst_c0 = res; + MONO_INST_NULLIFY_SREGS (next); + } else { + ALLOC_DEST (cfg, dest, ins); + dest->opcode = OP_ICONST; + dest->inst_c0 = res; + } + break; + } + } + break; + } + case OP_FMOVE: + if (arg1->opcode == OP_R8CONST) { + ALLOC_DEST (cfg, dest, ins); + dest->opcode = OP_R8CONST; + dest->sreg1 = -1; + dest->inst_p0 = arg1->inst_p0; + } + break; + + /* + * TODO: + * conv.* opcodes. + * *ovf* opcodes? It's slow and hard to do in C. + * switch can be replaced by a simple jump + */ + default: + return NULL; + } + + return dest; +} + + +#endif /* DISABLE_JIT */ diff --git a/unity-2019.4.24f1-mbe/mono/mini/cpu-amd64.md b/unity-2019.4.24f1-mbe/mono/mini/cpu-amd64.md new file mode 100644 index 000000000..abb1725fb --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/cpu-amd64.md @@ -0,0 +1,796 @@ + +# x86-class cpu description file +# this file is read by genmdesc to pruduce a table with all the relevant information +# about the cpu instructions that may be used by the regsiter allocator, the scheduler +# and other parts of the arch-dependent part of mini. +# +# An opcode name is followed by a colon and optional specifiers. +# A specifier has a name, a colon and a value. Specifiers are separated by white space. +# Here is a description of the specifiers valid for this file and their possible values. +# +# dest:register describes the destination register of an instruction +# src1:register describes the first source register of an instruction +# src2:register describes the second source register of an instruction +# +# i integer register +# b base register (used in address references) +# f floating point register +# a EAX register +# d EDX register +# l long reg (forced eax:edx) +# s ECX register +# c register which can be used as a byte register (RAX..RDX) +# A - first arg reg (rdi/rcx) +# +# len:number describe the maximun length in bytes of the instruction +# number is a positive integer. If the length is not specified +# it defaults to zero. But lengths are only checked if the given opcode +# is encountered during compilation. Some opcodes, like CONV_U4 are +# transformed into other opcodes in the brg files, so they do not show up +# during code generation. +# +# cost:number describe how many cycles are needed to complete the instruction (unused) +# +# clob:spec describe if the instruction clobbers registers or has special needs +# +# c clobbers caller-save registers +# 1 clobbers the first source register +# a EAX is clobbered +# d EDX is clobbered +# x both the source operands are clobbered (xchg) +# m sets an XMM reg +# +# flags:spec describe if the instruction uses or sets the flags (unused) +# +# s sets the flags +# u uses the flags +# m uses and modifies the flags +# +# res:spec describe what units are used in the processor (unused) +# +# delay: describe delay slots (unused) +# +# the required specifiers are: len, clob (if registers are clobbered), the registers +# specifiers if the registers are actually used, flags (when scheduling is implemented). +# +# See the code in mini-x86.c for more details on how the specifiers are used. +# + +break: len:2 +tailcall: len:120 clob:c +br: len:6 +label: len:0 +seq_point: len:46 clob:c +il_seq_point: len:0 + +long_add: dest:i src1:i src2:i len:3 clob:1 +long_sub: dest:i src1:i src2:i len:3 clob:1 +long_mul: dest:i src1:i src2:i len:4 clob:1 +long_div: dest:a src1:a src2:i len:16 clob:d +long_div_un: dest:a src1:a src2:i len:16 clob:d +long_rem: dest:d src1:a src2:i len:16 clob:a +long_rem_un: dest:d src1:a src2:i len:16 clob:a +long_and: dest:i src1:i src2:i len:3 clob:1 +long_or: dest:i src1:i src2:i len:3 clob:1 +long_xor: dest:i src1:i src2:i len:3 clob:1 +long_shl: dest:i src1:i src2:s clob:1 len:3 +long_shr: dest:i src1:i src2:s clob:1 len:3 +long_shr_un: dest:i src1:i src2:s clob:1 len:3 +long_neg: dest:i src1:i len:3 clob:1 +long_not: dest:i src1:i len:3 clob:1 +long_conv_to_i1: dest:i src1:i len:4 +long_conv_to_i2: dest:i src1:i len:4 +long_conv_to_i4: dest:i src1:i len:3 +long_conv_to_i8: dest:i src1:i len:3 +long_conv_to_r4: dest:f src1:i len:15 +long_conv_to_r8: dest:f src1:i len:9 +long_conv_to_u4: dest:i src1:i len:3 +long_conv_to_u8: dest:i src1:i len:3 +long_conv_to_r_un: dest:f src1:i len:64 +long_conv_to_ovf_i4_un: dest:i src1:i len:16 +long_conv_to_ovf_u4: dest:i src1:i len:15 +long_conv_to_u2: dest:i src1:i len:4 +long_conv_to_u1: dest:i src1:i len:4 +zext_i4: dest:i src1:i len:4 + +long_mul_imm: dest:i src1:i clob:1 len:12 +long_min: dest:i src1:i src2:i len:16 clob:1 +long_min_un: dest:i src1:i src2:i len:16 clob:1 +long_max: dest:i src1:i src2:i len:16 clob:1 +long_max_un: dest:i src1:i src2:i len:16 clob:1 + +throw: src1:i len:24 +rethrow: src1:i len:24 +start_handler: len:16 +endfinally: len:9 +endfilter: src1:a len:9 +get_ex_obj: dest:a len:16 + +ckfinite: dest:f src1:f len:43 +ceq: dest:c len:8 +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:120 +compare: src1:i src2:i len:3 +lcompare: src1:i src2:i len:3 +icompare: src1:i src2:i len:3 +compare_imm: src1:i len:13 +icompare_imm: src1:i len:8 +fcompare: src1:f src2:f clob:a len:13 +rcompare: src1:f src2:f clob:a len:13 +oparglist: src1:b len:11 +checkthis: src1:b len:5 +call: dest:a clob:c len:32 +voidcall: clob:c len:32 +voidcall_reg: src1:i clob:c len:32 +voidcall_membase: src1:b clob:c len:32 +fcall: dest:f len:64 clob:c +fcall_reg: dest:f src1:i len:64 clob:c +fcall_membase: dest:f src1:b len:64 clob:c +rcall: dest:f len:64 clob:c +rcall_reg: dest:f src1:i len:64 clob:c +rcall_membase: dest:f src1:b len:64 clob:c +lcall: dest:a len:64 clob:c +lcall_reg: dest:a src1:i len:64 clob:c +lcall_membase: dest:a src1:b len:64 clob:c +vcall: len:64 clob:c +vcall_reg: src1:i len:64 clob:c +vcall_membase: src1:b len:64 clob:c +call_reg: dest:a src1:i len:32 clob:c +call_membase: dest:a src1:b len:32 clob:c +iconst: dest:i len:10 +i8const: dest:i len:10 +r4const: dest:f len:14 +r8const: dest:f len:9 +store_membase_imm: dest:b len:15 +store_membase_reg: dest:b src1:i len:9 +storei8_membase_reg: dest:b src1:i len:9 +storei1_membase_imm: dest:b len:11 +storei1_membase_reg: dest:b src1:c len:9 +storei2_membase_imm: dest:b len:13 +storei2_membase_reg: dest:b src1:i len:9 +storei4_membase_imm: dest:b len:13 +storei4_membase_reg: dest:b src1:i len:9 +storei8_membase_imm: dest:b len:18 +storer4_membase_reg: dest:b src1:f len:15 +storer8_membase_reg: dest:b src1:f len:10 +load_membase: dest:i src1:b len:8 +loadi1_membase: dest:c src1:b len:9 +loadu1_membase: dest:c src1:b len:9 +loadi2_membase: dest:i src1:b len:9 +loadu2_membase: dest:i src1:b len:9 +loadi4_membase: dest:i src1:b len:9 +loadu4_membase: dest:i src1:b len:9 +loadi8_membase: dest:i src1:b len:18 +loadr4_membase: dest:f src1:b len:16 +loadr8_membase: dest:f src1:b len:16 +loadu4_mem: dest:i len:10 +amd64_loadi8_memindex: dest:i src1:i src2:i len:10 +move: dest:i src1:i len:3 +add_imm: dest:i src1:i len:8 clob:1 +sub_imm: dest:i src1:i len:8 clob:1 +mul_imm: dest:i src1:i len:12 +and_imm: dest:i src1:i len:8 clob:1 +or_imm: dest:i src1:i len:8 clob:1 +xor_imm: dest:i src1:i len:8 clob:1 +shl_imm: dest:i src1:i len:8 clob:1 +shr_imm: dest:i src1:i len:8 clob:1 +shr_un_imm: dest:i src1:i len:8 clob:1 +cond_exc_eq: len:8 +cond_exc_ne_un: len:8 +cond_exc_lt: len:8 +cond_exc_lt_un: len:8 +cond_exc_gt: len:8 +cond_exc_gt_un: len:8 +cond_exc_ge: len:8 +cond_exc_ge_un: len:8 +cond_exc_le: len:8 +cond_exc_le_un: len:8 +cond_exc_ov: len:8 +cond_exc_no: len:8 +cond_exc_c: len:8 +cond_exc_nc: len:8 +cond_exc_iov: len:8 +cond_exc_ic: len:8 + +long_mul_ovf: dest:i src1:i src2:i clob:1 len:16 +long_mul_ovf_un: dest:i src1:i src2:i len:22 +long_shr_imm: dest:i src1:i clob:1 len:11 +long_shr_un_imm: dest:i src1:i clob:1 len:11 +long_shl_imm: dest:i src1:i clob:1 len:11 + +long_beq: len:8 +long_bge: len:8 +long_bgt: len:8 +long_ble: len:8 +long_blt: len:8 +long_bne_un: len:8 +long_bge_un: len:8 +long_bgt_un: len:8 +long_ble_un: len:8 +long_blt_un: len:8 + +float_beq: len:13 +float_bne_un: len:18 +float_blt: len:13 +float_blt_un: len:30 +float_bgt: len:13 +float_bgt_un: len:30 +float_bge: len:32 +float_bge_un: len:13 +float_ble: len:32 +float_ble_un: len:13 +float_add: dest:f src1:f src2:f clob:1 len:5 +float_sub: dest:f src1:f src2:f clob:1 len:5 +float_mul: dest:f src1:f src2:f clob:1 len:5 +float_div: dest:f src1:f src2:f clob:1 len:5 +float_div_un: dest:f src1:f src2:f clob:1 len:5 +float_rem: dest:f src1:f src2:f clob:1 len:19 +float_rem_un: dest:f src1:f src2:f clob:1 len:19 +float_neg: dest:f src1:f clob:1 len:23 +float_not: dest:f src1:f clob:1 len:3 +float_conv_to_i1: dest:i src1:f len:49 +float_conv_to_i2: dest:i src1:f len:49 +float_conv_to_i4: dest:i src1:f len:49 +float_conv_to_i8: dest:i src1:f len:49 +float_conv_to_u4: dest:i src1:f len:49 +float_conv_to_u8: dest:i src1:f len:49 +float_conv_to_u2: dest:i src1:f len:49 +float_conv_to_u1: dest:i src1:f len:49 +float_conv_to_i: dest:i src1:f len:49 +float_conv_to_ovf_i: dest:a src1:f len:40 +float_conv_to_ovd_u: dest:a src1:f len:40 +float_mul_ovf: +float_ceq: dest:i src1:f src2:f len:35 +float_cgt: dest:i src1:f src2:f len:35 +float_cgt_un: dest:i src1:f src2:f len:48 +float_clt: dest:i src1:f src2:f len:35 +float_clt_un: dest:i src1:f src2:f len:42 +float_cneq: dest:i src1:f src2:f len:42 +float_cge: dest:i src1:f src2:f len:35 +float_cle: dest:i src1:f src2:f len:35 +float_ceq_membase: dest:i src1:f src2:b len:35 +float_cgt_membase: dest:i src1:f src2:b len:35 +float_cgt_un_membase: dest:i src1:f src2:b len:48 +float_clt_membase: dest:i src1:f src2:b len:35 +float_clt_un_membase: dest:i src1:f src2:b len:42 +float_conv_to_u: dest:i src1:f len:46 + +# R4 opcodes +r4_conv_to_i1: dest:i src1:f len:32 +r4_conv_to_u1: dest:i src1:f len:32 +r4_conv_to_i2: dest:i src1:f len:32 +r4_conv_to_u2: dest:i src1:f len:32 +r4_conv_to_i4: dest:i src1:f len:16 +r4_conv_to_u4: dest:i src1:f len:32 +r4_conv_to_i8: dest:i src1:f len:32 +r4_conv_to_r8: dest:f src1:f len:17 +r4_conv_to_r4: dest:f src1:f len:17 +r4_add: dest:f src1:f src2:f clob:1 len:5 +r4_sub: dest:f src1:f src2:f clob:1 len:5 +r4_mul: dest:f src1:f src2:f clob:1 len:5 +r4_div: dest:f src1:f src2:f clob:1 len:5 +r4_neg: dest:f src1:f clob:1 len:23 +r4_ceq: dest:i src1:f src2:f len:35 +r4_cgt: dest:i src1:f src2:f len:35 +r4_cgt_un: dest:i src1:f src2:f len:48 +r4_clt: dest:i src1:f src2:f len:35 +r4_clt_un: dest:i src1:f src2:f len:42 +r4_cneq: dest:i src1:f src2:f len:42 +r4_cge: dest:i src1:f src2:f len:35 +r4_cle: dest:i src1:f src2:f len:35 + +fmove: dest:f src1:f len:8 +rmove: dest:f src1:f len:8 +move_f_to_i4: dest:i src1:f len:16 +move_i4_to_f: dest:f src1:i len:16 +move_f_to_i8: dest:i src1:f len:5 +move_i8_to_f: dest:f src1:i len:5 +call_handler: len:14 clob:c +aot_const: dest:i len:10 +gc_safe_point: clob:c src1:i len:40 +x86_test_null: src1:i len:5 +x86_compare_membase_reg: src1:b src2:i len:9 +x86_compare_membase_imm: src1:b len:13 +x86_compare_reg_membase: src1:i src2:b len:8 +x86_inc_reg: dest:i src1:i clob:1 len:3 +x86_inc_membase: src1:b len:8 +x86_dec_reg: dest:i src1:i clob:1 len:3 +x86_dec_membase: src1:b len:8 +x86_add_membase_imm: src1:b len:13 +x86_sub_membase_imm: src1:b len:13 +x86_push: src1:i len:3 +x86_push_imm: len:6 +x86_push_membase: src1:b len:8 +x86_push_obj: src1:b len:40 +x86_lea: dest:i src1:i src2:i len:8 +x86_lea_membase: dest:i src1:i len:11 +x86_xchg: src1:i src2:i clob:x len:2 +x86_fpop: src1:f len:3 +x86_seteq_membase: src1:b len:9 + +x86_add_reg_membase: dest:i src1:i src2:b clob:1 len:13 +x86_sub_reg_membase: dest:i src1:i src2:b clob:1 len:13 +x86_mul_reg_membase: dest:i src1:i src2:b clob:1 len:13 +x86_and_reg_membase: dest:i src1:i src2:b clob:1 len:13 +x86_or_reg_membase: dest:i src1:i src2:b clob:1 len:13 +x86_xor_reg_membase: dest:i src1:i src2:b clob:1 len:13 + +amd64_test_null: src1:i len:5 +amd64_icompare_membase_reg: src1:b src2:i len:8 +amd64_icompare_membase_imm: src1:b len:13 +amd64_icompare_reg_membase: src1:i src2:b len:8 +amd64_set_xmmreg_r4: dest:f src1:f len:14 clob:m +amd64_set_xmmreg_r8: dest:f src1:f len:14 clob:m +amd64_save_sp_to_lmf: len:16 +tls_get: dest:i len:32 +tls_set: src1:i len:16 +atomic_add_i4: src1:b src2:i dest:i len:32 +atomic_add_i8: src1:b src2:i dest:i len:32 +atomic_exchange_i4: src1:b src2:i dest:i len:12 +atomic_exchange_i8: src1:b src2:i dest:i len:12 +atomic_cas_i4: src1:b src2:i src3:a dest:a len:24 +atomic_cas_i8: src1:b src2:i src3:a dest:a len:24 +memory_barrier: len:3 +atomic_load_i1: dest:c src1:b len:9 +atomic_load_u1: dest:c src1:b len:9 +atomic_load_i2: dest:i src1:b len:9 +atomic_load_u2: dest:i src1:b len:9 +atomic_load_i4: dest:i src1:b len:9 +atomic_load_u4: dest:i src1:b len:9 +atomic_load_i8: dest:i src1:b len:9 +atomic_load_u8: dest:i src1:b len:9 +atomic_load_r4: dest:f src1:b len:16 +atomic_load_r8: dest:f src1:b len:16 +atomic_store_i1: dest:b src1:c len:12 +atomic_store_u1: dest:b src1:c len:12 +atomic_store_i2: dest:b src1:i len:12 +atomic_store_u2: dest:b src1:i len:12 +atomic_store_i4: dest:b src1:i len:12 +atomic_store_u4: dest:b src1:i len:12 +atomic_store_i8: dest:b src1:i len:12 +atomic_store_u8: dest:b src1:i len:12 +atomic_store_r4: dest:b src1:f len:18 +atomic_store_r8: dest:b src1:f len:13 +adc: dest:i src1:i src2:i len:3 clob:1 +addcc: dest:i src1:i src2:i len:3 clob:1 +subcc: dest:i src1:i src2:i len:3 clob:1 +adc_imm: dest:i src1:i len:8 clob:1 +sbb: dest:i src1:i src2:i len:3 clob:1 +sbb_imm: dest:i src1:i len:8 clob:1 +br_reg: src1:i len:3 +sin: dest:f src1:f len:32 +cos: dest:f src1:f len:32 +abs: dest:f src1:f clob:1 len:32 +tan: dest:f src1:f len:59 +atan: dest:f src1:f len:9 +sqrt: dest:f src1:f len:32 +sext_i1: dest:i src1:i len:4 +sext_i2: dest:i src1:i len:4 +sext_i4: dest:i src1:i len:8 + +laddcc: dest:i src1:i src2:i len:3 clob:1 +lsubcc: dest:i src1:i src2:i len:3 clob:1 + +# 32 bit opcodes +int_add: dest:i src1:i src2:i clob:1 len:4 +int_sub: dest:i src1:i src2:i clob:1 len:4 +int_mul: dest:i src1:i src2:i clob:1 len:4 +int_mul_ovf: dest:i src1:i src2:i clob:1 len:32 +int_mul_ovf_un: dest:i src1:i src2:i clob:1 len:32 +int_div: dest:a src1:a src2:i clob:d len:32 +int_div_un: dest:a src1:a src2:i clob:d len:32 +int_rem: dest:d src1:a src2:i clob:a len:32 +int_rem_un: dest:d src1:a src2:i clob:a len:32 +int_and: dest:i src1:i src2:i clob:1 len:4 +int_or: dest:i src1:i src2:i clob:1 len:4 +int_xor: dest:i src1:i src2:i clob:1 len:4 +int_shl: dest:i src1:i src2:s clob:1 len:4 +int_shr: dest:i src1:i src2:s clob:1 len:4 +int_shr_un: dest:i src1:i src2:s clob:1 len:4 +int_adc: dest:i src1:i src2:i clob:1 len:4 +int_adc_imm: dest:i src1:i clob:1 len:8 +int_sbb: dest:i src1:i src2:i clob:1 len:4 +int_sbb_imm: dest:i src1:i clob:1 len:8 +int_addcc: dest:i src1:i src2:i clob:1 len:16 +int_subcc: dest:i src1:i src2:i clob:1 len:16 +int_add_imm: dest:i src1:i clob:1 len:8 +int_sub_imm: dest:i src1:i clob:1 len:8 +int_mul_imm: dest:i src1:i clob:1 len:32 +int_div_imm: dest:a src1:i clob:d len:32 +int_div_un_imm: dest:a src1:i clob:d len:32 +int_rem_un_imm: dest:d src1:i clob:a len:32 +int_and_imm: dest:i src1:i clob:1 len:8 +int_or_imm: dest:i src1:i clob:1 len:8 +int_xor_imm: dest:i src1:i clob:1 len:8 +int_shl_imm: dest:i src1:i clob:1 len:8 +int_shr_imm: dest:i src1:i clob:1 len:8 +int_shr_un_imm: dest:i src1:i clob:1 len:8 +int_min: dest:i src1:i src2:i len:16 clob:1 +int_max: dest:i src1:i src2:i len:16 clob:1 +int_min_un: dest:i src1:i src2:i len:16 clob:1 +int_max_un: dest:i src1:i src2:i len:16 clob:1 + +int_neg: dest:i src1:i clob:1 len:4 +int_not: dest:i src1:i clob:1 len:4 +int_conv_to_r4: dest:f src1:i len:15 +int_conv_to_r8: dest:f src1:i len:9 +int_ceq: dest:c len:8 +int_cgt: dest:c len:8 +int_cgt_un: dest:c len:8 +int_clt: dest:c len:8 +int_clt_un: dest:c len:8 + +int_cneq: dest:c len:8 +int_cge: dest:c len:8 +int_cle: dest:c len:8 +int_cge_un: dest:c len:8 +int_cle_un: dest:c len:8 + +int_beq: len:8 +int_bne_un: len:8 +int_blt: len:8 +int_blt_un: len:8 +int_bgt: len:8 +int_bgt_un: len:8 +int_bge: len:8 +int_bge_un: len:8 +int_ble: len:8 +int_ble_un: len:8 + +card_table_wbarrier: src1:a src2:i clob:d len:56 + +relaxed_nop: len:2 +hard_nop: len:1 + +# Linear IR opcodes +nop: len:0 +dummy_use: src1:i len:0 +dummy_store: len:0 +dummy_iconst: dest:i len:0 +dummy_r8const: dest:f len:0 +not_reached: len:0 +not_null: src1:i len:0 + +long_ceq: dest:c len:64 +long_cgt: dest:c len:64 +long_cgt_un: dest:c len:64 +long_clt: dest:c len:64 +long_clt_un: dest:c len:64 + +int_conv_to_i1: dest:i src1:i len:4 +int_conv_to_i2: dest:i src1:i len:4 +int_conv_to_i4: dest:i src1:i len:3 +int_conv_to_i8: dest:i src1:i len:3 +int_conv_to_u4: dest:i src1:i len:3 +int_conv_to_u8: dest:i src1:i len:3 + +int_conv_to_u: dest:i src1:i len:4 +int_conv_to_u2: dest:i src1:i len:4 +int_conv_to_u1: dest:i src1:i len:4 +int_conv_to_i: dest:i src1:i len:4 + +cond_exc_ieq: len:8 +cond_exc_ine_un: len:8 +cond_exc_ilt: len:8 +cond_exc_ilt_un: len:8 +cond_exc_igt: len:8 +cond_exc_igt_un: len:8 +cond_exc_ige: len:8 +cond_exc_ige_un: len:8 +cond_exc_ile: len:8 +cond_exc_ile_un: len:8 +cond_exc_ino: len:8 +cond_exc_inc: len:8 + +x86_compare_membase8_imm: src1:b len:9 + +jump_table: dest:i len:18 + +cmov_ieq: dest:i src1:i src2:i len:16 clob:1 +cmov_ige: dest:i src1:i src2:i len:16 clob:1 +cmov_igt: dest:i src1:i src2:i len:16 clob:1 +cmov_ile: dest:i src1:i src2:i len:16 clob:1 +cmov_ilt: dest:i src1:i src2:i len:16 clob:1 +cmov_ine_un: dest:i src1:i src2:i len:16 clob:1 +cmov_ige_un: dest:i src1:i src2:i len:16 clob:1 +cmov_igt_un: dest:i src1:i src2:i len:16 clob:1 +cmov_ile_un: dest:i src1:i src2:i len:16 clob:1 +cmov_ilt_un: dest:i src1:i src2:i len:16 clob:1 + +cmov_leq: dest:i src1:i src2:i len:16 clob:1 +cmov_lge: dest:i src1:i src2:i len:16 clob:1 +cmov_lgt: dest:i src1:i src2:i len:16 clob:1 +cmov_lle: dest:i src1:i src2:i len:16 clob:1 +cmov_llt: dest:i src1:i src2:i len:16 clob:1 +cmov_lne_un: dest:i src1:i src2:i len:16 clob:1 +cmov_lge_un: dest:i src1:i src2:i len:16 clob:1 +cmov_lgt_un: dest:i src1:i src2:i len:16 clob:1 +cmov_lle_un: dest:i src1:i src2:i len:16 clob:1 +cmov_llt_un: dest:i src1:i src2:i len:16 clob:1 + +long_add_imm: dest:i src1:i clob:1 len:12 +long_sub_imm: dest:i src1:i clob:1 len:12 +long_and_imm: dest:i src1:i clob:1 len:12 +long_or_imm: dest:i src1:i clob:1 len:12 +long_xor_imm: dest:i src1:i clob:1 len:12 + +lcompare_imm: src1:i len:13 + +amd64_compare_membase_reg: src1:b src2:i len:9 +amd64_compare_membase_imm: src1:b len:14 +amd64_compare_reg_membase: src1:i src2:b len:9 + +amd64_add_reg_membase: dest:i src1:i src2:b clob:1 len:14 +amd64_sub_reg_membase: dest:i src1:i src2:b clob:1 len:14 +amd64_and_reg_membase: dest:i src1:i src2:b clob:1 len:14 +amd64_or_reg_membase: dest:i src1:i src2:b clob:1 len:14 +amd64_xor_reg_membase: dest:i src1:i src2:b clob:1 len:14 + +amd64_add_membase_imm: src1:b len:16 +amd64_sub_membase_imm: src1:b len:16 +amd64_and_membase_imm: src1:b len:13 +amd64_or_membase_imm: src1:b len:13 +amd64_xor_membase_imm: src1:b len:13 + +x86_and_membase_imm: src1:b len:12 +x86_or_membase_imm: src1:b len:12 +x86_xor_membase_imm: src1:b len:12 + +x86_add_membase_reg: src1:b src2:i len:12 +x86_sub_membase_reg: src1:b src2:i len:12 +x86_and_membase_reg: src1:b src2:i len:12 +x86_or_membase_reg: src1:b src2:i len:12 +x86_xor_membase_reg: src1:b src2:i len:12 +x86_mul_membase_reg: src1:b src2:i len:14 + +amd64_add_membase_reg: src1:b src2:i len:13 +amd64_sub_membase_reg: src1:b src2:i len:13 +amd64_and_membase_reg: src1:b src2:i len:13 +amd64_or_membase_reg: src1:b src2:i len:13 +amd64_xor_membase_reg: src1:b src2:i len:13 +amd64_mul_membase_reg: src1:b src2:i len:15 + +float_conv_to_r4: dest:f src1:f len:17 + +vcall2: len:64 clob:c +vcall2_reg: src1:i len:64 clob:c +vcall2_membase: src1:b len:64 clob:c + +dyn_call: src1:i src2:i len:192 clob:c + +localloc_imm: dest:i len:120 + +load_mem: dest:i len:16 +loadi8_mem: dest:i len:16 +loadi4_mem: dest:i len:16 +loadu1_mem: dest:i len:16 +loadu2_mem: dest:i len:16 + + +#SIMD + +addps: dest:x src1:x src2:x len:4 clob:1 +divps: dest:x src1:x src2:x len:4 clob:1 +mulps: dest:x src1:x src2:x len:4 clob:1 +subps: dest:x src1:x src2:x len:4 clob:1 +maxps: dest:x src1:x src2:x len:4 clob:1 +minps: dest:x src1:x src2:x len:4 clob:1 +compps: dest:x src1:x src2:x len:5 clob:1 +andps: dest:x src1:x src2:x len:4 clob:1 +andnps: dest:x src1:x src2:x len:4 clob:1 +orps: dest:x src1:x src2:x len:4 clob:1 +xorps: dest:x src1:x src2:x len:4 clob:1 + +haddps: dest:x src1:x src2:x len:5 clob:1 +hsubps: dest:x src1:x src2:x len:5 clob:1 +addsubps: dest:x src1:x src2:x len:5 clob:1 +dupps_low: dest:x src1:x len:5 +dupps_high: dest:x src1:x len:5 + +addpd: dest:x src1:x src2:x len:5 clob:1 +divpd: dest:x src1:x src2:x len:5 clob:1 +mulpd: dest:x src1:x src2:x len:5 clob:1 +subpd: dest:x src1:x src2:x len:5 clob:1 +maxpd: dest:x src1:x src2:x len:5 clob:1 +minpd: dest:x src1:x src2:x len:5 clob:1 +comppd: dest:x src1:x src2:x len:6 clob:1 +andpd: dest:x src1:x src2:x len:5 clob:1 +andnpd: dest:x src1:x src2:x len:5 clob:1 +orpd: dest:x src1:x src2:x len:5 clob:1 +xorpd: dest:x src1:x src2:x len:5 clob:1 +sqrtpd: dest:x src1:x len:5 clob:1 + +haddpd: dest:x src1:x src2:x len:6 clob:1 +hsubpd: dest:x src1:x src2:x len:6 clob:1 +addsubpd: dest:x src1:x src2:x len:6 clob:1 +duppd: dest:x src1:x len:6 + +pand: dest:x src1:x src2:x len:5 clob:1 +por: dest:x src1:x src2:x len:5 clob:1 +pxor: dest:x src1:x src2:x len:5 clob:1 + +sqrtps: dest:x src1:x len:5 +rsqrtps: dest:x src1:x len:5 +rcpps: dest:x src1:x len:5 + +pshufflew_high: dest:x src1:x len:6 +pshufflew_low: dest:x src1:x len:6 +pshuffled: dest:x src1:x len:6 +shufps: dest:x src1:x src2:x len:5 clob:1 +shufpd: dest:x src1:x src2:x len:6 clob:1 + +extract_mask: dest:i src1:x len:6 + +paddb: dest:x src1:x src2:x len:5 clob:1 +paddw: dest:x src1:x src2:x len:5 clob:1 +paddd: dest:x src1:x src2:x len:5 clob:1 +paddq: dest:x src1:x src2:x len:5 clob:1 + +psubb: dest:x src1:x src2:x len:5 clob:1 +psubw: dest:x src1:x src2:x len:5 clob:1 +psubd: dest:x src1:x src2:x len:5 clob:1 +psubq: dest:x src1:x src2:x len:5 clob:1 + +pmaxb_un: dest:x src1:x src2:x len:5 clob:1 +pmaxw_un: dest:x src1:x src2:x len:6 clob:1 +pmaxd_un: dest:x src1:x src2:x len:6 clob:1 + +pmaxb: dest:x src1:x src2:x len:6 clob:1 +pmaxw: dest:x src1:x src2:x len:5 clob:1 +pmaxd: dest:x src1:x src2:x len:6 clob:1 + +pavgb_un: dest:x src1:x src2:x len:5 clob:1 +pavgw_un: dest:x src1:x src2:x len:5 clob:1 + +pminb_un: dest:x src1:x src2:x len:5 clob:1 +pminw_un: dest:x src1:x src2:x len:6 clob:1 +pmind_un: dest:x src1:x src2:x len:6 clob:1 + +pminb: dest:x src1:x src2:x len:6 clob:1 +pminw: dest:x src1:x src2:x len:5 clob:1 +pmind: dest:x src1:x src2:x len:6 clob:1 + +pcmpeqb: dest:x src1:x src2:x len:5 clob:1 +pcmpeqw: dest:x src1:x src2:x len:5 clob:1 +pcmpeqd: dest:x src1:x src2:x len:5 clob:1 +pcmpeqq: dest:x src1:x src2:x len:6 clob:1 + +pcmpgtb: dest:x src1:x src2:x len:5 clob:1 +pcmpgtw: dest:x src1:x src2:x len:5 clob:1 +pcmpgtd: dest:x src1:x src2:x len:5 clob:1 +pcmpgtq: dest:x src1:x src2:x len:6 clob:1 + +psumabsdiff: dest:x src1:x src2:x len:5 clob:1 + +unpack_lowb: dest:x src1:x src2:x len:5 clob:1 +unpack_loww: dest:x src1:x src2:x len:5 clob:1 +unpack_lowd: dest:x src1:x src2:x len:5 clob:1 +unpack_lowq: dest:x src1:x src2:x len:5 clob:1 +unpack_lowps: dest:x src1:x src2:x len:5 clob:1 +unpack_lowpd: dest:x src1:x src2:x len:5 clob:1 + +unpack_highb: dest:x src1:x src2:x len:5 clob:1 +unpack_highw: dest:x src1:x src2:x len:5 clob:1 +unpack_highd: dest:x src1:x src2:x len:5 clob:1 +unpack_highq: dest:x src1:x src2:x len:5 clob:1 +unpack_highps: dest:x src1:x src2:x len:5 clob:1 +unpack_highpd: dest:x src1:x src2:x len:5 clob:1 + +packw: dest:x src1:x src2:x len:5 clob:1 +packd: dest:x src1:x src2:x len:5 clob:1 + +packw_un: dest:x src1:x src2:x len:5 clob:1 +packd_un: dest:x src1:x src2:x len:6 clob:1 + +paddb_sat: dest:x src1:x src2:x len:5 clob:1 +paddb_sat_un: dest:x src1:x src2:x len:5 clob:1 + +paddw_sat: dest:x src1:x src2:x len:5 clob:1 +paddw_sat_un: dest:x src1:x src2:x len:5 clob:1 + +psubb_sat: dest:x src1:x src2:x len:5 clob:1 +psubb_sat_un: dest:x src1:x src2:x len:5 clob:1 + +psubw_sat: dest:x src1:x src2:x len:5 clob:1 +psubw_sat_un: dest:x src1:x src2:x len:5 clob:1 + +pmulw: dest:x src1:x src2:x len:5 clob:1 +pmuld: dest:x src1:x src2:x len:6 clob:1 +pmulq: dest:x src1:x src2:x len:5 clob:1 + +pmul_high_un: dest:x src1:x src2:x len:5 clob:1 +pmul_high: dest:x src1:x src2:x len:5 clob:1 + +pshrw: dest:x src1:x len:6 clob:1 +pshrw_reg: dest:x src1:x src2:x len:5 clob:1 + +psarw: dest:x src1:x len:6 clob:1 +psarw_reg: dest:x src1:x src2:x len:5 clob:1 + +pshlw: dest:x src1:x len:6 clob:1 +pshlw_reg: dest:x src1:x src2:x len:5 clob:1 + +pshrd: dest:x src1:x len:6 clob:1 +pshrd_reg: dest:x src1:x src2:x len:5 clob:1 + +psard: dest:x src1:x len:6 clob:1 +psard_reg: dest:x src1:x src2:x len:5 clob:1 + +pshld: dest:x src1:x len:6 clob:1 +pshld_reg: dest:x src1:x src2:x len:5 clob:1 + +pshrq: dest:x src1:x len:6 clob:1 +pshrq_reg: dest:x src1:x src2:x len:5 clob:1 + +pshlq: dest:x src1:x len:6 clob:1 +pshlq_reg: dest:x src1:x src2:x len:5 clob:1 + +cvtdq2pd: dest:x src1:x len:5 clob:1 +cvtdq2ps: dest:x src1:x len:4 clob:1 +cvtpd2dq: dest:x src1:x len:5 clob:1 +cvtpd2ps: dest:x src1:x len:5 clob:1 +cvtps2dq: dest:x src1:x len:5 clob:1 +cvtps2pd: dest:x src1:x len:4 clob:1 +cvttpd2dq: dest:x src1:x len:5 clob:1 +cvttps2dq: dest:x src1:x len:5 clob:1 + +xmove: dest:x src1:x len:5 +xzero: dest:x len:5 +xones: dest:x len:5 + +iconv_to_x: dest:x src1:i len:5 +extract_i4: dest:i src1:x len:5 + +extract_i8: dest:i src1:x len:9 + +extract_i2: dest:i src1:x len:13 +extract_u2: dest:i src1:x len:13 +extract_i1: dest:i src1:x len:13 +extract_u1: dest:i src1:x len:13 +extract_r8: dest:f src1:x len:5 + +iconv_to_r4_raw: dest:f src1:i len:10 + +insert_i2: dest:x src1:x src2:i len:6 clob:1 + +extractx_u2: dest:i src1:x len:6 +insertx_u1_slow: dest:x src1:i src2:i len:18 clob:x + +insertx_i4_slow: dest:x src1:x src2:i len:16 clob:x +insertx_i8_slow: dest:x src1:x src2:i len:13 +insertx_r4_slow: dest:x src1:x src2:f len:24 +insertx_r8_slow: dest:x src1:x src2:f len:24 + +loadx_membase: dest:x src1:b len:9 +storex_membase: dest:b src1:x len:9 +storex_membase_reg: dest:b src1:x len:9 + +loadx_aligned_membase: dest:x src1:b len:7 +storex_aligned_membase_reg: dest:b src1:x len:7 +storex_nta_membase_reg: dest:b src1:x len:7 + +fconv_to_r8_x: dest:x src1:f len:4 +xconv_r8_to_i4: dest:y src1:x len:7 + +prefetch_membase: src1:b len:4 + +expand_i2: dest:x src1:i len:18 +expand_i4: dest:x src1:i len:11 +expand_i8: dest:x src1:i len:11 +expand_r4: dest:x src1:f len:16 +expand_r8: dest:x src1:f len:13 + +liverange_start: len:0 +liverange_end: len:0 +gc_liveness_def: len:0 +gc_liveness_use: len:0 +gc_spill_slot_liveness_def: len:0 +gc_param_slot_liveness_def: len:0 + +generic_class_init: src1:A len:32 clob:c +get_last_error: dest:i len:32 + +fill_prof_call_ctx: src1:i len:128 diff --git a/unity-2019.4.24f1-mbe/mono/mini/cpu-arm.md b/unity-2019.4.24f1-mbe/mono/mini/cpu-arm.md new file mode 100644 index 000000000..e6a91fc17 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/cpu-arm.md @@ -0,0 +1,408 @@ +# Copyright 2003-2011 Novell, Inc (http://www.novell.com) +# Copyright 2011 Xamarin, Inc (http://www.xamarin.com) +# Licensed under the MIT license. See LICENSE file in the project root for full license information. +# arm cpu description file +# this file is read by genmdesc to pruduce a table with all the relevant information +# about the cpu instructions that may be used by the regsiter allocator, the scheduler +# and other parts of the arch-dependent part of mini. +# +# An opcode name is followed by a colon and optional specifiers. +# A specifier has a name, a colon and a value. Specifiers are separated by white space. +# Here is a description of the specifiers valid for this file and their possible values. +# +# dest:register describes the destination register of an instruction +# src1:register describes the first source register of an instruction +# src2:register describes the second source register of an instruction +# +# register may have the following values: +# i integer register +# a r0 register (first argument/result reg) +# b base register (used in address references) +# f floating point register +# g floating point register returned in r0:r1 for soft-float mode +# +# len:number describe the maximun length in bytes of the instruction +# number is a positive integer +# +# cost:number describe how many cycles are needed to complete the instruction (unused) +# +# clob:spec describe if the instruction clobbers registers or has special needs +# +# spec can be one of the following characters: +# c clobbers caller-save registers +# r 'reserves' the destination register until a later instruction unreserves it +# used mostly to set output registers in function calls +# +# flags:spec describe if the instruction uses or sets the flags (unused) +# +# spec can be one of the following chars: +# s sets the flags +# u uses the flags +# m uses and modifies the flags +# +# res:spec describe what units are used in the processor (unused) +# +# delay: describe delay slots (unused) +# +# the required specifiers are: len, clob (if registers are clobbered), the registers +# specifiers if the registers are actually used, flags (when scheduling is implemented). +# +# See the code in mini-x86.c for more details on how the specifiers are used. +# +nop: len:4 +relaxed_nop: len:4 +break: len:4 +br: len:16 +switch: src1:i len:12 +# See the comment in resume_from_signal_handler, we can't copy the fp regs from sigctx to MonoContext on linux, +# since the corresponding sigctx structures are not well defined. +seq_point: len:52 clob:c +il_seq_point: len:0 + +throw: src1:i len:24 +rethrow: src1:i len:20 +start_handler: len:20 +endfinally: len:32 +call_handler: len:16 clob:c +endfilter: src1:i len:16 +get_ex_obj: dest:i len:16 + +ckfinite: dest:f src1:f len:112 +ceq: dest:i len:12 +cgt: dest:i len:12 +cgt.un: dest:i len:12 +clt: dest:i len:12 +clt.un: dest:i len:12 +localloc: dest:i src1:i len:60 +compare: src1:i src2:i len:4 +compare_imm: src1:i len:12 +fcompare: src1:f src2:f len:12 +rcompare: src1:f src2:f len:12 +oparglist: src1:i len:12 +setlret: src1:i src2:i len:12 +checkthis: src1:b len:4 +call: dest:a clob:c len:20 +call_reg: dest:a src1:i len:8 clob:c +call_membase: dest:a src1:b len:30 clob:c +voidcall: len:20 clob:c +voidcall_reg: src1:i len:8 clob:c +voidcall_membase: src1:b len:24 clob:c +fcall: dest:g len:28 clob:c +fcall_reg: dest:g src1:i len:16 clob:c +fcall_membase: dest:g src1:b len:30 clob:c +rcall: dest:g len:28 clob:c +rcall_reg: dest:g src1:i len:16 clob:c +rcall_membase: dest:g src1:b len:30 clob:c +lcall: dest:l len:20 clob:c +lcall_reg: dest:l src1:i len:8 clob:c +lcall_membase: dest:l src1:b len:24 clob:c +vcall: len:64 clob:c +vcall_reg: src1:i len:64 clob:c +vcall_membase: src1:b len:70 clob:c +tailcall: len:160 clob:c +iconst: dest:i len:16 +r4const: dest:f len:24 +r8const: dest:f len:20 +label: len:0 +store_membase_imm: dest:b len:20 +store_membase_reg: dest:b src1:i len:20 +storei1_membase_imm: dest:b len:20 +storei1_membase_reg: dest:b src1:i len:12 +storei2_membase_imm: dest:b len:20 +storei2_membase_reg: dest:b src1:i len:12 +storei4_membase_imm: dest:b len:20 +storei4_membase_reg: dest:b src1:i len:20 +storei8_membase_imm: dest:b +storei8_membase_reg: dest:b src1:i +storer4_membase_reg: dest:b src1:f len:60 +storer8_membase_reg: dest:b src1:f len:24 +store_memindex: dest:b src1:i src2:i len:4 +storei1_memindex: dest:b src1:i src2:i len:4 +storei2_memindex: dest:b src1:i src2:i len:4 +storei4_memindex: dest:b src1:i src2:i len:4 +load_membase: dest:i src1:b len:20 +loadi1_membase: dest:i src1:b len:4 +loadu1_membase: dest:i src1:b len:4 +loadi2_membase: dest:i src1:b len:4 +loadu2_membase: dest:i src1:b len:4 +loadi4_membase: dest:i src1:b len:4 +loadu4_membase: dest:i src1:b len:4 +loadi8_membase: dest:i src1:b +loadr4_membase: dest:f src1:b len:56 +loadr8_membase: dest:f src1:b len:24 +load_memindex: dest:i src1:b src2:i len:4 +loadi1_memindex: dest:i src1:b src2:i len:4 +loadu1_memindex: dest:i src1:b src2:i len:4 +loadi2_memindex: dest:i src1:b src2:i len:4 +loadu2_memindex: dest:i src1:b src2:i len:4 +loadi4_memindex: dest:i src1:b src2:i len:4 +loadu4_memindex: dest:i src1:b src2:i len:4 +loadu4_mem: dest:i len:8 +move: dest:i src1:i len:4 +fmove: dest:f src1:f len:4 +move_f_to_i4: dest:i src1:f len:28 +move_i4_to_f: dest:f src1:i len:8 +add_imm: dest:i src1:i len:12 +sub_imm: dest:i src1:i len:12 +mul_imm: dest:i src1:i len:12 +and_imm: dest:i src1:i len:12 +or_imm: dest:i src1:i len:12 +xor_imm: dest:i src1:i len:12 +shl_imm: dest:i src1:i len:8 +shr_imm: dest:i src1:i len:8 +shr_un_imm: dest:i src1:i len:8 +cond_exc_eq: len:8 +cond_exc_ne_un: len:8 +cond_exc_lt: len:8 +cond_exc_lt_un: len:8 +cond_exc_gt: len:8 +cond_exc_gt_un: len:8 +cond_exc_ge: len:8 +cond_exc_ge_un: len:8 +cond_exc_le: len:8 +cond_exc_le_un: len:8 +cond_exc_ov: len:12 +cond_exc_no: len:8 +cond_exc_c: len:12 +cond_exc_nc: len:8 +#float_beq: src1:f src2:f len:20 +#float_bne_un: src1:f src2:f len:20 +#float_blt: src1:f src2:f len:20 +#float_blt_un: src1:f src2:f len:20 +#float_bgt: src1:f src2:f len:20 +#float_bgt_un: src1:f src2:f len:20 +#float_bge: src1:f src2:f len:20 +#float_bge_un: src1:f src2:f len:20 +#float_ble: src1:f src2:f len:20 +#float_ble_un: src1:f src2:f len:20 +float_add: dest:f src1:f src2:f len:4 +float_sub: dest:f src1:f src2:f len:4 +float_mul: dest:f src1:f src2:f len:4 +float_div: dest:f src1:f src2:f len:4 +float_div_un: dest:f src1:f src2:f len:4 +float_rem: dest:f src1:f src2:f len:16 +float_rem_un: dest:f src1:f src2:f len:16 +float_neg: dest:f src1:f len:4 +float_not: dest:f src1:f len:4 +float_conv_to_i1: dest:i src1:f len:88 +float_conv_to_i2: dest:i src1:f len:88 +float_conv_to_i4: dest:i src1:f len:88 +float_conv_to_i8: dest:l src1:f len:88 +float_conv_to_r4: dest:f src1:f len:8 +float_conv_to_u4: dest:i src1:f len:88 +float_conv_to_u8: dest:l src1:f len:88 +float_conv_to_u2: dest:i src1:f len:88 +float_conv_to_u1: dest:i src1:f len:88 +float_conv_to_i: dest:i src1:f len:40 +float_ceq: dest:i src1:f src2:f len:16 +float_cgt: dest:i src1:f src2:f len:16 +float_cgt_un: dest:i src1:f src2:f len:20 +float_clt: dest:i src1:f src2:f len:16 +float_clt_un: dest:i src1:f src2:f len:20 +float_cneq: dest:y src1:f src2:f len:20 +float_cge: dest:y src1:f src2:f len:20 +float_cle: dest:y src1:f src2:f len:20 +float_conv_to_u: dest:i src1:f len:36 + +# R4 opcodes +rmove: dest:f src1:f len:4 +r4_conv_to_i1: dest:i src1:f len:88 +r4_conv_to_i2: dest:i src1:f len:88 +r4_conv_to_i4: dest:i src1:f len:88 +r4_conv_to_u1: dest:i src1:f len:88 +r4_conv_to_u2: dest:i src1:f len:88 +r4_conv_to_u4: dest:i src1:f len:88 +r4_conv_to_r4: dest:f src1:f len:16 +r4_conv_to_r8: dest:f src1:f len:16 +r4_add: dest:f src1:f src2:f len:4 +r4_sub: dest:f src1:f src2:f len:4 +r4_mul: dest:f src1:f src2:f len:4 +r4_div: dest:f src1:f src2:f len:4 +r4_rem: dest:f src1:f src2:f len:16 +r4_neg: dest:f src1:f len:4 +r4_ceq: dest:i src1:f src2:f len:16 +r4_cgt: dest:i src1:f src2:f len:16 +r4_cgt_un: dest:i src1:f src2:f len:20 +r4_clt: dest:i src1:f src2:f len:16 +r4_clt_un: dest:i src1:f src2:f len:20 +r4_cneq: dest:y src1:f src2:f len:20 +r4_cge: dest:y src1:f src2:f len:20 +r4_cle: dest:y src1:f src2:f len:20 + +setfret: src1:f len:12 +aot_const: dest:i len:16 +objc_get_selector: dest:i len:32 +sqrt: dest:f src1:f len:4 +adc: dest:i src1:i src2:i len:4 +addcc: dest:i src1:i src2:i len:4 +subcc: dest:i src1:i src2:i len:4 +adc_imm: dest:i src1:i len:12 +addcc_imm: dest:i src1:i len:12 +subcc_imm: dest:i src1:i len:12 +sbb: dest:i src1:i src2:i len:4 +sbb_imm: dest:i src1:i len:12 +br_reg: src1:i len:8 +bigmul: len:8 dest:l src1:i src2:i +bigmul_un: len:8 dest:l src1:i src2:i +tls_get: len:16 dest:i +tls_set: len:16 src1:i clob:c + +# 32 bit opcodes +int_add: dest:i src1:i src2:i len:4 +int_sub: dest:i src1:i src2:i len:4 +int_mul: dest:i src1:i src2:i len:4 +int_div: dest:i src1:i src2:i len:4 +int_div_un: dest:i src1:i src2:i len:4 +int_rem: dest:i src1:i src2:i len:8 +int_rem_un: dest:i src1:i src2:i len:8 +int_and: dest:i src1:i src2:i len:4 +int_or: dest:i src1:i src2:i len:4 +int_xor: dest:i src1:i src2:i len:4 +int_shl: dest:i src1:i src2:i len:4 +int_shr: dest:i src1:i src2:i len:4 +int_shr_un: dest:i src1:i src2:i len:4 +int_neg: dest:i src1:i len:4 +int_not: dest:i src1:i len:4 +int_conv_to_i1: dest:i src1:i len:8 +int_conv_to_i2: dest:i src1:i len:8 +int_conv_to_i4: dest:i src1:i len:4 +int_conv_to_r4: dest:f src1:i len:84 +int_conv_to_r8: dest:f src1:i len:84 +int_conv_to_u4: dest:i src1:i +int_conv_to_r_un: dest:f src1:i len:56 +int_conv_to_u2: dest:i src1:i len:8 +int_conv_to_u1: dest:i src1:i len:4 +int_beq: len:16 +int_bge: len:16 +int_bgt: len:16 +int_ble: len:16 +int_blt: len:16 +int_bne_un: len:16 +int_bge_un: len:16 +int_bgt_un: len:16 +int_ble_un: len:16 +int_blt_un: len:16 +int_add_ovf: dest:i src1:i src2:i len:16 +int_add_ovf_un: dest:i src1:i src2:i len:16 +int_mul_ovf: dest:i src1:i src2:i len:16 +int_mul_ovf_un: dest:i src1:i src2:i len:16 +int_sub_ovf: dest:i src1:i src2:i len:16 +int_sub_ovf_un: dest:i src1:i src2:i len:16 +add_ovf_carry: dest:i src1:i src2:i len:16 +sub_ovf_carry: dest:i src1:i src2:i len:16 +add_ovf_un_carry: dest:i src1:i src2:i len:16 +sub_ovf_un_carry: dest:i src1:i src2:i len:16 + +arm_rsbs_imm: dest:i src1:i len:4 +arm_rsc_imm: dest:i src1:i len:4 + +# Linear IR opcodes +dummy_use: src1:i len:0 +dummy_store: len:0 +dummy_iconst: dest:i len:0 +dummy_r8const: dest:f len:0 +not_reached: len:0 +not_null: src1:i len:0 + +int_adc: dest:i src1:i src2:i len:4 +int_addcc: dest:i src1:i src2:i len:4 +int_subcc: dest:i src1:i src2:i len:4 +int_sbb: dest:i src1:i src2:i len:4 +int_adc_imm: dest:i src1:i len:12 +int_sbb_imm: dest:i src1:i len:12 + +int_add_imm: dest:i src1:i len:12 +int_sub_imm: dest:i src1:i len:12 +int_mul_imm: dest:i src1:i len:12 +int_div_imm: dest:i src1:i len:20 +int_div_un_imm: dest:i src1:i len:12 +int_rem_imm: dest:i src1:i len:28 +int_rem_un_imm: dest:i src1:i len:16 +int_and_imm: dest:i src1:i len:12 +int_or_imm: dest:i src1:i len:12 +int_xor_imm: dest:i src1:i len:12 +int_shl_imm: dest:i src1:i len:8 +int_shr_imm: dest:i src1:i len:8 +int_shr_un_imm: dest:i src1:i len:8 + +int_ceq: dest:i len:12 +int_cgt: dest:i len:12 +int_cgt_un: dest:i len:12 +int_clt: dest:i len:12 +int_clt_un: dest:i len:12 + +int_cneq: dest:i len:12 +int_cge: dest:i len:12 +int_cle: dest:i len:12 +int_cge_un: dest:i len:12 +int_cle_un: dest:i len:12 + +cond_exc_ieq: len:16 +cond_exc_ine_un: len:16 +cond_exc_ilt: len:16 +cond_exc_ilt_un: len:16 +cond_exc_igt: len:16 +cond_exc_igt_un: len:16 +cond_exc_ige: len:16 +cond_exc_ige_un: len:16 +cond_exc_ile: len:16 +cond_exc_ile_un: len:16 +cond_exc_iov: len:20 +cond_exc_ino: len:16 +cond_exc_ic: len:20 +cond_exc_inc: len:16 + +icompare: src1:i src2:i len:4 +icompare_imm: src1:i len:12 + +long_conv_to_ovf_i4_2: dest:i src1:i src2:i len:36 + +vcall2: len:64 clob:c +vcall2_reg: src1:i len:64 clob:c +vcall2_membase: src1:b len:64 clob:c +dyn_call: src1:i src2:i len:252 clob:c + +# This is different from the original JIT opcodes +float_beq: len:32 +float_bne_un: len:32 +float_blt: len:32 +float_blt_un: len:32 +float_bgt: len:32 +float_bgt_un: len:32 +float_bge: len:32 +float_bge_un: len:32 +float_ble: len:32 +float_ble_un: len:32 + +liverange_start: len:0 +liverange_end: len:0 +gc_liveness_def: len:0 +gc_liveness_use: len:0 +gc_spill_slot_liveness_def: len:0 +gc_param_slot_liveness_def: len:0 +gc_safe_point: clob:c src1:i len:40 + +atomic_add_i4: dest:i src1:i src2:i len:64 +atomic_exchange_i4: dest:i src1:i src2:i len:64 +atomic_cas_i4: dest:i src1:i src2:i src3:i len:64 +memory_barrier: len:8 clob:a +atomic_load_i1: dest:i src1:b len:28 +atomic_load_u1: dest:i src1:b len:28 +atomic_load_i2: dest:i src1:b len:28 +atomic_load_u2: dest:i src1:b len:28 +atomic_load_i4: dest:i src1:b len:28 +atomic_load_u4: dest:i src1:b len:28 +atomic_load_r4: dest:f src1:b len:80 +atomic_load_r8: dest:f src1:b len:32 +atomic_store_i1: dest:b src1:i len:28 +atomic_store_u1: dest:b src1:i len:28 +atomic_store_i2: dest:b src1:i len:28 +atomic_store_u2: dest:b src1:i len:28 +atomic_store_i4: dest:b src1:i len:28 +atomic_store_u4: dest:b src1:i len:28 +atomic_store_r4: dest:b src1:f len:80 +atomic_store_r8: dest:b src1:f len:32 + +generic_class_init: src1:a len:44 clob:c + +fill_prof_call_ctx: src1:i len:128 diff --git a/unity-2019.4.24f1-mbe/mono/mini/cpu-arm64.md b/unity-2019.4.24f1-mbe/mono/mini/cpu-arm64.md new file mode 100644 index 000000000..840d26067 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/cpu-arm64.md @@ -0,0 +1,484 @@ +# Copyright 2011-2013 Xamarin, Inc (http://www.xamarin.com) +# Copyright 2003-2011 Novell, Inc (http://www.novell.com) +# Licensed under the MIT license. See LICENSE file in the project root for full license information. +# arm64 cpu description file +# this file is read by genmdesc to pruduce a table with all the relevant information +# about the cpu instructions that may be used by the regsiter allocator, the scheduler +# and other parts of the arch-dependent part of mini. +# +# An opcode name is followed by a colon and optional specifiers. +# A specifier has a name, a colon and a value. Specifiers are separated by white space. +# Here is a description of the specifiers valid for this file and their possible values. +# +# dest:register describes the destination register of an instruction +# src1:register describes the first source register of an instruction +# src2:register describes the second source register of an instruction +# +# register may have the following values: +# i integer register +# a r3 register (output from calls) +# b base register (used in address references) +# f floating point register +# g floating point register returned in r0:r1 for soft-float mode +# +# len:number describe the maximun length in bytes of the instruction +# number is a positive integer +# +# cost:number describe how many cycles are needed to complete the instruction (unused) +# +# clob:spec describe if the instruction clobbers registers or has special needs +# +# spec can be one of the following characters: +# c clobbers caller-save registers +# r 'reserves' the destination register until a later instruction unreserves it +# used mostly to set output registers in function calls +# +# flags:spec describe if the instruction uses or sets the flags (unused) +# +# spec can be one of the following chars: +# s sets the flags +# u uses the flags +# m uses and modifies the flags +# +# res:spec describe what units are used in the processor (unused) +# +# delay: describe delay slots (unused) +# +# the required specifiers are: len, clob (if registers are clobbered), the registers +# specifiers if the registers are actually used, flags (when scheduling is implemented). +# +# See the code in mini-x86.c for more details on how the specifiers are used. +# +nop: len:4 +relaxed_nop: len:4 +break: len:20 +jmp: len:92 +br: len:16 +switch: src1:i len:12 +# See the comment in resume_from_signal_handler, we can't copy the fp regs from sigctx to MonoContext on linux, +# since the corresponding sigctx structures are not well defined. +seq_point: len:40 clob:c +il_seq_point: len:0 + +throw: src1:i len:24 +rethrow: src1:i len:20 +start_handler: len:32 +endfinally: len:32 +call_handler: len:16 clob:c +endfilter: src1:i len:32 +get_ex_obj: dest:i len:16 + +ckfinite: dest:f src1:f len:64 +ceq: dest:i len:12 +cgt: dest:i len:12 +cgt.un: dest:i len:12 +clt: dest:i len:12 +clt.un: dest:i len:12 +localloc: dest:i src1:i len:96 +compare: src1:i src2:i len:4 +compare_imm: src1:i len:20 +fcompare: src1:f src2:f len:12 +rcompare: src1:f src2:f len:12 +oparglist: src1:i len:12 +setlret: src1:i src2:i len:12 +checkthis: src1:b len:4 +call: dest:a clob:c len:32 +call_reg: dest:a src1:i len:32 clob:c +call_membase: dest:a src1:b len:32 clob:c +voidcall: len:32 clob:c +voidcall_reg: src1:i len:32 clob:c +voidcall_membase: src1:b len:32 clob:c +fcall: dest:f len:32 clob:c +fcall_reg: dest:f src1:i len:32 clob:c +fcall_membase: dest:f src1:b len:32 clob:c +rcall: dest:f len:32 clob:c +rcall_reg: dest:f src1:i len:32 clob:c +rcall_membase: dest:f src1:b len:32 clob:c +lcall: dest:l len:32 clob:c +lcall_reg: dest:l src1:i len:32 clob:c +lcall_membase: dest:l src1:b len:32 clob:c +vcall: len:32 clob:c +vcall_reg: src1:i len:32 clob:c +vcall_membase: src1:b len:32 clob:c +tailcall: len:64 clob:c +iconst: dest:i len:16 +r4const: dest:f len:24 +r8const: dest:f len:20 +label: len:0 +store_membase_imm: dest:b len:20 +store_membase_reg: dest:b src1:i len:20 +storei1_membase_imm: dest:b len:20 +storei1_membase_reg: dest:b src1:i len:12 +storei2_membase_imm: dest:b len:20 +storei2_membase_reg: dest:b src1:i len:12 +storei4_membase_imm: dest:b len:20 +storei4_membase_reg: dest:b src1:i len:20 +storer4_membase_reg: dest:b src1:f len:20 +storer8_membase_reg: dest:b src1:f len:24 +store_memindex: dest:b src1:i src2:i len:4 +storei1_memindex: dest:b src1:i src2:i len:4 +storei2_memindex: dest:b src1:i src2:i len:4 +storei4_memindex: dest:b src1:i src2:i len:4 +load_membase: dest:i src1:b len:20 +loadi1_membase: dest:i src1:b len:32 +loadu1_membase: dest:i src1:b len:32 +loadi2_membase: dest:i src1:b len:32 +loadu2_membase: dest:i src1:b len:32 +loadi4_membase: dest:i src1:b len:32 +loadu4_membase: dest:i src1:b len:32 +loadr4_membase: dest:f src1:b len:32 +loadr8_membase: dest:f src1:b len:32 +load_memindex: dest:i src1:b src2:i len:4 +loadi1_memindex: dest:i src1:b src2:i len:4 +loadu1_memindex: dest:i src1:b src2:i len:4 +loadi2_memindex: dest:i src1:b src2:i len:4 +loadu2_memindex: dest:i src1:b src2:i len:4 +loadi4_memindex: dest:i src1:b src2:i len:4 +loadu4_memindex: dest:i src1:b src2:i len:4 +loadu4_mem: dest:i len:8 +move: dest:i src1:i len:4 +fmove: dest:f src1:f len:4 +rmove: dest:f src1:f len:4 +move_f_to_i4: dest:i src1:f len:8 +move_i4_to_f: dest:f src1:i len:8 +move_f_to_i8: dest:i src1:f len:4 +move_i8_to_f: dest:f src1:i len:4 +add_imm: dest:i src1:i len:12 +sub_imm: dest:i src1:i len:12 +mul_imm: dest:i src1:i len:12 +and_imm: dest:i src1:i len:12 +or_imm: dest:i src1:i len:12 +xor_imm: dest:i src1:i len:12 +shl_imm: dest:i src1:i len:8 +shr_imm: dest:i src1:i len:8 +shr_un_imm: dest:i src1:i len:8 +cond_exc_eq: len:8 +cond_exc_ne_un: len:8 +cond_exc_lt: len:8 +cond_exc_lt_un: len:8 +cond_exc_gt: len:8 +cond_exc_gt_un: len:8 +cond_exc_ge: len:8 +cond_exc_ge_un: len:8 +cond_exc_le: len:8 +cond_exc_le_un: len:8 +cond_exc_ov: len:12 +cond_exc_no: len:8 +cond_exc_c: len:12 +cond_exc_nc: len:8 +#float_beq: src1:f src2:f len:20 +#float_bne_un: src1:f src2:f len:20 +#float_blt: src1:f src2:f len:20 +#float_blt_un: src1:f src2:f len:20 +#float_bgt: src1:f src2:f len:20 +#float_bgt_un: src1:f src2:f len:20 +#float_bge: src1:f src2:f len:20 +#float_bge_un: src1:f src2:f len:20 +#float_ble: src1:f src2:f len:20 +#float_ble_un: src1:f src2:f len:20 +float_add: dest:f src1:f src2:f len:4 +float_sub: dest:f src1:f src2:f len:4 +float_mul: dest:f src1:f src2:f len:4 +float_div: dest:f src1:f src2:f len:4 +float_div_un: dest:f src1:f src2:f len:4 +float_rem: dest:f src1:f src2:f len:16 +float_rem_un: dest:f src1:f src2:f len:16 +float_neg: dest:f src1:f len:4 +float_not: dest:f src1:f len:4 +float_conv_to_i1: dest:i src1:f len:40 +float_conv_to_i2: dest:i src1:f len:40 +float_conv_to_i4: dest:i src1:f len:40 +float_conv_to_i8: dest:l src1:f len:40 +float_conv_to_r4: dest:f src1:f len:8 +float_conv_to_u4: dest:i src1:f len:40 +float_conv_to_u8: dest:l src1:f len:40 +float_conv_to_u2: dest:i src1:f len:40 +float_conv_to_u1: dest:i src1:f len:40 +float_conv_to_i: dest:i src1:f len:40 +float_ceq: dest:i src1:f src2:f len:16 +float_cgt: dest:i src1:f src2:f len:16 +float_cgt_un: dest:i src1:f src2:f len:20 +float_clt: dest:i src1:f src2:f len:16 +float_clt_un: dest:i src1:f src2:f len:20 +float_cneq: dest:i src1:f src2:f len:20 +float_cge: dest:i src1:f src2:f len:20 +float_cle: dest:i src1:f src2:f len:20 +float_conv_to_u: dest:i src1:f len:36 +setfret: src1:f len:12 + +# R4 opcodes +r4_conv_to_i1: dest:i src1:f len:8 +r4_conv_to_u1: dest:i src1:f len:8 +r4_conv_to_i2: dest:i src1:f len:8 +r4_conv_to_u2: dest:i src1:f len:8 +r4_conv_to_i4: dest:i src1:f len:8 +r4_conv_to_u4: dest:i src1:f len:8 +r4_conv_to_i8: dest:l src1:f len:8 +r4_conv_to_u8: dest:l src1:f len:8 +r4_conv_to_r4: dest:f src1:f len:4 +r4_conv_to_r8: dest:f src1:f len:4 +r4_add: dest:f src1:f src2:f len:4 +r4_sub: dest:f src1:f src2:f len:4 +r4_mul: dest:f src1:f src2:f len:4 +r4_div: dest:f src1:f src2:f len:4 +r4_rem: dest:f src1:f src2:f len:16 +r4_neg: dest:f src1:f len:4 +r4_ceq: dest:i src1:f src2:f len:16 +r4_cgt: dest:i src1:f src2:f len:16 +r4_cgt_un: dest:i src1:f src2:f len:20 +r4_clt: dest:i src1:f src2:f len:16 +r4_clt_un: dest:i src1:f src2:f len:20 +r4_cneq: dest:i src1:f src2:f len:20 +r4_cge: dest:i src1:f src2:f len:20 +r4_cle: dest:i src1:f src2:f len:20 + +aot_const: dest:i len:16 +objc_get_selector: dest:i len:32 +sqrt: dest:f src1:f len:4 +adc: dest:i src1:i src2:i len:4 +addcc: dest:i src1:i src2:i len:4 +subcc: dest:i src1:i src2:i len:4 +adc_imm: dest:i src1:i len:12 +addcc_imm: dest:i src1:i len:12 +subcc_imm: dest:i src1:i len:12 +sbb: dest:i src1:i src2:i len:4 +sbb_imm: dest:i src1:i len:12 +br_reg: src1:i len:8 +bigmul: len:8 dest:l src1:i src2:i +bigmul_un: len:8 dest:l src1:i src2:i +tls_get: dest:i len:32 +tls_set: src1:i len:32 + +# 32 bit opcodes +int_add: dest:i src1:i src2:i len:4 +int_sub: dest:i src1:i src2:i len:4 +int_mul: dest:i src1:i src2:i len:4 +int_div: dest:i src1:i src2:i len:72 +int_div_un: dest:i src1:i src2:i len:72 +int_rem: dest:i src1:i src2:i len:72 +int_rem_un: dest:i src1:i src2:i len:72 +int_and: dest:i src1:i src2:i len:4 +int_or: dest:i src1:i src2:i len:4 +int_xor: dest:i src1:i src2:i len:4 +int_shl: dest:i src1:i src2:i len:4 +int_shr: dest:i src1:i src2:i len:4 +int_shr_un: dest:i src1:i src2:i len:4 +int_neg: dest:i src1:i len:4 +int_not: dest:i src1:i len:4 +int_conv_to_i1: dest:i src1:i len:8 +int_conv_to_i2: dest:i src1:i len:8 +int_conv_to_i4: dest:i src1:i len:4 +int_conv_to_r4: dest:f src1:i len:36 +int_conv_to_r8: dest:f src1:i len:36 +int_conv_to_u4: dest:i src1:i +int_conv_to_r_un: dest:f src1:i len:56 +int_conv_to_u2: dest:i src1:i len:8 +int_conv_to_u1: dest:i src1:i len:4 +int_beq: len:16 +int_bge: len:16 +int_bgt: len:16 +int_ble: len:16 +int_blt: len:16 +int_bne_un: len:16 +int_bge_un: len:16 +int_bgt_un: len:16 +int_ble_un: len:16 +int_blt_un: len:16 +int_add_ovf: dest:i src1:i src2:i len:16 +int_add_ovf_un: dest:i src1:i src2:i len:16 +int_mul_ovf: dest:i src1:i src2:i len:16 +int_mul_ovf_un: dest:i src1:i src2:i len:16 +int_sub_ovf: dest:i src1:i src2:i len:16 +int_sub_ovf_un: dest:i src1:i src2:i len:16 +add_ovf_carry: dest:i src1:i src2:i len:16 +sub_ovf_carry: dest:i src1:i src2:i len:16 +add_ovf_un_carry: dest:i src1:i src2:i len:16 +sub_ovf_un_carry: dest:i src1:i src2:i len:16 + +arm_rsbs_imm: dest:i src1:i len:4 +arm_rsc_imm: dest:i src1:i len:4 + +# Linear IR opcodes +dummy_use: src1:i len:0 +dummy_store: len:0 +not_reached: len:0 +not_null: src1:i len:0 + +int_adc: dest:i src1:i src2:i len:4 +int_addcc: dest:i src1:i src2:i len:4 +int_subcc: dest:i src1:i src2:i len:4 +int_sbb: dest:i src1:i src2:i len:4 +int_adc_imm: dest:i src1:i len:12 +int_sbb_imm: dest:i src1:i len:12 + +int_add_imm: dest:i src1:i len:12 +int_sub_imm: dest:i src1:i len:12 +int_mul_imm: dest:i src1:i len:12 +int_div_imm: dest:i src1:i len:20 +int_div_un_imm: dest:i src1:i len:12 +int_rem_imm: dest:i src1:i len:28 +int_rem_un_imm: dest:i src1:i len:16 +int_and_imm: dest:i src1:i len:12 +int_or_imm: dest:i src1:i len:12 +int_xor_imm: dest:i src1:i len:12 +int_shl_imm: dest:i src1:i len:8 +int_shr_imm: dest:i src1:i len:8 +int_shr_un_imm: dest:i src1:i len:8 + +int_ceq: dest:i len:12 +int_cgt: dest:i len:12 +int_cgt_un: dest:i len:12 +int_clt: dest:i len:12 +int_clt_un: dest:i len:12 + +int_cneq: dest:i len:12 +int_cge: dest:i len:12 +int_cle: dest:i len:12 +int_cge_un: dest:i len:12 +int_cle_un: dest:i len:12 + +cond_exc_ieq: len:16 +cond_exc_ine_un: len:16 +cond_exc_ilt: len:16 +cond_exc_ilt_un: len:16 +cond_exc_igt: len:16 +cond_exc_igt_un: len:16 +cond_exc_ige: len:16 +cond_exc_ige_un: len:16 +cond_exc_ile: len:16 +cond_exc_ile_un: len:16 +cond_exc_iov: len:20 +cond_exc_ino: len:16 +cond_exc_ic: len:20 +cond_exc_inc: len:16 + +icompare: src1:i src2:i len:4 +icompare_imm: src1:i len:12 + +long_conv_to_ovf_i4_2: dest:i src1:i src2:i len:36 + +vcall2: len:40 clob:c +vcall2_reg: src1:i len:40 clob:c +vcall2_membase: src1:b len:40 clob:c +dyn_call: src1:i src2:i len:216 clob:c + +# This is different from the original JIT opcodes +float_beq: len:32 +float_bne_un: len:32 +float_blt: len:32 +float_blt_un: len:32 +float_bgt: len:32 +float_bgt_un: len:32 +float_bge: len:32 +float_bge_un: len:32 +float_ble: len:32 +float_ble_un: len:32 + +liverange_start: len:0 +liverange_end: len:0 +gc_liveness_def: len:0 +gc_liveness_use: len:0 +gc_spill_slot_liveness_def: len:0 +gc_param_slot_liveness_def: len:0 + +# 64 bit opcodes +i8const: dest:i len:16 +sext_i4: dest:i src1:i len:4 +zext_i4: dest:i src1:i len:4 +jump_table: dest:i len:16 +long_add: dest:i src1:i src2:i len:4 +long_sub: dest:i src1:i src2:i len:4 +long_mul: dest:i src1:i src2:i len:4 +long_div: dest:i src1:i src2:i len:80 +long_div_un: dest:i src1:i src2:i len:64 +long_rem: dest:i src1:i src2:i len:80 +long_rem_un: dest:i src1:i src2:i len:64 +long_and: dest:i src1:i src2:i len:4 +long_or: dest:i src1:i src2:i len:4 +long_xor: dest:i src1:i src2:i len:4 +long_shl: dest:i src1:i src2:i len:4 +long_shr: dest:i src1:i src2:i len:4 +long_shr_un: dest:i src1:i src2:i len:4 +long_neg: dest:i src1:i len:4 +long_not: dest:i src1:i len:4 +long_add_imm: dest:i src1:i len:12 +long_sub_imm: dest:i src1:i len:12 +long_mul_imm: dest:i src1:i len:12 +long_and_imm: dest:i src1:i len:12 +long_or_imm: dest:i src1:i len:12 +long_xor_imm: dest:i src1:i len:12 +long_shl_imm: dest:i src1:i len:12 +long_shr_imm: dest:i src1:i len:12 +long_shr_un_imm: dest:i src1:i len:12 +long_add_ovf: dest:i src1:i src2:i len:16 +long_add_ovf_un: dest:i src1:i src2:i len:16 +long_mul_ovf: dest:i src1:i src2:i len:16 +long_mul_ovf_un: dest:i src1:i src2:i len:16 +long_sub_ovf: dest:i src1:i src2:i len:16 +long_sub_ovf_un: dest:i src1:i src2:i len:16 +lcompare: src1:i src2:i len:4 +lcompare_imm: src1:i len:20 +long_beq: len:4 +long_bge: len:4 +long_bgt: len:4 +long_ble: len:4 +long_blt: len:4 +long_bne_un: len:4 +long_bge_un: len:4 +long_bgt_un: len:4 +long_ble_un: len:4 +long_blt_un: len:4 +long_ceq: dest:i len:12 +long_cgt: dest:i len:12 +long_cgt_un: dest:i len:12 +long_clt: dest:i len:12 +long_clt_un: dest:i len:12 +long_conv_to_i1: dest:i src1:i len:4 +long_conv_to_i2: dest:i src1:i len:4 +long_conv_to_u1: dest:i src1:i len:4 +long_conv_to_u2: dest:i src1:i len:4 +long_conv_to_r8: dest:f src1:i len:8 +long_conv_to_r4: dest:f src1:i len:12 +loadi8_membase: dest:i src1:b len:12 +storei8_membase_imm: dest:b len:20 +storei8_membase_reg: dest:b src1:i len:12 +long_conv_to_r_un: dest:f src1:i len:8 +arm_setfreg_r4: dest:f src1:f len:8 +localloc_imm: dest:i len:64 +arm64_cbzw: src1:i len:16 +arm64_cbzx: src1:i len:16 +arm64_cbnzw: src1:i len:16 +arm64_cbnzx: src1:i len:16 + +atomic_add_i4: dest:i src1:i src2:i len:32 +atomic_add_i8: dest:i src1:i src2:i len:32 +atomic_exchange_i4: dest:i src1:i src2:i len:32 +atomic_exchange_i8: dest:i src1:i src2:i len:32 +atomic_cas_i4: dest:i src1:i src2:i src3:i len:32 +atomic_cas_i8: dest:i src1:i src2:i src3:i len:32 +memory_barrier: len:8 clob:a +atomic_load_i1: dest:i src1:b len:24 +atomic_load_u1: dest:i src1:b len:24 +atomic_load_i2: dest:i src1:b len:24 +atomic_load_u2: dest:i src1:b len:24 +atomic_load_i4: dest:i src1:b len:24 +atomic_load_u4: dest:i src1:b len:24 +atomic_load_i8: dest:i src1:b len:20 +atomic_load_u8: dest:i src1:b len:20 +atomic_load_r4: dest:f src1:b len:28 +atomic_load_r8: dest:f src1:b len:24 +atomic_store_i1: dest:b src1:i len:20 +atomic_store_u1: dest:b src1:i len:20 +atomic_store_i2: dest:b src1:i len:20 +atomic_store_u2: dest:b src1:i len:20 +atomic_store_i4: dest:b src1:i len:20 +atomic_store_u4: dest:b src1:i len:20 +atomic_store_i8: dest:b src1:i len:20 +atomic_store_u8: dest:b src1:i len:20 +atomic_store_r4: dest:b src1:f len:28 +atomic_store_r8: dest:b src1:f len:24 + +generic_class_init: src1:a len:44 clob:c +gc_safe_point: src1:i len:12 clob:c + +fill_prof_call_ctx: src1:i len:128 diff --git a/unity-2019.4.24f1-mbe/mono/mini/cpu-mips.md b/unity-2019.4.24f1-mbe/mono/mini/cpu-mips.md new file mode 100644 index 000000000..85c3476ee --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/cpu-mips.md @@ -0,0 +1,458 @@ +# mips cpu description file +# this file is read by genmdesc to pruduce a table with all the relevant +# information about the cpu instructions that may be used by the regsiter +# allocator, the scheduler and other parts of the arch-dependent part of mini. +# +# An opcode name is followed by a colon and optional specifiers. +# A specifier has a name, a colon and a value. +# Specifiers are separated by white space. +# Here is a description of the specifiers valid for this file and their +# possible values. +# +# dest:register describes the destination register of an instruction +# src1:register describes the first source register of an instruction +# src2:register describes the second source register of an instruction +# +# register may have the following values: +# i integer register +# l integer register pair +# v v0 register (output from calls) +# V v0/v1 register pair (output from calls) +# a at register +# b base register (used in address references) +# f floating point register (pair - always) +# g floating point register return pair (f0/f1) +# +# len:number describe the maximun length in bytes of the instruction +# number is a positive integer +# +# cost:number describe how many cycles are needed to complete the instruction (unused) +# +# clob:spec describe if the instruction clobbers registers or has special needs +# +# spec can be one of the following characters: +# c clobbers caller-save registers +# r 'reserves' the destination register until a later instruction unreserves it +# used mostly to set output registers in function calls +# +# flags:spec describe if the instruction uses or sets the flags (unused) +# +# spec can be one of the following chars: +# s sets the flags +# u uses the flags +# m uses and modifies the flags +# +# res:spec describe what units are used in the processor (unused) +# +# delay: describe delay slots (unused) +# +# the required specifiers are: len, clob (if registers are clobbered), the registers +# specifiers if the registers are actually used, flags (when scheduling is implemented). +# +# See the code in mini-x86.c for more details on how the specifiers are used. +# +memory_barrier: len:4 +nop: len:4 +relaxed_nop: len:4 +break: len:16 +jmp: len:92 +call: dest:v clob:c len:20 +br: len:16 +switch: src1:i len:40 +seq_point: len:24 +il_seq_point: len:0 + +int_conv_to_r_un: dest:f src1:i len:32 +throw: src1:i len:24 +rethrow: src1:i len:24 +ckfinite: dest:f src1:f len:52 +start_handler: len:16 +endfinally: len:12 +ceq: dest:i len:16 +cgt: dest:i len:16 +cgt.un: dest:i len:16 +clt: dest:i len:16 +clt.un: dest:i len:16 +localloc: dest:i src1:i len:60 +compare: src1:i src2:i len:20 +compare_imm: src1:i len:20 +fcompare: src1:f src2:f len:12 +oparglist: src1:i len:12 +setlret: src1:i src2:i len:12 +checkthis: src1:b len:4 + +voidcall: len:20 clob:c +voidcall_reg: src1:i len:20 clob:c +voidcall_membase: src1:b len:20 clob:c + +fcall: dest:g len:20 clob:c +fcall_reg: dest:g src1:i len:20 clob:c +fcall_membase: dest:g src1:b len:20 clob:c + +lcall: dest:V len:28 clob:c +lcall_reg: dest:V src1:i len:28 clob:c +lcall_membase: dest:V src1:b len:28 clob:c + +call_reg: dest:v src1:i len:20 clob:c +call_membase: dest:v src1:b len:20 clob:c + +vcall: len:16 clob:c +vcall_reg: src1:i len:20 clob:c +vcall_membase: src1:b len:20 clob:c + +vcall2: len:16 clob:c +vcall2_reg: src1:i len:20 clob:c +vcall2_membase: src1:b len:20 clob:c + +jump_table: dest:i len:8 + +iconst: dest:i len:12 +i8const: dest:l len:24 +r4const: dest:f len:20 +r8const: dest:f len:28 +label: len:0 +store_membase_imm: dest:b len:20 +store_membase_reg: dest:b src1:i len:20 +storei1_membase_imm: dest:b len:20 +storei1_membase_reg: dest:b src1:i len:20 +storei2_membase_imm: dest:b len:20 +storei2_membase_reg: dest:b src1:i len:20 +storei4_membase_imm: dest:b len:20 +storei4_membase_reg: dest:b src1:i len:20 +storei8_membase_imm: dest:b +storei8_membase_reg: dest:b src1:i len:20 +storer4_membase_reg: dest:b src1:f len:20 +storer8_membase_reg: dest:b src1:f len:20 +load_membase: dest:i src1:b len:20 +loadi1_membase: dest:i src1:b len:20 +loadu1_membase: dest:i src1:b len:20 +loadi2_membase: dest:i src1:b len:20 +loadu2_membase: dest:i src1:b len:20 +loadi4_membase: dest:i src1:b len:20 +loadu4_membase: dest:i src1:b len:20 +loadi8_membase: dest:i src1:b len:20 +loadr4_membase: dest:f src1:b len:20 +loadr8_membase: dest:f src1:b len:20 +load_memindex: dest:i src1:b src2:i len:4 +loadi1_memindex: dest:i src1:b src2:i len:12 +loadu1_memindex: dest:i src1:b src2:i len:12 +loadi2_memindex: dest:i src1:b src2:i len:12 +loadu2_memindex: dest:i src1:b src2:i len:12 +loadi4_memindex: dest:i src1:b src2:i len:12 +loadu4_memindex: dest:i src1:b src2:i len:12 +loadr4_memindex: dest:f src1:b src2:i len:12 +loadr8_memindex: dest:f src1:b src2:i len:12 +store_memindex: dest:b src1:i src2:i len:12 +storei1_memindex: dest:b src1:i src2:i len:12 +storei2_memindex: dest:b src1:i src2:i len:12 +storei4_memindex: dest:b src1:i src2:i len:12 +storer4_memindex: dest:b src1:f src2:i len:12 +storer8_memindex: dest:b src1:f src2:i len:12 +loadu4_mem: dest:i len:8 +move: dest:i src1:i len:4 +fmove: dest:f src1:f len:8 +move_f_to_i4: dest:i src1:f len:4 +move_i4_to_f: dest:f src1:i len:4 +add_imm: dest:i src1:i len:12 +sub_imm: dest:i src1:i len:12 +mul_imm: dest:i src1:i len:20 +# there is no actual support for division or reminder by immediate +# we simulate them, though (but we need to change the burg rules +# to allocate a symbolic reg for src2) +div_imm: dest:i src1:i src2:i len:20 +div_un_imm: dest:i src1:i src2:i len:12 +rem_imm: dest:i src1:i src2:i len:28 +rem_un_imm: dest:i src1:i src2:i len:16 +and_imm: dest:i src1:i len:12 +or_imm: dest:i src1:i len:12 +xor_imm: dest:i src1:i len:12 +shl_imm: dest:i src1:i len:8 +shr_imm: dest:i src1:i len:8 +shr_un_imm: dest:i src1:i len:8 + +# Linear IR opcodes +dummy_use: src1:i len:0 +dummy_store: len:0 +not_reached: len:0 +not_null: src1:i len:0 + +# 32 bit opcodes +int_add: dest:i src1:i src2:i len:4 +int_sub: dest:i src1:i src2:i len:4 +int_mul: dest:i src1:i src2:i len:16 +int_div: dest:i src1:i src2:i len:84 +int_div_un: dest:i src1:i src2:i len:40 +int_rem: dest:i src1:i src2:i len:84 +int_rem_un: dest:i src1:i src2:i len:40 +int_and: dest:i src1:i src2:i len:4 +int_or: dest:i src1:i src2:i len:4 +int_xor: dest:i src1:i src2:i len:4 +int_shl: dest:i src1:i src2:i len:4 +int_shr: dest:i src1:i src2:i len:4 +int_shr_un: dest:i src1:i src2:i len:4 +int_neg: dest:i src1:i len:4 +int_not: dest:i src1:i len:4 +int_conv_to_i1: dest:i src1:i len:8 +int_conv_to_i2: dest:i src1:i len:8 +int_conv_to_i4: dest:i src1:i len:4 +int_conv_to_r4: dest:f src1:i len:36 +int_conv_to_r8: dest:f src1:i len:36 +int_conv_to_u4: dest:i src1:i +int_conv_to_u2: dest:i src1:i len:8 +int_conv_to_u1: dest:i src1:i len:4 +int_beq: len:8 +int_bge: len:8 +int_bgt: len:8 +int_ble: len:8 +int_blt: len:8 +int_bne_un: len:8 +int_bge_un: len:8 +int_bgt_un: len:8 +int_ble_un: len:8 +int_blt_un: len:8 +int_add_ovf: dest:i src1:i src2:i len:16 +int_add_ovf_un: dest:i src1:i src2:i len:16 +int_mul_ovf: dest:i src1:i src2:i len:56 +int_mul_ovf_un: dest:i src1:i src2:i len:56 +int_sub_ovf: dest:i src1:i src2:i len:16 +int_sub_ovf_un: dest:i src1:i src2:i len:16 + +int_adc: dest:i src1:i src2:i len:4 +int_addcc: dest:i src1:i src2:i len:4 +int_subcc: dest:i src1:i src2:i len:4 +int_sbb: dest:i src1:i src2:i len:4 +int_adc_imm: dest:i src1:i len:12 +int_sbb_imm: dest:i src1:i len:12 + +int_add_imm: dest:i src1:i len:12 +int_sub_imm: dest:i src1:i len:12 +int_mul_imm: dest:i src1:i len:12 +int_div_imm: dest:i src1:i len:20 +int_div_un_imm: dest:i src1:i len:12 +int_rem_imm: dest:i src1:i len:28 +int_rem_un_imm: dest:i src1:i len:16 +int_and_imm: dest:i src1:i len:12 +int_or_imm: dest:i src1:i len:12 +int_xor_imm: dest:i src1:i len:12 +int_shl_imm: dest:i src1:i len:8 +int_shr_imm: dest:i src1:i len:8 +int_shr_un_imm: dest:i src1:i len:8 + +int_ceq: dest:i len:16 +int_cgt: dest:i len:16 +int_cgt_un: dest:i len:16 +int_clt: dest:i len:16 +int_clt_un: dest:i len:16 + +cond_exc_eq: len:32 +cond_exc_ne_un: len:32 +cond_exc_lt: len:32 +cond_exc_lt_un: len:32 +cond_exc_gt: len:32 +cond_exc_gt_un: len:32 +cond_exc_ge: len:32 +cond_exc_ge_un: len:32 +cond_exc_le: len:32 +cond_exc_le_un: len:32 +cond_exc_ov: len:32 +cond_exc_no: len:32 +cond_exc_c: len:32 +cond_exc_nc: len:32 + +cond_exc_ieq: len:32 +cond_exc_ine_un: len:32 +cond_exc_ilt: len:32 +cond_exc_ilt_un: len:32 +cond_exc_igt: len:32 +cond_exc_igt_un: len:32 +cond_exc_ige: len:32 +cond_exc_ige_un: len:32 +cond_exc_ile: len:32 +cond_exc_ile_un: len:32 +cond_exc_iov: len:12 +cond_exc_ino: len:32 +cond_exc_ic: len:12 +cond_exc_inc: len:32 + +icompare: src1:i src2:i len:4 +icompare_imm: src1:i len:12 + +# 64 bit opcodes +long_add: dest:i src1:i src2:i len:4 +long_sub: dest:i src1:i src2:i len:4 +long_mul: dest:i src1:i src2:i len:32 +long_mul_imm: dest:i src1:i len:4 +long_div: dest:i src1:i src2:i len:40 +long_div_un: dest:i src1:i src2:i len:16 +long_rem: dest:i src1:i src2:i len:48 +long_rem_un: dest:i src1:i src2:i len:24 +long_and: dest:i src1:i src2:i len:4 +long_or: dest:i src1:i src2:i len:4 +long_xor: dest:i src1:i src2:i len:4 +long_shl: dest:i src1:i src2:i len:4 +long_shl_imm: dest:i src1:i len:4 +long_shr: dest:i src1:i src2:i len:4 +long_shr_un: dest:i src1:i src2:i len:4 +long_shr_imm: dest:i src1:i len:4 +long_shr_un_imm: dest:i src1:i len:4 +long_neg: dest:i src1:i len:4 +long_not: dest:i src1:i len:4 +long_conv_to_i1: dest:i src1:l len:32 +long_conv_to_i2: dest:i src1:l len:32 +long_conv_to_i4: dest:i src1:l len:32 +long_conv_to_r4: dest:f src1:l len:32 +long_conv_to_r8: dest:f src1:l len:32 +long_conv_to_u4: dest:i src1:l len:32 +long_conv_to_u8: dest:l src1:l len:32 +long_conv_to_u2: dest:i src1:l len:32 +long_conv_to_u1: dest:i src1:l len:32 +long_conv_to_i: dest:i src1:l len:32 +long_conv_to_ovf_i: dest:i src1:i src2:i len:32 +long_conv_to_ovf_i4_2: dest:i src1:i src2:i len:32 +zext_i4: dest:i src1:i len:16 +sext_i4: dest:i src1:i len:16 + +long_beq: len:8 +long_bge: len:8 +long_bgt: len:8 +long_ble: len:8 +long_blt: len:8 +long_bne_un: len:8 +long_bge_un: len:8 +long_bgt_un: len:8 +long_ble_un: len:8 +long_blt_un: len:8 +long_add_ovf: dest:i src1:i src2:i len:16 +long_add_ovf_un: dest:i src1:i src2:i len:16 +long_mul_ovf: dest:i src1:i src2:i len:16 +long_mul_ovf_un: dest:i src1:i src2:i len:16 +long_sub_ovf: dest:i src1:i src2:i len:16 +long_sub_ovf_un: dest:i src1:i src2:i len:16 + +long_ceq: dest:i len:12 +long_cgt: dest:i len:12 +long_cgt_un: dest:i len:12 +long_clt: dest:i len:12 +long_clt_un: dest:i len:12 + +long_add_imm: dest:i src1:i clob:1 len:4 +long_sub_imm: dest:i src1:i clob:1 len:4 +long_and_imm: dest:i src1:i clob:1 len:4 +long_or_imm: dest:i src1:i clob:1 len:4 +long_xor_imm: dest:i src1:i clob:1 len:4 + +lcompare: src1:i src2:i len:4 +lcompare_imm: src1:i len:12 + +long_conv_to_r_un: dest:f src1:i src2:i len:37 + +float_beq: len:16 +float_bne_un: len:16 +float_blt: len:16 +float_blt_un: len:16 +float_bgt: len:16 +float_bgt_un: len:16 +float_bge: len:16 +float_bge_un: len:16 +float_ble: len:16 +float_ble_un: len:16 + +float_add: dest:f src1:f src2:f len:4 +float_sub: dest:f src1:f src2:f len:4 +float_mul: dest:f src1:f src2:f len:4 +float_div: dest:f src1:f src2:f len:4 +float_div_un: dest:f src1:f src2:f len:4 +float_rem: dest:f src1:f src2:f len:16 +float_rem_un: dest:f src1:f src2:f len:16 +float_neg: dest:f src1:f len:4 +float_not: dest:f src1:f len:4 +float_conv_to_i1: dest:i src1:f len:40 +float_conv_to_i2: dest:i src1:f len:40 +float_conv_to_i4: dest:i src1:f len:40 +float_conv_to_i8: dest:l src1:f len:40 +float_conv_to_r4: dest:f src1:f len:8 +float_conv_to_u4: dest:i src1:f len:40 +float_conv_to_u8: dest:l src1:f len:40 +float_conv_to_u2: dest:i src1:f len:40 +float_conv_to_u1: dest:i src1:f len:40 +float_conv_to_i: dest:i src1:f len:40 +float_ceq: dest:i src1:f src2:f len:20 +float_cgt: dest:i src1:f src2:f len:20 +float_cgt_un: dest:i src1:f src2:f len:20 +float_clt: dest:i src1:f src2:f len:20 +float_clt_un: dest:i src1:f src2:f len:20 +float_conv_to_u: dest:i src1:f len:36 +call_handler: len:20 clob:c +endfilter: src1:i len:16 +aot_const: dest:i len:8 +sqrt: dest:f src1:f len:4 +adc: dest:i src1:i src2:i len:4 +addcc: dest:i src1:i src2:i len:4 +subcc: dest:i src1:i src2:i len:4 +adc_imm: dest:i src1:i len:12 +addcc_imm: dest:i src1:i len:12 +subcc_imm: dest:i src1:i len:12 +sbb: dest:i src1:i src2:i len:4 +sbb_imm: dest:i src1:i len:12 +br_reg: src1:i len:8 +#ppc_subfic: dest:i src1:i len:4 +#ppc_subfze: dest:i src1:i len:4 +bigmul: len:52 dest:l src1:i src2:i +bigmul_un: len:52 dest:l src1:i src2:i +mips_beq: src1:i src2:i len:24 +mips_bgez: src1:i len:24 +mips_bgtz: src1:i len:24 +mips_blez: src1:i len:24 +mips_bltz: src1:i len:24 +mips_bne: src1:i src2:i len:24 +mips_cvtsd: dest:f src1:f len:8 +mips_fbeq: src1:f src2:f len:16 +mips_fbge: src1:f src2:f len:32 +mips_fbge_un: src1:f src2:f len:16 +mips_fbgt: src1:f src2:f len:32 +mips_fbgt_un: src1:f src2:f len:16 +mips_fble: src1:f src2:f len:32 +mips_fble_un: src1:f src2:f len:16 +mips_fblt: src1:f src2:f len:32 +mips_fblt_un: src1:f src2:f len:16 +mips_fbne: src1:f src2:f len:16 +mips_lwc1: dest:f src1:b len:16 +mips_mtc1_s: dest:f src1:i len:8 +mips_mtc1_s2: dest:f src1:i src2:i len:8 +mips_mfc1_s: dest:i src1:f len:8 +mips_mtc1_d: dest:f src1:i len:8 +mips_mfc1_d: dest:i src1:f len:8 +mips_slti: dest:i src1:i len:4 +mips_slt: dest:i src1:i src2:i len:4 +mips_sltiu: dest:i src1:i len:4 +mips_sltu: dest:i src1:i src2:i len:4 +mips_cond_exc_eq: src1:i src2:i len:44 +mips_cond_exc_ge: src1:i src2:i len:44 +mips_cond_exc_gt: src1:i src2:i len:44 +mips_cond_exc_le: src1:i src2:i len:44 +mips_cond_exc_lt: src1:i src2:i len:44 +mips_cond_exc_ne_un: src1:i src2:i len:44 +mips_cond_exc_ge_un: src1:i src2:i len:44 +mips_cond_exc_gt_un: src1:i src2:i len:44 +mips_cond_exc_le_un: src1:i src2:i len:44 +mips_cond_exc_lt_un: src1:i src2:i len:44 +mips_cond_exc_ov: src1:i src2:i len:44 +mips_cond_exc_no: src1:i src2:i len:44 +mips_cond_exc_c: src1:i src2:i len:44 +mips_cond_exc_nc: src1:i src2:i len:44 +mips_cond_exc_ieq: src1:i src2:i len:44 +mips_cond_exc_ige: src1:i src2:i len:44 +mips_cond_exc_igt: src1:i src2:i len:44 +mips_cond_exc_ile: src1:i src2:i len:44 +mips_cond_exc_ilt: src1:i src2:i len:44 +mips_cond_exc_ine_un: src1:i src2:i len:44 +mips_cond_exc_ige_un: src1:i src2:i len:44 +mips_cond_exc_igt_un: src1:i src2:i len:44 +mips_cond_exc_ile_un: src1:i src2:i len:44 +mips_cond_exc_ilt_un: src1:i src2:i len:44 +mips_cond_exc_iov: src1:i src2:i len:44 +mips_cond_exc_ino: src1:i src2:i len:44 +mips_cond_exc_ic: src1:i src2:i len:44 +mips_cond_exc_inc: src1:i src2:i len:44 + +gc_safe_point: len:0 diff --git a/unity-2019.4.24f1-mbe/mono/mini/cpu-ppc.md b/unity-2019.4.24f1-mbe/mono/mini/cpu-ppc.md new file mode 100644 index 000000000..0baa0fe3c --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/cpu-ppc.md @@ -0,0 +1,330 @@ +# powerpc cpu description file +# this file is read by genmdesc to pruduce a table with all the relevant information +# about the cpu instructions that may be used by the regsiter allocator, the scheduler +# and other parts of the arch-dependent part of mini. +# +# An opcode name is followed by a colon and optional specifiers. +# A specifier has a name, a colon and a value. Specifiers are separated by white space. +# Here is a description of the specifiers valid for this file and their possible values. +# +# dest:register describes the destination register of an instruction +# src1:register describes the first source register of an instruction +# src2:register describes the second source register of an instruction +# +# register may have the following values: +# i integer register +# a r3 register (output from calls) +# b base register (used in address references) +# f floating point register +# +# len:number describe the maximun length in bytes of the instruction +# number is a positive integer +# +# cost:number describe how many cycles are needed to complete the instruction (unused) +# +# clob:spec describe if the instruction clobbers registers or has special needs +# +# spec can be one of the following characters: +# c clobbers caller-save registers +# r 'reserves' the destination register until a later instruction unreserves it +# used mostly to set output registers in function calls +# +# flags:spec describe if the instruction uses or sets the flags (unused) +# +# spec can be one of the following chars: +# s sets the flags +# u uses the flags +# m uses and modifies the flags +# +# res:spec describe what units are used in the processor (unused) +# +# delay: describe delay slots (unused) +# +# the required specifiers are: len, clob (if registers are clobbered), the registers +# specifiers if the registers are actually used, flags (when scheduling is implemented). +# +# See the code in mini-x86.c for more details on how the specifiers are used. +# +memory_barrier: len:4 +nop: len:4 +relaxed_nop: len:4 +break: len:32 +seq_point: len:24 +il_seq_point: len:0 +jmp: len:108 +tailcall: len:120 clob:c +call: dest:a clob:c len:16 +br: len:4 +throw: src1:i len:20 +rethrow: src1:i len:20 +ckfinite: dest:f src1:f +ppc_check_finite: src1:i len:16 +add_ovf_carry: dest:i src1:i src2:i len:16 +sub_ovf_carry: dest:i src1:i src2:i len:16 +add_ovf_un_carry: dest:i src1:i src2:i len:16 +sub_ovf_un_carry: dest:i src1:i src2:i len:16 +start_handler: len:32 +endfinally: len:28 +ceq: dest:i len:12 +cgt: dest:i len:12 +cgt.un: dest:i len:12 +clt: dest:i len:12 +clt.un: dest:i len:12 +localloc: dest:i src1:i len:60 +compare: src1:i src2:i len:4 +compare_imm: src1:i len:12 +fcompare: src1:f src2:f len:12 +oparglist: src1:i len:12 +setlret: src1:i src2:i len:12 +checkthis: src1:b len:4 +voidcall: len:16 clob:c +voidcall_reg: src1:i len:16 clob:c +voidcall_membase: src1:b len:16 clob:c +fcall: dest:g len:16 clob:c +fcall_reg: dest:g src1:i len:16 clob:c +fcall_membase: dest:g src1:b len:16 clob:c +lcall: dest:l len:16 clob:c +lcall_reg: dest:l src1:i len:16 clob:c +lcall_membase: dest:l src1:b len:16 clob:c +vcall: len:16 clob:c +vcall_reg: src1:i len:16 clob:c +vcall_membase: src1:b len:16 clob:c +call_reg: dest:a src1:i len:16 clob:c +call_membase: dest:a src1:b len:16 clob:c +iconst: dest:i len:8 +r4const: dest:f len:12 +r8const: dest:f len:24 +label: len:0 +store_membase_reg: dest:b src1:i len:12 +storei1_membase_reg: dest:b src1:i len:12 +storei2_membase_reg: dest:b src1:i len:12 +storei4_membase_reg: dest:b src1:i len:12 +storer4_membase_reg: dest:b src1:f len:16 +storer8_membase_reg: dest:b src1:f len:12 +load_membase: dest:i src1:b len:12 +loadi1_membase: dest:i src1:b len:16 +loadu1_membase: dest:i src1:b len:12 +loadi2_membase: dest:i src1:b len:12 +loadu2_membase: dest:i src1:b len:12 +loadi4_membase: dest:i src1:b len:12 +loadu4_membase: dest:i src1:b len:12 +loadr4_membase: dest:f src1:b len:12 +loadr8_membase: dest:f src1:b len:12 +load_memindex: dest:i src1:b src2:i len:4 +loadi1_memindex: dest:i src1:b src2:i len:8 +loadu1_memindex: dest:i src1:b src2:i len:4 +loadi2_memindex: dest:i src1:b src2:i len:4 +loadu2_memindex: dest:i src1:b src2:i len:4 +loadi4_memindex: dest:i src1:b src2:i len:4 +loadu4_memindex: dest:i src1:b src2:i len:4 +loadr4_memindex: dest:f src1:b src2:i len:4 +loadr8_memindex: dest:f src1:b src2:i len:4 +store_memindex: dest:b src1:i src2:i len:4 +storei1_memindex: dest:b src1:i src2:i len:4 +storei2_memindex: dest:b src1:i src2:i len:4 +storei4_memindex: dest:b src1:i src2:i len:4 +storer4_memindex: dest:b src1:i src2:i len:8 +storer8_memindex: dest:b src1:i src2:i len:4 +loadu4_mem: dest:i len:8 +move: dest:i src1:i len:4 +fmove: dest:f src1:f len:4 +move_f_to_i4: dest:i src1:f len:8 +move_i4_to_f: dest:f src1:i len:8 +add_imm: dest:i src1:i len:4 +sub_imm: dest:i src1:i len:4 +mul_imm: dest:i src1:i len:4 +# there is no actual support for division or reminder by immediate +# we simulate them, though (but we need to change the burg rules +# to allocate a symbolic reg for src2) +div_imm: dest:i src1:i src2:i len:20 +div_un_imm: dest:i src1:i src2:i len:12 +rem_imm: dest:i src1:i src2:i len:28 +rem_un_imm: dest:i src1:i src2:i len:16 +and_imm: dest:i src1:i len:4 +or_imm: dest:i src1:i len:4 +xor_imm: dest:i src1:i len:4 +shl_imm: dest:i src1:i len:4 +shr_imm: dest:i src1:i len:4 +shr_un_imm: dest:i src1:i len:4 +cond_exc_eq: len:8 +cond_exc_ne_un: len:8 +cond_exc_lt: len:8 +cond_exc_lt_un: len:8 +cond_exc_gt: len:8 +cond_exc_gt_un: len:8 +cond_exc_ge: len:8 +cond_exc_ge_un: len:8 +cond_exc_le: len:8 +cond_exc_le_un: len:8 +cond_exc_ov: len:12 +cond_exc_no: len:8 +cond_exc_c: len:12 +cond_exc_nc: len:8 +long_conv_to_ovf_i: dest:i src1:i src2:i len:32 +long_mul_ovf: +long_conv_to_r_un: dest:f src1:i src2:i len:37 +float_beq: len:8 +float_bne_un: len:8 +float_blt: len:8 +float_blt_un: len:8 +float_bgt: len:8 +float_bgt_un: len:8 +float_bge: len:8 +float_bge_un: len:8 +float_ble: len:8 +float_ble_un: len:8 +float_add: dest:f src1:f src2:f len:4 +float_sub: dest:f src1:f src2:f len:4 +float_mul: dest:f src1:f src2:f len:4 +float_div: dest:f src1:f src2:f len:4 +float_div_un: dest:f src1:f src2:f len:4 +float_rem: dest:f src1:f src2:f len:16 +float_rem_un: dest:f src1:f src2:f len:16 +float_neg: dest:f src1:f len:4 +float_not: dest:f src1:f len:4 +float_conv_to_i1: dest:i src1:f len:40 +float_conv_to_i2: dest:i src1:f len:40 +float_conv_to_i4: dest:i src1:f len:40 +float_conv_to_i8: dest:l src1:f len:40 +float_conv_to_r4: dest:f src1:f len:4 +float_conv_to_u4: dest:i src1:f len:40 +float_conv_to_u8: dest:l src1:f len:40 +float_conv_to_u2: dest:i src1:f len:40 +float_conv_to_u1: dest:i src1:f len:40 +float_conv_to_i: dest:i src1:f len:40 +float_ceq: dest:i src1:f src2:f len:16 +float_cgt: dest:i src1:f src2:f len:16 +float_cgt_un: dest:i src1:f src2:f len:20 +float_clt: dest:i src1:f src2:f len:16 +float_clt_un: dest:i src1:f src2:f len:20 +float_conv_to_u: dest:i src1:f len:36 +float_cneq: dest:i src1:f src2:f len:16 +float_cge: dest:i src1:f src2:f len:16 +float_cle: dest:i src1:f src2:f len:16 +call_handler: len:12 clob:c +endfilter: src1:i len:32 +aot_const: dest:i len:8 +load_gotaddr: dest:i len:32 +got_entry: dest:i src1:b len:32 +sqrt: dest:f src1:f len:4 +adc: dest:i src1:i src2:i len:4 +addcc: dest:i src1:i src2:i len:4 +subcc: dest:i src1:i src2:i len:4 +addcc_imm: dest:i src1:i len:4 +sbb: dest:i src1:i src2:i len:4 +br_reg: src1:i len:8 +ppc_subfic: dest:i src1:i len:4 +ppc_subfze: dest:i src1:i len:4 +bigmul: len:12 dest:l src1:i src2:i +bigmul_un: len:12 dest:l src1:i src2:i + +# Linear IR opcodes +dummy_use: src1:i len:0 +dummy_store: len:0 +not_reached: len:0 +not_null: src1:i len:0 + +# 32 bit opcodes +int_add: dest:i src1:i src2:i len:4 +int_sub: dest:i src1:i src2:i len:4 +int_mul: dest:i src1:i src2:i len:4 +int_div: dest:i src1:i src2:i len:40 +int_div_un: dest:i src1:i src2:i len:16 +int_rem: dest:i src1:i src2:i len:48 +int_rem_un: dest:i src1:i src2:i len:24 +int_and: dest:i src1:i src2:i len:4 +int_or: dest:i src1:i src2:i len:4 +int_xor: dest:i src1:i src2:i len:4 +int_shl: dest:i src1:i src2:i len:4 +int_shr: dest:i src1:i src2:i len:4 +int_shr_un: dest:i src1:i src2:i len:4 +int_neg: dest:i src1:i len:4 +int_not: dest:i src1:i len:4 +int_conv_to_i1: dest:i src1:i len:8 +int_conv_to_i2: dest:i src1:i len:8 +int_conv_to_i4: dest:i src1:i len:4 +int_conv_to_r4: dest:f src1:i len:36 +int_conv_to_r8: dest:f src1:i len:36 +int_conv_to_u4: dest:i src1:i +int_conv_to_u2: dest:i src1:i len:8 +int_conv_to_u1: dest:i src1:i len:4 +int_beq: len:8 +int_bge: len:8 +int_bgt: len:8 +int_ble: len:8 +int_blt: len:8 +int_bne_un: len:8 +int_bge_un: len:8 +int_bgt_un: len:8 +int_ble_un: len:8 +int_blt_un: len:8 +int_add_ovf: dest:i src1:i src2:i len:16 +int_add_ovf_un: dest:i src1:i src2:i len:16 +int_mul_ovf: dest:i src1:i src2:i len:16 +int_mul_ovf_un: dest:i src1:i src2:i len:16 +int_sub_ovf: dest:i src1:i src2:i len:16 +int_sub_ovf_un: dest:i src1:i src2:i len:16 + +int_adc: dest:i src1:i src2:i len:4 +int_addcc: dest:i src1:i src2:i len:4 +int_subcc: dest:i src1:i src2:i len:4 +int_sbb: dest:i src1:i src2:i len:4 +int_adc_imm: dest:i src1:i len:12 +int_sbb_imm: dest:i src1:i len:12 + +int_add_imm: dest:i src1:i len:12 +int_sub_imm: dest:i src1:i len:12 +int_mul_imm: dest:i src1:i len:12 +int_div_imm: dest:i src1:i len:20 +int_div_un_imm: dest:i src1:i len:12 +int_rem_imm: dest:i src1:i len:28 +int_rem_un_imm: dest:i src1:i len:16 +int_and_imm: dest:i src1:i len:12 +int_or_imm: dest:i src1:i len:12 +int_xor_imm: dest:i src1:i len:12 +int_shl_imm: dest:i src1:i len:8 +int_shr_imm: dest:i src1:i len:8 +int_shr_un_imm: dest:i src1:i len:8 + +int_ceq: dest:i len:12 +int_cgt: dest:i len:12 +int_cgt_un: dest:i len:12 +int_clt: dest:i len:12 +int_clt_un: dest:i len:12 + +int_cneq: dest:i len:12 +int_cge: dest:i len:12 +int_cle: dest:i len:12 +int_cge_un: dest:i len:12 +int_cle_un: dest:i len:12 + +cond_exc_ieq: len:8 +cond_exc_ine_un: len:8 +cond_exc_ilt: len:8 +cond_exc_ilt_un: len:8 +cond_exc_igt: len:8 +cond_exc_igt_un: len:8 +cond_exc_ige: len:8 +cond_exc_ige_un: len:8 +cond_exc_ile: len:8 +cond_exc_ile_un: len:8 +cond_exc_iov: len:12 +cond_exc_ino: len:8 +cond_exc_ic: len:12 +cond_exc_inc: len:8 + +icompare: src1:i src2:i len:4 +icompare_imm: src1:i len:12 + +long_conv_to_ovf_i4_2: dest:i src1:i src2:i len:32 + +vcall2: len:20 clob:c +vcall2_reg: src1:i len:8 clob:c +vcall2_membase: src1:b len:16 clob:c + +jump_table: dest:i len:8 + +atomic_add_i4: src1:b src2:i dest:i len:28 +atomic_cas_i4: src1:b src2:i src3:i dest:i len:38 + +gc_safe_point: len:0 diff --git a/unity-2019.4.24f1-mbe/mono/mini/cpu-ppc64.md b/unity-2019.4.24f1-mbe/mono/mini/cpu-ppc64.md new file mode 100644 index 000000000..2c28e7dca --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/cpu-ppc64.md @@ -0,0 +1,398 @@ +# powerpc cpu description file +# this file is read by genmdesc to pruduce a table with all the relevant information +# about the cpu instructions that may be used by the regsiter allocator, the scheduler +# and other parts of the arch-dependent part of mini. +# +# An opcode name is followed by a colon and optional specifiers. +# A specifier has a name, a colon and a value. Specifiers are separated by white space. +# Here is a description of the specifiers valid for this file and their possible values. +# +# dest:register describes the destination register of an instruction +# src1:register describes the first source register of an instruction +# src2:register describes the second source register of an instruction +# +# register may have the following values: +# i integer register +# a r3 register (output from calls) +# b base register (used in address references) +# f floating point register +# +# len:number describe the maximun length in bytes of the instruction +# number is a positive integer +# +# cost:number describe how many cycles are needed to complete the instruction (unused) +# +# clob:spec describe if the instruction clobbers registers or has special needs +# +# spec can be one of the following characters: +# c clobbers caller-save registers +# r 'reserves' the destination register until a later instruction unreserves it +# used mostly to set output registers in function calls +# +# flags:spec describe if the instruction uses or sets the flags (unused) +# +# spec can be one of the following chars: +# s sets the flags +# u uses the flags +# m uses and modifies the flags +# +# res:spec describe what units are used in the processor (unused) +# +# delay: describe delay slots (unused) +# +# the required specifiers are: len, clob (if registers are clobbered), the registers +# specifiers if the registers are actually used, flags (when scheduling is implemented). +# +# See the code in mini-x86.c for more details on how the specifiers are used. +# +tailcall: len:124 clob:c +memory_barrier: len:4 +nop: len:4 +relaxed_nop: len:4 +break: len:40 +seq_point: len:48 +il_seq_point: len:0 +jmp: len:96 +call: dest:a clob:c len:36 +br: len:4 +throw: src1:i len:40 +rethrow: src1:i len:40 +ckfinite: dest:f src1:f +ppc_check_finite: src1:i len:16 +add_ovf_carry: dest:i src1:i src2:i len:16 +sub_ovf_carry: dest:i src1:i src2:i len:16 +add_ovf_un_carry: dest:i src1:i src2:i len:16 +sub_ovf_un_carry: dest:i src1:i src2:i len:16 +start_handler: len:16 +endfinally: len:20 +ceq: dest:i len:12 +cgt: dest:i len:12 +cgt.un: dest:i len:12 +clt: dest:i len:12 +clt.un: dest:i len:12 +localloc: dest:i src1:i len:60 +compare: src1:i src2:i len:4 +compare_imm: src1:i len:12 +fcompare: src1:f src2:f len:12 +oparglist: src1:i len:12 +setlret: src1:i src2:i len:12 +checkthis: src1:b len:4 +voidcall: len:36 clob:c +voidcall_reg: src1:i len:16 clob:c +voidcall_membase: src1:b len:16 clob:c +fcall: dest:g len:36 clob:c +fcall_reg: dest:g src1:i len:16 clob:c +fcall_membase: dest:g src1:b len:16 clob:c +lcall: dest:a len:36 clob:c +lcall_reg: dest:a src1:i len:16 clob:c +lcall_membase: dest:a src1:b len:16 clob:c +vcall: len:16 clob:c +vcall_reg: src1:i len:16 clob:c +vcall_membase: src1:b len:12 clob:c +call_reg: dest:a src1:i len:16 clob:c +call_membase: dest:a src1:b len:16 clob:c +iconst: dest:i len:20 +i8const: dest:i len:20 +r4const: dest:f len:12 +r8const: dest:f len:24 +label: len:0 +store_membase_reg: dest:b src1:i len:12 +storei1_membase_reg: dest:b src1:i len:12 +storei2_membase_reg: dest:b src1:i len:12 +storei4_membase_reg: dest:b src1:i len:12 +storei8_membase_reg: dest:b src1:i len:12 +storer4_membase_reg: dest:b src1:f len:16 +storer8_membase_reg: dest:b src1:f len:12 +load_membase: dest:i src1:b len:12 +loadi1_membase: dest:i src1:b len:16 +loadu1_membase: dest:i src1:b len:12 +loadi2_membase: dest:i src1:b len:12 +loadu2_membase: dest:i src1:b len:12 +loadi4_membase: dest:i src1:b len:12 +loadu4_membase: dest:i src1:b len:12 +loadi8_membase: dest:i src1:b len:12 +loadr4_membase: dest:f src1:b len:12 +loadr8_membase: dest:f src1:b len:12 +load_memindex: dest:i src1:b src2:i len:4 +loadi1_memindex: dest:i src1:b src2:i len:8 +loadu1_memindex: dest:i src1:b src2:i len:4 +loadi2_memindex: dest:i src1:b src2:i len:4 +loadu2_memindex: dest:i src1:b src2:i len:4 +loadi4_memindex: dest:i src1:b src2:i len:4 +loadu4_memindex: dest:i src1:b src2:i len:4 +loadi8_memindex: dest:i src1:b src2:i len:4 +loadr4_memindex: dest:f src1:b src2:i len:4 +loadr8_memindex: dest:f src1:b src2:i len:4 +store_memindex: dest:b src1:i src2:i len:4 +storei1_memindex: dest:b src1:i src2:i len:4 +storei2_memindex: dest:b src1:i src2:i len:4 +storei4_memindex: dest:b src1:i src2:i len:4 +storei8_memindex: dest:b src1:i src2:i len:4 +storer4_memindex: dest:b src1:i src2:i len:8 +storer8_memindex: dest:b src1:i src2:i len:4 +loadu4_mem: dest:i len:8 +move: dest:i src1:i len:4 +fmove: dest:f src1:f len:4 +add_imm: dest:i src1:i len:4 +sub_imm: dest:i src1:i len:4 +mul_imm: dest:i src1:i len:4 +# there is no actual support for division or reminder by immediate +# we simulate them, though (but we need to change the burg rules +# to allocate a symbolic reg for src2) +div_imm: dest:i src1:i src2:i len:20 +div_un_imm: dest:i src1:i src2:i len:12 +rem_imm: dest:i src1:i src2:i len:28 +rem_un_imm: dest:i src1:i src2:i len:16 +and_imm: dest:i src1:i len:4 +or_imm: dest:i src1:i len:4 +xor_imm: dest:i src1:i len:4 +shl_imm: dest:i src1:i len:4 +shr_imm: dest:i src1:i len:4 +shr_un_imm: dest:i src1:i len:4 +cond_exc_eq: len:8 +cond_exc_ne_un: len:8 +cond_exc_lt: len:8 +cond_exc_lt_un: len:8 +cond_exc_gt: len:8 +cond_exc_gt_un: len:8 +cond_exc_ge: len:8 +cond_exc_ge_un: len:8 +cond_exc_le: len:8 +cond_exc_le_un: len:8 +cond_exc_ov: len:12 +cond_exc_no: len:8 +cond_exc_c: len:12 +cond_exc_nc: len:8 +long_conv_to_ovf_i: dest:i src1:i src2:i len:32 +#long_mul_ovf: +long_conv_to_r_un: dest:f src1:i src2:i len:37 +float_beq: len:8 +float_bne_un: len:8 +float_blt: len:8 +float_blt_un: len:8 +float_bgt: len:8 +float_bgt_un: len:8 +float_bge: len:8 +float_bge_un: len:8 +float_ble: len:8 +float_ble_un: len:8 +float_add: dest:f src1:f src2:f len:4 +float_sub: dest:f src1:f src2:f len:4 +float_mul: dest:f src1:f src2:f len:4 +float_div: dest:f src1:f src2:f len:4 +float_div_un: dest:f src1:f src2:f len:4 +float_rem: dest:f src1:f src2:f len:16 +float_rem_un: dest:f src1:f src2:f len:16 +float_neg: dest:f src1:f len:4 +float_not: dest:f src1:f len:4 +float_conv_to_i1: dest:i src1:f len:40 +float_conv_to_i2: dest:i src1:f len:40 +float_conv_to_i4: dest:i src1:f len:40 +float_conv_to_i8: dest:i src1:f len:40 +float_conv_to_r4: dest:f src1:f len:4 +float_conv_to_u4: dest:i src1:f len:40 +float_conv_to_u8: dest:i src1:f len:40 +float_conv_to_u2: dest:i src1:f len:40 +float_conv_to_u1: dest:i src1:f len:40 +float_conv_to_i: dest:i src1:f len:40 +float_ceq: dest:i src1:f src2:f len:16 +float_cgt: dest:i src1:f src2:f len:16 +float_cgt_un: dest:i src1:f src2:f len:20 +float_clt: dest:i src1:f src2:f len:16 +float_clt_un: dest:i src1:f src2:f len:20 +float_conv_to_u: dest:i src1:f len:36 +float_cneq: dest:i src1:f src2:f len:16 +float_cge: dest:i src1:f src2:f len:16 +float_cle: dest:i src1:f src2:f len:16 +call_handler: len:12 clob:c +endfilter: src1:i len:20 +aot_const: dest:i len:8 +load_gotaddr: dest:i len:32 +got_entry: dest:i src1:b len:32 +sqrt: dest:f src1:f len:4 +adc: dest:i src1:i src2:i len:4 +addcc: dest:i src1:i src2:i len:4 +subcc: dest:i src1:i src2:i len:4 +addcc_imm: dest:i src1:i len:4 +sbb: dest:i src1:i src2:i len:4 +br_reg: src1:i len:8 +ppc_subfic: dest:i src1:i len:4 +ppc_subfze: dest:i src1:i len:4 +bigmul: len:12 dest:i src1:i src2:i +bigmul_un: len:12 dest:i src1:i src2:i + +# Linear IR opcodes +dummy_use: src1:i len:0 +dummy_store: len:0 +not_reached: len:0 +not_null: src1:i len:0 + +# 32 bit opcodes +int_add: dest:i src1:i src2:i len:4 +int_sub: dest:i src1:i src2:i len:4 +int_mul: dest:i src1:i src2:i len:4 +int_div: dest:i src1:i src2:i len:40 +int_div_un: dest:i src1:i src2:i len:16 +int_rem: dest:i src1:i src2:i len:48 +int_rem_un: dest:i src1:i src2:i len:24 +int_and: dest:i src1:i src2:i len:4 +int_or: dest:i src1:i src2:i len:4 +int_xor: dest:i src1:i src2:i len:4 +int_shl: dest:i src1:i src2:i len:4 +int_shr: dest:i src1:i src2:i len:4 +int_shr_un: dest:i src1:i src2:i len:4 +int_neg: dest:i src1:i len:4 +int_not: dest:i src1:i len:4 +int_conv_to_i1: dest:i src1:i len:8 +int_conv_to_i2: dest:i src1:i len:8 +int_conv_to_i4: dest:i src1:i len:4 +sext_i4: dest:i src1:i len:4 +int_conv_to_r4: dest:f src1:i len:20 +int_conv_to_r8: dest:f src1:i len:16 +int_conv_to_u4: dest:i src1:i len:4 +int_conv_to_u2: dest:i src1:i len:8 +int_conv_to_u1: dest:i src1:i len:4 +int_beq: len:8 +int_bge: len:8 +int_bgt: len:8 +int_ble: len:8 +int_blt: len:8 +int_bne_un: len:8 +int_bge_un: len:8 +int_bgt_un: len:8 +int_ble_un: len:8 +int_blt_un: len:8 +int_add_ovf: dest:i src1:i src2:i len:16 +int_add_ovf_un: dest:i src1:i src2:i len:16 +int_mul_ovf: dest:i src1:i src2:i len:16 +int_mul_ovf_un: dest:i src1:i src2:i len:16 +int_sub_ovf: dest:i src1:i src2:i len:16 +int_sub_ovf_un: dest:i src1:i src2:i len:16 + +int_adc: dest:i src1:i src2:i len:4 +int_addcc: dest:i src1:i src2:i len:4 +int_subcc: dest:i src1:i src2:i len:4 +int_sbb: dest:i src1:i src2:i len:4 +int_adc_imm: dest:i src1:i len:12 +int_sbb_imm: dest:i src1:i len:12 + +int_add_imm: dest:i src1:i len:4 +int_sub_imm: dest:i src1:i len:12 +int_mul_imm: dest:i src1:i len:12 +int_div_imm: dest:i src1:i len:20 +int_div_un_imm: dest:i src1:i len:12 +int_rem_imm: dest:i src1:i len:28 +int_rem_un_imm: dest:i src1:i len:16 +int_and_imm: dest:i src1:i len:12 +int_or_imm: dest:i src1:i len:12 +int_xor_imm: dest:i src1:i len:12 +int_shl_imm: dest:i src1:i len:8 +int_shr_imm: dest:i src1:i len:8 +int_shr_un_imm: dest:i src1:i len:8 + +int_ceq: dest:i len:12 +int_cgt: dest:i len:12 +int_cgt_un: dest:i len:12 +int_clt: dest:i len:12 +int_clt_un: dest:i len:12 + +int_cneq: dest:i len:12 +int_cge: dest:i len:12 +int_cle: dest:i len:12 +int_cge_un: dest:i len:12 +int_cle_un: dest:i len:12 + +cond_exc_ieq: len:8 +cond_exc_ine_un: len:8 +cond_exc_ilt: len:8 +cond_exc_ilt_un: len:8 +cond_exc_igt: len:8 +cond_exc_igt_un: len:8 +cond_exc_ige: len:8 +cond_exc_ige_un: len:8 +cond_exc_ile: len:8 +cond_exc_ile_un: len:8 +cond_exc_iov: len:12 +cond_exc_ino: len:8 +cond_exc_ic: len:12 +cond_exc_inc: len:8 + +icompare: src1:i src2:i len:4 +icompare_imm: src1:i len:12 + +# 64 bit opcodes +long_add: dest:i src1:i src2:i len:4 +long_sub: dest:i src1:i src2:i len:4 +long_mul: dest:i src1:i src2:i len:4 +long_mul_imm: dest:i src1:i len:4 +long_div: dest:i src1:i src2:i len:40 +long_div_un: dest:i src1:i src2:i len:16 +long_rem: dest:i src1:i src2:i len:48 +long_rem_un: dest:i src1:i src2:i len:24 +long_and: dest:i src1:i src2:i len:4 +long_or: dest:i src1:i src2:i len:4 +long_xor: dest:i src1:i src2:i len:4 +long_shl: dest:i src1:i src2:i len:4 +long_shl_imm: dest:i src1:i len:4 +long_shr: dest:i src1:i src2:i len:4 +long_shr_un: dest:i src1:i src2:i len:4 +long_shr_imm: dest:i src1:i len:4 +long_shr_un_imm: dest:i src1:i len:4 +long_neg: dest:i src1:i len:4 +long_not: dest:i src1:i len:4 +long_conv_to_i1: dest:i src1:i len:4 +long_conv_to_i2: dest:i src1:i len:4 +long_conv_to_i4: dest:i src1:i len:4 +long_conv_to_r4: dest:f src1:i len:16 +long_conv_to_r8: dest:f src1:i len:12 +long_conv_to_u4: dest:i src1:i +long_conv_to_u2: dest:i src1:i len:4 +long_conv_to_u1: dest:i src1:i len:4 +zext_i4: dest:i src1:i len:4 + +long_beq: len:8 +long_bge: len:8 +long_bgt: len:8 +long_ble: len:8 +long_blt: len:8 +long_bne_un: len:8 +long_bge_un: len:8 +long_bgt_un: len:8 +long_ble_un: len:8 +long_blt_un: len:8 +long_add_ovf: dest:i src1:i src2:i len:16 +long_add_ovf_un: dest:i src1:i src2:i len:16 +long_mul_ovf: dest:i src1:i src2:i len:16 +long_mul_ovf_un: dest:i src1:i src2:i len:16 +long_sub_ovf: dest:i src1:i src2:i len:16 +long_sub_ovf_un: dest:i src1:i src2:i len:16 + +long_ceq: dest:i len:12 +long_cgt: dest:i len:12 +long_cgt_un: dest:i len:12 +long_clt: dest:i len:12 +long_clt_un: dest:i len:12 + +long_add_imm: dest:i src1:i clob:1 len:4 +long_sub_imm: dest:i src1:i clob:1 len:4 +long_and_imm: dest:i src1:i clob:1 len:4 +long_or_imm: dest:i src1:i clob:1 len:4 +long_xor_imm: dest:i src1:i clob:1 len:4 + +lcompare: src1:i src2:i len:4 +lcompare_imm: src1:i len:12 + +#long_conv_to_ovf_i4_2: dest:i src1:i src2:i len:30 + +vcall2: len:36 clob:c +vcall2_reg: src1:i len:16 clob:c +vcall2_membase: src1:b len:16 clob:c + +jump_table: dest:i len:20 + +atomic_add_i4: src1:b src2:i dest:i len:28 +atomic_add_i8: src1:b src2:i dest:i len:28 +atomic_cas_i4: src1:b src2:i src3:i dest:i len:38 +atomic_cas_i8: src1:b src2:i src3:i dest:i len:38 + +gc_safe_point: len:0 diff --git a/unity-2019.4.24f1-mbe/mono/mini/cpu-s390x.md b/unity-2019.4.24f1-mbe/mono/mini/cpu-s390x.md new file mode 100644 index 000000000..f0a89c6fc --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/cpu-s390x.md @@ -0,0 +1,418 @@ +# S/390 cpu description file +# this file is read by genmdesc to pruduce a table with all the relevant information +# about the cpu instructions that may be used by the regsiter allocator, the scheduler +# and other parts of the arch-dependent part of mini. +# +# An opcode name is followed by a colon and optional specifiers. +# A specifier has a name, a colon and a value. Specifiers are separated by white space. +# Here is a description of the specifiers valid for this file and their possible values. +# +# dest:register describes the destination register of an instruction +# src1:register describes the first source register of an instruction +# src2:register describes the second source register of an instruction +# +# register may have the following values: +# i integer register +# a r3 register (output from calls) +# b base register (used in address references) +# f floating point register +# +# len:number describe the maximun length in bytes of the instruction +# number is a positive integer +# +# cost:number describe how many cycles are needed to complete the instruction (unused) +# +# clob:spec describe if the instruction clobbers registers or has special needs +# +# spec can be one of the following characters: +# c clobbers caller-save registers +# r 'reserves' the destination register until a later instruction unreserves it +# used mostly to set output registers in function calls +# +# flags:spec describe if the instruction uses or sets the flags (unused) +# +# spec can be one of the following chars: +# s sets the flags +# u uses the flags +# m uses and modifies the flags +# +# res:spec describe what units are used in the processor (unused) +# +# delay: describe delay slots (unused) +# +# the required specifiers are: len, clob (if registers are clobbered), the registers +# specifiers if the registers are actually used, flags (when scheduling is implemented). +# +# See the code in mini-x86.c for more details on how the specifiers are used. +# + +adc: dest:i src1:i src2:i len:6 +adc_imm: dest:i src1:i len:14 +add_imm: dest:i src1:i len:24 +add_ovf_carry: dest:i src1:1 src2:i len:28 +add_ovf_un_carry: dest:i src1:1 src2:i len:12 +addcc: dest:i src1:i src2:i len:12 +and_imm: dest:i src1:i len:24 +aot_const: dest:i len:8 +atomic_add_i4: src1:b src2:i dest:i len:28 +atomic_add_i8: src1:b src2:i dest:i len:30 +atomic_exchange_i4: src1:b src2:i dest:i len:18 +atomic_exchange_i8: src1:b src2:i dest:i len:24 +br: len:6 +br_reg: src1:i len:8 +break: len:22 +call: dest:o clob:c len:26 +call_handler: len:12 clob:c +call_membase: dest:o src1:b len:12 clob:c +call_reg: dest:o src1:i len:8 clob:c +ceq: dest:i len:12 +cgt.un: dest:i len:12 +cgt: dest:i len:12 +checkthis: src1:b len:16 +ckfinite: dest:f src1:f len:22 +clt.un: dest:i len:12 +clt: dest:i len:12 +compare: src1:i src2:i len:4 +compare_imm: src1:i len:20 +cond_exc_c: len:8 +cond_exc_eq: len:8 +cond_exc_ge: len:8 +cond_exc_ge_un: len:8 +cond_exc_gt: len:8 +cond_exc_gt_un: len:8 +cond_exc_le: len:8 +cond_exc_le_un: len:8 +cond_exc_lt: len:8 +cond_exc_lt_un: len:8 +cond_exc_nc: len:8 +cond_exc_ne_un: len:8 +cond_exc_no: len:8 +cond_exc_ov: len:8 +div_imm: dest:i src1:i len:24 +div_un_imm: dest:i src1:i len:24 +endfinally: len:8 +fcall: dest:g len:26 clob:c +fcall_membase: dest:g src1:b len:14 clob:c +fcall_reg: dest:g src1:i len:10 clob:c +fcompare: src1:f src2:f len:14 +float_add: dest:f src1:f src2:f len:6 + +float_beq: len:10 +float_bge: len:10 +float_bge_un: len:8 +float_bgt: len:10 +float_ble: len:10 +float_ble_un: len:8 +float_blt: len:10 +float_blt_un: len:8 +float_bne_un: len:8 +float_bgt_un: len:8 + +float_ceq: dest:i src1:f src2:f len:16 +float_cgt: dest:i src1:f src2:f len:16 +float_cgt_un: dest:i src1:f src2:f len:16 +float_clt: dest:i src1:f src2:f len:16 +float_clt_un: dest:i src1:f src2:f len:16 +float_cneq: dest:y src1:f src2:f len:16 +float_cge: dest:y src1:f src2:f len:16 +float_cle: dest:y src1:f src2:f len:16 + +float_conv_to_i1: dest:i src1:f len:50 +float_conv_to_i2: dest:i src1:f len:50 +float_conv_to_i4: dest:i src1:f len:50 +float_conv_to_i8: dest:l src1:f len:50 +float_conv_to_i: dest:i src1:f len:52 +float_conv_to_r4: dest:f src1:f len:8 +float_conv_to_u1: dest:i src1:f len:72 +float_conv_to_u2: dest:i src1:f len:72 +float_conv_to_u4: dest:i src1:f len:72 +float_conv_to_u8: dest:i src1:f len:72 +float_conv_to_u: dest:i src1:f len:36 +float_div: dest:f src1:f src2:f len:6 +float_div_un: dest:f src1:f src2:f len:6 +float_mul: dest:f src1:f src2:f len:6 +float_neg: dest:f src1:f len:6 +float_not: dest:f src1:f len:6 +float_rem: dest:f src1:f src2:f len:16 +float_rem_un: dest:f src1:f src2:f len:16 +float_sub: dest:f src1:f src2:f len:6 +fmove: dest:f src1:f len:4 +move_f_to_i4: dest:i src1:f len:14 +move_i4_to_f: dest:f src1:i len:14 +move_f_to_i8: dest:i src1:f len:4 +move_i8_to_f: dest:f src1:i len:8 +i8const: dest:i len:20 +icompare: src1:i src2:i len:4 +icompare_imm: src1:i len:18 +iconst: dest:i len:40 +jmp: len:50 +label: len:0 +lcall: dest:o len:22 clob:c +lcall_membase: dest:o src1:b len:12 clob:c +lcall_reg: dest:o src1:i len:8 clob:c +lcompare: src1:i src2:i len:4 +load_membase: dest:i src1:b len:30 +loadi1_membase: dest:i src1:b len:40 +loadi2_membase: dest:i src1:b len:30 +loadi4_membase: dest:i src1:b len:30 +loadi8_membase: dest:i src1:b len:30 +loadr4_membase: dest:f src1:b len:28 +loadr8_membase: dest:f src1:b len:28 +loadu1_membase: dest:i src1:b len:30 +loadu2_membase: dest:i src1:b len:30 +loadu4_mem: dest:i len:8 +loadu4_membase: dest:i src1:b len:30 +localloc: dest:i src1:i len:106 +memory_barrier: len:10 +move: dest:i src1:i len:4 +mul_imm: dest:i src1:i len:24 +nop: len:4 +relaxed_nop: len:4 +oparglist: src1:i len:28 +bigmul: len:2 dest:i src1:a src2:i +bigmul_un: len:2 dest:i src1:a src2:i +endfilter: src1:i len:28 +rethrow: src1:i len:26 +or_imm: dest:i src1:i len:24 +r4const: dest:f len:26 +r8const: dest:f len:24 +rem_imm: dest:i src1:i len:24 +rem_un_imm: dest:i src1:i len:24 +s390_bkchain: len:8 dest:i src1:i +s390_move: len:48 dest:b src1:b +s390_setf4ret: dest:f src1:f len:4 +sbb: dest:i src1:i src2:i len:6 +sbb_imm: dest:i src1:i len:14 +seq_point: len:54 +il_seq_point: len:0 +sext_i4: dest:i src1:i len:4 +zext_i4: dest:i src1:i len:4 +shl_imm: dest:i src1:i len:10 +shr_imm: dest:i src1:i len:10 +shr_un_imm: dest:i src1:i len:10 +sqrt: dest:f src1:f len:4 +start_handler: len:26 +store_membase_imm: dest:b len:46 +store_membase_reg: dest:b src1:i len:26 +storei1_membase_imm: dest:b len:46 +storei1_membase_reg: dest:b src1:i len:26 +storei2_membase_imm: dest:b len:46 +storei2_membase_reg: dest:b src1:i len:26 +storei4_membase_imm: dest:b len:46 +storei4_membase_reg: dest:b src1:i len:26 +storei8_membase_imm: dest:b len:46 +storei8_membase_reg: dest:b src1:i len:26 +storer4_membase_reg: dest:b src1:f len:28 +storer8_membase_reg: dest:b src1:f len:24 +sub_imm: dest:i src1:i len:18 +sub_ovf_carry: dest:i src1:1 src2:i len:28 +sub_ovf_un_carry: dest:i src1:1 src2:i len:12 +subcc: dest:i src1:i src2:i len:12 +throw: src1:i len:26 +tls_get: dest:1 len:32 +tls_set: src1:1 len:32 +vcall: len:22 clob:c +vcall_membase: src1:b len:12 clob:c +vcall_reg: src1:i len:8 clob:c +voidcall: len:22 clob:c +voidcall_membase: src1:b len:12 clob:c +voidcall_reg: src1:i len:8 clob:c +xor_imm: dest:i src1:i len:20 + +# 32 bit opcodes +int_adc: dest:i src1:i src2:i len:12 +int_adc_imm: dest:i src1:i len:14 +int_addcc: dest:i src1:i src2:i len:12 +int_add: dest:i src1:i src2:i len:12 +int_add_imm: dest:i src1:i len:20 +int_and: dest:i src1:i src2:i len:12 +int_and_imm: dest:i src1:i len:24 +int_beq: len:8 +int_bge: len:8 +int_bge_un: len:8 +int_bgt: len:8 +int_bgt_un: len:8 +int_ble: len:8 +int_ble_un: len:8 +int_blt: len:8 +int_blt_un: len:8 +int_bne_un: len:8 + +int_ceq: dest:i len:12 +int_cgt: dest:i len:12 +int_cgt_un: dest:i len:12 +int_clt: dest:i len:12 +int_clt_un: dest:i len:12 + +int_cneq: dest:i len:12 +int_cge: dest:i len:12 +int_cle: dest:i len:12 +int_cge_un: dest:i len:12 +int_cle_un: dest:i len:12 + +int_div: dest:a src1:i src2:i len:16 +int_div_imm: dest:a src1:i len:24 +int_div_un: dest:a src1:i src2:i len:16 +int_div_un_imm: dest:a src1:i len:24 +int_mul: dest:i src1:i src2:i len:16 +int_mul_imm: dest:i src1:i len:24 +int_mul_ovf: dest:i src1:i src2:i len:44 +int_mul_ovf_un: dest:i src1:i src2:i len:22 +int_add_ovf: dest:i src1:i src2:i len:32 +int_add_ovf_un: dest:i src1:i src2:i len:32 +int_sub_ovf: dest:i src1:i src2:i len:32 +int_sub_ovf_un: dest:i src1:i src2:i len:32 +int_neg: dest:i src1:i len:12 +int_not: dest:i src1:i len:12 +int_or: dest:i src1:i src2:i len:12 +int_or_imm: dest:i src1:i len:24 +int_rem: dest:d src1:i src2:i len:16 +int_rem_imm: dest:d src1:i len:24 +int_rem_un: dest:d src1:i src2:i len:16 +int_rem_un_imm: dest:d src1:i len:24 +int_sbb: dest:i src1:i src2:i len:6 +int_sbb_imm: dest:i src1:i len:14 +int_shl: dest:i src1:i src2:i clob:s len:12 +int_shl_imm: dest:i src1:i len:10 +int_shr: dest:i src1:i src2:i clob:s len:12 +int_shr_imm: dest:i src1:i len:10 +int_shr_un: dest:i src1:i src2:i clob:s len:12 +int_shr_un_imm: dest:i src1:i len:10 +int_subcc: dest:i src1:i src2:i len:12 +int_sub: dest:i src1:i src2:i len:12 +int_sub_imm: dest:i src1:i len:20 +int_xor: dest:i src1:i src2:i len:12 +int_xor_imm: dest:i src1:i len:24 +int_conv_to_r4: dest:f src1:i len:16 +int_conv_to_r8: dest:f src1:i len:16 + +# 64 bit opcodes +long_add: dest:i src1:i src2:i len:12 +long_sub: dest:i src1:i src2:i len:12 +long_add_ovf: dest:i src1:i src2:i len:32 +long_add_ovf_un: dest:i src1:i src2:i len:32 +long_div: dest:i src1:i src2:i len:12 +long_div_un: dest:i src1:i src2:i len:16 +long_mul: dest:i src1:i src2:i len:12 +long_mul_imm: dest:i src1:i len:20 +long_mul_ovf: dest:i src1:i src2:i len:56 +long_mul_ovf_un: dest:i src1:i src2:i len:64 +long_and: dest:i src1:i src2:i len:8 +long_or: dest:i src1:i src2:i len:8 +long_xor: dest:i src1:i src2:i len:8 +long_neg: dest:i src1:i len:6 +long_not: dest:i src1:i len:12 +long_rem: dest:i src1:i src2:i len:12 +long_rem_imm: dest:i src1:i len:12 +long_rem_un: dest:i src1:i src2:i len:16 +long_shl: dest:i src1:i src2:i len:14 +long_shl_imm: dest:i src1:i len:14 +long_shr_un: dest:i src1:i src2:i len:14 +long_shr: dest:i src1:i src2:i len:14 +long_shr_imm: dest:i src1:i len:14 +long_shr_un_imm: dest:i src1:i len:14 +long_sub_imm: dest:i src1:i len:16 +long_sub_ovf: dest:i src1:i src2:i len:16 +long_sub_ovf_un: dest:i src1:i src2:i len:28 + +long_conv_to_i1: dest:i src1:i len:12 +long_conv_to_i2: dest:i src1:i len:12 +long_conv_to_i4: dest:i src1:i len:4 +long_conv_to_i8: dest:i src1:i len:4 +long_conv_to_i: dest:i src1:i len:4 +long_conv_to_ovf_i: dest:i src1:i len:44 +long_conv_to_ovf_i4_un: dest:i src1:i len:50 +long_conv_to_ovf_u4: dest:i src1:i len:48 +long_conv_to_ovf_u8_un: dest:i src1:i len:4 +long_conv_to_r4: dest:f src1:i len:16 +long_conv_to_r8: dest:f src1:i len:16 +long_conv_to_u1: dest:i src1:i len:16 +long_conv_to_u2: dest:i src1:i len:24 +long_conv_to_u4: dest:i src1:i len:4 +long_conv_to_u8: dest:i src1:i len:4 +long_conv_to_u: dest:i src1:i len:4 +long_conv_to_r_un: dest:f src1:i len:37 + +long_beq: len:8 +long_bge_un: len:8 +long_bge: len:8 +long_bgt_un: len:8 +long_bgt: len:8 +long_ble_un: len:8 +long_ble: len:8 +long_blt_un: len:8 +long_blt: len:8 +long_bne_un: len:8 + +# Linear IR opcodes +dummy_use: src1:i len:0 +dummy_store: len:0 +not_reached: len:0 +not_null: src1:i len:0 + +jump_table: dest:i len:24 + +int_conv_to_i1: dest:i src1:i len:12 +int_conv_to_i2: dest:i src1:i len:12 +int_conv_to_i4: dest:i src1:i len:4 +int_conv_to_i: dest:i src1:i len:4 +int_conv_to_u1: dest:i src1:i len:10 +int_conv_to_u2: dest:i src1:i len:16 +int_conv_to_u4: dest:i src1:i len:4 +int_conv_to_r_un: dest:f src1:i len:37 + +cond_exc_ic: len:8 +cond_exc_ieq: len:8 +cond_exc_ige: len:8 +cond_exc_ige_un: len:8 +cond_exc_igt: len:8 +cond_exc_igt_un: len:8 +cond_exc_ile: len:8 +cond_exc_ile_un: len:8 +cond_exc_ilt: len:8 +cond_exc_ilt_un: len:8 +cond_exc_inc: len:8 +cond_exc_ine_un: len:8 +cond_exc_ino: len:8 +cond_exc_iov: len:8 + +lcompare_imm: src1:i len:20 + +long_add_imm: dest:i src1:i len:20 + +long_ceq: dest:i len:12 +long_cgt_un: dest:i len:12 +long_cgt: dest:i len:12 +long_clt_un: dest:i len:12 +long_clt: dest:i len:12 + +vcall2: len:22 clob:c +vcall2_membase: src1:b len:12 clob:c +vcall2_reg: src1:i len:8 clob:c + +s390_int_add_ovf: len:32 dest:i src1:i src2:i +s390_int_add_ovf_un: len:32 dest:i src1:i src2:i +s390_int_sub_ovf: len:32 dest:i src1:i src2:i +s390_int_sub_ovf_un: len:32 dest:i src1:i src2:i + +s390_long_add_ovf: dest:i src1:i src2:i len:32 +s390_long_add_ovf_un: dest:i src1:i src2:i len:32 +s390_long_sub_ovf: dest:i src1:i src2:i len:32 +s390_long_sub_ovf_un: dest:i src1:i src2:i len:32 + +gc_liveness_def: len:0 +gc_liveness_use: len:0 +gc_spill_slot_liveness_def: len:0 +gc_param_slot_liveness_def: len:0 +gc_safe_point: clob:c src1:i len:32 + +generic_class_init: src1:A len:32 clob:c + +s390_crj: src1:i src2:i len:24 +s390_crj_un: src1:i src2:i len:24 +s390_cgrj: src1:i src2:i len:24 +s390_cgrj_un: src1:i src2:i len:24 +s390_cij: len:24 +s390_cij_un: src1:i len:24 +s390_cgij: len:24 +s390_cgij_un: len:24 diff --git a/unity-2019.4.24f1-mbe/mono/mini/cpu-sparc.md b/unity-2019.4.24f1-mbe/mono/mini/cpu-sparc.md new file mode 100644 index 000000000..d3409bc1d --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/cpu-sparc.md @@ -0,0 +1,320 @@ +# sparc32 cpu description file +# this file is read by genmdesc to pruduce a table with all the relevant information +# about the cpu instructions that may be used by the register allocator, the scheduler +# and other parts of the arch-dependent part of mini. +# +# An opcode name is followed by a colon and optional specifiers. +# A specifier has a name, a colon and a value. Specifiers are separated by white space. +# Here is a description of the specifiers valid for this file and their possible values. +# +# dest:register describes the destination register of an instruction +# src1:register describes the first source register of an instruction +# src2:register describes the second source register of an instruction +# +# register may have the following values: +# i integer register +# b base register (used in address references) +# f floating point register +# L register pair (same as 'i' on v9) +# l %o0:%o1 register pair (same as 'i' on v9) +# o %o0 +# +# len:number describe the maximun length in bytes of the instruction +# number is a positive integer +# +# cost:number describe how many cycles are needed to complete the instruction (unused) +# +# clob:spec describe if the instruction clobbers registers or has special needs +# +# spec can be one of the following characters: +# c clobbers caller-save registers +# r 'reserves' the destination register until a later instruction unreserves it +# used mostly to set output registers in function calls +# +# flags:spec describe if the instruction uses or sets the flags (unused) +# +# spec can be one of the following chars: +# s sets the flags +# u uses the flags +# m uses and modifies the flags +# +# res:spec describe what units are used in the processor (unused) +# +# delay: describe delay slots (unused) +# +# the required specifiers are: len, clob (if registers are clobbered), the registers +# specifiers if the registers are actually used, flags (when scheduling is implemented). +# +# See the code in mini-sparc32.c for more details on how the specifiers are used. +# +label: len:0 +break: len:64 +jmp: len:64 +br: len:8 + +throw: src1:i len:64 +rethrow: src1:i len:64 +start_handler: len:64 +endfinally: len:64 +endfilter: src1:i len:64 + +ckfinite: dest:f src1:f len:40 +ceq: dest:i len:64 +cgt: dest:i len:64 +cgt.un: dest:i len:64 +clt: dest:i len:64 +clt.un: dest:i len:64 +localloc: dest:i src1:i len:64 +localloc_imm: dest:i len:64 +compare: src1:i src2:i len:4 +icompare: src1:i src2:i len:4 +compare_imm: src1:i len:64 +icompare_imm: src1:i len:64 +fcompare: src1:f src2:f len:64 +lcompare: src1:i src2:i len:4 +setfret: dest:f src1:f len:8 +checkthis: src1:b len:4 +oparglist: src1:i len:64 +call: dest:o clob:c len:40 +call_reg: dest:o src1:i len:64 clob:c +call_membase: dest:o src1:b len:64 clob:c +voidcall: len:64 clob:c +voidcall_reg: src1:i len:64 clob:c +voidcall_membase: src1:b len:64 clob:c +fcall: dest:f len:64 clob:c +fcall_reg: dest:f src1:i len:64 clob:c +fcall_membase: dest:f src1:b len:64 clob:c +lcall: dest:l len:42 clob:c +lcall_reg: dest:l src1:i len:64 clob:c +lcall_membase: dest:l src1:b len:64 clob:c +vcall: len:40 clob:c +vcall_reg: src1:i len:64 clob:c +vcall_membase: src1:b len:64 clob:c +iconst: dest:i len:64 +i8const: dest:i len:64 +r4const: dest:f len:64 +r8const: dest:f len:64 +store_membase_imm: dest:b len:64 +store_membase_reg: dest:b src1:i len:64 +storei1_membase_imm: dest:b len:64 +storei1_membase_reg: dest:b src1:i len:64 +storei2_membase_imm: dest:b len:64 +storei2_membase_reg: dest:b src1:i len:64 +storei4_membase_imm: dest:b len:64 +storei4_membase_reg: dest:b src1:i len:64 +storei8_membase_imm: dest:b len:64 len:64 +storei8_membase_reg: dest:b src1:i len:64 +storer4_membase_reg: dest:b src1:f len:64 +storer8_membase_reg: dest:b src1:f len:64 +load_membase: dest:i src1:b len:64 +loadi1_membase: dest:i src1:b len:64 +loadu1_membase: dest:i src1:b len:64 +loadi2_membase: dest:i src1:b len:64 +loadu2_membase: dest:i src1:b len:64 +loadi4_membase: dest:i src1:b len:64 +loadu4_membase: dest:i src1:b len:64 +loadi8_membase: dest:i src1:b len:64 +loadr4_membase: dest:f src1:b len:64 +loadr8_membase: dest:f src1:b len:64 +loadu4_mem: dest:i len:8 +move: dest:i src1:i len:4 +add_imm: dest:i src1:i len:64 +addcc_imm: dest:i src1:i len:64 +sub_imm: dest:i src1:i len:64 +subcc_imm: dest:i src1:i len:64 +mul_imm: dest:i src1:i len:64 +div_imm: dest:a src1:i src2:i len:64 +div_un_imm: dest:a src1:i src2:i len:64 +rem_imm: dest:d src1:i src2:i len:64 +rem_un_imm: dest:d src1:i src2:i len:64 +and_imm: dest:i src1:i len:64 +or_imm: dest:i src1:i len:64 +xor_imm: dest:i src1:i len:64 +shl_imm: dest:i src1:i len:64 +shr_imm: dest:i src1:i len:64 +shr_un_imm: dest:i src1:i len:64 +cond_exc_eq: len:64 +cond_exc_ne_un: len:64 +cond_exc_lt: len:64 +cond_exc_lt_un: len:64 +cond_exc_gt: len:64 +cond_exc_gt_un: len:64 +cond_exc_ge: len:64 +cond_exc_ge_un: len:64 +cond_exc_le: len:64 +cond_exc_le_un: len:64 +cond_exc_ov: len:64 +cond_exc_no: len:64 +cond_exc_c: len:64 +cond_exc_nc: len:64 +float_beq: len:8 +float_bne_un: len:64 +float_blt: len:8 +float_blt_un: len:64 +float_bgt: len:8 +float_bgt_un: len:64 +float_bge: len:64 +float_bge_un: len:64 +float_ble: len:64 +float_ble_un: len:64 +float_add: dest:f src1:f src2:f len:4 +float_sub: dest:f src1:f src2:f len:4 +float_mul: dest:f src1:f src2:f len:4 +float_div: dest:f src1:f src2:f len:4 +float_div_un: dest:f src1:f src2:f len:4 +float_rem: dest:f src1:f src2:f len:64 +float_rem_un: dest:f src1:f src2:f len:64 +float_neg: dest:f src1:f len:4 +float_not: dest:f src1:f len:4 +float_conv_to_i1: dest:i src1:f len:40 +float_conv_to_i2: dest:i src1:f len:40 +float_conv_to_i4: dest:i src1:f len:40 +float_conv_to_i8: dest:L src1:f len:40 +float_conv_to_r4: dest:f src1:f len:8 +float_conv_to_u4: dest:i src1:f len:40 +float_conv_to_u8: dest:L src1:f len:40 +float_conv_to_u2: dest:i src1:f len:40 +float_conv_to_u1: dest:i src1:f len:40 +float_conv_to_i: dest:i src1:f len:40 +float_ceq: dest:i src1:f src2:f len:64 +float_cgt: dest:i src1:f src2:f len:64 +float_cgt_un: dest:i src1:f src2:f len:64 +float_clt: dest:i src1:f src2:f len:64 +float_clt_un: dest:i src1:f src2:f len:64 +float_conv_to_u: dest:i src1:f len:64 +call_handler: len:64 clob:c +aot_const: dest:i len:64 +adc: dest:i src1:i src2:i len:4 +addcc: dest:i src1:i src2:i len:4 +subcc: dest:i src1:i src2:i len:4 +adc_imm: dest:i src1:i len:64 +sbb: dest:i src1:i src2:i len:4 +sbb_imm: dest:i src1:i len:64 +br_reg: src1:i len:8 +bigmul: len:2 dest:L src1:a src2:i +bigmul_un: len:2 dest:L src1:a src2:i +fmove: dest:f src1:f len:8 + +# 32 bit opcodes +int_add: dest:i src1:i src2:i len:64 +int_sub: dest:i src1:i src2:i len:64 +int_mul: dest:i src1:i src2:i len:64 +int_div: dest:i src1:i src2:i len:64 +int_div_un: dest:i src1:i src2:i len:64 +int_rem: dest:i src1:i src2:i len:64 +int_rem_un: dest:i src1:i src2:i len:64 +int_and: dest:i src1:i src2:i len:64 +int_or: dest:i src1:i src2:i len:64 +int_xor: dest:i src1:i src2:i len:64 +int_shl: dest:i src1:i src2:i len:64 +int_shr: dest:i src1:i src2:i len:64 +int_shr_un: dest:i src1:i src2:i len:64 +int_adc: dest:i src1:i src2:i len:64 +int_adc_imm: dest:i src1:i len:64 +int_sbb: dest:i src1:i src2:i len:64 +int_sbb_imm: dest:i src1:i len:64 +int_addcc: dest:i src1:i src2:i len:64 +int_subcc: dest:i src1:i src2:i len:64 +int_add_imm: dest:i src1:i len:64 +int_sub_imm: dest:i src1:i len:64 +int_mul_imm: dest:i src1:i len:64 +int_div_imm: dest:i src1:i len:64 +int_div_un_imm: dest:i src1:i len:64 +int_rem_imm: dest:i src1:i len:64 +int_rem_un_imm: dest:i src1:i len:64 +int_and_imm: dest:i src1:i len:64 +int_or_imm: dest:i src1:i len:64 +int_xor_imm: dest:i src1:i len:64 +int_shl_imm: dest:i src1:i len:64 +int_shr_imm: dest:i src1:i len:64 +int_shr_un_imm: dest:i src1:i len:64 +int_mul_ovf: dest:i src1:i src2:i len:64 +int_mul_ovf_un: dest:i src1:i src2:i len:64 +int_conv_to_i1: dest:i src1:i len:8 +int_conv_to_i2: dest:i src1:i len:8 +int_conv_to_i4: dest:i src1:i len:4 +int_conv_to_i8: dest:i src1:i len:4 +int_conv_to_r4: dest:f src1:i len:64 +int_conv_to_r8: dest:f src1:i len:64 +int_conv_to_u4: dest:i src1:i len:4 +int_conv_to_u8: dest:i src1:i len:4 +int_conv_to_u2: dest:i src1:i len:8 +int_conv_to_u1: dest:i src1:i len:4 +int_conv_to_i: dest:i src1:i len:4 +int_neg: dest:i src1:i len:64 +int_not: dest:i src1:i len:64 +int_ceq: dest:i len:64 +int_cgt: dest:i len:64 +int_cgt_un: dest:i len:64 +int_clt: dest:i len:64 +int_clt_un: dest:i len:64 +int_beq: len:8 +int_bge: len:8 +int_bgt: len:8 +int_ble: len:8 +int_blt: len:8 +int_bne_un: len:64 +int_bge_un: len:64 +int_bgt_un: len:64 +int_ble_un: len:64 +int_blt_un: len:64 + +# 64 bit opcodes +long_shl: dest:i src1:i src2:i len:64 +long_shr: dest:i src1:i src2:i len:64 +long_shr_un: dest:i src1:i src2:i len:64 +long_conv_to_ovf_i: dest:i src1:i src2:i len:48 +long_mul_ovf: +long_conv_to_r_un: dest:f src1:i src2:i len:64 +long_shr_imm: dest:i src1:i len:64 +long_shr_un_imm: dest:i src1:i len:64 +long_shl_imm: dest:i src1:i len:64 + +memory_barrier: len:4 + +sparc_brz: src1:i len: 8 +sparc_brlez: src1:i len: 8 +sparc_brlz: src1:i len: 8 +sparc_brnz: src1:i len: 8 +sparc_brgz: src1:i len: 8 +sparc_brgez: src1:i len: 8 +sparc_cond_exc_eqz: src1:i len:64 +sparc_cond_exc_nez: src1:i len:64 +sparc_cond_exc_ltz: src1:i len:64 +sparc_cond_exc_gtz: src1:i len:64 +sparc_cond_exc_gez: src1:i len:64 +sparc_cond_exc_lez: src1:i len:64 + +relaxed_nop: len:0 + +# Linear IR opcodes +nop: len:0 +dummy_use: src1:i len:0 +dummy_store: len:0 +not_reached: len:0 +not_null: src1:i len:0 + +jump_table: dest:i len:64 + +cond_exc_ieq: len:64 +cond_exc_ine_un: len:64 +cond_exc_ilt: len:64 +cond_exc_ilt_un: len:64 +cond_exc_igt: len:64 +cond_exc_igt_un: len:64 +cond_exc_ige: len:64 +cond_exc_ige_un: len:64 +cond_exc_ile: len:64 +cond_exc_ile_un: len:64 +cond_exc_iov: len:64 +cond_exc_ino: len:64 +cond_exc_ic: len:64 +cond_exc_inc: len:64 + +long_conv_to_ovf_i4_2: dest:i src1:i src2:i len:48 + +vcall2: len:40 clob:c +vcall2_reg: src1:i len:64 clob:c +vcall2_membase: src1:b len:64 clob:c + +gc_safe_point: len:0 diff --git a/unity-2019.4.24f1-mbe/mono/mini/cpu-wasm.md b/unity-2019.4.24f1-mbe/mono/mini/cpu-wasm.md new file mode 100644 index 000000000..e69de29bb diff --git a/unity-2019.4.24f1-mbe/mono/mini/cpu-x86.md b/unity-2019.4.24f1-mbe/mono/mini/cpu-x86.md new file mode 100644 index 000000000..e0c9625f7 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/cpu-x86.md @@ -0,0 +1,655 @@ +# x86-class cpu description file +# this file is read by genmdesc to pruduce a table with all the relevant information +# about the cpu instructions that may be used by the regsiter allocator, the scheduler +# and other parts of the arch-dependent part of mini. +# +# An opcode name is followed by a colon and optional specifiers. +# A specifier has a name, a colon and a value. Specifiers are separated by white space. +# Here is a description of the specifiers valid for this file and their possible values. +# +# dest:register describes the destination register of an instruction +# src1:register describes the first source register of an instruction +# src2:register describes the second source register of an instruction +# +# register may have the following values: +# i integer register +# b base register (used in address references) +# f floating point register +# a EAX register +# d EDX register +# s ECX register +# l long reg (forced eax:edx) +# L long reg (dynamic) +# y the reg needs to be one of EAX,EBX,ECX,EDX (sete opcodes) +# x XMM reg (XMM0 - X007) +# +# len:number describe the maximun length in bytes of the instruction +# number is a positive integer. If the length is not specified +# it defaults to zero. But lengths are only checked if the given opcode +# is encountered during compilation. Some opcodes, like CONV_U4 are +# transformed into other opcodes in the brg files, so they do not show up +# during code generation. +# +# cost:number describe how many cycles are needed to complete the instruction (unused) +# +# clob:spec describe if the instruction clobbers registers or has special needs +# +# spec can be one of the following characters: +# c clobbers caller-save registers +# 1 clobbers the first source register +# a EAX is clobbered +# d EDX is clobbered +# x both the source operands are clobbered (xchg) +# +# flags:spec describe if the instruction uses or sets the flags (unused) +# +# spec can be one of the following chars: +# s sets the flags +# u uses the flags +# m uses and modifies the flags +# +# res:spec describe what units are used in the processor (unused) +# +# delay: describe delay slots (unused) +# +# the required specifiers are: len, clob (if registers are clobbered), the registers +# specifiers if the registers are actually used, flags (when scheduling is implemented). +# +# Templates can be defined by using the 'template' keyword instead of an opcode name. +# The template name is assigned from a (required) 'name' specifier. +# To apply a template to an opcode, just use the template:template_name specifier: any value +# defined by the template can be overridden by adding more specifiers after the template. +# +# See the code in mini-x86.c for more details on how the specifiers are used. +# +break: len:1 +call: dest:a clob:c len:17 +tailcall: len:120 clob:c +br: len:5 +seq_point: len:26 clob:c +il_seq_point: len:0 + +int_beq: len:6 +int_bge: len:6 +int_bgt: len:6 +int_ble: len:6 +int_blt: len:6 +int_bne_un: len:6 +int_bge_un: len:6 +int_bgt_un: len:6 +int_ble_un: len:6 +int_blt_un: len:6 +label: len:0 + +#template: name:ibalu + +int_add: dest:i src1:i src2:i clob:1 len:2 +int_sub: dest:i src1:i src2:i clob:1 len:2 +int_mul: dest:i src1:i src2:i clob:1 len:3 +int_div: dest:a src1:a src2:i len:15 clob:d +int_div_un: dest:a src1:a src2:i len:15 clob:d +int_rem: dest:d src1:a src2:i len:15 clob:a +int_rem_un: dest:d src1:a src2:i len:15 clob:a +int_and: dest:i src1:i src2:i clob:1 len:2 +int_or: dest:i src1:i src2:i clob:1 len:2 +int_xor: dest:i src1:i src2:i clob:1 len:2 +int_shl: dest:i src1:i src2:s clob:1 len:2 +int_shr: dest:i src1:i src2:s clob:1 len:2 +int_shr_un: dest:i src1:i src2:s clob:1 len:2 +int_min: dest:i src1:i src2:i len:16 clob:1 +int_min_un: dest:i src1:i src2:i len:16 clob:1 +int_max: dest:i src1:i src2:i len:16 clob:1 +int_max_un: dest:i src1:i src2:i len:16 clob:1 + +int_neg: dest:i src1:i len:2 clob:1 +int_not: dest:i src1:i len:2 clob:1 +int_conv_to_i1: dest:i src1:y len:3 +int_conv_to_i2: dest:i src1:i len:3 +int_conv_to_i4: dest:i src1:i len:2 +int_conv_to_r4: dest:f src1:i len:13 +int_conv_to_r8: dest:f src1:i len:7 +int_conv_to_u4: dest:i src1:i +int_conv_to_u2: dest:i src1:i len:3 +int_conv_to_u1: dest:i src1:y len:3 +int_conv_to_i: dest:i src1:i len:3 +int_mul_ovf: dest:i src1:i src2:i clob:1 len:9 +int_mul_ovf_un: dest:i src1:i src2:i len:16 + +throw: src1:i len:13 +rethrow: src1:i len:13 +start_handler: len:16 +endfinally: len:16 +endfilter: src1:a len:16 +get_ex_obj: dest:a len:16 + +ckfinite: dest:f src1:f len:32 +ceq: dest:y len:6 +cgt: dest:y len:6 +cgt.un: dest:y len:6 +clt: dest:y len:6 +clt.un: dest:y len:6 +localloc: dest:i src1:i len:120 +compare: src1:i src2:i len:2 +compare_imm: src1:i len:6 +fcompare: src1:f src2:f clob:a len:9 +oparglist: src1:b len:10 +checkthis: src1:b len:3 +voidcall: len:17 clob:c +voidcall_reg: src1:i len:11 clob:c +voidcall_membase: src1:b len:16 clob:c +fcall: dest:f len:17 clob:c +fcall_reg: dest:f src1:i len:11 clob:c +fcall_membase: dest:f src1:b len:16 clob:c +lcall: dest:l len:17 clob:c +lcall_reg: dest:l src1:i len:11 clob:c +lcall_membase: dest:l src1:b len:16 clob:c +vcall: len:17 clob:c +vcall_reg: src1:i len:11 clob:c +vcall_membase: src1:b len:16 clob:c +call_reg: dest:a src1:i len:11 clob:c +call_membase: dest:a src1:b len:16 clob:c +iconst: dest:i len:5 +r4const: dest:f len:15 +r8const: dest:f len:16 +store_membase_imm: dest:b len:11 +store_membase_reg: dest:b src1:i len:7 +storei1_membase_imm: dest:b len:10 +storei1_membase_reg: dest:b src1:y len:7 +storei2_membase_imm: dest:b len:11 +storei2_membase_reg: dest:b src1:i len:7 +storei4_membase_imm: dest:b len:10 +storei4_membase_reg: dest:b src1:i len:7 +storei8_membase_imm: dest:b +storei8_membase_reg: dest:b src1:i +storer4_membase_reg: dest:b src1:f len:7 +storer8_membase_reg: dest:b src1:f len:7 +store_mem_imm: len:12 +load_membase: dest:i src1:b len:7 +loadi1_membase: dest:y src1:b len:7 +loadu1_membase: dest:y src1:b len:7 +loadi2_membase: dest:i src1:b len:7 +loadu2_membase: dest:i src1:b len:7 +loadi4_membase: dest:i src1:b len:7 +loadu4_membase: dest:i src1:b len:7 +loadi8_membase: dest:i src1:b +loadr4_membase: dest:f src1:b len:7 +loadr8_membase: dest:f src1:b len:7 +loadu4_mem: dest:i len:9 +move: dest:i src1:i len:2 +addcc_imm: dest:i src1:i len:6 clob:1 +add_imm: dest:i src1:i len:6 clob:1 +subcc_imm: dest:i src1:i len:6 clob:1 +sub_imm: dest:i src1:i len:6 clob:1 +mul_imm: dest:i src1:i len:9 +and_imm: dest:i src1:i len:6 clob:1 +or_imm: dest:i src1:i len:6 clob:1 +xor_imm: dest:i src1:i len:6 clob:1 +shl_imm: dest:i src1:i len:6 clob:1 +shr_imm: dest:i src1:i len:6 clob:1 +shr_un_imm: dest:i src1:i len:6 clob:1 +cond_exc_eq: len:6 +cond_exc_ne_un: len:6 +cond_exc_lt: len:6 +cond_exc_lt_un: len:6 +cond_exc_gt: len:6 +cond_exc_gt_un: len:6 +cond_exc_ge: len:6 +cond_exc_ge_un: len:6 +cond_exc_le: len:6 +cond_exc_le_un: len:6 +cond_exc_ov: len:6 +cond_exc_no: len:6 +cond_exc_c: len:6 +cond_exc_nc: len:6 +long_shl: dest:L src1:L src2:s clob:1 len:21 +long_shr: dest:L src1:L src2:s clob:1 len:22 +long_shr_un: dest:L src1:L src2:s clob:1 len:22 +long_shr_imm: dest:L src1:L clob:1 len:10 +long_shr_un_imm: dest:L src1:L clob:1 len:10 +long_shl_imm: dest:L src1:L clob:1 len:10 +float_beq: len:12 +float_bne_un: len:18 +float_blt: len:12 +float_blt_un: len:20 +float_bgt: len:12 +float_bgt_un: len:20 +float_bge: len:22 +float_bge_un: len:12 +float_ble: len:22 +float_ble_un: len:12 +float_add: dest:f src1:f src2:f len:2 +float_sub: dest:f src1:f src2:f len:2 +float_mul: dest:f src1:f src2:f len:2 +float_div: dest:f src1:f src2:f len:2 +float_div_un: dest:f src1:f src2:f len:2 +float_rem: dest:f src1:f src2:f len:17 +float_rem_un: dest:f src1:f src2:f len:17 +float_neg: dest:f src1:f len:2 +float_not: dest:f src1:f len:2 +float_conv_to_i1: dest:y src1:f len:39 +float_conv_to_i2: dest:y src1:f len:39 +float_conv_to_i4: dest:i src1:f len:39 +float_conv_to_i8: dest:L src1:f len:39 +float_conv_to_u4: dest:i src1:f len:39 +float_conv_to_u8: dest:L src1:f len:39 +float_conv_to_u2: dest:y src1:f len:39 +float_conv_to_u1: dest:y src1:f len:39 +float_conv_to_i: dest:i src1:f len:39 +float_conv_to_ovf_i: dest:a src1:f len:30 +float_conv_to_ovd_u: dest:a src1:f len:30 +float_mul_ovf: +float_ceq: dest:y src1:f src2:f len:25 +float_cgt: dest:y src1:f src2:f len:25 +float_cgt_un: dest:y src1:f src2:f len:37 +float_clt: dest:y src1:f src2:f len:25 +float_clt_un: dest:y src1:f src2:f len:32 +float_cneq: dest:y src1:f src2:f len:25 +float_cge: dest:y src1:f src2:f len:37 +float_cle: dest:y src1:f src2:f len:37 +float_conv_to_u: dest:i src1:f len:36 +call_handler: len:11 clob:c +aot_const: dest:i len:5 +load_gotaddr: dest:i len:64 +got_entry: dest:i src1:b len:7 +gc_safe_point: clob:c src1:i len:20 +x86_test_null: src1:i len:2 +x86_compare_membase_reg: src1:b src2:i len:7 +x86_compare_membase_imm: src1:b len:11 +x86_compare_membase8_imm: src1:b len:8 +x86_compare_mem_imm: len:11 +x86_compare_reg_membase: src1:i src2:b len:7 +x86_inc_reg: dest:i src1:i clob:1 len:1 +x86_inc_membase: src1:b len:7 +x86_dec_reg: dest:i src1:i clob:1 len:1 +x86_dec_membase: src1:b len:7 +x86_add_membase_imm: src1:b len:11 +x86_sub_membase_imm: src1:b len:11 +x86_and_membase_imm: src1:b len:11 +x86_or_membase_imm: src1:b len:11 +x86_xor_membase_imm: src1:b len:11 +x86_push: src1:i len:1 +x86_push_imm: len:5 +x86_push_membase: src1:b len:7 +x86_push_obj: src1:b len:30 +x86_push_got_entry: src1:b len:7 +x86_lea: dest:i src1:i src2:i len:7 +x86_lea_membase: dest:i src1:i len:10 +x86_xchg: src1:i src2:i clob:x len:1 +x86_fpop: src1:f len:2 +x86_fp_load_i8: dest:f src1:b len:7 +x86_fp_load_i4: dest:f src1:b len:7 +x86_seteq_membase: src1:b len:7 +x86_setne_membase: src1:b len:7 + +x86_add_reg_membase: dest:i src1:i src2:b clob:1 len:11 +x86_sub_reg_membase: dest:i src1:i src2:b clob:1 len:11 +x86_mul_reg_membase: dest:i src1:i src2:b clob:1 len:13 + +adc: dest:i src1:i src2:i len:2 clob:1 +addcc: dest:i src1:i src2:i len:2 clob:1 +subcc: dest:i src1:i src2:i len:2 clob:1 +adc_imm: dest:i src1:i len:6 clob:1 +sbb: dest:i src1:i src2:i len:2 clob:1 +sbb_imm: dest:i src1:i len:6 clob:1 +br_reg: src1:i len:2 +sin: dest:f src1:f len:6 +cos: dest:f src1:f len:6 +abs: dest:f src1:f len:2 +tan: dest:f src1:f len:49 +atan: dest:f src1:f len:8 +sqrt: dest:f src1:f len:2 +round: dest:f src1:f len:2 +bigmul: len:2 dest:l src1:a src2:i +bigmul_un: len:2 dest:l src1:a src2:i +sext_i1: dest:i src1:y len:3 +sext_i2: dest:i src1:y len:3 +tls_get: dest:i len:32 +tls_set: src1:i len:20 +atomic_add_i4: src1:b src2:i dest:i len:16 +atomic_exchange_i4: src1:b src2:i dest:a len:24 +atomic_cas_i4: src1:b src2:i src3:a dest:a len:24 +memory_barrier: len:16 +atomic_load_i1: dest:y src1:b len:7 +atomic_load_u1: dest:y src1:b len:7 +atomic_load_i2: dest:i src1:b len:7 +atomic_load_u2: dest:i src1:b len:7 +atomic_load_i4: dest:i src1:b len:7 +atomic_load_u4: dest:i src1:b len:7 +atomic_load_r4: dest:f src1:b len:10 +atomic_load_r8: dest:f src1:b len:10 +atomic_store_i1: dest:b src1:y len:10 +atomic_store_u1: dest:b src1:y len:10 +atomic_store_i2: dest:b src1:i len:10 +atomic_store_u2: dest:b src1:i len:10 +atomic_store_i4: dest:b src1:i len:10 +atomic_store_u4: dest:b src1:i len:10 +atomic_store_r4: dest:b src1:f len:10 +atomic_store_r8: dest:b src1:f len:10 + +card_table_wbarrier: src1:a src2:i clob:d len:34 + +relaxed_nop: len:2 +hard_nop: len:1 + +# Linear IR opcodes +nop: len:0 +dummy_use: src1:i len:0 +dummy_store: len:0 +dummy_iconst: dest:i len:0 +dummy_r8const: dest:f len:0 +not_reached: len:0 +not_null: src1:i len:0 + +jump_table: dest:i len:5 + +int_adc: dest:i src1:i src2:i len:2 clob:1 +int_addcc: dest:i src1:i src2:i len:2 clob:1 +int_subcc: dest:i src1:i src2:i len:2 clob:1 +int_sbb: dest:i src1:i src2:i len:2 clob:1 + +int_add_imm: dest:i src1:i len:6 clob:1 +int_sub_imm: dest:i src1:i len:6 clob:1 +int_mul_imm: dest:i src1:i len:9 +int_div_imm: dest:a src1:a len:15 clob:d +int_div_un_imm: dest:a src1:a len:15 clob:d +int_rem_imm: dest:a src1:a len:15 clob:d +int_rem_un_imm: dest:d src1:a len:15 clob:a +int_and_imm: dest:i src1:i len:6 clob:1 +int_or_imm: dest:i src1:i len:6 clob:1 +int_xor_imm: dest:i src1:i len:6 clob:1 +int_shl_imm: dest:i src1:i len:6 clob:1 +int_shr_imm: dest:i src1:i len:6 clob:1 +int_shr_un_imm: dest:i src1:i len:6 clob:1 + +int_conv_to_r_un: dest:f src1:i len:32 + +int_ceq: dest:y len:6 +int_cgt: dest:y len:6 +int_cgt_un: dest:y len:6 +int_clt: dest:y len:6 +int_clt_un: dest:y len:6 + +int_cneq: dest:y len:6 +int_cge: dest:y len:6 +int_cle: dest:y len:6 +int_cge_un: dest:y len:6 +int_cle_un: dest:y len:6 + +cond_exc_ieq: len:6 +cond_exc_ine_un: len:6 +cond_exc_ilt: len:6 +cond_exc_ilt_un: len:6 +cond_exc_igt: len:6 +cond_exc_igt_un: len:6 +cond_exc_ige: len:6 +cond_exc_ige_un: len:6 +cond_exc_ile: len:6 +cond_exc_ile_un: len:6 +cond_exc_iov: len:6 +cond_exc_ino: len:6 +cond_exc_ic: len:6 +cond_exc_inc: len:6 + +icompare: src1:i src2:i len:2 +icompare_imm: src1:i len:6 + +cmov_ieq: dest:i src1:i src2:i len:16 clob:1 +cmov_ige: dest:i src1:i src2:i len:16 clob:1 +cmov_igt: dest:i src1:i src2:i len:16 clob:1 +cmov_ile: dest:i src1:i src2:i len:16 clob:1 +cmov_ilt: dest:i src1:i src2:i len:16 clob:1 +cmov_ine_un: dest:i src1:i src2:i len:16 clob:1 +cmov_ige_un: dest:i src1:i src2:i len:16 clob:1 +cmov_igt_un: dest:i src1:i src2:i len:16 clob:1 +cmov_ile_un: dest:i src1:i src2:i len:16 clob:1 +cmov_ilt_un: dest:i src1:i src2:i len:16 clob:1 + +long_conv_to_ovf_i4_2: dest:i src1:i src2:i len:30 +long_conv_to_r8_2: dest:f src1:i src2:i len:14 +long_conv_to_r4_2: dest:f src1:i src2:i len:14 +long_conv_to_r_un_2: dest:f src1:i src2:i len:40 + +fmove: dest:f src1:f +move_f_to_i4: dest:i src1:f len:17 +move_i4_to_f: dest:f src1:i len:17 +float_conv_to_r4: dest:f src1:f len:12 + +load_mem: dest:i len:9 +loadi4_mem: dest:i len:9 +loadu1_mem: dest:i len:9 +loadu2_mem: dest:i len:9 + +vcall2: len:17 clob:c +vcall2_reg: src1:i len:11 clob:c +vcall2_membase: src1:b len:16 clob:c + +localloc_imm: dest:i len:120 + +x86_add_membase_reg: src1:b src2:i len:11 +x86_sub_membase_reg: src1:b src2:i len:11 +x86_and_membase_reg: src1:b src2:i len:11 +x86_or_membase_reg: src1:b src2:i len:11 +x86_xor_membase_reg: src1:b src2:i len:11 +x86_mul_membase_reg: src1:b src2:i len:13 + +x86_and_reg_membase: dest:i src1:i src2:b clob:1 len:6 +x86_or_reg_membase: dest:i src1:i src2:b clob:1 len:6 +x86_xor_reg_membase: dest:i src1:i src2:b clob:1 len:6 + +x86_fxch: len:2 + +addps: dest:x src1:x src2:x len:3 clob:1 +divps: dest:x src1:x src2:x len:3 clob:1 +mulps: dest:x src1:x src2:x len:3 clob:1 +subps: dest:x src1:x src2:x len:3 clob:1 +maxps: dest:x src1:x src2:x len:3 clob:1 +minps: dest:x src1:x src2:x len:3 clob:1 +compps: dest:x src1:x src2:x len:4 clob:1 +andps: dest:x src1:x src2:x len:3 clob:1 +andnps: dest:x src1:x src2:x len:3 clob:1 +orps: dest:x src1:x src2:x len:3 clob:1 +xorps: dest:x src1:x src2:x len:3 clob:1 + +haddps: dest:x src1:x src2:x len:4 clob:1 +hsubps: dest:x src1:x src2:x len:4 clob:1 +addsubps: dest:x src1:x src2:x len:4 clob:1 +dupps_low: dest:x src1:x len:4 +dupps_high: dest:x src1:x len:4 + +addpd: dest:x src1:x src2:x len:4 clob:1 +divpd: dest:x src1:x src2:x len:4 clob:1 +mulpd: dest:x src1:x src2:x len:4 clob:1 +subpd: dest:x src1:x src2:x len:4 clob:1 +maxpd: dest:x src1:x src2:x len:4 clob:1 +minpd: dest:x src1:x src2:x len:4 clob:1 +comppd: dest:x src1:x src2:x len:5 clob:1 +andpd: dest:x src1:x src2:x len:4 clob:1 +andnpd: dest:x src1:x src2:x len:4 clob:1 +orpd: dest:x src1:x src2:x len:4 clob:1 +xorpd: dest:x src1:x src2:x len:4 clob:1 +sqrtpd: dest:x src1:x len:4 clob:1 + +haddpd: dest:x src1:x src2:x len:5 clob:1 +hsubpd: dest:x src1:x src2:x len:5 clob:1 +addsubpd: dest:x src1:x src2:x len:5 clob:1 +duppd: dest:x src1:x len:5 + +pand: dest:x src1:x src2:x len:4 clob:1 +por: dest:x src1:x src2:x len:4 clob:1 +pxor: dest:x src1:x src2:x len:4 clob:1 + +sqrtps: dest:x src1:x len:4 +rsqrtps: dest:x src1:x len:4 +rcpps: dest:x src1:x len:4 + +pshufflew_high: dest:x src1:x len:5 +pshufflew_low: dest:x src1:x len:5 +pshuffled: dest:x src1:x len:5 +shufps: dest:x src1:x src2:x len:4 clob:1 +shufpd: dest:x src1:x src2:x len:5 clob:1 + +extract_mask: dest:i src1:x len:4 + +paddb: dest:x src1:x src2:x len:4 clob:1 +paddw: dest:x src1:x src2:x len:4 clob:1 +paddd: dest:x src1:x src2:x len:4 clob:1 +paddq: dest:x src1:x src2:x len:4 clob:1 + +psubb: dest:x src1:x src2:x len:4 clob:1 +psubw: dest:x src1:x src2:x len:4 clob:1 +psubd: dest:x src1:x src2:x len:4 clob:1 +psubq: dest:x src1:x src2:x len:4 clob:1 + +pmaxb_un: dest:x src1:x src2:x len:4 clob:1 +pmaxw_un: dest:x src1:x src2:x len:5 clob:1 +pmaxd_un: dest:x src1:x src2:x len:5 clob:1 + +pmaxb: dest:x src1:x src2:x len:5 clob:1 +pmaxw: dest:x src1:x src2:x len:4 clob:1 +pmaxd: dest:x src1:x src2:x len:5 clob:1 + +pavgb_un: dest:x src1:x src2:x len:4 clob:1 +pavgw_un: dest:x src1:x src2:x len:4 clob:1 + +pminb_un: dest:x src1:x src2:x len:4 clob:1 +pminw_un: dest:x src1:x src2:x len:5 clob:1 +pmind_un: dest:x src1:x src2:x len:5 clob:1 + +pminb: dest:x src1:x src2:x len:5 clob:1 +pminw: dest:x src1:x src2:x len:4 clob:1 +pmind: dest:x src1:x src2:x len:5 clob:1 + +pcmpeqb: dest:x src1:x src2:x len:4 clob:1 +pcmpeqw: dest:x src1:x src2:x len:4 clob:1 +pcmpeqd: dest:x src1:x src2:x len:4 clob:1 +pcmpeqq: dest:x src1:x src2:x len:5 clob:1 + +pcmpgtb: dest:x src1:x src2:x len:4 clob:1 +pcmpgtw: dest:x src1:x src2:x len:4 clob:1 +pcmpgtd: dest:x src1:x src2:x len:4 clob:1 +pcmpgtq: dest:x src1:x src2:x len:5 clob:1 + +psumabsdiff: dest:x src1:x src2:x len:4 clob:1 + +unpack_lowb: dest:x src1:x src2:x len:4 clob:1 +unpack_loww: dest:x src1:x src2:x len:4 clob:1 +unpack_lowd: dest:x src1:x src2:x len:4 clob:1 +unpack_lowq: dest:x src1:x src2:x len:4 clob:1 +unpack_lowps: dest:x src1:x src2:x len:3 clob:1 +unpack_lowpd: dest:x src1:x src2:x len:4 clob:1 + +unpack_highb: dest:x src1:x src2:x len:4 clob:1 +unpack_highw: dest:x src1:x src2:x len:4 clob:1 +unpack_highd: dest:x src1:x src2:x len:4 clob:1 +unpack_highq: dest:x src1:x src2:x len:4 clob:1 +unpack_highps: dest:x src1:x src2:x len:3 clob:1 +unpack_highpd: dest:x src1:x src2:x len:4 clob:1 + +packw: dest:x src1:x src2:x len:4 clob:1 +packd: dest:x src1:x src2:x len:4 clob:1 + +packw_un: dest:x src1:x src2:x len:4 clob:1 +packd_un: dest:x src1:x src2:x len:5 clob:1 + +paddb_sat: dest:x src1:x src2:x len:4 clob:1 +paddb_sat_un: dest:x src1:x src2:x len:4 clob:1 + +paddw_sat: dest:x src1:x src2:x len:4 clob:1 +paddw_sat_un: dest:x src1:x src2:x len:4 clob:1 + +psubb_sat: dest:x src1:x src2:x len:4 clob:1 +psubb_sat_un: dest:x src1:x src2:x len:4 clob:1 + +psubw_sat: dest:x src1:x src2:x len:4 clob:1 +psubw_sat_un: dest:x src1:x src2:x len:4 clob:1 + +pmulw: dest:x src1:x src2:x len:4 clob:1 +pmuld: dest:x src1:x src2:x len:5 clob:1 +pmulq: dest:x src1:x src2:x len:4 clob:1 + +pmul_high_un: dest:x src1:x src2:x len:4 clob:1 +pmul_high: dest:x src1:x src2:x len:4 clob:1 + +pshrw: dest:x src1:x len:5 clob:1 +pshrw_reg: dest:x src1:x src2:x len:4 clob:1 + +psarw: dest:x src1:x len:5 clob:1 +psarw_reg: dest:x src1:x src2:x len:4 clob:1 + +pshlw: dest:x src1:x len:5 clob:1 +pshlw_reg: dest:x src1:x src2:x len:4 clob:1 + +pshrd: dest:x src1:x len:5 clob:1 +pshrd_reg: dest:x src1:x src2:x len:4 clob:1 + +psard: dest:x src1:x len:5 clob:1 +psard_reg: dest:x src1:x src2:x len:4 clob:1 + +pshld: dest:x src1:x len:5 clob:1 +pshld_reg: dest:x src1:x src2:x len:4 clob:1 + +pshrq: dest:x src1:x len:5 clob:1 +pshrq_reg: dest:x src1:x src2:x len:4 clob:1 + +pshlq: dest:x src1:x len:5 clob:1 +pshlq_reg: dest:x src1:x src2:x len:4 clob:1 + +cvtdq2pd: dest:x src1:x len:4 clob:1 +cvtdq2ps: dest:x src1:x len:3 clob:1 +cvtpd2dq: dest:x src1:x len:4 clob:1 +cvtpd2ps: dest:x src1:x len:4 clob:1 +cvtps2dq: dest:x src1:x len:4 clob:1 +cvtps2pd: dest:x src1:x len:3 clob:1 +cvttpd2dq: dest:x src1:x len:4 clob:1 +cvttps2dq: dest:x src1:x len:4 clob:1 + +xmove: dest:x src1:x len:4 +xzero: dest:x len:4 +xones: dest:x len:4 + +iconv_to_x: dest:x src1:i len:4 +extract_i4: dest:i src1:x len:4 + +extract_i2: dest:i src1:x len:10 +extract_u2: dest:i src1:x len:10 +extract_i1: dest:i src1:x len:10 +extract_u1: dest:i src1:x len:10 +extract_r8: dest:f src1:x len:8 + +insert_i2: dest:x src1:x src2:i len:5 clob:1 + +extractx_u2: dest:i src1:x len:5 +insertx_u1_slow: dest:x src1:i src2:i len:16 clob:x + +insertx_i4_slow: dest:x src1:x src2:i len:13 clob:x +insertx_r4_slow: dest:x src1:x src2:f len:24 clob:1 +insertx_r8_slow: dest:x src1:x src2:f len:24 clob:1 + +loadx_membase: dest:x src1:b len:7 +storex_membase: dest:b src1:x len:7 +storex_membase_reg: dest:b src1:x len:7 + +loadx_aligned_membase: dest:x src1:b len:7 +storex_aligned_membase_reg: dest:b src1:x len:7 +storex_nta_membase_reg: dest:b src1:x len:7 + +fconv_to_r8_x: dest:x src1:f len:14 +xconv_r8_to_i4: dest:y src1:x len:7 + +prefetch_membase: src1:b len:4 + +expand_i2: dest:x src1:i len:15 +expand_i4: dest:x src1:i len:9 +expand_r4: dest:x src1:f len:20 +expand_r8: dest:x src1:f len:20 + +liverange_start: len:0 +liverange_end: len:0 +gc_liveness_def: len:0 +gc_liveness_use: len:0 +gc_spill_slot_liveness_def: len:0 +gc_param_slot_liveness_def: len:0 +get_sp: dest:i len:6 +set_sp: src1:i len:6 + +fill_prof_call_ctx: src1:i len:128 diff --git a/unity-2019.4.24f1-mbe/mono/mini/debug-mini.c b/unity-2019.4.24f1-mbe/mono/mini/debug-mini.c new file mode 100644 index 000000000..30874eb3c --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/debug-mini.c @@ -0,0 +1,756 @@ +/** + * \file + * Mini-specific debugging stuff. + * + * Author: + * Martin Baulig (martin@ximian.com) + * + * (C) 2003 Ximian, Inc. + */ + +#include "mini.h" +#include "jit.h" +#include "config.h" +#include +#include +#include +#include +#include + +#include + +#include + +typedef struct { + guint32 index; + MonoMethodDesc *desc; +} MiniDebugBreakpointInfo; + +typedef struct +{ + MonoDebugMethodJitInfo *jit; + GArray *line_numbers; + guint32 has_line_numbers; + guint32 breakpoint_id; +} MiniDebugMethodInfo; + +static inline void +record_line_number (MiniDebugMethodInfo *info, guint32 address, guint32 offset) +{ + MonoDebugLineNumberEntry lne; + + lne.native_offset = address; + lne.il_offset = offset; + + g_array_append_val (info->line_numbers, lne); +} + + +void +mono_debug_init_method (MonoCompile *cfg, MonoBasicBlock *start_block, guint32 breakpoint_id) +{ + MiniDebugMethodInfo *info; + + if (!mono_debug_enabled ()) + return; + + info = g_new0 (MiniDebugMethodInfo, 1); + info->breakpoint_id = breakpoint_id; + + cfg->debug_info = info; +} + +void +mono_debug_open_method (MonoCompile *cfg) +{ + MiniDebugMethodInfo *info; + MonoDebugMethodJitInfo *jit; + MonoMethodHeader *header; + + info = (MiniDebugMethodInfo *) cfg->debug_info; + if (!info) + return; + + mono_class_init (cfg->method->klass); + + header = cfg->header; + g_assert (header); + + info->jit = jit = g_new0 (MonoDebugMethodJitInfo, 1); + info->line_numbers = g_array_new (FALSE, TRUE, sizeof (MonoDebugLineNumberEntry)); + jit->num_locals = header->num_locals; + jit->locals = g_new0 (MonoDebugVarInfo, jit->num_locals); +} + +static void +write_variable (MonoInst *inst, MonoDebugVarInfo *var) +{ + var->type = inst->inst_vtype; + + if (inst->opcode == OP_REGVAR) + var->index = inst->dreg | MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER; + else if (inst->flags & MONO_INST_IS_DEAD) + var->index = MONO_DEBUG_VAR_ADDRESS_MODE_DEAD; + else if (inst->opcode == OP_REGOFFSET) { + /* the debug interface needs fixing to allow 0(%base) address */ + var->index = inst->inst_basereg | MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET; + var->offset = inst->inst_offset; + } else if (inst->opcode == OP_GSHAREDVT_ARG_REGOFFSET) { + var->index = inst->inst_basereg | MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET_INDIR; + var->offset = inst->inst_offset; + } else if (inst->opcode == OP_GSHAREDVT_LOCAL) { + var->index = inst->inst_imm | MONO_DEBUG_VAR_ADDRESS_MODE_GSHAREDVT_LOCAL; + } else if (inst->opcode == OP_VTARG_ADDR) { + MonoInst *vtaddr; + + vtaddr = inst->inst_left; + g_assert (vtaddr->opcode == OP_REGOFFSET); + var->offset = vtaddr->inst_offset; + var->index = vtaddr->inst_basereg | MONO_DEBUG_VAR_ADDRESS_MODE_VTADDR; + } else { + g_assert_not_reached (); + } +} + +/* + * mono_debug_add_vg_method: + * + * Register symbol information for the method with valgrind + */ +static void +mono_debug_add_vg_method (MonoMethod *method, MonoDebugMethodJitInfo *jit) +{ +#ifdef VALGRIND_ADD_LINE_INFO + MonoError error; + MonoMethodHeader *header; + MonoDebugMethodInfo *minfo; + int i; + char *filename = NULL; + guint32 address, line_number; + const char *full_name; + guint32 *addresses; + guint32 *lines; + + if (!RUNNING_ON_VALGRIND) + return; + + header = mono_method_get_header_checked (method, &error); + mono_error_assert_ok (&error); /* FIXME don't swallow the error */ + + full_name = mono_method_full_name (method, TRUE); + + addresses = g_new0 (guint32, header->code_size + 1); + lines = g_new0 (guint32, header->code_size + 1); + + /* + * Very simple code to convert the addr->offset mappings that mono has + * into [addr-addr] ->line number mappings. + */ + + minfo = mono_debug_lookup_method (method); + if (minfo) { + /* Create offset->line number mapping */ + for (i = 0; i < header->code_size; ++i) { + MonoDebugSourceLocation *location; + + location = mono_debug_method_lookup_location (minfo, i); + if (!location) + continue; + + lines [i] = location.row; + if (!filename) + filename = location.source_file; + + mono_debug_free_source_location (location); + } + } + + /* Create address->offset mapping */ + for (i = 0; i < jit->num_line_numbers; ++i) { + MonoDebugLineNumberEntry *lne = jit->line_numbers [i]; + + g_assert (lne->offset <= header->code_size); + + if ((addresses [lne->offset] == 0) || (lne->address < addresses [lne->offset])) + addresses [lne->offset] = lne->address; + } + /* Fill out missing addresses */ + address = 0; + for (i = 0; i < header->code_size; ++i) { + if (addresses [i] == 0) + addresses [i] = address; + else + address = addresses [i]; + } + + address = 0; + line_number = 0; + i = 0; + while (i < header->code_size) { + if (lines [i] == line_number) + i ++; + else { + if (line_number > 0) { + //g_assert (addresses [i] - 1 >= address); + + if (addresses [i] - 1 >= address) { + VALGRIND_ADD_LINE_INFO (jit->code_start + address, jit->code_start + addresses [i] - 1, filename, line_number); + //printf ("[%d-%d] -> %d.\n", address, addresses [i] - 1, line_number); + } + } + address = addresses [i]; + line_number = lines [i]; + } + } + + if (line_number > 0) { + VALGRIND_ADD_LINE_INFO (jit->code_start + address, jit->code_start + jit->code_size - 1, filename, line_number); + //printf ("[%d-%d] -> %d.\n", address, jit->code_size - 1, line_number); + } + + VALGRIND_ADD_SYMBOL (jit->code_start, jit->code_size, full_name); + + g_free (addresses); + g_free (lines); + mono_metadata_free_mh (header); +#endif /* VALGRIND_ADD_LINE_INFO */ +} + +void +mono_debug_close_method (MonoCompile *cfg) +{ + MiniDebugMethodInfo *info; + MonoDebugMethodJitInfo *jit; + MonoMethodHeader *header; + MonoMethodSignature *sig; + MonoMethod *method; + int i; + + info = (MiniDebugMethodInfo *) cfg->debug_info; + if (!info || !info->jit) { + if (info) + g_free (info); + return; + } + + method = cfg->method; + header = cfg->header; + sig = mono_method_signature (method); + + jit = info->jit; + jit->code_start = cfg->native_code; + jit->epilogue_begin = cfg->epilog_begin; + jit->code_size = cfg->code_len; + jit->has_var_info = debug_options.mdb_optimizations || MONO_CFG_PROFILE_CALL_CONTEXT (cfg); + + if (jit->epilogue_begin) + record_line_number (info, jit->epilogue_begin, header->code_size); + + if (jit->has_var_info) { + jit->num_params = sig->param_count; + jit->params = g_new0 (MonoDebugVarInfo, jit->num_params); + + for (i = 0; i < jit->num_locals; i++) + write_variable (cfg->locals [i], &jit->locals [i]); + + if (sig->hasthis) { + jit->this_var = g_new0 (MonoDebugVarInfo, 1); + write_variable (cfg->args [0], jit->this_var); + } + + for (i = 0; i < jit->num_params; i++) + write_variable (cfg->args [i + sig->hasthis], &jit->params [i]); + + if (cfg->gsharedvt_info_var) { + jit->gsharedvt_info_var = g_new0 (MonoDebugVarInfo, 1); + jit->gsharedvt_locals_var = g_new0 (MonoDebugVarInfo, 1); + write_variable (cfg->gsharedvt_info_var, jit->gsharedvt_info_var); + write_variable (cfg->gsharedvt_locals_var, jit->gsharedvt_locals_var); + } + } + + jit->num_line_numbers = info->line_numbers->len; + jit->line_numbers = g_new0 (MonoDebugLineNumberEntry, jit->num_line_numbers); + + for (i = 0; i < jit->num_line_numbers; i++) + jit->line_numbers [i] = g_array_index (info->line_numbers, MonoDebugLineNumberEntry, i); + + mono_debug_add_method (cfg->method_to_register, jit, cfg->domain); + + mono_debug_add_vg_method (method, jit); + + mono_debug_free_method_jit_info (jit); + mono_debug_free_method (cfg); +} + +void +mono_debug_free_method (MonoCompile *cfg) +{ + MiniDebugMethodInfo *info; + + info = (MiniDebugMethodInfo *) cfg->debug_info; + if (info) { + if (info->line_numbers) + g_array_free (info->line_numbers, TRUE); + g_free (info); + cfg->debug_info = NULL; + } +} + +void +mono_debug_record_line_number (MonoCompile *cfg, MonoInst *ins, guint32 address) +{ + MiniDebugMethodInfo *info; + MonoMethodHeader *header; + guint32 offset; + + info = (MiniDebugMethodInfo *) cfg->debug_info; + if (!info || !info->jit || !ins->cil_code) + return; + + header = cfg->header; + g_assert (header); + + if ((ins->cil_code < header->code) || + (ins->cil_code > header->code + header->code_size)) + return; + + offset = ins->cil_code - header->code; + if (!info->has_line_numbers) { + info->jit->prologue_end = address; + info->has_line_numbers = TRUE; + } + + record_line_number (info, address, offset); +} + +void +mono_debug_open_block (MonoCompile *cfg, MonoBasicBlock *bb, guint32 address) +{ + MiniDebugMethodInfo *info; + MonoMethodHeader *header; + guint32 offset; + + info = (MiniDebugMethodInfo *) cfg->debug_info; + if (!info || !info->jit || !bb->cil_code) + return; + + header = cfg->header; + g_assert (header); + + if ((bb->cil_code < header->code) || + (bb->cil_code > header->code + header->code_size)) + return; + + offset = bb->cil_code - header->code; + if (!info->has_line_numbers) { + info->jit->prologue_end = address; + info->has_line_numbers = TRUE; + } + + record_line_number (info, address, offset); +} + +static inline void +encode_value (gint32 value, guint8 *buf, guint8 **endbuf) +{ + guint8 *p = buf; + + //printf ("ENCODE: %d 0x%x.\n", value, value); + + /* + * Same encoding as the one used in the metadata, extended to handle values + * greater than 0x1fffffff. + */ + if ((value >= 0) && (value <= 127)) + *p++ = value; + else if ((value >= 0) && (value <= 16383)) { + p [0] = 0x80 | (value >> 8); + p [1] = value & 0xff; + p += 2; + } else if ((value >= 0) && (value <= 0x1fffffff)) { + p [0] = (value >> 24) | 0xc0; + p [1] = (value >> 16) & 0xff; + p [2] = (value >> 8) & 0xff; + p [3] = value & 0xff; + p += 4; + } + else { + p [0] = 0xff; + p [1] = (value >> 24) & 0xff; + p [2] = (value >> 16) & 0xff; + p [3] = (value >> 8) & 0xff; + p [4] = value & 0xff; + p += 5; + } + if (endbuf) + *endbuf = p; +} + +static inline gint32 +decode_value (guint8 *ptr, guint8 **rptr) +{ + guint8 b = *ptr; + gint32 len; + + if ((b & 0x80) == 0){ + len = b; + ++ptr; + } else if ((b & 0x40) == 0){ + len = ((b & 0x3f) << 8 | ptr [1]); + ptr += 2; + } else if (b != 0xff) { + len = ((b & 0x1f) << 24) | + (ptr [1] << 16) | + (ptr [2] << 8) | + ptr [3]; + ptr += 4; + } + else { + len = (ptr [1] << 24) | (ptr [2] << 16) | (ptr [3] << 8) | ptr [4]; + ptr += 5; + } + if (rptr) + *rptr = ptr; + + //printf ("DECODE: %d.\n", len); + return len; +} + +static void +serialize_variable (MonoDebugVarInfo *var, guint8 *p, guint8 **endbuf) +{ + guint32 flags = var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS; + + encode_value (var->index, p, &p); + + switch (flags) { + case MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER: + break; + case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET: + case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET_INDIR: + case MONO_DEBUG_VAR_ADDRESS_MODE_VTADDR: + encode_value (var->offset, p, &p); + break; + case MONO_DEBUG_VAR_ADDRESS_MODE_GSHAREDVT_LOCAL: + case MONO_DEBUG_VAR_ADDRESS_MODE_DEAD: + break; + default: + g_assert_not_reached (); + } + *endbuf = p; +} + +void +mono_debug_serialize_debug_info (MonoCompile *cfg, guint8 **out_buf, guint32 *buf_len) +{ + MonoDebugMethodJitInfo *jit; + guint32 size, prev_offset, prev_native_offset; + guint8 *buf, *p; + int i; + + /* Can't use cfg->debug_info as it is freed by close_method () */ + jit = mono_debug_find_method (cfg->method, mono_domain_get ()); + if (!jit) { + *buf_len = 0; + return; + } + + size = ((jit->num_params + jit->num_locals + 1) * 10) + (jit->num_line_numbers * 10) + 64; + p = buf = (guint8 *)g_malloc (size); + encode_value (jit->epilogue_begin, p, &p); + encode_value (jit->prologue_end, p, &p); + encode_value (jit->code_size, p, &p); + encode_value (jit->has_var_info, p, &p); + + if (jit->has_var_info) { + for (i = 0; i < jit->num_params; ++i) + serialize_variable (&jit->params [i], p, &p); + + if (jit->this_var) + serialize_variable (jit->this_var, p, &p); + + for (i = 0; i < jit->num_locals; i++) + serialize_variable (&jit->locals [i], p, &p); + + if (jit->gsharedvt_info_var) { + encode_value (1, p, &p); + serialize_variable (jit->gsharedvt_info_var, p, &p); + serialize_variable (jit->gsharedvt_locals_var, p, &p); + } else { + encode_value (0, p, &p); + } + } + + encode_value (jit->num_line_numbers, p, &p); + + prev_offset = 0; + prev_native_offset = 0; + for (i = 0; i < jit->num_line_numbers; ++i) { + /* Sometimes, the offset values are not in increasing order */ + MonoDebugLineNumberEntry *lne = &jit->line_numbers [i]; + encode_value (lne->il_offset - prev_offset, p, &p); + encode_value (lne->native_offset - prev_native_offset, p, &p); + prev_offset = lne->il_offset; + prev_native_offset = lne->native_offset; + } + + g_assert (p - buf < size); + + *out_buf = buf; + *buf_len = p - buf; +} + +static void +deserialize_variable (MonoDebugVarInfo *var, guint8 *p, guint8 **endbuf) +{ + guint32 flags; + + var->index = decode_value (p, &p); + + flags = var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS; + + switch (flags) { + case MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER: + break; + case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET: + case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET_INDIR: + case MONO_DEBUG_VAR_ADDRESS_MODE_VTADDR: + var->offset = decode_value (p, &p); + break; + case MONO_DEBUG_VAR_ADDRESS_MODE_GSHAREDVT_LOCAL: + case MONO_DEBUG_VAR_ADDRESS_MODE_DEAD: + break; + default: + g_assert_not_reached (); + } + *endbuf = p; +} + +static MonoDebugMethodJitInfo * +deserialize_debug_info (MonoMethod *method, guint8 *code_start, guint8 *buf, guint32 buf_len) +{ + MonoError error; + MonoMethodHeader *header; + gint32 offset, native_offset, prev_offset, prev_native_offset; + MonoDebugMethodJitInfo *jit; + guint8 *p; + int i; + + header = mono_method_get_header_checked (method, &error); + mono_error_assert_ok (&error); /* FIXME don't swallow the error */ + + jit = g_new0 (MonoDebugMethodJitInfo, 1); + jit->code_start = code_start; + + p = buf; + jit->epilogue_begin = decode_value (p, &p); + jit->prologue_end = decode_value (p, &p); + jit->code_size = decode_value (p, &p); + jit->has_var_info = decode_value (p, &p); + + if (jit->has_var_info) { + jit->num_locals = header->num_locals; + jit->num_params = mono_method_signature (method)->param_count; + jit->params = g_new0 (MonoDebugVarInfo, jit->num_params); + jit->locals = g_new0 (MonoDebugVarInfo, jit->num_locals); + + for (i = 0; i < jit->num_params; ++i) + deserialize_variable (&jit->params [i], p, &p); + + if (mono_method_signature (method)->hasthis) { + jit->this_var = g_new0 (MonoDebugVarInfo, 1); + deserialize_variable (jit->this_var, p, &p); + } + + for (i = 0; i < jit->num_locals; i++) + deserialize_variable (&jit->locals [i], p, &p); + + if (decode_value (p, &p)) { + jit->gsharedvt_info_var = g_new0 (MonoDebugVarInfo, 1); + jit->gsharedvt_locals_var = g_new0 (MonoDebugVarInfo, 1); + deserialize_variable (jit->gsharedvt_info_var, p, &p); + deserialize_variable (jit->gsharedvt_locals_var, p, &p); + } + } + + jit->num_line_numbers = decode_value (p, &p); + jit->line_numbers = g_new0 (MonoDebugLineNumberEntry, jit->num_line_numbers); + + prev_offset = 0; + prev_native_offset = 0; + for (i = 0; i < jit->num_line_numbers; ++i) { + MonoDebugLineNumberEntry *lne = &jit->line_numbers [i]; + + offset = prev_offset + decode_value (p, &p); + native_offset = prev_native_offset + decode_value (p, &p); + + lne->native_offset = native_offset; + lne->il_offset = offset; + + prev_offset = offset; + prev_native_offset = native_offset; + } + + mono_metadata_free_mh (header); + return jit; +} + +void +mono_debug_add_aot_method (MonoDomain *domain, MonoMethod *method, guint8 *code_start, + guint8 *debug_info, guint32 debug_info_len) +{ + MonoDebugMethodJitInfo *jit; + + if (!mono_debug_enabled ()) + return; + + if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) || + (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) || + (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) || + (method->flags & METHOD_ATTRIBUTE_ABSTRACT) || + (method->wrapper_type != MONO_WRAPPER_NONE)) + return; + + if (debug_info_len == 0) + return; + + jit = deserialize_debug_info (method, code_start, debug_info, debug_info_len); + + mono_debug_add_method (method, jit, domain); + + mono_debug_add_vg_method (method, jit); + + mono_debug_free_method_jit_info (jit); +} + +static void +print_var_info (MonoDebugVarInfo *info, int idx, const char *name, const char *type) +{ + switch (info->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS) { + case MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER: + g_print ("%s %s (%d) in register %s\n", type, name, idx, mono_arch_regname (info->index & (~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS))); + break; + case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET: + g_print ("%s %s (%d) in memory: base register %s + %d\n", type, name, idx, mono_arch_regname (info->index & (~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS)), info->offset); + break; + case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET_INDIR: + g_print ("%s %s (%d) in indir memory: base register %s + %d\n", type, name, idx, mono_arch_regname (info->index & (~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS)), info->offset); + break; + case MONO_DEBUG_VAR_ADDRESS_MODE_GSHAREDVT_LOCAL: + g_print ("%s %s (%d) gsharedvt local.\n", type, name, idx); + break; + case MONO_DEBUG_VAR_ADDRESS_MODE_VTADDR: + g_print ("%s %s (%d) vt address: base register %s + %d\n", type, name, idx, mono_arch_regname (info->index & (~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS)), info->offset); + break; + case MONO_DEBUG_VAR_ADDRESS_MODE_TWO_REGISTERS: + default: + g_assert_not_reached (); + } +} + +/** + * mono_debug_print_locals: + * + * Prints to stdout the information about the local variables in + * a method (if \p only_arguments is false) or about the arguments. + * The information includes the storage info (where the variable + * lives, in a register or in memory). + * The method is found by looking up what method has been emitted at + * the instruction address \p ip. + * This is for use inside a debugger. + */ +void +mono_debug_print_vars (gpointer ip, gboolean only_arguments) +{ + MonoDomain *domain = mono_domain_get (); + MonoJitInfo *ji = mono_jit_info_table_find (domain, (char *)ip); + MonoDebugMethodJitInfo *jit; + int i; + + if (!ji) + return; + + jit = mono_debug_find_method (jinfo_get_method (ji), domain); + if (!jit) + return; + + if (only_arguments) { + char **names; + names = g_new (char *, jit->num_params); + mono_method_get_param_names (jinfo_get_method (ji), (const char **) names); + if (jit->this_var) + print_var_info (jit->this_var, 0, "this", "Arg"); + for (i = 0; i < jit->num_params; ++i) { + print_var_info (&jit->params [i], i, names [i]? names [i]: "unknown name", "Arg"); + } + g_free (names); + } else { + for (i = 0; i < jit->num_locals; ++i) { + print_var_info (&jit->locals [i], i, "", "Local"); + } + } + mono_debug_free_method_jit_info (jit); +} + +/* + * The old Debugger breakpoint interface. + * + * This interface is used to insert breakpoints on methods which are not yet JITed. + * The debugging code keeps a list of all such breakpoints and automatically inserts the + * breakpoint when the method is JITed. + */ + +static GPtrArray *breakpoints; + +static int +mono_debugger_insert_breakpoint_full (MonoMethodDesc *desc) +{ + static int last_breakpoint_id = 0; + MiniDebugBreakpointInfo *info; + + info = g_new0 (MiniDebugBreakpointInfo, 1); + info->desc = desc; + info->index = ++last_breakpoint_id; + + if (!breakpoints) + breakpoints = g_ptr_array_new (); + + g_ptr_array_add (breakpoints, info); + + return info->index; +} + +/*FIXME This is part of the public API by accident, remove it from there when possible. */ +int +mono_debugger_insert_breakpoint (const gchar *method_name, gboolean include_namespace) +{ + MonoMethodDesc *desc; + + desc = mono_method_desc_new (method_name, include_namespace); + if (!desc) + return 0; + + return mono_debugger_insert_breakpoint_full (desc); +} + +/*FIXME This is part of the public API by accident, remove it from there when possible. */ +int +mono_debugger_method_has_breakpoint (MonoMethod *method) +{ + int i; + + if (!breakpoints) + return 0; + + for (i = 0; i < breakpoints->len; i++) { + MiniDebugBreakpointInfo *info = (MiniDebugBreakpointInfo *)g_ptr_array_index (breakpoints, i); + + if (!mono_method_desc_full_match (info->desc, method)) + continue; + + return info->index; + } + + return 0; +} diff --git a/unity-2019.4.24f1-mbe/mono/mini/debugger-agent.c b/unity-2019.4.24f1-mbe/mono/mini/debugger-agent.c new file mode 100644 index 000000000..1a9c9bae9 --- /dev/null +++ b/unity-2019.4.24f1-mbe/mono/mini/debugger-agent.c @@ -0,0 +1,12755 @@ +/** + * \file + * Soft Debugger back-end module + * + * Author: + * Zoltan Varga (vargaz@gmail.com) + * + * Copyright 2009-2010 Novell, Inc. + * Copyright 2011 Xamarin Inc. + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#include +#include +#include +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_SELECT_H +#include +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#ifdef HAVE_NETINET_TCP_H +#include +#endif +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include + +#ifdef HAVE_PTHREAD_H +#include +#endif + +#ifdef HOST_WIN32 +#ifdef _MSC_VER +#include +#include +#endif +#include +#include +#endif + +#ifdef HOST_ANDROID +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + * shutdown sequence. +*/ +#if !defined (TARGET_IOS) +#define TRY_MANAGED_SYSTEM_ENVIRONMENT_EXIT +#endif + + +#ifndef MONO_ARCH_SOFT_DEBUG_SUPPORTED +#define DISABLE_DEBUGGER_AGENT 1 +#endif + +#ifdef DISABLE_SOFT_DEBUG +#define DISABLE_DEBUGGER_AGENT 1 +#endif + +#ifndef DISABLE_DEBUGGER_AGENT + +#include + +#include +#include + +#ifndef S_IWUSR + #define S_IWUSR S_IWRITE +#endif + +#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; + char *address; + int log_level; + char *log_file; + gboolean suspend; + gboolean server; + gboolean onuncaught; + GSList *onthrow; + int timeout; + char *launch; + gboolean embedding; + gboolean defer; + int keepalive; + gboolean setpgid; +} AgentConfig; + +typedef struct +{ + int id; + guint32 il_offset, native_offset; + MonoDomain *domain; + MonoMethod *method; + /* + * If method is gshared, this is the actual instance, otherwise this is equal to + * method. + */ + MonoMethod *actual_method; + /* + * This is the method which is visible to debugger clients. Same as method, + * except for native-to-managed wrappers. + */ + MonoMethod *api_method; + MonoContext ctx; + MonoDebugMethodJitInfo *jit; +#ifdef RUNTIME_IL2CPP + Il2CppSequencePointExecutionContext* frame_context; +#endif // RUNTIME_IL2CPP + MonoJitInfo *ji; + MonoInterpFrameHandle interp_frame; + int flags; + mgreg_t *reg_locations [MONO_MAX_IREGS]; + /* + * Whenever ctx is set. This is FALSE for the last frame of running threads, since + * the frame can become invalid. + */ + gboolean has_ctx; +} StackFrame; + +typedef struct _InvokeData InvokeData; + +struct _InvokeData +{ + int id; + int flags; + guint8 *p; + guint8 *endp; + /* This is the context which needs to be restored after the invoke */ + MonoContext ctx; + gboolean has_ctx; + /* + * If this is set, invoke this method with the arguments given by ARGS. + */ + MonoMethod *method; + gpointer *args; + guint32 suspend_count; + int nmethods; + + InvokeData *last_invoke; +}; + +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; + StackFrame **frames; + /* + * Whenever the frame info is up-to-date. If not, compute_frame_info () will need to + * re-compute it. + */ + gboolean frames_up_to_date; + /* + * Points to data about a pending invoke which needs to be executed after the thread + * resumes. + */ + InvokeData *pending_invoke; + /* + * Set to TRUE if this thread is suspended in suspend_current () or it is executing + * native code. + */ + gboolean suspended; + /* + * Signals whenever the thread is in the process of suspending, i.e. it will suspend + * within a finite amount of time. + */ + gboolean suspending; + /* + * Set to TRUE if this thread is suspended in suspend_current (). + */ + gboolean really_suspended; + /* Used to pass the context to the breakpoint/single step handler */ + MonoContext handler_ctx; + /* Whenever thread_stop () was called for this thread */ + gboolean terminated; + + /* Whenever to disable breakpoints (used during invokes) */ + gboolean disable_breakpoints; + + /* + * Number of times this thread has been resumed using resume_thread (). + */ + guint32 resume_count; + + MonoInternalThread *thread; + + /* + * Information about the frame which transitioned to native code for running + * threads. + */ + StackFrameInfo async_last_frame; + + /* + * The context where the stack walk can be started for running threads. + */ + MonoThreadUnwindState async_state; + + /* + * The context used for filter clauses + */ + MonoThreadUnwindState filter_state; + + gboolean abort_requested; + + /* + * The current mono_runtime_invoke_checked invocation. + */ + InvokeData *invoke; + + StackFrameInfo catch_frame; +#ifdef RUNTIME_IL2CPP + MonoException *exception; +#endif // RUNTIME_IL2CPP + gboolean has_catch_frame; + + /* + * The context which needs to be restored after handling a single step/breakpoint + * event. This is the same as the ctx at step/breakpoint site, but includes changes + * to caller saved registers done by set_var (). + */ + MonoThreadUnwindState restore_state; + /* Frames computed from restore_state */ + int restore_frame_count; + StackFrame **restore_frames; + + /* The currently unloading appdomain */ + MonoDomain *domain_unloading; +} DebuggerTlsData; + +typedef struct { + const char *name; + void (*connect) (const char *address); + int (*wait_for_attach) (void); + void (*close1) (void); + void (*close2) (void); + int (*send) (void *buf, int len); + int (*recv) (void *buf, int len); +} DebuggerTransport; + +/* + * Wire Protocol definitions + */ + +#define HEADER_LENGTH 11 + +#define MAJOR_VERSION 2 +#define MINOR_VERSION 46 + +typedef enum { + CMD_SET_VM = 1, + CMD_SET_OBJECT_REF = 9, + CMD_SET_STRING_REF = 10, + CMD_SET_THREAD = 11, + CMD_SET_ARRAY_REF = 13, + CMD_SET_EVENT_REQUEST = 15, + CMD_SET_STACK_FRAME = 16, + CMD_SET_APPDOMAIN = 20, + CMD_SET_ASSEMBLY = 21, + CMD_SET_METHOD = 22, + CMD_SET_TYPE = 23, + CMD_SET_MODULE = 24, + CMD_SET_FIELD = 25, + CMD_SET_EVENT = 64, + CMD_SET_POINTER = 65 +} CommandSet; + +typedef enum { + EVENT_KIND_VM_START = 0, + EVENT_KIND_VM_DEATH = 1, + EVENT_KIND_THREAD_START = 2, + EVENT_KIND_THREAD_DEATH = 3, + EVENT_KIND_APPDOMAIN_CREATE = 4, + EVENT_KIND_APPDOMAIN_UNLOAD = 5, + EVENT_KIND_METHOD_ENTRY = 6, + EVENT_KIND_METHOD_EXIT = 7, + EVENT_KIND_ASSEMBLY_LOAD = 8, + EVENT_KIND_ASSEMBLY_UNLOAD = 9, + EVENT_KIND_BREAKPOINT = 10, + EVENT_KIND_STEP = 11, + EVENT_KIND_TYPE_LOAD = 12, + EVENT_KIND_EXCEPTION = 13, + EVENT_KIND_KEEPALIVE = 14, + EVENT_KIND_USER_BREAK = 15, + EVENT_KIND_USER_LOG = 16 +} EventKind; + +typedef enum { + SUSPEND_POLICY_NONE = 0, + SUSPEND_POLICY_EVENT_THREAD = 1, + SUSPEND_POLICY_ALL = 2 +} SuspendPolicy; + +typedef enum { + ERR_NONE = 0, + ERR_INVALID_OBJECT = 20, + ERR_INVALID_FIELDID = 25, + ERR_INVALID_FRAMEID = 30, + ERR_NOT_IMPLEMENTED = 100, + ERR_NOT_SUSPENDED = 101, + ERR_INVALID_ARGUMENT = 102, + ERR_UNLOADED = 103, + ERR_NO_INVOCATION = 104, + ERR_ABSENT_INFORMATION = 105, + ERR_NO_SEQ_POINT_AT_IL_OFFSET = 106, + ERR_INVOKE_ABORTED = 107, + ERR_LOADER_ERROR = 200, /*XXX extend the protocol to pass this information down the pipe */ +} ErrorCode; + +typedef enum { + MOD_KIND_COUNT = 1, + MOD_KIND_THREAD_ONLY = 3, + MOD_KIND_LOCATION_ONLY = 7, + MOD_KIND_EXCEPTION_ONLY = 8, + MOD_KIND_STEP = 10, + MOD_KIND_ASSEMBLY_ONLY = 11, + MOD_KIND_SOURCE_FILE_ONLY = 12, + MOD_KIND_TYPE_NAME_ONLY = 13, + MOD_KIND_NONE = 14 +} ModifierKind; + +typedef enum { + STEP_DEPTH_INTO = 0, + STEP_DEPTH_OVER = 1, + STEP_DEPTH_OUT = 2 +} StepDepth; + +typedef enum { + STEP_SIZE_MIN = 0, + STEP_SIZE_LINE = 1 +} StepSize; + +typedef enum { + STEP_FILTER_NONE = 0, + STEP_FILTER_STATIC_CTOR = 1, + STEP_FILTER_DEBUGGER_HIDDEN = 2, + STEP_FILTER_DEBUGGER_STEP_THROUGH = 4, + STEP_FILTER_DEBUGGER_NON_USER_CODE = 8 +} StepFilter; + +typedef enum { + TOKEN_TYPE_STRING = 0, + TOKEN_TYPE_TYPE = 1, + TOKEN_TYPE_FIELD = 2, + TOKEN_TYPE_METHOD = 3, + TOKEN_TYPE_UNKNOWN = 4 +} DebuggerTokenType; + +typedef enum { + VALUE_TYPE_ID_NULL = 0xf0, + VALUE_TYPE_ID_TYPE = 0xf1, + VALUE_TYPE_ID_PARENT_VTYPE = 0xf2 +} ValueTypeId; + +typedef enum { + FRAME_FLAG_DEBUGGER_INVOKE = 1, + FRAME_FLAG_NATIVE_TRANSITION = 2 +} StackFrameFlags; + +typedef enum { + INVOKE_FLAG_DISABLE_BREAKPOINTS = 1, + INVOKE_FLAG_SINGLE_THREADED = 2, + INVOKE_FLAG_RETURN_OUT_THIS = 4, + INVOKE_FLAG_RETURN_OUT_ARGS = 8, + INVOKE_FLAG_VIRTUAL = 16 +} InvokeFlags; + +typedef enum { + BINDING_FLAGS_IGNORE_CASE = 0x70000000, +} BindingFlagsExtensions; + +typedef enum { + CMD_VM_VERSION = 1, + CMD_VM_ALL_THREADS = 2, + CMD_VM_SUSPEND = 3, + CMD_VM_RESUME = 4, + CMD_VM_EXIT = 5, + CMD_VM_DISPOSE = 6, + CMD_VM_INVOKE_METHOD = 7, + CMD_VM_SET_PROTOCOL_VERSION = 8, + CMD_VM_ABORT_INVOKE = 9, + CMD_VM_SET_KEEPALIVE = 10, + CMD_VM_GET_TYPES_FOR_SOURCE_FILE = 11, + CMD_VM_GET_TYPES = 12, + CMD_VM_INVOKE_METHODS = 13, + CMD_VM_START_BUFFERING = 14, + CMD_VM_STOP_BUFFERING = 15 +} CmdVM; + +typedef enum { + CMD_THREAD_GET_FRAME_INFO = 1, + CMD_THREAD_GET_NAME = 2, + CMD_THREAD_GET_STATE = 3, + CMD_THREAD_GET_INFO = 4, + CMD_THREAD_GET_ID = 5, + CMD_THREAD_GET_TID = 6, + CMD_THREAD_SET_IP = 7 +} CmdThread; + +typedef enum { + CMD_EVENT_REQUEST_SET = 1, + CMD_EVENT_REQUEST_CLEAR = 2, + CMD_EVENT_REQUEST_CLEAR_ALL_BREAKPOINTS = 3 +} CmdEvent; + +typedef enum { + CMD_COMPOSITE = 100 +} CmdComposite; + +typedef enum { + CMD_APPDOMAIN_GET_ROOT_DOMAIN = 1, + CMD_APPDOMAIN_GET_FRIENDLY_NAME = 2, + CMD_APPDOMAIN_GET_ASSEMBLIES = 3, + CMD_APPDOMAIN_GET_ENTRY_ASSEMBLY = 4, + CMD_APPDOMAIN_CREATE_STRING = 5, + CMD_APPDOMAIN_GET_CORLIB = 6, + CMD_APPDOMAIN_CREATE_BOXED_VALUE = 7 +} CmdAppDomain; + +typedef enum { + CMD_ASSEMBLY_GET_LOCATION = 1, + CMD_ASSEMBLY_GET_ENTRY_POINT = 2, + CMD_ASSEMBLY_GET_MANIFEST_MODULE = 3, + CMD_ASSEMBLY_GET_OBJECT = 4, + CMD_ASSEMBLY_GET_TYPE = 5, + CMD_ASSEMBLY_GET_NAME = 6, + CMD_ASSEMBLY_GET_DOMAIN = 7 +} CmdAssembly; + +typedef enum { + CMD_MODULE_GET_INFO = 1, +} CmdModule; + +typedef enum { + CMD_FIELD_GET_INFO = 1, +} CmdField; + +typedef enum { + CMD_METHOD_GET_NAME = 1, + CMD_METHOD_GET_DECLARING_TYPE = 2, + CMD_METHOD_GET_DEBUG_INFO = 3, + CMD_METHOD_GET_PARAM_INFO = 4, + CMD_METHOD_GET_LOCALS_INFO = 5, + CMD_METHOD_GET_INFO = 6, + CMD_METHOD_GET_BODY = 7, + CMD_METHOD_RESOLVE_TOKEN = 8, + CMD_METHOD_GET_CATTRS = 9, + CMD_METHOD_MAKE_GENERIC_METHOD = 10 +} CmdMethod; + +typedef enum { + CMD_TYPE_GET_INFO = 1, + CMD_TYPE_GET_METHODS = 2, + CMD_TYPE_GET_FIELDS = 3, + CMD_TYPE_GET_VALUES = 4, + CMD_TYPE_GET_OBJECT = 5, + CMD_TYPE_GET_SOURCE_FILES = 6, + CMD_TYPE_SET_VALUES = 7, + CMD_TYPE_IS_ASSIGNABLE_FROM = 8, + CMD_TYPE_GET_PROPERTIES = 9, + CMD_TYPE_GET_CATTRS = 10, + CMD_TYPE_GET_FIELD_CATTRS = 11, + CMD_TYPE_GET_PROPERTY_CATTRS = 12, + CMD_TYPE_GET_SOURCE_FILES_2 = 13, + CMD_TYPE_GET_VALUES_2 = 14, + CMD_TYPE_GET_METHODS_BY_NAME_FLAGS = 15, + CMD_TYPE_GET_INTERFACES = 16, + CMD_TYPE_GET_INTERFACE_MAP = 17, + CMD_TYPE_IS_INITIALIZED = 18, + CMD_TYPE_CREATE_INSTANCE = 19, + CMD_TYPE_GET_VALUE_SIZE = 20 +} CmdType; + +typedef enum { + CMD_STACK_FRAME_GET_VALUES = 1, + CMD_STACK_FRAME_GET_THIS = 2, + CMD_STACK_FRAME_SET_VALUES = 3, + CMD_STACK_FRAME_GET_DOMAIN = 4, + CMD_STACK_FRAME_SET_THIS = 5, +} CmdStackFrame; + +typedef enum { + CMD_ARRAY_REF_GET_LENGTH = 1, + CMD_ARRAY_REF_GET_VALUES = 2, + CMD_ARRAY_REF_SET_VALUES = 3, +} CmdArray; + +typedef enum { + CMD_STRING_REF_GET_VALUE = 1, + CMD_STRING_REF_GET_LENGTH = 2, + 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, + CMD_OBJECT_REF_IS_COLLECTED = 3, + CMD_OBJECT_REF_GET_ADDRESS = 4, + CMD_OBJECT_REF_GET_DOMAIN = 5, + CMD_OBJECT_REF_SET_VALUES = 6, + CMD_OBJECT_REF_GET_INFO = 7, +} CmdObject; + +typedef struct { + ModifierKind kind; + union { + int count; /* For kind == MOD_KIND_COUNT */ + MonoInternalThread *thread; /* For kind == MOD_KIND_THREAD_ONLY */ + MonoClass *exc_class; /* For kind == MONO_KIND_EXCEPTION_ONLY */ + MonoAssembly **assemblies; /* For kind == MONO_KIND_ASSEMBLY_ONLY */ + GHashTable *source_files; /* For kind == MONO_KIND_SOURCE_FILE_ONLY */ + GHashTable *type_names; /* For kind == MONO_KIND_TYPE_NAME_ONLY */ + StepFilter filter; /* For kind == MOD_KIND_STEP */ + } data; + gboolean caught, uncaught, subclasses; /* For kind == MOD_KIND_EXCEPTION_ONLY */ +} Modifier; + +typedef struct{ + int id; + int event_kind; + int suspend_policy; + int nmodifiers; + gpointer info; + Modifier modifiers [MONO_ZERO_LEN_ARRAY]; +} EventRequest; + +/* + * Describes a single step request. + */ +typedef struct { + EventRequest *req; + MonoInternalThread *thread; + StepDepth depth; + StepSize size; + StepFilter filter; + gpointer last_sp; + gpointer start_sp; + MonoMethod *start_method; + MonoMethod *last_method; + int last_line; + /* Whenever single stepping is performed using start/stop_single_stepping () */ + gboolean global; + /* The list of breakpoints used to implement step-over */ + GSList *bps; + /* The number of frames at the start of a step-over */ + int nframes; + /* If set, don't stop in methods that are not part of user assemblies */ + MonoAssembly** user_assemblies; + /* Used to distinguish stepping breakpoint hits in parallel tasks executions */ + int async_id; + /* Used to know if we are in process of async step-out and distishing from exception breakpoints */ + MonoMethod* async_stepout_method; +} SingleStepReq; + +/* + * Contains additional information for an event + */ +typedef struct { + /* For EVENT_KIND_EXCEPTION */ + MonoObject *exc; + MonoContext catch_ctx; + gboolean caught; + /* For EVENT_KIND_USER_LOG */ + int level; + char *category, *message; + /* For EVENT_KIND_TYPE_LOAD */ + MonoClass *klass; +} DebuggerEventInfo; + +typedef struct { + guint8 *buf, *p, *end; +} Buffer; + +typedef struct ReplyPacket { + int id; + int error; + Buffer *data; +} ReplyPacket; + +#define DEBUG(level,s) do { if (G_UNLIKELY ((level) <= log_level)) { s; fflush (log_file); } } while (0) + +#ifdef HOST_ANDROID +#define DEBUG_PRINTF(level, ...) do { if (G_UNLIKELY ((level) <= log_level)) { g_print (__VA_ARGS__); } } while (0) +#else +#define DEBUG_PRINTF(level, ...) do { if (G_UNLIKELY ((level) <= log_level)) { fprintf (log_file, __VA_ARGS__); fflush (log_file); } } while (0) +#endif + +#ifdef HOST_WIN32 +#define get_last_sock_error() WSAGetLastError() +#define MONO_EWOULDBLOCK WSAEWOULDBLOCK +#define MONO_EINTR WSAEINTR +#else +#define get_last_sock_error() errno +#define MONO_EWOULDBLOCK EWOULDBLOCK +#define MONO_EINTR EINTR +#endif + +#define CHECK_PROTOCOL_VERSION(major,minor) \ + (protocol_version_set && (major_version > (major) || (major_version == (major) && minor_version >= (minor)))) + +/* + * Globals + */ + +static AgentConfig agent_config; + +/* + * Whenever the agent is fully initialized. + * When using the onuncaught or onthrow options, only some parts of the agent are + * initialized on startup, and the full initialization which includes connection + * establishment and the startup of the agent thread is only done in response to + * an event. + */ +static gint32 inited; + +#ifndef DISABLE_SOCKET_TRANSPORT +static int conn_fd; +static int listen_fd; +#endif + +static int packet_id = 0; + +static int objref_id = 0; + +static int event_request_id = 0; + +static int frame_id = 0; + +static GPtrArray *event_requests; + +static MonoNativeTlsKey debugger_tls_id; + +static gboolean vm_start_event_sent, vm_death_event_sent, disconnected; + +/* Maps MonoInternalThread -> DebuggerTlsData */ +/* Protected by the loader lock */ +static MonoGHashTable *thread_to_tls; + +/* Maps tid -> MonoInternalThread */ +/* Protected by the loader lock */ +static MonoGHashTable *tid_to_thread; + +/* Maps tid -> MonoThread (not MonoInternalThread) */ +/* Protected by the loader lock */ +static MonoGHashTable *tid_to_thread_obj; + +static MonoNativeThreadId debugger_thread_id; + +static MonoThreadHandle *debugger_thread_handle; + +static int log_level; + +static int file_check_valid_memory = -1; + +static char* filename_check_valid_memory; + +static gboolean embedding; + +static FILE *log_file; + +/* Assemblies whose assembly load event has no been sent yet */ +/* Protected by the dbg lock */ +static GPtrArray *pending_assembly_loads; + +/* Whenever the debugger thread has exited */ +static gboolean debugger_thread_exited; + +/* Cond variable used to wait for debugger_thread_exited becoming true */ +static MonoCoopCond debugger_thread_exited_cond; + +/* Mutex for the cond var above */ +static MonoCoopMutex debugger_thread_exited_mutex; + +/* The single step request instance */ +static SingleStepReq *ss_req; + +#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED +/* Number of single stepping operations in progress */ +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; + +/* Whenever the variables above are set by the client */ +static gboolean protocol_version_set; + +/* A hash table containing all active domains */ +/* Protected by the loader lock */ +static GHashTable *domains; + +/* The number of times the runtime is suspended */ +static gint32 suspend_count; + +/* Whenever to buffer reply messages and send them together */ +static gboolean buffer_replies; + +/* Buffered reply packets */ +static ReplyPacket reply_packets [128]; +int nreply_packets; + +#define dbg_lock() mono_coop_mutex_lock (&debug_mutex) +#define dbg_unlock() mono_coop_mutex_unlock (&debug_mutex) +static MonoCoopMutex debug_mutex; + +static void transport_init (void); +static void transport_connect (const char *address); +static gboolean transport_handshake (void); +static void register_transport (DebuggerTransport *trans); + +static gsize WINAPI debugger_thread (void *arg); + +static void runtime_initialized (MonoProfiler *prof); + +static void runtime_shutdown (MonoProfiler *prof); + +static void thread_startup (MonoProfiler *prof, uintptr_t tid); + +static void thread_end (MonoProfiler *prof, uintptr_t tid); + +static void appdomain_load (MonoProfiler *prof, MonoDomain *domain); + +static void appdomain_start_unload (MonoProfiler *prof, MonoDomain *domain); + +static void appdomain_unload (MonoProfiler *prof, MonoDomain *domain); + +static void emit_appdomain_load (gpointer key, gpointer value, gpointer user_data); + +static void emit_thread_start (gpointer key, gpointer value, gpointer user_data); + +static void invalidate_each_thread (gpointer key, gpointer value, gpointer user_data); + +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); + +static void emit_type_load (gpointer key, gpointer type, gpointer user_data); + +static void jit_done (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo); + +static void jit_failed (MonoProfiler *prof, MonoMethod *method); + +static void jit_end (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo); + +static void add_pending_breakpoints (MonoMethod *method, MonoJitInfo *jinfo); + +static void start_single_stepping (void); + +static void stop_single_stepping (void); + +static void suspend_current (void); + +static void clear_event_requests_for_assembly (MonoAssembly *assembly); + +static void clear_types_for_assembly (MonoAssembly *assembly); + +static void clear_breakpoints_for_domain (MonoDomain *domain); + +static void process_profiler_event (EventKind event, gpointer arg); + +/* Submodule init/cleanup */ +static void breakpoints_init (void); +static void breakpoints_cleanup (void); + +static void objrefs_init (void); +static void objrefs_cleanup (void); + +static void ids_init (void); +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); + +static void start_debugger_thread (void); +static void stop_debugger_thread (void); + +static void finish_agent_init (gboolean on_startup); + +static void process_profiler_event (EventKind event, gpointer arg); + +static void invalidate_frames (DebuggerTlsData *tls); + +#ifndef DISABLE_SOCKET_TRANSPORT +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) +{ + MonoInternalThread *internal; + + internal = mono_thread_internal_current (); + if (!internal) + return FALSE; + + return internal->debugger_thread; +} +#endif // RUNTIME_IL2CPP + +static int +parse_address (char *address, char **host, int *port) +{ + char *pos = strchr (address, ':'); + + if (pos == NULL || pos == address) + return 1; + + size_t len = pos - address; + *host = (char *)g_malloc (len + 1); + memcpy (*host, address, len); + (*host) [len] = '\0'; + + *port = atoi (pos + 1); + + return 0; +} + +static void +print_usage (void) +{ + fprintf (stderr, "Usage: mono --debugger-agent=[